carray 2.0.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (386) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +6 -25
  3. data/CHANGELOG.md +338 -0
  4. data/{NEWS.md → CHANGELOG.v1.md} +3 -0
  5. data/LICENSE +1 -1
  6. data/README.md +120 -36
  7. data/carray.gemspec +32 -30
  8. data/ext/ca_array_pool.c +91 -0
  9. data/ext/ca_axis_descriptor.h +186 -0
  10. data/ext/ca_axis_dispatch.c +924 -0
  11. data/ext/ca_axis_group.c +1244 -0
  12. data/ext/ca_bincmp_dispatch.c +76 -0
  13. data/ext/ca_bincmp_dispatch.h +85 -0
  14. data/ext/ca_binop_dispatch.c +124 -0
  15. data/ext/ca_binop_dispatch.h +152 -0
  16. data/ext/ca_categorical_iterator.c +1375 -0
  17. data/ext/ca_compare.c +94 -0
  18. data/ext/ca_compare.h +26 -0
  19. data/ext/ca_composite_dispatch.c +414 -0
  20. data/ext/ca_composite_dispatch.h +116 -0
  21. data/ext/ca_for_buffer.h +96 -0
  22. data/ext/ca_for_each_element.h +239 -0
  23. data/ext/ca_group_iter.c +304 -0
  24. data/ext/ca_iter_substrate.h +325 -0
  25. data/ext/ca_kernel_iterator.c +4367 -0
  26. data/ext/ca_kernel_iterator.h +2596 -0
  27. data/ext/ca_moncmp_dispatch.c +37 -0
  28. data/ext/ca_moncmp_dispatch.h +62 -0
  29. data/ext/ca_monop_dispatch.c +200 -0
  30. data/ext/ca_monop_dispatch.h +235 -0
  31. data/ext/ca_obj_array.c +355 -359
  32. data/ext/ca_obj_bincmp.c +839 -0
  33. data/ext/ca_obj_binop.c +948 -0
  34. data/ext/ca_obj_bitarray.c +369 -164
  35. data/ext/ca_obj_bitfield.c +294 -234
  36. data/ext/ca_obj_block.c +189 -711
  37. data/ext/ca_obj_byte_swap.c +766 -0
  38. data/ext/ca_obj_const_string.c +967 -0
  39. data/ext/ca_obj_face.c +750 -0
  40. data/ext/ca_obj_face.h +279 -0
  41. data/ext/ca_obj_fake.c +239 -100
  42. data/ext/ca_obj_farray.c +54 -441
  43. data/ext/ca_obj_field.c +82 -529
  44. data/ext/ca_obj_fixlen_string.c +308 -0
  45. data/ext/ca_obj_grid.c +866 -440
  46. data/ext/ca_obj_meld.c +1039 -0
  47. data/ext/ca_obj_moncmp.c +588 -0
  48. data/ext/ca_obj_monop.c +1123 -0
  49. data/ext/ca_obj_object.c +866 -296
  50. data/ext/ca_obj_record.c +470 -0
  51. data/ext/ca_obj_reduce.c +97 -82
  52. data/ext/ca_obj_refer.c +593 -459
  53. data/ext/ca_obj_remap.c +475 -0
  54. data/ext/ca_obj_repeat.c +92 -477
  55. data/ext/ca_obj_roll.c +624 -0
  56. data/ext/ca_obj_select.c +344 -296
  57. data/ext/ca_obj_select_axis.c +1306 -0
  58. data/ext/ca_obj_shift.c +231 -793
  59. data/ext/ca_obj_source.c +78 -0
  60. data/ext/ca_obj_stack.c +1173 -0
  61. data/ext/ca_obj_stride.c +2584 -0
  62. data/ext/ca_obj_string.c +270 -0
  63. data/ext/ca_obj_tile.c +622 -0
  64. data/ext/ca_obj_time.c +548 -0
  65. data/ext/ca_obj_timedelta.c +437 -0
  66. data/ext/ca_obj_transpose.c +62 -516
  67. data/ext/ca_obj_triop.c +785 -0
  68. data/ext/ca_obj_window.c +1202 -565
  69. data/ext/ca_op_byte_swap.c +175 -0
  70. data/ext/ca_op_cmplx64.h +123 -0
  71. data/ext/ca_op_ipower.c +316 -0
  72. data/ext/ca_op_powi.h +88 -0
  73. data/ext/ca_sort_kernels.h +132 -0
  74. data/ext/ca_sweep_engine.c +473 -0
  75. data/ext/ca_sweep_engine.h +166 -0
  76. data/ext/ca_transform_common.c +235 -0
  77. data/ext/ca_triop_dispatch.c +55 -0
  78. data/ext/ca_triop_dispatch.h +62 -0
  79. data/ext/carray.h +810 -420
  80. data/ext/carray_access.c +873 -731
  81. data/ext/carray_attribute.c +98 -329
  82. data/ext/carray_bincount.c +255 -0
  83. data/ext/carray_broadcast.c +376 -0
  84. data/ext/carray_build_flags.h +3 -0
  85. data/ext/carray_call_cfunc.c +2897 -874
  86. data/ext/carray_call_cfunc.h +313 -0
  87. data/ext/carray_cast.c +1264 -315
  88. data/ext/carray_cast_func.rb +81 -40
  89. data/ext/carray_class.c +53 -63
  90. data/ext/carray_config.h +28 -0
  91. data/ext/carray_conversion.c +350 -346
  92. data/ext/carray_copy.c +168 -270
  93. data/ext/carray_core.c +1396 -206
  94. data/ext/carray_count.c +312 -0
  95. data/ext/carray_data_type.c +43 -19
  96. data/ext/carray_element.c +585 -213
  97. data/ext/carray_factorize.c +2542 -0
  98. data/ext/carray_generate.c +230 -559
  99. data/ext/carray_histogram.c +490 -0
  100. data/ext/carray_hold.c +228 -0
  101. data/ext/carray_index_classifier.c +1021 -0
  102. data/ext/carray_index_classifier.h +27 -0
  103. data/ext/carray_internal.h +136 -0
  104. data/ext/carray_kernels_bincmp.c +4446 -0
  105. data/ext/carray_kernels_binop.c +11001 -0
  106. data/ext/carray_kernels_init.c +1131 -0
  107. data/ext/carray_kernels_map.c +3467 -0
  108. data/ext/carray_kernels_moncmp.c +2097 -0
  109. data/ext/carray_kernels_monop.c +18313 -0
  110. data/ext/carray_kernels_reduce_aggregate.c +25837 -0
  111. data/ext/carray_kernels_reduce_boolean.c +330 -0
  112. data/ext/carray_kernels_reduce_cumulative.c +14593 -0
  113. data/ext/carray_kernels_reduce_extreme.c +16948 -0
  114. data/ext/carray_kernels_reduce_variance.c +3910 -0
  115. data/ext/carray_kernels_scan.c +3693 -0
  116. data/ext/carray_kernels_search.c +32138 -0
  117. data/ext/carray_kernels_sort.c +10626 -0
  118. data/ext/carray_kernels_triop.c +1392 -0
  119. data/ext/carray_lazy.c +737 -0
  120. data/ext/carray_loop.c +88 -200
  121. data/ext/carray_mask.c +853 -158
  122. data/ext/carray_math_kernel.h +120 -0
  123. data/ext/carray_mathfunc.c +10 -241
  124. data/ext/carray_median_percentile.c +1257 -0
  125. data/ext/carray_memory_view.c +1650 -0
  126. data/ext/carray_operator.c +1525 -320
  127. data/ext/carray_order.c +664 -1394
  128. data/ext/carray_partition.c +416 -0
  129. data/ext/carray_random.c +518 -0
  130. data/ext/carray_scatter.c +357 -0
  131. data/ext/carray_slab.c +1219 -0
  132. data/ext/carray_slab.h +84 -0
  133. data/ext/carray_sort.c +829 -0
  134. data/ext/carray_sort_kernel.c +620 -0
  135. data/ext/carray_struct.c +695 -0
  136. data/ext/carray_test.c +343 -229
  137. data/ext/carray_undef.c +34 -17
  138. data/ext/carray_utils.c +175 -74
  139. data/ext/extconf.rb +234 -55
  140. data/ext/mk_call_cfunc.rb +671 -0
  141. data/ext/mkkernel.rb +9096 -0
  142. data/ext/ruby_carray.c +211 -108
  143. data/ext/version.h +4 -14
  144. data/ext/version.rb +5 -13
  145. data/lib/carray/arrow_tensor.rb +401 -0
  146. data/lib/carray/attribute.rb +166 -0
  147. data/lib/carray/autoload_carray.rb +239 -0
  148. data/lib/carray/autoload_method_extension.rb +45 -0
  149. data/lib/carray/axis_group.rb +711 -0
  150. data/lib/carray/basics.rb +481 -0
  151. data/lib/carray/bincount_nd.rb +358 -0
  152. data/lib/carray/block_iterator.rb +614 -0
  153. data/lib/carray/boolean_reduce.rb +109 -0
  154. data/lib/carray/categorical.rb +561 -0
  155. data/lib/carray/categorical_iterator.rb +1084 -0
  156. data/lib/carray/complex.rb +150 -0
  157. data/lib/carray/conditional.rb +216 -0
  158. data/lib/carray/const_string.rb +228 -0
  159. data/lib/carray/construct.rb +160 -328
  160. data/lib/carray/core_extensions.rb +297 -0
  161. data/lib/carray/data_type_extension.rb +250 -0
  162. data/lib/carray/fixlen_string.rb +95 -0
  163. data/lib/carray/frame/concat.rb +132 -0
  164. data/lib/carray/frame/convert.rb +95 -0
  165. data/lib/carray/frame/csv_parser.rb +211 -0
  166. data/lib/carray/frame/frame.rb +642 -0
  167. data/lib/carray/frame/group.rb +186 -0
  168. data/lib/carray/frame/io.rb +321 -0
  169. data/lib/carray/frame/join.rb +248 -0
  170. data/lib/carray/frame/records.rb +99 -0
  171. data/lib/carray/frame/sort.rb +113 -0
  172. data/lib/carray/frame/verbs.rb +316 -0
  173. data/lib/carray/frame.rb +16 -0
  174. data/lib/carray/fuse_source.rb +123 -0
  175. data/lib/carray/fusion.rb +218 -0
  176. data/lib/carray/histogram.rb +512 -0
  177. data/lib/carray/inspect.rb +37 -26
  178. data/lib/carray/iterator.rb +58 -349
  179. data/lib/carray/lazy.rb +941 -0
  180. data/lib/carray/mask_gap_fill.rb +200 -0
  181. data/lib/carray/math.rb +78 -342
  182. data/lib/carray/meld_reduce.rb +289 -0
  183. data/lib/carray/methods/align_addr.rb +116 -0
  184. data/lib/carray/methods/bin.rb +128 -0
  185. data/lib/carray/methods/bincount.rb +87 -0
  186. data/lib/carray/methods/bit_string.rb +92 -0
  187. data/lib/carray/methods/broadcast.rb +63 -0
  188. data/lib/carray/methods/choose.rb +39 -0
  189. data/lib/carray/methods/composition.rb +280 -0
  190. data/lib/carray/methods/gather_nd.rb +206 -0
  191. data/lib/carray/methods/index.rb +39 -0
  192. data/lib/carray/methods/insert_block.rb +99 -0
  193. data/lib/carray/methods/is_in.rb +141 -0
  194. data/lib/carray/methods/join.rb +90 -0
  195. data/lib/carray/methods/locate_addr.rb +52 -0
  196. data/lib/carray/methods/mask_duplicates.rb +41 -0
  197. data/lib/carray/methods/meshgrid.rb +90 -0
  198. data/lib/carray/methods/mode.rb +126 -0
  199. data/lib/carray/methods/nunique.rb +46 -0
  200. data/lib/carray/methods/resize.rb +56 -0
  201. data/lib/carray/methods/snap.rb +161 -0
  202. data/lib/carray/methods/string_format.rb +57 -0
  203. data/lib/carray/methods/unique.rb +47 -0
  204. data/lib/carray/methods/value_counts.rb +71 -0
  205. data/lib/carray/mkmf.rb +124 -101
  206. data/lib/carray/runtime.rb +89 -0
  207. data/lib/carray/serialize.rb +478 -167
  208. data/lib/carray/slab_iterator.rb +305 -0
  209. data/lib/carray/stack.rb +291 -0
  210. data/lib/carray/string.rb +56 -180
  211. data/lib/carray/string_operation_extension.rb +289 -0
  212. data/lib/carray/struct.rb +335 -323
  213. data/lib/carray/struct_builder.rb +697 -0
  214. data/lib/carray/table.rb +41 -2
  215. data/lib/carray/time.rb +2654 -38
  216. data/lib/carray/window_iterator.rb +927 -0
  217. data/lib/carray.rb +55 -57
  218. data/yard-stubs/ca_obj_array.rb +385 -0
  219. data/yard-stubs/ca_obj_bitarray.rb +38 -0
  220. data/yard-stubs/ca_obj_bitfield.rb +43 -0
  221. data/yard-stubs/ca_obj_block.rb +73 -0
  222. data/yard-stubs/ca_obj_byte_swap.rb +56 -0
  223. data/yard-stubs/ca_obj_fake.rb +31 -0
  224. data/yard-stubs/ca_obj_farray.rb +32 -0
  225. data/yard-stubs/ca_obj_field.rb +45 -0
  226. data/yard-stubs/ca_obj_grid.rb +35 -0
  227. data/yard-stubs/ca_obj_refer.rb +72 -0
  228. data/yard-stubs/ca_obj_roll.rb +45 -0
  229. data/yard-stubs/ca_obj_shift.rb +43 -0
  230. data/yard-stubs/ca_obj_stride.rb +181 -0
  231. data/yard-stubs/ca_obj_tile.rb +29 -0
  232. data/yard-stubs/ca_obj_transpose.rb +40 -0
  233. data/yard-stubs/ca_obj_window.rb +49 -0
  234. data/yard-stubs/carray_access.rb +131 -0
  235. data/yard-stubs/carray_attribute.rb +246 -0
  236. data/yard-stubs/carray_broadcast.rb +37 -0
  237. data/yard-stubs/carray_cast.rb +489 -0
  238. data/yard-stubs/carray_class.rb +65 -0
  239. data/yard-stubs/carray_conversion.rb +76 -0
  240. data/yard-stubs/carray_copy.rb +79 -0
  241. data/yard-stubs/carray_core.rb +114 -0
  242. data/yard-stubs/carray_count.rb +79 -0
  243. data/yard-stubs/carray_element.rb +108 -0
  244. data/yard-stubs/carray_generate.rb +66 -0
  245. data/yard-stubs/carray_lazy.rb +23 -0
  246. data/yard-stubs/carray_loop.rb +140 -0
  247. data/yard-stubs/carray_mask.rb +259 -0
  248. data/yard-stubs/carray_math.rb +132 -0
  249. data/yard-stubs/carray_mathfunc.rb +45 -0
  250. data/yard-stubs/carray_median_percentile.rb +89 -0
  251. data/yard-stubs/carray_memory_view.rb +163 -0
  252. data/yard-stubs/carray_order.rb +312 -0
  253. data/yard-stubs/carray_random.rb +89 -0
  254. data/yard-stubs/carray_scatter.rb +106 -0
  255. data/yard-stubs/carray_slab.rb +57 -0
  256. data/yard-stubs/carray_sort.rb +163 -0
  257. data/yard-stubs/carray_test.rb +85 -0
  258. data/yard-stubs/carray_undef.rb +64 -0
  259. data/yard-stubs/carray_utils.rb +97 -0
  260. data/yard-stubs/ruby_carray.rb +193 -0
  261. metadata +220 -138
  262. data/Rakefile +0 -51
  263. data/TODO.md +0 -18
  264. data/ext/ca_iter_block.c +0 -257
  265. data/ext/ca_iter_dimension.c +0 -299
  266. data/ext/ca_iter_window.c +0 -214
  267. data/ext/ca_obj_mapping.c +0 -644
  268. data/ext/ca_obj_unbound_repeat.c +0 -529
  269. data/ext/carray_iterator.c +0 -641
  270. data/ext/carray_math.rb +0 -850
  271. data/ext/carray_numeric.c +0 -259
  272. data/ext/carray_sort_addr.c +0 -254
  273. data/ext/carray_stat.c +0 -2100
  274. data/ext/carray_stat_proc.rb +0 -1999
  275. data/ext/mkmath.rb +0 -741
  276. data/ext/ruby_ccomplex.c +0 -509
  277. data/ext/ruby_float_func.c +0 -86
  278. data/lib/carray/array.rb +0 -8
  279. data/lib/carray/autoload/autoload_base.rb +0 -19
  280. data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
  281. data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
  282. data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
  283. data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
  284. data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
  285. data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
  286. data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
  287. data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
  288. data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
  289. data/lib/carray/autoload/autoload_gem_random.rb +0 -8
  290. data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
  291. data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
  292. data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
  293. data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
  294. data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
  295. data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
  296. data/lib/carray/autoload/autoload_object_link.rb +0 -1
  297. data/lib/carray/autoload/autoload_object_pack.rb +0 -2
  298. data/lib/carray/autoload.rb +0 -141
  299. data/lib/carray/basic.rb +0 -191
  300. data/lib/carray/broadcast.rb +0 -101
  301. data/lib/carray/compose.rb +0 -315
  302. data/lib/carray/convert.rb +0 -115
  303. data/lib/carray/info.rb +0 -110
  304. data/lib/carray/io/imagemagick.rb +0 -235
  305. data/lib/carray/mask.rb +0 -102
  306. data/lib/carray/math/histogram.rb +0 -177
  307. data/lib/carray/math/recurrence.rb +0 -93
  308. data/lib/carray/object/ca_obj_iterator.rb +0 -50
  309. data/lib/carray/object/ca_obj_link.rb +0 -50
  310. data/lib/carray/object/ca_obj_pack.rb +0 -99
  311. data/lib/carray/obsolete.rb +0 -256
  312. data/lib/carray/ordering.rb +0 -181
  313. data/lib/carray/testing.rb +0 -51
  314. data/lib/carray/transform.rb +0 -109
  315. data/mailmap +0 -1
  316. data/misc/Methods.ja.md +0 -182
  317. data/misc/NOTE +0 -51
  318. data/spec/Classes/CABitfield_spec.rb +0 -58
  319. data/spec/Classes/CABlockIterator_spec.rb +0 -114
  320. data/spec/Classes/CABlock_spec.rb +0 -205
  321. data/spec/Classes/CAField_spec.rb +0 -39
  322. data/spec/Classes/CAGrid_spec.rb +0 -75
  323. data/spec/Classes/CAMap_spec.rb +0 -0
  324. data/spec/Classes/CAMapping_spec.rb +0 -105
  325. data/spec/Classes/CAObject_attribute_spec.rb +0 -33
  326. data/spec/Classes/CAObject_spec.rb +0 -33
  327. data/spec/Classes/CARefer_spec.rb +0 -93
  328. data/spec/Classes/CARepeat_spec.rb +0 -65
  329. data/spec/Classes/CASelect_spec.rb +0 -22
  330. data/spec/Classes/CAShift_spec.rb +0 -16
  331. data/spec/Classes/CAStruct_spec.rb +0 -71
  332. data/spec/Classes/CATranspose_spec.rb +0 -60
  333. data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
  334. data/spec/Classes/CAWindow_spec.rb +0 -54
  335. data/spec/Classes/CAWrap_spec.rb +0 -8
  336. data/spec/Classes/CArray_spec.rb +0 -184
  337. data/spec/Classes/CScalar_spec.rb +0 -55
  338. data/spec/Features/feature_130_spec.rb +0 -19
  339. data/spec/Features/feature_attributes_spec.rb +0 -280
  340. data/spec/Features/feature_boolean_spec.rb +0 -98
  341. data/spec/Features/feature_broadcast.rb +0 -116
  342. data/spec/Features/feature_cast_function.rb +0 -19
  343. data/spec/Features/feature_cast_spec.rb +0 -33
  344. data/spec/Features/feature_class_spec.rb +0 -84
  345. data/spec/Features/feature_complex_spec.rb +0 -42
  346. data/spec/Features/feature_composite_spec.rb +0 -124
  347. data/spec/Features/feature_convert_spec.rb +0 -46
  348. data/spec/Features/feature_copy_spec.rb +0 -123
  349. data/spec/Features/feature_creation_spec.rb +0 -84
  350. data/spec/Features/feature_element_spec.rb +0 -144
  351. data/spec/Features/feature_extream_spec.rb +0 -54
  352. data/spec/Features/feature_generate_spec.rb +0 -74
  353. data/spec/Features/feature_index_spec.rb +0 -69
  354. data/spec/Features/feature_mask_spec.rb +0 -580
  355. data/spec/Features/feature_math_spec.rb +0 -97
  356. data/spec/Features/feature_order_spec.rb +0 -146
  357. data/spec/Features/feature_ref_store_spec.rb +0 -209
  358. data/spec/Features/feature_serialization_spec.rb +0 -125
  359. data/spec/Features/feature_stat_spec.rb +0 -397
  360. data/spec/Features/feature_virtual_spec.rb +0 -48
  361. data/spec/Features/method_eq_spec.rb +0 -81
  362. data/spec/Features/method_is_nan_spec.rb +0 -12
  363. data/spec/Features/method_map_spec.rb +0 -54
  364. data/spec/Features/method_max_with.rb +0 -20
  365. data/spec/Features/method_min_with.rb +0 -19
  366. data/spec/Features/method_ne_spec.rb +0 -18
  367. data/spec/Features/method_project_spec.rb +0 -188
  368. data/spec/Features/method_ref_spec.rb +0 -27
  369. data/spec/Features/method_round_spec.rb +0 -11
  370. data/spec/Features/method_s_linspace_spec.rb +0 -48
  371. data/spec/Features/method_s_span_spec.rb +0 -14
  372. data/spec/Features/method_seq_spec.rb +0 -47
  373. data/spec/Features/method_sort_with.rb +0 -43
  374. data/spec/Features/method_sorted_with.rb +0 -29
  375. data/spec/Features/method_span_spec.rb +0 -42
  376. data/spec/Features/method_wrap_readonly_spec.rb +0 -43
  377. data/spec/UnitTest/test_CAVirtual.rb +0 -214
  378. data/spec/spec_all.rb +0 -10
  379. data/utils/ca_ase.rb +0 -21
  380. data/utils/ca_methods.rb +0 -15
  381. data/utils/cast_checker.rb +0 -30
  382. data/utils/convert_test.rb +0 -73
  383. data/utils/extract_yard.rb +0 -22
  384. data/utils/guess_shape.rb +0 -76
  385. data/utils/monkey_patch_methods.rb +0 -62
  386. data/utils/remove_resource_fork.sh +0 -5
