/*
|
* Copyright (c) 2020, Rockchip Electronics Co., Ltd
|
*
|
* This program is free software; you can redistribute it and/or modify
|
* it under the terms of the GNU General Public License as published by
|
* the Free Software Foundation; either version 2 of the License, or
|
* (at your option) any later version.
|
*
|
* This program is distributed in the hope that it will be useful,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* GNU General Public License for more details.
|
*/
|
|
#include "j2s.h"
|
|
static bool j2s_template_dumping = false;
|
|
__attribute__((weak)) void* j2s_alloc_data(j2s_ctx* ctx, size_t size)
|
{
|
return malloc(size);
|
}
|
|
__attribute__((weak)) void j2s_release_data(j2s_ctx* ctx, void* ptr)
|
{
|
free(ptr);
|
}
|
|
static cJSON* _j2s_obj_to_json(j2s_ctx* ctx, int obj_index, void* ptr);
|
|
static cJSON* _j2s_struct_to_json(j2s_ctx* ctx, int struct_index, void* ptr);
|
|
static int _j2s_json_to_obj(j2s_ctx* ctx, cJSON* json, cJSON* parent,
|
int obj_index, void* ptr, bool query);
|
|
static int _j2s_json_to_struct(j2s_ctx* ctx, cJSON* json, int struct_index,
|
void* ptr, bool query);
|
|
static int _j2s_obj_from_cache(j2s_ctx* ctx, int obj_index, int fd, void* ptr);
|
|
static int _j2s_struct_from_cache(j2s_ctx* ctx, int struct_index, int fd,
|
void* ptr);
|
|
static void _j2s_obj_to_cache(j2s_ctx* ctx, int obj_index, int fd, void* ptr);
|
|
static void _j2s_struct_to_cache(j2s_ctx* ctx, int struct_index, int fd,
|
void* ptr);
|
|
static inline int j2s_find_struct_index(j2s_ctx* ctx, const char* name)
|
{
|
if (!name)
|
return -1;
|
|
for (int i = 0; i < ctx->num_struct; i++) {
|
j2s_struct* struct_obj = &ctx->structs[i];
|
if (!strcmp(struct_obj->name, name))
|
return i;
|
}
|
|
return -1;
|
}
|
|
cJSON* j2s_struct_to_json(j2s_ctx* ctx, const char* name, void* ptr)
|
{
|
int struct_index = name ? j2s_find_struct_index(ctx, name) : ctx->root_index;
|
|
return _j2s_struct_to_json(ctx, struct_index, ptr);
|
}
|
|
int j2s_json_to_struct(j2s_ctx* ctx, cJSON* json, const char* name, void* ptr)
|
{
|
int struct_index = name ? j2s_find_struct_index(ctx, name) : ctx->root_index;
|
|
return _j2s_json_to_struct(ctx, json, struct_index, ptr, false);
|
}
|
|
int j2s_json_from_struct(j2s_ctx* ctx, cJSON* json, const char* name,
|
void* ptr)
|
{
|
int struct_index = name ? j2s_find_struct_index(ctx, name) : ctx->root_index;
|
|
return _j2s_json_to_struct(ctx, json, struct_index, ptr, true);
|
}
|
|
void j2s_struct_to_cache(j2s_ctx* ctx, const char* name, int fd, void* ptr)
|
{
|
int struct_index = name ? j2s_find_struct_index(ctx, name) : ctx->root_index;
|
|
_j2s_struct_to_cache(ctx, struct_index, fd, ptr);
|
}
|
|
int j2s_struct_from_cache(j2s_ctx* ctx, const char* name, int fd, void* ptr)
|
{
|
int struct_index = name ? j2s_find_struct_index(ctx, name) : ctx->root_index;
|
|
return _j2s_struct_from_cache(ctx, struct_index, fd, ptr);
|
}
|
|
/* Enum name to value */
|
static inline int j2s_enum_get_value(j2s_ctx* ctx, int enum_index,
|
const char* name)
|
{
|
j2s_enum* enum_obj;
|
|
if (enum_index < 0 || !name)
|
return -1;
|
|
enum_obj = &ctx->enums[enum_index];
|
|
for (int i = 0; i < enum_obj->num_value; i++) {
|
j2s_enum_value* enum_value = &ctx->enum_values[enum_obj->value_index + i];
|
|
if (!strcmp(enum_value->name, name))
|
return enum_value->value;
|
}
|
|
WARN("unknown enum name: %s for %s\n", name, enum_obj->name);
|
return -1;
|
}
|
|
/* Enum value to name */
|
static inline const char* j2s_enum_get_name(j2s_ctx* ctx, int enum_index,
|
int value)
|
{
|
j2s_enum* enum_obj;
|
|
if (enum_index < 0)
|
goto out;
|
|
enum_obj = &ctx->enums[enum_index];
|
|
for (int i = 0; i < enum_obj->num_value; i++) {
|
j2s_enum_value* enum_value = &ctx->enum_values[enum_obj->value_index + i];
|
|
if (enum_value->value == value)
|
return enum_value->name;
|
}
|
|
WARN("unknown enum value: %d for %s\n", value, enum_obj->name);
|
out:
|
return "INVALID";
|
}
|
|
static cJSON* _j2s_enum_to_json(j2s_ctx* ctx, int enum_index)
|
{
|
j2s_enum* enum_obj;
|
cJSON *root, *item;
|
|
if (enum_index < 0)
|
return NULL;
|
|
enum_obj = &ctx->enums[enum_index];
|
|
root = cJSON_CreateObject();
|
DASSERT(root, return NULL);
|
|
for (int i = 0; i < enum_obj->num_value; i++) {
|
j2s_enum_value* enum_value = &ctx->enum_values[enum_obj->value_index + i];
|
|
item = cJSON_CreateNumber(enum_value->value);
|
if (item)
|
cJSON_AddItemToObject(root, enum_value->name, item);
|
}
|
|
return root;
|
}
|
|
cJSON* j2s_enums_to_json(j2s_ctx* ctx)
|
{
|
cJSON *root, *item;
|
|
if (!ctx->num_enum)
|
return NULL;
|
|
root = cJSON_CreateObject();
|
DASSERT(root, return NULL);
|
|
for (int i = 0; i < ctx->num_enum; i++) {
|
j2s_enum* enum_obj = &ctx->enums[i];
|
|
item = _j2s_enum_to_json(ctx, i);
|
if (item)
|
cJSON_AddItemToObject(root, enum_obj->name, item);
|
}
|
|
return root;
|
}
|
|
static cJSON* _j2s_struct_to_template_json(j2s_ctx* ctx, int struct_index)
|
{
|
j2s_struct* struct_obj;
|
cJSON* json;
|
|
if (struct_index < 0)
|
return NULL;
|
|
struct_obj = &ctx->structs[struct_index];
|
if (struct_obj->child_index < 0)
|
return NULL;
|
|
DBG("start struct: %s\n", struct_obj->name);
|
j2s_template_dumping = true;
|
json = _j2s_struct_to_json(ctx, struct_index, NULL);
|
j2s_template_dumping = false;
|
DBG("finish struct: %s\n", struct_obj->name);
|
|
return json;
|
}
|
|
cJSON* j2s_struct_to_template_json(j2s_ctx* ctx, const char* name)
|
{
|
int struct_index = name ? j2s_find_struct_index(ctx, name) : ctx->root_index;
|
|
return _j2s_struct_to_template_json(ctx, struct_index);
|
}
|
|
int j2s_struct_size(j2s_ctx* ctx, int struct_index)
|
{
|
j2s_struct* struct_obj;
|
j2s_obj* child;
|
int child_index, child_size;
|
|
if (struct_index < 0)
|
return 0;
|
|
struct_obj = &ctx->structs[struct_index];
|
|
/* Find last child */
|
for (child = NULL, child_index = struct_obj->child_index; child_index >= 0;
|
child_index = child->next_index)
|
child = &ctx->objs[child_index];
|
|
if (!child)
|
return 0;
|
|
if (J2S_IS_POINTER(child)) {
|
child_size = (int)sizeof(void*);
|
} else if (J2S_IS_ARRAY(child)) {
|
child_size = child->elem_size * child->num_elem;
|
} else {
|
child_size = child->elem_size;
|
}
|
|
return child_size + child->offset;
|
}
|
|
const char* j2s_type_name(j2s_type type)
|
{
|
switch (type) {
|
case J2S_TYPE_INT_8:
|
return "int8_t";
|
case J2S_TYPE_UINT_8:
|
return "uint8_t";
|
case J2S_TYPE_INT_16:
|
return "int16_t";
|
case J2S_TYPE_UINT_16:
|
return "uint16_t";
|
case J2S_TYPE_INT_32:
|
return "int32_t";
|
case J2S_TYPE_UINT_32:
|
return "uint32_t";
|
case J2S_TYPE_INT_64:
|
return "int64_t";
|
case J2S_TYPE_UINT_64:
|
return "uint64_t";
|
case J2S_TYPE_FLOAT:
|
return "float";
|
case J2S_TYPE_DOUBLE:
|
return "double";
|
case J2S_TYPE_STRING:
|
return "char";
|
case J2S_TYPE_STRUCT:
|
return "struct";
|
default:
|
return "unknown";
|
}
|
}
|
|
/* Get number value from a obj */
|
static inline double j2s_obj_get_value(j2s_ctx* ctx, int obj_index, void* ptr)
|
{
|
j2s_obj* obj;
|
double value;
|
|
if (!ptr || obj_index < 0)
|
return 0;
|
|
obj = &ctx->objs[obj_index];
|
ptr += obj->offset;
|
|
#define J2S_FETCH_NUM(type, ptr) \
|
value = (double)*((type*)ptr); \
|
return value;
|
|
switch (obj->type) {
|
case J2S_TYPE_INT_8:
|
J2S_FETCH_NUM(int8_t, ptr);
|
case J2S_TYPE_UINT_8:
|
J2S_FETCH_NUM(uint8_t, ptr);
|
case J2S_TYPE_INT_16:
|
J2S_FETCH_NUM(int16_t, ptr);
|
case J2S_TYPE_UINT_16:
|
J2S_FETCH_NUM(uint16_t, ptr);
|
case J2S_TYPE_INT_32:
|
J2S_FETCH_NUM(int32_t, ptr);
|
case J2S_TYPE_UINT_32:
|
J2S_FETCH_NUM(uint32_t, ptr);
|
case J2S_TYPE_INT_64:
|
J2S_FETCH_NUM(int64_t, ptr);
|
case J2S_TYPE_UINT_64:
|
J2S_FETCH_NUM(uint64_t, ptr);
|
case J2S_TYPE_FLOAT:
|
J2S_FETCH_NUM(float, ptr);
|
case J2S_TYPE_DOUBLE:
|
J2S_FETCH_NUM(double, ptr);
|
default:
|
return 0;
|
}
|
}
|
|
/* Set number value to a obj */
|
static inline int j2s_obj_set_value(j2s_ctx* ctx, int obj_index, double value,
|
void* ptr)
|
{
|
j2s_obj* obj;
|
|
if (!ptr || obj_index < 0)
|
return 0;
|
|
obj = &ctx->objs[obj_index];
|
ptr += obj->offset;
|
|
#define J2S_STORE_NUM(type, value, ptr) \
|
*(type*)ptr = (type)value; \
|
return 0;
|
|
switch (obj->type) {
|
case J2S_TYPE_INT_8:
|
J2S_STORE_NUM(int8_t, value, ptr);
|
case J2S_TYPE_UINT_8:
|
J2S_STORE_NUM(uint8_t, value, ptr);
|
case J2S_TYPE_INT_16:
|
J2S_STORE_NUM(int16_t, value, ptr);
|
case J2S_TYPE_UINT_16:
|
J2S_STORE_NUM(uint16_t, value, ptr);
|
case J2S_TYPE_INT_32:
|
J2S_STORE_NUM(int32_t, value, ptr);
|
case J2S_TYPE_UINT_32:
|
J2S_STORE_NUM(uint32_t, value, ptr);
|
case J2S_TYPE_INT_64:
|
J2S_STORE_NUM(int64_t, value, ptr);
|
case J2S_TYPE_UINT_64:
|
J2S_STORE_NUM(uint64_t, value, ptr);
|
case J2S_TYPE_FLOAT:
|
J2S_STORE_NUM(float, value, ptr);
|
case J2S_TYPE_DOUBLE:
|
J2S_STORE_NUM(double, value, ptr);
|
default:
|
return 0;
|
}
|
}
|
|
/* Extract array to the first elem */
|
static inline void j2s_extract_array(j2s_obj* obj)
|
{
|
if (obj->flags & J2S_FLAG_DEP_ARRAY) {
|
obj->flags &= ~J2S_FLAG_DEP_ARRAY;
|
obj->num_elem = obj->elem_size / obj->base_elem_size;
|
} else {
|
obj->flags &= ~J2S_FLAG_ARRAY;
|
obj->num_elem = 1;
|
}
|
|
obj->elem_size = obj->base_elem_size;
|
}
|
|
/* Extract dynamic array to normal array */
|
static inline void* j2s_extract_dynamic_array(j2s_obj* obj, int len,
|
void* ptr)
|
{
|
if (!j2s_template_dumping) {
|
ptr += obj->offset;
|
ptr = *(void**)ptr;
|
if (!ptr)
|
return NULL;
|
}
|
|
obj->offset = 0;
|
obj->len_index = -1;
|
obj->num_elem = len;
|
|
if (obj->flags & J2S_FLAG_DEP_POINTER) {
|
obj->flags &= ~J2S_FLAG_DEP_POINTER;
|
} else {
|
obj->flags &= ~J2S_FLAG_ARRAY_POINTER;
|
obj->flags &= ~J2S_FLAG_POINTER;
|
}
|
|
if (obj->flags & J2S_FLAG_ARRAY) {
|
obj->flags |= J2S_FLAG_DEP_ARRAY;
|
} else {
|
obj->flags |= J2S_FLAG_ARRAY;
|
}
|
|
return ptr;
|
}
|
|
static cJSON* j2s_get_index_json(j2s_ctx* ctx, cJSON* parent, int obj_index)
|
{
|
j2s_obj* obj;
|
char index_name[MAX_NAME + 10];
|
|
if (obj_index < 0)
|
return NULL;
|
|
obj = &ctx->objs[obj_index];
|
|
/* Handle array with index obj @<name>_index */
|
snprintf(index_name, sizeof(index_name), "@%s_index", obj->name);
|
return cJSON_GetObjectItemCaseSensitive(parent, index_name);
|
}
|
|
static cJSON* _j2s_obj_to_json(j2s_ctx* ctx, int obj_index, void* ptr)
|
{
|
j2s_obj* obj;
|
cJSON* root;
|
double value;
|
|
if (obj_index < 0)
|
return NULL;
|
|
obj = &ctx->objs[obj_index];
|
|
DBG("handling obj: %s from %p[%d]\n", obj->name, ptr, obj->offset);
|
|
/* Handle simple string */
|
if (J2S_IS_SIMPLE_STRING(obj)) {
|
if (j2s_template_dumping)
|
return cJSON_CreateString("");
|
|
ptr += obj->offset;
|
if (obj->flags & J2S_FLAG_POINTER)
|
ptr = *(char**)ptr;
|
return cJSON_CreateString(ptr ?: "");
|
}
|
|
/* Handle array member */
|
if (J2S_IS_ARRAY(obj)) {
|
j2s_obj tmp_obj;
|
cJSON* item;
|
|
root = cJSON_CreateArray();
|
DASSERT(root, return NULL);
|
|
tmp_obj = *obj;
|
|
/* Walk into array */
|
j2s_extract_array(obj);
|
|
for (int i = 0; i < tmp_obj.num_elem; i++) {
|
DBG("handling array: %s %d/%d\n", obj->name, i, tmp_obj.num_elem);
|
|
item = _j2s_obj_to_json(ctx, obj_index, ptr);
|
if (item)
|
cJSON_AddItemToArray(root, item);
|
|
obj->offset += tmp_obj.elem_size;
|
}
|
|
*obj = tmp_obj;
|
return root;
|
}
|
|
/* Handle dynamic array */
|
if (J2S_IS_POINTER(obj)) {
|
j2s_obj tmp_obj;
|
int len;
|
|
DASSERT_MSG(obj->len_index >= 0, return NULL,
|
"dynamic array %s missing len\n", obj->name);
|
|
if (j2s_template_dumping) {
|
len = 1;
|
} else {
|
len = j2s_obj_get_value(ctx, obj->len_index, ptr);
|
}
|
|
if (!len)
|
return cJSON_CreateArray();
|
|
tmp_obj = *obj;
|
|
/* Walk into dynamic array */
|
ptr = j2s_extract_dynamic_array(obj, len, ptr);
|
DASSERT_MSG(j2s_template_dumping || ptr, return NULL,
|
"found null pointer at %s\n", obj->name);
|
|
DBG("handling dynamic array: %s %d*%d from %p\n", obj->name, obj->elem_size,
|
obj->num_elem, ptr);
|
|
root = _j2s_obj_to_json(ctx, obj_index, ptr);
|
|
*obj = tmp_obj;
|
return root;
|
}
|
|
/* Handle struct member */
|
if (obj->type == J2S_TYPE_STRUCT)
|
return _j2s_struct_to_json(ctx, obj->struct_index, ptr + obj->offset);
|
|
/* Handle basic member */
|
if (j2s_template_dumping) {
|
for (int i = 0; i < ctx->num_obj; i++) {
|
if (ctx->objs[i].len_index == obj_index)
|
return cJSON_CreateNumber(1);
|
}
|
|
if (obj->enum_index >= 0) {
|
/* Use first value as default */
|
j2s_enum* enum_obj = &ctx->enums[obj->enum_index];
|
j2s_enum_value* enum_value = &ctx->enum_values[enum_obj->value_index];
|
|
return cJSON_CreateString(enum_value->name);
|
}
|
|
return cJSON_CreateNumber(0);
|
}
|
|
value = j2s_obj_get_value(ctx, obj_index, ptr);
|
|
if (obj->enum_index >= 0) {
|
/* Convert enum value to name */
|
const char* name = j2s_enum_get_name(ctx, obj->enum_index, (int)value);
|
return cJSON_CreateString(name);
|
}
|
|
return cJSON_CreateNumber(value);
|
}
|
|
static cJSON* _j2s_struct_to_json(j2s_ctx* ctx, int struct_index, void* ptr)
|
{
|
j2s_struct* struct_obj;
|
j2s_obj* child;
|
cJSON *root, *item;
|
int child_index, ret = 0;
|
|
if (struct_index < 0)
|
return NULL;
|
|
struct_obj = &ctx->structs[struct_index];
|
if (struct_obj->child_index < 0)
|
return NULL;
|
|
root = cJSON_CreateObject();
|
DASSERT(root, return NULL);
|
|
DBG("start struct: %s from %p\n", struct_obj->name, ptr);
|
|
ret = -1;
|
|
/* Walk child list */
|
for (child_index = struct_obj->child_index; child_index >= 0;
|
child_index = child->next_index) {
|
child = &ctx->objs[child_index];
|
|
DBG("start child: %s (%s) from %p\n", child->name, struct_obj->name, ptr);
|
|
item = _j2s_obj_to_json(ctx, child_index, ptr);
|
DBG("finish child: %s (%s)\n", child->name, struct_obj->name);
|
|
if (item) {
|
if (ctx->dump_desc && child_index < ctx->num_desc) {
|
/* Dump desc to template JSON as @<member> */
|
char buf[MAX_NAME + 1] = "@";
|
|
const char* desc = ctx->descs[child_index];
|
if (desc) {
|
cJSON* json = cJSON_CreateString(desc);
|
DASSERT(json, goto out);
|
|
strcat(buf, child->name);
|
cJSON_AddItemToObject(root, buf, json);
|
}
|
}
|
|
cJSON_AddItemToObject(root, child->name, item);
|
}
|
}
|
|
ret = 0;
|
out:
|
DBG("finish struct: %s\n", struct_obj->name);
|
|
if (ret < 0) {
|
cJSON_Delete(root);
|
return NULL;
|
}
|
return root;
|
}
|
|
static int j2s_json_to_array_with_index(j2s_ctx* ctx, cJSON* json,
|
cJSON* index_json, cJSON* parent,
|
j2s_obj* obj, void* ptr, bool query)
|
{
|
j2s_obj tmp_obj;
|
cJSON *index_item, *item;
|
int size, index, ret = -1;
|
|
size = cJSON_GetArraySize(index_json);
|
if (!size)
|
return 0;
|
|
tmp_obj = *obj;
|
|
/* Walk into array */
|
j2s_extract_array(obj);
|
|
if (query) {
|
cJSON* root;
|
|
ret = 0;
|
|
/* Clear the original array */
|
root = cJSON_CreateArray();
|
cJSON_ReplaceItemInObjectCaseSensitive(parent, obj->name, root);
|
|
for (int i = 0; i < size; i++) {
|
index_item = cJSON_GetArrayItem(index_json, i);
|
index = cJSON_GetNumberValue(index_item);
|
obj->offset = tmp_obj.offset + index * tmp_obj.elem_size;
|
item = NULL;
|
|
DBG("handling index array: %s %d/%d\n", obj->name, index,
|
tmp_obj.num_elem);
|
|
/* Query item */
|
if (index < tmp_obj.num_elem)
|
item = _j2s_obj_to_json(ctx, obj - ctx->objs, ptr);
|
|
if (!item) {
|
item = cJSON_CreateObject();
|
if (!item) {
|
ret = -1;
|
break;
|
}
|
}
|
|
cJSON_AddItemToArray(root, item);
|
}
|
} else {
|
for (int i = 0; i < size; i++) {
|
index_item = cJSON_GetArrayItem(index_json, i);
|
index = cJSON_GetNumberValue(index_item);
|
obj->offset = tmp_obj.offset + index * tmp_obj.elem_size;
|
|
DBG("handling index array: %s %d/%d\n", obj->name, index,
|
tmp_obj.num_elem);
|
|
if (index >= tmp_obj.num_elem)
|
continue;
|
|
/* Apply item */
|
item = cJSON_GetArrayItem(json, i);
|
if (!item)
|
break;
|
|
ret = _j2s_json_to_obj(ctx, item, parent, obj - ctx->objs, ptr, false);
|
if (ret < 0)
|
break;
|
}
|
}
|
|
*obj = tmp_obj;
|
return ret;
|
}
|
|
static int _j2s_json_to_obj(j2s_ctx* ctx, cJSON* json, cJSON* parent,
|
int obj_index, void* ptr, bool query)
|
{
|
j2s_obj* obj;
|
cJSON* root = json;
|
int ret = 0;
|
|
if (obj_index < 0)
|
return -1;
|
|
obj = &ctx->objs[obj_index];
|
|
DBG("handling obj: %s from %p[%d]\n", obj->name, ptr, obj->offset);
|
|
/* Handle simple string */
|
if (J2S_IS_SIMPLE_STRING(obj)) {
|
ptr += obj->offset;
|
|
if (query) {
|
if (obj->flags == J2S_FLAG_POINTER)
|
ptr = *(char**)ptr;
|
|
cJSON_SetValuestring(root, ptr ?: "");
|
} else {
|
char* str = cJSON_GetStringValue(root);
|
|
if (obj->flags == J2S_FLAG_ARRAY) {
|
strncpy(ptr, str ?: "", obj->num_elem);
|
} else {
|
char** buf = (char**)ptr;
|
if (*buf)
|
free(*buf);
|
*buf = strdup(str ?: "");
|
}
|
}
|
|
return 0;
|
}
|
|
/* Handle array member */
|
if (J2S_IS_ARRAY(obj)) {
|
j2s_obj tmp_obj;
|
cJSON *item, *index_json;
|
|
index_json = j2s_get_index_json(ctx, parent, obj_index);
|
if (index_json && obj->type != J2S_TYPE_STRING && obj->flags != J2S_FLAG_ARRAY) {
|
cJSON_DetachItemViaPointer(parent, index_json);
|
index_json = NULL;
|
WARN("ignoring index for dep types %s\n", obj->name);
|
}
|
|
if (index_json)
|
return j2s_json_to_array_with_index(ctx, json, index_json, parent, obj,
|
ptr, query);
|
|
tmp_obj = *obj;
|
|
/* Walk into array */
|
j2s_extract_array(obj);
|
|
for (int i = 0; i < tmp_obj.num_elem; i++) {
|
DBG("handling array: %s %d/%d\n", obj->name, i, tmp_obj.num_elem);
|
|
item = cJSON_GetArrayItem(root, i);
|
if (!item)
|
continue;
|
|
ret = _j2s_json_to_obj(ctx, item, parent, obj_index, ptr, query);
|
if (ret < 0)
|
break;
|
|
obj->offset += tmp_obj.elem_size;
|
}
|
|
*obj = tmp_obj;
|
return ret;
|
}
|
|
/* Handle dynamic array */
|
if (J2S_IS_POINTER(obj)) {
|
j2s_obj tmp_obj;
|
cJSON *len_json, *index_json;
|
char* len_name;
|
int len, old_len;
|
|
DASSERT_MSG(obj->len_index >= 0, return -1,
|
"dynamic array %s missing len\n", obj->name);
|
|
len_name = ctx->objs[obj->len_index].name;
|
|
len_json = cJSON_GetObjectItemCaseSensitive(parent, len_name);
|
if (!len_json && !query)
|
WARN("missing len in json for dynamic array '%s'\n", obj->name);
|
|
index_json = j2s_get_index_json(ctx, parent, obj_index);
|
|
if (query && !index_json) {
|
/* Query dynamic array len */
|
if (len_json)
|
cJSON_DetachItemViaPointer(parent, len_json);
|
|
len_json = _j2s_obj_to_json(ctx, obj->len_index, ptr);
|
DASSERT_MSG(len_json, return -1, "failed to query %s\n", len_name);
|
|
cJSON_AddItemToObject(parent, len_name, len_json);
|
|
/* Force query the whole dynamic array */
|
cJSON_DetachItemViaPointer(parent, json);
|
cJSON_Delete(json);
|
|
json = _j2s_obj_to_json(ctx, obj_index, ptr);
|
DASSERT_MSG(json, return -1, "failed to query %s\n", obj->name);
|
|
cJSON_AddItemToObject(parent, obj->name, json);
|
return 0;
|
}
|
|
old_len = j2s_obj_get_value(ctx, obj->len_index, ptr);
|
|
if (len_json) {
|
len = cJSON_GetArraySize(json);
|
/* Fallback to array size */
|
cJSON_SetNumberValue(len_json, len);
|
} else if (index_json) {
|
len = old_len;
|
} else {
|
/* Fallback to array size */
|
len = cJSON_GetArraySize(json);
|
}
|
|
if (len != old_len) {
|
/* Dynamic array size changed, realloc it */
|
void** buf = (void**)(ptr + obj->offset);
|
|
if (old_len && *buf)
|
j2s_release_data(ctx, *buf);
|
|
*buf = j2s_alloc_data(ctx, len * obj->elem_size);
|
|
j2s_obj_set_value(ctx, obj->len_index, len, ptr);
|
|
DBG("re-alloc %s from %d*%d to %d*%d = %p\n", obj->name, old_len,
|
obj->elem_size, len, obj->elem_size, *buf);
|
}
|
|
if (!len)
|
return 0;
|
|
tmp_obj = *obj;
|
|
/* Walk into dynamic array */
|
ptr = j2s_extract_dynamic_array(obj, len, ptr);
|
DASSERT_MSG(ptr, return -1, "found null pointer at %s\n", obj->name);
|
|
DBG("handling dynamic array: %s %d*%d from %p\n", obj->name, obj->elem_size,
|
obj->num_elem, ptr);
|
|
ret = _j2s_json_to_obj(ctx, root, parent, obj_index, ptr, query);
|
|
*obj = tmp_obj;
|
return ret;
|
}
|
|
/* Handle struct member */
|
if (obj->type == J2S_TYPE_STRUCT)
|
return _j2s_json_to_struct(ctx, root, obj->struct_index, ptr + obj->offset,
|
query);
|
|
/* Handle basic member */
|
if (query) {
|
double value = j2s_obj_get_value(ctx, obj_index, ptr);
|
|
if (obj->enum_index >= 0) {
|
/* Convert enum value to name */
|
const char* name = j2s_enum_get_name(ctx, obj->enum_index, (int)value);
|
cJSON_SetValuestring(root, name);
|
return 0;
|
}
|
|
cJSON_SetNumberValue(root, value);
|
return 0;
|
} else {
|
double value;
|
|
if (obj->enum_index >= 0) {
|
/* Convert enum name to value */
|
char* name = cJSON_GetStringValue(root);
|
|
value = (double)j2s_enum_get_value(ctx, obj->enum_index, name);
|
} else {
|
value = cJSON_GetNumberValue(root);
|
}
|
|
j2s_obj_set_value(ctx, obj_index, value, ptr);
|
return 0;
|
}
|
}
|
|
static int _j2s_json_to_struct(j2s_ctx* ctx, cJSON* json, int struct_index,
|
void* ptr, bool query)
|
{
|
j2s_struct* struct_obj;
|
j2s_obj* child;
|
cJSON *item, *root = json;
|
int child_index, ret = 0;
|
|
if (struct_index < 0)
|
return -1;
|
|
struct_obj = &ctx->structs[struct_index];
|
|
DBG("start struct: %s from %p\n", struct_obj->name, ptr);
|
|
/* Walk child list */
|
for (child_index = struct_obj->child_index; child_index >= 0;
|
child_index = child->next_index) {
|
child = &ctx->objs[child_index];
|
|
item = cJSON_GetObjectItemCaseSensitive(root, child->name);
|
if (!item)
|
continue;
|
|
DBG("start child: %s (%s) from %p\n", child->name, struct_obj->name, ptr);
|
ret = _j2s_json_to_obj(ctx, item, root, child_index, ptr, query);
|
DBG("finish child: %s (%s)\n", child->name, struct_obj->name);
|
if (ret < 0)
|
break;
|
}
|
|
DBG("finish struct: %s\n", struct_obj->name);
|
return ret;
|
}
|
|
static int j2s_restore_obj(j2s_ctx* ctx, j2s_obj* obj, int fd, void* ptr)
|
{
|
char buf[MAX_NAME];
|
void** data_ptr;
|
int size;
|
|
if (!J2S_IS_POINTER(obj))
|
return 0;
|
|
data_ptr = (void**)(ptr + obj->offset);
|
|
if (read(fd, buf, MAX_NAME) != MAX_NAME)
|
return -1;
|
|
if (strncmp(obj->name, buf, MAX_NAME) < 0)
|
return -1;
|
|
if (read(fd, &size, sizeof(int)) != sizeof(int))
|
return -1;
|
|
if (!size) {
|
*data_ptr = NULL;
|
return 0;
|
}
|
|
*data_ptr = j2s_alloc_data(ctx, size);
|
if (!*data_ptr)
|
return -1;
|
|
if (read(fd, *data_ptr, size) != size) {
|
j2s_release_data(ctx, *data_ptr);
|
return -1;
|
}
|
|
DBG("restore obj: %s to %p, size %d\n", obj->name, *data_ptr, size);
|
|
return size;
|
}
|
|
static void j2s_store_obj(j2s_obj* obj, int fd, void* ptr)
|
{
|
char buf[MAX_NAME] = { 0 };
|
int size;
|
ssize_t bytes_written = 0;
|
|
if (!J2S_IS_POINTER(obj))
|
return;
|
|
ptr = *(void**)(ptr + obj->offset);
|
if (!ptr)
|
return;
|
|
if (J2S_IS_SIMPLE_STRING(obj)) {
|
size = strlen(ptr) + 1;
|
} else {
|
size = obj->num_elem * obj->elem_size;
|
}
|
|
DBG("store obj: %s from %p, size %d\n", obj->name, ptr, size);
|
|
strcpy(buf, obj->name);
|
bytes_written = write(fd, buf, MAX_NAME);
|
bytes_written = write(fd, &size, sizeof(int));
|
bytes_written = write(fd, ptr, size);
|
}
|
|
static int _j2s_obj_from_cache(j2s_ctx* ctx, int obj_index, int fd, void* ptr)
|
{
|
j2s_obj* obj;
|
int ret = 0;
|
|
if (obj_index < 0)
|
return -1;
|
|
obj = &ctx->objs[obj_index];
|
|
DBG("handling obj: %s from %p[%d]\n", obj->name, ptr, obj->offset);
|
|
/* Handle simple string */
|
if (J2S_IS_SIMPLE_STRING(obj))
|
return j2s_restore_obj(ctx, obj, fd, ptr);
|
|
/* Handle array member */
|
if (J2S_IS_ARRAY(obj)) {
|
j2s_obj tmp_obj = *obj;
|
|
if (obj->type != J2S_TYPE_STRUCT && obj->type != J2S_TYPE_STRING)
|
return 0;
|
|
/* Walk into array */
|
j2s_extract_array(obj);
|
|
for (int i = 0; i < tmp_obj.num_elem; i++) {
|
DBG("handling array: %s %d/%d\n", obj->name, i, tmp_obj.num_elem);
|
|
ret = _j2s_obj_from_cache(ctx, obj_index, fd, ptr);
|
if (ret < 0)
|
break;
|
|
obj->offset += tmp_obj.elem_size;
|
}
|
|
*obj = tmp_obj;
|
return ret;
|
}
|
|
/* Handle dynamic array */
|
if (J2S_IS_POINTER(obj)) {
|
j2s_obj tmp_obj = *obj;
|
int size;
|
|
size = j2s_restore_obj(ctx, obj, fd, ptr);
|
if (size <= 0)
|
return size;
|
|
/* Walk into dynamic array */
|
ptr = j2s_extract_dynamic_array(obj, size / obj->elem_size, ptr);
|
|
DBG("handling dynamic array: %s %d*%d from %p\n", obj->name, obj->elem_size,
|
obj->num_elem, ptr);
|
|
ret = _j2s_obj_from_cache(ctx, obj_index, fd, ptr);
|
|
*obj = tmp_obj;
|
return ret;
|
}
|
|
/* Handle struct member */
|
if (obj->type == J2S_TYPE_STRUCT)
|
return _j2s_struct_from_cache(ctx, obj->struct_index, fd,
|
ptr + obj->offset);
|
|
return 0;
|
}
|
|
static int _j2s_struct_from_cache(j2s_ctx* ctx, int struct_index, int fd,
|
void* ptr)
|
{
|
j2s_struct* struct_obj;
|
j2s_obj* child;
|
int child_index;
|
|
if (struct_index < 0)
|
return -1;
|
|
if (struct_index == ctx->root_index) {
|
int root_size = j2s_struct_size(ctx, ctx->root_index);
|
if (read(fd, ptr, root_size) != root_size)
|
return -1;
|
}
|
|
struct_obj = &ctx->structs[struct_index];
|
|
/* Walk child list */
|
for (child_index = struct_obj->child_index; child_index >= 0;
|
child_index = child->next_index) {
|
child = &ctx->objs[child_index];
|
if (_j2s_obj_from_cache(ctx, child_index, fd, ptr) < 0)
|
return -1;
|
}
|
|
return 0;
|
}
|
|
static void _j2s_obj_to_cache(j2s_ctx* ctx, int obj_index, int fd, void* ptr)
|
{
|
j2s_obj* obj;
|
|
if (obj_index < 0)
|
return;
|
|
obj = &ctx->objs[obj_index];
|
|
DBG("handling obj: %s from %p[%d]\n", obj->name, ptr, obj->offset);
|
|
/* Handle simple string */
|
if (J2S_IS_SIMPLE_STRING(obj)) {
|
j2s_store_obj(obj, fd, ptr);
|
return;
|
}
|
|
/* Handle array member */
|
if (J2S_IS_ARRAY(obj)) {
|
j2s_obj tmp_obj = *obj;
|
|
if (obj->type != J2S_TYPE_STRUCT && obj->type != J2S_TYPE_STRING)
|
return;
|
|
/* Walk into array */
|
j2s_extract_array(obj);
|
|
for (int i = 0; i < tmp_obj.num_elem; i++) {
|
DBG("handling array: %s %d/%d\n", obj->name, i, tmp_obj.num_elem);
|
|
_j2s_obj_to_cache(ctx, obj_index, fd, ptr);
|
obj->offset += tmp_obj.elem_size;
|
}
|
|
*obj = tmp_obj;
|
return;
|
}
|
|
/* Handle dynamic array */
|
if (J2S_IS_POINTER(obj)) {
|
j2s_obj tmp_obj = *obj;
|
int len;
|
|
if (obj->len_index < 0)
|
return;
|
|
len = j2s_obj_get_value(ctx, obj->len_index, ptr);
|
if (!len)
|
return;
|
|
obj->num_elem = len;
|
j2s_store_obj(obj, fd, ptr);
|
|
/* Walk into dynamic array */
|
ptr = j2s_extract_dynamic_array(obj, len, ptr);
|
|
DBG("handling dynamic array: %s %d*%d from %p\n", obj->name, obj->elem_size,
|
obj->num_elem, ptr);
|
|
_j2s_obj_to_cache(ctx, obj_index, fd, ptr);
|
|
*obj = tmp_obj;
|
return;
|
}
|
|
/* Handle struct member */
|
if (obj->type == J2S_TYPE_STRUCT)
|
_j2s_struct_to_cache(ctx, obj->struct_index, fd, ptr + obj->offset);
|
}
|
|
static void _j2s_struct_to_cache(j2s_ctx* ctx, int struct_index, int fd,
|
void* ptr)
|
{
|
j2s_struct* struct_obj;
|
j2s_obj* child;
|
int child_index;
|
ssize_t bytes_written = 0;
|
|
if (struct_index < 0)
|
return;
|
|
if (struct_index == ctx->root_index)
|
bytes_written = write(fd, ptr, j2s_struct_size(ctx, struct_index));
|
|
struct_obj = &ctx->structs[struct_index];
|
|
/* Walk child list */
|
for (child_index = struct_obj->child_index; child_index >= 0;
|
child_index = child->next_index) {
|
child = &ctx->objs[child_index];
|
_j2s_obj_to_cache(ctx, child_index, fd, ptr);
|
}
|
}
|