carray 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (339) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +5 -25
  3. data/CHANGELOG.md +16 -0
  4. data/LICENSE +1 -1
  5. data/NEWS.md +3 -0
  6. data/README.md +128 -44
  7. data/carray.gemspec +22 -24
  8. data/ext/ca_array_pool.c +91 -0
  9. data/ext/ca_axis_descriptor.h +186 -0
  10. data/ext/ca_axis_dispatch.c +924 -0
  11. data/ext/ca_axis_group.c +1208 -0
  12. data/ext/ca_bincmp_dispatch.c +76 -0
  13. data/ext/ca_bincmp_dispatch.h +85 -0
  14. data/ext/ca_binop_dispatch.c +125 -0
  15. data/ext/ca_binop_dispatch.h +159 -0
  16. data/ext/ca_categorical_iterator.c +1375 -0
  17. data/ext/ca_compare.c +94 -0
  18. data/ext/ca_compare.h +26 -0
  19. data/ext/ca_composite_dispatch.c +414 -0
  20. data/ext/ca_composite_dispatch.h +116 -0
  21. data/ext/ca_for_buffer.h +96 -0
  22. data/ext/ca_for_each_element.h +241 -0
  23. data/ext/ca_group_iter.c +304 -0
  24. data/ext/ca_iter_substrate.h +325 -0
  25. data/ext/ca_kernel_iterator.c +4321 -0
  26. data/ext/ca_kernel_iterator.h +2603 -0
  27. data/ext/ca_moncmp_dispatch.c +37 -0
  28. data/ext/ca_moncmp_dispatch.h +62 -0
  29. data/ext/ca_monop_dispatch.c +200 -0
  30. data/ext/ca_monop_dispatch.h +235 -0
  31. data/ext/ca_obj_array.c +355 -359
  32. data/ext/ca_obj_bincmp.c +809 -0
  33. data/ext/ca_obj_binop.c +892 -0
  34. data/ext/ca_obj_bitarray.c +369 -164
  35. data/ext/ca_obj_bitfield.c +294 -234
  36. data/ext/ca_obj_block.c +189 -711
  37. data/ext/ca_obj_byte_swap.c +766 -0
  38. data/ext/ca_obj_const_string.c +965 -0
  39. data/ext/ca_obj_face.c +670 -0
  40. data/ext/ca_obj_face.h +247 -0
  41. data/ext/ca_obj_fake.c +228 -100
  42. data/ext/ca_obj_farray.c +54 -441
  43. data/ext/ca_obj_field.c +82 -529
  44. data/ext/ca_obj_fixlen_string.c +306 -0
  45. data/ext/ca_obj_grid.c +858 -440
  46. data/ext/ca_obj_meld.c +1034 -0
  47. data/ext/ca_obj_moncmp.c +569 -0
  48. data/ext/ca_obj_monop.c +1111 -0
  49. data/ext/ca_obj_object.c +774 -298
  50. data/ext/ca_obj_record.c +468 -0
  51. data/ext/ca_obj_reduce.c +97 -82
  52. data/ext/ca_obj_refer.c +569 -459
  53. data/ext/ca_obj_remap.c +475 -0
  54. data/ext/ca_obj_repeat.c +92 -477
  55. data/ext/ca_obj_roll.c +616 -0
  56. data/ext/ca_obj_select.c +344 -296
  57. data/ext/ca_obj_select_axis.c +1296 -0
  58. data/ext/ca_obj_shift.c +230 -792
  59. data/ext/ca_obj_source.c +78 -0
  60. data/ext/ca_obj_stack.c +1173 -0
  61. data/ext/ca_obj_stride.c +2501 -0
  62. data/ext/ca_obj_string.c +268 -0
  63. data/ext/ca_obj_tile.c +614 -0
  64. data/ext/ca_obj_time.c +546 -0
  65. data/ext/ca_obj_timedelta.c +435 -0
  66. data/ext/ca_obj_transpose.c +62 -516
  67. data/ext/ca_obj_triop.c +746 -0
  68. data/ext/ca_obj_unbound_repeat.c +208 -241
  69. data/ext/ca_obj_window.c +1131 -563
  70. data/ext/ca_op_byte_swap.c +175 -0
  71. data/ext/ca_op_ipower.c +319 -0
  72. data/ext/ca_op_powi.h +88 -0
  73. data/ext/ca_sort_kernels.h +132 -0
  74. data/ext/ca_sweep_engine.c +430 -0
  75. data/ext/ca_sweep_engine.h +157 -0
  76. data/ext/ca_transform_common.c +228 -0
  77. data/ext/ca_triop_dispatch.c +55 -0
  78. data/ext/ca_triop_dispatch.h +62 -0
  79. data/ext/carray.h +795 -402
  80. data/ext/carray_access.c +831 -711
  81. data/ext/carray_attribute.c +98 -330
  82. data/ext/carray_bincount.c +255 -0
  83. data/ext/carray_broadcast.c +283 -0
  84. data/ext/carray_call_cfunc.c +1360 -828
  85. data/ext/carray_call_cfunc.h +160 -0
  86. data/ext/carray_cast.c +1212 -301
  87. data/ext/carray_cast_func.rb +81 -40
  88. data/ext/carray_class.c +53 -63
  89. data/ext/carray_config.h +28 -0
  90. data/ext/carray_conversion.c +350 -346
  91. data/ext/carray_copy.c +156 -268
  92. data/ext/carray_core.c +1342 -199
  93. data/ext/carray_count.c +312 -0
  94. data/ext/carray_data_type.c +43 -19
  95. data/ext/carray_element.c +585 -213
  96. data/ext/carray_factorize.c +2542 -0
  97. data/ext/carray_generate.c +230 -559
  98. data/ext/carray_histogram.c +490 -0
  99. data/ext/carray_hold.c +228 -0
  100. data/ext/carray_index_classifier.c +1035 -0
  101. data/ext/carray_index_classifier.h +27 -0
  102. data/ext/carray_internal.h +120 -0
  103. data/ext/carray_kernels_bincmp.c +4445 -0
  104. data/ext/carray_kernels_binop.c +10979 -0
  105. data/ext/carray_kernels_init.c +36 -0
  106. data/ext/carray_kernels_map.c +3466 -0
  107. data/ext/carray_kernels_moncmp.c +2096 -0
  108. data/ext/carray_kernels_monop.c +18312 -0
  109. data/ext/carray_kernels_reduce_aggregate.c +25836 -0
  110. data/ext/carray_kernels_reduce_boolean.c +329 -0
  111. data/ext/carray_kernels_reduce_cumulative.c +14592 -0
  112. data/ext/carray_kernels_reduce_extreme.c +16947 -0
  113. data/ext/carray_kernels_reduce_variance.c +3909 -0
  114. data/ext/carray_kernels_scan.c +3692 -0
  115. data/ext/carray_kernels_search.c +32137 -0
  116. data/ext/carray_kernels_sort.c +10625 -0
  117. data/ext/carray_kernels_triop.c +1391 -0
  118. data/ext/carray_lazy.c +567 -0
  119. data/ext/carray_loop.c +88 -200
  120. data/ext/carray_mask.c +848 -154
  121. data/ext/carray_math_kernel.h +120 -0
  122. data/ext/carray_mathfunc.c +10 -241
  123. data/ext/carray_median_percentile.c +1257 -0
  124. data/ext/carray_memory_view.c +1625 -0
  125. data/ext/carray_operator.c +1526 -318
  126. data/ext/carray_order.c +664 -1394
  127. data/ext/carray_partition.c +416 -0
  128. data/ext/carray_random.c +518 -0
  129. data/ext/carray_scatter.c +357 -0
  130. data/ext/carray_slab.c +1219 -0
  131. data/ext/carray_slab.h +84 -0
  132. data/ext/carray_sort.c +829 -0
  133. data/ext/carray_sort_kernel.c +620 -0
  134. data/ext/carray_struct.c +695 -0
  135. data/ext/carray_test.c +343 -229
  136. data/ext/carray_undef.c +34 -17
  137. data/ext/carray_utils.c +175 -74
  138. data/ext/extconf.rb +216 -55
  139. data/ext/mk_call_cfunc.rb +480 -0
  140. data/ext/mkkernel.rb +8842 -0
  141. data/ext/ruby_carray.c +202 -101
  142. data/ext/version.h +4 -14
  143. data/ext/version.rb +5 -13
  144. data/lib/carray/arrow_tensor.rb +401 -0
  145. data/lib/carray/attribute.rb +166 -0
  146. data/lib/carray/autoload_carray.rb +220 -0
  147. data/lib/carray/autoload_method_extension.rb +44 -0
  148. data/lib/carray/axis_group.rb +711 -0
  149. data/lib/carray/basics.rb +481 -0
  150. data/lib/carray/bincount_nd.rb +358 -0
  151. data/lib/carray/block_iterator.rb +604 -0
  152. data/lib/carray/boolean_reduce.rb +109 -0
  153. data/lib/carray/categorical.rb +561 -0
  154. data/lib/carray/categorical_iterator.rb +1062 -0
  155. data/lib/carray/complex.rb +150 -0
  156. data/lib/carray/conditional.rb +216 -0
  157. data/lib/carray/const_string.rb +228 -0
  158. data/lib/carray/construct.rb +139 -328
  159. data/lib/carray/core_extensions.rb +240 -0
  160. data/lib/carray/data_type_extension.rb +233 -0
  161. data/lib/carray/fixlen_string.rb +95 -0
  162. data/lib/carray/frame/concat.rb +132 -0
  163. data/lib/carray/frame/convert.rb +95 -0
  164. data/lib/carray/frame/csv_parser.rb +211 -0
  165. data/lib/carray/frame/frame.rb +649 -0
  166. data/lib/carray/frame/group.rb +186 -0
  167. data/lib/carray/frame/io.rb +164 -0
  168. data/lib/carray/frame/join.rb +248 -0
  169. data/lib/carray/frame/records.rb +99 -0
  170. data/lib/carray/frame/sort.rb +113 -0
  171. data/lib/carray/frame/verbs.rb +299 -0
  172. data/lib/carray/frame.rb +16 -0
  173. data/lib/carray/histogram.rb +512 -0
  174. data/lib/carray/inspect.rb +37 -20
  175. data/lib/carray/iterator.rb +57 -349
  176. data/lib/carray/lazy.rb +889 -0
  177. data/lib/carray/mask_gap_fill.rb +200 -0
  178. data/lib/carray/math.rb +78 -342
  179. data/lib/carray/meld_reduce.rb +289 -0
  180. data/lib/carray/methods/align_addr.rb +116 -0
  181. data/lib/carray/methods/bin.rb +128 -0
  182. data/lib/carray/methods/bincount.rb +87 -0
  183. data/lib/carray/methods/bit_string.rb +92 -0
  184. data/lib/carray/methods/broadcast.rb +63 -0
  185. data/lib/carray/methods/choose.rb +39 -0
  186. data/lib/carray/methods/composition.rb +280 -0
  187. data/lib/carray/methods/gather_nd.rb +206 -0
  188. data/lib/carray/methods/index.rb +39 -0
  189. data/lib/carray/methods/insert_block.rb +99 -0
  190. data/lib/carray/methods/is_in.rb +141 -0
  191. data/lib/carray/methods/join.rb +90 -0
  192. data/lib/carray/methods/locate_addr.rb +47 -0
  193. data/lib/carray/methods/mask_duplicates.rb +41 -0
  194. data/lib/carray/methods/meshgrid.rb +91 -0
  195. data/lib/carray/methods/mode.rb +126 -0
  196. data/lib/carray/methods/nunique.rb +46 -0
  197. data/lib/carray/methods/resize.rb +56 -0
  198. data/lib/carray/methods/snap.rb +156 -0
  199. data/lib/carray/methods/string_format.rb +57 -0
  200. data/lib/carray/methods/unique.rb +47 -0
  201. data/lib/carray/methods/value_counts.rb +71 -0
  202. data/lib/carray/mkmf.rb +124 -101
  203. data/lib/carray/runtime.rb +108 -0
  204. data/lib/carray/serialize.rb +478 -167
  205. data/lib/carray/slab_iterator.rb +292 -0
  206. data/lib/carray/stack.rb +291 -0
  207. data/lib/carray/string.rb +56 -180
  208. data/lib/carray/string_operation_extension.rb +289 -0
  209. data/lib/carray/struct.rb +335 -323
  210. data/lib/carray/struct_builder.rb +697 -0
  211. data/lib/carray/table.rb +41 -2
  212. data/lib/carray/time.rb +2255 -38
  213. data/lib/carray/window_iterator.rb +655 -0
  214. data/lib/carray.rb +55 -57
  215. metadata +163 -130
  216. data/Rakefile +0 -51
  217. data/TODO.md +0 -18
  218. data/ext/ca_iter_block.c +0 -257
  219. data/ext/ca_iter_dimension.c +0 -299
  220. data/ext/ca_iter_window.c +0 -214
  221. data/ext/ca_obj_mapping.c +0 -644
  222. data/ext/carray_iterator.c +0 -641
  223. data/ext/carray_math.rb +0 -850
  224. data/ext/carray_numeric.c +0 -259
  225. data/ext/carray_sort_addr.c +0 -254
  226. data/ext/carray_stat.c +0 -2100
  227. data/ext/carray_stat_proc.rb +0 -1999
  228. data/ext/mkmath.rb +0 -741
  229. data/ext/ruby_ccomplex.c +0 -509
  230. data/ext/ruby_float_func.c +0 -86
  231. data/lib/carray/array.rb +0 -8
  232. data/lib/carray/autoload/autoload_base.rb +0 -19
  233. data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
  234. data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
  235. data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
  236. data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
  237. data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
  238. data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
  239. data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
  240. data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
  241. data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
  242. data/lib/carray/autoload/autoload_gem_random.rb +0 -8
  243. data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
  244. data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
  245. data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
  246. data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
  247. data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
  248. data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
  249. data/lib/carray/autoload/autoload_object_link.rb +0 -1
  250. data/lib/carray/autoload/autoload_object_pack.rb +0 -2
  251. data/lib/carray/autoload.rb +0 -141
  252. data/lib/carray/basic.rb +0 -191
  253. data/lib/carray/broadcast.rb +0 -101
  254. data/lib/carray/compose.rb +0 -315
  255. data/lib/carray/convert.rb +0 -115
  256. data/lib/carray/info.rb +0 -110
  257. data/lib/carray/io/imagemagick.rb +0 -235
  258. data/lib/carray/mask.rb +0 -102
  259. data/lib/carray/math/histogram.rb +0 -177
  260. data/lib/carray/math/recurrence.rb +0 -93
  261. data/lib/carray/object/ca_obj_iterator.rb +0 -50
  262. data/lib/carray/object/ca_obj_link.rb +0 -50
  263. data/lib/carray/object/ca_obj_pack.rb +0 -99
  264. data/lib/carray/obsolete.rb +0 -256
  265. data/lib/carray/ordering.rb +0 -181
  266. data/lib/carray/testing.rb +0 -51
  267. data/lib/carray/transform.rb +0 -109
  268. data/misc/Methods.ja.md +0 -182
  269. data/misc/NOTE +0 -51
  270. data/spec/Classes/CABitfield_spec.rb +0 -58
  271. data/spec/Classes/CABlockIterator_spec.rb +0 -114
  272. data/spec/Classes/CABlock_spec.rb +0 -205
  273. data/spec/Classes/CAField_spec.rb +0 -39
  274. data/spec/Classes/CAGrid_spec.rb +0 -75
  275. data/spec/Classes/CAMap_spec.rb +0 -0
  276. data/spec/Classes/CAMapping_spec.rb +0 -105
  277. data/spec/Classes/CAObject_attribute_spec.rb +0 -33
  278. data/spec/Classes/CAObject_spec.rb +0 -33
  279. data/spec/Classes/CARefer_spec.rb +0 -93
  280. data/spec/Classes/CARepeat_spec.rb +0 -65
  281. data/spec/Classes/CASelect_spec.rb +0 -22
  282. data/spec/Classes/CAShift_spec.rb +0 -16
  283. data/spec/Classes/CAStruct_spec.rb +0 -71
  284. data/spec/Classes/CATranspose_spec.rb +0 -60
  285. data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
  286. data/spec/Classes/CAWindow_spec.rb +0 -54
  287. data/spec/Classes/CAWrap_spec.rb +0 -8
  288. data/spec/Classes/CArray_spec.rb +0 -184
  289. data/spec/Classes/CScalar_spec.rb +0 -55
  290. data/spec/Classes/ex1.rb +0 -46
  291. data/spec/Features/feature_130_spec.rb +0 -19
  292. data/spec/Features/feature_attributes_spec.rb +0 -280
  293. data/spec/Features/feature_boolean_spec.rb +0 -98
  294. data/spec/Features/feature_broadcast.rb +0 -116
  295. data/spec/Features/feature_cast_function.rb +0 -19
  296. data/spec/Features/feature_cast_spec.rb +0 -33
  297. data/spec/Features/feature_class_spec.rb +0 -84
  298. data/spec/Features/feature_complex_spec.rb +0 -42
  299. data/spec/Features/feature_composite_spec.rb +0 -124
  300. data/spec/Features/feature_convert_spec.rb +0 -46
  301. data/spec/Features/feature_copy_spec.rb +0 -123
  302. data/spec/Features/feature_creation_spec.rb +0 -84
  303. data/spec/Features/feature_element_spec.rb +0 -144
  304. data/spec/Features/feature_extream_spec.rb +0 -54
  305. data/spec/Features/feature_generate_spec.rb +0 -74
  306. data/spec/Features/feature_index_spec.rb +0 -69
  307. data/spec/Features/feature_mask_spec.rb +0 -580
  308. data/spec/Features/feature_math_spec.rb +0 -97
  309. data/spec/Features/feature_order_spec.rb +0 -146
  310. data/spec/Features/feature_ref_store_spec.rb +0 -209
  311. data/spec/Features/feature_serialization_spec.rb +0 -125
  312. data/spec/Features/feature_stat_spec.rb +0 -397
  313. data/spec/Features/feature_virtual_spec.rb +0 -48
  314. data/spec/Features/method_eq_spec.rb +0 -81
  315. data/spec/Features/method_is_nan_spec.rb +0 -12
  316. data/spec/Features/method_map_spec.rb +0 -54
  317. data/spec/Features/method_max_with.rb +0 -20
  318. data/spec/Features/method_min_with.rb +0 -19
  319. data/spec/Features/method_ne_spec.rb +0 -18
  320. data/spec/Features/method_project_spec.rb +0 -188
  321. data/spec/Features/method_ref_spec.rb +0 -27
  322. data/spec/Features/method_round_spec.rb +0 -11
  323. data/spec/Features/method_s_linspace_spec.rb +0 -48
  324. data/spec/Features/method_s_span_spec.rb +0 -14
  325. data/spec/Features/method_seq_spec.rb +0 -47
  326. data/spec/Features/method_sort_with.rb +0 -43
  327. data/spec/Features/method_sorted_with.rb +0 -29
  328. data/spec/Features/method_span_spec.rb +0 -42
  329. data/spec/Features/method_wrap_readonly_spec.rb +0 -43
  330. data/spec/UnitTest/test_CAVirtual.rb +0 -214
  331. data/spec/spec_all.rb +0 -10
  332. data/utils/ca_ase.rb +0 -21
  333. data/utils/ca_methods.rb +0 -15
  334. data/utils/cast_checker.rb +0 -30
  335. data/utils/convert_test.rb +0 -73
  336. data/utils/extract_yard.rb +0 -22
  337. data/utils/guess_shape.rb +0 -76
  338. data/utils/monkey_patch_methods.rb +0 -62
  339. data/utils/remove_resource_fork.sh +0 -5
