carray 3.0.2 → 3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +304 -0
  3. data/README.md +3 -4
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +225 -0
  6. data/ext/ca_axis_group.c +177 -99
  7. data/ext/ca_binop_dispatch.c +54 -32
  8. data/ext/ca_binop_dispatch.h +1 -0
  9. data/ext/ca_categorical_iterator.c +66 -71
  10. data/ext/ca_compare.c +8 -2
  11. data/ext/ca_for_buffer.h +21 -68
  12. data/ext/ca_group_iter.c +27 -11
  13. data/ext/ca_kernel_iterator.c +663 -214
  14. data/ext/ca_kernel_iterator.h +231 -167
  15. data/ext/ca_monop_dispatch.c +4 -4
  16. data/ext/ca_monop_dispatch.h +4 -0
  17. data/ext/ca_obj_array.c +132 -22
  18. data/ext/ca_obj_bincmp.c +152 -121
  19. data/ext/ca_obj_binop.c +107 -210
  20. data/ext/ca_obj_bitarray.c +37 -24
  21. data/ext/ca_obj_bitfield.c +95 -62
  22. data/ext/ca_obj_block.c +1 -0
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +96 -25
  25. data/ext/ca_obj_face.c +406 -64
  26. data/ext/ca_obj_face.h +92 -47
  27. data/ext/ca_obj_fake.c +21 -50
  28. data/ext/ca_obj_farray.c +1 -0
  29. data/ext/ca_obj_field.c +16 -3
  30. data/ext/ca_obj_fixlen_string.c +21 -21
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +189 -107
  33. data/ext/ca_obj_moncmp.c +61 -61
  34. data/ext/ca_obj_monop.c +125 -107
  35. data/ext/ca_obj_object.c +543 -233
  36. data/ext/ca_obj_record.c +51 -16
  37. data/ext/ca_obj_reduce.c +5 -8
  38. data/ext/ca_obj_refer.c +117 -33
  39. data/ext/ca_obj_remap.c +57 -17
  40. data/ext/ca_obj_repeat.c +21 -5
  41. data/ext/ca_obj_roll.c +115 -11
  42. data/ext/ca_obj_select.c +47 -49
  43. data/ext/ca_obj_select_axis.c +58 -60
  44. data/ext/ca_obj_shift.c +22 -102
  45. data/ext/ca_obj_stack.c +223 -118
  46. data/ext/ca_obj_stride.c +195 -34
  47. data/ext/ca_obj_string.c +17 -18
  48. data/ext/ca_obj_tile.c +32 -16
  49. data/ext/ca_obj_time.c +23 -25
  50. data/ext/ca_obj_timedelta.c +21 -23
  51. data/ext/ca_obj_transpose.c +12 -3
  52. data/ext/ca_obj_triop.c +110 -117
  53. data/ext/ca_obj_window.c +149 -27
  54. data/ext/ca_op_ipower.c +24 -11
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_sort_kernels.h +70 -0
  58. data/ext/ca_sweep_engine.c +102 -79
  59. data/ext/ca_sweep_engine.h +7 -20
  60. data/ext/ca_triop_dispatch.c +4 -1
  61. data/ext/ca_triop_dispatch.h +5 -2
  62. data/ext/caframe_csv_reader.c +463 -0
  63. data/ext/carray.h +70 -27
  64. data/ext/carray_access.c +455 -211
  65. data/ext/carray_address_basis.c +177 -36
  66. data/ext/carray_attribute.c +45 -3
  67. data/ext/carray_bincount.c +32 -2
  68. data/ext/carray_broadcast.c +46 -14
  69. data/ext/carray_call_cfunc.c +0 -28
  70. data/ext/carray_cast.c +1444 -209
  71. data/ext/carray_cast_func.rb +34 -21
  72. data/ext/carray_conversion.c +197 -73
  73. data/ext/carray_copy.c +32 -11
  74. data/ext/carray_core.c +514 -122
  75. data/ext/carray_count.c +140 -87
  76. data/ext/carray_element.c +18 -27
  77. data/ext/carray_factorize.c +569 -277
  78. data/ext/carray_generate.c +76 -40
  79. data/ext/carray_histogram.c +152 -94
  80. data/ext/carray_hold.c +2 -1
  81. data/ext/carray_index_classifier.c +63 -96
  82. data/ext/carray_internal.h +385 -3
  83. data/ext/carray_lazy.c +834 -79
  84. data/ext/carray_loop.c +81 -65
  85. data/ext/carray_mask.c +682 -316
  86. data/ext/carray_math_kernel.h +1 -0
  87. data/ext/carray_median_percentile.c +221 -76
  88. data/ext/carray_memory_view.c +254 -248
  89. data/ext/carray_operator.c +1089 -359
  90. data/ext/carray_order.c +133 -69
  91. data/ext/carray_partition.c +102 -56
  92. data/ext/carray_random.c +273 -239
  93. data/ext/carray_scatter.c +308 -65
  94. data/ext/carray_segment.c +257 -0
  95. data/ext/carray_slab.c +170 -70
  96. data/ext/carray_sort.c +215 -218
  97. data/ext/carray_sort_kernel.c +8 -1
  98. data/ext/carray_struct.c +99 -21
  99. data/ext/carray_test.c +167 -64
  100. data/ext/carray_time_text.c +1182 -0
  101. data/ext/carray_utils.c +363 -98
  102. data/ext/extconf.rb +27 -2
  103. data/ext/mk_call_cfunc.rb +0 -2
  104. data/ext/mk_pow5_table.rb +35 -0
  105. data/ext/mkkernel.rb +1076 -846
  106. data/ext/ruby_carray.c +7 -1
  107. data/ext/version.h +4 -4
  108. data/ext/version.rb +4 -2
  109. data/lib/carray/attribute.rb +39 -0
  110. data/lib/carray/autoload_carray.rb +19 -0
  111. data/lib/carray/autoload_method_extension.rb +6 -0
  112. data/lib/carray/axis_group.rb +174 -26
  113. data/lib/carray/basics.rb +91 -25
  114. data/lib/carray/bincount_nd.rb +6 -0
  115. data/lib/carray/block_iterator.rb +118 -48
  116. data/lib/carray/boolean_reduce.rb +21 -29
  117. data/lib/carray/categorical.rb +94 -26
  118. data/lib/carray/categorical_iterator.rb +333 -826
  119. data/lib/carray/complex.rb +36 -29
  120. data/lib/carray/conditional.rb +94 -7
  121. data/lib/carray/const_string.rb +46 -0
  122. data/lib/carray/frame/concat.rb +157 -6
  123. data/lib/carray/frame/convert.rb +13 -7
  124. data/lib/carray/frame/csv_parser.rb +589 -82
  125. data/lib/carray/frame/describe.rb +79 -0
  126. data/lib/carray/frame/frame.rb +124 -25
  127. data/lib/carray/frame/group.rb +189 -63
  128. data/lib/carray/frame/io.rb +412 -72
  129. data/lib/carray/frame/join.rb +134 -46
  130. data/lib/carray/frame/records.rb +37 -16
  131. data/lib/carray/frame/reshape.rb +448 -0
  132. data/lib/carray/frame/sort.rb +26 -2
  133. data/lib/carray/frame/verbs.rb +715 -48
  134. data/lib/carray/frame.rb +2 -0
  135. data/lib/carray/fuse_source.rb +111 -2
  136. data/lib/carray/fusion.rb +229 -38
  137. data/lib/carray/inspect.rb +29 -11
  138. data/lib/carray/iterator.rb +14 -2
  139. data/lib/carray/lazy.rb +54 -30
  140. data/lib/carray/mask_gap_fill.rb +66 -52
  141. data/lib/carray/math.rb +32 -19
  142. data/lib/carray/meld_reduce.rb +40 -7
  143. data/lib/carray/methods/align_addr.rb +8 -4
  144. data/lib/carray/methods/bincount.rb +2 -2
  145. data/lib/carray/methods/bit_string.rb +14 -3
  146. data/lib/carray/methods/composition.rb +16 -10
  147. data/lib/carray/methods/factorize.rb +2 -2
  148. data/lib/carray/methods/gather_nd.rb +13 -2
  149. data/lib/carray/methods/insert_block.rb +9 -14
  150. data/lib/carray/methods/is_in.rb +117 -4
  151. data/lib/carray/methods/join.rb +1 -1
  152. data/lib/carray/methods/locate_addr.rb +177 -36
  153. data/lib/carray/methods/mode.rb +18 -1
  154. data/lib/carray/methods/repeat.rb +1 -20
  155. data/lib/carray/methods/resize.rb +21 -23
  156. data/lib/carray/methods/snap.rb +34 -12
  157. data/lib/carray/methods/unique.rb +11 -2
  158. data/lib/carray/record_order.rb +208 -0
  159. data/lib/carray/runtime.rb +14 -11
  160. data/lib/carray/segment_iterator.rb +990 -0
  161. data/lib/carray/serialize.rb +197 -27
  162. data/lib/carray/slab_iterator.rb +51 -27
  163. data/lib/carray/stack.rb +10 -4
  164. data/lib/carray/string_operation_extension.rb +4 -3
  165. data/lib/carray/struct.rb +142 -51
  166. data/lib/carray/struct_builder.rb +110 -14
  167. data/lib/carray/table.rb +2 -2
  168. data/lib/carray/time.rb +517 -103
  169. data/lib/carray/window_iterator.rb +127 -72
  170. data/lib/carray.rb +2 -2
  171. data/yard-stubs/ca_obj_array.rb +4 -4
  172. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  173. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  174. data/yard-stubs/ca_obj_fake.rb +1 -1
  175. data/yard-stubs/ca_obj_field.rb +1 -1
  176. data/yard-stubs/ca_obj_refer.rb +3 -3
  177. data/yard-stubs/ca_obj_roll.rb +0 -1
  178. data/yard-stubs/ca_obj_stride.rb +10 -7
  179. data/yard-stubs/ca_obj_transpose.rb +1 -1
  180. data/yard-stubs/ca_obj_window.rb +1 -2
  181. data/yard-stubs/carray_access.rb +5 -4
  182. data/yard-stubs/carray_broadcast.rb +1 -1
  183. data/yard-stubs/carray_cast.rb +78 -43
  184. data/yard-stubs/carray_conversion.rb +13 -1
  185. data/yard-stubs/carray_copy.rb +8 -3
  186. data/yard-stubs/carray_core.rb +3 -3
  187. data/yard-stubs/carray_count.rb +9 -0
  188. data/yard-stubs/carray_lazy.rb +5 -1
  189. data/yard-stubs/carray_loop.rb +4 -1
  190. data/yard-stubs/carray_mask.rb +34 -10
  191. data/yard-stubs/carray_math.rb +25 -13
  192. data/yard-stubs/carray_median_percentile.rb +10 -6
  193. data/yard-stubs/carray_order.rb +34 -13
  194. data/yard-stubs/carray_scatter.rb +10 -3
  195. data/yard-stubs/carray_segment.rb +72 -0
  196. data/yard-stubs/carray_slab.rb +2 -1
  197. data/yard-stubs/carray_sort.rb +43 -3
  198. data/yard-stubs/carray_test.rb +10 -0
  199. data/yard-stubs/carray_utils.rb +0 -26
  200. metadata +11 -19
  201. data/ext/ca_for_each_element.h +0 -239
  202. data/ext/carray_build_flags.h +0 -3
  203. data/ext/carray_config.h +0 -28
  204. data/ext/carray_kernels_bincmp.c +0 -4446
  205. data/ext/carray_kernels_binop.c +0 -11001
  206. data/ext/carray_kernels_init.c +0 -1131
  207. data/ext/carray_kernels_map.c +0 -3467
  208. data/ext/carray_kernels_moncmp.c +0 -2097
  209. data/ext/carray_kernels_monop.c +0 -18313
  210. data/ext/carray_kernels_reduce_aggregate.c +0 -26005
  211. data/ext/carray_kernels_reduce_boolean.c +0 -330
  212. data/ext/carray_kernels_reduce_cumulative.c +0 -14862
  213. data/ext/carray_kernels_reduce_extreme.c +0 -17494
  214. data/ext/carray_kernels_reduce_variance.c +0 -3910
  215. data/ext/carray_kernels_scan.c +0 -3693
  216. data/ext/carray_kernels_search.c +0 -32218
  217. data/ext/carray_kernels_sort.c +0 -10626
  218. data/ext/carray_kernels_triop.c +0 -1392
