I’m getting hung up in my brain when trying to map a list from one type to another. I try to avoid using allocators when possible, and something inside me tells me I shouldn’t have to allocate here, but I’m a GC boy, so I have nothing to base that feeling on.
I’m converting a rust program to zig, and I’m trying to read bytes from a zmq socket into a tagged union. It looks like this:
pub const WorkerStatusTag = enum {
idle,
busy,
};
pub const WorkerStatus = union(WorkerStatusTag) {
/// The worker is currently idle.
idle: void,
/// The worker is currently processing one or more requests.
/// each u128 is a Ulid identifier for the request being handled.
busy: []u128,
fn parse(bytes: []const u8) !WorkerStatus {
switch (bytes[0]) {
0x1 => return .idle,
// TODO: Read bytes[1..] in 16 byte chunks and return the array.
0x2 => return .{ .busy = .{} },
else => unreachable, // FIXME: return ParseError
}
}
};
The protocol is simple: either the worker sends x01 when it has no work, or it sends x02 followed by the list of ids in big endian format.
Since all the data is available in bytes, I want to just return a re-mapping of it in the structured types I’m looking for, and then copy/transfer ownership of it later when I update the struct that holds the latest status of all the workers.
Thank you. This does appear to be the correct answer, but I’m having trouble solving the panic. I looked at the docs on alignment, I’ve gotten this far (none of my attempts work):
zig build test
test
└─ run test failure
align: 16
len: 48
thread 2387579 panic: incorrect alignment
/home/spencer/git.verticalaxion.com/vai/src/Worker.zig:77:102: 0x12b2935 in parse (root.zig)
const requests: []Client.Request.Id = std.mem.bytesAsSlice(u128, @as([]align(16) u8, @alignCast(bytes[1..])));
^
/home/spencer/git.verticalaxion.com/vai/src/Worker.zig:200:43: 0x12b339f in test.parse (root.zig)
const _status = try WorkerStatus.parse(&buffer);
^
/nix/store/q7ym78ggwsd3ahbcamjqafsagvsg9hvi-zig-0.15.1/lib/zig/compiler/test_runner.zig:130:29: 0x122d433 in mainServer (test_runner.zig)
test_fn.func() catch |err| switch (err) {
^
/nix/store/q7ym78ggwsd3ahbcamjqafsagvsg9hvi-zig-0.15.1/lib/zig/compiler/test_runner.zig:64:26: 0x122e6b6 in main (test_runner.zig)
return mainServer() catch @panic("internal test runner failure");
^
/nix/store/q7ym78ggwsd3ahbcamjqafsagvsg9hvi-zig-0.15.1/lib/zig/std/start.zig:618:22: 0x1228ce5 in main (std.zig)
root.main();
^
???:?:?: 0x7f68f6a2a4d7 in ??? (libc.so.6)
Unwind information for `libc.so.6:0x7f68f6a2a4d7` was not available, trace may be incomplete
???:?:?: 0x7f68f6a2a59a in ??? (libc.so.6)
???:?:?: 0x12ffe94 in ??? (???)
error: while executing test 'Worker.test.parse', the following command terminated with signal 6 (expected exited with code 0):
./.zig-cache/o/a53f244ce28a62ecf202d535b758adc4/test --cache-dir=./.zig-cache --seed=0x4efcb2b6 --listen=-
Build Summary: 8/10 steps succeeded; 1 failed; 32/32 tests passed
test transitive failure
└─ run test failure
error: the following build command failed with exit code 1:
.zig-cache/o/32db72e130a577b8042338e6aa26fb9c/build /nix/store/q7ym78ggwsd3ahbcamjqafsagvsg9hvi-zig-0.15.1/bin/zig /nix/store/q7ym78ggwsd3ahbcamjqafsagvsg9hvi-zig-0.15.1/lib/zig /home/spencer/git.verticalaxion.com/vai .zig-cache /home/spencer/.cache/zig --seed 0x4efcb2b6 -Z6a794ff4f105c0f4 test
pub const WorkerStatus = union(WorkerStatusTag) {
/// The worker is currently idle.
idle: void,
/// The worker is currently processing one or more requests.
/// each u128 is a Ulid identifier for the request being handled.
busy: []align(1) u128,
However, the endian conversion functions in the stdlib take a []T rather than a []align(x) T , so you’ll probably run into issues there. You might need to implement that manually.
If you insist on no allocations, and can’t fix up the alignment in some other way, maybe use @ptrCast and then manually iterate over every element of the slice and convert it using busy[i] = std.mem.bigToNative(u128, busy[i])?
On another note: Does the ULID actually need to be converted, or could you just treat it as an opaque 16 bytes? So instead, busy would be declared as:
pub const WorkerStatus = union(WorkerStatusTag) {
/// The worker is currently idle.
idle: void,
/// The worker is currently processing one or more requests.
/// each u128 is a Ulid identifier for the request being handled.
busy: []Ulid,
const Ulid = [16]u8;
Semantically, I think it might make more sense to treat it that way. It’s not an integer, it’s a 128-bit / 16-byte unique identifier. As it’s a ULID rather than UUID, if you need to sort it, you can use std.mem.order or std.mem.lessThan to sort it lexicographically.