1
Fork 0
turbonss/src/Group.zig

160 lines
5.2 KiB
Zig
Raw Normal View History

const std = @import("std");
2022-06-07 05:32:21 +03:00
const os = std.os;
const mem = std.mem;
2022-06-07 05:32:21 +03:00
const fmt = std.fmt;
2022-04-19 07:18:41 +03:00
const meta = std.meta;
2022-06-07 05:32:21 +03:00
const maxInt = std.math.maxInt;
const Allocator = mem.Allocator;
2022-06-07 05:32:21 +03:00
const ArrayList = std.ArrayList;
const Group = @This();
gid: u32,
2022-04-16 05:36:49 +03:00
name: []const u8,
members: []align(1) const []const u8,
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);
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-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);
}
pub fn deinit(self: *Group, allocator: Allocator) void {
2022-04-16 05:36:49 +03:00
allocator.free(self._buf);
self.* = undefined;
}
2022-06-07 05:32:21 +03:00
pub const FromReaderError = error{ InvalidRecord, OutOfMemory } || os.ReadError;
pub fn fromReader(allocator: Allocator, reader: anytype) FromReaderError![]Group {
var groups = ArrayList(Group).init(allocator);
2022-06-15 12:56:19 +03:00
errdefer {
for (groups.items) |*group| group.deinit(allocator);
groups.deinit();
}
2022-06-07 05:32:21 +03:00
var member_ptrs = ArrayList([]const u8).init(allocator);
defer member_ptrs.deinit();
var line = ArrayList(u8).init(allocator);
defer line.deinit();
2022-07-02 21:19:07 +03:00
var i: usize = 0;
while (true) : (i += 1) {
2022-06-07 05:32:21 +03:00
// TODO: catch and interpret different errors
const max = std.math.maxInt(u32);
reader.readUntilDelimiterArrayList(&line, '\n', max) catch |err| switch (err) {
error.EndOfStream => break,
error.StreamTooLong => unreachable,
else => |e| return e,
};
var it = mem.split(u8, line.items, ":");
const name = it.next() orelse return error.InvalidRecord;
_ = it.next() orelse return error.InvalidRecord; // password
const gids = it.next() orelse return error.InvalidRecord;
const members_commas = it.next() orelse return error.InvalidRecord;
if (it.next() != null) return error.InvalidRecord;
const gid = fmt.parseInt(u32, gids, 10) catch return error.InvalidRecord;
var members_it = mem.split(u8, members_commas, ",");
while (members_it.next()) |member|
2022-07-03 09:44:09 +03:00
if (member.len != 0)
try member_ptrs.append(member);
2022-06-07 05:32:21 +03:00
2022-07-02 21:19:07 +03:00
const group = try init(allocator, gid, name, member_ptrs.items);
try groups.append(group);
2022-06-07 05:32:21 +03:00
member_ptrs.shrinkRetainingCapacity(0);
}
return groups.toOwnedSlice();
}
2022-06-07 13:14:50 +03:00
pub fn writeTo(self: *const Group, writer: anytype) os.WriteError!void {
try writer.print("{s}:x:{d}:", .{ self.name, self.gid });
if (self.members.len != 0) {
try writer.writeAll(self.members[0]);
for (self.members[1..]) |member|
try writer.print(",{s}", .{member});
}
2022-06-07 13:14:50 +03:00
try writer.writeByte('\n');
}
2022-04-19 10:46:02 +03:00
// suggested buffer size in bytes if all strings were zero-terminated
// (for CGroup).
2022-04-18 13:11:20 +03:00
pub fn strlenZ(self: *const Group) usize {
2022-04-19 07:18:41 +03:00
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;
2022-04-18 13:11:20 +03:00
}
2022-04-19 10:46:02 +03:00
pub const CGroup = extern struct {
2022-07-14 19:14:47 +03:00
gr_name: [*:0]const u8,
gr_passwd: [*:0]const u8 = "x",
gr_gid: u32,
2022-04-19 10:46:02 +03:00
// Should be [*:null]align(1) const ?[*:0]const u8
2022-07-07 07:00:13 +03:00
// https://github.com/ziglang/zig/issues/7517
2022-07-14 19:14:47 +03:00
gr_mem: [*]align(1) const ?[*:0]const u8,
2022-04-19 10:46:02 +03:00
};
// size of the pointer to a single member.
2022-07-14 19:14:47 +03:00
pub const ptr_size = @sizeOf(meta.Child(meta.fieldInfo(CGroup, .gr_mem).field_type));
2022-04-19 10:46:02 +03:00
const testing = std.testing;
2022-07-12 12:59:47 +03:00
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-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-26 12:06:07 +02:00
var cloned = try group.clone(testing.allocator);
defer cloned.deinit(testing.allocator);
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");
}