carray 2.0.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (386) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +6 -25
  3. data/CHANGELOG.md +338 -0
  4. data/{NEWS.md → CHANGELOG.v1.md} +3 -0
  5. data/LICENSE +1 -1
  6. data/README.md +120 -36
  7. data/carray.gemspec +32 -30
  8. data/ext/ca_array_pool.c +91 -0
  9. data/ext/ca_axis_descriptor.h +186 -0
  10. data/ext/ca_axis_dispatch.c +924 -0
  11. data/ext/ca_axis_group.c +1244 -0
  12. data/ext/ca_bincmp_dispatch.c +76 -0
  13. data/ext/ca_bincmp_dispatch.h +85 -0
  14. data/ext/ca_binop_dispatch.c +124 -0
  15. data/ext/ca_binop_dispatch.h +152 -0
  16. data/ext/ca_categorical_iterator.c +1375 -0
  17. data/ext/ca_compare.c +94 -0
  18. data/ext/ca_compare.h +26 -0
  19. data/ext/ca_composite_dispatch.c +414 -0
  20. data/ext/ca_composite_dispatch.h +116 -0
  21. data/ext/ca_for_buffer.h +96 -0
  22. data/ext/ca_for_each_element.h +239 -0
  23. data/ext/ca_group_iter.c +304 -0
  24. data/ext/ca_iter_substrate.h +325 -0
  25. data/ext/ca_kernel_iterator.c +4367 -0
  26. data/ext/ca_kernel_iterator.h +2596 -0
  27. data/ext/ca_moncmp_dispatch.c +37 -0
  28. data/ext/ca_moncmp_dispatch.h +62 -0
  29. data/ext/ca_monop_dispatch.c +200 -0
  30. data/ext/ca_monop_dispatch.h +235 -0
  31. data/ext/ca_obj_array.c +355 -359
  32. data/ext/ca_obj_bincmp.c +839 -0
  33. data/ext/ca_obj_binop.c +948 -0
  34. data/ext/ca_obj_bitarray.c +369 -164
  35. data/ext/ca_obj_bitfield.c +294 -234
  36. data/ext/ca_obj_block.c +189 -711
  37. data/ext/ca_obj_byte_swap.c +766 -0
  38. data/ext/ca_obj_const_string.c +967 -0
  39. data/ext/ca_obj_face.c +750 -0
  40. data/ext/ca_obj_face.h +279 -0
  41. data/ext/ca_obj_fake.c +239 -100
  42. data/ext/ca_obj_farray.c +54 -441
  43. data/ext/ca_obj_field.c +82 -529
  44. data/ext/ca_obj_fixlen_string.c +308 -0
  45. data/ext/ca_obj_grid.c +866 -440
  46. data/ext/ca_obj_meld.c +1039 -0
  47. data/ext/ca_obj_moncmp.c +588 -0
  48. data/ext/ca_obj_monop.c +1123 -0
  49. data/ext/ca_obj_object.c +866 -296
  50. data/ext/ca_obj_record.c +470 -0
  51. data/ext/ca_obj_reduce.c +97 -82
  52. data/ext/ca_obj_refer.c +593 -459
  53. data/ext/ca_obj_remap.c +475 -0
  54. data/ext/ca_obj_repeat.c +92 -477
  55. data/ext/ca_obj_roll.c +624 -0
  56. data/ext/ca_obj_select.c +344 -296
  57. data/ext/ca_obj_select_axis.c +1306 -0
  58. data/ext/ca_obj_shift.c +231 -793
  59. data/ext/ca_obj_source.c +78 -0
  60. data/ext/ca_obj_stack.c +1173 -0
  61. data/ext/ca_obj_stride.c +2584 -0
  62. data/ext/ca_obj_string.c +270 -0
  63. data/ext/ca_obj_tile.c +622 -0
  64. data/ext/ca_obj_time.c +548 -0
  65. data/ext/ca_obj_timedelta.c +437 -0
  66. data/ext/ca_obj_transpose.c +62 -516
  67. data/ext/ca_obj_triop.c +785 -0
  68. data/ext/ca_obj_window.c +1202 -565
  69. data/ext/ca_op_byte_swap.c +175 -0
  70. data/ext/ca_op_cmplx64.h +123 -0
  71. data/ext/ca_op_ipower.c +316 -0
  72. data/ext/ca_op_powi.h +88 -0
  73. data/ext/ca_sort_kernels.h +132 -0
  74. data/ext/ca_sweep_engine.c +473 -0
  75. data/ext/ca_sweep_engine.h +166 -0
  76. data/ext/ca_transform_common.c +235 -0
  77. data/ext/ca_triop_dispatch.c +55 -0
  78. data/ext/ca_triop_dispatch.h +62 -0
  79. data/ext/carray.h +810 -420
  80. data/ext/carray_access.c +873 -731
  81. data/ext/carray_attribute.c +98 -329
  82. data/ext/carray_bincount.c +255 -0
  83. data/ext/carray_broadcast.c +376 -0
  84. data/ext/carray_build_flags.h +3 -0
  85. data/ext/carray_call_cfunc.c +2897 -874
  86. data/ext/carray_call_cfunc.h +313 -0
  87. data/ext/carray_cast.c +1264 -315
  88. data/ext/carray_cast_func.rb +81 -40
  89. data/ext/carray_class.c +53 -63
  90. data/ext/carray_config.h +28 -0
  91. data/ext/carray_conversion.c +350 -346
  92. data/ext/carray_copy.c +168 -270
  93. data/ext/carray_core.c +1396 -206
  94. data/ext/carray_count.c +312 -0
  95. data/ext/carray_data_type.c +43 -19
  96. data/ext/carray_element.c +585 -213
  97. data/ext/carray_factorize.c +2542 -0
  98. data/ext/carray_generate.c +230 -559
  99. data/ext/carray_histogram.c +490 -0
  100. data/ext/carray_hold.c +228 -0
  101. data/ext/carray_index_classifier.c +1021 -0
  102. data/ext/carray_index_classifier.h +27 -0
  103. data/ext/carray_internal.h +136 -0
  104. data/ext/carray_kernels_bincmp.c +4446 -0
  105. data/ext/carray_kernels_binop.c +11001 -0
  106. data/ext/carray_kernels_init.c +1131 -0
  107. data/ext/carray_kernels_map.c +3467 -0
  108. data/ext/carray_kernels_moncmp.c +2097 -0
  109. data/ext/carray_kernels_monop.c +18313 -0
  110. data/ext/carray_kernels_reduce_aggregate.c +25837 -0
  111. data/ext/carray_kernels_reduce_boolean.c +330 -0
  112. data/ext/carray_kernels_reduce_cumulative.c +14593 -0
  113. data/ext/carray_kernels_reduce_extreme.c +16948 -0
  114. data/ext/carray_kernels_reduce_variance.c +3910 -0
  115. data/ext/carray_kernels_scan.c +3693 -0
  116. data/ext/carray_kernels_search.c +32138 -0
  117. data/ext/carray_kernels_sort.c +10626 -0
  118. data/ext/carray_kernels_triop.c +1392 -0
  119. data/ext/carray_lazy.c +737 -0
  120. data/ext/carray_loop.c +88 -200
  121. data/ext/carray_mask.c +853 -158
  122. data/ext/carray_math_kernel.h +120 -0
  123. data/ext/carray_mathfunc.c +10 -241
  124. data/ext/carray_median_percentile.c +1257 -0
  125. data/ext/carray_memory_view.c +1650 -0
  126. data/ext/carray_operator.c +1525 -320
  127. data/ext/carray_order.c +664 -1394
  128. data/ext/carray_partition.c +416 -0
  129. data/ext/carray_random.c +518 -0
  130. data/ext/carray_scatter.c +357 -0
  131. data/ext/carray_slab.c +1219 -0
  132. data/ext/carray_slab.h +84 -0
  133. data/ext/carray_sort.c +829 -0
  134. data/ext/carray_sort_kernel.c +620 -0
  135. data/ext/carray_struct.c +695 -0
  136. data/ext/carray_test.c +343 -229
  137. data/ext/carray_undef.c +34 -17
  138. data/ext/carray_utils.c +175 -74
  139. data/ext/extconf.rb +234 -55
  140. data/ext/mk_call_cfunc.rb +671 -0
  141. data/ext/mkkernel.rb +9096 -0
  142. data/ext/ruby_carray.c +211 -108
  143. data/ext/version.h +4 -14
  144. data/ext/version.rb +5 -13
  145. data/lib/carray/arrow_tensor.rb +401 -0
  146. data/lib/carray/attribute.rb +166 -0
  147. data/lib/carray/autoload_carray.rb +239 -0
  148. data/lib/carray/autoload_method_extension.rb +45 -0
  149. data/lib/carray/axis_group.rb +711 -0
  150. data/lib/carray/basics.rb +481 -0
  151. data/lib/carray/bincount_nd.rb +358 -0
  152. data/lib/carray/block_iterator.rb +614 -0
  153. data/lib/carray/boolean_reduce.rb +109 -0
  154. data/lib/carray/categorical.rb +561 -0
  155. data/lib/carray/categorical_iterator.rb +1084 -0
  156. data/lib/carray/complex.rb +150 -0
  157. data/lib/carray/conditional.rb +216 -0
  158. data/lib/carray/const_string.rb +228 -0
  159. data/lib/carray/construct.rb +160 -328
  160. data/lib/carray/core_extensions.rb +297 -0
  161. data/lib/carray/data_type_extension.rb +250 -0
  162. data/lib/carray/fixlen_string.rb +95 -0
  163. data/lib/carray/frame/concat.rb +132 -0
  164. data/lib/carray/frame/convert.rb +95 -0
  165. data/lib/carray/frame/csv_parser.rb +211 -0
  166. data/lib/carray/frame/frame.rb +642 -0
  167. data/lib/carray/frame/group.rb +186 -0
  168. data/lib/carray/frame/io.rb +321 -0
  169. data/lib/carray/frame/join.rb +248 -0
  170. data/lib/carray/frame/records.rb +99 -0
  171. data/lib/carray/frame/sort.rb +113 -0
  172. data/lib/carray/frame/verbs.rb +316 -0
  173. data/lib/carray/frame.rb +16 -0
  174. data/lib/carray/fuse_source.rb +123 -0
  175. data/lib/carray/fusion.rb +218 -0
  176. data/lib/carray/histogram.rb +512 -0
  177. data/lib/carray/inspect.rb +37 -26
  178. data/lib/carray/iterator.rb +58 -349
  179. data/lib/carray/lazy.rb +941 -0
  180. data/lib/carray/mask_gap_fill.rb +200 -0
  181. data/lib/carray/math.rb +78 -342
  182. data/lib/carray/meld_reduce.rb +289 -0
  183. data/lib/carray/methods/align_addr.rb +116 -0
  184. data/lib/carray/methods/bin.rb +128 -0
  185. data/lib/carray/methods/bincount.rb +87 -0
  186. data/lib/carray/methods/bit_string.rb +92 -0
  187. data/lib/carray/methods/broadcast.rb +63 -0
  188. data/lib/carray/methods/choose.rb +39 -0
  189. data/lib/carray/methods/composition.rb +280 -0
  190. data/lib/carray/methods/gather_nd.rb +206 -0
  191. data/lib/carray/methods/index.rb +39 -0
  192. data/lib/carray/methods/insert_block.rb +99 -0
  193. data/lib/carray/methods/is_in.rb +141 -0
  194. data/lib/carray/methods/join.rb +90 -0
  195. data/lib/carray/methods/locate_addr.rb +52 -0
  196. data/lib/carray/methods/mask_duplicates.rb +41 -0
  197. data/lib/carray/methods/meshgrid.rb +90 -0
  198. data/lib/carray/methods/mode.rb +126 -0
  199. data/lib/carray/methods/nunique.rb +46 -0
  200. data/lib/carray/methods/resize.rb +56 -0
  201. data/lib/carray/methods/snap.rb +161 -0
  202. data/lib/carray/methods/string_format.rb +57 -0
  203. data/lib/carray/methods/unique.rb +47 -0
  204. data/lib/carray/methods/value_counts.rb +71 -0
  205. data/lib/carray/mkmf.rb +124 -101
  206. data/lib/carray/runtime.rb +89 -0
  207. data/lib/carray/serialize.rb +478 -167
  208. data/lib/carray/slab_iterator.rb +305 -0
  209. data/lib/carray/stack.rb +291 -0
  210. data/lib/carray/string.rb +56 -180
  211. data/lib/carray/string_operation_extension.rb +289 -0
  212. data/lib/carray/struct.rb +335 -323
  213. data/lib/carray/struct_builder.rb +697 -0
  214. data/lib/carray/table.rb +41 -2
  215. data/lib/carray/time.rb +2654 -38
  216. data/lib/carray/window_iterator.rb +927 -0
  217. data/lib/carray.rb +55 -57
  218. data/yard-stubs/ca_obj_array.rb +385 -0
  219. data/yard-stubs/ca_obj_bitarray.rb +38 -0
  220. data/yard-stubs/ca_obj_bitfield.rb +43 -0
  221. data/yard-stubs/ca_obj_block.rb +73 -0
  222. data/yard-stubs/ca_obj_byte_swap.rb +56 -0
  223. data/yard-stubs/ca_obj_fake.rb +31 -0
  224. data/yard-stubs/ca_obj_farray.rb +32 -0
  225. data/yard-stubs/ca_obj_field.rb +45 -0
  226. data/yard-stubs/ca_obj_grid.rb +35 -0
  227. data/yard-stubs/ca_obj_refer.rb +72 -0
  228. data/yard-stubs/ca_obj_roll.rb +45 -0
  229. data/yard-stubs/ca_obj_shift.rb +43 -0
  230. data/yard-stubs/ca_obj_stride.rb +181 -0
  231. data/yard-stubs/ca_obj_tile.rb +29 -0
  232. data/yard-stubs/ca_obj_transpose.rb +40 -0
  233. data/yard-stubs/ca_obj_window.rb +49 -0
  234. data/yard-stubs/carray_access.rb +131 -0
  235. data/yard-stubs/carray_attribute.rb +246 -0
  236. data/yard-stubs/carray_broadcast.rb +37 -0
  237. data/yard-stubs/carray_cast.rb +489 -0
  238. data/yard-stubs/carray_class.rb +65 -0
  239. data/yard-stubs/carray_conversion.rb +76 -0
  240. data/yard-stubs/carray_copy.rb +79 -0
  241. data/yard-stubs/carray_core.rb +114 -0
  242. data/yard-stubs/carray_count.rb +79 -0
  243. data/yard-stubs/carray_element.rb +108 -0
  244. data/yard-stubs/carray_generate.rb +66 -0
  245. data/yard-stubs/carray_lazy.rb +23 -0
  246. data/yard-stubs/carray_loop.rb +140 -0
  247. data/yard-stubs/carray_mask.rb +259 -0
  248. data/yard-stubs/carray_math.rb +132 -0
  249. data/yard-stubs/carray_mathfunc.rb +45 -0
  250. data/yard-stubs/carray_median_percentile.rb +89 -0
  251. data/yard-stubs/carray_memory_view.rb +163 -0
  252. data/yard-stubs/carray_order.rb +312 -0
  253. data/yard-stubs/carray_random.rb +89 -0
  254. data/yard-stubs/carray_scatter.rb +106 -0
  255. data/yard-stubs/carray_slab.rb +57 -0
  256. data/yard-stubs/carray_sort.rb +163 -0
  257. data/yard-stubs/carray_test.rb +85 -0
  258. data/yard-stubs/carray_undef.rb +64 -0
  259. data/yard-stubs/carray_utils.rb +97 -0
  260. data/yard-stubs/ruby_carray.rb +193 -0
  261. metadata +220 -138
  262. data/Rakefile +0 -51
  263. data/TODO.md +0 -18
  264. data/ext/ca_iter_block.c +0 -257
  265. data/ext/ca_iter_dimension.c +0 -299
  266. data/ext/ca_iter_window.c +0 -214
  267. data/ext/ca_obj_mapping.c +0 -644
  268. data/ext/ca_obj_unbound_repeat.c +0 -529
  269. data/ext/carray_iterator.c +0 -641
  270. data/ext/carray_math.rb +0 -850
  271. data/ext/carray_numeric.c +0 -259
  272. data/ext/carray_sort_addr.c +0 -254
  273. data/ext/carray_stat.c +0 -2100
  274. data/ext/carray_stat_proc.rb +0 -1999
  275. data/ext/mkmath.rb +0 -741
  276. data/ext/ruby_ccomplex.c +0 -509
  277. data/ext/ruby_float_func.c +0 -86
  278. data/lib/carray/array.rb +0 -8
  279. data/lib/carray/autoload/autoload_base.rb +0 -19
  280. data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
  281. data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
  282. data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
  283. data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
  284. data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
  285. data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
  286. data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
  287. data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
  288. data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
  289. data/lib/carray/autoload/autoload_gem_random.rb +0 -8
  290. data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
  291. data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
  292. data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
  293. data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
  294. data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
  295. data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
  296. data/lib/carray/autoload/autoload_object_link.rb +0 -1
  297. data/lib/carray/autoload/autoload_object_pack.rb +0 -2
  298. data/lib/carray/autoload.rb +0 -141
  299. data/lib/carray/basic.rb +0 -191
  300. data/lib/carray/broadcast.rb +0 -101
  301. data/lib/carray/compose.rb +0 -315
  302. data/lib/carray/convert.rb +0 -115
  303. data/lib/carray/info.rb +0 -110
  304. data/lib/carray/io/imagemagick.rb +0 -235
  305. data/lib/carray/mask.rb +0 -102
  306. data/lib/carray/math/histogram.rb +0 -177
  307. data/lib/carray/math/recurrence.rb +0 -93
  308. data/lib/carray/object/ca_obj_iterator.rb +0 -50
  309. data/lib/carray/object/ca_obj_link.rb +0 -50
  310. data/lib/carray/object/ca_obj_pack.rb +0 -99
  311. data/lib/carray/obsolete.rb +0 -256
  312. data/lib/carray/ordering.rb +0 -181
  313. data/lib/carray/testing.rb +0 -51
  314. data/lib/carray/transform.rb +0 -109
  315. data/mailmap +0 -1
  316. data/misc/Methods.ja.md +0 -182
  317. data/misc/NOTE +0 -51
  318. data/spec/Classes/CABitfield_spec.rb +0 -58
  319. data/spec/Classes/CABlockIterator_spec.rb +0 -114
  320. data/spec/Classes/CABlock_spec.rb +0 -205
  321. data/spec/Classes/CAField_spec.rb +0 -39
  322. data/spec/Classes/CAGrid_spec.rb +0 -75
  323. data/spec/Classes/CAMap_spec.rb +0 -0
  324. data/spec/Classes/CAMapping_spec.rb +0 -105
  325. data/spec/Classes/CAObject_attribute_spec.rb +0 -33
  326. data/spec/Classes/CAObject_spec.rb +0 -33
  327. data/spec/Classes/CARefer_spec.rb +0 -93
  328. data/spec/Classes/CARepeat_spec.rb +0 -65
  329. data/spec/Classes/CASelect_spec.rb +0 -22
  330. data/spec/Classes/CAShift_spec.rb +0 -16
  331. data/spec/Classes/CAStruct_spec.rb +0 -71
  332. data/spec/Classes/CATranspose_spec.rb +0 -60
  333. data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
  334. data/spec/Classes/CAWindow_spec.rb +0 -54
  335. data/spec/Classes/CAWrap_spec.rb +0 -8
  336. data/spec/Classes/CArray_spec.rb +0 -184
  337. data/spec/Classes/CScalar_spec.rb +0 -55
  338. data/spec/Features/feature_130_spec.rb +0 -19
  339. data/spec/Features/feature_attributes_spec.rb +0 -280
  340. data/spec/Features/feature_boolean_spec.rb +0 -98
  341. data/spec/Features/feature_broadcast.rb +0 -116
  342. data/spec/Features/feature_cast_function.rb +0 -19
  343. data/spec/Features/feature_cast_spec.rb +0 -33
  344. data/spec/Features/feature_class_spec.rb +0 -84
  345. data/spec/Features/feature_complex_spec.rb +0 -42
  346. data/spec/Features/feature_composite_spec.rb +0 -124
  347. data/spec/Features/feature_convert_spec.rb +0 -46
  348. data/spec/Features/feature_copy_spec.rb +0 -123
  349. data/spec/Features/feature_creation_spec.rb +0 -84
  350. data/spec/Features/feature_element_spec.rb +0 -144
  351. data/spec/Features/feature_extream_spec.rb +0 -54
  352. data/spec/Features/feature_generate_spec.rb +0 -74
  353. data/spec/Features/feature_index_spec.rb +0 -69
  354. data/spec/Features/feature_mask_spec.rb +0 -580
  355. data/spec/Features/feature_math_spec.rb +0 -97
  356. data/spec/Features/feature_order_spec.rb +0 -146
  357. data/spec/Features/feature_ref_store_spec.rb +0 -209
  358. data/spec/Features/feature_serialization_spec.rb +0 -125
  359. data/spec/Features/feature_stat_spec.rb +0 -397
  360. data/spec/Features/feature_virtual_spec.rb +0 -48
  361. data/spec/Features/method_eq_spec.rb +0 -81
  362. data/spec/Features/method_is_nan_spec.rb +0 -12
  363. data/spec/Features/method_map_spec.rb +0 -54
  364. data/spec/Features/method_max_with.rb +0 -20
  365. data/spec/Features/method_min_with.rb +0 -19
  366. data/spec/Features/method_ne_spec.rb +0 -18
  367. data/spec/Features/method_project_spec.rb +0 -188
  368. data/spec/Features/method_ref_spec.rb +0 -27
  369. data/spec/Features/method_round_spec.rb +0 -11
  370. data/spec/Features/method_s_linspace_spec.rb +0 -48
  371. data/spec/Features/method_s_span_spec.rb +0 -14
  372. data/spec/Features/method_seq_spec.rb +0 -47
  373. data/spec/Features/method_sort_with.rb +0 -43
  374. data/spec/Features/method_sorted_with.rb +0 -29
  375. data/spec/Features/method_span_spec.rb +0 -42
  376. data/spec/Features/method_wrap_readonly_spec.rb +0 -43
  377. data/spec/UnitTest/test_CAVirtual.rb +0 -214
  378. data/spec/spec_all.rb +0 -10
  379. data/utils/ca_ase.rb +0 -21
  380. data/utils/ca_methods.rb +0 -15
  381. data/utils/cast_checker.rb +0 -30
  382. data/utils/convert_test.rb +0 -73
  383. data/utils/extract_yard.rb +0 -22
  384. data/utils/guess_shape.rb +0 -76
  385. data/utils/monkey_patch_methods.rb +0 -62
  386. data/utils/remove_resource_fork.sh +0 -5
