diff options
Diffstat (limited to 'src/arena.c')
| -rw-r--r-- | src/arena.c | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/src/arena.c b/src/arena.c new file mode 100644 index 0000000..d340f3a --- /dev/null +++ b/src/arena.c | |||
| @@ -0,0 +1,183 @@ | |||
| 1 | #include "arena.h" | ||
| 2 | #include "shared.h" | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | #include <stdint.h> | ||
| 6 | #include <stdlib.h> | ||
| 7 | #include <errno.h> | ||
| 8 | #include <error.h> | ||
| 9 | |||
| 10 | typedef struct an { | ||
| 11 | void *membase; | ||
| 12 | void *memcur; | ||
| 13 | size_t allocated; | ||
| 14 | size_t used; | ||
| 15 | |||
| 16 | struct an *next; | ||
| 17 | } arenanode; | ||
| 18 | |||
| 19 | typedef struct arena { | ||
| 20 | arenanode *start; | ||
| 21 | arenanode *current; | ||
| 22 | |||
| 23 | // For keeping track of the largest possible thing that can be allocated to one node | ||
| 24 | size_t node_memspace; | ||
| 25 | } arena; | ||
| 26 | |||
| 27 | int arenanode_init(arenanode **an, size_t bytes) { | ||
| 28 | (*an) = xcalloc(1, sizeof(**an)); | ||
| 29 | if(!(*an)) | ||
| 30 | return -1; | ||
| 31 | |||
| 32 | (*an)->allocated = bytes; | ||
| 33 | (*an)->used = 0; | ||
| 34 | (*an)->next = NULL; | ||
| 35 | |||
| 36 | void *mem = xcalloc(bytes, 1); | ||
| 37 | if(!mem) { | ||
| 38 | free((*an)); | ||
| 39 | (*an) = NULL; | ||
| 40 | return -1; | ||
| 41 | } | ||
| 42 | |||
| 43 | (*an)->membase = mem; | ||
| 44 | (*an)->memcur = mem; | ||
| 45 | |||
| 46 | return 0; | ||
| 47 | } | ||
| 48 | |||
| 49 | int arena_init(arena **a, size_t bytes) { | ||
| 50 | if(!ISPOWOF2(MEM_ALIGN_BYTES)) | ||
| 51 | XALLOC_EXIT("<arena_init> \"MEM_ALIGN_BYTES\" is not a power of 2. Refusing to create a new arena"); | ||
| 52 | |||
| 53 | (*a) = xcalloc(1, sizeof(**a)); | ||
| 54 | if(!(*a)) | ||
| 55 | return -1; | ||
| 56 | (*a)->start = NULL; | ||
| 57 | (*a)->current = NULL; | ||
| 58 | (*a)->node_memspace = bytes; | ||
| 59 | |||
| 60 | arenanode *base; | ||
| 61 | if(arenanode_init(&base, bytes) < 0) { | ||
| 62 | free(*a); | ||
| 63 | (*a) = NULL; | ||
| 64 | return -1; | ||
| 65 | } | ||
| 66 | |||
| 67 | (*a)->start = base; | ||
| 68 | (*a)->current = base; | ||
| 69 | |||
| 70 | return 0; | ||
| 71 | } | ||
| 72 | |||
| 73 | void* arena_alloc(arena * const arena, size_t bytes) { | ||
| 74 | if(!arena) { | ||
| 75 | errno = EINVAL; | ||
| 76 | return NULL; | ||
| 77 | } | ||
| 78 | if(bytes > arena->node_memspace) { | ||
| 79 | errno = ENOMEM; | ||
| 80 | return NULL; | ||
| 81 | } | ||
| 82 | if(!ISPOWOF2(MEM_ALIGN_BYTES)) | ||
| 83 | XALLOC_EXIT("<arena_alloc> \"MEM_ALIGN_BYTES\" is not a power of 2. Refusing to allocate any memory"); | ||
| 84 | |||
| 85 | if(bytes > ((arena->current)->allocated - (arena->current)->used)) { | ||
| 86 | arenanode *new; | ||
| 87 | if(arenanode_init(&new, arena->node_memspace) < 0) | ||
| 88 | return NULL; | ||
| 89 | |||
| 90 | arena->current->next = new; | ||
| 91 | arena->current = new; | ||
| 92 | } | ||
| 93 | |||
| 94 | size_t alignment = MEM_ALIGN(bytes); | ||
| 95 | size_t offset = bytes + alignment; | ||
| 96 | |||
| 97 | void *mem = arena->current->memcur; | ||
| 98 | arena->current->memcur = (void*)(((uint8_t*)arena->current->memcur) + offset); | ||
| 99 | |||
| 100 | arena->current->used += offset; | ||
| 101 | |||
| 102 | return mem; | ||
| 103 | |||
| 104 | // Note: This implementation assumes that malloc provides already-aligned memory. If it | ||
| 105 | // doesn't, that sucks and blows everything up | ||
| 106 | } | ||
| 107 | |||
| 108 | int arena_free(arena **arena) { | ||
| 109 | if(!arena) { | ||
| 110 | errno = EINVAL; | ||
| 111 | return -1; | ||
| 112 | } | ||
| 113 | if(!(*arena)) { | ||
| 114 | errno = EINVAL; | ||
| 115 | return -1; | ||
| 116 | } | ||
| 117 | |||
| 118 | (*arena)->current = (*arena)->start; | ||
| 119 | for(arenanode *n; (*arena)->current != NULL;) { | ||
| 120 | n = (*arena)->current->next; | ||
| 121 | |||
| 122 | free((*arena)->current->membase); | ||
| 123 | free((*arena)->current); | ||
| 124 | |||
| 125 | (*arena)->current = n; | ||
| 126 | } | ||
| 127 | free((*arena)); | ||
| 128 | (*arena) = NULL; | ||
| 129 | |||
| 130 | return 0; | ||
| 131 | } | ||
| 132 | |||
| 133 | int arena_clear(arena **arena) { | ||
| 134 | if(!arena) { | ||
| 135 | errno = EINVAL; | ||
| 136 | return -1; | ||
| 137 | } | ||
| 138 | if(!(*arena)) { | ||
| 139 | errno = EINVAL; | ||
| 140 | return -1; | ||
| 141 | } | ||
| 142 | |||
| 143 | size_t bytes = (*arena)->node_memspace; | ||
| 144 | |||
| 145 | int ret = 0; | ||
| 146 | if((ret = arena_free(arena)) < 0) | ||
| 147 | return ret; | ||
| 148 | return arena_init(arena, bytes); | ||
| 149 | } | ||
| 150 | |||
| 151 | |||
| 152 | |||
| 153 | // Simple Arena is an arena that can't expand whenever allocating memory, meaning what you originally allocated is what you get | ||
| 154 | |||
| 155 | typedef arena simplearena; | ||
| 156 | |||
| 157 | int simplearena_init(simplearena **a, size_t bytes) { | ||
| 158 | return arena_init(a, bytes); | ||
| 159 | } | ||
| 160 | |||
| 161 | void* simplearena_alloc(simplearena * const a, size_t bytes) { | ||
| 162 | // The criteria to allocate new memory in arena_alloc is 'bytes > ((a->current)->allocated - (a->current)->used)', so if this | ||
| 163 | // is true, just return NULL & set errno | ||
| 164 | |||
| 165 | if(!a) { | ||
| 166 | errno = EINVAL; | ||
| 167 | return NULL; | ||
| 168 | } | ||
| 169 | if(bytes > ((a->current)->allocated - (a->current)->used)) { | ||
| 170 | errno = ENOMEM; | ||
| 171 | return NULL; | ||
| 172 | } | ||
| 173 | |||
| 174 | return arena_alloc(a, bytes); | ||
| 175 | } | ||
| 176 | |||
| 177 | int simplearena_free(simplearena **a) { | ||
| 178 | return arena_free(a); | ||
| 179 | } | ||
| 180 | |||
| 181 | int simplearena_clear(simplearena **a) { | ||
| 182 | return arena_clear(a); | ||
| 183 | } \ No newline at end of file | ||
