-- file VirtTOC.Mesa -- Edited by Schroeder, October 17, 1979 3:38 PM. -- Edited by Brotz, January 28, 1983 1:55 PM. -- Edited by Levin, February 25, 1981 10:37 AM. DIRECTORY exD: FROM "ExceptionDefs" USING [SysBug], FrameDefs USING [GetCaller], Inline USING [BITAND, COPY], Process USING [Yield], Storage USING [Node], String USING [WordsForString], vmD: FROM "VirtualMgrDefs" USING [bugTrapValue, DisplayMessagePtr, DMList, DMListBlk, GetBuffer, HardTOCAddress, MakeBufferEmpty, PageNumber, TOCFixedPart, TOCFixedPartPtr, TOCHandle, TOCIndex, TOCMinStringSize, TOCOverflow, TOCPageHeader, TOCPageHeaderBlk, TOCPageTableSize, TOCType, VoidCharCache], VMDefs USING [Error, MarkStartWait, Page, Release]; VirtTOC: MONITOR IMPORTS exD, FrameDefs, Inline, Process, Storage, String, vmD, VMDefs EXPORTS vmD = BEGIN OPEN vmD; -- The TOC Department is implemented here and in VirtSS.mesa. nextKey: CARDINAL _ 1; LockTOC: PUBLIC ENTRY PROCEDURE [toc: TOCHandle] RETURNS [key: CARDINAL] = -- Returns either 0, meaning that the toc is already locked, or some key # 0. A caller -- that receives a 0 must not call any other procedures requiring the TOC key. A non-0 -- key may be used by the caller in subsequent calls to VirtTOC procedures until the -- toc is unlocked by explicit call to UnlockTOC. BEGIN IF toc.key # 0 THEN RETURN[0]; key _ toc.key _ nextKey; nextKey _ nextKey + 1; IF nextKey = 0 THEN nextKey _ 1; toc.keyHolder _ FrameDefs.GetCaller[]; END; -- of LockTOC -- WaitForLock: PUBLIC PROCEDURE [toc: TOCHandle] RETURNS [key: CARDINAL] = -- Busy waits until it can return a non-zero key for toc. BEGIN WHILE (key _ LockTOC[toc]) = 0 DO Process.Yield[]; ENDLOOP; toc.keyHolder _ FrameDefs.GetCaller[]; END; -- of WaitForLock -- UnlockTOC: PUBLIC PROCEDURE [toc: TOCHandle, key: CARDINAL] = -- Unlocks the toc. BEGIN IF key = 0 OR key # toc.key THEN exD.SysBug[]; toc.keyHolder _ NIL; toc.key _ 0; END; -- of UnlockTOC -- ExtendTOC: PUBLIC PROCEDURE [toc: TOCHandle, key: CARDINAL, fp: TOCFixedPartPtr, s: STRING] = -- The TOC data and string are incorporated in the TOC structure at the end. After this -- procedure is called, the information and string can be modified with -- PutTOC(FixedPart/String) and accessed with GetTOC(FixedPart/String). Care is taken -- to allocate a disk page for each new page of the TOC that is assigned. -- May raise TOCOverflow. BEGIN numWords, remainingWordsOnPage: CARDINAL; sLen: CARDINAL = s.length; sMaxLen: CARDINAL = MAX[sLen, TOCMinStringSize]; sWords: CARDINAL = String.WordsForString[sMaxLen]; hta: HardTOCAddress; pageHeader: TOCPageHeader; IF key = 0 OR key # toc.key THEN exD.SysBug[]; numWords _ SIZE[TOCFixedPart] + sWords; IF numWords > 256 - SIZE[TOCPageHeaderBlk] THEN exD.SysBug[]; IF toc.indexFF = 0 THEN remainingWordsOnPage _ 0 ELSE BEGIN GetBuffer[toc, toc.filePageFF - 1, FALSE]; pageHeader _ LOOPHOLE[toc.buffer]; hta _ LOOPHOLE[toc.buffer + SIZE[TOCPageHeaderBlk]]; THROUGH [toc.pageTable[toc.logicalPageNumber] .. toc.indexFF) DO hta _ WordsInTOCEntry[hta] + hta; ENDLOOP; remainingWordsOnPage _ 256 - Inline.BITAND[255, LOOPHOLE[hta, CARDINAL]]; IF remainingWordsOnPage = 256 THEN remainingWordsOnPage _ 0; END; IF numWords > remainingWordsOnPage THEN BEGIN -- Get a new page. IF toc.filePageFF >= TOCPageTableSize THEN ERROR TOCOverflow; GetBuffer[toc, toc.filePageFF, TRUE]; pageHeader _ LOOPHOLE[toc.buffer]; hta _ LOOPHOLE[toc.buffer + SIZE[TOCPageHeaderBlk]]; pageHeader.numberOfEntries _ 0; pageHeader.garbageDetector _ TOCType; -- for LoadTOC. --make sure there's a place on the disk for the new page VMDefs.MarkStartWait[toc.buffer ! VMDefs.Error => IF reason = resources THEN {VMDefs.Release[toc.buffer]; toc.bufferState _ empty}]; toc.pageTable[toc.filePageFF].firstTOCIndex _ toc.indexFF; toc.filePageFF _ toc.filePageFF+1; END; -- We have gotten and inited a new page. toc.indexFF _ toc.indexFF + 1; pageHeader.numberOfEntries _ pageHeader.numberOfEntries + 1; toc.bufferState _ dirty; fp.changed _ FALSE; PutTOCFixedPartInMemory[fp, hta]; PutTOCStringInMemory[s, sWords, sLen, sMaxLen, hta + SIZE[TOCFixedPart]]; END; -- of ExtendTOC -- GetTOCFixedPart: PUBLIC PROCEDURE [toc: TOCHandle, key: CARDINAL, index: TOCIndex, fp: TOCFixedPartPtr] = -- Obtains the index'th TOCEntry data portion and copies it into the structure pointed at by -- fP. BEGIN hta: HardTOCAddress; IF key = 0 OR key # toc.key THEN exD.SysBug[]; hta _ FindTOCAddress[toc, index]; fp^ _ LOOPHOLE[hta, TOCFixedPartPtr]^; END; -- of GetTOCFixedPart -- GetTOCString: PUBLIC PROCEDURE [toc: TOCHandle, key: CARDINAL, index: TOCIndex, s: STRING] = -- Obtains the index'th TOCEntry string and copies as much as will fit into s. BEGIN hta: HardTOCAddress; s2: STRING; size: CARDINAL; IF key = 0 OR key # toc.key THEN exD.SysBug[]; hta _ FindTOCAddress[toc, index]; s2 _ LOOPHOLE[hta + SIZE[TOCFixedPart], STRING]; size _ MIN[s.maxlength, s2.length]; Inline.COPY[@s2.text, String.WordsForString[size] - 2, @s.text]; s.length _ size; END; -- of GetTOCString -- PutTOCFixedPart: PUBLIC PROCEDURE [toc: TOCHandle, key: CARDINAL, index: TOCIndex, fp: TOCFixedPartPtr, changed: BOOLEAN _ TRUE] = -- Obtains the index'th TOCEntry and copies portions of the data structure pointed to by toc -- into it. Also sets the changed flag of that TOCEntry to "changed" and marks the TOC -- page as having been written. BEGIN hta: HardTOCAddress; IF key = 0 OR key # toc.key THEN exD.SysBug[]; hta _ FindTOCAddress[toc, index]; PutTOCFixedPartInMemory[fp, hta]; MarkChange[toc, index, hta, changed]; END; -- of PutTOCFixedPart -- PutTOCString: PUBLIC PROCEDURE [toc: TOCHandle, key: CARDINAL, index: TOCIndex, s: STRING] = -- Obtains the index'th TOCEntry and copies as much of s as will fit into the string portion -- of this entry. Marks the TOC page as having been written. BEGIN hta: HardTOCAddress; old: STRING; sMaxLen, sLen: CARDINAL; IF key = 0 OR key # toc.key THEN exD.SysBug[]; hta _ FindTOCAddress[toc, index]; old _ LOOPHOLE[hta + SIZE[TOCFixedPart], STRING]; sMaxLen _ old.maxlength; sLen _ MIN[s.length, sMaxLen]; PutTOCStringInMemory[s, String.WordsForString[sMaxLen], sLen, sMaxLen, old]; MarkChange[toc, index, hta]; END; -- of PutTOCString -- MarkChange: PROCEDURE [toc: TOCHandle, index: TOCIndex, hta: HardTOCAddress, changed: BOOLEAN _ TRUE] = -- House-keep changed records. BEGIN LOOPHOLE[hta, TOCFixedPartPtr].changed _ changed; IF changed THEN toc.firstChange _ (IF toc.firstChange = 0 THEN index ELSE MIN[toc.firstChange, index]); toc.bufferState _ dirty; END; -- of MarkChange -- FirstFreeTOCIndex: PUBLIC PROCEDURE [toc: TOCHandle, key: CARDINAL] RETURNS [index: TOCIndex] = -- This procedure returns the TOC Index value of the next entry available after the current -- TOC, which is equal to the number of entries in the TOC and one greater than the real -- last TOC Index. If the toc is not open then zero is returned. BEGIN IF key = 0 OR key # toc.key THEN exD.SysBug[]; RETURN[IF ~toc.open THEN 0 ELSE toc.indexFF]; END; -- of FirstFreeTOCIndex -- FirstChangedTOCIndex: PUBLIC PROCEDURE [toc: TOCHandle] RETURNS [TOCIndex] = -- Returns the TOC Index value of the 1st (i.e. the numerically lowest) TOC entry which -- has been changed via PutTOCFixedPart. This is the first TOC entry which has the -- "changed" BOOLEAN as true. ExtendTOC does not normally set this true (as does -- PutTOCFixedPart), but will respect that setting if the user does so. If no TOC entries -- have changed=TRUE, then the first free (ref. GetFirstFreeTOCIndex) index is returned. -- Non-recoverable errors: "No TOC open". BEGIN IF ~toc.open THEN exD.SysBug []; RETURN[IF toc.firstChange = 0 THEN toc.indexFF ELSE toc.firstChange]; END; -- of FirstChangedTOCIndex -- PutTOCFixedPartInMemory: PROCEDURE [fp: TOCFixedPartPtr, hta: HardTOCAddress] = -- Internal proc to stash FixedPart into memory. BEGIN fp.bugTrap _ bugTrapValue; Inline.COPY[fp, SIZE[TOCFixedPart], hta]; END; -- of PutTOCFixedPartInMemory -- PutTOCStringInMemory: PROCEDURE [s: STRING, sWords, sLen, sMaxLen: CARDINAL, hta: HardTOCAddress] = -- Internal proc to stash String into memory. (This is made complicated by my avoiding -- knowing the structure of a STRING, which may change!) BEGIN Inline.COPY[s, sWords, hta]; LOOPHOLE[hta, STRING]^ _ StringBody[length: sLen, maxlength: sMaxLen, text: ]; END; -- of PutTOCFixedPartInMemory -- TOCAddressOnPage: PUBLIC PROCEDURE [toc: TOCHandle, index: TOCIndex, page: VMDefs.Page, pn: PageNumber] RETURNS [TOCFixedPartPtr] = BEGIN fp: TOCFixedPartPtr _ LOOPHOLE[page + SIZE[TOCPageHeaderBlk]]; first: TOCIndex = toc.pageTable[pn]; FOR i: CARDINAL IN [first .. index) DO fp _ fp + WordsInTOCEntry[fp]; ENDLOOP; IF fp.bugTrap # bugTrapValue THEN exD.SysBug[]; RETURN[fp]; END; -- of TOCAddressOnPage -- SearchTOCTable: PUBLIC PROCEDURE [toc: TOCHandle, index: TOCIndex] RETURNS [pn: PageNumber] = -- Maps a TOCIndex to a TOC file page number. BEGIN IF index >= toc.indexFF THEN exD.SysBug[]; FOR pn IN [1 .. toc.filePageFF) DO IF toc.pageTable[pn] > index THEN EXIT; REPEAT FINISHED => pn _ toc.filePageFF; ENDLOOP; pn _ pn - 1; END; -- of SearchTOCTable -- WordsInTOCEntry: PROCEDURE [hta: HardTOCAddress] RETURNS [CARDINAL] = INLINE BEGIN fWords: CARDINAL = SIZE[TOCFixedPart]; RETURN[fWords + String.WordsForString[LOOPHOLE[hta + fWords, STRING].maxlength]]; END; -- of WordsInTOCEntry -- FindTOCAddress: PUBLIC PROCEDURE [toc: TOCHandle, index: TOCIndex] RETURNS [hta: HardTOCAddress] = -- You'd better be careful, cache activity may cause the pointers returned to become -- dangling references. -- Leaves the toc page containing index in toc.buffer. BEGIN pn: PageNumber = SearchTOCTable[toc, index]; -- index is on "page". IF toc.key = 0 THEN exD.SysBug[]; GetBuffer[toc, pn, FALSE]; RETURN [TOCAddressOnPage[toc, index, toc.buffer, pn]]; END; -- of FindTOCAddress -- LoadDisplayMessage: PUBLIC PROCEDURE [toc: TOCHandle, key: CARDINAL, index: TOCIndex, msg: DisplayMessagePtr] = -- Calls ClearMessage[msg] and then initializes msg to be the messsage string pointed to by -- the index TOC entry. Any previous contents of msg are lost. BEGIN hta: HardTOCAddress; fp: TOCFixedPartPtr; dml, nilMsgDml: DMList _ NIL; IF key = 0 OR key # toc.key OR ~toc.open THEN exD.SysBug[]; hta _ FindTOCAddress[toc, index]; fp _ LOOPHOLE[hta]; FOR dml _ toc.dmList, dml.next UNTIL dml = NIL DO IF dml.dm = msg THEN exD.SysBug[]; -- someone forgot to unvirtualize the last msg. IF nilMsgDml = NIL AND dml.dm = NIL THEN nilMsgDml _ dml; ENDLOOP; IF nilMsgDml = NIL THEN BEGIN nilMsgDml _ Storage.Node[SIZE[DMListBlk]]; nilMsgDml^ _ DMListBlk[next: toc.dmList, dm: msg]; toc.dmList _ nilMsgDml; END ELSE nilMsgDml.dm _ msg; VoidCharCache[@msg.get]; msg.toc _ toc; msg.file _ toc.mailFile; msg.open _ TRUE; msg.index _ index; msg.firstPage _ fp.firstPage; msg.firstByte _ fp.firstByte + fp.offsetToHeader; msg.textLength _ fp.textLength; -- in bytes, END; -- of LoadDisplayMessage -- FlushDisplayMessage: PUBLIC PROCEDURE [msg: DisplayMessagePtr, key: CARDINAL] = -- Notifies the virtual manager that a displayed message is no longer in use. BEGIN dml, nilMsgDml: DMList _ NIL; CheckpointDisplayMessage[msg, key]; FOR dml _ msg.toc.dmList, dml.next UNTIL dml = NIL DO IF dml.dm = msg THEN {dml.dm _ NIL; EXIT}; REPEAT FINISHED => exD.SysBug[]; ENDLOOP; msg.toc _ NIL; msg.file _ NIL; msg.index _ 0; msg.open _ FALSE; msg.firstPage _ 0; msg.firstByte _ 0; msg.textLength _ 0; END; -- of FlushDisplayMessage -- CheckpointDisplayMessage: PUBLIC PROC [msg: DisplayMessagePtr, key: CARDINAL] = -- In cases where the mail file is to be written (as when appending to the file), this -- procedure updates the mail file to contain all changes in msg. msg is manipulateds so -- that subsequent accesses to its contents will require initial reading from the mail file -- once again. BEGIN IF msg.toc = NIL OR key = 0 OR key # msg.toc.key OR ~msg.toc.open THEN exD.SysBug[]; MakeBufferEmpty[msg]; VoidCharCache[@msg.get]; END; -- of CheckpointDisplayMessage -- END. -- of VirtTOCz20461(529)\f1