Casting a slice from u8 to a union/struct/whatever (type punning)

I want to parse a spirv file.
So I load the file using readFileAlloc and get a []u8, and want to use it as if it was a slice of…

const spirv_file_structure = packed union {
    w: u32,
    i: packed struct {
        i: u16,
        c: u16,
    },
};

@ptrCast complains with:

src/loader.zig:211:41: error: TODO: implement @ptrCast between slices changing the length
    const idk: []spirv_file_structure = @ptrCast(buf);

I tried to allocate a new slice (of the same byte size) and use @memcpy, but it also complains:

src/loader.zig:210:5: error: TODO: lower @memcpy to a for loop because the element types have different ABI sizes
    @memcpy(spirv_file, buf);

Which got me to read the @memcpy documentation that says The source element type must support Type Coercion into the dest element type. The element types may have different ABI size, however, that may incur a performance penalty. Isn’t it just a memcpy ? If it does coercion why not call it… memtransform or something ? Anyway i’m getting off topic.

I got what i needed by using std.zig.c_builtins.__builtin_memcpy, but i want to know if there’s a way to just type pun a slice of something into a slice of something else.

Use std.mem.bytesAsSlice.

4 Likes

Hello @gens, welcome to the forum!

I edited the formatting of your post a tiny bit, putting some more parts into single backticks like this:

`[]u8`
`@ptrCast` complains with:

The slice brackets were displayed as a box on my end (may depend on font ligatures?), with the code formatting it is more readable.

I think it is best to use inline blocks even for small code pieces, for example code that contains @ might confuse discourse (the forum software), because that is its way to mention other users in normal text. If that user doesn’t exist it is probably not a big problem, but it might get confusing if in the future somebody joins with that username.

Sure. Thanks @LucasSantos91 and to you.

On topic, I looked at how std.mem.bytesAsSlice does this and… one part is compiler internal/dependent (making a new pointer type from parameters).

It is using Zigs comptime feature, but from looking at the code, I don’t see any “compiler internal” code there. As far as I understood it, there aren’t supposed to be compiler internals within std/the standard library.

Can you rephrase it if you mean something else, maybe I am misunderstanding you?

Creating new types using comptime is a feature of the language.

bytesAsSlice calls CopyPtrAttrs that uses @Type and sets std.builtin.Type.Pointer.Size that says This data structure is used by the Zig language code generation and therefore must be kept in sync with the compiler implementation (it’s set to .Many in this case).

I guess I did phrase it wrong by grasping, as the Size enum will probably not remove things.
Anyway it’s still to early for me to construct pointer types :), but good to know how if I have to.