-- Copyright (C) 1981, 1984, 1985 by Xerox Corporation. All rights reserved. -- Reader.mesa, Transport Mechanism Filestore - heap object reader -- -- HGM, 15-Sep-85 12:43:45 -- 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 [LongCOPY, 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 = LONG 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: LONG 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: LONG POINTER TO HeapXDefs.PageHeader = LOOPHOLE[from.page, LONG 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, LONG 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.LongCOPY[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: LONG STRING] RETURNS [ended: BOOLEAN] = BEGIN Read: PROCEDURE [base: LONG 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.