From dde8c3ee42386521ee830abdd249d2c6ff7c38f5 Mon Sep 17 00:00:00 2001 From: sfja Date: Mon, 23 Mar 2026 16:19:50 +0100 Subject: [PATCH] fix --- Makefile | 18 +++- data.json | 5 - src/collections.c | 98 +++++++++++++---- src/collections.h | 13 +++ src/json.h | 34 ++++-- src/json_parse.c | 42 +++++--- src/json_query.c | 252 +++++++++++++++++++++++++++++++++++++++++++ src/json_stringify.c | 100 +++++++++++++++++ src/json_value.c | 195 ++++++++++++++++++++++++++++++--- src/main.c | 34 ++++-- 10 files changed, 715 insertions(+), 76 deletions(-) delete mode 100644 data.json create mode 100644 src/json_query.c create mode 100644 src/json_stringify.c diff --git a/Makefile b/Makefile index abd6afb..a84a1d6 100644 --- a/Makefile +++ b/Makefile @@ -1,26 +1,36 @@ MAKEFLAGS += -j16 -CFLAGS=-std=c17 -pedantic-errors -Wall -Wextra -Wconversion -g +CFLAGS=-std=c17 -pedantic-errors -Wall -Wextra -Wconversion LDFLAGS= # CFLAGS+=-fsanitize=address +RELEASE=0 + +ifeq ($(RELEASE),1) + CFLAGS += -O3 +else + CFLAGS += -g +endif + build_dir = build obj_dir = $(build_dir)/obj sources = \ src/main.c \ - src/json_parse.c \ + src/collections.c \ src/json_value.c \ - src/collections.c + src/json_parse.c \ + src/json_query.c \ + src/json_stringify.c target=$(build_dir)/jq all: $(target) debug: $(target) - gdb tui -ex 'b main.c:254' -ex 'b collections.c:161' -ex 'r' --args build/jq '.' data.json + gdb -ex 'r' --args build/jq '.' data.json $(target): $(sources:%.c=$(obj_dir)/%.o) gcc -o $@ $(CFLAGS) $(LDFLAGS) $^ diff --git a/data.json b/data.json deleted file mode 100644 index a4c1715..0000000 --- a/data.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "baz": false, - "foo": [123, "bar"] -} - diff --git a/src/collections.c b/src/collections.c index 08afe88..c52f6d3 100644 --- a/src/collections.c +++ b/src/collections.c @@ -58,6 +58,8 @@ void *array_insert_at(void **data, return src_ptr; } +#define small_count_max 3 + void smallarray_construct(struct smallarray *a) { *a = (struct smallarray) { 0 }; @@ -77,27 +79,30 @@ static bool sa_is_big(struct smallarray const *a) static void sa_push_big(struct smallarray *a, void *value) { - size_t capacity = 8; - void **ptr = malloc(capacity * sizeof(void *)); - size_t count = 0; + if (!sa_is_big(a)) { + size_t capacity = 8; + void **ptr = malloc(capacity * sizeof(void *)); + size_t count = 0; - for (size_t i = 0; i < 3; ++i) { - if ((a->smalldata[i] & 1) == 0) - break; - ptr[i] = (void *)(a->smalldata[i] & ~1ull); - count += 1; + for (size_t i = 0; i < small_count_max; ++i) { + if ((a->smalldata[i] & 1) == 0) + break; + ptr[i] = (void *)(a->smalldata[i] & ~1ull); + count += 1; + } + + a->capacity = capacity; + a->data = ptr; + a->count = count; } - a->capacity = capacity; - a->data = ptr; - a->count = count; array_push( (void **)&a->data, &a->capacity, &a->count, &value, sizeof(void *)); } static void sa_push_small(struct smallarray *a, void *value) { - for (size_t i = 0; i < 3; ++i) { + for (size_t i = 0; i < small_count_max; ++i) { if (a->smalldata[i] & 1) continue; a->smalldata[i] = (size_t)value | 1; @@ -109,7 +114,7 @@ void smallarray_push(struct smallarray *a, void *value) { assert((size_t)value % 2 == 0 && "pointer must be 2 bytes aligned"); - if (sa_is_big(a)) { + if (sa_is_big(a) || smallarray_count(a) >= small_count_max) { sa_push_big(a, value); } else { sa_push_small(a, value); @@ -122,7 +127,7 @@ size_t smallarray_count(struct smallarray const *a) return a->count; } else { size_t count = 0; - for (size_t i = 0; i < 3; ++i) { + for (size_t i = 0; i < small_count_max; ++i) { if ((a->smalldata[i] & 1) == 0) break; count += 1; @@ -136,12 +141,17 @@ void *smallarray_get(struct smallarray *a, size_t idx) if (sa_is_big(a)) { return a->data[idx]; } else { - if (idx >= 3 || (a->smalldata[idx] & 1) == 0) + if (idx >= small_count_max || (a->smalldata[idx] & 1) == 0) return NULL; return (void *)(a->smalldata[idx] & ~1ull); } } +void const *smallarray_get_const(struct smallarray const *a, size_t idx) +{ + return smallarray_get((struct smallarray *)a, idx); +} + static uint64_t hash_key(char const *data) { // djb2 @@ -172,14 +182,15 @@ static uint64_t hash_key_sized(char const *data, size_t size) void hashmap_construct(struct hashmap *t) { - *t = (struct hashmap) { NULL, 0, 0 }; + *t = (struct hashmap) { NULL, 0, 0, NULL, 0, 0 }; } void hashmap_destroy(struct hashmap *t) { - if (t->buckets) { + if (t->buckets) free(t->buckets); - } + if (t->keys) + free(t->keys); } static struct hash_entry *find_entry(struct hashmap *m, uint64_t hash) @@ -256,7 +267,31 @@ static struct hash_entry *make_entry(struct hashmap *m, uint64_t hash) assert(false); } -static void hashmap_set_internal(struct hashmap *m, uint64_t hash, void *value) +static void insert_key( + struct hashmap *m, char const *key, size_t key_size, uint64_t hash) +{ + key_size = key_size != 0 ? key_size : strlen(key); + char *key_value = malloc(key_size + 1); + strncpy(key_value, key, key_size); + key_value[key_size] = '\0'; + + struct hash_key_entry key_entry = { + .hash = hash, + .value = key_value, + }; + + array_push((void **)&m->keys, + &m->keys_capacity, + &m->keys_count, + &key_entry, + sizeof(key_entry)); +} + +static void hashmap_set_internal(struct hashmap *m, + char const *key, + size_t key_size, + uint64_t hash, + void *value) { if (m->buckets_count == 0) { struct hash_bucket *bucket = insert_bucket_at(m, 0); @@ -264,6 +299,8 @@ static void hashmap_set_internal(struct hashmap *m, uint64_t hash, void *value) bucket->count += 1; bucket->first_hash = hash; bucket->last_hash = hash; + + insert_key(m, key, key_size, hash); return; } @@ -271,6 +308,8 @@ static void hashmap_set_internal(struct hashmap *m, uint64_t hash, void *value) if (!entry) { entry = make_entry(m, hash); entry->hash = hash; + + insert_key(m, key, key_size, hash); } entry->value = value; return; @@ -279,14 +318,14 @@ static void hashmap_set_internal(struct hashmap *m, uint64_t hash, void *value) void hashmap_set(struct hashmap *m, char const *key, void *value) { uint64_t hash = hash_key(key); - hashmap_set_internal(m, hash, value); + hashmap_set_internal(m, key, 0, hash, value); } void hashmap_set_sized( struct hashmap *m, char const *key, size_t key_size, void *value) { uint64_t hash = hash_key_sized(key, key_size); - hashmap_set_internal(m, hash, value); + hashmap_set_internal(m, key, key_size, hash, value); } bool hashmap_has(struct hashmap *m, char const *key) @@ -311,6 +350,23 @@ void *hashmap_get_sized(struct hashmap *m, char const *key, size_t key_size) return hashmap_get_internal(m, hash); } +void *hashmap_get_hash(struct hashmap *m, size_t hash) +{ + return hashmap_get_internal(m, hash); +} + +void const *hashmap_get_hash_const(struct hashmap const *m, size_t hash) +{ + return hashmap_get_internal((struct hashmap *)m, hash); +} + +void hashmap_keys( + struct hashmap const *m, struct hash_key_entry const **keys, size_t *count) +{ + *keys = m->keys; + *count = m->keys_count; +} + struct blockalloc_block { unsigned char *data; size_t size; diff --git a/src/collections.h b/src/collections.h index 87b92bb..1fb386d 100644 --- a/src/collections.h +++ b/src/collections.h @@ -34,6 +34,7 @@ void smallarray_destroy(struct smallarray *a); void smallarray_push(struct smallarray *a, void *value); size_t smallarray_count(struct smallarray const *a); void *smallarray_get(struct smallarray *a, size_t idx); +void const *smallarray_get_const(struct smallarray const *a, size_t idx); struct hash_entry { uint64_t hash; @@ -49,10 +50,18 @@ struct hash_bucket { uint64_t last_hash; }; +struct hash_key_entry { + uint64_t hash; + char *value; +}; + struct hashmap { struct hash_bucket *buckets; size_t buckets_capacity; size_t buckets_count; + struct hash_key_entry *keys; + size_t keys_capacity; + size_t keys_count; }; void hashmap_construct(struct hashmap *m); @@ -63,6 +72,10 @@ void hashmap_set_sized( bool hashmap_has(struct hashmap *m, char const *key); void *hashmap_get(struct hashmap *m, char const *key); void *hashmap_get_sized(struct hashmap *m, char const *key, size_t key_size); +void *hashmap_get_hash(struct hashmap *m, size_t hash); +void const *hashmap_get_hash_const(struct hashmap const *m, size_t hash); +void hashmap_keys( + struct hashmap const *m, struct hash_key_entry const **keys, size_t* count); #define blockalloc_default_block 4096 diff --git a/src/json.h b/src/json.h index 7515b13..385ff50 100644 --- a/src/json.h +++ b/src/json.h @@ -1,6 +1,7 @@ #ifndef JSON_H #define JSON_H +#include "collections.h" #include #include #include @@ -23,29 +24,50 @@ void json_free(struct json_value *value); bool json_is(struct json_value const *value, enum json_type type); +enum json_type json_get_type(struct json_value const *value); bool json_get_bool(struct json_value const *value); int64_t json_get_int(struct json_value const *value); double json_get_float(struct json_value const *value); -char *json_get_string(struct json_value *value); +char const *json_get_string(struct json_value const *value); -void json_set_bool(struct json_value *value, bool val); -void json_set_int(struct json_value *value, int64_t val); -void json_set_float(struct json_value *value, double val); -void json_set_string(struct json_value *value, char *val); +void json_set_null(struct json_value **value); +void json_set_bool(struct json_value **value, bool val); +void json_set_int(struct json_value **value, int64_t val); +void json_set_float(struct json_value **value, double val); +void json_set_string(struct json_value *value, char *vaAl); +struct json_value *json_new_int(int64_t val); +struct json_value *json_new_float(double val); + +size_t json_array_count(struct json_value const *array); struct json_value *json_idx(struct json_value *array, size_t idx); +struct json_value const *json_idx_const(struct json_value const *array, size_t idx); void json_push(struct json_value *array, struct json_value *value); struct json_value *json_key(struct json_value *object, char const *key); struct json_value *json_key_sized( struct json_value *object, char const *key, size_t key_size); +struct json_value const *json_key_hash_const( + struct json_value const *object, size_t hash); void json_set( struct json_value *object, char const *key, struct json_value *value); void json_set_sized(struct json_value *object, char const *key, size_t key_size, struct json_value *value); +void json_object_keys( + struct json_value const *object, struct hash_key_entry const **keys, size_t *count); -struct json_value *json_parse(char const *text, size_t text_size); +struct json_value *json_parse( + char const *text, size_t text_size, struct blockalloc* alloc); + +struct json_value *json_query(struct json_value *val, char const *query); + +typedef int JsonWriteCb(void* self, const char* data, size_t size); + +int json_stringify( + struct json_value const *node, + JsonWriteCb write, + void* self); #endif diff --git a/src/json_parse.c b/src/json_parse.c index a59fa85..65f7070 100644 --- a/src/json_parse.c +++ b/src/json_parse.c @@ -110,10 +110,13 @@ static struct tok t_make_ident_tok( struct tokenizer *t, struct loc loc, size_t *i) { char const *kws[] = { "null", "false", "true" }; + size_t const lens[] = { 4, 5, 4 }; enum tokty tys[] = { tt_null, tt_false, tt_true }; - for (size_t kw_i = 0; kw_i < sizeof(kws) / sizeof(kws[0]); ++kw_i) { - if (strncmp(kws[kw_i], &t->text[loc.idx], *i - loc.idx)) { - return t_tok(t, tys[kw_i], loc); + for (size_t j = 0; j < sizeof(kws) / sizeof(kws[0]); ++j) { + size_t len = *i - loc.idx; + if (lens[j] == len && strncmp(kws[j], &t->text[loc.idx], len) == 0) { + + return t_tok(t, tys[j], loc); } } report(loc, "invalid identifier", t->text, t->len); @@ -201,7 +204,7 @@ static struct tok tokenizer_next(struct tokenizer *t) struct parser { struct tokenizer tokenizer; struct tok tok; - struct blockalloc allocator; + struct blockalloc* alloc; }; static void p_step(struct parser *p) @@ -210,14 +213,13 @@ static void p_step(struct parser *p) } static void parser_construct( - struct parser *p, char const *text, size_t text_len) + struct parser *p, char const *text, size_t text_len, struct blockalloc* alloc) { *p = (struct parser) { .tokenizer = (struct tokenizer) { text, text_len, 0, 1, 1, false }, .tok = (struct tok) { 0 }, - .allocator = (struct blockalloc) { 0 }, + .alloc = alloc, }; - blockalloc_construct(&p->allocator); p_step(p); } @@ -300,18 +302,16 @@ static struct json_value *parser_parse(struct parser *p) return val; } else if (*ty == tt_int) { int64_t value = strtol(p->tok.ptr, NULL, 10); - struct json_value *val = json_new(json_int); - json_set_int(val, value); + struct json_value *val = json_new_int(value); p_step(p); return val; } else if (*ty == tt_float) { double value = strtod(p->tok.ptr, NULL); - struct json_value *val = json_new(json_float); - json_set_float(val, value); + struct json_value *val = json_new_float(value); p_step(p); return val; } else if (*ty == tt_string) { - char *value = blockalloc_alloc(&p->allocator, p->tok.len - 2 + 1, 2); + char *value = blockalloc_alloc(p->alloc, p->tok.len - 2 + 1, 2); strncpy(value, p->tok.ptr + 1, p->tok.len - 2); value[p->tok.len - 2] = '\0'; struct json_value *val = json_new(json_string); @@ -323,17 +323,27 @@ static struct json_value *parser_parse(struct parser *p) } else if (*ty == '{') { return parser_parse_object(p, ty); } else { - p_report(p, loc, "expected expression"); + if (!p->tokenizer.failed) { + p_report(p, loc, "expected expression"); + } return NULL; } } -struct json_value *json_parse(char const *text, size_t text_size) +struct json_value *json_parse( + char const *text, size_t text_size, struct blockalloc* alloc) { text_size = text_size > 0 ? text_size : strlen(text); struct parser p; - parser_construct(&p, text, text_size); + parser_construct(&p, text, text_size, alloc); - return parser_parse(&p); + struct json_value *result = parser_parse(&p); + + if (result && p.tokenizer.failed) { + json_free(result); + return NULL; + } + + return result; } diff --git a/src/json_query.c b/src/json_query.c new file mode 100644 index 0000000..03d11e7 --- /dev/null +++ b/src/json_query.c @@ -0,0 +1,252 @@ +#include "json.h" +#include +#include +#include +#include +#include + +static void report( + size_t idx, char const *message, char const *text, size_t text_len) +{ + fprintf(stderr, "error: %s\n", message); + if (!text) + return; + static char const *spaces = " "; + printf("|%.*s\n" + "|%.*s^\n", + (int)text_len, + text, + (int)idx, + spaces); +} + +enum tokty { + tt_eof, + tt_string, + tt_ident, + tt_int = '0', + tt_dot = '.', + tt_lbracket = '[', + tt_rbracket = ']', +}; + +struct tok { + enum tokty ty; + char const *ptr; + size_t idx; + size_t len; +}; + +struct tokenizer { + char const *text; + size_t len; + size_t idx; + bool failed; +}; + +static void t_step(struct tokenizer *t) +{ + if (t->idx >= t->len) + return; + t->idx += 1; +} + +static struct tok t_tok(struct tokenizer *t, enum tokty ty, size_t idx) +{ + return (struct tok) { ty, &t->text[idx], idx, t->idx - idx }; +} +static struct tok tokenizer_next(struct tokenizer *t); + +static struct tok t_make_number_tok( + struct tokenizer *t, size_t idx, size_t *i) +{ + while (*i < t->len && t->text[*i] >= '0' && t->text[*i] <= '9') { + t_step(t); + } + return t_tok(t, tt_int, idx); +} + +static struct tok t_make_string_tok( + struct tokenizer *t, size_t idx, size_t *i) +{ + t_step(t); + while (*i < t->len && t->text[*i] != '\"') { + if (t->text[*i] == '\\') { + t_step(t); + if (*i >= t->len) + break; + } + t_step(t); + } + if (*i >= t->len && t->text[*i] != '\"') { + report(idx, "malformed string", t->text, t->len); + t->failed = true; + return tokenizer_next(t); + } + t_step(t); + return t_tok(t, tt_string, idx); +} + +static struct tok tokenizer_next(struct tokenizer *t) +{ + size_t idx = t->idx; + size_t *i = &t->idx; + if (*i >= t->len) { + return t_tok(t, tt_eof, idx); + } + bool matched = false; + while (*i < t->len && strchr(" \t\r\n", t->text[*i]) != NULL) { + matched = true; + t_step(t); + } + if (matched) { + return tokenizer_next(t); + } + if (strchr(".[]0", t->text[*i]) != NULL) { + enum tokty ty = (enum tokty)t->text[*i]; + t_step(t); + return t_tok(t, ty, idx); + } + while (*i < t->len && t->text[*i] >= 'a' && t->text[*i] <= 'z') { + matched = true; + t_step(t); + } + if (matched) { + return t_tok(t, tt_ident, idx); + } + if (t->text[*i] >= '1' && t->text[*i] <= '9') { + return t_make_number_tok(t, idx, i); + } + if (t->text[*i] == '\"') { + return t_make_string_tok(t, idx, i); + } + report(idx, "illegal character", t->text, t->len); + t->failed = true; + t_step(t); + return tokenizer_next(t); +} + +enum path_seg_ty { + pt_error, + pt_eof, + pt_ident_key, + pt_string_key, + pt_idx, +}; + +struct path_seg { + enum path_seg_ty ty; + struct tok tok; +}; + +struct parser { + struct tokenizer tokenizer; + struct tok tok; +}; + +static void p_step(struct parser *p) +{ + p->tok = tokenizer_next(&p->tokenizer); +} + +static void p_report(struct parser *p, size_t idx, char const *message) +{ + report(idx, message, p->tokenizer.text, p->tokenizer.len); +} + +static struct path_seg parse_next(struct parser *p) +{ + size_t idx = p->tok.idx; + enum tokty *ty = &p->tok.ty; + + if (*ty == tt_eof) { + return (struct path_seg) { .ty = pt_eof, .tok = p->tok }; + } else if (*ty == '.') { + p_step(p); + struct tok tok = p->tok; + if (*ty == tt_eof || *ty != tt_ident) { + p_report(p, p->tok.idx, "expected identifier"); + return (struct path_seg) { .ty = pt_error, .tok = p->tok }; + } + p_step(p); + return (struct path_seg) { .ty = pt_ident_key, .tok = tok }; + } else if (*ty == '[') { + p_step(p); + struct tok tok = p->tok; + enum path_seg_ty seg_ty; + if (tok.ty == tt_int) { + seg_ty = pt_idx; + } else if (tok.ty == tt_string) { + seg_ty = pt_string_key; + } else { + p_report(p, p->tok.idx, "expected string or integer"); + return (struct path_seg) { .ty = pt_error, .tok = p->tok }; + } + p_step(p); + if (*ty == tt_eof || *ty != ']') { + p_report(p, p->tok.idx, "expected ']'"); + return (struct path_seg) { .ty = pt_error, .tok = p->tok }; + } + p_step(p); + return (struct path_seg) { .ty = seg_ty, .tok = tok }; + } else { + if (!p->tokenizer.failed) { + p_report(p, idx, "expected expression"); + } + return (struct path_seg) { .ty = pt_error, .tok = p->tok }; + } +} + +struct json_value *resolve_next_path_seg( + struct parser *p, struct json_value* node) +{ + struct path_seg seg = parse_next(p); + switch (seg.ty){ + case pt_error: + return NULL; + case pt_eof: + return node; + case pt_ident_key: { + if (!json_is(node, json_object)) + return NULL; + struct json_value *child = + json_key_sized(node, seg.tok.ptr, seg.tok.len); + return resolve_next_path_seg(p, child); + } + case pt_string_key: { + if (!json_is(node, json_object)) + return NULL; + struct json_value *child = + json_key_sized(node, seg.tok.ptr + 1, seg.tok.len - 2); + return resolve_next_path_seg(p, child); + } + case pt_idx: { + if (!json_is(node, json_array)) + return NULL; + struct json_value *child = + json_idx(node, strtoull(seg.tok.ptr, NULL, 10)); + return resolve_next_path_seg(p, child); + } + } + assert(false); +} + +struct json_value *json_query(struct json_value *val, char const *query) +{ + size_t query_len = strlen(query); + + struct parser p = { + .tokenizer = (struct tokenizer) { + .text = query, + .len = query_len, + .idx = 0, + .failed = false, + }, + .tok = (struct tok){0}, + }; + p_step(&p); + + return resolve_next_path_seg(&p, val); +} + + diff --git a/src/json_stringify.c b/src/json_stringify.c new file mode 100644 index 0000000..db8ec59 --- /dev/null +++ b/src/json_stringify.c @@ -0,0 +1,100 @@ +#include "collections.h" +#include "json.h" +#include +#include + +#define TRY(EXPR) do {int result = (EXPR); if (result != 0) { return result; }} while(0) + +static int write_indent(JsonWriteCb write, void *self, int depth) +{ + for (int i = 0; i < depth; ++i) { + TRY(write(self, " ", 4)); + } + return 0; +} + +static int stringify_pretty_rec( + struct json_value const *node, JsonWriteCb write, void *self, int depth) +{ + static const char* kws[] = { + [json_null] = "null", + [json_false] = "false", + [json_true] = "true", + }; + enum json_type ty = json_get_type(node); + switch (ty) { + case json_null: + case json_false: + case json_true: + TRY(write(self, kws[ty], strlen(kws[ty]))); + break; + case json_int: { + char buf[25] = ""; + int len = snprintf(buf, sizeof(buf) - 1, "%ld", json_get_int(node)); + TRY(write(self, buf, (size_t)len)); + break; + } + case json_float: { + char buf[32] = ""; + int len = snprintf(buf, sizeof(buf) - 1, "%f", json_get_float(node)); + TRY(write(self, buf, (size_t)len)); + break; + } + case json_string: { + const char* str = json_get_string(node); + TRY(write(self, "\"", 1)); + TRY(write(self, str, strlen(str))); + TRY(write(self, "\"", 1)); + break; + } + case json_array: { + size_t count = json_array_count(node); + TRY(write(self, "[", 1)); + for (size_t i = 0; i < count; ++i) { + struct json_value const *child = json_idx_const(node, i); + + if (i != 0) { + TRY(write(self, ",", 1)); + } + TRY(write(self, "\n", 1)); + TRY(write_indent(write, self, depth + 1)); + TRY(stringify_pretty_rec(child, write, self, depth + 1)); + } + TRY(write(self, "\n", 1)); + TRY(write_indent(write, self, depth)); + TRY(write(self, "]", 1)); + break; + } + case json_object: { + struct hash_key_entry const *keys; + size_t count; + json_object_keys(node, &keys, &count); + TRY(write(self, "{", 1)); + for (size_t i = 0; i < count; ++i) { + struct json_value const *child = + json_key_hash_const(node, keys[i].hash); + + if (i != 0) { + TRY(write(self, ",", 1)); + } + TRY(write(self, "\n", 1)); + TRY(write_indent(write, self, depth + 1)); + TRY(write(self, "\"", 1)); + TRY(write(self, keys[i].value, strlen(keys[i].value))); + TRY(write(self, "\": ", 3)); + TRY(stringify_pretty_rec(child, write, self, depth + 1)); + } + TRY(write(self, "\n", 1)); + TRY(write_indent(write, self, depth)); + TRY(write(self, "}", 1)); + break; + } + } + return 0; +} + +int json_stringify( + struct json_value const *node, JsonWriteCb write, void *self) +{ + return stringify_pretty_rec(node, write, self, 0); +} diff --git a/src/json_value.c b/src/json_value.c index 5fd097e..b045b17 100644 --- a/src/json_value.c +++ b/src/json_value.c @@ -19,7 +19,43 @@ struct json_value { }; }; -struct json_value *json_new(enum json_type ty) +#define shallow_int_min (-144115188075855872ll) +#define shallow_int_max 144115188075855871ll + +static bool is_shallow(struct json_value const *ptr) +{ + return ((size_t)ptr >> 1 & 1) != 0; +} + +static enum json_type shallow_ty(struct json_value const *ptr) +{ + return (enum json_type)((size_t)ptr >> 2 & 0xf); +} + +static void set_shallow_ty(struct json_value **ptr, enum json_type ty) +{ + *(size_t *)ptr |= (ty & 0xf) << 2; +} + +static size_t shallow_val(struct json_value const *ptr) +{ + return (size_t)ptr >> 6; +} + +static void set_shallow_val(struct json_value **ptr, size_t val) +{ + *(size_t *)ptr |= val << 6; +} + +static struct json_value *new_shallow(enum json_type ty) +{ + struct json_value *shallow_val = 0; + *(size_t *)&shallow_val |= 0x2; + set_shallow_ty(&shallow_val, ty); + return shallow_val; +} + +static struct json_value *new_deep(enum json_type ty) { struct json_value *v = malloc(sizeof(struct json_value)); *v = (struct json_value) { .ty = ty }; @@ -41,8 +77,28 @@ struct json_value *json_new(enum json_type ty) return v; } +struct json_value *json_new(enum json_type ty) +{ + switch (ty) { + case json_null: + case json_false: + case json_true: + case json_int: + case json_float: + return new_shallow(ty); + case json_string: + case json_array: + case json_object: + return new_deep(ty); + } + assert(false); +} + void json_free(struct json_value *v) { + if (is_shallow(v)) + return; + switch (v->ty) { case json_null: case json_false: @@ -66,57 +122,151 @@ void json_free(struct json_value *v) bool json_is(struct json_value const *value, enum json_type type) { + if (is_shallow(value)) + return shallow_ty(value) == type; + return value->ty == type; } +enum json_type json_get_type(struct json_value const *value) +{ + if (is_shallow(value)) + return shallow_ty(value); + + return value->ty; +} + bool json_get_bool(struct json_value const *value) { - assert(value->ty == json_true || value->ty == json_false); - return value->ty == json_true; + enum json_type ty; + + if (is_shallow(value)) { + ty = shallow_ty(value); + } else { + ty = value->ty; + } + + assert(ty == json_true || ty == json_false); + return ty == json_true; } int64_t json_get_int(struct json_value const *value) { + if (is_shallow(value)) { + assert(shallow_ty(value) == json_int); + return (int64_t)shallow_val(value); + } + assert(value->ty == json_int); return value->int_val; } double json_get_float(struct json_value const *value) { + if (is_shallow(value)) { + assert(shallow_ty(value) == json_float); + size_t raw_val = shallow_val(value) << 6; + return *(double *)&raw_val; + } + assert(value->ty == json_float); return value->float_val; } -char *json_get_string(struct json_value *value) +char const *json_get_string(struct json_value const *value) { assert(value->ty == json_string); return value->string_val; } -void json_set_bool(struct json_value *value, bool val) +void json_set_null(struct json_value **value) { - assert(value->ty == json_true || value->ty == json_false); - value->ty = val ? json_true : json_false; + if (is_shallow(*value)) { + set_shallow_ty(value, json_null); + return; + } + + (*value)->ty = json_null; } -void json_set_int(struct json_value *value, int64_t val) + +void json_set_bool(struct json_value **value, bool val) { - assert(value->ty == json_int); - value->int_val = val; + if (is_shallow(*value)) { + set_shallow_ty(value, val ? json_true : json_false); + } + + (*value)->ty = val ? json_true : json_false; } -void json_set_float(struct json_value *value, double val) +void json_set_int(struct json_value **value, int64_t val) { - assert(value->ty == json_float); - value->float_val = val; + if (is_shallow(*value)) { + if (val >= shallow_int_min && val <= shallow_int_max) { + set_shallow_ty(value, json_int); + set_shallow_val(value, (size_t)val); + return; + } else { + *value = new_deep(json_int); + } + } + + (*value)->ty = json_int; + (*value)->int_val = val; +} +void json_set_float(struct json_value **value, double val) +{ + if (is_shallow(*value)) { + size_t raw_val = *(size_t *)&val; + if ((raw_val & 0x7f) == 0) { + set_shallow_ty(value, json_float); + set_shallow_val(value, raw_val >> 6); + return; + } else { + *value = new_deep(json_float); + } + } + + (*value)->ty = json_float; + (*value)->float_val = val; } void json_set_string(struct json_value *value, char *val) { - assert(value->ty == json_string); + value->ty = json_string; value->string_val = val; } +struct json_value *json_new_int(int64_t val) +{ + struct json_value *node = json_new(json_int); + json_set_int(&node, val); + return node; +} + +struct json_value *json_new_float(double val) +{ + struct json_value *node = json_new(json_float); + json_set_float(&node, val); + return node; +} + +size_t json_array_count(struct json_value const *array) +{ + assert(array->ty == json_array); + return smallarray_count(&array->array_vals); +} + struct json_value *json_idx(struct json_value *array, size_t idx) { assert(array->ty == json_array); + if (idx >= smallarray_count(&array->array_vals)) + return NULL; return smallarray_get(&array->array_vals, idx); } +struct json_value const *json_idx_const(struct json_value const *array, size_t idx) +{ + assert(array->ty == json_array); + if (idx >= smallarray_count(&array->array_vals)) + return NULL; + return smallarray_get_const(&array->array_vals, idx); +} + void json_push(struct json_value *array, struct json_value *value) { assert(array->ty == json_array); @@ -136,6 +286,14 @@ struct json_value *json_key_sized( return hashmap_get_sized(&object->object_fields, key, key_size); } +struct json_value const *json_key_hash_const( + struct json_value const *object, size_t hash) +{ + assert(object->ty == json_object); + return hashmap_get_hash_const(&object->object_fields, hash); +} + + void json_set( struct json_value *object, char const *key, struct json_value *value) { @@ -151,3 +309,12 @@ void json_set_sized(struct json_value *object, assert(object->ty == json_object); hashmap_set_sized(&object->object_fields, key, key_size, value); } + +void json_object_keys( + struct json_value const *object, struct hash_key_entry const **keys, size_t *count) +{ + assert(object->ty == json_object); + *keys = object->object_fields.keys; + *count = object->object_fields.keys_count; +} + diff --git a/src/main.c b/src/main.c index 11a9d34..5c65d2e 100644 --- a/src/main.c +++ b/src/main.c @@ -9,13 +9,20 @@ #include #include +static int stringify_cb(void *self, char const *data, unsigned long size) +{ + (void)self; + fwrite(data, size, 1, stdout); + return 0; +} + int main(int argc, char *argv[]) { if (argc < 3) { fprintf(stderr, "error: incorrect arguments\n"); return EXIT_FAILURE; } - char const *pattern = argv[1]; + char const *query = argv[1]; char const *filename = argv[2]; FILE *file = fopen(filename, "r"); @@ -48,16 +55,23 @@ int main(int argc, char *argv[]) } fclose(file); - struct json_value *val = json_parse(text, file_size); + struct blockalloc alloc = { 0 }; + blockalloc_construct(&alloc); + + struct json_value *parsed = json_parse(text, file_size, &alloc); + if (!parsed) { + fprintf(stderr, "something went wrong\n"); + return EXIT_FAILURE; + } free(text); - typedef struct json_value V; + struct json_value *result = json_query(parsed, query); + if (!result) { + return EXIT_FAILURE; + } + json_stringify(result, stringify_cb, NULL); + fputc('\n', stdout); - V *a = json_key(val, "foo"); - V *b = json_idx(a, 0); - V *c = json_idx(a, 1); - printf(".foo[0] = %ld\n", json_get_int(b)); - printf(".foo[1] = %s\n", json_get_string(c)); - - json_free(val); + json_free(parsed); + blockalloc_destroy(&alloc); }