carray 2.0.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (339) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +5 -25
  3. data/CHANGELOG.md +16 -0
  4. data/LICENSE +1 -1
  5. data/NEWS.md +3 -0
  6. data/README.md +128 -44
  7. data/carray.gemspec +22 -24
  8. data/ext/ca_array_pool.c +91 -0
  9. data/ext/ca_axis_descriptor.h +186 -0
  10. data/ext/ca_axis_dispatch.c +924 -0
  11. data/ext/ca_axis_group.c +1208 -0
  12. data/ext/ca_bincmp_dispatch.c +76 -0
  13. data/ext/ca_bincmp_dispatch.h +85 -0
  14. data/ext/ca_binop_dispatch.c +125 -0
  15. data/ext/ca_binop_dispatch.h +159 -0
  16. data/ext/ca_categorical_iterator.c +1375 -0
  17. data/ext/ca_compare.c +94 -0
  18. data/ext/ca_compare.h +26 -0
  19. data/ext/ca_composite_dispatch.c +414 -0
  20. data/ext/ca_composite_dispatch.h +116 -0
  21. data/ext/ca_for_buffer.h +96 -0
  22. data/ext/ca_for_each_element.h +241 -0
  23. data/ext/ca_group_iter.c +304 -0
  24. data/ext/ca_iter_substrate.h +325 -0
  25. data/ext/ca_kernel_iterator.c +4321 -0
  26. data/ext/ca_kernel_iterator.h +2603 -0
  27. data/ext/ca_moncmp_dispatch.c +37 -0
  28. data/ext/ca_moncmp_dispatch.h +62 -0
  29. data/ext/ca_monop_dispatch.c +200 -0
  30. data/ext/ca_monop_dispatch.h +235 -0
  31. data/ext/ca_obj_array.c +355 -359
  32. data/ext/ca_obj_bincmp.c +809 -0
  33. data/ext/ca_obj_binop.c +892 -0
  34. data/ext/ca_obj_bitarray.c +369 -164
  35. data/ext/ca_obj_bitfield.c +294 -234
  36. data/ext/ca_obj_block.c +189 -711
  37. data/ext/ca_obj_byte_swap.c +766 -0
  38. data/ext/ca_obj_const_string.c +965 -0
  39. data/ext/ca_obj_face.c +670 -0
  40. data/ext/ca_obj_face.h +247 -0
  41. data/ext/ca_obj_fake.c +228 -100
  42. data/ext/ca_obj_farray.c +54 -441
  43. data/ext/ca_obj_field.c +82 -529
  44. data/ext/ca_obj_fixlen_string.c +306 -0
  45. data/ext/ca_obj_grid.c +858 -440
  46. data/ext/ca_obj_meld.c +1034 -0
  47. data/ext/ca_obj_moncmp.c +569 -0
  48. data/ext/ca_obj_monop.c +1111 -0
  49. data/ext/ca_obj_object.c +772 -297
  50. data/ext/ca_obj_record.c +468 -0
  51. data/ext/ca_obj_reduce.c +97 -82
  52. data/ext/ca_obj_refer.c +569 -459
  53. data/ext/ca_obj_remap.c +475 -0
  54. data/ext/ca_obj_repeat.c +92 -477
  55. data/ext/ca_obj_roll.c +616 -0
  56. data/ext/ca_obj_select.c +344 -296
  57. data/ext/ca_obj_select_axis.c +1296 -0
  58. data/ext/ca_obj_shift.c +229 -791
  59. data/ext/ca_obj_source.c +78 -0
  60. data/ext/ca_obj_stack.c +1173 -0
  61. data/ext/ca_obj_stride.c +2501 -0
  62. data/ext/ca_obj_string.c +268 -0
  63. data/ext/ca_obj_tile.c +614 -0
  64. data/ext/ca_obj_time.c +546 -0
  65. data/ext/ca_obj_timedelta.c +435 -0
  66. data/ext/ca_obj_transpose.c +62 -516
  67. data/ext/ca_obj_triop.c +746 -0
  68. data/ext/ca_obj_unbound_repeat.c +207 -240
  69. data/ext/ca_obj_window.c +1131 -563
  70. data/ext/ca_op_byte_swap.c +175 -0
  71. data/ext/ca_op_ipower.c +319 -0
  72. data/ext/ca_op_powi.h +88 -0
  73. data/ext/ca_sort_kernels.h +132 -0
  74. data/ext/ca_sweep_engine.c +430 -0
  75. data/ext/ca_sweep_engine.h +157 -0
  76. data/ext/ca_transform_common.c +228 -0
  77. data/ext/ca_triop_dispatch.c +55 -0
  78. data/ext/ca_triop_dispatch.h +62 -0
  79. data/ext/carray.h +795 -402
  80. data/ext/carray_access.c +831 -711
  81. data/ext/carray_attribute.c +98 -329
  82. data/ext/carray_bincount.c +255 -0
  83. data/ext/carray_broadcast.c +283 -0
  84. data/ext/carray_call_cfunc.c +1360 -828
  85. data/ext/carray_call_cfunc.h +160 -0
  86. data/ext/carray_cast.c +1212 -301
  87. data/ext/carray_cast_func.rb +81 -40
  88. data/ext/carray_class.c +53 -63
  89. data/ext/carray_config.h +28 -0
  90. data/ext/carray_conversion.c +350 -346
  91. data/ext/carray_copy.c +156 -268
  92. data/ext/carray_core.c +1341 -198
  93. data/ext/carray_count.c +312 -0
  94. data/ext/carray_data_type.c +43 -19
  95. data/ext/carray_element.c +585 -213
  96. data/ext/carray_factorize.c +2542 -0
  97. data/ext/carray_generate.c +230 -559
  98. data/ext/carray_histogram.c +490 -0
  99. data/ext/carray_hold.c +228 -0
  100. data/ext/carray_index_classifier.c +1035 -0
  101. data/ext/carray_index_classifier.h +27 -0
  102. data/ext/carray_internal.h +120 -0
  103. data/ext/carray_kernels_bincmp.c +4445 -0
  104. data/ext/carray_kernels_binop.c +10979 -0
  105. data/ext/carray_kernels_init.c +36 -0
  106. data/ext/carray_kernels_map.c +3466 -0
  107. data/ext/carray_kernels_moncmp.c +2096 -0
  108. data/ext/carray_kernels_monop.c +18312 -0
  109. data/ext/carray_kernels_reduce_aggregate.c +25836 -0
  110. data/ext/carray_kernels_reduce_boolean.c +329 -0
  111. data/ext/carray_kernels_reduce_cumulative.c +14592 -0
  112. data/ext/carray_kernels_reduce_extreme.c +16947 -0
  113. data/ext/carray_kernels_reduce_variance.c +3909 -0
  114. data/ext/carray_kernels_scan.c +3692 -0
  115. data/ext/carray_kernels_search.c +32137 -0
  116. data/ext/carray_kernels_sort.c +10625 -0
  117. data/ext/carray_kernels_triop.c +1391 -0
  118. data/ext/carray_lazy.c +567 -0
  119. data/ext/carray_loop.c +88 -200
  120. data/ext/carray_mask.c +848 -154
  121. data/ext/carray_math_kernel.h +120 -0
  122. data/ext/carray_mathfunc.c +10 -241
  123. data/ext/carray_median_percentile.c +1257 -0
  124. data/ext/carray_memory_view.c +1625 -0
  125. data/ext/carray_operator.c +1526 -318
  126. data/ext/carray_order.c +664 -1394
  127. data/ext/carray_partition.c +416 -0
  128. data/ext/carray_random.c +518 -0
  129. data/ext/carray_scatter.c +357 -0
  130. data/ext/carray_slab.c +1219 -0
  131. data/ext/carray_slab.h +84 -0
  132. data/ext/carray_sort.c +829 -0
  133. data/ext/carray_sort_kernel.c +620 -0
  134. data/ext/carray_struct.c +695 -0
  135. data/ext/carray_test.c +343 -229
  136. data/ext/carray_undef.c +34 -17
  137. data/ext/carray_utils.c +175 -74
  138. data/ext/extconf.rb +216 -55
  139. data/ext/mk_call_cfunc.rb +480 -0
  140. data/ext/mkkernel.rb +8842 -0
  141. data/ext/ruby_carray.c +202 -101
  142. data/ext/version.h +5 -15
  143. data/ext/version.rb +5 -13
  144. data/lib/carray/arrow_tensor.rb +401 -0
  145. data/lib/carray/attribute.rb +166 -0
  146. data/lib/carray/autoload_carray.rb +220 -0
  147. data/lib/carray/autoload_method_extension.rb +44 -0
  148. data/lib/carray/axis_group.rb +711 -0
  149. data/lib/carray/basics.rb +481 -0
  150. data/lib/carray/bincount_nd.rb +358 -0
  151. data/lib/carray/block_iterator.rb +604 -0
  152. data/lib/carray/boolean_reduce.rb +109 -0
  153. data/lib/carray/categorical.rb +561 -0
  154. data/lib/carray/categorical_iterator.rb +1062 -0
  155. data/lib/carray/complex.rb +150 -0
  156. data/lib/carray/conditional.rb +216 -0
  157. data/lib/carray/const_string.rb +228 -0
  158. data/lib/carray/construct.rb +139 -328
  159. data/lib/carray/core_extensions.rb +240 -0
  160. data/lib/carray/data_type_extension.rb +233 -0
  161. data/lib/carray/fixlen_string.rb +95 -0
  162. data/lib/carray/frame/concat.rb +132 -0
  163. data/lib/carray/frame/convert.rb +95 -0
  164. data/lib/carray/frame/csv_parser.rb +211 -0
  165. data/lib/carray/frame/frame.rb +649 -0
  166. data/lib/carray/frame/group.rb +186 -0
  167. data/lib/carray/frame/io.rb +164 -0
  168. data/lib/carray/frame/join.rb +248 -0
  169. data/lib/carray/frame/records.rb +99 -0
  170. data/lib/carray/frame/sort.rb +113 -0
  171. data/lib/carray/frame/verbs.rb +299 -0
  172. data/lib/carray/frame.rb +16 -0
  173. data/lib/carray/histogram.rb +512 -0
  174. data/lib/carray/inspect.rb +37 -20
  175. data/lib/carray/iterator.rb +57 -349
  176. data/lib/carray/lazy.rb +889 -0
  177. data/lib/carray/mask_gap_fill.rb +200 -0
  178. data/lib/carray/math.rb +78 -342
  179. data/lib/carray/meld_reduce.rb +289 -0
  180. data/lib/carray/methods/align_addr.rb +116 -0
  181. data/lib/carray/methods/bin.rb +128 -0
  182. data/lib/carray/methods/bincount.rb +87 -0
  183. data/lib/carray/methods/bit_string.rb +92 -0
  184. data/lib/carray/methods/broadcast.rb +63 -0
  185. data/lib/carray/methods/choose.rb +39 -0
  186. data/lib/carray/methods/composition.rb +280 -0
  187. data/lib/carray/methods/gather_nd.rb +206 -0
  188. data/lib/carray/methods/index.rb +39 -0
  189. data/lib/carray/methods/insert_block.rb +99 -0
  190. data/lib/carray/methods/is_in.rb +141 -0
  191. data/lib/carray/methods/join.rb +90 -0
  192. data/lib/carray/methods/locate_addr.rb +47 -0
  193. data/lib/carray/methods/mask_duplicates.rb +41 -0
  194. data/lib/carray/methods/meshgrid.rb +91 -0
  195. data/lib/carray/methods/mode.rb +126 -0
  196. data/lib/carray/methods/nunique.rb +46 -0
  197. data/lib/carray/methods/resize.rb +56 -0
  198. data/lib/carray/methods/snap.rb +156 -0
  199. data/lib/carray/methods/string_format.rb +57 -0
  200. data/lib/carray/methods/unique.rb +47 -0
  201. data/lib/carray/methods/value_counts.rb +71 -0
  202. data/lib/carray/mkmf.rb +124 -101
  203. data/lib/carray/runtime.rb +108 -0
  204. data/lib/carray/serialize.rb +478 -167
  205. data/lib/carray/slab_iterator.rb +292 -0
  206. data/lib/carray/stack.rb +291 -0
  207. data/lib/carray/string.rb +56 -180
  208. data/lib/carray/string_operation_extension.rb +289 -0
  209. data/lib/carray/struct.rb +335 -323
  210. data/lib/carray/struct_builder.rb +697 -0
  211. data/lib/carray/table.rb +41 -2
  212. data/lib/carray/time.rb +2255 -38
  213. data/lib/carray/window_iterator.rb +655 -0
  214. data/lib/carray.rb +55 -57
  215. metadata +163 -130
  216. data/Rakefile +0 -51
  217. data/TODO.md +0 -18
  218. data/ext/ca_iter_block.c +0 -257
  219. data/ext/ca_iter_dimension.c +0 -299
  220. data/ext/ca_iter_window.c +0 -214
  221. data/ext/ca_obj_mapping.c +0 -644
  222. data/ext/carray_iterator.c +0 -641
  223. data/ext/carray_math.rb +0 -850
  224. data/ext/carray_numeric.c +0 -259
  225. data/ext/carray_sort_addr.c +0 -254
  226. data/ext/carray_stat.c +0 -2100
  227. data/ext/carray_stat_proc.rb +0 -1999
  228. data/ext/mkmath.rb +0 -741
  229. data/ext/ruby_ccomplex.c +0 -509
  230. data/ext/ruby_float_func.c +0 -86
  231. data/lib/carray/array.rb +0 -8
  232. data/lib/carray/autoload/autoload_base.rb +0 -19
  233. data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
  234. data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
  235. data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
  236. data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
  237. data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
  238. data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
  239. data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
  240. data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
  241. data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
  242. data/lib/carray/autoload/autoload_gem_random.rb +0 -8
  243. data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
  244. data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
  245. data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
  246. data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
  247. data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
  248. data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
  249. data/lib/carray/autoload/autoload_object_link.rb +0 -1
  250. data/lib/carray/autoload/autoload_object_pack.rb +0 -2
  251. data/lib/carray/autoload.rb +0 -141
  252. data/lib/carray/basic.rb +0 -191
  253. data/lib/carray/broadcast.rb +0 -101
  254. data/lib/carray/compose.rb +0 -315
  255. data/lib/carray/convert.rb +0 -115
  256. data/lib/carray/info.rb +0 -110
  257. data/lib/carray/io/imagemagick.rb +0 -235
  258. data/lib/carray/mask.rb +0 -102
  259. data/lib/carray/math/histogram.rb +0 -177
  260. data/lib/carray/math/recurrence.rb +0 -93
  261. data/lib/carray/object/ca_obj_iterator.rb +0 -50
  262. data/lib/carray/object/ca_obj_link.rb +0 -50
  263. data/lib/carray/object/ca_obj_pack.rb +0 -99
  264. data/lib/carray/obsolete.rb +0 -256
  265. data/lib/carray/ordering.rb +0 -181
  266. data/lib/carray/testing.rb +0 -51
  267. data/lib/carray/transform.rb +0 -109
  268. data/mailmap +0 -1
  269. data/misc/Methods.ja.md +0 -182
  270. data/misc/NOTE +0 -51
  271. data/spec/Classes/CABitfield_spec.rb +0 -58
  272. data/spec/Classes/CABlockIterator_spec.rb +0 -114
  273. data/spec/Classes/CABlock_spec.rb +0 -205
  274. data/spec/Classes/CAField_spec.rb +0 -39
  275. data/spec/Classes/CAGrid_spec.rb +0 -75
  276. data/spec/Classes/CAMap_spec.rb +0 -0
  277. data/spec/Classes/CAMapping_spec.rb +0 -105
  278. data/spec/Classes/CAObject_attribute_spec.rb +0 -33
  279. data/spec/Classes/CAObject_spec.rb +0 -33
  280. data/spec/Classes/CARefer_spec.rb +0 -93
  281. data/spec/Classes/CARepeat_spec.rb +0 -65
  282. data/spec/Classes/CASelect_spec.rb +0 -22
  283. data/spec/Classes/CAShift_spec.rb +0 -16
  284. data/spec/Classes/CAStruct_spec.rb +0 -71
  285. data/spec/Classes/CATranspose_spec.rb +0 -60
  286. data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
  287. data/spec/Classes/CAWindow_spec.rb +0 -54
  288. data/spec/Classes/CAWrap_spec.rb +0 -8
  289. data/spec/Classes/CArray_spec.rb +0 -184
  290. data/spec/Classes/CScalar_spec.rb +0 -55
  291. data/spec/Features/feature_130_spec.rb +0 -19
  292. data/spec/Features/feature_attributes_spec.rb +0 -280
  293. data/spec/Features/feature_boolean_spec.rb +0 -98
  294. data/spec/Features/feature_broadcast.rb +0 -116
  295. data/spec/Features/feature_cast_function.rb +0 -19
  296. data/spec/Features/feature_cast_spec.rb +0 -33
  297. data/spec/Features/feature_class_spec.rb +0 -84
  298. data/spec/Features/feature_complex_spec.rb +0 -42
  299. data/spec/Features/feature_composite_spec.rb +0 -124
  300. data/spec/Features/feature_convert_spec.rb +0 -46
  301. data/spec/Features/feature_copy_spec.rb +0 -123
  302. data/spec/Features/feature_creation_spec.rb +0 -84
  303. data/spec/Features/feature_element_spec.rb +0 -144
  304. data/spec/Features/feature_extream_spec.rb +0 -54
  305. data/spec/Features/feature_generate_spec.rb +0 -74
  306. data/spec/Features/feature_index_spec.rb +0 -69
  307. data/spec/Features/feature_mask_spec.rb +0 -580
  308. data/spec/Features/feature_math_spec.rb +0 -97
  309. data/spec/Features/feature_order_spec.rb +0 -146
  310. data/spec/Features/feature_ref_store_spec.rb +0 -209
  311. data/spec/Features/feature_serialization_spec.rb +0 -125
  312. data/spec/Features/feature_stat_spec.rb +0 -397
  313. data/spec/Features/feature_virtual_spec.rb +0 -48
  314. data/spec/Features/method_eq_spec.rb +0 -81
  315. data/spec/Features/method_is_nan_spec.rb +0 -12
  316. data/spec/Features/method_map_spec.rb +0 -54
  317. data/spec/Features/method_max_with.rb +0 -20
  318. data/spec/Features/method_min_with.rb +0 -19
  319. data/spec/Features/method_ne_spec.rb +0 -18
  320. data/spec/Features/method_project_spec.rb +0 -188
  321. data/spec/Features/method_ref_spec.rb +0 -27
  322. data/spec/Features/method_round_spec.rb +0 -11
  323. data/spec/Features/method_s_linspace_spec.rb +0 -48
  324. data/spec/Features/method_s_span_spec.rb +0 -14
  325. data/spec/Features/method_seq_spec.rb +0 -47
  326. data/spec/Features/method_sort_with.rb +0 -43
  327. data/spec/Features/method_sorted_with.rb +0 -29
  328. data/spec/Features/method_span_spec.rb +0 -42
  329. data/spec/Features/method_wrap_readonly_spec.rb +0 -43
  330. data/spec/UnitTest/test_CAVirtual.rb +0 -214
  331. data/spec/spec_all.rb +0 -10
  332. data/utils/ca_ase.rb +0 -21
  333. data/utils/ca_methods.rb +0 -15
  334. data/utils/cast_checker.rb +0 -30
  335. data/utils/convert_test.rb +0 -73
  336. data/utils/extract_yard.rb +0 -22
  337. data/utils/guess_shape.rb +0 -76
  338. data/utils/monkey_patch_methods.rb +0 -62
  339. data/utils/remove_resource_fork.sh +0 -5
