Move bytes to extern struct

I have an extern struct of exactly x bytes.
I read a file into a slice of bytes ([ ]u8).
How can I move x bytes into that struct?
This saves time reading / manipulating the separate fields.

You can just reinterpret memory without physical move:

const std = @import("std");
const S = extern struct {
    a: u8,
    b: u32 align(1),
};

pub fn main() void {
    const d: [5]u8 = .{0x07, 0x01, 0x02, 0x03, 0x04};
    const p: *const S = @ptrCast(&d);
    std.debug.print("{}\n", .{@sizeOf(S)});
    std.debug.print("{any}\n", .{p.*});
    // 0x04030201 = 67305985
}
3 Likes

Ok that is a nice one to know.
But after reading the bytes two fields have to be swapped, so i would rather copy the bytes first.

pub const SectionHeader = extern struct
{
    pub const STRUCTSIZE: u16 = @sizeOf(SectionHeader); // this must be 10.

    bitcount: u8 = 0,
    checksum: u8 = 0,
    _unused1: u16 = 0,
    decompressed_size: u16 = 0, // big endian on disk
    _unused2: u16 = 0,  
    compressed_size: u16 = 0, // big endian on disk

    pub fn init_from_bytes(bytes: []u8, offset: usize) SectionHeader
    {
        // Here i want to move 10 bytes from bytes[offset..offset + 10] into the struct.
        // After that i need to correct the 2 big endian fields.
    }
}

You can swap them in place:

const std = @import("std");
const S = extern struct {
    a: u8,
    b: u16 align(1), // BE
};

fn swapBytes(d:  *align(1) u16) void{
    _ = d;
    //...
}

pub fn main() void {
    var d: [3]u8 = .{0x07, 0x00, 0x01};
                          // BE here
    const p: *S = @ptrCast(&d);
    std.debug.print("{}\n", .{@sizeOf(S)});
    swapBytes(&p.b);
    std.debug.print("{any}\n", .{p.*});
}

I think you can return *SectionHeader here.
BTW, please, someone who knows, give a tip where stuff like C’s htons() is in Zig stdlib?

no problem it’s in std.mem.nativeToBig and the reverse std.mem.bigToNative i think it’s the name not sure but it’s there

1 Like

Version 3. :slight_smile:

const std = @import("std");
const S = extern struct {
    a: u8,
    b: u16 align(1), // supposed to be in BE byte order
    fn initFromBytes(d: []u8) *S {
        const p: *S = @ptrCast(d);
        p.b = std.mem.bigToNative(u16, p.b);
        return p;
    }
};

pub fn main() void {
    var d: [3]u8 = .{0x07, 0x00, 0x01};
                          // BE here
    const p: *S = S.initFromBytes(&d);
    std.debug.print("{}\n", .{@sizeOf(S)});
    std.debug.print("{any}\n", .{p.*});
    // { .a = 7, .b = 1 } as expected on LE machine
}
1 Like

Thanks! I will play around with that.

Edit: BTW I just came from Delphi from C# from Rust and then moved to Zig, which seems promising. Ultimate control.

I made my choice with brackets and naming :slight_smile:
Brackets on new line: I can’t read asymmetric code.
Everything snake_case except types, which are PascalCase.

See also this topic, very similar.

You can also use std.mem.bytesToValue().

1 Like

Wow. this bytesToValue seems perfect!
Do we finally have a language which understands what is needed?
In C# or Rust it is quite a hell to do this.

bytesToValue calls bytesAsValue which in turn is doing what I proposed in the very beginning (pointer casting, to avoid copying):

/// Given a pointer to an array of bytes, returns a pointer to a value of the specified type
/// backed by those bytes, preserving pointer attributes.
pub fn bytesAsValue(comptime T: type, bytes: anytype) BytesAsValueReturnType(T, @TypeOf(bytes)) {
    return @ptrCast(bytes);
}

yes i saw it in the zig sourcecode.

bytesToValue() returns a copy (if I got it right), so if you really need to keep original data, use bytesToValue() (using my example):

    fn initFromBytes(d: []u8) S {
        var s: S = std.mem.bytesToValue(S, d);
        s.b = std.mem.bigToNative(u16, s.b);
        return s;
    }

Otherwise just

    fn initFromBytes(d: []u8) *S {
        const p: *S = std.mem.bytesAsValue(S, d);
        p.b = std.mem.bigToNative(u16, p.b);
        return p;
    }

You might also consider std.mem.byteSwapAllFields (std library documentation)

1 Like
const std = @import("std");

const S = extern struct {
    a: u8,
    b: u16 align(1), // supposed to be in BE byte order
    c: f32 align(1), // also BE

    fn initFromBytes(d: []u8) *S {
        const p: *S = std.mem.bytesAsValue(S, d);
        std.mem.byteSwapAllFields(S, p);
        return p;
    }
};

pub fn main() void {
    var d: [7]u8 = .{0x07, 0x00, 0x02, 0x40, 0x49, 0x0f, 0xd8};
    const p: *S = S.initFromBytes(&d);
    std.debug.print("{}\n", .{@sizeOf(S)});
    std.debug.print("{any}\n", .{p.*});
    // { .a = 7, .b = 2, .c = 3.141592e0 }
}

That’s really cool!