carray 3.0.1 → 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 (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +824 -0
  3. data/README.md +5 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +258 -4
  6. data/ext/ca_axis_group.c +368 -184
  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 +90 -41
  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 +960 -245
  14. data/ext/ca_kernel_iterator.h +352 -181
  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 +194 -42
  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 +5 -4
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +180 -50
  25. data/ext/ca_obj_face.c +428 -62
  26. data/ext/ca_obj_face.h +107 -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 +39 -26
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +235 -55
  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 +551 -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 +88 -75
  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 -123
  46. data/ext/ca_obj_stride.c +267 -35
  47. data/ext/ca_obj_string.c +25 -22
  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 +157 -29
  54. data/ext/ca_op_ipower.c +25 -13
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_rng_normal.h +42 -0
  58. data/ext/ca_rng_xoshiro256pp.h +105 -0
  59. data/ext/ca_sort_kernels.h +70 -0
  60. data/ext/ca_sweep_engine.c +385 -198
  61. data/ext/ca_sweep_engine.h +31 -23
  62. data/ext/ca_triop_dispatch.c +4 -1
  63. data/ext/ca_triop_dispatch.h +5 -2
  64. data/ext/caframe_csv_reader.c +463 -0
  65. data/ext/carray.h +90 -28
  66. data/ext/carray_access.c +487 -231
  67. data/ext/carray_address_basis.c +731 -0
  68. data/ext/carray_attribute.c +45 -3
  69. data/ext/carray_bincount.c +32 -2
  70. data/ext/carray_broadcast.c +49 -17
  71. data/ext/carray_call_cfunc.c +667 -511
  72. data/ext/carray_cast.c +1556 -247
  73. data/ext/carray_cast_func.rb +34 -21
  74. data/ext/carray_conversion.c +197 -73
  75. data/ext/carray_copy.c +84 -38
  76. data/ext/carray_core.c +591 -119
  77. data/ext/carray_count.c +148 -96
  78. data/ext/carray_element.c +18 -27
  79. data/ext/carray_factorize.c +604 -291
  80. data/ext/carray_generate.c +76 -40
  81. data/ext/carray_histogram.c +152 -94
  82. data/ext/carray_hold.c +2 -1
  83. data/ext/carray_index_classifier.c +63 -96
  84. data/ext/carray_internal.h +400 -1
  85. data/ext/carray_lazy.c +834 -79
  86. data/ext/carray_loop.c +88 -66
  87. data/ext/carray_mask.c +705 -324
  88. data/ext/carray_math_kernel.h +1 -0
  89. data/ext/carray_median_percentile.c +263 -63
  90. data/ext/carray_memory_view.c +254 -248
  91. data/ext/carray_operator.c +1093 -363
  92. data/ext/carray_order.c +133 -69
  93. data/ext/carray_partition.c +102 -56
  94. data/ext/carray_random.c +582 -204
  95. data/ext/carray_scatter.c +308 -65
  96. data/ext/carray_segment.c +257 -0
  97. data/ext/carray_slab.c +183 -70
  98. data/ext/carray_sort.c +235 -240
  99. data/ext/carray_sort_kernel.c +8 -1
  100. data/ext/carray_struct.c +99 -21
  101. data/ext/carray_test.c +167 -64
  102. data/ext/carray_time_text.c +1182 -0
  103. data/ext/carray_utils.c +363 -98
  104. data/ext/extconf.rb +27 -2
  105. data/ext/mk_call_cfunc.rb +103 -118
  106. data/ext/mk_pow5_table.rb +35 -0
  107. data/ext/mkkernel.rb +1349 -851
  108. data/ext/ruby_carray.c +17 -2
  109. data/ext/version.h +4 -4
  110. data/ext/version.rb +4 -2
  111. data/lib/carray/attribute.rb +39 -0
  112. data/lib/carray/autoload_carray.rb +24 -3
  113. data/lib/carray/autoload_method_extension.rb +18 -0
  114. data/lib/carray/axis_group.rb +245 -20
  115. data/lib/carray/basics.rb +95 -25
  116. data/lib/carray/bincount_nd.rb +6 -0
  117. data/lib/carray/block_iterator.rb +200 -54
  118. data/lib/carray/boolean_reduce.rb +21 -29
  119. data/lib/carray/categorical.rb +244 -59
  120. data/lib/carray/categorical_iterator.rb +391 -757
  121. data/lib/carray/complex.rb +36 -29
  122. data/lib/carray/conditional.rb +94 -7
  123. data/lib/carray/const_string.rb +177 -27
  124. data/lib/carray/construct.rb +40 -0
  125. data/lib/carray/data_type_extension.rb +3 -0
  126. data/lib/carray/data_type_limits.rb +91 -0
  127. data/lib/carray/fixlen_string.rb +1 -1
  128. data/lib/carray/frame/concat.rb +157 -6
  129. data/lib/carray/frame/convert.rb +13 -7
  130. data/lib/carray/frame/csv_parser.rb +590 -76
  131. data/lib/carray/frame/describe.rb +79 -0
  132. data/lib/carray/frame/frame.rb +205 -35
  133. data/lib/carray/frame/group.rb +199 -40
  134. data/lib/carray/frame/io.rb +452 -60
  135. data/lib/carray/frame/join.rb +134 -46
  136. data/lib/carray/frame/records.rb +54 -19
  137. data/lib/carray/frame/reshape.rb +448 -0
  138. data/lib/carray/frame/sort.rb +26 -2
  139. data/lib/carray/frame/verbs.rb +726 -56
  140. data/lib/carray/frame.rb +2 -0
  141. data/lib/carray/fuse_source.rb +111 -2
  142. data/lib/carray/fusion.rb +229 -38
  143. data/lib/carray/inspect.rb +71 -20
  144. data/lib/carray/iterator.rb +157 -2
  145. data/lib/carray/lazy.rb +54 -67
  146. data/lib/carray/mask_gap_fill.rb +66 -50
  147. data/lib/carray/math.rb +32 -19
  148. data/lib/carray/meld_reduce.rb +40 -7
  149. data/lib/carray/methods/align_addr.rb +8 -4
  150. data/lib/carray/methods/bincount.rb +2 -2
  151. data/lib/carray/methods/bit_string.rb +14 -3
  152. data/lib/carray/methods/composition.rb +16 -10
  153. data/lib/carray/methods/discovery_along.rb +74 -0
  154. data/lib/carray/methods/factorize.rb +50 -0
  155. data/lib/carray/methods/gather_nd.rb +13 -2
  156. data/lib/carray/methods/insert_block.rb +9 -14
  157. data/lib/carray/methods/is_in.rb +130 -6
  158. data/lib/carray/methods/join.rb +1 -1
  159. data/lib/carray/methods/locate_addr.rb +235 -21
  160. data/lib/carray/methods/mask_duplicates.rb +35 -1
  161. data/lib/carray/methods/mode.rb +18 -1
  162. data/lib/carray/methods/nunique.rb +22 -1
  163. data/lib/carray/methods/repeat.rb +91 -0
  164. data/lib/carray/methods/resize.rb +21 -23
  165. data/lib/carray/methods/snap.rb +34 -12
  166. data/lib/carray/methods/unique.rb +52 -3
  167. data/lib/carray/record_order.rb +208 -0
  168. data/lib/carray/rng.rb +86 -0
  169. data/lib/carray/runtime.rb +14 -11
  170. data/lib/carray/segment_iterator.rb +990 -0
  171. data/lib/carray/serialize.rb +197 -27
  172. data/lib/carray/slab_iterator.rb +109 -40
  173. data/lib/carray/stack.rb +10 -4
  174. data/lib/carray/string_operation_extension.rb +9 -4
  175. data/lib/carray/struct.rb +142 -51
  176. data/lib/carray/struct_builder.rb +110 -14
  177. data/lib/carray/table.rb +2 -2
  178. data/lib/carray/time.rb +535 -105
  179. data/lib/carray/window_iterator.rb +259 -82
  180. data/lib/carray.rb +4 -2
  181. data/yard-stubs/ca_obj_array.rb +4 -4
  182. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  183. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  184. data/yard-stubs/ca_obj_block.rb +2 -7
  185. data/yard-stubs/ca_obj_fake.rb +1 -1
  186. data/yard-stubs/ca_obj_field.rb +1 -1
  187. data/yard-stubs/ca_obj_refer.rb +3 -3
  188. data/yard-stubs/ca_obj_roll.rb +0 -1
  189. data/yard-stubs/ca_obj_stride.rb +10 -7
  190. data/yard-stubs/ca_obj_transpose.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +11 -4
  192. data/yard-stubs/carray_access.rb +6 -5
  193. data/yard-stubs/carray_broadcast.rb +2 -2
  194. data/yard-stubs/carray_cast.rb +78 -43
  195. data/yard-stubs/carray_conversion.rb +13 -1
  196. data/yard-stubs/carray_copy.rb +8 -3
  197. data/yard-stubs/carray_core.rb +3 -83
  198. data/yard-stubs/carray_count.rb +16 -2
  199. data/yard-stubs/carray_lazy.rb +210 -1
  200. data/yard-stubs/carray_loop.rb +4 -1
  201. data/yard-stubs/carray_mask.rb +34 -10
  202. data/yard-stubs/carray_math.rb +1499 -4
  203. data/yard-stubs/carray_median_percentile.rb +26 -8
  204. data/yard-stubs/carray_order.rb +43 -82
  205. data/yard-stubs/carray_scatter.rb +10 -3
  206. data/yard-stubs/carray_segment.rb +72 -0
  207. data/yard-stubs/carray_slab.rb +11 -8
  208. data/yard-stubs/carray_sort.rb +50 -8
  209. data/yard-stubs/carray_test.rb +10 -0
  210. data/yard-stubs/carray_utils.rb +0 -26
  211. metadata +19 -19
  212. data/ext/ca_for_each_element.h +0 -239
  213. data/ext/carray_build_flags.h +0 -3
  214. data/ext/carray_config.h +0 -28
  215. data/ext/carray_kernels_bincmp.c +0 -4446
  216. data/ext/carray_kernels_binop.c +0 -11001
  217. data/ext/carray_kernels_init.c +0 -1131
  218. data/ext/carray_kernels_map.c +0 -3467
  219. data/ext/carray_kernels_moncmp.c +0 -2097
  220. data/ext/carray_kernels_monop.c +0 -18313
  221. data/ext/carray_kernels_reduce_aggregate.c +0 -25837
  222. data/ext/carray_kernels_reduce_boolean.c +0 -330
  223. data/ext/carray_kernels_reduce_cumulative.c +0 -14593
  224. data/ext/carray_kernels_reduce_extreme.c +0 -16948
  225. data/ext/carray_kernels_reduce_variance.c +0 -3910
  226. data/ext/carray_kernels_scan.c +0 -3693
  227. data/ext/carray_kernels_search.c +0 -32138
  228. data/ext/carray_kernels_sort.c +0 -10626
  229. 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
@@ -460,6 +458,19 @@ ca_slab_setup_input_slab_view (ca_slab_iter_state_t *st, CArray *src,
460
458
  }
461
459
  }
