carray 2.0.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (386) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +6 -25
  3. data/CHANGELOG.md +338 -0
  4. data/{NEWS.md → CHANGELOG.v1.md} +3 -0
  5. data/LICENSE +1 -1
  6. data/README.md +120 -36
  7. data/carray.gemspec +32 -30
  8. data/ext/ca_array_pool.c +91 -0
  9. data/ext/ca_axis_descriptor.h +186 -0
  10. data/ext/ca_axis_dispatch.c +924 -0
  11. data/ext/ca_axis_group.c +1244 -0
  12. data/ext/ca_bincmp_dispatch.c +76 -0
  13. data/ext/ca_bincmp_dispatch.h +85 -0
  14. data/ext/ca_binop_dispatch.c +124 -0
  15. data/ext/ca_binop_dispatch.h +152 -0
  16. data/ext/ca_categorical_iterator.c +1375 -0
  17. data/ext/ca_compare.c +94 -0
  18. data/ext/ca_compare.h +26 -0
  19. data/ext/ca_composite_dispatch.c +414 -0
  20. data/ext/ca_composite_dispatch.h +116 -0
  21. data/ext/ca_for_buffer.h +96 -0
  22. data/ext/ca_for_each_element.h +239 -0
  23. data/ext/ca_group_iter.c +304 -0
  24. data/ext/ca_iter_substrate.h +325 -0
  25. data/ext/ca_kernel_iterator.c +4367 -0
  26. data/ext/ca_kernel_iterator.h +2596 -0
  27. data/ext/ca_moncmp_dispatch.c +37 -0
  28. data/ext/ca_moncmp_dispatch.h +62 -0
  29. data/ext/ca_monop_dispatch.c +200 -0
  30. data/ext/ca_monop_dispatch.h +235 -0
  31. data/ext/ca_obj_array.c +355 -359
  32. data/ext/ca_obj_bincmp.c +839 -0
  33. data/ext/ca_obj_binop.c +948 -0
  34. data/ext/ca_obj_bitarray.c +369 -164
  35. data/ext/ca_obj_bitfield.c +294 -234
  36. data/ext/ca_obj_block.c +189 -711
  37. data/ext/ca_obj_byte_swap.c +766 -0
  38. data/ext/ca_obj_const_string.c +967 -0
  39. data/ext/ca_obj_face.c +750 -0
  40. data/ext/ca_obj_face.h +279 -0
  41. data/ext/ca_obj_fake.c +239 -100
  42. data/ext/ca_obj_farray.c +54 -441
  43. data/ext/ca_obj_field.c +82 -529
  44. data/ext/ca_obj_fixlen_string.c +308 -0
  45. data/ext/ca_obj_grid.c +866 -440
  46. data/ext/ca_obj_meld.c +1039 -0
  47. data/ext/ca_obj_moncmp.c +588 -0
  48. data/ext/ca_obj_monop.c +1123 -0
  49. data/ext/ca_obj_object.c +866 -296
  50. data/ext/ca_obj_record.c +470 -0
  51. data/ext/ca_obj_reduce.c +97 -82
  52. data/ext/ca_obj_refer.c +593 -459
  53. data/ext/ca_obj_remap.c +475 -0
  54. data/ext/ca_obj_repeat.c +92 -477
  55. data/ext/ca_obj_roll.c +624 -0
  56. data/ext/ca_obj_select.c +344 -296
  57. data/ext/ca_obj_select_axis.c +1306 -0
  58. data/ext/ca_obj_shift.c +231 -793
  59. data/ext/ca_obj_source.c +78 -0
  60. data/ext/ca_obj_stack.c +1173 -0
  61. data/ext/ca_obj_stride.c +2584 -0
  62. data/ext/ca_obj_string.c +270 -0
  63. data/ext/ca_obj_tile.c +622 -0
  64. data/ext/ca_obj_time.c +548 -0
  65. data/ext/ca_obj_timedelta.c +437 -0
  66. data/ext/ca_obj_transpose.c +62 -516
  67. data/ext/ca_obj_triop.c +785 -0
  68. data/ext/ca_obj_window.c +1202 -565
  69. data/ext/ca_op_byte_swap.c +175 -0
  70. data/ext/ca_op_cmplx64.h +123 -0
  71. data/ext/ca_op_ipower.c +316 -0
  72. data/ext/ca_op_powi.h +88 -0
  73. data/ext/ca_sort_kernels.h +132 -0
  74. data/ext/ca_sweep_engine.c +473 -0
  75. data/ext/ca_sweep_engine.h +166 -0
  76. data/ext/ca_transform_common.c +235 -0
  77. data/ext/ca_triop_dispatch.c +55 -0
  78. data/ext/ca_triop_dispatch.h +62 -0
  79. data/ext/carray.h +810 -420
  80. data/ext/carray_access.c +873 -731
  81. data/ext/carray_attribute.c +98 -329
  82. data/ext/carray_bincount.c +255 -0
  83. data/ext/carray_broadcast.c +376 -0
  84. data/ext/carray_build_flags.h +3 -0
  85. data/ext/carray_call_cfunc.c +2897 -874
  86. data/ext/carray_call_cfunc.h +313 -0
  87. data/ext/carray_cast.c +1264 -315
  88. data/ext/carray_cast_func.rb +81 -40
  89. data/ext/carray_class.c +53 -63
  90. data/ext/carray_config.h +28 -0
  91. data/ext/carray_conversion.c +350 -346
  92. data/ext/carray_copy.c +168 -270
  93. data/ext/carray_core.c +1396 -206
  94. data/ext/carray_count.c +312 -0
  95. data/ext/carray_data_type.c +43 -19
  96. data/ext/carray_element.c +585 -213
  97. data/ext/carray_factorize.c +2542 -0
  98. data/ext/carray_generate.c +230 -559
  99. data/ext/carray_histogram.c +490 -0
  100. data/ext/carray_hold.c +228 -0
  101. data/ext/carray_index_classifier.c +1021 -0
  102. data/ext/carray_index_classifier.h +27 -0
  103. data/ext/carray_internal.h +136 -0
  104. data/ext/carray_kernels_bincmp.c +4446 -0
  105. data/ext/carray_kernels_binop.c +11001 -0
  106. data/ext/carray_kernels_init.c +1131 -0
  107. data/ext/carray_kernels_map.c +3467 -0
  108. data/ext/carray_kernels_moncmp.c +2097 -0
  109. data/ext/carray_kernels_monop.c +18313 -0
  110. data/ext/carray_kernels_reduce_aggregate.c +25837 -0
  111. data/ext/carray_kernels_reduce_boolean.c +330 -0
  112. data/ext/carray_kernels_reduce_cumulative.c +14593 -0
  113. data/ext/carray_kernels_reduce_extreme.c +16948 -0
  114. data/ext/carray_kernels_reduce_variance.c +3910 -0
  115. data/ext/carray_kernels_scan.c +3693 -0
  116. data/ext/carray_kernels_search.c +32138 -0
  117. data/ext/carray_kernels_sort.c +10626 -0
  118. data/ext/carray_kernels_triop.c +1392 -0
  119. data/ext/carray_lazy.c +737 -0
  120. data/ext/carray_loop.c +88 -200
  121. data/ext/carray_mask.c +853 -158
  122. data/ext/carray_math_kernel.h +120 -0
  123. data/ext/carray_mathfunc.c +10 -241
  124. data/ext/carray_median_percentile.c +1257 -0
  125. data/ext/carray_memory_view.c +1650 -0
  126. data/ext/carray_operator.c +1525 -320
  127. data/ext/carray_order.c +664 -1394
  128. data/ext/carray_partition.c +416 -0
  129. data/ext/carray_random.c +518 -0
  130. data/ext/carray_scatter.c +357 -0
  131. data/ext/carray_slab.c +1219 -0
  132. data/ext/carray_slab.h +84 -0
  133. data/ext/carray_sort.c +829 -0
  134. data/ext/carray_sort_kernel.c +620 -0
  135. data/ext/carray_struct.c +695 -0
  136. data/ext/carray_test.c +343 -229
  137. data/ext/carray_undef.c +34 -17
  138. data/ext/carray_utils.c +175 -74
  139. data/ext/extconf.rb +234 -55
  140. data/ext/mk_call_cfunc.rb +671 -0
  141. data/ext/mkkernel.rb +9096 -0
  142. data/ext/ruby_carray.c +211 -108
  143. data/ext/version.h +4 -14
  144. data/ext/version.rb +5 -13
  145. data/lib/carray/arrow_tensor.rb +401 -0
  146. data/lib/carray/attribute.rb +166 -0
  147. data/lib/carray/autoload_carray.rb +239 -0
  148. data/lib/carray/autoload_method_extension.rb +45 -0
  149. data/lib/carray/axis_group.rb +711 -0
  150. data/lib/carray/basics.rb +481 -0
  151. data/lib/carray/bincount_nd.rb +358 -0
  152. data/lib/carray/block_iterator.rb +614 -0
  153. data/lib/carray/boolean_reduce.rb +109 -0
  154. data/lib/carray/categorical.rb +561 -0
  155. data/lib/carray/categorical_iterator.rb +1084 -0
  156. data/lib/carray/complex.rb +150 -0
  157. data/lib/carray/conditional.rb +216 -0
  158. data/lib/carray/const_string.rb +228 -0
  159. data/lib/carray/construct.rb +160 -328
  160. data/lib/carray/core_extensions.rb +297 -0
  161. data/lib/carray/data_type_extension.rb +250 -0
  162. data/lib/carray/fixlen_string.rb +95 -0
  163. data/lib/carray/frame/concat.rb +132 -0
  164. data/lib/carray/frame/convert.rb +95 -0
  165. data/lib/carray/frame/csv_parser.rb +211 -0
  166. data/lib/carray/frame/frame.rb +642 -0
  167. data/lib/carray/frame/group.rb +186 -0
  168. data/lib/carray/frame/io.rb +321 -0
  169. data/lib/carray/frame/join.rb +248 -0
  170. data/lib/carray/frame/records.rb +99 -0
  171. data/lib/carray/frame/sort.rb +113 -0
  172. data/lib/carray/frame/verbs.rb +316 -0
  173. data/lib/carray/frame.rb +16 -0
  174. data/lib/carray/fuse_source.rb +123 -0
  175. data/lib/carray/fusion.rb +218 -0
  176. data/lib/carray/histogram.rb +512 -0
  177. data/lib/carray/inspect.rb +37 -26
  178. data/lib/carray/iterator.rb +58 -349
  179. data/lib/carray/lazy.rb +941 -0
  180. data/lib/carray/mask_gap_fill.rb +200 -0
  181. data/lib/carray/math.rb +78 -342
  182. data/lib/carray/meld_reduce.rb +289 -0
  183. data/lib/carray/methods/align_addr.rb +116 -0
  184. data/lib/carray/methods/bin.rb +128 -0
  185. data/lib/carray/methods/bincount.rb +87 -0
  186. data/lib/carray/methods/bit_string.rb +92 -0
  187. data/lib/carray/methods/broadcast.rb +63 -0
  188. data/lib/carray/methods/choose.rb +39 -0
  189. data/lib/carray/methods/composition.rb +280 -0
  190. data/lib/carray/methods/gather_nd.rb +206 -0
  191. data/lib/carray/methods/index.rb +39 -0
  192. data/lib/carray/methods/insert_block.rb +99 -0
  193. data/lib/carray/methods/is_in.rb +141 -0
  194. data/lib/carray/methods/join.rb +90 -0
  195. data/lib/carray/methods/locate_addr.rb +52 -0
  196. data/lib/carray/methods/mask_duplicates.rb +41 -0
  197. data/lib/carray/methods/meshgrid.rb +90 -0
  198. data/lib/carray/methods/mode.rb +126 -0
  199. data/lib/carray/methods/nunique.rb +46 -0
  200. data/lib/carray/methods/resize.rb +56 -0
  201. data/lib/carray/methods/snap.rb +161 -0
  202. data/lib/carray/methods/string_format.rb +57 -0
  203. data/lib/carray/methods/unique.rb +47 -0
  204. data/lib/carray/methods/value_counts.rb +71 -0
  205. data/lib/carray/mkmf.rb +124 -101
  206. data/lib/carray/runtime.rb +89 -0
  207. data/lib/carray/serialize.rb +478 -167
  208. data/lib/carray/slab_iterator.rb +305 -0
  209. data/lib/carray/stack.rb +291 -0
  210. data/lib/carray/string.rb +56 -180
  211. data/lib/carray/string_operation_extension.rb +289 -0
  212. data/lib/carray/struct.rb +335 -323
  213. data/lib/carray/struct_builder.rb +697 -0
  214. data/lib/carray/table.rb +41 -2
  215. data/lib/carray/time.rb +2654 -38
  216. data/lib/carray/window_iterator.rb +927 -0
  217. data/lib/carray.rb +55 -57
  218. data/yard-stubs/ca_obj_array.rb +385 -0
  219. data/yard-stubs/ca_obj_bitarray.rb +38 -0
  220. data/yard-stubs/ca_obj_bitfield.rb +43 -0
  221. data/yard-stubs/ca_obj_block.rb +73 -0
  222. data/yard-stubs/ca_obj_byte_swap.rb +56 -0
  223. data/yard-stubs/ca_obj_fake.rb +31 -0
  224. data/yard-stubs/ca_obj_farray.rb +32 -0
  225. data/yard-stubs/ca_obj_field.rb +45 -0
  226. data/yard-stubs/ca_obj_grid.rb +35 -0
  227. data/yard-stubs/ca_obj_refer.rb +72 -0
  228. data/yard-stubs/ca_obj_roll.rb +45 -0
  229. data/yard-stubs/ca_obj_shift.rb +43 -0
  230. data/yard-stubs/ca_obj_stride.rb +181 -0
  231. data/yard-stubs/ca_obj_tile.rb +29 -0
  232. data/yard-stubs/ca_obj_transpose.rb +40 -0
  233. data/yard-stubs/ca_obj_window.rb +49 -0
  234. data/yard-stubs/carray_access.rb +131 -0
  235. data/yard-stubs/carray_attribute.rb +246 -0
  236. data/yard-stubs/carray_broadcast.rb +37 -0
  237. data/yard-stubs/carray_cast.rb +489 -0
  238. data/yard-stubs/carray_class.rb +65 -0
  239. data/yard-stubs/carray_conversion.rb +76 -0
  240. data/yard-stubs/carray_copy.rb +79 -0
  241. data/yard-stubs/carray_core.rb +114 -0
  242. data/yard-stubs/carray_count.rb +79 -0
  243. data/yard-stubs/carray_element.rb +108 -0
  244. data/yard-stubs/carray_generate.rb +66 -0
  245. data/yard-stubs/carray_lazy.rb +23 -0
  246. data/yard-stubs/carray_loop.rb +140 -0
  247. data/yard-stubs/carray_mask.rb +259 -0
  248. data/yard-stubs/carray_math.rb +132 -0
  249. data/yard-stubs/carray_mathfunc.rb +45 -0
  250. data/yard-stubs/carray_median_percentile.rb +89 -0
  251. data/yard-stubs/carray_memory_view.rb +163 -0
  252. data/yard-stubs/carray_order.rb +312 -0
  253. data/yard-stubs/carray_random.rb +89 -0
  254. data/yard-stubs/carray_scatter.rb +106 -0
  255. data/yard-stubs/carray_slab.rb +57 -0
  256. data/yard-stubs/carray_sort.rb +163 -0
  257. data/yard-stubs/carray_test.rb +85 -0
  258. data/yard-stubs/carray_undef.rb +64 -0
  259. data/yard-stubs/carray_utils.rb +97 -0
  260. data/yard-stubs/ruby_carray.rb +193 -0
  261. metadata +220 -138
  262. data/Rakefile +0 -51
  263. data/TODO.md +0 -18
  264. data/ext/ca_iter_block.c +0 -257
  265. data/ext/ca_iter_dimension.c +0 -299
  266. data/ext/ca_iter_window.c +0 -214
  267. data/ext/ca_obj_mapping.c +0 -644
  268. data/ext/ca_obj_unbound_repeat.c +0 -529
  269. data/ext/carray_iterator.c +0 -641
  270. data/ext/carray_math.rb +0 -850
  271. data/ext/carray_numeric.c +0 -259
  272. data/ext/carray_sort_addr.c +0 -254
  273. data/ext/carray_stat.c +0 -2100
  274. data/ext/carray_stat_proc.rb +0 -1999
  275. data/ext/mkmath.rb +0 -741
  276. data/ext/ruby_ccomplex.c +0 -509
  277. data/ext/ruby_float_func.c +0 -86
  278. data/lib/carray/array.rb +0 -8
  279. data/lib/carray/autoload/autoload_base.rb +0 -19
  280. data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
  281. data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
  282. data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
  283. data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
  284. data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
  285. data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
  286. data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
  287. data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
  288. data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
  289. data/lib/carray/autoload/autoload_gem_random.rb +0 -8
  290. data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
  291. data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
  292. data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
  293. data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
  294. data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
  295. data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
  296. data/lib/carray/autoload/autoload_object_link.rb +0 -1
  297. data/lib/carray/autoload/autoload_object_pack.rb +0 -2
  298. data/lib/carray/autoload.rb +0 -141
  299. data/lib/carray/basic.rb +0 -191
  300. data/lib/carray/broadcast.rb +0 -101
  301. data/lib/carray/compose.rb +0 -315
  302. data/lib/carray/convert.rb +0 -115
  303. data/lib/carray/info.rb +0 -110
  304. data/lib/carray/io/imagemagick.rb +0 -235
  305. data/lib/carray/mask.rb +0 -102
  306. data/lib/carray/math/histogram.rb +0 -177
  307. data/lib/carray/math/recurrence.rb +0 -93
  308. data/lib/carray/object/ca_obj_iterator.rb +0 -50
  309. data/lib/carray/object/ca_obj_link.rb +0 -50
  310. data/lib/carray/object/ca_obj_pack.rb +0 -99
  311. data/lib/carray/obsolete.rb +0 -256
  312. data/lib/carray/ordering.rb +0 -181
  313. data/lib/carray/testing.rb +0 -51
  314. data/lib/carray/transform.rb +0 -109
  315. data/mailmap +0 -1
  316. data/misc/Methods.ja.md +0 -182
  317. data/misc/NOTE +0 -51
  318. data/spec/Classes/CABitfield_spec.rb +0 -58
  319. data/spec/Classes/CABlockIterator_spec.rb +0 -114
  320. data/spec/Classes/CABlock_spec.rb +0 -205
  321. data/spec/Classes/CAField_spec.rb +0 -39
  322. data/spec/Classes/CAGrid_spec.rb +0 -75
  323. data/spec/Classes/CAMap_spec.rb +0 -0
  324. data/spec/Classes/CAMapping_spec.rb +0 -105
  325. data/spec/Classes/CAObject_attribute_spec.rb +0 -33
  326. data/spec/Classes/CAObject_spec.rb +0 -33
  327. data/spec/Classes/CARefer_spec.rb +0 -93
  328. data/spec/Classes/CARepeat_spec.rb +0 -65
  329. data/spec/Classes/CASelect_spec.rb +0 -22
  330. data/spec/Classes/CAShift_spec.rb +0 -16
  331. data/spec/Classes/CAStruct_spec.rb +0 -71
  332. data/spec/Classes/CATranspose_spec.rb +0 -60
  333. data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
  334. data/spec/Classes/CAWindow_spec.rb +0 -54
  335. data/spec/Classes/CAWrap_spec.rb +0 -8
  336. data/spec/Classes/CArray_spec.rb +0 -184
  337. data/spec/Classes/CScalar_spec.rb +0 -55
  338. data/spec/Features/feature_130_spec.rb +0 -19
  339. data/spec/Features/feature_attributes_spec.rb +0 -280
  340. data/spec/Features/feature_boolean_spec.rb +0 -98
  341. data/spec/Features/feature_broadcast.rb +0 -116
  342. data/spec/Features/feature_cast_function.rb +0 -19
  343. data/spec/Features/feature_cast_spec.rb +0 -33
  344. data/spec/Features/feature_class_spec.rb +0 -84
  345. data/spec/Features/feature_complex_spec.rb +0 -42
  346. data/spec/Features/feature_composite_spec.rb +0 -124
  347. data/spec/Features/feature_convert_spec.rb +0 -46
  348. data/spec/Features/feature_copy_spec.rb +0 -123
  349. data/spec/Features/feature_creation_spec.rb +0 -84
  350. data/spec/Features/feature_element_spec.rb +0 -144
  351. data/spec/Features/feature_extream_spec.rb +0 -54
  352. data/spec/Features/feature_generate_spec.rb +0 -74
  353. data/spec/Features/feature_index_spec.rb +0 -69
  354. data/spec/Features/feature_mask_spec.rb +0 -580
  355. data/spec/Features/feature_math_spec.rb +0 -97
  356. data/spec/Features/feature_order_spec.rb +0 -146
  357. data/spec/Features/feature_ref_store_spec.rb +0 -209
  358. data/spec/Features/feature_serialization_spec.rb +0 -125
  359. data/spec/Features/feature_stat_spec.rb +0 -397
  360. data/spec/Features/feature_virtual_spec.rb +0 -48
  361. data/spec/Features/method_eq_spec.rb +0 -81
  362. data/spec/Features/method_is_nan_spec.rb +0 -12
  363. data/spec/Features/method_map_spec.rb +0 -54
  364. data/spec/Features/method_max_with.rb +0 -20
  365. data/spec/Features/method_min_with.rb +0 -19
  366. data/spec/Features/method_ne_spec.rb +0 -18
  367. data/spec/Features/method_project_spec.rb +0 -188
  368. data/spec/Features/method_ref_spec.rb +0 -27
  369. data/spec/Features/method_round_spec.rb +0 -11
  370. data/spec/Features/method_s_linspace_spec.rb +0 -48
  371. data/spec/Features/method_s_span_spec.rb +0 -14
  372. data/spec/Features/method_seq_spec.rb +0 -47
  373. data/spec/Features/method_sort_with.rb +0 -43
  374. data/spec/Features/method_sorted_with.rb +0 -29
  375. data/spec/Features/method_span_spec.rb +0 -42
  376. data/spec/Features/method_wrap_readonly_spec.rb +0 -43
  377. data/spec/UnitTest/test_CAVirtual.rb +0 -214
  378. data/spec/spec_all.rb +0 -10
  379. data/utils/ca_ase.rb +0 -21
  380. data/utils/ca_methods.rb +0 -15
  381. data/utils/cast_checker.rb +0 -30
  382. data/utils/convert_test.rb +0 -73
  383. data/utils/extract_yard.rb +0 -22
  384. data/utils/guess_shape.rb +0 -76
  385. data/utils/monkey_patch_methods.rb +0 -62
  386. data/utils/remove_resource_fork.sh +0 -5
