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_object.c CHANGED
@@ -1,10 +1,9 @@
1
1
  /* ---------------------------------------------------------------------------
2
2
 
3
- ca_obj_object.c
4
-
5
- This file is part of Ruby/CArray extension library.
6
-
7
- Copyright (C) 2005-2020 Hiroki Motoyoshi
3
+ CAObject: the per-element Ruby-callback bridge view. Each cell read /
4
+ write forwards to the user object's fetch_* / store_* (and optional
5
+ bulk copy_* / sync_* / *_block) callbacks. Also the base for Faces
6
+ authored in Ruby (face: true wraps a storage parent).
8
7
 
9
8
  ---------------------------------------------------------------------------- */
10
9
 
@@ -35,14 +34,38 @@
35
34
  */
36
35
 
37
36
  #include "carray.h"
37
+ #include "ca_obj_face.h" /* ca_face_* helpers for Face mode shortcuts */
38
+
39
+ static size_t
40
+ ca_object_dsize (const void *ap)
41
+ {
42
+ const CAObject *ca = (const CAObject *) ap;
43
+ return sizeof(CAObject) + ca->ndim * sizeof(ca_size_t);
44
+ }
45
+
46
+ /* Pool framework hooks: single ndim-sized tail (dim) in the _pool buffer
47
+ (uniform alloc/free discipline). */
48
+ static size_t
49
+ ca_object_pool_bytes (int8_t ndim)
50
+ {
51
+ ca_size_t n = (ndim > 0) ? ndim : 1;
52
+ return (size_t) n * sizeof(ca_size_t);
53
+ }
54
+
55
+ static void
56
+ ca_object_pool_init (void *ap, int8_t ndim)
57
+ {
58
+ CAObject *ca = (CAObject *) ap;
59
+ ca->dim = (ca_size_t *) ca->_pool;
60
+ }
38
61
 
