<<>> <> <> <> <> <> <<>> DIRECTORY Basics USING [CopyBytes, UnsafeBlock], IO USING [STREAM, STREAMRecord, StreamProcs, CreateStreamProcs, EndOfStream, Error], IOUtils USING [closedStreamProcs], PFS USING [AccessOptions, CreateOptions, Error, ErrorDesc, FileType, StreamBufferParms, StreamOptions, UniqueID, OpenFile, Open, PATH, Read, Write, GetInfo, SetByteCountAndUniqueID], PFSExtras USING [NewClose], PFSBackdoor USING [ProduceError], RefText USING [BaseFromText, New], Rope USING [ROPE], TiogaFileIO USING [Parts, GetParts]; <> <<>> PFSStreamImpl: CEDAR PROGRAM IMPORTS Basics, IO, IOUtils, PFS, PFSExtras, PFSBackdoor, RefText, TiogaFileIO EXPORTS PFS ~ BEGIN ROPE: TYPE ~ Rope.ROPE; STREAM: TYPE ~ IO.STREAM; StreamProcs: TYPE ~ REF IO.StreamProcs; <<>> StreamData: TYPE ~ REF StreamDataRep; StreamDataRep: TYPE ~ RECORD [ file: PFS.OpenFile, bufferStart: CARD ¬ 0, -- current stream index is bufferStart+bufferIndex stop: CARD ¬ CARD.LAST, -- max visible file length (for hiding Tioga formatting) closeFileOnClose: BOOL ¬ FALSE, error: PFS.ErrorDesc ¬ [ok, NIL, NIL, NIL] ]; <> <> <> <> <> <> <> <> <> <<>> fileStreamClass: ATOM ~ $UFileIO; fileInputProcs: StreamProcs ~ IO.CreateStreamProcs[ variety: $input, class: fileStreamClass, getChar: PFSGetChar, unsafeGetBlock: PFSUnsafeGetBlock, endOf: PFSEndOf, backup: PFSBackup, flush: PFSFlush, close: PFSClose, getIndex: PFSGetIndex, setIndex: PFSSetIndex, getLength: PFSGetLength ]; fileOutputProcs: StreamProcs ~ IO.CreateStreamProcs[ variety: $output, class: fileStreamClass, putChar: PFSPutChar, unsafePutBlock: PFSUnsafePutBlock, flush: PFSFlush, close: PFSClose, getIndex: PFSGetIndex, setIndex: PFSSetIndex, getLength: PFSGetLength, setLength: PFSSetLength ]; bufferSize: NAT ~ 8192; FileLength: PROC [file: PFS.OpenFile] RETURNS [CARD] ~ { RETURN[MAX[PFS.GetInfo[file].bytes, 0]]; }; StreamOpen: PUBLIC PROC [fileName: PFS.PATH, accessOptions: PFS.AccessOptions, wantedUniqueID: PFS.UniqueID, checkFileType: BOOL, fileType: PFS.FileType, createOptions: PFS.CreateOptions, streamOptions: PFS.StreamOptions, streamBufferParms: PFS.StreamBufferParms] RETURNS [STREAM] ~ { openFile: PFS.OpenFile ~ PFS.Open[name: fileName, access: accessOptions, wantedUniqueID: wantedUniqueID, checkFileType: checkFileType, fileType: fileType, createOptions: createOptions]; RETURN StreamFromOpenFile[openFile, accessOptions, streamOptions, streamBufferParms]; }; <<>> StreamFromOpenFile: PUBLIC PROC [openFile: PFS.OpenFile, accessOptions: PFS.AccessOptions, streamOptions: PFS.StreamOptions, streamBufferParms: PFS.StreamBufferParms] RETURNS [STREAM] ~ { <<*** Note: streamBufferParms are ignored! ***>> output: BOOL ~ (accessOptions#read); data: StreamData ~ NEW[StreamDataRep ¬ [file: openFile]]; self: STREAM ~ NEW[IO.STREAMRecord ¬ [streamProcs: fileInputProcs, streamData: data, buffer: RefText.New[bufferSize]]]; <> length: CARD ~ FileLength[openFile]; IF length>0 AND NOT streamOptions[includeFormatting] THEN { parts: TiogaFileIO.Parts ~ TiogaFileIO.GetParts[s: self, start: 0, len: length]; PFSSetIndex[self, 0]; IF parts.isTioga THEN { IF output THEN PFSBackdoor.ProduceError[cantUpdateTiogaFile, "Can't update Tioga file"]; data.stop ¬ parts.start1+parts.len1; self.bufferInputLength ¬ MIN[self.bufferInputLength, data.stop]; <> }; }; data.closeFileOnClose ¬ streamOptions[closeFSOpenFileOnClose]; IF output THEN { self.streamProcs ¬ fileOutputProcs; self.bufferInputLength ¬ 0; self.bufferOutputLength ¬ bufferSize; self.bufferIndex ¬ 0; data.bufferStart ¬ IF accessOptions=append THEN length ELSE 0; }; RETURN[self]; }; ReadBuffer: PROC [self: STREAM] ~ { data: StreamData ~ NARROW[self.streamData]; index: CARD ~ data.bufferStart+self.bufferIndex; -- current stream index start: CARD ~ (index/bufferSize)*bufferSize; -- start index for buffer containing index IF data.bufferStart#start THEN { data.bufferStart ¬ start; self.bufferIndex ¬ index-start; self.bufferInputLength ¬ 0; }; IF start < data.stop THEN { len: NAT ~ self.bufferInputLength; -- current buffer length max: NAT ~ MIN[bufferSize, data.stop-start]; -- max buffer length IF len < max THEN TRUSTED { self.bufferInputLength ¬ len + PFS.Read[file: data.file, filePosition: start+len, nBytes: max-len, to: RefText.BaseFromText[self.buffer], toStart: len ! PFS.Error => { data.error ¬ error; ERROR IO.Error[Failure, self] }]; }; }; }; WriteBuffer: PROC [self: STREAM] ~ { IF self.bufferIndex>0 THEN { data: StreamData ~ NARROW[self.streamData]; start: CARD ~ data.bufferStart; len: NAT ~ self.bufferIndex; bytesWritten: NAT ~ PFS.Write[file: data.file, filePosition: start, nBytes: len, from: RefText.BaseFromText[self.buffer], fromStart: 0 ! PFS.Error => { data.error ¬ error; ERROR IO.Error[Failure, self] }]; IF bytesWritten#len THEN ERROR IO.Error[Failure, self, NIL, "PFS.Write failed."]; data.bufferStart ¬ start+len; self.bufferIndex ¬ 0; }; }; PFSGetChar: PROC [self: STREAM] RETURNS [char: CHAR] ~ { IF NOT self.bufferIndex < self.bufferInputLength THEN ReadBuffer[self]; IF self.bufferIndex < self.bufferInputLength THEN { i: NAT ~ self.bufferIndex; self.bufferIndex ¬ i+1; RETURN[self.buffer[i]] } ELSE ERROR IO.EndOfStream[self]; }; PFSEndOf: PROC [self: STREAM] RETURNS [BOOL] ~ { IF NOT self.bufferIndex < self.bufferInputLength THEN ReadBuffer[self]; RETURN[NOT (self.bufferIndex < self.bufferInputLength)]; }; PFSBackup: PROC [self: STREAM, char: CHAR] ~ { <> index: INT ~ PFSGetIndex[self]; IF index>0 THEN PFSSetIndex[self, index-1] ELSE ERROR IO.Error[IllegalBackup, self]; IF PFSGetChar[self]=char THEN PFSSetIndex[self, index-1] ELSE ERROR IO.Error[IllegalBackup, self]; }; PFSUnsafeGetBlock: UNSAFE PROC [self: STREAM, block: Basics.UnsafeBlock] RETURNS [nBytesRead: INT ¬ 0] ~ { data: StreamData ~ NARROW[self.streamData]; startIndex: CARD ~ block.startIndex; -- BoundsFault if block.startIndex<0 count: CARD ~ block.count; -- BoundsFault if block.count<0 done: CARD ¬ 0; WHILE done < count DO IF self.bufferIndex < self.bufferInputLength THEN { n: CARDINAL ~ MIN[count-done, CARD[self.bufferInputLength-self.bufferIndex]]; TRUSTED --UNSAFE-- { Basics.CopyBytes[ dstBase: block.base, dstStart: startIndex+done, srcBase: RefText.BaseFromText[self.buffer], srcStart: self.bufferIndex, count: n] }; self.bufferIndex ¬ self.bufferIndex+n; done ¬ done+n; IF done = count THEN EXIT; }; <> IF (count-done) < bufferSize THEN { ReadBuffer[self]; IF NOT self.bufferIndex < self.bufferInputLength THEN EXIT; } ELSE { -- big transfer, copy directly from file index: CARD ~ data.bufferStart+self.bufferIndex; n: CARD ¬ 0; IF index < data.stop THEN TRUSTED --UNSAFE-- { n ¬ PFS.Read[file: data.file, filePosition: index, nBytes: MIN[count-done, data.stop-index], to: block.base, toStart: startIndex+done ! PFS.Error => { data.error ¬ error; ERROR IO.Error[Failure, self] }] }; self.bufferInputLength ¬ 0; self.bufferIndex ¬ 0; data.bufferStart ¬ index+n; done ¬ done+n; EXIT; }; ENDLOOP; RETURN[done]; }; PFSPutChar: PROC [self: STREAM, char: CHAR] ~ { IF NOT self.bufferIndex < self.bufferOutputLength THEN WriteBuffer[self]; IF self.bufferIndex < self.bufferOutputLength THEN { i: NAT ~ self.bufferIndex; self.bufferIndex ¬ i+1; self.buffer[i] ¬ char } ELSE ERROR; -- Flush should have ensured bufferIndex < bufferOutputLength }; PFSUnsafePutBlock: PROC [self: STREAM, block: Basics.UnsafeBlock] ~ { data: StreamData ~ NARROW[self.streamData]; startIndex: CARD ~ block.startIndex; -- BoundsFault if block.startIndex<0 count: CARD ~ block.count; -- BoundsFault if block.count<0 IF self.bufferIndex > 0 THEN WriteBuffer[self]; <> IF self.bufferIndex#0 THEN ERROR; -- WriteBuffer failed?? IF count < self.bufferOutputLength THEN { TRUSTED { Basics.CopyBytes[ dstBase: RefText.BaseFromText[self.buffer], dstStart: 0, srcBase: block.base, srcStart: startIndex, count: count] }; self.bufferIndex ¬ count; } ELSE { -- big transfer, copy directly to file bytesWritten: CARD ~ PFS.Write[file: data.file, filePosition: data.bufferStart, nBytes: count, from: block.base, fromStart: startIndex ! PFS.Error => { data.error ¬ error; ERROR IO.Error[Failure, self] }]; IF bytesWritten#count THEN ERROR IO.Error[Failure, self, NIL, "PFS.Write failed."]; data.bufferStart ¬ data.bufferStart+count; }; }; PFSFlush: PROC [self: STREAM] ~ { IF self.bufferOutputLength=0 THEN self.bufferInputLength ¬ 0 -- input stream ELSE WriteBuffer[self]; -- output stream }; PFSGetIndex: PROC [self: STREAM] RETURNS [INT] ~ { data: StreamData ~ NARROW[self.streamData]; RETURN[data.bufferStart+self.bufferIndex]; }; PFSGetLength: PROC [self: STREAM] RETURNS [INT] ~ { data: StreamData ~ NARROW[self.streamData]; IF data.stop0 THEN data.bufferStart+len ELSE 0; RETURN[MAX[FileLength[data.file], bufferEnd]]; }; }; PFSSetIndex: PROC [self: STREAM, index: INT] ~ { data: StreamData ~ NARROW[self.streamData]; old: CARD ~ data.bufferStart+self.bufferIndex; new: CARD ~ index; -- bounds fault if index<0 IF new=old THEN RETURN; IF self.bufferOutputLength=0 THEN { -- input stream start: CARD ~ data.bufferStart; len: NAT ~ self.bufferInputLength; IF new IN[start..(start+len)] THEN self.bufferIndex ¬ new-start ELSE { self.bufferInputLength ¬ 0; self.bufferIndex ¬ 0; data.bufferStart ¬ new; }; } ELSE { -- output stream WriteBuffer[self]; data.bufferStart ¬ new; }; }; PFSSetLength: PROC [self: STREAM, length: INT] ~ { data: StreamData ~ NARROW[self.streamData]; index: CARD ~ data.bufferStart+self.bufferIndex; len: CARD ~ length; -- bounds fault if length<0 IF self.bufferIndex>0 THEN WriteBuffer[self]; PFS.SetByteCountAndUniqueID[file: data.file, bytes: len ! PFS.Error => {data.error ¬ error; ERROR IO.Error[Failure, self] }]; IF len { data.error ¬ error; ERROR IO.Error[Failure, self] }]; self.streamProcs ¬ IOUtils.closedStreamProcs; self.bufferInputLength ¬ 0; self.bufferOutputLength ¬ 0; self.bufferIndex ¬ 0; self.buffer ¬ NIL; }; ErrorFromStream: PUBLIC PROC [self: STREAM] RETURNS [PFS.ErrorDesc] ~ { WITH self.streamData SELECT FROM data: StreamData => RETURN[data.error]; ENDCASE => ERROR IO.Error[NotImplementedForThisStream, self]; }; OpenFileFromStream: PUBLIC PROC [self: STREAM] RETURNS [PFS.OpenFile] ~ { WITH self.streamData SELECT FROM data: StreamData => RETURN[data.file]; ENDCASE => ERROR IO.Error[NotImplementedForThisStream, self]; <> }; END.