-- VultureOpsImpl.mesa
-- Scavanger for Mail Archive files.
-- Maurice Herlihy August 17, 1982
DIRECTORY
BodyDefs,
IO,
Rope,
Space,
VMDefs,
VultureDefs,
VultureOps;
VultureOpsImpl:
PROGRAM
IMPORTS
IO, Space
EXPORTS VultureOps
= {
-- Public types.
Item:
PUBLIC
TYPE =
LONG
POINTER
TO BodyDefs.ItemHeader;
-- Opaque types
DirStream:
PUBLIC
TYPE =
REF DirStreamRep;
DirStreamRep:
PUBLIC
TYPE =
RECORD[
dirPage:
LONG
POINTER
TO
UNSPECIFIED,
-- first directory page
pages:
INT,
-- number of pages
next:
INT
-- index of next directory
];
ItemStream:
PUBLIC
TYPE =
REF ItemStreamRep;
ItemStreamRep:
PUBLIC
TYPE =
RECORD[
start:
LONG
POINTER
TO
UNSPECIFIED,
-- start of region
size:
INT,
-- size of region
next:
INT
-- offset of next item
];
ROPE:
TYPE = Rope.
ROPE;
postMarkSize:
INT = 6;
-- Suggested by empirical observation.
EndOfStream:
PUBLIC
ERROR =
CODE;
BadFormat:
PUBLIC
ERROR =
CODE;
OpenDirStream: PUBLIC PROC [space: Space.Handle] RETURNS [dirStream: DirStream]
-- Signals EndOfStream --
={
first: INT ← FindFirstMessage[space -- resignal EndOfStream --];
dirStream ← NEW[
DirStreamRep ← [
dirPage: Space.LongPointer[space],
pages: first / VMDefs.pageSize,
next: 0
]
];
};
NextDirectory: PUBLIC PROC [dirStream: DirStream] RETURNS [directory: VultureDefs.Directory]
-- Signals EndOfStream --
= {
pageIndex: INT ← dirStream.next / VultureDefs.entriesPerPage;
entryIndex: INT ← dirStream.next MOD VultureDefs.entriesPerPage;
dirPage: VultureDefs.DirPage ← LOOPHOLE[dirStream.dirPage + pageIndex*VMDefs.pageSize];
IF pageIndex >= dirStream.pages THEN ERROR EndOfStream;
dirStream.next ← dirStream.next+1;
directory ← @dirPage.entries[entryIndex];
};
CloseDirStream:
PUBLIC
PROC [dirStream: DirStream] = {
directory: VultureDefs.Directory;
maxIndex:
INT;
index:
INT;
IF dirStream =
NIL
THEN
RETURN;
-- File is very sick.
maxIndex ← dirStream.pages * VultureDefs.entriesPerPage - 1;
FOR index
IN [dirStream.next .. maxIndex]
DO
directory ← NextDirectory[dirStream];
directory.bodyStart ← 0;
directory.bodyLength ← 0;
ENDLOOP;
};
FindFirstMessage: PROC [space: Space.Handle] RETURNS [first: INT]
-- Signals EndOfStream --
={
itemStream: ItemStream;
-- read offset of first message from directory.
dirPage: VultureDefs.DirPage ← LOOPHOLE[Space.LongPointer[space]];
first ← dirPage.entries[0].bodyStart;
-- if this points to a page boundary, believe it.
IF first > 0 AND first MOD VMDefs.pageSize = 0 THEN RETURN;
-- otherwise try a linear scan from second page.
itemStream ← NEW[ItemStreamRep ← [
start: LOOPHOLE[Space.LongPointer[space]],
size: LONG[Space.GetAttributes[space].size]*LONG[VMDefs.pageSize],
next: 0
]
];
first ← FindPostmark[itemStream -- resignal EndOfStream --];
};
OpenItemStream: PUBLIC PROC [space: Space.Handle] RETURNS [stream: ItemStream]
-- signals EndOfStream --
={
stream ← NEW[
ItemStreamRep ← [
start: LOOPHOLE[Space.LongPointer[space]],
size: LONG[Space.GetAttributes[space].size]*LONG[VMDefs.pageSize],
next: FindFirstMessage[space -- Resignal EndOfStream --]
]
];
};
NextItem: PUBLIC PROC [stream: ItemStream] RETURNS [item: Item] ={
IF stream.next + SIZE[BodyDefs.ItemHeader] >= stream.size
THEN ERROR EndOfStream;
item ← LOOPHOLE[stream.start+stream.next];
IF ~IsItem[item] THEN ERROR BadFormat;
stream.next ← (item.length+1)/2 + SIZE[BodyDefs.ItemHeader] + stream.next;
-- check size for overflow
IF stream.next > stream.size THEN {
stream.next ← stream.size;
ERROR BadFormat;
}
};
Current: PUBLIC PROC [stream: ItemStream] RETURNS [INT] ={
RETURN [stream.next];
};
Reset: PUBLIC PROC [stream: ItemStream, address: INT] = {
IF address > stream.size THEN ERROR EndOfStream;
stream.next ← address;
};
FindPostmark: PUBLIC PROC [stream: ItemStream] RETURNS [INT] = {
stream.next ← stream.next+1;
WHILE stream.next < stream.size - SIZE[BodyDefs.ItemHeader] DO
item: Item ← LOOPHOLE[stream.start+stream.next];
IF item.type = BodyDefs.ItemType[PostMark]
AND item.length = postMarkSize THEN RETURN [stream.next];
stream.next ← stream.next + 1;
ENDLOOP;
ERROR EndOfStream;
};
NoMoreItems: PUBLIC PROC [stream: ItemStream] RETURNS [BOOL] ={
RETURN[stream.next >= stream.size]
};
ParseItem: PUBLIC PROC [item: Item] RETURNS [ROPE] = {
format: ROPE;
SELECT item.type FROM
BodyDefs.ItemType[PostMark] => format ← "PostMark(%g)";
BodyDefs.ItemType[Sender] => format ← "Sender(%g)";
BodyDefs.ItemType[ReturnTo] => format ← "Return To(%g)";
BodyDefs.ItemType[Recipients] => format ← "Recipients(%g)";
BodyDefs.ItemType[Text] => format ← "Text(%g)";
BodyDefs.ItemType[Capability] => format ← "Capability(%g)";
BodyDefs.ItemType[Audio] => format ← "Audio(%g)";
BodyDefs.ItemType[updateItem] => format ← "updateItem(%g)";
BodyDefs.ItemType[reMail] => format ← "reMail(%g)";
BodyDefs.ItemType[LastItem] => format ← "Last Item(%g)";
ENDCASE => format ← "Unknown Item!(%g)";
RETURN [IO.PutFR[format, IO.int[item.length]]];
};
IsItem: PROC [item: Item] RETURNS [BOOL] = {
SELECT item.type FROM
BodyDefs.ItemType[PostMark] => RETURN[TRUE];
BodyDefs.ItemType[Sender] => RETURN[TRUE];
BodyDefs.ItemType[ReturnTo] => RETURN[TRUE];
BodyDefs.ItemType[Recipients] => RETURN[TRUE];
BodyDefs.ItemType[Text] => RETURN[TRUE];
BodyDefs.ItemType[Capability] => RETURN[TRUE];
BodyDefs.ItemType[Audio] => RETURN[TRUE];
BodyDefs.ItemType[updateItem] => RETURN[TRUE];
BodyDefs.ItemType[reMail] => RETURN[TRUE];
BodyDefs.ItemType[LastItem] => RETURN[TRUE];
ENDCASE => RETURN[FALSE];
};
}.