@@ -0,0 +1,257 @@
1
+ /* ---------------------------------------------------------------------------
2
+
3
+ carray_segment.c
4
+
5
+ Conversions between the representations of a flat sequence cut into
6
+ consecutive segments:
7
+
8
+ lengths [2, 0, 3] one count per segment (k)
9
+ offsets [0, 2, 2, 5] every boundary, first and last included (k+1)
10
+ segment index [0, 0, 2, 2, 2] the segment each element belongs to (n)
11
+
12
+ CArray.segment_offsets(lengths: l) lengths -> offsets
13
+ CArray.segment_index(lengths: l) lengths -> segment index
14
+ CArray.segment_index(offsets: o) offsets -> segment index
15
+
16
+ Everything is computed in int64, so a total beyond 2**53 stays exact
17
+ (a cumsum would go through float64). Offsets need not start at 0: the
18
+ offsets of rows a..b cut out of a longer table start at offsets[a], and
19
+ the segment index then covers offsets[0]...offsets[-1] with the segments
20
+ numbered from 0.
21
+
22
+ Inputs are read through ca_copy_data straight into an int64 array that
23
+ Ruby already owns, so a refusal after reading leaks nothing.
24
+
25
+ --------------------------------------------------------------------------- */
26
+
27
+ #include "carray.h"
28
+
29
+ static ID id_lengths, id_offsets;
30
+
31
+ /* Read `v` (any integer or boolean CArray, or an Array of Integers) as
32
+ int64 into a fresh Ruby-owned array of `pad_front + v.elements`
33
+ elements, starting after `pad_front` cells. Refuses a non-integer
34
+ data type and masked cells, since neither names a count or a
35
+ position. */
36
+ static VALUE
37
+ segment_read_int64 (VALUE v, const char *name, int is_lengths,
38
+ ca_size_t pad_front, CArray **out_ca, ca_size_t *out_k)
39
+ {
40
+ CArray *src, *dst;
41
+ VALUE rsrc, rdst;
42
+ ca_size_t k;
43
+ const char *role = is_lengths ? "lengths" : "offsets";
44
+
45
+ if ( TYPE(v) == T_ARRAY ) {
46
+ long i;
47
+ for (i = 0; i < RARRAY_LEN(v); i++) {
48
+ if ( ! RB_INTEGER_TYPE_P(RARRAY_AREF(v, i)) ) {
49
+ rb_raise(rb_eArgError, "%s: %s must be integers", name, role);
50
+ }
51
+ }
52
+ }
53
+ else if ( ! rb_obj_is_carray(v) ) {
54
+ rb_raise(rb_eTypeError, "%s: %s must be a CArray or an Array of Integers",
55
+ name, role);
56
+ }
57
+ else {
58
+ GetCArray(v, src);
59
+ if ( ! ca_is_integer_type(src) && src->data_type != CA_BOOLEAN ) {
60
+ rb_raise(rb_eCADataTypeError, "%s: %s must be integers (got %s)",
61
+ name, role, ca_type_name[src->data_type]);
62
+ }
63
+ if ( ca_has_mask(src) &&
64
+ NUM2LL(rb_funcall(v, rb_intern("count_masked"), 0)) > 0 ) {
65
+ rb_raise(rb_eArgError, is_lengths ? "%s: a masked length is not a count"
66
+ : "%s: a masked offset is not a position",
67
+ name);
68
+ }
69
+ }
70
+
71
+ rsrc = rb_ca_wrap_readonly(v, INT2NUM(CA_INT64));
72
+ GetCArray(rsrc, src);
73
+ k = src->elements;
74
+ {
75
+ ca_size_t dim[1];
76
+ dim[0] = pad_front + k;
77
+ rdst = rb_carray_new(CA_INT64, 1, dim, 0, NULL);
78
+ }
79
+ GetCArray(rdst, dst);
80
+ if ( k > 0 ) {
81
+ ca_copy_data(src, dst->ptr + pad_front * sizeof(int64_t));
82
+ }
83
+ RB_GC_GUARD(rsrc);
84
+ *out_ca = dst;
85
+ *out_k = k;
86
+ return rdst;
87
+ }
88
+
89
+ /* Turn a lengths array read into o[1..k] into offsets in place, o[0] = 0. */
90
+ static void
91
+ segment_prefix_sum (int64_t *o, ca_size_t k, const char *name)
92
+ {
93
+ int64_t s = 0;
94
+ ca_size_t c;
95
+ o[0] = 0;
96
+ for (c = 1; c <= k; c++) {
97
+ int64_t l = o[c];
98
+ if ( l < 0 ) {
99
+ rb_raise(rb_eArgError, "%s: lengths must not be negative (got %lld at %lld)",
100
+ name, (long long) l, (long long) (c - 1));
101
+ }
102
+ if ( s > INT64_MAX - l ) {
103
+ rb_raise(rb_eRangeError, "%s: the total length overflows int64", name);
104
+ }
105
+ s += l;
106
+ o[c] = s;
107
+ }
108
+ }
109
+
110
+ /* Parse the one role keyword. Returns the value and sets *is_lengths. */
111
+ static VALUE
112
+ segment_role (int argc, VALUE *argv, const char *name, int allow_offsets,
113
+ int *is_lengths)
114
+ {
115
+ VALUE opts, vals[2];
116
+ ID ids[2];
117
+ int nids;
118
+
119
+ rb_scan_args(argc, argv, ":", &opts);
120
+ ids[0] = id_lengths;
121
+ ids[1] = id_offsets;
122
+ nids = allow_offsets ? 2 : 1;
123
+ vals[0] = vals[1] = Qundef;
124
+ rb_get_kwargs(opts, ids, 0, nids, vals);
125
+
126
+ if ( vals[0] != Qundef && nids == 2 && vals[1] != Qundef ) {
127
+ rb_raise(rb_eArgError, "%s: give lengths: or offsets:, not both", name);
128
+ }
129
+ if ( vals[0] != Qundef ) {
130
+ *is_lengths = 1;
131
+ return vals[0];
132
+ }
133
+ if ( nids == 2 && vals[1] != Qundef ) {
134
+ *is_lengths = 0;
135
+ return vals[1];
136
+ }
137
+ rb_raise(rb_eArgError, allow_offsets ? "%s: lengths: or offsets: is required"
138
+ : "%s: lengths: is required", name);
139
+ return Qnil;
140
+ }
141
+
142
+ /* CArray.segment_offsets(lengths: l) -> int64[k+1] */
143
+ static VALUE
144
+ rb_ca_s_segment_offsets (int argc, VALUE *argv, VALUE klass)
145
+ {
146
+ const char *name = "segment_offsets";
147
+ int is_lengths;
148
+ VALUE v, ro;
149
+ CArray *co;
150
+ ca_size_t k;
151
+
152
+ v = segment_role(argc, argv, name, 0, &is_lengths);
153
+ ro = segment_read_int64(v, name, 1, 1, &co, &k);
154
+ segment_prefix_sum((int64_t *) co->ptr, k, name);
155
+ return ro;
156
+ }
157
+
158
+ /* The boundaries named by the one role keyword, as a validated int64 array
159
+ of k+1 elements that Ruby owns: lengths are summed, offsets are checked
160
+ to be non-empty and non-decreasing. */
161
+ static VALUE
162
+ segment_bounds (int argc, VALUE *argv, const char *name, CArray **out_co,
163
+ ca_size_t *out_k)
164
+ {
165
+ int is_lengths;
166
+ VALUE v, ro;
167
+ CArray *co;
168
+ ca_size_t k, c;
169
+ int64_t *o;
170
+
171
+ v = segment_role(argc, argv, name, 1, &is_lengths);
172
+
173
+ if ( is_lengths ) {
174
+ ro = segment_read_int64(v, name, 1, 1, &co, &k);
175
+ segment_prefix_sum((int64_t *) co->ptr, k, name);
176
+ }
177
+ else {
178
+ ro = segment_read_int64(v, name, 0, 0, &co, &k);
179
+ if ( k == 0 ) {
180
+ rb_raise(rb_eArgError,
181
+ "%s: offsets need at least one element (the start of the first segment)",
182
+ name);
183
+ }
184
+ o = (int64_t *) co->ptr;
185
+ for (c = 1; c < k; c++) {
186
+ if ( o[c] < o[c-1] ) {
187
+ rb_raise(rb_eArgError,
188
+ "%s: offsets must not decrease (%lld after %lld at %lld)",
189
+ name, (long long) o[c], (long long) o[c-1], (long long) c);
190
+ }
191
+ }
192
+ k -= 1; /* k+1 boundaries -> k segments */
193
+ }
194
+ *out_co = co;
195
+ *out_k = k;
196
+ return ro;
197
+ }
198
+
199
+ /* CArray.segment_index(lengths: l) -> int64[l.sum]
200
+ CArray.segment_index(offsets: o) -> int64[o[-1] - o[0]] */
201
+ static VALUE
202
+ rb_ca_s_segment_index (int argc, VALUE *argv, VALUE klass)
203
+ {
204
+ VALUE ro, rout;
205
+ CArray *co, *cout;
206
+ ca_size_t k, c;
207
+ int64_t *o, *q, base, total;
208
+
209
+ ro = segment_bounds(argc, argv, "segment_index", &co, &k);
210
+ o = (int64_t *) co->ptr;
211
+
212
+ base = o[0];
213
+ total = o[k] - base;
214
+ {
215
+ ca_size_t dim[1];
216
+ dim[0] = (ca_size_t) total;
217
+ rout = rb_carray_new(CA_INT64, 1, dim, 0, NULL);
218
+ }
219
+ GetCArray(rout, cout);
220
+ q = (int64_t *) cout->ptr;
221
+ for (c = 0; c < k; c++) {
222
+ int64_t p = o[c] - base, e = o[c+1] - base;
223
+ for (; p < e; p++) {
224
+ q[p] = (int64_t) c;
225
+ }
226
+ }
227
+ RB_GC_GUARD(ro);
228
+ return rout;
229
+ }
230
+
231
+ /* CArray.__segment_bounds__(name, lengths: l | offsets: o) -> int64[k+1]
232
+
233
+ Internal: the validated boundaries, for a Ruby caller that takes the same
234
+ two keywords (CArray#segments). `name` heads the error messages. */
235
+ static VALUE
236
+ rb_ca_s_segment_bounds (int argc, VALUE *argv, VALUE klass)
237
+ {
238
+ CArray *co;
239
+ ca_size_t k;
240
+ VALUE rname;
241
+ if ( argc < 1 ) {
242
+ rb_raise(rb_eArgError, "__segment_bounds__: the caller's name is required");
243
+ }
244
+ rname = argv[0];
245
+ StringValue(rname);
246
+ return segment_bounds(argc - 1, argv + 1, RSTRING_PTR(rname), &co, &k);
247
+ }
248
+
249
+ void
250
+ Init_carray_segment (void)
251
+ {
252
+ id_lengths = rb_intern("lengths");
253
+ id_offsets = rb_intern("offsets");
254
+ rb_define_singleton_method(rb_cCArray, "segment_offsets", rb_ca_s_segment_offsets, -1);
255
+ rb_define_singleton_method(rb_cCArray, "segment_index", rb_ca_s_segment_index, -1);
256
+ rb_define_singleton_method(rb_cCArray, "__segment_bounds__", rb_ca_s_segment_bounds, -1);
257
+ }
data/ext/carray_slab.c CHANGED
@@ -28,6 +28,7 @@
28
28
  --------------------------------------------------------------------------- */
