DIRECTORY Ascii, Basics USING [charsPerWord, Comparison, UnsafeBlock], BTree, BTreeVM, Convert, IO, Process, RefTab, Rope USING [Equal, Flatten, FromRefText, Index, InlineFetch, Length, ROPE, Substr, Text, UnsafeMoveChars], PBasics USING [ByteBlt, LowHalf], YggDID USING [DID, EqualDIDs, StabilizeDID, VolatilizeDID], YggDIDMap USING [AddComponentFile, GetComponentFiles, OpenDocumentFromDID, RemoveComponentFile], YggDIDPrivate USING [DIDRep], YggEnvironment USING [nullDID, nullTransID, Outcome, TransID], YggFile USING [BytesForPages, Create, Delete, FileFromComponentFiles, FileHandle, PageCount, PagesForBytes, SetByteSize], YggFileStream USING [StreamFromComponentFilesAndTid], YggFixedNames USING [], YggIndex USING [], YggInline USING [BytesForWords, WordsForBytes], YggInternal USING [Document, FileHandle], YggLock USING [LockID, MakeLockID, Release, Set], YggNaming USING [EnumProc, ErrorDesc, Version], YggRep USING [DocType], YggTransaction USING [CreateTrans, EqualTrans, Finish, IsNullTrans]; YggNamingImpl: CEDAR MONITOR IMPORTS Ascii, BTree, BTreeVM, Convert, IO, PBasics, Process, RefTab, Rope, YggDID, YggDIDMap, YggFile, YggFileStream, YggInline, YggLock, YggTransaction EXPORTS YggDID, YggNaming ~ BEGIN ROPE: TYPE = Rope.ROPE; Error: PUBLIC ERROR [error: YggNaming.ErrorDesc] = CODE; DID: PUBLIC TYPE ~ REF DIDRep; DIDRep: PUBLIC TYPE ~ YggDIDPrivate.DIDRep; EntryPtr: TYPE = LONG BASE POINTER TO Entry; nameOffset: INT = 2+ UNITS[DIDRep]; Entry: TYPE = MACHINE DEPENDENT RECORD [ size(0): CARDINAL, -- size in words of entire entry (16 bit PrincOps words and 32 bit Mimosa words) version(1): YggNaming.Version, -- version part of name didNamed(2): DIDRep, -- DID that is named name(nameOffset): PACKED ARRAY [0..0) OF CHAR -- name of object, extra nulls added if needed ]; Key: TYPE = REF KeyRep; KeyRep: TYPE = RECORD [ name: ROPE, version: YggNaming.Version ]; AllNulls: PACKED ARRAY [0..Basics.charsPerWord) OF CHAR _ ALL[0C]; StandardCacheSize: INT _ 8; bytesPerPage: INT _ 1024; DirectoryCache: RefTab.Ref _ NIL; desiredSizeOfDirectoryCache: INT _ 50; DirectoryCacheEntry: TYPE = RECORD [ did: YggDID.DID, isADirectory: BOOL _ FALSE, treeStream: IO.STREAM, btreevmHandle: BTreeVM.Handle, tree: BTree.Tree, unused: BOOL _ FALSE, users: INT _ 0 -- -1 means in the process of being opened ]; scratchDirectoryCacheKey: REF DirectoryCacheEntry _ NIL; DirectoryContentsAtom: ATOM _ $directoryContents; DeferredList: LIST OF DeferredItem _ NIL; DeferredItem: TYPE = RECORD[ trans: YggEnvironment.TransID, deferredUpdate: LIST OF DeferredTransItem ]; DeferredTransItem: TYPE = RECORD[ directoryDid: YggDID.DID, update: LIST OF DUpdateItem ]; arcode: TYPE = {update, remove, invalidate}; DUpdateItem: TYPE = RECORD[ addOrRemove: arcode, name: ROPE, version: ROPE, itemDID: YggDID.DID, updateType: BTree.UpdateType ]; DefaultDirectorySizeInPages: INT _ 7; MyCondition: CONDITION; oops: INT _ 0; PreCommit: PUBLIC PROC[tid: YggEnvironment.TransID] ~ { deferredUpdate: LIST OF DeferredTransItem _ NIL; deferredUpdate _ FindItemOnDeferredList[tid, FALSE]; FOR lodti: LIST OF DeferredTransItem _ deferredUpdate, lodti.rest UNTIL lodti = NIL DO ENDLOOP; }; Commit: PUBLIC PROC[tid: YggEnvironment.TransID] ~ { [] _ FindItemOnDeferredList[tid, TRUE]; }; Abort: PUBLIC PROC[tid: YggEnvironment.TransID] ~ { deferredUpdate: LIST OF DeferredTransItem _ NIL; deferredUpdate _ FindItemOnDeferredList[tid, TRUE]; FOR lodti: LIST OF DeferredTransItem _ deferredUpdate, lodti.rest UNTIL lodti = NIL DO RemoveDirectoryFromCache[lodti.first.directoryDid]; ENDLOOP; }; FindItemOnDeferredList: ENTRY PROC [tid: YggEnvironment.TransID, removeIt: BOOL] RETURNS [deferredUpdate: LIST OF DeferredTransItem _ NIL] ~ { prev: LIST OF DeferredItem _ NIL; FOR oldi: LIST OF DeferredItem _ DeferredList, oldi.rest UNTIL oldi = NIL DO IF YggTransaction.EqualTrans[tid, oldi.first.trans] THEN { deferredUpdate _ oldi.first.deferredUpdate; IF removeIt THEN { IF prev = NIL THEN DeferredList _ oldi.rest ELSE prev.rest _ oldi.rest; }; EXIT; }; prev _ oldi; ENDLOOP; }; RememberDeferredUpdate: ENTRY PROC [trans: YggEnvironment.TransID, directoryDid: YggDID.DID, addOrRemove: arcode, name: ROPE, version: ROPE, itemDID: YggDID.DID, updateType: BTree.UpdateType]~ { dupdate: DUpdateItem _ [addOrRemove, name, version, itemDID, updateType]; FOR oldi: LIST OF DeferredItem _ DeferredList, oldi.rest UNTIL oldi = NIL DO IF YggTransaction.EqualTrans[trans, oldi.first.trans] THEN { deferredUpdate: LIST OF DeferredTransItem; FOR deferredUpdate _ oldi.first.deferredUpdate, deferredUpdate.rest UNTIL deferredUpdate.rest = NIL DO ENDLOOP; deferredUpdate.rest _ CONS[[directoryDid, LIST[dupdate]], NIL]; EXIT; }; REPEAT FINISHED => DeferredList _ CONS [[trans, LIST[[directoryDid, LIST[dupdate]]]], DeferredList]; ENDLOOP; }; MkDir: PUBLIC PROC [trans: YggEnvironment.TransID, did: YggDID.DID] RETURNS [directoryAlreadyExists: BOOL _ TRUE] ~ { effectiveTrans: YggEnvironment.TransID; doc: YggInternal.Document _ NIL; granted: BOOL _ FALSE; released: BOOL _ FALSE; pageSize: YggFile.PageCount; didLockID: YggLock.LockID; componentFiles: LIST OF YggInternal.FileHandle; newFile: YggInternal.FileHandle _ NIL; didLockID _ YggLock.MakeLockID[did]; effectiveTrans _ IF YggTransaction.IsNullTrans[trans] THEN YggTransaction.CreateTrans[YggEnvironment.nullTransID] ELSE trans; granted _ YggLock.Set[trans: effectiveTrans, lock: didLockID, mode: directoryWrite, wait: FALSE]; IF ~granted THEN RETURN[TRUE]; doc _ YggDIDMap.OpenDocumentFromDID[did, effectiveTrans]; componentFiles _ YggDIDMap.GetComponentFiles[doc]; IF YggFile.FileFromComponentFiles [componentFiles: componentFiles, fileUse: DirectoryContentsAtom] # NIL THEN { released _ YggLock.Release[trans: effectiveTrans, lock: didLockID, mode: directoryWrite]; IF YggTransaction.IsNullTrans[trans] THEN [] _ YggTransaction.Finish[transID: effectiveTrans, requestedOutcome: abort]; RETURN[TRUE]; }; pageSize _ DefaultDirectorySizeInPages * YggFile.PagesForBytes[bytesPerPage]; newFile _ YggFile.Create[size: pageSize, did: did, fileUse: DirectoryContentsAtom, nearToDid: did, tid: effectiveTrans]; YggFile.SetByteSize[newFile, YggFile.BytesForPages[pageSize], effectiveTrans]; YggDIDMap.AddComponentFile[doc: doc, componentFile: newFile, tid: effectiveTrans]; [] _ OpenDir[did: did, fileUse: DirectoryContentsAtom, cacheSize: StandardCacheSize, initialize: TRUE, trans: trans]; DoneWithDirectory[did]; IF YggTransaction.IsNullTrans[trans] THEN { outcome: YggEnvironment.Outcome; outcome _ YggTransaction.Finish[transID: effectiveTrans, requestedOutcome: commit]; IF outcome # commit THEN ERROR Error[[$commitFailed, "bug: commit failed in MkDir in Naming, but it really should succeed"]]; }; RememberDeferredUpdate[trans, did, invalidate, NIL, NIL, YggEnvironment.nullDID, insert]; RETURN[FALSE]; }; RmDir: PUBLIC PROC [trans: YggEnvironment.TransID, did: YggDID.DID] RETURNS [directoryDoesNotExist: BOOL _ FALSE] ~ { doc: YggInternal.Document _ NIL; granted: BOOL _ FALSE; released: BOOL _ FALSE; didLockID: YggLock.LockID; componentFiles: LIST OF YggInternal.FileHandle; oldFile: YggInternal.FileHandle _ NIL; IF YggTransaction.IsNullTrans[trans] THEN ERROR Error[[$nullTransaction, "RmDir in Naming needs a transaction"]]; didLockID _ YggLock.MakeLockID[did]; granted _ YggLock.Set[trans: trans, lock: didLockID, mode: directoryWrite, wait: FALSE]; IF ~granted THEN ERROR Error[[$cantGetDirectoryLock, "RmDir in Naming could not get a write lock; a retry might work."]]; doc _ YggDIDMap.OpenDocumentFromDID[did, trans]; componentFiles _ YggDIDMap.GetComponentFiles[doc]; IF (oldFile _ YggFile.FileFromComponentFiles[componentFiles: componentFiles, fileUse: DirectoryContentsAtom]) = NIL THEN RETURN[FALSE]; YggDIDMap.RemoveComponentFile[doc: doc, componentFile: oldFile, tid: trans]; YggFile.Delete[file: oldFile, tid: trans]; RemoveDirectoryFromCache[did]; }; Lookup: PUBLIC PROC [trans: YggEnvironment.TransID, directoryDid: YggDID.DID, namePattern: ROPE, version: ROPE] RETURNS [nameFound: BOOL _ FALSE, moreThanOneMatch: BOOL _ FALSE, didFound: YggDID.DID _ YggEnvironment.nullDID, nameMatched: ROPE _ NIL, versionMatched: ROPE _ NIL] ~ { foundProc: YggNaming.EnumProc ~ { nameFound _ TRUE; IF numberFound > 0 THEN { moreThanOneMatch _ TRUE; didFound _ YggEnvironment.nullDID; RETURN[stop: TRUE]; }; numberFound _ numberFound + 1; moreThanOneMatch _ FALSE; didFound _ did; nameMatched _ Rope.FromRefText[name]; versionMatched _ Convert.RopeFromInt[version]; }; numberFound: INT _ 0; [] _ EnumerateEntries[trans, directoryDid, namePattern, version, NIL, NIL, foundProc]; }; VersionMatching: TYPE = {all, bangHOnly, bangLOnly, numberMatch}; EnumerateEntries: PUBLIC PROC [trans: YggEnvironment.TransID, directoryDid: YggDID.DID, namePattern: ROPE, version: ROPE, nameToStart: ROPE, nameToStartVersion: ROPE, proc: YggNaming.EnumProc] RETURNS [notADirectory: BOOL _ TRUE, completedEnumerate: BOOL _ TRUE] ~ { enumProc: UNSAFE PROC [entry: BTree.Entry] RETURNS [continue: BOOL _ TRUE] = TRUSTED { maxEntryChars: INT; entryPtr: EntryPtr = LOOPHOLE[entry]; maxEntryChars _ YggInline.BytesForWords[entryPtr.size - WORDS[Entry]]; IF matching = numberMatch THEN { countSinceLastClashTest _ countSinceLastClashTest + 1; IF countSinceLastClashTest < 10 AND keyRef.version # entryPtr.version THEN RETURN[TRUE]; -- wrong version countSinceLastClashTest _ 0; }; SELECT Match[@entryPtr.name, maxEntryChars, flatNamePattern] FROM fit => { makeRefTextName: UNSAFE PROC [charArray: LONG POINTER TO PACKED ARRAY [0..0) OF CHAR, maxSize: INT] RETURNS [textName: REF TEXT] ~ { GetText: UNSAFE PROC [textRep: LONG POINTER TO PACKED ARRAY [0..0) OF CHAR, text: REF TEXT] = UNCHECKED { text.length _ nameSize; [] _ PBasics.ByteBlt [ to: [ BASE[DESCRIPTOR[text]], 0, nameSize ], from: [ charArray, 0, nameSize ] ]; }; nameSize: INT _ maxSize; WHILE nameSize > 0 DO IF charArray^[nameSize-1] # 0C THEN EXIT; nameSize _ nameSize-1; ENDLOOP; textName _ NEW[TEXT[nameSize]]; GetText[charArray, LOOPHOLE[textName]]; }; name: REF TEXT _ NIL; IF matching = numberMatch AND (keyRef.version # entryPtr.version) THEN RETURN[TRUE]; -- wrong version name _ makeRefTextName[@entryPtr.name, maxEntryChars]; continue _ ~proc[name, entryPtr.version, YggDID.VolatilizeDID[buffer: @entryPtr.didNamed]]; RETURN; }; compatible => RETURN[TRUE]; clash => RETURN[FALSE]; ENDCASE => ERROR; }; granted: BOOL _ FALSE; tree: BTree.Tree; didLockID: YggLock.LockID; countSinceLastClashTest: INT _ 0; matching: VersionMatching _ all; flatNamePattern: Rope.Text = Rope.Flatten[namePattern]; keyRef: Key _ NIL; start: Rope.Text = Rope.Flatten[Rope.Substr[namePattern, 0, Rope.Index[namePattern, 0, "*"]]]; keyRef _ CheckoutKey[]; keyRef.name _ start; didLockID _ YggLock.MakeLockID[directoryDid]; granted _ YggLock.Set[trans: trans, lock: didLockID, mode: directoryRead, wait: FALSE]; IF ~granted THEN ERROR Error[[$cantGetDirectoryLock, "EnumerateEntries in Naming could not get a read lock; a retry might work."]]; [tree: tree] _ GetTreeForDID[directoryDid]; IF tree = NIL THEN RETURN[TRUE, TRUE]; notADirectory _ FALSE; SELECT TRUE FROM Rope.Equal[version, "*"], Rope.Equal[version, ""] => { keyRef.version _ [0]; matching _ all; }; Rope.Equal[version, "h", FALSE]=> { keyRef.version _ [0]; matching _ bangHOnly; }; Rope.Equal[version, "l", FALSE]=> { keyRef.version _ [0]; matching _ bangLOnly; }; ENDCASE => { notInt: BOOL _ FALSE; matching _ numberMatch; keyRef.version _ [Convert.CardFromRope[version ! Convert.Error => {notInt _ TRUE; CONTINUE}]]; IF notInt THEN { DoneWithDirectory[directoryDid]; ERROR Error[[$badVersionSpec, "The version specification is bogus"]]; }; }; TRUSTED {[] _ BTree.EnumerateEntries[tree: tree, relation: greater, key: keyRef, pathStk: NIL, useExistingPath: FALSE, Proc: enumProc];}; DoneWithDirectory[directoryDid]; IF ~YggLock.Release[trans: trans, lock: didLockID, mode: directoryRead] THEN ERROR; RecycleKey[keyRef]; }; MatchResult: TYPE = {fit, compatible, clash}; Match: UNSAFE PROC [name: LONG POINTER TO PACKED ARRAY [0..0) OF CHAR, maxNameSize: INT, pattern: Rope.Text] RETURNS [MatchResult] = UNCHECKED { SubMatch: PROC [i1: INT, len1: INT, i2: INT, len2: INT] RETURNS [MatchResult] = TRUSTED { WHILE len1 > 0 DO c1: CHAR = pText[i1]; IF c1 = '* THEN { -- quick kill for * at end of pattern IF len1 = 1 THEN RETURN [fit]; { -- first, accept the * j1: INT = i1 + 1; nlen1: INT = len1 - 1; j2: INT _ i2; nlen2: INT _ len2; WHILE nlen2 >= 0 DO IF SubMatch[j1, nlen1, j2, nlen2] = fit THEN RETURN [fit]; j2 _ j2 + 1; nlen2 _ nlen2 - 1; ENDLOOP; }; RETURN [compatible]; }; IF len2 = 0 THEN RETURN [compatible]; IF Ascii.Upper[c1] # Ascii.Upper[name[i2]] THEN RETURN [clash]; i1 _ i1 + 1; len1 _ len1 - 1; i2 _ i2 + 1; len2 _ len2 - 1; ENDLOOP; RETURN [IF len2 = 0 THEN fit ELSE clash]; }; pText: REF TEXT = LOOPHOLE[pattern]; nameSize: INT _ maxNameSize; WHILE nameSize > 0 DO IF name^[nameSize-1] # 0C THEN EXIT; nameSize _ nameSize-1; ENDLOOP; RETURN [SubMatch [0, pText.length, 0, nameSize]]; }; SimpleEntryProc: YggNaming.EnumProc = { -- for debugging purposes cont: BOOLEAN _ TRUE; RETURN[cont]; }; UpdateItem: PUBLIC PROC [trans: YggEnvironment.TransID, directoryDid: YggDID.DID, name: ROPE, version: ROPE, did: YggDID.DID, updateType: BTree.UpdateType _ insertOrReplace] RETURNS [notADirectory: BOOL _ FALSE, nameFound: BOOL _ FALSE] ~ { writeEntryInner: UNSAFE PROCEDURE [entry: BTree.Entry] = UNCHECKED { nBytes: INT; nullsToMove: INT; entryPtr: EntryPtr = LOOPHOLE[entry]; entryPtr.version _ keyRef.version; entryPtr.size _ entSize; YggDID.StabilizeDID[did: did, buffer: @entryPtr.didNamed]; TRUSTED {nBytes _ Rope.UnsafeMoveChars[block: [LOOPHOLE[@entryPtr.name], 0, nameSize], rope: name, start: 0];}; nullsToMove _ YggInline.BytesForWords[YggInline.WordsForBytes[nameSize]] - nameSize; IF nullsToMove < 0 THEN ERROR; TRUSTED {nBytes _ PBasics.ByteBlt[ from: [blockPointer: @AllNulls, startIndex: 0, stopIndexPlusOne: nullsToMove], to: [blockPointer: @entryPtr.name, startIndex: nameSize, stopIndexPlusOne: nameSize+nullsToMove]]; }; IF nBytes # nullsToMove THEN ERROR; }; granted: BOOL _ FALSE; didLockID: YggLock.LockID; nameSize: INT; keyRef: Key _ NIL; entSize: INT; tree: BTree.Tree; notInt: BOOL _ FALSE; treeStream: IO.STREAM; IF YggTransaction.IsNullTrans[trans] THEN ERROR Error[[$nullTransaction, "UpdateItem in Naming needs a transaction"]]; nameSize _ Rope.Length[name]; entSize _ YggInline.WordsForBytes[nameSize] + WORDS[Entry]; keyRef _ CheckoutKey[]; keyRef.name _ name; keyRef.version _ [Convert.CardFromRope[version ! Convert.Error => {notInt _ TRUE; CONTINUE}]]; IF notInt AND ~(Rope.Equal[version, "h", FALSE]) THEN ERROR Error[[$badVersionSpec, "The version specification is bogus"]]; didLockID _ YggLock.MakeLockID[directoryDid]; granted _ YggLock.Set[trans: trans, lock: didLockID, mode: directoryWrite, wait: FALSE]; IF ~granted THEN ERROR Error[[$cantGetDirectoryLock, "UpdateItem in Naming could not get a write lock; a retry might work."]]; [tree, treeStream] _ GetTreeForDID[directoryDid]; NewTransForStream[trans, treeStream]; IF notInt THEN { -- highest or lowest version has to be found notADirectory: BOOL _ TRUE; completedEnumerate: BOOL _ TRUE; foundSomething: BOOL _ FALSE; versionFound: YggNaming.Version; enumProc: YggNaming.EnumProc ~ { foundSomething _ TRUE; versionFound _ version; RETURN[TRUE]; }; [notADirectory: notADirectory, completedEnumerate: completedEnumerate] _ EnumerateEntries[trans: trans, directoryDid: directoryDid, namePattern: name, version: version, nameToStart: name, nameToStartVersion: version, proc: enumProc]; IF notADirectory THEN { DoneWithDirectory[directoryDid]; RETURN[TRUE, FALSE]; }; SELECT TRUE FROM foundSomething AND updateType = insert => { keyRef.version _ [versionFound+1]; }; foundSomething AND (updateType = replace OR updateType = insertOrReplace) => { keyRef.version _ versionFound; }; ~foundSomething AND (updateType = insert OR updateType = insertOrReplace) => { keyRef.version _ [1]; }; ~foundSomething AND updateType = replace => { DoneWithDirectory[directoryDid]; IF ~YggLock.Release[trans: trans, lock: didLockID, mode: directoryWrite] THEN ERROR; RETURN[FALSE, FALSE] }; ENDCASE => ERROR; }; TRUSTED {BTree.UpdateEntry[tree: tree, key: keyRef, pathStk: NIL, useExistingPath: FALSE, words: entSize, Proc: writeEntryInner, updateType: updateType ! BTree.Error => { SELECT reason FROM wrongUpdateType => {nameFound _ TRUE; CONTINUE;}; ENDCASE ; }; ];}; RememberDeferredUpdate[trans, directoryDid, invalidate, NIL, NIL, YggEnvironment.nullDID, insert]; DoneWithDirectory[directoryDid]; RecycleKey[keyRef]; }; DeleteItem: PUBLIC PROC [trans: YggEnvironment.TransID, directoryDid: YggDID.DID, name: ROPE, version: ROPE] RETURNS [found: BOOLEAN] ~ { keyRef: Key _ NIL; tree: BTree.Tree; granted: BOOL _ FALSE; didLockID: YggLock.LockID; notInt: BOOL _ FALSE; treeStream: IO.STREAM; IF YggTransaction.IsNullTrans[trans] THEN ERROR Error[[$nullTransaction, "UpdateItem in Naming needs a transaction"]]; keyRef _ CheckoutKey[]; keyRef.name _ name; keyRef.version _ [Convert.CardFromRope[version ! Convert.Error => {notInt _ TRUE; CONTINUE}]]; IF notInt THEN { ERROR Error[[$badVersionSpec, "The version specification is bogus"]]; }; didLockID _ YggLock.MakeLockID[directoryDid]; granted _ YggLock.Set[trans: trans, lock: didLockID, mode: directoryWrite, wait: FALSE]; IF ~granted THEN ERROR Error[[$cantGetDirectoryLock, "DeleteItem in Naming could not get a write lock; a retry might work."]]; [tree, treeStream] _ GetTreeForDID[directoryDid]; NewTransForStream[trans, treeStream]; found _ BTree.DeleteKey[tree: tree, key: keyRef, pathStk: NIL, useExistingPath: FALSE]; RememberDeferredUpdate[trans, directoryDid, invalidate, NIL, NIL, YggEnvironment.nullDID, insert]; DoneWithDirectory[directoryDid]; RecycleKey[keyRef]; }; HasDirectory: PUBLIC PROC [trans: YggEnvironment.TransID, directoryDid: YggDID.DID] RETURNS [isDirectory: BOOL] ~ { tree: BTree.Tree; [tree: tree] _ GetTreeForDID[directoryDid]; IF tree = NIL THEN RETURN [FALSE] ELSE { DoneWithDirectory[directoryDid]; RETURN [TRUE]; }; }; GetTreeForDID: PROC [directoryDid: YggDID.DID] RETURNS [tree: BTree.Tree, treeStream: IO.STREAM _ NIL] ~ { [tree, treeStream] _ OpenDir[directoryDid, DirectoryContentsAtom, StandardCacheSize, FALSE]; }; OpenDir: PROC [did: YggDID.DID, fileUse: ATOM, cacheSize: BTreeVM.CacheSize, initialize: BOOL, trans: YggEnvironment.TransID _ YggEnvironment.nullTransID] RETURNS [tree: BTree.Tree _ NIL, treeStream: IO.STREAM _ NIL] ~ { btreevmHandle: BTreeVM.Handle _ NIL; ce: REF DirectoryCacheEntry; found: BOOL; innerOpen: ENTRY PROC = { val: RefTab.Val; scratchDirectoryCacheKey.did _ did; [found, val] _ RefTab.Fetch[x: DirectoryCache, key: scratchDirectoryCacheKey]; IF found THEN { ce _ NARROW[val]; WHILE ce.users = -1 DO WAIT MyCondition; ENDLOOP; IF ce.isADirectory THEN { ce.users _ ce.users + 1; IF ce.unused THEN { ce.unused _ FALSE; }; }; } ELSE { ce _ NEW[DirectoryCacheEntry _ [did, TRUE, treeStream, NIL, NIL, FALSE, -1]]; IF ~RefTab.Insert[x: DirectoryCache, key: ce, val: ce] THEN ERROR; }; }; innerOpen[]; IF found THEN { IF ~ce.isADirectory THEN RETURN[NIL, NIL]; treeStream _ ce.treeStream; tree _ ce.tree; IF ~YggTransaction.IsNullTrans[trans] THEN { NewTransForStream[trans, treeStream]; }; } ELSE { opened: ENTRY PROC = { ce.users _ 1; BROADCAST MyCondition; }; letGo: ENTRY PROC = { ce.users _ 0; BROADCAST MyCondition; }; doc: YggInternal.Document _ NIL; componentFiles: LIST OF YggFile.FileHandle; doc _ YggDIDMap.OpenDocumentFromDID[did, trans]; componentFiles _ YggDIDMap.GetComponentFiles[doc]; IF YggFile.FileFromComponentFiles[componentFiles, DirectoryContentsAtom] = NIL THEN { ce.isADirectory _ FALSE; letGo[]; RETURN[NIL, NIL]; }; treeStream _ NewStreamForBTreeVM[componentFiles, trans]; NewTransForStream[trans, treeStream]; NewReadStreamForStream [trans: trans, stream: treeStream]; -- use this trans for read while doing init IF treeStream = NIL THEN RETURN[NIL, NIL]; btreevmHandle _ BTreeVM.Open[treeStream, bytesPerPage, cacheSize, 0]; ce.btreevmHandle _ btreevmHandle; ce.treeStream _ treeStream; tree _ BTree.New[ repPrim: [compare: Compare, entrySize: EntrySize], storPrim: [referencePage: BTreeVM.ReferencePage, releasePage: BTreeVM.ReleasePage], minEntrySize: SIZE[YggRep.DocType] + SIZE[CARD], initialState: suspended ]; BTree.Open[ tree: tree, storage: btreevmHandle, pageSize: bytesPerPage, initialize: initialize, maintainRecomputableState: TRUE ]; ce.tree _ tree; NullReadStreamForStream [stream: treeStream]; -- use null trans next time opened[]; }; }; DoneWithDirectory: ENTRY PROC [ directoryDid: YggDID.DID ] = { ce: REF DirectoryCacheEntry; found: BOOL; val: RefTab.Val; scratchDirectoryCacheKey.did _ directoryDid; [found, val] _ RefTab.Fetch[x: DirectoryCache, key: scratchDirectoryCacheKey]; IF found THEN { ce _ NARROW[val]; ce.users _ ce.users - 1; } ELSE oops _ oops + 1; -- debugging }; RemoveDirectoryFromCache: ENTRY PROC [ directoryDid: YggDID.DID ] = { scratchDirectoryCacheKey.did _ directoryDid; [] _ RefTab.Delete[x: DirectoryCache, key: scratchDirectoryCacheKey]; }; Compare: UNSAFE PROC [key: BTree.Key, entry: BTree.Entry] RETURNS [result: Basics.Comparison _ equal] = UNCHECKED { keyRef: Key = NARROW[key]; entryPtr: EntryPtr = LOOPHOLE[entry]; maxEntryChars: INT; keySize: INT; maxEntryChars _ YggInline.BytesForWords[entryPtr.size - WORDS[Entry]]; keySize _ Rope.Length[keyRef.name]; FOR inx: INT IN [0..MIN[maxEntryChars, keySize]) DO entryChar: CHAR; keyChar: CHAR; IF (entryChar _ entryPtr.name[inx]) = 0C THEN RETURN[greater]; keyChar _ Rope.InlineFetch[keyRef.name, inx]; IF entryChar = keyChar THEN LOOP; IF keyChar < entryChar THEN RETURN [less] ELSE RETURN [greater]; ENDLOOP; IF maxEntryChars < keySize THEN RETURN [greater]; IF maxEntryChars > keySize THEN { IF entryPtr.name[keySize] # 0C THEN RETURN [less]; }; SELECT keyRef.version FROM > entryPtr.version => RETURN[greater]; < entryPtr.version => RETURN[less]; ENDCASE => RETURN[equal]; }; EntrySize: UNSAFE PROC [entry: BTree.Entry] RETURNS [words: BTree.EntSize _ 4] = UNCHECKED { entryPtr: EntryPtr = LOOPHOLE[entry]; RETURN[entryPtr.size]; }; myStreamProcs: REF IO.StreamProcs _ IO.CreateStreamProcs[variety: inputOutput, class: $YggdrasilDirectory, unsafeGetBlock: myUnsafeGetBlock, unsafePutBlock: myUnsafePutBlock, flush: myFlush, getLength: myGetLength, setIndex: mySetIndex]; MyStream: TYPE = REF MyStreamRep; MyStreamRep: TYPE = RECORD [ tid: YggEnvironment.TransID, componentFiles: LIST OF YggInternal.FileHandle, readStream: IO.STREAM, currentWriteStream: IO.STREAM, writeStreamPos: INT _ -1 ]; NewStreamForBTreeVM: PROC [componentFiles: LIST OF YggInternal.FileHandle, trans: YggEnvironment.TransID] RETURNS [stream: IO.STREAM] = { stream _ IO.CreateStream[streamProcs: myStreamProcs, streamData: NEW[MyStreamRep _ [tid: trans, componentFiles: componentFiles, readStream: NIL, currentWriteStream: NIL]], backingStream: NIL]; }; NewTransForStream: PROC [trans: YggEnvironment.TransID, newStream: IO.STREAM] = { myStream: MyStream = NARROW[newStream.streamData]; IF ~YggTransaction.EqualTrans[trans, myStream.tid] THEN { myStream.tid _ trans; myStream.currentWriteStream _ NIL; myStream.writeStreamPos _ -1; }; }; NullReadStreamForStream: PROC [stream: IO.STREAM] = { myStream: MyStream = NARROW[stream.streamData]; myStream.readStream _ NIL; }; NewReadStreamForStream: PROC [trans: YggEnvironment.TransID, stream: IO.STREAM] = { myStream: MyStream = NARROW[stream.streamData]; myStream.readStream _ YggFileStream.StreamFromComponentFilesAndTid[componentFiles: myStream.componentFiles, fileUse: DirectoryContentsAtom, tid: trans, readOnly: TRUE]; }; myUnsafeGetBlock: UNSAFE PROC [self: IO.STREAM, block: Basics.UnsafeBlock] RETURNS [nBytesRead: INT] ~ TRUSTED { myStream: MyStream = NARROW[self.streamData]; IF myStream.readStream = NIL THEN { myStream.readStream _ YggFileStream.StreamFromComponentFilesAndTid[componentFiles: myStream.componentFiles, fileUse: DirectoryContentsAtom, tid: YggEnvironment.nullTransID, readOnly: TRUE]; }; nBytesRead _ IO.UnsafeGetBlock[myStream.readStream, block]; }; myUnsafePutBlock: PROC [self: IO.STREAM, block: Basics.UnsafeBlock] ~ { myStream: MyStream = NARROW[self.streamData]; IF myStream.currentWriteStream = NIL THEN { myStream.currentWriteStream _ YggFileStream.StreamFromComponentFilesAndTid[componentFiles: myStream.componentFiles, fileUse: DirectoryContentsAtom, tid: myStream.tid]; }; IF myStream.writeStreamPos # -1 THEN { IO.SetIndex[self: myStream.currentWriteStream, index: myStream.writeStreamPos] ; myStream.writeStreamPos _ -1; }; IO.UnsafePutBlock[myStream.currentWriteStream, block]; }; myFlush: PROC [self: IO.STREAM] ~ { myStream: MyStream = NARROW[self.streamData]; IF myStream.currentWriteStream = NIL THEN { myStream.currentWriteStream _ YggFileStream.StreamFromComponentFilesAndTid[componentFiles: myStream.componentFiles, fileUse: DirectoryContentsAtom, tid: myStream.tid]; }; IF myStream.writeStreamPos # -1 THEN { IO.SetIndex[self: myStream.currentWriteStream, index: myStream.writeStreamPos] ; myStream.writeStreamPos _ -1; }; IO.Flush[myStream.currentWriteStream]; }; myGetLength: PROC [self: IO.STREAM] RETURNS [length: INT] ~ { myStream: MyStream = NARROW[self.streamData]; IF myStream.readStream = NIL THEN { myStream.readStream _ YggFileStream.StreamFromComponentFilesAndTid[componentFiles: myStream.componentFiles, fileUse: DirectoryContentsAtom, tid: YggEnvironment.nullTransID, readOnly: TRUE]; }; length _ IO.GetLength[myStream.readStream]; }; mySetIndex: PROC [self: IO.STREAM, index: INT] ~ { myStream: MyStream = NARROW[self.streamData]; IF myStream.readStream = NIL THEN { myStream.readStream _ YggFileStream.StreamFromComponentFilesAndTid[componentFiles: myStream.componentFiles, fileUse: DirectoryContentsAtom, tid: YggEnvironment.nullTransID, readOnly: TRUE]; }; IO.SetIndex[self: myStream.readStream, index: index ! IO.EndOfStream => CONTINUE]; IF myStream.currentWriteStream # NIL THEN { IO.SetIndex[self: myStream.currentWriteStream, index: index] ; myStream.writeStreamPos _ -1; } ELSE myStream.writeStreamPos _ index; }; stockKeys: ARRAY [0..numberOfStockKeys] OF Key _ ALL[NIL]; numberOfStockKeys: INT = 10; stockKeyIndex: INT _ 0; CheckoutKey: ENTRY PROC RETURNS [k: Key] = { IF stockKeyIndex = 0 THEN k _ NEW [KeyRep] ELSE { stockKeyIndex _ stockKeyIndex - 1; k _ stockKeys[stockKeyIndex]; }; }; RecycleKey: ENTRY PROC [k: Key] = { IF stockKeyIndex < numberOfStockKeys THEN { stockKeys[stockKeyIndex] _ k; stockKeyIndex _ stockKeyIndex + 1; }; }; equalProc: RefTab.EqualProc = { k1: REF DirectoryCacheEntry; k2: REF DirectoryCacheEntry; k1 _ NARROW[key1]; k2 _ NARROW[key2]; RETURN [YggDID.EqualDIDs[k1.did, k2.did]]; }; hashProc: RefTab.HashProc = { keyRef: REF DirectoryCacheEntry; keyRef _ NARROW[key]; RETURN [PBasics.LowHalf[keyRef.did.didLow]]; }; NamingCacheTrimProcess: PROC = { ticksToWait: Process.Ticks; ticksToWait _ Process.MsecToTicks[1315]; DO innerTrim: ENTRY PROC = { eachPairAction: RefTab.EachPairAction = { ce: REF DirectoryCacheEntry; ce _ NARROW[val]; IF ce.unused THEN { [] _ RefTab.Delete[x: DirectoryCache, key: ce]; size _ size - 1; IF size <= desiredSizeOfDirectoryCache THEN quit _ TRUE; } ELSE ce.unused _ TRUE; }; size: INT; size _ RefTab.GetSize[DirectoryCache]; [] _ RefTab.Pairs[x: DirectoryCache, action: eachPairAction]; }; Process.Pause[ticksToWait]; IF RefTab.GetSize[DirectoryCache] > desiredSizeOfDirectoryCache THEN innerTrim[]; ENDLOOP; }; TRUSTED { Process.Detach[FORK NamingCacheTrimProcess]; Process.SetTimeout[condition: @MyCondition, ticks: Process.MsecToTicks[171]]; }; DirectoryCache _ RefTab.Create[mod: 47, equal: equalProc, hash: hashProc]; scratchDirectoryCacheKey _ NEW[DirectoryCacheEntry]; END. fYggNamingImpl.mesa Copyright Σ 1988, 1989 by Xerox Corporation. All rights reserved. Bob Hagmann March 21, 1989 8:59:12 am PST Top level naming ``interface'' for Yggdrasil. Things to think about: 1) Is the directory caching reasonable? 2) Does locking work OK? Types, variables, and constants Transactions Directories Make a new directory. Destroy a directory. Files Look up the name/version in the directory property of the directoryDid. The namePattern may include "*" characters. The version is either NIL (highest version), "h" (highest version), "l" (lowest version), or the ASCII string for a number (base 10). If nameFound is TRUE, then the name/version specifies a single file and it's did is returned in didFound while its name/version is returned in nameMatched and versionMatched. If nameFound is FALSE but moreThanOneMatch is TRUE, then the name/version specifies more than one file. didFound is not interesting. If both nameFound and moreThanOneMatch are FALSE, then no names match the name/version and didFound is not interesting. Calls `proc' for each entry in the specified matching key values. The enumeration is halted when either the matching of entries is exhausted or `proc' returns FALSE The match result is computed on the assumtion that name is always GE the pattern prefix (characters up to the first star). In this case, "compatible" means that it is sensible to present another name GE the current one, and "clash" means that such a name cannot "fit". "1" is the pattern, "2" is the name else must take all combinations at this point demand an exact match in both strings Directroy manipulation [name: REF TEXT, version: Version, did: YggDID.DID] RETURNS [continue: BOOL] set breakpoint here to look at entry Adds a new entry to the index. PROC [name: REF TEXT, version: Version, did: YggDID.DID] RETURNS [stop: BOOL _ FALSE]; Deletes the entry that contains the given attribute value for the given key. Returns TRUE iff there is a directory contents BTreeVM related procedures Must call DoneWithDirectory[treeStream] when done!! Initiates directory activity. This can be called any number of times to get a new directory handle or reopen a directory that has been closed. scratchDirectoryCacheKey _ NEW[DirectoryCacheEntry]; -- used to use scratchDirectoryCacheKey as a key SafeStorage.EnableFinalization[h]; Done with the current use of the BTree. RmDir has removed it. My streams Utilities Equals and hash PROC [key1, key2: Key] RETURNS [BOOL]; PROC PROC [key: Key] RETURNS [CARDINAL]; Initialization, cache trim, and finalization PROC [key: Key, val: Val] RETURNS [quit: BOOL _ FALSE]; Initialization Κ!(˜codešœ™KšœB™BKšœ)™)—K™K™K™-K™™K™'K™—K™šΟk ˜ Kšœ˜Kšœœ)˜5Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœœ9œ˜jKšœœ˜!Kšœœœ*˜;Kšœ œQ˜`Kšœœ ˜Kšœœ*˜>Kšœœl˜yKšœœ"˜5Kšœœ˜Kšœ œ˜Kšœ œ ˜/Kšœ œ˜)Kšœœ$˜1Kšœ œ ˜/Kšœœ ˜Kšœœ0˜D—K˜KšΡbln œœ˜Kšœ’˜™Kšœ˜šœ˜K˜—head™Icode0šœœœ˜M˜KšΟnœœœ œ˜8K˜K˜Kšœœœœ˜Kšœœœ˜+K˜Icode2š œ œœœœœ˜,Kšœ œœ ˜#š œœœ œœ˜(Kšœ œΟcP˜cKšœ ˜6Kšœ ˜*Kšœ œœœ .˜]Kšœ˜—K˜Nšœœœ˜šœœœ˜Nšœœ˜ Nšœ˜Nšœ˜—K˜Kš Ÿœœœœœœ˜BK˜KšŸœœ˜K˜Mšœœ˜Nšœœ˜!Nšœœ˜&šœœœ˜$Nšœ œ˜Nšœœœ˜Nšœ œœ˜Kšœ˜Kšœ˜Kšœœœ˜Nšœœ *˜:Nšœ˜—Nšœœ˜8N˜1NšŸ œœœœ˜)šœœœ˜Nšœ˜Nšœœœ˜)N˜—šœœœ˜!Nšœœ˜Nšœ˜N˜—Nšœœ ˜,šœ œœ˜Nšœ˜Nšœœ˜ Nšœ œ˜Nšœœ˜Nšœ˜N˜—N˜NšŸœœ˜%Nšœ  œ˜N˜Nšœœ˜˜K˜——™ šŸ œœœ!˜7Kšœœœœ˜0Kšœ-œ˜4š œœœ0œ œ˜VKšœ˜—K˜—K˜šŸœœœ!˜4Kšœ!œ˜'K˜—K˜šŸœœœ!˜3Kšœœœœ˜0Kšœ-œ˜3š œœœ0œ œ˜VKšœ3˜3Kšœ˜—K˜K˜—šŸœ œ(œœœœœ˜ŽKšœœœœ˜!š œœœ(œœ˜Lšœ2œ˜:Kšœ+˜+šœ œ˜Kšœœœ˜+Kšœœ˜K˜—Kšœ˜K˜—Kšœ ˜ Kšœ˜—K˜K˜—šŸœœœ6œœ œœ"˜ΒKšœI˜Iš œœœ(œœ˜Lšœ4œ˜Nšœœœ˜Nšœœœ˜ Nšœœœ˜Nšœ ˜ š’œ˜ Nšœœœ œœœœ™VNšœœ˜Nšœ˜Nšœœ˜ N˜—Nšœι˜ιšœœ˜Kšœ ˜ Jšœœœ˜J˜—šœœ˜šœœ˜+Jšœ"˜"J˜—šœœœ#˜NJšœ˜J˜—šœœœ#˜NJšœ˜J˜—šœœ˜-Kšœ ˜ JšœGœœ˜TJšœœœ˜J˜—Jšœœ˜—K˜—šœ6œœR˜ͺšœ˜Kšœ œ œ˜1Kšœ˜ —Kšœ˜—Kšœ˜Kšœ8œœ"˜bKšœ ˜ Kšœ˜K˜—K˜šŸ œœœ6œœ œœ œ˜‰KšœL™LNšœœ˜Kšœ˜Kšœ œœ˜Kšœ˜Kšœœœ˜Kšœ œœ˜Kšœ#œœG˜vKšœ˜Kšœ˜KšœLœœ˜^šœœ˜Kšœ@˜EK˜—Kšœ-˜-KšœQœ˜XKšœ œœh˜~Kšœ1˜1Kšœ%˜%KšœW˜WKšœ8œœ"˜bKšœ ˜ Kšœ˜K˜K˜—š Ÿ œ œ6œœœ˜sKšœœ"™.Kšœ˜Kšœ+˜+š œœœœœœ˜(Kšœ ˜ Kšœœ˜K˜—K˜K™—K˜—™š ’ œœœœ œœ ˜jKšœ ‘œ ‘œ ™3KšœUœ˜\N˜N˜—šŸœœœ œ,œ>œœœœœ˜άKšœ™Nšœ œ˜$Nšœœ˜Nšœœ˜ š’ œœœ˜Nšœ˜Nšœ#˜#NšœN˜Nšœœ˜Nšœœ˜Nšœœœœ˜1šœœ˜Nšœ˜šœ œ˜Nšœ œ˜N˜—N˜—N˜—šœœ˜Nš œœœœœœ˜MNšœ5œœ˜BNšœœH™fN˜—N˜—Nšœ ˜ šœœ˜Nš œœœœœ˜*Nšœ˜N˜šœ$œ˜,Kšœ%˜%K˜—N˜—˜š’œœœ˜Nšœ ˜ Nš œ ˜N˜—š’œœœ˜Nšœ ˜ Nš œ ˜N˜—Kšœœ˜ Kšœœœ˜+Kšœ0˜0Kšœ2˜2šœIœœ˜UKšœœ˜Nšœ˜Kšœœœ˜K˜—Kšœ8˜8Kšœ%˜%Kšœ< +˜gKš œœœœœœ˜*NšœE˜ENšœ!˜!Nšœ˜šœ˜K˜2KšœS˜SKšœœœœ˜1K˜Kšœ˜—šœ ˜ Kšœ ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜—N˜Kšœ/ ˜JNšœ ˜ N™"N˜—N˜Nšœ˜N˜—šŸœ œœ˜>K™'Nšœœ˜Nšœœ˜ Nšœ˜Nšœ,˜,NšœN˜Nšœœ˜Nšœœ˜Nšœ˜N˜—Nšœœ  ˜$K˜K˜—š’œ œœ˜EK™Kšœ,˜,NšœE˜EK˜K˜—š Ÿœœœ&œ' œ˜sNšœœ˜Nšœœ˜%Nšœœ˜Nšœ œ˜ Nšœ8œ ˜FNšœ#˜#š œœœœ˜3Nšœ œ˜Nšœ œ˜Nšœ'œœ ˜>Jšœ-˜-Jšœœœ˜!Jš œœœœœ ˜@Nšœ˜—Nšœœœ ˜1šœœ˜!Nšœœœ˜2Nšœ˜—šœ˜Nšœœ ˜&Nšœœ˜#Nšœœ˜—N˜—š Ÿ œœœœ œ˜\Nšœœ˜%Nšœ˜Nšœ˜——™ JšœœœœΘ˜ξJšœ œœ ˜!šœ œœ˜Jšœ˜Jšœœœ˜/Jšœ œœ˜Jšœœœ˜Jšœœ˜J˜—šŸœœœœ8œ œœ˜‰Nš œ œ6œHœœœ˜ΐNšœ˜—šŸœœ,œœ˜QKšœœ˜2šœ1œ˜9Nšœ˜Nšœœ˜"Nšœ˜N˜—Nšœ˜—šŸœœ œœ˜5Kšœœ˜/Kšœœ˜Nšœ˜—šŸœœ)œœ˜SKšœœ˜/Kšœ’œ˜¨Nšœ˜—š’œœœœœœœœ˜pKšœœ˜-šœœœ˜#Kšœ·œ˜½N˜—Nšœ œ,˜;N˜—š’œœœœ ˜GKšœœ˜-šœœœ˜+Kšœ§˜§N˜—šœ&˜&KšœN˜PKšœ˜K˜—Kšœ4˜6N˜—š’œœ œ˜#Kšœœ˜-šœœœ˜+Kšœ§˜§N˜—šœœ˜&KšœN˜PKšœ˜K˜—Kšœ$˜&N˜—š ’ œœ œœ œ˜=Kšœœ˜-šœœœ˜#Kšœ·œ˜½N˜—Nšœœ ˜+N˜—š ’ œœœœ œ˜2Kšœœ˜-šœœœ˜#Kšœ·œ˜½N˜—Nšœ4œœ˜Ršœœœ˜+Nšœ<˜>Nšœ˜N˜—Nšœ%˜%N˜——™ Nš œ œœœœ˜:Nšœœ˜Nšœœ˜šŸ œœœœ ˜,Nšœœœ ˜*šœœ˜Nšœ"˜"Nšœ˜Nšœ˜—Nšœ˜—šŸ œœœ ˜#šœ#œ˜,Nšœ˜Nšœ"˜"N˜—Nšœ˜——™š’ œ˜Nšœœœ™&Nšœœ˜Nšœœ˜Nšœœ˜Nšœœ˜Nšœ$˜*Nšœ˜—š’œ˜Nšœœ œœ™(Nšœœ˜ Nšœ œ˜Nšœ&˜,Nšœ˜——™,šŸœœ˜ Nšœ˜Nšœ(˜(š˜š’ œœœ˜šœ)˜)Nšœœœœ™7Nšœœ˜Nšœœ˜šœ ˜Nšœ/˜/Nšœ˜Nšœ%œœ˜8N˜—Nšœœ œ˜Nšœ˜—Nšœœ˜ Nšœ&˜&Nšœ=˜=Nšœ˜—Nšœ˜Nšœ>œ ˜QNšœ˜—Nšœ˜—K˜—™N˜šœ˜ Nšœœ˜,NšœM˜MNšœ˜—NšœJ˜JNšœ4˜4—K˜Kšœ˜—…—lĘR