39
62
  const rb_data_type_t caobject_data_type = {
40
- .parent = &cavirtual_data_type,
63
+ .parent = &caview_data_type,
41
64
  .wrap_struct_name = "CAObject",
42
65
  .function = {
43
66
  .dmark = ca_mark,
44
67
  .dfree = ca_free,
45
- .dsize = NULL,
68
+ .dsize = ca_object_dsize,
46
69
  .dcompact = NULL
47
70
  },
48
71
  .flags = RUBY_TYPED_FREE_IMMEDIATELY
@@ -50,22 +73,22 @@ const rb_data_type_t caobject_data_type = {
50
73
 
51
74
  VALUE rb_cCAObject;
52
75
 
53
-
54
- /* yard:
55
- class CAObject < CAVirtual # :nodoc:
56
- end
57
- */
58
-
59
-
60
76
  /* ---------------------------------------------------------------------- */
61
77
 
78
+ static size_t
79
+ ca_objectmask_dsize (const void *ap)
80
+ {
81
+ const CAObjectMask *ca = (const CAObjectMask *) ap;
82
+ return sizeof(CAObjectMask) + ca->ndim * sizeof(ca_size_t);
83
+ }
84
+
62
85
  const rb_data_type_t caobjectmask_data_type = {
63
- .parent = &carray_data_type,
86
+ .parent = &carray_data_type,
64
87
  .wrap_struct_name = "CAObjectMask",
65
88
  .function = {
66
89
  .dmark = ca_mark,
67
90
  .dfree = ca_free,
68
- .dsize = NULL,
91
+ .dsize = ca_objectmask_dsize,
69
92
  .dcompact = NULL
70
93
  },
71
94
  .flags = RUBY_TYPED_FREE_IMMEDIATELY
@@ -74,16 +97,16 @@ const rb_data_type_t caobjectmask_data_type = {
74
97
  static int8_t CA_OBJ_OBJECT_MASK;
75
98
  VALUE rb_cCAObjectMask;
76
99
 
77
- /* yard:
78
- class CAObjectMask < CAVirtual # :nodoc:
79
- end
80
- */
81
-
82
-
83
100
  static CAObjectMask *
84
101
  ca_objmask_new (VALUE array, int8_t ndim, ca_size_t *dim)
85
102
  {
86
103
  CAObjectMask *ca = ALLOC(CAObjectMask);
104
+ /* CA_OBJ_OBJECT_MASK registers no pool_init, so it lives on the legacy
105
+ ALLOC_N path (dim allocated separately, freed via free_ca_wrap's else
106
+ branch). ALLOC leaves _pool uninitialized; carray_setup_i keys the
107
+ dim allocation on _pool == NULL, so leaving it as garbage skips the
108
+ allocation and crashes the memcpy. Force the legacy path. */
109
+ ca->_pool = NULL;
87
110
  ca_wrap_setup_null((CArray *)ca, CA_BOOLEAN, ndim, dim, 0, NULL);
88
111
  ca->obj_type = CA_OBJ_OBJECT_MASK;
89
112
  ca->array = array;
@@ -106,111 +129,99 @@ ca_objmask_func_clone (void *ap)
106
129
  return ca_objmask_new(ca->array, ca->ndim, ca->dim);
107
130
  }
108
131
 
109
- void
110
- ca_objmask_func_fetch_addr (void *ap, ca_size_t addr, void *ptr)
132
+ /* per-cell cores: objmask reads/writes its own CA_BOOLEAN bit storage
133
+ (a contiguous wrap over the shared mask bytes) AND mirrors through the
134
+ user's Ruby mask_fetch_* / mask_store_* callbacks. Entity bit access
135
+ goes through ca_array_func_xfer_index / xfer_addrs. GET/PUT
136
+ direction-unified; the slots below forward. */
137
+ static void
138
+ ca_objmask_xfer_addr_one (CAObjectMask *ca, ca_size_t addr, void *ptr, int dir)
111
139
  {
112
- CAObjectMask *ca = (CAObjectMask *) ap;
113
140
  volatile VALUE ridx, raddr, rval;
114
141
  int i;
115
-
116
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_fetch_addr"), Qtrue) ) {
117
- raddr = SIZE2NUM(addr);
118
- rval = rb_funcall(ca->array, rb_intern("mask_fetch_addr"), 1, raddr);
119
- *(uint8_t*) ptr = NUM2INT(rval) == 0 ? 0 : 1;
120
- ca_array_func_store_addr(ca, addr, ptr);
121
- }
122
- else if ( rb_obj_respond_to(ca->array, rb_intern("mask_fetch_index"), Qtrue) ) {
123
- ca_size_t idx[CA_RANK_MAX];
124
- ca_addr2index(ca, addr, idx);
125
- ridx = rb_ary_new2(ca->ndim);
126
- for (i=0; i<ca->ndim; i++) {
127
- rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
142
+ if ( dir == CA_XFER_GET ) {
143
+ if ( rb_obj_respond_to(ca->array, rb_intern("mask_fetch_addr"), Qtrue) ) {
144
+ raddr = SIZE2NUM(addr);
145
+ rval = rb_funcall(ca->array, rb_intern("mask_fetch_addr"), 1, raddr);
146
+ *(uint8_t*) ptr = NUM2INT(rval) == 0 ? 0 : 1;
147
+ ca_array_func_xfer_addrs(ca, 1, &addr, ptr, CA_XFER_PUT);
148
+ }
149
+ else if ( rb_obj_respond_to(ca->array, rb_intern("mask_fetch_index"), Qtrue) ) {
150
+ ca_size_t idx[CA_RANK_MAX];
151
+ ca_addr2index((CArray *)ca, addr, idx);
152
+ ridx = rb_ary_new2(ca->ndim);
153
+ for (i=0; i<ca->ndim; i++) {
154
+ rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
155
+ }
156
+ rval = rb_funcall(ca->array, rb_intern("mask_fetch_index"), 1, ridx);
157
+ *(uint8_t*) ptr = NUM2INT(rval) == 0 ? 0 : 1;
158
+ ca_array_func_xfer_index(ca, idx, ptr, CA_XFER_PUT);
159
+ }
160
+ else {
161
+ ca_array_func_xfer_addrs(ca, 1, &addr, ptr, CA_XFER_GET);
128
162
  }
129
- rval = rb_funcall(ca->array, rb_intern("mask_fetch_index"), 1, ridx);
130
- *(uint8_t*) ptr = NUM2INT(rval) == 0 ? 0 : 1;
131
- ca_array_func_store_index(ca, idx, ptr);
132
163
  }
133
164
  else {
134
- ca_array_func_fetch_addr(ca, addr, ptr);
165
+ ca_array_func_xfer_addrs(ca, 1, &addr, ptr, CA_XFER_PUT);
166
+ rval = INT2NUM( *(uint8_t*)ptr );
167
+ if ( rb_obj_respond_to(ca->array, rb_intern("mask_store_addr"), Qtrue) ) {
168
+ raddr = SIZE2NUM(addr);
169
+ rb_funcall(ca->array, rb_intern("mask_store_addr"), 2, raddr, rval);
170
+ }
171
+ else if ( rb_obj_respond_to(ca->array, rb_intern("mask_store_index"), Qtrue) ) {
172
+ ca_size_t idx[CA_RANK_MAX];
173
+ ca_addr2index((CArray *)ca, addr, idx);
174
+ ridx = rb_ary_new2(ca->ndim);
175
+ for (i=0; i<ca->ndim; i++) {
176
+ rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
177
+ }
178
+ rb_funcall(ca->array, rb_intern("mask_store_index"), 2, ridx, rval);
179
+ }
135
180
  }
136
181
  }
137
182
 
138
- void
139
- ca_objmask_func_fetch_index (void *ap, ca_size_t *idx, void *ptr)
183
+ static void
184
+ ca_objmask_xfer_index_one (CAObjectMask *ca, ca_size_t *idx, void *ptr, int dir)
140
185
  {
141
- CAObjectMask *ca = (CAObjectMask *) ap;
142
186
  volatile VALUE ridx, raddr, rval;
143
187
  int i;
144
-
145
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_fetch_index"), Qtrue) ) {
146
- ridx = rb_ary_new2(ca->ndim);
147
- for (i=0; i<ca->ndim; i++) {
148
- rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
188
+ if ( dir == CA_XFER_GET ) {
189
+ if ( rb_obj_respond_to(ca->array, rb_intern("mask_fetch_index"), Qtrue) ) {
190
+ ridx = rb_ary_new2(ca->ndim);
191
+ for (i=0; i<ca->ndim; i++) {
192
+ rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
193
+ }
194
+ rval = rb_funcall(ca->array, rb_intern("mask_fetch_index"), 1, ridx);
195
+ *(uint8_t*) ptr = NUM2INT(rval) == 0 ? 0 : 1;
196
+ ca_array_func_xfer_index(ca, idx, ptr, CA_XFER_PUT);
197
+ }
198
+ else if ( rb_obj_respond_to(ca->array, rb_intern("mask_fetch_addr"), Qtrue) ) {
199
+ ca_size_t addr = ca_index2addr((CArray *)ca, idx);
200
+ raddr = SIZE2NUM(addr);
201
+ rval = rb_funcall(ca->array, rb_intern("mask_fetch_addr"), 1, raddr);
202
+ *(uint8_t*) ptr = NUM2INT(rval) == 0 ? 0 : 1;
203
+ ca_array_func_xfer_addrs(ca, 1, &addr, ptr, CA_XFER_PUT);
204
+ }
205
+ else {
206
+ ca_array_func_xfer_index(ca, idx, ptr, CA_XFER_GET);
149
207
  }
150
- rval = rb_funcall(ca->array, rb_intern("mask_fetch_index"), 1, ridx);
151
- *(uint8_t*) ptr = NUM2INT(rval) == 0 ? 0 : 1;
152
- ca_array_func_store_index(ca, idx, ptr);
153
- }
154
- else if ( rb_obj_respond_to(ca->array, rb_intern("mask_fetch_addr"), Qtrue) ) {
155
- ca_size_t addr = ca_index2addr(ca, idx);
156
- raddr = SIZE2NUM(addr);
157
- rval = rb_funcall(ca->array, rb_intern("mask_fetch_addr"), 1, raddr);
158
- *(uint8_t*) ptr = NUM2INT(rval) == 0 ? 0 : 1;
159
- ca_array_func_store_addr(ca, addr, ptr);
160
208
  }
161
209
  else {
162
- ca_array_func_fetch_index(ca, idx, ptr);
163
- }
164
- }
165
-
166
-
167
- void
168
- ca_objmask_func_store_addr (void *ap, ca_size_t addr, void *ptr)
169
- {
170
- CAObjectMask *ca = (CAObjectMask *) ap;
171
- volatile VALUE ridx, raddr, rval;
172
- int i;
173
-
174
- ca_array_func_store_addr(ca, addr, ptr);
175
- rval = INT2NUM( *(uint8_t*)ptr );
176
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_store_addr"), Qtrue) ) {
177
- raddr = SIZE2NUM(addr);
178
- rb_funcall(ca->array, rb_intern("mask_store_addr"), 2, raddr, rval);
179
- }
180
- else if ( rb_obj_respond_to(ca->array, rb_intern("mask_store_index"), Qtrue) ) {
181
- ca_size_t idx[CA_RANK_MAX];
182
- ca_addr2index(ca, addr, idx);
183
- ridx = rb_ary_new2(ca->ndim);
184
- for (i=0; i<ca->ndim; i++) {
185
- rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
210
+ ca_array_func_xfer_index(ca, idx, ptr, CA_XFER_PUT);
211
+ rval = INT2NUM( *(uint8_t*)ptr );
212
+ if ( rb_obj_respond_to(ca->array, rb_intern("mask_store_index"), Qtrue) ) {
213
+ ridx = rb_ary_new2(ca->ndim);
214
+ for (i=0; i<ca->ndim; i++) {
215
+ rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
216
+ }
217
+ rb_funcall(ca->array, rb_intern("mask_store_index"), 2, ridx, rval);
186
218
  }
187
- rb_funcall(ca->array, rb_intern("mask_store_index"), 2, ridx, rval);
188
- }
189
- }
190
-
191
- void
192
- ca_objmask_func_store_index (void *ap, ca_size_t *idx, void *ptr)
193
- {
194
- CAObjectMask *ca = (CAObjectMask *) ap;
195
- volatile VALUE ridx, raddr, rval;
196
- int i;
197
-
198
- ca_array_func_store_index(ca, idx, ptr);
199
- rval = INT2NUM( *(uint8_t*)ptr );
200
-
201
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_store_index"), Qtrue) ) {
202
- ridx = rb_ary_new2(ca->ndim);
203
- for (i=0; i<ca->ndim; i++) {
204
- rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
219
+ else if ( rb_obj_respond_to(ca->array, rb_intern("mask_store_addr"), Qtrue) ) {
220
+ ca_size_t addr = ca_index2addr((CArray *)ca, idx);
221
+ raddr = SIZE2NUM(addr);
222
+ rb_funcall(ca->array, rb_intern("mask_store_addr"), 2, raddr, rval);
205
223
  }
206
- rb_funcall(ca->array, rb_intern("mask_store_index"), 2, ridx, rval);
207
224
  }
208
- else if ( rb_obj_respond_to(ca->array, rb_intern("mask_store_addr"), Qtrue) ) {
209
- ca_size_t addr = ca_index2addr(ca, idx);
210
- raddr = SIZE2NUM(addr);
211
- rb_funcall(ca->array, rb_intern("mask_store_addr"), 2, raddr, rval);
212
- }
213
-
214
225
  }
215
226
 
216
227
  static void
@@ -234,46 +245,149 @@ ca_objmask_func_sync (void *ap)
234
245
  }
235
246
 
236
247
  static void
237
- ca_objmask_func_copy_data (void *ap, void *ptr)
248
+ ca_objmask_func_xfer_all (void *ap, void *data, int dir)
238
249
  {
239
250
  CAObjectMask *ca = (CAObjectMask *) ap;
240
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_copy_data"), Qtrue) ) {
241
- char *ptr0 = ca->ptr;
242
- ca->ptr = ptr;
243
- rb_funcall(ca->array, rb_intern("mask_copy_data"),
244
- 1, ca_objmask_mask_data(ca));
245
- ca->ptr = ptr0;
251
+ if ( dir == CA_XFER_GET ) {
252
+ if ( rb_obj_respond_to(ca->array, rb_intern("mask_copy_data"), Qtrue) ) {
253
+ char *ptr0 = ca->ptr;
254
+ ca->ptr = (char *) data;
255
+ rb_funcall(ca->array, rb_intern("mask_copy_data"),
256
+ 1, ca_objmask_mask_data(ca));
257
+ ca->ptr = ptr0;
258
+ }
259
+ else {
260
+ ca_array_func_xfer_all(ca, data, CA_XFER_GET);
261
+ }
246
262
  }
247
263
  else {
248
- ca_array_func_copy_data(ca, ptr);
264
+ if ( rb_obj_respond_to(ca->array, rb_intern("mask_copy_data"), Qtrue) ) {
265
+ char *ptr0 = ca->ptr;
266
+ ca->ptr = (char *) data;
267
+ rb_funcall(ca->array, rb_intern("mask_sync_data"),
268
+ 1, ca_objmask_mask_data(ca));
269
+ ca->ptr = ptr0;
270
+ }
271
+ else {
272
+ ca_array_func_xfer_all(ca, data, CA_XFER_PUT);
273
+ }
274
+ }
275
+ }
276
+
277
+ void
278
+ ca_objmask_func_fill_data (void *ap, void *val)
279
+ {
280
+ CAObjectMask *ca = (CAObjectMask *) ap;
281
+ ca_array_func_fill_data(ca, val);
282
+ if ( rb_obj_respond_to(ca->array, rb_intern("mask_fill_data"), Qtrue) ) {
283
+ rb_funcall(ca->array, rb_intern("mask_fill_data"),
284
+ 1, INT2NUM(*(uint8_t*)val));
249
285
  }
250
286
  }
251
287
 
252
288
  static void
253
- ca_objmask_func_sync_data (void *ap, void *ptr)
289
+ ca_objmask_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
290
+ {
291
+ ca_objmask_xfer_index_one((CAObjectMask *) ap, idx, data, dir);
292
+ }
293
+
294
+ /* Forward declaration: ca_object_wrap_transient (defined further down,
295
+ near the data-side xfer functions). */
296
+ static VALUE
297
+ ca_object_wrap_transient (int8_t data_type, ca_size_t bytes,
298
+ int8_t ndim, ca_size_t *dim, void *data, int dir);
299
+
300
+ /* Mask parallel of the bulk-addrs path. When `mask_copy_addrs` /
301
+ `mask_sync_addrs` is defined, 1-call bulk dispatch; else per-cell loop. */
302
+ static void
303
+ ca_objmask_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
304
+ void *data, int dir)
254
305
  {
255
306
  CAObjectMask *ca = (CAObjectMask *) ap;
256
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_copy_data"), Qtrue) ) {
257
- char *ptr0 = ca->ptr;
258
- ca->ptr = ptr;
259
- rb_funcall(ca->array, rb_intern("mask_sync_data"),
260
- 1, ca_objmask_mask_data(ca));
261
- ca->ptr = ptr0;
307
+ ID mid = (dir == CA_XFER_GET) ? rb_intern("mask_copy_addrs")
308
+ : rb_intern("mask_sync_addrs");
309
+
310
+ if ( n > 0 && rb_obj_respond_to(ca->array, mid, Qtrue) ) {
311
+ volatile VALUE raddrs, rdata;
312
+ ca_size_t dim1[1] = { n };
313
+ raddrs = ca_object_wrap_transient(CA_SIZE, sizeof(ca_size_t),
314
+ 1, dim1, addrs, CA_XFER_PUT);
315
+ rdata = ca_object_wrap_transient(ca->data_type, ca->bytes,
316
+ 1, dim1, data, dir);
317
+ rb_funcall(ca->array, mid, 2, raddrs, rdata);
318
+ return;
262
319
  }
263
- else {
264
- ca_array_func_sync_data(ca, ptr);
320
+
321
+ {
322
+ char *d = (char *) data;
323
+ ca_size_t i;
324
+ for ( i = 0; i < n; i++ ) {
325
+ ca_objmask_xfer_addr_one(ca, addrs[i], d + i * ca->bytes, dir);
326
+ }
265
327
  }
266
328
  }
267
329
 
268
- void
269
- ca_objmask_func_fill_data (void *ap, void *val)
330
+ /* Mask parallel of the partial-region path. Same per-axis index-step
331
+ gate; dispatches mask_copy_block / mask_sync_block when the gate passes
332
+ and the callback is defined. Otherwise falls back to flat-addr
333
+ expansion -> mask xfer_addrs. */
334
+ static void
335
+ ca_objmask_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
336
+ ca_size_t *strides, void *data, int dir)
270
337
  {
271
338
  CAObjectMask *ca = (CAObjectMask *) ap;
272
- ca_array_func_fill_data(ca, val);
273
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_fill_data"), Qtrue) ) {
274
- rb_funcall(ca->array, rb_intern("mask_fill_data"),
275
- 1, INT2NUM(*(uint8_t*)val));
339
+ int8_t ndim = ca->ndim;
340
+ ca_size_t native[CA_RANK_MAX], steps[CA_RANK_MAX], idx[CA_RANK_MAX];
341
+ ca_size_t *vaddrs;
342
+ ca_size_t base = 0, n = 1, i, s;
343
+ int8_t k;
344
+ volatile VALUE holder;
345
+ ID mid_block = (dir == CA_XFER_GET) ? rb_intern("mask_copy_block")
346
+ : rb_intern("mask_sync_block");
347
+
348
+ s = ca->bytes;
349
+ for (k = ndim - 1; k >= 0; k--) { native[k] = s; s *= ca->dim[k]; }
350
+ for (k = 0; k < ndim; k++) { base += starts[k] * native[k]; n *= counts[k]; }
351
+
352
+ if ( n == 0 ) return;
353
+
354
+ if ( rb_obj_respond_to(ca->array, mid_block, Qtrue) ) {
355
+ /* The request is over the view's addresses, so a transposed / flat
356
+ request is legal; the per-axis copy_block dispatch below would
357
+ misread it. See ca_xfer_stride_request_is_axis_box (carray.h). */
358
+ int aligned = ca_xfer_stride_request_is_axis_box(ca, starts, counts, strides);
359
+ for ( k = 0; aligned && k < ndim; k++ ) {
360
+ if ( strides[k] <= 0 || strides[k] % native[k] != 0 ) { aligned = 0; break; }
361
+ steps[k] = strides[k] / native[k];
362
+ }
363
+ if ( aligned ) {
364
+ volatile VALUE rstarts, rcounts, rsteps, rdata;
365
+ rstarts = rb_ary_new_capa(ndim);
366
+ rcounts = rb_ary_new_capa(ndim);
367
+ rsteps = rb_ary_new_capa(ndim);
368
+ for ( k = 0; k < ndim; k++ ) {
369
+ rb_ary_push(rstarts, SIZE2NUM(starts[k]));
370
+ rb_ary_push(rcounts, SIZE2NUM(counts[k]));
371
+ rb_ary_push(rsteps, SIZE2NUM(steps[k]));
372
+ }
373
+ rdata = ca_object_wrap_transient(ca->data_type, ca->bytes,
374
+ ndim, counts, data, dir);
375
+ rb_funcall(ca->array, mid_block, 4, rstarts, rcounts, rsteps, rdata);
376
+ return;
377
+ }
378
+ }
379
+
380
+ vaddrs = ALLOCV_N(ca_size_t, holder, n);
381
+ for (k = 0; k < ndim; k++) idx[k] = 0;
382
+ for (i = 0; i < n; i++) {
383
+ ca_size_t off = base;
384
+ for (k = 0; k < ndim; k++) off += idx[k] * strides[k];
385
+ vaddrs[i] = off / ca->bytes;
386
+ k = ndim - 1;
387
+ while (k >= 0) { if (++idx[k] < counts[k]) break; idx[k] = 0; k--; }
276
388
  }
389
+ ca_objmask_func_xfer_addrs(ca, n, vaddrs, data, dir);
390
+ ALLOCV_END(holder);
277
391
  }
278
392
 
279
393
  static ca_operation_function_t ca_objmask_func = {
@@ -281,27 +395,24 @@ static ca_operation_function_t ca_objmask_func = {
281
395
  CA_REAL_ARRAY,
282
396
  free_ca_wrap,
283
397
  ca_objmask_func_clone,
284
- ca_array_func_ptr_at_addr,
285
- ca_array_func_ptr_at_index,
286
- ca_objmask_func_fetch_addr,
287
- ca_objmask_func_fetch_index,
288
- ca_objmask_func_store_addr,
289
- ca_objmask_func_store_index,
290
398
  ca_array_func_allocate,
291
399
  ca_objmask_func_attach,
292
400
  ca_objmask_func_sync,
293
401
  ca_array_func_detach,
294
- ca_objmask_func_copy_data,
295
- ca_objmask_func_sync_data,
296
402
  ca_objmask_func_fill_data,
297
403
  ca_array_func_create_mask,
404
+ ca_objmask_func_xfer_index,
405
+ ca_objmask_func_xfer_addrs,
406
+ NULL, /* fold_stride: never-fold (callback boundary) */
407
+ ca_objmask_func_xfer_stride,
408
+ ca_objmask_func_xfer_all,
298
409
  };
299
410
 
300
411
  static VALUE
301
412
  rb_ca_objmask_s_allocate (VALUE klass)
302
413
  {
303
414
  CAObjectMask *ca;
304
- return TypedData_Make_Struct(klass, CAObjectMask, &caobject_data_type, ca);
415
+ return TypedData_Make_Struct(klass, CAObjectMask, &caobjectmask_data_type, ca);
305
416
  }
306
417
 
307
418
  static VALUE
@@ -361,7 +472,9 @@ ca_object_setup (CAObject *ca,
361
472
  ca->attach = 0;
362
473
  ca->nosync = 0;
363
474
 
364
- ca->dim = ALLOC_N(ca_size_t, ndim);
475
+ if ( ! ca->_pool ) {
476
+ ca->dim = ALLOC_N(ca_size_t, ndim);
477
+ }
365
478
 
366
479
  ca->data = ca_wrap_new_null(data_type, ndim, dim, bytes, NULL);
367
480
 
@@ -373,7 +486,7 @@ ca_object_setup (CAObject *ca,
373
486
  static CAObject *
374
487
  ca_object_new (int8_t data_type, int8_t ndim, ca_size_t *dim, ca_size_t bytes)
375
488
  {
376
- CAObject *ca = ALLOC(CAObject);
489
+ CAObject *ca = (CAObject *) ca_array_alloc(CA_OBJ_OBJECT, ndim);
377
490
  ca_object_setup(ca, data_type, ndim, dim, bytes);
378
491
  return ca;
379
492
  }
@@ -384,8 +497,13 @@ free_ca_object (void *ap)
384
497
  CAObject *ca = (CAObject *) ap;
385
498
  if ( ca != NULL ) {
386
499
  /* ca->mask will be GC-ed by Ruby interpreter */
387
- xfree(ca->dim);
388
- xfree(ca);
500
+ if ( ca->_pool ) {
501
+ ca_array_free(ca); /* dim lives in _pool */
502
+ }
503
+ else {
504
+ xfree(ca->dim);
505
+ xfree(ca);
506
+ }
389
507
  }
390
508
  }
391
509
 
@@ -395,186 +513,252 @@ static void *
395
513
  ca_object_func_clone (void *ap)
396
514
  {
397
515
  CAObject *ca = (CAObject *) ap;
398
- return ca_object_new(ca->bytes, ca->ndim, ca->dim, ca->bytes);
516
+ return ca_object_new(ca->data_type, ca->ndim, ca->dim, ca->bytes);
399
517
  }
400
518
 
401
519
  #define ca_object_func_ptr_at_addr ca_array_func_ptr_at_addr
402
520
  #define ca_object_func_ptr_at_index ca_array_func_ptr_at_index
403
521
 
522
+ /* per-cell cores: the addr- and index-primary Ruby-callback bridge logic,
523
+ direction-unified. The fetch_* / store_* slots below are thin
524
+ forwarders onto these. */
404
525
  static void
405
- ca_object_func_fetch_addr (void *ap, ca_size_t addr, void *ptr)
526
+ ca_object_xfer_addr_one (CAObject *ca, ca_size_t addr, void *ptr, int dir)
406
527
  {
407
- CAObject *ca = (CAObject *) ap;
408
528
  volatile VALUE ridx, raddr, rval;
409
529
  int i;
410
- if ( rb_obj_respond_to(ca->self, rb_intern("fetch_addr"), Qtrue) ) {
411
- raddr = SIZE2NUM(addr);
412
- rval = rb_funcall(ca->self, rb_intern("fetch_addr"), 1, raddr);
413
- if ( rval == CA_UNDEF ) {
414
- ca_update_mask(ca);
415
- if ( ! ca->mask ) {
416
- ca_create_mask(ca);
530
+ if ( dir == CA_XFER_GET ) {
531
+ if ( rb_obj_respond_to(ca->self, rb_intern("fetch_addr"), Qtrue) ) {
532
+ raddr = SIZE2NUM(addr);
533
+ rval = rb_funcall(ca->self, rb_intern("fetch_addr"), 1, raddr);
534
+ if ( rval == CA_UNDEF ) {
535
+ ca_update_mask(ca);
536
+ if ( ! ca->mask ) {
537
+ ca_create_mask(ca);
538
+ }
539
+ *((boolean8_t*)ca->mask->ptr + addr) = 1;
540
+ if ( ca->data_type == CA_OBJECT ) {
541
+ rb_ca_obj2ptr(ca->self, INT2NUM(0), ptr);
542
+ }
417
543
  }
418
- *(boolean8_t*)ca_ptr_at_addr(ca->mask, addr) = 1;
419
- if ( ca->data_type == CA_OBJECT ) {
420
- rb_ca_obj2ptr(ca->self, INT2NUM(0), ptr);
544
+ else {
545
+ if ( ca_has_mask(ca) ) {
546
+ *((boolean8_t*)ca->mask->ptr + addr) = 0;
547
+ }
548
+ rb_ca_obj2ptr(ca->self, rval, ptr);
421
549
  }
422
550
  }
423
551
  else {
424
- if ( ca_has_mask(ca) ) {
425
- *(boolean8_t*)ca_ptr_at_addr(ca->mask, addr) = 0;
552
+ ca_size_t idx[CA_RANK_MAX];
553
+ ca_addr2index(ca, addr, idx);
554
+ ridx = rb_ary_new2(ca->ndim);
555
+ for (i=0; i<ca->ndim; i++) {
556
+ rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
557
+ }
558
+ rval = rb_funcall(ca->self, rb_intern("fetch_index"), 1, ridx);
559
+ if ( rval == CA_UNDEF ) {
560
+ ca_update_mask(ca);
561
+ if ( ! ca->mask ) {
562
+ ca_create_mask(ca);
563
+ }
564
+ *((boolean8_t*)ca->mask->ptr + ca_index2addr(ca->mask, idx)) = 1;
565
+ if ( ca->data_type == CA_OBJECT ) {
566
+ rb_ca_obj2ptr(ca->self, INT2NUM(0), ptr);
567
+ }
568
+ }
569
+ else {
570
+ if ( ca_has_mask(ca) ) {
571
+ *((boolean8_t*)ca->mask->ptr + ca_index2addr(ca->mask, idx)) = 0;
572
+ }
573
+ rb_ca_obj2ptr(ca->self, rval, ptr);
426
574
  }
427
- rb_ca_obj2ptr(ca->self, rval, ptr);
428
575
  }
429
576
  }
430
577
  else {
431
- ca_size_t idx[CA_RANK_MAX];
432
- ca_addr2index(ca, addr, idx);
433
- ridx = rb_ary_new2(ca->ndim);
434
- for (i=0; i<ca->ndim; i++) {
435
- rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
436
- }
437
- rval = rb_funcall(ca->self, rb_intern("fetch_index"), 1, ridx);
438
- if ( rval == CA_UNDEF ) {
439
- ca_update_mask(ca);
440
- if ( ! ca->mask ) {
441
- ca_create_mask(ca);
442
- }
443
- *(boolean8_t*)ca_ptr_at_index(ca->mask, idx) = 1;
444
- if ( ca->data_type == CA_OBJECT ) {
445
- rb_ca_obj2ptr(ca->self, INT2NUM(0), ptr);
446
- }
578
+ if ( rb_obj_respond_to(ca->self, rb_intern("store_addr"), Qtrue) ) {
579
+ raddr = SIZE2NUM(addr);
580
+ rval = rb_ca_ptr2obj(ca->self, ptr);
581
+ rb_funcall(ca->self, rb_intern("store_addr"), 2, raddr, rval);
447
582
  }
448
583
  else {
449
- if ( ca_has_mask(ca) ) {
450
- *(boolean8_t*)ca_ptr_at_index(ca->mask, idx) = 0;
584
+ ca_size_t idx[CA_RANK_MAX];
585
+ ca_addr2index(ca, addr, idx);
586
+ ridx = rb_ary_new2(ca->ndim);
587
+ for (i=0; i<ca->ndim; i++) {
588
+ rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
451
589
  }
452
- rb_ca_obj2ptr(ca->self, rval, ptr);
590
+ rval = rb_ca_ptr2obj(ca->self, ptr);
591
+ rb_funcall(ca->self, rb_intern("store_index"), 2, ridx, rval);
453
592
  }
454
593
  }
455
594
  }
456
595
 
457
596
  static void
458
- ca_object_func_fetch_index (void *ap, ca_size_t *idx, void *ptr)
597
+ ca_object_xfer_index_one (CAObject *ca, ca_size_t *idx, void *ptr, int dir)
459
598
  {
460
- CAObject *ca = (CAObject *) ap;
461
599
  volatile VALUE ridx, raddr, rval;
462
600
  int i;
463
- if ( rb_obj_respond_to(ca->self, rb_intern("fetch_index"), Qtrue) ) {
464
- ridx = rb_ary_new2(ca->ndim);
465
- for (i=0; i<ca->ndim; i++) {
466
- rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
467
- }
468
- rval = rb_funcall(ca->self, rb_intern("fetch_index"), 1, ridx);
469
- if ( rval == CA_UNDEF ) {
470
- ca_update_mask(ca);
471
- if ( ! ca->mask ) {
472
- ca_create_mask(ca);
601
+ if ( dir == CA_XFER_GET ) {
602
+ if ( rb_obj_respond_to(ca->self, rb_intern("fetch_index"), Qtrue) ) {
603
+ ridx = rb_ary_new2(ca->ndim);
604
+ for (i=0; i<ca->ndim; i++) {
605
+ rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
473
606
  }
474
- *(boolean8_t*)ca_ptr_at_index(ca->mask, idx) = 1;
475
- if ( ca->data_type == CA_OBJECT ) {
476
- rb_ca_obj2ptr(ca->self, INT2NUM(0), ptr);
607
+ rval = rb_funcall(ca->self, rb_intern("fetch_index"), 1, ridx);
608
+ if ( rval == CA_UNDEF ) {
609
+ ca_update_mask(ca);
610
+ if ( ! ca->mask ) {
611
+ ca_create_mask(ca);
612
+ }
613
+ *((boolean8_t*)ca->mask->ptr + ca_index2addr(ca->mask, idx)) = 1;
614
+ if ( ca->data_type == CA_OBJECT ) {
615
+ rb_ca_obj2ptr(ca->self, INT2NUM(0), ptr);
616
+ }
617
+ }
618
+ else {
619
+ if ( ca_has_mask(ca) ) {
620
+ *((boolean8_t*)ca->mask->ptr + ca_index2addr(ca->mask, idx)) = 0;
621
+ }
622
+ rb_ca_obj2ptr(ca->self, rval, ptr);
477
623
  }
478
624
  }
479
625
  else {
480
- if ( ca_has_mask(ca) ) {
481
- *(boolean8_t*)ca_ptr_at_index(ca->mask, idx) = 0;
626
+ ca_size_t addr = ca_index2addr(ca, idx);
627
+ raddr = SIZE2NUM(addr);
628
+ rval = rb_funcall(ca->self, rb_intern("fetch_addr"), 1, raddr);
629
+ if ( rval == CA_UNDEF ) {
630
+ ca_update_mask(ca);
631
+ if ( ! ca->mask ) {
632
+ ca_create_mask(ca);
633
+ }
634
+ *((boolean8_t*)ca->mask->ptr + addr) = 1;
635
+ if ( ca->data_type == CA_OBJECT ) {
636
+ rb_ca_obj2ptr(ca->self, INT2NUM(0), ptr);
637
+ }
638
+ }
639
+ else {
640
+ if ( ca_has_mask(ca) ) {
641
+ *((boolean8_t*)ca->mask->ptr + addr) = 0;
642
+ }
643
+ rb_ca_obj2ptr(ca->self, rval, ptr);
482
644
  }
483
- rb_ca_obj2ptr(ca->self, rval, ptr);
484
645
  }
485
646
  }
486
647
  else {
487
- ca_size_t addr = ca_index2addr(ca, idx);
488
- raddr = SIZE2NUM(addr);
489
- rval = rb_funcall(ca->self, rb_intern("fetch_addr"), 1, raddr);
490
- if ( rval == CA_UNDEF ) {
491
- ca_update_mask(ca);
492
- if ( ! ca->mask ) {
493
- ca_create_mask(ca);
494
- }
495
- *(boolean8_t*)ca_ptr_at_addr(ca->mask, addr) = 1;
496
- if ( ca->data_type == CA_OBJECT ) {
497
- rb_ca_obj2ptr(ca->self, INT2NUM(0), ptr);
648
+ if ( rb_obj_respond_to(ca->self, rb_intern("store_index"), Qtrue) ) {
649
+ ridx = rb_ary_new2(ca->ndim);
650
+ for (i=0; i<ca->ndim; i++) {
651
+ rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
498
652
  }
653
+ rval = rb_ca_ptr2obj(ca->self, ptr);
654
+ rb_funcall(ca->self, rb_intern("store_index"), 2, ridx, rval);
499
655
  }
500
656
  else {
501
- if ( ca_has_mask(ca) ) {
502
- *(boolean8_t*)ca_ptr_at_addr(ca->mask, addr) = 0;
503
- }
504
- rb_ca_obj2ptr(ca->self, rval, ptr);
657
+ ca_size_t addr = ca_index2addr(ca, idx);
658
+ raddr = SIZE2NUM(addr);
659
+ rval = rb_ca_ptr2obj(ca->self, ptr);
660
+ rb_funcall(ca->self, rb_intern("store_addr"), 2, raddr, rval);
505
661
  }
506
662
  }
507
663
  }
508
664
 
509
665
  static void
510
- ca_object_func_store_addr (void *ap, ca_size_t addr, void *ptr)
666
+ ca_object_func_allocate (void *ap)
511
667
  {
512
668
  CAObject *ca = (CAObject *) ap;
513
- volatile VALUE ridx, raddr, rval;
514
- int i;
515
- if ( rb_obj_respond_to(ca->self, rb_intern("store_addr"), Qtrue) ) {
516
- raddr = SIZE2NUM(addr);
517
- rval = rb_ca_ptr2obj(ca->self, ptr);
518
- rb_funcall(ca->self, rb_intern("store_addr"), 2, raddr, rval);
669
+ /* ca->data->ptr = ALLOC_N(char, ca_length(ca)); */
670
+ ca->data->ptr = xmalloc(ca_length(ca));
671
+ if ( ca_is_object_type(ca->data) ) { /* GC safe */
672
+ VALUE *p = (VALUE *) ca->data->ptr;
673
+ VALUE zero = INT2NUM(0);
674
+ ca_size_t i;
675
+ for (i=0; i<ca->elements; i++) {
676
+ *p++ = zero;
677
+ }
678
+ }
679
+ ca->ptr = ca->data->ptr;
680
+ }
681
+
682
+ /* ca_object_dispatch_{copy,sync,fill}: bulk-path entry points used by
683
+ func_attach / func_sync / func_{copy,sync,fill}_data.
684
+
685
+ Contract: prefer user-supplied bulk methods (copy_data / sync_data /
686
+ fill_data) when defined; otherwise fall back to per-element
687
+ fetch_addr / store_addr loops, which themselves dispatch through the
688
+ existing fetch_addr<->fetch_index and store_addr<->store_index mutual
689
+ fallback at line 418/471/523/546.
690
+
691
+ Net effect: a CAObject subclass that only defines fetch_addr (or
692
+ only fetch_index) becomes fully usable -- to_ca, attach, sync,
693
+ arithmetic all work via the slow per-element fallback. The fast bulk
694
+ path is opt-in by defining copy_data / sync_data / fill_data. */
695
+
696
+ static void
697
+ ca_object_dispatch_copy (CAObject *ca, void *ptr)
698
+ {
699
+ if ( rb_obj_respond_to(ca->self, rb_intern("copy_data"), Qtrue) ) {
700
+ char *ptr0 = ca->data->ptr;
701
+ ca->data->ptr = ptr;
702
+ rb_funcall(ca->self, rb_intern("copy_data"),
703
+ 1, rb_ivar_get(ca->self, rb_intern("__data__")));
704
+ ca->data->ptr = ptr0;
519
705
  }
520
706
  else {
521
- ca_size_t idx[CA_RANK_MAX];
522
- ca_addr2index(ca, addr, idx);
523
- ridx = rb_ary_new2(ca->ndim);
524
- for (i=0; i<ca->ndim; i++) {
525
- rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
707
+ ca_size_t addr;
708
+ char *base = (char *) ptr;
709
+ for ( addr = 0; addr < ca->elements; addr++ ) {
710
+ ca_object_xfer_addr_one(ca, addr, base + addr * ca->bytes, CA_XFER_GET);
526
711
  }
527
- rval = rb_ca_ptr2obj(ca->self, ptr);
528
- rb_funcall(ca->self, rb_intern("store_index"), 2, ridx, rval);
529
712
  }
530
713
  }
531
714
 
532
715
  static void
533
- ca_object_func_store_index (void *ap, ca_size_t *idx, void *ptr)
716
+ ca_object_dispatch_sync (CAObject *ca, void *ptr)
534
717
  {
535
- CAObject *ca = (CAObject *) ap;
536
- volatile VALUE ridx, raddr, rval;
537
- int i;
538
- if ( rb_obj_respond_to(ca->self, rb_intern("store_index"), Qtrue) ) {
539
- ridx = rb_ary_new2(ca->ndim);
540
- for (i=0; i<ca->ndim; i++) {
541
- rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
542
- }
543
- rval = rb_ca_ptr2obj(ca->self, ptr);
544
- rb_funcall(ca->self, rb_intern("store_index"), 2, ridx, rval);
718
+ if ( rb_obj_respond_to(ca->self, rb_intern("sync_data"), Qtrue) ) {
719
+ char *ptr0 = ca->data->ptr;
720
+ ca->data->ptr = ptr;
721
+ rb_funcall(ca->self, rb_intern("sync_data"),
722
+ 1, rb_ivar_get(ca->self, rb_intern("__data__")));
723
+ ca->data->ptr = ptr0;
545
724
  }
546
725
  else {
547
- ca_size_t addr = ca_index2addr(ca, idx);
548
- raddr = SIZE2NUM(addr);
549
- rval = rb_ca_ptr2obj(ca->self, ptr);
550
- rb_funcall(ca->self, rb_intern("store_addr"), 2, raddr, rval);
726
+ ca_size_t addr;
727
+ char *base = (char *) ptr;
728
+ for ( addr = 0; addr < ca->elements; addr++ ) {
729
+ ca_object_xfer_addr_one(ca, addr, base + addr * ca->bytes, CA_XFER_PUT);
730
+ }
551
731
  }
552
732
  }
553
733
 
554
734
  static void
555
- ca_object_func_allocate (void *ap)
735
+ ca_object_dispatch_fill (CAObject *ca, void *ptr)
556
736
  {
557
- CAObject *ca = (CAObject *) ap;
558
- /* ca->data->ptr = ALLOC_N(char, ca_length(ca)); */
559
- ca->data->ptr = malloc_with_check(ca_length(ca));
560
- if ( ca_is_object_type(ca->data) ) { /* GC safe */
561
- VALUE *p = (VALUE *) ca->data->ptr;
562
- VALUE zero = INT2NUM(0);
563
- ca_size_t i;
564
- for (i=0; i<ca->elements; i++) {
565
- *p++ = zero;
737
+ if ( rb_obj_respond_to(ca->self, rb_intern("fill_data"), Qtrue) ) {
738
+ volatile VALUE rval = rb_ca_ptr2obj(ca->self, ptr);
739
+ rb_funcall(ca->self, rb_intern("fill_data"), 1, rval);
740
+ }
741
+ else {
742
+ ca_size_t addr;
743
+ for ( addr = 0; addr < ca->elements; addr++ ) {
744
+ ca_object_xfer_addr_one(ca, addr, ptr, CA_XFER_PUT);
566
745
  }
567
746
  }
568
- ca->ptr = ca->data->ptr;
569
747
  }
570
748
 
571
749
  static void
572
750
  ca_object_func_attach (void *ap)
573
751
  {
574
752
  CAObject *ca = (CAObject *) ap;
575
- volatile VALUE data = rb_ivar_get(ca->self, rb_intern("__data__"));
753
+ /* Face mode thin-forward to parent (= bypass the Ruby callback; same
754
+ semantic as ca_face_* helpers; CAObject prefix == CAView prefix so
755
+ layout is compatible). */
756
+ if ( ca_is_face(ca) ) {
757
+ ca_face_attach(ap);
758
+ return;
759
+ }
576
760
  /* ca->data->ptr = ALLOC_N(char, ca_length(ca)); */
577
- ca->data->ptr = malloc_with_check(ca_length(ca));
761
+ ca->data->ptr = xmalloc(ca_length(ca));
578
762
  if ( ca_is_object_type(ca->data) ) { /* GC safe */
579
763
  VALUE *p = (VALUE *) ca->data->ptr;
580
764
  VALUE zero = INT2NUM(0);
@@ -583,8 +767,8 @@ ca_object_func_attach (void *ap)
583
767
  *p++ = zero;
584
768
  }
585
769
  }
586
- ca->ptr = ca->data->ptr;
587
- rb_funcall(ca->self, rb_intern("copy_data"), 1, data);
770
+ ca->ptr = ca->data->ptr;
771
+ ca_object_dispatch_copy(ca, ca->ptr);
588
772
  if ( ca_has_mask(ca->data) ) {
589
773
  ca_update_mask(ca);
590
774
  if ( ! ca->mask ) {
@@ -597,48 +781,317 @@ static void
597
781
  ca_object_func_sync (void *ap)
598
782
  {
599
783
  CAObject *ca = (CAObject *) ap;
600
- rb_funcall(ca->self, rb_intern("sync_data"),
601
- 1, rb_ivar_get(ca->self, rb_intern("__data__")));
784
+ if ( ca_is_face(ca) ) {
785
+ ca_face_sync(ap);
786
+ return;
787
+ }
788
+ ca_object_dispatch_sync(ca, ca->data->ptr);
602
789
  }
603
790
 
604
791
  static void
605
792
  ca_object_func_detach (void *ap)
606
793
  {
607
794
  CAObject *ca = (CAObject *) ap;
608
- free(ca->data->ptr);
795
+ if ( ca_is_face(ca) ) {
796
+ ca_face_detach(ap);
797
+ return;
798
+ }
799
+ xfree(ca->data->ptr);
609
800
  ca->data->ptr = NULL;
610
801
  ca->ptr = NULL;
611
802
  }
612
803
 
804
+ /* xfer_*: CAObject is a per-element Ruby callback bridge -- a never-fold
805
+ boundary with no axis structure. GC
806
+ protection (cyclic + rb_protect on GET) for CA_OBJECT element type is
807
+ supplied by the central ca_xfer_* dispatchers, so these per-cell loops run
808
+ raw. */
809
+
810
+ /* Build a transient borrowed-buffer CArray for partial-region callbacks.
811
+
812
+ Used by ca_object_func_xfer_{stride,addrs} (and mask variants) to wrap the
813
+ slot's `data` buffer in a Ruby-visible CArray (counts-shape for block,
814
+ [n] for addrs) before invoking copy_block / sync_block / copy_addrs /
815
+ sync_addrs. Lifecycle: buffer is owned by the slot caller; the wrapper
816
+ installs free_ca_wrap as dfree, so the underlying ptr is NOT freed when
817
+ the wrapper is GC'd.
818
+
819
+ Object data_type GC safety: for GET (dir == CA_XFER_GET) the buffer may be
820
+ uninitialized garbage on entry, so we INT2NUM(0) zero-fill before
821
+ wrapping (same idiom as ca_object_func_allocate). PUT entries are
822
+ already valid VALUEs and need no init. */
823
+ static VALUE
824
+ ca_object_wrap_transient (int8_t data_type, ca_size_t bytes,
825
+ int8_t ndim, ca_size_t *dim, void *data, int dir)
826
+ {
827
+ CAWrap *wrap;
828
+ ca_size_t n = 1, i;
829
+
830
+ for ( i = 0; i < ndim; i++ ) n *= dim[i];
831
+
832
+ if ( dir == CA_XFER_GET && data_type == CA_OBJECT ) {
833
+ VALUE *p = (VALUE *) data;
834
+ VALUE zero = INT2NUM(0);
835
+ for ( i = 0; i < n; i++ ) p[i] = zero;
836
+ }
837
+
838
+ wrap = ca_wrap_new(data_type, ndim, dim, bytes, NULL, (char *) data);
839
+ return ca_wrap_struct(wrap);
840
+ }
841
+
613
842
  static void
614
- ca_object_func_copy_data (void *ap, void *ptr)
843
+ ca_object_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
615
844
  {
616
- CAObject *ca = (CAObject *) ap;
617
- char *ptr0 = ca->data->ptr;
618
- ca->data->ptr = ptr;
619
- rb_funcall(ca->self, rb_intern("copy_data"),
620
- 1, rb_ivar_get(ca->self, rb_intern("__data__")));
621
- ca->data->ptr = ptr0;
845
+ if ( ca_is_face((CAObject *) ap) ) {
846
+ ca_face_xfer_index(ap, idx, data, dir);
847
+ return;
848
+ }
849
+ ca_object_xfer_index_one((CAObject *) ap, idx, data, dir);
622
850
  }
623
851
 
852
+ /* Bulk-addrs path. When `copy_addrs` / `sync_addrs` is defined, dispatch
853
+ the entire address list in one Ruby call (1-D transient wrappers for
854
+ addrs and data). Otherwise fall back to a per-cell loop. */
624
855
  static void
625
- ca_object_func_sync_data (void *ap, void *ptr)
856
+ ca_object_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
857
+ void *data, int dir)
626
858
  {
627
- CAObject *ca = (CAObject *) ap;
628
- char *ptr0 = ca->data->ptr;
629
- ca->data->ptr = ptr;
630
- rb_funcall(ca->self, rb_intern("sync_data"),
631
- 1, rb_ivar_get(ca->self, rb_intern("__data__")));
632
- ca->data->ptr = ptr0;
859
+ CAObject *ca = (CAObject *) ap;
860
+ ID mid;
861
+ if ( ca_is_face(ca) ) {
862
+ ca_face_xfer_addrs(ap, n, addrs, data, dir);
863
+ return;
864
+ }
865
+ mid = (dir == CA_XFER_GET) ? rb_intern("copy_addrs")
866
+ : rb_intern("sync_addrs");
867
+
868
+ if ( n > 0 && rb_obj_respond_to(ca->self, mid, Qtrue) ) {
869
+ volatile VALUE raddrs, rdata;
870
+ ca_size_t dim1[1] = { n };
871
+ raddrs = ca_object_wrap_transient(CA_SIZE, sizeof(ca_size_t),
872
+ 1, dim1, addrs, CA_XFER_PUT);
873
+ rdata = ca_object_wrap_transient(ca->data_type, ca->bytes,
874
+ 1, dim1, data, dir);
875
+ rb_funcall(ca->self, mid, 2, raddrs, rdata);
876
+ return;
877
+ }
878
+
879
+ {
880
+ char *d = (char *) data;
881
+ ca_size_t i;
882
+ for ( i = 0; i < n; i++ ) {
883
+ ca_object_xfer_addr_one(ca, addrs[i], d + i * ca->bytes, dir);
884
+ }
885
+ }
886
+ }
887
+
888
+ /* Partial-region dispatch.
889
+
890
+ Gate: if `copy_block` / `sync_block` is defined AND all per-axis byte
891
+ strides represent an order-preserving, step >= 1 sub-region of self
892
+ (i.e. strides[k] > 0 && strides[k] % native[k] == 0), invoke the
893
+ high-level callback with index-space (starts, counts, steps) and a
894
+ counts-shaped transient. Empty region (any counts[k] == 0) returns
895
+ early without invoking the callback. Otherwise (negative/zero/non-
896
+ aligned strides, or callback undefined) fall back to flat-addr
897
+ expansion -> xfer_addrs (which itself may dispatch to copy_addrs /
898
+ sync_addrs if defined, else per-cell).
899
+
900
+ Gate proof: gate passes => for all k, strides[k] = m_k *
901
+ native[k] with m_k >= 1. If block axis k mapped to self axis pi(k),
902
+ strides[k] = native[pi(k)] would force native[pi(k)] >= native[k] hence
903
+ pi(k) <= k for all k. native is strictly decreasing in k, so the only
904
+ permutation satisfying pi(k) <= k for all k is identity. Therefore gate
905
+ passage <=> each block axis k traverses self axis k with step m_k.
906
+ Transpose / fancy / sub-element / negative / zero stride all fail the
907
+ gate and route to addrs path. */
908
+ static void
909
+ ca_object_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
910
+ ca_size_t *strides, void *data, int dir)
911
+ {
912
+ CAObject *ca = (CAObject *) ap;
913
+ int8_t ndim;
914
+ ca_size_t native[CA_RANK_MAX], steps[CA_RANK_MAX], idx[CA_RANK_MAX];
915
+ ca_size_t *vaddrs;
916
+ ca_size_t base = 0, n = 1, i, s;
917
+ int8_t k;
918
+ volatile VALUE holder;
919
+ ID mid_block;
920
+
921
+ if ( ca_is_face(ca) ) {
922
+ ca_face_xfer_stride(ap, starts, counts, strides, data, dir);
923
+ return;
924
+ }
925
+
926
+ ndim = ca->ndim;
927
+ mid_block = (dir == CA_XFER_GET) ? rb_intern("copy_block")
928
+ : rb_intern("sync_block");
929
+
930
+ s = ca->bytes;
931
+ for (k = ndim - 1; k >= 0; k--) { native[k] = s; s *= ca->dim[k]; }
932
+ for (k = 0; k < ndim; k++) { base += starts[k] * native[k]; n *= counts[k]; }
933
+
934
+ /* empty region: nothing to transfer */
935
+ if ( n == 0 ) return;
936
+
937
+ /* gate:
938
+ - reject strides[k] <= 0 (negative / zero broadcast)
939
+ - C99 makes (-native) % native == 0 true, so we must reject <= 0
940
+ before the % check
941
+ - given %==0 + >0, strides/native >= 1 holds automatically */
942
+ if ( rb_obj_respond_to(ca->self, mid_block, Qtrue) ) {
943
+ /* The request is over the view's addresses, so a transposed / flat
944
+ request is legal; the per-axis copy_block dispatch below would
945
+ misread it. See ca_xfer_stride_request_is_axis_box (carray.h). */
946
+ int aligned = ca_xfer_stride_request_is_axis_box(ca, starts, counts, strides);
947
+ for ( k = 0; aligned && k < ndim; k++ ) {
948
+ if ( strides[k] <= 0 || strides[k] % native[k] != 0 ) { aligned = 0; break; }
949
+ steps[k] = strides[k] / native[k]; /* >= 1 by construction */
950
+ }
951
+ if ( aligned ) {
952
+ /* high-level per-axis sub-region: dispatch copy_block / sync_block */
953
+ volatile VALUE rstarts, rcounts, rsteps, rdata;
954
+ rstarts = rb_ary_new_capa(ndim);
955
+ rcounts = rb_ary_new_capa(ndim);
956
+ rsteps = rb_ary_new_capa(ndim);
957
+ for ( k = 0; k < ndim; k++ ) {
958
+ rb_ary_push(rstarts, SIZE2NUM(starts[k]));
959
+ rb_ary_push(rcounts, SIZE2NUM(counts[k]));
960
+ rb_ary_push(rsteps, SIZE2NUM(steps[k]));
961
+ }
962
+ rdata = ca_object_wrap_transient(ca->data_type, ca->bytes,
963
+ ndim, counts, data, dir);
964
+ rb_funcall(ca->self, mid_block, 4, rstarts, rcounts, rsteps, rdata);
965
+ return;
966
+ }
967
+ }
968
+
969
+ /* fallback: flat-addr expansion -> xfer_addrs (per-cell or copy_addrs).
970
+ Handles negative strides, zero stride (broadcast), sub-element, and
971
+ fancy permutations. Same body as the non-block path. */
972
+ vaddrs = ALLOCV_N(ca_size_t, holder, n);
973
+ for (k = 0; k < ndim; k++) idx[k] = 0;
974
+ for (i = 0; i < n; i++) {
975
+ ca_size_t off = base;
976
+ for (k = 0; k < ndim; k++) off += idx[k] * strides[k];
977
+ vaddrs[i] = off / ca->bytes;
978
+ k = ndim - 1;
979
+ while (k >= 0) { if (++idx[k] < counts[k]) break; idx[k] = 0; k--; }
980
+ }
981
+ ca_object_func_xfer_addrs(ca, n, vaddrs, data, dir);
982
+ ALLOCV_END(holder);
983
+ }
984
+
985
+ static void
986
+ ca_object_func_xfer_all (void *ap, void *data, int dir)
987
+ {
988
+ if ( ca_is_face((CAObject *) ap) ) {
989
+ ca_face_xfer_all(ap, data, dir);
990
+ return;
991
+ }
992
+ if ( dir == CA_XFER_GET ) {
993
+ ca_object_dispatch_copy((CAObject *) ap, data);
994
+ }
995
+ else {
996
+ ca_object_dispatch_sync((CAObject *) ap, data);
997
+ }
633
998
  }
634
999
 
635
1000
  static void
636
1001
  ca_object_func_fill_data (void *ap, void *ptr)
1002
+ {
1003
+ if ( ca_is_face((CAObject *) ap) ) {
1004
+ ca_face_fill_data(ap, ptr);
1005
+ return;
1006
+ }
1007
+ ca_object_dispatch_fill((CAObject *) ap, ptr);
1008
+ }
1009
+
1010
+ /* Partial fill. fill_data carries no region and can only say "fill
1011
+ everything I cover", so before these two slots existed the only way to
1012
+ fill part of a CAObject was the per-cell default -- one store_addr per
1013
+ cell. The region arrives in the view's own address space. With
1014
+ `fill_block` defined (and the region an axis-aligned forward sub-box of
1015
+ self) it becomes one call; with `fill_addrs` defined it becomes one call
1016
+ per address window. With neither defined the behaviour is exactly the
1017
+ old default, so an existing subclass sees no change. */
1018
+ static void
1019
+ ca_object_func_fill_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *ptr)
637
1020
  {
638
1021
  CAObject *ca = (CAObject *) ap;
639
- volatile VALUE rval;
640
- rval = rb_ca_ptr2obj(ca->self, ptr);
641
- rb_funcall(ca->self, rb_intern("fill_data"), 1, rval);
1022
+ ID mid = rb_intern("fill_addrs");
1023
+
1024
+ if ( ca_is_face(ca) ) {
1025
+ ca_face_fill_addrs(ap, n, addrs, ptr);
1026
+ return;
1027
+ }
1028
+
1029
+ if ( n > 0 && rb_obj_respond_to(ca->self, mid, Qtrue) ) {
1030
+ volatile VALUE raddrs, rval;
1031
+ ca_size_t dim1[1] = { n };
1032
+ raddrs = ca_object_wrap_transient(CA_SIZE, sizeof(ca_size_t),
1033
+ 1, dim1, addrs, CA_XFER_PUT);
1034
+ rval = rb_ca_ptr2obj(ca->self, ptr);
1035
+ rb_funcall(ca->self, mid, 2, raddrs, rval);
1036
+ return;
1037
+ }
1038
+
1039
+ ca_fill_addrs_default(ap, n, addrs, ptr);
1040
+ }
1041
+
1042
+ /* Gate: one region axis per view axis, forward, and a whole number of
1043
+ elements per step. native is strictly decreasing, so steps[k] =
1044
+ m_k * native[k] with m_k >= 1 admits only the identity permutation --
1045
+ transpose, negative and zero (broadcast) steps, sub-element steps and
1046
+ dimension-dropping regions all fail it and take the addrs route. The
1047
+ bound check then confirms the decomposed box lies inside self. */
1048
+ static void
1049
+ ca_object_func_fill_stride (void *ap, ca_size_t base, int8_t ndim,
1050
+ ca_size_t *counts, ca_size_t *steps, void *ptr)
1051
+ {
1052
+ CAObject *ca = (CAObject *) ap;
1053
+ ca_size_t native[CA_RANK_MAX], istep[CA_RANK_MAX], start[CA_RANK_MAX];
1054
+ ca_size_t s;
1055
+ int8_t k;
1056
+ ID mid = rb_intern("fill_block");
1057
+
1058
+ if ( ca_is_face(ca) ) {
1059
+ ca_face_fill_stride(ap, base, ndim, counts, steps, ptr);
1060
+ return;
1061
+ }
1062
+
1063
+ if ( ndim == ca->ndim && rb_obj_respond_to(ca->self, mid, Qtrue) ) {
1064
+ int aligned = 1;
1065
+ s = 1;
1066
+ for ( k = ca->ndim - 1; k >= 0; k-- ) { native[k] = s; s *= ca->dim[k]; }
1067
+ for ( k = 0; k < ndim; k++ ) {
1068
+ if ( steps[k] <= 0 || steps[k] % native[k] != 0 ) { aligned = 0; break; }
1069
+ istep[k] = steps[k] / native[k];
1070
+ start[k] = ( base / native[k] ) % ca->dim[k];
1071
+ if ( start[k] + ( counts[k] - 1 ) * istep[k] >= ca->dim[k] ) {
1072
+ aligned = 0;
1073
+ break;
1074
+ }
1075
+ }
1076
+ if ( aligned ) {
1077
+ volatile VALUE rstarts, rcounts, rsteps, rval;
1078
+ rstarts = rb_ary_new_capa(ndim);
1079
+ rcounts = rb_ary_new_capa(ndim);
1080
+ rsteps = rb_ary_new_capa(ndim);
1081
+ for ( k = 0; k < ndim; k++ ) {
1082
+ rb_ary_push(rstarts, SIZE2NUM(start[k]));
1083
+ rb_ary_push(rcounts, SIZE2NUM(counts[k]));
1084
+ rb_ary_push(rsteps, SIZE2NUM(istep[k]));
1085
+ }
1086
+ rval = rb_ca_ptr2obj(ca->self, ptr);
1087
+ rb_funcall(ca->self, mid, 4, rstarts, rcounts, rsteps, rval);
1088
+ return;
1089
+ }
1090
+ }
1091
+
1092
+ /* addrs route: address windows -> ca_fill_addrs -> `fill_addrs` when the
1093
+ author defined it, else the per-cell default. */
1094
+ ca_fill_stride_via_addrs(ap, base, ndim, counts, steps, ptr);
642
1095
  }
643
1096
 
644
1097
  static void
@@ -646,6 +1099,18 @@ ca_object_func_create_mask (void *ap)
646
1099
  {
647
1100
  CAObject *ca = (CAObject *) ap;
648
1101
  volatile VALUE rmask;
1102
+ if ( ca_is_face(ca) ) {
1103
+ /* Face: the mask refers to the parent's mask (same as the C-level Face
1104
+ create_mask, e.g. ca_time_func_create_mask). Storage is the
1105
+ parent, so the mask must be the parent's, not the internal __data__. */
1106
+ ca_update_mask(ca->parent);
1107
+ if ( ! ca->parent->mask ) {
1108
+ ca_create_mask(ca->parent);
1109
+ }
1110
+ ca->mask = (CArray *) ca_refer_new(ca->parent->mask,
1111
+ CA_BOOLEAN, ca->ndim, ca->dim, 0, 0);
1112
+ return;
1113
+ }
649
1114
  if ( rb_obj_respond_to(ca->self, rb_intern("create_mask"), Qtrue) ) {
650
1115
  rb_funcall(ca->self, rb_intern("create_mask"), 0);
651
1116
  }
@@ -664,23 +1129,22 @@ ca_object_func_create_mask (void *ap)
664
1129
 
665
1130
  ca_operation_function_t ca_object_func = {
666
1131
  -1, /* CA_OBJ_OBJECT */
667
- CA_VIRTUAL_ARRAY,
1132
+ CA_VIEW_ARRAY,
668
1133
  free_ca_object,
669
1134
  ca_object_func_clone,
670
- ca_object_func_ptr_at_addr,
671
- ca_object_func_ptr_at_index,
672
- ca_object_func_fetch_addr,
673
- ca_object_func_fetch_index,
674
- ca_object_func_store_addr,
675
- ca_object_func_store_index,
676
1135
  ca_object_func_allocate,
677
1136
  ca_object_func_attach,
678
1137
  ca_object_func_sync,
679
1138
  ca_object_func_detach,
680
- ca_object_func_copy_data,
681
- ca_object_func_sync_data,
682
1139
  ca_object_func_fill_data,
683
1140
  ca_object_func_create_mask,
1141
+ ca_object_func_xfer_index,
1142
+ ca_object_func_xfer_addrs,
1143
+ NULL, /* fold_stride: never-fold (callback boundary) */
1144
+ ca_object_func_xfer_stride,
1145
+ ca_object_func_xfer_all,
1146
+ .fill_addrs = ca_object_func_fill_addrs,
1147
+ .fill_stride = ca_object_func_fill_stride,
684
1148
  };
685
1149
 
686
1150
  /* ------------------------------------------------------------------- */
@@ -701,13 +1165,28 @@ rb_ca_object_initialize_copy (VALUE self, VALUE other)
701
1165
  TypedData_Get_Struct(self, CAObject, &caobject_data_type, ca);
702
1166
  TypedData_Get_Struct(other, CAObject, &caobject_data_type, cs);
703
1167
 
1168
+ if ( ca_func[CA_OBJ_OBJECT].pool_init ) {
1169
+ ca_array_pool_alloc(ca, CA_OBJ_OBJECT, cs->ndim);
1170
+ }
704
1171
  ca_object_setup(ca, cs->data_type, cs->ndim, cs->dim, cs->bytes);
705
1172
  ca->self = self;
706
1173
 
707
- rb_ca_data_type_inherit(self, other);
1174
+ /* preserve flags (e.g. CA_FLAG_READ_ONLY) and structural parent;
1175
+ ca_object_setup zeroed both, so restore from the source.
1176
+ Copy the @parent ivar directly rather than via rb_ca_set_parent --
1177
+ the latter would re-freeze self when parent is frozen, but
1178
+ dup/clone semantics for freezing are handled separately by Ruby. */
1179
+ ca->flags = cs->flags;
1180
+ ca->parent = cs->parent;
1181
+ if ( cs->parent ) {
1182
+ volatile VALUE rparent = rb_ivar_get(other, rb_intern("parent"));
1183
+ if ( ! NIL_P(rparent) ) {
1184
+ rb_ivar_set(self, rb_intern("parent"), rparent);
1185
+ }
1186
+ }
1187
+
708
1188
 
709
1189
  data = ca_wrap_struct(ca->data);
710
- rb_ca_data_type_inherit(data, self);
711
1190
  rb_ivar_set(self, rb_intern("__data__"), data);
712
1191
 
713
1192
  ca_update_mask(cs);
@@ -723,7 +1202,9 @@ rb_ca_object_initialize_copy (VALUE self, VALUE other)
723
1202
  static VALUE
724
1203
  rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
725
1204
  {
726
- volatile VALUE rtype, rdim, ropt, rbytes = Qnil, rrdonly = Qnil, rparent = Qnil, rdata;
1205
+ volatile VALUE rtype, rdim, ropt, rbytes = Qnil, rrdonly = Qnil,
1206
+ rparent = Qnil, rface = Qnil, rstorage = Qnil, rdata,
1207
+ rorderable = Qnil, rcomparable = Qnil;
727
1208
  CAObject *ca;
728
1209
  int8_t data_type, ndim;
729
1210
  ca_size_t dim[CA_RANK_MAX];
@@ -731,15 +1212,47 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
731
1212
  int i;
732
1213
 
733
1214
  rb_scan_args(argc, argv, "21", (VALUE *) &rtype, (VALUE *) &rdim, (VALUE *) &ropt);
734
- rb_scan_options(ropt, "bytes,read_only,parent",
735
- &rbytes, &rrdonly, &rparent);
1215
+ rb_scan_options(ropt, "bytes,read_only,parent,face,storage,orderable_storage,comparable_storage",
1216
+ &rbytes, &rrdonly, &rparent, &rface, &rstorage,
1217
+ &rorderable, &rcomparable);
736
1218
 
737
- if ( ( ! NIL_P(rparent) ) && rb_obj_is_carray(rparent) ) {
1219
+ if ( ( ! NIL_P(rparent) ) && ( ! rb_obj_is_carray(rparent) ) ) {
738
1220
  rb_raise(rb_eRuntimeError, "option :parent should be a carray");
739
1221
  }
740
1222
 
1223
+ /* :storage is meaningful only under face: true (= it documents the
1224
+ parent's storage data_type, which is otherwise inferred from
1225
+ parent.data_type). Reject misuse loudly so the contract is explicit. */
1226
+ if ( ! NIL_P(rstorage) && ! RTEST(rface) ) {
1227
+ rb_raise(rb_eArgError,
1228
+ "CAObject: :storage is only meaningful with face: true "
1229
+ "(use the data_type argument for non-Face CAObject)");
1230
+ }
1231
+
1232
+ /* Face ordering/search opt-in: a Ruby-defined Face wires the C flags via
1233
+ these kwargs instead of a Ruby callback, so a Face can still be
1234
+ authored C-only. Both are meaningful only under face: true. */
1235
+ if ( ( RTEST(rorderable) || RTEST(rcomparable) ) && ! RTEST(rface) ) {
1236
+ rb_raise(rb_eArgError,
1237
+ "CAObject: :orderable_storage / :comparable_storage are only "
1238
+ "meaningful with face: true");
1239
+ }
1240
+
1241
+ /* face: true requires :parent. Under Face semantics the parent is the
1242
+ true storage owner and CAObject itself carries the semantic identity.
1243
+ The declared data_type is the SURFACE (= what dispatch sees);
1244
+ parent.data_type is the STORAGE. They may differ (= NonNumeric Face
1245
+ declares surface = CA_FIXLEN to gate mkkernel numeric ops while
1246
+ storage stays e.g. int64). */
1247
+ if ( RTEST(rface) ) {
1248
+ if ( NIL_P(rparent) ) {
1249
+ rb_raise(rb_eArgError,
1250
+ "CAObject Face mode (= face: true) requires :parent option "
1251
+ "(= storage CArray)");
1252
+ }
1253
+ }
1254
+
741
1255
  rb_ca_guess_type_and_bytes(rtype, rbytes, &data_type, &bytes);
742
- rb_ca_data_type_import(self, rtype);
743
1256
 
744
1257
  Check_Type(rdim, T_ARRAY);
745
1258
 
@@ -753,7 +1266,6 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
753
1266
  ca->self = self;
754
1267
 
755
1268
  rdata = ca_wrap_struct(ca->data);
756
- rb_ca_data_type_inherit(rdata, self);
757
1269
 
758
1270
  rb_ivar_set(self, rb_intern("__data__"), rdata);
759
1271
 
@@ -766,6 +1278,56 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
766
1278
  TypedData_Get_Struct(rparent, CArray, &carray_data_type, cp);
767
1279
  ca->parent = cp;
768
1280
  rb_ca_set_parent(self, rparent);
1281
+
1282
+ /* Face mode invariants. The check the author opts into depends on
1283
+ whether :storage is explicit:
1284
+ - :storage omitted → "surface == storage" contract:
1285
+ surface (= declared data_type) must equal
1286
+ parent.data_type. This catches typos for
1287
+ Numeric Face authors who didn't intend
1288
+ surface/storage to differ.
1289
+ - :storage given → "surface != storage" opted-in:
1290
+ surface is free (= e.g. CA_FIXLEN for
1291
+ NonNumeric Face), :storage must agree with
1292
+ parent.data_type, and bytes must match. */
1293
+ if ( RTEST(rface) ) {
1294
+ if ( bytes != cp->bytes ) {
1295
+ rb_raise(rb_eTypeError,
1296
+ "CAObject Face: bytes mismatch "
1297
+ "(declared=%lld, parent=%lld)",
1298
+ (long long) bytes, (long long) cp->bytes);
1299
+ }
1300
+ if ( NIL_P(rstorage) ) {
1301
+ /* surface == storage contract */
1302
+ if ( cp->data_type != data_type ) {
1303
+ rb_raise(rb_eTypeError,
1304
+ "CAObject Face: parent data_type mismatch "
1305
+ "(declared=%d, parent=%d). Use :storage option to "
1306
+ "opt into surface != storage (NonNumeric Face).",
1307
+ (int) data_type, (int) cp->data_type);
1308
+ }
1309
+ }
1310
+ else {
1311
+ /* opted-in surface != storage: validate :storage against parent */
1312
+ int8_t declared_storage;
1313
+ ca_size_t declared_storage_bytes;
1314
+ rb_ca_guess_type_and_bytes(rstorage, Qnil,
1315
+ &declared_storage, &declared_storage_bytes);
1316
+ if ( cp->data_type != declared_storage ) {
1317
+ rb_raise(rb_eTypeError,
1318
+ "CAObject Face: :storage mismatch "
1319
+ "(declared=%d, parent.data_type=%d)",
1320
+ (int) declared_storage, (int) cp->data_type);
1321
+ }
1322
+ }
1323
+ ca_set_flag(ca, CA_FLAG_IS_FACE);
1324
+ if ( RTEST(rorderable) ) {
1325
+ ca_set_flag(ca, CA_FLAG_FACE_ORDERABLE_STORAGE);
1326
+ }
1327
+ if ( RTEST(rcomparable) ) {
1328
+ ca_set_flag(ca, CA_FLAG_FACE_COMPARABLE_STORAGE);
1329
+ }
1330
+ }
769
1331
  }
770
1332
 
771
1333
  ca_update_mask(ca);
@@ -774,14 +1336,14 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
774
1336
  }
775
1337
 
776
1338
  void
777
- Init_ca_obj_object ()
1339
+ Init_ca_obj_object (void)
778
1340
  {
779
1341
  rb_cCAObjectMask = rb_define_class("CAObjectMask", rb_cCArray);
780
1342
 
781
1343
  CA_OBJ_OBJECT_MASK = ca_install_obj_type(rb_cCAObjectMask,
782
1344
  &caobjectmask_data_type,
783
1345
  rb_cCArrayMask,
784
- &carray_data_type, ca_objmask_func);
1346
+ &carray_data_type, &ca_objmask_func, sizeof(ca_objmask_func));
785
1347
  rb_define_const(rb_cObject, "CA_OBJ_OBJECT_MASK", INT2NUM(CA_OBJ_OBJECT_MASK));
786
1348
 
787
1349
  rb_define_alloc_func(rb_cCAObjectMask, rb_ca_objmask_s_allocate);
@@ -791,6 +1353,14 @@ Init_ca_obj_object ()
791
1353
 
792
1354
  rb_define_const(rb_cObject, "CA_OBJ_OBJECT", INT2NUM(CA_OBJ_OBJECT));
793
1355
 
1356
+ /* CA_OBJ_OBJECT is a builtin obj_type: carray_core.c assigned
1357
+ ca_func[CA_OBJ_OBJECT] = ca_object_func (without pool hooks) before
1358
+ this runs. Override the three pool slots in place (same pattern as
1359
+ CABlock overriding the CAStride baseline). */
1360
+ ca_func[CA_OBJ_OBJECT].struct_size = sizeof(CAObject);
1361
+ ca_func[CA_OBJ_OBJECT].pool_bytes = ca_object_pool_bytes;
1362
+ ca_func[CA_OBJ_OBJECT].pool_init = ca_object_pool_init;
1363
+
794
1364
  rb_define_alloc_func(rb_cCAObject, rb_ca_object_s_allocate);
795
1365
  rb_define_method(rb_cCAObject, "initialize_copy",
796
1366
  rb_ca_object_initialize_copy, 1);