2022-03-25 14:31:56 +02:00
|
|
|
const std = @import("std");
|
|
|
|
|
|
|
|
const mem = std.mem;
|
|
|
|
const Allocator = mem.Allocator;
|
|
|
|
const Group = @This();
|
|
|
|
|
|
|
|
gid: u32,
|
2022-04-16 05:36:49 +03:00
|
|
|
name: []const u8,
|
|
|
|
members: []align(1) const []const u8,
|
2022-03-25 14:31:56 +02:00
|
|
|
|
2022-04-16 05:36:49 +03:00
|
|
|
// 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.
|
2022-04-11 00:30:12 +03:00
|
|
|
_buf: []const u8,
|
|
|
|
|
2022-04-16 05:36:49 +03:00
|
|
|
pub fn init(
|
2022-04-11 00:30:12 +03:00
|
|
|
allocator: Allocator,
|
|
|
|
gid: u32,
|
|
|
|
name: []const u8,
|
2022-04-16 05:36:49 +03:00
|
|
|
members: []align(1) const []const u8,
|
2022-04-11 00:30:12 +03:00
|
|
|
) error{OutOfMemory}!Group {
|
2022-04-16 05:36:49 +03:00
|
|
|
const buf_len = @sizeOf([]const u8) * members.len + name.len + blk: {
|
2022-04-11 00:30:12 +03:00
|
|
|
var sum: usize = 0;
|
|
|
|
for (members) |member| sum += member.len;
|
|
|
|
break :blk @intCast(u32, sum);
|
|
|
|
};
|
2022-04-16 05:36:49 +03:00
|
|
|
var buf = try allocator.alloc(u8, buf_len);
|
2022-04-11 00:30:12 +03:00
|
|
|
errdefer allocator.free(buf);
|
2022-04-16 05:36:49 +03:00
|
|
|
var ptr_end = @sizeOf([]const u8) * members.len;
|
|
|
|
var members_ptr = mem.bytesAsSlice([]const u8, buf[0..ptr_end]);
|
2022-04-16 06:24:55 +03:00
|
|
|
var offset: usize = ptr_end;
|
2022-04-16 05:36:49 +03:00
|
|
|
for (members) |member, i| {
|
2022-04-16 06:24:55 +03:00
|
|
|
mem.copy(u8, buf[offset..], member);
|
|
|
|
members_ptr[i] = buf[offset .. offset + member.len];
|
|
|
|
offset += member.len;
|
2022-03-26 12:06:07 +02:00
|
|
|
}
|
2022-04-16 06:24:55 +03:00
|
|
|
mem.copy(u8, buf[offset..], name);
|
2022-03-25 14:31:56 +02:00
|
|
|
return Group{
|
2022-04-16 05:36:49 +03:00
|
|
|
.gid = gid,
|
2022-04-16 06:24:55 +03:00
|
|
|
.name = buf[offset .. offset + name.len],
|
2022-04-16 05:36:49 +03:00
|
|
|
.members = members_ptr,
|
2022-04-11 00:30:12 +03:00
|
|
|
._buf = buf,
|
2022-03-25 14:31:56 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-04-16 05:36:49 +03:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2022-03-25 14:31:56 +02:00
|
|
|
pub fn deinit(self: *Group, allocator: Allocator) void {
|
2022-04-16 05:36:49 +03:00
|
|
|
allocator.free(self._buf);
|
2022-03-25 14:31:56 +02:00
|
|
|
self.* = undefined;
|
|
|
|
}
|
|
|
|
|
2022-04-18 13:11:20 +03:00
|
|
|
// buffer size in bytes if all strings were zero-terminated.
|
|
|
|
pub fn strlenZ(self: *const Group) usize {
|
|
|
|
return self._buf.len +
|
|
|
|
self.members.len + // each membername sentinel
|
|
|
|
1; // name sentinel
|
|
|
|
}
|
|
|
|
|
2022-03-25 14:31:56 +02:00
|
|
|
const testing = std.testing;
|
|
|
|
|
|
|
|
test "Group.clone" {
|
2022-03-26 12:06:07 +02:00
|
|
|
// TODO: how to do this on stack?
|
2022-04-11 00:30:12 +03:00
|
|
|
var member1 = mem.sliceTo(try testing.allocator.dupeZ(u8, "member1"), 0);
|
2022-03-26 12:06:07 +02:00
|
|
|
defer testing.allocator.free(member1);
|
2022-03-25 14:31:56 +02:00
|
|
|
|
2022-04-16 05:36:49 +03:00
|
|
|
var group = try init(testing.allocator, 1, "foo", &[_][]const u8{ member1, "member2" });
|
2022-04-16 06:24:55 +03:00
|
|
|
defer group.deinit(testing.allocator);
|
2022-03-25 14:31:56 +02:00
|
|
|
|
2022-03-26 12:06:07 +02:00
|
|
|
var cloned = try group.clone(testing.allocator);
|
|
|
|
defer cloned.deinit(testing.allocator);
|
2022-03-25 14:31:56 +02:00
|
|
|
|
2022-03-26 12:06:07 +02:00
|
|
|
group.gid = 2;
|
|
|
|
group.name = "bar";
|
|
|
|
member1[0] = 'x';
|
|
|
|
try testing.expectEqual(cloned.gid, 1);
|
|
|
|
try testing.expectEqualSlices(u8, cloned.name, "foo");
|
2022-04-16 05:36:49 +03:00
|
|
|
try testing.expectEqualSlices(u8, cloned.members[0], "member1");
|
2022-03-25 14:31:56 +02:00
|
|
|
}
|