Commit Diff


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();
 }