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_face.c ADDED
@@ -0,0 +1,670 @@
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_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
71
+ {
72
+ CAView *cav = (CAView *) ap;
73
+ ca_xfer_index(cav->parent, idx, data, dir);
74
+ }
75
+
76
+ void
77
+ ca_face_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
78
+ void *data, int dir)
79
+ {
80
+ CAView *cav = (CAView *) ap;
81
+ ca_xfer_addrs(cav->parent, n, addrs, data, dir);
82
+ }
83
+
84
+ void
85
+ ca_face_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
86
+ ca_size_t *strides, void *data, int dir)
87
+ {
88
+ CAView *cav = (CAView *) ap;
89
+ ca_xfer_stride(cav->parent, starts, counts, strides, data, dir);
90
+ }
91
+
92
+ void
93
+ ca_face_xfer_all (void *ap, void *data, int dir)
94
+ {
95
+ CAView *cav = (CAView *) ap;
96
+ ca_xfer_all(cav->parent, data, dir);
97
+ }
98
+
99
+ /* rb_ca_face_template(original_face, new_parent, new_dim) — duplicate
100
+ * the CArray struct of original_face (including its subclass tail) and
101
+ * return a new Face VALUE bound to new_parent with its own shape.
102
+ *
103
+ * Called from ca_face_lift and from subclass lift / clone / dup paths. */
104
+ VALUE
105
+ rb_ca_face_template (VALUE original_face, CArray *new_parent,
106
+ ca_size_t *new_dim)
107
+ {
108
+ CArray *src;
109
+ CArray *dst;
110
+ CAView *vdst;
111
+ size_t size;
112
+ const rb_data_type_t *tdata;
113
+
114
+ TypedData_Get_Struct(original_face, CArray, &carray_data_type, src);
115
+ tdata = ca_typeddata[src->obj_type];
116
+
117
+ /* Actual subclass size (incl. tail) — via TypedData's dsize callback */
118
+ size = (tdata && tdata->function.dsize)
119
+ ? tdata->function.dsize(src)
120
+ : sizeof(CAView);
121
+
122
+ dst = (CArray *) xmalloc(size);
123
+ /* CAREFUL: the whole-struct memcpy is how a C-tail Face
124
+ (CATime.unit, CARecord.data_class, ...) carries its subclass
125
+ state without per-subclass knowledge. It necessarily also copies
126
+ the CAView instance-state prefix — shape, attach state, and
127
+ framework-owned storage pointers — which must be reset below
128
+ before the new struct is exposed to GC. */
129
+ memcpy(dst, src, size);
130
+
131
+ /* CAREFUL: reset every field that was copied above but must not be
132
+ inherited from src. The framework-owned _pool / dim in
133
+ particular must be allocated fresh — reusing src's pool buffer
134
+ would alias it and double-free at GC. Pool-migrated obj_types
135
+ route through ca_array_pool_alloc; legacy ones fall back to
136
+ ALLOC_N. Any raw struct copy of a CArray-family object owes
137
+ this reset. */
138
+ vdst = (CAView *) dst;
139
+ vdst->parent = new_parent;
140
+ vdst->attach = 0;
141
+ vdst->nosync = 0;
142
+ dst->ptr = NULL;
143
+ dst->mask = NULL;
144
+ dst->elements = new_parent->elements;
145
+ dst->_pool = NULL;
146
+ if ( ca_func[src->obj_type].pool_init ) {
147
+ ca_array_pool_alloc(dst, src->obj_type, new_parent->ndim); /* ndim + _pool + dim */
148
+ }
149
+ else {
150
+ dst->ndim = new_parent->ndim;
151
+ dst->dim = ALLOC_N(ca_size_t, new_parent->ndim);
152
+ }
153
+ memcpy(dst->dim,
154
+ new_dim ? new_dim : new_parent->dim,
155
+ sizeof(ca_size_t) * new_parent->ndim);
156
+
157
+ return TypedData_Wrap_Struct(CLASS_OF(original_face),
158
+ (rb_data_type_t *) tdata,
159
+ dst);
160
+ }
161
+
162
+ /* ca_face_lift(view, face_parent) — at the tail of user-facing entry
163
+ * points (`ca[...]` etc.), when the operand was a Face, re-wrap the
164
+ * resulting view as a Face of the same subclass. Thin wrapper over
165
+ * rb_ca_face_template.
166
+ *
167
+ * Caller (rb_ca_aref / rb_ca_store) must have already checked that
168
+ * face_parent carries the CA_FLAG_IS_FACE flag. */
169
+ VALUE
170
+ ca_face_lift (VALUE view, VALUE face_parent)
171
+ {
172
+ CArray *view_ca;
173
+ VALUE lifted;
174
+ static ID id_copy_state = 0;
175
+ if ( id_copy_state == 0 ) id_copy_state = rb_intern("copy_state");
176
+
177
+ TypedData_Get_Struct(view, CArray, &carray_data_type, view_ca);
178
+
179
+ /* Idempotent lift: if the view is already a Face of the same class as
180
+ face_parent, it has already been lifted — a builder that lifts internally
181
+ (e.g. rb_ca_refer_new → rb_ca_refer) whose caller lifts the result again.
182
+ Re-wrapping would stack an adjacent same-class double Face; return the
183
+ already-lifted view unchanged. Same *class* (not just obj_type) so two
184
+ distinct CAObject-based Faces, which share obj_type CA_OBJ_OBJECT, are not
185
+ conflated — that is a legitimate stack and must proceed to a real lift. */
186
+ if ( ca_is_face(view_ca)
187
+ && rb_obj_class(view) == rb_obj_class(face_parent) ) {
188
+ return view;
189
+ }
190
+
191
+ /* CAREFUL: rewrite view's data_type to the storage data_type before
192
+ lifting. view-creation inherits the Face's surface data_type
193
+ (often CA_FIXLEN), but the invariant is that Face.parent.data_type
194
+ is always the storage type — so view (the new Face's parent-to-be)
195
+ must carry the storage type, and only the new Face wrapper
196
+ presents the surface. */
197
+ {
198
+ CArray *fp, *p;
199
+ TypedData_Get_Struct(face_parent, CArray, &carray_data_type, fp);
200
+ p = fp;
201
+ while (p && ca_is_face(p)) {
202
+ p = ((CAView *) p)->parent;
203
+ }
204
+ if (p && p->data_type != view_ca->data_type) {
205
+ view_ca->data_type = p->data_type;
206
+ }
207
+
208
+ /* §8.3 one-level local swap: put the view where the Face used to sit, so
209
+ the lifted chain carries exactly ONE Face (the new wrapper) rather than
210
+ a redundant intermediate Face. A fetch-path builder builds its view on
211
+ the un-stripped Face operand, leaving view->parent == the Face; move it
212
+ down ONE level to fp->parent. Never a full strip_face walk: an inner
213
+ distinct Face the user stacked underneath (docs/topics/CAFace.md §8.3) must be
214
+ preserved, and fp->parent points at it. Guard on pointer-equality so
215
+ only a view built directly on this Face fires (order.c builds on
216
+ stripped storage → no-op). CAStack (multi-parent) is excluded here and
217
+ handled by builder-side pre-strip. data_type above stays a full strip
218
+ (surface concern, §8 #7) — the parent pointer is the structural concern
219
+ (§8.3); the two axes are deliberately separate. */
220
+ if ( ((CAView *) view_ca)->parent == fp
221
+ && ! ca_test_flag(view_ca, CA_FLAG_MULTI_PARENTS) ) {
222
+ ((CAView *) view_ca)->parent = ((CAView *) fp)->parent; /* C pointer: one level */
223
+ rb_ca_set_parent(view, rb_ca_parent(face_parent)); /* @parent ivar: lockstep */
224
+ }
225
+ }
226
+
227
+ /* Pass view_ca->dim explicitly: passing new_dim=NULL would adopt
228
+ new_parent->dim, but new_parent here is the (typically) narrower
229
+ view. */
230
+ lifted = rb_ca_face_template(face_parent, view_ca, view_ca->dim);
231
+ /* Pin view as a GC root of lifted so the slice is not collected. */
232
+ rb_ca_set_parent(lifted, view);
233
+
234
+ /* Optional Ruby callback for ivar / state carry. Faces holding
235
+ semantic state in the struct tail (CATime / CATimedelta
236
+ etc.) auto-carry via the template memcpy; Faces holding state in
237
+ Ruby ivars (Ruby-implemented CAObject-derived Faces etc.) should
238
+ override copy_state to copy them. */
239
+ if ( rb_respond_to(face_parent, id_copy_state) ) {
240
+ rb_funcall(lifted, id_copy_state, 1, face_parent);
241
+ }
242
+
243
+ #ifdef CARRAY_DEV_BUILD
244
+ /* Soundness tripwire (docs/internal/FaceLiftSoundness.md §0/§7): after a lift the view
245
+ node under the new Face must not point back at the Face we lifted from —
246
+ the §8.3 one-level swap must have moved it exactly one level, so a
247
+ redundant middle Face is structurally caught here, wherever it enters.
248
+ Skip only when the lifted node is not a view (the ordering kernels lift a
249
+ fresh entity result — no swap applies). Multi-parent CAStack is covered:
250
+ its parents are pre-stripped in the builder (ca_stack_setup_with_axis), so
251
+ ->parent (slot 0) is storage; this checks that slot. */
252
+ {
253
+ CArray *lifted_ca, *lp, *fps;
254
+ TypedData_Get_Struct(lifted, CArray, &carray_data_type, lifted_ca);
255
+ lp = CAVIEW(lifted_ca)->parent;
256
+ TypedData_Get_Struct(face_parent, CArray, &carray_data_type, fps);
257
+ if ( lp && ca_is_view(lp) ) {
258
+ if ( CAVIEW(lp)->parent == fps ) {
259
+ rb_bug("ca_face_lift: §8.3 one-level swap did not fire — redundant "
260
+ "middle Face (view->parent still points at the lifted Face)");
261
+ }
262
+ if ( ca_is_face(lp) && lp->obj_type == fps->obj_type ) {
263
+ rb_bug("ca_face_lift: adjacent same-class double Face under the lifted "
264
+ "wrapper (CAFace.md §8 #2/#3)");
265
+ }
266
+ }
267
+ }
268
+ #endif
269
+
270
+ return lifted;
271
+ }
272
+
273
+ /* ca_strip_face(src) — walk down through Face parents to the storage
274
+ * CArray. Called at kernel_iterator entry and lazy chain endpoints
275
+ * where downstream code needs to observe the storage layout, not the
276
+ * Face surface. */
277
+ CArray *
278
+ ca_strip_face (CArray *src)
279
+ {
280
+ while (src && ca_is_face(src)) {
281
+ src = ((CAView *) src)->parent;
282
+ }
283
+ return src;
284
+ }
285
+
286
+ /* rb_ca_strip_face_value(v) — VALUE-level counterpart to ca_strip_face.
287
+ * Walks down the Face `parent` ivar chain and returns the root storage
288
+ * VALUE (a non-Face CArray). Non-Face input is returned as-is.
289
+ *
290
+ * Used by ordering / search kernel dispatchers where the switch on
291
+ * data_type must see storage type instead of Face surface (CA_FIXLEN),
292
+ * and where subsequent `rb_ca_template*` calls must not re-lift the
293
+ * result into a Face wrapper (index / addr / linear outputs are not
294
+ * Face-semantic). Complements ca_strip_face on the C-struct axis. */
295
+ VALUE
296
+ rb_ca_strip_face_value (VALUE v)
297
+ {
298
+ CArray *ca;
299
+ if ( ! rb_obj_is_kind_of(v, rb_cCArray) ) return v;
300
+ TypedData_Get_Struct(v, CArray, &carray_data_type, ca);
301
+ while ( ca_is_face(ca) ) {
302
+ v = rb_ca_parent(v);
303
+ TypedData_Get_Struct(v, CArray, &carray_data_type, ca);
304
+ }
305
+ return v;
306
+ }
307
+
308
+ /* CAFace — abstract marker class used as the code-organisation relay
309
+ for the Face mechanism. The semantic gate is CA_FLAG_IS_FACE only,
310
+ read via ca_is_face(); the class hierarchy is organisation, not the
311
+ basis for the predicate. CAObject-derived Faces satisfy
312
+ ca_is_face()=true without inheriting CAFace at the Ruby level.
313
+
314
+ caface_data_type is a TypedData chain entry with no callbacks;
315
+ subclasses provide their own dmark / dfree / dsize.
316
+
317
+ rb_cCAFace's allocator is sealed off so the class cannot be
318
+ instantiated directly; it exists only as the inheritance target
319
+ for concrete Faces (CATime / CATimedelta / CARecord / ...). */
320
+
321
+ VALUE rb_cCAFace;
322
+
323
+ const rb_data_type_t caface_data_type = {
324
+ .wrap_struct_name = "CAFace",
325
+ .parent = &caview_data_type,
326
+ .function = {
327
+ .dmark = NULL, /* subclass overrides */
328
+ .dfree = NULL, /* subclass overrides */
329
+ .dsize = NULL, /* subclass overrides */
330
+ },
331
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
332
+ };
333
+
334
+ static VALUE
335
+ rb_ca_face_s_allocate_forbidden (VALUE klass)
336
+ {
337
+ rb_raise(rb_eTypeError,
338
+ "CAFace is abstract; instantiate a concrete Face subclass "
339
+ "(CATime / CATimedelta / ...)");
340
+ }
341
+
342
+ /* Face-local C dispatch table for storage_to_scalar (the read/decode
343
+ direction). Sized to CA_OBJ_TYPE_MAX so any registered obj_type can be
344
+ indexed directly; file-scope zero-init leaves unregistered slots NULL. */
345
+ ca_face_storage_to_scalar_fn ca_face_storage_to_scalar_table[CA_OBJ_TYPE_MAX];
346
+
347
+ void
348
+ ca_face_register_storage_to_scalar (int obj_type, ca_face_storage_to_scalar_fn fn)
349
+ {
350
+ if ( obj_type < 0 || obj_type >= CA_OBJ_TYPE_MAX ) {
351
+ rb_raise(rb_eArgError,
352
+ "ca_face_register_storage_to_scalar: obj_type %d out of range",
353
+ obj_type);
354
+ }
355
+ ca_face_storage_to_scalar_table[obj_type] = fn;
356
+ }
357
+
358
+ /* Face-local C dispatch table for scalar_to_storage (the write/encode
359
+ direction), the mirror of the decode table above. Unregistered slots
360
+ stay NULL; a Face without a C fast path relies on the Ruby fallback in
361
+ ca_face_scalar_to_storage (carray_cast.c). */
362
+ ca_face_scalar_to_storage_fn ca_face_scalar_to_storage_table[CA_OBJ_TYPE_MAX];
363
+
364
+ void
365
+ ca_face_register_scalar_to_storage (int obj_type, ca_face_scalar_to_storage_fn fn)
366
+ {
367
+ if ( obj_type < 0 || obj_type >= CA_OBJ_TYPE_MAX ) {
368
+ rb_raise(rb_eArgError,
369
+ "ca_face_register_scalar_to_storage: obj_type %d out of range",
370
+ obj_type);
371
+ }
372
+ ca_face_scalar_to_storage_table[obj_type] = fn;
373
+ }
374
+
375
+ /* -- Face state homogeneity check -- */
376
+
377
+ ca_face_state_compatible_fn ca_face_state_compatible_table[CA_OBJ_TYPE_MAX];
378
+
379
+ void
380
+ ca_face_register_state_compatible (int obj_type, ca_face_state_compatible_fn fn)
381
+ {
382
+ if ( obj_type < 0 || obj_type >= CA_OBJ_TYPE_MAX ) {
383
+ rb_raise(rb_eArgError,
384
+ "ca_face_register_state_compatible: obj_type %d out of range",
385
+ obj_type);
386
+ }
387
+ ca_face_state_compatible_table[obj_type] = fn;
388
+ }
389
+
390
+ /* Detect whether `va`'s class overrides `face_state_compatible?` (= the
391
+ method is owned by a class other than rb_cCArray). Uses #method / #owner
392
+ reflection; called only on the cold path (no C hook registered). */
393
+ static int
394
+ ca_face_has_ruby_state_check (VALUE va)
395
+ {
396
+ static ID id_face_state_compatible_p = 0;
397
+ static ID id_method = 0;
398
+ static ID id_owner = 0;
399
+ VALUE m, owner;
400
+ if ( id_face_state_compatible_p == 0 ) {
401
+ id_face_state_compatible_p = rb_intern("face_state_compatible?");
402
+ id_method = rb_intern("method");
403
+ id_owner = rb_intern("owner");
404
+ }
405
+ m = rb_funcall(va, id_method, 1, ID2SYM(id_face_state_compatible_p));
406
+ owner = rb_funcall(m, id_owner, 0);
407
+ return ( owner != rb_cCArray ) ? 1 : 0;
408
+ }
409
+
410
+ int
411
+ ca_face_state_compatible (VALUE va, CArray *a, VALUE vb, CArray *b)
412
+ {
413
+ ca_face_state_compatible_fn fn = ca_face_state_compatible_table[a->obj_type];
414
+ if ( fn != NULL ) {
415
+ return fn(a, b);
416
+ }
417
+ if ( ca_face_has_ruby_state_check(va) ) {
418
+ VALUE r = rb_funcall(va, rb_intern("face_state_compatible?"), 1, vb);
419
+ return RTEST(r) ? 1 : 0;
420
+ }
421
+ return 1;
422
+ }
423
+
424
+ /* Ruby-side: CArray#face_state_compatible?(other) */
425
+ static VALUE
426
+ rb_ca_face_state_compatible_p (VALUE self, VALUE other)
427
+ {
428
+ CArray *a, *b;
429
+
430
+ if ( ! rb_obj_is_kind_of(other, rb_cCArray) ) {
431
+ rb_raise(rb_eArgError,
432
+ "face_state_compatible?: argument must be a CArray");
433
+ }
434
+ TypedData_Get_Struct(self, CArray, &carray_data_type, a);
435
+ TypedData_Get_Struct(other, CArray, &carray_data_type, b);
436
+ if ( ! ca_is_face(a) || ! ca_is_face(b) ) {
437
+ rb_raise(rb_eArgError,
438
+ "face_state_compatible?: both receiver and argument must be Face");
439
+ }
440
+ if ( a->obj_type != b->obj_type ) {
441
+ rb_raise(rb_eArgError,
442
+ "face_state_compatible?: must be the same Face class");
443
+ }
444
+ return ca_face_state_compatible(self, a, other, b) ? Qtrue : Qfalse;
445
+ }
446
+
447
+ /* -- Face state portability -- */
448
+
449
+ /* int8_t per slot to distinguish "unregistered" (-1, dispatch falls
450
+ through to Ruby fallback) from "registered as not-portable"
451
+ (0, e.g. CAConstString) and "registered as portable" (1). */
452
+ int8_t ca_face_state_portable_table[CA_OBJ_TYPE_MAX];
453
+
454
+ /* Per-obj_type bound Ruby class method returning the registered value.
455
+ Defined once for each obj_type that registers a value, so user-facing
456
+ `<FaceClass>.face_state_portable?` reflects the C table without
457
+ needing a reverse-lookup at every call. */
458
+ static VALUE
459
+ rb_ca_s_face_state_portable_true (VALUE klass)
460
+ {
461
+ return Qtrue;
462
+ }
463
+
464
+ static VALUE
465
+ rb_ca_s_face_state_portable_false (VALUE klass)
466
+ {
467
+ return Qfalse;
468
+ }
469
+
470
+ void
471
+ ca_face_register_state_portable (int obj_type, int portable)
472
+ {
473
+ if ( obj_type < 0 || obj_type >= CA_OBJ_TYPE_MAX ) {
474
+ rb_raise(rb_eArgError,
475
+ "ca_face_register_state_portable: obj_type %d out of range",
476
+ obj_type);
477
+ }
478
+ ca_face_state_portable_table[obj_type] = portable ? 1 : 0;
479
+ /* Mirror to a singleton method on the class so Ruby-side
480
+ introspection (<FaceClass>.face_state_portable?) agrees with the
481
+ C table. CAREFUL: ca_class[obj_type] must be set by
482
+ ca_install_obj_type before this call — Faces register from
483
+ within their Init_*, which runs after ca_install_obj_type. */
484
+ if ( !NIL_P(ca_class[obj_type]) ) {
485
+ rb_define_singleton_method(ca_class[obj_type], "face_state_portable?",
486
+ portable ? rb_ca_s_face_state_portable_true
487
+ : rb_ca_s_face_state_portable_false,
488
+ 0);
489
+ }
490
+ }
491
+
492
+ /* Detect whether `klass`'s singleton class owns `face_state_portable?`
493
+ (= the predicate is defined on the Face class itself, not inherited
494
+ from rb_cCArray's metaclass). Cold path; only consulted when no C
495
+ entry is registered. */
496
+ static int
497
+ ca_face_class_has_portable_method (VALUE klass)
498
+ {
499
+ static ID id_face_state_portable_p = 0;
500
+ static ID id_method = 0;
501
+ static ID id_owner = 0;
502
+ VALUE m, owner;
503
+ if ( NIL_P(klass) ) return 0;
504
+ if ( id_face_state_portable_p == 0 ) {
505
+ id_face_state_portable_p = rb_intern("face_state_portable?");
506
+ id_method = rb_intern("method");
507
+ id_owner = rb_intern("owner");
508
+ }
509
+ if ( !rb_respond_to(klass, id_face_state_portable_p) ) return 0;
510
+ m = rb_funcall(klass, id_method, 1, ID2SYM(id_face_state_portable_p));
511
+ owner = rb_funcall(m, id_owner, 0);
512
+ /* owner is a (singleton) class — if it isn't rb_singleton_class(rb_cCArray)
513
+ or the singleton chain rooted at CArray, the Face class (or its
514
+ ancestor) has its own definition. */
515
+ return ( owner != rb_singleton_class(rb_cCArray) ) ? 1 : 0;
516
+ }
517
+
518
+ int
519
+ ca_face_state_portable (int obj_type, VALUE klass)
520
+ {
521
+ int8_t entry;
522
+ if ( obj_type < 0 || obj_type >= CA_OBJ_TYPE_MAX ) return 1;
523
+ entry = ca_face_state_portable_table[obj_type];
524
+ if ( entry >= 0 ) {
525
+ return entry ? 1 : 0;
526
+ }
527
+ if ( !NIL_P(klass) && ca_face_class_has_portable_method(klass) ) {
528
+ VALUE r = rb_funcall(klass, rb_intern("face_state_portable?"), 0);
529
+ return RTEST(r) ? 1 : 0;
530
+ }
531
+ return 1; /* default portable */
532
+ }
533
+
534
+ /* CArray.face_state_portable? — class method on CArray. Subclasses
535
+ (Face classes) override by defining their own `def self.face_state_portable?`.
536
+ This default returns true (= 1) for non-Face CArray classes; the actual
537
+ per-Face decision goes through ca_face_state_portable from C callers. */
538
+ static VALUE
539
+ rb_ca_s_face_state_portable_p (VALUE klass)
540
+ {
541
+ return Qtrue;
542
+ }
543
+
544
+ /* CArray#face_lift(face_parent) — Ruby surface for ca_face_lift.
545
+ Used by CArray.stack to re-wrap a raw CAStack with the homogeneous
546
+ Face class carried by face_parent (typically list[0] after the
547
+ promote_list pass). Delegates the data_type rewrite and optional
548
+ copy_state callback to ca_face_lift. */
549
+ static VALUE
550
+ rb_ca_face_lift_method (VALUE self, VALUE face_parent)
551
+ {
552
+ CArray *fp;
553
+ if ( ! rb_obj_is_kind_of(face_parent, rb_cCArray) ) {
554
+ rb_raise(rb_eArgError, "face_lift: face_parent must be a CArray");
555
+ }
556
+ TypedData_Get_Struct(face_parent, CArray, &carray_data_type, fp);
557
+ if ( ! ca_is_face(fp) ) {
558
+ rb_raise(rb_eArgError, "face_lift: face_parent must be a Face instance");
559
+ }
560
+ return ca_face_lift(self, face_parent);
561
+ }
562
+
563
+ /* ca_face_reconcile_comparison(pself, pother) — Face gate for the element-
564
+ * wise comparison operators (< <= > >=, eq, ne; <=> composes from > and <).
565
+ * Mirrors the search query gate (PROPOSAL_FACE_ORDERING_GATE): when the LHS
566
+ * is a Face over NUMERIC storage, memcmp on the surface fixlen bytes would
567
+ * mis-order the storage, so descend to storage (ORDERABLE licenses that the
568
+ * numeric order equals the surface order) and reconcile the RHS:
569
+ *
570
+ * - RHS is a Face: COMPARABLE self strips it; else it must respond to
571
+ * to_comparable (reconcile to self's reference, e.g. unit alignment,
572
+ * which may raise on loss); else raise.
573
+ * - RHS is plain (bare number / non-Face array / Scalar): a direct storage
574
+ * compare, which only a COMPARABLE self licenses -- a non-COMPARABLE
575
+ * Face (e.g. a unit-bearing CATime) rejects it.
576
+ *
577
+ * A Face over FIXLEN storage (CARecord / string faces) is left untouched:
578
+ * memcmp on its bytes is already the correct order/equality, same as a plain
579
+ * fixlen array. Both operands are rebound to storage via *pself / *pother.
580
+ * No-op when self is not a Face. */
581
+ void
582
+ ca_face_reconcile_comparison (volatile VALUE *pself, volatile VALUE *pother)
583
+ {
584
+ VALUE self = *pself, other = *pother;
585
+ CArray *sc, *storage;
586
+
587
+ if ( ! rb_obj_is_kind_of(self, rb_cCArray) ) {
588
+ return;
589
+ }
590
+ TypedData_Get_Struct(self, CArray, &carray_data_type, sc);
591
+ if ( ! ca_is_face(sc) ) {
592
+ return;
593
+ }
594
+
595
+ storage = ca_strip_face(sc);
596
+ if ( storage->data_type == CA_FIXLEN ) {
597
+ /* genuine fixlen semantics: memcmp is the correct comparison, leave
598
+ the Face(s) in place (same result as a plain fixlen array). */
599
+ return;
600
+ }
601
+
602
+ if ( ! ca_test_flag(sc, CA_FLAG_FACE_ORDERABLE_STORAGE) ) {
603
+ rb_raise(rb_eArgError,
604
+ "comparison: Face operand (%s) is not orderable by storage; "
605
+ "use ca.parent to descend to storage",
606
+ rb_obj_classname(self));
607
+ }
608
+
609
+ {
610
+ VALUE self_ref = self; /* reference before stripping (for to_comparable) */
611
+ int comparable = ca_test_flag(sc, CA_FLAG_FACE_COMPARABLE_STORAGE);
612
+ int other_is_face = 0;
613
+
614
+ if ( rb_obj_is_kind_of(other, rb_cCArray) ) {
615
+ CArray *oc;
616
+ TypedData_Get_Struct(other, CArray, &carray_data_type, oc);
617
+ other_is_face = ca_is_face(oc);
618
+ }
619
+
620
+ if ( comparable ) {
621
+ /* directly comparable Face reference: strip a Face operand, take a
622
+ plain operand as-is (storage compare is licensed). */
623
+ if ( other_is_face ) {
624
+ other = rb_ca_strip_face_value(other);
625
+ }
626
+ }
627
+ else if ( rb_respond_to(self_ref, rb_intern("to_comparable")) ) {
628
+ /* non-COMPARABLE Face reference: the reference brings the operand into
629
+ its own space (unit alignment / instant lift), whatever the operand
630
+ type -- Face CArray, our Scalar, a Ruby Time / DateTime, etc. The
631
+ reference's to_comparable raises if it cannot reconcile it. */
632
+ other = rb_funcall(self_ref, rb_intern("to_comparable"), 1, other);
633
+ other = rb_ca_strip_face_value(other);
634
+ }
635
+ else {
636
+ rb_raise(rb_eArgError,
637
+ "comparison: non-comparable Face reference (%s) has no "
638
+ "to_comparable to reconcile the operand; use ca.parent to "
639
+ "compare the hidden storage explicitly",
640
+ rb_obj_classname(self_ref));
641
+ }
642
+
643
+ *pself = rb_ca_strip_face_value(self);
644
+ *pother = other;
645
+ }
646
+ }
647
+
648
+ void
649
+ Init_ca_face (void)
650
+ {
651
+ int i;
652
+
653
+ /* CAREFUL: rb_cCAView must already exist (defined in ruby_carray.c
654
+ ahead of this Init) — CAFace inherits from it. */
655
+ rb_cCAFace = rb_define_class("CAFace", rb_cCAView);
656
+ rb_define_alloc_func(rb_cCAFace, rb_ca_face_s_allocate_forbidden);
657
+
658
+ /* Initialise the portable table to -1 for every slot so dispatch
659
+ can distinguish "explicitly registered 0" (CAConstString) from
660
+ "never registered" (falls through to the Ruby default). */
661
+ for ( i = 0; i < CA_OBJ_TYPE_MAX; i++ ) {
662
+ ca_face_state_portable_table[i] = -1;
663
+ }
664
+
665
+ rb_define_method(rb_cCArray, "face_state_compatible?",
666
+ rb_ca_face_state_compatible_p, 1);
667
+ rb_define_singleton_method(rb_cCArray, "face_state_portable?",
668
+ rb_ca_s_face_state_portable_p, 0);
669
+ rb_define_method(rb_cCArray, "face_lift", rb_ca_face_lift_method, 1);
670
+ }