29
29
 
30
30
  #include "carray.h"
31
+ #include "carray_internal.h" /* ca_attach_window, ca_calling_method_name, ca_axis_integer */
31
32
  #include "carray_slab.h"
32
33
 
33
34
  /* Forward declarations: per-form loop bodies are defined later in this
@@ -67,22 +68,23 @@ ca_slab_parse_axes (ca_slab_iter_state_t *st, VALUE axis_arg, int8_t src_ndim)
67
68
  n = (int) RARRAY_LEN(arr);
68
69
  if ( n < 1 || n > CA_RANK_MAX ) {
69
70
  rb_raise(rb_eArgError,
70
- "CArray::SlabIterator: axis count %d out of range [1..%d]",
71
- n, CA_RANK_MAX);
71
+ "%s: axis count %d out of range [1..%d]",
72
+ ca_calling_method_name(), n, CA_RANK_MAX);
72
73
  }
73
74
 
74
75
  for ( i = 0; i < n; i++ ) {
75
- int k = NUM2INT(rb_ary_entry(arr, i));
76
+ int k = (int) ca_axis_integer(rb_ary_entry(arr, i), NULL);
76
77
  if ( k < 0 ) k += src_ndim;
77
78
  if ( k < 0 || k >= src_ndim ) {
78
79
  rb_raise(rb_eArgError,
79
- "CArray::SlabIterator: axis %d out of range for ndim %d",
80
- NUM2INT(rb_ary_entry(arr, i)), src_ndim);
80
+ "%s: axis %d out of range for ndim %d",
81
+ ca_calling_method_name(), NUM2INT(rb_ary_entry(arr, i)),
82
+ src_ndim);
81
83
  }
82
84
  for ( j = 0; j < i; j++ ) {
83
85
  if ( st->slab_axes[j] == (int8_t) k ) {
84
86
  rb_raise(rb_eArgError,
85
- "CArray::SlabIterator: duplicate axis %d", k);
87
+ "%s: duplicate axis %d", ca_calling_method_name(), k);
86
88
  }
87
89
  }
88
90
  st->slab_axes[i] = (int8_t) k;
@@ -368,23 +370,16 @@ ca_slab_setup_input_slab_view (ca_slab_iter_state_t *st, CArray *src,
368
370
  p_in via slab_strides into the scratch, present slab_view as
369
371
  a CAStride over the carrier with contig strides.
370
372
 
371
- Both paths require ALIAS T1 mode (= p_in is in src memory).
372
- Non-ALIAS multi-axis T1 modes (= PER_SLAB scratch where T1 picks
373
- its own gather for descriptor-framework sources) remain
374
- NotImpError until a follow-on substrate phase. */
375
- uint8_t am = st->t1->alias_mode;
376
- int contig;
377
- if ( am != CA_ITER_ALIAS_CONTIG &&
378
- am != CA_ITER_ALIAS_STRIDED &&
379
- am != CA_ITER_ALIAS_ATTACH ) {
380
- rb_raise(rb_eNotImpError,
381
- "multi-axis slab with non-ALIAS T1 mode not implemented yet "
382
- "(alias_mode = %d)", (int) am);
383
- }
384
- contig = ca_slab_strides_are_row_major_contig(slab_ndim,
385
- st->t1->slab_dims,
386
- st->t1->slab_strides,
387
- src->bytes);
373
+ Path (a) needs p_in to lie in src's own buffer, which holds only
374
+ when src is an entity. Any other source -- a view, whose cells the
375
+ iterator hands out from the root, a parent or its own scratch --
376
+ takes path (b): the iterator describes every slab by p_in and
377
+ slab_strides, whatever its mode. */
378
+ int contig = ca_is_entity(src)
379
+ && ca_slab_strides_are_row_major_contig(slab_ndim,
380
+ st->t1->slab_dims,
381
+ st->t1->slab_strides,
382
+ src->bytes);
388
383
  if ( contig ) {
389
384
  /* (a) contig multi-axis ALIAS: parent=src, K-D strides match src. */
390
385
  st->slab_view = ca_slab_build_slab_view(st->self, src->data_type,
@@ -440,7 +435,10 @@ ca_slab_setup_input_slab_view (ca_slab_iter_state_t *st, CArray *src,
440
435
  ca_size_t strides[1];
441
436
  dim[0] = fiber_len;
442
437
  strides[0] = src->bytes;
443
- in_alias = (in_stride == src->bytes);
438
+ /* A window onto src only when the fiber lies in src's own buffer:
439
+ an entity whose fiber is contiguous. A view's fiber comes from
440
+ the root or the iterator's scratch, so it goes through a carrier. */
441
+ in_alias = ca_is_entity(src) && (in_stride == src->bytes);
444
442
 
445
443
  if ( in_alias ) {
446
444
  /* ALIAS: parent = src. ca_stride_setup auto-creates slab_view->mask
@@ -525,6 +523,44 @@ ca_slab_advance_slab_view (ca_slab_iter_state_t *st,
525
523
  }
526
524
  }
527
525
 
526
+ /* map_slab: mark in the output the cells the block's result has masked.
527
+ The output is an entity walked in place, so the slab starts at
528
+ last_data_off in its buffer and steps by slab_strides; the output gets
529
+ a mask only when some result has a masked cell. */
530
+ static void
531
+ ca_map_slab_carry_mask (ca_slab_iter_state_t *st, CArray *out, CArray *res,
532
+ ca_size_t slab_elements)
533
+ {
534
+ ca_iter_state *t = st->t1_out;
535
+ volatile VALUE h = 0;
536
+ boolean8_t *m;
537
+ ca_size_t idx[CA_RANK_MAX];
538
+ ca_size_t i;
539
+ int8_t k;
540
+
541
+ if ( ! ca_has_mask(res) ) return;
542
+ ca_update_mask(res);
543
+ m = (boolean8_t *) ALLOCV(h, slab_elements);
544
+ ca_xfer_all(res->mask, m, CA_XFER_GET);
545
+ for ( i = 0; i < slab_elements && ! m[i]; i++ ) ;
546
+ if ( i < slab_elements ) {
547
+ ca_create_mask(out);
548
+ for ( k = 0; k < t->slab_ndim; k++ ) idx[k] = 0;
549
+ for ( i = 0; i < slab_elements; i++ ) {
550
+ if ( m[i] ) {
551
+ ca_size_t off = t->last_data_off;
552
+ for ( k = 0; k < t->slab_ndim; k++ ) off += idx[k] * t->slab_strides[k];
553
+ ((boolean8_t *) out->mask->ptr)[off / out->bytes] = 1;
554
+ }
555
+ for ( k = t->slab_ndim - 1; k >= 0; k-- ) {
556
+ if ( ++idx[k] < t->slab_dims[k] ) break;
557
+ idx[k] = 0;
558
+ }
559
+ }
560
+ }
561
+ ALLOCV_END(h);
562
+ }
563
+
528
564
  /* Convert a Ruby Numeric scalar into bytes of slab_view's data_type and
529
565
  fill the entire slab buffer at out_ptr. Used when the user block
530
566
  returns a scalar instead of a CArray (= broadcast over the slab).
@@ -533,9 +569,6 @@ static void
533
569
  ca_slab_fill_scalar (VALUE source, char *out_ptr, ca_size_t slab_elements,
534
570
  int8_t out_data_type, ca_size_t out_bytes, VALUE val)
535
571
  {
536
- char buf[64];
537
- char *scratch = (out_bytes <= (ca_size_t) sizeof(buf)) ? buf
538
- : xmalloc(out_bytes);
539
572
  ca_size_t i;
540
573
  /* Use source's data_type-aware conversion (rb_ca_obj2ptr looks at the
541
574
  receiver's data_type). Build a stand-in scalar buffer via the
@@ -546,18 +579,51 @@ ca_slab_fill_scalar (VALUE source, char *out_ptr, ca_size_t slab_elements,
546
579
  conversion through the actual output entity (= we already have
547
580
  `source`-keyed obj2ptr in carray_cast.c). Here we construct an
548
581
  ephemeral CScalar of the output data_type to anchor the cast. */
549
- VALUE cs_anchor =
582
+ volatile VALUE cs_anchor =
550
583
  rb_cscalar_new_with_value((int) out_data_type, (int) out_bytes, val);
551
584
  CScalar *anchor_ca;
552
585
  (void) source;
553
586
  TypedData_Get_Struct(cs_anchor, CScalar, &cscalar_data_type, anchor_ca);
554
- ca_attach(anchor_ca);
555
- memcpy(scratch, anchor_ca->ptr, out_bytes);
556
- ca_detach(anchor_ca);
557
587
  for ( i = 0; i < slab_elements; i++ ) {
558
- memcpy(out_ptr + i * out_bytes, scratch, out_bytes);
588
+ memcpy(out_ptr + i * out_bytes, anchor_ca->ptr, out_bytes);
589
+ }
590
+ RB_GC_GUARD(cs_anchor);
591
+ }
592
+
593
+ /* map_slab's cast-on-scatter: each cell of the block's result converted
594
+ into the output data type, with the result attached for the fetches. */
595
+
596
+ typedef struct {
597
+ VALUE result;
598
+ CArray *res;
599
+ VALUE output_view;
600
+ char *write_target;
601
+ ca_size_t out_bytes;
602
+ ca_size_t slab_elements;
603
+ } ca_map_slab_cast_ctx_t;
604
+
605
+ static VALUE
606
+ ca_map_slab_cast_body (VALUE arg)
607
+ {
608
+ ca_map_slab_cast_ctx_t *c = (ca_map_slab_cast_ctx_t *) arg;
609
+ volatile VALUE holder;
610
+ char *scratch = ALLOCV_N(char, holder, c->out_bytes);
611
+ ca_size_t i;
612
+ for ( i = 0; i < c->slab_elements; i++ ) {
613
+ VALUE elem;
614
+ ca_size_t idx[CA_RANK_MAX] = { 0 };
615
+ ca_size_t flat = i;
616
+ int8_t k;
617
+ for ( k = c->res->ndim - 1; k >= 0; k-- ) {
618
+ idx[k] = flat % c->res->dim[k];
619
+ flat /= c->res->dim[k];
620
+ }
621
+ elem = rb_ca_fetch_index(c->result, idx);
622
+ rb_ca_obj2ptr(c->output_view, elem, scratch);
623
+ memcpy(c->write_target + i * c->out_bytes, scratch, c->out_bytes);
559
624
  }
560
- if ( scratch != buf ) xfree(scratch);
625
+ ALLOCV_END(holder);
626
+ return Qnil;
561
627
  }
562
628
 
563
629
  static VALUE
@@ -573,16 +639,13 @@ ca_slab_run_map (ca_slab_iter_state_t *st)
573
639
 
574
640
  TypedData_Get_Struct(st->self, CArray, &carray_data_type, src);
575
641
 
576
- /* Mask transparent carry: input mask is exposed via slab_view
577
- (= user's block can see slab.mask / slab.has_mask?). Output mask
578
- is intentionally NOT scattered — block return CArray mask info is
579
- dropped (= simplest contract). */
642
+ /* The input slab carries the source's mask; a CArray the block
643
+ returns carries its mask into the output (ca_map_slab_carry_mask). */
580
644
 
581
645
  /* Step 1: allocate output entity (same shape as src, data_type from opts). */
582
646
  st->output = rb_carray_new(st->out_data_type, src->ndim, src->dim,
583
647
  st->out_bytes, NULL);
584
648
  TypedData_Get_Struct(st->output, CArray, &carray_data_type, out);
585
- ca_allocate(out); /* entity ptr usable as both read + write target */
586
649
 
587
650
  /* Step 2: T1 init on both sides. FIBER_CONTIG is naxes==1 only;
588
651
  multi-axis omits it. Output side gets CA_KERNEL_WRITE. */
@@ -698,34 +761,22 @@ ca_slab_run_map (ca_slab_iter_state_t *st)
698
761
  (long long) slab_elements, (int) slab_cs->ndim);
699
762
  }
700
763
  if ( res->data_type == st->out_data_type && res->bytes == st->out_bytes ) {
701
- ca_attach(res);
702
764
  ca_xfer_all(res, write_target, CA_XFER_GET);
703
- ca_detach(res);
704
765
  }
705
766
  else {
706
767
  /* Cast-on-scatter via per-cell obj2ptr. Writes to write_target
707
- (= either p_out direct or own_out_scratch). */
708
- char buf[64];
709
- char *scratch = (st->out_bytes <= (ca_size_t) sizeof(buf))
710
- ? buf : xmalloc(st->out_bytes);
711
- ca_size_t i;
712
- ca_attach(res);
713
- for ( i = 0; i < slab_elements; i++ ) {
714
- VALUE elem;
715
- ca_size_t idx[CA_RANK_MAX] = { 0 };
716
- ca_size_t flat = i;
717
- int8_t k;
718
- for ( k = res->ndim - 1; k >= 0; k-- ) {
719
- idx[k] = flat % res->dim[k];
720
- flat /= res->dim[k];
721
- }
722
- elem = rb_ca_fetch_index(result, idx);
723
- rb_ca_obj2ptr(st->output_slab_view, elem, scratch);
724
- memcpy(write_target + i * st->out_bytes, scratch, st->out_bytes);
725
- }
726
- ca_detach(res);
727
- if ( scratch != buf ) xfree(scratch);
768
+ (= either p_out direct or own_out_scratch). The conversion
769
+ can raise, so the result is read in a window. */
770
+ ca_map_slab_cast_ctx_t c;
771
+ c.result = result;
772
+ c.res = res;
773
+ c.output_view = st->output_slab_view;
774
+ c.write_target = write_target;
775
+ c.out_bytes = st->out_bytes;
776
+ c.slab_elements = slab_elements;
777
+ ca_attach_window(1, &res, "r", ca_map_slab_cast_body, (VALUE) &c);
728
778
  }
779
+ ca_map_slab_carry_mask(st, out, res, slab_elements);
729
780
  }
