1
Fork 0
turbonss/src/sections.zig

240 lines
7.4 KiB
Zig
Raw Normal View History

2022-02-28 10:31:14 +02:00
const std = @import("std");
const unicode = std.unicode;
const sort = std.sort;
const Allocator = std.mem.Allocator;
const ArrayList = std.ArrayList;
const StringHashMap = std.StringHashMap;
const AutoHashMap = std.AutoHashMap;
const BufSet = std.BufSet;
const pad = @import("padding.zig");
const userImport = @import("user.zig");
const groupImport = @import("group.zig");
const User = userImport.User;
const Group = groupImport.Group;
const Corpus = struct {
2022-03-01 05:47:44 +02:00
arena: std.heap.ArenaAllocator,
2022-02-28 10:31:14 +02:00
// sorted by name, by unicode codepoint
users: []User,
// sorted by gid
groups: []Group,
// pointing to `users` and `groups` slices above.
name2user: StringHashMap(*const User),
uid2user: AutoHashMap(u32, *const User),
name2group: StringHashMap(*const Group),
gid2group: AutoHashMap(u32, *const Group),
groupname2users: StringHashMap(ArrayList(*const User)),
username2groups: StringHashMap(ArrayList(*const Group)),
pub const initErr = Allocator.Error ||
error{InvalidUtf8} ||
error{Duplicate} ||
error{NotFound};
pub fn init(
2022-03-01 05:47:44 +02:00
baseAllocator: Allocator,
2022-02-28 10:31:14 +02:00
usersConst: []const User,
groupsConst: []const Group,
) initErr!Corpus {
2022-03-01 05:47:44 +02:00
var arena = std.heap.ArenaAllocator.init(baseAllocator);
var allocator = arena.allocator();
2022-02-28 10:31:14 +02:00
var users = try allocator.alloc(User, usersConst.len);
var groups = try allocator.alloc(Group, groupsConst.len);
std.mem.copy(User, users, usersConst);
std.mem.copy(Group, groups, groupsConst);
sort.sort(User, users, {}, cmpUser);
sort.sort(Group, groups, {}, cmpGroup);
var name2user = StringHashMap(*const User).init(allocator);
var uid2user = AutoHashMap(u32, *const User).init(allocator);
var name2group = StringHashMap(*const Group).init(allocator);
var gid2group = AutoHashMap(u32, *const Group).init(allocator);
for (users) |user| {
var res1 = try name2user.getOrPut(user.name);
if (res1.found_existing) {
return error.Duplicate;
}
res1.value_ptr.* = &user;
var res2 = try uid2user.getOrPut(user.uid);
if (res2.found_existing) {
return error.Duplicate;
}
res2.value_ptr.* = &user;
}
for (groups) |group| {
var res1 = try name2group.getOrPut(group.name);
if (res1.found_existing) {
return error.Duplicate;
}
res1.value_ptr.* = &group;
var res2 = try gid2group.getOrPut(group.gid);
if (res2.found_existing) {
return error.Duplicate;
}
res2.value_ptr.* = &group;
}
var groupname2users = StringHashMap(ArrayList(*const User)).init(allocator);
var username2groups = StringHashMap(ArrayList(*const Group)).init(allocator);
for (groups) |group| {
const cnt = group.members.count();
var members = try ArrayList(*const User).initCapacity(allocator, cnt);
var it = group.members.iterator();
while (it.next()) |memberName| {
if (name2user.get(memberName.*)) |user| {
members.appendAssumeCapacity(user);
} else {
return error.NotFound;
}
2022-03-01 05:47:44 +02:00
var groupUsers: *ArrayList(*const Group) = undefined;
2022-02-28 10:31:14 +02:00
var result = try username2groups.getOrPut(memberName.*);
2022-03-01 05:47:44 +02:00
if (!result.found_existing) {
groupUsers = &ArrayList(*const Group).init(allocator);
result.value_ptr = groupUsers;
2022-02-28 10:31:14 +02:00
} else {
2022-03-01 05:47:44 +02:00
groupUsers = result.value_ptr;
2022-02-28 10:31:14 +02:00
}
2022-03-01 05:47:44 +02:00
try groupUsers.*.append(&group);
2022-02-28 10:31:14 +02:00
}
var result = try groupname2users.getOrPut(group.name);
if (result.found_existing) {
return error.Duplicate;
}
result.value_ptr.* = members;
}
return Corpus{
2022-03-01 05:47:44 +02:00
.arena = arena,
2022-02-28 10:31:14 +02:00
.users = users,
.groups = groups,
.name2user = name2user,
.uid2user = uid2user,
.name2group = name2group,
.gid2group = gid2group,
.groupname2users = groupname2users,
.username2groups = username2groups,
};
}
2022-03-01 05:47:44 +02:00
pub fn deinit(self: *Corpus) void {
self.arena.deinit();
self.* = undefined;
}
2022-02-28 10:31:14 +02:00
};
// cmpUser compares two users for sorting. By username's utf8 codepoints, ascending.
fn cmpUser(_: void, a: User, b: User) bool {
var utf8_a = (unicode.Utf8View.init(a.name) catch unreachable).iterator();
var utf8_b = (unicode.Utf8View.init(b.name) catch unreachable).iterator();
while (utf8_a.nextCodepoint()) |codepoint_a| {
if (utf8_b.nextCodepoint()) |codepoint_b| {
if (codepoint_a != codepoint_b) {
return codepoint_a < codepoint_b;
}
} else {
// a is a prefix of b. It is thus shorter.
return false;
}
}
// b is a prefix of a
return true;
}
fn cmpGroup(_: void, a: Group, b: Group) bool {
return a.gid < b.gid;
}
const testing = std.testing;
fn someMembers(allocator: Allocator, members: []const []const u8) !BufSet {
var bufset = BufSet.init(allocator);
for (members) |member| {
try bufset.insert(member);
}
return bufset;
}
test "test corpus" {
const allocator = testing.allocator;
var users = [_]User{ User{
.uid = 1000,
.gid = 1000,
.name = "vidmantas",
.gecos = "Vidmantas Kaminskas",
.home = "/home/vidmantas",
.shell = "/bin/bash",
}, User{
.uid = 0,
.gid = std.math.maxInt(u32),
.name = "Name" ** 8,
.gecos = "Gecos" ** 51,
.home = "Home" ** 16,
.shell = "She.LllL" ** 8,
}, User{
.uid = 1002,
.gid = 1002,
.name = "svc-bar",
.gecos = "",
.home = "/",
.shell = "/",
} };
2022-03-01 05:47:44 +02:00
var members1 = try someMembers(allocator, &[_][]const u8{"vidmantas"});
defer members1.deinit();
var members2 = try someMembers(allocator, &[_][]const u8{ "svc-bar", "Name" ** 8 });
defer members2.deinit();
var members3 = try someMembers(allocator, &[_][]const u8{});
defer members3.deinit();
2022-02-28 10:31:14 +02:00
var groups = [_]Group{ Group{
.gid = 1000,
.name = "vidmantas",
2022-03-01 05:47:44 +02:00
.members = members1,
2022-02-28 10:31:14 +02:00
}, Group{
.gid = 1234,
.name = "service-account",
2022-03-01 05:47:44 +02:00
.members = members2,
2022-02-28 10:31:14 +02:00
}, Group{
.gid = 9999,
.name = "empty",
2022-03-01 05:47:44 +02:00
.members = members3,
2022-02-28 10:31:14 +02:00
} };
2022-03-01 05:47:44 +02:00
var corpus = try Corpus.init(allocator, users[0..], groups[0..]);
defer corpus.deinit();
try testing.expectEqualStrings(corpus.users[0].name, "Name" ** 8);
try testing.expectEqualStrings(corpus.users[1].name, "svc-bar");
try testing.expectEqualStrings(corpus.users[2].name, "vidmantas");
2022-02-28 10:31:14 +02:00
}
fn testUser(name: []const u8) User {
var result = std.mem.zeroes(User);
result.name = name;
return result;
}
test "users compare function" {
const a = testUser("a");
const b = testUser("b");
const bb = testUser("bb");
try testing.expect(cmpUser({}, a, b));
try testing.expect(!cmpUser({}, b, a));
try testing.expect(cmpUser({}, a, bb));
try testing.expect(!cmpUser({}, bb, a));
try testing.expect(cmpUser({}, b, bb));
try testing.expect(!cmpUser({}, bb, b));
}