data/ext/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;
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]);
141
205
  }
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;
150
- }
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,383 @@ 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);
217
282
  }
218
283
 
219
- static char *
220
- ca_grid_func_ptr_at_addr (void *ap, ca_size_t addr)
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];
293
+ }
294
+
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]);
227
309
  }
228
- else {
229
- return ca->ptr + ca->bytes * addr;
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
+ }
230
353
  }
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);
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
+ }
247
393
 
248
- if ( ca->parent->ptr == NULL ) {
249
- return ca_ptr_at_addr(ca->parent, n);
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
+ }
400
+
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
+ /* The request is over the view's addresses, so a transposed / flat request
482
+ is legal and must not be composed axis-by-axis; see
483
+ ca_xfer_stride_request_is_axis_box (carray.h). */
484
+ if ( ! ca_xfer_stride_request_is_axis_box(g, starts, counts, strides) ) {
485
+ aligned = 0;
486
+ }
487
+ else {
488
+ for (k = 0; k < ndim; k++) {
489
+ if (strides[k] % gnative[k] != 0) { aligned = 0; break; }
490
+ src_step[k] = strides[k] / gnative[k];
491
+ }
269
492
  }
270
- ca_fetch_index(ca->parent, idx0, ptr);
271
- }
272
493
 
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]);
494
+ if (!aligned) {
495
+ ca_size_t idx[CA_RANK_MAX], doff = 0, base = 0;
496
+ for (k = 0; k < ndim; k++) base += starts[k] * gnative[k];
497
+ for (k = 0; k < ndim; k++) idx[k] = 0;
498
+ while (1) {
499
+ ca_size_t goff = base, gidx[CA_RANK_MAX];
500
+ for (k = 0; k < ndim; k++) goff += idx[k] * strides[k];
501
+ ca_addr2index((CArray *) g, goff / g->bytes, gidx);
502
+ ca_grid_func_xfer_index(g, gidx, d + doff, dir);
503
+ doff += g->bytes;
504
+ k = ndim - 1;
505
+ while (k >= 0) { if (++idx[k] < counts[k]) break; idx[k] = 0; k--; }
506
+ if (k < 0) break;
507
+ }
508
+ return;
509
+ }
510
+
511
+ /* Fast path: when parent is attached (ptr != NULL), translate the
512
+ sub-region request (starts/counts/src_step) into a transient
513
+ ca_axis_desc_t[] sub-descriptor and call
514
+ ca_axis_dispatch_gather/_scatter directly. This is the same engine
515
+ entry that ca_grid_func_xfer_all uses (minus the ca_attach(parent)
516
+ call, which per-region functions must not do). Collapses N
517
+ outer-row dispatch calls into 1.
518
+
519
+ INDEX axis with src_step==1: zero-copy pointer offset into
520
+ g->axes[k].indices. INDEX axis with src_step!=1: materialise a
521
+ sub-indices array via ALLOCV (per-call, not per-row). STRIDE axis:
522
+ trivially fold starts/src_step into start/step.
523
+
524
+ Parent unattached falls through to the legacy per-row outer loop. */
525
+ if (parent->ptr) {
526
+ ca_axis_desc_t sub_axes[CA_RANK_MAX];
527
+ ca_size_t parent_axis_dims[CA_RANK_MAX];
528
+ ca_size_t *index_bufs[CA_RANK_MAX];
529
+ volatile VALUE holders[CA_RANK_MAX];
530
+ ca_size_t total_elements = 1;
531
+ ca_size_t j;
532
+ int8_t have_alloc = 0;
533
+
534
+ for (k = 0; k < ndim; k++) { index_bufs[k] = NULL; holders[k] = Qnil; }
535
+
536
+ for (k = 0; k < ndim; k++) {
537
+ parent_axis_dims[k] = parent->dim[k];
538
+ total_elements *= counts[k];
539
+ if (g->axes[k].kind == CAG_AXIS_STRIDE) {
540
+ sub_axes[k].kind = CA_AXIS_KIND_STRIDE;
541
+ sub_axes[k].count = counts[k];
542
+ sub_axes[k].start = g->axes[k].start + starts[k] * g->axes[k].step;
543
+ sub_axes[k].step = g->axes[k].step * src_step[k];
544
+ sub_axes[k].indices = NULL;
545
+ } else {
546
+ sub_axes[k].kind = CA_AXIS_KIND_INDEX;
547
+ sub_axes[k].count = counts[k];
548
+ sub_axes[k].start = 0;
549
+ sub_axes[k].step = 0;
550
+ if (src_step[k] == 1) {
551
+ /* zero-copy: pointer offset into the original indices */
552
+ sub_axes[k].indices = g->axes[k].indices + starts[k];
553
+ } else {
554
+ /* sub-sampled or reversed: materialise sub-indices */
555
+ index_bufs[k] = ALLOCV_N(ca_size_t, holders[k], counts[k]);
556
+ for (j = 0; j < counts[k]; j++) {
557
+ index_bufs[k][j] =
558
+ g->axes[k].indices[starts[k] + j * src_step[k]];
559
+ }
560
+ sub_axes[k].indices = index_bufs[k];
561
+ have_alloc = 1;
562
+ }
563
+ }
564
+ }
565
+
566
+ if (dir == CA_XFER_GET) {
567
+ ca_axis_dispatch_gather(parent, parent_axis_dims, sub_axes, ndim,
568
+ g->bytes, total_elements, NULL, d);
569
+ } else {
570
+ ca_axis_dispatch_scatter(parent, parent_axis_dims, sub_axes, ndim,
571
+ g->bytes, total_elements, d);
572
+ }
573
+
574
+ if (have_alloc) {
575
+ for (k = 0; k < ndim; k++) {
576
+ if (index_bufs[k] != NULL) ALLOCV_END(holders[k]);
577
+ }
578
+ }
579
+ return;
580
+ }
581
+
582
+ /* Fallback (parent unattached): per-row outer loop + per-row
583
+ ca_xfer_stride/ca_xfer_addrs. */
584
+ for (k = 0; k < ndim; k++) o[k] = 0;
585
+
586
+ while (1) {
587
+ ca_size_t pbase = 0, doff = 0;
588
+ for (k = 0; k < inner; k++) {
589
+ ca_size_t gpos = starts[k] + o[k] * src_step[k];
590
+ ca_size_t ppos = (g->axes[k].kind == CAG_AXIS_STRIDE)
591
+ ? (g->axes[k].start + gpos * g->axes[k].step)
592
+ : g->axes[k].indices[gpos];
593
+ pbase += ppos * pnative[k];
594
+ doff += o[k] * dstride[k];
595
+ }
596
+
597
+ if (g->axes[inner].kind == CAG_AXIS_STRIDE) {
598
+ ca_size_t pstarts[CA_RANK_MAX], pcounts[CA_RANK_MAX], pstrides[CA_RANK_MAX];
599
+ ca_size_t inner_pbase =
600
+ (g->axes[inner].start + starts[inner] * g->axes[inner].step) * pnative[inner];
601
+ ca_addr2index((CArray *) parent, (pbase + inner_pbase) / parent->bytes, pstarts);
602
+ for (k = 0; k < ndim; k++) { pcounts[k] = 1; pstrides[k] = 0; }
603
+ pcounts[inner] = counts[inner];
604
+ pstrides[inner] = src_step[inner] * g->axes[inner].step * pnative[inner];
605
+ ca_xfer_stride(parent, pstarts, pcounts, pstrides, d + doff, dir);
606
+ }
607
+ else {
608
+ ca_size_t *addrs;
609
+ ca_size_t j, n = counts[inner];
610
+ volatile VALUE holder;
611
+ addrs = ALLOCV_N(ca_size_t, holder, n);
612
+ for (j = 0; j < n; j++) {
613
+ ca_size_t gpos = starts[inner] + j * src_step[inner];
614
+ ca_size_t ppos = g->axes[inner].indices[gpos];
615
+ addrs[j] = (pbase + ppos * pnative[inner]) / parent->bytes;
616
+ }
617
+ ca_xfer_addrs(parent, n, addrs, d + doff, dir);
618
+ ALLOCV_END(holder);
619
+ }
620
+
621
+ k = inner - 1;
622
+ while (k >= 0) { if (++o[k] < counts[k]) break; o[k] = 0; k--; }
623
+ if (k < 0) break;
282
624
  }
