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
}
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
Version 3.
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
}
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
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().
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)
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!