TBytesStream

I guess that you all already worked with a TBytesStream. It is derived from a TMemoryStream and offers direct access to the underlying TBytes:

  
...
TBytesStream = class(TMemoryStream)
  private
  ...
    property Bytes: TBytes read FBytes;
  end;
...

So far so good, the problem is that a TMemoryStream allocates more memory than the current Size. The reason is that not necessary to realloc memory for every write operation. You can see this in the Realloc method

...
const
  MemoryDelta = $2000; { Must be a power of 2 }
...
function TBytesStream.Realloc(var NewCapacity: Longint): Pointer;
begin
  if (NewCapacity > 0) and (NewCapacity <> FSize) then
    NewCapacity := (NewCapacity + (MemoryDelta - 1)) and not (MemoryDelta - 1);
  Result := Pointer(FBytes);
  if NewCapacity <> FCapacity then
  begin
    SetLength(FBytes, NewCapacity);
    Result := Pointer(FBytes);
  ...
  end;
end;
...

As you can see the NewCapacity is larger than the Size of the Stream. If you look deeper at the code you can see that the NewCapacity is of power of 2.

But anyway, the point is that the property Bytes can include more bytes than the Size of the Stream. That’s why you must be very careful if you want to access directly to the Bytes property.

In order to avoid any issues I created a record helper for TBytes with the property BytesReal that includes the used bytes:

...
  TBytesStreamHelper = class helper for TBytesStream
  strict private
    function get_BytesReal: TBytes; 
  public
    property BytesReal: TBytes read get_BytesReal;
  end;
...
function TBytesStreamHelper.get_BytesReal: TBytes;
begin
  if Capacity > Size then
    Result := Copy(Bytes, 0, Size)
  else
    Result := Bytes;
end;
...

If you like than add the TBytesStreamHelper to your project and use the new property BytesReal.
 

This entry was posted in Delphi, RAD Studio 10.4 Sydney, Tips and Tricks, Uncategorized. Bookmark the permalink.