283
- ca_store_index(ca->parent, idx0, ptr);
284
625
  }
285
626
 
286
627
  static void
@@ -288,27 +629,34 @@ ca_grid_func_allocate (void *ap)
288
629
  {
289
630
  CAGrid *ca = (CAGrid *) ap;
290
631
  ca_attach(ca->parent);
291
- /* ca->ptr = ALLOC_N(char, ca_length(ca)); */
292
- ca->ptr = malloc_with_check(ca_length(ca));
632
+ ca->ptr = xmalloc(ca_length(ca));
293
633
  }
294
634
 
635
+ /* attach / sync / copy_data / sync_data / fill_data all go through the
636
+ descriptor engine (ca_axis_dispatch.c). This file supplies only
637
+ describe_axes; the engine does the rest. */
638
+
295
639
  static void
296
640
  ca_grid_func_attach (void *ap)
297
641
  {
298
- void ca_grid_attach (CAGrid *cb);
299
-
300
642
  CAGrid *ca = (CAGrid *) ap;
643
+ ca_axis_desc_t desc[CA_RANK_MAX];
644
+ ca_size_t pdims[CA_RANK_MAX];
301
645
  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);
646
+ ca_grid_describe_axes(ca, desc, pdims);
647
+ ca->ptr = ca_axis_dispatch_attach(ca->parent, pdims, desc, ca->ndim,
648
+ ca->bytes, ca->elements, NULL);
305
649
  }
