561 lines
16 KiB
C
561 lines
16 KiB
C
#ifndef FLECS_JSON_IMPL
|
|
#include "flecs_json.h"
|
|
#endif
|
|
|
|
/* Simple serializer to turn values into strings. Use this code as a template
|
|
* for when implementing a new serializer. */
|
|
|
|
static
|
|
void json_ser_type(
|
|
ecs_world_t *world,
|
|
ecs_vector_t *ser,
|
|
const void *base,
|
|
ecs_strbuf_t *str);
|
|
|
|
static
|
|
void json_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 json_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:
|
|
ecs_strbuf_appendstrn(str, "\"", 1);
|
|
ecs_strbuf_appendstrn(str, (char*)base, 1);
|
|
ecs_strbuf_appendstrn(str, "\"", 1);
|
|
break;
|
|
case EcsByte:
|
|
ecs_strbuf_append(str, "%u", *(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, "%u", *(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, "%d", *(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_strbuf_appendstrn(str, "\"", 1);
|
|
ecs_strbuf_appendstr(str, value);
|
|
ecs_strbuf_appendstrn(str, "\"", 1);
|
|
} else {
|
|
ecs_strbuf_appendstr(str, "null");
|
|
}
|
|
break;
|
|
}
|
|
case EcsEntity:
|
|
ecs_strbuf_appendstrn(str, "\"", 1);
|
|
ecs_strbuf_appendstr(str, ecs_get_name(world, *(ecs_entity_t*)base));
|
|
ecs_strbuf_appendstrn(str, "\"", 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Serialize enumeration */
|
|
static
|
|
void json_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);
|
|
ecs_assert(constant != NULL, ECS_INVALID_PARAMETER, NULL);
|
|
|
|
ecs_strbuf_appendstrn(str, "\"", 1);
|
|
ecs_strbuf_appendstr(str, *constant);
|
|
ecs_strbuf_appendstrn(str, "\"", 1);
|
|
}
|
|
|
|
/* Serialize bitmask */
|
|
static
|
|
void json_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);
|
|
|
|
int32_t value = *(int32_t*)base;
|
|
ecs_map_key_t key;
|
|
char **constant;
|
|
|
|
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_append(str, "\"%s\"", *constant);
|
|
}
|
|
}
|
|
|
|
ecs_strbuf_list_pop(str, "]");
|
|
}
|
|
|
|
/* Serialize elements of a contiguous array */
|
|
static
|
|
void json_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);
|
|
json_ser_type(world, elem_ops, ptr, str);
|
|
ptr = ECS_OFFSET(ptr, elem_size);
|
|
}
|
|
|
|
ecs_strbuf_list_pop(str, "]");
|
|
}
|
|
|
|
/* Serialize array */
|
|
static
|
|
void json_ser_array(
|
|
ecs_world_t *world,
|
|
ecs_type_op_t *op,
|
|
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);
|
|
|
|
json_ser_elements(world, ser->ops, base, op->count, op->size, str);
|
|
}
|
|
|
|
/* Serialize vector */
|
|
static
|
|
void json_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;
|
|
|
|
int32_t count = ecs_vector_count(value);
|
|
void *array = ecs_vector_first_t(value, op->size, op->alignment);
|
|
|
|
const EcsMetaTypeSerializer *ser = ecs_get_ref_w_entity(world, &op->is.collection, 0, 0);
|
|
ecs_assert(ser != NULL, ECS_INTERNAL_ERROR, NULL);
|
|
|
|
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);
|
|
size_t elem_size = elem_op_hdr->size;
|
|
|
|
/* Serialize contiguous buffer of vector */
|
|
json_ser_elements(world, elem_ops, array, count, elem_size, str);
|
|
}
|
|
|
|
/* Serialize map */
|
|
static
|
|
void json_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);
|
|
ecs_strbuf_appendstrn(str, "\"", 1);
|
|
json_ser_type_op(world, key_op, (void*)&key, str);
|
|
ecs_strbuf_appendstrn(str, "\"", 1);
|
|
ecs_strbuf_appendstr(str, ":");
|
|
json_ser_type(world, elem_ser->ops, ptr, str);
|
|
|
|
key = 0;
|
|
}
|
|
|
|
ecs_strbuf_list_pop(str, "}");
|
|
}
|
|
|
|
/* Forward serialization to the different type kinds */
|
|
static
|
|
void json_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:
|
|
json_ser_primitive(world, op, ECS_OFFSET(base, op->offset), str);
|
|
break;
|
|
case EcsOpEnum:
|
|
json_ser_enum(world, op, ECS_OFFSET(base, op->offset), str);
|
|
break;
|
|
case EcsOpBitmask:
|
|
json_ser_bitmask(world, op, ECS_OFFSET(base, op->offset), str);
|
|
break;
|
|
case EcsOpArray:
|
|
json_ser_array(world, op, ECS_OFFSET(base, op->offset), str);
|
|
break;
|
|
case EcsOpVector:
|
|
json_ser_vector(world, op, ECS_OFFSET(base, op->offset), str);
|
|
break;
|
|
case EcsOpMap:
|
|
json_ser_map(world, op, ECS_OFFSET(base, op->offset), str);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Iterate over the type ops of a type */
|
|
static
|
|
void json_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:
|
|
json_ser_type_op(world, op, base, str);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
char* ecs_ptr_to_json(
|
|
ecs_world_t *world,
|
|
ecs_entity_t type,
|
|
void* ptr)
|
|
{
|
|
ecs_entity_t ecs_typeid(EcsMetaTypeSerializer) = ecs_lookup(world, "EcsMetaTypeSerializer");
|
|
const EcsMetaTypeSerializer *ser = ecs_get(world, type, EcsMetaTypeSerializer);
|
|
ecs_assert(ser != NULL, ECS_INVALID_PARAMETER, NULL);
|
|
|
|
ecs_strbuf_t str = ECS_STRBUF_INIT;
|
|
json_ser_type(world, ser->ops, ptr, &str);
|
|
return ecs_strbuf_get(&str);
|
|
}
|
|
|
|
void json_ser_column(
|
|
ecs_world_t *world,
|
|
const EcsMetaTypeSerializer *ser,
|
|
void *ptr,
|
|
int32_t count,
|
|
ecs_strbuf_t *str)
|
|
{
|
|
ecs_vector_t *ops = ser->ops;
|
|
ecs_type_op_t *hdr = ecs_vector_first(ops, ecs_type_op_t);
|
|
ecs_assert(hdr->kind == EcsOpHeader, ECS_INTERNAL_ERROR, NULL);
|
|
int32_t size = hdr->size;
|
|
|
|
ecs_strbuf_list_push(str, "[", ",");
|
|
|
|
int i;
|
|
for (i = 0; i < count; i ++) {
|
|
ecs_strbuf_list_next(str);
|
|
json_ser_type(world, ops, ptr, str);
|
|
ptr = ECS_OFFSET(ptr, size);
|
|
}
|
|
|
|
ecs_strbuf_list_pop(str, "]");
|
|
}
|
|
|
|
static
|
|
void serialize_type(
|
|
ecs_world_t *world,
|
|
ecs_type_t type,
|
|
ecs_strbuf_t *str)
|
|
{
|
|
ecs_strbuf_list_push(str, "[", ",");
|
|
|
|
int i, count = ecs_vector_count(type);
|
|
ecs_entity_t *comps = ecs_vector_first(type, ecs_entity_t);
|
|
for (i = 0; i < count; i ++) {
|
|
ecs_entity_t comp = comps[i];
|
|
bool has_role = (comp & ECS_COMPONENT_MASK) != comp;
|
|
ecs_entity_t comp_e = comp & ECS_COMPONENT_MASK;
|
|
|
|
if (has_role) {
|
|
ecs_strbuf_list_next(str);
|
|
ecs_strbuf_list_push(str, "[", ",");
|
|
ecs_strbuf_list_append(str, "\"%s\"", ecs_role_str(comp));
|
|
}
|
|
|
|
if (ECS_HAS_ROLE(comp, TRAIT)) {
|
|
ecs_entity_t hi = ecs_entity_t_hi(comp_e);
|
|
char *hi_path = ecs_get_fullpath(world, hi);
|
|
ecs_strbuf_list_append(str, "\"%s\"", hi_path);
|
|
ecs_os_free(hi_path);
|
|
comp_e = ecs_entity_t_lo(comp_e);
|
|
}
|
|
|
|
char *comp_path = ecs_get_fullpath(world, comp_e);
|
|
if (comp_path) {
|
|
ecs_strbuf_list_append(str, "\"%s\"", comp_path);
|
|
} else {
|
|
ecs_strbuf_list_append(str, "%d", (int32_t)comp);
|
|
}
|
|
|
|
if (has_role) {
|
|
ecs_strbuf_list_pop(str, "]");
|
|
}
|
|
|
|
ecs_os_free(comp_path);
|
|
}
|
|
|
|
ecs_strbuf_list_pop(str, "]");
|
|
}
|
|
|
|
char* ecs_type_to_json(
|
|
ecs_world_t *world,
|
|
ecs_type_t type)
|
|
{
|
|
ecs_strbuf_t str = ECS_STRBUF_INIT;
|
|
serialize_type(world, type, &str);
|
|
return ecs_strbuf_get(&str);
|
|
}
|
|
|
|
char* ecs_iter_to_json(
|
|
ecs_world_t *world,
|
|
ecs_iter_t *it,
|
|
ecs_iter_next_action_t iter_next,
|
|
ecs_type_t select)
|
|
{
|
|
ecs_strbuf_t str = ECS_STRBUF_INIT;
|
|
ecs_entity_t ecs_typeid(EcsMetaTypeSerializer) =
|
|
ecs_lookup_fullpath(world, "flecs.meta.MetaTypeSerializer");
|
|
ecs_assert(ecs_typeid(EcsMetaTypeSerializer) != 0, ECS_INTERNAL_ERROR, NULL);
|
|
|
|
ecs_strbuf_list_push(&str, "[", ",");
|
|
|
|
while (iter_next(it)) {
|
|
ecs_type_t table_type = ecs_iter_type(it);
|
|
ecs_entity_t *comps = ecs_vector_first(table_type, ecs_entity_t);
|
|
int32_t i, count = ecs_vector_count(table_type);
|
|
|
|
if (!it->count) {
|
|
continue;
|
|
}
|
|
|
|
ecs_strbuf_list_next(&str);
|
|
ecs_strbuf_list_push(&str, "{", ",");
|
|
|
|
/* Serialize type */
|
|
ecs_strbuf_list_appendstr(&str, "\"type\":");
|
|
serialize_type(world, table_type, &str);
|
|
|
|
/* Add entity identifiers */
|
|
ecs_strbuf_list_appendstr(&str, "\"entities\":");
|
|
ecs_strbuf_list_push(&str, "[", ",");
|
|
for (i = 0; i < it->count; i ++) {
|
|
ecs_strbuf_list_append(&str, "%d", (int32_t)it->entities[i]);
|
|
}
|
|
ecs_strbuf_list_pop(&str, "]");
|
|
|
|
/* Serialize data */
|
|
ecs_strbuf_list_appendstr(&str, "\"data\":");
|
|
ecs_strbuf_list_push(&str, "{", ",");
|
|
|
|
for (i = 0; i < count; i ++) {
|
|
if (select) {
|
|
if (!ecs_type_has_entity(world, select, comps[i])) {
|
|
continue;
|
|
}
|
|
}
|
|
const EcsMetaTypeSerializer *ser = ecs_get(
|
|
world, comps[i], EcsMetaTypeSerializer);
|
|
|
|
/* Don't serialize if there's no metadata for component */
|
|
if (!ser) {
|
|
continue;
|
|
}
|
|
|
|
char *comp_path = ecs_get_fullpath(world, comps[i]);
|
|
ecs_strbuf_list_append(&str, "\"%s\":", comp_path);
|
|
ecs_os_free(comp_path);
|
|
|
|
json_ser_column(
|
|
world, ser, ecs_table_column(it, i), it->count, &str);
|
|
}
|
|
|
|
ecs_strbuf_list_pop(&str, "}");
|
|
ecs_strbuf_list_pop(&str, "}");
|
|
}
|
|
|
|
ecs_strbuf_list_pop(&str, "]");
|
|
|
|
return ecs_strbuf_get(&str);
|
|
}
|
|
|
|
char* ecs_entity_to_json(
|
|
ecs_world_t *world,
|
|
ecs_entity_t entity,
|
|
ecs_type_t select)
|
|
{
|
|
ecs_strbuf_t str = ECS_STRBUF_INIT;
|
|
ecs_entity_t ecs_typeid(EcsMetaTypeSerializer) =
|
|
ecs_lookup_fullpath(world, "flecs.meta.MetaTypeSerializer");
|
|
ecs_assert(ecs_typeid(EcsMetaTypeSerializer) != 0, ECS_INTERNAL_ERROR, NULL);
|
|
|
|
ecs_strbuf_list_push(&str, "{", ",");
|
|
|
|
/* Serialize type */
|
|
ecs_type_t type = ecs_get_type(world, entity);
|
|
ecs_strbuf_list_appendstr(&str, "\"type\":");
|
|
serialize_type(world, type, &str);
|
|
|
|
/* Serialize entity id */
|
|
ecs_strbuf_list_append(&str, "\"entity\":%d", (int32_t)entity);
|
|
|
|
/* Serialize entity path */
|
|
char *path = ecs_get_fullpath(world, entity);
|
|
ecs_strbuf_list_append(&str, "\"path\":\"%s\"", path);
|
|
ecs_os_free(path);
|
|
|
|
/* Serialize data */
|
|
if (type) {
|
|
ecs_strbuf_list_appendstr(&str, "\"data\":");
|
|
ecs_strbuf_list_push(&str, "{", ",");
|
|
|
|
int i, count = ecs_vector_count(type);
|
|
ecs_entity_t *comps = ecs_vector_first(type, ecs_entity_t);
|
|
for (i = 0; i < count; i ++) {
|
|
if (select) {
|
|
if (!ecs_type_has_entity(world, select, comps[i])) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
const EcsMetaTypeSerializer *ser = ecs_get(
|
|
world, comps[i], EcsMetaTypeSerializer);
|
|
|
|
/* Don't serialize if there's no metadata for component */
|
|
if (!ser) {
|
|
continue;
|
|
}
|
|
|
|
char *comp_path = ecs_get_fullpath(world, comps[i]);
|
|
ecs_strbuf_list_append(&str, "\"%s\":", comp_path);
|
|
ecs_os_free(comp_path);
|
|
|
|
const void *comp_ptr = ecs_get_w_entity(world, entity, comps[i]);
|
|
ecs_assert(comp_ptr != NULL, ECS_INTERNAL_ERROR, NULL);
|
|
|
|
json_ser_type(world, ser->ops, comp_ptr, &str);
|
|
}
|
|
|
|
ecs_strbuf_list_pop(&str, "}");
|
|
}
|
|
|
|
ecs_strbuf_list_pop(&str, "}");
|
|
|
|
return ecs_strbuf_get(&str);
|
|
} |