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, // storage of name + members _buf: []const u8, pub fn new( allocator: Allocator, gid: u32, name: []const u8, members: []const []const u8, ) error{OutOfMemory}!Group { const buf_len = blk: { var sum: usize = 0; for (members) |member| sum += member.len; 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); 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); } return Group{ .gid = self.gid, .name = buf[0..self.name.len], .members = members[0.. :null], ._buf = buf, }; } pub fn deinit(self: *Group, allocator: Allocator) void { allocator.free(self.name); if (self.members[0]) |firstmember| allocator.free(firstmember); allocator.free(self.members); self.* = undefined; } const testing = std.testing; test "Group.clone" { // TODO: how to do this on stack? 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 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, @as([*:0]const u8, cloned.members[0].?), "member1"); }