Compare commits
2 commits
e20ce39550
...
0aef87a252
| Author | SHA1 | Date | |
|---|---|---|---|
| 0aef87a252 | |||
| 0e174f878f |
10 changed files with 323 additions and 134 deletions
|
|
@ -1,3 +1,7 @@
|
||||||
|
//! ----------------------------------------------------
|
||||||
|
//! ANSI Colors for terminal
|
||||||
|
//! ----------------------------------------------------
|
||||||
|
|
||||||
// --- RESET ---
|
// --- RESET ---
|
||||||
pub const reset = "\x1b[0m";
|
pub const reset = "\x1b[0m";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,24 @@
|
||||||
|
//! ----------------------------------------------------
|
||||||
|
//! ----------------------------------------------------
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```zig
|
||||||
|
/// var entities: FixedArrayList(u32, 4) = .empty;
|
||||||
|
///
|
||||||
|
/// try entities.append(1);
|
||||||
|
/// try entities.append(2);
|
||||||
|
/// try entities.append(3);
|
||||||
|
///
|
||||||
|
/// if (entities.getPtr(0)) |v| v.* = 69;
|
||||||
|
///
|
||||||
|
/// for (entities.items()) |item| {
|
||||||
|
/// std.debug.print("Entity: {}\n", .{item});
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
pub fn FixedArrayList(
|
pub fn FixedArrayList(
|
||||||
comptime T: type,
|
comptime T: type,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
//! ----------------------------------------------------
|
||||||
|
//! ----------------------------------------------------
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const FixedArrayList = @import("fixed_array_list.zig").FixedArrayList;
|
const FixedArrayList = @import("fixed_array_list.zig").FixedArrayList;
|
||||||
const Handle = @import("handle.zig").Handle;
|
const Handle = @import("handle.zig").Handle;
|
||||||
|
|
@ -6,6 +9,15 @@ const Handle = @import("handle.zig").Handle;
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```zig
|
/// ```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(
|
pub fn FixedSlotMap(
|
||||||
|
|
@ -26,6 +38,9 @@ pub fn FixedSlotMap(
|
||||||
generations: FixedArrayList(u32, Capacity),
|
generations: FixedArrayList(u32, Capacity),
|
||||||
free_list: FixedArrayList(u32, Capacity),
|
free_list: FixedArrayList(u32, Capacity),
|
||||||
|
|
||||||
|
// --- GENERATION COUNTER ---
|
||||||
|
next_gen: u32 = 0,
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
pub const empty = Self{
|
pub const empty = Self{
|
||||||
|
|
@ -40,66 +55,75 @@ pub fn FixedSlotMap(
|
||||||
self: *Self,
|
self: *Self,
|
||||||
value: T,
|
value: T,
|
||||||
) error{Full}!Handle(T) {
|
) error{Full}!Handle(T) {
|
||||||
// --- NEW GENERATION ---
|
// --- REUSE A FREED SLOT ---
|
||||||
if (self.free_list.pop()) |idx| {
|
if (self.free_list.pop()) |idx| {
|
||||||
self.values.data[@intCast(idx)] = value;
|
const gen = self.mint();
|
||||||
self.generations.data[@intCast(idx)] += 1;
|
self.values.items()[idx] = value;
|
||||||
|
self.generations.items()[idx] = gen;
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.idx = idx,
|
.idx = idx,
|
||||||
.gen = self.generations.data[idx],
|
.gen = gen,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- METADATA ---
|
// --- METADATA ---
|
||||||
const idx = self.values.len();
|
const idx: u32 = @intCast(self.values.items().len);
|
||||||
const handle: Handle(T) = .{
|
const gen = self.mint();
|
||||||
.idx = @intCast(idx),
|
|
||||||
.gen = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
// --- NEW VALUE ---
|
// --- NEW VALUE ---
|
||||||
try self.values.append(value);
|
try self.values.append(value);
|
||||||
errdefer _ = self.values.swapRemove(idx);
|
errdefer _ = self.values.pop();
|
||||||
|
try self.generations.append(gen);
|
||||||
// --- NEW GENERATION ---
|
|
||||||
try self.generations.append(1);
|
|
||||||
|
|
||||||
// --- RESULT ---
|
// --- RESULT ---
|
||||||
return handle;
|
return .{
|
||||||
|
.idx = idx,
|
||||||
|
.gen = gen,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
|
/// Returns an error if the free list could not grow
|
||||||
|
/// Stale handles are silently ignored
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
pub fn remove(
|
pub fn remove(
|
||||||
self: *Self,
|
self: *Self,
|
||||||
handle: Handle(T),
|
handle: Handle(T),
|
||||||
) void {
|
) error{OutOfMemory}!void {
|
||||||
const idx: u32 = handle.idx;
|
const idx: u32 = handle.idx;
|
||||||
|
|
||||||
// --- CHECK IF STALE ---
|
// --- CHECK IF STALE ---
|
||||||
if (idx >= self.generations.len()) return;
|
if (idx >= self.generations.items().len) return;
|
||||||
if (self.generations.data[idx] != handle.gen) return;
|
if (self.generations.items()[idx] != handle.gen) return;
|
||||||
|
|
||||||
// --- GENERATION ---
|
// --- FREE ---
|
||||||
self.generations.data[idx] +|= 1;
|
try self.free_list.append(idx);
|
||||||
self.free_list.append(idx) catch |err| {
|
self.generations.items()[idx] = self.mint();
|
||||||
std.debug.print("[SLOTMAP_ERROR] {s}\n", .{@errorName(err)});
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
pub fn get(self: *Self, handle: Handle(T)) ?*T {
|
pub fn get(self: *Self, handle: Handle(T)) ?*T {
|
||||||
if (handle.idx >= self.values.len()) return null;
|
if (handle.idx >= self.values.len()) return null;
|
||||||
if (self.generations.data[handle.idx] != handle.gen) return null;
|
if (self.generations.items()[handle.idx] != handle.gen) return null;
|
||||||
return &self.values.data[handle.idx];
|
return &self.values.items()[handle.idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
pub fn items(self: *Self) []T {
|
pub fn clear(self: *Self) void {
|
||||||
return self.values.items();
|
self.values.clear();
|
||||||
|
self.generations.clear();
|
||||||
|
self.free_list.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
fn mint(self: *Self) u32 {
|
||||||
|
const gen = self.next_gen +% 1;
|
||||||
|
self.next_gen = gen;
|
||||||
|
return gen;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
99
src/gtl.zig
99
src/gtl.zig
|
|
@ -1,3 +1,21 @@
|
||||||
|
//! ----------------------------------------------------
|
||||||
|
//! # Game Template Library
|
||||||
|
//!
|
||||||
|
//! Low *cortisol* library for games and game engines
|
||||||
|
//!
|
||||||
|
//! # Features
|
||||||
|
//!
|
||||||
|
//! **dynamic**
|
||||||
|
//! | SlotMap
|
||||||
|
//! | KeyedSlotMap
|
||||||
|
//! | Handle(T) **Generational**
|
||||||
|
//!
|
||||||
|
//! **fixed**
|
||||||
|
//! | FixedArrayList
|
||||||
|
//! | FixedSlotMap
|
||||||
|
//! | RingBuffer
|
||||||
|
//! ----------------------------------------------------
|
||||||
|
|
||||||
//
|
//
|
||||||
// PRIVATE
|
// PRIVATE
|
||||||
//
|
//
|
||||||
|
|
@ -74,13 +92,17 @@ test "SlotMap" {
|
||||||
// --- ADD MATERIALS ---
|
// --- ADD MATERIALS ---
|
||||||
_ = try materials.put(.{ .style = .{ .albedo = .{ 0.8, 0.2, 0.2, 1.0 } } });
|
_ = try materials.put(.{ .style = .{ .albedo = .{ 0.8, 0.2, 0.2, 1.0 } } });
|
||||||
_ = try materials.put(.{ .unique = .{ .topology = .line_list } });
|
_ = try materials.put(.{ .unique = .{ .topology = .line_list } });
|
||||||
|
const c = try materials.put(.{ .unique = .{ .topology = .line_list } });
|
||||||
|
_ = try materials.put(.{ .unique = .{ .topology = .line_list } });
|
||||||
|
|
||||||
|
try materials.remove(c);
|
||||||
|
|
||||||
// --- # ---
|
// --- # ---
|
||||||
if (log.print(.debug, "MATERIALS", ansi.blue)) |a| {
|
if (log.print(.debug, "MATERIALS", ansi.blue)) |a| {
|
||||||
defer a.end();
|
defer a.end();
|
||||||
|
|
||||||
// --- # ---
|
// --- # ---
|
||||||
for (materials.items()) |material| {
|
for (materials.values.items) |material| {
|
||||||
if (a.section(.debug, "", ansi.cyan)) |b| {
|
if (a.section(.debug, "", ansi.cyan)) |b| {
|
||||||
defer b.end();
|
defer b.end();
|
||||||
|
|
||||||
|
|
@ -106,74 +128,31 @@ test "SlotMap" {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test "SlotMap-DEP" {
|
test "KeyedSlotMap" {
|
||||||
{
|
std.debug.print("{s}━━━ KEYED SLOTMAP ━━━{s}\n", .{ ansi.blue, ansi.reset });
|
||||||
// --- LOG ---
|
|
||||||
log.warn("...", .{}, @src());
|
|
||||||
log.err("...", .{}, @src());
|
|
||||||
log.info("...", .{}, @src());
|
|
||||||
log.debug("...", .{}, @src());
|
|
||||||
log.trace("...", .{}, @src());
|
|
||||||
|
|
||||||
// --- PRINT ---
|
// --- # ---
|
||||||
if (log.print(.debug, "DEVICE INFO", ansi.bold ++ ansi.red)) |a| {
|
var pipelines: KeyedSlotMap(Material.Unique, u32) = .init(std.testing.allocator);
|
||||||
defer a.end();
|
defer pipelines.deinit();
|
||||||
|
|
||||||
// --- VALUES ---
|
// --- ADD PIPELINES ---
|
||||||
a.write("device -> {s}", .{"GTX"});
|
_ = try pipelines.put(.{}, 0);
|
||||||
a.write("vendor -> {s}", .{"Nvidia"});
|
_ = try pipelines.put(.{ .topology = .line_list }, 0);
|
||||||
|
|
||||||
// --- SECTION ---
|
// --- ITERATOR ---
|
||||||
if (a.section(.err, "MEMORY", ansi.green)) |b| {
|
var it = pipelines.iterator();
|
||||||
defer b.end();
|
while (it.next()) |entry| {
|
||||||
|
std.debug.print("[{}] {}\n", .{
|
||||||
b.write("VRAM -> {}GB", .{24});
|
entry.value,
|
||||||
}
|
entry.key.topology,
|
||||||
}
|
});
|
||||||
|
|
||||||
log.set(.{ .style = .rounded });
|
|
||||||
|
|
||||||
// --- PRINT ---
|
|
||||||
if (log.print(.debug, "DEVICE INFO", ansi.bold ++ ansi.red)) |a| {
|
|
||||||
defer a.end();
|
|
||||||
|
|
||||||
// --- VALUES ---
|
|
||||||
a.write("device -> {s}", .{"GTX"});
|
|
||||||
a.write("vendor -> {s}", .{"Nvidia"});
|
|
||||||
|
|
||||||
// --- SECTION ---
|
|
||||||
if (a.section(.err, "MEMORY", ansi.green)) |b| {
|
|
||||||
defer b.end();
|
|
||||||
|
|
||||||
b.write("VRAM -> {}GB", .{24});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.set(.{ .style = .pipe });
|
|
||||||
|
|
||||||
// --- PRINT ---
|
|
||||||
if (log.print(.debug, "DEVICE INFO", ansi.bold ++ ansi.red)) |a| {
|
|
||||||
defer a.end();
|
|
||||||
|
|
||||||
// --- VALUES ---
|
|
||||||
a.write("device -> {s}", .{"GTX"});
|
|
||||||
a.write("vendor -> {s}", .{"Nvidia"});
|
|
||||||
|
|
||||||
// --- SECTION ---
|
|
||||||
if (a.section(.err, "MEMORY", ansi.green)) |b| {
|
|
||||||
defer b.end();
|
|
||||||
|
|
||||||
// --- VALUES ---
|
|
||||||
b.write("VRAM -> {}GB", .{24});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test "RingBuffer" {
|
test "RingBuffer" {
|
||||||
std.debug.print("{s}━━━ RING BUFFER ━━━{s}\n", .{ ansi.blue, ansi.reset });
|
std.debug.print("{s}━━━ RING BUFFER ━━━{s}\n", .{ ansi.blue, ansi.reset });
|
||||||
|
|
||||||
var prev_inputs: RingBuffer([]const u8, 256) = .{};
|
var prev_inputs: RingBuffer([]const u8, 256) = .empty;
|
||||||
|
|
||||||
try prev_inputs.push("w");
|
try prev_inputs.push("w");
|
||||||
try prev_inputs.push("s");
|
try prev_inputs.push("s");
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,25 @@
|
||||||
|
//! ----------------------------------------------------
|
||||||
|
//! ----------------------------------------------------
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// Generational Handle
|
||||||
|
/// ----------------------------------------------------
|
||||||
pub fn Handle(comptime T: type) type {
|
pub fn Handle(comptime T: type) type {
|
||||||
_ = T;
|
_ = T;
|
||||||
return struct {
|
return struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
|
//
|
||||||
|
// FIELDS
|
||||||
|
//
|
||||||
|
|
||||||
idx: u32 = 0,
|
idx: u32 = 0,
|
||||||
gen: u32 = 0,
|
gen: u32 = 0,
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
pub fn eql(
|
pub fn eql(
|
||||||
self: Self,
|
self: Self,
|
||||||
other: Self,
|
other: Self,
|
||||||
|
|
@ -13,5 +27,14 @@ pub fn Handle(comptime T: type) type {
|
||||||
return self.idx == other.idx and
|
return self.idx == other.idx and
|
||||||
self.gen == other.gen;
|
self.gen == other.gen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn format(
|
||||||
|
self: @This(),
|
||||||
|
writer: *std.Io.Writer,
|
||||||
|
) std.Io.Writer.Error!void {
|
||||||
|
try writer.print("{}:{}", .{ self.idx, self.gen });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
//! ----------------------------------------------------
|
||||||
|
//! ----------------------------------------------------
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const SlotMap = @import("slot_map.zig").SlotMap;
|
const SlotMap = @import("slot_map.zig").SlotMap;
|
||||||
const Handle = @import("handle.zig").Handle;
|
const Handle = @import("handle.zig").Handle;
|
||||||
|
|
@ -6,19 +9,22 @@ const Handle = @import("handle.zig").Handle;
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```zig
|
/// ```zig
|
||||||
/// // --- SHADER MAP ---
|
/// // --- # ---
|
||||||
/// var shaders: KeyedSlotMap(ShaderID, GPUShader) = .init(alloc);
|
/// var pipelines: KeyedSlotMap(Material.Unique, u32) = .init(std.testing.allocator);
|
||||||
/// defer shaders.deinit();
|
|
||||||
///
|
|
||||||
/// // --- PIPELINE MAP ---
|
|
||||||
/// var pipelines: KeyedSlotMap(PipelineKey, GPUPipeline) = .init(alloc);
|
|
||||||
/// defer pipelines.deinit();
|
/// defer pipelines.deinit();
|
||||||
///
|
///
|
||||||
/// // --- ADD MATERIALS ---
|
/// // --- ADD PIPELINES ---
|
||||||
/// try materials.append(alloc, .{ .shader = .pbr }); // BLACK
|
/// _ = try pipelines.put(.{}, 0);
|
||||||
/// try materials.append(alloc, .{ .shader = .pbr, .albedo = .{ 1, 0, 0, 1 } }); // RED
|
/// _ = try pipelines.put(.{ .topology = .line_list }, 0);
|
||||||
/// try materials.append(alloc, .{ .shader = .unlit, .albedo = .{ 0, 1, 0, 1 } }); // GREEN
|
///
|
||||||
/// try materials.append(alloc, .{ .shader = .unlit, .albedo = .{ 0, 0, 1, 1 } }); // GREEN
|
/// // --- ITERATOR ---
|
||||||
|
/// var it = pipelines.iterator();
|
||||||
|
/// while (it.next()) |entry| {
|
||||||
|
/// std.debug.print("[{}] {}\n", .{
|
||||||
|
/// entry.value,
|
||||||
|
/// entry.key.topology,
|
||||||
|
/// });
|
||||||
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
pub fn KeyedSlotMap(
|
pub fn KeyedSlotMap(
|
||||||
|
|
@ -35,6 +41,71 @@ pub fn KeyedSlotMap(
|
||||||
value: V,
|
value: V,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// Cursor over live entries
|
||||||
|
/// Removed (freed) slots are skipped
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub const Iterator = struct {
|
||||||
|
entries: []Entry,
|
||||||
|
free_list: []const u32,
|
||||||
|
index: usize = 0,
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn next(self: *Iterator) ?*Entry {
|
||||||
|
return advance(self.entries, self.free_list, &self.index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// Cursor over live keys
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub const KeyIterator = struct {
|
||||||
|
entries: []Entry,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// Cursor over live values
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub const ValueIterator = struct {
|
||||||
|
entries: []Entry,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// Skips slots that live on the free list (tombstones)
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
fn advance(
|
||||||
|
entries: []Entry,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// FIELDS
|
// FIELDS
|
||||||
//
|
//
|
||||||
|
|
@ -76,7 +147,7 @@ pub fn KeyedSlotMap(
|
||||||
|
|
||||||
// --- RESULT ---
|
// --- RESULT ---
|
||||||
const handle = try self.slot_map.put(.{ .value = value, .key = key });
|
const handle = try self.slot_map.put(.{ .value = value, .key = key });
|
||||||
errdefer self.slot_map.remove(handle);
|
errdefer self.slot_map.remove(handle) catch {};
|
||||||
|
|
||||||
// --- NEW LOOKUP ---
|
// --- NEW LOOKUP ---
|
||||||
try self.lookup.put(key, handle);
|
try self.lookup.put(key, handle);
|
||||||
|
|
@ -86,28 +157,35 @@ pub fn KeyedSlotMap(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
|
/// Returns an error if the free list could not grow
|
||||||
|
/// Stale handles are silently ignored
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
pub fn remove(
|
pub fn remove(
|
||||||
self: *Self,
|
self: *Self,
|
||||||
handle: Handle(V),
|
handle: Handle(V),
|
||||||
) void {
|
) error{OutOfMemory}!void {
|
||||||
// --- GET IDX ---
|
|
||||||
const idx: u32 = handle.idx;
|
const idx: u32 = handle.idx;
|
||||||
|
|
||||||
|
// --- CHECK IF STALE ---
|
||||||
|
if (idx >= self.slot_map.generations.items.len) return;
|
||||||
|
if (self.slot_map.generations.items[idx] != handle.gen) return;
|
||||||
|
|
||||||
|
// --- GRAB KEY BEFORE REMOVING ---
|
||||||
|
const key = self.slot_map.values.items[idx].key;
|
||||||
|
|
||||||
// --- REMOVE FROM SLOTMAP ---
|
// --- REMOVE FROM SLOTMAP ---
|
||||||
self.slot_map.remove(handle);
|
try self.slot_map.remove(handle);
|
||||||
|
|
||||||
// --- REMOVE KEY ---
|
// --- REMOVE KEY ---
|
||||||
const key = self.values.items[idx].key;
|
|
||||||
_ = self.lookup.remove(key);
|
_ = self.lookup.remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
pub fn removeByKey(self: *Self, key: K) 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);
|
||||||
self.slot_map.remove(handle);
|
try self.slot_map.remove(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
|
|
@ -162,7 +240,7 @@ pub fn KeyedSlotMap(
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
pub fn count(self: *Self) u32 {
|
pub fn count(self: *Self) u32 {
|
||||||
return self.lookup.count();
|
return @intCast(self.lookup.count());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
|
|
@ -170,5 +248,35 @@ pub fn KeyedSlotMap(
|
||||||
pub fn items(self: *Self) []Entry {
|
pub fn items(self: *Self) []Entry {
|
||||||
return self.slot_map.values.items;
|
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,
|
||||||
|
.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,
|
||||||
|
.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,
|
||||||
|
.free_list = self.slot_map.free_list.items,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
//! ----------------------------------------------------
|
||||||
|
//! ----------------------------------------------------
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const ansi = @import("ansi.zig");
|
const ansi = @import("ansi.zig");
|
||||||
|
|
||||||
|
|
@ -252,7 +255,7 @@ pub fn print(
|
||||||
|
|
||||||
// --- RESULT ---
|
// --- RESULT ---
|
||||||
const box = boxChars();
|
const box = boxChars();
|
||||||
std.debug.print("{s}{s}{s}{s} {s}\n", .{ color, box.top_left, box.horizontal, ansi.reset, name });
|
std.debug.print("{s}{s}{s}[{s}{s}{s}]{s}\n", .{ color, box.top_left, box.horizontal, ansi.reset, name, color, ansi.reset });
|
||||||
return .{ .color = color };
|
return .{ .color = color };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,10 +48,19 @@ pub fn RingBuffer(
|
||||||
// FIELDS
|
// FIELDS
|
||||||
//
|
//
|
||||||
|
|
||||||
buffers: [Capacity]T = std.mem.zeroes([Capacity]T),
|
buffers: [Capacity]T,
|
||||||
head: usize = 0,
|
head: usize,
|
||||||
tail: usize = 0,
|
tail: usize,
|
||||||
count: usize = 0,
|
count: usize,
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub const empty = Self{
|
||||||
|
.buffers = undefined,
|
||||||
|
.head = 0,
|
||||||
|
.tail = 0,
|
||||||
|
.count = 0,
|
||||||
|
};
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
//! ----------------------------------------------------
|
||||||
|
//! ----------------------------------------------------
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Handle = @import("handle.zig").Handle;
|
const Handle = @import("handle.zig").Handle;
|
||||||
|
|
||||||
|
|
@ -29,6 +32,9 @@ pub fn SlotMap(comptime T: type) type {
|
||||||
generations: std.ArrayList(u32),
|
generations: std.ArrayList(u32),
|
||||||
free_list: std.ArrayList(u32),
|
free_list: std.ArrayList(u32),
|
||||||
|
|
||||||
|
// --- GENERATION COUNTER ---
|
||||||
|
next_gen: u32 = 0,
|
||||||
|
|
||||||
// --- MEMORY ---
|
// --- MEMORY ---
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
|
|
||||||
|
|
@ -37,10 +43,8 @@ pub fn SlotMap(comptime T: type) type {
|
||||||
pub fn init(alloc: std.mem.Allocator) Self {
|
pub fn init(alloc: std.mem.Allocator) Self {
|
||||||
return .{
|
return .{
|
||||||
.values = .empty,
|
.values = .empty,
|
||||||
|
|
||||||
.generations = .empty,
|
.generations = .empty,
|
||||||
.free_list = .empty,
|
.free_list = .empty,
|
||||||
|
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -59,52 +63,51 @@ pub fn SlotMap(comptime T: type) type {
|
||||||
self: *Self,
|
self: *Self,
|
||||||
value: T,
|
value: T,
|
||||||
) error{OutOfMemory}!Handle(T) {
|
) error{OutOfMemory}!Handle(T) {
|
||||||
// --- NEW GENERATION ---
|
// --- REUSE A FREED SLOT ---
|
||||||
if (self.free_list.pop()) |idx| {
|
if (self.free_list.pop()) |idx| {
|
||||||
self.values.items[@intCast(idx)] = value;
|
const gen = self.mint();
|
||||||
self.generations.items[@intCast(idx)] += 1;
|
self.values.items[idx] = value;
|
||||||
|
self.generations.items[idx] = gen;
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.idx = idx,
|
.idx = idx,
|
||||||
.gen = self.generations.items[idx],
|
.gen = gen,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- METADATA ---
|
// --- METADATA ---
|
||||||
const idx = self.values.items.len;
|
const idx: u32 = @intCast(self.values.items.len);
|
||||||
const handle: Handle(T) = .{
|
const gen = self.mint();
|
||||||
.idx = @intCast(idx),
|
|
||||||
.gen = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
// --- NEW VALUE ---
|
// --- NEW VALUE ---
|
||||||
try self.values.append(self.alloc, value);
|
try self.values.append(self.alloc, value);
|
||||||
errdefer _ = self.values.swapRemove(idx);
|
errdefer _ = self.values.pop();
|
||||||
|
try self.generations.append(self.alloc, gen);
|
||||||
// --- NEW GENERATION ---
|
|
||||||
try self.generations.append(self.alloc, 1);
|
|
||||||
|
|
||||||
// --- RESULT ---
|
// --- RESULT ---
|
||||||
return handle;
|
return .{
|
||||||
|
.idx = idx,
|
||||||
|
.gen = gen,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
|
/// Returns an error if the free list could not grow
|
||||||
|
/// Stale handles are silently ignored
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
pub fn remove(
|
pub fn remove(
|
||||||
self: *Self,
|
self: *Self,
|
||||||
handle: Handle(T),
|
handle: Handle(T),
|
||||||
) void {
|
) error{OutOfMemory}!void {
|
||||||
const idx: u32 = handle.idx;
|
const idx: u32 = handle.idx;
|
||||||
|
|
||||||
// --- CHECK IF STALE ---
|
// --- CHECK IF STALE ---
|
||||||
if (idx >= self.generations.items.len) return;
|
if (idx >= self.generations.items.len) return;
|
||||||
if (self.generations.items[idx] != handle.gen) return;
|
if (self.generations.items[idx] != handle.gen) return;
|
||||||
|
|
||||||
// --- GENERATION ---
|
// --- FREE ---
|
||||||
self.generations.items[idx] +|= 1;
|
try self.free_list.append(self.alloc, idx);
|
||||||
self.free_list.append(self.alloc, idx) catch |err| {
|
self.generations.items[idx] = self.mint();
|
||||||
std.debug.print("[SLOTMAP_ERROR] {s}\n", .{@errorName(err)});
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
|
|
@ -116,9 +119,21 @@ pub fn SlotMap(comptime T: type) type {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
|
/// Reduce length to 0
|
||||||
|
/// Invalidates all element pointers and handles
|
||||||
/// ----------------------------------------------------
|
/// ----------------------------------------------------
|
||||||
pub fn items(self: *Self) []T {
|
pub fn clearRetainingCapacity(self: *Self) void {
|
||||||
return self.values.items;
|
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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
|
//! ----------------------------------------------------
|
||||||
|
//! ----------------------------------------------------
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// Returns hash of @typeName(T)
|
||||||
|
/// ----------------------------------------------------
|
||||||
pub fn of(comptime T: type) u64 {
|
pub fn of(comptime T: type) u64 {
|
||||||
return std.hash.Wyhash.hash(
|
return std.hash.Wyhash.hash(
|
||||||
694721,
|
694721,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue