carray 3.0.5 → 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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +233 -0
  3. data/README.md +1 -1
  4. data/carray.gemspec +3 -2
  5. data/ext/ca_axis_dispatch.c +6 -0
  6. data/ext/ca_axis_group.c +1 -1
  7. data/ext/ca_categorical_iterator.c +7 -7
  8. data/ext/ca_kernel_iterator.c +116 -231
  9. data/ext/ca_kernel_iterator.h +410 -254
  10. data/ext/ca_obj_bincmp.c +9 -1
  11. data/ext/ca_obj_binop.c +10 -1
  12. data/ext/ca_obj_bitfield.c +2 -0
  13. data/ext/ca_obj_byte_swap.c +2 -0
  14. data/ext/ca_obj_face.c +2 -0
  15. data/ext/ca_obj_fake.c +3 -1
  16. data/ext/ca_obj_grid.c +167 -12
  17. data/ext/ca_obj_meld.c +69 -113
  18. data/ext/ca_obj_moncmp.c +9 -1
  19. data/ext/ca_obj_monop.c +40 -2
  20. data/ext/ca_obj_object.c +54 -20
  21. data/ext/ca_obj_reduce.c +15 -1
  22. data/ext/ca_obj_refer.c +65 -20
  23. data/ext/ca_obj_remap.c +69 -10
  24. data/ext/ca_obj_roll.c +1 -0
  25. data/ext/ca_obj_select.c +4 -1
  26. data/ext/ca_obj_select_axis.c +1 -0
  27. data/ext/ca_obj_shift.c +1 -0
  28. data/ext/ca_obj_stack.c +310 -111
  29. data/ext/ca_obj_stride.c +131 -45
  30. data/ext/ca_obj_string.c +18 -2
  31. data/ext/ca_obj_tile.c +1 -0
  32. data/ext/ca_obj_triop.c +9 -1
  33. data/ext/ca_obj_window.c +1 -0
  34. data/ext/ca_op_ipower.c +8 -8
  35. data/ext/caframe_csv_reader.c +1 -1
  36. data/ext/carray.h +2 -1
  37. data/ext/carray_access.c +27 -64
  38. data/ext/carray_address_basis.c +134 -0
  39. data/ext/carray_attribute.c +8 -0
  40. data/ext/carray_broadcast.c +3 -4
  41. data/ext/carray_build_flags.h +1 -1
  42. data/ext/carray_cast.c +8 -11
  43. data/ext/carray_conversion.c +9 -5
  44. data/ext/carray_core.c +811 -72
  45. data/ext/carray_element.c +13 -9
  46. data/ext/carray_histogram.c +25 -3
  47. data/ext/carray_internal.h +147 -12
  48. data/ext/carray_kernels_bincmp.c +2007 -3
  49. data/ext/carray_kernels_binop.c +25365 -5199
  50. data/ext/carray_kernels_init.c +8 -8
  51. data/ext/carray_kernels_monop.c +10696 -3678
  52. data/ext/carray_kernels_reduce_aggregate.c +4429 -702
  53. data/ext/carray_kernels_reduce_boolean.c +1533 -15
  54. data/ext/carray_kernels_reduce_cumulative.c +1808 -248
  55. data/ext/carray_kernels_reduce_extreme.c +4508 -511
  56. data/ext/carray_kernels_reduce_variance.c +2284 -152
  57. data/ext/carray_kernels_scan.c +240 -240
  58. data/ext/carray_kernels_search.c +4211 -264
  59. data/ext/carray_kernels_sort.c +367 -450
  60. data/ext/carray_lazy.c +47 -0
  61. data/ext/carray_loop.c +1 -1
  62. data/ext/carray_mask.c +130 -139
  63. data/ext/carray_median_percentile.c +4 -4
  64. data/ext/carray_memory_view.c +13 -7
  65. data/ext/carray_operator.c +147 -44
  66. data/ext/carray_order.c +3 -2
  67. data/ext/carray_random.c +4 -1
  68. data/ext/carray_slab.c +97 -21
  69. data/ext/carray_sort.c +1 -1
  70. data/ext/carray_test.c +74 -19
  71. data/ext/carray_time_text.c +2 -2
  72. data/ext/extconf.rb +11 -0
  73. data/ext/mkkernel.rb +346 -94
  74. data/ext/ruby_carray.c +4 -1
  75. data/ext/version.h +4 -4
  76. data/lib/carray/attribute.rb +25 -1
  77. data/lib/carray/autoload_carray.rb +3 -3
  78. data/lib/carray/autoload_method_extension.rb +5 -3
  79. data/lib/carray/axis_group.rb +13 -7
  80. data/lib/carray/basics.rb +1 -1
  81. data/lib/carray/block_iterator.rb +17 -3
  82. data/lib/carray/categorical.rb +6 -8
  83. data/lib/carray/categorical_iterator.rb +2 -2
  84. data/lib/carray/complex.rb +2 -2
  85. data/lib/carray/conditional.rb +3 -2
  86. data/lib/carray/frame/column_verbs.rb +45 -14
  87. data/lib/carray/frame/convert.rb +19 -0
  88. data/lib/carray/frame/csv_parser.rb +32 -32
  89. data/lib/carray/frame/describe.rb +4 -2
  90. data/lib/carray/frame/frame.rb +399 -78
  91. data/lib/carray/frame/group.rb +679 -53
  92. data/lib/carray/frame/io.rb +209 -71
  93. data/lib/carray/frame/join.rb +146 -37
  94. data/lib/carray/frame/records.rb +3 -3
  95. data/lib/carray/frame/reshape.rb +89 -11
  96. data/lib/carray/frame/sort.rb +57 -9
  97. data/lib/carray/frame/verbs.rb +384 -49
  98. data/lib/carray/frame/windows.rb +44 -6
  99. data/lib/carray/inspect.rb +7 -2
  100. data/lib/carray/iterator.rb +32 -0
  101. data/lib/carray/lazy.rb +17 -8
  102. data/lib/carray/mask_gap_fill.rb +37 -17
  103. data/lib/carray/meld_reduce.rb +1 -1
  104. data/lib/carray/methods/choose.rb +3 -2
  105. data/lib/carray/methods/meshgrid.rb +3 -1
  106. data/lib/carray/runtime.rb +47 -8
  107. data/lib/carray/segment_iterator.rb +23 -8
  108. data/lib/carray/serialize.rb +34 -17
  109. data/lib/carray/stack.rb +18 -6
  110. data/lib/carray/time.rb +5 -0
  111. data/lib/carray/window_iterator.rb +16 -4
  112. data/yard-stubs/ca_obj_array.rb +26 -1
  113. data/yard-stubs/carray_cast.rb +8 -5
  114. data/yard-stubs/carray_conversion.rb +8 -2
  115. data/yard-stubs/carray_mask.rb +20 -1
  116. data/yard-stubs/carray_math.rb +31 -16
  117. data/yard-stubs/carray_memory_view.rb +7 -0
  118. data/yard-stubs/carray_test.rb +7 -4
  119. metadata +18 -4
