ossp-pkg/sio/sio.pod
##
## OSSP sio - Stream I/O
## Copyright (c) 2002-2005 Cable & Wireless <http://www.cw.com/>
## Copyright (c) 2002-2005 The OSSP Project <http://www.ossp.org/>
## Copyright (c) 2002-2005 Ralf S. Engelschall <rse@engelschall.com>
##
## This file is part of OSSP sio, a layered stream I/O library
## which can be found at http://www.ossp.org/pkg/lib/sio/.
##
## Permission to use, copy, modify, and distribute this software for
## any purpose with or without fee is hereby granted, provided that
## the above copyright notice and this permission notice appear in all
## copies.
##
## THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
## SUCH DAMAGE.
##
## sio.pod: stream I/O library manual page
##
=pod
=head1 NAME
B<OSSP sio> - Stream I/O
=head1 VERSION
B<OSSP SIO SIO_VERSION_STR>
=head1 SYNOPSIS
=over 4
=item B<Abstract Data Types>:
sio_rc_t,
sio_mode_t,
sio_flag_t,
sio_t,
sio_stage_t,
sio_module_t,
sio_labelnum_t.
=item B<Stream Operations>:
sio_create,
sio_destroy,
sio_input,
sio_output,
sio_read,
sio_write,
sio_push,
sio_flag,
sio_clearflag.
=item B<Stage Operations>:
sio_create_stage,
sio_destroy_stage,
sio_configure_stage,
sio_attach,
sio_detach.
=item B<Error Handling>:
sio_error.
=item B<Internal Stage Operations>:
sio_label,
sios->init,
sios->configure,
sios->cleanup,
sios->openr,
sios->closer,
sios->openw,
sios->closew,
sios->input,
sios->output,
sios->shutdown.
=back
=head1 DESCRIPTION
B<OSSP sio> defines an abstract type of a pair of half-duplex data pipes.
It provides the following key features:
=over 4
=back
=head1 DATA TYPES
B<OSSP sio> uses six data types in its API:
=over 4
=item B<sio_rc_t> (Return Code Type)
This is an exported enumerated integer type with the following possible
values:
SIO_OK Everything Ok
SIO_ERR_ARG Invalid Argument
SIO_ERR_MEM Not Enough Memory
SIO_ERR_EOF End Of Communication
SIO_ERR_INT Internal Error
SIO_UPSTREAM Invoke Upstream Stage
SIO_DOWNSTREAM Invoke Downstream Stage
SIO_XSTREAM Invoke Crossstream Stage
SIO_LOOP Loop through current Stage
=item B<sio_mode_t> (Attach Mode Type)
This is an exported enumerated integer type with the following possible
values:
SIO_MODE_INVALID Stage is not attached
SIO_MODE_READ Stage is attached for reading
SIO_MODE_WRITE Stage is attached for writing
SIO_MODE_READWRITE Stage is attached for reading and writing
=item B<sio_flag_t> (Flag Type)
This is an exported enumerated integer type with the following possible
values:
SIO_FLAG_ERROR Identify the error flag
SIO_FLAG_EOF Identify the end-of-file flag
=item B<sio_t> (Stream I/O Type)
This is an opaque data type representing a pair of two half-duplex data
pipes. Only pointers to this abstract data type are used in the API.
=item B<sio_stage_t> (Stream Stage Type)
This is an opaque pointer type representing a processing node on
a data pipe.
=item B<sio_module_t> (Stream Stage Module Type)
This is an opaque data type representing the methods of a stream
stage. Only pointers to this abstract data type are used in the API.
=item B<sio_labelnum_t> (Data Label Type)
This is an exported enumerated integer type with the following possible
values:
SIO_LN_DATA Label number for user data chunks
SIO_LN_ERROR Label number for error data chunks
SIO_LN_EOF Label number for end-of-file data chunks
=back
=head1 FUNCTIONS
B<OSSP sio> provides a bunch of API functions, all modelled after the
same prototype: "C<sio_rc_t> C<sio_>I<name>C<(sio__t *,>...C<)>".
This means every function returns C<sio_rc_t> to indicate its
success (C<SIO_OK>) or failure (C<SIO_ERR_XXX>) by returning a return code
(the corresponding describing text can be determined by passing this
return code to C<sio_error>). Each function name starts with the common
prefix C<sio_> and receives a C<sio_t> object on which it operates as
its first argument.
=head2 Stream Operations
=over 4
=item sio_rc_t B<sio_create>(sio_t **I<siop>);
Create a stream object. The object is stored in I<siop> on success.
=item sio_rc_t B<sio_destroy>(sio_t *I<sio>);
Destroy the stream object I<sio>. The object is invalid after this
call succeeded.
=item sio_rc_t B<sio_input>(sio_t *I<sio>, al_t *I<al>, size_t limit, al_label_t label);
Pull data from stream object I<sio>, the data is appended to the
assembly line I<al> and will contain no more than I<limit> bytes.
If label != NULL then only data that is tagged with this label
will be pulled from the stream. However, finally all data must
be pulled from the stream before new I/O operations are scheduled.
=item sio_rc_t B<sio_output>(sio_t *I<sio>, al_t *I<al>);
Push data to stream object I<sio>, the data is spliced from the
assembly line I<al>. When the stream has no place for all data
in the assembly line then part (or all) of it will stay in I<al>.
=item sio_rc_t B<sio_read>(sio_t *I<sio>, char *I<dst>, size_t I<n>, size_t *<actualp>);
Copy up to I<n> user data bytes from stream I<sio> and store them into
the buffer I<dst>. The actual number of bytes copied is stored in I<actualp>.
Error chunks and end-of-file chunks passing down the stream will set
the corresponding flags and can be queried with B<sio_flag>.
=item sio_rc_t B<sio_write>(sio_t *I<sio>, char *I<src>, size_t I<n>, size_t *<actualp>);
Copy I<n> bytes from buffer I<src> to stream object I<sio>. I<actualp>
stores the actual number of bytes copied in case the stream has no
place for the whole buffer. The bytes will be labeled as user data
inside the stream.
=item sio_rc_t B<sio_push>(sio_t *I<sio>);
Push an end-of-file chunk up the stream I<sio> to trigger stages
to flush internal buffers. It depends on the stages wether they
honor this request.
=item sio_rc_t B<sio_flag>(sio_t *I<sio>, sio_flag_t I<fl>);
Query internal flags of the stream I<sio>. Currently you can
specify the flags B<SIO_FLAG_ERROR> and B<SIO_FLAG_EOF>. The
return value is either SIO_TRUE or SIO_FALSE.
=item int B<sio_clearflag>(sio_t *I<sio>, sio_flag_t I<fl>);
Clear internal flags of the stream I<sio>. Currently you can
specify the flags B<SIO_FLAG_ERROR> and B<SIO_FLAG_EOF>. The
function returns the previous state of the flag.
=back
=head2 Stage Operations
=over 4
=item sio_rc_t B<sio_create_stage>(sio_t *I<sio>, sio_module_t *<siom>, sio_stage_t **I<siosp>);
Create a stage object for processing data in stream I<sio> using methods
from module I<siom>. The object is stored in I<siosp> on success.
=item sio_rc_t B<sio_destroy_stage>(sio_t *I<sio>, sio_stage_t *I<sios>);
Destroy the stage object I<sios>. The object is invalid after this call
succeeded.
=item sio_rc_t B<sio_configure_stage>(sio_t *I<sio>, sio_stage_t *I<sios>, void *I<o>, void *I<v>);
Pass additional data to stage object I<sios>. The data is specific to
the stage and passed as two void pointers. Most stages will cast
I<o> to a const char * and interpret it as a parameter name and will
cast I<v> to a pointer to the value of that parameter.
=back
=head2 Error Handling
=over 4
=item const char *B<sio_error>(sio_rc_t I<rv>);
Retrieve a string that describes the return code I<rv> in english.
=back
=head2 B<Internal Stage Operations>:
The I<sio_label> method is used internally by stages that implement
a standard I/O model where data and signalling events (error, end-of-file)
are sent synchronously within the data stream.
=over 4
=item sio_rc_t B<sio_label>(sio_t *I<sio>, sio_labelnum_t I<ln>, al_label_t *<labelp>);
Map the generic B<OSSP sio> data label number I<ln> to a B<OSSP al> label
value and store the result in I<labelp>.
=back
A stage module implements the following functions and exports them
in a global I<sio_module_t> variable which is declared as following:
typedef struct {
const char *name;
sio_rc_t (*init) (sio_t *, void **);
sio_rc_t (*configure) (sio_t *, void *, void *, void *);
sio_rc_t (*cleanup) (sio_t *, void *);
sio_rc_t (*openr) (sio_t *, al_t *, void *);
sio_rc_t (*closer) (sio_t *, al_t *, void *);
sio_rc_t (*openw) (sio_t *, al_t *, void *);
sio_rc_t (*closew) (sio_t *, al_t *, void *);
sio_rc_t (*input) (sio_t *, al_t *, void *);
sio_rc_t (*output) (sio_t *, al_t *, void *);
sio_rc_t (*shutdown) (sio_t *, void *);
} sio_module_t;
Applications reference this variable to create stage intances with
B<sio_create_stage>.
=over 4
=item sio_rc_t B<sios-E<gt>init>(sio_t *I<sio>, void **I<up>);
Initialize a stage, a pointer to private per-instance data
will be stored in I<up> and will be passed to the subsequent
methods.
=item sio_rc_t B<sios-E<gt>configure>(sio_t *I<sio>, void *I<u>, void *I<o>, void *I<v>);
Pass additional data to a stage. The data is specific to
the stage and passed as two void pointers. Most stages will cast
I<o> to a const char * and interpret it as a parameter name and will
cast I<v> to a pointer to the value of that parameter.
=item sio_rc_t B<sios-E<gt>cleanup>(sio_t *I<sio>, void *I<u>);
Deinitialize a stage and free all ressources allocated at init time.
=item sio_rc_t B<sios-E<gt>openr>(sio_t *I<sio>, al_t *I<al>, void *I<u>);
Open a stage for reading. The stage will be attached at the stream
top on success.
=item sio_rc_t B<sios-E<gt>closer>(sio_t *I<sio>, al_t *I<al>, void *I<u>);
Close a stage for reading and free all ressources allocated at openr time.
=item sio_rc_t B<sios-E<gt>openw>(sio_t *I<sio>, al_t *I<al>, void *I<u>);
Open a stage for writing. The stage will be attached at the stream
top on success.
=item sio_rc_t B<sios-E<gt>closew>(sio_t *I<sio>, al_t *I<al>, void *I<u>);
Close a stage for writing and free all ressources allocated at openw time.
=item sio_rc_t B<sios-E<gt>input>(sio_t *I<sio>, al_t *I<al>, void *I<u>);
Transport data downstream to a reader, if there is no data then pull
more from upstream stages.
The special return codes B<SIO_UPSTREAM> and B<SIO_DOWNSTREAM> can be
used to force either direction, the standard code B<SIO_OK> lets the
scheduler determine the direction depending on the input assembly line.
The special return code B<SIO_XSTREAM> will schedule the corresponding
output side of the stream and the code B<SIO_LOOP> instructs the
scheduler to call the same stage again.
Any other code will abort the scheduler and leave the stream in an
inconsistent state.
=item sio_rc_t B<sios-E<gt>output>(sio_t *I<sio>, al_t *I<al>, void *I<u>);
Transport data upstream from a writer. if there is no data then request
more data from downstream stages.
The special return codes B<SIO_UPSTREAM> and B<SIO_DOWNSTREAM> can be
used to force either direction, the standard code B<SIO_OK> lets the
scheduler determine the direction depending on the input assembly line.
The special return code B<SIO_XSTREAM> will schedule the corresponding
input side of the stream and the code B<SIO_LOOP> instructiosn the
scheduler to call the same stage again.
Any other code will abort the scheduler and leave the stream in an
inconsistent state.
=item sio_rc_t B<sios-E<gt>shutdown>(sio_t *I<sio>, void *I<u>);
Prepare for a close operation. If this function returns SIO_OK
then the scheduler will go one more round through the writer
and the reader so that final handshakes can be delivered to
upstream modules.
NOTE: The shutdown operation is optional, modules may specify
a NULL pointer here.
=back
=head1 THEORY
A B<OSSP sio> stream is a pair of two half-duplex data pipes. Each data
pipe consists of an B<OSSP al> assembly line and a chain of processing
nodes called stages.
An input operation schedules the stage on the input half, each stage
passes data from the top-most stage downstream to a reader.
The top-most stage needs to a be a producer, e.g.
an object corresponding to a file descriptor open for reading.
An output operation schedules the stage on the output half, each stage
passes data upstream from a writer to the top-most stage.
The top-most stage needs to be a consumer, e.g.
an object corresponding to a file descriptor open for writing.
A stage can implement complex protocols by telling the scheduler
to cross sides and invoke the output stage on input operations
and vice versa. The stage must then implement a state machine
that directs the scheduler back to the originating side to
keep I/O semantics consistent.
=head2 Stage Modules
=over 4
=item Private data per instance
Private data needs to be encapsuled into a structure that can
be referenced with a single pointer.
typedef struct {
int a;
float b;
char *c;
} private_t;
sio_rc_t XXX_init(sio_t *sio, void **up) {
private_t *my;
my = (private_t *)malloc(sizeof(private_t));
if (my == NULL)
return SIO_ERR_MEM;
/* initialize private data... */
/* pass back to SIO, the pointer will be passed
* to every other function in the module.
*/
*up = my;
return SIO_OK;
}
The init function is called when the stage is created with
B<sio_create_stage> and a corresponding cleanup function is called
when the stage is detroyed with B<sio_destroy_stage>.
sio_rc_t XXX_cleanup(sio_t *sio, void *u)
{
private_t *my = (private_t *)u;
/* deinitialize private data... */
/* free memory */
free(my);
return SIO_OK;
}
Most stages require parameters to complete initialization. These
parameters are passed through B<sio_configure_stage> to the
configure function of the module.
sio_rc_t XXX_configure(sio_t *sio, void *u, void *o, void *v)
{
private_t *my = (private_t *)u;
const char *name = (const char *)o;
if (strcmp(name, "parameter1") == 0) {
/* fetch parameter by reference */
int par1 = *(int *)v;
/* store parameter1 */
my->a = par1;
} elsif (strcmp(name, "parameter2") == 0) {
/* fetch parameter by reference */
float par2 = *(float *)v;
/* store parameter2 */
my->b = par2;
} elsif (strcmp(name, "parameter2") == 0) {
/* XXX - fetch parameter by value */
char *par3 = (char *)v;
/* store parameter2 */
my->c = par3;
} else {
return SIO_ERR_ARG;
}
return SIO_OK;
}
=item Attaching and detaching stages
A data pipe consists of one or more processing nodes, that operate
on a shared B<OSSP al> assembly line. B<OSSP sio> will call the
corresponding B<sios-E<gt>openr> and B<sios-E<gt>openw> functions
before attaching a node to the input pipe or the output pipe
respectively.
The functions B<sios-E<gt>closer> and B<sios-E<gt>closew> are
then called after detaching a node.
=item Input and Output
The input and output functions of all stages on a data pipe are
called by a scheduler which is started by calling the API functions
B<sio_input> and B<sio_output>. The scheduler first calls the
bottom-most stage which then returns a special status code
to direct the scheduler either upstream to the next stage
or downstream to the previous stage.
The standard action of an input stage is to push data downstream:
if (can deliver data)
put data on assembly line
return SIO_DOWNSTREAM
else
return SIO_UPSTREAM
The standard action of an output stage is the reverse to push
data upstream:
if (can deliver data)
put data on assembly line
return SIO_UPSTREAM
else
return SIO_DOWNSTREAM
In many situations this can be handled by the scheduler:
put data on assembly line
return SIO_OK
which then interprets any data on the assembly line as
ready to be delivered.
=item Top-Most Stage
The top-most stage behaves differently since it has no
upstream peer to send data to or fetch data from. Instead
it needs to discard output data and block until it can
deliver data. The only valid return code is therefore
SIO_DOWNSTREAM.
=item Standard Input and Output
The basic <OSSP sio> operation does not support failure
modes, in particular: end-of-file conditions and errors.
This is implemented on top using the <OSSP al> data labelling
capability.
=item Full-Duplex Protocol Operation
The two halfs of a <OSSP sio> stream are separated by design:
B<sio_input> schedules all stages attached to the input
assembly line and B<sio_output> schedules all stages attached
to the output assembly line.
A full-duplex protocol however needs to mix input and output
operations. This can be done by telling the scheduler to
cross sides with the result code SIO_XSTREAM and by maintaining
a state machine that directs the scheduler back to the
originating track.
Example:
sio_rc_t XXX_openr(sio_t *sio, al_t *al, void *u)
{
private_t *my = (private_t *)u;
...
/* cache reference to input assembly line */
my->al_in = al;
my->state = INIT;
...
}
sio_rc_t XXX_openw(sio_t *sio, al_t *al, void *u)
{
private_t *my = (private_t *)u;
...
/* cache reference to output assembly line */
my->al_out = al;
my->state = INIT;
...
}
sio_rc_t XXX_input(sio_t *sio, al_t *al, void *u)
{
return XXX_protocol(sio, (private_t *)u, 0);
}
sio_rc_t XXX_output(sio_t *sio, al_t *al, void *u)
{
return XXX_protocol(sio, (private_t *)u, 1);
}
sio_rc_t XXX_protocol(sio_t *sio, private_t *my, int isoutput)
{
switch (my->state) {
case INIT:
my->isoutput = isoutput;
my->state = NEXT;
...
/* protocol needs global assembly lines,
* we need to put aside any data */
XXX_preserve_al(my);
/* protocol starts with output */
if (isoutput) {
return SIO_LOOP; /* next state on this side */
} else
return SIO_XSTREAM; /* next state on other side */
case NEXT:
....
case INPUT:
al_append_bytes(my->al_in, ...);
...
case OUTPUT:
al_append_bytes(my->al_out, ...);
...
case LAST:
...
my->state = DONE;
/* restore global asembly lines */
XXX_restore_output(my);
if (isoutput != my->isoutput)
return SIO_XSTREAM;
else
return SIO_LOOP;
case DONE:
...
}
...
}
=back
=head1 SEE ALSO
B<OSSP al>
=head1 HISTORY
B<OSSP sio> was invented in October 2002 by Michael van Elst
E<lt>mlelstv@dev.de.cw.netE<gt> under contract with Cable & Wireless
Germany E<lt>http://www.cw.com/deE<gt> for use inside the OSSP project
E<lt>http://www.ossp.org/E<gt>.
=head1 AUTHORS
Michael van Elst
mlelstv@dev.de.cw.net
=cut