462
460
 
461
+ /* The slab is a window onto the source when the fiber is contiguous and
462
+ a copy in the iterator's scratch when it is not, so writing through it
463
+ landed on one axis and vanished on the next. Nothing in the surface
464
+ said which you would get. Refuse the write on both instead: the block
465
+ forms that produce values (map_slab, reduce_slab) hand their result
466
+ back rather than writing into the slab, and an in-place walk belongs
467
+ to `ca[] = ...` or a C kernel with CA_KERNEL_WRITE. */
468
+ {
469
+ CArray *slab;
470
+ TypedData_Get_Struct(st->slab_view, CArray, &carray_data_type, slab);
471
+ ca_set_flag(slab, CA_FLAG_READ_ONLY);
472
+ }
473
+
463
474
  return in_alias;
464
475
  }
465
476
 
@@ -512,6 +523,44 @@ ca_slab_advance_slab_view (ca_slab_iter_state_t *st,
512
523
  }
513
524
  }
514
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
+
515
564
  /* Convert a Ruby Numeric scalar into bytes of slab_view's data_type and
516
565
  fill the entire slab buffer at out_ptr. Used when the user block
517
566
  returns a scalar instead of a CArray (= broadcast over the slab).
@@ -520,9 +569,6 @@ static void
520
569
  ca_slab_fill_scalar (VALUE source, char *out_ptr, ca_size_t slab_elements,
521
570
  int8_t out_data_type, ca_size_t out_bytes, VALUE val)
522
571
  {
523
- char buf[64];
524
- char *scratch = (out_bytes <= (ca_size_t) sizeof(buf)) ? buf
525
- : xmalloc(out_bytes);
526
572
  ca_size_t i;
527
573
  /* Use source's data_type-aware conversion (rb_ca_obj2ptr looks at the
528
574
  receiver's data_type). Build a stand-in scalar buffer via the
@@ -533,18 +579,51 @@ ca_slab_fill_scalar (VALUE source, char *out_ptr, ca_size_t slab_elements,
533
579
  conversion through the actual output entity (= we already have
534
580
  `source`-keyed obj2ptr in carray_cast.c). Here we construct an
535
581
  ephemeral CScalar of the output data_type to anchor the cast. */
