//! ---------------------------------------------------- //! ---------------------------------------------------- const std = @import("std"); const FixedArrayList = @import("fixed_array_list.zig").FixedArrayList; const Handle = @import("handle.zig").Handle; /// ---------------------------------------------------- /// # Example /// /// ```zig /// 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(); /// ---------------------------------------------------- /// ---------------------------------------------------- pub const Entry = struct { value: *T, handle: Handle(T), }; /// ---------------------------------------------------- /// Cursor over live entries, yielding the value and its handle /// Removed (freed) slots are skipped /// ---------------------------------------------------- pub const Iterator = struct { values: []T, generations: []const u32, free_list: []const u32, index: usize = 0, /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn next(self: *Iterator) ?Entry { const i = nextLive(self.values.len, self.free_list, &self.index) orelse return null; return .{ .value = &self.values[i], .handle = .{ .idx = @intCast(i), .gen = self.generations[i] }, }; } }; /// ---------------------------------------------------- /// Cursor over live handles /// Removed (freed) slots are skipped /// ---------------------------------------------------- pub const HandleIterator = struct { values: []T, generations: []const u32, free_list: []const u32, index: usize = 0, /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn next(self: *HandleIterator) ?Handle(T) { const i = nextLive(self.values.len, self.free_list, &self.index) orelse return null; return .{ .idx = @intCast(i), .gen = self.generations[i] }; } }; /// ---------------------------------------------------- /// Cursor over live values /// Removed (freed) slots are skipped /// ---------------------------------------------------- pub const ValueIterator = struct { values: []T, generations: []const u32, free_list: []const u32, index: usize = 0, /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn next(self: *ValueIterator) ?*T { const i = nextLive(self.values.len, self.free_list, &self.index) orelse return null; return &self.values[i]; } }; /// ---------------------------------------------------- /// Next live index, or null /// Skips slots that live on the free list ( `󰮢` tombstones `󰮢` ) /// ---------------------------------------------------- fn nextLive( len: usize, free_list: []const u32, index: *usize, ) ?usize { while (index.* < len) : (index.* += 1) { if (std.mem.indexOfScalar(u32, free_list, @intCast(index.*)) == null) { const i = index.*; index.* += 1; return i; } } return null; } // // FIELDS // // --- VALUES --- values: FixedArrayList(T, Capacity), // --- LIFE TIMES --- generations: FixedArrayList(u32, Capacity), free_list: FixedArrayList(u32, Capacity), // --- 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) { // --- 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(value); errdefer _ = self.values.pop(); try self.generations.append(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(idx); self.generations.items()[idx] = self.mint(); } /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn get(self: *Self, handle: Handle(T)) ?*T { if (handle.idx >= self.values.len()) return null; 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(); } /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn contains(self: *Self, handle: Handle(T)) bool { return self.get(handle) != null; } /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn iterator(self: *Self) Iterator { return .{ .values = self.values.items(), .generations = self.generations.items(), .free_list = self.free_list.items(), }; } /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn handleIterator(self: *Self) HandleIterator { return .{ .values = self.values.items(), .generations = self.generations.items(), .free_list = self.free_list.items(), }; } /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn valueIterator(self: *Self) ValueIterator { return .{ .values = self.values.items(), .generations = self.generations.items(), .free_list = self.free_list.items(), }; } /// ---------------------------------------------------- /// ---------------------------------------------------- fn mint(self: *Self) u32 { const gen = self.next_gen +% 1; self.next_gen = gen; return gen; } }; }