carray 3.0.2 → 3.0.4

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 (247) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +568 -0
  3. data/README.md +6 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +220 -0
  7. data/ext/ca_axis_group.c +201 -123
  8. data/ext/ca_binop_dispatch.c +46 -52
  9. data/ext/ca_binop_dispatch.h +1 -1
  10. data/ext/ca_categorical_iterator.c +249 -183
  11. data/ext/ca_compare.c +7 -12
  12. data/ext/ca_for_buffer.h +21 -68
  13. data/ext/ca_group_iter.c +38 -23
  14. data/ext/ca_kernel_iterator.c +1765 -2076
  15. data/ext/ca_kernel_iterator.h +584 -566
  16. data/ext/ca_monop_dispatch.c +86 -63
  17. data/ext/ca_monop_dispatch.h +5 -0
  18. data/ext/ca_obj_array.c +223 -56
  19. data/ext/ca_obj_bincmp.c +172 -138
  20. data/ext/ca_obj_binop.c +116 -230
  21. data/ext/ca_obj_bitarray.c +40 -26
  22. data/ext/ca_obj_bitfield.c +100 -68
  23. data/ext/ca_obj_block.c +19 -9
  24. data/ext/ca_obj_byte_swap.c +45 -46
  25. data/ext/ca_obj_const_string.c +269 -44
  26. data/ext/ca_obj_face.c +612 -89
  27. data/ext/ca_obj_face.h +111 -47
  28. data/ext/ca_obj_fake.c +36 -79
  29. data/ext/ca_obj_farray.c +7 -3
  30. data/ext/ca_obj_field.c +33 -8
  31. data/ext/ca_obj_fixlen_string.c +23 -24
  32. data/ext/ca_obj_grid.c +541 -210
  33. data/ext/ca_obj_meld.c +331 -111
  34. data/ext/ca_obj_moncmp.c +80 -65
  35. data/ext/ca_obj_monop.c +177 -149
  36. data/ext/ca_obj_object.c +664 -248
  37. data/ext/ca_obj_record.c +63 -23
  38. data/ext/ca_obj_reduce.c +18 -16
  39. data/ext/ca_obj_refer.c +140 -58
  40. data/ext/ca_obj_remap.c +85 -24
  41. data/ext/ca_obj_repeat.c +24 -9
  42. data/ext/ca_obj_roll.c +121 -16
  43. data/ext/ca_obj_select.c +61 -58
  44. data/ext/ca_obj_select_axis.c +73 -107
  45. data/ext/ca_obj_shift.c +32 -102
  46. data/ext/ca_obj_stack.c +372 -122
  47. data/ext/ca_obj_stride.c +348 -140
  48. data/ext/ca_obj_string.c +88 -22
  49. data/ext/ca_obj_tile.c +38 -21
  50. data/ext/ca_obj_time.c +45 -31
  51. data/ext/ca_obj_timedelta.c +43 -29
  52. data/ext/ca_obj_transpose.c +17 -6
  53. data/ext/ca_obj_triop.c +125 -141
  54. data/ext/ca_obj_window.c +330 -44
  55. data/ext/ca_op_ipower.c +24 -11
  56. data/ext/ca_op_powi.h +57 -30
  57. data/ext/ca_pow5_table.h +656 -0
  58. data/ext/ca_sort_kernels.h +70 -0
  59. data/ext/ca_sweep_engine.c +104 -81
  60. data/ext/ca_sweep_engine.h +7 -20
  61. data/ext/ca_triop_dispatch.c +4 -1
  62. data/ext/ca_triop_dispatch.h +5 -2
  63. data/ext/caframe_csv_reader.c +542 -0
  64. data/ext/carray.h +241 -92
  65. data/ext/carray_access.c +608 -276
  66. data/ext/carray_address_basis.c +494 -97
  67. data/ext/carray_attribute.c +129 -4
  68. data/ext/carray_bincount.c +39 -7
  69. data/ext/carray_broadcast.c +56 -19
  70. data/ext/carray_build_flags.h +1 -1
  71. data/ext/carray_call_cfunc.c +108 -28
  72. data/ext/carray_cast.c +1576 -223
  73. data/ext/carray_cast_func.rb +77 -24
  74. data/ext/carray_class.c +4 -3
  75. data/ext/carray_conversion.c +208 -75
  76. data/ext/carray_copy.c +82 -24
  77. data/ext/carray_core.c +646 -162
  78. data/ext/carray_count.c +171 -88
  79. data/ext/carray_element.c +94 -60
  80. data/ext/carray_factorize.c +670 -324
  81. data/ext/carray_generate.c +260 -83
  82. data/ext/carray_histogram.c +209 -106
  83. data/ext/carray_hold.c +14 -16
  84. data/ext/carray_index_classifier.c +422 -243
  85. data/ext/carray_internal.h +692 -3
  86. data/ext/carray_kernels_bincmp.c +7 -0
  87. data/ext/carray_kernels_binop.c +487 -120
  88. data/ext/carray_kernels_init.c +653 -50
  89. data/ext/carray_kernels_moncmp.c +15 -8
  90. data/ext/carray_kernels_monop.c +99 -43
  91. data/ext/carray_kernels_reduce_aggregate.c +3925 -6112
  92. data/ext/carray_kernels_reduce_boolean.c +112 -36
  93. data/ext/carray_kernels_reduce_cumulative.c +2127 -6903
  94. data/ext/carray_kernels_reduce_extreme.c +3032 -2892
  95. data/ext/carray_kernels_reduce_variance.c +439 -500
  96. data/ext/carray_kernels_scan.c +1097 -488
  97. data/ext/carray_kernels_search.c +3787 -2176
  98. data/ext/carray_kernels_sort.c +1200 -920
  99. data/ext/carray_kernels_triop.c +479 -7
  100. data/ext/carray_key_windows.c +216 -0
  101. data/ext/carray_lazy.c +967 -153
  102. data/ext/carray_loop.c +81 -65
  103. data/ext/carray_mask.c +910 -324
  104. data/ext/carray_math_kernel.h +1 -0
  105. data/ext/carray_median_percentile.c +299 -100
  106. data/ext/carray_memory_view.c +872 -758
  107. data/ext/carray_operator.c +1171 -364
  108. data/ext/carray_order.c +505 -133
  109. data/ext/carray_partition.c +112 -65
  110. data/ext/carray_random.c +327 -239
  111. data/ext/carray_round.c +273 -0
  112. data/ext/carray_scatter.c +346 -78
  113. data/ext/carray_segment.c +257 -0
  114. data/ext/carray_slab.c +249 -86
  115. data/ext/carray_sort.c +224 -227
  116. data/ext/carray_sort_kernel.c +8 -1
  117. data/ext/carray_struct.c +118 -39
  118. data/ext/carray_test.c +193 -69
  119. data/ext/carray_time_text.c +1182 -0
  120. data/ext/carray_undef.c +4 -0
  121. data/ext/carray_utils.c +579 -113
  122. data/ext/extconf.rb +27 -2
  123. data/ext/mk_call_cfunc.rb +19 -4
  124. data/ext/mk_pow5_table.rb +35 -0
  125. data/ext/mkkernel.rb +1202 -1014
  126. data/ext/ruby_carray.c +17 -5
  127. data/ext/version.h +4 -4
  128. data/ext/version.rb +4 -2
  129. data/lib/carray/attribute.rb +160 -17
  130. data/lib/carray/autoload_carray.rb +23 -27
  131. data/lib/carray/autoload_method_extension.rb +20 -5
  132. data/lib/carray/axis_group.rb +360 -63
  133. data/lib/carray/basics.rb +162 -28
  134. data/lib/carray/bincount_nd.rb +18 -2
  135. data/lib/carray/block_iterator.rb +231 -58
  136. data/lib/carray/boolean_reduce.rb +34 -42
  137. data/lib/carray/categorical.rb +242 -52
  138. data/lib/carray/categorical_iterator.rb +437 -827
  139. data/lib/carray/complex.rb +41 -34
  140. data/lib/carray/conditional.rb +150 -14
  141. data/lib/carray/const_string.rb +59 -4
  142. data/lib/carray/construct.rb +15 -11
  143. data/lib/carray/data_type_extension.rb +1 -0
  144. data/lib/carray/distance_limit.rb +77 -0
  145. data/lib/carray/fixlen_string.rb +7 -1
  146. data/lib/carray/frame/column_verbs.rb +70 -0
  147. data/lib/carray/frame/concat.rb +157 -6
  148. data/lib/carray/frame/convert.rb +82 -7
  149. data/lib/carray/frame/csv_parser.rb +688 -87
  150. data/lib/carray/frame/describe.rb +80 -0
  151. data/lib/carray/frame/frame.rb +369 -45
  152. data/lib/carray/frame/group.rb +631 -90
  153. data/lib/carray/frame/io.rb +506 -79
  154. data/lib/carray/frame/join.rb +411 -62
  155. data/lib/carray/frame/records.rb +116 -19
  156. data/lib/carray/frame/reshape.rb +455 -0
  157. data/lib/carray/frame/sort.rb +30 -18
  158. data/lib/carray/frame/verbs.rb +868 -61
  159. data/lib/carray/frame/windows.rb +117 -0
  160. data/lib/carray/frame.rb +4 -0
  161. data/lib/carray/fuse_source.rb +118 -2
  162. data/lib/carray/fusion.rb +257 -42
  163. data/lib/carray/histogram.rb +6 -1
  164. data/lib/carray/inspect.rb +54 -13
  165. data/lib/carray/iterator.rb +99 -9
  166. data/lib/carray/key_windows.rb +620 -0
  167. data/lib/carray/lazy.rb +216 -193
  168. data/lib/carray/mask_gap_fill.rb +112 -92
  169. data/lib/carray/math.rb +32 -19
  170. data/lib/carray/meld_reduce.rb +52 -21
  171. data/lib/carray/methods/align_addr.rb +8 -4
  172. data/lib/carray/methods/bin.rb +26 -18
  173. data/lib/carray/methods/bincount.rb +18 -2
  174. data/lib/carray/methods/bit_string.rb +15 -4
  175. data/lib/carray/methods/choose.rb +16 -3
  176. data/lib/carray/methods/composition.rb +18 -10
  177. data/lib/carray/methods/factorize.rb +2 -2
  178. data/lib/carray/methods/gather_nd.rb +25 -5
  179. data/lib/carray/methods/insert_block.rb +9 -14
  180. data/lib/carray/methods/is_between.rb +59 -0
  181. data/lib/carray/methods/is_in.rb +140 -10
  182. data/lib/carray/methods/join.rb +2 -7
  183. data/lib/carray/methods/lag.rb +49 -0
  184. data/lib/carray/methods/locate_addr.rb +182 -45
  185. data/lib/carray/methods/mode.rb +18 -1
  186. data/lib/carray/methods/nunique.rb +11 -6
  187. data/lib/carray/methods/repeat.rb +1 -20
  188. data/lib/carray/methods/resize.rb +21 -23
  189. data/lib/carray/methods/snap.rb +34 -12
  190. data/lib/carray/methods/unique.rb +11 -2
  191. data/lib/carray/methods/value_counts.rb +4 -11
  192. data/lib/carray/record_order.rb +208 -0
  193. data/lib/carray/rng.rb +3 -0
  194. data/lib/carray/runtime.rb +65 -17
  195. data/lib/carray/segment_iterator.rb +1221 -0
  196. data/lib/carray/serialize.rb +214 -30
  197. data/lib/carray/slab_iterator.rb +101 -27
  198. data/lib/carray/stack.rb +10 -4
  199. data/lib/carray/string.rb +105 -5
  200. data/lib/carray/string_operation_extension.rb +12 -4
  201. data/lib/carray/struct.rb +137 -58
  202. data/lib/carray/struct_builder.rb +124 -17
  203. data/lib/carray/time.rb +1222 -186
  204. data/lib/carray/window_iterator.rb +242 -81
  205. data/lib/carray.rb +9 -2
  206. data/yard-stubs/ca_obj_array.rb +4 -4
  207. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  208. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  209. data/yard-stubs/ca_obj_fake.rb +1 -1
  210. data/yard-stubs/ca_obj_field.rb +5 -1
  211. data/yard-stubs/ca_obj_grid.rb +21 -10
  212. data/yard-stubs/ca_obj_refer.rb +5 -4
  213. data/yard-stubs/ca_obj_roll.rb +2 -3
  214. data/yard-stubs/ca_obj_shift.rb +5 -1
  215. data/yard-stubs/ca_obj_stride.rb +10 -7
  216. data/yard-stubs/ca_obj_tile.rb +1 -1
  217. data/yard-stubs/ca_obj_transpose.rb +1 -1
  218. data/yard-stubs/ca_obj_window.rb +9 -5
  219. data/yard-stubs/carray_access.rb +11 -5
  220. data/yard-stubs/carray_attribute.rb +0 -5
  221. data/yard-stubs/carray_broadcast.rb +1 -1
  222. data/yard-stubs/carray_cast.rb +88 -43
  223. data/yard-stubs/carray_conversion.rb +15 -2
  224. data/yard-stubs/carray_copy.rb +12 -5
  225. data/yard-stubs/carray_core.rb +3 -3
  226. data/yard-stubs/carray_count.rb +9 -0
  227. data/yard-stubs/carray_element.rb +10 -4
  228. data/yard-stubs/carray_generate.rb +21 -1
  229. data/yard-stubs/carray_lazy.rb +5 -1
  230. data/yard-stubs/carray_loop.rb +4 -1
  231. data/yard-stubs/carray_mask.rb +60 -12
  232. data/yard-stubs/carray_math.rb +52 -14
  233. data/yard-stubs/carray_median_percentile.rb +15 -8
  234. data/yard-stubs/carray_memory_view.rb +40 -23
  235. data/yard-stubs/carray_order.rb +61 -29
  236. data/yard-stubs/carray_random.rb +13 -4
  237. data/yard-stubs/carray_scatter.rb +21 -9
  238. data/yard-stubs/carray_segment.rb +72 -0
  239. data/yard-stubs/carray_slab.rb +10 -2
  240. data/yard-stubs/carray_sort.rb +49 -8
  241. data/yard-stubs/carray_test.rb +15 -3
  242. data/yard-stubs/carray_utils.rb +0 -26
  243. metadata +19 -5
  244. data/ext/ca_for_each_element.h +0 -239
  245. data/ext/carray_kernels_map.c +0 -3467
  246. data/lib/carray/arrow_tensor.rb +0 -401
  247. data/lib/carray/table.rb +0 -108
