<> <> <> <> <> <> <> <> <> <<>> DIRECTORY Basics USING [CompareCard, CompareINT, Comparison], PrincOpsUtils USING [ByteBlt], RopeFile USING [AppendChars], RefText USING [ObtainScratch, ReleaseScratch], Rope USING [FetchType, InlineSize, MakeRope, MapType, NoRope, ROPE, Text], RopeReader USING [Body, Chars, CharsArray, charsPerArray, Get, GetIndex, GetRope, MaxLen, Mode, Offset, ReadChar, Ref, SetIndex, SetPosition]; RopeReaderImpl: CEDAR MONITOR IMPORTS Basics, PrincOpsUtils, RefText, Rope, RopeFile, RopeReader EXPORTS RopeReader = BEGIN OPEN RopeReader; ROPE: TYPE = Rope.ROPE; Text: TYPE = Rope.Text; ReadOffEnd: PUBLIC ERROR = CODE; ReadChar: PUBLIC PROC [reader: Ref, mode: Mode] RETURNS [char: CHAR] = { rope: ROPE ~ reader.rope; -- the rope being read size: INT ~ rope.InlineSize[]; -- size of the rope index: INT ~ reader.index+reader.current; -- index of next char that Get would read text: REF TEXT ~ reader.text; -- text buffer length: NAT _ text.maxLength; -- number of chars to be read into buffer forward: BOOL ~ SELECT mode FROM get, peek => TRUE, backwards, peekbackwards => FALSE, ENDCASE => ERROR; inBounds: BOOL ~ IF forward THEN index IN[0..size) ELSE index IN(0..size]; IF NOT inBounds THEN { IF reader.charForEndOfRope THEN RETURN[reader.endChar] ELSE ERROR ReadOffEnd; }; IF forward THEN { rem: INT ~ size-index; IF rem { reader.current _ i+1; RETURN[text[i]] }; backwards => { RETURN[text[reader.current _ i-1]] }; peek => { RETURN[text[i]] }; peekbackwards => { RETURN[text[i-1]] }; ENDCASE => ERROR; } ELSE ERROR; }; AppendToString: PUBLIC PROC[str: REF TEXT, rope: ROPE, start: INT _ 0, len: INT _ LAST[INT]] RETURNS [count: NAT _ 0] = { <> count _ RopeFile.AppendChars[buffer: str, rope: rope, start: start, len: len]; }; AppendToChars: PUBLIC PROC[chars: REF CharsArray, offset: NAT _ 0, rope: ROPE, start: INT _ 0, len: INT _ LAST[INT]] RETURNS [count: NAT _ 0] = { <> text: REF TEXT _ RefText.ObtainScratch[RopeReader.charsPerArray]; InRange: TYPE ~ [0..RopeReader.charsPerArray]; text.length _ 0; count _ RopeFile.AppendChars[buffer: text, rope: rope, start: start, len: MIN[len, NAT[RopeReader.charsPerArray-offset]]]; TRUSTED { count _ PrincOpsUtils.ByteBlt[ to: [blockPointer: LOOPHOLE[chars], startIndex: InRange[offset], stopIndexPlusOne: InRange[offset+count]], from: [blockPointer: LOOPHOLE[text, LONG POINTER] + SIZE[TEXT[0]], startIndex: 0, stopIndexPlusOne: count] ]; }; RefText.ReleaseScratch[text]; }; GetString: PUBLIC PROC[reader: Ref, str: REF TEXT, length: NAT _ LAST[NAT]] RETURNS [count: NAT] = { index: INT ~ GetIndex[reader]; count _ RopeFile.AppendChars[buffer: str, rope: GetRope[reader], start: index, len: length]; SetIndex[reader, index+count]; }; BackwardsGetString: PUBLIC PROC[reader: Ref, str: REF TEXT, length: NAT _ LAST[NAT]] RETURNS [count: NAT] = { index: INT ~ GetIndex[reader]; rem: NAT ~ str.maxLength-str.length; start: INT _ 0; len: INT _ MIN[length, rem]; IF index> <<-- returns TRUE if r1 is same as len chars of r2 starting at start>> free1, free2: BOOLEAN _ FALSE; size1: Offset _ Rope.InlineSize[r1]; size2: Offset _ Rope.InlineSize[r2]; start1 _ MIN[start1,size1]; start2 _ MIN[start2,size2]; IF len=MaxLen THEN { IF (len _ size1-start1) # size2-start2 THEN RETURN [FALSE] } ELSE IF start1+len > size1 THEN RETURN [FALSE] ELSE IF start2+len > size2 THEN RETURN [FALSE]; IF rdr1 = NIL THEN { rdr1 _ GetRopeReader[]; free1 _ TRUE }; IF rdr2 = NIL THEN { rdr2 _ GetRopeReader[]; free2 _ TRUE }; SetPosition[rdr1,r1,start1]; SetPosition[rdr2,r2,start2]; eq _ TRUE; WHILE len > 0 DO num: NAT _ NAT.LAST; -- loop on NAT instead of INT IF len NULL; ENDLOOP; IF free1 THEN FreeRopeReader[rdr1]; IF free2 THEN FreeRopeReader[rdr2]; }; CompareSubstrs: PUBLIC PROC [ r1, r2: ROPE, start1, len1, start2, len2: Offset, rdr1, rdr2: Ref _ NIL, case: BOOLEAN _ TRUE] RETURNS [result: Basics.Comparison] = { <<-- uses readers rdr1 and rdr2 to compare ropes r1 and r2>> <<-- if ~case then all characters forced lowercase for comparison>> size1: Offset _ Rope.InlineSize[r1]; size2: Offset _ Rope.InlineSize[r2]; rem, rem1, rem2: Offset; free1, free2: BOOLEAN _ FALSE; rem1 _ IF start1 > size1 THEN 0 ELSE size1-start1; rem2 _ IF start2 > size2 THEN 0 ELSE size2-start2; IF rdr1 = NIL THEN { rdr1 _ GetRopeReader[]; free1 _ TRUE }; IF rdr2 = NIL THEN { rdr2 _ GetRopeReader[]; free2 _ TRUE }; len1 _ MIN[len1,rem1]; len2 _ MIN[len2,rem2]; rem _ MIN[len1,len2]; SetPosition[rdr1,r1,start1]; SetPosition[rdr2,r2,start2]; result _ equal; WHILE rem > 0 DO num: NAT _ NAT.LAST; IF rem> IF LOOPHOLE[c1-'A, CARDINAL] <= ('Z-'A) THEN c1 _ LOOPHOLE[LOOPHOLE[c1, CARDINAL] + ('a-'A), CHAR]; IF LOOPHOLE[c2-'A, CARDINAL] <= ('Z-'A) THEN c2 _ LOOPHOLE[LOOPHOLE[c2, CARDINAL] + ('a-'A), CHAR]; }; IF c1 # c2 THEN { result _ Basics.CompareCard[LOOPHOLE[c1, CARDINAL], LOOPHOLE[c2, CARDINAL]]; GO TO Finis; }; ENDLOOP; REPEAT Finis => NULL; ENDLOOP; IF free1 THEN FreeRopeReader[rdr1]; IF free2 THEN FreeRopeReader[rdr2]; IF result # equal THEN RETURN; -- if a character differerence, then return it RETURN [Basics.CompareINT[len1, len2]]; -- if equal so far, the length determines }; <<-- implementation of ropes using character arrays>> CharsFetch: Rope.FetchType = { <> WITH data SELECT FROM x: Chars => RETURN[x[index]]; ENDCASE => ERROR Rope.NoRope; }; CharsMap: Rope.MapType = { <> WITH base SELECT FROM x: Chars => { k: NAT ~ start; l: NAT ~ len; FOR i: NAT IN[k..k+l) DO IF action[x[i]] THEN RETURN [TRUE]; ENDLOOP; }; ENDCASE => ERROR Rope.NoRope; RETURN [FALSE] }; CharsRope: PUBLIC PROC [chars: Chars] RETURNS [ROPE] = TRUSTED { <> RETURN [Rope.MakeRope[LOOPHOLE[chars], charsPerArray, CharsFetch, CharsMap]]; }; <<-- ***** rope reader cache>> defaultSize: NAT _ 200; roperdr1, roperdr2, roperdr3: Ref _ NIL; -- shared rope readers SetDefaultSize: PUBLIC ENTRY PROC[size: NAT] = { defaultSize _ size; roperdr1 _ roperdr2 _ roperdr3 _ NIL; }; Create: PUBLIC PROC[size: NAT _ 0] RETURNS [Ref] = { IF size=0 THEN size _ defaultSize; RETURN[NEW[Body _ [text: NEW[TEXT[size]]]]] }; GetRopeReader: PUBLIC ENTRY PROC RETURNS [reader: Ref] = { IF roperdr3 # NIL THEN { reader _ roperdr3; roperdr3 _ NIL } ELSE IF roperdr2 # NIL THEN { reader _ roperdr2; roperdr2 _ NIL } ELSE IF roperdr1 # NIL THEN { reader _ roperdr1; roperdr1 _ NIL } ELSE reader _ Create[]; }; FreeRopeReader: PUBLIC ENTRY PROC [reader: Ref] = { SetPosition[reader, NIL]; IF reader.text.maxLength#defaultSize THEN RETURN; reader.charForEndOfRope _ FALSE; IF roperdr3 = reader OR roperdr2 = reader OR roperdr1 = reader THEN ERROR; IF roperdr3 = NIL THEN roperdr3 _ reader ELSE IF roperdr2 = NIL THEN roperdr2 _ reader ELSE IF roperdr1 = NIL THEN roperdr1 _ reader; }; END.