carray 2.0.1 → 3.0.0

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 (339) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +5 -25
  3. data/CHANGELOG.md +16 -0
  4. data/LICENSE +1 -1
  5. data/NEWS.md +3 -0
  6. data/README.md +128 -44
  7. data/carray.gemspec +22 -24
  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 +1208 -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 +125 -0
  15. data/ext/ca_binop_dispatch.h +159 -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 +241 -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 +4321 -0
  26. data/ext/ca_kernel_iterator.h +2603 -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 +809 -0
  33. data/ext/ca_obj_binop.c +892 -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 +965 -0
  39. data/ext/ca_obj_face.c +670 -0
  40. data/ext/ca_obj_face.h +247 -0
  41. data/ext/ca_obj_fake.c +228 -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 +306 -0
  45. data/ext/ca_obj_grid.c +858 -440
  46. data/ext/ca_obj_meld.c +1034 -0
  47. data/ext/ca_obj_moncmp.c +569 -0
  48. data/ext/ca_obj_monop.c +1111 -0
  49. data/ext/ca_obj_object.c +772 -297
  50. data/ext/ca_obj_record.c +468 -0
  51. data/ext/ca_obj_reduce.c +97 -82
  52. data/ext/ca_obj_refer.c +569 -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 +616 -0
  56. data/ext/ca_obj_select.c +344 -296
  57. data/ext/ca_obj_select_axis.c +1296 -0
  58. data/ext/ca_obj_shift.c +229 -791
  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 +2501 -0
  62. data/ext/ca_obj_string.c +268 -0
  63. data/ext/ca_obj_tile.c +614 -0
  64. data/ext/ca_obj_time.c +546 -0
  65. data/ext/ca_obj_timedelta.c +435 -0
  66. data/ext/ca_obj_transpose.c +62 -516
  67. data/ext/ca_obj_triop.c +746 -0
  68. data/ext/ca_obj_unbound_repeat.c +207 -240
  69. data/ext/ca_obj_window.c +1131 -563
  70. data/ext/ca_op_byte_swap.c +175 -0
  71. data/ext/ca_op_ipower.c +319 -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 +430 -0
  75. data/ext/ca_sweep_engine.h +157 -0
  76. data/ext/ca_transform_common.c +228 -0
  77. data/ext/ca_triop_dispatch.c +55 -0
  78. data/ext/ca_triop_dispatch.h +62 -0
  79. data/ext/carray.h +795 -402
  80. data/ext/carray_access.c +831 -711
  81. data/ext/carray_attribute.c +98 -329
  82. data/ext/carray_bincount.c +255 -0
  83. data/ext/carray_broadcast.c +283 -0
  84. data/ext/carray_call_cfunc.c +1360 -828
  85. data/ext/carray_call_cfunc.h +160 -0
  86. data/ext/carray_cast.c +1212 -301
  87. data/ext/carray_cast_func.rb +81 -40
  88. data/ext/carray_class.c +53 -63
  89. data/ext/carray_config.h +28 -0
  90. data/ext/carray_conversion.c +350 -346
  91. data/ext/carray_copy.c +156 -268
  92. data/ext/carray_core.c +1341 -198
  93. data/ext/carray_count.c +312 -0
  94. data/ext/carray_data_type.c +43 -19
  95. data/ext/carray_element.c +585 -213
  96. data/ext/carray_factorize.c +2542 -0
  97. data/ext/carray_generate.c +230 -559
  98. data/ext/carray_histogram.c +490 -0
  99. data/ext/carray_hold.c +228 -0
  100. data/ext/carray_index_classifier.c +1035 -0
  101. data/ext/carray_index_classifier.h +27 -0
  102. data/ext/carray_internal.h +120 -0
  103. data/ext/carray_kernels_bincmp.c +4445 -0
  104. data/ext/carray_kernels_binop.c +10979 -0
  105. data/ext/carray_kernels_init.c +36 -0
  106. data/ext/carray_kernels_map.c +3466 -0
  107. data/ext/carray_kernels_moncmp.c +2096 -0
  108. data/ext/carray_kernels_monop.c +18312 -0
  109. data/ext/carray_kernels_reduce_aggregate.c +25836 -0
  110. data/ext/carray_kernels_reduce_boolean.c +329 -0
  111. data/ext/carray_kernels_reduce_cumulative.c +14592 -0
  112. data/ext/carray_kernels_reduce_extreme.c +16947 -0
  113. data/ext/carray_kernels_reduce_variance.c +3909 -0
  114. data/ext/carray_kernels_scan.c +3692 -0
  115. data/ext/carray_kernels_search.c +32137 -0
  116. data/ext/carray_kernels_sort.c +10625 -0
  117. data/ext/carray_kernels_triop.c +1391 -0
  118. data/ext/carray_lazy.c +567 -0
  119. data/ext/carray_loop.c +88 -200
  120. data/ext/carray_mask.c +848 -154
  121. data/ext/carray_math_kernel.h +120 -0
  122. data/ext/carray_mathfunc.c +10 -241
  123. data/ext/carray_median_percentile.c +1257 -0
  124. data/ext/carray_memory_view.c +1625 -0
  125. data/ext/carray_operator.c +1526 -318
  126. data/ext/carray_order.c +664 -1394
  127. data/ext/carray_partition.c +416 -0
  128. data/ext/carray_random.c +518 -0
  129. data/ext/carray_scatter.c +357 -0
  130. data/ext/carray_slab.c +1219 -0
  131. data/ext/carray_slab.h +84 -0
  132. data/ext/carray_sort.c +829 -0
  133. data/ext/carray_sort_kernel.c +620 -0
  134. data/ext/carray_struct.c +695 -0
  135. data/ext/carray_test.c +343 -229
  136. data/ext/carray_undef.c +34 -17
  137. data/ext/carray_utils.c +175 -74
  138. data/ext/extconf.rb +216 -55
  139. data/ext/mk_call_cfunc.rb +480 -0
  140. data/ext/mkkernel.rb +8842 -0
  141. data/ext/ruby_carray.c +202 -101
  142. data/ext/version.h +5 -15
  143. data/ext/version.rb +5 -13
  144. data/lib/carray/arrow_tensor.rb +401 -0
  145. data/lib/carray/attribute.rb +166 -0
  146. data/lib/carray/autoload_carray.rb +220 -0
  147. data/lib/carray/autoload_method_extension.rb +44 -0
  148. data/lib/carray/axis_group.rb +711 -0
  149. data/lib/carray/basics.rb +481 -0
  150. data/lib/carray/bincount_nd.rb +358 -0
  151. data/lib/carray/block_iterator.rb +604 -0
  152. data/lib/carray/boolean_reduce.rb +109 -0
  153. data/lib/carray/categorical.rb +561 -0
  154. data/lib/carray/categorical_iterator.rb +1062 -0
  155. data/lib/carray/complex.rb +150 -0
  156. data/lib/carray/conditional.rb +216 -0
  157. data/lib/carray/const_string.rb +228 -0
  158. data/lib/carray/construct.rb +139 -328
  159. data/lib/carray/core_extensions.rb +240 -0
  160. data/lib/carray/data_type_extension.rb +233 -0
  161. data/lib/carray/fixlen_string.rb +95 -0
  162. data/lib/carray/frame/concat.rb +132 -0
  163. data/lib/carray/frame/convert.rb +95 -0
  164. data/lib/carray/frame/csv_parser.rb +211 -0
  165. data/lib/carray/frame/frame.rb +649 -0
  166. data/lib/carray/frame/group.rb +186 -0
  167. data/lib/carray/frame/io.rb +164 -0
  168. data/lib/carray/frame/join.rb +248 -0
  169. data/lib/carray/frame/records.rb +99 -0
  170. data/lib/carray/frame/sort.rb +113 -0
  171. data/lib/carray/frame/verbs.rb +299 -0
  172. data/lib/carray/frame.rb +16 -0
  173. data/lib/carray/histogram.rb +512 -0
  174. data/lib/carray/inspect.rb +37 -20
  175. data/lib/carray/iterator.rb +57 -349
  176. data/lib/carray/lazy.rb +889 -0
  177. data/lib/carray/mask_gap_fill.rb +200 -0
  178. data/lib/carray/math.rb +78 -342
  179. data/lib/carray/meld_reduce.rb +289 -0
  180. data/lib/carray/methods/align_addr.rb +116 -0
  181. data/lib/carray/methods/bin.rb +128 -0
  182. data/lib/carray/methods/bincount.rb +87 -0
  183. data/lib/carray/methods/bit_string.rb +92 -0
  184. data/lib/carray/methods/broadcast.rb +63 -0
  185. data/lib/carray/methods/choose.rb +39 -0
  186. data/lib/carray/methods/composition.rb +280 -0
  187. data/lib/carray/methods/gather_nd.rb +206 -0
  188. data/lib/carray/methods/index.rb +39 -0
  189. data/lib/carray/methods/insert_block.rb +99 -0
  190. data/lib/carray/methods/is_in.rb +141 -0
  191. data/lib/carray/methods/join.rb +90 -0
  192. data/lib/carray/methods/locate_addr.rb +47 -0
  193. data/lib/carray/methods/mask_duplicates.rb +41 -0
  194. data/lib/carray/methods/meshgrid.rb +91 -0
  195. data/lib/carray/methods/mode.rb +126 -0
  196. data/lib/carray/methods/nunique.rb +46 -0
  197. data/lib/carray/methods/resize.rb +56 -0
  198. data/lib/carray/methods/snap.rb +156 -0
  199. data/lib/carray/methods/string_format.rb +57 -0
  200. data/lib/carray/methods/unique.rb +47 -0
  201. data/lib/carray/methods/value_counts.rb +71 -0
  202. data/lib/carray/mkmf.rb +124 -101
  203. data/lib/carray/runtime.rb +108 -0
  204. data/lib/carray/serialize.rb +478 -167
  205. data/lib/carray/slab_iterator.rb +292 -0
  206. data/lib/carray/stack.rb +291 -0
  207. data/lib/carray/string.rb +56 -180
  208. data/lib/carray/string_operation_extension.rb +289 -0
  209. data/lib/carray/struct.rb +335 -323
  210. data/lib/carray/struct_builder.rb +697 -0
  211. data/lib/carray/table.rb +41 -2
  212. data/lib/carray/time.rb +2255 -38
  213. data/lib/carray/window_iterator.rb +655 -0
  214. data/lib/carray.rb +55 -57
  215. metadata +163 -130
  216. data/Rakefile +0 -51
  217. data/TODO.md +0 -18
  218. data/ext/ca_iter_block.c +0 -257
  219. data/ext/ca_iter_dimension.c +0 -299
  220. data/ext/ca_iter_window.c +0 -214
  221. data/ext/ca_obj_mapping.c +0 -644
  222. data/ext/carray_iterator.c +0 -641
  223. data/ext/carray_math.rb +0 -850
  224. data/ext/carray_numeric.c +0 -259
  225. data/ext/carray_sort_addr.c +0 -254
  226. data/ext/carray_stat.c +0 -2100
  227. data/ext/carray_stat_proc.rb +0 -1999
  228. data/ext/mkmath.rb +0 -741
  229. data/ext/ruby_ccomplex.c +0 -509
  230. data/ext/ruby_float_func.c +0 -86
  231. data/lib/carray/array.rb +0 -8
  232. data/lib/carray/autoload/autoload_base.rb +0 -19
  233. data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
  234. data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
  235. data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
  236. data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
  237. data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
  238. data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
  239. data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
  240. data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
  241. data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
  242. data/lib/carray/autoload/autoload_gem_random.rb +0 -8
  243. data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
  244. data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
  245. data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
  246. data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
  247. data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
  248. data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
  249. data/lib/carray/autoload/autoload_object_link.rb +0 -1
  250. data/lib/carray/autoload/autoload_object_pack.rb +0 -2
  251. data/lib/carray/autoload.rb +0 -141
  252. data/lib/carray/basic.rb +0 -191
  253. data/lib/carray/broadcast.rb +0 -101
  254. data/lib/carray/compose.rb +0 -315
  255. data/lib/carray/convert.rb +0 -115
  256. data/lib/carray/info.rb +0 -110
  257. data/lib/carray/io/imagemagick.rb +0 -235
  258. data/lib/carray/mask.rb +0 -102
  259. data/lib/carray/math/histogram.rb +0 -177
  260. data/lib/carray/math/recurrence.rb +0 -93
  261. data/lib/carray/object/ca_obj_iterator.rb +0 -50
  262. data/lib/carray/object/ca_obj_link.rb +0 -50
  263. data/lib/carray/object/ca_obj_pack.rb +0 -99
  264. data/lib/carray/obsolete.rb +0 -256
  265. data/lib/carray/ordering.rb +0 -181
  266. data/lib/carray/testing.rb +0 -51
  267. data/lib/carray/transform.rb +0 -109
  268. data/mailmap +0 -1
  269. data/misc/Methods.ja.md +0 -182
  270. data/misc/NOTE +0 -51
  271. data/spec/Classes/CABitfield_spec.rb +0 -58
  272. data/spec/Classes/CABlockIterator_spec.rb +0 -114
  273. data/spec/Classes/CABlock_spec.rb +0 -205
  274. data/spec/Classes/CAField_spec.rb +0 -39
  275. data/spec/Classes/CAGrid_spec.rb +0 -75
  276. data/spec/Classes/CAMap_spec.rb +0 -0
  277. data/spec/Classes/CAMapping_spec.rb +0 -105
  278. data/spec/Classes/CAObject_attribute_spec.rb +0 -33
  279. data/spec/Classes/CAObject_spec.rb +0 -33
  280. data/spec/Classes/CARefer_spec.rb +0 -93
  281. data/spec/Classes/CARepeat_spec.rb +0 -65
  282. data/spec/Classes/CASelect_spec.rb +0 -22
  283. data/spec/Classes/CAShift_spec.rb +0 -16
  284. data/spec/Classes/CAStruct_spec.rb +0 -71
  285. data/spec/Classes/CATranspose_spec.rb +0 -60
  286. data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
  287. data/spec/Classes/CAWindow_spec.rb +0 -54
  288. data/spec/Classes/CAWrap_spec.rb +0 -8
  289. data/spec/Classes/CArray_spec.rb +0 -184
  290. data/spec/Classes/CScalar_spec.rb +0 -55
  291. data/spec/Features/feature_130_spec.rb +0 -19
  292. data/spec/Features/feature_attributes_spec.rb +0 -280
  293. data/spec/Features/feature_boolean_spec.rb +0 -98
  294. data/spec/Features/feature_broadcast.rb +0 -116
  295. data/spec/Features/feature_cast_function.rb +0 -19
  296. data/spec/Features/feature_cast_spec.rb +0 -33
  297. data/spec/Features/feature_class_spec.rb +0 -84
  298. data/spec/Features/feature_complex_spec.rb +0 -42
  299. data/spec/Features/feature_composite_spec.rb +0 -124
  300. data/spec/Features/feature_convert_spec.rb +0 -46
  301. data/spec/Features/feature_copy_spec.rb +0 -123
  302. data/spec/Features/feature_creation_spec.rb +0 -84
  303. data/spec/Features/feature_element_spec.rb +0 -144
  304. data/spec/Features/feature_extream_spec.rb +0 -54
  305. data/spec/Features/feature_generate_spec.rb +0 -74
  306. data/spec/Features/feature_index_spec.rb +0 -69
  307. data/spec/Features/feature_mask_spec.rb +0 -580
  308. data/spec/Features/feature_math_spec.rb +0 -97
  309. data/spec/Features/feature_order_spec.rb +0 -146
  310. data/spec/Features/feature_ref_store_spec.rb +0 -209
  311. data/spec/Features/feature_serialization_spec.rb +0 -125
  312. data/spec/Features/feature_stat_spec.rb +0 -397
  313. data/spec/Features/feature_virtual_spec.rb +0 -48
  314. data/spec/Features/method_eq_spec.rb +0 -81
  315. data/spec/Features/method_is_nan_spec.rb +0 -12
  316. data/spec/Features/method_map_spec.rb +0 -54
  317. data/spec/Features/method_max_with.rb +0 -20
  318. data/spec/Features/method_min_with.rb +0 -19
  319. data/spec/Features/method_ne_spec.rb +0 -18
  320. data/spec/Features/method_project_spec.rb +0 -188
  321. data/spec/Features/method_ref_spec.rb +0 -27
  322. data/spec/Features/method_round_spec.rb +0 -11
  323. data/spec/Features/method_s_linspace_spec.rb +0 -48
  324. data/spec/Features/method_s_span_spec.rb +0 -14
  325. data/spec/Features/method_seq_spec.rb +0 -47
  326. data/spec/Features/method_sort_with.rb +0 -43
  327. data/spec/Features/method_sorted_with.rb +0 -29
  328. data/spec/Features/method_span_spec.rb +0 -42
  329. data/spec/Features/method_wrap_readonly_spec.rb +0 -43
  330. data/spec/UnitTest/test_CAVirtual.rb +0 -214
  331. data/spec/spec_all.rb +0 -10
  332. data/utils/ca_ase.rb +0 -21
  333. data/utils/ca_methods.rb +0 -15
  334. data/utils/cast_checker.rb +0 -30
  335. data/utils/convert_test.rb +0 -73
  336. data/utils/extract_yard.rb +0 -22
  337. data/utils/guess_shape.rb +0 -76
  338. data/utils/monkey_patch_methods.rb +0 -62
  339. data/utils/remove_resource_fork.sh +0 -5
