core_builtin_soa.odin 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. package runtime
  2. import "base:intrinsics"
  3. _ :: intrinsics
  4. /*
  5. SOA types are implemented with this sort of layout:
  6. SOA Fixed Array
  7. struct {
  8. f0: [N]T0,
  9. f1: [N]T1,
  10. f2: [N]T2,
  11. }
  12. SOA Slice
  13. struct {
  14. f0: ^T0,
  15. f1: ^T1,
  16. f2: ^T2,
  17. len: int,
  18. }
  19. SOA Dynamic Array
  20. struct {
  21. f0: ^T0,
  22. f1: ^T1,
  23. f2: ^T2,
  24. len: int,
  25. cap: int,
  26. allocator: Allocator,
  27. }
  28. A footer is used rather than a header purely to simplify access to the fields internally
  29. i.e. field index of the AOS == SOA
  30. */
  31. Raw_SOA_Footer_Slice :: struct {
  32. len: int,
  33. }
  34. Raw_SOA_Footer_Dynamic_Array :: struct {
  35. len: int,
  36. cap: int,
  37. allocator: Allocator,
  38. }
  39. @(builtin, require_results)
  40. raw_soa_footer_slice :: proc(array: ^$T/#soa[]$E) -> (footer: ^Raw_SOA_Footer_Slice) {
  41. if array == nil {
  42. return nil
  43. }
  44. field_count := uintptr(intrinsics.type_struct_field_count(E))
  45. footer = (^Raw_SOA_Footer_Slice)(uintptr(array) + field_count*size_of(rawptr))
  46. return
  47. }
  48. @(builtin, require_results)
  49. raw_soa_footer_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) -> (footer: ^Raw_SOA_Footer_Dynamic_Array) {
  50. if array == nil {
  51. return nil
  52. }
  53. field_count: uintptr
  54. when intrinsics.type_is_array(E) {
  55. field_count = len(E)
  56. } else {
  57. field_count = uintptr(intrinsics.type_struct_field_count(E))
  58. }
  59. footer = (^Raw_SOA_Footer_Dynamic_Array)(uintptr(array) + field_count*size_of(rawptr))
  60. return
  61. }
  62. raw_soa_footer :: proc{
  63. raw_soa_footer_slice,
  64. raw_soa_footer_dynamic_array,
  65. }
  66. @(builtin, require_results)
  67. make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
  68. if length <= 0 {
  69. return
  70. }
  71. footer := raw_soa_footer(&array)
  72. if size_of(E) == 0 {
  73. footer.len = length
  74. return
  75. }
  76. max_align := max(alignment, align_of(E))
  77. ti := type_info_of(typeid_of(T))
  78. ti = type_info_base(ti)
  79. si := &ti.variant.(Type_Info_Struct)
  80. field_count := uintptr(intrinsics.type_struct_field_count(E))
  81. total_size := 0
  82. for i in 0..<field_count {
  83. type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
  84. total_size += type.size * length
  85. total_size = align_forward_int(total_size, max_align)
  86. }
  87. allocator := allocator
  88. if allocator.procedure == nil {
  89. allocator = context.allocator
  90. }
  91. assert(allocator.procedure != nil)
  92. new_bytes: []byte
  93. new_bytes, err = allocator.procedure(
  94. allocator.data, .Alloc, total_size, max_align,
  95. nil, 0, loc,
  96. )
  97. if new_bytes == nil || err != nil {
  98. return
  99. }
  100. new_data := raw_data(new_bytes)
  101. data := uintptr(&array)
  102. offset := 0
  103. for i in 0..<field_count {
  104. type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
  105. offset = align_forward_int(offset, max_align)
  106. (^uintptr)(data)^ = uintptr(new_data) + uintptr(offset)
  107. data += size_of(rawptr)
  108. offset += type.size * length
  109. }
  110. footer.len = length
  111. return
  112. }
  113. @(builtin, require_results)
  114. make_soa_slice :: proc($T: typeid/#soa[]$E, length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
  115. return make_soa_aligned(T, length, align_of(E), allocator, loc)
  116. }
  117. @(builtin, require_results)
  118. make_soa_dynamic_array :: proc($T: typeid/#soa[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
  119. context.allocator = allocator
  120. reserve_soa(&array, DEFAULT_RESERVE_CAPACITY, loc) or_return
  121. return array, nil
  122. }
  123. @(builtin, require_results)
  124. make_soa_dynamic_array_len :: proc($T: typeid/#soa[dynamic]$E, #any_int length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
  125. context.allocator = allocator
  126. resize_soa(&array, length, loc) or_return
  127. return array, nil
  128. }
  129. @(builtin, require_results)
  130. make_soa_dynamic_array_len_cap :: proc($T: typeid/#soa[dynamic]$E, #any_int length, capacity: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
  131. context.allocator = allocator
  132. reserve_soa(&array, capacity, loc) or_return
  133. resize_soa(&array, length, loc) or_return
  134. return array, nil
  135. }
  136. @builtin
  137. make_soa :: proc{
  138. make_soa_slice,
  139. make_soa_dynamic_array,
  140. make_soa_dynamic_array_len,
  141. make_soa_dynamic_array_len_cap,
  142. }
  143. @builtin
  144. resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
  145. if array == nil {
  146. return nil
  147. }
  148. reserve_soa(array, length, loc) or_return
  149. footer := raw_soa_footer(array)
  150. footer.len = length
  151. return nil
  152. }
  153. @builtin
  154. reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
  155. if array == nil {
  156. return nil
  157. }
  158. old_cap := cap(array)
  159. if capacity <= old_cap {
  160. return nil
  161. }
  162. if array.allocator.procedure == nil {
  163. array.allocator = context.allocator
  164. }
  165. assert(array.allocator.procedure != nil)
  166. footer := raw_soa_footer(array)
  167. if size_of(E) == 0 {
  168. footer.cap = capacity
  169. return nil
  170. }
  171. ti := type_info_of(typeid_of(T))
  172. ti = type_info_base(ti)
  173. si := &ti.variant.(Type_Info_Struct)
  174. field_count: uintptr
  175. when intrinsics.type_is_array(E) {
  176. field_count = len(E)
  177. } else {
  178. field_count = uintptr(intrinsics.type_struct_field_count(E))
  179. }
  180. assert(footer.cap == old_cap)
  181. old_size := 0
  182. new_size := 0
  183. max_align :: align_of(E)
  184. for i in 0..<field_count {
  185. type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
  186. old_size += type.size * old_cap
  187. new_size += type.size * capacity
  188. old_size = align_forward_int(old_size, max_align)
  189. new_size = align_forward_int(new_size, max_align)
  190. }
  191. old_data := (^rawptr)(array)^
  192. new_bytes := array.allocator.procedure(
  193. array.allocator.data, .Alloc, new_size, max_align,
  194. nil, old_size, loc,
  195. ) or_return
  196. new_data := raw_data(new_bytes)
  197. footer.cap = capacity
  198. old_offset := 0
  199. new_offset := 0
  200. for i in 0..<field_count {
  201. type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
  202. old_offset = align_forward_int(old_offset, max_align)
  203. new_offset = align_forward_int(new_offset, max_align)
  204. new_data_elem := rawptr(uintptr(new_data) + uintptr(new_offset))
  205. old_data_elem := rawptr(uintptr(old_data) + uintptr(old_offset))
  206. mem_copy(new_data_elem, old_data_elem, type.size * old_cap)
  207. (^rawptr)(uintptr(array) + i*size_of(rawptr))^ = new_data_elem
  208. old_offset += type.size * old_cap
  209. new_offset += type.size * capacity
  210. }
  211. array.allocator.procedure(
  212. array.allocator.data, .Free, 0, max_align,
  213. old_data, old_size, loc,
  214. ) or_return
  215. return nil
  216. }
  217. @builtin
  218. append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
  219. if array == nil {
  220. return 0, nil
  221. }
  222. if cap(array) <= len(array) + 1 {
  223. cap := 2 * cap(array) + 8
  224. err = reserve_soa(array, cap, loc) // do not 'or_return' here as it could be a partial success
  225. }
  226. footer := raw_soa_footer(array)
  227. if size_of(E) > 0 && cap(array)-len(array) > 0 {
  228. ti := type_info_of(T)
  229. ti = type_info_base(ti)
  230. si := &ti.variant.(Type_Info_Struct)
  231. field_count: uintptr
  232. when intrinsics.type_is_array(E) {
  233. field_count = len(E)
  234. } else {
  235. field_count = uintptr(intrinsics.type_struct_field_count(E))
  236. }
  237. data := (^rawptr)(array)^
  238. soa_offset := 0
  239. item_offset := 0
  240. arg_copy := arg
  241. arg_ptr := &arg_copy
  242. max_align :: align_of(E)
  243. for i in 0..<field_count {
  244. type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
  245. soa_offset = align_forward_int(soa_offset, max_align)
  246. item_offset = align_forward_int(item_offset, type.align)
  247. dst := rawptr(uintptr(data) + uintptr(soa_offset) + uintptr(type.size * footer.len))
  248. src := rawptr(uintptr(arg_ptr) + uintptr(item_offset))
  249. mem_copy(dst, src, type.size)
  250. soa_offset += type.size * cap(array)
  251. item_offset += type.size
  252. }
  253. footer.len += 1
  254. return 1, err
  255. }
  256. return 0, err
  257. }
  258. @builtin
  259. append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
  260. if array == nil {
  261. return
  262. }
  263. arg_len := len(args)
  264. if arg_len == 0 {
  265. return
  266. }
  267. if cap(array) <= len(array)+arg_len {
  268. cap := 2 * cap(array) + max(8, arg_len)
  269. err = reserve_soa(array, cap, loc) // do not 'or_return' here as it could be a partial success
  270. }
  271. arg_len = min(cap(array)-len(array), arg_len)
  272. footer := raw_soa_footer(array)
  273. if size_of(E) > 0 && arg_len > 0 {
  274. ti := type_info_of(typeid_of(T))
  275. ti = type_info_base(ti)
  276. si := &ti.variant.(Type_Info_Struct)
  277. field_count := uintptr(intrinsics.type_struct_field_count(E))
  278. data := (^rawptr)(array)^
  279. soa_offset := 0
  280. item_offset := 0
  281. args_ptr := &args[0]
  282. max_align :: align_of(E)
  283. for i in 0..<field_count {
  284. type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
  285. soa_offset = align_forward_int(soa_offset, max_align)
  286. item_offset = align_forward_int(item_offset, type.align)
  287. dst := uintptr(data) + uintptr(soa_offset) + uintptr(type.size * footer.len)
  288. src := uintptr(args_ptr) + uintptr(item_offset)
  289. for j in 0..<arg_len {
  290. d := rawptr(dst + uintptr(j*type.size))
  291. s := rawptr(src + uintptr(j*size_of(E)))
  292. mem_copy(d, s, type.size)
  293. }
  294. soa_offset += type.size * cap(array)
  295. item_offset += type.size
  296. }
  297. }
  298. footer.len += arg_len
  299. return arg_len, err
  300. }
  301. // The append_soa built-in procedure appends elements to the end of an #soa dynamic array
  302. @builtin
  303. append_soa :: proc{
  304. append_soa_elem,
  305. append_soa_elems,
  306. }
  307. delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
  308. when intrinsics.type_struct_field_count(E) != 0 {
  309. array := array
  310. ptr := (^rawptr)(&array)^
  311. free(ptr, allocator, loc) or_return
  312. }
  313. return nil
  314. }
  315. delete_soa_dynamic_array :: proc(array: $T/#soa[dynamic]$E, loc := #caller_location) -> Allocator_Error {
  316. when intrinsics.type_struct_field_count(E) != 0 {
  317. array := array
  318. ptr := (^rawptr)(&array)^
  319. footer := raw_soa_footer(&array)
  320. free(ptr, footer.allocator, loc) or_return
  321. }
  322. return nil
  323. }
  324. @builtin
  325. delete_soa :: proc{
  326. delete_soa_slice,
  327. delete_soa_dynamic_array,
  328. }
  329. clear_soa_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) {
  330. when intrinsics.type_struct_field_count(E) != 0 {
  331. footer := raw_soa_footer(array)
  332. footer.len = 0
  333. }
  334. }
  335. @builtin
  336. clear_soa :: proc{
  337. clear_soa_dynamic_array,
  338. }
  339. // Converts soa slice into a soa dynamic array without cloning or allocating memory
  340. @(require_results)
  341. into_dynamic_soa :: proc(array: $T/#soa[]$E) -> #soa[dynamic]E {
  342. d: #soa[dynamic]E
  343. footer := raw_soa_footer_dynamic_array(&d)
  344. footer^ = {
  345. cap = len(array),
  346. len = 0,
  347. allocator = nil_allocator(),
  348. }
  349. field_count: uintptr
  350. when intrinsics.type_is_array(E) {
  351. field_count = len(E)
  352. } else {
  353. field_count = uintptr(intrinsics.type_struct_field_count(E))
  354. }
  355. array := array
  356. dynamic_data := ([^]rawptr)(&d)[:field_count]
  357. slice_data := ([^]rawptr)(&array)[:field_count]
  358. copy(dynamic_data, slice_data)
  359. return d
  360. }
  361. // `unordered_remove_soa` removed the element at the specified `index`. It does so by replacing the current end value
  362. // with the old value, and reducing the length of the dynamic array by 1.
  363. //
  364. // Note: This is an O(1) operation.
  365. // Note: If you the elements to remain in their order, use `ordered_remove_soa`.
  366. // Note: If the index is out of bounds, this procedure will panic.
  367. @builtin
  368. unordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, index: int, loc := #caller_location) #no_bounds_check {
  369. bounds_check_error_loc(loc, index, len(array))
  370. if index+1 < len(array) {
  371. ti := type_info_of(typeid_of(T))
  372. ti = type_info_base(ti)
  373. si := &ti.variant.(Type_Info_Struct)
  374. field_count: uintptr
  375. when intrinsics.type_is_array(E) {
  376. field_count = len(E)
  377. } else {
  378. field_count = uintptr(intrinsics.type_struct_field_count(E))
  379. }
  380. data := uintptr(array)
  381. for i in 0..<field_count {
  382. type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
  383. offset := rawptr((^uintptr)(data)^ + uintptr(index*type.size))
  384. final := rawptr((^uintptr)(data)^ + uintptr((len(array)-1)*type.size))
  385. mem_copy(offset, final, type.size)
  386. data += size_of(rawptr)
  387. }
  388. }
  389. raw_soa_footer_dynamic_array(array).len -= 1
  390. }
  391. // `ordered_remove_soa` removed the element at the specified `index` whilst keeping the order of the other elements.
  392. //
  393. // Note: This is an O(N) operation.
  394. // Note: If you the elements do not have to remain in their order, prefer `unordered_remove_soa`.
  395. // Note: If the index is out of bounds, this procedure will panic.
  396. @builtin
  397. ordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, index: int, loc := #caller_location) #no_bounds_check {
  398. bounds_check_error_loc(loc, index, len(array))
  399. if index+1 < len(array) {
  400. ti := type_info_of(typeid_of(T))
  401. ti = type_info_base(ti)
  402. si := &ti.variant.(Type_Info_Struct)
  403. field_count: uintptr
  404. when intrinsics.type_is_array(E) {
  405. field_count = len(E)
  406. } else {
  407. field_count = uintptr(intrinsics.type_struct_field_count(E))
  408. }
  409. data := uintptr(array)
  410. for i in 0..<field_count {
  411. type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
  412. offset := (^uintptr)(data)^ + uintptr(index*type.size)
  413. length := type.size*(len(array) - index - 1)
  414. mem_copy(rawptr(offset), rawptr(offset + uintptr(type.size)), length)
  415. data += size_of(rawptr)
  416. }
  417. }
  418. raw_soa_footer_dynamic_array(array).len -= 1
  419. }