From 37516080db6878bef6fb1e6c5542165f3f982792 Mon Sep 17 00:00:00 2001 From: Martin Kagamino Lehoux Date: Sat, 23 Aug 2025 22:07:34 +0200 Subject: code upgrade --- CMakeLists.txt | 2 +- Makefile | 5 +++++ README.md | 6 ++++-- generate.fish | 9 ++++----- include/assert.h | 17 +++++++++-------- include/testing.h | 10 ++++++++++ include/types.h | 4 ++-- src/json.c | 9 +++++---- tests/test_json.c | 55 ++++++++++++++++++++++++++--------------------------- tests/test_string.c | 16 ++++++++-------- 10 files changed, 75 insertions(+), 58 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 94d59a8..06cbb74 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.30.5) project(kagamic VERSION 0.1 LANGUAGES C) -set(CMAKE_C_STANDARD 17) +set(CMAKE_C_STANDARD 23) file(GLOB SOURCES "src/*.c") file(GLOB TEST_SOURCES "test/*.c") diff --git a/Makefile b/Makefile index 30f1f1b..4c508f0 100644 --- a/Makefile +++ b/Makefile @@ -22,3 +22,8 @@ test: format: clang-format -i src/*.c include/*.h tests/*.c *.c + +check: + make test + clang-format -i --dry-run src/*.c include/*.h tests/*.c *.c --fail-on-incomplete-format + ! rg 't_assert\(.*==' diff --git a/README.md b/README.md index 9fa35fe..3e9b39b 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,13 @@ ## TODO - Vec_get not safe, I made a type mistake. And should be more efficient for loops -- Fix tests in release -- Static functions for private? - `valgrind --leak-check=full ./your_program` - web server, Json parsing, struct codegen - assert not in release +- python based generate +- test helpers to load json files +- add test with backspaces weird +- remove t_assert ## Benchmarks diff --git a/generate.fish b/generate.fish index 96f8542..fc84937 100644 --- a/generate.fish +++ b/generate.fish @@ -13,12 +13,11 @@ for f in $fs; for func in $funcs; set pfunc (echo $func | awk '{match($0, /test_[^(]+/); print substr($0, RSTART, RLENGTH)}'); echo "Found $pfunc in $f"; - echo " test($pfunc);" >> test.c + echo " test($pfunc);" >> test.c end; end; echo " - printf(\"Success: %d, Failures: %d\n\", success, failures); + printf(\"Success: %d, Failures: %d\n\", success, failures); - return failures; -} -" >> test.c; + return failures; +}" >> test.c; diff --git a/include/assert.h b/include/assert.h index ad9c2fd..aa62bd1 100644 --- a/include/assert.h +++ b/include/assert.h @@ -3,12 +3,13 @@ #include #include -#define assert(condition, ...) \ - do { \ - if (!(condition)) { \ - fprintf(stderr, "Assertion failed: %s:%d in %s(): ", __FILE__, __LINE__, __func__); \ - fprintf(stderr, __VA_ARGS__); \ - fprintf(stderr, "\n"); \ - abort(); \ - } \ +#define assert(condition, ...) \ + do { \ + if (!(condition)) { \ + fprintf(stderr, "Assertion failed: %s:%d in %s(): ", __FILE__, \ + __LINE__, __func__); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + abort(); \ + } \ } while (0) diff --git a/include/testing.h b/include/testing.h index 9e0c022..dc070d3 100644 --- a/include/testing.h +++ b/include/testing.h @@ -1,4 +1,6 @@ +#pragma once #include +#include #define t_assert(fact) \ if (!(fact)) { \ @@ -22,3 +24,11 @@ #a, b, #b, __FILE__, __LINE__, __func__); \ return 1; \ } + +#define t_assert_strequal(a, b) \ + if ((a.len != b.len) || (a.len && memcmp(a.data, b.data, a.len))) { \ + fprintf(stderr, \ + "Assertion failed: %.*s (%s) != %.*s (%s) at %s:%d in %s()\n", (i32)a.len, a.data, \ + #a, (i32)b.len, b.data, #b, __FILE__, __LINE__, __func__); \ + return 1; \ + } diff --git a/include/types.h b/include/types.h index da3a093..6df9510 100644 --- a/include/types.h +++ b/include/types.h @@ -1,7 +1,7 @@ +#include #include #include #include -#include typedef ptrdiff_t size; typedef char byte; @@ -9,7 +9,7 @@ typedef uintptr_t uptr; typedef int32_t i32; typedef uint64_t u64; typedef float f32; -typedef __m256i byte_32; +typedef __m256i bytex32; #define sizeof(x) (size)sizeof(x) #define countof(a) (sizeof(a) / sizeof(*(a))) diff --git a/src/json.c b/src/json.c index c299b63..f6dc4e6 100644 --- a/src/json.c +++ b/src/json.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -185,15 +186,15 @@ parse_result parse_string(byte *src, uptr start_pos, uptr max_pos) { result.pos = start_pos + 1; // SIMD optimization: process 32 bytes at a time using AVX2 - byte_32 quote_mask = _mm256_set1_epi8('"'); + bytex32 quote_mask = _mm256_set1_epi8('"'); for (; result.pos + 32 <= max_pos; result.pos += 32) { - byte_32 chunk = _mm256_loadu_si256((byte_32 *)(src + result.pos)); - byte_32 cmp = _mm256_cmpeq_epi8(chunk, quote_mask); + bytex32 chunk = _mm256_loadu_si256((bytex32 *)(src + result.pos)); + bytex32 cmp = _mm256_cmpeq_epi8(chunk, quote_mask); i32 mask = _mm256_movemask_epi8(cmp); if (mask != 0) { - result.pos += __builtin_ctz(mask); + result.pos += stdc_trailing_zeros_ui(mask); if (src[result.pos - 1] != '\\') break; }; } diff --git a/tests/test_json.c b/tests/test_json.c index 53dc6e3..b5ad9af 100644 --- a/tests/test_json.c +++ b/tests/test_json.c @@ -8,7 +8,7 @@ i32 test_json_integer() { JSON json_int = JSON_parse_raw(&perm, "123"); - t_assert(json_int.as.integer == 123); + t_assert_equal(json_int.as.integer, 123); t_assert_equal(Arena_get_used(perm), 0); return 0; } @@ -18,7 +18,7 @@ i32 test_json_int_array() { JSON json_int_array = JSON_parse_raw(&perm, "[1,2,3]"); - t_assert(json_int_array.type == JSON_ARRAY); + t_assert_equal(json_int_array.type, JSON_ARRAY); t_assert_equal(json_int_array.as.array.len, 3); int ints[3]; int sum = 0; @@ -27,10 +27,10 @@ i32 test_json_int_array() { ints[i] = item->as.integer; sum += item->as.integer; } - t_assert(ints[0] == 1); - t_assert(ints[1] == 2); - t_assert(ints[2] == 3); - t_assert(sum == 6); + t_assert_equal(ints[0], 1); + t_assert_equal(ints[1], 2); + t_assert_equal(ints[2], 3); + t_assert_equal(sum, 6); t_assert_equal(Arena_get_used(perm), 576); return 0; @@ -41,7 +41,7 @@ i32 test_json_empty_array() { JSON json_empty_array = JSON_parse_raw(&perm, "[]"); - t_assert(json_empty_array.as.array.len == 0); + t_assert_equal(json_empty_array.as.array.len, 0); t_assert_equal(Arena_get_used(perm), 0); // TODO: weird return 0; @@ -54,10 +54,10 @@ i32 test_json_nested_array() { JSON sub_array = *Vec_get(doc.as.array, JSON, 0); JSON val_1 = *Vec_get(sub_array.as.array, JSON, 0); - t_assert(val_1.as.integer == 1); + t_assert_equal(val_1.as.integer, 1); JSON val_2 = *Vec_get(sub_array.as.array, JSON, 1); - t_assert(Str_equals(StrView_copy(&perm, val_2.as.string), S("abc"))); - t_assert_equal(Arena_get_used(perm), 1155); + t_assert_strequal(val_2.as.string, S("abc")); + t_assert_equal(Arena_get_used(perm), 1152); return 0; } @@ -67,8 +67,8 @@ i32 test_json_string() { JSON js = JSON_parse_raw(&perm, "\"abc\""); - t_assert(Str_equals(StrView_copy(&perm, js.as.string), S("abc"))); - t_assert_equal(Arena_get_used(perm), 3); + t_assert_strequal(js.as.string, S("abc")); + t_assert_equal(Arena_get_used(perm), 0); return 0; } @@ -92,8 +92,8 @@ i32 test_json_object() { t_assert_equal(doc.as.object.len, 1); t_assert(Str_equals(*Vec_get(doc.as.object.keys, Str, 0), S("key"))); JSON value = *JSONObject_get(doc.as.object, S("key")); - t_assert(Str_equals(StrView_copy(&perm, value.as.string), S("value"))); - t_assert_equal(Arena_get_used(perm), 677); + t_assert_strequal(value.as.string, S("value")); + t_assert_equal(Arena_get_used(perm), 672); return 0; } @@ -104,9 +104,9 @@ i32 test_json_boolean() { JSON doc = JSON_parse_raw(&perm, "{\"a\": true, \"b\": false}"); JSON a = *JSONObject_get(doc.as.object, S("a")); - t_assert(a.as.boolean == true); + t_assert_equal(a.as.boolean, true); JSON b = *JSONObject_get(doc.as.object, S("b")); - t_assert(b.as.boolean == false); + t_assert_equal(b.as.boolean, false); t_assert_equal(Arena_get_used(perm), 672); return 0; @@ -132,7 +132,7 @@ i32 test_json_null() { JSON doc = JSON_parse_raw(&perm, "[null]"); t_assert_equal(doc.as.array.len, 1); JSON first = *Vec_get(doc.as.array, JSON, 0); - t_assert(first.type == JSON_NULL); + t_assert_equal(first.type, JSON_NULL); t_assert_equal(Arena_get_used(perm), 576); return 0; @@ -143,7 +143,7 @@ i32 test_json_long_spaces() { JSON doc = JSON_parse_raw( &perm, "{\t \t \t \t \t }"); - t_assert(doc.as.object.len == 0); + t_assert_equal(doc.as.object.len, 0); return 0; } @@ -158,10 +158,9 @@ i32 test_json_multiline() { " \"generator_version\": \"1.0\"\n" "}"); JSON target_size_mb = *JSONObject_get(doc.as.object, S("target_size_mb")); - t_assert(target_size_mb.as.integer == 1); + t_assert_equal(target_size_mb.as.integer, 1); JSON generated_at = *JSONObject_get(doc.as.object, S("generated_at")); - t_assert(Str_equals(StrView_copy(&perm, generated_at.as.string), - S("2025-07-22T10:36:30.974926"))); + t_assert_strequal(generated_at.as.string, S("2025-07-22T10:36:30.974926")); return 0; } @@ -172,9 +171,9 @@ i32 test_json_nested_object() { JSON doc = JSON_parse_raw(&perm, "{\"1\":{\"2\":{}}}"); JSON lvl_1 = *JSONObject_get(doc.as.object, S("1")); - t_assert(lvl_1.type == JSON_OBJECT); + t_assert_equal(lvl_1.type, JSON_OBJECT); JSON lvl_2 = *JSONObject_get(lvl_1.as.object, S("2")); - t_assert(lvl_2.type == JSON_OBJECT); + t_assert_equal(lvl_2.type, JSON_OBJECT); return 0; } @@ -183,7 +182,7 @@ i32 test_json_array_in_object() { JSON doc = JSON_parse_raw(&perm, "{\"tags\": [\"TWvZi0\",\"pM0TPv9X\"]}"); JSON tags = *JSONObject_get(doc.as.object, S("tags")); - t_assert(tags.type == JSON_ARRAY); + t_assert_equal(tags.type, JSON_ARRAY); t_assert_equal(tags.as.array.len, 2); return 0; @@ -218,11 +217,11 @@ i32 test_json_complex_multiline() { " }"); JSON id = *JSONObject_get(doc.as.object, S("id")); - t_assert(id.as.integer == 819228); + t_assert_equal(id.as.integer, 819228); JSON metadata = *JSONObject_get(doc.as.object, S("metadata")); JSON preferences = *JSONObject_get(metadata.as.object, S("preferences")); JSON theme = *JSONObject_get(preferences.as.object, S("theme")); - t_assert(Str_equals(StrView_copy(&perm, theme.as.string), S("auto"))); + t_assert_strequal(theme.as.string, S("auto")); return 0; } @@ -232,9 +231,9 @@ i32 test_negative_numbers() { JSON doc = JSON_parse_raw(&perm, "[-1, -2.5]"); JSON integer = *Vec_get(doc.as.array, JSON, 0); - t_assert(integer.as.integer == -1); + t_assert_equal(integer.as.integer, -1); JSON floating = *Vec_get(doc.as.array, JSON, 1); - t_assert(floating.as.floating == -2.5); + t_assert_fequal(floating.as.floating, -2.5); return 0; } diff --git a/tests/test_string.c b/tests/test_string.c index 8b5d0d6..297e294 100644 --- a/tests/test_string.c +++ b/tests/test_string.c @@ -4,7 +4,7 @@ #include i32 test_string_literal_equality() { - t_assert(Str_equals(S("Bonjour"), S("Bonjour"))); + t_assert_strequal(S("Bonjour"), S("Bonjour")); return 0; } @@ -14,28 +14,28 @@ i32 test_string_copy() { Str cpy = Str_copy(&perm, src.data, src.len); - t_assert(Str_equals(src, cpy)); + t_assert_strequal(src, cpy); t_assert_equal(Arena_get_used(perm), 5); return 0; } i32 test_fast_string() { - byte_32 quote_mask = _mm256_set1_epi8('"'); + bytex32 quote_mask = _mm256_set1_epi8('"'); byte *s = malloc(32 * sizeof(byte)); s = "abcdefghijklmnopqrstuvwxyzabcdef"; - byte_32 chunk = _mm256_loadu_si256((byte_32 *)s); - byte_32 cmp = _mm256_cmpeq_epi8(chunk, quote_mask); + bytex32 chunk = _mm256_loadu_si256((bytex32 *)s); + bytex32 cmp = _mm256_cmpeq_epi8(chunk, quote_mask); i32 mask = _mm256_movemask_epi8(cmp); - t_assert(_mm_popcnt_u32(mask) == 0); + t_assert_equal(_mm_popcnt_u32(mask), 0); s = "abcdefghijklm\"nopqrstuvwxyzabcde"; - chunk = _mm256_loadu_si256((byte_32 *)s); + chunk = _mm256_loadu_si256((bytex32 *)s); cmp = _mm256_cmpeq_epi8(chunk, quote_mask); mask = _mm256_movemask_epi8(cmp); - t_assert(_mm_popcnt_u32(mask) == 1); + t_assert_equal(_mm_popcnt_u32(mask), 1); return 0; } -- cgit v1.3