carray 3.0.1 → 3.0.3

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 (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +824 -0
  3. data/README.md +5 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +258 -4
  6. data/ext/ca_axis_group.c +368 -184
  7. data/ext/ca_binop_dispatch.c +54 -32
  8. data/ext/ca_binop_dispatch.h +1 -0
  9. data/ext/ca_categorical_iterator.c +90 -41
  10. data/ext/ca_compare.c +8 -2
  11. data/ext/ca_for_buffer.h +21 -68
  12. data/ext/ca_group_iter.c +27 -11
  13. data/ext/ca_kernel_iterator.c +960 -245
  14. data/ext/ca_kernel_iterator.h +352 -181
  15. data/ext/ca_monop_dispatch.c +4 -4
  16. data/ext/ca_monop_dispatch.h +4 -0
  17. data/ext/ca_obj_array.c +194 -42
  18. data/ext/ca_obj_bincmp.c +152 -121
  19. data/ext/ca_obj_binop.c +107 -210
  20. data/ext/ca_obj_bitarray.c +37 -24
  21. data/ext/ca_obj_bitfield.c +95 -62
  22. data/ext/ca_obj_block.c +5 -4
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +180 -50
  25. data/ext/ca_obj_face.c +428 -62
  26. data/ext/ca_obj_face.h +107 -47
  27. data/ext/ca_obj_fake.c +21 -50
  28. data/ext/ca_obj_farray.c +1 -0
  29. data/ext/ca_obj_field.c +16 -3
  30. data/ext/ca_obj_fixlen_string.c +39 -26
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +235 -55
  33. data/ext/ca_obj_moncmp.c +61 -61
  34. data/ext/ca_obj_monop.c +125 -107
  35. data/ext/ca_obj_object.c +551 -233
  36. data/ext/ca_obj_record.c +51 -16
  37. data/ext/ca_obj_reduce.c +5 -8
  38. data/ext/ca_obj_refer.c +117 -33
  39. data/ext/ca_obj_remap.c +57 -17
  40. data/ext/ca_obj_repeat.c +21 -5
  41. data/ext/ca_obj_roll.c +115 -11
  42. data/ext/ca_obj_select.c +88 -75
  43. data/ext/ca_obj_select_axis.c +58 -60
  44. data/ext/ca_obj_shift.c +22 -102
  45. data/ext/ca_obj_stack.c +223 -123
  46. data/ext/ca_obj_stride.c +267 -35
  47. data/ext/ca_obj_string.c +25 -22
  48. data/ext/ca_obj_tile.c +32 -16
  49. data/ext/ca_obj_time.c +23 -25
  50. data/ext/ca_obj_timedelta.c +21 -23
  51. data/ext/ca_obj_transpose.c +12 -3
  52. data/ext/ca_obj_triop.c +110 -117
  53. data/ext/ca_obj_window.c +157 -29
  54. data/ext/ca_op_ipower.c +25 -13
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_rng_normal.h +42 -0
  58. data/ext/ca_rng_xoshiro256pp.h +105 -0
  59. data/ext/ca_sort_kernels.h +70 -0
  60. data/ext/ca_sweep_engine.c +385 -198
  61. data/ext/ca_sweep_engine.h +31 -23
  62. data/ext/ca_triop_dispatch.c +4 -1
  63. data/ext/ca_triop_dispatch.h +5 -2
  64. data/ext/caframe_csv_reader.c +463 -0
  65. data/ext/carray.h +90 -28
  66. data/ext/carray_access.c +487 -231
  67. data/ext/carray_address_basis.c +731 -0
  68. data/ext/carray_attribute.c +45 -3
  69. data/ext/carray_bincount.c +32 -2
  70. data/ext/carray_broadcast.c +49 -17
  71. data/ext/carray_call_cfunc.c +667 -511
  72. data/ext/carray_cast.c +1556 -247
  73. data/ext/carray_cast_func.rb +34 -21
  74. data/ext/carray_conversion.c +197 -73
  75. data/ext/carray_copy.c +84 -38
  76. data/ext/carray_core.c +591 -119
  77. data/ext/carray_count.c +148 -96
  78. data/ext/carray_element.c +18 -27
  79. data/ext/carray_factorize.c +604 -291
  80. data/ext/carray_generate.c +76 -40
  81. data/ext/carray_histogram.c +152 -94
  82. data/ext/carray_hold.c +2 -1
  83. data/ext/carray_index_classifier.c +63 -96
  84. data/ext/carray_internal.h +400 -1
  85. data/ext/carray_lazy.c +834 -79
  86. data/ext/carray_loop.c +88 -66
  87. data/ext/carray_mask.c +705 -324
  88. data/ext/carray_math_kernel.h +1 -0
  89. data/ext/carray_median_percentile.c +263 -63
  90. data/ext/carray_memory_view.c +254 -248
  91. data/ext/carray_operator.c +1093 -363
  92. data/ext/carray_order.c +133 -69
  93. data/ext/carray_partition.c +102 -56
  94. data/ext/carray_random.c +582 -204
  95. data/ext/carray_scatter.c +308 -65
  96. data/ext/carray_segment.c +257 -0
  97. data/ext/carray_slab.c +183 -70
  98. data/ext/carray_sort.c +235 -240
  99. data/ext/carray_sort_kernel.c +8 -1
  100. data/ext/carray_struct.c +99 -21
  101. data/ext/carray_test.c +167 -64
  102. data/ext/carray_time_text.c +1182 -0
  103. data/ext/carray_utils.c +363 -98
  104. data/ext/extconf.rb +27 -2
  105. data/ext/mk_call_cfunc.rb +103 -118
  106. data/ext/mk_pow5_table.rb +35 -0
  107. data/ext/mkkernel.rb +1349 -851
  108. data/ext/ruby_carray.c +17 -2
  109. data/ext/version.h +4 -4
  110. data/ext/version.rb +4 -2
  111. data/lib/carray/attribute.rb +39 -0
  112. data/lib/carray/autoload_carray.rb +24 -3
  113. data/lib/carray/autoload_method_extension.rb +18 -0
  114. data/lib/carray/axis_group.rb +245 -20
  115. data/lib/carray/basics.rb +95 -25
  116. data/lib/carray/bincount_nd.rb +6 -0
  117. data/lib/carray/block_iterator.rb +200 -54
  118. data/lib/carray/boolean_reduce.rb +21 -29
  119. data/lib/carray/categorical.rb +244 -59
  120. data/lib/carray/categorical_iterator.rb +391 -757
  121. data/lib/carray/complex.rb +36 -29
  122. data/lib/carray/conditional.rb +94 -7
  123. data/lib/carray/const_string.rb +177 -27
  124. data/lib/carray/construct.rb +40 -0
  125. data/lib/carray/data_type_extension.rb +3 -0
  126. data/lib/carray/data_type_limits.rb +91 -0
  127. data/lib/carray/fixlen_string.rb +1 -1
  128. data/lib/carray/frame/concat.rb +157 -6
  129. data/lib/carray/frame/convert.rb +13 -7
  130. data/lib/carray/frame/csv_parser.rb +590 -76
  131. data/lib/carray/frame/describe.rb +79 -0
  132. data/lib/carray/frame/frame.rb +205 -35
  133. data/lib/carray/frame/group.rb +199 -40
  134. data/lib/carray/frame/io.rb +452 -60
  135. data/lib/carray/frame/join.rb +134 -46
  136. data/lib/carray/frame/records.rb +54 -19
  137. data/lib/carray/frame/reshape.rb +448 -0
  138. data/lib/carray/frame/sort.rb +26 -2
  139. data/lib/carray/frame/verbs.rb +726 -56
  140. data/lib/carray/frame.rb +2 -0
  141. data/lib/carray/fuse_source.rb +111 -2
  142. data/lib/carray/fusion.rb +229 -38
  143. data/lib/carray/inspect.rb +71 -20
  144. data/lib/carray/iterator.rb +157 -2
  145. data/lib/carray/lazy.rb +54 -67
  146. data/lib/carray/mask_gap_fill.rb +66 -50
  147. data/lib/carray/math.rb +32 -19
  148. data/lib/carray/meld_reduce.rb +40 -7
  149. data/lib/carray/methods/align_addr.rb +8 -4
  150. data/lib/carray/methods/bincount.rb +2 -2
  151. data/lib/carray/methods/bit_string.rb +14 -3
  152. data/lib/carray/methods/composition.rb +16 -10
  153. data/lib/carray/methods/discovery_along.rb +74 -0
  154. data/lib/carray/methods/factorize.rb +50 -0
  155. data/lib/carray/methods/gather_nd.rb +13 -2
  156. data/lib/carray/methods/insert_block.rb +9 -14
  157. data/lib/carray/methods/is_in.rb +130 -6
  158. data/lib/carray/methods/join.rb +1 -1
  159. data/lib/carray/methods/locate_addr.rb +235 -21
  160. data/lib/carray/methods/mask_duplicates.rb +35 -1
  161. data/lib/carray/methods/mode.rb +18 -1
  162. data/lib/carray/methods/nunique.rb +22 -1
  163. data/lib/carray/methods/repeat.rb +91 -0
  164. data/lib/carray/methods/resize.rb +21 -23
  165. data/lib/carray/methods/snap.rb +34 -12
  166. data/lib/carray/methods/unique.rb +52 -3
  167. data/lib/carray/record_order.rb +208 -0
  168. data/lib/carray/rng.rb +86 -0
  169. data/lib/carray/runtime.rb +14 -11
  170. data/lib/carray/segment_iterator.rb +990 -0
  171. data/lib/carray/serialize.rb +197 -27
  172. data/lib/carray/slab_iterator.rb +109 -40
  173. data/lib/carray/stack.rb +10 -4
  174. data/lib/carray/string_operation_extension.rb +9 -4
  175. data/lib/carray/struct.rb +142 -51
  176. data/lib/carray/struct_builder.rb +110 -14
  177. data/lib/carray/table.rb +2 -2
  178. data/lib/carray/time.rb +535 -105
  179. data/lib/carray/window_iterator.rb +259 -82
  180. data/lib/carray.rb +4 -2
  181. data/yard-stubs/ca_obj_array.rb +4 -4
  182. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  183. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  184. data/yard-stubs/ca_obj_block.rb +2 -7
  185. data/yard-stubs/ca_obj_fake.rb +1 -1
  186. data/yard-stubs/ca_obj_field.rb +1 -1
  187. data/yard-stubs/ca_obj_refer.rb +3 -3
  188. data/yard-stubs/ca_obj_roll.rb +0 -1
  189. data/yard-stubs/ca_obj_stride.rb +10 -7
  190. data/yard-stubs/ca_obj_transpose.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +11 -4
  192. data/yard-stubs/carray_access.rb +6 -5
  193. data/yard-stubs/carray_broadcast.rb +2 -2
  194. data/yard-stubs/carray_cast.rb +78 -43
  195. data/yard-stubs/carray_conversion.rb +13 -1
  196. data/yard-stubs/carray_copy.rb +8 -3
  197. data/yard-stubs/carray_core.rb +3 -83
  198. data/yard-stubs/carray_count.rb +16 -2
  199. data/yard-stubs/carray_lazy.rb +210 -1
  200. data/yard-stubs/carray_loop.rb +4 -1
  201. data/yard-stubs/carray_mask.rb +34 -10
  202. data/yard-stubs/carray_math.rb +1499 -4
  203. data/yard-stubs/carray_median_percentile.rb +26 -8
  204. data/yard-stubs/carray_order.rb +43 -82
  205. data/yard-stubs/carray_scatter.rb +10 -3
  206. data/yard-stubs/carray_segment.rb +72 -0
  207. data/yard-stubs/carray_slab.rb +11 -8
  208. data/yard-stubs/carray_sort.rb +50 -8
  209. data/yard-stubs/carray_test.rb +10 -0
  210. data/yard-stubs/carray_utils.rb +0 -26
  211. metadata +19 -19
  212. data/ext/ca_for_each_element.h +0 -239
  213. data/ext/carray_build_flags.h +0 -3
  214. data/ext/carray_config.h +0 -28
  215. data/ext/carray_kernels_bincmp.c +0 -4446
  216. data/ext/carray_kernels_binop.c +0 -11001
  217. data/ext/carray_kernels_init.c +0 -1131
  218. data/ext/carray_kernels_map.c +0 -3467
  219. data/ext/carray_kernels_moncmp.c +0 -2097
  220. data/ext/carray_kernels_monop.c +0 -18313
  221. data/ext/carray_kernels_reduce_aggregate.c +0 -25837
  222. data/ext/carray_kernels_reduce_boolean.c +0 -330
  223. data/ext/carray_kernels_reduce_cumulative.c +0 -14593
  224. data/ext/carray_kernels_reduce_extreme.c +0 -16948
  225. data/ext/carray_kernels_reduce_variance.c +0 -3910
  226. data/ext/carray_kernels_scan.c +0 -3693
  227. data/ext/carray_kernels_search.c +0 -32138
  228. data/ext/carray_kernels_sort.c +0 -10626
  229. data/ext/carray_kernels_triop.c +0 -1392
@@ -26,11 +26,12 @@
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 "carray_internal.h"
34
35
  #include "ca_obj_face.h"
35
36
  #include <ruby/encoding.h>
36
37
 
@@ -38,10 +39,10 @@
38
39
  typedef struct {
39
40
  /* === CAView prefix === */
40
41
  int16_t obj_type;
41
- int8_t data_type; /* CA_FIXLEN surface (numeric gate); storage is int64 */
42
+ int8_t data_type; /* CA_FIXLEN surface (numeric gate); storage is fixlen-16 */
42
43
  int8_t ndim;
43
44
  int32_t flags; /* CA_FLAG_IS_FACE set */
44
- ca_size_t bytes; /* sizeof(int64_t) = 8 (one offset per element) */
45
+ ca_size_t bytes; /* 2 * sizeof(int64_t) = 16 (one (start,end) pair per element) */
45
46
  ca_size_t elements;
46
47
  ca_size_t *dim;
47
48
  char *ptr;
@@ -200,18 +201,6 @@ ca_const_string_func_allocate (void *ap)
200
201
  ca->ptr = ca->parent->ptr;
201
202
  }
202
203
 
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
204
  ca_operation_function_t ca_const_string_func = {
216
205
  -1, /* CA_OBJ_CONST_STRING — filled by ca_install_obj_type */
217
206
  CA_VIEW_ARRAY,
@@ -222,7 +211,7 @@ ca_operation_function_t ca_const_string_func = {
222
211
  ca_face_sync,
223
212
  ca_face_detach,
224
213
  ca_face_fill_data,
225
- ca_const_string_func_create_mask,
214
+ ca_view_func_create_mask,
226
215
  ca_face_xfer_index,
227
216
  ca_face_xfer_addrs,
228
217
  NULL, /* fold_stride: identity Face is not foldable */
@@ -255,12 +244,12 @@ ca_const_string_decode (CAConstString *ca, int64_t start, int64_t end)
255
244
  return rb_str_freeze(s);
256
245
  }
257
246
 
258
- /* storage_to_scalar in C (= skip rb_funcall on the per-cell fetch hot
247
+ /* storage_to_element in C (= skip rb_funcall on the per-cell fetch hot
259
248
  path). Since the Face surface is CA_FIXLEN (16 bytes), fetch delivers a
260
249
  16-byte raw String = the (start,end) int64 pair. Decode the buffer slice
261
250
  → frozen Ruby String (plain frozen String, no Scalar wrapper). */
262
251
  static VALUE
263
- rb_ca_const_string_storage_to_scalar (VALUE self, VALUE raw)
252
+ rb_ca_const_string_storage_to_element (VALUE self, VALUE raw)
264
253
  {
265
254
  CAConstString *ca;
266
255
  int64_t pair[2];
@@ -269,7 +258,7 @@ rb_ca_const_string_storage_to_scalar (VALUE self, VALUE raw)
269
258
 
270
259
  if ( TYPE(raw) != T_STRING || RSTRING_LEN(raw) != (long) sizeof(pair) ) {
271
260
  rb_raise(rb_eArgError,
272
- "CAConstString#storage_to_scalar: expected a %lu-byte (start,end) cell",
261
+ "CAConstString#storage_to_element: expected a %lu-byte (start,end) cell",
273
262
  (unsigned long) sizeof(pair));
274
263
  }
275
264
  memcpy(pair, RSTRING_PTR(raw), sizeof(pair));
@@ -299,6 +288,54 @@ rb_ca_const_string_wrap (VALUE parent_val, VALUE buffer, int encoding_id)
299
288
  rb_raise(rb_eTypeError, "CAConstString.wrap: buffer: must be a String");
300
289
  }
301
290
 
291
+ /* This is the one door through which already-built offsets enter, so it
292
+ validates rather than assumes. Everything downstream -- the per-cell
293
+ decode, the native byte scans, the sort comparator -- reads `buf + start`
294
+ for `end - start` bytes on the strength of these pairs; an out-of-range
295
+ one is an out-of-bounds read of the heap, not a wrong answer. Refuse it
296
+ here, while the input is still in the caller's hands, rather than letting
297
+ it surface as an IndexError from a decode or as a crash from a scan.
298
+
299
+ A masked cell is exempt: its bytes may be anything at all, that being the
300
+ CArray mask contract, and no reader dereferences them (the scan skips on
301
+ the mask before touching an offset; `.value`, the explicit strip, raises
302
+ from the decode). */
303
+ {
304
+ ca_size_t i, n;
305
+ int64_t *pair;
306
+ boolean8_t *m;
307
+
308
+ ca_update_mask(parent);
309
+ ca_attach(parent); /* window: detached before its one raise */
310
+ n = parent->elements;
311
+ pair = (int64_t *) parent->ptr;
312
+ m = parent->mask ? (boolean8_t *) parent->mask->ptr : NULL;
313
+ for ( i = 0; i < n; i++ ) {
314
+ int64_t start = pair[2 * i], end = pair[2 * i + 1];
315
+ if ( m && m[i] ) {
316
+ continue;
317
+ }
318
+ if ( start < 0 || end < start || end > (int64_t) RSTRING_LEN(buffer) ) {
319
+ ca_detach(parent);
320
+ rb_raise(rb_eArgError,
321
+ "CAConstString.wrap: element %lld has range [%lld,%lld), "
322
+ "outside the %ld-byte buffer",
323
+ (long long) i, (long long) start, (long long) end,
324
+ (long) RSTRING_LEN(buffer));
325
+ }
326
+ }
327
+ ca_detach(parent);
328
+ }
329
+
330
+ /* Take ownership of the offsets, the way CACategorical.from_codes takes
331
+ ownership of its codes. Without this the Face is read-only but its
332
+ storage is not, so `cs.parent[i] = <anything>` walks straight past the
333
+ check above and back into an out-of-bounds read. The flag rather than
334
+ #freeze, for the reason from_codes gives: freeze would propagate through
335
+ views and Faces. A caller that wants to keep a mutable entity of its own
336
+ passes `.copy`. */
337
+ ca_set_flag(parent, CA_FLAG_READ_ONLY);
338
+
302
339
  ca = ca_const_string_new(parent, rb_str_freeze(buffer), encoding_id);
303
340
  obj = TypedData_Wrap_Struct(rb_cCAConstString, &catext_data_type, ca);
304
341
  rb_ca_set_parent(obj, parent_val); /* pin parent VALUE for GC */
@@ -397,10 +434,13 @@ rb_ca_const_string_encoding (VALUE self)
397
434
  return rb_enc_from_encoding(rb_enc_from_index(ca->encoding_id));
398
435
  }
399
436
 
400
- /* `CAConstString#buffer` — returns the frozen internal byte buffer
401
- (length-prefix format). Exposes the defining tail state for
402
- introspection and the Arrow-boundary component-buffer export
403
- path. */
437
+ /* `CAConstString#buffer` — returns the frozen internal byte buffer: a pure
438
+ concatenation of the element bytes, with no per-record length prefix (=
439
+ the Arrow values buffer). Exposes the defining tail state for
440
+ introspection and the Arrow-boundary component-buffer export path. The
441
+ ranges that index it live in the storage, one (start,end) int64 pair per
442
+ element; `wrap` is where a hand-built pair array is checked against a
443
+ buffer. */
404
444
  static VALUE
405
445
  rb_ca_const_string_buffer (VALUE self)
406
446
  {
@@ -411,6 +451,54 @@ rb_ca_const_string_buffer (VALUE self)
411
451
 
412
452
  /* ------------------------------------------------------------------- */
413
453
 
454
+ /* to_string: the cells as a CAString over an object array of Strings, in
455
+ one pass over the buffer (shape and mask carried). */
456
+ static VALUE
457
+ rb_ca_const_string_to_string (VALUE self)
458
+ {
459
+ CAConstString *ca;
460
+ volatile VALUE vpairs, out;
461
+ CArray *pe, *co;
462
+ rb_encoding *enc;
463
+ const char *buf;
464
+ const int64_t *pair;
465
+ boolean8_t *m;
466
+ VALUE *cells;
467
+ ca_size_t i;
468
+
469
+ TypedData_Get_Struct(self, CAConstString, &catext_data_type, ca);
470
+ vpairs = rb_ca_parent(self);
471
+ TypedData_Get_Struct(vpairs, CArray, &carray_data_type, pe);
472
+ if ( ! ca_is_entity(pe) ) {
473
+ vpairs = rb_ca_copy(vpairs);
474
+ TypedData_Get_Struct(vpairs, CArray, &carray_data_type, pe);
475
+ }
476
+ enc = rb_enc_from_index(ca->encoding_id);
477
+ out = rb_carray_new(CA_OBJECT, pe->ndim, pe->dim, 0, NULL);
478
+ TypedData_Get_Struct(out, CArray, &carray_data_type, co);
479
+ m = ca_has_mask(pe) ? (boolean8_t *) pe->mask->ptr : NULL;
480
+ if ( m ) {
481
+ ca_create_mask(co);
482
+ memcpy(co->mask->ptr, m, pe->elements);
483
+ }
484
+ buf = RSTRING_PTR(ca->buffer);
485
+ pair = (const int64_t *) pe->ptr;
486
+ cells = (VALUE *) co->ptr;
487
+ for (i = 0; i < pe->elements; i++) {
488
+ if ( m && m[i] ) {
489
+ continue;
490
+ }
491
+ /* out is reachable and marks its cells, so each String is held as it
492
+ is stored */
493
+ cells[i] = rb_enc_str_new(buf + pair[2 * i],
494
+ (long) ( pair[2 * i + 1] - pair[2 * i] ), enc);
495
+ }
496
+ RB_GC_GUARD(vpairs);
497
+ RB_GC_GUARD(self);
498
+ return rb_funcall(rb_const_get(rb_cObject, rb_intern("CAString")),
499
+ rb_intern("wrap"), 1, out);
500
+ }
501
+
414
502
  /* copy = compacting deep copy — the only chain-descent point in
415
503
  CAConstString. Produces a standalone CAConstString:
416
504
  - a fresh contiguous fixlen-16 (start,end) pair entity
@@ -435,8 +523,8 @@ rb_ca_const_string_copy (VALUE self)
435
523
  TypedData_Get_Struct(self, CAConstString, &catext_data_type, ca);
436
524
 
437
525
  /* materialize logical-order (start,end) pairs + mask */
438
- ca_attach((CArray *) ca);
439
526
  ca_update_mask((CArray *) ca);
527
+ ca_attach((CArray *) ca); /* window: detached before its one raise */
440
528
 
441
529
  n = ca->elements;
442
530
  src_pair = (int64_t *) ca->ptr;
@@ -516,12 +604,14 @@ typedef struct {
516
604
  ca_size_t n;
517
605
  } ca_const_string_scan_t;
518
606
 
607
+ /* Opens the scan's window; ca_const_string_scan_end closes it. What runs
608
+ between them in this file reads bytes and raises only after closing. */
519
609
  static void
520
610
  ca_const_string_scan_begin (VALUE self, ca_const_string_scan_t *s)
521
611
  {
522
612
  TypedData_Get_Struct(self, CAConstString, &catext_data_type, s->ca);
523
- ca_attach((CArray *) s->ca);
524
613
  ca_update_mask((CArray *) s->ca);
614
+ ca_attach((CArray *) s->ca); /* window: closed by ca_const_string_scan_end */
525
615
  s->off = (int64_t *) s->ca->ptr;
526
616
  s->m = s->ca->mask ? (boolean8_t *) s->ca->mask->ptr : NULL;
527
617
  s->buf = RSTRING_PTR(s->ca->buffer);
@@ -535,12 +625,43 @@ ca_const_string_scan_end (ca_const_string_scan_t *s)
535
625
  ca_detach((CArray *) s->ca);
536
626
  }
537
627
 
628
+ typedef struct {
629
+ VALUE self;
630
+ ca_const_string_scan_t *s;
631
+ } ca_const_string_scan_arg_t;
632
+
633
+ static VALUE
634
+ ca_const_string_scan_begin_body (VALUE arg)
635
+ {
636
+ ca_const_string_scan_arg_t *x = (ca_const_string_scan_arg_t *) arg;
637
+ ca_const_string_scan_begin(x->self, x->s);
638
+ return Qnil;
639
+ }
640
+
641
+ /* Begins the scans of two arrays, both or neither: when the second one's
642
+ attach raises, the first is closed before the raise goes on. */
643
+ static void
644
+ ca_const_string_scan_begin_pair (VALUE va, ca_const_string_scan_t *a,
645
+ VALUE vb, ca_const_string_scan_t *b)
646
+ {
647
+ ca_const_string_scan_arg_t x;
648
+ int tag = 0;
649
+ ca_const_string_scan_begin(va, a);
650
+ x.self = vb;
651
+ x.s = b;
652
+ rb_protect(ca_const_string_scan_begin_body, (VALUE) &x, &tag);
653
+ if ( tag ) {
654
+ ca_const_string_scan_end(a);
655
+ rb_jump_tag(tag);
656
+ }
657
+ }
658
+
538
659
  /* Resolve element i to its record bytes; returns 0 if masked.
539
660
  Storage is (start,end) int64 pairs: s->off holds 2 int64 per element,
540
661
  so element i occupies s->off[2*i] (start) .. s->off[2*i+1] (end).
541
662
  The buffer is a pure concatenation; length = end - start. */
542
663
  static inline int
543
- ca_const_string_record (ca_const_string_scan_t *s, ca_size_t i, const char **pp, int32_t *plen)
664
+ ca_const_string_record (ca_const_string_scan_t *s, ca_size_t i, const char **pp, int64_t *plen)
544
665
  {
545
666
  int64_t start, end;
546
667
  if ( s->m && s->m[i] ) {
@@ -549,7 +670,7 @@ ca_const_string_record (ca_const_string_scan_t *s, ca_size_t i, const char **pp,
549
670
  start = s->off[2 * i];
550
671
  end = s->off[2 * i + 1];
551
672
  *pp = s->buf + start;
552
- *plen = (int32_t) (end - start);
673
+ *plen = (int64_t) (end - start);
553
674
  return 1;
554
675
  }
555
676
 
@@ -598,7 +719,7 @@ rb_ca_const_string_byte_length (VALUE self)
598
719
  CArray *co;
599
720
  int64_t *op;
600
721
  const char *p;
601
- int32_t len;
722
+ int64_t len;
602
723
  ca_size_t i;
603
724
 
604
725
  ca_const_string_scan_begin(self, &s);
@@ -633,7 +754,7 @@ ca_const_string_predicate (VALUE self, VALUE query, int op)
633
754
  boolean8_t *out;
634
755
  const char *qp, *p;
635
756
  long qlen;
636
- int32_t len;
757
+ int64_t len;
637
758
  ca_size_t i;
638
759
 
639
760
  if ( TYPE(query) != T_STRING ) {
@@ -686,11 +807,10 @@ rb_ca_const_string_eq (VALUE self, VALUE other)
686
807
  they are read only when both records are valid, but GCC cannot prove
687
808
  that -- initialize to keep -Wmaybe-uninitialized quiet and defensive. */
688
809
  const char *pa = NULL, *pb = NULL;
689
- int32_t la = 0, lb = 0;
810
+ int64_t la = 0, lb = 0;
690
811
  ca_size_t i;
691
812
 
692
- ca_const_string_scan_begin(self, &a);
693
- ca_const_string_scan_begin(other, &b);
813
+ ca_const_string_scan_begin_pair(self, &a, other, &b);
694
814
  if ( a.n != b.n ) {
695
815
  ca_const_string_scan_end(&a);
696
816
  ca_const_string_scan_end(&b);
@@ -717,7 +837,7 @@ rb_ca_const_string_count (VALUE self, VALUE query)
717
837
  ca_const_string_scan_t s;
718
838
  const char *qp, *p;
719
839
  long qlen;
720
- int32_t len;
840
+ int64_t len;
721
841
  ca_size_t i, c = 0;
722
842
 
723
843
  if ( TYPE(query) != T_STRING ) {
@@ -747,7 +867,7 @@ rb_ca_const_string_search (VALUE self, VALUE query)
747
867
  ca_const_string_scan_t s;
748
868
  const char *qp, *p;
749
869
  long qlen;
750
- int32_t len;
870
+ int64_t len;
751
871
  ca_size_t i;
752
872
  VALUE result = Qnil;
753
873
 
@@ -786,7 +906,7 @@ ca_const_string_byte_cmp (const void *a, const void *b)
786
906
  /* sort raises on masked input, so record() always sets these here; the
787
907
  initializers keep -Wmaybe-uninitialized quiet and stay defensive. */
788
908
  const char *pa = NULL, *pb = NULL;
789
- int32_t la = 0, lb = 0;
909
+ int64_t la = 0, lb = 0;
790
910
  int c;
791
911
  ca_const_string_record(ca_const_string_sort_ctx, ia, &pa, &la);
792
912
  ca_const_string_record(ca_const_string_sort_ctx, ib, &pb, &lb);
@@ -836,9 +956,15 @@ rb_ca_const_string_sort_index (VALUE self)
836
956
  return vout;
837
957
  }
838
958
 
839
- /* CAConstString#sort is defined in Ruby (lib/carray/const_string.rb) as `self[sort_index]`,
840
- i.e. a no-copy view over the offset source (buffer + offsets shared, only
841
- the gather order changes) — consistent with CArray#sort being a view. */
959
+ /* The ordering surface is defined in Ruby (lib/carray/const_string.rb) on
960
+ top of these three: a storage cell is a byte range, so storage order is
961
+ insertion order rather than string order, and every member has to read
962
+ the bytes. What is native here is the flat form, which is the common one
963
+ for a string column and which a byte scan does an order of magnitude
964
+ faster than decoding a Ruby String per cell; the per-axis forms go
965
+ through #to_string. These are spelled as internals because the public
966
+ names carry CArray's meanings: what the permutation below answers is
967
+ sort_addr (view-flat addresses in sorted order), not sort_index. */
842
968
 
843
969
  /* CAConstString#min / #max → the byte-min / byte-max element as a frozen String,
844
970
  skipping masked elements; nil if empty or all masked. */
@@ -847,7 +973,7 @@ ca_const_string_extremum (VALUE self, int want_max)
847
973
  {
848
974
  ca_const_string_scan_t s;
849
975
  const char *p, *bestp = NULL;
850
- int32_t len, bestlen = 0;
976
+ int64_t len, bestlen = 0;
851
977
  ca_size_t i;
852
978
  int found = 0;
853
979
 
@@ -893,10 +1019,11 @@ rb_ca_const_string_initialize_copy (VALUE self, VALUE other)
893
1019
  {
894
1020
  CAConstString *ca, *cs;
895
1021
  TypedData_Get_Struct(self, CAConstString, &catext_data_type, ca);
1022
+ ca_check_uninitialized(ca);
896
1023
  TypedData_Get_Struct(other, CAConstString, &catext_data_type, cs);
897
- /* T.0/T.1: shallow re-setup (shares buffer + offset-source). T.3 will
898
- replace this with compacting deep copy (rebased offsets + compacted
899
- buffer + detach). */
1024
+ /* Shallow re-setup: shares the buffer and the offset source, which is the
1025
+ documented `dup` semantics for a view. `copy` is the compacting deep
1026
+ copy (rebased offsets, compacted buffer, detached). */
900
1027
  if ( ca_func[CA_OBJ_CONST_STRING].pool_init ) {
901
1028
  ca_array_pool_alloc(ca, CA_OBJ_CONST_STRING, cs->parent->ndim);
902
1029
  }
@@ -928,11 +1055,12 @@ Init_ca_obj_const_string (void)
928
1055
  rb_define_singleton_method(rb_cCAConstString, "__build__", rb_ca_const_string_build, 2);
929
1056
  rb_define_method(rb_cCAConstString, "encoding", rb_ca_const_string_encoding, 0);
930
1057
  rb_define_method(rb_cCAConstString, "buffer", rb_ca_const_string_buffer, 0);
931
- rb_define_method(rb_cCAConstString, "storage_to_scalar",
932
- rb_ca_const_string_storage_to_scalar, 1);
1058
+ rb_define_method(rb_cCAConstString, "storage_to_element",
1059
+ rb_ca_const_string_storage_to_element, 1);
933
1060
 
934
1061
  /* copy = compacting deep copy; to_ca is the same descent point. */
935
1062
  rb_define_method(rb_cCAConstString, "copy", rb_ca_const_string_copy, 0);
1063
+ rb_define_method(rb_cCAConstString, "to_string", rb_ca_const_string_to_string, 0);
936
1064
  rb_define_method(rb_cCAConstString, "to_ca", rb_ca_const_string_to_ca, -1);
937
1065
 
938
1066
  /* native byte ops (§3.7) */
@@ -949,14 +1077,16 @@ Init_ca_obj_const_string (void)
949
1077
  rb_define_method(rb_cCAConstString, "search", rb_ca_const_string_search, 1);
950
1078
  rb_define_method(rb_cCAConstString, "find_value_index", rb_ca_const_string_search, 1);
951
1079
 
952
- /* native sort_index / min / max (§3.7), byte-memcmp comparator.
953
- sort / sort_copy are defined in Ruby on top of sort_index. */
954
- rb_define_method(rb_cCAConstString, "sort_index", rb_ca_const_string_sort_index, 0);
955
- rb_define_method(rb_cCAConstString, "min", rb_ca_const_string_min, 0);
956
- rb_define_method(rb_cCAConstString, "max", rb_ca_const_string_max, 0);
1080
+ /* Native flat sort permutation / min / max (§3.7), byte-memcmp
1081
+ comparator. The public ordering surface is built on these in
1082
+ lib/carray/const_string.rb; see the comment above their definitions. */
1083
+ rb_define_method(rb_cCAConstString, "__sort_addr_bytes__",
1084
+ rb_ca_const_string_sort_index, 0);
1085
+ rb_define_method(rb_cCAConstString, "__min_bytes__", rb_ca_const_string_min, 0);
1086
+ rb_define_method(rb_cCAConstString, "__max_bytes__", rb_ca_const_string_max, 0);
957
1087
 
958
1088
  /* Y-pilot: Face-local C-level fast path for per-cell scalar fetch. */
959
- ca_face_register_storage_to_scalar(CA_OBJ_CONST_STRING, rb_ca_const_string_storage_to_scalar);
1089
+ ca_face_register_storage_to_element(CA_OBJ_CONST_STRING, rb_ca_const_string_storage_to_element);
960
1090
 
961
1091
  /* F.S1: CAConstString's `buffer` field is per-parent (= each CAConstString holds its
962
1092
  own frozen byte String; cell offsets index into THIS instance's