RefTextImpl.mesa, Operations on mutable garbage-collected strings (REF TEXT).
Copyright © 1985 by Xerox Corporation. All rights reserved.
MBrown on September 17, 1983 5:34 pm
Russ Atkinson on January 31, 1985 5:07:58 pm PST
Paul Rovner on August 8, 1983 11:39 am
DIRECTORY
Basics USING [bytesPerWord],
RefText USING [BoundsFault, ErrorCode],
Rope USING [AppendChars, InlineSize, ROPE];
RefTextImpl:
CEDAR
MONITOR
IMPORTS RefText, Rope
EXPORTS RefText
= BEGIN
ROPE: TYPE = Rope.ROPE;
ReserveChars:
PUBLIC
PROC [to:
REF
TEXT, nChars:
NAT]
RETURNS [
REF
TEXT] = {
newMinLength: INT = INT[to.length] + INT[nChars]; -- ERROR PointerFault if to=NIL
IF newMinLength <= to.maxLength THEN RETURN [to];
IF newMinLength > NAT.LAST OR to.length > to.maxLength THEN RefText.BoundsFault[];
{ expandBy:
NAT =
MAX[16, to.maxLength, nChars];
newLength:
NAT =
IF expandBy > NAT.LAST-to.maxLength THEN NAT.LAST ELSE expandBy+to.maxLength;
newText: REF TEXT = NEW[TEXT[newLength]];
FOR i: NAT IN [0..to.length) DO newText[i] ← to[i] ENDLOOP;
newText.length ← to.length;
RETURN [newText];
}
};
Append:
PUBLIC
PROC [to:
REF
TEXT, from:
REF
READONLY
TEXT, start:
NAT, len:
NAT]
RETURNS [
REF
TEXT] =
TRUSTED {
RETURN [AppendRope[to, LOOPHOLE[from], start, len]];
};
AppendChar:
PUBLIC
PROC [to:
REF
TEXT, from:
CHAR]
RETURNS [
REF
TEXT] = {
IF to.length >= to.maxLength
THEN {
-- ERROR PointerFault if to=NIL
to ← ReserveChars[to, 1];
};
to[to.length] ← from;
to.length ← to.length + 1;
RETURN [to];
};
AppendRope:
PUBLIC
PROC [to:
REF
TEXT, from:
ROPE, start:
INT, len:
NAT]
RETURNS [
REF
TEXT] = {
rem: INT ← Rope.InlineSize[from] - start;
IF start < 0 OR rem < 0 THEN RefText.BoundsFault[];
IF len < rem THEN rem ← len;
IF rem > 0
THEN {
There are characters to append
to ← ReserveChars[to, rem];
[] ← Rope.AppendChars[to, from, start, rem];
};
RETURN [to];
};
New:
PUBLIC
PROC [nChars:
NAT]
RETURNS [
REF
TEXT] = {
nChars ← nChars + (nChars MOD Basics.bytesPerWord);
RETURN [NEW[TEXT[nChars]]];
};
TextIndex: TYPE = [0..1];
TextMaxLength: ARRAY TextIndex OF NAT = [100, 512];
NTextsToAllocate:
ARRAY TextIndex
OF
NAT = [8, 2];
There are 10 overhead words per text (4 in the TEXT, 6 in the LIST), plus
6 overhead words per TextIndex value.
available: ARRAY TextIndex OF LIST OF REF TEXT ← ALL[NIL];
reserved: ARRAY TextIndex OF LIST OF REF TEXT ← ALL[NIL];
InterestingQuantity: TYPE = {obtainCalled, nCharsTooLarge, availEmpty};
Counts: ARRAY InterestingQuantity OF INT ← ALL[0];
Bump: INTERNAL PROC [q: InterestingQuantity] = INLINE { Counts[q] ← Counts[q] + 1 };
Error: PUBLIC ERROR [ec: RefText.ErrorCode] = CODE;
ObtainScratch:
PUBLIC
ENTRY
PROC [nChars:
NAT]
RETURNS [
REF
TEXT] = {
i: TextIndex ← 0;
avail: LIST OF REF TEXT;
Bump[obtainCalled];
FOR i
IN [0 ..
LAST[TextIndex]]
DO
IF nChars <= TextMaxLength[i] THEN EXIT;
REPEAT
FINISHED => {
Bump[nCharsTooLarge]; RETURN [New[nChars]] }; -- too large for pool
ENDLOOP;
IF (avail ← available[i].rest) =
NIL
THEN {
Give last element of reserved[i] to the collector, allocate a new one, and put it in avail.
r: LIST OF REF TEXT;
Bump[availEmpty];
r ← reserved[i];
UNTIL r.rest.rest = NIL DO r ← r.rest ENDLOOP;
avail ← r.rest; r.rest ← NIL; avail.first ← New[TextMaxLength[i]] };
Move first element of available[i] to front of reserved[i].
available[i].rest ← avail.rest;
avail.rest ← reserved[i].rest; reserved[i].rest ← avail;
IF avail.first.length # 0 THEN RETURN WITH ERROR Error[clientModifiedReleasedText];
RETURN [avail.first] };
ReleaseScratch:
PUBLIC
ENTRY
PROC [t:
REF
TEXT] = {
i: TextIndex ← 0;
r, l: LIST OF REF TEXT;
IF t = NIL THEN RETURN;
FOR i
IN [0 ..
LAST[TextIndex]]
DO
IF t.maxLength = TextMaxLength[i] THEN EXIT;
REPEAT FINISHED => RETURN; -- no good for pool
ENDLOOP;
r ← reserved[i];
WHILE (l ← r.rest) #
NIL
DO
IF l.first = t
THEN {
r.rest ← l.rest;
l.rest ← available[i].rest; available[i].rest ← l;
t.length ← 0;
EXIT };
r ← l;
ENDLOOP;
};
InitializePool:
PROC [] = {
FOR i: TextIndex
IN TextIndex
DO
reserved[i] ← CONS[NIL, NIL];
construct a list of NTextsToAllocate[i]+1 nodes.
FOR j:
NAT
IN [0..NTextsToAllocate[i]]
DO
l: LIST OF REF TEXT ← CONS[NIL, available[i]];
available[i] ← l;
ENDLOOP;
ENDLOOP;
FOR i: TextIndex
IN TextIndex
DO
allocate a TEXT of maxLength TextMaxLength[i] for each node but the first.
l: LIST OF REF TEXT ← available[i].rest;
WHILE l #
NIL
DO
l.first ← New[TextMaxLength[i]];
l ← l.rest;
ENDLOOP;
ENDLOOP;
};
InitializePool[];
END.