GTL/src/slot_map.zig

128 lines
4 KiB
Zig
Raw Normal View History

2026-08-02 13:23:12 +01:00
//! ----------------------------------------------------
//! ----------------------------------------------------
2026-07-12 19:46:40 +01:00
const std = @import("std");
const Handle = @import("handle.zig").Handle;
/// ----------------------------------------------------
/// # Example
///
/// ```zig
/// // --- MATERIALS ---
/// var materials: SlotMap(Material) = .init(alloc);
/// defer materials.deinit();
///
/// // --- ADD MATERIALS ---
/// _ = try materials.put(.{ .style = .{ .albedo = .{ 0.8, 0.2, 0.2, 1.0 } } });
/// _ = try materials.put(.{ .unique = .{ .topology = .line_list } });
/// ```
2026-07-12 19:46:40 +01:00
/// ----------------------------------------------------
2026-07-12 21:22:53 +01:00
pub fn SlotMap(comptime T: type) type {
2026-07-12 19:46:40 +01:00
return struct {
const Self = @This();
//
// FIELDS
//
// --- VALUES ---
2026-07-12 21:22:53 +01:00
values: std.ArrayList(T),
2026-07-12 19:46:40 +01:00
// --- LIFE TIMES ---
generations: std.ArrayList(u32),
free_list: std.ArrayList(u32),
2026-07-12 21:22:53 +01:00
// --- MEMORY ---
2026-07-12 19:46:40 +01:00
alloc: std.mem.Allocator,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(alloc: std.mem.Allocator) Self {
return .{
.values = .empty,
.generations = .empty,
.free_list = .empty,
.alloc = alloc,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self) void {
self.values.deinit(self.alloc);
self.generations.deinit(self.alloc);
self.free_list.deinit(self.alloc);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn put(
self: *Self,
2026-07-12 21:22:53 +01:00
value: T,
) error{OutOfMemory}!Handle(T) {
2026-07-12 19:46:40 +01:00
// --- NEW GENERATION ---
if (self.free_list.pop()) |idx| {
2026-07-12 21:22:53 +01:00
self.values.items[@intCast(idx)] = value;
2026-07-12 19:46:40 +01:00
self.generations.items[@intCast(idx)] += 1;
return .{
.idx = idx,
.gen = self.generations.items[idx],
};
}
// --- METADATA ---
const idx = self.values.items.len;
2026-07-12 21:22:53 +01:00
const handle: Handle(T) = .{
2026-07-12 19:46:40 +01:00
.idx = @intCast(idx),
.gen = 1,
};
// --- NEW VALUE ---
2026-07-12 21:22:53 +01:00
try self.values.append(self.alloc, value);
2026-07-12 19:46:40 +01:00
errdefer _ = self.values.swapRemove(idx);
// --- NEW GENERATION ---
try self.generations.append(self.alloc, 1);
// --- RESULT ---
return handle;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn remove(
self: *Self,
2026-07-12 21:22:53 +01:00
handle: Handle(T),
2026-07-12 19:46:40 +01:00
) void {
const idx: u32 = handle.idx;
// --- CHECK IF STALE ---
if (idx >= self.generations.items.len) return;
if (self.generations.items[idx] != handle.gen) return;
// --- GENERATION ---
self.generations.items[idx] +|= 1;
self.free_list.append(self.alloc, idx) catch |err| {
std.debug.print("[SLOTMAP_ERROR] {s}\n", .{@errorName(err)});
};
2026-07-12 19:46:40 +01:00
}
/// ----------------------------------------------------
/// ----------------------------------------------------
2026-07-12 21:22:53 +01:00
pub fn get(self: *Self, handle: Handle(T)) ?*T {
2026-07-12 19:46:40 +01:00
if (handle.idx >= self.values.items.len) return null;
if (self.generations.items[handle.idx] != handle.gen) return null;
2026-07-12 21:22:53 +01:00
return &self.values.items[handle.idx];
2026-07-12 19:46:40 +01:00
}
2026-07-13 00:50:40 +01:00
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn items(self: *Self) []T {
return self.values.items;
}
2026-07-12 19:46:40 +01:00
};
}