IOSimpleStreamsImpl.mesa
Copyright Ó 1985, 1986, 1987, 1991, 1992 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
Michael Plass, September 23, 1991 10:55 am PDT
Carl Hauser, July 22, 1988 9:01:21 am PDT
Doug Wyatt, May 20, 1992 4:28 pm PDT
TO DO
remove "debugRopeFromROS" flag when no longer needed
adjust buffer size or Concat test in PutRope?
DIRECTORY
Basics USING [RawBytes, UnsafeBlock],
IO,
IOUtils,
RefText,
Rope USING [ROPE, AppendChars, Concat, Fetch, FromRefText, Size, Substr, Text, UnsafeMoveChars];
Input Stream from ROPE (RIS)
InputRopeStreamData: TYPE = REF InputRopeStreamRecord;
InputRopeStreamRecord:
TYPE =
RECORD [
rope: ROPE,
length: INT, -- length of rope
start: INT -- current stream index is start+bufferIndex
];
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,
close: RopeStreamClose
];
RIS:
PUBLIC
PROC [rope:
ROPE, oldStream:
STREAM]
RETURNS [
STREAM] = {
size: INT ~ Rope.Size[rope];
self: STREAM ~ IF oldStream#NIL THEN oldStream ELSE NEW[IO.STREAMRecord];
data: InputRopeStreamData ~
WITH self.streamData
SELECT
FROM
oldData: InputRopeStreamData => oldData,
ENDCASE => NEW[InputRopeStreamRecord];
smallBufSize: NAT ~ 128;
largeBufSize: NAT ~ 2048;
bufSize: NAT ~ IF size<=smallBufSize THEN smallBufSize ELSE largeBufSize;
buffer: REF TEXT ~ IF self.buffer#NIL AND self.buffer.maxLength>=bufSize
THEN self.buffer ELSE RefText.ObtainScratch[bufSize];
data ¬ [rope: rope, length: size, start: 0];
self ¬ [streamProcs: InputRopeStreamProcs, streamData: data, buffer: buffer];
RETURN[self];
};
RISNormalize:
PROC [self:
STREAM]
RETURNS [InputRopeStreamData] ~
INLINE {
Ensures that data.start is current stream index, and buffer is empty.
data: InputRopeStreamData = NARROW[self.streamData];
data.start ¬ data.start+self.bufferIndex;
self.bufferIndex ¬ self.bufferInputLength ¬ 0;
RETURN[data];
};
InputRopeStreamGetChar:
PROC [self:
STREAM]
RETURNS [char:
CHAR] = {
i: NAT ~ self.bufferIndex;
IF i < self.bufferInputLength
THEN { self.bufferIndex ¬ i+1; RETURN[self.buffer[i]] }
ELSE {
data: InputRopeStreamData ~ RISNormalize[self];
self.buffer.length ¬ 0;
self.bufferInputLength ¬ Rope.AppendChars[self.buffer, data.rope, data.start];
IF self.bufferInputLength > 0
THEN { self.bufferIndex ¬ 1; RETURN[self.buffer[0]] }
ELSE ERROR IO.EndOfStream[self];
};
};
InputRopeStreamGetBlock:
PROC [self:
STREAM, block:
REF
TEXT, startIndex:
NAT, count:
NAT]
RETURNS [nBytesRead:
NAT] = {
data: InputRopeStreamData ~ RISNormalize[self];
index: INT ~ data.start;
block.length ¬ startIndex;
nBytesRead ¬ Rope.AppendChars[buffer: block, rope: data.rope, start: index, len: count];
data.start ¬ index+nBytesRead;
};
InputRopeStreamUnsafeGetBlock:
UNSAFE
PROC [self:
STREAM, block: Basics.UnsafeBlock]
RETURNS [nBytesRead:
INT] = {
data: InputRopeStreamData ~ RISNormalize[self];
index: INT ~ data.start;
TRUSTED { nBytesRead ¬ Rope.UnsafeMoveChars[block: block, rope: data.rope, start: index] };
data.start ¬ index+nBytesRead;
};
InputRopeStreamEndOf:
PROC [self:
STREAM]
RETURNS [
BOOL] = {
data: InputRopeStreamData = NARROW[self.streamData];
RETURN[(data.start+self.bufferIndex) >= data.length];
};
InputRopeStreamBackup:
PROC [self:
STREAM, char:
CHAR] = {
data: InputRopeStreamData ~ RISNormalize[self];
Assume this is called only if IO.Backup couldn't simply decrement bufferIndex.
IF data.start>0
AND Rope.Fetch[data.rope, data.start-1]=char
THEN data.start ¬ data.start-1
ELSE ERROR IO.Error[$IllegalBackup, self];
};
InputRopeStreamReset:
PROC [self:
STREAM] = {
data: InputRopeStreamData ~ RISNormalize[self];
data.start ¬ data.length;
};
InputRopeStreamGetIndex:
PROC [self:
STREAM]
RETURNS [
INT] = {
data: InputRopeStreamData = NARROW[self.streamData];
RETURN[data.start+self.bufferIndex];
};
InputRopeStreamSetIndex:
PROC [self:
STREAM, index:
INT] = {
data: InputRopeStreamData = NARROW[self.streamData];
IF index NOT IN[0..data.length] THEN ERROR IO.Error[$BadIndex, self];
IF index
IN[data.start..data.start+self.bufferInputLength]
THEN self.bufferIndex ¬ index-data.start
ELSE { self.bufferIndex ¬ self.bufferInputLength ¬ 0; data.start ¬ index };
};
InputRopeStreamGetLength:
PROC [self:
STREAM]
RETURNS [length:
INT] = {
data: InputRopeStreamData = NARROW[self.streamData];
RETURN[data.length];
};
RopeStreamClose:
PROC [self:
STREAM, abort:
BOOL] = {
RefText.ReleaseScratch[self.buffer];
self ¬ [streamProcs: IOUtils.closedStreamProcs];
};
GetRope:
PUBLIC
PROC [self:
STREAM, len:
INT ¬
INT.
LAST,
demand:
BOOL ¬
FALSE]
RETURNS [rope:
ROPE ¬
NIL] = {
ropeSize: INT ¬ 0;
WITH self.streamData
SELECT
FROM
data: InputRopeStreamData => {
index: INT ~ data.start+self.bufferIndex; -- current stream index
rope ¬ Rope.Substr[data.rope, index, len];
ropeSize ¬ Rope.Size[rope];
InputRopeStreamSetIndex[self, index+ropeSize];
};
ENDCASE => {
bufSize: NAT = 512;
stackSize: NAT = 10;
s: ARRAY [0..stackSize) OF ROPE ¬ ALL[NIL];
buf: REF TEXT ¬ RefText.ObtainScratch[bufSize];
residual: INT ¬ len;
UNTIL residual = 0
OR
IO.EndOf[self]
DO
chunkSize: NAT ~ MIN[residual, bufSize];
bytesRead: [0..NAT.LAST] ¬ IO.GetBlock[self: self, block: buf, startIndex: 0, count: chunkSize];
IF bytesRead=0 THEN EXIT;
residual ¬ residual - bytesRead;
rope ¬ Rope.FromRefText[buf];
FOR i:
NAT
IN [0..stackSize)
DO
IF s[i] = NIL THEN {s[i] ¬ rope; rope ¬ NIL; EXIT};
IF i = stackSize-1 THEN {s[i] ¬ Rope.Concat[s[i], rope]; rope ¬ NIL; EXIT};
rope ¬ Rope.Concat[s[i], rope];
s[i] ¬ NIL;
ENDLOOP;
ENDLOOP;
IF rope # NIL THEN ERROR;
FOR i:
NAT
IN [0..stackSize)
DO
IF s[i] # NIL THEN rope ¬ Rope.Concat[s[i], rope];
ENDLOOP;
ropeSize ¬ Rope.Size[rope];
IF ropeSize+residual # len THEN ERROR;
RefText.ReleaseScratch[buf];
};
IF demand AND ropeSize<len THEN ERROR IO.EndOfStream[self];
};
Output Stream to ROPE (ROS)
OutputRopeStreamProcs:
REF StreamProcs =
IO.CreateStreamProcs[
variety: $output, class: $ROPE,
putChar: OutputRopeStreamPutChar,
eraseChar: OutputRopeStreamEraseChar,
reset: OutputRopeStreamReset,
getIndex: OutputRopeStreamGetIndex,
getLength: OutputRopeStreamGetIndex, -- getLength=getIndex
close: RopeStreamClose
];
OutputRopeStreamData: TYPE = ROPE;
ROS:
PUBLIC
PROC [oldStream:
STREAM]
RETURNS [
STREAM] = {
self: STREAM ~ IF oldStream#NIL THEN oldStream ELSE NEW[IO.STREAMRecord];
buffer: REF TEXT ¬ self.buffer;
IF buffer=NIL OR buffer.maxLength<10 THEN buffer ¬ RefText.ObtainScratch[256];
self ¬ [streamProcs: OutputRopeStreamProcs, streamData: NIL, buffer: buffer, bufferOutputLength: buffer.maxLength, bufferIndex: 0];
RETURN[self];
};
ROSNormalize:
PROC [self:
STREAM]
RETURNS [rope:
ROPE] ~ {
Ensures that streamData is the entire output rope, and buffer is empty.
rope ¬ NARROW[self.streamData]; -- previous rope, possibly NIL
IF self.bufferIndex>0
THEN {
self.buffer.length ¬ self.bufferIndex;
self.streamData ¬ rope ¬ Rope.Concat[rope, Rope.FromRefText[self.buffer]];
self.bufferIndex ¬ 0;
};
};
OutputRopeStreamPutChar:
PROC [self:
STREAM, char:
CHAR] = {
i: NAT ~ self.bufferIndex;
IF i < self.bufferOutputLength
THEN { self.bufferIndex ¬ i+1; self.buffer[i] ¬ char }
ELSE { [] ¬ ROSNormalize[self]; self.bufferIndex ¬ 1; self.buffer[0] ¬ char };
};
OutputRopeStreamEraseChar:
PROC [self:
STREAM, char:
CHAR] = {
IF self.bufferIndex > 0
THEN self.bufferIndex ¬ self.bufferIndex-1
ELSE {
rope: ROPE ~ ROSNormalize[self];
size: INT ~ Rope.Size[rope];
IF size>0 THEN self.streamData ¬ Rope.Substr[rope, 0, size-1];
};
};
OutputRopeStreamReset:
PROC [self:
STREAM] = {
self.streamData ¬ NIL;
self.bufferIndex ¬ 0;
};
OutputRopeStreamGetIndex:
PROC [self:
STREAM]
RETURNS [
INT] = {
IF self.streamData=
NIL
THEN RETURN[self.bufferIndex]
ELSE RETURN[Rope.Size[NARROW[self.streamData]]+self.bufferIndex];
};
debugRopeFromROS:
BOOL ¬
FALSE;
-- crock until important clients get fixed
RopeFromROS:
PUBLIC
PROC [self:
STREAM, close:
BOOL]
RETURNS [
ROPE] = {
IF self.streamProcs=OutputRopeStreamProcs
THEN {
rope: ROPE ~ ROSNormalize[self];
IF close THEN RopeStreamClose[self, FALSE];
RETURN[IF debugRopeFromROS AND Rope.Size[rope]=0 THEN NIL ELSE rope];
}
ELSE ERROR IO.Error[$NotImplementedForThisStream, self];
};
PutRope:
PUBLIC
PROC [self:
STREAM, r:
ROPE, start:
INT ¬ 0, len:
INT ¬
INT.
LAST] = {
ref: REF ~ r;
rem: INT ~ Rope.Size[r]-CARD[start];
IF rem<len THEN len ¬ rem;
IF len<=0 THEN RETURN;
IF
ISTYPE[ref,
ROPE]
AND self.streamProcs=OutputRopeStreamProcs
AND (self.bufferIndex=0
OR len>
NAT[self.bufferOutputLength-self.bufferIndex])
THEN self.streamData ¬ Rope.Concat[ROSNormalize[self], Rope.Substr[r, start, len]]
ELSE
WITH ref
SELECT
FROM
t: REF TEXT => IO.PutBlock[self, t, start, len];
t: Rope.Text => IO.PutBlock[self, RefText.TrustTextRopeAsText[t], start, len];
ENDCASE =>
TRUSTED {
max: CARD ~ 64;
buffer: PACKED ARRAY [0..max) OF BYTE;
base: POINTER TO Basics.RawBytes ~ LOOPHOLE[@buffer];
WHILE len > 0
DO
moved: NAT ~ Rope.UnsafeMoveChars[[base, 0, MIN[len, max]], r, start];
IO.UnsafePutBlock[self, [base, 0, moved]];
start ¬ start + moved;
len ¬ len - moved;
ENDLOOP;
};
};
Input Stream from REF TEXT (TIS)
InputTextStreamProcs:
REF StreamProcs =
IO.CreateStreamProcs[
variety: $input, class: $TEXT,
getChar: InputTextStreamGetChar,
endOf: InputTextStreamEndOf,
reset: InputTextStreamReset,
getIndex: InputTextStreamGetIndex,
setIndex: InputTextStreamSetIndex,
getLength: InputTextStreamGetLength,
backup: InputTextStreamBackup
];
TIS:
PUBLIC
PROC [text:
REF
READONLY
TEXT, oldStream:
STREAM]
RETURNS [
STREAM] = {
self: STREAM ~ IF oldStream#NIL THEN oldStream ELSE NEW[IO.STREAMRecord];
self ¬ [streamProcs: InputTextStreamProcs, streamData: NIL];
TRUSTED { self.buffer ¬ LOOPHOLE[text] }; -- loophole because text is READONLY
self.bufferInputLength ¬ MIN[text.length, text.maxLength];
RETURN[self];
};
InputTextStreamGetChar:
PROC [self:
STREAM]
RETURNS [
CHAR] = {
i: NAT ~ self.bufferIndex;
IF i < self.bufferInputLength
THEN { self.bufferIndex ¬ i+1; RETURN[self.buffer[i]] }
ELSE ERROR IO.EndOfStream[self];
};
InputTextStreamEndOf:
PROC [self:
STREAM]
RETURNS [
BOOL] = {
RETURN[self.bufferIndex>=self.bufferInputLength];
};
InputTextStreamReset:
PROC [self:
STREAM] = {
self.bufferIndex ¬ self.bufferInputLength;
};
InputTextStreamBackup:
PROC [self:
STREAM, char:
CHAR] = {
IF self.bufferIndex>0
AND self.buffer[self.bufferIndex-1]=char
THEN self.bufferIndex ¬ self.bufferIndex-1
ELSE ERROR IO.Error[$IllegalBackup, self];
};
InputTextStreamGetIndex:
PROC [self:
STREAM]
RETURNS [
INT] = {
RETURN[self.bufferIndex];
};
InputTextStreamSetIndex:
PROC [self:
STREAM, index:
INT] = {
IF index
IN[0..self.bufferInputLength]
THEN self.bufferIndex ¬ index
ELSE ERROR IO.Error[$BadIndex, self];
};
InputTextStreamGetLength:
PROC [self:
STREAM]
RETURNS [length:
INT] = {
RETURN[self.bufferInputLength];
};
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
];
TOS:
PUBLIC
PROC [text:
REF
TEXT, oldStream:
STREAM]
RETURNS [
STREAM] = {
self: STREAM ~ IF oldStream#NIL THEN oldStream ELSE NEW[IO.STREAMRecord];
self ¬ [streamProcs: OutputTextStreamProcs, streamData: NIL];
self.buffer ¬ IF text#NIL THEN text ELSE RefText.New[128];
self.bufferOutputLength ¬ self.buffer.maxLength;
RETURN[self];
};
TextFromTOS:
PUBLIC
PROC [self:
STREAM]
RETURNS [
REF
TEXT] = {
IF self.streamProcs=OutputTextStreamProcs
THEN { self.buffer.length ¬ self.bufferIndex; RETURN[self.buffer] }
ELSE ERROR IO.Error[$NotImplementedForThisStream, self];
};
AppendOutput:
PUBLIC
PROC [to:
REF
TEXT, from:
PROC [
STREAM]]
RETURNS [
REF
TEXT] ~ {
self: STREAM ~ NEW[IO.STREAMRecord ¬ [streamProcs: OutputTextStreamProcs,
buffer: to, bufferOutputLength: to.maxLength, bufferIndex: to.length]];
from[self];
self.buffer.length ¬ self.bufferIndex;
RETURN[self.buffer];
};
OutputTextStreamPutChar:
PROC [self:
STREAM, char:
CHAR] = {
i: NAT ~ self.bufferIndex;
IF i < self.bufferOutputLength
THEN { self.bufferIndex ¬ i+1; self.buffer[i] ¬ char }
ELSE {
self.buffer.length ¬ i;
self.buffer ¬ RefText.AppendChar[self.buffer, char];
self.bufferIndex ¬ self.buffer.length;
self.bufferOutputLength ¬ self.buffer.maxLength;
IF NOT(self.bufferIndex = i+1 AND self.buffer[i] = char) THEN ERROR; -- sanity check
};
};
OutputTextStreamEraseChar:
PROC [self:
STREAM, char:
CHAR] = {
IF self.bufferIndex>0 THEN self.bufferIndex ¬ self.bufferIndex-1;
};
OutputTextStreamReset:
PROC [self:
STREAM] = {
self.bufferIndex ¬ 0;
};
OutputTextStreamGetIndex:
PROC [self:
STREAM]
RETURNS [
INT] = {
RETURN[self.bufferIndex];
};
OutputTextStreamGetLength:
PROC [self:
STREAM]
RETURNS [
INT] = {
RETURN[self.bufferIndex];
};