-- Transport Mechanism Filestore - heap object reader --
-- [Juniper]<Grapevine>MS>Reader.mesa
-- Mark Johnson 10-Nov-81 16:59:41 --
-- Randy Gobbel 19-May-81 11:52:23 --
-- Andrew Birrell 24-Feb-81 16:25:59 --
DIRECTORY
BodyDefs USING [ItemHeader, maxRNameLength, RName, RNameSize],
HeapDefs USING [
Buffer, HeapEndRead, HeapReadRName, HeapStartRead, ObjectOffset, objectStart],
HeapFileDefs USING [NextPage],
HeapXDefs USING [PageHeader, ObjectHeader, ReaderData],
Inline USING [COPY, LowHalf],
ObjectDirDefs USING [FreeObject, ObjectNumber, UseObject],
ObjectDirXDefs USING [ObjectBase],
ProtocolDefs USING [endSST, Failed, SendCount],
PupStream USING [StreamClosing],
Stream USING [Handle, PutBlock, SetSST],
VMDefs USING [AllocatePage, Page, PageIndex, pageSize, ReadPage, Release];
Reader: MONITOR LOCKS from USING from: Handle
IMPORTS
BodyDefs, HeapDefs, HeapFileDefs, Inline, ObjectDirDefs, ObjectDirXDefs,
ProtocolDefs, PupStream, Stream, VMDefs
EXPORTS HeapDefs, HeapXDefs =
BEGIN
ReaderData: PUBLIC TYPE = HeapXDefs.ReaderData;
Handle: TYPE = POINTER TO ReaderData;
SubObjectMissing: ERROR = CODE;
ReadingTooFar: ERROR = CODE;
ObjectNotThere: SIGNAL = CODE;
HeapReadData: PUBLIC ENTRY PROCEDURE [from: Handle, to: HeapDefs.Buffer]
RETURNS [end: BOOLEAN, used: CARDINAL] =
BEGIN -- there is always a current page --
WHILE from.stopped DO WAIT from.canStart ENDLOOP;
IF from.end THEN ERROR ReadingTooFar[];
end ← FALSE;
used ← 0;
DO
BEGIN
word: VMDefs.PageIndex ← from.where.word;
givenOffset: HeapDefs.ObjectOffset; --offset specified by disk data--
objectHead: POINTER TO HeapXDefs.ObjectHeader;
endOfSubObject: BOOLEAN ← FALSE; --whether complete sub-object read--
ignored: BOOLEAN ← FALSE; --whether sub-object was ignored--
-- check for offset given by page header --
IF word = FIRST[VMDefs.PageIndex] THEN
BEGIN
pageHead: POINTER TO HeapXDefs.PageHeader =
LOOPHOLE[from.page, POINTER] + word;
word ← word + SIZE[HeapXDefs.PageHeader];
givenOffset ← pageHead.offset;
END
ELSE givenOffset ← HeapDefs.objectStart;
-- read object or sub-object header --
objectHead ← LOOPHOLE[from.page, POINTER] + word;
word ← word + SIZE[HeapXDefs.ObjectHeader];
IF objectHead.number # from.object THEN -- skip past gaps or intervening objects --
BEGIN
IF from.offset = HeapDefs.objectStart THEN SIGNAL ObjectNotThere[];
ignored ← endOfSubObject ← TRUE;
END
ELSE
BEGIN
IF from.offset < givenOffset THEN ERROR SubObjectMissing[];
BEGIN -- read object data --
relocation: CARDINAL = Inline.LowHalf[
MIN[from.offset - givenOffset, objectHead.size]];
available: CARDINAL = objectHead.size - relocation;
amount: CARDINAL = MIN[to.length - used, available];
Inline.COPY[from.page + word + relocation, amount, to.where + used];
used ← used + amount;
from.offset ← from.offset + amount;
-- update disk position only if complete sub-object read --
endOfSubObject ← amount >= available;
END;
END;
-- deal with end of page or of object --
-- Remember that an object never ends on a page boundary --
IF endOfSubObject THEN
BEGIN
from.where.word ← word + objectHead.size;
IF from.where.word + SIZE[HeapXDefs.ObjectHeader] > LAST[VMDefs.PageIndex]
THEN
BEGIN -- move to sub-object on next page --
VMDefs.Release[from.page];
from.where ← HeapFileDefs.NextPage[from.where];
from.page ← VMDefs.ReadPage[
from.where.page, IF from.object.type = body THEN 1 ELSE 0
--lookAhead-- ];
END
ELSE -- ignorable sub-object or end of object --
IF NOT ignored THEN
BEGIN --positioned after object--
from.end ← end ← TRUE;
EXIT
END;
END
ELSE -- check for buffer full --
IF used = to.length THEN EXIT ELSE ERROR;
END;
ENDLOOP;
END;
BadString: ERROR [why: {tooLong, endOfObject}] = CODE;
HeapReadString: PUBLIC PROCEDURE [from: Handle, s: STRING]
RETURNS [ended: BOOLEAN] =
BEGIN
Read: PROCEDURE [base: POINTER, amount: CARDINAL] =
BEGIN
used: CARDINAL;
[ended, used] ← HeapReadData[from, [base, amount]];
IF used # amount THEN ERROR BadString[endOfObject];
END;
temp: STRING = [0];
Read[temp, SIZE[StringBody [0]]];
IF temp.length > s.maxlength THEN ERROR BadString[tooLong];
s.length ← temp.length;
IF s.length > 0 THEN
BEGIN
IF ended THEN ERROR BadString[endOfObject];
Read[@(s.text), SIZE[StringBody [s.length]] - SIZE[StringBody [0]]];
END;
END;
ReadItemHeader: PUBLIC PROCEDURE [from: Handle]
RETURNS [header: BodyDefs.ItemHeader] =
BEGIN [] ← HeapReadData[from, [@header, SIZE[BodyDefs.ItemHeader]]]; END;
ReadRList: PUBLIC PROCEDURE [
from: Handle, work: PROCEDURE [BodyDefs.RName] RETURNS [done: BOOLEAN]] =
BEGIN
length: CARDINAL ← LAST[CARDINAL];
[, ] ← HeapReadData[from, [@length, SIZE[CARDINAL]]];
WHILE length > 0 DO
name: BodyDefs.RName = [BodyDefs.maxRNameLength];
[] ← HeapDefs.HeapReadRName[from, name];
length ← length - BodyDefs.RNameSize[name];
IF work[name] THEN EXIT;
ENDLOOP;
END;
SendComponent: PUBLIC PROCEDURE [from: Handle, str: Stream.Handle] =
BEGIN
length: CARDINAL ← LAST[CARDINAL];
bLength: CARDINAL = 64;
buffer: ARRAY [0..bLength) OF WORD;
bufferAddr: LONG POINTER ← @buffer;
[, ] ← HeapReadData[from, [@length, SIZE[CARDINAL]]];
ProtocolDefs.SendCount[str, length];
WHILE length > 0 DO
used: CARDINAL = HeapReadData[from, [@buffer, MIN[length, bLength]]].used;
length ← length - used;
Stream.PutBlock[
str, [LOOPHOLE[bufferAddr], 0, used * 2], FALSE !
PupStream.StreamClosing => ERROR ProtocolDefs.Failed[communicationError]];
ENDLOOP;
END;
SendObj: PUBLIC PROCEDURE [from: Handle, str: Stream.Handle] =
BEGIN
ended: BOOLEAN ← FALSE;
used: CARDINAL;
buffer: VMDefs.Page = VMDefs.AllocatePage[];
bufferAddr: LONG POINTER ← buffer;
BEGIN
ENABLE
UNWIND => BEGIN VMDefs.Release[buffer]; HeapDefs.HeapEndRead[from]; END;
DO
BEGIN
[ended, used] ← HeapReadData[from, [buffer, VMDefs.pageSize]];
Stream.PutBlock[
str, [LOOPHOLE[bufferAddr], 0, used * 2], FALSE !
PupStream.StreamClosing => ERROR ProtocolDefs.Failed[communicationError]];
IF ended THEN EXIT;
END
ENDLOOP;
END;
VMDefs.Release[buffer];
HeapDefs.HeapEndRead[from];
Stream.SetSST[
str, ProtocolDefs.endSST !
PupStream.StreamClosing => ERROR ProtocolDefs.Failed[communicationError]];
END;
ReaderInfo: ENTRY PROCEDURE [from: Handle]
RETURNS [ObjectDirDefs.ObjectNumber, HeapDefs.ObjectOffset] = INLINE
BEGIN
ObjectDirDefs.UseObject[from.object];
RETURN[from.object, from.offset];
END;
CopyReader: PUBLIC PROCEDURE [from: Handle] RETURNS [new: Handle] =
BEGIN
-- Must not enter Reader monitor, since it calls ReaderAlloc --
object: ObjectDirDefs.ObjectNumber;
offset: HeapDefs.ObjectOffset;
[object, offset] ← ReaderInfo[from]; -- snap-shot of reader --
new ← HeapDefs.HeapStartRead[object];
ObjectDirDefs.FreeObject[object]; -- to compensate for ReaderInfo --
SetReaderOffset[new, offset];
END;
GetReaderOffset: PUBLIC ENTRY PROCEDURE [from: Handle]
RETURNS [HeapDefs.ObjectOffset] = BEGIN RETURN[from.offset]; END;
SetReaderOffset: PUBLIC ENTRY PROCEDURE [
from: Handle, offset: HeapDefs.ObjectOffset] =
BEGIN
WHILE from.stopped DO WAIT from.canStart ENDLOOP;
IF offset >= from.offset THEN from.offset ← offset -- let reader skip by itself --
ELSE {from.offset ← offset; Reset[from]};
END;
GetReaderObject: PUBLIC ENTRY PROCEDURE [from: Handle]
RETURNS [ObjectDirDefs.ObjectNumber] = {RETURN[from.object]};
Reset: INTERNAL PROCEDURE [from: Handle] =
BEGIN
-- re-calculate disk address for current position --
VMDefs.Release[from.page];
from.where ← ObjectDirXDefs.ObjectBase[from.object];
from.end ← FALSE;
BEGIN
-- skip to approximate place efficiently --
excess: HeapDefs.ObjectOffset ← from.offset;
WHILE excess > LAST[VMDefs.PageIndex] DO
from.where ← HeapFileDefs.NextPage[from.where];
excess ← excess - LAST[VMDefs.PageIndex];
ENDLOOP;
END;
from.page ← VMDefs.ReadPage[from.where.page, 0 --lookAhead-- ];
-- reader will skip to correct place all by itself --
END;
StopReader: PUBLIC ENTRY PROCEDURE [from: Handle] =
BEGIN from.stopped ← TRUE; END;
RestartReader: PUBLIC ENTRY PROCEDURE [from: Handle] =
BEGIN
Reset[from]; --to re-calculate disk address--
from.stopped ← FALSE;
BROADCAST from.canStart;
END;
END.