Ran into a compile error on Mac that I don’t get on Linux.
var chunk_decoded: [8]u5 = ...;
var bytes: [5]u8 = @bitCast(chunk_decoded);
which gives me this error when the target is mac or windows, but not linux. I get this with aarch64 and x86_64
@bitCast size mismatch: destination type '[5]u8' has 40 bits but source type '[8]u5' has 61 bits
On linux, it compiles (and runs) fine, just as I expect.
Is this a bug or is there something I’m missing about arbitrarily-sized integers?
I don’t see how this worked on Linux. As far as I know, Zig doesn’t pack arrays of integers tightly, like it does for packed structs. The error you got is the behavior I would have expected for all platforms. Each u5 takes one byte, with the top 3 bits being padding, so [8]u5 takes 8 bytes = 64 bits, with the top 3 bits of the last integer being left out.
The only way this bitcast would work is if Zig is packing this array tightly, which is kinda cool if it is, but I don’t think it is.
3 Likes
What you’re saying makes sense, and a little debugging confirms that [8]u5 does take up 64 bits.
std.debug.print("@bitSizeOf [8]u5 {d}, @sizeOf [8]u5 {d}\n", .{@bitSizeOf([8]u5), @sizeOf([8]u5)});
@bitSizeOf [8]u5 40, @sizeOf [8]u5 8
The @sizeOf docs even say “This size may contain padding bytes.”
But all I can say for sure is that the code compiles and runs correctly on linux.
base32.zig · GitHub if you want to confirm for yourself.
1 Like
This test also passes just fine.
test "bitCast" {
var eight_fives = [8]u5{ 0b11000, 0b11001, 0b11010, 0b11011, 0b11100, 0b11101, 0b11110, 0b11111 };
std.mem.reverse(u5, &eight_fives);
var five_eights: [5]u8 = @bitCast(eight_fives);
std.mem.reverse(u8, &five_eights);
const five_eights_b = [5]u8{ 0b11000110, 0b01110101, 0b10111110, 0b01110111, 0b11011111 };
try testing.expectEqualSlices(u8, &five_eights, &five_eights_b);
}
Okay, I don’t understand exactly why it works on linux but not macos or windows. But I can get it to compile cross-platform if I use a packed struct instead of an array, and then @bitCast that.
An integer field uses exactly as many bits as its bit width. For example, a u5 will use 5 bits of the backing integer.
const EightFives = packed struct {
a: u5,
b: u5,
c: u5,
d: u5,
e: u5,
f: u5,
g: u5,
h: u5,
};
const eight_fives: EightFives = ... // same contents as my [8]u5 previously
var bytes: [5]u8 = @bitCast(eight_fives);
std.mem.reverse(u8, &bytes);
4 Likes