data/ext/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,146 @@ 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
+ int aligned = 1;
356
+ for ( k = 0; k < ndim; k++ ) {
357
+ if ( strides[k] <= 0 || strides[k] % native[k] != 0 ) { aligned = 0; break; }
358
+ steps[k] = strides[k] / native[k];
359
+ }
360
+ if ( aligned ) {
361
+ volatile VALUE rstarts, rcounts, rsteps, rdata;
362
+ rstarts = rb_ary_new_capa(ndim);
363
+ rcounts = rb_ary_new_capa(ndim);
364
+ rsteps = rb_ary_new_capa(ndim);
365
+ for ( k = 0; k < ndim; k++ ) {
366
+ rb_ary_push(rstarts, SIZE2NUM(starts[k]));
367
+ rb_ary_push(rcounts, SIZE2NUM(counts[k]));
368
+ rb_ary_push(rsteps, SIZE2NUM(steps[k]));
369
+ }
370
+ rdata = ca_object_wrap_transient(ca->data_type, ca->bytes,
371
+ ndim, counts, data, dir);
372
+ rb_funcall(ca->array, mid_block, 4, rstarts, rcounts, rsteps, rdata);
373
+ return;
374
+ }
375
+ }
376
+
377
+ vaddrs = ALLOCV_N(ca_size_t, holder, n);
378
+ for (k = 0; k < ndim; k++) idx[k] = 0;
379
+ for (i = 0; i < n; i++) {
380
+ ca_size_t off = base;
381
+ for (k = 0; k < ndim; k++) off += idx[k] * strides[k];
382
+ vaddrs[i] = off / ca->bytes;
383
+ k = ndim - 1;
384
+ while (k >= 0) { if (++idx[k] < counts[k]) break; idx[k] = 0; k--; }
276
385
  }
