RopeReader.mesa
written by Bill Paxton, January 1981
last edit by Paxton, September 13, 1982 9:33 am
last edit by McGregor, June 10, 1982 2:30 pm
last edit by Maxwell, January 5, 1983 8:36 am
last edit by Russ Atkinson, July 22, 1983 9:33 am
last edit by Doug Wyatt, January 19, 1984 10:04:50 am PST
RopeReader provides fast inline access to characters in ropes
designed for speed in reading a sequence of characters, either forwards or backwards
from packed arrays (such as created by RopeIO)
or from text ropes (the packed sequence of characters variant)
position it where you want to read by SetPosition
RopeReader.SetPosition[reader,rope,index];
where rope is to the one to read from, and index is the initial position for the reader
you can reposition a reader at any time
read its position in any of the following ways:
[rope,index] ← RopeReader.Position[reader];
rope ← RopeReader.GetRope[reader];
index ← RopeReader.GetIndex[reader];
there are equality and comparison routines that use readers for efficiency
Equal, EqSubstr, EqSubstrs
Compare, CompareSubstr, CompareSubstrs
DIRECTORY
Basics USING [Comparison],
Rope USING [MaxLen, ROPE, Text];
***** RopeReader Declarations
ROPE: TYPE = Rope.ROPE;
Offset: TYPE = INT;
MaxLen: INT = Rope.MaxLen;
Ref: TYPE = REF Body;
Body:
TYPE =
PRIVATE
RECORD [
text: REF TEXT ←, -- text buffer (must not be NIL)
current: NAT ← 0, -- index in text of current character (next to be returned by Get)
rope: ROPE ← NIL, -- rope that we are reading
index: INT ← 0, -- index in rope of text[0]
charForEndOfRope:
BOOLEAN ←
FALSE,
if true, return endChar; else give ERROR ReadOffEnd
endChar: CHAR ← 0C -- char to return when reach end of rope
];
ReadOffEnd: ERROR;
***** RopeReader Operations
Create:
PROC[size:
NAT ← 0]
RETURNS [Ref];
Creates a rope reader with a text buffer of the given size; if size=0, uses default.
GetRope:
PROC [reader: Ref]
RETURNS [rope:
ROPE] =
INLINE {
RETURN [reader.rope]
};
GetIndex:
PROC [reader: Ref]
RETURNS [index:
INT] =
INLINE {
RETURN [reader.index+reader.current]
};
Position:
PROC [reader: Ref]
RETURNS [rope:
ROPE, index:
INT] =
INLINE {
RETURN [rope: GetRope[reader], index: GetIndex[reader]]
};
SetPosition:
PROC [reader: Ref, rope:
ROPE, index:
INT ← 0] =
INLINE {
IF GetRope[reader]=rope AND GetIndex[reader]=index THEN RETURN;
reader.rope ← rope; reader.current ← 0; reader.text.length ← 0; reader.index ← index;
SetIndex:
PROC [reader: Ref, index:
INT ← 0] =
INLINE {
IF GetIndex[reader]=index THEN RETURN;
reader.current ← 0; reader.text.length ← 0; reader.index ← index;
BackupIndex:
PROC [reader: Ref, amount:
INT] =
INLINE {
SetIndex[reader, GetIndex[reader]-amount]
};
BumpIndex:
PROC [reader: Ref, amount:
INT] =
INLINE {
SetIndex[reader, GetIndex[reader]+amount]
};
GetEndChar:
PROC [reader: Ref]
RETURNS [endChar: CHAR] = INLINE { RETURN [reader.endChar] };
Returns the current end char. This will be returned rather than giving ReadOffEnd error if CharForEndOfRope is true.
CharForEndOfRope:
PROC [reader: Ref]
RETURNS [
BOOLEAN] =
INLINE { RETURN [reader.charForEndOfRope] };
SetCharForEndOfRope:
PROC [reader: Ref, char:
CHAR] =
INLINE {
reader.endChar ← char; reader.charForEndOfRope ← TRUE };
ClearCharForEndOfRope:
PROC [reader: Ref] =
INLINE {
reader.charForEndOfRope ← FALSE };
Mode: PRIVATE TYPE = {get, backwards, peek, peekbackwards};
ReadChar: PRIVATE PROC [reader: Ref, mode: Mode] RETURNS [CHAR];
Get:
PROC [reader: Ref]
RETURNS [
CHAR] =
INLINE {
get character, then increment reader location
text: REF TEXT ~ reader.text;
i: NAT ~ reader.current;
IF i<text.length THEN { reader.current ← i+1; RETURN[text[i]] }
ELSE RETURN[ReadChar[reader, $get]];
};
Backwards:
PROC [reader: Ref]
RETURNS [
CHAR] =
INLINE {
decrement reader location, then get character
text: REF TEXT ~ reader.text;
i: NAT ~ reader.current;
IF i>0 THEN RETURN[text[reader.current ← i-1]]
ELSE RETURN[ReadChar[reader, $backwards]]
};
Peek:
PROC [reader: Ref]
RETURNS [
CHAR] =
INLINE {
get character without incrementing reader location
text: REF TEXT ~ reader.text;
i: NAT ~ reader.current;
IF i<text.length THEN RETURN[text[i]]
ELSE RETURN[ReadChar[reader, $peek]]
};
PeekBackwards:
PROC [reader: Ref]
RETURNS [
CHAR] =
INLINE {
like Backwards, but doesn't change position
text: REF TEXT ~ reader.text;
i: NAT ~ reader.current;
IF i>0 THEN RETURN[text[i-1]]
ELSE RETURN[ReadChar[reader, $peekbackwards]]
};
Here are some rope comparison operations that make use of readers for efficiency.
Equal:
PROC [r1,r2:
ROPE, rdr1,rdr2: Ref ←
NIL]
RETURNS [
BOOLEAN] =
INLINE {
uses readers rdr1 and rdr2 to read ropes r1 and r2 to test for equality
RETURN [EqSubstrs[r1,r2,0,0,MaxLen,rdr1,rdr2]] };
EqSubstrs:
PROC [r1,r2:
ROPE, start1,start2,len:
INT, rdr1,rdr2: Ref ←
NIL]
RETURNS [BOOLEAN];
uses readers rdr1 and rdr2 to read ropes r1 and r2 to test for equality
returns TRUE if r1 starting at start1 is same as
len chars of r2 starting at start2
returns FALSE if not enough chars available in either rope
i.e., if start1+len > size[r1] or start2+len > size[r2]
if rdr1 or rdr2 is NIL, gets own readers from cache
Compare:
PROC [r1,r2:
ROPE, rdr1,rdr2: Ref ←
NIL, case:
BOOLEAN ←
TRUE]
RETURNS [Basics.Comparison] = INLINE {
uses readers rdr1 and rdr2 to compare ropes r1 and r2
returns less for r1 < r2; equal for r1 = r2; and greater for r1 > r2
if ~case then all characters forced lowercase for comparison
RETURN [CompareSubstrs[r1,r2,0,MaxLen,0,MaxLen,rdr1,rdr2,case]] };
CompareSubstrs:
PROC [
r1,r2: ROPE, start1,len1,start2,len2: INT,
rdr1,rdr2: Ref ← NIL, case: BOOLEAN ← TRUE]
RETURNS [Basics.Comparison];
uses readers rdr1 and rdr2 to compare substrings of ropes r1 and r2
if rdr1 or rdr2 is NIL, gets own readers from cache
The following operations provide for reading blocks of characters
AppendToString:
PROC[str:
REF TEXT,
rope: ROPE, start: INT ← 0, len: INT ← LAST[INT]]
RETURNS [count: NAT];
appends characters to string from rope; returns number of characters
updates str.length
GetString:
PROC [reader: Ref, str:
REF TEXT, length:
NAT ←
LAST[
NAT]]
RETURNS [count: NAT];
appends characters to string from reader; returns number of characters
updates str.length
BackwardsGetString:
PROC [reader: Ref, str:
REF TEXT, length:
NAT ←
LAST[
NAT]]
RETURNS [count: NAT];
appends characters to string from reader; returns number of characters
updates str.length
AppendToText:
PROC[txt: Rope.Text,
rope: ROPE, start: INT ← 0, len: INT ← LAST[INT]]
RETURNS [count:
NAT] =
TRUSTED
INLINE {
count ← AppendToString[str: LOOPHOLE[txt], rope: rope, start: start, len: len] };
appends characters to text from rope; returns number of characters
updates txt.length
GetText:
PROC [reader: Ref, txt: Rope.Text, length:
NAT ←
LAST[
NAT]]
RETURNS [count:
NAT] =
TRUSTED
INLINE {
count ← GetString[reader: reader, str: LOOPHOLE[txt], length: length] };
appends characters to text from reader; returns number of characters
updates txt.length
BackwardsGetText:
PROC [reader: Ref, txt: Rope.Text, length:
NAT ←
LAST[
NAT]]
RETURNS [count:
NAT] =
TRUSTED
INLINE {
count ← BackwardsGetString[reader: reader, str: LOOPHOLE[txt], length: length] };
appends characters to text from reader; returns number of characters
updates txt.length
***** Private declarations
charsPerPage: NAT = 512;
pagesPerArray: NAT = 16;
charsPerArray: NAT = charsPerPage*pagesPerArray;
Chars: TYPE = REF READONLY CharsArray;
CharsArray: TYPE = PACKED ARRAY [0..charsPerArray) OF CHAR;
CharsRope:
PROC [chars: Chars]
RETURNS [
ROPE];
create a ROPE from a CharsArray; this routine is mainly for internal use
AppendToChars:
PROC[chars:
REF CharsArray, offset:
NAT ← 0,
rope: ROPE, start: INT ← 0, len: INT ← LAST[INT]]
RETURNS [count: NAT];
appends characters to CharsArray from rope; returns number of characters
updates str.length
GetChars:
PROC [
reader: Ref,
chars: REF CharsArray,
length: NAT ← LAST[NAT],
offset: NAT ← 0]
RETURNS [count: NAT];
appends characters to CharsArray from reader; returns number of characters
BackwardsGetChars:
PROC [
reader: Ref,
chars: REF CharsArray,
length: NAT ← LAST[NAT],
offset: NAT ← 0]
RETURNS [count: NAT];
The next routines provide a small cache of rope readers
so can avoid creating a lot of garbage readers
don't use these unless you are certain to free the reader at most once
no harm if fail to free it, but total chaos if you free it more than once
GetRopeReader: PROC RETURNS [reader: Ref];
FreeRopeReader: PROC [reader: Ref];
SetDefaultSize:
PROC[size:
NAT];
Sets the default buffer size used by Create and readers in the cache.