-- Registration Server - Implementation of using heap and BTree as a registry. -- [Juniper]MS>Registry.mesa -- Randy Gobbel, 20-May-81 13:03:14 -- J. Dion, September 10, 1979. -- Andrew Birrell, 26-Oct-82 13:18:34 DIRECTORY BodyDefs USING [maxRNameLength, RName, RNameSize, Timestamp], HeapDefs, Inline USING [LowHalf], PupDefs USING [PupNameLookup], PupStream USING [PupAddress], Process USING [InitializeCondition, MsecToTicks], ProtocolDefs, RegistryDefs, Time USING [Current, Packed]; Registry: MONITOR IMPORTS BodyDefs, HeapDefs, Inline, PupDefs, Process, Time EXPORTS RegistryDefs = BEGIN HeapObjectEnded: ERROR = CODE; MangledHeapObject: ERROR = CODE; Copy: PUBLIC PROC [ reader: HeapDefs.ReaderHandle, writer: HeapDefs.WriterHandle] = -- copies one component of an entry -- BEGIN bLength: CARDINAL = 64; buffer: ARRAY [0..bLength) OF WORD; length: CARDINAL ¬ ReadComponentLength[reader]; WriteComponentLength[writer, length]; WHILE length > 0 DO used: CARDINAL = HeapDefs.HeapReadData[ reader, [@buffer, MIN[length, bLength]]].used; length ¬ length - used; HeapDefs.HeapWriteData[writer, [@buffer, used]]; ENDLOOP; END; Skip: PUBLIC PROC [reader: HeapDefs.ReaderHandle] = -- skips one component of an entry -- BEGIN length: CARDINAL = ReadComponentLength[reader]; IF length > 0 --optimisation-- THEN HeapDefs.SetReaderOffset[reader, length + HeapDefs.GetReaderOffset[reader]]; END; SkipIfEmpty: PUBLIC PROC [reader: HeapDefs.ReaderHandle] RETURNS [empty: BOOLEAN] = BEGIN pos: HeapDefs.ObjectOffset = HeapDefs.GetReaderOffset[reader]; length: CARDINAL = ReadComponentLength[reader]; IF length = 0 THEN RETURN[TRUE] ELSE BEGIN HeapDefs.SetReaderOffset[reader, pos]; RETURN[FALSE] END; END; WriteList: PUBLIC PROC [ writer: HeapDefs.WriterHandle, name: BodyDefs.RName, stamp: BodyDefs.Timestamp] = BEGIN -- writes single or zero entry list, with no deletions -- IF name = NIL THEN {WriteEmptyList[writer]; WriteEmptyList[writer]} ELSE {WriteRNameList[writer, name]; WriteStampList[writer, stamp]; }; WriteEmptyList[writer]; WriteEmptyList[writer]; END; StartSublist: PUBLIC PROC RETURNS [writer: HeapDefs.WriterHandle] = BEGIN writer ¬ HeapDefs.HeapStartWrite[temp]; WriteComponentLength[writer, 0]; -- place holder -- END; AddNameToSublist: PUBLIC PROC [ writer: HeapDefs.WriterHandle, name: BodyDefs.RName] = BEGIN HeapDefs.HeapWriteRName[writer, name]; END; EndSublist: PUBLIC PROC [writer: HeapDefs.WriterHandle, count: CARDINAL] RETURNS [reader: HeapDefs.ReaderHandle] = BEGIN offset: HeapDefs.ObjectOffset ¬ HeapDefs.GetWriterOffset[writer]; HeapDefs.SetWriterOffset[writer, HeapDefs.objectStart]; WriteComponentLength[writer, Inline.LowHalf[offset - SIZE[CARDINAL]]]; HeapDefs.SetWriterOffset[writer, offset]; WriteComponentLength[writer, count * SIZE[BodyDefs.Timestamp]]; BEGIN stamp: BodyDefs.Timestamp = MakeTimestamp[]; THROUGH [0..count) DO WriteTimestamp[writer, stamp] ENDLOOP; END; WriteEmptyList[writer]; WriteEmptyList[writer]; -- deletions -- BEGIN GetReader: PROC [obj: HeapDefs.ObjectNumber] = { reader ¬ HeapDefs.HeapStartRead[obj]}; HeapDefs.HeapEndWrite[writer, GetReader]; END; END; EnumerateRList: PUBLIC PROC [ reader: HeapDefs.ReaderHandle, work: PROC [BodyDefs.RName] RETURNS [done: BOOLEAN]] = BEGIN length: CARDINAL ¬ ReadComponentLength[reader]; WHILE length > 0 DO name: BodyDefs.RName = [BodyDefs.maxRNameLength]; [] ¬ HeapDefs.HeapReadRName[reader, name]; length ¬ length - BodyDefs.RNameSize[name]; IF work[name] THEN EXIT; ENDLOOP; END; ReadPrefix: PUBLIC PROC [reader: HeapDefs.ReaderHandle, name: BodyDefs.RName] RETURNS [type: ProtocolDefs.RNameType, stamp: BodyDefs.Timestamp] = BEGIN length: CARDINAL = ReadComponentLength[reader]; stamp ¬ ReadTimestamp[reader]; [, ] ¬ HeapDefs.HeapReadData[reader, [@type, SIZE[ProtocolDefs.RNameType]]]; [] ¬ HeapDefs.HeapReadRName[reader, name]; IF length # SIZE[ProtocolDefs.RNameType] + SIZE[BodyDefs.Timestamp] + BodyDefs.RNameSize[name] THEN ERROR MangledHeapObject; END; WritePrefix: PUBLIC PROC [ writer: HeapDefs.WriterHandle, type: ProtocolDefs.RNameType, stamp: POINTER TO BodyDefs.Timestamp, name: BodyDefs.RName] = BEGIN WriteComponentLength[ writer, SIZE[ProtocolDefs.RNameType] + SIZE[BodyDefs.Timestamp] + BodyDefs.RNameSize[name]]; WriteTimestamp[writer, stamp­]; HeapDefs.HeapWriteData[writer, [@type, SIZE[ProtocolDefs.RNameType]]]; HeapDefs.HeapWriteRName[writer, name]; END; ReadPassword: PUBLIC PROC [reader: HeapDefs.ReaderHandle] RETURNS [pw: ProtocolDefs.Password, stamp: BodyDefs.Timestamp] = BEGIN IF ReadComponentLength[reader] # SIZE[ProtocolDefs.Password] + SIZE[BodyDefs.Timestamp] THEN ERROR MangledHeapObject; stamp ¬ ReadTimestamp[reader]; [] ¬ HeapDefs.HeapReadData[reader, [@pw, SIZE[ProtocolDefs.Password]]]; END; WritePassword: PUBLIC PROC [ writer: HeapDefs.WriterHandle, pw: ProtocolDefs.Password, stamp: BodyDefs.Timestamp] = BEGIN WriteComponentLength[ writer, SIZE[ProtocolDefs.Password] + SIZE[BodyDefs.Timestamp]]; WriteTimestamp[writer, stamp]; HeapDefs.HeapWriteData[writer, [@pw, SIZE[ProtocolDefs.Password]]]; END; ReadConnect: PUBLIC PROC [ reader: HeapDefs.ReaderHandle, connect: ProtocolDefs.Connect] RETURNS [stamp: BodyDefs.Timestamp] = BEGIN [] ¬ ReadComponentLength[reader]; stamp ¬ ReadTimestamp[reader]; connect.length ¬ LAST[CARDINAL]; [, ] ¬ HeapDefs.HeapReadData[reader, [@(connect.length), SIZE[CARDINAL]]]; IF connect.length > connect.maxlength THEN ERROR MangledHeapObject; [] ¬ HeapDefs.HeapReadData[ reader, [@(connect.text), (1 + connect.length) / 2]]; END; WriteConnect: PUBLIC PROC [ writer: HeapDefs.WriterHandle, connect: ProtocolDefs.Connect, stamp: BodyDefs.Timestamp] = BEGIN WriteComponentLength[ writer, SIZE[BodyDefs.Timestamp] + SIZE[CARDINAL] + (1 + connect.length) / 2]; WriteTimestamp[writer, stamp]; HeapDefs.HeapWriteData[writer, [@(connect.length), SIZE[CARDINAL]]]; HeapDefs.HeapWriteData[writer, [@(connect.text), (1 + connect.length) / 2]]; END; WriteStampList: PROC [ writer: HeapDefs.WriterHandle, stamp: BodyDefs.Timestamp] = BEGIN WriteComponentLength[writer, SIZE[BodyDefs.Timestamp]]; WriteTimestamp[writer, stamp]; END; WriteRNameList: PROC [writer: HeapDefs.WriterHandle, name: BodyDefs.RName] = BEGIN WriteComponentLength[writer, BodyDefs.RNameSize[name]]; HeapDefs.HeapWriteRName[writer, name]; END; WriteEmptyList: PROC [writer: HeapDefs.WriterHandle] = INLINE BEGIN zero: CARDINAL ¬ 0; HeapDefs.HeapWriteData[writer, [@zero, SIZE[CARDINAL]]]; END; ReadComponentLength: PROC [reader: HeapDefs.ReaderHandle] RETURNS [length: CARDINAL] = INLINE BEGIN length ¬ LAST[CARDINAL]; [, ] ¬ HeapDefs.HeapReadData[reader, [@length, SIZE[CARDINAL]]]; END; WriteComponentLength: PROC [writer: HeapDefs.WriterHandle, length: CARDINAL] = BEGIN HeapDefs.HeapWriteData[writer, [@length, SIZE[CARDINAL]]]; END; ReadTimestamp: PROC [reader: HeapDefs.ReaderHandle] RETURNS [stamp: BodyDefs.Timestamp] = INLINE BEGIN [, ] ¬ HeapDefs.HeapReadData[reader, [@stamp, SIZE[BodyDefs.Timestamp]]]; END; WriteTimestamp: PROCEDURE [ writer: HeapDefs.WriterHandle, stamp: BodyDefs.Timestamp] = BEGIN HeapDefs.HeapWriteData[writer, [@stamp, SIZE[BodyDefs.Timestamp]]]; END; -- single-name updates -- -- RegistryDefs.UpdateInfo: TYPE = { done, noChange, outOfDate }; -- noChange => name already (not) there (timestamp may have been altered) -- outOfDate => better timestamp already in list AddName: PUBLIC PROC [ reader: HeapDefs.ReaderHandle, name: BodyDefs.RName, stamp: POINTER TO BodyDefs.Timestamp, writer: HeapDefs.WriterHandle] RETURNS [info: RegistryDefs.UpdateInfo] = BEGIN memInfo: RegistryDefs.UpdateInfo = SingleChangeToSublist[ reader, name, stamp, writer, add]; delMemInfo: RegistryDefs.UpdateInfo = SingleChangeToSublist[ reader, name, stamp, writer, remove]; info ¬ SELECT memInfo FROM done => IF delMemInfo = outOfDate THEN outOfDate ELSE done, noChange => IF delMemInfo = noChange THEN noChange ELSE ERROR, outOfDate => IF delMemInfo = noChange THEN outOfDate ELSE ERROR, ENDCASE => ERROR; END; RemoveName: PUBLIC PROC [ reader: HeapDefs.ReaderHandle, name: BodyDefs.RName, stamp: POINTER TO BodyDefs.Timestamp, writer: HeapDefs.WriterHandle] RETURNS [info: RegistryDefs.UpdateInfo] = BEGIN memInfo: RegistryDefs.UpdateInfo = SingleChangeToSublist[ reader, name, stamp, writer, remove]; delMemInfo: RegistryDefs.UpdateInfo = SingleChangeToSublist[ reader, name, stamp, writer, add]; info ¬ SELECT delMemInfo FROM done => IF memInfo = outOfDate THEN outOfDate ELSE memInfo, noChange => IF memInfo = noChange THEN noChange ELSE ERROR, outOfDate => IF memInfo = noChange THEN outOfDate ELSE ERROR, ENDCASE => ERROR; END; SingleChangeToSublist: PROC [ reader: HeapDefs.ReaderHandle, name: BodyDefs.RName, stamp: POINTER TO BodyDefs.Timestamp, writer: HeapDefs.WriterHandle, change: {add, remove}] RETURNS [info: RegistryDefs.UpdateInfo] = BEGIN beforeCount: CARDINAL ¬ 0; afterCount: CARDINAL ¬ 0; member: BodyDefs.RName = [BodyDefs.maxRNameLength]; originalLength: CARDINAL = ReadComponentLength[reader]; length: CARDINAL ¬ originalLength; lengthPos: HeapDefs.ObjectOffset = HeapDefs.GetWriterOffset[writer]; ResetLength: PROC = BEGIN -- length didn't change after all -- now: HeapDefs.ObjectOffset = HeapDefs.GetWriterOffset[writer]; HeapDefs.SetWriterOffset[writer, lengthPos]; WriteComponentLength[writer, originalLength]; HeapDefs.SetWriterOffset[writer, now]; END; WriteComponentLength[ writer, IF change = add THEN length + BodyDefs.RNameSize[name] ELSE length - BodyDefs.RNameSize[name]]; info ¬ done; -- copy preceding names -- WHILE length > 0 DO [] ¬ HeapDefs.HeapReadRName[reader, member]; length ¬ length - BodyDefs.RNameSize[member]; SELECT CompareRNames[member, name] FROM less => BEGIN HeapDefs.HeapWriteRName[writer, member]; beforeCount ¬ beforeCount + 1; END; equal => BEGIN IF change = add THEN { info ¬ noChange; HeapDefs.HeapWriteRName[writer, name]}; EXIT END; greater => BEGIN IF change = add THEN HeapDefs.HeapWriteRName[writer, name] ELSE info ¬ noChange; afterCount ¬ 1; HeapDefs.HeapWriteRName[writer, member]; EXIT END; ENDCASE => ERROR; REPEAT FINISHED => IF change = add THEN HeapDefs.HeapWriteRName[writer, name] ELSE info ¬ noChange; ENDLOOP; -- copy following names -- WHILE length > 0 DO [] ¬ HeapDefs.HeapReadRName[reader, member]; length ¬ length - BodyDefs.RNameSize[member]; HeapDefs.HeapWriteRName[writer, member]; afterCount ¬ afterCount + 1; ENDLOOP; SELECT info FROM done => NULL; noChange => ResetLength[]; ENDCASE => ERROR; -- stamp list length -- WriteComponentLength[ writer, IF info = noChange THEN ReadComponentLength[reader] ELSE IF change = add THEN ReadComponentLength[reader] + SIZE[BodyDefs.Timestamp] ELSE ReadComponentLength[reader] - SIZE[BodyDefs.Timestamp]]; THROUGH [1..beforeCount] DO WriteTimestamp[writer, ReadTimestamp[reader]]; ENDLOOP; IF change = add THEN BEGIN IF info = done THEN WriteTimestamp[writer, stamp­] ELSE BEGIN prev: BodyDefs.Timestamp = ReadTimestamp[reader]; best: BodyDefs.Timestamp; SELECT CompareTimestamps[stamp­, prev] FROM less => {best ¬ prev; info ¬ outOfDate}; equal => best ¬ stamp­; greater => best ¬ stamp­; ENDCASE => ERROR; WriteTimestamp[writer, best]; END; END ELSE IF info = noChange THEN NULL ELSE BEGIN prev: BodyDefs.Timestamp = ReadTimestamp[reader]; IF CompareTimestamps[stamp­, prev] = less THEN info ¬ outOfDate; END; THROUGH [1..afterCount] DO WriteTimestamp[writer, ReadTimestamp[reader]]; ENDLOOP; END; -- General merge algorithm -- Inlist: TYPE = RECORD [ RNameReader, TimestampReader: HeapDefs.ReaderHandle, RNameWordsLeft, TimestampWordsLeft: CARDINAL, RNameStart, TimestampStart: HeapDefs.ObjectOffset, name: BodyDefs.RName, stamp: BodyDefs.Timestamp, empty: BOOLEAN]; Outlist: TYPE = RECORD [ RNameWriter, TimestampWriter: HeapDefs.WriterHandle, RNameStart: HeapDefs.ObjectOffset, RNameWordsUsed, TimestampWordsUsed: CARDINAL]; MergeLists: PUBLIC PROC [ reader1, reader2: HeapDefs.ReaderHandle, writer: HeapDefs.WriterHandle] RETURNS [newer1, newer2: BOOLEAN] = BEGIN -- allocate buffers for R-Names locally, to avoid FSP fragmentation -- name1: BodyDefs.RName = [BodyDefs.maxRNameLength]; name2: BodyDefs.RName = [BodyDefs.maxRNameLength]; name3: BodyDefs.RName = [BodyDefs.maxRNameLength]; name4: BodyDefs.RName = [BodyDefs.maxRNameLength]; addlist1, addlist2, dellist1, dellist2: Inlist; n1, n2: BOOLEAN; CreateInlist[@addlist1, name1, reader1]; CreateInlist[@dellist1, name2, reader1]; CreateInlist[@addlist2, name3, reader2]; CreateInlist[@dellist2, name4, reader2]; [newer1, newer2] ¬ MergeSublists[ @addlist1, @addlist2, @dellist1, @dellist2, writer]; ResetInlist[@addlist1]; ResetInlist[@dellist1]; ResetInlist[@addlist2]; ResetInlist[@dellist2]; [n1, n2] ¬ MergeSublists[@dellist1, @dellist2, @addlist1, @addlist2, writer]; newer1 ¬ newer1 OR n1; newer2 ¬ newer2 OR n2; DeleteInlist[@addlist1]; DeleteInlist[@dellist1]; DeleteInlist[@addlist2]; DeleteInlist[@dellist2]; END; MergeSublists: PROCEDURE [ add1, add2, del1, del2: POINTER TO Inlist, writer: HeapDefs.WriterHandle] RETURNS [newer1, newer2: BOOLEAN] = BEGIN outlist: Outlist; CreateOutlist[@outlist, writer]; newer1 ¬ newer2 ¬ FALSE; UNTIL CompareElements[add1, add2] = Null DO WHILE CompareElements[add1, add2] = Less DO WHILE CompareElements[del2, add1] = Less DO Get[del2] ENDLOOP; IF ~(CompareElements[del2, add1] = Equal AND Newer[del2, add1]) THEN BEGIN Put[add1, @outlist]; newer1 ¬ TRUE END; Get[add1] ENDLOOP; IF CompareElements[add1, add2] = Equal THEN BEGIN IF Newer[add1, add2] THEN BEGIN Put[add1, @outlist]; newer1 ¬ TRUE END ELSE BEGIN Put[add2, @outlist]; newer2 ¬ TRUE END; Get[add1]; Get[add2] END; WHILE CompareElements[add2, add1] = Less DO WHILE CompareElements[del1, add2] = Less DO Get[del1] ENDLOOP; IF ~(CompareElements[del1, add2] = Equal AND Newer[del1, add2]) THEN BEGIN Put[add2, @outlist]; newer2 ¬ TRUE END; Get[add2] ENDLOOP; ENDLOOP; DeleteOutlist[@outlist, writer] END; CreateInlist: PROCEDURE [ inlist: POINTER TO Inlist, name: BodyDefs.RName, reader: HeapDefs.ReaderHandle] = BEGIN inlist.name ¬ name; inlist.RNameReader ¬ HeapDefs.CopyReader[reader]; inlist.RNameStart ¬ HeapDefs.GetReaderOffset[reader]; Skip[reader]; inlist.TimestampReader ¬ HeapDefs.CopyReader[reader]; inlist.TimestampStart ¬ HeapDefs.GetReaderOffset[reader]; Skip[reader]; inlist.RNameWordsLeft ¬ ReadComponentLength[inlist.RNameReader]; inlist.TimestampWordsLeft ¬ ReadComponentLength[inlist.TimestampReader]; Get[inlist]; END; ResetInlist: PROCEDURE [inlist: POINTER TO Inlist] = BEGIN HeapDefs.SetReaderOffset[inlist.RNameReader, inlist.RNameStart]; HeapDefs.SetReaderOffset[inlist.TimestampReader, inlist.TimestampStart]; inlist.RNameWordsLeft ¬ ReadComponentLength[inlist.RNameReader]; inlist.TimestampWordsLeft ¬ ReadComponentLength[inlist.TimestampReader]; Get[inlist]; END; DeleteInlist: PROCEDURE [inlist: POINTER TO Inlist] = BEGIN HeapDefs.HeapEndRead[inlist.RNameReader]; HeapDefs.HeapEndRead[inlist.TimestampReader]; END; CreateOutlist: PROCEDURE [outlist: POINTER TO Outlist, writer: HeapDefs.WriterHandle] = BEGIN outlist.RNameWriter ¬ writer; outlist.RNameStart ¬ HeapDefs.GetWriterOffset[writer]; WriteComponentLength[outlist.RNameWriter, 0] --placeholder-- ; outlist.RNameWordsUsed ¬ 0; outlist.TimestampWriter ¬ HeapDefs.HeapStartWrite[temp]; outlist.TimestampWordsUsed ¬ 0; END; DeleteOutlist: PROCEDURE [outlist: POINTER TO Outlist, writer: HeapDefs.WriterHandle] = BEGIN CopyToWriter: PROC [object: HeapDefs.ObjectNumber] = BEGIN reader: HeapDefs.ReaderHandle = HeapDefs.HeapStartRead[object]; bLength: CARDINAL = 64; buffer: ARRAY [0..bLength) OF WORD; length: CARDINAL ¬ outlist.TimestampWordsUsed; WHILE length > 0 DO used: CARDINAL = HeapDefs.HeapReadData[ reader, [@buffer, MIN[length, bLength]]].used; length ¬ length - used; HeapDefs.HeapWriteData[writer, [@buffer, used]]; ENDLOOP; HeapDefs.HeapEndRead[reader]; END; TimestampStart: HeapDefs.ObjectOffset = HeapDefs.GetWriterOffset[ outlist.RNameWriter]; -- write component length for RName list -- HeapDefs.SetWriterOffset[outlist.RNameWriter, outlist.RNameStart]; WriteComponentLength[outlist.RNameWriter, outlist.RNameWordsUsed]; HeapDefs.SetWriterOffset[outlist.RNameWriter, TimestampStart]; IF outlist.RNameWriter # writer THEN ERROR; -- write component length for timestamp list -- WriteComponentLength[writer, outlist.TimestampWordsUsed]; -- append timestamp list -- HeapDefs.HeapEndWrite[outlist.TimestampWriter, CopyToWriter]; END; Get: PROCEDURE [inlist: POINTER TO Inlist] = BEGIN IF inlist.RNameWordsLeft > 0 THEN BEGIN [] ¬ HeapDefs.HeapReadRName[inlist.RNameReader, inlist.name]; inlist.stamp ¬ ReadTimestamp[inlist.TimestampReader]; IF inlist.TimestampWordsLeft < SIZE[BodyDefs.Timestamp] OR inlist.RNameWordsLeft < BodyDefs.RNameSize[inlist.name] THEN ERROR MangledHeapObject; inlist.TimestampWordsLeft ¬ inlist.TimestampWordsLeft - SIZE[BodyDefs.Timestamp]; inlist.RNameWordsLeft ¬ inlist.RNameWordsLeft - BodyDefs.RNameSize[inlist.name]; IF (inlist.TimestampWordsLeft = 0 OR inlist.RNameWordsLeft = 0) AND inlist.TimestampWordsLeft + inlist.RNameWordsLeft # 0 THEN ERROR MangledHeapObject; inlist.empty ¬ FALSE; END ELSE inlist.empty ¬ TRUE; END; Put: PROCEDURE [inlist: POINTER TO Inlist, outlist: POINTER TO Outlist] = BEGIN IF inlist.empty THEN ERROR HeapObjectEnded; HeapDefs.HeapWriteRName[outlist.RNameWriter, inlist.name]; outlist.RNameWordsUsed ¬ outlist.RNameWordsUsed + BodyDefs.RNameSize[inlist.name]; WriteTimestamp[outlist.TimestampWriter, inlist.stamp]; outlist.TimestampWordsUsed ¬ outlist.TimestampWordsUsed + SIZE[BodyDefs.Timestamp]; END; CompareElements: PROCEDURE [e1, e2: POINTER TO Inlist] RETURNS [{Less, Equal, Greater, Null}] = BEGIN IF e1.empty THEN IF e2.empty THEN RETURN[Null] ELSE RETURN[Greater] ELSE IF e2.empty THEN RETURN[Less] ELSE SELECT CompareRNames[e1.name, e2.name] FROM less => RETURN[Less]; equal => RETURN[Equal]; greater => RETURN[Greater]; ENDCASE => ERROR; END; Newer: PROCEDURE [e1, e2: POINTER TO Inlist] RETURNS [BOOLEAN] = BEGIN RETURN[CompareTimestamps[e1.stamp, e2.stamp] = greater] END; CompareRNames: PUBLIC PROC [n1, n2: BodyDefs.RName] RETURNS [RegistryDefs.Comparison] = BEGIN length: CARDINAL = MIN[n1.length, n2.length]; i: CARDINAL; FOR i IN [0..length) DO BEGIN ch1: CHARACTER = IF n1[i] IN CHARACTER ['A..'Z] THEN (n1[i] - 'A) + 'a ELSE n1[i]; ch2: CHARACTER = IF n2[i] IN CHARACTER ['A..'Z] THEN (n2[i] - 'A) + 'a ELSE n2[i]; IF ch1 < ch2 THEN RETURN[less]; IF ch1 > ch2 THEN RETURN[greater]; END ENDLOOP; IF n1.length < n2.length THEN RETURN[less] ELSE IF n1.length = n2.length THEN RETURN[equal] ELSE RETURN[greater]; END; futureLimit: CARDINAL ¬ 14; CompareTimestamps: PUBLIC ENTRY PROC [t1, t2: BodyDefs.Timestamp] RETURNS [RegistryDefs.Comparison] = BEGIN -- Garbage check: treat timestamps more than 14 days future as 0 -- latest: Time.Packed = LOOPHOLE[prevTime + (LONG[futureLimit] * 24) * 60 * 60]; IF t1.time > latest THEN RETURN[less]; IF t2.time > latest THEN RETURN[greater]; IF t1.time < t2.time THEN RETURN[less]; IF t1.time > t2.time THEN RETURN[greater]; IF t1.net < t2.net THEN RETURN[less]; IF t1.net > t2.net THEN RETURN[greater]; IF t1.host < t2.host THEN RETURN[less]; IF t1.host > t2.host THEN RETURN[greater]; RETURN[equal] END; prevTime: Time.Packed ¬ Time.Current[]; aWeeWhile: CONDITION; MakeTimestamp: PUBLIC ENTRY PROCEDURE RETURNS [stamp: BodyDefs.Timestamp] = BEGIN newTime: Time.Packed; WHILE (newTime ¬ Time.Current[]) = prevTime DO WAIT aWeeWhile -- ensure uniqueness of timestamps -- ENDLOOP; stamp.net ¬ ThisMachine.net; stamp.host ¬ ThisMachine.host; stamp.time ¬ prevTime ¬ newTime; END; ThisMachine: PupStream.PupAddress; -- regPurger subroutines -- CheckStampList: PUBLIC PROC [ reader: HeapDefs.ReaderHandle, limit: BodyDefs.Timestamp] RETURNS [old: BOOLEAN] = BEGIN length: CARDINAL = ReadComponentLength[reader]; THROUGH [0..length / SIZE[BodyDefs.Timestamp]) DO IF CompareTimestamps[ReadTimestamp[reader], limit] = less THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE] END; FilterStampList: PUBLIC PROC [ reader: HeapDefs.ReaderHandle, limit: BodyDefs.Timestamp, writer: HeapDefs.WriterHandle] = BEGIN namePos: HeapDefs.ObjectOffset = HeapDefs.GetWriterOffset[writer]; nameLength: CARDINAL ¬ 0; stampLength: CARDINAL; stampReader: HeapDefs.ReaderHandle = HeapDefs.CopyReader[reader]; Action: PROC [name: BodyDefs.RName] RETURNS [done: BOOLEAN] = BEGIN stamp: BodyDefs.Timestamp = ReadTimestamp[stampReader]; done ¬ FALSE; IF CompareTimestamps[stamp, limit] # less THEN BEGIN HeapDefs.HeapWriteRName[writer, name]; nameLength ¬ nameLength + BodyDefs.RNameSize[name]; END ELSE stampLength ¬ stampLength - SIZE[BodyDefs.Timestamp]; END; Skip[stampReader]; stampLength ¬ ReadComponentLength[stampReader]; WriteComponentLength[writer, 0]; EnumerateRList[reader, Action]; BEGIN newPos: HeapDefs.ObjectOffset = HeapDefs.GetWriterOffset[writer]; HeapDefs.SetWriterOffset[writer, namePos]; WriteComponentLength[writer, nameLength]; HeapDefs.SetWriterOffset[writer, newPos]; END; HeapDefs.HeapEndRead[stampReader]; WriteComponentLength[writer, stampLength]; THROUGH [0..ReadComponentLength[reader] / SIZE[BodyDefs.Timestamp]) DO stamp: BodyDefs.Timestamp = ReadTimestamp[reader]; IF CompareTimestamps[stamp, limit] # less THEN WriteTimestamp[writer, stamp]; ENDLOOP; END; PupDefs.PupNameLookup[@ThisMachine, "ME"]; Process.InitializeCondition[@aWeeWhile, Process.MsecToTicks[100]]; END.