carray 3.0.5 → 3.0.6
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 +233 -0
- data/README.md +1 -1
- data/carray.gemspec +3 -2
- data/ext/ca_axis_dispatch.c +6 -0
- data/ext/ca_axis_group.c +1 -1
- data/ext/ca_categorical_iterator.c +7 -7
- data/ext/ca_kernel_iterator.c +116 -231
- data/ext/ca_kernel_iterator.h +410 -254
- data/ext/ca_obj_bincmp.c +9 -1
- data/ext/ca_obj_binop.c +10 -1
- data/ext/ca_obj_bitfield.c +2 -0
- data/ext/ca_obj_byte_swap.c +2 -0
- data/ext/ca_obj_face.c +2 -0
- data/ext/ca_obj_fake.c +3 -1
- data/ext/ca_obj_grid.c +167 -12
- data/ext/ca_obj_meld.c +69 -113
- data/ext/ca_obj_moncmp.c +9 -1
- data/ext/ca_obj_monop.c +40 -2
- data/ext/ca_obj_object.c +54 -20
- data/ext/ca_obj_reduce.c +15 -1
- data/ext/ca_obj_refer.c +65 -20
- data/ext/ca_obj_remap.c +69 -10
- data/ext/ca_obj_roll.c +1 -0
- data/ext/ca_obj_select.c +4 -1
- data/ext/ca_obj_select_axis.c +1 -0
- data/ext/ca_obj_shift.c +1 -0
- data/ext/ca_obj_stack.c +310 -111
- data/ext/ca_obj_stride.c +131 -45
- data/ext/ca_obj_string.c +18 -2
- data/ext/ca_obj_tile.c +1 -0
- data/ext/ca_obj_triop.c +9 -1
- data/ext/ca_obj_window.c +1 -0
- data/ext/ca_op_ipower.c +8 -8
- data/ext/caframe_csv_reader.c +1 -1
- data/ext/carray.h +2 -1
- data/ext/carray_access.c +27 -64
- data/ext/carray_address_basis.c +134 -0
- data/ext/carray_attribute.c +8 -0
- data/ext/carray_broadcast.c +3 -4
- data/ext/carray_build_flags.h +1 -1
- data/ext/carray_cast.c +8 -11
- data/ext/carray_conversion.c +9 -5
- data/ext/carray_core.c +811 -72
- data/ext/carray_element.c +13 -9
- data/ext/carray_histogram.c +25 -3
- data/ext/carray_internal.h +147 -12
- data/ext/carray_kernels_bincmp.c +2007 -3
- data/ext/carray_kernels_binop.c +25365 -5199
- data/ext/carray_kernels_init.c +8 -8
- data/ext/carray_kernels_monop.c +10696 -3678
- data/ext/carray_kernels_reduce_aggregate.c +4429 -702
- data/ext/carray_kernels_reduce_boolean.c +1533 -15
- data/ext/carray_kernels_reduce_cumulative.c +1808 -248
- data/ext/carray_kernels_reduce_extreme.c +4508 -511
- data/ext/carray_kernels_reduce_variance.c +2284 -152
- data/ext/carray_kernels_scan.c +240 -240
- data/ext/carray_kernels_search.c +4211 -264
- data/ext/carray_kernels_sort.c +367 -450
- data/ext/carray_lazy.c +47 -0
- data/ext/carray_loop.c +1 -1
- data/ext/carray_mask.c +130 -139
- data/ext/carray_median_percentile.c +4 -4
- data/ext/carray_memory_view.c +13 -7
- data/ext/carray_operator.c +147 -44
- data/ext/carray_order.c +3 -2
- data/ext/carray_random.c +4 -1
- data/ext/carray_slab.c +97 -21
- data/ext/carray_sort.c +1 -1
- data/ext/carray_test.c +74 -19
- data/ext/carray_time_text.c +2 -2
- data/ext/extconf.rb +11 -0
- data/ext/mkkernel.rb +346 -94
- data/ext/ruby_carray.c +4 -1
- data/ext/version.h +4 -4
- data/lib/carray/attribute.rb +25 -1
- data/lib/carray/autoload_carray.rb +3 -3
- data/lib/carray/autoload_method_extension.rb +5 -3
- data/lib/carray/axis_group.rb +13 -7
- data/lib/carray/basics.rb +1 -1
- data/lib/carray/block_iterator.rb +17 -3
- data/lib/carray/categorical.rb +6 -8
- data/lib/carray/categorical_iterator.rb +2 -2
- data/lib/carray/complex.rb +2 -2
- data/lib/carray/conditional.rb +3 -2
- data/lib/carray/frame/column_verbs.rb +45 -14
- data/lib/carray/frame/convert.rb +19 -0
- data/lib/carray/frame/csv_parser.rb +32 -32
- data/lib/carray/frame/describe.rb +4 -2
- data/lib/carray/frame/frame.rb +399 -78
- data/lib/carray/frame/group.rb +679 -53
- data/lib/carray/frame/io.rb +209 -71
- data/lib/carray/frame/join.rb +146 -37
- data/lib/carray/frame/records.rb +3 -3
- data/lib/carray/frame/reshape.rb +89 -11
- data/lib/carray/frame/sort.rb +57 -9
- data/lib/carray/frame/verbs.rb +384 -49
- data/lib/carray/frame/windows.rb +44 -6
- data/lib/carray/inspect.rb +7 -2
- data/lib/carray/iterator.rb +32 -0
- data/lib/carray/lazy.rb +17 -8
- data/lib/carray/mask_gap_fill.rb +37 -17
- data/lib/carray/meld_reduce.rb +1 -1
- data/lib/carray/methods/choose.rb +3 -2
- data/lib/carray/methods/meshgrid.rb +3 -1
- data/lib/carray/runtime.rb +47 -8
- data/lib/carray/segment_iterator.rb +23 -8
- data/lib/carray/serialize.rb +34 -17
- data/lib/carray/stack.rb +18 -6
- data/lib/carray/time.rb +5 -0
- data/lib/carray/window_iterator.rb +16 -4
- data/yard-stubs/ca_obj_array.rb +26 -1
- data/yard-stubs/carray_cast.rb +8 -5
- data/yard-stubs/carray_conversion.rb +8 -2
- data/yard-stubs/carray_mask.rb +20 -1
- data/yard-stubs/carray_math.rb +31 -16
- data/yard-stubs/carray_memory_view.rb +7 -0
- data/yard-stubs/carray_test.rb +7 -4
- metadata +18 -4
data/ext/carray_core.c
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
#include "carray.h"
|
|
11
11
|
#include "carray_internal.h" /* ca_lazy_arena_enter / _exit */
|
|
12
12
|
#include "ca_iter_substrate.h"
|
|
13
|
+
#include "ca_obj_face.h"
|
|
13
14
|
#include <stdarg.h>
|
|
14
15
|
|
|
15
16
|
/* definition of ca_endian */
|
|
@@ -29,6 +30,10 @@ const rb_data_type_t *ca_mask_typeddata[CA_OBJ_TYPE_MAX];
|
|
|
29
30
|
ca_operation_function_t ca_func[CA_OBJ_TYPE_MAX];
|
|
30
31
|
int ca_obj_num = 0;
|
|
31
32
|
|
|
33
|
+
/* The number of obj_types carray installs for itself, set once
|
|
34
|
+
Init_carray_ext has installed them all; 0 until then. */
|
|
35
|
+
int ca_obj_num_builtin = 0;
|
|
36
|
+
|
|
32
37
|
/*
|
|
33
38
|
definition of validity of each data_type [1 for valid, 0 for invalid]
|
|
34
39
|
|
|
@@ -350,6 +355,14 @@ ca_install_obj_type (VALUE klass,
|
|
|
350
355
|
sizeof(((ca_operation_function_t *) 0)->xfer_all)));
|
|
351
356
|
}
|
|
352
357
|
|
|
358
|
+
/* ca_allocate / ca_attach / ca_sync / ca_detach call these four slots
|
|
359
|
+
without asking, from any path that takes a view's buffer. */
|
|
360
|
+
if ( ! func->allocate || ! func->attach || ! func->sync || ! func->detach ) {
|
|
361
|
+
rb_raise(rb_eArgError,
|
|
362
|
+
"ca_install_obj_type: operation table has no allocate / attach / "
|
|
363
|
+
"sync / detach slot");
|
|
364
|
+
}
|
|
365
|
+
|
|
353
366
|
/* A refused table takes no number. */
|
|
354
367
|
obj_type = ca_obj_num++;
|
|
355
368
|
|
|
@@ -811,75 +824,330 @@ ca_index2addr (void *ap, ca_size_t *idx)
|
|
|
811
824
|
|
|
812
825
|
/* ------------------------------------------------------------------- */
|
|
813
826
|
|
|
814
|
-
/*
|
|
815
|
-
+ ca_fetch_addr, ca_fetch_index,
|
|
816
|
-
+ ca_store_addr, ca_store_index,
|
|
817
|
-
to avoid the system stack error in reference of the object array.
|
|
818
|
-
*/
|
|
827
|
+
/* Recursion over object arrays.
|
|
819
828
|
|
|
820
|
-
|
|
821
|
-
|
|
829
|
+
A cell of an object array can hold the array itself or a view of it.
|
|
830
|
+
Reading such a cell, or computing over the array, would come back into
|
|
831
|
+
the same cells without end. Both are refused when they enter an array
|
|
832
|
+
that the same Ruby method is already reading or computing over, in this
|
|
833
|
+
thread and fiber: rb_exec_recursive keeps that record, as it does for an
|
|
834
|
+
Array that holds itself (Array#flatten, Array#inspect). The array is
|
|
835
|
+
not written to, so a frozen array, or one shared between Ractors, is
|
|
836
|
+
read and computed over as any other.
|
|
837
|
+
|
|
838
|
+
A read, a computation and the evaluation of a lazy expression are
|
|
839
|
+
recorded apart (CA_RECURSION_READ / CA_RECURSION_COMPUTE /
|
|
840
|
+
CA_RECURSION_EVAL): a computation reads the cells it computes over, and
|
|
841
|
+
a reduction over a lazy expression evaluates it. */
|
|
842
|
+
|
|
843
|
+
#define CA_RECURSION_COMPUTE 0
|
|
844
|
+
#define CA_RECURSION_READ 1
|
|
845
|
+
#define CA_RECURSION_EVAL 2
|
|
846
|
+
|
|
847
|
+
static VALUE
|
|
848
|
+
ca_recursion_key (CArray *ca, int kind)
|
|
822
849
|
{
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
850
|
+
/* the struct of an array is aligned, so the low two bits are free */
|
|
851
|
+
return ULL2NUM(((unsigned long long) (uintptr_t) ca) | (unsigned long long) kind);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
NORETURN(static void ca_refuse_cyclic (int kind));
|
|
855
|
+
|
|
856
|
+
static void
|
|
857
|
+
ca_refuse_cyclic (int kind)
|
|
858
|
+
{
|
|
859
|
+
if ( kind == CA_RECURSION_COMPUTE ) {
|
|
860
|
+
rb_raise(rb_eArgError, "%s: cyclic reference is not allowed in CArray",
|
|
861
|
+
ca_calling_method_name());
|
|
862
|
+
}
|
|
863
|
+
rb_raise(rb_eArgError, "cyclic reference is not allowed in CArray");
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
typedef struct {
|
|
867
|
+
int n;
|
|
868
|
+
CArray **ops;
|
|
869
|
+
int kind; /* CA_RECURSION_COMPUTE or CA_RECURSION_EVAL */
|
|
870
|
+
int k;
|
|
871
|
+
VALUE (*body)(VALUE);
|
|
872
|
+
VALUE arg;
|
|
873
|
+
} ca_computing_t;
|
|
874
|
+
|
|
875
|
+
static VALUE ca_computing_next (ca_computing_t *c);
|
|
876
|
+
|
|
877
|
+
static VALUE
|
|
878
|
+
ca_computing_step (VALUE key, VALUE varg, int recursive)
|
|
879
|
+
{
|
|
880
|
+
(void) key;
|
|
881
|
+
if ( recursive ) {
|
|
882
|
+
ca_refuse_cyclic(CA_RECURSION_COMPUTE); /* names the method */
|
|
883
|
+
}
|
|
884
|
+
return ca_computing_next((ca_computing_t *) varg);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
static VALUE
|
|
888
|
+
ca_computing_next (ca_computing_t *c)
|
|
889
|
+
{
|
|
890
|
+
while ( c->k < c->n ) {
|
|
891
|
+
CArray *ca = c->ops[c->k++];
|
|
892
|
+
int j, seen = 0;
|
|
893
|
+
if ( ! ca || ( c->kind == CA_RECURSION_COMPUTE && ! ca_is_object_type(ca) ) ) continue;
|
|
894
|
+
for ( j = 0; j < c->k - 1; j++ ) {
|
|
895
|
+
if ( c->ops[j] == ca ) seen = 1;
|
|
828
896
|
}
|
|
829
|
-
|
|
897
|
+
if ( seen ) continue;
|
|
898
|
+
return rb_exec_recursive(ca_computing_step,
|
|
899
|
+
ca_recursion_key(ca, c->kind),
|
|
900
|
+
(VALUE) c);
|
|
830
901
|
}
|
|
902
|
+
return c->body(c->arg);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
static VALUE ca_recursion_probe (VALUE key, VALUE arg, int recursive);
|
|
906
|
+
|
|
907
|
+
/* Whether ca is, or reaches through its parents, a lazy node being
|
|
908
|
+
evaluated (recorded by ca_computing_run_nodes). A cell value that does
|
|
909
|
+
would come back into that evaluation when it is computed. The walk is
|
|
910
|
+
bounded; past the bound the value is not refused here, and is caught when
|
|
911
|
+
it is evaluated, as a deeper expression is. */
|
|
912
|
+
static int
|
|
913
|
+
ca_reaches_evaluating_walk (CArray *ca, int *budget)
|
|
914
|
+
{
|
|
915
|
+
if ( --(*budget) < 0 ) {
|
|
916
|
+
return 0;
|
|
917
|
+
}
|
|
918
|
+
if ( RTEST(rb_exec_recursive(ca_recursion_probe,
|
|
919
|
+
ca_recursion_key(ca, CA_RECURSION_EVAL),
|
|
920
|
+
Qnil)) ) {
|
|
921
|
+
return 1;
|
|
922
|
+
}
|
|
923
|
+
if ( ca_is_entity(ca) ) {
|
|
924
|
+
return 0;
|
|
925
|
+
}
|
|
926
|
+
if ( ca_test_flag(ca, CA_FLAG_MULTI_PARENTS) ) {
|
|
927
|
+
CAMultiParent *mp = (CAMultiParent *) ca;
|
|
928
|
+
int32_t k;
|
|
929
|
+
for ( k = 0; k < mp->n_parents; k++ ) {
|
|
930
|
+
if ( mp->parents[k] && ca_reaches_evaluating_walk(mp->parents[k], budget) ) {
|
|
931
|
+
return 1;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
return 0;
|
|
935
|
+
}
|
|
936
|
+
return ((CAView *) ca)->parent
|
|
937
|
+
? ca_reaches_evaluating_walk(((CAView *) ca)->parent, budget)
|
|
938
|
+
: 0;
|
|
831
939
|
}
|
|
832
940
|
|
|
833
941
|
void
|
|
834
|
-
|
|
942
|
+
ca_refuse_cells_reaching_evaluation (ca_size_t n, const VALUE *cells)
|
|
835
943
|
{
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
944
|
+
ca_size_t i;
|
|
945
|
+
for ( i = 0; i < n; i++ ) {
|
|
946
|
+
if ( rb_obj_is_carray(cells[i]) ) {
|
|
947
|
+
CArray *cv;
|
|
948
|
+
int budget = 1024;
|
|
949
|
+
TypedData_Get_Struct(cells[i], CArray, &carray_data_type, cv);
|
|
950
|
+
if ( ! ca_is_entity(cv) && ca_reaches_evaluating_walk(cv, &budget) ) {
|
|
951
|
+
ca_refuse_cyclic(CA_RECURSION_COMPUTE);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
/* Runs body(arg), a computation over ops[0..n), with each object array
|
|
958
|
+
among them recorded as computed over. Raises when one of them is
|
|
959
|
+
already being computed over by the same method: a cell's value, or an
|
|
960
|
+
element's operation, led the computation back into it. */
|
|
961
|
+
VALUE
|
|
962
|
+
ca_computing_run (int n, CArray **ops, VALUE (*body)(VALUE), VALUE arg)
|
|
963
|
+
{
|
|
964
|
+
ca_computing_t c;
|
|
965
|
+
c.n = n; c.ops = ops; c.kind = CA_RECURSION_COMPUTE; c.k = 0; c.body = body; c.arg = arg;
|
|
966
|
+
return ca_computing_next(&c);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
/* As ca_computing_run, for the nodes of a lazy expression evaluated over
|
|
970
|
+
object cells: every array of ops[0..n) is recorded whatever its data type
|
|
971
|
+
(a comparison is boolean), apart from computations, so a reduction over
|
|
972
|
+
the expression evaluating it is no re-entry. */
|
|
973
|
+
VALUE
|
|
974
|
+
ca_computing_run_nodes (int n, CArray **ops, VALUE (*body)(VALUE), VALUE arg)
|
|
975
|
+
{
|
|
976
|
+
ca_computing_t c;
|
|
977
|
+
c.n = n; c.ops = ops; c.kind = CA_RECURSION_EVAL; c.k = 0; c.body = body; c.arg = arg;
|
|
978
|
+
return ca_computing_next(&c);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
static VALUE
|
|
982
|
+
ca_recursion_probe (VALUE key, VALUE arg, int recursive)
|
|
983
|
+
{
|
|
984
|
+
(void) key; (void) arg;
|
|
985
|
+
return recursive ? Qtrue : Qfalse;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
static VALUE
|
|
989
|
+
ca_refuse_reentry_step (VALUE self, VALUE name, int recursive)
|
|
990
|
+
{
|
|
991
|
+
(void) self;
|
|
992
|
+
if ( recursive ) {
|
|
993
|
+
rb_raise(rb_eArgError, "%" PRIsVALUE ": cyclic reference is not allowed in CArray",
|
|
994
|
+
name);
|
|
840
995
|
}
|
|
996
|
+
return rb_yield(Qnil);
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
/* refuse_reentry(name) { ... } -- runs the block, refusing as cyclic a
|
|
1000
|
+
second call for the same array from inside it (rb_exec_recursive, by
|
|
1001
|
+
the receiver). Ruby code that walks the cells of an object array and
|
|
1002
|
+
comes back to the array through a cell, such as the nested dump of a
|
|
1003
|
+
view, goes through this. */
|
|
1004
|
+
static VALUE
|
|
1005
|
+
rb_ca_refuse_reentry (VALUE self, VALUE name)
|
|
1006
|
+
{
|
|
1007
|
+
return rb_exec_recursive(ca_refuse_reentry_step, self, name);
|
|
841
1008
|
}
|
|
842
1009
|
|
|
1010
|
+
/* A cell read that delivers an array being read refuses it: the array the
|
|
1011
|
+
cell was read from (an entity is not recorded, see ca_with_cyclic_check)
|
|
1012
|
+
or one recorded further out. */
|
|
843
1013
|
void
|
|
844
|
-
ca_test_cyclic_check(void *ap, void *ptr)
|
|
1014
|
+
ca_test_cyclic_check (void *ap, void *ptr)
|
|
845
1015
|
{
|
|
846
1016
|
CArray *ca = (CArray *) ap;
|
|
847
|
-
|
|
848
|
-
/* ca.data_type == CA_OBJECT */
|
|
849
1017
|
if ( ca_is_object_type(ca) ) {
|
|
850
|
-
VALUE rval = *(VALUE*) ptr;
|
|
1018
|
+
VALUE rval = *(VALUE *) ptr;
|
|
851
1019
|
if ( rb_obj_is_carray(rval) ) {
|
|
852
1020
|
CArray *cv;
|
|
853
1021
|
TypedData_Get_Struct(rval, CArray, &carray_data_type, cv);
|
|
854
|
-
if (
|
|
855
|
-
|
|
1022
|
+
if ( cv == ca ||
|
|
1023
|
+
RTEST(rb_exec_recursive(ca_recursion_probe,
|
|
1024
|
+
ca_recursion_key(cv, CA_RECURSION_READ),
|
|
1025
|
+
Qnil)) ) {
|
|
1026
|
+
ca_refuse_cyclic(CA_RECURSION_READ);
|
|
856
1027
|
}
|
|
857
1028
|
}
|
|
858
1029
|
}
|
|
859
1030
|
}
|
|
860
1031
|
|
|
861
|
-
/*
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
and cannot raise, so it runs unprotected. */
|
|
1032
|
+
/* Refuse to write into an object array a value that is a CArray sharing
|
|
1033
|
+
its cells -- the array itself or a view of it. Reading such a cell back
|
|
1034
|
+
is refused as cyclic (ca_test_cyclic_check); refusing the write where it
|
|
1035
|
+
passes through here reports the mistake where it is made. A write that
|
|
1036
|
+
reaches the cells another way is still caught when the cell is read.
|
|
1037
|
+
data holds n VALUEs. */
|
|
868
1038
|
static void
|
|
869
|
-
|
|
1039
|
+
ca_refuse_storing_self (CArray *ca, const void *data, ca_size_t n)
|
|
870
1040
|
{
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
1041
|
+
const char *d = (const char *) data;
|
|
1042
|
+
ca_size_t i;
|
|
1043
|
+
for (i=0; i<n; i++) {
|
|
1044
|
+
VALUE rval;
|
|
1045
|
+
memcpy(&rval, d + i * sizeof(VALUE), sizeof(VALUE));
|
|
1046
|
+
if ( rb_obj_is_carray(rval) ) {
|
|
1047
|
+
CArray *cv;
|
|
1048
|
+
TypedData_Get_Struct(rval, CArray, &carray_data_type, cv);
|
|
1049
|
+
if ( ca_shares_root(ca, cv) ) {
|
|
1050
|
+
rb_raise(rb_eArgError, "%s: can not store an object array into itself",
|
|
1051
|
+
ca_calling_method_name());
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
875
1054
|
}
|
|
876
|
-
|
|
877
|
-
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
typedef struct {
|
|
1058
|
+
VALUE (*body)(VALUE);
|
|
1059
|
+
VALUE arg;
|
|
1060
|
+
} ca_reading_t;
|
|
1061
|
+
|
|
1062
|
+
static VALUE
|
|
1063
|
+
ca_reading_step (VALUE key, VALUE varg, int recursive)
|
|
1064
|
+
{
|
|
1065
|
+
ca_reading_t *r = (ca_reading_t *) varg;
|
|
1066
|
+
(void) key;
|
|
1067
|
+
if ( recursive ) {
|
|
1068
|
+
ca_refuse_cyclic(CA_RECURSION_READ);
|
|
878
1069
|
}
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
1070
|
+
return r->body(r->arg);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
static char ca_obj_moves_cells[CA_OBJ_TYPE_MAX];
|
|
1074
|
+
|
|
1075
|
+
void
|
|
1076
|
+
ca_register_cell_moving_view (int8_t obj_type)
|
|
1077
|
+
{
|
|
1078
|
+
ca_obj_moves_cells[(uint8_t) obj_type] = 1;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/* Whether the transfers of a Face are the forwarders of ca_obj_face.c, which
|
|
1082
|
+
hand the request to the parent unchanged. A Face with transfer slots of
|
|
1083
|
+
its own (one defined in Ruby on CAObject, or by an extension) may run Ruby
|
|
1084
|
+
and answers no. */
|
|
1085
|
+
static int
|
|
1086
|
+
ca_face_forwards_transfers (CArray *ca)
|
|
1087
|
+
{
|
|
1088
|
+
ca_operation_function_t *f = &ca_func[ca->obj_type];
|
|
1089
|
+
return f->xfer_index == ca_face_xfer_index &&
|
|
1090
|
+
f->xfer_addrs == ca_face_xfer_addrs &&
|
|
1091
|
+
f->xfer_stride == ca_face_xfer_stride &&
|
|
1092
|
+
f->xfer_all == ca_face_xfer_all;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
/* Whether a transfer of ca reaches entities through cell-moving views only:
|
|
1096
|
+
then it calls no Ruby on the way. An unknown view, one that computes its
|
|
1097
|
+
cells (CAObject, a lazy expression) or converts them, answers no. */
|
|
1098
|
+
static int
|
|
1099
|
+
ca_transfer_calls_no_ruby (CArray *ca, int depth)
|
|
1100
|
+
{
|
|
1101
|
+
while ( ! ca_is_entity(ca) ) {
|
|
1102
|
+
if ( depth++ > 64 ) {
|
|
1103
|
+
return 0;
|
|
1104
|
+
}
|
|
1105
|
+
if ( ca_test_flag(ca, CA_FLAG_MULTI_PARENTS) ) {
|
|
1106
|
+
CAMultiParent *mp = (CAMultiParent *) ca;
|
|
1107
|
+
int32_t k;
|
|
1108
|
+
if ( ! ca_obj_moves_cells[(uint8_t) ca->obj_type] ) {
|
|
1109
|
+
return 0;
|
|
1110
|
+
}
|
|
1111
|
+
for ( k = 0; k < mp->n_parents; k++ ) {
|
|
1112
|
+
if ( ! mp->parents[k] ||
|
|
1113
|
+
! ca_transfer_calls_no_ruby(mp->parents[k], depth) ) {
|
|
1114
|
+
return 0;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
return 1;
|
|
1118
|
+
}
|
|
1119
|
+
if ( ca_is_face(ca) ? ! ca_face_forwards_transfers(ca)
|
|
1120
|
+
: ! ( ca_is_stride_family(ca) ||
|
|
1121
|
+
ca_obj_moves_cells[(uint8_t) ca->obj_type] ) ) {
|
|
1122
|
+
return 0;
|
|
1123
|
+
}
|
|
1124
|
+
ca = ((CAView *) ca)->parent;
|
|
1125
|
+
if ( ! ca ) {
|
|
1126
|
+
return 0;
|
|
1127
|
+
}
|
|
882
1128
|
}
|
|
1129
|
+
return 1;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
/* Runs body(arg) -- one transfer of an object cell or cells -- with ca
|
|
1133
|
+
recorded as being read. Entering a transfer of ca again from inside it
|
|
1134
|
+
is refused as cyclic. A transfer of an entity, or of views that only move
|
|
1135
|
+
cells down to entities, copies VALUEs and calls no Ruby, so it cannot be
|
|
1136
|
+
entered again; it is not recorded, and a cell holding the array itself is
|
|
1137
|
+
caught by ca_test_cyclic_check. */
|
|
1138
|
+
static void
|
|
1139
|
+
ca_with_cyclic_check (CArray *ca, int dir, VALUE (*body)(VALUE), VALUE arg)
|
|
1140
|
+
{
|
|
1141
|
+
ca_reading_t r;
|
|
1142
|
+
(void) dir;
|
|
1143
|
+
if ( ca_transfer_calls_no_ruby(ca, 0) ) {
|
|
1144
|
+
body(arg);
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
r.body = body;
|
|
1148
|
+
r.arg = arg;
|
|
1149
|
+
rb_exec_recursive(ca_reading_step, ca_recursion_key(ca, CA_RECURSION_READ),
|
|
1150
|
+
(VALUE) &r);
|
|
883
1151
|
}
|
|
884
1152
|
|
|
885
1153
|
/* ------------------------------------------------------------------- */
|
|
@@ -912,6 +1180,58 @@ ca_store_addr (void *ap, ca_size_t addr, void *pval)
|
|
|
912
1180
|
public C-API for external ext gems). Every view supplies an
|
|
913
1181
|
xfer_index slot. */
|
|
914
1182
|
|
|
1183
|
+
/* ---- A write through a view, tried first against a stack's stand-ins ----
|
|
1184
|
+
(ca_multi_parent_value_trial_below) */
|
|
1185
|
+
|
|
1186
|
+
typedef struct {
|
|
1187
|
+
int kind;
|
|
1188
|
+
CArray *ca;
|
|
1189
|
+
ca_size_t n;
|
|
1190
|
+
ca_size_t *addrs;
|
|
1191
|
+
ca_size_t *starts;
|
|
1192
|
+
ca_size_t *counts;
|
|
1193
|
+
ca_size_t *strides;
|
|
1194
|
+
void *data;
|
|
1195
|
+
ca_size_t base;
|
|
1196
|
+
int8_t ndim;
|
|
1197
|
+
ca_size_t *steps;
|
|
1198
|
+
} ca_value_trial_put_t;
|
|
1199
|
+
|
|
1200
|
+
enum { CA_TRIAL_ADDRS, CA_TRIAL_STRIDE, CA_TRIAL_ALL, CA_TRIAL_FILL,
|
|
1201
|
+
CA_TRIAL_FILL_STRIDE, CA_TRIAL_FILL_ADDRS, CA_TRIAL_SYNC };
|
|
1202
|
+
|
|
1203
|
+
static VALUE
|
|
1204
|
+
ca_value_trial_put_body (VALUE arg)
|
|
1205
|
+
{
|
|
1206
|
+
ca_value_trial_put_t *a = (ca_value_trial_put_t *) arg;
|
|
1207
|
+
switch ( a->kind ) {
|
|
1208
|
+
case CA_TRIAL_ADDRS:
|
|
1209
|
+
ca_xfer_addrs(a->ca, a->n, a->addrs, a->data, CA_XFER_PUT); break;
|
|
1210
|
+
case CA_TRIAL_STRIDE:
|
|
1211
|
+
ca_xfer_stride(a->ca, a->starts, a->counts, a->strides, a->data, CA_XFER_PUT); break;
|
|
1212
|
+
case CA_TRIAL_ALL:
|
|
1213
|
+
ca_xfer_all(a->ca, a->data, CA_XFER_PUT); break;
|
|
1214
|
+
case CA_TRIAL_FILL:
|
|
1215
|
+
ca_fill_data(a->ca, a->data); break;
|
|
1216
|
+
case CA_TRIAL_FILL_STRIDE:
|
|
1217
|
+
ca_fill_stride(a->ca, a->base, a->ndim, a->counts, a->steps, a->data); break;
|
|
1218
|
+
case CA_TRIAL_FILL_ADDRS:
|
|
1219
|
+
ca_fill_addrs(a->ca, a->n, a->addrs, a->data); break;
|
|
1220
|
+
case CA_TRIAL_SYNC:
|
|
1221
|
+
ca_func[a->ca->obj_type].sync(a->ca); break;
|
|
1222
|
+
}
|
|
1223
|
+
return Qnil;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
#define CA_VALUE_TRIAL_PUT(...) \
|
|
1227
|
+
do { \
|
|
1228
|
+
ca_value_trial_put_t a_ = { __VA_ARGS__ }; \
|
|
1229
|
+
CArray *m_ = ca_multi_parent_value_trial_below(a_.ca); \
|
|
1230
|
+
if ( m_ != NULL ) { \
|
|
1231
|
+
ca_multi_parent_value_trial_through(m_, ca_value_trial_put_body, (VALUE) &a_); \
|
|
1232
|
+
} \
|
|
1233
|
+
} while (0)
|
|
1234
|
+
|
|
915
1235
|
static inline void
|
|
916
1236
|
ca_xfer_index_dispatch (CArray *ca, ca_size_t *idx, void *data, int dir)
|
|
917
1237
|
{
|
|
@@ -950,6 +1270,7 @@ ca_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
|
|
|
950
1270
|
ca_refuse_read_only();
|
|
951
1271
|
}
|
|
952
1272
|
|
|
1273
|
+
|
|
953
1274
|
/* Fast path: non-CA_OBJECT element type needs no GC protection. */
|
|
954
1275
|
if ( ca->data_type != CA_OBJECT ) {
|
|
955
1276
|
ca_xfer_index_dispatch(ca, idx, data, dir);
|
|
@@ -957,6 +1278,9 @@ ca_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
|
|
|
957
1278
|
}
|
|
958
1279
|
|
|
959
1280
|
/* CA_OBJECT slow path: cyclic check. */
|
|
1281
|
+
if ( dir == CA_XFER_PUT ) {
|
|
1282
|
+
ca_refuse_storing_self(ca, data, 1);
|
|
1283
|
+
}
|
|
960
1284
|
{
|
|
961
1285
|
struct ca_xfer_index_args args;
|
|
962
1286
|
args.ca = ca;
|
|
@@ -1030,7 +1354,27 @@ ca_xfer_addrs_dispatch (CArray *ca, ca_size_t n, ca_size_t *addrs,
|
|
|
1030
1354
|
return;
|
|
1031
1355
|
}
|
|
1032
1356
|
/* Per-cell loop for arbitrary (non-sequential) addrs:
|
|
1033
|
-
fancy gather/scatter from CASelect 2-D mapper, CSA sparse mask, etc.
|
|
1357
|
+
fancy gather/scatter from CASelect 2-D mapper, CSA sparse mask, etc.
|
|
1358
|
+
Cells of 1, 2, 4 or 8 bytes are copied with that length as a
|
|
1359
|
+
constant, which the compiler turns into one load and store (and
|
|
1360
|
+
which stays legal for an unaligned buffer); a length known only at
|
|
1361
|
+
run time is a call per cell. */
|
|
1362
|
+
#define CA_XFER_ADDRS_AS(W) \
|
|
1363
|
+
do { \
|
|
1364
|
+
if ( dir == CA_XFER_GET ) { \
|
|
1365
|
+
for (i=0; i<n; i++) memcpy(d + i * (W), ca->ptr + (W) * addrs[i], (W)); \
|
|
1366
|
+
} \
|
|
1367
|
+
else { \
|
|
1368
|
+
for (i=0; i<n; i++) memcpy(ca->ptr + (W) * addrs[i], d + i * (W), (W)); \
|
|
1369
|
+
} \
|
|
1370
|
+
} while (0)
|
|
1371
|
+
switch ( ca->bytes ) {
|
|
1372
|
+
case 1: CA_XFER_ADDRS_AS(1); return;
|
|
1373
|
+
case 2: CA_XFER_ADDRS_AS(2); return;
|
|
1374
|
+
case 4: CA_XFER_ADDRS_AS(4); return;
|
|
1375
|
+
case 8: CA_XFER_ADDRS_AS(8); return;
|
|
1376
|
+
}
|
|
1377
|
+
#undef CA_XFER_ADDRS_AS
|
|
1034
1378
|
for ( i = 0; i < n; i++ ) {
|
|
1035
1379
|
char *p = ca->ptr + ca->bytes * addrs[i];
|
|
1036
1380
|
if ( dir == CA_XFER_GET ) memcpy(d + i * ca->bytes, p, ca->bytes);
|
|
@@ -1078,6 +1422,11 @@ ca_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *data, int dir)
|
|
|
1078
1422
|
ca_refuse_read_only();
|
|
1079
1423
|
}
|
|
1080
1424
|
|
|
1425
|
+
if ( dir == CA_XFER_PUT && ca->ptr == NULL ) {
|
|
1426
|
+
CA_VALUE_TRIAL_PUT(CA_TRIAL_ADDRS, ca, n, addrs, NULL, NULL, NULL, data);
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
|
|
1081
1430
|
/* Fast path: non-CA_OBJECT element type needs no GC protection. */
|
|
1082
1431
|
if ( ca->data_type != CA_OBJECT ) {
|
|
1083
1432
|
ca_xfer_addrs_dispatch(ca, n, addrs, data, dir);
|
|
@@ -1085,6 +1434,9 @@ ca_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *data, int dir)
|
|
|
1085
1434
|
}
|
|
1086
1435
|
|
|
1087
1436
|
/* CA_OBJECT slow path: cyclic check. */
|
|
1437
|
+
if ( dir == CA_XFER_PUT ) {
|
|
1438
|
+
ca_refuse_storing_self(ca, data, n);
|
|
1439
|
+
}
|
|
1088
1440
|
{
|
|
1089
1441
|
struct ca_xfer_addrs_args args;
|
|
1090
1442
|
args.ca = ca;
|
|
@@ -1440,7 +1792,6 @@ ca_xfer_stride_dispatch (CArray *ca, ca_size_t *starts, ca_size_t *counts,
|
|
|
1440
1792
|
int8_t ndim = ca->ndim;
|
|
1441
1793
|
ca_size_t native[CA_RANK_MAX];
|
|
1442
1794
|
ca_size_t base = 0;
|
|
1443
|
-
ca_size_t doff = 0;
|
|
1444
1795
|
ca_size_t s;
|
|
1445
1796
|
int8_t k;
|
|
1446
1797
|
ca_size_t idx[CA_RANK_MAX];
|
|
@@ -1467,18 +1818,27 @@ ca_xfer_stride_dispatch (CArray *ca, ca_size_t *starts, ca_size_t *counts,
|
|
|
1467
1818
|
return;
|
|
1468
1819
|
}
|
|
1469
1820
|
|
|
1470
|
-
/* fallback
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
k = ndim
|
|
1480
|
-
|
|
1481
|
-
|
|
1821
|
+
/* fallback for a type without xfer_stride: the region's cells by address,
|
|
1822
|
+
handed to xfer_addrs in one call (data is packed over counts, as
|
|
1823
|
+
xfer_addrs takes it). */
|
|
1824
|
+
{
|
|
1825
|
+
volatile VALUE holder = 0;
|
|
1826
|
+
ca_size_t n = 1, i = 0;
|
|
1827
|
+
ca_size_t *addrs;
|
|
1828
|
+
for ( k = 0; k < ndim; k++ ) n *= counts[k];
|
|
1829
|
+
addrs = ALLOCV_N(ca_size_t, holder, n);
|
|
1830
|
+
for ( k = 0; k < ndim; k++ ) idx[k] = 0;
|
|
1831
|
+
/* empty: its one caller, ca_xfer_stride, returns on an empty region */
|
|
1832
|
+
while ( 1 ) {
|
|
1833
|
+
ca_size_t soff = base;
|
|
1834
|
+
for ( k = 0; k < ndim; k++ ) soff += idx[k] * strides[k];
|
|
1835
|
+
addrs[i++] = soff / ca->bytes;
|
|
1836
|
+
k = ndim - 1;
|
|
1837
|
+
while ( k >= 0 ) { if ( ++idx[k] < counts[k] ) break; idx[k] = 0; k--; }
|
|
1838
|
+
if ( k < 0 ) break;
|
|
1839
|
+
}
|
|
1840
|
+
ca_xfer_addrs_dispatch(ca, n, addrs, d, dir);
|
|
1841
|
+
ALLOCV_END(holder);
|
|
1482
1842
|
}
|
|
1483
1843
|
}
|
|
1484
1844
|
|
|
@@ -1523,11 +1883,22 @@ ca_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
|
|
|
1523
1883
|
|
|
1524
1884
|
if ( ca_region_is_empty(ca->ndim, counts) ) return;
|
|
1525
1885
|
|
|
1886
|
+
if ( dir == CA_XFER_PUT && ca->ptr == NULL ) {
|
|
1887
|
+
CA_VALUE_TRIAL_PUT(CA_TRIAL_STRIDE, ca, 0, NULL, starts, counts, strides, data);
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
|
|
1526
1891
|
if ( ca->data_type != CA_OBJECT ) {
|
|
1527
1892
|
ca_xfer_stride_dispatch(ca, starts, counts, strides, data, dir);
|
|
1528
1893
|
return;
|
|
1529
1894
|
}
|
|
1530
1895
|
|
|
1896
|
+
if ( dir == CA_XFER_PUT ) {
|
|
1897
|
+
ca_size_t n = 1;
|
|
1898
|
+
int8_t k;
|
|
1899
|
+
for (k=0; k<ca->ndim; k++) n *= counts[k];
|
|
1900
|
+
ca_refuse_storing_self(ca, data, n);
|
|
1901
|
+
}
|
|
1531
1902
|
{
|
|
1532
1903
|
struct ca_xfer_stride_args args;
|
|
1533
1904
|
args.ca = ca; args.starts = starts; args.counts = counts;
|
|
@@ -1588,6 +1959,9 @@ ca_xfer_all (void *ap, void *data, int dir)
|
|
|
1588
1959
|
if ( ca->elements == 0 ) {
|
|
1589
1960
|
return;
|
|
1590
1961
|
}
|
|
1962
|
+
if ( dir == CA_XFER_PUT && ca->ptr == NULL ) {
|
|
1963
|
+
CA_VALUE_TRIAL_PUT(CA_TRIAL_ALL, ca, 0, NULL, NULL, NULL, NULL, data);
|
|
1964
|
+
}
|
|
1591
1965
|
/* Universal arena lifetime hook. ca_xfer_all is the single universal
|
|
1592
1966
|
entry for materialise (to_ca -> ca_copy -> ca_copy_data ->
|
|
1593
1967
|
ca_xfer_all), so wrapping here covers every outermost view type,
|
|
@@ -1679,14 +2053,111 @@ rb_ca_s_attached_views (VALUE klass)
|
|
|
1679
2053
|
return LONG2NUM(__atomic_load_n(&ca_attached_views, __ATOMIC_RELAXED));
|
|
1680
2054
|
}
|
|
1681
2055
|
|
|
1682
|
-
/* Set once at load from CARRAY_GENERAL_PATH; see
|
|
2056
|
+
/* Set once at load from CARRAY_GENERAL_PATH and CARRAY_SHORTCUTS_OFF; see
|
|
2057
|
+
carray_internal.h. */
|
|
1683
2058
|
int ca_fast_paths_on = 1;
|
|
1684
2059
|
int ca_iter_transfer_only_on = 0;
|
|
2060
|
+
unsigned char ca_shortcut_off[CA_SC_COUNT];
|
|
2061
|
+
|
|
2062
|
+
/* In the order of enum ca_shortcut. */
|
|
2063
|
+
static const char *ca_shortcut_names[CA_SC_COUNT] = {
|
|
2064
|
+
"shift_whole_view",
|
|
2065
|
+
"stack",
|
|
2066
|
+
"stack_outer_k",
|
|
2067
|
+
"fiber_fused_conversion",
|
|
2068
|
+
"per_slab_hoist",
|
|
2069
|
+
"protect_only_may_raise",
|
|
2070
|
+
"stack_addrs_k0",
|
|
2071
|
+
"stack_stride_box",
|
|
2072
|
+
"stack_stride_reloc",
|
|
2073
|
+
"stack_parents_k0",
|
|
2074
|
+
"stack_fold",
|
|
2075
|
+
"meld_stride_box",
|
|
2076
|
+
"meld_parents_axis0",
|
|
2077
|
+
"median_partition",
|
|
2078
|
+
"percentile_partition",
|
|
2079
|
+
"loop_interchange",
|
|
2080
|
+
"stack_loop_interchange",
|
|
2081
|
+
"lazy_streaming_reduce",
|
|
2082
|
+
"short_fiber_reduce",
|
|
2083
|
+
"meld_reduce",
|
|
2084
|
+
"segment_nunique",
|
|
2085
|
+
"segment_scan_kernel",
|
|
2086
|
+
"segment_edge_kernel",
|
|
2087
|
+
"segment_reduce_kernel",
|
|
2088
|
+
"segment_all_any_kernel",
|
|
2089
|
+
"categorical_group_by_index",
|
|
2090
|
+
"axis_group_count_kernel",
|
|
2091
|
+
"axis_group_whole_block",
|
|
2092
|
+
"axis_group_reduce_kernel",
|
|
2093
|
+
"axis_group_scan_kernel",
|
|
2094
|
+
"window_fold_by_offset",
|
|
2095
|
+
};
|
|
1685
2096
|
|
|
2097
|
+
static int
|
|
2098
|
+
ca_shortcut_lookup (const char *name, long len)
|
|
2099
|
+
{
|
|
2100
|
+
int i;
|
|
2101
|
+
for (i=0; i<CA_SC_COUNT; i++) {
|
|
2102
|
+
if ( (long) strlen(ca_shortcut_names[i]) == len
|
|
2103
|
+
&& ! strncmp(ca_shortcut_names[i], name, len) ) {
|
|
2104
|
+
return i;
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
return -1;
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
/* CARRAY_SHORTCUTS_OFF is a comma-separated list of names. A name it does
|
|
2111
|
+
not know is warned about, so a misspelt one does not leave the shortcut
|
|
2112
|
+
on unnoticed. */
|
|
2113
|
+
static void
|
|
2114
|
+
ca_shortcuts_off_from_env (void)
|
|
2115
|
+
{
|
|
2116
|
+
const char *p = getenv("CARRAY_SHORTCUTS_OFF");
|
|
2117
|
+
if ( ! p ) {
|
|
2118
|
+
return;
|
|
2119
|
+
}
|
|
2120
|
+
while ( *p ) {
|
|
2121
|
+
const char *q = strchr(p, ',');
|
|
2122
|
+
long len = q ? (long) (q - p) : (long) strlen(p);
|
|
2123
|
+
if ( len > 0 ) {
|
|
2124
|
+
int id = ca_shortcut_lookup(p, len);
|
|
2125
|
+
if ( id < 0 ) {
|
|
2126
|
+
rb_warn("CARRAY_SHORTCUTS_OFF: unknown shortcut %.*s", (int) len, p);
|
|
2127
|
+
}
|
|
2128
|
+
else {
|
|
2129
|
+
ca_shortcut_off[id] = 1;
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
if ( ! q ) {
|
|
2133
|
+
break;
|
|
2134
|
+
}
|
|
2135
|
+
p = q + 1;
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2139
|
+
/* CArray.__fast_paths_enabled__ answers whether the fast paths are on;
|
|
2140
|
+
with a name, whether that one is. */
|
|
1686
2141
|
static VALUE
|
|
1687
|
-
rb_ca_s_fast_paths_enabled (VALUE klass)
|
|
2142
|
+
rb_ca_s_fast_paths_enabled (int argc, VALUE *argv, VALUE klass)
|
|
1688
2143
|
{
|
|
1689
|
-
|
|
2144
|
+
VALUE rname;
|
|
2145
|
+
int id;
|
|
2146
|
+
|
|
2147
|
+
rb_scan_args(argc, argv, "01", &rname);
|
|
2148
|
+
if ( NIL_P(rname) ) {
|
|
2149
|
+
return ca_fast_paths_on ? Qtrue : Qfalse;
|
|
2150
|
+
}
|
|
2151
|
+
if ( SYMBOL_P(rname) ) {
|
|
2152
|
+
rname = rb_sym2str(rname);
|
|
2153
|
+
}
|
|
2154
|
+
StringValue(rname);
|
|
2155
|
+
id = ca_shortcut_lookup(RSTRING_PTR(rname), RSTRING_LEN(rname));
|
|
2156
|
+
if ( id < 0 ) {
|
|
2157
|
+
rb_raise(rb_eArgError, "__fast_paths_enabled__: unknown shortcut %"PRIsVALUE,
|
|
2158
|
+
rname);
|
|
2159
|
+
}
|
|
2160
|
+
return ca_fast_path(id) ? Qtrue : Qfalse;
|
|
1690
2161
|
}
|
|
1691
2162
|
|
|
1692
2163
|
static VALUE
|
|
@@ -1880,27 +2351,37 @@ ca_sync_all_step (VALUE arg)
|
|
|
1880
2351
|
return Qnil;
|
|
1881
2352
|
}
|
|
1882
2353
|
|
|
2354
|
+
/* Syncs `ca`, carrying on past a raise: the first exception is kept in
|
|
2355
|
+
*error (left as it is when one is already there), to be raised once
|
|
2356
|
+
the others have been synced. A throw or break cannot wait, and goes at
|
|
2357
|
+
once. */
|
|
2358
|
+
void
|
|
2359
|
+
ca_sync_carry_on (CArray *ca, volatile VALUE *error)
|
|
2360
|
+
{
|
|
2361
|
+
int tag = 0;
|
|
2362
|
+
rb_protect(ca_sync_all_step, (VALUE) ca, &tag);
|
|
2363
|
+
if ( tag ) {
|
|
2364
|
+
VALUE err = rb_errinfo();
|
|
2365
|
+
if ( ! RTEST(rb_obj_is_kind_of(err, rb_eException)) ) {
|
|
2366
|
+
rb_jump_tag(tag);
|
|
2367
|
+
}
|
|
2368
|
+
if ( NIL_P(*error) ) {
|
|
2369
|
+
*error = err;
|
|
2370
|
+
}
|
|
2371
|
+
rb_set_errinfo(Qnil);
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
|
|
1883
2375
|
/* Syncs every one of list[0..n-1] in turn, carrying on past one that
|
|
1884
2376
|
raises so that the others still receive what was written, then raises
|
|
1885
|
-
the first exception.
|
|
2377
|
+
the first exception. */
|
|
1886
2378
|
void
|
|
1887
2379
|
ca_sync_all (CArray **list, int32_t n)
|
|
1888
2380
|
{
|
|
1889
2381
|
volatile VALUE error = Qnil;
|
|
1890
2382
|
int32_t k;
|
|
1891
2383
|
for ( k = 0; k < n; k++ ) {
|
|
1892
|
-
|
|
1893
|
-
rb_protect(ca_sync_all_step, (VALUE) list[k], &tag);
|
|
1894
|
-
if ( tag ) {
|
|
1895
|
-
VALUE err = rb_errinfo();
|
|
1896
|
-
if ( ! RTEST(rb_obj_is_kind_of(err, rb_eException)) ) {
|
|
1897
|
-
rb_jump_tag(tag);
|
|
1898
|
-
}
|
|
1899
|
-
if ( NIL_P(error) ) {
|
|
1900
|
-
error = err;
|
|
1901
|
-
}
|
|
1902
|
-
rb_set_errinfo(Qnil);
|
|
1903
|
-
}
|
|
2384
|
+
ca_sync_carry_on(list[k], &error);
|
|
1904
2385
|
}
|
|
1905
2386
|
if ( ! NIL_P(error) ) {
|
|
1906
2387
|
rb_exc_raise(error);
|
|
@@ -2092,6 +2573,234 @@ ca_attach_window (int32_t n, CArray **list, const char *modes,
|
|
|
2092
2573
|
return result;
|
|
2093
2574
|
}
|
|
2094
2575
|
|
|
2576
|
+
/* ---- A window whose Fiber is abandoned ------------------------------------
|
|
2577
|
+
|
|
2578
|
+
A window that yields to a block is closed by rb_ensure. Ruby does not
|
|
2579
|
+
run the ensure of a Fiber that is suspended and then collected -- an
|
|
2580
|
+
Enumerator stopped with #next and dropped is one -- so such a window
|
|
2581
|
+
would stay open for good: a view keeps its attach, and a source that
|
|
2582
|
+
publishes its ptr only while held (a CASource) keeps it published and
|
|
2583
|
+
stops checking its backend.
|
|
2584
|
+
|
|
2585
|
+
The guard closes it from a finalizer instead. The window's state lives
|
|
2586
|
+
on the heap, owned by the guard; a sentinel object that only the
|
|
2587
|
+
window's frame refers to carries a finalizer that holds the guard. When
|
|
2588
|
+
the Fiber is collected the sentinel goes with it, and the finalizer --
|
|
2589
|
+
which runs outside GC, where Ruby may be called -- runs `cleanup`. A
|
|
2590
|
+
window that closes normally disarms the guard first.
|
|
2591
|
+
|
|
2592
|
+
`mark` marks the VALUEs `arg` holds, which nothing else does once the
|
|
2593
|
+
frame that referred to them is gone. The guard frees `arg` with xfree. */
|
|
2594
|
+
|
|
2595
|
+
typedef struct {
|
|
2596
|
+
void (*cleanup) (void *arg);
|
|
2597
|
+
void (*mark) (void *arg);
|
|
2598
|
+
void *arg;
|
|
2599
|
+
int armed;
|
|
2600
|
+
} ca_window_guard_t;
|
|
2601
|
+
|
|
2602
|
+
static void
|
|
2603
|
+
ca_window_guard_mark (void *p)
|
|
2604
|
+
{
|
|
2605
|
+
ca_window_guard_t *g = (ca_window_guard_t *) p;
|
|
2606
|
+
if ( g->arg && g->mark ) {
|
|
2607
|
+
g->mark(g->arg);
|
|
2608
|
+
}
|
|
2609
|
+
}
|
|
2610
|
+
|
|
2611
|
+
static void
|
|
2612
|
+
ca_window_guard_free (void *p)
|
|
2613
|
+
{
|
|
2614
|
+
ca_window_guard_t *g = (ca_window_guard_t *) p;
|
|
2615
|
+
xfree(g->arg);
|
|
2616
|
+
xfree(g);
|
|
2617
|
+
}
|
|
2618
|
+
|
|
2619
|
+
static const rb_data_type_t ca_window_guard_data_type = {
|
|
2620
|
+
.wrap_struct_name = "CArrayWindowGuard",
|
|
2621
|
+
.function = {
|
|
2622
|
+
.dmark = ca_window_guard_mark,
|
|
2623
|
+
.dfree = ca_window_guard_free,
|
|
2624
|
+
.dsize = NULL,
|
|
2625
|
+
},
|
|
2626
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
|
2627
|
+
};
|
|
2628
|
+
|
|
2629
|
+
static VALUE
|
|
2630
|
+
ca_window_guard_cleanup_step (VALUE gv)
|
|
2631
|
+
{
|
|
2632
|
+
ca_window_guard_t *g;
|
|
2633
|
+
TypedData_Get_Struct(gv, ca_window_guard_t, &ca_window_guard_data_type, g);
|
|
2634
|
+
g->cleanup(g->arg);
|
|
2635
|
+
return Qnil;
|
|
2636
|
+
}
|
|
2637
|
+
|
|
2638
|
+
/* The finalizer is the guard itself, called with the sentinel's object id.
|
|
2639
|
+
A proc made here would capture the frame it is made in -- inside the
|
|
2640
|
+
Fiber -- and keep the Fiber alive through the finalizer table. */
|
|
2641
|
+
static VALUE rb_cCAWindowGuard;
|
|
2642
|
+
|
|
2643
|
+
static VALUE
|
|
2644
|
+
ca_window_guard_call (VALUE gv, VALUE object_id)
|
|
2645
|
+
{
|
|
2646
|
+
ca_window_guard_t *g;
|
|
2647
|
+
int tag = 0;
|
|
2648
|
+
(void) object_id;
|
|
2649
|
+
TypedData_Get_Struct(gv, ca_window_guard_t, &ca_window_guard_data_type, g);
|
|
2650
|
+
if ( g->armed && g->cleanup ) {
|
|
2651
|
+
g->armed = 0;
|
|
2652
|
+
rb_protect(ca_window_guard_cleanup_step, gv, &tag);
|
|
2653
|
+
if ( tag ) {
|
|
2654
|
+
rb_set_errinfo(Qnil);
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
return Qnil;
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2660
|
+
/* Takes ownership of arg (allocated with xmalloc). Returns the guard and
|
|
2661
|
+
stores the sentinel; the caller keeps both on its frame (RB_GC_GUARD)
|
|
2662
|
+
for as long as the window may be open. */
|
|
2663
|
+
VALUE
|
|
2664
|
+
ca_window_guard_arm (void (*cleanup)(void *), void (*mark)(void *), void *arg,
|
|
2665
|
+
VALUE *sentinel)
|
|
2666
|
+
{
|
|
2667
|
+
ca_window_guard_t *g;
|
|
2668
|
+
volatile VALUE gv, sv;
|
|
2669
|
+
gv = TypedData_Make_Struct(rb_cCAWindowGuard, ca_window_guard_t,
|
|
2670
|
+
&ca_window_guard_data_type, g);
|
|
2671
|
+
g->cleanup = cleanup;
|
|
2672
|
+
g->mark = mark;
|
|
2673
|
+
g->arg = arg;
|
|
2674
|
+
g->armed = 1;
|
|
2675
|
+
sv = rb_class_new_instance(0, NULL, rb_cObject);
|
|
2676
|
+
rb_define_finalizer(sv, gv);
|
|
2677
|
+
*sentinel = sv;
|
|
2678
|
+
return gv;
|
|
2679
|
+
}
|
|
2680
|
+
|
|
2681
|
+
/* Called first thing when the window closes normally, so that cleanup
|
|
2682
|
+
cannot also run from the finalizer. */
|
|
2683
|
+
void
|
|
2684
|
+
ca_window_guard_disarm (VALUE gv, VALUE sentinel)
|
|
2685
|
+
{
|
|
2686
|
+
ca_window_guard_t *g;
|
|
2687
|
+
TypedData_Get_Struct(gv, ca_window_guard_t, &ca_window_guard_data_type, g);
|
|
2688
|
+
g->armed = 0;
|
|
2689
|
+
/* rb_undefine_finalizer is declared in the headers but not exported by
|
|
2690
|
+
every libruby (3.4.1 lacks it), so go through ObjectSpace. */
|
|
2691
|
+
rb_funcall(rb_const_get(rb_cObject, rb_intern("ObjectSpace")),
|
|
2692
|
+
rb_intern("undefine_finalizer"), 1, sentinel);
|
|
2693
|
+
}
|
|
2694
|
+
|
|
2695
|
+
/* ca_attach_window for a window that yields to a block, closed by the
|
|
2696
|
+
guard above if its Fiber is abandoned. The context, the list and the
|
|
2697
|
+
modes are copied to the heap, since the caller's frame goes with the
|
|
2698
|
+
Fiber; `owners` keeps the arrays in list alive until then. */
|
|
2699
|
+
|
|
2700
|
+
typedef struct {
|
|
2701
|
+
ca_window_ctx_t w;
|
|
2702
|
+
VALUE owners;
|
|
2703
|
+
CArray *list[CA_RANK_MAX];
|
|
2704
|
+
char modes[CA_RANK_MAX + 1];
|
|
2705
|
+
} ca_guarded_window_t;
|
|
2706
|
+
|
|
2707
|
+
static void
|
|
2708
|
+
ca_guarded_window_mark (void *p)
|
|
2709
|
+
{
|
|
2710
|
+
ca_guarded_window_t *gw = (ca_guarded_window_t *) p;
|
|
2711
|
+
rb_gc_mark(gw->owners);
|
|
2712
|
+
rb_gc_mark(gw->w.masks);
|
|
2713
|
+
rb_gc_mark(gw->w.sync_err);
|
|
2714
|
+
}
|
|
2715
|
+
|
|
2716
|
+
static void
|
|
2717
|
+
ca_guarded_window_cleanup (void *p)
|
|
2718
|
+
{
|
|
2719
|
+
ca_guarded_window_t *gw = (ca_guarded_window_t *) p;
|
|
2720
|
+
ca_attach_window_close((VALUE) &gw->w);
|
|
2721
|
+
}
|
|
2722
|
+
|
|
2723
|
+
typedef struct {
|
|
2724
|
+
ca_guarded_window_t *gw;
|
|
2725
|
+
VALUE guard;
|
|
2726
|
+
VALUE sentinel;
|
|
2727
|
+
} ca_guarded_window_run_t;
|
|
2728
|
+
|
|
2729
|
+
static VALUE
|
|
2730
|
+
ca_guarded_window_close (VALUE varg)
|
|
2731
|
+
{
|
|
2732
|
+
ca_guarded_window_run_t *r = (ca_guarded_window_run_t *) varg;
|
|
2733
|
+
ca_window_guard_disarm(r->guard, r->sentinel);
|
|
2734
|
+
return ca_attach_window_close((VALUE) &r->gw->w);
|
|
2735
|
+
}
|
|
2736
|
+
|
|
2737
|
+
VALUE
|
|
2738
|
+
ca_attach_window_yielding (VALUE owners, int32_t n, CArray **list,
|
|
2739
|
+
const char *modes, VALUE (*body)(VALUE), VALUE arg)
|
|
2740
|
+
{
|
|
2741
|
+
ca_guarded_window_t *gw;
|
|
2742
|
+
ca_guarded_window_run_t r;
|
|
2743
|
+
volatile VALUE guard, sentinel;
|
|
2744
|
+
VALUE result;
|
|
2745
|
+
int tag = 0;
|
|
2746
|
+
|
|
2747
|
+
if ( n < 1 || n > CA_RANK_MAX ) { /* rank-check: a count of arrays, bounded by gw->list */
|
|
2748
|
+
rb_raise(rb_eRuntimeError, "[BUG] ca_attach_window_yielding: %d arrays", (int) n);
|
|
2749
|
+
}
|
|
2750
|
+
gw = ZALLOC(ca_guarded_window_t);
|
|
2751
|
+
gw->owners = owners;
|
|
2752
|
+
memcpy(gw->list, list, sizeof(CArray *) * (size_t) n);
|
|
2753
|
+
memcpy(gw->modes, modes, (size_t) n);
|
|
2754
|
+
gw->w.n = n;
|
|
2755
|
+
gw->w.list = gw->list;
|
|
2756
|
+
gw->w.modes = gw->modes;
|
|
2757
|
+
gw->w.opened = 0;
|
|
2758
|
+
gw->w.body_done = 0;
|
|
2759
|
+
gw->w.sync_tag = 0;
|
|
2760
|
+
gw->w.sync_err = Qnil;
|
|
2761
|
+
gw->w.body = body;
|
|
2762
|
+
gw->w.arg = arg;
|
|
2763
|
+
gw->w.masks = Qnil;
|
|
2764
|
+
guard = ca_window_guard_arm(NULL, ca_guarded_window_mark, gw, (VALUE *) &sentinel);
|
|
2765
|
+
ca_attach_window_save_masks(&gw->w);
|
|
2766
|
+
|
|
2767
|
+
rb_protect(ca_attach_window_open, (VALUE) &gw->w, &tag);
|
|
2768
|
+
if ( tag ) {
|
|
2769
|
+
ca_window_guard_disarm(guard, sentinel);
|
|
2770
|
+
while ( gw->w.opened > 0 ) {
|
|
2771
|
+
gw->w.opened--;
|
|
2772
|
+
if ( gw->w.list[gw->w.opened] ) {
|
|
2773
|
+
ca_detach(gw->w.list[gw->w.opened]);
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
ca_attach_window_restore_masks(&gw->w);
|
|
2777
|
+
rb_jump_tag(tag);
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2780
|
+
/* Only now is there something for the finalizer to close. */
|
|
2781
|
+
{
|
|
2782
|
+
ca_window_guard_t *g;
|
|
2783
|
+
TypedData_Get_Struct(guard, ca_window_guard_t, &ca_window_guard_data_type, g);
|
|
2784
|
+
g->cleanup = ca_guarded_window_cleanup;
|
|
2785
|
+
}
|
|
2786
|
+
|
|
2787
|
+
r.gw = gw;
|
|
2788
|
+
r.guard = guard;
|
|
2789
|
+
r.sentinel = sentinel;
|
|
2790
|
+
result = rb_ensure(ca_attach_window_body, (VALUE) &gw->w,
|
|
2791
|
+
ca_guarded_window_close, (VALUE) &r);
|
|
2792
|
+
if ( gw->w.sync_tag && RB_TYPE_P(gw->w.sync_err, T_OBJECT) &&
|
|
2793
|
+
RTEST(rb_obj_is_kind_of(gw->w.sync_err, rb_eException)) ) {
|
|
2794
|
+
VALUE err = gw->w.sync_err;
|
|
2795
|
+
RB_GC_GUARD(guard);
|
|
2796
|
+
RB_GC_GUARD(sentinel);
|
|
2797
|
+
rb_exc_raise(err);
|
|
2798
|
+
}
|
|
2799
|
+
RB_GC_GUARD(guard);
|
|
2800
|
+
RB_GC_GUARD(sentinel);
|
|
2801
|
+
return result;
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2095
2804
|
/* re-gather an attached view's buffer from its parent */
|
|
2096
2805
|
|
|
2097
2806
|
void
|
|
@@ -2139,6 +2848,12 @@ ca_sync (void *ap)
|
|
|
2139
2848
|
ca_refuse_read_only();
|
|
2140
2849
|
}
|
|
2141
2850
|
|
|
2851
|
+
/* Before the mask is synced: a refused write leaves the mask as well. */
|
|
2852
|
+
if ( ca_is_view(ca) && ! CAVIEW(ca)->nosync ) {
|
|
2853
|
+
CA_VALUE_TRIAL_PUT(CA_TRIAL_SYNC, ca, 0, NULL, NULL, NULL, NULL, NULL);
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
|
|
2142
2857
|
/* Only a mask that exists can hold writes: one made inside the window
|
|
2143
2858
|
is attached with the view (ca_create_mask). Making one here would
|
|
2144
2859
|
read the parent's mask while the window closes, and a parent that
|
|
@@ -2370,6 +3085,11 @@ ca_fill_data (void *ap, void *aptr)
|
|
|
2370
3085
|
ca_refuse_read_only();
|
|
2371
3086
|
}
|
|
2372
3087
|
|
|
3088
|
+
if ( ca->ptr == NULL ) {
|
|
3089
|
+
CA_VALUE_TRIAL_PUT(CA_TRIAL_FILL, ca, 0, NULL, NULL, NULL, NULL, ptr);
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
|
|
2373
3093
|
if ( ca_is_view(ca) ) { /* view array */
|
|
2374
3094
|
if ( ca_is_attached(ca) ) { /* ca is to be attached */
|
|
2375
3095
|
ca_func[CA_OBJ_ARRAY].fill_data(ap, ptr);
|
|
@@ -2426,6 +3146,12 @@ ca_fill_stride (void *ap, ca_size_t base, int8_t ndim,
|
|
|
2426
3146
|
|
|
2427
3147
|
if ( ca_region_is_empty(ndim, counts) ) return;
|
|
2428
3148
|
|
|
3149
|
+
if ( ca->ptr == NULL ) {
|
|
3150
|
+
CA_VALUE_TRIAL_PUT(CA_TRIAL_FILL_STRIDE, ca, 0, NULL, NULL, counts, NULL, ptr,
|
|
3151
|
+
base, ndim, steps);
|
|
3152
|
+
}
|
|
3153
|
+
|
|
3154
|
+
|
|
2429
3155
|
if ( ca_func[ca->obj_type].fill_stride ) {
|
|
2430
3156
|
ca_func[ca->obj_type].fill_stride(ap, base, ndim, counts, steps, ptr);
|
|
2431
3157
|
return;
|
|
@@ -2544,6 +3270,11 @@ ca_fill_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *ptr)
|
|
|
2544
3270
|
ca_refuse_read_only();
|
|
2545
3271
|
}
|
|
2546
3272
|
|
|
3273
|
+
if ( ca->ptr == NULL ) {
|
|
3274
|
+
CA_VALUE_TRIAL_PUT(CA_TRIAL_FILL_ADDRS, ca, n, addrs, NULL, NULL, NULL, ptr);
|
|
3275
|
+
}
|
|
3276
|
+
|
|
3277
|
+
|
|
2547
3278
|
if ( ca_func[ca->obj_type].fill_addrs ) {
|
|
2548
3279
|
ca_func[ca->obj_type].fill_addrs(ap, n, addrs, ptr);
|
|
2549
3280
|
return;
|
|
@@ -3067,6 +3798,13 @@ Init_carray_core (void)
|
|
|
3067
3798
|
|
|
3068
3799
|
ca_init_obj_type();
|
|
3069
3800
|
|
|
3801
|
+
rb_define_private_method(rb_cCArray, "refuse_reentry", rb_ca_refuse_reentry, 1);
|
|
3802
|
+
|
|
3803
|
+
rb_cCAWindowGuard = rb_define_class_under(rb_cCArray, "WindowGuard", rb_cObject);
|
|
3804
|
+
rb_undef_alloc_func(rb_cCAWindowGuard);
|
|
3805
|
+
rb_undef_method(CLASS_OF(rb_cCAWindowGuard), "new");
|
|
3806
|
+
rb_define_method(rb_cCAWindowGuard, "call", ca_window_guard_call, 1);
|
|
3807
|
+
|
|
3070
3808
|
#ifdef CARRAY_DEV_BUILD
|
|
3071
3809
|
rb_define_singleton_method(rb_cCArray, "attach", rb_ca_s_attach, -1);
|
|
3072
3810
|
rb_define_singleton_method(rb_cCArray, "attach!", rb_ca_s_attach_bang, -1);
|
|
@@ -3085,9 +3823,10 @@ Init_carray_core (void)
|
|
|
3085
3823
|
const char *general = getenv("CARRAY_GENERAL_PATH");
|
|
3086
3824
|
ca_fast_paths_on = !( general && general[0] && strcmp(general, "0") );
|
|
3087
3825
|
ca_iter_transfer_only_on = ( general && ! strcmp(general, "2") );
|
|
3826
|
+
ca_shortcuts_off_from_env();
|
|
3088
3827
|
}
|
|
3089
3828
|
rb_define_singleton_method(rb_cCArray, "__fast_paths_enabled__",
|
|
3090
|
-
rb_ca_s_fast_paths_enabled,
|
|
3829
|
+
rb_ca_s_fast_paths_enabled, -1);
|
|
3091
3830
|
rb_define_singleton_method(rb_cCArray, "__iter_by_transfer_only__",
|
|
3092
3831
|
rb_ca_s_iter_by_transfer_only, 0);
|
|
3093
3832
|
#endif
|