diff options
Diffstat (limited to 'src/arena.c')
-rw-r--r-- | src/arena.c | 43 |
1 files changed, 43 insertions, 0 deletions
diff --git a/src/arena.c b/src/arena.c index 7ca5626..5b50404 100644 --- a/src/arena.c +++ b/src/arena.c @@ -5,6 +5,7 @@ #include <stddef.h> #include <stdint.h> #include <stdlib.h> +#include <string.h> #include "alloc.h" #include "common.h" @@ -69,6 +70,48 @@ arena_alloc(struct _arena **a, size_t nmemb, size_t size, size_t align) return p->data; } +void * +_arena_grow(arena *a, void *ptr, size_t old_nmemb, size_t new_nmemb, + size_t size, size_t align) +{ + assert(IS_POW_2(align)); + assert(new_nmemb * size != 0); + + if (size > SIZE_MAX / new_nmemb) { + errno = EOVERFLOW; + err("%s:", __func__); + } + + size *= new_nmemb; + + for (struct _arena *p = *a; p != NULL; p = p->next) { + if (ptr < p->data || ptr > p->last) + continue; + + /* If we need to grow the given allocation, but it was the last + allocation made in a region, then we first see if we can just eat + more trailing free space in the region to avoid a memcpy(). */ + if (ptr == p->last) { + size_t rest = p->cap - p->len; + size_t need = (new_nmemb - old_nmemb) * size; + if (need <= rest) { + p->len += need; + return ptr; + } + } + + void *dst = arena_alloc(a, new_nmemb, size, align); + return memcpy(dst, ptr, old_nmemb * size); + } + +#if DEBUG + err("%s:%d: tried to resize pointer that wasn’t allocated", __func__, + __LINE__); +#else + __builtin_unreachable(); +#endif +} + void arena_free(struct _arena **a) { |