130 lines
3.1 KiB
Zig
130 lines
3.1 KiB
Zig
const std = @import("std");
|
|
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
pub const EndpointID = u32;
|
|
|
|
pub const Link = struct {
|
|
a: EndpointID,
|
|
b: EndpointID,
|
|
|
|
pub fn init(a: EndpointID, b: EndpointID) Link {
|
|
return if (a <= b)
|
|
.{ .a = a, .b = b }
|
|
else
|
|
.{ .a = b, .b = a };
|
|
}
|
|
|
|
pub fn eql(self: Link, other: Link) bool {
|
|
return self.a == other.a and self.b == other.b;
|
|
}
|
|
};
|
|
|
|
gpa: Allocator,
|
|
broken_links: std.ArrayList(Link),
|
|
|
|
pub fn init(self: *@This(), gpa: Allocator) void {
|
|
self.gpa = gpa;
|
|
self.broken_links = .empty;
|
|
}
|
|
|
|
pub fn deinit(self: *@This()) void {
|
|
self.broken_links.deinit(self.gpa);
|
|
}
|
|
|
|
pub fn breakLink(self: *@This(), a: EndpointID, b: EndpointID) Allocator.Error!void {
|
|
if (a == b)
|
|
return;
|
|
|
|
const link = Link.init(a, b);
|
|
if (self.contains(link))
|
|
return;
|
|
|
|
try self.broken_links.append(self.gpa, link);
|
|
}
|
|
|
|
pub fn healLink(self: *@This(), a: EndpointID, b: EndpointID) void {
|
|
const link = Link.init(a, b);
|
|
for (self.broken_links.items, 0..) |item, i| {
|
|
if (item.eql(link)) {
|
|
_ = self.broken_links.swapRemove(i);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn clear(self: *@This()) void {
|
|
self.broken_links.clearRetainingCapacity();
|
|
}
|
|
|
|
pub fn isBroken(self: *const @This(), a: EndpointID, b: EndpointID) bool {
|
|
if (a == b)
|
|
return false;
|
|
|
|
return self.contains(Link.init(a, b));
|
|
}
|
|
|
|
pub fn links(self: *const @This()) []const Link {
|
|
return self.broken_links.items;
|
|
}
|
|
|
|
fn contains(self: *const @This(), link: Link) bool {
|
|
for (self.broken_links.items) |item| {
|
|
if (item.eql(link))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
test "broken links are symmetric" {
|
|
var partition: @This() = undefined;
|
|
partition.init(std.testing.allocator);
|
|
defer partition.deinit();
|
|
|
|
try partition.breakLink(1, 2);
|
|
|
|
try std.testing.expect(partition.isBroken(1, 2));
|
|
try std.testing.expect(partition.isBroken(2, 1));
|
|
try std.testing.expect(!partition.isBroken(1, 1));
|
|
}
|
|
|
|
test "breaking the same link twice does not duplicate it" {
|
|
var partition: @This() = undefined;
|
|
partition.init(std.testing.allocator);
|
|
defer partition.deinit();
|
|
|
|
try partition.breakLink(1, 2);
|
|
try partition.breakLink(2, 1);
|
|
|
|
try std.testing.expectEqual(@as(usize, 1), partition.links().len);
|
|
}
|
|
|
|
test "healing a link removes only that link" {
|
|
var partition: @This() = undefined;
|
|
partition.init(std.testing.allocator);
|
|
defer partition.deinit();
|
|
|
|
try partition.breakLink(1, 2);
|
|
try partition.breakLink(2, 3);
|
|
|
|
partition.healLink(2, 1);
|
|
|
|
try std.testing.expect(!partition.isBroken(1, 2));
|
|
try std.testing.expect(partition.isBroken(2, 3));
|
|
}
|
|
|
|
test "clear heals every link" {
|
|
var partition: @This() = undefined;
|
|
partition.init(std.testing.allocator);
|
|
defer partition.deinit();
|
|
|
|
try partition.breakLink(1, 2);
|
|
try partition.breakLink(3, 4);
|
|
|
|
partition.clear();
|
|
|
|
try std.testing.expectEqual(@as(usize, 0), partition.links().len);
|
|
try std.testing.expect(!partition.isBroken(1, 2));
|
|
try std.testing.expect(!partition.isBroken(3, 4));
|
|
}
|