64 lines
1.6 KiB
Zig
64 lines
1.6 KiB
Zig
|
const std = @import("std");
|
||
|
|
||
|
const HeaderSize = @bitSizeOf(Header) / 8;
|
||
|
const Magic = [4]u8{ 0xf0, 0x9f, 0xa4, 0xb7 };
|
||
|
const Version = 0;
|
||
|
const Bom = 0x1234;
|
||
|
|
||
|
const Header = packed struct {
|
||
|
magic: [4]u8,
|
||
|
version: u8,
|
||
|
bom: u16,
|
||
|
num_shells: u8,
|
||
|
num_users: u32,
|
||
|
num_groups: u32,
|
||
|
offset_cmph_uid2user: u32,
|
||
|
offset_cmph_groupname2group: u32,
|
||
|
offset_cmph_username2user: u32,
|
||
|
offset_idx: u32,
|
||
|
offset_groups: u32,
|
||
|
offset_users: u32,
|
||
|
offset_groupmembers: u32,
|
||
|
offset_additional_gids: u32,
|
||
|
|
||
|
// TODO(motiejus) should we be returning *Header instead? That way all
|
||
|
// stacks could be smaller by 48 bytes, and we would be referring to
|
||
|
// it's fields in the mmap'ed region.
|
||
|
pub fn init(blob: [HeaderSize]u8) Header {
|
||
|
return @bitCast(Header, blob);
|
||
|
}
|
||
|
|
||
|
pub fn asArray(self: *const Header) [HeaderSize]u8 {
|
||
|
return @bitCast([HeaderSize]u8, self.*);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const testing = std.testing;
|
||
|
|
||
|
test "header size is byte-aligned" {
|
||
|
try testing.expectEqual(HeaderSize * 8, @bitSizeOf(Header));
|
||
|
}
|
||
|
|
||
|
test "header pack and unpack" {
|
||
|
const header = Header{
|
||
|
.magic = Magic,
|
||
|
.version = Version,
|
||
|
.bom = Bom,
|
||
|
.num_shells = 0,
|
||
|
.num_users = 0,
|
||
|
.num_groups = 0,
|
||
|
.offset_cmph_uid2user = 0,
|
||
|
.offset_cmph_groupname2group = 0,
|
||
|
.offset_cmph_username2user = 0,
|
||
|
.offset_idx = 0,
|
||
|
.offset_groups = 0,
|
||
|
.offset_users = 0,
|
||
|
.offset_groupmembers = 0,
|
||
|
.offset_additional_gids = 0,
|
||
|
};
|
||
|
|
||
|
const blob = header.asArray();
|
||
|
const header2 = Header.init(blob);
|
||
|
try testing.expectEqual(header, header2);
|
||
|
}
|