274 lines
7.6 KiB
C
274 lines
7.6 KiB
C
/* This software is licensed by the MIT License, see LICENSE file */
|
|
/* Copyright © 2022 Gregory Lirent */
|
|
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <limits.h>
|
|
#include <errno.h>
|
|
#include "buffer.h"
|
|
|
|
#include "../modules/libcdsb/include/list.h"
|
|
#include "../modules/libcdsb/include/map.h"
|
|
#include "../modules/libcdsb/include/string.h"
|
|
|
|
typedef struct {
|
|
FILE* stream;
|
|
char cur;
|
|
} reader_t;
|
|
|
|
#define next libcjsonp_builtin_read_next
|
|
#define next_sign libcjsonp_builtin_read_next_sign
|
|
|
|
static bool libcjsonp_builtin_parse(value_t* x, reader_t* s);
|
|
|
|
static inline char libcjsonp_builtin_read_next(reader_t *x) {
|
|
int cur = fgetc(x->stream);
|
|
|
|
return x->cur = (cur == EOF) ? 0 : cur;
|
|
}
|
|
|
|
static inline char libcjsonp_builtin_read_next_sign(reader_t *x) {
|
|
for (int cur;;) {
|
|
switch (cur = fgetc(x->stream)) {
|
|
default: return x->cur = cur;
|
|
case EOF: return x->cur = 0;
|
|
|
|
case '\n': case ' ': case '\r':
|
|
case '\t': case '\v': break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool libcjsonp_builtin_parse_number(value_t* x, reader_t* s) {
|
|
char b[64], *p = b;
|
|
bool is_float = false;
|
|
|
|
for (;;) {
|
|
switch (*p++ = s->cur) {
|
|
case '.': case 'E': case 'e': if (p > b+1) {
|
|
is_float = true;
|
|
break;
|
|
} case 0: return false;
|
|
|
|
case '\n': case ' ': case '\r':
|
|
case '\t': case '\v': next_sign(s);
|
|
default: p[-1] = 0; goto break_;
|
|
|
|
case '-': case '+': case '0': case '1':
|
|
case '2': case '3': case '4': case '5':
|
|
case '6': case '7': case '8': case '9': break;
|
|
}
|
|
next(s);
|
|
}
|
|
|
|
break_:
|
|
|
|
return (bool)libcjsonp_builtin_fetch_number(x, b, is_float);
|
|
}
|
|
|
|
static bool libcjsonp_builtin_parse_string(vtype_string* x, reader_t* s) {
|
|
|
|
char *mem, *ptr;
|
|
size_t nmemb, size;
|
|
vtype_uint8 esc;
|
|
|
|
mem = 0;
|
|
nmemb = 0;
|
|
size = 0;
|
|
esc = 0;
|
|
|
|
while (next(s)) {
|
|
if (s->cur == '"' && !esc) {
|
|
if (!nmemb) {
|
|
mem = realloc(mem, size + 1);
|
|
mem[size] = 0;
|
|
} else *ptr = 0;
|
|
|
|
x->buffer = mem;
|
|
libcjsonp_string_unescape(x);
|
|
|
|
return true;
|
|
} else if (s->cur == '\\') {
|
|
esc ^= 1;
|
|
} else esc = 0;
|
|
|
|
if (nmemb--) {
|
|
*ptr++ = s->cur;
|
|
} else {
|
|
mem = realloc(mem, size += nmemb = buffer_block);
|
|
ptr = mem + (size - buffer_block);
|
|
}
|
|
}
|
|
|
|
libcdsb_free(mem);
|
|
return false;
|
|
}
|
|
|
|
static bool libcjsonp_builtin_parse_map(vtype_map* x, reader_t* s) {
|
|
vtype_string name;
|
|
value_t value;
|
|
|
|
map_init(x, VTYPE_STRING);
|
|
|
|
if (next_sign(s) == '}')
|
|
return true;
|
|
|
|
for (;;) {
|
|
if (!libcjsonp_builtin_parse_string(&name, s))
|
|
return false;
|
|
|
|
if (next_sign(s) != ':') {
|
|
goto bad_;
|
|
} else next_sign(s);
|
|
|
|
if (!libcjsonp_builtin_parse(&value, s))
|
|
goto bad_;
|
|
|
|
switch (s->cur) {
|
|
case ',':
|
|
libcdsb_map_inject(x, &name, VTYPE_STRING, value.value, value.type);
|
|
next_sign(s);
|
|
break;
|
|
case '}':
|
|
return true;
|
|
default:
|
|
if (value.type == VTYPE_MAP) map_free ((void*)value.value);
|
|
else if (value.type == VTYPE_LIST) list_free ((void*)value.value);
|
|
else if (value.type == VTYPE_STRING) string_free((void*)value.value);
|
|
bad_:
|
|
string_free(&name);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool libcjsonp_builtin_parse_list(vtype_list* x, reader_t* s) {
|
|
value_t value;
|
|
|
|
list_init(x);
|
|
|
|
if (next_sign(s) == ']')
|
|
return true;
|
|
|
|
for (;;) {
|
|
if (!libcjsonp_builtin_parse(&value, s))
|
|
return false;
|
|
|
|
switch (s->cur) {
|
|
case ',':
|
|
libcdsb_list_attach(x, -1, value.value, value.type, 1);
|
|
next_sign(s);
|
|
break;
|
|
case ']':
|
|
return true;
|
|
default:
|
|
if (value.type == VTYPE_MAP) map_free ((void*)value.value);
|
|
else if (value.type == VTYPE_LIST) list_free ((void*)value.value);
|
|
else if (value.type == VTYPE_STRING) string_free((void*)value.value);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool libcjsonp_builtin_parse(value_t* x, reader_t* s) {
|
|
bool ret = true;
|
|
|
|
switch (s->cur) {
|
|
case '{': if (!(ret = libcjsonp_builtin_parse_map((void*)x->value, s))) {
|
|
map_free((void*)x->value);
|
|
} else x->type = VTYPE_MAP;
|
|
break;
|
|
|
|
case '[': if (!(ret = libcjsonp_builtin_parse_list((void*)x->value, s))) {
|
|
list_free((void*)x->value);
|
|
} else x->type = VTYPE_LIST;
|
|
break;
|
|
|
|
case '"': if (!(ret = libcjsonp_builtin_parse_string((void*)x->value, s))) {
|
|
string_free((void*)x->value);
|
|
} else x->type = VTYPE_STRING;
|
|
break;
|
|
|
|
case 't': if (next(s) == 'r' && next(s) == 'u' && next(s) == 'e') {
|
|
x->value->b = true;
|
|
x->type = VTYPE_BOOLEAN;
|
|
} else ret = false;
|
|
break;
|
|
|
|
case 'f': if (next(s) == 'a' && next(s) == 'l' && next(s) == 's' && next(s) == 'e') {
|
|
x->value->b = false;
|
|
x->type = VTYPE_BOOLEAN;
|
|
} else ret = false;
|
|
break;
|
|
|
|
case 'n': if (next(s) == 'u' && next(s) == 'l' && next(s) == 'l') {
|
|
x->value->ptr = 0;
|
|
x->type = VTYPE_POINTER;
|
|
} else ret = false;
|
|
break;
|
|
|
|
case '-': case '0': case '1': case '2': case '3':
|
|
case '4': case '5': case '6': case '7': case '8':
|
|
case '9': return libcjsonp_builtin_parse_number(x, s);
|
|
}
|
|
|
|
if (ret) next_sign(s);
|
|
return ret;
|
|
}
|
|
|
|
/*#####################################################################################################################*/
|
|
|
|
bool json_load(json_t* x, FILE* s) {
|
|
|
|
reader_t reader;
|
|
value_t value;
|
|
|
|
reader.stream = s;
|
|
next_sign(&reader);
|
|
|
|
if (libcjsonp_builtin_parse(&value, &reader)) {
|
|
switch (x->type = value.type) {
|
|
default:
|
|
#ifndef NDEBUG
|
|
abort();
|
|
#endif
|
|
|
|
case VTYPE_BOOLEAN:
|
|
case VTYPE_INT8:
|
|
case VTYPE_UINT8: x->data = libcdsb_memndup(value.value, sizeof(vtype_uint8));
|
|
break;
|
|
|
|
case VTYPE_INT16:
|
|
case VTYPE_UINT16: x->data = libcdsb_memndup(value.value, sizeof(vtype_uint16));
|
|
break;
|
|
|
|
case VTYPE_INT32:
|
|
case VTYPE_UINT32:
|
|
x86_: x->data = libcdsb_memndup(value.value, sizeof(vtype_uint32));
|
|
break;
|
|
|
|
case VTYPE_POINTER: if (sizeof(void*) == sizeof(vtype_uint32)) goto x86_;
|
|
case VTYPE_INT64:
|
|
case VTYPE_UINT64: x->data = libcdsb_memndup(value.value, sizeof(vtype_uint64));
|
|
break;
|
|
|
|
case VTYPE_LDOUBLE: x->data = libcdsb_memndup(value.value, sizeof(vtype_ldouble));
|
|
break;
|
|
|
|
case VTYPE_MAP: x->data = libcdsb_memndup(value.value, sizeof(vtype_map));
|
|
break;
|
|
|
|
case VTYPE_LIST: x->data = libcdsb_memndup(value.value, sizeof(vtype_list));
|
|
break;
|
|
|
|
case VTYPE_STRING: x->data = libcdsb_memndup(value.value, sizeof(vtype_string));
|
|
break;
|
|
}
|
|
} else {
|
|
memset(x, 0, sizeof(*x));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|