/* 
 | 
 *  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 <sys/stat.h> 
 | 
  
 | 
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 ?: "<null>"); 
 | 
  
 | 
    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 ?: "<NULL>"); 
 | 
        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 ?: "<null>"); 
 | 
  
 | 
    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; 
 | 
} 
 |