//! ---------------------------------------------------- //! ---------------------------------------------------- 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 } }); /// ``` /// ---------------------------------------------------- pub fn SlotMap(comptime T: type) type { return struct { const Self = @This(); // // FIELDS // // --- VALUES --- values: std.ArrayList(T), // --- LIFE TIMES --- generations: std.ArrayList(u32), free_list: std.ArrayList(u32), // --- GENERATION COUNTER --- next_gen: u32 = 0, // --- MEMORY --- 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, value: T, ) error{OutOfMemory}!Handle(T) { // --- REUSE A FREED SLOT --- if (self.free_list.pop()) |idx| { const gen = self.mint(); self.values.items[idx] = value; self.generations.items[idx] = gen; return .{ .idx = idx, .gen = gen, }; } // --- METADATA --- const idx: u32 = @intCast(self.values.items.len); const gen = self.mint(); // --- NEW VALUE --- try self.values.append(self.alloc, value); errdefer _ = self.values.pop(); try self.generations.append(self.alloc, gen); // --- RESULT --- return .{ .idx = idx, .gen = gen, }; } /// ---------------------------------------------------- /// Returns an error if the free list could not grow /// Stale handles are silently ignored /// ---------------------------------------------------- pub fn remove( self: *Self, handle: Handle(T), ) error{OutOfMemory}!void { const idx: u32 = handle.idx; // --- CHECK IF STALE --- if (idx >= self.generations.items.len) return; if (self.generations.items[idx] != handle.gen) return; // --- FREE --- try self.free_list.append(self.alloc, idx); self.generations.items[idx] = self.mint(); } /// ---------------------------------------------------- /// ---------------------------------------------------- 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]; } /// ---------------------------------------------------- /// Reduce length to 0 /// Invalidates all element pointers and handles /// ---------------------------------------------------- pub fn clearRetainingCapacity(self: *Self) void { self.values.clearRetainingCapacity(); self.generations.clearRetainingCapacity(); self.free_list.clearRetainingCapacity(); } /// ---------------------------------------------------- /// ---------------------------------------------------- fn mint(self: *Self) u32 { const gen = self.next_gen +% 1; self.next_gen = gen; return gen; } }; }