carray 3.0.6 → 3.0.7

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 (139) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +110 -0
  3. data/README.md +24 -3
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_group.c +6 -6
  6. data/ext/ca_binop_dispatch.c +8 -0
  7. data/ext/ca_binop_dispatch.h +1 -0
  8. data/ext/ca_categorical_iterator.c +7 -7
  9. data/ext/ca_kernel_iterator.h +6 -4
  10. data/ext/ca_obj_array.c +4 -4
  11. data/ext/ca_obj_bincmp.c +19 -3
  12. data/ext/ca_obj_binop.c +36 -11
  13. data/ext/ca_obj_bitarray.c +2 -4
  14. data/ext/ca_obj_bitfield.c +86 -4
  15. data/ext/ca_obj_byte_swap.c +2 -4
  16. data/ext/ca_obj_const_string.c +422 -257
  17. data/ext/ca_obj_face.c +71 -33
  18. data/ext/ca_obj_fake.c +112 -13
  19. data/ext/ca_obj_farray.c +7 -1
  20. data/ext/ca_obj_fixlen_string.c +51 -17
  21. data/ext/ca_obj_grid.c +12 -7
  22. data/ext/ca_obj_meld.c +5 -5
  23. data/ext/ca_obj_monop.c +65 -39
  24. data/ext/ca_obj_object.c +9 -9
  25. data/ext/ca_obj_reduce.c +2 -4
  26. data/ext/ca_obj_refer.c +104 -10
  27. data/ext/ca_obj_repeat.c +1 -1
  28. data/ext/ca_obj_roll.c +2 -4
  29. data/ext/ca_obj_select.c +8 -6
  30. data/ext/ca_obj_select_axis.c +8 -6
  31. data/ext/ca_obj_stack.c +5 -5
  32. data/ext/ca_obj_stride.c +46 -8
  33. data/ext/ca_obj_string.c +2 -2
  34. data/ext/ca_obj_tile.c +2 -4
  35. data/ext/ca_obj_triop.c +15 -12
  36. data/ext/ca_obj_window.c +22 -15
  37. data/ext/ca_op_ipower.c +63 -15
  38. data/ext/ca_op_powi.h +6 -29
  39. data/ext/ca_sort_kernels.h +3 -0
  40. data/ext/ca_sweep_engine.c +90 -23
  41. data/ext/ca_sweep_engine.h +5 -6
  42. data/ext/caframe_csv_reader.c +1 -1
  43. data/ext/carray.h +3 -1
  44. data/ext/carray_access.c +98 -39
  45. data/ext/carray_address_basis.c +98 -24
  46. data/ext/carray_broadcast.c +2 -2
  47. data/ext/carray_call_cfunc.c +5 -8
  48. data/ext/carray_cast.c +311 -37
  49. data/ext/carray_cast_func.rb +37 -5
  50. data/ext/carray_conversion.c +56 -2
  51. data/ext/carray_copy.c +24 -0
  52. data/ext/carray_core.c +69 -6
  53. data/ext/carray_count.c +28 -9
  54. data/ext/carray_element.c +9 -3
  55. data/ext/carray_factorize.c +11 -11
  56. data/ext/carray_histogram.c +2 -2
  57. data/ext/carray_index_classifier.c +7 -5
  58. data/ext/carray_internal.h +94 -0
  59. data/ext/carray_kernels_binop.c +55 -55
  60. data/ext/carray_kernels_init.c +14 -14
  61. data/ext/carray_kernels_monop.c +90 -140
  62. data/ext/carray_lazy.c +16 -1
  63. data/ext/carray_loop.c +9 -2
  64. data/ext/carray_mask.c +118 -11
  65. data/ext/carray_median_percentile.c +13 -8
  66. data/ext/carray_memory_view.c +41 -22
  67. data/ext/carray_operator.c +72 -11
  68. data/ext/carray_order.c +52 -9
  69. data/ext/carray_partition.c +1 -1
  70. data/ext/carray_random.c +1 -1
  71. data/ext/carray_round.c +24 -1
  72. data/ext/carray_scatter.c +4 -0
  73. data/ext/carray_segment.c +1 -1
  74. data/ext/carray_slab.c +13 -4
  75. data/ext/carray_sort.c +6 -3
  76. data/ext/carray_sort_kernel.c +24 -0
  77. data/ext/carray_struct.c +21 -6
  78. data/ext/carray_test.c +85 -39
  79. data/ext/carray_time_text.c +6 -0
  80. data/ext/carray_utils.c +124 -54
  81. data/ext/mk_call_cfunc.rb +6 -11
  82. data/ext/mkkernel.rb +37 -27
  83. data/ext/version.h +4 -4
  84. data/lib/carray/axis_group.rb +9 -6
  85. data/lib/carray/basics.rb +26 -8
  86. data/lib/carray/bincount_nd.rb +8 -2
  87. data/lib/carray/block_iterator.rb +48 -21
  88. data/lib/carray/categorical.rb +21 -8
  89. data/lib/carray/categorical_iterator.rb +2 -1
  90. data/lib/carray/conditional.rb +52 -4
  91. data/lib/carray/const_string.rb +74 -0
  92. data/lib/carray/construct.rb +2 -2
  93. data/lib/carray/core_extensions.rb +19 -68
  94. data/lib/carray/data_type_extension.rb +1 -0
  95. data/lib/carray/fixlen_string.rb +83 -1
  96. data/lib/carray/frame/column_verbs.rb +61 -6
  97. data/lib/carray/frame/concat.rb +50 -6
  98. data/lib/carray/frame/csv_parser.rb +13 -4
  99. data/lib/carray/frame/frame.rb +38 -6
  100. data/lib/carray/frame/group.rb +67 -40
  101. data/lib/carray/frame/io.rb +10 -5
  102. data/lib/carray/frame/join.rb +20 -6
  103. data/lib/carray/frame/records.rb +6 -4
  104. data/lib/carray/frame/reshape.rb +25 -5
  105. data/lib/carray/frame/verbs.rb +78 -25
  106. data/lib/carray/frame/windows.rb +30 -6
  107. data/lib/carray/fuse_source.rb +4 -2
  108. data/lib/carray/iterator.rb +8 -0
  109. data/lib/carray/key_windows.rb +10 -4
  110. data/lib/carray/lazy.rb +12 -17
  111. data/lib/carray/methods/bin.rb +3 -0
  112. data/lib/carray/methods/composition.rb +1 -1
  113. data/lib/carray/methods/factorize.rb +2 -0
  114. data/lib/carray/methods/gather_nd.rb +4 -1
  115. data/lib/carray/methods/is_in.rb +23 -3
  116. data/lib/carray/methods/locate_addr.rb +63 -12
  117. data/lib/carray/methods/repeat.rb +11 -1
  118. data/lib/carray/record_order.rb +35 -6
  119. data/lib/carray/runtime.rb +10 -0
  120. data/lib/carray/segment_iterator.rb +19 -16
  121. data/lib/carray/serialize.rb +22 -0
  122. data/lib/carray/slab_iterator.rb +1 -1
  123. data/lib/carray/stack.rb +3 -3
  124. data/lib/carray/string.rb +6 -6
  125. data/lib/carray/string_operation_extension.rb +25 -4
  126. data/lib/carray/struct_builder.rb +4 -1
  127. data/lib/carray/time.rb +190 -46
  128. data/lib/carray/window_iterator.rb +48 -12
  129. data/yard-stubs/ca_obj_bitfield.rb +4 -1
  130. data/yard-stubs/ca_obj_refer.rb +3 -0
  131. data/yard-stubs/ca_obj_window.rb +2 -1
  132. data/yard-stubs/carray_access.rb +4 -2
  133. data/yard-stubs/carray_cast.rb +6 -0
  134. data/yard-stubs/carray_count.rb +6 -3
  135. data/yard-stubs/carray_math.rb +5 -0
  136. data/yard-stubs/carray_order.rb +6 -0
  137. data/yard-stubs/carray_scatter.rb +2 -1
  138. data/yard-stubs/carray_test.rb +18 -5
  139. metadata +2 -2
