carray 3.0.3 → 3.0.4
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 +264 -0
- data/README.md +3 -2
- data/carray.gemspec +1 -1
- data/ext/ca_axis_descriptor.h +27 -24
- data/ext/ca_axis_dispatch.c +1 -6
- data/ext/ca_axis_group.c +27 -27
- data/ext/ca_binop_dispatch.c +1 -29
- data/ext/ca_binop_dispatch.h +0 -1
- data/ext/ca_categorical_iterator.c +205 -134
- data/ext/ca_compare.c +7 -18
- data/ext/ca_group_iter.c +16 -17
- data/ext/ca_kernel_iterator.c +1234 -1994
- data/ext/ca_kernel_iterator.h +391 -437
- data/ext/ca_monop_dispatch.c +82 -59
- data/ext/ca_monop_dispatch.h +1 -0
- data/ext/ca_obj_array.c +96 -39
- data/ext/ca_obj_bincmp.c +24 -21
- data/ext/ca_obj_binop.c +15 -26
- data/ext/ca_obj_bitarray.c +3 -2
- data/ext/ca_obj_bitfield.c +5 -6
- data/ext/ca_obj_block.c +18 -9
- data/ext/ca_obj_byte_swap.c +5 -6
- data/ext/ca_obj_const_string.c +173 -19
- data/ext/ca_obj_face.c +238 -57
- data/ext/ca_obj_face.h +19 -0
- data/ext/ca_obj_fake.c +15 -29
- data/ext/ca_obj_farray.c +6 -3
- data/ext/ca_obj_field.c +17 -5
- data/ext/ca_obj_fixlen_string.c +2 -3
- data/ext/ca_obj_grid.c +406 -132
- data/ext/ca_obj_meld.c +172 -34
- data/ext/ca_obj_moncmp.c +23 -8
- data/ext/ca_obj_monop.c +67 -57
- data/ext/ca_obj_object.c +157 -51
- data/ext/ca_obj_record.c +12 -7
- data/ext/ca_obj_reduce.c +13 -8
- data/ext/ca_obj_refer.c +25 -27
- data/ext/ca_obj_remap.c +30 -9
- data/ext/ca_obj_repeat.c +9 -10
- data/ext/ca_obj_roll.c +6 -5
- data/ext/ca_obj_select.c +14 -9
- data/ext/ca_obj_select_axis.c +15 -47
- data/ext/ca_obj_shift.c +11 -1
- data/ext/ca_obj_stack.c +179 -34
- data/ext/ca_obj_stride.c +162 -115
- data/ext/ca_obj_string.c +71 -4
- data/ext/ca_obj_tile.c +6 -5
- data/ext/ca_obj_time.c +22 -6
- data/ext/ca_obj_timedelta.c +22 -6
- data/ext/ca_obj_transpose.c +5 -3
- data/ext/ca_obj_triop.c +23 -32
- data/ext/ca_obj_window.c +190 -26
- data/ext/ca_sweep_engine.c +2 -2
- data/ext/caframe_csv_reader.c +80 -1
- data/ext/carray.h +181 -75
- data/ext/carray_access.c +184 -96
- data/ext/carray_address_basis.c +343 -87
- data/ext/carray_attribute.c +89 -6
- data/ext/carray_bincount.c +10 -8
- data/ext/carray_broadcast.c +13 -8
- data/ext/carray_build_flags.h +3 -0
- data/ext/carray_call_cfunc.c +108 -0
- data/ext/carray_cast.c +142 -24
- data/ext/carray_cast_func.rb +46 -6
- data/ext/carray_class.c +4 -3
- data/ext/carray_config.h +28 -0
- data/ext/carray_conversion.c +16 -7
- data/ext/carray_copy.c +51 -14
- data/ext/carray_core.c +147 -55
- data/ext/carray_count.c +50 -20
- data/ext/carray_element.c +76 -33
- data/ext/carray_factorize.c +118 -64
- data/ext/carray_generate.c +191 -50
- data/ext/carray_histogram.c +62 -17
- data/ext/carray_hold.c +12 -15
- data/ext/carray_index_classifier.c +388 -176
- data/ext/carray_internal.h +336 -29
- data/ext/carray_kernels_bincmp.c +4453 -0
- data/ext/carray_kernels_binop.c +11368 -0
- data/ext/carray_kernels_init.c +1734 -0
- data/ext/carray_kernels_moncmp.c +2104 -0
- data/ext/carray_kernels_monop.c +18369 -0
- data/ext/carray_kernels_reduce_aggregate.c +23818 -0
- data/ext/carray_kernels_reduce_boolean.c +406 -0
- data/ext/carray_kernels_reduce_cumulative.c +10086 -0
- data/ext/carray_kernels_reduce_extreme.c +17634 -0
- data/ext/carray_kernels_reduce_variance.c +3849 -0
- data/ext/carray_kernels_scan.c +4302 -0
- data/ext/carray_kernels_search.c +33829 -0
- data/ext/carray_kernels_sort.c +10906 -0
- data/ext/carray_kernels_triop.c +1864 -0
- data/ext/carray_key_windows.c +216 -0
- data/ext/carray_lazy.c +170 -111
- data/ext/carray_mask.c +292 -72
- data/ext/carray_median_percentile.c +82 -28
- data/ext/carray_memory_view.c +669 -561
- data/ext/carray_operator.c +124 -47
- data/ext/carray_order.c +384 -76
- data/ext/carray_partition.c +28 -27
- data/ext/carray_random.c +67 -13
- data/ext/carray_round.c +273 -0
- data/ext/carray_scatter.c +63 -38
- data/ext/carray_segment.c +3 -3
- data/ext/carray_slab.c +91 -28
- data/ext/carray_sort.c +35 -35
- data/ext/carray_struct.c +19 -18
- data/ext/carray_test.c +26 -5
- data/ext/carray_undef.c +4 -0
- data/ext/carray_utils.c +246 -45
- data/ext/mk_call_cfunc.rb +19 -2
- data/ext/mkkernel.rb +198 -240
- data/ext/ruby_carray.c +10 -4
- data/ext/version.h +4 -4
- data/lib/carray/attribute.rb +126 -22
- data/lib/carray/autoload_carray.rb +8 -31
- data/lib/carray/autoload_method_extension.rb +14 -5
- data/lib/carray/axis_group.rb +255 -106
- data/lib/carray/basics.rb +78 -10
- data/lib/carray/bincount_nd.rb +12 -2
- data/lib/carray/block_iterator.rb +126 -23
- data/lib/carray/boolean_reduce.rb +17 -17
- data/lib/carray/categorical.rb +165 -43
- data/lib/carray/categorical_iterator.rb +130 -27
- data/lib/carray/complex.rb +5 -5
- data/lib/carray/conditional.rb +67 -18
- data/lib/carray/const_string.rb +14 -5
- data/lib/carray/construct.rb +15 -11
- data/lib/carray/data_type_extension.rb +1 -0
- data/lib/carray/distance_limit.rb +77 -0
- data/lib/carray/fixlen_string.rb +7 -1
- data/lib/carray/frame/column_verbs.rb +70 -0
- data/lib/carray/frame/convert.rb +69 -0
- data/lib/carray/frame/csv_parser.rb +111 -17
- data/lib/carray/frame/describe.rb +3 -2
- data/lib/carray/frame/frame.rb +246 -21
- data/lib/carray/frame/group.rb +510 -95
- data/lib/carray/frame/io.rb +115 -28
- data/lib/carray/frame/join.rb +312 -51
- data/lib/carray/frame/records.rb +83 -7
- data/lib/carray/frame/reshape.rb +42 -35
- data/lib/carray/frame/sort.rb +7 -19
- data/lib/carray/frame/verbs.rb +188 -48
- data/lib/carray/frame/windows.rb +117 -0
- data/lib/carray/frame.rb +2 -0
- data/lib/carray/fuse_source.rb +11 -4
- data/lib/carray/fusion.rb +40 -16
- data/lib/carray/histogram.rb +6 -1
- data/lib/carray/inspect.rb +31 -8
- data/lib/carray/iterator.rb +85 -7
- data/lib/carray/key_windows.rb +620 -0
- data/lib/carray/lazy.rb +182 -183
- data/lib/carray/mask_gap_fill.rb +63 -57
- data/lib/carray/meld_reduce.rb +12 -14
- data/lib/carray/methods/bin.rb +26 -18
- data/lib/carray/methods/bincount.rb +16 -0
- data/lib/carray/methods/bit_string.rb +1 -1
- data/lib/carray/methods/choose.rb +16 -3
- data/lib/carray/methods/composition.rb +3 -1
- data/lib/carray/methods/gather_nd.rb +14 -5
- data/lib/carray/methods/insert_block.rb +2 -2
- data/lib/carray/methods/is_between.rb +59 -0
- data/lib/carray/methods/is_in.rb +53 -36
- data/lib/carray/methods/join.rb +1 -6
- data/lib/carray/methods/lag.rb +49 -0
- data/lib/carray/methods/locate_addr.rb +47 -51
- data/lib/carray/methods/mode.rb +1 -1
- data/lib/carray/methods/nunique.rb +11 -6
- data/lib/carray/methods/resize.rb +3 -3
- data/lib/carray/methods/value_counts.rb +4 -11
- data/lib/carray/record_order.rb +3 -3
- data/lib/carray/rng.rb +3 -0
- data/lib/carray/runtime.rb +53 -8
- data/lib/carray/segment_iterator.rb +313 -82
- data/lib/carray/serialize.rb +24 -10
- data/lib/carray/slab_iterator.rb +50 -0
- data/lib/carray/string.rb +105 -5
- data/lib/carray/string_operation_extension.rb +8 -1
- data/lib/carray/struct.rb +9 -21
- data/lib/carray/struct_builder.rb +19 -8
- data/lib/carray/time.rb +726 -104
- data/lib/carray/window_iterator.rb +122 -16
- data/lib/carray.rb +7 -0
- data/yard-stubs/ca_obj_bitfield.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +4 -0
- data/yard-stubs/ca_obj_grid.rb +21 -10
- data/yard-stubs/ca_obj_refer.rb +2 -1
- data/yard-stubs/ca_obj_roll.rb +2 -2
- data/yard-stubs/ca_obj_shift.rb +5 -1
- data/yard-stubs/ca_obj_tile.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +8 -3
- data/yard-stubs/carray_access.rb +6 -1
- data/yard-stubs/carray_attribute.rb +0 -5
- data/yard-stubs/carray_cast.rb +10 -0
- data/yard-stubs/carray_conversion.rb +2 -1
- data/yard-stubs/carray_copy.rb +4 -2
- data/yard-stubs/carray_count.rb +4 -4
- data/yard-stubs/carray_element.rb +10 -4
- data/yard-stubs/carray_generate.rb +21 -1
- data/yard-stubs/carray_mask.rb +26 -2
- data/yard-stubs/carray_math.rb +27 -1
- data/yard-stubs/carray_median_percentile.rb +5 -2
- data/yard-stubs/carray_memory_view.rb +40 -23
- data/yard-stubs/carray_order.rb +29 -18
- data/yard-stubs/carray_random.rb +13 -4
- data/yard-stubs/carray_scatter.rb +14 -9
- data/yard-stubs/carray_slab.rb +9 -2
- data/yard-stubs/carray_sort.rb +6 -5
- data/yard-stubs/carray_test.rb +5 -3
- metadata +25 -3
- data/lib/carray/arrow_tensor.rb +0 -401
- data/lib/carray/table.rb +0 -108
data/ext/carray_core.c
CHANGED
|
@@ -323,12 +323,13 @@ ca_install_obj_type (VALUE klass,
|
|
|
323
323
|
const ca_operation_function_t *func,
|
|
324
324
|
size_t func_size)
|
|
325
325
|
{
|
|
326
|
-
int obj_type
|
|
326
|
+
int obj_type;
|
|
327
327
|
|
|
328
|
-
|
|
328
|
+
/* The obj_type parameters of ca_array_alloc / ca_stride_setup are
|
|
329
|
+
int8_t, so a number of 128 or above cannot be passed to them. */
|
|
330
|
+
if ( ca_obj_num > INT8_MAX || ca_obj_num + 1 >= CA_OBJ_TYPE_MAX ) {
|
|
329
331
|
rb_raise(rb_eRuntimeError,
|
|
330
|
-
"internal: too many CArray object types installed
|
|
331
|
-
CA_OBJ_TYPE_MAX);
|
|
332
|
+
"internal: too many CArray object types installed (an obj_type is numbered below 128)");
|
|
332
333
|
}
|
|
333
334
|
|
|
334
335
|
if ( func_size > sizeof(ca_operation_function_t) ) {
|
|
@@ -338,16 +339,20 @@ ca_install_obj_type (VALUE klass,
|
|
|
338
339
|
func_size, sizeof(ca_operation_function_t));
|
|
339
340
|
}
|
|
340
341
|
|
|
341
|
-
/*
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
342
|
+
/* xfer_index, xfer_addrs and xfer_all have no default, so the table
|
|
343
|
+
reaches through xfer_all; the slots after it fall back when NULL. */
|
|
344
|
+
if ( func_size < offsetof(ca_operation_function_t, xfer_all) +
|
|
345
|
+
sizeof(((ca_operation_function_t *) 0)->xfer_all) ) {
|
|
345
346
|
rb_raise(rb_eRuntimeError,
|
|
346
347
|
"operation table is too small to dispatch (%zu < %zu)",
|
|
347
348
|
func_size,
|
|
348
|
-
(size_t) offsetof(ca_operation_function_t,
|
|
349
|
+
(size_t) (offsetof(ca_operation_function_t, xfer_all) +
|
|
350
|
+
sizeof(((ca_operation_function_t *) 0)->xfer_all)));
|
|
349
351
|
}
|
|
350
352
|
|
|
353
|
+
/* A refused table takes no number. */
|
|
354
|
+
obj_type = ca_obj_num++;
|
|
355
|
+
|
|
351
356
|
/* Copy by the caller's length and zero the rest: slots this build knows
|
|
352
357
|
about but the caller does not are NULL, which every dispatcher already
|
|
353
358
|
reads as "not provided". */
|
|
@@ -557,7 +562,7 @@ rb_ca_new_reduced (VALUE self, int8_t *slab_axes, int8_t naxes, int32_t data_typ
|
|
|
557
562
|
ca_size_t bytes = ca_sizeof[data_type];
|
|
558
563
|
if ( bytes <= 0 ) {
|
|
559
564
|
rb_raise(rb_eArgError,
|
|
560
|
-
"rb_ca_new_reduced: data_type=%d
|
|
565
|
+
"rb_ca_new_reduced: data_type=%d has no fixed width (use rb_ca_new_reduced_bytes)",
|
|
561
566
|
(int) data_type);
|
|
562
567
|
}
|
|
563
568
|
return rb_ca_new_reduced_bytes(self, slab_axes, naxes, data_type, bytes, keep_axis);
|
|
@@ -565,9 +570,8 @@ rb_ca_new_reduced (VALUE self, int8_t *slab_axes, int8_t naxes, int32_t data_typ
|
|
|
565
570
|
|
|
566
571
|
/* ------------------------------------------------------------------- */
|
|
567
572
|
|
|
568
|
-
/* Parse the variadic axis argument of a reduction kernel into
|
|
569
|
-
|
|
570
|
-
int8_t array, with full validation.
|
|
573
|
+
/* Parse the variadic axis argument of a reduction kernel into an int8_t
|
|
574
|
+
array, with full validation.
|
|
571
575
|
|
|
572
576
|
Accepts:
|
|
573
577
|
- Integer args: kernel(0, 2, 3) → axes = {0, 2, 3}
|
|
@@ -575,8 +579,9 @@ rb_ca_new_reduced (VALUE self, int8_t *slab_axes, int8_t naxes, int32_t data_typ
|
|
|
575
579
|
- Negative axes (Python-style): -1 = innermost, normalised to
|
|
576
580
|
positive in-range indices before validation
|
|
577
581
|
|
|
582
|
+
argc == 0 means every axis (a full reduction).
|
|
583
|
+
|
|
578
584
|
Validation (raises ArgumentError on failure):
|
|
579
|
-
- argc == 0 → no axes given
|
|
580
585
|
- naxes > ca->ndim → too many axes
|
|
581
586
|
- any axis out of [0, ca->ndim) → range
|
|
582
587
|
- duplicate axes → duplicate
|
|
@@ -591,6 +596,10 @@ rb_ca_new_reduced (VALUE self, int8_t *slab_axes, int8_t naxes, int32_t data_typ
|
|
|
591
596
|
variadic entry (rb_ca_parse_reduce_axes) and the kwarg entry
|
|
592
597
|
(rb_ca_parse_reduce_axes_kw). ctx names the method in error messages;
|
|
593
598
|
the two public entries pass the method the user called. */
|
|
599
|
+
/* ctx NULL names the method the user called, looked up only when a
|
|
600
|
+
message is built: the lookup walks the frames. */
|
|
601
|
+
#define CA_CTX_NAME(ctx) ( (ctx) ? (ctx) : ca_calling_method_name() )
|
|
602
|
+
|
|
594
603
|
static int8_t
|
|
595
604
|
parse_axes_items (const VALUE *items, int count, CArray *ca,
|
|
596
605
|
int8_t *out_axes, const char *ctx)
|
|
@@ -599,31 +608,25 @@ parse_axes_items (const VALUE *items, int count, CArray *ca,
|
|
|
599
608
|
int8_t seen[CA_RANK_MAX];
|
|
600
609
|
|
|
601
610
|
if ( count <= 0 ) {
|
|
602
|
-
rb_raise(rb_eArgError, "%s: empty axes array", ctx);
|
|
611
|
+
rb_raise(rb_eArgError, "%s: empty axes array", CA_CTX_NAME(ctx));
|
|
603
612
|
}
|
|
604
|
-
if ( count > CA_RANK_MAX ) {
|
|
613
|
+
if ( count > CA_RANK_MAX ) { /* rank-check: a list of axes, no longer than the rank */
|
|
605
614
|
rb_raise(rb_eArgError,
|
|
606
615
|
"%s: too many axes (%d > CA_RANK_MAX=%d)",
|
|
607
|
-
ctx, count, CA_RANK_MAX);
|
|
616
|
+
CA_CTX_NAME(ctx), count, CA_RANK_MAX);
|
|
608
617
|
}
|
|
609
618
|
if ( count > ca->ndim ) {
|
|
610
619
|
rb_raise(rb_eArgError,
|
|
611
620
|
"%s: too many axes (%d > ndim=%d)",
|
|
612
|
-
ctx, count, (int) ca->ndim);
|
|
621
|
+
CA_CTX_NAME(ctx), count, (int) ca->ndim);
|
|
613
622
|
}
|
|
614
623
|
|
|
615
624
|
for ( i = 0; i < CA_RANK_MAX; i++ ) seen[i] = 0;
|
|
616
625
|
for ( i = 0; i < count; i++ ) {
|
|
617
|
-
ca_size_t
|
|
618
|
-
ca_size_t a = ( raw < 0 ) ? raw + ca->ndim : raw;
|
|
619
|
-
if ( a < 0 || a >= ca->ndim ) {
|
|
620
|
-
rb_raise(rb_eArgError,
|
|
621
|
-
"%s: axis %ld out of range for ndim %d",
|
|
622
|
-
ctx, (long) raw, (int) ca->ndim);
|
|
623
|
-
}
|
|
626
|
+
ca_size_t a = ca_normalize_axis_arg(items[i], (int) ca->ndim, ctx);
|
|
624
627
|
if ( seen[a] ) {
|
|
625
628
|
rb_raise(rb_eArgError,
|
|
626
|
-
"%s: duplicate axis %ld", ctx, (long) a);
|
|
629
|
+
"%s: duplicate axis %ld", CA_CTX_NAME(ctx), (long) a);
|
|
627
630
|
}
|
|
628
631
|
seen[a] = 1;
|
|
629
632
|
out_axes[i] = (int8_t) a;
|
|
@@ -656,8 +659,7 @@ rb_ca_parse_reduce_axes (int argc, VALUE *argv, CArray *ca, int8_t *out_axes)
|
|
|
656
659
|
items = (const VALUE *) RARRAY_CONST_PTR(arr);
|
|
657
660
|
}
|
|
658
661
|
|
|
659
|
-
return parse_axes_items(items, count, ca, out_axes,
|
|
660
|
-
ca_calling_method_name());
|
|
662
|
+
return parse_axes_items(items, count, ca, out_axes, NULL);
|
|
661
663
|
}
|
|
662
664
|
|
|
663
665
|
/* Kwarg form of rb_ca_parse_reduce_axes — accepts the `axis:` value as
|
|
@@ -698,14 +700,13 @@ rb_ca_parse_reduce_axes_kw_ctx (VALUE axis_val, CArray *ca, int8_t *out_axes,
|
|
|
698
700
|
rb_raise(rb_eTypeError,
|
|
699
701
|
"%s: axis must be nil, an Integer or an Array of Integer "
|
|
700
702
|
"(got %"PRIsVALUE")",
|
|
701
|
-
ctx, rb_obj_class(axis_val));
|
|
703
|
+
CA_CTX_NAME(ctx), rb_obj_class(axis_val));
|
|
702
704
|
}
|
|
703
705
|
|
|
704
706
|
int8_t
|
|
705
707
|
rb_ca_parse_reduce_axes_kw (VALUE axis_val, CArray *ca, int8_t *out_axes)
|
|
706
708
|
{
|
|
707
|
-
return rb_ca_parse_reduce_axes_kw_ctx(axis_val, ca, out_axes,
|
|
708
|
-
ca_calling_method_name());
|
|
709
|
+
return rb_ca_parse_reduce_axes_kw_ctx(axis_val, ca, out_axes, NULL);
|
|
709
710
|
}
|
|
710
711
|
|
|
711
712
|
/* ------------------------------------------------------------------- */
|
|
@@ -946,7 +947,7 @@ ca_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
|
|
|
946
947
|
CArray *ca = (CArray *) ap;
|
|
947
948
|
|
|
948
949
|
if ( dir == CA_XFER_PUT && ca_is_readonly(ca) ) {
|
|
949
|
-
|
|
950
|
+
ca_refuse_read_only();
|
|
950
951
|
}
|
|
951
952
|
|
|
952
953
|
/* Fast path: non-CA_OBJECT element type needs no GC protection. */
|
|
@@ -1074,7 +1075,7 @@ ca_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *data, int dir)
|
|
|
1074
1075
|
CArray *ca = (CArray *) ap;
|
|
1075
1076
|
|
|
1076
1077
|
if ( dir == CA_XFER_PUT && ca_is_readonly(ca) ) {
|
|
1077
|
-
|
|
1078
|
+
ca_refuse_read_only();
|
|
1078
1079
|
}
|
|
1079
1080
|
|
|
1080
1081
|
/* Fast path: non-CA_OBJECT element type needs no GC protection. */
|
|
@@ -1376,6 +1377,7 @@ ca_xfer_strided_walk (char *src_base,
|
|
|
1376
1377
|
ca_size_t inner_dst_bytes = counts[ndim-2] * counts[ndim-1] * bytes;
|
|
1377
1378
|
|
|
1378
1379
|
for ( k = 0; k < outer_n; k++ ) idx[k] = 0;
|
|
1380
|
+
/* empty: the walk returns on an empty region at its entry */
|
|
1379
1381
|
while ( 1 ) {
|
|
1380
1382
|
ca_size_t soff = 0;
|
|
1381
1383
|
for ( k = 0; k < outer_n; k++ ) soff += idx[k] * src_strides[k];
|
|
@@ -1393,6 +1395,7 @@ ca_xfer_strided_walk (char *src_base,
|
|
|
1393
1395
|
|
|
1394
1396
|
/* prefix axes [0..slab_start-1] on odometer, slab-sized memcpy per iter. */
|
|
1395
1397
|
for ( k = 0; k < slab_start; k++ ) idx[k] = 0;
|
|
1398
|
+
/* empty: the walk returns on an empty region at its entry */
|
|
1396
1399
|
while ( 1 ) {
|
|
1397
1400
|
ca_size_t soff = 0;
|
|
1398
1401
|
for ( k = 0; k < slab_start; k++ ) soff += idx[k] * src_strides[k];
|
|
@@ -1466,6 +1469,7 @@ ca_xfer_stride_dispatch (CArray *ca, ca_size_t *starts, ca_size_t *counts,
|
|
|
1466
1469
|
|
|
1467
1470
|
/* fallback: per-cell via byte offset -> flat addr -> index -> xfer_index. */
|
|
1468
1471
|
for ( k = 0; k < ndim; k++ ) idx[k] = 0;
|
|
1472
|
+
/* empty: its one caller, ca_xfer_stride, returns on an empty region */
|
|
1469
1473
|
while ( 1 ) {
|
|
1470
1474
|
ca_size_t soff = base, vidx[CA_RANK_MAX];
|
|
1471
1475
|
for ( k = 0; k < ndim; k++ ) soff += idx[k] * strides[k];
|
|
@@ -1514,7 +1518,7 @@ ca_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
|
|
|
1514
1518
|
CArray *ca = (CArray *) ap;
|
|
1515
1519
|
|
|
1516
1520
|
if ( dir == CA_XFER_PUT && ca_is_readonly(ca) ) {
|
|
1517
|
-
|
|
1521
|
+
ca_refuse_read_only();
|
|
1518
1522
|
}
|
|
1519
1523
|
|
|
1520
1524
|
if ( ca_region_is_empty(ca->ndim, counts) ) return;
|
|
@@ -1607,9 +1611,7 @@ ca_xfer_all (void *ap, void *data, int dir)
|
|
|
1607
1611
|
written by nested transfers land inside it. */
|
|
1608
1612
|
if ( ca->data_type == CA_OBJECT && dir == CA_XFER_GET ) {
|
|
1609
1613
|
if ( data != (void *) ca->ptr ) {
|
|
1610
|
-
|
|
1611
|
-
ca_size_t i;
|
|
1612
|
-
for ( i = 0; i < ca->elements; i++ ) *p++ = Qnil;
|
|
1614
|
+
ca_object_cells_clear(data, ca->elements);
|
|
1613
1615
|
}
|
|
1614
1616
|
guard = ca_gc_hold_push(data, ca->elements);
|
|
1615
1617
|
}
|
|
@@ -1666,23 +1668,32 @@ static long ca_attached_views = 0;
|
|
|
1666
1668
|
#define CA_VIEW_ATTACH_STEP(ca, d) do { \
|
|
1667
1669
|
uint32_t attach_before_ = CAVIEW(ca)->attach; \
|
|
1668
1670
|
CAVIEW(ca)->attach += (d); \
|
|
1669
|
-
ca_attached_views
|
|
1671
|
+
__atomic_add_fetch(&ca_attached_views, \
|
|
1672
|
+
( CAVIEW(ca)->attach > 0 ) - ( attach_before_ > 0 ), \
|
|
1673
|
+
__ATOMIC_RELAXED); \
|
|
1670
1674
|
} while (0)
|
|
1671
1675
|
|
|
1672
1676
|
static VALUE
|
|
1673
1677
|
rb_ca_s_attached_views (VALUE klass)
|
|
1674
1678
|
{
|
|
1675
|
-
return LONG2NUM(ca_attached_views);
|
|
1679
|
+
return LONG2NUM(__atomic_load_n(&ca_attached_views, __ATOMIC_RELAXED));
|
|
1676
1680
|
}
|
|
1677
1681
|
|
|
1678
1682
|
/* Set once at load from CARRAY_GENERAL_PATH; see carray_internal.h. */
|
|
1679
1683
|
int ca_fast_paths_on = 1;
|
|
1684
|
+
int ca_iter_transfer_only_on = 0;
|
|
1680
1685
|
|
|
1681
1686
|
static VALUE
|
|
1682
1687
|
rb_ca_s_fast_paths_enabled (VALUE klass)
|
|
1683
1688
|
{
|
|
1684
1689
|
return ca_fast_paths_on ? Qtrue : Qfalse;
|
|
1685
1690
|
}
|
|
1691
|
+
|
|
1692
|
+
static VALUE
|
|
1693
|
+
rb_ca_s_iter_by_transfer_only (VALUE klass)
|
|
1694
|
+
{
|
|
1695
|
+
return ca_iter_transfer_only_on ? Qtrue : Qfalse;
|
|
1696
|
+
}
|
|
1686
1697
|
#else
|
|
1687
1698
|
#define CA_VIEW_ATTACH_STEP(ca, d) ( CAVIEW(ca)->attach += (d) )
|
|
1688
1699
|
#endif
|
|
@@ -1753,6 +1764,18 @@ ca_attach_view_first (VALUE arg)
|
|
|
1753
1764
|
return Qnil;
|
|
1754
1765
|
}
|
|
1755
1766
|
|
|
1767
|
+
static VALUE
|
|
1768
|
+
ca_attach_mask_step (VALUE arg)
|
|
1769
|
+
{
|
|
1770
|
+
CArray *ca = (CArray *) arg;
|
|
1771
|
+
CArray *mask0 = ca->mask;
|
|
1772
|
+
ca_update_mask(ca);
|
|
1773
|
+
if ( ca->mask == mask0 ) { /* see ca_attach_view_first */
|
|
1774
|
+
ca_attach(ca->mask);
|
|
1775
|
+
}
|
|
1776
|
+
return Qnil;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1756
1779
|
/* attach parent's data to ca->ptr */
|
|
1757
1780
|
|
|
1758
1781
|
void
|
|
@@ -1797,10 +1820,18 @@ ca_attach (void *ap)
|
|
|
1797
1820
|
}
|
|
1798
1821
|
|
|
1799
1822
|
{
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1823
|
+
/* Making the mask can raise (a CAObject's mask hooks); the attach
|
|
1824
|
+
taken above is given back then. */
|
|
1825
|
+
int tag = 0;
|
|
1826
|
+
rb_protect(ca_attach_mask_step, (VALUE) ca, &tag);
|
|
1827
|
+
if ( tag ) {
|
|
1828
|
+
if ( ca_is_view(ca) ) {
|
|
1829
|
+
CA_VIEW_ATTACH_STEP(ca, -1);
|
|
1830
|
+
}
|
|
1831
|
+
else {
|
|
1832
|
+
ca_func[ca->obj_type].detach(ap);
|
|
1833
|
+
}
|
|
1834
|
+
rb_jump_tag(tag);
|
|
1804
1835
|
}
|
|
1805
1836
|
}
|
|
1806
1837
|
}
|
|
@@ -1887,7 +1918,10 @@ ca_sync_all (CArray **list, int32_t n)
|
|
|
1887
1918
|
even when body raised or broke out -- the cells body wrote reach the
|
|
1888
1919
|
parent, as they would for an entity, which has no separate buffer. An
|
|
1889
1920
|
'a' array is not synced after a raise: allocate did not gather, so its
|
|
1890
|
-
cells body never wrote hold nothing faithful to push back.
|
|
1921
|
+
cells body never wrote hold nothing faithful to push back. Allocate
|
|
1922
|
+
clears the mask as it opens, and an entity (or the parent under a view)
|
|
1923
|
+
has no separate mask to fall back on, so the mask of an 'a' array is
|
|
1924
|
+
copied before it opens and written back when body raises.
|
|
1891
1925
|
|
|
1892
1926
|
Every array is detached, in reverse order, whatever happens. body's
|
|
1893
1927
|
own exception wins over one raised by a sync; when body returned and a
|
|
@@ -1903,8 +1937,57 @@ typedef struct {
|
|
|
1903
1937
|
volatile VALUE sync_err;
|
|
1904
1938
|
VALUE (*body)(VALUE);
|
|
1905
1939
|
VALUE arg;
|
|
1940
|
+
volatile VALUE masks; /* per array: the mask bytes of an 'a' array, or nil */
|
|
1906
1941
|
} ca_window_ctx_t;
|
|
1907
1942
|
|
|
1943
|
+
/* Copy the masks of the 'a' arrays before they open. */
|
|
1944
|
+
static void
|
|
1945
|
+
ca_attach_window_save_masks (ca_window_ctx_t *w)
|
|
1946
|
+
{
|
|
1947
|
+
int32_t k;
|
|
1948
|
+
w->masks = Qnil;
|
|
1949
|
+
for ( k = 0; k < w->n; k++ ) {
|
|
1950
|
+
CArray *ca = w->list[k];
|
|
1951
|
+
if ( w->modes[k] != 'a' || ! ca || ! ca_has_mask(ca) ) {
|
|
1952
|
+
continue;
|
|
1953
|
+
}
|
|
1954
|
+
if ( NIL_P(w->masks) ) {
|
|
1955
|
+
w->masks = rb_ary_new_capa(w->n);
|
|
1956
|
+
}
|
|
1957
|
+
ca_update_mask(ca);
|
|
1958
|
+
{
|
|
1959
|
+
VALUE buf = rb_str_new(NULL, (long) ca->elements);
|
|
1960
|
+
ca_copy_data(ca->mask, RSTRING_PTR(buf));
|
|
1961
|
+
rb_ary_store(w->masks, k, buf);
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
/* Write the copied masks back, once every array is detached. */
|
|
1967
|
+
static void
|
|
1968
|
+
ca_attach_window_restore_masks (ca_window_ctx_t *w)
|
|
1969
|
+
{
|
|
1970
|
+
int32_t k;
|
|
1971
|
+
if ( NIL_P(w->masks) ) {
|
|
1972
|
+
return;
|
|
1973
|
+
}
|
|
1974
|
+
for ( k = 0; k < w->n; k++ ) {
|
|
1975
|
+
VALUE buf = rb_ary_entry(w->masks, k);
|
|
1976
|
+
CArray *ca = w->list[k];
|
|
1977
|
+
if ( NIL_P(buf) ) {
|
|
1978
|
+
continue;
|
|
1979
|
+
}
|
|
1980
|
+
ca_update_mask(ca);
|
|
1981
|
+
if ( ! ca->mask ) {
|
|
1982
|
+
ca_create_mask(ca);
|
|
1983
|
+
}
|
|
1984
|
+
ca_xfer_all(ca->mask, RSTRING_PTR(buf), CA_XFER_PUT);
|
|
1985
|
+
/* held only by w->masks, where compaction can move it; the write may
|
|
1986
|
+
call into Ruby (a CAObject's mask hooks) */
|
|
1987
|
+
RB_GC_GUARD(buf);
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1908
1991
|
static VALUE
|
|
1909
1992
|
ca_attach_window_open (VALUE varg)
|
|
1910
1993
|
{
|
|
@@ -1963,6 +2046,9 @@ ca_attach_window_close (VALUE varg)
|
|
|
1963
2046
|
ca_detach(ca);
|
|
1964
2047
|
}
|
|
1965
2048
|
}
|
|
2049
|
+
if ( ! w->body_done ) {
|
|
2050
|
+
ca_attach_window_restore_masks(w);
|
|
2051
|
+
}
|
|
1966
2052
|
return Qnil;
|
|
1967
2053
|
}
|
|
1968
2054
|
|
|
@@ -1983,6 +2069,7 @@ ca_attach_window (int32_t n, CArray **list, const char *modes,
|
|
|
1983
2069
|
w.sync_err = Qnil;
|
|
1984
2070
|
w.body = body;
|
|
1985
2071
|
w.arg = arg;
|
|
2072
|
+
ca_attach_window_save_masks(&w);
|
|
1986
2073
|
|
|
1987
2074
|
rb_protect(ca_attach_window_open, (VALUE) &w, &tag);
|
|
1988
2075
|
if ( tag ) {
|
|
@@ -1992,6 +2079,7 @@ ca_attach_window (int32_t n, CArray **list, const char *modes,
|
|
|
1992
2079
|
ca_detach(w.list[w.opened]);
|
|
1993
2080
|
}
|
|
1994
2081
|
}
|
|
2082
|
+
ca_attach_window_restore_masks(&w);
|
|
1995
2083
|
rb_jump_tag(tag);
|
|
1996
2084
|
}
|
|
1997
2085
|
|
|
@@ -2004,7 +2092,7 @@ ca_attach_window (int32_t n, CArray **list, const char *modes,
|
|
|
2004
2092
|
return result;
|
|
2005
2093
|
}
|
|
2006
2094
|
|
|
2007
|
-
/*
|
|
2095
|
+
/* re-gather an attached view's buffer from its parent */
|
|
2008
2096
|
|
|
2009
2097
|
void
|
|
2010
2098
|
ca_update (void *ap)
|
|
@@ -2022,7 +2110,7 @@ ca_update (void *ap)
|
|
|
2022
2110
|
}
|
|
2023
2111
|
else {
|
|
2024
2112
|
rb_raise(rb_eRuntimeError,
|
|
2025
|
-
"[BUG] ca_update() called for not-attached
|
|
2113
|
+
"[BUG] ca_update() called for not-attached virtual array");
|
|
2026
2114
|
}
|
|
2027
2115
|
|
|
2028
2116
|
}
|
|
@@ -2048,8 +2136,7 @@ ca_sync (void *ap)
|
|
|
2048
2136
|
}
|
|
2049
2137
|
|
|
2050
2138
|
if ( ca_is_readonly(ca) ) {
|
|
2051
|
-
|
|
2052
|
-
"can not modify read-only array");
|
|
2139
|
+
ca_refuse_read_only();
|
|
2053
2140
|
}
|
|
2054
2141
|
|
|
2055
2142
|
/* Only a mask that exists can hold writes: one made inside the window
|
|
@@ -2259,8 +2346,7 @@ ca_sync_data (void *ap, char *ptr)
|
|
|
2259
2346
|
CArray *ca = (CArray *) ap;
|
|
2260
2347
|
|
|
2261
2348
|
if ( ca_is_readonly(ca) ) {
|
|
2262
|
-
|
|
2263
|
-
"can not sync data to read-only array");
|
|
2349
|
+
ca_refuse_read_only();
|
|
2264
2350
|
}
|
|
2265
2351
|
|
|
2266
2352
|
if ( ca_is_view(ca) && CAVIEW(ca)->nosync ) {
|
|
@@ -2281,8 +2367,7 @@ ca_fill_data (void *ap, void *aptr)
|
|
|
2281
2367
|
char *ptr = (char *) aptr;
|
|
2282
2368
|
|
|
2283
2369
|
if ( ca_is_readonly(ca) ) {
|
|
2284
|
-
|
|
2285
|
-
"can not fill data to read-only array");
|
|
2370
|
+
ca_refuse_read_only();
|
|
2286
2371
|
}
|
|
2287
2372
|
|
|
2288
2373
|
if ( ca_is_view(ca) ) { /* view array */
|
|
@@ -2314,7 +2399,10 @@ ca_fill_stride_default (void *ap, ca_size_t base, int8_t ndim,
|
|
|
2314
2399
|
ca_size_t idx[CA_RANK_MAX];
|
|
2315
2400
|
int8_t k;
|
|
2316
2401
|
|
|
2402
|
+
if ( ca_region_is_empty(ndim, counts) ) return;
|
|
2403
|
+
|
|
2317
2404
|
for ( k = 0; k < ndim; k++ ) idx[k] = 0;
|
|
2405
|
+
/* empty: the function returns on an empty region at its entry */
|
|
2318
2406
|
while ( 1 ) {
|
|
2319
2407
|
ca_size_t addr = base, vidx[CA_RANK_MAX];
|
|
2320
2408
|
for ( k = 0; k < ndim; k++ ) addr += idx[k] * steps[k];
|
|
@@ -2333,7 +2421,7 @@ ca_fill_stride (void *ap, ca_size_t base, int8_t ndim,
|
|
|
2333
2421
|
CArray *ca = (CArray *) ap;
|
|
2334
2422
|
|
|
2335
2423
|
if ( ca_is_readonly(ca) ) {
|
|
2336
|
-
|
|
2424
|
+
ca_refuse_read_only();
|
|
2337
2425
|
}
|
|
2338
2426
|
|
|
2339
2427
|
if ( ca_region_is_empty(ndim, counts) ) return;
|
|
@@ -2429,6 +2517,7 @@ ca_fill_stride_via_addrs (void *ap, ca_size_t base, int8_t ndim,
|
|
|
2429
2517
|
idx[k] = 0;
|
|
2430
2518
|
}
|
|
2431
2519
|
|
|
2520
|
+
/* empty: an axis of count 0 returns above */
|
|
2432
2521
|
while ( 1 ) {
|
|
2433
2522
|
ca_size_t addr = base;
|
|
2434
2523
|
for ( k = 0; k < ndim; k++ ) addr += idx[k] * steps[k];
|
|
@@ -2452,7 +2541,7 @@ ca_fill_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *ptr)
|
|
|
2452
2541
|
CArray *ca = (CArray *) ap;
|
|
2453
2542
|
|
|
2454
2543
|
if ( ca_is_readonly(ca) ) {
|
|
2455
|
-
|
|
2544
|
+
ca_refuse_read_only();
|
|
2456
2545
|
}
|
|
2457
2546
|
|
|
2458
2547
|
if ( ca_func[ca->obj_type].fill_addrs ) {
|
|
@@ -2483,7 +2572,7 @@ ca_fill (void *ap, void *aptr)
|
|
|
2483
2572
|
char *ptr = (char *) aptr;
|
|
2484
2573
|
|
|
2485
2574
|
if ( ca_is_readonly(ca) ) {
|
|
2486
|
-
|
|
2575
|
+
ca_refuse_read_only();
|
|
2487
2576
|
}
|
|
2488
2577
|
|
|
2489
2578
|
ca_fill_data(ap, ptr);
|
|
@@ -2992,9 +3081,12 @@ Init_carray_core (void)
|
|
|
2992
3081
|
{
|
|
2993
3082
|
const char *general = getenv("CARRAY_GENERAL_PATH");
|
|
2994
3083
|
ca_fast_paths_on = !( general && general[0] && strcmp(general, "0") );
|
|
3084
|
+
ca_iter_transfer_only_on = ( general && ! strcmp(general, "2") );
|
|
2995
3085
|
}
|
|
2996
3086
|
rb_define_singleton_method(rb_cCArray, "__fast_paths_enabled__",
|
|
2997
3087
|
rb_ca_s_fast_paths_enabled, 0);
|
|
3088
|
+
rb_define_singleton_method(rb_cCArray, "__iter_by_transfer_only__",
|
|
3089
|
+
rb_ca_s_iter_by_transfer_only, 0);
|
|
2998
3090
|
#endif
|
|
2999
3091
|
|
|
3000
3092
|
rb_define_method(rb_cCArray, "members", rb_ca_members, 0);
|
data/ext/carray_count.c
CHANGED
|
@@ -82,8 +82,16 @@ ca_count_each_body (VALUE arg)
|
|
|
82
82
|
/* Extract vk as a scalar VALUE via flat addr. rb_ca_fetch_addr
|
|
83
83
|
takes a ca_size_t addr (not a Ruby VALUE). */
|
|
84
84
|
VALUE vk = rb_ca_fetch_addr(c->rval, k);
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
VALUE sub;
|
|
86
|
+
/* A masked query cell asks nothing: its count is masked. Passing it
|
|
87
|
+
on would ask count(UNDEF), the count of self's masked cells. */
|
|
88
|
+
if ( vk == CA_UNDEF ) {
|
|
89
|
+
sub = CA_UNDEF;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
inner_argv[0] = vk;
|
|
93
|
+
sub = rb_ca_count(inner_call_argc, inner_argv, c->self);
|
|
94
|
+
}
|
|
87
95
|
|
|
88
96
|
/* Place sub into output[..., k] slot.
|
|
89
97
|
Output stride for v-axis = 1, base stride = v_elements. */
|
|
@@ -169,15 +177,21 @@ rb_ca_count (int argc, VALUE *argv, VALUE self)
|
|
|
169
177
|
rval = ca_face_element_to_storage(self, src, rval);
|
|
170
178
|
}
|
|
171
179
|
else {
|
|
172
|
-
if ( src->obj_type != CA_OBJ_RECORD
|
|
173
|
-
|
|
174
|
-
ca_face_refuse_order("count(v)", self, "count");
|
|
180
|
+
if ( src->obj_type != CA_OBJ_RECORD ) {
|
|
181
|
+
ca_face_require_orderable(self, "count(v)", "count");
|
|
175
182
|
}
|
|
176
|
-
|
|
177
|
-
|
|
183
|
+
{
|
|
184
|
+
volatile VALUE ref = self, query = rval;
|
|
185
|
+
ca_face_reconcile_pair(&ref, &query, "count(v)");
|
|
186
|
+
self = ref;
|
|
187
|
+
rval = query;
|
|
188
|
+
}
|
|
189
|
+
/* The reconcile lifts a scalar query to a length-1 array; unwrap it so
|
|
178
190
|
a scalar query still answers with a count rather than a length-1
|
|
179
|
-
array from the broadcast branch below.
|
|
180
|
-
|
|
191
|
+
array from the broadcast branch below. A Ruby Array is a list of
|
|
192
|
+
queries and stays one, like a CArray query. */
|
|
193
|
+
if ( query_was_scalar && ! RB_TYPE_P(argv[0], T_ARRAY)
|
|
194
|
+
&& rb_obj_is_carray(rval) ) {
|
|
181
195
|
rval = rb_ca_fetch_addr(rval, 0);
|
|
182
196
|
}
|
|
183
197
|
}
|
|
@@ -210,12 +224,11 @@ rb_ca_count (int argc, VALUE *argv, VALUE self)
|
|
|
210
224
|
CArray *cv;
|
|
211
225
|
GetCArray(rval, cv);
|
|
212
226
|
|
|
213
|
-
/*
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (
|
|
217
|
-
|
|
218
|
-
int8_t qt = ca_promote_type(src->data_type, cv->data_type);
|
|
227
|
+
/* The array is counted in the type it and v match in, as `eq` compares
|
|
228
|
+
(ca_query_match_type): each v[k] below is then a value of that type,
|
|
229
|
+
not one cut to the array's. */
|
|
230
|
+
if ( ca_query_match_applies(src, rval) ) {
|
|
231
|
+
int8_t qt = ca_query_match_type(src->data_type, rval);
|
|
219
232
|
if ( qt != src->data_type ) {
|
|
220
233
|
self = rb_ca_wrap_readonly(self, INT2NUM(qt));
|
|
221
234
|
GetCArray(self, src);
|
|
@@ -341,11 +354,28 @@ rb_ca_count (int argc, VALUE *argv, VALUE self)
|
|
|
341
354
|
"count(v) on numeric array: v must be numeric, got %s",
|
|
342
355
|
rb_obj_classname(rval));
|
|
343
356
|
}
|
|
344
|
-
/*
|
|
345
|
-
compares
|
|
346
|
-
|
|
347
|
-
if ( ca_is_integer_type(src) &&
|
|
348
|
-
|
|
357
|
+
/* The query is compared in the type it and the array match in, as `eq`
|
|
358
|
+
compares (ca_query_match_type): 1.5 does not match 1, and an Integer
|
|
359
|
+
the array's type cannot hold matches nothing. */
|
|
360
|
+
if ( ca_is_integer_type(src) && ! RB_INTEGER_TYPE_P(rval)
|
|
361
|
+
&& ! RB_FLOAT_TYPE_P(rval) && ! RB_TYPE_P(rval, T_COMPLEX)
|
|
362
|
+
&& rb_obj_is_kind_of(rval, rb_cNumeric) ) {
|
|
363
|
+
/* A Rational or BigDecimal query: an integral one is the Integer it
|
|
364
|
+
equals; any other is compared as a float, like a Float query,
|
|
365
|
+
rather than cut to the array's type. */
|
|
366
|
+
VALUE ival = rb_funcall(rval, rb_intern("to_i"), 0);
|
|
367
|
+
VALUE *nargv = ALLOCA_N(VALUE, argc);
|
|
368
|
+
MEMCPY(nargv, argv, VALUE, argc);
|
|
369
|
+
nargv[0] = RTEST(rb_funcall(rval, rb_intern("=="), 1, ival))
|
|
370
|
+
? ival : rb_funcall(rval, rb_intern("to_f"), 0);
|
|
371
|
+
argv = nargv;
|
|
372
|
+
rval = nargv[0];
|
|
373
|
+
}
|
|
374
|
+
if ( ca_query_match_applies(src, rval) ) {
|
|
375
|
+
int8_t qt = ca_query_match_type(src->data_type, rval);
|
|
376
|
+
if ( qt != src->data_type ) {
|
|
377
|
+
self = rb_ca_wrap_readonly(self, INT2NUM(qt));
|
|
378
|
+
}
|
|
349
379
|
}
|
|
350
380
|
/* Pass full argv; count_equal_ki pops argv[0] as value_arg. */
|
|
351
381
|
return rb_ca_count_equal_ki(argc, argv, self);
|