-- HeapPrint.mesa

-- Andrew Birrell August 12, 1982 1:16 pm

DIRECTORY
Directory,
File,
HeapXDefs,
ObjectDirDefs,
IO,
Rope		USING[ ROPE ],
Space;

HeapPrint: PROGRAM
IMPORTS Directory, File, IO, Space
SHARES ObjectDirDefs =

BEGIN

-- Heap.chain --

segmentCount: CARDINAL = 200; -- number of segments in message body file --
Segment:      TYPE = [0..segmentCount);
SegmentPtr:   TYPE = [ FIRST[Segment] .. 1+LAST[Segment] ];

noSegment:    SegmentPtr = LAST[SegmentPtr];

-- the 'free' and 'written' chains are stored permanently on a disk page--
PageType: TYPE = RECORD[written: ARRAY[0..0]OF SegmentPtr --for alignment--,
                        free:    ARRAY[0..0]OF SegmentPtr --for alignment--,
                        ser:     LONG INTEGER,
                        next:    ARRAY Segment OF SegmentPtr];

ChainPage: TYPE = LONG POINTER TO PageType;

chain: ChainPage;
segSize: CARDINAL;
fileSize: INT;

GetChain: PROC =
  BEGIN
  file: File.Capability = Directory.LookupUnlimited["Heap.chain"];
  space: Space.Handle = Space.Create[2, Space.virtualMemory];
  chain0: ChainPage = Space.LongPointer[space];
  chain1: ChainPage = Space.LongPointer[space] + 256;
  Space.Map[space, [file,1]];
  chain ← IF chain0.ser > chain1.ser THEN chain0 ELSE chain1;
  segSize ← fileSize / segmentCount;
  END;

First: PROC RETURNS[first: CARDINAL] =
  -- returns file-relative page number of first written page --
  { first ← chain.written[0] * segSize; out.PutF["\n%g: ", [cardinal[first]]] };

NoMorePages: ERROR = CODE;

Next: PROC[prev: CARDINAL] RETURNS[ next: CARDINAL ] =
  BEGIN
  IF prev MOD segSize < segSize-1
  THEN { next ← prev+1; out.PutChar[IO.SP] }
  ELSE BEGIN
       seg: SegmentPtr = chain.next[prev / segSize];
       IF seg = noSegment
       THEN ERROR NoMorePages[]
       ELSE BEGIN
            next ← seg * segSize;
            out.PutF["\n%g: ", [cardinal[next]]];
            END;
       END;
  END;


-- Heap.data --

briefType: ARRAY ObjectDirDefs.ObjectType OF Rope.ROPE = [
  gap: ".",
  body: "B",
  SLinput: "S",
  SLpending: "S",
  SLforward: "S",
  RSobject: "R",
  RSmail: "RSmail",
  temp: "temp",
  RSname: "RSname",
  MSname : "MSname",
  testMode: "test",
  TOC: "T",
  archived: "A",
  delArch: "spare15",
  spare16: "spare16",
  spare17: "spare17" ];

fullType: ARRAY ObjectDirDefs.ObjectType OF Rope.ROPE = [
  gap: "gap",
  body: "body",
  SLinput: "SLinput",
  SLpending: "SLpending",
  SLforward: "SLforward",
  RSobject: "RSobject",
  RSmail: "RSmail",
  temp: "temp",
  RSname: "RSname",
  MSname : "MSname",
  testMode: "testMode",
  TOC: "TOC",
  archived: "archived",
  delArch: "spare15",
  spare16: "spare16",
  spare17: "spare17" ];

in, out, textOut: IO.STREAM ← NIL;

gap: ObjectDirDefs.ObjectNumber ← ObjectDirDefs.noObject;

interesting: ObjectDirDefs.ObjectNumber ← -- object to watch for --
  [page: 1, fill: 0, type: SLforward, index: 36];

wanted: INT ← 500; -- number of characters to print --

nextOffset: LONG CARDINAL ← 0; -- next offset to expect in current object --

current: ObjectDirDefs.ObjectNumber;

pauseAtPage: CARDINAL ← LAST[CARDINAL]; -- wait for CR to be typed at that time --

watchPage: REF PACKED ARRAY NAT OF BOOL =
    NEW[PACKED ARRAY NAT OF BOOL ← ALL[FALSE]]; -- pages to print --

file: File.Capability = Directory.LookupUnlimited["Heap.data"];
space: Space.Handle = Space.Create[1, Space.virtualMemory];
header: LONG POINTER TO HeapXDefs.PageHeader = Space.LongPointer[space];
pageWords: LONG POINTER TO ARRAY [0..256) OF WORD = LOOPHOLE[header];
pageChars: LONG POINTER TO PACKED ARRAY [0..512) OF CHAR = LOOPHOLE[header];