data/ext/carray_test.c CHANGED
@@ -1,26 +1,21 @@
1
1
  /* ---------------------------------------------------------------------------
2
2
 
3
- carray_test.c
4
-
5
- This file is part of Ruby/CArray extension library.
6
-
7
- Copyright (C) 2005-2020 Hiroki Motoyoshi
8
-
9
- This file includes the modified routine (ca_mem_hash) from
10
-
11
- * string.c in Ruby distribution ( ruby-1.8.6 )
12
-
13
- Copyright (C) 1993-2003 Yukihiro Matsumoto
14
- Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
15
- Copyright (C) 2000 Information-technology Promotion Agency, Japan
3
+ Predicates and equality/hash for CArray:
4
+ - internal check_* / has_same_* helpers used across ext/ sources
5
+ - Ruby surface: valid_index? / valid_addr? / same_shape? / == /
6
+ eql? / hash / freeze (docs in yard-stubs/carray_test.rb)
7
+ - per-dtype element-equality table `ca_eql[CA_NTYPE]`
8
+ - `ca_compare_common` shared body for `==` and `eql?`
16
9
 
17
10
  ---------------------------------------------------------------------------- */
18
11
 
19
12
  #include "carray.h"
20
13
 
21
14
  static ID id_equal;
15
+ static ID id_eql;
22
16
 
