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
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, Allocate, Free, PageNumberForAddress, PageCount, PagesForBytes];
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];
};
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;
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;
};
PutBlock:
PROC [self:
STREAM, block:
REF
READONLY
TEXT, startIndex:
NAT, count:
NAT] =
TRUSTED {
s: REF StreamObject = NARROW [self.streamData];
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 = 8;
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, 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 s.buffer ← VM.AddressForPageNumber[ VM.Allocate[count: bufferVMPages].page ]
ELSE { s.buffer ← retainedBuffer; retainedBuffer ← NIL };
};
FreeBuffer:
ENTRY
PROC [s:
REF StreamObject] =
TRUSTED {
ENABLE UNWIND => NULL;
IF retainedBuffer =
NIL
THEN retainedBuffer ← s.buffer
ELSE VM.Free[ [VM.PageNumberForAddress[s.buffer], bufferVMPages] ];
s.buffer ← NIL;
};
}.