Compare commits

...

2 commits

Author SHA1 Message Date
0aef87a252 Improved stuff n stuff 2026-08-03 16:30:26 +01:00
0e174f878f Improved documentation and consistancy 2026-08-02 13:23:12 +01:00
10 changed files with 323 additions and 134 deletions

View file

@ -1,3 +1,7 @@
//! ----------------------------------------------------
//! ANSI Colors for terminal
//! ----------------------------------------------------
// --- RESET --- // --- RESET ---
pub const reset = "\x1b[0m"; pub const reset = "\x1b[0m";

View file

@ -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,

View file

@ -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;
} }
}; };
} }

View file

@ -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");

View file

@ -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 });
}
}; };
} }

View file

@ -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,
};
}
}; };
} }

View file

@ -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 };
} }

View file

@ -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,
};
/// ---------------------------------------------------- /// ----------------------------------------------------
/// ---------------------------------------------------- /// ----------------------------------------------------

View file

@ -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;
} }
}; };
} }

View file

@ -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,