Преглед на файлове

Support using allocator resize in `_reserve_soa` (fixes #5615)

Damian Tarnawski преди 3 седмици
родител
ревизия
05706864b7
променени са 2 файла, в които са добавени 77 реда и са изтрити 5 реда
  1. 51 5
      base/runtime/core_builtin_soa.odin
  2. 26 0
      tests/core/runtime/test_core_runtime.odin

+ 51 - 5
base/runtime/core_builtin_soa.odin

@@ -249,17 +249,63 @@ _reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, zero_memory: boo
 
 	old_data := (^rawptr)(array)^
 
-	new_bytes := array.allocator.procedure(
+	new_bytes, resize_err := array.allocator.procedure(
+		array.allocator.data, .Resize if zero_memory else .Resize_Non_Zeroed, new_size, max_align,
+		old_data, old_size, loc,
+	)
+	new_data := raw_data(new_bytes)
+
+	old_offset := 0
+	new_offset := 0
+
+	if resize_err == .None {
+
+		footer.cap = capacity
+
+		// Adjust layout
+		// before: |x x y y z z|
+		// now:    |x x y y z z _ _ _|
+		// after:  |x x _ y y _ z z _|
+
+		for i in 0..<field_count {
+			type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
+
+			old_offset = align_forward_int(old_offset, max_align)
+			new_offset = align_forward_int(new_offset, max_align)
+
+			new_data_elem := rawptr(uintptr(new_data) + uintptr(new_offset))
+			old_data_elem := rawptr(uintptr(new_data) + uintptr(old_offset))
+
+			mem_copy(new_data_elem, old_data_elem, type.size * old_cap)
+
+			(^rawptr)(uintptr(array) + i*size_of(rawptr))^ = new_data_elem
+
+			mem_zero(old_data_elem, int(uintptr(new_data_elem) - uintptr(old_data_elem)))
+
+			old_offset += type.size * old_cap
+			new_offset += type.size * capacity
+		}
+
+		return nil
+	}
+
+	if resize_err != .Mode_Not_Implemented {
+		return resize_err
+	}
+
+	new_bytes = array.allocator.procedure(
 		array.allocator.data, .Alloc if zero_memory else .Alloc_Non_Zeroed, new_size, max_align,
 		nil, old_size, loc,
 	) or_return
-	new_data := raw_data(new_bytes)
-
+	new_data = raw_data(new_bytes)
 
 	footer.cap = capacity
 
-	old_offset := 0
-	new_offset := 0
+	// Adjust layout
+	// before: |x x y y z z|
+	// now:    |x x y y z z| ... |_ _ _ _ _ _ _ _ _|
+	// after:                    |x x _ y y _ z z _|
+
 	for i in 0..<field_count {
 		type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
 

+ 26 - 0
tests/core/runtime/test_core_runtime.odin

@@ -179,6 +179,32 @@ test_map_get :: proc(t: ^testing.T) {
 	}
 }
 
+@(test)
+test_soa_array_allocator_resize :: proc(t: ^testing.T) {
+	arena: runtime.Arena
+	context.allocator = runtime.arena_allocator(&arena)
+	defer runtime.arena_destroy(&arena)
+
+	array, err := make(#soa[dynamic][2]int, 2, 3)
+	array[0] = [2]int{1, 2}
+	array[1] = [2]int{3, 4}
+
+	testing.expect_value(t, err, nil)
+	testing.expect_value(t, len(array), 2)
+	testing.expect_value(t, cap(array), 3)
+
+	err = resize(&array, 4)
+
+	testing.expect_value(t, err, nil)
+	testing.expect_value(t, len(array), 4)
+	testing.expect_value(t, cap(array), 4)
+
+	testing.expect_value(t, array[0], [2]int{1, 2})
+	testing.expect_value(t, array[1], [2]int{3, 4})
+	testing.expect_value(t, array[2], [2]int{0, 0})
+	testing.expect_value(t, array[3], [2]int{0, 0})
+}
+
 @(test)
 test_memory_equal :: proc(t: ^testing.T) {
 	data: [256]u8