Add ArenaAllocator::realloc

This commit is contained in:
2025-08-15 12:30:05 -04:00
parent 6c506a2ba2
commit 52f0eeee1f
3 changed files with 286 additions and 1 deletions

View File

@@ -1,4 +1,5 @@
#include "arena_allocator.hpp"
#include <cassert>
ArenaAllocator::~ArenaAllocator() {
while (current_block_) {
@@ -63,6 +64,70 @@ void ArenaAllocator::reset() {
current_offset_ = 0;
}
void *ArenaAllocator::realloc(void *ptr, size_t old_size, size_t new_size,
size_t alignment) {
if (ptr == nullptr) {
return allocate(new_size, alignment);
}
if (new_size == old_size) {
return ptr;
}
// Assert that we have a current block if ptr is not null
assert(current_block_ &&
"realloc called with non-null ptr but no current block exists");
// Assert that current_offset_ is large enough (should always be true for
// valid callers)
assert(current_offset_ >= old_size &&
"current_offset_ must be >= old_size for valid last allocation");
// Check if this was the last allocation by comparing with expected location
char *expected_last_alloc_start =
current_block_->data() + current_offset_ - old_size;
if (ptr == expected_last_alloc_start) {
// This is indeed the last allocation
if (new_size > old_size) {
// Growing - check if we have space
size_t additional_space_needed = new_size - old_size;
if (current_offset_ + additional_space_needed <= current_block_->size) {
// We can extend in place
current_offset_ += additional_space_needed;
return ptr;
}
} else {
// Shrinking - just update the offset
size_t space_to_free = old_size - new_size;
current_offset_ -= space_to_free;
return new_size == 0 ? nullptr : ptr;
}
}
// Can't extend in place
if (new_size == 0) {
// For non-last allocations, we can't reclaim memory but still return
// nullptr
return nullptr;
}
if (new_size <= old_size) {
// Shrinking - no need to allocate, just return the same pointer
return ptr;
}
// Growing but can't extend in place - need to allocate new space and copy
void *new_ptr = allocate(new_size, alignment);
if (new_ptr && ptr) {
// Copy all the old data since we're growing
std::memcpy(new_ptr, ptr, old_size);
}
return new_ptr;
}
std::vector<ArenaAllocator::PointerInfo>
ArenaAllocator::find_intra_arena_pointers() const {
std::vector<PointerInfo> pointers;
@@ -380,4 +445,4 @@ void ArenaAllocator::dump_memory_contents(std::ostream &out, const char *data,
}
out << "|" << std::dec << std::endl;
}
}
}

View File

@@ -208,6 +208,50 @@ public:
return ptr;
}
/**
* @brief Reallocate memory, extending in place if possible or copying to a
* new location.
*
* This method provides realloc-like functionality for the arena allocator.
* If the given pointer was the last allocation and there's sufficient space
* in the current block to extend it, the allocation is grown in place.
* Otherwise, a new allocation is made and the old data is copied.
*
* @param ptr Pointer to the existing allocation (must be from this allocator)
* @param old_size Size of the existing allocation in bytes
* @param new_size Desired new size in bytes
* @param alignment Required alignment (default: alignof(std::max_align_t))
* @return Pointer to the reallocated memory (may be the same as ptr or
* different)
* @throws std::bad_alloc if memory allocation fails
*
* ## Behavior:
* - If new_size == old_size, returns ptr unchanged
* - If new_size == 0, returns nullptr (no deallocation occurs)
* - If ptr is null, behaves like allocate(new_size, alignment)
* - If ptr was the last allocation and space exists, extends in place
* - Otherwise allocates new space and copies min(old_size, new_size) bytes
*
* ## Performance:
* - In-place extension: O(1) - just updates offset
* - Copy required: O(min(old_size, new_size)) for the memcpy
*
* ## Example:
* ```cpp
* void* ptr = arena.allocate(100);
* // ... use ptr ...
* ptr = arena.realloc(ptr, 100, 200); // May extend in place or copy
* ```
*
* ## Safety Notes:
* - The caller must provide the correct old_size - this is not tracked
* - The old pointer becomes invalid if a copy occurs
* - Like malloc/realloc, the contents beyond old_size are uninitialized
* - When copying to new location, uses the specified alignment
*/
void *realloc(void *ptr, size_t old_size, size_t new_size,
size_t alignment = alignof(std::max_align_t));
/**
* @brief Construct an object of type T in the arena using placement new.
*