carray 3.0.4 → 3.0.6

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 (155) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +438 -0
  3. data/README.md +1 -1
  4. data/carray.gemspec +3 -2
  5. data/ext/ca_array_pool.c +25 -1
  6. data/ext/ca_axis_dispatch.c +19 -20
  7. data/ext/ca_axis_group.c +1 -1
  8. data/ext/ca_categorical_iterator.c +34 -27
  9. data/ext/ca_compare.c +4 -4
  10. data/ext/ca_composite_dispatch.c +9 -9
  11. data/ext/ca_kernel_iterator.c +122 -237
  12. data/ext/ca_kernel_iterator.h +522 -337
  13. data/ext/ca_obj_array.c +69 -19
  14. data/ext/ca_obj_bincmp.c +10 -1
  15. data/ext/ca_obj_binop.c +11 -1
  16. data/ext/ca_obj_bitarray.c +5 -1
  17. data/ext/ca_obj_bitfield.c +60 -43
  18. data/ext/ca_obj_byte_swap.c +4 -1
  19. data/ext/ca_obj_const_string.c +15 -3
  20. data/ext/ca_obj_face.c +44 -1
  21. data/ext/ca_obj_fake.c +3 -1
  22. data/ext/ca_obj_field.c +13 -3
  23. data/ext/ca_obj_grid.c +219 -17
  24. data/ext/ca_obj_meld.c +69 -113
  25. data/ext/ca_obj_moncmp.c +9 -1
  26. data/ext/ca_obj_monop.c +86 -12
  27. data/ext/ca_obj_object.c +118 -32
  28. data/ext/ca_obj_record.c +3 -0
  29. data/ext/ca_obj_reduce.c +19 -1
  30. data/ext/ca_obj_refer.c +80 -26
  31. data/ext/ca_obj_remap.c +69 -10
  32. data/ext/ca_obj_roll.c +27 -6
  33. data/ext/ca_obj_select.c +38 -6
  34. data/ext/ca_obj_select_axis.c +1 -0
  35. data/ext/ca_obj_shift.c +32 -7
  36. data/ext/ca_obj_stack.c +311 -111
  37. data/ext/ca_obj_stride.c +159 -64
  38. data/ext/ca_obj_string.c +27 -2
  39. data/ext/ca_obj_tile.c +28 -7
  40. data/ext/ca_obj_transpose.c +8 -4
  41. data/ext/ca_obj_triop.c +12 -1
  42. data/ext/ca_obj_window.c +80 -50
  43. data/ext/ca_op_ipower.c +12 -12
  44. data/ext/ca_sort_kernels.h +6 -3
  45. data/ext/caframe_csv_reader.c +1 -1
  46. data/ext/carray.h +35 -1
  47. data/ext/carray_access.c +43 -67
  48. data/ext/carray_address_basis.c +134 -0
  49. data/ext/carray_attribute.c +32 -0
  50. data/ext/carray_broadcast.c +3 -4
  51. data/ext/carray_build_flags.h +1 -1
  52. data/ext/carray_cast.c +251 -93
  53. data/ext/carray_cast_func.rb +56 -26
  54. data/ext/carray_conversion.c +11 -7
  55. data/ext/carray_core.c +814 -72
  56. data/ext/carray_count.c +6 -0
  57. data/ext/carray_element.c +22 -13
  58. data/ext/carray_factorize.c +20 -20
  59. data/ext/carray_generate.c +8 -2
  60. data/ext/carray_histogram.c +26 -4
  61. data/ext/carray_index_classifier.c +4 -4
  62. data/ext/carray_internal.h +159 -12
  63. data/ext/carray_kernels_bincmp.c +2174 -170
  64. data/ext/carray_kernels_binop.c +25653 -5487
  65. data/ext/carray_kernels_init.c +8 -8
  66. data/ext/carray_kernels_moncmp.c +96 -96
  67. data/ext/carray_kernels_monop.c +12253 -5235
  68. data/ext/carray_kernels_reduce_aggregate.c +6150 -2423
  69. data/ext/carray_kernels_reduce_boolean.c +1533 -15
  70. data/ext/carray_kernels_reduce_cumulative.c +2442 -882
  71. data/ext/carray_kernels_reduce_extreme.c +5658 -1661
  72. data/ext/carray_kernels_reduce_variance.c +2534 -402
  73. data/ext/carray_kernels_scan.c +368 -368
  74. data/ext/carray_kernels_search.c +4551 -604
  75. data/ext/carray_kernels_sort.c +659 -751
  76. data/ext/carray_kernels_triop.c +114 -114
  77. data/ext/carray_key_windows.c +4 -0
  78. data/ext/carray_lazy.c +47 -0
  79. data/ext/carray_loop.c +1 -1
  80. data/ext/carray_mask.c +175 -161
  81. data/ext/carray_median_percentile.c +4 -4
  82. data/ext/carray_memory_view.c +15 -9
  83. data/ext/carray_operator.c +152 -49
  84. data/ext/carray_order.c +230 -10
  85. data/ext/carray_random.c +22 -19
  86. data/ext/carray_scatter.c +24 -7
  87. data/ext/carray_slab.c +128 -29
  88. data/ext/carray_sort.c +224 -16
  89. data/ext/carray_sort_kernel.c +5 -7
  90. data/ext/carray_struct.c +35 -43
  91. data/ext/carray_test.c +74 -19
  92. data/ext/carray_time_text.c +6 -4
  93. data/ext/carray_utils.c +63 -21
  94. data/ext/extconf.rb +11 -0
  95. data/ext/mkkernel.rb +546 -199
  96. data/ext/ruby_carray.c +4 -1
  97. data/ext/version.h +4 -4
  98. data/lib/carray/attribute.rb +42 -5
  99. data/lib/carray/autoload_carray.rb +3 -3
  100. data/lib/carray/autoload_method_extension.rb +5 -3
  101. data/lib/carray/axis_group.rb +13 -7
  102. data/lib/carray/basics.rb +28 -8
  103. data/lib/carray/block_iterator.rb +17 -3
  104. data/lib/carray/categorical.rb +35 -23
  105. data/lib/carray/categorical_iterator.rb +8 -8
  106. data/lib/carray/complex.rb +2 -2
  107. data/lib/carray/conditional.rb +3 -2
  108. data/lib/carray/const_string.rb +10 -0
  109. data/lib/carray/data_type_extension.rb +8 -1
  110. data/lib/carray/fixlen_string.rb +41 -2
  111. data/lib/carray/frame/column_verbs.rb +48 -16
  112. data/lib/carray/frame/concat.rb +17 -3
  113. data/lib/carray/frame/convert.rb +19 -0
  114. data/lib/carray/frame/csv_parser.rb +32 -32
  115. data/lib/carray/frame/describe.rb +4 -2
  116. data/lib/carray/frame/frame.rb +405 -85
  117. data/lib/carray/frame/group.rb +777 -64
  118. data/lib/carray/frame/io.rb +212 -72
  119. data/lib/carray/frame/join.rb +146 -37
  120. data/lib/carray/frame/records.rb +3 -3
  121. data/lib/carray/frame/reshape.rb +89 -11
  122. data/lib/carray/frame/sort.rb +57 -9
  123. data/lib/carray/frame/verbs.rb +384 -49
  124. data/lib/carray/frame/windows.rb +44 -6
  125. data/lib/carray/fuse_source.rb +31 -2
  126. data/lib/carray/inspect.rb +7 -2
  127. data/lib/carray/iterator.rb +32 -0
  128. data/lib/carray/lazy.rb +52 -8
  129. data/lib/carray/mask_gap_fill.rb +37 -17
  130. data/lib/carray/meld_reduce.rb +1 -1
  131. data/lib/carray/methods/choose.rb +3 -2
  132. data/lib/carray/methods/is_in.rb +8 -1
  133. data/lib/carray/methods/meshgrid.rb +3 -1
  134. data/lib/carray/runtime.rb +47 -8
  135. data/lib/carray/segment_iterator.rb +35 -16
  136. data/lib/carray/serialize.rb +53 -19
  137. data/lib/carray/stack.rb +33 -18
  138. data/lib/carray/string.rb +8 -0
  139. data/lib/carray/string_operation_extension.rb +13 -5
  140. data/lib/carray/struct_builder.rb +5 -3
  141. data/lib/carray/time.rb +112 -68
  142. data/lib/carray/window_iterator.rb +16 -4
  143. data/yard-stubs/ca_obj_array.rb +26 -1
  144. data/yard-stubs/ca_obj_transpose.rb +5 -4
  145. data/yard-stubs/carray_attribute.rb +3 -1
  146. data/yard-stubs/carray_cast.rb +10 -5
  147. data/yard-stubs/carray_conversion.rb +13 -3
  148. data/yard-stubs/carray_element.rb +7 -6
  149. data/yard-stubs/carray_mask.rb +28 -4
  150. data/yard-stubs/carray_math.rb +45 -20
  151. data/yard-stubs/carray_memory_view.rb +7 -0
  152. data/yard-stubs/carray_order.rb +100 -4
  153. data/yard-stubs/carray_scatter.rb +7 -5
  154. data/yard-stubs/carray_test.rb +7 -4
  155. metadata +18 -4
