carray 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (339) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +5 -25
  3. data/CHANGELOG.md +16 -0
  4. data/LICENSE +1 -1
  5. data/NEWS.md +3 -0
  6. data/README.md +128 -44
  7. data/carray.gemspec +22 -24
  8. data/ext/ca_array_pool.c +91 -0
  9. data/ext/ca_axis_descriptor.h +186 -0
  10. data/ext/ca_axis_dispatch.c +924 -0
  11. data/ext/ca_axis_group.c +1208 -0
  12. data/ext/ca_bincmp_dispatch.c +76 -0
  13. data/ext/ca_bincmp_dispatch.h +85 -0
  14. data/ext/ca_binop_dispatch.c +125 -0
  15. data/ext/ca_binop_dispatch.h +159 -0
  16. data/ext/ca_categorical_iterator.c +1375 -0
  17. data/ext/ca_compare.c +94 -0
  18. data/ext/ca_compare.h +26 -0
  19. data/ext/ca_composite_dispatch.c +414 -0
  20. data/ext/ca_composite_dispatch.h +116 -0
  21. data/ext/ca_for_buffer.h +96 -0
  22. data/ext/ca_for_each_element.h +241 -0
  23. data/ext/ca_group_iter.c +304 -0
  24. data/ext/ca_iter_substrate.h +325 -0
  25. data/ext/ca_kernel_iterator.c +4321 -0
  26. data/ext/ca_kernel_iterator.h +2603 -0
  27. data/ext/ca_moncmp_dispatch.c +37 -0
  28. data/ext/ca_moncmp_dispatch.h +62 -0
  29. data/ext/ca_monop_dispatch.c +200 -0
  30. data/ext/ca_monop_dispatch.h +235 -0
  31. data/ext/ca_obj_array.c +355 -359
  32. data/ext/ca_obj_bincmp.c +809 -0
  33. data/ext/ca_obj_binop.c +892 -0
  34. data/ext/ca_obj_bitarray.c +369 -164
  35. data/ext/ca_obj_bitfield.c +294 -234
  36. data/ext/ca_obj_block.c +189 -711
  37. data/ext/ca_obj_byte_swap.c +766 -0
  38. data/ext/ca_obj_const_string.c +965 -0
  39. data/ext/ca_obj_face.c +670 -0
  40. data/ext/ca_obj_face.h +247 -0
  41. data/ext/ca_obj_fake.c +228 -100
  42. data/ext/ca_obj_farray.c +54 -441
  43. data/ext/ca_obj_field.c +82 -529
  44. data/ext/ca_obj_fixlen_string.c +306 -0
  45. data/ext/ca_obj_grid.c +858 -440
  46. data/ext/ca_obj_meld.c +1034 -0
  47. data/ext/ca_obj_moncmp.c +569 -0
  48. data/ext/ca_obj_monop.c +1111 -0
  49. data/ext/ca_obj_object.c +774 -298
  50. data/ext/ca_obj_record.c +468 -0
  51. data/ext/ca_obj_reduce.c +97 -82
  52. data/ext/ca_obj_refer.c +569 -459
  53. data/ext/ca_obj_remap.c +475 -0
  54. data/ext/ca_obj_repeat.c +92 -477
  55. data/ext/ca_obj_roll.c +616 -0
  56. data/ext/ca_obj_select.c +344 -296
  57. data/ext/ca_obj_select_axis.c +1296 -0
  58. data/ext/ca_obj_shift.c +230 -792
  59. data/ext/ca_obj_source.c +78 -0
  60. data/ext/ca_obj_stack.c +1173 -0
  61. data/ext/ca_obj_stride.c +2501 -0
  62. data/ext/ca_obj_string.c +268 -0
  63. data/ext/ca_obj_tile.c +614 -0
  64. data/ext/ca_obj_time.c +546 -0
  65. data/ext/ca_obj_timedelta.c +435 -0
  66. data/ext/ca_obj_transpose.c +62 -516
  67. data/ext/ca_obj_triop.c +746 -0
  68. data/ext/ca_obj_unbound_repeat.c +208 -241
  69. data/ext/ca_obj_window.c +1131 -563
  70. data/ext/ca_op_byte_swap.c +175 -0
  71. data/ext/ca_op_ipower.c +319 -0
  72. data/ext/ca_op_powi.h +88 -0
  73. data/ext/ca_sort_kernels.h +132 -0
  74. data/ext/ca_sweep_engine.c +430 -0
  75. data/ext/ca_sweep_engine.h +157 -0
  76. data/ext/ca_transform_common.c +228 -0
  77. data/ext/ca_triop_dispatch.c +55 -0
  78. data/ext/ca_triop_dispatch.h +62 -0
  79. data/ext/carray.h +795 -402
  80. data/ext/carray_access.c +831 -711
  81. data/ext/carray_attribute.c +98 -330
  82. data/ext/carray_bincount.c +255 -0
  83. data/ext/carray_broadcast.c +283 -0
  84. data/ext/carray_call_cfunc.c +1360 -828
  85. data/ext/carray_call_cfunc.h +160 -0
  86. data/ext/carray_cast.c +1212 -301
  87. data/ext/carray_cast_func.rb +81 -40
  88. data/ext/carray_class.c +53 -63
  89. data/ext/carray_config.h +28 -0
  90. data/ext/carray_conversion.c +350 -346
  91. data/ext/carray_copy.c +156 -268
  92. data/ext/carray_core.c +1342 -199
  93. data/ext/carray_count.c +312 -0
  94. data/ext/carray_data_type.c +43 -19
  95. data/ext/carray_element.c +585 -213
  96. data/ext/carray_factorize.c +2542 -0
  97. data/ext/carray_generate.c +230 -559
  98. data/ext/carray_histogram.c +490 -0
  99. data/ext/carray_hold.c +228 -0
  100. data/ext/carray_index_classifier.c +1035 -0
  101. data/ext/carray_index_classifier.h +27 -0
  102. data/ext/carray_internal.h +120 -0
  103. data/ext/carray_kernels_bincmp.c +4445 -0
  104. data/ext/carray_kernels_binop.c +10979 -0
  105. data/ext/carray_kernels_init.c +36 -0
  106. data/ext/carray_kernels_map.c +3466 -0
  107. data/ext/carray_kernels_moncmp.c +2096 -0
  108. data/ext/carray_kernels_monop.c +18312 -0
  109. data/ext/carray_kernels_reduce_aggregate.c +25836 -0
  110. data/ext/carray_kernels_reduce_boolean.c +329 -0
  111. data/ext/carray_kernels_reduce_cumulative.c +14592 -0
  112. data/ext/carray_kernels_reduce_extreme.c +16947 -0
  113. data/ext/carray_kernels_reduce_variance.c +3909 -0
  114. data/ext/carray_kernels_scan.c +3692 -0
  115. data/ext/carray_kernels_search.c +32137 -0
  116. data/ext/carray_kernels_sort.c +10625 -0
  117. data/ext/carray_kernels_triop.c +1391 -0
  118. data/ext/carray_lazy.c +567 -0
  119. data/ext/carray_loop.c +88 -200
  120. data/ext/carray_mask.c +848 -154
  121. data/ext/carray_math_kernel.h +120 -0
  122. data/ext/carray_mathfunc.c +10 -241
  123. data/ext/carray_median_percentile.c +1257 -0
  124. data/ext/carray_memory_view.c +1625 -0
  125. data/ext/carray_operator.c +1526 -318
  126. data/ext/carray_order.c +664 -1394
  127. data/ext/carray_partition.c +416 -0
  128. data/ext/carray_random.c +518 -0
  129. data/ext/carray_scatter.c +357 -0
  130. data/ext/carray_slab.c +1219 -0
  131. data/ext/carray_slab.h +84 -0
  132. data/ext/carray_sort.c +829 -0
  133. data/ext/carray_sort_kernel.c +620 -0
  134. data/ext/carray_struct.c +695 -0
  135. data/ext/carray_test.c +343 -229
  136. data/ext/carray_undef.c +34 -17
  137. data/ext/carray_utils.c +175 -74
  138. data/ext/extconf.rb +216 -55
  139. data/ext/mk_call_cfunc.rb +480 -0
  140. data/ext/mkkernel.rb +8842 -0
  141. data/ext/ruby_carray.c +202 -101
  142. data/ext/version.h +4 -14
  143. data/ext/version.rb +5 -13
  144. data/lib/carray/arrow_tensor.rb +401 -0
  145. data/lib/carray/attribute.rb +166 -0
  146. data/lib/carray/autoload_carray.rb +220 -0
  147. data/lib/carray/autoload_method_extension.rb +44 -0
  148. data/lib/carray/axis_group.rb +711 -0
  149. data/lib/carray/basics.rb +481 -0
  150. data/lib/carray/bincount_nd.rb +358 -0
  151. data/lib/carray/block_iterator.rb +604 -0
  152. data/lib/carray/boolean_reduce.rb +109 -0
  153. data/lib/carray/categorical.rb +561 -0
  154. data/lib/carray/categorical_iterator.rb +1062 -0
  155. data/lib/carray/complex.rb +150 -0
  156. data/lib/carray/conditional.rb +216 -0
  157. data/lib/carray/const_string.rb +228 -0
  158. data/lib/carray/construct.rb +139 -328
  159. data/lib/carray/core_extensions.rb +240 -0
  160. data/lib/carray/data_type_extension.rb +233 -0
  161. data/lib/carray/fixlen_string.rb +95 -0
  162. data/lib/carray/frame/concat.rb +132 -0
  163. data/lib/carray/frame/convert.rb +95 -0
  164. data/lib/carray/frame/csv_parser.rb +211 -0
  165. data/lib/carray/frame/frame.rb +649 -0
  166. data/lib/carray/frame/group.rb +186 -0
  167. data/lib/carray/frame/io.rb +164 -0
  168. data/lib/carray/frame/join.rb +248 -0
  169. data/lib/carray/frame/records.rb +99 -0
  170. data/lib/carray/frame/sort.rb +113 -0
  171. data/lib/carray/frame/verbs.rb +299 -0
  172. data/lib/carray/frame.rb +16 -0
  173. data/lib/carray/histogram.rb +512 -0
  174. data/lib/carray/inspect.rb +37 -20
  175. data/lib/carray/iterator.rb +57 -349
  176. data/lib/carray/lazy.rb +889 -0
  177. data/lib/carray/mask_gap_fill.rb +200 -0
  178. data/lib/carray/math.rb +78 -342
  179. data/lib/carray/meld_reduce.rb +289 -0
  180. data/lib/carray/methods/align_addr.rb +116 -0
  181. data/lib/carray/methods/bin.rb +128 -0
  182. data/lib/carray/methods/bincount.rb +87 -0
  183. data/lib/carray/methods/bit_string.rb +92 -0
  184. data/lib/carray/methods/broadcast.rb +63 -0
  185. data/lib/carray/methods/choose.rb +39 -0
  186. data/lib/carray/methods/composition.rb +280 -0
  187. data/lib/carray/methods/gather_nd.rb +206 -0
  188. data/lib/carray/methods/index.rb +39 -0
  189. data/lib/carray/methods/insert_block.rb +99 -0
  190. data/lib/carray/methods/is_in.rb +141 -0
  191. data/lib/carray/methods/join.rb +90 -0
  192. data/lib/carray/methods/locate_addr.rb +47 -0
  193. data/lib/carray/methods/mask_duplicates.rb +41 -0
  194. data/lib/carray/methods/meshgrid.rb +91 -0
  195. data/lib/carray/methods/mode.rb +126 -0
  196. data/lib/carray/methods/nunique.rb +46 -0
  197. data/lib/carray/methods/resize.rb +56 -0
  198. data/lib/carray/methods/snap.rb +156 -0
  199. data/lib/carray/methods/string_format.rb +57 -0
  200. data/lib/carray/methods/unique.rb +47 -0
  201. data/lib/carray/methods/value_counts.rb +71 -0
  202. data/lib/carray/mkmf.rb +124 -101
  203. data/lib/carray/runtime.rb +108 -0
  204. data/lib/carray/serialize.rb +478 -167
  205. data/lib/carray/slab_iterator.rb +292 -0
  206. data/lib/carray/stack.rb +291 -0
  207. data/lib/carray/string.rb +56 -180
  208. data/lib/carray/string_operation_extension.rb +289 -0
  209. data/lib/carray/struct.rb +335 -323
  210. data/lib/carray/struct_builder.rb +697 -0
  211. data/lib/carray/table.rb +41 -2
  212. data/lib/carray/time.rb +2255 -38
  213. data/lib/carray/window_iterator.rb +655 -0
  214. data/lib/carray.rb +55 -57
  215. metadata +163 -130
  216. data/Rakefile +0 -51
  217. data/TODO.md +0 -18
  218. data/ext/ca_iter_block.c +0 -257
  219. data/ext/ca_iter_dimension.c +0 -299
  220. data/ext/ca_iter_window.c +0 -214
  221. data/ext/ca_obj_mapping.c +0 -644
  222. data/ext/carray_iterator.c +0 -641
  223. data/ext/carray_math.rb +0 -850
  224. data/ext/carray_numeric.c +0 -259
  225. data/ext/carray_sort_addr.c +0 -254
  226. data/ext/carray_stat.c +0 -2100
  227. data/ext/carray_stat_proc.rb +0 -1999
  228. data/ext/mkmath.rb +0 -741
  229. data/ext/ruby_ccomplex.c +0 -509
  230. data/ext/ruby_float_func.c +0 -86
  231. data/lib/carray/array.rb +0 -8
  232. data/lib/carray/autoload/autoload_base.rb +0 -19
  233. data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
  234. data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
  235. data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
  236. data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
  237. data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
  238. data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
  239. data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
  240. data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
  241. data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
  242. data/lib/carray/autoload/autoload_gem_random.rb +0 -8
  243. data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
  244. data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
  245. data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
  246. data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
  247. data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
  248. data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
  249. data/lib/carray/autoload/autoload_object_link.rb +0 -1
  250. data/lib/carray/autoload/autoload_object_pack.rb +0 -2
  251. data/lib/carray/autoload.rb +0 -141
  252. data/lib/carray/basic.rb +0 -191
  253. data/lib/carray/broadcast.rb +0 -101
  254. data/lib/carray/compose.rb +0 -315
  255. data/lib/carray/convert.rb +0 -115
  256. data/lib/carray/info.rb +0 -110
  257. data/lib/carray/io/imagemagick.rb +0 -235
  258. data/lib/carray/mask.rb +0 -102
  259. data/lib/carray/math/histogram.rb +0 -177
  260. data/lib/carray/math/recurrence.rb +0 -93
  261. data/lib/carray/object/ca_obj_iterator.rb +0 -50
  262. data/lib/carray/object/ca_obj_link.rb +0 -50
  263. data/lib/carray/object/ca_obj_pack.rb +0 -99
  264. data/lib/carray/obsolete.rb +0 -256
  265. data/lib/carray/ordering.rb +0 -181
  266. data/lib/carray/testing.rb +0 -51
  267. data/lib/carray/transform.rb +0 -109
  268. data/misc/Methods.ja.md +0 -182
  269. data/misc/NOTE +0 -51
  270. data/spec/Classes/CABitfield_spec.rb +0 -58
  271. data/spec/Classes/CABlockIterator_spec.rb +0 -114
  272. data/spec/Classes/CABlock_spec.rb +0 -205
  273. data/spec/Classes/CAField_spec.rb +0 -39
  274. data/spec/Classes/CAGrid_spec.rb +0 -75
  275. data/spec/Classes/CAMap_spec.rb +0 -0
  276. data/spec/Classes/CAMapping_spec.rb +0 -105
  277. data/spec/Classes/CAObject_attribute_spec.rb +0 -33
  278. data/spec/Classes/CAObject_spec.rb +0 -33
  279. data/spec/Classes/CARefer_spec.rb +0 -93
  280. data/spec/Classes/CARepeat_spec.rb +0 -65
  281. data/spec/Classes/CASelect_spec.rb +0 -22
  282. data/spec/Classes/CAShift_spec.rb +0 -16
  283. data/spec/Classes/CAStruct_spec.rb +0 -71
  284. data/spec/Classes/CATranspose_spec.rb +0 -60
  285. data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
  286. data/spec/Classes/CAWindow_spec.rb +0 -54
  287. data/spec/Classes/CAWrap_spec.rb +0 -8
  288. data/spec/Classes/CArray_spec.rb +0 -184
  289. data/spec/Classes/CScalar_spec.rb +0 -55
  290. data/spec/Classes/ex1.rb +0 -46
  291. data/spec/Features/feature_130_spec.rb +0 -19
  292. data/spec/Features/feature_attributes_spec.rb +0 -280
  293. data/spec/Features/feature_boolean_spec.rb +0 -98
  294. data/spec/Features/feature_broadcast.rb +0 -116
  295. data/spec/Features/feature_cast_function.rb +0 -19
  296. data/spec/Features/feature_cast_spec.rb +0 -33
  297. data/spec/Features/feature_class_spec.rb +0 -84
  298. data/spec/Features/feature_complex_spec.rb +0 -42
  299. data/spec/Features/feature_composite_spec.rb +0 -124
  300. data/spec/Features/feature_convert_spec.rb +0 -46
  301. data/spec/Features/feature_copy_spec.rb +0 -123
  302. data/spec/Features/feature_creation_spec.rb +0 -84
  303. data/spec/Features/feature_element_spec.rb +0 -144
  304. data/spec/Features/feature_extream_spec.rb +0 -54
  305. data/spec/Features/feature_generate_spec.rb +0 -74
  306. data/spec/Features/feature_index_spec.rb +0 -69
  307. data/spec/Features/feature_mask_spec.rb +0 -580
  308. data/spec/Features/feature_math_spec.rb +0 -97
  309. data/spec/Features/feature_order_spec.rb +0 -146
  310. data/spec/Features/feature_ref_store_spec.rb +0 -209
  311. data/spec/Features/feature_serialization_spec.rb +0 -125
  312. data/spec/Features/feature_stat_spec.rb +0 -397
  313. data/spec/Features/feature_virtual_spec.rb +0 -48
  314. data/spec/Features/method_eq_spec.rb +0 -81
  315. data/spec/Features/method_is_nan_spec.rb +0 -12
  316. data/spec/Features/method_map_spec.rb +0 -54
  317. data/spec/Features/method_max_with.rb +0 -20
  318. data/spec/Features/method_min_with.rb +0 -19
  319. data/spec/Features/method_ne_spec.rb +0 -18
  320. data/spec/Features/method_project_spec.rb +0 -188
  321. data/spec/Features/method_ref_spec.rb +0 -27
  322. data/spec/Features/method_round_spec.rb +0 -11
  323. data/spec/Features/method_s_linspace_spec.rb +0 -48
  324. data/spec/Features/method_s_span_spec.rb +0 -14
  325. data/spec/Features/method_seq_spec.rb +0 -47
  326. data/spec/Features/method_sort_with.rb +0 -43
  327. data/spec/Features/method_sorted_with.rb +0 -29
  328. data/spec/Features/method_span_spec.rb +0 -42
  329. data/spec/Features/method_wrap_readonly_spec.rb +0 -43
  330. data/spec/UnitTest/test_CAVirtual.rb +0 -214
  331. data/spec/spec_all.rb +0 -10
  332. data/utils/ca_ase.rb +0 -21
  333. data/utils/ca_methods.rb +0 -15
  334. data/utils/cast_checker.rb +0 -30
  335. data/utils/convert_test.rb +0 -73
  336. data/utils/extract_yard.rb +0 -22
  337. data/utils/guess_shape.rb +0 -76
  338. data/utils/monkey_patch_methods.rb +0 -62
  339. data/utils/remove_resource_fork.sh +0 -5
