mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-14 01:34:35 +00:00
adds the pop procedure family for soa arrays
This commit is contained in:
@@ -215,6 +215,8 @@ remove_range_fixed_capacity_dynamic_array :: proc(array: ^$D/[dynamic; $N]$E, #a
|
||||
unordered_remove :: proc{
|
||||
unordered_remove_dynamic_array,
|
||||
unordered_remove_fixed_capacity_dynamic_array,
|
||||
|
||||
unordered_remove_soa,
|
||||
}
|
||||
|
||||
|
||||
@@ -222,6 +224,8 @@ unordered_remove :: proc{
|
||||
ordered_remove :: proc{
|
||||
ordered_remove_dynamic_array,
|
||||
ordered_remove_fixed_capacity_dynamic_array,
|
||||
|
||||
ordered_remove_soa,
|
||||
}
|
||||
|
||||
@builtin
|
||||
@@ -271,6 +275,8 @@ pop_fixed_capacity_dynamic_array :: proc(array: ^$T/[dynamic; $N]$E, loc := #cal
|
||||
pop :: proc{
|
||||
pop_dynamic_array,
|
||||
pop_fixed_capacity_dynamic_array,
|
||||
|
||||
pop_soa,
|
||||
}
|
||||
|
||||
// `pop_safe_dynamic_array` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
@@ -303,6 +309,8 @@ pop_safe_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[dynamic
|
||||
pop_safe :: proc{
|
||||
pop_safe_dynamic_array,
|
||||
pop_safe_fixed_capacity_dynamic_array,
|
||||
|
||||
pop_safe_soa,
|
||||
}
|
||||
|
||||
|
||||
@@ -342,6 +350,8 @@ pop_front_fixed_capacity_dynamic_array :: proc(array: ^$T/[dynamic; $N]$E, loc :
|
||||
pop_front :: proc{
|
||||
pop_front_dynamic_array,
|
||||
pop_front_fixed_capacity_dynamic_array,
|
||||
|
||||
pop_front_soa,
|
||||
}
|
||||
|
||||
|
||||
@@ -381,6 +391,8 @@ pop_front_safe_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[d
|
||||
pop_front_safe :: proc {
|
||||
pop_front_safe_dynamic_array,
|
||||
pop_front_safe_fixed_capacity_dynamic_array,
|
||||
|
||||
pop_front_safe_soa,
|
||||
}
|
||||
|
||||
|
||||
@@ -1212,6 +1224,9 @@ inject_at :: proc{
|
||||
inject_at_elem_fixed_capacity_dynamic_array,
|
||||
inject_at_elems_fixed_capacity_dynamic_array,
|
||||
inject_at_elem_string_fixed_capacity_dynamic_array,
|
||||
|
||||
inject_at_elem_soa,
|
||||
inject_at_elems_soa,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ Raw_SOA_Footer_Dynamic_Array :: struct {
|
||||
// Multipointer indexing lowers to GEP and doesn't capture, so prefer that throughout.
|
||||
|
||||
@(builtin, require_results)
|
||||
raw_soa_footer_slice :: proc(array: ^$T/#soa[]$E) -> (footer: ^Raw_SOA_Footer_Slice) {
|
||||
raw_soa_footer_slice :: proc "contextless" (array: ^$T/#soa[]$E) -> (footer: ^Raw_SOA_Footer_Slice) {
|
||||
if array == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -65,7 +65,7 @@ raw_soa_footer_slice :: proc(array: ^$T/#soa[]$E) -> (footer: ^Raw_SOA_Footer_Sl
|
||||
return
|
||||
}
|
||||
@(builtin, require_results)
|
||||
raw_soa_footer_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) -> (footer: ^Raw_SOA_Footer_Dynamic_Array) {
|
||||
raw_soa_footer_dynamic_array :: proc "contextless" (array: ^$T/#soa[dynamic]$E) -> (footer: ^Raw_SOA_Footer_Dynamic_Array) {
|
||||
if array == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -704,6 +704,58 @@ into_dynamic_soa :: proc(array: $T/#soa[]$E) -> #soa[dynamic]E {
|
||||
return d
|
||||
}
|
||||
|
||||
// `pop_soa` will remove and return the end value of the #soa dynamic array `array` and reduces the length of `array` by 1.
|
||||
//
|
||||
// Note: If the #soa dynamic array has no elements (`len(array) == 0`), this procedure will panic.
|
||||
@builtin
|
||||
pop_soa :: proc(#no_alias array: ^$T/#soa[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
|
||||
assert(len(array) > 0, loc=loc)
|
||||
res = array[len(array)-1]
|
||||
raw_soa_footer_dynamic_array(array).len -= 1
|
||||
return
|
||||
}
|
||||
|
||||
// `pop_safe_soa` trys to remove and return the end value of the #soa dynamic array `array` and reduces the length of `array` by 1.
|
||||
// If the operation is not possible, it will return false.
|
||||
@builtin
|
||||
pop_safe_soa :: proc "contextless" (#no_alias array: ^$T/#soa[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
||||
if len(array) == 0 {
|
||||
return
|
||||
}
|
||||
res, ok = array[len(array)-1], true
|
||||
raw_soa_footer_dynamic_array(array).len -= 1
|
||||
return
|
||||
}
|
||||
|
||||
// `pop_front_soa` will remove and return the first value of the #soa dynamic array `array` and reduces the length of `array` by 1,
|
||||
// whilst keeping the order of the other elements.
|
||||
//
|
||||
// Note: This is an O(N) operation.
|
||||
// Note: If the #soa dynamic array has no elements (`len(array) == 0`), this procedure will panic.
|
||||
@builtin
|
||||
pop_front_soa :: proc(#no_alias array: ^$T/#soa[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
|
||||
assert(len(array) > 0, loc=loc)
|
||||
res = array[0]
|
||||
_ordered_remove_soa(array, 0)
|
||||
return
|
||||
}
|
||||
|
||||
// `pop_front_safe_soa` trys to remove and return the first value of the #soa dynamic array `array` and reduces the
|
||||
// length of `array` by 1, whilst keeping the order of the other elements.
|
||||
// If the operation is not possible, it will return false.
|
||||
//
|
||||
// Note: This is an O(N) operation.
|
||||
@builtin
|
||||
pop_front_safe_soa :: proc "contextless" (#no_alias array: ^$T/#soa[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
||||
if len(array) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
res, ok = array[0], true
|
||||
_ordered_remove_soa(array, 0)
|
||||
return
|
||||
}
|
||||
|
||||
// `unordered_remove_soa` removed the element at the specified `index`. It does so by replacing the current end value
|
||||
// with the old value, and reducing the length of the dynamic array by 1.
|
||||
//
|
||||
@@ -714,7 +766,6 @@ into_dynamic_soa :: proc(array: $T/#soa[]$E) -> #soa[dynamic]E {
|
||||
unordered_remove_soa :: proc(#no_alias array: ^$T/#soa[dynamic]$E, #any_int index: int, loc := #caller_location) #no_bounds_check {
|
||||
bounds_check_error_loc(loc, index, len(array))
|
||||
if index+1 < len(array) {
|
||||
// Use the compiler's #soa element load and store lowering.
|
||||
array[index] = array[len(array)-1]
|
||||
}
|
||||
raw_soa_footer_dynamic_array(array).len -= 1
|
||||
@@ -728,6 +779,12 @@ unordered_remove_soa :: proc(#no_alias array: ^$T/#soa[dynamic]$E, #any_int inde
|
||||
@builtin
|
||||
ordered_remove_soa :: proc(#no_alias array: ^$T/#soa[dynamic]$E, #any_int index: int, loc := #caller_location) #no_bounds_check {
|
||||
bounds_check_error_loc(loc, index, len(array))
|
||||
_ordered_remove_soa(array, index)
|
||||
}
|
||||
|
||||
// the unchecked body of ordered_remove_soa, shared with the front pops.
|
||||
// index must already be known to be in bounds.
|
||||
_ordered_remove_soa :: proc "contextless" (#no_alias array: ^$T/#soa[dynamic]$E, index: int) #no_bounds_check {
|
||||
if index+1 < len(array) {
|
||||
ti := type_info_of(typeid_of(T))
|
||||
ti = type_info_base(ti)
|
||||
|
||||
@@ -1081,11 +1081,9 @@ test_memory_compare_zero :: proc(t: ^testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Runs identical append/inject/remove sequences for a #soa[dynamic] array
|
||||
// Runs identical append/inject/remove/pop sequences for a #soa[dynamic] array
|
||||
// and an AoS [dynamic] reference, comparing all elements after each
|
||||
// stage. Covers appends, injections (interior, at the end, and past the
|
||||
// end where the gap must read as zero elements), unordered and ordered
|
||||
// removes.
|
||||
// stage. Covers appends, injections, pops, unordered and ordered removes.
|
||||
@(test)
|
||||
test_soa_array_append_inject_remove :: proc(t: ^testing.T) {
|
||||
check :: proc(t: ^testing.T, $E: typeid, mk: proc(i: int) -> E) {
|
||||
@@ -1242,6 +1240,74 @@ test_soa_array_append_inject_remove :: proc(t: ^testing.T) {
|
||||
testing.expect_value(t, err, nil)
|
||||
non_zero_append(&ref, mk(42))
|
||||
expect_same(t, soa, ref)
|
||||
|
||||
// interleave pops from both ends
|
||||
n, err = append(&soa, ..buf[:])
|
||||
testing.expect_value(t, n, 32)
|
||||
testing.expect_value(t, err, nil)
|
||||
append(&ref, ..buf[:])
|
||||
for _ in 0..<8 {
|
||||
testing.expect_value(t, pop_soa(&soa), pop(&ref))
|
||||
testing.expect_value(t, pop_front_soa(&soa), pop_front(&ref))
|
||||
}
|
||||
expect_same(t, soa, ref)
|
||||
|
||||
// safe pops from both ends, down to empty
|
||||
popped: E
|
||||
for len(ref) > 0 {
|
||||
popped, ok = pop_safe_soa(&soa)
|
||||
testing.expect(t, ok)
|
||||
testing.expect_value(t, popped, pop(&ref))
|
||||
if len(ref) == 0 {
|
||||
break
|
||||
}
|
||||
popped, ok = pop_front_safe_soa(&soa)
|
||||
testing.expect(t, ok)
|
||||
testing.expect_value(t, popped, pop_front(&ref))
|
||||
}
|
||||
expect_same(t, soa, ref)
|
||||
testing.expect_value(t, len(soa), 0)
|
||||
|
||||
// safe pops must report failure on an empty array
|
||||
_, ok = pop_safe_soa(&soa)
|
||||
testing.expect(t, !ok)
|
||||
_, ok = pop_front_safe_soa(&soa)
|
||||
testing.expect(t, !ok)
|
||||
|
||||
// test procedure groups resolution for the builtin names
|
||||
for i in 0..<10 {
|
||||
append(&soa, mk(700 + i))
|
||||
append(&ref, mk(700 + i))
|
||||
}
|
||||
ok, err = inject_at(&soa, 3, mk(800))
|
||||
testing.expect(t, ok)
|
||||
testing.expect_value(t, err, nil)
|
||||
inject_at(&ref, 3, mk(800))
|
||||
expect_same(t, soa, ref)
|
||||
|
||||
ok, err = inject_at(&soa, 2, ..buf[:BATCH_LEN])
|
||||
testing.expect(t, ok)
|
||||
testing.expect_value(t, err, nil)
|
||||
inject_at(&ref, 2, ..buf[:BATCH_LEN])
|
||||
expect_same(t, soa, ref)
|
||||
|
||||
ordered_remove(&soa, 4)
|
||||
ordered_remove(&ref, 4)
|
||||
unordered_remove(&soa, 1)
|
||||
unordered_remove(&ref, 1)
|
||||
expect_same(t, soa, ref)
|
||||
|
||||
testing.expect_value(t, pop(&soa), pop(&ref))
|
||||
testing.expect_value(t, pop_front(&soa), pop_front(&ref))
|
||||
expect_same(t, soa, ref)
|
||||
|
||||
popped, ok = pop_safe(&soa)
|
||||
testing.expect(t, ok)
|
||||
testing.expect_value(t, popped, pop(&ref))
|
||||
popped, ok = pop_front_safe(&soa)
|
||||
testing.expect(t, ok)
|
||||
testing.expect_value(t, popped, pop_front(&ref))
|
||||
expect_same(t, soa, ref)
|
||||
}
|
||||
|
||||
// mixed field widths + padding
|
||||
|
||||
Reference in New Issue
Block a user