data/ext/carray_mask.c CHANGED
@@ -13,6 +13,7 @@
13
13
  #include "carray.h"
14
14
  #include "carray_internal.h" /* ca_lazy_arena_* */
15
15
  #include "ca_obj_face.h" /* CA_FACE_LIFT_IF_FACE for the mask-lift path */
16
+ #include <ruby/ractor.h>
16
17
 
17
18
  /* ============================================================================
18
19
  * Word-bulk mask substrate helpers.
@@ -37,16 +38,32 @@
37
38
  #define CA_MASK_WORD_BYTES 8
38
39
  #define CA_MASK_WORD_LSB 0x0101010101010101ULL
39
40
 
41
+ /* A mask may start at any byte (a window or a block view offsets it), so a
42
+ word is read and written through memcpy, never through a uint64_t
43
+ pointer; the compiler still lowers each to a single load or store. */
44
+ static inline uint64_t
45
+ ca_mask_word_load (const boolean8_t *p)
46
+ {
47
+ uint64_t w;
48
+ memcpy(&w, p, sizeof(w));
49
+ return w;
50
+ }
51
+
52
+ static inline void
53
+ ca_mask_word_store (boolean8_t *p, uint64_t w)
54
+ {
55
+ memcpy(p, &w, sizeof(w));
56
+ }
57
+
40
58
  /* ma[i] |= ms[i] for i in [0, n) -- used by ca_copy_mask_overlay_n */
41
59
  static inline void
42
60
  ca_mask_word_or (boolean8_t *ma, const boolean8_t *ms, ca_size_t n)
