High-level wrapper around Flecs, a powerful ECS (Entity Component System) library, written in Zig language
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

100 lines
3.4 KiB

const std = @import("std");
const Allocator = std.mem.Allocator;
pub usingnamespace @import("./entity.zig");
pub usingnamespace @import("./id.zig");
pub usingnamespace @import("./iter.zig");
pub usingnamespace @import("./pair.zig");
pub usingnamespace @import("./path.zig");
pub usingnamespace @import("./world.zig");
pub const c = @import("./c.zig");
pub fn Context(comptime ctx: anytype) type {
return struct {
pub const Entity = @import("./entity.zig").Entity(ctx);
pub const Id = @import("./id.zig").Id(ctx);
pub const Iter = @import("./iter.zig").Iter(ctx);
pub const Pair = @import("./pair.zig").Pair(ctx);
pub const World = @import("./world.zig").World(ctx);
pub const Path = @import("./path.zig").Path;
};
}
pub fn Lookup(comptime ctx: anytype, comptime T: type) type {
_ = .{ ctx, T }; // Only necessary to create a unique type.
return struct {
pub var id: c.ecs_entity_t = 0;
};
}
pub var is_initialized = false;
pub var allocator: Allocator = undefined;
/// Ensures that some global settings are set up to interface with Flecs.
/// Must be called before creating a `World`. Subsequent calls are a no-op.
pub fn init(alloc: Allocator) void {
if (is_initialized) {
std.debug.assert(allocator.ptr == alloc.ptr);
return;
}
is_initialized = true;
allocator = alloc;
c.ecs_os_api.malloc_ = flecsMalloc;
c.ecs_os_api.realloc_ = flecsRealloc;
c.ecs_os_api.calloc_ = flecsCalloc;
c.ecs_os_api.free_ = flecsFree;
}
fn flecsMalloc(size: i32) callconv(.C) ?*anyopaque {
return allocLengthEncodedSlice(size, null).ptr;
}
fn flecsRealloc(ptr: ?*anyopaque, size: i32) callconv(.C) ?*anyopaque {
return allocLengthEncodedSlice(size, sliceFromPtr(ptr.?)).ptr;
}
fn flecsCalloc(size: i32) callconv(.C) ?*anyopaque {
var slice = allocLengthEncodedSlice(size, null);
@memset(slice, 0);
return slice.ptr;
}
fn flecsFree(ptr: ?*anyopaque) callconv(.C) void {
const slice = sliceFromPtr(ptr.?);
allocator.free(slice);
}
/// Reserves an additional `@sizeOf(i32)` bytes, which is used to store the
/// length so we can use a simple pointer offset to "encode" the full slice
/// information (including length) into just a single pointer.
///
/// Optionally allows passing a slice to be reallocated into this new slice.
/// The `old_slice` must be the full slice as returned by `sliceFromPtr(...)`.
///
/// Returns the pointer from the offset where the actual data is stored.
/// This allows manipulating the contents, such as zeroing it out.
fn allocLengthEncodedSlice(size: i32, old_slice: ?[]u8) []u8 {
const slice_len = @as(usize, @intCast(size)) + @sizeOf(i32);
const slice = if (old_slice) |old|
allocator.realloc(old, slice_len) catch @panic("OOM")
else
allocator.allocWithOptions(u8, slice_len, @alignOf(i32), null) catch @panic("OOM");
@as(*i32, @alignCast(@ptrCast(slice.ptr))).* = size;
return slice[@sizeOf(i32)..];
}
/// Recovers the original slice that was allocated by `allocSlice` to get the
/// specified pointer. Returns the full slice including the "encoded" length.
fn sliceFromPtr(ptr: *anyopaque) []u8 {
const slice_ptr = @as([*]align(@alignOf(i32)) u8, @alignCast(@ptrCast(ptr))) - @sizeOf(i32);
const slice_len: usize = @intCast(@as(*i32, @ptrCast(slice_ptr)).*);
return slice_ptr[0..(slice_len + @sizeOf(i32))];
}
test {
std.testing.refAllDecls(@This());
}