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