-- RefTextImpl.mesa, Operations on mutable garbage-collected strings (REF TEXT). -- MBrown on May 2, 1982 11:13 pm -- Russ Atkinson, June 7, 1982 6:01 pm DIRECTORY RefText, Runtime USING [BoundsFault], SafeStorage USING [NewZone]; RefTextImpl: CEDAR MONITOR IMPORTS Runtime, SafeStorage EXPORTS RefText = BEGIN Append: PUBLIC PROC [to: REF TEXT, from: REF READONLY TEXT, start: NAT, len: NAT] = TRUSTED { j: NAT _ to.length; -- ERROR PointerFault if to=NIL IF start > from.length THEN ERROR Runtime.BoundsFault; len _ MIN[from.length-start, len]; IF j > to.maxLength OR len > to.maxLength-j THEN ERROR Runtime.BoundsFault; FOR i: NAT IN [start..start+len) DO to[j] _ from[i]; j _ j + 1; ENDLOOP; to.length _ j }; z: ZONE = SafeStorage.NewZone[]; New: PUBLIC PROC [nChars: NAT] RETURNS [REF TEXT] = { RETURN [z.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; 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.