diff --git a/lib/CGroup.zig b/lib/CGroup.zig new file mode 100644 index 0000000..2ba9c4d --- /dev/null +++ b/lib/CGroup.zig @@ -0,0 +1,6 @@ +const CGroup = @This(); + +gid: u32, +name: [:0]const u8, +// https://github.com/ziglang/zig/issues/7517 +members: [:null]const ?[*:0]const u8, diff --git a/lib/Corpus.zig b/lib/Corpus.zig index 9914f44..80219c5 100644 --- a/lib/Corpus.zig +++ b/lib/Corpus.zig @@ -86,7 +86,7 @@ pub fn init( members.len = 0; for (groupmembers) |member_name| { - if (name2user.get(member_name.?)) |user_idx| { + if (name2user.get(member_name)) |user_idx| { members.len += 1; members[members.len - 1] = user_idx; try user2groups[user_idx].append(allocator, @intCast(u32, i)); @@ -203,16 +203,16 @@ pub fn testCorpus(allocator: Allocator) !Corpus { .shell = "/usr/sbin/nologin", } }; - var members0 = &[_:null]?[*:0]const u8{"root"}; - var members1 = &[_:null]?[*:0]const u8{"vidmantas"}; - var members2 = &[_:null]?[*:0]const u8{ "svc-bar", "vidmantas" }; - var members3 = &[_:null]?[*:0]const u8{ "svc-bar", "Name" ** 8, "vidmantas", "root" }; + var members0 = &[_][]const u8{"root"}; + var members1 = &[_][]const u8{"vidmantas"}; + var members2 = &[_][]const u8{ "svc-bar", "vidmantas" }; + var members3 = &[_][]const u8{ "svc-bar", "Name" ** 8, "vidmantas", "root" }; const groups = [_]Group{ - Group{ .gid = 0, .name = "root", .members = members0 }, - Group{ .gid = 128, .name = "vidmantas", .members = members1 }, - Group{ .gid = 9999, .name = "all", .members = members3 }, - Group{ .gid = 100000, .name = "service-account", .members = members2 }, + try Group.init(allocator, 0, "root", members0), + try Group.init(allocator, 128, "vidmantas", members1), + try Group.init(allocator, 9999, "all", members3), + try Group.init(allocator, 100000, "service-account", members2), }; return try Corpus.init(allocator, users[0..], groups[0..]); diff --git a/lib/DB.zig b/lib/DB.zig index dff0566..c7ec2a6 100644 --- a/lib/DB.zig +++ b/lib/DB.zig @@ -15,6 +15,7 @@ const pad = @import("padding.zig"); const compress = @import("compress.zig"); const PackedUser = @import("PackedUser.zig"); const User = @import("User.zig"); +const CGroup = @import("CGroup.zig"); const Group = @import("Group.zig"); const PackedGroup = @import("PackedGroup.zig"); const GroupStored = PackedGroup.GroupStored; @@ -219,10 +220,12 @@ pub fn fromBytes(buf: []align(8) const u8) InvalidHeader!DB { } const GroupMemberNames = struct { + _buf: []u8, arr: [:null]const ?[*:0]const u8, pub fn deinit(self: *GroupMemberNames, allocator: Allocator) void { - allocator.free(self.arr[0]); + if (self._buf.len == 0) return; + allocator.free(self._buf); allocator.free(self.arr); } }; @@ -236,22 +239,26 @@ fn groupMemberNames( ) error{OutOfMemory}!GroupMemberNames { const v = compress.uvarintMust(self.groupmembers[offset..]); const total_members_len = v.value; - offset += v.bytes_read; - var vit = compress.VarintSliceIteratorMust(self.groupmembers[offset..]); + const offset2 = offset + v.bytes_read; + var vit = compress.VarintSliceIteratorMust(self.groupmembers[offset2..]); const num_members = vit.remaining; - if (num_members == 0) return null; + if (num_members == 0) + return GroupMemberNames{ + ._buf = &[0]u8{}, + .arr = &[1:null]?[*:0]const u8{null}, + }; // TODO (zig 0.10+) make result type sentinel-aware and stop // the terminating-null-pointer-dancing. - var result = try allocator.alloc(?[:0]const u8, num_members + 1); - errdefer allocator.free(result); - result.len = num_members + 1; - result[num_members] = null; - result.len = num_members; + var arr = try allocator.alloc(?[*:0]const u8, num_members + 1); + errdefer allocator.free(arr); + arr.len = num_members + 1; + arr[num_members] = null; + arr.len = num_members; var buf = try allocator.alloc(u8, total_members_len + num_members); errdefer allocator.free(buf); - var it = compress.DeltaCompressionIterator(&vit); + var it = compress.DeltaDecompressionIterator(&vit); var i: usize = 0; while (it.nextMust()) |member_offset| : (i += 1) { const entry = PackedUser.fromBytes(self.users[member_offset..]); @@ -260,9 +267,9 @@ fn groupMemberNames( buf.len += name.len + 1; mem.copy(u8, buf[old_len..], name); buf[buf.len - 1] = 0; - result[i] = buf[old_len..buf.len :0]; + arr[i] = buf[old_len..buf.len :0]; } - return GroupMemberNames{ .arr = result }; + return GroupMemberNames{ ._buf = buf, .arr = arr }; } // getgrtnam returns a Group entry by name. The Group must be @@ -271,7 +278,7 @@ fn getgrnam( self: *const DB, allocator: Allocator, name: []const u8, -) error{OutOfMemory}!?Group { +) error{OutOfMemory}!?CGroup { const idx = bdz.search(self.bdz_groupname, name); const offset = self.idx_groupname2group[idx]; const group = PackedGroup.fromBytes(self.groups[offset..]).group; @@ -283,7 +290,7 @@ fn getgrnam( const namez = try allocator.dupeZ(u8, name); errdefer allocator.free(namez); - return Group{ + return CGroup{ .name = namez, .gid = group.gid(), .members = members.arr, diff --git a/lib/Group.zig b/lib/Group.zig index c71180a..47ed53b 100644 --- a/lib/Group.zig +++ b/lib/Group.zig @@ -2,67 +2,56 @@ const std = @import("std"); const mem = std.mem; const Allocator = mem.Allocator; - const Group = @This(); gid: u32, -name: [:0]const u8, -// should be [:null]const ?[:0]const u8, but it crashes the compiler. See -// https://github.com/ziglang/zig/issues/7517. -// members[members.len] is always null for cloned groups. Once the bug -// in the compiler is fixed, we should update the return type here. -members: [:null]const ?[*:0]const u8, +name: []const u8, +members: []align(1) const []const u8, -// storage of name + members +// storage of name, members and members strings. for no particular reason. +// Everything (name, members, member strings) could be allocated separately +// (one extreme) and only once (another extreme). For some reason I chose the +// right extreme, but it doesn't have to be this way. _buf: []const u8, -pub fn new( +pub fn init( allocator: Allocator, gid: u32, name: []const u8, - members: []const []const u8, + members: []align(1) const []const u8, ) error{OutOfMemory}!Group { - const buf_len = blk: { + const buf_len = @sizeOf([]const u8) * members.len + name.len + blk: { var sum: usize = 0; for (members) |member| sum += member.len; + std.debug.print("\nsum: {d}\n", .{sum}); break :blk @intCast(u32, sum); }; - _ = buf_len; - _ = allocator; - _ = gid; - _ = name; - _ = members; - return error.OutOfMemory; -} - -pub fn clone(self: *const Group, allocator: Allocator) error{OutOfMemory}!Group { - var members = try allocator.allocSentinel(?[*:0]const u8, self.members.len, null); - errdefer allocator.free(members); - - var buf = try allocator.alloc(u8, self._buf.len); + var buf = try allocator.alloc(u8, buf_len); errdefer allocator.free(buf); - mem.copy(u8, buf, self._buf); - - const our_begin = @ptrToInt(members.ptr); - const their_begin = @ptrToInt(self.members.ptr); - for (self.members) |*name_ptr, i| { - const offset = @ptrToInt(name_ptr) - their_begin; - members[i] = @intToPtr([*:0]const u8, our_begin + offset); + var ptr_end = @sizeOf([]const u8) * members.len; + var members_ptr = mem.bytesAsSlice([]const u8, buf[0..ptr_end]); + var start: usize = name.len; + for (members) |member, i| { + mem.copy(u8, buf[start..], member); + members_ptr[i] = buf[start .. start + member.len]; + start += member.len; } - + mem.copy(u8, buf[start..], name); return Group{ - .gid = self.gid, - .name = buf[0..self.name.len], - .members = members[0.. :null], + .gid = gid, + .name = buf[start .. start + name.len], + .members = members_ptr, ._buf = buf, }; } +// This could be made more efficient, but clone() is never in the hot path. +pub fn clone(self: *const Group, allocator: Allocator) error{OutOfMemory}!Group { + return init(allocator, self.gid, self.name, self.members); +} + pub fn deinit(self: *Group, allocator: Allocator) void { - allocator.free(self.name); - if (self.members[0]) |firstmember| - allocator.free(firstmember); - allocator.free(self.members); + allocator.free(self._buf); self.* = undefined; } @@ -73,11 +62,7 @@ test "Group.clone" { var member1 = mem.sliceTo(try testing.allocator.dupeZ(u8, "member1"), 0); defer testing.allocator.free(member1); - var group = Group{ - .gid = 1, - .name = "foo", - .members = &[_:null]?[*:0]const u8{ member1, "member2" }, - }; + var group = try init(testing.allocator, 1, "foo", &[_][]const u8{ member1, "member2" }); var cloned = try group.clone(testing.allocator); defer cloned.deinit(testing.allocator); @@ -87,5 +72,5 @@ test "Group.clone" { member1[0] = 'x'; try testing.expectEqual(cloned.gid, 1); try testing.expectEqualSlices(u8, cloned.name, "foo"); - //try testing.expectEqualSlices(u8, @as([*:0]const u8, cloned.members[0].?), "member1"); + try testing.expectEqualSlices(u8, cloned.members[0], "member1"); } diff --git a/lib/compress.zig b/lib/compress.zig index 322eb8c..c792815 100644 --- a/lib/compress.zig +++ b/lib/compress.zig @@ -166,6 +166,12 @@ const deltaDecompressionIterator = struct { pub fn remaining(self: *const deltaDecompressionIterator) usize { return self.vit.remaining; } + + pub fn nextMust(self: *deltaDecompressionIterator) ?u64 { + return self.next() catch |err| switch (err) { + error.Overflow => unreachable, + }; + } }; pub fn DeltaDecompressionIterator(vit: *varintSliceIterator) deltaDecompressionIterator {