OSSP sio - Stream I/O OSSP SIO .9.0 (06-Feb-2003) sio_rc_t, sio_mode_t, sio_flag_t, sio_t, sio_stage_t, sio_module_t, sio_labelnum_t. sio_create, sio_destroy, sio_input, sio_output, sio_read, sio_write, sio_push, sio_flag, sio_clearflag. sio_create_stage, sio_destroy_stage, sio_configure_stage, sio_attach, sio_detach. sio_error. sio_label, sios->init, sios->configure, sios->cleanup, sios->openr, sios->closer, sios->openw, sios->closew, sios->input, sios->output, sios->shutdown. OSSP sio defines an abstract type of a pair of half-duplex data pipes. It provides the following key features: OSSP sio uses six data types in its API: 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 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 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 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. This is an opaque pointer type representing a processing node on a data pipe. 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. 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 OSSP sio provides a bunch of API functions, all modelled after the same prototype: "sio_rc_t sio_name(sio__t *,...)". This means every function returns sio_rc_t to indicate its success (SIO_OK) or failure (SIO_ERR_XXX) by returning a return code (the corresponding describing text can be determined by passing this return code to sio_error). Each function name starts with the common prefix sio_ and receives a sio_t object on which it operates as its first argument.

Stream Operations
Create a stream object. The object is stored in siop on success. Destroy the stream object sio. The object is invalid after this call succeeded. Pull data from stream object sio, the data is appended to the assembly line al and will contain no more than 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. Push data to stream object sio, the data is spliced from the assembly line al. When the stream has no place for all data in the assembly line then part (or all) of it will stay in al. Copy up to n user data bytes from stream sio and store them into the buffer dst. The actual number of bytes copied is stored in actualp. Error chunks and end-of-file chunks passing down the stream will set the corresponding flags and can be queried with sio_flag. Copy n bytes from buffer src to stream object sio. 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. Push an end-of-file chunk up the stream sio to trigger stages to flush internal buffers. It depends on the stages wether they honor this request. Query internal flags of the stream sio. Currently you can specify the flags SIO_FLAG_ERROR and SIO_FLAG_EOF. The return value is either SIO_TRUE or SIO_FALSE. Clear internal flags of the stream sio. Currently you can specify the flags SIO_FLAG_ERROR and SIO_FLAG_EOF. The function returns the previous state of the flag.

Stage Operations
Create a stage object for processing data in stream sio using methods from module siom. The object is stored in siosp on success. Destroy the stage object sios. The object is invalid after this call succeeded. Pass additional data to stage object sios. The data is specific to the stage and passed as two void pointers. Most stages will cast o to a const char * and interpret it as a parameter name and will cast v to a pointer to the value of that parameter.

Error Handling
Retrieve a string that describes the return code rv in english.

Internal Stage Operations:
The 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. Map the generic OSSP sio data label number ln to a OSSP al label value and store the result in labelp. A stage module implements the following functions and exports them in a global 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 sio_create_stage. Initialize a stage, a pointer to private per-instance data will be stored in up and will be passed to the subsequent methods. Pass additional data to a stage. The data is specific to the stage and passed as two void pointers. Most stages will cast o to a const char * and interpret it as a parameter name and will cast v to a pointer to the value of that parameter. Deinitialize a stage and free all ressources allocated at init time. Open a stage for reading. The stage will be attached at the stream top on success. Close a stage for reading and free all ressources allocated at openr time. Open a stage for writing. The stage will be attached at the stream top on success. Close a stage for writing and free all ressources allocated at openw time. Transport data downstream to a reader, if there is no data then pull more from upstream stages. The special return codes SIO_UPSTREAM and SIO_DOWNSTREAM can be used to force either direction, the standard code SIO_OK lets the scheduler determine the direction depending on the input assembly line. The special return code SIO_XSTREAM will schedule the corresponding output side of the stream and the code 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. Transport data upstream from a writer. if there is no data then request more data from downstream stages. The special return codes SIO_UPSTREAM and SIO_DOWNSTREAM can be used to force either direction, the standard code SIO_OK lets the scheduler determine the direction depending on the input assembly line. The special return code SIO_XSTREAM will schedule the corresponding input side of the stream and the code 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. 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. A OSSP sio stream is a pair of two half-duplex data pipes. Each data pipe consists of an 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.

Stage Modules
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 sio_create_stage and a corresponding cleanup function is called when the stage is detroyed with 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 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; } A data pipe consists of one or more processing nodes, that operate on a shared OSSP al assembly line. OSSP sio will call the corresponding sios->openr and sios->openw functions before attaching a node to the input pipe or the output pipe respectively. The functions sios->closer and sios->closew are then called after detaching a node. 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 sio_input and 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. 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. 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. The two halfs of a <OSSP sio> stream are separated by design: sio_input schedules all stages attached to the input assembly line and 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: ... } ... } OSSP al OSSP sio was invented in October 2002 by Michael van Elst <mlelstv@dev.de.cw.net> under contract with Cable & Wireless Germany <http://www.cw.com/de> for use inside the OSSP project <http://www.ossp.org/>. Michael van Elst mlelstv@dev.de.cw.net