[{$mode objfpc}
unit windowedstream;
interface
uses
Classes, SysUtils;
type
TWindowedStream = class(TStream)
private
FBaseStream: TStream;
FStartOffset: Int64;
FWindowSize: Int64;
FPosition: Int64;
public
constructor Create(BaseStream: TStream; StartOffset, WindowSize: Int64);
function Read(var Buffer; Count: Longint): Longint; override;
function Write(const Buffer; Count: Longint): Longint; override;
function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
function Size: Int64;virtual;
end;
implementation
{ including math is not necessary }
{$if not declared(min)}
function min(const a,b:uint64):Uint64;
begin
Result := a;
If b < a then result := b;
end;
{$ifend}
constructor TWindowedStream.Create(BaseStream: TStream; StartOffset, WindowSize: Int64);
begin
inherited Create;
if not Assigned(BaseStream) then
raise EArgumentException.Create('BaseStream cannot be nil');
if (StartOffset < 0) or (WindowSize < 0) then
raise EArgumentException.Create('StartOffset and WindowSize must be non-negative');
if StartOffset + WindowSize > BaseStream.Size then
raise EArgumentException.Create('Window exceeds base stream size');
FBaseStream := BaseStream;
FStartOffset := StartOffset;
FWindowSize := WindowSize;
FPosition := 0;
end;
function TWindowedStream.Read(var Buffer; Count: Longint): Longint;
var
BytesToRead: Int64;
begin
if FPosition >= FWindowSize then
Exit(0);
FBaseStream.Position := FStartOffset + FPosition;
BytesToRead := Min(Count, FWindowSize - FPosition);
Result := FBaseStream.Read(Buffer, BytesToRead);
Inc(FPosition, Result);
end;
function TWindowedStream.Write(const Buffer; Count: Longint): Longint;
var
BytesToWrite: Int64;
begin
if FPosition >= FWindowSize then
Exit(0);
FBaseStream.Position := FStartOffset + FPosition;
BytesToWrite := Min(Count, FWindowSize - FPosition);
Result := FBaseStream.Write(Buffer, BytesToWrite);
Inc(FPosition, Result);
end;
function TWindowedStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
var
NewPosition: Int64;
begin
case Origin of
soBeginning: NewPosition := Offset;
soCurrent: NewPosition := FPosition + Offset;
soEnd: NewPosition := FWindowSize + Offset;
else
raise EStreamError.Create('Invalid seek origin');
end;
if (NewPosition < 0) or (NewPosition > FWindowSize) then
raise EStreamError.Create('Seek out of bounds');
FPosition := NewPosition;
Result := FPosition;
end;
function TWindowedStream.Size: Int64;
begin
Result := FWindowSize;
end;
end.