CreateRefStreamProcs:
PROC[
getChar: PROC[self: STREAM] RETURNS[CHAR] ← NIL,
endOf: PROC[self: STREAM] RETURNS[BOOL] ← NIL,
charsAvail: PROC[self: STREAM] RETURNS[BOOL] ← NIL,
getBlock: PROC[self: STREAM, block: REF TEXT, startIndex: NAT, stopIndexPlusOne: NAT] RETURNS[nBytesRead: NAT] ← NIL,
unsafeGetBlock: UNSAFE PROC[self: STREAM, block: UnsafeBlock] RETURNS[nBytesRead: INT] ← NIL,
putChar: PROC[self: STREAM, char: CHAR] ← NIL,
putBlock: PROC[self: STREAM, block: REF READONLY TEXT, startIndex: NAT, stopIndexPlusOne: NAT] ← NIL,
unsafePutBlock: PROC[self: STREAM, block: UnsafeBlock] ← NIL,
flush: PROC[self: STREAM] ← NIL,
reset: PROC[self: STREAM] ← NIL,
close: PROC[self: STREAM, abort: BOOL ← FALSE] ← NIL,
getIndex: PROC[self: STREAM] RETURNS [INT] ← NIL,
setIndex: PROC[self: STREAM, index: INT] ← NIL,
getLength: PROC[self: STREAM] RETURNS [length: INT] ← NIL,
setLength: PROC[self: STREAM, length: INT] ← NIL,
backup: PROC[self: STREAM, char: CHAR] ← NIL,
userAbort: PROC[self: STREAM] RETURNS[abort: BOOL] ← NIL,
setUserAbort: PROC[self: STREAM] ← NIL,
resetUserAbort: PROC[self: STREAM] ← NIL,
setEcho: PROC[self: STREAM, echoTo: STREAM] RETURNS [oldEcho: STREAM] ← NIL,
eraseChar: PROC[self: STREAM, char: CHAR] ← NIL,
currentPosition: PROC[self: STREAM] RETURNS[position: INT] ← NIL,
name: Rope.ROPE ← NIL -- used for printing the stream itself as a ref any.
]
RETURNS [REF StreamProcs];
creates a REF streamprocs, supplying appropriate defaults for the NIL procedures Intended to be performed only once for each different stream class and the result saved, i.e. all instances of this stream can share the same streamProcs.
What it means to default a stream operation
The default procedure is to first check if there is a backingStream, and if so, call the corresponding operation in the backingStream. If no backingStream is specified, the default procedure is as follows:
CharsAvail: check whether endOf was specified, and if so return ~endOf[handle], otherwise raise signal NotImplementedForThisStream as described below
EndOf: check whether charsAvail was specified, and if so return ~charsAvail[handle], otherwise raise signal NotImplementedForThisStream as described below
GetBlock: implement via n Calls on GetChar
PutBlock: implement via n calls to PutChar
Flush, Reset: Nops, do not signal
Close: if abort = NIL, call Flush, otherwise, call Reset. do not signal
Backup: replace, i.e. ambush, the stream's original StreamProcs by a different set of procedures in which getChar, getBlock, endOf, charsAvail, and reset have different definitions when the stream is in the "backed up" state. The remaining procedures call through to the original procedures. When the character has been consumed, the streams original procedures are restored. Note that this causes the stream to run more slowly only when in the backed up state.
SetEcho: similar to Backup, i.e. ambushes the getChar and getBlock procedures, causing them to also output to the indicated handle. This causes all procedure calls in the stream to run more slowly due to the extra level of procedure call and lookup;
CurrentPosition: ambushes the streams putChar and putBlock procedures, and counts the characters as they go by. Since this does not occur until the first call to CurrentPosition, characters that were output before this call will not be counted, i.e. the first call to CurrentPosition will always return 0, and subsequent calls until the first CR is seen will return the number of characters output since the first call;
EraseChar: output \ followed by the character. Defaulting EraseChar does not affect the performance of the stream on other operations.
For all other cases, the default procedure is to raise SIGNAL Signal[NotImplementedForThisStream] (i.e. client can resume) , if the corresponding operation does NOT return a value (i.e. is executed for effect only, such as putChar), or raise ERROR Error[NotImplementedForThisStream] if the corresponding operation requires a return value, such as getChar.
In general, the decision about whether or not to implement a particular operation for a stream, must be made on the basis of performance and frequency of use versus convenience For example, if the stream implements backup itself, it can include the necessary code in its own getChar, getBlock, reset, etc., so that these procedures will continue to run fast, even when the streeam is backed up. However, it is more work for the implementor. For example, viewer streams implement SetEcho, but InputRopeStream does not. Similarly, an edited stream implements backup but viewer stream does not, on the grounds that most calls to a viewer stream that involve parsing and backing up will instead be going to an an edited stream.
AmbushProcsStream:
PRIVATE PROCEDURE[self:
STREAM, streamProcs:
REF StreamProcs, streamData:
REF
ANY, reusing:
STREAM ←
NIL] ;
used to build a layered stream on top of another stream, for those cases where the desired effect is to have any program that has hold of self to see the new behaviour. Implemented by smashing self to point to the new procs and data, with a backing stream that contains the same data, procs, etc. as self originally did. if reusing is non-NIL, it is reused and no allocations are performed. Typically reusing is saved on the property list of a stream which is going to be ambushed and then unambushed repeatedly.
UnAmbushProcsStream:
PRIVATE PROCEDURE[self:
STREAM];
inverse of AmbushProcsStream, i.e. elevates the backing stream to self. Nop if no backing stream.