aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/json/json.c (renamed from src/json.c)8
-rw-r--r--src/json/json.h34
-rw-r--r--src/json/test_json.c224
-rw-r--r--src/json/test_json/test_json_backspaces.json1
-rw-r--r--src/json/test_json/test_json_complex_multiline.json24
-rw-r--r--src/json/test_json/test_json_multiline.json5
-rw-r--r--src/std/assert.h15
-rw-r--r--src/std/log.c (renamed from src/log.c)4
-rw-r--r--src/std/log.h10
-rw-r--r--src/std/memory.c (renamed from src/memory.c)6
-rw-r--r--src/std/memory.h16
-rw-r--r--src/std/str.c (renamed from src/str.c)4
-rw-r--r--src/std/str.h30
-rw-r--r--src/std/test_string.c41
-rw-r--r--src/std/test_time.c13
-rw-r--r--src/std/test_vec.c88
-rw-r--r--src/std/testing.c (renamed from src/testing.c)10
-rw-r--r--src/std/testing.h36
-rw-r--r--src/std/time.c (renamed from src/time.c)6
-rw-r--r--src/std/time.h20
-rw-r--r--src/std/types.h16
-rw-r--r--src/std/vec.c (renamed from src/vec.c)6
-rw-r--r--src/std/vec.h18
23 files changed, 613 insertions, 22 deletions
diff --git a/src/json.c b/src/json/json.c
index 67bbf2e..d16415b 100644
--- a/src/json.c
+++ b/src/json/json.c
@@ -8,10 +8,10 @@
#include <stdlib.h>
#include <string.h>
-#include "../include/assert.h"
-#include "../include/json.h"
-#include "../include/str.h"
-#include "../include/types.h"
+#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);
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<Str>
+ Vec values; // Vec<JSON>
+ 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<JSON>
+ 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/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 <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/src/log.c b/src/std/log.c
index c1d1717..980c7e7 100644
--- a/src/log.c
+++ b/src/std/log.c
@@ -1,7 +1,7 @@
#include <stdio.h>
-#include "../include/log.h"
-#include "../include/types.h"
+#include "log.h"
+#include "types.h"
void info(Logger l, const byte *name, const byte *str, ...) {
if (l.level >= INFO) {
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/memory.c b/src/std/memory.c
index 7072c61..3448819 100644
--- a/src/memory.c
+++ b/src/std/memory.c
@@ -2,9 +2,9 @@
#include <stdlib.h>
#include <string.h>
-#include "../include/assert.h"
-#include "../include/memory.h"
-#include "../include/types.h"
+#include "assert.h"
+#include "memory.h"
+#include "types.h"
Arena Arena_new(size cap) {
Arena a = {0};
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 <stddef.h>
+
+#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/str.c b/src/std/str.c
index 3523d98..714cc59 100644
--- a/src/str.c
+++ b/src/std/str.c
@@ -2,8 +2,8 @@
#include <stdio.h>
#include <string.h>
-#include "../include/memory.h"
-#include "../include/str.h"
+#include "memory.h"
+#include "str.h"
u64 Str_hash64(Str s) {
u64 h = 0x100;
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 <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#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 <stdio.h>
+
+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/testing.c b/src/std/testing.c
index 937d2dc..af2d1c8 100644
--- a/src/testing.c
+++ b/src/std/testing.c
@@ -1,10 +1,10 @@
#include <stdio.h>
-#include "../include/assert.h"
-#include "../include/memory.h"
-#include "../include/str.h"
-#include "../include/testing.h"
-#include "../include/types.h"
+#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");
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 <stdio.h>
+#include <string.h>
+
+#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/time.c b/src/std/time.c
index cbabf96..331f1d5 100644
--- a/src/time.c
+++ b/src/std/time.c
@@ -1,8 +1,8 @@
#include <time.h>
-#include "../include/assert.h"
-#include "../include/time.h"
-#include "../include/types.h"
+#include "assert.h"
+#include "time.h"
+#include "types.h"
Time Time_get() {
struct timespec system_time;
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 <immintrin.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+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/vec.c b/src/std/vec.c
index 05ed70f..077e64a 100644
--- a/src/vec.c
+++ b/src/std/vec.c
@@ -1,8 +1,8 @@
#include <string.h>
-#include "../include/assert.h"
-#include "../include/types.h"
-#include "../include/vec.h"
+#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");
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 <stddef.h>
+#include <stdint.h>
+
+#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)