turbonss/lib/Group.zig

103 lines
3.3 KiB
Zig

const std = @import("std");
const mem = std.mem;
const meta = std.meta;
const Allocator = mem.Allocator;
const Group = @This();
gid: u32,
name: []const u8,
members: []align(1) const []const u8,
// 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 init(
allocator: Allocator,
gid: u32,
name: []const u8,
members: []align(1) const []const u8,
) error{OutOfMemory}!Group {
const buf_len = @sizeOf([]const u8) * members.len + name.len + blk: {
var sum: usize = 0;
for (members) |member| sum += member.len;
break :blk @intCast(u32, sum);
};
var buf = try allocator.alloc(u8, buf_len);
errdefer allocator.free(buf);
var ptr_end = @sizeOf([]const u8) * members.len;
var members_ptr = mem.bytesAsSlice([]const u8, buf[0..ptr_end]);
var offset: usize = ptr_end;
for (members) |member, i| {
mem.copy(u8, buf[offset..], member);
members_ptr[i] = buf[offset .. offset + member.len];
offset += member.len;
}
mem.copy(u8, buf[offset..], name);
return Group{
.gid = gid,
.name = buf[offset .. offset + 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._buf);
self.* = undefined;
}
// suggested buffer size in bytes if all strings were zero-terminated
// (for CGroup).
pub fn strlenZ(self: *const Group) usize {
var count: usize = 0;
for (self.members) |member|
count += member.len + 1;
count += ptr_size * (self.members.len + 1);
count += self.name.len + 1;
return count;
}
// Name type should be 0-terminated, members should be null-terminated, but
// sentinel is not part of the type (but is always there). Adding sentinel
// crashes zig compiler in pre-0.10. https://github.com/ziglang/zig/issues/7517
pub const CGroup = extern struct {
gid: u32,
// should be [*:0]const u8
name: [*]const u8,
// Should be [*:null]align(1) const ?[*:0]const u8
members: [*]align(1) const ?[*:0]const u8,
};
// size of the pointer to a single member.
pub const ptr_size = @sizeOf(meta.Child(meta.fieldInfo(CGroup, .members).field_type));
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 = try init(testing.allocator, 1, "foo", &[_][]const u8{ member1, "member2" });
defer group.deinit(testing.allocator);
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");
}