diff --git a/src/fixed_array_list.zig b/src/fixed_array_list.zig index 4fe9096..b8449d9 100644 --- a/src/fixed_array_list.zig +++ b/src/fixed_array_list.zig @@ -108,6 +108,7 @@ pub fn FixedArrayList( } /// ---------------------------------------------------- + /// Prefer for iteration /// ---------------------------------------------------- pub fn items(self: *Self) []T { return self.data[0..self.cursor]; diff --git a/src/fixed_slot_map.zig b/src/fixed_slot_map.zig index 61d4950..ed6bbc1 100644 --- a/src/fixed_slot_map.zig +++ b/src/fixed_slot_map.zig @@ -27,6 +27,89 @@ pub fn FixedSlotMap( 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 // @@ -118,6 +201,42 @@ pub fn FixedSlotMap( 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 { diff --git a/src/gtl.zig b/src/gtl.zig index 245ed9e..9ced6a7 100644 --- a/src/gtl.zig +++ b/src/gtl.zig @@ -102,7 +102,8 @@ test "SlotMap" { defer a.end(); // --- # --- - for (materials.values.items) |material| { + var it = materials.valueIterator(); + while (it.next()) |material| { if (a.section(.debug, "", ansi.cyan)) |b| { defer b.end(); @@ -194,4 +195,7 @@ test "FixedSlotMap" { if (materials.get(handle)) |v| { std.debug.print("Material: {}\n", .{v.unique.topology}); } + + var it = materials.valueIterator(); + while (it.next()) |_| {} } diff --git a/src/keyed_slot_map.zig b/src/keyed_slot_map.zig index d935fa1..343e152 100644 --- a/src/keyed_slot_map.zig +++ b/src/keyed_slot_map.zig @@ -47,13 +47,33 @@ pub fn KeyedSlotMap( /// ---------------------------------------------------- pub const Iterator = struct { entries: []Entry, + generations: []const u32, free_list: []const u32, index: usize = 0, /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn next(self: *Iterator) ?*Entry { - return advance(self.entries, self.free_list, &self.index); + const i = nextLive(self.entries.len, self.free_list, &self.index) orelse return null; + return &self.entries[i]; + } + }; + + /// ---------------------------------------------------- + /// Cursor over live handles + /// Removed (freed) slots are skipped + /// ---------------------------------------------------- + pub const HandleIterator = struct { + entries: []Entry, + generations: []const u32, + free_list: []const u32, + index: usize = 0, + + /// ---------------------------------------------------- + /// ---------------------------------------------------- + pub fn next(self: *HandleIterator) ?Handle(V) { + const i = nextLive(self.entries.len, self.free_list, &self.index) orelse return null; + return .{ .idx = @intCast(i), .gen = self.generations[i] }; } }; @@ -62,14 +82,15 @@ pub fn KeyedSlotMap( /// ---------------------------------------------------- pub const KeyIterator = struct { entries: []Entry, + generations: []const u32, free_list: []const u32, index: usize = 0, /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn next(self: *KeyIterator) ?*K { - const entry = advance(self.entries, self.free_list, &self.index) orelse return null; - return &entry.key; + const i = nextLive(self.entries.len, self.free_list, &self.index) orelse return null; + return &self.entries[i].key; } }; @@ -78,30 +99,33 @@ pub fn KeyedSlotMap( /// ---------------------------------------------------- pub const ValueIterator = struct { entries: []Entry, + generations: []const u32, free_list: []const u32, index: usize = 0, /// ---------------------------------------------------- /// ---------------------------------------------------- pub fn next(self: *ValueIterator) ?*V { - const entry = advance(self.entries, self.free_list, &self.index) orelse return null; - return &entry.value; + const i = nextLive(self.entries.len, self.free_list, &self.index) orelse return null; + return &self.entries[i].value; } }; /// ---------------------------------------------------- - /// Skips slots that live on the free list (tombstones) + /// Next live index, or null + /// Skips slots that live on the free list ( `󰮢` tombstones `󰮢` rip-bozo 😭 ) /// ---------------------------------------------------- - fn advance( - entries: []Entry, + fn nextLive( + len: usize, free_list: []const u32, index: *usize, - ) ?*Entry { - while (index.* < entries.len) : (index.* += 1) { - if (std.mem.indexOfScalar(u32, free_list, @intCast(index.*)) != null) continue; - const entry = &entries[index.*]; - index.* += 1; - return entry; + ) ?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; } @@ -245,36 +269,40 @@ pub fn KeyedSlotMap( /// ---------------------------------------------------- /// ---------------------------------------------------- - pub fn items(self: *Self) []Entry { - return self.slot_map.values.items; - } - - /// ---------------------------------------------------- - /// Skips slots that live on the free list (tombstones) - /// ---------------------------------------------------- pub fn iterator(self: *Self) Iterator { return .{ .entries = self.slot_map.values.items, + .generations = self.slot_map.generations.items, .free_list = self.slot_map.free_list.items, }; } /// ---------------------------------------------------- - /// Skips slots that live on the free list (tombstones) /// ---------------------------------------------------- pub fn keyIterator(self: *Self) KeyIterator { return .{ .entries = self.slot_map.values.items, + .generations = self.slot_map.generations.items, .free_list = self.slot_map.free_list.items, }; } /// ---------------------------------------------------- - /// Skips slots that live on the free list (tombstones) /// ---------------------------------------------------- pub fn valueIterator(self: *Self) ValueIterator { return .{ .entries = self.slot_map.values.items, + .generations = self.slot_map.generations.items, + .free_list = self.slot_map.free_list.items, + }; + } + + /// ---------------------------------------------------- + /// ---------------------------------------------------- + pub fn handleIterator(self: *Self) HandleIterator { + return .{ + .entries = self.slot_map.values.items, + .generations = self.slot_map.generations.items, .free_list = self.slot_map.free_list.items, }; } diff --git a/src/slot_map.zig b/src/slot_map.zig index 517de79..f7570e0 100644 --- a/src/slot_map.zig +++ b/src/slot_map.zig @@ -21,6 +21,89 @@ pub fn SlotMap(comptime T: type) 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 // @@ -118,6 +201,42 @@ pub fn SlotMap(comptime T: type) type { return &self.values.items[handle.idx]; } + /// ---------------------------------------------------- + /// ---------------------------------------------------- + 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, + }; + } + /// ---------------------------------------------------- /// Reduce length to 0 /// Invalidates all element pointers and handles