## ## OSSP sio - Stream I/O ## Copyright (c) 2002-2005 Cable & Wireless ## Copyright (c) 2002-2005 The OSSP Project ## Copyright (c) 2002-2005 Ralf S. Engelschall ## ## 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 - Stream I/O =head1 VERSION B =head1 SYNOPSIS =over 4 =item B: sio_rc_t, sio_mode_t, sio_flag_t, sio_t, sio_stage_t, sio_module_t, sio_labelnum_t. =item B: sio_create, sio_destroy, sio_input, sio_output, sio_read, sio_write, sio_push, sio_flag, sio_clearflag. =item B: sio_create_stage, sio_destroy_stage, sio_configure_stage, sio_attach, sio_detach. =item B: sio_error. =item B: 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 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 uses six data types in its API: =over 4 =item B (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 (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 (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 (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 (Stream Stage Type) This is an opaque pointer type representing a processing node on a data pipe. =item B (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 (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 provides a bunch of API functions, all modelled after the same prototype: "C CIC<(sio__t *,>...C<)>". This means every function returns C to indicate its success (C) or failure (C) by returning a return code (the corresponding describing text can be determined by passing this return code to C). Each function name starts with the common prefix C and receives a C object on which it operates as its first argument. =head2 Stream Operations =over 4 =item sio_rc_t B(sio_t **I); Create a stream object. The object is stored in I on success. =item sio_rc_t B(sio_t *I); Destroy the stream object I. The object is invalid after this call succeeded. =item sio_rc_t B(sio_t *I, al_t *I, size_t limit, al_label_t label); Pull data from stream object I, the data is appended to the assembly line I and will contain no more than I 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_t *I, al_t *I); Push data to stream object I, the data is spliced from the assembly line I. When the stream has no place for all data in the assembly line then part (or all) of it will stay in I. =item sio_rc_t B(sio_t *I, char *I, size_t I, size_t *); Copy up to I user data bytes from stream I and store them into the buffer I. The actual number of bytes copied is stored in I. Error chunks and end-of-file chunks passing down the stream will set the corresponding flags and can be queried with B. =item sio_rc_t B(sio_t *I, char *I, size_t I, size_t *); Copy I bytes from buffer I to stream object I. I 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_t *I); Push an end-of-file chunk up the stream I to trigger stages to flush internal buffers. It depends on the stages wether they honor this request. =item sio_rc_t B(sio_t *I, sio_flag_t I); Query internal flags of the stream I. Currently you can specify the flags B and B. The return value is either SIO_TRUE or SIO_FALSE. =item int B(sio_t *I, sio_flag_t I); Clear internal flags of the stream I. Currently you can specify the flags B and B. The function returns the previous state of the flag. =back =head2 Stage Operations =over 4 =item sio_rc_t B(sio_t *I, sio_module_t *, sio_stage_t **I); Create a stage object for processing data in stream I using methods from module I. The object is stored in I on success. =item sio_rc_t B(sio_t *I, sio_stage_t *I); Destroy the stage object I. The object is invalid after this call succeeded. =item sio_rc_t B(sio_t *I, sio_stage_t *I, void *I, void *I); Pass additional data to stage object I. The data is specific to the stage and passed as two void pointers. Most stages will cast I to a const char * and interpret it as a parameter name and will cast I to a pointer to the value of that parameter. =back =head2 Error Handling =over 4 =item const char *B(sio_rc_t I); Retrieve a string that describes the return code I in english. =back =head2 B: The I 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_t *I, sio_labelnum_t I, al_label_t *); Map the generic B data label number I to a B label value and store the result in I. =back A stage module implements the following functions and exports them in a global I 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. =over 4 =item sio_rc_t Binit>(sio_t *I, void **I); Initialize a stage, a pointer to private per-instance data will be stored in I and will be passed to the subsequent methods. =item sio_rc_t Bconfigure>(sio_t *I, void *I, void *I, void *I); Pass additional data to a stage. The data is specific to the stage and passed as two void pointers. Most stages will cast I to a const char * and interpret it as a parameter name and will cast I to a pointer to the value of that parameter. =item sio_rc_t Bcleanup>(sio_t *I, void *I); Deinitialize a stage and free all ressources allocated at init time. =item sio_rc_t Bopenr>(sio_t *I, al_t *I, void *I); Open a stage for reading. The stage will be attached at the stream top on success. =item sio_rc_t Bcloser>(sio_t *I, al_t *I, void *I); Close a stage for reading and free all ressources allocated at openr time. =item sio_rc_t Bopenw>(sio_t *I, al_t *I, void *I); Open a stage for writing. The stage will be attached at the stream top on success. =item sio_rc_t Bclosew>(sio_t *I, al_t *I, void *I); Close a stage for writing and free all ressources allocated at openw time. =item sio_rc_t Binput>(sio_t *I, al_t *I, void *I); Transport data downstream to a reader, if there is no data then pull more from upstream stages. The special return codes B and B can be used to force either direction, the standard code B lets the scheduler determine the direction depending on the input assembly line. The special return code B will schedule the corresponding output side of the stream and the code B 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 Boutput>(sio_t *I, al_t *I, void *I); Transport data upstream from a writer. if there is no data then request more data from downstream stages. The special return codes B and B can be used to force either direction, the standard code B lets the scheduler determine the direction depending on the input assembly line. The special return code B will schedule the corresponding input side of the stream and the code B 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 Bshutdown>(sio_t *I, void *I); 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 stream is a pair of two half-duplex data pipes. Each data pipe consists of an B 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 and a corresponding cleanup function is called when the stage is detroyed with B. 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 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 assembly line. B will call the corresponding Bopenr> and Bopenw> functions before attaching a node to the input pipe or the output pipe respectively. The functions Bcloser> and Bclosew> 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 and B. 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 operation does not support failure modes, in particular: end-of-file conditions and errors. This is implemented on top using the data labelling capability. =item Full-Duplex Protocol Operation The two halfs of a stream are separated by design: B schedules all stages attached to the input assembly line and B 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 =head1 HISTORY B was invented in October 2002 by Michael van Elst Emlelstv@dev.de.cw.netE under contract with Cable & Wireless Germany Ehttp://www.cw.com/deE for use inside the OSSP project Ehttp://www.ossp.org/E. =head1 AUTHORS Michael van Elst mlelstv@dev.de.cw.net =cut