carray 2.0.0 → 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 +774 -298
  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 +230 -792
  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 +208 -241
  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 -330
  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 +1342 -199
  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 +4 -14
  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/misc/Methods.ja.md +0 -182
  269. data/misc/NOTE +0 -51
  270. data/spec/Classes/CABitfield_spec.rb +0 -58
  271. data/spec/Classes/CABlockIterator_spec.rb +0 -114
  272. data/spec/Classes/CABlock_spec.rb +0 -205
  273. data/spec/Classes/CAField_spec.rb +0 -39
  274. data/spec/Classes/CAGrid_spec.rb +0 -75
  275. data/spec/Classes/CAMap_spec.rb +0 -0
  276. data/spec/Classes/CAMapping_spec.rb +0 -105
  277. data/spec/Classes/CAObject_attribute_spec.rb +0 -33
  278. data/spec/Classes/CAObject_spec.rb +0 -33
  279. data/spec/Classes/CARefer_spec.rb +0 -93
  280. data/spec/Classes/CARepeat_spec.rb +0 -65
  281. data/spec/Classes/CASelect_spec.rb +0 -22
  282. data/spec/Classes/CAShift_spec.rb +0 -16
  283. data/spec/Classes/CAStruct_spec.rb +0 -71
  284. data/spec/Classes/CATranspose_spec.rb +0 -60
  285. data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
  286. data/spec/Classes/CAWindow_spec.rb +0 -54
  287. data/spec/Classes/CAWrap_spec.rb +0 -8
  288. data/spec/Classes/CArray_spec.rb +0 -184
  289. data/spec/Classes/CScalar_spec.rb +0 -55
  290. data/spec/Classes/ex1.rb +0 -46
  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/ca_obj_grid.c CHANGED
@@ -1,15 +1,41 @@
1
1
  /* ---------------------------------------------------------------------------
2
2
 
3
- ca_obj_grid.c
3
+ CAGrid -- a coordinate-selected view: each axis independently picks a
4
+ set of parent indices (whole axis, a contiguous range, or an arbitrary
5
+ index list) and the view is their Cartesian product.
4
6
 
5
- This file is part of Ruby/CArray extension library.
6
-
7
- Copyright (C) 2005-2020 Hiroki Motoyoshi
7
+ Gather / scatter / fill_value all go through the shared per-axis
8
+ descriptor engine in ca_axis_dispatch.c; this file emits the per-axis
9
+ descriptor (ca_grid_describe_axes) and holds the region-delivery
10
+ (xfer_stride / xfer_addrs) and fold_stride paths. Sibling of
11
+ ca_obj_select_axis.c (CASelectAxis), which rb_ca_grid routes to for the
12
+ single-boolean-axis slab-copy fast path.
8
13
 
9
14
  ---------------------------------------------------------------------------- */
10
15
 
11
16
  #include "carray.h"
12
17
 
