-- FileListsImpl.Mesa -- Last edited by Sandman on July 8, 1980 9:14 AM -- Last edited by Sweet on August 28, 1980 10:01 AM -- Last edited by Lewis on 14-Dec-80 19:27:35 -- Copyright Xerox Corporation 1979, 1980 DIRECTORY BcdDefs USING [VersionStamp], Environment USING [PageCount, wordsPerPage], Inline USING [BITAND, BITXOR], FileLists, Segments USING [PageCount], Space USING [ Create, Delete, GetHandle, Handle, LongPointer, Map, PageFromLongPointer, virtualMemory], LongString USING [AppendChar, AppendString, EquivalentString], IncludeCheckerTable USING [ Allocate, AddNotify, Base, Create, Destroy, DropNotify, Notifier, Region, Selector]; FileListsImpl: PROGRAM IMPORTS Inline, Space, LongString, IncludeCheckerTable EXPORTS FileLists = BEGIN OPEN FileLists; FilenameChars: CARDINAL = 39; -- DYNAMICALLY ALLOCATED TABLES userListType: IncludeCheckerTable.Selector = 0; fileEntryType: IncludeCheckerTable.Selector = 1; includeFileType: IncludeCheckerTable.Selector = 2; incFileDescType: IncludeCheckerTable.Selector = 3; ulb, feb, ifb, ifdb: PUBLIC IncludeCheckerTable.Base; UpdateTableBases: IncludeCheckerTable.Notifier = BEGIN ulb ← base[userListType]; feb ← base[fileEntryType]; ifb ← base[includeFileType]; ifdb ← base[incFileDescType]; END; -- HASHING AND STRING OPERATIONS HVSize: CARDINAL = 71; ulHashVec: ARRAY [0..HVSize) OF UserListPtr; feHashVec: ARRAY [0..HVSize) OF FE; ifdHashVec: ARRAY [0..HVSize) OF IncFileDesc; HashFn: PROC [s: LONG STRING] RETURNS [[0..HVSize)] = BEGIN CharBits: PROC [CHARACTER, WORD] RETURNS [WORD] = LOOPHOLE[Inline.BITAND]; Mask: WORD = 337B; n: CARDINAL = s.length; v: WORD; v ← CharBits[s[0], Mask]*177B + CharBits[s[n - 1], Mask]; RETURN[Inline.BITXOR[v, n*17B] MOD HVSize] END; CompareString: PROC [a, b: LONG STRING] RETURNS [{less, equal, greater}] = BEGIN l: CARDINAL = MIN[a.length, b.length]; i: CARDINAL; ca, cb: CHARACTER; CharAnd: PROC [CHARACTER, WORD] RETURNS [CHARACTER] = Inline.BITAND; FOR i IN [0..l) DO ca ← a[i]; cb ← b[i]; IF ca IN ['a..'z] THEN ca ← CharAnd[ca, 137B]; -- ignore case shifts IF cb IN ['a..'z] THEN cb ← CharAnd[cb, 137B]; IF ca < cb THEN RETURN[less]; IF ca > cb THEN RETURN[greater]; ENDLOOP; RETURN[ SELECT a.length FROM < b.length => less, = b.length => equal, ENDCASE => greater] END; -- INITIALIZATION AND FINALIZATION TablePages: Segments.PageCount = 150; tableSpace: LONG POINTER ← NIL; Initialize: PUBLIC PROC = BEGIN tableRegion: IncludeCheckerTable.Region; TableWeights: ARRAY [0..3] OF CARDINAL ← [2, 4, 11, 3]; -- empiracal tableSpace ← GetTableStorage[pages: TablePages]; tableRegion ← [ origin: LOOPHOLE[tableSpace, IncludeCheckerTable.Base], size: (TablePages * Environment.wordsPerPage)]; IncludeCheckerTable.Create[ region: tableRegion, weights: DESCRIPTOR[TableWeights]]; IncludeCheckerTable.AddNotify[UpdateTableBases]; FOR i: CARDINAL IN [0..HVSize) DO ulHashVec[i] ← ULnil; feHashVec[i] ← FEnil; ifdHashVec[i] ← IFDnil; ENDLOOP; userList ← userListEnd ← ULnil; fileList ← FEnil; END; Finalize: PUBLIC PROC = BEGIN IncludeCheckerTable.DropNotify[UpdateTableBases]; IncludeCheckerTable.Destroy[]; FreeTableStorage[tableSpace]; END; GetTableStorage: PROC [ pages: Environment.PageCount] RETURNS [base: LONG POINTER] = BEGIN new: Space.Handle ← Space.Create[ size: pages, parent: Space.virtualMemory]; Space.Map[new]; base ← Space.LongPointer[new]; END; FreeTableStorage: PROC [base: LONG POINTER] = {Space.Delete[Space.GetHandle[Space.PageFromLongPointer[base]]]}; -- USER-SPECIFIED FILE NAME LIST userList: UserListPtr ← ULnil; userListEnd: UserListPtr ← ULnil; mainPart: STRING ← [FilenameChars + 1]; InsertInUserList: PUBLIC PROC [fileName: LONG STRING] = BEGIN hash: [0..HVSize); newItem: UserListPtr; mainPart.length ← 0; FOR i: CARDINAL IN [0..fileName.length) DO IF fileName[i] = '. THEN EXIT; LongString.AppendChar[mainPart, fileName[i]]; ENDLOOP; hash ← HashFn[mainPart]; FOR p: UserListPtr ← ulHashVec[hash], ulb[p].next WHILE p # ULnil DO IF LongString.EquivalentString[mainPart, @ulb[p].name] THEN RETURN; ENDLOOP; newItem ← ulHashVec[hash] ← NewUserListItem[mainPart, ulHashVec[hash]]; IF userList = ULnil THEN userList ← userListEnd ← newItem ELSE BEGIN ulb[userListEnd].link ← newItem; userListEnd ← newItem; END; END; NewUserListItem: PROC [ name: LONG STRING, next: UserListPtr] RETURNS [p: UserListPtr] = BEGIN p ← IncludeCheckerTable.Allocate[userListType, (SIZE[UserListItem] + (name.length + 1)/2)]; ulb[p] ← UserListItem[ next: next, link: ULnil, name: [length: 0, maxlength: name.length, text: ]]; LongString.AppendString[@ulb[p].name, name]; RETURN[p]; END; IsInUserList: PUBLIC PROC [fileName: LONG STRING] RETURNS [BOOLEAN] = BEGIN IF userList = ULnil THEN RETURN[FALSE] ELSE RETURN[ScanUserList[fileName].found]; END; ScanUserList: PROC [ fileName: LONG STRING] RETURNS [found: BOOLEAN, userListName: LONG STRING] = BEGIN hash: [0..HVSize); IF userList # ULnil THEN BEGIN hash ← HashFn[fileName]; FOR p: UserListPtr ← ulHashVec[hash], ulb[p].next WHILE p # ULnil DO IF LongString.EquivalentString[fileName, @ulb[p].name] THEN RETURN[TRUE, @ulb[p].name]; ENDLOOP; END; RETURN[FALSE, NIL]; END; EnumerateUserList: PUBLIC PROC [ userProc: PROC[LONG STRING] RETURNS [BOOLEAN]] = BEGIN FOR p: UserListPtr ← userList, ulb[p].link WHILE p # ULnil DO IF userProc[@ulb[p].name] THEN RETURN; ENDLOOP; END; UserListLength: PUBLIC PROC RETURNS [count: CARDINAL] = BEGIN AddOne: PROC[LONG STRING] RETURNS [stop: BOOLEAN] = {count ← count+1; RETURN[FALSE]}; count ← 0; IF userList # ULnil THEN EnumerateUserList[AddOne]; RETURN[count]; END; -- FILE LIST fileList: PUBLIC FE ← FEnil; userNameCopy: STRING ← [FilenameChars + 1]; InsertInFileList: PUBLIC PROC [name: LONG STRING] RETURNS [fe: FE] = BEGIN found: BOOLEAN; userListName: LONG STRING; hash: [0..HVSize) ← HashFn[name]; userNameCopy.length ← 0; FOR fe ← feHashVec[hash], feb[fe].next WHILE fe # FEnil DO IF LongString.EquivalentString[name, @feb[fe].name] THEN RETURN[fe]; ENDLOOP; [found, userListName] ← ScanUserList[name]; -- use user's name if possible IF found THEN BEGIN LongString.AppendString[to: userNameCopy, from: userListName]; fe ← feHashVec[hash] ← NewFE[userNameCopy, feHashVec[hash]]; END ELSE fe ← feHashVec[hash] ← NewFE[name, feHashVec[hash]]; RETURN[fe]; END; NewFE: PROC [name: LONG STRING, next: FE] RETURNS [fe: FE] = BEGIN last: FE ← FEnil; -- follows fe FOR fe ← fileList, feb[fe].link UNTIL fe = FEnil DO SELECT CompareString[@feb[fe].name, name] FROM less => last ← fe; equal => ERROR; ENDCASE => EXIT; ENDLOOP; fe ← IncludeCheckerTable.Allocate[fileEntryType, (SIZE[FileEntry] + (name.length + 1)/2)]; IF last = FEnil THEN BEGIN feb[fe] ← FileEntry[ next: next, link: fileList, name: StringBody[length: 0, maxlength: name.length, text:]]; fileList ← fe; END ELSE BEGIN feb[fe] ← FileEntry[ next: next, link: feb[last].link, name: [length: 0, maxlength: name.length, text:]]; feb[last].link ← fe; END; LongString.AppendString[@feb[fe].name, name]; RETURN[fe]; END; -- INCLUDES/INCLUDED BY LISTS InsertIncludeFileItem: PUBLIC PROC [ incList: IncFile, fe: FE, feName: LONG STRING, stamp: BcdDefs.VersionStamp, fileOpenedByCompiler: BOOLEAN] RETURNS [IncFile] = BEGIN p: IncFile; incListLast: IncFile ← IFnil; incListFile: FE; FOR p ← incList, ifb[p].link UNTIL p = IFnil DO incListFile ← ifdb[ifb[p].includeFileDesc].file; SELECT CompareString[@feb[incListFile].name, feName] FROM less => incListLast ← p; equal => RETURN[incList]; ENDCASE => EXIT; ENDLOOP; p ← IncludeCheckerTable.Allocate[includeFileType, SIZE[IncludeFileItem]]; IF incListLast = IFnil THEN BEGIN ifb[p] ← IncludeFileItem[ link: incList, includeFileDesc: AddIFD[fe, feName, stamp], fileOpenedByCompiler: fileOpenedByCompiler]; incList ← p; END ELSE BEGIN ifb[p] ← IncludeFileItem[ link: ifb[incListLast].link, includeFileDesc: AddIFD[fe, feName, stamp], fileOpenedByCompiler: fileOpenedByCompiler]; ifb[incListLast].link ← p; END; RETURN[incList]; END; -- INCLUDED FILE DESCRIPTORS (avoids duplicating data in includes/included by lists) AddIFD: PROC [ fe: FE, feName: LONG STRING, stamp: BcdDefs.VersionStamp] RETURNS [d: IncFileDesc] = BEGIN i: [0..HVSize) ← HashFn[feName]; incFile: FE; FOR d ← ifdHashVec[i], ifdb[d].next UNTIL d = IFDnil DO incFile ← ifdb[d].file; IF LongString.EquivalentString[feName, @feb[incFile].name] AND stamp = ifdb[d].stamp THEN RETURN[d]; ENDLOOP; d ← ifdHashVec[i] ← NewIFD[fe, stamp, ifdHashVec[i]]; -- get new ifd RETURN[d]; END; NewIFD: PROC [ fe: FE, stamp: BcdDefs.VersionStamp, next: IncFileDesc] RETURNS [d: IncFileDesc] = BEGIN d ← IncludeCheckerTable.Allocate[incFileDescType, SIZE[IncFileDescItem]]; ifdb[d] ← IncFileDescItem[next: next, file: fe, stamp: stamp]; RETURN[d]; END; END.