Oberon || Library || Module Index || Search Engine || Definition || Module

Ulm's Oberon Library:


Streams - abstraction for byte oriented input and output


(* Whence = (fromStart, fromPos, fromEnd); *)
CONST fromStart = 0; fromPos = 1; fromEnd = 2;
(* capabilities of a stream *)
CONST read = 0; write = 1; addrio = 2; bufio = 3; seek = 4; tell = 5;
CONST trunc = 6; flush = 7; close = 8; holes = 9; handler = 10;
(* BufMode = (nobuf, linebuf, onebuf, bufpool); *)
CONST nobuf = 0; linebuf = 1; onebuf = 2; bufpool = 3;

TYPE Address = LONGINT; TYPE Count = LONGINT; TYPE Whence = SHORTINT; TYPE CapabilitySet = SET; (* OF Capability *) TYPE Modes = SET; (* OF Mode; *) TYPE BufMode = SHORTINT; TYPE ErrorCode = SHORTINT; (* see below *) TYPE Stream = POINTER TO StreamRec; TYPE Message = RECORD (Objects.ObjectRec) END;

TYPE AddrIOProc = PROCEDURE (s: Stream; ptr: Address; cnt: Count) : Count; TYPE BufIOProc = PROCEDURE (s: Stream; VAR buf: ARRAY OF BYTE; off, cnt: Count) : Count; TYPE SeekProc = PROCEDURE (s: Stream; cnt: Count; whence: Whence) : BOOLEAN; TYPE TellProc = PROCEDURE (s: Stream; VAR cnt: Count) : BOOLEAN; TYPE ReadProc = PROCEDURE (s: Stream; VAR byte: BYTE) : BOOLEAN; TYPE WriteProc = PROCEDURE (s: Stream; byte: BYTE) : BOOLEAN; TYPE TruncProc = PROCEDURE (s: Stream; cnt: Count) : BOOLEAN; TYPE FlushProc = PROCEDURE (s: Stream) : BOOLEAN; TYPE CloseProc = PROCEDURE (s: Stream) : BOOLEAN; TYPE HandlerProc = PROCEDURE (s: Stream; VAR msg: Message);

TYPE Interface = POINTER TO InterfaceRec; TYPE InterfaceRec = RECORD (Objects.ObjectRec) addrread: AddrIOProc; (* read, addrio *) addrwrite: AddrIOProc; (* write, addrio *) bufread: BufIOProc; (* read, bufio *) bufwrite: BufIOProc; (* write, bufio *) read: ReadProc; (* read *) write: WriteProc; (* write *) seek: SeekProc; (* seek *) tell: TellProc; (* tell *) trunc: TruncProc; (* trunc *) flush: FlushProc; (* flush *) close: CloseProc; (* close *) handler: HandlerProc; (* handler *) END;

TYPE StreamRec = RECORD (Services.ObjectRec) (* following components are set after i/o-operations *) count: Count; (* resulting count of last operation *) errors: INTEGER; (* incremented for each error; may be set to 0 *) error: BOOLEAN; (* last operation successful? *) lasterror: ErrorCode; (* error code of last error *) eof: BOOLEAN; (* last read-operation with count=0 returned *) END;

TYPE Event = POINTER TO EventRec; TYPE EventRec = RECORD (Events.EventRec) stream: Stream; errorcode: ErrorCode; END;

VAR null: Stream; (* accepts any output; does not return input *) VAR stdin, stdout, stderr: Stream; VAR errormsg: ARRAY errorcodes OF Events.Message; VAR error: Events.EventType;

PROCEDURE Init(s: Stream; if: Interface; caps: CapabilitySet; bufmode: BufMode); PROCEDURE GetBufMode(s: Stream) : BufMode; PROCEDURE LineTerm(s: Stream; termch: BYTE); PROCEDURE Tie(in, out: Stream); PROCEDURE SetBufferPoolSize(s: Stream; nbuf: INTEGER); PROCEDURE GetBufferPoolSize(s: Stream; VAR nbuf: INTEGER); PROCEDURE Capabilities(s: Stream) : CapabilitySet;

PROCEDURE GetFlushEvent(s: Stream; VAR type: Events.EventType); PROCEDURE GetCloseEvent(s: Stream; VAR type: Events.EventType);

