Just added proper iterator's for [SlotMap, KeyedSlotMap] 🥀

This commit is contained in:
abux 2026-08-03 21:28:54 +01:00
parent 0aef87a252
commit 5ae089628b
5 changed files with 295 additions and 24 deletions

View file

@ -108,6 +108,7 @@ pub fn FixedArrayList(
} }
/// ---------------------------------------------------- /// ----------------------------------------------------
/// Prefer for iteration
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn items(self: *Self) []T { pub fn items(self: *Self) []T {
return self.data[0..self.cursor]; return self.data[0..self.cursor];

View file

@ -27,6 +27,89 @@ pub fn FixedSlotMap(
return struct { return struct {
const Self = @This(); 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 // FIELDS
// //
@ -118,6 +201,42 @@ pub fn FixedSlotMap(
self.free_list.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 { fn mint(self: *Self) u32 {

View file

@ -102,7 +102,8 @@ test "SlotMap" {
defer a.end(); defer a.end();
// --- # --- // --- # ---
for (materials.values.items) |material| { var it = materials.valueIterator();
while (it.next()) |material| {
if (a.section(.debug, "", ansi.cyan)) |b| { if (a.section(.debug, "", ansi.cyan)) |b| {
defer b.end(); defer b.end();
@ -194,4 +195,7 @@ test "FixedSlotMap" {
if (materials.get(handle)) |v| { if (materials.get(handle)) |v| {
std.debug.print("Material: {}\n", .{v.unique.topology}); std.debug.print("Material: {}\n", .{v.unique.topology});
} }
var it = materials.valueIterator();
while (it.next()) |_| {}
} }

View file

@ -47,13 +47,33 @@ pub fn KeyedSlotMap(
/// ---------------------------------------------------- /// ----------------------------------------------------
pub const Iterator = struct { pub const Iterator = struct {
entries: []Entry, entries: []Entry,
generations: []const u32,
free_list: []const u32, free_list: []const u32,
index: usize = 0, index: usize = 0,
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn next(self: *Iterator) ?*Entry { 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 { pub const KeyIterator = struct {
entries: []Entry, entries: []Entry,
generations: []const u32,
free_list: []const u32, free_list: []const u32,
index: usize = 0, index: usize = 0,
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn next(self: *KeyIterator) ?*K { pub fn next(self: *KeyIterator) ?*K {
const entry = advance(self.entries, self.free_list, &self.index) orelse return null; const i = nextLive(self.entries.len, self.free_list, &self.index) orelse return null;
return &entry.key; return &self.entries[i].key;
} }
}; };
@ -78,30 +99,33 @@ pub fn KeyedSlotMap(
/// ---------------------------------------------------- /// ----------------------------------------------------
pub const ValueIterator = struct { pub const ValueIterator = struct {
entries: []Entry, entries: []Entry,
generations: []const u32,
free_list: []const u32, free_list: []const u32,
index: usize = 0, index: usize = 0,
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn next(self: *ValueIterator) ?*V { pub fn next(self: *ValueIterator) ?*V {
const entry = advance(self.entries, self.free_list, &self.index) orelse return null; const i = nextLive(self.entries.len, self.free_list, &self.index) orelse return null;
return &entry.value; 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( fn nextLive(
entries: []Entry, len: usize,
free_list: []const u32, free_list: []const u32,
index: *usize, index: *usize,
) ?*Entry { ) ?usize {
while (index.* < entries.len) : (index.* += 1) { while (index.* < len) : (index.* += 1) {
if (std.mem.indexOfScalar(u32, free_list, @intCast(index.*)) != null) continue; if (std.mem.indexOfScalar(u32, free_list, @intCast(index.*)) == null) {
const entry = &entries[index.*]; const i = index.*;
index.* += 1; index.* += 1;
return entry; return i;
}
} }
return null; 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 { pub fn iterator(self: *Self) Iterator {
return .{ return .{
.entries = self.slot_map.values.items, .entries = self.slot_map.values.items,
.generations = self.slot_map.generations.items,
.free_list = self.slot_map.free_list.items, .free_list = self.slot_map.free_list.items,
}; };
} }
/// ---------------------------------------------------- /// ----------------------------------------------------
/// Skips slots that live on the free list (tombstones)
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn keyIterator(self: *Self) KeyIterator { pub fn keyIterator(self: *Self) KeyIterator {
return .{ return .{
.entries = self.slot_map.values.items, .entries = self.slot_map.values.items,
.generations = self.slot_map.generations.items,
.free_list = self.slot_map.free_list.items, .free_list = self.slot_map.free_list.items,
}; };
} }
/// ---------------------------------------------------- /// ----------------------------------------------------
/// Skips slots that live on the free list (tombstones)
/// ---------------------------------------------------- /// ----------------------------------------------------
pub fn valueIterator(self: *Self) ValueIterator { pub fn valueIterator(self: *Self) ValueIterator {
return .{ return .{
.entries = self.slot_map.values.items, .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, .free_list = self.slot_map.free_list.items,
}; };
} }

View file

@ -21,6 +21,89 @@ pub fn SlotMap(comptime T: type) type {
return struct { return struct {
const Self = @This(); 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 // FIELDS
// //
@ -118,6 +201,42 @@ pub fn SlotMap(comptime T: type) type {
return &self.values.items[handle.idx]; 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 /// Reduce length to 0
/// Invalidates all element pointers and handles /// Invalidates all element pointers and handles