122 lines
3.0 KiB
Zig
Executable File
122 lines
3.0 KiB
Zig
Executable File
const debug = @import("./debug.zig");
|
|
|
|
const io = @import("./io.zig");
|
|
|
|
const math = @import("./math.zig");
|
|
|
|
const stack = @import("./stack.zig");
|
|
|
|
pub fn Dense(comptime key: Key, comptime Element: type) type {
|
|
const KeySlot = Slot(key);
|
|
const Index = math.Unsigned(key.index_bits);
|
|
|
|
return struct {
|
|
capacity: usize = 0,
|
|
values: []Element = &.{},
|
|
slots: ?[*]KeySlot = null,
|
|
erase: ?[*]Index = null,
|
|
next_free: Index = 0,
|
|
|
|
const Self = @This();
|
|
|
|
pub fn fetch(self: Self, slot: KeySlot) ?*Element {
|
|
if (slot.index >= self.values.len) {
|
|
return null;
|
|
}
|
|
|
|
const redirect = &self.slots[slot.index];
|
|
|
|
if (slot.salt != redirect.salt) {
|
|
return null;
|
|
}
|
|
|
|
return &self.values[redirect.index];
|
|
}
|
|
|
|
pub fn clear(self: *Self) void {
|
|
self.next_free = 0;
|
|
self.values = self.values[0 .. 0];
|
|
|
|
{
|
|
var index = @as(usize, 0);
|
|
|
|
while (index < self.capacity) : (index += 1) {
|
|
const slot = &self.slots[index];
|
|
|
|
slot.salt = math.max(slot.salt +% 1, 1);
|
|
slot.index = index;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn deinit(self: *Self, allocator: io.Allocator) void {
|
|
io.deallocate(allocator, self.values.ptr);
|
|
io.deallocate(allocator, self.slots);
|
|
io.deallocate(allocator, self.erase);
|
|
}
|
|
|
|
pub fn grow(self: *Self, allocator: io.Allocator, growth_amount: usize) io.AllocationError!void {
|
|
const grown_capacity = self.capacity + growth_amount;
|
|
|
|
self.values = try io.reallocate(allocator, self.values, grown_capacity);
|
|
self.slots = (try io.reallocate(allocator, self.slots.?[0 .. self.values.len], grown_capacity)).ptr;
|
|
self.erase = (try io.reallocate(allocator, self.erase.?[0 .. self.values.len], grown_capacity)).ptr;
|
|
self.capacity = grown_capacity;
|
|
|
|
// Add new values to the freelist
|
|
{
|
|
var index = @intCast(Index, self.values.len);
|
|
|
|
while (index < self.capacity) : (index += 1) {
|
|
const slot = &self.slots.?[index];
|
|
|
|
slot.salt = 1;
|
|
slot.index = index;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn insert(self: *Self, allocator: io.Allocator, value: Element) io.AllocationError!KeySlot {
|
|
if (self.values.len == self.capacity) {
|
|
try self.grow(allocator, math.max(usize, 1, self.capacity));
|
|
}
|
|
|
|
const index_of_redirect = self.next_free;
|
|
const redirect = &self.slots.?[index_of_redirect];
|
|
|
|
// redirect.index points to the next free slot.
|
|
self.next_free = redirect.index;
|
|
redirect.index = @intCast(Index, self.values.len);
|
|
self.values = self.values.ptr[0 .. self.values.len + 1];
|
|
self.values[redirect.index] = value;
|
|
self.erase.?[redirect.index] = index_of_redirect;
|
|
|
|
return KeySlot{
|
|
.index = index_of_redirect,
|
|
.salt = redirect.salt,
|
|
};
|
|
}
|
|
|
|
pub fn remove(_: *Self, _: u128) bool {
|
|
// TODO: Implement.
|
|
}
|
|
};
|
|
}
|
|
|
|
pub const Key = struct {
|
|
index_bits: usize,
|
|
salt_bits: usize,
|
|
};
|
|
|
|
pub fn Slot(comptime key: Key) type {
|
|
return extern struct {
|
|
index: math.Unsigned(key.index_bits),
|
|
salt: math.Unsigned(key.salt_bits),
|
|
};
|
|
}
|
|
|
|
pub const addressable_key = Key{
|
|
.index_bits = (@bitSizeOf(usize) / 2),
|
|
.salt_bits = (@bitSizeOf(usize) / 2),
|
|
};
|