diff options
| author | Aryadev Chavali <aryadev@aryadevchavali.com> | 2025-04-09 22:49:21 +0100 | 
|---|---|---|
| committer | Aryadev Chavali <aryadev@aryadevchavali.com> | 2025-04-09 22:49:21 +0100 | 
| commit | 1dd0f8835cea1ac6af0bf63eebdffdb4e66a0693 (patch) | |
| tree | 9fbb8e2efe1a1976c5551cb4061278b12f92269f | |
| parent | 1b78493b191f21a9c8bb2879908122e7efce92ae (diff) | |
| download | prick-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.h | 46 | 
1 files changed, 23 insertions, 23 deletions
| @@ -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;  } | 