536
- VALUE cs_anchor =
582
+ volatile VALUE cs_anchor =
537
583
  rb_cscalar_new_with_value((int) out_data_type, (int) out_bytes, val);
538
584
  CScalar *anchor_ca;
539
585
  (void) source;
540
586
  TypedData_Get_Struct(cs_anchor, CScalar, &cscalar_data_type, anchor_ca);
541
- ca_attach(anchor_ca);
542
- memcpy(scratch, anchor_ca->ptr, out_bytes);
543
- ca_detach(anchor_ca);
544
587
  for ( i = 0; i < slab_elements; i++ ) {
545
- memcpy(out_ptr + i * out_bytes, scratch, out_bytes);
588
+ memcpy(out_ptr + i * out_bytes, anchor_ca->ptr, out_bytes);
546
589
  }
547
- if ( scratch != buf ) xfree(scratch);
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);
624
+ }
625
+ ALLOCV_END(holder);
626
+ return Qnil;
548
627
  }
549
628
 
550
629
  static VALUE
@@ -560,16 +639,13 @@ ca_slab_run_map (ca_slab_iter_state_t *st)
560
639
 
561
640
  TypedData_Get_Struct(st->self, CArray, &carray_data_type, src);
562
641
 
563
- /* Mask transparent carry: input mask is exposed via slab_view
564
- (= user's block can see slab.mask / slab.has_mask?). Output mask
565
- is intentionally NOT scattered — block return CArray mask info is
566
- 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). */
567
644
 