23
- /* various checking routine */
17
+ /* Internal checking routines: raise on structural mismatch. Used
18
+ throughout ext/ sources at API boundaries; not exposed to Ruby. */
24
19
 
25
20
  void
26
21
  ca_check_type (void *ap, int8_t data_type)
@@ -127,7 +122,7 @@ rb_check_carray_object (VALUE arg)
127
122
  }
128
123
  }
129
124
 
130
- /* various predicate routine */
125
+ /* Internal predicate routines: return 0/1, do not raise. */
131
126
 
132
127
  int
133
128
  ca_has_same_shape (void *ap1, void *ap2)
@@ -164,8 +159,8 @@ ca_is_valid_index (void *ap, ca_size_t *idx)
164
159
  return 1;
165
160
  }
166
161
 
167
- /* predicate whether data_type is integer or not */
168
-
162
+ /* rb_ca_is_type(arg, type) returns 1 if arg is a CArray whose
163
+ data_type equals `type`, else raises if not a CArray. */
169
164
  int
170
165
  rb_ca_is_type (VALUE arg, int type)
171
166
  {
@@ -177,59 +172,13 @@ rb_ca_is_type (VALUE arg, int type)
177
172
  return ca->data_type == type;
178
173
  }
179
174
 
180
- /* ------------------------------------------------------------- */
181
-
182
- void
183
- ca_check_data_class (VALUE rtype)
184
- {
185
- if ( ! rb_obj_is_data_class(rtype) ) {
186
- VALUE inspect = rb_inspect(rtype);
187
- rb_raise(rb_eRuntimeError,
188
- "<%s> is not a data_class, which should has the features\n" \
189
- " * constant data_class::DATA_SIZE -> integer\n" \
190
- " * constant data_class::MEMBERS -> array of string\n" \
191
- " * constant data_class::MEMBER_TABLE -> hash\n" \
192
- " * method data_class.decode(str) -> data_class object\n" \
193
- " * method data_class#encode() -> string", StringValuePtr(inspect));
194
- }
195
- }
196
-
197
- VALUE
198
- rb_obj_is_data_class (VALUE rtype)
199
- {
200
- VALUE has_data_size, has_member_names, has_member_table;
201
- VALUE has_encode, has_decode;
202
- if ( TYPE(rtype) == T_CLASS ) {
203
- has_data_size =
204
- rb_funcall(rtype, rb_intern("const_defined?"), 1, rb_str_new2("DATA_SIZE"));
205
- has_member_names =
206
- rb_funcall(rtype, rb_intern("const_defined?"), 1, rb_str_new2("MEMBERS"));
207
- has_member_table =
208
- rb_funcall(rtype, rb_intern("const_defined?"), 1, rb_str_new2("MEMBER_TABLE"));
209
- has_encode =
210
- rb_funcall(rtype, rb_intern("method_defined?"), 1, rb_str_new2("encode"));
211
- has_decode = rb_respond_to(rtype, rb_intern("decode"));
212
- return ( RTEST(has_data_size) &&
213
- RTEST(has_member_table) && RTEST(has_member_names) &&
214
- RTEST(has_encode) && RTEST(has_decode) ) ? Qtrue : Qfalse;
215
- }
216
- return Qfalse;
217
- }
218
-
219
- static VALUE
220
- rb_ca_s_is_data_class (VALUE self, VALUE rklass)
221
- {
222
- return rb_obj_is_data_class(rklass);
223
- }
224
-
225
- /* ------------------------------------------------------------- */
226
-
227
- /* @overload valid_index? (*idx)
228
-
229
- (Inquiry)
230
- Returns true if the given number list is valid as array index for the object
231
- */
175
+ /* [MOVED] data_class identity predicates
176
+ (ca_check_data_class / rb_obj_is_data_class / rb_ca_s_is_data_class)
177
+ live in ca_obj_record.c; signatures stay in carray.h for cross-file
178
+ extern use. */
232
179
 