data/ext/carray_mask.c CHANGED
@@ -531,11 +531,69 @@ ca_mask_standin_current (CArray *mask_view, CArray *reading, CArray *parent,
531
531
  return ( parent->mask != NULL ) ? parent->mask : reading;
532
532
  }
533
533
 
534
+ /* The first view from ca up the parent chain whose mask is attached, or
535
+ NULL. An attached mask may alias its root: whether it does is not asked,
536
+ as ca_attach_is_alias answers yes for any view that already has a pointer,
537
+ and the copy made for one that does is a no-op. */
538
+ static CArray *
539
+ ca_mask_buffer_holder (CArray *ca)
540
+ {
541
+ while ( ca != NULL && ca_is_view(ca)
542
+ && ! ca_test_flag(ca, CA_FLAG_MULTI_PARENTS) ) {
543
+ if ( ca->mask != NULL && ca_is_view(ca->mask)
544
+ && ca_is_attached(ca->mask) ) {
545
+ return ca;
546
+ }
547
+ ca = CAVIEW(ca)->parent;
548
+ }
549
+ return NULL;
550
+ }
551
+
552
+ /* Marks the cells addrs of mask, the mask of a view at or below chain. The
553
+ write goes past any buffer an attach window holds on the way to the root,
554
+ and the sync that closes that window sends the buffer: the cells this
555
+ write turned true are copied into it, read as the difference of two
556
+ gathers so that what the window has written there meanwhile stays. */
557
+ static void
558
+ ca_mask_fill_past_buffers (CArray *mask, CArray *chain, ca_size_t m,
559
+ ca_size_t *addrs)
560
+ {
561
+ volatile VALUE holder = 0, reader;
562
+ CArray *owner = ca_mask_buffer_holder(chain);
563
+ CArray *held, *rd;
564
+ boolean8_t *before, *after, *z;
565
+ boolean8_t one = 1;
566
+ ca_size_t i, n;
567
+
568
+ if ( owner == NULL ) {
569
+ ca_fill_addrs(mask, m, addrs, &one);
570
+ return;
571
+ }
572
+ held = owner->mask;
573
+ n = held->elements;
574
+ /* A view answers from its buffer while it has one: the clone has none. */
575
+ rd = (CArray *) ca_clone(held);
576
+ reader = ca_wrap_struct(rd);
577
+ before = ALLOCV_N(boolean8_t, holder, 2 * n);
578
+ after = before + n;
579
+ ca_xfer_all(rd, before, CA_XFER_GET);
580
+ ca_mask_fill_past_buffers(mask, CAVIEW(owner)->parent, m, addrs);
581
+ ca_xfer_all(rd, after, CA_XFER_GET);
582
+ z = (boolean8_t *) held->ptr;
583
+ for ( i = 0; i < n; i++ ) {
584
+ if ( after[i] && ! before[i] ) {
585
+ z[i] = 1;
586
+ }
587
+ }
588
+ ALLOCV_END(holder);
589
+ RB_GC_GUARD(reader);
590
+ }
591
+
534
592
  void