306
650
 
307
651
  static void
308
652
  ca_grid_func_sync (void *ap)
309
653
  {
310
654
  CAGrid *ca = (CAGrid *) ap;
311
- ca_grid_sync(ca);
655
+ ca_axis_desc_t desc[CA_RANK_MAX];
656
+ ca_size_t pdims[CA_RANK_MAX];
657
+ ca_grid_describe_axes(ca, desc, pdims);
658
+ ca_axis_dispatch_scatter(ca->parent, pdims, desc, ca->ndim, ca->bytes,
659
+ ca->elements, ca->ptr);
312
660
  ca_sync(ca->parent);
313
661
  }
314
662
 
@@ -316,42 +664,79 @@ static void
316
664
  ca_grid_func_detach (void *ap)
317
665
  {
318
666
  CAGrid *ca = (CAGrid *) ap;
319
- free(ca->ptr);
667
+ xfree(ca->ptr);
320
668
  ca->ptr = NULL;
321
669
  ca_detach(ca->parent);
322
670
  }
323
671
 
672
+ /* xfer_all: fast path on parent->ptr, with no silent transitive attach
673
+ of the parent. A cold parent uses a proper 2-pass -- materialise the
674
+ parent into scratch via ca_xfer_all, temporarily expose it as
675
+ parent->ptr, run the fast path, then (for PUT) write scratch back. */
324
676
  static void
