615 lines
18 KiB
C
615 lines
18 KiB
C
/**
|
|
* @file id_index.c
|
|
* @brief Index for looking up tables by (component) id.
|
|
*
|
|
* An id record stores the administration for an in use (component) id, that is
|
|
* an id that has been used in tables.
|
|
*
|
|
* An id record contains a table cache, which stores the list of tables that
|
|
* have the id. Each entry in the cache (a table record) stores the first
|
|
* occurrence of the id in the table and the number of occurrences of the id in
|
|
* the table (in the case of wildcard ids).
|
|
*
|
|
* Id records are used in lots of scenarios, like uncached queries, or for
|
|
* getting a component array/component for an entity.
|
|
*/
|
|
|
|
#include "../private_api.h"
|
|
|
|
static
|
|
ecs_id_record_elem_t* flecs_id_record_elem(
|
|
ecs_id_record_t *head,
|
|
ecs_id_record_elem_t *list,
|
|
ecs_id_record_t *idr)
|
|
{
|
|
return ECS_OFFSET(idr, (uintptr_t)list - (uintptr_t)head);
|
|
}
|
|
|
|
static
|
|
void flecs_id_record_elem_insert(
|
|
ecs_id_record_t *head,
|
|
ecs_id_record_t *idr,
|
|
ecs_id_record_elem_t *elem)
|
|
{
|
|
ecs_id_record_elem_t *head_elem = flecs_id_record_elem(idr, elem, head);
|
|
ecs_id_record_t *cur = head_elem->next;
|
|
elem->next = cur;
|
|
elem->prev = head;
|
|
if (cur) {
|
|
ecs_id_record_elem_t *cur_elem = flecs_id_record_elem(idr, elem, cur);
|
|
cur_elem->prev = idr;
|
|
}
|
|
head_elem->next = idr;
|
|
}
|
|
|
|
static
|
|
void flecs_id_record_elem_remove(
|
|
ecs_id_record_t *idr,
|
|
ecs_id_record_elem_t *elem)
|
|
{
|
|
ecs_id_record_t *prev = elem->prev;
|
|
ecs_id_record_t *next = elem->next;
|
|
ecs_assert(prev != NULL, ECS_INTERNAL_ERROR, NULL);
|
|
|
|
ecs_id_record_elem_t *prev_elem = flecs_id_record_elem(idr, elem, prev);
|
|
prev_elem->next = next;
|
|
if (next) {
|
|
ecs_id_record_elem_t *next_elem = flecs_id_record_elem(idr, elem, next);
|
|
next_elem->prev = prev;
|
|
}
|
|
}
|
|
|
|
static
|
|
void flecs_insert_id_elem(
|
|
ecs_world_t *world,
|
|
ecs_id_record_t *idr,
|
|
ecs_id_t wildcard,
|
|
ecs_id_record_t *widr)
|
|
{
|
|
ecs_assert(ecs_id_is_wildcard(wildcard), ECS_INTERNAL_ERROR, NULL);
|
|
if (!widr) {
|
|
widr = flecs_id_record_ensure(world, wildcard);
|
|
}
|
|
ecs_assert(widr != NULL, ECS_INTERNAL_ERROR, NULL);
|
|
|
|
if (ECS_PAIR_SECOND(wildcard) == EcsWildcard) {
|
|
ecs_assert(ECS_PAIR_FIRST(wildcard) != EcsWildcard,
|
|
ECS_INTERNAL_ERROR, NULL);
|
|
flecs_id_record_elem_insert(widr, idr, &idr->first);
|
|
} else {
|
|
ecs_assert(ECS_PAIR_FIRST(wildcard) == EcsWildcard,
|
|
ECS_INTERNAL_ERROR, NULL);
|
|
flecs_id_record_elem_insert(widr, idr, &idr->second);
|
|
|
|
if (idr->flags & EcsIdTraversable) {
|
|
flecs_id_record_elem_insert(widr, idr, &idr->trav);
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
void flecs_remove_id_elem(
|
|
ecs_id_record_t *idr,
|
|
ecs_id_t wildcard)
|
|
{
|
|
ecs_assert(ecs_id_is_wildcard(wildcard), ECS_INTERNAL_ERROR, NULL);
|
|
|
|
if (ECS_PAIR_SECOND(wildcard) == EcsWildcard) {
|
|
ecs_assert(ECS_PAIR_FIRST(wildcard) != EcsWildcard,
|
|
ECS_INTERNAL_ERROR, NULL);
|
|
flecs_id_record_elem_remove(idr, &idr->first);
|
|
} else {
|
|
ecs_assert(ECS_PAIR_FIRST(wildcard) == EcsWildcard,
|
|
ECS_INTERNAL_ERROR, NULL);
|
|
flecs_id_record_elem_remove(idr, &idr->second);
|
|
|
|
if (idr->flags & EcsIdTraversable) {
|
|
flecs_id_record_elem_remove(idr, &idr->trav);
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
ecs_id_t flecs_id_record_hash(
|
|
ecs_id_t id)
|
|
{
|
|
id = ecs_strip_generation(id);
|
|
if (ECS_IS_PAIR(id)) {
|
|
ecs_entity_t r = ECS_PAIR_FIRST(id);
|
|
ecs_entity_t o = ECS_PAIR_SECOND(id);
|
|
if (r == EcsAny) {
|
|
r = EcsWildcard;
|
|
}
|
|
if (o == EcsAny) {
|
|
o = EcsWildcard;
|
|
}
|
|
id = ecs_pair(r, o);
|
|
}
|
|
return id;
|
|
}
|
|
|
|
static
|
|
ecs_id_record_t* flecs_id_record_new(
|
|
ecs_world_t *world,
|
|
ecs_id_t id)
|
|
{
|
|
ecs_id_record_t *idr, *idr_t = NULL;
|
|
ecs_id_t hash = flecs_id_record_hash(id);
|
|
if (hash >= FLECS_HI_ID_RECORD_ID) {
|
|
idr = flecs_bcalloc(&world->allocators.id_record);
|
|
ecs_map_insert_ptr(&world->id_index_hi, hash, idr);
|
|
} else {
|
|
idr = &world->id_index_lo[hash];
|
|
ecs_os_zeromem(idr);
|
|
}
|
|
|
|
ecs_table_cache_init(world, &idr->cache);
|
|
|
|
idr->id = id;
|
|
idr->refcount = 1;
|
|
idr->reachable.current = -1;
|
|
|
|
bool is_wildcard = ecs_id_is_wildcard(id);
|
|
bool is_pair = ECS_IS_PAIR(id);
|
|
|
|
ecs_entity_t rel = 0, tgt = 0, role = id & ECS_ID_FLAGS_MASK;
|
|
if (is_pair) {
|
|
// rel = ecs_pair_first(world, id);
|
|
rel = ECS_PAIR_FIRST(id);
|
|
ecs_assert(rel != 0, ECS_INTERNAL_ERROR, NULL);
|
|
|
|
/* Relationship object can be 0, as tables without a ChildOf
|
|
* relationship are added to the (ChildOf, 0) id record */
|
|
tgt = ECS_PAIR_SECOND(id);
|
|
|
|
#ifdef FLECS_DEBUG
|
|
/* Check constraints */
|
|
if (tgt) {
|
|
tgt = ecs_get_alive(world, tgt);
|
|
ecs_assert(tgt != 0, ECS_INTERNAL_ERROR, NULL);
|
|
}
|
|
if (tgt && !ecs_id_is_wildcard(tgt)) {
|
|
/* Check if target of relationship satisfies OneOf property */
|
|
ecs_entity_t oneof = flecs_get_oneof(world, rel);
|
|
ecs_check( !oneof || ecs_has_pair(world, tgt, EcsChildOf, oneof),
|
|
ECS_CONSTRAINT_VIOLATED, NULL);
|
|
(void)oneof;
|
|
|
|
/* Check if we're not trying to inherit from a final target */
|
|
if (rel == EcsIsA) {
|
|
bool is_final = ecs_has_id(world, tgt, EcsFinal);
|
|
ecs_check(!is_final, ECS_CONSTRAINT_VIOLATED,
|
|
"cannot inherit from final entity");
|
|
(void)is_final;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!is_wildcard && (rel != EcsFlag)) {
|
|
/* Inherit flags from (relationship, *) record */
|
|
ecs_id_record_t *idr_r = flecs_id_record_ensure(
|
|
world, ecs_pair(rel, EcsWildcard));
|
|
idr->parent = idr_r;
|
|
idr->flags = idr_r->flags;
|
|
|
|
/* If pair is not a wildcard, append it to wildcard lists. These
|
|
* allow for quickly enumerating all relationships for an object,
|
|
* or all objecs for a relationship. */
|
|
flecs_insert_id_elem(world, idr, ecs_pair(rel, EcsWildcard), idr_r);
|
|
|
|
idr_t = flecs_id_record_ensure(world, ecs_pair(EcsWildcard, tgt));
|
|
flecs_insert_id_elem(world, idr, ecs_pair(EcsWildcard, tgt), idr_t);
|
|
|
|
if (rel == EcsUnion) {
|
|
idr->flags |= EcsIdUnion;
|
|
}
|
|
}
|
|
} else {
|
|
rel = id & ECS_COMPONENT_MASK;
|
|
ecs_assert(rel != 0, ECS_INTERNAL_ERROR, NULL);
|
|
}
|
|
|
|
/* Initialize type info if id is not a tag */
|
|
if (!is_wildcard && (!role || is_pair)) {
|
|
if (!(idr->flags & EcsIdTag)) {
|
|
const ecs_type_info_t *ti = flecs_type_info_get(world, rel);
|
|
if (!ti && tgt) {
|
|
ti = flecs_type_info_get(world, tgt);
|
|
}
|
|
idr->type_info = ti;
|
|
}
|
|
}
|
|
|
|
/* Mark entities that are used as component/pair ids. When a tracked
|
|
* entity is deleted, cleanup policies are applied so that the store
|
|
* won't contain any tables with deleted ids. */
|
|
|
|
/* Flag for OnDelete policies */
|
|
flecs_add_flag(world, rel, EcsEntityIsId);
|
|
if (tgt) {
|
|
/* Flag for OnDeleteTarget policies */
|
|
ecs_record_t *tgt_r = flecs_entities_get_any(world, tgt);
|
|
ecs_assert(tgt_r != NULL, ECS_INTERNAL_ERROR, NULL);
|
|
flecs_record_add_flag(tgt_r, EcsEntityIsTarget);
|
|
if (idr->flags & EcsIdTraversable) {
|
|
/* Flag used to determine if object should be traversed when
|
|
* propagating events or with super/subset queries */
|
|
flecs_record_add_flag(tgt_r, EcsEntityIsTraversable);
|
|
|
|
/* Add reference to (*, tgt) id record to entity record */
|
|
tgt_r->idr = idr_t;
|
|
}
|
|
}
|
|
|
|
ecs_observable_t *o = &world->observable;
|
|
idr->flags |= flecs_observers_exist(o, id, EcsOnAdd) * EcsIdHasOnAdd;
|
|
idr->flags |= flecs_observers_exist(o, id, EcsOnRemove) * EcsIdHasOnRemove;
|
|
idr->flags |= flecs_observers_exist(o, id, EcsOnSet) * EcsIdHasOnSet;
|
|
idr->flags |= flecs_observers_exist(o, id, EcsUnSet) * EcsIdHasUnSet;
|
|
idr->flags |= flecs_observers_exist(o, id, EcsOnTableFill) * EcsIdHasOnTableFill;
|
|
idr->flags |= flecs_observers_exist(o, id, EcsOnTableEmpty) * EcsIdHasOnTableEmpty;
|
|
idr->flags |= flecs_observers_exist(o, id, EcsOnTableCreate) * EcsIdHasOnTableCreate;
|
|
idr->flags |= flecs_observers_exist(o, id, EcsOnTableDelete) * EcsIdHasOnTableDelete;
|
|
|
|
if (ecs_should_log_1()) {
|
|
char *id_str = ecs_id_str(world, id);
|
|
ecs_dbg_1("#[green]id#[normal] %s #[green]created", id_str);
|
|
ecs_os_free(id_str);
|
|
}
|
|
|
|
/* Update counters */
|
|
world->info.id_create_total ++;
|
|
|
|
if (!is_wildcard) {
|
|
world->info.id_count ++;
|
|
|
|
if (idr->type_info) {
|
|
world->info.component_id_count ++;
|
|
} else {
|
|
world->info.tag_id_count ++;
|
|
}
|
|
|
|
if (is_pair) {
|
|
world->info.pair_id_count ++;
|
|
}
|
|
} else {
|
|
world->info.wildcard_id_count ++;
|
|
}
|
|
|
|
return idr;
|
|
#ifdef FLECS_DEBUG
|
|
error:
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
static
|
|
void flecs_id_record_assert_empty(
|
|
ecs_id_record_t *idr)
|
|
{
|
|
(void)idr;
|
|
ecs_assert(flecs_table_cache_count(&idr->cache) == 0,
|
|
ECS_INTERNAL_ERROR, NULL);
|
|
ecs_assert(flecs_table_cache_empty_count(&idr->cache) == 0,
|
|
ECS_INTERNAL_ERROR, NULL);
|
|
}
|
|
|
|
static
|
|
void flecs_id_record_free(
|
|
ecs_world_t *world,
|
|
ecs_id_record_t *idr)
|
|
{
|
|
ecs_poly_assert(world, ecs_world_t);
|
|
ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL);
|
|
ecs_id_t id = idr->id;
|
|
|
|
flecs_id_record_assert_empty(idr);
|
|
|
|
/* Id is still in use by a filter, query, rule or observer */
|
|
ecs_assert((world->flags & EcsWorldQuit) || (idr->keep_alive == 0),
|
|
ECS_ID_IN_USE, "cannot delete id that is queried for");
|
|
|
|
if (ECS_IS_PAIR(id)) {
|
|
ecs_entity_t rel = ECS_PAIR_FIRST(id);
|
|
ecs_entity_t tgt = ECS_PAIR_SECOND(id);
|
|
if (!ecs_id_is_wildcard(id)) {
|
|
if (ECS_PAIR_FIRST(id) != EcsFlag) {
|
|
/* If id is not a wildcard, remove it from the wildcard lists */
|
|
flecs_remove_id_elem(idr, ecs_pair(rel, EcsWildcard));
|
|
flecs_remove_id_elem(idr, ecs_pair(EcsWildcard, tgt));
|
|
}
|
|
} else {
|
|
ecs_log_push_2();
|
|
|
|
/* If id is a wildcard, it means that all id records that match the
|
|
* wildcard are also empty, so release them */
|
|
if (ECS_PAIR_FIRST(id) == EcsWildcard) {
|
|
/* Iterate (*, Target) list */
|
|
ecs_id_record_t *cur, *next = idr->second.next;
|
|
while ((cur = next)) {
|
|
flecs_id_record_assert_empty(cur);
|
|
next = cur->second.next;
|
|
flecs_id_record_release(world, cur);
|
|
}
|
|
} else {
|
|
/* Iterate (Relationship, *) list */
|
|
ecs_assert(ECS_PAIR_SECOND(id) == EcsWildcard,
|
|
ECS_INTERNAL_ERROR, NULL);
|
|
ecs_id_record_t *cur, *next = idr->first.next;
|
|
while ((cur = next)) {
|
|
flecs_id_record_assert_empty(cur);
|
|
next = cur->first.next;
|
|
flecs_id_record_release(world, cur);
|
|
}
|
|
}
|
|
|
|
ecs_log_pop_2();
|
|
}
|
|
}
|
|
|
|
/* Update counters */
|
|
world->info.id_delete_total ++;
|
|
|
|
if (!ecs_id_is_wildcard(id)) {
|
|
world->info.id_count --;
|
|
|
|
if (ECS_IS_PAIR(id)) {
|
|
world->info.pair_id_count --;
|
|
}
|
|
|
|
if (idr->type_info) {
|
|
world->info.component_id_count --;
|
|
} else {
|
|
world->info.tag_id_count --;
|
|
}
|
|
} else {
|
|
world->info.wildcard_id_count --;
|
|
}
|
|
|
|
/* Unregister the id record from the world & free resources */
|
|
ecs_table_cache_fini(&idr->cache);
|
|
flecs_name_index_free(idr->name_index);
|
|
ecs_vec_fini_t(&world->allocator, &idr->reachable.ids, ecs_reachable_elem_t);
|
|
|
|
ecs_id_t hash = flecs_id_record_hash(id);
|
|
if (hash >= FLECS_HI_ID_RECORD_ID) {
|
|
ecs_map_remove(&world->id_index_hi, hash);
|
|
flecs_bfree(&world->allocators.id_record, idr);
|
|
} else {
|
|
idr->id = 0; /* Tombstone */
|
|
}
|
|
|
|
if (ecs_should_log_1()) {
|
|
char *id_str = ecs_id_str(world, id);
|
|
ecs_dbg_1("#[green]id#[normal] %s #[red]deleted", id_str);
|
|
ecs_os_free(id_str);
|
|
}
|
|
}
|
|
|
|
ecs_id_record_t* flecs_id_record_ensure(
|
|
ecs_world_t *world,
|
|
ecs_id_t id)
|
|
{
|
|
ecs_id_record_t *idr = flecs_id_record_get(world, id);
|
|
if (!idr) {
|
|
idr = flecs_id_record_new(world, id);
|
|
}
|
|
return idr;
|
|
}
|
|
|
|
ecs_id_record_t* flecs_id_record_get(
|
|
const ecs_world_t *world,
|
|
ecs_id_t id)
|
|
{
|
|
ecs_poly_assert(world, ecs_world_t);
|
|
if (id == ecs_pair(EcsIsA, EcsWildcard)) {
|
|
return world->idr_isa_wildcard;
|
|
} else if (id == ecs_pair(EcsChildOf, EcsWildcard)) {
|
|
return world->idr_childof_wildcard;
|
|
} else if (id == ecs_pair_t(EcsIdentifier, EcsName)) {
|
|
return world->idr_identifier_name;
|
|
}
|
|
|
|
ecs_id_t hash = flecs_id_record_hash(id);
|
|
ecs_id_record_t *idr = NULL;
|
|
if (hash >= FLECS_HI_ID_RECORD_ID) {
|
|
idr = ecs_map_get_deref(&world->id_index_hi, ecs_id_record_t, hash);
|
|
} else {
|
|
idr = &world->id_index_lo[hash];
|
|
if (!idr->id) {
|
|
idr = NULL;
|
|
}
|
|
}
|
|
|
|
return idr;
|
|
}
|
|
|
|
ecs_id_record_t* flecs_query_id_record_get(
|
|
const ecs_world_t *world,
|
|
ecs_id_t id)
|
|
{
|
|
ecs_id_record_t *idr = flecs_id_record_get(world, id);
|
|
if (!idr) {
|
|
ecs_entity_t first = ECS_PAIR_FIRST(id);
|
|
if (ECS_IS_PAIR(id) && (first != EcsWildcard)) {
|
|
idr = flecs_id_record_get(world, ecs_pair(EcsUnion, first));
|
|
}
|
|
return idr;
|
|
}
|
|
if (ECS_IS_PAIR(id) &&
|
|
ECS_PAIR_SECOND(id) == EcsWildcard &&
|
|
(idr->flags & EcsIdUnion))
|
|
{
|
|
idr = flecs_id_record_get(world,
|
|
ecs_pair(EcsUnion, ECS_PAIR_FIRST(id)));
|
|
}
|
|
|
|
return idr;
|
|
}
|
|
|
|
void flecs_id_record_claim(
|
|
ecs_world_t *world,
|
|
ecs_id_record_t *idr)
|
|
{
|
|
(void)world;
|
|
idr->refcount ++;
|
|
}
|
|
|
|
int32_t flecs_id_record_release(
|
|
ecs_world_t *world,
|
|
ecs_id_record_t *idr)
|
|
{
|
|
int32_t rc = -- idr->refcount;
|
|
ecs_assert(rc >= 0, ECS_INTERNAL_ERROR, NULL);
|
|
|
|
if (!rc) {
|
|
flecs_id_record_free(world, idr);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
void flecs_id_record_release_tables(
|
|
ecs_world_t *world,
|
|
ecs_id_record_t *idr)
|
|
{
|
|
/* Cache should not contain tables that aren't empty */
|
|
ecs_assert(flecs_table_cache_count(&idr->cache) == 0,
|
|
ECS_INTERNAL_ERROR, NULL);
|
|
|
|
ecs_table_cache_iter_t it;
|
|
if (flecs_table_cache_empty_iter(&idr->cache, &it)) {
|
|
ecs_table_record_t *tr;
|
|
while ((tr = flecs_table_cache_next(&it, ecs_table_record_t))) {
|
|
/* Release current table */
|
|
flecs_table_free(world, tr->hdr.table);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool flecs_id_record_set_type_info(
|
|
ecs_world_t *world,
|
|
ecs_id_record_t *idr,
|
|
const ecs_type_info_t *ti)
|
|
{
|
|
bool is_wildcard = ecs_id_is_wildcard(idr->id);
|
|
if (!is_wildcard) {
|
|
if (ti) {
|
|
if (!idr->type_info) {
|
|
world->info.tag_id_count --;
|
|
world->info.component_id_count ++;
|
|
}
|
|
} else {
|
|
if (idr->type_info) {
|
|
world->info.tag_id_count ++;
|
|
world->info.component_id_count --;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool changed = idr->type_info != ti;
|
|
idr->type_info = ti;
|
|
|
|
return changed;
|
|
}
|
|
|
|
ecs_hashmap_t* flecs_id_record_name_index_ensure(
|
|
ecs_world_t *world,
|
|
ecs_id_record_t *idr)
|
|
{
|
|
ecs_hashmap_t *map = idr->name_index;
|
|
if (!map) {
|
|
map = idr->name_index = flecs_name_index_new(world, &world->allocator);
|
|
}
|
|
|
|
return map;
|
|
}
|
|
|
|
ecs_hashmap_t* flecs_id_name_index_ensure(
|
|
ecs_world_t *world,
|
|
ecs_id_t id)
|
|
{
|
|
ecs_poly_assert(world, ecs_world_t);
|
|
|
|
ecs_id_record_t *idr = flecs_id_record_get(world, id);
|
|
ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL);
|
|
|
|
return flecs_id_record_name_index_ensure(world, idr);
|
|
}
|
|
|
|
ecs_hashmap_t* flecs_id_name_index_get(
|
|
const ecs_world_t *world,
|
|
ecs_id_t id)
|
|
{
|
|
ecs_poly_assert(world, ecs_world_t);
|
|
|
|
ecs_id_record_t *idr = flecs_id_record_get(world, id);
|
|
if (!idr) {
|
|
return NULL;
|
|
}
|
|
|
|
return idr->name_index;
|
|
}
|
|
|
|
ecs_table_record_t* flecs_table_record_get(
|
|
const ecs_world_t *world,
|
|
const ecs_table_t *table,
|
|
ecs_id_t id)
|
|
{
|
|
ecs_poly_assert(world, ecs_world_t);
|
|
|
|
ecs_id_record_t* idr = flecs_id_record_get(world, id);
|
|
if (!idr) {
|
|
return NULL;
|
|
}
|
|
|
|
return (ecs_table_record_t*)ecs_table_cache_get(&idr->cache, table);
|
|
}
|
|
|
|
ecs_table_record_t* flecs_id_record_get_table(
|
|
const ecs_id_record_t *idr,
|
|
const ecs_table_t *table)
|
|
{
|
|
ecs_assert(idr != NULL, ECS_INTERNAL_ERROR, NULL);
|
|
return (ecs_table_record_t*)ecs_table_cache_get(&idr->cache, table);
|
|
}
|
|
|
|
void flecs_init_id_records(
|
|
ecs_world_t *world)
|
|
{
|
|
/* Cache often used id records on world */
|
|
world->idr_wildcard = flecs_id_record_ensure(world, EcsWildcard);
|
|
world->idr_wildcard_wildcard = flecs_id_record_ensure(world,
|
|
ecs_pair(EcsWildcard, EcsWildcard));
|
|
world->idr_any = flecs_id_record_ensure(world, EcsAny);
|
|
world->idr_isa_wildcard = flecs_id_record_ensure(world,
|
|
ecs_pair(EcsIsA, EcsWildcard));
|
|
}
|
|
|
|
void flecs_fini_id_records(
|
|
ecs_world_t *world)
|
|
{
|
|
/* Loop & delete first element until there are no elements left. Id records
|
|
* can recursively delete each other, this ensures we always have a
|
|
* valid iterator. */
|
|
while (ecs_map_count(&world->id_index_hi) > 0) {
|
|
ecs_map_iter_t it = ecs_map_iter(&world->id_index_hi);
|
|
ecs_map_next(&it);
|
|
flecs_id_record_release(world, ecs_map_ptr(&it));
|
|
}
|
|
|
|
int32_t i;
|
|
for (i = 0; i < FLECS_HI_ID_RECORD_ID; i ++) {
|
|
ecs_id_record_t *idr = &world->id_index_lo[i];
|
|
if (idr->id) {
|
|
flecs_id_record_release(world, idr);
|
|
}
|
|
}
|
|
|
|
ecs_assert(ecs_map_count(&world->id_index_hi) == 0,
|
|
ECS_INTERNAL_ERROR, NULL);
|
|
|
|
ecs_map_fini(&world->id_index_hi);
|
|
ecs_os_free(world->id_index_lo);
|
|
}
|