RopeReaderImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
written by Bill Paxton, January 1981
Bill Paxton, December 22, 1981 10:24 am
Maxwell, January 5, 1983 12:05 pm
Russ Atkinson, March 13, 1985 9:14:18 pm PST
Paul Rovner, August 10, 1983 4:21 pm
Doug Wyatt, May 23, 1984 11:37:49 am PDT
Michael Plass, March 1, 1985 12:06:51 pm PST
DIRECTORY
Basics USING [CompareCard, CompareINT, Comparison],
Rope USING [AppendChars, InlineSize, ROPE, Text],
RopeReader USING [Body, Get, GetIndex, GetRope, MaxLen, Mode, ReadChar, Ref, SetPosition];
RopeReaderImpl: CEDAR MONITOR
IMPORTS Basics, Rope, 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<length THEN length ← rem;
reader.index ← index; reader.current ← 0;
}
ELSE {
IF index<length THEN length ← index;
reader.index ← index-length; reader.current ← length;
};
text.length ← 0;
[] ← Rope.AppendChars[buffer: text, rope: rope, start: reader.index, len: length];
IF text.length=length THEN {
i: NAT ~ reader.current;
SELECT mode FROM
get => { 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;
};
EqSubstrs: PUBLIC PROC [r1, r2: ROPE, start1, start2, len: INT, rdr1, rdr2: Ref ← NIL] RETURNS [eq: BOOL] = {
uses readers rdr1 and rdr2 to read ropes r1 and r2 to test for equality
returns TRUE if r1 is same as len chars of r2 starting at start
free1, free2: BOOLFALSE;
size1: INT ← Rope.InlineSize[r1];
size2: INT ← 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: NATNAT.LAST; -- loop on NAT instead of INT
IF len<num THEN num ← len;
len ← len-num;
THROUGH [0..num) DO
IF Get[rdr1]#Get[rdr2] THEN { eq ← FALSE; GOTO Finis };
ENDLOOP;
REPEAT Finis => NULL;
ENDLOOP;
IF free1 THEN FreeRopeReader[rdr1];
IF free2 THEN FreeRopeReader[rdr2];
};
CompareSubstrs: PUBLIC PROC [r1, r2: ROPE, start1, len1, start2, len2: INT, rdr1, rdr2: Ref ← NIL, case: BOOLTRUE] 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: INT ← Rope.InlineSize[r1];
size2: INT ← Rope.InlineSize[r2];
rem, rem1, rem2: INT;
free1, free2: BOOLFALSE;
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: NATNAT.LAST;
IF rem<num THEN num ← rem;
rem ← rem-num;
THROUGH [0..num) DO
c1: CHAR ← Get[rdr1];
c2: CHAR ← Get[rdr2];
IF ~case THEN {
special crock by RRA, verified as generating good code (generates few jumps)
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
};
***** rope reader cache
defaultSize: NAT ← 200;
roperdr1: Ref ← NIL;
roperdr2: Ref ← NIL;
roperdr3: Ref ← NIL;
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] = {
SELECT Ref.NIL FROM
# roperdr3 => { reader ← roperdr3; roperdr3 ← NIL };
# roperdr2 => { reader ← roperdr2; roperdr2 ← NIL };
# roperdr1 => { reader ← roperdr1; roperdr1 ← NIL };
ENDCASE => 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;
SELECT Ref.NIL FROM
roperdr3 => roperdr3 ← reader;
roperdr2 => roperdr2 ← reader;
roperdr1 => roperdr1 ← reader;
ENDCASE => NULL;
};
END.