Make Path parts sentinel terminated

This means Entity.init doesn't need to allocate.
main
copygirl 1 year ago
parent f58c71016b
commit 09b018ecec
  1. 22
      src/entity.zig
  2. 45
      src/path.zig

@ -59,9 +59,9 @@ pub fn Entity(comptime ctx: anytype) type {
// Set to modify an existing entity. // Set to modify an existing entity.
id: ?Self = null, id: ?Self = null,
/// Set to specify the entity's name. /// Set to specify the entity's name.
name: ?[]const u8 = null, name: ?[:0]const u8 = null,
/// Set to specify the entity's symbol. /// Set to specify the entity's symbol.
symbol: ?[]const u8 = null, symbol: ?[:0]const u8 = null,
/// Whether to use a small id when generating one. /// Whether to use a small id when generating one.
/// These are typically reserved for components. /// These are typically reserved for components.
use_low_id: bool = false, use_low_id: bool = false,
@ -95,9 +95,6 @@ pub fn Entity(comptime ctx: anytype) type {
var name = config.name; var name = config.name;
var scope = if (config.parent) |p| p.raw else null; var scope = if (config.parent) |p| p.raw else null;
// TODO: Use an allocator that's well-fitted for super short-lived allocations.
const alloc = flecs.allocator;
if (config.path) |path| { if (config.path) |path| {
if (path.absolute) { if (path.absolute) {
// Specifying parent with an absolute path is invalid. // Specifying parent with an absolute path is invalid.
@ -131,11 +128,9 @@ pub fn Entity(comptime ctx: anytype) type {
scope = found; scope = found;
}, },
.name => |n| { .name => |n| {
const nameZ = try alloc.dupeZ(u8, n); const found = c.ecs_lookup_child(world.raw, parent, n.ptr);
defer alloc.free(nameZ);
const found = c.ecs_lookup_child(world.raw, parent, nameZ.ptr);
if (found == 0) { if (found == 0) {
var desc = std.mem.zeroInit(c.ecs_entity_desc_t, .{ .sep = "".ptr, .name = nameZ.ptr }); var desc = std.mem.zeroInit(c.ecs_entity_desc_t, .{ .sep = "".ptr, .name = n.ptr });
desc.add[0] = c.ecs_pair(c.EcsChildOf, parent); desc.add[0] = c.ecs_pair(c.EcsChildOf, parent);
scope = c.ecs_entity_init(world.raw, &desc); scope = c.ecs_entity_init(world.raw, &desc);
if (scope == 0) return err.getLastErrorOrUnknown(); if (scope == 0) return err.getLastErrorOrUnknown();
@ -150,16 +145,11 @@ pub fn Entity(comptime ctx: anytype) type {
const previous = if (scope) |s| world.setScope(s) else null; const previous = if (scope) |s| world.setScope(s) else null;
defer _ = if (scope != null) world.setScope(previous); defer _ = if (scope != null) world.setScope(previous);
const nameZ = if (name) |n| try alloc.dupeZ(u8, n) else null;
defer if (nameZ) |n| alloc.free(n);
const symbolZ = if (config.symbol) |s| try alloc.dupeZ(u8, s) else null;
defer if (symbolZ) |s| alloc.free(s);
var desc = std.mem.zeroInit(c.ecs_entity_desc_t, .{ var desc = std.mem.zeroInit(c.ecs_entity_desc_t, .{
.sep = "".ptr, // Disable tokenization. .sep = "".ptr, // Disable tokenization.
.id = if (id) |i| i else 0, .id = if (id) |i| i else 0,
.name = if (nameZ) |n| n.ptr else null, .name = if (name) |n| n.ptr else null,
.symbol = if (symbolZ) |s| s.ptr else null, .symbol = if (config.symbol) |s| s.ptr else null,
.use_low_id = config.use_low_id, .use_low_id = config.use_low_id,
}); });

@ -24,7 +24,7 @@ pub const Path = struct {
/// Represents an `Entity` in a `Path`, either by name or its numeric id. /// Represents an `Entity` in a `Path`, either by name or its numeric id.
pub const EntityPart = union(enum) { pub const EntityPart = union(enum) {
id: u32, id: u32,
name: []const u8, name: [:0]const u8,
}; };
/// Format used to parse and stringify `Path`s. /// Format used to parse and stringify `Path`s.
@ -62,32 +62,29 @@ pub const Path = struct {
/// to this function, as they aren't cloned and ownership stays the same. /// to this function, as they aren't cloned and ownership stays the same.
/// In many cases, the lifetime of `Path`s is relatively short. When this /// In many cases, the lifetime of `Path`s is relatively short. When this
/// is not the case, it's recommended to `.clone()` the path after creation. /// is not the case, it's recommended to `.clone()` the path after creation.
pub fn buildParts(parts: anytype) t: { pub fn buildParts(parts: anytype) [numElements(parts)]EntityPart {
if (!std.meta.trait.isTuple(@TypeOf(parts))) if (comptime !std.meta.trait.isTuple(@TypeOf(parts)))
@compileError("Expected tuple, got '" ++ @typeName(@TypeOf(parts)) ++ "'"); @compileError("Expected tuple, got '" ++ @typeName(@TypeOf(parts)) ++ "'");
const len = @typeInfo(@TypeOf(parts)).Struct.fields.len; var result: [numElements(parts)]EntityPart = undefined;
break :t [len]EntityPart;
} {
const len = @typeInfo(@TypeOf(parts)).Struct.fields.len;
var result: [len]EntityPart = undefined;
inline for (&result, parts) |*res, part| { inline for (&result, parts) |*res, part| {
const msg = "Expected '[]const u8' or 'u32', got '" ++ @typeName(@TypeOf(part)) ++ "'"; res.* = if (comptime std.meta.trait.isZigString(@TypeOf(part)))
res.* = switch (@typeInfo(@TypeOf(part))) { .{ .name = part }
.Pointer => |p| switch (p.size) { else switch (@typeInfo(@TypeOf(part))) {
.One => switch (@typeInfo(p.child)) {
.Array => |a| if (a.child == u8) .{ .name = part } else @compileError(msg),
else => @compileError(msg),
},
.Slice => if (p.child == u8) .{ .name = part } else @compileError(msg),
else => @compileError(msg),
},
.Int, .ComptimeInt => .{ .id = part }, .Int, .ComptimeInt => .{ .id = part },
else => @compileError(msg), else => @compileError("Expected '[:0]const u8' or 'u32', got '" ++ @typeName(@TypeOf(part)) ++ "'"),
}; };
} }
return result; return result;
} }
/// Returns the number of elements of the specified tuple type.
fn numElements(parts: anytype) usize {
return if (comptime std.meta.trait.isTuple(@TypeOf(parts)))
@typeInfo(@TypeOf(parts)).Struct.fields.len
else
0;
}
/// Creates a new `Path` from the specified parts. /// Creates a new `Path` from the specified parts.
/// ///
/// The resulting path does not own any of the given slices. /// The resulting path does not own any of the given slices.
@ -103,8 +100,9 @@ pub const Path = struct {
/// resulting path will be absolute. The rest of the string will be split /// resulting path will be absolute. The rest of the string will be split
/// by the specified seperator, becoming its parts. /// by the specified seperator, becoming its parts.
/// ///
/// The parts array will be allocated with the specified `Allocator` and is /// This function will allocate duplicate strings taken from the specified
/// owned by the resulting path. `deinit()` must be called to free it. /// source `path`, to ensure they are sentinel-terminated. The resulting
/// `Path` takes ownership of these.
pub fn fromString(path: []const u8, options: ?FormatOptions, alloc: Allocator) !Path { pub fn fromString(path: []const u8, options: ?FormatOptions, alloc: Allocator) !Path {
if (path.len == 0) return error.MustNotBeEmpty; if (path.len == 0) return error.MustNotBeEmpty;
const opt = options orelse FormatOptions.default; const opt = options orelse FormatOptions.default;
@ -129,13 +127,14 @@ pub const Path = struct {
parts[i] = if (parseNumericId(str)) |id| parts[i] = if (parseNumericId(str)) |id|
.{ .id = id } .{ .id = id }
else else
.{ .name = str }; .{ .name = try alloc.dupeZ(u8, str) };
return .{ return .{
.absolute = absolute, .absolute = absolute,
.parts = parts, .parts = parts,
.alloc = alloc, .alloc = alloc,
.owns_array = true, .owns_array = true,
.owns_parts = true,
}; };
} }
@ -202,7 +201,7 @@ pub const Path = struct {
for (parts) |*part| { for (parts) |*part| {
if (part.* == .name) if (part.* == .name)
part.* = .{ .name = try alloc.dupe(u8, part.name) }; part.* = .{ .name = try alloc.dupeZ(u8, part.name) };
num_allocated += 1; num_allocated += 1;
} }

Loading…
Cancel
Save