LSN020.Streams ------------------------ !topic LSN on Stream input/output $Revision: 1.1 $ !reference MS-G.3.1;3.1 !reference MS-14.2.1;3.1 !from Tucker Taft $Date: 91/11/05 18:06:42 $ !discussion The Mapping Spec says relatively little about "Stream" input/output other than to define the attribute subprograms T'WRITE and T'READ. Here is an LSN which attempts to discuss more of the issues associated with stream input/output, and provide a more detailed proposal. Stream input/output is a form of I/O popularized by the Unix operating system, where a file (or other I/O port) is seen as a sequence or stream of bytes. Streams can be text-oriented, using control characters to separate lines (or pages, heaven forbid), or binary-oriented, with record boundaries determined completely by the application, with little or no help from the underlying O/S. For Ada 9X, we are proposing that Distribution and Sequential_IO make use of standard attribute subprograms, T'WRITE and T'READ, which write a value of type T into a stream, and read a value of type T from a stream, respectively. Originally the T'WRITE and T'READ attributes were proposed for the Distribution area of the Specialized Needs Annex, but because of widespread interest, we are currently planning to move stream support into the Systems Programming Area. The T'WRITE and T'READ attributes are dependent on a "stream" class of types. All types in the stream class must provide primitives for reading and/or writing an array of "Storage_Elements" (normally bytes). A stream can be associated with an external file, with an internal buffer, with a network channel, etc. Rather than modifying Sequential_IO to support heterogeneous I/O, we include here a proposal for a Stream_IO package which allows for direct use of T'READ and T'WRITE to accomplish heterogenous I/O (see end of this LSN). The Stream_IO package would presumably also be in the Systems Programming Area of the Annex. All types in the stream class must be derived from a root stream type. Here is the package defining the root stream type: ------------------------------ with IO_Exceptions; package Stream_Support is type Storage_Element is range 0 .. 2**Storage_Unit-1; type Storage_Array is array(Positive range <>) of Storage_Element; pragma Pack(Storage_Array); -- For each different STREAM kind a new type will be derived -- from Root_Stream_Type to represent the particular structure and -- requirements of that STREAM. type Root_Stream_Type is tagged limited private; type Stream_Access is access Root_Stream_Type'CLASS; Use_Error : exception renames IO_Exceptions.Use_Error; -- Read array of storage elements from stream procedure Read( Stream : in out Root_Stream_Type; Item : out Storage_Array; Last : out Natural -- Last < Item'LAST only if end-of-stream reached ); -- Default implementation raises USE_ERROR -- indicating stream does not support input. -- Write array of storage elements into stream procedure Write( Stream : in out Root_Stream_Type; Item : in Storage_Array ); -- Default implementation raises USE_ERROR -- indicating stream does not support output. -- Align stream cursor on multiple of alignment procedure Align( Stream : in out Root_Stream_Type; Alignment : in Positive ); -- Default implementation has no effect. -- T'WRITE and T'READ are implemented in terms of the above -- dispatching operations, or in terms of C'WRITE/C'READ -- where C is a component type of T. private type Root_Stream_Type is tagged record null; -- Additional components to be added by extension end record; end Stream_Support; package Stream_Support.Buffer_Stream_Support is -- The following is a simple stream type which will be -- provided by each implementation. type Buffer_Stream (Length : Positive) is new Root_Stream_Type with record Count : Natural := 0; Buffer : Storage_Array(1..Length); end record; -- Read array of storage elements from stream procedure Read( Stream : in out Buffer_Stream; Item : out Storage_Array; Last : out Natural ); -- Last < Item'LAST only if end-of-stream reached -- (I.e., Stream.Count >= Stream.Length) -- Write array of storage elements into stream procedure Write( Stream : in out Buffer_Stream; Item : in Storage_Array ); -- USE_ERROR is raised if the buffer would overflow -- (I.e., Stream.Count + Item'LENGTH > Stream.Length) -- Adjust Count to be multiple of alignment procedure Align( Stream : in out Buffer_Stream; Alignment : in Positive ); end Stream_Support.Buffer_Stream_Support; --------------------------------------------- Predefined T'WRITE and T'READ The compiler will automatically provide implementations of T'WRITE and T'READ for all non-limited types. The default implementations may be overridden using the attribute definition clause. T'READ and T'WRITE will generally be implemented in terms of 'READ and 'WRITE operations of simpler component types, or in terms of the primitive stream operations defined in package Stream_Support. The specifications for T'WRITE and T'READ are as given in MS-G.3.1: procedure T'WRITE(STREAM : STREAM_ACCESS; VALUE : T); function T'READ(STREAM : STREAM_ACCESS); The predefined read/write operations for scalar or access type T transfer the number of storage elements which would be used for a component of an unpacked array of T. (This definition is meant to bypass concerns about register size, optimal size of stand-alone objects, etc.) For a constrained array or record which is packed or has a rep clause, the predefined read/write operations transfer sufficient storage elements to represent the Item parameter, using the specified in-memory representation. For a constrained array or record which is unpacked and has no rep clause, the predefined read/write operations consist of repeated reads/writes of the components in the canonical order (last dimension varying fastest for an array, order of component declaration for a record). Calls on the Align operation are inserted where necessary to maintain appropriate alignment. For an unconstrained array, the 'LENGTH of each dimension is first written to the stream, followed by the storage element sequence defined above for constrained arrays. Validation on T'READ The predefined T'READ operation for a record or an array may omit certain range checks on scalar subcomponents (other than discriminants), if the base type of the subcomponent does not have a default initialization. If the base type of the scalar subcomponent does have a default initialization, then full range checking must be performed as part of the read. This requirement preserves the desirable property that instances of scalar types with default type initialization are always within known bounds. T'WRITE and T'READ for Tagged Class-Wide Types The predefined 'READ and 'WRITE operations for a tagged class-wide type T'CLASS use a unique tag ID in place of the type tag which appears in the in-memory representation. Each type derived from T has a distinct tag ID, assigned at compile time so that its value does not vary from program to program which uses the type. The T'CLASS'READ operation looks up the tag ID in a table at run-time to find the appropriate type tag, or raises CONSTRAINT_ERROR if the tag ID is not found (implying the type was not linked into the reading program). Stream Representation of Exceptions The EXCEPTION_INFO (aka EXCEPTION_OCCURRENCE) type will have 'READ and 'WRITE operations, which can use a tag ID mechanism to identify the exception similar to that used for type tags. A procedure will be provided for propagating an exception given an EXCEPTION_INFO for it. Heterogeneous I/O: A general stream-oriented I/O package will be provided to support heterogeneous I/O: -------------------------------------------- with Stream_Support; use Stream_Support; with IO_Exceptions; package Stream_IO is -- This package is similar to Sequential_IO, -- Except that it reads and writes Storage_Elements -- directly or by using T'READ and T'WRITE type File_Type is limited private; type File_Mode is (In_File, Out_File, Append_File); procedure Create(File : in out File_Type; Mode : in File_Mode := Out_File; Name : in String := ""; Form : in String := ""); procedure Open(File : in out File_Type; Mode : in File_Mode; Name : in String; Form : in String := ""); procedure Close(File : in out File_Type); procedure Delete(File : in out File_Type); procedure Reset(File : in out File_Type; Mode : in File_Mode); procedure Reset(File : in out File_Type); function Mode(File : in File_Type) return File_Mode; function Name(File : in File_Type) return String; function Form(File : in File_Type) return String; function Is_Open(File : in File_Type) return Boolean; function End_Of_File(File : in File_Type) return Boolean; function Stream(File : in File_Type) return Stream_Support.Stream_Access; -- Return stream access for use with T'WRITE and T'READ. -- The following operations are equivalent -- to the analogous operations on Streams: -- Read array of storage elements from file procedure Read( File : in File_Type; Item : out Storage_Array; Last : out Natural -- Last < Item'LAST only if end-of-stream reached ); -- Raises USE_ERROR ifmode is not In_File -- Write array of storage elements into file procedure Write( File : in File_Type; Item : in Storage_Array ); -- Raises USE_ERROR if mode is not Out_File or Append_File -- Align file offset to be multiple of alignment procedure Align( File : in File_Type; Alignment : in Positive ); -- Adjust offset in File to multiple of alignment -- Operations on position within file -- These operations raise USE_ERROR if the file -- does not support repositioning. type File_Offset is range 0..; -- Offset in file, in storage units. type Base_Position is (Beginning_Of_File, Current_Position, End_Of_File); -- Base for repositioning -- Get current file position, as an offset relative to beginning of file function Position(File : in File_Type) return File_Offset; -- Set file position procedure Set_Position(File : in File_Type; Offset : in File_Offset; Relative_To : Base_Position := Beginning_Of_File); -- Get current size, in storage units function Size(File : in File_Type) return File_Offset; -- Change mode without repositioning procedure Set_Mode(File : in out File_Type; Mode : in File_Mode); -- Flush any buffers associated with file, -- without closing file or repositioning procedure Flush(File : in out File_Type); -- exceptions Status_Error : exception renames IO_Exceptions.Status_Error; Mode_Error : exception renames IO_Exceptions.Mode_Error; Name_Error : exception renames IO_Exceptions.Name_Error; Use_Error : exception renames IO_Exceptions.Use_Error; Device_Error : exception renames IO_Exceptions.Device_Error; End_Error : exception renames IO_Exceptions.End_Error; Data_Error : exception renames IO_Exceptions.Data_Error; end Stream_IO; -------------------------- The above package is intended to obviate the need for the changes originally proposed for Sequential_IO and Text_IO. The only changes currently planned for Sequential_IO and Text_IO are to add Append_File as a File_Mode, and perhaps to add a Flush operation (at least to Text_IO). Comments welcome... ========================