Search code examples
castingtype-conversionuniond

What does cast do exactly?


I was playing with integer array hashing and the different ways to go from a representation to the other. I ended up with the following:

void main(string[] args) {
    import std.algorithm, std.array, std.conv, std.stdio, std.digest.md;

    union hashU {
        ubyte[] hashA;
        int[]   hashN;
    };

    hashU a;
    auto md5 = new MD5Digest();

    a.hashN = [1, 2, 3, 4, 5];

    /* Using an union, no actual data conversion */
    md5.put( a.hashA );
    auto hash = md5.finish();
    writeln(hash);
    // [253, 255, 63, 4, 193, 99, 182, 232, 28, 231, 57, 107, 18, 254, 75, 175]

    /* Using a cast... Doesn't match any of the other representations */
    md5.put( cast(ubyte[])(a.hashN) );
    hash = md5.finish();
    writeln(hash);
    // [254, 5, 74, 210, 231, 185, 139, 238, 103, 63, 159, 242, 45, 80, 240, 12]

    /* Using .to! to convert from array to array */
    md5.put( a.hashN.to!(ubyte[]) );
    hash = md5.finish();
    writeln(hash);
    // [124, 253, 208, 120, 137, 179, 41, 93, 106, 85, 9, 20, 171, 53, 224, 104]

    /* This matches the previous transformation */
    md5.put( a.hashN.map!(x => x.to!ubyte).array );
    hash = md5.finish();
    writeln(hash);
    // [124, 253, 208, 120, 137, 179, 41, 93, 106, 85, 9, 20, 171, 53, 224, 104]
}

My question is the following: what does the cast do? I'd have expected it to do either the same as .to! or the union trick, but it doesn't seem so.


Solution

  • As far as I know, cast simply tells the compiler to start talking to the chunk of memory that you've cast as the type you've cast it to.

    The last two options in your example actually convert the numbers to the new type, and so do some actual work. Which is why they end up being the same values.

    The issue in your first two examples is that the int[] is larger in memory than the ubyte[]. (4 bytes per element vs 1 byte per element)

    I've edited your first two methods:

        /* Using an union, no actual data conversion */
    md5.put( a.hashA );
    auto hash = md5.finish();
    writefln("Hash of: %s -> %s", a.hashA, hash);
    // Hash of: [1, 0, 0, 0, 2] -> [253, 255, 63, 4, 193, 99, 182, 232, 28, 231, 57, 107, 18, 254, 75, 175] 
    // notice 5 bytes in first array
    
    /* Using a cast... Doesn't match any of the other representations */
    md5.put( cast(ubyte[])(a.hashN) );
    hash = md5.finish();
    writefln("Hash of: %s -> %s", cast(ubyte[])(a.hashN), hash);
    // Hash of: [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0] -> [254, 5, 74, 210, 231, 185, 139, 238, 103, 63, 159, 242, 45, 80, 240, 12] 
    // notice 20 bytes (4 x 5 bytes) in first array
    

    So in the first, you're reading the length of ubyte[]'s bytes. In the second your casting the length of int[]'s length to ubyte[].

    Edit: Prob wasn't clear. A union is pretty dumb, it simply stores all the values in the same memory. When you go to read any of them out, it will only read X bits of that memory, depending on the length of the type you're reading.

    So because you're reading the int[] THEN casting it, it reads all 20 of the bytes and casts them to a ubyte[]. Which is of course different than just reading the 5 bytes of the ubyte[] variable.

    I think I made sense there :)