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.
This commit is contained in:
48
arena.h
48
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,
|
uint8_t *arena_realloc(arena_t *arena, uint8_t *pointer, uint32_t old_size,
|
||||||
uint32_t new_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
|
// Firstly find the region the pointer exists in
|
||||||
region_t *prev, *reg;
|
region_t *prev, *reg;
|
||||||
for (prev = NULL, reg = arena->beg;
|
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)
|
prev = reg, reg = reg->next)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!reg)
|
|
||||||
// pointer isn't allocated in the arena
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
int cleanup = 0;
|
|
||||||
uint8_t *new_ptr = NULL;
|
uint8_t *new_ptr = NULL;
|
||||||
if (old_size == reg->size && reg->capacity == reg->size)
|
|
||||||
|
// pointer isn't allocated in the arena, just allocate a new pointer
|
||||||
|
if (!reg)
|
||||||
|
goto arena_realloc__allocate_new;
|
||||||
|
|
||||||
|
/*
|
||||||
|
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))
|
||||||
{
|
{
|
||||||
// Completely filled region, may as well reallocate
|
reg->size += newsize - oldsize;
|
||||||
cleanup = 1;
|
return ptr;
|
||||||
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
|
|
||||||
{
|
arena_realloc__allocate_new:
|
||||||
// Allocate a new portion of memory on the arena
|
new_ptr = arena_alloc(arena, newsize);
|
||||||
new_ptr = arena_alloc(arena, new_size);
|
memcpy(new_ptr, ptr, oldsize);
|
||||||
}
|
|
||||||
memcpy(new_ptr, pointer, MIN(old_size, new_size));
|
|
||||||
if (cleanup)
|
|
||||||
free(reg);
|
|
||||||
return new_ptr;
|
return new_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user