PROCEDURE Close(s: Stream) : BOOLEAN; PROCEDURE Release(s: Stream); PROCEDURE CloseAll;

PROCEDURE ReadByte(s: Stream; VAR byte: BYTE) : BOOLEAN; PROCEDURE ReadPart(s: Stream; VAR buf: ARRAY OF BYTE; off, cnt: Count) : BOOLEAN; PROCEDURE Read(s: Stream; VAR buf: ARRAY OF BYTE) : BOOLEAN; PROCEDURE ReadPacket(s: Stream; VAR buf: ARRAY OF BYTE; off, maxcnt: Count) : Count;

PROCEDURE WriteByte(s: Stream; byte: BYTE) : BOOLEAN; PROCEDURE WritePart(s: Stream; (* read-only *) VAR buf: ARRAY OF Byte; off, cnt: Count) : BOOLEAN; PROCEDURE Write(s: Stream; (* read-only *) VAR buf: ARRAY OF Byte) : BOOLEAN; PROCEDURE WritePartC(s: Stream; buf: ARRAY OF Byte; off, cnt: Count) : BOOLEAN; PROCEDURE WriteC(s: Stream; buf: ARRAY OF Byte) : BOOLEAN;

PROCEDURE Seek(s: Stream; offset: Count; whence: Whence) : BOOLEAN; PROCEDURE Tell(s: Stream; VAR offset: Count) : BOOLEAN; PROCEDURE GetPos(s: Stream; VAR offset: Count); PROCEDURE SetPos(s: Stream; offset: Count);

PROCEDURE Trunc(s: Stream; length: Count) : BOOLEAN;

PROCEDURE Back(s: Stream) : BOOLEAN; PROCEDURE Insert(s: Stream; byte: BYTE) : BOOLEAN;

PROCEDURE Flush(s: Stream) : BOOLEAN; PROCEDURE InputInBuffer(s: Stream) : BOOLEAN; PROCEDURE OutputInBuffer(s: Stream) : BOOLEAN; PROCEDURE OutputWillBeBuffered(s: Stream) : BOOLEAN; PROCEDURE Touch(s: Stream);

PROCEDURE Send(s: Stream; VAR message: Message);

PROCEDURE Copy(source, dest: Stream; maxcnt: Count) : BOOLEAN;


The Streams module defines an extensible abstraction for byte oriented I/O-operations and implements an efficient buffering mechanism.

A stream is a data structure which references two components: a sequence of bytes and a position. Each byte of a sequence has an address: the first byte of a stream has address 0, the second byte 1, etc. The current position is the address of the byte to be affected by the next read or write operation. Thus the current position cannot be negative. Read and write requests cause the position to advance by the returned byte count which is less or equal to the requested count. Failures of read or write operations (count value equals zero) do not modify the current position.

The length of a stream is defined to be the highest byte address plus one. The length is infinite if the highest byte address is not defined (in case of infinite streams). In normal case a current position is valid if and only if it ranges from 0 to the stream length. Streams with holes may invalidate positions within this range or allow positions beyond the stream length.

Bidirectional streams reference a communication channel and do not maintain a current position. Instead, reading a character removes it from the input queue and writing a character appends it to the output queue. Streams are bidirectional if they support read and write operations but neither seek nor tell operations.

An interface defines a set of procedures which implement a Streams-abstraction for a specific form of input and/or output. Not every interface procedure needs to be implemented. The set of implemented procedures is given by the caps parameter of Init. At least read or write must be provided. Note that the byte-wise read or write operation must always be given even if additional read/write-operations are supported (bufio or addrio). All other operations (seek, tell, trunc, close, and handler) are optional. The interface procedures should meet following specifications:

read: PROCEDURE(s: Stream; VAR byte: BYTE) : BOOLEAN;
assign the byte at the current position to byte and increment the current position by 1. Return of FALSE indicates read errors or end of stream.

write: PROCEDURE(s: Stream; byte: BYTE) : BOOLEAN;
replace the byte at the current position by byte and increment the current position by 1. Return of FALSE indicates write errors.

seek: PROCEDURE(s: Stream; cnt: Count; whence: Whence) : BOOLEAN;
sets the current position in dependence of whence and cnt to:
new position at cnt
new position at the current position + cnt
new position at the length of the stream + cnt

