1
Fork 0

endianess and nblocks

main
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 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()));
//}
}

View File

@ -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));
}
}