From 1705d97620ef14c88a1ddfdc06bc76776a6f5796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Madar=C3=A1sz?= Date: Sun, 17 Jan 2021 16:33:56 +0100 Subject: [PATCH] code: armageddon --- CMakeLists.txt | 2 +- code/apps/server/CMakeLists.txt | 4 + code/apps/server/header/components.h | 24 - code/apps/server/header/components/general.h | 36 + code/apps/server/header/components/net.h | 26 + code/apps/server/source/network.c | 11 +- code/apps/server/source/world/world.c | 9 +- code/vendors/flecs/flecs_meta.c | 3106 ++++++++++++++++++ code/vendors/flecs/flecs_meta.h | 688 ++++ 9 files changed, 3873 insertions(+), 33 deletions(-) delete mode 100644 code/apps/server/header/components.h create mode 100644 code/apps/server/header/components/general.h create mode 100644 code/apps/server/header/components/net.h create mode 100644 code/vendors/flecs/flecs_meta.c create mode 100644 code/vendors/flecs/flecs_meta.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a9ca57..aeb5f2d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,7 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}) include_directories(eco2d-cli code/vendors code/common) include_directories(eco2d-client code/vendors code/common) -include_directories(eco2d-server code/vendors code/common) +include_directories(eco2d-server code/vendors code/vendors/flecs code/common) add_subdirectory(code/apps/client) add_subdirectory(code/apps/server) diff --git a/code/apps/server/CMakeLists.txt b/code/apps/server/CMakeLists.txt index b5ae915..845235d 100644 --- a/code/apps/server/CMakeLists.txt +++ b/code/apps/server/CMakeLists.txt @@ -12,12 +12,16 @@ add_executable(eco2d-server header/world/world.h header/world/blocks.h header/world/blocks_info.h + header/components/general.h + header/components/net.h ../../vendors/cwpack/cwpack.c ../../vendors/cwpack/cwpack.h ../../vendors/flecs/flecs.c ../../vendors/flecs/flecs.h + ../../vendors/flecs/flecs_meta.c + ../../vendors/flecs/flecs_meta.h ) include_directories(eco2d-server header) diff --git a/code/apps/server/header/components.h b/code/apps/server/header/components.h deleted file mode 100644 index e770f77..0000000 --- a/code/apps/server/header/components.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include "flecs/flecs.h" - -typedef struct { - int16_t x, y; -} chunk; - -typedef struct { - float x, y; -} position; - -typedef struct { - uint16_t peer_id; -} netclient; - -ECS_COMPONENT_DECLARE(chunk); -ECS_COMPONENT_DECLARE(position); -ECS_COMPONENT_DECLARE(netclient); - -static inline void components_register(ecs_world_t *ecs) { - ECS_COMPONENT_DEFINE(ecs, chunk); - ECS_COMPONENT_DEFINE(ecs, position); - ECS_COMPONENT_DEFINE(ecs, netclient); -} diff --git a/code/apps/server/header/components/general.h b/code/apps/server/header/components/general.h new file mode 100644 index 0000000..d400224 --- /dev/null +++ b/code/apps/server/header/components/general.h @@ -0,0 +1,36 @@ +#pragma once +#include "flecs/flecs.h" +#include "flecs/flecs_meta.h" + +ECS_STRUCT(Chunk, { + int16_t x; + int16_t y; +}); + +ECS_STRUCT(Position, { + int16_t x; + int16_t y; +}); + +typedef struct { + ECS_DECLARE_COMPONENT(Chunk); + ECS_DECLARE_COMPONENT(Position); +} Common; + +#define CommonImportHandles(handles)\ + ECS_IMPORT_COMPONENT(handles, Chunk);\ + ECS_IMPORT_COMPONENT(handles, Position); + +static inline void CommonImport(ecs_world_t *ecs) { + ECS_MODULE(ecs, Common); + + ecs_set_name_prefix(ecs, "Common"); + + ECS_IMPORT(ecs, FlecsMeta); + + ECS_META(ecs, Chunk); + ECS_META(ecs, Position); + + ECS_EXPORT_COMPONENT(Chunk); + ECS_EXPORT_COMPONENT(Position); +} diff --git a/code/apps/server/header/components/net.h b/code/apps/server/header/components/net.h new file mode 100644 index 0000000..e2b4428 --- /dev/null +++ b/code/apps/server/header/components/net.h @@ -0,0 +1,26 @@ +#pragma once +#include "flecs/flecs.h" +#include "flecs/flecs_meta.h" + +ECS_STRUCT(NetClient, { + uint16_t peer_id; +}); + +typedef struct { + ECS_DECLARE_COMPONENT(NetClient); +} Net; + +#define NetImportHandles(handles)\ + ECS_IMPORT_COMPONENT(handles, NetClient);\ + +static inline void NetImport(ecs_world_t *ecs) { + ECS_MODULE(ecs, Net); + + ecs_set_name_prefix(ecs, "Net"); + + ECS_IMPORT(ecs, FlecsMeta); + + ECS_META(ecs, NetClient); + + ECS_EXPORT_COMPONENT(NetClient); +} diff --git a/code/apps/server/source/network.c b/code/apps/server/source/network.c index ac925d1..f817b50 100644 --- a/code/apps/server/source/network.c +++ b/code/apps/server/source/network.c @@ -9,9 +9,11 @@ #include "system.h" #include "network.h" -#include "components.h" #include "world/world.h" +#include "components/general.h" +#include "components/net.h" + #define NETWORK_UPDATE_DELAY 0.100 #define NETWORK_MAX_CLIENTS 32 @@ -59,6 +61,9 @@ int32_t network_server_stop(void) { } int32_t network_server_tick(void) { + ECS_IMPORT(world_ecs(), Common); + ECS_IMPORT(world_ecs(), Net); + ENetEvent event = {0}; while (enet_host_service(server, &event, 1) > 0) { switch (event.type) { @@ -67,8 +72,8 @@ int32_t network_server_tick(void) { uint16_t peer_id = event.peer->incomingPeerID; ecs_entity_t e = ecs_new(world_ecs(), 0); - ecs_set(world_ecs(), e, position, {0, 0}); - ecs_set(world_ecs(), e, netclient, {peer_id}); + ecs_set(world_ecs(), e, Position, {0, 0}); + ecs_set(world_ecs(), e, NetClient, {peer_id}); event.peer->data = (void*)((uint32_t)e); diff --git a/code/apps/server/source/world/world.c b/code/apps/server/source/world/world.c index fe75dc4..149c3a1 100644 --- a/code/apps/server/source/world/world.c +++ b/code/apps/server/source/world/world.c @@ -1,6 +1,6 @@ #include "zpl.h" #include "librg.h" -#include "components.h" +#include "components/general.h" #include "world/world.h" typedef struct { @@ -69,7 +69,6 @@ int32_t world_init(int32_t seed, uint16_t block_size, uint16_t chunk_size, uint1 } world.ecs = ecs_init(); - components_register(world.ecs); world.tracker = librg_world_create(); @@ -85,17 +84,17 @@ int32_t world_init(int32_t seed, uint16_t block_size, uint16_t chunk_size, uint1 librg_config_chunkamount_set(world.tracker, world_size, world_size, 1); librg_config_chunkoffset_set(world.tracker, LIBRG_OFFSET_MID, LIBRG_OFFSET_MID, LIBRG_OFFSET_MID); + ECS_IMPORT(world.ecs, Common); + for (int i = 0; i < chunk_size * chunk_size; ++i) { ecs_entity_t e = ecs_new(world.ecs, 0); - ecs_set(world.ecs, e, chunk, { + ecs_set(world.ecs, e, Chunk, { .x = i % chunk_size, .y = i / chunk_size, }); librg_entity_track(world.tracker, e); librg_entity_chunk_set(world.tracker, e, i); - - zpl_printf("creating chunk: #%lld %d %d\n", e, i % chunk_size, i / chunk_size); } // librg_event_set(world.tracker, LIBRG_WRITE_UPDATE, world_write_update); diff --git a/code/vendors/flecs/flecs_meta.c b/code/vendors/flecs/flecs_meta.c new file mode 100644 index 0000000..769acf9 --- /dev/null +++ b/code/vendors/flecs/flecs_meta.c @@ -0,0 +1,3106 @@ +#ifndef FLECS_META_IMPL +#include "flecs_meta.h" +#endif +#ifndef FLECS_META_PARSER_H +#define FLECS_META_PARSER_H + + +#define ECS_META_IDENTIFIER_LENGTH (256) + +#define ecs_meta_error(ctx, ptr, ...)\ + ecs_parser_error((ctx)->name, (ctx)->decl, ptr - (ctx)->decl, __VA_ARGS__) + +typedef char ecs_meta_token_t[ECS_META_IDENTIFIER_LENGTH]; + +typedef struct ecs_meta_parse_ctx_t { + const char *name; + const char *decl; +} ecs_meta_parse_ctx_t; + +typedef struct ecs_meta_type_t { + ecs_meta_token_t type; + ecs_meta_token_t params; + bool is_const; + bool is_ptr; +} ecs_meta_type_t; + +typedef struct ecs_meta_member_t { + ecs_meta_type_t type; + ecs_meta_token_t name; + int64_t count; + bool is_partial; +} ecs_meta_member_t; + +typedef struct ecs_meta_constant_t { + ecs_meta_token_t name; + int64_t value; + bool is_value_set; +} ecs_meta_constant_t; + +typedef struct ecs_meta_params_t { + ecs_meta_type_t key_type; + ecs_meta_type_t type; + int64_t count; + bool is_key_value; + bool is_fixed_size; +} ecs_meta_params_t; + +const char* ecs_meta_parse_constant( + const char *ptr, + ecs_meta_constant_t *token_out, + ecs_meta_parse_ctx_t *ctx); + +const char* ecs_meta_parse_member( + const char *ptr, + ecs_meta_member_t *token_out, + ecs_meta_parse_ctx_t *ctx); + +void ecs_meta_parse_params( + const char *ptr, + ecs_meta_params_t *token_out, + ecs_meta_parse_ctx_t *ctx); + +#endif + +static +ecs_vector_t* serialize_type( + ecs_world_t *world, + ecs_entity_t entity, + ecs_vector_t *ops, + int32_t offset, + FlecsMeta *module); + +static +ecs_size_t ecs_get_primitive_size( + ecs_primitive_kind_t kind) +{ + switch(kind) { + case EcsBool: return sizeof(bool); + case EcsChar: return sizeof(char); + case EcsByte: return sizeof(char); + case EcsU8: return sizeof(uint8_t); + case EcsU16: return sizeof(uint16_t); + case EcsU32: return sizeof(uint32_t); + case EcsU64: return sizeof(uint64_t); + case EcsI8: return sizeof(int8_t); + case EcsI16: return sizeof(int16_t); + case EcsI32: return sizeof(int32_t); + case EcsI64: return sizeof(int64_t); + case EcsF32: return sizeof(float); + case EcsF64: return sizeof(double); + case EcsIPtr: return sizeof(intptr_t); + case EcsUPtr: return sizeof(uintptr_t); + case EcsString: return sizeof(char*); + case EcsEntity: return sizeof(ecs_entity_t); + default: + ecs_abort(ECS_INTERNAL_ERROR, NULL); + } +} + +static +int16_t ecs_get_primitive_alignment( + ecs_primitive_kind_t kind) +{ + switch(kind) { + case EcsBool: return ECS_ALIGNOF(bool); + case EcsChar: return ECS_ALIGNOF(char); + case EcsByte: return ECS_ALIGNOF(char); + case EcsU8: return ECS_ALIGNOF(uint8_t); + case EcsU16: return ECS_ALIGNOF(uint16_t); + case EcsU32: return ECS_ALIGNOF(uint32_t); + case EcsU64: return ECS_ALIGNOF(uint64_t); + case EcsI8: return ECS_ALIGNOF(int8_t); + case EcsI16: return ECS_ALIGNOF(int16_t); + case EcsI32: return ECS_ALIGNOF(int32_t); + case EcsI64: return ECS_ALIGNOF(int64_t); + case EcsF32: return ECS_ALIGNOF(float); + case EcsF64: return ECS_ALIGNOF(double); + case EcsIPtr: return ECS_ALIGNOF(intptr_t); + case EcsUPtr: return ECS_ALIGNOF(uintptr_t); + case EcsString: return ECS_ALIGNOF(char*); + case EcsEntity: return ECS_ALIGNOF(ecs_entity_t); + default: + ecs_abort(ECS_INTERNAL_ERROR, NULL); + } +} + +static +ecs_vector_t* serialize_primitive( + ecs_world_t *world, + ecs_entity_t entity, + const EcsPrimitive *type, + ecs_vector_t *ops, + FlecsMeta *module) +{ + (void)world; + (void)entity; + (void)module; + + ecs_type_op_t *op; + if (!ops) { + op = ecs_vector_add(&ops, ecs_type_op_t); + *op = (ecs_type_op_t) { + .kind = EcsOpHeader, + .size = ecs_get_primitive_size(type->kind), + .alignment = ecs_get_primitive_alignment(type->kind) + }; + } + + op = ecs_vector_add(&ops, ecs_type_op_t); + + *op = (ecs_type_op_t) { + .kind = EcsOpPrimitive, + .size = ecs_get_primitive_size(type->kind), + .alignment = ecs_get_primitive_alignment(type->kind), + .count = 1, + .is.primitive = type->kind + }; + + return ops; +} + +static +ecs_vector_t* serialize_enum( + ecs_world_t *world, + ecs_entity_t entity, + const EcsEnum *type, + ecs_vector_t *ops, + FlecsMeta *module) +{ + (void)type; + + FlecsMetaImportHandles(*module); + + ecs_type_op_t *op; + if (!ops) { + op = ecs_vector_add(&ops, ecs_type_op_t); + *op = (ecs_type_op_t) { + .kind = EcsOpHeader, + .size = sizeof(int32_t), + .alignment = ECS_ALIGNOF(int32_t) + }; + } + + op = ecs_vector_add(&ops, ecs_type_op_t); + + ecs_ref_t ref = {0}; + ecs_get_ref(world, &ref, entity, EcsEnum); + + *op = (ecs_type_op_t) { + .kind = EcsOpEnum, + .size = sizeof(int32_t), + .alignment = ECS_ALIGNOF(int32_t), + .count = 1, + .is.constant = ref + }; + + return ops; +} + +static +ecs_vector_t* serialize_bitmask( + ecs_world_t *world, + ecs_entity_t entity, + const EcsBitmask *type, + ecs_vector_t *ops, + FlecsMeta *module) +{ + (void)type; + + FlecsMetaImportHandles(*module); + + ecs_type_op_t *op; + if (!ops) { + op = ecs_vector_add(&ops, ecs_type_op_t); + *op = (ecs_type_op_t) { + .kind = EcsOpHeader, + .size = sizeof(int32_t), + .alignment = ECS_ALIGNOF(int32_t) + }; + } + + op = ecs_vector_add(&ops, ecs_type_op_t); + + ecs_ref_t ref = {0}; + ecs_get_ref(world, &ref, entity, EcsBitmask); + + *op = (ecs_type_op_t) { + .kind = EcsOpBitmask, + .size = sizeof(int32_t), + .alignment = ECS_ALIGNOF(int32_t), + .count = 1, + .is.constant = ref + }; + + return ops; +} + +static +ecs_vector_t* serialize_struct( + ecs_world_t *world, + ecs_entity_t entity, + const EcsStruct *type, + ecs_vector_t *ops, + int32_t offset, + FlecsMeta *module) +{ + FlecsMetaImportHandles(*module); + + ecs_type_op_t *op_header = NULL; + if (!ops) { + op_header = ecs_vector_add(&ops, ecs_type_op_t); + } + + int32_t push_op = ecs_vector_count(ops); + + ecs_type_op_t *op = ecs_vector_add(&ops, ecs_type_op_t); + *op = (ecs_type_op_t) { + .kind = EcsOpPush + }; + + ecs_size_t size = 0; + int16_t alignment = 0; + + EcsMember *members = ecs_vector_first(type->members, EcsMember); + int32_t i, count = ecs_vector_count(type->members); + + for (i = 0; i < count; i ++) { + /* Add type operations of member to struct ops */ + int32_t prev_count = ecs_vector_count(ops); + ops = serialize_type(world, members[i].type, ops, offset + size, module); + +#ifndef NDEBUG + int32_t op_count = ecs_vector_count(ops); + + /* At least one op should be added */ + ecs_assert(prev_count != op_count, ECS_INTERNAL_ERROR, NULL); + ecs_assert(ops != NULL, ECS_INTERNAL_ERROR, NULL); +#endif + + op = ecs_vector_get(ops, ecs_type_op_t, prev_count); + op->name = members[i].name; + + const EcsMetaType *meta_type = ecs_get(world, members[i].type, EcsMetaType); + ecs_size_t member_size = meta_type->size * op->count; + int16_t member_alignment = meta_type->alignment; + + ecs_assert(member_size != 0, ECS_INTERNAL_ERROR, op->name); + ecs_assert(member_alignment != 0, ECS_INTERNAL_ERROR, op->name); + + size = ECS_ALIGN(size, member_alignment); + op->offset = offset + size; + + size += member_size; + + if (member_alignment > alignment) { + alignment = member_alignment; + } + } + + /* Align struct size to struct alignment */ + size = ECS_ALIGN(size, alignment); + + /* Size and alignment are ordinarily determined by ECS_STRUCT and should be + * the same as the values computed here. However, there are two exceptions. + * The first exception is when an application defines a type by populating + * the EcsStruct component directly and does not provide size and alignment + * values for EcsMetaType. The second scenario is when the type definition + * contains an ECS_PRIVATE, in which case the type may contain + * members that are not described. + * + * In the first case the computed values should be set in EcsMetaType. In the + * second case the values from EcsMetaType should be assigned to the type + * operation. */ + bool is_added; + EcsMetaType *base_type = ecs_get_mut(world, entity, EcsMetaType, &is_added); + ecs_assert(base_type != NULL, ECS_INTERNAL_ERROR, NULL); + if (!is_added) { + if (!type->is_partial) { + /* EcsMetaType existed already, and this is not a partial type. This + * means that computed size and alignment should match exactly. */ + if (base_type->size) { + ecs_assert(base_type->size == size, ECS_INTERNAL_ERROR, NULL); + } + + if (base_type->alignment) { + ecs_assert( + base_type->alignment == alignment, ECS_INTERNAL_ERROR, NULL); + } + } else { + /* EcsMetaType exists, and this is a partial type. In this case the + * computed values only apply to the members described in EcsStruct + * but not to the type as a whole. Use the values from EcsMetaType. Note + * that it is not allowed to have a partial type for which no size + * and alignment are specified in EcsMetaType. */ + ecs_assert(base_type->size != 0, ECS_INVALID_PARAMETER, NULL); + ecs_assert(base_type->alignment != 0, ECS_INVALID_PARAMETER, NULL); + + size = base_type->size; + alignment = base_type->alignment; + } + } else { + /* If EcsMetaType was not set yet, initialize descriptor, alias to NULL + * since it won't be used here */ + base_type->descriptor = NULL; + base_type->alias = NULL; + } + + base_type->kind = EcsStructType; + base_type->size = size; + base_type->alignment = alignment; + + op = ecs_vector_add(&ops, ecs_type_op_t); + *op = (ecs_type_op_t) { + .kind = EcsOpPop, + }; + + if (op_header) { + op_header = ecs_vector_first(ops, ecs_type_op_t); + *op_header = (ecs_type_op_t) { + .kind = EcsOpHeader, + .size = size, + .alignment = alignment + }; + } + + ecs_type_op_t *op_push = ecs_vector_get(ops, ecs_type_op_t, push_op); + ecs_assert(op_push->kind == EcsOpPush, ECS_INTERNAL_ERROR, NULL); + op_push->size = size; + op_push->alignment = alignment; + op_push->count = 1; + + return ops; +} + +static +ecs_vector_t* serialize_array( + ecs_world_t *world, + ecs_entity_t entity, + const EcsArray *type, + ecs_vector_t *ops, + FlecsMeta *handles) +{ + (void)entity; + + FlecsMetaImportHandles(*handles); + + ecs_type_op_t *op_header = NULL; + if (!ops) { + op_header = ecs_vector_add(&ops, ecs_type_op_t); + } + + const EcsMetaType *element_type = ecs_get(world, type->element_type, EcsMetaType); + ecs_assert(element_type != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_ref_t ref = {0}; + ecs_get_ref(world, &ref, type->element_type, EcsMetaTypeSerializer); + + ecs_type_op_t *op = ecs_vector_add(&ops, ecs_type_op_t); + *op = (ecs_type_op_t){ + .kind = EcsOpArray, + .count = type->count, + .size = element_type->size, + .alignment = element_type->alignment, + .is.collection = ref + }; + + if (op_header) { + op_header = ecs_vector_first(ops, ecs_type_op_t); + *op_header = (ecs_type_op_t) { + .kind = EcsOpHeader, + .size = op->size, + .alignment = op->alignment + }; + } + + EcsMetaType *meta = ecs_get_mut(world, entity, EcsMetaType, NULL); + + if(!meta->size || !meta->alignment) { + meta->size = type->count * element_type->size; + meta->alignment = element_type->alignment; + } + + return ops; +} + +static +ecs_vector_t* serialize_vector( + ecs_world_t *world, + ecs_entity_t entity, + const EcsVector *type, + ecs_vector_t *ops, + FlecsMeta *handles) +{ + (void)entity; + + FlecsMetaImportHandles(*handles); + + ecs_type_op_t *op = NULL; + if (!ops) { + op = ecs_vector_add(&ops, ecs_type_op_t); + *op = (ecs_type_op_t) { + .kind = EcsOpHeader, + .size = sizeof(ecs_vector_t*), + .alignment = ECS_ALIGNOF(ecs_vector_t*) + }; + } + + const EcsMetaType *element_type = ecs_get(world, type->element_type, EcsMetaType); + ecs_assert(element_type != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_ref_t ref = {0}; + ecs_get_ref(world, &ref, type->element_type, EcsMetaTypeSerializer); + + op = ecs_vector_add(&ops, ecs_type_op_t); + *op = (ecs_type_op_t){ + .kind = EcsOpVector, + .count = 1, + .size = element_type->size, + .alignment = element_type->alignment, + .is.collection = ref + }; + + return ops; +} + +static +ecs_vector_t* serialize_map( + ecs_world_t *world, + ecs_entity_t entity, + const EcsMap *type, + ecs_vector_t *ops, + FlecsMeta *handles) +{ + FlecsMetaImportHandles(*handles); + + ecs_type_op_t *op = NULL; + if (!ops) { + op = ecs_vector_add(&ops, ecs_type_op_t); + *op = (ecs_type_op_t) { + .kind = EcsOpHeader, + .size = sizeof(ecs_map_t*), + .alignment = ECS_ALIGNOF(ecs_map_t*) + }; + } + + const EcsMetaTypeSerializer *key_cache = ecs_get(world, type->key_type, EcsMetaTypeSerializer); + ecs_assert(key_cache != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(key_cache->ops != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(ecs_vector_count(key_cache->ops) != 0, ECS_INTERNAL_ERROR, NULL); + + /* Make sure first op is the header */ + ecs_type_op_t *key_op = ecs_vector_first(key_cache->ops, ecs_type_op_t); + ecs_assert(key_op->kind == EcsOpHeader, ECS_INTERNAL_ERROR, NULL); + + if (ecs_vector_count(key_cache->ops) != 2) { + const EcsMetaType *ptr = ecs_get(world, entity, EcsMetaType); + ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_meta_parse_ctx_t ctx = { + .name = ecs_get_name(world, entity), + .decl = ptr->descriptor + }; + + ecs_meta_error( &ctx, ctx.decl, + "invalid key type '%s' for map", ecs_get_name(world, type->key_type)); + } + + key_op = ecs_vector_get(key_cache->ops, ecs_type_op_t, 1); + ecs_assert(key_op != NULL, ECS_INTERNAL_ERROR, NULL); + + if (key_op->count != 1) { + const EcsMetaType *ptr = ecs_get(world, entity, EcsMetaType); + ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_meta_parse_ctx_t ctx = { + .name = ecs_get_name(world, entity), + .decl = ptr->descriptor + }; + ecs_meta_error( &ctx, ctx.decl, "array type invalid for key type"); + } + + ecs_ref_t key_ref = {0}; + ecs_get_ref(world, &key_ref, type->key_type, EcsMetaTypeSerializer); + + ecs_ref_t element_ref = {0}; + ecs_get_ref(world, &element_ref, type->element_type, EcsMetaTypeSerializer); + + op = ecs_vector_add(&ops, ecs_type_op_t); + *op = (ecs_type_op_t){ + .kind = EcsOpMap, + .count = 1, + .size = sizeof(ecs_map_t*), + .alignment = ECS_ALIGNOF(ecs_map_t*), + .is.map = { + .key = key_ref, + .element = element_ref + } + }; + + return ops; +} + +static +ecs_vector_t* serialize_type( + ecs_world_t *world, + ecs_entity_t entity, + ecs_vector_t *ops, + int32_t offset, + FlecsMeta *module) +{ + FlecsMetaImportHandles(*module); + + const EcsMetaType *type = ecs_get(world, entity, EcsMetaType); + ecs_assert(type != NULL, ECS_INVALID_PARAMETER, NULL); + + switch(type->kind) { + case EcsPrimitiveType: { + const EcsPrimitive *t = ecs_get(world, entity, EcsPrimitive); + ecs_assert(t != NULL, ECS_INTERNAL_ERROR, NULL); + return serialize_primitive(world, entity, t, ops, module); + } + + case EcsEnumType: { + const EcsEnum *t = ecs_get(world, entity, EcsEnum); + ecs_assert(t != NULL, ECS_INTERNAL_ERROR, NULL); + return serialize_enum(world, entity, t, ops, module); + } + + case EcsBitmaskType: { + const EcsBitmask *t = ecs_get(world, entity, EcsBitmask); + ecs_assert(t != NULL, ECS_INTERNAL_ERROR, NULL); + return serialize_bitmask(world, entity, t, ops, module); + } + + case EcsStructType: { + const EcsStruct *t = ecs_get(world, entity, EcsStruct); + ecs_assert(t != NULL, ECS_INTERNAL_ERROR, NULL); + return serialize_struct(world, entity, t, ops, offset, module); + } + + case EcsArrayType: { + const EcsArray *t = ecs_get(world, entity, EcsArray); + ecs_assert(t != NULL, ECS_INTERNAL_ERROR, NULL); + return serialize_array(world, entity, t, ops, module); + } + + case EcsVectorType: { + const EcsVector *t = ecs_get(world, entity, EcsVector); + ecs_assert(t != NULL, ECS_INTERNAL_ERROR, NULL); + return serialize_vector(world, entity, t, ops, module); + } + + case EcsMapType: { + const EcsMap *t = ecs_get(world, entity, EcsMap); + ecs_assert(t != NULL, ECS_INTERNAL_ERROR, NULL); + return serialize_map(world, entity, t, ops, module); + } + + default: + break; + } + + return NULL; +} + +void EcsSetPrimitive(ecs_iter_t *it) { + EcsPrimitive *type = ecs_column(it, EcsPrimitive, 1); + ECS_IMPORT_COLUMN(it, FlecsMeta, 2); + + ecs_world_t *world = it->world; + + int i; + for (i = 0; i < it->count; i ++) { + ecs_entity_t e = it->entities[i]; + + /* Size and alignment for primitive types can only be set after we know + * what kind of primitive type it is. Set values in case they haven't + * been set already */ + bool is_added; + EcsMetaType *base_type = ecs_get_mut(world, e, EcsMetaType, &is_added); + ecs_assert(base_type != NULL, ECS_INTERNAL_ERROR, NULL); + + base_type->size = ecs_get_primitive_size(type[i].kind); + base_type->alignment = ecs_get_primitive_alignment(type[i].kind); + + ecs_set(world, e, EcsMetaTypeSerializer, { + serialize_primitive( + world, e, &type[i], NULL, &ecs_module(FlecsMeta)) + }); + } +} + +void EcsSetEnum(ecs_iter_t *it) { + EcsEnum *type = ecs_column(it, EcsEnum, 1); + ECS_IMPORT_COLUMN(it, FlecsMeta, 2); + + ecs_world_t *world = it->world; + + int i; + for (i = 0; i < it->count; i ++) { + ecs_entity_t e = it->entities[i]; + + ecs_set(it->world, e, EcsMetaTypeSerializer, { + serialize_enum(world, e, &type[i], NULL, &ecs_module(FlecsMeta)) + }); + } +} + +void EcsSetBitmask(ecs_iter_t *it) { + EcsBitmask *type = ecs_column(it, EcsBitmask, 1); + ECS_IMPORT_COLUMN(it, FlecsMeta, 2); + + ecs_world_t *world = it->world; + + int i; + for (i = 0; i < it->count; i ++) { + ecs_entity_t e = it->entities[i]; + ecs_set(it->world, e, EcsMetaTypeSerializer, { + serialize_bitmask(world, e, &type[i], NULL, &ecs_module(FlecsMeta)) + }); + } +} + +void EcsSetStruct(ecs_iter_t *it) { + EcsStruct *type = ecs_column(it, EcsStruct, 1); + ECS_IMPORT_COLUMN(it, FlecsMeta, 2); + + ecs_world_t *world = it->world; + + int i; + for (i = 0; i < it->count; i ++) { + ecs_entity_t e = it->entities[i]; + ecs_set(it->world, e, EcsMetaTypeSerializer, { + serialize_struct(world, e, &type[i], NULL, 0, &ecs_module(FlecsMeta)) + }); + } +} + +void EcsSetArray(ecs_iter_t *it) { + EcsArray *type = ecs_column(it, EcsArray, 1); + ECS_IMPORT_COLUMN(it, FlecsMeta, 2); + + ecs_world_t *world = it->world; + + int i; + for (i = 0; i < it->count; i ++) { + ecs_entity_t e = it->entities[i]; + ecs_set(it->world, e, EcsMetaTypeSerializer, { + serialize_array(world, e, &type[i], NULL, &ecs_module(FlecsMeta)) + }); + } +} + +void EcsSetVector(ecs_iter_t *it) { + EcsVector *type = ecs_column(it, EcsVector, 1); + ECS_IMPORT_COLUMN(it, FlecsMeta, 2); + + ecs_world_t *world = it->world; + + int i; + for (i = 0; i < it->count; i ++) { + ecs_entity_t e = it->entities[i]; + ecs_set(it->world, e, EcsMetaTypeSerializer, { + serialize_vector(world, e, &type[i], NULL, &ecs_module(FlecsMeta)) + }); + } +} + +void EcsSetMap(ecs_iter_t *it) { + EcsMap *type = ecs_column(it, EcsMap, 1); + ECS_IMPORT_COLUMN(it, FlecsMeta, 2); + + ecs_world_t *world = it->world; + + int i; + for (i = 0; i < it->count; i ++) { + ecs_entity_t e = it->entities[i]; + ecs_set(it->world, e, EcsMetaTypeSerializer, { + serialize_map(world, e, &type[i], NULL, &ecs_module(FlecsMeta)) + }); + } +} + +char* ecs_chresc( + char *out, + char in, + char delimiter) +{ + char *bptr = out; + switch(in) { + case '\a': + *bptr++ = '\\'; + *bptr = 'a'; + break; + case '\b': + *bptr++ = '\\'; + *bptr = 'b'; + break; + case '\f': + *bptr++ = '\\'; + *bptr = 'f'; + break; + case '\n': + *bptr++ = '\\'; + *bptr = 'n'; + break; + case '\r': + *bptr++ = '\\'; + *bptr = 'r'; + break; + case '\t': + *bptr++ = '\\'; + *bptr = 't'; + break; + case '\v': + *bptr++ = '\\'; + *bptr = 'v'; + break; + case '\\': + *bptr++ = '\\'; + *bptr = '\\'; + break; + default: + if (in == delimiter) { + *bptr++ = '\\'; + *bptr = delimiter; + } else { + *bptr = in; + } + break; + } + + *(++bptr) = '\0'; + + return bptr; +} + +const char* ecs_chrparse( + const char *in, + char *out) +{ + const char *result = in + 1; + char ch; + + if (in[0] == '\\') { + result ++; + + switch(in[1]) { + case 'a': + ch = '\a'; + break; + case 'b': + ch = '\b'; + break; + case 'f': + ch = '\f'; + break; + case 'n': + ch = '\n'; + break; + case 'r': + ch = '\r'; + break; + case 't': + ch = '\t'; + break; + case 'v': + ch = '\v'; + break; + case '\\': + ch = '\\'; + break; + case '"': + ch = '"'; + break; + case '0': + ch = '\0'; + break; + case ' ': + ch = ' '; + break; + case '$': + ch = '$'; + break; + default: + goto error; + } + } else { + ch = in[0]; + } + + if (out) { + *out = ch; + } + + return result; +error: + return NULL; +} + +ecs_size_t ecs_stresc( + char *out, + ecs_size_t n, + char delimiter, + const char *in) +{ + const char *ptr = in; + char ch, *bptr = out, buff[3]; + ecs_size_t written = 0; + while ((ch = *ptr++)) { + if ((written += (ecs_size_t)(ecs_chresc(buff, ch, delimiter) - buff)) <= n) { + *bptr++ = buff[0]; + if ((ch = buff[1])) { + *bptr = ch; + bptr++; + } + } + } + + if (bptr) { + while (written < n) { + *bptr = '\0'; + bptr++; + written++; + } + } + return written; +} + +/* Simple serializer to turn values into strings. Use this code as a template + * for when implementing a new serializer. */ + +static +int str_ser_type( + ecs_world_t *world, + ecs_vector_t *ser, + const void *base, + ecs_strbuf_t *str); + +static +int str_ser_type_op( + ecs_world_t *world, + ecs_type_op_t *op, + const void *base, + ecs_strbuf_t *str); + +/* Serialize a primitive value */ +static +void str_ser_primitive( + ecs_world_t *world, + ecs_type_op_t *op, + const void *base, + ecs_strbuf_t *str) +{ + const char *bool_str[] = { "false", "true" }; + + switch(op->is.primitive) { + case EcsBool: + ecs_strbuf_appendstr(str, bool_str[(int)*(bool*)base]); + break; + case EcsChar: { + char chbuf[3]; + ecs_chresc(chbuf, *(char*)base, '\''); + + ecs_strbuf_appendstrn(str, "'", 1); + ecs_strbuf_appendstr(str, chbuf); + ecs_strbuf_appendstrn(str, "'", 1); + break; + } + case EcsByte: + ecs_strbuf_append(str, "0x%x", *(uint8_t*)base); + break; + case EcsU8: + ecs_strbuf_append(str, "%u", *(uint8_t*)base); + break; + case EcsU16: + ecs_strbuf_append(str, "%u", *(uint16_t*)base); + break; + case EcsU32: + ecs_strbuf_append(str, "%u", *(uint32_t*)base); + break; + case EcsU64: + ecs_strbuf_append(str, "%llu", *(uint64_t*)base); + break; + case EcsI8: + ecs_strbuf_append(str, "%d", *(int8_t*)base); + break; + case EcsI16: + ecs_strbuf_append(str, "%d", *(int16_t*)base); + break; + case EcsI32: + ecs_strbuf_append(str, "%d", *(int32_t*)base); + break; + case EcsI64: + ecs_strbuf_append(str, "%lld", *(int64_t*)base); + break; + case EcsF32: + ecs_strbuf_append(str, "%f", *(float*)base); + break; + case EcsF64: + ecs_strbuf_append(str, "%f", *(double*)base); + break; + case EcsIPtr: + ecs_strbuf_append(str, "%i", *(intptr_t*)base); + break; + case EcsUPtr: + ecs_strbuf_append(str, "%u", *(uintptr_t*)base); + break; + case EcsString: { + char *value = *(char**)base; + if (value) { + ecs_size_t length = ecs_stresc(NULL, 0, '"', value); + if (length == ecs_os_strlen(value)) { + ecs_strbuf_appendstrn(str, "\"", 1); + ecs_strbuf_appendstr(str, value); + ecs_strbuf_appendstrn(str, "\"", 1); + } else { + char *out = ecs_os_malloc(length + 3); + ecs_stresc(out + 1, length, '"', value); + out[0] = '"'; + out[length + 1] = '"'; + out[length + 2] = '\0'; + ecs_strbuf_appendstr_zerocpy(str, out); + } + } else { + ecs_strbuf_appendstr(str, "nullptr"); + } + break; + } + case EcsEntity: { + ecs_entity_t e = *(ecs_entity_t*)base; + const char *name = ecs_get_name(world, e); + if (name) { + ecs_strbuf_appendstr(str, name); + } else { + ecs_strbuf_append(str, "%u", e); + } + break; + } + } +} + +/* Serialize enumeration */ +static +int str_ser_enum( + ecs_world_t *world, + ecs_type_op_t *op, + const void *base, + ecs_strbuf_t *str) +{ + const EcsEnum *enum_type = ecs_get_ref_w_entity(world, &op->is.constant, 0, 0); + ecs_assert(enum_type != NULL, ECS_INVALID_PARAMETER, NULL); + + int32_t value = *(int32_t*)base; + + /* Enumeration constants are stored in a map that is keyed on the + * enumeration value. */ + char **constant = ecs_map_get(enum_type->constants, char*, value); + if (!constant) { + return -1; + } + + ecs_strbuf_appendstr(str, *constant); + + return 0; +} + +/* Serialize bitmask */ +static +int str_ser_bitmask( + ecs_world_t *world, + ecs_type_op_t *op, + const void *base, + ecs_strbuf_t *str) +{ + const EcsBitmask *bitmask_type = ecs_get_ref_w_entity(world, &op->is.constant, 0, 0); + ecs_assert(bitmask_type != NULL, ECS_INVALID_PARAMETER, NULL); + + uint32_t value = *(uint32_t*)base; + ecs_map_key_t key; + char **constant; + int count = 0; + + ecs_strbuf_list_push(str, "", " | "); + + /* Multiple flags can be set at a given time. Iterate through all the flags + * and append the ones that are set. */ + ecs_map_iter_t it = ecs_map_iter(bitmask_type->constants); + while ((constant = ecs_map_next(&it, char*, &key))) { + if ((value & key) == key) { + ecs_strbuf_list_appendstr(str, *constant); + count ++; + } + } + + if (!count) { + ecs_strbuf_list_appendstr(str, "0"); + } + + ecs_strbuf_list_pop(str, ""); + + return 0; +} + +/* Serialize elements of a contiguous array */ +static +int str_ser_elements( + ecs_world_t *world, + ecs_vector_t *elem_ops, + const void *base, + int32_t elem_count, + int32_t elem_size, + ecs_strbuf_t *str) +{ + ecs_strbuf_list_push(str, "[", ", "); + + const void *ptr = base; + + int i; + for (i = 0; i < elem_count; i ++) { + ecs_strbuf_list_next(str); + if (str_ser_type(world, elem_ops, ptr, str)) { + return -1; + } + ptr = ECS_OFFSET(ptr, elem_size); + } + + ecs_strbuf_list_pop(str, "]"); + + return 0; +} + +/* Serialize array */ +static +int str_ser_array( + ecs_world_t *world, + ecs_type_op_t *op, + const void *base, + ecs_strbuf_t *str) +{ + const EcsMetaTypeSerializer *ser = ecs_get_ref_w_entity(world, &op->is.collection, 0, 0); + ecs_assert(ser != NULL, ECS_INTERNAL_ERROR, NULL); + + return str_ser_elements( + world, ser->ops, base, op->count, op->size, str); +} + +/* Serialize vector */ +static +int str_ser_vector( + ecs_world_t *world, + ecs_type_op_t *op, + const void *base, + ecs_strbuf_t *str) +{ + ecs_vector_t *value = *(ecs_vector_t**)base; + if (!value) { + ecs_strbuf_appendstr(str, "nullptr"); + return 0; + } + + const EcsMetaTypeSerializer *ser = ecs_get_ref_w_entity(world, &op->is.collection, 0, 0); + ecs_assert(ser != NULL, ECS_INTERNAL_ERROR, NULL); + + int32_t count = ecs_vector_count(value); + void *array = ecs_vector_first_t(value, op->size, op->alignment); + ecs_vector_t *elem_ops = ser->ops; + + ecs_type_op_t *elem_op_hdr = (ecs_type_op_t*)ecs_vector_first(elem_ops, ecs_type_op_t); + ecs_assert(elem_op_hdr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(elem_op_hdr->kind == EcsOpHeader, ECS_INTERNAL_ERROR, NULL); + ecs_size_t elem_size = elem_op_hdr->size; + + /* Serialize contiguous buffer of vector */ + return str_ser_elements(world, elem_ops, array, count, elem_size, str); +} + +/* Serialize map */ +static +int str_ser_map( + ecs_world_t *world, + ecs_type_op_t *op, + const void *base, + ecs_strbuf_t *str) +{ + ecs_map_t *value = *(ecs_map_t**)base; + + const EcsMetaTypeSerializer *key_ser = ecs_get_ref_w_entity(world, &op->is.map.key, 0, 0); + ecs_assert(key_ser != NULL, ECS_INTERNAL_ERROR, NULL); + + const EcsMetaTypeSerializer *elem_ser = ecs_get_ref_w_entity(world, &op->is.map.element, 0, 0); + ecs_assert(elem_ser != NULL, ECS_INTERNAL_ERROR, NULL); + + /* 2 instructions, one for the header */ + ecs_assert(ecs_vector_count(key_ser->ops) == 2, ECS_INTERNAL_ERROR, NULL); + + ecs_type_op_t *key_op = ecs_vector_first(key_ser->ops, ecs_type_op_t); + ecs_assert(key_op->kind == EcsOpHeader, ECS_INTERNAL_ERROR, NULL); + key_op = &key_op[1]; + + ecs_map_iter_t it = ecs_map_iter(value); + ecs_map_key_t key; + void *ptr; + + ecs_strbuf_list_push(str, "{", ", "); + + while ((ptr = _ecs_map_next(&it, 0, &key))) { + ecs_strbuf_list_next(str); + if (str_ser_type_op(world, key_op, (void*)&key, str)) { + return -1; + } + + ecs_strbuf_appendstr(str, " = "); + + if (str_ser_type(world, elem_ser->ops, ptr, str)) { + return -1; + } + + key = 0; + } + + ecs_strbuf_list_pop(str, "}"); + + return 0; +} + +/* Forward serialization to the different type kinds */ +static +int str_ser_type_op( + ecs_world_t *world, + ecs_type_op_t *op, + const void *base, + ecs_strbuf_t *str) +{ + switch(op->kind) { + case EcsOpHeader: + case EcsOpPush: + case EcsOpPop: + /* Should not be parsed as single op */ + ecs_abort(ECS_INVALID_PARAMETER, NULL); + break; + case EcsOpPrimitive: + str_ser_primitive(world, op, ECS_OFFSET(base, op->offset), str); + break; + case EcsOpEnum: + if (str_ser_enum(world, op, ECS_OFFSET(base, op->offset), str)) { + return -1; + } + break; + case EcsOpBitmask: + if (str_ser_bitmask(world, op, ECS_OFFSET(base, op->offset), str)) { + return -1; + } + break; + case EcsOpArray: + if (str_ser_array(world, op, ECS_OFFSET(base, op->offset), str)) { + return -1; + } + break; + case EcsOpVector: + if (str_ser_vector(world, op, ECS_OFFSET(base, op->offset), str)) { + return -1; + } + break; + case EcsOpMap: + if (str_ser_map(world, op, ECS_OFFSET(base, op->offset), str)) { + return -1; + } + break; + } + + return 0; +} + +/* Iterate over the type ops of a type */ +static +int str_ser_type( + ecs_world_t *world, + ecs_vector_t *ser, + const void *base, + ecs_strbuf_t *str) +{ + ecs_type_op_t *ops = (ecs_type_op_t*)ecs_vector_first(ser, ecs_type_op_t); + int32_t count = ecs_vector_count(ser); + + for (int i = 0; i < count; i ++) { + ecs_type_op_t *op = &ops[i]; + + if (op->name) { + if (op->kind != EcsOpHeader) + { + ecs_strbuf_list_next(str); + } + + ecs_strbuf_append(str, "%s = ", op->name); + } + + switch(op->kind) { + case EcsOpHeader: + break; + case EcsOpPush: + ecs_strbuf_list_push(str, "{", ", "); + break; + case EcsOpPop: + ecs_strbuf_list_pop(str, "}"); + break; + default: + if (str_ser_type_op(world, op, base, str)) { + goto error; + } + break; + } + } + + return 0; +error: + ecs_strbuf_reset(str); + return -1; +} + +char* ecs_ptr_to_str( + ecs_world_t *world, + ecs_entity_t type, + void* ptr) +{ + ecs_entity_t ecs_entity(EcsMetaTypeSerializer) = ecs_lookup_fullpath(world, "flecs.meta.MetaTypeSerializer"); + const EcsMetaTypeSerializer *ser = ecs_get(world, type, EcsMetaTypeSerializer); + ecs_assert(ser != NULL, ECS_INVALID_PARAMETER, NULL); + + ecs_strbuf_t str = ECS_STRBUF_INIT; + if (str_ser_type(world, ser->ops, ptr, &str)) { + return NULL; + } + + return ecs_strbuf_get(&str); +} + +char* ecs_entity_to_str( + ecs_world_t *world, + ecs_entity_t entity) +{ + ecs_type_t type = ecs_get_type(world, entity); + ecs_entity_t *ids = (ecs_entity_t*)ecs_vector_first(type, ecs_entity_t); + int32_t count = ecs_vector_count(type); + + ecs_entity_t ecs_entity(EcsMetaTypeSerializer) = ecs_lookup_fullpath(world, "flecs.meta.MetaTypeSerializer"); + ecs_strbuf_t str = ECS_STRBUF_INIT; + + const char *name = ecs_get_name(world, entity); + if (name) { + ecs_strbuf_append(&str, "%s: ", name); + } + + ecs_strbuf_appendstr(&str, "{\n"); + + int i, comps_serialized = 0; + for (i = 0; i < count; i ++) { + const EcsMetaTypeSerializer *ser = ecs_get(world, ids[i], EcsMetaTypeSerializer); + if (ser) { + const void *ptr = ecs_get_w_entity(world, entity, ids[i]); + ecs_strbuf_append(&str, " %s: ", ecs_get_name(world, ids[i])); + if (str_ser_type(world, ser->ops, ptr, &str)) { + goto error; + } + + ecs_strbuf_appendstr(&str, "\n"); + comps_serialized ++; + } + } + + ecs_strbuf_appendstr(&str, "}"); + + return ecs_strbuf_get(&str); +error: + ecs_strbuf_reset(&str); + return NULL; +} +#ifndef FLECS_META_SERIALIZER_H +#define FLECS_META_SERIALIZER_H + + +void EcsAddStruct( + ecs_iter_t *it); + +void EcsSetPrimitive( + ecs_iter_t *it); + +void EcsSetEnum( + ecs_iter_t *it); + +void EcsSetBitmask( + ecs_iter_t *it); + +void EcsSetStruct( + ecs_iter_t *it); + +void EcsSetArray( + ecs_iter_t *it); + +void EcsSetVector( + ecs_iter_t *it); + +void EcsSetMap( + ecs_iter_t *it); + +#endif +#ifndef FLECS_META_TYPE_H +#define FLECS_META_TYPE_H + + +ecs_entity_t ecs_meta_lookup_array( + ecs_world_t *world, + ecs_entity_t e, + const char *params_decl, + ecs_meta_parse_ctx_t *ctx); + +ecs_entity_t ecs_meta_lookup_vector( + ecs_world_t *world, + ecs_entity_t e, + const char *params_decl, + ecs_meta_parse_ctx_t *ctx); + +ecs_entity_t ecs_meta_lookup_map( + ecs_world_t *world, + ecs_entity_t e, + const char *params_decl, + ecs_meta_parse_ctx_t *ctx); + +ecs_entity_t ecs_meta_lookup( + ecs_world_t *world, + ecs_meta_type_t *token, + const char *ptr, + int64_t count, + ecs_meta_parse_ctx_t *ctx); + +#endif + +ECS_CTOR(EcsMetaType, ptr, { + ptr->descriptor = NULL; + ptr->alias = NULL; +}) + +ECS_DTOR(EcsMetaType, ptr, { + ecs_os_free((char*)ptr->descriptor); + ptr->descriptor = NULL; + ptr->alias = NULL; +}) + +ECS_COPY(EcsMetaType, dst, src, { + if (dst->descriptor) { + ecs_os_free((char*)dst->descriptor); + dst->descriptor = NULL; + } + + if (src->descriptor) { + dst->descriptor = ecs_os_strdup(src->descriptor); + } else { + dst->descriptor = NULL; + } + + dst->kind = src->kind; + dst->size = src->size; + dst->alignment = src->alignment; + dst->alias = src->alias; +}) + +ECS_MOVE(EcsMetaType, dst, src, { + dst->kind = src->kind; + dst->size = src->size; + dst->alignment = src->alignment; + dst->alias = src->alias; + + src->descriptor = NULL; + src->alias = NULL; +}) + +ECS_CTOR(EcsStruct, ptr, { + ptr->members = NULL; + ptr->is_partial = false; +}) + +ECS_DTOR(EcsStruct, ptr, { + ecs_vector_each(ptr->members, EcsMember, m, { + ecs_os_free(m->name); + }); + ecs_vector_free(ptr->members); +}) + +ECS_CTOR(EcsEnum, ptr, { + ptr->constants = NULL; +}) + +ECS_DTOR(EcsEnum, ptr, { + ecs_map_each(ptr->constants, char*, key, c_ptr, { + ecs_os_free(*c_ptr); + }) + ecs_map_free(ptr->constants); +}) + +ECS_CTOR(EcsBitmask, ptr, { + ptr->constants = NULL; +}) + +ECS_DTOR(EcsBitmask, ptr, { + ecs_map_each(ptr->constants, char*, key, c_ptr, { + ecs_os_free(*c_ptr); + }) + ecs_map_free(ptr->constants); +}) + +ECS_CTOR(EcsMetaTypeSerializer, ptr, { + ptr->ops = NULL; +}) + +ECS_DTOR(EcsMetaTypeSerializer, ptr, { + ecs_vector_free(ptr->ops); +}) + +static +void ecs_set_primitive( + ecs_world_t *world, + ecs_entity_t e, + EcsMetaType *type) +{ + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(e != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(type != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_entity_t ecs_entity(EcsPrimitive) = ecs_lookup_fullpath(world, "flecs.meta.Primitive"); + ecs_assert(ecs_entity(EcsPrimitive) != 0, ECS_INTERNAL_ERROR, NULL); + + const char *descr = type->descriptor; + + if (!strcmp(descr, "bool")) { + ecs_set(world, e, EcsPrimitive, {EcsBool}); + } else + if (!strcmp(descr, "char")) { + ecs_set(world, e, EcsPrimitive, {EcsChar}); + } else + if (!strcmp(descr, "u8")) { + ecs_set(world, e, EcsPrimitive, {EcsU8}); + } else + if (!strcmp(descr, "u8")) { + ecs_set(world, e, EcsPrimitive, {EcsU8}); + } else + if (!strcmp(descr, "u16")) { + ecs_set(world, e, EcsPrimitive, {EcsU16}); + } else + if (!strcmp(descr, "u32")) { + ecs_set(world, e, EcsPrimitive, {EcsU32}); + } else + if (!strcmp(descr, "u64")) { + ecs_set(world, e, EcsPrimitive, {EcsU64}); + } else + if (!strcmp(descr, "i8")) { + ecs_set(world, e, EcsPrimitive, {EcsI8}); + } else + if (!strcmp(descr, "i16")) { + ecs_set(world, e, EcsPrimitive, {EcsI16}); + } else + if (!strcmp(descr, "i32")) { + ecs_set(world, e, EcsPrimitive, {EcsI32}); + } else + if (!strcmp(descr, "f32")) { + ecs_set(world, e, EcsPrimitive, {EcsF32}); + } else + if (!strcmp(descr, "f64")) { + ecs_set(world, e, EcsPrimitive, {EcsF64}); + } else + if (!strcmp(descr, "iptr")) { + ecs_set(world, e, EcsPrimitive, {EcsIPtr}); + } else + if (!strcmp(descr, "uptr")) { + ecs_set(world, e, EcsPrimitive, {EcsUPtr}); + } else + if (!strcmp(descr, "string")) { + ecs_set(world, e, EcsPrimitive, {EcsString}); + } else + if (!strcmp(descr, "entity")) { + ecs_set(world, e, EcsPrimitive, {EcsEntity}); + } +} + +static +void ecs_set_constants( + ecs_world_t *world, + ecs_entity_t e, + ecs_entity_t comp, + bool is_bitmask, + EcsMetaType *type) +{ + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(e != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(type != NULL, ECS_INTERNAL_ERROR, NULL); + + const char *ptr = type->descriptor; + const char *name = ecs_get_name(world, e); + + ecs_meta_parse_ctx_t ctx = { + .name = name, + .decl = ptr + }; + + ecs_map_t *constants = ecs_map_new(char*, 1); + ecs_meta_constant_t token; + int64_t last_value = 0; + + while ((ptr = ecs_meta_parse_constant(ptr, &token, &ctx))) { + if (token.is_value_set) { + last_value = token.value; + } else if (is_bitmask) { + ecs_meta_error(&ctx, ptr, + "bitmask requires explicit value assignment"); + } + + char *constant_name = ecs_os_strdup(token.name); + ecs_map_set(constants, last_value, &constant_name); + + last_value ++; + } + + ecs_set_ptr_w_entity(world, e, comp, sizeof(EcsEnum), &(EcsEnum){ + .constants = constants + }); +} + +static +void ecs_set_bitmask( + ecs_world_t *world, + ecs_entity_t e, + EcsMetaType *type) +{ + ecs_entity_t comp = ecs_lookup_fullpath(world, "flecs.meta.Bitmask"); + ecs_assert(comp != 0, ECS_INTERNAL_ERROR, NULL); + ecs_set_constants(world, e, comp, true, type); +} + +static +void ecs_set_enum( + ecs_world_t *world, + ecs_entity_t e, + EcsMetaType *type) +{ + ecs_entity_t comp = ecs_lookup_fullpath(world, "flecs.meta.Enum"); + ecs_assert(comp != 0, ECS_INTERNAL_ERROR, NULL); + ecs_set_constants(world, e, comp, false, type); +} + +static +void ecs_set_struct( + ecs_world_t *world, + ecs_entity_t e, + EcsMetaType *type) +{ + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(e != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(type != NULL, ECS_INTERNAL_ERROR, NULL); + + const char *ptr = type->descriptor; + const char *name = ecs_get_name(world, e); + bool is_partial = false; + + ecs_meta_parse_ctx_t ctx = { + .name = name, + .decl = ptr + }; + + ecs_vector_t *members = NULL; + ecs_meta_member_t token; + + while ((ptr = ecs_meta_parse_member(ptr, &token, &ctx))) { + EcsMember *m = ecs_vector_add(&members, EcsMember); + m->name = ecs_os_strdup(token.name); + m->type = ecs_meta_lookup(world, &token.type, ptr, token.count, &ctx); + ecs_assert(type != 0, ECS_INTERNAL_ERROR, NULL); + } + + is_partial = token.is_partial; + + ecs_entity_t ecs_entity(EcsStruct) = ecs_lookup_fullpath(world, "flecs.meta.Struct"); + ecs_assert(ecs_entity(EcsStruct) != 0, ECS_INTERNAL_ERROR, NULL); + ecs_set(world, e, EcsStruct, {members, is_partial}); +} + +static +void ecs_set_array( + ecs_world_t *world, + ecs_entity_t e, + EcsMetaType *type) +{ + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(e != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(type != NULL, ECS_INTERNAL_ERROR, NULL); + + const char *ptr = type->descriptor; + const char *name = ecs_get_name(world, e); + + ecs_meta_parse_ctx_t ctx = { + .name = name, + .decl = ptr + }; + + ecs_meta_lookup_array(world, e, type->descriptor, &ctx); +} + +static +void ecs_set_vector( + ecs_world_t *world, + ecs_entity_t e, + EcsMetaType *type) +{ + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(e != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(type != NULL, ECS_INTERNAL_ERROR, NULL); + + const char *ptr = type->descriptor; + const char *name = ecs_get_name(world, e); + + ecs_meta_parse_ctx_t ctx = { + .name = name, + .decl = ptr + }; + + ecs_meta_lookup_vector(world, e, type->descriptor, &ctx); +} + +static +void ecs_set_map( + ecs_world_t *world, + ecs_entity_t e, + EcsMetaType *type) +{ + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(e != 0, ECS_INTERNAL_ERROR, NULL); + ecs_assert(type != NULL, ECS_INTERNAL_ERROR, NULL); + + const char *ptr = type->descriptor; + const char *name = ecs_get_name(world, e); + + ecs_meta_parse_ctx_t ctx = { + .name = name, + .decl = ptr + }; + + ecs_meta_lookup_map(world, e, type->descriptor, &ctx); +} + +static +void EcsSetType(ecs_iter_t *it) { + ECS_COLUMN(it, EcsMetaType, type, 1); + + ecs_world_t *world = it->world; + + int i; + for(i = 0; i < it->count; i ++) { + ecs_entity_t e = it->entities[i]; + + /* If type does not contain a descriptor, application will have to + * manually initialize type specific data */ + if (!type[i].descriptor) { + + /* For some types we can set the size and alignment automatically */ + if (!type[i].size || !type[i].alignment) { + switch(type[i].kind) { + case EcsBitmaskType: + case EcsEnumType: + type[i].size = sizeof(int32_t); + type[i].alignment = ECS_ALIGNOF(int32_t); + break; + case EcsVectorType: + type[i].size = sizeof(ecs_vector_t*); + type[i].alignment = ECS_ALIGNOF(ecs_vector_t*); + break; + case EcsMapType: + type[i].size = sizeof(ecs_map_t*); + type[i].alignment = ECS_ALIGNOF(ecs_map_t*); + break; + default: + break; + } + } + + continue; + } + + switch(type[i].kind) { + case EcsPrimitiveType: + ecs_set_primitive(world, e, type); + break; + case EcsBitmaskType: + ecs_set_bitmask(world, e, type); + break; + case EcsEnumType: + ecs_set_enum(world, e, type); + break; + case EcsStructType: + ecs_set_struct(world, e, type); + break; + case EcsArrayType: + ecs_set_array(world, e, type); + break; + case EcsVectorType: + ecs_set_vector(world, e, type); + break; + case EcsMapType: + ecs_set_map(world, e, type); + break; + } + } +} + +void ecs_new_meta( + ecs_world_t *world, + ecs_entity_t component, + EcsMetaType *meta_type) +{ + ecs_entity_t ecs_entity(EcsMetaType) = + ecs_lookup_fullpath(world, "flecs.meta.MetaType"); + ecs_assert(ecs_entity(EcsMetaType) != 0, ECS_MODULE_UNDEFINED, "flecs.meta"); + + if (meta_type->alias) { + EcsMetaType *alias = meta_type->alias; + meta_type->kind = alias->kind; + meta_type->descriptor = alias->descriptor; + } + + ecs_set_ptr(world, component, EcsMetaType, meta_type); +} + +/* Utility macro to insert meta data for type with meta descriptor */ +#define ECS_COMPONENT_TYPE(world, type)\ + ecs_set_ptr(world, ecs_entity(type), EcsMetaType, &__##type##__) + +/* Utility macro to insert metadata for primitive type */ +#define ECS_COMPONENT_PRIMITIVE(world, type, kind)\ + ECS_COMPONENT(world, type);\ + ecs_set(world, ecs_entity(type), EcsMetaType, {EcsPrimitiveType, 0, 0, NULL, NULL});\ + ecs_set(world, ecs_entity(type), EcsPrimitive, {kind}) + +void FlecsMetaImport( + ecs_world_t *world) +{ + ECS_MODULE(world, FlecsMeta); + + ecs_set_name_prefix(world, "Ecs"); + + ECS_COMPONENT(world, EcsPrimitive); + ECS_COMPONENT(world, EcsEnum); + ECS_COMPONENT(world, EcsBitmask); + ECS_COMPONENT(world, EcsMember); + ECS_COMPONENT(world, EcsStruct); + ECS_COMPONENT(world, EcsArray); + ECS_COMPONENT(world, EcsVector); + ECS_COMPONENT(world, EcsMap); + ECS_COMPONENT(world, EcsMetaType); + ECS_COMPONENT(world, ecs_type_op_kind_t); + ECS_COMPONENT(world, ecs_type_op_t); + ECS_COMPONENT(world, EcsMetaTypeSerializer); + + ECS_SYSTEM(world, EcsSetType, EcsOnSet, EcsMetaType); + + ecs_set_component_actions(world, EcsMetaType, { + .ctor = ecs_ctor(EcsMetaType), + .dtor = ecs_dtor(EcsMetaType), + .copy = ecs_copy(EcsMetaType), + .move = ecs_move(EcsMetaType) + }); + + ecs_set_component_actions(world, EcsStruct, { + .ctor = ecs_ctor(EcsStruct), + .dtor = ecs_dtor(EcsStruct) + }); + + ecs_set_component_actions(world, EcsEnum, { + .ctor = ecs_ctor(EcsEnum), + .dtor = ecs_dtor(EcsEnum) + }); + + ecs_set_component_actions(world, EcsBitmask, { + .ctor = ecs_ctor(EcsBitmask), + .dtor = ecs_dtor(EcsBitmask) + }); + + ecs_set_component_actions(world, EcsMetaTypeSerializer, { + .ctor = ecs_ctor(EcsMetaTypeSerializer), + .dtor = ecs_dtor(EcsMetaTypeSerializer) + }); + + ECS_SYSTEM(world, EcsSetPrimitive, EcsOnSet, Primitive, flecs.meta:flecs.meta); + ECS_SYSTEM(world, EcsSetEnum, EcsOnSet, Enum, flecs.meta:flecs.meta); + ECS_SYSTEM(world, EcsSetBitmask, EcsOnSet, Bitmask, flecs.meta:flecs.meta); + ECS_SYSTEM(world, EcsSetStruct, EcsOnSet, Struct, flecs.meta:flecs.meta); + ECS_SYSTEM(world, EcsSetArray, EcsOnSet, Array, flecs.meta:flecs.meta); + ECS_SYSTEM(world, EcsSetVector, EcsOnSet, Vector, flecs.meta:flecs.meta); + ECS_SYSTEM(world, EcsSetMap, EcsOnSet, Map, flecs.meta:flecs.meta); + + ECS_EXPORT_COMPONENT(EcsPrimitive); + ECS_EXPORT_COMPONENT(EcsEnum); + ECS_EXPORT_COMPONENT(EcsBitmask); + ECS_EXPORT_COMPONENT(EcsStruct); + ECS_EXPORT_COMPONENT(EcsArray); + ECS_EXPORT_COMPONENT(EcsVector); + ECS_EXPORT_COMPONENT(EcsMap); + ECS_EXPORT_COMPONENT(EcsMetaType); + ECS_EXPORT_COMPONENT(EcsMetaTypeSerializer); + + /* -- Initialize builtin primitive types -- */ + ecs_entity_t old_scope = ecs_set_scope(world, EcsFlecsCore); + ECS_COMPONENT_PRIMITIVE(world, bool, EcsBool); + ECS_COMPONENT_PRIMITIVE(world, char, EcsChar); + ECS_COMPONENT_PRIMITIVE(world, ecs_byte_t, EcsByte); + ECS_COMPONENT_PRIMITIVE(world, uint8_t, EcsU8); + ECS_COMPONENT_PRIMITIVE(world, uint16_t, EcsU16); + ECS_COMPONENT_PRIMITIVE(world, uint32_t, EcsU32); + ECS_COMPONENT_PRIMITIVE(world, uint64_t, EcsU64); + ECS_COMPONENT_PRIMITIVE(world, uintptr_t, EcsUPtr); + ECS_COMPONENT_PRIMITIVE(world, int8_t, EcsI8); + ECS_COMPONENT_PRIMITIVE(world, int16_t, EcsI16); + ECS_COMPONENT_PRIMITIVE(world, int32_t, EcsI32); + ECS_COMPONENT_PRIMITIVE(world, int64_t, EcsI64); + ECS_COMPONENT_PRIMITIVE(world, intptr_t, EcsIPtr); + ECS_COMPONENT_PRIMITIVE(world, size_t, EcsUPtr); + ECS_COMPONENT_PRIMITIVE(world, float, EcsF32); + ECS_COMPONENT_PRIMITIVE(world, double, EcsF64); + ECS_COMPONENT_PRIMITIVE(world, ecs_size_t, EcsI32); + ECS_COMPONENT_PRIMITIVE(world, ecs_string_t, EcsString); + ECS_COMPONENT_PRIMITIVE(world, ecs_entity_t, EcsEntity); + + /* If stdbool is included, the above bool declaration will have been + * registered with the name _Bool. To make sure meta also knows the type by + * its regular name, check and register if necessary */ + if (!ecs_lookup(world, "bool")) { + ecs_entity_t type = ecs_new_component( + world, 0, "bool", sizeof(bool), ECS_ALIGNOF(bool)); + ecs_set(world, type, EcsMetaType, { + EcsPrimitiveType, 0, 0, NULL, NULL}); + ecs_set(world, type, EcsPrimitive, {EcsBool}); + } + + ecs_set_scope(world, old_scope); + + /* -- Initialize builtin meta components -- */ + ecs_set_ptr(world, ecs_set(world, 0, + EcsName, {"ecs_primitive_kind_t", NULL, NULL}), + EcsMetaType, &__ecs_primitive_kind_t__); + + ecs_set(world, ecs_set(world, 0, + EcsName, {"ecs_type_kind_t", NULL, NULL}), + EcsMetaType, { + EcsEnumType, + sizeof(ecs_type_kind_t), + ECS_ALIGNOF(ecs_type_kind_t), + __ecs_type_kind_t__, + NULL + }); + + /* Insert meta definitions for other types */ + ECS_COMPONENT_TYPE(world, EcsPrimitive); + ECS_COMPONENT_TYPE(world, EcsEnum); + ECS_COMPONENT_TYPE(world, EcsBitmask); + ECS_COMPONENT_TYPE(world, EcsMember); + ECS_COMPONENT_TYPE(world, EcsStruct); + ECS_COMPONENT_TYPE(world, EcsArray); + ECS_COMPONENT_TYPE(world, EcsVector); + ECS_COMPONENT_TYPE(world, EcsMap); + ECS_COMPONENT_TYPE(world, EcsMetaType); + ECS_COMPONENT_TYPE(world, ecs_type_op_kind_t); + ECS_COMPONENT_TYPE(world, ecs_type_op_t); + ECS_COMPONENT_TYPE(world, EcsMetaTypeSerializer); + + /* -- Initialize metadata for public Flecs core components -- */ + ecs_set(world, ecs_entity(EcsName), EcsMetaType, { + .kind = EcsStructType, + .size = sizeof(EcsName), + .alignment = ECS_ALIGNOF(EcsName), + .descriptor = "{ecs_string_t value; ECS_PRIVATE; }", + .alias = NULL + }); + + ecs_set(world, ecs_entity(EcsComponent), EcsMetaType, { + .kind = EcsStructType, + .size = sizeof(EcsComponent), + .alignment = ECS_ALIGNOF(EcsComponent), + .descriptor = "{int32_t size; int32_t alignment;}", + .alias = NULL + }); + + ecs_set(world, ecs_entity(EcsSignatureExpr), EcsMetaType, { + .kind = EcsStructType, + .size = sizeof(EcsSignatureExpr), + .alignment = ECS_ALIGNOF(EcsSignatureExpr), + .descriptor = "{const char *expr;}", + .alias = NULL + }); +} + +ecs_entity_t ecs_meta_lookup_array( + ecs_world_t *world, + ecs_entity_t e, + const char *params_decl, + ecs_meta_parse_ctx_t *ctx) +{ + ecs_meta_parse_ctx_t param_ctx = { + .name = ctx->name, + .decl = params_decl + }; + + ecs_meta_params_t params; + ecs_meta_parse_params(params_decl, ¶ms, ¶m_ctx); + if (!params.is_fixed_size) { + ecs_meta_error(ctx, params_decl, "missing size for array"); + } + + if (!params.count) { + ecs_meta_error(ctx, params_decl, "invalid array size"); + } + + ecs_entity_t element_type = ecs_lookup_symbol(world, params.type.type); + if (!element_type) { + ecs_meta_error(ctx, params_decl, "unknown element type '%s'", + params.type.type); + } + + if (!e) { + ecs_entity_t ecs_entity(EcsMetaType) = ecs_lookup_fullpath(world, "flecs.meta.MetaType"); + ecs_assert(ecs_entity(EcsMetaType) != 0, ECS_INTERNAL_ERROR, NULL); + + const EcsMetaType *elem_type = ecs_get(world, element_type, EcsMetaType); + ecs_assert(elem_type != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_assert(elem_type->size * params.count <= INT32_MAX, + ECS_INVALID_PARAMETER, NULL); + + e = ecs_set(world, 0, EcsMetaType, { + EcsArrayType, (int32_t)(elem_type->size * params.count), + elem_type->alignment, NULL, NULL + }); + } + + ecs_entity_t ecs_entity(EcsArray) = ecs_lookup_fullpath(world, "flecs.meta.Array"); + ecs_assert(ecs_entity(EcsArray) != 0, ECS_INTERNAL_ERROR, NULL); + + ecs_assert(params.count <= INT32_MAX, ECS_INVALID_PARAMETER, NULL); + + return ecs_set(world, e, EcsArray, { element_type, (int32_t)params.count }); +} + +ecs_entity_t ecs_meta_lookup_vector( + ecs_world_t *world, + ecs_entity_t e, + const char *params_decl, + ecs_meta_parse_ctx_t *ctx) +{ + ecs_meta_parse_ctx_t param_ctx = { + .name = ctx->name, + .decl = params_decl + }; + + ecs_meta_params_t params; + ecs_meta_parse_params(params_decl, ¶ms, ¶m_ctx); + if (params.is_key_value) { + ecs_meta_error(ctx, params_decl, + "unexpected key value parameters for vector"); + } + + ecs_entity_t element_type = ecs_meta_lookup( + world, ¶ms.type, params_decl, 1, ¶m_ctx); + + if (!e) { + ecs_entity_t ecs_entity(EcsMetaType) = ecs_lookup_fullpath(world, "flecs.meta.MetaType"); + ecs_assert(ecs_entity(EcsMetaType) != 0, ECS_INTERNAL_ERROR, NULL); + + e = ecs_set(world, 0, EcsMetaType, {EcsVectorType, 0, 0, NULL, NULL}); + } + + ecs_entity_t ecs_entity(EcsVector) = ecs_lookup_fullpath(world, "flecs.meta.Vector"); + ecs_assert(ecs_entity(EcsVector) != 0, ECS_INTERNAL_ERROR, NULL); + + return ecs_set(world, e, EcsVector, { element_type }); +} + +ecs_entity_t ecs_meta_lookup_map( + ecs_world_t *world, + ecs_entity_t e, + const char *params_decl, + ecs_meta_parse_ctx_t *ctx) +{ + ecs_meta_parse_ctx_t param_ctx = { + .name = ctx->name, + .decl = params_decl + }; + + ecs_meta_params_t params; + ecs_meta_parse_params(params_decl, ¶ms, ¶m_ctx); + if (!params.is_key_value) { + ecs_meta_error(ctx, params_decl, + "missing key type for map"); + } + + ecs_entity_t key_type = ecs_meta_lookup( + world, ¶ms.key_type, params_decl, 1, ¶m_ctx); + + ecs_entity_t element_type = ecs_meta_lookup( + world, ¶ms.type, params_decl, 1, ¶m_ctx); + + if (!e) { + ecs_entity_t ecs_entity(EcsMetaType) = ecs_lookup_fullpath(world, "flecs.meta.MetaType"); + ecs_assert(ecs_entity(EcsMetaType) != 0, ECS_INTERNAL_ERROR, NULL); + + e = ecs_set(world, 0, EcsMetaType, {EcsMapType, 0, 0, NULL, NULL}); + } + + ecs_entity_t ecs_entity(EcsMap) = ecs_lookup_fullpath(world, "flecs.meta.Map"); + ecs_assert(ecs_entity(EcsMap) != 0, ECS_INTERNAL_ERROR, NULL); + + return ecs_set(world, e, EcsMap, { key_type, element_type }); +} + +ecs_entity_t ecs_meta_lookup_bitmask( + ecs_world_t *world, + ecs_entity_t e, + const char *params_decl, + ecs_meta_parse_ctx_t *ctx) +{ + (void)e; + + ecs_meta_parse_ctx_t param_ctx = { + .name = ctx->name, + .decl = params_decl + }; + + ecs_meta_params_t params; + ecs_meta_parse_params(params_decl, ¶ms, ¶m_ctx); + if (params.is_key_value) { + ecs_meta_error(ctx, params_decl, + "unexpected key value parameters for bitmask"); + } + + if (params.is_fixed_size) { + ecs_meta_error(ctx, params_decl, + "unexpected size for bitmask"); + } + + ecs_entity_t bitmask_type = ecs_meta_lookup( + world, ¶ms.type, params_decl, 1, ¶m_ctx); + ecs_assert(bitmask_type != 0, ECS_INVALID_PARAMETER, NULL); + +#ifndef NDEBUG + /* Make sure this is a bitmask type */ + ecs_entity_t ecs_entity(EcsMetaType) = ecs_lookup_fullpath(world, "flecs.meta.MetaType"); + ecs_assert(ecs_entity(EcsMetaType) != 0, ECS_INTERNAL_ERROR, NULL); + + const EcsMetaType *type_ptr = ecs_get(world, bitmask_type, EcsMetaType); + ecs_assert(type_ptr != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(type_ptr->kind == EcsBitmaskType, ECS_INVALID_PARAMETER, NULL); +#endif + + return bitmask_type; +} + +ecs_entity_t ecs_meta_lookup( + ecs_world_t *world, + ecs_meta_type_t *token, + const char *ptr, + int64_t count, + ecs_meta_parse_ctx_t *ctx) +{ + ecs_assert(world != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(token != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(ctx != NULL, ECS_INTERNAL_ERROR, NULL); + + const char *typename = token->type; + ecs_entity_t type = 0; + + /* Parse vector type */ + if (!strcmp(typename, "ecs_array")) { + type = ecs_meta_lookup_array(world, 0, token->params, ctx); + + } else if (!strcmp(typename, "ecs_vector") || !strcmp(typename, "flecs::vector")) { + type = ecs_meta_lookup_vector(world, 0, token->params, ctx); + + } else if (!strcmp(typename, "ecs_map") | !strcmp(typename, "flecs::map")) { + type = ecs_meta_lookup_map(world, 0, token->params, ctx); + + } else if (!strcmp(typename, "flecs::bitmask")) { + type = ecs_meta_lookup_bitmask(world, 0, token->params, ctx); + + } else if (!strcmp(typename, "flecs::byte")) { + type = ecs_lookup(world, "ecs_byte_t"); + + } else { + if (token->is_ptr && !strcmp(typename, "char")) { + typename = "ecs_string_t"; + } else + if (token->is_ptr) { + typename = "uintptr_t"; + } else + if (!strcmp(typename, "char*") || !strcmp(typename, "flecs::string")) { + typename = "ecs_string_t"; + } + + type = ecs_lookup_symbol(world, typename); + if (!type) { + ecs_meta_error(ctx, ptr, "unknown type '%s'", typename); + return 0; + } + } + + if (count != 1) { + /* If count is not 1, insert array type. First lookup EcsMetaType of the + * element type to get the size and alignment. Then create a new + * entity for the array type, and assign it to the member type. */ + ecs_entity_t ecs_entity(EcsMetaType) = ecs_lookup_fullpath(world, "flecs.meta.MetaType"); + ecs_entity_t ecs_entity(EcsArray) = ecs_lookup_fullpath(world, "flecs.meta.Array"); + const EcsMetaType *type_ptr = ecs_get(world, type, EcsMetaType); + + ecs_assert(count <= INT32_MAX, ECS_INVALID_PARAMETER, NULL); + + type = ecs_set(world, ecs_set(world, 0, + EcsMetaType, {EcsArrayType, type_ptr->size, type_ptr->alignment, NULL, NULL}), + EcsArray, {type, (int32_t)count}); + } + + return type; +} + +static +ecs_meta_scope_t* get_scope( + ecs_meta_cursor_t *cursor) +{ + ecs_assert(cursor != NULL, ECS_INVALID_PARAMETER, NULL); + return &cursor->scope[cursor->depth]; +} + +static +ecs_type_op_t* get_op( + ecs_meta_scope_t *scope) +{ + ecs_type_op_t *ops = ecs_vector_first(scope->ops, ecs_type_op_t); + ecs_assert(ops != NULL, ECS_INVALID_PARAMETER, NULL); + return &ops[scope->cur_op]; +} + +static +ecs_type_op_t* get_ptr( + ecs_meta_scope_t *scope) +{ + ecs_type_op_t *op = get_op(scope); + + if (scope->vector) { + _ecs_vector_set_min_count(&scope->vector, ECS_VECTOR_U(op->size, op->alignment), scope->cur_elem + 1); + scope->base = ecs_vector_first_t(scope->vector, op->size, op->alignment); + } + + return ECS_OFFSET(scope->base, op->offset + op->size * scope->cur_elem); +} + +ecs_meta_cursor_t ecs_meta_cursor( + ecs_world_t *world, + ecs_entity_t type, + void *base) +{ + ecs_assert(world != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(type != 0, ECS_INVALID_PARAMETER, NULL); + ecs_assert(base != NULL, ECS_INVALID_PARAMETER, NULL); + + ecs_meta_cursor_t result; + ecs_entity_t ecs_entity(EcsMetaTypeSerializer) = + ecs_lookup_fullpath(world, "flecs.meta.MetaTypeSerializer"); + ecs_assert(ecs_entity(EcsMetaTypeSerializer) != 0, ECS_INVALID_PARAMETER, NULL); + + const EcsMetaTypeSerializer *ser = ecs_get(world, type, EcsMetaTypeSerializer); + ecs_assert(ser != NULL, ECS_INVALID_PARAMETER, NULL); + +#ifndef NDEBUG + ecs_type_op_t *ops = ecs_vector_first(ser->ops, ecs_type_op_t); + ecs_assert(ops != NULL, ECS_INVALID_PARAMETER, NULL); + ecs_assert(ops[0].kind == EcsOpHeader, ECS_INVALID_PARAMETER, NULL); +#endif + + result.world = world; + result.depth = 0; + result.scope[0].type = type; + result.scope[0].ops = ser->ops; + result.scope[0].start = 1; + result.scope[0].cur_op = 1; + result.scope[0].cur_elem = 0; + result.scope[0].base = base; + result.scope[0].is_collection = false; + result.scope[0].count = 0; + result.scope[0].vector = NULL; + + return result; +} + +void* ecs_meta_get_ptr( + ecs_meta_cursor_t *cursor) +{ + return get_ptr(cursor->scope); +} + +int ecs_meta_next( + ecs_meta_cursor_t *cursor) +{ + ecs_meta_scope_t *scope = get_scope(cursor); + int32_t ops_count = ecs_vector_count(scope->ops); + + if (scope->count) { + if (scope->cur_op >= scope->count) { + return -1; + } + } else { + if (scope->cur_op >= ops_count) { + return -1; + } + } + + if (scope->is_collection) { + scope->cur_op = 1; + scope->cur_elem ++; + + if (scope->count) { + if (scope->cur_elem >= scope->count) { + return -1; + } + } + } else { + scope->cur_op ++; + } + + return 0; +} + +int ecs_meta_move( + ecs_meta_cursor_t *cursor, + int32_t pos) +{ + ecs_meta_scope_t *scope = get_scope(cursor); + int32_t ops_count = ecs_vector_count(scope->ops); + + if(pos < 0) return -1; + + if (scope->is_collection) { + if (scope->count) { + if (pos >= scope->count) { + return -1; + } + + scope->cur_op = 1; + scope->cur_elem = pos; + } + } else { + if (pos >= ops_count) { + return -1; + } + scope->cur_op = scope->start + pos; + } + + return 0; +} + +int ecs_meta_move_name( + ecs_meta_cursor_t *cursor, + const char *name) +{ + ecs_meta_scope_t *scope = get_scope(cursor); + ecs_type_op_t *ops = ecs_vector_first(scope->ops, ecs_type_op_t); + int32_t i, ops_count = ecs_vector_count(scope->ops); + int32_t depth = 1; + + for (i = scope->start; i < ops_count; i ++) { + ecs_type_op_t *op = &ops[i]; + + if (depth <= 1) { + if (op->name && !strcmp(op->name, name)) { + scope->cur_op = i; + return 0; + } + } + + if (op->kind == EcsOpPush) { + depth ++; + } + + if (op->kind == EcsOpPop) { + depth --; + if (depth < 0) { + return -1; + } + } + } + + return -1; +} + +int ecs_meta_push( + ecs_meta_cursor_t *cursor) +{ + ecs_meta_scope_t *scope = get_scope(cursor); + ecs_type_op_t *op = get_op(scope); + + if (scope->vector) { + /* This makes sure the vector has enough space for the pushed element */ + get_ptr(scope); + } + + scope->cur_op ++; + cursor->depth ++; + ecs_meta_scope_t *child_scope = get_scope(cursor); + child_scope->cur_elem = 0; + + switch(op->kind) { + case EcsOpPush: { + child_scope->base = ECS_OFFSET(scope->base, op->size * scope->cur_elem); + child_scope->start = scope->cur_op; + child_scope->cur_op = scope->cur_op; + child_scope->ops = scope->ops; + child_scope->is_collection = false; + child_scope->count = 0; + child_scope->vector = NULL; + break; + } + case EcsOpArray: + case EcsOpVector: { + void *ptr = ECS_OFFSET(scope->base, op->offset); + const EcsMetaTypeSerializer *ser = ecs_get_ref_w_entity(cursor->world, + &op->is.collection, 0, 0); + ecs_assert(ser != NULL, ECS_INTERNAL_ERROR, NULL); + + ecs_vector_t *ops = ser->ops; + + if (op->kind == EcsOpArray) { + child_scope->base = ptr; + child_scope->count = op->count; + child_scope->vector = NULL; + } else { + ecs_vector_t *v = *(ecs_vector_t**)ptr; + if (!v) { + v = ecs_vector_new_t(op->size, op->alignment, 2); + } else { + ecs_vector_set_count_t(&v, op->size, op->alignment, 0); + } + + child_scope->base = ecs_vector_first_t(v, op->size, op->alignment); + child_scope->count = 0; + child_scope->vector = v; + } + child_scope->start = 1; + child_scope->cur_op = 1; + child_scope->ops = ops; + child_scope->is_collection = true; +#ifndef NDEBUG + ecs_type_op_t *hdr = ecs_vector_first(child_scope->ops, ecs_type_op_t); + ecs_assert(hdr->kind == EcsOpHeader, ECS_INTERNAL_ERROR, NULL); +#endif + } + break; + default: + return -1; + } + + return 0; +} + +int ecs_meta_pop( + ecs_meta_cursor_t *cursor) +{ + ecs_meta_scope_t *scope = get_scope(cursor); + ecs_type_op_t *ops = ecs_vector_first(scope->ops, ecs_type_op_t); + int32_t i, ops_count = ecs_vector_count(scope->ops); + + if (scope->is_collection) { + cursor->depth --; + if (scope->vector) { + /* Vector ptr may have changed, so reassign vector field */ + ecs_meta_scope_t *parent_scope = get_scope(cursor); + parent_scope->cur_op --; + void *ptr = get_ptr(parent_scope); + *(ecs_vector_t**)ptr = scope->vector; + parent_scope->cur_op ++; + } + return 0; + } else { + for (i = scope->cur_op; i < ops_count; i ++) { + ecs_type_op_t *op = &ops[i]; + if (op->kind == EcsOpPop) { + cursor->depth -- ; + ecs_meta_scope_t *parent_scope = get_scope(cursor); + if (parent_scope->is_collection) { + parent_scope->cur_op = 1; + parent_scope->cur_elem ++; + } else { + parent_scope->cur_op = i; + } + return 0; + } + } + } + + return -1; +} + +int ecs_meta_set_bool( + ecs_meta_cursor_t *cursor, + bool value) +{ + ecs_meta_scope_t *scope = get_scope(cursor); + ecs_type_op_t *op = get_op(scope); + + if (op->kind != EcsOpPrimitive || op->is.primitive != EcsBool) { + return -1; + } else { + void *ptr = get_ptr(scope); + *(bool*)ptr = value; + return 0; + } +} + +int ecs_meta_set_char( + ecs_meta_cursor_t *cursor, + char value) +{ + ecs_meta_scope_t *scope = get_scope(cursor); + ecs_type_op_t *op = get_op(scope); + + if (op->kind != EcsOpPrimitive || op->is.primitive != EcsChar) { + return -1; + } else { + void *ptr = get_ptr(scope); + *(char*)ptr = value; + return 0; + } +} + +int ecs_meta_set_int( + ecs_meta_cursor_t *cursor, + int64_t value) +{ + ecs_meta_scope_t *scope = get_scope(cursor); + ecs_type_op_t *op = get_op(scope); + + ecs_primitive_kind_t primitive = op->is.primitive; + + if (op->kind != EcsOpPrimitive) { + if (op->kind == EcsOpEnum || op->kind == EcsOpBitmask) { + primitive = EcsI32; + } else { + return -1; + } + } + + void *ptr = get_ptr(scope); + + switch(primitive) { + case EcsBool: + if (value > 1 || value < 0) { + return -1; + } + *(bool*)ptr = (bool)value; + break; + case EcsI8: + case EcsChar: + if (value > INT8_MAX || value < INT8_MIN) { + return -1; + } + *(int8_t*)ptr = (int8_t)value; + break; + case EcsU8: + case EcsByte: + if (value > UINT8_MAX || value < INT8_MIN) { + return -1; + } + *(uint8_t*)ptr = (uint8_t)value; + break; + case EcsI16: + if (value > INT16_MAX || value < INT16_MIN) { + return -1; + } + *(int16_t*)ptr = (int16_t)value; + break; + case EcsU16: + if (value > UINT16_MAX || value < INT16_MIN) { + return -1; + } + *(uint16_t*)ptr = (uint16_t)value; + break; + case EcsI32: + if (value > INT32_MAX || value < INT32_MIN) { + return -1; + } + *(int32_t*)ptr = (int32_t)value; + break; + case EcsU32: + if (value > UINT32_MAX || value < INT32_MIN) { + return -1; + } + *(uint32_t*)ptr = (uint32_t)value; + break; + case EcsI64: + if (value > INT64_MAX) { + return -1; + } + *(int64_t*)ptr = (int64_t)value; + break; + case EcsU64: + *(uint64_t*)ptr = (uint64_t)value; + break; + case EcsEntity: + *(ecs_entity_t*)ptr = (ecs_entity_t)value; + break; + case EcsF32: + if (value > ((1 << 24)-1) || value < -(1 << 24)) { + return -1; + } + *(float*)ptr = (float)value; + break; + case EcsF64: + if (value > ((1LL << 53)-1) || value < -(1LL << 53)) { + return -1; + } + *(double*)ptr = (double)value; + break; + case EcsIPtr: + if (value > INTPTR_MAX) { + return -1; + } + *(intptr_t*)ptr = (intptr_t)value; + break; + case EcsUPtr: + *(uintptr_t*)ptr = (uintptr_t)value; + break; + default: + if(!value) return ecs_meta_set_null(cursor); + return -1; + } + + return 0; +} + +int ecs_meta_set_uint( + ecs_meta_cursor_t *cursor, + uint64_t value) +{ + ecs_meta_scope_t *scope = get_scope(cursor); + ecs_type_op_t *op = get_op(scope); + + if (op->kind != EcsOpPrimitive) { + return -1; + } else { + void *ptr = get_ptr(scope); + + switch(op->is.primitive) { + case EcsU8: + case EcsByte: + if (value > UINT8_MAX) { + return -1; + } + *(uint8_t*)ptr = (uint8_t)value; + break; + case EcsU16: + if (value > UINT16_MAX) { + return -1; + } + *(uint16_t*)ptr = (uint16_t)value; + break; + case EcsU32: + if (value > UINT32_MAX) { + return -1; + } + *(uint32_t*)ptr = (uint32_t)value; + break; + case EcsU64: + if (value > UINT64_MAX) { + return -1; + } + *(uint64_t*)ptr = (uint64_t)value; + break; + case EcsUPtr: + if (value > UINTPTR_MAX) { + return -1; + } + *(uintptr_t*)ptr = (uintptr_t)value; + break; + case EcsEntity: + *(ecs_entity_t*)ptr = value; + break; + default: + if(!value) return ecs_meta_set_null(cursor); + return -1; + } + + return 0; + } +} + +int ecs_meta_set_float( + ecs_meta_cursor_t *cursor, + double value) +{ + ecs_meta_scope_t *scope = get_scope(cursor); + ecs_type_op_t *op = get_op(scope); + + if (op->kind != EcsOpPrimitive) { + return -1; + } else { + void *ptr = get_ptr(scope); + + switch(op->is.primitive) { + case EcsF32: + *(float*)ptr = (float)value; + break; + case EcsF64: + *(double*)ptr = value; + break; + default: + return -1; + break; + } + + return 0; + } +} + +int ecs_meta_set_string( + ecs_meta_cursor_t *cursor, + const char *value) +{ + ecs_meta_scope_t *scope = get_scope(cursor); + ecs_type_op_t *op = get_op(scope); + + if (op->kind != EcsOpPrimitive) { + return -1; + } else { + void *ptr = get_ptr(scope); + + switch(op->is.primitive) { + case EcsString: + if (*(char**)ptr) { + ecs_os_free(*(char**)ptr); + } + if (value) { + *(char**)ptr = ecs_os_strdup(value); + } else { + *(char**)ptr = NULL; + } + break; + default: + return -1; + break; + } + + return 0; + } +} + +int ecs_meta_set_entity( + ecs_meta_cursor_t *cursor, + ecs_entity_t value) +{ + ecs_meta_scope_t *scope = get_scope(cursor); + ecs_type_op_t *op = get_op(scope); + + if (op->kind != EcsOpPrimitive) { + return -1; + } else { + void *ptr = get_ptr(scope); + + switch(op->is.primitive) { + case EcsEntity: + *(ecs_entity_t*)ptr = value; + break; + default: + return -1; + break; + } + + return 0; + } +} + +int ecs_meta_set_null( + ecs_meta_cursor_t *cursor) +{ + ecs_meta_scope_t *scope = get_scope(cursor); + ecs_type_op_t *op = get_op(scope); + + switch(op->kind) { + case EcsOpPrimitive: { + if (op->is.primitive != EcsString) { + return -1; + } + + void *ptr = get_ptr(scope); + char *str = *(char**)ptr; + if (str) { + ecs_os_free(str); + } + + *(char**)ptr = NULL; + break; + } + + case EcsOpVector: { + void *ptr = get_ptr(scope); + ecs_vector_t *vec = *(ecs_vector_t**)ptr; + if (vec) { + ecs_vector_free(vec); + } + + *(ecs_vector_t**)ptr = NULL; + break; + } + + default: + return -1; + break; + } + + return 0; +} +#include +#include + +static +const char* skip_ws(const char *ptr) { + while (isspace(*ptr)) { + ptr ++; + } + + return ptr; +} + +static +const char* skip_scope(const char *ptr, ecs_meta_parse_ctx_t *ctx) { + /* Keep track of which characters were used to open the scope */ + char stack[256]; + int32_t sp = 0; + char ch; + + while ((ch = *ptr)) { + if (ch == '(') { + stack[sp++] = ch; + } else if (ch == '<') { + stack[sp++] = ch; + } else if (ch == '>') { + if (stack[--sp] != '<') { + ecs_meta_error(ctx, ptr, "mismatching < > in type definition"); + } + } else if (ch == ')') { + if (stack[--sp] != '(') { + ecs_meta_error(ctx, ptr, "mismatching ( ) in type definition"); + } + } + + ptr ++; + + if (!sp) { + break; + } + } + + return ptr; +} + +static +const char* parse_digit( + const char *ptr, + int64_t *value_out, + ecs_meta_parse_ctx_t *ctx) +{ + ptr = skip_ws(ptr); + + if (!isdigit(*ptr) && *ptr != '-') { + ecs_meta_error(ctx, ptr, "expected number, got %c", *ptr); + } + + *value_out = strtol(ptr, NULL, 0); + + if (ptr[0] == '-') { + ptr ++; + } else + if (ptr[0] == '0' && ptr[1] == 'x') { + ptr += 2; + } + + while (isdigit(*ptr)) { + ptr ++; + } + + return skip_ws(ptr); +} + +static +const char* parse_identifier( + const char *ptr, + char *buff, + char *params, + ecs_meta_parse_ctx_t *ctx) +{ + ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(buff != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_assert(ctx != NULL, ECS_INTERNAL_ERROR, NULL); + + char *bptr = buff, ch; + + if (params) { + params[0] = '\0'; + } + + /* Ignore whitespaces */ + ptr = skip_ws(ptr); + + if (!isalpha(*ptr)) { + ecs_meta_error(ctx, ptr, + "invalid identifier (starts with '%c')", *ptr); + } + + while ((ch = *ptr) && !isspace(ch) && ch != ';' && ch != ',' && ch != ')' && ch != '>') { + /* Type definitions can contain macro's or templates */ + if (ch == '(' || ch == '<') { + if (!params) { + ecs_meta_error(ctx, ptr, "unexpected %c", *ptr); + } + + const char *end = skip_scope(ptr, ctx); + ecs_os_strncpy(params, ptr, (ecs_size_t)(end - ptr)); + params[end - ptr] = '\0'; + + ptr = end; + } else { + *bptr = ch; + bptr ++; + ptr ++; + } + } + + *bptr = '\0'; + + if (!ch) { + ecs_meta_error(ctx, ptr, "unexpected end of token"); + } + + return ptr; +} + +static +const char * ecs_meta_open_scope( + const char *ptr, + ecs_meta_parse_ctx_t *ctx) +{ + /* Skip initial whitespaces */ + ptr = skip_ws(ptr); + + /* Is this the start of the type definition? */ + if (ctx->decl == ptr) { + if (*ptr != '{') { + ecs_meta_error(ctx, ptr, "missing '{' in struct definition"); + } + + ptr ++; + ptr = skip_ws(ptr); + } + + /* Is this the end of the type definition? */ + if (!*ptr) { + ecs_meta_error(ctx, ptr, "missing '}' at end of struct definition"); + } + + /* Is this the end of the type definition? */ + if (*ptr == '}') { + ptr = skip_ws(ptr + 1); + if (*ptr) { + ecs_meta_error(ctx, ptr, + "stray characters after struct definition"); + } + return NULL; + } + + return ptr; +} + +const char* ecs_meta_parse_constant( + const char *ptr, + ecs_meta_constant_t *token, + ecs_meta_parse_ctx_t *ctx) +{ + ptr = ecs_meta_open_scope(ptr, ctx); + if (!ptr) { + return NULL; + } + + token->is_value_set = false; + + /* Parse token, constant identifier */ + ptr = parse_identifier(ptr, token->name, NULL, ctx); + ptr = skip_ws(ptr); + + /* Explicit value assignment */ + if (*ptr == '=') { + int64_t value = 0; + ptr = parse_digit(ptr + 1, &value, ctx); + token->value = value; + token->is_value_set = true; + } + + /* Expect a ',' or '}' */ + if (*ptr != ',' && *ptr != '}') { + ecs_meta_error(ctx, ptr, "missing , after enum constant"); + } + + if (*ptr == ',') { + return ptr + 1; + } else { + return ptr; + } +} + +static +const char* ecs_meta_parse_type( + const char *ptr, + ecs_meta_type_t *token, + ecs_meta_parse_ctx_t *ctx) +{ + token->is_ptr = false; + token->is_const = false; + + ptr = skip_ws(ptr); + + /* Parse token, expect type identifier or ECS_PROPERTY */ + ptr = parse_identifier(ptr, token->type, token->params, ctx); + + if (!strcmp(token->type, "ECS_PRIVATE")) { + /* Members from this point are not stored in metadata */ + return NULL; + } + + /* If token is const, set const flag and continue parsing type */ + if (!strcmp(token->type, "const")) { + token->is_const = true; + + /* Parse type after const */ + ptr = parse_identifier(ptr + 1, token->type, token->params, ctx); + } + + /* Check if type is a pointer */ + ptr = skip_ws(ptr); + if (*ptr == '*') { + token->is_ptr = true; + ptr ++; + } + + return ptr; +} + +const char* ecs_meta_parse_member( + const char *ptr, + ecs_meta_member_t *token, + ecs_meta_parse_ctx_t *ctx) +{ + ptr = ecs_meta_open_scope(ptr, ctx); + if (!ptr) { + return NULL; + } + + token->count = 1; + token->is_partial = false; + + /* Parse member type */ + ptr = ecs_meta_parse_type(ptr, &token->type, ctx); + if (!ptr) { + /* If NULL is returned, parsing should stop */ + token->is_partial = true; + return NULL; + } + + /* Next token is the identifier */ + ptr = parse_identifier(ptr, token->name, NULL, ctx); + + /* Skip whitespace between member and [ or ; */ + ptr = skip_ws(ptr); + + /* Check if this is an array */ + char *array_start = strchr(token->name, '['); + if (!array_start) { + /* If the [ was separated by a space, it will not be parsed as part of + * the name */ + if (*ptr == '[') { + array_start = (char*)ptr; /* safe, will not be modified */ + } + } + + if (array_start) { + /* Check if the [ matches with a ] */ + char *array_end = strchr(array_start, ']'); + if (!array_end) { + ecs_meta_error(ctx, ptr, "missing ']'"); + + } else if (array_end - array_start == 0) { + ecs_meta_error(ctx, ptr, "dynamic size arrays are not supported"); + } + + token->count = atoi(array_start + 1); + + if (array_start == ptr) { + /* If [ was found after name, continue parsing after ] */ + ptr = array_end + 1; + } else { + /* If [ was fonud in name, replace it with 0 terminator */ + array_start[0] = '\0'; + } + } + + /* Expect a ; */ + if (*ptr != ';') { + ecs_meta_error(ctx, ptr, "missing ; after member declaration"); + } + + return ptr + 1; +} + +void ecs_meta_parse_params( + const char *ptr, + ecs_meta_params_t *token, + ecs_meta_parse_ctx_t *ctx) +{ + token->is_key_value = false; + token->is_fixed_size = false; + + ptr = skip_ws(ptr); + if (*ptr != '(' && *ptr != '<') { + ecs_meta_error(ctx, ptr, + "expected '(' at start of collection definition"); + } + + ptr ++; + + /* Parse type identifier */ + ptr = ecs_meta_parse_type(ptr, &token->type, ctx); + ptr = skip_ws(ptr); + + /* If next token is a ',' the first type was a key type */ + if (*ptr == ',') { + ptr = skip_ws(ptr + 1); + + if (isdigit(*ptr)) { + int64_t value; + ptr = parse_digit(ptr, &value, ctx); + token->count = value; + token->is_fixed_size = true; + } else { + token->key_type = token->type; + + /* Parse element type */ + ptr = ecs_meta_parse_type(ptr, &token->type, ctx); + ptr = skip_ws(ptr); + + token->is_key_value = true; + } + } + + if (*ptr != ')' && *ptr != '>') { + ecs_meta_error(ctx, ptr, + "expected ')' at end of collection definition"); + } +} diff --git a/code/vendors/flecs/flecs_meta.h b/code/vendors/flecs/flecs_meta.h new file mode 100644 index 0000000..b076ae9 --- /dev/null +++ b/code/vendors/flecs/flecs_meta.h @@ -0,0 +1,688 @@ +#define flecs_meta_STATIC +#ifndef FLECS_META_H +#define FLECS_META_H + +/* This generated file contains includes for project dependencies */ +/* + ) + (.) + .|. + | | + _.--| |--._ + .-'; ;`-'& ; `&. + \ & ; & &_/ + |"""---...---"""| + \ | | | | | | | / + `---.|.|.|.---' + + * This file is generated by bake.lang.c for your convenience. Headers of + * dependencies will automatically show up in this file. Include bake_config.h + * in your main project file. Do not edit! */ + +#ifndef FLECS_META_BAKE_CONFIG_H +#define FLECS_META_BAKE_CONFIG_H + +/* Headers of public dependencies */ +#include + +/* Convenience macro for exporting symbols */ +#ifndef flecs_meta_STATIC +#if flecs_meta_EXPORTS && (defined(_MSC_VER) || defined(__MINGW32__)) + #define FLECS_META_API __declspec(dllexport) +#elif flecs_meta_EXPORTS + #define FLECS_META_API __attribute__((__visibility__("default"))) +#elif defined _MSC_VER + #define FLECS_META_API __declspec(dllimport) +#else + #define FLECS_META_API +#endif +#else + #define FLECS_META_API +#endif + +#endif + + +#ifndef FLECS_LEGACY +//////////////////////////////////////////////////////////////////////////////// +//// Utility macro's (do not use in code!) +//////////////////////////////////////////////////////////////////////////////// + +#define ECS_ENUM_BOOTSTRAP(name, ...)\ +typedef enum name __VA_ARGS__ name;\ +ECS_UNUSED \ +static const char * __##name##__ = #__VA_ARGS__ + +#define ECS_STRUCT_IMPL(name, descriptor, ...)\ +typedef struct name __VA_ARGS__ name;\ +ECS_UNUSED \ +static EcsMetaType __##name##__ = {EcsStructType, sizeof(name), ECS_ALIGNOF(name), descriptor, NULL}\ + +#define ECS_ENUM_IMPL(name, descriptor, ...)\ +typedef enum name __VA_ARGS__ name;\ +ECS_UNUSED \ +static EcsMetaType __##name##__ = {EcsEnumType, sizeof(name), ECS_ALIGNOF(name), descriptor, NULL} + +#define ECS_BITMASK_IMPL(name, descriptor, ...)\ +typedef enum name __VA_ARGS__ name;\ +ECS_UNUSED \ +static EcsMetaType __##name##__ = {EcsBitmaskType, sizeof(name), ECS_ALIGNOF(name), descriptor, NULL} + +#define ECS_STRUCT_C(T, ...) ECS_STRUCT_IMPL(T, #__VA_ARGS__, __VA_ARGS__) +#define ECS_ENUM_C(T, ...) ECS_ENUM_IMPL(T, #__VA_ARGS__, __VA_ARGS__) +#define ECS_BITMASK_C(T, ...) ECS_BITMASK_IMPL(T, #__VA_ARGS__, __VA_ARGS__) + +#define ECS_ARRAY(name, T, length)\ +typedef T name[length];\ +ECS_UNUSED \ +static EcsMetaType __##name##__ = {EcsArrayType, sizeof(T) * length, ECS_ALIGNOF(T), "(" #T "," #length ")", NULL} + +#define ECS_VECTOR(name, T)\ +typedef ecs_vector_t *name;\ +ECS_UNUSED \ +static EcsMetaType __##name##__ = {EcsVectorType, sizeof(ecs_vector_t*), ECS_ALIGNOF(ecs_vector_t*), "(" #T ")", NULL} + +#define ECS_MAP(name, K, T)\ +typedef ecs_map_t *name;\ +ECS_UNUSED \ +static EcsMetaType __##name##__ = {EcsMapType, sizeof(ecs_map_t*), ECS_ALIGNOF(ecs_map_t*), "(" #K "," #T ")", NULL} + +#ifdef __cplusplus + +// Unspecialized class (see below) +namespace flecs { +template +class __meta__ { }; +} + +// Specialized C++ class that stores name and descriptor of type +#define ECS_META_CPP(T, kind, descr);\ +namespace flecs {\ +template<>\ +class __meta__ {\ +public:\ + static const char* name() {\ + return #T;\ + }\ + static EcsMetaType descriptor() {\ + return {kind, sizeof(T), ECS_ALIGNOF(T), descr, NULL};\ + }\ +};\ +} + +#endif + + +//////////////////////////////////////////////////////////////////////////////// +//// Use these macro's to define types. +//// The API will automatically select the right macro's for C or C++ +//////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus + +// C++ + +// Define a struct +#define ECS_STRUCT(T, ...)\ + ECS_STRUCT_IMPL(T, #__VA_ARGS__, __VA_ARGS__);\ + ECS_META_CPP(T, EcsStructType, #__VA_ARGS__) + +// Define an enumeration +#define ECS_ENUM(T, ...)\ + ECS_ENUM_IMPL(T, #__VA_ARGS__, __VA_ARGS__);\ + ECS_META_CPP(T, EcsEnumType, #__VA_ARGS__); + +// Define a bitmask +#define ECS_BITMASK(T, ...)\ + ECS_BITMASK_IMPL(T, #__VA_ARGS__, __VA_ARGS__);\ + ECS_META_CPP(T, EcsBitmaskType, #__VA_ARGS__) + +#else + +// C + +// Define a struct +#define ECS_STRUCT(name, ...)\ + ECS_STRUCT_IMPL(name, #__VA_ARGS__, __VA_ARGS__) + +// Define an enumeration +#define ECS_ENUM(name, ...)\ + ECS_ENUM_IMPL(name, #__VA_ARGS__, __VA_ARGS__) + +// Define a bitmask +#define ECS_BITMASK(name, ...)\ + ECS_BITMASK_IMPL(name, #__VA_ARGS__, __VA_ARGS__) + +// Define a type alias +#define ECS_ALIAS(type, name)\ + typedef type name;\ + ECS_UNUSED \ + static EcsMetaType __##name##__ = {0, sizeof(name), ECS_ALIGNOF(name), NULL, &__##type##__};\ + +#endif + + +//////////////////////////////////////////////////////////////////////////////// +//// Use these macro's inside types +//////////////////////////////////////////////////////////////////////////////// + +// Define a vector +#define ecs_vector(T) ecs_vector_t* + +// Define a map +#define ecs_map(K, T) ecs_map_t* + +// Indicate that members after this should not be serialized +#define ECS_PRIVATE + + +//////////////////////////////////////////////////////////////////////////////// +//// Meta description types +//////////////////////////////////////////////////////////////////////////////// + +/* Explicit string type */ +typedef const char* ecs_string_t; + +/* Explicit byte type */ +typedef uint8_t ecs_byte_t; + +#ifdef __cplusplus + +namespace flecs { + using string = ecs_string_t; + using byte = ecs_byte_t; + + // Define a bitmask + // In C++ trying to assign multiple flags to a variable of an enum type will + // result in a compiler error. Use this template so that the serializer knows + // this value is a bitmask, while also keeping the compiler happy. + template + using bitmask = int32_t; +} + +#endif + +ECS_ENUM_BOOTSTRAP( ecs_type_kind_t, { + EcsPrimitiveType, + EcsBitmaskType, + EcsEnumType, + EcsStructType, + EcsArrayType, + EcsVectorType, + EcsMapType +}); + +ECS_STRUCT( EcsMetaType, { + ecs_type_kind_t kind; + ecs_size_t size; + int16_t alignment; + const char *descriptor; + void *alias; +}); + +ECS_ENUM( ecs_primitive_kind_t, { + EcsBool, + EcsChar, + EcsByte, + EcsU8, + EcsU16, + EcsU32, + EcsU64, + EcsI8, + EcsI16, + EcsI32, + EcsI64, + EcsF32, + EcsF64, + EcsUPtr, + EcsIPtr, + EcsString, + EcsEntity +}); + +ECS_STRUCT( EcsPrimitive, { + ecs_primitive_kind_t kind; +}); + +// Define EcsBitmask for both C and C++. Both representations are equivalent in +// memory, but allow for a nicer type-safe API in C++ +#if defined(__cplusplus) && !defined(FLECS_NO_CPP) +ECS_STRUCT( EcsBitmask, { + flecs::map constants; +}); +#else +ECS_STRUCT( EcsBitmask, { + ecs_map(int32_t, ecs_string_t) constants; +}); +#endif + +// Define EcsEnum for both C and C++. Both representations are equivalent in +// memory, but allow for a nicer type-safe API in C++ +#if defined(__cplusplus) && !defined(FLECS_NO_CPP) +ECS_STRUCT( EcsEnum, { + flecs::map constants; +}); +#else +ECS_STRUCT( EcsEnum, { + ecs_map(int32_t, ecs_string_t) constants; +}); +#endif + +ECS_STRUCT( EcsMember, { + char *name; + ecs_entity_t type; +}); + +// Define EcsStruct for both C and C++. Both representations are equivalent in +// memory, but allow for a nicer type-safe API in C++ +#if defined(__cplusplus) && !defined(FLECS_NO_CPP) +ECS_STRUCT( EcsStruct, { + flecs::vector members; + bool is_partial; +}); +#else +ECS_STRUCT( EcsStruct, { + ecs_vector(EcsMember) members; + bool is_partial; +}); +#endif + +ECS_STRUCT( EcsArray, { + ecs_entity_t element_type; + int32_t count; +}); + +ECS_STRUCT( EcsVector, { + ecs_entity_t element_type; +}); + +ECS_STRUCT( EcsMap, { + ecs_entity_t key_type; + ecs_entity_t element_type; +}); + + +//////////////////////////////////////////////////////////////////////////////// +//// Type serializer +//////////////////////////////////////////////////////////////////////////////// + +ECS_ENUM_C( ecs_type_op_kind_t, { + EcsOpHeader, + EcsOpPrimitive, + EcsOpEnum, + EcsOpBitmask, + EcsOpPush, + EcsOpPop, + EcsOpArray, + EcsOpVector, + EcsOpMap +}); + +typedef ecs_vector_t ecs_type_op_vector_t; +typedef ecs_vector_t ecs_constant_vector_t; + +ECS_STRUCT_C( ecs_type_op_t, { + ecs_entity_t type; + ecs_type_op_kind_t kind; + ecs_size_t size; /* Size of value or element type if array or vector */ + int16_t alignment; /* Alignment of value */ + int32_t count; /* Number of elements (only used for arrays) */ + int32_t offset; /* Offset of value */ + const char *name; /* Name of value (only used for struct members) */ + +ECS_PRIVATE + + /* Instruction-specific data */ + union { + ecs_primitive_kind_t primitive; + ecs_ref_t constant; + ecs_ref_t collection; + + struct { + ecs_ref_t key; + ecs_ref_t element; + } map; + } is; +}); + +ECS_STRUCT_C( EcsMetaTypeSerializer, { + ecs_vector(ecs_type_op_t) ops; +}); + +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +//////////////////////////////////////////////////////////////////////////////// +//// Pretty printer +//////////////////////////////////////////////////////////////////////////////// + +/** Convert value to a string. */ +FLECS_META_API +char* ecs_ptr_to_str( + ecs_world_t *world, + ecs_entity_t type, + void* ptr); + +/** Convert value to a string. */ +FLECS_META_API +char* ecs_entity_to_str( + ecs_world_t *world, + ecs_entity_t entity); + + +//////////////////////////////////////////////////////////////////////////////// +//// Serialization utilities +//////////////////////////////////////////////////////////////////////////////// + +#define ECS_MAX_I8 127 +#define ECS_MIN_I8 -128 + +#define ECS_MAX_I16 32767 +#define ECS_MIN_I16 -32768 + +#define ECS_MAX_I32 2147483647 +#define ECS_MIN_I32 -2147483648 + +#define ECS_MAX_I64 9223372036854775807 +#define ECS_MIN_I64 (-9223372036854775807 - 1) + +#define ECS_MAX_U8 255u +#define ECS_MAX_U16 65535u +#define ECS_MAX_U32 4294967295u +#define ECS_MAX_U64 18446744073709551615u + +#define ECS_MAX_I8_STR "127" +#define ECS_MIN_I8_STR "-128" + +#define ECS_MAX_I16_STR "32767" +#define ECS_MIN_I16_STR "-32768" + +#define ECS_MAX_I32_STR "2147483647" +#define ECS_MIN_I32_STR "-2147483648" + +#define ECS_MAX_I64_STR "9223372036854775807" +#define ECS_MIN_I64_STR "-9223372036854775808" + +#define ECS_MAX_U8_STR "255" +#define ECS_MAX_U16_STR "65535" +#define ECS_MAX_U32_STR "4294967295" +#define ECS_MAX_U64_STR "18446744073709551615" + +/** Escape a character */ +FLECS_META_API +char* ecs_chresc( + char *out, + char in, + char delimiter); + +/** Parse an escaped character */ +FLECS_META_API +const char* ecs_chrparse( + const char *in, + char *out); + +/** Escape a string */ +FLECS_META_API +ecs_size_t ecs_stresc( + char *out, + ecs_size_t n, + char delimiter, + const char *in); + +#ifdef __cplusplus +} +#endif + + +//////////////////////////////////////////////////////////////////////////////// +//// Deserialization API +//////////////////////////////////////////////////////////////////////////////// + +#define ECS_META_MAX_SCOPE_DEPTH (32) /* >32 levels of nesting is not sane */ + +typedef struct ecs_meta_scope_t { + ecs_entity_t type; + ecs_vector_t *ops; + int32_t start; + int32_t cur_op; + int32_t cur_elem; + int32_t count; + void *base; + ecs_vector_t *vector; + bool is_collection; +} ecs_meta_scope_t; + +typedef struct ecs_meta_cursor_t { + ecs_world_t *world; + ecs_meta_scope_t scope[ECS_META_MAX_SCOPE_DEPTH]; + int32_t depth; +} ecs_meta_cursor_t; + +FLECS_META_API +ecs_meta_cursor_t ecs_meta_cursor( + ecs_world_t *world, + ecs_entity_t type, + void *base); + +FLECS_META_API +void* ecs_meta_get_ptr( + ecs_meta_cursor_t *cursor); + +FLECS_META_API +int ecs_meta_next( + ecs_meta_cursor_t *cursor); + +FLECS_META_API +int ecs_meta_move( + ecs_meta_cursor_t *cursor, + int32_t pos); + +FLECS_META_API +int ecs_meta_move_name( + ecs_meta_cursor_t *cursor, + const char *name); + +FLECS_META_API +int ecs_meta_push( + ecs_meta_cursor_t *cursor); + +FLECS_META_API +int ecs_meta_pop( + ecs_meta_cursor_t *cursor); + +FLECS_META_API +int ecs_meta_set_bool( + ecs_meta_cursor_t *cursor, + bool value); + +FLECS_META_API +int ecs_meta_set_char( + ecs_meta_cursor_t *cursor, + char value); + +FLECS_META_API +int ecs_meta_set_int( + ecs_meta_cursor_t *cursor, + int64_t value); + +FLECS_META_API +int ecs_meta_set_uint( + ecs_meta_cursor_t *cursor, + uint64_t value); + +FLECS_META_API +int ecs_meta_set_float( + ecs_meta_cursor_t *cursor, + double value); + +FLECS_META_API +int ecs_meta_set_string( + ecs_meta_cursor_t *cursor, + const char *value); + +FLECS_META_API +int ecs_meta_set_entity( + ecs_meta_cursor_t *cursor, + ecs_entity_t value); + +FLECS_META_API +int ecs_meta_set_null( + ecs_meta_cursor_t *cursor); + + +//////////////////////////////////////////////////////////////////////////////// +//// Module implementation +//////////////////////////////////////////////////////////////////////////////// + +typedef struct FlecsMeta { + ECS_DECLARE_COMPONENT(EcsPrimitive); + ECS_DECLARE_COMPONENT(EcsEnum); + ECS_DECLARE_COMPONENT(EcsBitmask); + ECS_DECLARE_COMPONENT(EcsStruct); + ECS_DECLARE_COMPONENT(EcsArray); + ECS_DECLARE_COMPONENT(EcsVector); + ECS_DECLARE_COMPONENT(EcsMap); + ECS_DECLARE_COMPONENT(EcsMetaType); + ECS_DECLARE_COMPONENT(EcsMetaTypeSerializer); +} FlecsMeta; + +#ifdef __cplusplus +extern "C" { +#endif + +FLECS_META_API +void FlecsMetaImport( + ecs_world_t *world); + +#define FlecsMetaImportHandles(handles)\ + ECS_IMPORT_COMPONENT(handles, EcsPrimitive);\ + ECS_IMPORT_COMPONENT(handles, EcsEnum);\ + ECS_IMPORT_COMPONENT(handles, EcsBitmask);\ + ECS_IMPORT_COMPONENT(handles, EcsStruct);\ + ECS_IMPORT_COMPONENT(handles, EcsArray);\ + ECS_IMPORT_COMPONENT(handles, EcsVector);\ + ECS_IMPORT_COMPONENT(handles, EcsMap);\ + ECS_IMPORT_COMPONENT(handles, EcsMetaType);\ + ECS_IMPORT_COMPONENT(handles, EcsMetaTypeSerializer); + +//////////////////////////////////////////////////////////////////////////////// +//// Macro for inserting metadata in C application +//////////////////////////////////////////////////////////////////////////////// + +FLECS_META_API +void ecs_new_meta( + ecs_world_t *world, + ecs_entity_t component, + struct EcsMetaType *meta_type); + +#define ECS_META(world, T)\ + ECS_COMPONENT(world, T);\ + ecs_new_meta(world, ecs_entity(T), &__##T##__); + +#ifdef __cplusplus +} +#endif + + +//////////////////////////////////////////////////////////////////////////////// +//// C++ Module implementation +//////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +#ifndef FLECS_NO_CPP + +namespace flecs { +namespace components { + +class meta { +public: + using Primitive = EcsPrimitive; + using Enum = EcsEnum; + using Bitmask = EcsBitmask; + using Struct = EcsStruct; + using Array = EcsArray; + using Vector = EcsVector; + using Type = EcsMetaType; + using TypeSerializer = EcsMetaTypeSerializer; + + enum TypeKind { + PrimitiveType = EcsPrimitiveType, + BitmaskType = EcsBitmaskType, + EnumType = EcsEnumType, + StructType = EcsStructType, + ArrayType = EcsArrayType, + VectorType = EcsVectorType, + MapType = EcsMapType + }; + + meta(flecs::world& world) { + FlecsMetaImport(world.c_ptr()); + + flecs::module(world, "flecs::components::meta"); + + flecs::component(world, "EcsPrimitive"); + flecs::component(world, "EcsEnum"); + flecs::component(world, "EcsBitmask"); + flecs::component(world, "EcsStruct"); + flecs::component(world, "EcsArray"); + flecs::component(world, "EcsVector"); + flecs::component(world, "EcsMetaType"); + flecs::component(world, "EcsMetaTypeSerializer"); + } +}; +} + +//////////////////////////////////////////////////////////////////////////////// +//// Functions for inserting metadata in C++ applications +//////////////////////////////////////////////////////////////////////////////// + +// Template that injects metadata into ECS +template +flecs::entity meta(flecs::world& world) { + flecs::entity e = flecs::component(world, flecs::__meta__::name()); + e.set({ flecs::__meta__::descriptor() }); + return e; +} + + +//////////////////////////////////////////////////////////////////////////////// +//// Serialize values to string +//////////////////////////////////////////////////////////////////////////////// + +template +std::string pretty_print(flecs::world& world, flecs::entity_t type, T& data) { + char *str = ecs_ptr_to_str(world.c_ptr(), type, &data); + std::string result = std::string(str); + free(str); + return result; +} + +template +std::string pretty_print(flecs::world& world, flecs::entity type, T& data) { + return pretty_print(world, type.id(), data); +} + +template +std::string pretty_print(flecs::world& world, T& data) { + entity_t type = _::component_info::id(); + return flecs::pretty_print(world, type, data); +} + +template <> +inline std::string pretty_print(flecs::world& world, flecs::entity& entity) { + char *str = ecs_entity_to_str(world.c_ptr(), entity.id()); + std::string result = std::string(str); + free(str); + return result; +} + +} + +#endif // FLECS_NO_CPP +#endif // __cplusplus + +#endif // FLECS_META_H