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
@@ -26,11 +26,13 @@
26
26
  bytes once in logical order, without offset-sharing dedup.
27
27
 
28
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).
29
+ ca_obj_time.c (int64 Face + storage_to_element fast path).
30
30
 
31
31
  --------------------------------------------------------------------------- */
32
32
 
33
33
  #include "carray.h"
34
+ #include "ruby/util.h"
35
+ #include "carray_internal.h"
34
36
  #include "ca_obj_face.h"
35
37
  #include <ruby/encoding.h>
36
38
 
@@ -121,6 +123,12 @@ ca_const_string_setup (CAConstString *ca, CArray *parent, VALUE buffer, int enco
121
123
  "(parent.data_type must be CA_FIXLEN with bytes == 16)");
122
124
  }
123
125
 
126
+ /* ca == NULL asks for the checks above only, so that a constructor can
127
+ run them before it allocates. */
128
+ if ( ca == NULL ) {
129
+ return 0;
130
+ }
131
+
124
132
  ca->obj_type = CA_OBJ_CONST_STRING;
125
133
  /* FIXLEN surface auto-gates mkkernel numeric dispatch. */
126
134
  ca->data_type = CA_FIXLEN;
@@ -142,9 +150,6 @@ ca_const_string_setup (CAConstString *ca, CArray *parent, VALUE buffer, int enco
142
150
  ca->buffer = buffer;
143
151
  ca->encoding_id = encoding_id;
144
152
 
145
- if ( ca_has_mask(parent) ) {
146
- ca_create_mask((CArray *) ca);
147
- }
148
153
 
149
154
  if ( ca_is_scalar(parent) ) {
150
155
  ca_set_flag(ca, CA_FLAG_SCALAR);
@@ -159,8 +164,11 @@ ca_const_string_setup (CAConstString *ca, CArray *parent, VALUE buffer, int enco
159
164
  CAConstString *
160
165
  ca_const_string_new (CArray *parent, VALUE buffer, int encoding_id)
161
166
  {
162
- CAConstString *ca = (CAConstString *) ca_array_alloc(CA_OBJ_CONST_STRING, parent->ndim);
167
+ CAConstString *ca;
168
+ ca_const_string_setup(NULL, parent, buffer, encoding_id);
169
+ ca = (CAConstString *) ca_array_alloc(CA_OBJ_CONST_STRING, parent->ndim);
163
170
  ca_const_string_setup(ca, parent, buffer, encoding_id);
171
+ ca_fill_or_free((CArray *) ca, ca_view_inherit_mask, (VALUE) ca);
164
172
  return ca;
165
173
  }
166
174
 
@@ -200,18 +208,6 @@ ca_const_string_func_allocate (void *ap)
200
208
  ca->ptr = ca->parent->ptr;
201
209
  }
202
210
 
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
211
  ca_operation_function_t ca_const_string_func = {
216
212
  -1, /* CA_OBJ_CONST_STRING — filled by ca_install_obj_type */
217
213
  CA_VIEW_ARRAY,
@@ -222,7 +218,7 @@ ca_operation_function_t ca_const_string_func = {
222
218
  ca_face_sync,
223
219
  ca_face_detach,
224
220
  ca_face_fill_data,
225
- ca_const_string_func_create_mask,
221
+ ca_view_func_create_mask,
226
222
  ca_face_xfer_index,
227
223
  ca_face_xfer_addrs,
228
224
  NULL, /* fold_stride: identity Face is not foldable */
@@ -255,12 +251,12 @@ ca_const_string_decode (CAConstString *ca, int64_t start, int64_t end)
255
251
  return rb_str_freeze(s);
256
252
  }
257
253
 
258
- /* storage_to_scalar in C (= skip rb_funcall on the per-cell fetch hot
254
+ /* storage_to_element in C (= skip rb_funcall on the per-cell fetch hot
259
255
  path). Since the Face surface is CA_FIXLEN (16 bytes), fetch delivers a
260
256
  16-byte raw String = the (start,end) int64 pair. Decode the buffer slice
261
257
  → frozen Ruby String (plain frozen String, no Scalar wrapper). */
262
258
  static VALUE
263
- rb_ca_const_string_storage_to_scalar (VALUE self, VALUE raw)
259
+ rb_ca_const_string_storage_to_element (VALUE self, VALUE raw)
264
260
  {
265
261
  CAConstString *ca;
266
262
  int64_t pair[2];
@@ -269,7 +265,7 @@ rb_ca_const_string_storage_to_scalar (VALUE self, VALUE raw)
269
265
 
270
266
  if ( TYPE(raw) != T_STRING || RSTRING_LEN(raw) != (long) sizeof(pair) ) {
271
267
  rb_raise(rb_eArgError,
272
- "CAConstString#storage_to_scalar: expected a %lu-byte (start,end) cell",
268
+ "CAConstString#storage_to_element: expected a %lu-byte (start,end) cell",
273
269
  (unsigned long) sizeof(pair));
274
270
  }
275
271
  memcpy(pair, RSTRING_PTR(raw), sizeof(pair));
@@ -316,8 +312,8 @@ rb_ca_const_string_wrap (VALUE parent_val, VALUE buffer, int encoding_id)
316
312
  int64_t *pair;
317
313
  boolean8_t *m;
318
314
 
319
- ca_attach(parent);
320
315
  ca_update_mask(parent);
316
+ ca_attach(parent); /* window: detached before its one raise */
321
317
  n = parent->elements;
322
318
  pair = (int64_t *) parent->ptr;
323
319
  m = parent->mask ? (boolean8_t *) parent->mask->ptr : NULL;
@@ -462,6 +458,54 @@ rb_ca_const_string_buffer (VALUE self)
462
458
 
463
459
  /* ------------------------------------------------------------------- */
464
460
 
461
+ /* to_string: the cells as a CAString over an object array of Strings, in
462
+ one pass over the buffer (shape and mask carried). */
463
+ static VALUE
464
+ rb_ca_const_string_to_string (VALUE self)
465
+ {
466
+ CAConstString *ca;
467
+ volatile VALUE vpairs, out;
468
+ CArray *pe, *co;
469
+ rb_encoding *enc;
470
+ const char *buf;
471
+ const int64_t *pair;
472
+ boolean8_t *m;
473
+ VALUE *cells;
474
+ ca_size_t i;
475
+
476
+ TypedData_Get_Struct(self, CAConstString, &catext_data_type, ca);
477
+ vpairs = rb_ca_parent(self);
478
+ TypedData_Get_Struct(vpairs, CArray, &carray_data_type, pe);
479
+ if ( ! ca_is_entity(pe) ) {
480
+ vpairs = rb_ca_copy(vpairs);
481
+ TypedData_Get_Struct(vpairs, CArray, &carray_data_type, pe);
482
+ }
483
+ enc = rb_enc_from_index(ca->encoding_id);
484
+ out = rb_carray_new(CA_OBJECT, pe->ndim, pe->dim, 0, NULL);
485
+ TypedData_Get_Struct(out, CArray, &carray_data_type, co);
486
+ m = ca_has_mask(pe) ? (boolean8_t *) pe->mask->ptr : NULL;
487
+ if ( m ) {
488
+ ca_create_mask(co);
489
+ memcpy(co->mask->ptr, m, pe->elements);
490
+ }
491
+ buf = RSTRING_PTR(ca->buffer);
492
+ pair = (const int64_t *) pe->ptr;
493
+ cells = (VALUE *) co->ptr;
494
+ for (i = 0; i < pe->elements; i++) {
495
+ if ( m && m[i] ) {
496
+ continue;
497
+ }
498
+ /* out is reachable and marks its cells, so each String is held as it
499
+ is stored */
500
+ cells[i] = rb_enc_str_new(buf + pair[2 * i],
501
+ (long) ( pair[2 * i + 1] - pair[2 * i] ), enc);
502
+ }
503
+ RB_GC_GUARD(vpairs);
504
+ RB_GC_GUARD(self);
505
+ return rb_funcall(rb_const_get(rb_cObject, rb_intern("CAString")),
506
+ rb_intern("wrap"), 1, out);
507
+ }
508
+
465
509
  /* copy = compacting deep copy — the only chain-descent point in
466
510
  CAConstString. Produces a standalone CAConstString:
467
511
  - a fresh contiguous fixlen-16 (start,end) pair entity
@@ -486,8 +530,8 @@ rb_ca_const_string_copy (VALUE self)
486
530
  TypedData_Get_Struct(self, CAConstString, &catext_data_type, ca);
487
531
 
488
532
  /* materialize logical-order (start,end) pairs + mask */
489
- ca_attach((CArray *) ca);
490
533
  ca_update_mask((CArray *) ca);
534
+ ca_attach((CArray *) ca); /* window: detached before its one raise */
491
535
 
492
536
  n = ca->elements;
493
537
  src_pair = (int64_t *) ca->ptr;
@@ -537,7 +581,9 @@ rb_ca_const_string_copy (VALUE self)
537
581
 
538
582
  ca_detach((CArray *) ca);
539
583
 
540
- return rb_ca_const_string_wrap(vout, rb_str_freeze(newbuf), ca->encoding_id);
584
+ vout = rb_ca_const_string_wrap(vout, rb_str_freeze(newbuf), ca->encoding_id);
585
+ rb_ca_inherit_attr(vout, self);
586
+ return vout;
541
587
  }
542
588
 
543
589
  /* to_ca descends to the same compacting copy. The result is detached
@@ -567,12 +613,14 @@ typedef struct {
567
613
  ca_size_t n;
568
614
  } ca_const_string_scan_t;
569
615
 
616
+ /* Opens the scan's window; ca_const_string_scan_end closes it. What runs
617
+ between them in this file reads bytes and raises only after closing. */
570
618
  static void
571
619
  ca_const_string_scan_begin (VALUE self, ca_const_string_scan_t *s)
572
620
  {
573
621
  TypedData_Get_Struct(self, CAConstString, &catext_data_type, s->ca);
574
- ca_attach((CArray *) s->ca);
575
622
  ca_update_mask((CArray *) s->ca);
623
+ ca_attach((CArray *) s->ca); /* window: closed by ca_const_string_scan_end */
576
624
  s->off = (int64_t *) s->ca->ptr;
577
625
  s->m = s->ca->mask ? (boolean8_t *) s->ca->mask->ptr : NULL;
578
626
  s->buf = RSTRING_PTR(s->ca->buffer);
@@ -586,6 +634,38 @@ ca_const_string_scan_end (ca_const_string_scan_t *s)
586
634
  ca_detach((CArray *) s->ca);
587
635
  }
588
636
 
637
+ typedef struct {
638
+ VALUE self;
639
+ ca_const_string_scan_t *s;
640
+ } ca_const_string_scan_arg_t;
641
+
642
+ static VALUE
643
+ ca_const_string_scan_begin_body (VALUE arg)
644
+ {
645
+ ca_const_string_scan_arg_t *x = (ca_const_string_scan_arg_t *) arg;
646
+ ca_const_string_scan_begin(x->self, x->s);
647
+ return Qnil;
648
+ }
649
+
650
+ /* Begins the scans of two arrays, both or neither: when the second one's
651
+ attach raises, the first is closed before the raise goes on. */
652
+ static void
653
+ ca_const_string_scan_begin_pair (VALUE va, ca_const_string_scan_t *a,
654
+ VALUE vb, ca_const_string_scan_t *b)
655
+ {
656
+ ca_const_string_scan_arg_t x;
657
+ int tag = 0;
658
+ ca_const_string_scan_begin(va, a);
659
+ x.self = vb;
660
+ x.s = b;
661
+ /* protect: ends the first scan, which is not an array */
662
+ rb_protect(ca_const_string_scan_begin_body, (VALUE) &x, &tag);
663
+ if ( tag ) {
664
+ ca_const_string_scan_end(a);
665
+ rb_jump_tag(tag);
666
+ }
667
+ }
668
+
589
669
  /* Resolve element i to its record bytes; returns 0 if masked.
590
670
  Storage is (start,end) int64 pairs: s->off holds 2 int64 per element,
591
671
  so element i occupies s->off[2*i] (start) .. s->off[2*i+1] (end).
@@ -740,8 +820,7 @@ rb_ca_const_string_eq (VALUE self, VALUE other)
740
820
  int64_t la = 0, lb = 0;
741
821
  ca_size_t i;
742
822
 
743
- ca_const_string_scan_begin(self, &a);
744
- ca_const_string_scan_begin(other, &b);
823
+ ca_const_string_scan_begin_pair(self, &a, other, &b);
745
824
  if ( a.n != b.n ) {
746
825
  ca_const_string_scan_end(&a);
747
826
  ca_const_string_scan_end(&b);
@@ -825,13 +904,12 @@ rb_ca_const_string_search (VALUE self, VALUE query)
825
904
  byte-for-byte faithful to `to_object.sort` with no collation
826
905
  loss. */
827
906
 
828
- /* qsort comparator context — single-threaded under the GVL (thread-safety is
829
- a non-goal; see project memory), so a file-static context is safe. */
830
- static ca_const_string_scan_t *ca_const_string_sort_ctx;
831
-
907
+ /* The scan is passed to the comparator, not held in a static: Ractors
908
+ sort at the same time. */
832
909
  static int
833
- ca_const_string_byte_cmp (const void *a, const void *b)
910
+ ca_const_string_byte_cmp (const void *a, const void *b, void *ctx)
834
911
  {
912
+ ca_const_string_scan_t *scan = (ca_const_string_scan_t *) ctx;
835
913
  ca_size_t ia = *(const ca_size_t *) a;
836
914
  ca_size_t ib = *(const ca_size_t *) b;
837
915
  /* sort raises on masked input, so record() always sets these here; the
@@ -839,13 +917,21 @@ ca_const_string_byte_cmp (const void *a, const void *b)
839
917
  const char *pa = NULL, *pb = NULL;
840
918
  int64_t la = 0, lb = 0;
841
919
  int c;
842
- ca_const_string_record(ca_const_string_sort_ctx, ia, &pa, &la);
843
- ca_const_string_record(ca_const_string_sort_ctx, ib, &pb, &lb);
920
+ ca_const_string_record(scan, ia, &pa, &la);
921
+ ca_const_string_record(scan, ib, &pb, &lb);
844
922
  c = memcmp(pa, pb, (size_t) (la < lb ? la : lb));
845
923
  if ( c != 0 ) return c;
846
924
  return (la > lb) - (la < lb);
847
925
  }
848
926
 
927
+ static int
928
+ ca_const_string_index_cmp (const void *a, const void *b)
929
+ {
930
+ ca_size_t ia = *(const ca_size_t *) a;
931
+ ca_size_t ib = *(const ca_size_t *) b;
932
+ return (ia > ib) - (ia < ib);
933
+ }
934
+
849
935
  /* compute the ascending sort permutation; raises on masked input (matches
850
936
  base CArray#sort). caller xfree()s the returned index array. */
851
937
  static ca_size_t *
@@ -859,9 +945,19 @@ ca_const_string_sort_perm (ca_const_string_scan_t *s)
859
945
  }
860
946
  idx = ALLOC_N(ca_size_t, s->n);
861
947
  for ( i = 0; i < s->n; i++ ) idx[i] = i;
862
- ca_const_string_sort_ctx = s;
863
- qsort(idx, (size_t) s->n, sizeof(ca_size_t), ca_const_string_byte_cmp);
864
- ca_const_string_sort_ctx = NULL;
948
+ ruby_qsort(idx, (size_t) s->n, sizeof(ca_size_t), ca_const_string_byte_cmp, s);
949
+ /* qsort is not stable. Equal strings keep their order, as CArray's own
950
+ sort does, so each run of them is put back in position order. Breaking
951
+ the tie inside the comparator instead doubles the sort on columns with
952
+ many repeats. */
953
+ for ( i = 0; i < s->n; ) {
954
+ ca_size_t j = i + 1;
955
+ while ( j < s->n && ca_const_string_byte_cmp(&idx[i], &idx[j], s) == 0 ) j++;
956
+ if ( j - i > 1 ) {
957
+ qsort(idx + i, (size_t) (j - i), sizeof(ca_size_t), ca_const_string_index_cmp);
958
+ }
959
+ i = j;
960
+ }
865
961
  return idx;
866
962
  }
867
963
 
@@ -936,6 +1032,128 @@ ca_const_string_extremum (VALUE self, int want_max)
936
1032
  static VALUE rb_ca_const_string_min (VALUE self) { return ca_const_string_extremum(self, 0); }
937
1033
  static VALUE rb_ca_const_string_max (VALUE self) { return ca_const_string_extremum(self, 1); }
938
1034
 
1035
+ /* ------------------------------------------------------------------- */
1036
+ /* Native factorize: categorize's discovery pass, read from the bytes. The
1037
+ storage cells are byte ranges, so the fixlen lane of
1038
+ CArray#__factorize_appearance__ would tell ranges apart, not strings. A
1039
+ String is made per distinct value only, after the scan's window closes. */
1040
+
1041
+ static uint64_t
1042
+ ca_const_string_hash_bytes (const char *p, int64_t len)
1043
+ {
1044
+ uint64_t h = 1469598103934665603ULL; /* FNV-1a */
1045
+ int64_t i;
1046
+ for ( i = 0; i < len; i++ ) {
1047
+ h ^= (unsigned char) p[i];
1048
+ h *= 1099511628211ULL;
1049
+ }
1050
+ return h;
1051
+ }
1052
+
1053
+ /* CAConstString#__factorize_appearance__ -> [codes, levels], as the CArray
1054
+ method answers: codes are the narrowest unsigned type holding the k codes
1055
+ and the type-max sentinel, which masked cells store; levels are the k
1056
+ distinct Strings in first-appearance order. */
1057
+ static VALUE
1058
+ rb_ca_const_string_factorize_appearance (VALUE self)
1059
+ {
1060
+ ca_const_string_scan_t s;
1061
+ CAConstString *ca;
1062
+ volatile VALUE vcodes, levels;
1063
+ CArray *co;
1064
+ uint32_t *codes, *slot;
1065
+ int64_t *lev_start, *lev_len;
1066
+ size_t cap = 16, mask_bits;
1067
+ ca_size_t n, i, k = 0;
1068
+ int8_t code_dt;
1069
+ VALUE slot_holder = 0, start_holder = 0, len_holder = 0;
1070
+
1071
+ TypedData_Get_Struct(self, CAConstString, &catext_data_type, ca);
1072
+ n = ca->elements;
1073
+ if ( (uint64_t) n >= 0xFFFFFFFFULL ) {
1074
+ rb_raise(rb_eArgError, "categorize: too many elements for uint32 codes");
1075
+ }
1076
+ while ( cap < (size_t) n * 2 ) {
1077
+ cap <<= 1;
1078
+ }
1079
+ mask_bits = cap - 1;
1080
+ /* Everything the scan writes is allocated before its window opens, and
1081
+ held by the collector: opening the window reads the offsets, which
1082
+ can raise for a parent that answers in Ruby. */
1083
+ vcodes = rb_carray_new(CA_UINT32, ca->ndim, ca->dim, 0, NULL);
1084
+ TypedData_Get_Struct(vcodes, CArray, &carray_data_type, co);
1085
+ codes = (uint32_t *) co->ptr;
1086
+ slot = ALLOCV_N(uint32_t, slot_holder, cap); /* code + 1; 0 is empty */
1087
+ MEMZERO(slot, uint32_t, cap);
1088
+ lev_start = ALLOCV_N(int64_t, start_holder, n > 0 ? n : 1);
1089
+ lev_len = ALLOCV_N(int64_t, len_holder, n > 0 ? n : 1);
1090
+
1091
+ ca_const_string_scan_begin(self, &s);
1092
+ for ( i = 0; i < s.n; i++ ) {
1093
+ const char *p;
1094
+ int64_t len;
1095
+ size_t j;
1096
+ if ( ! ca_const_string_record(&s, i, &p, &len) ) {
1097
+ codes[i] = 0xFFFFFFFFu;
1098
+ continue;
1099
+ }
1100
+ j = (size_t) ca_const_string_hash_bytes(p, len) & mask_bits;
1101
+ for (;;) {
1102
+ uint32_t c = slot[j];
1103
+ if ( c == 0 ) {
1104
+ lev_start[k] = (int64_t) (p - s.buf);
1105
+ lev_len[k] = len;
1106
+ slot[j] = (uint32_t) (k + 1);
1107
+ codes[i] = (uint32_t) k;
1108
+ k++;
1109
+ break;
1110
+ }
1111
+ if ( lev_len[c - 1] == len && memcmp(s.buf + lev_start[c - 1], p, (size_t) len) == 0 ) {
1112
+ codes[i] = c - 1;
1113
+ break;
1114
+ }
1115
+ j = (j + 1) & mask_bits;
1116
+ }
1117
+ }
1118
+ ca_const_string_scan_end(&s);
1119
+ ALLOCV_END(slot_holder);
1120
+
1121
+ /* The buffer is the column's frozen String, alive past the window. */
1122
+ levels = rb_ary_new_capa(k);
1123
+ {
1124
+ const char *buf = RSTRING_PTR(ca->buffer);
1125
+ rb_encoding *enc = rb_enc_from_index(ca->encoding_id);
1126
+ for ( i = 0; i < k; i++ ) {
1127
+ rb_ary_push(levels, rb_enc_str_new(buf + lev_start[i], lev_len[i], enc));
1128
+ }
1129
+ }
1130
+ ALLOCV_END(start_holder);
1131
+ ALLOCV_END(len_holder);
1132
+
1133
+ if ( k <= 0xFF ) { code_dt = CA_UINT8; }
1134
+ else if ( k <= 0xFFFF ) { code_dt = CA_UINT16; }
1135
+ else { code_dt = CA_UINT32; }
1136
+ if ( code_dt != CA_UINT32 ) {
1137
+ VALUE vnarrow = rb_carray_new(code_dt, ca->ndim, ca->dim, 0, NULL);
1138
+ CArray *cn;
1139
+ TypedData_Get_Struct(vnarrow, CArray, &carray_data_type, cn);
1140
+ if ( code_dt == CA_UINT8 ) {
1141
+ uint8_t *cp = (uint8_t *) cn->ptr;
1142
+ for ( i = 0; i < n; i++ ) {
1143
+ cp[i] = codes[i] == 0xFFFFFFFFu ? (uint8_t) 0xFF : (uint8_t) codes[i];
1144
+ }
1145
+ }
1146
+ else {
1147
+ uint16_t *cp = (uint16_t *) cn->ptr;
1148
+ for ( i = 0; i < n; i++ ) {
1149
+ cp[i] = codes[i] == 0xFFFFFFFFu ? (uint16_t) 0xFFFF : (uint16_t) codes[i];
1150
+ }
1151
+ }
1152
+ vcodes = vnarrow;
1153
+ }
1154
+ return rb_ary_new3(2, vcodes, levels);
1155
+ }
1156
+
939
1157
  /* ------------------------------------------------------------------- */
940
1158
 
941
1159
  static VALUE
@@ -950,6 +1168,7 @@ rb_ca_const_string_initialize_copy (VALUE self, VALUE other)
950
1168
  {
951
1169
  CAConstString *ca, *cs;
952
1170
  TypedData_Get_Struct(self, CAConstString, &catext_data_type, ca);
1171
+ ca_check_uninitialized(ca);
953
1172
  TypedData_Get_Struct(other, CAConstString, &catext_data_type, cs);
954
1173
  /* Shallow re-setup: shares the buffer and the offset source, which is the
955
1174
  documented `dup` semantics for a view. `copy` is the compacting deep
@@ -958,6 +1177,7 @@ rb_ca_const_string_initialize_copy (VALUE self, VALUE other)
958
1177
  ca_array_pool_alloc(ca, CA_OBJ_CONST_STRING, cs->parent->ndim);
959
1178
  }
960
1179
  ca_const_string_setup(ca, cs->parent, cs->buffer, cs->encoding_id);
1180
+ ca_view_inherit_mask((VALUE) ca);
961
1181
  return self;
962
1182
  }
963
1183
 
@@ -985,11 +1205,12 @@ Init_ca_obj_const_string (void)
985
1205
  rb_define_singleton_method(rb_cCAConstString, "__build__", rb_ca_const_string_build, 2);
986
1206
  rb_define_method(rb_cCAConstString, "encoding", rb_ca_const_string_encoding, 0);
987
1207
  rb_define_method(rb_cCAConstString, "buffer", rb_ca_const_string_buffer, 0);
988
- rb_define_method(rb_cCAConstString, "storage_to_scalar",
989
- rb_ca_const_string_storage_to_scalar, 1);
1208
+ rb_define_method(rb_cCAConstString, "storage_to_element",
1209
+ rb_ca_const_string_storage_to_element, 1);
990
1210
 
991
1211
  /* copy = compacting deep copy; to_ca is the same descent point. */
992
1212
  rb_define_method(rb_cCAConstString, "copy", rb_ca_const_string_copy, 0);
1213
+ rb_define_method(rb_cCAConstString, "to_string", rb_ca_const_string_to_string, 0);
993
1214
  rb_define_method(rb_cCAConstString, "to_ca", rb_ca_const_string_to_ca, -1);
994
1215
 
995
1216
  /* native byte ops (§3.7) */
@@ -1009,13 +1230,17 @@ Init_ca_obj_const_string (void)
1009
1230
  /* Native flat sort permutation / min / max (§3.7), byte-memcmp
1010
1231
  comparator. The public ordering surface is built on these in
1011
1232
  lib/carray/const_string.rb; see the comment above their definitions. */
1012
- rb_define_method(rb_cCAConstString, "__sort_addr_bytes__",
1013
- rb_ca_const_string_sort_index, 0);
1014
- rb_define_method(rb_cCAConstString, "__min_bytes__", rb_ca_const_string_min, 0);
1015
- rb_define_method(rb_cCAConstString, "__max_bytes__", rb_ca_const_string_max, 0);
1233
+ rb_define_private_method(rb_cCAConstString, "sort_addr_by_bytes",
1234
+ rb_ca_const_string_sort_index, 0);
1235
+ rb_define_private_method(rb_cCAConstString, "min_string_by_bytes",
1236
+ rb_ca_const_string_min, 0);
1237
+ rb_define_private_method(rb_cCAConstString, "__factorize_appearance__",
1238
+ rb_ca_const_string_factorize_appearance, 0);
1239
+ rb_define_private_method(rb_cCAConstString, "max_string_by_bytes",
1240
+ rb_ca_const_string_max, 0);
1016
1241
 
1017
1242
  /* Y-pilot: Face-local C-level fast path for per-cell scalar fetch. */
1018
- ca_face_register_storage_to_scalar(CA_OBJ_CONST_STRING, rb_ca_const_string_storage_to_scalar);
1243
+ ca_face_register_storage_to_element(CA_OBJ_CONST_STRING, rb_ca_const_string_storage_to_element);
1019
1244
 
1020
1245
  /* F.S1: CAConstString's `buffer` field is per-parent (= each CAConstString holds its
1021
1246
  own frozen byte String; cell offsets index into THIS instance's