commit 1a06caead69676af14aec4f145173cd819caf17d from: Evan Burkey date: Sat Mar 14 06:32:53 2026 UTC add arena_save and arena_restore for scoped temporary allocations commit - afccd2672f33f8db43d1d7290f7c0c25daaecef2 commit + 1a06caead69676af14aec4f145173cd819caf17d blob - af1bf32c978a6307eeb41f46b079b5affe93f477 blob + a1a15aebc088f0eccd463e74c523616e8a1a64d6 --- include/lfmemory.h +++ include/lfmemory.h @@ -9,11 +9,18 @@ #define LF_DEFAULT_ALIGNMENT (2*sizeof(void*)) #endif // LF_DEFAULT_ALIGNMENT +#ifndef LF_ARENA_MAX_SAVE_POINTS +#define LF_ARENA_MAX_SAVE_POINTS 32 +#endif + typedef struct { unsigned char* buf; size_t buf_sz; size_t offset_cur; size_t offset_prev; + + size_t save_stack[LF_ARENA_MAX_SAVE_POINTS]; + size_t save_count; } ArenaAllocator; void arena_init(ArenaAllocator *allocator, size_t buf_sz); @@ -22,6 +29,8 @@ void *arena_malloc(ArenaAllocator* allocator, size_t s void arena_resize_buf(ArenaAllocator *allocator, size_t new_sz); void *arena_resize(ArenaAllocator *allocator, void *mem, size_t old_sz, size_t new_sz); void arena_clear(ArenaAllocator *allocator); +int arena_save(ArenaAllocator *allocator); +void arena_restore(ArenaAllocator *allocator); typedef struct { unsigned char *buf; blob - 83289bdda46e17a6c8bf99a1fbcdf72cef59c53c blob + 627dbf52c50beebcc40de79d911dd68f39120235 --- src/memory.c +++ src/memory.c @@ -16,6 +16,7 @@ void arena_init(ArenaAllocator *allocator, size_t buf_ allocator->buf_sz = buf_sz; allocator->offset_cur = 0; allocator->offset_prev = 0; + allocator->save_count = 0; } void arena_free(ArenaAllocator *allocator) { @@ -27,13 +28,31 @@ void arena_free(ArenaAllocator *allocator) { allocator->buf_sz = 0; allocator->offset_cur = 0; allocator->offset_prev = 0; + allocator->save_count = 0; } void arena_clear(ArenaAllocator *allocator) { allocator->offset_cur = 0; allocator->offset_prev = 0; + allocator->save_count = 0; } +int arena_save(ArenaAllocator *allocator) { + if (allocator->save_count >= LF_ARENA_MAX_SAVE_POINTS) { + return -1; + } + allocator->save_stack[allocator->save_count++] = allocator->offset_cur; + return 0; +} + +void arena_restore(ArenaAllocator *allocator) { + if (allocator->save_count == 0) { + return; + } + allocator->offset_cur = allocator->save_stack[--allocator->save_count]; + allocator->offset_prev = allocator->offset_cur; +} + static uintptr_t align_forward(const uintptr_t ptr, const uintptr_t align) { if (!is_power_of_two(align)) { return 0; blob - 11d06ce65252457ffd479c6d1f893f96c1b21ca5 blob + 6fc0580bc98896e1b56234a8c7a163f12e7c9289 --- tests/test_memory.c +++ tests/test_memory.c @@ -88,5 +88,67 @@ int main() { pool_destroy(pool); + /* arena save/restore */ + ArenaAllocator save_arena; + arena_init(&save_arena, 512); + + int *perm1 = arena_malloc(&save_arena, sizeof(int)); + *perm1 = 100; + int *perm2 = arena_malloc(&save_arena, sizeof(int)); + *perm2 = 200; + + /* save, do temp work, restore */ + ASSERT_EQ(arena_save(&save_arena), 0); + size_t saved_offset = save_arena.offset_cur; + + int *tmp1 = arena_malloc(&save_arena, sizeof(int)); + *tmp1 = 999; + int *tmp2 = arena_malloc(&save_arena, sizeof(int)); + *tmp2 = 888; + ASSERT_TRUE(save_arena.offset_cur > saved_offset); + + arena_restore(&save_arena); + ASSERT_EQ(save_arena.offset_cur, saved_offset); + + /* permanent data still intact */ + ASSERT_EQ(*perm1, 100); + ASSERT_EQ(*perm2, 200); + + /* new alloc reuses temp space */ + int *perm3 = arena_malloc(&save_arena, sizeof(int)); + *perm3 = 300; + ASSERT_NOT_NULL(perm3); + + /* nested save/restore */ + arena_clear(&save_arena); + int *outer = arena_malloc(&save_arena, sizeof(int)); + *outer = 1; + + ASSERT_EQ(arena_save(&save_arena), 0); + size_t outer_offset = save_arena.offset_cur; + + int *mid = arena_malloc(&save_arena, sizeof(int)); + *mid = 2; + + ASSERT_EQ(arena_save(&save_arena), 0); + size_t inner_offset = save_arena.offset_cur; + + int *inner = arena_malloc(&save_arena, sizeof(int)); + *inner = 3; + + arena_restore(&save_arena); + ASSERT_EQ(save_arena.offset_cur, inner_offset); + + arena_restore(&save_arena); + ASSERT_EQ(save_arena.offset_cur, outer_offset); + + ASSERT_EQ(*outer, 1); + + /* restore on empty stack is a no-op */ + arena_restore(&save_arena); + ASSERT_EQ(save_arena.offset_cur, outer_offset); + + arena_free(&save_arena); + TEST_REPORT(); }