Browse Source

* Added windowed stream by Danny Milosavljevic (bug ID 12845)

git-svn-id: trunk@32797 -
michael 9 năm trước cách đây
mục cha
commit
de2eb17d6e
1 tập tin đã thay đổi với 128 bổ sung0 xóa
  1. 128 0
      packages/fcl-base/src/streamex.pp

+ 128 - 0
packages/fcl-base/src/streamex.pp

@@ -156,6 +156,28 @@ type
      function ReadLine: string; override; overload;
    end;
 
+  { 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;
+  public
+    constructor Create(aStream : TStream; const aSize : Int64; const aPositionHere : Int64 = 0);
+    destructor Destroy(); override;
+    function Read(var aBuffer; aCount : longint) : longint; override;
+    function Write(const aBuffer; aCount : Longint): Longint; override;
+    function Seek(const aOffset: Int64; aOrigin: TSeekorigin): Int64; override;
+  end;
+
+
   TStreamHelper = class helper for TStream
                      function  ReadWordLE :word;
                      function  ReadDWordLE:dword;
@@ -183,6 +205,13 @@ type
 
 Implementation
 
+ResourceString
+  SErrCannotWriteOutsideWindow = 'Cannot write outside allocated window.';
+  SErrInvalidSeekWindow = 'Cannot seek outside allocated window.';
+  SErrInvalidSeekOrigin = 'Invalid seek origin.';
+  SErrCannotChangeWindowSize  = 'Cannot change the size of a windowed stream';
+
+
 { TBidirBinaryObjectReader }
 
 function TBidirBinaryObjectReader.GetPosition: Longint;
@@ -607,4 +636,103 @@ begin
 end;
 {$endif}
 
+{ 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;
+  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(SErrCannotWriteOutsideWindow);
+    //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(SErrInvalidSeekOrigin);
+
+  vSourcePosition := fStart + vNewPositionHere - fStartingPositionHere;
+  if (vSourcePosition < 0) or (vSourcePosition >= fFrontier) then
+    raise EReadError.Create(SErrInvalidSeekWindow);
+    
+  Result := Source.Seek(vSourcePosition, 0);
+  //if Result = -1 ??? can that happen?
+  Result := vNewPositionHere;
+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(SErrCannotChangeWindowSize);
+end;
+
+
 end.