data/ext/carray_test.c CHANGED
@@ -10,6 +10,8 @@
10
10
  ---------------------------------------------------------------------------- */
11
11
 
12
12
  #include "carray.h"
13
+ #include "carray_internal.h" /* ca_attach_window */
14
+ #include "ca_obj_face.h" /* ca_face_equal */
13
15
 
14
16
  static ID id_equal;
15
17
  static ID id_eql;
@@ -26,13 +28,34 @@ ca_check_type (void *ap, int8_t data_type)
26
28
  }
27
29
  }
28
30
 
31
+ /* For `initialize` and `initialize_copy`: raises unless the struct is as
32
+ the allocator left it. Setting up a struct that is already set up
33
+ would overwrite what it owns (its buffer, its shape arrays, its parent
34
+ list) without freeing it.
35
+
36
+ What is tested is dim: NULL means the struct owns nothing yet. That
37
+ holds on three conditions, which a new array class has to keep:
38
+ - its allocator zero-fills the struct (TypedData_Make_Struct does);
39
+ - its setup gives the struct a dim (there is no array without one);
40
+ - its `initialize` runs the argument checks before it takes the pool
41
+ or anything else (ca_array_pool_alloc sets dim), so that a rejected
42
+ argument leaves dim NULL and the object can be initialized again. */
43
+ void
44
+ ca_check_uninitialized (void *ap)
45
+ {
46
+ CArray *ca = (CArray *) ap;
47
+ if ( ca->dim != NULL ) {
48
+ rb_raise(rb_eTypeError, "already initialized array");
49
+ }
50
+ }
51
+
29
52
  void