325
- ca_grid_func_copy_data (void *ap, void *ptr)
677
+ ca_grid_func_run_fast_path (CAGrid *ca, char *data, int dir)
326
678
  {
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);
679
+ ca_axis_desc_t desc[CA_RANK_MAX];
680
+ ca_size_t pdims[CA_RANK_MAX];
681
+ ca_grid_describe_axes(ca, desc, pdims);
682
+ if ( dir == CA_XFER_GET ) {
683
+ ca_axis_dispatch_gather(ca->parent, pdims, desc, ca->ndim, ca->bytes,
684
+ ca->elements, NULL, data);
685
+ } else {
686
+ ca_axis_dispatch_scatter(ca->parent, pdims, desc, ca->ndim, ca->bytes,
687
+ ca->elements, data);
688
+ }
334
689
  }
335
690
 
336
691
  static void
337
- ca_grid_func_sync_data (void *ap, void *ptr)
692
+ ca_grid_func_xfer_all (void *ap, void *data, int dir)
338
693
  {
339
694
  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);
695
+ if ( ca->parent->ptr ) {
696
+ ca_grid_func_run_fast_path(ca, (char *) data, dir);
697
+ return;
698
+ }
699
+ /* Proper 2-pass cold fallback. */
700
+ {
701
+ volatile VALUE holder;
702
+ CArray *parent = ca->parent;
703
+ ca_size_t plen = parent->elements * parent->bytes;
704
+ char *parent_scratch = ALLOCV_N(char, holder, plen);
705
+ char *parent_ptr_saved = parent->ptr;
706
+ ca_xfer_all(parent, parent_scratch, CA_XFER_GET);
707
+ parent->ptr = parent_scratch;
708
+ ca_grid_func_run_fast_path(ca, (char *) data, dir);
709
+ if ( dir == CA_XFER_PUT ) {
710
+ ca_xfer_all(parent, parent_scratch, CA_XFER_PUT);
711
+ }
712
+ parent->ptr = parent_ptr_saved;
713
+ ALLOCV_END(holder);
714
+ }
347
715
  }
348
716
 
349
717
  static void
350
718
  ca_grid_func_fill_data (void *ap, void *ptr)
351
719
  {
352
720
  CAGrid *ca = (CAGrid *) ap;
721
+ ca_axis_desc_t desc[CA_RANK_MAX];
722
+ ca_size_t pdims[CA_RANK_MAX];
723
+
724
+ ca_grid_describe_axes(ca, desc, pdims);
725
+
726
+ /* Writing the selected cells used to go through a whole-parent attach and
727
+ sync. Where that attach is a gather rather than an alias, the cells this
728
+ view did not select make the round trip for nothing -- and through a
729
+ lossy layer they do not come back the same. Hand each slab to the parent
730
+ as a region instead. (PROPOSAL_PARTIAL_FILL_WHOLE_ROOT_WRITEBACK.md) */
731
+ if ( !ca_is_attached(ca->parent) && !ca_attach_is_alias(ca->parent) ) {
732
+ ca_axis_dispatch_fill_value_via_parent(ca->parent, pdims, desc, ca->ndim,
733
+ ca->bytes, ca->elements, ptr);
734
+ return;
735
+ }
736
+
353
737
  ca_attach(ca->parent);
354
- ca_grid_fill(ca, ptr);
738
+ ca_axis_dispatch_fill_value(ca->parent, pdims, desc, ca->ndim, ca->bytes,
739
+ ca->elements, ptr);
355
740
  ca_sync(ca->parent);
356
741
  ca_detach(ca->parent);
357
742
  }
@@ -367,271 +752,77 @@ ca_grid_func_create_mask (void *ap)
367
752
  }
