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/carray_slab.c
ADDED
|
@@ -0,0 +1,1219 @@
|
|
|
1
|
+
/* ---------------------------------------------------------------------------
|
|
2
|
+
|
|
3
|
+
Slab-iterator engine.
|
|
4
|
+
|
|
5
|
+
Public surface (all on rb_cCArray, defined in Init_carray_slab):
|
|
6
|
+
|
|
7
|
+
CArray#map_slab (axis:, data_type: nil) { |slab| ... } -> CArray
|
|
8
|
+
CArray#reduce_slab (axis:, init: <opt>, data_type: nil) { |...| ... } -> CArray
|
|
9
|
+
CArray#each_slab (axis:) { |slab| ... } -> self
|
|
10
|
+
CArray#each_slab (axis:) -> Enumerator
|
|
11
|
+
|
|
12
|
+
axis: accepts Integer / Array<Integer> / nil (= full-view slab = all axes).
|
|
13
|
+
|
|
14
|
+
Internals:
|
|
15
|
+
- ca_slab_iter_state_t (see carray_slab.h) is the per-call state struct.
|
|
16
|
+
- Each entry point stack-allocates a zeroed state, populates it via
|
|
17
|
+
slab_state_init (= nofail; only validation + scalar field fills),
|
|
18
|
+
then rb_ensure-runs slab_state_run_body (= acquires T1 substrate +
|
|
19
|
+
scratch + output, runs per-form loop) and slab_state_finish (= frees
|
|
20
|
+
T1 state + scratch + nils the VALUE handles).
|
|
21
|
+
- All heap acquisition lives in run_body so init-time rb_raise paths
|
|
22
|
+
cannot leak; rb_ensure guarantees finish on both normal and
|
|
23
|
+
exception exit.
|
|
24
|
+
- Conservative stack scan keeps the state struct's VALUE fields alive
|
|
25
|
+
across rb_yield; no dmark callback is required because the state is
|
|
26
|
+
not Ruby-wrapped.
|
|
27
|
+
|
|
28
|
+
--------------------------------------------------------------------------- */
|
|
29
|
+
|
|
30
|
+
#include "carray.h"
|
|
31
|
+
#include "carray_slab.h"
|
|
32
|
+
|
|
33
|
+
/* Forward declarations: per-form loop bodies are defined later in this
|
|
34
|
+
file and take a state pointer directly. */
|
|
35
|
+
static VALUE ca_slab_run_map (ca_slab_iter_state_t *st);
|
|
36
|
+
static VALUE ca_slab_run_reduce_slab (ca_slab_iter_state_t *st);
|
|
37
|
+
static VALUE ca_slab_run_reduce_fiber (ca_slab_iter_state_t *st);
|
|
38
|
+
static VALUE ca_slab_run_each (ca_slab_iter_state_t *st);
|
|
39
|
+
|
|
40
|
+
/* ------------------------------------------------------------------- */
|
|
41
|
+
/* axis parsing helper */
|
|
42
|
+
/* ------------------------------------------------------------------- */
|
|
43
|
+
|
|
44
|
+
/* Parse `axis_arg` (Integer / Array<Integer> / nil) into slab_axes[] /
|
|
45
|
+
slab_ndim on `st`. Negative indices are normalised against src_ndim.
|
|
46
|
+
Raises ArgumentError on out-of-range or duplicate axes. */
|
|
47
|
+
static void
|
|
48
|
+
ca_slab_parse_axes (ca_slab_iter_state_t *st, VALUE axis_arg, int8_t src_ndim)
|
|
49
|
+
{
|
|
50
|
+
int i, j, n;
|
|
51
|
+
VALUE arr;
|
|
52
|
+
|
|
53
|
+
if ( NIL_P(axis_arg) ) {
|
|
54
|
+
/* axis: nil = full-view slab (= all axes are slab axes) */
|
|
55
|
+
st->slab_ndim = src_ndim;
|
|
56
|
+
for ( i = 0; i < src_ndim; i++ ) st->slab_axes[i] = (int8_t) i;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if ( TYPE(axis_arg) == T_ARRAY ) {
|
|
61
|
+
arr = axis_arg;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
arr = rb_ary_new3(1, axis_arg);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
n = (int) RARRAY_LEN(arr);
|
|
68
|
+
if ( n < 1 || n > CA_RANK_MAX ) {
|
|
69
|
+
rb_raise(rb_eArgError,
|
|
70
|
+
"CArray::SlabIterator: axis count %d out of range [1..%d]",
|
|
71
|
+
n, CA_RANK_MAX);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
for ( i = 0; i < n; i++ ) {
|
|
75
|
+
int k = NUM2INT(rb_ary_entry(arr, i));
|
|
76
|
+
if ( k < 0 ) k += src_ndim;
|
|
77
|
+
if ( k < 0 || k >= src_ndim ) {
|
|
78
|
+
rb_raise(rb_eArgError,
|
|
79
|
+
"CArray::SlabIterator: axis %d out of range for ndim %d",
|
|
80
|
+
NUM2INT(rb_ary_entry(arr, i)), src_ndim);
|
|
81
|
+
}
|
|
82
|
+
for ( j = 0; j < i; j++ ) {
|
|
83
|
+
if ( st->slab_axes[j] == (int8_t) k ) {
|
|
84
|
+
rb_raise(rb_eArgError,
|
|
85
|
+
"CArray::SlabIterator: duplicate axis %d", k);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
st->slab_axes[i] = (int8_t) k;
|
|
89
|
+
}
|
|
90
|
+
st->slab_ndim = (int8_t) n;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* ------------------------------------------------------------------- */
|
|
94
|
+
/* slab_state_init -- nofail population of a stack-allocated state. */
|
|
95
|
+
/* ------------------------------------------------------------------- */
|
|
96
|
+
|
|
97
|
+
/* Populate `st` (= caller's stack-allocated, zero-initialised state) with
|
|
98
|
+
form / source / axes / data_type / init_val. Validation that may
|
|
99
|
+
`rb_raise` (= axis range, duplicate axis, bad data_type symbol) runs
|
|
100
|
+
here, BEFORE any heap acquisition — so an init-time raise leaks
|
|
101
|
+
nothing. All xmalloc / rb_carray_new / ca_iter_state_init_l2 calls
|
|
102
|
+
live downstream in slab_state_run_body / ca_slab_run_*, where
|
|
103
|
+
rb_ensure protects them via slab_state_finish.
|
|
104
|
+
|
|
105
|
+
form : CA_SLAB_FORM_{MAP, REDUCE_FIBER, REDUCE_SLAB, EACH}
|
|
106
|
+
source : VALUE pointing to the source CArray (or subclass)
|
|
107
|
+
axis_arg : VALUE — Integer / Array / nil
|
|
108
|
+
data_type : VALUE — Symbol / Integer / nil (= source.data_type).
|
|
109
|
+
Ignored when form == EACH.
|
|
110
|
+
init_val : VALUE — REDUCE_FIBER initial accumulator, or Qundef
|
|
111
|
+
if not applicable. Stored as Qnil if Qundef. */
|
|
112
|
+
static void
|
|
113
|
+
slab_state_init (ca_slab_iter_state_t *st, VALUE source, int8_t form,
|
|
114
|
+
VALUE axis_arg, VALUE data_type, VALUE init_val)
|
|
115
|
+
{
|
|
116
|
+
CArray *src;
|
|
117
|
+
|
|
118
|
+
/* Caller stack-allocates with `{0}` which sets VALUE fields to Qfalse
|
|
119
|
+
(= 0). Body code uses `== Qnil` to distinguish unset slots from
|
|
120
|
+
populated CArray handles, so we must promote them to Qnil here. */
|
|
121
|
+
st->self = Qnil;
|
|
122
|
+
st->slab_view = Qnil;
|
|
123
|
+
st->carrier = Qnil;
|
|
124
|
+
st->mask_carrier = Qnil;
|
|
125
|
+
st->output = Qnil;
|
|
126
|
+
st->output_slab_view = Qnil;
|
|
127
|
+
st->init_val = Qnil;
|
|
128
|
+
|
|
129
|
+
rb_check_carray_object(source);
|
|
130
|
+
TypedData_Get_Struct(source, CArray, &carray_data_type, src);
|
|
131
|
+
|
|
132
|
+
st->form = form;
|
|
133
|
+
st->self = source;
|
|
134
|
+
|
|
135
|
+
ca_slab_parse_axes(st, axis_arg, src->ndim);
|
|
136
|
+
|
|
137
|
+
if ( form == CA_SLAB_FORM_MAP ||
|
|
138
|
+
form == CA_SLAB_FORM_REDUCE_FIBER ||
|
|
139
|
+
form == CA_SLAB_FORM_REDUCE_SLAB ) {
|
|
140
|
+
if ( NIL_P(data_type) ) {
|
|
141
|
+
st->out_data_type = src->data_type;
|
|
142
|
+
st->out_bytes = src->bytes;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
int8_t dt;
|
|
146
|
+
ca_size_t bytes;
|
|
147
|
+
rb_ca_guess_type_and_bytes(data_type, Qnil, &dt, &bytes);
|
|
148
|
+
st->out_data_type = dt;
|
|
149
|
+
st->out_bytes = bytes;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if ( form == CA_SLAB_FORM_REDUCE_FIBER ) {
|
|
153
|
+
st->init_val = (init_val == Qundef) ? Qnil : init_val;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/* ------------------------------------------------------------------- */
|
|
159
|
+
/* MAP form loop */
|
|
160
|
+
/* ------------------------------------------------------------------- */
|
|
161
|
+
|
|
162
|
+
/* Slab-view modes:
|
|
163
|
+
|
|
164
|
+
ALIAS mode : slab_view.parent = src, base_offset mutates per iter
|
|
165
|
+
(+ ptr mutates for fast path)
|
|
166
|
+
SCRATCH mode: slab_view.parent = carrier, base_offset = 0 (constant)
|
|
167
|
+
(carrier = internal CAWrap borrowing T1's scratch buffer;
|
|
168
|
+
per-iter ptr mutation tracks T1's possibly-realloc'd scratch)
|
|
169
|
+
|
|
170
|
+
Both modes use CAStride (not a bare CAWrap) so that:
|
|
171
|
+
- ca_stride_func_xfer_index's ptr fast path hits when ptr is
|
|
172
|
+
non-NULL -- no compose-fold work per cell read.
|
|
173
|
+
- ca_stride_func_clone preserves (parent, base_offset, strides), so
|
|
174
|
+
`row.dup` and other derived views see the CURRENT iter's data:
|
|
175
|
+
* ALIAS : clone reads src memory directly (base_offset is
|
|
176
|
+
just-mutated, strides are src-native = correct)
|
|
177
|
+
* SCRATCH: clone reads carrier (= a CAWrap borrowing T1 scratch),
|
|
178
|
+
base_offset=0, strides=contig — also correct because
|
|
179
|
+
carrier->ptr was set to the iter's scratch addr.
|
|
180
|
+
- a bare CAWrap-as-slab would be correct in-block but lose the
|
|
181
|
+
parent-slot GC pin in escape scenarios.
|
|
182
|
+
|
|
183
|
+
CAREFUL: in ALIAS mode mutate both ptr AND base_offset per iter.
|
|
184
|
+
The ptr fast path alone would suffice for xfer_index, but
|
|
185
|
+
ca_stride_func_clone bypasses ptr and reads (parent, base_offset,
|
|
186
|
+
strides) directly -- a stale base_offset there yields wrong data for
|
|
187
|
+
`slab.dup` etc. */
|
|
188
|
+
|
|
189
|
+
/* ------------------------------------------------------------------- */
|
|
190
|
+
/* K-D gather/scatter helpers for non-contig multi-axis */
|
|
191
|
+
/* ------------------------------------------------------------------- */
|
|
192
|
+
|
|
193
|
+
/* Test whether `strides` describe a row-major contig layout over `dims`
|
|
194
|
+
in `bytes`-element units. innermost stride must be `bytes` and each
|
|
195
|
+
outer stride must be `bytes * Π later_dims`. Single-axis collapses
|
|
196
|
+
to `strides[0] == bytes`. */
|
|
197
|
+
static int
|
|
198
|
+
ca_slab_strides_are_row_major_contig (int8_t ndim, ca_size_t *dims,
|
|
199
|
+
ca_size_t *strides, ca_size_t bytes)
|
|
200
|
+
{
|
|
201
|
+
ca_size_t expected = bytes;
|
|
202
|
+
int8_t k;
|
|
203
|
+
for ( k = ndim - 1; k >= 0; k-- ) {
|
|
204
|
+
if ( strides[k] != expected ) return 0;
|
|
205
|
+
expected *= dims[k];
|
|
206
|
+
}
|
|
207
|
+
return 1;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* K-D gather: row-major walk over (ndim, dims), copy each cell from
|
|
211
|
+
src_base + Σ idx[k] * src_strides[k] into dst + i * bytes. dst is
|
|
212
|
+
a contig buffer of size (Π dims) * bytes. */
|
|
213
|
+
static void
|
|
214
|
+
ca_slab_gather_k_d (char *dst, char *src_base,
|
|
215
|
+
int8_t ndim, ca_size_t *dims,
|
|
216
|
+
ca_size_t *src_strides, ca_size_t bytes)
|
|
217
|
+
{
|
|
218
|
+
ca_size_t kidx[CA_RANK_MAX] = { 0 };
|
|
219
|
+
ca_size_t total = 1;
|
|
220
|
+
ca_size_t i;
|
|
221
|
+
int8_t k;
|
|
222
|
+
for ( k = 0; k < ndim; k++ ) total *= dims[k];
|
|
223
|
+
for ( i = 0; i < total; i++ ) {
|
|
224
|
+
ca_size_t off = 0;
|
|
225
|
+
for ( k = 0; k < ndim; k++ ) off += kidx[k] * src_strides[k];
|
|
226
|
+
memcpy(dst + i * bytes, src_base + off, bytes);
|
|
227
|
+
for ( k = ndim - 1; k >= 0; k-- ) {
|
|
228
|
+
if ( ++kidx[k] < dims[k] ) break;
|
|
229
|
+
kidx[k] = 0;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/* K-D scatter: row-major walk from contig src to dst_base via dst_strides. */
|
|
235
|
+
static void
|
|
236
|
+
ca_slab_scatter_k_d (char *dst_base, char *src,
|
|
237
|
+
int8_t ndim, ca_size_t *dims,
|
|
238
|
+
ca_size_t *dst_strides, ca_size_t bytes)
|
|
239
|
+
{
|
|
240
|
+
ca_size_t kidx[CA_RANK_MAX] = { 0 };
|
|
241
|
+
ca_size_t total = 1;
|
|
242
|
+
ca_size_t i;
|
|
243
|
+
int8_t k;
|
|
244
|
+
for ( k = 0; k < ndim; k++ ) total *= dims[k];
|
|
245
|
+
for ( i = 0; i < total; i++ ) {
|
|
246
|
+
ca_size_t off = 0;
|
|
247
|
+
for ( k = 0; k < ndim; k++ ) off += kidx[k] * dst_strides[k];
|
|
248
|
+
memcpy(dst_base + off, src + i * bytes, bytes);
|
|
249
|
+
for ( k = ndim - 1; k >= 0; k-- ) {
|
|
250
|
+
if ( ++kidx[k] < dims[k] ) break;
|
|
251
|
+
kidx[k] = 0;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/* Build a CAStride wrapping `parent_value` (= a CArray VALUE that the
|
|
257
|
+
caller has already created and GC-pinned). ndim = 1, dim = [fiber_len],
|
|
258
|
+
strides = [stride]. base_offset = 0; the caller mutates it per iter
|
|
259
|
+
in ALIAS mode. ptr is NULL initially; caller mutates it per iter to
|
|
260
|
+
T1's contig delivery. attach = 1 forces user-side ca_attach into
|
|
261
|
+
the increment-only path. */
|
|
262
|
+
static VALUE
|
|
263
|
+
ca_slab_build_slab_view (VALUE parent_value, int8_t data_type,
|
|
264
|
+
ca_size_t bytes, int8_t ndim,
|
|
265
|
+
ca_size_t *dim, ca_size_t *strides)
|
|
266
|
+
{
|
|
267
|
+
CArray *parent;
|
|
268
|
+
CAStride *cs;
|
|
269
|
+
VALUE obj;
|
|
270
|
+
TypedData_Get_Struct(parent_value, CArray, &carray_data_type, parent);
|
|
271
|
+
cs = ca_stride_new(CA_OBJ_STRIDE, parent, data_type, bytes,
|
|
272
|
+
ndim, dim, strides, 0);
|
|
273
|
+
cs->attach = 1;
|
|
274
|
+
cs->ptr = NULL;
|
|
275
|
+
/* Sync mask's view attach counter to match the data side. Without
|
|
276
|
+
this, a user `slab.to_a` (= ca_attach + ca_detach pair on slab_view)
|
|
277
|
+
stays net-zero on the data side (attach 1->2->1) but over-detaches
|
|
278
|
+
on the mask side (attach 0->1->0 invokes the mask's func.detach,
|
|
279
|
+
NULLing mask->ptr that T1 owns and decrementing root mask attach
|
|
280
|
+
unilaterally). */
|
|
281
|
+
if ( cs->mask != NULL && ca_is_view(cs->mask) ) {
|
|
282
|
+
CAVIEW(cs->mask)->attach = 1;
|
|
283
|
+
}
|
|
284
|
+
obj = ca_wrap_struct(cs);
|
|
285
|
+
rb_ca_set_parent(obj, parent_value);
|
|
286
|
+
return obj;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/* Build an internal CAWrap that will borrow T1's scratch buffer in
|
|
290
|
+
SCRATCH mode. This carrier is the parent slot of slab_view, so
|
|
291
|
+
GC-pinning slab_view (via iter state ivar) keeps the carrier alive
|
|
292
|
+
for the entire walk. Carrier never escapes to user surface. T1 owns
|
|
293
|
+
the scratch; the slab view borrows it. free_ca_wrap leaves ptr
|
|
294
|
+
untouched, so dropping the carrier ivar at finish does NOT xfree T1's
|
|
295
|
+
scratch.
|
|
296
|
+
|
|
297
|
+
When `has_mask` is non-zero, the carrier is paired with an internal
|
|
298
|
+
boolean8_t CAWrap mask whose `ptr` will track T1's fiber_mask_scratch
|
|
299
|
+
per iter. The mask is wired into `carrier->mask` directly (= bypass
|
|
300
|
+
ca_setup_mask, which would try to sync data from a NULL ptr). The
|
|
301
|
+
carrier owns the mask via free_ca_wrap → ca_free(carrier->mask), so
|
|
302
|
+
we DO NOT also keep the mask as a separate Ruby VALUE (= avoid double
|
|
303
|
+
free via two GC chains). */
|
|
304
|
+
static VALUE
|
|
305
|
+
ca_slab_build_carrier (int8_t data_type, ca_size_t bytes,
|
|
306
|
+
ca_size_t fiber_len, int has_mask)
|
|
307
|
+
{
|
|
308
|
+
ca_size_t dim[1];
|
|
309
|
+
CAWrap *cw;
|
|
310
|
+
dim[0] = fiber_len;
|
|
311
|
+
cw = ca_wrap_new_null(data_type, 1, dim, bytes, NULL);
|
|
312
|
+
if ( has_mask ) {
|
|
313
|
+
/* Build a mask sibling. Direct field assignment bypasses
|
|
314
|
+
ca_setup_mask (= which calls ca_sync_data on a NULL ptr and
|
|
315
|
+
crashes). mask_carrier is owned by carrier via free_ca_wrap chain;
|
|
316
|
+
no separate Ruby pinning needed. */
|
|
317
|
+
CAWrap *mask_cw = ca_wrap_new_null(CA_BOOLEAN, 1, dim, 1, NULL);
|
|
318
|
+
cw->mask = (CArray *) mask_cw;
|
|
319
|
+
ca_set_flag(mask_cw, CA_FLAG_MASK_ARRAY);
|
|
320
|
+
}
|
|
321
|
+
return TypedData_Wrap_Struct(rb_cCAWrap, &cawrap_data_type, cw);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/* Build the OUTPUT slab view as a simple CAWrap with mutable ptr.
|
|
325
|
+
Output is internal — user never derives a view from it — so the
|
|
326
|
+
parent-slot GC pin / clone correctness arguments don't apply.
|
|
327
|
+
|
|
328
|
+
ndim/dim accept K-D shape so the OUTPUT slab matches the input slab
|
|
329
|
+
for multi-axis map_slab (= the block returns a K-D CArray of the same
|
|
330
|
+
shape as the input slab; ca_xfer_all + p_out works as a contig memcpy
|
|
331
|
+
when the output slab is row-major contig in output memory, which is
|
|
332
|
+
the case for innermost-K axes on a freshly-allocated entity). */
|
|
333
|
+
static VALUE
|
|
334
|
+
ca_slab_build_output_view (int8_t data_type, ca_size_t bytes,
|
|
335
|
+
int8_t ndim, ca_size_t *dim)
|
|
336
|
+
{
|
|
337
|
+
CAWrap *cw;
|
|
338
|
+
cw = ca_wrap_new_null(data_type, ndim, dim, bytes, NULL);
|
|
339
|
+
return TypedData_Wrap_Struct(rb_cCAWrap, &cawrap_data_type, cw);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/* Common setup of the input slab view for all 4 forms. Decides ALIAS
|
|
343
|
+
vs SCRATCH from T1's slab_strides[0] vs src->bytes equality, builds
|
|
344
|
+
the slab_view + (when SCRATCH) carrier in place. The mask travels
|
|
345
|
+
alongside data automatically (= mask has the same shape as the value
|
|
346
|
+
array, so the same ALIAS / SCRATCH dispatch is used; ca_stride_setup
|
|
347
|
+
auto-creates slab_view->mask over parent->mask in both modes).
|
|
348
|
+
Returns 1 if ALIAS mode, 0 if SCRATCH. */
|
|
349
|
+
static int
|
|
350
|
+
ca_slab_setup_input_slab_view (ca_slab_iter_state_t *st, CArray *src,
|
|
351
|
+
ca_size_t fiber_len)
|
|
352
|
+
{
|
|
353
|
+
int8_t slab_ndim = st->t1->slab_ndim;
|
|
354
|
+
int is_multi = (slab_ndim > 1);
|
|
355
|
+
int has_mask = ca_has_mask(src);
|
|
356
|
+
int in_alias;
|
|
357
|
+
|
|
358
|
+
if ( is_multi ) {
|
|
359
|
+
/* Multi-axis slab.
|
|
360
|
+
|
|
361
|
+
Two paths:
|
|
362
|
+
(a) contig case (= slab_strides row-major over slab_dims): slab
|
|
363
|
+
is naturally contig in src memory, enable ptr fast path with
|
|
364
|
+
parent=src, base_offset mutated per iter.
|
|
365
|
+
(b) non-contig case (= e.g., axis: [0, 2] on a 3-D row-major
|
|
366
|
+
source): build OUR OWN gather scratch carrier (= CAWrap whose
|
|
367
|
+
ptr is an xmalloc'd buffer we own), gather K-D per iter from
|
|
368
|
+
p_in via slab_strides into the scratch, present slab_view as
|
|
369
|
+
a CAStride over the carrier with contig strides.
|
|
370
|
+
|
|
371
|
+
Both paths require ALIAS T1 mode (= p_in is in src memory).
|
|
372
|
+
Non-ALIAS multi-axis T1 modes (= PER_SLAB scratch where T1 picks
|
|
373
|
+
its own gather for descriptor-framework sources) remain
|
|
374
|
+
NotImpError until a follow-on substrate phase. */
|
|
375
|
+
uint8_t am = st->t1->alias_mode;
|
|
376
|
+
int contig;
|
|
377
|
+
if ( am != CA_ITER_ALIAS_CONTIG &&
|
|
378
|
+
am != CA_ITER_ALIAS_STRIDED &&
|
|
379
|
+
am != CA_ITER_ALIAS_ATTACH ) {
|
|
380
|
+
rb_raise(rb_eNotImpError,
|
|
381
|
+
"multi-axis slab with non-ALIAS T1 mode not implemented yet "
|
|
382
|
+
"(alias_mode = %d)", (int) am);
|
|
383
|
+
}
|
|
384
|
+
contig = ca_slab_strides_are_row_major_contig(slab_ndim,
|
|
385
|
+
st->t1->slab_dims,
|
|
386
|
+
st->t1->slab_strides,
|
|
387
|
+
src->bytes);
|
|
388
|
+
if ( contig ) {
|
|
389
|
+
/* (a) contig multi-axis ALIAS: parent=src, K-D strides match src. */
|
|
390
|
+
st->slab_view = ca_slab_build_slab_view(st->self, src->data_type,
|
|
391
|
+
src->bytes, slab_ndim,
|
|
392
|
+
st->t1->slab_dims,
|
|
393
|
+
st->t1->slab_strides);
|
|
394
|
+
in_alias = 1;
|
|
395
|
+
} else {
|
|
396
|
+
/* (b) non-contig multi-axis: alloc own scratch + carrier; build
|
|
397
|
+
slab_view atop carrier with row-major contig strides. Per-iter
|
|
398
|
+
gather lives in ca_slab_advance_slab_view (= K-D walk). */
|
|
399
|
+
ca_size_t elements = st->t1->slab_elements;
|
|
400
|
+
ca_size_t contig_strides[CA_RANK_MAX];
|
|
401
|
+
ca_size_t exp = src->bytes;
|
|
402
|
+
int8_t k;
|
|
403
|
+
st->own_scratch_elements = elements;
|
|
404
|
+
st->own_data_scratch = (char *) xmalloc(elements * src->bytes);
|
|
405
|
+
if ( has_mask ) {
|
|
406
|
+
st->own_mask_scratch = (boolean8_t *) xmalloc(elements);
|
|
407
|
+
}
|
|
408
|
+
/* Carrier wraps our own scratch. Direct field assignment of
|
|
409
|
+
carrier->ptr (= bypasses ca_setup_mask which would crash on
|
|
410
|
+
NULL mask ptr). Mask sibling is similarly wired post-build. */
|
|
411
|
+
{
|
|
412
|
+
CAWrap *cw = ca_wrap_new_null(src->data_type, slab_ndim,
|
|
413
|
+
st->t1->slab_dims, src->bytes, NULL);
|
|
414
|
+
cw->ptr = st->own_data_scratch;
|
|
415
|
+
if ( has_mask ) {
|
|
416
|
+
CAWrap *mcw = ca_wrap_new_null(CA_BOOLEAN, slab_ndim,
|
|
417
|
+
st->t1->slab_dims, 1, NULL);
|
|
418
|
+
mcw->ptr = (char *) st->own_mask_scratch;
|
|
419
|
+
cw->mask = (CArray *) mcw;
|
|
420
|
+
ca_set_flag(mcw, CA_FLAG_MASK_ARRAY);
|
|
421
|
+
}
|
|
422
|
+
st->carrier = TypedData_Wrap_Struct(rb_cCAWrap, &cawrap_data_type, cw);
|
|
423
|
+
}
|
|
424
|
+
/* Build slab_view's strides as row-major contig over slab_dims. */
|
|
425
|
+
for ( k = slab_ndim - 1; k >= 0; k-- ) {
|
|
426
|
+
contig_strides[k] = exp;
|
|
427
|
+
exp *= st->t1->slab_dims[k];
|
|
428
|
+
}
|
|
429
|
+
st->slab_view = ca_slab_build_slab_view(st->carrier, src->data_type,
|
|
430
|
+
src->bytes, slab_ndim,
|
|
431
|
+
st->t1->slab_dims,
|
|
432
|
+
contig_strides);
|
|
433
|
+
in_alias = 0;
|
|
434
|
+
}
|
|
435
|
+
(void) fiber_len; /* unused for multi-axis */
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
ca_size_t in_stride = st->t1->slab_strides[0];
|
|
439
|
+
ca_size_t dim[1];
|
|
440
|
+
ca_size_t strides[1];
|
|
441
|
+
dim[0] = fiber_len;
|
|
442
|
+
strides[0] = src->bytes;
|
|
443
|
+
in_alias = (in_stride == src->bytes);
|
|
444
|
+
|
|
445
|
+
if ( in_alias ) {
|
|
446
|
+
/* ALIAS: parent = src. ca_stride_setup auto-creates slab_view->mask
|
|
447
|
+
as a CAStride over src->mask when src has mask. Per-iter mutates
|
|
448
|
+
(ptr, base_offset) on both data and mask sides. */
|
|
449
|
+
st->slab_view = ca_slab_build_slab_view(st->self, src->data_type,
|
|
450
|
+
src->bytes, 1, dim, strides);
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
/* SCRATCH: carrier wraps T1's scratch + (when src has mask) wires
|
|
454
|
+
an internal mask sibling. slab_view is then auto-created with a
|
|
455
|
+
mask CAStride over carrier->mask = mask sibling. */
|
|
456
|
+
st->carrier = ca_slab_build_carrier(src->data_type, src->bytes,
|
|
457
|
+
fiber_len, has_mask);
|
|
458
|
+
st->slab_view = ca_slab_build_slab_view(st->carrier, src->data_type,
|
|
459
|
+
src->bytes, 1, dim, strides);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return in_alias;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/* Per-iter update of the slab_view + mask pointers. Called from inside
|
|
467
|
+
the form-specific loop after ca_iter_state_next_slab_axes returns the
|
|
468
|
+
data ptr `p_in` and mask ptr `m_in` (= NULL for unmasked sources).
|
|
469
|
+
For SCRATCH mode the data carrier's ptr is also tracked so the clone
|
|
470
|
+
path (= compose-fold via carrier) reads the current iter buffer. */
|
|
471
|
+
static void
|
|
472
|
+
ca_slab_advance_slab_view (ca_slab_iter_state_t *st,
|
|
473
|
+
CAStride *slab_cs, CAWrap *carrier_cw,
|
|
474
|
+
CArray *src, char *p_in, boolean8_t *m_in,
|
|
475
|
+
int in_alias)
|
|
476
|
+
{
|
|
477
|
+
if ( in_alias ) {
|
|
478
|
+
/* ALIAS — single-axis OR multi-axis contig. ptr fast path uses
|
|
479
|
+
ca_index2addr * bytes + ptr (= safe since slab is row-major
|
|
480
|
+
contig in src memory). base_offset is also updated so clone /
|
|
481
|
+
compose-fold derived views read the same cells via src memory. */
|
|
482
|
+
slab_cs->ptr = p_in;
|
|
483
|
+
slab_cs->base_offset = (ca_size_t) (p_in - src->ptr);
|
|
484
|
+
if ( slab_cs->mask != NULL ) {
|
|
485
|
+
CAStride *mask_cs = (CAStride *) slab_cs->mask;
|
|
486
|
+
mask_cs->ptr = (char *) m_in;
|
|
487
|
+
mask_cs->base_offset = (ca_size_t) ((char *) m_in - src->mask->ptr);
|
|
488
|
+
}
|
|
489
|
+
} else if ( st->own_data_scratch != NULL ) {
|
|
490
|
+
/* Non-contig multi-axis: K-D gather from src memory (via
|
|
491
|
+
p_in + slab_strides) into our own contig scratch. carrier_cw->
|
|
492
|
+
ptr is already pointed at the scratch from setup, no mutation
|
|
493
|
+
needed. slab_view shares carrier's ptr (= contig). */
|
|
494
|
+
ca_slab_gather_k_d(st->own_data_scratch, p_in,
|
|
495
|
+
st->t1->slab_ndim, st->t1->slab_dims,
|
|
496
|
+
st->t1->slab_strides, src->bytes);
|
|
497
|
+
if ( st->own_mask_scratch != NULL ) {
|
|
498
|
+
ca_slab_gather_k_d((char *) st->own_mask_scratch, (char *) m_in,
|
|
499
|
+
st->t1->slab_ndim, st->t1->slab_dims,
|
|
500
|
+
st->t1->slab_mask_strides, 1);
|
|
501
|
+
}
|
|
502
|
+
} else {
|
|
503
|
+
/* SCRATCH (single-axis): T1 owns the contig scratch; we just point
|
|
504
|
+
both slab_view and carrier at the iter's delivered addr. */
|
|
505
|
+
slab_cs->ptr = p_in;
|
|
506
|
+
carrier_cw->ptr = p_in;
|
|
507
|
+
if ( slab_cs->mask != NULL ) {
|
|
508
|
+
CAStride *mask_cs = (CAStride *) slab_cs->mask;
|
|
509
|
+
mask_cs->ptr = (char *) m_in;
|
|
510
|
+
carrier_cw->mask->ptr = (char *) m_in;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/* Convert a Ruby Numeric scalar into bytes of slab_view's data_type and
|
|
516
|
+
fill the entire slab buffer at out_ptr. Used when the user block
|
|
517
|
+
returns a scalar instead of a CArray (= broadcast over the slab).
|
|
518
|
+
`slab_elements` = Π slab_dims[] (= total cells, multi-axis K-D). */
|
|
519
|
+
static void
|
|
520
|
+
ca_slab_fill_scalar (VALUE source, char *out_ptr, ca_size_t slab_elements,
|
|
521
|
+
int8_t out_data_type, ca_size_t out_bytes, VALUE val)
|
|
522
|
+
{
|
|
523
|
+
char buf[64];
|
|
524
|
+
char *scratch = (out_bytes <= (ca_size_t) sizeof(buf)) ? buf
|
|
525
|
+
: xmalloc(out_bytes);
|
|
526
|
+
ca_size_t i;
|
|
527
|
+
/* Use source's data_type-aware conversion (rb_ca_obj2ptr looks at the
|
|
528
|
+
receiver's data_type). Build a stand-in scalar buffer via the
|
|
529
|
+
source CArray when out_data_type matches source data_type; otherwise we
|
|
530
|
+
need a CArray of the output data_type to drive the conversion. We
|
|
531
|
+
accept the source-data_type default plus an explicit data_type kwarg
|
|
532
|
+
that may differ — in either case the easiest path is to pull
|
|
533
|
+
conversion through the actual output entity (= we already have
|
|
534
|
+
`source`-keyed obj2ptr in carray_cast.c). Here we construct an
|
|
535
|
+
ephemeral CScalar of the output data_type to anchor the cast. */
|
|
536
|
+
VALUE cs_anchor =
|
|
537
|
+
rb_cscalar_new_with_value((int) out_data_type, (int) out_bytes, val);
|
|
538
|
+
CScalar *anchor_ca;
|
|
539
|
+
(void) source;
|
|
540
|
+
TypedData_Get_Struct(cs_anchor, CScalar, &cscalar_data_type, anchor_ca);
|
|
541
|
+
ca_attach(anchor_ca);
|
|
542
|
+
memcpy(scratch, anchor_ca->ptr, out_bytes);
|
|
543
|
+
ca_detach(anchor_ca);
|
|
544
|
+
for ( i = 0; i < slab_elements; i++ ) {
|
|
545
|
+
memcpy(out_ptr + i * out_bytes, scratch, out_bytes);
|
|
546
|
+
}
|
|
547
|
+
if ( scratch != buf ) xfree(scratch);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
static VALUE
|
|
551
|
+
ca_slab_run_map (ca_slab_iter_state_t *st)
|
|
552
|
+
{
|
|
553
|
+
CArray *src;
|
|
554
|
+
CArray *out;
|
|
555
|
+
uint32_t flags_in;
|
|
556
|
+
uint32_t flags_out;
|
|
557
|
+
ca_size_t slab_elements;
|
|
558
|
+
int rc;
|
|
559
|
+
int is_multi = (st->slab_ndim > 1);
|
|
560
|
+
|
|
561
|
+
TypedData_Get_Struct(st->self, CArray, &carray_data_type, src);
|
|
562
|
+
|
|
563
|
+
/* Mask transparent carry: input mask is exposed via slab_view
|
|
564
|
+
(= user's block can see slab.mask / slab.has_mask?). Output mask
|
|
565
|
+
is intentionally NOT scattered — block return CArray mask info is
|
|
566
|
+
dropped (= simplest contract). */
|
|
567
|
+
|
|
568
|
+
/* Step 1: allocate output entity (same shape as src, data_type from opts). */
|
|
569
|
+
st->output = rb_carray_new(st->out_data_type, src->ndim, src->dim,
|
|
570
|
+
st->out_bytes, NULL);
|
|
571
|
+
TypedData_Get_Struct(st->output, CArray, &carray_data_type, out);
|
|
572
|
+
ca_allocate(out); /* entity ptr usable as both read + write target */
|
|
573
|
+
|
|
574
|
+
/* Step 2: T1 init on both sides. FIBER_CONTIG is naxes==1 only;
|
|
575
|
+
multi-axis omits it. Output side gets CA_KERNEL_WRITE. */
|
|
576
|
+
st->t1 = (ca_iter_state *) xmalloc(sizeof(ca_iter_state));
|
|
577
|
+
st->t1_out = (ca_iter_state *) xmalloc(sizeof(ca_iter_state));
|
|
578
|
+
|
|
579
|
+
flags_in = is_multi ? 0 : CA_KERNEL_FIBER_CONTIG;
|
|
580
|
+
flags_out = (is_multi ? 0 : CA_KERNEL_FIBER_CONTIG) | CA_KERNEL_WRITE;
|
|
581
|
+
|
|
582
|
+
rc = ca_iter_state_init_l2(st->t1, src, CA_SLAB_AXES,
|
|
583
|
+
st->slab_axes, st->slab_ndim, flags_in);
|
|
584
|
+
if ( rc != CA_ITER_OK ) {
|
|
585
|
+
rb_raise(rb_eRuntimeError,
|
|
586
|
+
"CArray#map_slab: T1 init (READ side) failed rc=%d", rc);
|
|
587
|
+
}
|
|
588
|
+
st->t1_started = 1;
|
|
589
|
+
|
|
590
|
+
rc = ca_iter_state_init_l2(st->t1_out, out, CA_SLAB_AXES,
|
|
591
|
+
st->slab_axes, st->slab_ndim, flags_out);
|
|
592
|
+
if ( rc != CA_ITER_OK ) {
|
|
593
|
+
rb_raise(rb_eRuntimeError,
|
|
594
|
+
"CArray#map_slab: T1 init (WRITE side) failed rc=%d", rc);
|
|
595
|
+
}
|
|
596
|
+
st->t1_out_started = 1;
|
|
597
|
+
|
|
598
|
+
slab_elements = st->t1->slab_elements;
|
|
599
|
+
|
|
600
|
+
/* Step 3: input slab view + carrier + mask plumbing (= centralises
|
|
601
|
+
ALIAS / SCRATCH dispatch + mask carry + multi-axis contig +
|
|
602
|
+
non-contig paths via own_data_scratch). */
|
|
603
|
+
ca_slab_setup_input_slab_view(st, src, st->t1->slab_dims[0]);
|
|
604
|
+
|
|
605
|
+
/* Output slab view: K-D CAWrap matching the input slab shape (= same
|
|
606
|
+
slab_dims). For non-contig multi-axis (= input went through own-
|
|
607
|
+
scratch gather), allocate parallel own_out_scratch so we can scatter
|
|
608
|
+
the block's result back to non-contig output memory via the K-D
|
|
609
|
+
scatter helper. For the contig case, output_slab_view wraps p_out
|
|
610
|
+
directly each iter (= existing fast path). */
|
|
611
|
+
st->output_slab_view = ca_slab_build_output_view(out->data_type,
|
|
612
|
+
out->bytes,
|
|
613
|
+
st->t1->slab_ndim,
|
|
614
|
+
st->t1->slab_dims);
|
|
615
|
+
if ( st->own_data_scratch != NULL ) {
|
|
616
|
+
st->own_out_scratch =
|
|
617
|
+
(char *) xmalloc(st->own_scratch_elements * st->out_bytes);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/* Step 4: per-slab loop.
|
|
621
|
+
CAREFUL: never call ca_detach(slab_view) here — the T1 buffer is
|
|
622
|
+
borrowed; the slab_view's ptr would otherwise be mis-identified as
|
|
623
|
+
slab_view-owned at finish/dfree and double-freed. GC reclaims the
|
|
624
|
+
CAStride / CAWrap wrappers via ivar drop in run_ensure (free_ca_stride
|
|
625
|
+
and free_ca_wrap both leave ptr untouched). */
|
|
626
|
+
{
|
|
627
|
+
char *p_in, *p_out;
|
|
628
|
+
boolean8_t *m_in, *m_out;
|
|
629
|
+
CAStride *slab_cs;
|
|
630
|
+
CAWrap *carrier_cw = NULL;
|
|
631
|
+
CAWrap *out_slab_cw;
|
|
632
|
+
int in_alias = (st->carrier == Qnil);
|
|
633
|
+
TypedData_Get_Struct(st->slab_view, CAStride,
|
|
634
|
+
&castride_data_type, slab_cs);
|
|
635
|
+
if ( ! in_alias ) {
|
|
636
|
+
TypedData_Get_Struct(st->carrier, CAWrap,
|
|
637
|
+
&cawrap_data_type, carrier_cw);
|
|
638
|
+
}
|
|
639
|
+
TypedData_Get_Struct(st->output_slab_view, CAWrap,
|
|
640
|
+
&cawrap_data_type, out_slab_cw);
|
|
641
|
+
|
|
642
|
+
while ( ca_iter_state_next_slab_axes(st->t1, &p_in, &m_in) &&
|
|
643
|
+
ca_iter_state_next_slab_axes(st->t1_out, &p_out, &m_out) ) {
|
|
644
|
+
VALUE result;
|
|
645
|
+
|
|
646
|
+
ca_slab_advance_slab_view(st, slab_cs, carrier_cw,
|
|
647
|
+
src, p_in, m_in, in_alias);
|
|
648
|
+
/* Output write target per iter:
|
|
649
|
+
- contig case (= no own_out_scratch): write directly into
|
|
650
|
+
p_out (= output memory at this slab's offset)
|
|
651
|
+
- non-contig case (= own_out_scratch alloc'd): write into our
|
|
652
|
+
contig scratch, then K-D scatter back to p_out via t1_out
|
|
653
|
+
slab_strides */
|
|
654
|
+
{
|
|
655
|
+
char *write_target = (st->own_out_scratch != NULL)
|
|
656
|
+
? st->own_out_scratch : p_out;
|
|
657
|
+
out_slab_cw->ptr = write_target;
|
|
658
|
+
|
|
659
|
+
result = rb_yield(st->slab_view);
|
|
660
|
+
|
|
661
|
+
/* Strict shape check. */
|
|
662
|
+
if ( rb_obj_is_kind_of(result, rb_cCArray) ) {
|
|
663
|
+
CArray *res;
|
|
664
|
+
int shape_ok = 0;
|
|
665
|
+
TypedData_Get_Struct(result, CArray, &carray_data_type, res);
|
|
666
|
+
if ( res->elements == slab_elements ) {
|
|
667
|
+
if ( res->ndim == slab_cs->ndim ) {
|
|
668
|
+
int8_t k;
|
|
669
|
+
shape_ok = 1;
|
|
670
|
+
for ( k = 0; k < res->ndim; k++ ) {
|
|
671
|
+
if ( res->dim[k] != slab_cs->dim[k] ) { shape_ok = 0; break; }
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
else if ( ! is_multi && res->ndim == 1 &&
|
|
675
|
+
res->dim[0] == slab_cs->dim[0] ) {
|
|
676
|
+
shape_ok = 1;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
if ( ! shape_ok ) {
|
|
680
|
+
rb_raise(rb_eArgError,
|
|
681
|
+
"CArray#map_slab: block result shape mismatch "
|
|
682
|
+
"(got ndim=%d, elements=%lld; expected %lld elements "
|
|
683
|
+
"in shape matching the slab, ndim=%d)",
|
|
684
|
+
(int) res->ndim, (long long) res->elements,
|
|
685
|
+
(long long) slab_elements, (int) slab_cs->ndim);
|
|
686
|
+
}
|
|
687
|
+
if ( res->data_type == st->out_data_type && res->bytes == st->out_bytes ) {
|
|
688
|
+
ca_attach(res);
|
|
689
|
+
ca_xfer_all(res, write_target, CA_XFER_GET);
|
|
690
|
+
ca_detach(res);
|
|
691
|
+
}
|
|
692
|
+
else {
|
|
693
|
+
/* Cast-on-scatter via per-cell obj2ptr. Writes to write_target
|
|
694
|
+
(= either p_out direct or own_out_scratch). */
|
|
695
|
+
char buf[64];
|
|
696
|
+
char *scratch = (st->out_bytes <= (ca_size_t) sizeof(buf))
|
|
697
|
+
? buf : xmalloc(st->out_bytes);
|
|
698
|
+
ca_size_t i;
|
|
699
|
+
ca_attach(res);
|
|
700
|
+
for ( i = 0; i < slab_elements; i++ ) {
|
|
701
|
+
VALUE elem;
|
|
702
|
+
ca_size_t idx[CA_RANK_MAX] = { 0 };
|
|
703
|
+
ca_size_t flat = i;
|
|
704
|
+
int8_t k;
|
|
705
|
+
for ( k = res->ndim - 1; k >= 0; k-- ) {
|
|
706
|
+
idx[k] = flat % res->dim[k];
|
|
707
|
+
flat /= res->dim[k];
|
|
708
|
+
}
|
|
709
|
+
elem = rb_ca_fetch_index(result, idx);
|
|
710
|
+
rb_ca_obj2ptr(st->output_slab_view, elem, scratch);
|
|
711
|
+
memcpy(write_target + i * st->out_bytes, scratch, st->out_bytes);
|
|
712
|
+
}
|
|
713
|
+
ca_detach(res);
|
|
714
|
+
if ( scratch != buf ) xfree(scratch);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
else if ( rb_obj_is_kind_of(result, rb_cNumeric) ||
|
|
718
|
+
result == Qtrue || result == Qfalse ||
|
|
719
|
+
result == Qnil || ca_is_object_type(out) ) {
|
|
720
|
+
/* Scalar broadcast fill into write_target. */
|
|
721
|
+
ca_slab_fill_scalar(st->self, write_target, slab_elements,
|
|
722
|
+
st->out_data_type, st->out_bytes, result);
|
|
723
|
+
}
|
|
724
|
+
else {
|
|
725
|
+
rb_raise(rb_eArgError,
|
|
726
|
+
"CArray#map_slab: block must return CArray (same shape) "
|
|
727
|
+
"or Numeric scalar, got %"PRIsVALUE,
|
|
728
|
+
rb_obj_class(result));
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/* Non-contig multi-axis: K-D scatter from own_out_scratch back to
|
|
732
|
+
output memory at p_out via t1_out's slab_strides. */
|
|
733
|
+
if ( st->own_out_scratch != NULL ) {
|
|
734
|
+
ca_slab_scatter_k_d(p_out, st->own_out_scratch,
|
|
735
|
+
st->t1_out->slab_ndim, st->t1_out->slab_dims,
|
|
736
|
+
st->t1_out->slab_strides, st->out_bytes);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
ca_iter_state_sync_slab(st->t1_out);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
return st->output;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/* ------------------------------------------------------------------- */
|
|
748
|
+
/* REDUCE forms */
|
|
749
|
+
/* ------------------------------------------------------------------- */
|
|
750
|
+
|
|
751
|
+
/* Common setup for the two REDUCE forms: validate scope (single-axis,
|
|
752
|
+
no mask), allocate the reduced-shape output entity, init T1 READ
|
|
753
|
+
side, build the input slab view with ALIAS / SCRATCH dispatch.
|
|
754
|
+
Sets st->output / st->t1 / st->slab_view / st->carrier and returns
|
|
755
|
+
the source CArray ptr via *out_src + the fiber length via *out_n.
|
|
756
|
+
|
|
757
|
+
REDUCE has no WRITE-side T1; the output is filled cell-by-cell from
|
|
758
|
+
the block return value via rb_ca_obj2ptr. The output's dim is
|
|
759
|
+
src->dim with the slab axis removed (1-D src reducing axis 0 = scalar
|
|
760
|
+
form (ndim=1, dim=[1])). */
|
|
761
|
+
static void
|
|
762
|
+
ca_slab_reduce_setup (ca_slab_iter_state_t *st, CArray **out_src,
|
|
763
|
+
ca_size_t *out_n)
|
|
764
|
+
{
|
|
765
|
+
CArray *src;
|
|
766
|
+
CArray *out;
|
|
767
|
+
ca_size_t fiber_len;
|
|
768
|
+
ca_size_t out_dim[CA_RANK_MAX];
|
|
769
|
+
int8_t out_ndim;
|
|
770
|
+
int rc;
|
|
771
|
+
int8_t i;
|
|
772
|
+
int8_t j;
|
|
773
|
+
uint32_t flags;
|
|
774
|
+
int is_multi = (st->slab_ndim > 1);
|
|
775
|
+
|
|
776
|
+
TypedData_Get_Struct(st->self, CArray, &carray_data_type, src);
|
|
777
|
+
|
|
778
|
+
/* Mask transparent carry: input mask is visible to the user's
|
|
779
|
+
block via slab.mask / slab.has_mask?. For REDUCE_FIBER form
|
|
780
|
+
(= per-element inject), masked cells are yielded as CA::UNDEF —
|
|
781
|
+
matching CArray's standard per-cell access semantics. For
|
|
782
|
+
REDUCE_SLAB form, the user-provided block sees the masked slab
|
|
783
|
+
and can call slab.sum, .mean, etc., which honor mask via the
|
|
784
|
+
existing mask-aware reduction kernels. */
|
|
785
|
+
|
|
786
|
+
/* Compute outer-shape output: drop dim at every slab axis. Reducing
|
|
787
|
+
all axes (= flatten reduce, K = src->ndim) collapses to 0-D; we
|
|
788
|
+
represent this as ndim=1, dim=[1] (= scalar-like, accessible via
|
|
789
|
+
output[0]). CScalar is not exposed for this case. */
|
|
790
|
+
out_ndim = src->ndim - st->slab_ndim;
|
|
791
|
+
if ( out_ndim == 0 ) {
|
|
792
|
+
out_ndim = 1;
|
|
793
|
+
out_dim[0] = 1;
|
|
794
|
+
}
|
|
795
|
+
else {
|
|
796
|
+
j = 0;
|
|
797
|
+
for ( i = 0; i < src->ndim; i++ ) {
|
|
798
|
+
int8_t k;
|
|
799
|
+
int is_slab_axis = 0;
|
|
800
|
+
for ( k = 0; k < st->slab_ndim; k++ ) {
|
|
801
|
+
if ( st->slab_axes[k] == i ) { is_slab_axis = 1; break; }
|
|
802
|
+
}
|
|
803
|
+
if ( ! is_slab_axis ) out_dim[j++] = src->dim[i];
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
st->output = rb_carray_new(st->out_data_type, out_ndim, out_dim,
|
|
808
|
+
st->out_bytes, NULL);
|
|
809
|
+
TypedData_Get_Struct(st->output, CArray, &carray_data_type, out);
|
|
810
|
+
ca_allocate(out);
|
|
811
|
+
|
|
812
|
+
/* FIBER_CONTIG is naxes==1 only (T1 substrate contract). Multi-axis
|
|
813
|
+
omits the flag and uses bare CA_SLAB_AXES; T1 picks an in-src
|
|
814
|
+
alias_mode for entity / CAStride sources (= the only supported case;
|
|
815
|
+
non-ALIAS modes raise NotImpError in setup_input_slab_view). */
|
|
816
|
+
flags = is_multi ? 0 : CA_KERNEL_FIBER_CONTIG;
|
|
817
|
+
st->t1 = (ca_iter_state *) xmalloc(sizeof(ca_iter_state));
|
|
818
|
+
rc = ca_iter_state_init_l2(st->t1, src, CA_SLAB_AXES,
|
|
819
|
+
st->slab_axes, st->slab_ndim, flags);
|
|
820
|
+
if ( rc != CA_ITER_OK ) {
|
|
821
|
+
rb_raise(rb_eRuntimeError,
|
|
822
|
+
"CArray#reduce_slab: T1 init failed rc=%d", rc);
|
|
823
|
+
}
|
|
824
|
+
st->t1_started = 1;
|
|
825
|
+
|
|
826
|
+
fiber_len = st->t1->slab_elements;
|
|
827
|
+
|
|
828
|
+
/* Centralised ALIAS / SCRATCH + multi-axis + mask carry dispatch. */
|
|
829
|
+
ca_slab_setup_input_slab_view(st, src, st->t1->slab_dims[0]);
|
|
830
|
+
|
|
831
|
+
*out_src = src;
|
|
832
|
+
*out_n = fiber_len;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/* Per-slab block form: block receives the slab CArray, returns a
|
|
836
|
+
scalar; each scalar is cast into the corresponding output cell.
|
|
837
|
+
(`init:` absent ⇒ this path) */
|
|
838
|
+
static VALUE
|
|
839
|
+
ca_slab_run_reduce_slab (ca_slab_iter_state_t *st)
|
|
840
|
+
{
|
|
841
|
+
CArray *src;
|
|
842
|
+
CArray *out;
|
|
843
|
+
ca_size_t fiber_len;
|
|
844
|
+
char *p_in;
|
|
845
|
+
boolean8_t *m_in;
|
|
846
|
+
CAStride *slab_cs;
|
|
847
|
+
CAWrap *carrier_cw = NULL;
|
|
848
|
+
int in_alias;
|
|
849
|
+
ca_size_t out_idx = 0;
|
|
850
|
+
char buf[64];
|
|
851
|
+
char *scratch;
|
|
852
|
+
|
|
853
|
+
ca_slab_reduce_setup(st, &src, &fiber_len);
|
|
854
|
+
TypedData_Get_Struct(st->output, CArray, &carray_data_type, out);
|
|
855
|
+
TypedData_Get_Struct(st->slab_view, CAStride, &castride_data_type, slab_cs);
|
|
856
|
+
in_alias = (st->carrier == Qnil);
|
|
857
|
+
if ( ! in_alias ) {
|
|
858
|
+
TypedData_Get_Struct(st->carrier, CAWrap, &cawrap_data_type, carrier_cw);
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
scratch = (st->out_bytes <= (ca_size_t) sizeof(buf)) ? buf
|
|
862
|
+
: xmalloc(st->out_bytes);
|
|
863
|
+
|
|
864
|
+
while ( ca_iter_state_next_slab_axes(st->t1, &p_in, &m_in) ) {
|
|
865
|
+
VALUE result;
|
|
866
|
+
|
|
867
|
+
ca_slab_advance_slab_view(st, slab_cs, carrier_cw,
|
|
868
|
+
src, p_in, m_in, in_alias);
|
|
869
|
+
|
|
870
|
+
result = rb_yield(st->slab_view);
|
|
871
|
+
|
|
872
|
+
/* Strict scalar contract: block must return a scalar Ruby value
|
|
873
|
+
(Numeric / Object / etc.). A CArray is a
|
|
874
|
+
contract violation; even a 1-element CArray is rejected, since
|
|
875
|
+
the user almost always means to return the underlying scalar
|
|
876
|
+
value via `slab[0]`. Better surfaced as an error than silently
|
|
877
|
+
coerced through obj2ptr. */
|
|
878
|
+
if ( rb_obj_is_kind_of(result, rb_cCArray) ) {
|
|
879
|
+
CArray *res;
|
|
880
|
+
TypedData_Get_Struct(result, CArray, &carray_data_type, res);
|
|
881
|
+
if ( scratch != buf ) xfree(scratch);
|
|
882
|
+
rb_raise(rb_eArgError,
|
|
883
|
+
"CArray#reduce_slab: per-slab block must return a scalar "
|
|
884
|
+
"(got CArray with %lld element%s; use `slab[0]` or "
|
|
885
|
+
"`slab.sum` etc. to extract the scalar)",
|
|
886
|
+
(long long) res->elements,
|
|
887
|
+
res->elements == 1 ? "" : "s");
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
rb_ca_obj2ptr(st->output, result, scratch);
|
|
891
|
+
memcpy(out->ptr + out_idx * st->out_bytes, scratch, st->out_bytes);
|
|
892
|
+
out_idx++;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
if ( scratch != buf ) xfree(scratch);
|
|
896
|
+
return st->output;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
/* Per-element fiber form: block receives (acc, x) for each element in
|
|
900
|
+
the slab, returns the new accumulator. The final accumulator is
|
|
901
|
+
cast into the output cell. (`init:` given ⇒ this path) */
|
|
902
|
+
static VALUE
|
|
903
|
+
ca_slab_run_reduce_fiber (ca_slab_iter_state_t *st)
|
|
904
|
+
{
|
|
905
|
+
CArray *src;
|
|
906
|
+
CArray *out;
|
|
907
|
+
ca_size_t fiber_len;
|
|
908
|
+
char *p_in;
|
|
909
|
+
boolean8_t *m_in;
|
|
910
|
+
CAStride *slab_cs;
|
|
911
|
+
CAWrap *carrier_cw = NULL;
|
|
912
|
+
int in_alias;
|
|
913
|
+
ca_size_t out_idx = 0;
|
|
914
|
+
char buf[64];
|
|
915
|
+
char *scratch;
|
|
916
|
+
|
|
917
|
+
ca_slab_reduce_setup(st, &src, &fiber_len);
|
|
918
|
+
TypedData_Get_Struct(st->output, CArray, &carray_data_type, out);
|
|
919
|
+
TypedData_Get_Struct(st->slab_view, CAStride, &castride_data_type, slab_cs);
|
|
920
|
+
in_alias = (st->carrier == Qnil);
|
|
921
|
+
if ( ! in_alias ) {
|
|
922
|
+
TypedData_Get_Struct(st->carrier, CAWrap, &cawrap_data_type, carrier_cw);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
scratch = (st->out_bytes <= (ca_size_t) sizeof(buf)) ? buf
|
|
926
|
+
: xmalloc(st->out_bytes);
|
|
927
|
+
|
|
928
|
+
while ( ca_iter_state_next_slab_axes(st->t1, &p_in, &m_in) ) {
|
|
929
|
+
VALUE acc;
|
|
930
|
+
ca_size_t i;
|
|
931
|
+
|
|
932
|
+
ca_slab_advance_slab_view(st, slab_cs, carrier_cw,
|
|
933
|
+
src, p_in, m_in, in_alias);
|
|
934
|
+
|
|
935
|
+
acc = st->init_val;
|
|
936
|
+
{
|
|
937
|
+
/* K-D row-major walk over the slab. For single-axis (slab_ndim=1)
|
|
938
|
+
this collapses to a 1-D fiber loop; for multi-axis it visits
|
|
939
|
+
cells in row-major order over slab_dims. */
|
|
940
|
+
int8_t k;
|
|
941
|
+
ca_size_t kidx[CA_RANK_MAX] = { 0 };
|
|
942
|
+
ca_size_t total = st->t1->slab_elements;
|
|
943
|
+
for ( i = 0; i < total; i++ ) {
|
|
944
|
+
VALUE x;
|
|
945
|
+
x = rb_ca_fetch_index(st->slab_view, kidx);
|
|
946
|
+
acc = rb_yield_values(2, acc, x);
|
|
947
|
+
/* Increment kidx[] row-major. */
|
|
948
|
+
for ( k = slab_cs->ndim - 1; k >= 0; k-- ) {
|
|
949
|
+
if ( ++kidx[k] < slab_cs->dim[k] ) break;
|
|
950
|
+
kidx[k] = 0;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
(void) fiber_len;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
rb_ca_obj2ptr(st->output, acc, scratch);
|
|
957
|
+
memcpy(out->ptr + out_idx * st->out_bytes, scratch, st->out_bytes);
|
|
958
|
+
out_idx++;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
if ( scratch != buf ) xfree(scratch);
|
|
962
|
+
return st->output;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/* ------------------------------------------------------------------- */
|
|
966
|
+
/* EACH form */
|
|
967
|
+
/* ------------------------------------------------------------------- */
|
|
968
|
+
|
|
969
|
+
/* Side-effect only iteration: yield each slab to the block, discard
|
|
970
|
+
the return value, return self when done. No output entity, no
|
|
971
|
+
scatter — the simplest of the four forms.
|
|
972
|
+
|
|
973
|
+
Slab lifetime: block-only. If the user captures the slab object
|
|
974
|
+
across iterations (= `vals = []; ca.each_slab(...) { |s| vals << s }`),
|
|
975
|
+
every entry will reflect the LAST iter's data (capture-out is a
|
|
976
|
+
documented contract, no runtime check).
|
|
977
|
+
|
|
978
|
+
`break` / `next` / `return` interact with rb_ensure correctly: the
|
|
979
|
+
ensure_fn runs cleanup before the non-local exit propagates. */
|
|
980
|
+
static VALUE
|
|
981
|
+
ca_slab_run_each (ca_slab_iter_state_t *st)
|
|
982
|
+
{
|
|
983
|
+
CArray *src;
|
|
984
|
+
char *p_in;
|
|
985
|
+
boolean8_t *m_in;
|
|
986
|
+
CAStride *slab_cs;
|
|
987
|
+
CAWrap *carrier_cw = NULL;
|
|
988
|
+
int in_alias;
|
|
989
|
+
int rc;
|
|
990
|
+
|
|
991
|
+
TypedData_Get_Struct(st->self, CArray, &carray_data_type, src);
|
|
992
|
+
|
|
993
|
+
/* Mask transparent carry: user's block sees slab.mask /
|
|
994
|
+
slab.has_mask?; the block's return value is discarded by each_slab
|
|
995
|
+
so output mask is irrelevant. */
|
|
996
|
+
|
|
997
|
+
/* FIBER_CONTIG is naxes==1 only; multi-axis omits the flag. */
|
|
998
|
+
{
|
|
999
|
+
uint32_t flags = (st->slab_ndim == 1) ? CA_KERNEL_FIBER_CONTIG : 0;
|
|
1000
|
+
st->t1 = (ca_iter_state *) xmalloc(sizeof(ca_iter_state));
|
|
1001
|
+
rc = ca_iter_state_init_l2(st->t1, src, CA_SLAB_AXES,
|
|
1002
|
+
st->slab_axes, st->slab_ndim, flags);
|
|
1003
|
+
if ( rc != CA_ITER_OK ) {
|
|
1004
|
+
rb_raise(rb_eRuntimeError,
|
|
1005
|
+
"CArray#each_slab: T1 init failed rc=%d", rc);
|
|
1006
|
+
}
|
|
1007
|
+
st->t1_started = 1;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
in_alias = ca_slab_setup_input_slab_view(st, src, st->t1->slab_dims[0]);
|
|
1011
|
+
|
|
1012
|
+
TypedData_Get_Struct(st->slab_view, CAStride, &castride_data_type, slab_cs);
|
|
1013
|
+
if ( ! in_alias ) {
|
|
1014
|
+
TypedData_Get_Struct(st->carrier, CAWrap, &cawrap_data_type, carrier_cw);
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
while ( ca_iter_state_next_slab_axes(st->t1, &p_in, &m_in) ) {
|
|
1018
|
+
ca_slab_advance_slab_view(st, slab_cs, carrier_cw,
|
|
1019
|
+
src, p_in, m_in, in_alias);
|
|
1020
|
+
rb_yield(st->slab_view); /* return value discarded */
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
return st->self;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
/* ------------------------------------------------------------------- */
|
|
1027
|
+
/* rb_ensure callbacks (= run_body / finish), state-pointer based. */
|
|
1028
|
+
/* ------------------------------------------------------------------- */
|
|
1029
|
+
|
|
1030
|
+
static VALUE
|
|
1031
|
+
slab_state_run_body (VALUE arg)
|
|
1032
|
+
{
|
|
1033
|
+
ca_slab_iter_state_t *st = (ca_slab_iter_state_t *)(uintptr_t) arg;
|
|
1034
|
+
switch ( st->form ) {
|
|
1035
|
+
case CA_SLAB_FORM_MAP: return ca_slab_run_map(st);
|
|
1036
|
+
case CA_SLAB_FORM_REDUCE_SLAB: return ca_slab_run_reduce_slab(st);
|
|
1037
|
+
case CA_SLAB_FORM_REDUCE_FIBER: return ca_slab_run_reduce_fiber(st);
|
|
1038
|
+
case CA_SLAB_FORM_EACH: return ca_slab_run_each(st);
|
|
1039
|
+
default:
|
|
1040
|
+
rb_raise(rb_eNotImpError, "CArray slab: unknown form %d", (int) st->form);
|
|
1041
|
+
}
|
|
1042
|
+
return Qnil;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
static VALUE
|
|
1046
|
+
slab_state_finish (VALUE arg)
|
|
1047
|
+
{
|
|
1048
|
+
ca_slab_iter_state_t *st = (ca_slab_iter_state_t *)(uintptr_t) arg;
|
|
1049
|
+
|
|
1050
|
+
/* T1 substrate teardown.
|
|
1051
|
+
CAREFUL: ca_detach(slab_view) is NEVER called — T1 owns the buffer;
|
|
1052
|
+
dropping the VALUE handles lets GC reclaim the CAStride wrappers
|
|
1053
|
+
without xfreeing the borrowed ptr. */
|
|
1054
|
+
if ( st->t1_started ) ca_iter_state_finish(st->t1);
|
|
1055
|
+
if ( st->t1_out_started ) ca_iter_state_finish(st->t1_out);
|
|
1056
|
+
|
|
1057
|
+
/* Free heap-owned state (each guarded; partial-acquire paths land here
|
|
1058
|
+
too, so NULL checks matter). */
|
|
1059
|
+
if ( st->t1 ) { xfree(st->t1); st->t1 = NULL; }
|
|
1060
|
+
if ( st->t1_out ) { xfree(st->t1_out); st->t1_out = NULL; }
|
|
1061
|
+
if ( st->own_data_scratch ) { xfree(st->own_data_scratch); st->own_data_scratch = NULL; }
|
|
1062
|
+
if ( st->own_mask_scratch ) { xfree(st->own_mask_scratch); st->own_mask_scratch = NULL; }
|
|
1063
|
+
if ( st->own_out_scratch ) { xfree(st->own_out_scratch); st->own_out_scratch = NULL; }
|
|
1064
|
+
|
|
1065
|
+
/* Drop VALUE handles so the rest of the entry function does not hold
|
|
1066
|
+
references to scratch / slab views that GC may now collect. */
|
|
1067
|
+
st->slab_view = Qnil;
|
|
1068
|
+
st->carrier = Qnil;
|
|
1069
|
+
st->mask_carrier = Qnil;
|
|
1070
|
+
st->output_slab_view = Qnil;
|
|
1071
|
+
|
|
1072
|
+
return Qnil;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
/* ------------------------------------------------------------------- */
|
|
1076
|
+
/* Entry points: CArray#map_slab / #reduce_slab / #each_slab */
|
|
1077
|
+
/* ------------------------------------------------------------------- */
|
|
1078
|
+
|
|
1079
|
+
/* Shared kwarg parse: returns axis (required) + data_type / init via
|
|
1080
|
+
out-params. init_out is set to Qundef if the `:init` key was absent
|
|
1081
|
+
(= caller distinguishes per-slab vs per-element reduce forms). */
|
|
1082
|
+
static void
|
|
1083
|
+
slab_parse_kwargs (VALUE kw, int allow_init, int allow_data_type,
|
|
1084
|
+
const char *method_name,
|
|
1085
|
+
VALUE *axis_out, VALUE *data_type_out, VALUE *init_out)
|
|
1086
|
+
{
|
|
1087
|
+
ID keys[3];
|
|
1088
|
+
VALUE vals[3];
|
|
1089
|
+
int n_optional = 0;
|
|
1090
|
+
|
|
1091
|
+
keys[0] = rb_intern("axis");
|
|
1092
|
+
if ( allow_data_type ) { keys[1 + n_optional] = rb_intern("data_type"); n_optional++; }
|
|
1093
|
+
if ( allow_init ) { keys[1 + n_optional] = rb_intern("init"); n_optional++; }
|
|
1094
|
+
|
|
1095
|
+
if ( NIL_P(kw) ) {
|
|
1096
|
+
rb_raise(rb_eArgError, "%s: axis: keyword required", method_name);
|
|
1097
|
+
}
|
|
1098
|
+
rb_get_kwargs(kw, keys, 1, n_optional, vals);
|
|
1099
|
+
|
|
1100
|
+
*axis_out = vals[0];
|
|
1101
|
+
*data_type_out = (allow_data_type && vals[1] != Qundef) ? vals[1] : Qnil;
|
|
1102
|
+
*init_out = Qundef;
|
|
1103
|
+
if ( allow_init ) {
|
|
1104
|
+
int init_idx = allow_data_type ? 2 : 1;
|
|
1105
|
+
*init_out = vals[init_idx]; /* Qundef if `:init` not given */
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
/* Helper: split argv into kwarg hash (or Qnil). Entries take only kwargs. */
|
|
1110
|
+
static VALUE
|
|
1111
|
+
slab_extract_kwargs (int argc, VALUE *argv, const char *method_name)
|
|
1112
|
+
{
|
|
1113
|
+
VALUE kw = Qnil;
|
|
1114
|
+
if ( argc == 1 && RB_TYPE_P(argv[0], T_HASH) ) {
|
|
1115
|
+
kw = argv[0];
|
|
1116
|
+
}
|
|
1117
|
+
else if ( argc != 0 ) {
|
|
1118
|
+
rb_raise(rb_eArgError, "%s: expected only keyword arguments", method_name);
|
|
1119
|
+
}
|
|
1120
|
+
return kw;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
static VALUE
|
|
1124
|
+
rb_ca_map_slab (int argc, VALUE *argv, VALUE self)
|
|
1125
|
+
{
|
|
1126
|
+
ca_slab_iter_state_t st = {0};
|
|
1127
|
+
VALUE kw, axis, data_type, init_unused;
|
|
1128
|
+
VALUE result;
|
|
1129
|
+
|
|
1130
|
+
kw = slab_extract_kwargs(argc, argv, "CArray#map_slab");
|
|
1131
|
+
slab_parse_kwargs(kw, /*allow_init=*/0, /*allow_data_type=*/1,
|
|
1132
|
+
"CArray#map_slab",
|
|
1133
|
+
&axis, &data_type, &init_unused);
|
|
1134
|
+
|
|
1135
|
+
if ( ! rb_block_given_p() ) {
|
|
1136
|
+
rb_raise(rb_eLocalJumpError, "CArray#map_slab: block required");
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
slab_state_init(&st, self, CA_SLAB_FORM_MAP, axis, data_type, Qundef);
|
|
1140
|
+
|
|
1141
|
+
result = rb_ensure(slab_state_run_body, (VALUE)(uintptr_t)&st,
|
|
1142
|
+
slab_state_finish, (VALUE)(uintptr_t)&st);
|
|
1143
|
+
RB_GC_GUARD(self);
|
|
1144
|
+
return result;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
static VALUE
|
|
1148
|
+
rb_ca_reduce_slab (int argc, VALUE *argv, VALUE self)
|
|
1149
|
+
{
|
|
1150
|
+
ca_slab_iter_state_t st = {0};
|
|
1151
|
+
VALUE kw, axis, data_type, init_val;
|
|
1152
|
+
VALUE result;
|
|
1153
|
+
int8_t form;
|
|
1154
|
+
|
|
1155
|
+
kw = slab_extract_kwargs(argc, argv, "CArray#reduce_slab");
|
|
1156
|
+
slab_parse_kwargs(kw, /*allow_init=*/1, /*allow_data_type=*/1,
|
|
1157
|
+
"CArray#reduce_slab",
|
|
1158
|
+
&axis, &data_type, &init_val);
|
|
1159
|
+
|
|
1160
|
+
if ( ! rb_block_given_p() ) {
|
|
1161
|
+
rb_raise(rb_eLocalJumpError, "CArray#reduce_slab: block required");
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
/* `init:` absent => per-slab block form; present => per-element fiber form. */
|
|
1165
|
+
form = (init_val == Qundef) ? CA_SLAB_FORM_REDUCE_SLAB
|
|
1166
|
+
: CA_SLAB_FORM_REDUCE_FIBER;
|
|
1167
|
+
|
|
1168
|
+
slab_state_init(&st, self, form, axis, data_type, init_val);
|
|
1169
|
+
|
|
1170
|
+
result = rb_ensure(slab_state_run_body, (VALUE)(uintptr_t)&st,
|
|
1171
|
+
slab_state_finish, (VALUE)(uintptr_t)&st);
|
|
1172
|
+
RB_GC_GUARD(self);
|
|
1173
|
+
return result;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
static VALUE
|
|
1177
|
+
rb_ca_each_slab (int argc, VALUE *argv, VALUE self)
|
|
1178
|
+
{
|
|
1179
|
+
ca_slab_iter_state_t st = {0};
|
|
1180
|
+
VALUE kw, axis, data_type_unused, init_unused;
|
|
1181
|
+
VALUE result;
|
|
1182
|
+
|
|
1183
|
+
kw = slab_extract_kwargs(argc, argv, "CArray#each_slab");
|
|
1184
|
+
slab_parse_kwargs(kw, /*allow_init=*/0, /*allow_data_type=*/0,
|
|
1185
|
+
"CArray#each_slab",
|
|
1186
|
+
&axis, &data_type_unused, &init_unused);
|
|
1187
|
+
|
|
1188
|
+
if ( ! rb_block_given_p() ) {
|
|
1189
|
+
/* to_enum hop must mark kw as kwargs (Ruby 3 strict separation),
|
|
1190
|
+
otherwise the resumed enumerator dispatches each_slab(positional_hash)
|
|
1191
|
+
and raises ArgumentError, silently losing `axis:`. */
|
|
1192
|
+
VALUE enum_kw = rb_hash_new();
|
|
1193
|
+
VALUE args[2];
|
|
1194
|
+
rb_hash_aset(enum_kw, ID2SYM(rb_intern("axis")), axis);
|
|
1195
|
+
args[0] = ID2SYM(rb_intern("each_slab"));
|
|
1196
|
+
args[1] = enum_kw;
|
|
1197
|
+
return rb_funcallv_kw(self, rb_intern("to_enum"),
|
|
1198
|
+
2, args, RB_PASS_KEYWORDS);
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
slab_state_init(&st, self, CA_SLAB_FORM_EACH, axis, Qnil, Qundef);
|
|
1202
|
+
|
|
1203
|
+
result = rb_ensure(slab_state_run_body, (VALUE)(uintptr_t)&st,
|
|
1204
|
+
slab_state_finish, (VALUE)(uintptr_t)&st);
|
|
1205
|
+
RB_GC_GUARD(self);
|
|
1206
|
+
return result;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
/* ------------------------------------------------------------------- */
|
|
1210
|
+
/* Init_carray_slab */
|
|
1211
|
+
/* ------------------------------------------------------------------- */
|
|
1212
|
+
|
|
1213
|
+
void
|
|
1214
|
+
Init_carray_slab (void)
|
|
1215
|
+
{
|
|
1216
|
+
rb_define_method(rb_cCArray, "map_slab", rb_ca_map_slab, -1);
|
|
1217
|
+
rb_define_method(rb_cCArray, "reduce_slab", rb_ca_reduce_slab, -1);
|
|
1218
|
+
rb_define_method(rb_cCArray, "each_slab", rb_ca_each_slab, -1);
|
|
1219
|
+
}
|