data/ext/ca_obj_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,21 +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 = {
86
+ .parent = &carray_data_type,
63
87
  .wrap_struct_name = "CAObjectMask",
64
88
  .function = {
65
89
  .dmark = ca_mark,
66
90
  .dfree = ca_free,
67
- .dsize = NULL,
91
+ .dsize = ca_objectmask_dsize,
68
92
  .dcompact = NULL
69
93
  },
70
94
  .flags = RUBY_TYPED_FREE_IMMEDIATELY
@@ -73,16 +97,16 @@ const rb_data_type_t caobjectmask_data_type = {
73
97
  static int8_t CA_OBJ_OBJECT_MASK;
74
98
  VALUE rb_cCAObjectMask;
75
99
 
76
- /* yard:
77
- class CAObjectMask < CAVirtual # :nodoc:
78
- end
79
- */
80
-
81
-
82
100
  static CAObjectMask *
83
101
  ca_objmask_new (VALUE array, int8_t ndim, ca_size_t *dim)
84
102
  {
85
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;
86
110
  ca_wrap_setup_null((CArray *)ca, CA_BOOLEAN, ndim, dim, 0, NULL);
87
111
  ca->obj_type = CA_OBJ_OBJECT_MASK;
88
112
  ca->array = array;
@@ -105,111 +129,99 @@ ca_objmask_func_clone (void *ap)
105
129
  return ca_objmask_new(ca->array, ca->ndim, ca->dim);
106
130
  }
107
131
 
108
- void
109
- 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)
110
139
  {
111
- CAObjectMask *ca = (CAObjectMask *) ap;
112
140
  volatile VALUE ridx, raddr, rval;
113
141
  int i;
114
-
115
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_fetch_addr"), Qtrue) ) {
116
- raddr = SIZE2NUM(addr);
117
- rval = rb_funcall(ca->array, rb_intern("mask_fetch_addr"), 1, raddr);
118
- *(uint8_t*) ptr = NUM2INT(rval) == 0 ? 0 : 1;
119
- ca_array_func_store_addr(ca, addr, ptr);
120
- }
121
- else if ( rb_obj_respond_to(ca->array, rb_intern("mask_fetch_index"), Qtrue) ) {
122
- ca_size_t idx[CA_RANK_MAX];
123
- ca_addr2index(ca, addr, idx);
124
- ridx = rb_ary_new2(ca->ndim);
125
- for (i=0; i<ca->ndim; i++) {
126
- 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);
127
162
  }
128
- rval = rb_funcall(ca->array, rb_intern("mask_fetch_index"), 1, ridx);
129
- *(uint8_t*) ptr = NUM2INT(rval) == 0 ? 0 : 1;
130
- ca_array_func_store_index(ca, idx, ptr);
131
163
  }
