Jump to content
Jacek Laskowski

Casting pointer to TBytes

Recommended Posts

I have a method (from external code) that takes an array of TBytes as a parameter:

procedure Something(const aData : TBytes);

and I need to pass to it a (read-only) memory area, allocated by GetMem().

 

Is it possible to cast this to TBytes (or some other trick) without copying the memory to a separate variable of type TBytes?

 

Share this post


Link to post

You can not because a dynamic array is not just the data. There is also the length and reference count.

But you can pass a pointer and length of that memory,

Share this post


Link to post

By "need to pass it to a (read only) memory area" - what do you actually mean?
Do you want to get data from aData or put it to aData?
 

Edit: ok, I read that again.

 

What you want to do, is to allocate your TBytes array instead of using GetMem.

SetLength(MyData, Size);

Whatever fills your GetMem block, should also be able to fill @MyData[0] with the length you have preallocated?
You then pass that as Something(MyData);

Share this post


Link to post

Simplified version of the question: is it possible to pass a memory area (pointer) to a method that accepts an array of bytes (TBytes) without copying?

 

If it's not possible, there's no trick, that's too bad. I'm looking for another solution. Thanks for the help...

Share this post


Link to post
6 minutes ago, Jacek Laskowski said:

is it possible to pass a memory area (pointer) to a method that accepts an array of bytes (TBytes) without copying?

No - for the reason Cristian stated.

Share this post


Link to post

You need to have a function that accepts a pointer to the byte array, and its length. Then you can pass your raw memory pointer, or pass Pointer(Bytes), Length(Bytes) when you have a TBytes to hand.

Share this post


Link to post
3 hours ago, Jacek Laskowski said:

Simplified version of the question: is it possible to pass a memory area (pointer) to a method that accepts an array of bytes (TBytes) without copying?

The only way to do that is to make the memory area MIMIC a real TBytes, by adjusting the size of the GetMem() allocation to include a fake TBytes header whose reference count is set to 1 or higher so the method doesn't try to free the allocated memory internally as it passes around and uses the TBytes.  For example:

type
  // these are defined only in the System unit's implementation, so
  // they need to be defined manually in your code...
  PDynArrayRec = ^TDynArrayRec;
  TDynArrayRec = packed record
  {$IFDEF CPUX64}
    _Padding: LongInt; // Make 16 byte align for payload..
  {$ENDIF}
    RefCnt: LongInt;
    Length: NativeInt;
  end;

var
  MemBlk: Pointer;
  MyByteArr: PByte;
begin
  //GetMem(MyByteArr, DesiredSize);
  GetMem(MemBlk, SizeOf(TDynArrayRec) + DesiredSize);
  try
    PDynArrayRec(MemBlk).RefCnt := 1;
    PDynArrayRec(MemBlk).Length := DesiredSize;
    MyByteArr := PByte(MemBlk) + SizeOf(TDynArrayRec);

    // fill MyByteArr as needed ...

    Something(TBytes(MyByteArr));

    // ...
    
  finally
    //FreeMem(MyByteArr);
    FreeMem(MemBlk);
  end;
end;

If that is not an option, and changing the method (or calling a different method) is also not possible, then your only remaining options are to either:

 

- copy the GetMem() data to a temp TBytes, and then copy it back afterwards if needed.

 

- change the GetMem() allocation to TBytes to begin with.

Edited by Remy Lebeau
  • Like 1
  • Thanks 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

×