Recent

Author Topic: Is there a class or Gen that can stream data like the Virtual-Draw-Tree does?  (Read 1418 times)

jamie

  • Hero Member
  • *****
  • Posts: 7776
Looking for existing classes that can perform the same duties as the TVirtualDrawTree or String Tree when it comes to Saving and Loading of the nodes from and to a TStream?

  I am currently using a Virtual Draw Tree only as a Push and Pop type of variable data blocks with a fixed header to store compressed images using the compress libs that comes with Laz, so space is compacted.
 
 This code is intended for temporary storage of images but also want it to store other items later.

 I was looking to use the TDBF controls but that is over the top and most likely would not fully do the job.

 Currently the Virtual Draw Tree works but I don't need any of the visual (GUI) part of it, although I can optionally show it to inspect the data.

 I was looking for a Stackable way to write a header and trailing data block of variable size via  TStream.

 I know I can write a Generic based on a Tstream but would like to see what's already there.

Jamie
The only true wisdom is knowing you know nothing

Thaddy

  • Hero Member
  • *****
  • Posts: 19279
  • Glad to be alive.
TVirtualDrawTree probably uses a windowed stream. I believe there are several examples for that already.
A windowed stream works on top of another stream and just accesses part of the data in the stream.
Creating a windowed stream descendant in Object Pascal involves inheriting from TStream or one of its descendants (e.g., TMemoryStream, TFileStream) and overriding its methods to implement a "windowed" behavior.
A windowed stream essentially restricts access to a specific range of bytes within an underlying stream.
What is does is that it lets you take just the amount of items that you can e.g. display. E.g: the underlying data can stay on disk or memory and the windowed stream processes just a small amount of the underlying stream. In the case of the TVrtual classes this speeds up display enormously, with magnitudes on large underlying data.
An example:
Code: Pascal  [Select][+][-]
  1. [{$mode objfpc}
  2. unit windowedstream;
  3. interface
  4.  
  5. uses
  6.   Classes, SysUtils;
  7.  
  8. type
  9.   TWindowedStream = class(TStream)
  10.   private
  11.     FBaseStream: TStream;
  12.     FStartOffset: Int64;
  13.     FWindowSize: Int64;
  14.     FPosition: Int64;
  15.   public
  16.     constructor Create(BaseStream: TStream; StartOffset, WindowSize: Int64);
  17.     function Read(var Buffer; Count: Longint): Longint; override;
  18.     function Write(const Buffer; Count: Longint): Longint; override;
  19.     function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;
  20.     function Size: Int64;virtual;
  21.   end;
  22.  
  23. implementation
  24. { including math is not necessary }
  25. {$if not declared(min)}
  26. function min(const a,b:uint64):Uint64;
  27. begin
  28.   Result := a;
  29.   If b < a then result := b;
  30. end;
  31. {$ifend}
  32.  
  33. constructor TWindowedStream.Create(BaseStream: TStream; StartOffset, WindowSize: Int64);
  34. begin
  35.   inherited Create;
  36.   if not Assigned(BaseStream) then
  37.     raise EArgumentException.Create('BaseStream cannot be nil');
  38.   if (StartOffset < 0) or (WindowSize < 0) then
  39.     raise EArgumentException.Create('StartOffset and WindowSize must be non-negative');
  40.   if StartOffset + WindowSize > BaseStream.Size then
  41.     raise EArgumentException.Create('Window exceeds base stream size');
  42.  
  43.   FBaseStream := BaseStream;
  44.   FStartOffset := StartOffset;
  45.   FWindowSize := WindowSize;
  46.   FPosition := 0;
  47. end;
  48.  
  49. function TWindowedStream.Read(var Buffer; Count: Longint): Longint;
  50. var
  51.   BytesToRead: Int64;
  52. begin
  53.   if FPosition >= FWindowSize then
  54.     Exit(0);
  55.  
  56.   FBaseStream.Position := FStartOffset + FPosition;
  57.   BytesToRead := Min(Count, FWindowSize - FPosition);
  58.   Result := FBaseStream.Read(Buffer, BytesToRead);
  59.   Inc(FPosition, Result);
  60. end;
  61.  
  62. function TWindowedStream.Write(const Buffer; Count: Longint): Longint;
  63. var
  64.   BytesToWrite: Int64;
  65. begin
  66.   if FPosition >= FWindowSize then
  67.     Exit(0);
  68.  
  69.   FBaseStream.Position := FStartOffset + FPosition;
  70.   BytesToWrite := Min(Count, FWindowSize - FPosition);
  71.   Result := FBaseStream.Write(Buffer, BytesToWrite);
  72.   Inc(FPosition, Result);
  73. end;
  74.  
  75. function TWindowedStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
  76. var
  77.   NewPosition: Int64;
  78. begin
  79.   case Origin of
  80.     soBeginning: NewPosition := Offset;
  81.     soCurrent: NewPosition := FPosition + Offset;
  82.     soEnd: NewPosition := FWindowSize + Offset;
  83.   else
  84.     raise EStreamError.Create('Invalid seek origin');
  85.   end;
  86.  
  87.   if (NewPosition < 0) or (NewPosition > FWindowSize) then
  88.     raise EStreamError.Create('Seek out of bounds');
  89.  
  90.   FPosition := NewPosition;
  91.   Result := FPosition;
  92. end;
  93.  
  94. function TWindowedStream.Size: Int64;
  95. begin
  96.   Result := FWindowSize;
  97. end;
  98.  
  99. end.
Example:
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$H+}
  2. uses classes,windowedstream;
  3. var
  4.   FileStream: TFileStream;
  5.   WindowStream: TWindowedStream;
  6.   Buffer: array[0..1023] of Byte;
  7.   BytesRead: Integer;
  8. begin
  9.   FileStream := TFileStream.Create('example.dat', fmCreate or fmOpenRead);
  10.   try
  11.   FileStream.WriteAnsiString(StringOfChar('A', 1024));// just dummy data
  12.   Filestream.Position := 0;
  13.     WindowStream := TWindowedStream.Create(FileStream, 100, 50);
  14.     try
  15.       BytesRead := WindowStream.Read(Buffer, SizeOf(Buffer));
  16.       writeln(PChar(@Buffer[0]));
  17.     finally
  18.       WindowStream.Free;
  19.     end;
  20.   finally
  21.     FileStream.Free;
  22.   end;
  23. end.
Works best on seekable streams.
« Last Edit: July 21, 2025, 09:13:25 am by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

 

TinyPortal © 2005-2018