386
+ ca_objmask_func_xfer_addrs(ca, n, vaddrs, data, dir);
387
+ ALLOCV_END(holder);
277
388
  }
278
389
 
279
390
  static ca_operation_function_t ca_objmask_func = {
@@ -281,27 +392,24 @@ static ca_operation_function_t ca_objmask_func = {
281
392
  CA_REAL_ARRAY,
282
393
  free_ca_wrap,
283
394
  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
395
  ca_array_func_allocate,
291
396
  ca_objmask_func_attach,
292
397
  ca_objmask_func_sync,
293
398
  ca_array_func_detach,
294
- ca_objmask_func_copy_data,
295
- ca_objmask_func_sync_data,
296
399
  ca_objmask_func_fill_data,
297
400
  ca_array_func_create_mask,
401
+ ca_objmask_func_xfer_index,
402
+ ca_objmask_func_xfer_addrs,
403
+ NULL, /* fold_stride: never-fold (callback boundary) */
404
+ ca_objmask_func_xfer_stride,
405
+ ca_objmask_func_xfer_all,
298
406
  };
299
407
 
300
408
  static VALUE
301
409
  rb_ca_objmask_s_allocate (VALUE klass)
302
410
  {
303
411
  CAObjectMask *ca;
304
- return TypedData_Make_Struct(klass, CAObjectMask, &caobject_data_type, ca);
412
+ return TypedData_Make_Struct(klass, CAObjectMask, &caobjectmask_data_type, ca);
305
413
  }
306
414
 
307
415
  static VALUE
@@ -361,7 +469,9 @@ ca_object_setup (CAObject *ca,
361
469
  ca->attach = 0;
362
470
  ca->nosync = 0;
363
471
 
364
- ca->dim = ALLOC_N(ca_size_t, ndim);
472
+ if ( ! ca->_pool ) {
473
+ ca->dim = ALLOC_N(ca_size_t, ndim);
474
+ }
365
475
 
366
476
  ca->data = ca_wrap_new_null(data_type, ndim, dim, bytes, NULL);
367
477
 
@@ -373,7 +483,7 @@ ca_object_setup (CAObject *ca,
373
483
  static CAObject *
374
484
  ca_object_new (int8_t data_type, int8_t ndim, ca_size_t *dim, ca_size_t bytes)
375
485
  {
376
- CAObject *ca = ALLOC(CAObject);
486
+ CAObject *ca = (CAObject *) ca_array_alloc(CA_OBJ_OBJECT, ndim);
377
487
  ca_object_setup(ca, data_type, ndim, dim, bytes);
378
488
  return ca;
379
489
  }
@@ -384,8 +494,13 @@ free_ca_object (void *ap)
384
494
  CAObject *ca = (CAObject *) ap;
385
495
  if ( ca != NULL ) {
386
496
  /* ca->mask will be GC-ed by Ruby interpreter */
387
- xfree(ca->dim);
388
- xfree(ca);
497
+ if ( ca->_pool ) {
498
+ ca_array_free(ca); /* dim lives in _pool */
499
+ }
500
+ else {
501
+ xfree(ca->dim);
502
+ xfree(ca);
503
+ }
389
504
  }
390
505
  }
391
506
 
@@ -395,186 +510,252 @@ static void *
395
510
  ca_object_func_clone (void *ap)
396
511
  {
397
512
  CAObject *ca = (CAObject *) ap;
398
- return ca_object_new(ca->bytes, ca->ndim, ca->dim, ca->bytes);
513
+ return ca_object_new(ca->data_type, ca->ndim, ca->dim, ca->bytes);
399
514
  }
400
515
 
401
516
  #define ca_object_func_ptr_at_addr ca_array_func_ptr_at_addr
402
517
  #define ca_object_func_ptr_at_index ca_array_func_ptr_at_index
403
518
 
519
+ /* per-cell cores: the addr- and index-primary Ruby-callback bridge logic,
520
+ direction-unified. The fetch_* / store_* slots below are thin
521
+ forwarders onto these. */
404
522
  static void
405
- ca_object_func_fetch_addr (void *ap, ca_size_t addr, void *ptr)
523
+ ca_object_xfer_addr_one (CAObject *ca, ca_size_t addr, void *ptr, int dir)
406
524
  {
407
- CAObject *ca = (CAObject *) ap;
408
525
  volatile VALUE ridx, raddr, rval;
409
526
  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);
527
+ if ( dir == CA_XFER_GET ) {
528
+ if ( rb_obj_respond_to(ca->self, rb_intern("fetch_addr"), Qtrue) ) {
529
+ raddr = SIZE2NUM(addr);
530
+ rval = rb_funcall(ca->self, rb_intern("fetch_addr"), 1, raddr);
531
+ if ( rval == CA_UNDEF ) {
532
+ ca_update_mask(ca);
533
+ if ( ! ca->mask ) {
534
+ ca_create_mask(ca);
535
+ }
536
+ *((boolean8_t*)ca->mask->ptr + addr) = 1;
537
+ if ( ca->data_type == CA_OBJECT ) {
538
+ rb_ca_obj2ptr(ca->self, INT2NUM(0), ptr);
539
+ }
417
540
  }
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);
541
+ else {
542
+ if ( ca_has_mask(ca) ) {
543
+ *((boolean8_t*)ca->mask->ptr + addr) = 0;
544
+ }
545
+ rb_ca_obj2ptr(ca->self, rval, ptr);
421
546
  }
422
547
  }
423
548
  else {
424
- if ( ca_has_mask(ca) ) {
425
- *(boolean8_t*)ca_ptr_at_addr(ca->mask, addr) = 0;
549
+ ca_size_t idx[CA_RANK_MAX];
550
+ ca_addr2index(ca, addr, idx);
551
+ ridx = rb_ary_new2(ca->ndim);
552
+ for (i=0; i<ca->ndim; i++) {
553
+ rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
554
+ }
555
+ rval = rb_funcall(ca->self, rb_intern("fetch_index"), 1, ridx);
556
+ if ( rval == CA_UNDEF ) {
557
+ ca_update_mask(ca);
558
+ if ( ! ca->mask ) {
559
+ ca_create_mask(ca);
560
+ }
561
+ *((boolean8_t*)ca->mask->ptr + ca_index2addr(ca->mask, idx)) = 1;
562
+ if ( ca->data_type == CA_OBJECT ) {
563
+ rb_ca_obj2ptr(ca->self, INT2NUM(0), ptr);
564
+ }
565
+ }
566
+ else {
567
+ if ( ca_has_mask(ca) ) {
568
+ *((boolean8_t*)ca->mask->ptr + ca_index2addr(ca->mask, idx)) = 0;
569
+ }
570
+ rb_ca_obj2ptr(ca->self, rval, ptr);
426
571
  }
427
- rb_ca_obj2ptr(ca->self, rval, ptr);
428
572
  }
429
573
  }
430
574
  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
- }
575
+ if ( rb_obj_respond_to(ca->self, rb_intern("store_addr"), Qtrue) ) {
576
+ raddr = SIZE2NUM(addr);
577
+ rval = rb_ca_ptr2obj(ca->self, ptr);
578
+ rb_funcall(ca->self, rb_intern("store_addr"), 2, raddr, rval);
447
579
  }
448
580
  else {
449
- if ( ca_has_mask(ca) ) {
450
- *(boolean8_t*)ca_ptr_at_index(ca->mask, idx) = 0;
581
+ ca_size_t idx[CA_RANK_MAX];
582
+ ca_addr2index(ca, addr, idx);
583
+ ridx = rb_ary_new2(ca->ndim);
584
+ for (i=0; i<ca->ndim; i++) {
585
+ rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
451
586
  }
452
- rb_ca_obj2ptr(ca->self, rval, ptr);
587
+ rval = rb_ca_ptr2obj(ca->self, ptr);
588
+ rb_funcall(ca->self, rb_intern("store_index"), 2, ridx, rval);
453
589
  }
454
590
  }
455
591
  }