568
645
  /* Step 1: allocate output entity (same shape as src, data_type from opts). */
569
646
  st->output = rb_carray_new(st->out_data_type, src->ndim, src->dim,
570
647
  st->out_bytes, NULL);
571
648
  TypedData_Get_Struct(st->output, CArray, &carray_data_type, out);
572
- ca_allocate(out); /* entity ptr usable as both read + write target */
573
649
 
574
650
  /* Step 2: T1 init on both sides. FIBER_CONTIG is naxes==1 only;
575
651
  multi-axis omits it. Output side gets CA_KERNEL_WRITE. */
@@ -685,34 +761,22 @@ ca_slab_run_map (ca_slab_iter_state_t *st)
685
761
  (long long) slab_elements, (int) slab_cs->ndim);
686
762
  }
687
763
  if ( res->data_type == st->out_data_type && res->bytes == st->out_bytes ) {
688
- ca_attach(res);
689
764
  ca_xfer_all(res, write_target, CA_XFER_GET);
690
- ca_detach(res);
691
765
  }
692
766
  else {
693
767
  /* Cast-on-scatter via per-cell obj2ptr. Writes to write_target
694
- (= either p_out direct or own_out_scratch). */
695
- char buf[64];
696
- char *scratch = (st->out_bytes <= (ca_size_t) sizeof(buf))
697
- ? buf : xmalloc(st->out_bytes);
698
- ca_size_t i;
699
- ca_attach(res);
700
- for ( i = 0; i < slab_elements; i++ ) {
701
- VALUE elem;
702
- ca_size_t idx[CA_RANK_MAX] = { 0 };
703
- ca_size_t flat = i;
704
- int8_t k;
705
- for ( k = res->ndim - 1; k >= 0; k-- ) {
706
- idx[k] = flat % res->dim[k];
707
- flat /= res->dim[k];
708
- }
709
- elem = rb_ca_fetch_index(result, idx);
710
- rb_ca_obj2ptr(st->output_slab_view, elem, scratch);
711
- memcpy(write_target + i * st->out_bytes, scratch, st->out_bytes);
712
- }
713
- ca_detach(res);
714
- 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);
715
778
  }
779
+ ca_map_slab_carry_mask(st, out, res, slab_elements);
716
780
  }
