
<<SMProjImpl.mesa>>
<<Copyright © 1985 by Xerox Corporation.  All rights reserved.>>
<<last edit by Satterthwaite, May 14, 1986 4:06:04 pm PDT>>

DIRECTORY
Basics: TYPE USING [BITXOR],
BcdDefs: TYPE USING [Base, BcdBase, FTNull, MTIndex],
CompilerOps: TYPE USING [Transaction],
CS: TYPE USING [EndsIn, PartialName, z],
FS: TYPE USING [Close, Error, GetInfo, Open, OpenFile, nullOpenFile, Read],
HashTable: TYPE USING [Table, Create, Erase, ForEach, Fetch, Store],
Rope: TYPE USING [Concat, --Equal,-- ROPE],
SMProj: TYPE USING [Proj, ProjInfo],
TimeStamp: TYPE USING [Stamp],
VM: TYPE USING [AddressForPageNumber, Allocate, Free, Interval, nullInterval];

SMProjImpl: CEDAR MONITOR
IMPORTS Basics, CS, FS, HashTable, Rope, VM
EXPORTS SMProj ~ {

Proj: TYPE ~ SMProj.Proj;    -- can't be opaque with current RedBlackTree impl

<<all code in this module updates the projection table>>
<<therefore all PUBLIC procedures acquire the monitor lock>>

<<MDS usage (protected by the monitor lock)>>
table: HashTable.Table;
<<end of MDS>>


<<operations for cache management>>

GetKey: PROC[n: Proj] RETURNS[TimeStamp.Stamp] ~ {RETURN[n.stamp]};

CompareKeys: PROC[k1, k2: TimeStamp.Stamp] RETURNS[BOOL] ~ {RETURN[k1 = k2]};

HashFromKey: PROC[k: TimeStamp.Stamp] RETURNS[CARDINAL] ~ {
words: ARRAY [0..3) OF WORD ~ LOOPHOLE[k];
RETURN[Basics.BITXOR[Basics.BITXOR[words[0], words[1]], words[2]]]};

<<code to manipulate the projection (derived file) cache>>

Flush: PUBLIC ENTRY PROC ~ {
ENABLE UNWIND => {NULL};
table.Erase[];  table _ NIL};

Reset: PUBLIC ENTRY PROC ~ {
ENABLE UNWIND => {NULL};

ResetProj: PROC[proj: Proj] RETURNS[stop: BOOL_FALSE] ~ {
IF proj.state > $analyzed THEN {
proj.file.Close;  proj.file _ FS.nullOpenFile;
proj.state _ $analyzed};
proj.cantOpen _ FALSE};

[] _ table.ForEach[ResetProj]};

Erase: PUBLIC ENTRY PROC[proj: Proj] ~ {
ENABLE UNWIND => {NULL};
oldFile: FS.OpenFile ~ proj.file;
IF oldFile ~= FS.nullOpenFile THEN {

EraseProj: PROC[proj: Proj] RETURNS[stop: BOOL_FALSE] ~ {
IF oldFile = proj.file THEN {
proj.state _ MIN[proj.state, $analyzed];
proj.file.Close;  proj.file _ FS.nullOpenFile;
proj.cantOpen _ TRUE;
proj.localName _ NIL}
};

[] _ table.ForEach[EraseProj]};
};


Analyzed: PUBLIC ENTRY PROC[proj: Proj] RETURNS[BOOL] ~ {
ENABLE UNWIND => {NULL};
RETURN[proj # NIL AND proj.state >= $analyzed]};

Available: PUBLIC ENTRY PROC[proj: Proj] RETURNS[BOOL] ~ {
ENABLE UNWIND => {NULL};
RETURN[proj # NIL AND proj.state = $opened]};


Fill: PUBLIC ENTRY PROC[proj: Proj, localName, wDir: Rope.ROPE, new: BOOL] ~ {
ENABLE UNWIND => {NULL};
IF proj.state < $opened THEN {
proj.localName _
(IF CS.EndsIn[localName, ".bcd"] THEN localName ELSE localName.Concat[".bcd"]);
proj.wDir _ (IF CS.PartialName[localName] THEN wDir ELSE NIL);
IF ~new AND ~proj.cantOpen THEN OpenProj[proj]};
};


Find: PUBLIC ENTRY PROC[stamp: TimeStamp.Stamp] RETURNS[proj: Proj] ~ {
ENABLE UNWIND => {NULL};
proj _ table.Fetch[stamp].value;
IF proj = NIL THEN {
proj _ (CS.z).NEW[SMProj.ProjInfo _ [stamp~stamp, state~$empty]];
[] _ table.Store[stamp, proj]};
RETURN};


Read: PUBLIC ENTRY PROC[proj: Proj]
RETURNS[pages: VM.Interval, base: BcdDefs.BcdBase] ~ {
ENABLE UNWIND => {NULL};
RETURN (IF proj = NIL OR proj.file = FS.nullOpenFile
THEN [VM.nullInterval, NIL] ELSE LoadUpBcd[proj.file])
};


FindFile: PROC[proj: Proj] RETURNS[file: FS.OpenFile] ~ {
file _ FS.Open[name~proj.localName, wDir~proj.wDir
! FS.Error => {file _ FS.nullOpenFile; CONTINUE}];
RETURN};

OpenProj: PROC[proj: Proj] ~ {
file: FS.OpenFile ~ FindFile[proj];
IF file = FS.nullOpenFile THEN proj.cantOpen _ TRUE
ELSE IF proj.state = $analyzed THEN {
proj.file _ file;  proj.state _ $opened}
ELSE TRUSTED {
bcdBase: BcdDefs.BcdBase;
interval: VM.Interval;
[interval, bcdBase] _ LoadUpBcd[file];
IF bcdBase.version ~= proj.stamp THEN proj.cantOpen _ TRUE
ELSE {    -- should reject configs, etc.
firstMti: BcdDefs.MTIndex ~ BcdDefs.MTIndex.FIRST;
mtb: BcdDefs.Base ~ LOOPHOLE[bcdBase + bcdBase.mtOffset];
sgb: BcdDefs.Base ~ LOOPHOLE[bcdBase + bcdBase.sgOffset];
proj.interface _ bcdBase.definitions;
proj.symbolPages _ (IF sgb[mtb[firstMti].sseg].file = BcdDefs.FTNull
THEN [base~0, pages~0]
ELSE [base~sgb[mtb[firstMti].sseg].base-1, pages~sgb[mtb[firstMti].sseg].pages]);
proj.file _ file;
proj.state _ $opened};
VM.Free[interval]};
};


<<NB: a procedure identical to the following appears in SMFIImpl.  Refactor?>>

LoadUpBcd: PROC[file: FS.OpenFile]
RETURNS[pages: VM.Interval, base: BcdDefs.BcdBase] ~ TRUSTED {
nPages: INT _ MIN[10, file.GetInfo[].pages];
pages _ VM.nullInterval;  base _ NIL;
IF file = FS.nullOpenFile THEN ERROR;
DO
pages _ VM.Allocate[count~nPages];
base _ VM.AddressForPageNumber[pages.page];
FS.Read[file~file, from~0, nPages~nPages, to~base];
IF base.nPages <= nPages THEN EXIT;
nPages _ base.nPages;
VM.Free[pages];  pages _ VM.nullInterval
ENDLOOP;
RETURN};


<<for proj info provided by the compiler>>

Update: PUBLIC ENTRY PROC[
proj: Proj, parms: REF READONLY CompilerOps.Transaction] ~ {
ENABLE UNWIND => {NULL};
IF proj.stamp # parms.objectVersion THEN ERROR;
proj.interface _ parms.interface;
<<IF ~(proj.localName).Equal[parms.objectName, FALSE] THEN>>
<<proj.localName _ parms.objectName;>>
proj.file _ parms.objectFile;
proj.symbolPages _ [parms.symbolPages.base-1, parms.symbolPages.pages];
proj.cantOpen _ FALSE;  proj.state _ $opened};


<<start code>>

table _ HashTable.Create[ops~[GetKey, HashFromKey, CompareKeys]];

}.