456
592
 
457
593
  static void
458
- ca_object_func_fetch_index (void *ap, ca_size_t *idx, void *ptr)
594
+ ca_object_xfer_index_one (CAObject *ca, ca_size_t *idx, void *ptr, int dir)
459
595
  {
460
- CAObject *ca = (CAObject *) ap;
461
596
  volatile VALUE ridx, raddr, rval;
462
597
  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);
598
+ if ( dir == CA_XFER_GET ) {
599
+ if ( rb_obj_respond_to(ca->self, rb_intern("fetch_index"), Qtrue) ) {
600
+ ridx = rb_ary_new2(ca->ndim);
601
+ for (i=0; i<ca->ndim; i++) {
602
+ rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
473
603
  }
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);
604
+ rval = rb_funcall(ca->self, rb_intern("fetch_index"), 1, ridx);
605
+ if ( rval == CA_UNDEF ) {
606
+ ca_update_mask(ca);
607
+ if ( ! ca->mask ) {
608
+ ca_create_mask(ca);
609
+ }
610
+ *((boolean8_t*)ca->mask->ptr + ca_index2addr(ca->mask, idx)) = 1;
611
+ if ( ca->data_type == CA_OBJECT ) {
612
+ rb_ca_obj2ptr(ca->self, INT2NUM(0), ptr);
613
+ }
614
+ }
615
+ else {
616
+ if ( ca_has_mask(ca) ) {
617
+ *((boolean8_t*)ca->mask->ptr + ca_index2addr(ca->mask, idx)) = 0;
618
+ }
619
+ rb_ca_obj2ptr(ca->self, rval, ptr);
477
620
  }
478
621
  }