30
53
  ca_check_ndim (void *ap, int ndim)
31
54
  {
32
55
  CArray *ca = (CArray *) ap;
33
56
  if ( ! ca_is_scalar(ca) ) {
34
57
  if ( ca->ndim != ndim ) {
35
- rb_raise(rb_eRuntimeError, "ndim mismatch");
58
+ rb_raise(rb_eArgError, "ndim mismatch");
36
59
  }
37
60
  }
38
61
  }
@@ -44,11 +67,11 @@ ca_check_shape (void *ap, int ndim, ca_size_t *dim)
44
67
  int i;
45
68
  if ( ! ca_is_scalar(ca) ) {
46
69
  if ( ca->ndim != ndim ) {
47
- rb_raise(rb_eRuntimeError, "shape mismatch");
70
+ rb_raise(rb_eArgError, "shape mismatch");
48
71
  }
49
72
  for (i=0; i<ndim; i++) {
50
73
  if ( ca->dim[i] != dim[i] ) {
51
- rb_raise(rb_eRuntimeError, "shape mismatch");
74
+ rb_raise(rb_eArgError, "shape mismatch");
52
75
  }
53
76
  }
54
77
  }
@@ -59,7 +82,8 @@ ca_check_same_data_type (void *ap1, void *ap2)
59
82
  {
60
83
  CArray *ca1 = (CArray *) ap1;
61
84
  CArray *ca2 = (CArray *) ap2;
62
- if ( ca1->data_type != ca2->data_type ) {
85
+ if ( ca1->data_type != ca2->data_type ||
86
+ ( ca1->data_type == CA_FIXLEN && ca1->bytes != ca2->bytes ) ) {
63
87
  rb_raise(rb_eCADataTypeError, "data_type mismatch");
64
88
  }
65
89
  }
@@ -70,7 +94,7 @@ ca_check_same_ndim (void *ap1, void *ap2)
70
94
  CArray *ca1 = (CArray *) ap1;
71
95
  CArray *ca2 = (CArray *) ap2;
72
96
  if ( ca1->ndim != ca2->ndim ) {
73
- rb_raise(rb_eRuntimeError, "ndim mismatch");
97
+ rb_raise(rb_eArgError, "ndim mismatch");
74
98
  }
75
99
  }
