aboutsummaryrefslogtreecommitdiff
path: root/src/base/mem/arena.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/base/mem/arena.c')
-rw-r--r--src/base/mem/arena.c119
1 files changed, 119 insertions, 0 deletions
diff --git a/src/base/mem/arena.c b/src/base/mem/arena.c
new file mode 100644
index 0000000..b2ce044
--- /dev/null
+++ b/src/base/mem/arena.c
@@ -0,0 +1,119 @@
+#include "internal.h"
+
+#define ARENA_ALIGN 8
+#define ARENA_BLOCK_SIZE 1024 * 1024
+
+#define ALIGN_DOWN(n, a) ((n) & ~((a)-1))
+#define ALIGN_UP(n, a) ALIGN_DOWN((n) + (a)-1, (a))
+#define ALIGN_DOWN_PTR(p, a) ((void*)ALIGN_DOWN((uintptr)(p), (a)))
+#define ALIGN_UP_PTR(p, a) ((void*)ALIGN_UP((uintptr)(p), (a)))
+
+struct Block
+{
+ struct Block *next;
+ byte buf[];
+};
+
+struct mem·Arena
+{
+ void *heap;
+ mem·Allocator mem;
+
+ byte *off;
+ byte *end;
+ struct Block *curr;
+ struct Block first;
+};
+
+static
+void*
+·arenaalloc(void *heap, uint n, ulong size)
+{
+ return mem·arenaalloc(heap, n, size);
+}
+
+static
+void
+·arenafree(void *heap, void *ptr)
+{
+ /* no-op */
+}
+
+mem·Allocator mem·ArenaAllocator = {
+ .alloc = ·arenaalloc,
+ .free = ·arenafree,
+};
+
+
+static
+void
+grow(mem·Arena *a, vlong min)
+{
+ uintptr size;
+ struct Block *blk;
+
+ size = ALIGN_UP(MAX(min, ARENA_BLOCK_SIZE), ARENA_ALIGN);
+ blk = a->mem.alloc(a->heap, 1, sizeof(*blk) + size);
+ a->off = blk->buf;
+ a->end = a->off + size;
+
+ assert(a->curr->next == nil);
+ assert(a->off == ALIGN_DOWN_PTR(a->off, ARENA_ALIGN));
+
+ a->curr->next = blk;
+ a->curr = blk;
+}
+
+mem·Arena*
+mem·makearena(mem·Allocator from, void *impl)
+{
+ mem·Arena *a = from.alloc(impl, 1, sizeof(*a) + ARENA_BLOCK_SIZE);
+ a->mem = from;
+ a->heap = impl;
+ a->off = a->first.buf;
+ a->end = a->first.buf + ARENA_BLOCK_SIZE;
+ a->curr = &a->first;
+ a->first.next = nil;
+
+ return a;
+}
+
+void
+mem·freearena(mem·Arena *a)
+{
+ struct Block *it, *next;
+
+ it = a->first.next;
+ while (it != nil) {
+ next = it->next;
+ a->mem.free(a->heap, it);
+ it = next;
+ }
+
+ a->mem.free(a->heap, a);
+}
+
+void*
+mem·arenaalloc(mem·Arena *a, uint n, ulong size)
+{
+ if(!n) {
+ return nil;
+ }
+
+ void *ptr;
+ // TODO(nnoll): check for overflow
+ size = n * size;
+
+ if (size > (ulong)(a->end - a->off)) {
+ grow(a, size);
+ assert(size <= (uintptr)(a->end - a->off));
+ }
+
+ ptr = a->off;
+ a->off = ALIGN_UP_PTR(a->off + size, ARENA_ALIGN);
+
+ assert(a->off <= a->end);
+ assert(ptr == ALIGN_DOWN_PTR(ptr, ARENA_ALIGN));
+
+ return ptr;
+}