diff --git a/distr/flecs.c b/distr/flecs.c index 46ffdd45e..b0a9d5bab 100644 --- a/distr/flecs.c +++ b/distr/flecs.c @@ -18506,6 +18506,11 @@ void flecs_default_move_w_dtor(void *dst_ptr, void *src_ptr, cl->dtor(src_ptr, count, ti); } +static +bool flecs_default_equals(const void *a_ptr, const void *b_ptr, const ecs_type_info_t* ti) { + return ti->hooks.cmp(a_ptr, b_ptr, ti) == 0; +} + ECS_NORETURN static void flecs_ctor_illegal( void * dst, @@ -18577,6 +18582,28 @@ void flecs_move_ctor_illegal( ecs_abort(ECS_INVALID_OPERATION, "invalid move construct for %s", ti->name); } +ECS_NORETURN static +int flecs_comp_illegal( + const void *dst, + const void *src, + const ecs_type_info_t *ti) +{ + (void)dst; /* silence unused warning */ + (void)src; + ecs_abort(ECS_INVALID_OPERATION, "invalid compare hook for %s", ti->name); +} + +ECS_NORETURN static +bool flecs_equals_illegal( + const void *dst, + const void *src, + const ecs_type_info_t *ti) +{ + (void)dst; /* silence unused warning */ + (void)src; + ecs_abort(ECS_INVALID_OPERATION, "invalid equals hook for %s", ti->name); +} + void ecs_set_hooks_id( ecs_world_t *world, ecs_entity_t component, @@ -18588,27 +18615,56 @@ void ecs_set_hooks_id( ecs_flags32_t flags = h->flags; flags &= ~((ecs_flags32_t)ECS_TYPE_HOOKS); - /* TODO: enable asserts once RTT API is updated */ - /* - ecs_check(!(h->flags & ECS_TYPE_HOOK_CTOR_ILLEGAL) || !h->ctor, - ECS_INVALID_PARAMETER, "cannot specify both hook and illegal flag"); - ecs_check(!(h->flags & ECS_TYPE_HOOK_DTOR_ILLEGAL) || !h->dtor, - ECS_INVALID_PARAMETER, "cannot specify both hook and illegal flag"); - ecs_check(!(h->flags & ECS_TYPE_HOOK_COPY_ILLEGAL) || !h->copy, - ECS_INVALID_PARAMETER, "cannot specify both hook and illegal flag"); - ecs_check(!(h->flags & ECS_TYPE_HOOK_MOVE_ILLEGAL) || !h->move, - ECS_INVALID_PARAMETER, "cannot specify both hook and illegal flag"); - ecs_check(!(h->flags & ECS_TYPE_HOOK_COPY_CTOR_ILLEGAL) || !h->copy_ctor, - ECS_INVALID_PARAMETER, - "cannot specify both hook and illegal flag"); - ecs_check(!(h->flags & ECS_TYPE_HOOK_MOVE_CTOR_ILLEGAL) || !h->move_ctor, - ECS_INVALID_PARAMETER, "cannot specify both hook and illegal flag"); - ecs_check(!(h->flags & ECS_TYPE_HOOK_CTOR_MOVE_DTOR_ILLEGAL) || - !h->ctor_move_dtor, ECS_INVALID_PARAMETER, - "cannot specify both hook and illegal flag"); - ecs_check(!(h->flags & ECS_TYPE_HOOK_MOVE_DTOR_ILLEGAL) || !h->move_dtor, - ECS_INVALID_PARAMETER, "cannot specify both hook and illegal flag"); - */ + ecs_check(!(flags & ECS_TYPE_HOOK_CTOR_ILLEGAL && + h->ctor != NULL && + h->ctor != flecs_ctor_illegal), + ECS_INVALID_PARAMETER, "cannot specify both ctor hook and illegal flag"); + + ecs_check(!(flags & ECS_TYPE_HOOK_DTOR_ILLEGAL && + h->dtor != NULL && + h->dtor != flecs_dtor_illegal), + ECS_INVALID_PARAMETER, "cannot specify both dtor hook and illegal flag"); + + ecs_check(!(flags & ECS_TYPE_HOOK_COPY_ILLEGAL && + h->copy != NULL && + h->copy != flecs_copy_illegal), + ECS_INVALID_PARAMETER, "cannot specify both copy hook and illegal flag"); + + ecs_check(!(flags & ECS_TYPE_HOOK_MOVE_ILLEGAL && + h->move != NULL && + h->move != flecs_move_illegal), + ECS_INVALID_PARAMETER, "cannot specify both move hook and illegal flag"); + + ecs_check(!(flags & ECS_TYPE_HOOK_COPY_CTOR_ILLEGAL && + h->copy_ctor != NULL && + h->copy_ctor != flecs_copy_ctor_illegal), + ECS_INVALID_PARAMETER, "cannot specify both copy ctor hook and illegal flag"); + + ecs_check(!(flags & ECS_TYPE_HOOK_MOVE_CTOR_ILLEGAL && + h->move_ctor != NULL && + h->move_ctor != flecs_move_ctor_illegal), + ECS_INVALID_PARAMETER, "cannot specify both move ctor hook and illegal flag"); + + ecs_check(!(flags & ECS_TYPE_HOOK_CTOR_MOVE_DTOR_ILLEGAL && + h->ctor_move_dtor != NULL && + h->ctor_move_dtor != flecs_move_ctor_illegal), + ECS_INVALID_PARAMETER, "cannot specify both ctor move dtor hook and illegal flag"); + + ecs_check(!(flags & ECS_TYPE_HOOK_MOVE_DTOR_ILLEGAL && + h->move_dtor != NULL && + h->move_dtor != flecs_move_ctor_illegal), + ECS_INVALID_PARAMETER, "cannot specify both move dtor hook and illegal flag"); + + ecs_check(!(flags & ECS_TYPE_HOOK_CMP_ILLEGAL && + h->cmp != NULL && + h->cmp != flecs_comp_illegal), + ECS_INVALID_PARAMETER, "cannot specify both compare hook and illegal flag"); + + ecs_check(!(flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL && + h->equals != NULL && + h->equals != flecs_equals_illegal), + ECS_INVALID_PARAMETER, "cannot specify both equals hook and illegal flag"); + flecs_stage_from_world(&world); @@ -18646,6 +18702,8 @@ void ecs_set_hooks_id( if (h->move_ctor) ti->hooks.move_ctor = h->move_ctor; if (h->ctor_move_dtor) ti->hooks.ctor_move_dtor = h->ctor_move_dtor; if (h->move_dtor) ti->hooks.move_dtor = h->move_dtor; + if (h->cmp) ti->hooks.cmp = h->cmp; + if (h->equals) ti->hooks.equals = h->equals; if (h->on_add) ti->hooks.on_add = h->on_add; if (h->on_remove) ti->hooks.on_remove = h->on_remove; @@ -18744,6 +18802,19 @@ void ecs_set_hooks_id( } } + if(!h->cmp) { + flags |= ECS_TYPE_HOOK_CMP_ILLEGAL; + } + + if (!h->equals || h->equals == flecs_equals_illegal) { + if(flags & ECS_TYPE_HOOK_CMP_ILLEGAL) { + flags |= ECS_TYPE_HOOK_EQUALS_ILLEGAL; + } else { + ti->hooks.equals = flecs_default_equals; + flags &= ~ECS_TYPE_HOOK_EQUALS_ILLEGAL; + } + } + ti->hooks.flags = flags; if (ti->hooks.ctor) ti->hooks.flags |= ECS_TYPE_HOOK_CTOR; @@ -18754,11 +18825,15 @@ void ecs_set_hooks_id( if (ti->hooks.move_dtor) ti->hooks.flags |= ECS_TYPE_HOOK_MOVE_DTOR; if (ti->hooks.copy) ti->hooks.flags |= ECS_TYPE_HOOK_COPY; if (ti->hooks.copy_ctor) ti->hooks.flags |= ECS_TYPE_HOOK_COPY_CTOR; + if (ti->hooks.cmp) ti->hooks.flags |= ECS_TYPE_HOOK_CMP; + if (ti->hooks.equals) ti->hooks.flags |= ECS_TYPE_HOOK_EQUALS; if(flags & ECS_TYPE_HOOK_CTOR_ILLEGAL) ti->hooks.ctor = flecs_ctor_illegal; if(flags & ECS_TYPE_HOOK_DTOR_ILLEGAL) ti->hooks.dtor = flecs_dtor_illegal; if(flags & ECS_TYPE_HOOK_COPY_ILLEGAL) ti->hooks.copy = flecs_copy_illegal; if(flags & ECS_TYPE_HOOK_MOVE_ILLEGAL) ti->hooks.move = flecs_move_illegal; + if(flags & ECS_TYPE_HOOK_CMP_ILLEGAL) ti->hooks.cmp = flecs_comp_illegal; + if(flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL) ti->hooks.equals = flecs_equals_illegal; if(flags & ECS_TYPE_HOOK_COPY_CTOR_ILLEGAL) { ti->hooks.copy_ctor = flecs_copy_ctor_illegal; @@ -18773,9 +18848,10 @@ void ecs_set_hooks_id( } if(ti->hooks.flags & ECS_TYPE_HOOK_MOVE_DTOR_ILLEGAL) { - ti->hooks.ctor_move_dtor = flecs_move_ctor_illegal; + ti->hooks.move_dtor = flecs_move_ctor_illegal; } + error: return; } @@ -43580,6 +43656,16 @@ int flecs_expr_ser_primitive( void flecs_rtt_init_default_hooks( ecs_iter_t *it); +int ecs_compare_string( + const void *str_a, + const void *str_b, + const ecs_type_info_t *ti); + +bool ecs_equals_string( + const void *str_a, + const void *str_b, + const ecs_type_info_t *ti); + #endif #endif @@ -50581,7 +50667,11 @@ void flecs_meta_import_core_definitions( }), .type = { .size = ECS_SIZEOF(const char*), - .alignment = ECS_ALIGNOF(const char*) + .alignment = ECS_ALIGNOF(const char*), + .hooks = { + .cmp = ecs_compare_string, + .equals = ecs_equals_string + } } }), .type = { @@ -50862,6 +50952,386 @@ static ECS_DTOR(ecs_string_t, ptr, { *(ecs_string_t*)ptr = NULL; }) +/* Primitive comparers */ + +static +int ecs_compare_bool( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + return (int)(*((const ecs_bool_t*)a_ptr)) - (int)(*((const ecs_bool_t*)b_ptr)); +} + +static +bool ecs_equals_bool( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_bool(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_char( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + return (int)(*((const ecs_char_t*)a_ptr)) - (int)(*((const ecs_char_t*)b_ptr)); +} + +static +bool ecs_equals_char( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_char(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_byte( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + return (int)(*((const ecs_byte_t*)a_ptr)) - (int)(*((const ecs_byte_t*)b_ptr)); +} + +static +bool ecs_equals_byte( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_byte(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_u8( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + return (int)(*((const ecs_u8_t*)a_ptr)) - (int)(*((const ecs_u8_t*)b_ptr)); +} + +static +bool ecs_equals_u8( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_u8(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_u16( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + return (int)(*((const ecs_u16_t*)a_ptr)) - (int)(*((const ecs_u16_t*)b_ptr)); +} + +static +bool ecs_equals_u16( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_u16(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_u32( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + ecs_u32_t a = *((const ecs_u32_t*)a_ptr); + ecs_u32_t b = *((const ecs_u32_t*)b_ptr); + return (a > b) - (a < b); +} + +static +bool ecs_equals_u32( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_u32(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_u64( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + ecs_u64_t a = *((const ecs_u64_t*)a_ptr); + ecs_u64_t b = *((const ecs_u64_t*)b_ptr); + return (a > b) - (a < b); +} + +static +bool ecs_equals_u64( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_u64(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_uptr( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + ecs_uptr_t a = *((const ecs_uptr_t*)a_ptr); + ecs_uptr_t b = *((const ecs_uptr_t*)b_ptr); + return (a > b) - (a < b); +} + +static +bool ecs_equals_uptr( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_uptr(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_i8( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + return (int)(*((const ecs_i8_t*)a_ptr)) - + (int)(*((const ecs_i8_t*)b_ptr)); +} + +static +bool ecs_equals_i8( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_i8(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_i16( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + return (int)(*((const ecs_i16_t*)a_ptr)) - + (int)(*((const ecs_i16_t*)b_ptr)); +} + +static +bool ecs_equals_i16( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_i16(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_i32( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + ecs_i32_t a = *((const ecs_i32_t*)a_ptr); + ecs_i32_t b = *((const ecs_i32_t*)b_ptr); + return (a > b) - (a < b); +} + +static +bool ecs_equals_i32( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_i32(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_i64( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + ecs_i64_t a = *((const ecs_i64_t*)a_ptr); + ecs_i64_t b = *((const ecs_i64_t*)b_ptr); + return (a > b) - (a < b); +} + +static +bool ecs_equals_i64( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_i64(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_iptr( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + ecs_iptr_t a = *((const ecs_iptr_t*)a_ptr); + ecs_iptr_t b = *((const ecs_iptr_t*)b_ptr); + return (a > b) - (a < b); +} + +static +bool ecs_equals_iptr( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_iptr(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_f32( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + ecs_f32_t a = *((const ecs_f32_t*)a_ptr); + ecs_f32_t b = *((const ecs_f32_t*)b_ptr); + if (a < b) return -1; + if (a > b) return 1; + return 0; +} + +static +bool ecs_equals_f32( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + /* intentional equal check as if it was an integer */ + return ecs_compare_u32(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_f64( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + ecs_f64_t a = *((const ecs_f64_t*)a_ptr); + ecs_f64_t b = *((const ecs_f64_t*)b_ptr); + if (a < b) return -1; + if (a > b) return 1; + return 0; +} + +static +bool ecs_equals_f64( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + /* intentional equal check as if it was an integer */ + return ecs_compare_u64(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_entity( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + ecs_entity_t a = *((const ecs_entity_t*)a_ptr); + ecs_entity_t b = *((const ecs_entity_t*)b_ptr); + return (a > b) - (a < b); +} + +static +bool ecs_equals_entity( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_entity(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_id( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + ecs_id_t a = *((const ecs_id_t*)a_ptr); + ecs_id_t b = *((const ecs_id_t*)b_ptr); + return (a > b) - (a < b); +} + +static +bool ecs_equals_id( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_id(a_ptr, b_ptr, ti) == 0; +} + +int ecs_compare_string( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) { + (void)ti; + const ecs_string_t str_a = *((const ecs_string_t *) a_ptr); + const ecs_string_t str_b = *((const ecs_string_t *) b_ptr); + if(str_a == str_b) { + return 0; + } + if(str_a == NULL) { + return -1; + } + if(str_b == NULL) { + return 1; + } + return ecs_os_strcmp(str_a, str_b); +} + +bool ecs_equals_string( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_string(a_ptr, b_ptr, ti) == 0; +} + /* EcsTypeSerializer lifecycle */ @@ -51105,8 +51575,24 @@ int flecs_init_type( * serializers on uninitialized values. For runtime types (rtt), the default hooks are set by flecs_meta_rtt_init_default_hooks */ ecs_type_info_t *ti = flecs_type_info_ensure(world, type); - if (meta_type->existing && !ti->hooks.ctor) { - ti->hooks.ctor = flecs_default_ctor; + if (meta_type->existing) { + if(!ti->hooks.ctor) { + ti->hooks.ctor = flecs_default_ctor; + } + if(kind == EcsEnumType) { + /* Generate compare/equals hooks for enums, copying + the underlying type's hooks, which should be + any of the default primitive integral compare hooks, + i.e. ecs_compare_i8, _i16 _32... */ + const EcsEnum* enum_info = ecs_get(world, type, EcsEnum); + ecs_assert(enum_info != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_type_hooks_t *enum_hooks = ecs_get_hooks_id(world, enum_info->underlying_type); + ecs_assert(!(enum_hooks->flags & (ECS_TYPE_HOOK_CMP_ILLEGAL|ECS_TYPE_HOOK_EQUALS_ILLEGAL)), ECS_INTERNAL_ERROR, NULL); + ti->hooks.cmp = enum_hooks->cmp; + ti->hooks.equals = enum_hooks->equals; + ti->hooks.flags &= ~(ECS_TYPE_HOOK_CMP_ILLEGAL|ECS_TYPE_HOOK_EQUALS_ILLEGAL); + ti->hooks.flags |= ECS_TYPE_HOOK_CMP|ECS_TYPE_HOOK_EQUALS; + } } } else { if (meta_type->kind != kind) { @@ -52343,7 +52829,11 @@ void FlecsMetaImport( .symbol = #type });\ ecs_set(world, ecs_id(ecs_##type##_t), EcsPrimitive, {\ .kind = primitive_kind\ - }); + });\ + ecs_set_hooks(world, ecs_##type##_t, { \ + .cmp = ecs_compare_##type, \ + .equals = ecs_equals_##type \ + }) ECS_PRIMITIVE(world, bool, EcsBool); ECS_PRIMITIVE(world, char, EcsChar); @@ -52366,12 +52856,14 @@ void FlecsMetaImport( #undef ECS_PRIMITIVE - ecs_set_hooks(world, ecs_string_t, { - .ctor = flecs_default_ctor, - .copy = ecs_copy(ecs_string_t), - .move = ecs_move(ecs_string_t), - .dtor = ecs_dtor(ecs_string_t) - }); + ecs_type_hooks_t string_hooks = *ecs_get_hooks(world, ecs_string_t); + string_hooks.ctor = flecs_default_ctor; + string_hooks.copy = ecs_copy(ecs_string_t); + string_hooks.move = ecs_move(ecs_string_t); + string_hooks.dtor = ecs_dtor(ecs_string_t); + string_hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; + ecs_set_hooks_id(world, ecs_id(ecs_string_t), &string_hooks); + /* Set default child components. Can be used as hint by deserializers */ ecs_set(world, ecs_id(EcsStruct), EcsDefaultChildComponent, {ecs_id(EcsMember)}); @@ -52404,6 +52896,8 @@ typedef struct ecs_rtt_call_data_t { ecs_xtor_t xtor; ecs_move_t move; ecs_copy_t copy; + ecs_cmp_t cmp; + ecs_equals_t equals; } hook; const ecs_type_info_t *type_info; int32_t offset; @@ -52412,10 +52906,13 @@ typedef struct ecs_rtt_call_data_t { /* Lifecycle context for runtime structs */ typedef struct ecs_rtt_struct_ctx_t { - ecs_vec_t vctor; /* vector */ - ecs_vec_t vdtor; /* vector */ - ecs_vec_t vmove; /* vector */ - ecs_vec_t vcopy; /* vector */ + ecs_vec_t vctor; /* vector */ + ecs_vec_t vdtor; /* vector */ + ecs_vec_t vmove; /* vector */ + ecs_vec_t vcopy; /* vector */ + ecs_vec_t vcmp; /* vector */ + ecs_vec_t vequals; /* vector */ + } ecs_rtt_struct_ctx_t; /* Lifecycle context for runtime arrays */ @@ -52571,6 +53068,77 @@ void flecs_rtt_struct_copy( } } +/* Generic compare hook. It will read hook information call data from the + * structs's lifecycle context and call the compare hooks configured when + * the type was created. */ +static +int flecs_rtt_struct_cmp( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *type_info) +{ + if(a_ptr == b_ptr) { + return 0; + } + + ecs_rtt_struct_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_assert(rtt_ctx != NULL, ECS_INTERNAL_ERROR, NULL); + + int cb_count = ecs_vec_count(&rtt_ctx->vcmp); + int i; + for (i = 0; i < cb_count; i++) { + ecs_rtt_call_data_t *comp_data = + ecs_vec_get_t(&rtt_ctx->vcmp, ecs_rtt_call_data_t, i); + int c = comp_data->hook.cmp( + ECS_OFFSET(a_ptr, comp_data->offset), + ECS_OFFSET(b_ptr, comp_data->offset), + comp_data->type_info); + if (c != 0) { + return c; + } + } + return 0; +} + +/* Generic equals hook. It will read hook information call data from the + * structs's lifecycle context and call the equals hooks configured when + * the type was created. */ +static +bool flecs_rtt_struct_equals( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *type_info) +{ + if(a_ptr == b_ptr) { + return true; + } + + ecs_rtt_struct_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_assert(rtt_ctx != NULL, ECS_INTERNAL_ERROR, NULL); + + int cb_count = ecs_vec_count(&rtt_ctx->vequals); + int i; + for (i = 0; i < cb_count; i++) { + ecs_rtt_call_data_t *comp_data = + ecs_vec_get_t(&rtt_ctx->vequals, ecs_rtt_call_data_t, i); + bool eq = comp_data->hook.equals( + ECS_OFFSET(a_ptr, comp_data->offset), + ECS_OFFSET(b_ptr, comp_data->offset), + comp_data->type_info); + if (!eq) { + return false; + } + } + return true; +} + +static +void flecs_rtt_free_lifecycle_nop( + void *ctx) +{ + (void)ctx; +} + static void flecs_rtt_free_lifecycle_struct_ctx( void *ctx) @@ -52585,6 +53153,8 @@ void flecs_rtt_free_lifecycle_struct_ctx( ecs_vec_fini_t(NULL, &lifecycle_ctx->vdtor, ecs_rtt_call_data_t); ecs_vec_fini_t(NULL, &lifecycle_ctx->vmove, ecs_rtt_call_data_t); ecs_vec_fini_t(NULL, &lifecycle_ctx->vcopy, ecs_rtt_call_data_t); + ecs_vec_fini_t(NULL, &lifecycle_ctx->vcmp, ecs_rtt_call_data_t); + ecs_vec_fini_t(NULL, &lifecycle_ctx->vequals, ecs_rtt_call_data_t); ecs_os_free(ctx); } @@ -52597,40 +53167,52 @@ ecs_rtt_struct_ctx_t * flecs_rtt_configure_struct_hooks( bool ctor, bool dtor, bool move, - bool copy) + bool copy, + bool cmp, + bool equals) { ecs_type_hooks_t hooks = ti->hooks; if (hooks.lifecycle_ctx_free) { hooks.lifecycle_ctx_free(hooks.lifecycle_ctx); } + hooks.ctor = ctor && !(flags & ECS_TYPE_HOOK_CTOR_ILLEGAL) ? + flecs_rtt_struct_ctor : NULL; + + hooks.dtor = dtor && !(flags & ECS_TYPE_HOOK_DTOR_ILLEGAL) ? + flecs_rtt_struct_dtor : NULL; + + hooks.move = move && !(flags & ECS_TYPE_HOOK_MOVE_ILLEGAL) ? + flecs_rtt_struct_move : NULL; + + hooks.copy = copy && !(flags & ECS_TYPE_HOOK_COPY_ILLEGAL) ? + flecs_rtt_struct_copy : NULL; + + hooks.cmp = cmp && !(flags & ECS_TYPE_HOOK_CMP_ILLEGAL) ? + flecs_rtt_struct_cmp : NULL; + + hooks.equals = equals && !(flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL) ? + flecs_rtt_struct_equals : NULL; + ecs_rtt_struct_ctx_t *rtt_ctx = NULL; - if (ctor || dtor || move || copy) { + if (hooks.ctor || hooks.dtor || hooks.move || hooks.copy + || hooks.cmp || hooks.equals) { rtt_ctx = ecs_os_malloc_t(ecs_rtt_struct_ctx_t); ecs_vec_init_t(NULL, &rtt_ctx->vctor, ecs_rtt_call_data_t, 0); ecs_vec_init_t(NULL, &rtt_ctx->vdtor, ecs_rtt_call_data_t, 0); ecs_vec_init_t(NULL, &rtt_ctx->vmove, ecs_rtt_call_data_t, 0); ecs_vec_init_t(NULL, &rtt_ctx->vcopy, ecs_rtt_call_data_t, 0); + ecs_vec_init_t(NULL, &rtt_ctx->vcmp, ecs_rtt_call_data_t, 0); + ecs_vec_init_t(NULL, &rtt_ctx->vequals, ecs_rtt_call_data_t, 0); + hooks.lifecycle_ctx = rtt_ctx; hooks.lifecycle_ctx_free = flecs_rtt_free_lifecycle_struct_ctx; - - if (ctor) { - hooks.ctor = flecs_rtt_struct_ctor; - } - if (dtor) { - hooks.dtor = flecs_rtt_struct_dtor; - } - if (move) { - hooks.move = flecs_rtt_struct_move; - } - if (copy) { - hooks.copy = flecs_rtt_struct_copy; - } } else { hooks.lifecycle_ctx = NULL; - hooks.lifecycle_ctx_free = NULL; + hooks.lifecycle_ctx_free = flecs_rtt_free_lifecycle_nop; } - hooks.flags |= flags; + + hooks.flags = flags; hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; ecs_set_hooks_id(world, ti->component, &hooks); return rtt_ctx; @@ -52655,6 +53237,8 @@ void flecs_rtt_init_default_hooks_struct( bool dtor_hook_required = false; bool move_hook_required = false; bool copy_hook_required = false; + bool valid_cmp = true; + bool valid_equals = true; /* Iterate all struct members and see if any member type has hooks. If so, * the struct itself will need to have that hook: */ @@ -52669,6 +53253,9 @@ void flecs_rtt_init_default_hooks_struct( dtor_hook_required |= member_ti->hooks.dtor != NULL; move_hook_required |= member_ti->hooks.move != NULL; copy_hook_required |= member_ti->hooks.copy != NULL; + /* A struct has a valid cmp/equals hook if all its members have it: */ + valid_cmp &= member_ti->hooks.cmp != NULL; + valid_equals &= member_ti->hooks.equals != NULL; flags |= member_ti->hooks.flags; } @@ -52681,10 +53268,13 @@ void flecs_rtt_init_default_hooks_struct( ctor_hook_required, dtor_hook_required, move_hook_required, - copy_hook_required); + copy_hook_required, + valid_cmp, + valid_equals + ); if (!rtt_ctx) { - return; /* no hooks required */ + return; /* no hook forwarding required */ } /* At least a hook was configured, therefore examine each struct member to @@ -52737,6 +53327,24 @@ void flecs_rtt_init_default_hooks_struct( copy_data->hook.copy = flecs_rtt_default_copy; } } + if (valid_cmp) { + ecs_rtt_call_data_t *comp_data = + ecs_vec_append_t(NULL, &rtt_ctx->vcmp, ecs_rtt_call_data_t); + comp_data->offset = m->offset; + comp_data->type_info = member_ti; + comp_data->count = 1; + ecs_assert(member_ti->hooks.cmp, ECS_INTERNAL_ERROR, NULL); + comp_data->hook.cmp = member_ti->hooks.cmp; + } + if (valid_equals) { + ecs_rtt_call_data_t *comp_data = + ecs_vec_append_t(NULL, &rtt_ctx->vequals, ecs_rtt_call_data_t); + comp_data->offset = m->offset; + comp_data->type_info = member_ti; + comp_data->count = 1; + ecs_assert(member_ti->hooks.equals, ECS_INTERNAL_ERROR, NULL); + comp_data->hook.equals = member_ti->hooks.equals; + } } } @@ -52833,6 +53441,62 @@ void flecs_rtt_array_copy( } } +/* Generic array compare hook. It will invoke the compare hook of the underlying + * type for each element */ +static +int flecs_rtt_array_cmp( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *type_info) +{ + if(a_ptr == b_ptr) { + return 0; + } + + ecs_rtt_array_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_cmp_t cmp = rtt_ctx->type_info->hooks.cmp; + ecs_assert(cmp, ECS_INVALID_PARAMETER, NULL); + ecs_size_t element_size = rtt_ctx->type_info->size; + int i; + for (i = 0; i < rtt_ctx->elem_count; i++) { + const void *a_element = ECS_ELEM(a_ptr, element_size, i); + const void *b_element = ECS_ELEM(b_ptr, element_size, i); + int c = cmp(a_element, b_element, rtt_ctx->type_info); + if(c != 0) { + return c; + } + } + return 0; +} + +/* Generic array equals hook. It will invoke the equals hook of the underlying + * type for each element */ +static +bool flecs_rtt_array_equals( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *type_info) +{ + if(a_ptr == b_ptr) { + return true; + } + + ecs_rtt_array_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_equals_t equals = rtt_ctx->type_info->hooks.equals; + ecs_assert(equals, ECS_INVALID_PARAMETER, NULL); + ecs_size_t element_size = rtt_ctx->type_info->size; + int i; + for (i = 0; i < rtt_ctx->elem_count; i++) { + const void *a_element = ECS_ELEM(a_ptr, element_size, i); + const void *b_element = ECS_ELEM(b_ptr, element_size, i); + bool eq = equals(a_element, b_element, rtt_ctx->type_info); + if(!eq) { + return false; + } + } + return true; +} + /* Checks if an array's underlying type has hooks installed. If so, it generates * and installs required hooks for the array type itself. These hooks will * invoke the underlying type's hook for each element in the array. */ @@ -52843,26 +53507,44 @@ void flecs_rtt_init_default_hooks_array( { const EcsArray *array_info = ecs_get(world, component, EcsArray); ecs_assert(array_info != NULL, ECS_INTERNAL_ERROR, NULL); - const ecs_type_info_t *array_ti = + const ecs_type_info_t *element_ti = ecs_get_type_info(world, array_info->type); + ecs_flags32_t flags = element_ti->hooks.flags; bool ctor_hook_required = - array_ti->hooks.ctor && array_ti->hooks.ctor != flecs_default_ctor; - bool dtor_hook_required = array_ti->hooks.dtor != NULL; - bool move_hook_required = array_ti->hooks.move != NULL; - bool copy_hook_required = array_ti->hooks.copy != NULL; - ecs_flags32_t flags = array_ti->hooks.flags; + element_ti->hooks.ctor && element_ti->hooks.ctor != flecs_default_ctor; + bool dtor_hook_required = element_ti->hooks.dtor != NULL; + bool move_hook_required = element_ti->hooks.move != NULL; + bool copy_hook_required = element_ti->hooks.copy != NULL; + bool valid_cmp = element_ti->hooks.cmp != NULL && !(flags & ECS_TYPE_HOOK_CMP_ILLEGAL); + bool valid_equals = element_ti->hooks.equals != NULL && !(flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL); + ecs_type_hooks_t hooks = *ecs_get_hooks_id(world, component); + + hooks.ctor = ctor_hook_required && !(flags & ECS_TYPE_HOOK_CTOR_ILLEGAL) ? + flecs_rtt_array_ctor : NULL; + hooks.dtor = dtor_hook_required && !(flags & ECS_TYPE_HOOK_DTOR_ILLEGAL) ? + flecs_rtt_array_dtor : NULL; + hooks.move = move_hook_required && !(flags & ECS_TYPE_HOOK_MOVE_ILLEGAL) ? + flecs_rtt_array_move : NULL; + hooks.copy = copy_hook_required && !(flags & ECS_TYPE_HOOK_COPY_ILLEGAL) ? + flecs_rtt_array_copy : NULL; + hooks.cmp = valid_cmp && !(flags & ECS_TYPE_HOOK_CMP_ILLEGAL) ? + flecs_rtt_array_cmp : NULL; + hooks.equals = valid_equals && !(flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL) ? + flecs_rtt_array_equals : NULL; + if (hooks.lifecycle_ctx_free) { hooks.lifecycle_ctx_free(hooks.lifecycle_ctx); - hooks.lifecycle_ctx_free = NULL; + hooks.lifecycle_ctx_free = flecs_rtt_free_lifecycle_nop; } - if (ctor_hook_required || dtor_hook_required || move_hook_required || - copy_hook_required) { + if (hooks.ctor || hooks.dtor || hooks.move || + hooks.copy || hooks.cmp || hooks.equals) + { ecs_rtt_array_ctx_t *rtt_ctx = ecs_os_malloc_t(ecs_rtt_array_ctx_t); - rtt_ctx->type_info = array_ti; + rtt_ctx->type_info = element_ti; rtt_ctx->elem_count = array_info->count; if (hooks.lifecycle_ctx_free) { hooks.lifecycle_ctx_free(hooks.lifecycle_ctx); @@ -52872,23 +53554,7 @@ void flecs_rtt_init_default_hooks_array( hooks.lifecycle_ctx_free = flecs_rtt_free_lifecycle_array_ctx; } - if (ctor_hook_required) { - hooks.ctor = flecs_rtt_array_ctor; - } - - if (dtor_hook_required) { - hooks.dtor = flecs_rtt_array_dtor; - } - - if (move_hook_required) { - hooks.move = flecs_rtt_array_move; - } - - if (copy_hook_required) { - hooks.copy = flecs_rtt_array_copy; - } - - hooks.flags |= flags; + hooks.flags = flags; hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; ecs_set_hooks_id(world, component, &hooks); } @@ -53002,6 +53668,92 @@ void flecs_rtt_vector_copy( } } +/* Generic vector compare hook. */ +static +int flecs_rtt_vector_cmp( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *type_info) +{ + if(a_ptr == b_ptr) { + return 0; + } + + const ecs_vec_t *vec_a = a_ptr; + const ecs_vec_t *vec_b = b_ptr; + + ecs_size_t count_a = ecs_vec_count(vec_a); + ecs_size_t count_b = ecs_vec_count(vec_b); + { + int c = count_a - count_b; + if(c != 0) { + return c; + } + } + + ecs_rtt_vector_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_cmp_t cmp = rtt_ctx->type_info->hooks.cmp; + ecs_assert(cmp, ECS_INVALID_PARAMETER, NULL); + + ecs_size_t element_size = rtt_ctx->type_info->size; + const void *a = ecs_vec_first(vec_a); + const void *b = ecs_vec_first(vec_b); + + int i; + for (i = 0; i < count_a; i++) { + const void *a_element = ECS_ELEM(a, element_size, i); + const void *b_element = ECS_ELEM(b, element_size, i); + int c = cmp(a_element, b_element, rtt_ctx->type_info); + if(c != 0) { + return c; + } + } + return 0; +} + +/* Generic vector equals hook. */ +static +bool flecs_rtt_vector_equals( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *type_info) +{ + if(a_ptr == b_ptr) { + return true; + } + + const ecs_vec_t *vec_a = a_ptr; + const ecs_vec_t *vec_b = b_ptr; + + ecs_size_t count_a = ecs_vec_count(vec_a); + ecs_size_t count_b = ecs_vec_count(vec_b); + { + int c = count_a - count_b; + if(c != 0) { + return c; + } + } + + ecs_rtt_vector_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_equals_t equals = rtt_ctx->type_info->hooks.equals; + ecs_assert(equals, ECS_INVALID_PARAMETER, NULL); + + ecs_size_t element_size = rtt_ctx->type_info->size; + const void *a = ecs_vec_first(vec_a); + const void *b = ecs_vec_first(vec_b); + + int i; + for (i = 0; i < count_a; i++) { + const void *a_element = ECS_ELEM(a, element_size, i); + const void *b_element = ECS_ELEM(b, element_size, i); + int eq = equals(a_element, b_element, rtt_ctx->type_info); + if(!eq) { + return false; + } + } + return true; +} + /* Generates and installs required hooks for managing the vector and underlying * type lifecycle. Vectors always have hooks because at the very least the * vector structure itself must be initialized/destroyed/copied/moved, even if @@ -53013,20 +53765,41 @@ void flecs_rtt_init_default_hooks_vector( { const EcsVector *vector_info = ecs_get(world, component, EcsVector); ecs_assert(vector_info != NULL, ECS_INTERNAL_ERROR, NULL); - const ecs_type_info_t *vector_ti = + const ecs_type_info_t *element_ti = ecs_get_type_info(world, vector_info->type); ecs_rtt_vector_ctx_t *rtt_ctx = ecs_os_malloc_t(ecs_rtt_vector_ctx_t); - rtt_ctx->type_info = vector_ti; + rtt_ctx->type_info = element_ti; + ecs_flags32_t flags = element_ti->hooks.flags; + ecs_type_hooks_t hooks = *ecs_get_hooks_id(world, component); + if (hooks.lifecycle_ctx_free) { hooks.lifecycle_ctx_free(hooks.lifecycle_ctx); } hooks.lifecycle_ctx = rtt_ctx; hooks.lifecycle_ctx_free = flecs_rtt_free_lifecycle_vector_ctx; + hooks.ctor = flecs_rtt_vector_ctor; hooks.dtor = flecs_rtt_vector_dtor; hooks.move = flecs_rtt_vector_move; hooks.copy = flecs_rtt_vector_copy; + + if (element_ti->hooks.cmp != NULL && !(flags & ECS_TYPE_HOOK_CMP_ILLEGAL)) { + hooks.cmp = flecs_rtt_vector_cmp; + } else { + hooks.cmp = NULL; + } + + if (element_ti->hooks.equals != NULL && !(flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL)) { + hooks.equals = flecs_rtt_vector_equals; + } else { + hooks.equals = NULL; + } + + + /* propagate only the compare/equals hook illegal flag, if set */ + hooks.flags |= flags & (ECS_TYPE_HOOK_CMP_ILLEGAL|ECS_TYPE_HOOK_EQUALS_ILLEGAL); + hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; ecs_set_hooks_id(world, component, &hooks); } @@ -53057,27 +53830,39 @@ void flecs_rtt_init_default_hooks( * */ ecs_entity_t component = it->entities[i]; + + /* Skip configuring hooks for ids already in use */ + const ecs_world_t* w = ecs_get_world(world); + if(ecs_id_in_use(w, component) || + ecs_id_in_use(w, ecs_pair(component, EcsWildcard))) { + continue; + } + const ecs_type_info_t *ti = ecs_get_type_info(world, component); + ecs_assert(ti,ECS_INTERNAL_ERROR,NULL); - if (ti) { - if (type->kind == EcsStructType) { - flecs_rtt_init_default_hooks_struct(world, component, ti); - } else if (type->kind == EcsArrayType) { - flecs_rtt_init_default_hooks_array(world, component); - } else if (type->kind == EcsVectorType) { - flecs_rtt_init_default_hooks_vector(world, component); - } + if (type->kind == EcsStructType) { + flecs_rtt_init_default_hooks_struct(world, component, ti); + } else if (type->kind == EcsArrayType) { + flecs_rtt_init_default_hooks_array(world, component); + } else if (type->kind == EcsVectorType) { + flecs_rtt_init_default_hooks_vector(world, component); } + ecs_type_hooks_t hooks = ti->hooks; /* Make sure there is at least a default constructor. This ensures that * a new component value does not contain uninitialized memory, which * could cause serializers to crash when for example inspecting string * fields. */ - if (!ti || !ti->hooks.ctor) { - ecs_set_hooks_id(world, component, &(ecs_type_hooks_t){ - .ctor = flecs_default_ctor - }); + if(!ti->hooks.ctor && !(ti->hooks.flags & ECS_TYPE_HOOK_CTOR_ILLEGAL)) { + hooks.ctor = flecs_default_ctor; } + + hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; + ecs_set_hooks_id( + world, + component, + &hooks); } } diff --git a/distr/flecs.h b/distr/flecs.h index 397eaba1c..17fed4727 100644 --- a/distr/flecs.h +++ b/distr/flecs.h @@ -3368,6 +3368,18 @@ typedef void (*ecs_move_t)( int32_t count, const ecs_type_info_t *type_info); +/** Compare hook to compare component instances */ +typedef int (*ecs_cmp_t)( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *type_info); + +/** Equals operator hook */ +typedef bool (*ecs_equals_t)( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *type_info); + /** Destructor function for poly objects. */ typedef void (*flecs_poly_dtor_t)( ecs_poly_t *poly); @@ -3586,38 +3598,44 @@ struct ecs_observer_t { */ /* Flags that can be used to check which hooks a type has set */ -#define ECS_TYPE_HOOK_CTOR (1 << 0) -#define ECS_TYPE_HOOK_DTOR (1 << 1) -#define ECS_TYPE_HOOK_COPY (1 << 2) -#define ECS_TYPE_HOOK_MOVE (1 << 3) -#define ECS_TYPE_HOOK_COPY_CTOR (1 << 4) -#define ECS_TYPE_HOOK_MOVE_CTOR (1 << 5) -#define ECS_TYPE_HOOK_CTOR_MOVE_DTOR (1 << 6) -#define ECS_TYPE_HOOK_MOVE_DTOR (1 << 7) +#define ECS_TYPE_HOOK_CTOR ECS_CAST(ecs_flags32_t, 1 << 0) +#define ECS_TYPE_HOOK_DTOR ECS_CAST(ecs_flags32_t, 1 << 1) +#define ECS_TYPE_HOOK_COPY ECS_CAST(ecs_flags32_t, 1 << 2) +#define ECS_TYPE_HOOK_MOVE ECS_CAST(ecs_flags32_t, 1 << 3) +#define ECS_TYPE_HOOK_COPY_CTOR ECS_CAST(ecs_flags32_t, 1 << 4) +#define ECS_TYPE_HOOK_MOVE_CTOR ECS_CAST(ecs_flags32_t, 1 << 5) +#define ECS_TYPE_HOOK_CTOR_MOVE_DTOR ECS_CAST(ecs_flags32_t, 1 << 6) +#define ECS_TYPE_HOOK_MOVE_DTOR ECS_CAST(ecs_flags32_t, 1 << 7) +#define ECS_TYPE_HOOK_CMP ECS_CAST(ecs_flags32_t, 1 << 8) +#define ECS_TYPE_HOOK_EQUALS ECS_CAST(ecs_flags32_t, 1 << 9) + /* Flags that can be used to set/check which hooks of a type are invalid */ -#define ECS_TYPE_HOOK_CTOR_ILLEGAL (1 << 8) -#define ECS_TYPE_HOOK_DTOR_ILLEGAL (1 << 9) -#define ECS_TYPE_HOOK_COPY_ILLEGAL (1 << 10) -#define ECS_TYPE_HOOK_MOVE_ILLEGAL (1 << 11) -#define ECS_TYPE_HOOK_COPY_CTOR_ILLEGAL (1 << 12) -#define ECS_TYPE_HOOK_MOVE_CTOR_ILLEGAL (1 << 13) -#define ECS_TYPE_HOOK_CTOR_MOVE_DTOR_ILLEGAL (1 << 14) -#define ECS_TYPE_HOOK_MOVE_DTOR_ILLEGAL (1 << 15) +#define ECS_TYPE_HOOK_CTOR_ILLEGAL ECS_CAST(ecs_flags32_t, 1 << 10) +#define ECS_TYPE_HOOK_DTOR_ILLEGAL ECS_CAST(ecs_flags32_t, 1 << 12) +#define ECS_TYPE_HOOK_COPY_ILLEGAL ECS_CAST(ecs_flags32_t, 1 << 13) +#define ECS_TYPE_HOOK_MOVE_ILLEGAL ECS_CAST(ecs_flags32_t, 1 << 14) +#define ECS_TYPE_HOOK_COPY_CTOR_ILLEGAL ECS_CAST(ecs_flags32_t, 1 << 15) +#define ECS_TYPE_HOOK_MOVE_CTOR_ILLEGAL ECS_CAST(ecs_flags32_t, 1 << 16) +#define ECS_TYPE_HOOK_CTOR_MOVE_DTOR_ILLEGAL ECS_CAST(ecs_flags32_t, 1 << 17) +#define ECS_TYPE_HOOK_MOVE_DTOR_ILLEGAL ECS_CAST(ecs_flags32_t, 1 << 18) +#define ECS_TYPE_HOOK_CMP_ILLEGAL ECS_CAST(ecs_flags32_t, 1 << 19) +#define ECS_TYPE_HOOK_EQUALS_ILLEGAL ECS_CAST(ecs_flags32_t, 1 << 20) + /* All valid hook flags */ #define ECS_TYPE_HOOKS (ECS_TYPE_HOOK_CTOR|ECS_TYPE_HOOK_DTOR|\ ECS_TYPE_HOOK_COPY|ECS_TYPE_HOOK_MOVE|ECS_TYPE_HOOK_COPY_CTOR|\ ECS_TYPE_HOOK_MOVE_CTOR|ECS_TYPE_HOOK_CTOR_MOVE_DTOR|\ - ECS_TYPE_HOOK_MOVE_DTOR) + ECS_TYPE_HOOK_MOVE_DTOR|ECS_TYPE_HOOK_CMP|ECS_TYPE_HOOK_EQUALS) /* All invalid hook flags */ #define ECS_TYPE_HOOKS_ILLEGAL (ECS_TYPE_HOOK_CTOR_ILLEGAL|\ ECS_TYPE_HOOK_DTOR_ILLEGAL|ECS_TYPE_HOOK_COPY_ILLEGAL|\ ECS_TYPE_HOOK_MOVE_ILLEGAL|ECS_TYPE_HOOK_COPY_CTOR_ILLEGAL|\ ECS_TYPE_HOOK_MOVE_CTOR_ILLEGAL|ECS_TYPE_HOOK_CTOR_MOVE_DTOR_ILLEGAL|\ - ECS_TYPE_HOOK_MOVE_DTOR_ILLEGAL) - + ECS_TYPE_HOOK_MOVE_DTOR_ILLEGAL|ECS_TYPE_HOOK_CMP_ILLEGAL|\ + ECS_TYPE_HOOK_EQUALS_ILLEGAL) struct ecs_type_hooks_t { ecs_xtor_t ctor; /**< ctor */ ecs_xtor_t dtor; /**< dtor */ @@ -3642,11 +3660,18 @@ struct ecs_type_hooks_t { * not set explicitly it will be derived from other callbacks. */ ecs_move_t move_dtor; + /** Compare hook */ + ecs_cmp_t cmp; + + /** Equals hook */ + ecs_equals_t equals; + /** Hook flags. * Indicates which hooks are set for the type, and which hooks are illegal. * When an ILLEGAL flag is set when calling ecs_set_hooks() a hook callback * will be set that panics when called. */ ecs_flags32_t flags; + /** Callback that is invoked when an instance of a component is added. This * callback is invoked before triggers are invoked. */ @@ -21068,6 +21093,157 @@ ecs_move_t move_dtor(ecs_flags32_t &) { return move_dtor_impl; } +// Traits to check for operator<, operator>, and operator== +template +using void_t = void; + +// These traits causes a "float comparison warning" in some compilers +// when `T` is float or double. +// Disable this warning with the following pragmas: +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wfloat-equal" +#elif defined(__GNUC__) && !defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +// Trait to check for operator< +template +struct has_operator_less : std::false_type {}; + +// Only enable if T has an operator< that takes T as the right-hand side (no implicit conversion) +template +struct has_operator_less() < std::declval())>> : + std::is_same() < std::declval()), bool> {}; + +// Trait to check for operator> +template +struct has_operator_greater : std::false_type {}; + +// Only enable if T has an operator> that takes T as the right-hand side (no implicit conversion) +template +struct has_operator_greater() > std::declval())>> : + std::is_same() > std::declval()), bool> {}; + +// Trait to check for operator== +template +struct has_operator_equal : std::false_type {}; + +// Only enable if T has an operator== that takes T as the right-hand side (no implicit conversion) +template +struct has_operator_equal() == std::declval())>> : + std::is_same() == std::declval()), bool> {}; + +// 1. Compare function if `<`, `>`, are defined +template ::value && + has_operator_greater::value && + !has_operator_equal::value > = 0> +int compare_impl(const void *a, const void *b, const ecs_type_info_t *) { + const T& lhs = *static_cast(a); + const T& rhs = *static_cast(b); + if (lhs < rhs) return -1; + if (lhs > rhs) return 1; + return 0; +} + +// 2. Compare function if `<` and `==` are defined, ignoring `>` +// if defined. +template ::value && + has_operator_equal::value > = 0> +int compare_impl(const void *a, const void *b, const ecs_type_info_t *) { + const T& lhs = *static_cast(a); + const T& rhs = *static_cast(b); + if (lhs == rhs) return 0; + if (lhs < rhs) return -1; + return 1; // If not less and not equal, must be greater +} + +// 3. Compare function if `>` and `==` are defined, deducing `<` +template ::value && + has_operator_equal::value && + !has_operator_less::value > = 0> +int compare_impl(const void *a, const void *b, const ecs_type_info_t *) { + const T& lhs = *static_cast(a); + const T& rhs = *static_cast(b); + if (lhs == rhs) return 0; + if (lhs > rhs) return 1; + return -1; // If not greater and not equal, must be less +} + +// 4. Compare function if only `<` is defined, deducing the rest +template ::value && + !has_operator_greater::value && + !has_operator_equal::value > = 0> +int compare_impl(const void *a, const void *b, const ecs_type_info_t *) { + const T& lhs = *static_cast(a); + const T& rhs = *static_cast(b); + if (lhs < rhs) return -1; + if (rhs < lhs) return 1; + return 0; // If neither is less, they must be equal +} + +// 5. Compare function if only `>` is defined, deducing the rest +template ::value && + !has_operator_less::value && + !has_operator_equal::value > = 0> +int compare_impl(const void *a, const void *b, const ecs_type_info_t *) { + const T& lhs = *static_cast(a); + const T& rhs = *static_cast(b); + if (lhs > rhs) return 1; + if (rhs > lhs) return -1; + return 0; // If neither is greater, they must be equal +} + +// In order to have a generated compare hook, at least +// operator> or operator< must be defined: +template ::value || + has_operator_greater::value > = 0> +ecs_cmp_t compare() { + return compare_impl; +} + +template ::value && + !has_operator_greater::value > = 0> +ecs_cmp_t compare() { + return NULL; +} + +// Equals function enabled only if `==` is defined +template ::value > = 0> +bool equals_impl(const void *a, const void *b, const ecs_type_info_t *) { + const T& lhs = *static_cast(a); + const T& rhs = *static_cast(b); + return lhs == rhs; +} + +template ::value > = 0> +ecs_equals_t equals() { + return equals_impl; +} + +template ::value > = 0> +ecs_equals_t equals() { + return NULL; +} + +// re-enable the float comparison warning: +#if defined(__clang__) + #pragma clang diagnostic pop +#elif defined(__GNUC__) && !defined(__clang__) + #pragma GCC diagnostic pop +#endif + } // _ } // flecs @@ -27473,6 +27649,7 @@ void register_lifecycle_actions( cl.ctor_move_dtor = ctor_move_dtor(cl.flags); cl.move_dtor = move_dtor(cl.flags); + cl.flags &= ECS_TYPE_HOOKS_ILLEGAL; ecs_set_hooks_id(world, component, &cl); if (cl.flags & (ECS_TYPE_HOOK_MOVE_ILLEGAL|ECS_TYPE_HOOK_MOVE_CTOR_ILLEGAL)) @@ -27681,6 +27858,50 @@ struct type::value >> struct untyped_component : entity { using entity::entity; +protected: + +flecs::type_hooks_t get_hooks() const { + const flecs::type_hooks_t* h = ecs_get_hooks_id(world_, id_); + if (h) { + return *h; + } else { + return {}; + } +} + +void set_hooks(flecs::type_hooks_t &h) { + h.flags &= ECS_TYPE_HOOKS_ILLEGAL; + ecs_set_hooks_id(world_, id_, &h); +} + +public: + +untyped_component& on_compare( + ecs_cmp_t compare_callback) +{ + ecs_assert(compare_callback, ECS_INVALID_PARAMETER, NULL); + flecs::type_hooks_t h = get_hooks(); + h.cmp = compare_callback; + h.flags &= ~ECS_TYPE_HOOK_CMP_ILLEGAL; + if(h.flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL) { + h.flags &= ~ECS_TYPE_HOOK_EQUALS_ILLEGAL; + h.equals = NULL; + } + set_hooks(h); + return *this; +} + +untyped_component& on_equals( + ecs_equals_t equals_callback) +{ + ecs_assert(equals_callback, ECS_INVALID_PARAMETER, NULL); + flecs::type_hooks_t h = get_hooks(); + h.equals = equals_callback; + h.flags &= ~ECS_TYPE_HOOK_EQUALS_ILLEGAL; + set_hooks(h); + return *this; +} + # ifdef FLECS_META /** * @file addons/cpp/mixins/meta/untyped_component.inl @@ -28109,7 +28330,7 @@ struct component : untyped_component { h.on_add = Delegate::run_add; ctx->on_add = FLECS_NEW(Delegate)(FLECS_FWD(func)); ctx->free_on_add = _::free_obj; - ecs_set_hooks_id(world_, id_, &h); + set_hooks(h); return *this; } @@ -28125,7 +28346,7 @@ struct component : untyped_component { h.on_remove = Delegate::run_remove; ctx->on_remove = FLECS_NEW(Delegate)(FLECS_FWD(func)); ctx->free_on_remove = _::free_obj; - ecs_set_hooks_id(world_, id_, &h); + set_hooks(h); return *this; } @@ -28141,7 +28362,41 @@ struct component : untyped_component { h.on_set = Delegate::run_set; ctx->on_set = FLECS_NEW(Delegate)(FLECS_FWD(func)); ctx->free_on_set = _::free_obj; - ecs_set_hooks_id(world_, id_, &h); + set_hooks(h); + return *this; + } + + /** Register operator compare hook. */ + using untyped_component::on_compare; + component& on_compare() { + ecs_cmp_t handler = _::compare(); + ecs_assert(handler != NULL, ECS_INVALID_OPERATION, + "Type does not have operator> or operator< const or is inaccessible"); + on_compare(handler); + return *this; + } + + /** Type safe variant of compare op function */ + using cmp_hook = int(*)(const T* a, const T* b, const ecs_type_info_t *ti); + component& on_compare(cmp_hook callback) { + on_compare(reinterpret_cast(callback)); + return *this; + } + + /** Register operator equals hook. */ + using untyped_component::on_equals; + component& on_equals() { + ecs_equals_t handler = _::equals(); + ecs_assert(handler != NULL, ECS_INVALID_OPERATION, + "Type does not have operator== const or is inaccessible"); + on_equals(handler); + return *this; + } + + /** Type safe variant of equals op function */ + using equals_hook = bool(*)(const T* a, const T* b, const ecs_type_info_t *ti); + component& on_equals(equals_hook callback) { + on_equals(reinterpret_cast(callback)); return *this; } @@ -28209,15 +28464,6 @@ component& constant(const char *name, T value) { } return result; } - - flecs::type_hooks_t get_hooks() { - const flecs::type_hooks_t* h = ecs_get_hooks_id(world_, id_); - if (h) { - return *h; - } else { - return {}; - } - } }; } diff --git a/include/flecs.h b/include/flecs.h index 6602e43cb..400b4e557 100644 --- a/include/flecs.h +++ b/include/flecs.h @@ -672,6 +672,18 @@ typedef void (*ecs_move_t)( int32_t count, const ecs_type_info_t *type_info); +/** Compare hook to compare component instances */ +typedef int (*ecs_cmp_t)( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *type_info); + +/** Equals operator hook */ +typedef bool (*ecs_equals_t)( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *type_info); + /** Destructor function for poly objects. */ typedef void (*flecs_poly_dtor_t)( ecs_poly_t *poly); @@ -890,38 +902,44 @@ struct ecs_observer_t { */ /* Flags that can be used to check which hooks a type has set */ -#define ECS_TYPE_HOOK_CTOR (1 << 0) -#define ECS_TYPE_HOOK_DTOR (1 << 1) -#define ECS_TYPE_HOOK_COPY (1 << 2) -#define ECS_TYPE_HOOK_MOVE (1 << 3) -#define ECS_TYPE_HOOK_COPY_CTOR (1 << 4) -#define ECS_TYPE_HOOK_MOVE_CTOR (1 << 5) -#define ECS_TYPE_HOOK_CTOR_MOVE_DTOR (1 << 6) -#define ECS_TYPE_HOOK_MOVE_DTOR (1 << 7) +#define ECS_TYPE_HOOK_CTOR ECS_CAST(ecs_flags32_t, 1 << 0) +#define ECS_TYPE_HOOK_DTOR ECS_CAST(ecs_flags32_t, 1 << 1) +#define ECS_TYPE_HOOK_COPY ECS_CAST(ecs_flags32_t, 1 << 2) +#define ECS_TYPE_HOOK_MOVE ECS_CAST(ecs_flags32_t, 1 << 3) +#define ECS_TYPE_HOOK_COPY_CTOR ECS_CAST(ecs_flags32_t, 1 << 4) +#define ECS_TYPE_HOOK_MOVE_CTOR ECS_CAST(ecs_flags32_t, 1 << 5) +#define ECS_TYPE_HOOK_CTOR_MOVE_DTOR ECS_CAST(ecs_flags32_t, 1 << 6) +#define ECS_TYPE_HOOK_MOVE_DTOR ECS_CAST(ecs_flags32_t, 1 << 7) +#define ECS_TYPE_HOOK_CMP ECS_CAST(ecs_flags32_t, 1 << 8) +#define ECS_TYPE_HOOK_EQUALS ECS_CAST(ecs_flags32_t, 1 << 9) + /* Flags that can be used to set/check which hooks of a type are invalid */ -#define ECS_TYPE_HOOK_CTOR_ILLEGAL (1 << 8) -#define ECS_TYPE_HOOK_DTOR_ILLEGAL (1 << 9) -#define ECS_TYPE_HOOK_COPY_ILLEGAL (1 << 10) -#define ECS_TYPE_HOOK_MOVE_ILLEGAL (1 << 11) -#define ECS_TYPE_HOOK_COPY_CTOR_ILLEGAL (1 << 12) -#define ECS_TYPE_HOOK_MOVE_CTOR_ILLEGAL (1 << 13) -#define ECS_TYPE_HOOK_CTOR_MOVE_DTOR_ILLEGAL (1 << 14) -#define ECS_TYPE_HOOK_MOVE_DTOR_ILLEGAL (1 << 15) +#define ECS_TYPE_HOOK_CTOR_ILLEGAL ECS_CAST(ecs_flags32_t, 1 << 10) +#define ECS_TYPE_HOOK_DTOR_ILLEGAL ECS_CAST(ecs_flags32_t, 1 << 12) +#define ECS_TYPE_HOOK_COPY_ILLEGAL ECS_CAST(ecs_flags32_t, 1 << 13) +#define ECS_TYPE_HOOK_MOVE_ILLEGAL ECS_CAST(ecs_flags32_t, 1 << 14) +#define ECS_TYPE_HOOK_COPY_CTOR_ILLEGAL ECS_CAST(ecs_flags32_t, 1 << 15) +#define ECS_TYPE_HOOK_MOVE_CTOR_ILLEGAL ECS_CAST(ecs_flags32_t, 1 << 16) +#define ECS_TYPE_HOOK_CTOR_MOVE_DTOR_ILLEGAL ECS_CAST(ecs_flags32_t, 1 << 17) +#define ECS_TYPE_HOOK_MOVE_DTOR_ILLEGAL ECS_CAST(ecs_flags32_t, 1 << 18) +#define ECS_TYPE_HOOK_CMP_ILLEGAL ECS_CAST(ecs_flags32_t, 1 << 19) +#define ECS_TYPE_HOOK_EQUALS_ILLEGAL ECS_CAST(ecs_flags32_t, 1 << 20) + /* All valid hook flags */ #define ECS_TYPE_HOOKS (ECS_TYPE_HOOK_CTOR|ECS_TYPE_HOOK_DTOR|\ ECS_TYPE_HOOK_COPY|ECS_TYPE_HOOK_MOVE|ECS_TYPE_HOOK_COPY_CTOR|\ ECS_TYPE_HOOK_MOVE_CTOR|ECS_TYPE_HOOK_CTOR_MOVE_DTOR|\ - ECS_TYPE_HOOK_MOVE_DTOR) + ECS_TYPE_HOOK_MOVE_DTOR|ECS_TYPE_HOOK_CMP|ECS_TYPE_HOOK_EQUALS) /* All invalid hook flags */ #define ECS_TYPE_HOOKS_ILLEGAL (ECS_TYPE_HOOK_CTOR_ILLEGAL|\ ECS_TYPE_HOOK_DTOR_ILLEGAL|ECS_TYPE_HOOK_COPY_ILLEGAL|\ ECS_TYPE_HOOK_MOVE_ILLEGAL|ECS_TYPE_HOOK_COPY_CTOR_ILLEGAL|\ ECS_TYPE_HOOK_MOVE_CTOR_ILLEGAL|ECS_TYPE_HOOK_CTOR_MOVE_DTOR_ILLEGAL|\ - ECS_TYPE_HOOK_MOVE_DTOR_ILLEGAL) - + ECS_TYPE_HOOK_MOVE_DTOR_ILLEGAL|ECS_TYPE_HOOK_CMP_ILLEGAL|\ + ECS_TYPE_HOOK_EQUALS_ILLEGAL) struct ecs_type_hooks_t { ecs_xtor_t ctor; /**< ctor */ ecs_xtor_t dtor; /**< dtor */ @@ -946,11 +964,18 @@ struct ecs_type_hooks_t { * not set explicitly it will be derived from other callbacks. */ ecs_move_t move_dtor; + /** Compare hook */ + ecs_cmp_t cmp; + + /** Equals hook */ + ecs_equals_t equals; + /** Hook flags. * Indicates which hooks are set for the type, and which hooks are illegal. * When an ILLEGAL flag is set when calling ecs_set_hooks() a hook callback * will be set that panics when called. */ ecs_flags32_t flags; + /** Callback that is invoked when an instance of a component is added. This * callback is invoked before triggers are invoked. */ diff --git a/include/flecs/addons/cpp/component.hpp b/include/flecs/addons/cpp/component.hpp index c95814884..fbd8ba68a 100644 --- a/include/flecs/addons/cpp/component.hpp +++ b/include/flecs/addons/cpp/component.hpp @@ -112,6 +112,7 @@ void register_lifecycle_actions( cl.ctor_move_dtor = ctor_move_dtor(cl.flags); cl.move_dtor = move_dtor(cl.flags); + cl.flags &= ECS_TYPE_HOOKS_ILLEGAL; ecs_set_hooks_id(world, component, &cl); if (cl.flags & (ECS_TYPE_HOOK_MOVE_ILLEGAL|ECS_TYPE_HOOK_MOVE_CTOR_ILLEGAL)) @@ -320,6 +321,50 @@ struct type::value >> struct untyped_component : entity { using entity::entity; +protected: + +flecs::type_hooks_t get_hooks() const { + const flecs::type_hooks_t* h = ecs_get_hooks_id(world_, id_); + if (h) { + return *h; + } else { + return {}; + } +} + +void set_hooks(flecs::type_hooks_t &h) { + h.flags &= ECS_TYPE_HOOKS_ILLEGAL; + ecs_set_hooks_id(world_, id_, &h); +} + +public: + +untyped_component& on_compare( + ecs_cmp_t compare_callback) +{ + ecs_assert(compare_callback, ECS_INVALID_PARAMETER, NULL); + flecs::type_hooks_t h = get_hooks(); + h.cmp = compare_callback; + h.flags &= ~ECS_TYPE_HOOK_CMP_ILLEGAL; + if(h.flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL) { + h.flags &= ~ECS_TYPE_HOOK_EQUALS_ILLEGAL; + h.equals = NULL; + } + set_hooks(h); + return *this; +} + +untyped_component& on_equals( + ecs_equals_t equals_callback) +{ + ecs_assert(equals_callback, ECS_INVALID_PARAMETER, NULL); + flecs::type_hooks_t h = get_hooks(); + h.equals = equals_callback; + h.flags &= ~ECS_TYPE_HOOK_EQUALS_ILLEGAL; + set_hooks(h); + return *this; +} + # ifdef FLECS_META # include "mixins/meta/untyped_component.inl" # endif @@ -404,7 +449,7 @@ struct component : untyped_component { h.on_add = Delegate::run_add; ctx->on_add = FLECS_NEW(Delegate)(FLECS_FWD(func)); ctx->free_on_add = _::free_obj; - ecs_set_hooks_id(world_, id_, &h); + set_hooks(h); return *this; } @@ -420,7 +465,7 @@ struct component : untyped_component { h.on_remove = Delegate::run_remove; ctx->on_remove = FLECS_NEW(Delegate)(FLECS_FWD(func)); ctx->free_on_remove = _::free_obj; - ecs_set_hooks_id(world_, id_, &h); + set_hooks(h); return *this; } @@ -436,7 +481,41 @@ struct component : untyped_component { h.on_set = Delegate::run_set; ctx->on_set = FLECS_NEW(Delegate)(FLECS_FWD(func)); ctx->free_on_set = _::free_obj; - ecs_set_hooks_id(world_, id_, &h); + set_hooks(h); + return *this; + } + + /** Register operator compare hook. */ + using untyped_component::on_compare; + component& on_compare() { + ecs_cmp_t handler = _::compare(); + ecs_assert(handler != NULL, ECS_INVALID_OPERATION, + "Type does not have operator> or operator< const or is inaccessible"); + on_compare(handler); + return *this; + } + + /** Type safe variant of compare op function */ + using cmp_hook = int(*)(const T* a, const T* b, const ecs_type_info_t *ti); + component& on_compare(cmp_hook callback) { + on_compare(reinterpret_cast(callback)); + return *this; + } + + /** Register operator equals hook. */ + using untyped_component::on_equals; + component& on_equals() { + ecs_equals_t handler = _::equals(); + ecs_assert(handler != NULL, ECS_INVALID_OPERATION, + "Type does not have operator== const or is inaccessible"); + on_equals(handler); + return *this; + } + + /** Type safe variant of equals op function */ + using equals_hook = bool(*)(const T* a, const T* b, const ecs_type_info_t *ti); + component& on_equals(equals_hook callback) { + on_equals(reinterpret_cast(callback)); return *this; } @@ -456,15 +535,6 @@ struct component : untyped_component { } return result; } - - flecs::type_hooks_t get_hooks() { - const flecs::type_hooks_t* h = ecs_get_hooks_id(world_, id_); - if (h) { - return *h; - } else { - return {}; - } - } }; } diff --git a/include/flecs/addons/cpp/lifecycle_traits.hpp b/include/flecs/addons/cpp/lifecycle_traits.hpp index 425d28290..45a0b6a19 100644 --- a/include/flecs/addons/cpp/lifecycle_traits.hpp +++ b/include/flecs/addons/cpp/lifecycle_traits.hpp @@ -348,5 +348,156 @@ ecs_move_t move_dtor(ecs_flags32_t &) { return move_dtor_impl; } +// Traits to check for operator<, operator>, and operator== +template +using void_t = void; + +// These traits causes a "float comparison warning" in some compilers +// when `T` is float or double. +// Disable this warning with the following pragmas: +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wfloat-equal" +#elif defined(__GNUC__) && !defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +// Trait to check for operator< +template +struct has_operator_less : std::false_type {}; + +// Only enable if T has an operator< that takes T as the right-hand side (no implicit conversion) +template +struct has_operator_less() < std::declval())>> : + std::is_same() < std::declval()), bool> {}; + +// Trait to check for operator> +template +struct has_operator_greater : std::false_type {}; + +// Only enable if T has an operator> that takes T as the right-hand side (no implicit conversion) +template +struct has_operator_greater() > std::declval())>> : + std::is_same() > std::declval()), bool> {}; + +// Trait to check for operator== +template +struct has_operator_equal : std::false_type {}; + +// Only enable if T has an operator== that takes T as the right-hand side (no implicit conversion) +template +struct has_operator_equal() == std::declval())>> : + std::is_same() == std::declval()), bool> {}; + +// 1. Compare function if `<`, `>`, are defined +template ::value && + has_operator_greater::value && + !has_operator_equal::value > = 0> +int compare_impl(const void *a, const void *b, const ecs_type_info_t *) { + const T& lhs = *static_cast(a); + const T& rhs = *static_cast(b); + if (lhs < rhs) return -1; + if (lhs > rhs) return 1; + return 0; +} + +// 2. Compare function if `<` and `==` are defined, ignoring `>` +// if defined. +template ::value && + has_operator_equal::value > = 0> +int compare_impl(const void *a, const void *b, const ecs_type_info_t *) { + const T& lhs = *static_cast(a); + const T& rhs = *static_cast(b); + if (lhs == rhs) return 0; + if (lhs < rhs) return -1; + return 1; // If not less and not equal, must be greater +} + +// 3. Compare function if `>` and `==` are defined, deducing `<` +template ::value && + has_operator_equal::value && + !has_operator_less::value > = 0> +int compare_impl(const void *a, const void *b, const ecs_type_info_t *) { + const T& lhs = *static_cast(a); + const T& rhs = *static_cast(b); + if (lhs == rhs) return 0; + if (lhs > rhs) return 1; + return -1; // If not greater and not equal, must be less +} + +// 4. Compare function if only `<` is defined, deducing the rest +template ::value && + !has_operator_greater::value && + !has_operator_equal::value > = 0> +int compare_impl(const void *a, const void *b, const ecs_type_info_t *) { + const T& lhs = *static_cast(a); + const T& rhs = *static_cast(b); + if (lhs < rhs) return -1; + if (rhs < lhs) return 1; + return 0; // If neither is less, they must be equal +} + +// 5. Compare function if only `>` is defined, deducing the rest +template ::value && + !has_operator_less::value && + !has_operator_equal::value > = 0> +int compare_impl(const void *a, const void *b, const ecs_type_info_t *) { + const T& lhs = *static_cast(a); + const T& rhs = *static_cast(b); + if (lhs > rhs) return 1; + if (rhs > lhs) return -1; + return 0; // If neither is greater, they must be equal +} + +// In order to have a generated compare hook, at least +// operator> or operator< must be defined: +template ::value || + has_operator_greater::value > = 0> +ecs_cmp_t compare() { + return compare_impl; +} + +template ::value && + !has_operator_greater::value > = 0> +ecs_cmp_t compare() { + return NULL; +} + +// Equals function enabled only if `==` is defined +template ::value > = 0> +bool equals_impl(const void *a, const void *b, const ecs_type_info_t *) { + const T& lhs = *static_cast(a); + const T& rhs = *static_cast(b); + return lhs == rhs; +} + +template ::value > = 0> +ecs_equals_t equals() { + return equals_impl; +} + +template ::value > = 0> +ecs_equals_t equals() { + return NULL; +} + +// re-enable the float comparison warning: +#if defined(__clang__) + #pragma clang diagnostic pop +#elif defined(__GNUC__) && !defined(__clang__) + #pragma GCC diagnostic pop +#endif + } // _ } // flecs diff --git a/src/addons/meta/definitions.c b/src/addons/meta/definitions.c index 52c8701ce..abfa38e99 100644 --- a/src/addons/meta/definitions.c +++ b/src/addons/meta/definitions.c @@ -82,7 +82,11 @@ void flecs_meta_import_core_definitions( }), .type = { .size = ECS_SIZEOF(const char*), - .alignment = ECS_ALIGNOF(const char*) + .alignment = ECS_ALIGNOF(const char*), + .hooks = { + .cmp = ecs_compare_string, + .equals = ecs_equals_string + } } }), .type = { diff --git a/src/addons/meta/meta.c b/src/addons/meta/meta.c index 285437586..4720db68a 100644 --- a/src/addons/meta/meta.c +++ b/src/addons/meta/meta.c @@ -25,6 +25,386 @@ static ECS_DTOR(ecs_string_t, ptr, { *(ecs_string_t*)ptr = NULL; }) +/* Primitive comparers */ + +static +int ecs_compare_bool( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + return (int)(*((const ecs_bool_t*)a_ptr)) - (int)(*((const ecs_bool_t*)b_ptr)); +} + +static +bool ecs_equals_bool( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_bool(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_char( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + return (int)(*((const ecs_char_t*)a_ptr)) - (int)(*((const ecs_char_t*)b_ptr)); +} + +static +bool ecs_equals_char( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_char(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_byte( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + return (int)(*((const ecs_byte_t*)a_ptr)) - (int)(*((const ecs_byte_t*)b_ptr)); +} + +static +bool ecs_equals_byte( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_byte(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_u8( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + return (int)(*((const ecs_u8_t*)a_ptr)) - (int)(*((const ecs_u8_t*)b_ptr)); +} + +static +bool ecs_equals_u8( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_u8(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_u16( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + return (int)(*((const ecs_u16_t*)a_ptr)) - (int)(*((const ecs_u16_t*)b_ptr)); +} + +static +bool ecs_equals_u16( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_u16(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_u32( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + ecs_u32_t a = *((const ecs_u32_t*)a_ptr); + ecs_u32_t b = *((const ecs_u32_t*)b_ptr); + return (a > b) - (a < b); +} + +static +bool ecs_equals_u32( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_u32(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_u64( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + ecs_u64_t a = *((const ecs_u64_t*)a_ptr); + ecs_u64_t b = *((const ecs_u64_t*)b_ptr); + return (a > b) - (a < b); +} + +static +bool ecs_equals_u64( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_u64(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_uptr( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + ecs_uptr_t a = *((const ecs_uptr_t*)a_ptr); + ecs_uptr_t b = *((const ecs_uptr_t*)b_ptr); + return (a > b) - (a < b); +} + +static +bool ecs_equals_uptr( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_uptr(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_i8( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + return (int)(*((const ecs_i8_t*)a_ptr)) - + (int)(*((const ecs_i8_t*)b_ptr)); +} + +static +bool ecs_equals_i8( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_i8(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_i16( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + return (int)(*((const ecs_i16_t*)a_ptr)) - + (int)(*((const ecs_i16_t*)b_ptr)); +} + +static +bool ecs_equals_i16( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_i16(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_i32( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + ecs_i32_t a = *((const ecs_i32_t*)a_ptr); + ecs_i32_t b = *((const ecs_i32_t*)b_ptr); + return (a > b) - (a < b); +} + +static +bool ecs_equals_i32( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_i32(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_i64( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + ecs_i64_t a = *((const ecs_i64_t*)a_ptr); + ecs_i64_t b = *((const ecs_i64_t*)b_ptr); + return (a > b) - (a < b); +} + +static +bool ecs_equals_i64( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_i64(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_iptr( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + ecs_iptr_t a = *((const ecs_iptr_t*)a_ptr); + ecs_iptr_t b = *((const ecs_iptr_t*)b_ptr); + return (a > b) - (a < b); +} + +static +bool ecs_equals_iptr( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_iptr(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_f32( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + ecs_f32_t a = *((const ecs_f32_t*)a_ptr); + ecs_f32_t b = *((const ecs_f32_t*)b_ptr); + if (a < b) return -1; + if (a > b) return 1; + return 0; +} + +static +bool ecs_equals_f32( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + /* intentional equal check as if it was an integer */ + return ecs_compare_u32(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_f64( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + ecs_f64_t a = *((const ecs_f64_t*)a_ptr); + ecs_f64_t b = *((const ecs_f64_t*)b_ptr); + if (a < b) return -1; + if (a > b) return 1; + return 0; +} + +static +bool ecs_equals_f64( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + /* intentional equal check as if it was an integer */ + return ecs_compare_u64(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_entity( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + ecs_entity_t a = *((const ecs_entity_t*)a_ptr); + ecs_entity_t b = *((const ecs_entity_t*)b_ptr); + return (a > b) - (a < b); +} + +static +bool ecs_equals_entity( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_entity(a_ptr, b_ptr, ti) == 0; +} + +static +int ecs_compare_id( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + (void)ti; + ecs_id_t a = *((const ecs_id_t*)a_ptr); + ecs_id_t b = *((const ecs_id_t*)b_ptr); + return (a > b) - (a < b); +} + +static +bool ecs_equals_id( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_id(a_ptr, b_ptr, ti) == 0; +} + +int ecs_compare_string( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) { + (void)ti; + const ecs_string_t str_a = *((const ecs_string_t *) a_ptr); + const ecs_string_t str_b = *((const ecs_string_t *) b_ptr); + if(str_a == str_b) { + return 0; + } + if(str_a == NULL) { + return -1; + } + if(str_b == NULL) { + return 1; + } + return ecs_os_strcmp(str_a, str_b); +} + +bool ecs_equals_string( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *ti) +{ + return ecs_compare_string(a_ptr, b_ptr, ti) == 0; +} + /* EcsTypeSerializer lifecycle */ @@ -268,8 +648,24 @@ int flecs_init_type( * serializers on uninitialized values. For runtime types (rtt), the default hooks are set by flecs_meta_rtt_init_default_hooks */ ecs_type_info_t *ti = flecs_type_info_ensure(world, type); - if (meta_type->existing && !ti->hooks.ctor) { - ti->hooks.ctor = flecs_default_ctor; + if (meta_type->existing) { + if(!ti->hooks.ctor) { + ti->hooks.ctor = flecs_default_ctor; + } + if(kind == EcsEnumType) { + /* Generate compare/equals hooks for enums, copying + the underlying type's hooks, which should be + any of the default primitive integral compare hooks, + i.e. ecs_compare_i8, _i16 _32... */ + const EcsEnum* enum_info = ecs_get(world, type, EcsEnum); + ecs_assert(enum_info != NULL, ECS_INTERNAL_ERROR, NULL); + const ecs_type_hooks_t *enum_hooks = ecs_get_hooks_id(world, enum_info->underlying_type); + ecs_assert(!(enum_hooks->flags & (ECS_TYPE_HOOK_CMP_ILLEGAL|ECS_TYPE_HOOK_EQUALS_ILLEGAL)), ECS_INTERNAL_ERROR, NULL); + ti->hooks.cmp = enum_hooks->cmp; + ti->hooks.equals = enum_hooks->equals; + ti->hooks.flags &= ~(ECS_TYPE_HOOK_CMP_ILLEGAL|ECS_TYPE_HOOK_EQUALS_ILLEGAL); + ti->hooks.flags |= ECS_TYPE_HOOK_CMP|ECS_TYPE_HOOK_EQUALS; + } } } else { if (meta_type->kind != kind) { @@ -1506,7 +1902,11 @@ void FlecsMetaImport( .symbol = #type });\ ecs_set(world, ecs_id(ecs_##type##_t), EcsPrimitive, {\ .kind = primitive_kind\ - }); + });\ + ecs_set_hooks(world, ecs_##type##_t, { \ + .cmp = ecs_compare_##type, \ + .equals = ecs_equals_##type \ + }) ECS_PRIMITIVE(world, bool, EcsBool); ECS_PRIMITIVE(world, char, EcsChar); @@ -1529,12 +1929,14 @@ void FlecsMetaImport( #undef ECS_PRIMITIVE - ecs_set_hooks(world, ecs_string_t, { - .ctor = flecs_default_ctor, - .copy = ecs_copy(ecs_string_t), - .move = ecs_move(ecs_string_t), - .dtor = ecs_dtor(ecs_string_t) - }); + ecs_type_hooks_t string_hooks = *ecs_get_hooks(world, ecs_string_t); + string_hooks.ctor = flecs_default_ctor; + string_hooks.copy = ecs_copy(ecs_string_t); + string_hooks.move = ecs_move(ecs_string_t); + string_hooks.dtor = ecs_dtor(ecs_string_t); + string_hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; + ecs_set_hooks_id(world, ecs_id(ecs_string_t), &string_hooks); + /* Set default child components. Can be used as hint by deserializers */ ecs_set(world, ecs_id(EcsStruct), EcsDefaultChildComponent, {ecs_id(EcsMember)}); diff --git a/src/addons/meta/meta.h b/src/addons/meta/meta.h index 0f2d401c8..8c3d66712 100644 --- a/src/addons/meta/meta.h +++ b/src/addons/meta/meta.h @@ -37,6 +37,16 @@ int flecs_expr_ser_primitive( void flecs_rtt_init_default_hooks( ecs_iter_t *it); +int ecs_compare_string( + const void *str_a, + const void *str_b, + const ecs_type_info_t *ti); + +bool ecs_equals_string( + const void *str_a, + const void *str_b, + const ecs_type_info_t *ti); + #endif #endif diff --git a/src/addons/meta/rtt_lifecycle.c b/src/addons/meta/rtt_lifecycle.c index 172079128..2dbb64b13 100644 --- a/src/addons/meta/rtt_lifecycle.c +++ b/src/addons/meta/rtt_lifecycle.c @@ -15,6 +15,8 @@ typedef struct ecs_rtt_call_data_t { ecs_xtor_t xtor; ecs_move_t move; ecs_copy_t copy; + ecs_cmp_t cmp; + ecs_equals_t equals; } hook; const ecs_type_info_t *type_info; int32_t offset; @@ -23,10 +25,13 @@ typedef struct ecs_rtt_call_data_t { /* Lifecycle context for runtime structs */ typedef struct ecs_rtt_struct_ctx_t { - ecs_vec_t vctor; /* vector */ - ecs_vec_t vdtor; /* vector */ - ecs_vec_t vmove; /* vector */ - ecs_vec_t vcopy; /* vector */ + ecs_vec_t vctor; /* vector */ + ecs_vec_t vdtor; /* vector */ + ecs_vec_t vmove; /* vector */ + ecs_vec_t vcopy; /* vector */ + ecs_vec_t vcmp; /* vector */ + ecs_vec_t vequals; /* vector */ + } ecs_rtt_struct_ctx_t; /* Lifecycle context for runtime arrays */ @@ -182,6 +187,77 @@ void flecs_rtt_struct_copy( } } +/* Generic compare hook. It will read hook information call data from the + * structs's lifecycle context and call the compare hooks configured when + * the type was created. */ +static +int flecs_rtt_struct_cmp( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *type_info) +{ + if(a_ptr == b_ptr) { + return 0; + } + + ecs_rtt_struct_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_assert(rtt_ctx != NULL, ECS_INTERNAL_ERROR, NULL); + + int cb_count = ecs_vec_count(&rtt_ctx->vcmp); + int i; + for (i = 0; i < cb_count; i++) { + ecs_rtt_call_data_t *comp_data = + ecs_vec_get_t(&rtt_ctx->vcmp, ecs_rtt_call_data_t, i); + int c = comp_data->hook.cmp( + ECS_OFFSET(a_ptr, comp_data->offset), + ECS_OFFSET(b_ptr, comp_data->offset), + comp_data->type_info); + if (c != 0) { + return c; + } + } + return 0; +} + +/* Generic equals hook. It will read hook information call data from the + * structs's lifecycle context and call the equals hooks configured when + * the type was created. */ +static +bool flecs_rtt_struct_equals( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *type_info) +{ + if(a_ptr == b_ptr) { + return true; + } + + ecs_rtt_struct_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_assert(rtt_ctx != NULL, ECS_INTERNAL_ERROR, NULL); + + int cb_count = ecs_vec_count(&rtt_ctx->vequals); + int i; + for (i = 0; i < cb_count; i++) { + ecs_rtt_call_data_t *comp_data = + ecs_vec_get_t(&rtt_ctx->vequals, ecs_rtt_call_data_t, i); + bool eq = comp_data->hook.equals( + ECS_OFFSET(a_ptr, comp_data->offset), + ECS_OFFSET(b_ptr, comp_data->offset), + comp_data->type_info); + if (!eq) { + return false; + } + } + return true; +} + +static +void flecs_rtt_free_lifecycle_nop( + void *ctx) +{ + (void)ctx; +} + static void flecs_rtt_free_lifecycle_struct_ctx( void *ctx) @@ -196,6 +272,8 @@ void flecs_rtt_free_lifecycle_struct_ctx( ecs_vec_fini_t(NULL, &lifecycle_ctx->vdtor, ecs_rtt_call_data_t); ecs_vec_fini_t(NULL, &lifecycle_ctx->vmove, ecs_rtt_call_data_t); ecs_vec_fini_t(NULL, &lifecycle_ctx->vcopy, ecs_rtt_call_data_t); + ecs_vec_fini_t(NULL, &lifecycle_ctx->vcmp, ecs_rtt_call_data_t); + ecs_vec_fini_t(NULL, &lifecycle_ctx->vequals, ecs_rtt_call_data_t); ecs_os_free(ctx); } @@ -208,40 +286,52 @@ ecs_rtt_struct_ctx_t * flecs_rtt_configure_struct_hooks( bool ctor, bool dtor, bool move, - bool copy) + bool copy, + bool cmp, + bool equals) { ecs_type_hooks_t hooks = ti->hooks; if (hooks.lifecycle_ctx_free) { hooks.lifecycle_ctx_free(hooks.lifecycle_ctx); } + hooks.ctor = ctor && !(flags & ECS_TYPE_HOOK_CTOR_ILLEGAL) ? + flecs_rtt_struct_ctor : NULL; + + hooks.dtor = dtor && !(flags & ECS_TYPE_HOOK_DTOR_ILLEGAL) ? + flecs_rtt_struct_dtor : NULL; + + hooks.move = move && !(flags & ECS_TYPE_HOOK_MOVE_ILLEGAL) ? + flecs_rtt_struct_move : NULL; + + hooks.copy = copy && !(flags & ECS_TYPE_HOOK_COPY_ILLEGAL) ? + flecs_rtt_struct_copy : NULL; + + hooks.cmp = cmp && !(flags & ECS_TYPE_HOOK_CMP_ILLEGAL) ? + flecs_rtt_struct_cmp : NULL; + + hooks.equals = equals && !(flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL) ? + flecs_rtt_struct_equals : NULL; + ecs_rtt_struct_ctx_t *rtt_ctx = NULL; - if (ctor || dtor || move || copy) { + if (hooks.ctor || hooks.dtor || hooks.move || hooks.copy + || hooks.cmp || hooks.equals) { rtt_ctx = ecs_os_malloc_t(ecs_rtt_struct_ctx_t); ecs_vec_init_t(NULL, &rtt_ctx->vctor, ecs_rtt_call_data_t, 0); ecs_vec_init_t(NULL, &rtt_ctx->vdtor, ecs_rtt_call_data_t, 0); ecs_vec_init_t(NULL, &rtt_ctx->vmove, ecs_rtt_call_data_t, 0); ecs_vec_init_t(NULL, &rtt_ctx->vcopy, ecs_rtt_call_data_t, 0); + ecs_vec_init_t(NULL, &rtt_ctx->vcmp, ecs_rtt_call_data_t, 0); + ecs_vec_init_t(NULL, &rtt_ctx->vequals, ecs_rtt_call_data_t, 0); + hooks.lifecycle_ctx = rtt_ctx; hooks.lifecycle_ctx_free = flecs_rtt_free_lifecycle_struct_ctx; - - if (ctor) { - hooks.ctor = flecs_rtt_struct_ctor; - } - if (dtor) { - hooks.dtor = flecs_rtt_struct_dtor; - } - if (move) { - hooks.move = flecs_rtt_struct_move; - } - if (copy) { - hooks.copy = flecs_rtt_struct_copy; - } } else { hooks.lifecycle_ctx = NULL; - hooks.lifecycle_ctx_free = NULL; + hooks.lifecycle_ctx_free = flecs_rtt_free_lifecycle_nop; } - hooks.flags |= flags; + + hooks.flags = flags; hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; ecs_set_hooks_id(world, ti->component, &hooks); return rtt_ctx; @@ -266,6 +356,8 @@ void flecs_rtt_init_default_hooks_struct( bool dtor_hook_required = false; bool move_hook_required = false; bool copy_hook_required = false; + bool valid_cmp = true; + bool valid_equals = true; /* Iterate all struct members and see if any member type has hooks. If so, * the struct itself will need to have that hook: */ @@ -280,6 +372,9 @@ void flecs_rtt_init_default_hooks_struct( dtor_hook_required |= member_ti->hooks.dtor != NULL; move_hook_required |= member_ti->hooks.move != NULL; copy_hook_required |= member_ti->hooks.copy != NULL; + /* A struct has a valid cmp/equals hook if all its members have it: */ + valid_cmp &= member_ti->hooks.cmp != NULL; + valid_equals &= member_ti->hooks.equals != NULL; flags |= member_ti->hooks.flags; } @@ -292,10 +387,13 @@ void flecs_rtt_init_default_hooks_struct( ctor_hook_required, dtor_hook_required, move_hook_required, - copy_hook_required); + copy_hook_required, + valid_cmp, + valid_equals + ); if (!rtt_ctx) { - return; /* no hooks required */ + return; /* no hook forwarding required */ } /* At least a hook was configured, therefore examine each struct member to @@ -348,6 +446,24 @@ void flecs_rtt_init_default_hooks_struct( copy_data->hook.copy = flecs_rtt_default_copy; } } + if (valid_cmp) { + ecs_rtt_call_data_t *comp_data = + ecs_vec_append_t(NULL, &rtt_ctx->vcmp, ecs_rtt_call_data_t); + comp_data->offset = m->offset; + comp_data->type_info = member_ti; + comp_data->count = 1; + ecs_assert(member_ti->hooks.cmp, ECS_INTERNAL_ERROR, NULL); + comp_data->hook.cmp = member_ti->hooks.cmp; + } + if (valid_equals) { + ecs_rtt_call_data_t *comp_data = + ecs_vec_append_t(NULL, &rtt_ctx->vequals, ecs_rtt_call_data_t); + comp_data->offset = m->offset; + comp_data->type_info = member_ti; + comp_data->count = 1; + ecs_assert(member_ti->hooks.equals, ECS_INTERNAL_ERROR, NULL); + comp_data->hook.equals = member_ti->hooks.equals; + } } } @@ -444,6 +560,62 @@ void flecs_rtt_array_copy( } } +/* Generic array compare hook. It will invoke the compare hook of the underlying + * type for each element */ +static +int flecs_rtt_array_cmp( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *type_info) +{ + if(a_ptr == b_ptr) { + return 0; + } + + ecs_rtt_array_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_cmp_t cmp = rtt_ctx->type_info->hooks.cmp; + ecs_assert(cmp, ECS_INVALID_PARAMETER, NULL); + ecs_size_t element_size = rtt_ctx->type_info->size; + int i; + for (i = 0; i < rtt_ctx->elem_count; i++) { + const void *a_element = ECS_ELEM(a_ptr, element_size, i); + const void *b_element = ECS_ELEM(b_ptr, element_size, i); + int c = cmp(a_element, b_element, rtt_ctx->type_info); + if(c != 0) { + return c; + } + } + return 0; +} + +/* Generic array equals hook. It will invoke the equals hook of the underlying + * type for each element */ +static +bool flecs_rtt_array_equals( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *type_info) +{ + if(a_ptr == b_ptr) { + return true; + } + + ecs_rtt_array_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_equals_t equals = rtt_ctx->type_info->hooks.equals; + ecs_assert(equals, ECS_INVALID_PARAMETER, NULL); + ecs_size_t element_size = rtt_ctx->type_info->size; + int i; + for (i = 0; i < rtt_ctx->elem_count; i++) { + const void *a_element = ECS_ELEM(a_ptr, element_size, i); + const void *b_element = ECS_ELEM(b_ptr, element_size, i); + bool eq = equals(a_element, b_element, rtt_ctx->type_info); + if(!eq) { + return false; + } + } + return true; +} + /* Checks if an array's underlying type has hooks installed. If so, it generates * and installs required hooks for the array type itself. These hooks will * invoke the underlying type's hook for each element in the array. */ @@ -454,26 +626,44 @@ void flecs_rtt_init_default_hooks_array( { const EcsArray *array_info = ecs_get(world, component, EcsArray); ecs_assert(array_info != NULL, ECS_INTERNAL_ERROR, NULL); - const ecs_type_info_t *array_ti = + const ecs_type_info_t *element_ti = ecs_get_type_info(world, array_info->type); + ecs_flags32_t flags = element_ti->hooks.flags; bool ctor_hook_required = - array_ti->hooks.ctor && array_ti->hooks.ctor != flecs_default_ctor; - bool dtor_hook_required = array_ti->hooks.dtor != NULL; - bool move_hook_required = array_ti->hooks.move != NULL; - bool copy_hook_required = array_ti->hooks.copy != NULL; - ecs_flags32_t flags = array_ti->hooks.flags; + element_ti->hooks.ctor && element_ti->hooks.ctor != flecs_default_ctor; + bool dtor_hook_required = element_ti->hooks.dtor != NULL; + bool move_hook_required = element_ti->hooks.move != NULL; + bool copy_hook_required = element_ti->hooks.copy != NULL; + bool valid_cmp = element_ti->hooks.cmp != NULL && !(flags & ECS_TYPE_HOOK_CMP_ILLEGAL); + bool valid_equals = element_ti->hooks.equals != NULL && !(flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL); + ecs_type_hooks_t hooks = *ecs_get_hooks_id(world, component); + + hooks.ctor = ctor_hook_required && !(flags & ECS_TYPE_HOOK_CTOR_ILLEGAL) ? + flecs_rtt_array_ctor : NULL; + hooks.dtor = dtor_hook_required && !(flags & ECS_TYPE_HOOK_DTOR_ILLEGAL) ? + flecs_rtt_array_dtor : NULL; + hooks.move = move_hook_required && !(flags & ECS_TYPE_HOOK_MOVE_ILLEGAL) ? + flecs_rtt_array_move : NULL; + hooks.copy = copy_hook_required && !(flags & ECS_TYPE_HOOK_COPY_ILLEGAL) ? + flecs_rtt_array_copy : NULL; + hooks.cmp = valid_cmp && !(flags & ECS_TYPE_HOOK_CMP_ILLEGAL) ? + flecs_rtt_array_cmp : NULL; + hooks.equals = valid_equals && !(flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL) ? + flecs_rtt_array_equals : NULL; + if (hooks.lifecycle_ctx_free) { hooks.lifecycle_ctx_free(hooks.lifecycle_ctx); - hooks.lifecycle_ctx_free = NULL; + hooks.lifecycle_ctx_free = flecs_rtt_free_lifecycle_nop; } - if (ctor_hook_required || dtor_hook_required || move_hook_required || - copy_hook_required) { + if (hooks.ctor || hooks.dtor || hooks.move || + hooks.copy || hooks.cmp || hooks.equals) + { ecs_rtt_array_ctx_t *rtt_ctx = ecs_os_malloc_t(ecs_rtt_array_ctx_t); - rtt_ctx->type_info = array_ti; + rtt_ctx->type_info = element_ti; rtt_ctx->elem_count = array_info->count; if (hooks.lifecycle_ctx_free) { hooks.lifecycle_ctx_free(hooks.lifecycle_ctx); @@ -483,23 +673,7 @@ void flecs_rtt_init_default_hooks_array( hooks.lifecycle_ctx_free = flecs_rtt_free_lifecycle_array_ctx; } - if (ctor_hook_required) { - hooks.ctor = flecs_rtt_array_ctor; - } - - if (dtor_hook_required) { - hooks.dtor = flecs_rtt_array_dtor; - } - - if (move_hook_required) { - hooks.move = flecs_rtt_array_move; - } - - if (copy_hook_required) { - hooks.copy = flecs_rtt_array_copy; - } - - hooks.flags |= flags; + hooks.flags = flags; hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; ecs_set_hooks_id(world, component, &hooks); } @@ -613,6 +787,92 @@ void flecs_rtt_vector_copy( } } +/* Generic vector compare hook. */ +static +int flecs_rtt_vector_cmp( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *type_info) +{ + if(a_ptr == b_ptr) { + return 0; + } + + const ecs_vec_t *vec_a = a_ptr; + const ecs_vec_t *vec_b = b_ptr; + + ecs_size_t count_a = ecs_vec_count(vec_a); + ecs_size_t count_b = ecs_vec_count(vec_b); + { + int c = count_a - count_b; + if(c != 0) { + return c; + } + } + + ecs_rtt_vector_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_cmp_t cmp = rtt_ctx->type_info->hooks.cmp; + ecs_assert(cmp, ECS_INVALID_PARAMETER, NULL); + + ecs_size_t element_size = rtt_ctx->type_info->size; + const void *a = ecs_vec_first(vec_a); + const void *b = ecs_vec_first(vec_b); + + int i; + for (i = 0; i < count_a; i++) { + const void *a_element = ECS_ELEM(a, element_size, i); + const void *b_element = ECS_ELEM(b, element_size, i); + int c = cmp(a_element, b_element, rtt_ctx->type_info); + if(c != 0) { + return c; + } + } + return 0; +} + +/* Generic vector equals hook. */ +static +bool flecs_rtt_vector_equals( + const void *a_ptr, + const void *b_ptr, + const ecs_type_info_t *type_info) +{ + if(a_ptr == b_ptr) { + return true; + } + + const ecs_vec_t *vec_a = a_ptr; + const ecs_vec_t *vec_b = b_ptr; + + ecs_size_t count_a = ecs_vec_count(vec_a); + ecs_size_t count_b = ecs_vec_count(vec_b); + { + int c = count_a - count_b; + if(c != 0) { + return c; + } + } + + ecs_rtt_vector_ctx_t *rtt_ctx = type_info->hooks.lifecycle_ctx; + ecs_equals_t equals = rtt_ctx->type_info->hooks.equals; + ecs_assert(equals, ECS_INVALID_PARAMETER, NULL); + + ecs_size_t element_size = rtt_ctx->type_info->size; + const void *a = ecs_vec_first(vec_a); + const void *b = ecs_vec_first(vec_b); + + int i; + for (i = 0; i < count_a; i++) { + const void *a_element = ECS_ELEM(a, element_size, i); + const void *b_element = ECS_ELEM(b, element_size, i); + int eq = equals(a_element, b_element, rtt_ctx->type_info); + if(!eq) { + return false; + } + } + return true; +} + /* Generates and installs required hooks for managing the vector and underlying * type lifecycle. Vectors always have hooks because at the very least the * vector structure itself must be initialized/destroyed/copied/moved, even if @@ -624,20 +884,41 @@ void flecs_rtt_init_default_hooks_vector( { const EcsVector *vector_info = ecs_get(world, component, EcsVector); ecs_assert(vector_info != NULL, ECS_INTERNAL_ERROR, NULL); - const ecs_type_info_t *vector_ti = + const ecs_type_info_t *element_ti = ecs_get_type_info(world, vector_info->type); ecs_rtt_vector_ctx_t *rtt_ctx = ecs_os_malloc_t(ecs_rtt_vector_ctx_t); - rtt_ctx->type_info = vector_ti; + rtt_ctx->type_info = element_ti; + ecs_flags32_t flags = element_ti->hooks.flags; + ecs_type_hooks_t hooks = *ecs_get_hooks_id(world, component); + if (hooks.lifecycle_ctx_free) { hooks.lifecycle_ctx_free(hooks.lifecycle_ctx); } hooks.lifecycle_ctx = rtt_ctx; hooks.lifecycle_ctx_free = flecs_rtt_free_lifecycle_vector_ctx; + hooks.ctor = flecs_rtt_vector_ctor; hooks.dtor = flecs_rtt_vector_dtor; hooks.move = flecs_rtt_vector_move; hooks.copy = flecs_rtt_vector_copy; + + if (element_ti->hooks.cmp != NULL && !(flags & ECS_TYPE_HOOK_CMP_ILLEGAL)) { + hooks.cmp = flecs_rtt_vector_cmp; + } else { + hooks.cmp = NULL; + } + + if (element_ti->hooks.equals != NULL && !(flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL)) { + hooks.equals = flecs_rtt_vector_equals; + } else { + hooks.equals = NULL; + } + + + /* propagate only the compare/equals hook illegal flag, if set */ + hooks.flags |= flags & (ECS_TYPE_HOOK_CMP_ILLEGAL|ECS_TYPE_HOOK_EQUALS_ILLEGAL); + hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; ecs_set_hooks_id(world, component, &hooks); } @@ -668,27 +949,39 @@ void flecs_rtt_init_default_hooks( * */ ecs_entity_t component = it->entities[i]; - const ecs_type_info_t *ti = ecs_get_type_info(world, component); - if (ti) { - if (type->kind == EcsStructType) { - flecs_rtt_init_default_hooks_struct(world, component, ti); - } else if (type->kind == EcsArrayType) { - flecs_rtt_init_default_hooks_array(world, component); - } else if (type->kind == EcsVectorType) { - flecs_rtt_init_default_hooks_vector(world, component); - } + /* Skip configuring hooks for ids already in use */ + const ecs_world_t* w = ecs_get_world(world); + if(ecs_id_in_use(w, component) || + ecs_id_in_use(w, ecs_pair(component, EcsWildcard))) { + continue; + } + + const ecs_type_info_t *ti = ecs_get_type_info(world, component); + ecs_assert(ti,ECS_INTERNAL_ERROR,NULL); + + if (type->kind == EcsStructType) { + flecs_rtt_init_default_hooks_struct(world, component, ti); + } else if (type->kind == EcsArrayType) { + flecs_rtt_init_default_hooks_array(world, component); + } else if (type->kind == EcsVectorType) { + flecs_rtt_init_default_hooks_vector(world, component); } + ecs_type_hooks_t hooks = ti->hooks; /* Make sure there is at least a default constructor. This ensures that * a new component value does not contain uninitialized memory, which * could cause serializers to crash when for example inspecting string * fields. */ - if (!ti || !ti->hooks.ctor) { - ecs_set_hooks_id(world, component, &(ecs_type_hooks_t){ - .ctor = flecs_default_ctor - }); + if(!ti->hooks.ctor && !(ti->hooks.flags & ECS_TYPE_HOOK_CTOR_ILLEGAL)) { + hooks.ctor = flecs_default_ctor; } + + hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; + ecs_set_hooks_id( + world, + component, + &hooks); } } diff --git a/src/world.c b/src/world.c index afd4ff417..e13146148 100644 --- a/src/world.c +++ b/src/world.c @@ -1192,6 +1192,11 @@ void flecs_default_move_w_dtor(void *dst_ptr, void *src_ptr, cl->dtor(src_ptr, count, ti); } +static +bool flecs_default_equals(const void *a_ptr, const void *b_ptr, const ecs_type_info_t* ti) { + return ti->hooks.cmp(a_ptr, b_ptr, ti) == 0; +} + ECS_NORETURN static void flecs_ctor_illegal( void * dst, @@ -1263,6 +1268,28 @@ void flecs_move_ctor_illegal( ecs_abort(ECS_INVALID_OPERATION, "invalid move construct for %s", ti->name); } +ECS_NORETURN static +int flecs_comp_illegal( + const void *dst, + const void *src, + const ecs_type_info_t *ti) +{ + (void)dst; /* silence unused warning */ + (void)src; + ecs_abort(ECS_INVALID_OPERATION, "invalid compare hook for %s", ti->name); +} + +ECS_NORETURN static +bool flecs_equals_illegal( + const void *dst, + const void *src, + const ecs_type_info_t *ti) +{ + (void)dst; /* silence unused warning */ + (void)src; + ecs_abort(ECS_INVALID_OPERATION, "invalid equals hook for %s", ti->name); +} + void ecs_set_hooks_id( ecs_world_t *world, ecs_entity_t component, @@ -1274,27 +1301,56 @@ void ecs_set_hooks_id( ecs_flags32_t flags = h->flags; flags &= ~((ecs_flags32_t)ECS_TYPE_HOOKS); - /* TODO: enable asserts once RTT API is updated */ - /* - ecs_check(!(h->flags & ECS_TYPE_HOOK_CTOR_ILLEGAL) || !h->ctor, - ECS_INVALID_PARAMETER, "cannot specify both hook and illegal flag"); - ecs_check(!(h->flags & ECS_TYPE_HOOK_DTOR_ILLEGAL) || !h->dtor, - ECS_INVALID_PARAMETER, "cannot specify both hook and illegal flag"); - ecs_check(!(h->flags & ECS_TYPE_HOOK_COPY_ILLEGAL) || !h->copy, - ECS_INVALID_PARAMETER, "cannot specify both hook and illegal flag"); - ecs_check(!(h->flags & ECS_TYPE_HOOK_MOVE_ILLEGAL) || !h->move, - ECS_INVALID_PARAMETER, "cannot specify both hook and illegal flag"); - ecs_check(!(h->flags & ECS_TYPE_HOOK_COPY_CTOR_ILLEGAL) || !h->copy_ctor, - ECS_INVALID_PARAMETER, - "cannot specify both hook and illegal flag"); - ecs_check(!(h->flags & ECS_TYPE_HOOK_MOVE_CTOR_ILLEGAL) || !h->move_ctor, - ECS_INVALID_PARAMETER, "cannot specify both hook and illegal flag"); - ecs_check(!(h->flags & ECS_TYPE_HOOK_CTOR_MOVE_DTOR_ILLEGAL) || - !h->ctor_move_dtor, ECS_INVALID_PARAMETER, - "cannot specify both hook and illegal flag"); - ecs_check(!(h->flags & ECS_TYPE_HOOK_MOVE_DTOR_ILLEGAL) || !h->move_dtor, - ECS_INVALID_PARAMETER, "cannot specify both hook and illegal flag"); - */ + ecs_check(!(flags & ECS_TYPE_HOOK_CTOR_ILLEGAL && + h->ctor != NULL && + h->ctor != flecs_ctor_illegal), + ECS_INVALID_PARAMETER, "cannot specify both ctor hook and illegal flag"); + + ecs_check(!(flags & ECS_TYPE_HOOK_DTOR_ILLEGAL && + h->dtor != NULL && + h->dtor != flecs_dtor_illegal), + ECS_INVALID_PARAMETER, "cannot specify both dtor hook and illegal flag"); + + ecs_check(!(flags & ECS_TYPE_HOOK_COPY_ILLEGAL && + h->copy != NULL && + h->copy != flecs_copy_illegal), + ECS_INVALID_PARAMETER, "cannot specify both copy hook and illegal flag"); + + ecs_check(!(flags & ECS_TYPE_HOOK_MOVE_ILLEGAL && + h->move != NULL && + h->move != flecs_move_illegal), + ECS_INVALID_PARAMETER, "cannot specify both move hook and illegal flag"); + + ecs_check(!(flags & ECS_TYPE_HOOK_COPY_CTOR_ILLEGAL && + h->copy_ctor != NULL && + h->copy_ctor != flecs_copy_ctor_illegal), + ECS_INVALID_PARAMETER, "cannot specify both copy ctor hook and illegal flag"); + + ecs_check(!(flags & ECS_TYPE_HOOK_MOVE_CTOR_ILLEGAL && + h->move_ctor != NULL && + h->move_ctor != flecs_move_ctor_illegal), + ECS_INVALID_PARAMETER, "cannot specify both move ctor hook and illegal flag"); + + ecs_check(!(flags & ECS_TYPE_HOOK_CTOR_MOVE_DTOR_ILLEGAL && + h->ctor_move_dtor != NULL && + h->ctor_move_dtor != flecs_move_ctor_illegal), + ECS_INVALID_PARAMETER, "cannot specify both ctor move dtor hook and illegal flag"); + + ecs_check(!(flags & ECS_TYPE_HOOK_MOVE_DTOR_ILLEGAL && + h->move_dtor != NULL && + h->move_dtor != flecs_move_ctor_illegal), + ECS_INVALID_PARAMETER, "cannot specify both move dtor hook and illegal flag"); + + ecs_check(!(flags & ECS_TYPE_HOOK_CMP_ILLEGAL && + h->cmp != NULL && + h->cmp != flecs_comp_illegal), + ECS_INVALID_PARAMETER, "cannot specify both compare hook and illegal flag"); + + ecs_check(!(flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL && + h->equals != NULL && + h->equals != flecs_equals_illegal), + ECS_INVALID_PARAMETER, "cannot specify both equals hook and illegal flag"); + flecs_stage_from_world(&world); @@ -1332,6 +1388,8 @@ void ecs_set_hooks_id( if (h->move_ctor) ti->hooks.move_ctor = h->move_ctor; if (h->ctor_move_dtor) ti->hooks.ctor_move_dtor = h->ctor_move_dtor; if (h->move_dtor) ti->hooks.move_dtor = h->move_dtor; + if (h->cmp) ti->hooks.cmp = h->cmp; + if (h->equals) ti->hooks.equals = h->equals; if (h->on_add) ti->hooks.on_add = h->on_add; if (h->on_remove) ti->hooks.on_remove = h->on_remove; @@ -1430,6 +1488,19 @@ void ecs_set_hooks_id( } } + if(!h->cmp) { + flags |= ECS_TYPE_HOOK_CMP_ILLEGAL; + } + + if (!h->equals || h->equals == flecs_equals_illegal) { + if(flags & ECS_TYPE_HOOK_CMP_ILLEGAL) { + flags |= ECS_TYPE_HOOK_EQUALS_ILLEGAL; + } else { + ti->hooks.equals = flecs_default_equals; + flags &= ~ECS_TYPE_HOOK_EQUALS_ILLEGAL; + } + } + ti->hooks.flags = flags; if (ti->hooks.ctor) ti->hooks.flags |= ECS_TYPE_HOOK_CTOR; @@ -1440,11 +1511,15 @@ void ecs_set_hooks_id( if (ti->hooks.move_dtor) ti->hooks.flags |= ECS_TYPE_HOOK_MOVE_DTOR; if (ti->hooks.copy) ti->hooks.flags |= ECS_TYPE_HOOK_COPY; if (ti->hooks.copy_ctor) ti->hooks.flags |= ECS_TYPE_HOOK_COPY_CTOR; + if (ti->hooks.cmp) ti->hooks.flags |= ECS_TYPE_HOOK_CMP; + if (ti->hooks.equals) ti->hooks.flags |= ECS_TYPE_HOOK_EQUALS; if(flags & ECS_TYPE_HOOK_CTOR_ILLEGAL) ti->hooks.ctor = flecs_ctor_illegal; if(flags & ECS_TYPE_HOOK_DTOR_ILLEGAL) ti->hooks.dtor = flecs_dtor_illegal; if(flags & ECS_TYPE_HOOK_COPY_ILLEGAL) ti->hooks.copy = flecs_copy_illegal; if(flags & ECS_TYPE_HOOK_MOVE_ILLEGAL) ti->hooks.move = flecs_move_illegal; + if(flags & ECS_TYPE_HOOK_CMP_ILLEGAL) ti->hooks.cmp = flecs_comp_illegal; + if(flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL) ti->hooks.equals = flecs_equals_illegal; if(flags & ECS_TYPE_HOOK_COPY_CTOR_ILLEGAL) { ti->hooks.copy_ctor = flecs_copy_ctor_illegal; @@ -1459,9 +1534,10 @@ void ecs_set_hooks_id( } if(ti->hooks.flags & ECS_TYPE_HOOK_MOVE_DTOR_ILLEGAL) { - ti->hooks.ctor_move_dtor = flecs_move_ctor_illegal; + ti->hooks.move_dtor = flecs_move_ctor_illegal; } + error: return; } diff --git a/test/core/project.json b/test/core/project.json index fc21affde..4ebb9437d 100644 --- a/test/core/project.json +++ b/test/core/project.json @@ -1232,7 +1232,9 @@ "move_flags", "copy_flags", "ctor_move_dtor_flags", - "move_dtor_flags" + "move_dtor_flags", + "cmp_flags", + "equals_flags" ] }, { "id": "Pairs", diff --git a/test/core/src/ComponentLifecycle.c b/test/core/src/ComponentLifecycle.c index d6de7d44f..42f69bfca 100644 --- a/test/core/src/ComponentLifecycle.c +++ b/test/core/src/ComponentLifecycle.c @@ -3695,8 +3695,6 @@ void ComponentLifecycle_illegal_copy_and_ctor(void) { } void ComponentLifecycle_illegal_ctor_w_ctor(void) { - test_quarantine("27 Nov 2024"); - install_test_abort(); ecs_world_t *world = ecs_mini(); @@ -3712,8 +3710,6 @@ void ComponentLifecycle_illegal_ctor_w_ctor(void) { } void ComponentLifecycle_illegal_dtor_w_dtor(void) { - test_quarantine("27 Nov 2024"); - install_test_abort(); ecs_world_t *world = ecs_mini(); @@ -3729,8 +3725,6 @@ void ComponentLifecycle_illegal_dtor_w_dtor(void) { } void ComponentLifecycle_illegal_move_w_move(void) { - test_quarantine("27 Nov 2024"); - install_test_abort(); ecs_world_t *world = ecs_mini(); @@ -3746,8 +3740,6 @@ void ComponentLifecycle_illegal_move_w_move(void) { } void ComponentLifecycle_illegal_copy_w_copy(void) { - test_quarantine("27 Nov 2024"); - install_test_abort(); ecs_world_t *world = ecs_mini(); @@ -3763,8 +3755,6 @@ void ComponentLifecycle_illegal_copy_w_copy(void) { } void ComponentLifecycle_illegal_move_ctor_w_move_ctor(void) { - test_quarantine("27 Nov 2024"); - install_test_abort(); ecs_world_t *world = ecs_mini(); @@ -3780,8 +3770,6 @@ void ComponentLifecycle_illegal_move_ctor_w_move_ctor(void) { } void ComponentLifecycle_illegal_copy_ctor_w_copy_ctor(void) { - test_quarantine("27 Nov 2024"); - install_test_abort(); ecs_world_t *world = ecs_mini(); @@ -3797,8 +3785,6 @@ void ComponentLifecycle_illegal_copy_ctor_w_copy_ctor(void) { } void ComponentLifecycle_illegal_move_ctor_w_ctor_and_move(void) { - test_quarantine("27 Nov 2024"); - install_test_abort(); ecs_world_t *world = ecs_mini(); @@ -3825,8 +3811,6 @@ void ComponentLifecycle_illegal_move_ctor_w_ctor_and_move(void) { } void ComponentLifecycle_illegal_copy_ctor_w_ctor_and_copy(void) { - test_quarantine("27 Nov 2024"); - install_test_abort(); ecs_world_t *world = ecs_mini(); @@ -3864,7 +3848,9 @@ void ComponentLifecycle_ctor_flags(void) { const ecs_type_info_t *ti = ecs_get_type_info(world, ecs_id(Position)); test_assert(ti != NULL); test_assert(ti->hooks.ctor != NULL); - test_assert(ti->hooks.flags == ECS_TYPE_HOOK_CTOR); + test_assert(ti->hooks.flags == (ECS_TYPE_HOOK_CTOR| + ECS_TYPE_HOOK_CMP|ECS_TYPE_HOOK_EQUALS| + ECS_TYPE_HOOK_CMP_ILLEGAL|ECS_TYPE_HOOK_EQUALS_ILLEGAL)); ecs_fini(world); } @@ -3884,7 +3870,9 @@ void ComponentLifecycle_dtor_flags(void) { test_assert(ti->hooks.dtor != NULL); test_assert(ti->hooks.move_dtor != NULL); test_assert(ti->hooks.flags == - (ECS_TYPE_HOOK_CTOR|ECS_TYPE_HOOK_DTOR|ECS_TYPE_HOOK_MOVE_DTOR)); + (ECS_TYPE_HOOK_CTOR|ECS_TYPE_HOOK_DTOR|ECS_TYPE_HOOK_MOVE_DTOR| + ECS_TYPE_HOOK_CMP|ECS_TYPE_HOOK_EQUALS| + ECS_TYPE_HOOK_CMP_ILLEGAL|ECS_TYPE_HOOK_EQUALS_ILLEGAL)); ecs_fini(world); } @@ -3903,7 +3891,9 @@ void ComponentLifecycle_move_flags(void) { test_assert(ti->hooks.move != NULL); test_assert(ti->hooks.flags == (ECS_TYPE_HOOK_CTOR|ECS_TYPE_HOOK_MOVE|ECS_TYPE_HOOK_MOVE_CTOR| - ECS_TYPE_HOOK_CTOR_MOVE_DTOR|ECS_TYPE_HOOK_MOVE_DTOR)); + ECS_TYPE_HOOK_CTOR_MOVE_DTOR|ECS_TYPE_HOOK_MOVE_DTOR| + ECS_TYPE_HOOK_CMP|ECS_TYPE_HOOK_EQUALS| + ECS_TYPE_HOOK_CMP_ILLEGAL|ECS_TYPE_HOOK_EQUALS_ILLEGAL)); ecs_fini(world); } @@ -3921,7 +3911,9 @@ void ComponentLifecycle_copy_flags(void) { test_assert(ti != NULL); test_assert(ti->hooks.copy != NULL); test_assert(ti->hooks.flags == - (ECS_TYPE_HOOK_CTOR|ECS_TYPE_HOOK_COPY|ECS_TYPE_HOOK_COPY_CTOR)); + (ECS_TYPE_HOOK_CTOR|ECS_TYPE_HOOK_COPY|ECS_TYPE_HOOK_COPY_CTOR| + ECS_TYPE_HOOK_CMP|ECS_TYPE_HOOK_EQUALS| + ECS_TYPE_HOOK_CMP_ILLEGAL|ECS_TYPE_HOOK_EQUALS_ILLEGAL)); ecs_fini(world); } @@ -3947,7 +3939,9 @@ void ComponentLifecycle_ctor_move_dtor_flags(void) { test_assert(ti->hooks.move_dtor != NULL); test_assert(ti->hooks.flags == (ECS_TYPE_HOOK_CTOR|ECS_TYPE_HOOK_DTOR|ECS_TYPE_HOOK_MOVE| - ECS_TYPE_HOOK_MOVE_CTOR|ECS_TYPE_HOOK_CTOR_MOVE_DTOR|ECS_TYPE_HOOK_MOVE_DTOR)); + ECS_TYPE_HOOK_MOVE_CTOR|ECS_TYPE_HOOK_CTOR_MOVE_DTOR|ECS_TYPE_HOOK_MOVE_DTOR| + ECS_TYPE_HOOK_CMP|ECS_TYPE_HOOK_EQUALS| + ECS_TYPE_HOOK_CMP_ILLEGAL|ECS_TYPE_HOOK_EQUALS_ILLEGAL)); ecs_fini(world); } @@ -3969,7 +3963,66 @@ void ComponentLifecycle_move_dtor_flags(void) { test_assert(ti->hooks.move_dtor != NULL); test_assert(ti->hooks.flags == (ECS_TYPE_HOOK_CTOR|ECS_TYPE_HOOK_MOVE_CTOR|ECS_TYPE_HOOK_CTOR_MOVE_DTOR| - ECS_TYPE_HOOK_DTOR|ECS_TYPE_HOOK_MOVE|ECS_TYPE_HOOK_MOVE_DTOR)); + ECS_TYPE_HOOK_DTOR|ECS_TYPE_HOOK_MOVE|ECS_TYPE_HOOK_MOVE_DTOR| + ECS_TYPE_HOOK_CMP|ECS_TYPE_HOOK_EQUALS| + ECS_TYPE_HOOK_CMP_ILLEGAL|ECS_TYPE_HOOK_EQUALS_ILLEGAL)); ecs_fini(world); } + +int compare_Position(const void *a, const void *b, const ecs_type_info_t *ti) { + Position *pa = (Position*) a; + Position *pb = (Position*) b; + double ma = (pa->x) * (pa->x) + (pa->y) * (pa->y); + double mb = (pb->x) * (pb->x) + (pb->y) * (pb->y); + return mamb ? 1 : 0; +} + +bool equals_Position(const void *a, const void *b, const ecs_type_info_t *ti) { + Position *pa = (Position*) a; + Position *pb = (Position*) b; + return (pa->x == pb->x) && (pa->y == pb->y); +} + +void ComponentLifecycle_cmp_flags(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_set_hooks(world, Position, { + .cmp = compare_Position, + }); + + const ecs_type_info_t *ti = ecs_get_type_info(world, ecs_id(Position)); + test_assert(ti != NULL); + test_assert(ti->hooks.cmp != NULL); + + /* equals is autogenerated from valid cmp */ + test_assert(ti->hooks.equals != NULL); + + test_assert(ti->hooks.flags == (ECS_TYPE_HOOK_CMP|ECS_TYPE_HOOK_EQUALS)); + + ecs_fini(world); +} + +void ComponentLifecycle_equals_flags(void) { + ecs_world_t *world = ecs_mini(); + + ECS_COMPONENT(world, Position); + + ecs_set_hooks(world, Position, { + .equals = equals_Position, + }); + + const ecs_type_info_t *ti = ecs_get_type_info(world, ecs_id(Position)); + test_assert(ti != NULL); + test_assert(ti->hooks.equals != NULL); + + test_assert(ti->hooks.flags == ( + ECS_TYPE_HOOK_CMP| + ECS_TYPE_HOOK_EQUALS| + ECS_TYPE_HOOK_CMP_ILLEGAL + )); + + ecs_fini(world); +} \ No newline at end of file diff --git a/test/core/src/main.c b/test/core/src/main.c index 9f854bf9c..1d3b13c8d 100644 --- a/test/core/src/main.c +++ b/test/core/src/main.c @@ -1178,6 +1178,8 @@ void ComponentLifecycle_move_flags(void); void ComponentLifecycle_copy_flags(void); void ComponentLifecycle_ctor_move_dtor_flags(void); void ComponentLifecycle_move_dtor_flags(void); +void ComponentLifecycle_cmp_flags(void); +void ComponentLifecycle_equals_flags(void); // Testsuite 'Pairs' void Pairs_type_w_one_pair(void); @@ -6894,6 +6896,14 @@ bake_test_case ComponentLifecycle_testcases[] = { { "move_dtor_flags", ComponentLifecycle_move_dtor_flags + }, + { + "cmp_flags", + ComponentLifecycle_cmp_flags + }, + { + "equals_flags", + ComponentLifecycle_equals_flags } }; @@ -11714,7 +11724,7 @@ static bake_test_suite suites[] = { "ComponentLifecycle", ComponentLifecycle_setup, NULL, - 120, + 122, ComponentLifecycle_testcases }, { diff --git a/test/cpp/project.json b/test/cpp/project.json index 09dd0e6a2..8d1e92083 100644 --- a/test/cpp/project.json +++ b/test/cpp/project.json @@ -1083,7 +1083,22 @@ "sparse_component", "count_in_add_hook", "count_in_remove_hook", - "set_multiple_hooks" + "set_multiple_hooks", + "compare_WithGreaterThan", + "compare_WithLessThan", + "compare_WithLessAndGreaterThan", + "compare_WithEqualsAndGreaterThan", + "compare_WithEqualsAndLessThan", + "compare_WithEqualsOnly", + "compare_WithoutOperators", + "compare_uint8_Enum", + "compare_uint16_Enum", + "compare_uint32_Enum", + "compare_uint64_Enum", + "compare_int8_Enum", + "compare_int16_Enum", + "compare_int32_Enum", + "compare_int64_Enum" ] }, { "id": "Refs", diff --git a/test/cpp/src/ComponentLifecycle.cpp b/test/cpp/src/ComponentLifecycle.cpp index 258be5020..7a9ce3c6c 100644 --- a/test/cpp/src/ComponentLifecycle.cpp +++ b/test/cpp/src/ComponentLifecycle.cpp @@ -2280,3 +2280,573 @@ void ComponentLifecycle_set_multiple_hooks(void) { ecs.release(); /* destroys world */ test_int(removes, 2); /* two instances of `Pod` removed */ } + +struct WithGreaterThan { + int value; + bool operator>(const WithGreaterThan &other) const { + return value > other.value; + } +}; + +struct WithLessThan { + int value; + bool operator<(const WithLessThan &other) const { + return value < other.value; + } +}; + +struct WithLessAndGreaterThan { + int value; + bool operator<(const WithLessAndGreaterThan &other) const { + return value < other.value; + } + bool operator>(const WithLessAndGreaterThan &other) const { + return value > other.value; + } +}; + + +struct WithEqualsAndGreaterThan { + int value; + bool operator==(const WithEqualsAndGreaterThan &other) const { + return value == other.value; + } + bool operator>(const WithEqualsAndGreaterThan &other) const { + return value > other.value; + } +}; + +struct WithEqualsAndLessThan { + int value; + bool operator==(const WithEqualsAndLessThan &other) const { + return value == other.value; + } + bool operator<(const WithEqualsAndLessThan &other) const { + return value < other.value; + } +}; + +struct WithEqualsOnly { + int value; + bool operator==(const WithEqualsOnly &other) const { + return value == other.value; + } +}; + +struct WithoutOperators { + int16_t value; +}; + +static +int compare(flecs::world& ecs, flecs::entity_t id, const void *a, const void *b) { + const ecs_type_info_t* ti = ecs_get_type_info(ecs, id); + return ti->hooks.cmp(a, b, ti); +} + +static +bool equals(flecs::world& ecs, flecs::entity_t id, const void *a, const void *b) { + const ecs_type_info_t* ti = ecs_get_type_info(ecs, id); + return ti->hooks.equals(a, b, ti); +} + +void ComponentLifecycle_compare_WithGreaterThan(void) { + flecs::world ecs; + + auto component = ecs.component(); + + const ecs_type_hooks_t* hooks = ecs_get_hooks_id(ecs, component); + test_assert(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL); + test_assert(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL); + + /* generate cmp operator from C++ operator> */ + component.on_compare(); + + hooks = ecs_get_hooks_id(ecs, component); + test_assert(!(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL)); + /* `equals` is automatically generated: */ + test_assert(!(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL)); + + WithGreaterThan a = {1}; + WithGreaterThan b = {2}; + WithGreaterThan c = {1}; + + test_assert(compare(ecs, component, &a, &b) < 0); + test_assert(compare(ecs, component, &b, &a) > 0); + test_assert(compare(ecs, component, &a, &c) == 0); + test_assert(compare(ecs, component, &b, &c) > 0); + test_assert(compare(ecs, component, &c, &b) < 0); + test_assert(compare(ecs, component, &b, &b) == 0); + + /* test using autogenerated equals operator: */ + test_assert(equals(ecs, component, &a, &b) == false); + test_assert(equals(ecs, component, &a, &c) == true); + test_assert(equals(ecs, component, &a, &a) == true); +} + +void ComponentLifecycle_compare_WithLessThan(void) { + flecs::world ecs; + + auto component = ecs.component(); + + const ecs_type_hooks_t* hooks = ecs_get_hooks_id(ecs, component); + + test_assert(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL); + test_assert(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL); + + /* generate cmp operator from C++ operator< */ + component.on_compare(); + + hooks = ecs_get_hooks_id(ecs, component); + test_assert(!(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL)); + /* `equals` is automatically generated: */ + test_assert(!(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL)); + + + WithLessThan a = {1}; + WithLessThan b = {2}; + WithLessThan c = {1}; + + test_assert(compare(ecs, component, &a, &b) < 0); + test_assert(compare(ecs, component, &b, &a) > 0); + test_assert(compare(ecs, component, &a, &c) == 0); + test_assert(compare(ecs, component, &b, &c) > 0); + test_assert(compare(ecs, component, &c, &b) < 0); + test_assert(compare(ecs, component, &b, &b) == 0); + + /* test using autogenerated equals operator: */ + test_assert(equals(ecs, component, &a, &b) == false); + test_assert(equals(ecs, component, &a, &c) == true); + test_assert(equals(ecs, component, &a, &a) == true); +} + +void ComponentLifecycle_compare_WithLessAndGreaterThan(void) { + flecs::world ecs; + + auto component = ecs.component(); + + const ecs_type_hooks_t* hooks = ecs_get_hooks_id(ecs, component); + test_assert(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL); + test_assert(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL); + + /* generate cmp operator from C++ operator> and operator< */ + component.on_compare(); + + hooks = ecs_get_hooks_id(ecs, component); + test_assert(!(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL)); + /* `equals` is automatically generated: */ + test_assert(!(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL)); + + WithLessAndGreaterThan a = {1}; + WithLessAndGreaterThan b = {2}; + WithLessAndGreaterThan c = {1}; + + test_assert(compare(ecs, component, &a, &b) < 0); + test_assert(compare(ecs, component, &b, &a) > 0); + test_assert(compare(ecs, component, &a, &c) == 0); + test_assert(compare(ecs, component, &b, &c) > 0); + test_assert(compare(ecs, component, &c, &b) < 0); + test_assert(compare(ecs, component, &b, &b) == 0); + + /* test using autogenerated equals operator: */ + test_assert(equals(ecs, component, &a, &b) == false); + test_assert(equals(ecs, component, &a, &c) == true); + test_assert(equals(ecs, component, &a, &a) == true); +} + +void ComponentLifecycle_compare_WithEqualsAndGreaterThan(void) { + flecs::world ecs; + + auto component = ecs.component(); + + const ecs_type_hooks_t* hooks = ecs_get_hooks_id(ecs, component); + test_assert(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL); + test_assert(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL); + + /* generate cmp operator from C++ operator> and operator== */ + component.on_compare(); + + hooks = ecs_get_hooks_id(ecs, component); + test_assert(!(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL)); + /* `equals` is automatically generated: */ + test_assert(!(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL)); + + WithEqualsAndGreaterThan a = {1}; + WithEqualsAndGreaterThan b = {2}; + WithEqualsAndGreaterThan c = {1}; + + test_assert(compare(ecs, component, &a, &b) < 0); + test_assert(compare(ecs, component, &b, &a) > 0); + test_assert(compare(ecs, component, &a, &c) == 0); + test_assert(compare(ecs, component, &b, &c) > 0); + test_assert(compare(ecs, component, &c, &b) < 0); + test_assert(compare(ecs, component, &b, &b) == 0); + + /* test using equals operator: */ + test_assert(equals(ecs, component, &a, &b) == false); + test_assert(equals(ecs, component, &a, &c) == true); + test_assert(equals(ecs, component, &a, &a) == true); +} + +void ComponentLifecycle_compare_WithEqualsAndLessThan(void) { + flecs::world ecs; + + auto component = ecs.component(); + + const ecs_type_hooks_t* hooks = ecs_get_hooks_id(ecs, component); + test_assert(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL); + test_assert(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL); + + /* generate cmp operator from C++ operator< and operator== */ + component.on_compare(); + + hooks = ecs_get_hooks_id(ecs, component); + test_assert(!(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL)); + /* `equals` is automatically generated: */ + test_assert(!(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL)); + + + WithEqualsAndLessThan a = {1}; + WithEqualsAndLessThan b = {2}; + WithEqualsAndLessThan c = {1}; + + test_assert(compare(ecs, component, &a, &b) < 0); + test_assert(compare(ecs, component, &b, &a) > 0); + test_assert(compare(ecs, component, &a, &c) == 0); + test_assert(compare(ecs, component, &b, &c) > 0); + test_assert(compare(ecs, component, &c, &b) < 0); + test_assert(compare(ecs, component, &b, &b) == 0); + + /* test using equals operator: */ + test_assert(equals(ecs, component, &a, &b) == false); + test_assert(equals(ecs, component, &a, &c) == true); + test_assert(equals(ecs, component, &a, &a) == true); + + +} + +void ComponentLifecycle_compare_WithEqualsOnly(void) { + flecs::world ecs; + + auto component = ecs.component(); + + const ecs_type_hooks_t* hooks = ecs_get_hooks_id(ecs, component); + test_assert(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL); + test_assert(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL); + + /* generate equals operator from C++ operator== */ + component.on_equals(); + + hooks = ecs_get_hooks_id(ecs, component); + + /* can't compare if no < or > operators are defined */ + test_assert(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL); + + /* operator equals is defined: */ + test_assert(!(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL)); + + WithEqualsOnly a = {1}; + WithEqualsOnly b = {2}; + WithEqualsOnly c = {1}; + + test_assert(equals(ecs, component, &a, &b) == false); + test_assert(equals(ecs, component, &a, &c) == true); + test_assert(equals(ecs, component, &a, &a) == true); + +} + +void ComponentLifecycle_compare_WithoutOperators(void) { + flecs::world ecs; + + auto component = ecs.component(); + + const ecs_type_hooks_t* hooks = ecs_get_hooks_id(ecs, component); + + /* can't compare if no operators are defined at all */ + test_assert(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL); + test_assert(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL); + + /* define operator equals via a explicit callback: */ + component.on_equals([]( + const WithoutOperators *a, + const WithoutOperators *b, + const ecs_type_info_t* ti) -> bool { + return a->value == b->value; + }); + + hooks = ecs_get_hooks_id(ecs, component); + + test_assert(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL); + test_assert(!(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL)); + + WithoutOperators a = {1}; + WithoutOperators b = {2}; + WithoutOperators c = {1}; + + test_assert(equals(ecs, component, &a, &b) == false); + test_assert(equals(ecs, component, &a, &c) == true); + test_assert(equals(ecs, component, &a, &a) == true); + + component.on_compare([]( + const WithoutOperators *a, + const WithoutOperators *b, + const ecs_type_info_t *ti) -> int { + return a->value - b->value; + }); + + hooks = ecs_get_hooks_id(ecs, component); + + test_assert(!(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL)); + test_assert(!(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL)); + + test_assert(compare(ecs, component, &a, &b) < 0); + test_assert(compare(ecs, component, &b, &a) > 0); + test_assert(compare(ecs, component, &a, &c) == 0); + test_assert(compare(ecs, component, &b, &c) > 0); + test_assert(compare(ecs, component, &c, &b) < 0); + test_assert(compare(ecs, component, &b, &b) == 0); +} + + +template +struct TestUnsignedEnum { + enum Type : T { + Red = 1, + Yellow = 2, + Blue = 3 + }; +}; + +template +struct TestSignedEnum { + enum Type : T { + Red = -1, + Yellow = 0, + Blue = 1 + }; +}; + +void ComponentLifecycle_compare_uint8_Enum(void) { + + flecs::world ecs; + + using Enum8 = TestUnsignedEnum::Type; + + auto component = ecs.component(); + + const ecs_type_hooks_t* hooks = ecs_get_hooks_id(ecs, component); + + test_assert(!(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL)); + test_assert(!(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL)); + + Enum8 a = Enum8::Red; + Enum8 b = Enum8::Yellow; + Enum8 c = Enum8::Red; + + test_assert(compare(ecs, component, &a, &b) < 0); + test_assert(compare(ecs, component, &b, &a) > 0); + test_assert(compare(ecs, component, &a, &c) == 0); + test_assert(compare(ecs, component, &b, &c) > 0); + test_assert(compare(ecs, component, &c, &b) < 0); + test_assert(compare(ecs, component, &b, &b) == 0); + + test_assert(equals(ecs, component, &a, &c)); + test_assert(equals(ecs, component, &b, &b)); +} + + +void ComponentLifecycle_compare_uint16_Enum(void) { + + flecs::world ecs; + + using Enum16 = TestUnsignedEnum::Type; + + auto component = ecs.component(); + + const ecs_type_hooks_t* hooks = ecs_get_hooks_id(ecs, component); + + test_assert(!(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL)); + test_assert(!(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL)); + + Enum16 a = Enum16::Red; + Enum16 b = Enum16::Yellow; + Enum16 c = Enum16::Red; + + test_assert(compare(ecs, component, &a, &b) < 0); + test_assert(compare(ecs, component, &b, &a) > 0); + test_assert(compare(ecs, component, &a, &c) == 0); + test_assert(compare(ecs, component, &b, &c) > 0); + test_assert(compare(ecs, component, &c, &b) < 0); + test_assert(compare(ecs, component, &b, &b) == 0); + + test_assert(equals(ecs, component, &a, &c)); + test_assert(equals(ecs, component, &b, &b)); +} + +void ComponentLifecycle_compare_uint32_Enum(void) { + + flecs::world ecs; + + using Enum32 = TestUnsignedEnum::Type; + + auto component = ecs.component(); + + const ecs_type_hooks_t* hooks = ecs_get_hooks_id(ecs, component); + + test_assert(!(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL)); + test_assert(!(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL)); + + Enum32 a = Enum32::Red; + Enum32 b = Enum32::Yellow; + Enum32 c = Enum32::Red; + + test_assert(compare(ecs, component, &a, &b) < 0); + test_assert(compare(ecs, component, &b, &a) > 0); + test_assert(compare(ecs, component, &a, &c) == 0); + test_assert(compare(ecs, component, &b, &c) > 0); + test_assert(compare(ecs, component, &c, &b) < 0); + test_assert(compare(ecs, component, &b, &b) == 0); + + test_assert(equals(ecs, component, &a, &c)); + test_assert(equals(ecs, component, &b, &b)); +} + + +void ComponentLifecycle_compare_uint64_Enum(void) { + + flecs::world ecs; + + using Enum64 = TestUnsignedEnum::Type; + + auto component = ecs.component(); + + const ecs_type_hooks_t* hooks = ecs_get_hooks_id(ecs, component); + + test_assert(!(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL)); + test_assert(!(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL)); + + Enum64 a = Enum64::Red; + Enum64 b = Enum64::Yellow; + Enum64 c = Enum64::Red; + + test_assert(compare(ecs, component, &a, &b) < 0); + test_assert(compare(ecs, component, &b, &a) > 0); + test_assert(compare(ecs, component, &a, &c) == 0); + test_assert(compare(ecs, component, &b, &c) > 0); + test_assert(compare(ecs, component, &c, &b) < 0); + test_assert(compare(ecs, component, &b, &b) == 0); + + test_assert(equals(ecs, component, &a, &c)); + test_assert(equals(ecs, component, &b, &b)); +} + +void ComponentLifecycle_compare_int8_Enum(void) { + + flecs::world ecs; + + using Enum8 = TestSignedEnum::Type; + + auto component = ecs.component(); + + const ecs_type_hooks_t* hooks = ecs_get_hooks_id(ecs, component); + + test_assert(!(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL)); + test_assert(!(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL)); + + Enum8 a = Enum8::Red; + Enum8 b = Enum8::Yellow; + Enum8 c = Enum8::Red; + + test_assert(compare(ecs, component, &a, &b) < 0); + test_assert(compare(ecs, component, &b, &a) > 0); + test_assert(compare(ecs, component, &a, &c) == 0); + test_assert(compare(ecs, component, &b, &c) > 0); + test_assert(compare(ecs, component, &c, &b) < 0); + test_assert(compare(ecs, component, &b, &b) == 0); + + test_assert(equals(ecs, component, &a, &c)); + test_assert(equals(ecs, component, &b, &b)); +} + +void ComponentLifecycle_compare_int16_Enum(void) { + + flecs::world ecs; + + using Enum16 = TestSignedEnum::Type; + + auto component = ecs.component(); + + const ecs_type_hooks_t* hooks = ecs_get_hooks_id(ecs, component); + + test_assert(!(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL)); + test_assert(!(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL)); + + Enum16 a = Enum16::Red; + Enum16 b = Enum16::Yellow; + Enum16 c = Enum16::Red; + + test_assert(compare(ecs, component, &a, &b) < 0); + test_assert(compare(ecs, component, &b, &a) > 0); + test_assert(compare(ecs, component, &a, &c) == 0); + test_assert(compare(ecs, component, &b, &c) > 0); + test_assert(compare(ecs, component, &c, &b) < 0); + test_assert(compare(ecs, component, &b, &b) == 0); + + test_assert(equals(ecs, component, &a, &c)); + test_assert(equals(ecs, component, &b, &b)); +} + +void ComponentLifecycle_compare_int32_Enum(void) { + + flecs::world ecs; + + using Enum32 = TestSignedEnum::Type; + + auto component = ecs.component(); + + const ecs_type_hooks_t* hooks = ecs_get_hooks_id(ecs, component); + + test_assert(!(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL)); + test_assert(!(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL)); + + Enum32 a = Enum32::Red; + Enum32 b = Enum32::Yellow; + Enum32 c = Enum32::Red; + + test_assert(compare(ecs, component, &a, &b) < 0); + test_assert(compare(ecs, component, &b, &a) > 0); + test_assert(compare(ecs, component, &a, &c) == 0); + test_assert(compare(ecs, component, &b, &c) > 0); + test_assert(compare(ecs, component, &c, &b) < 0); + test_assert(compare(ecs, component, &b, &b) == 0); + + test_assert(equals(ecs, component, &a, &c)); + test_assert(equals(ecs, component, &b, &b)); +} + +void ComponentLifecycle_compare_int64_Enum(void) { + + flecs::world ecs; + + using Enum64 = TestSignedEnum::Type; + + auto component = ecs.component(); + + const ecs_type_hooks_t* hooks = ecs_get_hooks_id(ecs, component); + + test_assert(!(hooks->flags & ECS_TYPE_HOOK_CMP_ILLEGAL)); + test_assert(!(hooks->flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL)); + + Enum64 a = Enum64::Red; + Enum64 b = Enum64::Yellow; + Enum64 c = Enum64::Red; + + test_assert(compare(ecs, component, &a, &b) < 0); + test_assert(compare(ecs, component, &b, &a) > 0); + test_assert(compare(ecs, component, &a, &c) == 0); + test_assert(compare(ecs, component, &b, &c) > 0); + test_assert(compare(ecs, component, &c, &b) < 0); + test_assert(compare(ecs, component, &b, &b) == 0); + + test_assert(equals(ecs, component, &a, &c)); + test_assert(equals(ecs, component, &b, &b)); +} diff --git a/test/cpp/src/main.cpp b/test/cpp/src/main.cpp index e82ab2798..91ec5db42 100644 --- a/test/cpp/src/main.cpp +++ b/test/cpp/src/main.cpp @@ -1049,6 +1049,21 @@ void ComponentLifecycle_sparse_component(void); void ComponentLifecycle_count_in_add_hook(void); void ComponentLifecycle_count_in_remove_hook(void); void ComponentLifecycle_set_multiple_hooks(void); +void ComponentLifecycle_compare_WithGreaterThan(void); +void ComponentLifecycle_compare_WithLessThan(void); +void ComponentLifecycle_compare_WithLessAndGreaterThan(void); +void ComponentLifecycle_compare_WithEqualsAndGreaterThan(void); +void ComponentLifecycle_compare_WithEqualsAndLessThan(void); +void ComponentLifecycle_compare_WithEqualsOnly(void); +void ComponentLifecycle_compare_WithoutOperators(void); +void ComponentLifecycle_compare_uint8_Enum(void); +void ComponentLifecycle_compare_uint16_Enum(void); +void ComponentLifecycle_compare_uint32_Enum(void); +void ComponentLifecycle_compare_uint64_Enum(void); +void ComponentLifecycle_compare_int8_Enum(void); +void ComponentLifecycle_compare_int16_Enum(void); +void ComponentLifecycle_compare_int32_Enum(void); +void ComponentLifecycle_compare_int64_Enum(void); // Testsuite 'Refs' void Refs_get_ref_by_ptr(void); @@ -5534,6 +5549,66 @@ bake_test_case ComponentLifecycle_testcases[] = { { "set_multiple_hooks", ComponentLifecycle_set_multiple_hooks + }, + { + "compare_WithGreaterThan", + ComponentLifecycle_compare_WithGreaterThan + }, + { + "compare_WithLessThan", + ComponentLifecycle_compare_WithLessThan + }, + { + "compare_WithLessAndGreaterThan", + ComponentLifecycle_compare_WithLessAndGreaterThan + }, + { + "compare_WithEqualsAndGreaterThan", + ComponentLifecycle_compare_WithEqualsAndGreaterThan + }, + { + "compare_WithEqualsAndLessThan", + ComponentLifecycle_compare_WithEqualsAndLessThan + }, + { + "compare_WithEqualsOnly", + ComponentLifecycle_compare_WithEqualsOnly + }, + { + "compare_WithoutOperators", + ComponentLifecycle_compare_WithoutOperators + }, + { + "compare_uint8_Enum", + ComponentLifecycle_compare_uint8_Enum + }, + { + "compare_uint16_Enum", + ComponentLifecycle_compare_uint16_Enum + }, + { + "compare_uint32_Enum", + ComponentLifecycle_compare_uint32_Enum + }, + { + "compare_uint64_Enum", + ComponentLifecycle_compare_uint64_Enum + }, + { + "compare_int8_Enum", + ComponentLifecycle_compare_int8_Enum + }, + { + "compare_int16_Enum", + ComponentLifecycle_compare_int16_Enum + }, + { + "compare_int32_Enum", + ComponentLifecycle_compare_int32_Enum + }, + { + "compare_int64_Enum", + ComponentLifecycle_compare_int64_Enum } }; @@ -7158,7 +7233,7 @@ static bake_test_suite suites[] = { "ComponentLifecycle", NULL, NULL, - 89, + 104, ComponentLifecycle_testcases }, { diff --git a/test/meta/project.json b/test/meta/project.json index 920605671..7cf621648 100644 --- a/test/meta/project.json +++ b/test/meta/project.json @@ -115,6 +115,8 @@ "move_illegal", "copy", "copy_illegal", + "cmp_illegal", + "equals_illegal", "trivial_array", "array_ctor", "array_ctor_illegal", @@ -124,8 +126,10 @@ "array_move_illegal", "array_copy", "array_copy_illegal", + "array_cmp_illegal", "vector_lifecycle", "vector_lifecycle_trivial_type", + "vector_cmp_illegal", "opaque", "struct_with_ints", "struct_with_strings", @@ -1078,6 +1082,59 @@ "unit_prefix_from_suspend_defer", "quantity_from_suspend_defer" ] + },{ + "id": "PrimitiveCompare", + "testcases": [ + "bool", + "char", + "byte", + "u8", + "u16", + "u32", + "u64", + "uptr", + "i8", + "i16", + "i32", + "i64", + "iptr", + "f32", + "f64", + "entity", + "id", + "string", + "const_string" + ] + },{ + "id": "RttCompare", + "testcases": [ + "struct_with_ints", + "struct_with_strings", + "struct_with_opaque", + "nested_struct_with_strings", + "struct_with_array_of_strings", + "struct_with_array_of_array_of_strings", + "struct_with_vector_of_ints", + "struct_with_vector_of_strings", + "nested_struct_with_vector_of_ints", + "nested_struct_with_vector_of_strings", + "array_of_ints", + "array_of_strings", + "array_of_struct_with_ints", + "array_of_struct_with_strings", + "array_of_struct_with_opaques", + "array_of_array_of_strings", + "array_of_array_of_struct_with_strings", + "array_of_vectors_of_ints", + "array_of_vectors_of_strings", + "array_of_opaque", + "vector_of_ints", + "vector_of_strings", + "vector_of_struct_with_ints", + "vector_of_struct_with_strings", + "vector_of_arrays_of_strings", + "vector_of_opaque" + ] }] } } diff --git a/test/meta/src/PrimitiveCompare.c b/test/meta/src/PrimitiveCompare.c new file mode 100644 index 000000000..9253e6711 --- /dev/null +++ b/test/meta/src/PrimitiveCompare.c @@ -0,0 +1,594 @@ +#include +#include +#include "flecs.h" + + +static +int cmp(const void *a, const void *b, const ecs_type_info_t* ti) { + return ti->hooks.cmp(a, b, ti); +} + +static +bool equals(const void *a, const void *b, const ecs_type_info_t* ti) { + return ti->hooks.equals(a, b, ti); +} + +const ecs_type_info_t *sort_ti = NULL; +static +int compare_element(const void *a, const void *b) { + return cmp(a, b, sort_ti); +} + +static +void sort_array(const ecs_type_info_t* ti, void *arr, ecs_size_t num_elements) { + sort_ti = ti; + qsort(arr, num_elements, sort_ti->size, compare_element); +} + +static +bool str_equals(const char* a, const char* b) { + if(a == b) { + return true; + } + if(a == NULL) { + return false; + } + if(b == NULL) { + return false; + } + return ecs_os_strcmp(a,b) == 0; +} + +/* Primitive comparer testing + * Comparer is demonstrated to work if it successfully sorts an array of primitives. +*/ + +void PrimitiveCompare_bool(void) { + ecs_world_t *world = ecs_init(); + + ecs_bool_t arr[] = {true, false, true, false}; + ecs_bool_t expected[] = {false, false, true, true}; + + const ecs_type_info_t* ti = ecs_get_type_info(world, ecs_id(ecs_bool_t)); + + /* test "less" */ + test_assert(cmp(&arr[1], &arr[0], ti) < 0); /* false < true */ + + /* test "greater" */ + test_assert(cmp(&arr[0], &arr[1], ti) > 0); /* true > false */ + + /* test "equal" via cmp hook */ + test_assert(cmp(&arr[0], &arr[0], ti) == 0); /* true == true */ + /* test "equal" via equals hook */ + test_assert(equals(&arr[0], &arr[0], ti)); /* true == true */ + + /* further test by sorting the array */ + sort_array(ti, arr, 4); + + test_assert(memcmp(arr, expected, sizeof(arr)) == 0); + + ecs_fini(world); +} + +void PrimitiveCompare_char(void) { + ecs_world_t *world = ecs_init(); + + ecs_char_t arr[] = {'z', 'a', 'm', 'a', 'x', 'b'}; + ecs_char_t expected[] = {'a', 'a', 'b', 'm', 'x', 'z'}; + + const ecs_type_info_t* ti = ecs_get_type_info(world, ecs_id(ecs_char_t)); + + /* test "less" */ + test_assert(cmp(&arr[1], &arr[0], ti) < 0); /* 'a' < 'z' */ + + /* test "greater" */ + test_assert(cmp(&arr[0], &arr[1], ti) > 0); /* 'z' > 'a' */ + + /* test "equal" via cmp hook */ + test_assert(cmp(&arr[1], &arr[1], ti) == 0); /* 'a' == 'a' */ + /* test "equal" via equals hook */ + test_assert(equals(&arr[1], &arr[1], ti)); /* 'a' == 'a' */ + + /* further test by sorting the array */ + sort_array(ti, arr, 6); + + test_assert(memcmp(arr, expected, sizeof(arr)) == 0); + + ecs_fini(world); +} + +void PrimitiveCompare_byte(void) { + ecs_world_t *world = ecs_init(); + + ecs_byte_t arr[] = {0xFF, 0x01, 0x7F, 0x01, 0x00}; + ecs_byte_t expected[] = {0x00, 0x01, 0x01, 0x7F, 0xFF}; + + const ecs_type_info_t* ti = ecs_get_type_info(world, ecs_id(ecs_byte_t)); + + /* test "less" */ + test_assert(cmp(&arr[1], &arr[0], ti) < 0); /* 0x01 < 0xFF */ + + /* test "greater" */ + test_assert(cmp(&arr[0], &arr[1], ti) > 0); /* 0xFF > 0x01 */ + + /* test "equal" via cmp hook */ + test_assert(cmp(&arr[1], &arr[1], ti) == 0); /* 0x01 == 0x01 */ + /* test "equal" via equals hook */ + test_assert(equals(&arr[1], &arr[1], ti)); /* 0x01 == 0x01 */ + + /* further test by sorting the array */ + sort_array(ti, arr, 5); + + test_assert(memcmp(arr, expected, sizeof(arr)) == 0); + + ecs_fini(world); +} + +void PrimitiveCompare_u8(void) { + ecs_world_t *world = ecs_init(); + + ecs_u8_t arr[] = {1, 79, 12, 3, 255, 79, 0, 14}; + ecs_u8_t expected[] = {0, 1, 3, 12, 14, 79, 79, 255}; + + const ecs_type_info_t* ti = ecs_get_type_info(world, ecs_id(ecs_u8_t)); + + /* test "less" */ + test_assert(cmp(&arr[0], &arr[1], ti) < 0); /* 1 < 79 */ + + /* test "greater" */ + test_assert(cmp(&arr[1], &arr[0], ti) > 0); /* 79 > 1 */ + + /* test "equal" via cmp hook */ + test_assert(cmp(&arr[5], &arr[1], ti) == 0); /* 79 == 79 */ + /* test "equal" via equals hook */ + test_assert(equals(&arr[5], &arr[1], ti)); /* 79 == 79 */ + + /* further test by sorting the array */ + sort_array(ti, arr, 8); + + test_assert(memcmp(arr, expected, sizeof(arr)) == 0); + + ecs_fini(world); +} + +void PrimitiveCompare_u16(void) { + ecs_world_t *world = ecs_init(); + + ecs_u16_t arr[] = {1024, 65535, 0, 1, 1024, 256}; + ecs_u16_t expected[] = {0, 1, 256, 1024, 1024, 65535}; + + const ecs_type_info_t* ti = ecs_get_type_info(world, ecs_id(ecs_u16_t)); + + /* test "less" */ + test_assert(cmp(&arr[2], &arr[1], ti) < 0); /* 0 < 65535 */ + + /* test "greater" */ + test_assert(cmp(&arr[1], &arr[2], ti) > 0); /* 65535 > 0 */ + + /* test "equal" via cmp hook */ + test_assert(cmp(&arr[0], &arr[4], ti) == 0); /* 1024 == 1024 */ + /* test "equal" via equals hook */ + test_assert(equals(&arr[0], &arr[4], ti)); /* 1024 == 1024 */ + + /* further test by sorting the array */ + sort_array(ti, arr, 6); + + test_assert(memcmp(arr, expected, sizeof(arr)) == 0); + + ecs_fini(world); +} + +void PrimitiveCompare_u32(void) { + ecs_world_t *world = ecs_init(); + + ecs_u32_t arr[] = {100000, 500, 4294967295, 100000, 0}; + ecs_u32_t expected[] = {0, 500, 100000, 100000, 4294967295}; + + const ecs_type_info_t* ti = ecs_get_type_info(world, ecs_id(ecs_u32_t)); + + /* test "less" */ + test_assert(cmp(&arr[4], &arr[1], ti) < 0); /* 0 < 500 */ + + /* test "greater" */ + test_assert(cmp(&arr[2], &arr[1], ti) > 0); /* 4294967295 > 500 */ + + /* test "equal" via cmp hook */ + test_assert(cmp(&arr[0], &arr[3], ti) == 0); /* 100000 == 100000 */ + /* test "equal" via equals hook */ + test_assert(equals(&arr[0], &arr[3], ti)); /* 100000 == 100000 */ + + /* further test by sorting the array */ + sort_array(ti, arr, 5); + + test_assert(memcmp(arr, expected, sizeof(arr)) == 0); + + ecs_fini(world); +} + +void PrimitiveCompare_u64(void) { + ecs_world_t *world = ecs_init(); + + ecs_u64_t arr[] = {18446744073709551615ULL, 0, + 1000, 18446744073709551615ULL, 42}; + ecs_u64_t expected[] = {0, 42, 1000, + 18446744073709551615ULL, 18446744073709551615ULL}; + + const ecs_type_info_t* ti = ecs_get_type_info(world, ecs_id(ecs_u64_t)); + + /* test "less" */ + /* 0 < 18446744073709551615 */ + test_assert(cmp(&arr[1], &arr[0], ti) < 0); + + /* test "greater" */ + /* 18446744073709551615 > 1000 */ + test_assert(cmp(&arr[0], &arr[2], ti) > 0); + + /* test "equal" via cmp hook */ + /* 18446744073709551615 == 18446744073709551615 */ + test_assert(cmp(&arr[0], &arr[3], ti) == 0); + /* test "equal" via equals hook */ + test_assert(equals(&arr[0], &arr[3], ti)); + + /* further test by sorting the array */ + sort_array(ti, arr, 5); + + test_assert(memcmp(arr, expected, sizeof(arr)) == 0); + + ecs_fini(world); +} + +void PrimitiveCompare_uptr(void) { + ecs_world_t *world = ecs_init(); + + ecs_uptr_t arr[] = {(ecs_uptr_t)0x1234, (ecs_uptr_t)0x5678, (ecs_uptr_t)0x1234, (ecs_uptr_t)0x9ABC}; + ecs_uptr_t expected[] = {(ecs_uptr_t)0x1234, (ecs_uptr_t)0x1234, (ecs_uptr_t)0x5678, (ecs_uptr_t)0x9ABC}; + + const ecs_type_info_t* ti = ecs_get_type_info(world, ecs_id(ecs_uptr_t)); + + /* test "less" */ + test_assert(cmp(&arr[0], &arr[1], ti) < 0); /* 0x1234 < 0x5678 */ + + /* test "greater" */ + test_assert(cmp(&arr[3], &arr[1], ti) > 0); /* 0x9ABC > 0x5678 */ + + /* test "equal" via cmp hook */ + test_assert(cmp(&arr[0], &arr[2], ti) == 0); /* 0x1234 == 0x1234 */ + /* test "equal" via equals hook */ + test_assert(equals(&arr[0], &arr[2], ti)); /* 0x1234 == 0x1234 */ + + /* further test by sorting the array */ + sort_array(ti, arr, 4); + + test_assert(memcmp(arr, expected, sizeof(arr)) == 0); + + ecs_fini(world); +} + +void PrimitiveCompare_i8(void) { + ecs_world_t *world = ecs_init(); + + ecs_i8_t arr[] = {-128, 127, 0, -1, 127, 1}; + ecs_i8_t expected[] = {-128, -1, 0, 1, 127, 127}; + + const ecs_type_info_t* ti = ecs_get_type_info(world, ecs_id(ecs_i8_t)); + + /* test "less" */ + test_assert(cmp(&arr[0], &arr[1], ti) < 0); /* -128 < 127 */ + + /* test "greater" */ + test_assert(cmp(&arr[1], &arr[2], ti) > 0); /* 127 > 0 */ + + /* test "equal" via cmp hook */ + test_assert(cmp(&arr[1], &arr[4], ti) == 0); /* 127 == 127 */ + /* test "equal" via equals hook */ + test_assert(equals(&arr[1], &arr[4], ti)); /* 127 == 127 */ + + /* further test by sorting the array */ + sort_array(ti, arr, 6); + + test_assert(memcmp(arr, expected, sizeof(arr)) == 0); + + ecs_fini(world); +} + +void PrimitiveCompare_i16(void) { + ecs_world_t *world = ecs_init(); + + ecs_i16_t arr[] = {-32768, 32767, 100, -100, 32767}; + ecs_i16_t expected[] = {-32768, -100, 100, 32767, 32767}; + + const ecs_type_info_t* ti = ecs_get_type_info(world, ecs_id(ecs_i16_t)); + + /* test "less" */ + test_assert(cmp(&arr[0], &arr[1], ti) < 0); /* -32768 < 32767 */ + + /* test "greater" */ + test_assert(cmp(&arr[1], &arr[2], ti) > 0); /* 32767 > 100 */ + + /* test "equal" via cmp hook */ + test_assert(cmp(&arr[1], &arr[4], ti) == 0); /* 32767 == 32767 */ + /* test "equal" via equals hook */ + test_assert(equals(&arr[1], &arr[4], ti)); /* 32767 == 32767 */ + + /* further test by sorting the array */ + sort_array(ti, arr, 5); + + test_assert(memcmp(arr, expected, sizeof(arr)) == 0); + + ecs_fini(world); +} + +void PrimitiveCompare_i32(void) { + ecs_world_t *world = ecs_init(); + + ecs_i32_t arr[] = {-100000, 50000, 0, -100000, 100000}; + ecs_i32_t expected[] = {-100000, -100000, 0, 50000, 100000}; + + const ecs_type_info_t* ti = ecs_get_type_info(world, ecs_id(ecs_i32_t)); + + /* test "less" */ + test_assert(cmp(&arr[0], &arr[1], ti) < 0); /* -100000 < 50000 */ + + /* test "greater" */ + test_assert(cmp(&arr[1], &arr[2], ti) > 0); /* 50000 > 0 */ + + /* test "equal" via cmp hook */ + test_assert(cmp(&arr[0], &arr[3], ti) == 0); /* -100000 == -100000 */ + /* test "equal" via equals hook */ + test_assert(equals(&arr[0], &arr[3], ti)); /* -100000 == -100000 */ + + /* further test by sorting the array */ + sort_array(ti, arr, 5); + + test_assert(memcmp(arr, expected, sizeof(arr)) == 0); + + ecs_fini(world); +} + +void PrimitiveCompare_i64(void) { + ecs_world_t *world = ecs_init(); + + ecs_i64_t arr[] = {-9223372036854775807LL, 9223372036854775807LL, + 0, -1000, 9223372036854775807LL}; + ecs_i64_t expected[] = {-9223372036854775807LL, -1000, 0, + 9223372036854775807LL, 9223372036854775807LL}; + + const ecs_type_info_t* ti = ecs_get_type_info(world, ecs_id(ecs_i64_t)); + + /* test "less" */ + /* -9223372036854775807 < 9223372036854775807 */ + test_assert(cmp(&arr[0], &arr[1], ti) < 0); + + /* test "greater" */ + /* 9223372036854775807 > 0 */ + test_assert(cmp(&arr[1], &arr[2], ti) > 0); + + /* test "equal" via cmp hook */ + /* 9223372036854775807 == 9223372036854775807 */ + test_assert(cmp(&arr[1], &arr[4], ti) == 0); + /* test "equal" via equals hook */ + test_assert(equals(&arr[1], &arr[4], ti)); + + /* further test by sorting the array */ + sort_array(ti, arr, 5); + + test_assert(memcmp(arr, expected, sizeof(arr)) == 0); + + ecs_fini(world); +} + +void PrimitiveCompare_iptr(void) { + ecs_world_t *world = ecs_init(); + + ecs_iptr_t arr[] = {(ecs_iptr_t)-1000, (ecs_iptr_t)500, (ecs_iptr_t)0, (ecs_iptr_t)-1000}; + ecs_iptr_t expected[] = {(ecs_iptr_t)-1000, (ecs_iptr_t)-1000, (ecs_iptr_t)0, (ecs_iptr_t)500}; + + const ecs_type_info_t* ti = ecs_get_type_info(world, ecs_id(ecs_iptr_t)); + + /* test "less" */ + test_assert(cmp(&arr[0], &arr[1], ti) < 0); /* -1000 < 500 */ + + /* test "greater" */ + test_assert(cmp(&arr[1], &arr[2], ti) > 0); /* 500 > 0 */ + + /* test "equal" via cmp hook */ + test_assert(cmp(&arr[0], &arr[3], ti) == 0); /* -1000 == -1000 */ + /* test "equal" via equals hook */ + test_assert(equals(&arr[0], &arr[3], ti)); /* -1000 == -1000 */ + + /* further test by sorting the array */ + sort_array(ti, arr, 4); + + test_assert(memcmp(arr, expected, sizeof(arr)) == 0); + + ecs_fini(world); +} + +void PrimitiveCompare_f32(void) { + ecs_world_t *world = ecs_init(); + + ecs_f32_t arr[] = {3.14f, 2.71f, -1.0f, 2.71f, 0.0f}; + ecs_f32_t expected[] = {-1.0f, 0.0f, 2.71f, 2.71f, 3.14f}; + + const ecs_type_info_t* ti = ecs_get_type_info(world, ecs_id(ecs_f32_t)); + + /* test "less" */ + test_assert(cmp(&arr[2], &arr[0], ti) < 0); /* -1.0 < 3.14 */ + + /* test "greater" */ + test_assert(cmp(&arr[0], &arr[4], ti) > 0); /* 3.14 > 0.0 */ + + /* test "equal" via cmp hook */ + test_assert(cmp(&arr[1], &arr[3], ti) == 0); /* 2.71 == 2.71 */ + /* test "equal" via equals hook */ + test_assert(equals(&arr[1], &arr[3], ti)); /* 2.71 == 2.71 */ + + /* further test by sorting the array */ + sort_array(ti, arr, 5); + + test_assert(memcmp(arr, expected, sizeof(arr)) == 0); + + ecs_fini(world); +} + +void PrimitiveCompare_f64(void) { + ecs_world_t *world = ecs_init(); + + ecs_f64_t arr[] = {3.14159, 2.71828, -1.0, 2.71828, 0.0}; + ecs_f64_t expected[] = {-1.0, 0.0, 2.71828, 2.71828, 3.14159}; + + const ecs_type_info_t* ti = ecs_get_type_info(world, ecs_id(ecs_f64_t)); + + /* test "less" */ + test_assert(cmp(&arr[2], &arr[0], ti) < 0); /* -1.0 < 3.14159 */ + + /* test "greater" */ + test_assert(cmp(&arr[0], &arr[4], ti) > 0); /* 3.14159 > 0.0 */ + + /* test "equal" via cmp hook */ + test_assert(cmp(&arr[1], &arr[3], ti) == 0); /* 2.71828 == 2.71828 */ + /* test "equal" via equals hook */ + test_assert(equals(&arr[1], &arr[3], ti)); /* 2.71828 == 2.71828 */ + + /* further test by sorting the array */ + sort_array(ti, arr, 5); + + test_assert(memcmp(arr, expected, sizeof(arr)) == 0); + + ecs_fini(world); +} + +void PrimitiveCompare_entity(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t arr[] = {1000, 42, 1000, 500, 0}; + ecs_entity_t expected[] = {0, 42, 500, 1000, 1000}; + + const ecs_type_info_t* ti = ecs_get_type_info(world, ecs_id(ecs_entity_t)); + + /* test "less" */ + test_assert(cmp(&arr[4], &arr[1], ti) < 0); /* 0 < 42 */ + + /* test "greater" */ + test_assert(cmp(&arr[0], &arr[1], ti) > 0); /* 1000 > 42 */ + + /* test "equal" via cmp hook */ + test_assert(cmp(&arr[0], &arr[2], ti) == 0); /* 1000 == 1000 */ + /* test "equal" via equals hook */ + test_assert(equals(&arr[0], &arr[2], ti)); /* 1000 == 1000 */ + + /* further test by sorting the array */ + sort_array(ti, arr, 5); + + test_assert(memcmp(arr, expected, sizeof(arr)) == 0); + + ecs_fini(world); +} + +void PrimitiveCompare_id(void) { + ecs_world_t *world = ecs_init(); + + ecs_id_t arr[] = {1000, 42, 1000, 500, 0}; + ecs_id_t expected[] = {0, 42, 500, 1000, 1000}; + + const ecs_type_info_t* ti = ecs_get_type_info(world, ecs_id(ecs_id_t)); + + /* test "less" */ + test_assert(cmp(&arr[4], &arr[1], ti) < 0); /* 0 < 42 */ + + /* test "greater" */ + test_assert(cmp(&arr[0], &arr[1], ti) > 0); /* 1000 > 42 */ + + /* test "equal" via cmp hook */ + test_assert(cmp(&arr[0], &arr[2], ti) == 0); /* 1000 == 1000 */ + /* test "equal" via equals hook */ + test_assert(equals(&arr[0], &arr[2], ti)); /* 1000 == 1000 */ + + /* further test by sorting the array */ + sort_array(ti, arr, 5); + + test_assert(memcmp(arr, expected, sizeof(arr)) == 0); + + ecs_fini(world); +} + + +#define STRING_COUNT 8 +void PrimitiveCompare_string(void) { + ecs_world_t *world = ecs_init(); + + char* const_arr[] = {"world", "hello", NULL, "aa", "zz", "aa", "cc", "bb"}; + char* const_expected[] = {NULL, "aa", "aa", "bb", "cc", "hello", "world", "zz"}; + + ecs_string_t arr[STRING_COUNT]; + ecs_string_t expected[STRING_COUNT]; + + const ecs_type_info_t *ti = ecs_get_type_info(world, ecs_id(ecs_string_t)); + ti->hooks.copy_ctor(arr, const_arr, STRING_COUNT, ti); + ti->hooks.copy_ctor(expected, const_expected, STRING_COUNT, ti); + + /* test "less" */ + test_assert(cmp(&arr[3], &arr[7], ti) < 0); /* "aa < "bb" */ + test_assert(cmp(&arr[2], &arr[5], ti) < 0); /* NULL < "aa" */ + + /* test "greater" */ + test_assert(cmp(&arr[6], &arr[5], ti) > 0); /* "cc" > "aa" */ + + /* test "equal" via cmp hook */ + test_assert(cmp(&arr[3], &arr[5], ti) == 0); /* "aa" == "aa" */ + test_assert(cmp(&arr[2], &arr[2], ti) == 0); /* NULL == NULL */ + + /* test "equal" via equals hook */ + test_assert(equals(&arr[3], &arr[5], ti)); /* "aa" == "aa" */ + test_assert(equals(&arr[2], &arr[2], ti)); /* NULL == NULL */ + + /* further test by sorting the array */ + sort_array(ti, arr, STRING_COUNT); + + int i; + for(i = 0; i < STRING_COUNT; i++) { + test_assert(str_equals(arr[i], expected[i])); + } + + ti->hooks.dtor(arr, STRING_COUNT, ti); + ti->hooks.dtor(expected, STRING_COUNT, ti); + + ecs_fini(world); +} + +void PrimitiveCompare_const_string(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t const_string = ecs_lookup(world, "flecs.core.const_string_t"); + + char* arr[] = {"world", "hello", NULL, "aa", "zz", "aa", "cc", "bb"}; + char* expected[] = {NULL, "aa", "aa", "bb", "cc", "hello", "world", "zz"}; + + const ecs_type_info_t *ti = ecs_get_type_info(world, const_string); + + + /* test "less" */ + test_assert(cmp(&arr[3], &arr[7], ti) < 0); /* "aa < "bb" */ + test_assert(cmp(&arr[2], &arr[5], ti) < 0); /* NULL < "aa" */ + + /* test "greater" */ + test_assert(cmp(&arr[6], &arr[5], ti) > 0); /* "cc" > "aa" */ + + /* test "equal" via cmp hook */ + test_assert(cmp(&arr[3], &arr[5], ti) == 0); /* "aa" == "aa" */ + test_assert(cmp(&arr[2], &arr[2], ti) == 0); /* NULL == NULL */ + + /* test "equal" via equals hook */ + test_assert(equals(&arr[3], &arr[5], ti)); /* "aa" == "aa" */ + test_assert(equals(&arr[2], &arr[2], ti)); /* NULL == NULL */ + + /* further test by sorting the array */ + sort_array(ti, arr, STRING_COUNT); + + int i; + for(i = 0; i < STRING_COUNT; i++) { + test_assert(str_equals(arr[i], expected[i])); + } + + ecs_fini(world); +} diff --git a/test/meta/src/RttCompare.c b/test/meta/src/RttCompare.c new file mode 100644 index 000000000..f62544a9e --- /dev/null +++ b/test/meta/src/RttCompare.c @@ -0,0 +1,2389 @@ +#include +#include "flecs.h" + +static +int cmp(const ecs_world_t *world, ecs_entity_t component, ecs_entity_t ea, + ecs_entity_t eb) { + const ecs_type_info_t *ti = ecs_get_type_info(world, component); + + const void *a = ecs_get_id(world, ea, component); + const void *b = ecs_get_id(world, eb, component); + + return ti->hooks.cmp(a, b, ti); +} + +static +bool equals(const ecs_world_t *world, ecs_entity_t component, ecs_entity_t ea, + ecs_entity_t eb) { + const ecs_type_info_t *ti = ecs_get_type_info(world, component); + + const void *a = ecs_get_id(world, ea, component); + const void *b = ecs_get_id(world, eb, component); + + return ti->hooks.equals(a, b, ti); +} + +typedef struct OpaqueType { + int16_t value; +} OpaqueType; + +int opaque_type_compare(const void *a, const void *b, const ecs_type_info_t* ti) { + const OpaqueType* op_a = a; + const OpaqueType* op_b = b; + return op_a->value - op_b->value; +} + +ecs_entity_t define_opaque_type(ecs_world_t *world) { + ECS_COMPONENT(world, OpaqueType); + + ecs_type_hooks_t hooks = *ecs_get_hooks(world, OpaqueType); + hooks.cmp = opaque_type_compare; + hooks.flags &= ~ECS_TYPE_HOOK_CMP_ILLEGAL; + hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; + ecs_set_hooks_id(world, ecs_id(OpaqueType), &hooks); + + ecs_entity_t descriptor = ecs_struct(world, { + .members = { + {.name = "value", .type = ecs_id(ecs_i16_t)}, + } + }); + + ecs_opaque(world, { + .entity = ecs_id(OpaqueType), + .type = {.as_type = descriptor} + }); + + return ecs_id(OpaqueType); +} + +void RttCompare_struct_with_ints(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + ecs_i32_t a; + ecs_i32_t b; + } StructWithInts; + + ecs_entity_t struct_with_ints = ecs_struct(world, { + .members = { + {"a", ecs_id(ecs_i32_t)}, + {"b", ecs_id(ecs_i32_t)}, + } + }); + + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + StructWithInts *ptr1 = ecs_ensure_id(world, e1, struct_with_ints); + ptr1->a = 10; + ptr1->b = 20; + + StructWithInts *ptr2 = ecs_ensure_id(world, e2, struct_with_ints); + ptr2->a = 10; + ptr2->b = 25; + + StructWithInts *ptr3 = ecs_ensure_id(world, e3, struct_with_ints); + ptr3->a = 10; + ptr3->b = 20; + + /* Test "less" */ + /* {10, 20} < {10, 25} */ + test_assert(cmp(world, struct_with_ints, e1, e2) < 0); + + /* Test "greater" */ + /* {10, 25} > {10, 20} */ + test_assert(cmp(world, struct_with_ints, e2, e1) > 0); + + /* Test "equal" */ + /* {10, 20} == {10, 20} */ + test_assert(cmp(world, struct_with_ints, e1, e3) == 0); + test_assert(equals(world, struct_with_ints, e1, e3) == true); + test_assert(cmp(world, struct_with_ints, e1, e1) == 0); + test_assert(equals(world, struct_with_ints, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + + ecs_fini(world); +} + +void RttCompare_struct_with_strings(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + ecs_string_t a; + ecs_i32_t b; + ecs_string_t c; + } StructWithStrings; + + ecs_entity_t struct_with_strings = + ecs_struct(world, {.members = { + {"a", ecs_id(ecs_string_t)}, + {"b", ecs_id(ecs_i32_t)}, + {"c", ecs_id(ecs_string_t)}, + }}); + ecs_add_id(world, struct_with_strings, EcsSparse); + + /* Create three entities with StructWithStrings component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + StructWithStrings *ptr1 = ecs_ensure_id(world, e1, struct_with_strings); + ptr1->a = ecs_os_strdup("AA"); + ptr1->b = 20; + ptr1->c = ecs_os_strdup("CC"); + + StructWithStrings *ptr2 = ecs_ensure_id(world, e2, struct_with_strings); + ptr2->a = ecs_os_strdup("AA"); + ptr2->b = 25; + ptr2->c = ecs_os_strdup("BB"); + + StructWithStrings *ptr3 = ecs_ensure_id(world, e3, struct_with_strings); + ptr3->a = ecs_os_strdup("AA"); + ptr3->b = 20; + ptr3->c = ecs_os_strdup("CC"); + + /* Test "less" */ + /* {"AA", 20, "CC"} < {"AA", 25, "BB"} */ + test_assert(cmp(world, struct_with_strings, e1, e2) < 0); + + /* Test "greater" */ + /* {"AA", 25, "BB"} > {"AA", 20, "CC"} */ + test_assert(cmp(world, struct_with_strings, e2, e1) > 0); + + /* Test "equal" */ + /* {"AA", 20, "CC"} == {"AA", 20, "CC"} */ + test_assert(cmp(world, struct_with_strings, e1, e3) == 0); + test_assert(equals(world, struct_with_strings, e1, e3) == true); + test_assert(cmp(world, struct_with_strings, e1, e1) == 0); + test_assert(equals(world, struct_with_strings, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + + ecs_fini(world); +} + +void RttCompare_struct_with_opaque(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + OpaqueType a; + } StructWithOpaque; + + ecs_entity_t opaque = define_opaque_type(world); + + ecs_entity_t struct_with_opaque = ecs_struct(world, { + .members = { + {"a", opaque}, + } + }); + + /* Create three entities with StructWithOpaque component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + StructWithOpaque *ptr1 = ecs_ensure_id(world, e1, struct_with_opaque); + ptr1->a.value = 10; + + StructWithOpaque *ptr2 = ecs_ensure_id(world, e2, struct_with_opaque); + ptr2->a.value = 15; + + StructWithOpaque *ptr3 = ecs_ensure_id(world, e3, struct_with_opaque); + ptr3->a.value = 10; + + /* Test "less" */ + /* {10} < {15} */ + test_assert(cmp(world, struct_with_opaque, e1, e2) < 0); + + /* Test "greater" */ + /* {15} > {10} */ + test_assert(cmp(world, struct_with_opaque, e2, e1) > 0); + + /* Test "equal" */ + /* {10} == {10} */ + test_assert(cmp(world, struct_with_opaque, e1, e3) == 0); + test_assert(equals(world, struct_with_opaque, e1, e3) == true); + test_assert(cmp(world, struct_with_opaque, e1, e1) == 0); + test_assert(equals(world, struct_with_opaque, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + + ecs_fini(world); +} + +void RttCompare_nested_struct_with_strings(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + ecs_string_t a; + ecs_i32_t b; + ecs_string_t c; + } StructWithStrings; + + ecs_entity_t struct_with_strings = ecs_struct(world, { + .members = { + {"a", ecs_id(ecs_string_t)}, + {"b", ecs_id(ecs_i32_t)}, + {"c", ecs_id(ecs_string_t)}, + } + }); + + typedef struct { + StructWithStrings a; + ecs_i32_t b; + StructWithStrings c; + } NestedStructWithStrings; + + ecs_entity_t nested_struct_with_strings = ecs_struct(world, { + .members = { + {"a", struct_with_strings}, + {"b", ecs_id(ecs_i32_t)}, + {"c", struct_with_strings}, + } + }); + + /* Create three entities with NestedStructWithStrings component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + NestedStructWithStrings *ptr1 = ecs_ensure_id(world, e1, nested_struct_with_strings); + ptr1->a.a = ecs_os_strdup("AA"); + ptr1->a.b = 10; + ptr1->a.c = ecs_os_strdup("BB"); + ptr1->b = 20; + ptr1->c.a = ecs_os_strdup("CC"); + ptr1->c.b = 30; + ptr1->c.c = ecs_os_strdup("DD"); + + NestedStructWithStrings *ptr2 = ecs_ensure_id(world, e2, nested_struct_with_strings); + ptr2->a.a = ecs_os_strdup("AA"); + ptr2->a.b = 15; + ptr2->a.c = ecs_os_strdup("BB"); + ptr2->b = 25; + ptr2->c.a = ecs_os_strdup("CC"); + ptr2->c.b = 35; + ptr2->c.c = ecs_os_strdup("DD"); + + NestedStructWithStrings *ptr3 = ecs_ensure_id(world, e3, nested_struct_with_strings); + ptr3->a.a = ecs_os_strdup("AA"); + ptr3->a.b = 10; + ptr3->a.c = ecs_os_strdup("BB"); + ptr3->b = 20; + ptr3->c.a = ecs_os_strdup("CC"); + ptr3->c.b = 30; + ptr3->c.c = ecs_os_strdup("DD"); + + /* Test "less" */ + test_assert(cmp(world, nested_struct_with_strings, e1, e2) < 0); + + /* Test "greater" */ + test_assert(cmp(world, nested_struct_with_strings, e2, e1) > 0); + + /* Test "equal" */ + test_assert(cmp(world, nested_struct_with_strings, e1, e3) == 0); + test_assert(equals(world, nested_struct_with_strings, e1, e3) == true); + test_assert(cmp(world, nested_struct_with_strings, e1, e1) == 0); + test_assert(equals(world, nested_struct_with_strings, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + + ecs_fini(world); +} + +void RttCompare_struct_with_array_of_strings(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* ecs_string_t[3] */ array_of_strings = + ecs_array(world, {.type = ecs_id(ecs_string_t), .count = 3}); + + typedef struct { + ecs_string_t a[3]; + ecs_i32_t b; + } StructWithArrayOfStrings; + + ecs_entity_t struct_with_array_of_strings = ecs_struct(world, { + .members = { + {"a", array_of_strings}, + {"b", ecs_id(ecs_i32_t)}, + } + }); + + /* Create five entities with StructWithArrayOfStrings component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + + StructWithArrayOfStrings *ptr1 = ecs_ensure_id(world, e1, + struct_with_array_of_strings); + ptr1->a[0] = ecs_os_strdup("AA"); + ptr1->a[1] = ecs_os_strdup("BB"); + ptr1->a[2] = ecs_os_strdup("CC"); + ptr1->b = 10; + + StructWithArrayOfStrings *ptr2 = ecs_ensure_id(world, e2, + struct_with_array_of_strings); + ptr2->a[0] = ecs_os_strdup("AA"); + ptr2->a[1] = ecs_os_strdup("BB"); + ptr2->a[2] = ecs_os_strdup("CC"); + ptr2->b = 20; + + StructWithArrayOfStrings *ptr3 = ecs_ensure_id(world, e3, + struct_with_array_of_strings); + ptr3->a[0] = ecs_os_strdup("AA"); + ptr3->a[1] = ecs_os_strdup("BB"); + ptr3->a[2] = ecs_os_strdup("CC"); + ptr3->b = 10; + + StructWithArrayOfStrings *ptr4 = ecs_ensure_id(world, e4, + struct_with_array_of_strings); + ptr4->a[0] = ecs_os_strdup("AA"); + ptr4->a[1] = ecs_os_strdup("ZZ"); + ptr4->a[2] = ecs_os_strdup("CC"); + ptr4->b = 10; + + StructWithArrayOfStrings *ptr5 = ecs_ensure_id(world, e5, + struct_with_array_of_strings); + ptr5->a[0] = ecs_os_strdup("AA"); + ptr5->a[1] = ecs_os_strdup("AA"); + ptr5->a[2] = ecs_os_strdup("DD"); + ptr5->b = 15; + + /* Test "less" */ + /* {"AA", "BB", "CC", 10} < {"AA", "BB", "CC", 20} */ + test_assert(cmp(world, struct_with_array_of_strings, e1, e2) < 0); + + /* {"AA", "BB", "CC", 10} < {"AA", "ZZ", "CC", 10} */ + test_assert(cmp(world, struct_with_array_of_strings, e1, e4) < 0); + + /* {"AA", "AA", "DD", 15} < {"AA", "BB", "CC", 20} */ + test_assert(cmp(world, struct_with_array_of_strings, e5, e2) < 0); + + /* Test "greater" */ + /* {"AA", "BB", "CC", 20} > {"AA", "BB", "CC", 10} */ + test_assert(cmp(world, struct_with_array_of_strings, e2, e1) > 0); + + /* {"AA", "ZZ", "CC", 10} > {"AA", "BB", "CC", 10} */ + test_assert(cmp(world, struct_with_array_of_strings, e4, e1) > 0); + + /* {"AA", "BB", "CC", 20} > {"AA", "AA", "DD", 15} */ + test_assert(cmp(world, struct_with_array_of_strings, e2, e5) > 0); + + /* Test "equal" */ + /* {"AA", "BB", "CC", 10} == {"AA", "BB", "CC", 10} */ + test_assert(cmp(world, struct_with_array_of_strings, e1, e3) == 0); + test_assert(equals(world, struct_with_array_of_strings, e1, e3) == true); + test_assert(cmp(world, struct_with_array_of_strings, e1, e1) == 0); + test_assert(equals(world, struct_with_array_of_strings, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + ecs_delete(world, e5); + + ecs_fini(world); +} + +void RttCompare_struct_with_array_of_array_of_strings(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* ecs_string_t[3] */ array_of_strings = + ecs_array(world, {.type = ecs_id(ecs_string_t), .count = 3}); + + ecs_entity_t /* ecs_string_t[3][3] */ array_of_array_of_strings = + ecs_array(world, {.type = array_of_strings, .count = 3}); + + typedef struct { + ecs_string_t a[3][3]; + ecs_string_t b; + } StructWithArrayOfArrayOfStrings; + + ecs_entity_t struct_with_array_of_array_of_strings = ecs_struct(world, { + .members = { + {"a", array_of_array_of_strings}, + {"b", ecs_id(ecs_string_t)}, + } + }); + + /* Create five entities with StructWithArrayOfArrayOfStrings component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + + StructWithArrayOfArrayOfStrings *ptr1 = ecs_ensure_id(world, e1, + struct_with_array_of_array_of_strings); + for (int i = 0; i < 3; i++) { + ptr1->a[i][0] = ecs_os_strdup("AA"); + ptr1->a[i][1] = ecs_os_strdup("BB"); + ptr1->a[i][2] = ecs_os_strdup("CC"); + } + ptr1->b = ecs_os_strdup("DD"); + + StructWithArrayOfArrayOfStrings *ptr2 = ecs_ensure_id(world, e2, + struct_with_array_of_array_of_strings); + for (int i = 0; i < 3; i++) { + ptr2->a[i][0] = ecs_os_strdup("AA"); + ptr2->a[i][1] = ecs_os_strdup("BB"); + ptr2->a[i][2] = ecs_os_strdup("CC"); + } + ptr2->b = ecs_os_strdup("EE"); + + StructWithArrayOfArrayOfStrings *ptr3 = ecs_ensure_id(world, e3, + struct_with_array_of_array_of_strings); + for (int i = 0; i < 3; i++) { + ptr3->a[i][0] = ecs_os_strdup("AA"); + ptr3->a[i][1] = ecs_os_strdup("BB"); + ptr3->a[i][2] = ecs_os_strdup("CC"); + } + ptr3->b = ecs_os_strdup("DD"); + + StructWithArrayOfArrayOfStrings *ptr4 = ecs_ensure_id(world, e4, + struct_with_array_of_array_of_strings); + for (int i = 0; i < 3; i++) { + ptr4->a[i][0] = ecs_os_strdup("AA"); + ptr4->a[i][1] = ecs_os_strdup("ZZ"); + ptr4->a[i][2] = ecs_os_strdup("CC"); + } + ptr4->b = ecs_os_strdup("DD"); + + StructWithArrayOfArrayOfStrings *ptr5 = ecs_ensure_id(world, e5, + struct_with_array_of_array_of_strings); + for (int i = 0; i < 3; i++) { + ptr5->a[i][0] = ecs_os_strdup("XX"); + ptr5->a[i][1] = ecs_os_strdup("BB"); + ptr5->a[i][2] = ecs_os_strdup("YY"); + } + ptr5->b = ecs_os_strdup("FF"); + + /* Test "less" */ + /* {"AA", "BB", "CC", "DD"} < {"AA", "BB", "CC", "EE"} */ + test_assert(cmp(world, struct_with_array_of_array_of_strings, e1, e2) < 0); + + /* {"AA", "BB", "CC", "DD"} < {"AA", "ZZ", "CC", "DD"} */ + test_assert(cmp(world, struct_with_array_of_array_of_strings, e1, e4) < 0); + + /* {"AA", "BB", "CC", "DD"} < {"XX", "BB", "YY", "FF"} */ + test_assert(cmp(world, struct_with_array_of_array_of_strings, e1, e5) < 0); + + /* Test "greater" */ + /* {"AA", "BB", "CC", "EE"} > {"AA", "BB", "CC", "DD"} */ + test_assert(cmp(world, struct_with_array_of_array_of_strings, e2, e1) > 0); + + /* {"AA", "ZZ", "CC", "DD"} > {"AA", "BB", "CC", "DD"} */ + test_assert(cmp(world, struct_with_array_of_array_of_strings, e4, e1) > 0); + + /* {"XX", "BB", "YY", "FF"} > {"AA", "BB", "CC", "DD"} */ + test_assert(cmp(world, struct_with_array_of_array_of_strings, e5, e1) > 0); + + /* Test "equal" */ + /* {"AA", "BB", "CC", "DD"} == {"AA", "BB", "CC", "DD"} */ + test_assert(cmp(world, struct_with_array_of_array_of_strings, e1, e3) == 0); + test_assert(equals(world, struct_with_array_of_array_of_strings, e1, e3) == true); + test_assert(cmp(world, struct_with_array_of_array_of_strings, e1, e1) == 0); + test_assert(equals(world, struct_with_array_of_array_of_strings, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + ecs_delete(world, e5); + + ecs_fini(world); +} + +void RttCompare_struct_with_vector_of_ints(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* vector */ vector_of_ints = + ecs_vector(world, {.type = ecs_id(ecs_i32_t)}); + + typedef struct { + ecs_vec_t a; + } StructWithVectorOfInts; + + ecs_entity_t struct_with_vector_of_ints = ecs_struct(world, { + .members = { + {"a", vector_of_ints}, + } + }); + + /* Create four entities with StructWithVectorOfInts component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + + StructWithVectorOfInts *ptr1 = ecs_ensure_id(world, e1, struct_with_vector_of_ints); + ecs_vec_set_count(NULL, &ptr1->a, sizeof(ecs_i32_t), 3); + ecs_i32_t *v1 = ecs_vec_first(&ptr1->a); + v1[0] = 10; + v1[1] = 20; + v1[2] = 30; + + StructWithVectorOfInts *ptr2 = ecs_ensure_id(world, e2, struct_with_vector_of_ints); + ecs_vec_set_count(NULL, &ptr2->a, sizeof(ecs_i32_t), 3); + ecs_i32_t *v2 = ecs_vec_first(&ptr2->a); + v2[0] = 15; + v2[1] = 25; + v2[2] = 35; + + StructWithVectorOfInts *ptr3 = ecs_ensure_id(world, e3, struct_with_vector_of_ints); + ecs_vec_set_count(NULL, &ptr3->a, sizeof(ecs_i32_t), 3); + ecs_i32_t *v3 = ecs_vec_first(&ptr3->a); + v3[0] = 10; + v3[1] = 20; + v3[2] = 30; + + StructWithVectorOfInts *ptr4 = ecs_ensure_id(world, e4, struct_with_vector_of_ints); + ecs_vec_set_count(NULL, &ptr4->a, sizeof(ecs_i32_t), 2); + ecs_i32_t *v4 = ecs_vec_first(&ptr4->a); + v4[0] = 10; + v4[1] = 20; + + /* Test "less" */ + /* {10, 20, 30} < {15, 25, 35} */ + test_assert(cmp(world, struct_with_vector_of_ints, e1, e2) < 0); + + /* {10, 20} < {10, 20, 30} (because fewer elements) */ + test_assert(cmp(world, struct_with_vector_of_ints, e4, e1) < 0); + + /* Test "greater" */ + /* {15, 25, 35} > {10, 20, 30} */ + test_assert(cmp(world, struct_with_vector_of_ints, e2, e1) > 0); + + /* {10, 20, 30} > {10, 20} (because more elements) */ + test_assert(cmp(world, struct_with_vector_of_ints, e1, e4) > 0); + + /* Test "equal" */ + /* {10, 20, 30} == {10, 20, 30} */ + test_assert(cmp(world, struct_with_vector_of_ints, e1, e3) == 0); + test_assert(equals(world, struct_with_vector_of_ints, e1, e3) == true); + test_assert(cmp(world, struct_with_vector_of_ints, e1, e1) == 0); + test_assert(equals(world, struct_with_vector_of_ints, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + + ecs_fini(world); +} + +void RttCompare_struct_with_vector_of_strings(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* vector */ vector_of_strings = + ecs_vector(world, {.type = ecs_id(ecs_string_t)}); + + typedef struct { + ecs_vec_t a; + } StructWithVectorOfStrings; + + ecs_entity_t struct_with_vector_of_strings = ecs_struct(world, { + .members = { + {"a", vector_of_strings}, + } + }); + + /* Create four entities with StructWithVectorOfStrings component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + + StructWithVectorOfStrings *ptr1 = ecs_ensure_id(world, e1, struct_with_vector_of_strings); + ecs_vec_set_count(NULL, &ptr1->a, sizeof(ecs_string_t), 3); + ecs_string_t *va1 = ecs_vec_first(&ptr1->a); + va1[0] = ecs_os_strdup("AA"); + va1[1] = ecs_os_strdup("BB"); + va1[2] = ecs_os_strdup("CC"); + + StructWithVectorOfStrings *ptr2 = ecs_ensure_id(world, e2, struct_with_vector_of_strings); + ecs_vec_set_count(NULL, &ptr2->a, sizeof(ecs_string_t), 3); + ecs_string_t *va2 = ecs_vec_first(&ptr2->a); + va2[0] = ecs_os_strdup("AA"); + va2[1] = ecs_os_strdup("BB"); + va2[2] = ecs_os_strdup("DD"); + + StructWithVectorOfStrings *ptr3 = ecs_ensure_id(world, e3, struct_with_vector_of_strings); + ecs_vec_set_count(NULL, &ptr3->a, sizeof(ecs_string_t), 3); + ecs_string_t *va3 = ecs_vec_first(&ptr3->a); + va3[0] = ecs_os_strdup("AA"); + va3[1] = ecs_os_strdup("BB"); + va3[2] = ecs_os_strdup("CC"); + + StructWithVectorOfStrings *ptr4 = ecs_ensure_id(world, e4, struct_with_vector_of_strings); + ecs_vec_set_count(NULL, &ptr4->a, sizeof(ecs_string_t), 2); + ecs_string_t *va4 = ecs_vec_first(&ptr4->a); + va4[0] = ecs_os_strdup("AA"); + va4[1] = ecs_os_strdup("BB"); + + /* Test "less" */ + /* {"AA", "BB", "CC"} < {"AA", "BB", "DD"} */ + test_assert(cmp(world, struct_with_vector_of_strings, e1, e2) < 0); + + /* {"AA", "BB"} < {"AA", "BB", "CC"} (because fewer elements) */ + test_assert(cmp(world, struct_with_vector_of_strings, e4, e1) < 0); + + /* Test "greater" */ + /* {"AA", "BB", "DD"} > {"AA", "BB", "CC"} */ + test_assert(cmp(world, struct_with_vector_of_strings, e2, e1) > 0); + + /* {"AA", "BB", "CC"} > {"AA", "BB"} (because more elements) */ + test_assert(cmp(world, struct_with_vector_of_strings, e1, e4) > 0); + + /* Test "equal" */ + /* {"AA", "BB", "CC"} == {"AA", "BB", "CC"} */ + test_assert(cmp(world, struct_with_vector_of_strings, e1, e3) == 0); + test_assert(equals(world, struct_with_vector_of_strings, e1, e3) == true); + test_assert(cmp(world, struct_with_vector_of_strings, e1, e1) == 0); + test_assert(equals(world, struct_with_vector_of_strings, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + + ecs_fini(world); +} + +void RttCompare_nested_struct_with_vector_of_ints(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* vector */ vector_of_ints = + ecs_vector(world, {.type = ecs_id(ecs_i32_t)}); + + typedef struct { + ecs_vec_t a; + ecs_i32_t b; + ecs_vec_t c; + } InnerStruct1; + + ecs_entity_t inner_struct_1 = ecs_struct(world, { + .members = { + {"a", vector_of_ints}, + {"b", ecs_id(ecs_i32_t)}, + {"c", vector_of_ints}, + } + }); + + typedef struct { + ecs_vec_t a; + ecs_i32_t b; + InnerStruct1 c; + } NestedStructWithVectorOfInts; + + ecs_entity_t nested_struct_with_vector_of_ints = ecs_struct(world, { + .members = { + {"a", vector_of_ints}, + {"b", ecs_id(ecs_i32_t)}, + {"c", inner_struct_1}, + } + }); + + /* Create five entities with NestedStructWithVectorOfInts component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + + /* Entity e1 */ + NestedStructWithVectorOfInts *ptr1 = ecs_ensure_id(world, e1, + nested_struct_with_vector_of_ints); + ecs_vec_set_count(NULL, &ptr1->a, sizeof(ecs_i32_t), 3); + ecs_i32_t *va1 = ecs_vec_first(&ptr1->a); + va1[0] = 1; + va1[1] = 2; + va1[2] = 3; + ptr1->b = 10; + + ecs_vec_set_count(NULL, &ptr1->c.a, sizeof(ecs_i32_t), 2); + ecs_i32_t *vca1 = ecs_vec_first(&ptr1->c.a); + vca1[0] = 4; + vca1[1] = 5; + ptr1->c.b = 15; + + ecs_vec_set_count(NULL, &ptr1->c.c, sizeof(ecs_i32_t), 1); + ecs_i32_t *vcc1 = ecs_vec_first(&ptr1->c.c); + vcc1[0] = 6; + + /* Entity e2 */ + NestedStructWithVectorOfInts *ptr2 = ecs_ensure_id(world, e2, + nested_struct_with_vector_of_ints); + ecs_vec_set_count(NULL, &ptr2->a, sizeof(ecs_i32_t), 3); + ecs_i32_t *va2 = ecs_vec_first(&ptr2->a); + va2[0] = 1; + va2[1] = 2; + va2[2] = 3; + ptr2->b = 20; + + ecs_vec_set_count(NULL, &ptr2->c.a, sizeof(ecs_i32_t), 2); + ecs_i32_t *vca2 = ecs_vec_first(&ptr2->c.a); + vca2[0] = 4; + vca2[1] = 5; + ptr2->c.b = 15; + + ecs_vec_set_count(NULL, &ptr2->c.c, sizeof(ecs_i32_t), 1); + ecs_i32_t *vcc2 = ecs_vec_first(&ptr2->c.c); + vcc2[0] = 6; + + /* Entity e3 */ + NestedStructWithVectorOfInts *ptr3 = ecs_ensure_id(world, e3, + nested_struct_with_vector_of_ints); + ecs_vec_set_count(NULL, &ptr3->a, sizeof(ecs_i32_t), 3); + ecs_i32_t *va3 = ecs_vec_first(&ptr3->a); + va3[0] = 1; + va3[1] = 2; + va3[2] = 3; + ptr3->b = 10; + + ecs_vec_set_count(NULL, &ptr3->c.a, sizeof(ecs_i32_t), 2); + ecs_i32_t *vca3 = ecs_vec_first(&ptr3->c.a); + vca3[0] = 4; + vca3[1] = 5; + ptr3->c.b = 15; + + ecs_vec_set_count(NULL, &ptr3->c.c, sizeof(ecs_i32_t), 1); + ecs_i32_t *vcc3 = ecs_vec_first(&ptr3->c.c); + vcc3[0] = 6; + + /* Entity e4 - different vector values */ + NestedStructWithVectorOfInts *ptr4 = ecs_ensure_id(world, e4, + nested_struct_with_vector_of_ints); + ecs_vec_set_count(NULL, &ptr4->a, sizeof(ecs_i32_t), 3); + ecs_i32_t *va4 = ecs_vec_first(&ptr4->a); + va4[0] = 3; + va4[1] = 2; + va4[2] = 1; + ptr4->b = 10; + + ecs_vec_set_count(NULL, &ptr4->c.a, sizeof(ecs_i32_t), 2); + ecs_i32_t *vca4 = ecs_vec_first(&ptr4->c.a); + vca4[0] = 4; + vca4[1] = 5; + ptr4->c.b = 15; + + ecs_vec_set_count(NULL, &ptr4->c.c, sizeof(ecs_i32_t), 1); + ecs_i32_t *vcc4 = ecs_vec_first(&ptr4->c.c); + vcc4[0] = 6; + + /* Entity e5 - different nested structure values */ + NestedStructWithVectorOfInts *ptr5 = ecs_ensure_id(world, e5, + nested_struct_with_vector_of_ints); + ecs_vec_set_count(NULL, &ptr5->a, sizeof(ecs_i32_t), 3); + ecs_i32_t *va5 = ecs_vec_first(&ptr5->a); + va5[0] = 1; + va5[1] = 2; + va5[2] = 3; + ptr5->b = 30; + + ecs_vec_set_count(NULL, &ptr5->c.a, sizeof(ecs_i32_t), 2); + ecs_i32_t *vca5 = ecs_vec_first(&ptr5->c.a); + vca5[0] = 7; + vca5[1] = 8; + ptr5->c.b = 25; + + ecs_vec_set_count(NULL, &ptr5->c.c, sizeof(ecs_i32_t), 1); + ecs_i32_t *vcc5 = ecs_vec_first(&ptr5->c.c); + vcc5[0] = 9; + + /* Test "less" */ + /* {1, 2, 3, 10, {4, 5, 15, 6}} < {1, 2, 3, 20, {4, 5, 15, 6}} */ + test_assert(cmp(world, nested_struct_with_vector_of_ints, e1, e2) < 0); + /* {1, 2, 3, 10, {4, 5, 15, 6}} < {3, 2, 1, 10, {4, 5, 15, 6}} */ + test_assert(cmp(world, nested_struct_with_vector_of_ints, e1, e4) < 0); + /* {1, 2, 3, 10, {4, 5, 15, 6}} < {1, 2, 3, 30, {7, 8, 25, 9}} */ + test_assert(cmp(world, nested_struct_with_vector_of_ints, e1, e5) < 0); + + /* Test "greater" */ + /* {1, 2, 3, 20, {4, 5, 15, 6}} > {1, 2, 3, 10, {4, 5, 15, 6}} */ + test_assert(cmp(world, nested_struct_with_vector_of_ints, e2, e1) > 0); + /* {3, 2, 1, 10, {4, 5, 15, 6}} > {1, 2, 3, 10, {4, 5, 15, 6}} */ + test_assert(cmp(world, nested_struct_with_vector_of_ints, e4, e1) > 0); + /* {1, 2, 3, 30, {7, 8, 25, 9}} > {1, 2, 3, 10, {4, 5, 15, 6}} */ + test_assert(cmp(world, nested_struct_with_vector_of_ints, e5, e1) > 0); + + /* Test "equal" */ + /* {1, 2, 3, 10, {4, 5, 15, 6}} == {1, 2, 3, 10, {4, 5, 15, 6}} */ + test_assert(cmp(world, nested_struct_with_vector_of_ints, e1, e3) == 0); + test_assert(equals(world, nested_struct_with_vector_of_ints, e1, e3) == true); + test_assert(cmp(world, nested_struct_with_vector_of_ints, e1, e1) == 0); + test_assert(equals(world, nested_struct_with_vector_of_ints, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + ecs_delete(world, e5); + + ecs_fini(world); +} + +void RttCompare_nested_struct_with_vector_of_strings(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* vector */ vector_of_strings = + ecs_vector(world, {.type = ecs_id(ecs_string_t)}); + + typedef struct { + ecs_vec_t a; /* vector */ + ecs_i32_t b; + ecs_vec_t c; /* vector */ + } InnerStruct2; + + ecs_entity_t inner_struct_2 = ecs_struct(world, { + .members = { + {"a", vector_of_strings}, + {"b", ecs_id(ecs_i32_t)}, + {"c", vector_of_strings}, + } + }); + + typedef struct { + ecs_vec_t a; /* vector */ + ecs_i32_t b; + InnerStruct2 c; + } NestedStructWithVectorOfStrings; + + ecs_entity_t nested_struct_with_vector_of_strings = ecs_struct(world, { + .members = { + {"a", vector_of_strings}, + {"b", ecs_id(ecs_i32_t)}, + {"c", inner_struct_2}, + } + }); + + /* Create five entities with NestedStructWithVectorOfStrings component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + + NestedStructWithVectorOfStrings *ptr1 = ecs_ensure_id(world, e1, + nested_struct_with_vector_of_strings); + ecs_vec_set_count(NULL, &ptr1->a, sizeof(ecs_string_t), 3); + ecs_string_t *a1 = ecs_vec_first(&ptr1->a); + a1[0] = ecs_os_strdup("AA"); + a1[1] = ecs_os_strdup("BB"); + a1[2] = ecs_os_strdup("CC"); + ptr1->b = 10; + ecs_vec_set_count(NULL, &ptr1->c.a, sizeof(ecs_string_t), 2); + ecs_string_t *c_a1 = ecs_vec_first(&ptr1->c.a); + c_a1[0] = ecs_os_strdup("XX"); + c_a1[1] = ecs_os_strdup("YY"); + ptr1->c.b = 5; + ecs_vec_set_count(NULL, &ptr1->c.c, sizeof(ecs_string_t), 1); + ecs_string_t *c_c1 = ecs_vec_first(&ptr1->c.c); + c_c1[0] = ecs_os_strdup("ZZ"); + + NestedStructWithVectorOfStrings *ptr2 = ecs_ensure_id(world, e2, + nested_struct_with_vector_of_strings); + ecs_vec_set_count(NULL, &ptr2->a, sizeof(ecs_string_t), 3); + ecs_string_t *a2 = ecs_vec_first(&ptr2->a); + a2[0] = ecs_os_strdup("AA"); + a2[1] = ecs_os_strdup("BB"); + a2[2] = ecs_os_strdup("CC"); + ptr2->b = 15; + ecs_vec_set_count(NULL, &ptr2->c.a, sizeof(ecs_string_t), 2); + ecs_string_t *c_a2 = ecs_vec_first(&ptr2->c.a); + c_a2[0] = ecs_os_strdup("XX"); + c_a2[1] = ecs_os_strdup("YY"); + ptr2->c.b = 5; + ecs_vec_set_count(NULL, &ptr2->c.c, sizeof(ecs_string_t), 1); + ecs_string_t *c_c2 = ecs_vec_first(&ptr2->c.c); + c_c2[0] = ecs_os_strdup("ZZ"); + + NestedStructWithVectorOfStrings *ptr3 = ecs_ensure_id(world, e3, + nested_struct_with_vector_of_strings); + ecs_vec_set_count(NULL, &ptr3->a, sizeof(ecs_string_t), 3); + ecs_string_t *a3 = ecs_vec_first(&ptr3->a); + a3[0] = ecs_os_strdup("AA"); + a3[1] = ecs_os_strdup("BB"); + a3[2] = ecs_os_strdup("CC"); + ptr3->b = 10; + ecs_vec_set_count(NULL, &ptr3->c.a, sizeof(ecs_string_t), 2); + ecs_string_t *c_a3 = ecs_vec_first(&ptr3->c.a); + c_a3[0] = ecs_os_strdup("XX"); + c_a3[1] = ecs_os_strdup("YY"); + ptr3->c.b = 5; + ecs_vec_set_count(NULL, &ptr3->c.c, sizeof(ecs_string_t), 1); + ecs_string_t *c_c3 = ecs_vec_first(&ptr3->c.c); + c_c3[0] = ecs_os_strdup("ZZ"); + + NestedStructWithVectorOfStrings *ptr4 = ecs_ensure_id(world, e4, + nested_struct_with_vector_of_strings); + ecs_vec_set_count(NULL, &ptr4->a, sizeof(ecs_string_t), 2); + ecs_string_t *a4 = ecs_vec_first(&ptr4->a); + a4[0] = ecs_os_strdup("AA"); + a4[1] = ecs_os_strdup("DD"); + ptr4->b = 20; + ecs_vec_set_count(NULL, &ptr4->c.a, sizeof(ecs_string_t), 3); + ecs_string_t *c_a4 = ecs_vec_first(&ptr4->c.a); + c_a4[0] = ecs_os_strdup("XX"); + c_a4[1] = ecs_os_strdup("YY"); + c_a4[2] = ecs_os_strdup("ZZ"); + ptr4->c.b = 10; + ecs_vec_set_count(NULL, &ptr4->c.c, sizeof(ecs_string_t), 2); + ecs_string_t *c_c4 = ecs_vec_first(&ptr4->c.c); + c_c4[0] = ecs_os_strdup("WW"); + c_c4[1] = ecs_os_strdup("QQ"); + + NestedStructWithVectorOfStrings *ptr5 = ecs_ensure_id(world, e5, + nested_struct_with_vector_of_strings); + ecs_vec_set_count(NULL, &ptr5->a, sizeof(ecs_string_t), 1); + ecs_string_t *a5 = ecs_vec_first(&ptr5->a); + a5[0] = ecs_os_strdup("EE"); + ptr5->b = 5; + ecs_vec_set_count(NULL, &ptr5->c.a, sizeof(ecs_string_t), 2); + ecs_string_t *c_a5 = ecs_vec_first(&ptr5->c.a); + c_a5[0] = ecs_os_strdup("MM"); + c_a5[1] = ecs_os_strdup("NN"); + ptr5->c.b = 8; + ecs_vec_set_count(NULL, &ptr5->c.c, sizeof(ecs_string_t), 3); + ecs_string_t *c_c5 = ecs_vec_first(&ptr5->c.c); + c_c5[0] = ecs_os_strdup("OO"); + c_c5[1] = ecs_os_strdup("PP"); + c_c5[2] = ecs_os_strdup("RR"); + + /* Test "less" */ + /* {{"AA", "BB", "CC"}, 10, {{"XX", "YY"}, 5, {"ZZ"}}} < */ + /* {{"AA", "BB", "CC"}, 15, {{"XX", "YY"}, 5, {"ZZ"}}} */ + test_assert(cmp(world, nested_struct_with_vector_of_strings, e1, e2) < 0); + + /* {{"EE"}, 5, {{"MM", "NN"}, 8, {"OO", "PP", "RR"}}} < */ + /* {{"AA", "BB", "CC"}, 15, {{"XX", "YY"}, 5, {"ZZ"}}} */ + test_assert(cmp(world, nested_struct_with_vector_of_strings, e5, e2) < 0); + + /* {{"AA", "DD"}, 20, {{"XX", "YY", "ZZ"}, 10, {"WW", "QQ"}}} > */ + /* {{"AA", "BB", "CC"}, 10, {{"XX", "YY"}, 5, {"ZZ"}}} */ + test_assert(cmp(world, nested_struct_with_vector_of_strings, e4, e1) < 0); + + /* Test "greater" */ + /* {{"AA", "BB", "CC"}, 15, {{"XX", "YY"}, 5, {"ZZ"}}} > */ + /* {{"AA", "BB", "CC"}, 10, {{"XX", "YY"}, 5, {"ZZ"}}} */ + test_assert(cmp(world, nested_struct_with_vector_of_strings, e2, e1) > 0); + + /* {{"AA", "BB", "CC"}, 10, {{"XX", "YY"}, 5, {"ZZ"}}} < */ + /* {{"AA", "DD"}, 20, {{"XX", "YY", "ZZ"}, 10, {"WW", "QQ"}}} */ + test_assert(cmp(world, nested_struct_with_vector_of_strings, e1, e4) > 0); + + + /* {{"AA", "BB", "CC"}, 15, {{"XX", "YY"}, 5, {"ZZ"}}} > */ + /* {{"EE"}, 5, {{"MM", "NN"}, 8, {"OO", "PP", "RR"}}} */ + test_assert(cmp(world, nested_struct_with_vector_of_strings, e2, e5) > 0); + + /* Test "equal" */ + /* {{"AA", "BB", "CC"}, 10, {{"XX", "YY"}, 5, {"ZZ"}}} == */ + /* {{"AA", "BB", "CC"}, 10, {{"XX", "YY"}, 5, {"ZZ"}}} */ + test_assert(cmp(world, nested_struct_with_vector_of_strings, e1, e3) == 0); + test_assert(equals(world, nested_struct_with_vector_of_strings, e1, e3) == true); + test_assert(cmp(world, nested_struct_with_vector_of_strings, e1, e1) == 0); + test_assert(equals(world, nested_struct_with_vector_of_strings, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + ecs_delete(world, e5); + + ecs_fini(world); +} + +void RttCompare_array_of_ints(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* ecs_i32_t[3] */ array_of_ints = + ecs_array(world, {.type = ecs_id(ecs_i32_t), .count = 3}); + + /* Create five entities, each with an array of integers component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + + ecs_i32_t *arr1 = ecs_ensure_id(world, e1, array_of_ints); + arr1[0] = 1; + arr1[1] = 2; + arr1[2] = 3; + + ecs_i32_t *arr2 = ecs_ensure_id(world, e2, array_of_ints); + arr2[0] = 1; + arr2[1] = 2; + arr2[2] = 4; + + ecs_i32_t *arr3 = ecs_ensure_id(world, e3, array_of_ints); + arr3[0] = 1; + arr3[1] = 2; + arr3[2] = 3; + + ecs_i32_t *arr4 = ecs_ensure_id(world, e4, array_of_ints); + arr4[0] = 0; + arr4[1] = 5; + arr4[2] = 6; + + ecs_i32_t *arr5 = ecs_ensure_id(world, e5, array_of_ints); + arr5[0] = 1; + arr5[1] = 2; + arr5[2] = 2; + + /* Test "less" */ + /* {1, 2, 3} < {1, 2, 4} */ + test_assert(cmp(world, array_of_ints, e1, e2) < 0); + + /* {0, 5, 6} < {1, 2, 3} */ + test_assert(cmp(world, array_of_ints, e4, e1) < 0); + + /* Test "greater" */ + /* {1, 2, 4} > {1, 2, 3} */ + test_assert(cmp(world, array_of_ints, e2, e1) > 0); + + /* {1, 2, 3} > {1, 2, 2} */ + test_assert(cmp(world, array_of_ints, e1, e5) > 0); + + /* Test "equal" */ + /* {1, 2, 3} == {1, 2, 3} */ + test_assert(cmp(world, array_of_ints, e1, e3) == 0); + test_assert(equals(world, array_of_ints, e1, e3) == true); + test_assert(cmp(world, array_of_ints, e1, e1) == 0); + test_assert(equals(world, array_of_ints, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + ecs_delete(world, e5); + + ecs_fini(world); +} + +void RttCompare_array_of_strings(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* ecs_string_t[3] */ array_of_strings = + ecs_array(world, {.type = ecs_id(ecs_string_t), .count = 3}); + + /* Create five entities, each with an array of strings component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + + ecs_string_t *arr1 = ecs_ensure_id(world, e1, array_of_strings); + arr1[0] = ecs_os_strdup("AA"); + arr1[1] = ecs_os_strdup("BB"); + arr1[2] = ecs_os_strdup("CC"); + + ecs_string_t *arr2 = ecs_ensure_id(world, e2, array_of_strings); + arr2[0] = ecs_os_strdup("AA"); + arr2[1] = ecs_os_strdup("BB"); + arr2[2] = ecs_os_strdup("DD"); + + ecs_string_t *arr3 = ecs_ensure_id(world, e3, array_of_strings); + arr3[0] = ecs_os_strdup("AA"); + arr3[1] = ecs_os_strdup("BB"); + arr3[2] = ecs_os_strdup("CC"); + + ecs_string_t *arr4 = ecs_ensure_id(world, e4, array_of_strings); + arr4[0] = ecs_os_strdup("ZZ"); + arr4[1] = ecs_os_strdup("YY"); + arr4[2] = ecs_os_strdup("XX"); + + ecs_string_t *arr5 = ecs_ensure_id(world, e5, array_of_strings); + arr5[0] = ecs_os_strdup("AA"); + arr5[1] = ecs_os_strdup("AB"); + arr5[2] = ecs_os_strdup("AC"); + + /* Test "less" */ + /* {"AA", "BB", "CC"} < {"AA", "BB", "DD"} */ + test_assert(cmp(world, array_of_strings, e1, e2) < 0); + + /* {"AA", "AB", "AC"} < {"AA", "BB", "CC"} */ + test_assert(cmp(world, array_of_strings, e5, e1) < 0); + + /* Test "greater" */ + /* {"ZZ", "YY", "XX"} > {"AA", "BB", "CC"} */ + test_assert(cmp(world, array_of_strings, e4, e1) > 0); + + /* {"AA", "BB", "DD"} > {"AA", "BB", "CC"} */ + test_assert(cmp(world, array_of_strings, e2, e1) > 0); + + /* Test "equal" */ + /* {"AA", "BB", "CC"} == {"AA", "BB", "CC"} */ + test_assert(cmp(world, array_of_strings, e1, e3) == 0); + test_assert(equals(world, array_of_strings, e1, e3) == true); + test_assert(cmp(world, array_of_strings, e1, e1) == 0); + test_assert(equals(world, array_of_strings, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + ecs_delete(world, e5); + + ecs_fini(world); +} + +void RttCompare_array_of_struct_with_ints(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + ecs_i32_t a; + ecs_i32_t b; + } StructWithInts; + + ecs_entity_t struct_with_ints = ecs_struct(world, { + .members = { + {"a", ecs_id(ecs_i32_t)}, + {"b", ecs_id(ecs_i32_t)}, + } + }); + + ecs_entity_t /* StructWithInts[3] */ array_of_struct_with_ints = + ecs_array(world, {.type = struct_with_ints, .count = 3}); + + /* Create five entities with array of StructWithInts component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + + StructWithInts *arr1 = ecs_ensure_id(world, e1, array_of_struct_with_ints); + arr1[0].a = 1; arr1[0].b = 2; + arr1[1].a = 3; arr1[1].b = 4; + arr1[2].a = 5; arr1[2].b = 6; + + StructWithInts *arr2 = ecs_ensure_id(world, e2, array_of_struct_with_ints); + arr2[0].a = 1; arr2[0].b = 2; + arr2[1].a = 3; arr2[1].b = 4; + arr2[2].a = 5; arr2[2].b = 10; + + StructWithInts *arr3 = ecs_ensure_id(world, e3, array_of_struct_with_ints); + arr3[0].a = 1; arr3[0].b = 2; + arr3[1].a = 3; arr3[1].b = 4; + arr3[2].a = 5; arr3[2].b = 6; + + StructWithInts *arr4 = ecs_ensure_id(world, e4, array_of_struct_with_ints); + arr4[0].a = 0; arr4[0].b = 1; + arr4[1].a = 2; arr4[1].b = 3; + arr4[2].a = 4; arr4[2].b = 5; + + StructWithInts *arr5 = ecs_ensure_id(world, e5, array_of_struct_with_ints); + arr5[0].a = 7; arr5[0].b = 8; + arr5[1].a = 9; arr5[1].b = 10; + arr5[2].a = 11; arr5[2].b = 12; + + /* Test "less" */ + /* {{1, 2}, {3, 4}, {5, 6}} < {{1, 2}, {3, 4}, {5, 10}} */ + test_assert(cmp(world, array_of_struct_with_ints, e1, e2) < 0); + + /* {{0, 1}, {2, 3}, {4, 5}} < {{1, 2}, {3, 4}, {5, 6}} */ + test_assert(cmp(world, array_of_struct_with_ints, e4, e1) < 0); + + /* Test "greater" */ + /* {{1, 2}, {3, 4}, {5, 10}} > {{1, 2}, {3, 4}, {5, 6}} */ + test_assert(cmp(world, array_of_struct_with_ints, e2, e1) > 0); + + /* {{7, 8}, {9, 10}, {11, 12}} > {{1, 2}, {3, 4}, {5, 6}} */ + test_assert(cmp(world, array_of_struct_with_ints, e5, e1) > 0); + + /* Test "equal" */ + /* {{1, 2}, {3, 4}, {5, 6}} == {{1, 2}, {3, 4}, {5, 6}} */ + test_assert(cmp(world, array_of_struct_with_ints, e1, e3) == 0); + test_assert(equals(world, array_of_struct_with_ints, e1, e3) == true); + test_assert(cmp(world, array_of_struct_with_ints, e1, e1) == 0); + test_assert(equals(world, array_of_struct_with_ints, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + ecs_delete(world, e5); + + ecs_fini(world); +} + +void RttCompare_array_of_struct_with_strings(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + ecs_string_t a; + ecs_i32_t b; + ecs_string_t c; + } StructWithStrings; + + ecs_entity_t struct_with_strings = ecs_struct(world, { + .members = { + {"a", ecs_id(ecs_string_t)}, + {"b", ecs_id(ecs_i32_t)}, + {"c", ecs_id(ecs_string_t)}, + } + }); + + ecs_entity_t /* StructWithStrings[3] */ array_of_struct_with_strings = + ecs_array(world, {.type = struct_with_strings, .count = 3}); + + typedef struct { + StructWithStrings items[3]; + } ArrayOfStructWithStrings; + + ecs_entity_t struct_array_entity = ecs_struct(world, { + .members = { + {"items", array_of_struct_with_strings}, + } + }); + + /* Create five entities with ArrayOfStructWithStrings component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + + /* Initialize e1 */ + ArrayOfStructWithStrings *ptr1 = ecs_ensure_id(world, e1, struct_array_entity); + ptr1->items[0].a = ecs_os_strdup("AA"); + ptr1->items[0].b = 10; + ptr1->items[0].c = ecs_os_strdup("BB"); + ptr1->items[1].a = ecs_os_strdup("CC"); + ptr1->items[1].b = 15; + ptr1->items[1].c = ecs_os_strdup("DD"); + ptr1->items[2].a = ecs_os_strdup("EE"); + ptr1->items[2].b = 20; + ptr1->items[2].c = ecs_os_strdup("FF"); + + /* Initialize e2 with different value in the second struct */ + ArrayOfStructWithStrings *ptr2 = ecs_ensure_id(world, e2, struct_array_entity); + ptr2->items[0].a = ecs_os_strdup("AA"); + ptr2->items[0].b = 10; + ptr2->items[0].c = ecs_os_strdup("BB"); + ptr2->items[1].a = ecs_os_strdup("CC"); + ptr2->items[1].b = 18; // Different value + ptr2->items[1].c = ecs_os_strdup("DD"); + ptr2->items[2].a = ecs_os_strdup("EE"); + ptr2->items[2].b = 20; + ptr2->items[2].c = ecs_os_strdup("FF"); + + /* Initialize e3 identical to e1 */ + ArrayOfStructWithStrings *ptr3 = ecs_ensure_id(world, e3, struct_array_entity); + ptr3->items[0].a = ecs_os_strdup("AA"); + ptr3->items[0].b = 10; + ptr3->items[0].c = ecs_os_strdup("BB"); + ptr3->items[1].a = ecs_os_strdup("CC"); + ptr3->items[1].b = 15; + ptr3->items[1].c = ecs_os_strdup("DD"); + ptr3->items[2].a = ecs_os_strdup("EE"); + ptr3->items[2].b = 20; + ptr3->items[2].c = ecs_os_strdup("FF"); + + /* Initialize e4 with completely different strings */ + ArrayOfStructWithStrings *ptr4 = ecs_ensure_id(world, e4, struct_array_entity); + ptr4->items[0].a = ecs_os_strdup("XX"); + ptr4->items[0].b = 5; + ptr4->items[0].c = ecs_os_strdup("YY"); + ptr4->items[1].a = ecs_os_strdup("ZZ"); + ptr4->items[1].b = 25; + ptr4->items[1].c = ecs_os_strdup("WW"); + ptr4->items[2].a = ecs_os_strdup("VV"); + ptr4->items[2].b = 30; + ptr4->items[2].c = ecs_os_strdup("UU"); + + /* Initialize e5 with mixture of similar and different values */ + ArrayOfStructWithStrings *ptr5 = ecs_ensure_id(world, e5, struct_array_entity); + ptr5->items[0].a = ecs_os_strdup("AA"); + ptr5->items[0].b = 10; + ptr5->items[0].c = ecs_os_strdup("GG"); // Different from e1 + ptr5->items[1].a = ecs_os_strdup("CC"); + ptr5->items[1].b = 15; + ptr5->items[1].c = ecs_os_strdup("HH"); // Different from e1 + ptr5->items[2].a = ecs_os_strdup("EE"); + ptr5->items[2].b = 20; + ptr5->items[2].c = ecs_os_strdup("FF"); + + /* Test "less" */ + test_assert(cmp(world, struct_array_entity, e1, e2) < 0);/*15 < 18 */ + test_assert(cmp(world, struct_array_entity, e5, e4) < 0);/*"AA" < "XX" */ + test_assert(cmp(world, struct_array_entity, e1, e4) < 0);/*"CC" < "ZZ" */ + + /* Test "greater" */ + /* e2 > e1 because ptr2->items[1].b (18) > ptr1->items[1].b (15) */ + test_assert(cmp(world, struct_array_entity, e2, e1) > 0);/* 18 > 15 */ + test_assert(cmp(world, struct_array_entity, e5, e1) > 0);/* "GG" > "BB"*/ + + /* e4 > e1 because */ + test_assert(cmp(world, struct_array_entity, e4, e1) > 0);/* "XX" > "AA"*/ + + /* Test "equal" */ + test_assert(cmp(world, struct_array_entity, e1, e3) == 0); + test_assert(equals(world, struct_array_entity, e1, e3) == true); + test_assert(cmp(world, struct_array_entity, e1, e1) == 0); + test_assert(equals(world, struct_array_entity, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + ecs_delete(world, e5); + + ecs_fini(world); +} + +void RttCompare_array_of_struct_with_opaques(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t opaque = define_opaque_type(world); + + typedef struct { + OpaqueType a; + } StructWithOpaque; + + ecs_entity_t struct_with_opaque = ecs_struct(world, { + .members = { + {"a", opaque}, + } + }); + + ecs_entity_t /* StructWithOpaque[3] */ array_of_struct_with_opaques = + ecs_array(world, {.type = struct_with_opaque, .count = 3}); + + /* Create three entities with array of StructWithOpaque component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + StructWithOpaque *arr1 = ecs_ensure_id(world, e1, array_of_struct_with_opaques); + arr1[0].a.value = 5; + arr1[1].a.value = 10; + arr1[2].a.value = 15; + + StructWithOpaque *arr2 = ecs_ensure_id(world, e2, array_of_struct_with_opaques); + arr2[0].a.value = 5; + arr2[1].a.value = 15; + arr2[2].a.value = 20; + + StructWithOpaque *arr3 = ecs_ensure_id(world, e3, array_of_struct_with_opaques); + arr3[0].a.value = 5; + arr3[1].a.value = 10; + arr3[2].a.value = 15; + + /* Test "less" */ + /* {{5}, {10}, {15}} < {{5}, {15}, {20}} */ + test_assert(cmp(world, array_of_struct_with_opaques, e1, e2) < 0); + + /* Test "greater" */ + /* {{5}, {15}, {20}} > {{5}, {10}, {15}} */ + test_assert(cmp(world, array_of_struct_with_opaques, e2, e1) > 0); + + /* Test "equal" */ + /* {{5}, {10}, {15}} == {{5}, {10}, {15}} */ + test_assert(cmp(world, array_of_struct_with_opaques, e1, e3) == 0); + test_assert(equals(world, array_of_struct_with_opaques, e1, e3) == true); + test_assert(cmp(world, array_of_struct_with_opaques, e1, e1) == 0); + test_assert(equals(world, array_of_struct_with_opaques, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + + ecs_fini(world); +} + +void RttCompare_array_of_array_of_strings(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* ecs_string_t[3] */ array_of_strings = + ecs_array(world, {.type = ecs_id(ecs_string_t), .count = 3}); + + ecs_entity_t /* ecs_string_t[3][3] */ array_of_array_of_strings = + ecs_array(world, {.type = array_of_strings, .count = 3}); + + /* Create multiple entities with array of arrays of strings component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + + ecs_string_t (*arr1)[3] = ecs_ensure_id(world, e1, array_of_array_of_strings); + arr1[0][0] = ecs_os_strdup("AA"); + arr1[0][1] = ecs_os_strdup("BB"); + arr1[0][2] = ecs_os_strdup("CC"); + arr1[1][0] = ecs_os_strdup("AA"); + arr1[1][1] = ecs_os_strdup("BB"); + arr1[1][2] = ecs_os_strdup("CC"); + arr1[2][0] = ecs_os_strdup("AA"); + arr1[2][1] = ecs_os_strdup("BB"); + arr1[2][2] = ecs_os_strdup("CC"); + + ecs_string_t (*arr2)[3] = ecs_ensure_id(world, e2, array_of_array_of_strings); + arr2[0][0] = ecs_os_strdup("AA"); + arr2[0][1] = ecs_os_strdup("BB"); + arr2[0][2] = ecs_os_strdup("CC"); + arr2[1][0] = ecs_os_strdup("AA"); + arr2[1][1] = ecs_os_strdup("ZZ"); + arr2[1][2] = ecs_os_strdup("CC"); + arr2[2][0] = ecs_os_strdup("AA"); + arr2[2][1] = ecs_os_strdup("BB"); + arr2[2][2] = ecs_os_strdup("CC"); + + ecs_string_t (*arr3)[3] = ecs_ensure_id(world, e3, array_of_array_of_strings); + arr3[0][0] = ecs_os_strdup("AA"); + arr3[0][1] = ecs_os_strdup("BB"); + arr3[0][2] = ecs_os_strdup("CC"); + arr3[1][0] = ecs_os_strdup("AA"); + arr3[1][1] = ecs_os_strdup("BB"); + arr3[1][2] = ecs_os_strdup("CC"); + arr3[2][0] = ecs_os_strdup("AA"); + arr3[2][1] = ecs_os_strdup("BB"); + arr3[2][2] = ecs_os_strdup("CC"); + + ecs_string_t (*arr4)[3] = ecs_ensure_id(world, e4, array_of_array_of_strings); + arr4[0][0] = ecs_os_strdup("XX"); + arr4[0][1] = ecs_os_strdup("YY"); + arr4[0][2] = ecs_os_strdup("ZZ"); + arr4[1][0] = ecs_os_strdup("XX"); + arr4[1][1] = ecs_os_strdup("YY"); + arr4[1][2] = ecs_os_strdup("ZZ"); + arr4[2][0] = ecs_os_strdup("XX"); + arr4[2][1] = ecs_os_strdup("YY"); + arr4[2][2] = ecs_os_strdup("ZZ"); + + /* Test "less" */ + /* {"AA", ...} < {"XX", ...} */ + test_assert(cmp(world, array_of_array_of_strings, e1, e4) < 0); + /* {"AA", ...} < {"AA", ..., "ZZ"} */ + test_assert(cmp(world, array_of_array_of_strings, e1, e2) < 0); + + /* Test "greater" */ + /* {"XX", ...} > {"AA", ...} */ + test_assert(cmp(world, array_of_array_of_strings, e4, e1) > 0); + /* {"AA", ..., "ZZ"} > {"AA", ...} */ + test_assert(cmp(world, array_of_array_of_strings, e2, e1) > 0); + + /* Test "equal" */ + test_assert(cmp(world, array_of_array_of_strings, e1, e3) == 0); + test_assert(equals(world, array_of_array_of_strings, e1, e3) == true); + test_assert(cmp(world, array_of_array_of_strings, e1, e1) == 0); + test_assert(equals(world, array_of_array_of_strings, e1, e1) == true); + + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + + ecs_fini(world); +} + +void RttCompare_array_of_array_of_struct_with_strings(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + ecs_string_t a; + ecs_i32_t b; + ecs_string_t c; + } StructWithStrings; + + ecs_entity_t struct_with_strings = ecs_struct(world, { + .members = { + {"a", ecs_id(ecs_string_t)}, + {"b", ecs_id(ecs_i32_t)}, + {"c", ecs_id(ecs_string_t)}, + } + }); + + ecs_entity_t /* StructWithStrings[3] */ array_of_struct_with_strings = + ecs_array(world, {.type = struct_with_strings, .count = 3}); + + ecs_entity_t /* StructWithStrings[3][3] */ + array_of_array_of_struct_with_strings = ecs_array( + world, {.type = array_of_struct_with_strings, .count = 3}); + + /* Create five entities with the nested component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + + StructWithStrings (*ptr1)[3] = + ecs_ensure_id(world, e1, array_of_array_of_struct_with_strings); + ptr1[0][0].a = ecs_os_strdup("AA"); + ptr1[0][0].b = 10; + ptr1[0][0].c = ecs_os_strdup("CC"); + ptr1[1][1].a = ecs_os_strdup("BB"); + ptr1[1][1].b = 20; + ptr1[1][1].c = ecs_os_strdup("DD"); + + StructWithStrings (*ptr2)[3] = + ecs_ensure_id(world, e2, array_of_array_of_struct_with_strings); + ptr2[0][0].a = ecs_os_strdup("AA"); + ptr2[0][0].b = 10; + ptr2[0][0].c = ecs_os_strdup("CC"); + ptr2[1][1].a = ecs_os_strdup("BB"); + ptr2[1][1].b = 25; + ptr2[1][1].c = ecs_os_strdup("DD"); + + StructWithStrings (*ptr3)[3] = + ecs_ensure_id(world, e3, array_of_array_of_struct_with_strings); + ptr3[0][0].a = ecs_os_strdup("AA"); + ptr3[0][0].b = 10; + ptr3[0][0].c = ecs_os_strdup("CC"); + ptr3[1][1].a = ecs_os_strdup("BB"); + ptr3[1][1].b = 20; + ptr3[1][1].c = ecs_os_strdup("DD"); + + StructWithStrings (*ptr4)[3] = + ecs_ensure_id(world, e4, array_of_array_of_struct_with_strings); + ptr4[0][0].a = ecs_os_strdup("AA"); + ptr4[0][0].b = 5; + ptr4[0][0].c = ecs_os_strdup("EE"); + ptr4[1][1].a = ecs_os_strdup("ZZ"); + ptr4[1][1].b = 20; + ptr4[1][1].c = ecs_os_strdup("FF"); + + StructWithStrings (*ptr5)[3] = + ecs_ensure_id(world, e5, array_of_array_of_struct_with_strings); + ptr5[0][0].a = ecs_os_strdup("AA"); + ptr5[0][0].b = 15; + ptr5[0][0].c = ecs_os_strdup("GG"); + ptr5[1][1].a = ecs_os_strdup("BB"); + ptr5[1][1].b = 30; + ptr5[1][1].c = ecs_os_strdup("HH"); + + /* Test "less" */ + /* {{"AA", 10, "CC"}, {"BB", 20, "DD"}} < {{"AA", 10, "CC"}, {"BB", 25, "DD"}} */ + test_assert(cmp(world, array_of_array_of_struct_with_strings, e1, e2) < 0); + + /* {{"AA", 5, "EE"}, {"ZZ", 20, "FF"}} < {{"AA", 15, "GG"}, {"BB", 30, "HH"}} */ + test_assert(cmp(world, array_of_array_of_struct_with_strings, e4, e5) < 0); + + /* Test "greater" */ + /* {{"AA", 10, "CC"}, {"BB", 25, "DD"}} > {{"AA", 10, "CC"}, {"BB", 20, "DD"}} */ + test_assert(cmp(world, array_of_array_of_struct_with_strings, e2, e1) > 0); + + /* {{"AA", 15, "GG"}, {"BB", 30, "HH"}} > {{"AA", 5, "EE"}, {"ZZ", 20, "FF"}} */ + test_assert(cmp(world, array_of_array_of_struct_with_strings, e5, e4) > 0); + + /* Test "equal" */ + /* {{"AA", 10, "CC"}, {"BB", 20, "DD"}} == {{"AA", 10, "CC"}, {"BB", 20, "DD"}} */ + test_assert(cmp(world, array_of_array_of_struct_with_strings, e1, e3) == 0); + test_assert(equals(world, array_of_array_of_struct_with_strings, e1, e3) == true); + test_assert(cmp(world, array_of_array_of_struct_with_strings, e1, e1) == 0); + test_assert(equals(world, array_of_array_of_struct_with_strings, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + ecs_delete(world, e5); + + ecs_fini(world); +} + +void RttCompare_array_of_vectors_of_ints(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* vector */ vector_of_ints = + ecs_vector(world, {.type = ecs_id(ecs_i32_t)}); + + ecs_entity_t /* ecs_vec_t[3] */ array_of_vectors_of_ints = + ecs_array(world, {.type = vector_of_ints, .count = 3}); + + /* Create four entities with array of vectors of integers component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + + ecs_vec_t *arr1 = ecs_ensure_id(world, e1, array_of_vectors_of_ints); + ecs_vec_set_count(NULL, &arr1[0], sizeof(ecs_i32_t), 3); + ecs_i32_t *v1_0 = ecs_vec_first(&arr1[0]); + v1_0[0] = 10; + v1_0[1] = 20; + v1_0[2] = 30; + ecs_vec_set_count(NULL, &arr1[1], sizeof(ecs_i32_t), 2); + ecs_i32_t *v1_1 = ecs_vec_first(&arr1[1]); + v1_1[0] = 5; + v1_1[1] = 15; + ecs_vec_set_count(NULL, &arr1[2], sizeof(ecs_i32_t), 4); + ecs_i32_t *v1_2 = ecs_vec_first(&arr1[2]); + v1_2[0] = 1; + v1_2[1] = 2; + v1_2[2] = 3; + v1_2[3] = 4; + + ecs_vec_t *arr2 = ecs_ensure_id(world, e2, array_of_vectors_of_ints); + ecs_vec_set_count(NULL, &arr2[0], sizeof(ecs_i32_t), 3); + ecs_i32_t *v2_0 = ecs_vec_first(&arr2[0]); + v2_0[0] = 10; + v2_0[1] = 20; + v2_0[2] = 30; + ecs_vec_set_count(NULL, &arr2[1], sizeof(ecs_i32_t), 2); + ecs_i32_t *v2_1 = ecs_vec_first(&arr2[1]); + v2_1[0] = 5; + v2_1[1] = 15; + ecs_vec_set_count(NULL, &arr2[2], sizeof(ecs_i32_t), 4); + ecs_i32_t *v2_2 = ecs_vec_first(&arr2[2]); + v2_2[0] = 1; + v2_2[1] = 2; + v2_2[2] = 3; + v2_2[3] = 4; + + ecs_vec_t *arr3 = ecs_ensure_id(world, e3, array_of_vectors_of_ints); + ecs_vec_set_count(NULL, &arr3[0], sizeof(ecs_i32_t), 3); + ecs_i32_t *v3_0 = ecs_vec_first(&arr3[0]); + v3_0[0] = 10; + v3_0[1] = 20; + v3_0[2] = 31; // Different from arr1 + ecs_vec_set_count(NULL, &arr3[1], sizeof(ecs_i32_t), 2); + ecs_i32_t *v3_1 = ecs_vec_first(&arr3[1]); + v3_1[0] = 5; + v3_1[1] = 15; + ecs_vec_set_count(NULL, &arr3[2], sizeof(ecs_i32_t), 4); + ecs_i32_t *v3_2 = ecs_vec_first(&arr3[2]); + v3_2[0] = 1; + v3_2[1] = 2; + v3_2[2] = 3; + v3_2[3] = 4; + + ecs_vec_t *arr4 = ecs_ensure_id(world, e4, array_of_vectors_of_ints); + ecs_vec_set_count(NULL, &arr4[0], sizeof(ecs_i32_t), 3); + ecs_i32_t *v4_0 = ecs_vec_first(&arr4[0]); + v4_0[0] = 12; // Different from arr1 + v4_0[1] = 22; + v4_0[2] = 32; + ecs_vec_set_count(NULL, &arr4[1], sizeof(ecs_i32_t), 2); + ecs_i32_t *v4_1 = ecs_vec_first(&arr4[1]); + v4_1[0] = 7; // Different from arr1 + v4_1[1] = 17; + ecs_vec_set_count(NULL, &arr4[2], sizeof(ecs_i32_t), 4); + ecs_i32_t *v4_2 = ecs_vec_first(&arr4[2]); + v4_2[0] = 1; + v4_2[1] = 2; + v4_2[2] = 3; + v4_2[3] = 4; + + /* Test "less" */ + test_assert(cmp(world, array_of_vectors_of_ints, e1, e3) < 0); + + /* Test "greater" */ + test_assert(cmp(world, array_of_vectors_of_ints, e3, e1) > 0); + + /* Test "equal" */ + test_assert(cmp(world, array_of_vectors_of_ints, e1, e2) == 0); + test_assert(equals(world, array_of_vectors_of_ints, e1, e2) == true); + test_assert(cmp(world, array_of_vectors_of_ints, e1, e1) == 0); + test_assert(equals(world, array_of_vectors_of_ints, e1, e1) == true); + + /* Test when different in multiple fields */ + test_assert(cmp(world, array_of_vectors_of_ints, e1, e4) < 0); + test_assert(cmp(world, array_of_vectors_of_ints, e4, e1) > 0); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + + ecs_fini(world); +} + +void RttCompare_array_of_vectors_of_strings(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* vector */ vector_of_strings = + ecs_vector(world, {.type = ecs_id(ecs_string_t)}); + + ecs_entity_t /* ecs_vec_t[3] */ array_of_vectors_of_strings = + ecs_array(world, {.type = vector_of_strings, .count = 3}); + + /* Create five entities with array of vectors of strings */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + + /* Initialize e1 */ + ecs_vec_t *arr1 = ecs_ensure_id(world, e1, array_of_vectors_of_strings); + ecs_vec_set_count(NULL, &arr1[0], sizeof(ecs_string_t), 3); + ecs_string_t *vec1_0 = ecs_vec_first(&arr1[0]); + vec1_0[0] = ecs_os_strdup("AA"); + vec1_0[1] = ecs_os_strdup("BB"); + vec1_0[2] = ecs_os_strdup("CC"); + + ecs_vec_set_count(NULL, &arr1[1], sizeof(ecs_string_t), 2); + ecs_string_t *vec1_1 = ecs_vec_first(&arr1[1]); + vec1_1[0] = ecs_os_strdup("XX"); + vec1_1[1] = ecs_os_strdup("YY"); + + ecs_vec_set_count(NULL, &arr1[2], sizeof(ecs_string_t), 1); + ecs_string_t *vec1_2 = ecs_vec_first(&arr1[2]); + vec1_2[0] = ecs_os_strdup("ZZ"); + + /* Initialize e2 */ + ecs_vec_t *arr2 = ecs_ensure_id(world, e2, array_of_vectors_of_strings); + ecs_vec_set_count(NULL, &arr2[0], sizeof(ecs_string_t), 3); + ecs_string_t *vec2_0 = ecs_vec_first(&arr2[0]); + vec2_0[0] = ecs_os_strdup("AA"); + vec2_0[1] = ecs_os_strdup("BB"); + vec2_0[2] = ecs_os_strdup("CC"); + + ecs_vec_set_count(NULL, &arr2[1], sizeof(ecs_string_t), 2); + ecs_string_t *vec2_1 = ecs_vec_first(&arr2[1]); + vec2_1[0] = ecs_os_strdup("XX"); + vec2_1[1] = ecs_os_strdup("YY"); + + ecs_vec_set_count(NULL, &arr2[2], sizeof(ecs_string_t), 1); + ecs_string_t *vec2_2 = ecs_vec_first(&arr2[2]); + vec2_2[0] = ecs_os_strdup("AA"); + + /* Initialize e3 */ + ecs_vec_t *arr3 = ecs_ensure_id(world, e3, array_of_vectors_of_strings); + ecs_vec_set_count(NULL, &arr3[0], sizeof(ecs_string_t), 3); + ecs_string_t *vec3_0 = ecs_vec_first(&arr3[0]); + vec3_0[0] = ecs_os_strdup("AA"); + vec3_0[1] = ecs_os_strdup("BB"); + vec3_0[2] = ecs_os_strdup("CC"); + + ecs_vec_set_count(NULL, &arr3[1], sizeof(ecs_string_t), 2); + ecs_string_t *vec3_1 = ecs_vec_first(&arr3[1]); + vec3_1[0] = ecs_os_strdup("XX"); + vec3_1[1] = ecs_os_strdup("YY"); + + ecs_vec_set_count(NULL, &arr3[2], sizeof(ecs_string_t), 1); + ecs_string_t *vec3_2 = ecs_vec_first(&arr3[2]); + vec3_2[0] = ecs_os_strdup("ZZ"); + + /* Initialize e4 with different strings */ + ecs_vec_t *arr4 = ecs_ensure_id(world, e4, array_of_vectors_of_strings); + ecs_vec_set_count(NULL, &arr4[0], sizeof(ecs_string_t), 3); + ecs_string_t *vec4_0 = ecs_vec_first(&arr4[0]); + vec4_0[0] = ecs_os_strdup("AA"); + vec4_0[1] = ecs_os_strdup("BB"); + vec4_0[2] = ecs_os_strdup("CC"); + + ecs_vec_set_count(NULL, &arr4[1], sizeof(ecs_string_t), 2); + ecs_string_t *vec4_1 = ecs_vec_first(&arr4[1]); + vec4_1[0] = ecs_os_strdup("XX"); + vec4_1[1] = ecs_os_strdup("ZZ"); + + ecs_vec_set_count(NULL, &arr4[2], sizeof(ecs_string_t), 1); + ecs_string_t *vec4_2 = ecs_vec_first(&arr4[2]); + vec4_2[0] = ecs_os_strdup("AA"); + + /* Initialize e5 with even more variation */ + ecs_vec_t *arr5 = ecs_ensure_id(world, e5, array_of_vectors_of_strings); + ecs_vec_set_count(NULL, &arr5[0], sizeof(ecs_string_t), 3); + ecs_string_t *vec5_0 = ecs_vec_first(&arr5[0]); + vec5_0[0] = ecs_os_strdup("DD"); + vec5_0[1] = ecs_os_strdup("EE"); + vec5_0[2] = ecs_os_strdup("FF"); + + ecs_vec_set_count(NULL, &arr5[1], sizeof(ecs_string_t), 2); + ecs_string_t *vec5_1 = ecs_vec_first(&arr5[1]); + vec5_1[0] = ecs_os_strdup("GG"); + vec5_1[1] = ecs_os_strdup("HH"); + + ecs_vec_set_count(NULL, &arr5[2], sizeof(ecs_string_t), 1); + ecs_string_t *vec5_2 = ecs_vec_first(&arr5[2]); + vec5_2[0] = ecs_os_strdup("II"); + + /* Test "less" */ + test_assert(cmp(world, array_of_vectors_of_strings, e2, e4) < 0); + test_assert(cmp(world, array_of_vectors_of_strings, e2, e1) < 0); + test_assert(cmp(world, array_of_vectors_of_strings, e1, e5) < 0); + + /* Test "greater" */ + test_assert(cmp(world, array_of_vectors_of_strings, e5, e1) > 0); + test_assert(cmp(world, array_of_vectors_of_strings, e1, e2) > 0); + test_assert(cmp(world, array_of_vectors_of_strings, e4, e2) > 0); + + /* Test "equal" */ + test_assert(cmp(world, array_of_vectors_of_strings, e1, e3) == 0); + test_assert(equals(world, array_of_vectors_of_strings, e1, e3) == true); + test_assert(cmp(world, array_of_vectors_of_strings, e3, e1) == 0); + test_assert(equals(world, array_of_vectors_of_strings, e3, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + ecs_delete(world, e5); + + ecs_fini(world); +} + +void RttCompare_array_of_opaque(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t opaque_type = define_opaque_type(world); + + /* Define an array of OpaqueType with 3 elements */ + ecs_entity_t array_of_opaque = ecs_array(world, { + .type = opaque_type, .count = 3 + }); + + /* Create five entities with array of OpaqueType component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + + OpaqueType *ptr1 = ecs_ensure_id(world, e1, array_of_opaque); + ptr1[0].value = 5; + ptr1[1].value = 10; + ptr1[2].value = 15; + + OpaqueType *ptr2 = ecs_ensure_id(world, e2, array_of_opaque); + ptr2[0].value = 5; + ptr2[1].value = 10; + ptr2[2].value = 20; + + OpaqueType *ptr3 = ecs_ensure_id(world, e3, array_of_opaque); + ptr3[0].value = 5; + ptr3[1].value = 10; + ptr3[2].value = 15; + + OpaqueType *ptr4 = ecs_ensure_id(world, e4, array_of_opaque); + ptr4[0].value = 7; + ptr4[1].value = 8; + ptr4[2].value = 9; + + OpaqueType *ptr5 = ecs_ensure_id(world, e5, array_of_opaque); + ptr5[0].value = 3; + ptr5[1].value = 10; + ptr5[2].value = 15; + + /* Test "less" */ + /* {5, 10, 15} < {5, 10, 20} */ + test_assert(cmp(world, array_of_opaque, e1, e2) < 0); + + /* {3, 10, 15} < {7, 8, 9} */ + test_assert(cmp(world, array_of_opaque, e5, e4) < 0); + + /* Test "greater" */ + /* {5, 10, 20} > {5, 10, 15} */ + test_assert(cmp(world, array_of_opaque, e2, e1) > 0); + + /* {7, 8, 9} > {3, 10, 15} */ + test_assert(cmp(world, array_of_opaque, e4, e5) > 0); + + /* Test "equal" */ + /* {5, 10, 15} == {5, 10, 15} */ + test_assert(cmp(world, array_of_opaque, e1, e3) == 0); + test_assert(equals(world, array_of_opaque, e1, e3) == true); + test_assert(cmp(world, array_of_opaque, e1, e1) == 0); + test_assert(equals(world, array_of_opaque, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + ecs_delete(world, e5); + + ecs_fini(world); +} + +void RttCompare_vector_of_ints(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* vector */ vector_of_ints = + ecs_vector(world, {.type = ecs_id(ecs_i32_t)}); + + /* Create five entities with vector_of_ints component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + + ecs_vec_t *vec1 = ecs_ensure_id(world, e1, vector_of_ints); + ecs_vec_set_count(NULL, vec1, sizeof(ecs_i32_t), 3); + ecs_i32_t *v1_data = ecs_vec_first(vec1); + v1_data[0] = 10; + v1_data[1] = 20; + v1_data[2] = 30; + + ecs_vec_t *vec2 = ecs_ensure_id(world, e2, vector_of_ints); + ecs_vec_set_count(NULL, vec2, sizeof(ecs_i32_t), 3); + ecs_i32_t *v2_data = ecs_vec_first(vec2); + v2_data[0] = 10; + v2_data[1] = 25; + v2_data[2] = 30; + + ecs_vec_t *vec3 = ecs_ensure_id(world, e3, vector_of_ints); + ecs_vec_set_count(NULL, vec3, sizeof(ecs_i32_t), 3); + ecs_i32_t *v3_data = ecs_vec_first(vec3); + v3_data[0] = 10; + v3_data[1] = 20; + v3_data[2] = 30; + + ecs_vec_t *vec4 = ecs_ensure_id(world, e4, vector_of_ints); + ecs_vec_set_count(NULL, vec4, sizeof(ecs_i32_t), 2); + ecs_i32_t *v4_data = ecs_vec_first(vec4); + v4_data[0] = 10; + v4_data[1] = 20; + + ecs_vec_t *vec5 = ecs_ensure_id(world, e5, vector_of_ints); + ecs_vec_set_count(NULL, vec5, sizeof(ecs_i32_t), 3); + ecs_i32_t *v5_data = ecs_vec_first(vec5); + v5_data[0] = 40; + v5_data[1] = 50; + v5_data[2] = 60; + + /* Test "less" */ + /* {10, 20, 30} < {10, 25, 30} */ + test_assert(cmp(world, vector_of_ints, e1, e2) < 0); + + /* {10, 20, 30} < {40, 50, 60} */ + test_assert(cmp(world, vector_of_ints, e1, e5) < 0); + + /* {10, 20} < {10, 20, 30} */ + test_assert(cmp(world, vector_of_ints, e4, e1) < 0); + + /* Test "greater" */ + /* {10, 25, 30} > {10, 20, 30} */ + test_assert(cmp(world, vector_of_ints, e2, e1) > 0); + + /* {40, 50, 60} > {10, 20, 30} */ + test_assert(cmp(world, vector_of_ints, e5, e1) > 0); + + /* Test "equal" */ + /* {10, 20, 30} == {10, 20, 30} */ + test_assert(cmp(world, vector_of_ints, e1, e3) == 0); + test_assert(equals(world, vector_of_ints, e1, e3) == true); + test_assert(cmp(world, vector_of_ints, e1, e1) == 0); + test_assert(equals(world, vector_of_ints, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + ecs_delete(world, e5); + + ecs_fini(world); +} + +void RttCompare_vector_of_strings(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* vector */ vector_of_strings = + ecs_vector(world, {.type = ecs_id(ecs_string_t)}); + + /* Create five entities with a vector of strings component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + + ecs_vec_t *vec1 = ecs_ensure_id(world, e1, vector_of_strings); + ecs_vec_set_count(NULL, vec1, sizeof(ecs_string_t), 3); + ecs_string_t *v1_data = ecs_vec_first(vec1); + v1_data[0] = ecs_os_strdup("AA"); + v1_data[1] = ecs_os_strdup("BB"); + v1_data[2] = ecs_os_strdup("CC"); + + ecs_vec_t *vec2 = ecs_ensure_id(world, e2, vector_of_strings); + ecs_vec_set_count(NULL, vec2, sizeof(ecs_string_t), 3); + ecs_string_t *v2_data = ecs_vec_first(vec2); + v2_data[0] = ecs_os_strdup("AA"); + v2_data[1] = ecs_os_strdup("BB"); + v2_data[2] = ecs_os_strdup("DD"); + + ecs_vec_t *vec3 = ecs_ensure_id(world, e3, vector_of_strings); + ecs_vec_set_count(NULL, vec3, sizeof(ecs_string_t), 3); + ecs_string_t *v3_data = ecs_vec_first(vec3); + v3_data[0] = ecs_os_strdup("AA"); + v3_data[1] = ecs_os_strdup("BB"); + v3_data[2] = ecs_os_strdup("CC"); + + ecs_vec_t *vec4 = ecs_ensure_id(world, e4, vector_of_strings); + ecs_vec_set_count(NULL, vec4, sizeof(ecs_string_t), 3); + ecs_string_t *v4_data = ecs_vec_first(vec4); + v4_data[0] = ecs_os_strdup("ZZ"); + v4_data[1] = ecs_os_strdup("AA"); + v4_data[2] = ecs_os_strdup("DD"); + + ecs_vec_t *vec5 = ecs_ensure_id(world, e5, vector_of_strings); + ecs_vec_set_count(NULL, vec5, sizeof(ecs_string_t), 3); + ecs_string_t *v5_data = ecs_vec_first(vec5); + v5_data[0] = ecs_os_strdup("AA"); + v5_data[1] = ecs_os_strdup("AA"); + v5_data[2] = ecs_os_strdup("BB"); + + /* Test "less" */ + /* {"AA", "BB", "CC"} < {"AA", "BB", "DD"} */ + test_assert(cmp(world, vector_of_strings, e1, e2) < 0); + + /* {"AA", "AA", "BB"} < {"AA", "BB", "CC"} */ + test_assert(cmp(world, vector_of_strings, e5, e1) < 0); + + /* {"AA", "BB", "DD"} < {"ZZ", "AA", "DD"} */ + test_assert(cmp(world, vector_of_strings, e2, e4) < 0); + + /* Test "greater" */ + /* {"AA", "BB", "DD"} > {"AA", "BB", "CC"} */ + test_assert(cmp(world, vector_of_strings, e2, e1) > 0); + /* {"ZZ", "AA", "DD"} > {"AA", "BB", "DD"} */ + test_assert(cmp(world, vector_of_strings, e4, e2) > 0); + + /* {"AA", "BB", "CC"} > {"AA", "AA", "BB"} */ + test_assert(cmp(world, vector_of_strings, e1, e5) > 0); + + + /* Test "equal" */ + /* {"AA", "BB", "CC"} == {"AA", "BB", "CC"} */ + test_assert(cmp(world, vector_of_strings, e1, e3) == 0); + test_assert(equals(world, vector_of_strings, e1, e3) == true); + test_assert(cmp(world, vector_of_strings, e1, e1) == 0); + test_assert(equals(world, vector_of_strings, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + ecs_delete(world, e5); + + ecs_fini(world); +} + +void RttCompare_vector_of_struct_with_ints(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + ecs_i32_t a; + ecs_i32_t b; + } StructWithInts; + + ecs_entity_t struct_with_ints = ecs_struct(world, { + .members = { + {"a", ecs_id(ecs_i32_t)}, + {"b", ecs_id(ecs_i32_t)}, + } + }); + + ecs_entity_t /* vector */ vector_of_struct_with_ints = + ecs_vector(world, {.type = struct_with_ints}); + + /* Create three entities with a vector of StructWithInts component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + + ecs_vec_t *vec1 = ecs_ensure_id(world, e1, vector_of_struct_with_ints); + ecs_vec_set_count(NULL, vec1, sizeof(StructWithInts), 3); + StructWithInts *v1 = ecs_vec_first(vec1); + v1[0] = (StructWithInts){.a = 10, .b = 20}; + v1[1] = (StructWithInts){.a = 15, .b = 25}; + v1[2] = (StructWithInts){.a = 20, .b = 30}; + + ecs_vec_t *vec2 = ecs_ensure_id(world, e2, vector_of_struct_with_ints); + ecs_vec_set_count(NULL, vec2, sizeof(StructWithInts), 3); + StructWithInts *v2 = ecs_vec_first(vec2); + v2[0] = (StructWithInts){.a = 10, .b = 20}; + v2[1] = (StructWithInts){.a = 15, .b = 25}; + v2[2] = (StructWithInts){.a = 25, .b = 35}; + + ecs_vec_t *vec3 = ecs_ensure_id(world, e3, vector_of_struct_with_ints); + ecs_vec_set_count(NULL, vec3, sizeof(StructWithInts), 3); + StructWithInts *v3 = ecs_vec_first(vec3); + v3[0] = (StructWithInts){.a = 10, .b = 20}; + v3[1] = (StructWithInts){.a = 15, .b = 25}; + v3[2] = (StructWithInts){.a = 20, .b = 30}; + + /* Test "less" */ + /* vec1 < vec2 because v1[2].a < v2[2].a */ + test_assert(cmp(world, vector_of_struct_with_ints, e1, e2) < 0); + + /* Test "greater" */ + /* vec2 > vec1 because v2[2].a > v1[2].a */ + test_assert(cmp(world, vector_of_struct_with_ints, e2, e1) > 0); + + /* Test "equal" */ + /* vec1 == vec3 as they have identical values */ + test_assert(cmp(world, vector_of_struct_with_ints, e1, e3) == 0); + test_assert(equals(world, vector_of_struct_with_ints, e1, e3) == true); + test_assert(cmp(world, vector_of_struct_with_ints, e1, e1) == 0); + test_assert(equals(world, vector_of_struct_with_ints, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + + ecs_fini(world); +} + +void RttCompare_vector_of_struct_with_strings(void) { + ecs_world_t *world = ecs_init(); + + typedef struct { + ecs_string_t a; + ecs_i32_t b; + ecs_string_t c; + } StructWithStrings; + + ecs_entity_t struct_with_strings = ecs_struct(world, { + .members = { + {"a", ecs_id(ecs_string_t)}, + {"b", ecs_id(ecs_i32_t)}, + {"c", ecs_id(ecs_string_t)}, + } + }); + + ecs_entity_t /* vector */ vector_of_struct_with_strings = + ecs_vector(world, {.type = struct_with_strings}); + + /* Create entities with vector component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + + ecs_vec_t *vec1 = ecs_ensure_id(world, e1, vector_of_struct_with_strings); + ecs_vec_set_count(NULL, vec1, sizeof(StructWithStrings), 2); + StructWithStrings *v1 = ecs_vec_first(vec1); + v1[0].a = ecs_os_strdup("AA"); + v1[0].b = 10; + v1[0].c = ecs_os_strdup("BB"); + v1[1].a = ecs_os_strdup("CC"); + v1[1].b = 15; + v1[1].c = ecs_os_strdup("DD"); + + ecs_vec_t *vec2 = ecs_ensure_id(world, e2, vector_of_struct_with_strings); + ecs_vec_set_count(NULL, vec2, sizeof(StructWithStrings), 2); + StructWithStrings *v2 = ecs_vec_first(vec2); + v2[0].a = ecs_os_strdup("AA"); + v2[0].b = 20; + v2[0].c = ecs_os_strdup("BB"); + v2[1].a = ecs_os_strdup("CC"); + v2[1].b = 15; + v2[1].c = ecs_os_strdup("DD"); + + ecs_vec_t *vec3 = ecs_ensure_id(world, e3, vector_of_struct_with_strings); + ecs_vec_set_count(NULL, vec3, sizeof(StructWithStrings), 2); + StructWithStrings *v3 = ecs_vec_first(vec3); + v3[0].a = ecs_os_strdup("AA"); + v3[0].b = 10; + v3[0].c = ecs_os_strdup("BB"); + v3[1].a = ecs_os_strdup("CC"); + v3[1].b = 15; + v3[1].c = ecs_os_strdup("DD"); + + ecs_vec_t *vec4 = ecs_ensure_id(world, e4, vector_of_struct_with_strings); + ecs_vec_set_count(NULL, vec4, sizeof(StructWithStrings), 1); + StructWithStrings *v4 = ecs_vec_first(vec4); + v4[0].a = ecs_os_strdup("AA"); + v4[0].b = 5; + v4[0].c = ecs_os_strdup("BB"); + + /* Test "less" */ + /* vec1 < vec2, because {10} < {20} */ + test_assert(cmp(world, vector_of_struct_with_strings, e1, e2) < 0); + + /* vec4 < vec1, because vec4 has fewer elements */ + test_assert(cmp(world, vector_of_struct_with_strings, e4, e1) < 0); + + /* Test "greater" */ + /* vec2 > vec1, because {20} > {10} */ + test_assert(cmp(world, vector_of_struct_with_strings, e2, e1) > 0); + + /* vec1 > vec4, because vec1 has more elements */ + test_assert(cmp(world, vector_of_struct_with_strings, e1, e4) > 0); + + /* Test "equal" */ + /* vec1 == vec3, all elements are identical */ + test_assert(cmp(world, vector_of_struct_with_strings, e1, e3) == 0); + test_assert(equals(world, vector_of_struct_with_strings, e1, e3) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + + ecs_fini(world); +} + +void RttCompare_vector_of_arrays_of_strings(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t /* ecs_string_t[3] */ array_of_strings = + ecs_array(world, {.type = ecs_id(ecs_string_t), .count = 3}); + + ecs_entity_t /* vector */ vector_of_arrays_of_strings = + ecs_vector(world, {.type = array_of_strings}); + + /* Create five entities with vector of arrays of strings component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + + ecs_vec_t *vec1 = ecs_ensure_id(world, e1, vector_of_arrays_of_strings); + ecs_vec_set_count(NULL, vec1, sizeof(ecs_string_t[3]), 1); + ecs_string_t (*v1)[3] = ecs_vec_first(vec1); + v1[0][0] = ecs_os_strdup("AA"); + v1[0][1] = ecs_os_strdup("BB"); + v1[0][2] = ecs_os_strdup("CC"); + + ecs_vec_t *vec2 = ecs_ensure_id(world, e2, vector_of_arrays_of_strings); + ecs_vec_set_count(NULL, vec2, sizeof(ecs_string_t[3]), 1); + ecs_string_t (*v2)[3] = ecs_vec_first(vec2); + v2[0][0] = ecs_os_strdup("AA"); + v2[0][1] = ecs_os_strdup("BB"); + v2[0][2] = ecs_os_strdup("CC"); + + ecs_vec_t *vec3 = ecs_ensure_id(world, e3, vector_of_arrays_of_strings); + ecs_vec_set_count(NULL, vec3, sizeof(ecs_string_t[3]), 1); + ecs_string_t (*v3)[3] = ecs_vec_first(vec3); + v3[0][0] = ecs_os_strdup("AA"); + v3[0][1] = ecs_os_strdup("ZZ"); + v3[0][2] = ecs_os_strdup("CC"); + + ecs_vec_t *vec4 = ecs_ensure_id(world, e4, vector_of_arrays_of_strings); + ecs_vec_set_count(NULL, vec4, sizeof(ecs_string_t[3]), 1); + ecs_string_t (*v4)[3] = ecs_vec_first(vec4); + v4[0][0] = ecs_os_strdup("ZZ"); + v4[0][1] = ecs_os_strdup("AA"); + v4[0][2] = ecs_os_strdup("DD"); + + ecs_vec_t *vec5 = ecs_ensure_id(world, e5, vector_of_arrays_of_strings); + ecs_vec_set_count(NULL, vec5, sizeof(ecs_string_t[3]), 1); + ecs_string_t (*v5)[3] = ecs_vec_first(vec5); + v5[0][0] = ecs_os_strdup("AA"); + v5[0][1] = ecs_os_strdup("BB"); + v5[0][2] = ecs_os_strdup("DD"); + + /* Test "less" */ + /* {"AA", "BB", "CC"} < {"AA", "ZZ", "CC"} */ + test_assert(cmp(world, vector_of_arrays_of_strings, e1, e3) < 0); + + /* {"AA", "BB", "CC"} < {"ZZ", "AA", "DD"} */ + test_assert(cmp(world, vector_of_arrays_of_strings, e1, e4) < 0); + + /* {"AA", "BB", "DD"} < {"ZZ", "AA", "DD"} */ + test_assert(cmp(world, vector_of_arrays_of_strings, e5, e4) < 0); + + /* Test "greater" */ + /* {"ZZ", "AA", "DD"} > {"AA", "BB", "CC"} */ + test_assert(cmp(world, vector_of_arrays_of_strings, e4, e1) > 0); + + /* {"AA", "ZZ", "CC"} > {"AA", "BB", "CC"} */ + test_assert(cmp(world, vector_of_arrays_of_strings, e3, e1) > 0); + + /* Test "equal" */ + /* {"AA", "BB", "CC"} == {"AA", "BB", "CC"} */ + test_assert(cmp(world, vector_of_arrays_of_strings, e1, e2) == 0); + test_assert(equals(world, vector_of_arrays_of_strings, e1, e2) == true); + test_assert(cmp(world, vector_of_arrays_of_strings, e1, e1) == 0); + test_assert(equals(world, vector_of_arrays_of_strings, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + ecs_delete(world, e5); + + ecs_fini(world); +} + +void RttCompare_vector_of_opaque(void) { + ecs_world_t *world = ecs_init(); + + ecs_entity_t opaque = define_opaque_type(world); + + ecs_entity_t /* vector */ vector_of_opaque = + ecs_vector(world, {.type = opaque }); + + /* Create five entities with vector_of_opaque component */ + ecs_entity_t e1 = ecs_new(world); + ecs_entity_t e2 = ecs_new(world); + ecs_entity_t e3 = ecs_new(world); + ecs_entity_t e4 = ecs_new(world); + ecs_entity_t e5 = ecs_new(world); + + ecs_vec_t *vec1 = ecs_ensure_id(world, e1, vector_of_opaque); + ecs_vec_set_count(NULL, vec1, sizeof(OpaqueType), 3); + OpaqueType *v1_data = ecs_vec_first(vec1); + v1_data[0].value = 10; + v1_data[1].value = 20; + v1_data[2].value = 30; + + ecs_vec_t *vec2 = ecs_ensure_id(world, e2, vector_of_opaque); + ecs_vec_set_count(NULL, vec2, sizeof(OpaqueType), 3); + OpaqueType *v2_data = ecs_vec_first(vec2); + v2_data[0].value = 10; + v2_data[1].value = 25; + v2_data[2].value = 30; + + ecs_vec_t *vec3 = ecs_ensure_id(world, e3, vector_of_opaque); + ecs_vec_set_count(NULL, vec3, sizeof(OpaqueType), 3); + OpaqueType *v3_data = ecs_vec_first(vec3); + v3_data[0].value = 10; + v3_data[1].value = 20; + v3_data[2].value = 30; + + ecs_vec_t *vec4 = ecs_ensure_id(world, e4, vector_of_opaque); + ecs_vec_set_count(NULL, vec4, sizeof(OpaqueType), 2); + OpaqueType *v4_data = ecs_vec_first(vec4); + v4_data[0].value = 5; + v4_data[1].value = 15; + + ecs_vec_t *vec5 = ecs_ensure_id(world, e5, vector_of_opaque); + ecs_vec_set_count(NULL, vec5, sizeof(OpaqueType), 4); + OpaqueType *v5_data = ecs_vec_first(vec5); + v5_data[0].value = 10; + v5_data[1].value = 20; + v5_data[2].value = 25; + v5_data[3].value = 35; + + /* Test "less" */ + /* {10, 20, 30} < {10, 25, 30} */ + test_assert(cmp(world, vector_of_opaque, e1, e2) < 0); + + /* {5, 15} < {10, 20, 30} */ + test_assert(cmp(world, vector_of_opaque, e4, e1) < 0); + + /* Test "greater" */ + /* {10, 25, 30} > {10, 20, 30} */ + test_assert(cmp(world, vector_of_opaque, e2, e1) > 0); + + /* {10, 20, 30, 35} > {10, 20, 30} */ + test_assert(cmp(world, vector_of_opaque, e5, e1) > 0); + + /* Test "equal" */ + /* {10, 20, 30} == {10, 20, 30} */ + test_assert(cmp(world, vector_of_opaque, e1, e3) == 0); + test_assert(equals(world, vector_of_opaque, e1, e3) == true); + test_assert(cmp(world, vector_of_opaque, e1, e1) == 0); + test_assert(equals(world, vector_of_opaque, e1, e1) == true); + + ecs_delete(world, e1); + ecs_delete(world, e2); + ecs_delete(world, e3); + ecs_delete(world, e4); + ecs_delete(world, e5); + + ecs_fini(world); +} diff --git a/test/meta/src/RuntimeTypes.c b/test/meta/src/RuntimeTypes.c index 6a789450c..6b83bda09 100644 --- a/test/meta/src/RuntimeTypes.c +++ b/test/meta/src/RuntimeTypes.c @@ -215,6 +215,13 @@ const ecs_type_info_t *define_test_struct( return ecs_get_type_info(world, test_struct); } +/* Compares two instances of the given type */ +static +int cmp(const ecs_world_t* world, ecs_entity_t id, const void *a, const void *b) { + const ecs_type_info_t* ti = ecs_get_type_info(world, id); + return ti->hooks.cmp(a, b, ti); +} + /* Tests that a constructor is generated for a struct if at least a member has * itself a constructor Also tests if the generated constructor works. */ void RuntimeTypes_ctor(void) { @@ -273,6 +280,8 @@ void RuntimeTypes_ctor_illegal(void) { ecs_type_hooks_t hooks = nested_struct_ti->hooks; hooks.flags |= ECS_TYPE_HOOK_CTOR_ILLEGAL; /* mark constructor for "NestedStruct" as illegal */ + hooks.ctor = NULL; + hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; ecs_set_hooks_id(world, nested_struct, &hooks); @@ -349,6 +358,8 @@ void RuntimeTypes_dtor_illegal(void) { ecs_type_hooks_t hooks = nested_struct_ti->hooks; hooks.flags |= ECS_TYPE_HOOK_DTOR_ILLEGAL; /* mark destructor for "NestedStruct" as illegal */ + hooks.dtor = NULL; + hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; ecs_set_hooks_id(world, nested_struct, &hooks); @@ -441,6 +452,8 @@ void RuntimeTypes_move_illegal(void) { ecs_type_hooks_t hooks = nested_struct_ti->hooks; hooks.flags |= ECS_TYPE_HOOK_MOVE_ILLEGAL; /* mark move hook for "NestedStruct" as illegal */ + hooks.move = NULL; + hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; ecs_set_hooks_id(world, nested_struct, &hooks); @@ -530,14 +543,16 @@ void RuntimeTypes_copy_illegal(void) { ecs_type_hooks_t hooks = nested_struct_ti->hooks; hooks.flags |= ECS_TYPE_HOOK_COPY_ILLEGAL; /* mark copy hook for "NestedStruct" as illegal */ + hooks.copy = NULL; + hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; ecs_set_hooks_id(world, nested_struct, &hooks); /* Define TestStruct, which has two "NestedStruct" members. - * TestStruct's constructor should be set to illegal as well. */ + * TestStruct's copy and copy ctor should be set to illegal as well. */ const ecs_type_info_t *test_struct_ti = define_test_struct(world); - /* TestStruct should have an illegal move hook too: */ + /* TestStruct should have an illegal copy and copy ctor hook too: */ test_assert(test_struct_ti->hooks.flags & ECS_TYPE_HOOK_COPY_ILLEGAL); test_assert(test_struct_ti->hooks.flags & ECS_TYPE_HOOK_COPY_CTOR_ILLEGAL); @@ -549,6 +564,60 @@ void RuntimeTypes_copy_illegal(void) { ecs_fini(world); } +/* Tests that an illegal compare hook is set for a struct if at least a member has + * itself an illegal compare hook */ +void RuntimeTypes_cmp_illegal(void) { + ecs_world_t *world = ecs_init(); + + /* Define NestedStruct: */ + const ecs_type_info_t *nested_struct_ti = define_nested_struct(world); + + ecs_type_hooks_t hooks = nested_struct_ti->hooks; + hooks.flags |= ECS_TYPE_HOOK_CMP_ILLEGAL; /* mark cmp hook for "NestedStruct" as illegal */ + hooks.cmp = NULL; + + hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; + ecs_set_hooks_id(world, nested_struct, &hooks); + + /* Define TestStruct, which has two "NestedStruct" members. + * TestStruct's compare hook should be set to illegal as well. */ + const ecs_type_info_t *test_struct_ti = define_test_struct(world); + + /* TestStruct should have an illegal compare hook too: */ + test_assert(test_struct_ti->hooks.flags & ECS_TYPE_HOOK_CMP_ILLEGAL); + + ecs_fini(world); +} + +/* Tests that an illegal equals hook is set for a struct if at least a member has + * itself an illegal equals hook */ +void RuntimeTypes_equals_illegal(void) { + ecs_world_t *world = ecs_init(); + + /* Define NestedStruct: */ + const ecs_type_info_t *nested_struct_ti = define_nested_struct(world); + + ecs_type_hooks_t hooks = nested_struct_ti->hooks; + hooks.flags |= ECS_TYPE_HOOK_EQUALS_ILLEGAL; /* mark equals hook for "NestedStruct" as illegal */ + hooks.equals = NULL; + + /* must mark cmp illegal too, otherwise equals would be autogenerated from cmp */ + hooks.flags |= ECS_TYPE_HOOK_CMP_ILLEGAL; + hooks.cmp = NULL; + + hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; + ecs_set_hooks_id(world, nested_struct, &hooks); + + /* Define TestStruct, which has two "NestedStruct" members. + * TestStruct's equals hook should be set to illegal as well. */ + const ecs_type_info_t *test_struct_ti = define_test_struct(world); + + /* TestStruct should have an illegal equals hook too: */ + test_assert(test_struct_ti->hooks.flags & ECS_TYPE_HOOK_EQUALS_ILLEGAL); + + ecs_fini(world); +} + /* For the following tests, we model a "ResourceHandle" that must allocate an id * from a pool of resource ids. When constructing a handle, it must get a unique * id. When destroying a handle, it must return the id. When copying a handle, @@ -695,6 +764,18 @@ void ResourceHandle_copy( } } +/* compares two resource handles */ +static +int ResourceHandle_comp( + const void *a, + const void *b, + const ecs_type_info_t *ti) +{ + int va = ((const ResourceHandle*)a)->value; + int vb = ((const ResourceHandle*)b)->value; + return (va > vb) - (va < vb); +} + /* Defines a struct in Flecs to model the ResourceHandle struct * For different tests, it can set specific hooks or not. */ static ecs_entity_t resource_handle; @@ -728,6 +809,8 @@ void define_resource_handle( hooks.copy = ResourceHandle_copy; hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; + hooks.cmp = ResourceHandle_comp; + ecs_set_hooks_id(world, resource_handle, &hooks); } @@ -824,6 +907,8 @@ void RuntimeTypes_array_ctor_illegal(void) { ecs_type_hooks_t hooks = nested_struct_ti->hooks; hooks.flags |= ECS_TYPE_HOOK_CTOR_ILLEGAL; /* mark constructor for "NestedStruct" as illegal */ + hooks.ctor = NULL; + hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; ecs_set_hooks_id(world, nested_struct, &hooks); @@ -917,6 +1002,8 @@ void RuntimeTypes_array_dtor_illegal(void) { ecs_type_hooks_t hooks = nested_struct_ti->hooks; hooks.flags |= ECS_TYPE_HOOK_DTOR_ILLEGAL; /* mark destructor for "NestedStruct" as illegal */ + hooks.dtor = NULL; + hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; ecs_set_hooks_id(world, nested_struct, &hooks); @@ -938,27 +1025,6 @@ void RuntimeTypes_array_dtor_illegal(void) { ecs_fini(world); } -/* compares two resource handles */ -static -bool resource_handle_compare( - ecs_world_t *world, - void *a, - void *b, - int32_t count, - const ecs_type_info_t *ti) -{ - for (int j = 0; j < count; j++) { - const ResourceHandle *ra = - (const ResourceHandle *) ECS_ELEM(a, ti->size, j); - const ResourceHandle *rb = - (const ResourceHandle *) ECS_ELEM(b, ti->size, j); - if (ra->value != rb->value) { - return false; - } - } - return true; -} - /* Tests that if on the array's underlying type only a move hook is defined, * only a move hook is defined for the array itself. Tests that the specified * move hook is called for each array element. */ @@ -1010,17 +1076,13 @@ void RuntimeTypes_array_move(void) { ecs_add_id(world, e, ecs_new(world)); handles = (ResourceHandle *) ecs_get_mut_id(world, e, arr_of_resources); - const ecs_type_info_t *rh_ti = ecs_get_type_info(world, resource_handle); /* we should retrieve the same values: */ - test_assert(resource_handle_compare( - world, &(ResourceHandle){.id = 100, .value = 111}, &handles[0], 1, - rh_ti)); - test_assert(resource_handle_compare( - world, &(ResourceHandle){.id = 200, .value = 222}, &handles[1], 1, - rh_ti)); - test_assert(resource_handle_compare( - world, &(ResourceHandle){.id = 300, .value = 333}, &handles[2], 1, - rh_ti)); + test_assert(0 == cmp( + world, resource_handle, &(ResourceHandle){.id = 100, .value = 111}, &handles[0])); + test_assert(0 == cmp( + world, resource_handle, &(ResourceHandle){.id = 200, .value = 222}, &handles[1])); + test_assert(0 == cmp( + world, resource_handle, &(ResourceHandle){.id = 300, .value = 333}, &handles[2])); test_int(10, resources_left()); /* pool stays the same */ @@ -1048,6 +1110,8 @@ void RuntimeTypes_array_move_illegal(void) { ecs_type_hooks_t hooks = nested_struct_ti->hooks; hooks.flags |= ECS_TYPE_HOOK_MOVE_ILLEGAL; /* mark move hook for "NestedStruct" as illegal */ + hooks.move = NULL; + hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; ecs_set_hooks_id(world, nested_struct, &hooks); @@ -1126,13 +1190,12 @@ void RuntimeTypes_array_copy(void) { ResourceHandle *handles = (ResourceHandle *) ecs_get_mut_id(world, e, arr_of_resources); - const ecs_type_info_t *rh_ti = ecs_get_type_info(world, resource_handle); - test_assert(resource_handle_compare(world, &prefab_handles[0], &handles[0], - 1, rh_ti)); - test_assert(resource_handle_compare(world, &prefab_handles[1], &handles[1], - 1, rh_ti)); - test_assert(resource_handle_compare(world, &prefab_handles[2], &handles[2], - 1, rh_ti)); + test_assert(0 == cmp( + world, resource_handle, &prefab_handles[0], &handles[0])); + test_assert(0 == cmp( + world, resource_handle, &prefab_handles[1], &handles[1])); + test_assert(0 == cmp( + world, resource_handle, &prefab_handles[2], &handles[2])); ecs_delete(world, e); test_int(7, resources_left()); /* resources not returned since we @@ -1153,6 +1216,8 @@ void RuntimeTypes_array_copy_illegal(void) { ecs_type_hooks_t hooks = nested_struct_ti->hooks; hooks.flags |= ECS_TYPE_HOOK_COPY_ILLEGAL; /* mark copy hook for "NestedStruct" as illegal */ + hooks.copy = NULL; + hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; ecs_set_hooks_id(world, nested_struct, &hooks); @@ -1175,6 +1240,40 @@ void RuntimeTypes_array_copy_illegal(void) { ecs_fini(world); } +/* Tests that an illegal cmp hook is set for an array if its underlying type itself + * has an illegal cmp hook */ +void RuntimeTypes_array_cmp_illegal(void) { + ecs_world_t *world = ecs_init(); + + /* Define NestedStruct: */ + const ecs_type_info_t *nested_struct_ti = define_nested_struct(world); + + ecs_type_hooks_t hooks = nested_struct_ti->hooks; + hooks.flags |= ECS_TYPE_HOOK_CMP_ILLEGAL; /* mark compare hook + for "NestedStruct" as illegal */ + hooks.cmp = NULL; + + hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; + ecs_set_hooks_id(world, nested_struct, &hooks); + + /* Define test_arr, as an array of "NestedStruct". + * TestStruct's cmp hook should be set to illegal as well. */ + ecs_array_desc_t desc = {.entity = 0, .type = nested_struct, .count = 3}; + ecs_entity_t test_arr = ecs_array_init(world, &desc); + + const ecs_type_info_t* test_arr_ti = ecs_get_type_info(world, test_arr); + + /* test_arr should have an illegal cmp hook too: */ + test_assert(test_arr_ti->hooks.flags & ECS_TYPE_HOOK_CMP_ILLEGAL); + + /* No other hooks should've been set: */ + test_assert(test_arr_ti->hooks.ctor != NULL); + test_assert(test_arr_ti->hooks.dtor == NULL); + test_assert(test_arr_ti->hooks.move == NULL); + + ecs_fini(world); +} + /* Test vector types */ void RuntimeTypes_vector_lifecycle(void) { ecs_world_t *world = ecs_init(); @@ -1305,6 +1404,35 @@ void RuntimeTypes_vector_lifecycle_trivial_type(void) { free_resource_ids(); } +/* Tests that an illegal cmp hook is set for an array if its underlying type itself + * has an illegal cmp hook */ +void RuntimeTypes_vector_cmp_illegal(void) { + ecs_world_t *world = ecs_init(); + + /* Define NestedStruct: */ + const ecs_type_info_t *nested_struct_ti = define_nested_struct(world); + + ecs_type_hooks_t hooks = nested_struct_ti->hooks; + hooks.flags |= ECS_TYPE_HOOK_CMP_ILLEGAL; /* mark compare hook + for "NestedStruct" as illegal */ + hooks.cmp = NULL; + + hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; + ecs_set_hooks_id(world, nested_struct, &hooks); + + /* Define test_vec, as a vector of "NestedStruct". + * TestStruct's cmp hook should be set to illegal as well. */ + ecs_vector_desc_t desc = {.entity = 0, .type = nested_struct}; + ecs_entity_t test_vec = ecs_vector_init(world, &desc); + + const ecs_type_info_t* test_vec_ti = ecs_get_type_info(world, test_vec); + + /* test_vec should have an illegal cmp hook too: */ + test_assert(test_vec_ti->hooks.flags & ECS_TYPE_HOOK_CMP_ILLEGAL); + + ecs_fini(world); +} + /* Configure an opaque type that consumes resources */ ecs_entity_t define_ResourceHandle_opaque( ecs_world_t *world) @@ -1316,7 +1444,10 @@ ecs_entity_t define_ResourceHandle_opaque( hooks.dtor = ResourceHandle_dtor; hooks.move = ResourceHandle_move; hooks.copy = ResourceHandle_copy; + hooks.cmp = ResourceHandle_comp; + hooks.flags &= ~ECS_TYPE_HOOK_CMP_ILLEGAL; hooks.flags &= ECS_TYPE_HOOKS_ILLEGAL; + ecs_set_hooks_id(world, ecs_id(ResourceHandle), &hooks); /* Create struct type that describes the structure of ResourceHandle */ @@ -1388,32 +1519,30 @@ void RuntimeTypes_struct_with_ints(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - StructWithInts *ptr = ecs_ensure_id(world, e, struct_with_ints); - test_memory_zero(ptr, sizeof(StructWithInts)); - ptr->a = 100; - ptr->b = 101; - } + StructWithInts *ptr1 = ecs_ensure_id(world, e, struct_with_ints); + test_memory_zero(ptr1, sizeof(StructWithInts)); + ptr1->a = 100; + ptr1->b = 101; /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); - { - const StructWithInts *ptr = - ecs_get_id(world, instance, struct_with_ints); - test_int(100, ptr->a); - test_int(101, ptr->b); - } + const StructWithInts *ptr2 = + ecs_get_id(world, instance, struct_with_ints); + test_int(100, ptr2->a); + test_int(101, ptr2->b); + + test_assert(cmp(world, struct_with_ints, ptr1, ptr2) == 0); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); - { - const StructWithInts *ptr = - ecs_get_id(world, instance, struct_with_ints); - test_int(100, ptr->a); - test_int(101, ptr->b); - } + const StructWithInts *ptr3 = + ecs_get_id(world, instance, struct_with_ints); + test_int(100, ptr3->a); + test_int(101, ptr3->b); + + test_assert(cmp(world, struct_with_ints, ptr1, ptr3) == 0); /* Test deleting: */ ecs_delete(world, e); @@ -1441,34 +1570,33 @@ void RuntimeTypes_struct_with_strings(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - StructWithStrings *ptr = ecs_ensure_id(world, e, struct_with_strings); - test_memory_zero(ptr, sizeof(StructWithStrings)); - ptr->a = ecs_os_strdup("String100"); - ptr->b = 101; - ptr->c = ecs_os_strdup("String102"); - } + StructWithStrings *ptr1 = ecs_ensure_id(world, e, struct_with_strings); + test_memory_zero(ptr1, sizeof(StructWithStrings)); + ptr1->a = ecs_os_strdup("String100"); + ptr1->b = 101; + ptr1->c = ecs_os_strdup("String102"); + /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); - { - const StructWithStrings *ptr = - ecs_get_id(world, instance, struct_with_strings); - test_str("String100", ptr->a); - test_int(101, ptr->b); - test_str("String102", ptr->c); - } + const StructWithStrings *ptr2 = + ecs_get_id(world, instance, struct_with_strings); + test_str("String100", ptr2->a); + test_int(101, ptr2->b); + test_str("String102", ptr2->c); + + test_assert(cmp(world, struct_with_strings, ptr1, ptr2) == 0); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); - { - const StructWithStrings *ptr = - ecs_get_id(world, instance, struct_with_strings); - test_str("String100", ptr->a); - test_int(101, ptr->b); - test_str("String102", ptr->c); - } + const StructWithStrings *ptr3 = + ecs_get_id(world, instance, struct_with_strings); + test_str("String100", ptr3->a); + test_int(101, ptr3->b); + test_str("String102", ptr3->c); + + test_assert(cmp(world, struct_with_strings, ptr1, ptr3) == 0); /* Test deleting: */ ecs_delete(world, e); @@ -1498,33 +1626,34 @@ void RuntimeTypes_struct_with_opaque(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - StructWithOpaque *ptr = ecs_ensure_id(world, e, struct_with_opaque); - test_assert(ptr->a.id != 0); - test_int(0, ptr->a.value); - ptr->a.value = 100; - } + StructWithOpaque *ptr1 = ecs_ensure_id(world, e, struct_with_opaque); + test_assert(ptr1->a.id != 0); + test_int(0, ptr1->a.value); + ptr1->a.value = 100; + /* 1 resource(s) should have been used */ test_int(1, initial_resources - resources_left()); /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); - { - const StructWithOpaque *ptr = - ecs_get_id(world, instance, struct_with_opaque); - test_int(100, ptr->a.value); - } + const StructWithOpaque *ptr2 = + ecs_get_id(world, instance, struct_with_opaque); + test_int(100, ptr2->a.value); + + test_assert(cmp(world, struct_with_opaque, ptr1, ptr2) == 0); + /* 2 resource(s) should be in use now */ test_int(2, initial_resources - resources_left()); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); - { - const StructWithOpaque *ptr = - ecs_get_id(world, instance, struct_with_opaque); - test_int(100, ptr->a.value); - } + const StructWithOpaque *ptr3 = + ecs_get_id(world, instance, struct_with_opaque); + test_int(100, ptr3->a.value); + + test_assert(cmp(world, struct_with_opaque, ptr1, ptr3) == 0); + /* 2 resource(s) should still be in use after a move */ test_int(2, initial_resources - resources_left()); @@ -1580,47 +1709,45 @@ void RuntimeTypes_nested_struct_with_strings(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - NestedStructWithStrings *ptr = - ecs_ensure_id(world, e, nested_struct_with_strings); - test_memory_zero(ptr, sizeof(NestedStructWithStrings)); - ptr->a.a = ecs_os_strdup("String100"); - ptr->a.b = 101; - ptr->a.c = ecs_os_strdup("String102"); - ptr->b = 103; - ptr->c.a = ecs_os_strdup("String104"); - ptr->c.b = 105; - ptr->c.c = ecs_os_strdup("String106"); - } + NestedStructWithStrings *ptr1 = + ecs_ensure_id(world, e, nested_struct_with_strings); + test_memory_zero(ptr1, sizeof(NestedStructWithStrings)); + ptr1->a.a = ecs_os_strdup("String100"); + ptr1->a.b = 101; + ptr1->a.c = ecs_os_strdup("String102"); + ptr1->b = 103; + ptr1->c.a = ecs_os_strdup("String104"); + ptr1->c.b = 105; + ptr1->c.c = ecs_os_strdup("String106"); /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); - { - const NestedStructWithStrings *ptr = - ecs_get_id(world, instance, nested_struct_with_strings); - test_str("String100", ptr->a.a); - test_int(101, ptr->a.b); - test_str("String102", ptr->a.c); - test_int(103, ptr->b); - test_str("String104", ptr->c.a); - test_int(105, ptr->c.b); - test_str("String106", ptr->c.c); - } + const NestedStructWithStrings *ptr2 = + ecs_get_id(world, instance, nested_struct_with_strings); + test_str("String100", ptr2->a.a); + test_int(101, ptr2->a.b); + test_str("String102", ptr2->a.c); + test_int(103, ptr2->b); + test_str("String104", ptr2->c.a); + test_int(105, ptr2->c.b); + test_str("String106", ptr2->c.c); + + test_assert(cmp(world, nested_struct_with_strings, ptr1, ptr2) == 0); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); - { - const NestedStructWithStrings *ptr = - ecs_get_id(world, instance, nested_struct_with_strings); - test_str("String100", ptr->a.a); - test_int(101, ptr->a.b); - test_str("String102", ptr->a.c); - test_int(103, ptr->b); - test_str("String104", ptr->c.a); - test_int(105, ptr->c.b); - test_str("String106", ptr->c.c); - } + const NestedStructWithStrings *ptr3 = + ecs_get_id(world, instance, nested_struct_with_strings); + test_str("String100", ptr3->a.a); + test_int(101, ptr3->a.b); + test_str("String102", ptr3->a.c); + test_int(103, ptr3->b); + test_str("String104", ptr3->c.a); + test_int(105, ptr3->c.b); + test_str("String106", ptr3->c.c); + + test_assert(cmp(world, nested_struct_with_strings, ptr1, ptr3) == 0); /* Test deleting: */ ecs_delete(world, e); @@ -1649,38 +1776,36 @@ void RuntimeTypes_struct_with_array_of_strings(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - StructWithArrayOfStrings *ptr = - ecs_ensure_id(world, e, struct_with_array_of_strings); - test_memory_zero(ptr, sizeof(StructWithArrayOfStrings)); - ptr->a[0] = ecs_os_strdup("String100"); - ptr->a[1] = ecs_os_strdup("String101"); - ptr->a[2] = ecs_os_strdup("String102"); - ptr->b = 103; - } + StructWithArrayOfStrings *ptr1 = + ecs_ensure_id(world, e, struct_with_array_of_strings); + test_memory_zero(ptr1, sizeof(StructWithArrayOfStrings)); + ptr1->a[0] = ecs_os_strdup("String100"); + ptr1->a[1] = ecs_os_strdup("String101"); + ptr1->a[2] = ecs_os_strdup("String102"); + ptr1->b = 103; /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); - { - const StructWithArrayOfStrings *ptr = - ecs_get_id(world, instance, struct_with_array_of_strings); - test_str("String100", ptr->a[0]); - test_str("String101", ptr->a[1]); - test_str("String102", ptr->a[2]); - test_int(103, ptr->b); - } + const StructWithArrayOfStrings *ptr2 = + ecs_get_id(world, instance, struct_with_array_of_strings); + test_str("String100", ptr2->a[0]); + test_str("String101", ptr2->a[1]); + test_str("String102", ptr2->a[2]); + test_int(103, ptr2->b); + + test_assert(cmp(world, struct_with_array_of_strings, ptr1, ptr2) == 0); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); - { - const StructWithArrayOfStrings *ptr = - ecs_get_id(world, instance, struct_with_array_of_strings); - test_str("String100", ptr->a[0]); - test_str("String101", ptr->a[1]); - test_str("String102", ptr->a[2]); - test_int(103, ptr->b); - } + const StructWithArrayOfStrings *ptr3 = + ecs_get_id(world, instance, struct_with_array_of_strings); + test_str("String100", ptr3->a[0]); + test_str("String101", ptr3->a[1]); + test_str("String102", ptr3->a[2]); + test_int(103, ptr3->b); + + test_assert(cmp(world, struct_with_array_of_strings, ptr1, ptr3) == 0); /* Test deleting: */ ecs_delete(world, e); @@ -1712,47 +1837,43 @@ void RuntimeTypes_struct_with_array_of_array_of_strings(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - StructWithArrayOfArrayOfStrings *ptr = - ecs_ensure_id(world, e, struct_with_array_of_array_of_strings); - test_memory_zero(ptr, sizeof(StructWithArrayOfArrayOfStrings)); - int i; - for (i = 0; i < 3; i++) { - ptr->a[i][0] = ecs_os_strdup("String100"); - ptr->a[i][1] = ecs_os_strdup("String101"); - ptr->a[i][2] = ecs_os_strdup("String102"); - } - ptr->b = ecs_os_strdup("String103"); + StructWithArrayOfArrayOfStrings *ptr1 = + ecs_ensure_id(world, e, struct_with_array_of_array_of_strings); + test_memory_zero(ptr1, sizeof(StructWithArrayOfArrayOfStrings)); + int i; + for (i = 0; i < 3; i++) { + ptr1->a[i][0] = ecs_os_strdup("String100"); + ptr1->a[i][1] = ecs_os_strdup("String101"); + ptr1->a[i][2] = ecs_os_strdup("String102"); } + ptr1->b = ecs_os_strdup("String103"); /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); - { - const StructWithArrayOfArrayOfStrings *ptr = - ecs_get_id(world, instance, struct_with_array_of_array_of_strings); - int i; - for (i = 0; i < 3; i++) { - test_str("String100", ptr->a[i][0]); - test_str("String101", ptr->a[i][1]); - test_str("String102", ptr->a[i][2]); - } - test_str("String103", ptr->b); + const StructWithArrayOfArrayOfStrings *ptr2 = + ecs_get_id(world, instance, struct_with_array_of_array_of_strings); + for (i = 0; i < 3; i++) { + test_str("String100", ptr2->a[i][0]); + test_str("String101", ptr2->a[i][1]); + test_str("String102", ptr2->a[i][2]); } + test_str("String103", ptr2->b); + + test_assert(cmp(world, struct_with_array_of_array_of_strings, ptr1, ptr2) == 0); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); - { - const StructWithArrayOfArrayOfStrings *ptr = - ecs_get_id(world, instance, struct_with_array_of_array_of_strings); - int i; - for (i = 0; i < 3; i++) { - test_str("String100", ptr->a[i][0]); - test_str("String101", ptr->a[i][1]); - test_str("String102", ptr->a[i][2]); - } - test_str("String103", ptr->b); + const StructWithArrayOfArrayOfStrings *ptr3 = + ecs_get_id(world, instance, struct_with_array_of_array_of_strings); + for (i = 0; i < 3; i++) { + test_str("String100", ptr3->a[i][0]); + test_str("String101", ptr3->a[i][1]); + test_str("String102", ptr3->a[i][2]); } + test_str("String103", ptr3->b); + + test_assert(cmp(world, struct_with_array_of_array_of_strings, ptr1, ptr3) == 0); /* Test deleting: */ ecs_delete(world, e); @@ -1779,51 +1900,40 @@ void RuntimeTypes_struct_with_vector_of_ints(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - StructWithVectorOfInts *ptr = - ecs_ensure_id(world, e, struct_with_vector_of_ints); - { - test_int(0, ecs_vec_count(&ptr->a)); - ecs_vec_set_count(NULL, &ptr->a, sizeof(ecs_i32_t), 3); - ecs_i32_t *va = ecs_vec_first(&ptr->a); - invoke_type_ctor(world, va, 3, ecs_id(ecs_i32_t)); - test_memory_zero(va, sizeof(ecs_i32_t) * 3); - va[0] = 100; - va[1] = 101; - va[2] = 102; - } - } + StructWithVectorOfInts *ptr1 = ecs_ensure_id(world, e, struct_with_vector_of_ints); + test_int(0, ecs_vec_count(&ptr1->a)); + ecs_vec_set_count(NULL, &ptr1->a, sizeof(ecs_i32_t), 3); + ecs_i32_t *va1 = ecs_vec_first(&ptr1->a); + invoke_type_ctor(world, va1, 3, ecs_id(ecs_i32_t)); + test_memory_zero(va1, sizeof(ecs_i32_t) * 3); + va1[0] = 100; + va1[1] = 101; + va1[2] = 102; /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); - { - const StructWithVectorOfInts *ptr = - ecs_get_id(world, instance, struct_with_vector_of_ints); - { - test_int(3, ecs_vec_count(&ptr->a)); - const ecs_i32_t *va = ecs_vec_first(&ptr->a); - test_assert(va != NULL); - test_int(100, va[0]); - test_int(101, va[1]); - test_int(102, va[2]); - } - } + const StructWithVectorOfInts *ptr2 = ecs_get_id(world, instance, struct_with_vector_of_ints); + test_int(3, ecs_vec_count(&ptr2->a)); + const ecs_i32_t *va2 = ecs_vec_first(&ptr2->a); + test_assert(va2 != NULL); + test_int(100, va2[0]); + test_int(101, va2[1]); + test_int(102, va2[2]); + + test_assert(cmp(world, struct_with_vector_of_ints, ptr1, ptr2) == 0); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); - { - const StructWithVectorOfInts *ptr = - ecs_get_id(world, instance, struct_with_vector_of_ints); - { - test_int(3, ecs_vec_count(&ptr->a)); - const ecs_i32_t *va = ecs_vec_first(&ptr->a); - test_assert(va != NULL); - test_int(100, va[0]); - test_int(101, va[1]); - test_int(102, va[2]); - } - } + const StructWithVectorOfInts *ptr3 = ecs_get_id(world, instance, struct_with_vector_of_ints); + test_int(3, ecs_vec_count(&ptr3->a)); + const ecs_i32_t *va3 = ecs_vec_first(&ptr3->a); + test_assert(va3 != NULL); + test_int(100, va3[0]); + test_int(101, va3[1]); + test_int(102, va3[2]); + + test_assert(cmp(world, struct_with_vector_of_ints, ptr1, ptr3) == 0); /* Test deleting: */ ecs_delete(world, e); @@ -1850,51 +1960,43 @@ void RuntimeTypes_struct_with_vector_of_strings(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - StructWithVectorOfStrings *ptr = - ecs_ensure_id(world, e, struct_with_vector_of_strings); - { - test_int(0, ecs_vec_count(&ptr->a)); - ecs_vec_set_count(NULL, &ptr->a, sizeof(ecs_string_t), 3); - ecs_string_t *va = ecs_vec_first(&ptr->a); - invoke_type_ctor(world, va, 3, ecs_id(ecs_string_t)); - test_memory_zero(va, sizeof(ecs_string_t) * 3); - va[0] = ecs_os_strdup("String100"); - va[1] = ecs_os_strdup("String101"); - va[2] = ecs_os_strdup("String102"); - } - } + StructWithVectorOfStrings *ptr1 = + ecs_ensure_id(world, e, struct_with_vector_of_strings); + test_int(0, ecs_vec_count(&ptr1->a)); + ecs_vec_set_count(NULL, &ptr1->a, sizeof(ecs_string_t), 3); + ecs_string_t *va1 = ecs_vec_first(&ptr1->a); + invoke_type_ctor(world, va1, 3, ecs_id(ecs_string_t)); + test_memory_zero(va1, sizeof(ecs_string_t) * 3); + va1[0] = ecs_os_strdup("String100"); + va1[1] = ecs_os_strdup("String101"); + va1[2] = ecs_os_strdup("String102"); /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); - { - const StructWithVectorOfStrings *ptr = - ecs_get_id(world, instance, struct_with_vector_of_strings); - { - test_int(3, ecs_vec_count(&ptr->a)); - const ecs_string_t *va = ecs_vec_first(&ptr->a); - test_assert(va != NULL); - test_str("String100", va[0]); - test_str("String101", va[1]); - test_str("String102", va[2]); - } - } + const StructWithVectorOfStrings *ptr2 = + ecs_get_id(world, instance, struct_with_vector_of_strings); + test_int(3, ecs_vec_count(&ptr2->a)); + const ecs_string_t *va2 = ecs_vec_first(&ptr2->a); + test_assert(va2 != NULL); + test_str("String100", va2[0]); + test_str("String101", va2[1]); + test_str("String102", va2[2]); + + test_assert(cmp(world, struct_with_vector_of_strings, ptr1, ptr2) == 0); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); - { - const StructWithVectorOfStrings *ptr = - ecs_get_id(world, instance, struct_with_vector_of_strings); - { - test_int(3, ecs_vec_count(&ptr->a)); - const ecs_string_t *va = ecs_vec_first(&ptr->a); - test_assert(va != NULL); - test_str("String100", va[0]); - test_str("String101", va[1]); - test_str("String102", va[2]); - } - } + const StructWithVectorOfStrings *ptr3 = + ecs_get_id(world, instance, struct_with_vector_of_strings); + test_int(3, ecs_vec_count(&ptr3->a)); + const ecs_string_t *va3 = ecs_vec_first(&ptr3->a); + test_assert(va3 != NULL); + test_str("String100", va3[0]); + test_str("String101", va3[1]); + test_str("String102", va3[2]); + + test_assert(cmp(world, struct_with_vector_of_strings, ptr1, ptr3) == 0); /* Test deleting: */ ecs_delete(world, e); @@ -1939,111 +2041,109 @@ void RuntimeTypes_nested_struct_with_vector_of_ints(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - NestedStructWithVectorOfInts *ptr = - ecs_ensure_id(world, e, nested_struct_with_vector_of_ints); - { - test_int(0, ecs_vec_count(&ptr->a)); - ecs_vec_set_count(NULL, &ptr->a, sizeof(ecs_i32_t), 3); - ecs_i32_t *va = ecs_vec_first(&ptr->a); - invoke_type_ctor(world, va, 3, ecs_id(ecs_i32_t)); - test_memory_zero(va, sizeof(ecs_i32_t) * 3); - va[0] = 100; - va[1] = 101; - va[2] = 102; - } - test_int(0, ptr->b); - ptr->b = 103; - { - test_int(0, ecs_vec_count(&ptr->c.a)); - ecs_vec_set_count(NULL, &ptr->c.a, sizeof(ecs_i32_t), 3); - ecs_i32_t *vca = ecs_vec_first(&ptr->c.a); - invoke_type_ctor(world, vca, 3, ecs_id(ecs_i32_t)); - test_memory_zero(vca, sizeof(ecs_i32_t) * 3); - vca[0] = 104; - vca[1] = 105; - vca[2] = 106; - } - test_int(0, ptr->c.b); - ptr->c.b = 107; - { - test_int(0, ecs_vec_count(&ptr->c.c)); - ecs_vec_set_count(NULL, &ptr->c.c, sizeof(ecs_i32_t), 3); - ecs_i32_t *vcc = ecs_vec_first(&ptr->c.c); - invoke_type_ctor(world, vcc, 3, ecs_id(ecs_i32_t)); - test_memory_zero(vcc, sizeof(ecs_i32_t) * 3); - vcc[0] = 108; - vcc[1] = 109; - vcc[2] = 110; - } + NestedStructWithVectorOfInts *ptr1 = + ecs_ensure_id(world, e, nested_struct_with_vector_of_ints); + { + test_int(0, ecs_vec_count(&ptr1->a)); + ecs_vec_set_count(NULL, &ptr1->a, sizeof(ecs_i32_t), 3); + ecs_i32_t *va = ecs_vec_first(&ptr1->a); + invoke_type_ctor(world, va, 3, ecs_id(ecs_i32_t)); + test_memory_zero(va, sizeof(ecs_i32_t) * 3); + va[0] = 100; + va[1] = 101; + va[2] = 102; + } + test_int(0, ptr1->b); + ptr1->b = 103; + { + test_int(0, ecs_vec_count(&ptr1->c.a)); + ecs_vec_set_count(NULL, &ptr1->c.a, sizeof(ecs_i32_t), 3); + ecs_i32_t *vca = ecs_vec_first(&ptr1->c.a); + invoke_type_ctor(world, vca, 3, ecs_id(ecs_i32_t)); + test_memory_zero(vca, sizeof(ecs_i32_t) * 3); + vca[0] = 104; + vca[1] = 105; + vca[2] = 106; + } + test_int(0, ptr1->c.b); + ptr1->c.b = 107; + { + test_int(0, ecs_vec_count(&ptr1->c.c)); + ecs_vec_set_count(NULL, &ptr1->c.c, sizeof(ecs_i32_t), 3); + ecs_i32_t *vcc = ecs_vec_first(&ptr1->c.c); + invoke_type_ctor(world, vcc, 3, ecs_id(ecs_i32_t)); + test_memory_zero(vcc, sizeof(ecs_i32_t) * 3); + vcc[0] = 108; + vcc[1] = 109; + vcc[2] = 110; } /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); + const NestedStructWithVectorOfInts *ptr2 = + ecs_get_id(world, instance, nested_struct_with_vector_of_ints); { - const NestedStructWithVectorOfInts *ptr = - ecs_get_id(world, instance, nested_struct_with_vector_of_ints); - { - test_int(3, ecs_vec_count(&ptr->a)); - const ecs_i32_t *va = ecs_vec_first(&ptr->a); - test_assert(va != NULL); - test_int(100, va[0]); - test_int(101, va[1]); - test_int(102, va[2]); - } - test_int(103, ptr->b); - { - test_int(3, ecs_vec_count(&ptr->c.a)); - const ecs_i32_t *vca = ecs_vec_first(&ptr->c.a); - test_assert(vca != NULL); - test_int(104, vca[0]); - test_int(105, vca[1]); - test_int(106, vca[2]); - } - test_int(107, ptr->c.b); - { - test_int(3, ecs_vec_count(&ptr->c.c)); - const ecs_i32_t *vcc = ecs_vec_first(&ptr->c.c); - test_assert(vcc != NULL); - test_int(108, vcc[0]); - test_int(109, vcc[1]); - test_int(110, vcc[2]); - } + test_int(3, ecs_vec_count(&ptr2->a)); + const ecs_i32_t *va = ecs_vec_first(&ptr2->a); + test_assert(va != NULL); + test_int(100, va[0]); + test_int(101, va[1]); + test_int(102, va[2]); } + test_int(103, ptr2->b); + { + test_int(3, ecs_vec_count(&ptr2->c.a)); + const ecs_i32_t *vca = ecs_vec_first(&ptr2->c.a); + test_assert(vca != NULL); + test_int(104, vca[0]); + test_int(105, vca[1]); + test_int(106, vca[2]); + } + test_int(107, ptr2->c.b); + { + test_int(3, ecs_vec_count(&ptr2->c.c)); + const ecs_i32_t *vcc = ecs_vec_first(&ptr2->c.c); + test_assert(vcc != NULL); + test_int(108, vcc[0]); + test_int(109, vcc[1]); + test_int(110, vcc[2]); + } + + test_assert(cmp(world, nested_struct_with_vector_of_ints, ptr1, ptr2) == 0); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); + const NestedStructWithVectorOfInts *ptr3 = + ecs_get_id(world, instance, nested_struct_with_vector_of_ints); { - const NestedStructWithVectorOfInts *ptr = - ecs_get_id(world, instance, nested_struct_with_vector_of_ints); - { - test_int(3, ecs_vec_count(&ptr->a)); - const ecs_i32_t *va = ecs_vec_first(&ptr->a); - test_assert(va != NULL); - test_int(100, va[0]); - test_int(101, va[1]); - test_int(102, va[2]); - } - test_int(103, ptr->b); - { - test_int(3, ecs_vec_count(&ptr->c.a)); - const ecs_i32_t *vca = ecs_vec_first(&ptr->c.a); - test_assert(vca != NULL); - test_int(104, vca[0]); - test_int(105, vca[1]); - test_int(106, vca[2]); - } - test_int(107, ptr->c.b); - { - test_int(3, ecs_vec_count(&ptr->c.c)); - const ecs_i32_t *vcc = ecs_vec_first(&ptr->c.c); - test_assert(vcc != NULL); - test_int(108, vcc[0]); - test_int(109, vcc[1]); - test_int(110, vcc[2]); - } + test_int(3, ecs_vec_count(&ptr3->a)); + const ecs_i32_t *va = ecs_vec_first(&ptr3->a); + test_assert(va != NULL); + test_int(100, va[0]); + test_int(101, va[1]); + test_int(102, va[2]); + } + test_int(103, ptr3->b); + { + test_int(3, ecs_vec_count(&ptr3->c.a)); + const ecs_i32_t *vca = ecs_vec_first(&ptr3->c.a); + test_assert(vca != NULL); + test_int(104, vca[0]); + test_int(105, vca[1]); + test_int(106, vca[2]); } + test_int(107, ptr3->c.b); + { + test_int(3, ecs_vec_count(&ptr3->c.c)); + const ecs_i32_t *vcc = ecs_vec_first(&ptr3->c.c); + test_assert(vcc != NULL); + test_int(108, vcc[0]); + test_int(109, vcc[1]); + test_int(110, vcc[2]); + } + + test_assert(cmp(world, nested_struct_with_vector_of_ints, ptr1, ptr3) == 0); /* Test deleting: */ ecs_delete(world, e); @@ -2088,111 +2188,109 @@ void RuntimeTypes_nested_struct_with_vector_of_strings(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - NestedStructWithVectorOfStrings *ptr = - ecs_ensure_id(world, e, nested_struct_with_vector_of_strings); - { - test_int(0, ecs_vec_count(&ptr->a)); - ecs_vec_set_count(NULL, &ptr->a, sizeof(ecs_string_t), 3); - ecs_string_t *va = ecs_vec_first(&ptr->a); - invoke_type_ctor(world, va, 3, ecs_id(ecs_string_t)); - test_memory_zero(va, sizeof(ecs_string_t) * 3); - va[0] = ecs_os_strdup("String100"); - va[1] = ecs_os_strdup("String101"); - va[2] = ecs_os_strdup("String102"); - } - test_int(0, ptr->b); - ptr->b = 103; - { - test_int(0, ecs_vec_count(&ptr->c.a)); - ecs_vec_set_count(NULL, &ptr->c.a, sizeof(ecs_string_t), 3); - ecs_string_t *vca = ecs_vec_first(&ptr->c.a); - invoke_type_ctor(world, vca, 3, ecs_id(ecs_string_t)); - test_memory_zero(vca, sizeof(ecs_string_t) * 3); - vca[0] = ecs_os_strdup("String104"); - vca[1] = ecs_os_strdup("String105"); - vca[2] = ecs_os_strdup("String106"); - } - test_int(0, ptr->c.b); - ptr->c.b = 107; - { - test_int(0, ecs_vec_count(&ptr->c.c)); - ecs_vec_set_count(NULL, &ptr->c.c, sizeof(ecs_string_t), 3); - ecs_string_t *vcc = ecs_vec_first(&ptr->c.c); - invoke_type_ctor(world, vcc, 3, ecs_id(ecs_string_t)); - test_memory_zero(vcc, sizeof(ecs_string_t) * 3); - vcc[0] = ecs_os_strdup("String108"); - vcc[1] = ecs_os_strdup("String109"); - vcc[2] = ecs_os_strdup("String110"); - } + NestedStructWithVectorOfStrings *ptr1 = + ecs_ensure_id(world, e, nested_struct_with_vector_of_strings); + { + test_int(0, ecs_vec_count(&ptr1->a)); + ecs_vec_set_count(NULL, &ptr1->a, sizeof(ecs_string_t), 3); + ecs_string_t *va = ecs_vec_first(&ptr1->a); + invoke_type_ctor(world, va, 3, ecs_id(ecs_string_t)); + test_memory_zero(va, sizeof(ecs_string_t) * 3); + va[0] = ecs_os_strdup("String100"); + va[1] = ecs_os_strdup("String101"); + va[2] = ecs_os_strdup("String102"); + } + test_int(0, ptr1->b); + ptr1->b = 103; + { + test_int(0, ecs_vec_count(&ptr1->c.a)); + ecs_vec_set_count(NULL, &ptr1->c.a, sizeof(ecs_string_t), 3); + ecs_string_t *vca = ecs_vec_first(&ptr1->c.a); + invoke_type_ctor(world, vca, 3, ecs_id(ecs_string_t)); + test_memory_zero(vca, sizeof(ecs_string_t) * 3); + vca[0] = ecs_os_strdup("String104"); + vca[1] = ecs_os_strdup("String105"); + vca[2] = ecs_os_strdup("String106"); + } + test_int(0, ptr1->c.b); + ptr1->c.b = 107; + { + test_int(0, ecs_vec_count(&ptr1->c.c)); + ecs_vec_set_count(NULL, &ptr1->c.c, sizeof(ecs_string_t), 3); + ecs_string_t *vcc = ecs_vec_first(&ptr1->c.c); + invoke_type_ctor(world, vcc, 3, ecs_id(ecs_string_t)); + test_memory_zero(vcc, sizeof(ecs_string_t) * 3); + vcc[0] = ecs_os_strdup("String108"); + vcc[1] = ecs_os_strdup("String109"); + vcc[2] = ecs_os_strdup("String110"); } /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); + const NestedStructWithVectorOfStrings *ptr2 = + ecs_get_id(world, instance, nested_struct_with_vector_of_strings); { - const NestedStructWithVectorOfStrings *ptr = - ecs_get_id(world, instance, nested_struct_with_vector_of_strings); - { - test_int(3, ecs_vec_count(&ptr->a)); - const ecs_string_t *va = ecs_vec_first(&ptr->a); - test_assert(va != NULL); - test_str("String100", va[0]); - test_str("String101", va[1]); - test_str("String102", va[2]); - } - test_int(103, ptr->b); - { - test_int(3, ecs_vec_count(&ptr->c.a)); - const ecs_string_t *vca = ecs_vec_first(&ptr->c.a); - test_assert(vca != NULL); - test_str("String104", vca[0]); - test_str("String105", vca[1]); - test_str("String106", vca[2]); - } - test_int(107, ptr->c.b); - { - test_int(3, ecs_vec_count(&ptr->c.c)); - const ecs_string_t *vcc = ecs_vec_first(&ptr->c.c); - test_assert(vcc != NULL); - test_str("String108", vcc[0]); - test_str("String109", vcc[1]); - test_str("String110", vcc[2]); - } + test_int(3, ecs_vec_count(&ptr2->a)); + const ecs_string_t *va = ecs_vec_first(&ptr2->a); + test_assert(va != NULL); + test_str("String100", va[0]); + test_str("String101", va[1]); + test_str("String102", va[2]); + } + test_int(103, ptr2->b); + { + test_int(3, ecs_vec_count(&ptr2->c.a)); + const ecs_string_t *vca = ecs_vec_first(&ptr2->c.a); + test_assert(vca != NULL); + test_str("String104", vca[0]); + test_str("String105", vca[1]); + test_str("String106", vca[2]); } + test_int(107, ptr2->c.b); + { + test_int(3, ecs_vec_count(&ptr2->c.c)); + const ecs_string_t *vcc = ecs_vec_first(&ptr2->c.c); + test_assert(vcc != NULL); + test_str("String108", vcc[0]); + test_str("String109", vcc[1]); + test_str("String110", vcc[2]); + } + + test_assert(cmp(world, nested_struct_with_vector_of_strings, ptr1, ptr2) == 0); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); + const NestedStructWithVectorOfStrings *ptr3 = + ecs_get_id(world, instance, nested_struct_with_vector_of_strings); { - const NestedStructWithVectorOfStrings *ptr = - ecs_get_id(world, instance, nested_struct_with_vector_of_strings); - { - test_int(3, ecs_vec_count(&ptr->a)); - const ecs_string_t *va = ecs_vec_first(&ptr->a); - test_assert(va != NULL); - test_str("String100", va[0]); - test_str("String101", va[1]); - test_str("String102", va[2]); - } - test_int(103, ptr->b); - { - test_int(3, ecs_vec_count(&ptr->c.a)); - const ecs_string_t *vca = ecs_vec_first(&ptr->c.a); - test_assert(vca != NULL); - test_str("String104", vca[0]); - test_str("String105", vca[1]); - test_str("String106", vca[2]); - } - test_int(107, ptr->c.b); - { - test_int(3, ecs_vec_count(&ptr->c.c)); - const ecs_string_t *vcc = ecs_vec_first(&ptr->c.c); - test_assert(vcc != NULL); - test_str("String108", vcc[0]); - test_str("String109", vcc[1]); - test_str("String110", vcc[2]); - } + test_int(3, ecs_vec_count(&ptr3->a)); + const ecs_string_t *va = ecs_vec_first(&ptr3->a); + test_assert(va != NULL); + test_str("String100", va[0]); + test_str("String101", va[1]); + test_str("String102", va[2]); } + test_int(103, ptr3->b); + { + test_int(3, ecs_vec_count(&ptr3->c.a)); + const ecs_string_t *vca = ecs_vec_first(&ptr3->c.a); + test_assert(vca != NULL); + test_str("String104", vca[0]); + test_str("String105", vca[1]); + test_str("String106", vca[2]); + } + test_int(107, ptr3->c.b); + { + test_int(3, ecs_vec_count(&ptr3->c.c)); + const ecs_string_t *vcc = ecs_vec_first(&ptr3->c.c); + test_assert(vcc != NULL); + test_str("String108", vcc[0]); + test_str("String109", vcc[1]); + test_str("String110", vcc[2]); + } + + test_assert(cmp(world, nested_struct_with_vector_of_strings, ptr1, ptr3) == 0); /* Test deleting: */ ecs_delete(world, e); @@ -2209,32 +2307,30 @@ void RuntimeTypes_array_of_ints(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - ecs_i32_t *arr = ecs_ensure_id(world, e, array_of_ints); - test_memory_zero(arr, sizeof(ecs_i32_t[3])); - arr[0] = 100; - arr[1] = 101; - arr[2] = 102; - } + ecs_i32_t *arr1 = ecs_ensure_id(world, e, array_of_ints); + test_memory_zero(arr1, sizeof(ecs_i32_t[3])); + arr1[0] = 100; + arr1[1] = 101; + arr1[2] = 102; /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); - { - const ecs_i32_t *arr = ecs_get_id(world, e, array_of_ints); - test_int(100, arr[0]); - test_int(101, arr[1]); - test_int(102, arr[2]); - } + const ecs_i32_t *arr2 = ecs_get_id(world, e, array_of_ints); + test_int(100, arr2[0]); + test_int(101, arr2[1]); + test_int(102, arr2[2]); + + test_assert(cmp(world, array_of_ints, arr1, arr2) == 0); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); - { - const ecs_i32_t *arr = ecs_get_id(world, e, array_of_ints); - test_int(100, arr[0]); - test_int(101, arr[1]); - test_int(102, arr[2]); - } + const ecs_i32_t *arr3 = ecs_get_id(world, e, array_of_ints); + test_int(100, arr3[0]); + test_int(101, arr3[1]); + test_int(102, arr3[2]); + + test_assert(cmp(world, array_of_ints, arr1, arr3) == 0); /* Test deleting: */ ecs_delete(world, e); @@ -2251,32 +2347,30 @@ void RuntimeTypes_array_of_strings(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - ecs_string_t *arr = ecs_ensure_id(world, e, array_of_strings); - test_memory_zero(arr, sizeof(ecs_string_t[3])); - arr[0] = ecs_os_strdup("String100"); - arr[1] = ecs_os_strdup("String101"); - arr[2] = ecs_os_strdup("String102"); - } + ecs_string_t *arr1 = ecs_ensure_id(world, e, array_of_strings); + test_memory_zero(arr1, sizeof(ecs_string_t[3])); + arr1[0] = ecs_os_strdup("String100"); + arr1[1] = ecs_os_strdup("String101"); + arr1[2] = ecs_os_strdup("String102"); /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); - { - const ecs_string_t *arr = ecs_get_id(world, e, array_of_strings); - test_str("String100", arr[0]); - test_str("String101", arr[1]); - test_str("String102", arr[2]); - } + const ecs_string_t *arr2 = ecs_get_id(world, e, array_of_strings); + test_str("String100", arr2[0]); + test_str("String101", arr2[1]); + test_str("String102", arr2[2]); + + test_assert(cmp(world, array_of_strings, arr1, arr2) == 0); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); - { - const ecs_string_t *arr = ecs_get_id(world, e, array_of_strings); - test_str("String100", arr[0]); - test_str("String101", arr[1]); - test_str("String102", arr[2]); - } + const ecs_string_t *arr3 = ecs_get_id(world, e, array_of_strings); + test_str("String100", arr3[0]); + test_str("String101", arr3[1]); + test_str("String102", arr3[2]); + + test_assert(cmp(world, array_of_strings, arr1, arr3) == 0); /* Test deleting: */ ecs_delete(world, e); @@ -2305,44 +2399,42 @@ void RuntimeTypes_array_of_struct_with_ints(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - StructWithInts *arr = - ecs_ensure_id(world, e, array_of_struct_with_ints); - test_memory_zero(arr, sizeof(StructWithInts[3])); - arr[0].a = 100; - arr[0].b = 101; - arr[1].a = 102; - arr[1].b = 103; - arr[2].a = 104; - arr[2].b = 105; - } + StructWithInts *arr1 = + ecs_ensure_id(world, e, array_of_struct_with_ints); + test_memory_zero(arr1, sizeof(StructWithInts[3])); + arr1[0].a = 100; + arr1[0].b = 101; + arr1[1].a = 102; + arr1[1].b = 103; + arr1[2].a = 104; + arr1[2].b = 105; /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); - { - const StructWithInts *arr = - ecs_get_id(world, e, array_of_struct_with_ints); - test_int(100, arr[0].a); - test_int(101, arr[0].b); - test_int(102, arr[1].a); - test_int(103, arr[1].b); - test_int(104, arr[2].a); - test_int(105, arr[2].b); - } + const StructWithInts *arr2 = + ecs_get_id(world, e, array_of_struct_with_ints); + test_int(100, arr2[0].a); + test_int(101, arr2[0].b); + test_int(102, arr2[1].a); + test_int(103, arr2[1].b); + test_int(104, arr2[2].a); + test_int(105, arr2[2].b); + + test_assert(cmp(world, array_of_struct_with_ints, arr1, arr2) == 0); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); - { - const StructWithInts *arr = - ecs_get_id(world, e, array_of_struct_with_ints); - test_int(100, arr[0].a); - test_int(101, arr[0].b); - test_int(102, arr[1].a); - test_int(103, arr[1].b); - test_int(104, arr[2].a); - test_int(105, arr[2].b); - } + const StructWithInts *arr3 = + ecs_get_id(world, e, array_of_struct_with_ints); + test_int(100, arr3[0].a); + test_int(101, arr3[0].b); + test_int(102, arr3[1].a); + test_int(103, arr3[1].b); + test_int(104, arr3[2].a); + test_int(105, arr3[2].b); + + test_assert(cmp(world, array_of_struct_with_ints, arr1, arr3) == 0); /* Test deleting: */ ecs_delete(world, e); @@ -2373,53 +2465,51 @@ void RuntimeTypes_array_of_struct_with_strings(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - StructWithStrings *arr = - ecs_ensure_id(world, e, array_of_struct_with_strings); - test_memory_zero(arr, sizeof(StructWithStrings[3])); - arr[0].a = ecs_os_strdup("String100"); - arr[0].b = 101; - arr[0].c = ecs_os_strdup("String102"); - arr[1].a = ecs_os_strdup("String103"); - arr[1].b = 104; - arr[1].c = ecs_os_strdup("String105"); - arr[2].a = ecs_os_strdup("String106"); - arr[2].b = 107; - arr[2].c = ecs_os_strdup("String108"); - } + StructWithStrings *arr1 = + ecs_ensure_id(world, e, array_of_struct_with_strings); + test_memory_zero(arr1, sizeof(StructWithStrings[3])); + arr1[0].a = ecs_os_strdup("String100"); + arr1[0].b = 101; + arr1[0].c = ecs_os_strdup("String102"); + arr1[1].a = ecs_os_strdup("String103"); + arr1[1].b = 104; + arr1[1].c = ecs_os_strdup("String105"); + arr1[2].a = ecs_os_strdup("String106"); + arr1[2].b = 107; + arr1[2].c = ecs_os_strdup("String108"); /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); - { - const StructWithStrings *arr = - ecs_get_id(world, e, array_of_struct_with_strings); - test_str("String100", arr[0].a); - test_int(101, arr[0].b); - test_str("String102", arr[0].c); - test_str("String103", arr[1].a); - test_int(104, arr[1].b); - test_str("String105", arr[1].c); - test_str("String106", arr[2].a); - test_int(107, arr[2].b); - test_str("String108", arr[2].c); - } + const StructWithStrings *arr2 = + ecs_get_id(world, e, array_of_struct_with_strings); + test_str("String100", arr2[0].a); + test_int(101, arr2[0].b); + test_str("String102", arr2[0].c); + test_str("String103", arr2[1].a); + test_int(104, arr2[1].b); + test_str("String105", arr2[1].c); + test_str("String106", arr2[2].a); + test_int(107, arr2[2].b); + test_str("String108", arr2[2].c); + + test_assert(cmp(world, array_of_struct_with_strings, arr1, arr2) == 0); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); - { - const StructWithStrings *arr = - ecs_get_id(world, e, array_of_struct_with_strings); - test_str("String100", arr[0].a); - test_int(101, arr[0].b); - test_str("String102", arr[0].c); - test_str("String103", arr[1].a); - test_int(104, arr[1].b); - test_str("String105", arr[1].c); - test_str("String106", arr[2].a); - test_int(107, arr[2].b); - test_str("String108", arr[2].c); - } + const StructWithStrings *arr3 = + ecs_get_id(world, e, array_of_struct_with_strings); + test_str("String100", arr3[0].a); + test_int(101, arr3[0].b); + test_str("String102", arr3[0].c); + test_str("String103", arr3[1].a); + test_int(104, arr3[1].b); + test_str("String105", arr3[1].c); + test_str("String106", arr3[2].a); + test_int(107, arr3[2].b); + test_str("String108", arr3[2].c); + + test_assert(cmp(world, array_of_struct_with_strings, arr1, arr3) == 0); /* Test deleting: */ ecs_delete(world, e); @@ -2452,44 +2542,45 @@ void RuntimeTypes_array_of_struct_with_opaques(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - StructWithOpaque *arr = - ecs_ensure_id(world, e, array_of_struct_with_opaques); - test_assert(arr[0].a.id != 0); - test_int(0, arr[0].a.value); - arr[0].a.value = 100; - test_assert(arr[1].a.id != 0); - test_int(0, arr[1].a.value); - arr[1].a.value = 101; - test_assert(arr[2].a.id != 0); - test_int(0, arr[2].a.value); - arr[2].a.value = 102; - } + StructWithOpaque *arr1 = + ecs_ensure_id(world, e, array_of_struct_with_opaques); + test_assert(arr1[0].a.id != 0); + test_int(0, arr1[0].a.value); + arr1[0].a.value = 100; + test_assert(arr1[1].a.id != 0); + test_int(0, arr1[1].a.value); + arr1[1].a.value = 101; + test_assert(arr1[2].a.id != 0); + test_int(0, arr1[2].a.value); + arr1[2].a.value = 102; + /* 3 resource(s) should have been used */ test_int(3, initial_resources - resources_left()); /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); - { - const StructWithOpaque *arr = - ecs_get_id(world, e, array_of_struct_with_opaques); - test_int(100, arr[0].a.value); - test_int(101, arr[1].a.value); - test_int(102, arr[2].a.value); - } + const StructWithOpaque *arr2 = + ecs_get_id(world, e, array_of_struct_with_opaques); + test_int(100, arr2[0].a.value); + test_int(101, arr2[1].a.value); + test_int(102, arr2[2].a.value); + + test_assert(cmp(world, array_of_struct_with_opaques, arr1, arr2) == 0); + /* 6 resource(s) should be in use now */ test_int(6, initial_resources - resources_left()); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); - { - const StructWithOpaque *arr = - ecs_get_id(world, e, array_of_struct_with_opaques); - test_int(100, arr[0].a.value); - test_int(101, arr[1].a.value); - test_int(102, arr[2].a.value); - } + const StructWithOpaque *arr3 = + ecs_get_id(world, e, array_of_struct_with_opaques); + test_int(100, arr3[0].a.value); + test_int(101, arr3[1].a.value); + test_int(102, arr3[2].a.value); + + test_assert(cmp(world, array_of_struct_with_opaques, arr1, arr3) == 0); + /* 6 resource(s) should still be in use after a move */ test_int(6, initial_resources - resources_left()); @@ -2524,45 +2615,41 @@ void RuntimeTypes_array_of_array_of_strings(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - ecs_string_t(*arr)[3] = - ecs_ensure_id(world, e, array_of_array_of_strings); - test_memory_zero(arr, sizeof(ecs_string_t[3][3])); - int i; - for (i = 0; i < 3; i++) { - arr[i][0] = ecs_os_strdup("String100"); - arr[i][1] = ecs_os_strdup("String101"); - arr[i][2] = ecs_os_strdup("String102"); - } + ecs_string_t(*arr1)[3] = + ecs_ensure_id(world, e, array_of_array_of_strings); + test_memory_zero(arr1, sizeof(ecs_string_t[3][3])); + int i; + for (i = 0; i < 3; i++) { + arr1[i][0] = ecs_os_strdup("String100"); + arr1[i][1] = ecs_os_strdup("String101"); + arr1[i][2] = ecs_os_strdup("String102"); } /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); - { - const ecs_string_t(*arr)[3] = (const ecs_string_t(*)[3]) ecs_get_id( - world, e, array_of_array_of_strings); - int i; - for (i = 0; i < 3; i++) { - test_str("String100", arr[i][0]); - test_str("String101", arr[i][1]); - test_str("String102", arr[i][2]); - } + const ecs_string_t(*arr2)[3] = (const ecs_string_t(*)[3]) ecs_get_id( + world, e, array_of_array_of_strings); + for (i = 0; i < 3; i++) { + test_str("String100", arr2[i][0]); + test_str("String101", arr2[i][1]); + test_str("String102", arr2[i][2]); } + test_assert(cmp(world, array_of_array_of_strings, arr1, arr2) == 0); + /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); - { - const ecs_string_t(*arr)[3] = (const ecs_string_t(*)[3]) ecs_get_id( - world, e, array_of_array_of_strings); - int i; - for (i = 0; i < 3; i++) { - test_str("String100", arr[i][0]); - test_str("String101", arr[i][1]); - test_str("String102", arr[i][2]); - } + const ecs_string_t(*arr3)[3] = (const ecs_string_t(*)[3]) ecs_get_id( + world, e, array_of_array_of_strings); + for (i = 0; i < 3; i++) { + test_str("String100", arr3[i][0]); + test_str("String101", arr3[i][1]); + test_str("String102", arr3[i][2]); } + test_assert(cmp(world, array_of_array_of_strings, arr1, arr3) == 0); + /* Test deleting: */ ecs_delete(world, e); ecs_delete(world, instance); @@ -2596,64 +2683,60 @@ void RuntimeTypes_array_of_array_of_struct_with_strings(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - StructWithStrings(*arr)[3] = - ecs_ensure_id(world, e, array_of_array_of_struct_with_strings); - test_memory_zero(arr, sizeof(StructWithStrings[3][3])); - int i; - for (i = 0; i < 3; i++) { - arr[i][0].a = ecs_os_strdup("String100"); - arr[i][0].b = 101; - arr[i][0].c = ecs_os_strdup("String102"); - arr[i][1].a = ecs_os_strdup("String103"); - arr[i][1].b = 104; - arr[i][1].c = ecs_os_strdup("String105"); - arr[i][2].a = ecs_os_strdup("String106"); - arr[i][2].b = 107; - arr[i][2].c = ecs_os_strdup("String108"); - } + StructWithStrings(*arr1)[3] = + ecs_ensure_id(world, e, array_of_array_of_struct_with_strings); + test_memory_zero(arr1, sizeof(StructWithStrings[3][3])); + int i; + for (i = 0; i < 3; i++) { + arr1[i][0].a = ecs_os_strdup("String100"); + arr1[i][0].b = 101; + arr1[i][0].c = ecs_os_strdup("String102"); + arr1[i][1].a = ecs_os_strdup("String103"); + arr1[i][1].b = 104; + arr1[i][1].c = ecs_os_strdup("String105"); + arr1[i][2].a = ecs_os_strdup("String106"); + arr1[i][2].b = 107; + arr1[i][2].c = ecs_os_strdup("String108"); } /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); - { - const StructWithStrings(*arr)[3] = - (const StructWithStrings(*)[3]) ecs_get_id( - world, e, array_of_array_of_struct_with_strings); - int i; - for (i = 0; i < 3; i++) { - test_str("String100", arr[i][0].a); - test_int(101, arr[i][0].b); - test_str("String102", arr[i][0].c); - test_str("String103", arr[i][1].a); - test_int(104, arr[i][1].b); - test_str("String105", arr[i][1].c); - test_str("String106", arr[i][2].a); - test_int(107, arr[i][2].b); - test_str("String108", arr[i][2].c); - } - } + const StructWithStrings(*arr2)[3] = + (const StructWithStrings(*)[3]) ecs_get_id( + world, e, array_of_array_of_struct_with_strings); + for (i = 0; i < 3; i++) { + test_str("String100", arr2[i][0].a); + test_int(101, arr2[i][0].b); + test_str("String102", arr2[i][0].c); + test_str("String103", arr2[i][1].a); + test_int(104, arr2[i][1].b); + test_str("String105", arr2[i][1].c); + test_str("String106", arr2[i][2].a); + test_int(107, arr2[i][2].b); + test_str("String108", arr2[i][2].c); + } + + test_assert(cmp(world, array_of_array_of_struct_with_strings, arr1, arr2) == 0); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); - { - const StructWithStrings(*arr)[3] = - (const StructWithStrings(*)[3]) ecs_get_id( - world, e, array_of_array_of_struct_with_strings); - int i; - for (i = 0; i < 3; i++) { - test_str("String100", arr[i][0].a); - test_int(101, arr[i][0].b); - test_str("String102", arr[i][0].c); - test_str("String103", arr[i][1].a); - test_int(104, arr[i][1].b); - test_str("String105", arr[i][1].c); - test_str("String106", arr[i][2].a); - test_int(107, arr[i][2].b); - test_str("String108", arr[i][2].c); - } - } + const StructWithStrings(*arr3)[3] = + (const StructWithStrings(*)[3]) ecs_get_id( + world, e, array_of_array_of_struct_with_strings); + for (i = 0; i < 3; i++) { + test_str("String100", arr3[i][0].a); + test_int(101, arr3[i][0].b); + test_str("String102", arr3[i][0].c); + test_str("String103", arr3[i][1].a); + test_int(104, arr3[i][1].b); + test_str("String105", arr3[i][1].c); + test_str("String106", arr3[i][2].a); + test_int(107, arr3[i][2].b); + test_str("String108", arr3[i][2].c); + } + + test_assert(cmp(world, array_of_array_of_struct_with_strings, arr1, arr3) == 0); /* Test deleting: */ ecs_delete(world, e); @@ -2673,101 +2756,99 @@ void RuntimeTypes_array_of_vectors_of_ints(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - ecs_vec_t *arr = ecs_ensure_id(world, e, array_of_vectors_of_ints); - { - test_int(0, ecs_vec_count(&arr[0])); - ecs_vec_set_count(NULL, &arr[0], sizeof(ecs_i32_t), 3); - ecs_i32_t *v = ecs_vec_first(&arr[0]); - invoke_type_ctor(world, v, 3, ecs_id(ecs_i32_t)); - test_memory_zero(v, sizeof(ecs_i32_t) * 3); - v[0] = 100; - v[1] = 101; - v[2] = 102; - } - { - test_int(0, ecs_vec_count(&arr[1])); - ecs_vec_set_count(NULL, &arr[1], sizeof(ecs_i32_t), 3); - ecs_i32_t *v = ecs_vec_first(&arr[1]); - invoke_type_ctor(world, v, 3, ecs_id(ecs_i32_t)); - test_memory_zero(v, sizeof(ecs_i32_t) * 3); - v[0] = 103; - v[1] = 104; - v[2] = 105; - } - { - test_int(0, ecs_vec_count(&arr[2])); - ecs_vec_set_count(NULL, &arr[2], sizeof(ecs_i32_t), 3); - ecs_i32_t *v = ecs_vec_first(&arr[2]); - invoke_type_ctor(world, v, 3, ecs_id(ecs_i32_t)); - test_memory_zero(v, sizeof(ecs_i32_t) * 3); - v[0] = 106; - v[1] = 107; - v[2] = 108; - } + ecs_vec_t *arr1 = ecs_ensure_id(world, e, array_of_vectors_of_ints); + { + test_int(0, ecs_vec_count(&arr1[0])); + ecs_vec_set_count(NULL, &arr1[0], sizeof(ecs_i32_t), 3); + ecs_i32_t *v = ecs_vec_first(&arr1[0]); + invoke_type_ctor(world, v, 3, ecs_id(ecs_i32_t)); + test_memory_zero(v, sizeof(ecs_i32_t) * 3); + v[0] = 100; + v[1] = 101; + v[2] = 102; + } + { + test_int(0, ecs_vec_count(&arr1[1])); + ecs_vec_set_count(NULL, &arr1[1], sizeof(ecs_i32_t), 3); + ecs_i32_t *v = ecs_vec_first(&arr1[1]); + invoke_type_ctor(world, v, 3, ecs_id(ecs_i32_t)); + test_memory_zero(v, sizeof(ecs_i32_t) * 3); + v[0] = 103; + v[1] = 104; + v[2] = 105; + } + { + test_int(0, ecs_vec_count(&arr1[2])); + ecs_vec_set_count(NULL, &arr1[2], sizeof(ecs_i32_t), 3); + ecs_i32_t *v = ecs_vec_first(&arr1[2]); + invoke_type_ctor(world, v, 3, ecs_id(ecs_i32_t)); + test_memory_zero(v, sizeof(ecs_i32_t) * 3); + v[0] = 106; + v[1] = 107; + v[2] = 108; } /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); + const ecs_vec_t *arr2 = ecs_get_id(world, e, array_of_vectors_of_ints); { - const ecs_vec_t *arr = ecs_get_id(world, e, array_of_vectors_of_ints); - { - test_int(3, ecs_vec_count(&arr[0])); - const ecs_i32_t *v = ecs_vec_first(&arr[0]); - test_assert(v != NULL); - test_int(100, v[0]); - test_int(101, v[1]); - test_int(102, v[2]); - } - { - test_int(3, ecs_vec_count(&arr[1])); - const ecs_i32_t *v = ecs_vec_first(&arr[1]); - test_assert(v != NULL); - test_int(103, v[0]); - test_int(104, v[1]); - test_int(105, v[2]); - } - { - test_int(3, ecs_vec_count(&arr[2])); - const ecs_i32_t *v = ecs_vec_first(&arr[2]); - test_assert(v != NULL); - test_int(106, v[0]); - test_int(107, v[1]); - test_int(108, v[2]); - } + test_int(3, ecs_vec_count(&arr2[0])); + const ecs_i32_t *v = ecs_vec_first(&arr2[0]); + test_assert(v != NULL); + test_int(100, v[0]); + test_int(101, v[1]); + test_int(102, v[2]); } + { + test_int(3, ecs_vec_count(&arr2[1])); + const ecs_i32_t *v = ecs_vec_first(&arr2[1]); + test_assert(v != NULL); + test_int(103, v[0]); + test_int(104, v[1]); + test_int(105, v[2]); + } + { + test_int(3, ecs_vec_count(&arr2[2])); + const ecs_i32_t *v = ecs_vec_first(&arr2[2]); + test_assert(v != NULL); + test_int(106, v[0]); + test_int(107, v[1]); + test_int(108, v[2]); + } + + test_assert(cmp(world, array_of_vectors_of_ints, arr1, arr2) == 0); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); + const ecs_vec_t *arr3 = ecs_get_id(world, e, array_of_vectors_of_ints); { - const ecs_vec_t *arr = ecs_get_id(world, e, array_of_vectors_of_ints); - { - test_int(3, ecs_vec_count(&arr[0])); - const ecs_i32_t *v = ecs_vec_first(&arr[0]); - test_assert(v != NULL); - test_int(100, v[0]); - test_int(101, v[1]); - test_int(102, v[2]); - } - { - test_int(3, ecs_vec_count(&arr[1])); - const ecs_i32_t *v = ecs_vec_first(&arr[1]); - test_assert(v != NULL); - test_int(103, v[0]); - test_int(104, v[1]); - test_int(105, v[2]); - } - { - test_int(3, ecs_vec_count(&arr[2])); - const ecs_i32_t *v = ecs_vec_first(&arr[2]); - test_assert(v != NULL); - test_int(106, v[0]); - test_int(107, v[1]); - test_int(108, v[2]); - } + test_int(3, ecs_vec_count(&arr3[0])); + const ecs_i32_t *v = ecs_vec_first(&arr3[0]); + test_assert(v != NULL); + test_int(100, v[0]); + test_int(101, v[1]); + test_int(102, v[2]); + } + { + test_int(3, ecs_vec_count(&arr3[1])); + const ecs_i32_t *v = ecs_vec_first(&arr3[1]); + test_assert(v != NULL); + test_int(103, v[0]); + test_int(104, v[1]); + test_int(105, v[2]); + } + { + test_int(3, ecs_vec_count(&arr3[2])); + const ecs_i32_t *v = ecs_vec_first(&arr3[2]); + test_assert(v != NULL); + test_int(106, v[0]); + test_int(107, v[1]); + test_int(108, v[2]); } + test_assert(cmp(world, array_of_vectors_of_ints, arr1, arr3) == 0); + /* Test deleting: */ ecs_delete(world, e); ecs_delete(world, instance); @@ -2786,102 +2867,100 @@ void RuntimeTypes_array_of_vectors_of_strings(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - ecs_vec_t *arr = ecs_ensure_id(world, e, array_of_vectors_of_strings); - { - test_int(0, ecs_vec_count(&arr[0])); - ecs_vec_set_count(NULL, &arr[0], sizeof(ecs_string_t), 3); - ecs_string_t *v = ecs_vec_first(&arr[0]); - invoke_type_ctor(world, v, 3, ecs_id(ecs_string_t)); - test_memory_zero(v, sizeof(ecs_string_t) * 3); - v[0] = ecs_os_strdup("String100"); - v[1] = ecs_os_strdup("String101"); - v[2] = ecs_os_strdup("String102"); - } - { - test_int(0, ecs_vec_count(&arr[1])); - ecs_vec_set_count(NULL, &arr[1], sizeof(ecs_string_t), 3); - ecs_string_t *v = ecs_vec_first(&arr[1]); - invoke_type_ctor(world, v, 3, ecs_id(ecs_string_t)); - test_memory_zero(v, sizeof(ecs_string_t) * 3); - v[0] = ecs_os_strdup("String103"); - v[1] = ecs_os_strdup("String104"); - v[2] = ecs_os_strdup("String105"); - } - { - test_int(0, ecs_vec_count(&arr[2])); - ecs_vec_set_count(NULL, &arr[2], sizeof(ecs_string_t), 3); - ecs_string_t *v = ecs_vec_first(&arr[2]); - invoke_type_ctor(world, v, 3, ecs_id(ecs_string_t)); - test_memory_zero(v, sizeof(ecs_string_t) * 3); - v[0] = ecs_os_strdup("String106"); - v[1] = ecs_os_strdup("String107"); - v[2] = ecs_os_strdup("String108"); - } + ecs_vec_t *arr1 = ecs_ensure_id(world, e, array_of_vectors_of_strings); + { + test_int(0, ecs_vec_count(&arr1[0])); + ecs_vec_set_count(NULL, &arr1[0], sizeof(ecs_string_t), 3); + ecs_string_t *v = ecs_vec_first(&arr1[0]); + invoke_type_ctor(world, v, 3, ecs_id(ecs_string_t)); + test_memory_zero(v, sizeof(ecs_string_t) * 3); + v[0] = ecs_os_strdup("String100"); + v[1] = ecs_os_strdup("String101"); + v[2] = ecs_os_strdup("String102"); + } + { + test_int(0, ecs_vec_count(&arr1[1])); + ecs_vec_set_count(NULL, &arr1[1], sizeof(ecs_string_t), 3); + ecs_string_t *v = ecs_vec_first(&arr1[1]); + invoke_type_ctor(world, v, 3, ecs_id(ecs_string_t)); + test_memory_zero(v, sizeof(ecs_string_t) * 3); + v[0] = ecs_os_strdup("String103"); + v[1] = ecs_os_strdup("String104"); + v[2] = ecs_os_strdup("String105"); + } + { + test_int(0, ecs_vec_count(&arr1[2])); + ecs_vec_set_count(NULL, &arr1[2], sizeof(ecs_string_t), 3); + ecs_string_t *v = ecs_vec_first(&arr1[2]); + invoke_type_ctor(world, v, 3, ecs_id(ecs_string_t)); + test_memory_zero(v, sizeof(ecs_string_t) * 3); + v[0] = ecs_os_strdup("String106"); + v[1] = ecs_os_strdup("String107"); + v[2] = ecs_os_strdup("String108"); } /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); + const ecs_vec_t *arr2 = + ecs_get_id(world, e, array_of_vectors_of_strings); { - const ecs_vec_t *arr = - ecs_get_id(world, e, array_of_vectors_of_strings); - { - test_int(3, ecs_vec_count(&arr[0])); - const ecs_string_t *v = ecs_vec_first(&arr[0]); - test_assert(v != NULL); - test_str("String100", v[0]); - test_str("String101", v[1]); - test_str("String102", v[2]); - } - { - test_int(3, ecs_vec_count(&arr[1])); - const ecs_string_t *v = ecs_vec_first(&arr[1]); - test_assert(v != NULL); - test_str("String103", v[0]); - test_str("String104", v[1]); - test_str("String105", v[2]); - } - { - test_int(3, ecs_vec_count(&arr[2])); - const ecs_string_t *v = ecs_vec_first(&arr[2]); - test_assert(v != NULL); - test_str("String106", v[0]); - test_str("String107", v[1]); - test_str("String108", v[2]); - } + test_int(3, ecs_vec_count(&arr2[0])); + const ecs_string_t *v = ecs_vec_first(&arr2[0]); + test_assert(v != NULL); + test_str("String100", v[0]); + test_str("String101", v[1]); + test_str("String102", v[2]); + } + { + test_int(3, ecs_vec_count(&arr2[1])); + const ecs_string_t *v = ecs_vec_first(&arr2[1]); + test_assert(v != NULL); + test_str("String103", v[0]); + test_str("String104", v[1]); + test_str("String105", v[2]); + } + { + test_int(3, ecs_vec_count(&arr2[2])); + const ecs_string_t *v = ecs_vec_first(&arr2[2]); + test_assert(v != NULL); + test_str("String106", v[0]); + test_str("String107", v[1]); + test_str("String108", v[2]); } + test_assert(cmp(world, array_of_vectors_of_strings, arr1, arr2) == 0); + /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); + const ecs_vec_t *arr3 = + ecs_get_id(world, e, array_of_vectors_of_strings); { - const ecs_vec_t *arr = - ecs_get_id(world, e, array_of_vectors_of_strings); - { - test_int(3, ecs_vec_count(&arr[0])); - const ecs_string_t *v = ecs_vec_first(&arr[0]); - test_assert(v != NULL); - test_str("String100", v[0]); - test_str("String101", v[1]); - test_str("String102", v[2]); - } - { - test_int(3, ecs_vec_count(&arr[1])); - const ecs_string_t *v = ecs_vec_first(&arr[1]); - test_assert(v != NULL); - test_str("String103", v[0]); - test_str("String104", v[1]); - test_str("String105", v[2]); - } - { - test_int(3, ecs_vec_count(&arr[2])); - const ecs_string_t *v = ecs_vec_first(&arr[2]); - test_assert(v != NULL); - test_str("String106", v[0]); - test_str("String107", v[1]); - test_str("String108", v[2]); - } + test_int(3, ecs_vec_count(&arr3[0])); + const ecs_string_t *v = ecs_vec_first(&arr3[0]); + test_assert(v != NULL); + test_str("String100", v[0]); + test_str("String101", v[1]); + test_str("String102", v[2]); + } + { + test_int(3, ecs_vec_count(&arr3[1])); + const ecs_string_t *v = ecs_vec_first(&arr3[1]); + test_assert(v != NULL); + test_str("String103", v[0]); + test_str("String104", v[1]); + test_str("String105", v[2]); } + { + test_int(3, ecs_vec_count(&arr3[2])); + const ecs_string_t *v = ecs_vec_first(&arr3[2]); + test_assert(v != NULL); + test_str("String106", v[0]); + test_str("String107", v[1]); + test_str("String108", v[2]); + } + + test_assert(cmp(world, array_of_vectors_of_strings, arr1, arr3) == 0); /* Test deleting: */ ecs_delete(world, e); @@ -2904,41 +2983,42 @@ void RuntimeTypes_array_of_opaque(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - ResourceHandle *arr = ecs_ensure_id(world, e, array_of_opaque); - test_assert(arr[0].id != 0); - test_int(0, arr[0].value); - arr[0].value = 100; - test_assert(arr[1].id != 0); - test_int(0, arr[1].value); - arr[1].value = 101; - test_assert(arr[2].id != 0); - test_int(0, arr[2].value); - arr[2].value = 102; - } + ResourceHandle *arr1 = ecs_ensure_id(world, e, array_of_opaque); + test_assert(arr1[0].id != 0); + test_int(0, arr1[0].value); + arr1[0].value = 100; + test_assert(arr1[1].id != 0); + test_int(0, arr1[1].value); + arr1[1].value = 101; + test_assert(arr1[2].id != 0); + test_int(0, arr1[2].value); + arr1[2].value = 102; + /* 3 resource(s) should have been used */ test_int(3, initial_resources - resources_left()); /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); - { - const ResourceHandle *arr = ecs_get_id(world, e, array_of_opaque); - test_int(100, arr[0].value); - test_int(101, arr[1].value); - test_int(102, arr[2].value); - } + const ResourceHandle *arr2 = ecs_get_id(world, e, array_of_opaque); + test_int(100, arr2[0].value); + test_int(101, arr2[1].value); + test_int(102, arr2[2].value); + + test_assert(cmp(world, array_of_opaque, arr1, arr2) == 0); + /* 6 resource(s) should be in use now */ test_int(6, initial_resources - resources_left()); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); - { - const ResourceHandle *arr = ecs_get_id(world, e, array_of_opaque); - test_int(100, arr[0].value); - test_int(101, arr[1].value); - test_int(102, arr[2].value); - } + const ResourceHandle *arr3 = ecs_get_id(world, e, array_of_opaque); + test_int(100, arr3[0].value); + test_int(101, arr3[1].value); + test_int(102, arr3[2].value); + + test_assert(cmp(world, array_of_opaque, arr1, arr3) == 0); + /* 6 resource(s) should still be in use after a move */ test_int(6, initial_resources - resources_left()); @@ -2970,49 +3050,47 @@ void RuntimeTypes_vector_of_ints(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); + ecs_vec_t *vec1 = ecs_ensure_id(world, e, vector_of_ints); { - ecs_vec_t *vec = ecs_ensure_id(world, e, vector_of_ints); - { - test_int(0, ecs_vec_count(vec)); - ecs_vec_set_count(NULL, vec, sizeof(ecs_i32_t), 3); - ecs_i32_t *v = ecs_vec_first(vec); - invoke_type_ctor(world, v, 3, ecs_id(ecs_i32_t)); - test_memory_zero(v, sizeof(ecs_i32_t) * 3); - v[0] = 100; - v[1] = 101; - v[2] = 102; - } + test_int(0, ecs_vec_count(vec1)); + ecs_vec_set_count(NULL, vec1, sizeof(ecs_i32_t), 3); + ecs_i32_t *v = ecs_vec_first(vec1); + invoke_type_ctor(world, v, 3, ecs_id(ecs_i32_t)); + test_memory_zero(v, sizeof(ecs_i32_t) * 3); + v[0] = 100; + v[1] = 101; + v[2] = 102; } /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); + const ecs_vec_t *vec2 = ecs_get_id(world, e, vector_of_ints); { - const ecs_vec_t *vec = ecs_get_id(world, e, vector_of_ints); - { - test_int(3, ecs_vec_count(vec)); - const ecs_i32_t *v = ecs_vec_first(vec); - test_assert(v != NULL); - test_int(100, v[0]); - test_int(101, v[1]); - test_int(102, v[2]); - } + test_int(3, ecs_vec_count(vec2)); + const ecs_i32_t *v = ecs_vec_first(vec2); + test_assert(v != NULL); + test_int(100, v[0]); + test_int(101, v[1]); + test_int(102, v[2]); } + test_assert(cmp(world, vector_of_ints, vec1, vec2) == 0); + /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); + const ecs_vec_t *vec3 = ecs_get_id(world, e, vector_of_ints); { - const ecs_vec_t *vec = ecs_get_id(world, e, vector_of_ints); - { - test_int(3, ecs_vec_count(vec)); - const ecs_i32_t *v = ecs_vec_first(vec); - test_assert(v != NULL); - test_int(100, v[0]); - test_int(101, v[1]); - test_int(102, v[2]); - } + test_int(3, ecs_vec_count(vec3)); + const ecs_i32_t *v = ecs_vec_first(vec3); + test_assert(v != NULL); + test_int(100, v[0]); + test_int(101, v[1]); + test_int(102, v[2]); } + test_assert(cmp(world, vector_of_ints, vec1, vec3) == 0); + /* Test deleting: */ ecs_delete(world, e); ecs_delete(world, instance); @@ -3028,49 +3106,47 @@ void RuntimeTypes_vector_of_strings(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); + ecs_vec_t *vec1 = ecs_ensure_id(world, e, vector_of_strings); { - ecs_vec_t *vec = ecs_ensure_id(world, e, vector_of_strings); - { - test_int(0, ecs_vec_count(vec)); - ecs_vec_set_count(NULL, vec, sizeof(ecs_string_t), 3); - ecs_string_t *v = ecs_vec_first(vec); - invoke_type_ctor(world, v, 3, ecs_id(ecs_string_t)); - test_memory_zero(v, sizeof(ecs_string_t) * 3); - v[0] = ecs_os_strdup("String100"); - v[1] = ecs_os_strdup("String101"); - v[2] = ecs_os_strdup("String102"); - } + test_int(0, ecs_vec_count(vec1)); + ecs_vec_set_count(NULL, vec1, sizeof(ecs_string_t), 3); + ecs_string_t *v = ecs_vec_first(vec1); + invoke_type_ctor(world, v, 3, ecs_id(ecs_string_t)); + test_memory_zero(v, sizeof(ecs_string_t) * 3); + v[0] = ecs_os_strdup("String100"); + v[1] = ecs_os_strdup("String101"); + v[2] = ecs_os_strdup("String102"); } /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); + const ecs_vec_t *vec2 = ecs_get_id(world, e, vector_of_strings); { - const ecs_vec_t *vec = ecs_get_id(world, e, vector_of_strings); - { - test_int(3, ecs_vec_count(vec)); - const ecs_string_t *v = ecs_vec_first(vec); - test_assert(v != NULL); - test_str("String100", v[0]); - test_str("String101", v[1]); - test_str("String102", v[2]); - } + test_int(3, ecs_vec_count(vec2)); + const ecs_string_t *v = ecs_vec_first(vec2); + test_assert(v != NULL); + test_str("String100", v[0]); + test_str("String101", v[1]); + test_str("String102", v[2]); } + test_assert(cmp(world, vector_of_strings, vec1, vec2) == 0); + /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); + const ecs_vec_t *vec3 = ecs_get_id(world, e, vector_of_strings); { - const ecs_vec_t *vec = ecs_get_id(world, e, vector_of_strings); - { - test_int(3, ecs_vec_count(vec)); - const ecs_string_t *v = ecs_vec_first(vec); - test_assert(v != NULL); - test_str("String100", v[0]); - test_str("String101", v[1]); - test_str("String102", v[2]); - } + test_int(3, ecs_vec_count(vec3)); + const ecs_string_t *v = ecs_vec_first(vec3); + test_assert(v != NULL); + test_str("String100", v[0]); + test_str("String101", v[1]); + test_str("String102", v[2]); } + test_assert(cmp(world, vector_of_strings, vec1, vec3) == 0); + /* Test deleting: */ ecs_delete(world, e); ecs_delete(world, instance); @@ -3098,11 +3174,11 @@ void RuntimeTypes_vector_of_struct_with_ints(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); + ecs_vec_t *vec1 = ecs_ensure_id(world, e, vector_of_struct_with_ints); { - ecs_vec_t *vec = ecs_ensure_id(world, e, vector_of_struct_with_ints); - test_int(0, ecs_vec_count(vec)); - ecs_vec_set_count(NULL, vec, sizeof(StructWithInts), 3); - StructWithInts *v = ecs_vec_first(vec); + test_int(0, ecs_vec_count(vec1)); + ecs_vec_set_count(NULL, vec1, sizeof(StructWithInts), 3); + StructWithInts *v = ecs_vec_first(vec1); invoke_type_ctor(world, v, 3, struct_with_ints); test_memory_zero(v, sizeof(StructWithInts) * 3); v[0].a = 100; @@ -3115,10 +3191,10 @@ void RuntimeTypes_vector_of_struct_with_ints(void) { /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); + const ecs_vec_t *vec2 = ecs_get_id(world, e, vector_of_struct_with_ints); { - const ecs_vec_t *vec = ecs_get_id(world, e, vector_of_struct_with_ints); - test_int(3, ecs_vec_count(vec)); - const StructWithInts *v = ecs_vec_first(vec); + test_int(3, ecs_vec_count(vec2)); + const StructWithInts *v = ecs_vec_first(vec2); test_assert(v != NULL); test_int(100, v[0].a); test_int(101, v[0].b); @@ -3127,14 +3203,16 @@ void RuntimeTypes_vector_of_struct_with_ints(void) { test_int(104, v[2].a); test_int(105, v[2].b); } + test_assert(cmp(world, vector_of_struct_with_ints, vec1, vec2) == 0); + /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); + const ecs_vec_t *vec3 = ecs_get_id(world, e, vector_of_struct_with_ints); { - const ecs_vec_t *vec = ecs_get_id(world, e, vector_of_struct_with_ints); - test_int(3, ecs_vec_count(vec)); - const StructWithInts *v = ecs_vec_first(vec); + test_int(3, ecs_vec_count(vec3)); + const StructWithInts *v = ecs_vec_first(vec2); test_assert(v != NULL); test_int(100, v[0].a); test_int(101, v[0].b); @@ -3143,6 +3221,7 @@ void RuntimeTypes_vector_of_struct_with_ints(void) { test_int(104, v[2].a); test_int(105, v[2].b); } + test_assert(cmp(world, vector_of_struct_with_ints, vec1, vec3) == 0); /* Test deleting: */ ecs_delete(world, e); @@ -3173,11 +3252,11 @@ void RuntimeTypes_vector_of_struct_with_strings(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); + ecs_vec_t *vec1 = ecs_ensure_id(world, e, vector_of_struct_with_strings); { - ecs_vec_t *vec = ecs_ensure_id(world, e, vector_of_struct_with_strings); - test_int(0, ecs_vec_count(vec)); - ecs_vec_set_count(NULL, vec, sizeof(StructWithStrings), 3); - StructWithStrings *v = ecs_vec_first(vec); + test_int(0, ecs_vec_count(vec1)); + ecs_vec_set_count(NULL, vec1, sizeof(StructWithStrings), 3); + StructWithStrings *v = ecs_vec_first(vec1); invoke_type_ctor(world, v, 3, struct_with_strings); test_memory_zero(v, sizeof(StructWithStrings) * 3); v[0].a = ecs_os_strdup("String100"); @@ -3193,11 +3272,10 @@ void RuntimeTypes_vector_of_struct_with_strings(void) { /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); + const ecs_vec_t *vec2 = ecs_get_id(world, e, vector_of_struct_with_strings); { - const ecs_vec_t *vec = - ecs_get_id(world, e, vector_of_struct_with_strings); - test_int(3, ecs_vec_count(vec)); - const StructWithStrings *v = ecs_vec_first(vec); + test_int(3, ecs_vec_count(vec2)); + const StructWithStrings *v = ecs_vec_first(vec2); test_assert(v != NULL); test_str("String100", v[0].a); test_int(101, v[0].b); @@ -3209,15 +3287,15 @@ void RuntimeTypes_vector_of_struct_with_strings(void) { test_int(107, v[2].b); test_str("String108", v[2].c); } + test_assert(cmp(world, vector_of_struct_with_strings, vec1, vec2) == 0); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); + const ecs_vec_t *vec3 = ecs_get_id(world, e, vector_of_struct_with_strings); { - const ecs_vec_t *vec = - ecs_get_id(world, e, vector_of_struct_with_strings); - test_int(3, ecs_vec_count(vec)); - const StructWithStrings *v = ecs_vec_first(vec); + test_int(3, ecs_vec_count(vec3)); + const StructWithStrings *v = ecs_vec_first(vec3); test_assert(v != NULL); test_str("String100", v[0].a); test_int(101, v[0].b); @@ -3229,6 +3307,7 @@ void RuntimeTypes_vector_of_struct_with_strings(void) { test_int(107, v[2].b); test_str("String108", v[2].c); } + test_assert(cmp(world, vector_of_struct_with_strings, vec1, vec3) == 0); /* Test deleting: */ ecs_delete(world, e); @@ -3248,61 +3327,55 @@ void RuntimeTypes_vector_of_arrays_of_strings(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); + ecs_vec_t *vec1 = ecs_ensure_id(world, e, vector_of_arrays_of_strings); { - ecs_vec_t *vec = ecs_ensure_id(world, e, vector_of_arrays_of_strings); - { - test_int(0, ecs_vec_count(vec)); - ecs_vec_set_count(NULL, vec, sizeof(ecs_string_t[3]), 3); - ecs_string_t(*v)[3] = ecs_vec_first(vec); - invoke_type_ctor(world, v, 3, array_of_strings); - test_memory_zero(v, sizeof(ecs_string_t[3]) * 3); - int i; - for (i = 0; i < 3; i++) { - v[i][0] = ecs_os_strdup("String100"); - v[i][1] = ecs_os_strdup("String101"); - v[i][2] = ecs_os_strdup("String102"); - } + test_int(0, ecs_vec_count(vec1)); + ecs_vec_set_count(NULL, vec1, sizeof(ecs_string_t[3]), 3); + ecs_string_t(*v)[3] = ecs_vec_first(vec1); + invoke_type_ctor(world, v, 3, array_of_strings); + test_memory_zero(v, sizeof(ecs_string_t[3]) * 3); + int i; + for (i = 0; i < 3; i++) { + v[i][0] = ecs_os_strdup("String100"); + v[i][1] = ecs_os_strdup("String101"); + v[i][2] = ecs_os_strdup("String102"); } } /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); + const ecs_vec_t *vec2 = ecs_get_id(world, e, vector_of_arrays_of_strings); { - const ecs_vec_t *vec = - ecs_get_id(world, e, vector_of_arrays_of_strings); - { - test_int(3, ecs_vec_count(vec)); - const ecs_string_t(*v)[3] = - (const ecs_string_t(*)[3]) ecs_vec_first(vec); - test_assert(v != NULL); - int i; - for (i = 0; i < 3; i++) { - test_str("String100", v[i][0]); - test_str("String101", v[i][1]); - test_str("String102", v[i][2]); - } + test_int(3, ecs_vec_count(vec2)); + const ecs_string_t(*v)[3] = + (const ecs_string_t(*)[3]) ecs_vec_first(vec2); + test_assert(v != NULL); + int i; + for (i = 0; i < 3; i++) { + test_str("String100", v[i][0]); + test_str("String101", v[i][1]); + test_str("String102", v[i][2]); } } + test_assert(cmp(world, vector_of_arrays_of_strings, vec1, vec2) == 0); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); + const ecs_vec_t *vec3 = ecs_get_id(world, e, vector_of_arrays_of_strings); { - const ecs_vec_t *vec = - ecs_get_id(world, e, vector_of_arrays_of_strings); - { - test_int(3, ecs_vec_count(vec)); - const ecs_string_t(*v)[3] = - (const ecs_string_t(*)[3]) ecs_vec_first(vec); - test_assert(v != NULL); - int i; - for (i = 0; i < 3; i++) { - test_str("String100", v[i][0]); - test_str("String101", v[i][1]); - test_str("String102", v[i][2]); - } + test_int(3, ecs_vec_count(vec3)); + const ecs_string_t(*v)[3] = + (const ecs_string_t(*)[3]) ecs_vec_first(vec3); + test_assert(v != NULL); + int i; + for (i = 0; i < 3; i++) { + test_str("String100", v[i][0]); + test_str("String101", v[i][1]); + test_str("String102", v[i][2]); } } + test_assert(cmp(world, vector_of_arrays_of_strings, vec1, vec3) == 0); /* Test deleting: */ ecs_delete(world, e); @@ -3325,57 +3398,55 @@ void RuntimeTypes_vector_of_opaque(void) { /* Test constructor: */ ecs_entity_t e = ecs_new(world); - { - ecs_vec_t *vec = ecs_ensure_id(world, e, vector_of_opaque); - { - test_int(0, ecs_vec_count(vec)); - ecs_vec_set_count(NULL, vec, sizeof(ResourceHandle), 3); - ResourceHandle *v = ecs_vec_first(vec); - invoke_type_ctor(world, v, 3, resource_handle_opaque); - test_assert(v[0].id != 0); - test_int(0, v[0].value); - v[0].value = 100; - test_assert(v[1].id != 0); - test_int(0, v[1].value); - v[1].value = 101; - test_assert(v[2].id != 0); - test_int(0, v[2].value); - v[2].value = 102; - } + ecs_vec_t *vec1 = ecs_ensure_id(world, e, vector_of_opaque); + { + test_int(0, ecs_vec_count(vec1)); + ecs_vec_set_count(NULL, vec1, sizeof(ResourceHandle), 3); + ResourceHandle *v = ecs_vec_first(vec1); + invoke_type_ctor(world, v, 3, resource_handle_opaque); + test_assert(v[0].id != 0); + test_int(0, v[0].value); + v[0].value = 100; + test_assert(v[1].id != 0); + test_int(0, v[1].value); + v[1].value = 101; + test_assert(v[2].id != 0); + test_int(0, v[2].value); + v[2].value = 102; } /* 3 resource(s) should have been used */ test_int(3, initial_resources - resources_left()); /* Test copying: */ ecs_entity_t instance = ecs_clone(world, 0, e, true); + const ecs_vec_t *vec2 = ecs_get_id(world, e, vector_of_opaque); { - const ecs_vec_t *vec = ecs_get_id(world, e, vector_of_opaque); - { - test_int(3, ecs_vec_count(vec)); - const ResourceHandle *v = ecs_vec_first(vec); - test_assert(v != NULL); - test_int(100, v[0].value); - test_int(101, v[1].value); - test_int(102, v[2].value); - } + test_int(3, ecs_vec_count(vec2)); + const ResourceHandle *v = ecs_vec_first(vec2); + test_assert(v != NULL); + test_int(100, v[0].value); + test_int(101, v[1].value); + test_int(102, v[2].value); } + test_assert(cmp(world, vector_of_opaque, vec1, vec2) == 0); + /* 6 resource(s) should be in use now */ test_int(6, initial_resources - resources_left()); /* Test moving by forcing an archetype change: */ ECS_TAG(world, MakeMeMove); ecs_add(world, e, MakeMeMove); + const ecs_vec_t *vec3 = ecs_get_id(world, e, vector_of_opaque); { - const ecs_vec_t *vec = ecs_get_id(world, e, vector_of_opaque); - { - test_int(3, ecs_vec_count(vec)); - const ResourceHandle *v = ecs_vec_first(vec); - test_assert(v != NULL); - test_int(100, v[0].value); - test_int(101, v[1].value); - test_int(102, v[2].value); - } + test_int(3, ecs_vec_count(vec3)); + const ResourceHandle *v = ecs_vec_first(vec3); + test_assert(v != NULL); + test_int(100, v[0].value); + test_int(101, v[1].value); + test_int(102, v[2].value); } + test_assert(cmp(world, vector_of_opaque, vec1, vec3) == 0); + /* 6 resource(s) should still be in use after a move */ test_int(6, initial_resources - resources_left()); @@ -3398,3 +3469,4 @@ void RuntimeTypes_vector_of_opaque(void) { free_resource_ids(); } + diff --git a/test/meta/src/main.c b/test/meta/src/main.c index 4dfe2d065..3ab9b483a 100644 --- a/test/meta/src/main.c +++ b/test/meta/src/main.c @@ -104,6 +104,8 @@ void RuntimeTypes_move(void); void RuntimeTypes_move_illegal(void); void RuntimeTypes_copy(void); void RuntimeTypes_copy_illegal(void); +void RuntimeTypes_cmp_illegal(void); +void RuntimeTypes_equals_illegal(void); void RuntimeTypes_trivial_array(void); void RuntimeTypes_array_ctor(void); void RuntimeTypes_array_ctor_illegal(void); @@ -113,8 +115,10 @@ void RuntimeTypes_array_move(void); void RuntimeTypes_array_move_illegal(void); void RuntimeTypes_array_copy(void); void RuntimeTypes_array_copy_illegal(void); +void RuntimeTypes_array_cmp_illegal(void); void RuntimeTypes_vector_lifecycle(void); void RuntimeTypes_vector_lifecycle_trivial_type(void); +void RuntimeTypes_vector_cmp_illegal(void); void RuntimeTypes_opaque(void); void RuntimeTypes_struct_with_ints(void); void RuntimeTypes_struct_with_strings(void); @@ -1033,6 +1037,55 @@ void Misc_unit_from_suspend_defer(void); void Misc_unit_prefix_from_suspend_defer(void); void Misc_quantity_from_suspend_defer(void); +// Testsuite 'PrimitiveCompare' +void PrimitiveCompare_bool(void); +void PrimitiveCompare_char(void); +void PrimitiveCompare_byte(void); +void PrimitiveCompare_u8(void); +void PrimitiveCompare_u16(void); +void PrimitiveCompare_u32(void); +void PrimitiveCompare_u64(void); +void PrimitiveCompare_uptr(void); +void PrimitiveCompare_i8(void); +void PrimitiveCompare_i16(void); +void PrimitiveCompare_i32(void); +void PrimitiveCompare_i64(void); +void PrimitiveCompare_iptr(void); +void PrimitiveCompare_f32(void); +void PrimitiveCompare_f64(void); +void PrimitiveCompare_entity(void); +void PrimitiveCompare_id(void); +void PrimitiveCompare_string(void); +void PrimitiveCompare_const_string(void); + +// Testsuite 'RttCompare' +void RttCompare_struct_with_ints(void); +void RttCompare_struct_with_strings(void); +void RttCompare_struct_with_opaque(void); +void RttCompare_nested_struct_with_strings(void); +void RttCompare_struct_with_array_of_strings(void); +void RttCompare_struct_with_array_of_array_of_strings(void); +void RttCompare_struct_with_vector_of_ints(void); +void RttCompare_struct_with_vector_of_strings(void); +void RttCompare_nested_struct_with_vector_of_ints(void); +void RttCompare_nested_struct_with_vector_of_strings(void); +void RttCompare_array_of_ints(void); +void RttCompare_array_of_strings(void); +void RttCompare_array_of_struct_with_ints(void); +void RttCompare_array_of_struct_with_strings(void); +void RttCompare_array_of_struct_with_opaques(void); +void RttCompare_array_of_array_of_strings(void); +void RttCompare_array_of_array_of_struct_with_strings(void); +void RttCompare_array_of_vectors_of_ints(void); +void RttCompare_array_of_vectors_of_strings(void); +void RttCompare_array_of_opaque(void); +void RttCompare_vector_of_ints(void); +void RttCompare_vector_of_strings(void); +void RttCompare_vector_of_struct_with_ints(void); +void RttCompare_vector_of_struct_with_strings(void); +void RttCompare_vector_of_arrays_of_strings(void); +void RttCompare_vector_of_opaque(void); + bake_test_case PrimitiveTypes_testcases[] = { { "bool", @@ -1399,6 +1452,14 @@ bake_test_case RuntimeTypes_testcases[] = { "copy_illegal", RuntimeTypes_copy_illegal }, + { + "cmp_illegal", + RuntimeTypes_cmp_illegal + }, + { + "equals_illegal", + RuntimeTypes_equals_illegal + }, { "trivial_array", RuntimeTypes_trivial_array @@ -1435,6 +1496,10 @@ bake_test_case RuntimeTypes_testcases[] = { "array_copy_illegal", RuntimeTypes_array_copy_illegal }, + { + "array_cmp_illegal", + RuntimeTypes_array_cmp_illegal + }, { "vector_lifecycle", RuntimeTypes_vector_lifecycle @@ -1443,6 +1508,10 @@ bake_test_case RuntimeTypes_testcases[] = { "vector_lifecycle_trivial_type", RuntimeTypes_vector_lifecycle_trivial_type }, + { + "vector_cmp_illegal", + RuntimeTypes_vector_cmp_illegal + }, { "opaque", RuntimeTypes_opaque @@ -5028,6 +5097,192 @@ bake_test_case Misc_testcases[] = { } }; +bake_test_case PrimitiveCompare_testcases[] = { + { + "bool", + PrimitiveCompare_bool + }, + { + "char", + PrimitiveCompare_char + }, + { + "byte", + PrimitiveCompare_byte + }, + { + "u8", + PrimitiveCompare_u8 + }, + { + "u16", + PrimitiveCompare_u16 + }, + { + "u32", + PrimitiveCompare_u32 + }, + { + "u64", + PrimitiveCompare_u64 + }, + { + "uptr", + PrimitiveCompare_uptr + }, + { + "i8", + PrimitiveCompare_i8 + }, + { + "i16", + PrimitiveCompare_i16 + }, + { + "i32", + PrimitiveCompare_i32 + }, + { + "i64", + PrimitiveCompare_i64 + }, + { + "iptr", + PrimitiveCompare_iptr + }, + { + "f32", + PrimitiveCompare_f32 + }, + { + "f64", + PrimitiveCompare_f64 + }, + { + "entity", + PrimitiveCompare_entity + }, + { + "id", + PrimitiveCompare_id + }, + { + "string", + PrimitiveCompare_string + }, + { + "const_string", + PrimitiveCompare_const_string + } +}; + +bake_test_case RttCompare_testcases[] = { + { + "struct_with_ints", + RttCompare_struct_with_ints + }, + { + "struct_with_strings", + RttCompare_struct_with_strings + }, + { + "struct_with_opaque", + RttCompare_struct_with_opaque + }, + { + "nested_struct_with_strings", + RttCompare_nested_struct_with_strings + }, + { + "struct_with_array_of_strings", + RttCompare_struct_with_array_of_strings + }, + { + "struct_with_array_of_array_of_strings", + RttCompare_struct_with_array_of_array_of_strings + }, + { + "struct_with_vector_of_ints", + RttCompare_struct_with_vector_of_ints + }, + { + "struct_with_vector_of_strings", + RttCompare_struct_with_vector_of_strings + }, + { + "nested_struct_with_vector_of_ints", + RttCompare_nested_struct_with_vector_of_ints + }, + { + "nested_struct_with_vector_of_strings", + RttCompare_nested_struct_with_vector_of_strings + }, + { + "array_of_ints", + RttCompare_array_of_ints + }, + { + "array_of_strings", + RttCompare_array_of_strings + }, + { + "array_of_struct_with_ints", + RttCompare_array_of_struct_with_ints + }, + { + "array_of_struct_with_strings", + RttCompare_array_of_struct_with_strings + }, + { + "array_of_struct_with_opaques", + RttCompare_array_of_struct_with_opaques + }, + { + "array_of_array_of_strings", + RttCompare_array_of_array_of_strings + }, + { + "array_of_array_of_struct_with_strings", + RttCompare_array_of_array_of_struct_with_strings + }, + { + "array_of_vectors_of_ints", + RttCompare_array_of_vectors_of_ints + }, + { + "array_of_vectors_of_strings", + RttCompare_array_of_vectors_of_strings + }, + { + "array_of_opaque", + RttCompare_array_of_opaque + }, + { + "vector_of_ints", + RttCompare_vector_of_ints + }, + { + "vector_of_strings", + RttCompare_vector_of_strings + }, + { + "vector_of_struct_with_ints", + RttCompare_vector_of_struct_with_ints + }, + { + "vector_of_struct_with_strings", + RttCompare_vector_of_struct_with_strings + }, + { + "vector_of_arrays_of_strings", + RttCompare_vector_of_arrays_of_strings + }, + { + "vector_of_opaque", + RttCompare_vector_of_opaque + } +}; + static bake_test_suite suites[] = { { @@ -5055,7 +5310,7 @@ static bake_test_suite suites[] = { "RuntimeTypes", NULL, NULL, - 47, + 51, RuntimeTypes_testcases }, { @@ -5176,9 +5431,23 @@ static bake_test_suite suites[] = { NULL, 40, Misc_testcases + }, + { + "PrimitiveCompare", + NULL, + NULL, + 19, + PrimitiveCompare_testcases + }, + { + "RttCompare", + NULL, + NULL, + 26, + RttCompare_testcases } }; int main(int argc, char *argv[]) { - return bake_test_run("meta", argc, argv, suites, 21); + return bake_test_run("meta", argc, argv, suites, 23); }