diff --git a/zig-ecs/src/ecs/registry.zig b/zig-ecs/src/ecs/registry.zig index 7f81ad9..5164162 100644 --- a/zig-ecs/src/ecs/registry.zig +++ b/zig-ecs/src/ecs/registry.zig @@ -4,7 +4,6 @@ const utils = @import("utils.zig"); const Handles = @import("handles.zig").Handles; const SparseSet = @import("sparse_set.zig").SparseSet; -const TypeMap = @import("type_map.zig").TypeMap; const ComponentStorage = @import("component_storage.zig").ComponentStorage; const Sink = @import("../signals/sink.zig").Sink; @@ -30,10 +29,9 @@ pub fn Storage(comptime CompT: type) type { /// no errors to keep the API clean and because if a component array cant be allocated you've got bigger problems. /// Stores a maximum of u8 (256) component Storage(T). pub const Registry = struct { - typemap: TypeMap, handles: EntityHandles, - components: std.AutoHashMap(u8, usize), - contexts: std.AutoHashMap(u8, usize), + components: std.AutoHashMap(u32, usize), + contexts: std.AutoHashMap(u32, usize), groups: std.ArrayList(*GroupData), allocator: *std.mem.Allocator, @@ -80,19 +78,19 @@ pub const Registry = struct { fn maybeValidIf(self: *GroupData, entity: Entity) void { const isValid: bool = blk: { for (self.owned) |tid| { - const ptr = self.registry.components.getValue(@intCast(u8, tid)).?; + const ptr = self.registry.components.getValue(tid).?; if (!@intToPtr(*Storage(u1), ptr).contains(entity)) break :blk false; } for (self.include) |tid| { - const ptr = self.registry.components.getValue(@intCast(u8, tid)).?; + const ptr = self.registry.components.getValue(tid).?; if (!@intToPtr(*Storage(u1), ptr).contains(entity)) break :blk false; } for (self.exclude) |tid| { - const ptr = self.registry.components.getValue(@intCast(u8, tid)).?; + const ptr = self.registry.components.getValue(tid).?; if (@intToPtr(*Storage(u1), ptr).contains(entity)) break :blk false; } @@ -104,10 +102,10 @@ pub const Registry = struct { self.entity_set.add(entity); } else { if (isValid) { - const ptr = self.registry.components.getValue(@intCast(u8, self.owned[0])).?; + const ptr = self.registry.components.getValue(self.owned[0]).?; if (!(@intToPtr(*Storage(u1), ptr).set.index(entity) < self.current)) { for (self.owned) |tid| { - const store_ptr = self.registry.components.getValue(@intCast(u8, tid)).?; + const store_ptr = self.registry.components.getValue(tid).?; var store = @intToPtr(*Storage(u1), store_ptr); store.swap(store.data().*[self.current], entity); } @@ -123,14 +121,13 @@ pub const Registry = struct { if (self.entity_set.contains(entity)) self.entity_set.remove(entity); } else { - const ptr = self.registry.components.getValue(@intCast(u8, self.owned[0])).?; + const ptr = self.registry.components.getValue(self.owned[0]).?; var store = @intToPtr(*Storage(u1), ptr); if (store.contains(entity) and store.set.index(entity) < self.current) { self.current -= 1; for (self.owned) |tid| { - const store_ptr = self.registry.components.getValue(@intCast(u8, tid)).?; + const store_ptr = self.registry.components.getValue(tid).?; store = @intToPtr(*Storage(u1), store_ptr); - std.debug.warn("\n-------- len: {}, curr: {}, ent: {} \n", .{store.data().*.len, self.current, entity}); store.swap(store.data().*[self.current], entity); } } @@ -140,10 +137,9 @@ pub const Registry = struct { pub fn init(allocator: *std.mem.Allocator) Registry { return Registry{ - .typemap = TypeMap.init(allocator), .handles = EntityHandles.init(allocator), - .components = std.AutoHashMap(u8, usize).init(allocator), - .contexts = std.AutoHashMap(u8, usize).init(allocator), + .components = std.AutoHashMap(u32, usize).init(allocator), + .contexts = std.AutoHashMap(u32, usize).init(allocator), .groups = std.ArrayList(*GroupData).init(allocator), .allocator = allocator, }; @@ -164,21 +160,19 @@ pub const Registry = struct { self.components.deinit(); self.contexts.deinit(); self.groups.deinit(); - self.typemap.deinit(); self.handles.deinit(); } pub fn assure(self: *Registry, comptime T: type) *Storage(T) { - var type_id: u8 = undefined; - if (!self.typemap.getOrPut(T, &type_id)) { - var comp_set = Storage(T).initPtr(self.allocator); - var comp_set_ptr = @ptrToInt(comp_set); - _ = self.components.put(type_id, comp_set_ptr) catch unreachable; - return comp_set; + var type_id = utils.typeId(T); + if (self.components.get(type_id)) |kv| { + return @intToPtr(*Storage(T), kv.value); } - const ptr = self.components.getValue(type_id).?; - return @intToPtr(*Storage(T), ptr); + var comp_set = Storage(T).initPtr(self.allocator); + var comp_set_ptr = @ptrToInt(comp_set); + _ = self.components.put(type_id, comp_set_ptr) catch unreachable; + return comp_set; } /// Prepares a pool for the given type if required @@ -339,27 +333,21 @@ pub const Registry = struct { pub fn setContext(self: *Registry, context: var) void { std.debug.assert(@typeInfo(@TypeOf(context)) == .Pointer); - var type_id: u8 = undefined; - _ = self.typemap.getOrPut(@typeInfo(@TypeOf(context)).Pointer.child, &type_id); + var type_id = utils.typeId(@typeInfo(@TypeOf(context)).Pointer.child); _ = self.contexts.put(type_id, @ptrToInt(context)) catch unreachable; } /// Unsets a context variable if it exists pub fn unsetContext(self: *Registry, comptime T: type) void { std.debug.assert(@typeInfo(T) != .Pointer); - - var type_id: u8 = undefined; - _ = self.typemap.getOrPut(T, &type_id); - _ = self.contexts.put(type_id, 0) catch unreachable; + _ = self.contexts.put(utils.typeId(T), 0) catch unreachable; } /// Returns a pointer to an object in the context of the registry pub fn getContext(self: *Registry, comptime T: type) ?*T { std.debug.assert(@typeInfo(T) != .Pointer); - var type_id: u8 = undefined; - _ = self.typemap.getOrPut(T, &type_id); - return if (self.contexts.get(type_id)) |ptr| + return if (self.contexts.get(utils.typeId(T))) |ptr| return if (ptr.value > 0) @intToPtr(*T, ptr.value) else null else null; @@ -383,13 +371,13 @@ pub const Registry = struct { var includes_arr: [includes.len]u32 = undefined; inline for (includes) |t, i| { _ = self.assure(t); - includes_arr[i] = @as(u32, self.typemap.get(t)); + includes_arr[i] = utils.typeId(t); } var excludes_arr: [excludes.len]u32 = undefined; inline for (excludes) |t, i| { _ = self.assure(t); - excludes_arr[i] = @as(u32, self.typemap.get(t)); + excludes_arr[i] = utils.typeId(t); } return MultiView(includes.len, excludes.len).init(self, includes_arr, excludes_arr); @@ -414,19 +402,19 @@ pub const Registry = struct { var owned_arr: [owned.len]u32 = undefined; inline for (owned) |t, i| { _ = self.assure(t); - owned_arr[i] = @as(u32, self.typemap.get(t)); + owned_arr[i] = utils.typeId(t); } var includes_arr: [includes.len]u32 = undefined; inline for (includes) |t, i| { _ = self.assure(t); - includes_arr[i] = @as(u32, self.typemap.get(t)); + includes_arr[i] = utils.typeId(t); } var excludes_arr: [excludes.len]u32 = undefined; inline for (excludes) |t, i| { _ = self.assure(t); - excludes_arr[i] = @as(u32, self.typemap.get(t)); + excludes_arr[i] = utils.typeId(t); } // create a unique hash to identify the group @@ -470,9 +458,7 @@ pub const Registry = struct { while (view_iter.next()) |entity| { new_group_data.entity_set.add(entity); } - } else { - - } + } else {} if (owned.len == 0) { return BasicGroup(includes.len, excludes.len).init(&new_group_data.entity_set, self, includes_arr, excludes_arr); diff --git a/zig-ecs/src/ecs/type_map.zig b/zig-ecs/src/ecs/type_map.zig deleted file mode 100644 index c0c675b..0000000 --- a/zig-ecs/src/ecs/type_map.zig +++ /dev/null @@ -1,53 +0,0 @@ -const std = @import("std"); -const utils = @import("utils.zig"); - -pub const TypeMap = struct { - map: std.AutoHashMap(u32, u8), - counter: u8 = 0, - - pub fn init(allocator: *std.mem.Allocator) TypeMap { - return TypeMap{ - .map = std.AutoHashMap(u32, u8).init(allocator), - }; - } - - pub fn deinit(self: TypeMap) void { - self.map.deinit(); - } - - pub fn contains(self: TypeMap, comptime T: type) bool { - return self.map.contains(@truncate(u32, utils.typeHash(T))); - } - - /// gets the value for T. It MUST exist if you use this method to get it. - pub fn get(self: *TypeMap, comptime T: type) u8 { - return self.map.get(@truncate(u32, utils.typeHash(T))).?.value; - } - - /// gets the value for T if it exists. If it doesnt, it is registered and the value returned. - pub fn getOrPut(self: *TypeMap, comptime T: type, type_id: *u8) bool { - // TODO: is it safe to truncate to u32 here? - var res = self.map.getOrPut(@truncate(u32, utils.typeHash(T))) catch unreachable; - if (!res.found_existing) { - res.kv.value = self.counter; - self.counter += 1; - } - type_id.* = res.kv.value; - return res.found_existing; - } -}; - -test "TypeMap" { - var map = TypeMap.init(std.testing.allocator); - defer map.deinit(); - - var type_id: u8 = undefined; - _ = map.getOrPut(usize, &type_id); - std.testing.expectEqual(@as(u8, 0), type_id); - - _ = map.getOrPut(f32, &type_id); - std.testing.expectEqual(@as(u8, 1), type_id); - - _ = map.getOrPut(usize, &type_id); - std.testing.expectEqual(@as(u8, 0), type_id); -} diff --git a/zig-ecs/src/ecs/utils.zig b/zig-ecs/src/ecs/utils.zig index 2269f3c..2814cf8 100644 --- a/zig-ecs/src/ecs/utils.zig +++ b/zig-ecs/src/ecs/utils.zig @@ -1,3 +1,4 @@ +const std = @import("std"); /// sorts items using lessThan and keeps sub_items with the same sort pub fn sortSub(comptime T1: type, comptime T2: type, items: []T1, sub_items: []T2, lessThan: fn (lhs: T1, rhs: T1) bool) void { @@ -16,12 +17,16 @@ pub fn sortSub(comptime T1: type, comptime T2: type, items: []T1, sub_items: []T } /// comptime string hashing for the type names -pub fn typeHash(comptime T: type) comptime_int { - return stringHash(@typeName(T)); +pub fn typeId(comptime T: type) u32 { + return hashString(@typeName(T)); } -/// comptime string hashing, djb2 by Dan Bernstein -pub fn stringHash(comptime str: []const u8) comptime_int { +pub fn hashString(comptime str: []const u8) u32 { + return @truncate(u32, std.hash.Wyhash.hash(0, str)); +} + +/// comptime string hashing, djb2 by Dan Bernstein. Fails on large strings. +pub fn hashStringDjb2(comptime str: []const u8) comptime_int { var hash: comptime_int = 5381; for (str) |c| { hash = ((hash << 5) + hash) + @intCast(comptime_int, c); diff --git a/zig-ecs/src/ecs/views.zig b/zig-ecs/src/ecs/views.zig index 0522549..481112a 100644 --- a/zig-ecs/src/ecs/views.zig +++ b/zig-ecs/src/ecs/views.zig @@ -57,7 +57,7 @@ pub fn MultiView(comptime n_includes: usize, comptime n_excludes: usize) type { entities: *const []Entity, pub fn init(view: *Self) Iterator { - const ptr = view.registry.components.getValue(@intCast(u8, view.type_ids[0])).?; + const ptr = view.registry.components.getValue(view.type_ids[0]).?; return .{ .view = view, .entities = @intToPtr(*Storage(u8), ptr).data(), @@ -72,7 +72,7 @@ pub fn MultiView(comptime n_includes: usize, comptime n_excludes: usize) type { // entity must be in all other Storages for (it.view.type_ids) |tid| { - const ptr = it.view.registry.components.getValue(@intCast(u8, tid)).?; + const ptr = it.view.registry.components.getValue(tid).?; if (!@intToPtr(*Storage(u1), ptr).contains(entity)) { break :blk; } @@ -80,7 +80,7 @@ pub fn MultiView(comptime n_includes: usize, comptime n_excludes: usize) type { // entity must not be in all other excluded Storages for (it.view.exclude_type_ids) |tid| { - const ptr = it.view.registry.components.getValue(@intCast(u8, tid)).?; + const ptr = it.view.registry.components.getValue(tid).?; if (@intToPtr(*Storage(u1), ptr).contains(entity)) { break :blk; } @@ -119,7 +119,7 @@ pub fn MultiView(comptime n_includes: usize, comptime n_excludes: usize) type { // get our component counts in an array so we can sort the type_ids based on how many entities are in each var sub_items: [n_includes]usize = undefined; for (self.type_ids) |tid, i| { - const ptr = self.registry.components.getValue(@intCast(u8, tid)).?; + const ptr = self.registry.components.getValue(tid).?; const store = @intToPtr(*Storage(u8), ptr); sub_items[i] = store.len(); } diff --git a/zig-ecs/src/signals/dispatcher.zig b/zig-ecs/src/signals/dispatcher.zig index f70945c..bfd5526 100644 --- a/zig-ecs/src/signals/dispatcher.zig +++ b/zig-ecs/src/signals/dispatcher.zig @@ -1,17 +1,15 @@ const std = @import("std"); const Sink = @import("sink.zig").Sink; const Signal = @import("signal.zig").Signal; -const TypeMap = @import("../ecs/type_map.zig").TypeMap; +const utils = @import("../ecs/utils.zig"); pub const Dispatcher = struct { - typemap: TypeMap, - signals: std.AutoHashMap(u8, usize), + signals: std.AutoHashMap(u32, usize), allocator: *std.mem.Allocator, pub fn init(allocator: *std.mem.Allocator) Dispatcher { - return Dispatcher { - .typemap = TypeMap.init(allocator), - .signals = std.AutoHashMap(u8, usize).init(allocator), + return Dispatcher{ + .signals = std.AutoHashMap(u32, usize).init(allocator), .allocator = allocator, }; } @@ -24,21 +22,19 @@ pub const Dispatcher = struct { signal.deinit(); } - self.typemap.deinit(); self.signals.deinit(); } fn assure(self: *Dispatcher, comptime T: type) *Signal(T) { - var type_id: u8 = undefined; - if (!self.typemap.getOrPut(T, &type_id)) { - var signal = Signal(T).create(self.allocator); - var signal_ptr = @ptrToInt(signal); - _ = self.signals.put(type_id, signal_ptr) catch unreachable; - return signal; + var type_id = utils.typeId(T); + if (self.signals.get(type_id)) |kv| { + return @intToPtr(*Signal(T), kv.value); } - const ptr = self.signals.getValue(type_id).?; - return @intToPtr(*Signal(T), ptr); + var signal = Signal(T).create(self.allocator); + var signal_ptr = @ptrToInt(signal); + _ = self.signals.put(type_id, signal_ptr) catch unreachable; + return signal; } pub fn sink(self: *Dispatcher, comptime T: type) Sink(T) { diff --git a/zig-ecs/src/tests.zig b/zig-ecs/src/tests.zig index 8cd5be4..6896914 100644 --- a/zig-ecs/src/tests.zig +++ b/zig-ecs/src/tests.zig @@ -6,7 +6,6 @@ comptime { _ = @import("ecs/entity.zig"); _ = @import("ecs/handles.zig"); _ = @import("ecs/sparse_set.zig"); - _ = @import("ecs/type_map.zig"); _ = @import("ecs/views.zig"); _ = @import("ecs/groups.zig");