data/ext/ca_obj_face.c ADDED
@@ -0,0 +1,750 @@
1
+ /* ---------------------------------------------------------------------------
2
+
3
+ Face mechanism — shared helpers, abstract CAFace base class, and
4
+ registration tables for state homogeneity / portability.
5
+
6
+ A Face is storage-identical to its parent (same data_type, same
7
+ layout) and performs no value transformation. Storage ops therefore
8
+ thin-forward:
9
+ attach parent attach + alias parent->ptr
10
+ sync parent sync (alias path auto-propagates)
11
+ detach release alias + parent detach
12
+ xfer_* / delegate to parent
13
+ fill_data
14
+ Concrete Faces (CATime / CATimedelta / CARecord / ...) may
15
+ override individually; the default is pure thin-forward.
16
+
17
+ Also housed here:
18
+ rb_ca_face_template struct duplication that carries subclass tail
19
+ ca_face_lift re-wrap a view as a Face when the source was
20
+ ca_strip_face walk down through Face parents to storage
21
+ CAFace abstract class organisational relay in the class hierarchy
22
+ storage_to_scalar / per-obj_type dispatch for element decode (read)
23
+ scalar_to_storage and encode (write) between surface and storage
24
+ state-compatible / homogeneity + portability registries used by
25
+ state-portable promote_list / CAStack / MV producer
26
+
27
+ ---------------------------------------------------------------------------- */
28
+
29
+ #include "ruby.h"
30
+ #include "carray.h"
31
+ #include "ca_obj_face.h"
32
+
33
+ /* ---- shared op helpers (thin-forward) ---- */
34
+
35
+ void
36
+ ca_face_attach (void *ap)
37
+ {
38
+ CAView *cav = (CAView *) ap;
39
+ CArray *ca = (CArray *) ap;
40
+ ca_attach(cav->parent);
41
+ /* alias parent->ptr (Face has identical data layout, so copy is unneeded) */
42
+ ca->ptr = cav->parent->ptr;
43
+ }
44
+
45
+ void
46
+ ca_face_sync (void *ap)
47
+ {
48
+ CAView *cav = (CAView *) ap;
49
+ /* alias means no copy-back is needed; parent sync propagates */
50
+ ca_sync(cav->parent);
51
+ }
52
+
53
+ void
54
+ ca_face_detach (void *ap)
55
+ {
56
+ CAView *cav = (CAView *) ap;
57
+ CArray *ca = (CArray *) ap;
58
+ ca->ptr = NULL; /* release alias (do not xfree — parent owns) */
59
+ ca_detach(cav->parent);
60
+ }
61
+
62
+ void
63
+ ca_face_fill_data (void *ap, void *ptr)
64
+ {
65
+ CAView *cav = (CAView *) ap;
66
+ ca_fill(cav->parent, ptr);
67
+ }
68
+
69
+ void
70
+ ca_face_fill_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *ptr)
71
+ {
72
+ CAView *cav = (CAView *) ap;
73
+ ca_fill_addrs(cav->parent, n, addrs, ptr);
74
+ }
75
+
76
+ void
77
+ ca_face_fill_stride (void *ap, ca_size_t base, int8_t ndim,
78
+ ca_size_t *counts, ca_size_t *steps, void *ptr)
79
+ {
80
+ CAView *cav = (CAView *) ap;
81
+ ca_fill_stride(cav->parent, base, ndim, counts, steps, ptr);
82
+ }
83
+
84
+ void
85
+ ca_face_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
86
+ {
87
+ CAView *cav = (CAView *) ap;
88
+ ca_xfer_index(cav->parent, idx, data, dir);
89
+ }
90
+
91
+ void
92
+ ca_face_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
93
+ void *data, int dir)
94
+ {
95
+ CAView *cav = (CAView *) ap;
96
+ ca_xfer_addrs(cav->parent, n, addrs, data, dir);
97
+ }
98
+
99
+ void
100
+ ca_face_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
101
+ ca_size_t *strides, void *data, int dir)
102
+ {
103
+ CAView *cav = (CAView *) ap;
104
+ ca_xfer_stride(cav->parent, starts, counts, strides, data, dir);
105
+ }
106
+
107
+ void
108
+ ca_face_xfer_all (void *ap, void *data, int dir)
109
+ {
110
+ CAView *cav = (CAView *) ap;
111
+ ca_xfer_all(cav->parent, data, dir);
112
+ }
113
+
114
+ /* rb_ca_face_template(original_face, new_parent, new_dim) — duplicate
115
+ * the CArray struct of original_face (including its subclass tail) and
116
+ * return a new Face VALUE bound to new_parent with its own shape.
117
+ *
118
+ * Called from ca_face_lift and from subclass lift / clone / dup paths. */
119
+ VALUE
120
+ rb_ca_face_template (VALUE original_face, CArray *new_parent,
121
+ ca_size_t *new_dim)
122
+ {
123
+ CArray *src;
124
+ CArray *dst;
125
+ CAView *vdst;
126
+ size_t size;
127
+ const rb_data_type_t *tdata;
128
+
129
+ TypedData_Get_Struct(original_face, CArray, &carray_data_type, src);
130
+ tdata = ca_typeddata[src->obj_type];
131
+
132
+ /* Actual subclass size (incl. tail) — via TypedData's dsize callback */
133
+ size = (tdata && tdata->function.dsize)
134
+ ? tdata->function.dsize(src)
135
+ : sizeof(CAView);
136
+
137
+ dst = (CArray *) xmalloc(size);
138
+ /* CAREFUL: the whole-struct memcpy is how a C-tail Face
139
+ (CATime.unit, CARecord.data_class, ...) carries its subclass
140
+ state without per-subclass knowledge. It necessarily also copies
141
+ the CAView instance-state prefix — shape, attach state, and
142
+ framework-owned storage pointers — which must be reset below
143
+ before the new struct is exposed to GC. */
144
+ memcpy(dst, src, size);
145
+
146
+ /* CAREFUL: reset every field that was copied above but must not be
147
+ inherited from src. The framework-owned _pool / dim in
148
+ particular must be allocated fresh — reusing src's pool buffer
149
+ would alias it and double-free at GC. Pool-migrated obj_types
150
+ route through ca_array_pool_alloc; legacy ones fall back to
151
+ ALLOC_N. Any raw struct copy of a CArray-family object owes
152
+ this reset. */
153
+ vdst = (CAView *) dst;
154
+ vdst->parent = new_parent;
155
+ vdst->attach = 0;
156
+ vdst->nosync = 0;
157
+ dst->ptr = NULL;
158
+ dst->mask = NULL;
159
+ dst->elements = new_parent->elements;
160
+ dst->_pool = NULL;
161
+ if ( ca_func[src->obj_type].pool_init ) {
162
+ ca_array_pool_alloc(dst, src->obj_type, new_parent->ndim); /* ndim + _pool + dim */
163
+ }
164
+ else {
165
+ dst->ndim = new_parent->ndim;
166
+ dst->dim = ALLOC_N(ca_size_t, new_parent->ndim);
167
+ }
168
+ memcpy(dst->dim,
169
+ new_dim ? new_dim : new_parent->dim,
170
+ sizeof(ca_size_t) * new_parent->ndim);
171
+
172
+ return TypedData_Wrap_Struct(CLASS_OF(original_face),
173
+ (rb_data_type_t *) tdata,
174
+ dst);
175
+ }
176
+
177
+ /* ca_face_lift(view, face_parent) — at the tail of user-facing entry
178
+ * points (`ca[...]` etc.), when the operand was a Face, re-wrap the
179
+ * resulting view as a Face of the same subclass. Thin wrapper over
180
+ * rb_ca_face_template.
181
+ *
182
+ * Caller (rb_ca_aref / rb_ca_store) must have already checked that
183
+ * face_parent carries the CA_FLAG_IS_FACE flag. */
184
+ VALUE
185
+ ca_face_lift (VALUE view, VALUE face_parent)
186
+ {
187
+ CArray *view_ca;
188
+ VALUE lifted;
189
+ static ID id_copy_state = 0;
190
+ if ( id_copy_state == 0 ) id_copy_state = rb_intern("copy_state");
191
+
192
+ TypedData_Get_Struct(view, CArray, &carray_data_type, view_ca);
193
+
194
+ /* Idempotent lift: if the view is already a Face of the same class as
195
+ face_parent, it has already been lifted — a builder that lifts internally
196
+ (e.g. rb_ca_refer_new → rb_ca_refer) whose caller lifts the result again.
197
+ Re-wrapping would stack an adjacent same-class double Face; return the
198
+ already-lifted view unchanged. Same *class* (not just obj_type) so two
199
+ distinct CAObject-based Faces, which share obj_type CA_OBJ_OBJECT, are not
200
+ conflated — that is a legitimate stack and must proceed to a real lift. */
201
+ if ( ca_is_face(view_ca)
202
+ && rb_obj_class(view) == rb_obj_class(face_parent) ) {
203
+ return view;
204
+ }
205
+
206
+ /* CAREFUL: rewrite view's data_type to the storage data_type before
207
+ lifting. view-creation inherits the Face's surface data_type
208
+ (often CA_FIXLEN), but the invariant is that Face.parent.data_type
209
+ is always the storage type — so view (the new Face's parent-to-be)
210
+ must carry the storage type, and only the new Face wrapper
211
+ presents the surface. */
212
+ {
213
+ CArray *fp, *p;
214
+ TypedData_Get_Struct(face_parent, CArray, &carray_data_type, fp);
215
+ p = fp;
216
+ while (p && ca_is_face(p)) {
217
+ p = ((CAView *) p)->parent;
218
+ }
219
+ if (p && p->data_type != view_ca->data_type) {
220
+ view_ca->data_type = p->data_type;
221
+ }
222
+
223
+ /* §8.3 one-level local swap: put the view where the Face used to sit, so
224
+ the lifted chain carries exactly ONE Face (the new wrapper) rather than
225
+ a redundant intermediate Face. A fetch-path builder builds its view on
226
+ the un-stripped Face operand, leaving view->parent == the Face; move it
227
+ down ONE level to fp->parent. Never a full strip_face walk: an inner
228
+ distinct Face the user stacked underneath (docs/topics/CAFace.md §8.3) must be
229
+ preserved, and fp->parent points at it. Guard on pointer-equality so
230
+ only a view built directly on this Face fires (order.c builds on
231
+ stripped storage → no-op). CAStack (multi-parent) is excluded here and
232
+ handled by builder-side pre-strip. data_type above stays a full strip
233
+ (surface concern, §8 #7) — the parent pointer is the structural concern
234
+ (§8.3); the two axes are deliberately separate. */
235
+ if ( ((CAView *) view_ca)->parent == fp
236
+ && ! ca_test_flag(view_ca, CA_FLAG_MULTI_PARENTS) ) {
237
+ ((CAView *) view_ca)->parent = ((CAView *) fp)->parent; /* C pointer: one level */
238
+ rb_ca_set_parent(view, rb_ca_parent(face_parent)); /* @parent ivar: lockstep */
239
+ }
240
+ }
241
+
242
+ /* Pass view_ca->dim explicitly: passing new_dim=NULL would adopt
243
+ new_parent->dim, but new_parent here is the (typically) narrower
244
+ view. */
245
+ lifted = rb_ca_face_template(face_parent, view_ca, view_ca->dim);
246
+ /* Pin view as a GC root of lifted so the slice is not collected. */
247
+ rb_ca_set_parent(lifted, view);
248
+
249
+ /* Optional Ruby callback for ivar / state carry. Faces holding
250
+ semantic state in the struct tail (CATime / CATimedelta
251
+ etc.) auto-carry via the template memcpy; Faces holding state in
252
+ Ruby ivars (Ruby-implemented CAObject-derived Faces etc.) should
253
+ override copy_state to copy them. */
254
+ if ( rb_respond_to(face_parent, id_copy_state) ) {
255
+ rb_funcall(lifted, id_copy_state, 1, face_parent);
256
+ }
257
+
258
+ #ifdef CARRAY_DEV_BUILD
259
+ /* Soundness tripwire (docs/internal/FaceLiftSoundness.md §0/§7): after a lift the view
260
+ node under the new Face must not point back at the Face we lifted from —
261
+ the §8.3 one-level swap must have moved it exactly one level, so a
262
+ redundant middle Face is structurally caught here, wherever it enters.
263
+ Skip only when the lifted node is not a view (the ordering kernels lift a
264
+ fresh entity result — no swap applies). Multi-parent CAStack is covered:
265
+ its parents are pre-stripped in the builder (ca_stack_setup_with_axis), so
266
+ ->parent (slot 0) is storage; this checks that slot. */
267
+ {
268
+ CArray *lifted_ca, *lp, *fps;
269
+ TypedData_Get_Struct(lifted, CArray, &carray_data_type, lifted_ca);
270
+ lp = CAVIEW(lifted_ca)->parent;
271
+ TypedData_Get_Struct(face_parent, CArray, &carray_data_type, fps);
272
+ if ( lp && ca_is_view(lp) ) {
273
+ if ( CAVIEW(lp)->parent == fps ) {
274
+ rb_bug("ca_face_lift: §8.3 one-level swap did not fire — redundant "
275
+ "middle Face (view->parent still points at the lifted Face)");
276
+ }
277
+ if ( ca_is_face(lp) && lp->obj_type == fps->obj_type ) {
278
+ rb_bug("ca_face_lift: adjacent same-class double Face under the lifted "
279
+ "wrapper (CAFace.md §8 #2/#3)");
280
+ }
281
+ }
282
+ }
283
+ #endif
284
+
285
+ return lifted;
286
+ }
287
+
288
+ /* ca_lazy_marker_lift(view, marker) — the CALazyMarker half of
289
+ * ca_wrapper_lift. Same invariant as the Face lift (wrapper on top, view
290
+ * built against what the wrapper wraps) reached by a shorter route: a
291
+ * marker carries no subclass state, so there is nothing for
292
+ * rb_ca_face_template's struct copy to preserve, and building a fresh
293
+ * marker over the view gets data_type, bytes, shape, mask and flags right
294
+ * by construction. That last part is load-bearing for `refer(type)`,
295
+ * where the view's data_type differs from the one the old marker copied
296
+ * off the entity.
297
+ *
298
+ * The one piece it shares with the Face lift is the one-level swap: the
299
+ * builder handed us a view whose parent is the marker, and leaving it
300
+ * there would stack a redundant marker in the middle. */
301
+ static VALUE
302
+ ca_lazy_marker_lift (VALUE view, VALUE marker)
303
+ {
304
+ CArray *view_ca, *mp;
305
+ extern VALUE rb_ca_lazy_marker_new (VALUE cary);
306
+
307
+ TypedData_Get_Struct(view, CArray, &carray_data_type, view_ca);
308
+
309
+ /* Idempotent: a builder that wrapped internally already returned a
310
+ marker (mirrors the same guard in ca_face_lift). */
311
+ if ( ca_is_lazy_marker(view_ca) ) {
312
+ return view;
313
+ }
314
+
315
+ TypedData_Get_Struct(marker, CArray, &carray_data_type, mp);
316
+
317
+ /* One-level swap (CAFace.md section 8.3, same reasoning): move the view
318
+ off the marker and onto what the marker wraps. Guarded on pointer
319
+ equality so only a view built directly on this marker fires. */
320
+ if ( ca_is_view(view_ca)
321
+ && ((CAView *) view_ca)->parent == mp
322
+ && ! ca_test_flag(view_ca, CA_FLAG_MULTI_PARENTS) ) {
323
+ ((CAView *) view_ca)->parent = ((CAView *) mp)->parent; /* C pointer */
324
+ rb_ca_set_parent(view, rb_ca_parent(marker)); /* @parent ivar */
325
+ }
326
+
327
+ return rb_ca_lazy_marker_new(view);
328
+ }
329
+
330
+ /* ca_wrapper_lift(view, wrapper, wrapper_ca) — dispatch for
331
+ * CA_WRAPPER_LIFT. The macro has already established that wrapper_ca
332
+ * carries one of the two flags and that view is a CArray. */
333
+ VALUE
334
+ ca_wrapper_lift (VALUE view, VALUE wrapper, void *wrapper_ca)
335
+ {
336
+ CArray *ca = (CArray *) wrapper_ca;
337
+
338
+ if ( ca_is_face(ca) ) {
339
+ CArray *view_ca;
340
+ TypedData_Get_Struct(view, CArray, &carray_data_type, view_ca);
341
+ /* Some builders (select / repeat / ...) lift their own result; a
342
+ second lift here would double-wrap. Mirrors the guard the `[]`
343
+ call site used to carry inline. */
344
+ if ( ca_is_face(view_ca) ) {
345
+ return view;
346
+ }
347
+ return ca_face_lift(view, wrapper);
348
+ }
349
+
350
+ return ca_lazy_marker_lift(view, wrapper);
351
+ }
352
+
353
+ /* ca_strip_face(src) — walk down through Face parents to the storage
354
+ * CArray. Called at kernel_iterator entry and lazy chain endpoints
355
+ * where downstream code needs to observe the storage layout, not the
356
+ * Face surface. */
357
+ CArray *
358
+ ca_strip_face (CArray *src)
359
+ {
360
+ while (src && ca_is_face(src)) {
361
+ src = ((CAView *) src)->parent;
362
+ }
363
+ return src;
364
+ }
365
+
366
+ /* rb_ca_strip_face_value(v) — VALUE-level counterpart to ca_strip_face.
367
+ * Walks down the Face `parent` ivar chain and returns the root storage
368
+ * VALUE (a non-Face CArray). Non-Face input is returned as-is.
369
+ *
370
+ * Used by ordering / search kernel dispatchers where the switch on
371
+ * data_type must see storage type instead of Face surface (CA_FIXLEN),
372
+ * and where subsequent `rb_ca_template*` calls must not re-lift the
373
+ * result into a Face wrapper (index / addr / linear outputs are not
374
+ * Face-semantic). Complements ca_strip_face on the C-struct axis. */
375
+ VALUE
376
+ rb_ca_strip_face_value (VALUE v)
377
+ {
378
+ CArray *ca;
379
+ if ( ! rb_obj_is_kind_of(v, rb_cCArray) ) return v;
380
+ TypedData_Get_Struct(v, CArray, &carray_data_type, ca);
381
+ while ( ca_is_face(ca) ) {
382
+ v = rb_ca_parent(v);
383
+ TypedData_Get_Struct(v, CArray, &carray_data_type, ca);
384
+ }
385
+ return v;
386
+ }
387
+
388
+ /* CAFace — abstract marker class used as the code-organisation relay
389
+ for the Face mechanism. The semantic gate is CA_FLAG_IS_FACE only,
390
+ read via ca_is_face(); the class hierarchy is organisation, not the
391
+ basis for the predicate. CAObject-derived Faces satisfy
392
+ ca_is_face()=true without inheriting CAFace at the Ruby level.
393
+
394
+ caface_data_type is a TypedData chain entry with no callbacks;
395
+ subclasses provide their own dmark / dfree / dsize.
396
+
397
+ rb_cCAFace's allocator is sealed off so the class cannot be
398
+ instantiated directly; it exists only as the inheritance target
399
+ for concrete Faces (CATime / CATimedelta / CARecord / ...). */
400
+
401
+ VALUE rb_cCAFace;
402
+
403
+ const rb_data_type_t caface_data_type = {
404
+ .wrap_struct_name = "CAFace",
405
+ .parent = &caview_data_type,
406
+ .function = {
407
+ .dmark = NULL, /* subclass overrides */
408
+ .dfree = NULL, /* subclass overrides */
409
+ .dsize = NULL, /* subclass overrides */
410
+ },
411
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
412
+ };
413
+
414
+ static VALUE
415
+ rb_ca_face_s_allocate_forbidden (VALUE klass)
416
+ {
417
+ rb_raise(rb_eTypeError,
418
+ "CAFace is abstract; instantiate a concrete Face subclass "
419
+ "(CATime / CATimedelta / ...)");
420
+ }
421
+
422
+ /* Face-local C dispatch table for storage_to_scalar (the read/decode
423
+ direction). Sized to CA_OBJ_TYPE_MAX so any registered obj_type can be
424
+ indexed directly; file-scope zero-init leaves unregistered slots NULL. */
425
+ ca_face_storage_to_scalar_fn ca_face_storage_to_scalar_table[CA_OBJ_TYPE_MAX];
426
+
427
+ void
428
+ ca_face_register_storage_to_scalar (int obj_type, ca_face_storage_to_scalar_fn fn)
429
+ {
430
+ if ( obj_type < 0 || obj_type >= CA_OBJ_TYPE_MAX ) {
431
+ rb_raise(rb_eArgError,
432
+ "ca_face_register_storage_to_scalar: obj_type %d out of range",
433
+ obj_type);
434
+ }
435
+ ca_face_storage_to_scalar_table[obj_type] = fn;
436
+ }
437
+
438
+ /* Face-local C dispatch table for scalar_to_storage (the write/encode
439
+ direction), the mirror of the decode table above. Unregistered slots
440
+ stay NULL; a Face without a C fast path relies on the Ruby fallback in
441
+ ca_face_scalar_to_storage (carray_cast.c). */
442
+ ca_face_scalar_to_storage_fn ca_face_scalar_to_storage_table[CA_OBJ_TYPE_MAX];
443
+
444
+ void
445
+ ca_face_register_scalar_to_storage (int obj_type, ca_face_scalar_to_storage_fn fn)
446
+ {
447
+ if ( obj_type < 0 || obj_type >= CA_OBJ_TYPE_MAX ) {
448
+ rb_raise(rb_eArgError,
449
+ "ca_face_register_scalar_to_storage: obj_type %d out of range",
450
+ obj_type);
451
+ }
452
+ ca_face_scalar_to_storage_table[obj_type] = fn;
453
+ }
454
+
455
+ /* -- Face state homogeneity check -- */
456
+
457
+ ca_face_state_compatible_fn ca_face_state_compatible_table[CA_OBJ_TYPE_MAX];
458
+
459
+ void
460
+ ca_face_register_state_compatible (int obj_type, ca_face_state_compatible_fn fn)
461
+ {
462
+ if ( obj_type < 0 || obj_type >= CA_OBJ_TYPE_MAX ) {
463
+ rb_raise(rb_eArgError,
464
+ "ca_face_register_state_compatible: obj_type %d out of range",
465
+ obj_type);
466
+ }
467
+ ca_face_state_compatible_table[obj_type] = fn;
468
+ }
469
+
470
+ /* Detect whether `va`'s class overrides `face_state_compatible?` (= the
471
+ method is owned by a class other than rb_cCArray). Uses #method / #owner
472
+ reflection; called only on the cold path (no C hook registered). */
473
+ static int
474
+ ca_face_has_ruby_state_check (VALUE va)
475
+ {
476
+ static ID id_face_state_compatible_p = 0;
477
+ static ID id_method = 0;
478
+ static ID id_owner = 0;
479
+ VALUE m, owner;
480
+ if ( id_face_state_compatible_p == 0 ) {
481
+ id_face_state_compatible_p = rb_intern("face_state_compatible?");
482
+ id_method = rb_intern("method");
483
+ id_owner = rb_intern("owner");
484
+ }
485
+ m = rb_funcall(va, id_method, 1, ID2SYM(id_face_state_compatible_p));
486
+ owner = rb_funcall(m, id_owner, 0);
487
+ return ( owner != rb_cCArray ) ? 1 : 0;
488
+ }
489
+
490
+ int
491
+ ca_face_state_compatible (VALUE va, CArray *a, VALUE vb, CArray *b)
492
+ {
493
+ ca_face_state_compatible_fn fn = ca_face_state_compatible_table[a->obj_type];
494
+ if ( fn != NULL ) {
495
+ return fn(a, b);
496
+ }
497
+ if ( ca_face_has_ruby_state_check(va) ) {
498
+ VALUE r = rb_funcall(va, rb_intern("face_state_compatible?"), 1, vb);
499
+ return RTEST(r) ? 1 : 0;
500
+ }
501
+ return 1;
502
+ }
503
+
504
+ /* Ruby-side: CArray#face_state_compatible?(other) */
505
+ static VALUE
506
+ rb_ca_face_state_compatible_p (VALUE self, VALUE other)
507
+ {
508
+ CArray *a, *b;
509
+
510
+ if ( ! rb_obj_is_kind_of(other, rb_cCArray) ) {
511
+ rb_raise(rb_eArgError,
512
+ "face_state_compatible?: argument must be a CArray");
513
+ }
514
+ TypedData_Get_Struct(self, CArray, &carray_data_type, a);
515
+ TypedData_Get_Struct(other, CArray, &carray_data_type, b);
516
+ if ( ! ca_is_face(a) || ! ca_is_face(b) ) {
517
+ rb_raise(rb_eArgError,
518
+ "face_state_compatible?: both receiver and argument must be Face");
519
+ }
520
+ if ( a->obj_type != b->obj_type ) {
521
+ rb_raise(rb_eArgError,
522
+ "face_state_compatible?: must be the same Face class");
523
+ }
524
+ return ca_face_state_compatible(self, a, other, b) ? Qtrue : Qfalse;
525
+ }
526
+
527
+ /* -- Face state portability -- */
528
+
529
+ /* int8_t per slot to distinguish "unregistered" (-1, dispatch falls
530
+ through to Ruby fallback) from "registered as not-portable"
531
+ (0, e.g. CAConstString) and "registered as portable" (1). */
532
+ int8_t ca_face_state_portable_table[CA_OBJ_TYPE_MAX];
533
+
534
+ /* Per-obj_type bound Ruby class method returning the registered value.
535
+ Defined once for each obj_type that registers a value, so user-facing
536
+ `<FaceClass>.face_state_portable?` reflects the C table without
537
+ needing a reverse-lookup at every call. */
538
+ static VALUE
539
+ rb_ca_s_face_state_portable_true (VALUE klass)
540
+ {
541
+ return Qtrue;
542
+ }
543
+
544
+ static VALUE
545
+ rb_ca_s_face_state_portable_false (VALUE klass)
546
+ {
547
+ return Qfalse;
548
+ }
549
+
550
+ void
551
+ ca_face_register_state_portable (int obj_type, int portable)
552
+ {
553
+ if ( obj_type < 0 || obj_type >= CA_OBJ_TYPE_MAX ) {
554
+ rb_raise(rb_eArgError,
555
+ "ca_face_register_state_portable: obj_type %d out of range",
556
+ obj_type);
557
+ }
558
+ ca_face_state_portable_table[obj_type] = portable ? 1 : 0;
559
+ /* Mirror to a singleton method on the class so Ruby-side
560
+ introspection (<FaceClass>.face_state_portable?) agrees with the
561
+ C table. CAREFUL: ca_class[obj_type] must be set by
562
+ ca_install_obj_type before this call — Faces register from
563
+ within their Init_*, which runs after ca_install_obj_type. */
564
+ if ( !NIL_P(ca_class[obj_type]) ) {
565
+ rb_define_singleton_method(ca_class[obj_type], "face_state_portable?",
566
+ portable ? rb_ca_s_face_state_portable_true
567
+ : rb_ca_s_face_state_portable_false,
568
+ 0);
569
+ }
570
+ }
571
+
572
+ /* Detect whether `klass`'s singleton class owns `face_state_portable?`
573
+ (= the predicate is defined on the Face class itself, not inherited
574
+ from rb_cCArray's metaclass). Cold path; only consulted when no C
575
+ entry is registered. */
576
+ static int
577
+ ca_face_class_has_portable_method (VALUE klass)
578
+ {
579
+ static ID id_face_state_portable_p = 0;
580
+ static ID id_method = 0;
581
+ static ID id_owner = 0;
582
+ VALUE m, owner;
583
+ if ( NIL_P(klass) ) return 0;
584
+ if ( id_face_state_portable_p == 0 ) {
585
+ id_face_state_portable_p = rb_intern("face_state_portable?");
586
+ id_method = rb_intern("method");
587
+ id_owner = rb_intern("owner");
588
+ }
589
+ if ( !rb_respond_to(klass, id_face_state_portable_p) ) return 0;
590
+ m = rb_funcall(klass, id_method, 1, ID2SYM(id_face_state_portable_p));
591
+ owner = rb_funcall(m, id_owner, 0);
592
+ /* owner is a (singleton) class — if it isn't rb_singleton_class(rb_cCArray)
593
+ or the singleton chain rooted at CArray, the Face class (or its
594
+ ancestor) has its own definition. */
595
+ return ( owner != rb_singleton_class(rb_cCArray) ) ? 1 : 0;
596
+ }
597
+
598
+ int
599
+ ca_face_state_portable (int obj_type, VALUE klass)
600
+ {
601
+ int8_t entry;
602
+ if ( obj_type < 0 || obj_type >= CA_OBJ_TYPE_MAX ) return 1;
603
+ entry = ca_face_state_portable_table[obj_type];
604
+ if ( entry >= 0 ) {
605
+ return entry ? 1 : 0;
606
+ }
607
+ if ( !NIL_P(klass) && ca_face_class_has_portable_method(klass) ) {
608
+ VALUE r = rb_funcall(klass, rb_intern("face_state_portable?"), 0);
609
+ return RTEST(r) ? 1 : 0;
610
+ }
611
+ return 1; /* default portable */
612
+ }
613
+
614
+ /* CArray.face_state_portable? — class method on CArray. Subclasses
615
+ (Face classes) override by defining their own `def self.face_state_portable?`.
616
+ This default returns true (= 1) for non-Face CArray classes; the actual
617
+ per-Face decision goes through ca_face_state_portable from C callers. */
618
+ static VALUE
619
+ rb_ca_s_face_state_portable_p (VALUE klass)
620
+ {
621
+ return Qtrue;
622
+ }
623
+
624
+ /* CArray#face_lift(face_parent) — Ruby surface for ca_face_lift.
625
+ Used by CArray.stack to re-wrap a raw CAStack with the homogeneous
626
+ Face class carried by face_parent (typically list[0] after the
627
+ promote_list pass). Delegates the data_type rewrite and optional
628
+ copy_state callback to ca_face_lift. */
629
+ static VALUE
630
+ rb_ca_face_lift_method (VALUE self, VALUE face_parent)
631
+ {
632
+ CArray *fp;
633
+ if ( ! rb_obj_is_kind_of(face_parent, rb_cCArray) ) {
634
+ rb_raise(rb_eArgError, "face_lift: face_parent must be a CArray");
635
+ }
636
+ TypedData_Get_Struct(face_parent, CArray, &carray_data_type, fp);
637
+ if ( ! ca_is_face(fp) ) {
638
+ rb_raise(rb_eArgError, "face_lift: face_parent must be a Face instance");
639
+ }
640
+ return ca_face_lift(self, face_parent);
641
+ }
642
+
643
+ /* ca_face_reconcile_comparison(pself, pother) — Face gate for the element-
644
+ * wise comparison operators (< <= > >=, eq, ne; <=> composes from > and <).
645
+ * Mirrors the search query gate (PROPOSAL_FACE_ORDERING_GATE): when the LHS
646
+ * is a Face over NUMERIC storage, memcmp on the surface fixlen bytes would
647
+ * mis-order the storage, so descend to storage (ORDERABLE licenses that the
648
+ * numeric order equals the surface order) and reconcile the RHS:
649
+ *
650
+ * - RHS is a Face: COMPARABLE self strips it; else it must respond to
651
+ * to_comparable (reconcile to self's reference, e.g. unit alignment,
652
+ * which may raise on loss); else raise.
653
+ * - RHS is plain (bare number / non-Face array / Scalar): a direct storage
654
+ * compare, which only a COMPARABLE self licenses -- a non-COMPARABLE
655
+ * Face (e.g. a unit-bearing CATime) rejects it.
656
+ *
657
+ * A Face over FIXLEN storage (CARecord / string faces) is left untouched:
658
+ * memcmp on its bytes is already the correct order/equality, same as a plain
659
+ * fixlen array. Both operands are rebound to storage via *pself / *pother.
660
+ * No-op when self is not a Face. */
661
+ void
662
+ ca_face_reconcile_comparison (volatile VALUE *pself, volatile VALUE *pother)
663
+ {
664
+ VALUE self = *pself, other = *pother;
665
+ CArray *sc, *storage;
666
+
667
+ if ( ! rb_obj_is_kind_of(self, rb_cCArray) ) {
668
+ return;
669
+ }
670
+ TypedData_Get_Struct(self, CArray, &carray_data_type, sc);
671
+ if ( ! ca_is_face(sc) ) {
672
+ return;
673
+ }
674
+
675
+ storage = ca_strip_face(sc);
676
+ if ( storage->data_type == CA_FIXLEN ) {
677
+ /* genuine fixlen semantics: memcmp is the correct comparison, leave
678
+ the Face(s) in place (same result as a plain fixlen array). */
679
+ return;
680
+ }
681
+
682
+ if ( ! ca_test_flag(sc, CA_FLAG_FACE_ORDERABLE_STORAGE) ) {
683
+ rb_raise(rb_eArgError,
684
+ "comparison: Face operand (%s) is not orderable by storage; "
685
+ "use ca.parent to descend to storage",
686
+ rb_obj_classname(self));
687
+ }
688
+
689
+ {
690
+ VALUE self_ref = self; /* reference before stripping (for to_comparable) */
691
+ int comparable = ca_test_flag(sc, CA_FLAG_FACE_COMPARABLE_STORAGE);
692
+ int other_is_face = 0;
693
+
694
+ if ( rb_obj_is_kind_of(other, rb_cCArray) ) {
695
+ CArray *oc;
696
+ TypedData_Get_Struct(other, CArray, &carray_data_type, oc);
697
+ other_is_face = ca_is_face(oc);
698
+ }
699
+
700
+ if ( comparable ) {
701
+ /* directly comparable Face reference: strip a Face operand, take a
702
+ plain operand as-is (storage compare is licensed). */
703
+ if ( other_is_face ) {
704
+ other = rb_ca_strip_face_value(other);
705
+ }
706
+ }
707
+ else if ( rb_respond_to(self_ref, rb_intern("to_comparable")) ) {
708
+ /* non-COMPARABLE Face reference: the reference brings the operand into
709
+ its own space (unit alignment / instant lift), whatever the operand
710
+ type -- Face CArray, our Scalar, a Ruby Time / DateTime, etc. The
711
+ reference's to_comparable raises if it cannot reconcile it. */
712
+ other = rb_funcall(self_ref, rb_intern("to_comparable"), 1, other);
713
+ other = rb_ca_strip_face_value(other);
714
+ }
715
+ else {
716
+ rb_raise(rb_eArgError,
717
+ "comparison: non-comparable Face reference (%s) has no "
718
+ "to_comparable to reconcile the operand; use ca.parent to "
719
+ "compare the hidden storage explicitly",
720
+ rb_obj_classname(self_ref));
721
+ }
722
+
723
+ *pself = rb_ca_strip_face_value(self);
724
+ *pother = other;
725
+ }
726
+ }
727
+
728
+ void
729
+ Init_ca_face (void)
730
+ {
731
+ int i;
732
+
733
+ /* CAREFUL: rb_cCAView must already exist (defined in ruby_carray.c
734
+ ahead of this Init) — CAFace inherits from it. */
735
+ rb_cCAFace = rb_define_class("CAFace", rb_cCAView);
736
+ rb_define_alloc_func(rb_cCAFace, rb_ca_face_s_allocate_forbidden);
737
+
738
+ /* Initialise the portable table to -1 for every slot so dispatch
739
+ can distinguish "explicitly registered 0" (CAConstString) from
740
+ "never registered" (falls through to the Ruby default). */
741
+ for ( i = 0; i < CA_OBJ_TYPE_MAX; i++ ) {
742
+ ca_face_state_portable_table[i] = -1;
743
+ }
744
+
745
+ rb_define_method(rb_cCArray, "face_state_compatible?",
746
+ rb_ca_face_state_compatible_p, 1);
747
+ rb_define_singleton_method(rb_cCArray, "face_state_portable?",
748
+ rb_ca_s_face_state_portable_p, 0);
749
+ rb_define_method(rb_cCArray, "face_lift", rb_ca_face_lift_method, 1);
750
+ }