tell: PROCEDURE(s: Stream; VAR count: Count) : BOOLEAN;
stores the current position into count. This operation should not fail in normal case. Streams calls tell for buffered streams after opening. In case of failures Streams assumes a starting position of 0. In normal case Streams keeps track of the current position. This interface procedure is called only in case of unbuffered streams, or if Seek is called relative to the stream length (fromEnd) or if Touch is called.

flush: PROCEDURE(s: Stream) : BOOLEAN;
is called by Flush after having performed the necessary write operations. This interface procedure allows the implementation to propagate Flush operations to an underlying stream.

trunc: PROCEDURE(s: Stream; cnt: Count) : BOOLEAN;
truncate the length of the stream to cnt. Thus all bytes with addresses greater or equal to cnt are to be deleted. This call must not modify the current position.

close: PROCEDURE(s: Stream) : BOOLEAN;
is called by Close and allows final cleanup. Return of FALSE indicates errors on cleanup but does not prevent the stream from being closed.

handler: PROCEDURE(s: Stream; VAR msg: Message);
allows the implementation of extended operations which are identified and parameterized by extensions of Message.

The interface procedures of type AddrIOProc and BufIOProc must be equivalent to multiple calls of read or write (until the first failure) for buffered streams. Positive counts returned by reading procedures which are less than the requested count are not interpreted as failure. If necessary, Streams repeats reading until all requested bytes are read or a zero count is returned.

Init initializes the stream s for the interface specified by if and caps with buffering mode bufmode. Note that Init does not allocate s. Buffering mode is one of

All operations are translated to the appropriate interface procedures.
One buffer of a system dependent size is allocated to reduce the number of interface procedure calls.
Like onebuf but an automatic flush occurs if a line terminator is written (default 0AX; can be modified by LineTerm). Line buffered streams may be tied together. In case of read operations from line buffered streams the line buffered output of the associated stream gets flushed. This buffering mode is intended for interactive line oriented I/O and is much more efficient than nobuf.
A set of buffers serves as cache of the underlying implementation. The number of buffers may be retrieved and set by GetBufferPoolSize and SetBufferPoolSize.
This procedure is called by modules which implement a abstraction. Normal user programs call open procedures of abstraction implementations (e.g. UnixFiles.Open).

GetBufMode returns the buffering mode associated with s.

LineTerm allows to change the line terminator for line buffered streams. The default line terminator is newline (0AX).

Tie ties the line buffered stream in to the stream out, i.e. read operations on in cause out to be flushed. Bidirectional line buffered streams are always tied to themselves, so Tie must not be called if in is equal to out. Streams may be untied by calling Tie with out set to NIL.

GetBufferPoolSize and SetBufferPoolSize return and set the number of buffers. The number of buffers may only be modified if bufpool has been taken as buffering mode.

Capabilities returns the capabilities of the given stream.

GetFlushEvent returns an event type which will be raised before executing any flush operations (either call Flush or internal flush operations). This event type is suitable for bidirectional streams and allows to empty the input queue before the output queue gets flushed.

GetCloseEvent returns like GetFlushEvent an event type which will be raised before the stream is shut by the close operation. Streams assures that this event will be raised with a priority which is greater than the current priority (see Events). This event type allows some final operations to be called. Note that Streams protects against recursive calls of Close or Release (in this case a NestedCall error would be returned for the first call).

Close closes s. The value of s remains unchanged to allow examination of the public components (this is useful in case of errors). Release works like Close but does not return a BOOLEAN value. Note that Streams maintains a list of open streams which may need some cleanup on termination (i.e. streams which have a close interface procedure or are buffered). These streams are therefore not subject to the garbage collection as long as Close has not been called for them.

Streams supports the mechanisms of Resources:

ReadByte assigns the byte at the current position to byte and increments the current position by one. ReadByte returns FALSE in case of end of stream (current position equals the stream length) or errors. WriteByte assigns byte to the byte at the current position and increments the current position by one. ReadByte and WriteByte are much faster than Read and Write for single bytes.

ReadPart and WritePart allow to read and write parts of buf: off specifies the start position and cnt the number of bytes to be read or written. Read and Write call ReadPart and WritePart with off = 0 and cnt = LEN(buf).

