// TODO pretty print json #include #include #include #include #include #include "json.h" #include "error.h" #define JSON_BUF_INITIAL_CAPACITY 1024 static void json_push_context(json_t *buf, json_context_t ctx) { if (buf->context_depth >= JSON_MAX_DEPTH) { // TODO handle errors better than murdering the program error_fatal("Exceeded JSON max nesting depth"); } buf->context_stack[buf->context_depth] = ctx; buf->needs_comma[buf->context_depth] = false; buf->context_depth++; } static void json_pop_context(json_t *buf) { if (buf->context_depth <= 0) { // TODO better error handling error_fatal("JSON context pop"); } buf->context_depth--; } bool json_needs_comma(json_t *buf) { return buf->needs_comma[buf->context_depth - 1]; } void json_set_needs_comma(json_t *buf) { buf->needs_comma[buf->context_depth - 1] = true; } static void json_ensure_capacity(json_t *buf, size_t needed) { if (buf->length + needed >= buf->capacity) { size_t new_capacity = buf->capacity += JSON_BUF_INITIAL_CAPACITY; while (buf->length + needed >= new_capacity) { new_capacity += JSON_BUF_INITIAL_CAPACITY; } char *new_data = realloc(buf->data, new_capacity); if (!new_data) { error_fatal("realloc: %s", strerror(errno)); } buf->data = new_data; buf->capacity = new_capacity; } } void json_append(json_t *buf, const char *fmt, ...) { va_list args; va_start(args, fmt); int needed = vsnprintf(NULL, 0, fmt, args); va_end(args); if (needed < 0) { error_fatal("vsnprintf: %s", strerror(errno)); } json_ensure_capacity(buf, (size_t)needed + 1); va_start(args, fmt); vsprintf(buf->data + buf->length, fmt, args); va_end(args); buf->length += (size_t)needed; } void json_init(json_t *buf) { buf->capacity = JSON_BUF_INITIAL_CAPACITY; buf->length = 0; buf->data = malloc(buf->capacity); if (!buf->data) { error_fatal("malloc: %s", strerror(errno)); } buf->data[0] = '\0'; buf->context_depth = 0; memset(buf->needs_comma, 0, sizeof(buf->needs_comma)); } void json_free(json_t *buf) { if (buf->data) { free(buf->data); } buf->data = NULL; buf->length = 0; buf->capacity = 0; memset(buf->needs_comma, 0, sizeof(buf->needs_comma)); buf->context_depth = 0; } const char *json_get(json_t *buf) { return buf && buf->data ? buf->data : "{}"; } void json_escape_string(json_t *buf, const char *input) { json_append(buf, "\""); for (size_t i = 0; input[i] != '\0'; i++) { char c = input[i]; switch(c) { case '\\': json_append(buf, "\\\\"); break; case '"': json_append(buf, "\\\""); break; case '\b': json_append(buf, "\\b"); break; case '\f': json_append(buf, "\\f"); break; case '\n': json_append(buf, "\\n"); break; case '\r': json_append(buf, "\\r"); break; case '\t': json_append(buf, "\\t"); break; default: if ((unsigned char)c < 0x20) { json_append(buf, "\\u%04x", c); } else { json_append(buf, "%c", c); } } } json_append(buf, "\""); } void json_start_object(json_t *buf) { if (json_needs_comma(buf)) { json_append(buf, ","); } json_append(buf, "{"); json_push_context(buf, JSON_CONTEXT_OBJECT); } void json_end_object(json_t *buf) { json_append(buf, "}"); json_pop_context(buf); } void json_add_string(json_t *buf, const char *key, const char *value) { if (json_needs_comma(buf)) { json_append(buf, ","); } json_set_needs_comma(buf); json_escape_string(buf, key); json_append(buf, ":"); json_escape_string(buf, value); //buf->needs_comma = true; } void json_add_string_or_null(json_t *buf, const char *key, const char *value) { if (value && value[0] != '\0') { json_add_string(buf, key, value); } else { json_add_null(buf, key); } } void json_add_int(json_t *buf, const char *key, int value) { if (json_needs_comma(buf)) { json_append(buf, ","); } json_set_needs_comma(buf); json_escape_string(buf, key); json_append(buf, ":%d", value); } void json_add_int64(json_t *buf, const char *key, int64_t value) { if (json_needs_comma(buf)) { json_append(buf, ","); } json_set_needs_comma(buf); json_escape_string(buf, key); json_append(buf, ":%lld", value); } void json_add_uint64(json_t *buf, const char *key, uint64_t value) { if (json_needs_comma(buf)) { json_append(buf, ","); } json_set_needs_comma(buf); json_escape_string(buf, key); json_append(buf, ":%llu", value); } void json_add_double(json_t *buf, const char *key, double value) { if (json_needs_comma(buf)) { json_append(buf, ","); } json_set_needs_comma(buf); json_escape_string(buf, key); json_append(buf, ":%f", value); } void json_add_bool(json_t *buf, const char *key, bool value) { if (json_needs_comma(buf)) { json_append(buf, ","); } json_set_needs_comma(buf); json_escape_string(buf, key); json_append(buf, ":%s", value ? "true" : "false"); } void json_add_null(json_t *buf, const char *key) { if (json_needs_comma(buf)) { json_append(buf, ","); } json_set_needs_comma(buf); json_escape_string(buf, key); json_append(buf, ":null"); } void json_start_array(json_t *buf) { if (json_needs_comma(buf)) { json_append(buf, ","); } json_append(buf, "["); json_push_context(buf, JSON_CONTEXT_ARRAY); } void json_end_array(json_t *buf) { json_append(buf, "]"); json_pop_context(buf); } void json_add_array_start(json_t *buf, const char *key) { if (json_needs_comma(buf)) { json_append(buf, ","); } json_escape_string(buf, key); json_append(buf, ":"); json_push_context(buf, JSON_CONTEXT_ARRAY); json_append(buf, "["); buf->needs_comma[buf->context_depth - 1] = false; } void json_array_add_string(json_t *buf, const char *value) { if (json_needs_comma(buf)) { json_append(buf, ","); } json_escape_string(buf, value); json_set_needs_comma(buf); } void json_array_add_int(json_t *buf, int value) { if (json_needs_comma(buf)) { json_append(buf, ","); } json_set_needs_comma(buf); json_append(buf, "%d", value); } void json_array_add_int64(json_t *buf, int64_t value) { if (json_needs_comma(buf)) { json_append(buf, ","); } json_set_needs_comma(buf); json_append(buf, "%lld", value); } void json_array_add_uint64(json_t *buf, uint64_t value) { if (json_needs_comma(buf)) { json_append(buf, ","); } json_set_needs_comma(buf); json_append(buf, "%llu", value); } void json_array_add_double(json_t *buf, double value) { if (json_needs_comma(buf)) { json_append(buf, ","); } json_set_needs_comma(buf); json_append(buf, "%f", value); } void json_array_add_bool(json_t *buf, bool value) { if (json_needs_comma(buf)) { json_append(buf, ","); } json_set_needs_comma(buf); json_append(buf, "%s", value ? "true" : "false"); } void json_array_add_null(json_t *buf) { if (json_needs_comma(buf)) { json_append(buf, ","); } json_set_needs_comma(buf); json_append(buf, "null"); }