479
622
  else {
480
- if ( ca_has_mask(ca) ) {
481
- *(boolean8_t*)ca_ptr_at_index(ca->mask, idx) = 0;
623
+ ca_size_t addr = ca_index2addr(ca, idx);
624
+ raddr = SIZE2NUM(addr);
625
+ rval = rb_funcall(ca->self, rb_intern("fetch_addr"), 1, raddr);
626
+ if ( rval == CA_UNDEF ) {
627
+ ca_update_mask(ca);
628
+ if ( ! ca->mask ) {
629
+ ca_create_mask(ca);
630
+ }
631
+ *((boolean8_t*)ca->mask->ptr + addr) = 1;
632
+ if ( ca->data_type == CA_OBJECT ) {
633
+ rb_ca_obj2ptr(ca->self, INT2NUM(0), ptr);
634
+ }
635
+ }
636
+ else {
637
+ if ( ca_has_mask(ca) ) {
638
+ *((boolean8_t*)ca->mask->ptr + addr) = 0;
639
+ }
640
+ rb_ca_obj2ptr(ca->self, rval, ptr);
482
641
  }
483
- rb_ca_obj2ptr(ca->self, rval, ptr);
484
642
  }
485
643
  }
486
644
  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);
645
+ if ( rb_obj_respond_to(ca->self, rb_intern("store_index"), Qtrue) ) {
646
+ ridx = rb_ary_new2(ca->ndim);
647
+ for (i=0; i<ca->ndim; i++) {
648
+ rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
498
649
  }
650
+ rval = rb_ca_ptr2obj(ca->self, ptr);
651
+ rb_funcall(ca->self, rb_intern("store_index"), 2, ridx, rval);
499
652
  }
500
653
  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);
654
+ ca_size_t addr = ca_index2addr(ca, idx);
655
+ raddr = SIZE2NUM(addr);
656
+ rval = rb_ca_ptr2obj(ca->self, ptr);
657
+ rb_funcall(ca->self, rb_intern("store_addr"), 2, raddr, rval);
505
658
  }
506
659
  }
507
660
  }
508
661
 
509
662
  static void
510
- ca_object_func_store_addr (void *ap, ca_size_t addr, void *ptr)
663
+ ca_object_func_allocate (void *ap)
511
664
  {
512
665
  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);
666
+ /* ca->data->ptr = ALLOC_N(char, ca_length(ca)); */
667
+ ca->data->ptr = xmalloc(ca_length(ca));
668
+ if ( ca_is_object_type(ca->data) ) { /* GC safe */
669
+ VALUE *p = (VALUE *) ca->data->ptr;
670
+ VALUE zero = INT2NUM(0);
671
+ ca_size_t i;
672
+ for (i=0; i<ca->elements; i++) {
673
+ *p++ = zero;
674
+ }
675
+ }
676
+ ca->ptr = ca->data->ptr;
677
+ }
678
+
679
+ /* ca_object_dispatch_{copy,sync,fill}: bulk-path entry points used by
680
+ func_attach / func_sync / func_{copy,sync,fill}_data.
681
+
682
+ Contract: prefer user-supplied bulk methods (copy_data / sync_data /
683
+ fill_data) when defined; otherwise fall back to per-element
684
+ fetch_addr / store_addr loops, which themselves dispatch through the
685
+ existing fetch_addr<->fetch_index and store_addr<->store_index mutual
686
+ fallback at line 418/471/523/546.
687
+
688
+ Net effect: a CAObject subclass that only defines fetch_addr (or
689
+ only fetch_index) becomes fully usable -- to_ca, attach, sync,
690
+ arithmetic all work via the slow per-element fallback. The fast bulk
691
+ path is opt-in by defining copy_data / sync_data / fill_data. */
692
+
693
+ static void
694
+ ca_object_dispatch_copy (CAObject *ca, void *ptr)
695
+ {
696
+ if ( rb_obj_respond_to(ca->self, rb_intern("copy_data"), Qtrue) ) {
697
+ char *ptr0 = ca->data->ptr;
698
+ ca->data->ptr = ptr;
699
+ rb_funcall(ca->self, rb_intern("copy_data"),
700
+ 1, rb_ivar_get(ca->self, rb_intern("__data__")));
701
+ ca->data->ptr = ptr0;
519
702
  }
520
703
  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]));
704
+ ca_size_t addr;
705
+ char *base = (char *) ptr;
706
+ for ( addr = 0; addr < ca->elements; addr++ ) {
707
+ ca_object_xfer_addr_one(ca, addr, base + addr * ca->bytes, CA_XFER_GET);
526
708
  }
527
- rval = rb_ca_ptr2obj(ca->self, ptr);
528
- rb_funcall(ca->self, rb_intern("store_index"), 2, ridx, rval);
529
709
  }
530
710
  }
531
711
 
532
712
  static void
533
- ca_object_func_store_index (void *ap, ca_size_t *idx, void *ptr)
713
+ ca_object_dispatch_sync (CAObject *ca, void *ptr)
534
714
  {
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);
715
+ if ( rb_obj_respond_to(ca->self, rb_intern("sync_data"), Qtrue) ) {
716
+ char *ptr0 = ca->data->ptr;
717
+ ca->data->ptr = ptr;
718
+ rb_funcall(ca->self, rb_intern("sync_data"),
719
+ 1, rb_ivar_get(ca->self, rb_intern("__data__")));
720
+ ca->data->ptr = ptr0;
545
721
  }
546
722
  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);
723
+ ca_size_t addr;
724
+ char *base = (char *) ptr;
725
+ for ( addr = 0; addr < ca->elements; addr++ ) {
726
+ ca_object_xfer_addr_one(ca, addr, base + addr * ca->bytes, CA_XFER_PUT);
727
+ }
551
728
  }
552
729
  }
553
730
 
554
731
  static void
555
- ca_object_func_allocate (void *ap)
732
+ ca_object_dispatch_fill (CAObject *ca, void *ptr)
556
733
  {
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;
734
+ if ( rb_obj_respond_to(ca->self, rb_intern("fill_data"), Qtrue) ) {
735
+ volatile VALUE rval = rb_ca_ptr2obj(ca->self, ptr);
736
+ rb_funcall(ca->self, rb_intern("fill_data"), 1, rval);
737
+ }
738
+ else {
739
+ ca_size_t addr;
740
+ for ( addr = 0; addr < ca->elements; addr++ ) {
741
+ ca_object_xfer_addr_one(ca, addr, ptr, CA_XFER_PUT);
566
742
  }
567
743
  }
568
- ca->ptr = ca->data->ptr;
569
744
  }
570
745
 
571
746
  static void
572
747
  ca_object_func_attach (void *ap)
573
748
  {
574
749
  CAObject *ca = (CAObject *) ap;
575
- volatile VALUE data = rb_ivar_get(ca->self, rb_intern("__data__"));
750
+ /* Face mode thin-forward to parent (= bypass the Ruby callback; same
751
+ semantic as ca_face_* helpers; CAObject prefix == CAView prefix so
752
+ layout is compatible). */
753
+ if ( ca_is_face(ca) ) {
754
+ ca_face_attach(ap);
755
+ return;
756
+ }
576
757
  /* ca->data->ptr = ALLOC_N(char, ca_length(ca)); */
577
- ca->data->ptr = malloc_with_check(ca_length(ca));
758
+ ca->data->ptr = xmalloc(ca_length(ca));
578
759
  if ( ca_is_object_type(ca->data) ) { /* GC safe */
579
760
  VALUE *p = (VALUE *) ca->data->ptr;
580
761
  VALUE zero = INT2NUM(0);
@@ -583,8 +764,8 @@ ca_object_func_attach (void *ap)
583
764
  *p++ = zero;
584
765
  }
585
766
  }