180
+ /* CArray#valid_index?(*idx) — 1 if the index tuple is in range, else 0.
181
+ * The number of indices must equal ndim (raises otherwise). */
233
182
  static VALUE
234
183
  rb_ca_is_valid_index (int argc, VALUE *argv, VALUE self)
235
184
  {
@@ -245,11 +194,6 @@ rb_ca_is_valid_index (int argc, VALUE *argv, VALUE self)
245
194
  }
246
195
  for (i=0; i<ca->ndim; i++) {
247
196
  idx = NUM2SIZE(argv[i]);
248
- /*
249
- if ( idx < 0 ) {
250
- idx += ca->dim[i];
251
- }
252
- */
253
197
  if ( idx < 0 || idx >= ca->dim[i] ) {
254
198
  return Qfalse;
255
199
  }
@@ -258,12 +202,7 @@ rb_ca_is_valid_index (int argc, VALUE *argv, VALUE self)
258
202
  return Qtrue;
259
203
  }
260
204
 
261
- /* @overload valid_addr? (*addr)
262
-
263
- (Inquiry)
264
- Returns true if the given number is valid as array address for the object
265
- */
266
-
205
+ /* CArray#valid_addr?(addr) — 1 if addr is in range 0...elements, else 0. */
267
206
  static VALUE
268
207
  rb_ca_is_valid_addr (VALUE self, VALUE raddr)
269
208
  {
@@ -272,11 +211,6 @@ rb_ca_is_valid_addr (VALUE self, VALUE raddr)
272
211
 
273
212
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
274
213
  addr = NUM2SIZE(raddr);
275
- /*
276
- if ( addr < 0 ) {
277
- addr += ca->elements;
278
- }
279
- */
280
214
  if ( addr < 0 || addr >= ca->elements ) {
281
215
  return Qfalse;
282
216
  }
@@ -285,12 +219,8 @@ rb_ca_is_valid_addr (VALUE self, VALUE raddr)
285
219
  }
286
220
  }
287
221
 
288
- /* @overload has_same_shape?
289
-
290
- (Inquiry)
291
- Returns true if the object has the same shape with the given array.
292
- */
293
-
222
+ /* CArray#same_shape?(other) — 1 if self and other share ndim and dim[].
223
+ * Scalars compare true against any array (see ca_has_same_shape). */
294
224
  static VALUE
295
225
  rb_ca_has_same_shape (VALUE self, VALUE other)
