endianess and nblocks

This commit is contained in:
Motiejus Jakštys 2022-03-18 17:48:33 +01:00 committed by Motiejus Jakštys
parent 692d80cdd7
commit e4e845384f
2 changed files with 118 additions and 89 deletions

View File

@ -1,118 +1,105 @@
const std = @import("std"); 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 header_size = @sizeOf(Header);
const Magic = [4]u8{ 0xf0, 0x9f, 0xa4, 0xb7 }; const magic = [4]u8{ 0xf0, 0x9f, 0xa4, 0xb7 };
const Version = 0; const version = 0;
const Bom = 0x1234;
pub const SectionLength = 64; const Endian = enum(u8) {
big,
little,
const InvalidHeader = error{ fn native() Endian {
InvalidMagic, return switch (native_endian) {
InvalidVersion, .Little => Endian.little,
InvalidBom, .Big => Endian.big,
TooManyShells, };
InvalidOffset, }
}; };
const Header = packed struct { pub const section_length_bits = 6;
magic: [4]u8, pub const section_length = 1 << section_length_bits;
version: u8,
bom: u16, 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_shells: u8,
num_users: u32,
num_groups: u32, num_groups: u32,
offset_bdz_uid2user: u32, num_users: u32,
offset_bdz_groupname2group: u32, nblocks_bdz_gid: u32,
offset_bdz_username2user: u32, nblocks_bdz_groupname: u32,
offset_idx: u32, nblocks_bdz_uid: u32,
offset_groups: u32, nblocks_bdz_username: u32,
offset_users: u32, nblocks_groups: u64,
offset_groupmembers: u32, nblocks_users: u64,
offset_additional_gids: u32, nblocks_groupmembers: u64,
nblocks_additional_gids: u64,
pub fn init(blob: [HeaderSize]u8) InvalidHeader!Header { pub fn fromBytes(blob: [header_size]u8) InvalidHeader!Header {
const self = @bitCast(Header, blob); const self = mem.bytesAsValue(Header, blob);
for (Magic) |item, index| {
if (self.magic[index] != item) return error.InvalidMagic; if (!mem.eql(magic, blob[0..4]))
} return error.InvalidMagic;
if (self.version != 0) {
if (self.version != 0)
return error.InvalidVersion; return error.InvalidVersion;
}
if (self.bom != Bom) {
return error.InvalidBom;
}
if (self.num_shells > shell.max_shells) {
return error.TooManyShells;
}
const offsets = [_]u32{ if (self.endian != Endian.native())
self.offset_bdz_uid2user, return error.InvalidEndianess;
self.offset_bdz_groupname2group,
self.offset_bdz_username2user, if (self.num_shells > max_shells)
self.offset_idx, return error.TooManyShells;
self.offset_groups,
self.offset_users,
self.offset_groupmembers,
self.offset_additional_gids,
};
for (offsets) |offset| {
if (offset & (SectionLength - 1) != 0) {
return error.InvalidOffset;
}
}
return self; return self;
} }
pub fn asArray(self: *const Header) [HeaderSize]u8 { pub fn asBytes(self: *const Header) [header_size]u8 {
return @bitCast([HeaderSize]u8, self.*); return mem.asBytes(self);
} }
}; };
const testing = std.testing; const testing = std.testing;
test "Section length is a power of two" { 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" { test "header pack, unpack and validation" {
const goodHeader = Header{ //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 gotHeader = try Header.init(goodHeader.asArray()); //const gotHeader = try Header.init(goodHeader.asArray());
try testing.expectEqual(goodHeader, gotHeader); //try testing.expectEqual(goodHeader, gotHeader);
{ //{
var header = goodHeader; // var header = goodHeader;
header.magic[0] = 0; // header.magic[0] = 0;
try testing.expectError(error.InvalidMagic, Header.init(header.asArray())); // try testing.expectError(error.InvalidMagic, Header.init(header.asArray()));
} //}
{ //{
var header = goodHeader; // var header = goodHeader;
header.bom = 0x3412; // header.bom = 0x3412;
try testing.expectError(error.InvalidBom, Header.init(header.asArray())); // try testing.expectError(error.InvalidBom, Header.init(header.asArray()));
} //}
{ //{
var header = goodHeader; // var header = goodHeader;
header.offset_bdz_uid2user = 65; // header.offset_bdz_uid2user = 65;
try testing.expectError(error.InvalidOffset, Header.init(header.asArray())); // try testing.expectError(error.InvalidOffset, Header.init(header.asArray()));
} //}
} }

View File

@ -23,7 +23,10 @@ const PackedGroup = @import("group.zig").PackedGroup;
const ShellSections = @import("shell.zig").ShellWriter.ShellSections; const ShellSections = @import("shell.zig").ShellWriter.ShellSections;
const ShellReader = @import("shell.zig").ShellReader; const ShellReader = @import("shell.zig").ShellReader;
const ShellWriter = @import("shell.zig").ShellWriter; const ShellWriter = @import("shell.zig").ShellWriter;
const Header = @import("header.zig").Header;
const max_shells = @import("shell.zig").max_shells; 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 cmph = @import("cmph.zig");
const bdz = @import("bdz.zig"); const bdz = @import("bdz.zig");
@ -390,6 +393,13 @@ fn cmpGroup(_: void, a: Group, b: Group) bool {
return a.gid < b.gid; 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 { pub const AllSections = struct {
allocator: Allocator, allocator: Allocator,
@ -407,6 +417,7 @@ pub const AllSections = struct {
idx_groupname2group: []const u32, idx_groupname2group: []const u32,
idx_uid2user: []const u32, idx_uid2user: []const u32,
idx_name2user: []const u32, idx_name2user: []const u32,
header: Header,
pub fn init( pub fn init(
allocator: Allocator, allocator: Allocator,
@ -456,6 +467,21 @@ pub const AllSections = struct {
var idx_name2user = try bdzIdx([]const u8, allocator, bdz_username, unames, users.idx2offset); var idx_name2user = try bdzIdx([]const u8, allocator, bdz_username, unames, users.idx2offset);
errdefer allocator.free(idx_name2user); 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{ return AllSections{
.allocator = allocator, .allocator = allocator,
.bdz_gid = bdz_gid, .bdz_gid = bdz_gid,
@ -475,6 +501,7 @@ pub const AllSections = struct {
.idx_groupname2group = idx_groupname2group, .idx_groupname2group = idx_groupname2group,
.idx_uid2user = idx_uid2user, .idx_uid2user = idx_uid2user,
.idx_name2user = idx_name2user, .idx_name2user = idx_name2user,
.header = header,
}; };
} }
@ -745,3 +772,18 @@ test "bdzIdx on str" {
defer testing.allocator.free(result); defer testing.allocator.free(result);
try expectUsedHashes(testing.allocator, 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));
}
}