730
781
  else if ( rb_obj_is_kind_of(result, rb_cNumeric) ||
731
782
  result == Qtrue || result == Qfalse ||
@@ -820,7 +871,6 @@ ca_slab_reduce_setup (ca_slab_iter_state_t *st, CArray **out_src,
820
871
  st->output = rb_carray_new(st->out_data_type, out_ndim, out_dim,
821
872
  st->out_bytes, NULL);
822
873
  TypedData_Get_Struct(st->output, CArray, &carray_data_type, out);
823
- ca_allocate(out);
824
874
 
825
875
  /* FIBER_CONTIG is naxes==1 only (T1 substrate contract). Multi-axis
826
876
  omits the flag and uses bare CA_SLAB_AXES; T1 picks an in-src
@@ -862,6 +912,7 @@ ca_slab_run_reduce_slab (ca_slab_iter_state_t *st)
862
912
  ca_size_t out_idx = 0;
863
913
  char buf[64];
864
914
  char *scratch;
915
+ volatile VALUE scratch_holder = 0; /* 0, not Qnil: ALLOCV_END reads a non-zero holder */
865
916
 
866
917
  ca_slab_reduce_setup(st, &src, &fiber_len);
867
918
  TypedData_Get_Struct(st->output, CArray, &carray_data_type, out);
@@ -871,8 +922,10 @@ ca_slab_run_reduce_slab (ca_slab_iter_state_t *st)
871
922
  TypedData_Get_Struct(st->carrier, CAWrap, &cawrap_data_type, carrier_cw);
872
923
  }
873
924
 
874
- scratch = (st->out_bytes <= (ca_size_t) sizeof(buf)) ? buf
875
- : xmalloc(st->out_bytes);
925
+ /* The block and the conversion of its value can raise, so a scratch
926
+ too wide for buf is Ruby's temporary. */
927
+ scratch = (st->out_bytes <= (ca_size_t) sizeof(buf))
928
+ ? buf : ALLOCV_N(char, scratch_holder, st->out_bytes);
876
929
 
877
930
  while ( ca_iter_state_next_slab_axes(st->t1, &p_in, &m_in) ) {
878
931
  VALUE result;
@@ -891,7 +944,6 @@ ca_slab_run_reduce_slab (ca_slab_iter_state_t *st)
891
944
  if ( rb_obj_is_kind_of(result, rb_cCArray) ) {
892
945
  CArray *res;
893
946
  TypedData_Get_Struct(result, CArray, &carray_data_type, res);
894
- if ( scratch != buf ) xfree(scratch);
895
947
  rb_raise(rb_eArgError,
896
948
  "CArray#reduce_slab: per-slab block must return a scalar "
897
949
  "(got CArray with %lld element%s; use `slab[0]` or "
@@ -905,7 +957,7 @@ ca_slab_run_reduce_slab (ca_slab_iter_state_t *st)
905
957
  out_idx++;
906
958
  }
907
959
 
908
- if ( scratch != buf ) xfree(scratch);
960
+ ALLOCV_END(scratch_holder);
909
961
  return st->output;
910
962
  }
911
963
 
@@ -926,6 +978,7 @@ ca_slab_run_reduce_fiber (ca_slab_iter_state_t *st)
926
978
  ca_size_t out_idx = 0;
927
979
  char buf[64];
928
980
  char *scratch;
981
+ volatile VALUE scratch_holder = 0; /* 0, not Qnil: ALLOCV_END reads a non-zero holder */
929
982
 
930
983
  ca_slab_reduce_setup(st, &src, &fiber_len);
931
984
  TypedData_Get_Struct(st->output, CArray, &carray_data_type, out);
@@ -935,8 +988,10 @@ ca_slab_run_reduce_fiber (ca_slab_iter_state_t *st)
935
988
  TypedData_Get_Struct(st->carrier, CAWrap, &cawrap_data_type, carrier_cw);
936
989
  }
937
990
 
938
- scratch = (st->out_bytes <= (ca_size_t) sizeof(buf)) ? buf
939
- : xmalloc(st->out_bytes);
991
+ /* The block and the conversion of its value can raise, so a scratch
992
+ too wide for buf is Ruby's temporary. */
993
+ scratch = (st->out_bytes <= (ca_size_t) sizeof(buf))
994
+ ? buf : ALLOCV_N(char, scratch_holder, st->out_bytes);
940
995
 
941
996
  while ( ca_iter_state_next_slab_axes(st->t1, &p_in, &m_in) ) {
942
997
  VALUE acc;
@@ -971,7 +1026,7 @@ ca_slab_run_reduce_fiber (ca_slab_iter_state_t *st)
971
1026
  out_idx++;
972
1027
  }
973
1028
 
974
- if ( scratch != buf ) xfree(scratch);
1029
+ ALLOCV_END(scratch_holder);
975
1030
  return st->output;
976
1031
  }
