-- 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.