-- file VirtTOC.Mesa
-- Edited by Schroeder, October 17, 1979 3:38 PM.
-- Edited by Brotz, August 5, 1980 5:42 PM.
-- Edited by Levin, February 25, 1981 10:37 AM.

DIRECTORY
crD: FROM "CoreDefs",
exD: FROM "ExceptionDefs",
gsD: FROM "GlobalStorageDefs",
InlineDefs: FROM "InlineDefs",
ovD: FROM "OverviewDefs",
StringDefs: FROM "StringDefs",
SystemDefs: FROM "SystemDefs",
vmD: FROM "VirtualMgrDefs";

VirtTOC: PROGRAM
IMPORTS crD, exD, InlineDefs, StringDefs, vmD
EXPORTS vmD
SHARES vmD = PUBLIC

BEGIN

OPEN vmD;

-- Data Structures and Types.

tOCp: POINTER TO TOC←GetTOCPtr[];


-- The TOC Department is implemented here and in VirtSS.mesa.

ExtendTOC: PROCEDURE [fP: TOCFixedPartPtr, s: STRING] RETURNS [ovD.ErrorCode] =
-- The TOC data and string are encorporated in the TOC structure at the end (with
-- fP.changed←FALSE). After this procedure is called, the information and string can be
-- modified with PutTOC(Data/String) and accessed with GetTOC(Data/String).
BEGIN
numWords,remainingWordsOnPage: CARDINAL;
errorCode: ovD.ErrorCode ← ovD.ok;
sLen: CARDINAL = s.length;
sMaxLen: CARDINAL = MAX[sLen,tOCMinStringSize];
sWords: CARDINAL = StringDefs.WordsForString[sMaxLen];
mtPtr: MemoryTableEntryPtr;
hPtr: HardTOCAddress;

numWords ← SIZE[TOCFixedPart] + sWords;
IF numWords > 256 - SIZE[TOCPageHdr] THEN exD.SysBug[];
IF tOCp↑.indexFF=0
THEN remainingWordsOnPage ← 0
ELSE BEGIN
[mtPtr,] ← GetMtPtr[tOCp, tOCp↑.filePageFF-1, active];
IF mtPtr.state=unused THEN exD.SysBug[];
hPtr ← LOOPHOLE[mtPtr.address+SIZE[TOCPageHdr]];
THROUGH [tOCp↑.pageTableHeader↑[mtPtr↑.logicalPageNumber]..tOCp↑.indexFF) DO
hPtr ← WordsInTOCEntry[hPtr] + hPtr;
ENDLOOP;
remainingWordsOnPage ←
256 - InlineDefs.BITAND[255, LOOPHOLE[hPtr, CARDINAL]];
IF remainingWordsOnPage=256 THEN remainingWordsOnPage ← 0;
END;

IF numWords > remainingWordsOnPage
THEN BEGIN -- Get a new page.
IF tOCp↑.filePageFF>=tOCPageTableSize THEN RETURN [ovD.tOCOverflow];
[mtPtr,] ← GetMtPtr[tOCp, tOCp↑.filePageFF, new];
hPtr ← mtPtr.address+SIZE[TOCPageHdr];
mtPtr↑.address↑.numberOfEntries ← 0;
mtPtr↑.address↑.garbageDetector ← tOCType; -- for VirtualizeTOC.
--make sure there’s a place on the disk for the new page
errorCode ← crD.WritePages[mtPtr↑.address, 512, mtPtr↑.filePageNumber, tOCp↑.file];
SELECT errorCode FROM
ovD.ok => NULL;
ovD.diskFull =>
BEGIN --undo the extension, report disk full
mtPtr.state ← unused;
RETURN [ovD.diskFull]
END;
ENDCASE => BombIfIOError[errorCode];
tOCp↑.pageTableHeader[tOCp↑.filePageFF].firstTOCIndex ← tOCp↑.indexFF;
tOCp↑.filePageFF ← tOCp↑.filePageFF+1;
END; -- We have gotten and inited a new page.