368
753
 
369
754
  ca->mask = (CArray *) ca_grid_new_share(ca->parent->mask,
370
- ca->dim, ca->grid, ca->contig);
755
+ ca->ndim, ca->axes);
371
756
  }
372
757
 
373
758
  ca_operation_function_t ca_grid_func = {
374
759
  -1, /* CA_OBJ_GRID */
375
- CA_VIRTUAL_ARRAY,
760
+ CA_VIEW_ARRAY,
376
761
  free_ca_grid,
377
762
  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
763
  ca_grid_func_allocate,
385
764
  ca_grid_func_attach,
386
765
  ca_grid_func_sync,
387
766
  ca_grid_func_detach,
388
- ca_grid_func_copy_data,
389
- ca_grid_func_sync_data,
390
767
  ca_grid_func_fill_data,
391
768
  ca_grid_func_create_mask,
769
+ ca_grid_func_xfer_index,
770
+ ca_grid_func_xfer_addrs,
771
+ ca_grid_func_fold_stride,
772
+ ca_grid_func_xfer_stride,
773
+ ca_grid_func_xfer_all,
392
774
  };
393
775
 
394
776
  /* ------------------------------------------------------------------- */
395
777
 
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
778
  VALUE
615
- rb_ca_grid_new (VALUE cary, ca_size_t *dim, CArray **grid)
779
+ rb_ca_grid_new (VALUE cary, int8_t ndim, cag_axis_t *protos)
616
780
  {
617
781
  volatile VALUE obj;
618
782
  CArray *parent;
619
783
  CAGrid *ca;
620
784
  rb_check_carray_object(cary);
621
785
  TypedData_Get_Struct(cary, CArray, &carray_data_type, parent);
622
- ca = ca_grid_new(parent, dim, grid);
786
+ ca = ca_grid_new(parent, ndim, protos);
623
787
  obj = ca_wrap_struct(ca);
624
788
  rb_ca_set_parent(obj, cary);
625
- rb_ca_data_type_inherit(obj, cary);
626
789
  return obj;
627
790
  }
628
791
 
629
- /* yard:
630
- class CArray
631
- def grid
632
- end
633
- end
634
- */
792
+ /* Detect a plain Ruby Range (Integer first/last, exclude_end?, step=1
793
+ implicit) and convert it to STRIDE (start, count, step=1).
794
+ Returns 1 on success (fills *out_start, *out_count), 0 on fallback
795
+ (caller should materialise to INDEX). Negative endpoints are
796
+ normalised against dim_size like everywhere else in CArray. */
797
+ static int
798
+ cag_range_to_stride (VALUE range, ca_size_t dim_size,
799
+ ca_size_t *out_start, ca_size_t *out_count)
800
+ {
801
+ VALUE rb_first, rb_last;
802
+ ca_size_t first, last, count;
803
+ int exclude_end;
804
+
805
+ if ( ! rb_obj_is_kind_of(range, rb_cRange) ) return 0;
806
+ rb_first = rb_funcall(range, rb_intern("first"), 0);
807
+ rb_last = rb_funcall(range, rb_intern("last"), 0);
808
+ if ( ! FIXNUM_P(rb_first) || ! FIXNUM_P(rb_last) ) return 0;
809
+ exclude_end = RTEST(rb_funcall(range, rb_intern("exclude_end?"), 0));
810
+
811
+ first = NUM2SIZE(rb_first);
812
+ last = NUM2SIZE(rb_last);
813
+ if (first < 0) first += dim_size;
814
+ if (last < 0) last += dim_size;
815
+ if (exclude_end) last -= 1;
816
+
817
+ if (first < 0 || first >= dim_size) return 0;
818
+ if (last < first - 1) return 0; /* empty range */
819
+ if (last >= dim_size) return 0;
820
+ count = last - first + 1;
821
+
822
+ *out_start = first;
823
+ *out_count = count;
824
+ return 1;
825
+ }
635
826
 
636
827
  VALUE
637
828
  rb_ca_grid (int argc, VALUE *argv, VALUE self)
@@ -639,12 +830,23 @@ rb_ca_grid (int argc, VALUE *argv, VALUE self)
639
830
  volatile VALUE obj, ridx, rval;
640
831
  volatile VALUE list = rb_ary_new();
641
832
  CArray *ca;
642
- CArray *ci[CA_RANK_MAX];
643
- ca_size_t dim[CA_RANK_MAX];
644
- CArray *grid[CA_RANK_MAX];
833
+ CArray *ci[CA_RANK_MAX]; /* CAWrap holders for INDEX axes; NULL for STRIDE */
834
+ cag_axis_t protos[CA_RANK_MAX];
645
835
  ca_size_t i;
646
836
 
647
- TypedData_Get_Struct(self, CArray, &carray_data_type,ca);
837
+ /* Dispatch hook: route mixed AP + single boolean to CASelectAxis
838
+ (slab-copy optimised path). Falls through to per-cell CAGrid gather
839
+ when not eligible (integer index, multiple INDIRECT axes, indirect
840
+ not on axis 0). Inline argv[0] type check avoids the function call
841
+ when axis 0 is nil/Integer (most patterns with INDIRECT on inner axes). */
842
+ extern int ca_csa_dispatch_bypass;
843
+ if ( ! ca_csa_dispatch_bypass && argc > 0 && TYPE(argv[0]) == T_DATA ) {
844
+ if ( rb_ca_select_axis_eligible_p(argc, argv, self) ) {
845
+ return rb_ca_select_axis(argc, argv, self);
846
+ }
847
+ }
848
+
849
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
648
850
 
649
851
  ridx = rb_ary_new4(argc, argv);
650
852
 
@@ -679,43 +881,82 @@ rb_ca_grid (int argc, VALUE *argv, VALUE self)
679
881
  return rb_ca_grid(argc, argv, ref);
680
882
  }
681
883
 
