GTL/src/fixed_slot_map.zig

130 lines
4 KiB
Zig
Raw Normal View History

2026-08-02 13:23:12 +01:00
//! ----------------------------------------------------
//! ----------------------------------------------------
const std = @import("std");
const FixedArrayList = @import("fixed_array_list.zig").FixedArrayList;
const Handle = @import("handle.zig").Handle;
/// ----------------------------------------------------
/// # Example
///
/// ```zig
2026-08-02 13:23:12 +01:00
/// var materials: FixedSlotMap(Material, 3) = .empty;
///
/// _ = try materials.put(.{});
/// _ = try materials.put(.{});
/// const handle = try materials.put(.{ .unique = .{ .topology = .line_list } });
///
/// if (materials.get(handle)) |v| {
/// std.debug.print("Material: {}\n", .{v.unique.topology});
/// }
/// ```
/// ----------------------------------------------------
pub fn FixedSlotMap(
comptime T: type,
comptime Capacity: usize,
) type {
return struct {
const Self = @This();
//
// FIELDS
//
// --- VALUES ---
values: FixedArrayList(T, Capacity),
// --- LIFE TIMES ---
generations: FixedArrayList(u32, Capacity),
free_list: FixedArrayList(u32, Capacity),
2026-08-03 16:30:26 +01:00
// --- GENERATION COUNTER ---
next_gen: u32 = 0,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const empty = Self{
.values = .empty,
.generations = .empty,
.free_list = .empty,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn put(
self: *Self,
value: T,
) error{Full}!Handle(T) {
2026-08-03 16:30:26 +01:00
// --- REUSE A FREED SLOT ---
if (self.free_list.pop()) |idx| {
2026-08-03 16:30:26 +01:00
const gen = self.mint();
self.values.items()[idx] = value;
self.generations.items()[idx] = gen;
return .{
.idx = idx,
2026-08-03 16:30:26 +01:00
.gen = gen,
};
}
// --- METADATA ---
2026-08-03 16:30:26 +01:00
const idx: u32 = @intCast(self.values.items().len);
const gen = self.mint();
// --- NEW VALUE ---
try self.values.append(value);
2026-08-03 16:30:26 +01:00
errdefer _ = self.values.pop();
try self.generations.append(gen);
// --- RESULT ---
2026-08-03 16:30:26 +01:00
return .{
.idx = idx,
.gen = gen,
};
}
/// ----------------------------------------------------
2026-08-03 16:30:26 +01:00
/// Returns an error if the free list could not grow
/// Stale handles are silently ignored
/// ----------------------------------------------------
pub fn remove(
self: *Self,
handle: Handle(T),
2026-08-03 16:30:26 +01:00
) error{OutOfMemory}!void {
const idx: u32 = handle.idx;
// --- CHECK IF STALE ---
2026-08-03 16:30:26 +01:00
if (idx >= self.generations.items().len) return;
if (self.generations.items()[idx] != handle.gen) return;
2026-08-03 16:30:26 +01:00
// --- FREE ---
try self.free_list.append(idx);
self.generations.items()[idx] = self.mint();
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn get(self: *Self, handle: Handle(T)) ?*T {
if (handle.idx >= self.values.len()) return null;
2026-08-03 16:30:26 +01:00
if (self.generations.items()[handle.idx] != handle.gen) return null;
return &self.values.items()[handle.idx];
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn clear(self: *Self) void {
self.values.clear();
self.generations.clear();
self.free_list.clear();
}
/// ----------------------------------------------------
/// ----------------------------------------------------
2026-08-03 16:30:26 +01:00
fn mint(self: *Self) u32 {
const gen = self.next_gen +% 1;
self.next_gen = gen;
return gen;
}
};
}