Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TFileStream with offset

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;

like image 839
JuniorD Avatar asked May 12 '11 15:05

JuniorD


2 Answers

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!

like image 132
Jon Lennart Aasenden Avatar answered Sep 21 '22 12:09

Jon Lennart Aasenden


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;
like image 30
gabr Avatar answered Sep 19 '22 12:09

gabr