-- RTTCacheImpl.mesa
-- Russ Atkinson, June 28, 1982 1:32 pm
-- Last Modified By Paul Rovner On June 30, 1983 12:15 pm

DIRECTORY
Rope USING [Equal, ROPE],
RTTCache,
SafeStorage USING [Type];

RTTCacheImpl: CEDAR MONITOR
IMPORTS Rope
EXPORTS RTTCache
= BEGIN OPEN Rope, RTTCache, SafeStorage;

InvalidIndex: PUBLIC ERROR = CODE;

HashSize: CARDINAL = 256;
HashRange: TYPE = CARDINAL [0..HashSize);

intCache: IntCache ← NEW[IntCacheRep];
IntCache: TYPE = REF IntCacheRep;
IntCacheRep: TYPE = RECORD
[enabled: BOOLTRUE,
probes, misses, filled: INT ← 0,
hashVec: ARRAY HashRange OF IntEntry];

refCache: RefCache ← NEW[RefCacheRep];
RefCache: TYPE = REF RefCacheRep;
RefCacheRep: TYPE = RECORD
[enabled: BOOLTRUE,
probes, misses, filled: INT ← 0,
hashVec: ARRAY HashRange OF RefEntry];

Hash: PROC [type: Type, proc: ProcKey] RETURNS [HashRange] = {
RETURN [(LOOPHOLE[type, CARDINAL] + LOOPHOLE[proc, CARDINAL])
MOD HashSize];
};

LookupInt: PUBLIC ENTRY PROC
[type: Type, proc: ProcKey] RETURNS [entry: IntEntry] = {
-- comment
ENABLE UNWIND => NULL;
hash: HashRange ← Hash[type, proc];
intCache.probes ← intCache.probes + 1;
FOR entry ← intCache.hashVec[hash], entry.next WHILE entry # NIL DO
IF entry.type = type AND entry.proc = proc THEN RETURN;
ENDLOOP;
entry ← NEW[IntEntryRep ← [next: intCache.hashVec[hash], type: type, proc: proc]];
intCache.hashVec[hash] ← entry;
intCache.misses ← intCache.misses + 1;
};

FillIntEntry: PUBLIC ENTRY PROC
[entry: IntEntry, value: INT] RETURNS [alreadyFull: BOOL] = {
-- comment
ENABLE UNWIND => NULL;
IF NOT intCache.enabled THEN RETURN [TRUE];
IF entry.valid THEN RETURN [TRUE];
entry.int ← value;
entry.valid ← TRUE;
intCache.filled ← intCache.filled + 1;
RETURN [FALSE];
};

ClearIntEntry: PUBLIC ENTRY PROC [entry: IntEntry] = {
-- comment
ENABLE UNWIND => NULL;
IF NOT entry.valid THEN RETURN;
entry.int ← -1;
entry.valid ← FALSE;
intCache.filled ← intCache.filled - 1;
};

GetInt: PUBLIC ENTRY PROC [entry: IntEntry] RETURNS [value: INT, valid: BOOL] = {
-- atomically retrieves the value in the entry
RETURN [entry.int, entry.valid];
};

LookupRef: PUBLIC ENTRY PROC
[type: Type, proc: ProcKey] RETURNS [entry: RefEntry] = {
-- comment
ENABLE UNWIND => NULL;
hash: HashRange ← Hash[type, proc];
refCache.probes ← refCache.probes + 1;
FOR entry ← refCache.hashVec[hash], entry.next WHILE entry # NIL DO
IF entry.type = type AND entry.proc = proc THEN RETURN;
ENDLOOP;
entry ← NEW[RefEntryRep ← [next: refCache.hashVec[hash], type: type, proc: proc]];
refCache.hashVec[hash] ← entry;
refCache.misses ← refCache.misses + 1;
};

FillRefEntry: PUBLIC ENTRY PROC
[entry: RefEntry, value: REF] RETURNS [alreadyFull: BOOL] = {
-- comment
ENABLE UNWIND => NULL;
IF NOT refCache.enabled THEN RETURN [TRUE];
IF entry.valid THEN RETURN [TRUE];
entry.ref ← value;
entry.valid ← TRUE;
refCache.filled ← refCache.filled + 1;
RETURN [FALSE];
};

