Jump to content
MarkShark

Using SetLength(Self) inside a record helper for TBytes

Recommended Posts

I was writing a simple record helper for TBytes and realized that some of my routines call SetLength.  I'm wondering if this is a problem since the array could/will be reallocated in memory.

 

Simple example:

  TTestBytesHelper = record helper for TBytes
  private
    function GetLength: Integer; inline;
  public
    function ReadFromStream(AStream: TStream; ACount: Integer): Integer;
    property Length: Integer read GetLength;
  end;

{ TTestBytesHelper }

function TTestBytesHelper.GetLength: Integer;
begin
  Result := System.Length(Self);
end;

function TTestBytesHelper.ReadFromStream(AStream: TStream; ACount: Integer): Integer;
begin
  // Wondering about this SetLength call!
  System.SetLength(Self, ACount);
  Result := AStream.Read(Self, ACount);
end;

In my testing the ReadFromStream function does seem to work fine.  I'm just wondering if calling SetLength on Self while in a record helper is ok to do.  Thanks for any insight!

Share this post


Link to post
17 hours ago, MarkShark said:

I was writing a simple record helper for TBytes and realized that some of my routines call SetLength.  I'm wondering if this is a problem since the array could/will be reallocated in memory.

 

Simple example:


  TTestBytesHelper = record helper for TBytes
  private
    function GetLength: Integer; inline;
  public
    function ReadFromStream(AStream: TStream; ACount: Integer): Integer;
    property Length: Integer read GetLength;
  end;

{ TTestBytesHelper }

function TTestBytesHelper.GetLength: Integer;
begin
  Result := System.Length(Self);
end;

function TTestBytesHelper.ReadFromStream(AStream: TStream; ACount: Integer): Integer;
begin
  // Wondering about this SetLength call!
  System.SetLength(Self, ACount);
  Result := AStream.Read(Self, ACount);
end;

In my testing the ReadFromStream function does seem to work fine.  I'm just wondering if calling SetLength on Self while in a record helper is ok to do.  Thanks for any insight!

It's OK, a record or helper is just way to add methods for a type, they always work on the instance of the type you call the added method on.

  • Like 2

Share this post


Link to post
unit Unit2;

interface

uses
  System.SysUtils,
  System.Classes;

type
  TMyHelpToTBytes = record helper for TBytes
  private
    function MyGetLen: integer;
  public
    property MyLen: integer read MyGetLen;
    function MyGetBytes(const AStream: TStream; const AStart: integer = 0; ACount: integer = 32): TBytes;
    function MyBytesAsString: string;
  end;

implementation

{ TMyHelpToTBytes }

function TMyHelpToTBytes.MyGetLen: integer;
begin
  result := Length(Self);
end;

function TMyHelpToTBytes.MyGetBytes(const AStream: TStream; const AStart: integer = 0; ACount: integer = 32): TBytes;
begin
  result := [];
  //
  if (AStream = nil) or {(AStart < 0 ) or} (ACount < 1) then
    exit;
  //
  // maybe some protection here to avoid "AV"...?
  if ((AStart + ACount) > AStream.Size) then
    ACount := AStream.Size - AStart;
  //
  if ACount > 0 then
    begin
      Setlength(result, ACount);
      //
      AStream.Position := AStart;
      AStream.Read(result, ACount);
    end;
end;

function TMyHelpToTBytes.MyBytesAsString: string;
begin
  result := '';
  //
  for var B in Self do
    result := result + ',(' + B.ToString + ')';
  //
  result := result.Remove(0, 1);
end;

end.

 

uses
  Unit2;

procedure TForm1.Button1Click(Sender: TObject);
var
  MyBytes : TBytes;
  MyStream: TMemoryStream;
begin
  Memo1.Text := 'MyBytes Len = ' + MyBytes.MyLen.ToString + ', time: ' + TimeToStr(now);
  //
  MyStream := TMemoryStream.Create;
  try
    MyStream.LoadFromFile('..\..\Unit1.pas');
    //
    MyBytes := MyBytes.MyGetBytes(MyStream, random(100), random(1000));
    //
    Memo1.Lines.Add('MyBytes Len = ' + MyBytes.MyLen.ToString);
    Memo1.Lines.Add(MyBytes.MyBytesAsString);
    //
    MyBytes := MyBytes.MyGetBytes(MyStream, random(100), random(500));
    //
    Memo1.Lines.Add('MyBytes Len = ' + MyBytes.MyLen.ToString);
    Memo1.Lines.Add(MyBytes.MyBytesAsString);
    //
    MyBytes := MyBytes.MyGetBytes(MyStream, random(100), random(1500));
    //
    Memo1.Lines.Add('MyBytes Len = ' + MyBytes.MyLen.ToString);
    Memo1.Lines.Add(MyBytes.MyBytesAsString);
  finally
    MyStream.Free;
  end;
end;

initialization

ReportMemoryLeaksOnShutdown := true;

end.

 

image.thumb.png.a743a37dd41009d4f6df4bbf1459eebf.png

Edited by programmerdelphi2k
  • Haha 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×