-- File: CoreStreams.mesa -- Edited by Levin: 22-Oct-80 8:46:04 -- Edited by Schroeder: January 13, 1981 11:39 AM -- Edited by Brotz: 28-Oct-80 13:51:44 DIRECTORY ByteBltDefs, crD: FROM "CoreDefs", csD: FROM "CoreStreamDefs", exD: FROM "ExceptionDefs", Inline, ovD: FROM "OverviewDefs", Storage, Stream, tfD: FROM "TempFileDefs"; CoreStreams: PROGRAM IMPORTS ByteBltDefs, crD, exD, Inline, Storage, tfD EXPORTS csD SHARES crD = BEGIN OPEN csD; StreamHandle: TYPE = POINTER TO CStream; CStream: PUBLIC TYPE = RECORD [ fh: crD.UFileHandle, fileType: {csOpened, userOpened, temp}, implicitBuffer: BOOLEAN, type: StreamType, mode: StreamMode, bufferState: {empty, loaded, dirty}, nPages: CARDINAL, buffer: POINTER TO Buffer, filePage: crD.PageNumber, positionByte: CARDINAL, bytesInBuffer: CARDINAL, newEOF: Position, checkPointEOF: Position, checkPointPosition: Position]; Buffer: TYPE = RECORD [ SELECT OVERLAID StreamType FROM byte => [bytes: PACKED ARRAY OF CHARACTER], word => [words: ARRAY OF WORD], ENDCASE]; bytesPerPage: CARDINAL = 512; wordsPerPage: CARDINAL = bytesPerPage/2; Error: PUBLIC ERROR [reason: ovD.ErrorCode] = CODE; OpenFromName: PUBLIC PROCEDURE [ name: STRING, user: crD.DMSUser, type: StreamType, mode: StreamMode, nPages: CARDINAL, buffer: POINTER←NIL] RETURNS [sh: StreamHandle]= BEGIN fh: crD.UFileHandle; fileMode: crD.OpenMode = (IF mode = read THEN read ELSE update); error: ovD.ErrorCode; [error, fh] ← crD.OpenFile[user, name, fileMode]; ReportError[error]; sh ← Open[fh, type, mode, nPages, buffer]; sh.fileType ← csOpened; END; Open: PUBLIC PROCEDURE [ fh: crD.UFileHandle, type: StreamType, mode: StreamMode, nPages: CARDINAL, buffer: POINTER ← NIL] RETURNS [sh: StreamHandle] = BEGIN IF mode#read AND fh#NIL AND fh.access#update THEN Oops[]; sh ← Storage.Node[SIZE[CStream]]; sh↑ ← [fh: NIL, fileType: userOpened, implicitBuffer: FALSE, type: byte, mode: read, bufferState: empty, nPages: 1, buffer: NIL, filePage: 0, positionByte: 0, bytesInBuffer: 0, newEOF: 0, checkPointEOF: 0, checkPointPosition: 0 ]; sh.fh ← fh; sh.implicitBuffer ← buffer=NIL; sh.type ← type; sh.mode ← mode; sh.nPages ← nPages; sh.buffer ← IF sh.implicitBuffer THEN Storage.Pages[nPages] ELSE buffer; IF sh.fh = NIL THEN sh.fileType ← temp ELSE IF mode # overwrite THEN BEGIN eofPage: crD.PageNumber; eofByte: CARDINAL; [ , eofPage, eofByte] ← crD.UFileLength[sh.fh]; sh.newEOF ← sh.checkPointEOF ← MapPageByteToPosition[eofPage, eofByte]; IF mode = append THEN BEGIN sh.checkPointPosition ← sh.checkPointEOF; sh.filePage ← eofPage; sh.positionByte ← eofByte; END; END; END; -- of Open -- Checkpoint: PUBLIC PROCEDURE [sh: StreamHandle] = BEGIN IF sh.mode = read THEN RETURN; UpdateNewEOF[sh]; sh.checkPointEOF ← sh.newEOF; sh.checkPointPosition ← MapPageByteToPosition[sh.filePage, sh.positionByte]; IF sh.checkPointPosition > sh.checkPointEOF THEN Oops[]; IF sh.fh # NIL THEN BEGIN eofPage: crD.PageNumber; eofByte: CARDINAL; WriteBufferIfDirty[sh]; [ , eofPage, eofByte] ← crD.UFileLength[sh.fh]; IF sh.newEOF < MapPageByteToPosition[eofPage, eofByte] THEN BEGIN error: ovD.ErrorCode; [eofPage, eofByte] ← MapPositionToPageByte[sh.newEOF]; error ← crD.UFileTruncate[eofPage, eofByte, sh.fh]; ReportError[error]; END; END; END; Destroy: PUBLIC PROCEDURE [sh: StreamHandle] = BEGIN IF sh.implicitBuffer THEN Storage.FreePages[sh.buffer]; SELECT sh.fileType FROM csOpened => [] ← crD.CloseFile[sh.fh]; temp => IF sh.fh # NIL THEN tfD.FreeTempFile[sh.fh]; ENDCASE; --userOpened-- Storage.Free[sh]; END; Reset: PUBLIC PROCEDURE [sh: StreamHandle] = BEGIN eofPage: crD.PageNumber; eofByte: CARDINAL; sh.newEOF ← sh.checkPointEOF; [eofPage,eofByte] ← MapPositionToPageByte[sh.newEOF]; SELECT eofPage FROM < sh.filePage => -- checkPointEOF is before buffer BEGIN sh.bufferState ← empty; sh.bytesInBuffer ← 0; END; < sh.filePage+sh.nPages => -- checkPointEOF is in buffer BEGIN IF sh.bytesInBuffer # 0 THEN sh.bytesInBuffer ← (eofPage-sh.filePage)*bytesPerPage+eofByte; IF sh.bytesInBuffer = 0 THEN sh.bufferState ← loaded; END; ENDCASE; -- checkPointEOF is beyond buffer Reposition[sh, sh.checkPointPosition]; END; GetType: PUBLIC PROCEDURE [sh: StreamHandle] RETURNS [StreamType] = {RETURN[sh.type]}; Read: PUBLIC PROCEDURE [sh: StreamHandle] RETURNS [item: UNSPECIFIED] = BEGIN IF sh.positionByte = sh.nPages * bytesPerPage OR sh.bufferState = empty THEN PrepareBuffer[sh]; IF sh.positionByte >= sh.bytesInBuffer THEN ReportError[ovD.endOfStream]; IF sh.type = byte THEN BEGIN item ← sh.buffer.bytes[sh.positionByte]; sh.positionByte ← sh.positionByte + 1; END ELSE BEGIN item ← sh.buffer.words[sh.positionByte/2]; sh.positionByte ← sh.positionByte + 2; END; END; Write: PUBLIC PROCEDURE [sh: StreamHandle, item: UNSPECIFIED] = BEGIN IF sh.mode = read THEN Oops[]; IF sh.positionByte = sh.nPages * bytesPerPage OR sh.bufferState = empty THEN PrepareBuffer[sh]; IF sh.type = byte THEN BEGIN sh.buffer.bytes[sh.positionByte] ← item; sh.positionByte ← sh.positionByte + 1; END ELSE BEGIN sh.buffer.words[sh.positionByte/2] ← item; sh.positionByte ← sh.positionByte + 2; END; sh.bytesInBuffer ← MAX[sh.bytesInBuffer, sh.positionByte]; sh.bufferState ← dirty; END; ReadBlock: PUBLIC PROCEDURE [sh: StreamHandle, to: POINTER, start, nItems: CARDINAL] RETURNS [n: CARDINAL] = BEGIN sink, source: Stream.Block; bytesCopied: CARDINAL; sink ← MakeStreamBlock[sh, to, start, nItems]; source.blockPointer ← LONG[sh.buffer]; n ← sink.startIndex; UNTIL sink.startIndex = sink.stopIndexPlusOne DO PrepareBuffer[sh]; source.startIndex ← sh.positionByte; source.stopIndexPlusOne ← sh.bytesInBuffer; IF source.startIndex = source.stopIndexPlusOne THEN EXIT; bytesCopied ← ByteBltDefs.ByteBlt[to: sink, from: source]; sink.startIndex ← sink.startIndex + bytesCopied; sh.positionByte ← sh.positionByte + bytesCopied; ENDLOOP; n ← sink.startIndex - n; IF sh.type = word THEN n ← (n+1)/2; END; -- of ReadBlock -- WriteBlock: PUBLIC PROCEDURE [ sh: StreamHandle, from: POINTER, start, nItems: CARDINAL] = BEGIN IF sh.mode=read THEN Oops[]; CopyBlockToCStream [MakeStreamBlock[sh, from, start, nItems], sh]; END; -- of WriteBlock -- StreamCopy: PUBLIC PROCEDURE [from, to: StreamHandle, fromItems: LONG CARDINAL] = BEGIN Work: PROCEDURE[source: Stream.Block] = {CopyBlockToCStream[source, to]}; IF to.mode=read OR (to.type=word AND from.type=byte AND fromItems MOD 2 = 1) THEN Oops[]; ReadStream[from, fromItems, Work] END; -- of StreamCopy -- ReadStream: PUBLIC PROCEDURE [sh: StreamHandle, nItems: LONG CARDINAL, AcceptBlock: PROCEDURE[Stream.Block]] = BEGIN source: Stream.Block; items: LONG CARDINAL ← IF sh.type=word THEN 2*nItems ELSE nItems; source.blockPointer ← LONG[sh.buffer]; DO copyCount: CARDINAL; PrepareBuffer[sh]; copyCount ← Inline.LowHalf[ MIN[sh.bytesInBuffer-sh.positionByte, items]]; source.startIndex ← sh.positionByte; source.stopIndexPlusOne ← source.startIndex + copyCount; AcceptBlock[source]; IF copyCount = 0 THEN EXIT; sh.positionByte ← sh.positionByte + copyCount; items ← items - copyCount; ENDLOOP; END; GetLength: PUBLIC PROCEDURE [sh: StreamHandle] RETURNS [Position] = BEGIN UpdateNewEOF[sh]; RETURN [IF sh.type = word THEN sh.newEOF/2 ELSE sh.newEOF]; END; -- of GetLength -- GetPosition: PUBLIC PROCEDURE [sh: StreamHandle] RETURNS [Position] = {RETURN[IF sh.type = byte THEN MapPageByteToPosition[sh.filePage, sh.positionByte] ELSE MapPageWordToPosition[sh.filePage, sh.positionByte/2]]}; SetPosition: PUBLIC PROCEDURE [sh: StreamHandle, position: Position] = BEGIN bytePosition: Position = (IF sh.type = word THEN position*2 ELSE position); IF sh.mode = read AND bytePosition > sh.newEOF THEN Oops[]; Reposition[sh, bytePosition]; END; MapPageWordToPosition: PUBLIC PROCEDURE [page: CARDINAL, word: CARDINAL] RETURNS [Position] = {RETURN[LONG[page] * wordsPerPage + word]}; MapPositionToPageWord: PUBLIC PROCEDURE [position: Position] RETURNS [page: CARDINAL, word: [0..wordsPerPage)] = {[page, word] ← Inline.LongDivMod[position, wordsPerPage]}; MapPageByteToPosition: PUBLIC PROCEDURE [page: CARDINAL, byte: CARDINAL] RETURNS [Position] = {RETURN[LONG[page] * bytesPerPage + byte]}; MapPositionToPageByte: PUBLIC PROCEDURE [position: Position] RETURNS [page: CARDINAL, byte: [0..bytesPerPage)] = {[page, byte] ← Inline.LongDivMod[position, bytesPerPage]}; -- internal procedures Reposition: PROCEDURE [sh: StreamHandle, bp: Position] = BEGIN page: crD.PageNumber; byte: CARDINAL; [page, byte] ← MapPositionToPageByte[bp]; IF page IN [sh.filePage .. sh.filePage+sh.nPages) THEN sh.positionByte ← (page - sh.filePage)*bytesPerPage + byte ELSE BEGIN UpdateNewEOF[sh]; WriteBufferIfDirty[sh]; sh.filePage ← page; sh.positionByte ← byte; sh.bufferState ← empty; sh.bytesInBuffer ← 0; END; END; MakeStreamBlock: PROCEDURE [sh: StreamHandle, p: POINTER, s, n: CARDINAL] RETURNS[b: Stream.Block] = BEGIN b.blockPointer ← LONG[p]; b.startIndex ← (IF sh.type = byte THEN s ELSE s*2); b.stopIndexPlusOne ← b.startIndex + (IF sh.type = byte THEN n ELSE n*2); END; CopyBlockToCStream: PROCEDURE [source: Stream.Block, sh: StreamHandle] = BEGIN sink: Stream.Block; bytesCopied: CARDINAL; sink.blockPointer ← LONG[sh.buffer]; UNTIL source.startIndex = source.stopIndexPlusOne DO PrepareBuffer[sh]; sink.startIndex ← sh.positionByte; sink.stopIndexPlusOne ← sh.nPages * bytesPerPage; bytesCopied ← ByteBltDefs.ByteBlt[to: sink, from: source]; source.startIndex ← source.startIndex + bytesCopied; sh.positionByte ← sh.positionByte + bytesCopied; sh.bytesInBuffer ← MAX[sh.bytesInBuffer, sh.positionByte]; sh.bufferState ← dirty; ENDLOOP; END; PrepareBuffer: PROCEDURE [sh: StreamHandle] = BEGIN IF sh.positionByte = sh.nPages * bytesPerPage THEN BEGIN UpdateNewEOF[sh]; WriteBufferIfDirty[sh]; sh.filePage ← sh.filePage + sh.nPages; sh.positionByte ← 0; sh.bufferState ← empty END; IF sh.bufferState = empty THEN BEGIN bufferPosition: LONG CARDINAL = MapPageByteToPosition[sh.filePage, 0]; bufferBytes: CARDINAL = sh.nPages * bytesPerPage; sh.bytesInBuffer ← Inline.LowHalf [MIN[bufferBytes, MAX[sh.newEOF, bufferPosition] - bufferPosition]]; IF sh.bytesInBuffer > 0 THEN BEGIN error: ovD.ErrorCode; [error, ] ← crD.ReadPages[sh.buffer, bufferBytes, sh.filePage, sh.fh]; ReportError[error]; END; sh.bufferState ← loaded; END; END; WriteBufferIfDirty: PROCEDURE [sh: StreamHandle] = BEGIN IF sh.bufferState = dirty THEN BEGIN error: ovD.ErrorCode; IF sh.fh = NIL THEN sh.fh ← tfD.AllocateTempFile[]; error ← crD.WritePages[sh.buffer, sh.bytesInBuffer, sh.filePage, sh.fh]; ReportError[error]; sh.bufferState ← loaded; END; END; -- of WriteBufferIfDirty -- UpdateNewEOF: PROCEDURE [sh: StreamHandle] = BEGIN IF sh.bufferState = dirty THEN sh.newEOF ← MAX[sh.newEOF, MapPageByteToPosition[ sh.filePage, sh.bytesInBuffer]]; END; -- of UpdateNewEOF -- ReportError: PROCEDURE [error: ovD.ErrorCode] = {IF error # ovD.ok THEN ERROR Error[error]}; Oops: PROCEDURE = {exD.SysBug[]}; END. -- of CoreStreams --