endianess and nblocks
This commit is contained in:
parent
692d80cdd7
commit
e4e845384f
159
src/header.zig
159
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{
|
||||
fn native() Endian {
|
||||
return switch (native_endian) {
|
||||
.Little => Endian.little,
|
||||
.Big => Endian.big,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const section_length_bits = 6;
|
||||
pub const section_length = 1 << section_length_bits;
|
||||
|
||||
pub const InvalidHeader = error{
|
||||
InvalidMagic,
|
||||
InvalidVersion,
|
||||
InvalidBom,
|
||||
InvalidEndianess,
|
||||
TooManyShells,
|
||||
InvalidOffset,
|
||||
};
|
||||
|
||||
const Header = packed struct {
|
||||
magic: [4]u8,
|
||||
version: u8,
|
||||
bom: u16,
|
||||
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()));
|
||||
//}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue