summaryrefslogtreecommitdiff
path: root/archive/src/std
diff options
context:
space:
mode:
Diffstat (limited to 'archive/src/std')
-rw-r--r--archive/src/std/buf.h11
-rw-r--r--archive/src/std/containers/container_utils.h17
-rw-r--r--archive/src/std/containers/darray.h151
-rw-r--r--archive/src/std/containers/graphs.h14
-rw-r--r--archive/src/std/containers/hashmap.h27
-rw-r--r--archive/src/std/containers/hashset.h29
-rw-r--r--archive/src/std/containers/ring_queue.c68
-rw-r--r--archive/src/std/containers/ring_queue.h35
-rw-r--r--archive/src/std/containers/stack_array.h19
-rw-r--r--archive/src/std/mem.c135
-rw-r--r--archive/src/std/mem.h96
-rw-r--r--archive/src/std/str.c74
-rw-r--r--archive/src/std/str.h89
-rw-r--r--archive/src/std/utils.h4
14 files changed, 769 insertions, 0 deletions
diff --git a/archive/src/std/buf.h b/archive/src/std/buf.h
new file mode 100644
index 0000000..77fc7b9
--- /dev/null
+++ b/archive/src/std/buf.h
@@ -0,0 +1,11 @@
+/**
+ * @file buf.h
+ * @brief
+ */
+#pragma once
+#include "defines.h"
+
+typedef struct bytebuffer {
+ u8* buf;
+ size_t size;
+} bytebuffer;
diff --git a/archive/src/std/containers/container_utils.h b/archive/src/std/containers/container_utils.h
new file mode 100644
index 0000000..e1d164c
--- /dev/null
+++ b/archive/src/std/containers/container_utils.h
@@ -0,0 +1,17 @@
+/**
+ * @file container_utils.h
+ * @author your name (you@domain.com)
+ * @brief
+ * @version 0.1
+ * @date 2024-06-19
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+
+#pragma once
+
+typedef struct generic_iterator {
+} generic_iterator;
+
+typedef void* (*iterator_next_item)(void* iterator); \ No newline at end of file
diff --git a/archive/src/std/containers/darray.h b/archive/src/std/containers/darray.h
new file mode 100644
index 0000000..080afb4
--- /dev/null
+++ b/archive/src/std/containers/darray.h
@@ -0,0 +1,151 @@
+/**
+ * @file darray.h
+ * @brief Typed dynamic array
+ * @copyright Copyright (c) 2023
+ */
+// COPIED FROM KITC WITH SOME MINOR ADJUSTMENTS
+
+/* TODO:
+ - a 'find' function that takes a predicate (maybe wrap with a macro so we dont have to define a
+ new function?)
+*/
+
+#ifndef KITC_TYPED_ARRAY_H
+#define KITC_TYPED_ARRAY_H
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define DARRAY_DEFAULT_CAPACITY 64
+#define DARRAY_RESIZE_FACTOR 3
+
+/** @brief create a new darray type and functions with type `N` */
+#define typed_array(T) \
+ struct { \
+ /* @brief current number of items in the array */ \
+ size_t len; \
+ size_t capacity; \
+ T* data; \
+ }
+
+#define typed_array_iterator(T) \
+ struct { \
+ T##_darray* array; \
+ size_t current_idx; \
+ }
+
+#define PREFIX static
+
+#define KITC_DECL_TYPED_ARRAY(T) DECL_TYPED_ARRAY(T, T)
+
+#define DECL_TYPED_ARRAY(T, Type) \
+ typedef typed_array(T) Type##_darray; \
+ typedef typed_array_iterator(Type) Type##_darray_iter; \
+ \
+ /* Create a new one growable array */ \
+ PREFIX Type##_darray* Type##_darray_new(size_t starting_capacity) { \
+ Type##_darray* d; \
+ T* data; \
+ d = malloc(sizeof(Type##_darray)); \
+ data = malloc(starting_capacity * sizeof(T)); \
+ \
+ d->len = 0; \
+ d->capacity = starting_capacity; \
+ d->data = data; \
+ \
+ return d; \
+ } \
+ \
+ PREFIX void Type##_darray_free(Type##_darray* d) { \
+ if (d != NULL) { \
+ free(d->data); \
+ free(d); \
+ } \
+ } \
+ \
+ PREFIX T* Type##_darray_resize(Type##_darray* d, size_t capacity) { \
+ /* resize the internal data block */ \
+ T* new_data = realloc(d->data, sizeof(T) * capacity); \
+ /* TODO: handle OOM error */ \
+ \
+ d->capacity = capacity; \
+ d->data = new_data; \
+ return new_data; \
+ } \
+ \
+ PREFIX void Type##_darray_push(Type##_darray* d, T value) { \
+ if (d->len >= d->capacity) { \
+ size_t new_capacity = \
+ d->capacity > 0 ? d->capacity * DARRAY_RESIZE_FACTOR : DARRAY_DEFAULT_CAPACITY; \
+ T* resized = Type##_darray_resize(d, new_capacity); \
+ (void)resized; \
+ } \
+ \
+ d->data[d->len] = value; \
+ d->len += 1; \
+ } \
+ \
+ PREFIX void Type##_darray_push_copy(Type##_darray* d, const T* value) { \
+ if (d->len >= d->capacity) { \
+ size_t new_capacity = \
+ d->capacity > 0 ? d->capacity * DARRAY_RESIZE_FACTOR : DARRAY_DEFAULT_CAPACITY; \
+ T* resized = Type##_darray_resize(d, new_capacity); \
+ (void)resized; \
+ } \
+ \
+ T* place = d->data + d->len; \
+ d->len += 1; \
+ memcpy(place, value, sizeof(T)); \
+ } \
+ \
+ PREFIX void Type##_darray_pop(Type##_darray* d, T* dest) { \
+ T* item = d->data + (d->len - 1); \
+ d->len -= 1; \
+ memcpy(dest, item, sizeof(T)); \
+ } \
+ \
+ PREFIX void Type##_darray_ins(Type##_darray* d, const T* value, size_t index) { \
+ /* check if requires resize */ \
+ if (d->len + 1 > d->capacity) { \
+ size_t new_capacity = \
+ d->capacity > 0 ? d->capacity * DARRAY_RESIZE_FACTOR : DARRAY_DEFAULT_CAPACITY; \
+ T* resized = Type##_darray_resize(d, new_capacity); \
+ (void)resized; \
+ } \
+ \
+ /* shift existing data after index */ \
+ T* insert_dest = d->data + index; \
+ T* shift_dest = insert_dest + 1; \
+ \
+ int num_items = d->len - index; \
+ \
+ d->len += 1; \
+ memcpy(shift_dest, insert_dest, num_items * sizeof(T)); \
+ memcpy(insert_dest, value, sizeof(T)); \
+ } \
+ \
+ PREFIX void Type##_darray_clear(Type##_darray* d) { \
+ d->len = 0; \
+ memset(d->data, 0, d->capacity * sizeof(T)); \
+ } \
+ \
+ PREFIX size_t Type##_darray_len(Type##_darray* d) { return d->len; } \
+ \
+ PREFIX Type##_darray_iter Type##_darray_iter_new(Type##_darray* d) { \
+ Type##_darray_iter iterator; \
+ iterator.array = d; \
+ iterator.current_idx = 0; \
+ return iterator; \
+ } \
+ \
+ PREFIX void* Type##_darray_iter_next(Type##_darray_iter* iterator) { \
+ if (iterator->current_idx < iterator->array->len) { \
+ return &iterator->array->data[iterator->current_idx++]; \
+ } else { \
+ return NULL; \
+ } \
+ }
+
+#endif // KITC_TYPED_ARRAY_H
diff --git a/archive/src/std/containers/graphs.h b/archive/src/std/containers/graphs.h
new file mode 100644
index 0000000..5dbec97
--- /dev/null
+++ b/archive/src/std/containers/graphs.h
@@ -0,0 +1,14 @@
+/**
+ * @file graphs.h
+ * @author your name (you@domain.com)
+ * @brief
+ * @version 0.1
+ * @date 2024-04-27
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+
+// Adjacency list backed graphs
+
+// Matrix backed graphs (not as useful) \ No newline at end of file
diff --git a/archive/src/std/containers/hashmap.h b/archive/src/std/containers/hashmap.h
new file mode 100644
index 0000000..95c1c6b
--- /dev/null
+++ b/archive/src/std/containers/hashmap.h
@@ -0,0 +1,27 @@
+/**
+ * @file hashmap.h
+ * @author your name (you@domain.com)
+ * @brief
+ * @version 0.1
+ * @date 2024-04-27
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+
+typedef struct hashmap hashmap;
+
+/*
+Example usage
+-------------
+init hashmap
+insert (string, material)
+get (string) -> material_opt or material* ?
+
+*/
+
+void hashmap_init(hashmap* map);
+
+// ...
+
+void hashmap_free(hashmap* map); \ No newline at end of file
diff --git a/archive/src/std/containers/hashset.h b/archive/src/std/containers/hashset.h
new file mode 100644
index 0000000..7f87213
--- /dev/null
+++ b/archive/src/std/containers/hashset.h
@@ -0,0 +1,29 @@
+/**
+ * @file hashset.h
+ * @author your name (you@domain.com)
+ * @brief
+ * @version 0.1
+ * @date 2024-04-27
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+
+#include "defines.h"
+
+typedef struct hashset hashset;
+
+/** @brief Describes a function that will take a pointer to a datatype (e.g. a u64 or a struct)
+ and return a hashed key. */
+typedef uint64_t (*hash_item)(void* item);
+
+void hashset_init(hashset* set, hash_item hash_func, size_t initial_capacity);
+// TODO: void hashset_from_iterator();
+bool hashset_insert(hashset* set, void* item, uint64_t* out_key);
+void hashset_batch_insert(hashset* set, void* items, u64 item_count);
+bool hashset_contains(hashset* set, void* item);
+bool hashset_remove_item(hashset* set, void* item);
+bool hashset_remove_key(hashset* set, uint64_t key);
+void hashset_merge(hashset* set_a, hashset* set_b);
+hashset* hashset_merge_cloned(hashset* set_a, hashset* set_b);
+void hashset_free(hashset* set); \ No newline at end of file
diff --git a/archive/src/std/containers/ring_queue.c b/archive/src/std/containers/ring_queue.c
new file mode 100644
index 0000000..8bfc10b
--- /dev/null
+++ b/archive/src/std/containers/ring_queue.c
@@ -0,0 +1,68 @@
+#include "ring_queue.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "defines.h"
+
+ring_queue* ring_queue_new(size_t type_size, size_t capacity, void* memory) {
+ ring_queue* q = malloc(sizeof(ring_queue));
+ q->len = 0;
+ q->capacity = capacity;
+ q->type_size = type_size;
+ q->head = 0;
+ q->tail = -1;
+
+ if (memory) {
+ // caller owns the memory
+ q->owns_memory = false;
+ q->data = memory;
+ } else {
+ // ring queue should own the memory
+ q->owns_memory = true;
+ q->data = malloc(capacity * type_size);
+ }
+
+ return q;
+}
+
+void ring_queue_free(ring_queue* queue) {
+ if (queue) {
+ if (queue->owns_memory) {
+ free(queue->data);
+ }
+ free(queue);
+ }
+}
+
+bool ring_queue_enqueue(ring_queue* queue, const void* value) {
+ if (queue->len == queue->capacity) {
+ return false;
+ }
+
+ queue->tail = (queue->tail + 1) % queue->capacity;
+ memcpy(queue->data + (queue->tail * queue->type_size), value, queue->type_size);
+ queue->len++;
+ return true;
+}
+
+bool ring_queue_dequeue(ring_queue* queue, void* out_value) {
+ if (queue->len == 0) {
+ // queue is empty
+ return false;
+ }
+
+ memcpy(out_value, queue->data + (queue->head * queue->type_size), queue->type_size);
+ queue->head = (queue->head + 1) % queue->capacity;
+ queue->len--;
+ return true;
+}
+
+bool ring_queue_peek(const ring_queue* queue, void* out_value) {
+ if (queue->len == 0) {
+ // queue is empty
+ return false;
+ }
+
+ memcpy(out_value, queue->data + (queue->head * queue->type_size), queue->type_size);
+ return true;
+} \ No newline at end of file
diff --git a/archive/src/std/containers/ring_queue.h b/archive/src/std/containers/ring_queue.h
new file mode 100644
index 0000000..15d5da4
--- /dev/null
+++ b/archive/src/std/containers/ring_queue.h
@@ -0,0 +1,35 @@
+/**
+ * @file ring_queue.h
+ * @author your name (you@domain.com)
+ * @brief
+ * @version 0.1
+ * @date 2024-02-24
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+#pragma once
+#include "defines.h"
+
+/**
+ * @brief a fixed-size ring queue
+ */
+typedef struct ring_queue {
+ size_t len;
+ size_t capacity;
+ size_t type_size;
+ void* data;
+ bool owns_memory;
+ int32_t head;
+ int32_t tail;
+} ring_queue;
+
+ring_queue* ring_queue_new(size_t type_size, size_t capacity, void* memory_block);
+
+void ring_queue_free(ring_queue* queue);
+
+bool ring_queue_enqueue(ring_queue* queue, const void* value);
+
+bool ring_queue_dequeue(ring_queue* queue, void* out_value);
+
+bool ring_queue_peek(const ring_queue* queue, void* out_value); \ No newline at end of file
diff --git a/archive/src/std/containers/stack_array.h b/archive/src/std/containers/stack_array.h
new file mode 100644
index 0000000..d2b6bdd
--- /dev/null
+++ b/archive/src/std/containers/stack_array.h
@@ -0,0 +1,19 @@
+#pragma once
+#include <stdbool.h>
+
+// Defines "_sarray" types
+
+#define TYPED_STACK_ARRAY(T, Name, Len) \
+ typedef struct Name##_sarray { \
+ T items[ Len ]; \
+ size_t len; \
+ } Name##_sarray; \
+ Name##_sarray Name##_sarray_create() { \
+ Name##_sarray arr = { .len = 0 }; \
+ return arr; \
+ } \
+ bool Name##_sarray_push(Name##_sarray* arr, T item) { \
+ if (arr->len == Len) { return false; }\
+ arr->items[arr->len++] = item;\
+ return true;\
+ }
diff --git a/archive/src/std/mem.c b/archive/src/std/mem.c
new file mode 100644
index 0000000..1f9078b
--- /dev/null
+++ b/archive/src/std/mem.c
@@ -0,0 +1,135 @@
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "log.h"
+#include "mem.h"
+
+#ifndef DEFAULT_ALIGNMENT
+#define DEFAULT_ALIGNMENT (2 * sizeof(void*))
+#endif
+
+// --- Arena
+
+void* arena_alloc_align(arena* a, size_t size, size_t align) {
+ ptrdiff_t padding = -(uintptr_t)a->curr & (align - 1);
+ ptrdiff_t available = a->end - a->curr - padding;
+ // TRACE("Padding %td available %td", padding, available);
+ if (available < 0 || (ptrdiff_t)size > available) {
+ ERROR_EXIT("Arena ran out of memory\n");
+ }
+ void* p = a->curr + padding;
+ a->curr += padding + size;
+ return memset(p, 0, size);
+}
+void* arena_alloc(arena* a, size_t size) { return arena_alloc_align(a, size, DEFAULT_ALIGNMENT); }
+
+arena arena_create(void* backing_buffer, size_t capacity) {
+ return (arena){ .begin = backing_buffer,
+ .curr = backing_buffer,
+ .end = backing_buffer + (ptrdiff_t)capacity };
+}
+
+void arena_free_all(arena* a) {
+ a->curr = a->begin; // pop everything at once and reset to the start.
+}
+
+void arena_free_storage(arena* a) { free(a->begin); }
+
+arena_save arena_savepoint(arena* a) {
+ arena_save savept = { .arena = a, .savepoint = a->curr };
+ return savept;
+}
+
+void arena_rewind(arena_save savepoint) { savepoint.arena->curr = savepoint.savepoint; }
+
+// --- Pool
+
+void_pool void_pool_create(arena* a, const char* debug_label, u64 capacity, u64 entry_size) {
+ size_t memory_requirements = capacity * entry_size;
+ void* backing_buf = arena_alloc(a, memory_requirements);
+
+ assert(entry_size >= sizeof(void_pool_header)); // TODO: create my own assert with error message
+
+ void_pool pool = { .capacity = capacity,
+ .entry_size = entry_size,
+ .count = 0,
+ .backing_buffer = backing_buf,
+ .free_list_head = NULL,
+ .debug_label = debug_label };
+
+ void_pool_free_all(&pool);
+
+ return pool;
+}
+
+void void_pool_free_all(void_pool* pool) {
+ // set all entries to be free
+ for (u64 i = 0; i < pool->capacity; i++) {
+ void* ptr = &pool->backing_buffer[i * pool->entry_size];
+ void_pool_header* free_node =
+ (void_pool_header*)ptr; // we reuse the actual entry itself to hold the header
+ if (i == (pool->capacity - 1)) {
+ // if the last one we make its next pointer NULL indicating its full
+ free_node->next = NULL;
+ }
+ free_node->next = pool->free_list_head;
+ // now the head points to this entry
+ pool->free_list_head = free_node;
+ }
+}
+
+void* void_pool_get(void_pool* pool, u32 raw_handle) {
+ // An handle is an index into the array essentially
+ void* ptr = pool->backing_buffer + (raw_handle * pool->entry_size);
+ return ptr;
+}
+
+void* void_pool_alloc(void_pool* pool, u32* out_raw_handle) {
+ // get the next free node
+ if (pool->count == pool->capacity) {
+ WARN("Pool is full!");
+ return NULL;
+ }
+ if (pool->free_list_head == NULL) {
+ ERROR("%s Pool is full (head = null)", pool->debug_label);
+ return NULL;
+ }
+ void_pool_header* free_node = pool->free_list_head;
+
+ // What index does this become?
+ uintptr_t start = (uintptr_t)pool->backing_buffer;
+ uintptr_t cur = (uintptr_t)free_node;
+ // TRACE("%ld %ld ", start, cur);
+ assert(cur > start);
+ u32 index = (u32)((cur - start) / pool->entry_size);
+ /* printf("Index %d\n", index); */
+ if (out_raw_handle != NULL) {
+ *out_raw_handle = index;
+ }
+
+ pool->free_list_head = free_node->next;
+
+ memset(free_node, 0, pool->entry_size);
+ pool->count++;
+ return (void*)free_node;
+}
+
+void void_pool_dealloc(void_pool* pool, u32 raw_handle) {
+ // push free node back onto the free list
+ void* ptr = void_pool_get(pool, raw_handle);
+ void_pool_header* freed_node = (void_pool_header*)ptr;
+
+ freed_node->next = pool->free_list_head;
+ pool->free_list_head = freed_node;
+
+ pool->count--;
+}
+
+u32 void_pool_insert(void_pool* pool, void* item) {
+ u32 raw_handle;
+ void* item_dest = void_pool_alloc(pool, &raw_handle);
+ memcpy(item_dest, item, pool->entry_size);
+ return raw_handle;
+}
diff --git a/archive/src/std/mem.h b/archive/src/std/mem.h
new file mode 100644
index 0000000..56c1230
--- /dev/null
+++ b/archive/src/std/mem.h
@@ -0,0 +1,96 @@
+/**
+ * @file mem.h
+ * @brief Allocators, memory tracking
+ * @version 0.1
+ * @date 2024-02-24
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+#pragma once
+
+#include <stddef.h>
+#include "defines.h"
+
+typedef void* (*alloc_fn)(size_t size);
+typedef void (*free_fn)(void* ptr);
+
+typedef struct allocator_t {
+ alloc_fn alloc;
+ free_fn free;
+} allocator_t;
+
+// --- Arena
+
+// Inspired by https://nullprogram.com/blog/2023/09/27/
+typedef struct arena {
+ char* begin;
+ char* curr;
+ char* end;
+} arena;
+
+typedef struct arena_save {
+ arena* arena;
+ char* savepoint;
+} arena_save;
+
+arena arena_create(void* backing_buffer, size_t capacity);
+void* arena_alloc(arena* a, size_t size);
+void* arena_alloc_align(arena* a, size_t size, size_t align);
+void arena_free_all(arena* a);
+void arena_free_storage(arena* a);
+arena_save arena_savepoint(arena* a);
+void arena_rewind(arena_save savepoint);
+// TODO: arena_resize
+
+// --- Pool
+
+typedef struct void_pool_header void_pool_header;
+struct void_pool_header {
+ void_pool_header* next;
+};
+
+typedef struct void_pool {
+ u64 capacity;
+ u64 entry_size;
+ u64 count;
+ void* backing_buffer;
+ void_pool_header* free_list_head;
+ const char* debug_label;
+} void_pool;
+
+void_pool void_pool_create(arena* a, const char* debug_label, u64 capacity, u64 entry_size);
+void void_pool_free_all(void_pool* pool);
+bool void_pool_is_empty(void_pool* pool);
+bool void_pool_is_full(void_pool* pool);
+void* void_pool_get(void_pool* pool, u32 raw_handle);
+void* void_pool_alloc(void_pool* pool, u32* out_raw_handle);
+void void_pool_dealloc(void_pool* pool, u32 raw_handle);
+u32 void_pool_insert(void_pool* pool, void* item);
+// TODO: fn to dealloc from the pointer that was handed out
+
+// TODO: macro that lets us specialise
+
+/* typedef struct Name##_handle Name##_handle; \ */
+#define TYPED_POOL(T, Name) \
+ typedef struct Name##_pool { \
+ void_pool inner; \
+ } Name##_pool; \
+ \
+ static Name##_pool Name##_pool_create(arena* a, u64 cap, u64 entry_size) { \
+ void_pool p = void_pool_create(a, "\"" #Name "\"", cap, entry_size); \
+ return (Name##_pool){ .inner = p }; \
+ } \
+ static inline T* Name##_pool_get(Name##_pool* pool, Name##Handle handle) { \
+ return (T*)void_pool_get(&pool->inner, handle.raw); \
+ } \
+ static inline T* Name##_pool_alloc(Name##_pool* pool, Name##Handle* out_handle) { \
+ return (T*)void_pool_alloc(&pool->inner, &out_handle->raw); \
+ } \
+ static inline void Name##_pool_dealloc(Name##_pool* pool, Name##Handle handle) { \
+ void_pool_dealloc(&pool->inner, handle.raw); \
+ } \
+ static Name##Handle Name##_pool_insert(Name##_pool* pool, T* item) { \
+ u32 raw_handle = void_pool_insert(pool, item); \
+ return (Name##Handle){ .raw = raw_handle }; \
+ }
diff --git a/archive/src/std/str.c b/archive/src/std/str.c
new file mode 100644
index 0000000..89c76a0
--- /dev/null
+++ b/archive/src/std/str.c
@@ -0,0 +1,74 @@
+#include "str.h"
+#include <assert.h>
+#include <string.h>
+#include "log.h"
+#include "mem.h"
+
+Str8 Str8_create(u8* buf, size_t len) { return (Str8){ .buf = buf, .len = len }; }
+
+Str8 Str8_cstr_view(char* string) { return Str8_create((u8*)string, strlen(string)); }
+
+bool Str8_equals(Str8 a, Str8 b) {
+ if (a.len != b.len) {
+ return false;
+ }
+
+ for (size_t i = 0; i < a.len; i++) {
+ if (a.buf[i] != b.buf[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+char* Str8_to_cstr(arena* a, Str8 s) {
+ bool is_null_terminated = s.buf[s.len - 1] == 0;
+ size_t n_bytes = is_null_terminated ? s.len : s.len + 1;
+
+ u8* dest = arena_alloc(a, n_bytes);
+
+ memcpy(dest, s.buf, s.len);
+ if (is_null_terminated) {
+ dest[s.len] = '\0';
+ }
+ return (char*)dest;
+}
+
+char* Clone_cstr(arena* a, const char* s) {
+ if (s == NULL) {
+ WARN("Tried to clone a NULL char*");
+ return NULL;
+ }
+ Str8 st = Str8_cstr_view(s);
+ return Str8_to_cstr(a, st);
+}
+
+Str8 Str8_concat(arena* a, Str8 left, Str8 right) {
+ size_t n_bytes = left.len + right.len + 1;
+
+ u8* dest = arena_alloc(a, n_bytes);
+ memcpy(dest, left.buf, left.len);
+ memcpy(dest + right.len, right.buf, right.len);
+
+ dest[n_bytes - 1] = '\0';
+
+ return Str8_create(dest, n_bytes);
+}
+
+Str8 Str8_substr(Str8 s, u64 min, u64 max) {
+ assert(min >= 0);
+ assert(min < s.len);
+ assert(max >= 0);
+ assert(max <= s.len);
+ uint8_t* start = s.buf + (ptrdiff_t)min;
+ size_t new_len = max - min;
+ return (Str8){ .buf = start, .len = new_len };
+}
+
+Str8 Str8_take(Str8 s, u64 first_n) { return Str8_substr(s, 0, first_n); }
+
+Str8 Str8_drop(Str8 s, u64 last_n) { return Str8_substr(s, s.len - last_n, s.len); }
+
+Str8 Str8_skip(Str8 s, u64 n) { return Str8_substr(s, n, s.len); }
+
+Str8 Str8_chop(Str8 s, u64 n) { return Str8_substr(s, 0, s.len - n); }
diff --git a/archive/src/std/str.h b/archive/src/std/str.h
new file mode 100644
index 0000000..a29bf9a
--- /dev/null
+++ b/archive/src/std/str.h
@@ -0,0 +1,89 @@
+/**
+ * @file str.h
+ * @author your name (you@domain.com)
+ * @brief
+ * @version 0.1
+ * @date 2024-02-25
+ *
+ * @copyright Copyright (c) 2024
+ *
+ */
+#pragma once
+
+#include <ctype.h>
+
+#include "defines.h"
+#include "mem.h"
+
+/**
+ * @brief Fat pointer representing a UTF8 (TODO some APIs supporting utf8) encoded string
+ * @note when using `printf` you must use %s.*s length, string until our own modified
+ print routines are written. alternatively wrap in `cstr()` and pass to `%s`.
+ */
+typedef struct {
+ u8* buf;
+ size_t len;
+} Str8;
+
+// --- Constructors
+
+/** @brief Take a string literal and turn it into a `str8` */
+#define str8(s) \
+ (Str8) { (u8*)s, ((sizeof(s) / sizeof(*(s)) - 1)) }
+
+Str8 Str8_create(u8* buf, size_t len);
+
+// TODO: Str8_OntoArena(arena* a, Str8 s);
+
+/** @brief Return a null-terminated C string cloned onto an arena */
+char* Str8_to_cstr(arena* a, Str8 s);
+
+#define cstr(a, s) (Str8_to_cstr(a, s)) // Shorthand
+
+/** @brief Return a Str8 that references a statically allocated string.
+ `string` therefore must already be null-terminated.
+ @note The backing `string` cannot be modified. */
+Str8 Str8_cstr_view(char* string);
+
+char* Clone_cstr(arena* a, const char* s);
+
+// --- Comparisons
+
+/** @brief Compare two strings for exact equality */
+bool Str8_equals(Str8 a, Str8 b);
+
+/**
+ * @brief Compare the first `first_nchars` of each string for equality
+ * @details If either of the strings are shorter than the number only the characters up until the
+ end of the shorter string will be compared.
+ * @returns 0 if they are fully equal up until `first_nchars`, i.e they never differed, else it
+ returns the index at which the first string differed from the second string.
+*/
+size_t Str8_nequals(Str8 a, Str8 b, size_t first_nchars);
+
+bool Str8_ends_with(Str8 input_str, Str8 suffix);
+
+/// --- Subviews
+
+Str8 Str8_substr(Str8 s, u64 min, u64 max);
+/** @brief Keeps only the `first_n` chars of `s` */
+Str8 Str8_take(Str8 s, u64 first_n);
+/** @brief Keeps only the `last_n` chars of `s` */
+Str8 Str8_drop(Str8 s, u64 last_n);
+/** @brief Keeps everything after the first `n` chars of `s` */
+Str8 Str8_skip(Str8 s, u64 n);
+/** @brief Keeps everything before the last `n` chars of `s` */
+Str8 Str8_chop(Str8 s, u64 n);
+
+Str8 Str8_concat(arena* a, Str8 left, Str8 right);
+
+/// --- Misc
+
+static inline bool Str8_is_null_term(Str8 a) {
+ return a.buf[a.len] == 0; // This doesn't seem safe. YOLO
+}
+
+// TODO: move or delete this and replace with handling using our internal type
+static void skip_space(char* p) {
+ while (isspace((unsigned char)*p)) ++p;
+}
diff --git a/archive/src/std/utils.h b/archive/src/std/utils.h
new file mode 100644
index 0000000..c9827a3
--- /dev/null
+++ b/archive/src/std/utils.h
@@ -0,0 +1,4 @@
+#pragma once
+#include <stdbool.h>
+
+const char* bool_str(bool input) { return input ? "True" : "False"; } \ No newline at end of file