586
- ca->ptr = ca->data->ptr;
587
- rb_funcall(ca->self, rb_intern("copy_data"), 1, data);
767
+ ca->ptr = ca->data->ptr;
768
+ ca_object_dispatch_copy(ca, ca->ptr);
588
769
  if ( ca_has_mask(ca->data) ) {
589
770
  ca_update_mask(ca);
590
771
  if ( ! ca->mask ) {
@@ -597,48 +778,227 @@ static void
597
778
  ca_object_func_sync (void *ap)
598
779
  {
599
780
  CAObject *ca = (CAObject *) ap;
600
- rb_funcall(ca->self, rb_intern("sync_data"),
601
- 1, rb_ivar_get(ca->self, rb_intern("__data__")));
781
+ if ( ca_is_face(ca) ) {
782
+ ca_face_sync(ap);
783
+ return;
784
+ }
785
+ ca_object_dispatch_sync(ca, ca->data->ptr);
602
786
  }
603
787
 
604
788
  static void
605
789
  ca_object_func_detach (void *ap)
606
790
  {
607
791
  CAObject *ca = (CAObject *) ap;
608
- free(ca->data->ptr);
792
+ if ( ca_is_face(ca) ) {
793
+ ca_face_detach(ap);
794
+ return;
795
+ }
796
+ xfree(ca->data->ptr);
609
797
  ca->data->ptr = NULL;
610
798
  ca->ptr = NULL;
611
799
  }
612
800
 
801
+ /* xfer_*: CAObject is a per-element Ruby callback bridge -- a never-fold
802
+ boundary with no axis structure. GC
803
+ protection (cyclic + rb_protect on GET) for CA_OBJECT element type is
804
+ supplied by the central ca_xfer_* dispatchers, so these per-cell loops run
805
+ raw. */
806
+
807
+ /* Build a transient borrowed-buffer CArray for partial-region callbacks.
808
+
809
+ Used by ca_object_func_xfer_{stride,addrs} (and mask variants) to wrap the
810
+ slot's `data` buffer in a Ruby-visible CArray (counts-shape for block,
811
+ [n] for addrs) before invoking copy_block / sync_block / copy_addrs /
812
+ sync_addrs. Lifecycle: buffer is owned by the slot caller; the wrapper
813
+ installs free_ca_wrap as dfree, so the underlying ptr is NOT freed when
814
+ the wrapper is GC'd.
815
+
816
+ Object data_type GC safety: for GET (dir == CA_XFER_GET) the buffer may be
817
+ uninitialized garbage on entry, so we INT2NUM(0) zero-fill before
818
+ wrapping (same idiom as ca_object_func_allocate). PUT entries are
819
+ already valid VALUEs and need no init. */
820
+ static VALUE
821
+ ca_object_wrap_transient (int8_t data_type, ca_size_t bytes,
822
+ int8_t ndim, ca_size_t *dim, void *data, int dir)
823
+ {
824
+ CAWrap *wrap;
825
+ ca_size_t n = 1, i;
826
+
827
+ for ( i = 0; i < ndim; i++ ) n *= dim[i];
828
+
829
+ if ( dir == CA_XFER_GET && data_type == CA_OBJECT ) {
830
+ VALUE *p = (VALUE *) data;
831
+ VALUE zero = INT2NUM(0);
832
+ for ( i = 0; i < n; i++ ) p[i] = zero;
833
+ }
834
+
835
+ wrap = ca_wrap_new(data_type, ndim, dim, bytes, NULL, (char *) data);
836
+ return ca_wrap_struct(wrap);
837
+ }
838
+
613
839
  static void
614
- ca_object_func_copy_data (void *ap, void *ptr)
840
+ ca_object_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
615
841
  {
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;
842
+ if ( ca_is_face((CAObject *) ap) ) {
843
+ ca_face_xfer_index(ap, idx, data, dir);
844
+ return;
845
+ }
846
+ ca_object_xfer_index_one((CAObject *) ap, idx, data, dir);
622
847
  }
623
848
 
849
+ /* Bulk-addrs path. When `copy_addrs` / `sync_addrs` is defined, dispatch
850
+ the entire address list in one Ruby call (1-D transient wrappers for
851
+ addrs and data). Otherwise fall back to a per-cell loop. */
624
852
  static void
625
- ca_object_func_sync_data (void *ap, void *ptr)
853
+ ca_object_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
854
+ void *data, int dir)
626
855
  {
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;
856
+ CAObject *ca = (CAObject *) ap;
857
+ ID mid;
858
+ if ( ca_is_face(ca) ) {
859
+ ca_face_xfer_addrs(ap, n, addrs, data, dir);
860
+ return;
861
+ }
862
+ mid = (dir == CA_XFER_GET) ? rb_intern("copy_addrs")
863
+ : rb_intern("sync_addrs");
864
+
865
+ if ( n > 0 && rb_obj_respond_to(ca->self, mid, Qtrue) ) {
866
+ volatile VALUE raddrs, rdata;
867
+ ca_size_t dim1[1] = { n };
868
+ raddrs = ca_object_wrap_transient(CA_SIZE, sizeof(ca_size_t),
869
+ 1, dim1, addrs, CA_XFER_PUT);
870
+ rdata = ca_object_wrap_transient(ca->data_type, ca->bytes,
871
+ 1, dim1, data, dir);
872
+ rb_funcall(ca->self, mid, 2, raddrs, rdata);
873
+ return;
874
+ }
875
+
876
+ {
877
+ char *d = (char *) data;
878
+ ca_size_t i;
879
+ for ( i = 0; i < n; i++ ) {
880
+ ca_object_xfer_addr_one(ca, addrs[i], d + i * ca->bytes, dir);
881
+ }
882
+ }
883
+ }
884
+
885
+ /* Partial-region dispatch.
886
+
887
+ Gate: if `copy_block` / `sync_block` is defined AND all per-axis byte
888
+ strides represent an order-preserving, step >= 1 sub-region of self
889
+ (i.e. strides[k] > 0 && strides[k] % native[k] == 0), invoke the
890
+ high-level callback with index-space (starts, counts, steps) and a
891
+ counts-shaped transient. Empty region (any counts[k] == 0) returns
892
+ early without invoking the callback. Otherwise (negative/zero/non-
893
+ aligned strides, or callback undefined) fall back to flat-addr
894
+ expansion -> xfer_addrs (which itself may dispatch to copy_addrs /
895
+ sync_addrs if defined, else per-cell).
896
+
897
+ Gate proof: gate passes => for all k, strides[k] = m_k *
898
+ native[k] with m_k >= 1. If block axis k mapped to self axis pi(k),
899
+ strides[k] = native[pi(k)] would force native[pi(k)] >= native[k] hence
900
+ pi(k) <= k for all k. native is strictly decreasing in k, so the only
901
+ permutation satisfying pi(k) <= k for all k is identity. Therefore gate
902
+ passage <=> each block axis k traverses self axis k with step m_k.
903
+ Transpose / fancy / sub-element / negative / zero stride all fail the
904
+ gate and route to addrs path. */
905
+ static void
906
+ ca_object_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
907
+ ca_size_t *strides, void *data, int dir)
908
+ {
909
+ CAObject *ca = (CAObject *) ap;
910
+ int8_t ndim;
911
+ ca_size_t native[CA_RANK_MAX], steps[CA_RANK_MAX], idx[CA_RANK_MAX];
912
+ ca_size_t *vaddrs;
913
+ ca_size_t base = 0, n = 1, i, s;
914
+ int8_t k;
915
+ volatile VALUE holder;
916
+ ID mid_block;
917
+
918
+ if ( ca_is_face(ca) ) {
919
+ ca_face_xfer_stride(ap, starts, counts, strides, data, dir);
920
+ return;
921
+ }
922
+
923
+ ndim = ca->ndim;
924
+ mid_block = (dir == CA_XFER_GET) ? rb_intern("copy_block")
925
+ : rb_intern("sync_block");
926
+
927
+ s = ca->bytes;
928
+ for (k = ndim - 1; k >= 0; k--) { native[k] = s; s *= ca->dim[k]; }
929
+ for (k = 0; k < ndim; k++) { base += starts[k] * native[k]; n *= counts[k]; }
930
+
931
+ /* empty region: nothing to transfer */
932
+ if ( n == 0 ) return;
933
+
934
+ /* gate:
935
+ - reject strides[k] <= 0 (negative / zero broadcast)
936
+ - C99 makes (-native) % native == 0 true, so we must reject <= 0
937
+ before the % check
938
+ - given %==0 + >0, strides/native >= 1 holds automatically */
939
+ if ( rb_obj_respond_to(ca->self, mid_block, Qtrue) ) {
940
+ int aligned = 1;
941
+ for ( k = 0; k < ndim; k++ ) {
942
+ if ( strides[k] <= 0 || strides[k] % native[k] != 0 ) { aligned = 0; break; }
943
+ steps[k] = strides[k] / native[k]; /* >= 1 by construction */
944
+ }
945
+ if ( aligned ) {
946
+ /* high-level per-axis sub-region: dispatch copy_block / sync_block */
947
+ volatile VALUE rstarts, rcounts, rsteps, rdata;
948
+ rstarts = rb_ary_new_capa(ndim);
949
+ rcounts = rb_ary_new_capa(ndim);
950
+ rsteps = rb_ary_new_capa(ndim);
951
+ for ( k = 0; k < ndim; k++ ) {
952
+ rb_ary_push(rstarts, SIZE2NUM(starts[k]));
953
+ rb_ary_push(rcounts, SIZE2NUM(counts[k]));
954
+ rb_ary_push(rsteps, SIZE2NUM(steps[k]));
955
+ }
956
+ rdata = ca_object_wrap_transient(ca->data_type, ca->bytes,
957
+ ndim, counts, data, dir);
958
+ rb_funcall(ca->self, mid_block, 4, rstarts, rcounts, rsteps, rdata);
959
+ return;
960
+ }
961
+ }
962
+
963
+ /* fallback: flat-addr expansion -> xfer_addrs (per-cell or copy_addrs).
964
+ Handles negative strides, zero stride (broadcast), sub-element, and
965
+ fancy permutations. Same body as the non-block path. */
966
+ vaddrs = ALLOCV_N(ca_size_t, holder, n);
967
+ for (k = 0; k < ndim; k++) idx[k] = 0;
968
+ for (i = 0; i < n; i++) {
969
+ ca_size_t off = base;
970
+ for (k = 0; k < ndim; k++) off += idx[k] * strides[k];
971
+ vaddrs[i] = off / ca->bytes;
972
+ k = ndim - 1;
973
+ while (k >= 0) { if (++idx[k] < counts[k]) break; idx[k] = 0; k--; }
974
+ }
975
+ ca_object_func_xfer_addrs(ca, n, vaddrs, data, dir);
976
+ ALLOCV_END(holder);
977
+ }
978
+
979
+ static void
980
+ ca_object_func_xfer_all (void *ap, void *data, int dir)
981
+ {
982
+ if ( ca_is_face((CAObject *) ap) ) {
983
+ ca_face_xfer_all(ap, data, dir);
984
+ return;
985
+ }
986
+ if ( dir == CA_XFER_GET ) {
987
+ ca_object_dispatch_copy((CAObject *) ap, data);
988
+ }
989
+ else {
990
+ ca_object_dispatch_sync((CAObject *) ap, data);
991
+ }
633
992
  }
