423 lines
8.8 KiB
C
423 lines
8.8 KiB
C
|
|
#include "iron_json.h"
|
|
#include "iron_string.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <jsmn.h>
|
|
|
|
void *gc_alloc(size_t size);
|
|
|
|
static const int PTR_SIZE = 8;
|
|
static char *source;
|
|
static jsmntok_t *tokens;
|
|
static int num_tokens;
|
|
static uint32_t ti; // token index
|
|
static uint8_t *decoded;
|
|
static uint32_t wi; // write index
|
|
static uint32_t bottom;
|
|
static uint32_t array_count;
|
|
|
|
static inline uint64_t pad(uint32_t di, int n) {
|
|
return (n - (di % n)) % n;
|
|
}
|
|
|
|
static void store_u8(uint8_t u8) {
|
|
*(uint8_t *)(decoded + wi) = u8;
|
|
wi += 1;
|
|
}
|
|
|
|
static void store_i32(int32_t i32) {
|
|
// TODO: signed overflow is UB
|
|
// if (i32 > INT32_MAX)
|
|
// i32 = (int32_t)(i32 - INT32_MAX - 1) - INT32_MAX - 1;
|
|
wi += pad(wi, 4);
|
|
*(int32_t *)(decoded + wi) = i32;
|
|
wi += 4;
|
|
}
|
|
|
|
static void store_u32(uint32_t u32) {
|
|
wi += pad(wi, 4);
|
|
*(uint32_t *)(decoded + wi) = u32;
|
|
wi += 4;
|
|
}
|
|
|
|
static void store_f32(float f32) {
|
|
wi += pad(wi, 4);
|
|
*(float *)(decoded + wi) = f32;
|
|
wi += 4;
|
|
}
|
|
|
|
static void store_ptr(uint32_t ptr) {
|
|
wi += pad(wi, PTR_SIZE);
|
|
*(uint64_t *)(decoded + wi) = (uint64_t)decoded + (uint64_t)ptr;
|
|
wi += PTR_SIZE;
|
|
}
|
|
|
|
static void store_ptr_abs(void *ptr) {
|
|
wi += pad(wi, PTR_SIZE);
|
|
*(uint64_t *)(decoded + wi) = (uint64_t)ptr;
|
|
wi += PTR_SIZE;
|
|
}
|
|
|
|
static void store_string_bytes(char *str, uint32_t len) {
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
store_u8(str[i]);
|
|
}
|
|
store_u8('\0');
|
|
}
|
|
|
|
static bool is_key(char *s, jsmntok_t *t) {
|
|
return t->type == JSMN_STRING && s[t->end + 1] == ':';
|
|
}
|
|
|
|
static jsmntok_t get_token() {
|
|
jsmntok_t t = tokens[ti];
|
|
while (is_key(source, &t)) {
|
|
ti++;
|
|
t = tokens[ti];
|
|
}
|
|
return t;
|
|
}
|
|
|
|
static int traverse(uint32_t wi) {
|
|
jsmntok_t t = get_token();
|
|
if (t.type == JSMN_OBJECT) {
|
|
ti++;
|
|
uint32_t size = 0;
|
|
for (uint32_t i = 0; i < t.size; ++i) {
|
|
size += traverse(wi + size);
|
|
}
|
|
return pad(wi, PTR_SIZE) + size;
|
|
}
|
|
else if (t.type == JSMN_PRIMITIVE) {
|
|
ti++;
|
|
if (source[t.start] == 't' || source[t.start] == 'f') { // bool
|
|
return 1;
|
|
}
|
|
else if (source[t.start] == 'n') { // null
|
|
return pad(wi, PTR_SIZE) + PTR_SIZE;
|
|
}
|
|
else { // number
|
|
return pad(wi, 4) + 4;
|
|
}
|
|
}
|
|
else if (t.type == JSMN_ARRAY) {
|
|
ti++;
|
|
for (uint32_t i = 0; i < t.size; ++i) {
|
|
traverse(0);
|
|
}
|
|
return pad(wi, PTR_SIZE) + PTR_SIZE;
|
|
}
|
|
else if (t.type == JSMN_STRING) {
|
|
ti++;
|
|
return pad(wi, PTR_SIZE) + PTR_SIZE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int token_size() {
|
|
uint32_t _ti = ti;
|
|
uint32_t len = traverse(0);
|
|
ti = _ti;
|
|
return len;
|
|
}
|
|
|
|
static bool has_dot(char *str, uint32_t len) {
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
if (str[i] == '.') {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void token_write() {
|
|
jsmntok_t t = get_token();
|
|
|
|
if (t.type == JSMN_OBJECT) {
|
|
// TODO: Object containing another object
|
|
// Write object contents
|
|
uint32_t size = token_size();
|
|
size += pad(size, PTR_SIZE);
|
|
bottom += size * array_count;
|
|
ti++;
|
|
for (uint32_t i = 0; i < t.size; ++i) {
|
|
token_write();
|
|
}
|
|
}
|
|
else if (t.type == JSMN_PRIMITIVE) {
|
|
ti++;
|
|
if (source[t.start] == 't' || source[t.start] == 'f') { // bool
|
|
store_u8(source[t.start] == 't' ? 1 : 0);
|
|
}
|
|
else if (source[t.start] == 'n') { // null
|
|
store_ptr_abs(NULL);
|
|
}
|
|
else {
|
|
has_dot(source + t.start, t.end - t.start) ?
|
|
store_f32(strtof(source + t.start, NULL)) :
|
|
#ifdef _WIN32
|
|
store_i32(_strtoi64(source + t.start, NULL, 10));
|
|
#else
|
|
store_i32(strtol(source + t.start, NULL, 10));
|
|
#endif
|
|
}
|
|
}
|
|
else if (t.type == JSMN_ARRAY) {
|
|
ti++;
|
|
store_ptr(bottom);
|
|
|
|
uint32_t _wi = wi;
|
|
wi = bottom;
|
|
store_ptr(bottom + PTR_SIZE + 4 + 4); // Pointer to buffer contents
|
|
store_u32(t.size); // Element count
|
|
store_u32(0); // Capacity = 0 -> do not free on first realloc
|
|
bottom = wi;
|
|
|
|
if (t.size == 0) {
|
|
wi = _wi;
|
|
return;
|
|
}
|
|
|
|
uint32_t count = t.size;
|
|
array_count = count;
|
|
t = get_token();
|
|
|
|
if (t.type == JSMN_OBJECT) {
|
|
// Struct pointers
|
|
uint32_t size = token_size();
|
|
size += pad(size, PTR_SIZE);
|
|
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
store_ptr(bottom + count * PTR_SIZE + i * size);
|
|
}
|
|
|
|
// Struct contents
|
|
bottom = pad(wi, PTR_SIZE) + wi;
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
wi = pad(wi, PTR_SIZE) + wi;
|
|
token_write();
|
|
}
|
|
}
|
|
else if (t.type == JSMN_STRING) {
|
|
// String pointers
|
|
uint32_t _ti = ti;
|
|
uint32_t strings_length = 0;
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
store_ptr(bottom + count * PTR_SIZE + strings_length);
|
|
uint32_t length = t.end - t.start; // String length
|
|
strings_length += length;
|
|
strings_length += 1; // '\0'
|
|
ti++;
|
|
t = get_token();
|
|
}
|
|
ti = _ti;
|
|
t = get_token();
|
|
|
|
// String bytes
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
store_string_bytes(source + t.start, t.end - t.start);
|
|
ti++;
|
|
t = get_token();
|
|
}
|
|
bottom = pad(wi, PTR_SIZE) + wi;
|
|
}
|
|
else {
|
|
// Array contents
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
token_write();
|
|
}
|
|
bottom = pad(wi, PTR_SIZE) + wi;
|
|
}
|
|
|
|
wi = _wi;
|
|
array_count = 1;
|
|
}
|
|
else if (t.type == JSMN_STRING) {
|
|
ti++;
|
|
store_ptr(bottom);
|
|
|
|
uint32_t _wi = wi;
|
|
wi = bottom;
|
|
store_string_bytes(source + t.start, t.end - t.start);
|
|
bottom = pad(wi, PTR_SIZE) + wi;
|
|
wi = _wi;
|
|
}
|
|
}
|
|
|
|
static void load_tokens(char *s) {
|
|
jsmn_parser parser;
|
|
jsmn_init(&parser);
|
|
num_tokens = jsmn_parse(&parser, s, strlen(s), NULL, 0);
|
|
|
|
tokens = malloc(sizeof(jsmntok_t) * num_tokens);
|
|
jsmn_init(&parser);
|
|
jsmn_parse(&parser, s, strlen(s), tokens, num_tokens);
|
|
|
|
source = s;
|
|
ti = 0;
|
|
}
|
|
|
|
void *json_parse(char *s) {
|
|
load_tokens(s);
|
|
|
|
uint32_t out_size = strlen(s) * 2;
|
|
decoded = gc_alloc(out_size);
|
|
wi = 0;
|
|
bottom = 0;
|
|
array_count = 1;
|
|
token_write();
|
|
|
|
free(tokens);
|
|
return decoded;
|
|
}
|
|
|
|
static void token_write_to_map(any_map_t *m) {
|
|
jsmntok_t t = get_token();
|
|
|
|
if (t.type == JSMN_OBJECT) {
|
|
// TODO: Object containing another object
|
|
ti++;
|
|
for (uint32_t i = 0; i < t.size; ++i) {
|
|
token_write_to_map(m);
|
|
}
|
|
}
|
|
else if (t.type == JSMN_PRIMITIVE) {
|
|
jsmntok_t tkey = tokens[ti - 1];
|
|
ti++;
|
|
any_map_set(m, substring(source, tkey.start, tkey.end), substring(source, t.start, t.end));
|
|
}
|
|
else if (t.type == JSMN_ARRAY) {
|
|
ti++;
|
|
}
|
|
else if (t.type == JSMN_STRING) {
|
|
jsmntok_t tkey = tokens[ti - 1];
|
|
ti++;
|
|
any_map_set(m, substring(source, tkey.start, tkey.end), substring(source, t.start, t.end));
|
|
}
|
|
}
|
|
|
|
any_map_t *json_parse_to_map(char *s) {
|
|
load_tokens(s);
|
|
|
|
any_map_t *m = any_map_create();
|
|
token_write_to_map(m);
|
|
|
|
free(tokens);
|
|
return m;
|
|
}
|
|
|
|
static char *encoded;
|
|
static int keys;
|
|
|
|
void json_encode_begin() {
|
|
encoded = "{";
|
|
keys = 0;
|
|
}
|
|
|
|
char *json_encode_end() {
|
|
encoded = string_join(encoded, "}");
|
|
return encoded;
|
|
}
|
|
|
|
void json_encode_key(char *k) {
|
|
if (keys > 0) {
|
|
encoded = string_join(encoded, ",");
|
|
}
|
|
encoded = string_join(encoded, "\"");
|
|
encoded = string_join(encoded, k);
|
|
encoded = string_join(encoded, "\":");
|
|
keys++;
|
|
}
|
|
|
|
void json_encode_string_value(char *v) {
|
|
encoded = string_join(encoded, "\"");
|
|
encoded = string_join(encoded, v);
|
|
encoded = string_join(encoded, "\"");
|
|
}
|
|
|
|
void json_encode_string(char *k, char *v) {
|
|
json_encode_key(k);
|
|
json_encode_string_value(v);
|
|
}
|
|
|
|
void json_encode_string_array(char *k, char_ptr_array_t *a) {
|
|
json_encode_begin_array(k);
|
|
for (uint32_t i = 0; i < a->length; ++i) {
|
|
if (i > 0) {
|
|
encoded = string_join(encoded, ",");
|
|
}
|
|
json_encode_string_value(a->buffer[i]);
|
|
}
|
|
json_encode_end_array();
|
|
}
|
|
|
|
void json_encode_f32(char *k, float f) {
|
|
json_encode_key(k);
|
|
encoded = string_join(encoded, f32_to_string_with_zeros(f));
|
|
}
|
|
|
|
void json_encode_i32(char *k, int i) {
|
|
json_encode_key(k);
|
|
encoded = string_join(encoded, i32_to_string(i));
|
|
}
|
|
|
|
void json_encode_null(char *k) {
|
|
json_encode_key(k);
|
|
encoded = string_join(encoded, "null");
|
|
}
|
|
|
|
void json_encode_f32_array(char *k, f32_array_t *a) {
|
|
json_encode_begin_array(k);
|
|
for (uint32_t i = 0; i < a->length; ++i) {
|
|
if (i > 0) {
|
|
encoded = string_join(encoded, ",");
|
|
}
|
|
encoded = string_join(encoded, f32_to_string(a->buffer[i]));
|
|
}
|
|
json_encode_end_array();
|
|
}
|
|
|
|
void json_encode_i32_array(char *k, i32_array_t *a) {
|
|
json_encode_begin_array(k);
|
|
for (uint32_t i = 0; i < a->length; ++i) {
|
|
if (i > 0) {
|
|
encoded = string_join(encoded, ",");
|
|
}
|
|
encoded = string_join(encoded, i32_to_string(a->buffer[i]));
|
|
}
|
|
json_encode_end_array();
|
|
}
|
|
|
|
void json_encode_bool(char *k, bool b) {
|
|
json_encode_key(k);
|
|
encoded = string_join(encoded, b ? "true" : "false");
|
|
}
|
|
|
|
void json_encode_begin_array(char *k) {
|
|
json_encode_key(k);
|
|
encoded = string_join(encoded, "[");
|
|
}
|
|
|
|
void json_encode_end_array() {
|
|
encoded = string_join(encoded, "]");
|
|
}
|
|
|
|
void json_encode_begin_object() {
|
|
keys = 0;
|
|
encoded = string_join(encoded, "{");
|
|
}
|
|
|
|
void json_encode_end_object() {
|
|
encoded = string_join(encoded, "}");
|
|
}
|
|
|
|
void json_encode_map(any_map_t *m) {
|
|
any_array_t *keys = map_keys(m);
|
|
for (uint32_t i = 0; i < keys->length; i++) {
|
|
json_encode_string(keys->buffer[i], any_map_get(m, keys->buffer[i]));
|
|
}
|
|
}
|