132
164
  else {
133
- 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
+ }
134
180
  }
135
181
  }
136
182
 
137
- void
138
- 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)
139
185
  {
140
- CAObjectMask *ca = (CAObjectMask *) ap;
141
186
  volatile VALUE ridx, raddr, rval;
142
187
  int i;
143
-
144
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_fetch_index"), Qtrue) ) {
145
- ridx = rb_ary_new2(ca->ndim);
146
- for (i=0; i<ca->ndim; i++) {
147
- 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);
148
207
  }
149
- rval = rb_funcall(ca->array, rb_intern("mask_fetch_index"), 1, ridx);
150
- *(uint8_t*) ptr = NUM2INT(rval) == 0 ? 0 : 1;
151
- ca_array_func_store_index(ca, idx, ptr);
152
- }
153
- else if ( rb_obj_respond_to(ca->array, rb_intern("mask_fetch_addr"), Qtrue) ) {
154
- ca_size_t addr = ca_index2addr(ca, idx);
155
- raddr = SIZE2NUM(addr);
156
- rval = rb_funcall(ca->array, rb_intern("mask_fetch_addr"), 1, raddr);
157
- *(uint8_t*) ptr = NUM2INT(rval) == 0 ? 0 : 1;
158
- ca_array_func_store_addr(ca, addr, ptr);
159
208
  }
