views:

296

answers:

2

Those 3 types are very similar...

TArray is the generic version of TBytes. Both can be casted to PByteArray and used as buffer for calls to Windows API. (with the same restrictions as string to Pchar).

What I would like to know: Is this behavior "by design" or "By Implementation". Or more specifically, could it break in future release?

//Edit As stated lower... What I really want to know is: Is this as safe to typecast TBytes(or TArray) to PByteArray as it is to typecast String to PChar as far as forward compatibility is concerned. (Or maybe AnsiString to PAnsiChar is a better exemple ^_^)

+2  A: 

Simply put, an array of bytes is an array of bytes, and as long as the definitions of a byte and an array don't change, this won't change either. You're safe to use it that way, as long as you make sure to respect the array bounds, since casting it out of Delphi's array types nullifies your bounds checking.

EDIT: I think I see what you're asking a bit better now.

No, you shouldn't cast a dynamic array reference to a C-style array pointer. You can get away with it with strings because the compiler helps you out a little.

What you can do, though, is cast a pointer to element 0 of the dynamic array to a C-style array pointer. That will work, and won't change.

Mason Wheeler
Actually, you are wrong... an "Array of byte" is not an array of byte... It's a pointer to a dynamic array of byte which is reference counted and length managed (Pretty much like strings). What I want to know is: Is this as safe to typecast TBytes(or TArray<Byte>) to PByteArray as it is to typecast String to PChar as far as forward compatibility is concerned.
Ken Bourassa
An "array of Byte" **is** an array of byte. The only difference is that it is surfaced to the runtime primarily as a *reference type*, rather than a *value type* (as in the case of a static array). The compiler knows this of course and will (ok, should) take care of the typecast for you in the same way that it does for String <> PChar (often referred to as "compiler magic" - i.e. code generation that relies on the compiler being aware of it's own internals).
Deltics
@Ken: OK, I see what you mean. Editing my reply.
Mason Wheeler
@DelticsWell, all the compiler does pretty much nothing to cast a string to PChar. It puts the string pointer in the PChar, unless the string is empty where it will return a pointer to a #0 character.String DOES have a 12 bytes header (as of D2010, was 8 bytes before unicode) and reserve Length+1 char of memory. (String[Length+1] is the implicit null terminating character every string automatically reserve so they can be casted to PChar). So reference type... pointer... pretty much the same to me. Except that it has some additional icing on it. (Reference count, etc)
Ken Bourassa
@Ken: it may not do much, but it knows that it can allow it
Deltics
A: 

Two of those types are similar (identical in fact). The third is not.

TArray is declared as "Array of Byte", as is TBytes. You missed a further very relevant type however, TByteArray (the type referenced by PByteArray).

Being a pointer to TByteArray, PByteArray is strictly speaking a pointer to a static byte array, not a dynamic array (which the other byte array types all are). It is typed in this way in order to allow reference to offsets from that base pointer using an integer index. And note that this indexing is limited to 2^15 elements (0..32767). For arbitrary byte offsets (> 32767) from some base pointer, a PByteArray is no good:

var
  b: Byte;
  ab: TArray<Byte>;
  pba: PByteArray;
begin
  SetLength(ab, 100000);
  pba := @ab;             // << No cast necessary - the compiler knows (magic!)
  b   := pba[62767];      // << COMPILE ERROR!
end;

i.e. casting an Array of Byte or a TArray to a PByteArray is potentially going to lead to problems where the array has > 32K elements (and the pointer is passed to some code which attempts to access all elements). Casting to an untyped pointer avoids this of course (as long as the "recipient" of the pointer then handles access to the memory reference by the pointer appropriately).

BUT, none of this is likely to change in the future, it is merely a consequence of the implementation details that have long since applied in this area. The introduction of a syntactically sugared generic type declaration is a kipper rouge.

Deltics
On a side not, if the need arise, the 32767 limit can be bypassed by redeclaring locally the type this way.type PByteArray = ^TByteArray; TByteArray = array[0..MaxInt - 1] of Byte;
Ken Bourassa
"Kipper rouge"? What does that mean?
Rob Kennedy
Sorry - Kipper Rouge = "Red Herring" :)
Deltics