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
@@ -0,0 +1,965 @@
1
+ /* ---------------------------------------------------------------------------
2
+
3
+ ca_obj_const_string.c
4
+
5
+ CAConstString — read-only variable-length UTF-8 string column
6
+ (Arrow string layout via Face).
7
+
8
+ A Face whose tail carries the string `buffer` + encoding. Parent
9
+ (storage) is a fixlen-16 entity holding one `(int64 start, int64 end)`
10
+ pair per element; the buffer is a frozen byte String laid out as a
11
+ pure concatenation of the element bytes (= Arrow values buffer, no
12
+ per-record length prefix). Decoding element i: read the pair from
13
+ `parent[i]`, then `bytes = buf + start`, `len = end - start`. Each
14
+ element is self-describing (carries its own byte range), so gather /
15
+ sort / boolean-select views over the pairs decode correctly with no
16
+ buffer copy.
17
+
18
+ Surface data_type = CA_FIXLEN (numeric gate): mkkernel dispatch
19
+ routes numeric ops to `ca_*_not_implement` -> raise, preventing a
20
+ silent numeric strip of a CAConstString. Storage is fixlen-16 so
21
+ the byte size is told truthfully; the FIXLEN surface seals off
22
+ only the numeric meaning.
23
+
24
+ High-duplication columns belong in CACategorical (= Arrow
25
+ DictionaryArray), not here: CAConstString stores every element's
26
+ bytes once in logical order, without offset-sharing dedup.
27
+
28
+ Sibling of ca_obj_record.c (Face + VALUE tail + custom dmark) and
29
+ ca_obj_time.c (int64 Face + storage_to_scalar fast path).
30
+
31
+ --------------------------------------------------------------------------- */
32
+
33
+ #include "carray.h"
34
+ #include "ca_obj_face.h"
35
+ #include <ruby/encoding.h>
36
+
37
+ /* === struct layout (identical to the CATime prefix + tail) === */
38
+ typedef struct {
39
+ /* === CAView prefix === */
40
+ int16_t obj_type;
41
+ int8_t data_type; /* CA_FIXLEN surface (numeric gate); storage is int64 */
42
+ int8_t ndim;
43
+ int32_t flags; /* CA_FLAG_IS_FACE set */
44
+ ca_size_t bytes; /* sizeof(int64_t) = 8 (one offset per element) */
45
+ ca_size_t elements;
46
+ ca_size_t *dim;
47
+ char *ptr;
48
+ CArray *mask;
49
+ char *_pool; /* framework-managed pool buffer (NULL = legacy ALLOC_N path). */
50
+ CArray *parent; /* fixlen-16 (start,end) pair entity (offset-source) */
51
+ uint32_t attach;
52
+ uint8_t nosync;
53
+ /* === Face tail === */
54
+ VALUE buffer; /* frozen byte String (pure concat), marked by custom dmark */
55
+ int encoding_id; /* rb_enc_to_index() value */
56
+ } CAConstString;
57
+
58
+ static size_t
59
+ ca_const_string_dsize (const void *ap)
60
+ {
61
+ const CAConstString *ca = (const CAConstString *) ap;
62
+ return sizeof(CAConstString) + ca->ndim * sizeof(ca_size_t);
63
+ }
64
+
65
+ /* Pool framework hooks: single ndim-sized tail (dim) in the _pool
66
+ buffer (uniform alloc / free discipline). */
67
+ static size_t
68
+ ca_const_string_pool_bytes (int8_t ndim)
69
+ {
70
+ ca_size_t n = (ndim > 0) ? ndim : 1;
71
+ return (size_t) n * sizeof(ca_size_t);
72
+ }
73
+
74
+ static void
75
+ ca_const_string_pool_init (void *ap, int8_t ndim)
76
+ {
77
+ CAConstString *ca = (CAConstString *) ap;
78
+ ca->dim = (ca_size_t *) ca->_pool;
79
+ }
80
+
81
+ /* Custom dmark: ca_mark walks the standard prefix (CA_OBJECT elements, of
82
+ which a CAConstString has none); we additionally mark the buffer tail VALUE.
83
+ parent retention is via rb_ca_set_parent ivar (= not dmark), same as
84
+ CARecord / CATime. */
85
+ static void
86
+ ca_const_string_mark (void *ap)
87
+ {
88
+ CAConstString *ca = (CAConstString *) ap;
89
+ ca_mark(ca);
90
+ rb_gc_mark(ca->buffer);
91
+ }
92
+
93
+ const rb_data_type_t catext_data_type = {
94
+ .parent = &caface_data_type,
95
+ .wrap_struct_name = "CAConstString",
96
+ .function = {
97
+ .dmark = ca_const_string_mark,
98
+ .dfree = ca_free,
99
+ .dsize = ca_const_string_dsize,
100
+ .dcompact = NULL
101
+ },
102
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
103
+ };
104
+
105
+ static int8_t CA_OBJ_CONST_STRING;
106
+ static VALUE rb_cCAConstString;
107
+
108
+ /* yard:
109
+ class CAConstString < CAFace # :nodoc:
110
+ end
111
+ */
112
+
113
+ /* ------------------------------------------------------------------- */
114
+
115
+ int
116
+ ca_const_string_setup (CAConstString *ca, CArray *parent, VALUE buffer, int encoding_id)
117
+ {
118
+ if ( parent->data_type != CA_FIXLEN || parent->bytes != 2 * (ca_size_t) sizeof(int64_t) ) {
119
+ rb_raise(rb_eTypeError,
120
+ "CAConstString requires a fixlen-16 (start,end) pair storage "
121
+ "(parent.data_type must be CA_FIXLEN with bytes == 16)");
122
+ }
123
+
124
+ ca->obj_type = CA_OBJ_CONST_STRING;
125
+ /* FIXLEN surface auto-gates mkkernel numeric dispatch. */
126
+ ca->data_type = CA_FIXLEN;
127
+ ca->flags = CA_FLAG_IS_FACE;
128
+ ca->ndim = parent->ndim;
129
+ ca->bytes = 2 * sizeof(int64_t); /* (start,end) pair per element */
130
+ ca->elements = parent->elements;
131
+ ca->ptr = NULL;
132
+ ca->mask = NULL;
133
+ if ( ! ca->_pool ) {
134
+ ca->dim = ALLOC_N(ca_size_t, parent->ndim);
135
+ }
136
+ memcpy(ca->dim, parent->dim, sizeof(ca_size_t) * parent->ndim);
137
+
138
+ ca->parent = parent;
139
+ ca->attach = 0;
140
+ ca->nosync = 0;
141
+
142
+ ca->buffer = buffer;
143
+ ca->encoding_id = encoding_id;
144
+
145
+ if ( ca_has_mask(parent) ) {
146
+ ca_create_mask((CArray *) ca);
147
+ }
148
+
149
+ if ( ca_is_scalar(parent) ) {
150
+ ca_set_flag(ca, CA_FLAG_SCALAR);
151
+ }
152
+
153
+ /* CAConstString is always read-only (Invariant 4). */
154
+ ca_set_flag(ca, CA_FLAG_READ_ONLY);
155
+
156
+ return 0;
157
+ }
158
+
159
+ CAConstString *
160
+ ca_const_string_new (CArray *parent, VALUE buffer, int encoding_id)
161
+ {
162
+ CAConstString *ca = (CAConstString *) ca_array_alloc(CA_OBJ_CONST_STRING, parent->ndim);
163
+ ca_const_string_setup(ca, parent, buffer, encoding_id);
164
+ return ca;
165
+ }
166
+
167
+ static void
168
+ free_ca_text (void *ap)
169
+ {
170
+ CAConstString *ca = (CAConstString *) ap;
171
+ if ( ca != NULL ) {
172
+ ca_free(ca->mask);
173
+ if ( ca->_pool ) {
174
+ ca_array_free(ca); /* dim lives in _pool */
175
+ }
176
+ else {
177
+ xfree(ca->dim);
178
+ xfree(ca);
179
+ }
180
+ }
181
+ }
182
+
183
+ /* ------------------------------------------------------------------- */
184
+
185
+ static void *
186
+ ca_const_string_func_clone (void *ap)
187
+ {
188
+ CAConstString *ca = (CAConstString *) ap;
189
+ /* buffer is immutable → sharing the same VALUE reference is safe + cheap
190
+ (Invariant 7, MEMO §3). */
191
+ return ca_const_string_new(ca->parent, ca->buffer, ca->encoding_id);
192
+ }
193
+
194
+ static void
195
+ ca_const_string_func_allocate (void *ap)
196
+ {
197
+ CAConstString *ca = (CAConstString *) ap;
198
+ /* alias: parent attach + alias parent->ptr (Face has identical layout) */
199
+ ca_attach(ca->parent);
200
+ ca->ptr = ca->parent->ptr;
201
+ }
202
+
203
+ static void
204
+ ca_const_string_func_create_mask (void *ap)
205
+ {
206
+ CAConstString *ca = (CAConstString *) ap;
207
+ ca_update_mask(ca->parent);
208
+ if ( ! ca->parent->mask ) {
209
+ ca_create_mask(ca->parent);
210
+ }
211
+ ca->mask = (CArray *) ca_refer_new(ca->parent->mask,
212
+ CA_BOOLEAN, ca->ndim, ca->dim, 0, 0);
213
+ }
214
+
215
+ ca_operation_function_t ca_const_string_func = {
216
+ -1, /* CA_OBJ_CONST_STRING — filled by ca_install_obj_type */
217
+ CA_VIEW_ARRAY,
218
+ free_ca_text,
219
+ ca_const_string_func_clone,
220
+ ca_const_string_func_allocate,
221
+ ca_face_attach,
222
+ ca_face_sync,
223
+ ca_face_detach,
224
+ ca_face_fill_data,
225
+ ca_const_string_func_create_mask,
226
+ ca_face_xfer_index,
227
+ ca_face_xfer_addrs,
228
+ NULL, /* fold_stride: identity Face is not foldable */
229
+ ca_face_xfer_stride,
230
+ ca_face_xfer_all
231
+ };
232
+
233
+ /* ------------------------------------------------------------------- */
234
+
235
+ /* decode one record from the buffer given its [start,end) byte range →
236
+ frozen String. buffer layout: pure concatenation (Arrow values layout),
237
+ length = end - start. encoding from ca->encoding_id. */
238
+ static VALUE
239
+ ca_const_string_decode (CAConstString *ca, int64_t start, int64_t end)
240
+ {
241
+ const char *buf = RSTRING_PTR(ca->buffer);
242
+ long buflen = RSTRING_LEN(ca->buffer);
243
+ int64_t len = end - start;
244
+ VALUE s;
245
+
246
+ if ( start < 0 || len < 0 || end > (int64_t) buflen ) {
247
+ rb_raise(rb_eIndexError,
248
+ "CAConstString: record [%lld,%lld) out of buffer range (%ld bytes)",
249
+ (long long) start, (long long) end, buflen);
250
+ }
251
+ s = rb_enc_str_new(buf + start, (long) len,
252
+ rb_enc_from_index(ca->encoding_id));
253
+ return rb_str_freeze(s);
254
+ }
255
+
256
+ /* storage_to_scalar in C (= skip rb_funcall on the per-cell fetch hot
257
+ path). Since the Face surface is CA_FIXLEN (16 bytes), fetch delivers a
258
+ 16-byte raw String = the (start,end) int64 pair. Decode the buffer slice
259
+ → frozen Ruby String (plain frozen String, no Scalar wrapper). */
260
+ static VALUE
261
+ rb_ca_const_string_storage_to_scalar (VALUE self, VALUE raw)
262
+ {
263
+ CAConstString *ca;
264
+ int64_t pair[2];
265
+
266
+ TypedData_Get_Struct(self, CAConstString, &catext_data_type, ca);
267
+
268
+ if ( TYPE(raw) != T_STRING || RSTRING_LEN(raw) != (long) sizeof(pair) ) {
269
+ rb_raise(rb_eArgError,
270
+ "CAConstString#storage_to_scalar: expected a %lu-byte (start,end) cell",
271
+ (unsigned long) sizeof(pair));
272
+ }
273
+ memcpy(pair, RSTRING_PTR(raw), sizeof(pair));
274
+
275
+ return ca_const_string_decode(ca, pair[0], pair[1]);
276
+ }
277
+
278
+ /* ------------------------------------------------------------------- */
279
+
280
+ /* CAConstString.wrap(int64_offsets, buffer:, encoding:) — low-level Face wrap of an
281
+ existing fixlen-16 (start,end) pair entity + pure-concatenation buffer. */
282
+ static VALUE
283
+ rb_ca_const_string_wrap (VALUE parent_val, VALUE buffer, int encoding_id)
284
+ {
285
+ CArray *parent;
286
+ CAConstString *ca;
287
+ VALUE obj;
288
+
289
+ TypedData_Get_Struct(parent_val, CArray, &carray_data_type, parent);
290
+ if ( parent->data_type != CA_FIXLEN || parent->bytes != 2 * (ca_size_t) sizeof(int64_t) ) {
291
+ rb_raise(rb_eTypeError,
292
+ "CAConstString.wrap requires a fixlen-16 (start,end) pair entity "
293
+ "(got data_type=%d, bytes=%ld)",
294
+ parent->data_type, (long) parent->bytes);
295
+ }
296
+ if ( TYPE(buffer) != T_STRING ) {
297
+ rb_raise(rb_eTypeError, "CAConstString.wrap: buffer: must be a String");
298
+ }
299
+
300
+ ca = ca_const_string_new(parent, rb_str_freeze(buffer), encoding_id);
301
+ obj = TypedData_Wrap_Struct(rb_cCAConstString, &catext_data_type, ca);
302
+ rb_ca_set_parent(obj, parent_val); /* pin parent VALUE for GC */
303
+ return obj;
304
+ }
305
+
306
+ static VALUE
307
+ rb_ca_const_string_wrap_method (int argc, VALUE *argv, VALUE klass)
308
+ {
309
+ VALUE parent_val, ropt, rbuffer = Qnil, renc = Qnil;
310
+ int encoding_id;
311
+
312
+ rb_scan_args(argc, argv, "1:", &parent_val, &ropt);
313
+ rb_scan_options(ropt, "buffer,encoding", &rbuffer, &renc);
314
+ if ( NIL_P(rbuffer) ) {
315
+ rb_raise(rb_eArgError, "CAConstString.wrap: buffer: keyword is required");
316
+ }
317
+ encoding_id = NIL_P(renc) ? rb_utf8_encindex()
318
+ : rb_to_encoding_index(renc);
319
+
320
+ return rb_ca_const_string_wrap(parent_val, rbuffer, encoding_id);
321
+ }
322
+
323
+ /* Build a CAConstString from a Ruby Array of Strings / nils in a single C
324
+ pass: pure-concatenation buffer (Arrow values layout) + fixlen-16
325
+ (start,end) pair entity, with a mask for nil elements. Replaces the
326
+ per-element Ruby build loop -- no `String#b` copy, no `pack`, no per-row
327
+ method dispatch. `renc` is the target Encoding (nil = UTF-8). */
328
+ static VALUE
329
+ rb_ca_const_string_build (VALUE klass, VALUE values, VALUE renc)
330
+ {
331
+ long i, n;
332
+ int encoding_id;
333
+ rb_encoding *enc;
334
+ volatile VALUE buffer, vpair;
335
+ CArray *pe;
336
+ int64_t *pair;
337
+ boolean8_t *mask = NULL;
338
+ ca_size_t dim[1];
339
+ long hint = 0;
340
+
341
+ Check_Type(values, T_ARRAY);
342
+ n = RARRAY_LEN(values);
343
+ encoding_id = NIL_P(renc) ? rb_utf8_encindex() : rb_to_encoding_index(renc);
344
+ enc = rb_enc_from_index(encoding_id);
345
+
346
+ /* size hint: sum String lengths cheaply (skip non-String coercion here) */
347
+ for ( i = 0; i < n; i++ ) {
348
+ VALUE s = RARRAY_AREF(values, i);
349
+ if ( RB_TYPE_P(s, T_STRING) ) hint += RSTRING_LEN(s);
350
+ }
351
+ buffer = rb_str_buf_new(hint);
352
+ rb_enc_associate_index(buffer, rb_ascii8bit_encindex());
353
+
354
+ dim[0] = (ca_size_t) n;
355
+ vpair = rb_carray_new(CA_FIXLEN, 1, dim, 2 * sizeof(int64_t), NULL);
356
+ TypedData_Get_Struct(vpair, CArray, &carray_data_type, pe);
357
+ pair = (int64_t *) pe->ptr;
358
+
359
+ for ( i = 0; i < n; i++ ) {
360
+ VALUE s = RARRAY_AREF(values, i);
361
+ if ( NIL_P(s) ) {
362
+ if ( ! mask ) {
363
+ ca_create_mask(pe);
364
+ mask = (boolean8_t *) pe->mask->ptr;
365
+ }
366
+ mask[i] = 1;
367
+ pair[2 * i] = 0;
368
+ pair[2 * i + 1] = 0;
369
+ continue;
370
+ }
371
+ if ( ! RB_TYPE_P(s, T_STRING) ) {
372
+ s = rb_obj_as_string(s); /* to_s */
373
+ }
374
+ /* encoding gate: match the column encoding, or pure-ASCII passes through */
375
+ if ( rb_enc_get_index(s) != encoding_id && ! rb_enc_str_asciionly_p(s) ) {
376
+ rb_raise(rb_eArgError,
377
+ "CArray.const_string: element encoding %s does not match column "
378
+ "encoding %s (use String#encode to convert explicitly)",
379
+ rb_enc_name(rb_enc_get(s)), rb_enc_name(enc));
380
+ }
381
+ pair[2 * i] = (int64_t) RSTRING_LEN(buffer);
382
+ rb_str_cat(buffer, RSTRING_PTR(s), RSTRING_LEN(s));
383
+ pair[2 * i + 1] = (int64_t) RSTRING_LEN(buffer);
384
+ }
385
+
386
+ return rb_ca_const_string_wrap(vpair, rb_str_freeze(buffer), encoding_id);
387
+ }
388
+
389
+ /* CAConstString#encoding → Encoding */
390
+ static VALUE
391
+ rb_ca_const_string_encoding (VALUE self)
392
+ {
393
+ CAConstString *ca;
394
+ TypedData_Get_Struct(self, CAConstString, &catext_data_type, ca);
395
+ return rb_enc_from_encoding(rb_enc_from_index(ca->encoding_id));
396
+ }
397
+
398
+ /* `CAConstString#buffer` — returns the frozen internal byte buffer
399
+ (length-prefix format). Exposes the defining tail state for
400
+ introspection and the Arrow-boundary component-buffer export
401
+ path. */
402
+ static VALUE
403
+ rb_ca_const_string_buffer (VALUE self)
404
+ {
405
+ CAConstString *ca;
406
+ TypedData_Get_Struct(self, CAConstString, &catext_data_type, ca);
407
+ return ca->buffer;
408
+ }
409
+
410
+ /* ------------------------------------------------------------------- */
411
+
412
+ /* copy = compacting deep copy — the only chain-descent point in
413
+ CAConstString. Produces a standalone CAConstString:
414
+ - a fresh contiguous fixlen-16 (start,end) pair entity
415
+ - a fresh compacted buffer, each element's bytes repacked once in the
416
+ view's logical order (pure concatenation = Arrow values layout)
417
+ - encoding preserved, mask carried
418
+ - fully detached from the original buffer / pair-source
419
+ Cost is O(live bytes); a near-full copy is a near-full rebuild (accepted).
420
+ Done at the byte level (no per-element Ruby String boxing). */
421
+ static VALUE
422
+ rb_ca_const_string_copy (VALUE self)
423
+ {
424
+ volatile VALUE vout, newbuf;
425
+ CAConstString *ca;
426
+ CArray *co;
427
+ ca_size_t i, n;
428
+ int64_t *src_pair, *dst_pair;
429
+ boolean8_t *m = NULL;
430
+ const char *oldbuf;
431
+ long oldbuflen;
432
+
433
+ TypedData_Get_Struct(self, CAConstString, &catext_data_type, ca);
434
+
435
+ /* materialize logical-order (start,end) pairs + mask */
436
+ ca_attach((CArray *) ca);
437
+ ca_update_mask((CArray *) ca);
438
+
439
+ n = ca->elements;
440
+ src_pair = (int64_t *) ca->ptr;
441
+ m = ca->mask ? (boolean8_t *) ca->mask->ptr : NULL;
442
+ oldbuf = RSTRING_PTR(ca->buffer);
443
+ oldbuflen = RSTRING_LEN(ca->buffer);
444
+
445
+ /* fresh fixlen-16 pair entity, same shape (N-D preserved) */
446
+ vout = rb_carray_new(CA_FIXLEN, ca->ndim, ca->dim, 2 * sizeof(int64_t), NULL);
447
+ TypedData_Get_Struct(vout, CArray, &carray_data_type, co);
448
+ dst_pair = (int64_t *) co->ptr;
449
+
450
+ /* growable compacted buffer (BINARY) */
451
+ newbuf = rb_str_buf_new(oldbuflen);
452
+ rb_enc_associate_index(newbuf, rb_ascii8bit_encindex());
453
+
454
+ for ( i = 0; i < n; i++ ) {
455
+ int64_t start, end, len;
456
+ if ( m && m[i] ) {
457
+ dst_pair[2 * i] = 0;
458
+ dst_pair[2 * i + 1] = 0;
459
+ continue;
460
+ }
461
+ start = src_pair[2 * i];
462
+ end = src_pair[2 * i + 1];
463
+ len = end - start;
464
+ if ( start < 0 || len < 0 || end > (int64_t) oldbuflen ) {
465
+ ca_detach((CArray *) ca);
466
+ rb_raise(rb_eIndexError,
467
+ "CAConstString#copy: record [%lld,%lld) out of buffer range",
468
+ (long long) start, (long long) end);
469
+ }
470
+ dst_pair[2 * i] = (int64_t) RSTRING_LEN(newbuf);
471
+ rb_str_cat(newbuf, oldbuf + start, (long) len);
472
+ dst_pair[2 * i + 1] = (int64_t) RSTRING_LEN(newbuf);
473
+ }
474
+
475
+ /* carry mask onto the new pair entity */
476
+ if ( m ) {
477
+ boolean8_t *nm;
478
+ ca_create_mask(co);
479
+ nm = (boolean8_t *) co->mask->ptr;
480
+ for ( i = 0; i < n; i++ ) {
481
+ nm[i] = m[i] ? 1 : 0;
482
+ }
483
+ }
484
+
485
+ ca_detach((CArray *) ca);
486
+
487
+ return rb_ca_const_string_wrap(vout, rb_str_freeze(newbuf), ca->encoding_id);
488
+ }
489
+
490
+ /* to_ca descends to the same compacting copy. The result is detached
491
+ from the source buffer by construction, so `writable: true` — a demand
492
+ that writes reach the source — cannot be honoured and is refused. */
493
+ static VALUE
494
+ rb_ca_const_string_to_ca (int argc, VALUE *argv, VALUE self)
495
+ {
496
+ if ( ca_to_ca_writable_arg(argc, argv) ) {
497
+ ca_to_ca_refuse_writable(self);
498
+ }
499
+ return rb_ca_const_string_copy(self);
500
+ }
501
+
502
+ /* ------------------------------------------------------------------- */
503
+ /* Native byte ops: compare / scan record bytes straight out of the
504
+ buffer, without materializing a Ruby String per element. This is
505
+ the layer where CAConstString outperforms `to_object` followed by
506
+ per-cell Ruby String work. */
507
+
508
+ typedef struct {
509
+ CAConstString *ca;
510
+ int64_t *off;
511
+ boolean8_t *m;
512
+ const char *buf;
513
+ long buflen;
514
+ ca_size_t n;
515
+ } ca_const_string_scan_t;
516
+
517
+ static void
518
+ ca_const_string_scan_begin (VALUE self, ca_const_string_scan_t *s)
519
+ {
520
+ TypedData_Get_Struct(self, CAConstString, &catext_data_type, s->ca);
521
+ ca_attach((CArray *) s->ca);
522
+ ca_update_mask((CArray *) s->ca);
523
+ s->off = (int64_t *) s->ca->ptr;
524
+ s->m = s->ca->mask ? (boolean8_t *) s->ca->mask->ptr : NULL;
525
+ s->buf = RSTRING_PTR(s->ca->buffer);
526
+ s->buflen = RSTRING_LEN(s->ca->buffer);
527
+ s->n = s->ca->elements;
528
+ }
529
+
530
+ static void
531
+ ca_const_string_scan_end (ca_const_string_scan_t *s)
532
+ {
533
+ ca_detach((CArray *) s->ca);
534
+ }
535
+
536
+ /* Resolve element i to its record bytes; returns 0 if masked.
537
+ Storage is (start,end) int64 pairs: s->off holds 2 int64 per element,
538
+ so element i occupies s->off[2*i] (start) .. s->off[2*i+1] (end).
539
+ The buffer is a pure concatenation; length = end - start. */
540
+ static inline int
541
+ ca_const_string_record (ca_const_string_scan_t *s, ca_size_t i, const char **pp, int32_t *plen)
542
+ {
543
+ int64_t start, end;
544
+ if ( s->m && s->m[i] ) {
545
+ return 0;
546
+ }
547
+ start = s->off[2 * i];
548
+ end = s->off[2 * i + 1];
549
+ *pp = s->buf + start;
550
+ *plen = (int32_t) (end - start);
551
+ return 1;
552
+ }
553
+
554
+ /* Allocate a boolean output of the same shape; carry the input mask. */
555
+ static VALUE
556
+ ca_const_string_bool_out (ca_const_string_scan_t *s, boolean8_t **op)
557
+ {
558
+ VALUE vout;
559
+ CArray *co;
560
+ vout = rb_carray_new(CA_BOOLEAN, s->ca->ndim, s->ca->dim, 0, NULL);
561
+ TypedData_Get_Struct(vout, CArray, &carray_data_type, co);
562
+ *op = (boolean8_t *) co->ptr;
563
+ if ( s->m ) {
564
+ ca_size_t i;
565
+ boolean8_t *nm;
566
+ ca_create_mask(co);
567
+ nm = (boolean8_t *) co->mask->ptr;
568
+ for ( i = 0; i < s->n; i++ ) {
569
+ nm[i] = s->m[i] ? 1 : 0;
570
+ }
571
+ }
572
+ return vout;
573
+ }
574
+
575
+ /* memmem fallback (not in all libc). */
576
+ static const char *
577
+ ca_const_string_memmem (const char *hay, long hlen, const char *need, long nlen)
578
+ {
579
+ long i;
580
+ if ( nlen == 0 ) return hay;
581
+ if ( nlen > hlen ) return NULL;
582
+ for ( i = 0; i <= hlen - nlen; i++ ) {
583
+ if ( hay[i] == need[0] && memcmp(hay + i, need, nlen) == 0 ) {
584
+ return hay + i;
585
+ }
586
+ }
587
+ return NULL;
588
+ }
589
+
590
+ /* CAConstString#byte_length → int64 CArray of per-element byte lengths. */
591
+ static VALUE
592
+ rb_ca_const_string_byte_length (VALUE self)
593
+ {
594
+ ca_const_string_scan_t s;
595
+ VALUE vout;
596
+ CArray *co;
597
+ int64_t *op;
598
+ const char *p;
599
+ int32_t len;
600
+ ca_size_t i;
601
+
602
+ ca_const_string_scan_begin(self, &s);
603
+ vout = rb_carray_new(CA_INT64, s.ca->ndim, s.ca->dim, 0, NULL);
604
+ TypedData_Get_Struct(vout, CArray, &carray_data_type, co);
605
+ op = (int64_t *) co->ptr;
606
+ for ( i = 0; i < s.n; i++ ) {
607
+ op[i] = ca_const_string_record(&s, i, &p, &len) ? (int64_t) len : 0;
608
+ }
609
+ if ( s.m ) {
610
+ boolean8_t *nm;
611
+ ca_create_mask(co);
612
+ nm = (boolean8_t *) co->mask->ptr;
613
+ for ( i = 0; i < s.n; i++ ) {
614
+ nm[i] = s.m[i] ? 1 : 0;
615
+ }
616
+ }
617
+ ca_const_string_scan_end(&s);
618
+ return vout;
619
+ }
620
+
621
+ /* op codes for the shared predicate scan. */
622
+ enum { CA_TEXT_EQ, CA_TEXT_START, CA_TEXT_END, CA_TEXT_INCLUDE };
623
+
624
+ /* CAConstString#eq / start_with? / end_with? / include? — boolean mask vs a scalar
625
+ String query, byte-native. */
626
+ static VALUE
627
+ ca_const_string_predicate (VALUE self, VALUE query, int op)
628
+ {
629
+ ca_const_string_scan_t s;
630
+ VALUE vout;
631
+ boolean8_t *out;
632
+ const char *qp, *p;
633
+ long qlen;
634
+ int32_t len;
635
+ ca_size_t i;
636
+
637
+ if ( TYPE(query) != T_STRING ) {
638
+ query = rb_obj_as_string(query);
639
+ }
640
+ qp = RSTRING_PTR(query);
641
+ qlen = RSTRING_LEN(query);
642
+
643
+ ca_const_string_scan_begin(self, &s);
644
+ vout = ca_const_string_bool_out(&s, &out);
645
+
646
+ for ( i = 0; i < s.n; i++ ) {
647
+ boolean8_t r = 0;
648
+ if ( ca_const_string_record(&s, i, &p, &len) ) {
649
+ switch ( op ) {
650
+ case CA_TEXT_EQ:
651
+ r = ( len == qlen && memcmp(p, qp, qlen) == 0 );
652
+ break;
653
+ case CA_TEXT_START:
654
+ r = ( len >= qlen && memcmp(p, qp, qlen) == 0 );
655
+ break;
656
+ case CA_TEXT_END:
657
+ r = ( len >= qlen && memcmp(p + len - qlen, qp, qlen) == 0 );
658
+ break;
659
+ case CA_TEXT_INCLUDE:
660
+ r = ( ca_const_string_memmem(p, len, qp, qlen) != NULL );
661
+ break;
662
+ }
663
+ }
664
+ out[i] = r;
665
+ }
666
+ ca_const_string_scan_end(&s);
667
+ return vout;
668
+ }
669
+
670
+ static VALUE rb_ca_const_string_start_with (VALUE self, VALUE q) { return ca_const_string_predicate(self, q, CA_TEXT_START); }
671
+ static VALUE rb_ca_const_string_end_with (VALUE self, VALUE q) { return ca_const_string_predicate(self, q, CA_TEXT_END); }
672
+ static VALUE rb_ca_const_string_include (VALUE self, VALUE q) { return ca_const_string_predicate(self, q, CA_TEXT_INCLUDE); }
673
+
674
+ /* CAConstString#eq(value): value is a scalar String (broadcast) or a CAConstString
675
+ (element-wise). Returns a boolean CArray. */
676
+ static VALUE
677
+ rb_ca_const_string_eq (VALUE self, VALUE other)
678
+ {
679
+ if ( rb_obj_is_kind_of(other, rb_cCAConstString) ) {
680
+ ca_const_string_scan_t a, b;
681
+ VALUE vout;
682
+ boolean8_t *out;
683
+ /* record() leaves these unset for masked cells; short-circuit eval means
684
+ they are read only when both records are valid, but GCC cannot prove
685
+ that -- initialize to keep -Wmaybe-uninitialized quiet and defensive. */
686
+ const char *pa = NULL, *pb = NULL;
687
+ int32_t la = 0, lb = 0;
688
+ ca_size_t i;
689
+
690
+ ca_const_string_scan_begin(self, &a);
691
+ ca_const_string_scan_begin(other, &b);
692
+ if ( a.n != b.n ) {
693
+ ca_const_string_scan_end(&a);
694
+ ca_const_string_scan_end(&b);
695
+ rb_raise(rb_eArgError, "CAConstString#eq: element count mismatch (%lld vs %lld)",
696
+ (long long) a.n, (long long) b.n);
697
+ }
698
+ vout = ca_const_string_bool_out(&a, &out); /* mask carried from self */
699
+ for ( i = 0; i < a.n; i++ ) {
700
+ int va = ca_const_string_record(&a, i, &pa, &la);
701
+ int vb = ca_const_string_record(&b, i, &pb, &lb);
702
+ out[i] = ( va && vb && la == lb && memcmp(pa, pb, la) == 0 );
703
+ }
704
+ ca_const_string_scan_end(&a);
705
+ ca_const_string_scan_end(&b);
706
+ return vout;
707
+ }
708
+ return ca_const_string_predicate(self, other, CA_TEXT_EQ);
709
+ }
710
+
711
+ /* CAConstString#count(value): number of (non-masked) elements byte-equal to value. */
712
+ static VALUE
713
+ rb_ca_const_string_count (VALUE self, VALUE query)
714
+ {
715
+ ca_const_string_scan_t s;
716
+ const char *qp, *p;
717
+ long qlen;
718
+ int32_t len;
719
+ ca_size_t i, c = 0;
720
+
721
+ if ( TYPE(query) != T_STRING ) {
722
+ query = rb_obj_as_string(query);
723
+ }
724
+ qp = RSTRING_PTR(query);
725
+ qlen = RSTRING_LEN(query);
726
+
727
+ ca_const_string_scan_begin(self, &s);
728
+ for ( i = 0; i < s.n; i++ ) {
729
+ if ( ca_const_string_record(&s, i, &p, &len) && len == qlen && memcmp(p, qp, qlen) == 0 ) {
730
+ c++;
731
+ }
732
+ }
733
+ ca_const_string_scan_end(&s);
734
+ return SIZET2NUM(c);
735
+ }
736
+
737
+ /* CAConstString#search(query) / #find_value_index(query): flat index of the
738
+ first (non-masked) element byte-equal to query, or nil when none matches.
739
+ The numeric/fixlen search kernels cannot run here (storage is the int64
740
+ offset entity, not the string content), so this is a native memcmp scan --
741
+ the exact-lookup counterpart of the native sort / min / max below. */
742
+ static VALUE
743
+ rb_ca_const_string_search (VALUE self, VALUE query)
744
+ {
745
+ ca_const_string_scan_t s;
746
+ const char *qp, *p;
747
+ long qlen;
748
+ int32_t len;
749
+ ca_size_t i;
750
+ VALUE result = Qnil;
751
+
752
+ if ( TYPE(query) != T_STRING ) {
753
+ query = rb_obj_as_string(query);
754
+ }
755
+ qp = RSTRING_PTR(query);
756
+ qlen = RSTRING_LEN(query);
757
+
758
+ ca_const_string_scan_begin(self, &s);
759
+ for ( i = 0; i < s.n; i++ ) {
760
+ if ( ca_const_string_record(&s, i, &p, &len) && len == qlen && memcmp(p, qp, (size_t) qlen) == 0 ) {
761
+ result = SIZET2NUM(i);
762
+ break;
763
+ }
764
+ }
765
+ ca_const_string_scan_end(&s);
766
+ return result;
767
+ }
768
+
769
+ /* ------------------------------------------------------------------- */
770
+ /* Native sort / min / max. Ruby `String#<=>` is a byte comparison
771
+ within the same encoding, so a byte-memcmp comparator is
772
+ byte-for-byte faithful to `to_object.sort` with no collation
773
+ loss. */
774
+
775
+ /* qsort comparator context — single-threaded under the GVL (thread-safety is
776
+ a non-goal; see project memory), so a file-static context is safe. */
777
+ static ca_const_string_scan_t *ca_const_string_sort_ctx;
778
+
779
+ static int
780
+ ca_const_string_byte_cmp (const void *a, const void *b)
781
+ {
782
+ ca_size_t ia = *(const ca_size_t *) a;
783
+ ca_size_t ib = *(const ca_size_t *) b;
784
+ /* sort raises on masked input, so record() always sets these here; the
785
+ initializers keep -Wmaybe-uninitialized quiet and stay defensive. */
786
+ const char *pa = NULL, *pb = NULL;
787
+ int32_t la = 0, lb = 0;
788
+ int c;
789
+ ca_const_string_record(ca_const_string_sort_ctx, ia, &pa, &la);
790
+ ca_const_string_record(ca_const_string_sort_ctx, ib, &pb, &lb);
791
+ c = memcmp(pa, pb, (size_t) (la < lb ? la : lb));
792
+ if ( c != 0 ) return c;
793
+ return (la > lb) - (la < lb);
794
+ }
795
+
796
+ /* compute the ascending sort permutation; raises on masked input (matches
797
+ base CArray#sort). caller xfree()s the returned index array. */
798
+ static ca_size_t *
799
+ ca_const_string_sort_perm (ca_const_string_scan_t *s)
800
+ {
801
+ ca_size_t *idx;
802
+ ca_size_t i;
803
+ if ( s->m ) {
804
+ ca_const_string_scan_end(s);
805
+ rb_raise(rb_eArgError, "CAConstString#sort: cannot sort an array with masked elements");
806
+ }
807
+ idx = ALLOC_N(ca_size_t, s->n);
808
+ for ( i = 0; i < s->n; i++ ) idx[i] = i;
809
+ ca_const_string_sort_ctx = s;
810
+ qsort(idx, (size_t) s->n, sizeof(ca_size_t), ca_const_string_byte_cmp);
811
+ ca_const_string_sort_ctx = NULL;
812
+ return idx;
813
+ }
814
+
815
+ /* CAConstString#sort_index → int64 CArray of the ascending sort permutation. */
816
+ static VALUE
817
+ rb_ca_const_string_sort_index (VALUE self)
818
+ {
819
+ ca_const_string_scan_t s;
820
+ ca_size_t *idx;
821
+ VALUE vout;
822
+ CArray *co;
823
+ int64_t *op;
824
+ ca_size_t i;
825
+
826
+ ca_const_string_scan_begin(self, &s);
827
+ idx = ca_const_string_sort_perm(&s);
828
+ vout = rb_carray_new(CA_INT64, s.ca->ndim, s.ca->dim, 0, NULL);
829
+ TypedData_Get_Struct(vout, CArray, &carray_data_type, co);
830
+ op = (int64_t *) co->ptr;
831
+ for ( i = 0; i < s.n; i++ ) op[i] = (int64_t) idx[i];
832
+ xfree(idx);
833
+ ca_const_string_scan_end(&s);
834
+ return vout;
835
+ }
836
+
837
+ /* CAConstString#sort is defined in Ruby (lib/carray/const_string.rb) as `self[sort_index]`,
838
+ i.e. a no-copy view over the offset source (buffer + offsets shared, only
839
+ the gather order changes) — consistent with CArray#sort being a view. */
840
+
841
+ /* CAConstString#min / #max → the byte-min / byte-max element as a frozen String,
842
+ skipping masked elements; nil if empty or all masked. */
843
+ static VALUE
844
+ ca_const_string_extremum (VALUE self, int want_max)
845
+ {
846
+ ca_const_string_scan_t s;
847
+ const char *p, *bestp = NULL;
848
+ int32_t len, bestlen = 0;
849
+ ca_size_t i;
850
+ int found = 0;
851
+
852
+ ca_const_string_scan_begin(self, &s);
853
+ for ( i = 0; i < s.n; i++ ) {
854
+ int c;
855
+ if ( ! ca_const_string_record(&s, i, &p, &len) ) continue;
856
+ if ( ! found ) {
857
+ bestp = p; bestlen = len; found = 1;
858
+ continue;
859
+ }
860
+ c = memcmp(p, bestp, (size_t) (len < bestlen ? len : bestlen));
861
+ if ( c == 0 ) c = (len > bestlen) - (len < bestlen);
862
+ if ( ( want_max && c > 0 ) || ( ! want_max && c < 0 ) ) {
863
+ bestp = p; bestlen = len;
864
+ }
865
+ }
866
+ if ( ! found ) {
867
+ ca_const_string_scan_end(&s);
868
+ return Qnil;
869
+ }
870
+ {
871
+ VALUE str = rb_enc_str_new(bestp, bestlen, rb_enc_from_index(s.ca->encoding_id));
872
+ ca_const_string_scan_end(&s);
873
+ return rb_str_freeze(str);
874
+ }
875
+ }
876
+
877
+ static VALUE rb_ca_const_string_min (VALUE self) { return ca_const_string_extremum(self, 0); }
878
+ static VALUE rb_ca_const_string_max (VALUE self) { return ca_const_string_extremum(self, 1); }
879
+
880
+ /* ------------------------------------------------------------------- */
881
+
882
+ static VALUE
883
+ rb_ca_const_string_s_allocate (VALUE klass)
884
+ {
885
+ CAConstString *ca;
886
+ return TypedData_Make_Struct(klass, CAConstString, &catext_data_type, ca);
887
+ }
888
+
889
+ static VALUE
890
+ rb_ca_const_string_initialize_copy (VALUE self, VALUE other)
891
+ {
892
+ CAConstString *ca, *cs;
893
+ TypedData_Get_Struct(self, CAConstString, &catext_data_type, ca);
894
+ TypedData_Get_Struct(other, CAConstString, &catext_data_type, cs);
895
+ /* T.0/T.1: shallow re-setup (shares buffer + offset-source). T.3 will
896
+ replace this with compacting deep copy (rebased offsets + compacted
897
+ buffer + detach). */
898
+ if ( ca_func[CA_OBJ_CONST_STRING].pool_init ) {
899
+ ca_array_pool_alloc(ca, CA_OBJ_CONST_STRING, cs->parent->ndim);
900
+ }
901
+ ca_const_string_setup(ca, cs->parent, cs->buffer, cs->encoding_id);
902
+ return self;
903
+ }
904
+
905
+ void
906
+ Init_ca_obj_const_string (void)
907
+ {
908
+ rb_cCAConstString = rb_define_class("CAConstString", rb_cCAFace);
909
+
910
+ ca_const_string_func.struct_size = sizeof(CAConstString);
911
+ ca_const_string_func.pool_bytes = ca_const_string_pool_bytes;
912
+ ca_const_string_func.pool_init = ca_const_string_pool_init;
913
+
914
+ CA_OBJ_CONST_STRING = ca_install_obj_type(rb_cCAConstString,
915
+ &catext_data_type,
916
+ rb_cCArrayMask,
917
+ &carray_mask_data_type,
918
+ &ca_const_string_func, sizeof(ca_const_string_func));
919
+ rb_define_const(rb_cObject, "CA_OBJ_CONST_STRING", INT2NUM(CA_OBJ_CONST_STRING));
920
+
921
+ rb_define_alloc_func(rb_cCAConstString, rb_ca_const_string_s_allocate);
922
+ rb_define_method(rb_cCAConstString, "initialize_copy",
923
+ rb_ca_const_string_initialize_copy, 1);
924
+
925
+ rb_define_singleton_method(rb_cCAConstString, "wrap", rb_ca_const_string_wrap_method, -1);
926
+ rb_define_singleton_method(rb_cCAConstString, "__build__", rb_ca_const_string_build, 2);
927
+ rb_define_method(rb_cCAConstString, "encoding", rb_ca_const_string_encoding, 0);
928
+ rb_define_method(rb_cCAConstString, "buffer", rb_ca_const_string_buffer, 0);
929
+ rb_define_method(rb_cCAConstString, "storage_to_scalar",
930
+ rb_ca_const_string_storage_to_scalar, 1);
931
+
932
+ /* copy = compacting deep copy; to_ca is the same descent point. */
933
+ rb_define_method(rb_cCAConstString, "copy", rb_ca_const_string_copy, 0);
934
+ rb_define_method(rb_cCAConstString, "to_ca", rb_ca_const_string_to_ca, -1);
935
+
936
+ /* native byte ops (§3.7) */
937
+ rb_define_method(rb_cCAConstString, "byte_length", rb_ca_const_string_byte_length, 0);
938
+ rb_define_method(rb_cCAConstString, "eq", rb_ca_const_string_eq, 1);
939
+ rb_define_method(rb_cCAConstString, "count", rb_ca_const_string_count, 1);
940
+ rb_define_method(rb_cCAConstString, "start_with?", rb_ca_const_string_start_with, 1);
941
+ rb_define_method(rb_cCAConstString, "end_with?", rb_ca_const_string_end_with, 1);
942
+ rb_define_method(rb_cCAConstString, "include?", rb_ca_const_string_include, 1);
943
+
944
+ /* native exact-lookup search: flat index of the first byte-equal element,
945
+ or nil. `search` is what str_matches consumes; find_value_index is the
946
+ *_index-named surface (both exact, mask-skipping). */
947
+ rb_define_method(rb_cCAConstString, "search", rb_ca_const_string_search, 1);
948
+ rb_define_method(rb_cCAConstString, "find_value_index", rb_ca_const_string_search, 1);
949
+
950
+ /* native sort_index / min / max (§3.7), byte-memcmp comparator.
951
+ sort / sort_copy are defined in Ruby on top of sort_index. */
952
+ rb_define_method(rb_cCAConstString, "sort_index", rb_ca_const_string_sort_index, 0);
953
+ rb_define_method(rb_cCAConstString, "min", rb_ca_const_string_min, 0);
954
+ rb_define_method(rb_cCAConstString, "max", rb_ca_const_string_max, 0);
955
+
956
+ /* Y-pilot: Face-local C-level fast path for per-cell scalar fetch. */
957
+ ca_face_register_storage_to_scalar(CA_OBJ_CONST_STRING, rb_ca_const_string_storage_to_scalar);
958
+
959
+ /* F.S1: CAConstString's `buffer` field is per-parent (= each CAConstString holds its
960
+ own frozen byte String; cell offsets index into THIS instance's
961
+ buffer). Multi-parent CAStack lift cannot reuse list[0]'s buffer
962
+ without producing wrong reads for cells from other parents. Mark
963
+ as not-portable so CAStack rejects multi-parent CAConstString lift. */
964
+ ca_face_register_state_portable(CA_OBJ_CONST_STRING, 0);
965
+ }