From 1dd0f8835cea1ac6af0bf63eebdffdb4e66a0693 Mon Sep 17 00:00:00 2001 From: Aryadev Chavali Date: Wed, 9 Apr 2025 22:49:21 +0100 Subject: Fixed arena_realloc So the real purpose of arena_realloc is to figure out if we can get away with just adjusting the region that the pointer given resides in so that we don't need to _actually_ allocate any new memory. The previous implementation did this in the special case where the pointer given _is_ the entire region. But we can adjust the region's size _if_ the pointer given is the last allocation on the region i.e. it's on the tail end. --- arena.h | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/arena.h b/arena.h index 474c251..01e027a 100644 --- a/arena.h +++ b/arena.h @@ -185,6 +185,10 @@ 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) { + if (!pointer) + // Basically the same as allocating at this point + return arena_alloc(arena, newsize); + // Firstly find the region the pointer exists in region_t *prev, *reg; for (prev = NULL, reg = arena->beg; @@ -193,33 +197,29 @@ uint8_t *arena_realloc(arena_t *arena, uint8_t *pointer, uint32_t old_size, prev = reg, reg = reg->next) continue; + uint8_t *new_ptr = NULL; + + // pointer isn't allocated in the arena, just allocate a new pointer if (!reg) - // pointer isn't allocated in the arena - return NULL; + goto arena_realloc__allocate_new; - int cleanup = 0; - uint8_t *new_ptr = NULL; - if (old_size == reg->size && reg->capacity == reg->size) - { - // Completely filled region, may as well reallocate - cleanup = 1; - 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; - new_ptr = new_reg->bytes; - new_reg->size += new_size; - } - else + /* + If `ptr` is the latest allocation in `reg` and `reg` has enough capacity to + handle newsize, then we can adjust `reg` and not have to do any further + allocation work. + + This check is not UB because ptr is confirmed to be in reg. + */ + if (ptr + oldsize == reg->bytes + reg->size && + (reg->capacity - reg->size) > (newsize - oldsize)) { - // Allocate a new portion of memory on the arena - new_ptr = arena_alloc(arena, new_size); + reg->size += newsize - oldsize; + return ptr; } - memcpy(new_ptr, pointer, MIN(old_size, new_size)); - if (cleanup) - free(reg); + +arena_realloc__allocate_new: + new_ptr = arena_alloc(arena, newsize); + memcpy(new_ptr, ptr, oldsize); return new_ptr; } -- cgit v1.2.3-13-gbd6f