Note that WritePart and Write take buf as VAR parameter to avoid the overhead of an unnecessary copy. Neither WritePart nor Write modify buf. If an arbitrary expression is to be passed, WritePartC and WriteC may be used instead.

ReadPart tries to read the given number of bytes even when multiple calls of the read interface procedure are necessary. This may not be appropriate in cases where the first read operation wouldn't block but subsequent ones. ReadPacket returns available input from the associated buffer or, in case of unbuffered streams or empty buffers, invokes one read operation. maxcnt specifies the maximal number of bytes to be read and ReadPart returns the number of bytes actually copied into buf.

Seek sets the current position in dependence of whence and cnt to:

new position at cnt
new position at the current position + cnt
new position at the length of the stream + cnt
If s is buffered then the seek-operation is possibly delayed to the next read or write operation. This can result in seek-related errors on subsequent read or write operations.

Tell returns the current position. This position can differ from the real position of the underlying implementation in case of buffered streams. Touch allows to synchronize both positions.

GetPos and SetPos work like Tell and Seek (whence = fromStart) without returning a BOOLEAN value.

Trunc truncates the length of the stream to cnt. Thus all bytes with addresses greater or equal to cnt are deleted. This call does not modify the current position.

Stream buffering allows to undo read operations. At least one successful undo operation is guaranteed. Further undo operations require seek ability. Back decrements the current position by one. Insert works like Back but causes byte to be returned on next read operation.

Flush and Touch synchronize buffered streams with the underlying implementation. Flush is useful for output streams and causes the buffer to be flushed. The current position remains unchanged. Touch calls Flush, causes any buffer contents to be forgotten, and sets the current position to that of the underlying implementation.

InputInBuffer and OutputInBuffer return TRUE if any bytes are buffered for reading or writing resp. OutputWillBeBuffered returns TRUE if the next byte passed to one of the writing procedures will be buffered.

Send passes the given message to the handler associated with s.

Copy copies from the current position of source to the current position of dest until end of file is reached or maxcnt bytes are copied (if maxcnt is non-negative).

Some streams are predefined. The null-stream accepts any output and returns read requests with zero counts. The standard streams stdin, stdout, and stderr are initialized to null but are possibly reinitialized by other modules (e.g. UnixFiles initializes them to the UNIX standard files).


Error indications besides success or failure can be read from the public components of the stream record:
gives the count of the last read or write operation.
is incremented for each error. A typical application is to set errors to 0, then to call some stream operations and finally to check errors for being positive.
represents the success of the last operation.
is set to the error code of the last failure.
is TRUE if read operations return zero counts.

Note that Read, ReadPart, Write, WritePart, WriteC, and WritePartC return TRUE only if they were able to read or write the full amount of bytes as requested. In case of partial read or writes, the count component tells how many bytes were actually read or written.

Following error codes are currently implemented:

Send was called but no handler is defined.
LineTerm must not be called for streams which are not line buffered.
Operation must not be called for unbuffered streams, e.g. Back or Insert.
Bad parameter values, e.g. wrong counts or offsets.
write error: less bytes than requested have been written.
stream is read-only.
read error: less bytes than requested have been read.
stream is write-only.
failed seek operation: e.g. seek beyond stream length.
stream is not capable of seeking.
whence value is outside of [fromStart..fromEnd].
tell operation failed.
the underlying implementation does not maintain a current position.
stream is not capable of truncating.
trunc operation failed, e.g. current position is beyond the given length.
close operation of the underlying implementation ended with errors.
the given stream is locked.
flush operation failed.

Valid error numbers range from 0 to errorcodes-1. The array errormsg contains readable error messages for all valid error numbers.

All stream related errors lead to events of type error which are passed to RelatedEvents for further handling. Modules calling Init are expected to decide whether stream related events are to be queued or to be forwarded to another object. By default, events of type error are ignored.

An assertion of Init fails in case of invalid interfaces (e.g. empty set of capabilities).


error handling
allow nonblocking I/O operations
standard disciplines for streams

Edited by: borchert, last change: 2004/05/22, revision: 1.20, converted to HTML: 2004/05/22

Oberon || Library || Module Index || Search Engine || Definition || Module