generalize Shell2Index for hash and test stubs
This commit is contained in:
parent
a6349cd114
commit
d3be68b51d
@ -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.?..]);
|
||||
|
82
src/user.zig
82
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 {
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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" {
|
||||
|
Loading…
Reference in New Issue
Block a user