Thanks for using Compiler Explorer
Sponsors
Jakt
C++
Ada
Analysis
Android Java
Android Kotlin
Assembly
C
C3
Carbon
C++ (Circle)
CIRCT
Clean
CMake
CMakeScript
COBOL
C++ for OpenCL
MLIR
Cppx
Cppx-Blue
Cppx-Gold
Cpp2-cppfront
Crystal
C#
CUDA C++
D
Dart
Elixir
Erlang
Fortran
F#
Go
Haskell
HLSL
Hook
Hylo
ispc
Java
Julia
Kotlin
LLVM IR
LLVM MIR
Modula-2
Nim
Objective-C
Objective-C++
OCaml
OpenCL C
Pascal
Pony
Python
Racket
Ruby
Rust
Snowball
Scala
Solidity
Spice
Swift
LLVM TableGen
Toit
TypeScript Native
V
Vala
Visual Basic
Zig
Javascript
GIMPLE
zig source #1
Output
Compile to binary object
Link to binary
Execute the code
Intel asm syntax
Demangle identifiers
Verbose demangling
Filters
Unused labels
Library functions
Directives
Comments
Horizontal whitespace
Debug intrinsics
Compiler
zig 0.10.0
zig 0.11.0
zig 0.12.0
zig 0.2.0
zig 0.3.0
zig 0.4.0
zig 0.5.0
zig 0.6.0
zig 0.7.0
zig 0.7.1
zig 0.8.0
zig 0.9.0
zig trunk
Options
Source code
const std = @import("std"); const Allocator = std.mem.Allocator; pub fn Ecs(comptime Components: []const type) type { return struct { const Self = @This(); pub const QueryEntitiesIterator = struct { iter_idx: usize, valid_it: std.bit_set.DynamicBitSet.Iterator(.{}), tag_mask: u32, tags: []u32, pub fn next(self: *@This()) ?u32 { const tag_mask = self.tag_mask; while (self.valid_it.next()) |idx| { if (self.tags[idx] & tag_mask == tag_mask) { return @intCast(u32, idx); } } return null; } }; // Use the largest align of all Components for all. // component arrays const max_alignment = blk: { var a = 0; for (Components) |c| { a = std.math.max(a, @alignOf(c)); } break :blk a; }; fn findSlot(comptime C: type) usize { inline for (Components, 0..) |c, i| { if (c == C) { return i; } } @compileError("unknown component type: " ++ @typeName(C)); } fn compTagBits(comptime C: type) u32 { inline for (Components, 0..) |c, i| { if (c == C) { return 1 << i; } } @compileError("unhandled component type " ++ @typeName(C)); } allocator: Allocator, bytes: [*]align(max_alignment) u8 = undefined, len: usize = 0, soa_ptrs: [Components.len][*]align(max_alignment) u8, // validity and component masks valid: std.DynamicBitSet, tags: []u32, // `sizes.bytes` is an array of @sizeOf each S field. Sorted by alignment, descending. // `sizes.comps` is an array mapping from `sizes.bytes` array index to component index. const sizes = blk: { const Data = struct { size: usize, size_index: usize, alignment: usize, }; var data: [Components.len]Data = undefined; for (Components, 0..) |comp, i| { data[i] = .{ .size = @sizeOf(comp), .size_index = i, .alignment = @alignOf(comp), }; } const Sort = struct { fn lessThan(context: void, lhs: Data, rhs: Data) bool { _ = context; return lhs.alignment > rhs.alignment; } }; std.sort.sort(Data, &data, {}, Sort.lessThan); var sizes_bytes: [Components.len]usize = undefined; var comp_indexes: [Components.len]usize = undefined; for (data, 0..) |elem, i| { sizes_bytes[i] = elem.size; comp_indexes[i] = elem.size_index; } break :blk .{ .bytes = sizes_bytes, .comps = comp_indexes, }; }; fn capacityInBytes(capacity: usize) usize { comptime var elem_bytes: usize = 0; inline for (sizes.bytes) |size| elem_bytes += size; return elem_bytes * capacity; } fn allocatedBytes(self: Self) []align(max_alignment) u8 { return self.bytes[0..capacityInBytes(self.len)]; } pub fn init(allocator: Allocator, len: usize) !Self { var mem = try allocator.alignedAlloc(u8, max_alignment, capacityInBytes(len)); var ptr: [*]u8 = mem.ptr; var soa_ptrs: [Components.len][*]align(max_alignment) u8 = undefined; for (sizes.bytes, sizes.comps) |comp_size, i| { soa_ptrs[i] = @alignCast(max_alignment, ptr); ptr += comp_size * len; } var valid = try std.DynamicBitSet.initEmpty(allocator, len); var tags = try allocator.alloc(u32, len); std.mem.set(@TypeOf(tags[0]), tags, 0); return .{ .allocator = allocator, .bytes = mem.ptr, .len = len, .soa_ptrs = soa_ptrs, .valid = valid, .tags = tags, }; } fn items(self: *Self, comptime C: type) []C { comptime var comp_idx = findSlot(C); var ptr = @ptrCast([*]C, self.soa_ptrs[comp_idx]); return ptr[0..self.len]; } pub fn newEntity(self: *Self) u32 { var unset_it = self.valid.iterator(.{ .kind = .unset }); const idx = while (unset_it.next()) |idx| { break idx; } else { @panic("out of entities -- shouldn't get here"); }; self.valid.set(idx); self.tags[idx] = 0; return @intCast(u32, idx); } pub fn removeEntity(self: *Self, id: u32) void { self.valid.unset(id); self.tags[id] = 0; } pub fn get(self: *Self, comptime C: type, idx: usize) ?*C { const mask = compTagBits(C); if (self.valid.isSet(idx) and (self.tags[idx] & mask) != 0) { return &self.items(C)[idx]; } return null; } pub fn set(self: *Self, idx: usize, c: anytype) void { self.valid.set(idx); self.tags[idx] |= compTagBits(@TypeOf(c)); self.items(@TypeOf(c))[idx] = c; } pub fn queryEntities(self: *Self, comptime Comps: []const type) QueryEntitiesIterator { var tag_mask: u32 = 0; comptime { for (Comps) |c| { tag_mask |= compTagBits(c); } } return .{ .valid_it = self.valid.iterator(.{}), .iter_idx = 0, .tag_mask = tag_mask, .tags = self.tags, }; } pub fn deinit(self: *Self) void { self.valid.deinit(); self.allocator.free(self.tags); self.allocator.free(self.allocatedBytes()); } }; } const Position = struct { pos: @Vector(2, f32), }; const Age = packed struct { age: u16 }; const AgePosEcs = Ecs(&[_]type{ Age, Position }); export fn setTest(ecs: *AgePosEcs, idx: usize, p: @Vector(2, f32)) void { ecs.set(idx, Position{ .pos = p }); ecs.set(idx, Age{ .age = 15 }); } export fn getTest(ecs: *AgePosEcs, idx: usize) ?*Age { return ecs.get(Age, idx); } export fn iterateEntities(ecs: *AgePosEcs, out: [*]u32) void { var it = ecs.queryEntities(&[_]type{Position}); var idx: usize = 0; while (it.next()) |c| { out[idx] = c; } }
Become a Patron
Sponsor on GitHub
Donate via PayPal
Source on GitHub
Mailing list
Installed libraries
Wiki
Report an issue
How it works
Contact the author
CE on Mastodon
About the author
Statistics
Changelog
Version tree