-- 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 --