Fix Flecs OS API, move to separate file

main
copygirl 6 months ago
parent 12280219cb
commit dbfe13af58
  1. 72
      src/main.zig
  2. 78
      src/os_api.zig
  3. 36
      src/test/flecs/entity.zig
  4. 20
      src/world.zig

@ -1,5 +1,5 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const os_api = @import("./os_api.zig");
// Meant for internal use, but exposed since flecs-zig-ble
// doesn't wrap nearly enough of Flecs' available features.
@ -19,70 +19,14 @@ pub usingnamespace @import("./iter.zig");
pub usingnamespace @import("./pair.zig");
pub usingnamespace @import("./world.zig");
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 {
const 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))];
/// Must be called before creating a `World`. Subsequent calls are a no-op,
/// but the same allocator must be used.
pub fn init(allocator: std.mem.Allocator) void {
if (!os_api.is_setup)
os_api.setup(allocator)
else if (allocator.ptr != os_api.allocator.ptr)
std.debug.panic("init called multiple times, but allocator does not match", .{});
}
test {

@ -0,0 +1,78 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const c = @import("./c.zig");
pub var is_setup = false;
pub var allocator: Allocator = undefined;
pub fn setup(allocator_: Allocator) void {
if (is_setup) std.debug.panic("setup must only be called once", .{});
is_setup = true;
allocator = allocator_;
c.ecs_os_set_api_defaults();
var os_api = c.ecs_os_api;
os_api.malloc_ = flecsMalloc;
os_api.realloc_ = flecsRealloc;
os_api.calloc_ = flecsCalloc;
os_api.free_ = flecsFree;
c.ecs_os_set_api(&os_api);
_ = c.ecs_log_set_level(-1); // No tracing.
}
fn flecsMalloc(size: i32) callconv(.C) ?*anyopaque {
if (size == 0) return null;
return allocLengthEncodedSlice(size, null).ptr;
}
fn flecsRealloc(ptr: ?*anyopaque, size: i32) callconv(.C) ?*anyopaque {
if (size == 0) {
flecsFree(ptr);
return null;
} else {
const old = if (ptr) |p| sliceFromPtr(p) else null;
return allocLengthEncodedSlice(size, old).ptr;
}
}
fn flecsCalloc(size: i32) callconv(.C) ?*anyopaque {
if (size == 0) return null;
const slice = allocLengthEncodedSlice(size, null);
@memset(slice, 0);
return slice.ptr;
}
fn flecsFree(ptr: ?*anyopaque) callconv(.C) void {
if (ptr) |p| {
const slice = sliceFromPtr(p);
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: ?[]align(@sizeOf(i32)) u8) []u8 {
const slice_len = @sizeOf(i32) + @as(usize, @intCast(size));
const slice = if (old_slice) |old|
allocator.realloc(old, slice_len) catch @panic("OOM")
else
allocator.allocWithOptions(u8, slice_len, @sizeOf(i32), null) catch @panic("OOM");
@as([*]i32, @ptrCast(slice.ptr))[0] = 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) []align(@sizeOf(i32)) u8 {
const slice_ptr = @as([*]align(@sizeOf(i32)) u8, @alignCast(@ptrCast(ptr))) - @sizeOf(i32);
const slice_len = @as([*]i32, @ptrCast(slice_ptr))[0];
return slice_ptr[0..(@sizeOf(i32) + @as(usize, @intCast(slice_len)))];
}

@ -36,7 +36,7 @@ test "Entity_init_id_name" {
const e = try world.entity(.{ .name = "foo" }, .{});
try expect.equal("foo", e.name());
const path = try e.path(flecszigble.allocator);
const path = try e.path(alloc);
defer path.deinit();
try expect.fmt("foo", "{}", .{path});
}
@ -51,7 +51,7 @@ test "Entity_init_id_path" {
const e = try world.entity(.{ .path = e_path }, .{});
try expect.equal("child", e.name());
const path = try e.path(flecszigble.allocator);
const path = try e.path(alloc);
defer path.deinit();
try expect.fmt("parent.child", "{}", .{path});
}
@ -116,7 +116,7 @@ test "Entity_init_id_name_w_scope" {
try expect.true(e.has(.{ ChildOf, scope }));
try expect.equal("child", e.name());
const path = try e.path(flecszigble.allocator);
const path = try e.path(alloc);
defer path.deinit();
try expect.fmt("parent.child", "{}", .{path});
}
@ -138,7 +138,7 @@ test "Entity_init_id_path_w_scope" {
try expect.equal("grandchild", e.name());
const path = try e.path(flecszigble.allocator);
const path = try e.path(alloc);
defer path.deinit();
try expect.fmt("parent.child.grandchild", "{}", .{path});
}
@ -154,13 +154,13 @@ test "Entity_init_id_fullpath_w_scope" {
_ = world.setScope(scope);
try expect.equal(scope, world.scope());
const p = try Path.fromString("::parent.child.grandchild", .{ .root_sep = "::", .sep = "." }, flecszigble.allocator);
const p = try Path.fromString("::parent.child.grandchild", .{ .root_sep = "::", .sep = "." }, alloc);
defer p.deinit();
const e = try world.entity(.{ .path = p }, .{});
try expect.equal("grandchild", e.name());
const path = try e.path(flecszigble.allocator);
const path = try e.path(alloc);
defer path.deinit();
try expect.fmt("parent.child.grandchild", "{}", .{path});
}
@ -176,7 +176,7 @@ test "Entity_init_id_fullpath_w_scope_existing" {
_ = world.setScope(scope);
try expect.equal(scope, world.scope());
const p = try Path.fromString("::parent.child.grandchild", .{ .root_sep = "::", .sep = "." }, flecszigble.allocator);
const p = try Path.fromString("::parent.child.grandchild", .{ .root_sep = "::", .sep = "." }, alloc);
defer p.deinit();
const e = try world.entity(.{ .path = p }, .{});
@ -185,7 +185,7 @@ test "Entity_init_id_fullpath_w_scope_existing" {
try expect.equal("grandchild", e.name());
const path = try e.path(flecszigble.allocator);
const path = try e.path(alloc);
defer path.deinit();
try expect.fmt("parent.child.grandchild", "{}", .{path});
}
@ -203,7 +203,7 @@ test "Entity_init_id_name_1_comp" {
try expect.true(e.has(TagA));
try expect.equal("foo", e.name());
const path = try e.path(flecszigble.allocator);
const path = try e.path(alloc);
defer path.deinit();
try expect.fmt("foo", "{}", .{path});
}
@ -223,7 +223,7 @@ test "Entity_init_id_name_2_comp" {
try expect.true(e.has(TagA));
try expect.equal("foo", e.name());
const path = try e.path(flecszigble.allocator);
const path = try e.path(alloc);
defer path.deinit();
try expect.fmt("foo", "{}", .{path});
}
@ -249,7 +249,7 @@ test "Entity_init_id_name_2_comp_w_scope" {
try expect.true(e.has(TagA));
try expect.equal("child", e.name());
const path = try e.path(flecszigble.allocator);
const path = try e.path(alloc);
defer path.deinit();
try expect.fmt("parent.child", "{}", .{path});
}
@ -292,12 +292,12 @@ test "Entity_init_id_path_w_sep" {
var world = try World.initMinimal();
defer world.deinit();
const p = try Path.fromString("parent::child", .{ .root_sep = null, .sep = "::" }, flecszigble.allocator);
const p = try Path.fromString("parent::child", .{ .root_sep = null, .sep = "::" }, alloc);
defer p.deinit();
const e = try world.entity(.{ .path = p }, .{});
try expect.equal("child", e.name());
const path = try e.path(flecszigble.allocator);
const path = try e.path(alloc);
defer path.deinit();
try expect.fmt("parent.child", "{}", .{path});
}
@ -343,7 +343,7 @@ test "Entity_find_id_name_w_scope" {
const e = try world.entity(.{ .name = "child" }, .{});
try expect.equal("child", e.name());
const path = try e.path(flecszigble.allocator);
const path = try e.path(alloc);
defer path.deinit();
try expect.fmt("parent.child", "{}", .{path});
@ -361,7 +361,7 @@ test "Entity_find_id_path" {
const e = try world.entity(.{ .path = e_path }, .{});
try expect.equal("child", e.name());
const path = try e.path(flecszigble.allocator);
const path = try e.path(alloc);
defer path.deinit();
try expect.fmt("parent.child", "{}", .{path});
@ -385,7 +385,7 @@ test "Entity_find_id_path_w_scope" {
const e = try world.entity(.{ .path = e_path }, .{});
try expect.equal("grandchild", e.name());
const path = try e.path(flecszigble.allocator);
const path = try e.path(alloc);
defer path.deinit();
try expect.fmt("parent.child.grandchild", "{}", .{path});
@ -419,7 +419,7 @@ test "Entity_find_id_name_match_w_scope" {
const e = try world.entity(.{ .name = "child" }, .{});
try expect.equal("child", e.name());
const path = try e.path(flecszigble.allocator);
const path = try e.path(alloc);
defer path.deinit();
try expect.fmt("parent.child", "{}", .{path});
@ -612,7 +612,7 @@ test "Entity_init_w_scope_name" {
const child = try world.entity(.{ .name = "foo" }, .{});
try expect.equal("foo", child.name());
const path = try child.path(flecszigble.allocator);
const path = try child.path(alloc);
defer path.deinit();
try expect.fmt("parent.foo.foo", "{}", .{path});
}

@ -1,5 +1,5 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const os_api = @import("./os_api.zig");
const flecszigble = @import("./main.zig");
const c = @import("./c.zig");
@ -21,24 +21,24 @@ pub fn World(comptime ctx: anytype) type {
raw: *c.ecs_world_t,
pub fn init() !*Self {
std.debug.assert(flecszigble.is_initialized);
var result = try flecszigble.allocator.create(Self);
std.debug.assert(os_api.is_setup);
var result = try os_api.allocator.create(Self);
result.raw = c.ecs_init().?;
try Context.registerFlecsLookups(result);
return result;
}
pub fn initWithArgs(args: [][*:0]const u8) !*Self {
std.debug.assert(flecszigble.is_initialized);
var result = try flecszigble.allocator.create(Self);
std.debug.assert(os_api.is_setup);
var result = try os_api.allocator.create(Self);
result.raw = c.ecs_init_w_args(args.len, args.ptr).?;
try Context.registerFlecsLookups(result);
return result;
}
pub fn initMinimal() !*Self {
std.debug.assert(flecszigble.is_initialized);
var result = try flecszigble.allocator.create(Self);
std.debug.assert(os_api.is_setup);
var result = try os_api.allocator.create(Self);
result.raw = c.ecs_mini().?;
try Context.registerFlecsCoreLookups(result);
return result;
@ -46,7 +46,7 @@ pub fn World(comptime ctx: anytype) type {
pub fn deinit(self: *Self) void {
_ = c.ecs_fini(self.raw);
flecszigble.allocator.destroy(self);
os_api.allocator.destroy(self);
}
pub fn enableRest(self: *Self, port: u16) !void {
@ -212,7 +212,7 @@ pub fn World(comptime ctx: anytype) type {
func: SystemCallback,
pub fn init(world: *Self, callback: SystemCallback) !*SystemCallbackContext {
var result = try flecszigble.allocator.create(SystemCallbackContext);
var result = try os_api.allocator.create(SystemCallbackContext);
result.world = world;
result.func = callback;
return result;
@ -220,7 +220,7 @@ pub fn World(comptime ctx: anytype) type {
fn free(context: ?*anyopaque) callconv(.C) void {
const self: *SystemCallbackContext = @alignCast(@ptrCast(context));
flecszigble.allocator.destroy(self);
os_api.allocator.destroy(self);
}
// FIXME: Dependency loop.

Loading…
Cancel
Save