1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
#include "arena_allocator.h"
#include <assert.h>
#include <stddef.h>
#include <stdbool.h>
SandArena sand_create_arena(SandAllocator *a) {
return (SandArena) {
.parent = a,
.current_region = NULL,
};
}
#define MAX(a, b) ({ \
__auto_type _a = (a);\
__auto_type _b = (b);\
_a > _b ? _a : _b;\
})
#define REGION_DEFUALT_SIZE ((size_t)2048)
static SandRegion *allocate_new_region(SandAllocator *a, size_t size, size_t alignment) {
size_t padded_size = size + (alignment - 1);
size_t capacity = MAX(padded_size, REGION_DEFUALT_SIZE) ;
size_t region_size = offsetof(SandRegion, data) + capacity;
SandRegion *result = sand_allocate_aligned(a, region_size, _Alignof(SandRegion));
if (result == NULL) {
return NULL;
}
result->capacity = capacity;
result->used = 0;
return result;
}
static void *allocate(size_t size, size_t alignment, void *user_data) {
SandArena *arena = user_data;
// This is the first allocation made within the arena. Let's allocate the initial region.
if (arena->current_region == NULL) {
SandRegion *first_region = allocate_new_region(arena->parent, size, alignment);
if (first_region == NULL) {
return NULL;
}
first_region->prev = NULL;
arena->current_region = first_region;
}
SandRegion *iter = arena->current_region;
while (true) { // Loop looking for a region.
void *aligned_ptr = sand_align_pointer_forward(iter->data + iter->used, alignment);
void *end_ptr = iter->data + iter->capacity;
if (aligned_ptr + size > end_ptr) { // This pointer region doesn't have room; allocate a new one and try again.
SandRegion *new_region = allocate_new_region(arena->parent, size, alignment);
if (new_region == NULL) {
return NULL;
}
new_region->prev = arena->current_region;
arena->current_region = iter;
iter = new_region; // Next time is guaranteed to succeed.
continue;
} else { // Yay, we found a spot!
iter->used += aligned_ptr - (void *)iter->data;
return aligned_ptr;
}
}
assert(false); // unreachable
}
static void deallocate(void *old_ptr, size_t old_size, void *user_data) {
(void)old_ptr;
(void)old_size;
(void)user_data;
return; // No-op, as all allocations are freed when the arena is freed.
}
// Returns a `SandAllocator` which is valid as long as the arena is.
// The arena must not be moved afterwards, as the allocator retains a pointer to the arena.
SandAllocator sand_get_allocator_for_arena(SandArena *arena) {
return (SandAllocator) {
.allocate = allocate,
.reallocate = NULL, // TODO: Special case for if it is the last allocation of the current region.
.deallocate = deallocate,
.user_data = arena,
};
}
void sand_destroy_arena(SandArena *arena) {
SandRegion *iter = arena->current_region;
while (iter != NULL) {
SandRegion *next = iter->prev;
size_t region_size = offsetof(SandRegion, data) + iter->capacity;
sand_deallocate(arena->parent, iter, region_size);
iter = next;
}
}
|