From 3c4455efeb34de62d4ef39176eb9aa65f6c1e782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Tue, 1 Mar 2022 11:01:45 +0200 Subject: [PATCH] cloning users and groups --- src/group.zig | 52 +++++++++++++++++++++++++++++++++++++++- src/sections.zig | 62 +++++++++++++++++++++++++++++------------------- src/user.zig | 56 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+), 25 deletions(-) diff --git a/src/group.zig b/src/group.zig index 5f12b81..fed1a20 100644 --- a/src/group.zig +++ b/src/group.zig @@ -7,11 +7,28 @@ const InvalidRecord = validate.InvalidRecord; const mem = std.mem; const Allocator = mem.Allocator; const ArrayList = std.ArrayList; +const BufSet = std.BufSet; pub const Group = struct { gid: u32, name: []const u8, - members: std.BufSet, + members: BufSet, + + pub fn clone(self: *const Group, allocator: Allocator) Allocator.Error!Group { + var name = try allocator.alloc(u8, self.name.len); + mem.copy(u8, name, self.name); + return Group{ + .gid = self.gid, + .name = name, + .members = try self.members.cloneWithAllocator(allocator), + }; + } + + pub fn deinit(self: *Group, allocator: Allocator) void { + allocator.free(self.name); + self.members.deinit(); + self.* = undefined; + } }; const GroupStored = struct { @@ -119,6 +136,18 @@ const PackedGroup = struct { const testing = std.testing; +// someMembers constructs a bufset from an allocator and a list of strings. +pub fn someMembers( + allocator: Allocator, + members: []const []const u8, +) Allocator.Error!BufSet { + var bufset = BufSet.init(allocator); + for (members) |member| { + try bufset.insert(member); + } + return bufset; +} + test "PackedGroup alignment" { try testing.expectEqual(@sizeOf(PackedGroup) * 8, @bitSizeOf(PackedGroup)); } @@ -155,3 +184,24 @@ test "construct PackedGroups" { try testing.expectEqual(groups.len, i); } } + +test "Group.clone" { + var allocator = testing.allocator; + var arena = std.heap.ArenaAllocator.init(allocator); + defer arena.deinit(); + + var members = BufSet.init(allocator); + try members.insert("member1"); + try members.insert("member2"); + defer members.deinit(); + + var cloned = try members.cloneWithAllocator(arena.allocator()); + + cloned.remove("member1"); + try cloned.insert("member4"); + try testing.expect(members.contains("member1")); + try testing.expect(!members.contains("member4")); + + try testing.expect(!cloned.contains("member1")); + try testing.expect(cloned.contains("member4")); +} diff --git a/src/sections.zig b/src/sections.zig index 1dce9b3..4a38690 100644 --- a/src/sections.zig +++ b/src/sections.zig @@ -44,8 +44,12 @@ const Corpus = struct { var users = try allocator.alloc(User, usersConst.len); var groups = try allocator.alloc(Group, groupsConst.len); - std.mem.copy(User, users, usersConst); - std.mem.copy(Group, groups, groupsConst); + for (usersConst) |user, i| { + users[i] = try user.clone(allocator); + } + for (groupsConst) |group, i| { + groups[i] = try group.clone(allocator); + } sort.sort(User, users, {}, cmpUser); sort.sort(Group, groups, {}, cmpGroup); @@ -84,8 +88,10 @@ const Corpus = struct { var groupname2users = StringHashMap(ArrayList(*const User)).init(allocator); var username2groups = StringHashMap(ArrayList(*const Group)).init(allocator); for (groups) |group| { - const cnt = group.members.count(); - var members = try ArrayList(*const User).initCapacity(allocator, cnt); + var members = try ArrayList(*const User).initCapacity( + allocator, + group.members.count(), + ); var it = group.members.iterator(); while (it.next()) |memberName| { @@ -95,15 +101,11 @@ const Corpus = struct { return error.NotFound; } - var groupUsers: *ArrayList(*const Group) = undefined; - var result = try username2groups.getOrPut(memberName.*); - if (!result.found_existing) { - groupUsers = &ArrayList(*const Group).init(allocator); - result.value_ptr = groupUsers; - } else { - groupUsers = result.value_ptr; + var groupsOfMember = try username2groups.getOrPut(memberName.*); + if (!groupsOfMember.found_existing) { + groupsOfMember.value_ptr.* = ArrayList(*const Group).init(allocator); } - try groupUsers.*.append(&group); + try groupsOfMember.value_ptr.*.append(&group); } var result = try groupname2users.getOrPut(group.name); @@ -156,14 +158,6 @@ fn cmpGroup(_: void, a: Group, b: Group) bool { const testing = std.testing; -fn someMembers(allocator: Allocator, members: []const []const u8) !BufSet { - var bufset = BufSet.init(allocator); - for (members) |member| { - try bufset.insert(member); - } - return bufset; -} - test "test corpus" { const allocator = testing.allocator; @@ -190,13 +184,22 @@ test "test corpus" { .shell = "/", } }; - var members1 = try someMembers(allocator, &[_][]const u8{"vidmantas"}); + var members1 = try groupImport.someMembers( + allocator, + &[_][]const u8{"vidmantas"}, + ); defer members1.deinit(); - var members2 = try someMembers(allocator, &[_][]const u8{ "svc-bar", "Name" ** 8 }); + var members2 = try groupImport.someMembers( + allocator, + &[_][]const u8{ "svc-bar", "Name" ** 8 }, + ); defer members2.deinit(); - var members3 = try someMembers(allocator, &[_][]const u8{}); + var members3 = try groupImport.someMembers( + allocator, + &[_][]const u8{ "svc-bar", "Name" ** 8, "vidmantas" }, + ); defer members3.deinit(); var groups = [_]Group{ Group{ @@ -209,7 +212,7 @@ test "test corpus" { .members = members2, }, Group{ .gid = 9999, - .name = "empty", + .name = "all", .members = members3, } }; @@ -218,6 +221,17 @@ test "test corpus" { try testing.expectEqualStrings(corpus.users[0].name, "Name" ** 8); try testing.expectEqualStrings(corpus.users[1].name, "svc-bar"); try testing.expectEqualStrings(corpus.users[2].name, "vidmantas"); + + //if (corpus.uid2user.get(1000)) |user| { + // std.debug.print("user: {s}\n", .{user}); + // std.debug.print("user.*.name: {d}\n", .{user.*.name}); + // try testing.expectEqualStrings(user.*.name, "vidmantas"); + //} else { + // try testing.expect(false); + //} + //try testing.expectEqualStrings(corpus.uid2user.get(1000).?.name, "vidmantas"); + //try testing.expectEqualStrings(corpus.uid2user.get(0).?.name, "Name" ** 8); + //try testing.expectEqualStrings(corpus.uid2user.get(1002).?.name, "svc-bar"); } fn testUser(name: []const u8) User { diff --git a/src/user.zig b/src/user.zig index b2e6e9a..18f59af 100644 --- a/src/user.zig +++ b/src/user.zig @@ -21,6 +21,44 @@ pub const User = struct { gecos: []const u8, home: []const u8, shell: []const u8, + + // deep-clones a User record with a given Allocator. + pub fn clone(self: *const User, allocator: Allocator) Allocator.Error!User { + const stringdata = try allocator.alloc(u8, self.stringdata_len()); + //std.debug.print("\nptr to stringdata: {d}\n", .{@ptrToInt(stringdata.ptr)}); + const gecos_start = self.name.len; + const home_start = gecos_start + self.gecos.len; + const shell_start = home_start + self.shell.len; + mem.copy(u8, stringdata[0..self.name.len], self.name); + mem.copy(u8, stringdata[gecos_start..], self.gecos); + mem.copy(u8, stringdata[home_start..], self.home); + mem.copy(u8, stringdata[shell_start..], self.shell); + + const u = User{ + .uid = self.uid, + .gid = self.gid, + .name = stringdata[0..self.name.len], + .gecos = stringdata[gecos_start .. gecos_start + self.gecos.len], + .home = stringdata[home_start .. home_start + self.home.len], + .shell = stringdata[shell_start .. shell_start + self.shell.len], + }; + + return u; + } + + fn stringdata_len(self: *const User) usize { + return self.name.len + + self.gecos.len + + self.home.len + + self.shell.len; + } + + pub fn deinit(self: *const User, allocator: Allocator) void { + //allocator.free(self.stringdata); + //self.* = undefined; + const slice = self.home.ptr[0..self.stringdata_len()]; + allocator.free(slice); + } }; pub const PackedUserMut = packedUser(false); @@ -389,3 +427,21 @@ test "PackedUser.maxSize()" { try PackedUserConst.packTo(&buf, largeUser, testShellIndex); try testing.expectEqual(PackedUserConst.maxSize(), buf.items.len); } + +test "User.clone" { + var allocator = testing.allocator; + const user = User{ + .uid = 1000, + .gid = 1000, + .name = "vidmantas", + .gecos = "Vidmantas Kaminskas", + .home = "/home/vidmantas", + .shell = "/bin/bash", + }; + var user2 = try user.clone(allocator); + user2.shell = "/bin/zsh"; + + defer user2.deinit(allocator); + + try testing.expectEqualStrings(user.shell, "/bin/bash"); +}