/** * \file *
 * modp_json.c High performance JSON encoder/decoder
 * https://github.com/client9/stringencoders
 * NOTE THIS IS UNTESTED AND EXPERIMENTAL.
 *
 * Copyright © 2006-2016 Nick Galbreath
 * All rights reserved.
 * Released under MIT license.  See LICENSE for details.
 * 
*/ #include "modp_json.h" #include "modp_json_data.h" #include typedef enum { JSON_NONE, JSON_MAP_OPEN, JSON_MAP_CLOSE, JSON_MAP_KEY, JSON_MAP_VAL, JSON_ARY_OPEN, JSON_ARY_CLOSE, JSON_ARY_VAL, } json_state_t; static size_t modp_bjson_encode_strlen(const char* src, size_t len); static size_t modp_bjson_encode(char* dest, const char* src, size_t len); static void modp_json_add_char(modp_json_ctx* ctx, int c); static void modp_json_add_value(modp_json_ctx* ctx); static void modp_json_add_false(modp_json_ctx* ctx); static void modp_json_add_true(modp_json_ctx* ctx); static void modp_json_add_char(modp_json_ctx* ctx, int c) { if (ctx->dest) { *(ctx->dest + ctx->size) = (char)c; } ctx->size += 1; } static void modp_json_add_value(modp_json_ctx* ctx) { int depth = ctx->depth; switch (ctx->state[depth]) { case JSON_NONE: /* no-op */ break; case JSON_MAP_OPEN: /* NO comma */ ctx->state[depth] = JSON_MAP_KEY; break; case JSON_ARY_OPEN: /* NO comma */ ctx->state[depth] = JSON_ARY_VAL; break; case JSON_ARY_VAL: modp_json_add_char(ctx, ','); break; case JSON_MAP_KEY: modp_json_add_char(ctx, ':'); ctx->state[depth] = JSON_MAP_VAL; break; case JSON_MAP_VAL: modp_json_add_char(ctx, ','); ctx->state[depth] = JSON_MAP_KEY; break; } } void modp_json_init(modp_json_ctx* ctx, char* dest) { memset((void*)ctx, 0, sizeof(modp_json_ctx)); ctx->dest = dest; } size_t modp_json_end(modp_json_ctx* ctx) { if (ctx->dest) { *(ctx->dest + ctx->size) = '\0'; } return ctx->size; } void modp_json_map_open(modp_json_ctx* ctx) { modp_json_add_value(ctx); ctx->depth++; ctx->state[ctx->depth] = JSON_MAP_OPEN; modp_json_add_char(ctx, '{'); } void modp_json_map_close(modp_json_ctx* ctx) { assert(ctx->depth > 0); ctx->depth--; modp_json_add_char(ctx, '}'); } void modp_json_ary_open(modp_json_ctx* ctx) { modp_json_add_value(ctx); ctx->depth++; ctx->state[ctx->depth] = JSON_ARY_OPEN; modp_json_add_char(ctx, '['); } void modp_json_ary_close(modp_json_ctx* ctx) { assert(ctx->depth > 0); ctx->depth--; modp_json_add_char(ctx, ']'); } static void modp_json_add_true(modp_json_ctx* ctx) { char* wstr; if (ctx->dest) { wstr = ctx->dest + ctx->size; wstr[0] = 't'; wstr[1] = 'r'; wstr[2] = 'u'; wstr[3] = 'e'; } ctx->size += 4; } static void modp_json_add_false(modp_json_ctx* ctx) { char* wstr; if (ctx->dest) { wstr = ctx->dest + ctx->size; wstr[0] = 'f'; wstr[1] = 'a'; wstr[2] = 'l'; wstr[3] = 's'; wstr[4] = 'e'; } ctx->size += 5; } void modp_json_add_bool(modp_json_ctx* ctx, int val) { modp_json_add_value(ctx); if (val) { modp_json_add_true(ctx); } else { modp_json_add_false(ctx); } } void modp_json_add_null(modp_json_ctx* ctx) { char* wstr; modp_json_add_value(ctx); if (ctx->dest) { wstr = ctx->dest + ctx->size; wstr[0] = 'n'; wstr[1] = 'u'; wstr[2] = 'l'; wstr[3] = 'l'; } ctx->size += 4; } void modp_json_add_uint64(modp_json_ctx* ctx, uint64_t uv, int stringonly) { size_t r = (uv >= 10000000000000000000ULL) ? 20 : (uv >= 1000000000000000000ULL) ? 19 : (uv >= 100000000000000000ULL) ? 18 : (uv >= 10000000000000000ULL) ? 17 : (uv >= 1000000000000000ULL) ? 16 : (uv >= 100000000000000ULL) ? 15 : (uv >= 10000000000000ULL) ? 14 : (uv >= 1000000000000ULL) ? 13 : (uv >= 100000000000ULL) ? 12 : (uv >= 10000000000ULL) ? 11 : (uv >= 1000000000ULL) ? 10 : (uv >= 100000000ULL) ? 9 : (uv >= 10000000ULL) ? 8 : (uv >= 1000000ULL) ? 7 : (uv >= 100000ULL) ? 6 : (uv >= 10000ULL) ? 5 : (uv >= 1000ULL) ? 4 : (uv >= 100ULL) ? 3 : (uv >= 10ULL) ? 2 : 1ULL; if (uv > (1ULL << 53)) { stringonly = 1; } modp_json_add_value(ctx); if (ctx->dest) { char* wstr = ctx->dest + ctx->size; if (stringonly) { wstr[0] = '"'; wstr[r + 1] = '"'; wstr += r; } else { wstr += r - 1; } /* Conversion. Number is reversed. */ do *wstr-- = (char)(48 + (uv % 10)); while (uv /= 10); } if (stringonly) { r += 2; } ctx->size += r; } void modp_json_add_int32(modp_json_ctx* ctx, int v) { char* wstr; if (v > 0) { return modp_json_add_uint32(ctx, (uint32_t)v); } uint32_t uv = (uint32_t)(-v); size_t r = (uv >= 1000000000) ? 10 : (uv >= 100000000) ? 9 : (uv >= 10000000) ? 8 : (uv >= 1000000) ? 7 : (uv >= 100000) ? 6 : (uv >= 10000) ? 5 : (uv >= 1000) ? 4 : (uv >= 100) ? 3 : (uv >= 10) ? 2 : 1; modp_json_add_value(ctx); if (ctx->dest) { wstr = ctx->dest + ctx->size; *wstr = '-'; wstr += r; /* Conversion. Number is reversed. */ do *wstr-- = (char)(48 + (uv % 10)); while (uv /= 10); } ctx->size += r + 1; /* +1 for '-' minus sign */ } void modp_json_add_uint32(modp_json_ctx* ctx, uint32_t uv) { char* wstr; size_t r = (uv >= 1000000000UL) ? 10 : (uv >= 100000000UL) ? 9 : (uv >= 10000000UL) ? 8 : (uv >= 1000000UL) ? 7 : (uv >= 100000UL) ? 6 : (uv >= 10000UL) ? 5 : (uv >= 1000UL) ? 4 : (uv >= 100UL) ? 3 : (uv >= 10UL) ? 2 : 1UL; modp_json_add_value(ctx); if (ctx->dest) { wstr = ctx->dest + ctx->size; wstr += r - 1; do *wstr-- = (char)(48 + (uv % 10)); while (uv /= 10); } ctx->size += r; } void modp_json_add_cstring(modp_json_ctx* ctx, const char* src) { return modp_json_add_string(ctx, src, strlen(src)); } void modp_json_add_string(modp_json_ctx* ctx, const char* src, size_t len) { modp_json_add_value(ctx); if (ctx->dest) { ctx->size += modp_bjson_encode(ctx->dest + ctx->size, src, len); } else { ctx->size += modp_bjson_encode_strlen(src, len); } } static size_t modp_bjson_encode(char* dest, const char* src, size_t len) { static const char* hexchar = "0123456789ABCDEF"; const char* deststart = (const char*)dest; const uint8_t* s = (const uint8_t*)src; const uint8_t* srcend = s + len; uint8_t x; uint8_t val; *dest++ = '"'; while (s < srcend) { x = *s++; val = gsJSONEncodeMap[x]; if (val == 'a') { /* a for ascii, as-is */ *dest++ = (char)x; } else if (val == 'u') { /* u for unicode, 6 byte escape sequence */ dest[0] = '\\'; dest[1] = 'u'; dest[2] = '0'; dest[3] = '0'; dest[4] = hexchar[x >> 4]; dest[5] = hexchar[x & 0x0F]; dest += 6; } else { /* 2 byte escape sequence */ dest[0] = '\\'; dest[1] = (char)val; dest += 2; } } *dest++ = '"'; return (size_t)(dest - deststart); } static size_t modp_bjson_encode_strlen(const char* src, size_t len) { const uint8_t* s = (const uint8_t*)src; const uint8_t* srcend = s + len; size_t count = 2; /* for start and end quotes */ while (s < srcend) { count += (gsJSONEncodeLenMap[*s++]); } return count; }