160
209
  else {
161
- ca_array_func_fetch_index(ca, idx, ptr);
162
- }
163
- }
164
-
165
-
166
- void
167
- ca_objmask_func_store_addr (void *ap, ca_size_t addr, void *ptr)
168
- {
169
- CAObjectMask *ca = (CAObjectMask *) ap;
170
- volatile VALUE ridx, raddr, rval;
171
- int i;
172
-
173
- ca_array_func_store_addr(ca, addr, ptr);
174
- rval = INT2NUM( *(uint8_t*)ptr );
175
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_store_addr"), Qtrue) ) {
176
- raddr = SIZE2NUM(addr);
177
- rb_funcall(ca->array, rb_intern("mask_store_addr"), 2, raddr, rval);
178
- }
179
- else if ( rb_obj_respond_to(ca->array, rb_intern("mask_store_index"), Qtrue) ) {
180
- ca_size_t idx[CA_RANK_MAX];
181
- ca_addr2index(ca, addr, idx);
182
- ridx = rb_ary_new2(ca->ndim);
183
- for (i=0; i<ca->ndim; i++) {
184
- 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);
185
218
  }
186
- rb_funcall(ca->array, rb_intern("mask_store_index"), 2, ridx, rval);
187
- }
188
- }
189
-
190
- void
191
- ca_objmask_func_store_index (void *ap, ca_size_t *idx, void *ptr)
192
- {
193
- CAObjectMask *ca = (CAObjectMask *) ap;
194
- volatile VALUE ridx, raddr, rval;
195
- int i;
196
-
197
- ca_array_func_store_index(ca, idx, ptr);
198
- rval = INT2NUM( *(uint8_t*)ptr );
199
-
200
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_store_index"), Qtrue) ) {
201
- ridx = rb_ary_new2(ca->ndim);
202
- for (i=0; i<ca->ndim; i++) {
203
- 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);
204
223
  }
