WPBTreeImpl.mesa
Last Edited by: Swinehart, January 13, 1984 4:28 pm
Reads and write white pages data base records.
DIRECTORY
Atom,
Basics,
BTree,
BTreeVM,
Commander,
CommandTool,
FS,
FSBackdoor,
IO,
Log,
LupineRuntime,
PrincOps,
PrincOpsUtils,
ProcessProps,
RefTab,
RefText,
Rope,
ThNet,
UserProfile;
WPBTreeImpl: PROGRAM
IMPORTS Atom, Basics, BTree, BTreeVM, Commander, CommandTool, FS, FSBackdoor, IO, Log, LupineRuntime, RefTab, PrincOpsUtils, ProcessProps, RefText, Rope, ThNet, UserProfile
EXPORTS ThNet SHARES Rope
= BEGIN
OPEN IO;
ROPE: TYPE = Rope.ROPE;
Attrs: TYPE = ThNet.Attrs;
WPListing: TYPE = ThNet.WPListing;
Representation primitives
wpRepPrim: BTree.RepresentationPrimitives = [
compare: Compare,
compareEntries: CompareEntries,
entrySize: EntrySize,
entryFromRecord: EntryFromRecord,
newRecordFromEntry: NewRecordFromEntry];
Key: TYPE = ROPE;
Record: TYPE = REF WPEntryRecord;
Entry: TYPE = LONG POINTER TO WPEntryRecord;
WPEntryRecord: TYPE = RECORD
[
recordSize: EntSize,
linkIndex: INT←-1, -- If non-negative, word index of an additional record in another file.
recordData: SEQUENCE COMPUTED EntSize OF INTEGER
];
WPState: TYPE = ThNet.WPState;
WPStateRecord: TYPE = ThNet.WPStateRecord;
EntSize: TYPE = [0..LAST[BTree.PageSize]-BTree.reservedWordsPerPage+1];
lastEnt: EntSize = LAST[EntSize]-1;
Compare: PROCEDURE [key: BTree.Key, entry: Entry] RETURNS [BTree.Comparison] = {
recordKey: Key=RopeFromWPEntryRecord[entry,0].rope;
RETURN [CompareRopes[NARROW[key, Key], recordKey, FALSE]];
};
CompareEntries: PROCEDURE [entry1, entry2: Entry] RETURNS [BTree.Comparison] = {
e1: Key=RopeFromWPEntryRecord[entry1,0].rope;
e2: Key=RopeFromWPEntryRecord[entry2,0].rope;
RETURN [CompareRopes[e1, e2, FALSE]];
};
EntrySize: PROCEDURE [entry: Entry] RETURNS [words: BTree.EntSize] =
{ RETURN [SIZE[WPEntryRecord[entry.recordSize]]] };
EntryFromRecord: PROCEDURE [record: BTree.Record] RETURNS [entry: Entry] =
{ RETURN [LOOPHOLE[NARROW[record, Record]]] };
NewRecordFromEntry: PROCEDURE [entry: Entry] RETURNS [record: Record] =
BEGIN
record ← NEW[WPEntryRecord[entry.recordSize]];
PrincOpsUtils.LongCopy[to: LOOPHOLE[record], from: entry, nwords: SIZE[WPEntryRecord[entry.recordSize]]];
END;
CompareRopes: PROC[s1: ROPE, s2: ROPE, substringEqual: BOOL]
RETURNS[res: BTree.Comparison] = {
ss1: INT=Rope.Size[s1];
r2: ROPEIF substringEqual THEN Rope.Substr[s2, 0, ss1] ELSE s2;
RETURN [Rope.Compare[s1, r2, FALSE]];
};
wpAttrs: RefTab.Ref;
wpExtInt: RefTab.Ref;
ThNet Procedures
InitWhitePagesDatabase: PUBLIC SAFE PROC[
fileName: ROPE, accessOptions: FS.AccessOptions ← $read,
howToComplain: Log.WhereToReport←$System]
RETURNS[wpState: WPState←NIL] = TRUSTED {
buffer: REF TEXT = RefText.ObtainScratch[100];
treeName: ROPE=fileName.Concat[".Tree"];
dataName: ROPE=fileName.Concat[".TreeData"];
feepTreeName: ROPE=fileName.Concat[".FTree"];
treeFile, dataFile, feepFile: FS.OpenFile;
keyRecord, feepKeyRecord: Record;
IF wpAttrs=NIL THEN {
wpAttrs ← RefTab.Create[];
FOR attr: Attrs IN Attrs DO
IF keysForAttributes[attr] # $unassigned THEN
[]←RefTab.Store[wpAttrs, keysForAttributes[attr], NEW[Attrs𡤊ttr]];
ENDLOOP;
};
IF wpExtInt=NIL THEN {
s: IO.STREAM = FS.StreamOpen[UserProfile.Token[
key: "ThrushWPMappings",
default: "/Indigo/Voice/BTree/IntExt.map"]];
wpExtInt ← RefTab.Create[];
WHILE ~s.EndOf[] DO
ENABLE IO.EndOfStream=>EXIT;
int: ATOM ← Atom.MakeAtomFromRefText[s.GetCedarToken[buffer: buffer].token];
ext: ATOM;
[]←s.GetChar[];
ext ← Atom.MakeAtomFromRefText[s.GetLine[buffer]];
[]←wpExtInt.Store[ext, int]; -- external version is the key.
ENDLOOP;
s.Close[];
};
RefText.ReleaseScratch[buffer];
wpState ← NEW[WPStateRecord];
keyRecord ← NEW[WPEntryRecord[LAST[EntSize]]];
wpState.keyRecord ← keyRecord;
feepKeyRecord ← NEW[WPEntryRecord[LAST[EntSize]]];
wpState.feepKeyRecord ← feepKeyRecord;
wpState.wpTree ← BTree.New[repPrim: wpRepPrim, storPrim: [BTreeVM.ReferencePage, BTreeVM.ReleasePage], minEntrySize: 4];
wpState.wpFeepTree ← BTree.New[repPrim: wpRepPrim, storPrim: [BTreeVM.ReferencePage, BTreeVM.ReleasePage], minEntrySize: 4];
{ ENABLE FS.Error => IF error.group = user THEN {
Log.Problem[error.explanation, howToComplain]; treeFile←FS.nullOpenFile; CONTINUE; };
SELECT accessOptions FROM
$read => {
treeFile←FS.Open[name: treeName, remoteCheck: FALSE];
feepFile←FS.Open[name: feepTreeName, remoteCheck: FALSE];
dataFile ← FS.Open[name: dataName, remoteCheck: FALSE];
};
$create => {
treeFile←FS.Create[name: treeName, keep: 2, setKeep: TRUE, pages: 100];
feepFile←FS.Create[name: feepTreeName, keep: 2, setKeep: TRUE, pages: 100];
dataFile ← FS.Create[name: dataName, keep: 2, setKeep: TRUE, pages: 300];
};
$write => {
treeFile←FS.OpenOrCreate[name: treeName, keep: 2, pages: 100];
feepFile←FS.OpenOrCreate[name: feepTreeName, keep: 2, pages: 100];
dataFile ← FS.OpenOrCreate[name: dataName, keep: 2, pages: 300];
};
ENDCASE => Log.Problem["Don't request $append option for BTree", howToComplain];
};
IF treeFile=NIL OR dataFile = NIL OR feepFile=NIL THEN RETURN[NIL];
wpState.wpDataVMFile ← dataFile;
wpState.wpDataVMLen ← FS.GetInfo[dataFile].bytes/2;
wpState.wpVM ← BTreeVM.Open[file: FSBackdoor.GetFileHandle[treeFile], filePagesPerPage: 1, cacheSize: 20];
wpState.wpFeepVM ← BTreeVM.Open[file: FSBackdoor.GetFileHandle[feepFile], filePagesPerPage: 1, cacheSize: 20];
wpState.wpDataVM ← BTreeVM.Open[file: FSBackdoor.GetFileHandle[dataFile], filePagesPerPage: 1, cacheSize: 10];
[]←OpenWhitePagesDatabase[wpState, howToComplain, FS.GetInfo[dataFile].bytes=0];
};
OpenWhitePagesDatabase: SAFE PROC[
wpState: WPState, howToComplain: Log.WhereToReport←$System, init: BOOL�LSE]
RETURNS [ok: BOOLFALSE] = TRUSTED {
ENABLE BTree.Error => {
Log.Problem["Trouble opening BTree", howToComplain]; CONTINUE };
IF wpState=NIL THEN RETURN[FALSE];
IF wpState.wpOpen THEN RETURN;
wpState.wpTree.Open[storage: wpState.wpVM, pageSize: FS.WordsForPages[1],
initialize: init];
wpState.wpFeepTree.Open[storage: wpState.wpFeepVM, pageSize: FS.WordsForPages[1],
initialize: init];
ok ← wpState.wpOpen ← TRUE;
};
CloseWhitePagesDatabase: PUBLIC SAFE PROC[wpState: WPState, howToComplain: Log.WhereToReport←$System] RETURNS [ok: BOOLTRUE] = TRUSTED {
IF wpState=NIL OR ~wpState.wpOpen THEN RETURN;
wpState.wpTree.SetState[closed!BTree.Error => { ok←FALSE; CONTINUE}];
wpState.wpFeepTree.SetState[closed!BTree.Error => { ok←FALSE; CONTINUE}];
wpState.wpDataVM.FlushCache[];
wpState.wpDataVM.FreeBuffers[];
FS.SetByteCountAndCreatedTime[wpState.wpDataVMFile, wpState.wpDataVMLen*2!FS.Error => IF error.group=user THEN { Log.Problem[error.explanation, howToComplain]; CONTINUE; }];
wpState.wpDataVMValid ← TRUE;
wpState.wpOpen ← FALSE;
};
SetProperLength: SAFE PROC[name: ROPE] = CHECKED {
file: FS.OpenFile ← FS.Open[name, $write];
pages: INTFS.GetInfo[file].pages;
FS.SetByteCountAndCreatedTime[file, FS.BytesForPages[pages]];
FS.Close[file];
};
WhitePagesLookup: PUBLIC SAFE PROC[
wpState: WPState, name: ROPE, feep: BOOLFALSE ]
RETURNS [ listing: WPListing←NIL ] = TRUSTED {
tree: BTree.Tree ← IF feep THEN wpState.wpFeepTree ELSE wpState.wpTree;
name must be in form rr.xxxxxxx
Returns first listing for which name is a substring, case unimportant, of the listing
FindOne: SAFE PROC[record: BTree.Record] RETURNS [continue: BOOLEANFALSE] = TRUSTED {
index: EntSize;
key: ATOM;
value: ROPE;
entry: Entry ← EntryFromRecord[record];
[index, key, value] ← RopeFromWPEntryRecord[entry, 0];
SELECT CompareRopes[name,
RopeFromWPEntryRecord[entry,0].rope, TRUE] FROM
less => RETURN[TRUE];
equal => NULL;
ENDCASE => RETURN[FALSE];
IF feep AND listing#NIL THEN { listing ← NIL; RETURN; };
IF index=lastEnt OR
(record ← GetLinkedRecord[wpState, NARROW[record], $read]) = NIL THEN
RETURN[feep];
entry ← EntryFromRecord[record];
listing ← RefTab.Create[];
index ← 0;
WHILE index#lastEnt DO
[]←listing.Store[key, value];
[index, key, value] ← RopeFromWPEntryRecord[entry, index];
ENDLOOP;
RETURN[feep];
};
IF ~wpState.wpOpen AND ~OpenWhitePagesDatabase[wpState] THEN RETURN;
[]𡤋Tree.EnumerateRecords[tree: tree, key: name, relation: less, Proc: FindOne];
};
WhitePagesEntry: PUBLIC SAFE PROC[
wpState: WPState, name: ROPENIL, key: ATOM←$officeNumber, feep: BOOLFALSE,
listing: WPListing ← NIL]
RETURNS [fullRName: ROPENIL, entry: ROPENIL, newListing: WPListing←NIL] = TRUSTED {
IF listing = NIL THEN listing ← WhitePagesLookup[wpState, name, feep];
IF listing = NIL THEN RETURN;
newListing ← listing;
fullRName ← NARROW[listing.Fetch[$rName].val];
IF key#NIL THEN entry ← NARROW[listing.Fetch[key].val];
};
WhitePagesEnter: PUBLIC SAFE PROC[wpState: WPState, listing: WPListing] = TRUSTED {
Put in something new, or replace something old.
IF ~wpState.wpOpen AND ~OpenWhitePagesDatabase[wpState] THEN RETURN;
WhitePagesDo[wpState, listing, enter];
};
WhitePagesPrint: PUBLIC SAFE PROC[wpState: WPState, listing: WPListing] = TRUSTED {
Put in something new, or replace something old.
WhitePagesDo[wpState, listing, print];
};
WPFn: TYPE = { print, enter, note };
WhitePagesDo: SAFE PROC[wpState: WPState, listing: WPListing, fn: WPFn] = TRUSTED {
Put in something new, or replace something old.
dataPage: BTree.PagePtr;
dummyData: ARRAY [0..4) OF CARDINAL;
dataEntry: Entry ← LOOPHOLE[LONG[@dummyData[0]]];
keyEntry: Entry ← EntryFromRecord[wpState.keyRecord];
feepKeyEntry: Entry ← EntryFromRecord[wpState.feepKeyRecord];
entry: Entry ← keyEntry;
name, feepName: ROPE;
linkIndex: INT ← keyEntry.linkIndex;
linkPage: INT;
inPageIndex: INT;
refType: BTree.ReferenceType ← $write;
doIt: BOOLFALSE;
EPA: RefTab.EachPairAction = TRUSTED {
realKey: ATOMNARROW[key,ATOM];
SELECT fn FROM
enter => RopeToWPEntryRecord[entry, realKey, NARROW[val], doIt];
print => PrintWPEntry[realKey, NARROW[val]];
ENDCASE;
RETURN[FALSE];
};
Start Here
name←NARROW[listing.Fetch[$rName].val];
feepName ← ThNet.FeepName[name];
IF name=NIL THEN ERROR;
keyEntry.recordSize𡤀
feepKeyEntry.recordSize𡤀
SELECT fn FROM
enter => {
RopeToWPEntryRecord[keyEntry, $rName, name, TRUE];
RopeToWPEntryRecord[feepKeyEntry, $rName, feepName, TRUE];
};
note => { (GetCommanderHandle[]).out.Put[rope[name], rope["\n"]]; RETURN; };
ENDCASE;
keyEntry[keyEntry.recordSize] ← 0;
keyEntry.recordSize ← keyEntry.recordSize + (1+3); -- 1 for the ending 0, 2 for the header
feepKeyEntry[feepKeyEntry.recordSize] ← 0;
feepKeyEntry.recordSize ← feepKeyEntry.recordSize + (1+3);
dataEntry.recordSize𡤀
entry ← dataEntry;
[]←RefTab.Pairs[listing, EPA];
dataEntry.recordSize�taEntry.recordSize + (1+3);
IF fn=enter THEN {
linkIndex ← wpState.wpDataVMLen;
IF ~([linkPage, inPageIndex, ]←GetLinkValues[wpState, linkIndex]).ok THEN RETURN;
IF inPageIndex=0 OR inPageIndex+dataEntry.recordSize>FS.WordsForPages[1] THEN {
New Page needed.
refType ← $new;
IF inPageIndex#0 THEN {
linkPage←SUCC[linkPage];
inPageIndex ← 0;
linkIndex ← FS.WordsForPages[linkPage];
};
};
dataPage ← BTreeVM.ReferencePage[wpState.wpDataVM, linkPage, refType];
entry ← dataEntry ← dataPage+inPageIndex;
dataEntry.recordSize ← 0;
doIt ← TRUE;
[]←RefTab.Pairs[listing, EPA];
dataEntry[dataEntry.recordSize] ← 0;
dataEntry.recordSize ← dataEntry.recordSize + (1+3);
dataEntry.linkIndex ← -1;
wpState.wpDataVMLen ← linkIndex+dataEntry.recordSize;
BTreeVM.ReleasePage[wpState.wpDataVM, linkPage, endOfUpdate];
keyEntry.linkIndex ← linkIndex;
feepKeyEntry.linkIndex ← linkIndex;
BTree.UpdateRecord[tree: wpState.wpTree, key: name, record: NARROW[wpState.keyRecord]];
BTree.UpdateRecord[
tree: wpState.wpFeepTree, key: feepName, record: NARROW[wpState.feepKeyRecord]];
};
};
PrintWPEntry: PROC[key: ATOM, val: ROPE] = {
h: Commander.Handle = GetCommanderHandle[];
h.out.PutF["%g: %g\n", atom[key], rope[val]];
IF key=$rName AND h.out#h.err THEN h.err.Put[rope[val], rope["\n"]];
};
GetCommanderHandle: PROC RETURNS[handle: Commander.Handle] = {
p: Atom.PropList = ProcessProps.GetPropList[];
IF p=NIL THEN RETURN[NIL];
RETURN[NARROW[Commander.GetProperty[$CommanderHandle, p]]];
};
GetLinkedRecord: SAFE PROC[wpState: WPState, keyRecord: Record, access: FS.AccessOptions]
RETURNS[record: Record←NIL] = TRUSTED {
linkIndex: INT ← keyRecord.linkIndex;
linkPage: INT;
inPageIndex: INT;
entry: Entry;
pagePtr: BTree.PagePtr;
IF ([linkPage, inPageIndex,]←GetLinkValues[wpState, linkIndex]).ok=FALSE THEN RETURN;
pagePtr ← BTreeVM.ReferencePage[wpState.wpDataVM, linkPage,
SELECT access FROM $read => $read, $create => $new, ENDCASE => $write];
entry ← pagePtr+inPageIndex;
record ← NewRecordFromEntry[entry];
BTreeVM.ReleasePage[wpState.wpDataVM, linkPage, endOfUpdate];
};
GetLinkValues: SAFE PROC[wpState: WPState, linkIndex: INT]
RETURNS[linkPage, inPageIndex: INT, ok: BOOLFALSE] = TRUSTED {
IF linkIndex<0 OR linkIndex> wpState.wpDataVMLen THEN {
Log.Problem["BTree structure error"]; RETURN;
};
ok←TRUE;
linkPage ← FS.PagesForWords[linkIndex+1]-1;
inPageIndex ← linkIndex - FS.WordsForPages[linkPage];
};
RopeFromWPEntryRecord: PROC[entry: Entry, index: EntSize]
RETURNS[newIndex: EntSize←lastEnt, key: ATOM, rope: ROPE] = {
code: INTEGER;
keyRope: ROPE;
IF index=lastEnt THEN RETURN;
code ← entry[index];
SELECT code FROM
0 => RETURN;
<0 => {
key ← keysForAttributes[LOOPHOLE[-code]];
index←index+1;
};
ENDCASE => {
[index, keyRope] ← RFromWPER[entry, index];
key ← Atom.MakeAtom[keyRope];
};
[newIndex, rope] ← RFromWPER[entry, index];
};
RFromWPER: PROC[entry: Entry, index: EntSize]
RETURNS[newIndex: EntSize←lastEnt, rope: ROPE] = {
length: INTEGER;
wfc: LONG CARDINAL;
textRope: Rope.Text;
IF index=lastEnt THEN RETURN;
length ← entry[index];
rope ← textRope ← Rope.NewText[size: length];
wfc ← LupineRuntime.WordsForChars[length];
PrincOpsUtils.LongCOPY[
from: @entry[index+1],
to: BASE[DESCRIPTOR[textRope.text]],
nwords: wfc
];
newIndex ← index+wfc+1;
};
RopeToWPEntryRecord: PROC[entry: Entry, key: ATOM, rope: ROPE, doIt: BOOL] = {
index: EntSize ← entry.recordSize;
attr: REF Attrs ← NARROW[wpAttrs.Fetch[key].val];
IF attr#NIL THEN {
IF doIt THEN entry[index]←-LOOPHOLE[attr^, INTEGER];
entry.recordSize ← index+1;
}
ELSE RToWPER[entry, Atom.GetPName[key], doIt];
RToWPER[entry, rope, doIt];
};
RToWPER: PROC[entry: Entry, rope: ROPE, doIt: BOOL] = {
index: EntSize ← entry.recordSize;
wfc: LONG CARDINAL;
len: INTEGER ← rope.Length[];
IF doIt THEN entry[index] ← len;
wfc ← LupineRuntime.WordsForChars[len];
IF doIt THEN PrincOpsUtils.LongCOPY[
to: @entry[index+1],
from: BASE[DESCRIPTOR[Rope.Flatten[rope].text]],
nwords: wfc
];
entry.recordSize ← index+wfc+1;
};
Type "conversion" operations, callable from debugger
ConcreteKey: PROCEDURE [key: BTree.Key] RETURNS [Key] =
{ RETURN [NARROW[key]] };
ConcreteEntry: PROCEDURE [entry: BTree.Entry] RETURNS [Entry] =
{ RETURN [entry] };
Subsidiary procedures
file: FS.OpenFile ← FS.nullOpenFile;
WhitePagesFill: SAFE PROC[cmd: Commander.Handle, accessOptions: FS.AccessOptions←write] = TRUSTED {
s: IO.STREAM;
lineBuffer: REF TEXT = RefText.ObtainScratch[100];
tokenBuffer: REF TEXT = RefText.ObtainScratch[100];
listing: WPListing←NIL;
wpState: WPState;
numEntries: CARDINAL ← 0;
argsL: LIST OF ROPE;
IF ([argsL,] ← CommandTool.ParseToList[cmd, IO.SP]).length<2 THEN {
Log.Problem["Not enough file names specified", $System]; argsL ← NIL; };
IF argsL=NIL THEN RETURN;
s ← FS.StreamOpen[argsL.first];
wpState ← InitWhitePagesDatabase[argsL.rest.first, accessOptions];
IF wpState#NIL THEN WHILE ~s.EndOf[] DO
ENABLE IO.EndOfStream=>EXIT;
line: REF TEXT ← s.GetLine[buffer: lineBuffer];
lineS: IO.STREAM;
extKey, key: ATOM←NIL;
value: ROPE;
keyVal: REF TEXT;
KeyProc: IO.BreakProc = TRUSTED { RETURN[IF char=': THEN sepr ELSE other]; };
IF line.length=0 THEN {
IF listing#NIL THEN {
IF ~wpState.wpOpen AND ~OpenWhitePagesDatabase[wpState] THEN EXIT;
WhitePagesDo[wpState, listing, note];
WhitePagesEnter[wpState, listing];
key ← extKey ← NIL;
numEntries ← SUCC[numEntries];
IF Basics.BITSHIFT[numEntries, 12]=0 THEN []𡤌loseWhitePagesDatabase[wpState];
};
listing ← NIL;
LOOP;
};
IF listing = NIL THEN listing ← NewListing[];
lineS ← IO.TIS[line];
keyVal ← NIL;
keyVal ← lineS.GetToken[KeyProc, tokenBuffer!IO.EndOfStream => CONTINUE].token;
IF lineS.EndOf[] THEN { -- no key, extension of previous value.
IF key#NIL THEN value ← value.Cat["\n", Rope.FromRefText[keyVal]];
}
ELSE {
extKey ← Atom.MakeAtomFromRefText[keyVal];
key ← NARROW[wpExtInt.Fetch[extKey].val];
IF key=NIL THEN key𡤎xtKey;
[]←lineS.GetChar[!IO.EndOfStream => CONTINUE];
[]←lineS.SkipWhitespace[!IO.EndOfStream => CONTINUE];
value ← lineS.GetLineRope[!IO.EndOfStream => CONTINUE];
};
IF key#NIL THEN List[listing, key, value];
ENDLOOP;
(GetCommanderHandle[]).err.PutRope["-- Updates done.\n"];
s.Close[];
RefText.ReleaseScratch[lineBuffer];
RefText.ReleaseScratch[tokenBuffer];
[]𡤌loseWhitePagesDatabase[wpState];
};
NewListing: SAFE PROC RETURNS [WPListing] = TRUSTED { RETURN[RefTab.Create[]]; };
List: SAFE PROC[listing: WPListing, key: ATOM, value: ROPE] = TRUSTED {
[]←RefTab.Store[listing, key, value];
};
FillCmdInit: Commander.CommandProc = TRUSTED {
WhitePagesFill[cmd, $create];
};
FillCmd: Commander.CommandProc = TRUSTED {
WhitePagesFill[cmd, $write];
};
keysForAttributes: ARRAY ThNet.Attrs OF ATOM = [
$unassigned, $name, $rName, $officeNumber, $officeDDDNumber, $outsideNumber, $officeAddress, $officeLocation, $organization, $homeAddress, $frequency, $mailSystem, $primaryKey, $unassigned, $unassigned, $unassigned
];
Commander.Register["WPFill", FillCmdInit];
Commander.Register["WPRefill", FillCmd];
END.