// I, Danny Milosavljevic, hereby release this code into the public domain. // TODO finish. unit concatenated_streams; {$MODE OBJFPC} {$M+} interface uses classes; type TConcatenatedStream = class(TOwnerStream) private fNext : TStream; // TODO fStartingPositionHere? virtual gaps? 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 Next : TStream read fNext; 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 { TConcatenatedStream } constructor TConcatenatedStream.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 TConcatenatedStream.Destroy(); begin inherited Destroy(); end; function TConcatenatedStream.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 TConcatenatedStream.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 TConcatenatedStream.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 TConcatenatedStream.Seek(aOffset : longint; aOrigin : word) : longint; begin Result := Self.Seek(Int64(aOffset), TSeekOrigin(aOrigin)); end; function TConcatenatedStream.GetSize() : Int64; begin Result := fFrontier - fStart; end; procedure TConcatenatedStream.SetSize(const NewSize: Int64); overload; begin if NewSize = Self.GetSize() then Exit; raise EWriteError.Create('cannot change size of a TConcatenatedStream.'); end; end.