libcjsonp/src/load.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;
}