IOSimpleStreamsImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
MBrown on October 25, 1983 1:24 pm
Russ Atkinson (RRA) July 25, 1985 0:07:13 am PDT
TO DO
remove "debugRopeFromROS" flag when no longer needed
DIRECTORY
Basics USING [RawChars, UnsafeBlock],
IO,
IOUtils,
RefText,
Rope USING [AppendChars, Concat, Fetch, FromRefText, InlineFetch, InlineLength, Length, ROPE],
RuntimeError USING [BoundsFault];
IOSimpleStreamsImpl: CEDAR PROGRAM
IMPORTS IO, IOUtils, RefText, Rope, RuntimeError
EXPORTS IO
= BEGIN
STREAM: TYPE = IO.STREAM;
ROPE: TYPE = Rope.ROPE;
StreamProcs: TYPE = IO.StreamProcs;
Nowhere Stream
noWhereStream: PUBLIC STREAMIO.CreateStream[IO.CreateStreamProcs[
variety: $output, class: $Null, putChar: NoWherePutChar, close: NoWhereClose], NIL];
NoWherePutChar: PROC [self: STREAM, char: CHAR] = {};
NoWhereClose: PROC [self: STREAM, abort: BOOL] = {};
NoInput Stream
noInputStream: PUBLIC STREAMIO.CreateStream[
streamProcs: IO.CreateStreamProcs[
variety: $input, class: $Null,
getChar: NoInputGetChar,
endOf: NoInputEndOf,
backup: NoInputBackup,
close: NoInputClose],
streamData: NIL];
NoInputGetChar: PROC [self: STREAM] RETURNS [CHAR] = {
ERROR IO.EndOfStream[self] };
NoInputBackup: PROC [self: STREAM, char: CHAR] = {
ERROR IO.Error[$IllegalBackup, self] };
NoInputEndOf: PROC [self: STREAM] RETURNS [BOOL] = {
RETURN[TRUE] };
NoInputClose: PROC [self: STREAM, abort: BOOL] = { };
Input Stream from ROPE (RIS)
InputRopeStreamData: TYPE = REF InputRopeStreamRecord;
InputRopeStreamRecord: TYPE = RECORD [
rope: ROPE,
length: INT,
index: INT
];
InputRopeStreamProcs: REF StreamProcs = IO.CreateStreamProcs[
variety: $input, class: $ROPE,
getBlock: InputRopeStreamGetBlock,
unsafeGetBlock: InputRopeStreamUnsafeGetBlock,
getChar: InputRopeStreamGetChar,
endOf: InputRopeStreamEndOf,
reset: InputRopeStreamReset,
getIndex: InputRopeStreamGetIndex,
setIndex: InputRopeStreamSetIndex,
getLength: InputRopeStreamGetLength,
backup: InputRopeStreamBackup
];
RIS: PUBLIC PROC [rope: ROPE, oldStream: STREAM]
RETURNS [stream: STREAM] = {
IF oldStream = NIL THEN GOTO newStream;
WITH oldStream.streamData SELECT FROM
data: InputRopeStreamData => {
data^ ← [rope: rope, length: rope.InlineLength[], index: 0];
oldStream.streamProcs ← InputRopeStreamProcs;
RETURN[oldStream] };
ENDCASE => GOTO newStream;
EXITS newStream => RETURN[IO.CreateStream[
InputRopeStreamProcs, NEW[InputRopeStreamRecord ← [
rope: rope, length: rope.Length[], index: 0]]]];
};
InputRopeStreamGetBlock: PROC [self: STREAM, block: REF TEXT, startIndex: NAT, count: NAT] RETURNS [nBytesRead: NAT] = {
data: InputRopeStreamData = NARROW[self.streamData];
stop: NATMIN[CARDINAL[startIndex]+CARDINAL[count], NAT.LAST];
nBytes: NAT = MIN [block.maxLength, stop] - startIndex;
block.length ← startIndex;
nBytesRead ← Rope.AppendChars[block, data.rope, data.index, nBytes];
data.indexdata.index + nBytesRead;
block.length ← startIndex + nBytesRead;
RETURN[nBytesRead];
};
InputRopeStreamUnsafeGetBlock: UNSAFE PROC [self: STREAM, block: Basics.UnsafeBlock]
RETURNS [nBytesRead: INT ← 0] = UNCHECKED {
data: InputRopeStreamData = NARROW[self.streamData];
start: INT ← data.index;
IF block.startIndex < 0 OR block.count < 0 THEN ERROR RuntimeError.BoundsFault;
FOR i: INT IN [0 .. block.count) WHILE data.index < data.length DO
LOOPHOLE[block.base, LONG POINTER TO Basics.RawChars][block.startIndex+i] ← data.rope.InlineFetch[data.index];
data.index ← data.index + 1;
ENDLOOP;
RETURN [data.index - start];
};
InputRopeStreamGetChar: PROC [self: STREAM] RETURNS [char: CHAR] = {
data: InputRopeStreamData = NARROW[self.streamData];
IF data.index >= data.length THEN ERROR IO.EndOfStream[self];
char ← data.rope.InlineFetch[data.index];
data.index ← data.index + 1;
};
InputRopeStreamEndOf: PROC [self: STREAM] RETURNS [BOOL] = {
data: InputRopeStreamData = NARROW[self.streamData];
RETURN[data.index >= data.length];
};
InputRopeStreamBackup: PROC [self: STREAM, char: CHAR] = {
data: InputRopeStreamData = NARROW[self.streamData];
IF data.index = 0 OR data.rope.Fetch[data.index-1] # char THEN
ERROR IO.Error[$IllegalBackup, self];
data.index ← data.index - 1;
};
InputRopeStreamReset: PROC [self: STREAM] = {
data: InputRopeStreamData = NARROW[self.streamData];
data.index ← data.length;
};
InputRopeStreamGetIndex: PROC [self: STREAM] RETURNS [INT] = {
data: InputRopeStreamData = NARROW[self.streamData];
RETURN[data.index];
};
InputRopeStreamSetIndex: PROC [self: STREAM, index: INT] = {
data: InputRopeStreamData = NARROW[self.streamData];
IF index NOT IN [0 .. data.length] THEN ERROR IO.Error[$BadIndex, self];
data.index ← index;
};
InputRopeStreamGetLength: PROC [self: STREAM] RETURNS [length: INT] = {
data: InputRopeStreamData = NARROW[self.streamData];
RETURN[data.length];
};
Output Stream to ROPE (ROS)
OutputRopeStreamData: TYPE = REF OutputRopeStreamRecord;
OutputRopeStreamRecord: TYPE = RECORD [
rope: ROPE,
text: REF TEXT
];
OutputRopeStreamProcs: REF StreamProcs = IO.CreateStreamProcs[
variety: $output, class: $ROPE,
putChar: OutputRopeStreamPutChar,
eraseChar: OutputRopeStreamEraseChar,
reset: OutputRopeStreamReset,
getLength: OutputRopeStreamGetLength,
getIndex: OutputRopeStreamGetIndex,
close: OutputRopeStreamClose
];
ROS: PUBLIC PROC [oldStream: STREAM]
RETURNS [stream: STREAM] = {
IF oldStream = NIL THEN GOTO newStream;
WITH oldStream.streamData SELECT FROM
data: OutputRopeStreamData => {
data.rope ← NIL;
IF data.text = NIL THEN data.text ← RefText.ObtainScratch[100]
ELSE data.text.length ← 0;
oldStream.streamProcs ← OutputRopeStreamProcs;
RETURN[oldStream] };
ENDCASE => GOTO newStream;
EXITS newStream => RETURN[IO.CreateStream[
OutputRopeStreamProcs, NEW[OutputRopeStreamRecord ← [
rope: NIL, text: RefText.ObtainScratch[100]]]]];
};
OutputRopeStreamPutChar: PROC [self: STREAM, char: CHAR] = {
data: OutputRopeStreamData = NARROW[self.streamData];
text: REF TEXT = data.text;
IF text.length = text.maxLength THEN {
data.rope ← data.rope.Concat[Rope.FromRefText[text]];
text.length ← 0 };
text[text.length] ← char;
text.length ← text.length + 1;
};
OutputRopeStreamEraseChar: PROC [self: STREAM, char: CHAR] = {
data: OutputRopeStreamData = NARROW[self.streamData];
IF data.text.length # 0 THEN data.text.length ← data.text.length-1;
};
OutputRopeStreamReset: PROC [self: STREAM] = {
data: OutputRopeStreamData = NARROW[self.streamData];
data.rope ← NIL;
data.text.length ← 0;
};
OutputRopeStreamGetLength: PROC [self: STREAM] RETURNS [length: INT] = {
data: OutputRopeStreamData = NARROW[self.streamData];
RETURN[data.text.length + data.rope.Length[]];
};
OutputRopeStreamGetIndex: PROC [self: STREAM] RETURNS [index: INT] = {
RETURN[self.GetLength[]];
};
debugRopeFromROS: BOOLTRUE; -- crock until important clients get fixed
RopeFromROS: PUBLIC PROC [self: STREAM, close: BOOL] RETURNS [ROPE] = {
data: OutputRopeStreamData = NARROW[self.streamData];
result: ROPEIF data.text.length = 0 AND debugRopeFromROS THEN NIL
ELSE Rope.FromRefText[data.text];
IF close
THEN {
RefText.ReleaseScratch[data.text];
data.text ← NIL;
self.streamProcs ← IOUtils.closedStreamProcs;
IF data.rope # NIL THEN { result ← data.rope.Concat[result]; data.rope ← NIL }
}
ELSE {
data.text.length ← 0;
result ← data.rope ← data.rope.Concat[result];
};
RETURN[result];
};
OutputRopeStreamClose: PROC [self: STREAM, abort: BOOL] = {
data: OutputRopeStreamData = NARROW[self.streamData];
data.rope ← NIL;
RefText.ReleaseScratch[data.text];
data.text ← NIL;
self.streamProcs ← IOUtils.closedStreamProcs;
};
Input Stream from REF TEXT (TIS)
InputTextStreamData: TYPE = REF InputTextStreamRecord;
InputTextStreamRecord: TYPE = RECORD [
text: REF READONLY TEXT,
index: NAT
];
InputTextStreamProcs: REF StreamProcs = IO.CreateStreamProcs[
variety: $input, class: $TEXT,
getChar: InputTextStreamGetChar,
endOf: InputTextStreamEndOf,
reset: InputTextStreamReset,
getIndex: InputTextStreamGetIndex,
setIndex: InputTextStreamSetIndex,
getLength: InputTextStreamGetLength,
backup: InputTextStreamBackup
];
TextFromTOS: PUBLIC PROC [self: STREAM] RETURNS [REF TEXT] = {
Applies only to the result of a TOS call. Returns the entire output sequence as a ref text. Does not close the stream, so may be called repeatedly, but same ref text may be returned several times (and is mutable).
RETURN[NARROW[self.streamData]];
};
TIS: PUBLIC PROC [text: REF READONLY TEXT, oldStream: STREAM]
RETURNS [STREAM] = {
IF oldStream = NIL THEN GOTO newStream;
WITH oldStream.streamData SELECT FROM
data: InputTextStreamData => {
data^ ← [text: text, index: 0];
oldStream.streamProcs ← InputTextStreamProcs;
RETURN[oldStream] };
ENDCASE => GOTO newStream;
EXITS newStream => RETURN[IO.CreateStream[
InputTextStreamProcs, NEW[InputTextStreamRecord ← [
text: text, index: 0]]]];
};
InputTextStreamGetChar: PROC [self: STREAM] RETURNS [char: CHAR] = {
data: InputTextStreamData = NARROW[self.streamData];
IF data.index >= data.text.length THEN ERROR IO.EndOfStream[self];
char ← data.text[data.index];
data.index ← data.index + 1;
};
InputTextStreamEndOf: PROC [self: STREAM] RETURNS [BOOL] = {
data: InputTextStreamData = NARROW[self.streamData];
RETURN[data.index >= data.text.length];
};
InputTextStreamBackup: PROC [self: STREAM, char: CHAR] = {
data: InputTextStreamData = NARROW[self.streamData];
IF data.index = 0 OR data.text[data.index-1] # char THEN
ERROR IO.Error[$IllegalBackup, self];
data.index ← data.index - 1;
};
InputTextStreamReset: PROC [self: STREAM] = {
data: InputTextStreamData = NARROW[self.streamData];
data.index ← data.text.length;
};
InputTextStreamGetIndex: PROC [self: STREAM] RETURNS [INT] = {
data: InputTextStreamData = NARROW[self.streamData];
RETURN[data.index];
};
InputTextStreamSetIndex: PROC [self: STREAM, index: INT] = {
data: InputTextStreamData = NARROW[self.streamData];
IF index NOT IN [0 .. data.text.length] THEN ERROR IO.Error[$BadIndex, self];
data.index ← index;
};
InputTextStreamGetLength: PROC [self: STREAM] RETURNS [length: INT] = {
data: InputTextStreamData = NARROW[self.streamData];
RETURN[data.text.length];
};
Output Stream to REF TEXT (TOS)
OutputTextStreamProcs: REF StreamProcs = IO.CreateStreamProcs[
variety: $output, class: $TEXT,
putChar: OutputTextStreamPutChar,
eraseChar: OutputTextStreamEraseChar,
reset: OutputTextStreamReset,
getLength: OutputTextStreamGetLength,
getIndex: OutputTextStreamGetIndex,
close: NIL
];
TOS: PUBLIC PROC [text: REF TEXT, oldStream: STREAM]
RETURNS [stream: STREAM] = {
IF oldStream = NIL
THEN oldStream ← IO.CreateStream[OutputTextStreamProcs, NIL]
ELSE {
oldStream.streamProcs ← OutputTextStreamProcs;
WITH oldStream.streamData SELECT FROM
oldText: REF TEXT => IF text = NIL THEN text ← oldText;
ENDCASE;
};
IF text = NIL THEN text ← NEW[TEXT[100]];
text.length ← 0;
oldStream.streamData ← text;
RETURN[oldStream];
};
OutputTextStreamPutChar: PROC [self: STREAM, char: CHAR] = {
self.streamData ← RefText.InlineAppendChar[to: NARROW[self.streamData], from: char];
};
OutputTextStreamEraseChar: PROC [self: STREAM, char: CHAR] = {
text: REF TEXT = NARROW[self.streamData];
IF text.length > 0 THEN text.length ← text.length - 1;
};
OutputTextStreamReset: PROC [self: STREAM] = {
NARROW[self.streamData, REF TEXT].length ← 0;
};
OutputTextStreamGetLength: PROC [self: STREAM] RETURNS [length: INT] = {
RETURN[NARROW[self.streamData, REF TEXT].length];
};
OutputTextStreamGetIndex: PROC [self: STREAM] RETURNS [index: INT] = {
RETURN[NARROW[self.streamData, REF TEXT].length];
};
END.
Change Log
Changed by MBrown on October 25, 1983 1:24 pm
Fixed output rope stream and output text stream to implement EraseChar. Streams are prepared for Close to replace the stream procs with IOUtils.closedStreamProcs. RopeFromROS is somewhat more efficient in the normal case. RopeFromROS returns NIL when possible (work around bug in interpreter).
Russ Atkinson (RRA) July 22, 1985 8:51:08 pm PDT
Fixed RIS to use Rope.AppendChars for input, sped up UnsafeGetBlock too