Compare commits
No commits in common. "cf41b732416562be189eb93d5086e83811f3bb0c" and "3c411f25b9a60d4633fa283a076a1cc141eb9902" have entirely different histories.
cf41b73241
...
3c411f25b9
4 changed files with 24 additions and 314 deletions
|
|
@ -32,7 +32,6 @@ pub const Handle = @import("handle.zig").Handle;
|
||||||
pub const TypeID = @import("type_id.zig").of;
|
pub const TypeID = @import("type_id.zig").of;
|
||||||
|
|
||||||
// --- ARRAYS ---
|
// --- ARRAYS ---
|
||||||
pub const ResourceMap = @import("resource_map.zig").ResourceMap;
|
|
||||||
pub const SlotMap = @import("slot_map.zig").SlotMap;
|
pub const SlotMap = @import("slot_map.zig").SlotMap;
|
||||||
pub const KeyedSlotMap = @import("keyed_slot_map.zig").KeyedSlotMap;
|
pub const KeyedSlotMap = @import("keyed_slot_map.zig").KeyedSlotMap;
|
||||||
pub const FixedArrayList = @import("fixed_array_list.zig").FixedArrayList;
|
pub const FixedArrayList = @import("fixed_array_list.zig").FixedArrayList;
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ pub fn KeyedSlotMap(
|
||||||
//
|
//
|
||||||
|
|
||||||
// --- VALUES ---
|
// --- VALUES ---
|
||||||
map: SlotMap(Entry),
|
slot_map: SlotMap(Entry),
|
||||||
lookup: std.AutoHashMap(K, Handle(V)),
|
lookup: std.AutoHashMap(K, Handle(V)),
|
||||||
|
|
||||||
// --- MEMORY ---
|
// --- MEMORY ---
|
||||||
|
|
@ -145,7 +145,7 @@ pub fn KeyedSlotMap(
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
pub fn init(alloc: std.mem.Allocator) Self {
|
pub fn init(alloc: std.mem.Allocator) Self {
|
||||||
return .{
|
return .{
|
||||||
.map = .init(alloc),
|
.slot_map = .init(alloc),
|
||||||
.lookup = .init(alloc),
|
.lookup = .init(alloc),
|
||||||
|
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
|
|
@ -155,7 +155,7 @@ pub fn KeyedSlotMap(
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
self.map.deinit();
|
self.slot_map.deinit();
|
||||||
self.lookup.deinit();
|
self.lookup.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,8 +170,8 @@ pub fn KeyedSlotMap(
|
||||||
if (self.lookup.contains(key)) return error.Duplicate;
|
if (self.lookup.contains(key)) return error.Duplicate;
|
||||||
|
|
||||||
// --- RESULT ---
|
// --- RESULT ---
|
||||||
const handle = try self.map.put(.{ .value = value, .key = key });
|
const handle = try self.slot_map.put(.{ .value = value, .key = key });
|
||||||
errdefer self.map.remove(handle) catch {};
|
errdefer self.slot_map.remove(handle) catch {};
|
||||||
|
|
||||||
// --- NEW LOOKUP ---
|
// --- NEW LOOKUP ---
|
||||||
try self.lookup.put(key, handle);
|
try self.lookup.put(key, handle);
|
||||||
|
|
@ -191,14 +191,14 @@ pub fn KeyedSlotMap(
|
||||||
const idx: u32 = handle.idx;
|
const idx: u32 = handle.idx;
|
||||||
|
|
||||||
// --- CHECK IF STALE ---
|
// --- CHECK IF STALE ---
|
||||||
if (idx >= self.map.generations.items.len) return;
|
if (idx >= self.slot_map.generations.items.len) return;
|
||||||
if (self.map.generations.items[idx] != handle.gen) return;
|
if (self.slot_map.generations.items[idx] != handle.gen) return;
|
||||||
|
|
||||||
// --- GRAB KEY BEFORE REMOVING ---
|
// --- GRAB KEY BEFORE REMOVING ---
|
||||||
const key = self.map.values.items[idx].key;
|
const key = self.slot_map.values.items[idx].key;
|
||||||
|
|
||||||
// --- REMOVE FROM SLOTMAP ---
|
// --- REMOVE FROM SLOTMAP ---
|
||||||
try self.map.remove(handle);
|
try self.slot_map.remove(handle);
|
||||||
|
|
||||||
// --- REMOVE KEY ---
|
// --- REMOVE KEY ---
|
||||||
_ = self.lookup.remove(key);
|
_ = self.lookup.remove(key);
|
||||||
|
|
@ -209,13 +209,13 @@ pub fn KeyedSlotMap(
|
||||||
pub fn removeByKey(self: *Self, key: K) error{OutOfMemory}!void {
|
pub fn removeByKey(self: *Self, key: K) error{OutOfMemory}!void {
|
||||||
const handle = self.lookup.get(key) orelse return;
|
const handle = self.lookup.get(key) orelse return;
|
||||||
_ = self.lookup.remove(key);
|
_ = self.lookup.remove(key);
|
||||||
try self.map.remove(handle);
|
try self.slot_map.remove(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
pub fn get(self: *Self, handle: Handle(V)) ?*V {
|
pub fn get(self: *Self, handle: Handle(V)) ?*V {
|
||||||
const entry = if (self.map.get(handle)) |v| v else return null;
|
const entry = if (self.slot_map.get(handle)) |v| v else return null;
|
||||||
return &entry.value;
|
return &entry.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -272,16 +272,16 @@ pub fn KeyedSlotMap(
|
||||||
/// Removed (freed) slots are not counted
|
/// Removed (freed) slots are not counted
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
pub fn len(self: *const Self) usize {
|
pub fn len(self: *const Self) usize {
|
||||||
return self.map.values.items.len - self.map.free_list.items.len;
|
return self.slot_map.values.items.len - self.slot_map.free_list.items.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
pub fn iterator(self: *Self) Iterator {
|
pub fn iterator(self: *Self) Iterator {
|
||||||
return .{
|
return .{
|
||||||
.entries = self.map.values.items,
|
.entries = self.slot_map.values.items,
|
||||||
.generations = self.map.generations.items,
|
.generations = self.slot_map.generations.items,
|
||||||
.free_list = self.map.free_list.items,
|
.free_list = self.slot_map.free_list.items,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -289,9 +289,9 @@ pub fn KeyedSlotMap(
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
pub fn keyIterator(self: *Self) KeyIterator {
|
pub fn keyIterator(self: *Self) KeyIterator {
|
||||||
return .{
|
return .{
|
||||||
.entries = self.map.values.items,
|
.entries = self.slot_map.values.items,
|
||||||
.generations = self.map.generations.items,
|
.generations = self.slot_map.generations.items,
|
||||||
.free_list = self.map.free_list.items,
|
.free_list = self.slot_map.free_list.items,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -299,9 +299,9 @@ pub fn KeyedSlotMap(
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
pub fn valueIterator(self: *Self) ValueIterator {
|
pub fn valueIterator(self: *Self) ValueIterator {
|
||||||
return .{
|
return .{
|
||||||
.entries = self.map.values.items,
|
.entries = self.slot_map.values.items,
|
||||||
.generations = self.map.generations.items,
|
.generations = self.slot_map.generations.items,
|
||||||
.free_list = self.map.free_list.items,
|
.free_list = self.slot_map.free_list.items,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -309,9 +309,9 @@ pub fn KeyedSlotMap(
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
pub fn handleIterator(self: *Self) HandleIterator {
|
pub fn handleIterator(self: *Self) HandleIterator {
|
||||||
return .{
|
return .{
|
||||||
.entries = self.map.values.items,
|
.entries = self.slot_map.values.items,
|
||||||
.generations = self.map.generations.items,
|
.generations = self.slot_map.generations.items,
|
||||||
.free_list = self.map.free_list.items,
|
.free_list = self.slot_map.free_list.items,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,279 +0,0 @@
|
||||||
//! ----------------------------------------------------
|
|
||||||
//! ----------------------------------------------------
|
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
const Handle = @import("handle.zig").Handle;
|
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```zig
|
|
||||||
/// // --- MATERIALS ---
|
|
||||||
/// var materials: ResourceMap(Material, 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 ResourceMap(
|
|
||||||
comptime H: type,
|
|
||||||
comptime V: type,
|
|
||||||
) type {
|
|
||||||
return struct {
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
|
||||||
/// ----------------------------------------------------
|
|
||||||
pub const Entry = struct {
|
|
||||||
value: *V,
|
|
||||||
handle: Handle(H),
|
|
||||||
};
|
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
|
||||||
/// Cursor over live entries, yielding the value and its handle
|
|
||||||
/// Removed (freed) slots are skipped
|
|
||||||
/// ----------------------------------------------------
|
|
||||||
pub const Iterator = struct {
|
|
||||||
values: []V,
|
|
||||||
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: []V,
|
|
||||||
generations: []const u32,
|
|
||||||
free_list: []const u32,
|
|
||||||
index: usize = 0,
|
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
|
||||||
/// ----------------------------------------------------
|
|
||||||
pub fn next(self: *HandleIterator) ?Handle(H) {
|
|
||||||
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: []V,
|
|
||||||
generations: []const u32,
|
|
||||||
free_list: []const u32,
|
|
||||||
index: usize = 0,
|
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
|
||||||
/// ----------------------------------------------------
|
|
||||||
pub fn next(self: *ValueIterator) ?*V {
|
|
||||||
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(
|
|
||||||
n: usize,
|
|
||||||
free_list: []const u32,
|
|
||||||
index: *usize,
|
|
||||||
) ?usize {
|
|
||||||
while (index.* < n) : (index.* += 1) {
|
|
||||||
if (std.mem.indexOfScalar(u32, free_list, @intCast(index.*)) == null) {
|
|
||||||
const i = index.*;
|
|
||||||
index.* += 1;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// FIELDS
|
|
||||||
//
|
|
||||||
|
|
||||||
// --- VALUES ---
|
|
||||||
values: std.ArrayList(H),
|
|
||||||
|
|
||||||
// --- 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: V,
|
|
||||||
) error{OutOfMemory}!Handle(H) {
|
|
||||||
// --- 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(H),
|
|
||||||
) 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(H)) ?*V {
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
|
||||||
/// ----------------------------------------------------
|
|
||||||
pub fn contains(self: *Self, handle: Handle(H)) bool {
|
|
||||||
return self.get(handle) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
|
||||||
/// Current valid handle for an index
|
|
||||||
/// Returns null if out of range or the slot is freed
|
|
||||||
/// ----------------------------------------------------
|
|
||||||
pub fn getHandle(self: *const Self, idx: usize) ?Handle(H) {
|
|
||||||
if (idx >= self.values.items.len) return null;
|
|
||||||
if (std.mem.indexOfScalar(u32, self.free_list.items, @intCast(idx)) != null) return null;
|
|
||||||
return .{ .idx = @intCast(idx), .gen = self.generations.items[idx] };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
|
||||||
/// Number of live entries
|
|
||||||
/// Removed (freed) slots are not counted
|
|
||||||
/// ----------------------------------------------------
|
|
||||||
pub fn len(self: *const Self) usize {
|
|
||||||
return self.values.items.len - self.free_list.items.len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
|
||||||
/// ----------------------------------------------------
|
|
||||||
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
|
|
||||||
/// ----------------------------------------------------
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -207,16 +207,6 @@ pub fn SlotMap(comptime T: type) type {
|
||||||
return self.get(handle) != null;
|
return self.get(handle) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
|
||||||
/// Current valid handle for an index
|
|
||||||
/// Returns null if out of range or the slot is freed
|
|
||||||
/// ----------------------------------------------------
|
|
||||||
pub fn getHandle(self: *const Self, idx: usize) ?Handle(T) {
|
|
||||||
if (idx >= self.values.items.len) return null;
|
|
||||||
if (std.mem.indexOfScalar(u32, self.free_list.items, @intCast(idx)) != null) return null;
|
|
||||||
return .{ .idx = @intCast(idx), .gen = self.generations.items[idx] };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
/// Number of live entries
|
/// Number of live entries
|
||||||
/// Removed (freed) slots are not counted
|
/// Removed (freed) slots are not counted
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue