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