76
100
 
@@ -80,7 +104,7 @@ ca_check_same_elements (void *ap1, void *ap2)
80
104
  CArray *ca1 = (CArray *) ap1;
81
105
  CArray *ca2 = (CArray *) ap2;
82
106
  if ( ca1->elements != ca2->elements ) {
83
- rb_raise(rb_eRuntimeError, "elements mismatch");
107
+ rb_raise(rb_eArgError, "elements mismatch");
84
108
  }
85
109
  }
86
110
 
@@ -92,11 +116,11 @@ ca_check_same_shape (void *ap1, void *ap2)
92
116
  int i;
93
117
  if ( ( ! ca_is_scalar(ca1) ) && ( ! ca_is_scalar(ca2) ) ) {
94
118
  if ( ca1->ndim != ca2->ndim ) {
95
- rb_raise(rb_eRuntimeError, "shape mismatch");
119
+ rb_raise(rb_eArgError, "shape mismatch");
96
120
  }
97
121
  for (i=0; i<ca1->ndim; i++) {
98
122
  if ( ca1->dim[i] != ca2->dim[i] ) {
99
- rb_raise(rb_eRuntimeError, "shape mismatch");
123
+ rb_raise(rb_eArgError, "shape mismatch");
100
124
  }
101
125
  }
102
126
  }