296
226
  {
@@ -302,25 +232,25 @@ rb_ca_has_same_shape (VALUE self, VALUE other)
302
232
 
303
233
  /* ----------------------------------------------------------------------- */
304
234
 
305
- typedef int (*ca_eql_func)();
235
+ typedef int (*ca_eql_func)(const char *, const char *, ca_size_t);
306
236
 
307
237
  #define eql_type(type) \
308
238
  static int \
309
- eql_## type (type *a, type *b, int bytes) \
239
+ eql_## type (const char *a, const char *b, ca_size_t bytes) \
310
240
  { \
311
- return ( *a == *b ); \
241
+ return ( *(const type*)a == *(const type*)b ); \
312
242
  }
313
243
 
314
244
  static int
315
- eql_VALUE (VALUE *a, VALUE *b, int bytes)
245
+ eql_VALUE (const char *a, const char *b, ca_size_t bytes)
316
246
  {
317
- return ( rb_funcall(*a, id_equal, 1, *b) ) ? 1 : 0;
247
+ return RTEST(rb_funcall(*(const VALUE*)a, id_equal, 1, *(const VALUE*)b)) ? 1 : 0;
318
248
  }
319
249
 
320
250
  static int
321
- eql_data (char *a, char *b, int bytes)
251
+ eql_data (const char *a, const char *b, ca_size_t bytes)
322
252
  {
323
- return ( memcmp(a, b, bytes) ) ? 0 : 1;
253
+ return ( memcmp(a, b, (size_t)bytes) ) ? 0 : 1;
324
254
  }
325
255
 
326
256
  eql_type(boolean8_t)
@@ -334,10 +264,16 @@ eql_type(int64_t)
334
264
  eql_type(uint64_t)
335
265
  eql_type(float32_t)
336
266
  eql_type(float64_t)
337
- eql_type(float128_t)
338
267
  eql_type(cmplx64_t)
339
268
  eql_type(cmplx128_t)
340
- eql_type(cmplx256_t)
269
+
270
+ /* Placeholder for CA_FLOAT128 / CA_CMPLX256 slots (ca_valid[12] =
271
+ ca_valid[15] = 0, so never invoked). */
272
+ static int
273
+ eql_retired (const char *a, const char *b, ca_size_t bytes)
274
+ {
275
+ return 0;
276
+ }
341
277
 
342
278
  ca_eql_func
343
279
  ca_eql[CA_NTYPE] = {
@@ -353,15 +289,29 @@ ca_eql[CA_NTYPE] = {
353
289
  eql_uint64_t,
354
290
  eql_float32_t,
355
291
  eql_float64_t,
356
- eql_float128_t,
292
+ eql_retired, /* float128_t: ca_valid[12] = 0 */
357
293
  eql_cmplx64_t,
358
294
  eql_cmplx128_t,
359
- eql_cmplx256_t,
295
+ eql_retired, /* cmplx256_t: ca_valid[15] = 0 */
360
296
  eql_VALUE,
361
297
  };
362
298
 
363
- int
364
- ca_equal (void *ap, void *bp)
299
+ /* ca_compare_common(a, b, strict) — shared body of ca_equal (==) and
300
+ * ca_eql_strict (eql?). Returns 1 if arrays match under the selected
301
+ * semantics, else 0.
302
+ *
303
+ * Common structure: metadata pre-check (scalar / data_type / bytes /
304
+ * ndim / elements / dim), mask attach via ca_attach_n (recurses into
305
+ * mask), unified loop over the four masked/unmasked combinations.
306
+ *
307
+ * The only difference is the per-element comparator:
308
+ * == : ca_eql[data_type] (type-specific, so float NaN != NaN)
309
+ * eql?: memcmp for numeric, rb_funcall(:eql?) for CA_OBJECT
310
+ *
311
+ * The unmasked-unmasked branch takes a bulk memcmp or per-cell
312
+ * shortcut to amortise per-element function pointer dispatch. */
313
+ static int
314
+ ca_compare_common (void *ap, void *bp, int strict)
365
315
  {
366
316
  CArray *ca = (CArray *) ap;
367
317
  CArray *cb = (CArray *) bp;
@@ -372,83 +322,121 @@ ca_equal (void *ap, void *bp)
372
322
  ca_size_t bytes;
373
323
  char *pa;
374
324
  char *pb;
375
- ca_eql_func eql;
325
+ ca_eql_func eql = NULL; /* used only when !strict */
326
+ int is_object;
376
327
 
377
- if ( ca_is_scalar(ca) ^ ca_is_scalar(cb) ) { /* scalar comparison */
328
+ if ( ca_is_scalar(ca) ^ ca_is_scalar(cb) ) {
378
329
  return 0;
379
330
  }
380
-
381
- if ( ca->data_type != cb->data_type ) { /* data_type comparison */
331
+ if ( ca->data_type != cb->data_type ) {
382
332
  return 0;
383
333
  }
384
-
385
- if ( ca->bytes != cb->bytes ) { /* data_type comparison */
334
+ if ( ca->bytes != cb->bytes ) {
386
335
  return 0;
387
336
  }
388
-
389
- if ( ca->ndim != cb->ndim ) { /* ndim comparison */
337
+ if ( ca->ndim != cb->ndim ) {
390
338
  return 0;
391
339
  }
392
-
393
- if ( ca->elements != cb->elements ) { /* elements comparison */
340
+ if ( ca->elements != cb->elements ) {
394
341
  return 0;
395
342
  }
396
-
397
343
  for (i=0; i<ca->ndim; i++) {
398
- if ( ca->dim[i] != cb->dim[i] ) { /* dimensional shape comparison */
344
+ if ( ca->dim[i] != cb->dim[i] ) {
399
345
  return 0;
400
346
  }
401
347
  }
402
348
 
403
- ca_attach_n(2, ca, cb); /* array contents comparison */
349
+ ca_attach_n(2, ca, cb); /* recurses into ca->mask / cb->mask */
404
350
 
405
351
  masked_a = ca_is_any_masked(ca);
406
352
  masked_b = ca_is_any_masked(cb);
407
-
408
- bytes = ca->bytes;
409
-
410
- pa = ca->ptr;
411
- pb = cb->ptr;
412
-
413
- eql = ca_eql[ca->data_type];
414
-
415
- if ( masked_a && masked_b ) { /* masked vs masked */
416
- ma = (boolean8_t*) ca->mask->ptr;
417
- mb = (boolean8_t*) cb->mask->ptr;
418
- for (i=0; i<ca->elements; i++, ma++, mb++) {
419
- if ( *ma != *mb || ( ( ! *ma ) && ( ! eql(pa, pb, bytes) ) ) ) {
420
- flag = 0;
421
- break;
353
+ bytes = ca->bytes;
354
+ pa = ca->ptr;
355
+ pb = cb->ptr;
356
+ is_object = ( ca->data_type == CA_OBJECT );
357
+
358
+ if ( ! strict ) {
359
+ eql = ca_eql[ca->data_type];
360
+ }
361
+
362
+ /* unmasked-unmasked fast path: one bulk memcmp for numeric, per-cell
363
+ rb_funcall for CA_OBJECT. Avoids per-element function pointer dispatch
364
+ and enables SIMD memcmp on the common case. For strict (eql?) numeric
365
+ comparison is bitwise (= NaN.eql?(NaN) yields true since their bit
366
+ patterns match), so memcmp gives the right semantics. For non-strict
367
+ (==) of numeric data_types, type-equality is byte-equality EXCEPT for
368
+ floats where -0.0 == +0.0 (different bit pattern, same value) and
369
+ NaN != NaN (same bit pattern, unequal value) -- so we must keep the
370
+ per-element comparator there.
371
+
372
+ Integer / bool / fixlen data_types are byte-exact, so memcmp matches both
373
+ semantics. data_type >= CA_FLOAT32 (= floats / complex / object) needs
374
+ the comparator. */
375
+ if ( ! masked_a && ! masked_b ) {
376
+ if ( is_object ) {
377
+ ID id = strict ? id_eql : id_equal;
378
+ VALUE *va = (VALUE *) pa;
379
+ VALUE *vb = (VALUE *) pb;
380
+ for (i=0; i<ca->elements; i++) {
381
+ volatile VALUE x = va[i];
382
+ volatile VALUE y = vb[i];
383
+ if ( ! RTEST(rb_funcall(x, id, 1, y)) ) {
384
+ flag = 0;
385
+ break;
386
+ }
422
387
  }
423
- pa += bytes; pb += bytes;
424
388
  }
425
- }
426
- else if ( masked_a ) { /* masked vs not-masked */
427
- ma = (boolean8_t*) ca->mask->ptr;
428
- for (i=0; i<ca->elements; i++, ma++) {
429
- if ( *ma || ( ! eql(pa, pb, bytes) ) ) {
430
- flag = 0;
431
- break;
432
- }
433
- pa += bytes; pb += bytes;
389
+ else if ( strict
390
+ || ca->data_type <= CA_UINT64
391
+ || ca->data_type == CA_FIXLEN ) {
392
+ /* bytewise comparable: single memcmp */
393
+ flag = ( memcmp(pa, pb, (size_t)(ca->elements * bytes)) == 0 ) ? 1 : 0;
434
394
  }
435
- }
436
- else if ( masked_b ) { /* not-masked vs masked */
437
- mb = (boolean8_t*) cb->mask->ptr;
438
- for (i=0; i<ca->elements; i++, mb++) {
439
- if ( *mb || ( ! eql(pa, pb, bytes) ) ) {
440
- flag = 0;
441
- break;
395
+ else {
396
+ /* float / complex == : per-element comparator preserves NaN/-0.0 semantics */
397
+ for (i=0; i<ca->elements; i++) {
398
+ if ( ! eql(pa, pb, bytes) ) {
399
+ flag = 0;
400
+ break;
401
+ }
402
+ pa += bytes; pb += bytes;
442
403
  }
443
- pa += bytes; pb += bytes;
444
404
  }
445
405
  }
446
- else { /* not-masked vs not-masked */
406
+ else {
407
+ /* masked path: unified loop. ma/mb NULL => that side has no any-masked
408
+ cells. Loop invariants are simple enough that branch prediction
409
+ absorbs the per-iteration NULL test. */
410
+ ma = masked_a ? (boolean8_t *) ca->mask->ptr : NULL;
411
+ mb = masked_b ? (boolean8_t *) cb->mask->ptr : NULL;
447
412
  for (i=0; i<ca->elements; i++) {
448
- if ( ! eql(pa, pb, bytes) ) {
413
+ boolean8_t am = ma ? ma[i] : 0;
414
+ boolean8_t bm = mb ? mb[i] : 0;
415
+ if ( am != bm ) {
449
416
  flag = 0;
450
417
  break;
451
418
  }
419
+ if ( ! am ) {
420
+ int eq;
421
+ if ( is_object ) {
422
+ volatile VALUE x = *(VALUE *) pa;
423
+ volatile VALUE y = *(VALUE *) pb;
424
+ ID id = strict ? id_eql : id_equal;
425
+ eq = RTEST(rb_funcall(x, id, 1, y));
426
+ }
427
+ else if ( strict
428
+ || ca->data_type <= CA_UINT64
429
+ || ca->data_type == CA_FIXLEN ) {
430
+ eq = ( memcmp(pa, pb, (size_t)bytes) == 0 );
431
+ }
432
+ else {
433
+ eq = eql(pa, pb, bytes);
434
+ }
435
+ if ( ! eq ) {
436
+ flag = 0;
437
+ break;
438
+ }
439
+ }
452
440
  pa += bytes; pb += bytes;
453
441
  }
454
442
  }
@@ -458,12 +446,22 @@ ca_equal (void *ap, void *bp)
458
446
  return flag;
459
447
  }
460
448
 
461
- /* @overload == (other)
449
+ int
450
+ ca_equal (void *ap, void *bp)
451
+ {
452
+ return ca_compare_common(ap, bp, 0);
453
+ }
462
454
 
463
- (Inquiry)
464
- Returns true if the object equals the given array.
465
- */
455
+ static int
456
+ ca_eql_strict (void *ap, void *bp)
457
+ {
458
+ return ca_compare_common(ap, bp, 1);
459
+ }
466
460
 
461
+ /* CArray#==(other) — elementwise equality returning true / false.
462
+ * data_class, data_type, shape, and mask state must all match; NaN
463
+ * compares unequal per IEEE semantics. Non-CArray other returns
464
+ * false. */
467
465
  static VALUE
468
466
  rb_ca_equal (VALUE self, VALUE other)
469
467
  {
@@ -480,7 +478,7 @@ rb_ca_equal (VALUE self, VALUE other)
480
478
  else {
481
479
  VALUE dc1 = rb_ca_data_class(self);
482
480
  VALUE dc2 = rb_ca_data_class(other);
483
- if ( ! rb_funcall(dc1, rb_intern("=="), 1, dc2) ) {
481
+ if ( ! RTEST(rb_funcall(dc1, id_equal, 1, dc2)) ) {
484
482
  return Qfalse;
485
483
  }
486
484
  }
@@ -492,100 +490,115 @@ rb_ca_equal (VALUE self, VALUE other)
492
490
  return ( ca_equal(ca, cb) ) ? Qtrue : Qfalse;
493
491
  }
494
492
 
495
- /*
496
- ca_mem_hash()
493
+ /* CArray#eql?(other) — Hash-invariant equality (satisfies a.eql?(b) =>
494
+ * a.hash == b.hash). Same structural checks as ==, but element
495
+ * comparison is bitwise (numeric) or Object#eql? (CA_OBJECT), so
496
+ * NaN.eql?(NaN) holds. */
497
+ static VALUE
498
+ rb_ca_eql (VALUE self, VALUE other)
499
+ {
500
+ CArray *ca, *cb;
497
501
 
498
- This hash function is modified version of rb_str_hash()
499
- in string.c of Ruby 1.8.6 distribution.
500
- */
502
+ if ( ! rb_obj_is_carray(other) ) {
503
+ return Qfalse;
504
+ }
501
505
 
502
- int32_t
503
- ca_mem_hash (char *mp, ca_size_t mlen)
504
- {
505
- register ca_size_t len = mlen;
506
- register char *p = mp;
507
- register int32_t key = 0;
508
- while (len--) {
509
- key = key*65599 + *p;
510
- p++;
506
+ if ( rb_ca_has_data_class(self) || rb_ca_has_data_class(other) ) {
507
+ if ( rb_ca_has_data_class(self) ^ rb_ca_has_data_class(other) ) {
508
+ return Qfalse;
509
+ }
510
+ else {
511
+ VALUE dc1 = rb_ca_data_class(self);
512
+ VALUE dc2 = rb_ca_data_class(other);
513
+ if ( ! RTEST(rb_funcall(dc1, id_eql, 1, dc2)) ) {
514
+ return Qfalse;
515
+ }
516
+ }
511
517
  }
512
- return key;
518
+
519
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
520
+ TypedData_Get_Struct(other, CArray, &carray_data_type, cb);
521
+
522
+ return ( ca_eql_strict(ca, cb) ) ? Qtrue : Qfalse;
513
523
  }
514
524
 
515
- static int32_t
525
+ /* ca_hash(ca) — cheap Hash key for CArray. Mixes metadata (data_type,
526
+ * ndim, bytes, elements, scalar-ness, shape, mask presence) and, for
527
+ * unmasked arrays only, samples the leading CA_HASH_SAMPLE_BYTES of
528
+ * data. Masked arrays skip the data sample entirely — otherwise
529
+ * masked positions would have to be zeroed in the sample to preserve
530
+ * the eql?→hash invariant, and masked CArrays are rare as Hash keys.
531
+ * The collision rate cost buys no ca->mask->ptr access and no
532
+ * xmalloc/memcpy per call. */
533
+
534
+ #define CA_HASH_SAMPLE_BYTES 64
535
+
536
+ static st_index_t
516
537
  ca_hash (CArray *ca)
517
538
  {
518
- int32_t hash;
519
-
520
- if ( ca_is_any_masked(ca) ) {
521
- ca_size_t bytes = ca->bytes;
522
- boolean8_t *m = (boolean8_t*) ca->mask->ptr;
523
- /* char *tptr = ALLOC_N(char, ca_length(ca)); */
524
- char *tptr = malloc_with_check(ca_length(ca));
525
- char *p;
526
- int32_t i;
527
- ca_attach(ca);
528
- memcpy(tptr, ca->ptr, ca_length(ca));
529
- p = tptr;
530
- #ifdef _OPENMP
531
- #pragma omp parallel for
532
- #endif
533
- for (i=0; i<ca->elements; i++) {
534
- if ( *(m+i) ) {
535
- memset(p+i*bytes, 0, bytes);
536
- }
537
- }
538
- hash = ca_mem_hash(tptr, ca_length(ca));
539
- hash ^= ca_mem_hash(ca->mask->ptr, ca->elements);
540
- ca_detach(ca);
541
- free(tptr);
542
- }
543
- else {
539
+ st_index_t h;
540
+ int scalar_flag = ca_is_scalar(ca) ? 1 : 0;
541
+ int masked_flag;
542
+ ca_size_t data_len = ca_length(ca);
543
+ ca_size_t sample = (data_len < CA_HASH_SAMPLE_BYTES) ? data_len : CA_HASH_SAMPLE_BYTES;
544
+
545
+ h = rb_memhash(&ca->data_type, sizeof(ca->data_type));
546
+ h ^= rb_memhash(&ca->ndim, sizeof(ca->ndim));
547
+ h ^= rb_memhash(&ca->bytes, sizeof(ca->bytes));
548
+ h ^= rb_memhash(&ca->elements, sizeof(ca->elements));
549
+ h ^= rb_memhash(&scalar_flag, sizeof(scalar_flag));
550
+ if ( ca->ndim > 0 ) {
551
+ h ^= rb_memhash(ca->dim, sizeof(ca_size_t) * ca->ndim);
552
+ }
553
+
554
+ /* ca_is_any_masked() attaches/detaches the mask internally and returns
555
+ only a boolean, so we never touch ca->mask->ptr here. */
556
+ masked_flag = ca_is_any_masked(ca) ? 1 : 0;
557
+ h ^= rb_memhash(&masked_flag, sizeof(masked_flag));
558
+
559
+ if ( ! masked_flag && sample > 0 ) {
544
560
  ca_attach(ca);
545
- hash = ca_mem_hash(ca->ptr, ca_length(ca));
561
+ h ^= rb_memhash(ca->ptr, sample);
546
562
  ca_detach(ca);
547
563
  }
548
- return hash;
549
- }
550
-
551
- /* @overload hash
552
564
 
553
- (Inquiry)
554
- Returns the hash value of the object.
555
- */
565
+ return h;
566
+ }
556
567
 
568
+ /* CArray#hash — Hash key value; wraps ca_hash into a Ruby Integer. */
557
569
  VALUE
558
570
  rb_ca_hash (VALUE self)
559
571
  {
560
572
  CArray *ca;
561
- int32_t hash;
573
+ st_index_t hash;
562
574
 
563
575
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
564
576
  hash = ca_hash(ca);
565
- return ULONG2NUM(hash);
577
+ return ST2FIX(hash);
566
578
  }
567
579
 
568
580
  /* ----------------------------------------------------------------------- */
569
581
 
582
+ /* Mutation guard. Rejects both a Ruby-frozen object (FrozenError) and an
583
+ * array carrying CA_FLAG_READ_ONLY, whether set on self or inherited from a
584
+ * read-only parent (RuntimeError). Data-value stores are additionally gated
585
+ * in the xfer PUT path (ca_is_readonly), but mask writes, the `x[i] = UNDEF`
586
+ * masking path, and the direct-ptr entity hot paths in elem_store/incr/decr
587
+ * do not pass through xfer -- mutation entry points guard here so a
588
+ * read-only-flag-only (non-frozen) array is fully protected. */
570
589
  void
571
590
  rb_ca_modify (VALUE self)
572
591
  {
573
- if ( OBJ_FROZEN(self) ) {
574
- rb_error_frozen("CArray object");
575
- }
576
- /*
577
- if ( ( ! OBJ_TAINTED(self) ) && rb_safe_level() >= 4 ) {
578
- rb_raise(rb_eSecurityError, "Insecure: can't modify carray");
592
+ CArray *ca;
593
+ rb_check_frozen(self);
594
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
595
+ if ( ca_is_readonly(ca) ) {
596
+ rb_raise(rb_eRuntimeError, "can not modify read-only array");
579
597
  }
580
- */
581
598
  }
582
599
 
583
- /* @overload freeze
584
-
585
- Freeze the object.
586
-
587
- */
588
-
600
+ /* CArray#freeze freeze self and set CA_FLAG_READ_ONLY so subsequent
601
+ * mutations raise FrozenError. */
589
602
  VALUE
590
603
  rb_ca_freeze (VALUE self)
591
604
  {
@@ -595,20 +608,121 @@ rb_ca_freeze (VALUE self)
595
608
  return rb_obj_freeze(self);
596
609
  }
597
610
 
611
+ /* CArray#set_read_only_flag — set CA_FLAG_READ_ONLY so subsequent mutations
612
+ * raise (RuntimeError), WITHOUT rb_obj_freeze. One-way at the public surface:
613
+ * there is no public method to clear the flag, so the read-only guarantee
614
+ * cannot be weakened from user code. Unlike #freeze the Ruby object stays
615
+ * non-frozen, so views / Faces derived from it remain non-frozen and can
616
+ * memoise. A caller wanting a mutable array uses `.copy` (a copy does not
617
+ * inherit the flag). */
618
+ VALUE
619
+ rb_ca_set_read_only_flag (VALUE self)
620
+ {
621
+ CArray *ca;
622
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
623
+ ca_set_flag(ca, CA_FLAG_READ_ONLY);
624
+ return self;
625
+ }
626
+
627
+ /* Ensure-body arg for rb_ca_without_read_only_flag. */
628
+ struct without_read_only_state {
629
+ CArray *ca;
630
+ int was_readonly;
631
+ };
632
+
633
+ static VALUE
634
+ without_read_only_body (VALUE arg)
635
+ {
636
+ return rb_yield(Qnil);
637
+ }
638
+
639
+ static VALUE
640
+ without_read_only_ensure (VALUE arg)
641
+ {
642
+ struct without_read_only_state *s = (struct without_read_only_state *) arg;
643
+ if (s->was_readonly) {
644
+ ca_set_flag(s->ca, CA_FLAG_READ_ONLY);
645
+ }
646
+ return Qnil;
647
+ }
648
+
649
+ /* CArray#without_read_only_flag { ... } — PRIVATE, block-only escape reserved
650
+ * for library / class authors who understand the READONLY invariants of the
651
+ * subsystem they are working in. The public "One-way" guarantee stated on
652
+ * rb_ca_set_read_only_flag above is NOT weakened by this method: it is not
653
+ * accessible from user code by design (private + explicit `send` requirement
654
+ * is the signal, "I know internal invariants and I am not general user code").
655
+ *
656
+ * READONLY is set for many different reasons across the library, and the
657
+ * semantic behind the flag varies by class:
658
+ *
659
+ * (a) the entity backs external immutable memory (wrap_memory_view over
660
+ * an Arrow / Parquet / mmap MAP_PRIVATE buffer) — the values buffer
661
+ * is truly immutable, but CArray-side metadata (mask slot) is not;
662
+ * a bounded lift for mask attach is safe.
663
+ * (b) writes are permitted only through a formal, invariant-preserving
664
+ * API (a future variant, e.g. classes that keep derived caches in
665
+ * sync on write) — lifting READONLY would enable a low-level bypass
666
+ * of the invariant; DO NOT lift.
667
+ * (c) there is no writable target at all — CAObject today, the planned
668
+ * CASource similarly (elements are produced by a Ruby callback / an
669
+ * upstream source, not stored in a buffer) — lifting READONLY is
670
+ * meaningless because there is nothing to write to.
671
+ *
672
+ * A general lift primitive would be dangerous. The author must know which
673
+ * of (a) / (b) / (c) applies in their subsystem before reaching for this
674
+ * primitive: it exists so a library / class author who does know can perform
675
+ * a bounded escape without inventing a new flag.
676
+ *
677
+ * Canonical use: an interop bridge attaching a validity mask onto the
678
+ * read-only CAWrap returned by wrap_memory_view — the values buffer is
679
+ * immutable (producer's contract) but the mask slot is CArray-side metadata
680
+ * the bridge owns.
681
+ *
682
+ * Mechanics: clears CA_FLAG_READ_ONLY on self for the duration of the block;
683
+ * rb_ensure restores the flag on normal return AND on raise. Frozen objects
684
+ * still raise FrozenError. Self only: does not walk parent chains, does not
685
+ * touch ca->mask's flag.
686
+ *
687
+ * Caller discipline (not enforced by the primitive): confine usage to
688
+ * entities (CArray / CScalar / CAWrap). On a view of a read-only parent,
689
+ * the yielded block's mask= walks create_mask into the parent chain and
690
+ * mutates the parent's mask allocation as a side effect, breaking the
691
+ * view-inherits-readonly guarantee for sibling views of the same parent. */
692
+ VALUE
693
+ rb_ca_without_read_only_flag (VALUE self)
694
+ {
695
+ CArray *ca;
696
+ struct without_read_only_state s;
697
+ rb_check_frozen(self);
698
+ rb_need_block();
699
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
700
+ s.ca = ca;
701
+ s.was_readonly = ca_test_flag(ca, CA_FLAG_READ_ONLY);
702
+ if (s.was_readonly) {
703
+ ca_unset_flag(ca, CA_FLAG_READ_ONLY);
704
+ }
705
+ return rb_ensure(without_read_only_body, Qnil,
706
+ without_read_only_ensure, (VALUE) &s);
707
+ }
708
+
598
709
  void
599
- Init_carray_test ()
710
+ Init_carray_test (void)
600
711
  {
601
712
  id_equal = rb_intern("==");
602
-
713
+ id_eql = rb_intern("eql?");
714
+
603
715
  rb_define_method(rb_cCArray, "valid_index?", rb_ca_is_valid_index, -1);
604
716
  rb_define_method(rb_cCArray, "valid_addr?", rb_ca_is_valid_addr, 1);
605
717
  rb_define_method(rb_cCArray, "same_shape?", rb_ca_has_same_shape, 1);
606
718
  rb_define_method(rb_cCArray, "freeze", rb_ca_freeze, 0);
719
+ rb_define_method(rb_cCArray, "set_read_only_flag", rb_ca_set_read_only_flag, 0);
720
+ rb_define_private_method(rb_cCArray, "without_read_only_flag",
721
+ rb_ca_without_read_only_flag, 0);
607
722
 
608
- rb_define_method(rb_cCArray, "==", rb_ca_equal, 1);
609
- rb_define_alias(rb_cCArray, "eql?", "==");
610
- rb_define_method(rb_cCArray, "hash", rb_ca_hash, 0);
611
-
612
- rb_define_singleton_method(rb_cCArray, "data_class?", rb_ca_s_is_data_class, 1);
723
+ rb_define_method(rb_cCArray, "==", rb_ca_equal, 1);
724
+ rb_define_method(rb_cCArray, "eql?", rb_ca_eql, 1);
725
+ rb_define_method(rb_cCArray, "hash", rb_ca_hash, 0);
613
726
 
727
+ /* CArray.data_class?(klass) is bound in Init_ca_obj_record. */
614
728
  }