Added [FixedArrayList, FixedSlotMap] | Updated [gtl.zig, README.md]

This commit is contained in:
abux 2026-08-01 18:18:53 +01:00
parent 054ca5ea21
commit e20ce39550
4 changed files with 315 additions and 30 deletions

View file

@ -1,20 +1,23 @@
**Includes**
- ANSI
- SlotMap | KeyedSlotMap
- Custom Log
# Includes
## Arrays
- SlotMap
- KeyedSlotMap
- FixedSlotMap
- FixedArrayList
- RingBuffer
- Handle(T) **Generational**
## Misc
- log
- ansi (colors etc)
- TypeID
**Logs**
```zig
log.warn("...", .{});
log.err("...", .{});
log.info("...", .{});
log.debug("...", .{});
log.trace("...", .{});
log.fatal("...", .{});
```
# Examples
## KeyedSlotMap
**KeyedSlotMap**
```zig
// --- SHADER MAP ---
var shaders: KeyedSlotMap(ShaderID, GPUShader) = .init(alloc);
@ -25,7 +28,8 @@ const shader_handle = try shaders.put(.pbr, .{ .raw = undefined });
defer shaders.remove(shader_handle);
```
**TypeID**
## TypeID
```zig
const Player = struct {};
std.debug.print("TypeID(Player): {}\n", .{TypeID(Player)});
@ -34,15 +38,26 @@ std.debug.print("TypeID(Player): {}\n", .{TypeID(Player)});
Output: TypeID(Player): 2462284566368800629
```
# NicePrint
## Logs
## Styles
```zig
log.warn("...", .{});
log.err("...", .{});
log.info("...", .{});
log.debug("...", .{});
log.trace("...", .{});
log.fatal("...", .{});
```
## NicePrint
### Styles
- default
- rounded
- pipe
- custom
## Example
### Example
```zig
// --- PRINT ---
if (log.print(.debug, "DEVICE INFO", ansi.bold ++ ansi.red)) |a| {
@ -61,7 +76,7 @@ if (log.print(.debug, "DEVICE INFO", ansi.bold ++ ansi.red)) |a| {
}
```
## Output
### Output
```text
┏━ DEVICE INFO
┃ device -> GTX
@ -71,3 +86,6 @@ if (log.print(.debug, "DEVICE INFO", ansi.bold ++ ansi.red)) |a| {
┃ ┗━
┗━
```
# TODO
[ ] Lear how to properly structure markdown 🥀️

134
src/fixed_array_list.zig Normal file
View file

