<<>> <> <> <> <> <> <> <> <> <> DIRECTORY Basics USING [charsPerWord, RaiseBoundsFault, RawChars], Checksum USING [bytesPerHalfWord, ComputeChecksum], RefText USING [ErrorCode, line], RefTextExtras, Rope USING [AppendChars, Size, Fetch, ROPE, Text, TextBound], RopeHash USING [PureText], RopePrivate USING [NonNeg, QStore], SafeStorage; RopeOtherImpl: CEDAR MONITOR LOCKS pool USING pool: Pool IMPORTS Basics, Checksum, RefTextExtras, Rope, RopePrivate, SafeStorage EXPORTS RefText, RefTextExtras, RopeHash SHARES Rope = BEGIN ROPE: TYPE = Rope.ROPE; TextBound: TYPE = Rope.TextBound; Pool: TYPE = REF PoolObj; PoolObj: PUBLIC TYPE = MONITORED RECORD [ charsPerText: NAT, nextAvailable: INT ¬ -1, texts: SEQUENCE count: NAT OF REF TEXT ]; <> ReserveChars: PUBLIC PROC [to: REF TEXT, nChars: TextBound] RETURNS [REF TEXT] = { newMinLength: INT = INT[to.length] + INT[nChars]; -- ERROR PointerFault if to=NIL IF newMinLength <= to.maxLength THEN RETURN [to]; IF newMinLength > TextBound.LAST OR to.length > to.maxLength THEN Basics.RaiseBoundsFault[]; { expandBy: TextBound = MAX[16, to.maxLength, nChars]; newLength: TextBound = IF expandBy > TextBound.LAST-to.maxLength THEN TextBound.LAST ELSE expandBy+to.maxLength; newText: REF TEXT = untracedZone.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: TextBound, len: TextBound] 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: TextBound] RETURNS [REF TEXT] = { rem: INT ¬ Rope.Size[from] - start; IF start < 0 OR rem < 0 THEN Basics.RaiseBoundsFault[]; IF len < rem THEN rem ¬ len; IF rem > 0 THEN { <> to ¬ ReserveChars[to, rem]; [] ¬ Rope.AppendChars[to, from, start, rem]; }; RETURN [to]; }; New: PUBLIC PROC [nChars: TextBound] RETURNS [REF TEXT] = { <> text: REF TEXT; <> maxLength: NAT ¬ Basics.charsPerWord*WORDS[Basics.RawChars[nChars]]; IF maxLength > LAST[NAT15] THEN maxLength ¬ LAST[NAT15]; text ¬ untracedZone.NEW[TEXT[maxLength]]; text.length ¬ 0; RETURN[text]; }; Error: PUBLIC ERROR [ec: RefText.ErrorCode] = CODE; ObtainScratch: PUBLIC PROC [nChars: TextBound ¬ RefText.line] RETURNS [REF TEXT] = { IF nChars>512 THEN IF nChars>8192 THEN RETURN [New[nChars]] ELSE RETURN [RefTextExtras.ObtainScratch8192[]] ELSE IF nChars>100 THEN RETURN [RefTextExtras.ObtainScratch512[]] ELSE RETURN [RefTextExtras.ObtainScratch100[]] }; ReleaseScratch: PUBLIC PROC [t: REF TEXT] = { SELECT t.maxLength FROM 16 => RefTextExtras.ReleaseScratch16[t]; 100 => RefTextExtras.ReleaseScratch100[t]; 512 => RefTextExtras.ReleaseScratch512[t]; 8192 => RefTextExtras.ReleaseScratch8192[t]; ENDCASE; -- otherwise it doesn't belong in any pools. Let the garbage collector have it. }; ObtainInternal: PUBLIC ENTRY PROC [pool: Pool] RETURNS [thisText: REF TEXT] = { IF pool.nextAvailable = -1 THEN RETURN[New[pool.charsPerText]] -- pool is empty ELSE { thisText ¬ pool.texts[pool.nextAvailable]; pool.nextAvailable ¬ pool.nextAvailable -1; IF thisText.length # 0 THEN RETURN WITH ERROR Error[clientModifiedReleasedText]; }; }; ReleaseInternal: PUBLIC ENTRY PROC [pool: Pool, t: REF TEXT] = { IF t = NIL THEN RETURN; t.length ¬ 0; IF pool.nextAvailable = pool.count -1 THEN RETURN -- pool is full ELSE { pool.nextAvailable ¬ pool.nextAvailable + 1; pool.texts[pool.nextAvailable] ¬ t; }; }; CreatePool: PUBLIC PROC [charsPerText: CARD, textsInPool: CARD] RETURNS [pool: Pool] = { pool ¬ NEW[PoolObj[textsInPool]]; pool.charsPerText ¬ charsPerText; pool.nextAvailable ¬ textsInPool - 1; FOR i: NAT IN [0..textsInPool) DO pool.texts[i] ¬ New[charsPerText]; ENDLOOP; }; <> <> <> << <> TextIndex: TYPE = [0..2]; TextMaxLength: ARRAY TextIndex OF TextBound = [100, 512, 8192]; NTextsToAllocate: ARRAY TextIndex OF NAT = [8, 2, 2]; <> <<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 }; >> < { <> Bump[nCharsTooLarge]; RETURN [New[nChars]]; }; ENDLOOP; IF (avail ¬ available[i].rest) = NIL THEN { <> 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]] }; <> 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]; }; >> < 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; }; >> <> 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 <> l: LIST OF REF TEXT ¬ available[i].rest; WHILE l # NIL DO l.first ¬ New[TextMaxLength[i]]; l ¬ l.rest; ENDLOOP; ENDLOOP; }; >> <> defaultSeed: CARDINAL = 31415; bytesPerHalfWord: NAT ~ Checksum.bytesPerHalfWord; -- = 2 bufSize: NAT = 128*bytesPerHalfWord; TextSize: NAT = SIZE[TEXT[0]]; HalfWordAsChars: TYPE = PACKED ARRAY [0..bytesPerHalfWord) OF CHAR; --RopeHash.--FromRefText: PUBLIC PROC [text: RopeHash.PureText, seed: CARDINAL] RETURNS [hash: CARDINAL] = TRUSTED { len: TextBound ¬ text.length; nLeft: NAT = len MOD bytesPerHalfWord; nWhole: NAT = len - nLeft; p: LONG POINTER = LOOPHOLE[text, LONG POINTER] + TextSize; hash ¬ seed; IF nWhole >= bytesPerHalfWord THEN { hash ¬ Checksum.ComputeChecksum[hash, nWhole/bytesPerHalfWord, p]; }; IF nLeft # 0 THEN TRUSTED { leftovers: HalfWordAsChars ¬ ALL[0C]; FOR j: NAT IN [0..nLeft) DO leftovers[j] ¬ Rope.Fetch[LOOPHOLE[text], nWhole+j]; ENDLOOP; hash ¬ Checksum.ComputeChecksum[hash, BYTES[HalfWordAsChars]/bytesPerHalfWord, @leftovers]; }; }; --RopeHash.--FromRope: PUBLIC PROC [rope: ROPE, case: BOOL, start: INT, len: INT, seed: CARDINAL] RETURNS [hash: CARDINAL] = TRUSTED { rem: INT ¬ RopePrivate.NonNeg[Rope.Size[rope] - RopePrivate.NonNeg[start]]; IF rem < len THEN len ¬ rem; <> IF case AND start = 0 AND rem = len THEN WITH rope SELECT FROM text: Rope.Text => <> RETURN[FromRefText[LOOPHOLE[text], seed]]; ENDCASE; { <> buf: REF TEXT ¬ ObtainScratch[bufSize]; p: LONG POINTER = LOOPHOLE[buf, LONG POINTER] + SIZE[TEXT[0]]; bytes: NAT ¬ 0; hash ¬ seed; WHILE len > 0 DO buf.length ¬ 0; bytes ¬ Rope.AppendChars[buf, rope, start, len]; IF NOT case THEN <> FOR i: NAT IN [0..bytes) DO c: CHAR = Rope.Fetch[LOOPHOLE[buf], i]; IF c <= 'Z AND c >= 'A THEN RopePrivate.QStore[c + ('a-'A), LOOPHOLE[buf], i]; ENDLOOP; hash ¬ FromRefText[buf, hash]; start ¬ start + bytes; len ¬ len - bytes; ENDLOOP; ReleaseScratch[buf]; RETURN [hash]; }; }; untracedZone: ZONE ¬ NIL; -- initialized below in Init pool16, pool100, pool512, pool8192: PUBLIC Pool; Init: PROC = { TRUSTED {untracedZone ¬ SafeStorage.GetUntracedZone[]}; pool16 ¬ CreatePool[16, 10]; pool100 ¬ CreatePool[100, 4]; pool512 ¬ CreatePool[512, 4]; pool8192 ¬ CreatePool[8192, 4]; }; Init[]; END. <> <> <> <> <> <<>>