Jacek Laskowski 57 Posted February 9, 2021 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
David Heffernan 2345 Posted February 9, 2021 That's not an array of TBytes. It's not clear to me what your question is yet. Share this post Link to post
Cristian Peța 103 Posted February 9, 2021 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
Lars Fosdal 1792 Posted February 9, 2021 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
Jacek Laskowski 57 Posted February 9, 2021 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
Anders Melander 1782 Posted February 9, 2021 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
David Heffernan 2345 Posted February 9, 2021 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
Lars Fosdal 1792 Posted February 9, 2021 Nothing beats pointers to arrays for InterOp - even today. 1 1 Share this post Link to post
Remy Lebeau 1393 Posted February 9, 2021 (edited) 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 February 9, 2021 by Remy Lebeau 1 1 Share this post Link to post