diff --git a/README.md b/README.md index 1e27a7b..4f5b3f5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ **Includes** - ANSI -- SlotMap +- SlotMap | KeyedSlotMap - Custom Log - TypeID @@ -14,10 +14,10 @@ log.trace("...", .{}); log.fatal("...", .{}); ``` -**SlotMap** +**KeyedSlotMap** ```zig // --- SHADER MAP --- -var shaders: SlotMap(ShaderID, GPUShader) = .init(alloc); +var shaders: KeyedSlotMap(ShaderID, GPUShader) = .init(alloc); defer shaders.deinit(); // --- NEW SHADER --- diff --git a/src/gtl.zig b/src/gtl.zig index f81a081..56a014c 100644 --- a/src/gtl.zig +++ b/src/gtl.zig @@ -12,6 +12,7 @@ pub const log = @import("log.zig"); pub const ansi = @import("ansi.zig"); pub const Handle = @import("handle.zig").Handle; 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; // @@ -49,11 +50,11 @@ test "SlotMap" { // // --- SHADER MAP --- - var shaders: SlotMap(ShaderID, GPUShader) = .init(alloc); + var shaders: KeyedSlotMap(ShaderID, GPUShader) = .init(alloc); defer shaders.deinit(); // --- PIPELINE MAP --- - var pipelines: SlotMap(PipelineKey, GPUPipeline) = .init(alloc); + var pipelines: KeyedSlotMap(PipelineKey, GPUPipeline) = .init(alloc); defer pipelines.deinit(); // --- ADD MATERIALS --- @@ -88,7 +89,7 @@ test "SlotMap" { std.debug.print("┏━ SHADERS\n", .{}); defer std.debug.print("┗━\n", .{}); - for (shaders.values.items) |entry| { + for (shaders.items()) |entry| { const source = switch (entry.key) { .pbr => "assets/shaders/pbr.wgsl", .unlit => "assets/shaders/unlit.wgsl", diff --git a/src/keyed_slot_map.zig b/src/keyed_slot_map.zig new file mode 100644 index 0000000..7683831 --- /dev/null +++ b/src/keyed_slot_map.zig @@ -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; + } + }; +} diff --git a/src/slot_map.zig b/src/slot_map.zig index ac24adf..d4e9033 100644 --- a/src/slot_map.zig +++ b/src/slot_map.zig @@ -3,32 +3,22 @@ const Handle = @import("handle.zig").Handle; /// ---------------------------------------------------- /// ---------------------------------------------------- -pub fn SlotMap( - comptime K: type, - comptime V: type, -) type { +pub fn SlotMap(comptime T: 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)), + values: std.ArrayList(T), // --- LIFE TIMES --- generations: std.ArrayList(u32), free_list: std.ArrayList(u32), + // --- MEMORY --- alloc: std.mem.Allocator, /// ---------------------------------------------------- @@ -36,7 +26,6 @@ pub fn SlotMap( pub fn init(alloc: std.mem.Allocator) Self { return .{ .values = .empty, - .lookup = .init(alloc), .generations = .empty, .free_list = .empty, @@ -49,7 +38,6 @@ pub fn SlotMap( /// ---------------------------------------------------- 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); } @@ -58,18 +46,11 @@ pub fn SlotMap( /// ---------------------------------------------------- pub fn put( self: *Self, - key: K, - value: V, - ) error{ Duplicate, OutOfMemory }!Handle(V) { - // --- PREVENT DUPS --- - if (self.lookup.contains(key)) return error.Duplicate; - + value: T, + ) error{ Duplicate, OutOfMemory }!Handle(T) { // --- NEW GENERATION --- if (self.free_list.pop()) |idx| { - self.values.items[@intCast(idx)] = .{ - .key = key, - .value = value, - }; + self.values.items[@intCast(idx)] = value; self.generations.items[@intCast(idx)] += 1; return .{ @@ -80,22 +61,15 @@ pub fn SlotMap( // --- METADATA --- const idx = self.values.items.len; - const handle: Handle(V) = .{ + const handle: Handle(T) = .{ .idx = @intCast(idx), .gen = 1, }; // --- NEW VALUE --- - try self.values.append(self.alloc, .{ - .value = value, - .key = key, - }); + try self.values.append(self.alloc, value); 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); @@ -107,7 +81,7 @@ pub fn SlotMap( /// ---------------------------------------------------- pub fn remove( self: *Self, - handle: Handle(V), + handle: Handle(T), ) void { const idx: u32 = handle.idx; @@ -115,10 +89,6 @@ pub fn SlotMap( 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; @@ -126,44 +96,10 @@ pub fn SlotMap( /// ---------------------------------------------------- /// ---------------------------------------------------- - 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 { + pub fn get(self: *Self, handle: Handle(T)) ?*T { 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(); + return &self.values.items[handle.idx]; } }; }