Browse Source

Add `Arena_Temp`

gingerBill 2 years ago
parent
commit
494612827a
1 changed files with 77 additions and 4 deletions
  1. 77 4
      core/runtime/default_allocators_arena.odin

+ 77 - 4
core/runtime/default_allocators_arena.odin

@@ -18,6 +18,7 @@ Arena :: struct {
 	total_used:         uint,
 	total_used:         uint,
 	total_capacity:     uint,
 	total_capacity:     uint,
 	minimum_block_size: uint,
 	minimum_block_size: uint,
+	temp_count:         uint,
 }
 }
 
 
 @(private, require_results)
 @(private, require_results)
@@ -140,15 +141,20 @@ arena_init :: proc(arena: ^Arena, size: uint, backing_allocator: Allocator, loc
 }
 }
 
 
 
 
-// `arena_free_all` will free all but the first memory block, and then reset the memory block
-arena_free_all :: proc(arena: ^Arena, loc := #caller_location) {
-	for arena.curr_block != nil && arena.curr_block.prev != nil {
-		free_block := arena.curr_block
+arena_free_last_memory_block :: proc(arena: ^Arena, loc := #caller_location) {
+	if free_block := arena.curr_block; free_block != nil {
 		arena.curr_block = free_block.prev
 		arena.curr_block = free_block.prev
 
 
 		arena.total_capacity -= free_block.capacity
 		arena.total_capacity -= free_block.capacity
 		memory_block_dealloc(free_block, loc)
 		memory_block_dealloc(free_block, loc)
 	}
 	}
+}
+
+// `arena_free_all` will free all but the first memory block, and then reset the memory block
+arena_free_all :: proc(arena: ^Arena, loc := #caller_location) {
+	for arena.curr_block != nil && arena.curr_block.prev != nil {
+		arena_free_last_memory_block(arena, loc)
+	}
 
 
 	if arena.curr_block != nil {
 	if arena.curr_block != nil {
 		intrinsics.mem_zero(arena.curr_block.base, arena.curr_block.used)
 		intrinsics.mem_zero(arena.curr_block.base, arena.curr_block.used)
@@ -225,3 +231,70 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 
 
 	return
 	return
 }
 }
+
+
+
+
+Arena_Temp :: struct {
+	arena: ^Arena,
+	block: ^Memory_Block,
+	used:  uint,
+}
+
+@(require_results)
+arena_temp_begin :: proc(arena: ^Arena, loc := #caller_location) -> (temp: Arena_Temp) {
+	assert(arena != nil, "nil arena", loc)
+
+	temp.arena = arena
+	temp.block = arena.curr_block
+	if arena.curr_block != nil {
+		temp.used = arena.curr_block.used
+	}
+	arena.temp_count += 1
+	return
+}
+
+arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
+	assert(temp.arena != nil, "nil arena", loc)
+	arena := temp.arena
+
+	if temp.block != nil {
+		memory_block_found := false
+		for block := arena.curr_block; block != nil; block = block.prev {
+			if block == temp.block {
+				memory_block_found = true
+				break
+			}
+		}
+		if !memory_block_found {
+			assert(arena.curr_block == temp.block, "memory block stored within Arena_Temp not owned by Arena", loc)
+		}
+
+		for arena.curr_block != temp.block {
+			arena_free_last_memory_block(arena)
+		}
+
+		if block := arena.curr_block; block != nil {
+			assert(block.used >= temp.used, "out of order use of arena_temp_end", loc)
+			amount_to_zero := min(block.used-temp.used, block.capacity-block.used)
+			intrinsics.mem_zero(block.base[temp.used:], amount_to_zero)
+			block.used = temp.used
+		}
+	}
+
+	assert(arena.temp_count > 0, "double-use of arena_temp_end", loc)
+	arena.temp_count -= 1
+}
+
+// Ignore the use of a `arena_temp_begin` entirely
+arena_temp_ignore :: proc(temp: Arena_Temp, loc := #caller_location) {
+	assert(temp.arena != nil, "nil arena", loc)
+	arena := temp.arena
+
+	assert(arena.temp_count > 0, "double-use of arena_temp_end", loc)
+	arena.temp_count -= 1
+}
+
+arena_check_temp :: proc(arena: ^Arena, loc := #caller_location) {
+	assert(arena.temp_count == 0, "Arena_Temp not been ended", loc)
+}