From b997e31ac151679a516fd76da45bb36931a13e40 Mon Sep 17 00:00:00 2001 From: Martin Kagamino Lehoux Date: Tue, 22 Jul 2025 19:12:03 +0200 Subject: refactor(json): More consistent init --- README.md | 7 ++----- json.c | 61 +++++++++++++++++++++++++++++++++++++------------------------ test_json.c | 29 +++++++++++++++++++++++++++++ test_vec.c | 25 +++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 29 deletions(-) create mode 100644 test_vec.c diff --git a/README.md b/README.md index 6bc3aea..b8b1035 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,15 @@ ## TODO -- Testing setup - Vec_get not safe, I made a type mistake. And should be more efficient for loops - Fix tests in release - Benchmark 100MB file - json.loads: 526 ms ± 77 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) -- Vec_new returns pointer - More modern include pragma (once) - File structure - Static functions for private? -- JSON float -- JSON: remove pointer and use bitflag -- JSON: tagged union for memory +- JSON: tagged union for memory, remove pointer and use bitflag +- `valgrind --leak-check=full ./your_program` ## Resources diff --git a/json.c b/json.c index dc1ac67..cf78baa 100644 --- a/json.c +++ b/json.c @@ -20,16 +20,24 @@ JSONObject *JSONObject_new(Arena *a) { } i32 JSON_fprint(FILE *w, JSON json) { - if (json.string != 0) - return Str_fprint(w, *json.string); + if (json.string != 0) { + fprintf(w, "\""); + Str_fprint(w, *json.string); + return fprintf(w, "\""); + } if (json.integer != 0) return fprintf(w, "%d", *json.integer); + if (json.floating != 0) + return fprintf(w, "%f", *json.floating); + if (json.boolean != 0) + return fprintf(w, "%s", *json.boolean ? "true" : "false"); if (json.object != 0) { fprintf(w, "{"); for (size i = 0; i < json.object->len; i++) { Str key = *Vec_get(&json.object->keys, Str, i); + fprintf(w, "\""); Str_fprint(w, key); - fprintf(w, ": "); + fprintf(w, "\": "); JSON value = *Vec_get(&json.object->values, JSON, i); JSON_fprint(w, value); if (i < json.object->len - 1) @@ -65,7 +73,10 @@ typedef struct { } parse_number_result; parse_number_result parse_number(Arena *a, byte *src, uptr start_pos) { - parse_number_result result = {.pos = start_pos}; + parse_number_result result = {0}; + result.pos = start_pos; + result.floating = 0; + result.integer = 0; i32 integer = 0; while (isdigit(src[result.pos])) { integer = integer * 10 + (src[result.pos] - '0'); @@ -134,7 +145,8 @@ typedef struct { parse_string_result parse_string(Arena *a, byte *src, uptr start_pos) { assert(src[start_pos] == '"'); - parse_string_result result = {.pos = start_pos + 1}; + parse_string_result result = {0}; + result.pos = start_pos + 1; while (src[result.pos] != '"' && src[result.pos - 1] != '\\') { result.pos++; @@ -154,37 +166,38 @@ typedef struct { parse_object_result parse_object(Arena *a, byte *src, uptr start_pos) { assert(src[start_pos] == '{'); - parse_object_result result = {.pos = start_pos + 1}; + parse_object_result result = {0}; + result.pos = start_pos + 1; result.object = JSONObject_new(a); - start_pos = absorb_whitespaces(src, start_pos + 1); - if (src[start_pos] == '}') { - result.pos = start_pos + 1; + result.pos = absorb_whitespaces(src, result.pos); + if (src[result.pos] == '}') { + result.pos++; return result; } for (;;) { - assert(src[start_pos] == '"'); - parse_string_result str_result = parse_string(a, src, start_pos); - Str *key = str_result.string; - start_pos = absorb_whitespaces(src, str_result.pos); - assert(src[start_pos] == ':'); - start_pos = absorb_whitespaces(src, start_pos + 1); - parse_any_result any_result = parse_any(a, src, start_pos); + assert(src[result.pos] == '"'); + parse_string_result key_result = parse_string(a, src, result.pos); + result.pos = absorb_whitespaces(src, key_result.pos); + assert(src[result.pos] == ':'); + result.pos = absorb_whitespaces(src, result.pos + 1); + parse_any_result value_result = parse_any(a, src, result.pos); result.object->len++; - Vec_push(a, &result.object->keys, key); - Vec_push(a, &result.object->values, any_result.value); - start_pos = absorb_whitespaces(src, any_result.pos); - if (src[start_pos] != ',') { + Vec_push(a, &result.object->keys, key_result.string); + Vec_push(a, &result.object->values, value_result.value); + result.pos = absorb_whitespaces(src, value_result.pos); + if (src[result.pos] != ',') { break; } - start_pos = absorb_whitespaces(src, start_pos + 1); + result.pos = absorb_whitespaces(src, result.pos + 1); } - assert(src[start_pos] == '}'); - result.pos = start_pos + 1; + assert(src[result.pos] == '}'); + result.pos++; return result; } parse_any_result parse_any(Arena *a, byte *src, uptr start_pos) { - parse_any_result result = {.pos = start_pos}; + parse_any_result result = {0}; + result.pos = start_pos; result.value = new(a, JSON, 1); if (isdigit(src[result.pos])) { parse_number_result number_result = parse_number(a, src, result.pos); diff --git a/test_json.c b/test_json.c index 70b3965..7f0d07c 100644 --- a/test_json.c +++ b/test_json.c @@ -137,3 +137,32 @@ i32 test_json_null() { return 0; } + +i32 test_json_multiline() { + Arena perm = Arena_new(128e3); + + JSON doc = JSON_parse( + &perm, "{\n" + " \"generated_at\": \"2025-07-22T10:36:30.974926\",\n" + " \"target_size_mb\": 1,\n" + " \"generator_version\": \"1.0\"\n" + "}"); + JSON target_size_mb = *JSONObject_get(doc.object, S("target_size_mb")); + t_assert(*target_size_mb.integer == 1); + JSON generated_at = *JSONObject_get(doc.object, S("generated_at")); + t_assert(Str_equals(*generated_at.string, S("2025-07-22T10:36:30.974926"))); + + return 0; +} + +i32 test_json_nested_object() { + Arena perm = Arena_new(128e3); + + JSON doc = JSON_parse(&perm, "{\"1\":{\"2\":{}}}"); + + JSON lvl_1 = *JSONObject_get(doc.object, S("1")); + t_assert(lvl_1.object != 0); + JSON lvl_2 = *JSONObject_get(lvl_1.object, S("2")); + t_assert(lvl_2.object != 0); + return 0; +} diff --git a/test_vec.c b/test_vec.c new file mode 100644 index 0000000..18cb880 --- /dev/null +++ b/test_vec.c @@ -0,0 +1,25 @@ +#include "testing.h" +#include "vec.h" + +i32 test_int_vec() { + Arena perm = Arena_new(128e3); + + Vec *vec = Vec_new(&perm, i32, 2); + i32 *tmp = new(&perm, i32, 1); + *tmp = 1; + Vec_push(&perm, vec, tmp); + *tmp = 2; + Vec_push(&perm, vec, tmp); + + t_assert_equal(*Vec_get(vec, i32, 0), 1); + t_assert_equal(*Vec_get(vec, i32, 1), 2); + t_assert_equal(vec->cap, 2); + + *tmp = 3; + Vec_push(&perm, vec, tmp); + + t_assert_equal(*Vec_get(vec, i32, 2), 3); + t_assert_equal(vec->cap, 4); + + return 0; +} -- cgit v1.3