<<>> <> <> <> <> <> <> <> <<>> <> <> <> DIRECTORY Basics USING [RawBytes, UnsafeBlock], IO, IOUtils, RefText, Rope USING [ROPE, AppendChars, Concat, Fetch, FromRefText, Size, Substr, Text, UnsafeMoveChars]; IOSimpleStreamsImpl: CEDAR PROGRAM IMPORTS IO, IOUtils, RefText, Rope EXPORTS IO = BEGIN STREAM: TYPE = IO.STREAM; ROPE: TYPE = Rope.ROPE; StreamProcs: TYPE = IO.StreamProcs; <> noWhereStream: PUBLIC STREAM ¬ IO.CreateStream[IO.CreateStreamProcs[ variety: $output, class: $Null, putChar: NoWherePutChar, close: NoWhereClose], NIL]; NoWherePutChar: PROC [self: STREAM, char: CHAR] = {}; NoWhereClose: PROC [self: STREAM, abort: BOOL] = {}; <> noInputStream: PUBLIC STREAM ¬ IO.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] = { }; <> 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 { <> 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]; <> 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> OutputRopeStreamProcs: REF StreamProcs = IO.CreateStreamProcs[ variety: $output, class: $ROPE, putChar: OutputRopeStreamPutChar, eraseChar: OutputRopeStreamEraseChar, reset: OutputRopeStreamReset, getIndex: OutputRopeStreamGetIndex, getLength: OutputRopeStreamGetIndex, -- getLength=getIndex close: RopeStreamClose ]; <> <<>> 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] ~ { <> 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 remNAT[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; }; }; <> 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]; }; <<>> <> 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]; }; <<>> END. <> <> <> <> <> <> <> <<>>