From a44ce5c6f74fb777408b455ae21ca545447c0ade Mon Sep 17 00:00:00 2001 From: Martin Kagamino Lehoux Date: Sat, 23 Aug 2025 15:31:28 +0200 Subject: json: parse negative numbers --- CMakeLists.txt | 2 +- Makefile | 4 ++-- README.md | 18 ++++++++++-------- src/json.c | 16 +++++++++------- src/vec.c | 2 +- tests/test_json.c | 12 ++++++++++++ 6 files changed, 35 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f2f8ee..94d59a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ set(CMAKE_C_STANDARD 17) file(GLOB SOURCES "src/*.c") file(GLOB TEST_SOURCES "test/*.c") -add_compile_options(-Wall -pedantic -Wextra -march=native) +add_compile_options(-Wall -pedantic -Wextra -mavx2 -mno-avx512f) add_executable(main main.c ${SOURCES}) target_link_libraries(main m) diff --git a/Makefile b/Makefile index f4c5e8f..30f1f1b 100644 --- a/Makefile +++ b/Makefile @@ -11,8 +11,8 @@ release: profile: mkdir -p Profile cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -B Profile - cmake --build Release - valgrind --tool=callgrind --dump-instr=yes --callgrind-out-file=callgrind.out ./Release/main 100MB.json 1 + cmake --build Profile + valgrind --tool=callgrind --dump-instr=yes --callgrind-out-file=callgrind.out ./Profile/main 100MB.json 1 kcachegrind callgrind.out test: diff --git a/README.md b/README.md index d721753..b3d5b22 100644 --- a/README.md +++ b/README.md @@ -6,18 +6,20 @@ - Fix tests in release - Static functions for private? - `valgrind --leak-check=full ./your_program` -- negative numbers - web server, Json parsing, struct codegen -- what is a const char * +- what is a const char \* +- whitechar simd +- assert not in release ## Benchmarks -| version | result | throughput | ram| -|---|---|---|---| -|json.loads|652 ms ± 6.65 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)||| -|v1|336 ms ± 163 ms per loop (mean ± std. dev. of 10 runs, 1 loop each)||| -|no 0 mem alloc|299 ms ± 8 ms per loop (mean ± std. dev. of 10 runs, 1 loop each)||| -|Str_view|272 ms ± 6 ms per loop (mean ± std. dev. of 10 runs, 1 loop each)|330.46 MB/s|1255 MB| +| version | result | throughput | ram | +| -------------- | ------------------------------------------------------------------- | ----------- | ------- | +| json.loads | 652 ms ± 6.65 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) | | | +| v1 | 336 ms ± 163 ms per loop (mean ± std. dev. of 10 runs, 1 loop each) | | | +| no 0 mem alloc | 299 ms ± 8 ms per loop (mean ± std. dev. of 10 runs, 1 loop each) | | | +| Str_view | 272 ms ± 6 ms per loop (mean ± std. dev. of 10 runs, 1 loop each) | 330.46 MB/s | 1255 MB | +| simd | 254 ms ± 6 ms per loop (mean ± std. dev. of 10 runs, 1 loop each) | 353.36 MB/s | 1255 MB | ```c void print_bytes(byte_32 *data) { diff --git a/src/json.c b/src/json.c index 99ae01b..a237527 100644 --- a/src/json.c +++ b/src/json.c @@ -121,14 +121,15 @@ parse_result parse_any(Arena *a, byte *src, uptr pos, uptr max_pos); parse_result parse_number(byte *src, uptr start_pos) { parse_result result = {0}; - result.pos = start_pos; + bool is_neg = src[start_pos] == '-'; + result.pos = start_pos + is_neg; i32 integer = 0; while (isdigit(src[result.pos])) { integer = integer * 10 + (src[result.pos] - '0'); result.pos++; } if (src[result.pos] != '.') { - result.value = JSON_integer(integer); + result.value = JSON_integer(is_neg ? -integer : integer); return result; } result.pos++; @@ -139,7 +140,8 @@ parse_result parse_number(byte *src, uptr start_pos) { decimal_place *= 0.1; result.pos++; } - result.value = JSON_float((f32)integer + fractional_part); + result.value = JSON_float(is_neg ? -(f32)integer - fractional_part + : (f32)integer + fractional_part); return result; } @@ -177,7 +179,7 @@ parse_result parse_array(Arena *a, byte *src, uptr start_pos, uptr max_pos) { return result; } -parse_result parse_string(Arena *a, byte *src, uptr start_pos, uptr max_pos) { +parse_result parse_string(byte *src, uptr start_pos, uptr max_pos) { assert_parse(src[start_pos] == '"', "invalid string start", src, start_pos); parse_result result = {0}; result.pos = start_pos + 1; @@ -224,7 +226,7 @@ parse_result parse_object(Arena *a, byte *src, uptr start_pos, uptr max_pos) { return result; } for (;;) { - parse_result key_result = parse_string(a, src, result.pos, max_pos); + parse_result key_result = parse_string(src, result.pos, max_pos); result.pos = absorb_whitespaces(src, key_result.pos); assert_parse(src[result.pos] == ':', "invalid keyval delim", src, result.pos); @@ -249,9 +251,9 @@ parse_result parse_object(Arena *a, byte *src, uptr start_pos, uptr max_pos) { parse_result parse_any(Arena *a, byte *src, uptr pos, uptr max_pos) { pos = absorb_whitespaces(src, pos); - if (isdigit(src[pos])) return parse_number(src, pos); + if (isdigit(src[pos]) || src[pos] == '-') return parse_number(src, pos); if (src[pos] == '[') return parse_array(a, src, pos, max_pos); - if (src[pos] == '"') return parse_string(a, src, pos, max_pos); + if (src[pos] == '"') return parse_string(src, pos, max_pos); if (src[pos] == '{') return parse_object(a, src, pos, max_pos); if (memcmp(src + pos, "true", 4) == 0) { parse_result result = {0}; diff --git a/src/vec.c b/src/vec.c index a5c2fb4..e64db9b 100644 --- a/src/vec.c +++ b/src/vec.c @@ -5,7 +5,7 @@ #include "../include/vec.h" Vec _Vec_new(Arena *a, size tsize, size cap, size align) { - assert(cap > 0); + assert(cap > 0, "negative cap"); Vec v = {0}; v.tsize = tsize; v.cap = cap; diff --git a/tests/test_json.c b/tests/test_json.c index e050c72..1aec8c5 100644 --- a/tests/test_json.c +++ b/tests/test_json.c @@ -216,3 +216,15 @@ i32 test_json_complex_multiline() { return 0; } + +i32 test_negative_numbers() { + Arena perm = Arena_new(128e3); + JSON doc = JSON_parse_raw(&perm, "[-1, -2.5]"); + + JSON integer = *Vec_get(doc.as.array, JSON, 0); + t_assert(integer.as.integer == -1); + JSON floating = *Vec_get(doc.as.array, JSON, 1); + t_assert(floating.as.floating == -2.5); + + return 0; +} -- cgit v1.3