summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAryadev Chavali <aryadev@aryadevchavali.com>2024-11-01 07:48:19 +0000
committerAryadev Chavali <aryadev@aryadevchavali.com>2024-11-01 07:48:19 +0000
commit46f62c5d2c789fd6122daa02efcc4dff10f6a2b5 (patch)
tree70d64df5f43e6338a071299b3a7465d30c51b260
parentf0f1e06e1ee21dc5260da7d40f5a029bd71596aa (diff)
downloadprick-46f62c5d2c789fd6122daa02efcc4dff10f6a2b5.tar.gz
prick-46f62c5d2c789fd6122daa02efcc4dff10f6a2b5.tar.bz2
prick-46f62c5d2c789fd6122daa02efcc4dff10f6a2b5.zip
Simple arena implementation using singly linked list
Manages individual allocations via a bump allocator (region), with unfit allocations triggering a new region allocation that is added to a linked list. Any new region allocations will always be oversized for the initially requested size, to amortize the cost of future allocations.
-rw-r--r--arena.h164
1 files changed, 164 insertions, 0 deletions
diff --git a/arena.h b/arena.h
new file mode 100644
index 0000000..cedab26
--- /dev/null
+++ b/arena.h
@@ -0,0 +1,164 @@
+/* Copyright (C) 2024 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 Unlicense
+ * for details.
+
+ * You may distribute and modify this code under the terms of the
+ * Unlicense, which you should have received a copy of along with this
+ * program. If not, please go to <https://unlicense.org/>.
+
+ * Created: 2024-11-01
+ * Author: Aryadev Chavali
+ * Description: Arena allocator
+ */
+
+#ifndef ARENA_H
+#define ARENA_H
+
+#include <stdint.h>
+
+struct Region;
+typedef struct
+{
+ struct Region *beg, *end;
+} arena_t;
+
+uint8_t *arena_alloc(arena_t *arena, uint32_t size);
+uint8_t *arena_realloc(arena_t *arena, uint8_t *pointer, uint32_t old_size,
+ uint32_t new_size);
+void arena_reset(arena_t *arena);
+void arena_free(arena_t *arena);
+
+#ifdef ARENA_IMPL
+
+#include <malloc.h>
+#include <string.h>
+
+#ifndef REGION_DEFAULT_SIZE
+#define REGION_DEFAULT_SIZE 512
+#endif
+
+#ifndef REGION_CAPACITY_MULT
+#define REGION_CAPACITY_MULT 2
+#endif
+
+#define MAX(A, B) ((A) > (B) ? (A) : (B))
+
+/**
+ @brief A single block of memory to be used by an arena.
+
+ @details Blocks of memory arranged in a singly linked list.
+ Each individual node is a bump allocator.
+*/
+typedef struct Region
+{
+ struct Region *next;
+ uint32_t size, capacity;
+ uint8_t bytes[];
+} region_t;
+
+region_t *region_make(uint32_t capacity, region_t *next)
+{
+ capacity = MAX(capacity, REGION_DEFAULT_SIZE);
+ region_t *region = calloc(1, sizeof(*region) + capacity);
+ region->next = next;
+ region->size = 0;
+ region->capacity = capacity;
+ return region;
+}
+
+uint8_t *region_alloc_rec(region_t *region, uint32_t capacity)
+{
+ if (!region)
+ return NULL;
+
+ for (; region->next && region->capacity - region->size < capacity;
+ region = region->next)
+ continue;
+
+ if (region->capacity - region->size < capacity)
+ {
+ // no region->next, so make a new region that can fit the capacity required.
+ region_t *new = region_make(capacity * REGION_CAPACITY_MULT, NULL);
+ region->next = new;
+ region = new;
+ }
+
+ uint8_t *start = region->bytes + region->size;
+ region->size += capacity;
+ return start;
+}
+
+void region_delete(region_t *region)
+{
+ while (region)
+ {
+ region_t *next = region->next;
+ free(region);
+ region = next;
+ }
+}
+
+uint8_t *arena_alloc(arena_t *arena, uint32_t size)
+{
+ if (!arena->beg)
+ {
+ arena->beg = region_make(size, NULL);
+ arena->end = arena->beg;
+ }
+ uint8_t *start = region_alloc_rec(arena->beg, size);
+ // if we've attached a new region, end needs to be at that region
+ if (arena->end->next)
+ arena->end = arena->end->next;
+ return start;
+}
+
+uint8_t *arena_realloc(arena_t *arena, uint8_t *pointer, uint32_t old_size,
+ uint32_t new_size)
+{
+ // Firstly find the region the pointer exists in
+ region_t *prev, *reg;
+ for (prev = NULL, reg = arena->beg;
+ reg &&
+ !(reg->bytes <= pointer && reg->bytes + reg->size >= pointer + old_size);
+ prev = reg, reg = reg->next)
+ continue;
+
+ if (!reg)
+ // pointer isn't allocated in the arena
+ return NULL;
+
+ uint8_t *new_ptr = NULL;
+ if (old_size == reg->size && reg->capacity == reg->size)
+ {
+ // Completely filled region, may as well reallocate
+ region_t *new_reg = region_make(new_size * REGION_CAPACITY_MULT, reg->next);
+ // Chain this new region in place
+ if (prev)
+ prev->next = new_reg;
+ if (reg == arena->end)
+ arena->end = new_reg;
+ free(reg);
+ new_ptr = new_reg->bytes;
+ new_reg->size += new_size;
+ }
+ else
+ {
+ // Allocate a new portion of memory on the arena
+ new_ptr = arena_alloc(arena, new_size);
+ }
+ memcpy(new_ptr, pointer, old_size);
+ return new_ptr;
+}
+
+void arena_free(arena_t *arena)
+{
+ region_delete(arena->beg);
+ memset(arena, 0, sizeof(*arena));
+}
+
+#endif
+
+#endif