diff --git a/lib/PackedUser.zig b/lib/PackedUser.zig index 805038f..27603b7 100644 --- a/lib/PackedUser.zig +++ b/lib/PackedUser.zig @@ -5,6 +5,7 @@ const math = std.math; const Allocator = mem.Allocator; const ArrayList = std.ArrayList; const StringHashMap = std.StringHashMap; +const fieldInfo = std.meta.fieldInfo; const pad = @import("padding.zig"); const validate = @import("validate.zig"); @@ -129,9 +130,7 @@ pub fn iterator(section: []const u8, shell_reader: shellImport.ShellReader) Iter return Iterator{ .section = section, .shell_reader = shell_reader }; } -// packTo packs the User record and copies it to the given byte slice. -// The slice must have at least maxRecordSize() bytes available. The -// slice is passed as a pointer, so it can be mutated. +// packTo packs the User record and copies it to the given arraylist. pub fn packTo( arr: *ArrayList(u8), user: User, @@ -141,10 +140,11 @@ pub fn packTo( std.debug.assert(arr.items.len & 7 == 0); // function arguments are consts. We need to mutate the underlying // slice, so passing it via pointer instead. - const home_len = try validate.downCast(u6, user.home.len - 1); - const name_len = try validate.downCast(u5, user.name.len - 1); - const shell_len = try validate.downCast(u8, user.shell.len - 1); - const gecos_len = try validate.downCast(u8, user.gecos.len); + //const home_len = try validate.downCast(u6, user.home.len - 1); + const home_len = try validate.downCast(fieldInfo(Inner, .home_len).field_type, user.home.len - 1); + const name_len = try validate.downCast(fieldInfo(Inner, .name_len).field_type, user.name.len - 1); + const shell_len = try validate.downCast(fieldInfo(Inner, .shell_len_or_idx).field_type, user.shell.len - 1); + const gecos_len = try validate.downCast(fieldInfo(Inner, .gecos_len).field_type, user.gecos.len); try validate.utf8(user.home); try validate.utf8(user.name); @@ -211,6 +211,11 @@ pub fn shell(self: PackedUser, shell_reader: shellImport.ShellReader) []const u8 return shell_reader.get(self.inner.shell_len_or_idx); } +pub const max_str_len = math.maxInt(fieldInfo(Inner, .shell_len_or_idx).field_type) + 1 + + math.maxInt(fieldInfo(Inner, .home_len).field_type) + 1 + + math.maxInt(fieldInfo(Inner, .name_len).field_type) + 1 + + math.maxInt(fieldInfo(Inner, .gecos_len).field_type); + const testing = std.testing; test "PackedUser internal and external alignment" { @@ -232,6 +237,33 @@ const test_shell_reader = shellImport.ShellReader{ .index = &[_]u16{ 0, 9, 17 }, }; +const max_user = User{ + .uid = 0, + .gid = math.maxInt(u32), + .name = "Name" ** 8, + .gecos = "realname" ** 255 ++ "realnam", + .home = "Home" ** 16, + .shell = "She.LllL" ** 32, +}; + +test "max_user and max_str_len are consistent" { + const total_len = max_user.name.len + + max_user.gecos.len + + max_user.home.len + + max_user.shell.len; + try testing.expectEqual(total_len, max_str_len); +} + +test "pack max_user" { + var arr = ArrayList(u8).init(testing.allocator); + defer arr.deinit(); + + var idx_noop = StringHashMap(u8).init(testing.allocator); + defer idx_noop.deinit(); + + try packTo(&arr, max_user, 0, idx_noop); +} + test "construct PackedUser section" { var buf = ArrayList(u8).init(testing.allocator); defer buf.deinit(); @@ -250,14 +282,7 @@ test "construct PackedUser section" { .gecos = "Service Account", .home = "/home/service1", .shell = "/usr/bin/nologin", - }, User{ - .uid = 0, - .gid = math.maxInt(u32), - .name = "Name" ** 8, - .gecos = "Gecos" ** 51, - .home = "Home" ** 16, - .shell = "She.LllL" ** 32, - }, User{ + }, max_user, User{ .uid = 1002, .gid = 1002, .name = "svc-bar", diff --git a/lib/User.zig b/lib/User.zig index a250447..4152ab6 100644 --- a/lib/User.zig +++ b/lib/User.zig @@ -1,6 +1,8 @@ const std = @import("std"); const mem = std.mem; const Allocator = mem.Allocator; +const ArrayList = std.ArrayList; +const maxInt = std.math.maxInt; const User = @This(); @@ -48,6 +50,17 @@ pub fn deinit(self: *User, allocator: Allocator) void { self.* = undefined; } +pub fn fromReader(allocator: Allocator, reader: anytype) ![]User { + var users = ArrayList(User).init(allocator); + var scratch = ArrayList(u8).init(allocator); + defer scratch.deinit(); + while (true) { + const line = reader.readUntilDelimiterArrayList(&scratch, '\n', maxInt(usize)); + _ = line; + } + _ = users; +} + const testing = std.testing; test "User.clone" {