ClearRefEntry: PUBLIC ENTRY PROC [entry: RefEntry] = {
-- comment
ENABLE UNWIND => NULL;
IF NOT entry.valid THEN RETURN;
entry.ref ← NIL;
entry.valid ← FALSE;
refCache.filled ← refCache.filled - 1;
};

GetRef: PUBLIC ENTRY PROC [entry: RefEntry] RETURNS [value: REF, valid: BOOL] = {
-- atomically retrieves the value in the entry
RETURN [entry.ref, entry.valid];
};

GetStats: PUBLIC ENTRY PROC
RETURNS [intProbes, intMisses, intFilled, refProbes, refMisses, refFilled: INT] = {
ENABLE UNWIND => NULL;
RETURN [intCache.probes, intCache.misses, intCache.filled,
refCache.probes, refCache.misses, refCache.filled];
};

NewComponentMap: PUBLIC PROC [len: NAT] RETURNS [map: ComponentMap] = {
map ← NEW[ComponentMapRep[len]];
FOR i: NAT IN [0..len) DO
map[i] ← NullComponentEntry;
ENDLOOP;
};

FillNameComponent: PUBLIC ENTRY PROC
[map: ComponentMap, index: INT, name: ROPE] RETURNS [alreadyFull: BOOL] = {
-- comment
ENABLE UNWIND => NULL;
x: NAT ← 0;
IF index < 0 OR index >= map.len THEN RETURN WITH ERROR InvalidIndex;
x ← index;
IF map[x].validName THEN RETURN [TRUE];
map[x].name ← name;
map[x].validName ← TRUE;
map.filledNames ← map.filledNames + 1;
RETURN [FALSE];
};

FillTypeComponent: PUBLIC ENTRY PROC
[map: ComponentMap, index: INT, type: Type] RETURNS [alreadyFull: BOOL] = {
-- comment
ENABLE UNWIND => NULL;
x: NAT ← 0;
IF index < 0 OR index >= map.len THEN RETURN WITH ERROR InvalidIndex;
x ← index;
IF map[x].validType THEN RETURN [TRUE];
map[x].type ← type;
map[x].validType ← TRUE;
map.filledTypes ← map.filledTypes + 1;
RETURN [FALSE];
};

GetComponentAtIndex: PUBLIC ENTRY PROC
[map: ComponentMap, index: INT] RETURNS [comp: ComponentEntry] = {
-- atomically returns the given component entry
-- will ERROR InvalidIndex if index NOT IN [0..map.len)
ENABLE UNWIND => NULL;
IF index NOT IN [0..map.len) THEN RETURN WITH ERROR InvalidIndex;
RETURN [map[index]];
};

GetComponentForName: PUBLIC ENTRY PROC
[map: ComponentMap, name: ROPE, case: BOOLTRUE]
RETURNS [index: INT, comp: ComponentEntry] = {
-- atomically returns the given component entry for the given name
-- IF case = TRUE, then name search is case-sensitive (Rope convention)
-- will return [-1, NullComponentEntry] if no such name
ENABLE UNWIND => NULL;
FOR i: NAT IN [0..map.len) DO
comp: ComponentEntry ← map[i];
IF Rope.Equal[name, comp.name, case] THEN
RETURN [i, comp];
ENDLOOP;
RETURN [-1, NullComponentEntry];
};

-- internal procs for flushing the cache

DisableAndFlushIntCache: ENTRY PROC = {
ENABLE UNWIND => NULL;
intCache.enabled ← FALSE;
intCache.probes ← intCache.misses ← intCache.filled ← 0;
FOR i: HashRange IN HashRange DO
FOR list: IntEntry ← intCache.hashVec[i], list.next UNTIL list = NIL DO
list.valid ← FALSE;
list.int ← -1;
ENDLOOP;
ENDLOOP;
};

DisableAndFlushRefCache: ENTRY PROC = {
ENABLE UNWIND => NULL;
refCache.enabled ← FALSE;
refCache.probes ← refCache.misses ← refCache.filled ← 0;
FOR i: HashRange IN HashRange DO
FOR list: RefEntry ← refCache.hashVec[i], list.next UNTIL list = NIL DO
list.valid ← FALSE;
list.ref ← NIL;
ENDLOOP;
ENDLOOP;
};

DisableAndFlushAllCaches: PROC = {
DisableAndFlushIntCache[];
DisableAndFlushRefCache[];
};

END.