/* * 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" #ifdef __ANDROID__ #if defined(ISP_HW_V20) #include "j2s_generated_v20.h" #elif defined(ISP_HW_V21) #include "j2s_generated_v21.h" #else #error "Please define supported ISP version!!!, eg: -DISP_HW_V21" #endif #else #include "j2s_generated.h" #endif #include typedef struct { void* ptr; bool freeable; } j2s_ptr; typedef struct { int num_data; j2s_ptr* data; } j2s_priv_data; void* j2s_alloc_data(j2s_ctx* ctx, size_t size) { void* ptr = calloc(size, 1); if (!ptr) return NULL; if (j2s_add_data(ctx, ptr, false) < 0) { free(ptr); return NULL; } return ptr; } int j2s_add_data(j2s_ctx* ctx, void* ptr, bool freeable) { j2s_priv_data* priv; if (!ctx->priv) ctx->priv = calloc(1, sizeof(j2s_priv_data)); priv = ctx->priv; for (int i = 0; i < priv->num_data; i++) { j2s_ptr* data = &priv->data[i]; if (data->ptr) continue; data->ptr = ptr; data->freeable = freeable; return 0; } priv->num_data++; priv->data = realloc(priv->data, priv->num_data * sizeof(j2s_ptr)); if (!priv->data) { ERR("failed to realloc\n"); priv->num_data = 0; return -1; } priv->data[priv->num_data - 1].ptr = ptr; priv->data[priv->num_data - 1].freeable = freeable; return 0; } void j2s_release_data(j2s_ctx* ctx, void* ptr) { j2s_priv_data* priv = ctx->priv; void* free_ptr = ptr; for (int i = 0; priv && i < priv->num_data; i++) { j2s_ptr* data = &priv->data[i]; if (ptr != data->ptr) continue; if (data->ptr && data->freeable) { free(data->ptr); free_ptr = NULL; } data->ptr = NULL; //return; } if (free_ptr) free(ptr); } void* j2s_read_file(const char* file, size_t* size) { struct stat st; void* buf; int fd; DASSERT_MSG(file && !stat(file, &st), return NULL, "no such file: '%s'\n", file ?: ""); fd = open(file, O_RDONLY); DASSERT_MSG(fd >= 0, return NULL, "failed to open: '%s'\n", file); buf = malloc(st.st_size + 1); DASSERT(buf, return NULL); DBG("Read file: '%s'\n", file); if (read(fd, buf, st.st_size) != st.st_size) { ERR("failed to read: '%s'\n", file); free(buf); close(fd); return NULL; } ((char*)buf)[st.st_size] = '\0'; *size = st.st_size; close(fd); return buf; } static char* j2s_cache_file(const char* file) { char cache_file[256]; if (getenv("J2S_NO_CACHE")) { DBG("Cache not allowed\n"); return NULL; } strcpy(cache_file, getenv("J2S_CACHE") ?: "/var/cache/j2s-cache"); /* NULL for ctx cache file */ if (!file) return strdup(cache_file); strcat(cache_file, "-"); strcat(cache_file, strrchr(file, '/') ? strrchr(file, '/') + 1 : file); for (int i = 0; cache_file[i]; i++) { if (cache_file[i] == '.') cache_file[i] = '-'; } return strdup(cache_file); } static int j2s_cache_file_valid(const char* cache_file) { struct stat st; if (!cache_file || stat(cache_file, &st) < 0) { DBG("invalid cache: '%s'\n", cache_file ?: ""); return -1; } if (getuid() != st.st_uid) { DBG("invalid cache: '%s'\n", cache_file); return -1; } return 0; } static __attribute__((unused)) int j2s_load_ctx_cache(j2s_ctx* ctx, const char* cache_file) { void *buf, *ptr; size_t size; if (j2s_cache_file_valid(cache_file) < 0) return -1; buf = j2s_read_file(cache_file, &size); if (!buf || size <= sizeof(*ctx)) { DBG("invalid cache: '%s'\n", cache_file); goto err; } DBG("Loading ctx cache: '%s'\n", cache_file); ptr = buf; *ctx = *(j2s_ctx*)ptr; ctx->priv = NULL; ptr += sizeof(*ctx); if (ctx->magic != J2S_MAGIC || ctx->num_obj != J2S_NUM_OBJ || ctx->num_struct != J2S_NUM_STRUCT || ctx->num_enum != J2S_NUM_ENUM || ctx->num_enum_value != J2S_NUM_ENUM_VALUE) { DBG("invalid cache: '%s'\n", cache_file); goto err; } ctx->objs = ptr; ptr += ctx->num_obj * sizeof(*ctx->objs); ctx->structs = ptr; ptr += ctx->num_struct * sizeof(*ctx->structs); ctx->enums = ptr; ptr += ctx->num_enum * sizeof(*ctx->enums); ctx->enum_values = ptr; ptr += ctx->num_enum_value * sizeof(*ctx->enum_values); if (ptr != buf + size) { DBG("invalid cache: '%s'\n", cache_file); goto err; } if (j2s_add_data(ctx, buf, true) < 0) goto err; return 0; err: j2s_deinit(ctx); free(buf); return -1; } static __attribute__((unused)) void j2s_save_ctx_cache(j2s_ctx* ctx, const char* cache_file) { int fd; ssize_t bytes_written = 0; if (!cache_file) return; fd = creat(cache_file, S_IRUSR | S_IWUSR); if (fd < 0) { DBG("failed to create: '%s'\n", cache_file); return; } DBG("Saving ctx cache: '%s'\n", cache_file); ctx->num_desc = 0; bytes_written = write(fd, ctx, sizeof(*ctx)); bytes_written = write(fd, ctx->objs, ctx->num_obj * sizeof(*ctx->objs)); bytes_written = write(fd, ctx->structs, ctx->num_struct * sizeof(*ctx->structs)); bytes_written = write(fd, ctx->enums, ctx->num_enum * sizeof(*ctx->enums)); bytes_written = write(fd, ctx->enum_values, ctx->num_enum_value * sizeof(*ctx->enum_values)); close(fd); } void j2s_init(j2s_ctx* ctx) { DBG("J2S version: %s\n", J2S_VERSION); #ifdef J2S_ENABLE_DESC _j2s_init(ctx); #else char* cache_file = j2s_cache_file(NULL); if (j2s_load_ctx_cache(ctx, cache_file) < 0) { _j2s_init(ctx); j2s_save_ctx_cache(ctx, cache_file); } if (cache_file) free(cache_file); #endif ctx->manage_data = true; } void j2s_deinit(j2s_ctx* ctx) { j2s_priv_data* priv = ctx->priv; for (int i = 0; priv && i < priv->num_data; i++) { j2s_ptr* data = &priv->data[i]; if (!data->ptr || !data->freeable) continue; /* Always free the cache file buf */ if (ctx->manage_data || data->ptr + sizeof(*ctx) == ctx->objs) free(data->ptr); } if (priv) { if (priv->data) free(priv->data); free(priv); } } int j2s_load_struct_cache(j2s_ctx* ctx, const char* cache_file, void* ptr, void* auth_data, int auth_size) { int fd, ret = -1; if (j2s_cache_file_valid(cache_file) < 0) return -1; fd = open(cache_file, O_RDONLY); if (fd < 0) { DBG("failed to open: '%s'\n", cache_file); return -1; } DBG("Loading struct cache: '%s'\n", cache_file); /* The cache file should start with auth data */ if (auth_data && auth_size) { void* buf = malloc(auth_size); if (!buf) goto out; if (read(fd, buf, auth_size) != auth_size) { free(buf); goto out; } if (memcmp(buf, auth_data, auth_size)) { free(buf); goto out; } free(buf); } if (j2s_root_struct_from_cache(ctx, fd, ptr) < 0) goto out; /* Check end of file */ if (read(fd, &ret, 1) > 0) goto out; DBG("Loaded struct cache: '%s'\n", cache_file); ret = 0; out: close(fd); return ret; } void j2s_save_struct_cache(j2s_ctx* ctx, const char* cache_file, void* ptr, void* auth_data, int auth_size) { int fd; ssize_t bytes_written = 0; fd = creat(cache_file, S_IRUSR | S_IWUSR); if (fd < 0) { DBG("failed to create: '%s'\n", cache_file); return; } DBG("Saving struct cache: '%s'\n", cache_file); if (auth_data && auth_size) bytes_written = write(fd, auth_data, auth_size); j2s_root_struct_to_cache(ctx, fd, ptr); close(fd); } int j2s_json_file_to_struct(j2s_ctx* ctx, const char* file, const char* name, void* ptr) { char* cache_file; struct stat st; size_t size; char* buf; int ret = -1; DASSERT_MSG(file && !stat(file, &st), return -1, "no such file: '%s'\n", file ?: ""); cache_file = j2s_cache_file(file); /* Using the file stat as auth data */ if (!j2s_load_struct_cache(ctx, cache_file, ptr, &st, sizeof(st))) { free(cache_file); return 0; } memset(ptr, 0, j2s_struct_size(ctx, ctx->root_index)); buf = j2s_read_file(file, &size); if (!buf) goto out; DBG("Parse file: '%s', content:\n%s\n", file, buf); if (j2s_modify_struct(ctx, buf, name, ptr) < 0) goto out; j2s_save_struct_cache(ctx, cache_file, ptr, &st, sizeof(st)); ret = 0; out: free(cache_file); if (buf) free(buf); return ret; } char* j2s_dump_struct(j2s_ctx* ctx, const char* name, void* ptr) { cJSON *json, *item; char* buf; DBG("Dump: %s\n", name ? name : "root struct"); if (!name) { json = j2s_root_struct_to_json(ctx, ptr); } else { json = j2s_struct_to_json(ctx, name, ptr); } DASSERT(json, return NULL); if (ctx->dump_enums) { item = j2s_enums_to_json(ctx); if (item) cJSON_AddItemToObject(json, "@enum", item); } if (ctx->format_json) { buf = cJSON_Print(json); } else { buf = cJSON_PrintUnformatted(json); } cJSON_Delete(json); return buf; } int j2s_modify_struct(j2s_ctx* ctx, const char* str, const char* name, void* ptr) { cJSON* json; int ret = -1; json = cJSON_Parse(str); DASSERT_MSG(json, return -1, "failed to parse: '%s'\n", str); DBG("Modify:\n%s\n", str); ret = j2s_json_to_struct(ctx, json, name, ptr); cJSON_Delete(json); return ret; } char* j2s_query_struct(j2s_ctx* ctx, const char* str, void* ptr) { cJSON* json; char* buf; json = cJSON_Parse(str); DASSERT_MSG(json, return NULL, "failed to parse: '%s'\n", str); DBG("Query:\n%s\n", str); if (j2s_json_from_root_struct(ctx, json, ptr) < 0) { cJSON_Delete(json); return NULL; } if (ctx->format_json) { buf = cJSON_Print(json); } else { buf = cJSON_PrintUnformatted(json); } cJSON_Delete(json); return buf; } char* j2s_dump_template_struct(j2s_ctx* ctx, const char* name) { cJSON *json, *item; char* buf; DBG("Dump template: %s\n", name ? name : "root struct"); if (!name) { json = j2s_root_struct_to_template_json(ctx); } else { json = j2s_struct_to_template_json(ctx, name); } DASSERT(json, return NULL); if (ctx->dump_enums) { item = j2s_enums_to_json(ctx); if (item) cJSON_AddItemToObject(json, "@enum", item); } if (ctx->format_json) { buf = cJSON_Print(json); } else { buf = cJSON_PrintUnformatted(json); } cJSON_Delete(json); return buf; } char* j2s_dump_structs(j2s_ctx* ctx, j2s_struct_info* info) { cJSON *json, *item; char* buf; if (!info || !info->name) return NULL; DBG("Dump structs\n"); json = cJSON_CreateObject(); DASSERT(json, return NULL); for (; info->name; info++) { item = j2s_struct_to_json(ctx, info->name, info->ptr); if (!item) continue; cJSON_AddItemToObject(json, info->name, item); } if (ctx->dump_enums) { item = j2s_enums_to_json(ctx); if (item) cJSON_AddItemToObject(json, "@enum", item); } if (ctx->format_json) { buf = cJSON_Print(json); } else { buf = cJSON_PrintUnformatted(json); } cJSON_Delete(json); return buf; }