Added KeyedSlotMap

This commit is contained in:
abux 2026-07-12 21:22:53 +01:00
parent e5cf308609
commit 5c8e8434ec
4 changed files with 151 additions and 81 deletions

View file

@ -1,6 +1,6 @@
**Includes** **Includes**
- ANSI - ANSI
- SlotMap - SlotMap | KeyedSlotMap
- Custom Log - Custom Log
- TypeID - TypeID
@ -14,10 +14,10 @@ log.trace("...", .{});
log.fatal("...", .{}); log.fatal("...", .{});
``` ```
**SlotMap** **KeyedSlotMap**
```zig ```zig
// --- SHADER MAP --- // --- SHADER MAP ---
var shaders: SlotMap(ShaderID, GPUShader) = .init(alloc); var shaders: KeyedSlotMap(ShaderID, GPUShader) = .init(alloc);
defer shaders.deinit(); defer shaders.deinit();
// --- NEW SHADER --- // --- NEW SHADER ---

View file

@ -12,6 +12,7 @@ pub const log = @import("log.zig");
pub const ansi = @import("ansi.zig"); pub const ansi = @import("ansi.zig");
pub const Handle = @import("handle.zig").Handle; pub const Handle = @import("handle.zig").Handle;
pub const SlotMap = @import("slot_map.zig").SlotMap; pub const SlotMap = @import("slot_map.zig").SlotMap;
pub const KeyedSlotMap = @import("keyed_slot_map.zig").KeyedSlotMap;
pub const TypeID = @import("type_id.zig").of; pub const TypeID = @import("type_id.zig").of;
// //
@ -49,11 +50,11 @@ test "SlotMap" {
// //
// --- SHADER MAP --- // --- SHADER MAP ---
var shaders: SlotMap(ShaderID, GPUShader) = .init(alloc); var shaders: KeyedSlotMap(ShaderID, GPUShader) = .init(alloc);
defer shaders.deinit(); defer shaders.deinit();
// --- PIPELINE MAP --- // --- PIPELINE MAP ---
var pipelines: SlotMap(PipelineKey, GPUPipeline) = .init(alloc); var pipelines: KeyedSlotMap(PipelineKey, GPUPipeline) = .init(alloc);
defer pipelines.deinit(); defer pipelines.deinit();
// --- ADD MATERIALS --- // --- ADD MATERIALS ---
@ -88,7 +89,7 @@ test "SlotMap" {
std.debug.print("┏━ SHADERS\n", .{}); std.debug.print("┏━ SHADERS\n", .{});
defer std.debug.print("┗━\n", .{}); defer std.debug.print("┗━\n", .{});
for (shaders.values.items) |entry| { for (shaders.items()) |entry| {
const source = switch (entry.key) { const source = switch (entry.key) {
.pbr => "assets/shaders/pbr.wgsl", .pbr => "assets/shaders/pbr.wgsl",
.unlit => "assets/shaders/unlit.wgsl", .unlit => "assets/shaders/unlit.wgsl",

133
src/keyed_slot_map.zig Normal file
View file

@ -0,0 +1,133 @@
const std = @import("std");
const SlotMap = @import("slot_map.zig").SlotMap;
const Handle = @import("handle.zig").Handle;
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn KeyedSlotMap(
comptime K: type,
comptime V: type,
) type {
return struct {
const Self = @This();
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const Entry = struct {
key: K,
value: V,
};
//
// FIELDS
//
// --- VALUES ---
slot_map: SlotMap(Entry),
lookup: std.AutoHashMap(K, Handle(V)),
alloc: std.mem.Allocator,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(alloc: std.mem.Allocator) Self {
return .{
.slot_map = .init(alloc),
.lookup = .init(alloc),
.alloc = alloc,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn deinit(self: *Self) void {
self.slot_map.deinit();
self.lookup.deinit();
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn put(
self: *Self,
key: K,
value: V,
) error{ Duplicate, OutOfMemory }!Handle(V) {
// --- PREVENT DUPS ---
if (self.lookup.contains(key)) return error.Duplicate;
// --- RESULT ---
const handle = try self.slot_map.put(.{ .value = value, .key = key });
errdefer self.slot_map.remove(handle);
// --- NEW LOOKUP ---
try self.lookup.put(key, handle);
errdefer _ = self.lookup.remove(key);
return handle;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn remove(
self: *Self,
handle: Handle(V),
) void {
// --- GET IDX ---
const idx: u32 = handle.idx;
// --- REMOVE FROM SLOTMAP ---
self.slot_map.remove(handle);
// --- REMOVE KEY ---
const key = self.values.items[idx].key;
_ = self.lookup.remove(key);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn removeByKey(self: *Self, key: K) void {
const handle = self.lookup.get(key) orelse return;
_ = self.lookup.remove(key);
self.slot_map.remove(handle);
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn get(self: *Self, handle: Handle(V)) ?*V {
const entry = if (self.slot_map.get(handle)) |v| v else return null;
return &entry.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();
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn items(self: *Self) []Entry {
return self.slot_map.values.items;
}
};
}

View file

@ -3,32 +3,22 @@ const Handle = @import("handle.zig").Handle;
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn SlotMap( pub fn SlotMap(comptime T: type) type {
comptime K: type,
comptime V: type,
) type {
return struct { return struct {
const Self = @This(); const Self = @This();
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const Entry = struct {
key: K,
value: V,
};
// //
// FIELDS // FIELDS
// //
// --- VALUES --- // --- VALUES ---
values: std.ArrayList(Entry), values: std.ArrayList(T),
lookup: std.AutoHashMap(K, Handle(V)),
// --- LIFE TIMES --- // --- LIFE TIMES ---
generations: std.ArrayList(u32), generations: std.ArrayList(u32),
free_list: std.ArrayList(u32), free_list: std.ArrayList(u32),
// --- MEMORY ---
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
/// ---------------------------------------------------- /// ----------------------------------------------------
@ -36,7 +26,6 @@ pub fn SlotMap(
pub fn init(alloc: std.mem.Allocator) Self { pub fn init(alloc: std.mem.Allocator) Self {
return .{ return .{
.values = .empty, .values = .empty,
.lookup = .init(alloc),
.generations = .empty, .generations = .empty,
.free_list = .empty, .free_list = .empty,
@ -49,7 +38,6 @@ pub fn SlotMap(
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn deinit(self: *Self) void { pub fn deinit(self: *Self) void {
self.values.deinit(self.alloc); self.values.deinit(self.alloc);
self.lookup.deinit();
self.generations.deinit(self.alloc); self.generations.deinit(self.alloc);
self.free_list.deinit(self.alloc); self.free_list.deinit(self.alloc);
} }
@ -58,18 +46,11 @@ pub fn SlotMap(
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn put( pub fn put(
self: *Self, self: *Self,
key: K, value: T,
value: V, ) error{ Duplicate, OutOfMemory }!Handle(T) {
) error{ Duplicate, OutOfMemory }!Handle(V) {
// --- PREVENT DUPS ---
if (self.lookup.contains(key)) return error.Duplicate;
// --- NEW GENERATION --- // --- NEW GENERATION ---
if (self.free_list.pop()) |idx| { if (self.free_list.pop()) |idx| {
self.values.items[@intCast(idx)] = .{ self.values.items[@intCast(idx)] = value;
.key = key,
.value = value,
};
self.generations.items[@intCast(idx)] += 1; self.generations.items[@intCast(idx)] += 1;
return .{ return .{
@ -80,22 +61,15 @@ pub fn SlotMap(
// --- METADATA --- // --- METADATA ---
const idx = self.values.items.len; const idx = self.values.items.len;
const handle: Handle(V) = .{ const handle: Handle(T) = .{
.idx = @intCast(idx), .idx = @intCast(idx),
.gen = 1, .gen = 1,
}; };
// --- NEW VALUE --- // --- NEW VALUE ---
try self.values.append(self.alloc, .{ try self.values.append(self.alloc, value);
.value = value,
.key = key,
});
errdefer _ = self.values.swapRemove(idx); errdefer _ = self.values.swapRemove(idx);
// --- NEW LOOKUP ---
try self.lookup.put(key, handle);
errdefer _ = self.lookup.remove(key);
// --- NEW GENERATION --- // --- NEW GENERATION ---
try self.generations.append(self.alloc, 1); try self.generations.append(self.alloc, 1);
@ -107,7 +81,7 @@ pub fn SlotMap(
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn remove( pub fn remove(
self: *Self, self: *Self,
handle: Handle(V), handle: Handle(T),
) void { ) void {
const idx: u32 = handle.idx; const idx: u32 = handle.idx;
@ -115,10 +89,6 @@ pub fn SlotMap(
if (idx >= self.generations.items.len) return; if (idx >= self.generations.items.len) return;
if (self.generations.items[idx] != handle.gen) return; if (self.generations.items[idx] != handle.gen) return;
// --- REMOVE KEY ---
const key = self.values.items[idx].key;
_ = self.lookup.remove(key);
// --- GENERATION --- // --- GENERATION ---
self.generations.items[idx] +|= 1; self.generations.items[idx] +|= 1;
self.free_list.append(self.alloc, idx) catch unreachable; self.free_list.append(self.alloc, idx) catch unreachable;
@ -126,44 +96,10 @@ pub fn SlotMap(
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn removeByKey(self: *Self, key: K) void { pub fn get(self: *Self, handle: Handle(T)) ?*T {
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 (handle.idx >= self.values.items.len) return null;
if (self.generations.items[handle.idx] != handle.gen) return null; if (self.generations.items[handle.idx] != handle.gen) return null;
return &self.values.items[handle.idx].value; return &self.values.items[handle.idx];
}
/// ----------------------------------------------------
/// ----------------------------------------------------
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();
} }
}; };
} }