mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-04 14:48:47 +00:00
Merge branch 'master' of https://github.com/odin-lang/Odin
This commit is contained in:
@@ -501,6 +501,121 @@ append_soa :: proc{
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// `append_nothing_soa` appends an empty value to a dynamic SOA array. It returns `1, nil` if successful, and `0, err` when it was not possible,
|
||||||
|
// whatever `err` happens to be.
|
||||||
|
@builtin
|
||||||
|
append_nothing_soa :: proc(array: ^$T/#soa[dynamic]$E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
if array == nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
prev_len := len(array)
|
||||||
|
resize_soa(array, len(array)+1, loc) or_return
|
||||||
|
return len(array)-prev_len, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// `inject_at_elem_soa` injects an element in a dynamic SOA array at a specified index and moves the previous elements after that index "across"
|
||||||
|
@builtin
|
||||||
|
inject_at_elem_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||||
|
when !ODIN_NO_BOUNDS_CHECK {
|
||||||
|
ensure(index >= 0, "Index must be positive.", loc)
|
||||||
|
}
|
||||||
|
if array == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n := max(len(array), index)
|
||||||
|
m :: 1
|
||||||
|
new_len := n + m
|
||||||
|
|
||||||
|
resize_soa(array, new_len, loc) or_return
|
||||||
|
|
||||||
|
when size_of(E) != 0 {
|
||||||
|
ti := type_info_base(type_info_of(typeid_of(T)))
|
||||||
|
si := &ti.variant.(Type_Info_Struct)
|
||||||
|
|
||||||
|
field_count := len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
|
||||||
|
|
||||||
|
item_offset := 0
|
||||||
|
|
||||||
|
arg_copy := arg
|
||||||
|
arg_ptr := &arg_copy
|
||||||
|
|
||||||
|
for i in 0..<field_count {
|
||||||
|
data := (^uintptr)(uintptr(array) + uintptr(si.offsets[i]))^
|
||||||
|
type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
|
||||||
|
item_offset = align_forward_int(item_offset, type.align)
|
||||||
|
|
||||||
|
src := data + uintptr(index * type.size)
|
||||||
|
dst := data + uintptr((index + m) * type.size)
|
||||||
|
mem_copy(rawptr(dst), rawptr(src), (n - index) * type.size)
|
||||||
|
|
||||||
|
mem_copy(rawptr(src), rawptr(uintptr(arg_ptr) + uintptr(item_offset)), type.size)
|
||||||
|
|
||||||
|
item_offset += type.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// `inject_at_elems_soa` injects multiple elements in a dynamic SOA array at a specified index and moves the previous elements after that index "across"
|
||||||
|
@builtin
|
||||||
|
inject_at_elems_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||||
|
when !ODIN_NO_BOUNDS_CHECK {
|
||||||
|
ensure(index >= 0, "Index must be positive.", loc)
|
||||||
|
}
|
||||||
|
if array == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(args) == 0 {
|
||||||
|
ok = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
n := max(len(array), index)
|
||||||
|
m := len(args)
|
||||||
|
new_len := n + m
|
||||||
|
|
||||||
|
resize_soa(array, new_len, loc) or_return
|
||||||
|
|
||||||
|
when size_of(E) != 0 {
|
||||||
|
ti := type_info_base(type_info_of(typeid_of(T)))
|
||||||
|
si := &ti.variant.(Type_Info_Struct)
|
||||||
|
|
||||||
|
field_count := len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
|
||||||
|
|
||||||
|
item_offset := 0
|
||||||
|
|
||||||
|
args_ptr := &args[0]
|
||||||
|
|
||||||
|
for i in 0..<field_count {
|
||||||
|
data := (^uintptr)(uintptr(array) + uintptr(si.offsets[i]))^
|
||||||
|
type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
|
||||||
|
item_offset = align_forward_int(item_offset, type.align)
|
||||||
|
|
||||||
|
src := data + uintptr(index * type.size)
|
||||||
|
dst := data + uintptr((index + m) * type.size)
|
||||||
|
mem_copy(rawptr(dst), rawptr(src), (n - index) * type.size)
|
||||||
|
|
||||||
|
for j in 0..<len(args) {
|
||||||
|
d := rawptr(src + uintptr(j*type.size))
|
||||||
|
s := rawptr(uintptr(args_ptr) + uintptr(item_offset) + uintptr(j*size_of(E)))
|
||||||
|
mem_copy(d, s, type.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
item_offset += type.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// `inject_at_soa` injects something into a dynamic SOA array at a specified index and moves the previous elements after that index "across"
|
||||||
|
@builtin inject_at_soa :: proc{inject_at_elem_soa, inject_at_elems_soa}
|
||||||
|
|
||||||
|
|
||||||
delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||||
field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
|
field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
|
||||||
when field_count != 0 {
|
when field_count != 0 {
|
||||||
|
|||||||
@@ -3210,6 +3210,17 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr {
|
|||||||
return ce
|
return ce
|
||||||
}
|
}
|
||||||
|
|
||||||
|
empty_selector_expr :: proc(tok: tokenizer.Token, operand: ^ast.Expr) -> ^ast.Selector_Expr {
|
||||||
|
field := ast.new(ast.Ident, tok.pos, end_pos(tok))
|
||||||
|
field.name = ""
|
||||||
|
|
||||||
|
sel := ast.new(ast.Selector_Expr, operand.pos, field)
|
||||||
|
sel.expr = operand
|
||||||
|
sel.op = tok
|
||||||
|
sel.field = field
|
||||||
|
|
||||||
|
return sel
|
||||||
|
}
|
||||||
|
|
||||||
parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^ast.Expr) {
|
parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^ast.Expr) {
|
||||||
operand = value
|
operand = value
|
||||||
@@ -3343,8 +3354,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
|
|||||||
|
|
||||||
case:
|
case:
|
||||||
error(p, p.curr_tok.pos, "expected a selector")
|
error(p, p.curr_tok.pos, "expected a selector")
|
||||||
advance_token(p)
|
operand = empty_selector_expr(tok, operand)
|
||||||
operand = ast.new(ast.Bad_Expr, operand.pos, end_pos(tok))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case .Arrow_Right:
|
case .Arrow_Right:
|
||||||
@@ -3361,8 +3371,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
|
|||||||
operand = sel
|
operand = sel
|
||||||
case:
|
case:
|
||||||
error(p, p.curr_tok.pos, "expected a selector")
|
error(p, p.curr_tok.pos, "expected a selector")
|
||||||
advance_token(p)
|
operand = empty_selector_expr(tok, operand)
|
||||||
operand = ast.new(ast.Bad_Expr, operand.pos, end_pos(tok))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case .Pointer:
|
case .Pointer:
|
||||||
|
|||||||
@@ -756,6 +756,14 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) {
|
|||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
type_writer_appendc(w, CANONICAL_FIELD_SEPARATOR);
|
type_writer_appendc(w, CANONICAL_FIELD_SEPARATOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (f->flags & EntityFlags_IsSubtype) {
|
||||||
|
type_writer_appendc(w, "#subtype ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f->flags & EntityFlag_Using) {
|
||||||
|
type_writer_appendc(w, "using ");
|
||||||
|
}
|
||||||
type_writer_append(w, f->token.string.text, f->token.string.len);
|
type_writer_append(w, f->token.string.text, f->token.string.len);
|
||||||
type_writer_appendc(w, CANONICAL_TYPE_SEPARATOR);
|
type_writer_appendc(w, CANONICAL_TYPE_SEPARATOR);
|
||||||
write_type_to_canonical_string(w, f->type);
|
write_type_to_canonical_string(w, f->type);
|
||||||
|
|||||||
@@ -292,6 +292,65 @@ test_soa_array_allocator_resize_overlapping :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, array[3], [3]int{0, 0, 0})
|
testing.expect_value(t, array[3], [3]int{0, 0, 0})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
test_soa_array_inject_at_elem :: proc(t: ^testing.T) {
|
||||||
|
|
||||||
|
V :: struct {a: u8, b: f32}
|
||||||
|
|
||||||
|
array := make(#soa[dynamic]V, 0, 2)
|
||||||
|
defer delete(array)
|
||||||
|
|
||||||
|
append(&array, V{1, 1.5}, V{2, 2.5}, V{3, 3.5})
|
||||||
|
|
||||||
|
expect_inject(t, &array, 0, {0, 0.5}, {{0, 0.5}, {1, 1.5}, {2, 2.5}, {3, 3.5}})
|
||||||
|
expect_inject(t, &array, 2, {5, 5.5}, {{0, 0.5}, {1, 1.5}, {5, 5.5}, {2, 2.5}, {3, 3.5}})
|
||||||
|
expect_inject(t, &array, 5, {9, 9.5}, {{0, 0.5}, {1, 1.5}, {5, 5.5}, {2, 2.5}, {3, 3.5}, {9, 9.5}})
|
||||||
|
|
||||||
|
expect_inject :: proc(t: ^testing.T, arr: ^#soa[dynamic]V, index: int, arg: V, expected_slice: []V) {
|
||||||
|
ok, err := inject_at_soa(arr, index, arg)
|
||||||
|
testing.expectf(t, ok == true, "Injection of %v at index %d failed", arg, index)
|
||||||
|
testing.expectf(t, err == nil, "Injection allocation of %v at index %d failed: %v", arg, index, err)
|
||||||
|
equals := len(arr) == len(expected_slice)
|
||||||
|
for e, i in expected_slice {
|
||||||
|
if arr[i] != e {
|
||||||
|
equals = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
testing.expectf(t, equals, "After injection of %v at index %d, expected array to be\n&%v, got\n%v", arg, index, expected_slice, arr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
test_soa_array_inject_at_elems :: proc(t: ^testing.T) {
|
||||||
|
|
||||||
|
V :: struct {a: u8, b: f32}
|
||||||
|
|
||||||
|
array := make(#soa[dynamic]V, 0, 2)
|
||||||
|
defer delete(array)
|
||||||
|
|
||||||
|
append(&array, V{1, 1.5}, V{2, 2.5}, V{3, 3.5})
|
||||||
|
|
||||||
|
expect_inject(t, &array, 0, {{0, 0.5}}, {{0, 0.5}, {1, 1.5}, {2, 2.5}, {3, 3.5}})
|
||||||
|
expect_inject(t, &array, 2, {{5, 5.5}, {6, 6.5}}, {{0, 0.5}, {1, 1.5}, {5, 5.5}, {6, 6.5}, {2, 2.5}, {3, 3.5}})
|
||||||
|
expect_inject(t, &array, 6, {{9, 9.5}, {10, 10.5}}, {{0, 0.5}, {1, 1.5}, {5, 5.5}, {6, 6.5}, {2, 2.5}, {3, 3.5}, {9, 9.5}, {10, 10.5}})
|
||||||
|
expect_inject(t, &array, 6, {}, {{0, 0.5}, {1, 1.5}, {5, 5.5}, {6, 6.5}, {2, 2.5}, {3, 3.5}, {9, 9.5}, {10, 10.5}})
|
||||||
|
|
||||||
|
expect_inject :: proc(t: ^testing.T, arr: ^#soa[dynamic]V, index: int, args: []V, expected_slice: []V) {
|
||||||
|
ok, err := inject_at_soa(arr, index, ..args)
|
||||||
|
testing.expectf(t, ok == true, "Injection of %v at index %d failed", args, index)
|
||||||
|
testing.expectf(t, err == nil, "Injection allocation of %v at index %d failed: %v", args, index, err)
|
||||||
|
equals := len(arr) == len(expected_slice)
|
||||||
|
for e, i in expected_slice {
|
||||||
|
if arr[i] != e {
|
||||||
|
equals = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
testing.expectf(t, equals, "After injection of %v at index %d, expected array to be\n&%v, got\n%v", args, index, expected_slice, arr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
test_memory_equal :: proc(t: ^testing.T) {
|
test_memory_equal :: proc(t: ^testing.T) {
|
||||||
data: [256]u8
|
data: [256]u8
|
||||||
|
|||||||
Reference in New Issue
Block a user