tOCp↑.indexFF←tOCp↑.indexFF+1;
mtPtr.address↑.numberOfEntries ← mtPtr.address↑.numberOfEntries+1;
mtPtr.state ← dirty;
fP↑.changed ← FALSE;
PutTOCFixedPartInMemory[fP, hPtr];
PutTOCStringInMemory[s, sWords, sLen, sMaxLen, hPtr + SIZE[TOCFixedPart]];
RETURN[ovD.ok];
END; -- of ExtendTOC.


GetTOCFixedPart: PROCEDURE [index: TOCIndex, fP: TOCFixedPartPtr] =
-- Obtains the index’th TOCEntry data portion and copies it into the structure pointed at by
-- fP.
BEGIN
hPtr: HardTOCAddress;
[hPtr, ] ← FindTOCAddress[index];
fP↑ ← LOOPHOLE[hPtr, TOCFixedPartPtr]↑;
END; -- of GetTOCFixedPart.


GetTOCString: PROCEDURE [index: TOCIndex, s: STRING] =
-- Obtains the index’th TOCEntry string and copies as much as will fit into s.
BEGIN
x: CARDINAL = SIZE[CheatersSTRING];
hPtr: HardTOCAddress; s2: STRING; size: CARDINAL;
[hPtr,]←FindTOCAddress[index];
s2←LOOPHOLE[hPtr+SIZE[TOCFixedPart],STRING];
size←MIN[s.maxlength, s2.length];
InlineDefs.COPY[s2+x, StringDefs.WordsForString[size]-x, s+x];
s.length←size;
END; -- of GetTOCString.


PutTOCFixedPart: PROCEDURE [index: TOCIndex, toc: TOCFixedPartPtr] =
-- 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 TRUE and marks the TOC page
-- as having been written.
BEGIN
hPtr: HardTOCAddress;
mtPtr: MemoryTableEntryPtr;
[hPtr, mtPtr]←FindTOCAddress[index];
PutTOCFixedPartInMemory[toc, hPtr];
MarkChange[index, hPtr, mtPtr];
END; -- of PutTOCFixedPart.


