/*************************************************************************\ * Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * SPDX-License-Identifier: EPICS * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbConvertJSON.c */ #include #include #include "dbDefs.h" #include "errlog.h" #include "yajl_alloc.h" #include "yajl_parse.h" #include "dbAccessDefs.h" #include "dbConvertFast.h" #include "dbConvertJSON.h" typedef long (*FASTCONVERT)(); typedef struct parseContext { int depth; short dbrType; short dbrSize; char *pdest; size_t elems; } parseContext; static int dbcj_null(void *ctx) { errlogPrintf("dbConvertJSON: Null objects not supported\n"); return 0; /* Illegal */ } static int dbcj_boolean(void *ctx, int val) { errlogPrintf("dbConvertJSON: Boolean not supported\n"); return 0; /* Illegal */ } static int dbcj_integer(void *ctx, long long num) { parseContext *parser = (parseContext *) ctx; epicsInt64 val64 = num; FASTCONVERT conv = dbFastPutConvertRoutine[DBF_INT64][parser->dbrType]; if (parser->elems > 0) { conv(&val64, parser->pdest, NULL); parser->pdest += parser->dbrSize; parser->elems--; } return 1; } static int dbcj_double(void *ctx, double num) { parseContext *parser = (parseContext *) ctx; FASTCONVERT conv = dbFastPutConvertRoutine[DBF_DOUBLE][parser->dbrType]; if (parser->elems > 0) { conv(&num, parser->pdest, NULL); parser->pdest += parser->dbrSize; parser->elems--; } return 1; } static int dbcj_string(void *ctx, const unsigned char *val, size_t len) { parseContext *parser = (parseContext *) ctx; char *pdest = parser->pdest; if (parser->dbrType == DBF_CHAR || parser->dbrType == DBF_UCHAR) { /* Treating char array as long string */ if (len > parser->elems) len = parser->elems; strncpy(pdest, (const char *) val, len); parser->elems -= len; parser->pdest += len; return 1; } if (parser->dbrType != DBF_STRING) { errlogPrintf("dbConvertJSON: String \"%.*s\" provided, numeric value expected\n", (int)len, val); return 0; /* Illegal */ } if (parser->elems > 0) { if (len > parser->dbrSize - 1) len = parser->dbrSize - 1; strncpy(pdest, (const char *) val, len); pdest[len] = 0; parser->pdest += parser->dbrSize; parser->elems--; } return 1; } static int dbcj_start_map(void *ctx) { errlogPrintf("dbConvertJSON: Map type not supported\n"); return 0; /* Illegal */ } static int dbcj_start_array(void *ctx) { parseContext *parser = (parseContext *) ctx; if (++parser->depth > 1) errlogPrintf("dbConvertJSON: Embedded arrays not supported\n"); return (parser->depth == 1); } static yajl_callbacks dbcj_callbacks = { dbcj_null, dbcj_boolean, dbcj_integer, dbcj_double, NULL, dbcj_string, dbcj_start_map, NULL, NULL, dbcj_start_array, NULL }; long dbPutConvertJSON(const char *json, short dbrType, void *pdest, long *pnRequest) { parseContext context, *parser = &context; yajl_handle yh; yajl_status ys; size_t jlen = strlen(json); long status; if (INVALID_DB_REQ(dbrType)) { errlogPrintf("dbConvertJSON: Invalid dbrType %d\n", dbrType); return S_db_badDbrtype; } if (!jlen) { *pnRequest = 0; return 0; } if (!jlen) { *pnRequest = 0; return 0; } parser->depth = 0; parser->dbrType = dbrType; parser->dbrSize = dbValueSize(dbrType); parser->pdest = pdest; parser->elems = *pnRequest; yh = yajl_alloc(&dbcj_callbacks, NULL, parser); if (!yh) { errlogPrintf("dbConvertJSON: out of memory\n"); return S_db_noMemory; } ys = yajl_parse(yh, (const unsigned char *) json, jlen); if (ys == yajl_status_ok) ys = yajl_complete_parse(yh); switch (ys) { case yajl_status_ok: *pnRequest -= parser->elems; status = 0; break; default: { unsigned char *err = yajl_get_error(yh, 1, (const unsigned char *) json, jlen); errlogPrintf("dbConvertJSON: %s", err); yajl_free_error(yh, err); status = S_db_badField; } } yajl_free(yh); return status; } long dbLSConvertJSON(const char *json, char *pdest, epicsUInt32 size, epicsUInt32 *plen) { long nRequest = size-1; long status; if (!size) { *plen = 0; return 0; } status = dbPutConvertJSON(json, DBF_CHAR, pdest, &nRequest); pdest[nRequest++] = 0; *plen = nRequest; return status; }