aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--.vscode/launch.json5
-rw-r--r--CMakeLists.txt6
-rw-r--r--Makefile27
-rw-r--r--README.md15
-rw-r--r--generate_json.py19
-rw-r--r--include/assert.h14
-rw-r--r--include/json.h2
-rw-r--r--include/types.h2
-rw-r--r--main.c22
-rw-r--r--src/json.c77
-rw-r--r--src/memory.c4
-rw-r--r--tests/test_json.c87
-rw-r--r--tests/test_string.c22
14 files changed, 196 insertions, 109 deletions
diff --git a/.gitignore b/.gitignore
index 81546af..c2a342a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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)
diff --git a/Makefile b/Makefile
index 9cbfa44..f4c5e8f 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/README.md b/README.md
index 5c28f84..d721753 100644
--- a/README.md
+++ b/README.md
@@ -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)))
diff --git a/main.c b/main.c
index 5f40b38..5b18022 100644
--- a/main.c
+++ b/main.c
@@ -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");
diff --git a/src/json.c b/src/json.c
index af93d06..99ae01b 100644
--- a/src/json.c
+++ b/src/json.c
@@ -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;
+}