717
781
  else if ( rb_obj_is_kind_of(result, rb_cNumeric) ||
718
782
  result == Qtrue || result == Qfalse ||
@@ -807,7 +871,6 @@ ca_slab_reduce_setup (ca_slab_iter_state_t *st, CArray **out_src,
807
871
  st->output = rb_carray_new(st->out_data_type, out_ndim, out_dim,
808
872
  st->out_bytes, NULL);
809
873
  TypedData_Get_Struct(st->output, CArray, &carray_data_type, out);
810
- ca_allocate(out);
811
874
 
812
875
  /* FIBER_CONTIG is naxes==1 only (T1 substrate contract). Multi-axis
813
876
  omits the flag and uses bare CA_SLAB_AXES; T1 picks an in-src
@@ -849,6 +912,7 @@ ca_slab_run_reduce_slab (ca_slab_iter_state_t *st)
849
912
  ca_size_t out_idx = 0;
850
913
  char buf[64];
851
914
  char *scratch;
915
+ volatile VALUE scratch_holder = 0; /* 0, not Qnil: ALLOCV_END reads a non-zero holder */
852
916
 
853
917
  ca_slab_reduce_setup(st, &src, &fiber_len);
854
918
  TypedData_Get_Struct(st->output, CArray, &carray_data_type, out);
@@ -858,8 +922,10 @@ ca_slab_run_reduce_slab (ca_slab_iter_state_t *st)
858
922
  TypedData_Get_Struct(st->carrier, CAWrap, &cawrap_data_type, carrier_cw);
859
923
  }
860
924
 
861
- scratch = (st->out_bytes <= (ca_size_t) sizeof(buf)) ? buf
862
- : 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);
863
929
 
864
930
  while ( ca_iter_state_next_slab_axes(st->t1, &p_in, &m_in) ) {
865
931
  VALUE result;
@@ -878,7 +944,6 @@ ca_slab_run_reduce_slab (ca_slab_iter_state_t *st)
878
944
  if ( rb_obj_is_kind_of(result, rb_cCArray) ) {
879
945
  CArray *res;
880
946
  TypedData_Get_Struct(result, CArray, &carray_data_type, res);
881
- if ( scratch != buf ) xfree(scratch);
882
947
  rb_raise(rb_eArgError,
883
948
  "CArray#reduce_slab: per-slab block must return a scalar "
884
949
  "(got CArray with %lld element%s; use `slab[0]` or "
@@ -892,7 +957,7 @@ ca_slab_run_reduce_slab (ca_slab_iter_state_t *st)
892
957
  out_idx++;
893
958
  }
894
959
 
895
- if ( scratch != buf ) xfree(scratch);
960
+ ALLOCV_END(scratch_holder);
896
961
  return st->output;
897
962
  }
898
963
 
@@ -913,6 +978,7 @@ ca_slab_run_reduce_fiber (ca_slab_iter_state_t *st)
913
978
  ca_size_t out_idx = 0;
914
979
  char buf[64];
915
980
  char *scratch;
981
+ volatile VALUE scratch_holder = 0; /* 0, not Qnil: ALLOCV_END reads a non-zero holder */
916
982
 
917
983
  ca_slab_reduce_setup(st, &src, &fiber_len);
918
984
  TypedData_Get_Struct(st->output, CArray, &carray_data_type, out);
@@ -922,8 +988,10 @@ ca_slab_run_reduce_fiber (ca_slab_iter_state_t *st)
922
988
  TypedData_Get_Struct(st->carrier, CAWrap, &cawrap_data_type, carrier_cw);
923
989
  }
924
990
 
925
- scratch = (st->out_bytes <= (ca_size_t) sizeof(buf)) ? buf
926
- : 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);
927
995
 
928
996
  while ( ca_iter_state_next_slab_axes(st->t1, &p_in, &m_in) ) {
929
997
  VALUE acc;
@@ -958,7 +1026,7 @@ ca_slab_run_reduce_fiber (ca_slab_iter_state_t *st)
958
1026
  out_idx++;
959
1027
  }
960
1028
 
961
- if ( scratch != buf ) xfree(scratch);
1029
+ ALLOCV_END(scratch_holder);
962
1030
  return st->output;
963
1031
  }
964
1032
 
@@ -1042,11 +1110,56 @@ slab_state_run_body (VALUE arg)
1042
1110
  return Qnil;
1043
1111
  }
1044
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
+
1045
1156
  static VALUE
1046
1157
  slab_state_finish (VALUE arg)
1047
1158
  {
1048
1159
  ca_slab_iter_state_t *st = (ca_slab_iter_state_t *)(uintptr_t) arg;
1049
1160
 
1161
+ slab_state_release_views(st);
1162
+
1050
1163
  /* T1 substrate teardown.
1051
1164
  CAREFUL: ca_detach(slab_view) is NEVER called — T1 owns the buffer;
1052
1165
  dropping the VALUE handles lets GC reclaim the CAStride wrappers
@@ -1111,7 +1224,7 @@ static VALUE
1111
1224
  slab_extract_kwargs (int argc, VALUE *argv, const char *method_name)
1112
1225
  {
1113
1226
  VALUE kw = Qnil;
1114
- 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) ) {
1115
1228
  kw = argv[0];
1116
1229
  }
1117
1230
  else if ( argc != 0 ) {