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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +824 -0
- data/README.md +5 -6
- data/carray.gemspec +1 -1
- data/ext/ca_axis_dispatch.c +258 -4
- data/ext/ca_axis_group.c +368 -184
- data/ext/ca_binop_dispatch.c +54 -32
- data/ext/ca_binop_dispatch.h +1 -0
- data/ext/ca_categorical_iterator.c +90 -41
- data/ext/ca_compare.c +8 -2
- data/ext/ca_for_buffer.h +21 -68
- data/ext/ca_group_iter.c +27 -11
- data/ext/ca_kernel_iterator.c +960 -245
- data/ext/ca_kernel_iterator.h +352 -181
- data/ext/ca_monop_dispatch.c +4 -4
- data/ext/ca_monop_dispatch.h +4 -0
- data/ext/ca_obj_array.c +194 -42
- data/ext/ca_obj_bincmp.c +152 -121
- data/ext/ca_obj_binop.c +107 -210
- data/ext/ca_obj_bitarray.c +37 -24
- data/ext/ca_obj_bitfield.c +95 -62
- data/ext/ca_obj_block.c +5 -4
- data/ext/ca_obj_byte_swap.c +40 -40
- data/ext/ca_obj_const_string.c +180 -50
- data/ext/ca_obj_face.c +428 -62
- data/ext/ca_obj_face.h +107 -47
- data/ext/ca_obj_fake.c +21 -50
- data/ext/ca_obj_farray.c +1 -0
- data/ext/ca_obj_field.c +16 -3
- data/ext/ca_obj_fixlen_string.c +39 -26
- data/ext/ca_obj_grid.c +181 -124
- data/ext/ca_obj_meld.c +235 -55
- data/ext/ca_obj_moncmp.c +61 -61
- data/ext/ca_obj_monop.c +125 -107
- data/ext/ca_obj_object.c +551 -233
- data/ext/ca_obj_record.c +51 -16
- data/ext/ca_obj_reduce.c +5 -8
- data/ext/ca_obj_refer.c +117 -33
- data/ext/ca_obj_remap.c +57 -17
- data/ext/ca_obj_repeat.c +21 -5
- data/ext/ca_obj_roll.c +115 -11
- data/ext/ca_obj_select.c +88 -75
- data/ext/ca_obj_select_axis.c +58 -60
- data/ext/ca_obj_shift.c +22 -102
- data/ext/ca_obj_stack.c +223 -123
- data/ext/ca_obj_stride.c +267 -35
- data/ext/ca_obj_string.c +25 -22
- data/ext/ca_obj_tile.c +32 -16
- data/ext/ca_obj_time.c +23 -25
- data/ext/ca_obj_timedelta.c +21 -23
- data/ext/ca_obj_transpose.c +12 -3
- data/ext/ca_obj_triop.c +110 -117
- data/ext/ca_obj_window.c +157 -29
- data/ext/ca_op_ipower.c +25 -13
- data/ext/ca_op_powi.h +57 -30
- data/ext/ca_pow5_table.h +656 -0
- data/ext/ca_rng_normal.h +42 -0
- data/ext/ca_rng_xoshiro256pp.h +105 -0
- data/ext/ca_sort_kernels.h +70 -0
- data/ext/ca_sweep_engine.c +385 -198
- data/ext/ca_sweep_engine.h +31 -23
- data/ext/ca_triop_dispatch.c +4 -1
- data/ext/ca_triop_dispatch.h +5 -2
- data/ext/caframe_csv_reader.c +463 -0
- data/ext/carray.h +90 -28
- data/ext/carray_access.c +487 -231
- data/ext/carray_address_basis.c +731 -0
- data/ext/carray_attribute.c +45 -3
- data/ext/carray_bincount.c +32 -2
- data/ext/carray_broadcast.c +49 -17
- data/ext/carray_call_cfunc.c +667 -511
- data/ext/carray_cast.c +1556 -247
- data/ext/carray_cast_func.rb +34 -21
- data/ext/carray_conversion.c +197 -73
- data/ext/carray_copy.c +84 -38
- data/ext/carray_core.c +591 -119
- data/ext/carray_count.c +148 -96
- data/ext/carray_element.c +18 -27
- data/ext/carray_factorize.c +604 -291
- data/ext/carray_generate.c +76 -40
- data/ext/carray_histogram.c +152 -94
- data/ext/carray_hold.c +2 -1
- data/ext/carray_index_classifier.c +63 -96
- data/ext/carray_internal.h +400 -1
- data/ext/carray_lazy.c +834 -79
- data/ext/carray_loop.c +88 -66
- data/ext/carray_mask.c +705 -324
- data/ext/carray_math_kernel.h +1 -0
- data/ext/carray_median_percentile.c +263 -63
- data/ext/carray_memory_view.c +254 -248
- data/ext/carray_operator.c +1093 -363
- data/ext/carray_order.c +133 -69
- data/ext/carray_partition.c +102 -56
- data/ext/carray_random.c +582 -204
- data/ext/carray_scatter.c +308 -65
- data/ext/carray_segment.c +257 -0
- data/ext/carray_slab.c +183 -70
- data/ext/carray_sort.c +235 -240
- data/ext/carray_sort_kernel.c +8 -1
- data/ext/carray_struct.c +99 -21
- data/ext/carray_test.c +167 -64
- data/ext/carray_time_text.c +1182 -0
- data/ext/carray_utils.c +363 -98
- data/ext/extconf.rb +27 -2
- data/ext/mk_call_cfunc.rb +103 -118
- data/ext/mk_pow5_table.rb +35 -0
- data/ext/mkkernel.rb +1349 -851
- data/ext/ruby_carray.c +17 -2
- data/ext/version.h +4 -4
- data/ext/version.rb +4 -2
- data/lib/carray/attribute.rb +39 -0
- data/lib/carray/autoload_carray.rb +24 -3
- data/lib/carray/autoload_method_extension.rb +18 -0
- data/lib/carray/axis_group.rb +245 -20
- data/lib/carray/basics.rb +95 -25
- data/lib/carray/bincount_nd.rb +6 -0
- data/lib/carray/block_iterator.rb +200 -54
- data/lib/carray/boolean_reduce.rb +21 -29
- data/lib/carray/categorical.rb +244 -59
- data/lib/carray/categorical_iterator.rb +391 -757
- data/lib/carray/complex.rb +36 -29
- data/lib/carray/conditional.rb +94 -7
- data/lib/carray/const_string.rb +177 -27
- data/lib/carray/construct.rb +40 -0
- data/lib/carray/data_type_extension.rb +3 -0
- data/lib/carray/data_type_limits.rb +91 -0
- data/lib/carray/fixlen_string.rb +1 -1
- data/lib/carray/frame/concat.rb +157 -6
- data/lib/carray/frame/convert.rb +13 -7
- data/lib/carray/frame/csv_parser.rb +590 -76
- data/lib/carray/frame/describe.rb +79 -0
- data/lib/carray/frame/frame.rb +205 -35
- data/lib/carray/frame/group.rb +199 -40
- data/lib/carray/frame/io.rb +452 -60
- data/lib/carray/frame/join.rb +134 -46
- data/lib/carray/frame/records.rb +54 -19
- data/lib/carray/frame/reshape.rb +448 -0
- data/lib/carray/frame/sort.rb +26 -2
- data/lib/carray/frame/verbs.rb +726 -56
- data/lib/carray/frame.rb +2 -0
- data/lib/carray/fuse_source.rb +111 -2
- data/lib/carray/fusion.rb +229 -38
- data/lib/carray/inspect.rb +71 -20
- data/lib/carray/iterator.rb +157 -2
- data/lib/carray/lazy.rb +54 -67
- data/lib/carray/mask_gap_fill.rb +66 -50
- data/lib/carray/math.rb +32 -19
- data/lib/carray/meld_reduce.rb +40 -7
- data/lib/carray/methods/align_addr.rb +8 -4
- data/lib/carray/methods/bincount.rb +2 -2
- data/lib/carray/methods/bit_string.rb +14 -3
- data/lib/carray/methods/composition.rb +16 -10
- data/lib/carray/methods/discovery_along.rb +74 -0
- data/lib/carray/methods/factorize.rb +50 -0
- data/lib/carray/methods/gather_nd.rb +13 -2
- data/lib/carray/methods/insert_block.rb +9 -14
- data/lib/carray/methods/is_in.rb +130 -6
- data/lib/carray/methods/join.rb +1 -1
- data/lib/carray/methods/locate_addr.rb +235 -21
- data/lib/carray/methods/mask_duplicates.rb +35 -1
- data/lib/carray/methods/mode.rb +18 -1
- data/lib/carray/methods/nunique.rb +22 -1
- data/lib/carray/methods/repeat.rb +91 -0
- data/lib/carray/methods/resize.rb +21 -23
- data/lib/carray/methods/snap.rb +34 -12
- data/lib/carray/methods/unique.rb +52 -3
- data/lib/carray/record_order.rb +208 -0
- data/lib/carray/rng.rb +86 -0
- data/lib/carray/runtime.rb +14 -11
- data/lib/carray/segment_iterator.rb +990 -0
- data/lib/carray/serialize.rb +197 -27
- data/lib/carray/slab_iterator.rb +109 -40
- data/lib/carray/stack.rb +10 -4
- data/lib/carray/string_operation_extension.rb +9 -4
- data/lib/carray/struct.rb +142 -51
- data/lib/carray/struct_builder.rb +110 -14
- data/lib/carray/table.rb +2 -2
- data/lib/carray/time.rb +535 -105
- data/lib/carray/window_iterator.rb +259 -82
- data/lib/carray.rb +4 -2
- data/yard-stubs/ca_obj_array.rb +4 -4
- data/yard-stubs/ca_obj_bitarray.rb +7 -0
- data/yard-stubs/ca_obj_bitfield.rb +23 -12
- data/yard-stubs/ca_obj_block.rb +2 -7
- data/yard-stubs/ca_obj_fake.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +1 -1
- data/yard-stubs/ca_obj_refer.rb +3 -3
- data/yard-stubs/ca_obj_roll.rb +0 -1
- data/yard-stubs/ca_obj_stride.rb +10 -7
- data/yard-stubs/ca_obj_transpose.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +11 -4
- data/yard-stubs/carray_access.rb +6 -5
- data/yard-stubs/carray_broadcast.rb +2 -2
- data/yard-stubs/carray_cast.rb +78 -43
- data/yard-stubs/carray_conversion.rb +13 -1
- data/yard-stubs/carray_copy.rb +8 -3
- data/yard-stubs/carray_core.rb +3 -83
- data/yard-stubs/carray_count.rb +16 -2
- data/yard-stubs/carray_lazy.rb +210 -1
- data/yard-stubs/carray_loop.rb +4 -1
- data/yard-stubs/carray_mask.rb +34 -10
- data/yard-stubs/carray_math.rb +1499 -4
- data/yard-stubs/carray_median_percentile.rb +26 -8
- data/yard-stubs/carray_order.rb +43 -82
- data/yard-stubs/carray_scatter.rb +10 -3
- data/yard-stubs/carray_segment.rb +72 -0
- data/yard-stubs/carray_slab.rb +11 -8
- data/yard-stubs/carray_sort.rb +50 -8
- data/yard-stubs/carray_test.rb +10 -0
- data/yard-stubs/carray_utils.rb +0 -26
- metadata +19 -19
- data/ext/ca_for_each_element.h +0 -239
- data/ext/carray_build_flags.h +0 -3
- data/ext/carray_config.h +0 -28
- data/ext/carray_kernels_bincmp.c +0 -4446
- data/ext/carray_kernels_binop.c +0 -11001
- data/ext/carray_kernels_init.c +0 -1131
- data/ext/carray_kernels_map.c +0 -3467
- data/ext/carray_kernels_moncmp.c +0 -2097
- data/ext/carray_kernels_monop.c +0 -18313
- data/ext/carray_kernels_reduce_aggregate.c +0 -25837
- data/ext/carray_kernels_reduce_boolean.c +0 -330
- data/ext/carray_kernels_reduce_cumulative.c +0 -14593
- data/ext/carray_kernels_reduce_extreme.c +0 -16948
- data/ext/carray_kernels_reduce_variance.c +0 -3910
- data/ext/carray_kernels_scan.c +0 -3693
- data/ext/carray_kernels_search.c +0 -32138
- data/ext/carray_kernels_sort.c +0 -10626
- data/ext/carray_kernels_triop.c +0 -1392
data/ext/carray_operator.c
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
#include "carray.h"
|
|
14
14
|
#include "carray_internal.h" /* ca_lazy_arena_* */
|
|
15
|
+
#include "ca_binop_dispatch.h" /* ca_binop_is_trapping */
|
|
15
16
|
|
|
16
17
|
/* Operand scratch for a chunked / gathered kernel run.
|
|
17
18
|
|
|
@@ -30,6 +31,21 @@ ca_op_acquire_operand_scratch (CArray *ca, ca_size_t n_elements)
|
|
|
30
31
|
|
|
31
32
|
#include "ca_obj_face.h" /* ca_face_reconcile_comparison (comparison Face gate) */
|
|
32
33
|
|
|
34
|
+
/* A kernel table with no entry for the operand's data_type. Name the
|
|
35
|
+
method and the type, or the Face, rather than leave it to the table's
|
|
36
|
+
stub, which knows neither. */
|
|
37
|
+
NORETURN(static void ca_op_refuse_unimplemented (VALUE operand, CArray *ca,
|
|
38
|
+
VALUE eclass));
|
|
39
|
+
|
|
40
|
+
static void
|
|
41
|
+
ca_op_refuse_unimplemented (VALUE operand, CArray *ca, VALUE eclass)
|
|
42
|
+
{
|
|
43
|
+
const char *name = ca_calling_method_name();
|
|
44
|
+
ca_face_refuse_numeric(operand, name, eclass);
|
|
45
|
+
rb_raise(eclass, "%s: not defined for data_type %s",
|
|
46
|
+
name, ca_type_name[ca->data_type]);
|
|
47
|
+
}
|
|
48
|
+
|
|
33
49
|
VALUE rb_mCAMath;
|
|
34
50
|
|
|
35
51
|
extern ca_binop_func_t ca_binop_mul[CA_NTYPE];
|
|
@@ -52,8 +68,8 @@ ca_zerodiv (void)
|
|
|
52
68
|
|
|
53
69
|
These three helpers (ca_chunk_inner_size / ca_chunk_compute_n /
|
|
54
70
|
ca_chunked_gather) are extern so ca_sweep_engine.c can reuse the same
|
|
55
|
-
chunk policy + region-gather mechanism for the
|
|
56
|
-
|
|
71
|
+
chunk policy + region-gather mechanism for the chunked slab family
|
|
72
|
+
(ca_call_cslab_*) without duplicating the implementation. */
|
|
57
73
|
|
|
58
74
|
#define CA_CHUNK_TARGET_ELEMENTS 4096
|
|
59
75
|
|
|
@@ -133,6 +149,360 @@ ca_chunked_gather (CArray *ca, ca_size_t off, ca_size_t n, void *dest)
|
|
|
133
149
|
ca_xfer_stride(ca, starts, counts, strides, dest, CA_XFER_GET);
|
|
134
150
|
}
|
|
135
151
|
|
|
152
|
+
/* The chunked kernel run shared by the binop / triop / bincmp drivers.
|
|
153
|
+
|
|
154
|
+
Each input operand is acquired once and read chunk by chunk:
|
|
155
|
+
- a scalar (step 0) is gathered once, or aliased
|
|
156
|
+
- an alias array is read through its own ptr at the chunk offset
|
|
157
|
+
- any other array is gathered into an arena scratch every chunk
|
|
158
|
+
|
|
159
|
+
The acquires, the gathers and the kernel itself can all raise (a view
|
|
160
|
+
that refuses the transfer, an object lane calling Ruby), so the run
|
|
161
|
+
goes through ca_lazy_arena_protect and the release -- scratches back to
|
|
162
|
+
the arena, aliases detached -- happens however it ends. */
|
|
163
|
+
|
|
164
|
+
#define CA_CHUNKED_MAX_OPERANDS 3
|
|
165
|
+
|
|
166
|
+
typedef struct ca_chunked_run {
|
|
167
|
+
int n_op;
|
|
168
|
+
CArray *ca[CA_CHUNKED_MAX_OPERANDS];
|
|
169
|
+
ca_size_t step[CA_CHUNKED_MAX_OPERANDS];
|
|
170
|
+
char *src[CA_CHUNKED_MAX_OPERANDS];
|
|
171
|
+
void *scratch[CA_CHUNKED_MAX_OPERANDS];
|
|
172
|
+
int per_chunk[CA_CHUNKED_MAX_OPERANDS];
|
|
173
|
+
int attached[CA_CHUNKED_MAX_OPERANDS];
|
|
174
|
+
ca_size_t n_total;
|
|
175
|
+
ca_size_t chunk_n;
|
|
176
|
+
/* Calls the kernel on cells [off, off + n) with p[k] the chunk of
|
|
177
|
+
operand k. */
|
|
178
|
+
void (*kernel)(struct ca_chunked_run *run, ca_size_t off, ca_size_t n,
|
|
179
|
+
char **p);
|
|
180
|
+
void *data; /* the driver's kernel table and output */
|
|
181
|
+
} ca_chunked_run_t;
|
|
182
|
+
|
|
183
|
+
static void
|
|
184
|
+
ca_chunked_run_init (ca_chunked_run_t *run, ca_size_t n_total,
|
|
185
|
+
ca_size_t chunk_n)
|
|
186
|
+
{
|
|
187
|
+
MEMZERO(run, ca_chunked_run_t, 1);
|
|
188
|
+
run->n_total = n_total;
|
|
189
|
+
run->chunk_n = chunk_n;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
static void
|
|
193
|
+
ca_chunked_run_add (ca_chunked_run_t *run, CArray *ca, ca_size_t step)
|
|
194
|
+
{
|
|
195
|
+
run->ca[run->n_op] = ca;
|
|
196
|
+
run->step[run->n_op] = step;
|
|
197
|
+
run->n_op++;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
static void
|
|
201
|
+
ca_chunked_run_acquire (ca_chunked_run_t *run, int k)
|
|
202
|
+
{
|
|
203
|
+
CArray *ca = run->ca[k];
|
|
204
|
+
if ( ca_attach_is_alias(ca) ) {
|
|
205
|
+
ca_attach(ca); /* window: released by ca_chunked_run on the way out */
|
|
206
|
+
run->attached[k] = 1;
|
|
207
|
+
run->src[k] = (char *) ca->ptr;
|
|
208
|
+
}
|
|
209
|
+
else if ( run->step[k] == 0 ) {
|
|
210
|
+
run->scratch[k] = ca_op_acquire_operand_scratch(ca, 1);
|
|
211
|
+
ca_xfer_all(ca, run->scratch[k], CA_XFER_GET);
|
|
212
|
+
run->src[k] = (char *) run->scratch[k];
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
run->scratch[k] = ca_op_acquire_operand_scratch(ca, run->chunk_n);
|
|
216
|
+
run->src[k] = (char *) run->scratch[k];
|
|
217
|
+
run->per_chunk[k] = 1;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
static VALUE
|
|
222
|
+
ca_chunked_run_body (VALUE arg)
|
|
223
|
+
{
|
|
224
|
+
ca_chunked_run_t *run = (ca_chunked_run_t *) arg;
|
|
225
|
+
char *p[CA_CHUNKED_MAX_OPERANDS];
|
|
226
|
+
ca_size_t off;
|
|
227
|
+
int k;
|
|
228
|
+
|
|
229
|
+
for ( k = 0; k < run->n_op; k++ ) {
|
|
230
|
+
ca_chunked_run_acquire(run, k);
|
|
231
|
+
}
|
|
232
|
+
for ( off = 0; off < run->n_total; off += run->chunk_n ) {
|
|
233
|
+
ca_size_t n = ( off + run->chunk_n > run->n_total )
|
|
234
|
+
? run->n_total - off : run->chunk_n;
|
|
235
|
+
for ( k = 0; k < run->n_op; k++ ) {
|
|
236
|
+
if ( run->per_chunk[k] ) {
|
|
237
|
+
ca_chunked_gather(run->ca[k], off, n, run->scratch[k]);
|
|
238
|
+
p[k] = run->src[k];
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
p[k] = run->src[k] + ( run->step[k] ? off * run->ca[k]->bytes : 0 );
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
run->kernel(run, off, n, p);
|
|
245
|
+
}
|
|
246
|
+
return Qnil;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
static VALUE
|
|
250
|
+
ca_chunked_run_release (VALUE arg)
|
|
251
|
+
{
|
|
252
|
+
ca_chunked_run_t *run = (ca_chunked_run_t *) arg;
|
|
253
|
+
int k;
|
|
254
|
+
for ( k = run->n_op - 1; k >= 0; k-- ) {
|
|
255
|
+
if ( run->scratch[k] ) {
|
|
256
|
+
ca_lazy_arena_release(run->scratch[k]);
|
|
257
|
+
run->scratch[k] = NULL;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
for ( k = run->n_op - 1; k >= 0; k-- ) {
|
|
261
|
+
if ( run->attached[k] ) {
|
|
262
|
+
ca_detach(run->ca[k]);
|
|
263
|
+
run->attached[k] = 0;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return Qnil;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
static void
|
|
270
|
+
ca_chunked_run (ca_chunked_run_t *run)
|
|
271
|
+
{
|
|
272
|
+
ca_lazy_arena_protect(ca_chunked_run_body, ca_chunked_run_release,
|
|
273
|
+
(VALUE) run);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/* ---- Kernels that can raise -----------------------------------------
|
|
277
|
+
|
|
278
|
+
An operator kernel runs with its alias operands attached. Most kernels
|
|
279
|
+
cannot raise and are called directly. Three kinds can:
|
|
280
|
+
|
|
281
|
+
- an object lane, which calls Ruby for every cell
|
|
282
|
+
- an integer division by zero (div, mod, fmod, rcp, rcp_mul)
|
|
283
|
+
- a data type the operator has no kernel for (the not_implement slot)
|
|
284
|
+
|
|
285
|
+
Those run through ca_op_guarded, which releases the operands the call
|
|
286
|
+
holds if the kernel raises. An operand the kernel writes in place (the
|
|
287
|
+
receiver of a bang form) is synced first, so the cells written reach
|
|
288
|
+
its parent as they would for an entity. */
|
|
289
|
+
|
|
290
|
+
static int
|
|
291
|
+
ca_op_is_integer (int8_t dt)
|
|
292
|
+
{
|
|
293
|
+
return ( dt >= CA_INT8 && dt <= CA_UINT64 );
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
static int
|
|
297
|
+
ca_monop_may_raise (ca_monop_func_t func[], int8_t dt)
|
|
298
|
+
{
|
|
299
|
+
return dt == CA_OBJECT
|
|
300
|
+
|| func[dt] == ca_monop_not_implement
|
|
301
|
+
|| ( func == ca_monop_rcp && ca_op_is_integer(dt) );
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/* Which ops raise on a cell is ca_binop_is_trapping's list, the one the
|
|
305
|
+
lazy views skip masked cells by. */
|
|
306
|
+
static int
|
|
307
|
+
ca_binop_may_raise (ca_binop_func_t func[], int8_t dt)
|
|
308
|
+
{
|
|
309
|
+
int op_id;
|
|
310
|
+
if ( dt == CA_OBJECT || func[dt] == ca_binop_not_implement ) return 1;
|
|
311
|
+
op_id = ca_binop_op_id_of_table(func);
|
|
312
|
+
return op_id >= 0 && ca_binop_is_trapping((uint16_t) op_id, dt);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
static int
|
|
316
|
+
ca_triop_may_raise (ca_triop_func_t func[], int8_t dt)
|
|
317
|
+
{
|
|
318
|
+
return dt == CA_OBJECT || func[dt] == ca_triop_not_implement;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
static int
|
|
322
|
+
ca_moncmp_may_raise (ca_moncmp_func_t func[], int8_t dt)
|
|
323
|
+
{
|
|
324
|
+
return dt == CA_OBJECT || func[dt] == ca_moncmp_not_implement;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
static int
|
|
328
|
+
ca_bincmp_may_raise (ca_bincmp_func_t func[], int8_t dt)
|
|
329
|
+
{
|
|
330
|
+
return dt == CA_OBJECT || func[dt] == ca_bincmp_not_implement;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/* One kernel call, packed so that it can run under rb_ensure. */
|
|
334
|
+
|
|
335
|
+
typedef struct {
|
|
336
|
+
ca_monop_func_t f;
|
|
337
|
+
ca_size_t n; boolean8_t *m;
|
|
338
|
+
char *p1; ca_size_t i1;
|
|
339
|
+
char *p2; ca_size_t i2;
|
|
340
|
+
} ca_monop_call_t;
|
|
341
|
+
|
|
342
|
+
static VALUE
|
|
343
|
+
ca_monop_call (VALUE arg)
|
|
344
|
+
{
|
|
345
|
+
ca_monop_call_t *k = (ca_monop_call_t *) arg;
|
|
346
|
+
k->f(k->n, k->m, k->p1, k->i1, k->p2, k->i2);
|
|
347
|
+
return Qnil;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
typedef struct {
|
|
351
|
+
ca_binop_func_t f;
|
|
352
|
+
ca_size_t n; boolean8_t *m;
|
|
353
|
+
char *p1; ca_size_t i1;
|
|
354
|
+
char *p2; ca_size_t i2;
|
|
355
|
+
char *p3; ca_size_t i3;
|
|
356
|
+
} ca_binop_call_t;
|
|
357
|
+
|
|
358
|
+
static VALUE
|
|
359
|
+
ca_binop_call (VALUE arg)
|
|
360
|
+
{
|
|
361
|
+
ca_binop_call_t *k = (ca_binop_call_t *) arg;
|
|
362
|
+
k->f(k->n, k->m, k->p1, k->i1, k->p2, k->i2, k->p3, k->i3);
|
|
363
|
+
return Qnil;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
typedef struct {
|
|
367
|
+
ca_triop_func_t f;
|
|
368
|
+
ca_size_t n; boolean8_t *m;
|
|
369
|
+
char *p1; ca_size_t i1;
|
|
370
|
+
char *p2; ca_size_t i2;
|
|
371
|
+
char *p3; ca_size_t i3;
|
|
372
|
+
char *p4; ca_size_t i4;
|
|
373
|
+
} ca_triop_call_t;
|
|
374
|
+
|
|
375
|
+
static VALUE
|
|
376
|
+
ca_triop_call (VALUE arg)
|
|
377
|
+
{
|
|
378
|
+
ca_triop_call_t *k = (ca_triop_call_t *) arg;
|
|
379
|
+
k->f(k->n, k->m, k->p1, k->i1, k->p2, k->i2, k->p3, k->i3, k->p4, k->i4);
|
|
380
|
+
return Qnil;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
typedef struct {
|
|
384
|
+
ca_moncmp_func_t f;
|
|
385
|
+
ca_size_t n; boolean8_t *m;
|
|
386
|
+
char *p1; ca_size_t i1;
|
|
387
|
+
boolean8_t *p2; ca_size_t i2;
|
|
388
|
+
} ca_moncmp_call_t;
|
|
389
|
+
|
|
390
|
+
static VALUE
|
|
391
|
+
ca_moncmp_call (VALUE arg)
|
|
392
|
+
{
|
|
393
|
+
ca_moncmp_call_t *k = (ca_moncmp_call_t *) arg;
|
|
394
|
+
k->f(k->n, k->m, k->p1, k->i1, k->p2, k->i2);
|
|
395
|
+
return Qnil;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
typedef struct {
|
|
399
|
+
ca_bincmp_func_t f;
|
|
400
|
+
ca_size_t n; boolean8_t *m;
|
|
401
|
+
char *p1; ca_size_t b1; ca_size_t i1;
|
|
402
|
+
char *p2; ca_size_t b2; ca_size_t i2;
|
|
403
|
+
char *p3; ca_size_t b3; ca_size_t i3;
|
|
404
|
+
double tol;
|
|
405
|
+
} ca_bincmp_call_t;
|
|
406
|
+
|
|
407
|
+
static VALUE
|
|
408
|
+
ca_bincmp_call (VALUE arg)
|
|
409
|
+
{
|
|
410
|
+
ca_bincmp_call_t *k = (ca_bincmp_call_t *) arg;
|
|
411
|
+
k->f(k->n, k->m, k->p1, k->b1, k->i1, k->p2, k->b2, k->i2,
|
|
412
|
+
k->p3, k->b3, k->i3, k->tol);
|
|
413
|
+
return Qnil;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
typedef struct {
|
|
417
|
+
VALUE (*run)(VALUE);
|
|
418
|
+
VALUE arg;
|
|
419
|
+
int done;
|
|
420
|
+
int n;
|
|
421
|
+
CArray **held;
|
|
422
|
+
const char *mode; /* per held array: 'r' read, 'w' written in place */
|
|
423
|
+
} ca_op_guard_t;
|
|
424
|
+
|
|
425
|
+
static VALUE
|
|
426
|
+
ca_op_guard_body (VALUE varg)
|
|
427
|
+
{
|
|
428
|
+
ca_op_guard_t *g = (ca_op_guard_t *) varg;
|
|
429
|
+
g->run(g->arg);
|
|
430
|
+
g->done = 1;
|
|
431
|
+
return Qnil;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
static VALUE
|
|
435
|
+
ca_op_guard_sync (VALUE varg)
|
|
436
|
+
{
|
|
437
|
+
ca_sync((void *) varg);
|
|
438
|
+
return Qnil;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/* Runs only after a raise: the caller closes the operands itself when the
|
|
442
|
+
kernel returns. rb_ensure keeps the kernel's exception across this, so
|
|
443
|
+
a sync failing in here is dropped rather than let replace it. */
|
|
444
|
+
static VALUE
|
|
445
|
+
ca_op_guard_release (VALUE varg)
|
|
446
|
+
{
|
|
447
|
+
ca_op_guard_t *g = (ca_op_guard_t *) varg;
|
|
448
|
+
int k;
|
|
449
|
+
if ( g->done ) {
|
|
450
|
+
return Qnil;
|
|
451
|
+
}
|
|
452
|
+
for ( k = g->n - 1; k >= 0; k-- ) {
|
|
453
|
+
CArray *ca = g->held[k];
|
|
454
|
+
if ( ! ca || ! ca->ptr ) {
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
if ( g->mode[k] == 'w' ) {
|
|
458
|
+
int tag = 0;
|
|
459
|
+
rb_protect(ca_op_guard_sync, (VALUE) ca, &tag);
|
|
460
|
+
rb_set_errinfo(Qnil);
|
|
461
|
+
}
|
|
462
|
+
ca_detach(ca);
|
|
463
|
+
}
|
|
464
|
+
return Qnil;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/* Runs the kernel packed in `call` with `held` attached; see above. NULL
|
|
468
|
+
entries in held are skipped. */
|
|
469
|
+
static void
|
|
470
|
+
ca_op_guarded (VALUE (*run)(VALUE), void *call,
|
|
471
|
+
int n, CArray **held, const char *mode)
|
|
472
|
+
{
|
|
473
|
+
ca_op_guard_t g;
|
|
474
|
+
g.run = run;
|
|
475
|
+
g.arg = (VALUE) call;
|
|
476
|
+
g.done = 0;
|
|
477
|
+
g.n = n;
|
|
478
|
+
g.held = held;
|
|
479
|
+
g.mode = mode;
|
|
480
|
+
rb_ensure(ca_op_guard_body, (VALUE) &g, ca_op_guard_release, (VALUE) &g);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/* ORs one operand's mask into ma through an arena scratch. The scratch
|
|
484
|
+
is acquired inside the bracket: the transfer below opens a bracket of
|
|
485
|
+
its own, and an entry from depth 0 treats every slot as free. */
|
|
486
|
+
typedef struct {
|
|
487
|
+
void *scratch; /* first: released by ca_lazy_arena_release_held */
|
|
488
|
+
CArray *mask;
|
|
489
|
+
boolean8_t *ma;
|
|
490
|
+
ca_size_t elements;
|
|
491
|
+
} ca_mask_overlay_or_t;
|
|
492
|
+
|
|
493
|
+
static VALUE
|
|
494
|
+
ca_mask_overlay_or_body (VALUE arg)
|
|
495
|
+
{
|
|
496
|
+
ca_mask_overlay_or_t *o = (ca_mask_overlay_or_t *) arg;
|
|
497
|
+
boolean8_t *ms;
|
|
498
|
+
ca_size_t j;
|
|
499
|
+
o->scratch = ca_lazy_arena_acquire(o->elements);
|
|
500
|
+
ms = (boolean8_t *) o->scratch;
|
|
501
|
+
ca_xfer_all(o->mask, o->scratch, CA_XFER_GET);
|
|
502
|
+
for ( j = 0; j < o->elements; j++ ) o->ma[j] |= ms[j];
|
|
503
|
+
return Qnil;
|
|
504
|
+
}
|
|
505
|
+
|
|
136
506
|
/* Operand mask overlay without calling ca_attach on the operand masks.
|
|
137
507
|
|
|
138
508
|
`ca_copy_mask_overlay` (carray_mask.c) attaches each operand mask via
|
|
@@ -180,7 +550,6 @@ ca_mask_overlay_safe (CArray *ca_out, int n, ...)
|
|
|
180
550
|
|
|
181
551
|
boolean8_t *ma = (boolean8_t *) ca_out->mask->ptr;
|
|
182
552
|
ca_size_t elements = ca_out->elements;
|
|
183
|
-
ca_size_t j;
|
|
184
553
|
|
|
185
554
|
/* Fresh mask → zero-init. Existing mask (= bang variant where
|
|
186
555
|
ca_out IS one of the operands) → preserve as initial accumulator
|
|
@@ -200,16 +569,59 @@ ca_mask_overlay_safe (CArray *ca_out, int n, ...)
|
|
|
200
569
|
ca_xfer_all(cs->mask, &bit, CA_XFER_GET);
|
|
201
570
|
if ( bit ) memset(ma, 1, elements);
|
|
202
571
|
} else {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
572
|
+
ca_mask_overlay_or_t o;
|
|
573
|
+
o.scratch = NULL;
|
|
574
|
+
o.mask = cs->mask;
|
|
575
|
+
o.ma = ma;
|
|
576
|
+
o.elements = elements;
|
|
577
|
+
ca_lazy_arena_protect(ca_mask_overlay_or_body,
|
|
578
|
+
ca_lazy_arena_release_held, (VALUE) &o);
|
|
208
579
|
}
|
|
209
580
|
}
|
|
210
581
|
}
|
|
211
582
|
}
|
|
212
583
|
|
|
584
|
+
/* ca_mask_overlay_safe into a bang operation's self, which is attached:
|
|
585
|
+
reading an operand's mask can call Ruby (a CAObject's mask hooks) and
|
|
586
|
+
raise, and self is then detached before the raise goes on. Nothing has
|
|
587
|
+
been written to self's data yet, so there is nothing to sync. */
|
|
588
|
+
typedef struct {
|
|
589
|
+
CArray *out;
|
|
590
|
+
CArray *src[3];
|
|
591
|
+
int n;
|
|
592
|
+
} ca_overlay_args_t;
|
|
593
|
+
|
|
594
|
+
static VALUE
|
|
595
|
+
ca_overlay_attached_body (VALUE arg)
|
|
596
|
+
{
|
|
597
|
+
ca_overlay_args_t *a = (ca_overlay_args_t *) arg;
|
|
598
|
+
if ( a->n == 2 ) {
|
|
599
|
+
ca_mask_overlay_safe(a->out, 2, a->src[0], a->src[1]);
|
|
600
|
+
}
|
|
601
|
+
else {
|
|
602
|
+
ca_mask_overlay_safe(a->out, 3, a->src[0], a->src[1], a->src[2]);
|
|
603
|
+
}
|
|
604
|
+
return Qnil;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
static void
|
|
608
|
+
ca_mask_overlay_attached (CArray *out, int n,
|
|
609
|
+
CArray *s0, CArray *s1, CArray *s2)
|
|
610
|
+
{
|
|
611
|
+
ca_overlay_args_t a;
|
|
612
|
+
int tag = 0;
|
|
613
|
+
a.out = out;
|
|
614
|
+
a.n = n;
|
|
615
|
+
a.src[0] = s0;
|
|
616
|
+
a.src[1] = s1;
|
|
617
|
+
a.src[2] = s2;
|
|
618
|
+
rb_protect(ca_overlay_attached_body, (VALUE) &a, &tag);
|
|
619
|
+
if ( tag ) {
|
|
620
|
+
ca_detach(out);
|
|
621
|
+
rb_jump_tag(tag);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
213
625
|
/* Monop driver. ca1 is input-only (the driver does not attach it); ca2
|
|
214
626
|
is the output (a new entity, attach legit). fast = ca_attach_is_alias(ca1)
|
|
215
627
|
→ 1-shot; slow = ALLOCV + ca_xfer_all without ca_func[X].attach. */
|
|
@@ -229,6 +641,9 @@ rb_ca_call_monop (VALUE self, ca_monop_func_t func[])
|
|
|
229
641
|
self = rb_ca_wrap_readonly(self, INT2NUM(CA_INT64));
|
|
230
642
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
|
|
231
643
|
}
|
|
644
|
+
if ( func[ca1->data_type] == ca_monop_not_implement ) {
|
|
645
|
+
ca_op_refuse_unimplemented(self, ca1, rb_eCADataTypeError);
|
|
646
|
+
}
|
|
232
647
|
|
|
233
648
|
ca2 = ca_has_mask(ca1) ? ca_template_safe(ca1) : ca_template(ca1);
|
|
234
649
|
out = ca_wrap_struct(ca2);
|
|
@@ -236,11 +651,19 @@ rb_ca_call_monop (VALUE self, ca_monop_func_t func[])
|
|
|
236
651
|
ca_mask_overlay_safe(ca2, 1, ca1);
|
|
237
652
|
|
|
238
653
|
if ( ca_attach_is_alias(ca1) ) {
|
|
239
|
-
|
|
240
|
-
func[ca1->data_type]
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
654
|
+
ca_monop_call_t k;
|
|
655
|
+
k.f = func[ca1->data_type];
|
|
656
|
+
k.n = ca1->elements;
|
|
657
|
+
k.m = ( ca2->mask ) ? (boolean8_t *)ca2->mask->ptr : NULL;
|
|
658
|
+
k.p2 = ca2->ptr; k.i2 = 1;
|
|
659
|
+
ca_attach(ca1); /* window: the kernel runs through ca_op_guarded when it can raise */
|
|
660
|
+
k.p1 = ca1->ptr; k.i1 = 1;
|
|
661
|
+
if ( ca_monop_may_raise(func, ca1->data_type) ) {
|
|
662
|
+
ca_op_guarded(ca_monop_call, &k, 1, &ca1, "r");
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
ca_monop_call((VALUE) &k);
|
|
666
|
+
}
|
|
244
667
|
ca_detach(ca1);
|
|
245
668
|
}
|
|
246
669
|
else {
|
|
@@ -351,14 +774,26 @@ rb_ca_call_monop_bang (VALUE self, ca_monop_func_t func[])
|
|
|
351
774
|
rb_ca_modify(self);
|
|
352
775
|
|
|
353
776
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
|
|
777
|
+
if ( func[ca1->data_type] == ca_monop_not_implement ) {
|
|
778
|
+
ca_op_refuse_unimplemented(self, ca1, rb_eCADataTypeError);
|
|
779
|
+
}
|
|
354
780
|
|
|
355
|
-
ca_attach(ca1);
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
781
|
+
ca_attach(ca1); /* window: the kernel runs through ca_op_guarded when it can raise */
|
|
782
|
+
{
|
|
783
|
+
ca_monop_call_t k;
|
|
784
|
+
k.f = func[ca1->data_type];
|
|
785
|
+
k.n = ca1->elements;
|
|
786
|
+
k.m = ( ca1->mask ) ? (boolean8_t *)ca1->mask->ptr : NULL;
|
|
787
|
+
k.p1 = ca1->ptr; k.i1 = 1;
|
|
788
|
+
k.p2 = ca1->ptr; k.i2 = 1;
|
|
789
|
+
if ( ca_monop_may_raise(func, ca1->data_type) ) {
|
|
790
|
+
ca_op_guarded(ca_monop_call, &k, 1, &ca1, "w");
|
|
791
|
+
}
|
|
792
|
+
else {
|
|
793
|
+
ca_monop_call((VALUE) &k);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
ca_sync_detach(ca1);
|
|
362
797
|
|
|
363
798
|
return self;
|
|
364
799
|
}
|
|
@@ -380,9 +815,7 @@ rb_ca_call_monop_typed (VALUE self, ca_monop_func_t func[],
|
|
|
380
815
|
|
|
381
816
|
out_dt = out_data_types[ca1->data_type];
|
|
382
817
|
if ( out_dt < 0 ) {
|
|
383
|
-
|
|
384
|
-
"data_type-changing monop not implemented for input data_type %d",
|
|
385
|
-
ca1->data_type);
|
|
818
|
+
ca_op_refuse_unimplemented(self, ca1, rb_eCADataTypeError);
|
|
386
819
|
}
|
|
387
820
|
|
|
388
821
|
/* Allocate output with the per-op output data_type. Same shape as input. */
|
|
@@ -393,11 +826,19 @@ rb_ca_call_monop_typed (VALUE self, ca_monop_func_t func[],
|
|
|
393
826
|
ca_mask_overlay_safe(ca2, 1, ca1);
|
|
394
827
|
|
|
395
828
|
if ( ca_attach_is_alias(ca1) ) {
|
|
396
|
-
|
|
397
|
-
func[ca1->data_type]
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
829
|
+
ca_monop_call_t k;
|
|
830
|
+
k.f = func[ca1->data_type];
|
|
831
|
+
k.n = ca1->elements;
|
|
832
|
+
k.m = ( ca2->mask ) ? (boolean8_t *)ca2->mask->ptr : NULL;
|
|
833
|
+
k.p2 = ca2->ptr; k.i2 = 1;
|
|
834
|
+
ca_attach(ca1); /* window: the kernel runs through ca_op_guarded when it can raise */
|
|
835
|
+
k.p1 = ca1->ptr; k.i1 = 1;
|
|
836
|
+
if ( ca_monop_may_raise(func, ca1->data_type) ) {
|
|
837
|
+
ca_op_guarded(ca_monop_call, &k, 1, &ca1, "r");
|
|
838
|
+
}
|
|
839
|
+
else {
|
|
840
|
+
ca_monop_call((VALUE) &k);
|
|
841
|
+
}
|
|
401
842
|
ca_detach(ca1);
|
|
402
843
|
}
|
|
403
844
|
else {
|
|
@@ -562,7 +1003,7 @@ VALUE
|
|
|
562
1003
|
ca_kleene_bool_fixup (VALUE vout, VALUE vself, VALUE vother, int is_or)
|
|
563
1004
|
{
|
|
564
1005
|
CArray *out, *a, *b;
|
|
565
|
-
volatile VALUE va, vb;
|
|
1006
|
+
volatile VALUE va, vb, holder;
|
|
566
1007
|
boolean8_t *ov, *om, *av, *am, *bv, *bm;
|
|
567
1008
|
ca_size_t n, i;
|
|
568
1009
|
|
|
@@ -582,9 +1023,12 @@ ca_kleene_bool_fixup (VALUE vout, VALUE vself, VALUE vother, int is_or)
|
|
|
582
1023
|
TypedData_Get_Struct(va, CArray, &carray_data_type, a);
|
|
583
1024
|
TypedData_Get_Struct(vb, CArray, &carray_data_type, b);
|
|
584
1025
|
|
|
1026
|
+
/* One temporary of Ruby's for all four: reading an operand can raise. */
|
|
585
1027
|
n = out->elements;
|
|
586
|
-
av =
|
|
587
|
-
|
|
1028
|
+
av = ALLOCV_N(boolean8_t, holder, 4 * (n > 0 ? n : 1));
|
|
1029
|
+
am = av + n;
|
|
1030
|
+
bv = am + n;
|
|
1031
|
+
bm = bv + n;
|
|
588
1032
|
kleene_gather_bool(a, av, am, n);
|
|
589
1033
|
kleene_gather_bool(b, bv, bm, n);
|
|
590
1034
|
|
|
@@ -608,7 +1052,7 @@ ca_kleene_bool_fixup (VALUE vout, VALUE vself, VALUE vother, int is_or)
|
|
|
608
1052
|
}
|
|
609
1053
|
}
|
|
610
1054
|
|
|
611
|
-
|
|
1055
|
+
ALLOCV_END(holder);
|
|
612
1056
|
return vout;
|
|
613
1057
|
}
|
|
614
1058
|
|
|
@@ -622,6 +1066,71 @@ ca_kleene_bool_fixup (VALUE vout, VALUE vself, VALUE vother, int is_or)
|
|
|
622
1066
|
CATile expansion, attach-hostile roots, and unattachable test
|
|
623
1067
|
fixtures working.
|
|
624
1068
|
Output `ca3` is always a new entity (write target, attach legitimate). */
|
|
1069
|
+
typedef struct {
|
|
1070
|
+
ca_binop_func_t *func;
|
|
1071
|
+
int8_t dt;
|
|
1072
|
+
CArray *out;
|
|
1073
|
+
ca_size_t step_out;
|
|
1074
|
+
} ca_binop_chunk_t;
|
|
1075
|
+
|
|
1076
|
+
static void
|
|
1077
|
+
ca_binop_chunk_kernel (ca_chunked_run_t *run, ca_size_t off, ca_size_t n,
|
|
1078
|
+
char **p)
|
|
1079
|
+
{
|
|
1080
|
+
ca_binop_chunk_t *d = (ca_binop_chunk_t *) run->data;
|
|
1081
|
+
ca_size_t o = d->step_out ? off : 0;
|
|
1082
|
+
d->func[d->dt](n,
|
|
1083
|
+
d->out->mask ? ((boolean8_t *) d->out->mask->ptr) + o : NULL,
|
|
1084
|
+
p[0], run->step[0],
|
|
1085
|
+
p[1], run->step[1],
|
|
1086
|
+
(char *) d->out->ptr + o * d->out->bytes, d->step_out);
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
/* A boolean array as an object array of true / false. The ordinary cast
|
|
1090
|
+
gives 1 / 0, which a logical operator on an object array reads as two
|
|
1091
|
+
true values (0 is true in Ruby). Masked cells stay masked. */
|
|
1092
|
+
static VALUE
|
|
1093
|
+
ca_boolean_as_truth_objects (VALUE vb)
|
|
1094
|
+
{
|
|
1095
|
+
CArray *cb;
|
|
1096
|
+
ca_size_t i;
|
|
1097
|
+
VALUE out;
|
|
1098
|
+
TypedData_Get_Struct(vb, CArray, &carray_data_type, cb);
|
|
1099
|
+
out = rb_carray_new(CA_OBJECT, cb->ndim, cb->dim, 0, NULL);
|
|
1100
|
+
for ( i = 0; i < cb->elements; i++ ) {
|
|
1101
|
+
rb_ca_store_addr(out, i, rb_ca_fetch_addr(vb, i));
|
|
1102
|
+
}
|
|
1103
|
+
return out;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
static int
|
|
1107
|
+
ca_value_is_type (VALUE v, int8_t data_type)
|
|
1108
|
+
{
|
|
1109
|
+
CArray *ca;
|
|
1110
|
+
if ( ! rb_obj_is_carray(v) ) return 0;
|
|
1111
|
+
TypedData_Get_Struct(v, CArray, &carray_data_type, ca);
|
|
1112
|
+
return ca->data_type == data_type;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
/* A logical operator (`logical`: one with a boolean kernel) between a
|
|
1116
|
+
boolean array and an object array reads the boolean cells as true /
|
|
1117
|
+
false. The eager binop and the lazy builder both call this before
|
|
1118
|
+
anything else decides the operands' types, so both read them alike. */
|
|
1119
|
+
void
|
|
1120
|
+
ca_binop_read_boolean_as_truth (int logical, volatile VALUE *pa,
|
|
1121
|
+
volatile VALUE *pb)
|
|
1122
|
+
{
|
|
1123
|
+
if ( ! logical ) {
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
if ( ca_value_is_type(*pa, CA_BOOLEAN) && ca_value_is_type(*pb, CA_OBJECT) ) {
|
|
1127
|
+
*pa = ca_boolean_as_truth_objects(*pa);
|
|
1128
|
+
}
|
|
1129
|
+
else if ( ca_value_is_type(*pb, CA_BOOLEAN) && ca_value_is_type(*pa, CA_OBJECT) ) {
|
|
1130
|
+
*pb = ca_boolean_as_truth_objects(*pb);
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
|
|
625
1134
|
VALUE
|
|
626
1135
|
rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
|
|
627
1136
|
ca_binop_func_t func[])
|
|
@@ -633,7 +1142,10 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
|
|
|
633
1142
|
ca_size_t i1, i2, i3;
|
|
634
1143
|
int fast_path;
|
|
635
1144
|
|
|
636
|
-
|
|
1145
|
+
ca_binop_read_boolean_as_truth(func[CA_BOOLEAN] != ca_binop_not_implement,
|
|
1146
|
+
&self, &other);
|
|
1147
|
+
|
|
1148
|
+
/* do implicit casting */
|
|
637
1149
|
rb_ca_cast_self_or_other(&self, &other);
|
|
638
1150
|
|
|
639
1151
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
|
|
@@ -652,6 +1164,10 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
|
|
|
652
1164
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
|
|
653
1165
|
TypedData_Get_Struct(other, CArray, &carray_data_type, ca2);
|
|
654
1166
|
}
|
|
1167
|
+
if ( func[ca1->data_type] == ca_binop_not_implement ) {
|
|
1168
|
+
ca_op_refuse_unimplemented(ca_is_face(ca1) ? self : other, ca1,
|
|
1169
|
+
rb_eCADataTypeError);
|
|
1170
|
+
}
|
|
655
1171
|
|
|
656
1172
|
self_is_scalar = RTEST(rb_obj_is_cscalar(self));
|
|
657
1173
|
other_is_scalar = RTEST(rb_obj_is_cscalar(other));
|
|
@@ -698,13 +1214,23 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
|
|
|
698
1214
|
fast_path = ca_attach_is_alias(ca1) && ca_attach_is_alias(ca2);
|
|
699
1215
|
|
|
700
1216
|
if ( fast_path ) {
|
|
701
|
-
/* FAST PATH:
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
1217
|
+
/* FAST PATH: both operands alias. */
|
|
1218
|
+
ca_binop_call_t k;
|
|
1219
|
+
CArray *held[2];
|
|
1220
|
+
k.f = func[ca1->data_type];
|
|
1221
|
+
k.n = n_kernel;
|
|
1222
|
+
k.m = ( ca3->mask ) ? (boolean8_t *) ca3->mask->ptr : NULL;
|
|
1223
|
+
k.p3 = ca3->ptr; k.i3 = i3;
|
|
1224
|
+
ca_attach_n(2, ca1, ca2); /* window: the kernel runs through ca_op_guarded when it can raise */
|
|
1225
|
+
k.p1 = ca1->ptr; k.i1 = i1;
|
|
1226
|
+
k.p2 = ca2->ptr; k.i2 = i2;
|
|
1227
|
+
if ( ca_binop_may_raise(func, ca1->data_type) ) {
|
|
1228
|
+
held[0] = ca1; held[1] = ca2;
|
|
1229
|
+
ca_op_guarded(ca_binop_call, &k, 2, held, "rr");
|
|
1230
|
+
}
|
|
1231
|
+
else {
|
|
1232
|
+
ca_binop_call((VALUE) &k);
|
|
1233
|
+
}
|
|
708
1234
|
ca_detach_n(2, ca1, ca2);
|
|
709
1235
|
}
|
|
710
1236
|
else if ( ca1 == ca2 ) {
|
|
@@ -744,13 +1270,8 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
|
|
|
744
1270
|
- both array & both non-alias (= mt + mt2) → CHUNKED (peak 2x win)
|
|
745
1271
|
- one non-alias array + one alias/scalar (= mt + 3.14, mt + entity)
|
|
746
1272
|
→ 1-shot ALLOCV */
|
|
747
|
-
char *p1_src = NULL, *p2_src = NULL;
|
|
748
|
-
void *s1_arena = NULL, *s2_arena = NULL;
|
|
749
|
-
int gather_per_chunk1 = 0, gather_per_chunk2 = 0;
|
|
750
|
-
int attached1 = 0, attached2 = 0;
|
|
751
1273
|
int8_t dt = ca1->data_type;
|
|
752
1274
|
ca_size_t chunk_n;
|
|
753
|
-
ca_size_t off;
|
|
754
1275
|
int nonalias_arrays;
|
|
755
1276
|
int use_chunked;
|
|
756
1277
|
|
|
@@ -895,24 +1416,40 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
|
|
|
895
1416
|
int a1 = 0, a2 = 0;
|
|
896
1417
|
(void) h1; (void) h2;
|
|
897
1418
|
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
} else {
|
|
1419
|
+
/* Gathers first: a gather can raise, and nothing is attached yet. */
|
|
1420
|
+
if ( ! ca_attach_is_alias(ca1) ) {
|
|
901
1421
|
p1 = ALLOCV_N(char, h1, ca1->elements * ca1->bytes);
|
|
902
1422
|
ca_xfer_all(ca1, p1, CA_XFER_GET);
|
|
903
1423
|
}
|
|
904
|
-
if ( ca_attach_is_alias(ca2) ) {
|
|
905
|
-
ca_attach(ca2); p2 = (char *) ca2->ptr; a2 = 1;
|
|
906
|
-
} else {
|
|
1424
|
+
if ( ! ca_attach_is_alias(ca2) ) {
|
|
907
1425
|
p2 = ALLOCV_N(char, h2, ca2->elements * ca2->bytes);
|
|
908
1426
|
ca_xfer_all(ca2, p2, CA_XFER_GET);
|
|
909
1427
|
}
|
|
1428
|
+
if ( ca_attach_is_alias(ca1) ) {
|
|
1429
|
+
ca_attach(ca1); p1 = (char *) ca1->ptr; a1 = 1; /* window: an alias attach, cannot raise */
|
|
1430
|
+
}
|
|
1431
|
+
if ( ca_attach_is_alias(ca2) ) {
|
|
1432
|
+
ca_attach(ca2); p2 = (char *) ca2->ptr; a2 = 1; /* window: as above */
|
|
1433
|
+
}
|
|
910
1434
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
1435
|
+
{
|
|
1436
|
+
ca_binop_call_t k;
|
|
1437
|
+
CArray *held[2];
|
|
1438
|
+
k.f = func[dt];
|
|
1439
|
+
k.n = n_kernel;
|
|
1440
|
+
k.m = ( ca3->mask ) ? (boolean8_t *) ca3->mask->ptr : NULL;
|
|
1441
|
+
k.p1 = p1; k.i1 = i1;
|
|
1442
|
+
k.p2 = p2; k.i2 = i2;
|
|
1443
|
+
k.p3 = ca3->ptr; k.i3 = i3;
|
|
1444
|
+
if ( ca_binop_may_raise(func, dt) ) {
|
|
1445
|
+
held[0] = a1 ? ca1 : NULL;
|
|
1446
|
+
held[1] = a2 ? ca2 : NULL;
|
|
1447
|
+
ca_op_guarded(ca_binop_call, &k, 2, held, "rr");
|
|
1448
|
+
}
|
|
1449
|
+
else {
|
|
1450
|
+
ca_binop_call((VALUE) &k);
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
916
1453
|
|
|
917
1454
|
if ( a2 ) { ca_detach(ca2); } else { ALLOCV_END(h2); }
|
|
918
1455
|
if ( a1 ) { ca_detach(ca1); } else { ALLOCV_END(h1); }
|
|
@@ -934,88 +1471,34 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
|
|
|
934
1471
|
chunk_n = ca_chunk_compute_n(n_kernel, inner, maxb);
|
|
935
1472
|
}
|
|
936
1473
|
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
s1_arena = ca_op_acquire_operand_scratch(ca1, 1);
|
|
948
|
-
ca_xfer_all(ca1, s1_arena, CA_XFER_GET);
|
|
949
|
-
p1_src = (char *) s1_arena;
|
|
950
|
-
}
|
|
951
|
-
} else if ( ca_attach_is_alias(ca1) ) {
|
|
952
|
-
ca_attach(ca1);
|
|
953
|
-
p1_src = (char *) ca1->ptr;
|
|
954
|
-
attached1 = 1;
|
|
955
|
-
} else {
|
|
956
|
-
s1_arena = ca_op_acquire_operand_scratch(ca1, chunk_n);
|
|
957
|
-
p1_src = (char *) s1_arena;
|
|
958
|
-
gather_per_chunk1 = 1;
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
/* ca2 acquire (mirror) */
|
|
962
|
-
if ( i2 == 0 ) {
|
|
963
|
-
if ( ca_attach_is_alias(ca2) ) {
|
|
964
|
-
ca_attach(ca2);
|
|
965
|
-
p2_src = (char *) ca2->ptr;
|
|
966
|
-
attached2 = 1;
|
|
967
|
-
} else {
|
|
968
|
-
s2_arena = ca_op_acquire_operand_scratch(ca2, 1);
|
|
969
|
-
ca_xfer_all(ca2, s2_arena, CA_XFER_GET);
|
|
970
|
-
p2_src = (char *) s2_arena;
|
|
971
|
-
}
|
|
972
|
-
} else if ( ca_attach_is_alias(ca2) ) {
|
|
973
|
-
ca_attach(ca2);
|
|
974
|
-
p2_src = (char *) ca2->ptr;
|
|
975
|
-
attached2 = 1;
|
|
976
|
-
} else {
|
|
977
|
-
s2_arena = ca_op_acquire_operand_scratch(ca2, chunk_n);
|
|
978
|
-
p2_src = (char *) s2_arena;
|
|
979
|
-
gather_per_chunk2 = 1;
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
/* chunk loop: walk n_kernel cells in chunk_n strides */
|
|
983
|
-
for ( off = 0; off < n_kernel; off += chunk_n ) {
|
|
984
|
-
ca_size_t n_done = (off + chunk_n > n_kernel) ? n_kernel - off : chunk_n;
|
|
985
|
-
char *p1, *p2;
|
|
986
|
-
|
|
987
|
-
if ( gather_per_chunk1 ) {
|
|
988
|
-
ca_chunked_gather(ca1, off, n_done, s1_arena);
|
|
989
|
-
p1 = (char *) s1_arena;
|
|
990
|
-
} else {
|
|
991
|
-
p1 = p1_src + (i1 ? off * ca1->bytes : 0);
|
|
992
|
-
}
|
|
993
|
-
if ( gather_per_chunk2 ) {
|
|
994
|
-
ca_chunked_gather(ca2, off, n_done, s2_arena);
|
|
995
|
-
p2 = (char *) s2_arena;
|
|
996
|
-
} else {
|
|
997
|
-
p2 = p2_src + (i2 ? off * ca2->bytes : 0);
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
func[dt](n_done,
|
|
1001
|
-
ca3->mask ? ((boolean8_t *) ca3->mask->ptr) + (i3 ? off : 0)
|
|
1002
|
-
: NULL,
|
|
1003
|
-
p1, i1,
|
|
1004
|
-
p2, i2,
|
|
1005
|
-
(char *) ca3->ptr + (i3 ? off * ca3->bytes : 0), i3);
|
|
1474
|
+
{
|
|
1475
|
+
ca_binop_chunk_t d;
|
|
1476
|
+
ca_chunked_run_t run;
|
|
1477
|
+
d.func = func; d.dt = dt; d.out = ca3; d.step_out = i3;
|
|
1478
|
+
ca_chunked_run_init(&run, n_kernel, chunk_n);
|
|
1479
|
+
ca_chunked_run_add(&run, ca1, i1);
|
|
1480
|
+
ca_chunked_run_add(&run, ca2, i2);
|
|
1481
|
+
run.kernel = ca_binop_chunk_kernel;
|
|
1482
|
+
run.data = &d;
|
|
1483
|
+
ca_chunked_run(&run);
|
|
1006
1484
|
}
|
|
1007
|
-
|
|
1008
|
-
if ( s2_arena ) ca_lazy_arena_release(s2_arena);
|
|
1009
|
-
if ( s1_arena ) ca_lazy_arena_release(s1_arena);
|
|
1010
|
-
if ( attached2 ) ca_detach(ca2);
|
|
1011
|
-
if ( attached1 ) ca_detach(ca1);
|
|
1012
|
-
|
|
1013
|
-
ca_lazy_arena_exit();
|
|
1014
1485
|
}
|
|
1015
1486
|
|
|
1016
1487
|
return out;
|
|
1017
1488
|
}
|
|
1018
1489
|
|
|
1490
|
+
/* An input of a bang operation that lives in the same root as the target
|
|
1491
|
+
may share memory with it, and read in place it would show the kernel
|
|
1492
|
+
cells the kernel has already written (`a[1..5].add!(a[0..4])` would
|
|
1493
|
+
become a running sum). Such an input is gathered whole before the
|
|
1494
|
+
kernel runs. The target itself (`a.add!(a)`) reads each cell before
|
|
1495
|
+
writing it, so it is read in place. */
|
|
1496
|
+
static int
|
|
1497
|
+
ca_bang_input_may_overlap (CArray *target, CArray *input)
|
|
1498
|
+
{
|
|
1499
|
+
return input != target && ca_shares_root(input, target);
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1019
1502
|
/* Bang (in-place) variant. Invariant: the input-only operand (= other)
|
|
1020
1503
|
must not be attached; self IS the output (write target, attach
|
|
1021
1504
|
legitimate). Same fast/slow pattern as rb_ca_call_binop, applied to
|
|
@@ -1030,11 +1513,14 @@ rb_ca_call_binop_bang (VALUE self, VALUE other, ca_binop_func_t func[])
|
|
|
1030
1513
|
|
|
1031
1514
|
rb_ca_modify(self);
|
|
1032
1515
|
|
|
1033
|
-
/* do implicit casting
|
|
1516
|
+
/* do implicit casting */
|
|
1034
1517
|
rb_ca_cast_other(&self, &other);
|
|
1035
1518
|
|
|
1036
1519
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
|
|
1037
1520
|
TypedData_Get_Struct(other, CArray, &carray_data_type, ca2);
|
|
1521
|
+
if ( func[ca1->data_type] == ca_binop_not_implement ) {
|
|
1522
|
+
ca_op_refuse_unimplemented(self, ca1, rb_eCADataTypeError);
|
|
1523
|
+
}
|
|
1038
1524
|
|
|
1039
1525
|
self_is_scalar = RTEST(rb_obj_is_cscalar(self));
|
|
1040
1526
|
other_is_scalar = RTEST(rb_obj_is_cscalar(other));
|
|
@@ -1042,17 +1528,11 @@ rb_ca_call_binop_bang (VALUE self, VALUE other, ca_binop_func_t func[])
|
|
|
1042
1528
|
/* self is the write target, so its shape is the result's by definition
|
|
1043
1529
|
and the destination rule applies (the same one assignment uses), not
|
|
1044
1530
|
the symmetric one a binary operation is held to. */
|
|
1045
|
-
if ( !
|
|
1531
|
+
if ( !other_is_scalar ) {
|
|
1046
1532
|
ca_broadcast_to_destination(self, &other);
|
|
1047
1533
|
TypedData_Get_Struct(other, CArray, &carray_data_type, ca2);
|
|
1048
1534
|
other_is_scalar = RTEST(rb_obj_is_cscalar(other));
|
|
1049
1535
|
}
|
|
1050
|
-
if ( self_is_scalar && !other_is_scalar &&
|
|
1051
|
-
ca1->elements != ca2->elements ) {
|
|
1052
|
-
rb_raise(rb_eRuntimeError, "elements mismatch (%" PRId64 " <-> %" PRId64 ")",
|
|
1053
|
-
(ca_size_t) ca1->elements,
|
|
1054
|
-
(ca_size_t) ca2->elements);
|
|
1055
|
-
}
|
|
1056
1536
|
|
|
1057
1537
|
/* kernel strides: cscalar → 0 (broadcast). ca1 is both src1 (input)
|
|
1058
1538
|
and dst (output), same stride. ca2 is input only. */
|
|
@@ -1062,36 +1542,52 @@ rb_ca_call_binop_bang (VALUE self, VALUE other, ca_binop_func_t func[])
|
|
|
1062
1542
|
/* self IS the output (= write target). Always attach + ca_sync
|
|
1063
1543
|
(= legitimate per refined invariant; self can be view e.g.
|
|
1064
1544
|
`arr[i,nil].add!(b)`, sync writes back to root). */
|
|
1065
|
-
|
|
1066
|
-
ca_mask_overlay_safe(ca1, 2, ca1, ca2);
|
|
1067
|
-
|
|
1068
|
-
/* other is input only: fast path if alias-cheap, else materialise
|
|
1069
|
-
via ca_xfer_all without ca_func[X].attach. */
|
|
1070
|
-
if ( ca_attach_is_alias(ca2) ) {
|
|
1071
|
-
ca_attach(ca2);
|
|
1072
|
-
func[ca1->data_type](ca1->elements,
|
|
1073
|
-
( ca1->mask ) ? (boolean8_t *) ca1->mask->ptr : NULL,
|
|
1074
|
-
ca1->ptr, i1,
|
|
1075
|
-
ca2->ptr, i2,
|
|
1076
|
-
ca1->ptr, i1);
|
|
1077
|
-
ca_detach(ca2);
|
|
1078
|
-
}
|
|
1079
|
-
else {
|
|
1545
|
+
{
|
|
1080
1546
|
volatile VALUE h2 = Qnil;
|
|
1081
|
-
char *p2;
|
|
1547
|
+
char *p2 = NULL;
|
|
1548
|
+
int a2 = 0;
|
|
1549
|
+
int in_place2 = ca_attach_is_alias(ca2) &&
|
|
1550
|
+
! ca_bang_input_may_overlap(ca1, ca2);
|
|
1551
|
+
ca_binop_call_t k;
|
|
1552
|
+
CArray *held[2];
|
|
1082
1553
|
(void) h2;
|
|
1083
|
-
|
|
1084
|
-
ca_xfer_all
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1554
|
+
|
|
1555
|
+
/* other is input only: materialised via ca_xfer_all when it cannot be
|
|
1556
|
+
read in place -- before self is attached, since a gather can raise. */
|
|
1557
|
+
if ( ! in_place2 ) {
|
|
1558
|
+
p2 = ALLOCV_N(char, h2, ca2->elements * ca2->bytes);
|
|
1559
|
+
ca_xfer_all(ca2, p2, CA_XFER_GET);
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
ca_attach(ca1); /* window: the overlay detaches it if it raises; the
|
|
1563
|
+
kernel runs through ca_op_guarded when it can */
|
|
1564
|
+
ca_mask_overlay_attached(ca1, 2, ca1, ca2, NULL);
|
|
1565
|
+
|
|
1566
|
+
if ( in_place2 ) {
|
|
1567
|
+
ca_attach(ca2); /* window: an alias attach, which cannot raise */
|
|
1568
|
+
p2 = ca2->ptr;
|
|
1569
|
+
a2 = 1;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
k.f = func[ca1->data_type];
|
|
1573
|
+
k.n = ca1->elements;
|
|
1574
|
+
k.m = ( ca1->mask ) ? (boolean8_t *) ca1->mask->ptr : NULL;
|
|
1575
|
+
k.p1 = ca1->ptr; k.i1 = i1;
|
|
1576
|
+
k.p2 = p2; k.i2 = i2;
|
|
1577
|
+
k.p3 = ca1->ptr; k.i3 = i1;
|
|
1578
|
+
if ( ca_binop_may_raise(func, ca1->data_type) ) {
|
|
1579
|
+
held[0] = ca1;
|
|
1580
|
+
held[1] = a2 ? ca2 : NULL;
|
|
1581
|
+
ca_op_guarded(ca_binop_call, &k, 2, held, "wr");
|
|
1582
|
+
}
|
|
1583
|
+
else {
|
|
1584
|
+
ca_binop_call((VALUE) &k);
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
if ( a2 ) { ca_detach(ca2); } else { ALLOCV_END(h2); }
|
|
1091
1588
|
}
|
|
1092
1589
|
|
|
1093
|
-
|
|
1094
|
-
ca_detach(ca1);
|
|
1590
|
+
ca_sync_detach(ca1);
|
|
1095
1591
|
|
|
1096
1592
|
return self;
|
|
1097
1593
|
}
|
|
@@ -1131,18 +1627,24 @@ rb_ca_triop_select_template (VALUE self, VALUE other2, VALUE other3,
|
|
|
1131
1627
|
- alias-cheap operand → ca_attach (= O(1)) + use ca->ptr directly
|
|
1132
1628
|
- else → ALLOCV scratch + ca_xfer_all without ca_func[X].attach
|
|
1133
1629
|
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1630
|
+
GATHER every operand before ATTACH-ing any: a gather can raise, and an
|
|
1631
|
+
operand attached before it would be left attached. Pair with RELEASE;
|
|
1632
|
+
`h` must be a volatile VALUE declared by the caller (Qnil when nothing
|
|
1633
|
+
was gathered, which ALLOCV_END takes as a no-op). */
|
|
1634
|
+
#define EAGER_GATHER_INPUT(ca_, p_, h_) do { \
|
|
1635
|
+
if ( ! ca_attach_is_alias(ca_) ) { \
|
|
1636
|
+
(p_) = ALLOCV_N(char, (h_), (ca_)->elements * (ca_)->bytes); \
|
|
1637
|
+
ca_xfer_all((ca_), (p_), CA_XFER_GET); \
|
|
1638
|
+
} \
|
|
1639
|
+
} while (0)
|
|
1640
|
+
|
|
1641
|
+
#define EAGER_ATTACH_INPUT(ca_, p_, attached_) do { \
|
|
1138
1642
|
if ( ca_attach_is_alias(ca_) ) { \
|
|
1139
|
-
ca_attach(ca_);
|
|
1643
|
+
ca_attach(ca_); /* window: an alias attach, cannot raise */ \
|
|
1140
1644
|
(p_) = (char *)(ca_)->ptr; \
|
|
1141
1645
|
(attached_) = 1; \
|
|
1142
1646
|
} \
|
|
1143
1647
|
else { \
|
|
1144
|
-
(p_) = ALLOCV_N(char, (h_), (ca_)->elements * (ca_)->bytes); \
|
|
1145
|
-
ca_xfer_all((ca_), (p_), CA_XFER_GET); \
|
|
1146
1648
|
(attached_) = 0; \
|
|
1147
1649
|
} \
|
|
1148
1650
|
} while (0)
|
|
@@ -1156,6 +1658,39 @@ rb_ca_triop_select_template (VALUE self, VALUE other2, VALUE other3,
|
|
|
1156
1658
|
them); cao = new entity output (attach is legit). Each input
|
|
1157
1659
|
independently uses fast (= alias) or slow (= ALLOCV + ca_xfer_all)
|
|
1158
1660
|
path. */
|
|
1661
|
+
/* The triop output is always a fresh array the size of the run; for the
|
|
1662
|
+
bang form it is self, read and written in place. */
|
|
1663
|
+
typedef struct {
|
|
1664
|
+
ca_triop_func_t *func;
|
|
1665
|
+
int8_t dt;
|
|
1666
|
+
CArray *out;
|
|
1667
|
+
int in_place;
|
|
1668
|
+
} ca_triop_chunk_t;
|
|
1669
|
+
|
|
1670
|
+
static void
|
|
1671
|
+
ca_triop_chunk_kernel (ca_chunked_run_t *run, ca_size_t off, ca_size_t n,
|
|
1672
|
+
char **p)
|
|
1673
|
+
{
|
|
1674
|
+
ca_triop_chunk_t *d = (ca_triop_chunk_t *) run->data;
|
|
1675
|
+
char *out = (char *) d->out->ptr + off * d->out->bytes;
|
|
1676
|
+
boolean8_t *m = d->out->mask ? ((boolean8_t *) d->out->mask->ptr) + off
|
|
1677
|
+
: NULL;
|
|
1678
|
+
if ( d->in_place ) {
|
|
1679
|
+
d->func[d->dt](n, m,
|
|
1680
|
+
out, 1,
|
|
1681
|
+
p[0], run->step[0],
|
|
1682
|
+
p[1], run->step[1],
|
|
1683
|
+
out, 1);
|
|
1684
|
+
}
|
|
1685
|
+
else {
|
|
1686
|
+
d->func[d->dt](n, m,
|
|
1687
|
+
p[0], run->step[0],
|
|
1688
|
+
p[1], run->step[1],
|
|
1689
|
+
p[2], run->step[2],
|
|
1690
|
+
out, 1);
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1159
1694
|
VALUE
|
|
1160
1695
|
rb_ca_call_triop (VALUE self, VALUE other2, VALUE other3,
|
|
1161
1696
|
ca_triop_func_t func[])
|
|
@@ -1194,6 +1729,11 @@ rb_ca_call_triop (VALUE self, VALUE other2, VALUE other3,
|
|
|
1194
1729
|
TypedData_Get_Struct(other2, CArray, &carray_data_type, ca2);
|
|
1195
1730
|
TypedData_Get_Struct(other3, CArray, &carray_data_type, ca3);
|
|
1196
1731
|
}
|
|
1732
|
+
if ( func[ca1->data_type] == ca_triop_not_implement ) {
|
|
1733
|
+
ca_op_refuse_unimplemented(ca_is_face(ca1) ? self
|
|
1734
|
+
: ca_is_face(ca2) ? other2 : other3,
|
|
1735
|
+
ca1, rb_eCADataTypeError);
|
|
1736
|
+
}
|
|
1197
1737
|
|
|
1198
1738
|
/* Element-count check: all non-scalar operands must agree. */
|
|
1199
1739
|
{
|
|
@@ -1242,16 +1782,33 @@ rb_ca_call_triop (VALUE self, VALUE other2, VALUE other3,
|
|
|
1242
1782
|
int attached1, attached2, attached3;
|
|
1243
1783
|
(void) h1; (void) h2; (void) h3;
|
|
1244
1784
|
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1785
|
+
EAGER_GATHER_INPUT(ca1, p1, h1);
|
|
1786
|
+
EAGER_GATHER_INPUT(ca2, p2, h2);
|
|
1787
|
+
EAGER_GATHER_INPUT(ca3, p3, h3);
|
|
1788
|
+
EAGER_ATTACH_INPUT(ca1, p1, attached1);
|
|
1789
|
+
EAGER_ATTACH_INPUT(ca2, p2, attached2);
|
|
1790
|
+
EAGER_ATTACH_INPUT(ca3, p3, attached3);
|
|
1248
1791
|
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1792
|
+
{
|
|
1793
|
+
ca_triop_call_t k;
|
|
1794
|
+
CArray *held[3];
|
|
1795
|
+
k.f = func[ca1->data_type];
|
|
1796
|
+
k.n = cao->elements;
|
|
1797
|
+
k.m = ( cao->mask ) ? (boolean8_t *) cao->mask->ptr : NULL;
|
|
1798
|
+
k.p1 = p1; k.i1 = s1;
|
|
1799
|
+
k.p2 = p2; k.i2 = s2;
|
|
1800
|
+
k.p3 = p3; k.i3 = s3;
|
|
1801
|
+
k.p4 = cao->ptr; k.i4 = 1;
|
|
1802
|
+
if ( ca_triop_may_raise(func, ca1->data_type) ) {
|
|
1803
|
+
held[0] = attached1 ? ca1 : NULL;
|
|
1804
|
+
held[1] = attached2 ? ca2 : NULL;
|
|
1805
|
+
held[2] = attached3 ? ca3 : NULL;
|
|
1806
|
+
ca_op_guarded(ca_triop_call, &k, 3, held, "rrr");
|
|
1807
|
+
}
|
|
1808
|
+
else {
|
|
1809
|
+
ca_triop_call((VALUE) &k);
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1255
1812
|
|
|
1256
1813
|
EAGER_RELEASE_INPUT(ca3, h3, attached3);
|
|
1257
1814
|
EAGER_RELEASE_INPUT(ca2, h2, attached2);
|
|
@@ -1261,13 +1818,8 @@ rb_ca_call_triop (VALUE self, VALUE other2, VALUE other3,
|
|
|
1261
1818
|
/* CHUNKED PATH: per-operand decision matrix (the binop pattern
|
|
1262
1819
|
extended to 3 inputs). scalar/alias same as before; non-alias
|
|
1263
1820
|
array goes through per-chunk gather. */
|
|
1264
|
-
char *p1_src = NULL, *p2_src = NULL, *p3_src = NULL;
|
|
1265
|
-
void *s1_arena = NULL, *s2_arena = NULL, *s3_arena = NULL;
|
|
1266
|
-
int gpc1 = 0, gpc2 = 0, gpc3 = 0; /* gather-per-chunk flags */
|
|
1267
|
-
int att1 = 0, att2 = 0, att3 = 0;
|
|
1268
1821
|
int8_t dt = ca1->data_type;
|
|
1269
1822
|
ca_size_t chunk_n;
|
|
1270
|
-
ca_size_t off;
|
|
1271
1823
|
ca_size_t n_total = cao->elements;
|
|
1272
1824
|
|
|
1273
1825
|
{
|
|
@@ -1288,86 +1840,18 @@ rb_ca_call_triop (VALUE self, VALUE other2, VALUE other3,
|
|
|
1288
1840
|
chunk_n = ca_chunk_compute_n(n_total, inner, maxb);
|
|
1289
1841
|
}
|
|
1290
1842
|
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
} else if ( ca_attach_is_alias(ca1) ) {
|
|
1303
|
-
ca_attach(ca1); p1_src = (char *) ca1->ptr; att1 = 1;
|
|
1304
|
-
} else {
|
|
1305
|
-
s1_arena = ca_op_acquire_operand_scratch(ca1, chunk_n);
|
|
1306
|
-
p1_src = (char *) s1_arena; gpc1 = 1;
|
|
1307
|
-
}
|
|
1308
|
-
/* ca2 acquire (mirror) */
|
|
1309
|
-
if ( s2 == 0 ) {
|
|
1310
|
-
if ( ca_attach_is_alias(ca2) ) {
|
|
1311
|
-
ca_attach(ca2); p2_src = (char *) ca2->ptr; att2 = 1;
|
|
1312
|
-
} else {
|
|
1313
|
-
s2_arena = ca_op_acquire_operand_scratch(ca2, 1);
|
|
1314
|
-
ca_xfer_all(ca2, s2_arena, CA_XFER_GET);
|
|
1315
|
-
p2_src = (char *) s2_arena;
|
|
1316
|
-
}
|
|
1317
|
-
} else if ( ca_attach_is_alias(ca2) ) {
|
|
1318
|
-
ca_attach(ca2); p2_src = (char *) ca2->ptr; att2 = 1;
|
|
1319
|
-
} else {
|
|
1320
|
-
s2_arena = ca_op_acquire_operand_scratch(ca2, chunk_n);
|
|
1321
|
-
p2_src = (char *) s2_arena; gpc2 = 1;
|
|
1322
|
-
}
|
|
1323
|
-
/* ca3 acquire (mirror) */
|
|
1324
|
-
if ( s3 == 0 ) {
|
|
1325
|
-
if ( ca_attach_is_alias(ca3) ) {
|
|
1326
|
-
ca_attach(ca3); p3_src = (char *) ca3->ptr; att3 = 1;
|
|
1327
|
-
} else {
|
|
1328
|
-
s3_arena = ca_op_acquire_operand_scratch(ca3, 1);
|
|
1329
|
-
ca_xfer_all(ca3, s3_arena, CA_XFER_GET);
|
|
1330
|
-
p3_src = (char *) s3_arena;
|
|
1331
|
-
}
|
|
1332
|
-
} else if ( ca_attach_is_alias(ca3) ) {
|
|
1333
|
-
ca_attach(ca3); p3_src = (char *) ca3->ptr; att3 = 1;
|
|
1334
|
-
} else {
|
|
1335
|
-
s3_arena = ca_op_acquire_operand_scratch(ca3, chunk_n);
|
|
1336
|
-
p3_src = (char *) s3_arena; gpc3 = 1;
|
|
1337
|
-
}
|
|
1338
|
-
|
|
1339
|
-
for ( off = 0; off < n_total; off += chunk_n ) {
|
|
1340
|
-
ca_size_t n_done = (off + chunk_n > n_total) ? n_total - off
|
|
1341
|
-
: chunk_n;
|
|
1342
|
-
char *p1, *p2, *p3;
|
|
1343
|
-
|
|
1344
|
-
if ( gpc1 ) { ca_chunked_gather(ca1, off, n_done, s1_arena);
|
|
1345
|
-
p1 = (char *) s1_arena; }
|
|
1346
|
-
else { p1 = p1_src + (s1 ? off * ca1->bytes : 0); }
|
|
1347
|
-
if ( gpc2 ) { ca_chunked_gather(ca2, off, n_done, s2_arena);
|
|
1348
|
-
p2 = (char *) s2_arena; }
|
|
1349
|
-
else { p2 = p2_src + (s2 ? off * ca2->bytes : 0); }
|
|
1350
|
-
if ( gpc3 ) { ca_chunked_gather(ca3, off, n_done, s3_arena);
|
|
1351
|
-
p3 = (char *) s3_arena; }
|
|
1352
|
-
else { p3 = p3_src + (s3 ? off * ca3->bytes : 0); }
|
|
1353
|
-
|
|
1354
|
-
func[dt](n_done,
|
|
1355
|
-
cao->mask ? ((boolean8_t *) cao->mask->ptr) + off
|
|
1356
|
-
: NULL,
|
|
1357
|
-
p1, s1,
|
|
1358
|
-
p2, s2,
|
|
1359
|
-
p3, s3,
|
|
1360
|
-
(char *) cao->ptr + off * cao->bytes, 1);
|
|
1843
|
+
{
|
|
1844
|
+
ca_triop_chunk_t d;
|
|
1845
|
+
ca_chunked_run_t run;
|
|
1846
|
+
d.func = func; d.dt = dt; d.out = cao; d.in_place = 0;
|
|
1847
|
+
ca_chunked_run_init(&run, n_total, chunk_n);
|
|
1848
|
+
ca_chunked_run_add(&run, ca1, s1);
|
|
1849
|
+
ca_chunked_run_add(&run, ca2, s2);
|
|
1850
|
+
ca_chunked_run_add(&run, ca3, s3);
|
|
1851
|
+
run.kernel = ca_triop_chunk_kernel;
|
|
1852
|
+
run.data = &d;
|
|
1853
|
+
ca_chunked_run(&run);
|
|
1361
1854
|
}
|
|
1362
|
-
|
|
1363
|
-
if ( s3_arena ) ca_lazy_arena_release(s3_arena);
|
|
1364
|
-
if ( s2_arena ) ca_lazy_arena_release(s2_arena);
|
|
1365
|
-
if ( s1_arena ) ca_lazy_arena_release(s1_arena);
|
|
1366
|
-
if ( att3 ) ca_detach(ca3);
|
|
1367
|
-
if ( att2 ) ca_detach(ca2);
|
|
1368
|
-
if ( att1 ) ca_detach(ca1);
|
|
1369
|
-
|
|
1370
|
-
ca_lazy_arena_exit();
|
|
1371
1855
|
}
|
|
1372
1856
|
}
|
|
1373
1857
|
|
|
@@ -1377,6 +1861,13 @@ rb_ca_call_triop (VALUE self, VALUE other2, VALUE other3,
|
|
|
1377
1861
|
/* triop_bang (in-place) driver. ca1 = self = output (write target,
|
|
1378
1862
|
attach legit; keep ca_attach + ca_sync); ca2/ca3 = input only (fast/slow
|
|
1379
1863
|
dispatch via EAGER_ACQUIRE/RELEASE). */
|
|
1864
|
+
static VALUE
|
|
1865
|
+
ca_triop_bang_chunked (VALUE arg)
|
|
1866
|
+
{
|
|
1867
|
+
ca_chunked_run((ca_chunked_run_t *) arg);
|
|
1868
|
+
return Qnil;
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1380
1871
|
VALUE
|
|
1381
1872
|
rb_ca_call_triop_bang (VALUE self, VALUE other2, VALUE other3,
|
|
1382
1873
|
ca_triop_func_t func[])
|
|
@@ -1391,6 +1882,9 @@ rb_ca_call_triop_bang (VALUE self, VALUE other2, VALUE other3,
|
|
|
1391
1882
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
|
|
1392
1883
|
TypedData_Get_Struct(other2, CArray, &carray_data_type, ca2);
|
|
1393
1884
|
TypedData_Get_Struct(other3, CArray, &carray_data_type, ca3);
|
|
1885
|
+
if ( func[ca1->data_type] == ca_triop_not_implement ) {
|
|
1886
|
+
ca_op_refuse_unimplemented(self, ca1, rb_eCADataTypeError);
|
|
1887
|
+
}
|
|
1394
1888
|
|
|
1395
1889
|
/* self is the write target, so the destination rule applies to each
|
|
1396
1890
|
input operand in turn (the same one assignment uses). */
|
|
@@ -1403,10 +1897,6 @@ rb_ca_call_triop_bang (VALUE self, VALUE other2, VALUE other3,
|
|
|
1403
1897
|
TypedData_Get_Struct(other3, CArray, &carray_data_type, ca3);
|
|
1404
1898
|
}
|
|
1405
1899
|
|
|
1406
|
-
/* self IS the output (= write target; attach legit per refined invariant) */
|
|
1407
|
-
ca_attach(ca1);
|
|
1408
|
-
ca_mask_overlay_safe(ca1, 3, ca1, ca2, ca3);
|
|
1409
|
-
|
|
1410
1900
|
{
|
|
1411
1901
|
ca_size_t s2 = rb_obj_is_cscalar(other2) ? 0 : 1;
|
|
1412
1902
|
ca_size_t s3 = rb_obj_is_cscalar(other3) ? 0 : 1;
|
|
@@ -1414,81 +1904,119 @@ rb_ca_call_triop_bang (VALUE self, VALUE other2, VALUE other3,
|
|
|
1414
1904
|
/* Threshold dispatch on the input-only operands (ca2 / ca3). Self
|
|
1415
1905
|
(ca1) IS the output (attached + ca_sync as usual). >= 2 non-alias
|
|
1416
1906
|
input arrays → chunked, else 1-shot ALLOCV. */
|
|
1907
|
+
/* An input that may share memory with self is gathered whole, never
|
|
1908
|
+
read in place or per chunk: either would read cells already
|
|
1909
|
+
written. */
|
|
1910
|
+
int overlap2 = ca_bang_input_may_overlap(ca1, ca2);
|
|
1911
|
+
int overlap3 = ca_bang_input_may_overlap(ca1, ca3);
|
|
1417
1912
|
int nonalias_arrays = 0;
|
|
1418
1913
|
int use_chunked;
|
|
1419
1914
|
if ( s2 == 1 && !ca_attach_is_alias(ca2) ) nonalias_arrays++;
|
|
1420
1915
|
if ( s3 == 1 && !ca_attach_is_alias(ca3) ) nonalias_arrays++;
|
|
1421
|
-
use_chunked = (nonalias_arrays >= 2);
|
|
1916
|
+
use_chunked = (nonalias_arrays >= 2) && !overlap2 && !overlap3;
|
|
1422
1917
|
|
|
1423
1918
|
if ( !use_chunked ) {
|
|
1424
1919
|
volatile VALUE h2 = Qnil, h3 = Qnil;
|
|
1425
1920
|
char *p2, *p3;
|
|
1426
1921
|
int attached2, attached3;
|
|
1922
|
+
ca_triop_call_t k;
|
|
1923
|
+
CArray *held[3];
|
|
1427
1924
|
(void) h2; (void) h3;
|
|
1428
1925
|
|
|
1429
|
-
|
|
1430
|
-
|
|
1926
|
+
int in_place2 = ca_attach_is_alias(ca2) && !overlap2;
|
|
1927
|
+
int in_place3 = ca_attach_is_alias(ca3) && !overlap3;
|
|
1431
1928
|
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1929
|
+
/* The gathers can raise, so they run before self is attached. */
|
|
1930
|
+
if ( ! in_place2 ) {
|
|
1931
|
+
p2 = ALLOCV_N(char, h2, ca2->elements * ca2->bytes);
|
|
1932
|
+
ca_xfer_all(ca2, p2, CA_XFER_GET);
|
|
1933
|
+
}
|
|
1934
|
+
if ( ! in_place3 ) {
|
|
1935
|
+
p3 = ALLOCV_N(char, h3, ca3->elements * ca3->bytes);
|
|
1936
|
+
ca_xfer_all(ca3, p3, CA_XFER_GET);
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
/* self IS the output (= write target; attach legit per refined
|
|
1940
|
+
invariant) */
|
|
1941
|
+
ca_attach(ca1); /* window: the overlay detaches it if it raises; the
|
|
1942
|
+
kernel runs through ca_op_guarded */
|
|
1943
|
+
ca_mask_overlay_attached(ca1, 3, ca1, ca2, ca3);
|
|
1944
|
+
|
|
1945
|
+
attached2 = attached3 = 0;
|
|
1946
|
+
if ( in_place2 ) {
|
|
1947
|
+
ca_attach(ca2); /* window: an alias attach, cannot raise */
|
|
1948
|
+
p2 = (char *) ca2->ptr;
|
|
1949
|
+
attached2 = 1;
|
|
1950
|
+
}
|
|
1951
|
+
if ( in_place3 ) {
|
|
1952
|
+
ca_attach(ca3); /* window: as above */
|
|
1953
|
+
p3 = (char *) ca3->ptr;
|
|
1954
|
+
attached3 = 1;
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
k.f = func[ca1->data_type];
|
|
1958
|
+
k.n = ca1->elements;
|
|
1959
|
+
k.m = ( ca1->mask ) ? (boolean8_t *) ca1->mask->ptr : NULL;
|
|
1960
|
+
k.p1 = ca1->ptr; k.i1 = 1;
|
|
1961
|
+
k.p2 = p2; k.i2 = s2;
|
|
1962
|
+
k.p3 = p3; k.i3 = s3;
|
|
1963
|
+
k.p4 = ca1->ptr; k.i4 = 1;
|
|
1964
|
+
if ( ca_triop_may_raise(func, ca1->data_type) ) {
|
|
1965
|
+
held[0] = ca1;
|
|
1966
|
+
held[1] = attached2 ? ca2 : NULL;
|
|
1967
|
+
held[2] = attached3 ? ca3 : NULL;
|
|
1968
|
+
ca_op_guarded(ca_triop_call, &k, 3, held, "wrr");
|
|
1969
|
+
}
|
|
1970
|
+
else {
|
|
1971
|
+
ca_triop_call((VALUE) &k);
|
|
1972
|
+
}
|
|
1438
1973
|
|
|
1439
1974
|
EAGER_RELEASE_INPUT(ca3, h3, attached3);
|
|
1440
1975
|
EAGER_RELEASE_INPUT(ca2, h2, attached2);
|
|
1441
1976
|
}
|
|
1442
1977
|
else {
|
|
1443
1978
|
/* CHUNKED PATH: both ca2 and ca3 non-alias arrays. ca1 (= self =
|
|
1444
|
-
output) is
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1979
|
+
output) is attached; its ptr is contig (= ca_attach materialise +
|
|
1980
|
+
alias for entity, or full materialise for view). Write to
|
|
1981
|
+
ca1->ptr + off*bytes in chunks; sync at end. The run gathers
|
|
1982
|
+
per chunk, and any gather can raise, so the run is guarded
|
|
1983
|
+
whatever the kernel. */
|
|
1984
|
+
ca_attach(ca1); /* window: the overlay detaches it if it raises; the
|
|
1985
|
+
kernel runs through ca_op_guarded */
|
|
1986
|
+
ca_mask_overlay_attached(ca1, 3, ca1, ca2, ca3);
|
|
1453
1987
|
{
|
|
1454
|
-
|
|
1455
|
-
ca_size_t
|
|
1456
|
-
ca_size_t
|
|
1457
|
-
ca_size_t maxb = ca1->bytes;
|
|
1458
|
-
if ( ca2->bytes > maxb ) maxb = ca2->bytes;
|
|
1459
|
-
if ( ca3->bytes > maxb ) maxb = ca3->bytes;
|
|
1460
|
-
if ( inn2 > inner ) inner = inn2;
|
|
1461
|
-
if ( inn3 > inner ) inner = inn3;
|
|
1462
|
-
chunk_n = ca_chunk_compute_n(n_total, inner, maxb);
|
|
1463
|
-
}
|
|
1988
|
+
int8_t dt = ca1->data_type;
|
|
1989
|
+
ca_size_t chunk_n;
|
|
1990
|
+
ca_size_t n_total = ca1->elements;
|
|
1464
1991
|
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
ca1->mask ? ((boolean8_t *) ca1->mask->ptr) + off
|
|
1477
|
-
: NULL,
|
|
1478
|
-
(char *) ca1->ptr + off * ca1->bytes, 1,
|
|
1479
|
-
(char *) s2_arena, s2,
|
|
1480
|
-
(char *) s3_arena, s3,
|
|
1481
|
-
(char *) ca1->ptr + off * ca1->bytes, 1);
|
|
1482
|
-
}
|
|
1992
|
+
{
|
|
1993
|
+
ca_size_t inner = ca_chunk_inner_size(ca1);
|
|
1994
|
+
ca_size_t inn2 = ca_chunk_inner_size(ca2);
|
|
1995
|
+
ca_size_t inn3 = ca_chunk_inner_size(ca3);
|
|
1996
|
+
ca_size_t maxb = ca1->bytes;
|
|
1997
|
+
if ( ca2->bytes > maxb ) maxb = ca2->bytes;
|
|
1998
|
+
if ( ca3->bytes > maxb ) maxb = ca3->bytes;
|
|
1999
|
+
if ( inn2 > inner ) inner = inn2;
|
|
2000
|
+
if ( inn3 > inner ) inner = inn3;
|
|
2001
|
+
chunk_n = ca_chunk_compute_n(n_total, inner, maxb);
|
|
2002
|
+
}
|
|
1483
2003
|
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
2004
|
+
{
|
|
2005
|
+
ca_triop_chunk_t d;
|
|
2006
|
+
ca_chunked_run_t run;
|
|
2007
|
+
d.func = func; d.dt = dt; d.out = ca1; d.in_place = 1;
|
|
2008
|
+
ca_chunked_run_init(&run, n_total, chunk_n);
|
|
2009
|
+
ca_chunked_run_add(&run, ca2, s2);
|
|
2010
|
+
ca_chunked_run_add(&run, ca3, s3);
|
|
2011
|
+
run.kernel = ca_triop_chunk_kernel;
|
|
2012
|
+
run.data = &d;
|
|
2013
|
+
ca_op_guarded(ca_triop_bang_chunked, &run, 1, &ca1, "w");
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
1487
2016
|
}
|
|
1488
2017
|
}
|
|
1489
2018
|
|
|
1490
|
-
|
|
1491
|
-
ca_detach(ca1);
|
|
2019
|
+
ca_sync_detach(ca1);
|
|
1492
2020
|
|
|
1493
2021
|
return self;
|
|
1494
2022
|
}
|
|
@@ -1502,6 +2030,9 @@ rb_ca_call_moncmp (VALUE self, ca_moncmp_func_t func[])
|
|
|
1502
2030
|
CArray *ca1, *ca2; /* ca2 = ca1.op */
|
|
1503
2031
|
|
|
1504
2032
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
|
|
2033
|
+
if ( func[ca1->data_type] == ca_moncmp_not_implement ) {
|
|
2034
|
+
ca_op_refuse_unimplemented(self, ca1, rb_eCADataTypeError);
|
|
2035
|
+
}
|
|
1505
2036
|
|
|
1506
2037
|
if ( ca_is_scalar(ca1) ) {
|
|
1507
2038
|
out = rb_cscalar_new(CA_BOOLEAN, 0, NULL);
|
|
@@ -1528,11 +2059,20 @@ rb_ca_call_moncmp (VALUE self, ca_moncmp_func_t func[])
|
|
|
1528
2059
|
int attached1;
|
|
1529
2060
|
(void) h1;
|
|
1530
2061
|
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
2062
|
+
ca_moncmp_call_t k;
|
|
2063
|
+
EAGER_GATHER_INPUT(ca1, p1, h1);
|
|
2064
|
+
EAGER_ATTACH_INPUT(ca1, p1, attached1);
|
|
2065
|
+
k.f = func[ca1->data_type];
|
|
2066
|
+
k.n = ca1->elements;
|
|
2067
|
+
k.m = ( ca2->mask ) ? (boolean8_t *) ca2->mask->ptr : NULL;
|
|
2068
|
+
k.p1 = p1; k.i1 = 1;
|
|
2069
|
+
k.p2 = (boolean8_t *) ca2->ptr; k.i2 = 1;
|
|
2070
|
+
if ( attached1 && ca_moncmp_may_raise(func, ca1->data_type) ) {
|
|
2071
|
+
ca_op_guarded(ca_moncmp_call, &k, 1, &ca1, "r");
|
|
2072
|
+
}
|
|
2073
|
+
else {
|
|
2074
|
+
ca_moncmp_call((VALUE) &k);
|
|
2075
|
+
}
|
|
1536
2076
|
EAGER_RELEASE_INPUT(ca1, h1, attached1);
|
|
1537
2077
|
}
|
|
1538
2078
|
|
|
@@ -1545,6 +2085,180 @@ rb_ca_call_moncmp (VALUE self, ca_moncmp_func_t func[])
|
|
|
1545
2085
|
UNDEF-comparison identity checks below cast to ca_bincmp_func_t
|
|
1546
2086
|
explicitly. */
|
|
1547
2087
|
|
|
2088
|
+
typedef struct {
|
|
2089
|
+
ca_bincmp_func_t *func;
|
|
2090
|
+
int8_t dt;
|
|
2091
|
+
CArray *out;
|
|
2092
|
+
ca_size_t step_out;
|
|
2093
|
+
double tol;
|
|
2094
|
+
} ca_bincmp_chunk_t;
|
|
2095
|
+
|
|
2096
|
+
static void
|
|
2097
|
+
ca_bincmp_chunk_kernel (ca_chunked_run_t *run, ca_size_t off, ca_size_t n,
|
|
2098
|
+
char **p)
|
|
2099
|
+
{
|
|
2100
|
+
ca_bincmp_chunk_t *d = (ca_bincmp_chunk_t *) run->data;
|
|
2101
|
+
ca_size_t o = d->step_out ? off : 0;
|
|
2102
|
+
d->func[d->dt](n,
|
|
2103
|
+
d->out->mask ? ((boolean8_t *) d->out->mask->ptr) + o : NULL,
|
|
2104
|
+
p[0], run->ca[0]->bytes, run->step[0],
|
|
2105
|
+
p[1], run->ca[1]->bytes, run->step[1],
|
|
2106
|
+
(char *) d->out->ptr + o * d->out->bytes, d->out->bytes,
|
|
2107
|
+
d->step_out,
|
|
2108
|
+
d->tol);
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
/* The six comparisons answer by value between integers. Promotion takes
|
|
2112
|
+
uint64 against int64 (and uint8 against int8, ...) to the unsigned type,
|
|
2113
|
+
where -7 is 2**64 - 7 and 7 < -7 would be true; and an Integer outside an
|
|
2114
|
+
array's type has no cell of that type to stand in for it. */
|
|
2115
|
+
|
|
2116
|
+
static int
|
|
2117
|
+
ca_is_unsigned_integer_dt (int8_t dt)
|
|
2118
|
+
{
|
|
2119
|
+
return dt == CA_UINT8 || dt == CA_UINT16 || dt == CA_UINT32 ||
|
|
2120
|
+
dt == CA_UINT64;
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
static int
|
|
2124
|
+
ca_is_signed_integer_dt (int8_t dt)
|
|
2125
|
+
{
|
|
2126
|
+
return dt == CA_INT8 || dt == CA_INT16 || dt == CA_INT32 ||
|
|
2127
|
+
dt == CA_INT64;
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
/* The answer of `left op right` when every right is greater than every
|
|
2131
|
+
left (right_greater = 1), or every left greater than every right. */
|
|
2132
|
+
static int
|
|
2133
|
+
ca_bincmp_ordered_answer (int op_id, int right_greater)
|
|
2134
|
+
{
|
|
2135
|
+
switch ( op_id ) {
|
|
2136
|
+
case CA_BINCMP_LT: case CA_BINCMP_LE: return right_greater;
|
|
2137
|
+
case CA_BINCMP_GT: case CA_BINCMP_GE: return ! right_greater;
|
|
2138
|
+
case CA_BINCMP_EQ: return 0;
|
|
2139
|
+
default: return 1; /* CA_BINCMP_NE */
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
|
|
2143
|
+
/* The answer at a cell whose signed operand is negative: the unsigned
|
|
2144
|
+
operand is then the greater. signed_side is 1 (left) or 2 (right). */
|
|
2145
|
+
int
|
|
2146
|
+
ca_bincmp_answer_when_negative (int op_id, int signed_side)
|
|
2147
|
+
{
|
|
2148
|
+
return ca_bincmp_ordered_answer(op_id, signed_side == 1);
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
static int
|
|
2152
|
+
ca_bincmp_plain_integer_array (VALUE v, int8_t *dt)
|
|
2153
|
+
{
|
|
2154
|
+
CArray *ca;
|
|
2155
|
+
if ( ! rb_obj_is_carray(v) ) return 0;
|
|
2156
|
+
TypedData_Get_Struct(v, CArray, &carray_data_type, ca);
|
|
2157
|
+
if ( ca_is_face(ca) ) return 0;
|
|
2158
|
+
*dt = ca->data_type;
|
|
2159
|
+
return ca_is_unsigned_integer_dt(*dt) || ca_is_signed_integer_dt(*dt);
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
/* Prepares the operands of comparison op_id (CA_BINCMP_*). Returns
|
|
2163
|
+
0 nothing to do,
|
|
2164
|
+
1 every cell answers true, 2 every cell answers false
|
|
2165
|
+
(*self and *other are then both the array, whose mask the answer
|
|
2166
|
+
keeps),
|
|
2167
|
+
3 the operands are integers of mixed signedness that promote to the
|
|
2168
|
+
unsigned type; *signed_side is 1 or 2 for the signed one. */
|
|
2169
|
+
int
|
|
2170
|
+
ca_bincmp_integer_prepare (volatile VALUE *self, volatile VALUE *other,
|
|
2171
|
+
int op_id, int *signed_side)
|
|
2172
|
+
{
|
|
2173
|
+
int8_t dt1, dt2;
|
|
2174
|
+
int side;
|
|
2175
|
+
|
|
2176
|
+
if ( op_id < CA_BINCMP_LT || op_id > CA_BINCMP_NE ) return 0;
|
|
2177
|
+
|
|
2178
|
+
if ( RB_INTEGER_TYPE_P(*other) &&
|
|
2179
|
+
ca_bincmp_plain_integer_array(*self, &dt1) ) {
|
|
2180
|
+
side = ca_integer_range_side(*other, dt1);
|
|
2181
|
+
if ( side == 0 ) return 0;
|
|
2182
|
+
*other = *self;
|
|
2183
|
+
return ca_bincmp_ordered_answer(op_id, side > 0) ? 1 : 2;
|
|
2184
|
+
}
|
|
2185
|
+
if ( RB_INTEGER_TYPE_P(*self) &&
|
|
2186
|
+
ca_bincmp_plain_integer_array(*other, &dt2) ) {
|
|
2187
|
+
side = ca_integer_range_side(*self, dt2);
|
|
2188
|
+
if ( side == 0 ) return 0;
|
|
2189
|
+
*self = *other;
|
|
2190
|
+
return ca_bincmp_ordered_answer(op_id, side < 0) ? 1 : 2;
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
if ( ! ca_bincmp_plain_integer_array(*self, &dt1) ||
|
|
2194
|
+
! ca_bincmp_plain_integer_array(*other, &dt2) ) {
|
|
2195
|
+
return 0;
|
|
2196
|
+
}
|
|
2197
|
+
if ( ca_is_unsigned_integer_dt(dt1) == ca_is_unsigned_integer_dt(dt2) ) {
|
|
2198
|
+
return 0;
|
|
2199
|
+
}
|
|
2200
|
+
if ( ! ca_is_unsigned_integer_dt(ca_promote_type(dt1, dt2)) ) {
|
|
2201
|
+
return 0;
|
|
2202
|
+
}
|
|
2203
|
+
*signed_side = ca_is_signed_integer_dt(dt1) ? 1 : 2;
|
|
2204
|
+
return 3;
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
static int
|
|
2208
|
+
ca_bincmp_op_id_of (ca_bincmp_func_t *func)
|
|
2209
|
+
{
|
|
2210
|
+
if ( func == ca_bincmp_lt ) return CA_BINCMP_LT;
|
|
2211
|
+
if ( func == ca_bincmp_gt ) return CA_BINCMP_GT;
|
|
2212
|
+
if ( func == ca_bincmp_le ) return CA_BINCMP_LE;
|
|
2213
|
+
if ( func == ca_bincmp_ge ) return CA_BINCMP_GE;
|
|
2214
|
+
if ( func == ca_bincmp_eq ) return CA_BINCMP_EQ;
|
|
2215
|
+
if ( func == ca_bincmp_ne ) return CA_BINCMP_NE;
|
|
2216
|
+
return -1;
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
/* Compares the unsigned operand as uint64 with the signed one taken to
|
|
2220
|
+
uint64 modulo 2**64, then answers the cells where the signed operand is
|
|
2221
|
+
negative. */
|
|
2222
|
+
static VALUE
|
|
2223
|
+
ca_bincmp_mixed_sign (VALUE self, VALUE other, ca_bincmp_func_t func[],
|
|
2224
|
+
int op_id, int signed_side)
|
|
2225
|
+
{
|
|
2226
|
+
volatile VALUE l, r, su, out;
|
|
2227
|
+
CArray *cs, *co;
|
|
2228
|
+
int64_t *sp;
|
|
2229
|
+
boolean8_t *op, *m;
|
|
2230
|
+
ca_size_t i, step;
|
|
2231
|
+
int answer = ca_bincmp_answer_when_negative(op_id, signed_side);
|
|
2232
|
+
|
|
2233
|
+
l = rb_funcall(self, rb_intern("to_type"), 1,
|
|
2234
|
+
INT2NUM(( signed_side == 1 ) ? CA_INT64 : CA_UINT64));
|
|
2235
|
+
r = rb_funcall(other, rb_intern("to_type"), 1,
|
|
2236
|
+
INT2NUM(( signed_side == 2 ) ? CA_INT64 : CA_UINT64));
|
|
2237
|
+
ca_broadcast_pair(&l, &r);
|
|
2238
|
+
|
|
2239
|
+
/* A fresh uint64 entity holding the signed operand modulo 2**64; read
|
|
2240
|
+
as int64 it still says which cells are negative. */
|
|
2241
|
+
su = rb_funcall(( signed_side == 1 ) ? l : r, rb_intern("to_type"), 1,
|
|
2242
|
+
INT2NUM(CA_UINT64));
|
|
2243
|
+
out = ( signed_side == 1 ) ? rb_ca_call_bincmp(su, r, func, 0.0)
|
|
2244
|
+
: rb_ca_call_bincmp(l, su, func, 0.0);
|
|
2245
|
+
|
|
2246
|
+
TypedData_Get_Struct(su, CArray, &carray_data_type, cs);
|
|
2247
|
+
TypedData_Get_Struct(out, CArray, &carray_data_type, co);
|
|
2248
|
+
sp = (int64_t *) cs->ptr;
|
|
2249
|
+
op = (boolean8_t *) co->ptr;
|
|
2250
|
+
m = co->mask ? (boolean8_t *) co->mask->ptr : NULL;
|
|
2251
|
+
step = ( cs->elements == 1 ) ? 0 : 1;
|
|
2252
|
+
for ( i = 0; i < co->elements; i++ ) {
|
|
2253
|
+
if ( ( ! m || ! m[i] ) && sp[i * step] < 0 ) {
|
|
2254
|
+
op[i] = (boolean8_t) answer;
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
RB_GC_GUARD(l);
|
|
2258
|
+
RB_GC_GUARD(r);
|
|
2259
|
+
return out;
|
|
2260
|
+
}
|
|
2261
|
+
|
|
1548
2262
|
VALUE
|
|
1549
2263
|
rb_ca_call_bincmp (volatile VALUE self, volatile VALUE other,
|
|
1550
2264
|
ca_bincmp_func_t func[],
|
|
@@ -1562,7 +2276,7 @@ rb_ca_call_bincmp (volatile VALUE self, volatile VALUE other,
|
|
|
1562
2276
|
return rb_ca_is_not_masked(self);
|
|
1563
2277
|
}
|
|
1564
2278
|
else {
|
|
1565
|
-
rb_raise(
|
|
2279
|
+
rb_raise(rb_eArgError, "array can not be compared with UNDEF");
|
|
1566
2280
|
}
|
|
1567
2281
|
}
|
|
1568
2282
|
|
|
@@ -1572,11 +2286,25 @@ rb_ca_call_bincmp (volatile VALUE self, volatile VALUE other,
|
|
|
1572
2286
|
for fixlen-storage Faces (memcmp is already correct there). */
|
|
1573
2287
|
ca_face_reconcile_comparison(&self, &other);
|
|
1574
2288
|
|
|
1575
|
-
|
|
2289
|
+
{
|
|
2290
|
+
int op_id = ca_bincmp_op_id_of(func);
|
|
2291
|
+
int signed_side = 0;
|
|
2292
|
+
switch ( ca_bincmp_integer_prepare(&self, &other, op_id, &signed_side) ) {
|
|
2293
|
+
case 1: return rb_ca_call_bincmp(self, self, ca_bincmp_eq, 0.0);
|
|
2294
|
+
case 2: return rb_ca_call_bincmp(self, self, ca_bincmp_ne, 0.0);
|
|
2295
|
+
case 3: return ca_bincmp_mixed_sign(self, other, func, op_id, signed_side);
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2299
|
+
/* do implicit casting */
|
|
1576
2300
|
rb_ca_cast_self_or_other(&self, &other);
|
|
1577
2301
|
|
|
1578
2302
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
|
|
1579
2303
|
TypedData_Get_Struct(other, CArray, &carray_data_type, ca2);
|
|
2304
|
+
if ( func[ca1->data_type] == ca_bincmp_not_implement ) {
|
|
2305
|
+
ca_op_refuse_unimplemented(ca_is_face(ca1) ? self : other, ca1,
|
|
2306
|
+
rb_eTypeError);
|
|
2307
|
+
}
|
|
1580
2308
|
|
|
1581
2309
|
/* Same fast/slow shape as rb_ca_call_binop, but the output data_type is
|
|
1582
2310
|
fixed boolean (rb_ca_call_binop's output data_type matches its input). */
|
|
@@ -1651,15 +2379,29 @@ rb_ca_call_bincmp (volatile VALUE self, volatile VALUE other,
|
|
|
1651
2379
|
int attached1, attached2;
|
|
1652
2380
|
(void) h1; (void) h2;
|
|
1653
2381
|
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
2382
|
+
ca_bincmp_call_t k;
|
|
2383
|
+
CArray *held[2];
|
|
2384
|
+
EAGER_GATHER_INPUT(ca1, p1, h1);
|
|
2385
|
+
EAGER_GATHER_INPUT(ca2, p2, h2);
|
|
2386
|
+
EAGER_ATTACH_INPUT(ca1, p1, attached1);
|
|
2387
|
+
EAGER_ATTACH_INPUT(ca2, p2, attached2);
|
|
2388
|
+
|
|
2389
|
+
k.f = func[ca1->data_type];
|
|
2390
|
+
k.n = n_kernel;
|
|
2391
|
+
k.m = ( ca3->mask ) ? (boolean8_t *) ca3->mask->ptr : NULL;
|
|
2392
|
+
k.p1 = p1; k.b1 = ca1->bytes; k.i1 = i1;
|
|
2393
|
+
k.p2 = p2; k.b2 = ca2->bytes; k.i2 = i2;
|
|
2394
|
+
k.p3 = ca3->ptr; k.b3 = ca3->bytes; k.i3 = i3;
|
|
2395
|
+
k.tol = tol;
|
|
2396
|
+
if ( ( attached1 || attached2 ) &&
|
|
2397
|
+
ca_bincmp_may_raise(func, ca1->data_type) ) {
|
|
2398
|
+
held[0] = attached1 ? ca1 : NULL;
|
|
2399
|
+
held[1] = attached2 ? ca2 : NULL;
|
|
2400
|
+
ca_op_guarded(ca_bincmp_call, &k, 2, held, "rr");
|
|
2401
|
+
}
|
|
2402
|
+
else {
|
|
2403
|
+
ca_bincmp_call((VALUE) &k);
|
|
2404
|
+
}
|
|
1663
2405
|
|
|
1664
2406
|
EAGER_RELEASE_INPUT(ca2, h2, attached2);
|
|
1665
2407
|
EAGER_RELEASE_INPUT(ca1, h1, attached1);
|
|
@@ -1667,10 +2409,9 @@ rb_ca_call_bincmp (volatile VALUE self, volatile VALUE other,
|
|
|
1667
2409
|
else {
|
|
1668
2410
|
/* CHUNKED PATH (both operands non-alias array): per-chunk gather
|
|
1669
2411
|
into arena scratch. Mirrors the binop chunked branch. */
|
|
1670
|
-
void *s1_arena = NULL, *s2_arena = NULL;
|
|
1671
2412
|
ca_size_t b1 = ca1->bytes, b2 = ca2->bytes, b3 = ca3->bytes;
|
|
1672
2413
|
int8_t dt = ca1->data_type;
|
|
1673
|
-
ca_size_t chunk_n
|
|
2414
|
+
ca_size_t chunk_n;
|
|
1674
2415
|
|
|
1675
2416
|
{
|
|
1676
2417
|
ca_size_t inner1 = ca_chunk_inner_size(ca1);
|
|
@@ -1681,28 +2422,17 @@ rb_ca_call_bincmp (volatile VALUE self, volatile VALUE other,
|
|
|
1681
2422
|
chunk_n = ca_chunk_compute_n(n_kernel, inner, maxb);
|
|
1682
2423
|
}
|
|
1683
2424
|
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
func[dt](n_done,
|
|
1695
|
-
ca3->mask ? ((boolean8_t *) ca3->mask->ptr) + off
|
|
1696
|
-
: NULL,
|
|
1697
|
-
(char *) s1_arena, b1, i1,
|
|
1698
|
-
(char *) s2_arena, b2, i2,
|
|
1699
|
-
(char *) ca3->ptr + off * b3, b3, i3,
|
|
1700
|
-
tol);
|
|
2425
|
+
{
|
|
2426
|
+
ca_bincmp_chunk_t d;
|
|
2427
|
+
ca_chunked_run_t run;
|
|
2428
|
+
d.func = func; d.dt = dt; d.out = ca3; d.step_out = i3; d.tol = tol;
|
|
2429
|
+
ca_chunked_run_init(&run, n_kernel, chunk_n);
|
|
2430
|
+
ca_chunked_run_add(&run, ca1, i1);
|
|
2431
|
+
ca_chunked_run_add(&run, ca2, i2);
|
|
2432
|
+
run.kernel = ca_bincmp_chunk_kernel;
|
|
2433
|
+
run.data = &d;
|
|
2434
|
+
ca_chunked_run(&run);
|
|
1701
2435
|
}
|
|
1702
|
-
|
|
1703
|
-
ca_lazy_arena_release(s2_arena);
|
|
1704
|
-
ca_lazy_arena_release(s1_arena);
|
|
1705
|
-
ca_lazy_arena_exit();
|
|
1706
2436
|
}
|
|
1707
2437
|
}
|
|
1708
2438
|
}
|
|
@@ -1774,7 +2504,7 @@ ca_math_call (VALUE mod, VALUE arg, ID id)
|
|
|
1774
2504
|
return rb_funcall(rb_mMath, id, 1, arg);
|
|
1775
2505
|
}
|
|
1776
2506
|
else {
|
|
1777
|
-
rb_raise(
|
|
2507
|
+
rb_raise(rb_eTypeError, "unknown method for Math");
|
|
1778
2508
|
}
|
|
1779
2509
|
}
|
|
1780
2510
|
#endif
|
|
@@ -1784,7 +2514,7 @@ ca_math_call (VALUE mod, VALUE arg, ID id)
|
|
|
1784
2514
|
return rb_funcall(rb_mMath, id, 1, arg);
|
|
1785
2515
|
}
|
|
1786
2516
|
else {
|
|
1787
|
-
rb_raise(
|
|
2517
|
+
rb_raise(rb_eTypeError, "unknown method for Math");
|
|
1788
2518
|
}
|
|
1789
2519
|
}
|
|
1790
2520
|
}
|
|
@@ -1804,7 +2534,7 @@ rb_ca_coerce (VALUE self, VALUE other)
|
|
|
1804
2534
|
return rb_ca_coerce(self, rb_funcall(other,rb_intern("to_ca"),0));
|
|
1805
2535
|
}
|
|
1806
2536
|
else {
|
|
1807
|
-
/* do implicit casting
|
|
2537
|
+
/* do implicit casting */
|
|
1808
2538
|
rb_ca_cast_self_or_other(&self, &other);
|
|
1809
2539
|
return rb_assoc_new(other, self);
|
|
1810
2540
|
}
|