(*
    Title: 	Streams - Definitions
    LastEdit:	"Mon Sep 17 15:47:20 1984"
    Author: 	Mick Jordan
		Acorn Research Centre
*)

DEFINITION MODULE Streams;
(*  Streams defines an abstract object called a `Stream'
    and a set of valid operations on such objects.

    The source (for Input streams) or sink (for Output streams)
    is not defined by this module, which only provides
    generic procedures that are valid for all stream classes.
    A specific class is defined by an implementation which maps
    the generic procedures into specific operations.

    This module makes no assumptions about the type of object
    being stored on the stream; this is defined by an implementation
    and will typically be characters or bytes, but could certainly
    be words or bits.
    The 'size' of a stream element (in bits) can always by found by
    the function 'ElementSize'.

    The `StreamRep' module provides details on how to implement
    a specific stream class.
*)

FROM SYSTEM IMPORT WORD, ADDRESS;
FROM SystemTypes IMPORT LongCARDINAL, LongINTEGER;

EXPORT QUALIFIED
    Stream, StreamProcs, ErrorCode, StreamErrorCodeType,
    OptionalFunction, SeekMode,

    Initialise, Finalise, Create, Implements,
    Put, Get, Backspace, 
    Flush, Delete, ElementSize, 
    PutN, GetN, Seek, Tell;

(* EXCEPTIONs *)  (* EndOfStream, StreamError, WrongClass,
                  FunctionNotImplemented ; *)

TYPE ErrorCode  = (Success,          (* everything Ok *)
                   EndOfStream,      (* no more elements on (input) stream *)
                   StreamError,      (* stream-specific error *)
                   WrongClass);      (* wrong type of stream given
                                        to stream class implementation *)
(*  These errors are generic to all stream classes. 
    Further details on the nature of a `StreamError' can be
    found by interrogating the `StreamErrorCode' procedure exported
    by a given stream class.

    'status' will be set to Success by the relevant procedures in the package
    unless an error occurred.
*)

(* EXCEPTION EndOfStream, StreamError, WrongClass, FunctionNotImplemented; *)

TYPE OptionalFunction =
                  (OptPutN, OptGetN, OptSeek, OptTell);

TYPE StreamProcs; (* a handle on the stream class procedures *)

TYPE StreamErrorCodeType = WORD;
(*  Specific stream classes use the 'streamError' field of
    the stream block to store a handle on the actual error.
    The StreamErrorCode procedure then provides a mapping from
    this type to a more class specific type.
    The StreamErrorCodeType type avoids the client importing the WORD type.
*)

TYPE Stream = POINTER TO RECORD
                     procs : StreamProcs; (* stream class procedures *)
                    status : ErrorCode;   (* EndOfStream... etc. *)
                 extension : WORD;        (* user-level hook...  *)
              instanceData : WORD;        (* the type is class specific; *)
              streamError  : StreamErrorCodeType; (* type is class specific *)
                   END (* record *);           (* data is instance specific *)

PROCEDURE Initialise();
(*  Package Initialisation.
    It is called during the module initialisation process and should
    not need to be called by the user unless it is desired to restart
    after a call to Finalise;
*)

PROCEDURE Finalise();
(*  Closes all open streams by a call to `Delete' (see below).
    The order of closure is undefined.
*)

PROCEDURE Create(procs: StreamProcs; instanceData: WORD) : Stream;
(*  Creates a new stream instance, with "procs" defining the procedures
    to be called by the generic operations.
    "procs" and "instanceData" are stored in the allocated Stream record;
*)

PROCEDURE Delete(s : Stream) (* RAISES {StreamError} *);
(*  Closes the stream denoted by `s'.
    In order that status can be returned from the
    delete at the stream class level, 's' is not DISPOSEd.
*)

PROCEDURE ElementSize(s : Stream) : CARDINAL;
(*  returns the size in BITS of an element of stream 's'. *)

PROCEDURE Get(s: Stream): WORD (* RAISES {EndOfStream, StreamError} *);
(*  Returns the next element on stream `s'.
*)

PROCEDURE Put(s : Stream;  element : WORD) (* RAISES {StreamError} *);
(*  Outputs `element' to stream `s'. *)

PROCEDURE Flush(s : Stream) (* RAISES {StreamError} *);
(*  Flushes the (output side of) stream `s'. *)

PROCEDURE Backspace(s: Stream)
          (* RAISES {EndOfStream, StreamError} *);
(*  Performs a one element backspace on stream `s'. 
    Repeated calls are not guaranteed to have any additional effect.
*)

PROCEDURE Implements(s: Stream; f: OptionalFunction): BOOLEAN;
(*  Returns TRUE iff the optional function `f' is implemented
    by stream `s'.  This procedure must be called if you wish
    to use the procedures described below and are not sure of
    the characteristics of a particular stream instance.
*)

(*  ******************************************************************** *)
(*  The following procedures are not guaranteed to be implemented by     *)
(*  all streams.  If they are called on streams which do not implement   *)
(*  them, the package will raise the FunctionNotImplemented exception    *)

PROCEDURE GetN(s : Stream; address : ADDRESS;  nreq : CARDINAL) : CARDINAL
          (* RAISES {EndOfStream, StreamError, FunctionNotImplemented} *);
(*  Gets the next 'nreq' elements (at most) from stream `s',
    and places them at increasing addresses starting at 'address'. The size
    of the individual elements is determined by the stream implementation.
    The number of elements actually taken off is returned as result.
*)

PROCEDURE PutN(s : Stream;  address : ADDRESS;  nreq : CARDINAL) : CARDINAL
          (* RAISES {StreamError, FunctionNotImplemented} *);
(*  Outputs `nreq' elements to stream `s', from increasing addresses
    starting at 'address'. The result is the number actually put. The size
    of the individual elements is determined  by the stream implementation.
*)

TYPE SeekMode = (Beginning, Current, End);

PROCEDURE Seek(s : Stream;  seekmode : SeekMode;  offset : LongINTEGER)
          (* RAISES {StreamError, FunctionNotImplemented} *);
(*  Sets the 'next element' pointer on stream `s' to a position
    specified by offset taken relative to 'seekmode'.  
*)

PROCEDURE Tell(s : Stream;  VAR p : LongCARDINAL)
          (* RAISES {StreamError, FunctionNotImplemented} *);
(*  Sets p to the 'current element' pointer on stream `s'.
    Can be passed back to Seek if 'seekmode = beginning'.
*)

END Streams.
