carray 2.0.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (386) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +6 -25
  3. data/CHANGELOG.md +338 -0
  4. data/{NEWS.md → CHANGELOG.v1.md} +3 -0
  5. data/LICENSE +1 -1
  6. data/README.md +120 -36
  7. data/carray.gemspec +32 -30
  8. data/ext/ca_array_pool.c +91 -0
  9. data/ext/ca_axis_descriptor.h +186 -0
  10. data/ext/ca_axis_dispatch.c +924 -0
  11. data/ext/ca_axis_group.c +1244 -0
  12. data/ext/ca_bincmp_dispatch.c +76 -0
  13. data/ext/ca_bincmp_dispatch.h +85 -0
  14. data/ext/ca_binop_dispatch.c +124 -0
  15. data/ext/ca_binop_dispatch.h +152 -0
  16. data/ext/ca_categorical_iterator.c +1375 -0
  17. data/ext/ca_compare.c +94 -0
  18. data/ext/ca_compare.h +26 -0
  19. data/ext/ca_composite_dispatch.c +414 -0
  20. data/ext/ca_composite_dispatch.h +116 -0
  21. data/ext/ca_for_buffer.h +96 -0
  22. data/ext/ca_for_each_element.h +239 -0
  23. data/ext/ca_group_iter.c +304 -0
  24. data/ext/ca_iter_substrate.h +325 -0
  25. data/ext/ca_kernel_iterator.c +4367 -0
  26. data/ext/ca_kernel_iterator.h +2596 -0
  27. data/ext/ca_moncmp_dispatch.c +37 -0
  28. data/ext/ca_moncmp_dispatch.h +62 -0
  29. data/ext/ca_monop_dispatch.c +200 -0
  30. data/ext/ca_monop_dispatch.h +235 -0
  31. data/ext/ca_obj_array.c +355 -359
  32. data/ext/ca_obj_bincmp.c +839 -0
  33. data/ext/ca_obj_binop.c +948 -0
  34. data/ext/ca_obj_bitarray.c +369 -164
  35. data/ext/ca_obj_bitfield.c +294 -234
  36. data/ext/ca_obj_block.c +189 -711
  37. data/ext/ca_obj_byte_swap.c +766 -0
  38. data/ext/ca_obj_const_string.c +967 -0
  39. data/ext/ca_obj_face.c +750 -0
  40. data/ext/ca_obj_face.h +279 -0
  41. data/ext/ca_obj_fake.c +239 -100
  42. data/ext/ca_obj_farray.c +54 -441
  43. data/ext/ca_obj_field.c +82 -529
  44. data/ext/ca_obj_fixlen_string.c +308 -0
  45. data/ext/ca_obj_grid.c +866 -440
  46. data/ext/ca_obj_meld.c +1039 -0
  47. data/ext/ca_obj_moncmp.c +588 -0
  48. data/ext/ca_obj_monop.c +1123 -0
  49. data/ext/ca_obj_object.c +866 -296
  50. data/ext/ca_obj_record.c +470 -0
  51. data/ext/ca_obj_reduce.c +97 -82
  52. data/ext/ca_obj_refer.c +593 -459
  53. data/ext/ca_obj_remap.c +475 -0
  54. data/ext/ca_obj_repeat.c +92 -477
  55. data/ext/ca_obj_roll.c +624 -0
  56. data/ext/ca_obj_select.c +344 -296
  57. data/ext/ca_obj_select_axis.c +1306 -0
  58. data/ext/ca_obj_shift.c +231 -793
  59. data/ext/ca_obj_source.c +78 -0
  60. data/ext/ca_obj_stack.c +1173 -0
  61. data/ext/ca_obj_stride.c +2584 -0
  62. data/ext/ca_obj_string.c +270 -0
  63. data/ext/ca_obj_tile.c +622 -0
  64. data/ext/ca_obj_time.c +548 -0
  65. data/ext/ca_obj_timedelta.c +437 -0
  66. data/ext/ca_obj_transpose.c +62 -516
  67. data/ext/ca_obj_triop.c +785 -0
  68. data/ext/ca_obj_window.c +1202 -565
  69. data/ext/ca_op_byte_swap.c +175 -0
  70. data/ext/ca_op_cmplx64.h +123 -0
  71. data/ext/ca_op_ipower.c +316 -0
  72. data/ext/ca_op_powi.h +88 -0
  73. data/ext/ca_sort_kernels.h +132 -0
  74. data/ext/ca_sweep_engine.c +473 -0
  75. data/ext/ca_sweep_engine.h +166 -0
  76. data/ext/ca_transform_common.c +235 -0
  77. data/ext/ca_triop_dispatch.c +55 -0
  78. data/ext/ca_triop_dispatch.h +62 -0
  79. data/ext/carray.h +810 -420
  80. data/ext/carray_access.c +873 -731
  81. data/ext/carray_attribute.c +98 -329
  82. data/ext/carray_bincount.c +255 -0
  83. data/ext/carray_broadcast.c +376 -0
  84. data/ext/carray_build_flags.h +3 -0
  85. data/ext/carray_call_cfunc.c +2897 -874
  86. data/ext/carray_call_cfunc.h +313 -0
  87. data/ext/carray_cast.c +1264 -315
  88. data/ext/carray_cast_func.rb +81 -40
  89. data/ext/carray_class.c +53 -63
  90. data/ext/carray_config.h +28 -0
  91. data/ext/carray_conversion.c +350 -346
  92. data/ext/carray_copy.c +168 -270
  93. data/ext/carray_core.c +1396 -206
  94. data/ext/carray_count.c +312 -0
  95. data/ext/carray_data_type.c +43 -19
  96. data/ext/carray_element.c +585 -213
  97. data/ext/carray_factorize.c +2542 -0
  98. data/ext/carray_generate.c +230 -559
  99. data/ext/carray_histogram.c +490 -0
  100. data/ext/carray_hold.c +228 -0
  101. data/ext/carray_index_classifier.c +1021 -0
  102. data/ext/carray_index_classifier.h +27 -0
  103. data/ext/carray_internal.h +136 -0
  104. data/ext/carray_kernels_bincmp.c +4446 -0
  105. data/ext/carray_kernels_binop.c +11001 -0
  106. data/ext/carray_kernels_init.c +1131 -0
  107. data/ext/carray_kernels_map.c +3467 -0
  108. data/ext/carray_kernels_moncmp.c +2097 -0
  109. data/ext/carray_kernels_monop.c +18313 -0
  110. data/ext/carray_kernels_reduce_aggregate.c +25837 -0
  111. data/ext/carray_kernels_reduce_boolean.c +330 -0
  112. data/ext/carray_kernels_reduce_cumulative.c +14593 -0
  113. data/ext/carray_kernels_reduce_extreme.c +16948 -0
  114. data/ext/carray_kernels_reduce_variance.c +3910 -0
  115. data/ext/carray_kernels_scan.c +3693 -0
  116. data/ext/carray_kernels_search.c +32138 -0
  117. data/ext/carray_kernels_sort.c +10626 -0
  118. data/ext/carray_kernels_triop.c +1392 -0
  119. data/ext/carray_lazy.c +737 -0
  120. data/ext/carray_loop.c +88 -200
  121. data/ext/carray_mask.c +853 -158
  122. data/ext/carray_math_kernel.h +120 -0
  123. data/ext/carray_mathfunc.c +10 -241
  124. data/ext/carray_median_percentile.c +1257 -0
  125. data/ext/carray_memory_view.c +1650 -0
  126. data/ext/carray_operator.c +1525 -320
  127. data/ext/carray_order.c +664 -1394
  128. data/ext/carray_partition.c +416 -0
  129. data/ext/carray_random.c +518 -0
  130. data/ext/carray_scatter.c +357 -0
  131. data/ext/carray_slab.c +1219 -0
  132. data/ext/carray_slab.h +84 -0
  133. data/ext/carray_sort.c +829 -0
  134. data/ext/carray_sort_kernel.c +620 -0
  135. data/ext/carray_struct.c +695 -0
  136. data/ext/carray_test.c +343 -229
  137. data/ext/carray_undef.c +34 -17
  138. data/ext/carray_utils.c +175 -74
  139. data/ext/extconf.rb +234 -55
  140. data/ext/mk_call_cfunc.rb +671 -0
  141. data/ext/mkkernel.rb +9096 -0
  142. data/ext/ruby_carray.c +211 -108
  143. data/ext/version.h +4 -14
  144. data/ext/version.rb +5 -13
  145. data/lib/carray/arrow_tensor.rb +401 -0
  146. data/lib/carray/attribute.rb +166 -0
  147. data/lib/carray/autoload_carray.rb +239 -0
  148. data/lib/carray/autoload_method_extension.rb +45 -0
  149. data/lib/carray/axis_group.rb +711 -0
  150. data/lib/carray/basics.rb +481 -0
  151. data/lib/carray/bincount_nd.rb +358 -0
  152. data/lib/carray/block_iterator.rb +614 -0
  153. data/lib/carray/boolean_reduce.rb +109 -0
  154. data/lib/carray/categorical.rb +561 -0
  155. data/lib/carray/categorical_iterator.rb +1084 -0
  156. data/lib/carray/complex.rb +150 -0
  157. data/lib/carray/conditional.rb +216 -0
  158. data/lib/carray/const_string.rb +228 -0
  159. data/lib/carray/construct.rb +160 -328
  160. data/lib/carray/core_extensions.rb +297 -0
  161. data/lib/carray/data_type_extension.rb +250 -0
  162. data/lib/carray/fixlen_string.rb +95 -0
  163. data/lib/carray/frame/concat.rb +132 -0
  164. data/lib/carray/frame/convert.rb +95 -0
  165. data/lib/carray/frame/csv_parser.rb +211 -0
  166. data/lib/carray/frame/frame.rb +642 -0
  167. data/lib/carray/frame/group.rb +186 -0
  168. data/lib/carray/frame/io.rb +321 -0
  169. data/lib/carray/frame/join.rb +248 -0
  170. data/lib/carray/frame/records.rb +99 -0
  171. data/lib/carray/frame/sort.rb +113 -0
  172. data/lib/carray/frame/verbs.rb +316 -0
  173. data/lib/carray/frame.rb +16 -0
  174. data/lib/carray/fuse_source.rb +123 -0
  175. data/lib/carray/fusion.rb +218 -0
  176. data/lib/carray/histogram.rb +512 -0
  177. data/lib/carray/inspect.rb +37 -26
  178. data/lib/carray/iterator.rb +58 -349
  179. data/lib/carray/lazy.rb +941 -0
  180. data/lib/carray/mask_gap_fill.rb +200 -0
  181. data/lib/carray/math.rb +78 -342
  182. data/lib/carray/meld_reduce.rb +289 -0
  183. data/lib/carray/methods/align_addr.rb +116 -0
  184. data/lib/carray/methods/bin.rb +128 -0
  185. data/lib/carray/methods/bincount.rb +87 -0
  186. data/lib/carray/methods/bit_string.rb +92 -0
  187. data/lib/carray/methods/broadcast.rb +63 -0
  188. data/lib/carray/methods/choose.rb +39 -0
  189. data/lib/carray/methods/composition.rb +280 -0
  190. data/lib/carray/methods/gather_nd.rb +206 -0
  191. data/lib/carray/methods/index.rb +39 -0
  192. data/lib/carray/methods/insert_block.rb +99 -0
  193. data/lib/carray/methods/is_in.rb +141 -0
  194. data/lib/carray/methods/join.rb +90 -0
  195. data/lib/carray/methods/locate_addr.rb +52 -0
  196. data/lib/carray/methods/mask_duplicates.rb +41 -0
  197. data/lib/carray/methods/meshgrid.rb +90 -0
  198. data/lib/carray/methods/mode.rb +126 -0
  199. data/lib/carray/methods/nunique.rb +46 -0
  200. data/lib/carray/methods/resize.rb +56 -0
  201. data/lib/carray/methods/snap.rb +161 -0
  202. data/lib/carray/methods/string_format.rb +57 -0
  203. data/lib/carray/methods/unique.rb +47 -0
  204. data/lib/carray/methods/value_counts.rb +71 -0
  205. data/lib/carray/mkmf.rb +124 -101
  206. data/lib/carray/runtime.rb +89 -0
  207. data/lib/carray/serialize.rb +478 -167
  208. data/lib/carray/slab_iterator.rb +305 -0
  209. data/lib/carray/stack.rb +291 -0
  210. data/lib/carray/string.rb +56 -180
  211. data/lib/carray/string_operation_extension.rb +289 -0
  212. data/lib/carray/struct.rb +335 -323
  213. data/lib/carray/struct_builder.rb +697 -0
  214. data/lib/carray/table.rb +41 -2
  215. data/lib/carray/time.rb +2654 -38
  216. data/lib/carray/window_iterator.rb +927 -0
  217. data/lib/carray.rb +55 -57
  218. data/yard-stubs/ca_obj_array.rb +385 -0
  219. data/yard-stubs/ca_obj_bitarray.rb +38 -0
  220. data/yard-stubs/ca_obj_bitfield.rb +43 -0
  221. data/yard-stubs/ca_obj_block.rb +73 -0
  222. data/yard-stubs/ca_obj_byte_swap.rb +56 -0
  223. data/yard-stubs/ca_obj_fake.rb +31 -0
  224. data/yard-stubs/ca_obj_farray.rb +32 -0
  225. data/yard-stubs/ca_obj_field.rb +45 -0
  226. data/yard-stubs/ca_obj_grid.rb +35 -0
  227. data/yard-stubs/ca_obj_refer.rb +72 -0
  228. data/yard-stubs/ca_obj_roll.rb +45 -0
  229. data/yard-stubs/ca_obj_shift.rb +43 -0
  230. data/yard-stubs/ca_obj_stride.rb +181 -0
  231. data/yard-stubs/ca_obj_tile.rb +29 -0
  232. data/yard-stubs/ca_obj_transpose.rb +40 -0
  233. data/yard-stubs/ca_obj_window.rb +49 -0
  234. data/yard-stubs/carray_access.rb +131 -0
  235. data/yard-stubs/carray_attribute.rb +246 -0
  236. data/yard-stubs/carray_broadcast.rb +37 -0
  237. data/yard-stubs/carray_cast.rb +489 -0
  238. data/yard-stubs/carray_class.rb +65 -0
  239. data/yard-stubs/carray_conversion.rb +76 -0
  240. data/yard-stubs/carray_copy.rb +79 -0
  241. data/yard-stubs/carray_core.rb +114 -0
  242. data/yard-stubs/carray_count.rb +79 -0
  243. data/yard-stubs/carray_element.rb +108 -0
  244. data/yard-stubs/carray_generate.rb +66 -0
  245. data/yard-stubs/carray_lazy.rb +23 -0
  246. data/yard-stubs/carray_loop.rb +140 -0
  247. data/yard-stubs/carray_mask.rb +259 -0
  248. data/yard-stubs/carray_math.rb +132 -0
  249. data/yard-stubs/carray_mathfunc.rb +45 -0
  250. data/yard-stubs/carray_median_percentile.rb +89 -0
  251. data/yard-stubs/carray_memory_view.rb +163 -0
  252. data/yard-stubs/carray_order.rb +312 -0
  253. data/yard-stubs/carray_random.rb +89 -0
  254. data/yard-stubs/carray_scatter.rb +106 -0
  255. data/yard-stubs/carray_slab.rb +57 -0
  256. data/yard-stubs/carray_sort.rb +163 -0
  257. data/yard-stubs/carray_test.rb +85 -0
  258. data/yard-stubs/carray_undef.rb +64 -0
  259. data/yard-stubs/carray_utils.rb +97 -0
  260. data/yard-stubs/ruby_carray.rb +193 -0
  261. metadata +220 -138
  262. data/Rakefile +0 -51
  263. data/TODO.md +0 -18
  264. data/ext/ca_iter_block.c +0 -257
  265. data/ext/ca_iter_dimension.c +0 -299
  266. data/ext/ca_iter_window.c +0 -214
  267. data/ext/ca_obj_mapping.c +0 -644
  268. data/ext/ca_obj_unbound_repeat.c +0 -529
  269. data/ext/carray_iterator.c +0 -641
  270. data/ext/carray_math.rb +0 -850
  271. data/ext/carray_numeric.c +0 -259
  272. data/ext/carray_sort_addr.c +0 -254
  273. data/ext/carray_stat.c +0 -2100
  274. data/ext/carray_stat_proc.rb +0 -1999
  275. data/ext/mkmath.rb +0 -741
  276. data/ext/ruby_ccomplex.c +0 -509
  277. data/ext/ruby_float_func.c +0 -86
  278. data/lib/carray/array.rb +0 -8
  279. data/lib/carray/autoload/autoload_base.rb +0 -19
  280. data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
  281. data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
  282. data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
  283. data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
  284. data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
  285. data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
  286. data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
  287. data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
  288. data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
  289. data/lib/carray/autoload/autoload_gem_random.rb +0 -8
  290. data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
  291. data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
  292. data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
  293. data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
  294. data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
  295. data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
  296. data/lib/carray/autoload/autoload_object_link.rb +0 -1
  297. data/lib/carray/autoload/autoload_object_pack.rb +0 -2
  298. data/lib/carray/autoload.rb +0 -141
  299. data/lib/carray/basic.rb +0 -191
  300. data/lib/carray/broadcast.rb +0 -101
  301. data/lib/carray/compose.rb +0 -315
  302. data/lib/carray/convert.rb +0 -115
  303. data/lib/carray/info.rb +0 -110
  304. data/lib/carray/io/imagemagick.rb +0 -235
  305. data/lib/carray/mask.rb +0 -102
  306. data/lib/carray/math/histogram.rb +0 -177
  307. data/lib/carray/math/recurrence.rb +0 -93
  308. data/lib/carray/object/ca_obj_iterator.rb +0 -50
  309. data/lib/carray/object/ca_obj_link.rb +0 -50
  310. data/lib/carray/object/ca_obj_pack.rb +0 -99
  311. data/lib/carray/obsolete.rb +0 -256
  312. data/lib/carray/ordering.rb +0 -181
  313. data/lib/carray/testing.rb +0 -51
  314. data/lib/carray/transform.rb +0 -109
  315. data/mailmap +0 -1
  316. data/misc/Methods.ja.md +0 -182
  317. data/misc/NOTE +0 -51
  318. data/spec/Classes/CABitfield_spec.rb +0 -58
  319. data/spec/Classes/CABlockIterator_spec.rb +0 -114
  320. data/spec/Classes/CABlock_spec.rb +0 -205
  321. data/spec/Classes/CAField_spec.rb +0 -39
  322. data/spec/Classes/CAGrid_spec.rb +0 -75
  323. data/spec/Classes/CAMap_spec.rb +0 -0
  324. data/spec/Classes/CAMapping_spec.rb +0 -105
  325. data/spec/Classes/CAObject_attribute_spec.rb +0 -33
  326. data/spec/Classes/CAObject_spec.rb +0 -33
  327. data/spec/Classes/CARefer_spec.rb +0 -93
  328. data/spec/Classes/CARepeat_spec.rb +0 -65
  329. data/spec/Classes/CASelect_spec.rb +0 -22
  330. data/spec/Classes/CAShift_spec.rb +0 -16
  331. data/spec/Classes/CAStruct_spec.rb +0 -71
  332. data/spec/Classes/CATranspose_spec.rb +0 -60
  333. data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
  334. data/spec/Classes/CAWindow_spec.rb +0 -54
  335. data/spec/Classes/CAWrap_spec.rb +0 -8
  336. data/spec/Classes/CArray_spec.rb +0 -184
  337. data/spec/Classes/CScalar_spec.rb +0 -55
  338. data/spec/Features/feature_130_spec.rb +0 -19
  339. data/spec/Features/feature_attributes_spec.rb +0 -280
  340. data/spec/Features/feature_boolean_spec.rb +0 -98
  341. data/spec/Features/feature_broadcast.rb +0 -116
  342. data/spec/Features/feature_cast_function.rb +0 -19
  343. data/spec/Features/feature_cast_spec.rb +0 -33
  344. data/spec/Features/feature_class_spec.rb +0 -84
  345. data/spec/Features/feature_complex_spec.rb +0 -42
  346. data/spec/Features/feature_composite_spec.rb +0 -124
  347. data/spec/Features/feature_convert_spec.rb +0 -46
  348. data/spec/Features/feature_copy_spec.rb +0 -123
  349. data/spec/Features/feature_creation_spec.rb +0 -84
  350. data/spec/Features/feature_element_spec.rb +0 -144
  351. data/spec/Features/feature_extream_spec.rb +0 -54
  352. data/spec/Features/feature_generate_spec.rb +0 -74
  353. data/spec/Features/feature_index_spec.rb +0 -69
  354. data/spec/Features/feature_mask_spec.rb +0 -580
  355. data/spec/Features/feature_math_spec.rb +0 -97
  356. data/spec/Features/feature_order_spec.rb +0 -146
  357. data/spec/Features/feature_ref_store_spec.rb +0 -209
  358. data/spec/Features/feature_serialization_spec.rb +0 -125
  359. data/spec/Features/feature_stat_spec.rb +0 -397
  360. data/spec/Features/feature_virtual_spec.rb +0 -48
  361. data/spec/Features/method_eq_spec.rb +0 -81
  362. data/spec/Features/method_is_nan_spec.rb +0 -12
  363. data/spec/Features/method_map_spec.rb +0 -54
  364. data/spec/Features/method_max_with.rb +0 -20
  365. data/spec/Features/method_min_with.rb +0 -19
  366. data/spec/Features/method_ne_spec.rb +0 -18
  367. data/spec/Features/method_project_spec.rb +0 -188
  368. data/spec/Features/method_ref_spec.rb +0 -27
  369. data/spec/Features/method_round_spec.rb +0 -11
  370. data/spec/Features/method_s_linspace_spec.rb +0 -48
  371. data/spec/Features/method_s_span_spec.rb +0 -14
  372. data/spec/Features/method_seq_spec.rb +0 -47
  373. data/spec/Features/method_sort_with.rb +0 -43
  374. data/spec/Features/method_sorted_with.rb +0 -29
  375. data/spec/Features/method_span_spec.rb +0 -42
  376. data/spec/Features/method_wrap_readonly_spec.rb +0 -43
  377. data/spec/UnitTest/test_CAVirtual.rb +0 -214
  378. data/spec/spec_all.rb +0 -10
  379. data/utils/ca_ase.rb +0 -21
  380. data/utils/ca_methods.rb +0 -15
  381. data/utils/cast_checker.rb +0 -30
  382. data/utils/convert_test.rb +0 -73
  383. data/utils/extract_yard.rb +0 -22
  384. data/utils/guess_shape.rb +0 -76
  385. data/utils/monkey_patch_methods.rb +0 -62
  386. data/utils/remove_resource_fork.sh +0 -5