@@ -109,7 +133,7 @@ ca_check_index (void *ap, ca_size_t *idx)
109
133
  int i;
110
134
  for (i=0; i<ca->ndim; i++) {
111
135
  if ( idx[i] < 0 || idx[i] >= ca->dim[i] ) {
112
- rb_raise(rb_eRuntimeError, "invalid index");
136
+ rb_raise(rb_eIndexError, "invalid index");
113
137
  }
114
138
  }
115
139
  }
@@ -118,7 +142,7 @@ void
118
142
  rb_check_carray_object (VALUE arg)
119
143
  {
120
144
  if ( ! rb_obj_is_carray(arg) ) {
121
- rb_raise(rb_eRuntimeError, "CArray required");
145
+ rb_raise(rb_eTypeError, "CArray required");
122
146
  }
123
147
  }
124
148
 
@@ -166,7 +190,7 @@ rb_ca_is_type (VALUE arg, int type)
166
190
  {
167
191
  CArray *ca;
168
192
  if ( ! rb_obj_is_carray(arg) ) {
169
- rb_raise(rb_eRuntimeError, "CArray required");
193
+ rb_raise(rb_eTypeError, "CArray required");
170
194
  }
171
195
  TypedData_Get_Struct(arg, CArray, &carray_data_type, ca);
172
196
  return ca->data_type == type;
@@ -177,8 +201,9 @@ rb_ca_is_type (VALUE arg, int type)
177
201
  live in ca_obj_record.c; signatures stay in carray.h for cross-file
178
202
  extern use. */
179
203
 
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). */
204
+ /* CArray#valid_index?(*idx) -- whether self[*idx] names a cell: a negative
205
+ * index counts from the end of its axis, as it does at every door. The
206
+ * number of indices must equal ndim (raises otherwise). */
182
207
  static VALUE
183
208
  rb_ca_is_valid_index (int argc, VALUE *argv, VALUE self)
184
209
  {
@@ -194,6 +219,9 @@ rb_ca_is_valid_index (int argc, VALUE *argv, VALUE self)
194
219
  }
195
220
  for (i=0; i<ca->ndim; i++) {
196
221
  idx = NUM2SIZE(argv[i]);
222
+ if ( idx < 0 ) {
223
+ idx += ca->dim[i];
224
+ }
197
225
  if ( idx < 0 || idx >= ca->dim[i] ) {
198
226
  return Qfalse;
199
227
  }
@@ -202,7 +230,8 @@ rb_ca_is_valid_index (int argc, VALUE *argv, VALUE self)
202
230
  return Qtrue;
203
231
  }
204
232
 
