GTL/src/slot_map.zig

170 lines
5.5 KiB
Zig
Raw Normal View History

2026-07-12 19:46:40 +01:00
const std = @import("std");
const Handle = @import("handle.zig").Handle;
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn SlotMap(
comptime K: type,
comptime V: type,
) type {
return struct {
const Self = @This();
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const Entry = struct {
key: K,
value: V,
};
//
// FIELDS
//
// --- VALUES ---
values: std.ArrayList(Entry),
lookup: std.AutoHashMap(K, Handle(V)),
// --- LIFE TIMES ---
generations: std.ArrayList(u32),
free_list: std.ArrayList(u32),
alloc: std.mem.Allocator,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(alloc: std.mem.Allocator) Self {
return .{
.values = .empty,
.lookup = .init(alloc),
.generations = .empty,
.free_list = .empty,
.alloc = alloc,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self) void {
self.values.deinit(self.alloc);
self.lookup.deinit();
self.generations.deinit(self.alloc);
self.free_list.deinit(self.alloc);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn put(
self: *Self,
key: K,
value: V,
) error{ Duplicate, OutOfMemory }!Handle(V) {
// --- PREVENT DUPS ---
if (self.lookup.contains(key)) return error.Duplicate;
// --- NEW GENERATION ---
if (self.free_list.pop()) |idx| {
self.values.items[@intCast(idx)] = .{
.key = key,
.value = value,
};
self.generations.items[@intCast(idx)] += 1;
return .{
.idx = idx,
.gen = self.generations.items[idx],
};
}
// --- METADATA ---
const idx = self.values.items.len;
const handle: Handle(V) = .{
.idx = @intCast(idx),
.gen = 1,
};
// --- NEW VALUE ---
try self.values.append(self.alloc, .{
.value = value,
.key = key,
});
errdefer _ = self.values.swapRemove(idx);
// --- NEW LOOKUP ---
try self.lookup.put(key, handle);
errdefer _ = self.lookup.remove(key);
// --- NEW GENERATION ---
try self.generations.append(self.alloc, 1);
// --- RESULT ---
return handle;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn remove(
self: *Self,
handle: Handle(V),
) void {
const idx: u32 = handle.idx;
// --- CHECK IF STALE ---
if (idx >= self.generations.items.len) return;
if (self.generations.items[idx] != handle.gen) return;
// --- REMOVE KEY ---
const key = self.values.items[idx].key;
_ = self.lookup.remove(key);
// --- GENERATION ---
self.generations.items[idx] +|= 1;
self.free_list.append(self.alloc, idx) catch unreachable;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn removeByKey(self: *Self, key: K) void {
const idx = self.lookup.get(key) orelse return;
_ = self.lookup.remove(key);
self.generations.items[idx] +|= 1;
self.free_list.append(self.alloc, idx) catch unreachable;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn get(self: *Self, handle: Handle(V)) ?*V {
if (handle.idx >= self.values.items.len) return null;
if (self.generations.items[handle.idx] != handle.gen) return null;
return &self.values.items[handle.idx].value;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getByKey(self: *Self, key: K) ?*V {
const handle = self.lookup.get(key) orelse return null;
return self.get(handle);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getHandle(self: *Self, key: K) ?Handle(V) {
return self.lookup.get(key);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn contains(self: *Self, key: K) bool {
return self.lookup.contains(key);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn count(self: *Self) u32 {
return self.lookup.count();
}
};
}