carray 3.0.1 → 3.0.2

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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +520 -0
  3. data/README.md +2 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +33 -4
  6. data/ext/ca_axis_group.c +202 -96
  7. data/ext/ca_categorical_iterator.c +108 -54
  8. data/ext/ca_kernel_iterator.c +317 -51
  9. data/ext/ca_kernel_iterator.h +142 -35
  10. data/ext/ca_obj_array.c +62 -20
  11. data/ext/ca_obj_block.c +4 -4
  12. data/ext/ca_obj_const_string.c +85 -26
  13. data/ext/ca_obj_face.c +24 -0
  14. data/ext/ca_obj_face.h +15 -0
  15. data/ext/ca_obj_fixlen_string.c +18 -5
  16. data/ext/ca_obj_meld.c +123 -25
  17. data/ext/ca_obj_object.c +8 -0
  18. data/ext/ca_obj_select.c +49 -34
  19. data/ext/ca_obj_stack.c +3 -8
  20. data/ext/ca_obj_stride.c +72 -1
  21. data/ext/ca_obj_string.c +8 -4
  22. data/ext/ca_obj_window.c +8 -2
  23. data/ext/ca_op_ipower.c +1 -2
  24. data/ext/ca_rng_normal.h +42 -0
  25. data/ext/ca_rng_xoshiro256pp.h +105 -0
  26. data/ext/ca_sweep_engine.c +307 -143
  27. data/ext/ca_sweep_engine.h +26 -5
  28. data/ext/carray.h +21 -2
  29. data/ext/carray_access.c +32 -20
  30. data/ext/carray_address_basis.c +590 -0
  31. data/ext/carray_broadcast.c +3 -3
  32. data/ext/carray_call_cfunc.c +667 -483
  33. data/ext/carray_cast.c +115 -41
  34. data/ext/carray_copy.c +55 -30
  35. data/ext/carray_core.c +83 -3
  36. data/ext/carray_count.c +9 -10
  37. data/ext/carray_factorize.c +46 -25
  38. data/ext/carray_internal.h +17 -0
  39. data/ext/carray_kernels_reduce_aggregate.c +168 -0
  40. data/ext/carray_kernels_reduce_cumulative.c +270 -1
  41. data/ext/carray_kernels_reduce_extreme.c +554 -8
  42. data/ext/carray_kernels_scan.c +4 -4
  43. data/ext/carray_kernels_search.c +94 -14
  44. data/ext/carray_loop.c +7 -1
  45. data/ext/carray_mask.c +23 -8
  46. data/ext/carray_median_percentile.c +55 -0
  47. data/ext/carray_operator.c +4 -4
  48. data/ext/carray_order.c +1 -1
  49. data/ext/carray_random.c +384 -40
  50. data/ext/carray_slab.c +13 -0
  51. data/ext/carray_sort.c +20 -22
  52. data/ext/mk_call_cfunc.rb +103 -116
  53. data/ext/mkkernel.rb +297 -29
  54. data/ext/ruby_carray.c +10 -1
  55. data/ext/version.h +4 -4
  56. data/lib/carray/autoload_carray.rb +5 -3
  57. data/lib/carray/autoload_method_extension.rb +12 -0
  58. data/lib/carray/axis_group.rb +77 -0
  59. data/lib/carray/basics.rb +4 -0
  60. data/lib/carray/block_iterator.rb +92 -16
  61. data/lib/carray/categorical.rb +150 -33
  62. data/lib/carray/categorical_iterator.rb +207 -80
  63. data/lib/carray/const_string.rb +131 -27
  64. data/lib/carray/construct.rb +40 -0
  65. data/lib/carray/data_type_extension.rb +3 -0
  66. data/lib/carray/data_type_limits.rb +91 -0
  67. data/lib/carray/fixlen_string.rb +1 -1
  68. data/lib/carray/frame/csv_parser.rb +11 -4
  69. data/lib/carray/frame/frame.rb +81 -10
  70. data/lib/carray/frame/group.rb +36 -3
  71. data/lib/carray/frame/io.rb +67 -15
  72. data/lib/carray/frame/records.rb +18 -4
  73. data/lib/carray/frame/verbs.rb +14 -11
  74. data/lib/carray/inspect.rb +42 -9
  75. data/lib/carray/iterator.rb +143 -0
  76. data/lib/carray/lazy.rb +0 -37
  77. data/lib/carray/mask_gap_fill.rb +3 -1
  78. data/lib/carray/methods/discovery_along.rb +74 -0
  79. data/lib/carray/methods/factorize.rb +50 -0
  80. data/lib/carray/methods/is_in.rb +13 -2
  81. data/lib/carray/methods/locate_addr.rb +75 -2
  82. data/lib/carray/methods/mask_duplicates.rb +35 -1
  83. data/lib/carray/methods/nunique.rb +22 -1
  84. data/lib/carray/methods/repeat.rb +110 -0
  85. data/lib/carray/methods/unique.rb +41 -1
  86. data/lib/carray/rng.rb +86 -0
  87. data/lib/carray/slab_iterator.rb +58 -13
  88. data/lib/carray/string_operation_extension.rb +5 -1
  89. data/lib/carray/time.rb +18 -2
  90. data/lib/carray/window_iterator.rb +142 -20
  91. data/lib/carray.rb +2 -0
  92. data/yard-stubs/ca_obj_block.rb +2 -7
  93. data/yard-stubs/ca_obj_window.rb +10 -2
  94. data/yard-stubs/carray_access.rb +1 -1
  95. data/yard-stubs/carray_broadcast.rb +1 -1
  96. data/yard-stubs/carray_core.rb +0 -80
  97. data/yard-stubs/carray_count.rb +7 -2
  98. data/yard-stubs/carray_lazy.rb +205 -0
  99. data/yard-stubs/carray_math.rb +1486 -3
  100. data/yard-stubs/carray_median_percentile.rb +16 -2
  101. data/yard-stubs/carray_order.rb +9 -69
  102. data/yard-stubs/carray_slab.rb +9 -7
  103. data/yard-stubs/carray_sort.rb +7 -5
  104. metadata +9 -1
