Browse Source

Added support for growing in place to some arenas.

This affects `runtime.Arena` and `virtual.Arena`, but not currently
`mem.Arena`. These changes allow the last allocation that has been
made to be resized to a larger size by just extending their
allocation in-place, when there's sufficient room in the memory block to
do so.

Shrinking in place and re-using the rest of the allocation can be
supported using almost the same logic, but would require the memory to
be zeroed. Since this would add a additional cost that isn't currently
present, shrinking has not been changed.
Barinzaya 7 months ago
parent
commit
98b3a9eacd
2 changed files with 36 additions and 8 deletions
  1. 18 4
      base/runtime/default_temp_allocator_arena.odin
  2. 18 4
      core/mem/virtual/arena.odin

+ 18 - 4
base/runtime/default_temp_allocator_arena.odin

@@ -210,10 +210,24 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		case size == 0:
 			err = .Mode_Not_Implemented
 			return
-		case (uintptr(old_data) & uintptr(alignment-1) == 0) && size < old_size:
-			// shrink data in-place
-			data = old_data[:size]
-			return
+		case uintptr(old_data) & uintptr(alignment-1) == 0:
+			if size < old_size {
+				// shrink data in-place
+				data = old_data[:size]
+				return
+			}
+
+			if block := arena.curr_block; block != nil {
+				start := uint(uintptr(old_memory)) - uint(uintptr(block.base))
+				old_end := start + old_size
+				new_end := start + size
+				if start < old_end && old_end == block.used && new_end <= block.capacity {
+					// grow data in-place, adjusting next allocation
+					block.used = uint(new_end)
+					data = block.base[start:new_end]
+					return
+				}
+			}
 		}
 
 		new_memory := arena_alloc(arena, size, alignment, location) or_return

+ 18 - 4
core/mem/virtual/arena.odin

@@ -328,10 +328,24 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
 		case size == 0:
 			err = .Mode_Not_Implemented
 			return
-		case (uintptr(old_data) & uintptr(alignment-1) == 0) && size < old_size:
-			// shrink data in-place
-			data = old_data[:size]
-			return
+		case uintptr(old_data) & uintptr(alignment-1) == 0:
+			if size < old_size {
+				// shrink data in-place
+				data = old_data[:size]
+				return
+			}
+
+			if block := arena.curr_block; block != nil {
+				start := uint(uintptr(old_memory)) - uint(uintptr(block.base))
+				old_end := start + old_size
+				new_end := start + size
+				if start < old_end && old_end == block.used && new_end <= block.reserved {
+					// grow data in-place, adjusting next allocation
+					_ = alloc_from_memory_block(block, new_end - old_end, 1, default_commit_size=arena.default_commit_size) or_return
+					data = block.base[start:new_end]
+					return
+				}
+			}
 		}
 
 		new_memory := arena_alloc(arena, size, alignment, location) or_return