DIRECTORY CDLRUCache, CD; CDLRUCacheImpl: CEDAR MONITOR LOCKS lruCache USING lruCache: LRUCache IMPORTS CD EXPORTS CDLRUCache = BEGIN AequivalenceProc: TYPE = PROC[mySpecific, other: REF ANY] RETURNS [BOOL]; NewProc: TYPE = PROC [] RETURNS [CD.ObPtr]; LRUCache: TYPE = REF LRUCacheRep; LRUCacheRep: PUBLIC TYPE = MONITORED RECORD [ freeObject: CD.ObPtr_NIL, freeEntry: CARDINAL_0, aequivalence: AequivalenceProc, new: NewProc, access: SEQUENCE size: CARDINAL OF LinkRecord -- access[0] is not used but a dummy ]; DefaultAequivalence: PROC [mySpecific, other: REF ANY] RETURNS [BOOL] = { RETURN [mySpecific=other] }; Create: PUBLIC PROC [size: CARDINAL, aequivalenceProc: AequivalenceProc, newProc: NewProc] RETURNS [LRUCache] = BEGIN lru: LRUCache; size _ MAX[MIN[size, 256], 1]+1; --users don't know that access[0] is a dummy lru _ NEW[LRUCacheRep[size]]; lru.aequivalence _ aequivalenceProc; IF lru.aequivalence=NIL THEN lru.aequivalence_DefaultAequivalence; lru.new _ newProc; RETURN [lru] END; UnusedOrNew: PUBLIC ENTRY PROC [lruCache: LRUCache] RETURNS [CD.ObPtr] = BEGIN ob: CD.ObPtr; IF lruCache.new=NIL THEN RETURN WITH ERROR CD.Error[ec: callingError, explanation: "no newproc defined"]; IF lruCache.freeObject=NIL THEN ob _ lruCache.new[] ELSE { ob _ lruCache.freeObject; lruCache.freeObject _ NIL }; RETURN [ob] END; LinkRecord: TYPE = RECORD[ object: CD.ObPtr, hash: INT, older, younger: INTEGER ]; ReplaceByAequivalent: PUBLIC ENTRY PROC [lruCache: LRUCache, ob: CD.ObPtr] RETURNS [CD.ObPtr] = BEGIN Hash: PROC[ob: CD.ObPtr] RETURNS [INT] = INLINE { RETURN [ (ob.size.x MOD 1000B)*400000B+(ob.size.y MOD 1000B)*400B+ob.level] }; Aequivalent: PROC[ob, ob2: CD.ObPtr] RETURNS [BOOL] = INLINE BEGIN IF ob.size#ob2.size OR ob.p#ob2.p OR ob.level#ob2.level THEN RETURN [FALSE]; RETURN [lruCache.aequivalence[ob.specificRef, ob2.specificRef! ANY => GOTO SomeError]] EXITS SomeError => RETURN [FALSE] END; RemoveEntry: INTERNAL PROC[entry: INTEGER] = BEGIN lruCache.access[lruCache.access[entry].older].younger _ lruCache.access[entry].younger; lruCache.access[lruCache.access[entry].younger].older _ lruCache.access[entry].older; END; IncludeEntryAsYoungest: INTERNAL PROC[entry: INTEGER] = BEGIN lruCache.access[entry].older _ lruCache.access[0].older; lruCache.access[entry].younger _ 0; lruCache.access[lruCache.access[0].older].younger _ entry; lruCache.access[0].older _ i; END; i: INTEGER; hash: INT = Hash[ob]; FOR i _ lruCache.access[0].older, lruCache.access[i].older WHILE i#0 DO IF hash=lruCache.access[i].hash AND Aequivalent[lruCache.access[i].object, ob] THEN { RemoveEntry[i]; IncludeEntryAsYoungest[i]; lruCache.freeObject _ ob; RETURN [lruCache.access[i].object] }; ENDLOOP; IF lruCache.freeEntry