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 ropeSize0 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. ¬ 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? Nowhere Stream NoInput Stream Input Stream from ROPE (RIS) Ensures that data.start is current stream index, and buffer is empty. Assume this is called only if IO.Backup couldn't simply decrement bufferIndex. Output Stream to ROPE (ROS) OutputRopeStreamData: TYPE = ROPE; Ensures that streamData is the entire output rope, and buffer is empty. Input Stream from REF TEXT (TIS) Output Stream to REF TEXT (TOS) 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 Doug Wyatt, August 15, 1991 12:23:45 pm PDT For Cedar10.0, rewrote rope and text streams to use new buffering scheme. Κ€–(cedarcode) style•NewlineDelimiter ™codešΟc™Kšœ ΟeœI™TKšœ™"K™0K™.K™)K™$K™K™K™4K™-—K˜šΟk ˜ KšœŸœ˜%KšŸœ˜Kšœ˜Kšœ˜KšœŸœŸœP˜`K˜—K˜šΟnœŸœŸœ˜#KšŸœŸœ˜"KšŸœŸ˜ KšœŸ˜KšŸœŸœŸœŸœ˜KšŸœŸœŸœ˜Kšœ ŸœŸœ ˜#K˜—head™š œŸœŸœŸœŸœ˜DKšœOŸœ˜T—Kš œŸœŸœŸœ˜5Kš  œŸœŸœ Ÿœ˜4K˜—™šœŸœŸœŸœ˜/šœ Ÿœ˜"Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ Ÿœ˜—š  œŸœŸœŸœŸœ˜6KšŸœŸœ˜—š  œŸœŸœŸœ˜2KšŸœŸœ˜'—š   œŸœŸœŸœŸœ˜4KšŸœŸœ˜—Kš  œŸœŸœ Ÿœ˜5K˜—šœŸœ™KšœŸœŸœ˜7šœŸœŸœ˜&KšœŸœ˜ KšœŸœ˜KšœŸœ,˜7K˜K™—šœŸœŸœ˜=Kšœ˜Kšœ"˜"Kšœ.˜.Kšœ ˜ K˜K˜K˜"K˜"K˜$K˜K˜K˜K˜—š œŸœŸœŸœ ŸœŸœŸœ˜EKšœŸœ˜KšœŸœŸœ ŸœŸœ ŸœŸœŸœ˜IšœŸœŸœŸ˜KšœŸœ˜4KšŸœ˜$Kšœ˜K˜—š œŸœŸœ Ÿœ˜Kšœ˜K˜!K˜%K˜K˜#Kšœ%˜:K˜K˜K˜—šœŸœŸœ™"K™—š  œŸœŸœ ŸœŸœŸœ˜9KšœŸœŸœ ŸœŸœ ŸœŸœŸœ˜IKšœŸœŸœ˜KšŸœŸœŸœŸœ%˜NKšœ9ŸœH˜„KšŸœ˜ Kšœ˜K˜—š   œŸœŸœŸœŸœ˜:KšœG™GKšœŸœ˜>šŸœŸœ˜Kšœ&˜&KšœJ˜JK˜K˜—K˜K˜—š œŸœŸœŸœ˜šŸœ˜KšŸœ&˜*šŸœ˜KšœŸœ˜ KšœŸœ˜KšŸœŸœ0˜>K˜——Kšœ˜K˜—š œŸœŸœ˜.KšœŸœ˜Kšœ˜Kšœ˜K˜—š  œŸœŸœŸœŸœ˜?šŸœŸ˜KšŸœŸœ˜KšŸœŸœ Ÿœ%˜A—Kšœ˜K˜—šœŸœŸœ*˜JK˜—š  œŸœŸœŸœ ŸœŸœŸœ˜GšŸœ'˜)šŸœ˜KšœŸœ˜ KšŸœŸœŸœ˜+Kš ŸœŸœŸœŸœŸœŸœ˜EKšœ˜—KšŸœŸœŸœ+˜8—Kšœ˜K˜—š œŸœŸœŸœŸœ Ÿœ ŸœŸœŸœ˜UKšœŸœ˜ KšœŸœŸœ˜$KšŸœ Ÿœ ˜KšŸœŸœŸœ˜šŸœŸœŸœŸœ)ŸœŸœŸœ+˜ŽKšŸœN˜RšŸœŸœŸœŸ˜KšœœŸœŸœŸœ˜0KšœŸœ<˜NšŸœŸœ˜KšœŸœ˜Kš œŸœŸœ ŸœŸœ˜&KšœŸœŸœŸœ ˜5šŸœ Ÿ˜KšœŸœ"Ÿœ˜FKšŸœ(˜*Kšœ˜Kšœ˜KšŸœ˜—K˜———Kšœ˜K˜——šœŸœ™ šœŸœŸœ˜=Kšœ˜K˜ K˜K˜K˜"K˜"K˜$K˜Kšœ˜K™—š œŸœŸœŸœŸœŸœ ŸœŸœŸœ˜RKšœŸœŸœ ŸœŸœ ŸœŸœŸœ˜IKšœ8Ÿœ˜=KšŸœŸœ $˜NKšœŸœ˜:KšŸœ˜ Kšœ˜K˜—š  œŸœŸœŸœŸœ˜>KšœŸœ˜šŸœ˜KšŸœŸœ˜7KšŸœŸœŸœ˜ —Kšœ˜K˜—š  œŸœŸœŸœŸœ˜KšŸœ&˜*KšŸœŸœŸœ˜*—Kšœ˜K™—š  œŸœŸœŸœŸœ˜>KšŸœ˜Kšœ˜K™—š œŸœŸœ Ÿœ˜<šŸœŸœ˜&KšŸœ˜KšŸœŸœŸœ˜%—Kšœ˜K™—š  œŸœŸœŸœ Ÿœ˜GKšŸœ˜Kšœ˜K™——šœŸœ™šœŸœŸœ˜>Kšœ˜K˜!K˜%K˜K˜%K˜"Kšœ˜K™—š œŸœŸœŸœŸœ ŸœŸœŸœ˜IKšœŸœŸœ ŸœŸœ ŸœŸœŸœ˜IKšœ9Ÿœ˜>Kš œŸœŸœŸœŸœ˜:K˜0KšŸœ˜ Kšœ˜K˜—š  œŸœŸœŸœŸœŸœŸœ˜>šŸœ'˜)KšŸœ*Ÿœ˜CKšŸœŸœŸœ+˜8—Kšœ˜K™—š  œŸœŸœŸœŸœŸœŸœŸœŸœŸœ˜TKšœŸœŸœŸœ}˜’K˜ K˜&KšŸœ œ˜K˜K˜—š œŸœŸœŸœ˜KšŸœŸœ'˜AKšœ˜K™—š œŸœŸœ˜.Kšœ˜Kšœ˜K™—š  œŸœŸœŸœŸœ˜?KšŸœ˜Kšœ˜K™—š  œŸœŸœŸœŸœ˜@KšŸœ˜Kšœ˜K™——K˜KšŸœ˜š ™ ™-Kš¨™¨—™0K™G—™+K™I——K™—…—3τL