#include "c_Arena.h" #include #include #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() }