From cf534d762754860ed5b714974f99acd282d48b3b Mon Sep 17 00:00:00 2001 From: Martin Kagamino Lehoux Date: Wed, 8 Apr 2026 13:14:27 +0200 Subject: Restructure into src/{std,json}/ Co-Authored-By: Claude Sonnet 4.6 --- src/json.c | 282 --------------------- src/json/json.c | 282 +++++++++++++++++++++ src/json/json.h | 34 +++ src/json/test_json.c | 224 ++++++++++++++++ src/json/test_json/test_json_backspaces.json | 1 + .../test_json/test_json_complex_multiline.json | 24 ++ src/json/test_json/test_json_multiline.json | 5 + src/log.c | 12 - src/memory.c | 33 --- src/std/assert.h | 15 ++ src/std/log.c | 12 + src/std/log.h | 10 + src/std/memory.c | 33 +++ src/std/memory.h | 16 ++ src/std/str.c | 32 +++ src/std/str.h | 30 +++ src/std/test_string.c | 41 +++ src/std/test_time.c | 13 + src/std/test_vec.c | 88 +++++++ src/std/testing.c | 28 ++ src/std/testing.h | 36 +++ src/std/time.c | 48 ++++ src/std/time.h | 20 ++ src/std/types.h | 16 ++ src/std/vec.c | 27 ++ src/std/vec.h | 18 ++ src/str.c | 32 --- src/testing.c | 28 -- src/time.c | 48 ---- src/vec.c | 27 -- 30 files changed, 1053 insertions(+), 462 deletions(-) delete mode 100644 src/json.c create mode 100644 src/json/json.c create mode 100644 src/json/json.h create mode 100644 src/json/test_json.c create mode 100644 src/json/test_json/test_json_backspaces.json create mode 100644 src/json/test_json/test_json_complex_multiline.json create mode 100644 src/json/test_json/test_json_multiline.json delete mode 100644 src/log.c delete mode 100644 src/memory.c create mode 100644 src/std/assert.h create mode 100644 src/std/log.c create mode 100644 src/std/log.h create mode 100644 src/std/memory.c create mode 100644 src/std/memory.h create mode 100644 src/std/str.c create mode 100644 src/std/str.h create mode 100644 src/std/test_string.c create mode 100644 src/std/test_time.c create mode 100644 src/std/test_vec.c create mode 100644 src/std/testing.c create mode 100644 src/std/testing.h create mode 100644 src/std/time.c create mode 100644 src/std/time.h create mode 100644 src/std/types.h create mode 100644 src/std/vec.c create mode 100644 src/std/vec.h delete mode 100644 src/str.c delete mode 100644 src/testing.c delete mode 100644 src/time.c delete mode 100644 src/vec.c (limited to 'src') diff --git a/src/json.c b/src/json.c deleted file mode 100644 index 67bbf2e..0000000 --- a/src/json.c +++ /dev/null @@ -1,282 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../include/assert.h" -#include "../include/json.h" -#include "../include/str.h" -#include "../include/types.h" - -JSONObject *JSONObject_new(Arena *a) { - JSONObject *object = new(a, JSONObject, 1); - object->len = 0; - object->keys = Vec_new(a, StrView, 2); - object->values = Vec_new(a, JSON, 2); - return object; -} - -JSON JSON_integer(i32 value) { - JSON json = {0}; - json.type = JSON_INTEGER; - json.as.integer = value; - return json; -} - -JSON JSON_float(f32 value) { - JSON json = {0}; - json.type = JSON_FLOAT; - json.as.floating = value; - return json; -} - -JSON JSON_array(Vec items) { - JSON json = {0}; - json.type = JSON_ARRAY; - json.as.array = items; - return json; -} - -JSON JSON_string(StrView value) { - JSON json = {0}; - json.type = JSON_STRING; - json.as.string = value; - return json; -} - -JSON JSON_object(size len, Vec keys, Vec values) { - assert(len == keys.len && len == values.len, "length mismatch"); - JSON json = {0}; - json.type = JSON_OBJECT; - json.as.object.len = len; - json.as.object.keys = keys; - json.as.object.values = values; - return json; -} - -JSON JSON_boolean(bool value) { - JSON json = {0}; - json.type = JSON_BOOL; - json.as.boolean = value; - return json; -} - -JSON JSON_null() { - JSON json = {0}; - json.type = JSON_NULL; - return json; -} - -i32 JSON_fprint(FILE *w, JSON json) { - switch (json.type) { - case JSON_STRING: - fprintf(w, "\""); - Str_fprint(w, json.as.string); - return fprintf(w, "\""); - case JSON_INTEGER: - return fprintf(w, "%d", json.as.integer); - case JSON_FLOAT: - return fprintf(w, "%f", json.as.floating); - case JSON_NULL: - return fprintf(w, "null"); - case JSON_BOOL: - return fprintf(w, "%s", json.as.boolean ? "true" : "false"); - case JSON_ARRAY: - fprintf(w, "["); - for (size i = 0; i < json.as.array.len; i++) { - JSON value = *Vec_get(json.as.array, JSON, i); - JSON_fprint(w, value); - if (i < json.as.array.len - 1) fprintf(w, ", "); - } - fprintf(w, "]"); - return 0; - case JSON_OBJECT: - fprintf(w, "{"); - for (size i = 0; i < json.as.object.len; i++) { - StrView key = *Vec_get(json.as.object.keys, StrView, i); - fprintf(w, "\""); - Str_fprint(w, key); - fprintf(w, "\": "); - JSON value = *Vec_get(json.as.object.values, JSON, i); - JSON_fprint(w, value); - if (i < json.as.object.len - 1) fprintf(w, ", "); - } - fprintf(w, "}"); - return 0; - } - assert(false, "unknown json type %d", json.type); -} - -typedef struct { - JSON value; - uptr pos; -} parse_result; - -#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}; - byte *end; - f32 number = strtod(src + start_pos, &end); - assert_parse(end != src, "invalide number", src, result.pos); - result.pos += end - src; - result.value = - floor(number) == number ? JSON_integer(number) : JSON_float(number); - return result; -} - -uptr absorb_whitespaces(byte *src, uptr start_pos, uptr max_pos) { - uptr pos = start_pos; - while (pos < max_pos && isspace(src[pos])) { - pos++; - } - return 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] == ']') { - result.pos++; - return result; - } - Vec items = Vec_new(a, JSON, 6); - for (;;) { - result.pos = absorb_whitespaces(src, result.pos, max_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, max_pos); - if (src[result.pos] == ',') { - result.pos++; - } else { - break; - } - }; - assert_parse(src[result.pos] == ']', "invalid array end", src, result.pos); - result.pos++; - result.value = JSON_array(items); - return result; -} - -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; - - // SIMD optimization: process 32 bytes at a time using AVX2 - bytex32 quote_bytes = _mm256_set1_epi8('"'); - - for (; result.pos + 32 <= max_pos; result.pos += 32) { - bytex32 chunk = _mm256_loadu_si256((bytex32 *)(src + result.pos)); - i32 quote_mask = - _mm256_movemask_epi8(_mm256_cmpeq_epi8(chunk, quote_bytes)); - - if (quote_mask != 0) { - result.pos += stdc_trailing_zeros_ui(quote_mask); - break; - }; - } - - // Fall back to original byte-by-byte processing for the remaining bytes - for (; result.pos < max_pos; result.pos++) { - if (src[result.pos] == '"') { - uptr backslash_count = 0; - while (backslash_count < result.pos && - src[result.pos - backslash_count - 1] == '\\') { - backslash_count++; - } - if (!(backslash_count & 1)) break; - } - } - - 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++; - - return result; -} - -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, max_pos); - size len = 0; - Vec keys = Vec_new(a, StrView, 6); - Vec values = Vec_new(a, JSON, 6); - if (src[result.pos] == '}') { - result.pos++; - result.value = JSON_object(len, keys, values); - return result; - } - for (;;) { - parse_result key_result = parse_string(src, result.pos, max_pos); - result.pos = absorb_whitespaces(src, key_result.pos, max_pos); - assert_parse(src[result.pos] == ':', "invalid keyval delim", src, - result.pos); - result.pos = absorb_whitespaces(src, result.pos + 1, max_pos); - parse_result value_result = parse_any(a, src, result.pos, max_pos); - len++; - 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, max_pos); - if (src[result.pos] != ',') { - break; - } - result.pos = absorb_whitespaces(src, result.pos + 1, max_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, uptr max_pos) { - pos = absorb_whitespaces(src, pos, max_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(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); - result.pos = pos + 4; - return result; - } - if (memcmp(src + pos, "false", 5) == 0) { - parse_result result = {0}; - result.value = JSON_boolean(false); - result.pos = pos + 5; - return result; - } - if (memcmp(src + pos, "null", 4) == 0) { - parse_result result = {0}; - result.value = JSON_null(); - result.pos = pos + 4; - return result; - } - assert_parse(false, "unexpected input", src, pos); -} - -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++) { - StrView tmp_key = *Vec_get(obj.keys, StrView, i); - if (Str_equals(tmp_key, key)) return Vec_get(obj.values, JSON, i); - } - return 0; -} diff --git a/src/json/json.c b/src/json/json.c new file mode 100644 index 0000000..d16415b --- /dev/null +++ b/src/json/json.c @@ -0,0 +1,282 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../std/assert.h" +#include "json.h" +#include "../std/str.h" +#include "../std/types.h" + +JSONObject *JSONObject_new(Arena *a) { + JSONObject *object = new(a, JSONObject, 1); + object->len = 0; + object->keys = Vec_new(a, StrView, 2); + object->values = Vec_new(a, JSON, 2); + return object; +} + +JSON JSON_integer(i32 value) { + JSON json = {0}; + json.type = JSON_INTEGER; + json.as.integer = value; + return json; +} + +JSON JSON_float(f32 value) { + JSON json = {0}; + json.type = JSON_FLOAT; + json.as.floating = value; + return json; +} + +JSON JSON_array(Vec items) { + JSON json = {0}; + json.type = JSON_ARRAY; + json.as.array = items; + return json; +} + +JSON JSON_string(StrView value) { + JSON json = {0}; + json.type = JSON_STRING; + json.as.string = value; + return json; +} + +JSON JSON_object(size len, Vec keys, Vec values) { + assert(len == keys.len && len == values.len, "length mismatch"); + JSON json = {0}; + json.type = JSON_OBJECT; + json.as.object.len = len; + json.as.object.keys = keys; + json.as.object.values = values; + return json; +} + +JSON JSON_boolean(bool value) { + JSON json = {0}; + json.type = JSON_BOOL; + json.as.boolean = value; + return json; +} + +JSON JSON_null() { + JSON json = {0}; + json.type = JSON_NULL; + return json; +} + +i32 JSON_fprint(FILE *w, JSON json) { + switch (json.type) { + case JSON_STRING: + fprintf(w, "\""); + Str_fprint(w, json.as.string); + return fprintf(w, "\""); + case JSON_INTEGER: + return fprintf(w, "%d", json.as.integer); + case JSON_FLOAT: + return fprintf(w, "%f", json.as.floating); + case JSON_NULL: + return fprintf(w, "null"); + case JSON_BOOL: + return fprintf(w, "%s", json.as.boolean ? "true" : "false"); + case JSON_ARRAY: + fprintf(w, "["); + for (size i = 0; i < json.as.array.len; i++) { + JSON value = *Vec_get(json.as.array, JSON, i); + JSON_fprint(w, value); + if (i < json.as.array.len - 1) fprintf(w, ", "); + } + fprintf(w, "]"); + return 0; + case JSON_OBJECT: + fprintf(w, "{"); + for (size i = 0; i < json.as.object.len; i++) { + StrView key = *Vec_get(json.as.object.keys, StrView, i); + fprintf(w, "\""); + Str_fprint(w, key); + fprintf(w, "\": "); + JSON value = *Vec_get(json.as.object.values, JSON, i); + JSON_fprint(w, value); + if (i < json.as.object.len - 1) fprintf(w, ", "); + } + fprintf(w, "}"); + return 0; + } + assert(false, "unknown json type %d", json.type); +} + +typedef struct { + JSON value; + uptr pos; +} parse_result; + +#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}; + byte *end; + f32 number = strtod(src + start_pos, &end); + assert_parse(end != src, "invalide number", src, result.pos); + result.pos += end - src; + result.value = + floor(number) == number ? JSON_integer(number) : JSON_float(number); + return result; +} + +uptr absorb_whitespaces(byte *src, uptr start_pos, uptr max_pos) { + uptr pos = start_pos; + while (pos < max_pos && isspace(src[pos])) { + pos++; + } + return 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] == ']') { + result.pos++; + return result; + } + Vec items = Vec_new(a, JSON, 6); + for (;;) { + result.pos = absorb_whitespaces(src, result.pos, max_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, max_pos); + if (src[result.pos] == ',') { + result.pos++; + } else { + break; + } + }; + assert_parse(src[result.pos] == ']', "invalid array end", src, result.pos); + result.pos++; + result.value = JSON_array(items); + return result; +} + +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; + + // SIMD optimization: process 32 bytes at a time using AVX2 + bytex32 quote_bytes = _mm256_set1_epi8('"'); + + for (; result.pos + 32 <= max_pos; result.pos += 32) { + bytex32 chunk = _mm256_loadu_si256((bytex32 *)(src + result.pos)); + i32 quote_mask = + _mm256_movemask_epi8(_mm256_cmpeq_epi8(chunk, quote_bytes)); + + if (quote_mask != 0) { + result.pos += stdc_trailing_zeros_ui(quote_mask); + break; + }; + } + + // Fall back to original byte-by-byte processing for the remaining bytes + for (; result.pos < max_pos; result.pos++) { + if (src[result.pos] == '"') { + uptr backslash_count = 0; + while (backslash_count < result.pos && + src[result.pos - backslash_count - 1] == '\\') { + backslash_count++; + } + if (!(backslash_count & 1)) break; + } + } + + 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++; + + return result; +} + +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, max_pos); + size len = 0; + Vec keys = Vec_new(a, StrView, 6); + Vec values = Vec_new(a, JSON, 6); + if (src[result.pos] == '}') { + result.pos++; + result.value = JSON_object(len, keys, values); + return result; + } + for (;;) { + parse_result key_result = parse_string(src, result.pos, max_pos); + result.pos = absorb_whitespaces(src, key_result.pos, max_pos); + assert_parse(src[result.pos] == ':', "invalid keyval delim", src, + result.pos); + result.pos = absorb_whitespaces(src, result.pos + 1, max_pos); + parse_result value_result = parse_any(a, src, result.pos, max_pos); + len++; + 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, max_pos); + if (src[result.pos] != ',') { + break; + } + result.pos = absorb_whitespaces(src, result.pos + 1, max_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, uptr max_pos) { + pos = absorb_whitespaces(src, pos, max_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(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); + result.pos = pos + 4; + return result; + } + if (memcmp(src + pos, "false", 5) == 0) { + parse_result result = {0}; + result.value = JSON_boolean(false); + result.pos = pos + 5; + return result; + } + if (memcmp(src + pos, "null", 4) == 0) { + parse_result result = {0}; + result.value = JSON_null(); + result.pos = pos + 4; + return result; + } + assert_parse(false, "unexpected input", src, pos); +} + +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++) { + StrView tmp_key = *Vec_get(obj.keys, StrView, i); + if (Str_equals(tmp_key, key)) return Vec_get(obj.values, JSON, i); + } + return 0; +} diff --git a/src/json/json.h b/src/json/json.h new file mode 100644 index 0000000..15b3fca --- /dev/null +++ b/src/json/json.h @@ -0,0 +1,34 @@ +#pragma once +#include "../std/memory.h" +#include "../std/str.h" +#include "../std/vec.h" + +typedef struct { + Vec keys; // Vec + Vec values; // Vec + size len; +} JSONObject; +typedef enum { + JSON_NULL, + JSON_BOOL, + JSON_INTEGER, + JSON_FLOAT, + JSON_STRING, + JSON_ARRAY, + JSON_OBJECT +} JSONType; +typedef struct { + JSONType type; + union { + bool boolean; + i32 integer; + f32 floating; + Vec array; // Vec + StrView string; + JSONObject object; + } as; +} JSON; +JSON *JSONObject_get(JSONObject obj, Str key); +i32 JSON_fprint(FILE *w, JSON json); +JSON JSON_parse(Arena *a, byte *src, uptr len); +JSON JSON_Int(Arena *a, i32 val); diff --git a/src/json/test_json.c b/src/json/test_json.c new file mode 100644 index 0000000..058f191 --- /dev/null +++ b/src/json/test_json.c @@ -0,0 +1,224 @@ +#include "json.h" +#include "../std/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_raw(&perm, "123"); + + t_assert_equal(json_int.as.integer, 123); + t_assert_equal(Arena_get_used(perm), 0); + return 0; +} + +i32 test_json_int_array() { + Arena perm = Arena_new(128e3); + + JSON json_int_array = JSON_parse_raw(&perm, "[1,2,3]"); + + 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; + for (int i = 0; i < json_int_array.as.array.len; i++) { + JSON *item = Vec_get(json_int_array.as.array, JSON, i); + ints[i] = item->as.integer; + sum += item->as.integer; + } + 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; +} + +i32 test_json_empty_array() { + Arena perm = Arena_new(128e3); + + JSON json_empty_array = JSON_parse_raw(&perm, "[]"); + + t_assert_equal(json_empty_array.as.array.len, 0); + t_assert_equal(Arena_get_used(perm), 0); // TODO: weird + + return 0; +} + +i32 test_json_nested_array() { + Arena perm = Arena_new(128e3); + + 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); + t_assert_equal(val_1.as.integer, 1); + JSON val_2 = *Vec_get(sub_array.as.array, JSON, 1); + t_assert_strequal(val_2.as.string, S("abc")); + t_assert_equal(Arena_get_used(perm), 1152); + + return 0; +} + +i32 test_json_string() { + Arena perm = Arena_new(128e3); + + JSON js = JSON_parse_raw(&perm, "\"abc\""); + + t_assert_strequal(js.as.string, S("abc")); + t_assert_equal(Arena_get_used(perm), 0); + + return 0; +} + +i32 test_json_empty_object() { + Arena perm = Arena_new(128e3); + + JSON doc = JSON_parse_raw(&perm, "{}"); + + t_assert_equal(doc.as.object.len, 0); + t_assert_equal(Arena_get_used(perm), 672); + + return 0; +} + +i32 test_json_object() { + Arena perm = Arena_new(128e3); + + JSON doc = JSON_parse_raw(&perm, "{\"key\": \"value\"}"); + + t_assert_equal(doc.as.object.len, 1); + StrView key = *Vec_get(doc.as.object.keys, StrView, 0); + t_assert_strequal(key, S("key")); + JSON value = *JSONObject_get(doc.as.object, S("key")); + t_assert_strequal(value.as.string, S("value")); + t_assert_equal(Arena_get_used(perm), 672); + + return 0; +} + +i32 test_json_boolean() { + Arena perm = Arena_new(128e3); + + JSON doc = JSON_parse_raw(&perm, "{\"a\": true, \"b\": false}"); + + JSON a = *JSONObject_get(doc.as.object, S("a")); + t_assert_equal(a.as.boolean, true); + JSON b = *JSONObject_get(doc.as.object, S("b")); + t_assert_equal(b.as.boolean, false); + t_assert_equal(Arena_get_used(perm), 672); + + return 0; +} + +i32 test_json_float() { + Arena perm = Arena_new(128e3); + + 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); + JSON second = *Vec_get(doc.as.array, JSON, 1); + t_assert_fequal(second.as.floating, 2.3); + t_assert_equal(Arena_get_used(perm), 576); + + return 0; +} + +i32 test_json_null() { + Arena perm = Arena_new(128e3); + + 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_equal(first.type, JSON_NULL); + t_assert_equal(Arena_get_used(perm), 576); + + return 0; +} + +i32 test_json_long_spaces() { + Arena perm = Arena_new(128e3); + JSON doc = JSON_parse_raw( + &perm, "{\t \t \t \t \t }"); + + t_assert_equal(doc.as.object.len, 0); + + return 0; +} + +i32 test_json_multiline() { + Arena perm = Arena_new(128e3); + MemoryFile file = + load_file(&perm, "src/json/test_json/test_json_multiline.json"); + JSON doc = JSON_parse(&perm, file.content, file.size); + JSON target_size_mb = *JSONObject_get(doc.as.object, S("target_size_mb")); + t_assert_equal(target_size_mb.as.integer, 1); + JSON generated_at = *JSONObject_get(doc.as.object, S("generated_at")); + t_assert_strequal(generated_at.as.string, S("2025-07-22T10:36:30.974926")); + + return 0; +} + +i32 test_json_nested_object() { + Arena perm = Arena_new(128e3); + + JSON doc = JSON_parse_raw(&perm, "{\"1\":{\"2\":{}}}"); + + JSON lvl_1 = *JSONObject_get(doc.as.object, S("1")); + t_assert_equal(lvl_1.type, JSON_OBJECT); + JSON lvl_2 = *JSONObject_get(lvl_1.as.object, S("2")); + t_assert_equal(lvl_2.type, JSON_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_equal(tags.type, JSON_ARRAY); + t_assert_equal(tags.as.array.len, 2); + + return 0; +} + +i32 test_json_complex_multiline() { + Arena perm = Arena_new(128e3); + MemoryFile file = + load_file(&perm, "src/json/test_json/test_json_complex_multiline.json"); + JSON doc = JSON_parse(&perm, file.content, file.size); + + JSON id = *JSONObject_get(doc.as.object, S("id")); + 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_strequal(theme.as.string, S("auto")); + + 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_equal(integer.as.integer, -1); + JSON floating = *Vec_get(doc.as.array, JSON, 1); + t_assert_fequal(floating.as.floating, -2.5); + + return 0; +} + +i32 test_json_backspaces() { + Arena perm = Arena_new(128e3); + MemoryFile file = + load_file(&perm, "src/json/test_json/test_json_backspaces.json"); + JSON doc = JSON_parse(&perm, file.content, file.size); + + t_assert_strequal(doc.as.string, S("weird text\\")); + return 0; +} diff --git a/src/json/test_json/test_json_backspaces.json b/src/json/test_json/test_json_backspaces.json new file mode 100644 index 0000000..ceb65d6 --- /dev/null +++ b/src/json/test_json/test_json_backspaces.json @@ -0,0 +1 @@ +"weird text\\" diff --git a/src/json/test_json/test_json_complex_multiline.json b/src/json/test_json/test_json_complex_multiline.json new file mode 100644 index 0000000..a0cfc9b --- /dev/null +++ b/src/json/test_json/test_json_complex_multiline.json @@ -0,0 +1,24 @@ +{ + "id": 819228, + "username": "rCrRv4PRnUNuAy", + "email": "xzbwrzj@zwod.com", + "first_name": "4WH7g7PsOEm5", + "last_name": "6ulndxUgjbIayl", + "age": 30, + "phone": "+1-529-712-3931", + "is_active": false, + "balance": 9347.49, + "tags": [ + "TWvZi0", + "pM0TPv9X" + ], + "metadata": { + "last_login": "2024-06-13T21:22:32Z", + "login_count": 245, + "preferences": { + "theme": "auto", + "notifications": true, + "language": "en" + } + } +} diff --git a/src/json/test_json/test_json_multiline.json b/src/json/test_json/test_json_multiline.json new file mode 100644 index 0000000..7cb5f4c --- /dev/null +++ b/src/json/test_json/test_json_multiline.json @@ -0,0 +1,5 @@ +{ + "generated_at": "2025-07-22T10:36:30.974926", + "target_size_mb": 1, + "generator_version": "1.0" +} diff --git a/src/log.c b/src/log.c deleted file mode 100644 index c1d1717..0000000 --- a/src/log.c +++ /dev/null @@ -1,12 +0,0 @@ -#include - -#include "../include/log.h" -#include "../include/types.h" - -void info(Logger l, const byte *name, const byte *str, ...) { - if (l.level >= INFO) { - printf("[%s] ", name); - printf("%s", str); - printf("\n"); - } -} diff --git a/src/memory.c b/src/memory.c deleted file mode 100644 index 7072c61..0000000 --- a/src/memory.c +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include -#include - -#include "../include/assert.h" -#include "../include/memory.h" -#include "../include/types.h" - -Arena Arena_new(size cap) { - Arena a = {0}; - a.cap = cap; - a.beg = malloc(cap); - a.end = a.beg ? a.beg + cap : 0; - return a; -} - -// Magic to allow optimisation __attribute((malloc, alloc_size(2, 4), -// alloc_align(3))) -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, "out of memory"); - a->beg = beg + alloc_size; - return beg; -} - -void Arena_reset(Arena *a) { - a->beg = a->end - a->cap; - memset(a->beg, 0, a->end - a->beg); -} - -size Arena_get_used(Arena a) { return a.cap - (a.end - a.beg); } diff --git a/src/std/assert.h b/src/std/assert.h new file mode 100644 index 0000000..aa62bd1 --- /dev/null +++ b/src/std/assert.h @@ -0,0 +1,15 @@ +#pragma once + +#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(); \ + } \ + } while (0) diff --git a/src/std/log.c b/src/std/log.c new file mode 100644 index 0000000..980c7e7 --- /dev/null +++ b/src/std/log.c @@ -0,0 +1,12 @@ +#include + +#include "log.h" +#include "types.h" + +void info(Logger l, const byte *name, const byte *str, ...) { + if (l.level >= INFO) { + printf("[%s] ", name); + printf("%s", str); + printf("\n"); + } +} diff --git a/src/std/log.h b/src/std/log.h new file mode 100644 index 0000000..1458f03 --- /dev/null +++ b/src/std/log.h @@ -0,0 +1,10 @@ + +#include "types.h" + +typedef enum { INFO } LogLevel; + +typedef struct { + LogLevel level; +} Logger; + +void info(Logger l, const byte *name, const byte *str, ...); diff --git a/src/std/memory.c b/src/std/memory.c new file mode 100644 index 0000000..3448819 --- /dev/null +++ b/src/std/memory.c @@ -0,0 +1,33 @@ +#include +#include +#include + +#include "assert.h" +#include "memory.h" +#include "types.h" + +Arena Arena_new(size cap) { + Arena a = {0}; + a.cap = cap; + a.beg = malloc(cap); + a.end = a.beg ? a.beg + cap : 0; + return a; +} + +// Magic to allow optimisation __attribute((malloc, alloc_size(2, 4), +// alloc_align(3))) +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, "out of memory"); + a->beg = beg + alloc_size; + return beg; +} + +void Arena_reset(Arena *a) { + a->beg = a->end - a->cap; + memset(a->beg, 0, a->end - a->beg); +} + +size Arena_get_used(Arena a) { return a.cap - (a.end - a.beg); } diff --git a/src/std/memory.h b/src/std/memory.h new file mode 100644 index 0000000..d202e97 --- /dev/null +++ b/src/std/memory.h @@ -0,0 +1,16 @@ +#pragma once +#include + +#include "types.h" + +typedef struct { + size cap; + byte *beg; + byte *end; +} Arena; + +Arena Arena_new(size cap); +#define new(a, T, n) (T *)alloc(a, n, sizeof(T), _Alignof(T)) +void *alloc(Arena *a, size count, size tsize, size align); +size Arena_get_used(Arena a); +void Arena_reset(Arena *a); diff --git a/src/std/str.c b/src/std/str.c new file mode 100644 index 0000000..714cc59 --- /dev/null +++ b/src/std/str.c @@ -0,0 +1,32 @@ +#include +#include +#include + +#include "memory.h" +#include "str.h" + +u64 Str_hash64(Str s) { + u64 h = 0x100; + for (size i = 0; i < s.len; i++) { + h ^= s.data[i] & 255; + h *= 1111111111111111111; + } + return h; +} + +Str Str_copy(Arena *a, byte *data, size len) { + Str str = {0}; + str.len = len; + str.data = new(a, byte, len); + if (str.len) memcpy(str.data, data, len); + return str; +} + +StrView Str_view(byte *data, size len) { + StrView str = {0}; + str.len = len; + str.data = data; + return str; +} + +Str StrView_copy(Arena *a, StrView s) { return Str_copy(a, s.data, s.len); } diff --git a/src/std/str.h b/src/std/str.h new file mode 100644 index 0000000..526db63 --- /dev/null +++ b/src/std/str.h @@ -0,0 +1,30 @@ +#pragma once +#include +#include +#include + +#include "memory.h" +#include "types.h" + +typedef struct { + byte *data; + size len; +} Str; +u64 Str_hash64(Str s); +Str Str_copy(Arena *a, byte *data, size len); +i32 Str_fprint(FILE *w, Str s); + +typedef struct { + byte *data; + size len; +} StrView; +StrView Str_view(byte *data, size len); +Str StrView_copy(Arena *a, StrView s); + +// Macros working with both types +// Returns 1 if strings are equal, 0 otherwise +#define Str_equals(a, b) \ + ((a).len == (b).len) && (!(a).len || !memcmp((a).data, (b).data, (a).len)) +#define Str_fprint(w, s) fprintf(w, "%.*s", (i32)(s).len, (s).data) +#define S(s) \ + (Str) { s, lengthof(s) } diff --git a/src/std/test_string.c b/src/std/test_string.c new file mode 100644 index 0000000..d6bc716 --- /dev/null +++ b/src/std/test_string.c @@ -0,0 +1,41 @@ +#include "str.h" +#include "testing.h" +#include "types.h" +#include + +i32 test_string_literal_equality() { + t_assert_strequal(S("Bonjour"), S("Bonjour")); + return 0; +} + +i32 test_string_copy() { + Arena perm = Arena_new(128e3); + Str src = S("Hello"); + + Str cpy = Str_copy(&perm, src.data, src.len); + + t_assert_strequal(src, cpy); + t_assert_equal(Arena_get_used(perm), 5); + + return 0; +} + +i32 test_fast_string() { + bytex32 quote_mask = _mm256_set1_epi8('"'); + byte *s = malloc(32 * sizeof(byte)); + s = "abcdefghijklmnopqrstuvwxyzabcdef"; + + bytex32 chunk = _mm256_loadu_si256((bytex32 *)s); + bytex32 cmp = _mm256_cmpeq_epi8(chunk, quote_mask); + i32 mask = _mm256_movemask_epi8(cmp); + t_assert_equal(_mm_popcnt_u32(mask), 0); + + s = "abcdefghijklm\"nopqrstuvwxyzabcde"; + + chunk = _mm256_loadu_si256((bytex32 *)s); + cmp = _mm256_cmpeq_epi8(chunk, quote_mask); + mask = _mm256_movemask_epi8(cmp); + t_assert_equal(_mm_popcnt_u32(mask), 1); + + return 0; +} diff --git a/src/std/test_time.c b/src/std/test_time.c new file mode 100644 index 0000000..f948fd0 --- /dev/null +++ b/src/std/test_time.c @@ -0,0 +1,13 @@ +#include "testing.h" +#include "time.h" +#include "types.h" + +i32 test_time_diff() { + Time start = Time_get(); + Time end = Time_get(); + + Duration diff = Time_diff(end, start); + t_assert_equal(diff.s, 0); + + return 0; +} diff --git a/src/std/test_vec.c b/src/std/test_vec.c new file mode 100644 index 0000000..e95a1b6 --- /dev/null +++ b/src/std/test_vec.c @@ -0,0 +1,88 @@ +#include "testing.h" +#include "vec.h" + +typedef struct { + i32 x; + i32 y; +} Point; + +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; +} + +i32 test_vec_push() { + Arena perm = Arena_new(128e3); + Vec v = Vec_new(&perm, i32, 4); + + i32 vals[] = {10, 20, 30}; + for (i32 i = 0; i < 3; i++) Vec_push(&perm, &v, &vals[i]); + + t_assert_equal(v.len, 3); + t_assert_equal(*Vec_get(v, i32, 0), 10); + t_assert_equal(*Vec_get(v, i32, 1), 20); + t_assert_equal(*Vec_get(v, i32, 2), 30); + t_assert_equal(Arena_get_used(perm), v.cap * sizeof(i32)); + + return 0; +} + +i32 test_vec_grow() { + Arena perm = Arena_new(128e3); + Vec v = Vec_new(&perm, i32, 2); + + for (i32 i = 0; i < 8; i++) Vec_push(&perm, &v, &i); + + t_assert_equal(v.len, 8); + t_assert_equal(Arena_get_used(perm), sizeof(i32) * (2 + 4 + 8)); + + return 0; +} + +i32 test_vec_struct() { + Arena perm = Arena_new(128e3); + Vec v = Vec_new(&perm, Point, 2); + + Point p1 = {1, 2}, p2 = {3, 4}; + Vec_push(&perm, &v, &p1); + Vec_push(&perm, &v, &p2); + + Point *r0 = Vec_get(v, Point, 0); + Point *r1 = Vec_get(v, Point, 1); + t_assert_equal(r0->x, 1); + t_assert_equal(r0->y, 2); + t_assert_equal(r1->x, 3); + t_assert_equal(r1->y, 4); + t_assert_equal(Arena_get_used(perm), 16); + + return 0; +} + +i32 test_vec_initial_len() { + Arena perm = Arena_new(128e3); + Vec v = Vec_new(&perm, i32, 4); + + t_assert_equal(v.len, 0); + t_assert_equal(v.cap, 4); + t_assert_equal(Arena_get_used(perm), 16); + + return 0; +} diff --git a/src/std/testing.c b/src/std/testing.c new file mode 100644 index 0000000..af2d1c8 --- /dev/null +++ b/src/std/testing.c @@ -0,0 +1,28 @@ +#include + +#include "assert.h" +#include "memory.h" +#include "str.h" +#include "testing.h" +#include "types.h" + +MemoryFile load_file(Arena *a, const char *filename) { + FILE *file = fopen(filename, "rb"); + assert(file != 0, "failed to open file: %s", filename); + MemoryFile mem_file = {0}; + + // Get file size + fseek(file, 0, SEEK_END); + mem_file.size = ftell(file); + fseek(file, 0, SEEK_SET); + + mem_file.content = new(a, byte, mem_file.size + 1); + + size bytes_read = fread(mem_file.content, 1, mem_file.size, file); + assert(bytes_read == mem_file.size, "failed to read entire file: %s", + filename); + + mem_file.content[mem_file.size] = '\0'; // Null terminate + fclose(file); + return mem_file; +} diff --git a/src/std/testing.h b/src/std/testing.h new file mode 100644 index 0000000..388c778 --- /dev/null +++ b/src/std/testing.h @@ -0,0 +1,36 @@ +#pragma once +#include +#include + +#include "memory.h" + +typedef struct { + size size; + byte *content; +} MemoryFile; +MemoryFile load_file(Arena *a, const char *filename); + +#define t_assert_equal(a, b) \ + if (!(a == b)) { \ + fprintf(stderr, \ + "Assertion failed: %d (%s) != %d (%s) at %s:%d in %s()\n", \ + (i32)a, #a, (i32)b, #b, __FILE__, __LINE__, __func__); \ + return 1; \ + } + +#define t_assert_fequal(a, b) \ + if ((a - b) > 1e-6 || (b - a) > 1e-6) { \ + fprintf(stderr, \ + "Assertion failed: %f (%s) != %f (%s) at %s:%d in %s()\n", a, \ + #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/src/std/time.c b/src/std/time.c new file mode 100644 index 0000000..331f1d5 --- /dev/null +++ b/src/std/time.c @@ -0,0 +1,48 @@ +#include + +#include "assert.h" +#include "time.h" +#include "types.h" + +Time Time_get() { + struct timespec system_time; + assert(clock_gettime(CLOCK_MONOTONIC, &system_time) == 0, "clock_gettime failed"); + return (Time){system_time.tv_sec, system_time.tv_nsec}; +} + +f32 Time_ms(Time time) { + return time.s * 1e3 + time.ns / 1e6; +} + +Duration Time_diff(Time end, Time start) { + Duration d; + d.s = end.s - start.s; + d.ns = end.ns - start.ns; + if (d.ns >= (u64)1e9) { + d.s += d.ns / (u64)1e9; + d.ns = d.ns % (u64)1e9; + } + return d; +} + +f32 Duration_ms(Duration d) { + return d.s * 1e3 + d.ns / 1e6; +} + +Duration Duration_from_ms(f32 ms) { + Duration d; + d.s = (u64)(ms / 1e3); + d.ns = (u64)((ms - d.s * 1e3) * 1e6); + return d; +} + +Duration Duration_diff(Duration a, Duration b) { + Duration d; + d.s = a.s - b.s; + d.ns = a.ns - b.ns; + if (d.ns >= (u64)1e9) { + d.s += d.ns / (u64)1e9; + d.ns = d.ns % (u64)1e9; + } + return d; +} diff --git a/src/std/time.h b/src/std/time.h new file mode 100644 index 0000000..26bbf20 --- /dev/null +++ b/src/std/time.h @@ -0,0 +1,20 @@ +#pragma once + +#include "types.h" + +typedef struct { + u64 s; + u64 ns; +} Time; + +typedef struct { + u64 s; + u64 ns; +} Duration; + +Time Time_get(); +f32 Time_ms(Time time); +Duration Time_diff(Time end, Time start); +f32 Duration_ms(Duration duration); +Duration Duration_from_ms(f32 ms); +Duration Duration_diff(Duration a, Duration b); diff --git a/src/std/types.h b/src/std/types.h new file mode 100644 index 0000000..6df9510 --- /dev/null +++ b/src/std/types.h @@ -0,0 +1,16 @@ +#include +#include +#include +#include + +typedef ptrdiff_t size; +typedef char byte; +typedef uintptr_t uptr; +typedef int32_t i32; +typedef uint64_t u64; +typedef float f32; +typedef __m256i bytex32; + +#define sizeof(x) (size)sizeof(x) +#define countof(a) (sizeof(a) / sizeof(*(a))) +#define lengthof(s) (countof(s) - 1) diff --git a/src/std/vec.c b/src/std/vec.c new file mode 100644 index 0000000..077e64a --- /dev/null +++ b/src/std/vec.c @@ -0,0 +1,27 @@ +#include + +#include "assert.h" +#include "types.h" +#include "vec.h" + +Vec _Vec_new(Arena *a, size tsize, size cap, size align) { + assert(cap > 0, "negative cap"); + Vec v = {0}; + v.tsize = tsize; + v.cap = cap; + v.align = align; + v.len = 0; + v.data = alloc(a, cap, tsize, align); + return v; +} + +void Vec_push(Arena *a, Vec *v, void *data) { + if (v->len == v->cap) { + void *cpy = alloc(a, v->cap * 2, v->tsize, v->align); + memcpy(cpy, v->data, v->len * v->tsize); + v->cap *= 2; + v->data = cpy; + } + memcpy(v->data + v->len * v->tsize, data, v->tsize); + v->len++; +} diff --git a/src/std/vec.h b/src/std/vec.h new file mode 100644 index 0000000..32734ae --- /dev/null +++ b/src/std/vec.h @@ -0,0 +1,18 @@ +#pragma once +#include +#include + +#include "memory.h" +#include "types.h" + +typedef struct { + void *data; + size len; + size cap; + size tsize; + size align; +} Vec; +#define Vec_new(a, T, cap) _Vec_new(a, sizeof(T), cap, _Alignof(T)) +Vec _Vec_new(Arena *a, size tsize, size cap, size align); +void Vec_push(Arena *a, Vec *v, void *data); +#define Vec_get(v, T, pos) (T *)(v.data + v.tsize * pos) diff --git a/src/str.c b/src/str.c deleted file mode 100644 index 3523d98..0000000 --- a/src/str.c +++ /dev/null @@ -1,32 +0,0 @@ -#include -#include -#include - -#include "../include/memory.h" -#include "../include/str.h" - -u64 Str_hash64(Str s) { - u64 h = 0x100; - for (size i = 0; i < s.len; i++) { - h ^= s.data[i] & 255; - h *= 1111111111111111111; - } - return h; -} - -Str Str_copy(Arena *a, byte *data, size len) { - Str str = {0}; - str.len = len; - str.data = new(a, byte, len); - if (str.len) memcpy(str.data, data, len); - return str; -} - -StrView Str_view(byte *data, size len) { - StrView str = {0}; - str.len = len; - str.data = data; - return str; -} - -Str StrView_copy(Arena *a, StrView s) { return Str_copy(a, s.data, s.len); } diff --git a/src/testing.c b/src/testing.c deleted file mode 100644 index 937d2dc..0000000 --- a/src/testing.c +++ /dev/null @@ -1,28 +0,0 @@ -#include - -#include "../include/assert.h" -#include "../include/memory.h" -#include "../include/str.h" -#include "../include/testing.h" -#include "../include/types.h" - -MemoryFile load_file(Arena *a, const char *filename) { - FILE *file = fopen(filename, "rb"); - assert(file != 0, "failed to open file: %s", filename); - MemoryFile mem_file = {0}; - - // Get file size - fseek(file, 0, SEEK_END); - mem_file.size = ftell(file); - fseek(file, 0, SEEK_SET); - - mem_file.content = new(a, byte, mem_file.size + 1); - - size bytes_read = fread(mem_file.content, 1, mem_file.size, file); - assert(bytes_read == mem_file.size, "failed to read entire file: %s", - filename); - - mem_file.content[mem_file.size] = '\0'; // Null terminate - fclose(file); - return mem_file; -} diff --git a/src/time.c b/src/time.c deleted file mode 100644 index cbabf96..0000000 --- a/src/time.c +++ /dev/null @@ -1,48 +0,0 @@ -#include - -#include "../include/assert.h" -#include "../include/time.h" -#include "../include/types.h" - -Time Time_get() { - struct timespec system_time; - assert(clock_gettime(CLOCK_MONOTONIC, &system_time) == 0, "clock_gettime failed"); - return (Time){system_time.tv_sec, system_time.tv_nsec}; -} - -f32 Time_ms(Time time) { - return time.s * 1e3 + time.ns / 1e6; -} - -Duration Time_diff(Time end, Time start) { - Duration d; - d.s = end.s - start.s; - d.ns = end.ns - start.ns; - if (d.ns >= (u64)1e9) { - d.s += d.ns / (u64)1e9; - d.ns = d.ns % (u64)1e9; - } - return d; -} - -f32 Duration_ms(Duration d) { - return d.s * 1e3 + d.ns / 1e6; -} - -Duration Duration_from_ms(f32 ms) { - Duration d; - d.s = (u64)(ms / 1e3); - d.ns = (u64)((ms - d.s * 1e3) * 1e6); - return d; -} - -Duration Duration_diff(Duration a, Duration b) { - Duration d; - d.s = a.s - b.s; - d.ns = a.ns - b.ns; - if (d.ns >= (u64)1e9) { - d.s += d.ns / (u64)1e9; - d.ns = d.ns % (u64)1e9; - } - return d; -} diff --git a/src/vec.c b/src/vec.c deleted file mode 100644 index 05ed70f..0000000 --- a/src/vec.c +++ /dev/null @@ -1,27 +0,0 @@ -#include - -#include "../include/assert.h" -#include "../include/types.h" -#include "../include/vec.h" - -Vec _Vec_new(Arena *a, size tsize, size cap, size align) { - assert(cap > 0, "negative cap"); - Vec v = {0}; - v.tsize = tsize; - v.cap = cap; - v.align = align; - v.len = 0; - v.data = alloc(a, cap, tsize, align); - return v; -} - -void Vec_push(Arena *a, Vec *v, void *data) { - if (v->len == v->cap) { - void *cpy = alloc(a, v->cap * 2, v->tsize, v->align); - memcpy(cpy, v->data, v->len * v->tsize); - v->cap *= 2; - v->data = cpy; - } - memcpy(v->data + v->len * v->tsize, data, v->tsize); - v->len++; -} -- cgit v1.3