aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Kagamino Lehoux <martin@lehoux.net>2025-07-23 19:39:13 +0200
committerMartin Kagamino Lehoux <martin@lehoux.net>2025-07-23 19:39:26 +0200
commit421300c27123ca647772dc6d9bd0cabf7f5f9c99 (patch)
treed916b62056b2440aef374b21373d16272bb709ee /src
parent53e9e371a16ae2d167f1fc2577dff2c2426eed68 (diff)
refactor: src & pragma once
Diffstat (limited to 'src')
-rw-r--r--src/json.c263
-rw-r--r--src/log.c12
-rw-r--r--src/memory.c30
-rw-r--r--src/str.c32
-rw-r--r--src/vec.c29
5 files changed, 366 insertions, 0 deletions
diff --git a/src/json.c b/src/json.c
new file mode 100644
index 0000000..6f00da8
--- /dev/null
+++ b/src/json.c
@@ -0,0 +1,263 @@
+#include <assert.h>
+#include <ctype.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.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, Str, 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(Str 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);
+ 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++) {
+ Str key = *Vec_get(json.as.object.keys, Str, 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);
+}
+
+typedef struct {
+ JSON value;
+ uptr pos;
+} parse_result;
+
+parse_result parse_any(Arena *a, byte *src, uptr pos);
+
+parse_result parse_number(byte *src, uptr start_pos) {
+ parse_result result = {0};
+ result.pos = start_pos;
+ i32 integer = 0;
+ while (isdigit(src[result.pos])) {
+ integer = integer * 10 + (src[result.pos] - '0');
+ result.pos++;
+ }
+ if (src[result.pos] != '.') {
+ result.value = JSON_integer(integer);
+ return result;
+ }
+ result.pos++;
+ f32 fractional_part = 0.0;
+ f32 decimal_place = 0.1;
+ while (isdigit(src[result.pos])) {
+ fractional_part += (src[result.pos] - '0') * decimal_place;
+ decimal_place *= 0.1;
+ result.pos++;
+ }
+ result.value = JSON_float((f32)integer + fractional_part);
+ return result;
+}
+
+uptr absorb_whitespaces(byte *src, uptr start_pos) {
+ uptr pos = start_pos;
+ while (isspace(src[pos])) {
+ pos++;
+ }
+ return pos;
+}
+
+parse_result parse_array(Arena *a, byte *src, uptr start_pos) {
+ assert(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, 2);
+ for (;;) {
+ result.pos = absorb_whitespaces(src, result.pos);
+ parse_result item_result = parse_any(a, src, result.pos);
+ Vec_push(a, &items, &item_result.value);
+ result.pos = absorb_whitespaces(src, item_result.pos);
+ if (src[result.pos] == ',') {
+ result.pos++;
+ } else {
+ break;
+ }
+ };
+ assert(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 result = {0};
+ result.pos = start_pos + 1;
+
+ while (src[result.pos] != '"' && src[result.pos - 1] != '\\') {
+ result.pos++;
+ }
+ assert(src[result.pos] == '"');
+ result.value = JSON_string(
+ Str_copy(a, &src[start_pos + 1], result.pos - start_pos - 1));
+ result.pos++;
+
+ return result;
+}
+
+parse_result parse_object(Arena *a, byte *src, uptr start_pos) {
+ assert(src[start_pos] == '{');
+ parse_result result = {0};
+ result.pos = absorb_whitespaces(src, start_pos + 1);
+ size len = 0;
+ Vec keys = Vec_new(a, Str, 2);
+ Vec values = Vec_new(a, JSON, 2);
+ if (src[result.pos] == '}') {
+ result.pos++;
+ result.value = JSON_object(len, keys, values);
+ return result;
+ }
+ for (;;) {
+ assert(src[result.pos] == '"');
+ parse_result key_result = parse_string(a, src, result.pos);
+ result.pos = absorb_whitespaces(src, key_result.pos);
+ assert(src[result.pos] == ':');
+ result.pos = absorb_whitespaces(src, result.pos + 1);
+ parse_result value_result = parse_any(a, src, result.pos);
+ len++;
+ assert(key_result.value.type == JSON_STRING);
+ Vec_push(a, &keys, &key_result.value.as.string);
+ Vec_push(a, &values, &value_result.value);
+ result.pos = absorb_whitespaces(src, value_result.pos);
+ if (src[result.pos] != ',') {
+ break;
+ }
+ result.pos = absorb_whitespaces(src, result.pos + 1);
+ }
+ assert(src[result.pos] == '}');
+ result.pos++;
+ result.value = JSON_object(len, keys, values);
+ return result;
+}
+
+parse_result parse_any(Arena *a, byte *src, uptr 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 (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(false);
+}
+
+JSON JSON_parse(Arena *a, byte *src) { return parse_any(a, src, 0).value; }
+
+JSON *JSONObject_get(JSONObject obj, Str key) {
+ for (size i = 0; i < obj.len; i++) {
+ if (Str_equals(*Vec_get(obj.keys, Str, i), key)) {
+ return Vec_get(obj.values, JSON, i);
+ }
+ }
+ assert(false);
+}
diff --git a/src/log.c b/src/log.c
new file mode 100644
index 0000000..c1d1717
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,12 @@
+#include <stdio.h>
+
+#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
new file mode 100644
index 0000000..63acb39
--- /dev/null
+++ b/src/memory.c
@@ -0,0 +1,30 @@
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.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);
+ a->beg = beg + alloc_size;
+ return memset(beg, 0, count * tsize);
+}
+
+void Arena_reset(Arena *a) { a->beg = a->end - a->cap; }
+
+size Arena_get_used(Arena a) { return a.cap - (a.end - a.beg); }
diff --git a/src/str.c b/src/str.c
new file mode 100644
index 0000000..eccacd8
--- /dev/null
+++ b/src/str.c
@@ -0,0 +1,32 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../include/memory.h"
+#include "../include/str.h"
+
+// Returns 1 if strings are equal, 0 otherwise
+i32 Str_equals(Str a, Str b) {
+ return (a.len == b.len) && (!a.len || !memcmp(a.data, b.data, a.len));
+}
+
+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;
+}
+
+i32 Str_fprint(FILE *w, Str s) {
+ return fprintf(w, "%.*s", (i32)s.len, s.data);
+}
diff --git a/src/vec.c b/src/vec.c
new file mode 100644
index 0000000..a5c2fb4
--- /dev/null
+++ b/src/vec.c
@@ -0,0 +1,29 @@
+#include <string.h>
+
+#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);
+ 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++;
+}
+
+void *_Vec_get(Vec v, size pos) { return v.data + v.tsize * pos; }