123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- package runtime
- import "intrinsics"
- _ :: intrinsics;
- /*
- SOA types are implemented with this sort of layout:
- SOA Fixed Array
- struct {
- f0: [N]T0,
- f1: [N]T1,
- f2: [N]T2,
- }
- SOA Slice
- struct {
- f0: ^T0,
- f1: ^T1,
- f2: ^T2,
- len: int,
- }
- SOA Dynamic Array
- struct {
- f0: ^T0,
- f1: ^T1,
- f2: ^T2,
- len: int,
- cap: int,
- allocator: Allocator,
- }
- A footer is used rather than a header purely to simplify access to the fields internally
- i.e. field index of the AOS == SOA
- */
- Raw_SOA_Footer_Slice :: struct {
- len: int,
- }
- Raw_SOA_Footer_Dynamic_Array :: struct {
- len: int,
- cap: int,
- allocator: Allocator,
- }
- raw_soa_footer_slice :: proc(array: ^$T/#soa[]$E) -> (footer: ^Raw_SOA_Footer_Slice) {
- if array == nil {
- return nil;
- }
- field_count := uintptr(intrinsics.type_struct_field_count(E));
- footer = (^Raw_SOA_Footer_Slice)(uintptr(array) + field_count*size_of(rawptr));
- return;
- }
- raw_soa_footer_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) -> (footer: ^Raw_SOA_Footer_Dynamic_Array) {
- if array == nil {
- return nil;
- }
- field_count := uintptr(intrinsics.type_struct_field_count(E));
- footer = (^Raw_SOA_Footer_Dynamic_Array)(uintptr(array) + field_count*size_of(rawptr));
- return;
- }
- raw_soa_footer :: proc{
- raw_soa_footer_slice,
- raw_soa_footer_dynamic_array,
- };
- @builtin
- make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_second {
- if length <= 0 {
- return;
- }
- footer := raw_soa_footer(&array);
- if size_of(E) == 0 {
- footer.len = length;
- return;
- }
- max_align := max(alignment, align_of(E));
- ti := type_info_of(typeid_of(T));
- ti = type_info_base(ti);
- si := &ti.variant.(Type_Info_Struct);
- field_count := uintptr(intrinsics.type_struct_field_count(E));
- total_size := 0;
- for i in 0..<field_count {
- type := si.types[i].variant.(Type_Info_Pointer).elem;
- total_size += type.size * length;
- total_size = align_forward_int(total_size, max_align);
- }
- allocator := allocator;
- if allocator.procedure == nil {
- allocator = context.allocator;
- }
- assert(allocator.procedure != nil);
- new_bytes: []byte;
- new_bytes, err = allocator.procedure(
- allocator.data, .Alloc, total_size, max_align,
- nil, 0, loc,
- );
- if new_bytes == nil || err != nil {
- return;
- }
- new_data := raw_data(new_bytes);
- data := uintptr(&array);
- offset := 0;
- for i in 0..<field_count {
- type := si.types[i].variant.(Type_Info_Pointer).elem;
- offset = align_forward_int(offset, max_align);
- (^uintptr)(data)^ = uintptr(new_data) + uintptr(offset);
- data += size_of(rawptr);
- offset += type.size * length;
- }
- footer.len = length;
- return;
- }
- @builtin
- make_soa_slice :: proc($T: typeid/#soa[]$E, length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_second {
- return make_soa_aligned(T, length, align_of(E), allocator, loc);
- }
- @builtin
- make_soa_dynamic_array :: proc($T: typeid/#soa[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (array: T) {
- context.allocator = allocator;
- reserve_soa(&array, DEFAULT_RESERVE_CAPACITY, loc);
- return;
- }
- @builtin
- make_soa_dynamic_array_len :: proc($T: typeid/#soa[dynamic]$E, auto_cast length: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
- context.allocator = allocator;
- resize_soa(&array, length, loc);
- return;
- }
- @builtin
- make_soa_dynamic_array_len_cap :: proc($T: typeid/#soa[dynamic]$E, auto_cast length, capacity: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
- context.allocator = allocator;
- if reserve_soa(&array, capacity, loc) {
- resize_soa(&array, length, loc);
- }
- return;
- }
- @builtin
- make_soa :: proc{
- make_soa_slice,
- make_soa_dynamic_array,
- make_soa_dynamic_array_len,
- make_soa_dynamic_array_len_cap,
- };
- @builtin
- resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #caller_location) -> bool {
- if array == nil {
- return false;
- }
- if !reserve_soa(array, length, loc) {
- return false;
- }
- footer := raw_soa_footer(array);
- footer.len = length;
- return true;
- }
- @builtin
- reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
- if array == nil {
- return false;
- }
- old_cap := cap(array);
- if capacity <= old_cap {
- return true;
- }
- if array.allocator.procedure == nil {
- array.allocator = context.allocator;
- }
- assert(array.allocator.procedure != nil);
- footer := raw_soa_footer(array);
- if size_of(E) == 0 {
- footer.cap = capacity;
- return true;
- }
- ti := type_info_of(typeid_of(T));
- ti = type_info_base(ti);
- si := &ti.variant.(Type_Info_Struct);
- field_count := uintptr(intrinsics.type_struct_field_count(E));
- assert(footer.cap == old_cap);
- old_size := 0;
- new_size := 0;
- max_align :: align_of(E);
- for i in 0..<field_count {
- type := si.types[i].variant.(Type_Info_Pointer).elem;
- old_size += type.size * old_cap;
- new_size += type.size * capacity;
- old_size = align_forward_int(old_size, max_align);
- new_size = align_forward_int(new_size, max_align);
- }
- old_data := (^rawptr)(array)^;
- new_bytes, err := array.allocator.procedure(
- array.allocator.data, .Alloc, new_size, max_align,
- nil, old_size, loc,
- );
- if new_bytes == nil || err != nil {
- return false;
- }
- new_data := raw_data(new_bytes);
- footer.cap = capacity;
- old_offset := 0;
- new_offset := 0;
- for i in 0..<field_count {
- type := si.types[i].variant.(Type_Info_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(old_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;
- old_offset += type.size * old_cap;
- new_offset += type.size * capacity;
- }
- _, err = array.allocator.procedure(
- array.allocator.data, .Free, 0, max_align,
- old_data, old_size, loc,
- );
- return true;
- }
- @builtin
- append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_location) {
- if array == nil {
- return;
- }
- arg_len := 1;
- if cap(array) <= len(array)+arg_len {
- cap := 2 * cap(array) + max(8, arg_len);
- _ = reserve_soa(array, cap, loc);
- }
- arg_len = min(cap(array)-len(array), arg_len);
- footer := raw_soa_footer(array);
- if size_of(E) > 0 && arg_len > 0 {
- ti := type_info_of(typeid_of(T));
- ti = type_info_base(ti);
- si := &ti.variant.(Type_Info_Struct);
- field_count := uintptr(intrinsics.type_struct_field_count(E));
- data := (^rawptr)(array)^;
- soa_offset := 0;
- item_offset := 0;
- arg_copy := arg;
- arg_ptr := &arg_copy;
- max_align :: align_of(E);
- for i in 0..<field_count {
- type := si.types[i].variant.(Type_Info_Pointer).elem;
- soa_offset = align_forward_int(soa_offset, max_align);
- item_offset = align_forward_int(item_offset, type.align);
- dst := rawptr(uintptr(data) + uintptr(soa_offset) + uintptr(type.size * footer.len));
- src := rawptr(uintptr(arg_ptr) + uintptr(item_offset));
- mem_copy(dst, src, type.size);
- soa_offset += type.size * cap(array);
- item_offset += type.size;
- }
- }
- footer.len += arg_len;
- }
- @builtin
- append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_location) {
- if array == nil {
- return;
- }
- arg_len := len(args);
- if arg_len == 0 {
- return;
- }
- if cap(array) <= len(array)+arg_len {
- cap := 2 * cap(array) + max(8, arg_len);
- _ = reserve_soa(array, cap, loc);
- }
- arg_len = min(cap(array)-len(array), arg_len);
- footer := raw_soa_footer(array);
- if size_of(E) > 0 && arg_len > 0 {
- ti := type_info_of(typeid_of(T));
- ti = type_info_base(ti);
- si := &ti.variant.(Type_Info_Struct);
- field_count := uintptr(intrinsics.type_struct_field_count(E));
- data := (^rawptr)(array)^;
- soa_offset := 0;
- item_offset := 0;
- args_ptr := &args[0];
- max_align :: align_of(E);
- for i in 0..<field_count {
- type := si.types[i].variant.(Type_Info_Pointer).elem;
- soa_offset = align_forward_int(soa_offset, max_align);
- item_offset = align_forward_int(item_offset, type.align);
- dst := uintptr(data) + uintptr(soa_offset) + uintptr(type.size * footer.len);
- src := uintptr(args_ptr) + uintptr(item_offset);
- for j in 0..<arg_len {
- d := rawptr(dst + uintptr(j*type.size));
- s := rawptr(src + uintptr(j*size_of(E)));
- mem_copy(d, s, type.size);
- }
- soa_offset += type.size * cap(array);
- item_offset += type.size;
- }
- }
- footer.len += arg_len;
- }
- // The append_soa built-in procedure appends elements to the end of an #soa dynamic array
- @builtin
- append_soa :: proc{
- append_soa_elem,
- append_soa_elems,
- };
- delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc := #caller_location) {
- when intrinsics.type_struct_field_count(E) != 0 {
- array := array;
- ptr := (^rawptr)(&array)^;
- free(ptr, allocator, loc);
- }
- }
- delete_soa_dynamic_array :: proc(array: $T/#soa[dynamic]$E, loc := #caller_location) {
- when intrinsics.type_struct_field_count(E) != 0 {
- array := array;
- ptr := (^rawptr)(&array)^;
- footer := raw_soa_footer(&array);
- free(ptr, footer.allocator, loc);
- }
- }
- @builtin
- delete_soa :: proc{
- delete_soa_slice,
- delete_soa_dynamic_array,
- };
|