@ -0,0 +1,134 @@
const std = @import("std");
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn FixedArrayList(
comptime T: type,
comptime Capacity: usize,
) type {
return struct {
const Self = @This();
//
// FIELDS
//
data: [Capacity]T,
cursor: usize = 0,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const empty = Self{
.data = undefined,
.cursor = 0,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn append(self: *Self, item: T) error{Full}!void {
if (self.isFull()) return error.Full;
self.data[self.cursor] = item;
self.cursor += 1;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn appendMany(self: *Self, values: []const T) error{Full}!void {
if (self.cursor + values.len > Capacity) return error.Full;
@memcpy(
self.data[self.cursor .. self.cursor + values.len],
values,
);
self.cursor += values.len;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn pop(self: *Self) ?T {
if (self.cursor == 0) return null;
self.cursor -= 1;
return self.data[self.cursor];
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn swapRemove(self: *Self, i: usize) ?T {
if (i >= self.cursor) return null;
const removed: T = self.data[i];
self.cursor -= 1;
if (i != self.cursor) {
self.data[i] = self.data[self.cursor];
}
return removed;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn get(self: *Self, i: usize) ?T {
if (i >= self.cursor) return null;
return self.data[i];
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getPtr(self: *Self, i: usize) ?*T {
if (i >= self.cursor) return null;
return &self.data[i];
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn getConstPtr(self: *const Self, i: usize) ?*const T {
if (i >= self.cursor) return null;
return &self.data[i];
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn items(self: *Self) []T {
return self.data[0..self.cursor];
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn len(self: *const Self) usize {
return self.cursor;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn capacity(_: *const Self) usize {
return Capacity;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn available(self: *const Self) usize {
return Capacity - self.cursor;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn clear(self: *Self) void {
self.cursor = 0;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn isEmpty(self: *const Self) bool {
return self.cursor == 0;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn isFull(self: *const Self) bool {
return self.cursor == Capacity;
}
};
}

105
src/fixed_slot_map.zig Normal file
View file

@ -0,0 +1,105 @@
const std = @import("std");
const FixedArrayList = @import("fixed_array_list.zig").FixedArrayList;
const Handle = @import("handle.zig").Handle;
/// ----------------------------------------------------
/// # Example
///
/// ```zig
/// ```
/// ----------------------------------------------------
pub fn FixedSlotMap(
comptime T: type,
comptime Capacity: usize,
) type {
return struct {
const Self = @This();
//
// FIELDS
//
// --- VALUES ---
values: FixedArrayList(T, Capacity),
// --- LIFE TIMES ---
generations: FixedArrayList(u32, Capacity),
free_list: FixedArrayList(u32, Capacity),
/// ----------------------------------------------------
/// ----------------------------------------------------
pub const empty = Self{
.values = .empty,
.generations = .empty,
.free_list = .empty,
};
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn put(
self: *Self,
value: T,
) error{Full}!Handle(T) {
// --- NEW GENERATION ---
if (self.free_list.pop()) |idx| {
self.values.data[@intCast(idx)] = value;
self.generations.data[@intCast(idx)] += 1;
return .{
.idx = idx,
.gen = self.generations.data[idx],
};
}
// --- METADATA ---
const idx = self.values.len();
const handle: Handle(T) = .{
.idx = @intCast(idx),
.gen = 1,
};
// --- NEW VALUE ---
try self.values.append(value);
errdefer _ = self.values.swapRemove(idx);
// --- NEW GENERATION ---
try self.generations.append(1);
// --- RESULT ---
return handle;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn remove(
self: *Self,
handle: Handle(T),
) void {
const idx: u32 = handle.idx;
// --- CHECK IF STALE ---
if (idx >= self.generations.len()) return;
if (self.generations.data[idx] != handle.gen) return;
// --- GENERATION ---
self.generations.data[idx] +|= 1;
self.free_list.append(idx) catch |err| {
std.debug.print("[SLOTMAP_ERROR] {s}\n", .{@errorName(err)});
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn get(self: *Self, handle: Handle(T)) ?*T {
if (handle.idx >= self.values.len()) return null;
if (self.generations.data[handle.idx] != handle.gen) return null;
return &self.values.data[handle.idx];
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn items(self: *Self) []T {
return self.values.items();
}
};
}

View file

@ -11,10 +11,14 @@ const std = @import("std");
pub const log = @import("log.zig");
pub const ansi = @import("ansi.zig");
pub const Handle = @import("handle.zig").Handle;
pub const TypeID = @import("type_id.zig").of;
// --- ARRAYS ---
pub const SlotMap = @import("slot_map.zig").SlotMap;
pub const KeyedSlotMap = @import("keyed_slot_map.zig").KeyedSlotMap;
pub const FixedArrayList = @import("fixed_array_list.zig").FixedArrayList;
pub const FixedSlotMap = @import("fixed_slot_map.zig").FixedSlotMap;
pub const RingBuffer = @import("ring_buffer.zig").RingBuffer;
pub const TypeID = @import("type_id.zig").of;
//
// STRUCTS
@ -28,12 +32,6 @@ const GPUShader = struct { raw: *anyopaque };
const PipelineKey = struct { shader: ShaderID };
const GPUPipeline = struct { raw: *anyopaque };
// --- MATERIAL ---
const MaterialDep = struct {
shader: ShaderID = .pbr,
albedo: @Vector(4, f32) = .{ 0, 0, 0, 1 },
};
/// ----------------------------------------------------
/// ----------------------------------------------------
const Material = struct {
@ -170,10 +168,10 @@ test "SlotMap-DEP" {
}
}
}
}
//
// RING BUFFER EXAMPLE
//
test "RingBuffer" {
std.debug.print("{s}━━━ RING BUFFER ━━━{s}\n", .{ ansi.blue, ansi.reset });
var prev_inputs: RingBuffer([]const u8, 256) = .{};
@ -185,6 +183,36 @@ test "SlotMap-DEP" {
var i: usize = 0;
while (prev_inputs.pop()) |data| {
defer i += 1;
log.debug("[{}][PreviousInputs] -> \"{s}\"", .{ i, data }, null);
std.debug.print("[{}][PreviousInputs] -> \"{s}\"\n", .{ i, data });
}
}
test "FixedArrayList" {
std.debug.print("{s}━━━ FIXED ARRAY LIST ━━━{s}\n", .{ ansi.blue, ansi.reset });
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});
}
}
test "FixedSlotMap" {
std.debug.print("{s}━━━ FIXED SLOT MAP ━━━{s}\n", .{ ansi.blue, ansi.reset });
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});
}
}