Compare commits
No commits in common. "845582a14d5c43c0899dadba1a69afe76fd3e4dd" and "b9153e2708f5e32d3f5f5d0ba3142827042eb3cf" have entirely different histories.
845582a14d
...
b9153e2708
@ -1,33 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub const Node = struct {
|
|
||||||
id : usize,
|
|
||||||
summary: []const u8,
|
|
||||||
|
|
||||||
///
|
|
||||||
pub fn init(alloc: std.mem.Allocator, id: usize, summary: []const u8) !Node {
|
|
||||||
return .{
|
|
||||||
.id = id,
|
|
||||||
.summary = try alloc.dupe(u8, summary),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
/// pass the same allocator as init() call
|
|
||||||
pub fn deinit(self: *@This(), alloc: std.mem.Allocator) void {
|
|
||||||
alloc.free(self.summary);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
test "serialize" {
|
|
||||||
const alloc = std.testing.allocator;
|
|
||||||
var node = try Node.init(alloc, 0, "helloworld");
|
|
||||||
defer node.deinit(alloc);
|
|
||||||
|
|
||||||
var json = std.ArrayList(u8).init(alloc);
|
|
||||||
defer json.deinit();
|
|
||||||
try std.json.stringify(node, .{}, json.writer());
|
|
||||||
|
|
||||||
try std.testing.expectEqualStrings(
|
|
||||||
json.items,
|
|
||||||
\\{"id":0,"summary":"helloworld"}
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,195 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
|
|
||||||
/// A container for all existing instances of T.
|
|
||||||
/// This is like a dedicated allocator for the type, T, with a refcount system.
|
|
||||||
pub fn Store(comptime T: type) type {
|
|
||||||
const VTable = struct {
|
|
||||||
udata: ?*anyopaque = null,
|
|
||||||
deinitItem: ?*fn (?*anyopaque, *T) void = null,
|
|
||||||
};
|
|
||||||
return struct {
|
|
||||||
const Item = T;
|
|
||||||
const Slot = struct {
|
|
||||||
item : Item,
|
|
||||||
refcnt: usize,
|
|
||||||
|
|
||||||
pub fn ref(self: *@This()) void {
|
|
||||||
self.refcnt += 1;
|
|
||||||
}
|
|
||||||
pub fn unref(self: *@This()) void {
|
|
||||||
self.refcnt -= 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const Slots = std.ArrayList(*Slot);
|
|
||||||
const Error = error {
|
|
||||||
DetectedItemLeak,
|
|
||||||
};
|
|
||||||
|
|
||||||
alloc : std.mem.Allocator,
|
|
||||||
vtable: VTable,
|
|
||||||
slots : Slots,
|
|
||||||
|
|
||||||
///
|
|
||||||
pub fn init(alloc: std.mem.Allocator, vtable: VTable) @This() {
|
|
||||||
return .{
|
|
||||||
.alloc = alloc,
|
|
||||||
.vtable = vtable,
|
|
||||||
.slots = Slots.init(alloc),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
///
|
|
||||||
pub fn deinit(self: *@This()) !void {
|
|
||||||
defer self.slots.deinit();
|
|
||||||
|
|
||||||
try self.collectGarbage();
|
|
||||||
if (self.slots.items.len > 0) {
|
|
||||||
return Error.DetectedItemLeak;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a slot which contains newly-allocated item.
|
|
||||||
/// - A reference count of the returned slot must be 1
|
|
||||||
/// - The returned slot and its contents are alive till the reference count becomes 0
|
|
||||||
pub fn add(self: *@This(), item: Item) !*Slot {
|
|
||||||
for (self.slots.items) |slot| {
|
|
||||||
if (slot.refcnt == 0) {
|
|
||||||
self.deinitItem(&slot.item);
|
|
||||||
slot.* = .{ .item = item, .refcnt = 1, };
|
|
||||||
return slot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const newSlot = try self.alloc.create(Slot);
|
|
||||||
errdefer self.alloc.destroy(newSlot);
|
|
||||||
|
|
||||||
newSlot.* = .{ .item = item, .refcnt = 1, };
|
|
||||||
try self.slots.append(newSlot);
|
|
||||||
return newSlot;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Releases memory of slots whose reference count is 0.
|
|
||||||
/// Usually no need to call this function because such slots are reused.
|
|
||||||
pub fn collectGarbage(self: *@This()) !void {
|
|
||||||
var slots = self.slots.items;
|
|
||||||
|
|
||||||
var set: usize = 0;
|
|
||||||
var get: usize = 0;
|
|
||||||
while (set < slots.len) {
|
|
||||||
if (slots[set].refcnt == 0) {
|
|
||||||
self.deinitItem(&slots[set].item);
|
|
||||||
self.alloc.destroy(slots[set]);
|
|
||||||
|
|
||||||
get += 1;
|
|
||||||
if (get >= slots.len) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
slots[set] = slots[get];
|
|
||||||
} else {
|
|
||||||
set += 1;
|
|
||||||
get += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try self.slots.resize(set);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deinitItem(self: *@This(), item: *Item) void {
|
|
||||||
if (self.vtable.deinitItem != null) {
|
|
||||||
self.vtable.deinitItem.?(self.vtable.udata, item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
test "add new item" {
|
|
||||||
const Item = u16;
|
|
||||||
|
|
||||||
var sut = Store(Item).init(std.testing.allocator, .{});
|
|
||||||
defer sut.deinit() catch unreachable;
|
|
||||||
|
|
||||||
{
|
|
||||||
var slot = try sut.add(123);
|
|
||||||
defer slot.unref();
|
|
||||||
|
|
||||||
// check initial value
|
|
||||||
try std.testing.expect(slot.refcnt == 1);
|
|
||||||
try std.testing.expect(slot.item == 123);
|
|
||||||
|
|
||||||
// check refcnt handling
|
|
||||||
slot.ref();
|
|
||||||
try std.testing.expect(slot.refcnt == 2);
|
|
||||||
slot.unref();
|
|
||||||
try std.testing.expect(slot.refcnt == 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
test "reuse an expired slot by adding right after removal" {
|
|
||||||
const Item = u16;
|
|
||||||
|
|
||||||
var sut = Store(Item).init(std.testing.allocator, .{});
|
|
||||||
defer sut.deinit() catch unreachable;
|
|
||||||
|
|
||||||
var address: usize = undefined;
|
|
||||||
{
|
|
||||||
var slot = try sut.add(123);
|
|
||||||
defer slot.unref();
|
|
||||||
address = @intFromPtr(slot);
|
|
||||||
try std.testing.expect(slot.item == 123);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
var slot = try sut.add(456);
|
|
||||||
defer slot.unref();
|
|
||||||
try std.testing.expect(address == @intFromPtr(slot));
|
|
||||||
try std.testing.expect(slot.item == 456);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
test "reusing doesn't happen by adding twice" {
|
|
||||||
const Item = u16;
|
|
||||||
|
|
||||||
var sut = Store(Item).init(std.testing.allocator, .{});
|
|
||||||
defer sut.deinit() catch unreachable;
|
|
||||||
|
|
||||||
var slot1 = try sut.add(123);
|
|
||||||
defer slot1.unref();
|
|
||||||
|
|
||||||
var slot2 = try sut.add(456);
|
|
||||||
defer slot2.unref();
|
|
||||||
|
|
||||||
try std.testing.expect(slot1 != slot2);
|
|
||||||
try std.testing.expect(slot1.item == 123);
|
|
||||||
try std.testing.expect(slot2.item == 456);
|
|
||||||
}
|
|
||||||
test "chaotic addition and removals" {
|
|
||||||
const Item = u16;
|
|
||||||
|
|
||||||
var sut = Store(Item).init(std.testing.allocator, .{});
|
|
||||||
defer sut.deinit() catch unreachable;
|
|
||||||
|
|
||||||
var slots = std.ArrayList(*Store(Item).Slot).init(std.testing.allocator);
|
|
||||||
defer {
|
|
||||||
for (slots.items) |slot| { slot.unref(); }
|
|
||||||
slots.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
// add 0~300
|
|
||||||
for (0..300) |i| {
|
|
||||||
const x: u16 = @intCast(i);
|
|
||||||
try slots.append(try sut.add(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
// removes other than multiples of 3
|
|
||||||
for (0..100) |i| {
|
|
||||||
slots.orderedRemove(300 - (i+1)*3 + 2).unref();
|
|
||||||
slots.orderedRemove(300 - (i+1)*3 + 1).unref();
|
|
||||||
}
|
|
||||||
|
|
||||||
// add multiples of 3 from 300 to 600
|
|
||||||
for (0..100) |i| {
|
|
||||||
const x: u16 = @intCast(i);
|
|
||||||
try slots.append(try sut.add(300 + x*3));
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if the list is composed of multiples of 3 from 0~600
|
|
||||||
for (0..200) |i| {
|
|
||||||
const x: u16 = @intCast(i);
|
|
||||||
try std.testing.expectEqual(slots.items[i].item, x*3);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,4 @@
|
|||||||
pub const Digraph = @import("./Digraph.zig").Digraph;
|
pub const Digraph = @import("./Digraph.zig").Digraph;
|
||||||
pub const Node = @import("./Node.zig").Node;
|
|
||||||
pub const Store = @import("./Store.zig").Store;
|
|
||||||
|
|
||||||
test {
|
test {
|
||||||
@import("std").testing.refAllDecls(@This());
|
@import("std").testing.refAllDecls(@This());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user