solidc
Robust collection of general-purpose cross-platform C libraries and data structures designed for rapid and safe development in C
Loading...
Searching...
No Matches
arena.c
1#include "arena.h"
2#include "aligned_alloc.h"
3#include "macros.h"
4
5#include <stdlib.h>
6#include <string.h>
7
8#if defined(_WIN32)
9#include <windows.h>
10#else
11#include <sys/mman.h>
12#include <unistd.h>
13#endif
14
15#define STATIC_BUFFER_SIZE (1024 * 1024)
16
17static alignas(64) THREAD_LOCAL char static_buffer[STATIC_BUFFER_SIZE];
18static THREAD_LOCAL bool static_buffer_in_use = false;
19
20static ARENA_INLINE size_t get_page_size(void) {
21#if defined(_WIN32)
22 SYSTEM_INFO si;
23 GetSystemInfo(&si);
24 return si.dwPageSize;
25#else
26 return (size_t)sysconf(_SC_PAGESIZE);
27#endif
28}
29
30void arena_init(Arena* restrict a, void* restrict buf, size_t size) {
31 memset(a, 0, sizeof(Arena));
32 a->page_size = get_page_size();
33 a->heap_allocated = false;
34
35 a->first_block.base = (char*)buf;
36 a->first_block.end = (char*)buf + size;
37 a->first_block.is_static = true;
38 a->first_block.next = NULL;
39
40 a->head = &a->first_block;
41 a->current_block = a->head;
42 a->curr = a->head->base;
43 a->end = a->head->end;
44 a->total_committed = size;
45}
46
47Arena* arena_create(size_t reserve_size) {
48 Arena* a = (Arena*)aligned_alloc_xp(64, sizeof(Arena));
49 if (!a) return NULL;
50
51 if (!static_buffer_in_use && reserve_size <= STATIC_BUFFER_SIZE) {
52 arena_init(a, static_buffer, STATIC_BUFFER_SIZE);
53 static_buffer_in_use = true;
54 } else {
55 size_t initial_size = reserve_size > 0 ? reserve_size : ARENA_MIN_BLOCK_SIZE;
56 initial_size = (initial_size + get_page_size() - 1) & ~(get_page_size() - 1);
57
58 char* buf = (char*)aligned_alloc_xp(64, initial_size);
59 if (!buf) {
60 aligned_free_xp(a);
61 return NULL;
62 }
63 arena_init(a, buf, initial_size);
64 a->first_block.is_static = false; // We own this buffer; arena_destroy must free it.
65 }
66
67 a->heap_allocated = true;
68 return a;
69}
70
71void* _arena_alloc_slow(Arena* restrict a, size_t size, size_t alignment) {
72 // Try the next cached block before allocating a new one.
73 ArenaBlock* next = a->current_block->next;
74 if (next) {
75 uintptr_t aligned = ((uintptr_t)next->base + alignment - 1) & ~(alignment - 1);
76 if (aligned + size <= (uintptr_t)next->end) {
77 a->current_block = next;
78 a->curr = (char*)(aligned + size);
79 a->end = next->end;
80 return (void*)aligned;
81 }
82 }
83
84 // Allocate a new block. The ArenaBlock header lives at the start of the
85 // allocation itself, eliminating the separate malloc() call.
86 size_t current_size = (size_t)(a->current_block->end - a->current_block->base);
87 size_t needed = sizeof(ArenaBlock) + alignment + size;
88 size_t next_size = current_size * 2;
89
90 if (next_size < needed) next_size = needed;
91 if (next_size < ARENA_MIN_BLOCK_SIZE) next_size = ARENA_MIN_BLOCK_SIZE;
92 next_size = (next_size + a->page_size - 1) & ~(a->page_size - 1);
93
94 // Single allocation: header at [ptr], usable memory at [ptr + sizeof(ArenaBlock)].
95 char* ptr = (char*)aligned_alloc_xp(64, next_size);
96 if (!ptr) return NULL;
97
98 ArenaBlock* block = (ArenaBlock*)ptr;
99 block->base = (char*)(((uintptr_t)(ptr + sizeof(ArenaBlock)) + alignment - 1) & ~(alignment - 1));
100 block->end = ptr + next_size;
101 block->is_static = false;
102 block->next = a->current_block->next;
103
104 a->current_block->next = block;
105 a->current_block = block;
106 a->total_committed += next_size;
107
108 uintptr_t aligned = ((uintptr_t)block->base + alignment - 1) & ~(alignment - 1);
109 a->curr = (char*)(aligned + size);
110 a->end = block->end;
111
112 return (void*)aligned;
113}
114
115void arena_destroy(Arena* restrict a) {
116 if (!a) return;
117
118 ArenaBlock* block = a->head;
119
120 // Free the first block's buffer if we heap-allocated it.
121 if (block && !block->is_static) aligned_free_xp(block->base);
122
123 // Walk overflow blocks. Each was allocated as a single slab where the
124 // ArenaBlock header lives at the start of the pointer returned by
125 // aligned_alloc_xp, so we free the block pointer itself (not block->base).
126 block = block ? block->next : NULL;
127 while (block) {
128 ArenaBlock* temp = block;
129 block = block->next;
130 aligned_free_xp(temp); // frees the whole slab (header + data)
131 }
132
133 if (a->heap_allocated) aligned_free_xp(a);
134}
Aligned memory allocation functions for cross-platform support.
Portable utility macros for assertions, memory, math, iteration, timing, and platform compatibility.
char * end
Definition arena.h:65
bool is_static
Definition arena.h:66