106 lines
3.2 KiB
Zig
106 lines
3.2 KiB
Zig
// adapted from https://github.com/prime31/zig-ecs/blob/master/src/ecs/utils.zig
|
|
|
|
const std = @import("std");
|
|
|
|
/// stores a single object of type T for each T added
|
|
pub const TypeStore = struct {
|
|
map: std.AutoHashMap(u32, []u8),
|
|
allocator: *std.mem.Allocator,
|
|
|
|
pub fn init(allocator: *std.mem.Allocator) TypeStore {
|
|
return TypeStore{
|
|
.map = std.AutoHashMap(u32, []u8).init(allocator),
|
|
.allocator = allocator,
|
|
};
|
|
}
|
|
|
|
pub fn deinit(self: *TypeStore) void {
|
|
var iter = self.map.iterator();
|
|
while (iter.next()) |kv| {
|
|
self.allocator.free(kv.value);
|
|
}
|
|
self.map.deinit();
|
|
}
|
|
|
|
/// adds instance, returning a pointer to the item as it lives in the store
|
|
pub fn add(self: *TypeStore, instance: anytype) void {
|
|
var bytes = self.allocator.alloc(u8, @sizeOf(@TypeOf(instance))) catch unreachable;
|
|
std.mem.copy(u8, bytes, std.mem.asBytes(&instance));
|
|
_ = self.map.put(typeId(@TypeOf(instance)), bytes) catch unreachable;
|
|
}
|
|
|
|
pub fn get(self: *TypeStore, comptime T: type) *T {
|
|
if (self.map.get(typeId(T))) |bytes| {
|
|
return @ptrCast(*T, @alignCast(@alignOf(T), bytes));
|
|
}
|
|
unreachable;
|
|
}
|
|
|
|
pub fn getConst(self: *TypeStore, comptime T: type) T {
|
|
return self.get(T).*;
|
|
}
|
|
|
|
pub fn getOrAdd(self: *TypeStore, comptime T: type) *T {
|
|
if (!self.has(T)) {
|
|
var instance = std.mem.zeroes(T);
|
|
self.add(instance);
|
|
}
|
|
return self.get(T);
|
|
}
|
|
|
|
pub fn remove(self: *TypeStore, comptime T: type) void {
|
|
if (self.map.get(typeId(T))) |bytes| {
|
|
self.allocator.free(bytes);
|
|
_ = self.map.remove(typeId(T));
|
|
}
|
|
}
|
|
|
|
pub fn has(self: *TypeStore, comptime T: type) bool {
|
|
return self.map.contains(typeId(T));
|
|
}
|
|
|
|
fn typeId(comptime T: type) u32 {
|
|
comptime return hashStringFnv(u32, @typeName(T));
|
|
}
|
|
|
|
fn hashStringFnv(comptime ReturnType: type, comptime str: []const u8) ReturnType {
|
|
std.debug.assert(ReturnType == u32 or ReturnType == u64);
|
|
|
|
const prime = if (ReturnType == u32) @as(u32, 16777619) else @as(u64, 1099511628211);
|
|
var value = if (ReturnType == u32) @as(u32, 2166136261) else @as(u64, 14695981039346656037);
|
|
for (str) |c| {
|
|
value = (value ^ @intCast(u32, c)) *% prime;
|
|
}
|
|
return value;
|
|
}
|
|
};
|
|
|
|
test "TypeStore" {
|
|
const Vector = struct { x: f32 = 0, y: f32 = 0, z: f32 = 0 };
|
|
|
|
var store = TypeStore.init(std.testing.allocator);
|
|
defer store.deinit();
|
|
|
|
var orig = Vector{ .x = 5, .y = 6, .z = 8 };
|
|
store.add(orig);
|
|
std.testing.expect(store.has(Vector));
|
|
std.testing.expectEqual(store.get(Vector).*, orig);
|
|
|
|
var v = store.get(Vector);
|
|
std.testing.expectEqual(v.*, Vector{ .x = 5, .y = 6, .z = 8 });
|
|
v.*.x = 666;
|
|
|
|
var v2 = store.get(Vector);
|
|
std.testing.expectEqual(v2.*, Vector{ .x = 666, .y = 6, .z = 8 });
|
|
|
|
store.remove(Vector);
|
|
std.testing.expect(!store.has(Vector));
|
|
|
|
var v3 = store.getOrAdd(u32);
|
|
std.testing.expectEqual(v3.*, 0);
|
|
v3.* = 777;
|
|
|
|
var v4 = store.get(u32);
|
|
std.testing.expectEqual(v3.*, 777);
|
|
}
|