diff options
| author | Martin Kagamino Lehoux <martin@lehoux.net> | 2025-08-23 15:16:04 +0200 |
|---|---|---|
| committer | Martin Kagamino Lehoux <martin@lehoux.net> | 2025-08-27 11:44:07 +0200 |
| commit | 03b645f22bfc7afadc5a8aa6d59edf0eba74a048 (patch) | |
| tree | dda64286a408e104879be2544f9c7dc8aa8e333a | |
| parent | 6836db9b00d4f13a78a53cc5bdb0b63028188b55 (diff) | |
cleanup & json string simd parsing
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | .vscode/launch.json | 5 | ||||
| -rw-r--r-- | CMakeLists.txt | 6 | ||||
| -rw-r--r-- | Makefile | 27 | ||||
| -rw-r--r-- | README.md | 15 | ||||
| -rw-r--r-- | generate_json.py | 19 | ||||
| -rw-r--r-- | include/assert.h | 14 | ||||
| -rw-r--r-- | include/json.h | 2 | ||||
| -rw-r--r-- | include/types.h | 2 | ||||
| -rw-r--r-- | main.c | 22 | ||||
| -rw-r--r-- | src/json.c | 77 | ||||
| -rw-r--r-- | src/memory.c | 4 | ||||
| -rw-r--r-- | tests/test_json.c | 87 | ||||
| -rw-r--r-- | tests/test_string.c | 22 |
14 files changed, 196 insertions, 109 deletions
@@ -2,7 +2,8 @@ main Debug/ Release/ +Profile/ test.c -out.json +*.json callgrind.out diff --git a/.vscode/launch.json b/.vscode/launch.json index 3b84b2e..7a44b33 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,10 @@ "request": "launch", "name": "Debug", "program": "${workspaceFolder}/Debug/main", - "args": [], + "args": [ + "1MB.json", + "1" + ], "cwd": "${workspaceFolder}" } ] diff --git a/CMakeLists.txt b/CMakeLists.txt index a5fd18e..1f2f8ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,10 +5,12 @@ 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_executable(main main.c ${SOURCES}) target_link_libraries(main m) -target_compile_options(main PRIVATE -Wall -pedantic -Wextra) +target_compile_options(main PRIVATE) add_executable(test test.c ${SOURCES} ${TEST_SOURCES}) target_link_libraries(test m) -target_compile_options(test PRIVATE -Wall -pedantic -Wextra) +target_compile_options(test PRIVATE) @@ -1,23 +1,24 @@ debug: - mkdir -p Debug; \ - cd Debug; \ - cmake -DCMAKE_BUILD_TYPE=Debug ..; \ - make; + mkdir -p Debug + cmake -DCMAKE_BUILD_TYPE=Debug -B Debug + cmake --build Debug release: - mkdir -p Release; \ - cd Release; \ - cmake -DCMAKE_BUILD_TYPE=Release ..; \ - make; + mkdir -p Release + cmake -DCMAKE_BUILD_TYPE=Release -B Release + cmake --build Release -profile: release - valgrind --tool=callgrind --callgrind-out-file=callgrind.out ./Release/main +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 kcachegrind callgrind.out test: - fish generate.fish; - make debug; - ./Debug/test; + fish generate.fish + make debug + ./Debug/test format: clang-format -i src/*.c include/*.h tests/*.c *.c @@ -6,9 +6,9 @@ - Fix tests in release - Static functions for private? - `valgrind --leak-check=full ./your_program` -- assert with error msg? - negative numbers - web server, Json parsing, struct codegen +- what is a const char * ## Benchmarks @@ -19,6 +19,19 @@ |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| +```c +void print_bytes(byte_32 *data) { + for (i32 i = 0; i < 32; i++) { + byte c = ((byte *)data)[i]; + if (c >= 32 && c <= 126) { + printf("%c", c); + } else { + printf("."); + } + } +} +``` + ## Resources - https://nullprogram.com/blog/2023/10/08/ diff --git a/generate_json.py b/generate_json.py index 28c7011..b3e285f 100644 --- a/generate_json.py +++ b/generate_json.py @@ -174,19 +174,14 @@ def generate_large_json(target_size_mb=100, output_file="benchmark.json"): print(f" Server metrics: {len(data['analytics']['server_metrics']):,}") def main(): - if len(sys.argv) > 1: - try: - target_size = int(sys.argv[1]) - except ValueError: - print("Usage: python3 generate_json.py [size_in_mb] [output_file]") - sys.exit(1) - else: - target_size = 100 + assert len(sys.argv) == 2 + try: + target_size = int(sys.argv[1]) + except ValueError: + print("Usage: python3 generate_json.py [size_in_mb] [output_file]") + sys.exit(1) - if len(sys.argv) > 2: - output_file = sys.argv[2] - else: - output_file = f"benchmark_{target_size}mb.json" + output_file = f"{target_size}MB.json" try: generate_large_json(target_size, output_file) diff --git a/include/assert.h b/include/assert.h new file mode 100644 index 0000000..ad9c2fd --- /dev/null +++ b/include/assert.h @@ -0,0 +1,14 @@ +#pragma once + +#include <stdio.h> +#include <stdlib.h> + +#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/json.h b/include/json.h index 9bd668e..52566ab 100644 --- a/include/json.h +++ b/include/json.h @@ -30,5 +30,5 @@ typedef struct { } JSON; JSON *JSONObject_get(JSONObject obj, Str key); i32 JSON_fprint(FILE *w, JSON json); -JSON JSON_parse(Arena *a, byte *src); +JSON JSON_parse(Arena *a, byte *src, uptr len); JSON JSON_Int(Arena *a, i32 val); diff --git a/include/types.h b/include/types.h index bacedf8..da3a093 100644 --- a/include/types.h +++ b/include/types.h @@ -1,6 +1,7 @@ #include <stdbool.h> #include <stddef.h> #include <stdint.h> +#include <immintrin.h> typedef ptrdiff_t size; typedef char byte; @@ -8,6 +9,7 @@ typedef uintptr_t uptr; typedef int32_t i32; typedef uint64_t u64; typedef float f32; +typedef __m256i byte_32; #define sizeof(x) (size)sizeof(x) #define countof(a) (sizeof(a) / sizeof(*(a))) @@ -1,10 +1,10 @@ -#include <assert.h> #include <math.h> #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <time.h> +#include "include/assert.h" #include "include/json.h" #include "include/memory.h" #include "include/types.h" @@ -72,7 +72,7 @@ void run_benchmark(const char *filename, i32 iterations) { Arena_reset(&arena); size used = Arena_get_used(arena); double start_time = get_time_ns(); - JSON result = JSON_parse(&arena, content); + JSON result = JSON_parse(&arena, content, file_size); double end_time = get_time_ns(); timings[i] = end_time - start_time; @@ -107,19 +107,15 @@ void run_benchmark(const char *filename, i32 iterations) { } i32 main(i32 argc, char *argv[]) { - const char *filename = "out.json"; - i32 iterations = 1; + const char *filename = ""; // Parse command line arguments - if (argc >= 2) { - filename = argv[1]; - } - if (argc >= 3) { - iterations = atoi(argv[2]); - if (iterations <= 0) { - fprintf(stderr, "Error: Invalid number of iterations\n"); - return 1; - } + assert(argc == 3, "main <file.json> <iterations>"); + filename = argv[1]; + i32 iterations = atoi(argv[2]); + if (iterations <= 0) { + fprintf(stderr, "Error: Invalid number of iterations\n"); + return 1; } printf("JSON Parser Benchmark\n"); @@ -1,10 +1,11 @@ -#include <assert.h> #include <ctype.h> +#include <immintrin.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> #include <string.h> +#include "../include/assert.h" #include "../include/json.h" #include "../include/str.h" #include "../include/types.h" @@ -46,7 +47,7 @@ JSON JSON_string(StrView value) { } JSON JSON_object(size len, Vec keys, Vec values) { - assert(len == keys.len && len == values.len); + assert(len == keys.len && len == values.len, "length mismatch"); JSON json = {0}; json.type = JSON_OBJECT; json.as.object.len = len; @@ -105,7 +106,7 @@ i32 JSON_fprint(FILE *w, JSON json) { fprintf(w, "}"); return 0; } - assert(false); + assert(false, "unknown json type %d", json.type); } typedef struct { @@ -113,7 +114,10 @@ typedef struct { uptr pos; } parse_result; -parse_result parse_any(Arena *a, byte *src, uptr pos); +#define assert_parse(condition, msg, src, pos) \ + assert(condition, "%s (%c at %lu)", msg, src[pos], pos) + +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}; @@ -147,8 +151,8 @@ uptr absorb_whitespaces(byte *src, uptr start_pos) { return pos; } -parse_result parse_array(Arena *a, byte *src, uptr start_pos) { - assert(src[start_pos] == '['); +parse_result parse_array(Arena *a, byte *src, uptr start_pos, uptr max_pos) { + assert_parse(src[start_pos] == '[', "invalid array start", src, start_pos); parse_result result = {0}; result.pos = start_pos + 1; if (src[result.pos] == ']') { @@ -158,7 +162,7 @@ parse_result parse_array(Arena *a, byte *src, uptr start_pos) { Vec items = Vec_new(a, JSON, 2); for (;;) { result.pos = absorb_whitespaces(src, result.pos); - parse_result item_result = parse_any(a, src, result.pos); + parse_result item_result = parse_any(a, src, result.pos, max_pos); Vec_push(a, &items, &item_result.value); result.pos = absorb_whitespaces(src, item_result.pos); if (src[result.pos] == ',') { @@ -167,21 +171,39 @@ parse_result parse_array(Arena *a, byte *src, uptr start_pos) { break; } }; - assert(src[result.pos] == ']'); + assert_parse(src[result.pos] == ']', "invalid array end", src, result.pos); result.pos++; result.value = JSON_array(items); return result; } -parse_result parse_string(Arena *a, byte *src, uptr start_pos) { - assert(src[start_pos] == '"'); +parse_result parse_string(Arena *a, 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; + // SIMD optimization: process 32 bytes at a time using AVX2 + byte_32 quote_mask = _mm256_set1_epi8('"'); + + while (1) { + if (max_pos - result.pos < 32) break; + byte_32 chunk = _mm256_loadu_si256((byte_32 *)(src + result.pos)); + byte_32 cmp = _mm256_cmpeq_epi8(chunk, quote_mask); + i32 mask = _mm256_movemask_epi8(cmp); + + if (_mm_popcnt_u32(mask) > 0) { + result.pos += __builtin_ctz(mask); + break; + }; + result.pos += 32; + } + + // Fall back to original byte-by-byte processing for the remaining bytes while (src[result.pos] != '"' && src[result.pos - 1] != '\\') { result.pos++; } - assert(src[result.pos] == '"'); + + assert_parse(src[result.pos] == '"', "invalid string end", src, result.pos); result.value = JSON_string(Str_view(&src[start_pos + 1], result.pos - start_pos - 1)); result.pos++; @@ -189,8 +211,8 @@ parse_result parse_string(Arena *a, byte *src, uptr start_pos) { return result; } -parse_result parse_object(Arena *a, byte *src, uptr start_pos) { - assert(src[start_pos] == '{'); +parse_result parse_object(Arena *a, byte *src, uptr start_pos, uptr max_pos) { + assert_parse(src[start_pos] == '{', "invalid object start", src, start_pos); parse_result result = {0}; result.pos = absorb_whitespaces(src, start_pos + 1); size len = 0; @@ -202,14 +224,15 @@ parse_result parse_object(Arena *a, byte *src, uptr start_pos) { return result; } for (;;) { - assert(src[result.pos] == '"'); - parse_result key_result = parse_string(a, src, result.pos); + parse_result key_result = parse_string(a, src, result.pos, max_pos); result.pos = absorb_whitespaces(src, key_result.pos); - assert(src[result.pos] == ':'); + assert_parse(src[result.pos] == ':', "invalid keyval delim", src, + result.pos); result.pos = absorb_whitespaces(src, result.pos + 1); - parse_result value_result = parse_any(a, src, result.pos); + parse_result value_result = parse_any(a, src, result.pos, max_pos); len++; - assert(key_result.value.type == JSON_STRING); + assert(key_result.value.type == JSON_STRING, + "invalid json type for key: %d", key_result.value.type); Vec_push(a, &keys, &key_result.value.as.string); Vec_push(a, &values, &value_result.value); result.pos = absorb_whitespaces(src, value_result.pos); @@ -218,18 +241,18 @@ parse_result parse_object(Arena *a, byte *src, uptr start_pos) { } result.pos = absorb_whitespaces(src, result.pos + 1); } - assert(src[result.pos] == '}'); + assert_parse(src[result.pos] == '}', "invalid object end", src, result.pos); result.pos++; result.value = JSON_object(len, keys, values); return result; } -parse_result parse_any(Arena *a, byte *src, uptr 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 (src[pos] == '[') return parse_array(a, src, pos); - if (src[pos] == '"') return parse_string(a, src, pos); - if (src[pos] == '{') return parse_object(a, 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_object(a, src, pos, max_pos); if (memcmp(src + pos, "true", 4) == 0) { parse_result result = {0}; result.value = JSON_boolean(true); @@ -248,10 +271,12 @@ parse_result parse_any(Arena *a, byte *src, uptr pos) { result.pos = pos + 4; return result; } - assert(false); + assert_parse(false, "unexpected input", src, pos); } -JSON JSON_parse(Arena *a, byte *src) { return parse_any(a, src, 0).value; } +JSON JSON_parse(Arena *a, byte *src, uptr len) { + return parse_any(a, src, 0, len).value; +} JSON *JSONObject_get(JSONObject obj, Str key) { for (size i = 0; i < obj.len; i++) { @@ -259,5 +284,5 @@ JSON *JSONObject_get(JSONObject obj, Str key) { return Vec_get(obj.values, JSON, i); } } - assert(false); + return 0; } diff --git a/src/memory.c b/src/memory.c index 62f47e7..7072c61 100644 --- a/src/memory.c +++ b/src/memory.c @@ -1,8 +1,8 @@ -#include <assert.h> #include <stdint.h> #include <stdlib.h> #include <string.h> +#include "../include/assert.h" #include "../include/memory.h" #include "../include/types.h" @@ -20,7 +20,7 @@ void *alloc(Arena *a, size count, size tsize, size align) { size padding = -(uptr)a->beg & (align - 1); byte *beg = a->beg + padding; size alloc_size = count * tsize; - assert(beg + alloc_size < a->end); + assert(beg + alloc_size < a->end, "out of memory"); a->beg = beg + alloc_size; return beg; } diff --git a/tests/test_json.c b/tests/test_json.c index d8fa69c..e050c72 100644 --- a/tests/test_json.c +++ b/tests/test_json.c @@ -1,10 +1,12 @@ #include "../include/json.h" #include "../include/testing.h" +#define JSON_parse_raw(a, s) JSON_parse(a, s, lengthof(s)); + i32 test_json_integer() { Arena perm = Arena_new(128e3); - JSON json_int = JSON_parse(&perm, "123"); + JSON json_int = JSON_parse_raw(&perm, "123"); t_assert(json_int.as.integer == 123); t_assert_equal(Arena_get_used(perm), 0); @@ -14,7 +16,7 @@ i32 test_json_integer() { i32 test_json_int_array() { Arena perm = Arena_new(128e3); - JSON json_int_array = JSON_parse(&perm, "[1,2,3]"); + 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.as.array.len, 3); @@ -37,7 +39,7 @@ i32 test_json_int_array() { i32 test_json_empty_array() { Arena perm = Arena_new(128e3); - JSON json_empty_array = JSON_parse(&perm, "[]"); + JSON json_empty_array = JSON_parse_raw(&perm, "[]"); t_assert(json_empty_array.as.array.len == 0); t_assert_equal(Arena_get_used(perm), 0); // TODO: weird @@ -48,7 +50,7 @@ i32 test_json_empty_array() { i32 test_json_nested_array() { Arena perm = Arena_new(128e3); - JSON doc = JSON_parse(&perm, "[[1,\"abc\"]]"); + JSON doc = JSON_parse_raw(&perm, "[[1,\"abc\"]]"); JSON sub_array = *Vec_get(doc.as.array, JSON, 0); JSON val_1 = *Vec_get(sub_array.as.array, JSON, 0); @@ -63,7 +65,7 @@ i32 test_json_nested_array() { i32 test_json_string() { Arena perm = Arena_new(128e3); - JSON js = JSON_parse(&perm, "\"abc\""); + 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); @@ -74,7 +76,7 @@ i32 test_json_string() { i32 test_json_empty_object() { Arena perm = Arena_new(128e3); - JSON doc = JSON_parse(&perm, "{}"); + JSON doc = JSON_parse_raw(&perm, "{}"); t_assert_equal(doc.as.object.len, 0); t_assert_equal(Arena_get_used(perm), 224); @@ -85,7 +87,7 @@ i32 test_json_empty_object() { i32 test_json_object() { Arena perm = Arena_new(128e3); - JSON doc = JSON_parse(&perm, "{\"key\": \"value\"}"); + JSON doc = JSON_parse_raw(&perm, "{\"key\": \"value\"}"); t_assert_equal(doc.as.object.len, 1); t_assert(Str_equals(*Vec_get(doc.as.object.keys, Str, 0), S("key"))); @@ -99,7 +101,7 @@ i32 test_json_object() { i32 test_json_boolean() { Arena perm = Arena_new(128e3); - JSON doc = JSON_parse(&perm, "{\"a\": true, \"b\": false}"); + 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); @@ -113,7 +115,7 @@ i32 test_json_boolean() { i32 test_json_float() { Arena perm = Arena_new(128e3); - JSON doc = JSON_parse(&perm, "[1.2, 2.3]"); + JSON doc = JSON_parse_raw(&perm, "[1.2, 2.3]"); t_assert_equal(doc.as.array.len, 2); JSON first = *Vec_get(doc.as.array, JSON, 0); t_assert_fequal(first.as.floating, 1.2); @@ -127,7 +129,7 @@ i32 test_json_float() { i32 test_json_null() { Arena perm = Arena_new(128e3); - JSON doc = JSON_parse(&perm, "[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); @@ -139,7 +141,7 @@ i32 test_json_null() { i32 test_json_multiline() { Arena perm = Arena_new(128e3); - JSON doc = JSON_parse( + JSON doc = JSON_parse_raw( &perm, "{\n" " \"generated_at\": \"2025-07-22T10:36:30.974926\",\n" " \"target_size_mb\": 1,\n" @@ -157,7 +159,7 @@ i32 test_json_multiline() { i32 test_json_nested_object() { Arena perm = Arena_new(128e3); - JSON doc = JSON_parse(&perm, "{\"1\":{\"2\":{}}}"); + 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); @@ -166,33 +168,44 @@ i32 test_json_nested_object() { return 0; } +i32 test_json_array_in_object() { + Arena perm = Arena_new(128e3); + 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.as.array.len, 2); + + return 0; +} + i32 test_json_complex_multiline() { Arena perm = Arena_new(128e3); - JSON doc = - JSON_parse(&perm, "{\n" - " \"id\": 819228,\n" - " \"username\": \"rCrRv4PRnUNuAy\",\n" - " \"email\": \"xzbwrzj@zwod.com\",\n" - " \"first_name\": \"4WH7g7PsOEm5\",\n" - " \"last_name\": \"6ulndxUgjbIayl\",\n" - " \"age\": 30,\n" - " \"phone\": \"+1-529-712-3931\",\n" - " \"is_active\": false,\n" - " \"balance\": 9347.49,\n" - " \"tags\": [\n" - " \"TWvZi0\",\n" - " \"pM0TPv9X\"\n" - " ],\n" - " \"metadata\": {\n" - " \"last_login\": \"2024-06-13T21:22:32Z\",\n" - " \"login_count\": 245,\n" - " \"preferences\": {\n" - " \"theme\": \"auto\",\n" - " \"notifications\": true,\n" - " \"language\": \"en\"\n" - " }\n" - " }\n" - " }"); + JSON doc = JSON_parse_raw( + &perm, "{\n" + " \"id\": 819228,\n" + " \"username\": \"rCrRv4PRnUNuAy\",\n" + " \"email\": \"xzbwrzj@zwod.com\",\n" + " \"first_name\": \"4WH7g7PsOEm5\",\n" + " \"last_name\": \"6ulndxUgjbIayl\",\n" + " \"age\": 30,\n" + " \"phone\": \"+1-529-712-3931\",\n" + " \"is_active\": false,\n" + " \"balance\": 9347.49,\n" + " \"tags\": [\n" + " \"TWvZi0\",\n" + " \"pM0TPv9X\"\n" + " ],\n" + " \"metadata\": {\n" + " \"last_login\": \"2024-06-13T21:22:32Z\",\n" + " \"login_count\": 245,\n" + " \"preferences\": {\n" + " \"theme\": \"auto\",\n" + " \"notifications\": true,\n" + " \"language\": \"en\"\n" + " }\n" + " }\n" + " }"); JSON id = *JSONObject_get(doc.as.object, S("id")); t_assert(id.as.integer == 819228); diff --git a/tests/test_string.c b/tests/test_string.c index 1baab6a..8b5d0d6 100644 --- a/tests/test_string.c +++ b/tests/test_string.c @@ -1,5 +1,7 @@ #include "../include/string.h" #include "../include/testing.h" +#include "../include/types.h" +#include <stdio.h> i32 test_string_literal_equality() { t_assert(Str_equals(S("Bonjour"), S("Bonjour"))); @@ -17,3 +19,23 @@ i32 test_string_copy() { return 0; } + +i32 test_fast_string() { + byte_32 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); + i32 mask = _mm256_movemask_epi8(cmp); + t_assert(_mm_popcnt_u32(mask) == 0); + + s = "abcdefghijklm\"nopqrstuvwxyzabcde"; + + chunk = _mm256_loadu_si256((byte_32 *)s); + cmp = _mm256_cmpeq_epi8(chunk, quote_mask); + mask = _mm256_movemask_epi8(cmp); + t_assert(_mm_popcnt_u32(mask) == 1); + + return 0; +} |
