realloc_raw
This commit is contained in:
@@ -65,7 +65,7 @@ void ArenaAllocator::reset() {
|
|||||||
current_offset_ = 0;
|
current_offset_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *ArenaAllocator::realloc(void *ptr, size_t old_size, size_t new_size,
|
void *ArenaAllocator::realloc_raw(void *ptr, size_t old_size, size_t new_size,
|
||||||
size_t alignment) {
|
size_t alignment) {
|
||||||
if (ptr == nullptr) {
|
if (ptr == nullptr) {
|
||||||
return allocate_raw(new_size, alignment);
|
return allocate_raw(new_size, alignment);
|
||||||
|
|||||||
@@ -232,11 +232,44 @@ public:
|
|||||||
* - If new_size == 0, returns nullptr (no deallocation occurs)
|
* - If new_size == 0, returns nullptr (no deallocation occurs)
|
||||||
* - If ptr is null, behaves like allocate(new_size, alignment)
|
* - If ptr is null, behaves like allocate(new_size, alignment)
|
||||||
* - If ptr was the last allocation and space exists, extends in place
|
* - 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:
|
* ## Example:
|
||||||
* - In-place extension: O(1) - just updates offset
|
* ```cpp
|
||||||
* - Copy required: O(min(old_size, new_size)) for the memcpy
|
* void* ptr = arena.allocate(100);
|
||||||
|
* // ... use ptr ...
|
||||||
|
* ptr = arena.realloc_raw(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_raw(void *ptr, size_t old_size, size_t new_size,
|
||||||
|
size_t alignment = alignof(std::max_align_t));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 T's
|
||||||
|
* @param new_size Desired new size in T's
|
||||||
|
* @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
|
||||||
*
|
*
|
||||||
* ## Example:
|
* ## Example:
|
||||||
* ```cpp
|
* ```cpp
|
||||||
@@ -251,8 +284,10 @@ public:
|
|||||||
* - Like malloc/realloc, the contents beyond old_size are uninitialized
|
* - Like malloc/realloc, the contents beyond old_size are uninitialized
|
||||||
* - When copying to new location, uses the specified alignment
|
* - When copying to new location, uses the specified alignment
|
||||||
*/
|
*/
|
||||||
void *realloc(void *ptr, size_t old_size, size_t new_size,
|
template <typename T> T *realloc(T *ptr, size_t old_size, size_t new_size) {
|
||||||
size_t alignment = alignof(std::max_align_t));
|
return static_cast<T *>(realloc_raw(ptr, old_size * sizeof(T),
|
||||||
|
new_size * sizeof(T), alignof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Construct an object of type T in the arena using placement new.
|
* @brief Construct an object of type T in the arena using placement new.
|
||||||
|
|||||||
@@ -369,7 +369,7 @@ TEST_CASE("ArenaAllocator realloc functionality") {
|
|||||||
size_t used_before = fresh_arena.used_bytes();
|
size_t used_before = fresh_arena.used_bytes();
|
||||||
CHECK(used_before == 100);
|
CHECK(used_before == 100);
|
||||||
|
|
||||||
void *result = fresh_arena.realloc(ptr, 100, 0);
|
void *result = fresh_arena.realloc_raw(ptr, 100, 0);
|
||||||
CHECK(result == nullptr);
|
CHECK(result == nullptr);
|
||||||
CHECK(fresh_arena.used_bytes() == 0); // Memory should be reclaimed
|
CHECK(fresh_arena.used_bytes() == 0); // Memory should be reclaimed
|
||||||
|
|
||||||
@@ -381,18 +381,18 @@ TEST_CASE("ArenaAllocator realloc functionality") {
|
|||||||
CHECK(used_before2 >= 100); // At least 100 bytes due to potential alignment
|
CHECK(used_before2 >= 100); // At least 100 bytes due to potential alignment
|
||||||
|
|
||||||
void *result2 =
|
void *result2 =
|
||||||
arena2.realloc(ptr1, 50, 0); // ptr1 is not the last allocation
|
arena2.realloc_raw(ptr1, 50, 0); // ptr1 is not the last allocation
|
||||||
CHECK(result2 == nullptr);
|
CHECK(result2 == nullptr);
|
||||||
CHECK(arena2.used_bytes() ==
|
CHECK(arena2.used_bytes() ==
|
||||||
used_before2); // Memory should NOT be reclaimed
|
used_before2); // Memory should NOT be reclaimed
|
||||||
|
|
||||||
// realloc with nullptr behaves like allocate
|
// realloc with nullptr behaves like allocate
|
||||||
void *new_ptr = arena.realloc(nullptr, 0, 150);
|
void *new_ptr = arena.realloc_raw(nullptr, 0, 150);
|
||||||
CHECK(new_ptr != nullptr);
|
CHECK(new_ptr != nullptr);
|
||||||
CHECK(arena.used_bytes() >= 150);
|
CHECK(arena.used_bytes() >= 150);
|
||||||
|
|
||||||
// realloc with same size returns same pointer
|
// realloc with same size returns same pointer
|
||||||
void *same_ptr = arena.realloc(new_ptr, 150, 150);
|
void *same_ptr = arena.realloc_raw(new_ptr, 150, 150);
|
||||||
CHECK(same_ptr == new_ptr);
|
CHECK(same_ptr == new_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,7 +405,7 @@ TEST_CASE("ArenaAllocator realloc functionality") {
|
|||||||
std::memset(ptr, 0xAB, 100);
|
std::memset(ptr, 0xAB, 100);
|
||||||
|
|
||||||
// Should extend in place since it's the last allocation
|
// Should extend in place since it's the last allocation
|
||||||
void *extended_ptr = fresh_arena.realloc(ptr, 100, 200);
|
void *extended_ptr = fresh_arena.realloc_raw(ptr, 100, 200);
|
||||||
CHECK(extended_ptr == ptr); // Should be same pointer (in-place)
|
CHECK(extended_ptr == ptr); // Should be same pointer (in-place)
|
||||||
|
|
||||||
// Check that original data is preserved
|
// Check that original data is preserved
|
||||||
@@ -423,7 +423,7 @@ TEST_CASE("ArenaAllocator realloc functionality") {
|
|||||||
std::memset(ptr, 0xCD, 200);
|
std::memset(ptr, 0xCD, 200);
|
||||||
|
|
||||||
// Should shrink in place
|
// Should shrink in place
|
||||||
void *shrunk_ptr = fresh_arena.realloc(ptr, 200, 100);
|
void *shrunk_ptr = fresh_arena.realloc_raw(ptr, 200, 100);
|
||||||
CHECK(shrunk_ptr == ptr); // Same pointer
|
CHECK(shrunk_ptr == ptr); // Same pointer
|
||||||
CHECK(fresh_arena.used_bytes() == 100);
|
CHECK(fresh_arena.used_bytes() == 100);
|
||||||
|
|
||||||
@@ -447,7 +447,7 @@ TEST_CASE("ArenaAllocator realloc functionality") {
|
|||||||
|
|
||||||
// Try to reallocate ptr1 - should copy since ptr2 is now the last
|
// Try to reallocate ptr1 - should copy since ptr2 is now the last
|
||||||
// allocation
|
// allocation
|
||||||
void *realloc_ptr1 = fresh_arena.realloc(ptr1, 60, 120);
|
void *realloc_ptr1 = fresh_arena.realloc_raw(ptr1, 60, 120);
|
||||||
CHECK(realloc_ptr1 != ptr1); // Should be different pointer (copy occurred)
|
CHECK(realloc_ptr1 != ptr1); // Should be different pointer (copy occurred)
|
||||||
|
|
||||||
// Check that data was copied correctly
|
// Check that data was copied correctly
|
||||||
@@ -458,7 +458,7 @@ TEST_CASE("ArenaAllocator realloc functionality") {
|
|||||||
|
|
||||||
// Try to reallocate ptr2 (last allocation) - should extend in place if
|
// Try to reallocate ptr2 (last allocation) - should extend in place if
|
||||||
// space allows
|
// space allows
|
||||||
void *realloc_ptr2 = fresh_arena.realloc(ptr2, 30, 50);
|
void *realloc_ptr2 = fresh_arena.realloc_raw(ptr2, 30, 50);
|
||||||
// Note: this might not be in-place if the realloc of ptr1 used up space in
|
// Note: this might not be in-place if the realloc of ptr1 used up space in
|
||||||
// the block Let's just check the result is valid and data is preserved
|
// the block Let's just check the result is valid and data is preserved
|
||||||
CHECK(realloc_ptr2 != nullptr);
|
CHECK(realloc_ptr2 != nullptr);
|
||||||
@@ -477,7 +477,7 @@ TEST_CASE("ArenaAllocator realloc functionality") {
|
|||||||
std::memset(ptr, 0x33, 90);
|
std::memset(ptr, 0x33, 90);
|
||||||
|
|
||||||
// Try to extend beyond block size - should copy to new block
|
// Try to extend beyond block size - should copy to new block
|
||||||
void *extended_ptr = fresh_arena.realloc(ptr, 90, 150);
|
void *extended_ptr = fresh_arena.realloc_raw(ptr, 90, 150);
|
||||||
CHECK(extended_ptr != ptr); // Should be different (new block)
|
CHECK(extended_ptr != ptr); // Should be different (new block)
|
||||||
CHECK(fresh_arena.num_blocks() == 2); // Should have created new block
|
CHECK(fresh_arena.num_blocks() == 2); // Should have created new block
|
||||||
|
|
||||||
@@ -497,7 +497,7 @@ TEST_CASE("ArenaAllocator realloc functionality") {
|
|||||||
std::memset(ptr, 0x44, 50);
|
std::memset(ptr, 0x44, 50);
|
||||||
|
|
||||||
// Realloc with same alignment - should extend in place
|
// Realloc with same alignment - should extend in place
|
||||||
void *extended_ptr = fresh_arena.realloc(ptr, 50, 100, 16);
|
void *extended_ptr = fresh_arena.realloc_raw(ptr, 50, 100, 16);
|
||||||
CHECK(extended_ptr == ptr); // In place
|
CHECK(extended_ptr == ptr); // In place
|
||||||
CHECK(reinterpret_cast<uintptr_t>(extended_ptr) % 16 == 0);
|
CHECK(reinterpret_cast<uintptr_t>(extended_ptr) % 16 == 0);
|
||||||
|
|
||||||
@@ -520,7 +520,7 @@ TEST_CASE("ArenaAllocator realloc functionality") {
|
|||||||
|
|
||||||
// Grow in multiple steps
|
// Grow in multiple steps
|
||||||
for (size_t new_size = 100; new_size <= 500; new_size += 100) {
|
for (size_t new_size = 100; new_size <= 500; new_size += 100) {
|
||||||
void *new_ptr = fresh_arena.realloc(ptr, current_size, new_size);
|
void *new_ptr = fresh_arena.realloc_raw(ptr, current_size, new_size);
|
||||||
CHECK(new_ptr != nullptr);
|
CHECK(new_ptr != nullptr);
|
||||||
|
|
||||||
// Verify original data is still there
|
// Verify original data is still there
|
||||||
|
|||||||
Reference in New Issue
Block a user