634
993
 
635
994
  static void
636
995
  ca_object_func_fill_data (void *ap, void *ptr)
637
996
  {
638
- 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);
997
+ if ( ca_is_face((CAObject *) ap) ) {
998
+ ca_face_fill_data(ap, ptr);
999
+ return;
1000
+ }
1001
+ ca_object_dispatch_fill((CAObject *) ap, ptr);
642
1002
  }
643
1003
 
644
1004
  static void
@@ -646,6 +1006,18 @@ ca_object_func_create_mask (void *ap)
646
1006
  {
647
1007
  CAObject *ca = (CAObject *) ap;
648
1008
  volatile VALUE rmask;
1009
+ if ( ca_is_face(ca) ) {
1010
+ /* Face: the mask refers to the parent's mask (same as the C-level Face
1011
+ create_mask, e.g. ca_time_func_create_mask). Storage is the
1012
+ parent, so the mask must be the parent's, not the internal __data__. */
1013
+ ca_update_mask(ca->parent);
1014
+ if ( ! ca->parent->mask ) {
1015
+ ca_create_mask(ca->parent);
1016
+ }
1017
+ ca->mask = (CArray *) ca_refer_new(ca->parent->mask,
1018
+ CA_BOOLEAN, ca->ndim, ca->dim, 0, 0);
1019
+ return;
1020
+ }
649
1021
  if ( rb_obj_respond_to(ca->self, rb_intern("create_mask"), Qtrue) ) {
650
1022
  rb_funcall(ca->self, rb_intern("create_mask"), 0);
651
1023
  }
@@ -664,23 +1036,20 @@ ca_object_func_create_mask (void *ap)
664
1036
 
665
1037
  ca_operation_function_t ca_object_func = {
666
1038
  -1, /* CA_OBJ_OBJECT */
667
- CA_VIRTUAL_ARRAY,
1039
+ CA_VIEW_ARRAY,
668
1040
  free_ca_object,
669
1041
  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
1042
  ca_object_func_allocate,
677
1043
  ca_object_func_attach,
678
1044
  ca_object_func_sync,
679
1045
  ca_object_func_detach,
680
- ca_object_func_copy_data,
681
- ca_object_func_sync_data,
682
1046
  ca_object_func_fill_data,
683
1047
  ca_object_func_create_mask,
1048
+ ca_object_func_xfer_index,
1049
+ ca_object_func_xfer_addrs,
1050
+ NULL, /* fold_stride: never-fold (callback boundary) */
1051
+ ca_object_func_xfer_stride,
1052
+ ca_object_func_xfer_all,
684
1053
  };
685
1054
 
686
1055
  /* ------------------------------------------------------------------- */
@@ -701,13 +1070,28 @@ rb_ca_object_initialize_copy (VALUE self, VALUE other)
701
1070
  TypedData_Get_Struct(self, CAObject, &caobject_data_type, ca);
702
1071
  TypedData_Get_Struct(other, CAObject, &caobject_data_type, cs);
703
1072
 
1073
+ if ( ca_func[CA_OBJ_OBJECT].pool_init ) {
1074
+ ca_array_pool_alloc(ca, CA_OBJ_OBJECT, cs->ndim);
1075
+ }
704
1076
  ca_object_setup(ca, cs->data_type, cs->ndim, cs->dim, cs->bytes);
705
1077
  ca->self = self;
706
1078
 
707
- rb_ca_data_type_inherit(self, other);
1079
+ /* preserve flags (e.g. CA_FLAG_READ_ONLY) and structural parent;
1080
+ ca_object_setup zeroed both, so restore from the source.
1081
+ Copy the @parent ivar directly rather than via rb_ca_set_parent --
1082
+ the latter would re-freeze self when parent is frozen, but
1083
+ dup/clone semantics for freezing are handled separately by Ruby. */
1084
+ ca->flags = cs->flags;
1085
+ ca->parent = cs->parent;
1086
+ if ( cs->parent ) {
1087
+ volatile VALUE rparent = rb_ivar_get(other, rb_intern("parent"));
1088
+ if ( ! NIL_P(rparent) ) {
1089
+ rb_ivar_set(self, rb_intern("parent"), rparent);
1090
+ }
1091
+ }
1092
+
708
1093
 
709
1094
  data = ca_wrap_struct(ca->data);
710
- rb_ca_data_type_inherit(data, self);
711
1095
  rb_ivar_set(self, rb_intern("__data__"), data);
712
1096
 
713
1097
  ca_update_mask(cs);
@@ -723,7 +1107,9 @@ rb_ca_object_initialize_copy (VALUE self, VALUE other)
723
1107
  static VALUE
724
1108
  rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
725
1109
  {
726
- volatile VALUE rtype, rdim, ropt, rbytes = Qnil, rrdonly = Qnil, rparent = Qnil, rdata;
1110
+ volatile VALUE rtype, rdim, ropt, rbytes = Qnil, rrdonly = Qnil,
1111
+ rparent = Qnil, rface = Qnil, rstorage = Qnil, rdata,
1112
+ rorderable = Qnil, rcomparable = Qnil;
727
1113
  CAObject *ca;
728
1114
  int8_t data_type, ndim;
729
1115
  ca_size_t dim[CA_RANK_MAX];
@@ -731,15 +1117,47 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
731
1117
  int i;
732
1118
 
733
1119
  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);
1120
+ rb_scan_options(ropt, "bytes,read_only,parent,face,storage,orderable_storage,comparable_storage",
1121
+ &rbytes, &rrdonly, &rparent, &rface, &rstorage,
1122
+ &rorderable, &rcomparable);
736
1123
 
737
- if ( ( ! NIL_P(rparent) ) && rb_obj_is_carray(rparent) ) {
1124
+ if ( ( ! NIL_P(rparent) ) && ( ! rb_obj_is_carray(rparent) ) ) {
738
1125
  rb_raise(rb_eRuntimeError, "option :parent should be a carray");
739
1126
  }
740
1127
 
1128
+ /* :storage is meaningful only under face: true (= it documents the
1129
+ parent's storage data_type, which is otherwise inferred from
1130
+ parent.data_type). Reject misuse loudly so the contract is explicit. */
1131
+ if ( ! NIL_P(rstorage) && ! RTEST(rface) ) {
1132
+ rb_raise(rb_eArgError,
1133
+ "CAObject: :storage is only meaningful with face: true "
1134
+ "(use the data_type argument for non-Face CAObject)");
1135
+ }
1136
+
1137
+ /* Face ordering/search opt-in: a Ruby-defined Face wires the C flags via
1138
+ these kwargs instead of a Ruby callback, so a Face can still be
1139
+ authored C-only. Both are meaningful only under face: true. */
1140
+ if ( ( RTEST(rorderable) || RTEST(rcomparable) ) && ! RTEST(rface) ) {
1141
+ rb_raise(rb_eArgError,
1142
+ "CAObject: :orderable_storage / :comparable_storage are only "
1143
+ "meaningful with face: true");
1144
+ }
1145
+
1146
+ /* face: true requires :parent. Under Face semantics the parent is the
1147
+ true storage owner and CAObject itself carries the semantic identity.
1148
+ The declared data_type is the SURFACE (= what dispatch sees);
1149
+ parent.data_type is the STORAGE. They may differ (= NonNumeric Face
1150
+ declares surface = CA_FIXLEN to gate mkkernel numeric ops while
1151
+ storage stays e.g. int64). */
1152
+ if ( RTEST(rface) ) {
1153
+ if ( NIL_P(rparent) ) {
1154
+ rb_raise(rb_eArgError,
1155
+ "CAObject Face mode (= face: true) requires :parent option "
1156
+ "(= storage CArray)");
1157
+ }
1158
+ }
1159
+
741
1160
  rb_ca_guess_type_and_bytes(rtype, rbytes, &data_type, &bytes);
742
- rb_ca_data_type_import(self, rtype);
743
1161
 
744
1162
  Check_Type(rdim, T_ARRAY);
745
1163
 
@@ -753,7 +1171,6 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
753
1171
  ca->self = self;
754
1172
 
755
1173
  rdata = ca_wrap_struct(ca->data);
756
- rb_ca_data_type_inherit(rdata, self);
757
1174
 
758
1175
  rb_ivar_set(self, rb_intern("__data__"), rdata);
759
1176
 
@@ -766,6 +1183,56 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
766
1183
  TypedData_Get_Struct(rparent, CArray, &carray_data_type, cp);
767
1184
  ca->parent = cp;
768
1185
  rb_ca_set_parent(self, rparent);
1186
+
1187
+ /* Face mode invariants. The check the author opts into depends on
1188
+ whether :storage is explicit:
1189
+ - :storage omitted → "surface == storage" contract:
1190
+ surface (= declared data_type) must equal
1191
+ parent.data_type. This catches typos for
1192
+ Numeric Face authors who didn't intend
1193
+ surface/storage to differ.
1194
+ - :storage given → "surface != storage" opted-in:
1195
+ surface is free (= e.g. CA_FIXLEN for
1196
+ NonNumeric Face), :storage must agree with
1197
+ parent.data_type, and bytes must match. */
1198
+ if ( RTEST(rface) ) {
1199
+ if ( bytes != cp->bytes ) {
1200
+ rb_raise(rb_eTypeError,
1201
+ "CAObject Face: bytes mismatch "
1202
+ "(declared=%lld, parent=%lld)",
1203
+ (long long) bytes, (long long) cp->bytes);
1204
+ }
1205
+ if ( NIL_P(rstorage) ) {
1206
+ /* surface == storage contract */
1207
+ if ( cp->data_type != data_type ) {
1208
+ rb_raise(rb_eTypeError,
1209
+ "CAObject Face: parent data_type mismatch "
1210
+ "(declared=%d, parent=%d). Use :storage option to "
1211
+ "opt into surface != storage (NonNumeric Face).",
1212
+ (int) data_type, (int) cp->data_type);
1213
+ }
1214
+ }
1215
+ else {
1216
+ /* opted-in surface != storage: validate :storage against parent */
1217
+ int8_t declared_storage;
1218
+ ca_size_t declared_storage_bytes;
1219
+ rb_ca_guess_type_and_bytes(rstorage, Qnil,
1220
+ &declared_storage, &declared_storage_bytes);
1221
+ if ( cp->data_type != declared_storage ) {
1222
+ rb_raise(rb_eTypeError,
1223
+ "CAObject Face: :storage mismatch "
1224
+ "(declared=%d, parent.data_type=%d)",
1225
+ (int) declared_storage, (int) cp->data_type);
1226
+ }
1227
+ }
1228
+ ca_set_flag(ca, CA_FLAG_IS_FACE);
1229
+ if ( RTEST(rorderable) ) {
1230
+ ca_set_flag(ca, CA_FLAG_FACE_ORDERABLE_STORAGE);
1231
+ }
1232
+ if ( RTEST(rcomparable) ) {
1233
+ ca_set_flag(ca, CA_FLAG_FACE_COMPARABLE_STORAGE);
1234
+ }
1235
+ }
769
1236
  }
770
1237
 
771
1238
  ca_update_mask(ca);
@@ -774,14 +1241,14 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
774
1241
  }
