generalize Shell2Index for hash and test stubs

This commit is contained in:
Motiejus Jakštys 2022-03-06 17:55:29 +02:00 committed by Motiejus Jakštys
parent a6349cd114
commit d3be68b51d
2 changed files with 53 additions and 51 deletions

View File

@ -225,6 +225,8 @@ pub const UserGids = struct {
} }
}; };
const userGidsPaddingBits = 3;
const userGidsErr = Allocator.Error || error{Overflow}; const userGidsErr = Allocator.Error || error{Overflow};
pub fn userGids(allocator: Allocator, corpus: *const Corpus) userGidsErr!UserGids { pub fn userGids(allocator: Allocator, corpus: *const Corpus) userGidsErr!UserGids {
var blob = ArrayList(u8).init(allocator); 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. // zero'th entry is empty, so groupless users can refer to it.
try compress.appendUvarint(&blob, 0); try compress.appendUvarint(&blob, 0);
try pad.arrayList(&blob, userGidsPaddingBits);
var scratch = try allocator.alloc(u32, 256); var scratch = try allocator.alloc(u32, 256);
defer allocator.free(scratch); defer allocator.free(scratch);
@ -250,6 +253,7 @@ pub fn userGids(allocator: Allocator, corpus: *const Corpus) userGidsErr!UserGid
try compress.appendUvarint(&blob, usergroups.len); try compress.appendUvarint(&blob, usergroups.len);
for (scratch) |gid| for (scratch) |gid|
try compress.appendUvarint(&blob, gid); try compress.appendUvarint(&blob, gid);
try pad.arrayList(&blob, userGidsPaddingBits);
} else { } else {
try name2offset.putNoClobber(user.name, 0); try name2offset.putNoClobber(user.name, 0);
} }
@ -266,18 +270,18 @@ pub fn usersSection(
corpus: *const Corpus, corpus: *const Corpus,
gids: *const UserGids, gids: *const UserGids,
shells: *const ShellSections, 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 // 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. // 8 bytes. 24 is an optimistic lower bound for an average record size.
var buf = try ArrayList(u8).initCapacity(allocator, 24 * corpus.users.len); var buf = try ArrayList(u8).initCapacity(allocator, 24 * corpus.users.len);
for (corpus.users) |user| { for (corpus.users) |user| {
const offset = gids.name2offset.get(user.name).?; const offset = gids.name2offset.get(user.name).?;
std.debug.assert(offset & 7 == 0); std.debug.assert(offset & 7 == 0);
try userImport.PackedUserConst.packTo( try userImport.PackedUserHash.packTo(
&buf, &buf,
user, user,
@truncate(u29, @shrExact(offset, 3)), @truncate(u29, @shrExact(offset, 3)),
shells.indices.get, shells.indices,
); );
} }
return buf.toOwnedSlice(); return buf.toOwnedSlice();
@ -480,7 +484,7 @@ test "userGids" {
const groups = corpus.username2groups.get(user.name); const groups = corpus.username2groups.get(user.name);
const offset = user_gids.name2offset.get(user.name); const offset = user_gids.name2offset.get(user.name);
if (groups == null) { if (groups == null) {
try testing.expect(offset == null); try testing.expect(offset.? == 0);
continue; continue;
} }
var vit = try compress.VarintSliceIterator(user_gids.blob[offset.?..]); var vit = try compress.VarintSliceIterator(user_gids.blob[offset.?..]);

View File

@ -62,15 +62,28 @@ pub const User = struct {
} }
}; };
pub const PackedUserMut = packedUser(false); pub fn Shell2Index(T: type) type {
pub const PackedUserConst = packedUser(true); 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 { return struct {
const Self = @This(); const Self = @This();
const alignmentBits = 3; const alignmentBits = 3;
const shell2idxProto = fn ([]const u8) ?u6;
const InnerSize = @divExact(@bitSizeOf(Inner), 8); const InnerSize = @divExact(@bitSizeOf(Inner), 8);
const Inner = packed struct { 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 // PackedUser does not allocate; it re-interprets the "bytes" blob
// field. Both of those fields are pointers to "our representation" of // field. Both of those fields are pointers to "our representation" of
// that field. // that field.
inner: InnerPointer, inner: *const Inner,
userdata: Bytes, userdata: []const u8,
pub const Entry = struct { pub const Entry = struct {
user: Self, user: Self,
next: ?Bytes, next: ?[]const u8,
}; };
pub fn fromBytes(bytes: Bytes) Entry { pub fn fromBytes(bytes: []const u8) Entry {
const inner = mem.bytesAsValue( const inner = mem.bytesAsValue(
Inner, Inner,
// Should use InnerSize instead of sizeOf, see // Should use InnerSize instead of sizeOf, see
@ -159,7 +169,7 @@ fn packedUser(immutable: bool) type {
const endBlob = startBlob + inner.blobLength(); const endBlob = startBlob + inner.blobLength();
const nextStart = pad.roundUp(usize, alignmentBits, endBlob); const nextStart = pad.roundUp(usize, alignmentBits, endBlob);
var next: ?Bytes = null; var next: ?[]const u8 = null;
if (nextStart < bytes.len) if (nextStart < bytes.len)
next = bytes[nextStart..]; next = bytes[nextStart..];
@ -173,7 +183,7 @@ fn packedUser(immutable: bool) type {
} }
pub const Iterator = struct { pub const Iterator = struct {
section: ?Bytes, section: ?[]const u8,
shellIndex: Idx2ShellProto, shellIndex: Idx2ShellProto,
pub fn next(it: *Iterator) ?Self { 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 }; return Iterator{ .section = section, .shellIndex = idxFn };
} }
@ -198,7 +208,7 @@ fn packedUser(immutable: bool) type {
arr: *ArrayList(u8), arr: *ArrayList(u8),
user: User, user: User,
additional_gids_offset: u29, additional_gids_offset: u29,
idxFn: shell2idxProto, idxFn: ShellIndexType,
) packErr!void { ) packErr!void {
// function arguments are consts. We need to mutate the underlying // function arguments are consts. We need to mutate the underlying
// slice, so passing it via pointer instead. // slice, so passing it via pointer instead.
@ -216,8 +226,8 @@ fn packedUser(immutable: bool) type {
.uid = user.uid, .uid = user.uid,
.gid = user.gid, .gid = user.gid,
.additional_gids_offset = additional_gids_offset, .additional_gids_offset = additional_gids_offset,
.shell_here = idxFn(user.shell) == null, .shell_here = idxFn.get(user.shell) == null,
.shell_len_or_idx = idxFn(user.shell) orelse shell_len, .shell_len_or_idx = idxFn.get(user.shell) orelse shell_len,
.home_len = home_len, .home_len = home_len,
.name_is_a_suffix = mem.endsWith(u8, user.home, user.name), .name_is_a_suffix = mem.endsWith(u8, user.home, user.name),
.name_len = name_len, .name_len = name_len,
@ -306,12 +316,13 @@ test "PackedUser internal and external alignment" {
// more than 1, but it probably could be higher. // more than 1, but it probably could be higher.
try testing.expectEqual( try testing.expectEqual(
8, 8,
@sizeOf(PackedUserConst.Inner) * 8 - @sizeOf(PackedUserHash.Inner) * 8 -
@bitSizeOf(PackedUserConst.Inner), @bitSizeOf(PackedUserHash.Inner),
); );
} }
fn testShellIndex(shell: []const u8) ?u6 { const TestShellIndex = struct {
pub fn get(_: *const TestShellIndex, shell: []const u8) ?u6 {
if (mem.eql(u8, shell, "/bin/bash")) { if (mem.eql(u8, shell, "/bin/bash")) {
return 0; return 0;
} else if (mem.eql(u8, shell, "/bin/zsh")) { } else if (mem.eql(u8, shell, "/bin/zsh")) {
@ -319,6 +330,9 @@ fn testShellIndex(shell: []const u8) ?u6 {
} }
return null; return null;
} }
};
const PackedUserTest = packedUser(TestShellIndex);
fn testShell(index: u6) []const u8 { fn testShell(index: u6) []const u8 {
return switch (index) { return switch (index) {
@ -362,10 +376,10 @@ test "construct PackedUser section" {
.shell = "/", .shell = "/",
} }; } };
for (users) |user| 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 i: u29 = 0;
var it1 = PackedUserConst.iterator(buf.items, testShell); var it1 = PackedUserTest.iterator(buf.items, testShell);
while (it1.next()) |user| : (i += 1) { while (it1.next()) |user| : (i += 1) {
try testing.expectEqual(users[i].uid, user.uid()); try testing.expectEqual(users[i].uid, user.uid());
try testing.expectEqual(users[i].gid, user.gid()); 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.expectEqualStrings(users[i].shell, user.shell(testShell));
} }
try testing.expectEqual(users.len, i); 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()" { test "PackedUser.maxSize()" {
@ -402,7 +400,7 @@ test "PackedUser.maxSize()" {
// As of writing, I am getting a stack smashing error. // As of writing, I am getting a stack smashing error.
var buf = try ArrayList(u8).initCapacity( var buf = try ArrayList(u8).initCapacity(
testing.allocator, testing.allocator,
PackedUserConst.maxSize(), PackedUserHash.maxSize(),
); );
defer buf.deinit(); defer buf.deinit();
@ -414,8 +412,8 @@ test "PackedUser.maxSize()" {
.home = "Home" ** 16, // 64 .home = "Home" ** 16, // 64
.shell = "She.LllL" ** 8, // 64 .shell = "She.LllL" ** 8, // 64
}; };
try PackedUserConst.packTo(&buf, largeUser, math.maxInt(u29), testShellIndex); try PackedUserTest.packTo(&buf, largeUser, math.maxInt(u29), TestShellIndex{});
try testing.expectEqual(PackedUserConst.maxSize(), buf.items.len); try testing.expectEqual(PackedUserTest.maxSize(), buf.items.len);
} }
test "User.clone" { test "User.clone" {