20260829 Redesign
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
#include "c_Arena.h"
|
||||
#include <c_Test.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
#define C_ASSERT_ALIGNED_TO(ptr, align) \
|
||||
C_ASSERT(((uintptr_t)(ptr) % (align)) == 0, "Arena 分配出的地址不符合对齐边界")
|
||||
|
||||
/* ==============================================================================
|
||||
* 🧪 1. 测试 Arena 基础线性流水线分配 (c_Arena_Init / c_Arena_Alloc)
|
||||
* ============================================================================== */
|
||||
C_TEST_CASE(test_arena_basic_bump_allocation)
|
||||
{
|
||||
/* 1. 向传统堆借一块 1024 字节的大对齐沙盒作为 Arena 的后备支撑 */
|
||||
size_t backing_size = 1024;
|
||||
void* backing_buffer = C_ALLOC(backing_size);
|
||||
C_ASSERT(backing_buffer != NULL, "Backing buffer 申请成功");
|
||||
|
||||
c_Arena_t arena;
|
||||
c_Arena_Init(&arena, backing_buffer, backing_size);
|
||||
|
||||
/* 验证初始状态 */
|
||||
C_ASSERT_INT_EQ(arena.curr_offset, 0, "初始偏移量必须为 0");
|
||||
C_ASSERT_INT_EQ(arena.buf_len, backing_size, "沙盒边界容量对齐正确");
|
||||
|
||||
/* 2. 连续切分三块不同大小的内存 */
|
||||
int* arr1 = (int*)c_Arena_Alloc(&arena, 5 * sizeof(int)); /* 20 字节 */
|
||||
double* val1 = (double*)c_Arena_Alloc(&arena, sizeof(double)); /* 8 字节 */
|
||||
char* str1 = (char*)c_Arena_Alloc(&arena, 7); /* 7 字节 */
|
||||
|
||||
C_ASSERT(arr1 != NULL, "分配 arr1 成功");
|
||||
C_ASSERT(val1 != NULL, "分配 val1 成功");
|
||||
C_ASSERT(str1 != NULL, "分配 str1 成功");
|
||||
|
||||
/* 3. 核心物理对齐验证:检查 Bump 出来的每一块地址是否都顺应了 C_ALIGN_SIZE (16字节) 最大自然对齐 */
|
||||
C_ASSERT_ALIGNED_TO(arr1, C_ALIGN_SIZE);
|
||||
C_ASSERT_ALIGNED_TO(val1, C_ALIGN_SIZE);
|
||||
C_ASSERT_ALIGNED_TO(str1, C_ALIGN_SIZE);
|
||||
|
||||
/* 验证 c_Arena_Free 为安全的无操作空函数 */
|
||||
c_Arena_Free(&arena, arr1);
|
||||
|
||||
/* 销毁并解绑外部堆 */
|
||||
c_Arena_Destroy(&arena);
|
||||
C_FREE(backing_buffer);
|
||||
}
|
||||
|
||||
|
||||
/* ==============================================================================
|
||||
* 🧪 2. 测试快照保存与一键局部内存回滚 (c_ArenaTemp_Begin / End)
|
||||
* ============================================================================== */
|
||||
C_TEST_CASE(test_arena_snapshot_scratch_rollback)
|
||||
{
|
||||
size_t backing_size = 512;
|
||||
void* backing_buffer = C_ALLOC(backing_size);
|
||||
|
||||
c_Arena_t arena;
|
||||
c_Arena_Init(&arena, backing_buffer, backing_size);
|
||||
|
||||
/* 1. 先在常驻区分配一点持久数据 */
|
||||
int* persistent_data = (int*)c_Arena_Alloc(&arena, sizeof(int));
|
||||
*persistent_data = 8888;
|
||||
size_t persistent_offset = arena.curr_offset;
|
||||
|
||||
/* 2. 📸 拍下当前的物理快照 (保存当前划线位置) */
|
||||
c_ArenaTemp_t scratchpad = c_ArenaTemp_Begin(&arena);
|
||||
C_ASSERT_INT_EQ(scratchpad.curr_offset, persistent_offset, "快照必须精准锚定当前偏移量");
|
||||
|
||||
/* 3. 在这个局部临时沙盒里疯狂申请内存进行高频计算 */
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
void* temp_chunk = c_Arena_Alloc(&arena, 32);
|
||||
C_UNUSED(temp_chunk);
|
||||
}
|
||||
C_ASSERT(arena.curr_offset > persistent_offset, "临时分配导致划线指针一路向后推进");
|
||||
|
||||
/* 4. 🔄 结束局部计算,执行终极快照回滚 */
|
||||
c_ArenaTemp_End(scratchpad);
|
||||
|
||||
/* 断言:所有的临时内存被逻辑上“全量粉碎”,划线指针瞬间完好无损地缩回到持久化线之后! */
|
||||
C_ASSERT_INT_EQ(arena.curr_offset, persistent_offset, "一键回滚后,curr_offset 必须退回到最初的快照起点!");
|
||||
C_ASSERT_INT_EQ(*persistent_data, 8888, "常驻区的数据绝不能受到回滚机制的破坏");
|
||||
|
||||
c_Arena_Destroy(&arena);
|
||||
C_FREE(backing_buffer);
|
||||
}
|
||||
|
||||
|
||||
/* ==============================================================================
|
||||
* 🧪 3. 测试 `prev_offset` 驱动的高能原地重分配优化 (c_Arena_Resize)
|
||||
* ============================================================================== */
|
||||
C_TEST_CASE(test_arena_inplace_resize_optimization)
|
||||
{
|
||||
size_t backing_size = 512;
|
||||
void* backing_buffer = C_ALLOC(backing_size);
|
||||
|
||||
c_Arena_t arena;
|
||||
c_Arena_Init(&arena, backing_buffer, backing_size);
|
||||
|
||||
/* 1. 申请第一个隔离块,切断最底部的零线 */
|
||||
c_Arena_Alloc(&arena, 16);
|
||||
|
||||
/* 2. 申请目标数组:当前它处于整个沙盒的最顶端线(其偏移记录在 prev_offset 中) */
|
||||
int* array = (int*)c_Arena_Alloc(&arena, 4 * sizeof(int)); /* 申请 16 字节 */
|
||||
uintptr_t original_address = (uintptr_t)array;
|
||||
|
||||
array[0] = 100;
|
||||
array[3] = 400;
|
||||
|
||||
/* 3. 🎯 触发原地扩容:将其扩大到原先的 4 倍大小 (64字节) */
|
||||
int* resized_array = (int*)c_Arena_Resize(&arena, array, 4 * sizeof(int), 16 * sizeof(int));
|
||||
uintptr_t new_address = (uintptr_t)resized_array;
|
||||
|
||||
/* 🌟 核心断言:由于其属于上一次的最上方分配,Resize 逻辑应当直接划线,
|
||||
其返回的虚拟内存物理地址必须和原来完全一模一样,实现了零拷贝原地扩容! */
|
||||
C_ASSERT_INT_EQ(new_address, original_address, "Arena 对最后一次分配的块进行 Resize 时,必须触发零拷贝原地扩容优化!");
|
||||
C_ASSERT_INT_EQ(resized_array[0], 100, "扩容后元素 0 的数据保持完好");
|
||||
C_ASSERT_INT_EQ(resized_array[3], 400, "扩容后元素 3 的数据保持完好");
|
||||
|
||||
/* 4. 反向边界测试:如果在中间安插了新的分配,阻断了顶端线 */
|
||||
c_Arena_Alloc(&arena, 16); /* 抢占了顶端线 */
|
||||
|
||||
/* 此时再次对原本的 array 执行 Resize,由于其已经不是最新的分配,原地优化将会失效 */
|
||||
int* copy_moved_array = (int*)c_Arena_Resize(&arena, resized_array, 16 * sizeof(int), 32 * sizeof(int));
|
||||
uintptr_t moved_address = (uintptr_t)copy_moved_array;
|
||||
|
||||
/* 断言:此时原地优化被安全拦截,退化为分配新空间+数据迁移,地址应当发生改变 */
|
||||
C_ASSERT(moved_address != original_address, "被阻断的块执行 Resize 时,应当安全退化为新空间搬运迁移");
|
||||
C_ASSERT_INT_EQ(copy_moved_array[0], 100, "搬运迁移后数据依然保持完整性");
|
||||
|
||||
c_Arena_Destroy(&arena);
|
||||
C_FREE(backing_buffer);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
C_TEST_SUITE_BEGIN(MemoryArenaCoreTestSuite)
|
||||
|
||||
C_RUN_TEST_CASE(test_arena_basic_bump_allocation);
|
||||
C_RUN_TEST_CASE(test_arena_snapshot_scratch_rollback);
|
||||
C_RUN_TEST_CASE(test_arena_inplace_resize_optimization);
|
||||
|
||||
C_TEST_SUITE_END()
|
||||
}
|
||||
Reference in New Issue
Block a user