-- FileCache.Mesa -- Edited by: -- Sandman on January 17, 1979 9:52 AM -- Barbara on November 2, 1978 9:24 AM -- Bruce on July 7, 1980 11:56 AM -- Evans on Jun 26, 1980 1:54 PM DIRECTORY AltoFileDefs USING [eofDA, FP, NullFP], DebugOps USING [], DirectoryDefs USING [DirectoryLookup, EnumerateDirectory], Inline USING [BITAND, BITOR, COPY], SegmentDefs USING [AccessOptions, Append, DefaultAccess, DefaultVersion, FileAccessError, FileHandle, FileNameError, FileObject, FindFile, InsertFile, InvalidFP, NewFileOnly, Object, OldFileOnly, OpenFile, Read, ReleaseFile, SetFileAccess, VersionOptions], Storage USING [CopyString, FreeString, Node], String USING [AppendString, EquivalentSubStrings, SubStringDescriptor], SwapperOps USING [AllocateObject]; FileCache: PROGRAM IMPORTS DirectoryDefs, Inline, SegmentDefs, Storage, String, SwapperOps EXPORTS DebugOps SHARES SegmentDefs = BEGIN NullFP: AltoFileDefs.FP = AltoFileDefs.NullFP; FileHandle: TYPE = SegmentDefs.FileHandle; NullFileObject: SegmentDefs.FileObject = [ busy: FALSE, body: file[open: FALSE, length: FALSE, lengthvalid: FALSE, read: FALSE, write: FALSE, append: FALSE, lengthchanged: FALSE, lock: 0, swapcount: 0, segcount: 0, fp: NullFP]]; FCRecord: TYPE = RECORD [ name: STRING, fp: AltoFileDefs.FP]; FCSize: CARDINAL = 20; FCLimit: CARDINAL = FCSize-1; FCArray: DESCRIPTOR FOR ARRAY [0..FCSize) OF FCRecord; InitFileCache: PROCEDURE = BEGIN i: CARDINAL; FCArray ← DESCRIPTOR[Storage.Node[FCSize*SIZE[FCRecord]],FCSize]; FOR i IN [0..FCSize) DO FCArray[i] ← FCRecord[NIL,NullFP]; ENDLOOP; cacheInvalid ← FALSE; RETURN END; PromoteFCRecord: PROCEDURE [i: CARDINAL] = BEGIN ithFCR: FCRecord; IF i = FCLimit THEN RETURN; ithFCR ← FCArray[i]; Inline.COPY[to: @FCArray[i], from: @FCArray[i+1], nwords: SIZE[FCRecord]*(FCLimit-i)]; FCArray[FCLimit] ← ithFCR; RETURN END; CopyFileName: PROCEDURE [name: STRING] RETURNS [copy: STRING] = BEGIN IF name = NIL THEN RETURN[NIL]; copy ← Storage.CopyString[name]; RETURN END; AddFCRecord: PROCEDURE [name: STRING, fh: FileHandle] = BEGIN IF FCArray[0].name # NIL THEN Storage.FreeString[FCArray[0].name]; Inline.COPY[to: @FCArray[0], from: @FCArray[1], nwords: SIZE[FCRecord]*(FCSize-1)]; FCArray[FCLimit].name ← CopyFileName[name]; IF fh # NIL THEN FCArray[FCLimit].fp ← fh.fp ELSE FCArray[FCLimit].fp ← NullFP; cacheInvalid ← TRUE; RETURN END; CacheNewFile: PUBLIC PROCEDURE [ name: STRING, access: SegmentDefs.AccessOptions, version: SegmentDefs.VersionOptions ← SegmentDefs.DefaultVersion] RETURNS [file: FileHandle] = BEGIN OPEN AltoFileDefs; i: CARDINAL; create: BOOLEAN; [access,version] ← ValidateOptions[access,version]; create ← Inline.BITAND[version,SegmentDefs.OldFileOnly]=0; file ← NIL; FOR i DECREASING IN [0..FCSize) DO IF FCArray[i].name = NIL THEN BEGIN AddFCRecord[name,NIL]; EXIT END; IF EquivalentFileNames[name, FCArray[i].name] THEN BEGIN PromoteFCRecord[i]; EXIT END; REPEAT FINISHED => AddFCRecord[name,NIL]; ENDLOOP; IF FCArray[FCLimit].fp.leaderDA # eofDA AND ~cacheInvalid THEN BEGIN OPEN SegmentDefs; file ← InsertFile[@FCArray[FCLimit].fp, access]; OpenFile[file ! InvalidFP => GOTO BadCache]; RETURN; EXITS BadCache => BEGIN IF file.segcount = 0 THEN ReleaseFile[file]; cacheInvalid ← TRUE; END; END; ValidateCache[]; SELECT TRUE FROM FCArray[FCLimit].fp.leaderDA # eofDA => file ← SegmentDefs.InsertFile[@FCArray[FCLimit].fp, access]; create => file ← RealNewFile[name,access]; ENDCASE => ERROR SegmentDefs.FileNameError[name]; RETURN END; FileName: PUBLIC PROCEDURE [name: STRING, file: FileHandle] = BEGIN localname: STRING ← [40]; i: CARDINAL; BEGIN IF cacheInvalid THEN GO TO notincache ELSE FOR i DECREASING IN [0..FCSize) DO IF FCArray[i].name = NIL THEN GO TO notincache; IF FCArray[i].fp = file.fp THEN BEGIN String.AppendString[name,FCArray[i].name]; PromoteFCRecord[i]; RETURN END; REPEAT FINISHED => GO TO notincache; ENDLOOP; EXITS notincache => AddFCRecord[NIL,file]; END; ValidateCache[]; IF FCArray[FCLimit].name = NIL THEN BEGIN FOR i DECREASING IN [1..FCSize) DO FCArray[i] ← FCArray[i-1]; ENDLOOP; FCArray[0] ← [NIL,NullFP]; SIGNAL SegmentDefs.InvalidFP[@file.fp] END ELSE String.AppendString[name,FCArray[FCLimit].name]; RETURN END; EquivalentFileNames: PROCEDURE [n1, n2: STRING] RETURNS [BOOLEAN] = BEGIN s1,s2: String.SubStringDescriptor; s1 ← [base: n1, offset: 0, length: n1.length - (IF n1[n1.length-1] = '. THEN 1 ELSE 0)]; s2 ← [base: n2, offset: 0, length: n2.length - (IF n2[n2.length-1] = '. THEN 1 ELSE 0)]; RETURN[String.EquivalentSubStrings[@s1,@s2]] END; cacheInvalid: BOOLEAN; InvalidateFileCache: PUBLIC PROCEDURE = BEGIN cacheInvalid ← TRUE END; ValidateCache: PROCEDURE = BEGIN i: CARDINAL; num, found: CARDINAL ← 0; caseBit: WORD = 40B; CheckEntry: PROCEDURE [fp: POINTER TO AltoFileDefs.FP, dirname: STRING] RETURNS[BOOLEAN] = BEGIN fcr: POINTER TO FCRecord ← @FCArray[FCLimit]; THROUGH [0..FCSize) DO IF fcr.name = NIL THEN BEGIN IF fcr.fp.leaderDA = AltoFileDefs.eofDA THEN EXIT; IF fcr.fp = fp↑ THEN {fcr.name ← CopyFileName[dirname]; found ← found+1}; END ELSE IF Inline.BITOR[fcr.name[0],caseBit] = Inline.BITOR[dirname[0],caseBit] AND EquivalentFileNames[fcr.name, dirname] THEN {fcr.fp ← fp↑; found ← found+1}; fcr ← LOOPHOLE[fcr - SIZE[FCRecord]]; ENDLOOP; RETURN[found = num]; END; IF ~cacheInvalid THEN RETURN; FOR i IN [0..FCSize) DO IF FCArray[i].name # NIL THEN {num ← num+1; FCArray[i].fp ← NullFP; LOOP}; IF FCArray[i].fp.leaderDA # AltoFileDefs.eofDA THEN num ← num+1; ENDLOOP; DirectoryDefs.EnumerateDirectory[CheckEntry]; cacheInvalid ← FALSE; END; RealNewFile: PROCEDURE [name: STRING, access: SegmentDefs.AccessOptions] RETURNS [file: FileHandle] = BEGIN fp: AltoFileDefs.FP; [] ← DirectoryDefs.DirectoryLookup[@fp,name,TRUE]; IF (file ← SegmentDefs.FindFile[@fp]) = NIL THEN BEGIN file ← LOOPHOLE[ SwapperOps.AllocateObject[SIZE[file SegmentDefs.Object]]]; file↑ ← NullFileObject; file.fp ← fp; END; SegmentDefs.SetFileAccess[file,access]; RETURN END; ValidateOptions: PROCEDURE [ access: SegmentDefs.AccessOptions, version: SegmentDefs.VersionOptions] RETURNS [SegmentDefs.AccessOptions, SegmentDefs.VersionOptions] = BEGIN OPEN SegmentDefs, Inline; IF access = SegmentDefs.DefaultAccess THEN access ← SegmentDefs.Read; -- IF version = DefaultVersion THEN version ← 0; IF BITAND[version,NewFileOnly+OldFileOnly] = NewFileOnly+OldFileOnly OR (BITAND[version,NewFileOnly]#0 AND BITAND[access,Append]=0) THEN ERROR FileAccessError[NIL]; IF BITAND[access,Append]=0 THEN version ← BITOR[version,OldFileOnly]; RETURN[access,version] END; InitFileCache[]; END...