205
- rb_funcall(ca->array, rb_intern("mask_store_index"), 2, ridx, rval);
206
- }
207
- else if ( rb_obj_respond_to(ca->array, rb_intern("mask_store_addr"), Qtrue) ) {
208
- ca_size_t addr = ca_index2addr(ca, idx);
209
- raddr = SIZE2NUM(addr);
210
- rb_funcall(ca->array, rb_intern("mask_store_addr"), 2, raddr, rval);
211
224
  }
212
-
213
225
  }
214
226
 
215
227
  static void
@@ -233,46 +245,146 @@ ca_objmask_func_sync (void *ap)
233
245
  }
234
246
 
235
247
  static void
236
- ca_objmask_func_copy_data (void *ap, void *ptr)
248
+ ca_objmask_func_xfer_all (void *ap, void *data, int dir)
237
249
  {
238
250
  CAObjectMask *ca = (CAObjectMask *) ap;
239
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_copy_data"), Qtrue) ) {
240
- char *ptr0 = ca->ptr;
241
- ca->ptr = ptr;
242
- rb_funcall(ca->array, rb_intern("mask_copy_data"),
243
- 1, ca_objmask_mask_data(ca));
244
- 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
+ }
245
262
  }
246
263
  else {
247
- 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));
248
285
  }
249
286
  }
250
287
 
251
288
  static void
252
- 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)
253
305
  {
254
306
  CAObjectMask *ca = (CAObjectMask *) ap;
255
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_copy_data"), Qtrue) ) {
256
- char *ptr0 = ca->ptr;
257
- ca->ptr = ptr;
258
- rb_funcall(ca->array, rb_intern("mask_sync_data"),
259
- 1, ca_objmask_mask_data(ca));
260
- 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;
261
319
  }
262
- else {
263
- 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
+ }
264
327
  }
265
328
  }
266
329
 
267
- void
268
- 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)
269
337
  {
270
338
  CAObjectMask *ca = (CAObjectMask *) ap;
271
- ca_array_func_fill_data(ca, val);
272
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_fill_data"), Qtrue) ) {
273
- rb_funcall(ca->array, rb_intern("mask_fill_data"),
274
- 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
+ }
275
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--; }
385
+ }
386
+ ca_objmask_func_xfer_addrs(ca, n, vaddrs, data, dir);
387
+ ALLOCV_END(holder);
276
388
  }
277
389
 
278
390
  static ca_operation_function_t ca_objmask_func = {
@@ -280,27 +392,24 @@ static ca_operation_function_t ca_objmask_func = {
280
392
  CA_REAL_ARRAY,
281
393
  free_ca_wrap,
282
394
  ca_objmask_func_clone,
283
- ca_array_func_ptr_at_addr,
284
- ca_array_func_ptr_at_index,
285
- ca_objmask_func_fetch_addr,
286
- ca_objmask_func_fetch_index,
287
- ca_objmask_func_store_addr,
288
- ca_objmask_func_store_index,
289
395
  ca_array_func_allocate,
290
396
  ca_objmask_func_attach,
291
397
  ca_objmask_func_sync,
292
398
  ca_array_func_detach,
293
- ca_objmask_func_copy_data,
294
- ca_objmask_func_sync_data,
295
399
  ca_objmask_func_fill_data,
296
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,
297
406
  };
298
407
 
299
408
  static VALUE
300
409
  rb_ca_objmask_s_allocate (VALUE klass)
301
410
  {
302
411
  CAObjectMask *ca;
303
- return TypedData_Make_Struct(klass, CAObjectMask, &caobject_data_type, ca);
412
+ return TypedData_Make_Struct(klass, CAObjectMask, &caobjectmask_data_type, ca);
304
413
  }
305
414
 
306
415
  static VALUE
@@ -360,7 +469,9 @@ ca_object_setup (CAObject *ca,
360
469
  ca->attach = 0;
361
470
  ca->nosync = 0;
362
471
 
363
- ca->dim = ALLOC_N(ca_size_t, ndim);
472
+ if ( ! ca->_pool ) {
473
+ ca->dim = ALLOC_N(ca_size_t, ndim);
474
+ }
364
475
 
365
476
  ca->data = ca_wrap_new_null(data_type, ndim, dim, bytes, NULL);
366
477
 
@@ -372,7 +483,7 @@ ca_object_setup (CAObject *ca,
372
483
  static CAObject *
373
484
  ca_object_new (int8_t data_type, int8_t ndim, ca_size_t *dim, ca_size_t bytes)
374
485
  {
375
- CAObject *ca = ALLOC(CAObject);
486
+ CAObject *ca = (CAObject *) ca_array_alloc(CA_OBJ_OBJECT, ndim);
376
487
  ca_object_setup(ca, data_type, ndim, dim, bytes);
377
488
  return ca;
378
489
  }
@@ -383,8 +494,13 @@ free_ca_object (void *ap)
383
494
  CAObject *ca = (CAObject *) ap;
384
495
  if ( ca != NULL ) {
385
496
  /* ca->mask will be GC-ed by Ruby interpreter */
386
- xfree(ca->dim);
387
- 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
+ }
388
504
  }
389
505
  }
390
506
 
@@ -394,186 +510,252 @@ static void *
394
510
  ca_object_func_clone (void *ap)
395
511
  {
396
512
  CAObject *ca = (CAObject *) ap;
397
- 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);
398
514
  }