205
- /* CArray#valid_addr?(addr) — 1 if addr is in range 0...elements, else 0. */
233
+ /* CArray#valid_addr?(addr) -- whether addr names a cell: a negative address
234
+ * counts from the end. */
206
235
  static VALUE
207
236
  rb_ca_is_valid_addr (VALUE self, VALUE raddr)
208
237
  {
@@ -211,6 +240,9 @@ rb_ca_is_valid_addr (VALUE self, VALUE raddr)
211
240
 
212
241
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
213
242
  addr = NUM2SIZE(raddr);
243
+ if ( addr < 0 ) {
244
+ addr += ca->elements;
245
+ }
214
246
  if ( addr < 0 || addr >= ca->elements ) {
215
247
  return Qfalse;
216
248
  }
@@ -226,6 +258,7 @@ rb_ca_has_same_shape (VALUE self, VALUE other)
226
258
  {
227
259
  CArray *ca, *cb;
228
260
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
261
+ /* coerce: only the shape is compared */
229
262
  cb = ca_wrap_readonly(other, ca->data_type);
230
263
  return ca_has_same_shape(ca, cb) ? Qtrue : Qfalse;
231
264
  }
@@ -296,25 +329,20 @@ ca_eql[CA_NTYPE] = {
296
329
  eql_VALUE,
297
330
  };
298
331
 
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)
332
+ typedef struct {
333
+ CArray *ca;
334
+ CArray *cb;
335
+ int strict;
336
+ int flag;
337
+ } ca_compare_ctx_t;
338
+
339
+ static VALUE
340
+ ca_compare_body (VALUE arg)
315
341
  {
316
- CArray *ca = (CArray *) ap;
317
- CArray *cb = (CArray *) bp;
342
+ ca_compare_ctx_t *c = (ca_compare_ctx_t *) arg;
343
+ CArray *ca = c->ca;
344
+ CArray *cb = c->cb;
345
+ int strict = c->strict;
318
346
  int flag = 1;
319
347
  int masked_a, masked_b;
320
348
  boolean8_t *ma, *mb;
@@ -325,28 +353,6 @@ ca_compare_common (void *ap, void *bp, int strict)
325
353
  ca_eql_func eql = NULL; /* used only when !strict */
326
354
  int is_object;
327
355
 
328
- if ( ca_is_scalar(ca) ^ ca_is_scalar(cb) ) {
329
- return 0;
330
- }
331
- if ( ca->data_type != cb->data_type ) {
332
- return 0;
333
- }
334
- if ( ca->bytes != cb->bytes ) {
335
- return 0;
336
- }
337
- if ( ca->ndim != cb->ndim ) {
338
- return 0;
339
- }
340
- if ( ca->elements != cb->elements ) {
341
- return 0;
342
- }
343
- for (i=0; i<ca->ndim; i++) {
344
- if ( ca->dim[i] != cb->dim[i] ) {
345
- return 0;
346
- }
347
- }
348
-
349
- ca_attach_n(2, ca, cb); /* recurses into ca->mask / cb->mask */
350
356
 
351
357
  masked_a = ca_is_any_masked(ca);
352
358
  masked_b = ca_is_any_masked(cb);
@@ -441,9 +447,66 @@ ca_compare_common (void *ap, void *bp, int strict)
441
447
  }
442
448
  }
443
449
 
