3107 lines
82 KiB
C
3107 lines
82 KiB
C
|
#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 <stdio.h>
|
||
|
#include <ctype.h>
|
||
|
|
||
|
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");
|
||
|
}
|
||
|
}
|