www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Casting int[] to ubyte[]: element wise cast or slice cast

reply Dennis <dkorpel gmail.com> writes:
I assumed that casting an int[] to a ubyte[] would keep all bytes 
and quadruple the length of the original array. But when the 
array is a literal, it keeps the same length but truncates every 
int element to a ubyte:

```
import std.stdio;

void main()
{
     // enum:
     enum litA = [0x10203040, 0x50607080];
     pragma(msg, typeof(litA));
     ubyte[] a = cast(ubyte[]) litA;

     // auto:
     auto litB = [0x10203040, 0x50607080];
     pragma(msg, typeof(litB));
     ubyte[] b = cast(ubyte[]) litB;

     writeln(a);
     writeln(b);
}
```

Prints:
```
int[]
int[]
[64, 128]
[64, 48, 32, 16, 128, 112, 96, 80]
```

I looked at the spec and found this:

https://dlang.org/spec/expression.html#cast_expressions
"Casting a dynamic array to another dynamic array is done only if 
the array lengths multiplied by the element sizes match. The cast 
is done as a type paint, with the array length adjusted to match 
any change in element size. If there's not a match, a runtime 
error is generated."

So is this a bug or am I missing something?
Feb 15
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Feb 15, 2019 at 04:17:12PM +0000, Dennis via Digitalmars-d-learn wrote:
 I assumed that casting an int[] to a ubyte[] would keep all bytes and
 quadruple the length of the original array. But when the array is a
 literal, it keeps the same length but truncates every int element to a
 ubyte:
That's correct. If you want to *transcribe* a ubyte[] to an int[], what you want is probably something like this: import std.conv : to; ubyte[] data = ...; int[] result = data.map!(b => b.to!int).array; [...]
 I looked at the spec and found this:
 
 https://dlang.org/spec/expression.html#cast_expressions
 "Casting a dynamic array to another dynamic array is done only if the
 array lengths multiplied by the element sizes match. The cast is done
 as a type paint, with the array length adjusted to match any change in
 element size.  If there's not a match, a runtime error is generated."
 
 So is this a bug or am I missing something?
Read it again carefully: the casting is done "only if the array lengths multiplied by the element sizes match". In other words: T.sizeof * T[].length == U.sizeof * U[].length Why? because that's the only case where you can reinterpret the .sizeof*.length bytes as an array of a different type. There is no conversion, the cast works as a reinterpretation. It's by design. T -- I'm still trying to find a pun for "punishment"...
Feb 15
parent reply Dennis <dkorpel gmail.com> writes:
On Friday, 15 February 2019 at 17:18:28 UTC, H. S. Teoh wrote:
 Why? because that's the only case where you can reinterpret the 
 .sizeof*.length bytes as an array of a different type.  There 
 is no conversion, the cast works as a reinterpretation.  It's 
 by design.
But my problem is that it *isn't* reinterpreted in the case of an enum / array literal, the cast actually transcribes similar to your .map example. Look carefully at the difference of case A and B in my example code: the only difference is whether the int array uses auto or enum. I'm not going from ubyte to int, I'm going from int to ubyte which should always be possible by multiplying the length by 4, in my case: int.sizeof * 2 == ubyte.sizeof * 8 == 8 This happens in case B of my example code, but not in case A. There the cast goes from 4*2 bytes to 1*2 instead of 1*8 and I don't get a runtime error.
Feb 15
parent kdevel <kdevel vogtner.de> writes:
On Friday, 15 February 2019 at 18:11:11 UTC, Dennis wrote:

Your should report our observation: https://issues.dlang.org

```
void main ()
{
    enum A = [0x10203040, 0x50607080]; // shall enum behave like 
immutable?
    auto B = [0x10203040, 0x50607080];
    assert (A == B);
    auto p = cast (ubyte []) A;
    auto q = cast (ubyte []) B;
    assert (p == q);
}
```
Feb 16