@@ -0,0 +1,967 @@
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
+ .fill_addrs = ca_face_fill_addrs,
232
+ .fill_stride = ca_face_fill_stride,
233
+ };
234
+
235
+ /* ------------------------------------------------------------------- */
236
+
237
+ /* decode one record from the buffer given its [start,end) byte range →
238
+ frozen String. buffer layout: pure concatenation (Arrow values layout),
239
+ length = end - start. encoding from ca->encoding_id. */
240
+ static VALUE
241
+ ca_const_string_decode (CAConstString *ca, int64_t start, int64_t end)
242
+ {
243
+ const char *buf = RSTRING_PTR(ca->buffer);
244
+ long buflen = RSTRING_LEN(ca->buffer);
245
+ int64_t len = end - start;
246
+ VALUE s;
247
+
248
+ if ( start < 0 || len < 0 || end > (int64_t) buflen ) {
249
+ rb_raise(rb_eIndexError,
250
+ "CAConstString: record [%lld,%lld) out of buffer range (%ld bytes)",
251
+ (long long) start, (long long) end, buflen);
252
+ }
253
+ s = rb_enc_str_new(buf + start, (long) len,
254
+ rb_enc_from_index(ca->encoding_id));
255
+ return rb_str_freeze(s);
256
+ }
257
+
258
+ /* storage_to_scalar in C (= skip rb_funcall on the per-cell fetch hot
259
+ path). Since the Face surface is CA_FIXLEN (16 bytes), fetch delivers a
260
+ 16-byte raw String = the (start,end) int64 pair. Decode the buffer slice
261
+ → frozen Ruby String (plain frozen String, no Scalar wrapper). */
262
+ static VALUE
263
+ rb_ca_const_string_storage_to_scalar (VALUE self, VALUE raw)
264
+ {
265
+ CAConstString *ca;
266
+ int64_t pair[2];
267
+
268
+ TypedData_Get_Struct(self, CAConstString, &catext_data_type, ca);
269
+
270
+ if ( TYPE(raw) != T_STRING || RSTRING_LEN(raw) != (long) sizeof(pair) ) {
271
+ rb_raise(rb_eArgError,
272
+ "CAConstString#storage_to_scalar: expected a %lu-byte (start,end) cell",
273
+ (unsigned long) sizeof(pair));
274
+ }
275
+ memcpy(pair, RSTRING_PTR(raw), sizeof(pair));
276
+
277
+ return ca_const_string_decode(ca, pair[0], pair[1]);
278
+ }
279
+
280
+ /* ------------------------------------------------------------------- */
281
+
282
+ /* CAConstString.wrap(int64_offsets, buffer:, encoding:) — low-level Face wrap of an
283
+ existing fixlen-16 (start,end) pair entity + pure-concatenation buffer. */
284
+ static VALUE
285
+ rb_ca_const_string_wrap (VALUE parent_val, VALUE buffer, int encoding_id)
286
+ {
287
+ CArray *parent;
288
+ CAConstString *ca;
289
+ VALUE obj;
290
+
291
+ TypedData_Get_Struct(parent_val, CArray, &carray_data_type, parent);
292
+ if ( parent->data_type != CA_FIXLEN || parent->bytes != 2 * (ca_size_t) sizeof(int64_t) ) {
293
+ rb_raise(rb_eTypeError,
294
+ "CAConstString.wrap requires a fixlen-16 (start,end) pair entity "
295
+ "(got data_type=%d, bytes=%ld)",
296
+ parent->data_type, (long) parent->bytes);
297
+ }
298
+ if ( TYPE(buffer) != T_STRING ) {
299
+ rb_raise(rb_eTypeError, "CAConstString.wrap: buffer: must be a String");
300
+ }
301
+
302
+ ca = ca_const_string_new(parent, rb_str_freeze(buffer), encoding_id);
303
+ obj = TypedData_Wrap_Struct(rb_cCAConstString, &catext_data_type, ca);
304
+ rb_ca_set_parent(obj, parent_val); /* pin parent VALUE for GC */
305
+ return obj;
306
+ }
307
+
308
+ static VALUE
309
+ rb_ca_const_string_wrap_method (int argc, VALUE *argv, VALUE klass)
310
+ {
311
+ VALUE parent_val, ropt, rbuffer = Qnil, renc = Qnil;
312
+ int encoding_id;
313
+
314
+ rb_scan_args(argc, argv, "1:", &parent_val, &ropt);
315
+ rb_scan_options(ropt, "buffer,encoding", &rbuffer, &renc);
316
+ if ( NIL_P(rbuffer) ) {
317
+ rb_raise(rb_eArgError, "CAConstString.wrap: buffer: keyword is required");
318
+ }
319
+ encoding_id = NIL_P(renc) ? rb_utf8_encindex()
320
+ : rb_to_encoding_index(renc);
321
+
322
+ return rb_ca_const_string_wrap(parent_val, rbuffer, encoding_id);
323
+ }
324
+
325
+ /* Build a CAConstString from a Ruby Array of Strings / nils in a single C
326
+ pass: pure-concatenation buffer (Arrow values layout) + fixlen-16
327
+ (start,end) pair entity, with a mask for nil elements. Replaces the
328
+ per-element Ruby build loop -- no `String#b` copy, no `pack`, no per-row
329
+ method dispatch. `renc` is the target Encoding (nil = UTF-8). */
330
+ static VALUE
331
+ rb_ca_const_string_build (VALUE klass, VALUE values, VALUE renc)
332
+ {
333
+ long i, n;
334
+ int encoding_id;
335
+ rb_encoding *enc;
336
+ volatile VALUE buffer, vpair;
337
+ CArray *pe;
338
+ int64_t *pair;
339
+ boolean8_t *mask = NULL;
340
+ ca_size_t dim[1];
341
+ long hint = 0;
342
+
343
+ Check_Type(values, T_ARRAY);
344
+ n = RARRAY_LEN(values);
345
+ encoding_id = NIL_P(renc) ? rb_utf8_encindex() : rb_to_encoding_index(renc);
346
+ enc = rb_enc_from_index(encoding_id);
347
+
348
+ /* size hint: sum String lengths cheaply (skip non-String coercion here) */
349
+ for ( i = 0; i < n; i++ ) {
350
+ VALUE s = RARRAY_AREF(values, i);
351
+ if ( RB_TYPE_P(s, T_STRING) ) hint += RSTRING_LEN(s);
352
+ }
353
+ buffer = rb_str_buf_new(hint);
354
+ rb_enc_associate_index(buffer, rb_ascii8bit_encindex());
355
+
356
+ dim[0] = (ca_size_t) n;
357
+ vpair = rb_carray_new(CA_FIXLEN, 1, dim, 2 * sizeof(int64_t), NULL);
358
+ TypedData_Get_Struct(vpair, CArray, &carray_data_type, pe);
359
+ pair = (int64_t *) pe->ptr;
360
+
361
+ for ( i = 0; i < n; i++ ) {
362
+ VALUE s = RARRAY_AREF(values, i);
363
+ if ( NIL_P(s) ) {
364
+ if ( ! mask ) {
365
+ ca_create_mask(pe);
366
+ mask = (boolean8_t *) pe->mask->ptr;
367
+ }
368
+ mask[i] = 1;
369
+ pair[2 * i] = 0;
370
+ pair[2 * i + 1] = 0;
371
+ continue;
372
+ }
373
+ if ( ! RB_TYPE_P(s, T_STRING) ) {
374
+ s = rb_obj_as_string(s); /* to_s */
375
+ }
376
+ /* encoding gate: match the column encoding, or pure-ASCII passes through */
377
+ if ( rb_enc_get_index(s) != encoding_id && ! rb_enc_str_asciionly_p(s) ) {
378
+ rb_raise(rb_eArgError,
379
+ "CArray.const_string: element encoding %s does not match column "
380
+ "encoding %s (use String#encode to convert explicitly)",
381
+ rb_enc_name(rb_enc_get(s)), rb_enc_name(enc));
382
+ }
383
+ pair[2 * i] = (int64_t) RSTRING_LEN(buffer);
384
+ rb_str_cat(buffer, RSTRING_PTR(s), RSTRING_LEN(s));
385
+ pair[2 * i + 1] = (int64_t) RSTRING_LEN(buffer);
386
+ }
387
+
388
+ return rb_ca_const_string_wrap(vpair, rb_str_freeze(buffer), encoding_id);
389
+ }
390
+
391
+ /* CAConstString#encoding → Encoding */
392
+ static VALUE
393
+ rb_ca_const_string_encoding (VALUE self)
394
+ {
395
+ CAConstString *ca;
396
+ TypedData_Get_Struct(self, CAConstString, &catext_data_type, ca);
397
+ return rb_enc_from_encoding(rb_enc_from_index(ca->encoding_id));
398
+ }
399
+
400
+ /* `CAConstString#buffer` — returns the frozen internal byte buffer
401
+ (length-prefix format). Exposes the defining tail state for
402
+ introspection and the Arrow-boundary component-buffer export
403
+ path. */
404
+ static VALUE
405
+ rb_ca_const_string_buffer (VALUE self)
406
+ {
407
+ CAConstString *ca;
408
+ TypedData_Get_Struct(self, CAConstString, &catext_data_type, ca);
409
+ return ca->buffer;
410
+ }
411
+
412
+ /* ------------------------------------------------------------------- */
413
+
414
+ /* copy = compacting deep copy — the only chain-descent point in
415
+ CAConstString. Produces a standalone CAConstString:
416
+ - a fresh contiguous fixlen-16 (start,end) pair entity
417
+ - a fresh compacted buffer, each element's bytes repacked once in the
418
+ view's logical order (pure concatenation = Arrow values layout)
419
+ - encoding preserved, mask carried
420
+ - fully detached from the original buffer / pair-source
421
+ Cost is O(live bytes); a near-full copy is a near-full rebuild (accepted).
422
+ Done at the byte level (no per-element Ruby String boxing). */
423
+ static VALUE
424
+ rb_ca_const_string_copy (VALUE self)
425
+ {
426
+ volatile VALUE vout, newbuf;
427
+ CAConstString *ca;
428
+ CArray *co;
429
+ ca_size_t i, n;
430
+ int64_t *src_pair, *dst_pair;
431
+ boolean8_t *m = NULL;
432
+ const char *oldbuf;
433
+ long oldbuflen;
434
+
435
+ TypedData_Get_Struct(self, CAConstString, &catext_data_type, ca);
436
+
437
+ /* materialize logical-order (start,end) pairs + mask */
438
+ ca_attach((CArray *) ca);
439
+ ca_update_mask((CArray *) ca);
440
+
441
+ n = ca->elements;
442
+ src_pair = (int64_t *) ca->ptr;
443
+ m = ca->mask ? (boolean8_t *) ca->mask->ptr : NULL;
444
+ oldbuf = RSTRING_PTR(ca->buffer);
445
+ oldbuflen = RSTRING_LEN(ca->buffer);
446
+
447
+ /* fresh fixlen-16 pair entity, same shape (N-D preserved) */
448
+ vout = rb_carray_new(CA_FIXLEN, ca->ndim, ca->dim, 2 * sizeof(int64_t), NULL);
449
+ TypedData_Get_Struct(vout, CArray, &carray_data_type, co);
450
+ dst_pair = (int64_t *) co->ptr;
451
+
452
+ /* growable compacted buffer (BINARY) */
453
+ newbuf = rb_str_buf_new(oldbuflen);
454
+ rb_enc_associate_index(newbuf, rb_ascii8bit_encindex());
455
+
456
+ for ( i = 0; i < n; i++ ) {
457
+ int64_t start, end, len;
458
+ if ( m && m[i] ) {
459
+ dst_pair[2 * i] = 0;
460
+ dst_pair[2 * i + 1] = 0;
461
+ continue;
462
+ }
463
+ start = src_pair[2 * i];
464
+ end = src_pair[2 * i + 1];
465
+ len = end - start;
466
+ if ( start < 0 || len < 0 || end > (int64_t) oldbuflen ) {
467
+ ca_detach((CArray *) ca);
468
+ rb_raise(rb_eIndexError,
469
+ "CAConstString#copy: record [%lld,%lld) out of buffer range",
470
+ (long long) start, (long long) end);
471
+ }
472
+ dst_pair[2 * i] = (int64_t) RSTRING_LEN(newbuf);
473
+ rb_str_cat(newbuf, oldbuf + start, (long) len);
474
+ dst_pair[2 * i + 1] = (int64_t) RSTRING_LEN(newbuf);
475
+ }
476
+
477
+ /* carry mask onto the new pair entity */
478
+ if ( m ) {
479
+ boolean8_t *nm;
480
+ ca_create_mask(co);
481
+ nm = (boolean8_t *) co->mask->ptr;
482
+ for ( i = 0; i < n; i++ ) {
483
+ nm[i] = m[i] ? 1 : 0;
484
+ }
485
+ }
486
+
487
+ ca_detach((CArray *) ca);
488
+
489
+ return rb_ca_const_string_wrap(vout, rb_str_freeze(newbuf), ca->encoding_id);
490
+ }
491
+
492
+ /* to_ca descends to the same compacting copy. The result is detached
493
+ from the source buffer by construction, so `writable: true` — a demand
494
+ that writes reach the source — cannot be honoured and is refused. */
495
+ static VALUE
496
+ rb_ca_const_string_to_ca (int argc, VALUE *argv, VALUE self)
497
+ {
498
+ if ( ca_to_ca_writable_arg(argc, argv) ) {
499
+ ca_to_ca_refuse_writable(self);
500
+ }
501
+ return rb_ca_const_string_copy(self);
502
+ }
503
+
504
+ /* ------------------------------------------------------------------- */
505
+ /* Native byte ops: compare / scan record bytes straight out of the
506
+ buffer, without materializing a Ruby String per element. This is
507
+ the layer where CAConstString outperforms `to_object` followed by
508
+ per-cell Ruby String work. */
509
+
510
+ typedef struct {
511
+ CAConstString *ca;
512
+ int64_t *off;
513
+ boolean8_t *m;
514
+ const char *buf;
515
+ long buflen;
516
+ ca_size_t n;
517
+ } ca_const_string_scan_t;
518
+
519
+ static void
520
+ ca_const_string_scan_begin (VALUE self, ca_const_string_scan_t *s)
521
+ {
522
+ TypedData_Get_Struct(self, CAConstString, &catext_data_type, s->ca);
523
+ ca_attach((CArray *) s->ca);
524
+ ca_update_mask((CArray *) s->ca);
525
+ s->off = (int64_t *) s->ca->ptr;
526
+ s->m = s->ca->mask ? (boolean8_t *) s->ca->mask->ptr : NULL;
527
+ s->buf = RSTRING_PTR(s->ca->buffer);
528
+ s->buflen = RSTRING_LEN(s->ca->buffer);
529
+ s->n = s->ca->elements;
530
+ }
531
+
532
+ static void
533
+ ca_const_string_scan_end (ca_const_string_scan_t *s)
534
+ {
535
+ ca_detach((CArray *) s->ca);
536
+ }
537
+
538
+ /* Resolve element i to its record bytes; returns 0 if masked.
539
+ Storage is (start,end) int64 pairs: s->off holds 2 int64 per element,
540
+ so element i occupies s->off[2*i] (start) .. s->off[2*i+1] (end).
541
+ The buffer is a pure concatenation; length = end - start. */
542
+ static inline int
543
+ ca_const_string_record (ca_const_string_scan_t *s, ca_size_t i, const char **pp, int32_t *plen)
544
+ {
545
+ int64_t start, end;
546
+ if ( s->m && s->m[i] ) {
547
+ return 0;
548
+ }
549
+ start = s->off[2 * i];
550
+ end = s->off[2 * i + 1];
551
+ *pp = s->buf + start;
552
+ *plen = (int32_t) (end - start);
553
+ return 1;
554
+ }
555
+
556
+ /* Allocate a boolean output of the same shape; carry the input mask. */
557
+ static VALUE
558
+ ca_const_string_bool_out (ca_const_string_scan_t *s, boolean8_t **op)
559
+ {
560
+ VALUE vout;
561
+ CArray *co;
562
+ vout = rb_carray_new(CA_BOOLEAN, s->ca->ndim, s->ca->dim, 0, NULL);
563
+ TypedData_Get_Struct(vout, CArray, &carray_data_type, co);
564
+ *op = (boolean8_t *) co->ptr;
565
+ if ( s->m ) {
566
+ ca_size_t i;
567
+ boolean8_t *nm;
568
+ ca_create_mask(co);
569
+ nm = (boolean8_t *) co->mask->ptr;
570
+ for ( i = 0; i < s->n; i++ ) {
571
+ nm[i] = s->m[i] ? 1 : 0;
572
+ }
573
+ }
574
+ return vout;
575
+ }
576
+
577
+ /* memmem fallback (not in all libc). */
578
+ static const char *
579
+ ca_const_string_memmem (const char *hay, long hlen, const char *need, long nlen)
580
+ {
581
+ long i;
582
+ if ( nlen == 0 ) return hay;
583
+ if ( nlen > hlen ) return NULL;
584
+ for ( i = 0; i <= hlen - nlen; i++ ) {
585
+ if ( hay[i] == need[0] && memcmp(hay + i, need, nlen) == 0 ) {
586
+ return hay + i;
587
+ }
588
+ }
589
+ return NULL;
590
+ }
591
+
592
+ /* CAConstString#byte_length → int64 CArray of per-element byte lengths. */
593
+ static VALUE
594
+ rb_ca_const_string_byte_length (VALUE self)
595
+ {
596
+ ca_const_string_scan_t s;
597
+ VALUE vout;
598
+ CArray *co;
599
+ int64_t *op;
600
+ const char *p;
601
+ int32_t len;
602
+ ca_size_t i;
603
+
604
+ ca_const_string_scan_begin(self, &s);
605
+ vout = rb_carray_new(CA_INT64, s.ca->ndim, s.ca->dim, 0, NULL);
606
+ TypedData_Get_Struct(vout, CArray, &carray_data_type, co);
607
+ op = (int64_t *) co->ptr;
608
+ for ( i = 0; i < s.n; i++ ) {
609
+ op[i] = ca_const_string_record(&s, i, &p, &len) ? (int64_t) len : 0;
610
+ }
611
+ if ( s.m ) {
612
+ boolean8_t *nm;
613
+ ca_create_mask(co);
614
+ nm = (boolean8_t *) co->mask->ptr;
615
+ for ( i = 0; i < s.n; i++ ) {
616
+ nm[i] = s.m[i] ? 1 : 0;
617
+ }
618
+ }
619
+ ca_const_string_scan_end(&s);
620
+ return vout;
621
+ }
622
+
623
+ /* op codes for the shared predicate scan. */
624
+ enum { CA_TEXT_EQ, CA_TEXT_START, CA_TEXT_END, CA_TEXT_INCLUDE };
625
+
626
+ /* CAConstString#eq / start_with? / end_with? / include? — boolean mask vs a scalar
627
+ String query, byte-native. */
628
+ static VALUE
629
+ ca_const_string_predicate (VALUE self, VALUE query, int op)
630
+ {
631
+ ca_const_string_scan_t s;
632
+ VALUE vout;
633
+ boolean8_t *out;
634
+ const char *qp, *p;
635
+ long qlen;
636
+ int32_t len;
637
+ ca_size_t i;
638
+
639
+ if ( TYPE(query) != T_STRING ) {
640
+ query = rb_obj_as_string(query);
641
+ }
642
+ qp = RSTRING_PTR(query);
643
+ qlen = RSTRING_LEN(query);
644
+
645
+ ca_const_string_scan_begin(self, &s);
646
+ vout = ca_const_string_bool_out(&s, &out);
647
+
648
+ for ( i = 0; i < s.n; i++ ) {
649
+ boolean8_t r = 0;
650
+ if ( ca_const_string_record(&s, i, &p, &len) ) {
651
+ switch ( op ) {
652
+ case CA_TEXT_EQ:
653
+ r = ( len == qlen && memcmp(p, qp, qlen) == 0 );
654
+ break;
655
+ case CA_TEXT_START:
656
+ r = ( len >= qlen && memcmp(p, qp, qlen) == 0 );
657
+ break;
658
+ case CA_TEXT_END:
659
+ r = ( len >= qlen && memcmp(p + len - qlen, qp, qlen) == 0 );
660
+ break;
661
+ case CA_TEXT_INCLUDE:
662
+ r = ( ca_const_string_memmem(p, len, qp, qlen) != NULL );
663
+ break;
664
+ }
665
+ }
666
+ out[i] = r;
667
+ }
668
+ ca_const_string_scan_end(&s);
669
+ return vout;
670
+ }
671
+
672
+ static VALUE rb_ca_const_string_start_with (VALUE self, VALUE q) { return ca_const_string_predicate(self, q, CA_TEXT_START); }
673
+ static VALUE rb_ca_const_string_end_with (VALUE self, VALUE q) { return ca_const_string_predicate(self, q, CA_TEXT_END); }
674
+ static VALUE rb_ca_const_string_include (VALUE self, VALUE q) { return ca_const_string_predicate(self, q, CA_TEXT_INCLUDE); }
675
+
676
+ /* CAConstString#eq(value): value is a scalar String (broadcast) or a CAConstString
677
+ (element-wise). Returns a boolean CArray. */
678
+ static VALUE
679
+ rb_ca_const_string_eq (VALUE self, VALUE other)
680
+ {
681
+ if ( rb_obj_is_kind_of(other, rb_cCAConstString) ) {
682
+ ca_const_string_scan_t a, b;
683
+ VALUE vout;
684
+ boolean8_t *out;
685
+ /* record() leaves these unset for masked cells; short-circuit eval means
686
+ they are read only when both records are valid, but GCC cannot prove
687
+ that -- initialize to keep -Wmaybe-uninitialized quiet and defensive. */
688
+ const char *pa = NULL, *pb = NULL;
689
+ int32_t la = 0, lb = 0;
690
+ ca_size_t i;
691
+
692
+ ca_const_string_scan_begin(self, &a);
693
+ ca_const_string_scan_begin(other, &b);
694
+ if ( a.n != b.n ) {
695
+ ca_const_string_scan_end(&a);
696
+ ca_const_string_scan_end(&b);
697
+ rb_raise(rb_eArgError, "CAConstString#eq: element count mismatch (%lld vs %lld)",
698
+ (long long) a.n, (long long) b.n);
699
+ }
700
+ vout = ca_const_string_bool_out(&a, &out); /* mask carried from self */
701
+ for ( i = 0; i < a.n; i++ ) {
702
+ int va = ca_const_string_record(&a, i, &pa, &la);
703
+ int vb = ca_const_string_record(&b, i, &pb, &lb);
704
+ out[i] = ( va && vb && la == lb && memcmp(pa, pb, la) == 0 );
705
+ }
706
+ ca_const_string_scan_end(&a);
707
+ ca_const_string_scan_end(&b);
708
+ return vout;
709
+ }
710
+ return ca_const_string_predicate(self, other, CA_TEXT_EQ);
711
+ }
712
+
713
+ /* CAConstString#count(value): number of (non-masked) elements byte-equal to value. */
714
+ static VALUE
715
+ rb_ca_const_string_count (VALUE self, VALUE query)
716
+ {
717
+ ca_const_string_scan_t s;
718
+ const char *qp, *p;
719
+ long qlen;
720
+ int32_t len;
721
+ ca_size_t i, c = 0;
722
+
723
+ if ( TYPE(query) != T_STRING ) {
724
+ query = rb_obj_as_string(query);
725
+ }
726
+ qp = RSTRING_PTR(query);
727
+ qlen = RSTRING_LEN(query);
728
+
729
+ ca_const_string_scan_begin(self, &s);
730
+ for ( i = 0; i < s.n; i++ ) {
731
+ if ( ca_const_string_record(&s, i, &p, &len) && len == qlen && memcmp(p, qp, qlen) == 0 ) {
732
+ c++;
733
+ }
734
+ }
735
+ ca_const_string_scan_end(&s);
736
+ return SIZET2NUM(c);
737
+ }
738
+
739
+ /* CAConstString#search(query) / #find_value_index(query): flat index of the
740
+ first (non-masked) element byte-equal to query, or nil when none matches.
741
+ The numeric/fixlen search kernels cannot run here (storage is the int64
742
+ offset entity, not the string content), so this is a native memcmp scan --
743
+ the exact-lookup counterpart of the native sort / min / max below. */
744
+ static VALUE
745
+ rb_ca_const_string_search (VALUE self, VALUE query)
746
+ {
747
+ ca_const_string_scan_t s;
748
+ const char *qp, *p;
749
+ long qlen;
750
+ int32_t len;
751
+ ca_size_t i;
752
+ VALUE result = Qnil;
753
+
754
+ if ( TYPE(query) != T_STRING ) {
755
+ query = rb_obj_as_string(query);
756
+ }
757
+ qp = RSTRING_PTR(query);
758
+ qlen = RSTRING_LEN(query);
759
+
760
+ ca_const_string_scan_begin(self, &s);
761
+ for ( i = 0; i < s.n; i++ ) {
762
+ if ( ca_const_string_record(&s, i, &p, &len) && len == qlen && memcmp(p, qp, (size_t) qlen) == 0 ) {
763
+ result = SIZET2NUM(i);
764
+ break;
765
+ }
766
+ }
767
+ ca_const_string_scan_end(&s);
768
+ return result;
769
+ }
770
+
771
+ /* ------------------------------------------------------------------- */
772
+ /* Native sort / min / max. Ruby `String#<=>` is a byte comparison
773
+ within the same encoding, so a byte-memcmp comparator is
774
+ byte-for-byte faithful to `to_object.sort` with no collation
775
+ loss. */
776
+
777
+ /* qsort comparator context — single-threaded under the GVL (thread-safety is
778
+ a non-goal; see project memory), so a file-static context is safe. */
779
+ static ca_const_string_scan_t *ca_const_string_sort_ctx;
780
+
781
+ static int
782
+ ca_const_string_byte_cmp (const void *a, const void *b)
783
+ {
784
+ ca_size_t ia = *(const ca_size_t *) a;
785
+ ca_size_t ib = *(const ca_size_t *) b;
786
+ /* sort raises on masked input, so record() always sets these here; the
787
+ initializers keep -Wmaybe-uninitialized quiet and stay defensive. */
788
+ const char *pa = NULL, *pb = NULL;
789
+ int32_t la = 0, lb = 0;
790
+ int c;
791
+ ca_const_string_record(ca_const_string_sort_ctx, ia, &pa, &la);
792
+ ca_const_string_record(ca_const_string_sort_ctx, ib, &pb, &lb);
793
+ c = memcmp(pa, pb, (size_t) (la < lb ? la : lb));
794
+ if ( c != 0 ) return c;
795
+ return (la > lb) - (la < lb);
796
+ }
797
+
798
+ /* compute the ascending sort permutation; raises on masked input (matches
799
+ base CArray#sort). caller xfree()s the returned index array. */
800
+ static ca_size_t *
801
+ ca_const_string_sort_perm (ca_const_string_scan_t *s)
802
+ {
803
+ ca_size_t *idx;
804
+ ca_size_t i;
805
+ if ( s->m ) {
806
+ ca_const_string_scan_end(s);
807
+ rb_raise(rb_eArgError, "CAConstString#sort: cannot sort an array with masked elements");
808
+ }
809
+ idx = ALLOC_N(ca_size_t, s->n);
810
+ for ( i = 0; i < s->n; i++ ) idx[i] = i;
811
+ ca_const_string_sort_ctx = s;
812
+ qsort(idx, (size_t) s->n, sizeof(ca_size_t), ca_const_string_byte_cmp);
813
+ ca_const_string_sort_ctx = NULL;
814
+ return idx;
815
+ }
816
+
817
+ /* CAConstString#sort_index → int64 CArray of the ascending sort permutation. */
818
+ static VALUE
819
+ rb_ca_const_string_sort_index (VALUE self)
820
+ {
821
+ ca_const_string_scan_t s;
822
+ ca_size_t *idx;
823
+ VALUE vout;
824
+ CArray *co;
825
+ int64_t *op;
826
+ ca_size_t i;
827
+
828
+ ca_const_string_scan_begin(self, &s);
829
+ idx = ca_const_string_sort_perm(&s);
830
+ vout = rb_carray_new(CA_INT64, s.ca->ndim, s.ca->dim, 0, NULL);
831
+ TypedData_Get_Struct(vout, CArray, &carray_data_type, co);
832
+ op = (int64_t *) co->ptr;
833
+ for ( i = 0; i < s.n; i++ ) op[i] = (int64_t) idx[i];
834
+ xfree(idx);
835
+ ca_const_string_scan_end(&s);
836
+ return vout;
837
+ }
838
+
839
+ /* CAConstString#sort is defined in Ruby (lib/carray/const_string.rb) as `self[sort_index]`,
840
+ i.e. a no-copy view over the offset source (buffer + offsets shared, only
841
+ the gather order changes) — consistent with CArray#sort being a view. */
842
+
843
+ /* CAConstString#min / #max → the byte-min / byte-max element as a frozen String,
844
+ skipping masked elements; nil if empty or all masked. */
845
+ static VALUE
846
+ ca_const_string_extremum (VALUE self, int want_max)
847
+ {
848
+ ca_const_string_scan_t s;
849
+ const char *p, *bestp = NULL;
850
+ int32_t len, bestlen = 0;
851
+ ca_size_t i;
852
+ int found = 0;
853
+
854
+ ca_const_string_scan_begin(self, &s);
855
+ for ( i = 0; i < s.n; i++ ) {
856
+ int c;
857
+ if ( ! ca_const_string_record(&s, i, &p, &len) ) continue;
858
+ if ( ! found ) {
859
+ bestp = p; bestlen = len; found = 1;
860
+ continue;
861
+ }
862
+ c = memcmp(p, bestp, (size_t) (len < bestlen ? len : bestlen));
863
+ if ( c == 0 ) c = (len > bestlen) - (len < bestlen);
864
+ if ( ( want_max && c > 0 ) || ( ! want_max && c < 0 ) ) {
865
+ bestp = p; bestlen = len;
866
+ }
867
+ }
868
+ if ( ! found ) {
869
+ ca_const_string_scan_end(&s);
870
+ return Qnil;
871
+ }
872
+ {
873
+ VALUE str = rb_enc_str_new(bestp, bestlen, rb_enc_from_index(s.ca->encoding_id));
874
+ ca_const_string_scan_end(&s);
875
+ return rb_str_freeze(str);
876
+ }
877
+ }
878
+
879
+ static VALUE rb_ca_const_string_min (VALUE self) { return ca_const_string_extremum(self, 0); }
880
+ static VALUE rb_ca_const_string_max (VALUE self) { return ca_const_string_extremum(self, 1); }
881
+
882
+ /* ------------------------------------------------------------------- */
883
+
884
+ static VALUE
885
+ rb_ca_const_string_s_allocate (VALUE klass)
886
+ {
887
+ CAConstString *ca;
888
+ return TypedData_Make_Struct(klass, CAConstString, &catext_data_type, ca);
889
+ }
890
+
891
+ static VALUE
892
+ rb_ca_const_string_initialize_copy (VALUE self, VALUE other)
893
+ {
894
+ CAConstString *ca, *cs;
895
+ TypedData_Get_Struct(self, CAConstString, &catext_data_type, ca);
896
+ TypedData_Get_Struct(other, CAConstString, &catext_data_type, cs);
897
+ /* T.0/T.1: shallow re-setup (shares buffer + offset-source). T.3 will
898
+ replace this with compacting deep copy (rebased offsets + compacted
899
+ buffer + detach). */
900
+ if ( ca_func[CA_OBJ_CONST_STRING].pool_init ) {
901
+ ca_array_pool_alloc(ca, CA_OBJ_CONST_STRING, cs->parent->ndim);
902
+ }
903
+ ca_const_string_setup(ca, cs->parent, cs->buffer, cs->encoding_id);
904
+ return self;
905
+ }
906
+
907
+ void
908
+ Init_ca_obj_const_string (void)
909
+ {
910
+ rb_cCAConstString = rb_define_class("CAConstString", rb_cCAFace);
911
+
912
+ ca_const_string_func.struct_size = sizeof(CAConstString);
913
+ ca_const_string_func.pool_bytes = ca_const_string_pool_bytes;
914
+ ca_const_string_func.pool_init = ca_const_string_pool_init;
915
+
916
+ CA_OBJ_CONST_STRING = ca_install_obj_type(rb_cCAConstString,
917
+ &catext_data_type,
918
+ rb_cCArrayMask,
919
+ &carray_mask_data_type,
920
+ &ca_const_string_func, sizeof(ca_const_string_func));
921
+ rb_define_const(rb_cObject, "CA_OBJ_CONST_STRING", INT2NUM(CA_OBJ_CONST_STRING));
922
+
923
+ rb_define_alloc_func(rb_cCAConstString, rb_ca_const_string_s_allocate);
924
+ rb_define_method(rb_cCAConstString, "initialize_copy",
925
+ rb_ca_const_string_initialize_copy, 1);
926
+
927
+ rb_define_singleton_method(rb_cCAConstString, "wrap", rb_ca_const_string_wrap_method, -1);
928
+ rb_define_singleton_method(rb_cCAConstString, "__build__", rb_ca_const_string_build, 2);
929
+ rb_define_method(rb_cCAConstString, "encoding", rb_ca_const_string_encoding, 0);
930
+ rb_define_method(rb_cCAConstString, "buffer", rb_ca_const_string_buffer, 0);
931
+ rb_define_method(rb_cCAConstString, "storage_to_scalar",
932
+ rb_ca_const_string_storage_to_scalar, 1);
933
+
934
+ /* copy = compacting deep copy; to_ca is the same descent point. */
935
+ rb_define_method(rb_cCAConstString, "copy", rb_ca_const_string_copy, 0);
936
+ rb_define_method(rb_cCAConstString, "to_ca", rb_ca_const_string_to_ca, -1);
937
+
938
+ /* native byte ops (§3.7) */
939
+ rb_define_method(rb_cCAConstString, "byte_length", rb_ca_const_string_byte_length, 0);
940
+ rb_define_method(rb_cCAConstString, "eq", rb_ca_const_string_eq, 1);
941
+ rb_define_method(rb_cCAConstString, "count", rb_ca_const_string_count, 1);
942
+ rb_define_method(rb_cCAConstString, "start_with?", rb_ca_const_string_start_with, 1);
943
+ rb_define_method(rb_cCAConstString, "end_with?", rb_ca_const_string_end_with, 1);
944
+ rb_define_method(rb_cCAConstString, "include?", rb_ca_const_string_include, 1);
945
+
946
+ /* native exact-lookup search: flat index of the first byte-equal element,
947
+ or nil. `search` is what str_matches consumes; find_value_index is the
948
+ *_index-named surface (both exact, mask-skipping). */
949
+ rb_define_method(rb_cCAConstString, "search", rb_ca_const_string_search, 1);
950
+ rb_define_method(rb_cCAConstString, "find_value_index", rb_ca_const_string_search, 1);
951
+
952
+ /* native sort_index / min / max (§3.7), byte-memcmp comparator.
953
+ sort / sort_copy are defined in Ruby on top of sort_index. */
954
+ rb_define_method(rb_cCAConstString, "sort_index", rb_ca_const_string_sort_index, 0);
955
+ rb_define_method(rb_cCAConstString, "min", rb_ca_const_string_min, 0);
956
+ rb_define_method(rb_cCAConstString, "max", rb_ca_const_string_max, 0);
957
+
958
+ /* Y-pilot: Face-local C-level fast path for per-cell scalar fetch. */
959
+ ca_face_register_storage_to_scalar(CA_OBJ_CONST_STRING, rb_ca_const_string_storage_to_scalar);
960
+
961
+ /* F.S1: CAConstString's `buffer` field is per-parent (= each CAConstString holds its
962
+ own frozen byte String; cell offsets index into THIS instance's
963
+ buffer). Multi-parent CAStack lift cannot reuse list[0]'s buffer
964
+ without producing wrong reads for cells from other parents. Mark
965
+ as not-portable so CAStack rejects multi-parent CAConstString lift. */
966
+ ca_face_register_state_portable(CA_OBJ_CONST_STRING, 0);
967
+ }