444
- ca_detach_n(2, ca, cb);
450
+ c->flag = flag;
451
+ return Qnil;
452
+ }
453
+
454
+ /* ca_compare_common(a, b, strict) — shared body of ca_equal (==) and
455
+ * ca_eql_strict (eql?). Returns 1 if arrays match under the selected
456
+ * semantics, else 0.
457
+ *
458
+ * Common structure: metadata pre-check (scalar / data_type / bytes /
459
+ * ndim / elements / dim), then ca_compare_body in a window over both
460
+ * arrays (attaching an array attaches its mask), a unified loop over the
461
+ * four masked/unmasked combinations.
462
+ *
463
+ * The only difference is the per-element comparator:
464
+ * == : ca_eql[data_type] (type-specific, so float NaN != NaN)
465
+ * eql?: memcmp for numeric, rb_funcall(:eql?) for CA_OBJECT
466
+ *
467
+ * The unmasked-unmasked branch takes a bulk memcmp or per-cell
468
+ * shortcut to amortise per-element function pointer dispatch. */
469
+ static int
470
+ ca_compare_common (void *ap, void *bp, int strict)
471
+ {
472
+ CArray *ca = (CArray *) ap;
473
+ CArray *cb = (CArray *) bp;
474
+ int8_t i;
475
+
476
+ if ( ca_is_scalar(ca) ^ ca_is_scalar(cb) ) {
477
+ return 0;
478
+ }
479
+ if ( ca->data_type != cb->data_type ) {
480
+ return 0;
481
+ }
482
+ if ( ca->bytes != cb->bytes ) {
483
+ return 0;
484
+ }
485
+ if ( ca->ndim != cb->ndim ) {
486
+ return 0;
487
+ }
488
+ if ( ca->elements != cb->elements ) {
489
+ return 0;
490
+ }
491
+ for (i=0; i<ca->ndim; i++) {
492
+ if ( ca->dim[i] != cb->dim[i] ) {
493
+ return 0;
494
+ }
495
+ }
445
496
 
446
- return flag;
497
+ {
498
+ ca_compare_ctx_t c;
499
+ CArray *list[2];
500
+ c.ca = ca;
501
+ c.cb = cb;
502
+ c.strict = strict;
503
+ c.flag = 1;
504
+ list[0] = ca;
505
+ list[1] = cb;
506
+ /* The object lane calls #== / #eql? with both arrays attached. */
507
+ ca_attach_window(2, list, "rr", ca_compare_body, (VALUE) &c);
508
+ return c.flag;
509
+ }
447
510
  }
448
511
 
449
512
  int
@@ -452,8 +515,8 @@ ca_equal (void *ap, void *bp)
452
515
  return ca_compare_common(ap, bp, 0);
453
516
  }
454
517
 
455
- static int
456
- ca_eql_strict (void *ap, void *bp)
518
+ int
519
+ ca_eql_storage (void *ap, void *bp)
457
520
  {
458
521
  return ca_compare_common(ap, bp, 1);
459
522
  }
@@ -487,6 +550,10 @@ rb_ca_equal (VALUE self, VALUE other)
487
550
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
488
551
  TypedData_Get_Struct(other, CArray, &carray_data_type, cb);
489
552
 
553
+ if ( ! rb_ca_has_data_class(self) && ( ca_is_face(ca) || ca_is_face(cb) ) ) {
554
+ return ca_face_equal(self, other, 0) ? Qtrue : Qfalse;
555
+ }
556
+
490
557
  return ( ca_equal(ca, cb) ) ? Qtrue : Qfalse;
491
558
  }
492
559
 
@@ -519,13 +586,20 @@ rb_ca_eql (VALUE self, VALUE other)
519
586
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
520
587
  TypedData_Get_Struct(other, CArray, &carray_data_type, cb);
521
588
 
522
- return ( ca_eql_strict(ca, cb) ) ? Qtrue : Qfalse;
589
+ if ( ! rb_ca_has_data_class(self) && ( ca_is_face(ca) || ca_is_face(cb) ) ) {
590
+ return ca_face_equal(self, other, 1) ? Qtrue : Qfalse;
591
+ }
592
+
593
+ return ( ca_eql_storage(ca, cb) ) ? Qtrue : Qfalse;
523
594
  }
524
595
 
525
596
  /* ca_hash(ca) — cheap Hash key for CArray. Mixes metadata (data_type,
526
597
  * 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
598
+ * unmasked arrays only, samples the leading cells (CA_HASH_SAMPLE_BYTES
599
+ * of data, at least one cell), read by address so a view is not
600
+ * materialised for them. An object array samples the #hash of those
601
+ * cells, matching eql?, which compares them with #eql?. Masked arrays
602
+ * skip the data sample entirely — otherwise
529
603
  * masked positions would have to be zeroed in the sample to preserve
530
604
  * the eql?→hash invariant, and masked CArrays are rare as Hash keys.
531
605
  * The collision rate cost buys no ca->mask->ptr access and no
@@ -557,9 +631,36 @@ ca_hash (CArray *ca)
557
631
  h ^= rb_memhash(&masked_flag, sizeof(masked_flag));
558
632
 
559
633
  if ( ! masked_flag && sample > 0 ) {
560
- ca_attach(ca);
561
- h ^= rb_memhash(ca->ptr, sample);
562
- ca_detach(ca);
634
+ ca_size_t addrs[CA_HASH_SAMPLE_BYTES];
635
+ ca_size_t ncell = sample / ca->bytes;
636
+ ca_size_t i;
637
+ if ( ncell < 1 ) {
638
+ ncell = 1;
639
+ }
640
+ if ( ncell > ca->elements ) {
641
+ ncell = ca->elements;
642
+ }
643
+ for (i = 0; i < ncell; i++) {
644
+ addrs[i] = i;
645
+ }
646
+ if ( ca_is_object_type(ca) ) {
647
+ /* eql? compares object cells with #eql?, so the sample is their
648
+ #hash, not the VALUE bits. The cells sit on the stack, where the
649
+ GC sees them while #hash runs. */
650
+ VALUE cells[CA_HASH_SAMPLE_BYTES / sizeof(VALUE)];
651
+ ca_xfer_addrs(ca, ncell, addrs, cells, CA_XFER_GET);
652
+ for (i = 0; i < ncell; i++) {
653
+ h = rb_hash_uint(h, (st_index_t) NUM2LONG(rb_hash(cells[i])));
654
+ }
655
+ RB_GC_GUARD(cells[0]);
656
+ }
657
+ else {
658
+ volatile VALUE holder;
659
+ char *buf = ALLOCV_N(char, holder, ncell * ca->bytes);
660
+ ca_xfer_addrs(ca, ncell, addrs, buf, CA_XFER_GET);
661
+ h ^= rb_memhash(buf, ncell * ca->bytes);
662
+ ALLOCV_END(holder);
663
+ }
563
664
  }
