----------------------------------------------------------------------------- -- -- Onions Network Streams Library -- -- O N I O N S . I N S T R E A M S -- -- S p e c -- -- Copyright (C) 1997-1998 Regents of the University of California -- -- Onions is free software; you can redistribute it and/or modify it under -- the terms of the GNU General Public License as published by the Free -- Software Foundation, with or without the single exception listed below; -- either version 2, or (at your option) any later version. Onions is -- distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -- without even the implied warranty of MERCHANTABILITY or FITNESS FOR A -- PARTICULAR PURPOSE. See the GNU General Public License for more details. -- You should have received a copy of the GNU General Public License -- distributed with Onions; see the file COPYING. If not, write to the -- Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA -- 02111-1307, USA. -- -- As a special exception, if other files instantiate generics from this -- library, or you link this library with other files to produce an -- executable, this library does not by itself cause the resulting -- executable to be covered by the GNU General Public License. This -- exception does not however invalidate any other reasons why the -- executable file might be covered by the GNU General Public License. -- -- Created in 1997 by Roy T. Fielding ----------------------------------------------------------------------------- -- -- The Onions Input Streams class defines objects that read data from one -- interface to another while minimizing the copying of data in memory. -- Multiple input stream objects can be cascaded to build an input pipe, -- where reading from the head of the pipe causes the head stream object -- to read from the next outbound stream object, and on down the line. -- One of the main features of streams is that they can filter the data -- as it passes, converting, adding to, and/or removing from the input -- data before giving it to the next stream. Since multiple streams can be -- cascaded, the complete data conversion is the sum of the individual -- data conversions performed by the stream objects. -- -- This package defines an Input Stream with a null filter. -- The child packages File, Dir, and Channel implement streams for reading -- from files, directories, and TCP/IP sockets, respectively. -- Additional child packages can implement a filter simply by -- overriding the Process procedure for the derived stream object -- and duplicating the dispatched routines that call Process. -- -- Each stream object supports dual interfaces: the Ada.Streams interface -- that uses a caller-provided Stream_Element_Array to pass data, and our -- own Bucket interface that passes the data as a list of dynamically -- allocated Iovec structures. The Ada.Streams interface requires that -- the data be copied every time it is filtered or placed in a buffer. -- The Bucket interface minimizes copying and corresponds nicely to the -- more efficient C_readv system call. -- with Ada.IO_Exceptions; with Ada.Streams; with Interfaces.C.Strings; with System.Storage_Elements; with Onions.Thin; with Onions.Buckets; use Onions.Buckets; package Onions.Instreams is type Input_Stream is new Ada.Streams.Root_Stream_Type with private; type Input_Stream_Ptr is access Input_Stream; type Input_Pipe is access all Input_Stream'Class; ----------------------------------------------------- -- Input Pipe Classwide Manipulation Operations -- ----------------------------------------------------- -- Close and free a stream pipe. -- procedure Close (Pipe : in out Input_Pipe); -- Abort_Stream should only be used if a stream is interrupted -- by the user, or an error occurs that makes the whole stream bad. -- It forces the stream closed and discards the buffered data. -- procedure Abort_Stream (Pipe : in out Input_Pipe); -- Reset is like Close, but resets the stream to the -- state it would be in if it was just created. -- procedure Reset (Pipe : in Input_Pipe); -- Drain a stream pipe's read buffers. -- procedure Drain (Pipe : in Input_Pipe; ItemList : out Bucket_List); -- Push places the old Pipe outbound of Head and sets Pipe := Head -- Head is a pipe that might consist of multiple stream objects. -- procedure Push (Pipe : in out Input_Pipe; Head : in Input_Pipe); -- Pop unreads anything in the current Pipe head's buffers, -- moves that object to Head, sets Pipe to whatever is outbound of Head, -- and then disconnects Head from that outbound stream. -- procedure Pop (Pipe : in out Input_Pipe; Head : out Input_Pipe); --------------------------------------------- -- Input Pipe Classwide Input Operations -- --------------------------------------------- -- Read obtains a list of buckets from Stream, -- usually as many as can be provided without blocking. -- Since the interface does not require data copying, it is fast. -- procedure Read (Pipe : in Input_Pipe; ItemList : out Bucket_List); -- Read_Line blocks until the stream head can return a complete line -- of input. A line is defined as the zero or more characters before -- the next End_of_Line (a CRLF sequence, bare LF, or bare CR) or -- End_of_File. The End_of_Line character(s) are discarded. -- procedure Read_Line (Pipe : in Input_Pipe; ItemList : out Bucket_List); -- Unread allows the caller to return unprocessed buckets to this -- object's processed queue buffer, usually so that the caller -- can be replaced by some other processing routine that will do -- its own read for that data. -- procedure UnRead (Pipe : in Input_Pipe; ItemList : in Bucket_List); procedure UnRead (Pipe : in Input_Pipe; Item : in Ada.Streams.Stream_Element_Array; Last : in Ada.Streams.Stream_Element_Offset); ---------------------------------------------- -- Input Pipe Classwide Status Operations -- ---------------------------------------------- -- Set_Timeout places a limit on the amount of time in milliseconds -- any atomic stream operation is allowed to block. This limit applies -- to the entire stream pipe, but is only likely to be used by the most -- outbound stream object. A value of 0 means never timeout. The call -- is propagated to the Outbound stream object. Get_Timeout retrieves -- the current stream timeout value in milliseconds. -- procedure Set_Timeout (Pipe : in Input_Pipe; Millisec : in Natural); function Get_Timeout (Pipe : in Input_Pipe) return Natural; -- Get_Error can be called after an error exception has been raised -- to get the C errno or error string associated with the original error. -- The call is propagated to the Outbound stream object. -- function Get_Error (Pipe : in Input_Pipe) return C.int; function Get_Error (Pipe : in Input_Pipe) return String; -- Bytes returns a count of the stream pipe's outbound interface, -- usually for diagnostic purposes. -- function Bytes (Pipe : in Input_Pipe) return Natural; -- Name returns a string containing a meaningful name for this stream, -- usually obtained from the most outbound stream object (because that's -- where things like file, directory, or host names are stored). -- function Name (Pipe : in Input_Pipe) return String; -------------------------------------------------------------------- -- Dispatching Stream Control Operations -- -- -- -- If a dispatching method has an implementation that invokes -- -- another primitive method, then derived classes MUST override -- -- the dispatching method or be totally screwed, since Ada95 -- -- only does dynamic invocation within classwide methods. -- -- Combine this with hidden implementation bodies and ... yes, -- -- you guessed it, all dispatching methods must be overridden -- -- by all derived types. Who needs multiple inheritance when -- -- you can have zero inheritance at double the price? *sigh* -- -------------------------------------------------------------------- -- Free the storage associated with a stream object. -- procedure Free (SP : in out Input_Stream_Ptr); -- Close a stream object and propagate the close upstream. -- procedure Close (Stream : in out Input_Stream); -- Abort_Stream should only be used if a stream is interrupted -- by the user, or an error occurs that makes the whole stream bad. -- It forces the stream closed without a flush. -- procedure Abort_Stream (Stream : in out Input_Stream); -- Reset is like Close, but resets the stream to the state -- it would be in if it was just created. It discards -- anything in its own buffers. -- procedure Reset (Stream : in out Input_Stream); -- Drain the stream by reading once from outbound and processing any -- unprocessed data as if it were the end-of-stream. -- procedure Drain (Stream : in out Input_Stream; ItemList : out Bucket_List); -- Name returns a string containing a meaningful name for this stream, -- usually obtained from the most outbound stream object (because that's -- where things like file, directory, or host names are stored). -- function Name (Stream : in Input_Stream) return String; ------------------------------------------ -- Ada.Streams Dispatching Operations -- ------------------------------------------ -- Read obtains stream elements from Stream and places them into -- the components of Item until either each component is filled or -- no elements remain on the stream. Last is set to the index of -- the last component of Item that was filled. This interface is -- defined by Ada.Streams for abstract stream operations. We won't -- use it much because it forces a full data copy when filtering. -- procedure Read (Stream : in out Input_Stream; Item : out Ada.Streams.Stream_Element_Array; Last : out Ada.Streams.Stream_Element_Offset); -- Write is defined by Ada.Streams for abstract stream operations. -- Raises Mode_Error for an Input Stream. -- procedure Write (Stream : in out Input_Stream; Item : in Ada.Streams.Stream_Element_Array); ---------------------------------- -- Data Processing Operations -- ---------------------------------- -- Process does the magic necessary to read from the upstream object -- and move the data from the Unprocessed read queue to the Processed -- read queue. In this case, there is no magic. If Everything, then -- process the entire Unprocessed buffer as if it were the end-of-stream. -- Raises End_Error if end-of-stream is encountered and nothing has been -- processed, or Status_Error if the outbound stream is not connected. -- procedure Process (Stream : in out Input_Stream; Everything : Boolean); -- Unprocess undoes the magic of Process and UnReads the data to -- the upstream object. -- procedure Unprocess (Stream : in out Input_Stream); ------------------------ -- Utility Routines -- ------------------------ -- Read_Vector will perform a system readv on an open descriptor, -- using the available read buffer list of length Buffer_Num, -- put the filled buckets into Filled_List, and tell us what -- Filled_Amt was read. If the descriptor is not ready for reading, -- it will wait up to Timeout milliseconds for data to arrive, -- or forever if Timeout = 0. If Filled_Amt = 0, then End-of-File -- has been reached. -- -- Raises Timeout_Exceeded if we have to wait longer than Timeout; -- Device_Error if anything else goes fatally wrong. -- procedure Read_Vector (Filedes : in Onions.Thin.Descriptor; Timeout : in Natural; Buffer_List : in out Bucket_List; Buffer_Num : in Natural; Filled_List : out Bucket_List; Filled_Amt : out System.Storage_Elements.Storage_Offset); ------------------ -- Exceptions -- ------------------ -- Status_Error is raised if data is put down or read from a -- stream pipe in which the end-object doesn't have anything connected -- to its outbound interface (even though it expected another stream). -- Status_Error : exception renames Ada.IO_Exceptions.Status_Error; -- Mode_Error indicates an attempt to write to an Input_Stream -- Mode_Error : exception renames Ada.IO_Exceptions.Mode_Error; -- End_Error indicates an attempt to read from a closed pipe (EOF). -- End_Error : exception renames Ada.IO_Exceptions.End_Error; -- Other standard Ada exceptions for (file) streams which might be -- raised if the stream end-object is doing device Stream_IO operations. -- Name_Error : exception renames Ada.IO_Exceptions.Name_Error; Use_Error : exception renames Ada.IO_Exceptions.Use_Error; Device_Error : exception renames Ada.IO_Exceptions.Device_Error; Data_Error : exception renames Ada.IO_Exceptions.Data_Error; -- Timeout_Exceeded indicates that no data was available to be read -- on a descriptor in the time allotted to Timeout. -- Timeout_Exceeded : exception; private type Input_Stream is new Ada.Streams.Root_Stream_Type with record Outbound : Input_Pipe; -- the stream object stack below (outbound of) this object Unprocessed : Bucket_Brigade; -- data we have read from Outbound but not yet filtered Processed : Bucket_Brigade; -- data we have read from Outbound that has been filtered and -- is waiting for a call to our Read interface Byte_Count : System.Storage_Elements.Storage_Count := 0; -- amount of data read from the outbound stack Timeout : Natural := 0; -- time (in milliseconds) to limit blocking operations System_Error : C.int := 0; -- if a Unix system call results in an error, it will set errno -- to the global constant corresponding to that error. We save -- it here (from C_errno) so that it won't get trampled in later -- system calls by the exception handler. end record; end Onions.Instreams;