zig icon indicating copy to clipboard operation
zig copied to clipboard

std.mem.writeInt doesn't handle non-standard int sizes

Open Nairou opened this issue 9 months ago • 3 comments

Zig Version

0.12.0

Steps to Reproduce and Observed Behavior

test "writeInt error" {
    const buffer: [16]u8 = undefined;
    std.mem.writeInt( u2, &buffer, 1, .little );
}

Output:

/nix/store/vf2rryw77159s12x867whp05ayb7icpj-zig-0.12.0/lib/std/mem.zig:1807:52: error: exact division produced remainder
pub inline fn writeInt(comptime T: type, buffer: *[@divExact(@typeInfo(T).Int.bits, 8)]u8, value: T, endian: Endian) void {
                                                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/nairou/Downloads/test_error.zig:5:21: note: called from here
    std.mem.writeInt( u2, &buffer, 1, .little );
    ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~

Expected Behavior

Previous writeIntLittle() behavior, where the calculated number of bytes gets rounded up to the next whole byte, rather than assumed to already be a multiple of 8 bits.

Nairou avatar May 06 '24 01:05 Nairou

The docs say that integers passed to writeInt must have a bit width divisible by 8. I believe the function for this case is now writePackedInt: https://ziglang.org/documentation/0.12.0/std/#std.mem.writePackedInt

Lzard avatar May 06 '24 06:05 Lzard

I considered writePackedInt, but that would give a different result. If I have, say, a u21 field in a struct, I'd like it padded out to 32-bits in the stream, rather than offsetting the rest of the stream by an arbitrary number of bits.

I've gotten around the issue by wrapping the int in std.math.ByteAlignedInt before writing, but it seems like a weird/arbitrary restriction to require this.

Nairou avatar May 06 '24 14:05 Nairou

I agree with Nairou, this makes it harder to use the function for no reason. Plus, it's not clear how you can do this. Maybe better documentation is the solution.

Imagine you have the following:

var out_buffer: []u8 = try allocator.alloc(u8, some_length);
var some_number: u64 = 999;
var some_position: usize = <unknown at comptime>;

std.mem.writeInt(u64, out[some_position..some_position+8], some_number, .big);

That example throws:

expected type '*const [8]u8', found '[]u8'

What is the correct way to fix this?

Edit: Nairou's solution of using std.math.ByteAlignedInt does not work here because you need to know the size at compile time.

margual56 avatar Aug 07 '24 07:08 margual56