carray 3.0.6 → 3.0.7
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 +110 -0
- data/README.md +24 -3
- data/carray.gemspec +1 -1
- data/ext/ca_axis_group.c +6 -6
- data/ext/ca_binop_dispatch.c +8 -0
- data/ext/ca_binop_dispatch.h +1 -0
- data/ext/ca_categorical_iterator.c +7 -7
- data/ext/ca_kernel_iterator.h +6 -4
- data/ext/ca_obj_array.c +4 -4
- data/ext/ca_obj_bincmp.c +19 -3
- data/ext/ca_obj_binop.c +36 -11
- data/ext/ca_obj_bitarray.c +2 -4
- data/ext/ca_obj_bitfield.c +86 -4
- data/ext/ca_obj_byte_swap.c +2 -4
- data/ext/ca_obj_const_string.c +422 -257
- data/ext/ca_obj_face.c +71 -33
- data/ext/ca_obj_fake.c +112 -13
- data/ext/ca_obj_farray.c +7 -1
- data/ext/ca_obj_fixlen_string.c +51 -17
- data/ext/ca_obj_grid.c +12 -7
- data/ext/ca_obj_meld.c +5 -5
- data/ext/ca_obj_monop.c +65 -39
- data/ext/ca_obj_object.c +9 -9
- data/ext/ca_obj_reduce.c +2 -4
- data/ext/ca_obj_refer.c +104 -10
- data/ext/ca_obj_repeat.c +1 -1
- data/ext/ca_obj_roll.c +2 -4
- data/ext/ca_obj_select.c +8 -6
- data/ext/ca_obj_select_axis.c +8 -6
- data/ext/ca_obj_stack.c +5 -5
- data/ext/ca_obj_stride.c +46 -8
- data/ext/ca_obj_string.c +2 -2
- data/ext/ca_obj_tile.c +2 -4
- data/ext/ca_obj_triop.c +15 -12
- data/ext/ca_obj_window.c +22 -15
- data/ext/ca_op_ipower.c +63 -15
- data/ext/ca_op_powi.h +6 -29
- data/ext/ca_sort_kernels.h +3 -0
- data/ext/ca_sweep_engine.c +90 -23
- data/ext/ca_sweep_engine.h +5 -6
- data/ext/caframe_csv_reader.c +1 -1
- data/ext/carray.h +3 -1
- data/ext/carray_access.c +98 -39
- data/ext/carray_address_basis.c +98 -24
- data/ext/carray_broadcast.c +2 -2
- data/ext/carray_call_cfunc.c +5 -8
- data/ext/carray_cast.c +311 -37
- data/ext/carray_cast_func.rb +37 -5
- data/ext/carray_conversion.c +56 -2
- data/ext/carray_copy.c +24 -0
- data/ext/carray_core.c +69 -6
- data/ext/carray_count.c +28 -9
- data/ext/carray_element.c +9 -3
- data/ext/carray_factorize.c +11 -11
- data/ext/carray_histogram.c +2 -2
- data/ext/carray_index_classifier.c +7 -5
- data/ext/carray_internal.h +94 -0
- data/ext/carray_kernels_binop.c +55 -55
- data/ext/carray_kernels_init.c +14 -14
- data/ext/carray_kernels_monop.c +90 -140
- data/ext/carray_lazy.c +16 -1
- data/ext/carray_loop.c +9 -2
- data/ext/carray_mask.c +118 -11
- data/ext/carray_median_percentile.c +13 -8
- data/ext/carray_memory_view.c +41 -22
- data/ext/carray_operator.c +72 -11
- data/ext/carray_order.c +52 -9
- data/ext/carray_partition.c +1 -1
- data/ext/carray_random.c +1 -1
- data/ext/carray_round.c +24 -1
- data/ext/carray_scatter.c +4 -0
- data/ext/carray_segment.c +1 -1
- data/ext/carray_slab.c +13 -4
- data/ext/carray_sort.c +6 -3
- data/ext/carray_sort_kernel.c +24 -0
- data/ext/carray_struct.c +21 -6
- data/ext/carray_test.c +85 -39
- data/ext/carray_time_text.c +6 -0
- data/ext/carray_utils.c +124 -54
- data/ext/mk_call_cfunc.rb +6 -11
- data/ext/mkkernel.rb +37 -27
- data/ext/version.h +4 -4
- data/lib/carray/axis_group.rb +9 -6
- data/lib/carray/basics.rb +26 -8
- data/lib/carray/bincount_nd.rb +8 -2
- data/lib/carray/block_iterator.rb +48 -21
- data/lib/carray/categorical.rb +21 -8
- data/lib/carray/categorical_iterator.rb +2 -1
- data/lib/carray/conditional.rb +52 -4
- data/lib/carray/const_string.rb +74 -0
- data/lib/carray/construct.rb +2 -2
- data/lib/carray/core_extensions.rb +19 -68
- data/lib/carray/data_type_extension.rb +1 -0
- data/lib/carray/fixlen_string.rb +83 -1
- data/lib/carray/frame/column_verbs.rb +61 -6
- data/lib/carray/frame/concat.rb +50 -6
- data/lib/carray/frame/csv_parser.rb +13 -4
- data/lib/carray/frame/frame.rb +38 -6
- data/lib/carray/frame/group.rb +67 -40
- data/lib/carray/frame/io.rb +10 -5
- data/lib/carray/frame/join.rb +20 -6
- data/lib/carray/frame/records.rb +6 -4
- data/lib/carray/frame/reshape.rb +25 -5
- data/lib/carray/frame/verbs.rb +78 -25
- data/lib/carray/frame/windows.rb +30 -6
- data/lib/carray/fuse_source.rb +4 -2
- data/lib/carray/iterator.rb +8 -0
- data/lib/carray/key_windows.rb +10 -4
- data/lib/carray/lazy.rb +12 -17
- data/lib/carray/methods/bin.rb +3 -0
- data/lib/carray/methods/composition.rb +1 -1
- data/lib/carray/methods/factorize.rb +2 -0
- data/lib/carray/methods/gather_nd.rb +4 -1
- data/lib/carray/methods/is_in.rb +23 -3
- data/lib/carray/methods/locate_addr.rb +63 -12
- data/lib/carray/methods/repeat.rb +11 -1
- data/lib/carray/record_order.rb +35 -6
- data/lib/carray/runtime.rb +10 -0
- data/lib/carray/segment_iterator.rb +19 -16
- data/lib/carray/serialize.rb +22 -0
- data/lib/carray/slab_iterator.rb +1 -1
- data/lib/carray/stack.rb +3 -3
- data/lib/carray/string.rb +6 -6
- data/lib/carray/string_operation_extension.rb +25 -4
- data/lib/carray/struct_builder.rb +4 -1
- data/lib/carray/time.rb +190 -46
- data/lib/carray/window_iterator.rb +48 -12
- data/yard-stubs/ca_obj_bitfield.rb +4 -1
- data/yard-stubs/ca_obj_refer.rb +3 -0
- data/yard-stubs/ca_obj_window.rb +2 -1
- data/yard-stubs/carray_access.rb +4 -2
- data/yard-stubs/carray_cast.rb +6 -0
- data/yard-stubs/carray_count.rb +6 -3
- data/yard-stubs/carray_math.rb +5 -0
- data/yard-stubs/carray_order.rb +6 -0
- data/yard-stubs/carray_scatter.rb +2 -1
- data/yard-stubs/carray_test.rb +18 -5
- metadata +2 -2
data/ext/ca_obj_meld.c
CHANGED
|
@@ -883,8 +883,8 @@ ca_meld_func_allocate (void *ap)
|
|
|
883
883
|
{
|
|
884
884
|
CAMeld *ca = (CAMeld *) ap;
|
|
885
885
|
ca_meld_mask_refresh(ca);
|
|
886
|
-
|
|
887
|
-
|
|
886
|
+
ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, ca->n_parents,
|
|
887
|
+
ca->parents);
|
|
888
888
|
}
|
|
889
889
|
|
|
890
890
|
static VALUE
|
|
@@ -1129,7 +1129,7 @@ rb_ca_meld_new (VALUE parents_ary, int8_t meld_axis)
|
|
|
1129
1129
|
}
|
|
1130
1130
|
parents = ALLOCV_N(CArray *, holder, n);
|
|
1131
1131
|
for ( i = 0; i < n; i++ ) {
|
|
1132
|
-
VALUE p = rb_ary_entry(parents_ary, i);
|
|
1132
|
+
volatile VALUE p = rb_ary_entry(parents_ary, i);
|
|
1133
1133
|
rb_check_carray_object(p);
|
|
1134
1134
|
TypedData_Get_Struct(p, CArray, &carray_data_type, parents[i]);
|
|
1135
1135
|
ca_multi_parent_check_part(parents[i], "meld");
|
|
@@ -1185,7 +1185,7 @@ rb_ca_meld_initialize (int argc, VALUE *argv, VALUE self)
|
|
|
1185
1185
|
}
|
|
1186
1186
|
if ( ! NIL_P(axis_val) ) {
|
|
1187
1187
|
CArray *ref;
|
|
1188
|
-
VALUE first = rb_ary_entry(list, 0);
|
|
1188
|
+
volatile VALUE first = rb_ary_entry(list, 0);
|
|
1189
1189
|
rb_check_carray_object(first);
|
|
1190
1190
|
TypedData_Get_Struct(first, CArray, &carray_data_type, ref);
|
|
1191
1191
|
meld_axis = (int8_t) ca_normalize_axis_arg(
|
|
@@ -1195,7 +1195,7 @@ rb_ca_meld_initialize (int argc, VALUE *argv, VALUE self)
|
|
|
1195
1195
|
ca_check_uninitialized(ca);
|
|
1196
1196
|
parents = ALLOCV_N(CArray *, holder, n);
|
|
1197
1197
|
for ( i = 0; i < n; i++ ) {
|
|
1198
|
-
VALUE p = rb_ary_entry(list, i);
|
|
1198
|
+
volatile VALUE p = rb_ary_entry(list, i);
|
|
1199
1199
|
rb_check_carray_object(p);
|
|
1200
1200
|
TypedData_Get_Struct(p, CArray, &carray_data_type, parents[i]);
|
|
1201
1201
|
ca_multi_parent_check_part(parents[i], "meld");
|
data/ext/ca_obj_monop.c
CHANGED
|
@@ -64,6 +64,7 @@ extern int8_t CA_OBJ_LAZY_MARKER;
|
|
|
64
64
|
extern int8_t CA_OBJ_BINOP;
|
|
65
65
|
extern int8_t CA_OBJ_BINCMP;
|
|
66
66
|
extern int8_t CA_OBJ_MONCMP;
|
|
67
|
+
extern int8_t CA_OBJ_TRIOP;
|
|
67
68
|
|
|
68
69
|
typedef struct CAMonOp {
|
|
69
70
|
int16_t obj_type;
|
|
@@ -169,6 +170,7 @@ ca_monop_view_is_single_cast (CArray *view)
|
|
|
169
170
|
(so `1/zero` zero-check works). */
|
|
170
171
|
return ( p->obj_type != CA_OBJ_MONOP
|
|
171
172
|
&& p->obj_type != CA_OBJ_BINOP
|
|
173
|
+
&& p->obj_type != CA_OBJ_TRIOP
|
|
172
174
|
&& p->obj_type != CA_OBJ_BINCMP
|
|
173
175
|
&& p->obj_type != CA_OBJ_MONCMP
|
|
174
176
|
&& p->obj_type != CA_OBJ_LAZY_MARKER );
|
|
@@ -338,14 +340,17 @@ ca_size_t ca_monop_materialise_call_count = 0;
|
|
|
338
340
|
#define CA_MAX_LAZY_DEPTH 256
|
|
339
341
|
#endif
|
|
340
342
|
|
|
341
|
-
/* Whether a cast from `from` to `to`
|
|
342
|
-
|
|
343
|
+
/* Whether a cast from `from` to `to` can raise on a cell, so that it has to
|
|
344
|
+
skip the masked ones, whose values are not defined: a float or complex
|
|
345
|
+
value has no integer for NaN, a boolean takes only 0 and 1, and an object
|
|
346
|
+
cell may hold anything. */
|
|
343
347
|
static int
|
|
344
|
-
|
|
348
|
+
ca_monop_cast_can_raise (int8_t from, int8_t to)
|
|
345
349
|
{
|
|
346
350
|
int from_float = ( from == CA_FLOAT32 || from == CA_FLOAT64 ||
|
|
347
351
|
from == CA_CMPLX64 || from == CA_CMPLX128 );
|
|
348
|
-
return
|
|
352
|
+
return from == CA_OBJECT || to == CA_BOOLEAN
|
|
353
|
+
|| ( from_float && to >= CA_INT8 && to <= CA_UINT64 );
|
|
349
354
|
}
|
|
350
355
|
|
|
351
356
|
/* Apply a single op in-place on `data` (src == dst, unit element stride).
|
|
@@ -384,7 +389,8 @@ ca_data_type_stub (int8_t data_type)
|
|
|
384
389
|
return stub;
|
|
385
390
|
}
|
|
386
391
|
|
|
387
|
-
/* Pull leaf into the output buffer.
|
|
392
|
+
/* Pull leaf into the output buffer. `mask` marks the cells a cast must
|
|
393
|
+
skip, or is NULL. Three paths:
|
|
388
394
|
|
|
389
395
|
1. No size change (leaf.bytes == out.bytes): pull directly into
|
|
390
396
|
`data` using the caller's `strides` (= output byte layout).
|
|
@@ -411,7 +417,8 @@ ca_data_type_stub (int8_t data_type)
|
|
|
411
417
|
static int8_t
|
|
412
418
|
pull_leaf_with_optional_cast (CArray *leaf, uint16_t innermost_op,
|
|
413
419
|
int has_cast_at_leaf, int8_t out_bytes,
|
|
414
|
-
const ca_lazy_req_t *req, void *data
|
|
420
|
+
const ca_lazy_req_t *req, void *data,
|
|
421
|
+
boolean8_t *mask)
|
|
415
422
|
{
|
|
416
423
|
ca_size_t leaf_bytes = leaf->bytes;
|
|
417
424
|
ca_size_t slab_n = req->n;
|
|
@@ -437,7 +444,9 @@ pull_leaf_with_optional_cast (CArray *leaf, uint16_t innermost_op,
|
|
|
437
444
|
int8_t target_dt = (int8_t)(innermost_op - CA_MONOP_CAST_BASE);
|
|
438
445
|
CArray src_stub = ca_data_type_stub(leaf->data_type);
|
|
439
446
|
CArray dst_stub = ca_data_type_stub(target_dt);
|
|
440
|
-
|
|
447
|
+
ca_cast_block_skip_masked(slab_n, &src_stub, scratch, &dst_stub, data,
|
|
448
|
+
ca_monop_cast_can_raise(leaf->data_type, target_dt)
|
|
449
|
+
? mask : NULL);
|
|
441
450
|
result_dt = target_dt;
|
|
442
451
|
} else {
|
|
443
452
|
/* Path 3: memcpy scratch → start of data. Mid-chain cast will
|
|
@@ -465,7 +474,7 @@ ca_monop_eval_cells (void *node, const ca_lazy_req_t *req, void *data, int dir)
|
|
|
465
474
|
int has_cast_at_leaf = 0;
|
|
466
475
|
int chain_apply_count;
|
|
467
476
|
boolean8_t *slab_mask = NULL;
|
|
468
|
-
int
|
|
477
|
+
int may_raise = 0;
|
|
469
478
|
|
|
470
479
|
/* Writable-view op_ids (cast / byte_swap) use CAFake-style xfer_stride
|
|
471
480
|
for both GET and PUT. This avoids the chain-materialise inline path
|
|
@@ -483,11 +492,10 @@ ca_monop_eval_cells (void *node, const ca_lazy_req_t *req, void *data, int dir)
|
|
|
483
492
|
v = ALLOCV_N(char, holder, n * parent_bytes);
|
|
484
493
|
if ( dir == CA_XFER_GET ) {
|
|
485
494
|
ca_lazy_req_pull(mo->parent, req, v, CA_XFER_GET);
|
|
486
|
-
if ( is_cast &&
|
|
495
|
+
if ( is_cast && ca_monop_cast_can_raise(mo->parent->data_type,
|
|
487
496
|
mo->data_type) ) {
|
|
488
|
-
/* The cast raises on a
|
|
489
|
-
|
|
490
|
-
The mask is copied off the arena first so a raise leaves no
|
|
497
|
+
/* The cast raises on a cell it cannot convert; a masked cell
|
|
498
|
+
(mask_invalid keeps a NaN under the mask) is skipped. The mask is copied off the arena first so a raise leaves no
|
|
491
499
|
arena slot held. */
|
|
492
500
|
CArray *parent = mo->parent;
|
|
493
501
|
boolean8_t *arena_mask = ca_lazy_req_mask(req, 1, &parent);
|
|
@@ -498,7 +506,7 @@ ca_monop_eval_cells (void *node, const ca_lazy_req_t *req, void *data, int dir)
|
|
|
498
506
|
memcpy(m, arena_mask, n);
|
|
499
507
|
ca_lazy_arena_release(arena_mask);
|
|
500
508
|
}
|
|
501
|
-
|
|
509
|
+
ca_cast_block_skip_masked(n, mo->parent, v, (CArray *) mo, data, m);
|
|
502
510
|
if ( m ) {
|
|
503
511
|
ALLOCV_END(mholder);
|
|
504
512
|
}
|
|
@@ -567,25 +575,39 @@ ca_monop_eval_cells (void *node, const ca_lazy_req_t *req, void *data, int dir)
|
|
|
567
575
|
widest type along it: the leaf, or a step that widens before a later
|
|
568
576
|
one narrows (cmplx128.lazy.abs pulls 16-byte cells and answers 8-byte
|
|
569
577
|
ones). When that is wider than the output, run in a scratch and copy
|
|
570
|
-
the answer into `data` at the end.
|
|
578
|
+
the answer into `data` at the end. The same walk finds whether a step
|
|
579
|
+
can raise on a cell, so that it has to skip the masked ones. */
|
|
571
580
|
void *out = data;
|
|
572
581
|
void *work = NULL;
|
|
573
582
|
{
|
|
574
583
|
ca_size_t max_bytes = leaf->bytes;
|
|
575
584
|
int8_t dt = leaf->data_type;
|
|
576
585
|
for ( i = chain_len - 1; i >= 0; i-- ) {
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
586
|
+
int8_t next = ca_monop_is_cast(op_chain[i])
|
|
587
|
+
? (int8_t)(op_chain[i] - CA_MONOP_CAST_BASE)
|
|
588
|
+
: ca_lazy_promote_monop(op_chain[i], dt);
|
|
589
|
+
if ( ca_monop_is_cast(op_chain[i])
|
|
590
|
+
? ca_monop_cast_can_raise(dt, next)
|
|
591
|
+
: ca_op_raises_on_cell(CA_OP_KIND_MONOP, op_chain[i], dt) ) {
|
|
592
|
+
may_raise = 1;
|
|
593
|
+
}
|
|
594
|
+
dt = next;
|
|
580
595
|
if ( ca_sizeof[dt] > max_bytes ) max_bytes = ca_sizeof[dt];
|
|
581
596
|
}
|
|
582
597
|
if ( max_bytes > mo->bytes ) {
|
|
583
|
-
|
|
598
|
+
/* Object cells stay in the buffer until a cast takes them to numbers;
|
|
599
|
+
a cast at the leaf takes them before they reach it. */
|
|
600
|
+
work = ( leaf->data_type == CA_OBJECT && ! has_cast_at_leaf )
|
|
584
601
|
? ca_lazy_arena_acquire_object((slab_n * max_bytes + sizeof(VALUE) - 1) / sizeof(VALUE))
|
|
585
602
|
: ca_lazy_arena_acquire(slab_n * max_bytes);
|
|
586
603
|
data = work;
|
|
587
604
|
}
|
|
588
605
|
}
|
|
606
|
+
/* raises: the walk above asks ca_op_raises_on_cell of each operation; a
|
|
607
|
+
cast, which is not one, has ca_monop_cast_can_raise */
|
|
608
|
+
if ( may_raise ) {
|
|
609
|
+
slab_mask = ca_lazy_req_mask(req, 1, &leaf);
|
|
610
|
+
}
|
|
589
611
|
|
|
590
612
|
/* === 2. pull leaf into output buffer (with optional cast) === */
|
|
591
613
|
cur_dt = pull_leaf_with_optional_cast(
|
|
@@ -593,7 +615,7 @@ ca_monop_eval_cells (void *node, const ca_lazy_req_t *req, void *data, int dir)
|
|
|
593
615
|
has_cast_at_leaf ? op_chain[chain_len - 1] : 0,
|
|
594
616
|
has_cast_at_leaf,
|
|
595
617
|
(int8_t) mo->bytes,
|
|
596
|
-
req, data
|
|
618
|
+
req, data, slab_mask
|
|
597
619
|
);
|
|
598
620
|
|
|
599
621
|
/* === 3. apply remaining chain in reverse (leaf side → outermost) ===
|
|
@@ -620,14 +642,15 @@ ca_monop_eval_cells (void *node, const ca_lazy_req_t *req, void *data, int dir)
|
|
|
620
642
|
: ca_lazy_arena_acquire(total_bytes);
|
|
621
643
|
ca_monop_scratch_acquire_count++;
|
|
622
644
|
memcpy(scratch, data, total_bytes);
|
|
623
|
-
|
|
645
|
+
if ( work ) {
|
|
646
|
+
ca_lazy_arena_untag_objects(work);
|
|
647
|
+
}
|
|
648
|
+
ca_cast_block_skip_masked(slab_n, &src_stub, scratch, &dst_stub, data,
|
|
649
|
+
ca_monop_cast_can_raise(cur_dt, target_dt)
|
|
650
|
+
? slab_mask : NULL);
|
|
624
651
|
ca_lazy_arena_release(scratch);
|
|
625
652
|
cur_dt = target_dt;
|
|
626
653
|
} else {
|
|
627
|
-
if ( ! mask_taken && ca_op_raises_on_cell(CA_OP_KIND_MONOP, op_id, cur_dt) ) {
|
|
628
|
-
slab_mask = ca_lazy_req_mask(req, 1, &leaf);
|
|
629
|
-
mask_taken = 1;
|
|
630
|
-
}
|
|
631
654
|
apply_monop_in_place(op_id, cur_dt, slab_n, data,
|
|
632
655
|
ca_op_raises_on_cell(CA_OP_KIND_MONOP, op_id, cur_dt) ? slab_mask : NULL);
|
|
633
656
|
cur_dt = ca_lazy_promote_monop(op_id, cur_dt);
|
|
@@ -645,16 +668,18 @@ ca_monop_eval_cells (void *node, const ca_lazy_req_t *req, void *data, int dir)
|
|
|
645
668
|
/* A chain is evaluated from its leaf without evaluating the nodes under
|
|
646
669
|
the top one, so those nodes are recorded with it: a cell that holds an
|
|
647
670
|
expression over one of them is caught when it comes back, before a
|
|
648
|
-
longer chain over the same cells is built.
|
|
671
|
+
longer chain over the same cells is built. The walk stops where the
|
|
672
|
+
evaluation leaves the chain: a writable view evaluates its parent through
|
|
673
|
+
the transfer, as a leaf does, and that node records itself. */
|
|
649
674
|
static void
|
|
650
675
|
ca_monop_eval (CAMonOp *mo, const ca_lazy_req_t *req, void *data, int dir)
|
|
651
676
|
{
|
|
652
677
|
CArray *chain[CA_MAX_LAZY_DEPTH];
|
|
653
678
|
CArray *leaf = mo->parent;
|
|
654
679
|
int nchain = 0;
|
|
655
|
-
while (
|
|
656
|
-
|
|
657
|
-
|
|
680
|
+
while ( ! ca_monop_is_writable_view(mo->op_id) &&
|
|
681
|
+
nchain < CA_MAX_LAZY_DEPTH &&
|
|
682
|
+
leaf->obj_type == CA_OBJ_MONOP ) {
|
|
658
683
|
chain[nchain++] = leaf;
|
|
659
684
|
leaf = ((CAView *) leaf)->parent;
|
|
660
685
|
}
|
|
@@ -711,9 +736,11 @@ ca_monop_func_allocate (void *ap)
|
|
|
711
736
|
count. Total ca_attach(parent) calls per view lifecycle = 1. CAFake
|
|
712
737
|
uses this same pattern. */
|
|
713
738
|
if ( ca_monop_is_writable_view(ca->op_id) ) {
|
|
714
|
-
|
|
739
|
+
ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
|
|
740
|
+
}
|
|
741
|
+
else {
|
|
742
|
+
ca->ptr = ca_alloc_cells((CArray *) ca);
|
|
715
743
|
}
|
|
716
|
-
ca->ptr = ca_alloc_cells((CArray *) ca);
|
|
717
744
|
}
|
|
718
745
|
|
|
719
746
|
static void
|
|
@@ -741,20 +768,19 @@ ca_monop_func_attach (void *ap)
|
|
|
741
768
|
|
|
742
769
|
Mirrors CAFake.attach. */
|
|
743
770
|
if ( ca_monop_is_writable_view(ca->op_id) ) {
|
|
744
|
-
|
|
745
|
-
ca->ptr = ca_alloc_cells((CArray *) ca);
|
|
771
|
+
ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
|
|
746
772
|
if ( ca_monop_is_cast(ca->op_id) ) {
|
|
747
773
|
/* Cast parent.ptr (parent data_type) → ca.ptr (cast target data_type).
|
|
748
|
-
A cast that
|
|
774
|
+
A cast that can raise skips the masked cells; the parent's
|
|
749
775
|
mask is attached with the parent. A raise here is undone by
|
|
750
776
|
ca_attach, which calls the detach slot once ca->ptr is set. */
|
|
751
777
|
boolean8_t *m = NULL;
|
|
752
|
-
if (
|
|
778
|
+
if ( ca_monop_cast_can_raise(ca->parent->data_type, ca->data_type)
|
|
753
779
|
&& ca_has_mask(ca->parent) ) {
|
|
754
780
|
m = (boolean8_t *) ca->parent->mask->ptr;
|
|
755
781
|
}
|
|
756
|
-
|
|
757
|
-
|
|
782
|
+
ca_cast_block_skip_masked(ca->elements, ca->parent, ca->parent->ptr,
|
|
783
|
+
(CArray *) ca, ca->ptr, m);
|
|
758
784
|
}
|
|
759
785
|
else {
|
|
760
786
|
/* byte_swap: memcpy parent.ptr → ca.ptr (same data_type same bytes),
|
|
@@ -795,9 +821,9 @@ ca_monop_func_sync (void *ap)
|
|
|
795
821
|
/* Cast view → parent: reverse-cast bulk. */
|
|
796
822
|
ca_update_mask(ca);
|
|
797
823
|
if ( ca->mask ) {
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
824
|
+
ca_cast_block_skip_masked(ca->elements, ca, ca->ptr,
|
|
825
|
+
ca->parent, ca->parent->ptr,
|
|
826
|
+
(boolean8_t *) ca->mask->ptr);
|
|
801
827
|
}
|
|
802
828
|
else {
|
|
803
829
|
ca_cast_block(ca->elements, ca, ca->ptr, ca->parent, ca->parent->ptr);
|
data/ext/ca_obj_object.c
CHANGED
|
@@ -311,13 +311,13 @@ ca_object_hook_release (VALUE varg)
|
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
static void
|
|
314
|
-
ca_object_call_transient (VALUE recv,
|
|
314
|
+
ca_object_call_transient (VALUE recv, ID mid,
|
|
315
315
|
int argc, const VALUE *argv,
|
|
316
316
|
int ntrans, const VALUE *trans)
|
|
317
317
|
{
|
|
318
318
|
ca_object_hook_t h;
|
|
319
319
|
h.recv = recv;
|
|
320
|
-
h.mid =
|
|
320
|
+
h.mid = mid;
|
|
321
321
|
h.argc = argc;
|
|
322
322
|
h.argv = argv;
|
|
323
323
|
h.ntrans = ntrans;
|
|
@@ -472,7 +472,7 @@ ca_objmask_get_cells (CAObjectMask *ca, ca_size_t n, ca_size_t *addrs,
|
|
|
472
472
|
VALUE args[2];
|
|
473
473
|
args[0] = raddrs;
|
|
474
474
|
args[1] = rdata;
|
|
475
|
-
ca_object_call_transient(ca->array, "mask_copy_addrs", 2, args, 2, args);
|
|
475
|
+
ca_object_call_transient(ca->array, rb_intern("mask_copy_addrs"), 2, args, 2, args);
|
|
476
476
|
}
|
|
477
477
|
return;
|
|
478
478
|
}
|
|
@@ -520,7 +520,7 @@ ca_objmask_put_cells (CAObjectMask *ca, ca_size_t n, ca_size_t *addrs,
|
|
|
520
520
|
VALUE args[2];
|
|
521
521
|
args[0] = raddrs;
|
|
522
522
|
args[1] = rdata;
|
|
523
|
-
ca_object_call_transient(ca->array, "mask_sync_addrs", 2, args, 2, args);
|
|
523
|
+
ca_object_call_transient(ca->array, rb_intern("mask_sync_addrs"), 2, args, 2, args);
|
|
524
524
|
}
|
|
525
525
|
return;
|
|
526
526
|
}
|
|
@@ -707,7 +707,7 @@ ca_objmask_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
|
|
|
707
707
|
args[1] = rcounts;
|
|
708
708
|
args[2] = rsteps;
|
|
709
709
|
args[3] = rdata;
|
|
710
|
-
ca_object_call_transient(ca->array,
|
|
710
|
+
ca_object_call_transient(ca->array, mid_block,
|
|
711
711
|
4, args, 1, args + 3);
|
|
712
712
|
}
|
|
713
713
|
return;
|
|
@@ -1320,7 +1320,7 @@ ca_object_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
|
|
|
1320
1320
|
VALUE args[2];
|
|
1321
1321
|
args[0] = raddrs;
|
|
1322
1322
|
args[1] = rdata;
|
|
1323
|
-
ca_object_call_transient(ca->self,
|
|
1323
|
+
ca_object_call_transient(ca->self, mid, 2, args, 2, args);
|
|
1324
1324
|
}
|
|
1325
1325
|
return;
|
|
1326
1326
|
}
|
|
@@ -1417,7 +1417,7 @@ ca_object_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
|
|
|
1417
1417
|
args[1] = rcounts;
|
|
1418
1418
|
args[2] = rsteps;
|
|
1419
1419
|
args[3] = rdata;
|
|
1420
|
-
ca_object_call_transient(ca->self,
|
|
1420
|
+
ca_object_call_transient(ca->self, mid_block,
|
|
1421
1421
|
4, args, 1, args + 3);
|
|
1422
1422
|
}
|
|
1423
1423
|
return;
|
|
@@ -1494,7 +1494,7 @@ ca_object_func_fill_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *ptr)
|
|
|
1494
1494
|
VALUE args[2];
|
|
1495
1495
|
args[0] = raddrs;
|
|
1496
1496
|
args[1] = rval;
|
|
1497
|
-
ca_object_call_transient(ca->self,
|
|
1497
|
+
ca_object_call_transient(ca->self, mid, 2, args, 1, args);
|
|
1498
1498
|
}
|
|
1499
1499
|
return;
|
|
1500
1500
|
}
|
|
@@ -1763,7 +1763,7 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
|
|
|
1763
1763
|
|
|
1764
1764
|
ndim = RARRAY_LEN(rdim);
|
|
1765
1765
|
for (i=0; i<ndim; i++) {
|
|
1766
|
-
dim[i] =
|
|
1766
|
+
dim[i] = ca_integer_arg(rb_ary_entry(rdim, i), "a dimension", NULL);
|
|
1767
1767
|
}
|
|
1768
1768
|
|
|
1769
1769
|
TypedData_Get_Struct(self, CAObject, &caobject_data_type, ca);
|
data/ext/ca_obj_reduce.c
CHANGED
|
@@ -216,8 +216,7 @@ static void
|
|
|
216
216
|
ca_reduce_func_allocate (void *ap)
|
|
217
217
|
{
|
|
218
218
|
CAReduce *ca = (CAReduce *) ap;
|
|
219
|
-
|
|
220
|
-
ca->ptr = ca_alloc_cells((CArray *) ca);
|
|
219
|
+
ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
|
|
221
220
|
}
|
|
222
221
|
|
|
223
222
|
static void
|
|
@@ -226,8 +225,7 @@ ca_reduce_func_attach (void *ap)
|
|
|
226
225
|
CAReduce *ca = (CAReduce *) ap;
|
|
227
226
|
char *p;
|
|
228
227
|
ca_size_t i;
|
|
229
|
-
|
|
230
|
-
ca->ptr = ca_alloc_cells((CArray *) ca);
|
|
228
|
+
ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
|
|
231
229
|
p = ca->ptr;
|
|
232
230
|
for (i=0; i<ca->elements; i++) {
|
|
233
231
|
ca_reduce_func_xfer_addrs(ca, 1, &i, p, CA_XFER_GET);
|
data/ext/ca_obj_refer.c
CHANGED
|
@@ -339,16 +339,75 @@ ca_keep_chain_flags (CArray *view, CArray *src)
|
|
|
339
339
|
}
|
|
340
340
|
|
|
341
341
|
/* The sources of folded views (CA_FLAG_FOLDED), keyed by the view. Views are
|
|
342
|
-
made and freed in any Ractor, so the table is taken under a lock.
|
|
342
|
+
made and freed in any Ractor, so the table is taken under a lock.
|
|
343
|
+
|
|
344
|
+
st_insert can grow the table through ruby_xmalloc, which can run a GC, and
|
|
345
|
+
the GC frees folded views through ca_fold_forget. ca_fold_forget therefore
|
|
346
|
+
never waits for the lock: when it is held (by the inserting thread itself,
|
|
347
|
+
or by a Ractor stopped for this GC) the view is put on a list instead, and
|
|
348
|
+
whoever takes the lock next removes the listed views first. A freed view's
|
|
349
|
+
address can be reused by a new view only once that view is recorded, which
|
|
350
|
+
takes the lock and so empties the list first. */
|
|
343
351
|
static st_table *ca_fold_sources;
|
|
344
352
|
static rb_nativethread_lock_t ca_fold_lock;
|
|
345
353
|
|
|
354
|
+
typedef struct ca_fold_gone {
|
|
355
|
+
struct ca_fold_gone *next;
|
|
356
|
+
st_data_t key;
|
|
357
|
+
} ca_fold_gone_t;
|
|
358
|
+
|
|
359
|
+
static ca_fold_gone_t *ca_fold_gone_list = NULL;
|
|
360
|
+
|
|
361
|
+
/* With the lock held: remove the views freed while it was held elsewhere.
|
|
362
|
+
The list is allocated with malloc, not xmalloc: it is grown inside a GC. */
|
|
363
|
+
static void
|
|
364
|
+
ca_fold_forget_gone (void)
|
|
365
|
+
{
|
|
366
|
+
ca_fold_gone_t *g = __atomic_exchange_n(&ca_fold_gone_list, NULL, __ATOMIC_ACQ_REL);
|
|
367
|
+
while ( g ) {
|
|
368
|
+
ca_fold_gone_t *next = g->next;
|
|
369
|
+
st_data_t key = g->key;
|
|
370
|
+
st_delete(ca_fold_sources, &key, NULL);
|
|
371
|
+
free(g);
|
|
372
|
+
g = next;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
static void
|
|
377
|
+
ca_fold_lock_table (void)
|
|
378
|
+
{
|
|
379
|
+
ca_native_lock_without_gvl(&ca_fold_lock);
|
|
380
|
+
ca_fold_forget_gone();
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
typedef struct {
|
|
384
|
+
CArray *view;
|
|
385
|
+
CArray *src;
|
|
386
|
+
} ca_fold_note_arg_t;
|
|
387
|
+
|
|
388
|
+
static VALUE
|
|
389
|
+
ca_fold_insert (VALUE varg)
|
|
390
|
+
{
|
|
391
|
+
ca_fold_note_arg_t *a = (ca_fold_note_arg_t *) varg;
|
|
392
|
+
st_insert(ca_fold_sources, (st_data_t) a->view, (st_data_t) a->src);
|
|
393
|
+
return Qnil;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/* The growth of the table can raise NoMemoryError; the lock is released
|
|
397
|
+
before that raise goes on. */
|
|
346
398
|
void
|
|
347
399
|
ca_fold_note_source (CArray *view, CArray *src)
|
|
348
400
|
{
|
|
349
|
-
|
|
350
|
-
|
|
401
|
+
ca_fold_note_arg_t arg = { view, src };
|
|
402
|
+
int state = 0;
|
|
403
|
+
/* lock: st_insert can run a GC; a view it frees is listed, not waited for */
|
|
404
|
+
ca_fold_lock_table();
|
|
405
|
+
/* protect: releases a native lock; no array is held */
|
|
406
|
+
rb_protect(ca_fold_insert, (VALUE) &arg, &state);
|
|
351
407
|
rb_native_mutex_unlock(&ca_fold_lock);
|
|
408
|
+
if ( state ) {
|
|
409
|
+
rb_jump_tag(state);
|
|
410
|
+
}
|
|
352
411
|
ca_set_flag(view, CA_FLAG_FOLDED);
|
|
353
412
|
}
|
|
354
413
|
|
|
@@ -356,7 +415,7 @@ CArray *
|
|
|
356
415
|
ca_fold_source (CArray *view)
|
|
357
416
|
{
|
|
358
417
|
st_data_t src = 0;
|
|
359
|
-
|
|
418
|
+
ca_fold_lock_table();
|
|
360
419
|
st_lookup(ca_fold_sources, (st_data_t) view, &src);
|
|
361
420
|
rb_native_mutex_unlock(&ca_fold_lock);
|
|
362
421
|
return (CArray *) src;
|
|
@@ -366,9 +425,30 @@ void
|
|
|
366
425
|
ca_fold_forget (CArray *view)
|
|
367
426
|
{
|
|
368
427
|
st_data_t key = (st_data_t) view;
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
428
|
+
ca_fold_gone_t *g;
|
|
429
|
+
if ( rb_native_mutex_trylock(&ca_fold_lock) == 0 ) {
|
|
430
|
+
ca_fold_forget_gone();
|
|
431
|
+
st_delete(ca_fold_sources, &key, NULL);
|
|
432
|
+
rb_native_mutex_unlock(&ca_fold_lock);
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
g = (ca_fold_gone_t *) malloc(sizeof(*g));
|
|
436
|
+
if ( g == NULL ) {
|
|
437
|
+
/* No memory for the list: wait for the lock, as there is no other way
|
|
438
|
+
to drop the record. */
|
|
439
|
+
/* lock: only with no memory for the list; the wait may block the GC */
|
|
440
|
+
rb_native_mutex_lock(&ca_fold_lock);
|
|
441
|
+
ca_fold_forget_gone();
|
|
442
|
+
st_delete(ca_fold_sources, &key, NULL);
|
|
443
|
+
rb_native_mutex_unlock(&ca_fold_lock);
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
g->key = key;
|
|
447
|
+
g->next = __atomic_load_n(&ca_fold_gone_list, __ATOMIC_ACQUIRE);
|
|
448
|
+
while ( ! __atomic_compare_exchange_n(&ca_fold_gone_list, &g->next, g, 0,
|
|
449
|
+
__ATOMIC_RELEASE, __ATOMIC_ACQUIRE) ) {
|
|
450
|
+
/* g->next now holds the head that won; try again on top of it */
|
|
451
|
+
}
|
|
372
452
|
}
|
|
373
453
|
|
|
374
454
|
/* Builds the folded view of `ca` for reshape / flatten: a CAStride over
|
|
@@ -557,11 +637,25 @@ rb_ca_refer_kw (int argc, VALUE *argv, VALUE ropt, VALUE self)
|
|
|
557
637
|
rdim = rb_ca_dim(self);
|
|
558
638
|
}
|
|
559
639
|
}
|
|
640
|
+
if ( ca_is_face(ca) && ! rb_obj_is_data_class(rtype)
|
|
641
|
+
&& ( data_type == CA_OBJECT || ca->data_type == CA_FIXLEN ) ) {
|
|
642
|
+
/* A Face's cells do not mean their storage bytes, and the view below
|
|
643
|
+
would be lifted back to a Face reading cells of the wrong width. */
|
|
644
|
+
CArray *storage = ca_strip_face(ca);
|
|
645
|
+
if ( ! ( data_type == ca->data_type && bytes == ca->bytes )
|
|
646
|
+
&& ! ( data_type == storage->data_type
|
|
647
|
+
&& bytes == storage->bytes ) ) {
|
|
648
|
+
rb_raise(rb_eTypeError,
|
|
649
|
+
"%"PRIsVALUE" has no view of its values in another data_type: "
|
|
650
|
+
"#to_type gives the values, #parent.refer the raw storage",
|
|
651
|
+
rb_obj_class(self));
|
|
652
|
+
}
|
|
653
|
+
}
|
|
560
654
|
Check_Type(rdim, T_ARRAY);
|
|
561
655
|
CA_CHECK_RANK_GIVEN(RARRAY_LEN(rdim)); /* before dim[] is filled */
|
|
562
656
|
ndim = RARRAY_LEN(rdim);
|
|
563
|
-
for (i = 0; i < ndim; i++) dim[i] =
|
|
564
|
-
if (! NIL_P(roffset)) offset =
|
|
657
|
+
for (i = 0; i < ndim; i++) dim[i] = ca_integer_arg(rb_ary_entry(rdim, i), "a dimension", NULL);
|
|
658
|
+
if (! NIL_P(roffset)) offset = ca_integer_arg(roffset, "the offset", NULL);
|
|
565
659
|
/* When rtype is a data_class the result is a record array. It is
|
|
566
660
|
built on the storage, never on a Face, so that a Face stays on top
|
|
567
661
|
of the chain: re-reading a record array as its own data_class lifts
|
|
@@ -723,7 +817,7 @@ rb_ca_reshape (int argc, VALUE *argv, VALUE self)
|
|
|
723
817
|
new_dim[i] = ca->dim[src];
|
|
724
818
|
}
|
|
725
819
|
else {
|
|
726
|
-
new_dim[i] =
|
|
820
|
+
new_dim[i] = ca_integer_arg(argv[i], "a dimension", "reshape");
|
|
727
821
|
}
|
|
728
822
|
/* A product too large to count cannot equal the element count; an
|
|
729
823
|
extent of zero anywhere makes it zero whatever it overflowed to. */
|
data/ext/ca_obj_repeat.c
CHANGED
|
@@ -233,7 +233,7 @@ rb_ca_repeat (int argc, VALUE *argv, VALUE self)
|
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
235
|
else {
|
|
236
|
-
count[i] =
|
|
236
|
+
count[i] = ca_integer_arg(argv[i], "a repeat count", "repeat");
|
|
237
237
|
if ( count[i] == 0 ) {
|
|
238
238
|
rb_raise(rb_eArgError,
|
|
239
239
|
"zero repeat count specified in creating CARepeat object");
|
data/ext/ca_obj_roll.c
CHANGED
|
@@ -525,16 +525,14 @@ static void
|
|
|
525
525
|
ca_roll_func_allocate (void *ap)
|
|
526
526
|
{
|
|
527
527
|
CARoll *ca = (CARoll *) ap;
|
|
528
|
-
|
|
529
|
-
ca->ptr = ca_alloc_cells((CArray *) ca);
|
|
528
|
+
ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
|
|
530
529
|
}
|
|
531
530
|
|
|
532
531
|
static void
|
|
533
532
|
ca_roll_func_attach (void *ap)
|
|
534
533
|
{
|
|
535
534
|
CARoll *ca = (CARoll *) ap;
|
|
536
|
-
|
|
537
|
-
ca->ptr = ca_alloc_cells((CArray *) ca);
|
|
535
|
+
ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
|
|
538
536
|
ca_roll_iterate_regions(ca, ca->ptr, 0);
|
|
539
537
|
}
|
|
540
538
|
|
data/ext/ca_obj_select.c
CHANGED
|
@@ -465,9 +465,11 @@ ca_select_func_allocate (void *ap)
|
|
|
465
465
|
{
|
|
466
466
|
CASelect *ca = (CASelect *) ap;
|
|
467
467
|
if ( ! ca_parent_lends_no_memory(ca->parent) ) {
|
|
468
|
-
|
|
468
|
+
ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
ca->ptr = ca_alloc_cells((CArray *) ca);
|
|
469
472
|
}
|
|
470
|
-
ca->ptr = ca_alloc_cells((CArray *) ca);
|
|
471
473
|
}
|
|
472
474
|
|
|
473
475
|
static void
|
|
@@ -482,10 +484,10 @@ ca_select_func_attach (void *ap)
|
|
|
482
484
|
return;
|
|
483
485
|
}
|
|
484
486
|
|
|
485
|
-
|
|
487
|
+
ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
|
|
486
488
|
ca_select_describe_axes(ca, desc, pdims);
|
|
487
|
-
|
|
488
|
-
|
|
489
|
+
ca_axis_dispatch_gather(ca->parent, pdims, desc, ca->ndim,
|
|
490
|
+
ca->bytes, ca->elements, NULL, ca->ptr);
|
|
489
491
|
}
|
|
490
492
|
|
|
491
493
|
static void
|
|
@@ -600,7 +602,7 @@ ca_operation_function_t ca_select_func = {
|
|
|
600
602
|
/* ------------------------------------------------------------------- */
|
|
601
603
|
|
|
602
604
|
VALUE
|
|
603
|
-
rb_ca_select_new (VALUE cary, VALUE select)
|
|
605
|
+
rb_ca_select_new (VALUE cary, volatile VALUE select)
|
|
604
606
|
{
|
|
605
607
|
volatile VALUE obj;
|
|
606
608
|
CArray *parent, *cselect;
|
data/ext/ca_obj_select_axis.c
CHANGED
|
@@ -726,9 +726,11 @@ ca_select_axis_func_allocate (void *ap)
|
|
|
726
726
|
{
|
|
727
727
|
CASelectAxis *ca = (CASelectAxis *) ap;
|
|
728
728
|
if ( ! ca_parent_lends_no_memory(ca->parent) ) {
|
|
729
|
-
|
|
729
|
+
ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
|
|
730
|
+
}
|
|
731
|
+
else {
|
|
732
|
+
ca->ptr = ca_alloc_cells((CArray *) ca);
|
|
730
733
|
}
|
|
731
|
-
ca->ptr = ca_alloc_cells((CArray *) ca);
|
|
732
734
|
}
|
|
733
735
|
|
|
734
736
|
static void
|
|
@@ -743,10 +745,10 @@ ca_select_axis_func_attach (void *ap)
|
|
|
743
745
|
return;
|
|
744
746
|
}
|
|
745
747
|
|
|
746
|
-
|
|
748
|
+
ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
|
|
747
749
|
ca_select_axis_describe_axes(ca, desc, pdims);
|
|
748
|
-
|
|
749
|
-
|
|
750
|
+
ca_axis_dispatch_gather(ca->parent, pdims, desc, ca->ndim,
|
|
751
|
+
ca->bytes, ca->elements, NULL, ca->ptr);
|
|
750
752
|
}
|
|
751
753
|
|
|
752
754
|
static void
|
|
@@ -1008,7 +1010,7 @@ rb_ca_select_axis (int argc, VALUE *argv, VALUE self)
|
|
|
1008
1010
|
}
|
|
1009
1011
|
|
|
1010
1012
|
for ( i = 0; i < argc; i++ ) {
|
|
1011
|
-
VALUE arg = argv[i];
|
|
1013
|
+
volatile VALUE arg = argv[i];
|
|
1012
1014
|
|
|
1013
1015
|
if ( ! rb_obj_is_carray(arg) ) {
|
|
1014
1016
|
ca_index_axis_key(arg, parent->dim[i], i,
|