Question

I need to extend TFileStream so that it can work with a file not from 0 offset, but from user defined offset. I mean it must interpret user defined offset as stream beginning. My code is:


type
  TSuFileStream = class(TFileStream)
  protected
    FOffset : int64;

    procedure SetOffset(Offset : int64);

    procedure SetSize(NewSize: Longint); override;
    procedure SetSize(const NewSize: Int64); override;
  public
    constructor Create(const AFileName: string; Mode: Word); overload;
    constructor Create(const AFileName: string; Mode: Word; Rights: Cardinal); overload;

    function Seek(Offset: Longint; Origin: Word): Longint; override;
    function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;

    property Offset : int64 read FOffset write SetOffset;
  end;
...
constructor TSuFileStream.Create(const AFileName: string; Mode: Word);
begin
  inherited Create(AFileName, Mode);
  FOffset := 0;
end;

constructor TSuFileStream.Create(const AFileName: string; Mode: Word; Rights: Cardinal);
begin
  inherited Create(AFileName, Mode, Rights);
  FOffset := 0;
end;

procedure TSuFileStream.SetOffset(Offset : int64);
begin
  FOffset := Offset;
  inherited Seek(FOffset, soBeginning);
end;

procedure TSuFileStream.SetSize(NewSize: Longint);
begin
  inherited SetSize(FOffset + NewSize);
end;

procedure TSuFileStream.SetSize(const NewSize: Int64);
begin
  inherited SetSize(FOffset + NewSize);
end;

function TSuFileStream.Seek(Offset: Longint; Origin: Word): Longint;
begin
  Result := Seek(Int64(Offset), TSeekOrigin(Origin));
end;

function TSuFileStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
begin
  case Origin of
    soBeginning: Result := inherited Seek(FOffset + Offset, soBeginning) - FOffset;
    soCurrent: Result := inherited Seek(Offset, soCurrent) - FOffset;
    soEnd: Result := inherited Seek(Offset, soEnd) - FOffset;
  end;
end;
 

but it does not work propertly. The problem is in Seek function but I don't know why. When I pass such stream to a third party component it works only if TSuFileStream.Offset := 0;

Was it helpful?

Solution

Use TGpStreamWindow, available on my web and on Google Code.

Usage:

var
  offsetStream: TGpStreamWindow;

begin
  offsetStream := TGpStreamWindow.Create(originalStream, initialOffset, originalStream.Size - 1);
  try
    DoSomethingWith(offsetStream);
    offsetStream.SetWindow(anotherInitialOffset, originalStream.Size - 1);
    DoSomethingElseWith(offsetStream);
  finally FreeAndNil(offsetStream); end;
end;

OTHER TIPS

First, only override one of the method versions. As you can see from the class interface you have both longint and int64 versions of the same methods (like setSize and seek). This is in the Delphi documentation. Override the int64 versions.

Secondly, I would not override TFilestream but rather TStream directly to create a "in between stream" to work with.

In the constructor I would put 2 parameters:

  1. Actual source stream of any type
  2. Offset

So basically what you want to create is a proxy between the real stream and your custom version. That way, in your seek implementation all you have to add the offset (look at TMemoryStream and TFileStream to see how it's done) to the position. You also get the benefit of supporting any type of stream-source.

You should end up with a proxy that is easy to use:

mMyStream:=TMyProxyStream.Create(mRealStream,2800); //Root offset at 2800
try
  mMyStream.Read(mBuffer,1024); // After read, offset = 3824
  mMyStream.position:=0; //Reset offset back to to 2800
finally
  mMyStream.free;
end;

The seek functionality can be a bit tricky to calculate. Here is an example from a proxy class i coded for my buffer system (FOffset being an internal variable, this is the one you want to manipulate):

function TSLBufferStreamAdapter.Seek(const Offset:Int64;
         Origin:TSeekOrigin):Int64;
Begin
  Case Origin of
  soBeginning:
    Begin
      if Offset>=0 then
      FOffset:=Math.EnsureRange(Offset,0,FBufObj.Size);
    end;
  soCurrent:
    Begin
      FOffset:=math.EnsureRange(FOffset + Offset,0,FBufObj.Size);
    end;
  soEnd:
    Begin
      If Offset>0 then
      FOffset:=FBufObj.Size-1 else
      FOffset:=math.EnsureRange(FOffset-(abs(Offset)),0,FBufObj.Size);
    end;
  end;
  result:=FOffset;
end;

I update this reply now to include an update link. My library byterage has moved to google code - have a look there. Hope it helps!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top