977
1032
 
@@ -1055,11 +1110,56 @@ slab_state_run_body (VALUE arg)
1055
1110
  return Qnil;
1056
1111
  }
1057
1112
 
1113
+ /* The slab views outlive the walk: the block may keep the slab or a view
1114
+ of it (`row.dup`), and the GC marks an object array's cells for as long
1115
+ as it has a ptr. Before the scratch they point into is freed, the
1116
+ input slab and its carrier are moved onto a copy of the last slab,
1117
+ held by the carrier, and the internal output view lets go of its
1118
+ buffer. A slab that is a window onto the source itself points into
1119
+ the source and needs nothing. */
1120
+ static void
1121
+ slab_state_release_views (ca_slab_iter_state_t *st)
1122
+ {
1123
+ if ( st->output_slab_view != Qnil ) {
1124
+ CArray *ov;
1125
+ TypedData_Get_Struct(st->output_slab_view, CArray, &carray_data_type, ov);
1126
+ ov->ptr = NULL;
1127
+ }
1128
+ if ( st->carrier != Qnil && st->slab_view != Qnil ) {
1129
+ CArray *cw, *cs, *backing;
1130
+ volatile VALUE bv;
1131
+ TypedData_Get_Struct(st->carrier, CArray, &carray_data_type, cw);
1132
+ TypedData_Get_Struct(st->slab_view, CArray, &carray_data_type, cs);
1133
+ if ( cw->ptr == NULL ) {
1134
+ return;
1135
+ }
1136
+ backing = ca_copy(cw);
1137
+ bv = ca_wrap_struct(backing);
1138
+ if ( cw->mask && ! backing->mask ) {
1139
+ ca_create_mask(backing);
1140
+ }
1141
+ rb_ivar_set(st->carrier, rb_intern("__slab_backing__"), bv);
1142
+ if ( cs->ptr ) {
1143
+ cs->ptr = backing->ptr;
1144
+ }
1145
+ if ( cs->mask && cs->mask->ptr ) {
1146
+ cs->mask->ptr = backing->mask->ptr;
1147
+ }
1148
+ cw->ptr = backing->ptr;
1149
+ if ( cw->mask ) {
1150
+ cw->mask->ptr = backing->mask->ptr;
1151
+ }
1152
+ RB_GC_GUARD(bv);
1153
+ }
1154
+ }
1155
+
1058
1156
  static VALUE
1059
1157
  slab_state_finish (VALUE arg)
1060
1158
  {
1061
1159
  ca_slab_iter_state_t *st = (ca_slab_iter_state_t *)(uintptr_t) arg;
1062
1160
 
1161
+ slab_state_release_views(st);
1162
+
1063
1163
  /* T1 substrate teardown.
1064
1164
  CAREFUL: ca_detach(slab_view) is NEVER called — T1 owns the buffer;
1065
1165
  dropping the VALUE handles lets GC reclaim the CAStride wrappers
@@ -1124,7 +1224,7 @@ static VALUE
1124
1224
  slab_extract_kwargs (int argc, VALUE *argv, const char *method_name)
1125
1225
  {
1126
1226
  VALUE kw = Qnil;
1127
- if ( argc == 1 && RB_TYPE_P(argv[0], T_HASH) ) {
1227
+ if ( argc == 1 && rb_keyword_given_p() && RB_TYPE_P(argv[0], T_HASH) ) {
1128
1228
  kw = argv[0];
1129
1229
  }
1130
1230
  else if ( argc != 0 ) {