summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAryadev Chavali <aryadev@aryadevchavali.com>2025-04-09 22:49:21 +0100
committerAryadev Chavali <aryadev@aryadevchavali.com>2025-04-09 22:49:21 +0100
commit1dd0f8835cea1ac6af0bf63eebdffdb4e66a0693 (patch)
tree9fbb8e2efe1a1976c5551cb4061278b12f92269f
parent1b78493b191f21a9c8bb2879908122e7efce92ae (diff)
downloadprick-1dd0f8835cea1ac6af0bf63eebdffdb4e66a0693.tar.gz
prick-1dd0f8835cea1ac6af0bf63eebdffdb4e66a0693.tar.bz2
prick-1dd0f8835cea1ac6af0bf63eebdffdb4e66a0693.zip
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.
-rw-r--r--arena.h46
1 files 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;
}