18
+ /* Per-axis tagged kind. Each axis is either STRIDE (start/step/count,
19
+ no allocation) or INDEX (an owned ca_size_t index snapshot). A Range
20
+ argument is detected at rb_ca_grid and stored as STRIDE, so its
21
+ arithmetic-progression structure is available to axis-merge; a nil-arg
22
+ axis is STRIDE(0, dim, 1) and allocates nothing. */
23
+ typedef enum {
24
+ CAG_AXIS_STRIDE = 0,
25
+ CAG_AXIS_INDEX = 1
26
+ } cag_axis_kind_t;
27
+
28
+ typedef struct {
29
+ cag_axis_kind_t kind;
30
+ ca_size_t count; /* = ca->dim[k] (view's output size along axis) */
31
+ /* STRIDE-only */
32
+ ca_size_t start;
33
+ ca_size_t step;
34
+ /* INDEX-only. Owned in the non-share case (xfree'd by free_ca_grid),
35
+ aliased in the share case (parent owns; CA_FLAG_SHARE_INDEX). */
36
+ ca_size_t *indices;
37
+ } cag_axis_t;
38
+
13
39
  typedef struct {
14
40
  int16_t obj_type;
15
41
  int8_t data_type;
@@ -20,21 +46,60 @@ typedef struct {
20
46
  ca_size_t *dim;
21
47
  char *ptr;
22
48
  CArray *mask;
49
+ char *_pool; /* framework-managed pool buffer (NULL = legacy ALLOC_N path). */
23
50
  CArray *parent;
24
51
  uint32_t attach;
25
52
  uint8_t nosync;
26
53
  /* -------------*/
27
- CArray **grid;
28
- int8_t *contig;
54
+ cag_axis_t *axes; /* [ndim], owned (non-share) or aliased (share) */
29
55
  } CAGrid;
30
56
 
57
+ static size_t
58
+ ca_grid_dsize (const void *ap)
59
+ {
60
+ const CAGrid *ca = (const CAGrid *) ap;
61
+ size_t total = sizeof(CAGrid)
62
+ + ca->ndim * sizeof(ca_size_t) /* dim */
63
+ + ca->ndim * sizeof(cag_axis_t); /* axes */
64
+ /* indices for INDEX axes (variable per-axis count; STRIDE axes
65
+ contribute 0 — the big win vs. the legacy identity-allocation
66
+ pattern). */
67
+ int8_t k;
68
+ for (k = 0; k < ca->ndim; k++) {
69
+ if (ca->axes[k].kind == CAG_AXIS_INDEX && ca->axes[k].indices) {
70
+ total += ca->axes[k].count * sizeof(ca_size_t);
71
+ }
72
+ }
73
+ return total;
74
+ }
75
+
76
+ /* Pool framework hooks: CAGrid owns dim (ndim ca_size_t), axes (ndim
77
+ cag_axis_t), and per-INDEX-axis indices buffers. Only `dim` is always
78
+ owned and ndim-sized, so only dim moves into the _pool; `axes` is
79
+ sometimes aliased (CA_FLAG_SHARE_INDEX) and the per-axis `indices`
80
+ buffers are variable-size, so both stay on their own ALLOC_N path
81
+ (cf. CAWindow's fill). */
82
+ static size_t
83
+ ca_grid_pool_bytes (int8_t ndim)
84
+ {
85
+ ca_size_t n = (ndim > 0) ? ndim : 1;
86
+ return (size_t) n * sizeof(ca_size_t);
87
+ }
88
+
89
+ static void
90
+ ca_grid_pool_init (void *ap, int8_t ndim)
91
+ {
92
+ CAGrid *ca = (CAGrid *) ap;
93
+ ca->dim = (ca_size_t *) ca->_pool;
94
+ }
95
+
31
96
  const rb_data_type_t cagrid_data_type = {
32
- .parent = &cavirtual_data_type,
97
+ .parent = &caview_data_type,
33
98
  .wrap_struct_name = "CAGrid",
34
99
  .function = {
35
100
  .dmark = ca_mark,
36
101
  .dfree = ca_free,
37
- .dsize = NULL,
102
+ .dsize = ca_grid_dsize,
38
103
  .dcompact = NULL
39
104
  },
40
105
  .flags = RUBY_TYPED_FREE_IMMEDIATELY
@@ -46,7 +111,7 @@ const rb_data_type_t cagrid_mask_data_type = {
46
111
  .function = {
47
112
  .dmark = NULL,
48
113
  .dfree = ca_free_nop,
49
- .dsize = NULL,
114
+ .dsize = ca_grid_dsize,
50
115
  .dcompact = NULL
51
116
  },
52
117
  .flags = RUBY_TYPED_FREE_IMMEDIATELY
@@ -55,40 +120,47 @@ const rb_data_type_t cagrid_mask_data_type = {
55
120
  static VALUE rb_cCAGrid;
56
121
  static VALUE rb_cCAGridMask;
57
122
 
58
- /* yard:
59
- class CAGrid < CAVirtual # :nodoc:
60
- end
61
- */
62
-
63
123
  static int8_t CA_OBJ_GRID;
64
124
 
125
+ /* Setup: caller passes ndim + per-axis protos (cag_axis_t). Copies the
126
+ fields and, for INDEX axes, snapshots the indices into an owned buffer.
127
+ The share path (share=1, from initialize_copy and create_mask) aliases
128
+ the caller's protos buffer instead, tying its lifetime to the source
129
+ CAGrid (free_ca_grid skips the xfree under CA_FLAG_SHARE_INDEX). */
65
130
  int
66
- ca_grid_setup (CAGrid *ca, CArray *parent, ca_size_t *dim,
67
- CArray **grid, int8_t *contig, int share)
131
+ ca_grid_setup (CAGrid *ca, CArray *parent, int8_t ndim,
132
+ cag_axis_t *protos, int share)
68
133
  {
69
- int8_t ndim, data_type;
70
- ca_size_t *dim0;
134
+ int8_t k;
135
+ int8_t data_type;
71
136
  ca_size_t elements, bytes;
72
- ca_size_t j, k;
73
137
  double length;
74
- int8_t i;
138
+ ca_size_t *dim0;
75
139
 
140
+ CA_ASSUME(ndim >= 0 && ndim <= CA_RANK_MAX); /* bound loops/allocs over [CA_RANK_MAX] arrays */
76
141
  data_type = parent->data_type;
77
- ndim = parent->ndim;
78
142
  bytes = parent->bytes;
79
143
  dim0 = parent->dim;
80
144
 
145
+ /* parent->ndim should equal ndim (= number of axes provided).
146
+ rb_ca_grid pads with nil when fewer args are given, so by the
147
+ time we get here ndim == parent->ndim. */
148
+ if ( ndim != parent->ndim ) {
149
+ rb_raise(rb_eArgError,
150
+ "CAGrid: ndim mismatch (%d args vs parent->ndim %d)",
151
+ (int) ndim, (int) parent->ndim);
152
+ }
153
+
81
154
  elements = 1;
82
155
  length = bytes;
83
- for (i=0; i<ndim; i++) {
84
- if ( dim[i] < 0 ) {
85
- rb_raise(rb_eRuntimeError, "negative size for %i-th dimension", i);
156
+ for (k = 0; k < ndim; k++) {
157
+ if (protos[k].count < 0) {
158
+ rb_raise(rb_eRuntimeError, "negative size for %d-th dimension", k);
86
159
  }
87
- elements *= dim[i];
88
- length *= dim[i];
160
+ elements *= protos[k].count;
161
+ length *= protos[k].count;
89
162
  }
90
-
91
- if ( length > CA_LENGTH_MAX ) {
163
+ if (length > CA_LENGTH_MAX) {
92
164
  rb_raise(rb_eRuntimeError, "too large byte length");
93
165
  }
94
166
 
@@ -100,87 +172,72 @@ ca_grid_setup (CAGrid *ca, CArray *parent, ca_size_t *dim,
100
172
  ca->elements = elements;
101
173
  ca->ptr = NULL;
102
174
  ca->mask = NULL;
103
- ca->dim = ALLOC_N(ca_size_t, ndim);
104
-
175
+ if ( ! ca->_pool ) {
176
+ ca->dim = ALLOC_N(ca_size_t, ndim);
177
+ }
105
178
  ca->parent = parent;
106
179
  ca->attach = 0;
107
180
  ca->nosync = 0;
108
181
 
182
+ for (k = 0; k < ndim; k++) ca->dim[k] = protos[k].count;
183
+
109
184
  if ( share ) {
185
+ /* Alias the protos buffer; lifetime tied to the source CAGrid
186
+ (initialize_copy and create_mask call sites). free_ca_grid
187
+ skips the axes / indices xfree under SHARE_INDEX. */
110
188
  ca_set_flag(ca, CA_FLAG_SHARE_INDEX);
111
- ca->grid = grid;
112
- ca->contig = contig;
113
- }
114
- else {
115
- ca->grid = ALLOC_N(CArray *, ndim);
116
- ca->contig = ALLOC_N(int8_t, ndim);
117
- }
118
-
119
- memcpy(ca->dim, dim, ndim * sizeof(ca_size_t));
120
-
121
- if ( ! share ) {
122
- for (i=0; i<ndim; i++) {
123
- if ( grid[i] ) {
124
- if ( ca_is_any_masked(grid[i]) ) {
125
- ca_size_t gsize = grid[i]->elements - ca_count_masked(grid[i]);
126
- boolean8_t *m;
127
- ca_size_t n;
128
- ca->grid[i] = carray_new(CA_SIZE, 1, &gsize, 0, NULL);
129
- m = (boolean8_t *)grid[i]->mask->ptr;
130
- n = 0;
131
- for (j=0; j<grid[i]->elements; j++) {
132
- if ( ! *m ) {
133
- k = ((ca_size_t*)grid[i]->ptr)[j];
134
- CA_CHECK_INDEX(k, dim0[i]);
135
- ((ca_size_t*)ca->grid[i]->ptr)[n] = k;
136
- n++;
137
- }
138
- m++;
139
- }
140
- ca->contig[i] = 0;
141
- }
142
- else {
143
- ca->grid[i] = ca_template(grid[i]);
144
- for (j=0; j<grid[i]->elements; j++) {
145
- k = ((ca_size_t*)grid[i]->ptr)[j];
146
- CA_CHECK_INDEX(k, dim0[i]);
147
- ((ca_size_t*)ca->grid[i]->ptr)[j] = k;
148
- }
149
- ca->contig[i] = 0;
189
+ ca->axes = protos;
190
+ } else {
191
+ ca->axes = ALLOC_N(cag_axis_t, ndim);
192
+ for (k = 0; k < ndim; k++) {
193
+ ca->axes[k].kind = protos[k].kind;
194
+ ca->axes[k].count = protos[k].count;
195
+ if (protos[k].kind == CAG_AXIS_STRIDE) {
196
+ ca->axes[k].start = protos[k].start;
197
+ ca->axes[k].step = protos[k].step;
198
+ ca->axes[k].indices = NULL;
199
+ /* Bounds check: first and last parent index must be in range. */
200
+ if (protos[k].count > 0) {
201
+ ca_size_t first = protos[k].start;
202
+ ca_size_t last = first + (protos[k].count - 1) * protos[k].step;
203
+ CA_CHECK_INDEX(first, dim0[k]);
204
+ if (protos[k].count > 1) CA_CHECK_INDEX(last, dim0[k]);
150
205
  }
151
- }
152
- else {
153
- ca_size_t *p;
154
- ca->grid[i] = carray_new(CA_SIZE, 1, &dim[i], 0, NULL);
155
- p = (ca_size_t *)ca->grid[i]->ptr;
156
- for (j=0; j<dim[i]; j++) {
157
- *p++ = j;
206
+ } else {
207
+ ca->axes[k].start = 0;
208
+ ca->axes[k].step = 0;
209
+ ca->axes[k].indices = ALLOC_N(ca_size_t,
210
+ protos[k].count > 0 ? protos[k].count : 1);
211
+ ca_size_t j;
212
+ for (j = 0; j < protos[k].count; j++) {
213
+ ca_size_t v = protos[k].indices[j];
214
+ CA_CHECK_INDEX(v, dim0[k]);
215
+ ca->axes[k].indices[j] = v;
158
216
  }
159
- ca->contig[i] = 1;
160
217
  }
161
218
  }
162
219
  }
163
220
 
164
- if ( ca->ndim == 1 && ca_is_scalar(grid[0]) ) {
165
- ca_set_flag(ca, CA_FLAG_SCALAR);
221
+ if ( ca_has_mask(parent) ) {
222
+ ca_create_mask(ca);
166
223
  }
167
224
 
168
225
  return 0;
169
226
  }
170
227
 
171
228
  CAGrid *
172
- ca_grid_new (CArray *parent, ca_size_t *dim, CArray **grid)
229
+ ca_grid_new (CArray *parent, int8_t ndim, cag_axis_t *protos)
173
230
  {
174
- CAGrid *ca = ALLOC(CAGrid);
175
- ca_grid_setup(ca, parent, dim, grid, NULL, 0);
231
+ CAGrid *ca = (CAGrid *) ca_array_alloc(CA_OBJ_GRID, ndim);
232
+ ca_grid_setup(ca, parent, ndim, protos, 0);
176
233
  return ca;
177
234
  }
178
235
 
179
236
  CAGrid *
180
- ca_grid_new_share (CArray *parent, ca_size_t *dim, CArray **grid, int8_t *contig)
237
+ ca_grid_new_share (CArray *parent, int8_t ndim, cag_axis_t *protos)
181
238
  {
182
- CAGrid *ca = ALLOC(CAGrid);
183
- ca_grid_setup(ca, parent, dim, grid, contig, 1);
239
+ CAGrid *ca = (CAGrid *) ca_array_alloc(CA_OBJ_GRID, ndim);
240
+ ca_grid_setup(ca, parent, ndim, protos, 1);
184
241
  return ca;
185
242
  }
186
243
 
@@ -188,99 +245,375 @@ static void
188
245
  free_ca_grid (void *ap)
189
246
  {
190
247
  CAGrid *ca = (CAGrid *) ap;
191
- ca_size_t i;
192
248
  if ( ca != NULL ) {
193
249
  ca_free(ca->mask);
194
- if ( ! (ca->flags & CA_FLAG_SHARE_INDEX)) {
195
- xfree(ca->contig);
196
- for (i=0; i<ca->ndim; i++) {
197
- ca_free(ca->grid[i]);
250
+ if ( ! (ca->flags & CA_FLAG_SHARE_INDEX) ) {
251
+ int8_t k;
252
+ for (k = 0; k < ca->ndim; k++) {
253
+ if ( ca->axes[k].kind == CAG_AXIS_INDEX && ca->axes[k].indices ) {
254
+ xfree(ca->axes[k].indices);
255
+ }
198
256
  }
199
- xfree(ca->grid);
257
+ xfree(ca->axes);
258
+ }
259
+ /* dim is the only field in the pool; axes/indices freed above. */
260
+ if ( ca->_pool ) {
261
+ ca_array_free(ca); /* dim + struct */
262
+ }
263
+ else {
264
+ xfree(ca->dim);
265
+ xfree(ca);
200
266
  }
201
- xfree(ca->dim);
202
- xfree(ca);
203
267
  }
204
268
  }
205
269
 
206
270
  /* ------------------------------------------------------------------- */
207
271
 
208
- static void ca_grid_attach (CAGrid *ca);
209
- static void ca_grid_sync (CAGrid *ca);
210
- static void ca_grid_fill (CAGrid *ca, char *ptr);
272
+ /* gather, scatter, and broadcast-fill all run through the shared
273
+ descriptor engine in ca_axis_dispatch.c via the func_* dispatchers
274
+ below; this file only translates CAGrid state into the descriptor
275
+ (ca_grid_describe_axes). */
211
276
 
212
277
  static void *
213
278
  ca_grid_func_clone (void *ap)
214
279
  {
215
280
  CAGrid *ca = (CAGrid *) ap;
216
- return ca_grid_new_share(ca->parent, ca->dim, ca->grid, ca->contig);
281
+ return ca_grid_new_share(ca->parent, ca->ndim, ca->axes);
282
+ }
283
+
284
+ /* Per-axis parent-index lookup. STRIDE = one multiply, INDEX = one
285
+ indirect load. */
286
+ static inline ca_size_t
287
+ cag_parent_index_for (const cag_axis_t *ax, ca_size_t i)
288
+ {
289
+ if (ax->kind == CAG_AXIS_STRIDE) {
290
+ return ax->start + i * ax->step;
291
+ }
292
+ return ax->indices[i];
217
293
  }
218
294
 
219
- static char *
220
- ca_grid_func_ptr_at_addr (void *ap, ca_size_t addr)
295
+ /* per-cell logic lives in ca_grid_func_xfer_index; forward-declared for
296
+ the region-delivery paths below. */
297
+ static void ca_grid_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir);
298
+
299
+ /* dir-unified per-cell: translate per-axis idx via the grid descriptors,
300
+ then re-delegate to the parent's xfer_index. */
301
+ static void
302
+ ca_grid_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
221
303
  {
222
304
  CAGrid *ca = (CAGrid *) ap;
223
- if ( ! ca->ptr ) {
224
- ca_size_t idx[CA_RANK_MAX];
225
- ca_addr2index((CArray *)ca, addr, idx);
226
- return ca_ptr_at_index(ca, idx);
305
+ ca_size_t idx0[CA_RANK_MAX];
306
+ int8_t i;
307
+ for (i = 0; i < ca->ndim; i++) {
308
+ idx0[i] = cag_parent_index_for(&ca->axes[i], idx[i]);
309
+ }
310
+ ca_xfer_index(ca->parent, idx0, data, dir);
311
+ }
312
+
313
+ /* Batched address gather/scatter. Per-axis index remap
314
+ (cag_parent_index_for), always in-bounds; duplicate parent addrs
315
+ (duplicate grid indices) scatter last-write-wins. ONE parent
316
+ ca_xfer_addrs call -- no whole-view attach.
317
+
318
+ Fast path: when addrs form the whole-view sequential run
319
+ [0..elements-1] and parent.ptr is present, rebuild the full sub_axes
320
+ descriptor via ca_grid_describe_axes and dispatch through
321
+ ca_axis_dispatch_gather/_scatter directly, skipping the per-cell remap
322
+ loop. */
323
+ static void
324
+ ca_grid_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
325
+ void *data, int dir)
326
+ {
327
+ CAGrid *ca = (CAGrid *) ap;
328
+ CArray *parent = ca->parent;
329
+ ca_size_t *paddrs;
330
+ ca_size_t i, base;
331
+ int8_t k;
332
+ volatile VALUE holder;
333
+
334
+ /* Opportunistic fast path. ca_resolve_attached_root_via_identity lifts
335
+ parent->ptr through an identity CAStride compose-fold (handles a
336
+ flatten[idx].reshape(*) chain etc.). */
337
+ if ( n == ca->elements
338
+ && ca_xfer_addrs_is_sequential_run(n, addrs, &base) && base == 0 ) {
339
+ CArray *eff_parent = ca_resolve_attached_root_via_identity(parent);
340
+ if ( eff_parent->ptr ) {
341
+ ca_axis_desc_t sub_axes[CA_RANK_MAX];
342
+ ca_size_t parent_axis_dims[CA_RANK_MAX];
343
+ ca_grid_describe_axes(ca, sub_axes, parent_axis_dims);
344
+ if ( dir == CA_XFER_GET ) {
345
+ ca_axis_dispatch_gather(eff_parent, parent_axis_dims, sub_axes, ca->ndim,
346
+ ca->bytes, ca->elements, NULL, (char *) data);
347
+ } else {
348
+ ca_axis_dispatch_scatter(eff_parent, parent_axis_dims, sub_axes, ca->ndim,
349
+ ca->bytes, ca->elements, (char *) data);
350
+ }
351
+ return;
352
+ }
227
353
  }
228
- else {
229
- return ca->ptr + ca->bytes * addr;
354
+
355
+ /* Per-cell remap fallback (arbitrary addrs, view parent, partial). */
356
+ paddrs = ALLOCV_N(ca_size_t, holder, n);
357
+ for (i = 0; i < n; i++) {
358
+ ca_size_t vidx[CA_RANK_MAX], pidx[CA_RANK_MAX];
359
+ ca_addr2index((CArray *) ca, addrs[i], vidx);
360
+ for (k = 0; k < ca->ndim; k++) pidx[k] = cag_parent_index_for(&ca->axes[k], vidx[k]);
361
+ paddrs[i] = ca_index2addr(ca->parent, pidx);
230
362
  }
363
+ ca_xfer_addrs(ca->parent, n, paddrs, data, dir);
364
+ ALLOCV_END(holder);
231
365
  }
232
366
 
233
- static char *
234
- ca_grid_func_ptr_at_index (void *ap, ca_size_t *idx)
367
+ /* fold_stride: a CAGrid folds into the stride chain when every axis is
368
+ STRIDE or singleton-INDEX (count == 1). STRIDE axis k contributes
369
+ stride step_k*pstride_k and base start_k*pstride_k; a singleton INDEX
370
+ axis bakes indices_k[0]*pstride_k into the base (count 1 -> no stride).
371
+ A multi-element INDEX axis is a true gather -> decline (the grid
372
+ becomes the fold boundary, delivered by the descriptor engine).
373
+ Synthesises a CAStride over grid->parent and composes f through it. */
374
+ static int
375
+ ca_grid_func_fold_stride (void *ap, ca_fold_t *f, void **next_parent)
235
376
  {
236
- CAGrid *ca = (CAGrid *) ap;
237
- if ( ! ca->ptr ) {
238
- CArray **grid = ca->grid;
239
- ca_size_t *dim0 = ca->parent->dim;
240
- int8_t i;
241
- ca_size_t n;
242
-
243
- n = 0;
244
- for (i=0; i<ca->ndim; i++) {
245
- n = dim0[i]*n + *(ca_size_t*) ca_ptr_at_addr(grid[i], idx[i]);
377
+ CAGrid *g = (CAGrid *) ap;
378
+ CArray *parent = g->parent;
379
+ ca_size_t pstride[CA_RANK_MAX];
380
+ ca_size_t synth_strides[CA_RANK_MAX];
381
+ ca_size_t synth_base = 0;
382
+ ca_size_t next_strides[CA_RANK_MAX];
383
+ ca_size_t next_base;
384
+ ca_size_t s;
385
+ CAStride tmp, synth;
386
+ int8_t k;
387
+
388
+ for (k = 0; k < g->ndim; k++) {
389
+ if (g->axes[k].kind == CAG_AXIS_INDEX && g->axes[k].count != 1) {
390
+ return 0; /* multi-INDEX axis -> true gather, decline */
246
391
  }
392
+ }
393
+
394
+ /* parent row-major byte strides (g->bytes == parent->bytes) */
395
+ s = g->bytes;
396
+ for (k = g->ndim - 1; k >= 0; k--) {
397
+ pstride[k] = s;
398
+ s *= parent->dim[k];
399
+ }
247
400
 
248
- if ( ca->parent->ptr == NULL ) {
249
- return ca_ptr_at_addr(ca->parent, n);
401
+ for (k = 0; k < g->ndim; k++) {
402
+ if (g->axes[k].kind == CAG_AXIS_STRIDE) {
403
+ synth_strides[k] = g->axes[k].step * pstride[k];
404
+ synth_base += g->axes[k].start * pstride[k];
250
405
  }
251
- else {
252
- return ca->parent->ptr + ca->bytes * n;
406
+ else { /* singleton INDEX: count == 1, position baked into base */
407
+ synth_strides[k] = 0;
408
+ synth_base += g->axes[k].indices[0] * pstride[k];
253
409
  }
254
410
  }
255
- else {
256
- return ca_func[CA_OBJ_ARRAY].ptr_at_index(ca, idx);
411
+
412
+ tmp.ndim = f->ndim;
413
+ tmp.bytes = g->bytes;
414
+ tmp.dim = f->counts;
415
+ tmp.strides = f->strides;
416
+ tmp.base_offset = f->base;
417
+
418
+ synth.ndim = g->ndim;
419
+ synth.bytes = g->bytes;
420
+ synth.dim = g->dim;
421
+ synth.strides = synth_strides;
422
+ synth.base_offset = synth_base;
423
+
424
+ if (!ca_stride_compose_through(&tmp, &synth, next_strides, &next_base)) {
425
+ return 0;
257
426
  }
427
+
428
+ for (k = 0; k < f->ndim; k++) f->strides[k] = next_strides[k];
429
+ f->base = next_base;
430
+ *next_parent = parent;
431
+ return 1;
258
432
  }
259
433
 
434
+ /* xfer_stride: structural region delivery when CAGrid is the (declining)
435
+ fold boundary -- a multi-element INDEX axis. Instead of materialising
436
+ the whole grid, deliver only the requested region using the per-axis
437
+ kind:
438
+
439
+ - iterate the OUTER axes [0..ndim-2] with an odometer, computing the parent
440
+ byte base and the contiguous data offset for each combination;
441
+ - deliver the INNERMOST axis as one batched run into the contiguous data
442
+ sub-block at data+offset:
443
+ * STRIDE inner axis -> parent.xfer_stride (one strided run; entity
444
+ parent delivers a contiguous/strided memcpy);
445
+ * INDEX inner axis -> parent.xfer_addrs (gather the selected cells).
446
+
447
+ strides[] = src access byte strides into the grid (semantics b); data is
448
+ contiguous row-major over counts. g->bytes == parent->bytes (no reinterpret).
449
+ No whole-view attach -- only the requested cells touch the parent. */
260
450
  static void
261
- ca_grid_func_fetch_index (void *ap, ca_size_t *idx, void *ptr)
451
+ ca_grid_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
452
+ ca_size_t *strides, void *data, int dir)
262
453
  {
263
- CAGrid *ca = (CAGrid *) ap;
264
- CArray **grid = ca->grid;
265
- ca_size_t idx0[CA_RANK_MAX];
266
- int8_t i;
267
- for (i=0; i<ca->ndim; i++) {
268
- ca_fetch_addr(grid[i], idx[i], &idx0[i]);
454
+ CAGrid *g = (CAGrid *) ap;
455
+ CArray *parent = g->parent;
456
+ int8_t ndim = g->ndim;
457
+ int8_t inner = ndim - 1;
458
+ ca_size_t pnative[CA_RANK_MAX]; /* parent row-major byte strides */
459
+ ca_size_t gnative[CA_RANK_MAX]; /* grid row-major byte strides */
460
+ ca_size_t dstride[CA_RANK_MAX]; /* data row-major byte strides over counts */
461
+ ca_size_t src_step[CA_RANK_MAX]; /* element step into grid per axis (request) */
462
+ ca_size_t o[CA_RANK_MAX]; /* odometer over outer axes */
463
+ ca_size_t s;
464
+ int8_t k;
465
+ int aligned = 1;
466
+ char *d = (char *) data;
467
+
468
+ s = parent->bytes;
469
+ for (k = ndim - 1; k >= 0; k--) { pnative[k] = s; s *= parent->dim[k]; }
470
+ s = g->bytes;
471
+ for (k = ndim - 1; k >= 0; k--) { gnative[k] = s; s *= g->dim[k]; }
472
+ s = g->bytes;
473
+ for (k = ndim - 1; k >= 0; k--) { dstride[k] = s; s *= counts[k]; }
474
+
475
+ /* The structural decomposition assumes the request is axis-aligned: each
476
+ axis k is accessed by a stride that is a whole multiple of the grid's own
477
+ axis-k stride (src_step[k] = strides[k]/gnative[k]). A reshaped or
478
+ transposed leaf breaks this (cross-axis / non-multiple strides); fall back
479
+ to per-cell delivery (correct, still no whole-view attach). The wiring
480
+ guards ndim == grid->ndim, so counts/strides have ndim entries here. */
481
+ for (k = 0; k < ndim; k++) {
482
+ if (strides[k] % gnative[k] != 0) { aligned = 0; break; }
483
+ src_step[k] = strides[k] / gnative[k];
269
484
  }
270
- ca_fetch_index(ca->parent, idx0, ptr);
271
- }
272
485
 
273
- static void
274
- ca_grid_func_store_index (void *ap, ca_size_t *idx, void *ptr)
275
- {
276
- CAGrid *ca = (CAGrid *) ap;
277
- CArray **grid = ca->grid;
278
- ca_size_t idx0[CA_RANK_MAX];
279
- int8_t i;
280
- for (i=0; i<ca->ndim; i++) {
281
- ca_fetch_addr(grid[i], idx[i], &idx0[i]);
486
+ if (!aligned) {
487
+ ca_size_t idx[CA_RANK_MAX], doff = 0, base = 0;
488
+ for (k = 0; k < ndim; k++) base += starts[k] * gnative[k];
489
+ for (k = 0; k < ndim; k++) idx[k] = 0;
490
+ while (1) {
491
+ ca_size_t goff = base, gidx[CA_RANK_MAX];
492
+ for (k = 0; k < ndim; k++) goff += idx[k] * strides[k];
493
+ ca_addr2index((CArray *) g, goff / g->bytes, gidx);
494
+ ca_grid_func_xfer_index(g, gidx, d + doff, dir);
495
+ doff += g->bytes;
496
+ k = ndim - 1;
497
+ while (k >= 0) { if (++idx[k] < counts[k]) break; idx[k] = 0; k--; }
498
+ if (k < 0) break;
499
+ }
500
+ return;
501
+ }
502
+
503
+ /* Fast path: when parent is attached (ptr != NULL), translate the
504
+ sub-region request (starts/counts/src_step) into a transient
505
+ ca_axis_desc_t[] sub-descriptor and call
506
+ ca_axis_dispatch_gather/_scatter directly. This is the same engine
507
+ entry that ca_grid_func_xfer_all uses (minus the ca_attach(parent)
508
+ call, which per-region functions must not do). Collapses N
509
+ outer-row dispatch calls into 1.
510
+
511
+ INDEX axis with src_step==1: zero-copy pointer offset into
512
+ g->axes[k].indices. INDEX axis with src_step!=1: materialise a
513
+ sub-indices array via ALLOCV (per-call, not per-row). STRIDE axis:
514
+ trivially fold starts/src_step into start/step.
515
+
516
+ Parent unattached falls through to the legacy per-row outer loop. */
517
+ if (parent->ptr) {
518
+ ca_axis_desc_t sub_axes[CA_RANK_MAX];
519
+ ca_size_t parent_axis_dims[CA_RANK_MAX];
520
+ ca_size_t *index_bufs[CA_RANK_MAX];
521
+ volatile VALUE holders[CA_RANK_MAX];
522
+ ca_size_t total_elements = 1;
523
+ ca_size_t j;
524
+ int8_t have_alloc = 0;
525
+
526
+ for (k = 0; k < ndim; k++) { index_bufs[k] = NULL; holders[k] = Qnil; }
527
+
528
+ for (k = 0; k < ndim; k++) {
529
+ parent_axis_dims[k] = parent->dim[k];
530
+ total_elements *= counts[k];
531
+ if (g->axes[k].kind == CAG_AXIS_STRIDE) {
532
+ sub_axes[k].kind = CA_AXIS_KIND_STRIDE;
533
+ sub_axes[k].count = counts[k];
534
+ sub_axes[k].start = g->axes[k].start + starts[k] * g->axes[k].step;
535
+ sub_axes[k].step = g->axes[k].step * src_step[k];
536
+ sub_axes[k].indices = NULL;
537
+ } else {
538
+ sub_axes[k].kind = CA_AXIS_KIND_INDEX;
539
+ sub_axes[k].count = counts[k];
540
+ sub_axes[k].start = 0;
541
+ sub_axes[k].step = 0;
542
+ if (src_step[k] == 1) {
543
+ /* zero-copy: pointer offset into the original indices */
544
+ sub_axes[k].indices = g->axes[k].indices + starts[k];
545
+ } else {
546
+ /* sub-sampled or reversed: materialise sub-indices */
547
+ index_bufs[k] = ALLOCV_N(ca_size_t, holders[k], counts[k]);
548
+ for (j = 0; j < counts[k]; j++) {
549
+ index_bufs[k][j] =
550
+ g->axes[k].indices[starts[k] + j * src_step[k]];
551
+ }
552
+ sub_axes[k].indices = index_bufs[k];
553
+ have_alloc = 1;
554
+ }
555
+ }
556
+ }
557
+
558
+ if (dir == CA_XFER_GET) {
559
+ ca_axis_dispatch_gather(parent, parent_axis_dims, sub_axes, ndim,
560
+ g->bytes, total_elements, NULL, d);
561
+ } else {
562
+ ca_axis_dispatch_scatter(parent, parent_axis_dims, sub_axes, ndim,
563
+ g->bytes, total_elements, d);
564
+ }
565
+
566
+ if (have_alloc) {
567
+ for (k = 0; k < ndim; k++) {
568
+ if (index_bufs[k] != NULL) ALLOCV_END(holders[k]);
569
+ }
570
+ }
571
+ return;
572
+ }
573
+
574
+ /* Fallback (parent unattached): per-row outer loop + per-row
575
+ ca_xfer_stride/ca_xfer_addrs. */
576
+ for (k = 0; k < ndim; k++) o[k] = 0;
577
+
578
+ while (1) {
579
+ ca_size_t pbase = 0, doff = 0;
580
+ for (k = 0; k < inner; k++) {
581
+ ca_size_t gpos = starts[k] + o[k] * src_step[k];
582
+ ca_size_t ppos = (g->axes[k].kind == CAG_AXIS_STRIDE)
583
+ ? (g->axes[k].start + gpos * g->axes[k].step)
584
+ : g->axes[k].indices[gpos];
585
+ pbase += ppos * pnative[k];
586
+ doff += o[k] * dstride[k];
587
+ }
588
+
589
+ if (g->axes[inner].kind == CAG_AXIS_STRIDE) {
590
+ ca_size_t pstarts[CA_RANK_MAX], pcounts[CA_RANK_MAX], pstrides[CA_RANK_MAX];
591
+ ca_size_t inner_pbase =
592
+ (g->axes[inner].start + starts[inner] * g->axes[inner].step) * pnative[inner];
593
+ ca_addr2index((CArray *) parent, (pbase + inner_pbase) / parent->bytes, pstarts);
594
+ for (k = 0; k < ndim; k++) { pcounts[k] = 1; pstrides[k] = 0; }
595
+ pcounts[inner] = counts[inner];
596
+ pstrides[inner] = src_step[inner] * g->axes[inner].step * pnative[inner];
597
+ ca_xfer_stride(parent, pstarts, pcounts, pstrides, d + doff, dir);
598
+ }
599
+ else {
600
+ ca_size_t *addrs;
601
+ ca_size_t j, n = counts[inner];
602
+ volatile VALUE holder;
603
+ addrs = ALLOCV_N(ca_size_t, holder, n);
604
+ for (j = 0; j < n; j++) {
605
+ ca_size_t gpos = starts[inner] + j * src_step[inner];
606
+ ca_size_t ppos = g->axes[inner].indices[gpos];
607
+ addrs[j] = (pbase + ppos * pnative[inner]) / parent->bytes;
608
+ }
609
+ ca_xfer_addrs(parent, n, addrs, d + doff, dir);
610
+ ALLOCV_END(holder);
611
+ }
612
+
613
+ k = inner - 1;
614
+ while (k >= 0) { if (++o[k] < counts[k]) break; o[k] = 0; k--; }
615
+ if (k < 0) break;
282
616
  }
283
- ca_store_index(ca->parent, idx0, ptr);
284
617
  }
285
618
 
286
619
  static void
@@ -288,27 +621,34 @@ ca_grid_func_allocate (void *ap)
288
621
  {
289
622
  CAGrid *ca = (CAGrid *) ap;
290
623
  ca_attach(ca->parent);
291
- /* ca->ptr = ALLOC_N(char, ca_length(ca)); */
292
- ca->ptr = malloc_with_check(ca_length(ca));
624
+ ca->ptr = xmalloc(ca_length(ca));
293
625
  }
294
626
 
627
+ /* attach / sync / copy_data / sync_data / fill_data all go through the
628
+ descriptor engine (ca_axis_dispatch.c). This file supplies only
629
+ describe_axes; the engine does the rest. */
630
+
295
631
  static void
296
632
  ca_grid_func_attach (void *ap)
297
633
  {
298
- void ca_grid_attach (CAGrid *cb);
299
-
300
634
  CAGrid *ca = (CAGrid *) ap;
635
+ ca_axis_desc_t desc[CA_RANK_MAX];
636
+ ca_size_t pdims[CA_RANK_MAX];
301
637
  ca_attach(ca->parent);
302
- /* ca->ptr = ALLOC_N(char, ca_length(ca)); */
303
- ca->ptr = malloc_with_check(ca_length(ca));
304
- ca_grid_attach(ca);
638
+ ca_grid_describe_axes(ca, desc, pdims);
639
+ ca->ptr = ca_axis_dispatch_attach(ca->parent, pdims, desc, ca->ndim,
640
+ ca->bytes, ca->elements, NULL);
305
641
  }
306
642
 
307
643
  static void
308
644
  ca_grid_func_sync (void *ap)
309
645
  {
310
646
  CAGrid *ca = (CAGrid *) ap;
311
- ca_grid_sync(ca);
647
+ ca_axis_desc_t desc[CA_RANK_MAX];
648
+ ca_size_t pdims[CA_RANK_MAX];
649
+ ca_grid_describe_axes(ca, desc, pdims);
650
+ ca_axis_dispatch_scatter(ca->parent, pdims, desc, ca->ndim, ca->bytes,
651
+ ca->elements, ca->ptr);
312
652
  ca_sync(ca->parent);
313
653
  }
314
654
 
@@ -316,42 +656,79 @@ static void
316
656
  ca_grid_func_detach (void *ap)
317
657
  {
318
658
  CAGrid *ca = (CAGrid *) ap;
319
- free(ca->ptr);
659
+ xfree(ca->ptr);
320
660
  ca->ptr = NULL;
321
661
  ca_detach(ca->parent);
322
662
  }
323
663
 
664
+ /* xfer_all: fast path on parent->ptr, with no silent transitive attach
665
+ of the parent. A cold parent uses a proper 2-pass -- materialise the
666
+ parent into scratch via ca_xfer_all, temporarily expose it as
667
+ parent->ptr, run the fast path, then (for PUT) write scratch back. */
324
668
  static void
325
- ca_grid_func_copy_data (void *ap, void *ptr)
669
+ ca_grid_func_run_fast_path (CAGrid *ca, char *data, int dir)
326
670
  {
327
- CAGrid *ca = (CAGrid *) ap;
328
- char *ptr0 = ca->ptr;
329
- ca_attach(ca->parent);
330
- ca->ptr = ptr;
331
- ca_grid_attach(ca);
332
- ca->ptr = ptr0;
333
- ca_detach(ca->parent);
671
+ ca_axis_desc_t desc[CA_RANK_MAX];
672
+ ca_size_t pdims[CA_RANK_MAX];
673
+ ca_grid_describe_axes(ca, desc, pdims);
674
+ if ( dir == CA_XFER_GET ) {
675
+ ca_axis_dispatch_gather(ca->parent, pdims, desc, ca->ndim, ca->bytes,
676
+ ca->elements, NULL, data);
677
+ } else {
678
+ ca_axis_dispatch_scatter(ca->parent, pdims, desc, ca->ndim, ca->bytes,
679
+ ca->elements, data);
680
+ }
334
681
  }
335
682
 
336
683
  static void
337
- ca_grid_func_sync_data (void *ap, void *ptr)
684
+ ca_grid_func_xfer_all (void *ap, void *data, int dir)
338
685
  {
339
686
  CAGrid *ca = (CAGrid *) ap;
340
- char *ptr0 = ca->ptr;
341
- ca_attach(ca->parent);
342
- ca->ptr = ptr;
343
- ca_grid_sync(ca);
344
- ca->ptr = ptr0;
345
- ca_sync(ca->parent);
346
- ca_detach(ca->parent);
687
+ if ( ca->parent->ptr ) {
688
+ ca_grid_func_run_fast_path(ca, (char *) data, dir);
689
+ return;
690
+ }
691
+ /* Proper 2-pass cold fallback. */
692
+ {
693
+ volatile VALUE holder;
694
+ CArray *parent = ca->parent;
695
+ ca_size_t plen = parent->elements * parent->bytes;
696
+ char *parent_scratch = ALLOCV_N(char, holder, plen);
697
+ char *parent_ptr_saved = parent->ptr;
698
+ ca_xfer_all(parent, parent_scratch, CA_XFER_GET);
699
+ parent->ptr = parent_scratch;
700
+ ca_grid_func_run_fast_path(ca, (char *) data, dir);
701
+ if ( dir == CA_XFER_PUT ) {
702
+ ca_xfer_all(parent, parent_scratch, CA_XFER_PUT);
703
+ }
704
+ parent->ptr = parent_ptr_saved;
705
+ ALLOCV_END(holder);
706
+ }
347
707
  }
348
708
 
349
709
  static void
350
710
  ca_grid_func_fill_data (void *ap, void *ptr)
351
711
  {
352
712
  CAGrid *ca = (CAGrid *) ap;
713
+ ca_axis_desc_t desc[CA_RANK_MAX];
714
+ ca_size_t pdims[CA_RANK_MAX];
715
+
716
+ ca_grid_describe_axes(ca, desc, pdims);
717
+
718
+ /* Writing the selected cells used to go through a whole-parent attach and
719
+ sync. Where that attach is a gather rather than an alias, the cells this
720
+ view did not select make the round trip for nothing -- and through a
721
+ lossy layer they do not come back the same. Hand each slab to the parent
722
+ as a region instead. (PROPOSAL_PARTIAL_FILL_WHOLE_ROOT_WRITEBACK.md) */
723
+ if ( !ca_is_attached(ca->parent) && !ca_attach_is_alias(ca->parent) ) {
724
+ ca_axis_dispatch_fill_value_via_parent(ca->parent, pdims, desc, ca->ndim,
725
+ ca->bytes, ca->elements, ptr);
726
+ return;
727
+ }
728
+
353
729
  ca_attach(ca->parent);
354
- ca_grid_fill(ca, ptr);
730
+ ca_axis_dispatch_fill_value(ca->parent, pdims, desc, ca->ndim, ca->bytes,
731
+ ca->elements, ptr);
355
732
  ca_sync(ca->parent);
356
733
  ca_detach(ca->parent);
357
734
  }
@@ -367,271 +744,77 @@ ca_grid_func_create_mask (void *ap)
367
744
  }
368
745
 
369
746
  ca->mask = (CArray *) ca_grid_new_share(ca->parent->mask,
370
- ca->dim, ca->grid, ca->contig);
747
+ ca->ndim, ca->axes);
371
748
  }
372
749
 
373
750
  ca_operation_function_t ca_grid_func = {
374
751
  -1, /* CA_OBJ_GRID */
375
- CA_VIRTUAL_ARRAY,
752
+ CA_VIEW_ARRAY,
376
753
  free_ca_grid,
377
754
  ca_grid_func_clone,
378
- ca_grid_func_ptr_at_addr,
379
- ca_grid_func_ptr_at_index,
380
- NULL,
381
- ca_grid_func_fetch_index,
382
- NULL,
383
- ca_grid_func_store_index,
384
755
  ca_grid_func_allocate,
385
756
  ca_grid_func_attach,
386
757
  ca_grid_func_sync,
387
758
  ca_grid_func_detach,
388
- ca_grid_func_copy_data,
389
- ca_grid_func_sync_data,
390
759
  ca_grid_func_fill_data,
391
760
  ca_grid_func_create_mask,
761
+ ca_grid_func_xfer_index,
762
+ ca_grid_func_xfer_addrs,
763
+ ca_grid_func_fold_stride,
764
+ ca_grid_func_xfer_stride,
765
+ ca_grid_func_xfer_all,
392
766
  };
393
767
 
394
768
  /* ------------------------------------------------------------------- */
395
769
 
396
- #define proc_grid_attach(type) \
397
- { \
398
- ca_size_t *pi = (ca_size_t*) ca_ptr_at_addr(grid[level], 0); \
399
- type *p = (type*) ca_ptr_at_index(ca, idx); \
400
- type *q = (type*) ca_ptr_at_index(ca->parent, idx0); \
401
- for (i=0; i<ca->dim[level]; i++, pi++, p++) { \
402
- k = *pi; \
403
- *p = *(q+k); \
404
- } \
405
- }
406
-
407
- static void
408
- ca_grid_attach_loop (CAGrid *ca, int16_t level, ca_size_t *idx, ca_size_t *idx0)
409
- {
410
- CArray **grid = ca->grid;
411
- ca_size_t i, k;
412
-
413
- if ( level == ca->ndim - 1 ) {
414
- idx[level] = 0;
415
- idx0[level] = 0;
416
- if ( ca->contig[level] ) {
417
- memcpy(ca_ptr_at_index(ca, idx), ca_ptr_at_index(ca->parent, idx0),
418
- ca->bytes * ca->dim[level]);
419
- }
420
- else {
421
- switch ( ca->bytes ) {
422
- case 1: proc_grid_attach(int8_t); break;
423
- case 2: proc_grid_attach(int16_t); break;
424
- case 4: proc_grid_attach(int32_t); break;
425
- case 8: proc_grid_attach(float64_t); break;
426
- default:
427
- {
428
- ca_size_t *pi = (ca_size_t*) ca_ptr_at_addr(grid[level], 0);
429
- char *p = ca_ptr_at_index(ca, idx);
430
- char *q;
431
- idx0[level] = 0;
432
- q = ca_ptr_at_index(ca->parent, idx0);
433
- for (i=0; i<ca->dim[level]; i++, pi++, p+=ca->bytes) {
434
- k = *pi;
435
- memcpy(p, q + ca->bytes * k, ca->bytes);
436
- }
437
- }
438
- }
439
- }
440
- }
441
- else {
442
- if ( ca->contig[level] ) {
443
- for (i=0; i<ca->dim[level]; i++) {
444
- idx[level] = i;
445
- idx0[level] = i;
446
- ca_grid_attach_loop(ca, level+1, idx, idx0);
447
- }
448
- }
449
- else {
450
- ca_size_t *pi;
451
- pi = (ca_size_t*) ca_ptr_at_addr(grid[level], 0);
452
- for (i=0; i<ca->dim[level]; i++, pi++) {
453
- k = *pi;
454
- idx[level] = i;
455
- idx0[level] = k;
456
- ca_grid_attach_loop(ca, level+1, idx, idx0);
457
- }
458
- }
459
- }
460
- }
461
-
462
- static void
463
- ca_grid_attach (CAGrid *ca)
464
- {
465
- ca_size_t idx[CA_RANK_MAX];
466
- ca_size_t idx0[CA_RANK_MAX];
467
- ca_grid_attach_loop(ca, (int16_t) 0, idx, idx0);
468
- }
469
-
470
- #define proc_grid_sync(type) \
471
- { \
472
- ca_size_t *pi = (ca_size_t*) ca_ptr_at_addr(grid[level], 0); \
473
- type *p = (type*) ca_ptr_at_index(ca, idx); \
474
- type *q = (type*) ca_ptr_at_index(ca->parent, idx0); \
475
- for (i=0; i<ca->dim[level]; i++, pi++, p++) { \
476
- k = *pi; \
477
- *(q+k) = *p; \
478
- } \
479
- }
480
-
481
- static void
482
- ca_grid_sync_loop (CAGrid *ca, int16_t level, ca_size_t *idx, ca_size_t *idx0)
483
- {
484
- CArray **grid = ca->grid;
485
- ca_size_t i, k;
486
-
487
- if ( level == ca->ndim - 1 ) {
488
- idx[level] = 0;
489
- idx0[level] = 0;
490
- if ( ca->contig[level] ) {
491
- memcpy(ca_ptr_at_index(ca->parent, idx0), ca_ptr_at_index(ca, idx),
492
- ca->bytes * ca->dim[level]);
493
- }
494
- else {
495
- switch ( ca->bytes ) {
496
- case 1: proc_grid_sync(int8_t); break;
497
- case 2: proc_grid_sync(int16_t); break;
498
- case 4: proc_grid_sync(int32_t); break;
499
- case 8: proc_grid_sync(float64_t); break;
500
- default:
501
- {
502
- ca_size_t *pi = (ca_size_t*) ca_ptr_at_addr(grid[level], 0);
503
- char *p = ca_ptr_at_index(ca, idx);
504
- char *q;
505
- idx0[level] = 0;
506
- q = ca_ptr_at_index(ca->parent, idx0);
507
- for (i=0; i<ca->dim[level]; i++, pi++, p+=ca->bytes) {
508
- k = *pi;
509
- memcpy(q + ca->bytes * k, p, ca->bytes);
510
- }
511
- }
512
- }
513
- }
514
- }
515
- else {
516
- if ( ca->contig[level] ) {
517
- for (i=0; i<ca->dim[level]; i++) {
518
- idx[level] = i;
519
- idx0[level] = i;
520
- ca_grid_sync_loop(ca, level+1, idx, idx0);
521
- }
522
- }
523
- else {
524
- for (i=0; i<ca->dim[level]; i++) {
525
- k = *(ca_size_t*) ca_ptr_at_addr(grid[level], i);
526
- idx[level] = i;
527
- idx0[level] = k;
528
- ca_grid_sync_loop(ca, level+1, idx, idx0);
529
- }
530
- }
531
- }
532
- }
533
-
534
- static void
535
- ca_grid_sync (CAGrid *ca)
536
- {
537
- ca_size_t idx[CA_RANK_MAX];
538
- ca_size_t idx0[CA_RANK_MAX];
539
- ca_grid_sync_loop(ca, (int16_t) 0, idx, idx0);
540
- }
541
-
542
- #define proc_grid_fill(type) \
543
- { \
544
- ca_size_t *pi = (ca_size_t*) ca_ptr_at_addr(grid[level], 0); \
545
- type fval = *(type*)ptr; \
546
- type *q = (type*) ca_ptr_at_index(ca->parent, idx0); \
547
- for (i=0; i<ca->dim[level]; i++, pi++) { \
548
- k = *pi; \
549
- *(q+k) = fval; \
550
- } \
551
- }
552
-
553
- static void
554
- ca_grid_fill_loop (CAGrid *ca, char *ptr,
555
- int16_t level, ca_size_t *idx0)
556
- {
557
- CArray **grid = ca->grid;
558
- ca_size_t i, k;
559
- if ( level == ca->ndim - 1 ) {
560
- idx0[level] = 0;
561
- if ( ca->contig[level] ) {
562
- char *p = ca_ptr_at_index(ca->parent, idx0);
563
- for (i=0; i<ca->dim[level]; i++) {
564
- memcpy(p, ptr, ca->bytes);
565
- p += ca->bytes;
566
- }
567
- }
568
- else {
569
- switch ( ca->bytes ) {
570
- case 1: proc_grid_fill(int8_t); break;
571
- case 2: proc_grid_fill(int16_t); break;
572
- case 4: proc_grid_fill(int32_t); break;
573
- case 8: proc_grid_fill(float64_t); break;
574
- default:
575
- {
576
- ca_size_t *pi = (ca_size_t*) ca_ptr_at_addr(grid[level], 0);
577
- char *q;
578
- idx0[level] = 0;
579
- q = ca_ptr_at_index(ca->parent, idx0);
580
- for (i=0; i<ca->dim[level]; i++, pi++) {
581
- k = *pi;
582
- memcpy(q + ca->bytes * k, ptr, ca->bytes);
583
- }
584
- }
585
- }
586
- }
587
- }
588
- else {
589
- if ( ca->contig[level] ) {
590
- for (i=0; i<ca->dim[level]; i++) {
591
- idx0[level] = i;
592
- ca_grid_fill_loop(ca, ptr, level+1, idx0);
593
- }
594
- }
595
- else {
596
- for (i=0; i<ca->dim[level]; i++) {
597
- k = *(ca_size_t*) ca_ptr_at_addr(grid[level], i);
598
- idx0[level] = k;
599
- ca_grid_fill_loop(ca, ptr, level+1, idx0);
600
- }
601
- }
602
- }
603
- }
604
-
605
- static void
606
- ca_grid_fill (CAGrid *ca, char *ptr)
607
- {
608
- ca_size_t idx0[CA_RANK_MAX];
609
- ca_grid_fill_loop(ca, ptr, (int16_t) 0, idx0);
610
- }
611
-
612
- /* ------------------------------------------------------------------- */
613
-
614
770
  VALUE
615
- rb_ca_grid_new (VALUE cary, ca_size_t *dim, CArray **grid)
771
+ rb_ca_grid_new (VALUE cary, int8_t ndim, cag_axis_t *protos)
616
772
  {
617
773
  volatile VALUE obj;
618
774
  CArray *parent;
619
775
  CAGrid *ca;
620
776
  rb_check_carray_object(cary);
621
777
  TypedData_Get_Struct(cary, CArray, &carray_data_type, parent);
622
- ca = ca_grid_new(parent, dim, grid);
778
+ ca = ca_grid_new(parent, ndim, protos);
623
779
  obj = ca_wrap_struct(ca);
624
780
  rb_ca_set_parent(obj, cary);
625
- rb_ca_data_type_inherit(obj, cary);
626
781
  return obj;
627
782
  }
628
783
 
629
- /* yard:
630
- class CArray
631
- def grid
632
- end
633
- end
634
- */
784
+ /* Detect a plain Ruby Range (Integer first/last, exclude_end?, step=1
785
+ implicit) and convert it to STRIDE (start, count, step=1).
786
+ Returns 1 on success (fills *out_start, *out_count), 0 on fallback
787
+ (caller should materialise to INDEX). Negative endpoints are
788
+ normalised against dim_size like everywhere else in CArray. */
789
+ static int
790
+ cag_range_to_stride (VALUE range, ca_size_t dim_size,
791
+ ca_size_t *out_start, ca_size_t *out_count)
792
+ {
793
+ VALUE rb_first, rb_last;
794
+ ca_size_t first, last, count;
795
+ int exclude_end;
796
+
797
+ if ( ! rb_obj_is_kind_of(range, rb_cRange) ) return 0;
798
+ rb_first = rb_funcall(range, rb_intern("first"), 0);
799
+ rb_last = rb_funcall(range, rb_intern("last"), 0);
800
+ if ( ! FIXNUM_P(rb_first) || ! FIXNUM_P(rb_last) ) return 0;
801
+ exclude_end = RTEST(rb_funcall(range, rb_intern("exclude_end?"), 0));
802
+
803
+ first = NUM2SIZE(rb_first);
804
+ last = NUM2SIZE(rb_last);
805
+ if (first < 0) first += dim_size;
806
+ if (last < 0) last += dim_size;
807
+ if (exclude_end) last -= 1;
808
+
809
+ if (first < 0 || first >= dim_size) return 0;
810
+ if (last < first - 1) return 0; /* empty range */
811
+ if (last >= dim_size) return 0;
812
+ count = last - first + 1;
813
+
814
+ *out_start = first;
815
+ *out_count = count;
816
+ return 1;
817
+ }
635
818
 
636
819
  VALUE
637
820
  rb_ca_grid (int argc, VALUE *argv, VALUE self)
@@ -639,12 +822,23 @@ rb_ca_grid (int argc, VALUE *argv, VALUE self)
639
822
  volatile VALUE obj, ridx, rval;
640
823
  volatile VALUE list = rb_ary_new();
641
824
  CArray *ca;
642
- CArray *ci[CA_RANK_MAX];
643
- ca_size_t dim[CA_RANK_MAX];
644
- CArray *grid[CA_RANK_MAX];
825
+ CArray *ci[CA_RANK_MAX]; /* CAWrap holders for INDEX axes; NULL for STRIDE */
826
+ cag_axis_t protos[CA_RANK_MAX];
645
827
  ca_size_t i;
646
828
 
647
- TypedData_Get_Struct(self, CArray, &carray_data_type,ca);
829
+ /* Dispatch hook: route mixed AP + single boolean to CASelectAxis
830
+ (slab-copy optimised path). Falls through to per-cell CAGrid gather
831
+ when not eligible (integer index, multiple INDIRECT axes, indirect
832
+ not on axis 0). Inline argv[0] type check avoids the function call
833
+ when axis 0 is nil/Integer (most patterns with INDIRECT on inner axes). */
834
+ extern int ca_csa_dispatch_bypass;
835
+ if ( ! ca_csa_dispatch_bypass && argc > 0 && TYPE(argv[0]) == T_DATA ) {
836
+ if ( rb_ca_select_axis_eligible_p(argc, argv, self) ) {
837
+ return rb_ca_select_axis(argc, argv, self);
838
+ }
839
+ }
840
+
841
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
648
842
 
649
843
  ridx = rb_ary_new4(argc, argv);
650
844
 
@@ -679,43 +873,82 @@ rb_ca_grid (int argc, VALUE *argv, VALUE self)
679
873
  return rb_ca_grid(argc, argv, ref);
680
874
  }
681
875
 
682
- for (i=0; i<RARRAY_LEN(ridx); i++) {
876
+ /* Build per-axis protos:
877
+ nil -> STRIDE(0, parent.dim, 1), no CArray
878
+ Range -> STRIDE if plain integer Range, else fall through to INDEX
879
+ CArray -> INDEX with indices = (ca_size_t*)ca_wrap_readonly(...)->ptr */
880
+ for (i = 0; i < ca->ndim; i++) {
881
+ ca_size_t dim_size = ca->dim[i];
683
882
  rval = rb_ary_entry(ridx, i);
883
+ ci[i] = NULL;
684
884
  if ( NIL_P(rval) ) {
685
- ci[i] = NULL;
686
- dim[i] = ca->dim[i];
687
- grid[i] = NULL;
885
+ protos[i].kind = CAG_AXIS_STRIDE;
886
+ protos[i].count = dim_size;
887
+ protos[i].start = 0;
888
+ protos[i].step = 1;
889
+ protos[i].indices = NULL;
890
+ continue;
688
891
  }
689
- else {
690
- if ( rb_obj_is_carray(rval) ) {
691
- if ( rb_ca_is_boolean_type(rval) ) {
692
- rval = rb_ca_where(rval);
693
- }
892
+ if ( rb_obj_is_kind_of(rval, rb_cRange) ) {
893
+ ca_size_t start, count;
894
+ if ( cag_range_to_stride(rval, dim_size, &start, &count) ) {
895
+ protos[i].kind = CAG_AXIS_STRIDE;
896
+ protos[i].count = count;
897
+ protos[i].start = start;
898
+ protos[i].step = 1;
899
+ protos[i].indices = NULL;
900
+ continue;
694
901
  }
695
- else if ( rb_obj_is_kind_of(rval, rb_cRange) ) {
696
- rval = rb_funcall(rb_mKernel, rb_intern("CA_SIZE"), 1, rval);
697
- }
698
- else if ( TYPE(rval) == T_ARRAY ) {
699
- rb_raise(rb_eRuntimeError, "not implemented for this index");
902
+ /* fallback: materialise to integer array */
903
+ rval = rb_funcall(rb_mKernel, rb_intern("CA_SIZE"), 1, rval);
904
+ }
905
+ else if ( rb_obj_is_carray(rval) ) {
906
+ if ( rb_ca_is_boolean_type(rval) ) {
907
+ rval = rb_ca_where(rval);
700
908
  }
701
- ci[i] = ca_wrap_readonly(rval, CA_SIZE);
702
- rb_ary_push(list, rval);
703
- ca_attach(ci[i]);
909
+ }
910
+ else if ( TYPE(rval) == T_ARRAY ) {
911
+ rb_raise(rb_eRuntimeError, "not implemented for this index");
912
+ }
704
913
 
705
- if ( ca_is_any_masked(ci[i]) ) {
706
- dim[i] = ci[i]->elements - ca_count_masked(ci[i]);
707
- }
708
- else {
709
- dim[i] = ci[i]->elements;
914
+ /* INDEX axis */
915
+ ci[i] = ca_wrap_readonly(rval, CA_SIZE);
916
+ rb_ary_push(list, rval);
917
+ ca_attach(ci[i]);
918
+
919
+ protos[i].kind = CAG_AXIS_INDEX;
920
+ if ( ca_is_any_masked(ci[i]) ) {
921
+ /* mask-filtered snapshot: skip masked cells. We point protos[i]
922
+ .indices at a temp buffer alloc'd here and freed after setup
923
+ copies it (setup snapshots into its own ALLOC). Caller-side
924
+ lifetime: the temp lives until ca_grid_new returns. */
925
+ ca_size_t gsize = ci[i]->elements - ca_count_masked(ci[i]);
926
+ ca_size_t *src = (ca_size_t *) ci[i]->ptr;
927
+ boolean8_t *m = (boolean8_t *) ci[i]->mask->ptr;
928
+ ca_size_t *tmp = ALLOC_N(ca_size_t, gsize > 0 ? gsize : 1);
929
+ ca_size_t j, n = 0;
930
+ for (j = 0; j < ci[i]->elements; j++) {
931
+ if ( ! m[j] ) tmp[n++] = src[j];
710
932
  }
711
- grid[i] = ci[i];
933
+ protos[i].count = gsize;
934
+ protos[i].indices = tmp;
935
+ } else {
936
+ protos[i].count = ci[i]->elements;
937
+ protos[i].indices = (ca_size_t *) ci[i]->ptr;
712
938
  }
939
+ protos[i].start = 0;
940
+ protos[i].step = 0;
713
941
  }
714
942
 
715
- obj = rb_ca_grid_new(self, dim, grid);
943
+ obj = rb_ca_grid_new(self, ca->ndim, protos);
716
944
 
717
- for (i=0; i<RARRAY_LEN(ridx); i++) {
945
+ /* Cleanup: detach CAWraps + free temp masked-snapshot buffers (setup
946
+ has already copied indices into its own ALLOC). */
947
+ for (i = 0; i < ca->ndim; i++) {
718
948
  if ( ci[i] ) {
949
+ if ( ca_is_any_masked(ci[i]) ) {
950
+ xfree(protos[i].indices);
951
+ }
719
952
  ca_detach(ci[i]);
720
953
  }
721
954
  }
@@ -738,24 +971,197 @@ rb_ca_grid_initialize_copy (VALUE self, VALUE other)
738
971
  TypedData_Get_Struct(self, CAGrid, &cagrid_data_type, ca);
739
972
  TypedData_Get_Struct(other, CAGrid, &cagrid_data_type, cs);
740
973
 
741
- /* share grid info */
742
- ca_grid_setup(ca, cs->parent, cs->dim, cs->grid, cs->contig, 1);
974
+ if ( ca_func[CA_OBJ_GRID].pool_init ) {
975
+ ca_array_pool_alloc(ca, CA_OBJ_GRID, cs->ndim);
976
+ }
977
+ /* Share the source's axes buffer (CA_FLAG_SHARE_INDEX semantics:
978
+ source CAGrid owns the lifetime; clone aliases it). */
979
+ ca_grid_setup(ca, cs->parent, cs->ndim, cs->axes, 1);
743
980
 
744
981
  return self;
745
982
  }
746
983
 
747
984
 
748
985
 
986
+ /* ------------------------------------------------------------------- */
987
+ /* Producer interface: emit the per-axis descriptor array -- a 1:1
988
+ mechanical mapping from CAGrid's internal tagged storage to the
989
+ framework descriptor.
990
+
991
+ STRIDE axes (= nil-arg or arithmetic-progression Range) carry their
992
+ actual start/step, so axis-merge can fire on these axes when they
993
+ neighbour other STRIDE axes. INDEX axes borrow the owned
994
+ ca->axes[k].indices snapshot. */
995
+
996
+ void
997
+ ca_grid_describe_axes (void *ap, ca_axis_desc_t *out,
998
+ ca_size_t *out_parent_dims)
999
+ {
1000
+ CAGrid *ca = (CAGrid *) ap;
1001
+ int8_t k;
1002
+ for ( k = 0; k < ca->ndim; k++ ) {
1003
+ out_parent_dims[k] = ca->parent->dim[k];
1004
+ if ( ca->axes[k].kind == CAG_AXIS_STRIDE ) {
1005
+ out[k].kind = CA_AXIS_KIND_STRIDE;
1006
+ out[k].count = ca->axes[k].count;
1007
+ out[k].start = ca->axes[k].start;
1008
+ out[k].step = ca->axes[k].step;
1009
+ out[k].indices = NULL;
1010
+ } else {
1011
+ out[k].kind = CA_AXIS_KIND_INDEX;
1012
+ out[k].count = ca->axes[k].count;
1013
+ out[k].start = 0;
1014
+ out[k].step = 0;
1015
+ out[k].indices = ca->axes[k].indices;
1016
+ }
1017
+ }
1018
+ }
1019
+
1020
+ #ifdef CARRAY_DEV_BUILD
1021
+ /* ============================================================
1022
+ * Debug accessors (dev-only, stripped in release)
1023
+ *
1024
+ * Gated by CARRAY_DEV_BUILD (enabled via `extconf.rb --enable-dev-build`
1025
+ * or `CARRAY_DEV=1 rake build_ext`). They expose the per-axis descriptor
1026
+ * and the engine's raw output to Ruby so spec_ai can pin them; nothing in
1027
+ * lib/ or the rest of ext/ consumes them. CASelectAxis carries the same
1028
+ * four under the same fence.
1029
+ * ============================================================ */
1030
+
1031
+ /* Returns the per-axis descriptor as an Array, one entry per axis:
1032
+ [:stride, count, start, step] or [:index, count, indices]. */
1033
+ static VALUE
1034
+ rb_ca_grid_describe_axes (VALUE self)
1035
+ {
1036
+ CAGrid *ca;
1037
+ ca_axis_desc_t desc[CA_RANK_MAX];
1038
+ VALUE arr, entry, idx_ary;
1039
+ int8_t k;
1040
+ ca_size_t i;
1041
+
1042
+ ca_size_t pdims[CA_RANK_MAX];
1043
+ TypedData_Get_Struct(self, CAGrid, &cagrid_data_type, ca);
1044
+ ca_grid_describe_axes(ca, desc, pdims);
1045
+
1046
+ arr = rb_ary_new_capa(ca->ndim);
1047
+ for ( k = 0; k < ca->ndim; k++ ) {
1048
+ if ( desc[k].kind == CA_AXIS_KIND_STRIDE ) {
1049
+ entry = rb_ary_new_capa(4);
1050
+ rb_ary_push(entry, ID2SYM(rb_intern("stride")));
1051
+ rb_ary_push(entry, SIZE2NUM(desc[k].count));
1052
+ rb_ary_push(entry, SIZE2NUM(desc[k].start));
1053
+ rb_ary_push(entry, SIZE2NUM(desc[k].step));
1054
+ } else {
1055
+ entry = rb_ary_new_capa(3);
1056
+ rb_ary_push(entry, ID2SYM(rb_intern("index")));
1057
+ rb_ary_push(entry, SIZE2NUM(desc[k].count));
1058
+ idx_ary = rb_ary_new_capa(desc[k].count);
1059
+ for ( i = 0; i < desc[k].count; i++ ) {
1060
+ rb_ary_push(idx_ary, SIZE2NUM(desc[k].indices[i]));
1061
+ }
1062
+ rb_ary_push(entry, idx_ary);
1063
+ }
1064
+ rb_ary_push(arr, entry);
1065
+ }
1066
+ return arr;
1067
+ }
1068
+
1069
+ /* Returns the attach engine's gathered buffer as a String. */
1070
+ static VALUE
1071
+ rb_ca_grid_dispatch_attach_debug (VALUE self)
1072
+ {
1073
+ CAGrid *ca;
1074
+ ca_axis_desc_t desc[CA_RANK_MAX];
1075
+ char *buf;
1076
+ VALUE str;
1077
+
1078
+ ca_size_t pdims[CA_RANK_MAX];
1079
+ TypedData_Get_Struct(self, CAGrid, &cagrid_data_type, ca);
1080
+ ca_grid_describe_axes(ca, desc, pdims);
1081
+
1082
+ ca_attach(ca->parent);
1083
+ buf = ca_axis_dispatch_attach(ca->parent, pdims, desc, ca->ndim, ca->bytes,
1084
+ ca->elements, NULL);
1085
+ ca_detach(ca->parent);
1086
+
1087
+ str = rb_str_new(buf, ca->elements * ca->bytes);
1088
+ xfree(buf);
1089
+ return str;
1090
+ }
1091
+
1092
+ /* Drives the scatter engine with a view-shaped input String. */
1093
+ static VALUE
1094
+ rb_ca_grid_dispatch_scatter_debug (VALUE self, VALUE in_str)
1095
+ {
1096
+ CAGrid *ca;
1097
+ ca_axis_desc_t desc[CA_RANK_MAX];
1098
+ ca_size_t expected;
1099
+
1100
+ Check_Type(in_str, T_STRING);
1101
+ TypedData_Get_Struct(self, CAGrid, &cagrid_data_type, ca);
1102
+
1103
+ expected = ca->elements * ca->bytes;
1104
+ if ( (ca_size_t) RSTRING_LEN(in_str) != expected ) {
1105
+ rb_raise(rb_eArgError,
1106
+ "CAGrid#_dispatch_scatter_debug: input string length %lld "
1107
+ "!= expected %lld",
1108
+ (long long) RSTRING_LEN(in_str), (long long) expected);
1109
+ }
1110
+ ca_size_t pdims[CA_RANK_MAX];
1111
+ ca_grid_describe_axes(ca, desc, pdims);
1112
+
1113
+ ca_attach(ca->parent);
1114
+ ca_axis_dispatch_scatter(ca->parent, pdims, desc, ca->ndim, ca->bytes,
1115
+ ca->elements, RSTRING_PTR(in_str));
1116
+ ca_sync(ca->parent);
1117
+ ca_detach(ca->parent);
1118
+ return Qnil;
1119
+ }
1120
+
1121
+ /* Drives the fill_value engine with a one-element String. */
1122
+ static VALUE
1123
+ rb_ca_grid_dispatch_fill_value_debug (VALUE self, VALUE val_str)
1124
+ {
1125
+ CAGrid *ca;
1126
+ ca_axis_desc_t desc[CA_RANK_MAX];
1127
+
1128
+ Check_Type(val_str, T_STRING);
1129
+ TypedData_Get_Struct(self, CAGrid, &cagrid_data_type, ca);
1130
+
1131
+ if ( (ca_size_t) RSTRING_LEN(val_str) != ca->bytes ) {
1132
+ rb_raise(rb_eArgError,
1133
+ "CAGrid#_dispatch_fill_value_debug: value string length %lld "
1134
+ "!= bytes %lld",
1135
+ (long long) RSTRING_LEN(val_str), (long long) ca->bytes);
1136
+ }
1137
+ ca_size_t pdims[CA_RANK_MAX];
1138
+ ca_grid_describe_axes(ca, desc, pdims);
1139
+
1140
+ ca_attach(ca->parent);
1141
+ ca_axis_dispatch_fill_value(ca->parent, pdims, desc, ca->ndim, ca->bytes,
1142
+ ca->elements, RSTRING_PTR(val_str));
1143
+ ca_sync(ca->parent);
1144
+ ca_detach(ca->parent);
1145
+ return Qnil;
1146
+ }
1147
+ #endif /* CARRAY_DEV_BUILD */
1148
+
1149
+ /* ------------------------------------------------------------------- */
1150
+
749
1151
  void
750
- Init_ca_obj_grid ()
1152
+ Init_ca_obj_grid (void)
751
1153
  {
752
- rb_cCAGrid = rb_define_class("CAGrid", rb_cCAVirtual);
1154
+ rb_cCAGrid = rb_define_class("CAGrid", rb_cCAView);
753
1155
  rb_cCAGridMask = rb_define_class("CAGridMask", rb_cCAGrid);
754
1156
 
755
- CA_OBJ_GRID = ca_install_obj_type(rb_cCAGrid,
1157
+ ca_grid_func.struct_size = sizeof(CAGrid);
1158
+ ca_grid_func.pool_bytes = ca_grid_pool_bytes;
1159
+ ca_grid_func.pool_init = ca_grid_pool_init;
1160
+
1161
+ CA_OBJ_GRID = ca_install_obj_type(rb_cCAGrid,
756
1162
  &cagrid_data_type,
757
1163
  rb_cCAGridMask,
758
- &cagrid_mask_data_type, ca_grid_func);
1164
+ &cagrid_mask_data_type, &ca_grid_func, sizeof(ca_grid_func));
759
1165
  rb_define_const(rb_cObject, "CA_OBJ_GRID", INT2NUM(CA_OBJ_GRID));
760
1166
 
761
1167
  rb_define_method(rb_cCArray, "grid", rb_ca_grid, -1);
@@ -763,5 +1169,17 @@ Init_ca_obj_grid ()
763
1169
  rb_define_alloc_func(rb_cCAGrid, rb_ca_grid_s_allocate);
764
1170
  rb_define_method(rb_cCAGrid, "initialize_copy",
765
1171
  rb_ca_grid_initialize_copy, 1);
1172
+
1173
+ #ifdef CARRAY_DEV_BUILD
1174
+ /* debug accessors (dev-only, stripped in release) */
1175
+ rb_define_method(rb_cCAGrid, "_describe_axes",
1176
+ rb_ca_grid_describe_axes, 0);
1177
+ rb_define_method(rb_cCAGrid, "_dispatch_attach_debug",
1178
+ rb_ca_grid_dispatch_attach_debug, 0);
1179
+ rb_define_method(rb_cCAGrid, "_dispatch_scatter_debug",
1180
+ rb_ca_grid_dispatch_scatter_debug, 1);
1181
+ rb_define_method(rb_cCAGrid, "_dispatch_fill_value_debug",
1182
+ rb_ca_grid_dispatch_fill_value_debug, 1);
1183
+ #endif
766
1184
  }
767
1185