@@ -554,13 +554,54 @@ ca_iter_strip_storage_wrapper (CArray *src)
554
554
  }
555
555
 
556
556
  int
557
- ca_iter_state_init_l1 (ca_iter_state *st,
557
+ ca_iter_check_init (int rc)
558
+ {
559
+ const char *why;
560
+
561
+ if ( rc == CA_ITER_OK ) return rc;
562
+
563
+ switch ( rc ) {
564
+ case CA_ITER_ERR_NOT_CHEAP:
565
+ why = "the source cannot be read without materialising it";
566
+ break;
567
+ case CA_ITER_ERR_POLICY:
568
+ why = "the slab policy or the axes given do not apply to this source";
569
+ break;
570
+ case CA_ITER_ERR_FLAGS:
571
+ why = "this flag combination is not supported for this source";
572
+ break;
573
+ case CA_ITER_ERR_READONLY:
574
+ why = "the array is read-only and the kernel asked to write";
575
+ break;
576
+ case CA_ITER_ERR_MASK:
577
+ why = "the source carries a mask";
578
+ break;
579
+ case CA_ITER_ERR_MASK_NOT_ALLOWED:
580
+ why = "the kernel declared CA_KERNEL_NO_MASK and the source carries a mask";
581
+ break;
582
+ default:
583
+ why = "the request was refused";
584
+ break;
585
+ }
586
+ rb_raise(rb_eRuntimeError, "kernel iterator: %s (rc=%d)", why, rc);
587
+ return rc; /* not reached */
588
+ }
589
+
590
+ static int
591
+ ca_iter_state_init_l1_unprotected (ca_iter_state *st,
558
592
  struct _CArray *src,
559
593
  ca_slab_policy_t policy,
560
594
  int8_t *axes,
561
595
  int8_t naxes,
562
596
  uint32_t flags)
563
597
  {
598
+ /* Leave the state defined before anything can return. Every error
599
+ return below hands control back to a caller that may not look at the
600
+ code, so what it is left holding has to be a state that iterates zero
601
+ times rather than whatever its stack frame happened to contain. */
602
+ if ( st == NULL ) return CA_ITER_ERR_FLAGS;
603
+ memset(st, 0, sizeof(*st));
604
+
564
605
  /* PROPOSAL_CAFACE_PHASE_2 F.2.6 + PROPOSAL_LAZY_MARKER_LIFT Phase 0:
565
606
  storage-identical wrapper strip at entry (= same rationale as init_l2
566
607
  below). Strip before validate_inputs.
@@ -586,7 +627,6 @@ ca_iter_state_init_l1 (ca_iter_state *st,
586
627
 
587
628
  uint8_t src_kind = ca_iter_classify_source(src);
588
629
 
589
- memset(st, 0, sizeof(*st));
590
630
  st->src = src;
591
631
  st->src_kind = src_kind;
592
632
  st->level = 1;
@@ -740,14 +780,21 @@ ca_iter_state_init_l1 (ca_iter_state *st,
740
780
  return CA_ITER_OK;
741
781
  }
742
782
 
743
- int
744
- ca_iter_state_init_l2 (ca_iter_state *st,
783
+ static int
784
+ ca_iter_state_init_l2_unprotected (ca_iter_state *st,
745
785
  struct _CArray *src,
746
786
  ca_slab_policy_t policy,
747
787
  int8_t *axes,
748
788
  int8_t naxes,
749
789
  uint32_t flags)
750
790
  {
791
+ /* Leave the state defined before anything can return. Every error
792
+ return below hands control back to a caller that may not look at the
793
+ code, so what it is left holding has to be a state that iterates zero
794
+ times rather than whatever its stack frame happened to contain. */
795
+ if ( st == NULL ) return CA_ITER_ERR_FLAGS;
796
+ memset(st, 0, sizeof(*st));
797
+
751
798
  /* PROPOSAL_CAFACE_PHASE_2 F.2.6 (= MEMO §3.5 kernel_iterator entry strip)
752
799
  + PROPOSAL_LAZY_MARKER_LIFT Phase 0.
753
800
 
@@ -828,7 +875,6 @@ ca_iter_state_init_l2 (ca_iter_state *st,
828
875
  if ( ca_iter_should_per_fiber_fused(src, src_kind, fiber_ax,
829
876
  row_byte_strides[fiber_ax],
830
877
  flags) ) {
831
- memset(st, 0, sizeof(*st));
832
878
  st->src = src;
833
879
  st->src_kind = src_kind;
834
880
  st->level = 2;
@@ -922,7 +968,6 @@ ca_iter_state_init_l2 (ca_iter_state *st,
922
968
  return CA_ITER_ERR_FLAGS;
923
969
  }
924
970
 
925
- memset(st, 0, sizeof(*st));
926
971
  st->src = src;
927
972
  st->src_kind = CA_ITER_SRC_DESCRIPTOR;
928
973
  st->level = 2;
@@ -1091,7 +1136,6 @@ ca_iter_state_init_l2 (ca_iter_state *st,
1091
1136
  }
1092
1137
 
1093
1138
  if ( outer_has_shift ) {
1094
- memset(st, 0, sizeof(*st));
1095
1139
  st->src = src;
1096
1140
  st->src_kind = CA_ITER_SRC_DESCRIPTOR;
1097
1141
  st->level = 2;
@@ -1185,7 +1229,6 @@ ca_iter_state_init_l2 (ca_iter_state *st,
1185
1229
  return CA_ITER_OK;
1186
1230
  }
1187
1231
 
1188
- memset(st, 0, sizeof(*st));
1189
1232
  st->src = src;
1190
1233
  st->src_kind = CA_ITER_SRC_DESCRIPTOR; /* Phase B alias path */
1191
1234
  st->level = 2;
@@ -1333,7 +1376,6 @@ ca_iter_state_init_l2 (ca_iter_state *st,
1333
1376
  in_slab[ax] = 1;
1334
1377
  }
1335
1378
 
1336
- memset(st, 0, sizeof(*st));
1337
1379
  st->src = src;
1338
1380
  st->src_kind = CA_ITER_SRC_ATTACH;
1339
1381
  st->level = 2;
@@ -1386,6 +1428,7 @@ ca_iter_state_init_l2 (ca_iter_state *st,
1386
1428
  k_axis..N-2]; either way outer_idx[m] = parent axis m). */
1387
1429
  if ( ca_func[src->obj_type].attach == ca_stack_func.attach
1388
1430
  && !ca_has_mask(src)
1431
+ && !(flags & CA_KERNEL_WRITE)
1389
1432
  && naxes == 1 && axes[0] == ((CAStack *) src)->k_axis ) {
1390
1433
  CAStack *stack = (CAStack *) src;
1391
1434
  int8_t parent_ndim = src->ndim - 1;
@@ -1501,6 +1544,7 @@ ca_iter_state_init_l2 (ca_iter_state *st,
1501
1544
  s - 1); only the K stack axis (s == k_axis) has no parent
1502
1545
  counterpart. */
1503
1546
  if ( ca_func[src->obj_type].attach == ca_stack_func.attach
1547
+ && !(flags & CA_KERNEL_WRITE)
1504
1548
  && naxes >= 1 && !in_slab[((CAStack *) src)->k_axis] ) {
1505
1549
  CAStack *stack = (CAStack *) src;
1506
1550
  int8_t k_axis = stack->k_axis;
@@ -1721,7 +1765,6 @@ ca_iter_state_init_l2 (ca_iter_state *st,
1721
1765
  / CATile / CARoll). Same shape as L1 SRC_ATTACH: iterator-owns
1722
1766
  scratch + xfer_all GET/PUT. Yield as one 1-D strided slab. */
1723
1767
  if ( src_kind == CA_ITER_SRC_ATTACH ) {
1724
- memset(st, 0, sizeof(*st));
1725
1768
  st->src = src;
1726
1769
  st->src_kind = CA_ITER_SRC_ATTACH;
1727
1770
  st->level = 2;
@@ -1773,7 +1816,6 @@ ca_iter_state_init_l2 (ca_iter_state *st,
1773
1816
  int8_t nd = raw_ndim;
1774
1817
  int8_t k;
1775
1818
 
1776
- memset(st, 0, sizeof(*st));
1777
1819
  st->src = src;
1778
1820
  st->src_kind = CA_ITER_SRC_DESCRIPTOR_L2_ALIASABLE;
1779
1821
  st->level = 2;
@@ -1864,7 +1906,6 @@ ca_iter_state_init_l2 (ca_iter_state *st,
1864
1906
  /* raw_descs / raw_pdims / raw_ndim were populated by route_source
1865
1907
  above so we skip the local describe_axes call. Kept locals
1866
1908
  named raw_* to match the original code. */
1867
- memset(st, 0, sizeof(*st));
1868
1909
  st->src = src;
1869
1910
  st->src_kind = CA_ITER_SRC_DESCRIPTOR;
1870
1911
  st->level = 2;
@@ -1938,7 +1979,6 @@ ca_iter_state_init_l2 (ca_iter_state *st,
1938
1979
  }
1939
1980
 
1940
1981
  /* === CAStride / entity L2 path === */
1941
- memset(st, 0, sizeof(*st));
1942
1982
  st->src = src;
1943
1983
  st->src_kind = CA_ITER_SRC_CASTRIDE;
1944
1984
  st->level = 2;
@@ -2139,8 +2179,8 @@ ca_iter_state_init_l2 (ca_iter_state *st,
2139
2179
  return CA_ITER_OK;
2140
2180
  }
2141
2181
 
2142
- int
2143
- ca_iter_state_next_slab (ca_iter_state *st,
2182
+ static int
2183
+ ca_iter_state_next_slab_unprotected (ca_iter_state *st,
2144
2184
  char **out_ptr,
2145
2185
  boolean8_t **out_mask,
2146
2186
  ca_size_t *out_n)
@@ -2162,8 +2202,8 @@ ca_iter_state_next_slab (ca_iter_state *st,
2162
2202
  return 1;
2163
2203
  }
2164
2204
 
2165
- int
2166
- ca_iter_state_next_slab_strided (ca_iter_state *st,
2205
+ static int
2206
+ ca_iter_state_next_slab_strided_unprotected (ca_iter_state *st,
2167
2207
  char **out_ptr,
2168
2208
  boolean8_t **out_mask,
2169
2209
  ca_size_t *out_n,
@@ -2231,8 +2271,8 @@ ca_iter_state_next_slab_strided (ca_iter_state *st,
2231
2271
  return 1;
2232
2272
  }
2233
2273
 
2234
- int
2235
- ca_iter_state_next_slab_axes (ca_iter_state *st,
2274
+ static int
2275
+ ca_iter_state_next_slab_axes_unprotected (ca_iter_state *st,
2236
2276
  char **out_ptr,
2237
2277
  boolean8_t **out_mask)
2238
2278
  {
@@ -2742,38 +2782,22 @@ ca_iter_state_next_slab_axes (ca_iter_state *st,
2742
2782
  return 1;
2743
2783
  }
2744
2784
 
2745
- void
2746
- ca_iter_state_sync_slab (ca_iter_state *st)
2785
+ static void
2786
+ ca_iter_state_sync_slab_unprotected (ca_iter_state *st)
2747
2787
  {
2748
2788
  /* READ walk: nothing to sync. */
2749
2789
  if ( st == NULL || !(st->flags & CA_KERNEL_WRITE) ) return;
2750
2790
 
2751
- /* PROPOSAL_CASTACK_LOOP_INTERCHANGE Vector A rev2: STACK path is
2752
- READ-only in initial scope. Kernel writes into scratch would not
2753
- be valid to scatter back via xfer_all PUT (= scratch is slab-sized
2754
- not whole-view), so the SRC_ATTACH PUT below would be a semantic
2755
- mismatch. Per-slab scatter via direct per-parent memcpy is a
2756
- future extension once a WRITE-using kernel materialises. */
2791
+ /* Neither stack mode can be reached with WRITE set: both are declined
2792
+ at init, which sends a write destination down the generic SRC_ATTACH
2793
+ path instead. They cannot serve a write themselves -- ALIAS_STACK
2794
+ hands out a tile cache that finish frees, and STACK_OUTER_K aliases
2795
+ parents[k]->ptr, which is the attach buffer rather than the parent's
2796
+ own memory whenever the parent is a view. Scattering per parent
2797
+ would let the fast paths carry writes too; until then this branch is
2798
+ just the assertion that they are not asked to. */
2757
2799
  if ( st->alias_mode == CA_ITER_ALIAS_STACK
2758
2800
  || st->alias_mode == CA_ITER_ALIAS_STACK_OUTER_K ) {
2759
- /* STACK_OUTER_K (P.2 Case A) is READ-only scope: slabs are direct
2760
- aliases into parents[k]->ptr, kernel WRITE would scatter into
2761
- parent memory which is out of scope (see proposal R1). Skip
2762
- sync. */
2763
- st->write_dirty = 0;
2764
- return;
2765
- }
2766
-
2767
- /* SRC_ATTACH path (step 9 + 2026-05-31 refactor): kernel wrote into
2768
- iterator-owned scratch (= scratch_ptr). Push back via xfer_all PUT
2769
- which routes through the view's xfer_all slot -- handles CAFake
2770
- (cast back), CAByteSwap (swap back), CABitfield/CABitarray (bit
2771
- pack back), CAReduce (broadcast across reduce window). Inherits
2772
- transform-fused / partial materialise / etc. automatically. */
2773
- if ( st->src_kind == CA_ITER_SRC_ATTACH ) {
2774
- if ( st->src->elements > 0 ) {
2775
- ca_xfer_all(st->src, st->scratch_ptr, CA_XFER_PUT);
2776
- }
2777
2801
  st->write_dirty = 0;
2778
2802
  return;
2779
2803
  }
@@ -2852,12 +2876,32 @@ ca_iter_state_sync_slab (ca_iter_state *st)
2852
2876
  ca_stride_scatter_run(dst, st->fiber_data_scratch,
2853
2877
  bytes, n, data_step);
2854
2878
  }
2855
- /* Fall through to any subsequent src_kind scatter (= harmless: for
2856
- the alias paths reached here, scratch_ptr is NULL and the switch
2857
- below early-returns). But for SRC_DESCRIPTOR / SRC_ATTACH that
2858
- use the per-slab materialise path, fiber_data_scratch stays NULL
2859
- (those paths use scratch_ptr and the PER_SLAB(_HOIST) yield), so
2860
- this block does not fire. */
2879
+ /* Fall through. The gather above is not specific to a src_kind --
2880
+ it fires for any source whose fiber is not innermost-contig -- so
2881
+ for SRC_ATTACH this has just written the fiber back into the
2882
+ whole-view scratch, and the push-back below carries it to the
2883
+ source. For the alias paths scratch_ptr is NULL and the switch
2884
+ below early-returns. */
2885
+ }
2886
+
2887
+ /* SRC_ATTACH path (step 9 + 2026-05-31 refactor): kernel wrote into
2888
+ iterator-owned scratch (= scratch_ptr). Push back via xfer_all PUT
2889
+ which routes through the view's xfer_all slot -- handles CAFake
2890
+ (cast back), CAByteSwap (swap back), CABitfield/CABitarray (bit
2891
+ pack back), CAReduce (broadcast across reduce window). Inherits
2892
+ transform-fused / partial materialise / etc. automatically.
2893
+
2894
+ This has to stay below the two per-fiber blocks: consulting
2895
+ src_kind first would push back the whole-view buffer while the
2896
+ fiber the author actually wrote sat unscattered in
2897
+ fiber_data_scratch, and would push back a NULL one for
2898
+ PER_FIBER_FUSED, which owns no whole-view buffer at all. */
2899
+ if ( st->src_kind == CA_ITER_SRC_ATTACH ) {
2900
+ if ( st->src->elements > 0 ) {
2901
+ ca_xfer_all(st->src, st->scratch_ptr, CA_XFER_PUT);
2902
+ }
2903
+ st->write_dirty = 0;
2904
+ return;
2861
2905
  }
2862
2906
 
2863
2907
  /* alias path: kernel wrote through alias_ptr into parent directly
@@ -2996,6 +3040,228 @@ ca_iter_state_finish (ca_iter_state *st)
2996
3040
  st->alias_ptr = NULL;
2997
3041
  }
2998
3042
 
3043
+ /* ---- Raise-safe entry points ----------------------------------------
3044
+ *
3045
+ * A walk reads its source, and a source that converts as it is read (a
3046
+ * lazy view over an object array, say) raises when it meets a cell it
3047
+ * cannot convert. The caller's loop then never reaches
3048
+ * ca_iter_state_finish -- the block macros put it in the `for`
3049
+ * increment, which a raise jumps over, and it is past the raise in
3050
+ * hand-written walks too. So the walk finishes itself on the way out.
3051
+ *
3052
+ * Every entry point that can raise is called through rb_protect here,
3053
+ * and ca_iter_state_finish is idempotent (it clears st->src), so the
3054
+ * caller's own finish after an ordinary walk stays correct.
3055
+ *
3056
+ * Not covered: a kernel body that raises. It runs in the caller's
3057
+ * frame, between two calls of these functions, where the engine has no
3058
+ * hold on it. An object-lane kernel calling back into Ruby is the case
3059
+ * to watch.
3060
+ *
3061
+ * init runs protected always -- once per walk, next to a materialise.
3062
+ * next_slab / sync_slab are called once per slab, in walks whose whole
3063
+ * per-fiber cost can be a few nanoseconds, so they are protected only
3064
+ * when this walk can raise at all: when it gathers each fiber through
3065
+ * the source's own transfer slots, or when it writes scratch back
3066
+ * through them. Every other shape hands out pointers into memory that
3067
+ * init already materialised, and moves it with memcpy. */
3068
+
3069
+ /* Private flags bit, kept in ca_iter_state.flags rather than a field of
3070
+ its own: the state is stack-allocated by ext authors through the block
3071
+ macros, so growing it would overrun states built against an older
3072
+ header. */
3073
+ #define CA_ITER_FLAG_WALK_MAY_RAISE 0x80000000u
3074
+
3075
+ typedef struct {
3076
+ ca_iter_state *st;
3077
+ CArray *src;
3078
+ ca_slab_policy_t policy;
3079
+ int8_t *axes;
3080
+ int8_t naxes;
3081
+ uint32_t flags;
3082
+ char **out_ptr;
3083
+ boolean8_t **out_mask;
3084
+ ca_size_t *out_n;
3085
+ ca_size_t *out_stride_bytes;
3086
+ int rc;
3087
+ } ca_iter_call_t;
3088
+
3089
+ /* Whether next_slab / sync_slab on this walk need the protection. */
3090
+ static void
3091
+ ca_iter_mark_walk_may_raise (ca_iter_state *st)
3092
+ {
3093
+ if ( st == NULL ) return;
3094
+ if ( st->alias_mode == CA_ITER_ALIAS_PER_FIBER_FUSED ) {
3095
+ /* gathers each fiber through the source's xfer_stride slot */
3096
+ st->flags |= CA_ITER_FLAG_WALK_MAY_RAISE;
3097
+ }
3098
+ else if ( (st->flags & CA_KERNEL_WRITE)
3099
+ && (st->scratch_ptr != NULL
3100
+ || st->src_kind == CA_ITER_SRC_ATTACH) ) {
3101
+ /* writes back out of iterator-owned scratch, through the source's
3102
+ xfer_all / sync_data slot. A write into aliased memory needs no
3103
+ push-back at all: sync_slab returns at once. */
3104
+ st->flags |= CA_ITER_FLAG_WALK_MAY_RAISE;
3105
+ }
3106
+ }
3107
+
3108
+ static void
3109
+ ca_iter_protect (ca_iter_state *st, VALUE (*body)(VALUE), VALUE arg)
3110
+ {
3111
+ int tag = 0;
3112
+ rb_protect(body, arg, &tag);
3113
+ if ( tag ) {
3114
+ ca_iter_state_finish(st);
3115
+ rb_jump_tag(tag);
3116
+ }
3117
+ }
3118
+
3119
+ static VALUE
3120
+ ca_iter_call_init_l1 (VALUE arg)
3121
+ {
3122
+ ca_iter_call_t *c = (ca_iter_call_t *) arg;
3123
+ c->rc = ca_iter_state_init_l1_unprotected(c->st, c->src, c->policy,
3124
+ c->axes, c->naxes, c->flags);
3125
+ return Qnil;
3126
+ }
3127
+
3128
+ static VALUE
3129
+ ca_iter_call_init_l2 (VALUE arg)
3130
+ {
3131
+ ca_iter_call_t *c = (ca_iter_call_t *) arg;
3132
+ c->rc = ca_iter_state_init_l2_unprotected(c->st, c->src, c->policy,
3133
+ c->axes, c->naxes, c->flags);
3134
+ return Qnil;
3135
+ }
3136
+
3137
+ static VALUE
3138
+ ca_iter_call_next_slab (VALUE arg)
3139
+ {
3140
+ ca_iter_call_t *c = (ca_iter_call_t *) arg;
3141
+ c->rc = ca_iter_state_next_slab_unprotected(c->st, c->out_ptr,
3142
+ c->out_mask, c->out_n);
3143
+ return Qnil;
3144
+ }
3145
+
3146
+ static VALUE
3147
+ ca_iter_call_next_slab_strided (VALUE arg)
3148
+ {
3149
+ ca_iter_call_t *c = (ca_iter_call_t *) arg;
3150
+ c->rc = ca_iter_state_next_slab_strided_unprotected(c->st, c->out_ptr,
3151
+ c->out_mask, c->out_n,
3152
+ c->out_stride_bytes);
3153
+ return Qnil;
3154
+ }
3155
+
3156
+ static VALUE
3157
+ ca_iter_call_next_slab_axes (VALUE arg)
3158
+ {
3159
+ ca_iter_call_t *c = (ca_iter_call_t *) arg;
3160
+ c->rc = ca_iter_state_next_slab_axes_unprotected(c->st, c->out_ptr,
3161
+ c->out_mask);
3162
+ return Qnil;
3163
+ }
3164
+
3165
+ static VALUE
3166
+ ca_iter_call_sync_slab (VALUE arg)
3167
+ {
3168
+ ca_iter_call_t *c = (ca_iter_call_t *) arg;
3169
+ ca_iter_state_sync_slab_unprotected(c->st);
3170
+ return Qnil;
3171
+ }
3172
+
3173
+ int
3174
+ ca_iter_state_init_l1 (ca_iter_state *st,
3175
+ struct _CArray *src,
3176
+ ca_slab_policy_t policy,
3177
+ int8_t *axes,
3178
+ int8_t naxes,
3179
+ uint32_t flags)
3180
+ {
3181
+ ca_iter_call_t c;
3182
+ c.st = st; c.src = src; c.policy = policy;
3183
+ c.axes = axes; c.naxes = naxes; c.flags = flags; c.rc = CA_ITER_OK;
3184
+ ca_iter_protect(st, ca_iter_call_init_l1, (VALUE) &c);
3185
+ if ( c.rc == CA_ITER_OK ) ca_iter_mark_walk_may_raise(st);
3186
+ return c.rc;
3187
+ }
3188
+
3189
+ int
3190
+ ca_iter_state_init_l2 (ca_iter_state *st,
3191
+ struct _CArray *src,
3192
+ ca_slab_policy_t policy,
3193
+ int8_t *axes,
3194
+ int8_t naxes,
3195
+ uint32_t flags)
3196
+ {
3197
+ ca_iter_call_t c;
3198
+ c.st = st; c.src = src; c.policy = policy;
3199
+ c.axes = axes; c.naxes = naxes; c.flags = flags; c.rc = CA_ITER_OK;
3200
+ ca_iter_protect(st, ca_iter_call_init_l2, (VALUE) &c);
3201
+ if ( c.rc == CA_ITER_OK ) ca_iter_mark_walk_may_raise(st);
3202
+ return c.rc;
3203
+ }
3204
+
3205
+ int
3206
+ ca_iter_state_next_slab (ca_iter_state *st,
3207
+ char **out_ptr,
3208
+ boolean8_t **out_mask,
3209
+ ca_size_t *out_n)
3210
+ {
3211
+ ca_iter_call_t c;
3212
+ if ( st == NULL || !(st->flags & CA_ITER_FLAG_WALK_MAY_RAISE) ) {
3213
+ return ca_iter_state_next_slab_unprotected(st, out_ptr, out_mask, out_n);
3214
+ }
3215
+ c.st = st; c.out_ptr = out_ptr; c.out_mask = out_mask; c.out_n = out_n;
3216
+ c.rc = 0;
3217
+ ca_iter_protect(st, ca_iter_call_next_slab, (VALUE) &c);
3218
+ return c.rc;
3219
+ }
3220
+
3221
+ int
3222
+ ca_iter_state_next_slab_strided (ca_iter_state *st,
3223
+ char **out_ptr,
3224
+ boolean8_t **out_mask,
3225
+ ca_size_t *out_n,
3226
+ ca_size_t *out_stride_bytes)
3227
+ {
3228
+ ca_iter_call_t c;
3229
+ if ( st == NULL || !(st->flags & CA_ITER_FLAG_WALK_MAY_RAISE) ) {
3230
+ return ca_iter_state_next_slab_strided_unprotected(st, out_ptr, out_mask,
3231
+ out_n, out_stride_bytes);
3232
+ }
3233
+ c.st = st; c.out_ptr = out_ptr; c.out_mask = out_mask; c.out_n = out_n;
3234
+ c.out_stride_bytes = out_stride_bytes; c.rc = 0;
3235
+ ca_iter_protect(st, ca_iter_call_next_slab_strided, (VALUE) &c);
3236
+ return c.rc;
3237
+ }
3238
+
3239
+ int
3240
+ ca_iter_state_next_slab_axes (ca_iter_state *st,
3241
+ char **out_ptr,
3242
+ boolean8_t **out_mask)
3243
+ {
3244
+ ca_iter_call_t c;
3245
+ if ( st == NULL || !(st->flags & CA_ITER_FLAG_WALK_MAY_RAISE) ) {
3246
+ return ca_iter_state_next_slab_axes_unprotected(st, out_ptr, out_mask);
3247
+ }
3248
+ c.st = st; c.out_ptr = out_ptr; c.out_mask = out_mask; c.rc = 0;
3249
+ ca_iter_protect(st, ca_iter_call_next_slab_axes, (VALUE) &c);
3250
+ return c.rc;
3251
+ }
3252
+
3253
+ void
3254
+ ca_iter_state_sync_slab (ca_iter_state *st)
3255
+ {
3256
+ ca_iter_call_t c;
3257
+ if ( st == NULL || !(st->flags & CA_ITER_FLAG_WALK_MAY_RAISE) ) {
3258
+ ca_iter_state_sync_slab_unprotected(st);
3259
+ return;
3260
+ }
3261
+ c.st = st;
3262
+ ca_iter_protect(st, ca_iter_call_sync_slab, (VALUE) &c);
3263
+ }
3264
+
2999
3265
  #ifdef CARRAY_DEV_BUILD
3000
3266
  /* ============================================================
3001
3267
  * smoke surface (dev-only, stripped in release)