-- File: NetDirCache.mesa, Last Edit: HGM March 12, 1981 12:59 PM DIRECTORY String USING [EquivalentString], Storage USING [Node, Free], StatsDefs USING [StatCounterIndex, StatIncr], NameServerDefs USING [ CacheEntry, CacheEntryObject, SearchNetDirForName, SearchNetDirForAddress], PupTypes USING [PupAddress]; NetDirCache: MONITOR IMPORTS String, Storage, StatsDefs, NameServerDefs EXPORTS NameServerDefs = BEGIN OPEN StatsDefs, NameServerDefs; PupAddress: TYPE = PupTypes.PupAddress; CacheEntry: TYPE = NameServerDefs.CacheEntry; CacheEntryObject: TYPE = NameServerDefs.CacheEntryObject; firstCacheEntry: CacheEntry ← NIL; cacheLimit: CARDINAL ← 400; cacheWordsInUse: CARDINAL ← 0; sequenceNumber: CARDINAL ← 0; statHits, statMisses, statNone, statFile: PUBLIC StatCounterIndex; EmptyCacheEntryObject: CacheEntryObject = [next: NIL, size: SIZE[CacheEntryObject], count: 1, sequence: 0, names: DESCRIPTOR[NIL, 0], addrs: DESCRIPTOR[NIL, 0]]; SetCacheSize: PUBLIC PROCEDURE [n: CARDINAL] = BEGIN cacheLimit ← n; END; BumpCacheSize: PUBLIC PROCEDURE [n: INTEGER] = BEGIN cacheLimit ← cacheLimit + n; END; FlushWholeCache: PUBLIC ENTRY PROCEDURE = BEGIN WHILE firstCacheEntry # NIL DO FlushOneEntry[]; ENDLOOP; IF cacheWordsInUse # 0 THEN ERROR CacheShouldBeEmptyNow; END; EnumerateCache: PUBLIC ENTRY PROCEDURE [proc: PROCEDURE [CacheEntry]] = BEGIN FOR ce: CacheEntry ← firstCacheEntry, ce.next UNTIL ce = NIL DO proc[ce]; ENDLOOP; END; GetCacheLocation: PUBLIC PROCEDURE RETURNS [POINTER TO CacheEntry] = BEGIN RETURN[@firstCacheEntry]; END; SearchCacheForName: PUBLIC ENTRY PROCEDURE [key: STRING] RETURNS [ce: CacheEntry] = BEGIN previous: CacheEntry; FOR ce ← firstCacheEntry, ce.next UNTIL ce = NIL DO FOR i: CARDINAL IN [0..LENGTH[ce.names]) DO IF String.EquivalentString[key, ce.names[i]] THEN BEGIN StatIncr[statHits]; IF LENGTH[ce.addrs] = 0 THEN StatIncr[statMisses]; ce.count ← ce.count + 1; IF firstCacheEntry # ce THEN BEGIN -- move this entry to the head of the list previous.next ← ce.next; ce.next ← firstCacheEntry; firstCacheEntry ← ce; END; RETURN; END; ENDLOOP; previous ← ce; ENDLOOP; StatIncr[statNone]; END; SearchCacheForAddress: PUBLIC ENTRY PROCEDURE [key: PupAddress] RETURNS [ce: CacheEntry] = BEGIN previous: CacheEntry; FOR ce ← firstCacheEntry, ce.next UNTIL ce = NIL DO FOR i: CARDINAL IN [0..LENGTH[ce.addrs]) DO IF key = ce.addrs[i] THEN BEGIN StatIncr[statHits]; IF LENGTH[ce.addrs] = 0 THEN StatIncr[statMisses]; ce.count ← ce.count + 1; IF firstCacheEntry # ce THEN BEGIN -- move this entry to the head of the list previous.next ← ce.next; ce.next ← firstCacheEntry; firstCacheEntry ← ce; END; RETURN; END; ENDLOOP; previous ← ce; ENDLOOP; StatIncr[statNone]; END; ForceNameIntoCache: PUBLIC ENTRY PROCEDURE [key: STRING] RETURNS [ce: CacheEntry] = BEGIN mine: CacheEntryObject ← EmptyCacheEntryObject; IF ~NameServerDefs.SearchNetDirForName[key, @mine] THEN RETURN[NIL]; StatIncr[statFile]; ce ← AddToCache[@mine]; END; ForceAddressIntoCache: PUBLIC ENTRY PROCEDURE [key: PupAddress] RETURNS [ce: CacheEntry] = BEGIN mine: CacheEntryObject ← EmptyCacheEntryObject; IF ~NameServerDefs.SearchNetDirForAddress[key, @mine] THEN RETURN[NIL]; StatIncr[statFile]; ce ← AddToCache[@mine]; END; NothingInCacheToFlush: ERROR = CODE; CacheShouldBeEmptyNow: ERROR = CODE; FlushOneEntry: INTERNAL PROCEDURE = BEGIN previous, ce: CacheEntry; IF firstCacheEntry = NIL THEN ERROR NothingInCacheToFlush; ce ← firstCacheEntry; IF firstCacheEntry.next = NIL THEN firstCacheEntry ← NIL ELSE BEGIN WHILE ce.next # NIL DO previous ← ce; ce ← ce.next; ENDLOOP; previous.next ← NIL; END; cacheWordsInUse ← cacheWordsInUse - ce.size; FOR i: CARDINAL IN [0..LENGTH[ce.names]) DO Storage.Free[ce.names[i]]; ENDLOOP; IF BASE[ce.names] # NIL THEN Storage.Free[BASE[ce.names]]; IF BASE[ce.addrs] # NIL THEN Storage.Free[BASE[ce.addrs]]; Storage.Free[ce]; END; AddToCache: INTERNAL PROCEDURE [his: CacheEntry] RETURNS [ce: CacheEntry] = BEGIN WHILE his.size + cacheWordsInUse > cacheLimit DO FlushOneEntry[]; ENDLOOP; ce ← Storage.Node[SIZE[CacheEntryObject]]; ce↑ ← his↑; ce.next ← firstCacheEntry; ce.sequence ← (sequenceNumber ← sequenceNumber + 1); firstCacheEntry ← ce; cacheWordsInUse ← cacheWordsInUse + ce.size; END; END.