const std = @import("std"); const mem = std.mem; const Allocator = mem.Allocator; const Group = @This(); gid: u32, name: []const u8, members: []const []const u8, _members_buf: []const u8 = undefined, pub fn clone(self: *const Group, allocator: Allocator) Allocator.Error!Group { const total_members_len = blk: { var sum: usize = 0; for (self.members) |member| sum += member.len; break :blk sum; }; var members = try allocator.alloc([]const u8, self.members.len); errdefer allocator.free(members); members.len = self.members.len; var buf = try allocator.alloc(u8, total_members_len); errdefer allocator.free(buf); buf.len = 0; for (self.members) |member, i| { const old_len = buf.len; buf.len += member.len; mem.copy(u8, buf[old_len..], member); members[i] = buf[old_len..buf.len]; } return Group{ .gid = self.gid, .name = try allocator.dupe(u8, self.name), .members = members, ._members_buf = buf, }; } pub fn deinit(self: *Group, allocator: Allocator) void { allocator.free(self.name); allocator.free(self.members); allocator.free(self._members_buf); self.* = undefined; } const testing = std.testing; test "Group.clone" { // TODO: how to do this on stack? var member1 = try testing.allocator.dupe(u8, "member1"); defer testing.allocator.free(member1); var group = Group{ .gid = 1, .name = "foo", .members = &[_][]const u8{ member1, "member2" }, }; var cloned = try group.clone(testing.allocator); defer cloned.deinit(testing.allocator); group.gid = 2; group.name = "bar"; member1[0] = 'x'; try testing.expectEqual(cloned.gid, 1); try testing.expectEqualSlices(u8, cloned.name, "foo"); try testing.expectEqualSlices(u8, cloned.members[0], "member1"); }