286 lines
9.7 KiB
Zig
286 lines
9.7 KiB
Zig
const std = @import("std");
|
|
const App = @import("app");
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn Registry(
|
|
comptime K: type,
|
|
comptime V: type,
|
|
) type {
|
|
return struct {
|
|
const Self = @This();
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub const Metadata = struct {
|
|
source: ?[]const u8 = null,
|
|
reload: ?*const fn (*V, App.scheduler.Context) anyerror!void = null,
|
|
};
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub const Entry = struct {
|
|
key: K,
|
|
value: V,
|
|
};
|
|
|
|
//
|
|
// FIELDS
|
|
//
|
|
|
|
meta: std.ArrayList(Metadata),
|
|
lookup: std.AutoHashMap(K, usize),
|
|
entries: std.ArrayList(Entry),
|
|
alloc: std.mem.Allocator,
|
|
|
|
//
|
|
// LIFECYCLE
|
|
//
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn init(alloc: std.mem.Allocator) Self {
|
|
return .{
|
|
.meta = .empty,
|
|
.lookup = .init(alloc),
|
|
.entries = .empty,
|
|
.alloc = alloc,
|
|
};
|
|
}
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn deinit(self: *Self) void {
|
|
self.meta.deinit(self.alloc);
|
|
self.lookup.deinit();
|
|
self.entries.deinit(self.alloc);
|
|
}
|
|
|
|
//
|
|
// INSERTION
|
|
//
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn put(
|
|
self: *Self,
|
|
key: K,
|
|
value: V,
|
|
) !void {
|
|
if (self.lookup.contains(key)) return error.Duplicate;
|
|
try self.putNoDup(key, value, .{});
|
|
}
|
|
|
|
/// ----------------------------------------------------
|
|
/// Insert or replace
|
|
/// ----------------------------------------------------
|
|
pub fn upsert(
|
|
self: *Self,
|
|
key: K,
|
|
value: V,
|
|
) !?V {
|
|
if (self.lookup.contains(key)) {
|
|
const idx = self.lookup.get(key).?;
|
|
const old = self.entries.items[idx].value;
|
|
self.entries.items[idx].value = value;
|
|
return old;
|
|
}
|
|
try self.putNoDup(key, value, .{});
|
|
return null;
|
|
}
|
|
|
|
/// ----------------------------------------------------
|
|
/// Insert with metadata
|
|
/// ----------------------------------------------------
|
|
pub fn putWith(
|
|
self: *Self,
|
|
key: K,
|
|
value: V,
|
|
metadata: Metadata,
|
|
) !void {
|
|
if (self.lookup.contains(key)) return error.Duplicate;
|
|
try self.putNoDup(key, value, metadata);
|
|
}
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
fn putNoDup(
|
|
self: *Self,
|
|
key: K,
|
|
value: V,
|
|
metadata: Metadata,
|
|
) !void {
|
|
try self.entries.append(self.alloc, .{ .key = key, .value = value });
|
|
errdefer _ = self.entries.swapRemove(self.entries.items.len - 1);
|
|
|
|
try self.meta.append(self.alloc, metadata);
|
|
errdefer _ = self.meta.swapRemove(self.meta.items.len - 1);
|
|
|
|
try self.lookup.put(key, self.entries.items.len - 1);
|
|
}
|
|
|
|
//
|
|
// REMOVAL
|
|
//
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn remove(self: *Self, key: K) bool {
|
|
const idx = self.lookup.get(key) orelse return false;
|
|
self.removeIndex(idx);
|
|
return true;
|
|
}
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn removeLast(self: *Self) bool {
|
|
if (self.entries.items.len == 0) return false;
|
|
const last = self.entries.items.len - 1;
|
|
_ = self.lookup.remove(self.entries.items[last].key);
|
|
self.entries.swapRemove(last);
|
|
self.meta.swapRemove(last);
|
|
return true;
|
|
}
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
fn removeIndex(self: *Self, idx: usize) void {
|
|
const last = self.entries.items.len - 1;
|
|
|
|
// --- UPDATE LOOKUP ---
|
|
if (idx != last) {
|
|
self.lookup.put(self.entries.items[last].key, idx) catch {};
|
|
}
|
|
|
|
_ = self.meta.swapRemove(idx);
|
|
_ = self.lookup.remove(self.entries.items[idx].key);
|
|
_ = self.entries.swapRemove(idx);
|
|
}
|
|
|
|
//
|
|
// LOOKUP
|
|
//
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn contains(self: Self, key: K) bool {
|
|
return self.lookup.contains(key);
|
|
}
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn get(self: Self, key: K) ?*const V {
|
|
const idx = self.lookup.get(key) orelse return null;
|
|
return &self.entries.items[idx].value;
|
|
}
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn getPtr(self: *Self, key: K) ?*V {
|
|
const idx = self.lookup.get(key) orelse return null;
|
|
return &self.entries.items[idx].value;
|
|
}
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn getEntry(self: Self, key: K) ?*const Entry {
|
|
const idx = self.lookup.get(key) orelse return null;
|
|
return &self.entries.items[idx];
|
|
}
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn getMetadata(self: Self, key: K) ?*const Metadata {
|
|
const idx = self.lookup.get(key) orelse return null;
|
|
return &self.meta.items[idx];
|
|
}
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn getPtrMetadata(self: *Self, key: K) ?*Metadata {
|
|
const idx = self.lookup.get(key) orelse return null;
|
|
return &self.meta.items[idx];
|
|
}
|
|
|
|
//
|
|
// ITERATION
|
|
//
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn count(self: Self) usize {
|
|
return self.entries.items.len;
|
|
}
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn values(self: Self) []const Entry {
|
|
return self.entries.items;
|
|
}
|
|
|
|
/// ----------------------------------------------------
|
|
/// Value, Metadata
|
|
/// ----------------------------------------------------
|
|
pub fn iterator(self: *Self) Iterator {
|
|
return .{ .registry = self, .index = 0 };
|
|
}
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub const Iterator = struct {
|
|
registry: *Self,
|
|
index: usize,
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn next(it: *Iterator) ?struct { value: *V, meta: *Metadata } {
|
|
if (it.index >= it.registry.entries.items.len) return null;
|
|
defer it.index += 1;
|
|
return .{
|
|
.value = &it.registry.entries.items[it.index].value,
|
|
.meta = &it.registry.meta.items[it.index],
|
|
};
|
|
}
|
|
};
|
|
|
|
//
|
|
// IDK
|
|
//
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn reloadAll(
|
|
self: *Self,
|
|
ctx: App.scheduler.Context,
|
|
) anyerror!void {
|
|
var it = self.iterator();
|
|
while (it.next()) |entry| {
|
|
if (entry.meta.reload) |reload| try reload(entry.value, ctx);
|
|
}
|
|
}
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn deinitReverse(self: *Self) void {
|
|
while (self.entries.items.len > 0) {
|
|
_ = self.removeLast();
|
|
}
|
|
}
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn clear(self: *Self) void {
|
|
self.meta.clearRetainingCapacity();
|
|
self.lookup.clearRetainingCapacity();
|
|
self.entries.clearRetainingCapacity();
|
|
}
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn clearAndFree(self: *Self) void {
|
|
self.meta.clearAndFree();
|
|
self.lookup.clearAndFree();
|
|
self.entries.clearAndFree();
|
|
}
|
|
};
|
|
}
|