diff --git a/src/header.zig b/src/header.zig index 9872271..6a6169a 100644 --- a/src/header.zig +++ b/src/header.zig @@ -1,118 +1,105 @@ const std = @import("std"); -const shell = @import("shell.zig"); +const native_endian = @import("builtin").target.cpu.arch.endian(); +const mem = std.mem; +const max_shells = @import("shell.zig").max_shells; -const HeaderSize = @divExact(@bitSizeOf(Header), 8); -const Magic = [4]u8{ 0xf0, 0x9f, 0xa4, 0xb7 }; -const Version = 0; -const Bom = 0x1234; +const header_size = @sizeOf(Header); +const magic = [4]u8{ 0xf0, 0x9f, 0xa4, 0xb7 }; +const version = 0; -pub const SectionLength = 64; +const Endian = enum(u8) { + big, + little, -const InvalidHeader = error{ - InvalidMagic, - InvalidVersion, - InvalidBom, - TooManyShells, - InvalidOffset, + fn native() Endian { + return switch (native_endian) { + .Little => Endian.little, + .Big => Endian.big, + }; + } }; -const Header = packed struct { - magic: [4]u8, - version: u8, - bom: u16, +pub const section_length_bits = 6; +pub const section_length = 1 << section_length_bits; + +pub const InvalidHeader = error{ + InvalidMagic, + InvalidVersion, + InvalidEndianess, + TooManyShells, +}; + +pub const Header = packed struct { + magic: [4]u8 = magic, + version: u8 = version, + endian: Endian = Endian.native(), + nblocks_shell_blob: u8, num_shells: u8, - num_users: u32, num_groups: u32, - offset_bdz_uid2user: u32, - offset_bdz_groupname2group: u32, - offset_bdz_username2user: u32, - offset_idx: u32, - offset_groups: u32, - offset_users: u32, - offset_groupmembers: u32, - offset_additional_gids: u32, + num_users: u32, + nblocks_bdz_gid: u32, + nblocks_bdz_groupname: u32, + nblocks_bdz_uid: u32, + nblocks_bdz_username: u32, + nblocks_groups: u64, + nblocks_users: u64, + nblocks_groupmembers: u64, + nblocks_additional_gids: u64, - pub fn init(blob: [HeaderSize]u8) InvalidHeader!Header { - const self = @bitCast(Header, blob); - for (Magic) |item, index| { - if (self.magic[index] != item) return error.InvalidMagic; - } - if (self.version != 0) { + pub fn fromBytes(blob: [header_size]u8) InvalidHeader!Header { + const self = mem.bytesAsValue(Header, blob); + + if (!mem.eql(magic, blob[0..4])) + return error.InvalidMagic; + + if (self.version != 0) return error.InvalidVersion; - } - if (self.bom != Bom) { - return error.InvalidBom; - } - if (self.num_shells > shell.max_shells) { - return error.TooManyShells; - } - const offsets = [_]u32{ - self.offset_bdz_uid2user, - self.offset_bdz_groupname2group, - self.offset_bdz_username2user, - self.offset_idx, - self.offset_groups, - self.offset_users, - self.offset_groupmembers, - self.offset_additional_gids, - }; - for (offsets) |offset| { - if (offset & (SectionLength - 1) != 0) { - return error.InvalidOffset; - } - } + if (self.endian != Endian.native()) + return error.InvalidEndianess; + + if (self.num_shells > max_shells) + return error.TooManyShells; return self; } - pub fn asArray(self: *const Header) [HeaderSize]u8 { - return @bitCast([HeaderSize]u8, self.*); + pub fn asBytes(self: *const Header) [header_size]u8 { + return mem.asBytes(self); } }; const testing = std.testing; test "Section length is a power of two" { - try testing.expect(std.math.isPowerOfTwo(SectionLength)); + try testing.expect(std.math.isPowerOfTwo(section_length)); +} + +test "bit header size is equal to @sizeOf(Header)" { + try testing.expectEqual(@sizeOf(Header) * 8, @bitSizeOf(Header)); } test "header pack, unpack and validation" { - const goodHeader = Header{ - .magic = Magic, - .version = Version, - .bom = Bom, - .num_shells = 0, - .num_users = 0, - .num_groups = 0, - .offset_bdz_uid2user = 0, - .offset_bdz_groupname2group = 0, - .offset_bdz_username2user = 0, - .offset_idx = 0, - .offset_groups = 0, - .offset_users = 0, - .offset_groupmembers = 0, - .offset_additional_gids = 0, - }; + //const goodHeader = Header{}; - const gotHeader = try Header.init(goodHeader.asArray()); - try testing.expectEqual(goodHeader, gotHeader); + //const gotHeader = try Header.init(goodHeader.asArray()); + //try testing.expectEqual(goodHeader, gotHeader); - { - var header = goodHeader; - header.magic[0] = 0; - try testing.expectError(error.InvalidMagic, Header.init(header.asArray())); - } + //{ + // var header = goodHeader; + // header.magic[0] = 0; + // try testing.expectError(error.InvalidMagic, Header.init(header.asArray())); + //} - { - var header = goodHeader; - header.bom = 0x3412; - try testing.expectError(error.InvalidBom, Header.init(header.asArray())); - } + //{ + // var header = goodHeader; + // header.bom = 0x3412; + // try testing.expectError(error.InvalidBom, Header.init(header.asArray())); + //} - { - var header = goodHeader; - header.offset_bdz_uid2user = 65; - try testing.expectError(error.InvalidOffset, Header.init(header.asArray())); - } + //{ + // var header = goodHeader; + // header.offset_bdz_uid2user = 65; + // try testing.expectError(error.InvalidOffset, Header.init(header.asArray())); + //} } diff --git a/src/sections.zig b/src/sections.zig index aab0859..98db2cb 100644 --- a/src/sections.zig +++ b/src/sections.zig @@ -23,7 +23,10 @@ const PackedGroup = @import("group.zig").PackedGroup; const ShellSections = @import("shell.zig").ShellWriter.ShellSections; const ShellReader = @import("shell.zig").ShellReader; const ShellWriter = @import("shell.zig").ShellWriter; +const Header = @import("header.zig").Header; const max_shells = @import("shell.zig").max_shells; +const section_length_bits = @import("header.zig").section_length_bits; +const section_length = @import("header.zig").section_length; const cmph = @import("cmph.zig"); const bdz = @import("bdz.zig"); @@ -390,6 +393,13 @@ fn cmpGroup(_: void, a: Group, b: Group) bool { return a.gid < b.gid; } +// nblocks returns how many blocks a particular slice will take. +fn nblocks(arr: []const u8) u32 { + const upper = pad.roundUp(u38, section_length_bits, @intCast(u38, arr.len)); + assert(upper & (section_length - 1) == 0); + return @truncate(u32, upper >> 6); +} + pub const AllSections = struct { allocator: Allocator, @@ -407,6 +417,7 @@ pub const AllSections = struct { idx_groupname2group: []const u32, idx_uid2user: []const u32, idx_name2user: []const u32, + header: Header, pub fn init( allocator: Allocator, @@ -456,6 +467,21 @@ pub const AllSections = struct { var idx_name2user = try bdzIdx([]const u8, allocator, bdz_username, unames, users.idx2offset); errdefer allocator.free(idx_name2user); + const header = Header{ + .nblocks_shell_blob = undefined, + .num_shells = undefined, + .num_groups = undefined, + .num_users = undefined, + .nblocks_bdz_gid = undefined, + .nblocks_bdz_groupname = undefined, + .nblocks_bdz_uid = undefined, + .nblocks_bdz_username = undefined, + .nblocks_groups = undefined, + .nblocks_users = undefined, + .nblocks_groupmembers = undefined, + .nblocks_additional_gids = undefined, + }; + return AllSections{ .allocator = allocator, .bdz_gid = bdz_gid, @@ -475,6 +501,7 @@ pub const AllSections = struct { .idx_groupname2group = idx_groupname2group, .idx_uid2user = idx_uid2user, .idx_name2user = idx_name2user, + .header = header, }; } @@ -745,3 +772,18 @@ test "bdzIdx on str" { defer testing.allocator.free(result); try expectUsedHashes(testing.allocator, result); } + +test "nblocks" { + const T = struct { want: u32, arr: []const u8 }; + const tests = &[_]T{ + .{ .want = 0, .arr = &[_]u8{} }, + .{ .want = 1, .arr = &[_]u8{ 1, 2, 42 } }, + .{ .want = 1, .arr = &[_]u8{1} ** 63 }, + .{ .want = 1, .arr = &[_]u8{1} ** 64 }, + .{ .want = 2, .arr = &[_]u8{1} ** 65 }, + }; + + for (tests) |tt| { + try testing.expectEqual(tt.want, nblocks(tt.arr)); + } +}