43
61
  {
44
62
  ca_size_t nw = n / CA_MASK_WORD_BYTES;
45
63
  ca_size_t i;
46
- uint64_t *wa = (uint64_t *) ma;
47
- const uint64_t *ws = (const uint64_t *) ms;
48
64
  for (i = 0; i < nw; i++) {
49
- wa[i] |= ws[i];
65
+ ca_mask_word_store(ma + i*CA_MASK_WORD_BYTES,
66
+ ca_mask_word_load(ma + i*CA_MASK_WORD_BYTES) | ca_mask_word_load(ms + i*CA_MASK_WORD_BYTES));
50
67
  }
51
68
  for (i = nw * CA_MASK_WORD_BYTES; i < n; i++) {
52
69
  ma[i] |= ms[i];
@@ -62,11 +79,9 @@ ca_mask_word_and (boolean8_t *md,
62
79
  {
63
80
  ca_size_t nw = n / CA_MASK_WORD_BYTES;
64
81
  ca_size_t i;
65
- uint64_t *wd = (uint64_t *) md;
66
- const uint64_t *wa = (const uint64_t *) ma;
67
- const uint64_t *wb = (const uint64_t *) mb;
68
82
  for (i = 0; i < nw; i++) {
69
- wd[i] = wa[i] & wb[i];
83
+ ca_mask_word_store(md + i*CA_MASK_WORD_BYTES,
84
+ ca_mask_word_load(ma + i*CA_MASK_WORD_BYTES) & ca_mask_word_load(mb + i*CA_MASK_WORD_BYTES));
70
85
  }
71
86
  for (i = nw * CA_MASK_WORD_BYTES; i < n; i++) {
72
87
  md[i] = ma[i] & mb[i];
@@ -82,11 +97,9 @@ ca_mask_word_xor (boolean8_t *md,
82
97
  {
83
98
  ca_size_t nw = n / CA_MASK_WORD_BYTES;
84
99
  ca_size_t i;
85
- uint64_t *wd = (uint64_t *) md;
86
- const uint64_t *wa = (const uint64_t *) ma;
87
- const uint64_t *wb = (const uint64_t *) mb;
88
100
  for (i = 0; i < nw; i++) {
89
- wd[i] = wa[i] ^ wb[i];
101
+ ca_mask_word_store(md + i*CA_MASK_WORD_BYTES,
102
+ ca_mask_word_load(ma + i*CA_MASK_WORD_BYTES) ^ ca_mask_word_load(mb + i*CA_MASK_WORD_BYTES));
90
103
  }
91
104
  for (i = nw * CA_MASK_WORD_BYTES; i < n; i++) {
92
105
  md[i] = ma[i] ^ mb[i];
@@ -99,10 +112,8 @@ ca_mask_word_not (boolean8_t *md, const boolean8_t *ms, ca_size_t n)
99
112
  {
100
113
  ca_size_t nw = n / CA_MASK_WORD_BYTES;
101
114
  ca_size_t i;
102
- uint64_t *wd = (uint64_t *) md;
103
- const uint64_t *ws = (const uint64_t *) ms;
104
115
  for (i = 0; i < nw; i++) {
105
- wd[i] = ws[i] ^ CA_MASK_WORD_LSB;
116
+ ca_mask_word_store(md + i*CA_MASK_WORD_BYTES, ca_mask_word_load(ms + i*CA_MASK_WORD_BYTES) ^ CA_MASK_WORD_LSB);
106
117
  }
107
118
  for (i = nw * CA_MASK_WORD_BYTES; i < n; i++) {
108
119
  md[i] = ms[i] ^ (boolean8_t) 1;
@@ -116,9 +127,8 @@ ca_mask_word_any (const boolean8_t *m, ca_size_t n)
116
127
  {
117
128
  ca_size_t nw = n / CA_MASK_WORD_BYTES;
118
129
  ca_size_t i;
119
- const uint64_t *w = (const uint64_t *) m;
120
130
  for (i = 0; i < nw; i++) {
121
- if ( w[i] != 0 ) return 1;
131
+ if ( ca_mask_word_load(m + i*CA_MASK_WORD_BYTES) != 0 ) return 1;
122
132
  }
123
133
  for (i = nw * CA_MASK_WORD_BYTES; i < n; i++) {
124
134
  if ( m[i] ) return 1;
@@ -134,9 +144,8 @@ ca_mask_word_all_set (const boolean8_t *m, ca_size_t n)
134
144
  {
135
145
  ca_size_t nw = n / CA_MASK_WORD_BYTES;
136
146
  ca_size_t i;
137
- const uint64_t *w = (const uint64_t *) m;
138
147
  for (i = 0; i < nw; i++) {
139
- if ( w[i] != CA_MASK_WORD_LSB ) return 0;
148
+ if ( ca_mask_word_load(m + i*CA_MASK_WORD_BYTES) != CA_MASK_WORD_LSB ) return 0;
140
149
  }
141
150
  for (i = nw * CA_MASK_WORD_BYTES; i < n; i++) {
142
151
  if ( ! m[i] ) return 0;
@@ -293,7 +302,13 @@ ca_has_mask (void *ap)
293
302
  if ( ca->mask ) { /* mask array already created */
294
303
  return 1;
295
304
  }
296
- else if ( ca_is_value_array(ca) ) {
305
+ else if ( ca_test_flag(ca, CA_FLAG_VALUE_ARRAY) ) {
306
+ /* Only this array's own mark, not ca_is_value_array, which walks to
307
+ the root and would make every level of a view pay again for the
308
+ levels above it. A view below a value array still answers 0: it
309
+ asks its parent, and the question reaches the marked array. No
310
+ level in between has a mask, since ca_create_mask refuses one for
311
+ anything below a value array. */
297
312
  return 0; /* array itself is returned by CArray#value */
298
313
  }
299
314
  else if ( ca_is_entity(ca) ) { /* entity array */
@@ -308,6 +323,11 @@ ca_has_mask (void *ap)
308
323
  }
309
324
  return 0;
310
325
  }
326
+ /* The one level that answers without asking its parent: a value
327
+ array above it has to be looked for here. */
328
+ if ( ca_is_value_array(ca) ) {
329
+ return 0;
330
+ }
311
331
  if ( rb_obj_respond_to(co->self, rb_intern("mask_created?"), Qtrue) ) {
312
332
  return RTEST(rb_funcall(co->self, rb_intern("mask_created?"), 0));
313
333
  }
@@ -404,6 +424,45 @@ ca_view_func_create_mask (void *ap)
404
424
  CA_BOOLEAN, ca->ndim, ca->dim, 0, 0);
405
425
  }
406
426
 
427
+ /* The sync slot of a view whose mask came from ca_view_func_create_mask
428
+ calls this after syncing its parent. The view's mask is a refer over
429
+ the parent's mask, and when the parent's mask is itself a view, both
430
+ hold a buffer while the window is open; the refer writes past the
431
+ parent's buffer to the root. Syncing the parent then writes its
432
+ buffer, from before the view's mask was written, back over the root,
433
+ so the view's mask is synced once more. */
434
+
435
+ /* The sync slot of a view that maps its cells one to one onto its parent's
436
+ (a fake, a cast, a byte swap, a Face) calls this before syncing its
437
+ parent. The view's mask may have been written past the parent's mask
438
+ buffer, straight to the root (by a view over it), so the parent's
439
+ buffer can still hold the bits from before. The parent's own sync
440
+ decides from that buffer which cells it sends back, so the view's bits
441
+ are copied into it first. */
442
+
443
+ void
444
+ ca_view_pass_mask_to_parent (void *ap)
445
+ {
446
+ CAView *ca = (CAView *) ap;
447
+ if ( ca->mask != NULL && ca->mask->ptr != NULL
448
+ && ca->parent->mask != NULL && ca->parent->mask->ptr != NULL
449
+ && ca->mask->ptr != ca->parent->mask->ptr
450
+ && ca->elements == ca->parent->elements ) {
451
+ memmove(ca->parent->mask->ptr, ca->mask->ptr, (size_t) ca->elements);
452
+ }
453
+ }
454
+
455
+ void
456
+ ca_view_sync_mask_after_parent (void *ap)
457
+ {
458
+ CAView *ca = (CAView *) ap;
459
+ if ( ca->mask != NULL && ca->mask->ptr != NULL
460
+ && ca->parent->mask != NULL && ca->parent->mask->ptr != NULL
461
+ && ca_is_view(ca->parent->mask) ) {
462
+ ca_sync(ca->mask);
463
+ }
464
+ }
465
+
407
466
  /* create mask array if array has mask but has not mask array */
408
467
 
409
468
  void
@@ -433,20 +492,27 @@ ca_mask_standin_new (CArray *parent)
433
492
  return z;
434
493
  }
435
494
 
436
- /* A value array or a mask array: a parent that can never be given a mask,
437
- whose stand-in therefore stays in place for good. */
438
- static int
439
- ca_mask_standin_parent_unmaskable (CArray *parent)
495
+ /* A stack or meld refuses a value array or a mask array as a part: such an
496
+ array can have no mask, and a part of a stack or meld is an array like
497
+ any other, which can. */
498
+ void
499
+ ca_multi_parent_check_part (CArray *part, const char *name)
440
500
  {
441
- return ca_is_value_array(parent) || ca_is_mask_array(parent);
501
+ if ( ca_is_value_array(part) ) {
502
+ rb_raise(rb_eArgError,
503
+ "%s: a value array can have no mask and can not be a part", name);
504
+ }
505
+ if ( ca_is_mask_array(part) ) {
506
+ rb_raise(rb_eArgError,
507
+ "%s: a mask array can have no mask and can not be a part", name);
508
+ }
442
509
  }
443
510
 
444
511
  CArray *
445
512
  ca_mask_standin_current (CArray *mask_view, CArray *reading, CArray *parent,
446
513
  CArray *standin)
447
514
  {
448
- if ( standin == NULL || reading != standin
449
- || ca_mask_standin_parent_unmaskable(parent) ) {
515
+ if ( standin == NULL || reading != standin ) {
450
516
  return reading;
451
517
  }
452
518
  /* Not inside an attach window whose buffer is out: that buffer and the
@@ -482,23 +548,21 @@ ca_mask_standin_settle (CArray *parent, CArray *standin)
482
548
  if ( i == n ) {
483
549
  return;
484
550
  }
485
- if ( ca_mask_standin_parent_unmaskable(parent) ) {
486
- MEMZERO(z, boolean8_t, n);
487
- rb_raise(rb_eTypeError, "can not create mask array for the %s array",
488
- ca_is_value_array(parent) ? "value" : "mask");
489
- }
490
- ca_create_mask(parent);
491
551
  /* The trues are added to the parent's mask, not copied over it: the
492
- parent may have come to have masked cells of its own meanwhile. */
552
+ parent may have come to have masked cells of its own meanwhile. The
553
+ stand-in is cleared before the parent is asked for a mask, so a
554
+ parent that refuses one leaves it all false, as its readers take it
555
+ to be at rest. */
493
556
  addrs = ALLOCV_N(ca_size_t, holder, n);
494
557
  for ( ; i < n; i++ ) {
495
558
  if ( z[i] ) {
496
559
  addrs[m++] = i;
497
560
  }
498
561
  }
562
+ MEMZERO(z, boolean8_t, n);
563
+ ca_create_mask(parent);
499
564
  ca_fill_addrs(parent->mask, m, addrs, &one);
500
565
  ALLOCV_END(holder);
501
- MEMZERO(z, boolean8_t, n);
502
566
  }
503
567
 
504
568
  CArray *
@@ -531,114 +595,37 @@ ca_multi_parent_mask_refresh (CArray *mask, CArray **parents,
531
595
  CAVIEW(mask)->parent = mp->parents[0];
532
596
  }
533
597
 
534
- typedef struct {
535
- CArray *mask;
536
- CArray **blanks;
537
- CArray **saved;
538
- CArray *saved_parent;
539
- int32_t n;
540
- int finished;
541
- VALUE (*body)(VALUE);
542
- VALUE arg;
543
- } ca_mp_trial_t;
544
-
545
- static VALUE
546
- ca_mp_trial_body (VALUE p)
547
- {
548
- ca_mp_trial_t *t = (ca_mp_trial_t *) p;
549
- t->body(t->arg);
550
- t->finished = 1;
551
- return Qnil;
552
- }
553
-
554
- static void
555
- ca_mp_trial_free_blanks (ca_mp_trial_t *t)
556
- {
557
- int32_t k;
558
- for ( k = 0; k < t->n; k++ ) {
559
- ca_free(t->blanks[k]);
560
- t->blanks[k] = NULL;
561
- }
562
- }
563
-
564
- static VALUE
565
- ca_mp_trial_restore (VALUE p)
566
- {
567
- ca_mp_trial_t *t = (ca_mp_trial_t *) p;
568
- CAMultiParent *mp = (CAMultiParent *) t->mask;
569
- int32_t k;
570
- for ( k = 0; k < t->n; k++ ) {
571
- mp->parents[k] = t->saved[k];
572
- }
573
- CAVIEW(t->mask)->parent = t->saved_parent;
574
- if ( ! t->finished ) {
575
- ca_mp_trial_free_blanks(t);
576
- }
577
- return Qnil;
578
- }
579
-
598
+ /* Before a write that puts a true into the mask of a stack or meld (not one
599
+ with its buffer out): each part still reading its stand-in that shares
600
+ its root with another part is given its own mask, and the write goes to
601
+ those parts' masks in their order. Parts that share cells then take the
602
+ write of the last of them, as they do for values; through stand-ins,
603
+ settled afterwards, the trues would be added together. A part that
604
+ shares nothing keeps its stand-in, and gets no mask from falses. */
580
605
  void
581
- ca_multi_parent_mask_refuse_first (CArray *mask, CArray **parents,
582
- CArray **standins, int32_t n,
583
- VALUE (*body)(VALUE), VALUE arg)
606
+ ca_multi_parent_mask_adopt (CArray *mask, CArray **parents,
607
+ CArray **standins, int32_t n)
584
608
  {
585
609
  CAMultiParent *mp = (CAMultiParent *) mask;
586
- volatile VALUE h_blank = 0, h_saved = 0;
587
- ca_mp_trial_t t;
588
- int32_t k, refused = -1;
589
- if ( standins == NULL ) {
610
+ int32_t k, j;
611
+ if ( standins == NULL || mask->ptr != NULL ) {
590
612
  return;
591
613
  }
592
614
  for ( k = 0; k < n; k++ ) {
593
- if ( standins[k] != NULL && mp->parents[k] == standins[k]
594
- && ca_mask_standin_parent_unmaskable(parents[k]) ) {
595
- break;
615
+ if ( standins[k] == NULL || mp->parents[k] != standins[k]
616
+ || ca_test_flag(standins[k], CA_FLAG_READ_ONLY) ) {
617
+ continue;
596
618
  }
597
- }
598
- if ( k == n ) {
599
- return;
600
- }
601
- t.mask = mask;
602
- t.n = n;
603
- t.finished = 0;
604
- t.body = body;
605
- t.arg = arg;
606
- t.saved_parent = CAVIEW(mask)->parent;
607
- t.blanks = ALLOCV_N(CArray *, h_blank, n);
608
- t.saved = ALLOCV_N(CArray *, h_saved, n);
609
- for ( k = 0; k < n; k++ ) {
610
- t.blanks[k] = NULL;
611
- }
612
- for ( k = 0; k < n; k++ ) {
613
- CArray *m = mp->parents[k];
614
- t.saved[k] = m;
615
- t.blanks[k] = carray_new(CA_BOOLEAN, m->ndim, m->dim, 0, NULL);
616
- MEMZERO(t.blanks[k]->ptr, boolean8_t, t.blanks[k]->elements);
617
- }
618
- for ( k = 0; k < n; k++ ) {
619
- mp->parents[k] = t.blanks[k];
620
- }
621
- CAVIEW(mask)->parent = t.blanks[0];
622
- rb_ensure(ca_mp_trial_body, (VALUE) &t, ca_mp_trial_restore, (VALUE) &t);
623
- for ( k = 0; k < n && refused < 0; k++ ) {
624
- CArray *z = t.blanks[k];
625
- ca_size_t i;
626
- if ( standins[k] == NULL || t.saved[k] != standins[k]
627
- || ! ca_mask_standin_parent_unmaskable(parents[k]) ) {
619
+ for ( j = 0; j < n && ( j == k || ! ca_shares_root(parents[j], parents[k]) ); j++ ) ;
620
+ if ( j == n ) {
628
621
  continue;
629
622
  }
630
- for ( i = 0; i < z->elements && ! ((boolean8_t *) z->ptr)[i]; i++ ) ;
631
- if ( i < z->elements ) {
632
- refused = k;
623
+ ca_create_mask(parents[k]);
624
+ if ( parents[k]->mask != NULL ) {
625
+ mp->parents[k] = parents[k]->mask;
633
626
  }
634
627
  }
635
- ca_mp_trial_free_blanks(&t);
636
- ALLOCV_END(h_blank);
637
- ALLOCV_END(h_saved);
638
- if ( refused >= 0 ) {
639
- rb_raise(rb_eTypeError, "can not create mask array for the %s array",
640
- ca_is_value_array(parents[refused]) ? "value" : "mask");
641
- }
628
+ CAVIEW(mask)->parent = mp->parents[0];
642
629
  }
643
630
 
644
631
  void
@@ -646,18 +633,12 @@ ca_multi_parent_mask_settle (CArray *mask, CArray **parents,
646
633
  CArray **standins, int32_t n)
647
634
  {
648
635
  CAMultiParent *mp = (CAMultiParent *) mask;
649
- int32_t k, pass;
636
+ int32_t k;
650
637
  if ( standins == NULL ) {
651
638
  return;
652
639
  }
653
- /* The parts that can have no mask first, so that a refused true leaves
654
- the other parts as they were. */
655
- for ( pass = 0; pass < 2; pass++ ) {
656
- for ( k = 0; k < n; k++ ) {
657
- if ( standins[k] == NULL || mp->parents[k] != standins[k]
658
- || ca_mask_standin_parent_unmaskable(parents[k]) != ( pass == 0 ) ) {
659
- continue;
660
- }
640
+ for ( k = 0; k < n; k++ ) {
641
+ if ( standins[k] != NULL && mp->parents[k] == standins[k] ) {
661
642
  ca_mask_standin_settle(parents[k], standins[k]);
662
643
  }
663
644
  }
@@ -789,6 +770,9 @@ typedef struct {
789
770
  int k; /* entries in list */
790
771
  int ns; /* sources */
791
772
  ca_size_t elements;
773
+ CArray *dest; /* the destination */
774
+ int overwrite; /* clear the destination's mask first */
775
+ char *modes; /* attach modes, one per entry of list */
792
776
  } ca_mask_overlay_t;
793
777
 
794
778
  static VALUE
@@ -815,6 +799,31 @@ ca_mask_overlay_body (VALUE arg)
815
799
  return Qnil;
816
800
  }
817
801
 
802
+ /* Clear the destination's mask if asked, and OR the sources into it. */
803
+ static VALUE
804
+ ca_mask_overlay_write (VALUE arg)
805
+ {
806
+ ca_mask_overlay_t *o = (ca_mask_overlay_t *) arg;
807
+ CArray *ca = o->dest;
808
+ int i;
809
+ if ( o->overwrite ) {
810
+ boolean8_t zero = 0;
811
+ ca_fill(ca->mask, &zero);
812
+ }
813
+ o->k = 0;
814
+ o->list[o->k] = ca->mask;
815
+ o->modes[o->k++] = 'w';
816
+ for (i=0; i<o->ns; i++) {
817
+ if ( o->att[i] ) {
818
+ o->list[o->k] = o->att[i];
819
+ o->modes[o->k++] = 'r';
820
+ }
821
+ }
822
+ o->modes[o->k] = '\0';
823
+ ca_attach_window(o->k, o->list, o->modes, ca_mask_overlay_body, arg);
824
+ return Qnil;
825
+ }
826
+
818
827
  /* overwrite: the destination's mask is cleared before the sources are
819
828
  OR'd in (and left alone when it has none and no source has a mask). */
820
829
  static void
@@ -899,22 +908,10 @@ ca_copy_mask_overlay_core (CArray *ca, ca_size_t elements, int n,
899
908
  }
900
909
 
901
910
  ca_create_mask(ca);
902
- if ( overwrite ) {
903
- boolean8_t zero = 0;
904
- ca_fill(ca->mask, &zero);
905
- }
906
-
907
- o.k = 0;
908
- o.list[o.k] = ca->mask;
909
- modes[o.k++] = 'w';
910
- for (i=0; i<o.ns; i++) {
911
- if ( o.att[i] ) {
912
- o.list[o.k] = o.att[i];
913
- modes[o.k++] = 'r';
914
- }
915
- }
916
- modes[o.k] = '\0';
917
- ca_attach_window(o.k, o.list, modes, ca_mask_overlay_body, (VALUE) &o);
911
+ o.dest = ca;
912
+ o.overwrite = overwrite;
913
+ o.modes = modes;
914
+ ca_mask_overlay_write((VALUE) &o);
918
915
 
919
916
  for (i=0; i<n; i++) {
920
917
  if ( holders[i] ) {
@@ -1840,7 +1837,9 @@ ca_mask_copy_where (VALUE self, int argc, VALUE *argv)
1840
1837
  obj = rb_ca_copy(self);
1841
1838
  memcpy(all_args, argv, argc * sizeof(VALUE));
1842
1839
  all_args[argc] = CA_UNDEF;
1843
- rb_funcallv(obj, rb_intern("[]="), argc + 1, all_args);
1840
+ /* a CScalar takes no index; its one-cell view does */
1841
+ rb_funcallv(ca_is_scalar(ca) ? rb_funcall(obj, rb_intern("flatten"), 0) : obj,
1842
+ rb_intern("[]="), argc + 1, all_args);
1844
1843
  return obj;
1845
1844
  }
1846
1845
 
@@ -1872,6 +1871,19 @@ rb_ca_mask_where (int argc, VALUE *argv, VALUE self)
1872
1871
  return ca_mask_copy_where(self, argc, argv);
1873
1872
  }
1874
1873
 
1874
+ /* inherit_mask's sources are read cell by cell over self, so an array
1875
+ source has self's number of cells (a scalar's one cell applies to all). */
1876
+ static void
1877
+ ca_inherit_mask_check_source (CArray *ca, CArray *cs)
1878
+ {
1879
+ if ( ! ca_is_scalar(cs) && cs->elements != ca->elements ) {
1880
+ rb_raise(rb_eArgError,
1881
+ "%s: mask source has %lld elements, the receiver %lld",
1882
+ ca_calling_method_name(),
1883
+ (long long) cs->elements, (long long) ca->elements);
1884
+ }
1885
+ }
1886
+
1875
1887
  /* @overload inherit_mask (*others):
1876
1888
 
1877
1889
  (Masking, Destructive)
@@ -1895,6 +1907,7 @@ rb_ca_inherit_mask_method (int argc, VALUE *argv, VALUE self)
1895
1907
  for (i=0; i<argc; i++) {
1896
1908
  if ( rb_obj_is_carray(argv[i]) ) {
1897
1909
  TypedData_Get_Struct(argv[i], CArray, &carray_data_type, cs);
1910
+ ca_inherit_mask_check_source(ca, cs);
1898
1911
  slist[i] = cs;
1899
1912
  }
1900
1913
  else {
@@ -1938,6 +1951,7 @@ rb_ca_inherit_mask (VALUE self, int n, ...)
1938
1951
  other = va_arg(rothers, VALUE);
1939
1952
  if ( rb_obj_is_carray(other) ) {
1940
1953
  TypedData_Get_Struct(other, CArray, &carray_data_type, cs);
1954
+ ca_inherit_mask_check_source(ca, cs);
1941
1955
  slist[i] = cs;
1942
1956
  }
1943
1957
  else {
@@ -1976,6 +1990,7 @@ rb_ca_inherit_mask_replace_method (int argc, VALUE *argv, VALUE self)
1976
1990
  for (i=0; i<argc; i++) {
1977
1991
  if ( rb_obj_is_carray(argv[i]) ) {
1978
1992
  TypedData_Get_Struct(argv[i], CArray, &carray_data_type, cs);
1993
+ ca_inherit_mask_check_source(ca, cs);
1979
1994
  slist[i] = cs;
1980
1995
  }
1981
1996
  else {
@@ -2019,6 +2034,7 @@ rb_ca_inherit_mask_replace (VALUE self, int n, ...)
2019
2034
  other = va_arg(rothers, VALUE);
2020
2035
  if ( rb_obj_is_carray(other) ) {
2021
2036
  TypedData_Get_Struct(other, CArray, &carray_data_type, cs);
2037
+ ca_inherit_mask_check_source(ca, cs);
2022
2038
  slist[i] = cs;
2023
2039
  }
2024
2040
  else {
@@ -2266,11 +2282,9 @@ ca_binop_bit_or_i_boolean8_fast (ca_size_t n, boolean8_t *m,
2266
2282
  accumulating; here we need 3-arg). */
2267
2283
  ca_size_t nw = n / CA_MASK_WORD_BYTES;
2268
2284
  ca_size_t j;
2269
- uint64_t *wd = (uint64_t *) q3;
2270
- const uint64_t *wa = (const uint64_t *) q1;
2271
- const uint64_t *wb = (const uint64_t *) q2;
2272
2285
  for (j = 0; j < nw; j++) {
2273
- wd[j] = wa[j] | wb[j];
2286
+ 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));
2274
2288
  }
2275
2289
  for (j = nw * CA_MASK_WORD_BYTES; j < n; j++) {
2276
2290
  q3[j] = q1[j] | q2[j];
@@ -907,10 +907,10 @@ rb_ca_median_m_body (int argc, VALUE *argv, VALUE self)
907
907
  if ( n == 0 ) return mp_axis_all_masked(self, axis, keep_axis);
908
908
  /* mask / min_count / fill_value require the per-fiber select (each fiber
909
909
  has its own n_present); the plain partition path assumes a uniform n.
910
- Switch ca_fast_paths_enabled(): off, the per-fiber select. */
910
+ Switch ca_fast_path(CA_SC_MEDIAN_PARTITION): off, the per-fiber select. */
911
911
  VALUE res;
912
912
  if ( RTEST(rb_ca_has_mask(self)) || min_count > 0 || !NIL_P(fill_value)
913
- || ! ca_fast_paths_enabled() )
913
+ || ! ca_fast_path(CA_SC_MEDIAN_PARTITION) )
914
914
  res = is_obj
915
915
  ? median_object_axis_masked(self, axis, min_count, fill_value, keep_axis)
916
916
  : median_axis_masked(self, axis, min_count, fill_value, keep_axis);
@@ -1364,9 +1364,9 @@ rb_ca_percentile_kw (int argc, VALUE *argv, VALUE ropt, VALUE self)
1364
1364
  /* mask / min_count / fill_value require the per-fiber select (each fiber
1365
1365
  has its own n_present); the plain partition/sort path assumes a
1366
1366
  uniform n. These paths bake keep_axis into the reduced output.
1367
- Switch ca_fast_paths_enabled(): off, the per-fiber select. */
1367
+ Switch ca_fast_path(CA_SC_PERCENTILE_PARTITION): off, the per-fiber select. */
1368
1368
  int per_fiber = RTEST(rb_ca_has_mask(self)) || min_count > 0
1369
- || !NIL_P(fill_value) || ! ca_fast_paths_enabled();
1369
+ || !NIL_P(fill_value) || ! ca_fast_path(CA_SC_PERCENTILE_PARTITION);
1370
1370
  if ( !is_obj ) {
1371
1371
  result = per_fiber
1372
1372
  ? pct_axis_masked(self, pers, axis, method, min_count, fill_value, keep_axis)
@@ -202,7 +202,9 @@ ca_mv_record_member_spec (VALUE info, char *out, size_t outlen,
202
202
  a member it has no item for (see above), or members that overlap or come
203
203
  out of offset order (a union).
204
204
 
205
- The result is frozen and cached on the data_class. It is an ordinary
205
+ The result is frozen and cached on the data_class. Only the main Ractor
206
+ may set an instance variable on a class, so another Ractor builds the
207
+ format each time until the main one has cached it. It is an ordinary
206
208
  String that compaction can move, so a view takes a copy of its bytes
207
209
  rather than its pointer (ca_mv_get). */
208
210
  static VALUE
@@ -245,11 +247,11 @@ ca_mv_struct_format_for_data_class (VALUE data_class)
245
247
  }
246
248
 
247
249
  rb_obj_freeze(buf);
248
- rb_ivar_set(data_class, iv, buf);
250
+ if (ca_in_main_ractor()) rb_ivar_set(data_class, iv, buf);
249
251
  return buf;
250
252
 
251
253
  unsupported:
252
- rb_ivar_set(data_class, iv, Qfalse);
254
+ if (ca_in_main_ractor()) rb_ivar_set(data_class, iv, Qfalse);
253
255
  return Qnil;
254
256
  }
255
257
 
@@ -900,7 +902,7 @@ ca_mv_release (VALUE obj, rb_memory_view_t *view)
900
902
  }
901
903
 
902
904
  /* ---------------- importer ----------------
903
- Two-tier API mirroring numo-narray-memoryview:
905
+ Two-tier API:
904
906
 
905
907
  CArray.from_memory_view(obj) -> CArray (copy, independent buffer)
906
908
  CArray.wrap_memory_view(obj) -> CAWrap (zero-copy, shared memory)
@@ -1410,7 +1412,7 @@ ca_mv_wrap_body (VALUE arg)
1410
1412
  a->target_class);
1411
1413
  rb_ivar_set(wrap, rb_intern("memory_view_source"), a->obj);
1412
1414
  rb_ivar_set(wrap, rb_intern("memory_view_holder"), a->data_holder_obj);
1413
- ca_mv_extract(wrap, &ca);
1415
+ GetCArray(wrap, ca);
1414
1416
  if ( v->readonly ) {
1415
1417
  ca_set_flag(ca, CA_FLAG_READ_ONLY);
1416
1418
  }
@@ -1527,7 +1529,7 @@ ca_mv_from_body (VALUE arg)
1527
1529
 
1528
1530
  result = rb_carray_new(data_type, d->ndim, d->dim,
1529
1531
  ca_mv_carray_bytes_for(data_type, v->item_size), NULL);
1530
- ca_mv_extract(result, &ca);
1532
+ GetCArray(result, ca);
1531
1533
 
1532
1534
  if ( d->contiguous ) {
1533
1535
  memcpy(ca->ptr, v->data, (size_t) ca_length(ca));
@@ -1621,9 +1623,13 @@ rb_ca_s_memory_view_reject_reason (VALUE klass, VALUE obj)
1621
1623
  "producer cannot express yet (nested struct, "
1622
1624
  "CArray template, :fixlen, or :object)");
1623
1625
  }
1624
- return rb_sprintf("element data_type (%d) is not exportable via memory_view "
1625
- "(CA_FIXLEN without data_class / CA_OBJECT)",
1626
- ca->data_type);
1626
+ if (ca->data_type == CA_OBJECT) {
1627
+ return rb_str_new_cstr("an object array holds Ruby objects, not bytes "
1628
+ "a consumer can read; convert it with to_type "
1629
+ "first");
1630
+ }
1631
+ return rb_sprintf("data_type %s has no MemoryView format",
1632
+ ca_type_name[ca->data_type]);
1627
1633
  }
1628
1634
  if (ca_has_mask(ca)) {
1629
1635
  return rb_str_new_cstr("array has a mask; use arr.value (mask-ignoring CARefer) "