JSON/Src/mtojson.c

760 lines
13 KiB
C

/*
SPDX-License-Identifier: BSD-2-Clause
This file is Copyright (c) 2020, 2021 by Rene Kita
*/
#include "mtojson.h"
#include "cvt.h"
#include <stdint.h>
#include <string.h>
static char* gen_array(char *, const void *);
static char* gen_boolean(char *, const void *);
static char* gen_c_array(char *, const void *);
static char* gen_hex(char *, const void *);
static char* gen_hex_u8(char *, const void *);
static char* gen_hex_u16(char *, const void *);
static char* gen_hex_u32(char *, const void *);
static char* gen_hex_u64(char *, const void *);
static char* gen_int(char *, const void *);
static char* gen_int8_t(char *, const void *);
static char* gen_int16_t(char *, const void *);
static char* gen_int32_t(char *, const void *);
static char* gen_int64_t(char *, const void *);
static char* gen_long(char *, const void *);
static char* gen_longlong(char *, const void *);
static char* gen_null(char *, const void *);
static char* gen_object(char *, const void *);
static char* gen_primitive(char *, const void *);
static char* gen_string(char *, const void *);
static char* gen_uint(char *, const void *);
static char* gen_uint8_t(char *, const void *);
static char* gen_uint16_t(char *, const void *);
static char* gen_uint32_t(char *, const void *);
static char* gen_uint64_t(char *, const void *);
static char* gen_ulong(char *, const void *);
static char* gen_ulonglong(char *, const void *);
static char* gen_value(char *, const void *);
static char* gen_float(char *, const void *);
static char* (* const gen_functions[])(char *, const void *) = {
gen_primitive,
gen_array,
gen_boolean,
gen_hex,
gen_hex_u8,
gen_hex_u16,
gen_hex_u32,
gen_hex_u64,
gen_int,
gen_int8_t,
gen_int16_t,
gen_int32_t,
gen_int64_t,
gen_long,
gen_longlong,
gen_null,
gen_object,
gen_string,
gen_uint,
gen_uint8_t,
gen_uint16_t,
gen_uint32_t,
gen_uint64_t,
gen_ulong,
gen_ulonglong,
gen_value,
gen_float
};
static char* strcpy_val(char *out, const char *val, size_t len);
extern char bufFloat[20];
static char*
gen_float(char *out, const void *val)
{
char bufFloat[32];
memset(bufFloat, 0, sizeof(bufFloat));
if (!val)
return gen_null(out, val);
fromFloat(bufFloat, val);
return strcpy_val(out, &bufFloat[0], strlen(bufFloat));
}
static size_t remaining_length;
static int
reduce_rem_len(size_t len)
{
if (remaining_length < len)
return 0;
remaining_length -= len;
return 1;
}
static char*
strcpy_val(char *out, const char *val, size_t len)
{
if (!reduce_rem_len(len))
return NULL;
memcpy(out, val, len);
return out + len;
}
static char*
gen_null(char *out, const void *val)
{
(void)val;
return strcpy_val(out, "null", 4);
}
static char*
gen_boolean(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
if (*(_Bool*)val)
return strcpy_val(out, "true", 4);
else
return strcpy_val(out, "false", 5);
}
static char*
gen_string(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
if (!reduce_rem_len(2)) // 2 -> ""
return NULL;
char chars_to_escape[] = "\"\\";
const char *begin = (const char*)val;
const char *end = begin + strlen(begin);
*out++ = '"';
char *esc;
do {
esc = NULL;
for (int i = 0; i < 2; i++) {
char *tmp;
if ((tmp = strchr(begin, chars_to_escape[i]))) {
if (!esc)
esc = tmp;
else
esc = esc < tmp ? esc : tmp;
}
}
size_t len = esc ? (size_t)(esc - begin) : (size_t)(end - begin);
if (!(out = strcpy_val(out, begin, len)))
return NULL;
if (esc) {
char s[2];
s[0] = '\\';
s[1] = *esc;
if (!(out = strcpy_val(out, s, 2)))
return NULL;
begin = esc + 1;
}
} while (esc && *begin);
*out++ = '"';
return out;
}
static char*
utoa(char *dst, unsigned n, unsigned base)
{
char *s = dst;
char *e;
for (unsigned m = n; m >= base; m /= base)
s++;
e = s + 1;
size_t len = (size_t)(e - dst);
if (!reduce_rem_len(len))
return NULL;
for ( ; s >= dst; s--, n /= base)
*s = "0123456789ABCDEF"[n % base];
return e;
}
static char*
ultoa(char *dst, unsigned long n, unsigned base)
{
char *s = dst;
char *e;
for (unsigned long m = n; m >= base; m /= base)
s++;
e = s + 1;
size_t len = (size_t)(e - dst);
if (!reduce_rem_len(len))
return NULL;
for ( ; s >= dst; s--, n /= base)
*s = "0123456789ABCDEF"[n % base];
return e;
}
static char*
ulltoa(char *dst, unsigned long long n, unsigned base)
{
char *s = dst;
char *e;
for (unsigned long long m = n; m >= base; m /= base)
s++;
e = s + 1;
size_t len = (size_t)(e - dst);
if (!reduce_rem_len(len))
return NULL;
for ( ; s >= dst; s--, n /= base)
*s = "0123456789ABCDEF"[n % base];
return e;
}
static char*
gen_hex(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
if (!reduce_rem_len(2)) // 2 -> ""
return NULL;
*out++ = '"';
if (!(out = utoa(out, *(unsigned*)val, 16)))
return NULL;
*out++ = '"';
return out;
}
static char*
gen_hex_u8(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
if (!reduce_rem_len(2)) // 2 -> ""
return NULL;
*out++ = '"';
if (!(out = utoa(out, *(uint8_t*)val, 16)))
return NULL;
*out++ = '"';
return out;
}
static char*
gen_hex_u16(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
if (!reduce_rem_len(2)) // 2 -> ""
return NULL;
*out++ = '"';
if (!(out = utoa(out, *(uint16_t*)val, 16)))
return NULL;
*out++ = '"';
return out;
}
static char*
gen_hex_u32(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
if (!reduce_rem_len(2)) // 2 -> ""
return NULL;
*out++ = '"';
if (!(out = ultoa(out, *(uint32_t*)val, 16)))
return NULL;
*out++ = '"';
return out;
}
static char*
gen_hex_u64(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
if (!reduce_rem_len(2)) // 2 -> ""
return NULL;
*out++ = '"';
if (!(out = ulltoa(out, *(uint64_t*)val, 16)))
return NULL;
*out++ = '"';
return out;
}
static char*
gen_int(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
int n = *(int*)val;
unsigned u = (unsigned)n;
if (n < 0){
if (!reduce_rem_len(1))
return NULL;
*out++ = '-';
u = -(unsigned)n;
}
if (!(out = utoa(out, u, 10)))
return NULL;
return out;
}
static char*
gen_int8_t(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
int n = *(int8_t*)val;
unsigned u = (unsigned)n;
if (n < 0){
if (!reduce_rem_len(1))
return NULL;
*out++ = '-';
u = -(unsigned)n;
}
if (!(out = utoa(out, u, 10)))
return NULL;
return out;
}
static char*
gen_int16_t(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
int n = *(int16_t*)val;
unsigned u = (unsigned)n;
if (n < 0){
if (!reduce_rem_len(1))
return NULL;
*out++ = '-';
u = -(unsigned)n;
}
if (!(out = utoa(out, u, 10)))
return NULL;
return out;
}
static char*
gen_int32_t(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
int n = *(int*)val;
unsigned u = (unsigned)n;
if (n < 0){
if (!reduce_rem_len(1))
return NULL;
*out++ = '-';
u = -(unsigned)n;
}
if (!(out = utoa(out, u, 10)))
return NULL;
return out;
}
static char*
gen_int64_t(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
int64_t n = *(int64_t*)val;
uint64_t u = (uint64_t)n;
if (n < 0){
if (!reduce_rem_len(1))
return NULL;
*out++ = '-';
u = -(uint64_t)n;
}
if (!(out = ulltoa(out, u, 10)))
return NULL;
return out;
}
static char*
gen_uint(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
return utoa(out, *(unsigned*)val, 10);
}
static char*
gen_uint8_t(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
return utoa(out, *(uint8_t*)val, 10);
}
static char*
gen_uint16_t(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
return utoa(out, *(uint16_t*)val, 10);
}
static char*
gen_uint32_t(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
return ultoa(out, *(uint32_t*)val, 10);
}
static char*
gen_uint64_t(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
return ulltoa(out, *(uint64_t*)val, 10);
}
static char*
gen_long(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
long n = *(long*)val;
unsigned long u = (unsigned long)n;
if (n < 0){
if (!reduce_rem_len(1))
return NULL;
*out++ = '-';
u = -(unsigned long)n;
}
if (!(out = ultoa(out, u, 10)))
return NULL;
return out;
}
static char*
gen_longlong(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
long long n = *(long long*)val;
unsigned long long u = (unsigned long long)n;
if (n < 0){
if (!reduce_rem_len(1))
return NULL;
*out++ = '-';
u = -(unsigned long long)n;
}
if (!(out = ulltoa(out, u, 10)))
return NULL;
return out;
}
static char*
gen_ulong(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
return ultoa(out, *(unsigned long*)val, 10);
}
static char*
gen_ulonglong(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
return ulltoa(out, *(unsigned long long*)val, 10);
}
static char*
gen_value(char *out, const void *val)
{
return strcpy_val(out, (const char*)val, strlen((const char*)val));
}
static char*
gen_c_array(char *out, const void *val)
{
const struct to_json *tjs = (const struct to_json*)val;
if (!reduce_rem_len(2)) // 2 -> []
return NULL;
*out++ = '[';
if (*tjs->count == 0){
*out++ = ']';
return out;
}
size_t incr = 0;
char* (*func)(char *, const void *) = gen_functions[tjs->vtype];
switch (tjs->vtype) {
case t_to_boolean:
incr = sizeof(_Bool);
break;
case t_to_int:
incr = sizeof(int);
break;
case t_to_int8_t:
incr = sizeof(int8_t);
break;
case t_to_int16_t:
incr = sizeof(int16_t);
break;
case t_to_int32_t:
incr = sizeof(int32_t);
break;
case t_to_float:
incr = sizeof(float);
break;
case t_to_int64_t:
incr = sizeof(int64_t);
break;
case t_to_long:
incr = sizeof(unsigned long);
break;
case t_to_longlong:
incr = sizeof(unsigned long long);
break;
case t_to_object:
incr = sizeof(struct to_json);
break;
case t_to_hex:
case t_to_uint:
incr = sizeof(unsigned);
break;
case t_to_hex_u8:
case t_to_uint8_t:
incr = sizeof(uint8_t);
break;
case t_to_hex_u16:
case t_to_uint16_t:
incr = sizeof(uint16_t);
break;
case t_to_hex_u32:
case t_to_uint32_t:
incr = sizeof(uint32_t);
break;
case t_to_hex_u64:
case t_to_uint64_t:
incr = sizeof(uint64_t);
break;
case t_to_ulong:
incr = sizeof(unsigned long);
break;
case t_to_ulonglong:
incr = sizeof(unsigned long long);
break;
case t_to_array:
case t_to_null:
case t_to_primitive:
case t_to_string:
case t_to_value:
return NULL;
}
const char *p = tjs->value;
for (size_t i = 0; i < *tjs->count - 1; i++){
if (!(out = (*func)(out, p)))
return NULL;
if (!reduce_rem_len(1))
return NULL;
*out++ = ',';
p += incr;
}
if (!(out = (*func)(out, p)))
return NULL;
*out++ = ']';
return out;
}
static char*
gen_array(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
const struct to_json *tjs = (const struct to_json*)val;
if (!reduce_rem_len(2)) // 2 -> []
return NULL;
*out++ = '[';
while (tjs->value){
if (tjs->count)
out = gen_c_array(out, tjs);
else
out = gen_functions[tjs->vtype](out, tjs->value);
if (!out)
return NULL;
tjs++;
if (tjs->value){
if (!reduce_rem_len(1))
return NULL;
*out++ = ',';
}
}
*out++ = ']';
return out;
}
static char*
gen_object(char *out, const void *val)
{
if (!val)
return gen_null(out, val);
const struct to_json *tjs = (const struct to_json*)val;
if (!reduce_rem_len(2)) // 2 -> {}
return NULL;
*out++ = '{';
while (tjs->name){
const char *name = tjs->name;
size_t len = strlen(name) ;
if (!reduce_rem_len(len + 3)) // 3 -> "":
return NULL;
*out++ = '"';
memcpy(out, name, len);
out += len;
*out++ = '"';
*out++ = ':';
if (tjs->count)
out = gen_c_array(out, tjs);
else
out = gen_functions[tjs->vtype](out, tjs->value);
if (!out)
return NULL;
tjs++;
if (tjs->name){
if (!reduce_rem_len(1))
return NULL;
*out++ = ',';
}
}
*out++ = '}';
return out;
}
static char*
gen_primitive(char *out, const void *to_json)
{
const struct to_json *tjs = (const struct to_json *)to_json;
if (tjs->count)
return gen_c_array(out, tjs);
return gen_functions[tjs->vtype](out, tjs->value);
}
size_t
json_generate(char *out, const struct to_json *tjs, size_t len)
{
const char *start = out;
remaining_length = len;
if (!reduce_rem_len(1)) // \0
return 0;
switch (tjs->stype) {
case t_to_array:
out = gen_array(out, tjs);
break;
case t_to_object:
out = gen_object(out, tjs);
break;
case t_to_primitive:
out = gen_primitive(out, tjs);
break;
/* These are not valid ctypes */
case t_to_boolean:
case t_to_int:
case t_to_null:
case t_to_string:
case t_to_uint:
case t_to_value:
default:
return 0;
}
if (!out)
return 0;
*out = '\0';
return (size_t)(out - start);
}