535
593
  ca_mask_standin_settle (CArray *parent, CArray *standin)
536
594
  {
537
595
  volatile VALUE holder = 0;
538
- boolean8_t *z, one = 1;
596
+ boolean8_t *z;
539
597
  ca_size_t *addrs;
540
598
  ca_size_t i, m = 0, n;
541
599
 
@@ -561,7 +619,7 @@ ca_mask_standin_settle (CArray *parent, CArray *standin)
561
619
  }
562
620
  MEMZERO(z, boolean8_t, n);
563
621
  ca_create_mask(parent);
564
- ca_fill_addrs(parent->mask, m, addrs, &one);
622
+ ca_mask_fill_past_buffers(parent->mask, parent, m, addrs);
565
623
  ALLOCV_END(holder);
566
624
  }
567
625
 
@@ -628,20 +686,52 @@ ca_multi_parent_mask_adopt (CArray *mask, CArray **parents,
628
686
  CAVIEW(mask)->parent = mp->parents[0];
629
687
  }
630
688
 
689
+ typedef struct {
690
+ CArray *parent;
691
+ CArray *standin;
692
+ } ca_standin_settle_arg;
693
+
694
+ static VALUE
695
+ ca_standin_settle_body (VALUE arg)
696
+ {
697
+ ca_standin_settle_arg *a = (ca_standin_settle_arg *) arg;
698
+ ca_mask_standin_settle(a->parent, a->standin);
699
+ return Qnil;
700
+ }
701
+
702
+ /* Every part is settled, past a parent that refuses a mask, so no part keeps
703
+ a true in its stand-in; then the first exception is raised. */
631
704
  void
632
705
  ca_multi_parent_mask_settle (CArray *mask, CArray **parents,
633
706
  CArray **standins, int32_t n)
634
707
  {
635
708
  CAMultiParent *mp = (CAMultiParent *) mask;
709
+ volatile VALUE error = Qnil;
636
710
  int32_t k;
637
711
  if ( standins == NULL ) {
638
712
  return;
639
713
  }
640
714
  for ( k = 0; k < n; k++ ) {
641
715
  if ( standins[k] != NULL && mp->parents[k] == standins[k] ) {
642
- ca_mask_standin_settle(parents[k], standins[k]);
716
+ ca_standin_settle_arg a = { parents[k], standins[k] };
717
+ int tag = 0;
718
+ /* protect: goes on to the other parts; nothing to give back */
719
+ rb_protect(ca_standin_settle_body, (VALUE) &a, &tag);
720
+ if ( tag ) {
721
+ VALUE err = rb_errinfo();
722
+ if ( ! RTEST(rb_obj_is_kind_of(err, rb_eException)) ) {
723
+ rb_jump_tag(tag);
724
+ }
725
+ if ( NIL_P(error) ) {
726
+ error = err;
727
+ }
728
+ rb_set_errinfo(Qnil);
729
+ }
643
730
  }
644
731
  }
732
+ if ( ! NIL_P(error) ) {
733
+ rb_exc_raise(error);
734
+ }
645
735
  }
646
736
 
647
737
  void
@@ -1934,7 +2024,7 @@ rb_ca_inherit_mask_n (VALUE self, int n, VALUE *rothers)
1934
2024
  VALUE
1935
2025
  rb_ca_inherit_mask (VALUE self, int n, ...)