data/ext/carray_lazy.c ADDED
@@ -0,0 +1,737 @@
1
+ /* ---------------------------------------------------------------------------
2
+
3
+ CALazyMarker view + the shared lazy arena.
4
+
5
+ CALazyMarker is a zero-cost marker that holds a CArray entity and
6
+ causes `.sqrt` / `.sin` / etc. on it to build a lazy CAMonOp tree
7
+ instead of evaluating eagerly. A consumer op walks marker->parent
8
+ to drop the marker from the tree, so the marker itself is transient
9
+ — Ruby may keep a reference and re-consume (`m = a.lazy; m.sqrt +
10
+ m.sin`); the marker never mutates during consumption. Otherwise it
11
+ is a pure pass-through: every CArray operation delegates to the
12
+ parent. Detection happens via ca_is_lazy_view().
13
+
14
+ ca_lazy_arena is the shared slot-pool arena that CAMonOp / CABinOp
15
+ / CAMonCmp / CABinCmp use for scratch during to_ca; slot buffers
16
+ stay alive across calls to amortise mmap into steady-state reuse.
17
+
18
+ ---------------------------------------------------------------------------- */
19
+
20
+ #include "carray.h"
21
+ #include "carray_internal.h" /* ca_lazy_arena_*, ca_is_lazy_view */
22
+
23
+ int8_t CA_OBJ_LAZY_MARKER;
24
+ VALUE rb_cCALazyMarker;
25
+
26
+ typedef struct CALazyMarker {
27
+ int16_t obj_type;
28
+ int8_t data_type;
29
+ int8_t ndim;
30
+ int32_t flags;
31
+ ca_size_t bytes;
32
+ ca_size_t elements;
33
+ ca_size_t *dim;
34
+ char *ptr;
35
+ CArray *mask;
36
+ char *_pool; /* framework-managed pool buffer (NULL = legacy ALLOC_N path). */
37
+ CArray *parent;
38
+ uint32_t attach;
39
+ uint8_t nosync;
40
+ } CALazyMarker;
41
+
42
+ static size_t
43
+ ca_lazy_marker_dsize (const void *ap)
44
+ {
45
+ const CALazyMarker *ca = (const CALazyMarker *) ap;
46
+ return sizeof(CALazyMarker) + ca->ndim * sizeof(ca_size_t);
47
+ }
48
+
49
+ const rb_data_type_t calazy_marker_data_type = {
50
+ .parent = &caview_data_type,
51
+ .wrap_struct_name = "CALazyMarker",
52
+ .function = {
53
+ .dmark = ca_mark,
54
+ .dfree = ca_free,
55
+ .dsize = ca_lazy_marker_dsize,
56
+ .dcompact = NULL
57
+ },
58
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
59
+ };
60
+
61
+ /* ------------------------------------------------------------------- */
62
+
63
+ static int
64
+ ca_lazy_marker_setup (CALazyMarker *ca, CArray *parent)
65
+ {
66
+ ca->obj_type = CA_OBJ_LAZY_MARKER;
67
+ ca->data_type = parent->data_type;
68
+ ca->flags = 0;
69
+ ca->ndim = parent->ndim;
70
+ ca->bytes = parent->bytes;
71
+ ca->elements = parent->elements;
72
+ ca->ptr = NULL;
73
+ ca->mask = NULL;
74
+ ca->dim = ALLOC_N(ca_size_t, parent->ndim);
75
+ ca->parent = parent;
76
+ ca->attach = 0;
77
+ ca->nosync = 0;
78
+
79
+ memcpy(ca->dim, parent->dim, parent->ndim * sizeof(ca_size_t));
80
+
81
+ if ( ca_has_mask(parent) ) {
82
+ ca_create_mask(ca);
83
+ }
84
+
85
+ if ( ca_is_scalar(parent) ) {
86
+ ca_set_flag(ca, CA_FLAG_SCALAR);
87
+ }
88
+
89
+ /* CAREFUL: the marker must be read-only. CArray.fuse depends on
90
+ destructive ops being rejected against a marker, and lazy views
91
+ as a family carry CA_FLAG_READ_ONLY (same shape as CAFake).
92
+ Without this flag, `m[i] = x` would silently write through to
93
+ the parent and violate the shadow semantics. */
94
+ ca_set_flag(ca, CA_FLAG_READ_ONLY);
95
+
96
+ /* Storage-identical wrapper: the kernel_iterator entry strip and the
97
+ view-creation lift both ask for this. */
98
+ ca_set_flag(ca, CA_FLAG_IS_LAZY_MARKER);
99
+
100
+ return 0;
101
+ }
102
+
103
+ CALazyMarker *
104
+ ca_lazy_marker_new (CArray *parent)
105
+ {
106
+ CALazyMarker *ca = ALLOC(CALazyMarker);
107
+ ca_lazy_marker_setup(ca, parent);
108
+ return ca;
109
+ }
110
+
111
+ static void
112
+ free_ca_lazy_marker (void *ap)
113
+ {
114
+ CALazyMarker *ca = (CALazyMarker *) ap;
115
+ if ( ca != NULL ) {
116
+ ca_free(ca->mask);
117
+ xfree(ca->dim);
118
+ xfree(ca);
119
+ }
120
+ }
121
+
122
+ /* ------------------------------------------------------------------- */
123
+ /* Pass-through operations: everything delegates to parent. */
124
+ /* ------------------------------------------------------------------- */
125
+
126
+ static void *
127
+ ca_lazy_marker_func_clone (void *ap)
128
+ {
129
+ CALazyMarker *ca = (CALazyMarker *) ap;
130
+ return ca_lazy_marker_new(ca->parent);
131
+ }
132
+
133
+ static void
134
+ ca_lazy_marker_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
135
+ {
136
+ CALazyMarker *ca = (CALazyMarker *) ap;
137
+ if ( dir == CA_XFER_GET ) {
138
+ ca_fetch_index(ca->parent, idx, data);
139
+ } else {
140
+ ca_store_index(ca->parent, idx, data);
141
+ }
142
+ }
143
+
144
+ static void
145
+ ca_lazy_marker_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
146
+ void *data, int dir)
147
+ {
148
+ CALazyMarker *ca = (CALazyMarker *) ap;
149
+ ca_xfer_addrs(ca->parent, n, addrs, data, dir);
150
+ }
151
+
152
+ static void
153
+ ca_lazy_marker_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
154
+ ca_size_t *strides, void *data, int dir)
155
+ {
156
+ CALazyMarker *ca = (CALazyMarker *) ap;
157
+ ca_xfer_stride(ca->parent, starts, counts, strides, data, dir);
158
+ }
159
+
160
+ static void
161
+ ca_lazy_marker_func_xfer_all (void *ap, void *data, int dir)
162
+ {
163
+ CALazyMarker *ca = (CALazyMarker *) ap;
164
+ ca_xfer_all(ca->parent, data, dir);
165
+ }
166
+
167
+ static void
168
+ ca_lazy_marker_func_attach (void *ap)
169
+ {
170
+ CALazyMarker *ca = (CALazyMarker *) ap;
171
+ ca_attach(ca->parent);
172
+ ca->ptr = ca->parent->ptr; /* alias */
173
+ }
174
+
175
+ static void
176
+ ca_lazy_marker_func_sync (void *ap)
177
+ {
178
+ CALazyMarker *ca = (CALazyMarker *) ap;
179
+ ca_sync(ca->parent);
180
+ }
181
+
182
+ static void
183
+ ca_lazy_marker_func_detach (void *ap)
184
+ {
185
+ CALazyMarker *ca = (CALazyMarker *) ap;
186
+ ca->ptr = NULL;
187
+ ca_detach(ca->parent);
188
+ }
189
+
190
+ static void
191
+ ca_lazy_marker_func_allocate (void *ap)
192
+ {
193
+ CALazyMarker *ca = (CALazyMarker *) ap;
194
+ ca_allocate(ca->parent);
195
+ ca->ptr = ca->parent->ptr; /* alias */
196
+ }
197
+
198
+ static void
199
+ ca_lazy_marker_func_fill_data (void *ap, void *ptr)
200
+ {
201
+ CALazyMarker *ca = (CALazyMarker *) ap;
202
+ ca_fill(ca->parent, ptr);
203
+ }
204
+
205
+ static void
206
+ ca_lazy_marker_func_create_mask (void *ap)
207
+ {
208
+ CALazyMarker *ca = (CALazyMarker *) ap;
209
+ if ( ! ca->parent->mask ) {
210
+ ca_create_mask(ca->parent);
211
+ }
212
+ ca->mask =
213
+ (CArray *) ca_refer_new(ca->parent->mask,
214
+ CA_BOOLEAN, ca->ndim, ca->dim, 0, 0);
215
+ }
216
+
217
+ ca_operation_function_t ca_lazy_marker_func = {
218
+ -1, /* CA_OBJ_LAZY_MARKER, set at install time */
219
+ CA_VIEW_ARRAY,
220
+ free_ca_lazy_marker,
221
+ ca_lazy_marker_func_clone,
222
+ ca_lazy_marker_func_allocate,
223
+ ca_lazy_marker_func_attach,
224
+ ca_lazy_marker_func_sync,
225
+ ca_lazy_marker_func_detach,
226
+ ca_lazy_marker_func_fill_data,
227
+ ca_lazy_marker_func_create_mask,
228
+ ca_lazy_marker_func_xfer_index,
229
+ ca_lazy_marker_func_xfer_addrs,
230
+ NULL, /* fold_stride: not implemented */
231
+ ca_lazy_marker_func_xfer_stride,
232
+ ca_lazy_marker_func_xfer_all,
233
+ };
234
+
235
+ /* ------------------------------------------------------------------- */
236
+
237
+ VALUE
238
+ rb_ca_lazy_marker_new (VALUE cary)
239
+ {
240
+ volatile VALUE obj;
241
+ CArray *parent;
242
+ CALazyMarker *ca;
243
+ rb_check_carray_object(cary);
244
+ TypedData_Get_Struct(cary, CArray, &carray_data_type, parent);
245
+ ca = ca_lazy_marker_new(parent);
246
+ obj = ca_wrap_struct(ca);
247
+ rb_ca_set_parent(obj, cary);
248
+ return obj;
249
+ }
250
+
251
+ /* CArray#lazy — return a zero-cost CALazyMarker wrapping self. Subsequent
252
+ * element-wise ops (.sqrt / .sin / .+ / ...) build a lazy CAMonOp / CABinOp
253
+ * tree instead of evaluating eagerly; `.to_ca` materialises. */
254
+ VALUE
255
+ rb_ca_lazy (VALUE self)
256
+ {
257
+ return rb_ca_lazy_marker_new(self);
258
+ }
259
+
260
+ /* ------------------------------------------------------------------- */
261
+ /* CA_OBJECT GC guard */
262
+ /* ------------------------------------------------------------------- */
263
+
264
+ /* A CA_OBJECT cell is a VALUE. While a view materialises into a raw
265
+ buffer -- the destination `copy` will hand out, a view's own attach
266
+ buffer, or an arena scratch -- those VALUEs live somewhere the GC
267
+ cannot see: an xmalloc'd block owned by no Ruby object. For every
268
+ other data_type that is harmless, but an object-lane kernel calls
269
+ rb_funcall per cell, and any of those can collect. The results
270
+ written so far are then freed under the buffer, and the array comes
271
+ back holding recycled slots: wrong values, and a crash once a slot is
272
+ reused as something else.
273
+
274
+ So such buffers are registered here for the duration and marked from
275
+ a hidden guard object that lives as long as the process. Two kinds
276
+ are registered:
277
+
278
+ holds a contiguous run of cells, pushed and popped around a
279
+ whole-view transfer or the handoff to a Ruby owner
280
+ slots an arena scratch, tagged at acquire and held until release
281
+
282
+ Only contiguous runs are held. A strided window would cover the same
283
+ cells but its extent has to be taken on trust from the caller's
284
+ strides, and a window wider than the buffer behind it would be filled
285
+ and marked out of bounds. The whole-view entry knows the extent
286
+ exactly, and every partial transfer writes inside it. */
287
+
288
+ #define CA_GC_HOLD_MAX 64
289
+
290
+ typedef struct ca_gc_hold {
291
+ VALUE *ptr;
292
+ ca_size_t elements;
293
+ } ca_gc_hold_t;
294
+
295
+ static ca_gc_hold_t ca_gc_holds[CA_GC_HOLD_MAX];
296
+ static int ca_gc_hold_depth = 0;
297
+
298
+ static void ca_lazy_arena_mark_object_slots (void);
299
+
300
+ /* Returns the depth to hand back to ca_gc_hold_pop_to, so nested holds
301
+ unwind in order. Pushing is best-effort at the ceiling: a chain
302
+ deeper than CA_GC_HOLD_MAX loses the protection for its innermost
303
+ buffers rather than raising in the middle of a materialise. The cells
304
+ must already be valid VALUEs -- the caller fills a fresh buffer with
305
+ Qnil first. */
306
+ int
307
+ ca_gc_hold_push (void *ptr, ca_size_t n_elements)
308
+ {
309
+ int depth = ca_gc_hold_depth;
310
+
311
+ if ( depth >= CA_GC_HOLD_MAX || n_elements <= 0 || ptr == NULL ) {
312
+ return -1;
313
+ }
314
+ ca_gc_holds[depth].ptr = (VALUE *) ptr;
315
+ ca_gc_holds[depth].elements = n_elements;
316
+ ca_gc_hold_depth = depth + 1;
317
+ return depth;
318
+ }
319
+
320
+ void
321
+ ca_gc_hold_pop_to (int depth)
322
+ {
323
+ if ( depth >= 0 && depth < ca_gc_hold_depth ) {
324
+ ca_gc_hold_depth = depth;
325
+ }
326
+ }
327
+
328
+ static VALUE ca_gc_guard = Qnil;
329
+
330
+ /* The wrapped pointer must be non-NULL: Ruby's GC skips the mark
331
+ function of a TypedData whose data pointer is NULL. */
332
+ static int ca_gc_guard_body = 0;
333
+
334
+ static void
335
+ ca_gc_guard_mark (void *ptr)
336
+ {
337
+ int i;
338
+ (void) ptr;
339
+ for ( i = 0; i < ca_gc_hold_depth; i++ ) {
340
+ VALUE *p = ca_gc_holds[i].ptr;
341
+ ca_size_t n = ca_gc_holds[i].elements;
342
+ while ( n-- ) rb_gc_mark(*p++);
343
+ }
344
+ ca_lazy_arena_mark_object_slots();
345
+ }
346
+
347
+ static void
348
+ ca_gc_guard_free (void *ptr)
349
+ {
350
+ (void) ptr;
351
+ }
352
+
353
+ static const rb_data_type_t ca_gc_guard_data_type = {
354
+ "carray_object_gc_guard",
355
+ { ca_gc_guard_mark, ca_gc_guard_free, NULL, },
356
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
357
+ };
358
+
359
+ /* ------------------------------------------------------------------- */
360
+ /* ca_lazy_arena */
361
+ /* ------------------------------------------------------------------- */
362
+
363
+ /* Slot-pool arena. Each slot is an independent xmalloc'd buffer that
364
+ stays alive across to_ca calls; acquire / release toggles the
365
+ in_use flag, not the buffer itself, so simultaneous nested acquires
366
+ (deep CABinOp chain) hold stable pointers. A single-cursor + LIFO
367
+ stack design would force realloc when an outer acquire grew the
368
+ stack while an inner acquire still held the previous base — the
369
+ slot pool sidesteps that by design.
370
+
371
+ Steady state:
372
+ first to_ca call xmalloc N slots
373
+ subsequent calls reuse warm slots
374
+ best-fit allocation keeps small requests (mask scratch) out of
375
+ large data slots
376
+
377
+ Footprint: 32 slots × max observed size (up to 32 × max_slab_bytes;
378
+ e.g. N=1M f64, depth-8 chain = 8 × 8MB = 64MB kept resident). When
379
+ the slot pool is exhausted the acquire raises: this signals a
380
+ programming error or a pathological chain rather than a graceful
381
+ fallback.
382
+
383
+ CAREFUL: single-thread only -- thread-safety across concurrent access
384
+ is not a goal (see guides/devel/04_attach_lifecycle.md). The arena is
385
+ process-global static state. */
386
+
387
+ #define CA_LAZY_ARENA_SLOTS 32
388
+
389
+ typedef struct ca_lazy_arena_slot {
390
+ void *ptr; /* xmalloc'd buffer, NULL = unused-and-unallocated */
391
+ ca_size_t bytes; /* allocated size of ptr (= capacity of this slot) */
392
+ int in_use; /* 1 = currently acquired, 0 = available */
393
+ ca_size_t object_elements; /* >0 = holds VALUEs; marked while in_use */
394
+ } ca_lazy_arena_slot_t;
395
+
396
+ typedef struct ca_lazy_arena {
397
+ ca_lazy_arena_slot_t slots[CA_LAZY_ARENA_SLOTS];
398
+ int depth; /* enter/exit nest count */
399
+ ca_size_t debug_acquire_count; /* test instrumentation */
400
+ ca_size_t debug_xmalloc_count; /* test: how many xmallocs total */
401
+ ca_size_t debug_reuse_count; /* test: how many acquire = reuse */
402
+ } ca_lazy_arena_t;
403
+
404
+ static ca_lazy_arena_t ca_lazy_arena = {0};
405
+
406
+ void
407
+ ca_lazy_arena_enter (void)
408
+ {
409
+ if ( ca_lazy_arena.depth == 0 ) {
410
+ /* Top-level reset to recover from a prior exception that left
411
+ slots in_use without a matching release. Safe because
412
+ depth==0 means no nested caller is mid-acquire. */
413
+ int i;
414
+ for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
415
+ ca_lazy_arena.slots[i].in_use = 0;
416
+ ca_lazy_arena.slots[i].object_elements = 0;
417
+ }
418
+ }
419
+ ca_lazy_arena.depth++;
420
+ }
421
+
422
+ void
423
+ ca_lazy_arena_exit (void)
424
+ {
425
+ if ( ca_lazy_arena.depth > 0 ) {
426
+ ca_lazy_arena.depth--;
427
+ }
428
+ /* Keep all slot buffers, and leave in_use and the object tags alone.
429
+ A scratch can be acquired outside any enter/exit bracket -- a lazy
430
+ view's attach path does exactly that -- so an inner materialise
431
+ dropping to depth 0 is not evidence that nothing is held. Recovery
432
+ from an abandoned slot happens on the next depth-0 entry instead.
433
+
434
+ An abandoned object slot stays marked until then, which is a leak
435
+ but not a hazard: acquire only ever hands out a slot that is not in
436
+ use, so nothing overwrites it, and marking is what keeps its cells
437
+ from being collected in the first place. */
438
+ }
439
+
440
+ void *
441
+ ca_lazy_arena_acquire (ca_size_t bytes)
442
+ {
443
+ int i, best, empty;
444
+ ca_size_t best_bytes;
445
+
446
+ if ( bytes <= 0 ) bytes = 1;
447
+
448
+ ca_lazy_arena.debug_acquire_count++;
449
+
450
+ /* Pass 1: best-fit among unused warm slots (smallest >= bytes).
451
+ CAREFUL: ca_size_t is signed int64_t, so the "no match yet"
452
+ sentinel must be CA_LENGTH_MAX — NOT -1, which would compare
453
+ less than any real capacity. */
454
+ best = -1;
455
+ best_bytes = CA_LENGTH_MAX;
456
+ for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
457
+ ca_lazy_arena_slot_t *s = &ca_lazy_arena.slots[i];
458
+ if ( s->in_use ) continue;
459
+ if ( s->ptr == NULL ) continue;
460
+ if ( s->bytes < bytes ) continue;
461
+ if ( s->bytes < best_bytes ) {
462
+ best = i;
463
+ best_bytes = s->bytes;
464
+ }
465
+ }
466
+ if ( best >= 0 ) {
467
+ ca_lazy_arena.slots[best].in_use = 1;
468
+ ca_lazy_arena.slots[best].object_elements = 0;
469
+ ca_lazy_arena.debug_reuse_count++;
470
+ return ca_lazy_arena.slots[best].ptr;
471
+ }
472
+
473
+
474
+ /* Pass 2: empty slot — xmalloc a fresh buffer. */
475
+ empty = -1;
476
+ for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
477
+ if ( ca_lazy_arena.slots[i].ptr == NULL ) {
478
+ empty = i;
479
+ break;
480
+ }
481
+ }
482
+ if ( empty >= 0 ) {
483
+ ca_lazy_arena.slots[empty].ptr = xmalloc(bytes);
484
+ ca_lazy_arena.slots[empty].bytes = bytes;
485
+ ca_lazy_arena.slots[empty].in_use = 1;
486
+ ca_lazy_arena.slots[empty].object_elements = 0;
487
+ ca_lazy_arena.debug_xmalloc_count++;
488
+ return ca_lazy_arena.slots[empty].ptr;
489
+ }
490
+
491
+ /* Pass 3: all slots have ptrs. Find a non-in-use slot and grow it
492
+ (= xfree + xmalloc). This pays a mmap roundtrip but only on
493
+ slot pool exhaustion (= unusual size request). */
494
+ for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
495
+ if ( ! ca_lazy_arena.slots[i].in_use ) {
496
+ xfree(ca_lazy_arena.slots[i].ptr);
497
+ ca_lazy_arena.slots[i].ptr = xmalloc(bytes);
498
+ ca_lazy_arena.slots[i].bytes = bytes;
499
+ ca_lazy_arena.slots[i].in_use = 1;
500
+ ca_lazy_arena.slots[i].object_elements = 0;
501
+ ca_lazy_arena.debug_xmalloc_count++;
502
+ return ca_lazy_arena.slots[i].ptr;
503
+ }
504
+ }
505
+
506
+ /* Pass 4: all CA_LAZY_ARENA_SLOTS in use simultaneously. This is
507
+ a programming error or a pathological chain. Raise. */
508
+ rb_raise(rb_eRuntimeError,
509
+ "ca_lazy_arena_acquire: all %d slots in use simultaneously "
510
+ "(chain depth exceeds arena capacity)",
511
+ CA_LAZY_ARENA_SLOTS);
512
+ return NULL; /* unreachable */
513
+ }
514
+
515
+ void
516
+ ca_lazy_arena_release (void *ptr)
517
+ {
518
+ int i;
519
+ if ( ptr == NULL ) return;
520
+ for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
521
+ if ( ca_lazy_arena.slots[i].ptr == ptr ) {
522
+ ca_lazy_arena.slots[i].in_use = 0;
523
+ ca_lazy_arena.slots[i].object_elements = 0;
524
+ return;
525
+ }
526
+ }
527
+ /* Unknown ptr — could indicate caller mixed arena release with a
528
+ non-arena pointer. Be lenient (= no raise) to avoid masking
529
+ genuine bugs at unrelated callsites. */
530
+ }
531
+
532
+ /* Acquire a scratch that will hold `n_elements` VALUEs.
533
+
534
+ Unlike the byte-sized acquire this one Qnil-fills the buffer and tags
535
+ the slot, so the cells stay markable for as long as the caller holds
536
+ it. Object-lane kernels run rb_funcall over their operands, so a
537
+ scratch pulled from a lazy operand holds VALUEs that exist nowhere
538
+ else until the kernel has consumed them. */
539
+ void *
540
+ ca_lazy_arena_acquire_object (ca_size_t n_elements)
541
+ {
542
+ void *ptr;
543
+ int i;
544
+
545
+ if ( n_elements < 0 ) n_elements = 0;
546
+ ptr = ca_lazy_arena_acquire(n_elements * (ca_size_t) sizeof(VALUE));
547
+
548
+ {
549
+ VALUE *p = (VALUE *) ptr;
550
+ ca_size_t n = n_elements;
551
+ while ( n-- ) *p++ = Qnil;
552
+ }
553
+
554
+ for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
555
+ if ( ca_lazy_arena.slots[i].ptr == ptr ) {
556
+ ca_lazy_arena.slots[i].object_elements = n_elements;
557
+ break;
558
+ }
559
+ }
560
+ return ptr;
561
+ }
562
+
563
+ static void
564
+ ca_lazy_arena_mark_object_slots (void)
565
+ {
566
+ int i;
567
+ for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
568
+ ca_lazy_arena_slot_t *s = &ca_lazy_arena.slots[i];
569
+ ca_size_t n;
570
+ VALUE *p;
571
+ if ( ! s->in_use || s->object_elements <= 0 ) continue;
572
+ p = (VALUE *) s->ptr;
573
+ n = s->object_elements;
574
+ while ( n-- ) rb_gc_mark(*p++);
575
+ }
576
+ }
577
+
578
+ /* Test instrumentation — exposed to Ruby for the arena smoke and
579
+ regression assertions. Not user-facing API. */
580
+ static VALUE
581
+ rb_ca_lazy_arena_s_reset_counters (VALUE klass)
582
+ {
583
+ ca_lazy_arena.debug_acquire_count = 0;
584
+ ca_lazy_arena.debug_xmalloc_count = 0;
585
+ ca_lazy_arena.debug_reuse_count = 0;
586
+ return Qnil;
587
+ }
588
+
589
+ static VALUE
590
+ rb_ca_lazy_arena_s_acquire_count (VALUE klass)
591
+ {
592
+ return SIZE2NUM(ca_lazy_arena.debug_acquire_count);
593
+ }
594
+
595
+ static VALUE
596
+ rb_ca_lazy_arena_s_xmalloc_count (VALUE klass)
597
+ {
598
+ return SIZE2NUM(ca_lazy_arena.debug_xmalloc_count);
599
+ }
600
+
601
+ static VALUE
602
+ rb_ca_lazy_arena_s_reuse_count (VALUE klass)
603
+ {
604
+ return SIZE2NUM(ca_lazy_arena.debug_reuse_count);
605
+ }
606
+
607
+ static VALUE
608
+ rb_ca_lazy_arena_s_depth (VALUE klass)
609
+ {
610
+ return INT2NUM(ca_lazy_arena.depth);
611
+ }
612
+
613
+ static VALUE
614
+ rb_ca_lazy_arena_s_slot_in_use_count (VALUE klass)
615
+ {
616
+ int i, n = 0;
617
+ for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
618
+ if ( ca_lazy_arena.slots[i].in_use ) n++;
619
+ }
620
+ return INT2NUM(n);
621
+ }
622
+
623
+ /* Per-slot capacity sweep for observability tests. Returns an
624
+ Array of Integer bytes for every populated slot, so a test can
625
+ assert that mixed boolean (1 byte/cell) and f64 (8 byte/cell)
626
+ acquires develop distinct size classes under best-fit — otherwise
627
+ boolean acquires would sit in oversized f64 slots and force fresh
628
+ xmalloc for subsequent f64 acquires. */
629
+ static VALUE
630
+ rb_ca_lazy_arena_s_slot_capacities (VALUE klass)
631
+ {
632
+ int i;
633
+ VALUE arr = rb_ary_new();
634
+ (void) klass;
635
+ for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
636
+ if ( ca_lazy_arena.slots[i].ptr != NULL ) {
637
+ rb_ary_push(arr, SIZE2NUM(ca_lazy_arena.slots[i].bytes));
638
+ }
639
+ }
640
+ return arr;
641
+ }
642
+
643
+ /* ------------------------------------------------------------------- */
644
+
645
+ /* ca_lazy_wrap_scalar(other, self_ca) — wrap a non-CArray Ruby value
646
+ * as a 1-element CScalar whose data_type follows the array's family,
647
+ * mirroring the array+scalar branch of rb_ca_cast_self_or_other. The
648
+ * scalar takes on self's data_type so binops preserve self's
649
+ * precision (`f32_array + 2.5` stays f32).
650
+ *
651
+ * CA_OBJECT self -> CA_OBJECT
652
+ * CA_BOOLEAN self -> derive from other's Ruby type via
653
+ * ca_value_to_data_type (bool arithmetic is a
654
+ * corner case; eager widens the array, so we
655
+ * just let the scalar choose)
656
+ * numeric self -> self_ca->data_type
657
+ *
658
+ * Callers broadcast against the array via stride 0 in xfer_stride. */
659
+ VALUE
660
+ ca_lazy_wrap_scalar (VALUE other, CArray *self_ca)
661
+ {
662
+ int8_t dt;
663
+
664
+ if ( ca_is_object_type(self_ca) ) {
665
+ dt = CA_OBJECT;
666
+ }
667
+ else if ( self_ca->data_type == CA_BOOLEAN ) {
668
+ dt = ca_value_to_data_type(other);
669
+ }
670
+ else {
671
+ dt = self_ca->data_type;
672
+ }
673
+
674
+ return rb_cscalar_new_with_value(dt, 0, other);
675
+ }
676
+
677
+ static VALUE
678
+ rb_ca_lazy_marker_s_allocate (VALUE klass)
679
+ {
680
+ CALazyMarker *ca;
681
+ return TypedData_Make_Struct(klass, CALazyMarker,
682
+ &calazy_marker_data_type, ca);
683
+ }
684
+
685
+ static VALUE
686
+ rb_ca_lazy_marker_initialize_copy (VALUE self, VALUE other)
687
+ {
688
+ CALazyMarker *ca, *cs;
689
+ TypedData_Get_Struct(self, CALazyMarker, &calazy_marker_data_type, ca);
690
+ TypedData_Get_Struct(other, CALazyMarker, &calazy_marker_data_type, cs);
691
+ ca_lazy_marker_setup(ca, cs->parent);
692
+ return self;
693
+ }
694
+
695
+ void
696
+ Init_carray_lazy (void)
697
+ {
698
+ rb_cCALazyMarker = rb_define_class("CALazyMarker", rb_cCAView);
699
+
700
+ CA_OBJ_LAZY_MARKER = ca_install_obj_type(rb_cCALazyMarker,
701
+ &calazy_marker_data_type,
702
+ rb_cCArrayMask,
703
+ &carray_mask_data_type,
704
+ &ca_lazy_marker_func, sizeof(ca_lazy_marker_func));
705
+ rb_define_const(rb_cObject, "CA_OBJ_LAZY_MARKER",
706
+ INT2NUM(CA_OBJ_LAZY_MARKER));
707
+
708
+ rb_define_method(rb_cCArray, "lazy", rb_ca_lazy, 0);
709
+
710
+ /* Hidden, never collected: its mark function is what keeps the
711
+ in-flight CA_OBJECT buffers reachable. */
712
+ ca_gc_guard = TypedData_Wrap_Struct(rb_cObject, &ca_gc_guard_data_type,
713
+ &ca_gc_guard_body);
714
+ rb_gc_register_mark_object(ca_gc_guard);
715
+
716
+ rb_define_alloc_func(rb_cCALazyMarker, rb_ca_lazy_marker_s_allocate);
717
+ rb_define_method(rb_cCALazyMarker, "initialize_copy",
718
+ rb_ca_lazy_marker_initialize_copy, 1);
719
+
720
+ /* Arena test instrumentation (not user-facing). Bound on CArray
721
+ for symmetry with CAMonOp / CABinOp counters, but the arena is
722
+ a singleton process-wide state. */
723
+ rb_define_singleton_method(rb_cCArray, "__lazy_arena_reset_counters__",
724
+ rb_ca_lazy_arena_s_reset_counters, 0);
725
+ rb_define_singleton_method(rb_cCArray, "__lazy_arena_acquire_count__",
726
+ rb_ca_lazy_arena_s_acquire_count, 0);
727
+ rb_define_singleton_method(rb_cCArray, "__lazy_arena_xmalloc_count__",
728
+ rb_ca_lazy_arena_s_xmalloc_count, 0);
729
+ rb_define_singleton_method(rb_cCArray, "__lazy_arena_reuse_count__",
730
+ rb_ca_lazy_arena_s_reuse_count, 0);
731
+ rb_define_singleton_method(rb_cCArray, "__lazy_arena_depth__",
732
+ rb_ca_lazy_arena_s_depth, 0);
733
+ rb_define_singleton_method(rb_cCArray, "__lazy_arena_slot_in_use_count__",
734
+ rb_ca_lazy_arena_s_slot_in_use_count, 0);
735
+ rb_define_singleton_method(rb_cCArray, "__lazy_arena_slot_capacities__",
736
+ rb_ca_lazy_arena_s_slot_capacities, 0);
737
+ }