-- TEditBitmapCacheImpl.mesa; Edited by McGregor on 4-Dec-81 9:48:21 DIRECTORY Inline USING [LongNumber], TextEdit USING [Offset], TextNode USING [Location], TEditBitmapCache USING [Bitmap], TEditCompile USING [maxCacheEntries, maxCacheSize, useCache]; TEditBitmapCacheImpl: MONITOR EXPORTS TEditBitmapCache = BEGIN LRUVal: TYPE = LONG CARDINAL ← 0; CacheEntry: TYPE = RECORD [ lru: LRUVal, key: TextNode.Location, bitmap: TEditBitmapCache.Bitmap ]; CacheIndex: TYPE = [0..TEditCompile.maxCacheSize); cache: REF ARRAY CacheIndex OF CacheEntry ← NEW[ARRAY CacheIndex OF CacheEntry]; cacheFree: CacheIndex ← TEditCompile.maxCacheEntries; lruCount: LRUVal; Fetch: PUBLIC ENTRY PROCEDURE [key: TextNode.Location] RETURNS [TEditBitmapCache.Bitmap] = BEGIN -- returns NIL if no cache hit entry: CacheIndex; found: BOOLEAN; IF TEditCompile.useCache THEN BEGIN [found, entry] ← Find[key]; IF ~found THEN RETURN [NIL]; cache[entry].lru ← NextLRU[]; RETURN[cache[entry].bitmap]; END ELSE RETURN[NIL]; END; AddCacheEntry: PUBLIC ENTRY PROCEDURE [key: TextNode.Location, bitmap: TEditBitmapCache.Bitmap] = BEGIN entry: CacheIndex ← Hash[key]; IF TEditCompile.useCache THEN BEGIN IF bitmap=NIL OR key.node=NIL THEN ERROR; IF cacheFree=0 THEN FlushLRU; UNTIL (cache[entry].bitmap=NIL) OR (key=cache[entry].key) DO entry ← Next[entry]; ENDLOOP; IF key=cache[entry].key THEN RemoveEntry[entry]; cache[entry] ← [NextLRU[], key, bitmap]; cacheFree ← cacheFree - 1; END; END; FlushCacheEntry: PUBLIC ENTRY PROCEDURE [key: TextNode.Location] = BEGIN -- flushes all in node if key.where = LAST[Offset] entry: CacheIndex; found: BOOLEAN; IF TEditCompile.useCache THEN BEGIN IF key.where=LAST[TextEdit.Offset] THEN BEGIN FOR entry IN CacheIndex DO IF cache[entry].bitmap#NIL AND cache[entry].key.node=key.node THEN RemoveEntry[entry]; ENDLOOP; END ELSE BEGIN [found, entry] ← Find[key]; IF found THEN RemoveEntry[entry]; END; END; END; FlushOldCacheRange: PUBLIC ENTRY PROCEDURE [key: TextNode.Location] = BEGIN -- flushes all in node if last referenced earlier than key and whose -- where is greater or equal to than the key entry: CacheIndex; found: BOOLEAN; threshold: LRUVal; IF TEditCompile.useCache THEN BEGIN [found, entry] ← Find[key]; IF ~found THEN ERROR; threshold ← cache[entry].lru; FOR entry IN CacheIndex DO IF cache[entry].bitmap#NIL AND cache[entry].key.node=key.node AND cache[entry].lru<threshold AND cache[entry].key.where>=key.where THEN RemoveEntry[entry]; ENDLOOP; END; END; --UpdateCacheNodeOffsets: PUBLIC ENTRY TEditDocument.UpdateNodeOffsetsProc = -- BEGIN -- IF TEditCompile.useCache THEN BEGIN -- FOR entry:CacheIndex IN CacheIndex DO -- IF cache[entry].bitmap#NIL AND cache[entry].key.node=pos.node AND -- cache[entry].key.where>=pos.where THEN BEGIN -- cache[entry].key.where ← SELECT op FROM -- add => cache[entry].key.where + delta -- ENDCASE => cache[entry].key.where - delta; -- MoveEntry[entry, cache[entry].key]; -- END; -- ENDLOOP; -- END; -- END; FlushLRU: INTERNAL PROCEDURE = BEGIN minEntry, entry: CacheIndex; minVal: LRUVal ← LAST[LRUVal]; FOR entry IN CacheIndex DO IF cache[entry].bitmap#NIL AND cache[entry].lru<minVal THEN BEGIN minVal ← cache[entry].lru; minEntry ← entry; END; ENDLOOP; RemoveEntry[minEntry]; END; MoveEntry: INTERNAL PROCEDURE [entry: CacheIndex, newKey: TextNode.Location] = BEGIN -- this is to patch around the horrible problem of not having persistant names -- for text lines. If we change the key values of entries in the cache, we -- need to physically move them so that the hash function we use still gets a hit. bitmap: TEditBitmapCache.Bitmap ← cache[entry].bitmap; newEntry: CacheIndex ← Hash[newKey]; cache[entry].bitmap ← NIL; -- clear old value cache[entry].key ← [NIL, 0]; UNTIL (cache[newEntry].bitmap=NIL) OR (newKey=cache[newEntry].key) DO newEntry ← Next[newEntry]; ENDLOOP; IF newKey=cache[newEntry].key THEN cacheFree ← cacheFree + 1; -- the above case can (unobviously) occur when an intervening entry has been -- deleted, allowing for an older, identically keyed object to reside in the cache cache[newEntry] ← [NextLRU[], newKey, bitmap]; END; Find: INTERNAL PROCEDURE [key: TextNode.Location] RETURNS [found: BOOLEAN, entry: CacheIndex] = BEGIN entry ← Hash[key]; UNTIL cache[entry].key=key DO IF cache[entry].bitmap=NIL THEN RETURN [FALSE, entry]; entry ← Next[entry]; ENDLOOP; RETURN[TRUE, entry]; END; EmptyCache: INTERNAL PROCEDURE = BEGIN -- Flush everything in the cache FOR entry: CacheIndex IN CacheIndex DO IF cache[entry].bitmap#NIL THEN RemoveEntry[entry] ENDLOOP; IF cacheFree#TEditCompile.maxCacheEntries THEN ERROR; END; RemoveEntry: INTERNAL PROCEDURE [entry: CacheIndex] = BEGIN IF cacheFree=TEditCompile.maxCacheEntries THEN ERROR; cache[entry] ← [0, [NIL, 0], NIL]; -- throw this away cacheFree ← cacheFree + 1; END; Hash: INTERNAL PROCEDURE [key: TextNode.Location] RETURNS [CacheIndex] = INLINE BEGIN OPEN Inline; RETURN [(LOOPHOLE[key.node, LongNumber].lowbits + LOOPHOLE[key.where, LongNumber].lowbits) MOD TEditCompile.maxCacheSize] END; Next: INTERNAL PROCEDURE [current: CacheIndex] RETURNS [CacheIndex] = INLINE BEGIN RETURN[(current+3) MOD TEditCompile.maxCacheSize]; END; NextLRU: INTERNAL PROCEDURE RETURNS [LRUVal] = INLINE BEGIN RETURN[lruCount ← lruCount+1]; END; END.