1936
2026
  {
1937
- VALUE other;
2027
+ volatile VALUE other;
1938
2028
  CArray **slist;
1939
2029
  volatile VALUE holder;
1940
2030
  CArray *ca, *cs;
@@ -2017,7 +2107,7 @@ rb_ca_inherit_mask_replace_n (VALUE self, int n, VALUE *rothers)
2017
2107
  VALUE
2018
2108
  rb_ca_inherit_mask_replace (VALUE self, int n, ...)
2019
2109
  {
2020
- VALUE other;
2110
+ volatile VALUE other;
2021
2111
  CArray **slist;
2022
2112
  volatile VALUE holder;
2023
2113
  CArray *ca, *cs;
@@ -2253,6 +2343,20 @@ ca_binop_bit_and_i_boolean8_fast (ca_size_t n, boolean8_t *m,
2253
2343
  }
2254
2344
 
2255
2345
  /* ----- bit_or_i (boolean8_t fast) ----- */
2346
+ /* Every cell gets 0 or 1, even beside a masked cell: its byte is not
2347
+ defined (any value), and a cell Kleene resolves to true keeps the value
2348
+ written here. The word path folds each byte's eight bits into its low
2349
+ bit; a shift by less than 8 moves bits of the next byte only into the
2350
+ high bits, which the last mask drops. */
2351
+ static inline uint64_t
2352
+ ca_mask_word_nonzero_bytes (uint64_t w)
2353
+ {
2354
+ w |= w >> 4;
2355
+ w |= w >> 2;
2356
+ w |= w >> 1;
2357
+ return w & CA_MASK_WORD_LSB;
2358
+ }
2359
+
2256
2360
  static void
2257
2361
  ca_binop_bit_or_i_boolean8_fast (ca_size_t n, boolean8_t *m,
2258
2362
  char *ptr1, ca_size_t i1,
@@ -2273,7 +2377,7 @@ ca_binop_bit_or_i_boolean8_fast (ca_size_t n, boolean8_t *m,
2273
2377
  p1 = q1 + k*i1;
2274
2378
  p2 = q2 + k*i2;
2275
2379
  p3 = q3 + k*i3;
2276
- (*p3) = (*p1) | (*p2);
2380
+ (*p3) = ( (*p1) | (*p2) ) ? 1 : 0;
2277
2381
  }
2278
2382
  }
2279
2383
  }
@@ -2284,18 +2388,21 @@ ca_binop_bit_or_i_boolean8_fast (ca_size_t n, boolean8_t *m,
2284
2388
  ca_size_t j;
2285
2389
  for (j = 0; j < nw; j++) {
2286
2390
  ca_mask_word_store(q3 + j*CA_MASK_WORD_BYTES,
2287
- ca_mask_word_load(q1 + j*CA_MASK_WORD_BYTES) | ca_mask_word_load(q2 + j*CA_MASK_WORD_BYTES));
2391
+ ca_mask_word_nonzero_bytes(
2392
+ ca_mask_word_load(q1 + j*CA_MASK_WORD_BYTES) |
2393
+ ca_mask_word_load(q2 + j*CA_MASK_WORD_BYTES)));
2288
2394
  }
2289
2395
  for (j = nw * CA_MASK_WORD_BYTES; j < n; j++) {
2290
- q3[j] = q1[j] | q2[j];
2396
+ q3[j] = ( q1[j] | q2[j] ) ? 1 : 0;
2291
2397
  }
2292
2398
  }
2293
2399
  else {
2400
+ /* non-contig view: byte loop */
2294
2401
  for (k = 0; k < n; k++) {
2295
2402
  p1 = q1 + k*i1;
2296
2403
  p2 = q2 + k*i2;
2297
2404
  p3 = q3 + k*i3;
2298
- (*p3) = (*p1) | (*p2);
2405
+ (*p3) = ( (*p1) | (*p2) ) ? 1 : 0;
2299
2406
  }
2300
2407
  }
2301
2408
  }
@@ -2407,8 +2514,8 @@ ca_inherit_mask_check_args (int argc, VALUE *argv, const char *name)
2407
2514
  for (i=0; i<RARRAY_LEN(rest); i++) {
2408
2515
  VALUE v = RARRAY_AREF(rest, i);
2409
2516
  if ( ! rb_obj_is_carray(v) ) {
2410
- rb_raise(rb_eTypeError, "%s: argument %ld must be a CArray (got %s)",
2411
- name, i, rb_obj_classname(v));
2517
+ rb_raise(rb_eTypeError, "%s: argument %ld must be a CArray (got %"PRIsVALUE")",
2518
+ name, i, rb_obj_class(v));
2412
2519
  }
2413
2520
  }
2414
2521
  return rest;
@@ -138,10 +138,11 @@ mp_keep_axis_full_f64 (VALUE self, double v, int is_undef)
138
138
  /* [shared] all-UNDEF reduced CA_FLOAT64 output for a zero-length reduction
139
139
  axis. An order statistic of no elements has no value, so every reduced
140
140
  cell is masked -- matching mean / min (which return UNDEF cells), not a
141
- raise. keep_axis controls whether the reduced axis is dropped or kept
142
- as a length-1 axis. */
141
+ raise, unless fill_value (nil for none) gives the cells a value, as it
142
+ does for a fiber with no present cell. keep_axis controls whether the
143
+ reduced axis is dropped or kept as a length-1 axis. */
143
144
  static VALUE
144
- mp_axis_all_masked (VALUE self, long axis, int keep_axis)
145
+ mp_axis_all_masked (VALUE self, long axis, VALUE fill_value, int keep_axis)
145
146
  {
146
147
  int8_t ax = (int8_t) axis;
147
148
  CArray *ca;
@@ -150,6 +151,10 @@ mp_axis_all_masked (VALUE self, long axis, int keep_axis)
150
151
  (ca->data_type == CA_OBJECT) ? CA_OBJECT
151
152
  : CA_FLOAT64,
152
153
  keep_axis);
154
+ if ( ! NIL_P(fill_value) ) {
155
+ rb_ca_store_all(out, fill_value);
156
+ return out;
157
+ }
153
158
  CArray *co;
154
159
  GetCArray(out, co);
155
160
  ca_create_mask(co);
@@ -680,7 +685,7 @@ percentile_object_axis (VALUE self, VALUE pers, long axis, VALUE method)
680
685
  is applied by the entry (rb_ca_percentile_m), so build reduced. */
681
686
  VALUE res = rb_ary_new_capa(npers);
