Files
PixelDefense/engine/libs/flecs/src/entity_name.c

692 lines
16 KiB
C

/**
* @file entity_name.c
* @brief Functions for working with named entities.
*/
#include "private_api.h"
#include <ctype.h>
#define ECS_NAME_BUFFER_LENGTH (64)
static
bool flecs_path_append(
const ecs_world_t *world,
ecs_entity_t parent,
ecs_entity_t child,
const char *sep,
const char *prefix,
ecs_strbuf_t *buf)
{
ecs_poly_assert(world, ecs_world_t);
ecs_assert(sep[0] != 0, ECS_INVALID_PARAMETER, NULL);
ecs_entity_t cur = 0;
const char *name = NULL;
ecs_size_t name_len = 0;
if (child && ecs_is_alive(world, child)) {
cur = ecs_get_target(world, child, EcsChildOf, 0);
if (cur) {
ecs_assert(cur != child, ECS_CYCLE_DETECTED, NULL);
if (cur != parent && (cur != EcsFlecsCore || prefix != NULL)) {
flecs_path_append(world, parent, cur, sep, prefix, buf);
if (!sep[1]) {
ecs_strbuf_appendch(buf, sep[0]);
} else {
ecs_strbuf_appendstr(buf, sep);
}
}
} else if (prefix && prefix[0]) {
if (!prefix[1]) {
ecs_strbuf_appendch(buf, prefix[0]);
} else {
ecs_strbuf_appendstr(buf, prefix);
}
}
const EcsIdentifier *id = ecs_get_pair(
world, child, EcsIdentifier, EcsName);
if (id) {
name = id->value;
name_len = id->length;
}
}
if (name) {
ecs_strbuf_appendstrn(buf, name, name_len);
} else {
ecs_strbuf_appendint(buf, flecs_uto(int64_t, (uint32_t)child));
}
return cur != 0;
}
bool flecs_name_is_id(
const char *name)
{
ecs_assert(name != NULL, ECS_INTERNAL_ERROR, NULL);
if (!isdigit(name[0])) {
return false;
}
ecs_size_t i, length = ecs_os_strlen(name);
for (i = 1; i < length; i ++) {
char ch = name[i];
if (!isdigit(ch)) {
break;
}
}
return i >= length;
}
ecs_entity_t flecs_name_to_id(
const ecs_world_t *world,
const char *name)
{
int64_t result = atoll(name);
ecs_assert(result >= 0, ECS_INTERNAL_ERROR, NULL);
ecs_entity_t alive = ecs_get_alive(world, (ecs_entity_t)result);
if (alive) {
return alive;
} else {
if ((uint32_t)result == (uint64_t)result) {
return (ecs_entity_t)result;
} else {
return 0;
}
}
}
static
ecs_entity_t flecs_get_builtin(
const char *name)
{
if (name[0] == '.' && name[1] == '\0') {
return EcsThis;
} else if (name[0] == '*' && name[1] == '\0') {
return EcsWildcard;
} else if (name[0] == '_' && name[1] == '\0') {
return EcsAny;
} else if (name[0] == '$' && name[1] == '\0') {
return EcsVariable;
}
return 0;
}
static
bool flecs_is_sep(
const char **ptr,
const char *sep)
{
ecs_size_t len = ecs_os_strlen(sep);
if (!ecs_os_strncmp(*ptr, sep, len)) {
*ptr += len;
return true;
} else {
return false;
}
}
static
const char* flecs_path_elem(
const char *path,
const char *sep,
int32_t *len)
{
const char *ptr;
char ch;
int32_t template_nesting = 0;
int32_t count = 0;
for (ptr = path; (ch = *ptr); ptr ++) {
if (ch == '<') {
template_nesting ++;
} else if (ch == '>') {
template_nesting --;
}
ecs_check(template_nesting >= 0, ECS_INVALID_PARAMETER, path);
if (!template_nesting && flecs_is_sep(&ptr, sep)) {
break;
}
count ++;
}
if (len) {
*len = count;
}
if (count) {
return ptr;
} else {
return NULL;
}
error:
return NULL;
}
static
bool flecs_is_root_path(
const char *path,
const char *prefix)
{
if (prefix) {
return !ecs_os_strncmp(path, prefix, ecs_os_strlen(prefix));
} else {
return false;
}
}
static
ecs_entity_t flecs_get_parent_from_path(
const ecs_world_t *world,
ecs_entity_t parent,
const char **path_ptr,
const char *prefix,
bool new_entity)
{
bool start_from_root = false;
const char *path = *path_ptr;
if (flecs_is_root_path(path, prefix)) {
path += ecs_os_strlen(prefix);
parent = 0;
start_from_root = true;
}
if (!start_from_root && !parent && new_entity) {
parent = ecs_get_scope(world);
}
*path_ptr = path;
return parent;
}
static
void flecs_on_set_symbol(ecs_iter_t *it) {
EcsIdentifier *n = ecs_field(it, EcsIdentifier, 1);
ecs_world_t *world = it->world;
int i;
for (i = 0; i < it->count; i ++) {
ecs_entity_t e = it->entities[i];
flecs_name_index_ensure(
&world->symbols, e, n[i].value, n[i].length, n[i].hash);
}
}
void flecs_bootstrap_hierarchy(ecs_world_t *world) {
ecs_observer(world, {
.entity = ecs_entity(world, {.add = {ecs_childof(EcsFlecsInternals)}}),
.filter.terms[0] = {
.id = ecs_pair(ecs_id(EcsIdentifier), EcsSymbol),
.src.flags = EcsSelf
},
.callback = flecs_on_set_symbol,
.events = {EcsOnSet},
.yield_existing = true
});
}
/* Public functions */
void ecs_get_path_w_sep_buf(
const ecs_world_t *world,
ecs_entity_t parent,
ecs_entity_t child,
const char *sep,
const char *prefix,
ecs_strbuf_t *buf)
{
ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_check(buf != NULL, ECS_INVALID_PARAMETER, NULL);
world = ecs_get_world(world);
if (child == EcsWildcard) {
ecs_strbuf_appendch(buf, '*');
return;
}
if (child == EcsAny) {
ecs_strbuf_appendch(buf, '_');
return;
}
if (!sep) {
sep = ".";
}
if (!child || parent != child) {
flecs_path_append(world, parent, child, sep, prefix, buf);
} else {
ecs_strbuf_appendstrn(buf, "", 0);
}
error:
return;
}
char* ecs_get_path_w_sep(
const ecs_world_t *world,
ecs_entity_t parent,
ecs_entity_t child,
const char *sep,
const char *prefix)
{
ecs_strbuf_t buf = ECS_STRBUF_INIT;
ecs_get_path_w_sep_buf(world, parent, child, sep, prefix, &buf);
return ecs_strbuf_get(&buf);
}
ecs_entity_t ecs_lookup_child(
const ecs_world_t *world,
ecs_entity_t parent,
const char *name)
{
ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL);
world = ecs_get_world(world);
if (flecs_name_is_id(name)) {
ecs_entity_t result = flecs_name_to_id(world, name);
if (result && ecs_is_alive(world, result)) {
if (parent && !ecs_has_pair(world, result, EcsChildOf, parent)) {
return 0;
}
return result;
}
}
ecs_id_t pair = ecs_childof(parent);
ecs_hashmap_t *index = flecs_id_name_index_get(world, pair);
if (index) {
return flecs_name_index_find(index, name, 0, 0);
} else {
return 0;
}
error:
return 0;
}
ecs_entity_t ecs_lookup(
const ecs_world_t *world,
const char *name)
{
if (!name) {
return 0;
}
ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL);
world = ecs_get_world(world);
ecs_entity_t e = flecs_get_builtin(name);
if (e) {
return e;
}
if (flecs_name_is_id(name)) {
return flecs_name_to_id(world, name);
}
e = flecs_name_index_find(&world->aliases, name, 0, 0);
if (e) {
return e;
}
return ecs_lookup_child(world, 0, name);
error:
return 0;
}
ecs_entity_t ecs_lookup_symbol(
const ecs_world_t *world,
const char *name,
bool lookup_as_path,
bool recursive)
{
if (!name) {
return 0;
}
ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL);
world = ecs_get_world(world);
ecs_entity_t e = flecs_name_index_find(&world->symbols, name, 0, 0);
if (e) {
return e;
}
if (lookup_as_path) {
return ecs_lookup_path_w_sep(world, 0, name, ".", NULL, recursive);
}
error:
return 0;
}
ecs_entity_t ecs_lookup_path_w_sep(
const ecs_world_t *world,
ecs_entity_t parent,
const char *path,
const char *sep,
const char *prefix,
bool recursive)
{
if (!path) {
return 0;
}
ecs_check(world != NULL, ECS_INTERNAL_ERROR, NULL);
const ecs_world_t *stage = world;
world = ecs_get_world(world);
ecs_entity_t e = flecs_get_builtin(path);
if (e) {
return e;
}
e = flecs_name_index_find(&world->aliases, path, 0, 0);
if (e) {
return e;
}
char buff[ECS_NAME_BUFFER_LENGTH];
const char *ptr, *ptr_start;
char *elem = buff;
int32_t len, size = ECS_NAME_BUFFER_LENGTH;
ecs_entity_t cur;
bool lookup_path_search = false;
const ecs_entity_t *lookup_path = ecs_get_lookup_path(stage);
const ecs_entity_t *lookup_path_cur = lookup_path;
while (lookup_path_cur && *lookup_path_cur) {
lookup_path_cur ++;
}
if (!sep) {
sep = ".";
}
parent = flecs_get_parent_from_path(stage, parent, &path, prefix, true);
if (!sep[0]) {
return ecs_lookup_child(world, parent, path);
}
retry:
cur = parent;
ptr_start = ptr = path;
while ((ptr = flecs_path_elem(ptr, sep, &len))) {
if (len < size) {
ecs_os_memcpy(elem, ptr_start, len);
} else {
if (size == ECS_NAME_BUFFER_LENGTH) {
elem = NULL;
}
elem = ecs_os_realloc(elem, len + 1);
ecs_os_memcpy(elem, ptr_start, len);
size = len + 1;
}
elem[len] = '\0';
ptr_start = ptr;
cur = ecs_lookup_child(world, cur, elem);
if (!cur) {
goto tail;
}
}
tail:
if (!cur && recursive) {
if (!lookup_path_search) {
if (parent) {
parent = ecs_get_target(world, parent, EcsChildOf, 0);
goto retry;
} else {
lookup_path_search = true;
}
}
if (lookup_path_search) {
if (lookup_path_cur != lookup_path) {
lookup_path_cur --;
parent = lookup_path_cur[0];
goto retry;
}
}
}
if (elem != buff) {
ecs_os_free(elem);
}
return cur;
error:
return 0;
}
ecs_entity_t ecs_set_scope(
ecs_world_t *world,
ecs_entity_t scope)
{
ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_stage_t *stage = flecs_stage_from_world(&world);
ecs_entity_t cur = stage->scope;
stage->scope = scope;
return cur;
error:
return 0;
}
ecs_entity_t ecs_get_scope(
const ecs_world_t *world)
{
ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL);
const ecs_stage_t *stage = flecs_stage_from_readonly_world(world);
return stage->scope;
error:
return 0;
}
ecs_entity_t* ecs_set_lookup_path(
ecs_world_t *world,
const ecs_entity_t *lookup_path)
{
ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL);
ecs_stage_t *stage = flecs_stage_from_world(&world);
/* Safe: application owns lookup path */
ecs_entity_t *cur = ECS_CONST_CAST(ecs_entity_t*, stage->lookup_path);
stage->lookup_path = lookup_path;
return cur;
error:
return NULL;
}
ecs_entity_t* ecs_get_lookup_path(
const ecs_world_t *world)
{
ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL);
const ecs_stage_t *stage = flecs_stage_from_readonly_world(world);
/* Safe: application owns lookup path */
return ECS_CONST_CAST(ecs_entity_t*, stage->lookup_path);
error:
return NULL;
}
const char* ecs_set_name_prefix(
ecs_world_t *world,
const char *prefix)
{
ecs_poly_assert(world, ecs_world_t);
const char *old_prefix = world->info.name_prefix;
world->info.name_prefix = prefix;
return old_prefix;
}
static
void flecs_add_path(
ecs_world_t *world,
bool defer_suspend,
ecs_entity_t parent,
ecs_entity_t entity,
const char *name)
{
ecs_suspend_readonly_state_t srs;
ecs_world_t *real_world = NULL;
if (defer_suspend) {
real_world = flecs_suspend_readonly(world, &srs);
ecs_assert(real_world != NULL, ECS_INTERNAL_ERROR, NULL);
}
if (parent) {
ecs_add_pair(world, entity, EcsChildOf, parent);
}
ecs_set_name(world, entity, name);
if (defer_suspend) {
flecs_resume_readonly(real_world, &srs);
flecs_defer_path((ecs_stage_t*)world, parent, entity, name);
}
}
ecs_entity_t ecs_add_path_w_sep(
ecs_world_t *world,
ecs_entity_t entity,
ecs_entity_t parent,
const char *path,
const char *sep,
const char *prefix)
{
ecs_check(world != NULL, ECS_INVALID_PARAMETER, NULL);
if (!sep) {
sep = ".";
}
if (!path) {
if (!entity) {
entity = ecs_new_id(world);
}
if (parent) {
ecs_add_pair(world, entity, EcsChildOf, entity);
}
return entity;
}
bool root_path = flecs_is_root_path(path, prefix);
parent = flecs_get_parent_from_path(world, parent, &path, prefix, !entity);
char buff[ECS_NAME_BUFFER_LENGTH];
const char *ptr = path;
const char *ptr_start = path;
char *elem = buff;
int32_t len, size = ECS_NAME_BUFFER_LENGTH;
/* If we're in deferred/readonly mode suspend it, so that the name index is
* immediately updated. Without this, we could create multiple entities for
* the same name in a single command queue. */
bool suspend_defer = ecs_poly_is(world, ecs_stage_t) &&
(ecs_get_stage_count(world) <= 1);
ecs_entity_t cur = parent;
char *name = NULL;
if (sep[0]) {
while ((ptr = flecs_path_elem(ptr, sep, &len))) {
if (len < size) {
ecs_os_memcpy(elem, ptr_start, len);
} else {
if (size == ECS_NAME_BUFFER_LENGTH) {
elem = NULL;
}
elem = ecs_os_realloc(elem, len + 1);
ecs_os_memcpy(elem, ptr_start, len);
size = len + 1;
}
elem[len] = '\0';
ptr_start = ptr;
ecs_entity_t e = ecs_lookup_child(world, cur, elem);
if (!e) {
if (name) {
ecs_os_free(name);
}
name = ecs_os_strdup(elem);
/* If this is the last entity in the path, use the provided id */
bool last_elem = false;
if (!flecs_path_elem(ptr, sep, NULL)) {
e = entity;
last_elem = true;
}
if (!e) {
if (last_elem) {
ecs_entity_t prev = ecs_set_scope(world, 0);
e = ecs_new(world, 0);
ecs_set_scope(world, prev);
} else {
e = ecs_new_id(world);
}
}
if (!cur && last_elem && root_path) {
ecs_remove_pair(world, e, EcsChildOf, EcsWildcard);
}
flecs_add_path(world, suspend_defer, cur, e, name);
}
cur = e;
}
if (entity && (cur != entity)) {
ecs_throw(ECS_ALREADY_DEFINED, name);
}
if (name) {
ecs_os_free(name);
}
if (elem != buff) {
ecs_os_free(elem);
}
} else {
flecs_add_path(world, suspend_defer, parent, entity, path);
}
return cur;
error:
return 0;
}
ecs_entity_t ecs_new_from_path_w_sep(
ecs_world_t *world,
ecs_entity_t parent,
const char *path,
const char *sep,
const char *prefix)
{
return ecs_add_path_w_sep(world, 0, parent, path, sep, prefix);
}