399
515
 
400
516
  #define ca_object_func_ptr_at_addr ca_array_func_ptr_at_addr
401
517
  #define ca_object_func_ptr_at_index ca_array_func_ptr_at_index
402
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. */
403
522
  static void
404
- 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)
405
524
  {
406
- CAObject *ca = (CAObject *) ap;
407
525
  volatile VALUE ridx, raddr, rval;
408
526
  int i;
409
- if ( rb_obj_respond_to(ca->self, rb_intern("fetch_addr"), Qtrue) ) {
410
- raddr = SIZE2NUM(addr);
411
- rval = rb_funcall(ca->self, rb_intern("fetch_addr"), 1, raddr);
412
- if ( rval == CA_UNDEF ) {
413
- ca_update_mask(ca);
414
- if ( ! ca->mask ) {
415
- 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
+ }
416
540
  }
417
- *(boolean8_t*)ca_ptr_at_addr(ca->mask, addr) = 1;
418
- if ( ca->data_type == CA_OBJECT ) {
419
- 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);
420
546
  }
421
547
  }
422
548
  else {
423
- if ( ca_has_mask(ca) ) {
424
- *(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);
425
571
  }
426
- rb_ca_obj2ptr(ca->self, rval, ptr);
427
572
  }
428
573
  }
429
574
  else {
430
- ca_size_t idx[CA_RANK_MAX];
431
- ca_addr2index(ca, addr, idx);
432
- ridx = rb_ary_new2(ca->ndim);
433
- for (i=0; i<ca->ndim; i++) {
434
- rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
435
- }
436
- rval = rb_funcall(ca->self, rb_intern("fetch_index"), 1, ridx);
437
- if ( rval == CA_UNDEF ) {
438
- ca_update_mask(ca);
439
- if ( ! ca->mask ) {
440
- ca_create_mask(ca);
441
- }
442
- *(boolean8_t*)ca_ptr_at_index(ca->mask, idx) = 1;
443
- if ( ca->data_type == CA_OBJECT ) {
444
- rb_ca_obj2ptr(ca->self, INT2NUM(0), ptr);
445
- }
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);
446
579
  }
447
580
  else {
448
- if ( ca_has_mask(ca) ) {
449
- *(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]));
450
586
  }
451
- 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);
452
589
  }
453
590
  }
454
591
  }
455
592
 
456
593
  static void
457
- 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)
458
595
  {
459
- CAObject *ca = (CAObject *) ap;
460
596
  volatile VALUE ridx, raddr, rval;
461
597
  int i;
462
- if ( rb_obj_respond_to(ca->self, rb_intern("fetch_index"), Qtrue) ) {
463
- ridx = rb_ary_new2(ca->ndim);
464
- for (i=0; i<ca->ndim; i++) {
465
- rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
466
- }
467
- rval = rb_funcall(ca->self, rb_intern("fetch_index"), 1, ridx);
468
- if ( rval == CA_UNDEF ) {
469
- ca_update_mask(ca);
470
- if ( ! ca->mask ) {
471
- 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]));
603
+ }
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
+ }
472
614
  }
473
- *(boolean8_t*)ca_ptr_at_index(ca->mask, idx) = 1;
474
- if ( ca->data_type == CA_OBJECT ) {
475
- rb_ca_obj2ptr(ca->self, INT2NUM(0), ptr);
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);
476
620
  }
477
621
  }
478
622
  else {
479
- if ( ca_has_mask(ca) ) {
480
- *(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);
481
641
  }
482
- rb_ca_obj2ptr(ca->self, rval, ptr);
483
642
  }
484
643
  }
485
644
  else {
486
- ca_size_t addr = ca_index2addr(ca, idx);
487
- raddr = SIZE2NUM(addr);
488
- rval = rb_funcall(ca->self, rb_intern("fetch_addr"), 1, raddr);
489
- if ( rval == CA_UNDEF ) {
490
- ca_update_mask(ca);
491
- if ( ! ca->mask ) {
492
- ca_create_mask(ca);
493
- }
494
- *(boolean8_t*)ca_ptr_at_addr(ca->mask, addr) = 1;
495
- if ( ca->data_type == CA_OBJECT ) {
496
- 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]));
497
649
  }
650
+ rval = rb_ca_ptr2obj(ca->self, ptr);
651
+ rb_funcall(ca->self, rb_intern("store_index"), 2, ridx, rval);
498
652
  }
499
653
  else {
500
- if ( ca_has_mask(ca) ) {
501
- *(boolean8_t*)ca_ptr_at_addr(ca->mask, addr) = 0;
502
- }
503
- 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);
504
658
  }
505
659
  }
506
660
  }
507
661
 
508
662
  static void
