// I, Danny Milosavljevic, hereby release this code into the public domain. unit windowed_streams; {$MODE OBJFPC} {$M+} interface uses classes; type { allows you to represent just a small window of a bigger stream as a substream. also makes sure one is actually at the correct position before clobbering stuff. } TWindowedStream = class(TOwnerStream) private fStart : Int64; // in the source. fFrontier : Int64; // in the source. fStartingPositionHere : Int64; // position in this Stream corresponding to Position = fStart in the source. fPositionHere : Int64; // position in this Stream. protected //function GetPosition() : Int64; override; = Seek(0, soCurrent) already. function GetSize() : Int64; override; procedure SetSize(const NewSize: Int64); override; overload; published constructor Create(aStream : TStream; const aSize : Int64; const aPositionHere : Int64 = 0); property SourceStartingPosition : Int64 read fStart; // useful for debugging. public destructor Destroy(); override; function Read(var aBuffer; aCount : longint) : longint; override; function Write(const aBuffer; aCount : Longint): Longint; override; function Seek(aOffset : longint; aOrigin : word) : longint; override; function Seek(const aOffset: Int64; aOrigin: TSeekorigin): Int64; override; end; implementation { TWindowedStream } constructor TWindowedStream.Create(aStream : TStream; const aSize : Int64; const aPositionHere : Int64 = 0); begin inherited Create(aStream); fStart := aStream.Position; fFrontier := fStart + aSize; fStartingPositionHere := aPositionHere; fPositionHere := aPositionHere; end; destructor TWindowedStream.Destroy(); begin inherited Destroy(); end; function TWindowedStream.Read(var aBuffer; aCount : longint) : longint; var vSourcePosition : Int64; vNewSourcePosition : Int64; begin vSourcePosition := Source.Position; vNewSourcePosition := fStart + fPositionHere - fStartingPositionHere; //Writeln(vNewSourcePosition); if vNewSourcePosition <> vSourcePosition then // someone modified the file position. Bad bad. Source.Seek(vNewSourcePosition, 0); if vNewSourcePosition + aCount > fFrontier then // trying to access outside. aCount := fFrontier - vNewSourcePosition; Result := Source.Read(aBuffer, aCount); Inc(fPositionHere, Result); end; function TWindowedStream.Write(const aBuffer; aCount : Longint): Longint; var vSourcePosition : Int64; vNewSourcePosition : Int64; begin vSourcePosition := Source.Position; vNewSourcePosition := fStart + fPositionHere - fStartingPositionHere; if vNewSourcePosition <> vSourcePosition then // someone modified the file position. Bad bad. Source.Seek(vNewSourcePosition, 0); if vNewSourcePosition + aCount > fFrontier then // trying to access outside. raise EWriteError.Create('disk full.'); //aCount := fFrontier - vNewSourcePosition; Result := Source.Write(aBuffer, aCount); Inc(fPositionHere, Result); end; function TWindowedStream.Seek(const aOffset: Int64; aOrigin: TSeekOrigin): Int64; var vNewPositionHere : Int64; vSourcePosition : Int64; begin { here there fStartingPositionHere .... fStart fPositionHere............. x } if (aOrigin = soCurrent) and (aOffset = 0) then begin // get position. Result := fPositionHere; Exit; end; if aOrigin = soBeginning then vNewPositionHere := aOffset else if aOrigin = soCurrent then vNewPositionHere := fPositionHere + aOffset else if aOrigin = soEnd then vNewPositionHere := fStartingPositionHere + fFrontier - fStart + aOffset else raise EReadError.Create('invalid seek origin.'); vSourcePosition := fStart + vNewPositionHere - fStartingPositionHere; if (vSourcePosition < 0) or (vSourcePosition >= fFrontier) then raise EReadError.Create('tried to seek outside range.'); Result := Source.Seek(vSourcePosition, 0); //if Result = -1 ??? can that happen? Result := vNewPositionHere; end; function TWindowedStream.Seek(aOffset : longint; aOrigin : word) : longint; begin Result := Self.Seek(Int64(aOffset), TSeekOrigin(aOrigin)); end; function TWindowedStream.GetSize() : Int64; begin Result := fFrontier - fStart; end; procedure TWindowedStream.SetSize(const NewSize: Int64); overload; begin if NewSize = Self.GetSize() then Exit; raise EWriteError.Create('cannot change size of a TWindowedStream.'); end; end.