Init: PROC =
  BEGIN
  fileSize ← File.GetSize[file];
  GetChain[];
  Space.Map[space];
  [in: in, out: out] ← IO.CreateTTYStreams["Heap Summary"];
  out.PutRope["For start of new object: \"B\" = body, \"S\" = steering-list, \"R\" = RSobject, \"T\" = TOC, \"A\" = archive, \"$\" = illegal object number;\n\"-\" = continuation sub-object;\n\".\" = gap sub-object;\n\"g\" = imbedded garbage;\n\"*\" = sub-object also recorded in \"Object contents\" viewer;\n<sp> between pages;\n<cr> at segment boundary.\n\n"];
  END;
  
Main: PROC =
  BEGIN
  current ← gap;
  FOR i: CARDINAL ← First[], Next[i ! NoMorePages => CONTINUE]
  DO PrintPage[i, watchPage[i]] ENDLOOP;
  IF current # gap THEN out.PutRope["\nUnterminated object!"];
  END;

PrintPage: PROC[i: CARDINAL, all: BOOL] =
  BEGIN
  word: [0..256] ← SIZE[HeapXDefs.PageHeader];
  Space.CopyIn[space, [file, i+1]];
  IF i = pauseAtPage
  THEN { out.PutRope["\nType any character to continue:"]; [] ← in.GetChar[] };
  DO -- for each sub-object --
     offset: LONG CARDINAL =
               IF word # SIZE[HeapXDefs.PageHeader] THEN 0 ELSE header.offset;
     objH: LONG POINTER TO HeapXDefs.ObjectHeader = LOOPHOLE[header + word];
     objChars: LONG POINTER TO PACKED ARRAY [0..512) OF CHAR =
                              LOOPHOLE[objH + SIZE[HeapXDefs.ObjectHeader]];
     TypeObj: PROC[remark: Rope.ROPE] =
       BEGIN
       IF textOut = NIL
       THEN BEGIN
            [out: textOut] ← IO.CreateTTYStreams["Object contents"];
            textOut.PutRope["Object:  offset:  size:  heap-page:  heap-word:  remarks:\n"];
            END;
       textOut.PutF[
                "\n[p:%g,i:%g,%g]",
                [cardinal[objH.number.page]],
                [cardinal[objH.number.index]],
                [rope[fullType[objH.number.type]]] ];
       textOut.PutF[", o:%bB, s:%bB;  h:%g, w:%g, %g",
                [cardinal[offset]],
                [cardinal[objH.size]],
                [cardinal[i]],
                [cardinal[word]],
                [rope[remark]] ];
       IF objH.number = interesting
       THEN BEGIN
            IF wanted > 0
            THEN BEGIN
                 textOut.PutChar['\n];
                 FOR i: INT IN [0..objH.size*2)
                 DO textOut.PutChar[objChars[i]]; wanted ← wanted-1 ENDLOOP;
                 END;
            END;
       out.PutChar['*];
       END;
     SELECT TRUE FROM
       objH.number.page>256 OR objH.number.index>85 OR objH.number.fill#0 =>
         { out.PutChar['$]; TypeObj["illegal"] };
       objH.number = gap =>
         out.PutChar['.];
       objH.number = current =>
         out.PutChar['-];
       current # gap =>
         out.PutChar['g];
     ENDCASE =>
       { current ← objH.number; nextOffset ← 0;
         out.PutRope[briefType[objH.number.type]] };
     IF all THEN TypeObj["watchPage"];
     IF current # gap AND objH.number = current
     THEN SELECT TRUE FROM
            offset = nextOffset =>
              { IF objH.number = interesting THEN TypeObj["ok"];
                nextOffset ← offset + objH.size };
            offset < nextOffset AND nextOffset < offset + objH.size =>
              { IF objH.number = interesting THEN TypeObj["overlap"];
                nextOffset ← offset + objH.size };
            offset < nextOffset =>
              { IF objH.number = interesting THEN TypeObj["redundant"] };
            offset > nextOffset =>
              { TypeObj["****gap****"];
                nextOffset ← offset + objH.size };
          ENDCASE => ERROR;
     word ← word + SIZE[HeapXDefs.ObjectHeader];
     word ← word + objH.size;
     IF word + SIZE[HeapXDefs.ObjectHeader] > 255 THEN EXIT;
     IF objH.number = current THEN current ← gap;
  ENDLOOP;
  END;

Init[];

{ dummy: PROCESS = FORK Main[]; };

END.