682
687
  for ( long i = 0; i < npers; i++ )
683
- rb_ary_push(res, mp_axis_all_masked(self, axis, 0));
688
+ rb_ary_push(res, mp_axis_all_masked(self, axis, Qnil, 0));
684
689
  return res;
685
690
  }
686
691
 
@@ -774,7 +779,7 @@ percentile_object_axis_masked (VALUE self, VALUE pers, long axis, VALUE method,
774
779
  if ( M == 0 ) {
775
780
  VALUE res = rb_ary_new_capa(npers);
776
781
  for ( long i = 0; i < npers; i++ )
777
- rb_ary_push(res, mp_axis_all_masked(self, axis, keep_axis));
782
+ rb_ary_push(res, mp_axis_all_masked(self, axis, fill_value, keep_axis));
778
783
  return res;
779
784
  }
780
785
 
@@ -904,7 +909,7 @@ rb_ca_median_m_body (int argc, VALUE *argv, VALUE self)
904
909
  if ( !NIL_P(raxis) ) {
905
910
  long axis = rb_ca_normalize_axis_value(self, raxis, "median");
906
911
  long n = (long) ca->dim[axis];
907
- if ( n == 0 ) return mp_axis_all_masked(self, axis, keep_axis);
912
+ if ( n == 0 ) return mp_axis_all_masked(self, axis, fill_value, keep_axis);
908
913
  /* mask / min_count / fill_value require the per-fiber select (each fiber
909
914
  has its own n_present); the plain partition path assumes a uniform n.
910
915
  Switch ca_fast_path(CA_SC_MEDIAN_PARTITION): off, the per-fiber select. */
@@ -1138,7 +1143,7 @@ pct_axis (VALUE self, VALUE pers_ary, long axis, VALUE method, int keep_axis)
1138
1143
  /* zero-length axis: every requested p reduces to an all-UNDEF cell. */
1139
1144
  VALUE res = rb_ary_new_capa(npers);
1140
1145
  for ( long i = 0; i < npers; i++ )
1141
- rb_ary_push(res, mp_axis_all_masked(self, axis, keep_axis));
1146
+ rb_ary_push(res, mp_axis_all_masked(self, axis, Qnil, keep_axis));
1142
1147
  return res;
1143
1148
  }
1144
1149
 
@@ -1167,7 +1172,7 @@ pct_axis_masked (VALUE self, VALUE pers_ary, long axis, VALUE method,
1167
1172
  if ( M == 0 ) {
1168
1173
  VALUE res = rb_ary_new_capa(npers);
1169
1174
  for ( long i = 0; i < npers; i++ )
1170
- rb_ary_push(res, mp_axis_all_masked(self, axis, keep_axis));
1175
+ rb_ary_push(res, mp_axis_all_masked(self, axis, fill_value, keep_axis));
1171
1176
  return res;
1172
1177
  }
1173
1178
 
@@ -449,6 +449,22 @@ ca_mv_probe_data_type (VALUE obj)
449
449
  return data_type;
450
450
  }
451
451
 
452
+ /* True when format is a struct of several members that exactly describes an
453
+ item of item_size bytes. A consumer takes such items as raw bytes: the
454
+ format names no members to rebuild a record from. */
455
+ static bool
456
+ ca_mv_format_is_struct (const char *format, ssize_t item_size)
457
+ {
458
+ rb_memory_view_item_component_t *members = NULL;
459
+ size_t n_members = 0;
460
+ const char *err = NULL;
461
+ ssize_t size;
462
+ if ( format == NULL ) return false;
463
+ size = rb_memory_view_parse_item_format(format, &members, &n_members, &err);
464
+ if ( members != NULL ) xfree(members);
465
+ return size == item_size && n_members >= 2;
466
+ }
467
+
452
468
  /* Per-element byte width for consumer-side wrap/from CArray allocation.
453
469
  For numeric types it is `ca_sizeof[data_type]`; for CA_FIXLEN it is the
454
470
  MemoryView item_size, since fixlen has no compile-time element width. */
@@ -511,7 +527,7 @@ ca_mv_alloc_strides_contiguous (CArray *ca, ssize_t *shape, bool row_major)
511
527
 
512
528
  typedef struct {
513
529
  int depth; /* 0 = the view itself, 1 = parent, 2 = grand-, ... */
514
- const char *class_name; /* class name of the offending link */
530
+ VALUE klass; /* class of the offending link (nil: unknown) */
515
531
  const char *reason; /* short explanation */
516
532
  int face; /* refused because it is a Face: a copy is
517
533
  refused too, so no copy is suggested */
@@ -528,11 +544,10 @@ ca_mv_is_castride_family (CArray *ca)
528
544
  return ca_func[ca->obj_type].attach == ca_stride_func.attach;
529
545
  }
530
546
 
531
- static const char *
532
- ca_mv_class_name_of (CArray *ca)
547
+ static VALUE
548
+ ca_mv_class_of (CArray *ca)
533
549
  {
534
- VALUE klass = ca_class[ca->obj_type];
535
- return NIL_P(klass) ? "(unknown)" : rb_class2name(klass);
550
+ return ca_class[ca->obj_type];
536
551
  }
537
552
 
538
553
  static const char *
@@ -597,7 +612,7 @@ ca_mv_compose_to_entity (CArray *ca, ca_mv_reject_t *rej,
597
612
  if (ca_is_face(ca)) {
598
613
  if (! ca_face_memory_view_exportable(ca)) {
599
614
  rej->depth = 0;
600
- rej->class_name = ca_mv_class_name_of(ca);
615
+ rej->klass = ca_mv_class_of(ca);
601
616
  rej->reason = "a Face whose storage bytes do not carry its values "
602
617
  "without the Face; export its storage instead "
603
618
  "(e.g. .ticks, .codes)";
@@ -609,7 +624,7 @@ ca_mv_compose_to_entity (CArray *ca, ca_mv_reject_t *rej,
609
624
  if (ca_is_entity(ca)) {
610
625
  if (! ca_mv_is_core_entity(ca)) {
611
626
  rej->depth = 0;
612
- rej->class_name = ca_mv_class_name_of(ca);
627
+ rej->klass = ca_mv_class_of(ca);
613
628
  rej->reason = "installed outside the core; its bytes are not "
614
629
  "exported";
615
630
  return false;
@@ -620,7 +635,7 @@ ca_mv_compose_to_entity (CArray *ca, ca_mv_reject_t *rej,
620
635
  }
621
636
  if (!ca_mv_is_castride_family(ca)) {
622
637
  rej->depth = 0;
623
- rej->class_name = ca_mv_class_name_of(ca);
638
+ rej->klass = ca_mv_class_of(ca);
624
639
  rej->reason = ca_mv_reject_reason_for(ca);
625
640
  return false;
626
641
  }
@@ -628,7 +643,7 @@ ca_mv_compose_to_entity (CArray *ca, ca_mv_reject_t *rej,
628
643
  ca_stride_compose_to_root((CAStride *) ca, &root, out_strides, out_base);
629
644
  if (!ca_is_entity(root)) {
630
645
  rej->depth = -1;
631
- rej->class_name = ca_mv_class_name_of(root);
646
+ rej->klass = ca_mv_class_of(root);
632
647
  if (ca_mv_is_castride_family(root)) {
633
648
  rej->reason = "CAStride chain stride pattern does not compose "
634
649
  "linearly into entity (e.g. reshape across non-contig)";
@@ -639,7 +654,7 @@ ca_mv_compose_to_entity (CArray *ca, ca_mv_reject_t *rej,
639
654
  }
640
655
  if (! ca_mv_is_core_entity(root)) {
641
656
  rej->depth = -1;
642
- rej->class_name = ca_mv_class_name_of(root);
657
+ rej->klass = ca_mv_class_of(root);
643
658
  rej->reason = "installed outside the core; its bytes are not exported";
644
659
  return false;
645
660
  }
@@ -704,7 +719,7 @@ ca_mv_fixlen_format (ca_size_t bytes)
704
719
  {
705
720
  st_data_t found;
706
721
  char *fmt;
707
- rb_nativethread_lock_lock(&ca_mv_fixlen_formats_lock);
722
+ ca_native_lock_without_gvl(&ca_mv_fixlen_formats_lock);
708
723
  if ( st_lookup(ca_mv_fixlen_formats, (st_data_t) bytes, &found) ) {
709
724
  rb_nativethread_lock_unlock(&ca_mv_fixlen_formats_lock);
710
725
  return (const char *) found;
@@ -712,7 +727,8 @@ ca_mv_fixlen_format (ca_size_t bytes)
712
727
  rb_nativethread_lock_unlock(&ca_mv_fixlen_formats_lock);
713
728
  fmt = ALLOC_N(char, 24);
714
729
  snprintf(fmt, 24, "%llds", (long long) bytes);
715
- rb_nativethread_lock_lock(&ca_mv_fixlen_formats_lock);
730
+ /* lock: st_insert can run a GC, which does not take this lock */
731
+ ca_native_lock_without_gvl(&ca_mv_fixlen_formats_lock);
716
732
  if ( st_lookup(ca_mv_fixlen_formats, (st_data_t) bytes, &found) ) {
717
733
  xfree(fmt);
718
734
  fmt = (char *) found;
@@ -1171,6 +1187,9 @@ ca_mv_holder_take_data (ca_mv_imported_holder_t *h, VALUE src,
1171
1187
  }
1172
1188
 
1173
1189
  data_type = ca_mv_data_type_from_format(v->format, v->item_size);
1190
+ if ( data_type < 0 && ca_mv_format_is_struct(v->format, v->item_size) ) {
1191
+ data_type = CA_FIXLEN;
1192
+ }
1174
1193
  if ( data_type < 0 ) {
1175
1194
  rb_raise(rb_eArgError, "unsupported MemoryView format: %s (item_size=%ld)",
1176
1195
  v->format, (long) v->item_size);
@@ -1344,10 +1363,10 @@ ca_mv_wrap_body (VALUE arg)
1344
1363
  for a class would get something that is not it. */
1345
1364
  if ( a->target_class != rb_cCAWrap && ! d->contiguous ) {
1346
1365
  rb_raise(rb_eArgError,
1347
- "%s was requested, but this producer is strided, so the result "
1366
+ "%"PRIsVALUE" was requested, but this producer is strided, so the result "
1348
1367
  "would be a CAStride; pass a row-major contiguous producer, or "
1349
1368
  "use CArray.from_memory_view for a copy",
1350
- rb_class2name(a->target_class));
1369
+ a->target_class);
1351
1370
  }
1352
1371
 
1353
1372
  if ( m != NULL ) {
@@ -1503,8 +1522,8 @@ rb_ca_s_wrap_memory_view (int argc, VALUE *argv, VALUE klass)
1503
1522
  target_class = ( klass == rb_cCArray ) ? rb_cCAWrap : klass;
1504
1523
  if ( ! RTEST(rb_class_inherited_p(target_class, rb_cCAWrap)) ) {
1505
1524
  rb_raise(rb_eTypeError,
1506
- "wrap_memory_view builds a CAWrap; %s is not a subclass of it",
1507
- rb_class2name(target_class));
1525
+ "wrap_memory_view builds a CAWrap; %"PRIsVALUE" is not a subclass of it",
1526
+ target_class);
1508
1527
  }
1509
1528
  return ca_mv_wrap(obj, mask_obj, target_class, explicit_data_type, -1, 0);
1510
1529
  }
@@ -1585,8 +1604,8 @@ rb_ca_s_from_memory_view (int argc, VALUE *argv, VALUE klass)
1585
1604
  if ( klass != rb_cCArray &&
1586
1605
  ! RTEST(rb_class_inherited_p(klass, rb_cCAWrap)) ) {
1587
1606
  rb_raise(rb_eTypeError,
1588
- "from_memory_view builds a CArray; %s cannot be returned from it",
1589
- rb_class2name(klass));
1607
+ "from_memory_view builds a CArray; %"PRIsVALUE" cannot be returned from it",
1608
+ klass);
1590
1609
  }
1591
1610
  ca_mv_import_args_init(&a, obj, mask_obj);
1592
1611
  a.explicit_data_type = explicit_data_type;
@@ -1642,12 +1661,12 @@ rb_ca_s_memory_view_reject_reason (VALUE klass, VALUE obj)
1642
1661
  else if (rej.depth == 1) where = "parent";
1643
1662
  else where = NULL; /* ancestor[N] */
1644
1663
  if (where) {
1645
- return rb_sprintf("%s is %s (%s); zero-copy wrap not possible.%s",
1646
- where, rej.class_name, rej.reason,
1664
+ return rb_sprintf("%s is %"PRIsVALUE" (%s); zero-copy wrap not possible.%s",
1665
+ where, NIL_P(rej.klass) ? rb_str_new_cstr("(unknown)") : rej.klass, rej.reason,
1647
1666
  rej.face ? "" : " Export arr.copy for a snapshot.");
1648
1667
  } else {
1649
- return rb_sprintf("ancestor[%d] is %s (%s); zero-copy wrap not possible.%s",
1650
- rej.depth - 1, rej.class_name, rej.reason,
1668
+ return rb_sprintf("ancestor[%d] is %"PRIsVALUE" (%s); zero-copy wrap not possible.%s",
1669
+ rej.depth - 1, NIL_P(rej.klass) ? rb_str_new_cstr("(unknown)") : rej.klass, rej.reason,
1651
1670
  rej.face ? "" : " Export arr.copy for a snapshot.");
1652
1671
  }
1653
1672
  }
@@ -1272,7 +1272,7 @@ VALUE
1272
1272
  rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
1273
1273
  ca_binop_func_t func[])
1274
1274
  {
1275
- volatile VALUE out;
1275
+ volatile VALUE out, receiver;
1276
1276
  CArray *ca1, *ca2, *ca3; /* ca3 = ca1.op(ca2) */
1277
1277
  int self_is_scalar, other_is_scalar;
1278
1278
  ca_size_t n_kernel;
@@ -1282,12 +1282,43 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
1282
1282
  ca_binop_read_boolean_as_truth(func[CA_BOOLEAN] != ca_binop_not_implement,
1283
1283
  &self, &other);
1284
1284
 
1285
+ receiver = rb_obj_is_carray(other) ? Qnil : self;
1286
+
1287
+ /* pmax and its kin pick by order, so a value the array can not hold is
1288
+ refused as an ordering refuses it. */
1289
+ if ( ! rb_obj_is_carray(other) && rb_obj_is_carray(self) &&
1290
+ ca_binop_picks_by_order(ca_binop_op_id_of_table(func)) ) {
1291
+ CArray *cs;
1292
+ GetCArray(self, cs);
1293
+ /* a boolean array picks as its 0 / 1 int64 numbers */
1294
+ if ( func[cs->data_type == CA_BOOLEAN ? CA_INT64 : cs->data_type]
1295
+ != ca_binop_not_implement ) {
1296
+ ca_refuse_foreign_order(other, self);
1297
+ }
1298
+ else {
1299
+ /* an array the op is not defined for says so, whatever the operand */
1300
+ ca_op_refuse_unimplemented(self, cs->data_type, rb_eCADataTypeError);
1301
+ }
1302
+ }
1303
+
1285
1304
  /* do implicit casting */
1286
1305
  rb_ca_cast_self_or_other(&self, &other);
1287
1306
 
1288
1307
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
1289
1308
  TypedData_Get_Struct(other, CArray, &carray_data_type, ca2);
1290
1309
 
1310
+ /* A Face whose cells are not Ruby values does not go down to an object
1311
+ array to meet a single Ruby object: an operator it does not define for
1312
+ an array of its own stays undefined. */
1313
+ if ( ca1->data_type == CA_OBJECT && rb_obj_is_carray(receiver) ) {
1314
+ CArray *ca0;
1315
+ TypedData_Get_Struct(receiver, CArray, &carray_data_type, ca0);
1316
+ if ( ca_is_face(ca0) && ca0->data_type == CA_FIXLEN ) {
1317
+ ca_op_refuse_unimplemented(receiver, ca0->data_type,
1318
+ rb_eCADataTypeError);
1319
+ }
1320
+ }
1321
+
1291
1322
  /* Boolean-as-numeric promotion: an arithmetic op (+ - * / % ** ...) has
1292
1323
  no boolean kernel, so its func[CA_BOOLEAN] slot is ca_binop_not_implement.
1293
1324
  When both operands promoted to CA_BOOLEAN (= bool op bool), coerce them
@@ -1853,16 +1884,8 @@ rb_ca_call_triop (VALUE self, VALUE other2, VALUE other3,
1853
1884
  volatile VALUE out;
1854
1885
  CArray *ca1, *ca2, *ca3, *cao;
1855
1886
 
1856
- /* Pairwise data_type promotion: ((self, other2) -> common), then
1857
- ((self', other3) -> common). Mirrors how the binop driver normalises
1858
- two operands; for triop we apply it twice. After this, all three
1859
- CArrays share the same data_type (and unbound-repeats are resolved). */
1860
- rb_ca_cast_self_or_other(&self, &other2);
1861
- rb_ca_cast_self_or_other(&self, &other3);
1862
- rb_ca_cast_self_or_other(&other2, &other3);
1863
- /* one more pass to re-normalise self vs other2 in case the
1864
- other2/other3 cast widened other2 above self's data_type */
1865
- rb_ca_cast_self_or_other(&self, &other2);
1887
+ /* After this, all three CArrays share the same data_type. */
1888
+ rb_ca_cast_three(&self, &other2, &other3);
1866
1889
 
1867
1890
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
1868
1891
  TypedData_Get_Struct(other2, CArray, &carray_data_type, ca2);
@@ -2426,6 +2449,23 @@ ca_bincmp_mixed_sign (VALUE self, VALUE other, ca_bincmp_func_t func[],
2426
2449
  return out;
2427
2450
  }
2428
2451
 
2452
+ /* The answer of eq (ne = 0) or ne (ne = 1) when no cell can match: false
2453
+ (true for ne) in every cell, masked where `self` is masked. */
2454
+ VALUE
2455
+ ca_bincmp_no_match (VALUE self, int ne)
2456
+ {
2457
+ volatile VALUE out;
2458
+ CArray *ca, *co;
2459
+ GetCArray(self, ca);
2460
+ out = RTEST(rb_obj_is_cscalar(self))
2461
+ ? rb_cscalar_new(CA_BOOLEAN, 0, NULL)
2462
+ : rb_carray_new(CA_BOOLEAN, ca->ndim, ca->dim, 0, NULL);
2463
+ TypedData_Get_Struct(out, CArray, &carray_data_type, co);
2464
+ ca_mask_overlay_safe(co, 1, ca);
2465
+ memset(co->ptr, ne ? 1 : 0, co->elements * co->bytes);
2466
+ return out;
2467
+ }
2468
+
2429
2469
  VALUE
2430
2470
  rb_ca_call_bincmp (volatile VALUE self, volatile VALUE other,
2431
2471
  ca_bincmp_func_t func[],
@@ -2447,6 +2487,27 @@ rb_ca_call_bincmp (volatile VALUE self, volatile VALUE other,
2447
2487
  }
2448
2488
  }
2449
2489
 
2490
+ /* eq and ne with a value the array can not hold (nil, a String): no cell
2491
+ matches. */
2492
+ if ( (ca_bincmp_func_t) func == (ca_bincmp_func_t) ca_bincmp_eq
2493
+ || (ca_bincmp_func_t) func == (ca_bincmp_func_t) ca_bincmp_ne ) {
2494
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
2495
+ if ( ca_query_matches_nothing(ca1, other) ) {
2496
+ return ca_bincmp_no_match(self,
2497
+ (ca_bincmp_func_t) func == (ca_bincmp_func_t) ca_bincmp_ne);
2498
+ }
2499
+ }
2500
+
2501
+ /* An ordering with a value the array can not hold is refused as Float#<
2502
+ refuses it. An array that has no ordering at all (complex) keeps its
2503
+ own refusal below. */
2504
+ if ( ! rb_obj_is_carray(other) && ca_bincmp_op_id_of(func) <= CA_BINCMP_GE ) {
2505
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
2506
+ if ( func[ca1->data_type] != ca_bincmp_not_implement ) {
2507
+ ca_refuse_foreign_order(other, self);
2508
+ }
2509
+ }
2510
+
2450
2511
  /* Face gate: an ORDERABLE Face over numeric storage descends to storage
2451
2512
  (fixing the surface-fixlen memcmp mis-order) and reconciles a Face RHS
2452
2513
  via to_comparable (e.g. unit alignment). No-op for non-Face self and