aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/arena.c203
-rw-r--r--lib/arena.h51
-rw-r--r--lib/sv.c158
-rw-r--r--lib/sv.h70
-rw-r--r--lib/vec.c38
-rw-r--r--lib/vec.h30
6 files changed, 550 insertions, 0 deletions
diff --git a/lib/arena.c b/lib/arena.c
new file mode 100644
index 0000000..aa2d775
--- /dev/null
+++ b/lib/arena.c
@@ -0,0 +1,203 @@
+/* Copyright (C) 2025 Aryadev Chavali
+
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
+ * details.
+
+ * You may distribute and modify this code under the terms of the GNU General
+ * Public License Version 2, which you should have received a copy of along with
+ * this program. If not, please go to <https://www.gnu.org/licenses/>.
+
+ * Created: 2025-04-05
+ * Description: Implementations for memory models.
+ */
+
+#include <lib/arena.h>
+
+#include <malloc.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+page_t *page_create(u64 size)
+{
+ size = MAX(size, PAGE_DEFAULT_SIZE);
+ page_t *page = calloc(1, sizeof(*page) + size);
+ page->next = NULL;
+ page->size = 0;
+ page->capacity = size;
+ return page;
+}
+
+void page_resize(page_t **page, u64 size)
+{
+ if (!page)
+ return;
+ else if (!(*page))
+ *page = page_create(size);
+ else if (page[0]->capacity < size)
+ {
+ page[0]->capacity = MAX(size, page[0]->capacity * 1.5);
+ page[0] = realloc(page[0], sizeof(*page[0]) + page[0]->capacity);
+ }
+}
+
+i64 page_append(page_t *page, void *data, u64 size)
+{
+ if (!page || page->size + size >= page->capacity)
+ return -1;
+ if (data)
+ memcpy(page->data + page->size, data, size);
+ u64 ptr = page->size;
+ page->size += size;
+ return ptr;
+}
+
+u64 page_rappend(page_t **page, void *data, u64 size)
+{
+ page_resize(page, page[0]->size + size);
+ if (data)
+ memcpy(page[0]->data + page[0]->size, data, size);
+ u64 ptr = page[0]->size;
+ page[0]->size += size;
+ return ptr;
+}
+
+void *arena_alloc(arena_t *arena, u64 size)
+{
+ if (!arena)
+ return NULL;
+ else if (!arena->start)
+ {
+ arena->start = page_create(1);
+ arena->end = arena->start;
+ }
+
+ page_t *best_fit;
+ for (best_fit = arena->start; best_fit; best_fit = best_fit->next)
+ if (best_fit->size + size + MEMORY_ALIGNMENT < best_fit->capacity)
+ break;
+
+ if (!best_fit)
+ {
+ best_fit = page_create(size);
+ arena->end->next = best_fit;
+ arena->end = best_fit;
+ }
+
+ // NOTE: Fsanitize has a hissy fit if we don't align memory "correctly"
+ u64 offset = 0;
+
+ if (size % MEMORY_ALIGNMENT == 0)
+ {
+ u64 div = (((u64)(best_fit->data + best_fit->size)) % MEMORY_ALIGNMENT);
+ if (div != 0)
+ offset = MEMORY_ALIGNMENT - div;
+ }
+
+ void *start = best_fit->data + best_fit->size + offset;
+
+ best_fit->size += size + offset;
+
+ return start;
+}
+
+void *arena_realloc(arena_t *arena, void *ptr, u64 oldsize, u64 newsize)
+{
+ if (!ptr)
+ return arena_alloc(arena, newsize);
+ else if (!arena || !arena->start)
+ return NULL;
+ else if (newsize <= oldsize)
+ // No need to change anything.
+ return ptr;
+
+ bool copy_into_new = true;
+ void *start = NULL;
+ page_t *old_page = NULL, *best_fit = NULL;
+
+ for (page_t *page = arena->start; page; page = page->next)
+ {
+ if (!best_fit && page->size + newsize < page->capacity)
+ best_fit = page;
+ if (page->data <= (u8 *)ptr &&
+ (u8 *)(ptr) + oldsize <= page->data + page->capacity)
+ old_page = page;
+ }
+
+ // If the old page exists, ptr is the latest allocation in it, and it has
+ // enough space to contain the new size, just resize and return.
+ if (old_page && old_page->data + old_page->size - oldsize == ptr &&
+ old_page->size - oldsize + newsize < old_page->capacity)
+ {
+ start = ptr;
+ old_page->size += newsize - oldsize;
+ copy_into_new = false;
+ }
+ else
+ {
+ if (!old_page)
+ copy_into_new = false;
+ if (!best_fit)
+ {
+ best_fit = page_create(newsize);
+ arena->end->next = best_fit;
+ arena->end = best_fit;
+ }
+
+ start = best_fit->data + best_fit->size;
+ best_fit->size += newsize;
+ }
+
+ if (copy_into_new)
+ {
+ memcpy(start, ptr, oldsize);
+ memset(start + oldsize, 0, newsize - oldsize);
+ }
+
+ return start;
+}
+
+void arena_attach(arena_t *arena, page_t *page)
+{
+ if (!arena || !page)
+ return;
+ else if (!arena->start || !arena->end)
+ {
+ arena->start = page;
+ arena->end = page;
+ }
+ else
+ {
+ page->next = arena->start;
+ arena->start = page;
+ }
+}
+
+void arena_reset(arena_t *arena)
+{
+ if (!arena || !arena->start)
+ return;
+
+ for (page_t *cur = arena->start; cur; cur = cur->next)
+ {
+ if (cur->size == 0)
+ continue;
+ cur->size = 0;
+ memset(cur->data, 0, cur->capacity);
+ }
+}
+
+void arena_cleanup(arena_t *arena)
+{
+ if (!arena || !arena->start)
+ return;
+
+ for (page_t *cur = arena->start, *next = NULL; cur;
+ next = cur->next, free(cur), cur = next)
+ continue;
+
+ memset(arena, 0, sizeof(*arena));
+}
diff --git a/lib/arena.h b/lib/arena.h
new file mode 100644
index 0000000..3b0724f
--- /dev/null
+++ b/lib/arena.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2025 Aryadev Chavali
+
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
+ * details.
+
+ * You may distribute and modify this code under the terms of the GNU General
+ * Public License Version 2, which you should have received a copy of along with
+ * this program. If not, please go to <https://www.gnu.org/licenses/>.
+
+ * Created: 2025-04-05
+ * Description: Arena
+ */
+
+#ifndef ARENA_H
+#define ARENA_H
+
+#include <base.h>
+#include <stdio.h>
+
+#define PAGE_DEFAULT_SIZE 1024
+#define MEMORY_ALIGNMENT 8
+
+typedef struct Page
+{
+ struct Page *next;
+ u64 size, capacity;
+ u8 data[];
+} page_t;
+
+page_t *page_create(u64 size);
+void page_resize(page_t **page, u64 newsize);
+// Append - fail (by returning <0) if not enough space.
+i64 page_append(page_t *page, void *data, u64 size);
+// Append - will resize if necessary
+u64 page_rappend(page_t **page, void *data, u64 size);
+
+typedef struct Aren
+{
+ page_t *start, *end;
+} arena_t;
+
+void *arena_alloc(arena_t *arena, u64 size);
+void *arena_realloc(arena_t *arena, void *ptr, u64 oldsize, u64 newsize);
+void *arena_copy(arena_t *arena, void *ptr, u64 size);
+void arena_attach(arena_t *arena, page_t *page);
+void arena_reset(arena_t *arena);
+void arena_cleanup(arena_t *arena);
+
+#endif
diff --git a/lib/sv.c b/lib/sv.c
new file mode 100644
index 0000000..b2425b8
--- /dev/null
+++ b/lib/sv.c
@@ -0,0 +1,158 @@
+/* Copyright (C) 2025 Aryadev Chavali
+
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
+ * details.
+
+ * You may distribute and modify this code under the terms of the GNU General
+ * Public License Version 2, which you should have received a copy of along with
+ * this program. If not, please go to <https://www.gnu.org/licenses/>.
+
+ * Created: 2025-04-14
+ * Description: String View implementation
+ */
+
+#include <lib/sv.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+sv_t sv_make(arena_t *allocator, const char *data, u64 size)
+{
+ sv_t s = {0};
+ if (data)
+ s = sv_append(allocator, s, data, size);
+ else
+ {
+ s.data = arena_alloc(allocator, size);
+ s.size = size;
+ }
+ return s;
+}
+
+sv_t sv_copy(arena_t *allocator, sv_t sv)
+{
+ return sv_make(allocator, sv.data, sv.size);
+}
+
+sv_t sv_substr(sv_t sv, u64 index, u64 size)
+{
+ sv_t newsv = {0};
+ if (index + size > sv.size)
+ return newsv;
+ newsv.data = sv.data + index;
+ newsv.size = size;
+ return newsv;
+}
+
+sv_t sv_cut(sv_t sv, u64 index)
+{
+ return sv_substr(sv, index, sv.size < index ? 0 : sv.size - index);
+}
+
+sv_t sv_chop(sv_t sv, u64 size)
+{
+ return sv_substr(sv, 0, size);
+}
+
+sv_t sv_concat(arena_t *allocator, sv_t a, sv_t b)
+{
+ sv_t c = sv_make(allocator, a.data, a.size + b.size);
+ memcpy(c.data + a.size, b.data, b.size);
+ return c;
+}
+
+sv_t sv_append(arena_t *allocator, sv_t sv, const char *data, u64 size)
+{
+ if (!allocator)
+ return (sv_t){0};
+
+ sv_t newsv = {0};
+ newsv.size = sv.size + size;
+ newsv.data = arena_realloc(allocator, sv.data, sv.size, newsv.size);
+ if (data)
+ memcpy(newsv.data + sv.size, data, size);
+ return newsv;
+}
+
+sv_t sv_prepend(arena_t *allocator, sv_t sv, const char *data, u64 size)
+{
+ if (!allocator)
+ return (sv_t){0};
+
+ // TODO: Can we make this cheaper to do?
+ sv_t newsv = sv_make(allocator, NULL, size + sv.size);
+ // Copy over `data` to the left side
+ memcpy(newsv.data, data, size);
+ // Copy old string to the right side
+ if (sv.data)
+ memcpy(newsv.data + size, sv.data, sv.size);
+
+ return newsv;
+}
+
+sv_t sv_fmt(arena_t *allocator, char *fmt, ...)
+{
+ if (!allocator)
+ return (sv_t){0};
+
+ va_list ap_1, ap_2;
+ va_start(ap_1, fmt);
+
+ va_copy(ap_2, ap_1);
+ u64 size = vsnprintf(NULL, 0, fmt, ap_2);
+ va_end(ap_2);
+
+ sv_t sv = sv_make(allocator, NULL, size);
+ vsprintf(sv.data, fmt, ap_1);
+ va_end(ap_1);
+
+ return sv;
+}
+
+i64 sv_find_substr(const sv_t sv, const sv_t substr)
+{
+ if (substr.size == 0)
+ return 0;
+ else if (sv.size < substr.size)
+ return -1;
+ else if (sv.size == substr.size)
+ return strncmp(sv.data, substr.data, sv.size) == 0 ? 0 : -1;
+
+ for (u64 i = 0; i < (sv.size - substr.size); ++i)
+ if (strncmp(sv.data + i, substr.data, substr.size) == 0)
+ return i;
+ return -1;
+}
+
+i64 sv_find_subcstr(const sv_t sv, const char *substr, u64 size)
+{
+ return sv_find_substr(sv, SV((char *)substr, size));
+}
+
+i64 sv_find_any(const sv_t sv, const char *bag)
+{
+ for (u64 i = 0; i < sv.size; ++i)
+ if (strchr(bag, sv.data[i]))
+ return i;
+ return -1;
+}
+
+u64 sv_while(const sv_t sv, bool (*pred)(char))
+{
+ u64 i;
+ for (i = 0; i < sv.size && pred(sv.data[i]); ++i)
+ continue;
+ return i;
+}
+
+u64 sv_till(const sv_t sv, bool (*pred)(char))
+{
+ u64 i;
+ for (i = 0; i < sv.size && !pred(sv.data[i]); ++i)
+ continue;
+ return i;
+}
diff --git a/lib/sv.h b/lib/sv.h
new file mode 100644
index 0000000..3293b1c
--- /dev/null
+++ b/lib/sv.h
@@ -0,0 +1,70 @@
+/* Copyright (C) 2025 Aryadev Chavali
+
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
+ * details.
+
+ * You may distribute and modify this code under the terms of the GNU General
+ * Public License Version 2, which you should have received a copy of along with
+ * this program. If not, please go to <https://www.gnu.org/licenses/>.
+
+ * Created: 2025-04-14
+ * Description: String Views
+ */
+
+#ifndef SV_H
+#define SV_H
+
+#include <lib/arena.h>
+
+typedef struct SV
+{
+ u64 size;
+ char *data;
+} sv_t;
+
+#define SV(DATA, SIZE) ((const sv_t){.size = (SIZE), .data = (DATA)})
+#define SV_FMT(SV) (int)(SV).size, (SV).data
+#define PR_SV "%.*s"
+
+sv_t sv_make(arena_t *allocator, const char *data, u64 size);
+sv_t sv_copy(arena_t *allocator, sv_t sv);
+sv_t sv_append(arena_t *allocator, sv_t sv, const char *data, u64 size);
+sv_t sv_prepend(arena_t *allocator, sv_t sv, const char *data, u64 size);
+
+/**
+ * @brief Concatenate two string views, returning that concatenation.
+
+ * Allocates memory.
+ */
+sv_t sv_concat(arena_t *allocator, sv_t a, sv_t b);
+
+/**
+ * @brief Allocates a string view with the given `printf` format.
+ */
+sv_t sv_fmt(arena_t *allocator, char *fmt, ...);
+
+/**
+ * @brief Constructs a new string view at a different offset. Does not allocate
+ * new memory.
+ */
+sv_t sv_substr(sv_t sv, u64 index, u64 size);
+/**
+ * @brief Return a string view INDEX characters ahead (i.e. cut the string from
+ * the left).
+ */
+sv_t sv_cut(sv_t sv, u64 index);
+/**
+ * @brief Return a string view with SIZE (i.e. chopping the string from the
+ * right)
+ */
+sv_t sv_chop(sv_t sv, u64 size);
+
+i64 sv_find_substr(const sv_t sv, const sv_t substr);
+i64 sv_find_subcstr(const sv_t sv, const char *substr, u64 size);
+i64 sv_find_any(const sv_t sv, const char *bag);
+u64 sv_while(const sv_t sv, bool (*pred)(char));
+u64 sv_till(const sv_t sv, bool (*pred)(char));
+
+#endif
diff --git a/lib/vec.c b/lib/vec.c
new file mode 100644
index 0000000..551f679
--- /dev/null
+++ b/lib/vec.c
@@ -0,0 +1,38 @@
+/* Copyright (C) 2025 Aryadev Chavali
+
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
+ * details.
+
+ * You may distribute and modify this code under the terms of the GNU General
+ * Public License Version 2, which you should have received a copy of along with
+ * this program. If not, please go to <https://www.gnu.org/licenses/>.
+
+ * Created: 2025-04-15
+ * Description: Vector implementation
+ */
+
+#include <lib/vec.h>
+
+#include <malloc.h>
+#include <string.h>
+
+void vec_reserve(arena_t *allocator, vec_t *vec, u32 size)
+{
+ if (vec->cap - vec->size < size)
+ {
+ u32 old_cap = vec->cap;
+ vec->cap = MAX(vec->cap * 1.5, vec->size + size);
+ vec->data = arena_realloc(allocator, vec->data, old_cap, vec->cap);
+ }
+}
+
+u32 vec_append(arena_t *allocator, vec_t *vec, const void *data, u32 size)
+{
+ vec_reserve(allocator, vec, size);
+ memcpy(vec->data + vec->size, data, size);
+ u32 ptr = vec->size;
+ vec->size += size;
+ return ptr;
+}
diff --git a/lib/vec.h b/lib/vec.h
new file mode 100644
index 0000000..20a3523
--- /dev/null
+++ b/lib/vec.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2025 Aryadev Chavali
+
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for
+ * details.
+
+ * You may distribute and modify this code under the terms of the GNU General
+ * Public License Version 2, which you should have received a copy of along with
+ * this program. If not, please go to <https://www.gnu.org/licenses/>.
+
+ * Created: 2025-04-15
+ * Description: Vectors (dynamic sized arrays)
+ */
+
+#ifndef VEC_H
+#define VEC_H
+
+#include <lib/arena.h>
+
+typedef struct
+{
+ u32 size, cap;
+ u8 *data;
+} vec_t;
+
+void vec_reserve(arena_t *alloactor, vec_t *vec, u32 size);
+u32 vec_append(arena_t *allocator, vec_t *vec, const void *data, u32 size);
+
+#endif