564
665
 
565
666
  return h;
@@ -593,10 +694,21 @@ rb_ca_modify (VALUE self)
593
694
  rb_check_frozen(self);
594
695
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
595
696
  if ( ca_is_readonly(ca) ) {
596
- rb_raise(rb_eRuntimeError, "can not modify read-only array");
697
+ ca_refuse_read_only();
597
698
  }
598
699
  }
599
700
 
701
+ /* The refusal of a write into a read-only array that is not frozen (a
702
+ frozen one is Ruby's FrozenError, from rb_check_frozen): every guard
703
+ raises it, the entry points through rb_ca_modify and the transfers and
704
+ fills below them, which are the last line when an entry is missed. */
705
+ void
706
+ ca_refuse_read_only (void)
707
+ {
708
+ rb_raise(rb_eRuntimeError, "%s: can not modify read-only array",
709
+ ca_calling_method_name());
710
+ }
711
+
600
712
  /* CArray#freeze — freeze self and set CA_FLAG_READ_ONLY so subsequent
601
713
  * mutations raise FrozenError. */
602
714
  VALUE
@@ -642,6 +754,9 @@ without_read_only_ensure (VALUE arg)
642
754
  struct without_read_only_state *s = (struct without_read_only_state *) arg;
643
755
  if (s->was_readonly) {
644
756
  ca_set_flag(s->ca, CA_FLAG_READ_ONLY);
757
+ if (s->ca->mask) { /* the block may have replaced it */
758
+ ca_set_flag(s->ca->mask, CA_FLAG_READ_ONLY);
759
+ }
645
760
  }
646
761
  return Qnil;
647
762
  }
@@ -679,10 +794,16 @@ without_read_only_ensure (VALUE arg)
679
794
  * immutable (producer's contract) but the mask slot is CArray-side metadata
680
795
  * the bridge owns.
681
796
  *
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.
797
+ * Mechanics: clears CA_FLAG_READ_ONLY on self and on its mask for the
798
+ * duration of the block; rb_ensure restores both on normal return AND on
799
+ * raise, setting the flag on whatever mask self has by then. (A read-only
800
+ * array's mask carries the flag once CArray#mask has handed it out.) Frozen
801
+ * objects still raise FrozenError. Does not walk parent chains.
802
+ *
803
+ * The lift covers every write, not only the mask: a value write inside the
804
+ * block reaches the producer's buffer, which case (a) declares immutable (a
805
+ * frozen CArray wrapped through MemoryView changes). Keeping the block to
806
+ * the mask is the caller's part, like the choice among (a) / (b) / (c).
686
807
  *
687
808
  * Caller discipline (not enforced by the primitive): confine usage to
688
809
  * entities (CArray / CScalar / CAWrap). On a view of a read-only parent,
@@ -701,6 +822,9 @@ rb_ca_without_read_only_flag (VALUE self)
701
822
  s.was_readonly = ca_test_flag(ca, CA_FLAG_READ_ONLY);
702
823
  if (s.was_readonly) {
703
824
  ca_unset_flag(ca, CA_FLAG_READ_ONLY);
825
+ if (ca->mask) {
826
+ ca_unset_flag(ca->mask, CA_FLAG_READ_ONLY);
827
+ }
704
828
  }
705
829
  return rb_ensure(without_read_only_body, Qnil,
706
830
  without_read_only_ensure, (VALUE) &s);