509
- ca_object_func_store_addr (void *ap, ca_size_t addr, void *ptr)
663
+ ca_object_func_allocate (void *ap)
510
664
  {
511
665
  CAObject *ca = (CAObject *) ap;
512
- volatile VALUE ridx, raddr, rval;
513
- int i;
514
- if ( rb_obj_respond_to(ca->self, rb_intern("store_addr"), Qtrue) ) {
515
- raddr = SIZE2NUM(addr);
516
- rval = rb_ca_ptr2obj(ca->self, ptr);
517
- 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;
518
702
  }
519
703
  else {
520
- ca_size_t idx[CA_RANK_MAX];
521
- ca_addr2index(ca, addr, idx);
522
- ridx = rb_ary_new2(ca->ndim);
523
- for (i=0; i<ca->ndim; i++) {
524
- 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);
525
708
  }
526
- rval = rb_ca_ptr2obj(ca->self, ptr);
527
- rb_funcall(ca->self, rb_intern("store_index"), 2, ridx, rval);
528
709
  }
529
710
  }
530
711
 
531
712
  static void
532
- ca_object_func_store_index (void *ap, ca_size_t *idx, void *ptr)
713
+ ca_object_dispatch_sync (CAObject *ca, void *ptr)
533
714
  {
534
- CAObject *ca = (CAObject *) ap;
535
- volatile VALUE ridx, raddr, rval;
536
- int i;
537
- if ( rb_obj_respond_to(ca->self, rb_intern("store_index"), Qtrue) ) {
538
- ridx = rb_ary_new2(ca->ndim);
539
- for (i=0; i<ca->ndim; i++) {
540
- rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
541
- }
542
- rval = rb_ca_ptr2obj(ca->self, ptr);
543
- 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;
544
721
  }
545
722
  else {
546
- ca_size_t addr = ca_index2addr(ca, idx);
547
- raddr = SIZE2NUM(addr);
548
- rval = rb_ca_ptr2obj(ca->self, ptr);
549
- 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
+ }
550
728
  }
551
729
  }
552
730
 
553
731
  static void
554
- ca_object_func_allocate (void *ap)
732
+ ca_object_dispatch_fill (CAObject *ca, void *ptr)
555
733
  {
556
- CAObject *ca = (CAObject *) ap;
557
- /* ca->data->ptr = ALLOC_N(char, ca_length(ca)); */
558
- ca->data->ptr = malloc_with_check(ca_length(ca));
559
- if ( ca_is_object_type(ca->data) ) { /* GC safe */
560
- VALUE *p = (VALUE *) ca->data->ptr;
561
- VALUE zero = INT2NUM(0);
562
- ca_size_t i;
563
- for (i=0; i<ca->elements; i++) {
564
- *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);
565
742
  }
566
743
  }
567
- ca->ptr = ca->data->ptr;
568
744
  }
569
745
 
570
746
  static void
571
747
  ca_object_func_attach (void *ap)
572
748
  {
573
749
  CAObject *ca = (CAObject *) ap;
574
- 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
+ }
575
757
  /* ca->data->ptr = ALLOC_N(char, ca_length(ca)); */
576
- ca->data->ptr = malloc_with_check(ca_length(ca));
758
+ ca->data->ptr = xmalloc(ca_length(ca));
577
759
  if ( ca_is_object_type(ca->data) ) { /* GC safe */
578
760
  VALUE *p = (VALUE *) ca->data->ptr;
579
761
  VALUE zero = INT2NUM(0);
@@ -582,8 +764,8 @@ ca_object_func_attach (void *ap)
582
764
  *p++ = zero;
583
765
  }
584
766
  }
585
- ca->ptr = ca->data->ptr;
586
- rb_funcall(ca->self, rb_intern("copy_data"), 1, data);
767
+ ca->ptr = ca->data->ptr;
768
+ ca_object_dispatch_copy(ca, ca->ptr);
587
769
  if ( ca_has_mask(ca->data) ) {
588
770
  ca_update_mask(ca);
589
771
  if ( ! ca->mask ) {
@@ -596,48 +778,227 @@ static void
596
778
  ca_object_func_sync (void *ap)
597
779
  {
598
780
  CAObject *ca = (CAObject *) ap;
599
- rb_funcall(ca->self, rb_intern("sync_data"),
600
- 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);
601
786
  }
602
787
 
603
788
  static void
604
789
  ca_object_func_detach (void *ap)
605
790
  {
606
791
  CAObject *ca = (CAObject *) ap;
607
- free(ca->data->ptr);
792
+ if ( ca_is_face(ca) ) {
793
+ ca_face_detach(ap);
794
+ return;
795
+ }
796
+ xfree(ca->data->ptr);
608
797
  ca->data->ptr = NULL;
609
798
  ca->ptr = NULL;
610
799
  }
611
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
+
612
839
  static void
613
- 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)
614
841
  {
615
- CAObject *ca = (CAObject *) ap;
616
- char *ptr0 = ca->data->ptr;
617
- ca->data->ptr = ptr;
618
- rb_funcall(ca->self, rb_intern("copy_data"),
619
- 1, rb_ivar_get(ca->self, rb_intern("__data__")));
620
- 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);
621
847
  }
622
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. */
623
852
  static void
624
- 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)
625
855
  {
626
- CAObject *ca = (CAObject *) ap;
627
- char *ptr0 = ca->data->ptr;
628
- ca->data->ptr = ptr;
629
- rb_funcall(ca->self, rb_intern("sync_data"),
630
- 1, rb_ivar_get(ca->self, rb_intern("__data__")));
631
- 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
+ }
632
992
  }
633
993
 
634
994
  static void
635
995
  ca_object_func_fill_data (void *ap, void *ptr)
636
996
  {
637
- CAObject *ca = (CAObject *) ap;
638
- volatile VALUE rval;
639
- rval = rb_ca_ptr2obj(ca->self, ptr);
640
- 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);
641
1002
  }
642
1003
 
643
1004
  static void
@@ -645,6 +1006,18 @@ ca_object_func_create_mask (void *ap)
645
1006
  {
646
1007
  CAObject *ca = (CAObject *) ap;
647
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
+ }
648
1021
  if ( rb_obj_respond_to(ca->self, rb_intern("create_mask"), Qtrue) ) {
649
1022
  rb_funcall(ca->self, rb_intern("create_mask"), 0);
650
1023
  }
