<> <> <> <> <> <> DIRECTORY Basics USING [BITAND, BITOR, BITSHIFT], FS USING [Error, StreamOpen], GVPDefs, IO USING [Close, GetChar, GetLength, PutChar, PutF, PutFR, PutText, SetIndex, STREAM, UnsafeGetBlock, UnsafePutBlock], ObjectDirDefs USING [noObject, ObjectNumber, ObjectType]; GVPProcs: CEDAR PROGRAM IMPORTS Basics, FS, GVPDefs, IO EXPORTS GVPDefs SHARES ObjectDirDefs = BEGIN OPEN GVPDefs; <> <> <> <> <> <> gap: ObjectDirDefs.ObjectNumber _ ObjectDirDefs.noObject; currentObj: ObjectDirDefs.ObjectNumber; pageBuffer: REF PageByteVec _ NIL; maxSegments: CARDINAL; SegmentPtr: TYPE = CARDINAL; noSegment: SegmentPtr; externalNoSegment: CARDINAL = LAST[CARDINAL]; emptyCell: CARDINAL = LAST[CARDINAL]; <> <> <> <> <> <> <> CFHdrSize: CARDINAL = SIZE[LONG INTEGER] + SIZE[SegmentPtr]; ChainFilePage: TYPE = MACHINE DEPENDENT RECORD [ serialNumber(0): LONG INTEGER, chainHead(2): SegmentPtr, next(3): ARRAY [CFHdrSize..wordsPerPage) OF CARDINAL]; -- 3..256 ChainPage: TYPE = REF ChainFilePage; ChainSeq: TYPE = RECORD [SEQUENCE length: CARDINAL OF ChainPage]; <> <> <> <> <> MapSeq: TYPE = RECORD [SEQUENCE length: CARDINAL OF MapEntry]; map: REF MapSeq _ NIL; mapChanged: BOOLEAN _ FALSE; structureOK: BOOLEAN _ FALSE; <> <> <> <> <> LList, PList: REF WordSeq _ NIL; <> <> <> bitMapSeq: TYPE = RECORD[SEQUENCE length: CARDINAL OF WORD]; bitMap: REF bitMapSeq _ NIL; SetBitFromObj: PROC[obj: LONG POINTER TO ObjectHeader] RETURNS [bit: BOOLEAN] = TRUSTED BEGIN bitIndex: INT = obj.number.page * maxObjNumIndex + obj.number.index; bitWord: INT = bitIndex / bitsPerWord; bitPos: INT = bitIndex MOD bitsPerWord; bitMask: WORD = Basics.BITSHIFT[1, bitPos]; bit _ Basics.BITAND[bitMap[bitWord], bitMask]#0; bitMap[bitWord] _ Basics.BITOR[bitMap[bitWord], bitMask] END; <> <> <> <> LookAtThis: SIGNAL = CODE; GetChain: PROC[h: GVPRef] RETURNS[r: ROPE] = BEGIN i, j, prevJ: CARDINAL; chainStream, mapStream: IO.STREAM _ NIL; chainSize: CARDINAL; chainPages: REF ChainSeq; chainStream _ FS.StreamOpen [fileName: "heap.segments", accessOptions: read ! FS.Error => BEGIN r _ "Unable to open heap.segments"; GOTO fail END]; chainSize _ (chainStream.GetLength[] / bytesPerPage) / 2; -- pairs of pages chainPages _ NEW[ChainSeq[chainSize]]; mapStream _ FS.StreamOpen [fileName: "heap.map", accessOptions: read ! FS.Error => BEGIN r _ "Unable to open heap.map"; chainStream.Close[]; GOTO fail END]; h.heapStream _ FS.StreamOpen[fileName: "heap.data", accessOptions: write ! FS.Error => BEGIN r _ "Unable to open heap.data"; chainStream.Close[]; mapStream.Close[]; GOTO fail END]; maxSegments _ (h.heapStream.GetLength[] / pageByteSize) / segSize; noSegment _ maxSegments; LList _ NEW[WordSeq[maxSegments + 1]]; PList _ NEW[WordSeq[maxSegments + 1]]; map _ NEW[MapSeq[maxSegments*segSize]]; FOR i: CARDINAL IN [0..maxSegments] DO PList[i] _ emptyCell; LList[i] _ emptyCell; ENDLOOP; FOR i: CARDINAL IN [0.. maxSegments*segSize) DO map[i] _ mapStream.GetChar[] ENDLOOP; mapChanged _ FALSE; mapStream.Close[]; chainStream.SetIndex[0]; <> <> <> <> <> <> <> FOR i: CARDINAL IN [0..chainSize) -- copy chain file pages to my array DO chain0: ChainPage = NEW[ChainFilePage]; chain1: ChainPage = NEW[ChainFilePage]; TRUSTED { [] _ chainStream.UnsafeGetBlock[[LOOPHOLE[chain0], 0, bytesPerPage]]; [] _ chainStream.UnsafeGetBlock[[LOOPHOLE[chain1], 0, bytesPerPage]]; }; IF chain0.serialNumber > chain1.serialNumber THEN chainPages[i] _ chain0 ELSE chainPages[i] _ chain1; ENDLOOP; chainStream.Close[]; <> <> <<[515..767] etc. The entries form a list whose head is the chainHead field of the first page >> <> <> <> <> <> j _ chainPages[0].chainHead; -- the start of the list i _ 0; -- i counts logical segments DO chainJ: CARDINAL _ IF j = externalNoSegment -- end of list? THEN noSegment ELSE j - CFHdrSize * (j / wordsPerPage + 1); IF PList[chainJ] # emptyCell THEN SIGNAL LookAtThis; LList[i] _ chainJ; PList[chainJ] _ i; IF chainJ = noSegment THEN EXIT;-- note that PList[noSegment] = number of active segments i _ i + 1; prevJ _ j; j _ chainPages[j / wordsPerPage].next[j MOD wordsPerPage]; ENDLOOP; structureOK _ TRUE; FOR i: CARDINAL IN [0..chainSize) DO chainPages[i] _ NIL; ENDLOOP; ResetCurrent[h]; EXITS fail => NULL END; -- a mother of a proc! <> <> <> <> SetLogical: PUBLIC PROC[h: GVPRef, n: CARDINAL] RETURNS[ok: BOOLEAN _ FALSE] = BEGIN seg: CARDINAL = n/segSize; IF NOT seg IN [0..noSegment) THEN RETURN; IF NOT LList[seg] IN [0..noSegment) THEN RETURN; h.lPage _ n; h.pPage _ LList[seg] * segSize + n MOD segSize; ok _ TRUE END; SetPhysical: PUBLIC PROC[h: GVPRef, n: CARDINAL] RETURNS[ok: BOOLEAN _ FALSE] = BEGIN seg: CARDINAL = n/segSize; IF NOT seg IN [0..noSegment) THEN RETURN; IF NOT PList[seg] IN [0..noSegment) THEN RETURN; h.pPage _ n; h.lPage _ PList[seg] * segSize + n MOD segSize; ok _ TRUE END; <> <> <> <> NextPage: PUBLIC PROC[h: GVPRef] RETURNS[ok: BOOLEAN _ FALSE] = BEGIN IF h.lPage MOD segSize < segSize-1 THEN { h.lPage _ h.lPage+1; h.pPage _ h.pPage+1} ELSE BEGIN pSeg: SegmentPtr _ LList[h.lPage/segSize + 1]; -- new physical segment IF pSeg = noSegment THEN RETURN ELSE BEGIN h.pPage _ pSeg * segSize; h.lPage _ h.lPage+1; END; END; ok _ TRUE END; PrevPage: PUBLIC PROC[h: GVPRef] RETURNS[ok: BOOLEAN _ FALSE]= BEGIN IF h.lPage MOD segSize > 0 THEN { h.lPage _ h.lPage-1; h.pPage _ h.pPage-1 } ELSE BEGIN IF h.lPage = 0 THEN RETURN ELSE BEGIN h.lPage _ h.lPage-1; h.pPage _ LList[h.lPage/segSize] * segSize + segSize - 1 END END; ok _ TRUE END; ResetCurrent: PROC[h: GVPRef] = BEGIN h.lPage _ 0; h.pPage _ LList[h.lPage/segSize] * segSize END; <> PositionRope: PUBLIC PROC[h: GVPRef] RETURNS[r: ROPE] = BEGIN r _ IO.PutFR["position %d, page %d", [cardinal[h.lPage]], [cardinal[h.pPage]]] END; <> <> <> <> ReadHeapPage: PUBLIC PROC[h: GVPRef, page: CARDINAL, pageBuffer: REF PageByteVec] RETURNS [r: ROPE _ NIL] = BEGIN IF GetPageState[page] = emptyPage THEN r _ ReadServerPage[h, page, pageBuffer] ELSE r _ ReadLocalPage[h, page, pageBuffer] END; ReadLocalPage: PROC [h: GVPRef, page: CARDINAL, pageBuffer: REF PageByteVec] RETURNS [r: ROPE _ NIL] = BEGIN h.heapStream.SetIndex[LONG[page] * bytesPerPage]; TRUSTED { [] _ h.heapStream.UnsafeGetBlock[[LOOPHOLE[pageBuffer], 0, bytesPerPage]]; }; END; WriteLocalPage: PUBLIC PROC [h: GVPRef, page: CARDINAL, pageBuffer: REF PageByteVec] RETURNS [r: ROPE _ NIL] = BEGIN h.heapStream.SetIndex[LONG[page] * bytesPerPage]; TRUSTED { h.heapStream.UnsafePutBlock[[LOOPHOLE[pageBuffer], 0, bytesPerPage]]; }; SetPageState[page, alteredPage] END; <> <> <> WriteHeap: PUBLIC PROC [h: GVPRef] RETURNS[r: ROPE _ NIL] = BEGIN numSent: CARDINAL _ 0; r _ CheckStructure[h]; IF r#NIL THEN RETURN; FOR i: CARDINAL IN [0..map.length) DO IF GetPageState[i]#alteredPage THEN LOOP; r _ ReadLocalPage[h, i, pageBuffer]; IF r#NIL THEN RETURN; r _ WriteServerPage[h, i, pageBuffer]; IF r#NIL THEN RETURN; SetPageState[i, fullPage]; numSent _ numSent +1; ENDLOOP; IF numSent=0 THEN Flash["No pages written (none changed)"] ELSE h.logStream.PutF["\n%lHeap written%l, %d pages updated\n", [rope["b"]], [rope[" "]], [cardinal[numSent]]] END; <> <> <> <> <> <> ScanHeap: PUBLIC PROC [h: GVPRef, onlyError: BOOLEAN] RETURNS[r: ROPE _ NIL] = TRUSTED BEGIN out: IO.STREAM = h.browserStream; nextOffset: LONG CARDINAL _ 0; word: CARDINAL; page, index, type: CARDINAL; firstTime: BOOLEAN _ TRUE; searchValid: BOOLEAN; header: LONG POINTER TO PageHeader = LOOPHOLE[pageBuffer]; ReportError: PROC [r: ROPE] = TRUSTED BEGIN out.PutF["ERROR: page %4d, word %3d, %g\n", [cardinal[h.pPage]], [cardinal[word]], [rope[r]] ] END; ReportNote: PROC[r: ROPE] = TRUSTED BEGIN out.PutF["NOTE: page %4d, word %3d, %g\n", [cardinal[h.pPage]], [cardinal[word]], [rope[r]] ] END; FOR i: CARDINAL IN [0..bitMap.length) DO bitMap[i] _ 0 ENDLOOP; IF NOT onlyError THEN BEGIN [page, index, type] _ GetSearchPattern[]; IF type#lastCard AND NOT type IN [0..15] THEN {r _ "Bad type string"; RETURN} END; out.PutText["\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"]; out.PutF["Scan started at %g\n\n", [rope[PositionRope[h]]]]; searchValid _ NOT onlyError AND (page#lastCard OR index#lastCard OR type#lastCard); currentObj _ gap; DO -- for each page byte: INT = LONG[h.pPage] * bytesPerPage; h.heapStream.SetIndex[byte]; TRUSTED { [] _ h.heapStream.UnsafeGetBlock[[LOOPHOLE[pageBuffer], 0, bytesPerPage]]; }; word _ pageHdrSize; IF firstTime THEN BEGIN firstTime _ FALSE; IF h.lPage=0 THEN nextOffset _ 0 ELSE nextOffset _ header.offset; END; DO -- for each sub-object offset: LONG CARDINAL = IF word # pageHdrSize THEN 0 ELSE header.offset; objH: LONG POINTER TO ObjectHeader = LOOPHOLE[header, LONG POINTER] + word; IF searchValid THEN IF (objH.number.page=page OR page=lastCard) AND (objH.number.index=index OR index=lastCard) AND (LOOPHOLE[objH.number.type, CARDINAL]=type OR type=lastCard) THEN out.PutF ["SEARCH: page %4d, word %3d, found [p: %3bB, x: %3bB, t: %g]\n", [cardinal[h.pPage]], [cardinal[word]], [cardinal[objH.number.page]], [cardinal[objH.number.index]], [rope[ObjectRope[LOOPHOLE[objH.number.type, CARDINAL], FALSE]]]]; SELECT TRUE FROM objH.number.page>maxObjNumPage OR objH.number.index>maxObjNumIndex OR objH.number.fill#0 => ReportError["Illegal object number"]; objH.number = gap => NULL; objH.number = currentObj => NULL; currentObj # gap => ReportError["Garbage found"]; ENDCASE => NULL; IF offset = 0 THEN BEGIN currentObj _ objH.number; nextOffset _ 0; IF objH.number#gap AND SetBitFromObj[objH] THEN ReportNote[IO.PutFR["duplicate object p:%3bB x:%3bB", [cardinal[objH.number.page]], [cardinal[objH.number.index]]]] END; IF currentObj # gap AND objH.number = currentObj THEN SELECT TRUE FROM offset = nextOffset => nextOffset _ offset + objH.size; offset < nextOffset AND nextOffset < offset + objH.size => {nextOffset _ offset + objH.size; ReportNote["Overlap"]}; offset < nextOffset => {nextOffset _ offset + objH.size; ReportNote["redundant"]}; offset > nextOffset => {nextOffset _ offset + objH.size; ReportError["Gap"]}; ENDCASE => ERROR; word _ word + objHdrSize + objH.size; IF word + objHdrSize > 255 THEN EXIT; IF objH.number = currentObj THEN currentObj _ gap; ENDLOOP; IF h.lPage MOD 10 = 0 THEN Set[IO.PutFR["Scanning position %d of %d", [cardinal[h.lPage]], [cardinal[PList[maxSegments]*segSize]]]]; IF StopRequested[] THEN GOTO quit; IF NOT NextPage[h] THEN GOTO quit ENDLOOP EXITS quit => h.browserStream.PutF["\nScan stopped at %g\n", [rope[PositionRope[h]]]] END; <> <> GetPageState: PUBLIC PROC[page: CARDINAL] RETURNS [state: MapEntry] = {state _ map[page]}; SetPageState: PUBLIC PROC[page: CARDINAL, state: MapEntry] = BEGIN mapChanged _ TRUE; map[page] _ state END; <> <> CheckStructure: PUBLIC PROC[h: GVPRef] RETURNS [r: ROPE _ NIL] = BEGIN IF NOT structureOK THEN BEGIN Set["Initialising data structure"]; r _ GetChain[h]; END; Set[] END; <> <> <> <> ProcsInit: PUBLIC PROC = BEGIN structureOK _ FALSE; pageBuffer _ NEW[PageByteVec]; bitMap _ NEW[bitMapSeq[(maxObjNumPage*maxObjNumIndex) / bitsPerWord]] END; ProcsTidyUp: PUBLIC PROC = BEGIN h: GVPRef = GetHandle[]; IF map#NIL AND mapChanged THEN BEGIN mapStream: IO.STREAM = FS.StreamOpen[fileName: "heap.map", accessOptions: write]; FOR i: CARDINAL IN [0..map.length) DO mapStream.PutChar[map[i]] ENDLOOP; mapStream.Close[]; map _ NIL END; IF h.heapStream#NIL THEN {h.heapStream.Close[]; h.heapStream _ NIL}; pageBuffer _ NIL; bitMap _ NIL; LList _ NIL; PList _ NIL END; END.