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 TEXTALL[NIL];
reserved: ARRAY TextIndex OF LIST OF REF TEXTALL[NIL];
InterestingQuantity: TYPE = {obtainCalled, nCharsTooLarge, availEmpty};
Counts: ARRAY InterestingQuantity OF INTALL[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 TEXTCONS[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.