eco2d/code/vendors/flecs/flecs_json.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);
}