PutTOCString: PROCEDURE [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
hPtr: HardTOCAddress;
mtPtr: MemoryTableEntryPtr;
old: STRING; sMaxLen,sLen: CARDINAL;
[hPtr, mtPtr ]← FindTOCAddress[index];
old ← LOOPHOLE[hPtr+ SIZE[TOCFixedPart], STRING];
sMaxLen ← old.maxlength;
sLen ← MIN[s.length, sMaxLen];
PutTOCStringInMemory[s, StringDefs.WordsForString[sMaxLen], sLen, sMaxLen, old];
MarkChange[index, hPtr, mtPtr];
END; -- of PutTOCString.


MarkChange: PRIVATE PROCEDURE [index: TOCIndex, hPtr: HardTOCAddress,
mtPtr: MemoryTableEntryPtr] =
-- House-keep changed records.
BEGIN
LOOPHOLE[hPtr, TOCFixedPartPtr]↑.changed ← TRUE;
tOCp↑.firstChange ← IF tOCp↑.firstChange=0 THEN index
ELSE MIN[tOCp↑.firstChange, index];
mtPtr.state←dirty;
END; -- of MarkChange.


GetFirstFreeTOCIndex: PROCEDURE RETURNS [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
RETURN[IF ~tOCp↑.open THEN 0 ELSE tOCp↑.indexFF];
END; -- of GetFirstFreeTOCIndex.


GetFirstChangedTOCIndex: PROCEDURE 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 ~tOCp↑.open THEN exD.SysBug [];
RETURN[IF tOCp↑.firstChange=0 THEN tOCp↑.indexFF ELSE tOCp↑.firstChange];
END; -- of GetFirstChangedTOCIndex.


PutTOCFixedPartInMemory: PRIVATE PROCEDURE [fP: TOCFixedPartPtr,
hPtr: HardTOCAddress] =
-- Internal proc to stash FixedPart into memory.
BEGIN
fP.bugTrap←bugTrapValue;
InlineDefs.COPY[fP, SIZE[TOCFixedPart], hPtr];
END; -- of PutTOCFixedPartInMemory.


PutTOCStringInMemory: PRIVATE PROCEDURE [s: STRING, sWords, sLen, sMaxLen:
CARDINAL, hPtr: 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
InlineDefs.COPY[s,sWords,hPtr];
SmashStringSizes[LOOPHOLE[hPtr, STRING], sLen, sMaxLen];
END; -- of PutTOCFixedPartInMemory.


SmashStringSizes: PRIVATE PROCEDURE [s: STRING, sLen,sMaxLen: CARDINAL] =
-- BEWARE** Depends upon STRING format. **
-- Sets s.maxlength & s.length.
BEGIN
p: POINTER TO CheatersSTRING←LOOPHOLE[s];
p.length←sLen; p.maxlength←sMaxLen;
END; -- of SmashStringSizes.


GetTOCAddressOnPage: PROCEDURE [index: TOCIndex, mtPtr: MemoryTableEntryPtr]
RETURNS [TOCFixedPartPtr] =
BEGIN
p: TOCFixedPartPtr←LOOPHOLE[mtPtr.address+SIZE[TOCPageHdr]];
first: TOCIndex = tOCp↑.pageTableHeader↑[mtPtr↑.logicalPageNumber];
i: CARDINAL;
IF mtPtr.state=unused THEN exD.SysBug[];
FOR i IN [first..index) DO
p←WordsInTOCEntry[p]+p;
ENDLOOP;
IF p.bugTrap#bugTrapValue THEN exD.SysBug [];
RETURN[p];
END; -- of GetTOCAddressOnPage.


SearchTOCTable: PRIVATE PROCEDURE [index: TOCIndex] RETURNS [page: PageNumber] =
-- Maps a TOCIndex to a TOC file page number.
BEGIN
IF index>=tOCp↑.indexFF THEN exD.SysBug [];
FOR page IN [1..tOCp↑.filePageFF) DO
IF tOCp↑.pageTableHeader↑[page]>index THEN EXIT;
REPEAT
FINISHED => page←tOCp↑.filePageFF;
ENDLOOP;
page ← page - 1;
END; -- of SearchTOCTable.


WordsInTOCEntry: PROCEDURE [hPtr: HardTOCAddress] RETURNS [CARDINAL] = INLINE
BEGIN
fWords: CARDINAL = SIZE[TOCFixedPart];
RETURN[fWords+(LOOPHOLE[hPtr+fWords, STRING].maxlength+1)/2+2];
END; -- of WordsInTOCEntry.


FindTOCAddress: PROCEDURE [index: TOCIndex] RETURNS [p: HardTOCAddress,
mtPtr: MemoryTableEntryPtr] =
-- You’d better be careful, cache activity may cause the pointers returned to become
-- dangling references.
BEGIN
page: PageNumber=SearchTOCTable[index]; -- index is on "page".
[mtPtr,]←GetMtPtr[tOCp, page, active]; -- Maintains page buffers.
RETURN [GetTOCAddressOnPage[index,mtPtr],mtPtr];
END; --of FindTOCAddress.


VirtualizeMessage: PROCEDURE [index: TOCIndex, msg: DisplayMessagePtr]
RETURNS [ovD.ErrorCode] =
-- 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
hPtr: HardTOCAddress; mtPtr: MemoryTableEntryPtr;
tOCPtr: POINTER TO TOC = GetTOCPtr[];
fP: TOCFixedPartPtr; -- What a waste, fighting the type checker.
IF ~tOCPtr↑.open THEN exD.SysBug[];
[hPtr,mtPtr]←FindTOCAddress[index]; -- "SysBugs" errors.
fP←LOOPHOLE[hPtr]; -- Sigh. There must be a better way!
FOR mtPtr←msg↑.memoryHeader, mtPtr.next UNTIL mtPtr=NIL DO
mtPtr.state←unused;
ENDLOOP;
VoidCharCache[@msg.get];
msg↑.file←tOCPtr↑.mailFile;
msg↑.open←TRUE;
msg↑.firstPage←fP.firstPage;
msg↑.firstByte←fP.firstByte+fP.offsetToHeader;
msg↑.textLength←fP.textLength; -- in bytes,
-- Note: I’m supposed to check that the last character of the stamp is a <CR>, but that’s a real
-- pain to do.
RETURN [ovD.ok];
END; --of VirtualizeMessage.


END. -- of VirtTOC