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_stride.c
ADDED
|
@@ -0,0 +1,2501 @@
|
|
|
1
|
+
/* ---------------------------------------------------------------------------
|
|
2
|
+
|
|
3
|
+
CAStride: generic strided view array. Holds byte-unit strides
|
|
4
|
+
(negative allowed) and a byte-unit base_offset relative to parent->ptr.
|
|
5
|
+
Two-mode operation:
|
|
6
|
+
|
|
7
|
+
detached (ptr == NULL): each element access computes
|
|
8
|
+
parent->ptr + base_offset + sum_k(idx[k] * strides[k])
|
|
9
|
+
|
|
10
|
+
attached (ptr != NULL): own contiguous row-major buffer, populated
|
|
11
|
+
by gather copy on attach, scattered back on sync, freed on
|
|
12
|
+
final detach.
|
|
13
|
+
|
|
14
|
+
This is the whole strided-view family: CARefer and CABlock carry a tail
|
|
15
|
+
of their own on top of this prefix, CATranspose / CAFarray / CARepeat /
|
|
16
|
+
CAField are plain typedefs of it. All of them inherit the operation
|
|
17
|
+
table below unchanged, so a fast path added here reaches every one.
|
|
18
|
+
devel/CAStride.md is the reference for writing a subclass.
|
|
19
|
+
|
|
20
|
+
---------------------------------------------------------------------------- */
|
|
21
|
+
|
|
22
|
+
#include "carray.h"
|
|
23
|
+
#include "ca_iter_substrate.h"
|
|
24
|
+
#include "ca_obj_face.h" /* ca_is_face, used by the compose-fold walk */
|
|
25
|
+
|
|
26
|
+
static size_t
|
|
27
|
+
ca_stride_dsize (const void *ap)
|
|
28
|
+
{
|
|
29
|
+
const CAStride *ca = (const CAStride *) ap;
|
|
30
|
+
/* dim and strides are each ALLOC_N(ndim) (legacy) or wired into the
|
|
31
|
+
framework-managed _pool buffer (pool path). Either way the live byte
|
|
32
|
+
accounting is the struct plus 2*ndim ca_size_t cells. */
|
|
33
|
+
return sizeof(CAStride) + 2 * ca->ndim * sizeof(ca_size_t);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* Pool framework hooks.
|
|
37
|
+
When ca_func[obj_type].pool_init runs against the buffer allocated by
|
|
38
|
+
ca_array_pool_alloc, dim and strides are wired into a single contiguous
|
|
39
|
+
region instead of taking two separate ALLOC_N calls. The legacy
|
|
40
|
+
ALLOC_N path stays available for any obj_type that has not registered
|
|
41
|
+
these hooks yet (= ca->_pool stays NULL through setup). */
|
|
42
|
+
static size_t
|
|
43
|
+
ca_stride_pool_bytes (int8_t ndim)
|
|
44
|
+
{
|
|
45
|
+
ca_size_t n = (ndim > 0) ? ndim : 1;
|
|
46
|
+
return 2 * (size_t) n * sizeof(ca_size_t);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
static void
|
|
50
|
+
ca_stride_pool_init (void *ap, int8_t ndim)
|
|
51
|
+
{
|
|
52
|
+
CAStride *ca = (CAStride *) ap;
|
|
53
|
+
ca_size_t n = (ndim > 0) ? ndim : 1;
|
|
54
|
+
ca_size_t *base = (ca_size_t *) ca->_pool;
|
|
55
|
+
ca->dim = base + 0 * n;
|
|
56
|
+
ca->strides = base + 1 * n;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const rb_data_type_t castride_data_type = {
|
|
60
|
+
.parent = &caview_data_type,
|
|
61
|
+
.wrap_struct_name = "CAStride",
|
|
62
|
+
.function = {
|
|
63
|
+
.dmark = ca_mark,
|
|
64
|
+
.dfree = ca_free,
|
|
65
|
+
.dsize = ca_stride_dsize,
|
|
66
|
+
.dcompact = NULL
|
|
67
|
+
},
|
|
68
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const rb_data_type_t castride_mask_data_type = {
|
|
72
|
+
.parent = &castride_data_type,
|
|
73
|
+
.wrap_struct_name = "CAStrideMask",
|
|
74
|
+
.function = {
|
|
75
|
+
.dmark = NULL,
|
|
76
|
+
.dfree = ca_free_nop,
|
|
77
|
+
.dsize = ca_stride_dsize,
|
|
78
|
+
.dcompact = NULL
|
|
79
|
+
},
|
|
80
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
VALUE rb_cCAStride;
|
|
84
|
+
VALUE rb_cCAStrideMask;
|
|
85
|
+
int8_t CA_OBJ_STRIDE; /* assigned at Init time via ca_install_obj_type */
|
|
86
|
+
|
|
87
|
+
/* ------------------------------------------------------------------- */
|
|
88
|
+
|
|
89
|
+
/* `obj_type` is the dispatch tag stored in ca->obj_type. Pass
|
|
90
|
+
CA_OBJ_STRIDE when constructing a plain CAStride; pass the
|
|
91
|
+
subclass's own obj_type (e.g. CA_OBJ_TRANSPOSE) when used from a
|
|
92
|
+
subclass setup -- this avoids the "stamp it again after setup"
|
|
93
|
+
override pattern and makes the C-level dispatch wire up correctly
|
|
94
|
+
on the first try. */
|
|
95
|
+
int
|
|
96
|
+
ca_stride_setup (CAStride *ca, int8_t obj_type, CArray *parent,
|
|
97
|
+
int8_t data_type, ca_size_t bytes,
|
|
98
|
+
int8_t ndim, ca_size_t *dim,
|
|
99
|
+
ca_size_t *strides, ca_size_t base_offset)
|
|
100
|
+
{
|
|
101
|
+
ca_size_t elements;
|
|
102
|
+
int i;
|
|
103
|
+
|
|
104
|
+
if (ndim < 0 || ndim > CA_RANK_MAX) {
|
|
105
|
+
rb_raise(rb_eArgError, "invalid ndim %d", (int) ndim);
|
|
106
|
+
}
|
|
107
|
+
elements = 1;
|
|
108
|
+
for (i = 0; i < ndim; i++) {
|
|
109
|
+
if (dim[i] < 0) {
|
|
110
|
+
rb_raise(rb_eIndexError,
|
|
111
|
+
"invalid size for %i-th dimension (negative)", i);
|
|
112
|
+
}
|
|
113
|
+
elements *= dim[i];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
ca->obj_type = obj_type;
|
|
117
|
+
ca->data_type = data_type;
|
|
118
|
+
ca->flags = 0;
|
|
119
|
+
ca->ndim = ndim;
|
|
120
|
+
ca->bytes = bytes;
|
|
121
|
+
ca->elements = elements;
|
|
122
|
+
ca->ptr = NULL;
|
|
123
|
+
ca->mask = NULL;
|
|
124
|
+
ca->parent = parent;
|
|
125
|
+
ca->attach = 0;
|
|
126
|
+
ca->nosync = 0;
|
|
127
|
+
if ( ! ca->_pool ) {
|
|
128
|
+
/* Legacy path: caller used ALLOC(CAStride) without ca_array_alloc,
|
|
129
|
+
so dim/strides need their own backing. Pool path callers have
|
|
130
|
+
these already wired by ca_stride_pool_init. */
|
|
131
|
+
ca->dim = ALLOC_N(ca_size_t, ndim > 0 ? ndim : 1);
|
|
132
|
+
ca->strides = ALLOC_N(ca_size_t, ndim > 0 ? ndim : 1);
|
|
133
|
+
}
|
|
134
|
+
ca->base_offset = base_offset;
|
|
135
|
+
|
|
136
|
+
for (i = 0; i < ndim; i++) {
|
|
137
|
+
ca->dim[i] = dim[i];
|
|
138
|
+
ca->strides[i] = strides[i];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (parent && ca_has_mask(parent)) {
|
|
142
|
+
ca_create_mask(ca);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return 0;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
CAStride *
|
|
149
|
+
ca_stride_new (int8_t obj_type, CArray *parent,
|
|
150
|
+
int8_t data_type, ca_size_t bytes,
|
|
151
|
+
int8_t ndim, ca_size_t *dim,
|
|
152
|
+
ca_size_t *strides, ca_size_t base_offset)
|
|
153
|
+
{
|
|
154
|
+
CAStride *ca = (CAStride *) ca_array_alloc(obj_type, ndim);
|
|
155
|
+
ca_stride_setup(ca, obj_type, parent,
|
|
156
|
+
data_type, bytes, ndim, dim, strides, base_offset);
|
|
157
|
+
return ca;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
static void
|
|
161
|
+
free_ca_stride (void *ap)
|
|
162
|
+
{
|
|
163
|
+
CAStride *ca = (CAStride *) ap;
|
|
164
|
+
if (ca == NULL) return;
|
|
165
|
+
ca_free(ca->mask);
|
|
166
|
+
if (ca->_pool) {
|
|
167
|
+
/* Pool path: one xfree covers dim/strides via the pool buffer,
|
|
168
|
+
another covers the struct. */
|
|
169
|
+
ca_array_free(ca);
|
|
170
|
+
} else {
|
|
171
|
+
/* Legacy path: free dim/strides individually. */
|
|
172
|
+
xfree(ca->dim);
|
|
173
|
+
xfree(ca->strides);
|
|
174
|
+
xfree(ca);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
static int ca_layout_is_contiguous (int8_t ndim, const ca_size_t *dim,
|
|
179
|
+
const ca_size_t *strides, ca_size_t bytes);
|
|
180
|
+
/* ca_stride_xfer_with_layout / ca_stride_merge_axes are declared in
|
|
181
|
+
ca_iter_substrate.h — the kernel iterator drives them too. */
|
|
182
|
+
|
|
183
|
+
/* Forward decl: ca_stride_func defined later in the file. */
|
|
184
|
+
extern ca_operation_function_t ca_stride_func;
|
|
185
|
+
|
|
186
|
+
/* Shared cache-tiled transpose helper, defined in carray_core.c. Called by
|
|
187
|
+
three paths: the central dispatcher's ptr path, our root-direct memcpy
|
|
188
|
+
path, and ca_stride_xfer_with_layout's tile branch. Takes a raw src base
|
|
189
|
+
ptr and bytes so all three callers can pass whatever ptr + bytes pair they
|
|
190
|
+
already hold. */
|
|
191
|
+
void ca_xfer_stride_tiled_transpose_2d (char *src_base, ca_size_t bytes,
|
|
192
|
+
ca_size_t *counts, ca_size_t *strides,
|
|
193
|
+
char *data, int dir);
|
|
194
|
+
|
|
195
|
+
/* Compose `leaf->strides` and `leaf->base_offset` (which live in `parent`'s
|
|
196
|
+
own logical row-major contig byte space) into `out_strides` and
|
|
197
|
+
`out_base` expressed in `parent->parent`'s byte space.
|
|
198
|
+
Both `leaf` and `parent` are CAStride views; `leaf->parent == parent`.
|
|
199
|
+
Returns 1 on clean decomposition, 0 on failure (a stride that does not
|
|
200
|
+
align with parent's logical layout -- e.g. a synthetic stride that
|
|
201
|
+
crosses parent dim boundaries non-aligned). On 0, caller falls back to
|
|
202
|
+
materialise-parent path. */
|
|
203
|
+
int
|
|
204
|
+
ca_stride_compose_through (CAStride *leaf, CAStride *parent,
|
|
205
|
+
ca_size_t *out_strides, ca_size_t *out_base)
|
|
206
|
+
{
|
|
207
|
+
ca_size_t prod[CA_RANK_MAX + 1];
|
|
208
|
+
ca_size_t base_idx[CA_RANK_MAX]; /* base position in each parent dim */
|
|
209
|
+
int8_t k, m;
|
|
210
|
+
|
|
211
|
+
/* prod[k] = product of parent->dim[k..ndim-1] (in elements) */
|
|
212
|
+
prod[parent->ndim] = 1;
|
|
213
|
+
for (k = parent->ndim - 1; k >= 0; k--)
|
|
214
|
+
prod[k] = prod[k + 1] * parent->dim[k];
|
|
215
|
+
|
|
216
|
+
/* Compose base offset first. We need the per-dim base position
|
|
217
|
+
to validate stride composition against parent dim bounds (a small
|
|
218
|
+
forward stride starting near the end of a parent dim wraps into
|
|
219
|
+
the next dim with the wrong stride; the bounds check below needs
|
|
220
|
+
to know where in the dim we start).
|
|
221
|
+
|
|
222
|
+
Sub-element offset (= leaf->base_offset % parent->bytes != 0) is
|
|
223
|
+
captured into `sub_byte` and folded into out_base at the end,
|
|
224
|
+
rather than rejected. This handles CAField over CAStride family
|
|
225
|
+
(= second-or-later field of a multi-field record) and CARefer
|
|
226
|
+
byte-reinterpret + offset patterns. Per-cell memcpy in the hot
|
|
227
|
+
path uses LEAF's bytes (= field width), so a non-parent-aligned
|
|
228
|
+
base is correct. */
|
|
229
|
+
ca_size_t flat = leaf->base_offset / parent->bytes;
|
|
230
|
+
ca_size_t sub_byte = leaf->base_offset % parent->bytes;
|
|
231
|
+
ca_size_t base = parent->base_offset;
|
|
232
|
+
for (m = 0; m < parent->ndim; m++) {
|
|
233
|
+
base_idx[m] = flat / prod[m + 1];
|
|
234
|
+
flat -= base_idx[m] * prod[m + 1];
|
|
235
|
+
if (base_idx[m] >= parent->dim[m]) return 0;
|
|
236
|
+
base += base_idx[m] * parent->strides[m];
|
|
237
|
+
}
|
|
238
|
+
if (flat != 0) return 0;
|
|
239
|
+
*out_base = base + sub_byte;
|
|
240
|
+
|
|
241
|
+
/* Compose each leaf dim's stride.
|
|
242
|
+
Validity rule: leaf dim k must advance exactly one parent dim
|
|
243
|
+
(not cross multiple parent dims) AND stay within that parent
|
|
244
|
+
dim's bounds across leaf's full extent *given the base position*.
|
|
245
|
+
Otherwise the leaf's traversal would wrap across parent dim
|
|
246
|
+
boundaries, which is a non-strided access pattern that cannot
|
|
247
|
+
be folded. */
|
|
248
|
+
for (k = 0; k < leaf->ndim; k++) {
|
|
249
|
+
if (leaf->strides[k] % parent->bytes != 0) return 0;
|
|
250
|
+
ca_size_t advance = leaf->strides[k] / parent->bytes;
|
|
251
|
+
ca_size_t composed = 0;
|
|
252
|
+
int nonzero_count = 0;
|
|
253
|
+
int nonzero_dim = -1;
|
|
254
|
+
ca_size_t nonzero_step = 0;
|
|
255
|
+
for (m = 0; m < parent->ndim; m++) {
|
|
256
|
+
ca_size_t step = advance / prod[m + 1];
|
|
257
|
+
advance -= step * prod[m + 1];
|
|
258
|
+
composed += step * parent->strides[m];
|
|
259
|
+
if (step != 0) {
|
|
260
|
+
nonzero_count++;
|
|
261
|
+
nonzero_dim = m;
|
|
262
|
+
nonzero_step = step;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (advance != 0) return 0;
|
|
266
|
+
if (nonzero_count > 1) return 0; /* crosses parent dims */
|
|
267
|
+
if (nonzero_count == 1) {
|
|
268
|
+
/* Final position in parent dim after the full leaf extent.
|
|
269
|
+
For a forward step it must stay strictly below dim; for a
|
|
270
|
+
backward step it must stay at or above 0. */
|
|
271
|
+
ca_size_t final_pos =
|
|
272
|
+
base_idx[nonzero_dim] + (leaf->dim[k] - 1) * nonzero_step;
|
|
273
|
+
if (nonzero_step > 0) {
|
|
274
|
+
if (final_pos >= parent->dim[nonzero_dim]) return 0;
|
|
275
|
+
} else {
|
|
276
|
+
if (final_pos < 0) return 0;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
out_strides[k] = composed;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return 1;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/* Walk up the CAStride chain composing strides and base_offset until we
|
|
286
|
+
reach a non-CAStride parent (entity or non-stride view like CAReduce).
|
|
287
|
+
`out_strides` and `out_base` describe the leaf's element layout in
|
|
288
|
+
*out_root's ptr-byte space.
|
|
289
|
+
On any composition failure (non-aligned stride), returns the deepest
|
|
290
|
+
successfully-composed root (which may be the immediate parent or an
|
|
291
|
+
intermediate). The chain is always foldable for at least one step in
|
|
292
|
+
theory; the conservative return is the parent itself.
|
|
293
|
+
Caller must ca_attach(*out_root) if !ca_is_attached(*out_root) before
|
|
294
|
+
reading from (*out_root)->ptr + *out_base. Composition writes into
|
|
295
|
+
the provided ndim-sized out_strides buffer. */
|
|
296
|
+
/* CAWindow operation table, read by the fill_data wasted-gather gate below
|
|
297
|
+
to recognise a CAWindow root. Compose-fold itself does not special-case
|
|
298
|
+
CAWindow: that lives in CAWindow's own fold_stride slot. */
|
|
299
|
+
extern ca_operation_function_t ca_window_func;
|
|
300
|
+
|
|
301
|
+
/* Hybrid compose-fold walk. Two kinds of participant fold a leaf's stride coordinates one hop closer to the root:
|
|
302
|
+
|
|
303
|
+
- CAStride family: recognised open-inline by func-pointer comparison
|
|
304
|
+
(ca_func[obj_type].attach == ca_stride_func.attach), composed via
|
|
305
|
+
ca_stride_compose_through (stride machinery's own self-knowledge).
|
|
306
|
+
- sometimes-fold participants (CAWindow now; CAGrid/CSA/CATile later):
|
|
307
|
+
dispatched through the fold_stride operation slot, which composes the
|
|
308
|
+
fold state into the next parent's space or declines (-> boundary).
|
|
309
|
+
|
|
310
|
+
No view names beyond the CAStride family appear here; new participants
|
|
311
|
+
join by implementing fold_stride (open/closed principle on the foreign
|
|
312
|
+
axis). No new flags: the CAStride family is detected by attach-pointer
|
|
313
|
+
identity, fold participation by fold_stride != NULL. */
|
|
314
|
+
void
|
|
315
|
+
ca_stride_compose_to_root (CAStride *leaf,
|
|
316
|
+
CArray **out_root,
|
|
317
|
+
ca_size_t *out_strides,
|
|
318
|
+
ca_size_t *out_base)
|
|
319
|
+
{
|
|
320
|
+
ca_fold_t f;
|
|
321
|
+
CArray *cur = leaf->parent;
|
|
322
|
+
int8_t k;
|
|
323
|
+
|
|
324
|
+
f.ndim = leaf->ndim;
|
|
325
|
+
f.base = leaf->base_offset;
|
|
326
|
+
for (k = 0; k < leaf->ndim; k++) {
|
|
327
|
+
f.strides[k] = leaf->strides[k];
|
|
328
|
+
f.counts[k] = leaf->dim[k]; /* extent, used by compose-through bounds */
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
while (1) {
|
|
332
|
+
if (ca_func[cur->obj_type].attach == ca_stride_func.attach) {
|
|
333
|
+
/* OPEN: CAStride family. Compose f (leaf-in-cur-space) through the
|
|
334
|
+
CAStride parent into cur->parent's space. */
|
|
335
|
+
CAStride *p = (CAStride *) cur;
|
|
336
|
+
CAStride tmp;
|
|
337
|
+
ca_size_t next_strides[CA_RANK_MAX];
|
|
338
|
+
ca_size_t next_base;
|
|
339
|
+
tmp.ndim = f.ndim;
|
|
340
|
+
tmp.bytes = leaf->bytes;
|
|
341
|
+
tmp.dim = f.counts; /* extent in cur space */
|
|
342
|
+
tmp.strides = f.strides;
|
|
343
|
+
tmp.base_offset = f.base;
|
|
344
|
+
if (!ca_stride_compose_through(&tmp, p, next_strides, &next_base)) {
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
for (k = 0; k < f.ndim; k++) f.strides[k] = next_strides[k];
|
|
348
|
+
f.base = next_base;
|
|
349
|
+
cur = p->parent;
|
|
350
|
+
}
|
|
351
|
+
else if (ca_func[cur->obj_type].fold_stride) {
|
|
352
|
+
/* DUCK: sometimes-fold participant. It composes f and advances, or
|
|
353
|
+
declines (-> cur is the fold boundary). */
|
|
354
|
+
void *next;
|
|
355
|
+
if (!ca_func[cur->obj_type].fold_stride(cur, &f, &next)) {
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
cur = (CArray *) next;
|
|
359
|
+
}
|
|
360
|
+
else if (ca_is_face(cur)) {
|
|
361
|
+
/* Face is layout-identity over its parent (= byte-for-byte alias via
|
|
362
|
+
ca_face_attach, same data_type / bytes / strides). Walk through
|
|
363
|
+
as an identity step so the composed (strides, base) carry into
|
|
364
|
+
parent's space unchanged. Without this, compose stops at Face
|
|
365
|
+
and partial materialise / Face xfer_stride delegate paths re-
|
|
366
|
+
enter the dispatcher with the ROOT's bytes interpretation
|
|
367
|
+
(= entity FIXLEN bytes) instead of the LEAF's bytes (= e.g. f64
|
|
368
|
+
field width), causing a heap buffer overflow + wrong-value bulk
|
|
369
|
+
gather (= reporter's bug: CARecord chain + CAField bulk path). */
|
|
370
|
+
cur = ((CAView *) cur)->parent;
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
break; /* boundary: cur is the root we expose */
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
*out_root = cur;
|
|
378
|
+
*out_base = f.base;
|
|
379
|
+
for (k = 0; k < leaf->ndim; k++) out_strides[k] = f.strides[k];
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/* Resolves a candidate parent through identity CAStride compose-fold to find
|
|
383
|
+
an attached root.
|
|
384
|
+
Returns the resolved CArray (or the original `cand` if cand already has
|
|
385
|
+
ptr, isn't CAStride family, or doesn't identity-compose to a ptr-bearing
|
|
386
|
+
root). Used by view xfer_addrs slots (CSA / CAGrid / CASelect) to lift
|
|
387
|
+
the parent->ptr gate through view CAStride layers when the compose
|
|
388
|
+
is element-mapping identity (= simple reshape / alias).
|
|
389
|
+
|
|
390
|
+
"Identity compose-fold" semantics:
|
|
391
|
+
- cand->bytes == root->bytes (no byte reinterpret)
|
|
392
|
+
- composed_base == 0
|
|
393
|
+
- composed_strides[k] match row-major over cand->dim with cand->bytes
|
|
394
|
+
|
|
395
|
+
When true, the cand's flat byte addressing equals root->ptr's flat byte
|
|
396
|
+
addressing for the first `cand->elements * cand->bytes` bytes. The
|
|
397
|
+
axis_dispatch engine can then use root->ptr as parent->ptr directly
|
|
398
|
+
(with cand's logical shape passed via parent_axis_dims unchanged).
|
|
399
|
+
|
|
400
|
+
This unblocks the chain pattern a.flatten[idx].reshape(*idx.shape) where
|
|
401
|
+
intermediate CARefer layers are view (no explicit attach) but
|
|
402
|
+
element-identity-aliased to the leaf entity. */
|
|
403
|
+
CArray *
|
|
404
|
+
ca_resolve_attached_root_via_identity (CArray *cand)
|
|
405
|
+
{
|
|
406
|
+
CAStride *cs;
|
|
407
|
+
CArray *root;
|
|
408
|
+
ca_size_t composed_strides[CA_RANK_MAX];
|
|
409
|
+
ca_size_t composed_base;
|
|
410
|
+
ca_size_t s;
|
|
411
|
+
int8_t k;
|
|
412
|
+
|
|
413
|
+
if ( cand->ptr ) return cand;
|
|
414
|
+
if ( ca_func[cand->obj_type].attach != ca_stride_func.attach ) return cand;
|
|
415
|
+
|
|
416
|
+
cs = (CAStride *) cand;
|
|
417
|
+
ca_stride_compose_to_root(cs, &root, composed_strides, &composed_base);
|
|
418
|
+
if ( !root->ptr ) return cand;
|
|
419
|
+
if ( cs->bytes != root->bytes ) return cand;
|
|
420
|
+
if ( composed_base != 0 ) return cand;
|
|
421
|
+
|
|
422
|
+
s = root->bytes;
|
|
423
|
+
for ( k = cs->ndim - 1; k >= 0; k-- ) {
|
|
424
|
+
if ( composed_strides[k] != s ) return cand;
|
|
425
|
+
s *= cs->dim[k];
|
|
426
|
+
}
|
|
427
|
+
return root;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/* ------------------------------------------------------------------- */
|
|
431
|
+
|
|
432
|
+
static void *
|
|
433
|
+
ca_stride_func_clone (void *ap)
|
|
434
|
+
{
|
|
435
|
+
CAStride *ca = (CAStride *) ap;
|
|
436
|
+
/* Preserve the subclass identity (CATranspose, etc.) by reusing
|
|
437
|
+
the source's obj_type rather than hardcoding CA_OBJ_STRIDE. */
|
|
438
|
+
return ca_stride_new(ca->obj_type, ca->parent, ca->data_type, ca->bytes,
|
|
439
|
+
ca->ndim, ca->dim, ca->strides, ca->base_offset);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/* CAREFUL: per-cell access paths must not call ca_attach on any ancestor.
|
|
443
|
+
When ca->ptr is NULL the cell is delegated one hop to the parent instead;
|
|
444
|
+
attaching would materialise the whole parent to read one cell (ruinous for
|
|
445
|
+
a non-trivial chain) and would hand back a dangling pointer after the
|
|
446
|
+
matching ca_detach. The delegation recurses and bottoms out at an entity,
|
|
447
|
+
whose ptr is always live.
|
|
448
|
+
|
|
449
|
+
Byte-offset arithmetic: leaf's strides are in bytes relative to
|
|
450
|
+
parent's ptr space. off = base_offset + Σ idx[k]*strides[k] is the
|
|
451
|
+
byte offset into parent. Split into (addr, sub) = (off / parent.bytes,
|
|
452
|
+
off % parent.bytes); parent.ptr_at_addr(addr) returns the pointer to
|
|
453
|
+
parent's cell, and sub handles the byte-mismatched reinterpret case
|
|
454
|
+
(= CAField .real / .imag over complex). */
|
|
455
|
+
|
|
456
|
+
/* Direction-unified per-cell transfer. Shares the offset computation; the alias / attached-parent cases differ only
|
|
457
|
+
by memcpy direction, the non-attached delegate path branches GET/PUT. */
|
|
458
|
+
static void
|
|
459
|
+
ca_stride_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
|
|
460
|
+
{
|
|
461
|
+
CAStride *ca = (CAStride *) ap;
|
|
462
|
+
ca_size_t off;
|
|
463
|
+
int8_t k;
|
|
464
|
+
|
|
465
|
+
if (ca->ptr) {
|
|
466
|
+
/* Attached: row-major direct address (ca_index2addr inlined into the
|
|
467
|
+
loop below for symmetry with the un-attached branches). */
|
|
468
|
+
ca_size_t addr = ca_index2addr((CArray *) ca, idx);
|
|
469
|
+
char *p = ca->ptr + ca->bytes * addr;
|
|
470
|
+
if (dir == CA_XFER_GET) memcpy(data, p, ca->bytes);
|
|
471
|
+
else memcpy(p, data, ca->bytes);
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
off = ca->base_offset;
|
|
476
|
+
for (k = 0; k < ca->ndim; k++) {
|
|
477
|
+
off += idx[k] * ca->strides[k];
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (ca_is_attached(ca->parent)) {
|
|
481
|
+
char *p = ca->parent->ptr + off;
|
|
482
|
+
if (dir == CA_XFER_GET) memcpy(data, p, ca->bytes);
|
|
483
|
+
else memcpy(p, data, ca->bytes);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/* Parent not attached: delegate via the public addr dispatchers (no attach).
|
|
488
|
+
Handles per-view transforms and byte-mismatched reinterpret (CAField). */
|
|
489
|
+
{
|
|
490
|
+
ca_size_t pbytes = ca->parent->bytes;
|
|
491
|
+
if (ca->bytes == pbytes && off % pbytes == 0) {
|
|
492
|
+
/* Aligned single-cell delegate: use the parent's INDEX path, not the
|
|
493
|
+
addr path. ca_fetch_addr/ca_store_addr route through ca_xfer_addrs,
|
|
494
|
+
which for a multi-region parent (CAStack) does an O(K) bucket scan +
|
|
495
|
+
per-call ALLOCV -- an O(K)-per-cell catastrophe for per-cell access
|
|
496
|
+
over CAStride-over-CAStack. addr2index + xfer_index is O(ndim). */
|
|
497
|
+
ca_size_t pidx[CA_RANK_MAX];
|
|
498
|
+
ca_addr2index(ca->parent, off / pbytes, pidx);
|
|
499
|
+
ca_xfer_index(ca->parent, pidx, data, dir);
|
|
500
|
+
}
|
|
501
|
+
else {
|
|
502
|
+
char buf[64]; /* parent cell width <= 16 in practice */
|
|
503
|
+
char *scratch = (pbytes <= (ca_size_t) sizeof(buf)) ? buf : xmalloc(pbytes);
|
|
504
|
+
if (dir == CA_XFER_GET) {
|
|
505
|
+
ca_fetch_addr(ca->parent, off / pbytes, scratch);
|
|
506
|
+
memcpy(data, scratch + (off % pbytes), ca->bytes);
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
/* read-modify-write the parent cell for sub-byte reinterpret */
|
|
510
|
+
ca_fetch_addr(ca->parent, off / pbytes, scratch);
|
|
511
|
+
memcpy(scratch + (off % pbytes), data, ca->bytes);
|
|
512
|
+
ca_store_addr(ca->parent, off / pbytes, scratch);
|
|
513
|
+
}
|
|
514
|
+
if (scratch != buf) xfree(scratch);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/* Batched address gather/scatter.
|
|
520
|
+
|
|
521
|
+
Reached only when ca->ptr == NULL (the central dispatcher handles the
|
|
522
|
+
alias / attached / entity case with a direct memcpy fast path). Compose
|
|
523
|
+
the whole CAStride chain to its root ONCE, translate every addr to the
|
|
524
|
+
root's flat element address with affine arithmetic, then hand the whole
|
|
525
|
+
list to the root in a SINGLE ca_xfer_addrs call -- no whole-view attach,
|
|
526
|
+
no per-cell view dispatch through the intermediate views.
|
|
527
|
+
|
|
528
|
+
The root may itself be a non-foldable view (e.g. CASelect, CAFake); the
|
|
529
|
+
recursive ca_xfer_addrs call lets that view translate one more hop. The
|
|
530
|
+
recursion bottoms at an entity whose ptr is live.
|
|
531
|
+
|
|
532
|
+
Byte-mismatched reinterpret (CAField .real/.imag over complex, where
|
|
533
|
+
ca->bytes != root->bytes or the byte offset is not a multiple of the
|
|
534
|
+
root cell) cannot be expressed as a flat root address, so those cells
|
|
535
|
+
fall back to the per-cell xfer_index delegate (which also avoids attach). */
|
|
536
|
+
static void
|
|
537
|
+
ca_stride_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
|
|
538
|
+
void *data, int dir)
|
|
539
|
+
{
|
|
540
|
+
CAStride *ca = (CAStride *) ap;
|
|
541
|
+
CArray *root;
|
|
542
|
+
ca_size_t composed_strides[CA_RANK_MAX];
|
|
543
|
+
ca_size_t composed_base;
|
|
544
|
+
ca_size_t rbytes;
|
|
545
|
+
ca_size_t *paddrs;
|
|
546
|
+
ca_size_t i, base;
|
|
547
|
+
int8_t k;
|
|
548
|
+
int all_aligned = 1;
|
|
549
|
+
volatile VALUE holder;
|
|
550
|
+
char *d = (char *) data;
|
|
551
|
+
|
|
552
|
+
ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
|
|
553
|
+
rbytes = root->bytes;
|
|
554
|
+
|
|
555
|
+
/* Fast path: identity transform detection -- when the addr remap composed_base +
|
|
556
|
+
Σ idx[k]*composed_strides[k] reduces to an identity mapping over the
|
|
557
|
+
root's flat byte space (= simple reshape, same bytes, row-major
|
|
558
|
+
composed_strides), forward addrs as-is to root without the ALLOCV +
|
|
559
|
+
per-cell remap loop. Cascades through chain a[idx_2d] (= outer
|
|
560
|
+
reshape -> CAGrid -> inner reshape -> entity): outer CARefer is a
|
|
561
|
+
simple reshape so addrs pass through, then CAGrid Y.1.b fast path
|
|
562
|
+
triggers on the recursive call, then inner CARefer simple reshape
|
|
563
|
+
pass-through to entity. Detection is O(ndim) + O(n). */
|
|
564
|
+
if ( n == ca->elements
|
|
565
|
+
&& ca->bytes == rbytes && composed_base == 0
|
|
566
|
+
&& ca_xfer_addrs_is_sequential_run(n, addrs, &base) && base == 0 ) {
|
|
567
|
+
ca_size_t s = rbytes;
|
|
568
|
+
int is_identity = 1;
|
|
569
|
+
for ( k = ca->ndim - 1; k >= 0; k-- ) {
|
|
570
|
+
if ( composed_strides[k] != s ) { is_identity = 0; break; }
|
|
571
|
+
s *= ca->dim[k];
|
|
572
|
+
}
|
|
573
|
+
if ( is_identity ) {
|
|
574
|
+
ca_xfer_addrs(root, n, addrs, data, dir);
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
paddrs = ALLOCV_N(ca_size_t, holder, n);
|
|
580
|
+
for ( i = 0; i < n; i++ ) {
|
|
581
|
+
ca_size_t idx[CA_RANK_MAX];
|
|
582
|
+
ca_size_t off = composed_base;
|
|
583
|
+
ca_addr2index((CArray *) ca, addrs[i], idx);
|
|
584
|
+
for ( k = 0; k < ca->ndim; k++ ) {
|
|
585
|
+
off += idx[k] * composed_strides[k];
|
|
586
|
+
}
|
|
587
|
+
if ( ca->bytes == rbytes && off % rbytes == 0 ) {
|
|
588
|
+
paddrs[i] = off / rbytes;
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
591
|
+
all_aligned = 0;
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
if ( all_aligned ) {
|
|
597
|
+
ca_xfer_addrs(root, n, paddrs, data, dir);
|
|
598
|
+
}
|
|
599
|
+
else {
|
|
600
|
+
/* byte-mismatched reinterpret: deliver cell by cell via xfer_index
|
|
601
|
+
(still no whole-view attach -- delegates one cell at a time). */
|
|
602
|
+
for ( i = 0; i < n; i++ ) {
|
|
603
|
+
ca_size_t idx[CA_RANK_MAX];
|
|
604
|
+
ca_addr2index((CArray *) ca, addrs[i], idx);
|
|
605
|
+
ca_stride_func_xfer_index(ca, idx, d + i * ca->bytes, dir);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
ALLOCV_END(holder);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/* Optimised region delivery. Reached when ca->ptr == NULL (the central dispatcher handles the alias / attached case).
|
|
612
|
+
A transform parent (CAFake/CAByteSwap) recursing parent.xfer_stride lands
|
|
613
|
+
here; without this the request would fall to the dispatcher's per-cell path
|
|
614
|
+
(which re-composes each cell). Instead compose the chain to its root ONCE
|
|
615
|
+
and translate the request's strided access into the root's byte space, then
|
|
616
|
+
hand the whole region to the root in a SINGLE ca_xfer_stride (entity -> ptr
|
|
617
|
+
memcpy; boundary view -> one recursion).
|
|
618
|
+
|
|
619
|
+
Axis-aligned, byte-matching requests only; the byte-mismatch reinterpret
|
|
620
|
+
(CAField .real/.imag) and non-aligned access fall back to per-cell
|
|
621
|
+
xfer_index (which handles the sub-byte case). */
|
|
622
|
+
static void
|
|
623
|
+
ca_stride_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
|
|
624
|
+
ca_size_t *strides, void *data, int dir)
|
|
625
|
+
{
|
|
626
|
+
CAStride *ca = (CAStride *) ap;
|
|
627
|
+
CArray *root;
|
|
628
|
+
ca_size_t composed_strides[CA_RANK_MAX];
|
|
629
|
+
ca_size_t composed_base;
|
|
630
|
+
ca_size_t view_native[CA_RANK_MAX];
|
|
631
|
+
ca_size_t root_stride[CA_RANK_MAX];
|
|
632
|
+
ca_size_t root_base;
|
|
633
|
+
ca_size_t s;
|
|
634
|
+
int8_t ndim = ca->ndim, k;
|
|
635
|
+
int aligned = 1;
|
|
636
|
+
char *d = (char *) data;
|
|
637
|
+
|
|
638
|
+
ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
|
|
639
|
+
|
|
640
|
+
s = ca->bytes;
|
|
641
|
+
for (k = ndim - 1; k >= 0; k--) { view_native[k] = s; s *= ca->dim[k]; }
|
|
642
|
+
|
|
643
|
+
if (ca->bytes != root->bytes) {
|
|
644
|
+
aligned = 0;
|
|
645
|
+
}
|
|
646
|
+
else {
|
|
647
|
+
for (k = 0; k < ndim; k++) {
|
|
648
|
+
if (strides[k] % view_native[k] != 0) { aligned = 0; break; }
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/* Per-cell fallback (correct, no whole-view attach): byte-mismatch
|
|
653
|
+
reinterpret (CAField), non-aligned access, or a cold non-entity root
|
|
654
|
+
(whose ndim may differ from the view's -- e.g. a reshape over a boundary).
|
|
655
|
+
ca_stride_func_xfer_index composes one hop and delegates to the parent. */
|
|
656
|
+
if (!aligned || !root->ptr) {
|
|
657
|
+
ca_size_t idx[CA_RANK_MAX], doff = 0, base = 0;
|
|
658
|
+
for (k = 0; k < ndim; k++) base += starts[k] * view_native[k];
|
|
659
|
+
for (k = 0; k < ndim; k++) idx[k] = 0;
|
|
660
|
+
while (1) {
|
|
661
|
+
ca_size_t off = base, vmidx[CA_RANK_MAX];
|
|
662
|
+
for (k = 0; k < ndim; k++) off += idx[k] * strides[k];
|
|
663
|
+
ca_addr2index((CArray *) ca, off / ca->bytes, vmidx);
|
|
664
|
+
ca_stride_func_xfer_index(ca, vmidx, d + doff, dir);
|
|
665
|
+
doff += ca->bytes;
|
|
666
|
+
k = ndim - 1;
|
|
667
|
+
while (k >= 0) { if (++idx[k] < counts[k]) break; idx[k] = 0; k--; }
|
|
668
|
+
if (k < 0) break;
|
|
669
|
+
}
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/* Structural: root has a live ptr (entity / attached). Translate the
|
|
674
|
+
request into root's BYTE space and do a strided memcpy in the VIEW's ndim
|
|
675
|
+
(byte offsets into root->ptr -- independent of root's own ndim, so a
|
|
676
|
+
reshape view over a 1-D entity works). compose happened once. */
|
|
677
|
+
root_base = composed_base;
|
|
678
|
+
for (k = 0; k < ndim; k++) {
|
|
679
|
+
ca_size_t req_step = strides[k] / view_native[k];
|
|
680
|
+
root_base += starts[k] * composed_strides[k];
|
|
681
|
+
root_stride[k] = req_step * composed_strides[k];
|
|
682
|
+
}
|
|
683
|
+
/* Slab-merge, tile-block and the general driver all live in the shared
|
|
684
|
+
walker, which the central dispatcher's structural path also uses. */
|
|
685
|
+
ca_xfer_strided_walk(root->ptr + root_base, ca->bytes, ndim,
|
|
686
|
+
counts, root_stride, d, dir);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/* Match a region given over this view's addresses to this view's own axes.
|
|
690
|
+
|
|
691
|
+
The region can only be handed on if it is a box here: each of its axes has
|
|
692
|
+
to advance exactly one of ours and stay inside it for the whole traversal.
|
|
693
|
+
That is the same rule ca_stride_compose_through applies to a leaf against
|
|
694
|
+
its parent, asked here about a request instead -- and for the same reason,
|
|
695
|
+
since a request that carries from the end of one axis into the start of the
|
|
696
|
+
next has no per-axis step to carry down. A flat index over a multi-axis
|
|
697
|
+
view is exactly that shape and belongs on the per-cell walk.
|
|
698
|
+
|
|
699
|
+
Fills axis_of[k] with the view axis request axis k moves, and mult[k] with
|
|
700
|
+
how far. Returns 0 if the region is not a box. An axis of count 1 never
|
|
701
|
+
moves and is left unassigned (axis_of[k] = -1). */
|
|
702
|
+
|
|
703
|
+
static int
|
|
704
|
+
ca_stride_region_axes (CAStride *ca, ca_size_t base, int8_t ndim,
|
|
705
|
+
ca_size_t *counts, ca_size_t *steps,
|
|
706
|
+
ca_size_t *base_idx, int8_t *axis_of, ca_size_t *mult)
|
|
707
|
+
{
|
|
708
|
+
ca_size_t native[CA_RANK_MAX];
|
|
709
|
+
int used[CA_RANK_MAX];
|
|
710
|
+
ca_size_t s = 1;
|
|
711
|
+
int8_t j, k;
|
|
712
|
+
|
|
713
|
+
for (j = ca->ndim - 1; j >= 0; j--) { native[j] = s; s *= ca->dim[j]; }
|
|
714
|
+
for (j = 0; j < ca->ndim; j++) used[j] = 0;
|
|
715
|
+
|
|
716
|
+
if ( base < 0 || base >= ca->elements ) return 0;
|
|
717
|
+
ca_addr2index((CArray *) ca, base, base_idx);
|
|
718
|
+
|
|
719
|
+
for (k = 0; k < ndim; k++) {
|
|
720
|
+
int8_t found = -1;
|
|
721
|
+
ca_size_t q = 0;
|
|
722
|
+
|
|
723
|
+
if ( counts[k] <= 1 ) { axis_of[k] = -1; mult[k] = 0; continue; }
|
|
724
|
+
if ( steps[k] <= 0 ) return 0;
|
|
725
|
+
|
|
726
|
+
for (j = 0; j < ca->ndim; j++) {
|
|
727
|
+
ca_size_t qq;
|
|
728
|
+
if ( used[j] || ca->dim[j] <= 1 ) continue;
|
|
729
|
+
if ( steps[k] % native[j] != 0 ) continue;
|
|
730
|
+
qq = steps[k] / native[j];
|
|
731
|
+
if ( qq < 1 || qq >= ca->dim[j] ) continue;
|
|
732
|
+
if ( base_idx[j] + (counts[k] - 1) * qq >= ca->dim[j] ) continue;
|
|
733
|
+
if ( found >= 0 ) return 0; /* ambiguous: refuse rather than guess */
|
|
734
|
+
found = j;
|
|
735
|
+
q = qq;
|
|
736
|
+
}
|
|
737
|
+
if ( found < 0 ) return 0;
|
|
738
|
+
used[found] = 1;
|
|
739
|
+
axis_of[k] = found;
|
|
740
|
+
mult[k] = q;
|
|
741
|
+
}
|
|
742
|
+
return 1;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/* Compose the region into root's addresses and hand it on -- one value, one
|
|
746
|
+
hop, no attach. compose carries this view's axes into root's byte space,
|
|
747
|
+
so once each request axis is matched to one of ours the rest is a multiply.
|
|
748
|
+
root's ndim never enters into it, which is what lets a view that drops or
|
|
749
|
+
reorders axes hand its region down.
|
|
750
|
+
|
|
751
|
+
The byte-per-cell check is the same one xfer_stride makes: a view that
|
|
752
|
+
reinterprets width (CAField over a complex entity for .real) addresses
|
|
753
|
+
root in units root does not share, so there is no address to hand over
|
|
754
|
+
and the per-cell descent stands in. */
|
|
755
|
+
|
|
756
|
+
static void
|
|
757
|
+
ca_stride_func_fill_stride (void *ap, ca_size_t base, int8_t ndim,
|
|
758
|
+
ca_size_t *counts, ca_size_t *steps, void *ptr)
|
|
759
|
+
{
|
|
760
|
+
CAStride *ca = (CAStride *) ap;
|
|
761
|
+
CArray *root;
|
|
762
|
+
ca_size_t composed_strides[CA_RANK_MAX];
|
|
763
|
+
ca_size_t composed_base;
|
|
764
|
+
ca_size_t root_steps[CA_RANK_MAX];
|
|
765
|
+
ca_size_t base_idx[CA_RANK_MAX];
|
|
766
|
+
ca_size_t mult[CA_RANK_MAX];
|
|
767
|
+
int8_t axis_of[CA_RANK_MAX];
|
|
768
|
+
ca_size_t root_base;
|
|
769
|
+
int8_t k;
|
|
770
|
+
|
|
771
|
+
if ( ! ca_stride_region_axes(ca, base, ndim, counts, steps,
|
|
772
|
+
base_idx, axis_of, mult) ) {
|
|
773
|
+
ca_fill_stride_default(ca, base, ndim, counts, steps, ptr);
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
|
|
778
|
+
|
|
779
|
+
if ( ca->bytes != root->bytes ) {
|
|
780
|
+
ca_fill_stride_default(ca, base, ndim, counts, steps, ptr);
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
root_base = composed_base;
|
|
785
|
+
for (k = 0; k < ca->ndim; k++) {
|
|
786
|
+
root_base += base_idx[k] * composed_strides[k];
|
|
787
|
+
}
|
|
788
|
+
if ( root_base % root->bytes != 0 ) {
|
|
789
|
+
ca_fill_stride_default(ca, base, ndim, counts, steps, ptr);
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
for (k = 0; k < ndim; k++) {
|
|
794
|
+
ca_size_t st = ( axis_of[k] >= 0 )
|
|
795
|
+
? mult[k] * composed_strides[axis_of[k]]
|
|
796
|
+
: 0;
|
|
797
|
+
if ( st % root->bytes != 0 ) {
|
|
798
|
+
ca_fill_stride_default(ca, base, ndim, counts, steps, ptr);
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
root_steps[k] = st / root->bytes;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
ca_fill_stride(root, root_base / root->bytes, ndim, counts,
|
|
805
|
+
root_steps, ptr);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
extern int ca_stride_is_contiguous (CAStride *ca); /* defined below; non-static for Tier A */
|
|
809
|
+
|
|
810
|
+
/* Alias fast path:
|
|
811
|
+
When the view's strides describe a contiguous row-major run, the
|
|
812
|
+
view's logical memory is identical to a slice of the parent's
|
|
813
|
+
memory. We can skip allocating an own buffer and just point
|
|
814
|
+
ca->ptr into the parent. This makes attach O(1) for the common
|
|
815
|
+
"reshape / row-block / fully-covered slice" cases.
|
|
816
|
+
|
|
817
|
+
- attach / allocate: if contig, alias; otherwise allocate + (for
|
|
818
|
+
attach) gather.
|
|
819
|
+
- sync: if contig, the writes already landed in parent's memory,
|
|
820
|
+
so just propagate sync upward. Otherwise scatter.
|
|
821
|
+
- detach: if contig, ca->ptr was a borrow into parent and must
|
|
822
|
+
not be freed. Otherwise xfree.
|
|
823
|
+
|
|
824
|
+
ca_stride_is_contiguous is stable across the attach/detach
|
|
825
|
+
lifecycle of a view (its inputs -- strides[], dim[], bytes -- are
|
|
826
|
+
immutable), so checking it again at detach/sync time is safe.
|
|
827
|
+
|
|
828
|
+
Note: this preserves the byte semantics for byte-reinterpret views
|
|
829
|
+
(different bytes/data_type from parent) too -- aliasing the parent
|
|
830
|
+
pointer is exactly what byte-reinterpret needs. */
|
|
831
|
+
|
|
832
|
+
/* Fold-in-attach: instead of attaching the immediate parent and using
|
|
833
|
+
ca->parent->ptr + ca->base_offset, we walk up the CAStride chain
|
|
834
|
+
composing strides into the root entity's byte space, and attach
|
|
835
|
+
only the root.
|
|
836
|
+
|
|
837
|
+
Always pair ca_attach(root) with ca_detach(root) (regardless of
|
|
838
|
+
root's prior attach state); this preserves attach-count symmetry
|
|
839
|
+
even when root is the entity (which is "always attached" but the
|
|
840
|
+
counter still tracks borrowers). Compose is deterministic
|
|
841
|
+
(strides/base_offset are immutable), so sync/detach re-run the
|
|
842
|
+
walk and reach the same root and composed layout.
|
|
843
|
+
|
|
844
|
+
... except when the root is not an entity. Then it has no memory of
|
|
845
|
+
its own to borrow, and ca_attach(root) means "produce all of yourself
|
|
846
|
+
into a buffer" -- O(root) however few cells this view covers, which for
|
|
847
|
+
the lazy backings CAObject exists to serve (a file, a DB, a paged fetch)
|
|
848
|
+
is not slow but fatal. Such a root is asked for regions instead: this
|
|
849
|
+
view owns a buffer, ca_copy_data / ca_sync_data fill and drain it
|
|
850
|
+
through xfer_all, and the root is never attached.
|
|
851
|
+
|
|
852
|
+
The branch must be re-derivable at sync and detach time from the same
|
|
853
|
+
inputs, or detach frees a pointer it does not own. So it asks what the
|
|
854
|
+
root IS (entity? region-capable?), both immutable, and never whether the
|
|
855
|
+
root happens to be attached right now, which is not. */
|
|
856
|
+
|
|
857
|
+
/* Does this composed root have no memory to lend -- so that borrowing a
|
|
858
|
+
pointer from it means producing all of it first? See the note above.
|
|
859
|
+
Also consulted by the kernel iterator, which faces the same choice when it
|
|
860
|
+
composes a source down to its root.
|
|
861
|
+
|
|
862
|
+
Entities hold their own buffer. A CAStride-family root is one the fold
|
|
863
|
+
declined to walk through (a byte reinterpret, say); its own attach folds
|
|
864
|
+
onward as it always has, so leave that chain alone. What is left is the
|
|
865
|
+
boundary views that compute or fetch their contents -- CAObject, the lazy
|
|
866
|
+
per-element transforms, whatever a companion gem installed -- and of those,
|
|
867
|
+
only the ones with an xfer_stride slot can answer a region request at all.
|
|
868
|
+
The rest have nothing better than materialising, so they keep doing it once
|
|
869
|
+
under attach rather than once per transfer. */
|
|
870
|
+
int
|
|
871
|
+
ca_root_lends_no_memory (void *ap)
|
|
872
|
+
{
|
|
873
|
+
CArray *root = (CArray *) ap;
|
|
874
|
+
|
|
875
|
+
if ( ca_is_entity(root) ) return 0;
|
|
876
|
+
if ( ca_func[root->obj_type].attach == ca_stride_func.attach ) return 0;
|
|
877
|
+
return ca_func[root->obj_type].xfer_stride != NULL;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/* ca_attach_is_alias asks the same question from carray_core.c: a view whose
|
|
881
|
+
attach owns its buffer does not alias its parent, so writes through
|
|
882
|
+
ca->ptr need a ca_sync and callers must not assume otherwise. */
|
|
883
|
+
int
|
|
884
|
+
ca_stride_attach_aliases_root (CAStride *ca)
|
|
885
|
+
{
|
|
886
|
+
CArray *root;
|
|
887
|
+
ca_size_t composed_strides[CA_RANK_MAX];
|
|
888
|
+
ca_size_t composed_base;
|
|
889
|
+
|
|
890
|
+
/* Already holding a ptr: ca_attach only bumps the counter and hands that
|
|
891
|
+
ptr back, so what the root would have done does not arise. The slab
|
|
892
|
+
iterator relies on this -- it lends its view a buffer per iteration and
|
|
893
|
+
leaves base_offset meaningless, so re-deriving the data from the root
|
|
894
|
+
would read the wrong cells. */
|
|
895
|
+
if ( ca->ptr != NULL ) {
|
|
896
|
+
return 1;
|
|
897
|
+
}
|
|
898
|
+
ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
|
|
899
|
+
return !ca_root_lends_no_memory(root);
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
static void
|
|
903
|
+
ca_stride_func_allocate (void *ap)
|
|
904
|
+
{
|
|
905
|
+
CAStride *ca = (CAStride *) ap;
|
|
906
|
+
CArray *root;
|
|
907
|
+
ca_size_t composed_strides[CA_RANK_MAX];
|
|
908
|
+
ca_size_t composed_base;
|
|
909
|
+
|
|
910
|
+
ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
|
|
911
|
+
if (ca_root_lends_no_memory(root)) {
|
|
912
|
+
ca->ptr = xmalloc(ca_length(ca));
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
ca_attach(root);
|
|
916
|
+
if (ca_layout_is_contiguous(ca->ndim, ca->dim, composed_strides, ca->bytes)) {
|
|
917
|
+
ca->ptr = root->ptr + composed_base;
|
|
918
|
+
} else {
|
|
919
|
+
ca->ptr = xmalloc(ca_length(ca));
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
static void
|
|
924
|
+
ca_stride_func_attach (void *ap)
|
|
925
|
+
{
|
|
926
|
+
CAStride *ca = (CAStride *) ap;
|
|
927
|
+
CArray *root;
|
|
928
|
+
ca_size_t composed_strides[CA_RANK_MAX];
|
|
929
|
+
ca_size_t composed_base;
|
|
930
|
+
|
|
931
|
+
ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
|
|
932
|
+
if (ca_root_lends_no_memory(root)) {
|
|
933
|
+
/* Gather into a local buffer and publish it only once it is filled, so
|
|
934
|
+
ca->ptr stays NULL for the duration of the request -- a half-attached
|
|
935
|
+
view with a live ptr is what makes the per-cell dispatchers bypass the
|
|
936
|
+
transfer slots. */
|
|
937
|
+
char *buf = xmalloc(ca_length(ca));
|
|
938
|
+
ca_copy_data(ca, buf); /* region request, root stays cold */
|
|
939
|
+
ca->ptr = buf;
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
ca_attach(root);
|
|
943
|
+
if (ca_layout_is_contiguous(ca->ndim, ca->dim, composed_strides, ca->bytes)) {
|
|
944
|
+
ca->ptr = root->ptr + composed_base;
|
|
945
|
+
} else {
|
|
946
|
+
ca->ptr = xmalloc(ca_length(ca));
|
|
947
|
+
ca_stride_xfer_with_layout(ca, 0, root->ptr + composed_base, composed_strides);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
static void
|
|
952
|
+
ca_stride_func_sync (void *ap)
|
|
953
|
+
{
|
|
954
|
+
CAStride *ca = (CAStride *) ap;
|
|
955
|
+
CArray *root;
|
|
956
|
+
ca_size_t composed_strides[CA_RANK_MAX];
|
|
957
|
+
ca_size_t composed_base;
|
|
958
|
+
|
|
959
|
+
ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
|
|
960
|
+
if (ca_root_lends_no_memory(root)) {
|
|
961
|
+
/* xfer_all PUT writes through to the entity, recursing a hop per view,
|
|
962
|
+
so there is no ca_sync(root) to follow it with. */
|
|
963
|
+
ca_sync_data(ca, ca->ptr);
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
if (!ca_layout_is_contiguous(ca->ndim, ca->dim, composed_strides, ca->bytes)) {
|
|
967
|
+
ca_stride_xfer_with_layout(ca, 1, root->ptr + composed_base, composed_strides);
|
|
968
|
+
}
|
|
969
|
+
ca_sync(root);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
static void
|
|
973
|
+
ca_stride_func_detach (void *ap)
|
|
974
|
+
{
|
|
975
|
+
CAStride *ca = (CAStride *) ap;
|
|
976
|
+
CArray *root;
|
|
977
|
+
ca_size_t composed_strides[CA_RANK_MAX];
|
|
978
|
+
ca_size_t composed_base;
|
|
979
|
+
|
|
980
|
+
ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
|
|
981
|
+
if (ca_root_lends_no_memory(root)) {
|
|
982
|
+
xfree(ca->ptr); /* always our own; root was never attached */
|
|
983
|
+
ca->ptr = NULL;
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
if (!ca_layout_is_contiguous(ca->ndim, ca->dim, composed_strides, ca->bytes)) {
|
|
987
|
+
xfree(ca->ptr);
|
|
988
|
+
}
|
|
989
|
+
ca->ptr = NULL;
|
|
990
|
+
ca_detach(root);
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
/* Bridges a leaf whose ndim is smaller than the resolved root's -- an axis was dropped by integer
|
|
994
|
+
indexing (e.g. s[100,nil,nil] over a (K,180,360) CAStack) -- so the natural
|
|
995
|
+
partial-materialise xfer_stride path can still run. Reinsert each dropped
|
|
996
|
+
root axis as a degenerate count=1 axis, producing a full root-ndim region
|
|
997
|
+
request; the root's xfer_stride then delivers only the requested region
|
|
998
|
+
(CAStack slices just the touched parents, etc.) instead of the consumer
|
|
999
|
+
materialising the whole root.
|
|
1000
|
+
|
|
1001
|
+
Succeeds only for a pure axis drop with no transpose / reshape / strided
|
|
1002
|
+
sub-block / byte reinterpret on the surviving axes: ca->bytes == root->bytes,
|
|
1003
|
+
and the leaf's composed strides form a strictly forward subsequence of the
|
|
1004
|
+
root's row-major native strides. When it returns 0 (reshape / axis-merge /
|
|
1005
|
+
permute / step>1 slice) the caller keeps the 2-pass fallback, so correctness
|
|
1006
|
+
is never at risk -- only the partial-cost win is forgone.
|
|
1007
|
+
|
|
1008
|
+
Note: the count=1 axes are never iterated by the root's xfer_stride, so the
|
|
1009
|
+
destination buffer (row-major over the leaf's surviving dims) is laid out
|
|
1010
|
+
identically whether the degenerate axes are present or not. */
|
|
1011
|
+
static int
|
|
1012
|
+
ca_stride_bridge_dropped_axes (CAStride *ca, CArray *root,
|
|
1013
|
+
ca_size_t *composed_strides,
|
|
1014
|
+
ca_size_t composed_base,
|
|
1015
|
+
ca_size_t *r_starts, ca_size_t *r_counts,
|
|
1016
|
+
ca_size_t *r_strides)
|
|
1017
|
+
{
|
|
1018
|
+
ca_size_t native[CA_RANK_MAX];
|
|
1019
|
+
ca_size_t s;
|
|
1020
|
+
int8_t i, j;
|
|
1021
|
+
|
|
1022
|
+
if ( ca->bytes != root->bytes ) return 0; /* byte reinterpret */
|
|
1023
|
+
if ( ca->ndim >= root->ndim ) return 0; /* not an axis drop */
|
|
1024
|
+
|
|
1025
|
+
s = root->bytes;
|
|
1026
|
+
for ( i = root->ndim - 1; i >= 0; i-- ) { native[i] = s; s *= root->dim[i]; }
|
|
1027
|
+
|
|
1028
|
+
/* per-axis root indices recovered from the composed byte base (this also
|
|
1029
|
+
carries the dropped axes' selected positions, e.g. lat=5). */
|
|
1030
|
+
ca_addr2index(root, composed_base / root->bytes, r_starts);
|
|
1031
|
+
|
|
1032
|
+
j = 0;
|
|
1033
|
+
for ( i = 0; i < ca->ndim; i++ ) {
|
|
1034
|
+
while ( j < root->ndim && native[j] != composed_strides[i] ) {
|
|
1035
|
+
r_counts[j] = 1; /* dropped axis -> degenerate */
|
|
1036
|
+
r_strides[j] = native[j];
|
|
1037
|
+
j++;
|
|
1038
|
+
}
|
|
1039
|
+
if ( j >= root->ndim ) return 0; /* stride not a forward native match */
|
|
1040
|
+
/* The surviving region must fit within the matched root axis. A stride
|
|
1041
|
+
match alone is not enough: a flatten/axis-merge reshape (e.g. a 2x2
|
|
1042
|
+
CAGrid viewed as 1-D length 4) matches the innermost native stride but
|
|
1043
|
+
its extent overflows the axis (4 > 2), which would scatter out of
|
|
1044
|
+
bounds. Reject -> 2-pass fallback keeps such reshapes correct. */
|
|
1045
|
+
if ( r_starts[j] + ca->dim[i] > root->dim[j] ) return 0;
|
|
1046
|
+
r_counts[j] = ca->dim[i]; /* surviving axis -> leaf extent */
|
|
1047
|
+
r_strides[j] = composed_strides[i];
|
|
1048
|
+
j++;
|
|
1049
|
+
}
|
|
1050
|
+
while ( j < root->ndim ) { /* trailing dropped axes */
|
|
1051
|
+
r_counts[j] = 1;
|
|
1052
|
+
r_strides[j] = native[j];
|
|
1053
|
+
j++;
|
|
1054
|
+
}
|
|
1055
|
+
return 1;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
static void
|
|
1059
|
+
ca_stride_func_xfer_all (void *ap, void *data, int dir)
|
|
1060
|
+
{
|
|
1061
|
+
/* Whole-view transfer (step 4): direction-unified merge of copy_data /
|
|
1062
|
+
sync_data. Compose leaf strides up through the CAStride parent chain and
|
|
1063
|
+
gather/scatter directly from/to the resolved root (entity or first
|
|
1064
|
+
non-CAStride ancestor), skipping materialisation of intermediate CAStride
|
|
1065
|
+
views. */
|
|
1066
|
+
CAStride *ca = (CAStride *) ap;
|
|
1067
|
+
CArray *root;
|
|
1068
|
+
ca_size_t composed_strides[CA_RANK_MAX];
|
|
1069
|
+
ca_size_t composed_base;
|
|
1070
|
+
char *ptr0 = ca->ptr;
|
|
1071
|
+
char *ptr = (char *) data;
|
|
1072
|
+
|
|
1073
|
+
ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
|
|
1074
|
+
|
|
1075
|
+
/* Partial materialise: when the fold stops at a cold boundary view that knows how to deliver a region (has an
|
|
1076
|
+
xfer_stride slot), request only this view's composed region instead of
|
|
1077
|
+
attaching (materialising) the whole boundary. ca_is_attached(root) is the
|
|
1078
|
+
direct gate: entity / already-attached roots have a live ptr and take the
|
|
1079
|
+
bulk path below; only a cold boundary view reaches here. xfer_stride PUT
|
|
1080
|
+
writes through to the entity (recursing each hop), so no separate
|
|
1081
|
+
ca_sync(root) is needed. Un-slotted boundaries stay on the bulk path
|
|
1082
|
+
(no per-cell regression).
|
|
1083
|
+
|
|
1084
|
+
The region is stated in the root's own address space, so it can only be
|
|
1085
|
+
handed over when this view addresses the root in units the root shares:
|
|
1086
|
+
the same cell width, and a base that lands on a root element. A byte
|
|
1087
|
+
reinterpret (CARefer to a narrower data_type over a cold view) has
|
|
1088
|
+
neither -- the counts passed below are this view's cells, which the root
|
|
1089
|
+
reads as its own and answers with root->bytes apiece, overrunning the
|
|
1090
|
+
caller's buffer. Those requests fall through to the whole-root
|
|
1091
|
+
materialise, which addresses the root in bytes and needs no such
|
|
1092
|
+
agreement. Same rule xfer_stride and fill_stride already apply. */
|
|
1093
|
+
if ( !ca_is_attached(root) && ca_func[root->obj_type].xfer_stride
|
|
1094
|
+
&& ca->bytes == root->bytes && composed_base % root->bytes == 0 ) {
|
|
1095
|
+
ca_size_t starts[CA_RANK_MAX];
|
|
1096
|
+
if ( ca->ndim == root->ndim ) {
|
|
1097
|
+
ca_addr2index(root, composed_base / root->bytes, starts);
|
|
1098
|
+
ca_xfer_stride(root, starts, ca->dim, composed_strides, ptr, dir);
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
else {
|
|
1102
|
+
/* Leaf dropped an axis (ndim < root->ndim). Reinsert the dropped
|
|
1103
|
+
axes as degenerate count=1 axes so the region request matches the
|
|
1104
|
+
root's ndim, then run the same partial-materialise path. Falls
|
|
1105
|
+
through to the 2-pass fallback when the chain isn't a pure drop. */
|
|
1106
|
+
ca_size_t r_counts[CA_RANK_MAX], r_strides[CA_RANK_MAX];
|
|
1107
|
+
if ( ca_stride_bridge_dropped_axes(ca, root, composed_strides,
|
|
1108
|
+
composed_base, starts,
|
|
1109
|
+
r_counts, r_strides) ) {
|
|
1110
|
+
ca_xfer_stride(root, starts, r_counts, r_strides, ptr, dir);
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
/* Reshape / transpose that ADDS axes over a 1-D cold boundary root
|
|
1115
|
+
(ndim > root->ndim), e.g. big.swap_bytes[[pos, n*2]].reshape(n, 2)
|
|
1116
|
+
whose root is a CAMonOp / CAFake / CABinOp per-element transform.
|
|
1117
|
+
The view's shape can't be expressed in the root's single axis, but
|
|
1118
|
+
the root bytes it touches form a bounded flat span. Materialise
|
|
1119
|
+
ONLY that span (not the whole root) via one contiguous region
|
|
1120
|
+
request, then run the strided gather/scatter against it. Without
|
|
1121
|
+
this the code drops to the whole-root 2-pass fallback below, making
|
|
1122
|
+
a small per-record view over a large lazy root cost O(root) each --
|
|
1123
|
+
quadratic across a per-record loop.
|
|
1124
|
+
|
|
1125
|
+
Gated to root->ndim == 1 (a flat span is exactly one strided box, so
|
|
1126
|
+
the request is exact) and ca->bytes == root->bytes (no byte
|
|
1127
|
+
reinterpret, so the span endpoints are whole root elements). A wider
|
|
1128
|
+
lazy root has no single strided box to ask for, so it takes the
|
|
1129
|
+
whole-root fallback below -- correct, just not partial. */
|
|
1130
|
+
if ( root->ndim == 1 && ca->bytes == root->bytes ) {
|
|
1131
|
+
ca_size_t span_lo = composed_base;
|
|
1132
|
+
ca_size_t span_hi = composed_base + ca->bytes;
|
|
1133
|
+
int8_t k;
|
|
1134
|
+
for ( k = 0; k < ca->ndim; k++ ) {
|
|
1135
|
+
ca_size_t ext = (ca->dim[k] - 1) * composed_strides[k];
|
|
1136
|
+
if ( composed_strides[k] >= 0 ) span_hi += ext;
|
|
1137
|
+
else span_lo += ext;
|
|
1138
|
+
}
|
|
1139
|
+
{
|
|
1140
|
+
volatile VALUE holder;
|
|
1141
|
+
ca_size_t rlo = span_lo / root->bytes;
|
|
1142
|
+
ca_size_t rcount = (span_hi - span_lo) / root->bytes;
|
|
1143
|
+
ca_size_t rstep = root->bytes;
|
|
1144
|
+
char *scratch = ALLOCV_N(char, holder, rcount * root->bytes);
|
|
1145
|
+
ca_xfer_stride(root, &rlo, &rcount, &rstep, scratch, CA_XFER_GET);
|
|
1146
|
+
ca->ptr = ptr;
|
|
1147
|
+
ca_stride_xfer_with_layout(ca, (dir == CA_XFER_PUT) ? 1 : 0,
|
|
1148
|
+
scratch + (composed_base - span_lo),
|
|
1149
|
+
composed_strides);
|
|
1150
|
+
ca->ptr = ptr0;
|
|
1151
|
+
if ( dir == CA_XFER_PUT ) {
|
|
1152
|
+
ca_xfer_stride(root, &rlo, &rcount, &rstep, scratch, CA_XFER_PUT);
|
|
1153
|
+
}
|
|
1154
|
+
ALLOCV_END(holder);
|
|
1155
|
+
}
|
|
1156
|
+
return;
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
if ( ca_is_attached(root) ) {
|
|
1162
|
+
/* Hot path: root has live ptr (entity / pre-attached). Direct
|
|
1163
|
+
strided gather/scatter through composed strides, no attach. */
|
|
1164
|
+
ca->ptr = ptr;
|
|
1165
|
+
ca_stride_xfer_with_layout(ca, (dir == CA_XFER_PUT) ? 1 : 0,
|
|
1166
|
+
root->ptr + composed_base, composed_strides);
|
|
1167
|
+
ca->ptr = ptr0;
|
|
1168
|
+
if ( dir == CA_XFER_PUT ) {
|
|
1169
|
+
ca_sync(root); /* propagate scatter up to root's storage */
|
|
1170
|
+
}
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
/* Cold root without an xfer_stride slot, or an ndim mismatch the bridges
|
|
1175
|
+
above could not express: materialise the root into scratch via
|
|
1176
|
+
ca_xfer_all and run the direct strided gather/scatter against that.
|
|
1177
|
+
CAREFUL: do not "simplify" this to ca_attach(root) -- a transfer slot
|
|
1178
|
+
that attaches its parent materialises it behind the caller's back, and
|
|
1179
|
+
the per-cell xfer_addrs alternative explodes in cost once the parent is
|
|
1180
|
+
itself a view chain. */
|
|
1181
|
+
{
|
|
1182
|
+
volatile VALUE holder;
|
|
1183
|
+
ca_size_t rlen = root->elements * root->bytes;
|
|
1184
|
+
char *root_scratch = ALLOCV_N(char, holder, rlen);
|
|
1185
|
+
char *root_ptr_saved = root->ptr;
|
|
1186
|
+
ca_xfer_all(root, root_scratch, CA_XFER_GET);
|
|
1187
|
+
root->ptr = root_scratch;
|
|
1188
|
+
ca->ptr = ptr;
|
|
1189
|
+
ca_stride_xfer_with_layout(ca, (dir == CA_XFER_PUT) ? 1 : 0,
|
|
1190
|
+
root->ptr + composed_base, composed_strides);
|
|
1191
|
+
ca->ptr = ptr0;
|
|
1192
|
+
if ( dir == CA_XFER_PUT ) {
|
|
1193
|
+
/* Push back scratch (modified by scatter) to root. */
|
|
1194
|
+
ca_xfer_all(root, root_scratch, CA_XFER_PUT);
|
|
1195
|
+
}
|
|
1196
|
+
root->ptr = root_ptr_saved;
|
|
1197
|
+
ALLOCV_END(holder);
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
static void
|
|
1202
|
+
ca_stride_func_fill_data (void *ap, void *ptr)
|
|
1203
|
+
{
|
|
1204
|
+
/* Write `*ptr` to every element at the strided positions covered
|
|
1205
|
+
by this view, composing through the CAStride chain to write
|
|
1206
|
+
directly into the resolved root. Skips materialising any
|
|
1207
|
+
intermediate CAStride view.
|
|
1208
|
+
|
|
1209
|
+
Axis-merge is applied to the composed strides before the inner write
|
|
1210
|
+
loop. When merge collapses to a contig
|
|
1211
|
+
run on the innermost axis (mstrides[mndim-1] == bytes), fill that
|
|
1212
|
+
run with a tight memcpy-pattern loop instead of per-element memcpy.
|
|
1213
|
+
This converts e.g. mid_axis_3d's per-element 8-byte writes into
|
|
1214
|
+
200 iterations of "fill 80KB" each. */
|
|
1215
|
+
CAStride *ca = (CAStride *) ap;
|
|
1216
|
+
CArray *root;
|
|
1217
|
+
ca_size_t composed_strides[CA_RANK_MAX];
|
|
1218
|
+
ca_size_t composed_base;
|
|
1219
|
+
ca_size_t mdim[CA_RANK_MAX];
|
|
1220
|
+
ca_size_t mstrides[CA_RANK_MAX];
|
|
1221
|
+
int8_t mndim;
|
|
1222
|
+
ca_size_t idx[CA_RANK_MAX];
|
|
1223
|
+
int8_t k;
|
|
1224
|
+
ca_size_t n;
|
|
1225
|
+
ca_size_t bytes = ca->bytes;
|
|
1226
|
+
|
|
1227
|
+
if (ca->elements == 0) return;
|
|
1228
|
+
|
|
1229
|
+
ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
|
|
1230
|
+
|
|
1231
|
+
/* If compose stopped at a non-foldable parent (= neither CAStride family nor interior-only
|
|
1232
|
+
CAWindow), the upcoming ca_attach(root) would gather root's data
|
|
1233
|
+
into scratch only to overwrite every byte with *ptr -- wasted
|
|
1234
|
+
work. Delegate to root.fill_data instead.
|
|
1235
|
+
Safety gates:
|
|
1236
|
+
(1) elements match -- our view covers every root cell;
|
|
1237
|
+
(2) bytes match -- our cell width equals root's cell width.
|
|
1238
|
+
Without (2), a type-reinterpreting CAStride (CAField over a
|
|
1239
|
+
complex entity for `.real`/`.imag`) would corrupt root: root
|
|
1240
|
+
would read more bytes than ptr points to, overwriting cells the
|
|
1241
|
+
view did not intend to touch.
|
|
1242
|
+
Mask is independent of broadcast scalar fill (CArray semantics:
|
|
1243
|
+
fill writes data only, mask state preserved), so delegate path
|
|
1244
|
+
and existing path are mask-equivalent. */
|
|
1245
|
+
if ( ca_func[root->obj_type].attach != ca_stride_func.attach &&
|
|
1246
|
+
ca_func[root->obj_type].attach != ca_window_func.attach &&
|
|
1247
|
+
ca->elements == root->elements &&
|
|
1248
|
+
ca->bytes == root->bytes ) {
|
|
1249
|
+
ca_func[root->obj_type].fill_data(root, ptr);
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
/* The gate above only covers the half where the view spans all of root;
|
|
1254
|
+
"fill everything I cover" is a correct request to pass on only then.
|
|
1255
|
+
For anything short of that the old path attached root, and if that
|
|
1256
|
+
attach is a gather rather than an alias it pulls in the whole root and
|
|
1257
|
+
syncs it all back -- cells the caller never addressed make the round
|
|
1258
|
+
trip, and through a lossy transform layer they come back changed. Hand
|
|
1259
|
+
root the region instead.
|
|
1260
|
+
|
|
1261
|
+
What falls through is root already holding its data: an entity, whose
|
|
1262
|
+
ptr is live at rest, or a view someone outside is holding attached.
|
|
1263
|
+
Either way the strided write below lands in memory that is already
|
|
1264
|
+
there, so composing into it directly is both cheaper than a region
|
|
1265
|
+
hand-off and the reason the loop is written this way. */
|
|
1266
|
+
if ( !ca_is_attached(root) ) {
|
|
1267
|
+
ca_fill_stride_whole(ca, ptr);
|
|
1268
|
+
return;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
/* Local copies + merge */
|
|
1272
|
+
mndim = ca->ndim;
|
|
1273
|
+
for (k = 0; k < mndim; k++) {
|
|
1274
|
+
mdim[k] = ca->dim[k];
|
|
1275
|
+
mstrides[k] = composed_strides[k];
|
|
1276
|
+
}
|
|
1277
|
+
ca_stride_merge_axes(mstrides, mdim, &mndim);
|
|
1278
|
+
|
|
1279
|
+
/* Iterate prefix axes; fill inner run per iteration. */
|
|
1280
|
+
ca_size_t inner_count = mdim[mndim - 1];
|
|
1281
|
+
ca_size_t inner_stride = mstrides[mndim - 1];
|
|
1282
|
+
ca_size_t outer_total = ca->elements / inner_count;
|
|
1283
|
+
|
|
1284
|
+
for (k = 0; k < mndim; k++) idx[k] = 0;
|
|
1285
|
+
|
|
1286
|
+
for (n = 0; n < outer_total; n++) {
|
|
1287
|
+
ca_size_t off = composed_base;
|
|
1288
|
+
for (k = 0; k < mndim - 1; k++) off += idx[k] * mstrides[k];
|
|
1289
|
+
|
|
1290
|
+
/* Inner fill: if inner_stride == bytes, the inner run is contig
|
|
1291
|
+
and can be filled in a tight typed loop / memset-style. Otherwise
|
|
1292
|
+
per-element memcpy at stride. */
|
|
1293
|
+
char *dst = root->ptr + off;
|
|
1294
|
+
if (inner_stride == bytes) {
|
|
1295
|
+
/* Tight contig fill: repeat the bytes-wide value inner_count times. */
|
|
1296
|
+
ca_size_t i;
|
|
1297
|
+
switch (bytes) {
|
|
1298
|
+
case 1: {
|
|
1299
|
+
int8_t v;
|
|
1300
|
+
memcpy(&v, ptr, 1);
|
|
1301
|
+
memset(dst, v, inner_count);
|
|
1302
|
+
break;
|
|
1303
|
+
}
|
|
1304
|
+
case 2: {
|
|
1305
|
+
int16_t v; memcpy(&v, ptr, 2);
|
|
1306
|
+
int16_t *d = (int16_t *) dst;
|
|
1307
|
+
for (i = 0; i < inner_count; i++) d[i] = v;
|
|
1308
|
+
break;
|
|
1309
|
+
}
|
|
1310
|
+
case 4: {
|
|
1311
|
+
int32_t v; memcpy(&v, ptr, 4);
|
|
1312
|
+
int32_t *d = (int32_t *) dst;
|
|
1313
|
+
for (i = 0; i < inner_count; i++) d[i] = v;
|
|
1314
|
+
break;
|
|
1315
|
+
}
|
|
1316
|
+
case 8: {
|
|
1317
|
+
int64_t v; memcpy(&v, ptr, 8);
|
|
1318
|
+
int64_t *d = (int64_t *) dst;
|
|
1319
|
+
for (i = 0; i < inner_count; i++) d[i] = v;
|
|
1320
|
+
break;
|
|
1321
|
+
}
|
|
1322
|
+
default:
|
|
1323
|
+
for (i = 0; i < inner_count; i++) memcpy(dst + i * bytes, ptr, bytes);
|
|
1324
|
+
break;
|
|
1325
|
+
}
|
|
1326
|
+
} else {
|
|
1327
|
+
/* Strided inner: per-element memcpy at byte stride. */
|
|
1328
|
+
ca_size_t i;
|
|
1329
|
+
for (i = 0; i < inner_count; i++) {
|
|
1330
|
+
memcpy(dst + i * inner_stride, ptr, bytes);
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
for (k = mndim - 2; k >= 0; k--) {
|
|
1335
|
+
if (++idx[k] < mdim[k]) break;
|
|
1336
|
+
idx[k] = 0;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
/* The write went into root's own buffer; if that buffer is a view's
|
|
1341
|
+
scratch, only a sync puts it back. Nothing to detach: this path is
|
|
1342
|
+
reached only when root was already attached, so the attach is not
|
|
1343
|
+
ours to close. */
|
|
1344
|
+
ca_sync(root);
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
static void
|
|
1348
|
+
ca_stride_func_create_mask (void *ap)
|
|
1349
|
+
{
|
|
1350
|
+
CAStride *ca = (CAStride *) ap;
|
|
1351
|
+
ca_create_mask(ca->parent);
|
|
1352
|
+
/* The mask for a CAStride is itself a CAStride over the parent's
|
|
1353
|
+
mask, with the same shape, strides, and base_offset. Mask bytes
|
|
1354
|
+
are 1 byte each (boolean8_t), so we need to scale strides by
|
|
1355
|
+
parent->mask->bytes / parent->bytes -- but for the typical case
|
|
1356
|
+
mask->bytes == 1 and parent->bytes is the parent's item width.
|
|
1357
|
+
Strides in CAStride are byte-units already, so we need to convert
|
|
1358
|
+
to mask-byte-units.
|
|
1359
|
+
|
|
1360
|
+
Element layout: walking idx by [1,0,...] in parent advances
|
|
1361
|
+
parent->ptr by strides[k] bytes (= strides[k]/parent->bytes
|
|
1362
|
+
elements). The mask's same advance is in mask->bytes per element,
|
|
1363
|
+
so mask_strides[k] = (strides[k]/parent->bytes) * mask->bytes. */
|
|
1364
|
+
ca_size_t mask_strides[CA_RANK_MAX];
|
|
1365
|
+
ca_size_t mask_offset;
|
|
1366
|
+
int8_t k;
|
|
1367
|
+
ca_size_t parent_bytes = ca->parent->bytes;
|
|
1368
|
+
ca_size_t mask_bytes = ca->parent->mask->bytes;
|
|
1369
|
+
for (k = 0; k < ca->ndim; k++) {
|
|
1370
|
+
mask_strides[k] = (ca->strides[k] / parent_bytes) * mask_bytes;
|
|
1371
|
+
}
|
|
1372
|
+
mask_offset = (ca->base_offset / parent_bytes) * mask_bytes;
|
|
1373
|
+
/* The mask of a subclassed CAStride (e.g. CATranspose) is the same
|
|
1374
|
+
subclass. Pass ca->obj_type through so the Ruby mask wrapper
|
|
1375
|
+
picks up rb_cCATransMask / rb_cCAStrideMask correctly. */
|
|
1376
|
+
ca->mask =
|
|
1377
|
+
(CArray *) ca_stride_new(ca->obj_type, ca->parent->mask,
|
|
1378
|
+
ca->parent->mask->data_type,
|
|
1379
|
+
ca->parent->mask->bytes,
|
|
1380
|
+
ca->ndim, ca->dim,
|
|
1381
|
+
mask_strides, mask_offset);
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
ca_operation_function_t ca_stride_func = {
|
|
1385
|
+
-1, /* CA_OBJ_STRIDE: assigned at Init time */
|
|
1386
|
+
CA_VIEW_ARRAY,
|
|
1387
|
+
free_ca_stride,
|
|
1388
|
+
ca_stride_func_clone,
|
|
1389
|
+
ca_stride_func_allocate,
|
|
1390
|
+
ca_stride_func_attach,
|
|
1391
|
+
ca_stride_func_sync,
|
|
1392
|
+
ca_stride_func_detach,
|
|
1393
|
+
ca_stride_func_fill_data,
|
|
1394
|
+
ca_stride_func_create_mask,
|
|
1395
|
+
ca_stride_func_xfer_index,
|
|
1396
|
+
ca_stride_func_xfer_addrs,
|
|
1397
|
+
NULL, /* fold_stride: CAStride family is open-inline */
|
|
1398
|
+
ca_stride_func_xfer_stride,
|
|
1399
|
+
ca_stride_func_xfer_all,
|
|
1400
|
+
sizeof(CAStride), /* struct_size: pool framework */
|
|
1401
|
+
ca_stride_pool_bytes, /* pool_bytes */
|
|
1402
|
+
ca_stride_pool_init, /* pool_init */
|
|
1403
|
+
.fill_stride = ca_stride_func_fill_stride,
|
|
1404
|
+
};
|
|
1405
|
+
|
|
1406
|
+
/* ------------------------------------------------------------------- */
|
|
1407
|
+
/* gather / scatter loops */
|
|
1408
|
+
/* */
|
|
1409
|
+
/* Three fast paths followed by a correctness-first naive fallback. */
|
|
1410
|
+
/* Roughly in order of preference per call: */
|
|
1411
|
+
/* */
|
|
1412
|
+
/* P1 -- whole view is a single contiguous row-major run */
|
|
1413
|
+
/* (all strides match the natural product chain) */
|
|
1414
|
+
/* --> one memcpy for the entire elements * bytes. */
|
|
1415
|
+
/* */
|
|
1416
|
+
/* P2 -- innermost dim is contiguous (strides[ndim-1] == bytes) */
|
|
1417
|
+
/* --> outer loop with carried offset, each row copied with */
|
|
1418
|
+
/* memcpy. Handles the col-slice / strided-rows pattern */
|
|
1419
|
+
/* produced by CABlock-style views and by negative */
|
|
1420
|
+
/* outer-stride views (e.g. as_strided yrev). */
|
|
1421
|
+
/* */
|
|
1422
|
+
/* P3 -- innermost stride is a positive multiple of bytes */
|
|
1423
|
+
/* --> mcopy_step with element-stride. */
|
|
1424
|
+
/* */
|
|
1425
|
+
/* naive fallback -- per-element memcpy over a flat index walk. */
|
|
1426
|
+
/* Used for negative innermost stride, byte-misaligned */
|
|
1427
|
+
/* strides, and other shapes the fast paths can't express. */
|
|
1428
|
+
/* ------------------------------------------------------------------- */
|
|
1429
|
+
|
|
1430
|
+
/* True if `ca`'s strides describe a single row-major contiguous run.
|
|
1431
|
+
For each k: strides[k] == bytes * Product(dim[k+1:]) (innermost
|
|
1432
|
+
stride == bytes). dim[k] == 1 axes contribute no displacement, so
|
|
1433
|
+
their stride value is treated as a don't-care. */
|
|
1434
|
+
/* Non-static: `ca_attach_is_alias` (carray_core.c) calls it to decide whether
|
|
1435
|
+
a CAStride-family parent is alias-attachable, i.e. whether ca_attach is
|
|
1436
|
+
O(1). That predicate feeds the kernel iterator's alias decision. */
|
|
1437
|
+
int
|
|
1438
|
+
ca_stride_is_contiguous (CAStride *ca)
|
|
1439
|
+
{
|
|
1440
|
+
ca_size_t expected = ca->bytes;
|
|
1441
|
+
int8_t k;
|
|
1442
|
+
for (k = ca->ndim - 1; k >= 0; k--) {
|
|
1443
|
+
if (ca->dim[k] != 1 && ca->strides[k] != expected) {
|
|
1444
|
+
return 0;
|
|
1445
|
+
}
|
|
1446
|
+
expected *= ca->dim[k];
|
|
1447
|
+
}
|
|
1448
|
+
return 1;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
/* [MOVED] ca_stride_gather_run / ca_stride_scatter_run -> ca_iter_substrate.h
|
|
1452
|
+
as `static inline`. The general driver below depends on them inlining, and
|
|
1453
|
+
ca_transform_common.c needs the same definition; a static inline in the
|
|
1454
|
+
header gives both call sites the inlinable typed loops. */
|
|
1455
|
+
|
|
1456
|
+
/* Test if `strides[]` describe a row-major contiguous run over `dim[]`
|
|
1457
|
+
with element size `bytes`. Mirrors ca_stride_is_contiguous but
|
|
1458
|
+
takes an explicit strides array so the composed-fold path can re-test
|
|
1459
|
+
after composition. */
|
|
1460
|
+
static int
|
|
1461
|
+
ca_layout_is_contiguous (int8_t ndim, const ca_size_t *dim,
|
|
1462
|
+
const ca_size_t *strides, ca_size_t bytes)
|
|
1463
|
+
{
|
|
1464
|
+
ca_size_t expected = bytes;
|
|
1465
|
+
int8_t k;
|
|
1466
|
+
for (k = ndim - 1; k >= 0; k--) {
|
|
1467
|
+
if (dim[k] != 1 && strides[k] != expected) return 0;
|
|
1468
|
+
expected *= dim[k];
|
|
1469
|
+
}
|
|
1470
|
+
return 1;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
/* Merges contig-mergeable adjacent axes in-place.
|
|
1474
|
+
Given (strides[], dim[], ndim_inout), fold adjacent axes k, k+1 when
|
|
1475
|
+
they describe a single contiguous run on the strided side:
|
|
1476
|
+
strides[k+1] != 0 && strides[k] == strides[k+1] * dim[k+1]
|
|
1477
|
+
stride==0 axes (CARepeat fences) and dim==1 axes are special-cased:
|
|
1478
|
+
dim==1 axes are squashed first (no displacement), stride==0 axes
|
|
1479
|
+
are not merged with their neighbours.
|
|
1480
|
+
|
|
1481
|
+
Sign-agnostic: the condition uses signed equality so negative strides
|
|
1482
|
+
merge correctly as long as both adjacent strides agree in sign.
|
|
1483
|
+
|
|
1484
|
+
No mutation when the input is already "merged" (idempotent). */
|
|
1485
|
+
void
|
|
1486
|
+
ca_stride_merge_axes (ca_size_t *strides,
|
|
1487
|
+
ca_size_t *dim,
|
|
1488
|
+
int8_t *ndim_inout)
|
|
1489
|
+
{
|
|
1490
|
+
int8_t ndim = *ndim_inout;
|
|
1491
|
+
int8_t i, w;
|
|
1492
|
+
|
|
1493
|
+
if (ndim < 1) return;
|
|
1494
|
+
|
|
1495
|
+
/* Pass 1: squash dim==1 axes (they carry no displacement; their
|
|
1496
|
+
stride is a don't-care for the gather loop). */
|
|
1497
|
+
w = 0;
|
|
1498
|
+
for (i = 0; i < ndim; i++) {
|
|
1499
|
+
if (dim[i] == 1) continue;
|
|
1500
|
+
if (i != w) {
|
|
1501
|
+
strides[w] = strides[i];
|
|
1502
|
+
dim[w] = dim[i];
|
|
1503
|
+
}
|
|
1504
|
+
w++;
|
|
1505
|
+
}
|
|
1506
|
+
ndim = w;
|
|
1507
|
+
if (ndim < 1) {
|
|
1508
|
+
/* All-dim-1: keep one trivial axis for the gather loop. */
|
|
1509
|
+
dim[0] = 1;
|
|
1510
|
+
strides[0] = 0;
|
|
1511
|
+
ndim = 1;
|
|
1512
|
+
*ndim_inout = ndim;
|
|
1513
|
+
return;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
/* Pass 2: merge adjacent contig-mergeable pairs. Loop with explicit
|
|
1517
|
+
index because merges shift trailing axes inward. */
|
|
1518
|
+
i = 0;
|
|
1519
|
+
while (i + 1 < ndim) {
|
|
1520
|
+
if (strides[i + 1] != 0
|
|
1521
|
+
&& strides[i] == strides[i + 1] * dim[i + 1]) {
|
|
1522
|
+
/* Merge axis i with axis i+1: new axis at i has the inner stride
|
|
1523
|
+
and the combined count. Shift the tail leftward. */
|
|
1524
|
+
dim[i] = dim[i] * dim[i + 1];
|
|
1525
|
+
strides[i] = strides[i + 1];
|
|
1526
|
+
for (w = i + 1; w + 1 < ndim; w++) {
|
|
1527
|
+
strides[w] = strides[w + 1];
|
|
1528
|
+
dim[w] = dim[w + 1];
|
|
1529
|
+
}
|
|
1530
|
+
ndim--;
|
|
1531
|
+
/* Stay at i; the new neighbour at i+1 might also merge. */
|
|
1532
|
+
} else {
|
|
1533
|
+
i++;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
*ndim_inout = ndim;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
/* Generalised xfer: copy between ca->ptr (row-major contig) and the
|
|
1541
|
+
strided region at `base` with per-dim byte strides `strides[]`.
|
|
1542
|
+
`scatter == 0` gathers (strided -> contig), 1 scatters.
|
|
1543
|
+
Used both by the legacy ca_stride_gather/scatter wrappers (pass
|
|
1544
|
+
ca->strides and ca->parent->ptr + ca->base_offset) and by the
|
|
1545
|
+
composed-fold copy_data/sync_data (pass composed strides and a
|
|
1546
|
+
root-level base). */
|
|
1547
|
+
void
|
|
1548
|
+
ca_stride_xfer_with_layout (CAStride *ca, int scatter, char *base,
|
|
1549
|
+
const ca_size_t *strides)
|
|
1550
|
+
{
|
|
1551
|
+
ca_size_t bytes = ca->bytes;
|
|
1552
|
+
char *buf = ca->ptr; /* row-major side */
|
|
1553
|
+
|
|
1554
|
+
if (ca->elements == 0) return;
|
|
1555
|
+
|
|
1556
|
+
/* Copy shape + strides locally and apply axis-merge before driving the
|
|
1557
|
+
fast paths. Existing fast paths
|
|
1558
|
+
(P1 / P1.5 / general) consume the merged shape transparently:
|
|
1559
|
+
a fully-mergeable layout collapses to ndim=1 and lands on P1's
|
|
1560
|
+
whole-contig memcpy; a partially-mergeable one collapses outer
|
|
1561
|
+
iterations and feeds the general driver larger inner_count runs. */
|
|
1562
|
+
ca_size_t mdim[CA_RANK_MAX];
|
|
1563
|
+
ca_size_t mstrides[CA_RANK_MAX];
|
|
1564
|
+
int8_t mndim = ca->ndim;
|
|
1565
|
+
{
|
|
1566
|
+
int8_t i;
|
|
1567
|
+
for (i = 0; i < mndim; i++) {
|
|
1568
|
+
mdim[i] = ca->dim[i];
|
|
1569
|
+
mstrides[i] = strides[i];
|
|
1570
|
+
}
|
|
1571
|
+
ca_stride_merge_axes(mstrides, mdim, &mndim);
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
/* P1: whole-view contiguous (in the strided side's layout) */
|
|
1575
|
+
if (ca_layout_is_contiguous(mndim, mdim, mstrides, bytes)) {
|
|
1576
|
+
if (scatter) memcpy(base, buf, ca->elements * bytes);
|
|
1577
|
+
else memcpy(buf, base, ca->elements * bytes);
|
|
1578
|
+
return;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
/* P1.5: 2D specialised fast paths for common element widths. Strides
|
|
1582
|
+
are byte-valued; the inner loops advance source/destination pointers
|
|
1583
|
+
by raw byte counts and use constant-size memcpy for the unaligned
|
|
1584
|
+
load/store (single-instruction on x86/arm64). */
|
|
1585
|
+
#define CA_STRIDE_2D_TYPED(T) \
|
|
1586
|
+
do { \
|
|
1587
|
+
ca_size_t n0 = mdim[0]; \
|
|
1588
|
+
ca_size_t n1 = mdim[1]; \
|
|
1589
|
+
ca_size_t s0 = mstrides[0]; \
|
|
1590
|
+
ca_size_t s1 = mstrides[1]; \
|
|
1591
|
+
T *bp = (T *) buf; \
|
|
1592
|
+
ca_size_t i, j; \
|
|
1593
|
+
if (scatter) { \
|
|
1594
|
+
for (i = 0; i < n0; i++) { \
|
|
1595
|
+
char *dp = base + i * s0; \
|
|
1596
|
+
for (j = 0; j < n1; j++) { \
|
|
1597
|
+
T v = *bp++; \
|
|
1598
|
+
memcpy(dp, &v, sizeof(T)); \
|
|
1599
|
+
dp += s1; \
|
|
1600
|
+
} \
|
|
1601
|
+
} \
|
|
1602
|
+
} else { \
|
|
1603
|
+
for (i = 0; i < n0; i++) { \
|
|
1604
|
+
const char *sp = base + i * s0; \
|
|
1605
|
+
for (j = 0; j < n1; j++) { \
|
|
1606
|
+
T v; \
|
|
1607
|
+
memcpy(&v, sp, sizeof(T)); \
|
|
1608
|
+
*bp++ = v; \
|
|
1609
|
+
sp += s1; \
|
|
1610
|
+
} \
|
|
1611
|
+
} \
|
|
1612
|
+
} \
|
|
1613
|
+
} while (0)
|
|
1614
|
+
|
|
1615
|
+
if (mndim == 2 && mstrides[1] != bytes) {
|
|
1616
|
+
switch (bytes) {
|
|
1617
|
+
case 1: CA_STRIDE_2D_TYPED(int8_t); return;
|
|
1618
|
+
case 2: CA_STRIDE_2D_TYPED(int16_t); return;
|
|
1619
|
+
case 4: CA_STRIDE_2D_TYPED(int32_t); return;
|
|
1620
|
+
case 8: CA_STRIDE_2D_TYPED(int64_t); return;
|
|
1621
|
+
default: break;
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
#undef CA_STRIDE_2D_TYPED
|
|
1625
|
+
|
|
1626
|
+
/* EXPLORED AND REJECTED: a cache-tiled tile-block branch here (mirroring
|
|
1627
|
+
the dispatcher / root-direct ndim>=2 branch) measured as a net loss for
|
|
1628
|
+
the `a.transpose.to_ca` family at typical sizes -- roughly 1.6-1.9x
|
|
1629
|
+
slower for ndim=3 [2,500,500] and ndim=4 [4,8,100,100]. The general
|
|
1630
|
+
driver below already cache-streams the innermost axis through
|
|
1631
|
+
ca_stride_gather_run's typed memcpy, and the per-tile L1 staging cost
|
|
1632
|
+
dominates the small inner-pair blocks these shapes produce. Do not
|
|
1633
|
+
reattempt without first showing a regime where tiling wins. */
|
|
1634
|
+
|
|
1635
|
+
/* General driver. */
|
|
1636
|
+
ca_size_t inner_count = mdim[mndim - 1];
|
|
1637
|
+
ca_size_t inner_stride = mstrides[mndim - 1];
|
|
1638
|
+
ca_size_t outer_total = ca->elements / inner_count;
|
|
1639
|
+
ca_size_t idx[CA_RANK_MAX];
|
|
1640
|
+
int8_t k;
|
|
1641
|
+
|
|
1642
|
+
for (k = 0; k < mndim; k++) idx[k] = 0;
|
|
1643
|
+
|
|
1644
|
+
ca_size_t n;
|
|
1645
|
+
for (n = 0; n < outer_total; n++) {
|
|
1646
|
+
ca_size_t off = 0;
|
|
1647
|
+
for (k = 0; k < mndim - 1; k++) off += idx[k] * mstrides[k];
|
|
1648
|
+
|
|
1649
|
+
if (scatter) {
|
|
1650
|
+
ca_stride_scatter_run(base + off, buf, bytes, inner_count, inner_stride);
|
|
1651
|
+
} else {
|
|
1652
|
+
ca_stride_gather_run(buf, base + off, bytes, inner_count, inner_stride);
|
|
1653
|
+
}
|
|
1654
|
+
buf += inner_count * bytes;
|
|
1655
|
+
|
|
1656
|
+
for (k = mndim - 2; k >= 0; k--) {
|
|
1657
|
+
if (++idx[k] < mdim[k]) break;
|
|
1658
|
+
idx[k] = 0;
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
/* ------------------------------------------------------------------- */
|
|
1664
|
+
/* Ruby-level construction and attribute readers */
|
|
1665
|
+
/* ------------------------------------------------------------------- */
|
|
1666
|
+
|
|
1667
|
+
VALUE
|
|
1668
|
+
rb_ca_stride_new (VALUE cary,
|
|
1669
|
+
int8_t data_type, ca_size_t bytes,
|
|
1670
|
+
int8_t ndim, ca_size_t *dim,
|
|
1671
|
+
ca_size_t *strides, ca_size_t base_offset)
|
|
1672
|
+
{
|
|
1673
|
+
volatile VALUE obj;
|
|
1674
|
+
CArray *parent;
|
|
1675
|
+
CAStride *ca;
|
|
1676
|
+
|
|
1677
|
+
rb_check_carray_object(cary);
|
|
1678
|
+
TypedData_Get_Struct(cary, CArray, &carray_data_type, parent);
|
|
1679
|
+
ca = ca_stride_new(CA_OBJ_STRIDE, parent,
|
|
1680
|
+
data_type, bytes, ndim, dim, strides, base_offset);
|
|
1681
|
+
obj = ca_wrap_struct(ca);
|
|
1682
|
+
rb_ca_set_parent(obj, cary);
|
|
1683
|
+
return obj;
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
static VALUE
|
|
1687
|
+
rb_cs_s_allocate (VALUE klass)
|
|
1688
|
+
{
|
|
1689
|
+
CAStride *ca;
|
|
1690
|
+
return TypedData_Make_Struct(klass, CAStride, &castride_data_type, ca);
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
static VALUE
|
|
1694
|
+
rb_cs_initialize_copy (VALUE self, VALUE other)
|
|
1695
|
+
{
|
|
1696
|
+
CAStride *ca, *cs;
|
|
1697
|
+
TypedData_Get_Struct(self, CAStride, &castride_data_type, ca);
|
|
1698
|
+
TypedData_Get_Struct(other, CAStride, &castride_data_type, cs);
|
|
1699
|
+
/* Pool framework: self was created by rb_cs_s_allocate (= TypedData_Make_Struct),
|
|
1700
|
+
so ca->_pool is NULL. Wire up the pool before ca_stride_setup so the
|
|
1701
|
+
pool branch installs dim/strides. */
|
|
1702
|
+
if ( ca_func[CA_OBJ_STRIDE].pool_init ) {
|
|
1703
|
+
ca_array_pool_alloc(ca, CA_OBJ_STRIDE, cs->ndim);
|
|
1704
|
+
}
|
|
1705
|
+
ca_stride_setup(ca, cs->obj_type, cs->parent, cs->data_type, cs->bytes,
|
|
1706
|
+
cs->ndim, cs->dim, cs->strides, cs->base_offset);
|
|
1707
|
+
rb_ca_set_parent(self, rb_ca_parent(other));
|
|
1708
|
+
return self;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
/* Returns the byte strides as an Array of integers. */
|
|
1712
|
+
static VALUE
|
|
1713
|
+
rb_cs_strides (VALUE self)
|
|
1714
|
+
{
|
|
1715
|
+
CAStride *cs;
|
|
1716
|
+
volatile VALUE ary;
|
|
1717
|
+
int8_t i;
|
|
1718
|
+
TypedData_Get_Struct(self, CAStride, &castride_data_type, cs);
|
|
1719
|
+
ary = rb_ary_new2(cs->ndim);
|
|
1720
|
+
for (i = 0; i < cs->ndim; i++) {
|
|
1721
|
+
rb_ary_store(ary, i, LL2NUM((long long) cs->strides[i]));
|
|
1722
|
+
}
|
|
1723
|
+
return ary;
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
/* Returns the byte offset from parent->ptr to the [0,...,0] element. */
|
|
1727
|
+
static VALUE
|
|
1728
|
+
rb_cs_byte_offset (VALUE self)
|
|
1729
|
+
{
|
|
1730
|
+
CAStride *cs;
|
|
1731
|
+
TypedData_Get_Struct(self, CAStride, &castride_data_type, cs);
|
|
1732
|
+
return LL2NUM((long long) cs->base_offset);
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
/* as_strided(shape:, strides:, offset: 0) -- builds a CAStride view of the
|
|
1736
|
+
receiver from raw byte strides. Inherits the receiver's data_type and
|
|
1737
|
+
bytes.
|
|
1738
|
+
|
|
1739
|
+
CAREFUL: the strides / offset are NOT bounds-checked against the
|
|
1740
|
+
receiver's memory. Every other view constructor derives strides that are
|
|
1741
|
+
known to stay inside the parent; here the caller supplies them, so an
|
|
1742
|
+
out-of-range combination reads or writes past the buffer. */
|
|
1743
|
+
static VALUE
|
|
1744
|
+
rb_ca_as_strided (int argc, VALUE *argv, VALUE self)
|
|
1745
|
+
{
|
|
1746
|
+
CArray *parent;
|
|
1747
|
+
volatile VALUE ropt = Qnil, rshape = Qnil, rstrides = Qnil, roffset = Qnil;
|
|
1748
|
+
ca_size_t shape[CA_RANK_MAX];
|
|
1749
|
+
ca_size_t strides[CA_RANK_MAX];
|
|
1750
|
+
ca_size_t base_offset = 0;
|
|
1751
|
+
int8_t ndim;
|
|
1752
|
+
long len, i;
|
|
1753
|
+
|
|
1754
|
+
rb_check_carray_object(self);
|
|
1755
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, parent);
|
|
1756
|
+
|
|
1757
|
+
rb_scan_args(argc, argv, "0:", (VALUE *) &ropt);
|
|
1758
|
+
if (NIL_P(ropt)) {
|
|
1759
|
+
rb_raise(rb_eArgError,
|
|
1760
|
+
"as_strided requires keyword arguments: shape:, strides:");
|
|
1761
|
+
}
|
|
1762
|
+
rb_scan_options(ropt, "shape,strides,offset",
|
|
1763
|
+
&rshape, &rstrides, &roffset);
|
|
1764
|
+
if (NIL_P(rshape) || NIL_P(rstrides)) {
|
|
1765
|
+
rb_raise(rb_eArgError,
|
|
1766
|
+
"as_strided requires both shape: and strides: keywords");
|
|
1767
|
+
}
|
|
1768
|
+
Check_Type(rshape, T_ARRAY);
|
|
1769
|
+
Check_Type(rstrides, T_ARRAY);
|
|
1770
|
+
len = RARRAY_LEN(rshape);
|
|
1771
|
+
if (RARRAY_LEN(rstrides) != len) {
|
|
1772
|
+
rb_raise(rb_eArgError,
|
|
1773
|
+
"shape (%ld) and strides (%ld) length mismatch",
|
|
1774
|
+
len, RARRAY_LEN(rstrides));
|
|
1775
|
+
}
|
|
1776
|
+
if (len <= 0 || len > CA_RANK_MAX) {
|
|
1777
|
+
rb_raise(rb_eArgError, "invalid ndim %ld", len);
|
|
1778
|
+
}
|
|
1779
|
+
ndim = (int8_t) len;
|
|
1780
|
+
for (i = 0; i < len; i++) {
|
|
1781
|
+
shape[i] = NUM2SIZE(RARRAY_AREF(rshape, i));
|
|
1782
|
+
strides[i] = NUM2SIZE(RARRAY_AREF(rstrides, i));
|
|
1783
|
+
}
|
|
1784
|
+
if (! NIL_P(roffset)) {
|
|
1785
|
+
base_offset = NUM2SIZE(roffset);
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
return rb_ca_stride_new(self, parent->data_type, parent->bytes,
|
|
1789
|
+
ndim, shape, strides, base_offset);
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
/* sliding_windows(*window, step: nil) -- overlapping-window view over every
|
|
1793
|
+
axis. Parent [d0..dN-1] becomes [(di-wi)/si+1 ..., w0..wN-1]; result rank
|
|
1794
|
+
is 2*ndim, so the parent's ndim must not exceed CA_RANK_MAX / 2. Truncate
|
|
1795
|
+
mode: no padding. Windows overlap, so the view aliases each parent cell
|
|
1796
|
+
from several positions. */
|
|
1797
|
+
static VALUE
|
|
1798
|
+
rb_ca_sliding_windows (int argc, VALUE *argv, VALUE self)
|
|
1799
|
+
{
|
|
1800
|
+
CArray *parent;
|
|
1801
|
+
volatile VALUE rposary = Qnil, ropt = Qnil, rstep = Qnil;
|
|
1802
|
+
ca_size_t window[CA_RANK_MAX];
|
|
1803
|
+
ca_size_t step[CA_RANK_MAX];
|
|
1804
|
+
ca_size_t outdim[CA_RANK_MAX];
|
|
1805
|
+
ca_size_t outstrides[CA_RANK_MAX];
|
|
1806
|
+
ca_size_t parent_byte_stride[CA_RANK_MAX];
|
|
1807
|
+
int8_t i, ndim;
|
|
1808
|
+
long nargs;
|
|
1809
|
+
|
|
1810
|
+
rb_check_carray_object(self);
|
|
1811
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, parent);
|
|
1812
|
+
|
|
1813
|
+
ndim = parent->ndim;
|
|
1814
|
+
if (2 * (int) ndim > CA_RANK_MAX) {
|
|
1815
|
+
rb_raise(rb_eArgError,
|
|
1816
|
+
"sliding_windows: result rank %d exceeds CA_RANK_MAX (%d)",
|
|
1817
|
+
2 * (int) ndim, CA_RANK_MAX);
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
rb_scan_args(argc, argv, "*:", (VALUE *) &rposary, (VALUE *) &ropt);
|
|
1821
|
+
if (! NIL_P(ropt)) {
|
|
1822
|
+
rb_scan_options(ropt, "step", &rstep);
|
|
1823
|
+
}
|
|
1824
|
+
nargs = RARRAY_LEN(rposary);
|
|
1825
|
+
|
|
1826
|
+
if (nargs == 1 && TYPE(RARRAY_AREF(rposary, 0)) == T_ARRAY) {
|
|
1827
|
+
volatile VALUE wary = RARRAY_AREF(rposary, 0);
|
|
1828
|
+
if (RARRAY_LEN(wary) != ndim) {
|
|
1829
|
+
rb_raise(rb_eArgError,
|
|
1830
|
+
"sliding_windows: window length (%ld) must equal ndim (%d)",
|
|
1831
|
+
RARRAY_LEN(wary), (int) ndim);
|
|
1832
|
+
}
|
|
1833
|
+
for (i = 0; i < ndim; i++) {
|
|
1834
|
+
window[i] = NUM2SIZE(RARRAY_AREF(wary, i));
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
else if (nargs == ndim) {
|
|
1838
|
+
for (i = 0; i < ndim; i++) {
|
|
1839
|
+
window[i] = NUM2SIZE(RARRAY_AREF(rposary, i));
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
else {
|
|
1843
|
+
rb_raise(rb_eArgError,
|
|
1844
|
+
"sliding_windows: expected %d window sizes (or one Array), got %ld",
|
|
1845
|
+
(int) ndim, nargs);
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
if (NIL_P(rstep)) {
|
|
1849
|
+
for (i = 0; i < ndim; i++) step[i] = 1;
|
|
1850
|
+
}
|
|
1851
|
+
else if (TYPE(rstep) == T_ARRAY) {
|
|
1852
|
+
if (RARRAY_LEN(rstep) != ndim) {
|
|
1853
|
+
rb_raise(rb_eArgError,
|
|
1854
|
+
"sliding_windows: step length (%ld) must equal ndim (%d)",
|
|
1855
|
+
RARRAY_LEN(rstep), (int) ndim);
|
|
1856
|
+
}
|
|
1857
|
+
for (i = 0; i < ndim; i++) {
|
|
1858
|
+
step[i] = NUM2SIZE(RARRAY_AREF(rstep, i));
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
else {
|
|
1862
|
+
ca_size_t s = NUM2SIZE(rstep);
|
|
1863
|
+
for (i = 0; i < ndim; i++) step[i] = s;
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
for (i = 0; i < ndim; i++) {
|
|
1867
|
+
if (window[i] < 1) {
|
|
1868
|
+
rb_raise(rb_eArgError,
|
|
1869
|
+
"sliding_windows: window[%d]=%lld must be >= 1",
|
|
1870
|
+
(int) i, (long long) window[i]);
|
|
1871
|
+
}
|
|
1872
|
+
if (window[i] > parent->dim[i]) {
|
|
1873
|
+
rb_raise(rb_eArgError,
|
|
1874
|
+
"sliding_windows: window[%d]=%lld larger than parent dim[%d]=%lld",
|
|
1875
|
+
(int) i, (long long) window[i],
|
|
1876
|
+
(int) i, (long long) parent->dim[i]);
|
|
1877
|
+
}
|
|
1878
|
+
if (step[i] < 1) {
|
|
1879
|
+
rb_raise(rb_eArgError,
|
|
1880
|
+
"sliding_windows: step[%d]=%lld must be >= 1",
|
|
1881
|
+
(int) i, (long long) step[i]);
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
{
|
|
1886
|
+
ca_size_t s = parent->bytes;
|
|
1887
|
+
for (i = ndim - 1; i >= 0; i--) {
|
|
1888
|
+
parent_byte_stride[i] = s;
|
|
1889
|
+
s *= parent->dim[i];
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
for (i = 0; i < ndim; i++) {
|
|
1894
|
+
outdim[i] = (parent->dim[i] - window[i]) / step[i] + 1;
|
|
1895
|
+
outdim[ndim + i] = window[i];
|
|
1896
|
+
outstrides[i] = parent_byte_stride[i] * step[i];
|
|
1897
|
+
outstrides[ndim + i] = parent_byte_stride[i];
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
return rb_ca_stride_new(self, parent->data_type, parent->bytes,
|
|
1901
|
+
(int8_t)(2 * ndim), outdim, outstrides, 0);
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
/* unfold(*window, step: nil) -- sliding_windows over the leading `S` axes
|
|
1905
|
+
only, with the remaining `ndim - S` trailing axes riding along at their
|
|
1906
|
+
original strides. The window axes are inserted before the trailing ones,
|
|
1907
|
+
so the result rank is ndim + S. With S == ndim this is exactly
|
|
1908
|
+
sliding_windows. */
|
|
1909
|
+
static VALUE
|
|
1910
|
+
rb_ca_unfold (int argc, VALUE *argv, VALUE self)
|
|
1911
|
+
{
|
|
1912
|
+
CArray *parent;
|
|
1913
|
+
volatile VALUE rposary = Qnil, ropt = Qnil, rstep = Qnil;
|
|
1914
|
+
ca_size_t window[CA_RANK_MAX];
|
|
1915
|
+
ca_size_t step[CA_RANK_MAX];
|
|
1916
|
+
ca_size_t outdim[CA_RANK_MAX];
|
|
1917
|
+
ca_size_t outstrides[CA_RANK_MAX];
|
|
1918
|
+
ca_size_t parent_byte_stride[CA_RANK_MAX];
|
|
1919
|
+
int8_t i, ndim, nspatial, ntrail;
|
|
1920
|
+
int outrank;
|
|
1921
|
+
long nargs;
|
|
1922
|
+
|
|
1923
|
+
rb_check_carray_object(self);
|
|
1924
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, parent);
|
|
1925
|
+
|
|
1926
|
+
ndim = parent->ndim;
|
|
1927
|
+
|
|
1928
|
+
rb_scan_args(argc, argv, "*:", (VALUE *) &rposary, (VALUE *) &ropt);
|
|
1929
|
+
if (! NIL_P(ropt)) {
|
|
1930
|
+
rb_scan_options(ropt, "step", &rstep);
|
|
1931
|
+
}
|
|
1932
|
+
nargs = RARRAY_LEN(rposary);
|
|
1933
|
+
|
|
1934
|
+
if (nargs == 1 && TYPE(RARRAY_AREF(rposary, 0)) == T_ARRAY) {
|
|
1935
|
+
volatile VALUE wary = RARRAY_AREF(rposary, 0);
|
|
1936
|
+
if (RARRAY_LEN(wary) < 1 || RARRAY_LEN(wary) > ndim) {
|
|
1937
|
+
rb_raise(rb_eArgError,
|
|
1938
|
+
"unfold: window length (%ld) must be between 1 and ndim (%d)",
|
|
1939
|
+
RARRAY_LEN(wary), (int) ndim);
|
|
1940
|
+
}
|
|
1941
|
+
nspatial = (int8_t) RARRAY_LEN(wary);
|
|
1942
|
+
for (i = 0; i < nspatial; i++) {
|
|
1943
|
+
window[i] = NUM2SIZE(RARRAY_AREF(wary, i));
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
else if (nargs >= 1 && nargs <= ndim) {
|
|
1947
|
+
nspatial = (int8_t) nargs;
|
|
1948
|
+
for (i = 0; i < nspatial; i++) {
|
|
1949
|
+
window[i] = NUM2SIZE(RARRAY_AREF(rposary, i));
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
else {
|
|
1953
|
+
rb_raise(rb_eArgError,
|
|
1954
|
+
"unfold: expected 1..%d window sizes (or one Array), got %ld",
|
|
1955
|
+
(int) ndim, nargs);
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
ntrail = ndim - nspatial;
|
|
1959
|
+
outrank = (int) ndim + (int) nspatial;
|
|
1960
|
+
if (outrank > CA_RANK_MAX) {
|
|
1961
|
+
rb_raise(rb_eArgError,
|
|
1962
|
+
"unfold: result rank %d exceeds CA_RANK_MAX (%d)",
|
|
1963
|
+
outrank, CA_RANK_MAX);
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
if (NIL_P(rstep)) {
|
|
1967
|
+
for (i = 0; i < nspatial; i++) step[i] = 1;
|
|
1968
|
+
}
|
|
1969
|
+
else if (TYPE(rstep) == T_ARRAY) {
|
|
1970
|
+
if (RARRAY_LEN(rstep) != nspatial) {
|
|
1971
|
+
rb_raise(rb_eArgError,
|
|
1972
|
+
"unfold: step length (%ld) must equal window length (%d)",
|
|
1973
|
+
RARRAY_LEN(rstep), (int) nspatial);
|
|
1974
|
+
}
|
|
1975
|
+
for (i = 0; i < nspatial; i++) {
|
|
1976
|
+
step[i] = NUM2SIZE(RARRAY_AREF(rstep, i));
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
else {
|
|
1980
|
+
ca_size_t s = NUM2SIZE(rstep);
|
|
1981
|
+
for (i = 0; i < nspatial; i++) step[i] = s;
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
for (i = 0; i < nspatial; i++) {
|
|
1985
|
+
if (window[i] < 1) {
|
|
1986
|
+
rb_raise(rb_eArgError,
|
|
1987
|
+
"unfold: window[%d]=%lld must be >= 1",
|
|
1988
|
+
(int) i, (long long) window[i]);
|
|
1989
|
+
}
|
|
1990
|
+
if (window[i] > parent->dim[i]) {
|
|
1991
|
+
rb_raise(rb_eArgError,
|
|
1992
|
+
"unfold: window[%d]=%lld larger than parent dim[%d]=%lld",
|
|
1993
|
+
(int) i, (long long) window[i],
|
|
1994
|
+
(int) i, (long long) parent->dim[i]);
|
|
1995
|
+
}
|
|
1996
|
+
if (step[i] < 1) {
|
|
1997
|
+
rb_raise(rb_eArgError,
|
|
1998
|
+
"unfold: step[%d]=%lld must be >= 1",
|
|
1999
|
+
(int) i, (long long) step[i]);
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
{
|
|
2004
|
+
ca_size_t s = parent->bytes;
|
|
2005
|
+
for (i = ndim - 1; i >= 0; i--) {
|
|
2006
|
+
parent_byte_stride[i] = s;
|
|
2007
|
+
s *= parent->dim[i];
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
for (i = 0; i < nspatial; i++) {
|
|
2012
|
+
outdim[i] = (parent->dim[i] - window[i]) / step[i] + 1;
|
|
2013
|
+
outdim[nspatial + i] = window[i];
|
|
2014
|
+
outstrides[i] = parent_byte_stride[i] * step[i];
|
|
2015
|
+
outstrides[nspatial + i] = parent_byte_stride[i];
|
|
2016
|
+
}
|
|
2017
|
+
for (i = 0; i < ntrail; i++) {
|
|
2018
|
+
outdim[2 * nspatial + i] = parent->dim[nspatial + i];
|
|
2019
|
+
outstrides[2 * nspatial + i] = parent_byte_stride[nspatial + i];
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
return rb_ca_stride_new(self, parent->data_type, parent->bytes,
|
|
2023
|
+
(int8_t) outrank, outdim, outstrides, 0);
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
/* block_view(*block) -- non-overlapping tile view. Parent [d0..dN-1]
|
|
2027
|
+
becomes [d0/b0 ..., b0..bN-1]; result rank is 2*ndim. Unlike
|
|
2028
|
+
sliding_windows each parent dim must divide evenly, so no cell is dropped
|
|
2029
|
+
and none is aliased twice. */
|
|
2030
|
+
static VALUE
|
|
2031
|
+
rb_ca_block_view (int argc, VALUE *argv, VALUE self)
|
|
2032
|
+
{
|
|
2033
|
+
CArray *parent;
|
|
2034
|
+
volatile VALUE rposary = Qnil, ropt = Qnil;
|
|
2035
|
+
ca_size_t block[CA_RANK_MAX];
|
|
2036
|
+
ca_size_t outdim[CA_RANK_MAX];
|
|
2037
|
+
ca_size_t outstrides[CA_RANK_MAX];
|
|
2038
|
+
ca_size_t parent_byte_stride[CA_RANK_MAX];
|
|
2039
|
+
int8_t i, ndim;
|
|
2040
|
+
long nargs;
|
|
2041
|
+
|
|
2042
|
+
rb_check_carray_object(self);
|
|
2043
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, parent);
|
|
2044
|
+
|
|
2045
|
+
ndim = parent->ndim;
|
|
2046
|
+
if (2 * (int) ndim > CA_RANK_MAX) {
|
|
2047
|
+
rb_raise(rb_eArgError,
|
|
2048
|
+
"block_view: result rank %d exceeds CA_RANK_MAX (%d)",
|
|
2049
|
+
2 * (int) ndim, CA_RANK_MAX);
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
rb_scan_args(argc, argv, "*:", (VALUE *) &rposary, (VALUE *) &ropt);
|
|
2053
|
+
rb_reject_options(ropt);
|
|
2054
|
+
nargs = RARRAY_LEN(rposary);
|
|
2055
|
+
|
|
2056
|
+
if (nargs == 1 && TYPE(RARRAY_AREF(rposary, 0)) == T_ARRAY) {
|
|
2057
|
+
volatile VALUE bary = RARRAY_AREF(rposary, 0);
|
|
2058
|
+
if (RARRAY_LEN(bary) != ndim) {
|
|
2059
|
+
rb_raise(rb_eArgError,
|
|
2060
|
+
"block_view: block length (%ld) must equal ndim (%d)",
|
|
2061
|
+
RARRAY_LEN(bary), (int) ndim);
|
|
2062
|
+
}
|
|
2063
|
+
for (i = 0; i < ndim; i++) {
|
|
2064
|
+
block[i] = NUM2SIZE(RARRAY_AREF(bary, i));
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
else if (nargs == ndim) {
|
|
2068
|
+
for (i = 0; i < ndim; i++) {
|
|
2069
|
+
block[i] = NUM2SIZE(RARRAY_AREF(rposary, i));
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
else {
|
|
2073
|
+
rb_raise(rb_eArgError,
|
|
2074
|
+
"block_view: expected %d block sizes (or one Array), got %ld",
|
|
2075
|
+
(int) ndim, nargs);
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
for (i = 0; i < ndim; i++) {
|
|
2079
|
+
if (block[i] < 1) {
|
|
2080
|
+
rb_raise(rb_eArgError,
|
|
2081
|
+
"block_view: block[%d]=%lld must be >= 1",
|
|
2082
|
+
(int) i, (long long) block[i]);
|
|
2083
|
+
}
|
|
2084
|
+
if (parent->dim[i] % block[i] != 0) {
|
|
2085
|
+
rb_raise(rb_eArgError,
|
|
2086
|
+
"block_view: parent dim[%d]=%lld is not divisible by block[%d]=%lld",
|
|
2087
|
+
(int) i, (long long) parent->dim[i],
|
|
2088
|
+
(int) i, (long long) block[i]);
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
{
|
|
2093
|
+
ca_size_t s = parent->bytes;
|
|
2094
|
+
for (i = ndim - 1; i >= 0; i--) {
|
|
2095
|
+
parent_byte_stride[i] = s;
|
|
2096
|
+
s *= parent->dim[i];
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
for (i = 0; i < ndim; i++) {
|
|
2101
|
+
outdim[i] = parent->dim[i] / block[i];
|
|
2102
|
+
outdim[ndim + i] = block[i];
|
|
2103
|
+
outstrides[i] = parent_byte_stride[i] * block[i];
|
|
2104
|
+
outstrides[ndim + i] = parent_byte_stride[i];
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
return rb_ca_stride_new(self, parent->data_type, parent->bytes,
|
|
2108
|
+
(int8_t)(2 * ndim), outdim, outstrides, 0);
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
/* defined in ca_obj_transpose.c */
|
|
2112
|
+
extern VALUE rb_ca_trans_new (VALUE cary, ca_size_t *imap);
|
|
2113
|
+
|
|
2114
|
+
/* dim_view(*axes) -- moves the given axes to the front, keeping the rest in
|
|
2115
|
+
order. A thin alias over `transposed` that names the intent, so it returns
|
|
2116
|
+
a CATranspose and inherits its alias path and mask propagation. */
|
|
2117
|
+
static VALUE
|
|
2118
|
+
rb_ca_dim_view (int argc, VALUE *argv, VALUE self)
|
|
2119
|
+
{
|
|
2120
|
+
CArray *parent;
|
|
2121
|
+
volatile VALUE rposary = Qnil, ropt = Qnil;
|
|
2122
|
+
ca_size_t iter_axes[CA_RANK_MAX];
|
|
2123
|
+
ca_size_t imap[CA_RANK_MAX];
|
|
2124
|
+
int8_t seen[CA_RANK_MAX];
|
|
2125
|
+
int8_t ndim, n_iter, i, k;
|
|
2126
|
+
long nargs;
|
|
2127
|
+
|
|
2128
|
+
rb_check_carray_object(self);
|
|
2129
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, parent);
|
|
2130
|
+
ndim = parent->ndim;
|
|
2131
|
+
|
|
2132
|
+
rb_scan_args(argc, argv, "*:", (VALUE *) &rposary, (VALUE *) &ropt);
|
|
2133
|
+
rb_reject_options(ropt);
|
|
2134
|
+
nargs = RARRAY_LEN(rposary);
|
|
2135
|
+
|
|
2136
|
+
/* Accept either a single Array of axes or variadic Integers. */
|
|
2137
|
+
if (nargs == 1 && TYPE(RARRAY_AREF(rposary, 0)) == T_ARRAY) {
|
|
2138
|
+
volatile VALUE aary = RARRAY_AREF(rposary, 0);
|
|
2139
|
+
n_iter = (int8_t) RARRAY_LEN(aary);
|
|
2140
|
+
if (n_iter < 1) {
|
|
2141
|
+
rb_raise(rb_eArgError, "dim_view: at least one iteration axis required");
|
|
2142
|
+
}
|
|
2143
|
+
if (n_iter > ndim) {
|
|
2144
|
+
rb_raise(rb_eArgError,
|
|
2145
|
+
"dim_view: too many iteration axes (%d) for ndim (%d)",
|
|
2146
|
+
(int) n_iter, (int) ndim);
|
|
2147
|
+
}
|
|
2148
|
+
for (i = 0; i < n_iter; i++) {
|
|
2149
|
+
iter_axes[i] = NUM2SIZE(RARRAY_AREF(aary, i));
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
else if (nargs >= 1) {
|
|
2153
|
+
if (nargs > ndim) {
|
|
2154
|
+
rb_raise(rb_eArgError,
|
|
2155
|
+
"dim_view: too many iteration axes (%ld) for ndim (%d)",
|
|
2156
|
+
nargs, (int) ndim);
|
|
2157
|
+
}
|
|
2158
|
+
n_iter = (int8_t) nargs;
|
|
2159
|
+
for (i = 0; i < n_iter; i++) {
|
|
2160
|
+
iter_axes[i] = NUM2SIZE(RARRAY_AREF(rposary, i));
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
else {
|
|
2164
|
+
rb_raise(rb_eArgError,
|
|
2165
|
+
"dim_view: at least one iteration axis required");
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
/* Normalize negative indices and validate range / distinctness. */
|
|
2169
|
+
for (i = 0; i < ndim; i++) seen[i] = 0;
|
|
2170
|
+
for (i = 0; i < n_iter; i++) {
|
|
2171
|
+
ca_size_t a = iter_axes[i];
|
|
2172
|
+
if (a < 0) a += ndim;
|
|
2173
|
+
if (a < 0 || a >= ndim) {
|
|
2174
|
+
rb_raise(rb_eArgError,
|
|
2175
|
+
"dim_view: axis %lld out of range for ndim %d",
|
|
2176
|
+
(long long) iter_axes[i], (int) ndim);
|
|
2177
|
+
}
|
|
2178
|
+
if (seen[a]) {
|
|
2179
|
+
rb_raise(rb_eArgError,
|
|
2180
|
+
"dim_view: duplicate iteration axis %lld",
|
|
2181
|
+
(long long) a);
|
|
2182
|
+
}
|
|
2183
|
+
seen[a] = 1;
|
|
2184
|
+
iter_axes[i] = a;
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
/* imap: iter axes first (in given order), then remaining axes
|
|
2188
|
+
(in original order). */
|
|
2189
|
+
for (i = 0; i < n_iter; i++) {
|
|
2190
|
+
imap[i] = iter_axes[i];
|
|
2191
|
+
}
|
|
2192
|
+
k = n_iter;
|
|
2193
|
+
for (i = 0; i < ndim; i++) {
|
|
2194
|
+
if (! seen[i]) imap[k++] = i;
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
return rb_ca_trans_new(self, imap);
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
/* flip(*axes) -- reverses the listed axes by negating their strides; with no
|
|
2201
|
+
argument every axis is flipped. The named counterpart of the indexer form
|
|
2202
|
+
ca[-1..0, nil, -1..0]: both give a true negative-stride view, zero copy and
|
|
2203
|
+
write-through. */
|
|
2204
|
+
/* Build the flipped CAStride view from a per-axis flip[] flag array.
|
|
2205
|
+
* Shared by rb_ca_flip (Ruby entry) and rb_ca_flip_axis (C-callable entry).
|
|
2206
|
+
* Each flip[i] == 1 reverses axis i; 0 leaves it as-is. */
|
|
2207
|
+
static VALUE
|
|
2208
|
+
rb_ca_flip_build_view (VALUE self, CArray *parent, const int8_t *flip)
|
|
2209
|
+
{
|
|
2210
|
+
int8_t ndim = parent->ndim;
|
|
2211
|
+
int8_t i;
|
|
2212
|
+
ca_size_t outdim[CA_RANK_MAX];
|
|
2213
|
+
ca_size_t outstrides[CA_RANK_MAX];
|
|
2214
|
+
ca_size_t parent_byte_stride[CA_RANK_MAX];
|
|
2215
|
+
ca_size_t base_offset = 0;
|
|
2216
|
+
|
|
2217
|
+
/* parent row-major byte strides */
|
|
2218
|
+
{
|
|
2219
|
+
ca_size_t s = parent->bytes;
|
|
2220
|
+
for (i = ndim - 1; i >= 0; i--) {
|
|
2221
|
+
parent_byte_stride[i] = s;
|
|
2222
|
+
s *= parent->dim[i];
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
/* Build flipped strides and the corresponding base_offset.
|
|
2227
|
+
A flipped axis i contributes (dim[i]-1)*parent_byte_stride[i]
|
|
2228
|
+
to base_offset and has its stride sign inverted. */
|
|
2229
|
+
for (i = 0; i < ndim; i++) {
|
|
2230
|
+
outdim[i] = parent->dim[i];
|
|
2231
|
+
if (flip[i]) {
|
|
2232
|
+
outstrides[i] = -parent_byte_stride[i];
|
|
2233
|
+
base_offset += (parent->dim[i] - 1) * parent_byte_stride[i];
|
|
2234
|
+
}
|
|
2235
|
+
else {
|
|
2236
|
+
outstrides[i] = parent_byte_stride[i];
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
VALUE obj = rb_ca_stride_new(self, parent->data_type, parent->bytes,
|
|
2241
|
+
ndim, outdim, outstrides, base_offset);
|
|
2242
|
+
CA_FACE_LIFT_IF_FACE(obj, self, parent);
|
|
2243
|
+
return obj;
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
/* C-callable entry: flip a single axis (= the common case).
|
|
2247
|
+
* No rb_scan_args, safe to call directly from C. axis is normalized
|
|
2248
|
+
* here (Python-style negative allowed). For ext authors. */
|
|
2249
|
+
VALUE
|
|
2250
|
+
rb_ca_flip_axis (VALUE self, long axis)
|
|
2251
|
+
{
|
|
2252
|
+
CArray *parent;
|
|
2253
|
+
rb_check_carray_object(self);
|
|
2254
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, parent);
|
|
2255
|
+
int8_t ndim = parent->ndim;
|
|
2256
|
+
long a = (axis < 0) ? (axis + ndim) : axis;
|
|
2257
|
+
if (a < 0 || a >= ndim) {
|
|
2258
|
+
rb_raise(rb_eArgError,
|
|
2259
|
+
"flip_axis: axis %ld out of range for ndim %d",
|
|
2260
|
+
axis, (int) ndim);
|
|
2261
|
+
}
|
|
2262
|
+
int8_t flip[CA_RANK_MAX] = {0};
|
|
2263
|
+
flip[a] = 1;
|
|
2264
|
+
return rb_ca_flip_build_view(self, parent, flip);
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
/* Ruby binding entry: parses *args (axes) -> builds flip[] -> forwards. */
|
|
2268
|
+
static VALUE
|
|
2269
|
+
rb_ca_flip (int argc, VALUE *argv, VALUE self)
|
|
2270
|
+
{
|
|
2271
|
+
CArray *parent;
|
|
2272
|
+
volatile VALUE rposary = Qnil, ropt = Qnil;
|
|
2273
|
+
ca_size_t axes[CA_RANK_MAX];
|
|
2274
|
+
int8_t flip[CA_RANK_MAX];
|
|
2275
|
+
int8_t ndim, n_axes, i;
|
|
2276
|
+
long nargs;
|
|
2277
|
+
|
|
2278
|
+
rb_check_carray_object(self);
|
|
2279
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, parent);
|
|
2280
|
+
ndim = parent->ndim;
|
|
2281
|
+
|
|
2282
|
+
rb_scan_args(argc, argv, "*:", (VALUE *) &rposary, (VALUE *) &ropt);
|
|
2283
|
+
rb_reject_options(ropt);
|
|
2284
|
+
nargs = RARRAY_LEN(rposary);
|
|
2285
|
+
|
|
2286
|
+
for (i = 0; i < ndim; i++) flip[i] = 0;
|
|
2287
|
+
|
|
2288
|
+
if (nargs == 0) {
|
|
2289
|
+
/* No args: flip every axis. */
|
|
2290
|
+
for (i = 0; i < ndim; i++) flip[i] = 1;
|
|
2291
|
+
n_axes = ndim;
|
|
2292
|
+
}
|
|
2293
|
+
else {
|
|
2294
|
+
if (nargs == 1 && TYPE(RARRAY_AREF(rposary, 0)) == T_ARRAY) {
|
|
2295
|
+
volatile VALUE aary = RARRAY_AREF(rposary, 0);
|
|
2296
|
+
n_axes = (int8_t) RARRAY_LEN(aary);
|
|
2297
|
+
if (n_axes > ndim) {
|
|
2298
|
+
rb_raise(rb_eArgError,
|
|
2299
|
+
"flip: too many axes (%d) for ndim (%d)",
|
|
2300
|
+
(int) n_axes, (int) ndim);
|
|
2301
|
+
}
|
|
2302
|
+
for (i = 0; i < n_axes; i++) {
|
|
2303
|
+
axes[i] = NUM2SIZE(RARRAY_AREF(aary, i));
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
else {
|
|
2307
|
+
if (nargs > ndim) {
|
|
2308
|
+
rb_raise(rb_eArgError,
|
|
2309
|
+
"flip: too many axes (%ld) for ndim (%d)",
|
|
2310
|
+
nargs, (int) ndim);
|
|
2311
|
+
}
|
|
2312
|
+
n_axes = (int8_t) nargs;
|
|
2313
|
+
for (i = 0; i < n_axes; i++) {
|
|
2314
|
+
axes[i] = NUM2SIZE(RARRAY_AREF(rposary, i));
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
|
|
2318
|
+
for (i = 0; i < n_axes; i++) {
|
|
2319
|
+
ca_size_t a = axes[i];
|
|
2320
|
+
if (a < 0) a += ndim;
|
|
2321
|
+
if (a < 0 || a >= ndim) {
|
|
2322
|
+
rb_raise(rb_eArgError,
|
|
2323
|
+
"flip: axis %lld out of range for ndim %d",
|
|
2324
|
+
(long long) axes[i], (int) ndim);
|
|
2325
|
+
}
|
|
2326
|
+
if (flip[a]) {
|
|
2327
|
+
rb_raise(rb_eArgError,
|
|
2328
|
+
"flip: duplicate axis %lld",
|
|
2329
|
+
(long long) a);
|
|
2330
|
+
}
|
|
2331
|
+
flip[a] = 1;
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
(void) n_axes; /* unused after validation */
|
|
2336
|
+
return rb_ca_flip_build_view(self, parent, flip);
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
/* diagonal(offset = 0, axis: [0, 1]) -- view of one diagonal of the parent.
|
|
2340
|
+
The two designated axes collapse into a single diagonal axis appended at
|
|
2341
|
+
the END of the result; the remaining axes keep their order in front.
|
|
2342
|
+
`offset` shifts off the main diagonal (positive = super, negative = sub);
|
|
2343
|
+
an offset past the relevant axis yields an empty view. */
|
|
2344
|
+
static VALUE
|
|
2345
|
+
rb_ca_diagonal (int argc, VALUE *argv, VALUE self)
|
|
2346
|
+
{
|
|
2347
|
+
CArray *parent;
|
|
2348
|
+
volatile VALUE rposary = Qnil, ropt = Qnil;
|
|
2349
|
+
volatile VALUE raxis = Qnil, roffset = Qnil;
|
|
2350
|
+
ca_size_t parent_byte_stride[CA_RANK_MAX];
|
|
2351
|
+
ca_size_t outdim[CA_RANK_MAX];
|
|
2352
|
+
ca_size_t outstrides[CA_RANK_MAX];
|
|
2353
|
+
ca_size_t offset = 0;
|
|
2354
|
+
ca_size_t base_offset = 0;
|
|
2355
|
+
ca_size_t diag_len;
|
|
2356
|
+
ca_size_t ai, aj; /* normalized axis indices */
|
|
2357
|
+
int8_t ndim, out_k, i;
|
|
2358
|
+
long nargs;
|
|
2359
|
+
|
|
2360
|
+
rb_check_carray_object(self);
|
|
2361
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, parent);
|
|
2362
|
+
ndim = parent->ndim;
|
|
2363
|
+
|
|
2364
|
+
if (ndim < 2) {
|
|
2365
|
+
rb_raise(rb_eArgError, "diagonal: requires ndim >= 2 (got %d)", (int) ndim);
|
|
2366
|
+
}
|
|
2367
|
+
|
|
2368
|
+
rb_scan_args(argc, argv, "*:", (VALUE *) &rposary, (VALUE *) &ropt);
|
|
2369
|
+
if (! NIL_P(ropt)) {
|
|
2370
|
+
rb_scan_options(ropt, "axis,offset", &raxis, &roffset);
|
|
2371
|
+
}
|
|
2372
|
+
nargs = RARRAY_LEN(rposary);
|
|
2373
|
+
|
|
2374
|
+
/* offset from positional or keyword (not both) */
|
|
2375
|
+
if (nargs == 0) {
|
|
2376
|
+
if (! NIL_P(roffset)) offset = NUM2SIZE(roffset);
|
|
2377
|
+
}
|
|
2378
|
+
else if (nargs == 1) {
|
|
2379
|
+
if (! NIL_P(roffset)) {
|
|
2380
|
+
rb_raise(rb_eArgError,
|
|
2381
|
+
"diagonal: give offset positionally OR as keyword, not both");
|
|
2382
|
+
}
|
|
2383
|
+
offset = NUM2SIZE(RARRAY_AREF(rposary, 0));
|
|
2384
|
+
}
|
|
2385
|
+
else {
|
|
2386
|
+
rb_raise(rb_eArgError,
|
|
2387
|
+
"diagonal: too many positional args (got %ld, expected 0 or 1)",
|
|
2388
|
+
nargs);
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
/* axes: keyword, default [0, 1] */
|
|
2392
|
+
ai = 0;
|
|
2393
|
+
aj = 1;
|
|
2394
|
+
if (! NIL_P(raxis)) {
|
|
2395
|
+
if (TYPE(raxis) != T_ARRAY || RARRAY_LEN(raxis) != 2) {
|
|
2396
|
+
rb_raise(rb_eArgError,
|
|
2397
|
+
"diagonal: axis: must be an Array of 2 integers");
|
|
2398
|
+
}
|
|
2399
|
+
ai = NUM2SIZE(RARRAY_AREF(raxis, 0));
|
|
2400
|
+
aj = NUM2SIZE(RARRAY_AREF(raxis, 1));
|
|
2401
|
+
if (ai < 0) ai += ndim;
|
|
2402
|
+
if (aj < 0) aj += ndim;
|
|
2403
|
+
if (ai < 0 || ai >= ndim) {
|
|
2404
|
+
rb_raise(rb_eArgError, "diagonal: axis[0] out of range for ndim %d",
|
|
2405
|
+
(int) ndim);
|
|
2406
|
+
}
|
|
2407
|
+
if (aj < 0 || aj >= ndim) {
|
|
2408
|
+
rb_raise(rb_eArgError, "diagonal: axis[1] out of range for ndim %d",
|
|
2409
|
+
(int) ndim);
|
|
2410
|
+
}
|
|
2411
|
+
if (ai == aj) {
|
|
2412
|
+
rb_raise(rb_eArgError, "diagonal: axis[0] and axis[1] must be distinct");
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
/* parent row-major byte strides */
|
|
2417
|
+
{
|
|
2418
|
+
ca_size_t s = parent->bytes;
|
|
2419
|
+
for (i = ndim - 1; i >= 0; i--) {
|
|
2420
|
+
parent_byte_stride[i] = s;
|
|
2421
|
+
s *= parent->dim[i];
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2425
|
+
/* compute diagonal length and base_offset */
|
|
2426
|
+
if (offset >= 0) {
|
|
2427
|
+
if (offset >= parent->dim[aj]) {
|
|
2428
|
+
diag_len = 0;
|
|
2429
|
+
}
|
|
2430
|
+
else {
|
|
2431
|
+
ca_size_t a = parent->dim[ai];
|
|
2432
|
+
ca_size_t b = parent->dim[aj] - offset;
|
|
2433
|
+
diag_len = (a < b) ? a : b;
|
|
2434
|
+
}
|
|
2435
|
+
base_offset = offset * parent_byte_stride[aj];
|
|
2436
|
+
}
|
|
2437
|
+
else {
|
|
2438
|
+
ca_size_t neg = -offset;
|
|
2439
|
+
if (neg >= parent->dim[ai]) {
|
|
2440
|
+
diag_len = 0;
|
|
2441
|
+
}
|
|
2442
|
+
else {
|
|
2443
|
+
ca_size_t a = parent->dim[ai] - neg;
|
|
2444
|
+
ca_size_t b = parent->dim[aj];
|
|
2445
|
+
diag_len = (a < b) ? a : b;
|
|
2446
|
+
}
|
|
2447
|
+
base_offset = neg * parent_byte_stride[ai];
|
|
2448
|
+
}
|
|
2449
|
+
|
|
2450
|
+
/* build output: kept axes (in original order), then diagonal axis */
|
|
2451
|
+
out_k = 0;
|
|
2452
|
+
for (i = 0; i < ndim; i++) {
|
|
2453
|
+
if (i == (int8_t) ai || i == (int8_t) aj) continue;
|
|
2454
|
+
outdim[out_k] = parent->dim[i];
|
|
2455
|
+
outstrides[out_k] = parent_byte_stride[i];
|
|
2456
|
+
out_k++;
|
|
2457
|
+
}
|
|
2458
|
+
outdim[out_k] = diag_len;
|
|
2459
|
+
outstrides[out_k] = parent_byte_stride[ai] + parent_byte_stride[aj];
|
|
2460
|
+
out_k++;
|
|
2461
|
+
|
|
2462
|
+
{
|
|
2463
|
+
VALUE obj = rb_ca_stride_new(self, parent->data_type, parent->bytes,
|
|
2464
|
+
out_k, outdim, outstrides, base_offset);
|
|
2465
|
+
CA_FACE_LIFT_IF_FACE(obj, self, parent);
|
|
2466
|
+
return obj;
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
|
|
2470
|
+
void
|
|
2471
|
+
Init_ca_obj_stride (void)
|
|
2472
|
+
{
|
|
2473
|
+
/* rb_cCAStride and rb_cCAStrideMask are defined upfront in
|
|
2474
|
+
ruby_carray.c, so subclasses (CARepeat, CATranspose, CAFarray)
|
|
2475
|
+
can be defined before this Init runs. */
|
|
2476
|
+
|
|
2477
|
+
CA_OBJ_STRIDE = ca_install_obj_type(rb_cCAStride,
|
|
2478
|
+
&castride_data_type,
|
|
2479
|
+
rb_cCAStrideMask,
|
|
2480
|
+
&castride_mask_data_type,
|
|
2481
|
+
&ca_stride_func, sizeof(ca_stride_func));
|
|
2482
|
+
rb_define_const(rb_cObject, "CA_OBJ_STRIDE", INT2NUM(CA_OBJ_STRIDE));
|
|
2483
|
+
|
|
2484
|
+
rb_define_alloc_func(rb_cCAStride, rb_cs_s_allocate);
|
|
2485
|
+
rb_define_method(rb_cCAStride, "initialize_copy", rb_cs_initialize_copy, 1);
|
|
2486
|
+
|
|
2487
|
+
rb_define_method(rb_cCAStride, "strides", rb_cs_strides, 0);
|
|
2488
|
+
rb_define_method(rb_cCAStride, "byte_offset", rb_cs_byte_offset, 0);
|
|
2489
|
+
|
|
2490
|
+
rb_define_method(rb_cCArray, "as_strided", rb_ca_as_strided, -1);
|
|
2491
|
+
rb_define_method(rb_cCArray, "sliding_windows",
|
|
2492
|
+
rb_ca_sliding_windows, -1);
|
|
2493
|
+
rb_define_method(rb_cCArray, "unfold", rb_ca_unfold, -1);
|
|
2494
|
+
rb_define_method(rb_cCArray, "block_view", rb_ca_block_view, -1);
|
|
2495
|
+
rb_define_method(rb_cCArray, "dim_view", rb_ca_dim_view, -1);
|
|
2496
|
+
rb_define_method(rb_cCArray, "flip", rb_ca_flip, -1);
|
|
2497
|
+
/* `reverse` = `flip` no-arg form (= all-axis reversed view). Direct CAStride
|
|
2498
|
+
construction with negative strides, no indexer / attach detour. */
|
|
2499
|
+
rb_define_alias(rb_cCArray, "reverse", "flip");
|
|
2500
|
+
rb_define_method(rb_cCArray, "diagonal", rb_ca_diagonal, -1);
|
|
2501
|
+
}
|