682
- for (i=0; i<RARRAY_LEN(ridx); i++) {
884
+ /* Build per-axis protos:
885
+ nil -> STRIDE(0, parent.dim, 1), no CArray
886
+ Range -> STRIDE if plain integer Range, else fall through to INDEX
887
+ CArray -> INDEX with indices = (ca_size_t*)ca_wrap_readonly(...)->ptr */
888
+ for (i = 0; i < ca->ndim; i++) {
889
+ ca_size_t dim_size = ca->dim[i];
683
890
  rval = rb_ary_entry(ridx, i);
891
+ ci[i] = NULL;
684
892
  if ( NIL_P(rval) ) {
685
- ci[i] = NULL;
686
- dim[i] = ca->dim[i];
687
- grid[i] = NULL;
893
+ protos[i].kind = CAG_AXIS_STRIDE;
894
+ protos[i].count = dim_size;
895
+ protos[i].start = 0;
896
+ protos[i].step = 1;
897
+ protos[i].indices = NULL;
898
+ continue;
688
899
  }
689
- else {
690
- if ( rb_obj_is_carray(rval) ) {
691
- if ( rb_ca_is_boolean_type(rval) ) {
692
- rval = rb_ca_where(rval);
693
- }
900
+ if ( rb_obj_is_kind_of(rval, rb_cRange) ) {
901
+ ca_size_t start, count;
902
+ if ( cag_range_to_stride(rval, dim_size, &start, &count) ) {
903
+ protos[i].kind = CAG_AXIS_STRIDE;
904
+ protos[i].count = count;
905
+ protos[i].start = start;
906
+ protos[i].step = 1;
907
+ protos[i].indices = NULL;
908
+ continue;
694
909
  }
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");
910
+ /* fallback: materialise to integer array */
911
+ rval = rb_funcall(rb_mKernel, rb_intern("CA_SIZE"), 1, rval);
912
+ }
913
+ else if ( rb_obj_is_carray(rval) ) {
914
+ if ( rb_ca_is_boolean_type(rval) ) {
915
+ rval = rb_ca_where(rval);
700
916
  }
701
- ci[i] = ca_wrap_readonly(rval, CA_SIZE);
702
- rb_ary_push(list, rval);
703
- ca_attach(ci[i]);
917
+ }
918
+ else if ( TYPE(rval) == T_ARRAY ) {
919
+ rb_raise(rb_eRuntimeError, "not implemented for this index");
920
+ }
704
921
 
705
- if ( ca_is_any_masked(ci[i]) ) {
706
- dim[i] = ci[i]->elements - ca_count_masked(ci[i]);
922
+ /* INDEX axis */
923
+ ci[i] = ca_wrap_readonly(rval, CA_SIZE);
924
+ rb_ary_push(list, rval);
925
+ ca_attach(ci[i]);
926
+
927
+ protos[i].kind = CAG_AXIS_INDEX;
928
+ if ( ca_is_any_masked(ci[i]) ) {
929
+ /* mask-filtered snapshot: skip masked cells. We point protos[i]
930
+ .indices at a temp buffer alloc'd here and freed after setup
931
+ copies it (setup snapshots into its own ALLOC). Caller-side
932
+ lifetime: the temp lives until ca_grid_new returns. */
933
+ ca_size_t gsize = ci[i]->elements - ca_count_masked(ci[i]);
934
+ ca_size_t *src = (ca_size_t *) ci[i]->ptr;
935
+ boolean8_t *m = (boolean8_t *) ci[i]->mask->ptr;
936
+ ca_size_t *tmp = ALLOC_N(ca_size_t, gsize > 0 ? gsize : 1);
937
+ ca_size_t j, n = 0;
938
+ for (j = 0; j < ci[i]->elements; j++) {
939
+ if ( ! m[j] ) tmp[n++] = src[j];
707
940
  }
708
- else {
709
- dim[i] = ci[i]->elements;
710
- }
711
- grid[i] = ci[i];
941
+ protos[i].count = gsize;
942
+ protos[i].indices = tmp;
943
+ } else {
944
+ protos[i].count = ci[i]->elements;
945
+ protos[i].indices = (ca_size_t *) ci[i]->ptr;
712
946
  }
947
+ protos[i].start = 0;
948
+ protos[i].step = 0;
713
949
  }
714
950
 
715
- obj = rb_ca_grid_new(self, dim, grid);
951
+ obj = rb_ca_grid_new(self, ca->ndim, protos);
716
952
 
717
- for (i=0; i<RARRAY_LEN(ridx); i++) {
953
+ /* Cleanup: detach CAWraps + free temp masked-snapshot buffers (setup
954
+ has already copied indices into its own ALLOC). */
955
+ for (i = 0; i < ca->ndim; i++) {
718
956
  if ( ci[i] ) {
957
+ if ( ca_is_any_masked(ci[i]) ) {
958
+ xfree(protos[i].indices);
959
+ }
719
960
  ca_detach(ci[i]);
720
961
  }
721
962
  }
@@ -738,24 +979,197 @@ rb_ca_grid_initialize_copy (VALUE self, VALUE other)
738
979
  TypedData_Get_Struct(self, CAGrid, &cagrid_data_type, ca);
739
980
  TypedData_Get_Struct(other, CAGrid, &cagrid_data_type, cs);
740
981
 
741
- /* share grid info */
742
- ca_grid_setup(ca, cs->parent, cs->dim, cs->grid, cs->contig, 1);
982
+ if ( ca_func[CA_OBJ_GRID].pool_init ) {
983
+ ca_array_pool_alloc(ca, CA_OBJ_GRID, cs->ndim);
984
+ }
985
+ /* Share the source's axes buffer (CA_FLAG_SHARE_INDEX semantics:
986
+ source CAGrid owns the lifetime; clone aliases it). */
987
+ ca_grid_setup(ca, cs->parent, cs->ndim, cs->axes, 1);
743
988
 
744
989
  return self;
745
990
  }
746
991
 
747
992
 
748
993
 
994
+ /* ------------------------------------------------------------------- */
995
+ /* Producer interface: emit the per-axis descriptor array -- a 1:1
996
+ mechanical mapping from CAGrid's internal tagged storage to the
997
+ framework descriptor.
998
+
999
+ STRIDE axes (= nil-arg or arithmetic-progression Range) carry their
1000
+ actual start/step, so axis-merge can fire on these axes when they
1001
+ neighbour other STRIDE axes. INDEX axes borrow the owned
1002
+ ca->axes[k].indices snapshot. */
1003
+
1004
+ void
1005
+ ca_grid_describe_axes (void *ap, ca_axis_desc_t *out,
1006
+ ca_size_t *out_parent_dims)
1007
+ {
1008
+ CAGrid *ca = (CAGrid *) ap;
1009
+ int8_t k;
1010
+ for ( k = 0; k < ca->ndim; k++ ) {
1011
+ out_parent_dims[k] = ca->parent->dim[k];
1012
+ if ( ca->axes[k].kind == CAG_AXIS_STRIDE ) {
1013
+ out[k].kind = CA_AXIS_KIND_STRIDE;
1014
+ out[k].count = ca->axes[k].count;
1015
+ out[k].start = ca->axes[k].start;
1016
+ out[k].step = ca->axes[k].step;
1017
+ out[k].indices = NULL;
1018
+ } else {
1019
+ out[k].kind = CA_AXIS_KIND_INDEX;
1020
+ out[k].count = ca->axes[k].count;
1021
+ out[k].start = 0;
1022
+ out[k].step = 0;
1023
+ out[k].indices = ca->axes[k].indices;
1024
+ }
1025
+ }
1026
+ }
1027
+
1028
+ #ifdef CARRAY_DEV_BUILD
1029
+ /* ============================================================
1030
+ * Debug accessors (dev-only, stripped in release)
1031
+ *
1032
+ * Gated by CARRAY_DEV_BUILD (enabled via `extconf.rb --enable-dev-build`
1033
+ * or `CARRAY_DEV=1 rake build_ext`). They expose the per-axis descriptor
1034
+ * and the engine's raw output to Ruby so spec_ai can pin them; nothing in
1035
+ * lib/ or the rest of ext/ consumes them. CASelectAxis carries the same
1036
+ * four under the same fence.
1037
+ * ============================================================ */
1038
+
1039
+ /* Returns the per-axis descriptor as an Array, one entry per axis:
1040
+ [:stride, count, start, step] or [:index, count, indices]. */
1041
+ static VALUE
1042
+ rb_ca_grid_describe_axes (VALUE self)
1043
+ {
1044
+ CAGrid *ca;
1045
+ ca_axis_desc_t desc[CA_RANK_MAX];
1046
+ VALUE arr, entry, idx_ary;
1047
+ int8_t k;
1048
+ ca_size_t i;
1049
+
1050
+ ca_size_t pdims[CA_RANK_MAX];
1051
+ TypedData_Get_Struct(self, CAGrid, &cagrid_data_type, ca);
1052
+ ca_grid_describe_axes(ca, desc, pdims);
1053
+
1054
+ arr = rb_ary_new_capa(ca->ndim);
1055
+ for ( k = 0; k < ca->ndim; k++ ) {
1056
+ if ( desc[k].kind == CA_AXIS_KIND_STRIDE ) {
1057
+ entry = rb_ary_new_capa(4);
1058
+ rb_ary_push(entry, ID2SYM(rb_intern("stride")));
1059
+ rb_ary_push(entry, SIZE2NUM(desc[k].count));
1060
+ rb_ary_push(entry, SIZE2NUM(desc[k].start));
1061
+ rb_ary_push(entry, SIZE2NUM(desc[k].step));
1062
+ } else {
1063
+ entry = rb_ary_new_capa(3);
1064
+ rb_ary_push(entry, ID2SYM(rb_intern("index")));
1065
+ rb_ary_push(entry, SIZE2NUM(desc[k].count));
1066
+ idx_ary = rb_ary_new_capa(desc[k].count);
1067
+ for ( i = 0; i < desc[k].count; i++ ) {
1068
+ rb_ary_push(idx_ary, SIZE2NUM(desc[k].indices[i]));
1069
+ }
1070
+ rb_ary_push(entry, idx_ary);
1071
+ }
1072
+ rb_ary_push(arr, entry);
1073
+ }
1074
+ return arr;
1075
+ }
1076
+
1077
+ /* Returns the attach engine's gathered buffer as a String. */
1078
+ static VALUE
1079
+ rb_ca_grid_dispatch_attach_debug (VALUE self)
1080
+ {
1081
+ CAGrid *ca;
1082
+ ca_axis_desc_t desc[CA_RANK_MAX];
1083
+ char *buf;
1084
+ VALUE str;
1085
+
1086
+ ca_size_t pdims[CA_RANK_MAX];
1087
+ TypedData_Get_Struct(self, CAGrid, &cagrid_data_type, ca);
1088
+ ca_grid_describe_axes(ca, desc, pdims);
1089
+
1090
+ ca_attach(ca->parent);
1091
+ buf = ca_axis_dispatch_attach(ca->parent, pdims, desc, ca->ndim, ca->bytes,
1092
+ ca->elements, NULL);
1093
+ ca_detach(ca->parent);
1094
+
1095
+ str = rb_str_new(buf, ca->elements * ca->bytes);
1096
+ xfree(buf);
1097
+ return str;
1098
+ }
1099
+
1100
+ /* Drives the scatter engine with a view-shaped input String. */
1101
+ static VALUE
1102
+ rb_ca_grid_dispatch_scatter_debug (VALUE self, VALUE in_str)
1103
+ {
1104
+ CAGrid *ca;
1105
+ ca_axis_desc_t desc[CA_RANK_MAX];
1106
+ ca_size_t expected;
1107
+
1108
+ Check_Type(in_str, T_STRING);
1109
+ TypedData_Get_Struct(self, CAGrid, &cagrid_data_type, ca);
1110
+
1111
+ expected = ca->elements * ca->bytes;
1112
+ if ( (ca_size_t) RSTRING_LEN(in_str) != expected ) {
1113
+ rb_raise(rb_eArgError,
1114
+ "CAGrid#_dispatch_scatter_debug: input string length %lld "
1115
+ "!= expected %lld",
1116
+ (long long) RSTRING_LEN(in_str), (long long) expected);
1117
+ }
1118
+ ca_size_t pdims[CA_RANK_MAX];
1119
+ ca_grid_describe_axes(ca, desc, pdims);
1120
+
1121
+ ca_attach(ca->parent);
1122
+ ca_axis_dispatch_scatter(ca->parent, pdims, desc, ca->ndim, ca->bytes,
1123
+ ca->elements, RSTRING_PTR(in_str));
1124
+ ca_sync(ca->parent);
1125
+ ca_detach(ca->parent);
1126
+ return Qnil;
1127
+ }
1128
+
1129
+ /* Drives the fill_value engine with a one-element String. */
1130
+ static VALUE
1131
+ rb_ca_grid_dispatch_fill_value_debug (VALUE self, VALUE val_str)
1132
+ {
1133
+ CAGrid *ca;
1134
+ ca_axis_desc_t desc[CA_RANK_MAX];
1135
+
1136
+ Check_Type(val_str, T_STRING);
1137
+ TypedData_Get_Struct(self, CAGrid, &cagrid_data_type, ca);
1138
+
1139
+ if ( (ca_size_t) RSTRING_LEN(val_str) != ca->bytes ) {
1140
+ rb_raise(rb_eArgError,
1141
+ "CAGrid#_dispatch_fill_value_debug: value string length %lld "
1142
+ "!= bytes %lld",
1143
+ (long long) RSTRING_LEN(val_str), (long long) ca->bytes);
1144
+ }
1145
+ ca_size_t pdims[CA_RANK_MAX];
1146
+ ca_grid_describe_axes(ca, desc, pdims);
1147
+
1148
+ ca_attach(ca->parent);
1149
+ ca_axis_dispatch_fill_value(ca->parent, pdims, desc, ca->ndim, ca->bytes,
1150
+ ca->elements, RSTRING_PTR(val_str));
1151
+ ca_sync(ca->parent);
1152
+ ca_detach(ca->parent);
1153
+ return Qnil;
1154
+ }
1155
+ #endif /* CARRAY_DEV_BUILD */
1156
+
1157
+ /* ------------------------------------------------------------------- */
1158
+
749
1159
  void
750
- Init_ca_obj_grid ()
1160
+ Init_ca_obj_grid (void)
751
1161
  {
752
- rb_cCAGrid = rb_define_class("CAGrid", rb_cCAVirtual);
1162
+ rb_cCAGrid = rb_define_class("CAGrid", rb_cCAView);
753
1163
  rb_cCAGridMask = rb_define_class("CAGridMask", rb_cCAGrid);
754
1164
 
755
- CA_OBJ_GRID = ca_install_obj_type(rb_cCAGrid,
1165
+ ca_grid_func.struct_size = sizeof(CAGrid);
1166
+ ca_grid_func.pool_bytes = ca_grid_pool_bytes;
1167
+ ca_grid_func.pool_init = ca_grid_pool_init;
1168
+
1169
+ CA_OBJ_GRID = ca_install_obj_type(rb_cCAGrid,
756
1170
  &cagrid_data_type,
757
1171
  rb_cCAGridMask,
758
- &cagrid_mask_data_type, ca_grid_func);
1172
+ &cagrid_mask_data_type, &ca_grid_func, sizeof(ca_grid_func));
759
1173
  rb_define_const(rb_cObject, "CA_OBJ_GRID", INT2NUM(CA_OBJ_GRID));
760
1174
 
761
1175
  rb_define_method(rb_cCArray, "grid", rb_ca_grid, -1);
@@ -763,5 +1177,17 @@ Init_ca_obj_grid ()
763
1177
  rb_define_alloc_func(rb_cCAGrid, rb_ca_grid_s_allocate);
764
1178
  rb_define_method(rb_cCAGrid, "initialize_copy",
765
1179
  rb_ca_grid_initialize_copy, 1);
1180
+
1181
+ #ifdef CARRAY_DEV_BUILD
1182
+ /* debug accessors (dev-only, stripped in release) */
1183
+ rb_define_method(rb_cCAGrid, "_describe_axes",
1184
+ rb_ca_grid_describe_axes, 0);
1185
+ rb_define_method(rb_cCAGrid, "_dispatch_attach_debug",
1186
+ rb_ca_grid_dispatch_attach_debug, 0);
1187
+ rb_define_method(rb_cCAGrid, "_dispatch_scatter_debug",
1188
+ rb_ca_grid_dispatch_scatter_debug, 1);
1189
+ rb_define_method(rb_cCAGrid, "_dispatch_fill_value_debug",
1190
+ rb_ca_grid_dispatch_fill_value_debug, 1);
1191
+ #endif
766
1192
  }
767
1193