data/ext/carray_lazy.c ADDED
@@ -0,0 +1,567 @@
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
+ return 0;
97
+ }
98
+
99
+ CALazyMarker *
100
+ ca_lazy_marker_new (CArray *parent)
101
+ {
102
+ CALazyMarker *ca = ALLOC(CALazyMarker);
103
+ ca_lazy_marker_setup(ca, parent);
104
+ return ca;
105
+ }
106
+
107
+ static void
108
+ free_ca_lazy_marker (void *ap)
109
+ {
110
+ CALazyMarker *ca = (CALazyMarker *) ap;
111
+ if ( ca != NULL ) {
112
+ ca_free(ca->mask);
113
+ xfree(ca->dim);
114
+ xfree(ca);
115
+ }
116
+ }
117
+
118
+ /* ------------------------------------------------------------------- */
119
+ /* Pass-through operations: everything delegates to parent. */
120
+ /* ------------------------------------------------------------------- */
121
+
122
+ static void *
123
+ ca_lazy_marker_func_clone (void *ap)
124
+ {
125
+ CALazyMarker *ca = (CALazyMarker *) ap;
126
+ return ca_lazy_marker_new(ca->parent);
127
+ }
128
+
129
+ static void
130
+ ca_lazy_marker_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
131
+ {
132
+ CALazyMarker *ca = (CALazyMarker *) ap;
133
+ if ( dir == CA_XFER_GET ) {
134
+ ca_fetch_index(ca->parent, idx, data);
135
+ } else {
136
+ ca_store_index(ca->parent, idx, data);
137
+ }
138
+ }
139
+
140
+ static void
141
+ ca_lazy_marker_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
142
+ void *data, int dir)
143
+ {
144
+ CALazyMarker *ca = (CALazyMarker *) ap;
145
+ ca_xfer_addrs(ca->parent, n, addrs, data, dir);
146
+ }
147
+
148
+ static void
149
+ ca_lazy_marker_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
150
+ ca_size_t *strides, void *data, int dir)
151
+ {
152
+ CALazyMarker *ca = (CALazyMarker *) ap;
153
+ ca_xfer_stride(ca->parent, starts, counts, strides, data, dir);
154
+ }
155
+
156
+ static void
157
+ ca_lazy_marker_func_xfer_all (void *ap, void *data, int dir)
158
+ {
159
+ CALazyMarker *ca = (CALazyMarker *) ap;
160
+ ca_xfer_all(ca->parent, data, dir);
161
+ }
162
+
163
+ static void
164
+ ca_lazy_marker_func_attach (void *ap)
165
+ {
166
+ CALazyMarker *ca = (CALazyMarker *) ap;
167
+ ca_attach(ca->parent);
168
+ ca->ptr = ca->parent->ptr; /* alias */
169
+ }
170
+
171
+ static void
172
+ ca_lazy_marker_func_sync (void *ap)
173
+ {
174
+ CALazyMarker *ca = (CALazyMarker *) ap;
175
+ ca_sync(ca->parent);
176
+ }
177
+
178
+ static void
179
+ ca_lazy_marker_func_detach (void *ap)
180
+ {
181
+ CALazyMarker *ca = (CALazyMarker *) ap;
182
+ ca->ptr = NULL;
183
+ ca_detach(ca->parent);
184
+ }
185
+
186
+ static void
187
+ ca_lazy_marker_func_allocate (void *ap)
188
+ {
189
+ CALazyMarker *ca = (CALazyMarker *) ap;
190
+ ca_allocate(ca->parent);
191
+ ca->ptr = ca->parent->ptr; /* alias */
192
+ }
193
+
194
+ static void
195
+ ca_lazy_marker_func_fill_data (void *ap, void *ptr)
196
+ {
197
+ CALazyMarker *ca = (CALazyMarker *) ap;
198
+ ca_fill(ca->parent, ptr);
199
+ }
200
+
201
+ static void
202
+ ca_lazy_marker_func_create_mask (void *ap)
203
+ {
204
+ CALazyMarker *ca = (CALazyMarker *) ap;
205
+ if ( ! ca->parent->mask ) {
206
+ ca_create_mask(ca->parent);
207
+ }
208
+ ca->mask =
209
+ (CArray *) ca_refer_new(ca->parent->mask,
210
+ CA_BOOLEAN, ca->ndim, ca->dim, 0, 0);
211
+ }
212
+
213
+ ca_operation_function_t ca_lazy_marker_func = {
214
+ -1, /* CA_OBJ_LAZY_MARKER, set at install time */
215
+ CA_VIEW_ARRAY,
216
+ free_ca_lazy_marker,
217
+ ca_lazy_marker_func_clone,
218
+ ca_lazy_marker_func_allocate,
219
+ ca_lazy_marker_func_attach,
220
+ ca_lazy_marker_func_sync,
221
+ ca_lazy_marker_func_detach,
222
+ ca_lazy_marker_func_fill_data,
223
+ ca_lazy_marker_func_create_mask,
224
+ ca_lazy_marker_func_xfer_index,
225
+ ca_lazy_marker_func_xfer_addrs,
226
+ NULL, /* fold_stride: not implemented */
227
+ ca_lazy_marker_func_xfer_stride,
228
+ ca_lazy_marker_func_xfer_all,
229
+ };
230
+
231
+ /* ------------------------------------------------------------------- */
232
+
233
+ VALUE
234
+ rb_ca_lazy_marker_new (VALUE cary)
235
+ {
236
+ volatile VALUE obj;
237
+ CArray *parent;
238
+ CALazyMarker *ca;
239
+ rb_check_carray_object(cary);
240
+ TypedData_Get_Struct(cary, CArray, &carray_data_type, parent);
241
+ ca = ca_lazy_marker_new(parent);
242
+ obj = ca_wrap_struct(ca);
243
+ rb_ca_set_parent(obj, cary);
244
+ return obj;
245
+ }
246
+
247
+ /* CArray#lazy — return a zero-cost CALazyMarker wrapping self. Subsequent
248
+ * element-wise ops (.sqrt / .sin / .+ / ...) build a lazy CAMonOp / CABinOp
249
+ * tree instead of evaluating eagerly; `.to_ca` materialises. */
250
+ VALUE
251
+ rb_ca_lazy (VALUE self)
252
+ {
253
+ return rb_ca_lazy_marker_new(self);
254
+ }
255
+
256
+ /* ------------------------------------------------------------------- */
257
+ /* ca_lazy_arena */
258
+ /* ------------------------------------------------------------------- */
259
+
260
+ /* Slot-pool arena. Each slot is an independent xmalloc'd buffer that
261
+ stays alive across to_ca calls; acquire / release toggles the
262
+ in_use flag, not the buffer itself, so simultaneous nested acquires
263
+ (deep CABinOp chain) hold stable pointers. A single-cursor + LIFO
264
+ stack design would force realloc when an outer acquire grew the
265
+ stack while an inner acquire still held the previous base — the
266
+ slot pool sidesteps that by design.
267
+
268
+ Steady state:
269
+ first to_ca call xmalloc N slots
270
+ subsequent calls reuse warm slots
271
+ best-fit allocation keeps small requests (mask scratch) out of
272
+ large data slots
273
+
274
+ Footprint: 32 slots × max observed size (up to 32 × max_slab_bytes;
275
+ e.g. N=1M f64, depth-8 chain = 8 × 8MB = 64MB kept resident). When
276
+ the slot pool is exhausted the acquire raises: this signals a
277
+ programming error or a pathological chain rather than a graceful
278
+ fallback.
279
+
280
+ CAREFUL: single-thread only (see CLAUDE.md "thread-safety is not a
281
+ goal"). The arena is process-global static state. */
282
+
283
+ #define CA_LAZY_ARENA_SLOTS 32
284
+
285
+ typedef struct ca_lazy_arena_slot {
286
+ void *ptr; /* xmalloc'd buffer, NULL = unused-and-unallocated */
287
+ ca_size_t bytes; /* allocated size of ptr (= capacity of this slot) */
288
+ int in_use; /* 1 = currently acquired, 0 = available */
289
+ } ca_lazy_arena_slot_t;
290
+
291
+ typedef struct ca_lazy_arena {
292
+ ca_lazy_arena_slot_t slots[CA_LAZY_ARENA_SLOTS];
293
+ int depth; /* enter/exit nest count */
294
+ ca_size_t debug_acquire_count; /* test instrumentation */
295
+ ca_size_t debug_xmalloc_count; /* test: how many xmallocs total */
296
+ ca_size_t debug_reuse_count; /* test: how many acquire = reuse */
297
+ } ca_lazy_arena_t;
298
+
299
+ static ca_lazy_arena_t ca_lazy_arena = {0};
300
+
301
+ void
302
+ ca_lazy_arena_enter (void)
303
+ {
304
+ if ( ca_lazy_arena.depth == 0 ) {
305
+ /* Top-level reset to recover from a prior exception that left
306
+ slots in_use without a matching release. Safe because
307
+ depth==0 means no nested caller is mid-acquire. */
308
+ int i;
309
+ for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
310
+ ca_lazy_arena.slots[i].in_use = 0;
311
+ }
312
+ }
313
+ ca_lazy_arena.depth++;
314
+ }
315
+
316
+ void
317
+ ca_lazy_arena_exit (void)
318
+ {
319
+ if ( ca_lazy_arena.depth > 0 ) {
320
+ ca_lazy_arena.depth--;
321
+ }
322
+ /* Keep all slot buffers; they amortise into subsequent calls. */
323
+ }
324
+
325
+ void *
326
+ ca_lazy_arena_acquire (ca_size_t bytes)
327
+ {
328
+ int i, best, empty;
329
+ ca_size_t best_bytes;
330
+
331
+ if ( bytes <= 0 ) bytes = 1;
332
+
333
+ ca_lazy_arena.debug_acquire_count++;
334
+
335
+ /* Pass 1: best-fit among unused warm slots (smallest >= bytes).
336
+ CAREFUL: ca_size_t is signed int64_t, so the "no match yet"
337
+ sentinel must be CA_LENGTH_MAX — NOT -1, which would compare
338
+ less than any real capacity. */
339
+ best = -1;
340
+ best_bytes = CA_LENGTH_MAX;
341
+ for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
342
+ ca_lazy_arena_slot_t *s = &ca_lazy_arena.slots[i];
343
+ if ( s->in_use ) continue;
344
+ if ( s->ptr == NULL ) continue;
345
+ if ( s->bytes < bytes ) continue;
346
+ if ( s->bytes < best_bytes ) {
347
+ best = i;
348
+ best_bytes = s->bytes;
349
+ }
350
+ }
351
+ if ( best >= 0 ) {
352
+ ca_lazy_arena.slots[best].in_use = 1;
353
+ ca_lazy_arena.debug_reuse_count++;
354
+ return ca_lazy_arena.slots[best].ptr;
355
+ }
356
+
357
+
358
+ /* Pass 2: empty slot — xmalloc a fresh buffer. */
359
+ empty = -1;
360
+ for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
361
+ if ( ca_lazy_arena.slots[i].ptr == NULL ) {
362
+ empty = i;
363
+ break;
364
+ }
365
+ }
366
+ if ( empty >= 0 ) {
367
+ ca_lazy_arena.slots[empty].ptr = xmalloc(bytes);
368
+ ca_lazy_arena.slots[empty].bytes = bytes;
369
+ ca_lazy_arena.slots[empty].in_use = 1;
370
+ ca_lazy_arena.debug_xmalloc_count++;
371
+ return ca_lazy_arena.slots[empty].ptr;
372
+ }
373
+
374
+ /* Pass 3: all slots have ptrs. Find a non-in-use slot and grow it
375
+ (= xfree + xmalloc). This pays a mmap roundtrip but only on
376
+ slot pool exhaustion (= unusual size request). */
377
+ for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
378
+ if ( ! ca_lazy_arena.slots[i].in_use ) {
379
+ xfree(ca_lazy_arena.slots[i].ptr);
380
+ ca_lazy_arena.slots[i].ptr = xmalloc(bytes);
381
+ ca_lazy_arena.slots[i].bytes = bytes;
382
+ ca_lazy_arena.slots[i].in_use = 1;
383
+ ca_lazy_arena.debug_xmalloc_count++;
384
+ return ca_lazy_arena.slots[i].ptr;
385
+ }
386
+ }
387
+
388
+ /* Pass 4: all CA_LAZY_ARENA_SLOTS in use simultaneously. This is
389
+ a programming error or a pathological chain. Raise. */
390
+ rb_raise(rb_eRuntimeError,
391
+ "ca_lazy_arena_acquire: all %d slots in use simultaneously "
392
+ "(chain depth exceeds arena capacity)",
393
+ CA_LAZY_ARENA_SLOTS);
394
+ return NULL; /* unreachable */
395
+ }
396
+
397
+ void
398
+ ca_lazy_arena_release (void *ptr)
399
+ {
400
+ int i;
401
+ if ( ptr == NULL ) return;
402
+ for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
403
+ if ( ca_lazy_arena.slots[i].ptr == ptr ) {
404
+ ca_lazy_arena.slots[i].in_use = 0;
405
+ return;
406
+ }
407
+ }
408
+ /* Unknown ptr — could indicate caller mixed arena release with a
409
+ non-arena pointer. Be lenient (= no raise) to avoid masking
410
+ genuine bugs at unrelated callsites. */
411
+ }
412
+
413
+ /* Test instrumentation — exposed to Ruby for the arena smoke and
414
+ regression assertions. Not user-facing API. */
415
+ static VALUE
416
+ rb_ca_lazy_arena_s_reset_counters (VALUE klass)
417
+ {
418
+ ca_lazy_arena.debug_acquire_count = 0;
419
+ ca_lazy_arena.debug_xmalloc_count = 0;
420
+ ca_lazy_arena.debug_reuse_count = 0;
421
+ return Qnil;
422
+ }
423
+
424
+ static VALUE
425
+ rb_ca_lazy_arena_s_acquire_count (VALUE klass)
426
+ {
427
+ return SIZE2NUM(ca_lazy_arena.debug_acquire_count);
428
+ }
429
+
430
+ static VALUE
431
+ rb_ca_lazy_arena_s_xmalloc_count (VALUE klass)
432
+ {
433
+ return SIZE2NUM(ca_lazy_arena.debug_xmalloc_count);
434
+ }
435
+
436
+ static VALUE
437
+ rb_ca_lazy_arena_s_reuse_count (VALUE klass)
438
+ {
439
+ return SIZE2NUM(ca_lazy_arena.debug_reuse_count);
440
+ }
441
+
442
+ static VALUE
443
+ rb_ca_lazy_arena_s_depth (VALUE klass)
444
+ {
445
+ return INT2NUM(ca_lazy_arena.depth);
446
+ }
447
+
448
+ static VALUE
449
+ rb_ca_lazy_arena_s_slot_in_use_count (VALUE klass)
450
+ {
451
+ int i, n = 0;
452
+ for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
453
+ if ( ca_lazy_arena.slots[i].in_use ) n++;
454
+ }
455
+ return INT2NUM(n);
456
+ }
457
+
458
+ /* Per-slot capacity sweep for observability tests. Returns an
459
+ Array of Integer bytes for every populated slot, so a test can
460
+ assert that mixed boolean (1 byte/cell) and f64 (8 byte/cell)
461
+ acquires develop distinct size classes under best-fit — otherwise
462
+ boolean acquires would sit in oversized f64 slots and force fresh
463
+ xmalloc for subsequent f64 acquires. */
464
+ static VALUE
465
+ rb_ca_lazy_arena_s_slot_capacities (VALUE klass)
466
+ {
467
+ int i;
468
+ VALUE arr = rb_ary_new();
469
+ (void) klass;
470
+ for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
471
+ if ( ca_lazy_arena.slots[i].ptr != NULL ) {
472
+ rb_ary_push(arr, SIZE2NUM(ca_lazy_arena.slots[i].bytes));
473
+ }
474
+ }
475
+ return arr;
476
+ }
477
+
478
+ /* ------------------------------------------------------------------- */
479
+
480
+ /* ca_lazy_wrap_scalar(other, self_ca) — wrap a non-CArray Ruby value
481
+ * as a 1-element CScalar whose data_type follows the array's family,
482
+ * mirroring the array+scalar branch of rb_ca_cast_self_or_other. The
483
+ * scalar takes on self's data_type so binops preserve self's
484
+ * precision (`f32_array + 2.5` stays f32).
485
+ *
486
+ * CA_OBJECT self -> CA_OBJECT
487
+ * CA_BOOLEAN self -> derive from other's Ruby type via
488
+ * ca_value_to_data_type (bool arithmetic is a
489
+ * corner case; eager widens the array, so we
490
+ * just let the scalar choose)
491
+ * numeric self -> self_ca->data_type
492
+ *
493
+ * Callers broadcast against the array via stride 0 in xfer_stride
494
+ * (or wrap the scalar in CAUnboundRepeat when the chain needs it). */
495
+ VALUE
496
+ ca_lazy_wrap_scalar (VALUE other, CArray *self_ca)
497
+ {
498
+ int8_t dt;
499
+
500
+ if ( ca_is_object_type(self_ca) ) {
501
+ dt = CA_OBJECT;
502
+ }
503
+ else if ( self_ca->data_type == CA_BOOLEAN ) {
504
+ dt = ca_value_to_data_type(other);
505
+ }
506
+ else {
507
+ dt = self_ca->data_type;
508
+ }
509
+
510
+ return rb_cscalar_new_with_value(dt, 0, other);
511
+ }
512
+
513
+ static VALUE
514
+ rb_ca_lazy_marker_s_allocate (VALUE klass)
515
+ {
516
+ CALazyMarker *ca;
517
+ return TypedData_Make_Struct(klass, CALazyMarker,
518
+ &calazy_marker_data_type, ca);
519
+ }
520
+
521
+ static VALUE
522
+ rb_ca_lazy_marker_initialize_copy (VALUE self, VALUE other)
523
+ {
524
+ CALazyMarker *ca, *cs;
525
+ TypedData_Get_Struct(self, CALazyMarker, &calazy_marker_data_type, ca);
526
+ TypedData_Get_Struct(other, CALazyMarker, &calazy_marker_data_type, cs);
527
+ ca_lazy_marker_setup(ca, cs->parent);
528
+ return self;
529
+ }
530
+
531
+ void
532
+ Init_carray_lazy (void)
533
+ {
534
+ rb_cCALazyMarker = rb_define_class("CALazyMarker", rb_cCAView);
535
+
536
+ CA_OBJ_LAZY_MARKER = ca_install_obj_type(rb_cCALazyMarker,
537
+ &calazy_marker_data_type,
538
+ rb_cCArrayMask,
539
+ &carray_mask_data_type,
540
+ &ca_lazy_marker_func, sizeof(ca_lazy_marker_func));
541
+ rb_define_const(rb_cObject, "CA_OBJ_LAZY_MARKER",
542
+ INT2NUM(CA_OBJ_LAZY_MARKER));
543
+
544
+ rb_define_method(rb_cCArray, "lazy", rb_ca_lazy, 0);
545
+
546
+ rb_define_alloc_func(rb_cCALazyMarker, rb_ca_lazy_marker_s_allocate);
547
+ rb_define_method(rb_cCALazyMarker, "initialize_copy",
548
+ rb_ca_lazy_marker_initialize_copy, 1);
549
+
550
+ /* Arena test instrumentation (not user-facing). Bound on CArray
551
+ for symmetry with CAMonOp / CABinOp counters, but the arena is
552
+ a singleton process-wide state. */
553
+ rb_define_singleton_method(rb_cCArray, "__lazy_arena_reset_counters__",
554
+ rb_ca_lazy_arena_s_reset_counters, 0);
555
+ rb_define_singleton_method(rb_cCArray, "__lazy_arena_acquire_count__",
556
+ rb_ca_lazy_arena_s_acquire_count, 0);
557
+ rb_define_singleton_method(rb_cCArray, "__lazy_arena_xmalloc_count__",
558
+ rb_ca_lazy_arena_s_xmalloc_count, 0);
559
+ rb_define_singleton_method(rb_cCArray, "__lazy_arena_reuse_count__",
560
+ rb_ca_lazy_arena_s_reuse_count, 0);
561
+ rb_define_singleton_method(rb_cCArray, "__lazy_arena_depth__",
562
+ rb_ca_lazy_arena_s_depth, 0);
563
+ rb_define_singleton_method(rb_cCArray, "__lazy_arena_slot_in_use_count__",
564
+ rb_ca_lazy_arena_s_slot_in_use_count, 0);
565
+ rb_define_singleton_method(rb_cCArray, "__lazy_arena_slot_capacities__",
566
+ rb_ca_lazy_arena_s_slot_capacities, 0);
567
+ }