@@ -663,23 +1036,20 @@ ca_object_func_create_mask (void *ap)
663
1036
 
664
1037
  ca_operation_function_t ca_object_func = {
665
1038
  -1, /* CA_OBJ_OBJECT */
666
- CA_VIRTUAL_ARRAY,
1039
+ CA_VIEW_ARRAY,
667
1040
  free_ca_object,
668
1041
  ca_object_func_clone,
669
- ca_object_func_ptr_at_addr,
670
- ca_object_func_ptr_at_index,
671
- ca_object_func_fetch_addr,
672
- ca_object_func_fetch_index,
673
- ca_object_func_store_addr,
674
- ca_object_func_store_index,
675
1042
  ca_object_func_allocate,
676
1043
  ca_object_func_attach,
677
1044
  ca_object_func_sync,
678
1045
  ca_object_func_detach,
679
- ca_object_func_copy_data,
680
- ca_object_func_sync_data,
681
1046
  ca_object_func_fill_data,
682
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,
683
1053
  };
684
1054
 
685
1055
  /* ------------------------------------------------------------------- */
@@ -688,7 +1058,7 @@ static VALUE
688
1058
  rb_ca_object_s_allocate (VALUE klass)
689
1059
  {
690
1060
  CAObject *ca;
691
- return Data_Make_Struct(klass, CAObject, ca_mark, ca_free, ca);
1061
+ return TypedData_Make_Struct(klass, CAObject, &caobject_data_type, ca);
692
1062
  }
693
1063
 
694
1064
  static VALUE
@@ -700,13 +1070,28 @@ rb_ca_object_initialize_copy (VALUE self, VALUE other)
700
1070
  TypedData_Get_Struct(self, CAObject, &caobject_data_type, ca);
701
1071
  TypedData_Get_Struct(other, CAObject, &caobject_data_type, cs);
702
1072
 
1073
+ if ( ca_func[CA_OBJ_OBJECT].pool_init ) {
1074
+ ca_array_pool_alloc(ca, CA_OBJ_OBJECT, cs->ndim);
1075
+ }
703
1076
  ca_object_setup(ca, cs->data_type, cs->ndim, cs->dim, cs->bytes);
704
1077
  ca->self = self;
705
1078
 
706
- 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
+
707
1093
 
708
1094
  data = ca_wrap_struct(ca->data);
709
- rb_ca_data_type_inherit(data, self);
710
1095
  rb_ivar_set(self, rb_intern("__data__"), data);
711
1096
 
712
1097
  ca_update_mask(cs);
@@ -722,7 +1107,9 @@ rb_ca_object_initialize_copy (VALUE self, VALUE other)
722
1107
  static VALUE
723
1108
  rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
724
1109
  {
725
- 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;
726
1113
  CAObject *ca;
727
1114
  int8_t data_type, ndim;
728
1115
  ca_size_t dim[CA_RANK_MAX];
@@ -730,15 +1117,47 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
730
1117
  int i;
731
1118
 
732
1119
  rb_scan_args(argc, argv, "21", (VALUE *) &rtype, (VALUE *) &rdim, (VALUE *) &ropt);
733
- rb_scan_options(ropt, "bytes,read_only,parent",
734
- &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);
735
1123
 
736
- if ( ( ! NIL_P(rparent) ) && rb_obj_is_carray(rparent) ) {
1124
+ if ( ( ! NIL_P(rparent) ) && ( ! rb_obj_is_carray(rparent) ) ) {
737
1125
  rb_raise(rb_eRuntimeError, "option :parent should be a carray");
738
1126
  }
739
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
+
740
1160
  rb_ca_guess_type_and_bytes(rtype, rbytes, &data_type, &bytes);
741
- rb_ca_data_type_import(self, rtype);
742
1161
 
743
1162
  Check_Type(rdim, T_ARRAY);
744
1163
 
@@ -752,7 +1171,6 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
752
1171
  ca->self = self;
753
1172
 
754
1173
  rdata = ca_wrap_struct(ca->data);
755
- rb_ca_data_type_inherit(rdata, self);
756
1174
 
757
1175
  rb_ivar_set(self, rb_intern("__data__"), rdata);
758
1176
 
@@ -765,22 +1183,72 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
765
1183
  TypedData_Get_Struct(rparent, CArray, &carray_data_type, cp);
766
1184
  ca->parent = cp;
767
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
+ }
768
1236
  }
769
-
1237
+
770
1238
  ca_update_mask(ca);
771
1239
 
772
1240
  return Qnil;
773
1241
  }
774
1242
 
775
1243
  void
776
- Init_ca_obj_object ()
1244
+ Init_ca_obj_object (void)
777
1245
  {
778
1246
  rb_cCAObjectMask = rb_define_class("CAObjectMask", rb_cCArray);
779
1247
 
780
1248
  CA_OBJ_OBJECT_MASK = ca_install_obj_type(rb_cCAObjectMask,
781
1249
  &caobjectmask_data_type,
782
1250
  rb_cCArrayMask,
783
- &carray_data_type, ca_objmask_func);
1251
+ &carray_data_type, &ca_objmask_func, sizeof(ca_objmask_func));
784
1252
  rb_define_const(rb_cObject, "CA_OBJ_OBJECT_MASK", INT2NUM(CA_OBJ_OBJECT_MASK));
785
1253
 
786
1254
  rb_define_alloc_func(rb_cCAObjectMask, rb_ca_objmask_s_allocate);
@@ -790,6 +1258,14 @@ Init_ca_obj_object ()
790
1258
 
791
1259
  rb_define_const(rb_cObject, "CA_OBJ_OBJECT", INT2NUM(CA_OBJ_OBJECT));
792
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
+
793
1269
  rb_define_alloc_func(rb_cCAObject, rb_ca_object_s_allocate);
794
1270
  rb_define_method(rb_cCAObject, "initialize_copy",
795
1271
  rb_ca_object_initialize_copy, 1);