Operation tricks with packed structs with explicit size

I saw some complicated (new?) stories somewhere about packed structs with explicit size like:

const Pack = packed struct (u8)
{
    field1: bool,
    field2: bool,
    field3: bool,
}

What is the difference when omitting the (u8)?
What is the explicit meaning of the added (u8)?

Then some question about performance (yes I still am not able to read that assembler view in godbolt)…

Is there a possiblity to bitwise AND / OR / XOR etc. two structs? That would be nice…
If not: what is the smartest way to do it?

Is the compiler so extremely smart that when executing:

pack.field1 = true;
pack.field2 = false;
pack.field3 = true;

to immediately stuff in the binary byte instead of executing 3 statements?

A backing integer is inferred from the total bit size of the fields.
When you provide a backing integer–after packed struct–zig compiler enforces it and an error is displayed if the inferred bit size is different from the provided one.

It is possible using bitCasting to and from the backing type:

const xorResult: Pack = @bitCast(@as(u8, @bitCast(a)), @as(u8, @bitCast(b)));

Bitcasts of small values like this are free.

I suggest using godbolt to answer such questions. In this godbolt example, you can see that your assumption is correct and the compiler just inserts 5 (101₂) into the struct directly, however the compiler is doing some extra work to preserve the padding bits of the struct. In this case a direct initialization is better.

4 Likes

Absolutely crazy, Nice.
Now I see the trick: using export to just see the function.

1 Like