data/ext/ca_obj_stack.c CHANGED
@@ -19,6 +19,7 @@
19
19
 
20
20
  #include "carray.h"
21
21
  #include "carray_internal.h" /* ca_attach_all / ca_sync_all */
22
+ #include <ruby/ractor.h>
22
23
  #include "ca_composite_dispatch.h"
23
24
  #include "ca_obj_face.h"
24
25
 
@@ -289,12 +290,7 @@ ca_stack_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
289
290
  rb_raise(rb_eIndexError, "CAStack k-axis (axis %d) index %lld out of range [0, %d)",
290
291
  (int) k_axis, (long long) k, (int) ca->n_parents);
291
292
  }
292
- if ( k_axis == 0 && ca_fast_paths_enabled() ) {
293
- /* Fast path: parent_idx = idx[1..ndim-1]; pass idx+1 directly. */
294
- ca_xfer_index(ca->parents[k], idx + 1, data, dir);
295
- return;
296
- }
297
- /* General case: build parent_idx by removing the k_axis slot. */
293
+ /* The parent index is idx without the k_axis slot. */
298
294
  {
299
295
  ca_size_t pidx[CA_RANK_MAX];
300
296
  int8_t a, pndim = ca->ndim - 1;
@@ -331,7 +327,7 @@ ca_stack_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
331
327
  paddrs = ALLOCV_N(ca_size_t, holder1, n);
332
328
  pdata = ALLOCV_N(char, holder2, n * bytes);
333
329
 
334
- if ( k_axis == 0 && ca_fast_paths_enabled() ) {
330
+ if ( k_axis == 0 && ca_fast_path(CA_SC_STACK_ADDRS_K0) ) {
335
331
  /* Fast path: addr = k * parent_elements + parent_addr (row-major over
336
332
  view shape with K at axis 0). */
337
333
  for ( k = 0; k < ca->n_parents; k++ ) {
@@ -479,7 +475,13 @@ ca_stack_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
479
475
  ! ca_xfer_stride_request_is_axis_box(ca, starts, counts, strides) ) {
480
476
  structural = 0;
481
477
  }
482
- if ( ! ca_fast_paths_enabled() ) structural = 0;
478
+ /* With the stack axis between the first and the last, each parent's box
479
+ lands in dst one cell at a time below; the reloc walk further down
480
+ takes the same box a contiguous row at a time, about 5x faster on a
481
+ block of a 500x4x2000 stack. With it last the reloc walk goes cell by
482
+ cell instead, so the box stays here. */
483
+ if ( k_axis != 0 && k_axis != ca->ndim - 1 ) structural = 0;
484
+ if ( ! ca_fast_path(CA_SC_STACK_STRIDE_BOX) ) structural = 0;
483
485
 
484
486
  if ( structural ) {
485
487
  ca_size_t k_lo = starts[k_axis];
@@ -571,7 +573,7 @@ ca_stack_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
571
573
  with native[k_axis] removed, in order. */
572
574
  {
573
575
  int8_t p = -1;
574
- int reloc = ca_fast_paths_enabled();
576
+ int reloc = ca_fast_path(CA_SC_STACK_STRIDE_RELOC);
575
577
  for ( i = 0; i < ca->ndim; i++ ) {
576
578
  if ( strides[i] == native[k_axis] ) { p = i; break; }
577
579
  }
@@ -680,6 +682,25 @@ ca_stack_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
680
682
  }
681
683
  }
682
684
 
685
+ /* A parent's cells, delivered (PUT): into the buffer its attach handed
686
+ out, or through the parent (see ca_stack_xfer_parents). A parent with no
687
+ buffer out (a stand-in the trial of a write puts in place, which is never
688
+ attached) has nothing to go stale: the cells go through it. */
689
+ static void
690
+ ca_stack_put_parent (CArray *p, char *src, volatile VALUE *sync_error)
691
+ {
692
+ if ( sync_error != NULL && p->ptr != NULL ) {
693
+ /* Synced at once, before the next part is written: parts that share
694
+ cells then take the write of the last of them, as a write through
695
+ the parts does. */
696
+ memcpy(p->ptr, src, (size_t) (p->elements * p->bytes));
697
+ ca_sync_carry_on(p, sync_error);
698
+ }
699
+ else {
700
+ ca_xfer_all(p, src, CA_XFER_PUT);
701
+ }
702
+ }
703
+
683
704
  /* ------------------------------------------------------------------- */
684
705
  /* xfer_all: K-fold over all parents */
685
706
  /* ------------------------------------------------------------------- */
@@ -693,13 +714,14 @@ ca_stack_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
693
714
  correctness path (per-parent contig-read / scattered-write), not a
694
715
  perf-optimised one.
695
716
 
696
- into_buffers (PUT only) writes each parent's cells into the buffer its
697
- attach handed out, rather than through the parent to what is behind it:
698
- the sync of an attached stack, which then syncs each parent. Writing
717
+ A non-NULL sync_error (PUT only) writes each parent's cells into the
718
+ buffer its attach handed out, rather than through the parent to what is
719
+ behind it, and syncs the parent: the sync of an attached stack. Writing
699
720
  past the buffer would leave it stale, and the parent's own sync would
700
- push the stale buffer back over the cells just written. */
721
+ push the stale buffer back over the cells just written. An exception
722
+ from one parent's sync is kept in *sync_error while the others go on. */
701
723
  static void
702
- ca_stack_xfer_parents (CAStack *ca, char *d, int dir, int into_buffers)
724
+ ca_stack_xfer_parents (CAStack *ca, char *d, int dir, volatile VALUE *sync_error)
703
725
  {
704
726
  int8_t k_axis = ca->k_axis;
705
727
  ca_size_t bytes = ca->bytes;
@@ -709,12 +731,12 @@ ca_stack_xfer_parents (CAStack *ca, char *d, int dir, int into_buffers)
709
731
  counts. */
710
732
  if ( ca->elements == 0 ) return;
711
733
 
712
- if ( k_axis == 0 && ca_fast_paths_enabled() ) {
734
+ if ( k_axis == 0 && ca_fast_path(CA_SC_STACK_PARENTS_K0) ) {
713
735
  ca_size_t parent_bytes_total = ca->parents[0]->elements * bytes;
714
736
  for ( k = 0; k < ca->n_parents; k++ ) {
715
- if ( into_buffers ) {
716
- memcpy(ca->parents[k]->ptr, d + k * parent_bytes_total,
717
- (size_t) parent_bytes_total);
737
+ if ( dir == CA_XFER_PUT ) {
738
+ ca_stack_put_parent(ca->parents[k], d + k * parent_bytes_total,
739
+ sync_error);
718
740
  }
719
741
  else {
720
742
  ca_xfer_all(ca->parents[k], d + k * parent_bytes_total, dir);
@@ -808,12 +830,7 @@ ca_stack_xfer_parents (CAStack *ca, char *d, int dir, int into_buffers)
808
830
  }
809
831
 
810
832
  if ( dir == CA_XFER_PUT ) {
811
- if ( into_buffers ) {
812
- memcpy(ca->parents[k]->ptr, buf, (size_t) (parent_elements * bytes));
813
- }
814
- else {
815
- ca_xfer_all(ca->parents[k], buf, CA_XFER_PUT);
816
- }
833
+ ca_stack_put_parent(ca->parents[k], buf, sync_error);
817
834
  }
818
835
  }
819
836
  }
@@ -825,7 +842,7 @@ static void
825
842
  ca_stack_func_xfer_all (void *ap, void *data, int dir)
826
843
  {
827
844
  ca_stack_mask_refresh((CAStack *) ap);
828
- ca_stack_xfer_parents((CAStack *) ap, (char *) data, dir, 0);
845
+ ca_stack_xfer_parents((CAStack *) ap, (char *) data, dir, NULL);
829
846
  }
830
847
 
831
848
  /* ------------------------------------------------------------------- */
@@ -857,15 +874,19 @@ ca_stack_func_attach (void *ap)
857
874
  ca_fill_or_detach(ca->n_parents, ca->parents, ca_stack_attach_owned, (VALUE) ca);
858
875
  }
859
876
 
860
- static void ca_stack_mask_refuse_sync (CAStack *ca);
861
-
862
877
  static void
863
878
  ca_stack_func_sync (void *ap)
864
879
  {
865
880
  CAStack *ca = (CAStack *) ap;
866
- ca_stack_mask_refuse_sync(ca);
867
- ca_stack_xfer_parents(ca, ca->ptr, CA_XFER_PUT, 1);
868
- ca_sync_all(ca->parents, ca->n_parents);
881
+ CA_MULTI_PARENT_VALUE_TRIAL(ca, t,
882
+ ca_stack_xfer_parents((CAStack *) t, ca->ptr, CA_XFER_PUT, NULL));
883
+ {
884
+ volatile VALUE error = Qnil;
885
+ ca_stack_xfer_parents(ca, ca->ptr, CA_XFER_PUT, &error);
886
+ if ( ! NIL_P(error) ) {
887
+ rb_exc_raise(error);
888
+ }
889
+ }
869
890
  ca_stack_mask_settle(ca);
870
891
  }
871
892
 
@@ -969,7 +990,7 @@ ca_stack_func_fold_stride (void *ap, ca_fold_t *f, void **next_parent)
969
990
  through to CAStack.xfer_stride (k_axis-aware), which still delivers
970
991
  correct (if non-folded) per-parent dispatch. */
971
992
  if ( ca->k_axis != 0 ) return 0;
972
- if ( ! ca_fast_paths_enabled() ) return 0;
993
+ if ( ! ca_fast_path(CA_SC_STACK_FOLD) ) return 0;
973
994
  if ( pbt == 0 ) return 0; /* empty parents: no parent holds the box */
974
995
 
975
996
  for ( i = 0; i < f->ndim; i++ ) {
@@ -981,6 +1002,16 @@ ca_stack_func_fold_stride (void *ap, ca_fold_t *f, void **next_parent)
981
1002
  if ( k < 0 || k >= ca->n_parents ) return 0;
982
1003
  if ( hi >= (k + 1) * pbt ) return 0; /* box spans >1 parent */
983
1004
 
1005
+ /* A view folded past this stack is built on the parent's root and asks
1006
+ that root for its mask. Whether the parent is a value array or a mask
1007
+ array is answered along the parent's own chain, which the folded view
1008
+ is not on (ca_keep_chain_flags reads the chain of the view folded,
1009
+ and it stops at a stack): the view would read the root's mask through
1010
+ a value array. Such a parent stays behind the stack's transfer. */
1011
+ if ( ca_is_value_array(ca->parents[k]) || ca_is_mask_array(ca->parents[k]) ) {
1012
+ return 0;
1013
+ }
1014
+
984
1015
  f->base -= k * pbt;
985
1016
  for ( i = 0; i < f->ndim; i++ ) { /* degenerate the (size-1) K axis */
986
1017
  if ( f->strides[i] == pbt ) f->strides[i] = 0;
@@ -993,80 +1024,250 @@ ca_stack_func_fold_stride (void *ap, ca_fold_t *f, void **next_parent)
993
1024
  /* operation table */
994
1025
  /* ------------------------------------------------------------------- */
995
1026
 
996
- /* The table's write slots: after a write that may have put a true into a
997
- stand-in, the true goes on to the parent's mask, or is refused for a
998
- value array or a mask array (ca_mask_standin_settle). Only the mask of
999
- a CAStack has stand-ins. */
1000
- /* A write to the mask of a CAStack, run once against blank masks first
1001
- (ca_multi_parent_mask_refuse_first), so that a write refused for a value
1002
- array or mask array part changes no other part. */
1003
- enum { CA_STACK_PUT_INDEX, CA_STACK_PUT_ADDRS, CA_STACK_PUT_STRIDE, CA_STACK_PUT_ALL,
1004
- CA_STACK_PUT_FILL, CA_STACK_PUT_SYNC, CA_STACK_PUT_FILL_ADDRS,
1005
- CA_STACK_PUT_FILL_STRIDE };
1027
+ extern int8_t CA_OBJ_MELD;
1028
+ CAMeld *ca_meld_new (int32_t n_parents, CArray **parents, int8_t meld_axis);
1029
+
1030
+ /* For a stack or meld holding data (not the mask of one): its parts. */
1031
+ static int
1032
+ ca_value_trial_parts (CArray *ca, CArray ***parts, int32_t *n)
1033
+ {
1034
+ if ( ca->obj_type == CA_OBJ_STACK && ((CAStack *) ca)->mask_owner == NULL ) {
1035
+ *parts = ((CAStack *) ca)->parents;
1036
+ *n = ((CAStack *) ca)->n_parents;
1037
+ return 1;
1038
+ }
1039
+ if ( ca->obj_type == CA_OBJ_MELD && ((CAMeld *) ca)->mask_owner == NULL ) {
1040
+ *parts = ((CAMeld *) ca)->parents;
1041
+ *n = ((CAMeld *) ca)->n_parents;
1042
+ return 1;
1043
+ }
1044
+ return 0;
1045
+ }
1046
+
1047
+ /* The data type a write through `q` is converted to and can be refused by:
1048
+ the parent type of the first writable cast that changes the type, down
1049
+ the chain of single-parent views. -1 if there is none. */
1050
+ static int
1051
+ ca_value_trial_target (CArray *q)
1052
+ {
1053
+ int8_t t;
1054
+ while ( q != NULL ) {
1055
+ if ( ca_monop_writable_cast_target(q, &t) && t != q->data_type ) {
1056
+ return t;
1057
+ }
1058
+ if ( ! ca_is_view(q) || ca_test_flag(q, CA_FLAG_MULTI_PARENTS) ) {
1059
+ break;
1060
+ }
1061
+ q = CAVIEW(q)->parent;
1062
+ }
1063
+ return -1;
1064
+ }
1065
+
1066
+ static int
1067
+ ca_value_trial_needed (CArray *ca, int depth)
1068
+ {
1069
+ CArray **parts;
1070
+ int32_t n, k;
1071
+ if ( depth > 64 || ! ca_value_trial_parts(ca, &parts, &n) ) {
1072
+ return 0;
1073
+ }
1074
+ for ( k = 0; k < n; k++ ) {
1075
+ if ( ca_value_trial_target(parts[k]) >= 0
1076
+ || ca_value_trial_needed(parts[k], depth + 1) ) {
1077
+ return 1;
1078
+ }
1079
+ }
1080
+ return 0;
1081
+ }
1082
+
1083
+ static CArray *
1084
+ ca_value_trial_hold (VALUE keep, CArray *ca)
1085
+ {
1086
+ rb_ary_push(keep, ca_wrap_struct(ca));
1087
+ return ca;
1088
+ }
1089
+
1090
+ static CArray *ca_value_trial_like (CArray *ca, VALUE keep, int depth);
1091
+
1092
+ /* A scratch array standing for the part `q`: converting as `q` does when
1093
+ `q` can refuse a value, plain otherwise. */
1094
+ static CArray *
1095
+ ca_value_trial_standin (CArray *q, VALUE keep, int depth)
1096
+ {
1097
+ CArray **parts;
1098
+ int32_t n;
1099
+ int t = ca_value_trial_target(q);
1100
+ if ( t >= 0 ) {
1101
+ VALUE z = rb_carray_new((int8_t) t, q->ndim, q->dim, 0, NULL);
1102
+ /* coerce: the stand-in converts as the part's own cast does */
1103
+ VALUE w = rb_ca_wrap_writable(z, INT2NUM(q->data_type));
1104
+ rb_ary_push(keep, z);
1105
+ rb_ary_push(keep, w);
1106
+ return (CArray *) DATA_PTR(w);
1107
+ }
1108
+ if ( ca_value_trial_parts(q, &parts, &n) && ca_value_trial_needed(q, depth) ) {
1109
+ return ca_value_trial_like(q, keep, depth);
1110
+ }
1111
+ {
1112
+ VALUE z = rb_carray_new(q->data_type, q->ndim, q->dim, q->bytes, NULL);
1113
+ rb_ary_push(keep, z);
1114
+ return (CArray *) DATA_PTR(z);
1115
+ }
1116
+ }
1117
+
1118
+ static CArray *
1119
+ ca_value_trial_like (CArray *ca, VALUE keep, int depth)
1120
+ {
1121
+ volatile VALUE holder = 0;
1122
+ CArray **parts = NULL, **standins, *like;
1123
+ int32_t n = 0, k;
1124
+ ca_value_trial_parts(ca, &parts, &n);
1125
+ standins = ALLOCV_N(CArray *, holder, n);
1126
+ for ( k = 0; k < n; k++ ) {
1127
+ standins[k] = ca_value_trial_standin(parts[k], keep, depth + 1);
1128
+ }
1129
+ if ( ca->obj_type == CA_OBJ_STACK ) {
1130
+ like = (CArray *) ca_stack_new_with_axis(n, standins, ((CAStack *) ca)->k_axis);
1131
+ }
1132
+ else {
1133
+ like = (CArray *) ca_meld_new(n, standins, ((CAMeld *) ca)->meld_axis);
1134
+ }
1135
+ ALLOCV_END(holder);
1136
+ return ca_value_trial_hold(keep, like);
1137
+ }
1138
+
1139
+ /* Set while a write runs against stand-ins; a write nested in it is not
1140
+ tried again. */
1141
+ static rb_ractor_local_key_t ca_value_trial_depth_key;
1142
+
1143
+ static long
1144
+ ca_value_trial_depth (void)
1145
+ {
1146
+ VALUE v;
1147
+ if ( ca_value_trial_depth_key
1148
+ && rb_ractor_local_storage_value_lookup(ca_value_trial_depth_key, &v) ) {
1149
+ return FIX2LONG(v);
1150
+ }
1151
+ return 0;
1152
+ }
1153
+
1154
+ static void
1155
+ ca_value_trial_depth_add (long d)
1156
+ {
1157
+ rb_ractor_local_storage_value_set(ca_value_trial_depth_key,
1158
+ LONG2FIX(ca_value_trial_depth() + d));
1159
+ }
1160
+
1161
+ int
1162
+ ca_multi_parent_value_needs_trial (CArray *ca)
1163
+ {
1164
+ return ca_value_trial_depth() == 0 && ca_value_trial_needed(ca, 0);
1165
+ }
1166
+
1167
+ CArray *
1168
+ ca_multi_parent_value_trial (CArray *ca, VALUE keep)
1169
+ {
1170
+ return ca_value_trial_like(ca, keep, 0);
1171
+ }
1172
+
1173
+ /* A view over a stack or meld can hand a write on to it in several pieces
1174
+ (a CAGrid picking along the stack axis), each a write of its own. The
1175
+ stack or meld below `ca`, reached through views that have no buffer out
1176
+ (a view with one gets its write checked when it is synced), when a write
1177
+ through `ca` needs to be tried first; NULL otherwise. */
1178
+ CArray *
1179
+ ca_multi_parent_value_trial_below (CArray *ca)
1180
+ {
1181
+ CArray *q;
1182
+ if ( ! ca_is_view(ca) || ca_test_flag(ca, CA_FLAG_MULTI_PARENTS)
1183
+ || ca_value_trial_depth() > 0 ) {
1184
+ return NULL;
1185
+ }
1186
+ q = CAVIEW(ca)->parent;
1187
+ while ( q != NULL && ca_is_view(q) && ! ca_test_flag(q, CA_FLAG_MULTI_PARENTS) ) {
1188
+ if ( q->ptr != NULL ) {
1189
+ return NULL;
1190
+ }
1191
+ q = CAVIEW(q)->parent;
1192
+ }
1193
+ if ( q == NULL || q->ptr != NULL || ! ca_value_trial_needed(q, 0) ) {
1194
+ return NULL;
1195
+ }
1196
+ return q;
1197
+ }
1006
1198
 
1007
1199
  typedef struct {
1008
- int slot;
1009
- void *ap;
1010
- ca_size_t *idx;
1011
- ca_size_t n;
1012
- ca_size_t *addrs;
1013
- ca_size_t *starts;
1014
- ca_size_t *counts;
1015
- ca_size_t *strides;
1016
- void *data;
1017
- ca_size_t base;
1018
- int8_t ndim;
1019
- ca_size_t *steps;
1020
- } ca_stack_put_t;
1200
+ CAMultiParent *mp;
1201
+ CArray **saved;
1202
+ CArray *saved_parent;
1203
+ VALUE (*body)(VALUE);
1204
+ VALUE arg;
1205
+ } ca_value_trial_swap_t;
1206
+
1207
+ static VALUE
1208
+ ca_value_trial_swap_body (VALUE p)
1209
+ {
1210
+ ca_value_trial_swap_t *t = (ca_value_trial_swap_t *) p;
1211
+ return t->body(t->arg);
1212
+ }
1021
1213
 
1022
1214
  static VALUE
1023
- ca_stack_put_body (VALUE arg)
1215
+ ca_value_trial_swap_restore (VALUE p)
1024
1216
  {
1025
- ca_stack_put_t *a = (ca_stack_put_t *) arg;
1026
- switch ( a->slot ) {
1027
- case CA_STACK_PUT_INDEX:
1028
- ca_stack_func_xfer_index(a->ap, a->idx, a->data, CA_XFER_PUT); break;
1029
- case CA_STACK_PUT_ADDRS:
1030
- ca_stack_func_xfer_addrs(a->ap, a->n, a->addrs, a->data, CA_XFER_PUT); break;
1031
- case CA_STACK_PUT_STRIDE:
1032
- ca_stack_func_xfer_stride(a->ap, a->starts, a->counts, a->strides, a->data,
1033
- CA_XFER_PUT); break;
1034
- case CA_STACK_PUT_ALL:
1035
- ca_stack_func_xfer_all(a->ap, a->data, CA_XFER_PUT); break;
1036
- case CA_STACK_PUT_FILL:
1037
- ca_stack_func_fill_data(a->ap, a->data); break;
1038
- case CA_STACK_PUT_FILL_ADDRS:
1039
- ca_fill_addrs_default(a->ap, a->n, a->addrs, a->data); break;
1040
- case CA_STACK_PUT_FILL_STRIDE:
1041
- ca_fill_stride_default(a->ap, a->base, a->ndim, a->counts, a->steps,
1042
- a->data); break;
1043
- case CA_STACK_PUT_SYNC:
1044
- ca_stack_xfer_parents((CAStack *) a->ap, ((CArray *) a->ap)->ptr, CA_XFER_PUT, 1);
1045
- break;
1217
+ ca_value_trial_swap_t *t = (ca_value_trial_swap_t *) p;
1218
+ int32_t k;
1219
+ for ( k = 0; k < t->mp->n_parents; k++ ) {
1220
+ t->mp->parents[k] = t->saved[k];
1046
1221
  }
1222
+ CAVIEW(t->mp)->parent = t->saved_parent;
1223
+ ca_value_trial_depth_add(-1);
1047
1224
  return Qnil;
1048
1225
  }
1049
1226
 
1050
- static void
1051
- ca_stack_mask_refuse_first (ca_stack_put_t *a)
1227
+ /* Run `body(arg)` once with the parts of the stack or meld `m` replaced by
1228
+ stand-ins (ca_multi_parent_value_trial), then put the parts back. */
1229
+ void
1230
+ ca_multi_parent_value_trial_through (CArray *m, VALUE (*body)(VALUE), VALUE arg)
1052
1231
  {
1053
- CAStack *o = (CAStack *) ((CAStack *) a->ap)->mask_owner;
1054
- if ( o != NULL ) {
1055
- ca_multi_parent_mask_refuse_first((CArray *) a->ap, o->parents,
1056
- o->mask_standins, o->n_parents,
1057
- ca_stack_put_body, (VALUE) a);
1232
+ volatile VALUE keep = rb_ary_new();
1233
+ volatile VALUE holder = 0;
1234
+ CAMultiParent *mp = (CAMultiParent *) m;
1235
+ CAMultiParent *like = (CAMultiParent *) ca_multi_parent_value_trial(m, keep);
1236
+ ca_value_trial_swap_t t;
1237
+ int32_t k;
1238
+ t.mp = mp;
1239
+ t.saved = ALLOCV_N(CArray *, holder, mp->n_parents);
1240
+ t.saved_parent = CAVIEW(m)->parent;
1241
+ t.body = body;
1242
+ t.arg = arg;
1243
+ for ( k = 0; k < mp->n_parents; k++ ) {
1244
+ t.saved[k] = mp->parents[k];
1245
+ mp->parents[k] = like->parents[k];
1058
1246
  }
1247
+ CAVIEW(m)->parent = mp->parents[0];
1248
+ ca_value_trial_depth_add(1);
1249
+ rb_ensure(ca_value_trial_swap_body, (VALUE) &t,
1250
+ ca_value_trial_swap_restore, (VALUE) &t);
1251
+ ALLOCV_END(holder);
1252
+ RB_GC_GUARD(keep);
1059
1253
  }
1060
1254
 
1255
+ /* The table's write slots: after a write that may have put a true into a
1256
+ stand-in, the true goes on to the parent's mask (ca_mask_standin_settle).
1257
+ Only the mask of a CAStack has stand-ins. */
1258
+ /* After a write of n cells of `data` to the mask of a CAStack: a true
1259
+ that went into a stand-in goes on to its parent's mask. */
1061
1260
  static void
1062
- ca_stack_mask_refuse_sync (CAStack *ca)
1261
+ ca_stack_mask_adopt_before (void *ap, const void *data, ca_size_t n)
1063
1262
  {
1064
- ca_stack_put_t a = { CA_STACK_PUT_SYNC, ca, NULL, 0, NULL, NULL, NULL, NULL, NULL };
1065
- ca_stack_mask_refuse_first(&a);
1263
+ CAStack *ca = (CAStack *) ap;
1264
+ CAStack *o = (CAStack *) ca->mask_owner;
1265
+ if ( o != NULL && ca_mask_any_true(data, n) ) {
1266
+ ca_multi_parent_mask_adopt((CArray *) ca, o->parents, o->mask_standins,
1267
+ o->n_parents);
1268
+ }
1066
1269
  }
1067
1270
 
1068
- /* After a write of n cells of `data` to the mask of a CAStack: a true
1069
- that went into a stand-in goes on to its parent's mask. */
1070
1271
  static void
1071
1272
  ca_stack_mask_settle_after (CAStack *ca, const void *data, ca_size_t n)
1072
1273
  {
@@ -1078,10 +1279,7 @@ ca_stack_mask_settle_after (CAStack *ca, const void *data, ca_size_t n)
1078
1279
  static void
1079
1280
  ca_stack_put_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
1080
1281
  {
1081
- if ( dir == CA_XFER_PUT ) {
1082
- ca_stack_put_t a = { CA_STACK_PUT_INDEX, ap, idx, 0, NULL, NULL, NULL, NULL, data };
1083
- ca_stack_mask_refuse_first(&a);
1084
- }
1282
+ if ( dir == CA_XFER_PUT ) ca_stack_mask_adopt_before(ap, data, 1);
1085
1283
  ca_stack_func_xfer_index(ap, idx, data, dir);
1086
1284
  if ( dir == CA_XFER_PUT ) ca_stack_mask_settle_after((CAStack *) ap, data, 1);
1087
1285
  }
@@ -1091,8 +1289,9 @@ ca_stack_put_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *data,
1091
1289
  int dir)
1092
1290
  {
1093
1291
  if ( dir == CA_XFER_PUT ) {
1094
- ca_stack_put_t a = { CA_STACK_PUT_ADDRS, ap, NULL, n, addrs, NULL, NULL, NULL, data };
1095
- ca_stack_mask_refuse_first(&a);
1292
+ CA_MULTI_PARENT_VALUE_TRIAL(ap, t,
1293
+ ca_stack_func_xfer_addrs(t, n, addrs, data, dir));
1294
+ ca_stack_mask_adopt_before(ap, data, n);
1096
1295
  }
1097
1296
  ca_stack_func_xfer_addrs(ap, n, addrs, data, dir);
1098
1297
  if ( dir == CA_XFER_PUT ) ca_stack_mask_settle_after((CAStack *) ap, data, n);
@@ -1103,8 +1302,10 @@ ca_stack_put_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
1103
1302
  ca_size_t *strides, void *data, int dir)
1104
1303
  {
1105
1304
  if ( dir == CA_XFER_PUT ) {
1106
- ca_stack_put_t a = { CA_STACK_PUT_STRIDE, ap, NULL, 0, NULL, starts, counts, strides, data };
1107
- ca_stack_mask_refuse_first(&a);
1305
+ CA_MULTI_PARENT_VALUE_TRIAL(ap, t,
1306
+ ca_stack_func_xfer_stride(t, starts, counts, strides, data, dir));
1307
+ ca_stack_mask_adopt_before(ap, data,
1308
+ ca_shape_elements(((CArray *) ap)->ndim, counts, 1));
1108
1309
  }
1109
1310
  ca_stack_func_xfer_stride(ap, starts, counts, strides, data, dir);
1110
1311
  if ( dir == CA_XFER_PUT ) {
@@ -1117,8 +1318,8 @@ static void
1117
1318
  ca_stack_put_xfer_all (void *ap, void *data, int dir)
1118
1319
  {
1119
1320
  if ( dir == CA_XFER_PUT ) {
1120
- ca_stack_put_t a = { CA_STACK_PUT_ALL, ap, NULL, 0, NULL, NULL, NULL, NULL, data };
1121
- ca_stack_mask_refuse_first(&a);
1321
+ CA_MULTI_PARENT_VALUE_TRIAL(ap, t, ca_stack_func_xfer_all(t, data, dir));
1322
+ ca_stack_mask_adopt_before(ap, data, ((CArray *) ap)->elements);
1122
1323
  }
1123
1324
  ca_stack_func_xfer_all(ap, data, dir);
1124
1325
  if ( dir == CA_XFER_PUT ) {
@@ -1129,23 +1330,17 @@ ca_stack_put_xfer_all (void *ap, void *data, int dir)
1129
1330
  static void
1130
1331
  ca_stack_put_fill_data (void *ap, void *ptr)
1131
1332
  {
1132
- {
1133
- ca_stack_put_t a = { CA_STACK_PUT_FILL, ap, NULL, 0, NULL, NULL, NULL, NULL, ptr };
1134
- ca_stack_mask_refuse_first(&a);
1135
- }
1333
+ CA_MULTI_PARENT_VALUE_TRIAL(ap, t, ca_stack_func_fill_data(t, ptr));
1334
+ ca_stack_mask_adopt_before(ap, ptr, 1);
1136
1335
  ca_stack_func_fill_data(ap, ptr);
1137
1336
  ca_stack_mask_settle_after((CAStack *) ap, ptr, 1);
1138
1337
  }
1139
1338
 
1140
- /* A fill of part of the mask goes cell by cell through xfer_index; run
1141
- the whole of it against blank masks first, so that a cell refused late
1142
- does not leave the cells before it written. */
1143
1339
  static void
1144
1340
  ca_stack_put_fill_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *ptr)
1145
1341
  {
1146
- ca_stack_put_t a = { CA_STACK_PUT_FILL_ADDRS, ap, NULL, n, addrs, NULL, NULL,
1147
- NULL, ptr };
1148
- ca_stack_mask_refuse_first(&a);
1342
+ CA_MULTI_PARENT_VALUE_TRIAL(ap, t, ca_fill_addrs_default(t, n, addrs, ptr));
1343
+ ca_stack_mask_adopt_before(ap, ptr, 1);
1149
1344
  ca_fill_addrs_default(ap, n, addrs, ptr);
1150
1345
  ca_stack_mask_settle_after((CAStack *) ap, ptr, 1);
1151
1346
  }
@@ -1154,9 +1349,9 @@ static void
1154
1349
  ca_stack_put_fill_stride (void *ap, ca_size_t base, int8_t ndim,
1155
1350
  ca_size_t *counts, ca_size_t *steps, void *ptr)
1156
1351
  {
1157
- ca_stack_put_t a = { CA_STACK_PUT_FILL_STRIDE, ap, NULL, 0, NULL, NULL, counts,
1158
- NULL, ptr, base, ndim, steps };
1159
- ca_stack_mask_refuse_first(&a);
1352
+ CA_MULTI_PARENT_VALUE_TRIAL(ap, t,
1353
+ ca_fill_stride_default(t, base, ndim, counts, steps, ptr));
1354
+ ca_stack_mask_adopt_before(ap, ptr, 1);
1160
1355
  ca_fill_stride_default(ap, base, ndim, counts, steps, ptr);
1161
1356
  ca_stack_mask_settle_after((CAStack *) ap, ptr, 1);
1162
1357
  }
@@ -1216,6 +1411,7 @@ rb_ca_stack_new_with_axis (VALUE parents_ary, int8_t k_axis)
1216
1411
  VALUE p = rb_ary_entry(parents_ary, i);
1217
1412
  rb_check_carray_object(p);
1218
1413
  TypedData_Get_Struct(p, CArray, &carray_data_type, parents[i]);
1414
+ ca_multi_parent_check_part(parents[i], "stack");
1219
1415
  }
1220
1416
  ca = ca_stack_new_with_axis((int32_t) n, parents, k_axis);
1221
1417
  obj = ca_wrap_struct(ca);
@@ -1302,6 +1498,7 @@ rb_ca_stack_initialize (int argc, VALUE *argv, VALUE self)
1302
1498
  VALUE p = rb_ary_entry(list, i);
1303
1499
  rb_check_carray_object(p);
1304
1500
  TypedData_Get_Struct(p, CArray, &carray_data_type, parents[i]);
1501
+ ca_multi_parent_check_part(parents[i], "stack");
1305
1502
  }
1306
1503
  ca_stack_setup_with_axis(ca, (int32_t) n, parents, k_axis);
1307
1504
  rb_ivar_set(self, id_parents, rb_ary_dup(list)); /* GC anchor */
@@ -1379,6 +1576,7 @@ rb_ca_stack_k_axis (VALUE self)
1379
1576
  void
1380
1577
  Init_ca_obj_stack (void)
1381
1578
  {
1579
+ ca_value_trial_depth_key = rb_ractor_local_storage_value_newkey();
1382
1580
  /* CAStack must match the CAMultiParent layout convention so generic code
1383
1581
  can fold over parents[] via the CA_FLAG_MULTI_PARENTS path. */
1384
1582
  if ( offsetof(CAStack, n_parents) != offsetof(CAMultiParent, n_parents) ||
@@ -1397,6 +1595,7 @@ Init_ca_obj_stack (void)
1397
1595
  &castack_data_type,
1398
1596
  rb_cCAStackMask,
1399
1597
  &castack_mask_data_type, &ca_stack_func, sizeof(ca_stack_func));
1598
+ ca_register_cell_moving_view(CA_OBJ_STACK);
1400
1599
  rb_define_const(rb_cObject, "CA_OBJ_STACK", INT2NUM(CA_OBJ_STACK));
1401
1600
 
1402
1601
  id_parents = rb_intern("parents");