FSFileStreamImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Schroeder, December 15, 1983 4:26 pm
Levin, September 22, 1983 1:34 pm
Russ Atkinson, November 7, 1984 11:17:51 am PST
Russ Atkinson (RRA) May 10, 1985 5:09:12 pm PDT
Hal Murray, February 18, 1986 7:22:51 pm PST
DIRECTORY
BasicTime USING [GMT],
File USING [Error, Handle, Read, wordsPerPage, Write],
FS USING [Error, PagesForBytes],
FSFileOps USING [FileStreamMode, GetFileInfo, GetProps, SetFilePages],
FSReport USING [FileError],
IO USING [CreateStream, CreateStreamProcs, EndOfStream, STREAM, StreamProcs],
PrincOpsUtils USING [ByteBlt],
VM USING [AddressForPageNumber, Free, Interval, PageNumberForAddress, PageCount, PagesForBytes, SimpleAllocate, SwapIn];
FSFileStreamImpl: CEDAR MONITOR
IMPORTS File, FS, FSFileOps, FSReport, IO, PrincOpsUtils, VM
EXPORTS FSFileOps
= {
GMT: TYPE = BasicTime.GMT;
STREAM: TYPE = IO.STREAM;
Exported to FSFileOps
CreateFileStream: PUBLIC PROC [file: File.Handle, mode: FSFileOps.FileStreamMode ] RETURNS [STREAM] = {
Produces a stream on the indicated file.
s: REF StreamObject;
lastIndex: INT = (IF mode = oldReadOnly THEN FSFileOps.GetProps[file].bytes ELSE bytesPerFilePage*FSFileOps.GetFileInfo[file].pages);
s ← CreateStreamObject[file, mode, lastIndex];
AllocateBuffer[s];
RETURN [IO.CreateStream[IF mode=oldReadOnly THEN readProcs ELSE appendProcs, s]];
};
Copy: PUBLIC PROC [from, to: File.Handle] RETURNS [bytes: INT, createdTime: GMT] = {
The contents of the "from" file are transfered to the "to" file.
fromS, toS: REF StreamObject;
toS ← CreateStreamObject[to, newAppendOnly, bytesPerFilePage*FSFileOps.GetFileInfo[to].pages];
[bytes: bytes, created: createdTime] ← FSFileOps.GetProps[from];
fromS ← CreateStreamObject[from, oldReadOnly, bytes];
AllocateBuffer[fromS];
toS.buffer ← fromS.buffer; -- for faster copying
{ ENABLE FS.Error => FreeBuffer[fromS];
UNTIL fromS.index = fromS.lastFileIndexPlusOne DO
LoadBuffer[fromS];
toS.index ← fromS.index ← fromS.lastBufferIndexPlusOne;
StoreBuffer[toS];
ENDLOOP;
};
FreeBuffer[fromS];
};
Stream procs
Close: PROC [self: STREAM, abort: BOOL] = {
s: REF StreamObject = NARROW [self.streamData];
IF self.streamProcs = NIL THEN RETURN;
IF self.streamProcs = appendProcs AND s.index # 0
THEN {
start: CARDINAL = s.index - s.firstBufferIndex;
stop: CARDINAL = FS.PagesForBytes[s.index]*bytesPerFilePage - s.firstBufferIndex;
FOR i: INT IN [start .. stop) DO TRUSTED { s.buffer[i] ← 0C }; ENDLOOP;
StoreBuffer[s];
};
FreeBuffer[s];
self.streamProcs ← NIL;
self.streamData ← NIL;
};
EndOf: PROC [self: STREAM] RETURNS[BOOL] = {
s: REF StreamObject = NARROW [self.streamData];
RETURN[s.index >= s.lastFileIndexPlusOne];
};
GetLength: PROC [self: STREAM] RETURNS[length: INT] = {
s: REF StreamObject = NARROW [self.streamData];
RETURN[s.lastFileIndexPlusOne];
};
GetIndex: PROC [self: STREAM] RETURNS [i: INT] = {
s: REF StreamObject = NARROW [self.streamData];
RETURN[s.index];
};
SetIndex: PROC [self: STREAM, index: INT] = {
s: REF StreamObject = NARROW [self.streamData];
IF index > s.lastFileIndexPlusOne
THEN ERROR IO.EndOfStream[self];
s.index ← index;
};
GetChar: PROC [self: STREAM] RETURNS[char: CHAR] = TRUSTED {
s: REF StreamObject = NARROW [self.streamData];
IF s.index >= s.lastFileIndexPlusOne
THEN ERROR IO.EndOfStream[self];
IF s.index NOT IN [s.firstBufferIndex .. s.lastBufferIndexPlusOne)
THEN LoadBuffer[s];
char ← s.buffer[s.index - s.firstBufferIndex];
s.index ← s.index + 1;
};
PutChar: PROC [self: STREAM, char: CHAR] = TRUSTED {
s: REF StreamObject = NARROW [self.streamData];
IF s.index >= s.lastBufferIndexPlusOne
THEN StoreBuffer[s];
s.buffer[s.index - s.firstBufferIndex] ← char;
s.index ← s.index + 1;
};
GetBlock: PROC [self: STREAM, block: REF TEXT, startIndex: NAT, count: NAT] RETURNS[nBytesRead: NAT] = TRUSTED {
s: REF StreamObject = NARROW [self.streamData];
nBytesRead ← 0;
count ← MIN[count, block.maxLength-startIndex];
UNTIL count = 0 OR s.index = s.lastFileIndexPlusOne DO
bufferChars, bufferIndex, bytesToGet: CARDINAL;
IF s.index NOT IN [s.firstBufferIndex .. s.lastBufferIndexPlusOne)
THEN LoadBuffer[s];
bufferChars ← s.lastBufferIndexPlusOne - s.index;
bufferIndex ← s.index - s.firstBufferIndex;
bytesToGet ← MIN[count, bufferChars];
[] ← PrincOpsUtils.ByteBlt[
to: [ BASE[DESCRIPTOR[block]], startIndex, startIndex+bytesToGet ],
from: [ s.buffer, bufferIndex, bufferIndex+bytesToGet ]
];
s.index ← s.index + bytesToGet;
count ← count - bytesToGet;
startIndex ← startIndex + bytesToGet;
nBytesRead ← nBytesRead + bytesToGet;
ENDLOOP;
block.length ← startIndex;
};
PutBlock: PROC [self: STREAM, block: REF READONLY TEXT, startIndex: NAT, count: NAT] = TRUSTED {
s: REF StreamObject = NARROW [self.streamData];
count ← MIN[count, block.length-startIndex];
UNTIL count = 0 DO
bufferChars, bufferIndex, bytesToPut: CARDINAL;
IF s.index >= s.lastBufferIndexPlusOne
THEN StoreBuffer[s];
bufferChars ← s.lastBufferIndexPlusOne - s.index;
bufferIndex ← s.index - s.firstBufferIndex;
bytesToPut ← MIN[count, bufferChars];
[] ← PrincOpsUtils.ByteBlt[
to: [ s.buffer, bufferIndex, bufferIndex+bytesToPut ],
from: [ BASE[DESCRIPTOR[block]], startIndex, startIndex+bytesToPut ]
];
s.index ← s.index + bytesToPut;
count ← count - bytesToPut;
startIndex ← startIndex + bytesToPut;
ENDLOOP;
};
Internal stuff
bufferFilePages: INT = 32; -- ←←←← Was 8, HGM wants to go FAST
bytesPerFilePage: INT = File.wordsPerPage*2;
bufferBytes: INT = bufferFilePages * bytesPerFilePage;
bufferVMPages: VM.PageCount = VM.PagesForBytes[bufferBytes];
BufferPtr: TYPE = LONG POINTER TO PACKED ARRAY [0 .. 0) OF CHAR;
StreamObject: TYPE = RECORD [
file: File.Handle,
lastFileIndexPlusOne: INT, -- byte count for oldReadOnly, bytes in file pages for newAppendOnly
index: INT, -- stream index
buffer: BufferPtr,
firstBufferIndex: INT,
lastBufferIndexPlusOne: INT
];
readProcs: REF IO.StreamProcs = IO.CreateStreamProcs[variety: input, class: $FSFileStreamImplRead, close: Close, endOf: EndOf, getIndex: GetIndex, setIndex: SetIndex, getLength: GetLength, getChar: GetChar, getBlock: GetBlock];
appendProcs: REF IO.StreamProcs = IO.CreateStreamProcs[variety: output, class: $FSFileStreamImplAppend, close: Close, getIndex: GetIndex, putBlock: PutBlock];
CreateStreamObject: PROC [file: File.Handle, mode: FSFileOps.FileStreamMode, lastIndexPlusOne: INT] RETURNS [s: REF StreamObject] = {
s ← NEW [ StreamObject ];
s.file ← file;
s.index ← 0;
s.lastFileIndexPlusOne ← lastIndexPlusOne;
IF mode = oldReadOnly
THEN {
s.firstBufferIndex ← s.lastFileIndexPlusOne;
s.lastBufferIndexPlusOne ← s.lastFileIndexPlusOne;
}
ELSE {
s.firstBufferIndex ← 0;
s.lastBufferIndexPlusOne ← bufferBytes;
};
};
PageNumberForByteIndex: PROC [index: INT] RETURNS [INT] = {
RETURN[ index/(bytesPerFilePage) ]
};
LoadBuffer: PROC [s: REF StreamObject] = {
pageNumber: INT = PageNumberForByteIndex[s.index];
pageCount: INT;
s.firstBufferIndex ← pageNumber * bytesPerFilePage;
s.lastBufferIndexPlusOne ← MIN[s.lastFileIndexPlusOne, s.firstBufferIndex+bufferBytes];
pageCount ← PageNumberForByteIndex[s.lastBufferIndexPlusOne-1] - pageNumber + 1;
TRUSTED {
File.Read[file: s.file, from: [pageNumber], nPages: pageCount, to: s.buffer
! File.Error => FSReport.FileError[why] ];
};
};
StoreBuffer: PROC [s: REF StreamObject] = {
pageNumber: INT = PageNumberForByteIndex[s.firstBufferIndex];
pageCount: INT = PageNumberForByteIndex[s.index-1] - pageNumber + 1;
IF s.index > s.lastFileIndexPlusOne
THEN {
newSize: INT = pageNumber + pageCount;
FSFileOps.SetFilePages[s.file, newSize];
s.lastFileIndexPlusOne ← newSize * bytesPerFilePage;
};
s.firstBufferIndex ← s.lastBufferIndexPlusOne;
s.lastBufferIndexPlusOne ← s.firstBufferIndex+bufferBytes;
TRUSTED {
File.Write[file: s.file, to: [pageNumber], nPages: pageCount, from: s.buffer
! File.Error => FSReport.FileError[why] ];
};
};
retainedBuffer: BufferPtr ← NIL;
AllocateBuffer: ENTRY PROC [s: REF StreamObject] = TRUSTED {
ENABLE UNWIND => NULL;
IF retainedBuffer = NIL
THEN {
interval: VM.Interval ← VM.SimpleAllocate[count: bufferVMPages];
VM.SwapIn[interval];
s.buffer ← VM.AddressForPageNumber[ interval.page ];
}
ELSE {
s.buffer ← retainedBuffer;
retainedBuffer ← NIL;
};
};
FreeBuffer: ENTRY PROC [s: REF StreamObject] = TRUSTED {
ENABLE UNWIND => NULL;
interval: VM.Interval ← [VM.PageNumberForAddress[s.buffer], bufferVMPages];
IF retainedBuffer = NIL
THEN {
retainedBuffer ← s.buffer;
}
ELSE {
VM.Free[ interval ];
};
s.buffer ← NIL;
};
}.
Bob Hagmann January 28, 1986 4:13:35 pm PST
added GetIndex to allow STP to find out the stream length
changes to: GetIndex, readProcs