summary refs log tree commit diff
path: root/arena.h
diff options
context:
space:
mode:
authorwrmr2025-06-23 00:08:13 -0400
committerwrmr2025-06-23 00:08:13 -0400
commit77dbfeab0b777f7e063c7d10d91572f423c5dd18 (patch)
treeec0ca40e2e28dbb82be42467785642f1464057ca /arena.h
Initial commit
Diffstat (limited to 'arena.h')
-rw-r--r--arena.h149
1 files changed, 149 insertions, 0 deletions
diff --git a/arena.h b/arena.h
new file mode 100644
index 0000000..045cde0
--- /dev/null
+++ b/arena.h
@@ -0,0 +1,149 @@
+#ifndef ARENA_H
+#define ARENA_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+
+typedef struct ArenaPg {
+	struct ArenaPg *prev, *next;
+	char *beg, *end;
+	char data[];
+} ArenaPg;
+
+typedef struct {
+	ArenaPg *pg;
+	char *beg;
+} ArenaMark;
+
+typedef struct {
+	ArenaPg *cur, *tail;
+} Arena;
+
+#define new(a, t)\
+	(t*)arena_zeroed(arena_alloc(a, sizeof(t), _Alignof(t)), sizeof(t))
+
+#define new_arr(a, t, n)\
+	arena_alloc(a, sizeof(t) * n, _Alignof(t))
+
+#define resize(a, p, old, new)\
+	arena_realloc(a, p, (old) * sizeof(*(p)), (new) * sizeof(*(p)),\
+			_Alignof(__typeof__(*(p))))
+
+void arena_free(Arena *a);
+
+void arena_save(Arena *a, ArenaMark *m);
+void arena_load(Arena *a, ArenaMark *m);
+
+void arena_reset(Arena *a);
+void arena_reserve(Arena *a, ptrdiff_t n);
+
+void *arena_alloc(Arena *a, ptrdiff_t n, ptrdiff_t align);
+void *arena_realloc(Arena *a, void *ptr, ptrdiff_t old, ptrdiff_t new, ptrdiff_t align);
+void *arena_zeroed(void *p, size_t n);
+
+#define ARENA_BACKEND_MALLOC 0
+#define ARENA_BACKEND_MMAP 1
+
+#ifndef ARENA_BACKEND
+#if defined(__linux__)
+#	define ARENA_BACKEND ARENA_BACKEND_MMAP
+#else
+#	define ARENA_BACKEND ARENA_BACKEND_MALLOC
+#endif
+#endif
+
+#ifdef ARENA_IMPL
+
+#include <stdio.h>
+#include <stdlib.h>
+static void arena_pg_alloc_fail(void) {
+	fprintf(stderr, "failed to allocate arena page\n");
+	abort();
+}
+
+#if ARENA_BACKEND == ARENA_BACKEND_MMAP
+#include <sys/mman.h>
+#include <unistd.h>
+#define ARENA_PG_SIZE sysconf(_SC_PAGESIZE)
+static inline void *arena_pg_alloc(ptrdiff_t n) {
+	void *p = mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	return p == MAP_FAILED ? NULL : p;
+}
+static inline void arena_pg_free(void *ptr, ptrdiff_t n) { munmap(ptr, n); }
+#elif ARENA_BACKEND == ARENA_BACKEND_MALLOC
+#define ARENA_PG_SIZE 8192
+static inline void *arena_pg_alloc(ptrdiff_t n) { return malloc(n); }
+static inline void arena_pg_free(void *ptr, ptrdiff_t n) { free(ptr); (void)n; }
+#endif
+
+void arena_free(Arena *a) {
+	while (a->tail) {
+		a->cur = a->tail->prev;
+		arena_pg_free(a->tail, (uintptr_t)(a->tail->end - (char*)a->tail));
+		a->tail = a->cur;
+	}
+}
+
+void arena_reserve(Arena *a, ptrdiff_t n) {
+	while (a->cur && a->cur->beg + n >= a->cur->end) a->cur = a->cur->next;
+	if (a->cur) return;
+	ptrdiff_t cap = n + sizeof(ArenaPg);
+	cap += (uintptr_t)-cap & (ARENA_PG_SIZE - 1);
+	ArenaPg *p = arena_pg_alloc(cap);
+	if (!p) arena_pg_alloc_fail();
+	p->next = NULL;
+	p->prev = a->tail;
+	p->beg = p->data;
+	p->end = (char*)p + cap;
+	if (a->tail) a->tail->next = p;
+	a->cur = (a->tail = p);
+}
+
+void *arena_alloc(Arena *a, ptrdiff_t n, ptrdiff_t align) {
+	arena_reserve(a, n + (align - 1));
+	char *ptr = a->cur->beg + (-(uintptr_t)a->cur->beg & (align - 1));
+	a->cur->beg = ptr + n;
+	return ptr;
+}
+
+void *arena_realloc(Arena *a, void *ptr, ptrdiff_t old, ptrdiff_t new, ptrdiff_t align) {
+	if (a->cur && ptr == a->cur->beg - old && (char*)ptr + new < a->cur->end) {
+		a->cur->beg += new - old;
+		return ptr;
+	} else {
+		void *p = arena_alloc(a, new, align);
+		if (ptr) memcpy(p, ptr, old);
+		return p;
+	}
+}
+
+void *arena_zeroed(void *p, size_t n) {
+	memset(p, 0, n);
+	return p;
+}
+
+void arena_reset(Arena *a) {
+	if (!a->cur) return;
+	while (a->cur->prev) {
+		a->cur->beg = a->cur->data;
+		a->cur = a->cur->prev;
+	}
+	a->cur->beg = a->cur->data;
+}
+
+void arena_save(Arena *a, ArenaMark *m) {
+	m->pg = a->cur;
+	if (a->cur) m->beg = a->cur->beg;
+}
+
+void arena_load(Arena *a, ArenaMark *m) {
+	while (a->cur && a->cur != m->pg) {
+		a->cur->beg = a->cur->data;
+		a->cur = a->cur->prev;
+	}
+	if (a->cur) a->cur->beg = m->beg;
+}
+
+#endif
+#endif