carray 2.0.0 → 3.0.0
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/.yardopts +5 -25
- data/CHANGELOG.md +16 -0
- data/LICENSE +1 -1
- data/NEWS.md +3 -0
- data/README.md +128 -44
- data/carray.gemspec +22 -24
- data/ext/ca_array_pool.c +91 -0
- data/ext/ca_axis_descriptor.h +186 -0
- data/ext/ca_axis_dispatch.c +924 -0
- data/ext/ca_axis_group.c +1208 -0
- data/ext/ca_bincmp_dispatch.c +76 -0
- data/ext/ca_bincmp_dispatch.h +85 -0
- data/ext/ca_binop_dispatch.c +125 -0
- data/ext/ca_binop_dispatch.h +159 -0
- data/ext/ca_categorical_iterator.c +1375 -0
- data/ext/ca_compare.c +94 -0
- data/ext/ca_compare.h +26 -0
- data/ext/ca_composite_dispatch.c +414 -0
- data/ext/ca_composite_dispatch.h +116 -0
- data/ext/ca_for_buffer.h +96 -0
- data/ext/ca_for_each_element.h +241 -0
- data/ext/ca_group_iter.c +304 -0
- data/ext/ca_iter_substrate.h +325 -0
- data/ext/ca_kernel_iterator.c +4321 -0
- data/ext/ca_kernel_iterator.h +2603 -0
- data/ext/ca_moncmp_dispatch.c +37 -0
- data/ext/ca_moncmp_dispatch.h +62 -0
- data/ext/ca_monop_dispatch.c +200 -0
- data/ext/ca_monop_dispatch.h +235 -0
- data/ext/ca_obj_array.c +355 -359
- data/ext/ca_obj_bincmp.c +809 -0
- data/ext/ca_obj_binop.c +892 -0
- data/ext/ca_obj_bitarray.c +369 -164
- data/ext/ca_obj_bitfield.c +294 -234
- data/ext/ca_obj_block.c +189 -711
- data/ext/ca_obj_byte_swap.c +766 -0
- data/ext/ca_obj_const_string.c +965 -0
- data/ext/ca_obj_face.c +670 -0
- data/ext/ca_obj_face.h +247 -0
- data/ext/ca_obj_fake.c +228 -100
- data/ext/ca_obj_farray.c +54 -441
- data/ext/ca_obj_field.c +82 -529
- data/ext/ca_obj_fixlen_string.c +306 -0
- data/ext/ca_obj_grid.c +858 -440
- data/ext/ca_obj_meld.c +1034 -0
- data/ext/ca_obj_moncmp.c +569 -0
- data/ext/ca_obj_monop.c +1111 -0
- data/ext/ca_obj_object.c +774 -298
- data/ext/ca_obj_record.c +468 -0
- data/ext/ca_obj_reduce.c +97 -82
- data/ext/ca_obj_refer.c +569 -459
- data/ext/ca_obj_remap.c +475 -0
- data/ext/ca_obj_repeat.c +92 -477
- data/ext/ca_obj_roll.c +616 -0
- data/ext/ca_obj_select.c +344 -296
- data/ext/ca_obj_select_axis.c +1296 -0
- data/ext/ca_obj_shift.c +230 -792
- data/ext/ca_obj_source.c +78 -0
- data/ext/ca_obj_stack.c +1173 -0
- data/ext/ca_obj_stride.c +2501 -0
- data/ext/ca_obj_string.c +268 -0
- data/ext/ca_obj_tile.c +614 -0
- data/ext/ca_obj_time.c +546 -0
- data/ext/ca_obj_timedelta.c +435 -0
- data/ext/ca_obj_transpose.c +62 -516
- data/ext/ca_obj_triop.c +746 -0
- data/ext/ca_obj_unbound_repeat.c +208 -241
- data/ext/ca_obj_window.c +1131 -563
- data/ext/ca_op_byte_swap.c +175 -0
- data/ext/ca_op_ipower.c +319 -0
- data/ext/ca_op_powi.h +88 -0
- data/ext/ca_sort_kernels.h +132 -0
- data/ext/ca_sweep_engine.c +430 -0
- data/ext/ca_sweep_engine.h +157 -0
- data/ext/ca_transform_common.c +228 -0
- data/ext/ca_triop_dispatch.c +55 -0
- data/ext/ca_triop_dispatch.h +62 -0
- data/ext/carray.h +795 -402
- data/ext/carray_access.c +831 -711
- data/ext/carray_attribute.c +98 -330
- data/ext/carray_bincount.c +255 -0
- data/ext/carray_broadcast.c +283 -0
- data/ext/carray_call_cfunc.c +1360 -828
- data/ext/carray_call_cfunc.h +160 -0
- data/ext/carray_cast.c +1212 -301
- data/ext/carray_cast_func.rb +81 -40
- data/ext/carray_class.c +53 -63
- data/ext/carray_config.h +28 -0
- data/ext/carray_conversion.c +350 -346
- data/ext/carray_copy.c +156 -268
- data/ext/carray_core.c +1342 -199
- data/ext/carray_count.c +312 -0
- data/ext/carray_data_type.c +43 -19
- data/ext/carray_element.c +585 -213
- data/ext/carray_factorize.c +2542 -0
- data/ext/carray_generate.c +230 -559
- data/ext/carray_histogram.c +490 -0
- data/ext/carray_hold.c +228 -0
- data/ext/carray_index_classifier.c +1035 -0
- data/ext/carray_index_classifier.h +27 -0
- data/ext/carray_internal.h +120 -0
- data/ext/carray_kernels_bincmp.c +4445 -0
- data/ext/carray_kernels_binop.c +10979 -0
- data/ext/carray_kernels_init.c +36 -0
- data/ext/carray_kernels_map.c +3466 -0
- data/ext/carray_kernels_moncmp.c +2096 -0
- data/ext/carray_kernels_monop.c +18312 -0
- data/ext/carray_kernels_reduce_aggregate.c +25836 -0
- data/ext/carray_kernels_reduce_boolean.c +329 -0
- data/ext/carray_kernels_reduce_cumulative.c +14592 -0
- data/ext/carray_kernels_reduce_extreme.c +16947 -0
- data/ext/carray_kernels_reduce_variance.c +3909 -0
- data/ext/carray_kernels_scan.c +3692 -0
- data/ext/carray_kernels_search.c +32137 -0
- data/ext/carray_kernels_sort.c +10625 -0
- data/ext/carray_kernels_triop.c +1391 -0
- data/ext/carray_lazy.c +567 -0
- data/ext/carray_loop.c +88 -200
- data/ext/carray_mask.c +848 -154
- data/ext/carray_math_kernel.h +120 -0
- data/ext/carray_mathfunc.c +10 -241
- data/ext/carray_median_percentile.c +1257 -0
- data/ext/carray_memory_view.c +1625 -0
- data/ext/carray_operator.c +1526 -318
- data/ext/carray_order.c +664 -1394
- data/ext/carray_partition.c +416 -0
- data/ext/carray_random.c +518 -0
- data/ext/carray_scatter.c +357 -0
- data/ext/carray_slab.c +1219 -0
- data/ext/carray_slab.h +84 -0
- data/ext/carray_sort.c +829 -0
- data/ext/carray_sort_kernel.c +620 -0
- data/ext/carray_struct.c +695 -0
- data/ext/carray_test.c +343 -229
- data/ext/carray_undef.c +34 -17
- data/ext/carray_utils.c +175 -74
- data/ext/extconf.rb +216 -55
- data/ext/mk_call_cfunc.rb +480 -0
- data/ext/mkkernel.rb +8842 -0
- data/ext/ruby_carray.c +202 -101
- data/ext/version.h +4 -14
- data/ext/version.rb +5 -13
- data/lib/carray/arrow_tensor.rb +401 -0
- data/lib/carray/attribute.rb +166 -0
- data/lib/carray/autoload_carray.rb +220 -0
- data/lib/carray/autoload_method_extension.rb +44 -0
- data/lib/carray/axis_group.rb +711 -0
- data/lib/carray/basics.rb +481 -0
- data/lib/carray/bincount_nd.rb +358 -0
- data/lib/carray/block_iterator.rb +604 -0
- data/lib/carray/boolean_reduce.rb +109 -0
- data/lib/carray/categorical.rb +561 -0
- data/lib/carray/categorical_iterator.rb +1062 -0
- data/lib/carray/complex.rb +150 -0
- data/lib/carray/conditional.rb +216 -0
- data/lib/carray/const_string.rb +228 -0
- data/lib/carray/construct.rb +139 -328
- data/lib/carray/core_extensions.rb +240 -0
- data/lib/carray/data_type_extension.rb +233 -0
- data/lib/carray/fixlen_string.rb +95 -0
- data/lib/carray/frame/concat.rb +132 -0
- data/lib/carray/frame/convert.rb +95 -0
- data/lib/carray/frame/csv_parser.rb +211 -0
- data/lib/carray/frame/frame.rb +649 -0
- data/lib/carray/frame/group.rb +186 -0
- data/lib/carray/frame/io.rb +164 -0
- data/lib/carray/frame/join.rb +248 -0
- data/lib/carray/frame/records.rb +99 -0
- data/lib/carray/frame/sort.rb +113 -0
- data/lib/carray/frame/verbs.rb +299 -0
- data/lib/carray/frame.rb +16 -0
- data/lib/carray/histogram.rb +512 -0
- data/lib/carray/inspect.rb +37 -20
- data/lib/carray/iterator.rb +57 -349
- data/lib/carray/lazy.rb +889 -0
- data/lib/carray/mask_gap_fill.rb +200 -0
- data/lib/carray/math.rb +78 -342
- data/lib/carray/meld_reduce.rb +289 -0
- data/lib/carray/methods/align_addr.rb +116 -0
- data/lib/carray/methods/bin.rb +128 -0
- data/lib/carray/methods/bincount.rb +87 -0
- data/lib/carray/methods/bit_string.rb +92 -0
- data/lib/carray/methods/broadcast.rb +63 -0
- data/lib/carray/methods/choose.rb +39 -0
- data/lib/carray/methods/composition.rb +280 -0
- data/lib/carray/methods/gather_nd.rb +206 -0
- data/lib/carray/methods/index.rb +39 -0
- data/lib/carray/methods/insert_block.rb +99 -0
- data/lib/carray/methods/is_in.rb +141 -0
- data/lib/carray/methods/join.rb +90 -0
- data/lib/carray/methods/locate_addr.rb +47 -0
- data/lib/carray/methods/mask_duplicates.rb +41 -0
- data/lib/carray/methods/meshgrid.rb +91 -0
- data/lib/carray/methods/mode.rb +126 -0
- data/lib/carray/methods/nunique.rb +46 -0
- data/lib/carray/methods/resize.rb +56 -0
- data/lib/carray/methods/snap.rb +156 -0
- data/lib/carray/methods/string_format.rb +57 -0
- data/lib/carray/methods/unique.rb +47 -0
- data/lib/carray/methods/value_counts.rb +71 -0
- data/lib/carray/mkmf.rb +124 -101
- data/lib/carray/runtime.rb +108 -0
- data/lib/carray/serialize.rb +478 -167
- data/lib/carray/slab_iterator.rb +292 -0
- data/lib/carray/stack.rb +291 -0
- data/lib/carray/string.rb +56 -180
- data/lib/carray/string_operation_extension.rb +289 -0
- data/lib/carray/struct.rb +335 -323
- data/lib/carray/struct_builder.rb +697 -0
- data/lib/carray/table.rb +41 -2
- data/lib/carray/time.rb +2255 -38
- data/lib/carray/window_iterator.rb +655 -0
- data/lib/carray.rb +55 -57
- metadata +163 -130
- data/Rakefile +0 -51
- data/TODO.md +0 -18
- data/ext/ca_iter_block.c +0 -257
- data/ext/ca_iter_dimension.c +0 -299
- data/ext/ca_iter_window.c +0 -214
- data/ext/ca_obj_mapping.c +0 -644
- data/ext/carray_iterator.c +0 -641
- data/ext/carray_math.rb +0 -850
- data/ext/carray_numeric.c +0 -259
- data/ext/carray_sort_addr.c +0 -254
- data/ext/carray_stat.c +0 -2100
- data/ext/carray_stat_proc.rb +0 -1999
- data/ext/mkmath.rb +0 -741
- data/ext/ruby_ccomplex.c +0 -509
- data/ext/ruby_float_func.c +0 -86
- data/lib/carray/array.rb +0 -8
- data/lib/carray/autoload/autoload_base.rb +0 -19
- data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
- data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
- data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
- data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
- data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
- data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
- data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
- data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
- data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
- data/lib/carray/autoload/autoload_gem_random.rb +0 -8
- data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
- data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
- data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
- data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
- data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
- data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
- data/lib/carray/autoload/autoload_object_link.rb +0 -1
- data/lib/carray/autoload/autoload_object_pack.rb +0 -2
- data/lib/carray/autoload.rb +0 -141
- data/lib/carray/basic.rb +0 -191
- data/lib/carray/broadcast.rb +0 -101
- data/lib/carray/compose.rb +0 -315
- data/lib/carray/convert.rb +0 -115
- data/lib/carray/info.rb +0 -110
- data/lib/carray/io/imagemagick.rb +0 -235
- data/lib/carray/mask.rb +0 -102
- data/lib/carray/math/histogram.rb +0 -177
- data/lib/carray/math/recurrence.rb +0 -93
- data/lib/carray/object/ca_obj_iterator.rb +0 -50
- data/lib/carray/object/ca_obj_link.rb +0 -50
- data/lib/carray/object/ca_obj_pack.rb +0 -99
- data/lib/carray/obsolete.rb +0 -256
- data/lib/carray/ordering.rb +0 -181
- data/lib/carray/testing.rb +0 -51
- data/lib/carray/transform.rb +0 -109
- data/misc/Methods.ja.md +0 -182
- data/misc/NOTE +0 -51
- data/spec/Classes/CABitfield_spec.rb +0 -58
- data/spec/Classes/CABlockIterator_spec.rb +0 -114
- data/spec/Classes/CABlock_spec.rb +0 -205
- data/spec/Classes/CAField_spec.rb +0 -39
- data/spec/Classes/CAGrid_spec.rb +0 -75
- data/spec/Classes/CAMap_spec.rb +0 -0
- data/spec/Classes/CAMapping_spec.rb +0 -105
- data/spec/Classes/CAObject_attribute_spec.rb +0 -33
- data/spec/Classes/CAObject_spec.rb +0 -33
- data/spec/Classes/CARefer_spec.rb +0 -93
- data/spec/Classes/CARepeat_spec.rb +0 -65
- data/spec/Classes/CASelect_spec.rb +0 -22
- data/spec/Classes/CAShift_spec.rb +0 -16
- data/spec/Classes/CAStruct_spec.rb +0 -71
- data/spec/Classes/CATranspose_spec.rb +0 -60
- data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
- data/spec/Classes/CAWindow_spec.rb +0 -54
- data/spec/Classes/CAWrap_spec.rb +0 -8
- data/spec/Classes/CArray_spec.rb +0 -184
- data/spec/Classes/CScalar_spec.rb +0 -55
- data/spec/Classes/ex1.rb +0 -46
- data/spec/Features/feature_130_spec.rb +0 -19
- data/spec/Features/feature_attributes_spec.rb +0 -280
- data/spec/Features/feature_boolean_spec.rb +0 -98
- data/spec/Features/feature_broadcast.rb +0 -116
- data/spec/Features/feature_cast_function.rb +0 -19
- data/spec/Features/feature_cast_spec.rb +0 -33
- data/spec/Features/feature_class_spec.rb +0 -84
- data/spec/Features/feature_complex_spec.rb +0 -42
- data/spec/Features/feature_composite_spec.rb +0 -124
- data/spec/Features/feature_convert_spec.rb +0 -46
- data/spec/Features/feature_copy_spec.rb +0 -123
- data/spec/Features/feature_creation_spec.rb +0 -84
- data/spec/Features/feature_element_spec.rb +0 -144
- data/spec/Features/feature_extream_spec.rb +0 -54
- data/spec/Features/feature_generate_spec.rb +0 -74
- data/spec/Features/feature_index_spec.rb +0 -69
- data/spec/Features/feature_mask_spec.rb +0 -580
- data/spec/Features/feature_math_spec.rb +0 -97
- data/spec/Features/feature_order_spec.rb +0 -146
- data/spec/Features/feature_ref_store_spec.rb +0 -209
- data/spec/Features/feature_serialization_spec.rb +0 -125
- data/spec/Features/feature_stat_spec.rb +0 -397
- data/spec/Features/feature_virtual_spec.rb +0 -48
- data/spec/Features/method_eq_spec.rb +0 -81
- data/spec/Features/method_is_nan_spec.rb +0 -12
- data/spec/Features/method_map_spec.rb +0 -54
- data/spec/Features/method_max_with.rb +0 -20
- data/spec/Features/method_min_with.rb +0 -19
- data/spec/Features/method_ne_spec.rb +0 -18
- data/spec/Features/method_project_spec.rb +0 -188
- data/spec/Features/method_ref_spec.rb +0 -27
- data/spec/Features/method_round_spec.rb +0 -11
- data/spec/Features/method_s_linspace_spec.rb +0 -48
- data/spec/Features/method_s_span_spec.rb +0 -14
- data/spec/Features/method_seq_spec.rb +0 -47
- data/spec/Features/method_sort_with.rb +0 -43
- data/spec/Features/method_sorted_with.rb +0 -29
- data/spec/Features/method_span_spec.rb +0 -42
- data/spec/Features/method_wrap_readonly_spec.rb +0 -43
- data/spec/UnitTest/test_CAVirtual.rb +0 -214
- data/spec/spec_all.rb +0 -10
- data/utils/ca_ase.rb +0 -21
- data/utils/ca_methods.rb +0 -15
- data/utils/cast_checker.rb +0 -30
- data/utils/convert_test.rb +0 -73
- data/utils/extract_yard.rb +0 -22
- data/utils/guess_shape.rb +0 -76
- data/utils/monkey_patch_methods.rb +0 -62
- data/utils/remove_resource_fork.sh +0 -5
data/ext/ca_obj_stack.c
ADDED
|
@@ -0,0 +1,1173 @@
|
|
|
1
|
+
/* ---------------------------------------------------------------------------
|
|
2
|
+
|
|
3
|
+
CAStack view = an outer-axis stack of K uniform-shape parents.
|
|
4
|
+
shape = (K, *parent_shape); the k_axis dimension is the parent selector,
|
|
5
|
+
the remaining axes follow each parent's row-major layout.
|
|
6
|
+
|
|
7
|
+
Design summary:
|
|
8
|
+
- parents must be uniform shape + uniform data_type (= constructor raise)
|
|
9
|
+
- attach materialises via per-parent xfer_all (= K * parent.elements
|
|
10
|
+
bytes peak alloc, caller responsibility)
|
|
11
|
+
- xfer_stride dispatches per-parent over axis-0 range (= partial use →
|
|
12
|
+
partial cost)
|
|
13
|
+
- conditional fold_stride: counts[0] == 1 → fold to that parent;
|
|
14
|
+
multi-parent → fold boundary (decline)
|
|
15
|
+
- create_mask: horizontal propagation (= all parents' roots get
|
|
16
|
+
all-zero mask if any parent has mask) + mask CAStack itself
|
|
17
|
+
|
|
18
|
+
---------------------------------------------------------------------------- */
|
|
19
|
+
|
|
20
|
+
#include "carray.h"
|
|
21
|
+
#include "ca_composite_dispatch.h"
|
|
22
|
+
#include "ca_obj_face.h"
|
|
23
|
+
|
|
24
|
+
/* ------------------------------------------------------------------- */
|
|
25
|
+
/* TypedData */
|
|
26
|
+
/* ------------------------------------------------------------------- */
|
|
27
|
+
|
|
28
|
+
static size_t
|
|
29
|
+
ca_stack_dsize (const void *ap)
|
|
30
|
+
{
|
|
31
|
+
const CAStack *ca = (const CAStack *) ap;
|
|
32
|
+
return sizeof(CAStack)
|
|
33
|
+
+ ca->ndim * sizeof(ca_size_t) /* dim[] */
|
|
34
|
+
+ ca->n_parents * sizeof(CArray *); /* parents[] */
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const rb_data_type_t castack_data_type = {
|
|
38
|
+
.parent = &caview_data_type,
|
|
39
|
+
.wrap_struct_name = "CAStack",
|
|
40
|
+
.function = {
|
|
41
|
+
.dmark = ca_mark,
|
|
42
|
+
.dfree = ca_free,
|
|
43
|
+
.dsize = ca_stack_dsize,
|
|
44
|
+
.dcompact = NULL
|
|
45
|
+
},
|
|
46
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const rb_data_type_t castack_mask_data_type = {
|
|
50
|
+
.parent = &castack_data_type,
|
|
51
|
+
.wrap_struct_name = "CAStackMask",
|
|
52
|
+
.function = {
|
|
53
|
+
.dmark = ca_mark,
|
|
54
|
+
.dfree = ca_free_nop,
|
|
55
|
+
.dsize = ca_stack_dsize,
|
|
56
|
+
.dcompact = NULL
|
|
57
|
+
},
|
|
58
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
int8_t CA_OBJ_STACK;
|
|
62
|
+
|
|
63
|
+
VALUE rb_cCAStack;
|
|
64
|
+
VALUE rb_cCAStackMask;
|
|
65
|
+
|
|
66
|
+
static ID id_parents;
|
|
67
|
+
|
|
68
|
+
/* ------------------------------------------------------------------- */
|
|
69
|
+
/* setup / new / free / clone */
|
|
70
|
+
/* ------------------------------------------------------------------- */
|
|
71
|
+
|
|
72
|
+
/* Validate uniform shape + data_type across parents. Returns 0 on success,
|
|
73
|
+
raises ArgumentError on a uniform violation (hard reject at the
|
|
74
|
+
constructor; broadcast / coerce is not performed). */
|
|
75
|
+
static void
|
|
76
|
+
ca_stack_check_uniform (int32_t n_parents, CArray **parents)
|
|
77
|
+
{
|
|
78
|
+
CArray *ref;
|
|
79
|
+
int32_t i;
|
|
80
|
+
int8_t k;
|
|
81
|
+
if ( n_parents <= 0 ) {
|
|
82
|
+
rb_raise(rb_eArgError, "CAStack requires at least one parent");
|
|
83
|
+
}
|
|
84
|
+
ref = parents[0];
|
|
85
|
+
for ( i = 1; i < n_parents; i++ ) {
|
|
86
|
+
CArray *p = parents[i];
|
|
87
|
+
if ( p->data_type != ref->data_type ) {
|
|
88
|
+
rb_raise(rb_eArgError,
|
|
89
|
+
"CAStack parents must have uniform data_type "
|
|
90
|
+
"(parent[0]=%d, parent[%d]=%d)",
|
|
91
|
+
ref->data_type, i, p->data_type);
|
|
92
|
+
}
|
|
93
|
+
if ( p->ndim != ref->ndim ) {
|
|
94
|
+
rb_raise(rb_eArgError,
|
|
95
|
+
"CAStack parents must have uniform ndim "
|
|
96
|
+
"(parent[0]=%d, parent[%d]=%d)",
|
|
97
|
+
ref->ndim, i, p->ndim);
|
|
98
|
+
}
|
|
99
|
+
for ( k = 0; k < ref->ndim; k++ ) {
|
|
100
|
+
if ( p->dim[k] != ref->dim[k] ) {
|
|
101
|
+
rb_raise(rb_eArgError,
|
|
102
|
+
"CAStack parents must have uniform shape "
|
|
103
|
+
"(mismatch at axis %d: parent[0]=%lld, parent[%d]=%lld)",
|
|
104
|
+
(int) k,
|
|
105
|
+
(long long) ref->dim[k],
|
|
106
|
+
(int) i,
|
|
107
|
+
(long long) p->dim[k]);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if ( p->bytes != ref->bytes ) {
|
|
111
|
+
rb_raise(rb_eArgError,
|
|
112
|
+
"CAStack parents must have uniform bytes "
|
|
113
|
+
"(parent[0]=%lld, parent[%d]=%lld)",
|
|
114
|
+
(long long) ref->bytes, (int) i, (long long) p->bytes);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* One-level strip of a Face VALUE to its storage-side parent (non-Face as-is).
|
|
120
|
+
Used to keep the @parent GC/`.parent` ivar in step with the pre-stripped C
|
|
121
|
+
parents so the Ruby-visible chain of a stacked Face is single-Face too. */
|
|
122
|
+
static VALUE
|
|
123
|
+
ca_stack_face_parent1 (VALUE v)
|
|
124
|
+
{
|
|
125
|
+
CArray *c;
|
|
126
|
+
TypedData_Get_Struct(v, CArray, &carray_data_type, c);
|
|
127
|
+
return ca_is_face(c) ? rb_ca_parent(v) : v;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
int
|
|
131
|
+
ca_stack_setup_with_axis (CAStack *ca, int32_t n_parents, CArray **parents,
|
|
132
|
+
int8_t k_axis)
|
|
133
|
+
{
|
|
134
|
+
CArray *ref;
|
|
135
|
+
int32_t i;
|
|
136
|
+
int8_t a;
|
|
137
|
+
|
|
138
|
+
/* §8.3 / MEMO_FACE_DOUBLE_LIFT §15.3: pre-strip Face parents one level to
|
|
139
|
+
storage so a stacked Face lifts to a single-Face chain
|
|
140
|
+
(CATime[CAStack[entity, ...]]) instead of a Face on top of Face parents.
|
|
141
|
+
One level (not a full walk) preserves any distinct Face a parent stacked
|
|
142
|
+
underneath. A Face is storage-transparent, so stacking over storage reads
|
|
143
|
+
the same bytes; the lifted top Face (rb_ca_stack_s_new) carries the
|
|
144
|
+
identity. The @parents accessor keeps the originals (set by the callers). */
|
|
145
|
+
for ( i = 0; i < n_parents; i++ ) {
|
|
146
|
+
if ( ca_is_face(parents[i]) ) {
|
|
147
|
+
parents[i] = CAVIEW(parents[i])->parent;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
ca_stack_check_uniform(n_parents, parents);
|
|
152
|
+
ref = parents[0];
|
|
153
|
+
|
|
154
|
+
if ( k_axis < 0 || k_axis > ref->ndim ) {
|
|
155
|
+
rb_raise(rb_eArgError,
|
|
156
|
+
"CAStack k_axis %d out of range [0, %d]",
|
|
157
|
+
(int) k_axis, (int) ref->ndim);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
ca->obj_type = CA_OBJ_STACK;
|
|
161
|
+
ca->data_type = ref->data_type;
|
|
162
|
+
ca->flags = CA_FLAG_MULTI_PARENTS; /* fold-over-parents in generic code */
|
|
163
|
+
ca->ndim = ref->ndim + 1;
|
|
164
|
+
ca->bytes = ref->bytes;
|
|
165
|
+
ca->elements = (ca_size_t) n_parents * ref->elements;
|
|
166
|
+
ca->ptr = NULL;
|
|
167
|
+
ca->mask = NULL;
|
|
168
|
+
|
|
169
|
+
ca->parent = ref; /* CAView base field (= parents[0]) */
|
|
170
|
+
ca->attach = 0;
|
|
171
|
+
ca->nosync = 0;
|
|
172
|
+
|
|
173
|
+
ca->n_parents = n_parents;
|
|
174
|
+
ca->parents = ALLOC_N(CArray *, n_parents);
|
|
175
|
+
for ( i = 0; i < n_parents; i++ ) {
|
|
176
|
+
ca->parents[i] = parents[i];
|
|
177
|
+
}
|
|
178
|
+
ca->k_axis = k_axis;
|
|
179
|
+
|
|
180
|
+
ca->dim = ALLOC_N(ca_size_t, ca->ndim);
|
|
181
|
+
/* dim[a] = parent.dim[a] for a < k_axis
|
|
182
|
+
dim[k_axis] = K (= n_parents)
|
|
183
|
+
dim[a] = parent.dim[a-1] for a > k_axis */
|
|
184
|
+
for ( a = 0; a < k_axis; a++ ) ca->dim[a] = ref->dim[a];
|
|
185
|
+
ca->dim[k_axis] = n_parents;
|
|
186
|
+
for ( a = k_axis + 1; a < ca->ndim; a++ ) ca->dim[a] = ref->dim[a - 1];
|
|
187
|
+
|
|
188
|
+
/* mask: lazy; horizontal propagation triggers on first mask access. */
|
|
189
|
+
return 0;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
int
|
|
193
|
+
ca_stack_setup (CAStack *ca, int32_t n_parents, CArray **parents)
|
|
194
|
+
{
|
|
195
|
+
return ca_stack_setup_with_axis(ca, n_parents, parents, 0);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
CAStack *
|
|
199
|
+
ca_stack_new_with_axis (int32_t n_parents, CArray **parents, int8_t k_axis)
|
|
200
|
+
{
|
|
201
|
+
CAStack *ca = ALLOC(CAStack);
|
|
202
|
+
ca_stack_setup_with_axis(ca, n_parents, parents, k_axis);
|
|
203
|
+
return ca;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
CAStack *
|
|
207
|
+
ca_stack_new (int32_t n_parents, CArray **parents)
|
|
208
|
+
{
|
|
209
|
+
return ca_stack_new_with_axis(n_parents, parents, 0);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
static void
|
|
213
|
+
free_ca_stack (void *ap)
|
|
214
|
+
{
|
|
215
|
+
CAStack *ca = (CAStack *) ap;
|
|
216
|
+
if ( ca != NULL ) {
|
|
217
|
+
ca_free(ca->mask);
|
|
218
|
+
xfree(ca->parents);
|
|
219
|
+
xfree(ca->dim);
|
|
220
|
+
xfree(ca);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
static void *
|
|
225
|
+
ca_stack_func_clone (void *ap)
|
|
226
|
+
{
|
|
227
|
+
CAStack *ca = (CAStack *) ap;
|
|
228
|
+
return ca_stack_new_with_axis(ca->n_parents, ca->parents, ca->k_axis);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/* ------------------------------------------------------------------- */
|
|
232
|
+
/* xfer_index */
|
|
233
|
+
/* ------------------------------------------------------------------- */
|
|
234
|
+
|
|
235
|
+
/* view[..., k at k_axis, ...] = parents[k][parent_idx]
|
|
236
|
+
where parent_idx = idx[] with the k_axis slot removed. */
|
|
237
|
+
static void
|
|
238
|
+
ca_stack_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
|
|
239
|
+
{
|
|
240
|
+
CAStack *ca = (CAStack *) ap;
|
|
241
|
+
int8_t k_axis = ca->k_axis;
|
|
242
|
+
ca_size_t k = idx[k_axis];
|
|
243
|
+
if ( k < 0 || k >= ca->n_parents ) {
|
|
244
|
+
rb_raise(rb_eIndexError, "CAStack k-axis (axis %d) index %lld out of range [0, %d)",
|
|
245
|
+
(int) k_axis, (long long) k, (int) ca->n_parents);
|
|
246
|
+
}
|
|
247
|
+
if ( k_axis == 0 ) {
|
|
248
|
+
/* Fast path: parent_idx = idx[1..ndim-1]; pass idx+1 directly. */
|
|
249
|
+
ca_xfer_index(ca->parents[k], idx + 1, data, dir);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
/* General case: build parent_idx by removing the k_axis slot. */
|
|
253
|
+
{
|
|
254
|
+
ca_size_t pidx[CA_RANK_MAX];
|
|
255
|
+
int8_t a, pndim = ca->ndim - 1;
|
|
256
|
+
for ( a = 0; a < k_axis; a++ ) pidx[a] = idx[a];
|
|
257
|
+
for ( a = k_axis; a < pndim; a++ ) pidx[a] = idx[a + 1];
|
|
258
|
+
ca_xfer_index(ca->parents[k], pidx, data, dir);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/* ------------------------------------------------------------------- */
|
|
263
|
+
/* xfer_addrs (batched) */
|
|
264
|
+
/* ------------------------------------------------------------------- */
|
|
265
|
+
|
|
266
|
+
/* Per-addr: decompose addr -> (k, intra_addr) where k = addr / parent_elements
|
|
267
|
+
and intra_addr = addr % parent_elements. Group by k for batched parent
|
|
268
|
+
dispatch. Simple K-pass implementation: scan once per parent; OK for
|
|
269
|
+
typical K (= small to medium). Hot K cases can opt into a single-pass
|
|
270
|
+
bucket sort if needed. */
|
|
271
|
+
static void
|
|
272
|
+
ca_stack_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
|
|
273
|
+
void *data, int dir)
|
|
274
|
+
{
|
|
275
|
+
CAStack *ca = (CAStack *) ap;
|
|
276
|
+
int8_t k_axis = ca->k_axis;
|
|
277
|
+
ca_size_t parent_elements = ca->parents[0]->elements;
|
|
278
|
+
ca_size_t bytes = ca->bytes;
|
|
279
|
+
ca_size_t *paddrs;
|
|
280
|
+
char *pdata, *cdata = (char *) data;
|
|
281
|
+
volatile VALUE holder1, holder2;
|
|
282
|
+
ca_size_t i;
|
|
283
|
+
int32_t k;
|
|
284
|
+
|
|
285
|
+
paddrs = ALLOCV_N(ca_size_t, holder1, n);
|
|
286
|
+
pdata = ALLOCV_N(char, holder2, n * bytes);
|
|
287
|
+
|
|
288
|
+
if ( k_axis == 0 ) {
|
|
289
|
+
/* Fast path: addr = k * parent_elements + parent_addr (row-major over
|
|
290
|
+
view shape with K at axis 0). */
|
|
291
|
+
for ( k = 0; k < ca->n_parents; k++ ) {
|
|
292
|
+
ca_size_t m = 0;
|
|
293
|
+
for ( i = 0; i < n; i++ ) {
|
|
294
|
+
if ( addrs[i] / parent_elements == (ca_size_t) k ) {
|
|
295
|
+
paddrs[m] = addrs[i] % parent_elements;
|
|
296
|
+
if ( dir == CA_XFER_PUT ) {
|
|
297
|
+
memcpy(pdata + m * bytes, cdata + i * bytes, bytes);
|
|
298
|
+
}
|
|
299
|
+
m++;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if ( m > 0 ) {
|
|
303
|
+
ca_xfer_addrs(ca->parents[k], m, paddrs, pdata, dir);
|
|
304
|
+
if ( dir == CA_XFER_GET ) {
|
|
305
|
+
ca_size_t mm = 0;
|
|
306
|
+
for ( i = 0; i < n; i++ ) {
|
|
307
|
+
if ( addrs[i] / parent_elements == (ca_size_t) k ) {
|
|
308
|
+
memcpy(cdata + i * bytes, pdata + mm * bytes, bytes);
|
|
309
|
+
mm++;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
/* General case: decode addrs[i] in view row-major to extract
|
|
318
|
+
k = vidx[k_axis] and the parent's row-major address from the
|
|
319
|
+
remaining axes. */
|
|
320
|
+
int8_t pndim = ca->ndim - 1;
|
|
321
|
+
ca_size_t view_div[CA_RANK_MAX]; /* row-major divisor per view axis */
|
|
322
|
+
ca_size_t parent_mul[CA_RANK_MAX]; /* row-major multiplier per parent axis */
|
|
323
|
+
ca_size_t s;
|
|
324
|
+
int8_t a;
|
|
325
|
+
s = 1;
|
|
326
|
+
for ( a = ca->ndim - 1; a >= 0; a-- ) { view_div[a] = s; s *= ca->dim[a]; }
|
|
327
|
+
s = 1;
|
|
328
|
+
for ( a = pndim - 1; a >= 0; a-- ) {
|
|
329
|
+
parent_mul[a] = s;
|
|
330
|
+
s *= ca->parents[0]->dim[a];
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
for ( k = 0; k < ca->n_parents; k++ ) {
|
|
334
|
+
ca_size_t m = 0;
|
|
335
|
+
for ( i = 0; i < n; i++ ) {
|
|
336
|
+
ca_size_t addr = addrs[i];
|
|
337
|
+
ca_size_t vidx_k = (addr / view_div[k_axis]) % ca->dim[k_axis];
|
|
338
|
+
if ( vidx_k != (ca_size_t) k ) continue;
|
|
339
|
+
{
|
|
340
|
+
ca_size_t paddr = 0;
|
|
341
|
+
int8_t b;
|
|
342
|
+
for ( b = 0; b < k_axis; b++ ) {
|
|
343
|
+
ca_size_t v = (addr / view_div[b]) % ca->dim[b];
|
|
344
|
+
paddr += v * parent_mul[b];
|
|
345
|
+
}
|
|
346
|
+
for ( b = k_axis + 1; b < ca->ndim; b++ ) {
|
|
347
|
+
ca_size_t v = (addr / view_div[b]) % ca->dim[b];
|
|
348
|
+
paddr += v * parent_mul[b - 1];
|
|
349
|
+
}
|
|
350
|
+
paddrs[m] = paddr;
|
|
351
|
+
}
|
|
352
|
+
if ( dir == CA_XFER_PUT ) {
|
|
353
|
+
memcpy(pdata + m * bytes, cdata + i * bytes, bytes);
|
|
354
|
+
}
|
|
355
|
+
m++;
|
|
356
|
+
}
|
|
357
|
+
if ( m > 0 ) {
|
|
358
|
+
ca_xfer_addrs(ca->parents[k], m, paddrs, pdata, dir);
|
|
359
|
+
if ( dir == CA_XFER_GET ) {
|
|
360
|
+
ca_size_t mm = 0;
|
|
361
|
+
for ( i = 0; i < n; i++ ) {
|
|
362
|
+
ca_size_t addr = addrs[i];
|
|
363
|
+
ca_size_t vidx_k = (addr / view_div[k_axis]) % ca->dim[k_axis];
|
|
364
|
+
if ( vidx_k != (ca_size_t) k ) continue;
|
|
365
|
+
memcpy(cdata + i * bytes, pdata + mm * bytes, bytes);
|
|
366
|
+
mm++;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
(void) parent_elements;
|
|
372
|
+
}
|
|
373
|
+
ALLOCV_END(holder2);
|
|
374
|
+
ALLOCV_END(holder1);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/* ------------------------------------------------------------------- */
|
|
378
|
+
/* xfer_stride: K-fold per-parent dispatch over axis-0 range */
|
|
379
|
+
/* ------------------------------------------------------------------- */
|
|
380
|
+
|
|
381
|
+
/* Contract (= CATile pattern): caller's `strides[]` are the chain-composed
|
|
382
|
+
strides through root's byte space (= the result of ca_stride_compose_to_root
|
|
383
|
+
in CAStride.xfer_stride). starts[] is in CAStack-axis order (= addr2index
|
|
384
|
+
of composed_base); counts[]/strides[] are in OUTPUT-axis order.
|
|
385
|
+
|
|
386
|
+
Three paths:
|
|
387
|
+
- Structural (strides[i] == native[i] for all i): per-parent K-fold over
|
|
388
|
+
the request's k_axis range. k_axis=0 = each parent contig in dst (zero
|
|
389
|
+
copy slot); k_axis!=0 = per-parent xfer_stride into a slab buf, then
|
|
390
|
+
scatter/gather between buf and the view-strided dst positions.
|
|
391
|
+
- Reloc (permutation that only moved K from view axis k_axis to output
|
|
392
|
+
position p, non-K axes preserve relative order): per (leading + K)
|
|
393
|
+
position dispatch. K-innermost (p == ndim-1) escapes per-cell via the
|
|
394
|
+
parent's xfer_index; otherwise per-parent inner-block xfer_stride.
|
|
395
|
+
- Per-cell fallback via xfer_index. */
|
|
396
|
+
static void
|
|
397
|
+
ca_stack_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
|
|
398
|
+
ca_size_t *strides, void *data, int dir)
|
|
399
|
+
{
|
|
400
|
+
CAStack *ca = (CAStack *) ap;
|
|
401
|
+
int8_t k_axis = ca->k_axis;
|
|
402
|
+
int8_t pndim = ca->ndim - 1;
|
|
403
|
+
ca_size_t native[CA_RANK_MAX], dstride[CA_RANK_MAX];
|
|
404
|
+
ca_size_t pstarts[CA_RANK_MAX], pcounts[CA_RANK_MAX], pstrides[CA_RANK_MAX];
|
|
405
|
+
ca_size_t k, s;
|
|
406
|
+
int8_t i;
|
|
407
|
+
int structural = 1;
|
|
408
|
+
char *d = (char *) data;
|
|
409
|
+
|
|
410
|
+
/* CAStack's native row-major byte strides over its full shape. */
|
|
411
|
+
s = ca->bytes;
|
|
412
|
+
for ( i = ca->ndim - 1; i >= 0; i-- ) { native[i] = s; s *= ca->dim[i]; }
|
|
413
|
+
|
|
414
|
+
/* Parent's native (source) byte strides over its own row-major shape. */
|
|
415
|
+
s = ca->bytes;
|
|
416
|
+
for ( i = pndim - 1; i >= 0; i-- ) {
|
|
417
|
+
pstrides[i] = s;
|
|
418
|
+
s *= ca->parents[0]->dim[i];
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/* Structural iff strides[k] == native[k] for all k. */
|
|
422
|
+
for ( i = 0; i < ca->ndim; i++ ) {
|
|
423
|
+
if ( strides[i] != native[i] ) { structural = 0; break; }
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if ( structural ) {
|
|
427
|
+
ca_size_t k_lo = starts[k_axis];
|
|
428
|
+
ca_size_t k_hi = starts[k_axis] + counts[k_axis];
|
|
429
|
+
if ( k_lo < 0 || k_hi > ca->n_parents ) {
|
|
430
|
+
rb_raise(rb_eIndexError,
|
|
431
|
+
"CAStack xfer_stride k-axis (axis %d) [%lld, %lld) out of range [0, %d)",
|
|
432
|
+
(int) k_axis, (long long) k_lo, (long long) k_hi, (int) ca->n_parents);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/* dst row-major byte strides over output counts[]. */
|
|
436
|
+
s = ca->bytes;
|
|
437
|
+
for ( i = ca->ndim - 1; i >= 0; i-- ) { dstride[i] = s; s *= counts[i]; }
|
|
438
|
+
|
|
439
|
+
/* pstarts/pcounts = starts/counts with the k_axis slot removed. */
|
|
440
|
+
for ( i = 0; i < pndim; i++ ) {
|
|
441
|
+
int8_t v = (i < k_axis) ? i : (i + 1);
|
|
442
|
+
pstarts[i] = starts[v];
|
|
443
|
+
pcounts[i] = counts[v];
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if ( k_axis == 0 ) {
|
|
447
|
+
/* Each parent's row-major output is a contig slot of dstride[0] bytes
|
|
448
|
+
in dst -- zero copy. */
|
|
449
|
+
for ( k = k_lo; k < k_hi; k++ ) {
|
|
450
|
+
char *slot = d + (k - k_lo) * dstride[0];
|
|
451
|
+
ca_xfer_stride(ca->parents[k], pstarts, pcounts, pstrides, slot, dir);
|
|
452
|
+
}
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/* k_axis != 0: parent's row-major output of size slab_elements * bytes
|
|
457
|
+
does not fit contig in dst because the K-stride sits in the middle of
|
|
458
|
+
the dst row-major flow. Per-parent: deliver into a slab buf, then
|
|
459
|
+
walk parent_idx row-major to scatter/gather to/from dst at the
|
|
460
|
+
view-strided positions. */
|
|
461
|
+
{
|
|
462
|
+
ca_size_t slab_bytes = ca->bytes;
|
|
463
|
+
ca_size_t inner_dim[CA_RANK_MAX];
|
|
464
|
+
volatile VALUE holder;
|
|
465
|
+
char *buf;
|
|
466
|
+
int8_t a;
|
|
467
|
+
|
|
468
|
+
for ( i = 0; i < pndim; i++ ) {
|
|
469
|
+
inner_dim[i] = pcounts[i];
|
|
470
|
+
slab_bytes *= pcounts[i];
|
|
471
|
+
}
|
|
472
|
+
buf = ALLOCV_N(char, holder, slab_bytes);
|
|
473
|
+
|
|
474
|
+
for ( k = k_lo; k < k_hi; k++ ) {
|
|
475
|
+
ca_size_t k_off = (k - k_lo) * dstride[k_axis];
|
|
476
|
+
if ( dir == CA_XFER_GET ) {
|
|
477
|
+
ca_xfer_stride(ca->parents[k], pstarts, pcounts, pstrides, buf, CA_XFER_GET);
|
|
478
|
+
}
|
|
479
|
+
if ( pndim == 0 ) {
|
|
480
|
+
/* parent is scalar-shaped (1 element). */
|
|
481
|
+
if ( dir == CA_XFER_GET ) memcpy(d + k_off, buf, ca->bytes);
|
|
482
|
+
else memcpy(buf, d + k_off, ca->bytes);
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
ca_size_t pidx[CA_RANK_MAX];
|
|
486
|
+
ca_size_t paddr = 0;
|
|
487
|
+
for ( a = 0; a < pndim; a++ ) pidx[a] = 0;
|
|
488
|
+
while ( 1 ) {
|
|
489
|
+
ca_size_t voff = k_off;
|
|
490
|
+
for ( a = 0; a < k_axis; a++ ) voff += pidx[a] * dstride[a];
|
|
491
|
+
for ( a = k_axis; a < pndim; a++ ) voff += pidx[a] * dstride[a + 1];
|
|
492
|
+
if ( dir == CA_XFER_GET ) memcpy(d + voff, buf + paddr, ca->bytes);
|
|
493
|
+
else memcpy(buf + paddr, d + voff, ca->bytes);
|
|
494
|
+
paddr += ca->bytes;
|
|
495
|
+
i = pndim - 1;
|
|
496
|
+
while ( i >= 0 ) {
|
|
497
|
+
if ( ++pidx[i] < inner_dim[i] ) break;
|
|
498
|
+
pidx[i] = 0;
|
|
499
|
+
i--;
|
|
500
|
+
}
|
|
501
|
+
if ( i < 0 ) break;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
if ( dir == CA_XFER_PUT ) {
|
|
505
|
+
ca_xfer_stride(ca->parents[k], pstarts, pcounts, pstrides, buf, CA_XFER_PUT);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
ALLOCV_END(holder);
|
|
509
|
+
}
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/* Non-structural: try reloc fast path. The request is a permutation that
|
|
514
|
+
moved K from view axis k_axis to output position p, with the non-K axes
|
|
515
|
+
preserving their relative order. Detection: native[k_axis] appears in
|
|
516
|
+
strides[] at position p; the remaining strides[] equal native[0..ndim-1]
|
|
517
|
+
with native[k_axis] removed, in order. */
|
|
518
|
+
{
|
|
519
|
+
int8_t p = -1;
|
|
520
|
+
int reloc = 1;
|
|
521
|
+
for ( i = 0; i < ca->ndim; i++ ) {
|
|
522
|
+
if ( strides[i] == native[k_axis] ) { p = i; break; }
|
|
523
|
+
}
|
|
524
|
+
if ( p < 0 ) {
|
|
525
|
+
reloc = 0;
|
|
526
|
+
}
|
|
527
|
+
else {
|
|
528
|
+
/* non_K_native[j] = native of the j-th non-K view axis (in view order)
|
|
529
|
+
= native[j] if j < k_axis else native[j + 1].
|
|
530
|
+
For output position i != p, strides[i] should equal
|
|
531
|
+
non_K_native[i if i < p else i - 1]. */
|
|
532
|
+
for ( i = 0; i < ca->ndim; i++ ) {
|
|
533
|
+
int8_t j, v;
|
|
534
|
+
if ( i == p ) continue;
|
|
535
|
+
j = (i < p) ? i : (i - 1);
|
|
536
|
+
v = (j < k_axis) ? j : (j + 1);
|
|
537
|
+
if ( strides[i] != native[v] ) { reloc = 0; break; }
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
if ( reloc ) {
|
|
541
|
+
ca_size_t kk_lo = starts[k_axis];
|
|
542
|
+
ca_size_t kk_hi = starts[k_axis] + counts[p];
|
|
543
|
+
ca_size_t odo[CA_RANK_MAX];
|
|
544
|
+
int8_t a;
|
|
545
|
+
if ( kk_lo < 0 || kk_hi > ca->n_parents ) {
|
|
546
|
+
rb_raise(rb_eIndexError,
|
|
547
|
+
"CAStack xfer_stride k-axis [%lld, %lld) out of range [0, %d)",
|
|
548
|
+
(long long) kk_lo, (long long) kk_hi, (int) ca->n_parents);
|
|
549
|
+
}
|
|
550
|
+
if ( p == ca->ndim - 1 ) {
|
|
551
|
+
/* K innermost: per-cell via the parent's xfer_index directly, escaping
|
|
552
|
+
the CAStack-level addr2index dispatch hop in the per-cell fallback. */
|
|
553
|
+
ca_size_t pidx[CA_RANK_MAX], doff = 0;
|
|
554
|
+
for ( i = 0; i < ca->ndim; i++ ) odo[i] = 0;
|
|
555
|
+
while ( 1 ) {
|
|
556
|
+
ca_size_t k2 = starts[k_axis] + odo[p];
|
|
557
|
+
for ( a = 0; a < pndim; a++ ) {
|
|
558
|
+
int8_t v = (a < k_axis) ? a : (a + 1);
|
|
559
|
+
pidx[a] = starts[v] + odo[a];
|
|
560
|
+
}
|
|
561
|
+
ca_xfer_index(ca->parents[k2], pidx, d + doff, dir);
|
|
562
|
+
doff += ca->bytes;
|
|
563
|
+
i = ca->ndim - 1;
|
|
564
|
+
while ( i >= 0 ) { if ( ++odo[i] < counts[i] ) break; odo[i] = 0; i--; }
|
|
565
|
+
if ( i < 0 ) break;
|
|
566
|
+
}
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
/* dst row-major byte strides over output counts. */
|
|
570
|
+
s = ca->bytes;
|
|
571
|
+
for ( i = ca->ndim - 1; i >= 0; i-- ) { dstride[i] = s; s *= counts[i]; }
|
|
572
|
+
for ( i = 0; i <= p; i++ ) odo[i] = 0;
|
|
573
|
+
while ( 1 ) {
|
|
574
|
+
ca_size_t k2 = starts[k_axis] + odo[p];
|
|
575
|
+
ca_size_t doff = 0;
|
|
576
|
+
for ( i = 0; i <= p; i++ ) doff += odo[i] * dstride[i];
|
|
577
|
+
for ( a = 0; a < pndim; a++ ) {
|
|
578
|
+
int8_t v = (a < k_axis) ? a : (a + 1);
|
|
579
|
+
if ( a < p ) {
|
|
580
|
+
pstarts[a] = starts[v] + odo[a];
|
|
581
|
+
pcounts[a] = 1;
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
pstarts[a] = starts[v];
|
|
585
|
+
pcounts[a] = counts[a + 1];
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
ca_xfer_stride(ca->parents[k2], pstarts, pcounts, pstrides, d + doff, dir);
|
|
589
|
+
i = p;
|
|
590
|
+
while ( i >= 0 ) { if ( ++odo[i] < counts[i] ) break; odo[i] = 0; i--; }
|
|
591
|
+
if ( i < 0 ) break;
|
|
592
|
+
}
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/* Per-cell fallback via xfer_index. */
|
|
598
|
+
{
|
|
599
|
+
ca_size_t idx[CA_RANK_MAX], doff = 0, base = 0;
|
|
600
|
+
for ( i = 0; i < ca->ndim; i++ ) base += starts[i] * native[i];
|
|
601
|
+
for ( i = 0; i < ca->ndim; i++ ) idx[i] = 0;
|
|
602
|
+
while ( 1 ) {
|
|
603
|
+
ca_size_t toff = base, vidx[CA_RANK_MAX];
|
|
604
|
+
for ( i = 0; i < ca->ndim; i++ ) toff += idx[i] * strides[i];
|
|
605
|
+
ca_addr2index((CArray *) ca, toff / ca->bytes, vidx);
|
|
606
|
+
ca_stack_func_xfer_index(ca, vidx, d + doff, dir);
|
|
607
|
+
doff += ca->bytes;
|
|
608
|
+
i = ca->ndim - 1;
|
|
609
|
+
while ( i >= 0 ) { if ( ++idx[i] < counts[i] ) break; idx[i] = 0; i--; }
|
|
610
|
+
if ( i < 0 ) break;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/* ------------------------------------------------------------------- */
|
|
616
|
+
/* xfer_all: K-fold over all parents */
|
|
617
|
+
/* ------------------------------------------------------------------- */
|
|
618
|
+
|
|
619
|
+
/* gather/scatter the entire view buffer (= row-major contig over the
|
|
620
|
+
view's own shape).
|
|
621
|
+
- k_axis == 0: each parent slot occupies parent.elements * bytes
|
|
622
|
+
contiguous bytes in dst; deliver via parent.xfer_all directly.
|
|
623
|
+
- k_axis != 0: per-parent contiguous read into a temp buffer, then
|
|
624
|
+
scattered-write to dst at view row-major positions. This is the
|
|
625
|
+
correctness path (per-parent contig-read / scattered-write), not a
|
|
626
|
+
perf-optimised one. */
|
|
627
|
+
static void
|
|
628
|
+
ca_stack_func_xfer_all (void *ap, void *data, int dir)
|
|
629
|
+
{
|
|
630
|
+
CAStack *ca = (CAStack *) ap;
|
|
631
|
+
int8_t k_axis = ca->k_axis;
|
|
632
|
+
ca_size_t bytes = ca->bytes;
|
|
633
|
+
char *d = (char *) data;
|
|
634
|
+
int32_t k;
|
|
635
|
+
|
|
636
|
+
if ( k_axis == 0 ) {
|
|
637
|
+
ca_size_t parent_bytes_total = ca->parents[0]->elements * bytes;
|
|
638
|
+
for ( k = 0; k < ca->n_parents; k++ ) {
|
|
639
|
+
ca_xfer_all(ca->parents[k], d + k * parent_bytes_total, dir);
|
|
640
|
+
}
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
{
|
|
645
|
+
int8_t pndim = ca->ndim - 1;
|
|
646
|
+
ca_size_t parent_elements = ca->parents[0]->elements;
|
|
647
|
+
ca_size_t view_stride[CA_RANK_MAX]; /* byte strides over view row-major */
|
|
648
|
+
ca_size_t parent_dim[CA_RANK_MAX];
|
|
649
|
+
volatile VALUE holder;
|
|
650
|
+
char *buf;
|
|
651
|
+
ca_size_t s;
|
|
652
|
+
int8_t a;
|
|
653
|
+
|
|
654
|
+
s = bytes;
|
|
655
|
+
for ( a = ca->ndim - 1; a >= 0; a-- ) {
|
|
656
|
+
view_stride[a] = s;
|
|
657
|
+
s *= ca->dim[a];
|
|
658
|
+
}
|
|
659
|
+
for ( a = 0; a < pndim; a++ ) parent_dim[a] = ca->parents[0]->dim[a];
|
|
660
|
+
|
|
661
|
+
buf = ALLOCV_N(char, holder, parent_elements * bytes);
|
|
662
|
+
|
|
663
|
+
/* Per-parent axis step/back tables for incremental voff update.
|
|
664
|
+
parent axis a maps to view axis v = a if a < k_axis else a + 1;
|
|
665
|
+
step[a] = the dst byte stride contributed by parent axis a;
|
|
666
|
+
back[a] = (parent_dim[a] - 1) * step[a] = amount to subtract on wrap.
|
|
667
|
+
These are independent of k, so hoist outside the per-parent loop. */
|
|
668
|
+
{
|
|
669
|
+
ca_size_t step[CA_RANK_MAX];
|
|
670
|
+
ca_size_t back[CA_RANK_MAX];
|
|
671
|
+
for ( a = 0; a < pndim; a++ ) {
|
|
672
|
+
int8_t v = (a < k_axis) ? a : (a + 1);
|
|
673
|
+
step[a] = view_stride[v];
|
|
674
|
+
back[a] = (parent_dim[a] - 1) * step[a];
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
for ( k = 0; k < ca->n_parents; k++ ) {
|
|
678
|
+
ca_size_t k_off = (ca_size_t) k * view_stride[k_axis];
|
|
679
|
+
ca_size_t pidx[CA_RANK_MAX];
|
|
680
|
+
ca_size_t paddr_bytes = 0;
|
|
681
|
+
ca_size_t voff = k_off;
|
|
682
|
+
int8_t i;
|
|
683
|
+
|
|
684
|
+
if ( dir == CA_XFER_GET ) {
|
|
685
|
+
ca_xfer_all(ca->parents[k], buf, CA_XFER_GET);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
for ( a = 0; a < pndim; a++ ) pidx[a] = 0;
|
|
689
|
+
|
|
690
|
+
if ( pndim == 0 ) {
|
|
691
|
+
/* parent is scalar-shaped (= 1 element); single cell at k_off. */
|
|
692
|
+
if ( dir == CA_XFER_GET ) memcpy(d + k_off, buf, bytes);
|
|
693
|
+
else memcpy(buf, d + k_off, bytes);
|
|
694
|
+
}
|
|
695
|
+
else if ( dir == CA_XFER_GET ) {
|
|
696
|
+
/* dir-hoisted GET inner loop, incremental voff. */
|
|
697
|
+
while ( 1 ) {
|
|
698
|
+
memcpy(d + voff, buf + paddr_bytes, bytes);
|
|
699
|
+
paddr_bytes += bytes;
|
|
700
|
+
i = pndim - 1;
|
|
701
|
+
while ( i >= 0 ) {
|
|
702
|
+
if ( ++pidx[i] < parent_dim[i] ) { voff += step[i]; break; }
|
|
703
|
+
pidx[i] = 0;
|
|
704
|
+
voff -= back[i];
|
|
705
|
+
i--;
|
|
706
|
+
}
|
|
707
|
+
if ( i < 0 ) break;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
else {
|
|
711
|
+
/* dir-hoisted PUT inner loop, incremental voff. */
|
|
712
|
+
while ( 1 ) {
|
|
713
|
+
memcpy(buf + paddr_bytes, d + voff, bytes);
|
|
714
|
+
paddr_bytes += bytes;
|
|
715
|
+
i = pndim - 1;
|
|
716
|
+
while ( i >= 0 ) {
|
|
717
|
+
if ( ++pidx[i] < parent_dim[i] ) { voff += step[i]; break; }
|
|
718
|
+
pidx[i] = 0;
|
|
719
|
+
voff -= back[i];
|
|
720
|
+
i--;
|
|
721
|
+
}
|
|
722
|
+
if ( i < 0 ) break;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
if ( dir == CA_XFER_PUT ) {
|
|
727
|
+
ca_xfer_all(ca->parents[k], buf, CA_XFER_PUT);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
ALLOCV_END(holder);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/* ------------------------------------------------------------------- */
|
|
736
|
+
/* attach / sync / detach (materialise via xfer_all) */
|
|
737
|
+
/* ------------------------------------------------------------------- */
|
|
738
|
+
|
|
739
|
+
static void
|
|
740
|
+
ca_stack_func_allocate (void *ap)
|
|
741
|
+
{
|
|
742
|
+
CAStack *ca = (CAStack *) ap;
|
|
743
|
+
int32_t k;
|
|
744
|
+
for ( k = 0; k < ca->n_parents; k++ ) {
|
|
745
|
+
ca_attach(ca->parents[k]);
|
|
746
|
+
}
|
|
747
|
+
ca->ptr = xmalloc(ca_length(ca));
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
static void
|
|
751
|
+
ca_stack_func_attach (void *ap)
|
|
752
|
+
{
|
|
753
|
+
CAStack *ca = (CAStack *) ap;
|
|
754
|
+
int32_t k;
|
|
755
|
+
for ( k = 0; k < ca->n_parents; k++ ) {
|
|
756
|
+
ca_attach(ca->parents[k]);
|
|
757
|
+
}
|
|
758
|
+
ca->ptr = xmalloc(ca_length(ca));
|
|
759
|
+
ca_stack_func_xfer_all(ca, ca->ptr, CA_XFER_GET);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
static void
|
|
763
|
+
ca_stack_func_sync (void *ap)
|
|
764
|
+
{
|
|
765
|
+
CAStack *ca = (CAStack *) ap;
|
|
766
|
+
int32_t k;
|
|
767
|
+
ca_stack_func_xfer_all(ca, ca->ptr, CA_XFER_PUT);
|
|
768
|
+
for ( k = 0; k < ca->n_parents; k++ ) {
|
|
769
|
+
ca_sync(ca->parents[k]);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
static void
|
|
774
|
+
ca_stack_func_detach (void *ap)
|
|
775
|
+
{
|
|
776
|
+
CAStack *ca = (CAStack *) ap;
|
|
777
|
+
int32_t k;
|
|
778
|
+
xfree(ca->ptr);
|
|
779
|
+
ca->ptr = NULL;
|
|
780
|
+
for ( k = 0; k < ca->n_parents; k++ ) {
|
|
781
|
+
ca_detach(ca->parents[k]);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/* ------------------------------------------------------------------- */
|
|
786
|
+
/* fill_data: K-fold per-parent fill */
|
|
787
|
+
/* ------------------------------------------------------------------- */
|
|
788
|
+
|
|
789
|
+
static void
|
|
790
|
+
ca_stack_func_fill_data (void *ap, void *ptr)
|
|
791
|
+
{
|
|
792
|
+
CAStack *ca = (CAStack *) ap;
|
|
793
|
+
int32_t k;
|
|
794
|
+
for ( k = 0; k < ca->n_parents; k++ ) {
|
|
795
|
+
ca_fill(ca->parents[k], ptr);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/* ------------------------------------------------------------------- */
|
|
800
|
+
/* create_mask: horizontal propagation */
|
|
801
|
+
/* ------------------------------------------------------------------- */
|
|
802
|
+
|
|
803
|
+
/* All parents' roots gain all-zero mask (if not already), then build
|
|
804
|
+
mask CAStack from the K parent mask CArrays. Self-similar (= CAStack
|
|
805
|
+
re-used as its own mask class). */
|
|
806
|
+
static void
|
|
807
|
+
ca_stack_func_create_mask (void *ap)
|
|
808
|
+
{
|
|
809
|
+
CAStack *ca = (CAStack *) ap;
|
|
810
|
+
CArray **mask_parents;
|
|
811
|
+
volatile VALUE holder;
|
|
812
|
+
int32_t k;
|
|
813
|
+
|
|
814
|
+
mask_parents = ALLOCV_N(CArray *, holder, ca->n_parents);
|
|
815
|
+
for ( k = 0; k < ca->n_parents; k++ ) {
|
|
816
|
+
ca_update_mask(ca->parents[k]);
|
|
817
|
+
if ( ! ca->parents[k]->mask ) {
|
|
818
|
+
ca_create_mask(ca->parents[k]);
|
|
819
|
+
}
|
|
820
|
+
mask_parents[k] = ca->parents[k]->mask;
|
|
821
|
+
}
|
|
822
|
+
/* Build the mask stack with the parent's k_axis from the start so its
|
|
823
|
+
dim[] is laid out for that axis. (Patching ->k_axis after a default
|
|
824
|
+
ca_stack_new leaves dim[] computed for k_axis 0 — an inconsistent
|
|
825
|
+
geometry that mis-shapes arr.mask and breaks the structural
|
|
826
|
+
xfer_stride k-range check when k_axis != 0.) */
|
|
827
|
+
ca->mask = (CArray *) ca_stack_new_with_axis(ca->n_parents, mask_parents,
|
|
828
|
+
ca->k_axis);
|
|
829
|
+
ALLOCV_END(holder);
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/* ------------------------------------------------------------------- */
|
|
833
|
+
/* fold_stride: conditional participation */
|
|
834
|
+
/* ------------------------------------------------------------------- */
|
|
835
|
+
|
|
836
|
+
/* Fold to a single parent iff the request's byte box lies entirely within one
|
|
837
|
+
parent, then rebase into that parent's space and continue the compose there.
|
|
838
|
+
|
|
839
|
+
Testing only f->counts[0] == 1 is WRONG for a permuting chain: after a
|
|
840
|
+
transpose the K axis (native stride = parent_bytes_total) is no longer at
|
|
841
|
+
axis 0, so a size-1 leading PARENT axis (counts[0] == 1) would spuriously
|
|
842
|
+
fold a genuinely multi-parent request down to one parent -- silent data loss
|
|
843
|
+
(observed: t.transpose(1,0,2)[2..2, 0..3, 2..4]). Box containment is
|
|
844
|
+
order-independent and correct regardless of where the K axis ended up. */
|
|
845
|
+
static int
|
|
846
|
+
ca_stack_func_fold_stride (void *ap, ca_fold_t *f, void **next_parent)
|
|
847
|
+
{
|
|
848
|
+
CAStack *ca = (CAStack *) ap;
|
|
849
|
+
ca_size_t pbt = ca->parents[0]->elements * ca->bytes;
|
|
850
|
+
ca_size_t lo = f->base, hi = f->base;
|
|
851
|
+
ca_size_t k;
|
|
852
|
+
int8_t i;
|
|
853
|
+
|
|
854
|
+
/* k_axis != 0: parent byte blocks are interleaved (not contig in view
|
|
855
|
+
buffer), so the byte-box containment check and the size-1 K-axis
|
|
856
|
+
degeneration below would require detecting the K-axis contribution
|
|
857
|
+
inside an arbitrary stride composition -- not just probing for the
|
|
858
|
+
parent_bytes_total stride at axis 0. Decline fold; the caller falls
|
|
859
|
+
through to CAStack.xfer_stride (k_axis-aware), which still delivers
|
|
860
|
+
correct (if non-folded) per-parent dispatch. */
|
|
861
|
+
if ( ca->k_axis != 0 ) return 0;
|
|
862
|
+
|
|
863
|
+
for ( i = 0; i < f->ndim; i++ ) {
|
|
864
|
+
ca_size_t span = (f->counts[i] - 1) * f->strides[i];
|
|
865
|
+
if ( span >= 0 ) hi += span; else lo += span; /* negative stride extends lo */
|
|
866
|
+
}
|
|
867
|
+
if ( lo < 0 ) return 0;
|
|
868
|
+
k = lo / pbt;
|
|
869
|
+
if ( k < 0 || k >= ca->n_parents ) return 0;
|
|
870
|
+
if ( hi >= (k + 1) * pbt ) return 0; /* box spans >1 parent */
|
|
871
|
+
|
|
872
|
+
f->base -= k * pbt;
|
|
873
|
+
for ( i = 0; i < f->ndim; i++ ) { /* degenerate the (size-1) K axis */
|
|
874
|
+
if ( f->strides[i] == pbt ) f->strides[i] = 0;
|
|
875
|
+
}
|
|
876
|
+
*next_parent = (void *) ca->parents[k];
|
|
877
|
+
return 1;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/* ------------------------------------------------------------------- */
|
|
881
|
+
/* operation table */
|
|
882
|
+
/* ------------------------------------------------------------------- */
|
|
883
|
+
|
|
884
|
+
ca_operation_function_t ca_stack_func = {
|
|
885
|
+
-1, /* CA_OBJ_STACK */
|
|
886
|
+
CA_VIEW_ARRAY,
|
|
887
|
+
free_ca_stack,
|
|
888
|
+
ca_stack_func_clone,
|
|
889
|
+
ca_stack_func_allocate,
|
|
890
|
+
ca_stack_func_attach,
|
|
891
|
+
ca_stack_func_sync,
|
|
892
|
+
ca_stack_func_detach,
|
|
893
|
+
ca_stack_func_fill_data,
|
|
894
|
+
ca_stack_func_create_mask,
|
|
895
|
+
ca_stack_func_xfer_index,
|
|
896
|
+
ca_stack_func_xfer_addrs,
|
|
897
|
+
ca_stack_func_fold_stride,
|
|
898
|
+
ca_stack_func_xfer_stride,
|
|
899
|
+
ca_stack_func_xfer_all,
|
|
900
|
+
};
|
|
901
|
+
|
|
902
|
+
/* ------------------------------------------------------------------- */
|
|
903
|
+
/* Ruby surface */
|
|
904
|
+
/* ------------------------------------------------------------------- */
|
|
905
|
+
|
|
906
|
+
/* Build the raw CAStack VALUE from a Ruby array of CArray VALUEs.
|
|
907
|
+
|
|
908
|
+
Storage-level construction: no Face awareness, no promote/check
|
|
909
|
+
beyond ca_stack_check_uniform (data_type + shape uniformity).
|
|
910
|
+
Returns a fresh CAStack VALUE (not a Face-wrapped lift).
|
|
911
|
+
|
|
912
|
+
The Face dance (= homogeneity check + lift) lives in the high-level
|
|
913
|
+
Ruby `CArray.stack` defined in lib/carray/compose.rb, which calls
|
|
914
|
+
`CArray.promote_list` for the homogeneity + state / portability
|
|
915
|
+
verdict and `ca.face_lift(face_parent)` (= rb_ca_face_lift_method)
|
|
916
|
+
for the final re-wrap. Keeping CAStack constructor raw-only means
|
|
917
|
+
`CAStack.new(list, axis:)` follows the Class#new contract (= always
|
|
918
|
+
returns a CAStack), and the lift becomes a transparent Ruby-side
|
|
919
|
+
step rather than a hidden C dispatch surprise. */
|
|
920
|
+
VALUE
|
|
921
|
+
rb_ca_stack_new_with_axis (VALUE parents_ary, int8_t k_axis)
|
|
922
|
+
{
|
|
923
|
+
volatile VALUE obj;
|
|
924
|
+
CAStack *ca;
|
|
925
|
+
CArray **parents;
|
|
926
|
+
volatile VALUE holder;
|
|
927
|
+
long n, i;
|
|
928
|
+
|
|
929
|
+
Check_Type(parents_ary, T_ARRAY);
|
|
930
|
+
n = RARRAY_LEN(parents_ary);
|
|
931
|
+
if ( n <= 0 ) {
|
|
932
|
+
rb_raise(rb_eArgError, "CAStack requires at least one parent");
|
|
933
|
+
}
|
|
934
|
+
parents = ALLOCV_N(CArray *, holder, n);
|
|
935
|
+
for ( i = 0; i < n; i++ ) {
|
|
936
|
+
VALUE p = rb_ary_entry(parents_ary, i);
|
|
937
|
+
rb_check_carray_object(p);
|
|
938
|
+
TypedData_Get_Struct(p, CArray, &carray_data_type, parents[i]);
|
|
939
|
+
}
|
|
940
|
+
ca = ca_stack_new_with_axis((int32_t) n, parents, k_axis);
|
|
941
|
+
obj = ca_wrap_struct(ca);
|
|
942
|
+
rb_ivar_set(obj, id_parents, rb_ary_dup(parents_ary)); /* GC anchor */
|
|
943
|
+
rb_ca_set_parent(obj, ca_stack_face_parent1(rb_ary_entry(parents_ary, 0))); /* CAView base (Face-stripped, matches C ->parent) */
|
|
944
|
+
ALLOCV_END(holder);
|
|
945
|
+
return obj;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
VALUE
|
|
949
|
+
rb_ca_stack_new (VALUE parents_ary)
|
|
950
|
+
{
|
|
951
|
+
return rb_ca_stack_new_with_axis(parents_ary, 0);
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
/* CArray.stack class method + CArray#stack instance method are defined
|
|
955
|
+
in lib/carray/compose.rb as the high-level Ruby surface that delegates
|
|
956
|
+
to CAStack.new after promote_list + (optional) face_lift. CAStack.new
|
|
957
|
+
is the canonical low-level constructor; promote_list handles Face. */
|
|
958
|
+
|
|
959
|
+
static VALUE
|
|
960
|
+
rb_ca_stack_s_allocate (VALUE klass)
|
|
961
|
+
{
|
|
962
|
+
CAStack *ca;
|
|
963
|
+
return TypedData_Make_Struct(klass, CAStack, &castack_data_type, ca);
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
static VALUE
|
|
967
|
+
rb_ca_stack_initialize_copy (VALUE self, VALUE other)
|
|
968
|
+
{
|
|
969
|
+
CAStack *ca, *cs;
|
|
970
|
+
TypedData_Get_Struct(self, CAStack, &castack_data_type, ca);
|
|
971
|
+
TypedData_Get_Struct(other, CAStack, &castack_data_type, cs);
|
|
972
|
+
ca_stack_setup_with_axis(ca, cs->n_parents, cs->parents, cs->k_axis);
|
|
973
|
+
return self;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
/* CAStack#initialize(list, axis: 0) -- the OO constructor entry.
|
|
977
|
+
|
|
978
|
+
Class#new chain: allocate -> initialize. Allocate produces an empty
|
|
979
|
+
TypedData-wrapped CAStack struct; we set up the parents[] / k_axis /
|
|
980
|
+
dim[] in this method.
|
|
981
|
+
|
|
982
|
+
Raw-only: no Face homogeneity check, no lift. The lift is performed
|
|
983
|
+
by the high-level CArray.stack Ruby method (= lib/carray/compose.rb)
|
|
984
|
+
which calls CAStack.new then ca.face_lift(face_parent). */
|
|
985
|
+
static VALUE
|
|
986
|
+
rb_ca_stack_initialize (int argc, VALUE *argv, VALUE self)
|
|
987
|
+
{
|
|
988
|
+
CAStack *ca;
|
|
989
|
+
CArray **parents;
|
|
990
|
+
volatile VALUE holder;
|
|
991
|
+
VALUE list, kwargs, axis_val = Qnil;
|
|
992
|
+
int8_t k_axis = 0;
|
|
993
|
+
long n, i;
|
|
994
|
+
|
|
995
|
+
rb_scan_args(argc, argv, "1:", &list, &kwargs);
|
|
996
|
+
Check_Type(list, T_ARRAY);
|
|
997
|
+
rb_scan_options(kwargs, "axis", &axis_val);
|
|
998
|
+
n = RARRAY_LEN(list);
|
|
999
|
+
if ( n <= 0 ) {
|
|
1000
|
+
rb_raise(rb_eArgError, "CAStack.new requires at least one parent");
|
|
1001
|
+
}
|
|
1002
|
+
if ( ! NIL_P(axis_val) ) {
|
|
1003
|
+
/* Resolve k_axis against the first parent's ndim (= insertion position
|
|
1004
|
+
range [0, parent_ndim], i.e. half-open [0, parent_ndim + 1)). */
|
|
1005
|
+
CArray *ref;
|
|
1006
|
+
VALUE first = rb_ary_entry(list, 0);
|
|
1007
|
+
rb_check_carray_object(first);
|
|
1008
|
+
TypedData_Get_Struct(first, CArray, &carray_data_type, ref);
|
|
1009
|
+
k_axis = (int8_t) rb_ca_normalize_axis_for_ndim(
|
|
1010
|
+
NUM2LONG(axis_val), (int) ref->ndim + 1, "CAStack.new");
|
|
1011
|
+
}
|
|
1012
|
+
TypedData_Get_Struct(self, CAStack, &castack_data_type, ca);
|
|
1013
|
+
parents = ALLOCV_N(CArray *, holder, n);
|
|
1014
|
+
for ( i = 0; i < n; i++ ) {
|
|
1015
|
+
VALUE p = rb_ary_entry(list, i);
|
|
1016
|
+
rb_check_carray_object(p);
|
|
1017
|
+
TypedData_Get_Struct(p, CArray, &carray_data_type, parents[i]);
|
|
1018
|
+
}
|
|
1019
|
+
ca_stack_setup_with_axis(ca, (int32_t) n, parents, k_axis);
|
|
1020
|
+
rb_ivar_set(self, id_parents, rb_ary_dup(list)); /* GC anchor */
|
|
1021
|
+
rb_ca_set_parent(self, ca_stack_face_parent1(rb_ary_entry(list, 0))); /* CAView base (Face-stripped, matches C ->parent) */
|
|
1022
|
+
ALLOCV_END(holder);
|
|
1023
|
+
return self;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
/* CAStack.new(list, axis: 0) -- singleton override of Class#new that
|
|
1027
|
+
layers Face-aware semantics on top of the raw allocate + initialize
|
|
1028
|
+
chain.
|
|
1029
|
+
|
|
1030
|
+
For a homogeneous Face list (= all same Face class with compatible
|
|
1031
|
+
state + portable):
|
|
1032
|
+
- the raw CAStack is built via rb_obj_alloc + rb_obj_call_init
|
|
1033
|
+
(= bypasses the Class#new dispatcher to avoid recursion into
|
|
1034
|
+
this method)
|
|
1035
|
+
- then ca_face_lift re-wraps the result as the same Face class,
|
|
1036
|
+
carrying state from list[0]
|
|
1037
|
+
For non-Face / mixed / heterogeneous lists, returns the raw CAStack
|
|
1038
|
+
directly (= same result as the inherited Class#new chain).
|
|
1039
|
+
|
|
1040
|
+
Replaces the Ruby-level CAStack.new override in lib/carray/compose.rb
|
|
1041
|
+
(= used alias_method :__new_raw__, :new + class << self def new).
|
|
1042
|
+
Consolidating in C removes the dispatch trick and the per-call
|
|
1043
|
+
rb_funcall hops to face_state_portable? / face_state_compatible?. */
|
|
1044
|
+
static VALUE
|
|
1045
|
+
rb_ca_stack_s_new (int argc, VALUE *argv, VALUE klass)
|
|
1046
|
+
{
|
|
1047
|
+
VALUE list, kwargs;
|
|
1048
|
+
long n, i;
|
|
1049
|
+
int all_face = 1;
|
|
1050
|
+
VALUE face_class = Qnil;
|
|
1051
|
+
CArray *ref_face = NULL;
|
|
1052
|
+
VALUE obj;
|
|
1053
|
+
|
|
1054
|
+
rb_scan_args(argc, argv, "1:", &list, &kwargs);
|
|
1055
|
+
Check_Type(list, T_ARRAY);
|
|
1056
|
+
n = RARRAY_LEN(list);
|
|
1057
|
+
if ( n <= 0 ) {
|
|
1058
|
+
rb_raise(rb_eArgError, "CAStack.new requires at least one parent");
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
/* Inspect Face homogeneity in one pass. Bail to non-Face path as
|
|
1062
|
+
soon as a non-Face or class-mismatched element is seen. */
|
|
1063
|
+
for ( i = 0; i < n; i++ ) {
|
|
1064
|
+
VALUE p = rb_ary_entry(list, i);
|
|
1065
|
+
CArray *ca;
|
|
1066
|
+
rb_check_carray_object(p);
|
|
1067
|
+
TypedData_Get_Struct(p, CArray, &carray_data_type, ca);
|
|
1068
|
+
if ( !ca_is_face(ca) ) { all_face = 0; break; }
|
|
1069
|
+
if ( i == 0 ) {
|
|
1070
|
+
face_class = rb_obj_class(p);
|
|
1071
|
+
ref_face = ca;
|
|
1072
|
+
} else if ( rb_obj_class(p) != face_class ) {
|
|
1073
|
+
all_face = 0; break;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
/* Allocate + initialize raw CAStack. rb_obj_alloc calls the
|
|
1078
|
+
TypedData allocator directly (= bypasses the overridden Class#new,
|
|
1079
|
+
avoiding infinite recursion); rb_obj_call_init_kw dispatches to
|
|
1080
|
+
#initialize forwarding the kwargs hash (= Ruby 3.x strict kwarg
|
|
1081
|
+
separation requires the _kw variant when passing through). */
|
|
1082
|
+
obj = rb_obj_alloc(klass);
|
|
1083
|
+
rb_obj_call_init_kw(obj, argc, argv, RB_PASS_CALLED_KEYWORDS);
|
|
1084
|
+
|
|
1085
|
+
/* Single element or non-Face: no lift, return raw. */
|
|
1086
|
+
if ( !all_face || n < 2 ) return obj;
|
|
1087
|
+
|
|
1088
|
+
/* Homogeneous Face: portable + pairwise state-compatible checks,
|
|
1089
|
+
then ca_face_lift to re-wrap as the same Face class. */
|
|
1090
|
+
if ( !ca_face_state_portable(ref_face->obj_type, face_class) ) {
|
|
1091
|
+
rb_raise(rb_eArgError,
|
|
1092
|
+
"CAStack.new: %s state is not portable across multiple "
|
|
1093
|
+
"parents (= per-parent storage like CAConstString's buffer); "
|
|
1094
|
+
"strip Face with .parent if a storage-level CAStack is intended",
|
|
1095
|
+
rb_class2name(face_class));
|
|
1096
|
+
}
|
|
1097
|
+
for ( i = 1; i < n; i++ ) {
|
|
1098
|
+
VALUE p = rb_ary_entry(list, i);
|
|
1099
|
+
CArray *ca;
|
|
1100
|
+
TypedData_Get_Struct(p, CArray, &carray_data_type, ca);
|
|
1101
|
+
if ( !ca_face_state_compatible(rb_ary_entry(list, 0), ref_face,
|
|
1102
|
+
p, ca) ) {
|
|
1103
|
+
rb_raise(rb_eArgError,
|
|
1104
|
+
"CAStack.new: Face state mismatch across parents "
|
|
1105
|
+
"(= %s instance at index %ld differs in state from index 0)",
|
|
1106
|
+
rb_class2name(face_class), i);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
return ca_face_lift(obj, rb_ary_entry(list, 0));
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
/* Public Ruby accessor: stack.n_parents → K */
|
|
1113
|
+
static VALUE
|
|
1114
|
+
rb_ca_stack_n_parents (VALUE self)
|
|
1115
|
+
{
|
|
1116
|
+
CAStack *ca = (CAStack *) DATA_PTR(self);
|
|
1117
|
+
return INT2NUM(ca->n_parents);
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
/* Public Ruby accessor: stack.parents → Array of parent CArrays
|
|
1121
|
+
([[project_view_hierarchy_introspectability]]). */
|
|
1122
|
+
static VALUE
|
|
1123
|
+
rb_ca_stack_parents (VALUE self)
|
|
1124
|
+
{
|
|
1125
|
+
return rb_ivar_get(self, id_parents);
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
/* Public Ruby accessor: stack.k_axis → K axis insertion position [0, parent_ndim]. */
|
|
1129
|
+
static VALUE
|
|
1130
|
+
rb_ca_stack_k_axis (VALUE self)
|
|
1131
|
+
{
|
|
1132
|
+
CAStack *ca = (CAStack *) DATA_PTR(self);
|
|
1133
|
+
return INT2NUM((int) ca->k_axis);
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
void
|
|
1137
|
+
Init_ca_obj_stack (void)
|
|
1138
|
+
{
|
|
1139
|
+
/* CAStack must match the CAMultiParent layout convention so generic code
|
|
1140
|
+
can fold over parents[] via the CA_FLAG_MULTI_PARENTS path. */
|
|
1141
|
+
if ( offsetof(CAStack, n_parents) != offsetof(CAMultiParent, n_parents) ||
|
|
1142
|
+
offsetof(CAStack, parents) != offsetof(CAMultiParent, parents) ) {
|
|
1143
|
+
rb_raise(rb_eRuntimeError,
|
|
1144
|
+
"CAStack/CAMultiParent layout mismatch (build error)");
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
rb_cCAStack = rb_define_class("CAStack", rb_cCAView);
|
|
1148
|
+
rb_cCAStackMask = rb_define_class("CAStackMask", rb_cCAStack);
|
|
1149
|
+
|
|
1150
|
+
CA_OBJ_STACK = ca_install_obj_type(rb_cCAStack,
|
|
1151
|
+
&castack_data_type,
|
|
1152
|
+
rb_cCAStackMask,
|
|
1153
|
+
&castack_mask_data_type, &ca_stack_func, sizeof(ca_stack_func));
|
|
1154
|
+
rb_define_const(rb_cObject, "CA_OBJ_STACK", INT2NUM(CA_OBJ_STACK));
|
|
1155
|
+
|
|
1156
|
+
id_parents = rb_intern("parents");
|
|
1157
|
+
|
|
1158
|
+
/* CArray.stack / CArray#stack / CAStack#append are defined in Ruby
|
|
1159
|
+
(lib/carray/compose.rb). CAStack.new is C-side: the allocator +
|
|
1160
|
+
#initialize handle the raw build, and rb_ca_stack_s_new overrides
|
|
1161
|
+
Class#new to layer Face-aware lift on top. */
|
|
1162
|
+
|
|
1163
|
+
rb_define_alloc_func(rb_cCAStack, rb_ca_stack_s_allocate);
|
|
1164
|
+
rb_define_singleton_method(rb_cCAStack, "new", rb_ca_stack_s_new, -1);
|
|
1165
|
+
rb_define_method(rb_cCAStack, "initialize",
|
|
1166
|
+
rb_ca_stack_initialize, -1);
|
|
1167
|
+
rb_define_method(rb_cCAStack, "initialize_copy",
|
|
1168
|
+
rb_ca_stack_initialize_copy, 1);
|
|
1169
|
+
|
|
1170
|
+
rb_define_method(rb_cCAStack, "n_parents", rb_ca_stack_n_parents, 0);
|
|
1171
|
+
rb_define_method(rb_cCAStack, "parents", rb_ca_stack_parents, 0);
|
|
1172
|
+
rb_define_method(rb_cCAStack, "k_axis", rb_ca_stack_k_axis, 0);
|
|
1173
|
+
}
|