From d3be68b51d9b43f733966b686c2bcf911dfa1870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Sun, 6 Mar 2022 17:55:29 +0200 Subject: [PATCH] generalize Shell2Index for hash and test stubs --- src/sections.zig | 12 ++++--- src/user.zig | 92 +++++++++++++++++++++++------------------------- 2 files changed, 53 insertions(+), 51 deletions(-) diff --git a/src/sections.zig b/src/sections.zig index d9bcaf1..0bbffed 100644 --- a/src/sections.zig +++ b/src/sections.zig @@ -225,6 +225,8 @@ pub const UserGids = struct { } }; +const userGidsPaddingBits = 3; + const userGidsErr = Allocator.Error || error{Overflow}; pub fn userGids(allocator: Allocator, corpus: *const Corpus) userGidsErr!UserGids { var blob = ArrayList(u8).init(allocator); @@ -234,6 +236,7 @@ pub fn userGids(allocator: Allocator, corpus: *const Corpus) userGidsErr!UserGid // zero'th entry is empty, so groupless users can refer to it. try compress.appendUvarint(&blob, 0); + try pad.arrayList(&blob, userGidsPaddingBits); var scratch = try allocator.alloc(u32, 256); defer allocator.free(scratch); @@ -250,6 +253,7 @@ pub fn userGids(allocator: Allocator, corpus: *const Corpus) userGidsErr!UserGid try compress.appendUvarint(&blob, usergroups.len); for (scratch) |gid| try compress.appendUvarint(&blob, gid); + try pad.arrayList(&blob, userGidsPaddingBits); } else { try name2offset.putNoClobber(user.name, 0); } @@ -266,18 +270,18 @@ pub fn usersSection( corpus: *const Corpus, gids: *const UserGids, shells: *const ShellSections, -) error{ OutOfMemory, Overflow }![]const u8 { +) error{ OutOfMemory, Overflow, InvalidRecord }![]const u8 { // as of writing each user takes 15 bytes + strings + padding, padded to // 8 bytes. 24 is an optimistic lower bound for an average record size. var buf = try ArrayList(u8).initCapacity(allocator, 24 * corpus.users.len); for (corpus.users) |user| { const offset = gids.name2offset.get(user.name).?; std.debug.assert(offset & 7 == 0); - try userImport.PackedUserConst.packTo( + try userImport.PackedUserHash.packTo( &buf, user, @truncate(u29, @shrExact(offset, 3)), - shells.indices.get, + shells.indices, ); } return buf.toOwnedSlice(); @@ -480,7 +484,7 @@ test "userGids" { const groups = corpus.username2groups.get(user.name); const offset = user_gids.name2offset.get(user.name); if (groups == null) { - try testing.expect(offset == null); + try testing.expect(offset.? == 0); continue; } var vit = try compress.VarintSliceIterator(user_gids.blob[offset.?..]); diff --git a/src/user.zig b/src/user.zig index 6b316ad..1e5f494 100644 --- a/src/user.zig +++ b/src/user.zig @@ -62,15 +62,28 @@ pub const User = struct { } }; -pub const PackedUserMut = packedUser(false); -pub const PackedUserConst = packedUser(true); +pub fn Shell2Index(T: type) type { + return struct { + const Self = @This(); + data: T, -fn packedUser(immutable: bool) type { + pub fn init(data: T) Self { + return Self{ .data = data }; + } + + pub fn get(self: *const Self, str: []const u8) ?u6 { + return self.data.get(str); + } + }; +} + +pub const PackedUserHash = packedUser(std.StringHashMap(u6)); + +fn packedUser(comptime ShellIndexType: type) type { return struct { const Self = @This(); const alignmentBits = 3; - const shell2idxProto = fn ([]const u8) ?u6; const InnerSize = @divExact(@bitSizeOf(Inner), 8); const Inner = packed struct { @@ -133,21 +146,18 @@ fn packedUser(immutable: bool) type { } }; - const Bytes = if (immutable) []const u8 else []u8; - const InnerPointer = if (immutable) *const Inner else *Inner; - // PackedUser does not allocate; it re-interprets the "bytes" blob // field. Both of those fields are pointers to "our representation" of // that field. - inner: InnerPointer, - userdata: Bytes, + inner: *const Inner, + userdata: []const u8, pub const Entry = struct { user: Self, - next: ?Bytes, + next: ?[]const u8, }; - pub fn fromBytes(bytes: Bytes) Entry { + pub fn fromBytes(bytes: []const u8) Entry { const inner = mem.bytesAsValue( Inner, // Should use InnerSize instead of sizeOf, see @@ -159,7 +169,7 @@ fn packedUser(immutable: bool) type { const endBlob = startBlob + inner.blobLength(); const nextStart = pad.roundUp(usize, alignmentBits, endBlob); - var next: ?Bytes = null; + var next: ?[]const u8 = null; if (nextStart < bytes.len) next = bytes[nextStart..]; @@ -173,7 +183,7 @@ fn packedUser(immutable: bool) type { } pub const Iterator = struct { - section: ?Bytes, + section: ?[]const u8, shellIndex: Idx2ShellProto, pub fn next(it: *Iterator) ?Self { @@ -186,7 +196,7 @@ fn packedUser(immutable: bool) type { } }; - pub fn iterator(section: Bytes, idxFn: Idx2ShellProto) Iterator { + pub fn iterator(section: []const u8, idxFn: Idx2ShellProto) Iterator { return Iterator{ .section = section, .shellIndex = idxFn }; } @@ -198,7 +208,7 @@ fn packedUser(immutable: bool) type { arr: *ArrayList(u8), user: User, additional_gids_offset: u29, - idxFn: shell2idxProto, + idxFn: ShellIndexType, ) packErr!void { // function arguments are consts. We need to mutate the underlying // slice, so passing it via pointer instead. @@ -216,8 +226,8 @@ fn packedUser(immutable: bool) type { .uid = user.uid, .gid = user.gid, .additional_gids_offset = additional_gids_offset, - .shell_here = idxFn(user.shell) == null, - .shell_len_or_idx = idxFn(user.shell) orelse shell_len, + .shell_here = idxFn.get(user.shell) == null, + .shell_len_or_idx = idxFn.get(user.shell) orelse shell_len, .home_len = home_len, .name_is_a_suffix = mem.endsWith(u8, user.home, user.name), .name_len = name_len, @@ -306,19 +316,23 @@ test "PackedUser internal and external alignment" { // more than 1, but it probably could be higher. try testing.expectEqual( 8, - @sizeOf(PackedUserConst.Inner) * 8 - - @bitSizeOf(PackedUserConst.Inner), + @sizeOf(PackedUserHash.Inner) * 8 - + @bitSizeOf(PackedUserHash.Inner), ); } -fn testShellIndex(shell: []const u8) ?u6 { - if (mem.eql(u8, shell, "/bin/bash")) { - return 0; - } else if (mem.eql(u8, shell, "/bin/zsh")) { - return 1; +const TestShellIndex = struct { + pub fn get(_: *const TestShellIndex, shell: []const u8) ?u6 { + if (mem.eql(u8, shell, "/bin/bash")) { + return 0; + } else if (mem.eql(u8, shell, "/bin/zsh")) { + return 1; + } + return null; } - return null; -} +}; + +const PackedUserTest = packedUser(TestShellIndex); fn testShell(index: u6) []const u8 { return switch (index) { @@ -362,10 +376,10 @@ test "construct PackedUser section" { .shell = "/", } }; for (users) |user| - try PackedUserConst.packTo(&buf, user, math.maxInt(u29), testShellIndex); + try PackedUserTest.packTo(&buf, user, math.maxInt(u29), TestShellIndex{}); var i: u29 = 0; - var it1 = PackedUserConst.iterator(buf.items, testShell); + var it1 = PackedUserTest.iterator(buf.items, testShell); while (it1.next()) |user| : (i += 1) { try testing.expectEqual(users[i].uid, user.uid()); try testing.expectEqual(users[i].gid, user.gid()); @@ -379,22 +393,6 @@ test "construct PackedUser section" { try testing.expectEqualStrings(users[i].shell, user.shell(testShell)); } try testing.expectEqual(users.len, i); - - var it2 = PackedUserMut.iterator(buf.items, testShell); - i = 0; - while (it2.next()) |user| : (i += 1) { - user.setAdditionalGidsOffset(i); - } - try testing.expectEqual(users.len, i); - - var it3 = PackedUserConst.iterator(buf.items, testShell); - i = 0; - while (it3.next()) |user| : (i += 1) { - try testing.expectEqual(users[i].gid, user.gid()); - try testing.expectEqual(i, user.additionalGidsOffset()); - try testing.expectEqualStrings(users[i].name, user.name()); - } - try testing.expectEqual(users.len, i); } test "PackedUser.maxSize()" { @@ -402,7 +400,7 @@ test "PackedUser.maxSize()" { // As of writing, I am getting a stack smashing error. var buf = try ArrayList(u8).initCapacity( testing.allocator, - PackedUserConst.maxSize(), + PackedUserHash.maxSize(), ); defer buf.deinit(); @@ -414,8 +412,8 @@ test "PackedUser.maxSize()" { .home = "Home" ** 16, // 64 .shell = "She.LllL" ** 8, // 64 }; - try PackedUserConst.packTo(&buf, largeUser, math.maxInt(u29), testShellIndex); - try testing.expectEqual(PackedUserConst.maxSize(), buf.items.len); + try PackedUserTest.packTo(&buf, largeUser, math.maxInt(u29), TestShellIndex{}); + try testing.expectEqual(PackedUserTest.maxSize(), buf.items.len); } test "User.clone" {