775
1242
 
776
1243
  void
777
- Init_ca_obj_object ()
1244
+ Init_ca_obj_object (void)
778
1245
  {
779
1246
  rb_cCAObjectMask = rb_define_class("CAObjectMask", rb_cCArray);
780
1247
 
781
1248
  CA_OBJ_OBJECT_MASK = ca_install_obj_type(rb_cCAObjectMask,
782
1249
  &caobjectmask_data_type,
783
1250
  rb_cCArrayMask,
784
- &carray_data_type, ca_objmask_func);
1251
+ &carray_data_type, &ca_objmask_func, sizeof(ca_objmask_func));
785
1252
  rb_define_const(rb_cObject, "CA_OBJ_OBJECT_MASK", INT2NUM(CA_OBJ_OBJECT_MASK));
786
1253
 
787
1254
  rb_define_alloc_func(rb_cCAObjectMask, rb_ca_objmask_s_allocate);
@@ -791,6 +1258,14 @@ Init_ca_obj_object ()
791
1258
 
792
1259
  rb_define_const(rb_cObject, "CA_OBJ_OBJECT", INT2NUM(CA_OBJ_OBJECT));
793
1260
 
1261
+ /* CA_OBJ_OBJECT is a builtin obj_type: carray_core.c assigned
1262
+ ca_func[CA_OBJ_OBJECT] = ca_object_func (without pool hooks) before
1263
+ this runs. Override the three pool slots in place (same pattern as
1264
+ CABlock overriding the CAStride baseline). */
1265
+ ca_func[CA_OBJ_OBJECT].struct_size = sizeof(CAObject);
1266
+ ca_func[CA_OBJ_OBJECT].pool_bytes = ca_object_pool_bytes;
1267
+ ca_func[CA_OBJ_OBJECT].pool_init = ca_object_pool_init;
1268
+
794
1269
  rb_define_alloc_func(rb_cCAObject, rb_ca_object_s_allocate);
795
1270
  rb_define_method(rb_cCAObject, "initialize_copy",
796
1271
  rb_ca_object_initialize_copy, 1);