carray 2.0.1 → 3.0.1
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 +6 -25
- data/CHANGELOG.md +338 -0
- data/{NEWS.md → CHANGELOG.v1.md} +3 -0
- data/LICENSE +1 -1
- data/README.md +120 -36
- data/carray.gemspec +32 -30
- 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 +1244 -0
- data/ext/ca_bincmp_dispatch.c +76 -0
- data/ext/ca_bincmp_dispatch.h +85 -0
- data/ext/ca_binop_dispatch.c +124 -0
- data/ext/ca_binop_dispatch.h +152 -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 +239 -0
- data/ext/ca_group_iter.c +304 -0
- data/ext/ca_iter_substrate.h +325 -0
- data/ext/ca_kernel_iterator.c +4367 -0
- data/ext/ca_kernel_iterator.h +2596 -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 +839 -0
- data/ext/ca_obj_binop.c +948 -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 +967 -0
- data/ext/ca_obj_face.c +750 -0
- data/ext/ca_obj_face.h +279 -0
- data/ext/ca_obj_fake.c +239 -100
- data/ext/ca_obj_farray.c +54 -441
- data/ext/ca_obj_field.c +82 -529
- data/ext/ca_obj_fixlen_string.c +308 -0
- data/ext/ca_obj_grid.c +866 -440
- data/ext/ca_obj_meld.c +1039 -0
- data/ext/ca_obj_moncmp.c +588 -0
- data/ext/ca_obj_monop.c +1123 -0
- data/ext/ca_obj_object.c +866 -296
- data/ext/ca_obj_record.c +470 -0
- data/ext/ca_obj_reduce.c +97 -82
- data/ext/ca_obj_refer.c +593 -459
- data/ext/ca_obj_remap.c +475 -0
- data/ext/ca_obj_repeat.c +92 -477
- data/ext/ca_obj_roll.c +624 -0
- data/ext/ca_obj_select.c +344 -296
- data/ext/ca_obj_select_axis.c +1306 -0
- data/ext/ca_obj_shift.c +231 -793
- data/ext/ca_obj_source.c +78 -0
- data/ext/ca_obj_stack.c +1173 -0
- data/ext/ca_obj_stride.c +2584 -0
- data/ext/ca_obj_string.c +270 -0
- data/ext/ca_obj_tile.c +622 -0
- data/ext/ca_obj_time.c +548 -0
- data/ext/ca_obj_timedelta.c +437 -0
- data/ext/ca_obj_transpose.c +62 -516
- data/ext/ca_obj_triop.c +785 -0
- data/ext/ca_obj_window.c +1202 -565
- data/ext/ca_op_byte_swap.c +175 -0
- data/ext/ca_op_cmplx64.h +123 -0
- data/ext/ca_op_ipower.c +316 -0
- data/ext/ca_op_powi.h +88 -0
- data/ext/ca_sort_kernels.h +132 -0
- data/ext/ca_sweep_engine.c +473 -0
- data/ext/ca_sweep_engine.h +166 -0
- data/ext/ca_transform_common.c +235 -0
- data/ext/ca_triop_dispatch.c +55 -0
- data/ext/ca_triop_dispatch.h +62 -0
- data/ext/carray.h +810 -420
- data/ext/carray_access.c +873 -731
- data/ext/carray_attribute.c +98 -329
- data/ext/carray_bincount.c +255 -0
- data/ext/carray_broadcast.c +376 -0
- data/ext/carray_build_flags.h +3 -0
- data/ext/carray_call_cfunc.c +2897 -874
- data/ext/carray_call_cfunc.h +313 -0
- data/ext/carray_cast.c +1264 -315
- 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 +168 -270
- data/ext/carray_core.c +1396 -206
- 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 +1021 -0
- data/ext/carray_index_classifier.h +27 -0
- data/ext/carray_internal.h +136 -0
- data/ext/carray_kernels_bincmp.c +4446 -0
- data/ext/carray_kernels_binop.c +11001 -0
- data/ext/carray_kernels_init.c +1131 -0
- data/ext/carray_kernels_map.c +3467 -0
- data/ext/carray_kernels_moncmp.c +2097 -0
- data/ext/carray_kernels_monop.c +18313 -0
- data/ext/carray_kernels_reduce_aggregate.c +25837 -0
- data/ext/carray_kernels_reduce_boolean.c +330 -0
- data/ext/carray_kernels_reduce_cumulative.c +14593 -0
- data/ext/carray_kernels_reduce_extreme.c +16948 -0
- data/ext/carray_kernels_reduce_variance.c +3910 -0
- data/ext/carray_kernels_scan.c +3693 -0
- data/ext/carray_kernels_search.c +32138 -0
- data/ext/carray_kernels_sort.c +10626 -0
- data/ext/carray_kernels_triop.c +1392 -0
- data/ext/carray_lazy.c +737 -0
- data/ext/carray_loop.c +88 -200
- data/ext/carray_mask.c +853 -158
- 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 +1650 -0
- data/ext/carray_operator.c +1525 -320
- 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 +234 -55
- data/ext/mk_call_cfunc.rb +671 -0
- data/ext/mkkernel.rb +9096 -0
- data/ext/ruby_carray.c +211 -108
- 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 +239 -0
- data/lib/carray/autoload_method_extension.rb +45 -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 +614 -0
- data/lib/carray/boolean_reduce.rb +109 -0
- data/lib/carray/categorical.rb +561 -0
- data/lib/carray/categorical_iterator.rb +1084 -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 +160 -328
- data/lib/carray/core_extensions.rb +297 -0
- data/lib/carray/data_type_extension.rb +250 -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 +642 -0
- data/lib/carray/frame/group.rb +186 -0
- data/lib/carray/frame/io.rb +321 -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 +316 -0
- data/lib/carray/frame.rb +16 -0
- data/lib/carray/fuse_source.rb +123 -0
- data/lib/carray/fusion.rb +218 -0
- data/lib/carray/histogram.rb +512 -0
- data/lib/carray/inspect.rb +37 -26
- data/lib/carray/iterator.rb +58 -349
- data/lib/carray/lazy.rb +941 -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 +52 -0
- data/lib/carray/methods/mask_duplicates.rb +41 -0
- data/lib/carray/methods/meshgrid.rb +90 -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 +161 -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 +89 -0
- data/lib/carray/serialize.rb +478 -167
- data/lib/carray/slab_iterator.rb +305 -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 +2654 -38
- data/lib/carray/window_iterator.rb +927 -0
- data/lib/carray.rb +55 -57
- data/yard-stubs/ca_obj_array.rb +385 -0
- data/yard-stubs/ca_obj_bitarray.rb +38 -0
- data/yard-stubs/ca_obj_bitfield.rb +43 -0
- data/yard-stubs/ca_obj_block.rb +73 -0
- data/yard-stubs/ca_obj_byte_swap.rb +56 -0
- data/yard-stubs/ca_obj_fake.rb +31 -0
- data/yard-stubs/ca_obj_farray.rb +32 -0
- data/yard-stubs/ca_obj_field.rb +45 -0
- data/yard-stubs/ca_obj_grid.rb +35 -0
- data/yard-stubs/ca_obj_refer.rb +72 -0
- data/yard-stubs/ca_obj_roll.rb +45 -0
- data/yard-stubs/ca_obj_shift.rb +43 -0
- data/yard-stubs/ca_obj_stride.rb +181 -0
- data/yard-stubs/ca_obj_tile.rb +29 -0
- data/yard-stubs/ca_obj_transpose.rb +40 -0
- data/yard-stubs/ca_obj_window.rb +49 -0
- data/yard-stubs/carray_access.rb +131 -0
- data/yard-stubs/carray_attribute.rb +246 -0
- data/yard-stubs/carray_broadcast.rb +37 -0
- data/yard-stubs/carray_cast.rb +489 -0
- data/yard-stubs/carray_class.rb +65 -0
- data/yard-stubs/carray_conversion.rb +76 -0
- data/yard-stubs/carray_copy.rb +79 -0
- data/yard-stubs/carray_core.rb +114 -0
- data/yard-stubs/carray_count.rb +79 -0
- data/yard-stubs/carray_element.rb +108 -0
- data/yard-stubs/carray_generate.rb +66 -0
- data/yard-stubs/carray_lazy.rb +23 -0
- data/yard-stubs/carray_loop.rb +140 -0
- data/yard-stubs/carray_mask.rb +259 -0
- data/yard-stubs/carray_math.rb +132 -0
- data/yard-stubs/carray_mathfunc.rb +45 -0
- data/yard-stubs/carray_median_percentile.rb +89 -0
- data/yard-stubs/carray_memory_view.rb +163 -0
- data/yard-stubs/carray_order.rb +312 -0
- data/yard-stubs/carray_random.rb +89 -0
- data/yard-stubs/carray_scatter.rb +106 -0
- data/yard-stubs/carray_slab.rb +57 -0
- data/yard-stubs/carray_sort.rb +163 -0
- data/yard-stubs/carray_test.rb +85 -0
- data/yard-stubs/carray_undef.rb +64 -0
- data/yard-stubs/carray_utils.rb +97 -0
- data/yard-stubs/ruby_carray.rb +193 -0
- metadata +220 -138
- 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/ca_obj_unbound_repeat.c +0 -529
- 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/mailmap +0 -1
- 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/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_window.c
CHANGED
|
@@ -1,36 +1,111 @@
|
|
|
1
1
|
/* ---------------------------------------------------------------------------
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
CAWindow: a sliding rectangular view of the parent whose cells may fall
|
|
4
|
+
outside it. Each axis carries a start / count and a bounds policy that says
|
|
5
|
+
what an out-of-range (OOB) cell means: FILL / MASK give it `ca->fill` (or
|
|
6
|
+
mask it), NEAREST / RUBY / STRICT normalise or reject the index.
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
CAShift is a typedef of this struct (ca_obj_shift.c) and shares the whole
|
|
9
|
+
operation table, so every path here serves both.
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
Two internal models coexist, chosen per view by ca_window_recompute_embed:
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
embed model (FILL / MASK on every axis, embed_eligible)
|
|
14
|
+
The view decomposes into one alias rectangle (the part of the parent it
|
|
15
|
+
actually addresses, embed_*) plus its fill complement (the OOB part).
|
|
16
|
+
Attach / xfer are then "1 typed fill + 1 strided memcpy", with no
|
|
17
|
+
per-cell bound check. A window whose inner axes are full and which sits
|
|
18
|
+
entirely inside the parent (embed_alias_eligible) skips even that and
|
|
19
|
+
aliases the parent's buffer.
|
|
20
|
+
|
|
21
|
+
descriptor engine (any other bounds policy)
|
|
22
|
+
ca_window_describe_axes emits a per-axis descriptor and the shared engine
|
|
23
|
+
(ca_axis_dispatch.c) applies the policy per cell.
|
|
24
|
+
|
|
25
|
+
Region-copy helpers shared with CATile / CAStack live in
|
|
26
|
+
ca_composite_dispatch.c (included above).
|
|
27
|
+
|
|
28
|
+
--------------------------------------------------------------------------- */
|
|
10
29
|
|
|
11
30
|
#include "carray.h"
|
|
31
|
+
#include "ca_composite_dispatch.h"
|
|
32
|
+
#include "ca_obj_face.h" /* CA_FACE_LIFT_IF_FACE, used by rb_ca_window */
|
|
12
33
|
|
|
13
34
|
/* should not be static variable as used by CAIteratorWindow */
|
|
14
35
|
|
|
36
|
+
static size_t
|
|
37
|
+
ca_window_dsize (const void *ap)
|
|
38
|
+
{
|
|
39
|
+
const CAWindow *ca = (const CAWindow *) ap;
|
|
40
|
+
/* dim points to count; start, count, size0 are each ALLOC_N(ndim);
|
|
41
|
+
bounds is ALLOC_N(uint8_t, ndim) (per-axis);
|
|
42
|
+
fill is ALLOC_N(bytes);
|
|
43
|
+
embed_{parent_start,count,output_offset} are each ALLOC_N(ndim). */
|
|
44
|
+
return sizeof(CAWindow) + 6 * ca->ndim * sizeof(ca_size_t)
|
|
45
|
+
+ ca->ndim * sizeof(uint8_t) + ca->bytes;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Pool framework hooks for CAWindow. CAWindow owns eight variable-size tail
|
|
49
|
+
fields; seven of them are ndim-sized and move into a single _pool buffer:
|
|
50
|
+
|
|
51
|
+
6 * ndim ca_size_t : start, count, size0,
|
|
52
|
+
embed_parent_start, embed_count, embed_output_offset
|
|
53
|
+
1 * ndim uint8_t : bounds
|
|
54
|
+
|
|
55
|
+
`dim` aliases `count` (no allocation). The eighth field, `fill`, is
|
|
56
|
+
`bytes`-sized (element width, not ndim) and the ndim-only pool_bytes/
|
|
57
|
+
pool_init signature cannot size it; it stays on its own ALLOC_N inside
|
|
58
|
+
ca_window_setup. CAShift shares this struct and reuses these hooks via
|
|
59
|
+
the ca_window_func copy in Init_ca_obj_shift. */
|
|
60
|
+
static size_t
|
|
61
|
+
ca_window_pool_bytes (int8_t ndim)
|
|
62
|
+
{
|
|
63
|
+
ca_size_t n = (ndim > 0) ? ndim : 1;
|
|
64
|
+
return 6 * (size_t) n * sizeof(ca_size_t) + (size_t) n * sizeof(uint8_t);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static void
|
|
68
|
+
ca_window_pool_init (void *ap, int8_t ndim)
|
|
69
|
+
{
|
|
70
|
+
CAWindow *ca = (CAWindow *) ap;
|
|
71
|
+
ca_size_t n = (ndim > 0) ? ndim : 1;
|
|
72
|
+
ca_size_t *base = (ca_size_t *) ca->_pool;
|
|
73
|
+
/* six ca_size_t arrays first (8-byte aligned), then the uint8_t bounds
|
|
74
|
+
array after them. */
|
|
75
|
+
ca->start = base + 0 * n;
|
|
76
|
+
ca->count = base + 1 * n;
|
|
77
|
+
ca->size0 = base + 2 * n;
|
|
78
|
+
ca->embed_parent_start = base + 3 * n;
|
|
79
|
+
ca->embed_count = base + 4 * n;
|
|
80
|
+
ca->embed_output_offset = base + 5 * n;
|
|
81
|
+
ca->bounds = (uint8_t *) (base + 6 * n);
|
|
82
|
+
ca->dim = ca->count; /* alias; ca_window_setup re-sets */
|
|
83
|
+
}
|
|
84
|
+
|
|
15
85
|
const rb_data_type_t cawindow_data_type = {
|
|
16
|
-
.parent = &
|
|
86
|
+
.parent = &caview_data_type,
|
|
17
87
|
.wrap_struct_name = "CAWindow",
|
|
18
88
|
.function = {
|
|
19
89
|
.dmark = ca_mark,
|
|
20
90
|
.dfree = ca_free,
|
|
21
|
-
.dsize =
|
|
91
|
+
.dsize = ca_window_dsize,
|
|
22
92
|
.dcompact = NULL
|
|
23
93
|
},
|
|
24
94
|
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
|
25
95
|
};
|
|
26
96
|
|
|
97
|
+
/* CAREFUL: the mask TypedData must keep dfree = ca_free_nop. The mask CArray
|
|
98
|
+
is owned by the parent CAWindow's `ca->mask` field and freed by
|
|
99
|
+
free_ca_window's ca_free(ca->mask); if the wrapped Ruby VALUE (from the
|
|
100
|
+
`ca.mask` accessor / rb_ca_mask_array) freed it as well, GC stress would
|
|
101
|
+
double-free it. */
|
|
27
102
|
const rb_data_type_t cawindow_mask_data_type = {
|
|
28
103
|
.parent = &cawindow_data_type,
|
|
29
104
|
.wrap_struct_name = "CAWindowMask",
|
|
30
105
|
.function = {
|
|
31
106
|
.dmark = ca_mark,
|
|
32
|
-
.dfree =
|
|
33
|
-
.dsize =
|
|
107
|
+
.dfree = ca_free_nop,
|
|
108
|
+
.dsize = ca_window_dsize,
|
|
34
109
|
.dcompact = NULL
|
|
35
110
|
},
|
|
36
111
|
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
|
@@ -38,26 +113,552 @@ const rb_data_type_t cawindow_mask_data_type = {
|
|
|
38
113
|
|
|
39
114
|
int8_t CA_OBJ_WINDOW;
|
|
40
115
|
|
|
41
|
-
VALUE rb_cCAWindow;
|
|
42
|
-
VALUE rb_cCAWindowMask;
|
|
116
|
+
VALUE rb_cCAWindow;
|
|
117
|
+
VALUE rb_cCAWindowMask;
|
|
43
118
|
|
|
44
119
|
|
|
45
|
-
/*
|
|
46
|
-
class CAWindow < CAVirtual # :nodoc:
|
|
47
|
-
end
|
|
48
|
-
*/
|
|
120
|
+
/* ------------------------------------------------------------------- */
|
|
49
121
|
|
|
122
|
+
/* Computes the embed descriptor for a CAWindow / CAShift view. The
|
|
123
|
+
descriptor decomposes the view into "1 alias region (the part of parent
|
|
124
|
+
that is actually addressable) + 1 fill complement (the OOB part)".
|
|
125
|
+
|
|
126
|
+
Inputs: parent_dim[ndim], start[ndim], count[ndim]. step is
|
|
127
|
+
implicitly 1 for CAWindow (enforced in rb_ca_window).
|
|
128
|
+
bounds[] is not read here — the embed shape is policy-independent
|
|
129
|
+
(policy decides what to put in the fill complement, not which cells
|
|
130
|
+
are in the alias).
|
|
131
|
+
|
|
132
|
+
Outputs (written into ca->embed_*):
|
|
133
|
+
embed_parent_start[k] = max(0, start[k])
|
|
134
|
+
embed_count[k] = min(start[k]+count[k], parent_dim[k])
|
|
135
|
+
- embed_parent_start[k] (clamped to >= 0)
|
|
136
|
+
embed_output_offset[k]= embed_parent_start[k] - start[k]
|
|
137
|
+
embed_is_empty = 1 if any axis has embed_count[k] <= 0
|
|
138
|
+
embed_covers_all = 1 if every axis has start[k] >= 0
|
|
139
|
+
AND start[k]+count[k] <= parent_dim[k]
|
|
140
|
+
|
|
141
|
+
Called only through ca_window_recompute_embed, which also derives the
|
|
142
|
+
eligibility flags from the result. */
|
|
143
|
+
static void
|
|
144
|
+
ca_compute_embed_descriptor (int8_t ndim,
|
|
145
|
+
ca_size_t *parent_dim,
|
|
146
|
+
ca_size_t *start,
|
|
147
|
+
ca_size_t *count,
|
|
148
|
+
ca_size_t *embed_parent_start,
|
|
149
|
+
ca_size_t *embed_count,
|
|
150
|
+
ca_size_t *embed_output_offset,
|
|
151
|
+
uint8_t *embed_is_empty,
|
|
152
|
+
uint8_t *embed_covers_all)
|
|
153
|
+
{
|
|
154
|
+
int8_t k;
|
|
155
|
+
uint8_t any_empty = 0;
|
|
156
|
+
uint8_t all_interior = 1;
|
|
157
|
+
|
|
158
|
+
for ( k = 0; k < ndim; k++ ) {
|
|
159
|
+
ca_size_t s = start[k];
|
|
160
|
+
ca_size_t c = count[k];
|
|
161
|
+
ca_size_t pd = parent_dim[k];
|
|
162
|
+
ca_size_t ps, pe, oo, ec;
|
|
163
|
+
|
|
164
|
+
ps = (s > 0) ? s : 0; /* clamp low */
|
|
165
|
+
pe = (s + c < pd) ? (s + c) : pd;/* clamp high */
|
|
166
|
+
ec = pe - ps; /* alias count this axis */
|
|
167
|
+
if ( ec < 0 ) ec = 0; /* fully outside */
|
|
168
|
+
oo = ps - s; /* output-side offset */
|
|
169
|
+
|
|
170
|
+
embed_parent_start[k] = ps;
|
|
171
|
+
embed_count[k] = ec;
|
|
172
|
+
embed_output_offset[k] = oo;
|
|
173
|
+
|
|
174
|
+
if ( ec <= 0 ) any_empty = 1;
|
|
175
|
+
if ( s < 0 || s + c > pd ) all_interior = 0;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
*embed_is_empty = any_empty;
|
|
179
|
+
*embed_covers_all = all_interior;
|
|
180
|
+
}
|
|
50
181
|
|
|
51
182
|
/* ------------------------------------------------------------------- */
|
|
52
183
|
|
|
184
|
+
/* [MOVED] ca_fill_typed / ca_composite_region_gather / _scatter /
|
|
185
|
+
ca_composite_fill_complement -> ca_composite_dispatch.c (shared with
|
|
186
|
+
CATile / CAStack); reached via the include at the top of this file. */
|
|
187
|
+
|
|
188
|
+
/* Exposes CAWindow as a synthetic CAStride for compose-fold purposes. Used
|
|
189
|
+
by ca_stride_compose_to_root to walk *through* an interior-only CAWindow
|
|
190
|
+
without materialising it, enabling a full zero-copy chain when CAStride
|
|
191
|
+
family children wrap an interior-only CAWindow.
|
|
192
|
+
|
|
193
|
+
On success (interior-only + embed_eligible):
|
|
194
|
+
- synth_strides[k] = row-major byte stride of window->parent
|
|
195
|
+
(= bytes * Π window->parent->dim[k+1..ndim-1])
|
|
196
|
+
- synth_base = Σ embed_parent_start[k] * synth_strides[k]
|
|
197
|
+
(byte offset of embedded region start in
|
|
198
|
+
window->parent's byte space)
|
|
199
|
+
- synth_dim = pointer to window->dim (window's logical shape)
|
|
200
|
+
- synth_bytes = window->bytes
|
|
201
|
+
- synth_ndim = window->ndim
|
|
202
|
+
- next_parent = window->parent
|
|
203
|
+
Returns 1.
|
|
204
|
+
|
|
205
|
+
Caller assembles a CAStride struct from these fields and feeds it to
|
|
206
|
+
ca_stride_compose_through; the loop continues with next_parent.
|
|
207
|
+
|
|
208
|
+
Returns 0 if not foldable (= not a CAWindow / not embed_eligible /
|
|
209
|
+
not embed_covers_all). Callers must then break out of the
|
|
210
|
+
compose-fold loop and accept window as the root (= materialise via
|
|
211
|
+
ca_attach).
|
|
212
|
+
|
|
213
|
+
ndim invariant: synth_ndim equals window->ndim equals the leaf's
|
|
214
|
+
ndim by construction (CAStride children inherit ndim from parent).
|
|
215
|
+
No reshaping. */
|
|
216
|
+
int
|
|
217
|
+
ca_window_compose_fold (void *win_ap,
|
|
218
|
+
ca_size_t *synth_strides,
|
|
219
|
+
ca_size_t *synth_base,
|
|
220
|
+
ca_size_t **synth_dim,
|
|
221
|
+
ca_size_t *synth_bytes,
|
|
222
|
+
int8_t *synth_ndim,
|
|
223
|
+
CArray **next_parent)
|
|
224
|
+
{
|
|
225
|
+
CAWindow *w = (CAWindow *) win_ap;
|
|
226
|
+
ca_size_t s;
|
|
227
|
+
int8_t k;
|
|
228
|
+
|
|
229
|
+
if ( ! w->embed_eligible ) return 0;
|
|
230
|
+
if ( ! w->embed_covers_all ) return 0;
|
|
231
|
+
|
|
232
|
+
/* synth_strides = row-major byte stride over window->parent->dim */
|
|
233
|
+
s = w->bytes;
|
|
234
|
+
for ( k = w->ndim - 1; k >= 0; k-- ) {
|
|
235
|
+
synth_strides[k] = s;
|
|
236
|
+
s *= w->parent->dim[k];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/* synth_base = embedded region origin in window->parent's byte space */
|
|
240
|
+
*synth_base = 0;
|
|
241
|
+
for ( k = 0; k < w->ndim; k++ ) {
|
|
242
|
+
*synth_base += w->embed_parent_start[k] * synth_strides[k];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
*synth_dim = w->dim;
|
|
246
|
+
*synth_bytes = w->bytes;
|
|
247
|
+
*synth_ndim = w->ndim;
|
|
248
|
+
*next_parent = w->parent;
|
|
249
|
+
return 1;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/* fold_stride slot: compose the fold state f (leaf coords in this window's
|
|
253
|
+
byte space) through the interior
|
|
254
|
+
window into window->parent's byte space. Synthesises a CAStride layer
|
|
255
|
+
for the window-over-parent mapping (ca_window_compose_fold) and composes
|
|
256
|
+
f through it (ca_stride_compose_through). Declines (returns 0) when the
|
|
257
|
+
window is not interior-only, making the window the fold boundary. */
|
|
258
|
+
static int
|
|
259
|
+
ca_window_func_fold_stride (void *ap, ca_fold_t *f, void **next_parent)
|
|
260
|
+
{
|
|
261
|
+
CAWindow *w = (CAWindow *) ap;
|
|
262
|
+
ca_size_t synth_strides[CA_RANK_MAX];
|
|
263
|
+
ca_size_t synth_base;
|
|
264
|
+
ca_size_t *synth_dim;
|
|
265
|
+
ca_size_t synth_bytes;
|
|
266
|
+
int8_t synth_ndim;
|
|
267
|
+
CArray *win_parent;
|
|
268
|
+
CAStride tmp, synth;
|
|
269
|
+
ca_size_t next_strides[CA_RANK_MAX];
|
|
270
|
+
ca_size_t next_base;
|
|
271
|
+
int8_t k;
|
|
272
|
+
|
|
273
|
+
if (!ca_window_compose_fold(w, synth_strides, &synth_base, &synth_dim,
|
|
274
|
+
&synth_bytes, &synth_ndim, &win_parent)) {
|
|
275
|
+
return 0;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
tmp.ndim = f->ndim;
|
|
279
|
+
tmp.bytes = synth_bytes;
|
|
280
|
+
tmp.dim = f->counts; /* leaf extent in this window's space */
|
|
281
|
+
tmp.strides = f->strides;
|
|
282
|
+
tmp.base_offset = f->base;
|
|
283
|
+
|
|
284
|
+
synth.ndim = synth_ndim;
|
|
285
|
+
synth.bytes = synth_bytes;
|
|
286
|
+
synth.dim = synth_dim;
|
|
287
|
+
synth.strides = synth_strides;
|
|
288
|
+
synth.base_offset = synth_base;
|
|
289
|
+
|
|
290
|
+
if (!ca_stride_compose_through(&tmp, &synth, next_strides, &next_base)) {
|
|
291
|
+
return 0;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
for (k = 0; k < f->ndim; k++) f->strides[k] = next_strides[k];
|
|
295
|
+
f->base = next_base;
|
|
296
|
+
*next_parent = win_parent;
|
|
297
|
+
return 1;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
static void ca_window_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir);
|
|
301
|
+
|
|
302
|
+
/* xfer_stride: structural region delivery when a boundary-crossing CAWindow is
|
|
303
|
+
the (declining) fold boundary. The window fills the OOB cells itself and
|
|
304
|
+
hands the in-bound region to the parent via parent.xfer_stride, so no whole
|
|
305
|
+
view is materialised.
|
|
306
|
+
|
|
307
|
+
Structural path requires: FILL/MASK bounds (so OOB is a contiguous edge),
|
|
308
|
+
axis-aligned access, and unit src step (the natural window region / a
|
|
309
|
+
contiguous window slice). Other cases (PERIODIC/REFLECT/NEAREST bounds,
|
|
310
|
+
transposed / sub-sampled leaf) fall back to per-cell delivery (correct,
|
|
311
|
+
no whole-view attach). data is contiguous (semantics b). The wiring
|
|
312
|
+
guards ndim == window ndim. window.bytes == parent.bytes (no reinterpret). */
|
|
313
|
+
static void
|
|
314
|
+
ca_window_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
|
|
315
|
+
ca_size_t *strides, void *data, int dir)
|
|
316
|
+
{
|
|
317
|
+
CAWindow *w = (CAWindow *) ap;
|
|
318
|
+
CArray *parent = w->parent;
|
|
319
|
+
int8_t ndim = w->ndim;
|
|
320
|
+
int8_t inner = ndim - 1;
|
|
321
|
+
ca_size_t pnative[CA_RANK_MAX], wnative[CA_RANK_MAX], dstride[CA_RANK_MAX];
|
|
322
|
+
ca_size_t base_pos[CA_RANK_MAX]; /* window.start[k] + starts[k] (parent pos at o=0) */
|
|
323
|
+
ca_size_t lo[CA_RANK_MAX], hi[CA_RANK_MAX], o[CA_RANK_MAX];
|
|
324
|
+
ca_size_t s, n = 1, i;
|
|
325
|
+
int8_t k;
|
|
326
|
+
int structural = 1;
|
|
327
|
+
char *d = (char *) data;
|
|
328
|
+
|
|
329
|
+
s = parent->bytes;
|
|
330
|
+
for (k = ndim - 1; k >= 0; k--) { pnative[k] = s; s *= parent->dim[k]; }
|
|
331
|
+
s = w->bytes;
|
|
332
|
+
for (k = ndim - 1; k >= 0; k--) { wnative[k] = s; s *= w->dim[k]; }
|
|
333
|
+
s = w->bytes;
|
|
334
|
+
for (k = ndim - 1; k >= 0; k--) { dstride[k] = s; s *= counts[k]; }
|
|
335
|
+
for (k = 0; k < ndim; k++) n *= counts[k];
|
|
336
|
+
|
|
337
|
+
/* The request is over the view's addresses, so a transposed / flat request
|
|
338
|
+
is legal and must not be composed axis-by-axis; see
|
|
339
|
+
ca_xfer_stride_request_is_axis_box (carray.h). */
|
|
340
|
+
if ( ! ca_xfer_stride_request_is_axis_box(w, starts, counts, strides) ) {
|
|
341
|
+
structural = 0;
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
for (k = 0; k < ndim; k++) {
|
|
345
|
+
if ( (w->bounds[k] != CA_BOUNDS_FILL && w->bounds[k] != CA_BOUNDS_MASK)
|
|
346
|
+
|| strides[k] % wnative[k] != 0
|
|
347
|
+
|| strides[k] / wnative[k] != 1 ) {
|
|
348
|
+
structural = 0;
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (!structural) {
|
|
355
|
+
ca_size_t idx[CA_RANK_MAX], doff = 0, base = 0;
|
|
356
|
+
for (k = 0; k < ndim; k++) base += starts[k] * wnative[k];
|
|
357
|
+
for (k = 0; k < ndim; k++) idx[k] = 0;
|
|
358
|
+
while (1) {
|
|
359
|
+
ca_size_t woff = base, widx[CA_RANK_MAX];
|
|
360
|
+
for (k = 0; k < ndim; k++) woff += idx[k] * strides[k];
|
|
361
|
+
ca_addr2index((CArray *) w, woff / w->bytes, widx);
|
|
362
|
+
ca_window_func_xfer_index(w, widx, d + doff, dir);
|
|
363
|
+
doff += w->bytes;
|
|
364
|
+
k = ndim - 1;
|
|
365
|
+
while (k >= 0) { if (++idx[k] < counts[k]) break; idx[k] = 0; k--; }
|
|
366
|
+
if (k < 0) break;
|
|
367
|
+
}
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/* Intersect sub-region [starts, starts+counts) with the in-bound region.
|
|
372
|
+
lo[k]/hi[k] are offsets within the sub-region (= output-coordinate)
|
|
373
|
+
where the parent is in-bound. Same algebra as the per-row loop below,
|
|
374
|
+
hoisted up so the embed-based path can share it. */
|
|
375
|
+
for (k = 0; k < ndim; k++) {
|
|
376
|
+
ca_size_t l, h;
|
|
377
|
+
base_pos[k] = w->start[k] + starts[k];
|
|
378
|
+
l = -base_pos[k]; if (l < 0) l = 0;
|
|
379
|
+
h = w->size0[k] - base_pos[k]; if (h > counts[k]) h = counts[k];
|
|
380
|
+
if (h < 0) h = 0;
|
|
381
|
+
if (l > h) l = h;
|
|
382
|
+
lo[k] = l; hi[k] = h;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/* An unattached parent that ca_attach_is_alias accepts has memory to lend
|
|
386
|
+
and the attach neither allocates nor copies, so borrow it and re-enter
|
|
387
|
+
on the batched branch below. The recursion runs at most once: the
|
|
388
|
+
second entry sees parent->ptr != NULL. A parent with nothing to lend is
|
|
389
|
+
left alone — the per-row loop is the region protocol and must not be
|
|
390
|
+
traded for a whole-parent attach. */
|
|
391
|
+
if ( ! parent->ptr && ca_attach_is_alias(parent) ) {
|
|
392
|
+
ca_attach(parent);
|
|
393
|
+
if ( parent->ptr ) {
|
|
394
|
+
ca_window_func_xfer_stride(ap, starts, counts, strides, data, dir);
|
|
395
|
+
if ( dir == CA_XFER_PUT ) {
|
|
396
|
+
ca_sync(parent);
|
|
397
|
+
}
|
|
398
|
+
ca_detach(parent);
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
ca_detach(parent);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/* Fast path: when the parent is attached (ptr != NULL), drive
|
|
405
|
+
ca_composite_region_* + fill_complement directly with the sub-region
|
|
406
|
+
intersection geometry. This collapses the outer per-row dispatch loop
|
|
407
|
+
(one ca_xfer_stride per inner row) into a single batched routine. An
|
|
408
|
+
unattached parent with nothing to lend falls through to the per-row loop
|
|
409
|
+
below, which is equivalent but slower. */
|
|
410
|
+
if ( parent->ptr ) {
|
|
411
|
+
ca_size_t any_empty = 0;
|
|
412
|
+
ca_size_t alias_parent_start[CA_RANK_MAX];
|
|
413
|
+
ca_size_t alias_count[CA_RANK_MAX];
|
|
414
|
+
ca_size_t alias_output_offset[CA_RANK_MAX];
|
|
415
|
+
ca_size_t parent_strides[CA_RANK_MAX];
|
|
416
|
+
|
|
417
|
+
for (k = 0; k < ndim; k++) {
|
|
418
|
+
if (lo[k] >= hi[k]) { any_empty = 1; break; }
|
|
419
|
+
alias_output_offset[k] = lo[k];
|
|
420
|
+
alias_count[k] = hi[k] - lo[k];
|
|
421
|
+
alias_parent_start[k] = base_pos[k] + lo[k];
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (dir == CA_XFER_GET) {
|
|
425
|
+
if (any_empty) {
|
|
426
|
+
ca_fill_typed(d, w->fill, w->bytes, n);
|
|
427
|
+
} else {
|
|
428
|
+
ca_composite_fill_complement(d, dstride, counts,
|
|
429
|
+
alias_output_offset, alias_count,
|
|
430
|
+
w->fill, w->bytes, ndim);
|
|
431
|
+
/* parent_strides = row-major byte stride over parent->dim */
|
|
432
|
+
s = parent->bytes;
|
|
433
|
+
for (k = ndim - 1; k >= 0; k--) { parent_strides[k] = s; s *= parent->dim[k]; }
|
|
434
|
+
ca_composite_region_gather(parent->ptr, parent_strides,
|
|
435
|
+
alias_parent_start,
|
|
436
|
+
d, dstride, alias_output_offset,
|
|
437
|
+
alias_count, ndim, w->bytes);
|
|
438
|
+
}
|
|
439
|
+
} else { /* CA_XFER_PUT */
|
|
440
|
+
/* complement silent skip (no parent storage for fill region) */
|
|
441
|
+
if (!any_empty) {
|
|
442
|
+
s = parent->bytes;
|
|
443
|
+
for (k = ndim - 1; k >= 0; k--) { parent_strides[k] = s; s *= parent->dim[k]; }
|
|
444
|
+
ca_composite_region_scatter(parent->ptr, parent_strides,
|
|
445
|
+
alias_parent_start,
|
|
446
|
+
d, dstride, alias_output_offset,
|
|
447
|
+
alias_count, ndim, w->bytes);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/* Parent unattached: per-row outer loop + ca_xfer_stride(parent)
|
|
454
|
+
recursion. */
|
|
455
|
+
if (dir == CA_XFER_GET) {
|
|
456
|
+
for (i = 0; i < n; i++) memcpy(d + i * w->bytes, w->fill, w->bytes);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
for (k = 0; k < ndim; k++) {
|
|
460
|
+
if (lo[k] >= hi[k]) return; /* no in-bound cells (GET: filled; PUT: skip) */
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
for (k = 0; k < ndim; k++) o[k] = lo[k];
|
|
464
|
+
while (1) {
|
|
465
|
+
ca_size_t pbase = 0, doff = 0;
|
|
466
|
+
ca_size_t pstarts[CA_RANK_MAX], pcounts[CA_RANK_MAX], pstrides[CA_RANK_MAX];
|
|
467
|
+
ca_size_t inner_pbase;
|
|
468
|
+
for (k = 0; k < inner; k++) {
|
|
469
|
+
pbase += (base_pos[k] + o[k]) * pnative[k];
|
|
470
|
+
doff += o[k] * dstride[k];
|
|
471
|
+
}
|
|
472
|
+
inner_pbase = (base_pos[inner] + lo[inner]) * pnative[inner];
|
|
473
|
+
ca_addr2index((CArray *) parent, (pbase + inner_pbase) / parent->bytes, pstarts);
|
|
474
|
+
for (k = 0; k < ndim; k++) { pcounts[k] = 1; pstrides[k] = 0; }
|
|
475
|
+
pcounts[inner] = hi[inner] - lo[inner];
|
|
476
|
+
pstrides[inner] = pnative[inner];
|
|
477
|
+
ca_xfer_stride(parent, pstarts, pcounts, pstrides,
|
|
478
|
+
d + doff + lo[inner] * dstride[inner], dir);
|
|
479
|
+
|
|
480
|
+
k = inner - 1;
|
|
481
|
+
while (k >= 0) { o[k]++; if (o[k] < hi[k]) break; o[k] = lo[k]; k--; }
|
|
482
|
+
if (k < 0) break;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/* Executes the "1 typed fill + 1 strided memcpy" embed path on a
|
|
487
|
+
pre-allocated output buffer. Requires the parent to be attached (the
|
|
488
|
+
caller's responsibility).
|
|
489
|
+
|
|
490
|
+
Pre-conditions (must hold; not validated here):
|
|
491
|
+
- ca->embed_eligible == 1 (all axes FILL or MASK)
|
|
492
|
+
- ca->parent->ptr != NULL (parent attached)
|
|
493
|
+
- out_ptr points to ca->elements * ca->bytes writable bytes
|
|
494
|
+
|
|
495
|
+
Behaviour:
|
|
496
|
+
- if embed_covers_all: skip fill (alias overwrites every cell)
|
|
497
|
+
- else: fill the entire output with ca->fill (typed loop)
|
|
498
|
+
- if embed_is_empty: skip alias copy
|
|
499
|
+
- else: strided memcpy from parent[embed_parent_*] rectangle to
|
|
500
|
+
out_ptr[embed_output_*] rectangle. Inner contig run =
|
|
501
|
+
embed_count[ndim-1] * bytes. Outer axes iterate row-major
|
|
502
|
+
via a multi-dim cursor; per-axis offsets are precomputed in
|
|
503
|
+
bytes for both parent and output.
|
|
504
|
+
|
|
505
|
+
The strided copy itself is ca_composite_region_gather
|
|
506
|
+
(ca_composite_dispatch.c), shared with CATile / CAStack. */
|
|
507
|
+
static void
|
|
508
|
+
ca_window_attach_embed (CAWindow *ca, char *out_ptr)
|
|
509
|
+
{
|
|
510
|
+
int8_t ndim = ca->ndim;
|
|
511
|
+
ca_size_t bytes = ca->bytes;
|
|
512
|
+
ca_size_t parent_strides[CA_RANK_MAX];
|
|
513
|
+
ca_size_t output_strides[CA_RANK_MAX];
|
|
514
|
+
ca_size_t s;
|
|
515
|
+
int8_t k;
|
|
516
|
+
|
|
517
|
+
/* Row-major byte strides for output (used by fill-complement and
|
|
518
|
+
region-gather both). */
|
|
519
|
+
s = bytes;
|
|
520
|
+
for ( k = ndim - 1; k >= 0; k-- ) {
|
|
521
|
+
output_strides[k] = s;
|
|
522
|
+
s *= ca->count[k];
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/* Step 1: fill the complement of the alias rectangle only — filling the
|
|
526
|
+
whole output first would write the alias area twice. For
|
|
527
|
+
embed_is_empty (= alias is empty, whole output is fill) short-circuit
|
|
528
|
+
to a full-output typed fill since the complement is the whole output;
|
|
529
|
+
for embed_covers_all (= alias is the whole output, no fill) skip. */
|
|
530
|
+
if ( ! ca->embed_covers_all ) {
|
|
531
|
+
if ( ca->embed_is_empty ) {
|
|
532
|
+
ca_fill_typed(out_ptr, ca->fill, bytes, ca->elements);
|
|
533
|
+
} else {
|
|
534
|
+
ca_composite_fill_complement(out_ptr, output_strides, ca->count,
|
|
535
|
+
ca->embed_output_offset,
|
|
536
|
+
ca->embed_count,
|
|
537
|
+
ca->fill, bytes, ndim);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/* Step 2: strided memcpy from parent alias rectangle to output rectangle. */
|
|
542
|
+
if ( ca->embed_is_empty ) return;
|
|
543
|
+
|
|
544
|
+
s = bytes;
|
|
545
|
+
for ( k = ndim - 1; k >= 0; k-- ) {
|
|
546
|
+
parent_strides[k] = s;
|
|
547
|
+
s *= ca->parent->dim[k];
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
ca_composite_region_gather(ca->parent->ptr, parent_strides,
|
|
551
|
+
ca->embed_parent_start,
|
|
552
|
+
out_ptr, output_strides,
|
|
553
|
+
ca->embed_output_offset,
|
|
554
|
+
ca->embed_count, ndim, bytes);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/* Executes the reverse of ca_window_attach_embed on a caller-provided source
|
|
558
|
+
buffer. Strided memcpy from the alias sub-rectangle of in_ptr (= the
|
|
559
|
+
view's data layout) back into the parent's alias rectangle. The fill
|
|
560
|
+
region is ignored: writes in the OOB area have no parent cell to land in
|
|
561
|
+
and are dropped, matching ca_axis_dispatch_scatter's SHIFT-kind OOB.
|
|
562
|
+
|
|
563
|
+
Pre-conditions:
|
|
564
|
+
- ca->embed_eligible == 1
|
|
565
|
+
- ca->parent->ptr != NULL (parent attached)
|
|
566
|
+
- in_ptr points to ca->elements * ca->bytes valid bytes
|
|
567
|
+
|
|
568
|
+
The reverse strided copy is ca_composite_region_scatter
|
|
569
|
+
(ca_composite_dispatch.c), shared with the CATile / CAStack sync paths. */
|
|
570
|
+
static void
|
|
571
|
+
ca_window_sync_embed (CAWindow *ca, char *in_ptr)
|
|
572
|
+
{
|
|
573
|
+
int8_t ndim = ca->ndim;
|
|
574
|
+
ca_size_t bytes = ca->bytes;
|
|
575
|
+
ca_size_t parent_strides[CA_RANK_MAX];
|
|
576
|
+
ca_size_t input_strides[CA_RANK_MAX];
|
|
577
|
+
ca_size_t s;
|
|
578
|
+
int8_t k;
|
|
579
|
+
|
|
580
|
+
if ( ca->embed_is_empty ) return; /* nothing of in_ptr maps back */
|
|
581
|
+
|
|
582
|
+
/* Row-major byte strides for parent and the view-shaped source. */
|
|
583
|
+
s = bytes;
|
|
584
|
+
for ( k = ndim - 1; k >= 0; k-- ) {
|
|
585
|
+
parent_strides[k] = s;
|
|
586
|
+
s *= ca->parent->dim[k];
|
|
587
|
+
}
|
|
588
|
+
s = bytes;
|
|
589
|
+
for ( k = ndim - 1; k >= 0; k-- ) {
|
|
590
|
+
input_strides[k] = s;
|
|
591
|
+
s *= ca->count[k];
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
ca_composite_region_scatter(ca->parent->ptr, parent_strides,
|
|
595
|
+
ca->embed_parent_start,
|
|
596
|
+
in_ptr, input_strides,
|
|
597
|
+
ca->embed_output_offset,
|
|
598
|
+
ca->embed_count, ndim, bytes);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/* ------------------------------------------------------------------- */
|
|
602
|
+
|
|
603
|
+
/* Recomputes the embed descriptor + eligibility flags from the current
|
|
604
|
+
(start, count, bounds). The recompute is unconditional — no eligibility
|
|
605
|
+
pre-check — so callers never have to reason about which flags are stale.
|
|
606
|
+
Called by ca_window_setup.
|
|
607
|
+
*
|
|
608
|
+
* CAREFUL: any code path that mutates ca->start[] in place (e.g.
|
|
609
|
+
* ca_window_move, which is why this is not static) must call this
|
|
610
|
+
* afterwards. Otherwise embed_* keeps describing the OLD start and the
|
|
611
|
+
* embed-model attach / copy_data reads the wrong parent rectangle — wrong
|
|
612
|
+
* data, no error. */
|
|
613
|
+
void
|
|
614
|
+
ca_window_recompute_embed (CAWindow *ca)
|
|
615
|
+
{
|
|
616
|
+
int8_t ndim = ca->ndim;
|
|
617
|
+
int8_t i;
|
|
618
|
+
|
|
619
|
+
ca_compute_embed_descriptor(ndim, ca->parent->dim, ca->start, ca->count,
|
|
620
|
+
ca->embed_parent_start,
|
|
621
|
+
ca->embed_count,
|
|
622
|
+
ca->embed_output_offset,
|
|
623
|
+
&ca->embed_is_empty,
|
|
624
|
+
&ca->embed_covers_all);
|
|
625
|
+
|
|
626
|
+
/* embed path eligibility = all axes use FILL or MASK bounds. */
|
|
627
|
+
ca->embed_eligible = 1;
|
|
628
|
+
for (i = 0; i < ndim; i++) {
|
|
629
|
+
if ( ca->bounds[i] != CA_BOUNDS_FILL
|
|
630
|
+
&& ca->bounds[i] != CA_BOUNDS_MASK ) {
|
|
631
|
+
ca->embed_eligible = 0;
|
|
632
|
+
break;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/* direct-attach alias eligibility (see ca_window_func_attach). */
|
|
637
|
+
ca->embed_alias_eligible = 0;
|
|
638
|
+
if ( ca->embed_eligible && ca->embed_covers_all ) {
|
|
639
|
+
int alias_ok = 1;
|
|
640
|
+
for (i = 1; i < ndim; i++) {
|
|
641
|
+
if ( ca->start[i] != 0 || ca->count[i] != ca->parent->dim[i] ) {
|
|
642
|
+
alias_ok = 0;
|
|
643
|
+
break;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
if ( alias_ok ) ca->embed_alias_eligible = 1;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/* `bounds` is a per-axis uint8_t array. CAWindow's Ruby surface
|
|
651
|
+
(rb_ca_window) receives a single scalar policy and fans it out to all axes;
|
|
652
|
+
the per-axis form is what lets CAShift be a CAWindow specialisation. */
|
|
53
653
|
int
|
|
54
654
|
ca_window_setup (CAWindow *ca, CArray *parent,
|
|
55
|
-
ca_size_t *start, ca_size_t *count,
|
|
655
|
+
ca_size_t *start, ca_size_t *count, uint8_t *bounds, char *fill)
|
|
56
656
|
{
|
|
57
657
|
int8_t data_type, ndim;
|
|
58
658
|
ca_size_t *dim;
|
|
59
659
|
ca_size_t bytes, elements;
|
|
60
660
|
int i;
|
|
661
|
+
int any_mask;
|
|
61
662
|
|
|
62
663
|
data_type = parent->data_type;
|
|
63
664
|
ndim = parent->ndim;
|
|
@@ -86,18 +687,34 @@ ca_window_setup (CAWindow *ca, CArray *parent,
|
|
|
86
687
|
ca->parent = parent;
|
|
87
688
|
ca->attach = 0;
|
|
88
689
|
ca->nosync = 0;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
690
|
+
/* Pool path: bounds/start/count/size0/embed_* are already wired into
|
|
691
|
+
ca->_pool by ca_window_pool_init. Legacy path: ALLOC_N each. `fill`
|
|
692
|
+
is bytes-sized (not ndim) so it stays on its own ALLOC_N in both
|
|
693
|
+
branches. */
|
|
694
|
+
if ( ! ca->_pool ) {
|
|
695
|
+
ca->bounds = ALLOC_N(uint8_t, ndim);
|
|
696
|
+
ca->start = ALLOC_N(ca_size_t, ndim);
|
|
697
|
+
ca->count = ALLOC_N(ca_size_t, ndim);
|
|
698
|
+
ca->size0 = ALLOC_N(ca_size_t, ndim);
|
|
699
|
+
ca->embed_parent_start = ALLOC_N(ca_size_t, ndim);
|
|
700
|
+
ca->embed_count = ALLOC_N(ca_size_t, ndim);
|
|
701
|
+
ca->embed_output_offset = ALLOC_N(ca_size_t, ndim);
|
|
702
|
+
}
|
|
93
703
|
ca->fill = ALLOC_N(char, ca->bytes);
|
|
704
|
+
ca->embed_is_empty = 0;
|
|
705
|
+
ca->embed_covers_all = 0;
|
|
94
706
|
|
|
95
707
|
ca->dim = ca->count;
|
|
96
708
|
|
|
709
|
+
memcpy(ca->bounds, bounds, ndim * sizeof(uint8_t));
|
|
97
710
|
memcpy(ca->start, start, ndim * sizeof(ca_size_t));
|
|
98
711
|
memcpy(ca->count, count, ndim * sizeof(ca_size_t));
|
|
99
712
|
memcpy(ca->size0, dim, ndim * sizeof(ca_size_t));
|
|
100
713
|
|
|
714
|
+
/* Compute the embed descriptor + eligibility flags from the current
|
|
715
|
+
(start, count, bounds). See ca_window_recompute_embed below. */
|
|
716
|
+
ca_window_recompute_embed(ca);
|
|
717
|
+
|
|
101
718
|
if ( fill ) {
|
|
102
719
|
memcpy(ca->fill, fill, ca->bytes);
|
|
103
720
|
}
|
|
@@ -110,7 +727,15 @@ ca_window_setup (CAWindow *ca, CArray *parent,
|
|
|
110
727
|
}
|
|
111
728
|
}
|
|
112
729
|
|
|
113
|
-
if
|
|
730
|
+
/* Mask is needed if ANY axis uses MASK policy (per-axis). */
|
|
731
|
+
any_mask = 0;
|
|
732
|
+
for (i=0; i<ndim; i++) {
|
|
733
|
+
if ( ca->bounds[i] == CA_BOUNDS_MASK ) {
|
|
734
|
+
any_mask = 1;
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
if ( any_mask ) {
|
|
114
739
|
ca_create_mask(ca);
|
|
115
740
|
}
|
|
116
741
|
|
|
@@ -119,9 +744,9 @@ ca_window_setup (CAWindow *ca, CArray *parent,
|
|
|
119
744
|
|
|
120
745
|
CAWindow *
|
|
121
746
|
ca_window_new (CArray *parent,
|
|
122
|
-
ca_size_t *start, ca_size_t *count,
|
|
747
|
+
ca_size_t *start, ca_size_t *count, uint8_t *bounds, char *fill)
|
|
123
748
|
{
|
|
124
|
-
CAWindow *ca =
|
|
749
|
+
CAWindow *ca = (CAWindow *) ca_array_alloc(CA_OBJ_WINDOW, parent->ndim);
|
|
125
750
|
ca_window_setup(ca, parent, start, count, bounds, fill);
|
|
126
751
|
return ca;
|
|
127
752
|
}
|
|
@@ -132,18 +757,38 @@ free_ca_window (void *ap)
|
|
|
132
757
|
CAWindow *ca = (CAWindow *) ap;
|
|
133
758
|
if ( ca != NULL ) {
|
|
134
759
|
ca_free(ca->mask);
|
|
135
|
-
xfree(ca->fill);
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
760
|
+
xfree(ca->fill); /* always separate (bytes-sized) */
|
|
761
|
+
if ( ca->_pool ) {
|
|
762
|
+
/* bounds/start/count/size0/embed_* all live in ca->_pool. */
|
|
763
|
+
ca_array_free(ca); /* one xfree pool + one xfree struct */
|
|
764
|
+
}
|
|
765
|
+
else {
|
|
766
|
+
xfree(ca->bounds);
|
|
767
|
+
xfree(ca->start);
|
|
768
|
+
xfree(ca->count);
|
|
769
|
+
xfree(ca->size0);
|
|
770
|
+
xfree(ca->embed_parent_start);
|
|
771
|
+
xfree(ca->embed_count);
|
|
772
|
+
xfree(ca->embed_output_offset);
|
|
773
|
+
/* xfree(ca->dim); */
|
|
774
|
+
xfree(ca);
|
|
775
|
+
}
|
|
141
776
|
}
|
|
142
777
|
}
|
|
143
778
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
779
|
+
/* Path selection for attach / sync / copy_data / sync_data below.
|
|
780
|
+
|
|
781
|
+
embed_eligible (= all axes use FILL or MASK bounds) takes the embed path:
|
|
782
|
+
"1 typed fill + 1 strided memcpy" with no per-cell bound check (see
|
|
783
|
+
ca_window_attach_embed / ca_window_sync_embed above).
|
|
784
|
+
|
|
785
|
+
Everything else (PERIODIC — still reached through CAShift's roll form —
|
|
786
|
+
REFLECT / NEAREST / RUBY / STRICT) goes through the descriptor engine,
|
|
787
|
+
ca_axis_dispatch_* fed by ca_window_describe_axes (defined below), which
|
|
788
|
+
still promotes interior axes to STRIDE kind.
|
|
789
|
+
|
|
790
|
+
fill_data is the exception: it has its own split (see
|
|
791
|
+
ca_window_func_fill_data). */
|
|
147
792
|
|
|
148
793
|
/* ------------------------------------------------------------------- */
|
|
149
794
|
|
|
@@ -154,57 +799,11 @@ ca_window_func_clone (void *ap)
|
|
|
154
799
|
return ca_window_new(ca->parent, ca->start, ca->count, ca->bounds, ca->fill);
|
|
155
800
|
}
|
|
156
801
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
static char *
|
|
161
|
-
ca_window_func_ptr_at_addr (void *ap, ca_size_t addr)
|
|
162
|
-
{
|
|
163
|
-
CAWindow *ca = (CAWindow *) ap;
|
|
164
|
-
if ( ca->ptr ) {
|
|
165
|
-
return ca->ptr + ca->bytes * addr;
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
ca_size_t idx[CA_RANK_MAX];
|
|
169
|
-
ca_addr2index((CArray *)ca, addr, idx);
|
|
170
|
-
return ca_window_func_ptr_at_index(ca, idx);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
static char *
|
|
175
|
-
ca_window_func_ptr_at_index (void *ap, ca_size_t *idx)
|
|
176
|
-
{
|
|
177
|
-
ca_size_t k;
|
|
178
|
-
CAWindow *ca = (CAWindow *) ap;
|
|
179
|
-
if ( ! ca->ptr ) {
|
|
180
|
-
ca_size_t *start = ca->start;
|
|
181
|
-
ca_size_t *size0 = ca->size0;
|
|
182
|
-
int8_t i;
|
|
183
|
-
ca_size_t n;
|
|
184
|
-
n = 0;
|
|
185
|
-
for (i=0; i<ca->ndim; i++) {
|
|
186
|
-
k = start[i] + idx[i];
|
|
187
|
-
k = ca_bounds_normalize_index(ca->bounds, size0[i], k);
|
|
188
|
-
if ( k < 0 || k >= size0[i] ) {
|
|
189
|
-
return ca->fill;
|
|
190
|
-
}
|
|
191
|
-
n = size0[i] * n + k;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if ( ! ca->parent->ptr ) {
|
|
195
|
-
return ca_ptr_at_addr(ca->parent, n);
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
return ca->parent->ptr + ca->bytes * n;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
else {
|
|
202
|
-
return ca_func[CA_OBJ_ARRAY].ptr_at_index(ca, idx);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
802
|
+
/* Per-cell access. GET and PUT share the bound-normalised index walk; an OOB
|
|
803
|
+
cell is filled on GET and skipped on PUT. CAShift inherits this via the
|
|
804
|
+
ca_shift_func copy of the operation table. */
|
|
206
805
|
static void
|
|
207
|
-
|
|
806
|
+
ca_window_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
|
|
208
807
|
{
|
|
209
808
|
CAWindow *ca = (CAWindow *) ap;
|
|
210
809
|
ca_size_t *start = ca->start;
|
|
@@ -214,34 +813,143 @@ ca_window_func_fetch_index (void *ap, ca_size_t *idx, void *ptr)
|
|
|
214
813
|
ca_size_t k;
|
|
215
814
|
for (i=0; i<ca->ndim; i++) {
|
|
216
815
|
k = start[i] + idx[i];
|
|
217
|
-
k = ca_bounds_normalize_index(ca->bounds, size0[i], k);
|
|
816
|
+
k = ca_bounds_normalize_index(ca->bounds[i], size0[i], k);
|
|
218
817
|
if ( k < 0 || k >= size0[i] ) {
|
|
219
|
-
memcpy(
|
|
220
|
-
return;
|
|
818
|
+
if ( dir == CA_XFER_GET ) memcpy(data, ca->fill, ca->bytes);
|
|
819
|
+
return; /* PUT to out-of-bounds cell: skip */
|
|
221
820
|
}
|
|
222
821
|
idx0[i] = k;
|
|
223
822
|
}
|
|
224
|
-
|
|
823
|
+
ca_xfer_index(ca->parent, idx0, data, dir);
|
|
225
824
|
}
|
|
226
825
|
|
|
826
|
+
/* Batched address gather/scatter. Bound-normalises each view addr; OOB cells are handled inline (GET fills,
|
|
827
|
+
PUT skips) and the in-bounds cells are delivered to the parent in ONE
|
|
828
|
+
ca_xfer_addrs call. When some cells are OOB the in-bounds set is packed
|
|
829
|
+
into a contiguous temp (the parent's contig-buf contract), gathered/
|
|
830
|
+
scattered, then unpacked. No whole-view attach.
|
|
831
|
+
CAShift inherits via ca_shift_func copy. */
|
|
227
832
|
static void
|
|
228
|
-
|
|
833
|
+
ca_window_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
|
|
834
|
+
void *data, int dir)
|
|
229
835
|
{
|
|
230
836
|
CAWindow *ca = (CAWindow *) ap;
|
|
231
837
|
ca_size_t *start = ca->start;
|
|
232
838
|
ca_size_t *size0 = ca->size0;
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
ca_size_t
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
839
|
+
char *d = (char *) data;
|
|
840
|
+
ca_size_t *paddrs;
|
|
841
|
+
ca_size_t *pos;
|
|
842
|
+
ca_size_t m = 0, i, base;
|
|
843
|
+
int8_t k;
|
|
844
|
+
volatile VALUE h1, h2;
|
|
845
|
+
|
|
846
|
+
/* Fast path: an embed-eligible window (interior alias rectangle + OOB
|
|
847
|
+
strips for FILL mode) over a parent that resolves to a ptr-bearing root.
|
|
848
|
+
Whole-view sequential addrs let us drive ca_composite_region_gather /
|
|
849
|
+
ca_composite_fill_complement directly (= the same helpers
|
|
850
|
+
ca_window_attach_embed / _sync_embed use), skipping the per-cell
|
|
851
|
+
bounds-normalise + OOB-pack two-pass. Covers both interior-only windows
|
|
852
|
+
and boundary-crossing ones (CAShift included): the OOB strip fill is
|
|
853
|
+
batched through ca_composite_fill_complement. */
|
|
854
|
+
if ( ca->embed_eligible
|
|
855
|
+
&& n == ca->elements
|
|
856
|
+
&& ca_xfer_addrs_is_sequential_run(n, addrs, &base) && base == 0 ) {
|
|
857
|
+
CArray *eff_parent = ca_resolve_attached_root_via_identity(ca->parent);
|
|
858
|
+
if ( eff_parent->ptr ) {
|
|
859
|
+
ca_size_t output_strides[CA_RANK_MAX];
|
|
860
|
+
ca_size_t parent_strides[CA_RANK_MAX];
|
|
861
|
+
ca_size_t s;
|
|
862
|
+
s = ca->bytes;
|
|
863
|
+
for ( k = ca->ndim - 1; k >= 0; k-- ) {
|
|
864
|
+
output_strides[k] = s;
|
|
865
|
+
s *= ca->count[k];
|
|
866
|
+
}
|
|
867
|
+
s = eff_parent->bytes;
|
|
868
|
+
for ( k = ca->ndim - 1; k >= 0; k-- ) {
|
|
869
|
+
parent_strides[k] = s;
|
|
870
|
+
s *= ca->parent->dim[k]; /* logical shape from immediate parent */
|
|
871
|
+
}
|
|
872
|
+
if ( dir == CA_XFER_GET ) {
|
|
873
|
+
/* Step 1: fill OOB complement (no-op when covers_all). */
|
|
874
|
+
if ( ! ca->embed_covers_all ) {
|
|
875
|
+
if ( ca->embed_is_empty ) {
|
|
876
|
+
ca_fill_typed((char *) data, ca->fill, ca->bytes, ca->elements);
|
|
877
|
+
} else {
|
|
878
|
+
ca_composite_fill_complement((char *) data, output_strides, ca->count,
|
|
879
|
+
ca->embed_output_offset,
|
|
880
|
+
ca->embed_count,
|
|
881
|
+
ca->fill, ca->bytes, ca->ndim);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
/* Step 2: gather interior alias rectangle from parent. */
|
|
885
|
+
if ( ! ca->embed_is_empty ) {
|
|
886
|
+
ca_composite_region_gather(eff_parent->ptr, parent_strides,
|
|
887
|
+
ca->embed_parent_start,
|
|
888
|
+
(char *) data, output_strides,
|
|
889
|
+
ca->embed_output_offset,
|
|
890
|
+
ca->embed_count, ca->ndim, ca->bytes);
|
|
891
|
+
}
|
|
892
|
+
} else { /* CA_XFER_PUT */
|
|
893
|
+
/* Scatter the input rectangle back to the parent's alias
|
|
894
|
+
rectangle. Writes to OOB cells are dropped — same as
|
|
895
|
+
ca_window_sync_embed and the engine's SHIFT-kind scatter. */
|
|
896
|
+
if ( ! ca->embed_is_empty ) {
|
|
897
|
+
ca_composite_region_scatter(eff_parent->ptr, parent_strides,
|
|
898
|
+
ca->embed_parent_start,
|
|
899
|
+
(char *) data, output_strides,
|
|
900
|
+
ca->embed_output_offset,
|
|
901
|
+
ca->embed_count, ca->ndim, ca->bytes);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
240
904
|
return;
|
|
241
905
|
}
|
|
242
|
-
idx0[i] = k;
|
|
243
906
|
}
|
|
244
|
-
|
|
907
|
+
|
|
908
|
+
paddrs = ALLOCV_N(ca_size_t, h1, n);
|
|
909
|
+
pos = ALLOCV_N(ca_size_t, h2, n);
|
|
910
|
+
|
|
911
|
+
for (i = 0; i < n; i++) {
|
|
912
|
+
ca_size_t vidx[CA_RANK_MAX], pidx[CA_RANK_MAX];
|
|
913
|
+
int oob = 0;
|
|
914
|
+
ca_addr2index((CArray *) ca, addrs[i], vidx);
|
|
915
|
+
for (k = 0; k < ca->ndim; k++) {
|
|
916
|
+
ca_size_t kk = start[k] + vidx[k];
|
|
917
|
+
kk = ca_bounds_normalize_index(ca->bounds[k], size0[k], kk);
|
|
918
|
+
if (kk < 0 || kk >= size0[k]) { oob = 1; break; }
|
|
919
|
+
pidx[k] = kk;
|
|
920
|
+
}
|
|
921
|
+
if (oob) {
|
|
922
|
+
if (dir == CA_XFER_GET) memcpy(d + i * ca->bytes, ca->fill, ca->bytes);
|
|
923
|
+
/* PUT to OOB cell: skip */
|
|
924
|
+
}
|
|
925
|
+
else {
|
|
926
|
+
paddrs[m] = ca_index2addr(ca->parent, pidx);
|
|
927
|
+
pos[m] = i;
|
|
928
|
+
m++;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
if (m == n) { /* no OOB: deliver in place, contiguous */
|
|
933
|
+
ca_xfer_addrs(ca->parent, n, paddrs, data, dir);
|
|
934
|
+
}
|
|
935
|
+
else if (m > 0) { /* some OOB: pack the in-bounds set */
|
|
936
|
+
volatile VALUE h3;
|
|
937
|
+
char *packed = ALLOCV_N(char, h3, m * ca->bytes);
|
|
938
|
+
if (dir == CA_XFER_GET) {
|
|
939
|
+
ca_xfer_addrs(ca->parent, m, paddrs, packed, CA_XFER_GET);
|
|
940
|
+
for (i = 0; i < m; i++)
|
|
941
|
+
memcpy(d + pos[i] * ca->bytes, packed + i * ca->bytes, ca->bytes);
|
|
942
|
+
}
|
|
943
|
+
else {
|
|
944
|
+
for (i = 0; i < m; i++)
|
|
945
|
+
memcpy(packed + i * ca->bytes, d + pos[i] * ca->bytes, ca->bytes);
|
|
946
|
+
ca_xfer_addrs(ca->parent, m, paddrs, packed, CA_XFER_PUT);
|
|
947
|
+
}
|
|
948
|
+
ALLOCV_END(h3);
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
ALLOCV_END(h2);
|
|
952
|
+
ALLOCV_END(h1);
|
|
245
953
|
}
|
|
246
954
|
|
|
247
955
|
static void
|
|
@@ -250,26 +958,66 @@ ca_window_func_allocate (void *ap)
|
|
|
250
958
|
CAWindow *ca = (CAWindow *) ap;
|
|
251
959
|
ca_attach(ca->parent);
|
|
252
960
|
/* ca->ptr = ALLOC_N(char, ca_length(ca)); */
|
|
253
|
-
ca->ptr =
|
|
961
|
+
ca->ptr = xmalloc(ca_length(ca));
|
|
254
962
|
}
|
|
255
963
|
|
|
964
|
+
/* The engine paths below emit a per-axis descriptor (ca_window_describe_axes)
|
|
965
|
+
and let the shared engine's SHIFT-kind handling apply the boundary policy
|
|
966
|
+
and the OOB cell fill. CAShift inherits all of it through
|
|
967
|
+
ca_shift_func = a copy of ca_window_func. */
|
|
968
|
+
|
|
969
|
+
void ca_window_describe_axes (void *ap, ca_axis_desc_t *out,
|
|
970
|
+
ca_size_t *out_parent_dims);
|
|
971
|
+
|
|
256
972
|
static void
|
|
257
973
|
ca_window_func_attach (void *ap)
|
|
258
974
|
{
|
|
259
|
-
void ca_window_attach (CAWindow *cb);
|
|
260
|
-
|
|
261
975
|
CAWindow *ca = (CAWindow *) ap;
|
|
262
976
|
ca_attach(ca->parent);
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
977
|
+
if ( ca->embed_alias_eligible ) {
|
|
978
|
+
/* Alias path: inner axes full + interior, so the embedded region is a
|
|
979
|
+
contiguous run of parent storage. Skip malloc/memcpy and point
|
|
980
|
+
ca->ptr into the parent's buffer.
|
|
981
|
+
CAREFUL: sync and detach must agree with this — sync has nothing to
|
|
982
|
+
scatter back (the writes already landed in the parent) and detach must
|
|
983
|
+
not xfree a pointer it does not own. */
|
|
984
|
+
ca_size_t parent_row_stride = ca->bytes;
|
|
985
|
+
int8_t k;
|
|
986
|
+
for (k = ca->ndim - 1; k >= 1; k--) parent_row_stride *= ca->parent->dim[k];
|
|
987
|
+
ca->ptr = ca->parent->ptr + ca->start[0] * parent_row_stride;
|
|
988
|
+
} else if ( ca->embed_eligible ) {
|
|
989
|
+
/* Embed path: allocate, then 1 fill + 1 strided memcpy. */
|
|
990
|
+
ca_size_t out_len = ca->elements * ca->bytes;
|
|
991
|
+
ca->ptr = xmalloc(out_len > 0 ? out_len : 1);
|
|
992
|
+
ca_window_attach_embed(ca, ca->ptr);
|
|
993
|
+
} else {
|
|
994
|
+
/* Fallback: PERIODIC / REFLECT / NEAREST / RUBY / STRICT go through
|
|
995
|
+
the descriptor engine. */
|
|
996
|
+
ca_axis_desc_t desc[CA_RANK_MAX];
|
|
997
|
+
ca_size_t pdims[CA_RANK_MAX];
|
|
998
|
+
ca_window_describe_axes(ca, desc, pdims);
|
|
999
|
+
ca->ptr = ca_axis_dispatch_attach(ca->parent, pdims, desc, ca->ndim,
|
|
1000
|
+
ca->bytes, ca->elements, ca->fill);
|
|
1001
|
+
}
|
|
266
1002
|
}
|
|
267
1003
|
|
|
268
1004
|
static void
|
|
269
1005
|
ca_window_func_sync (void *ap)
|
|
270
1006
|
{
|
|
271
1007
|
CAWindow *ca = (CAWindow *) ap;
|
|
272
|
-
|
|
1008
|
+
if ( ca->embed_alias_eligible ) {
|
|
1009
|
+
/* Alias path: ca->ptr aliases the parent, so the writes already landed
|
|
1010
|
+
in parent storage. Nothing to scatter back. */
|
|
1011
|
+
} else if ( ca->embed_eligible ) {
|
|
1012
|
+
/* Embed path: write back the alias region only. */
|
|
1013
|
+
ca_window_sync_embed(ca, ca->ptr);
|
|
1014
|
+
} else {
|
|
1015
|
+
ca_axis_desc_t desc[CA_RANK_MAX];
|
|
1016
|
+
ca_size_t pdims[CA_RANK_MAX];
|
|
1017
|
+
ca_window_describe_axes(ca, desc, pdims);
|
|
1018
|
+
ca_axis_dispatch_scatter(ca->parent, pdims, desc, ca->ndim, ca->bytes,
|
|
1019
|
+
ca->elements, ca->ptr);
|
|
1020
|
+
}
|
|
273
1021
|
ca_sync(ca->parent);
|
|
274
1022
|
}
|
|
275
1023
|
|
|
@@ -277,42 +1025,219 @@ static void
|
|
|
277
1025
|
ca_window_func_detach (void *ap)
|
|
278
1026
|
{
|
|
279
1027
|
CAWindow *ca = (CAWindow *) ap;
|
|
280
|
-
|
|
1028
|
+
/* Alias path: ca->ptr aliases parent storage; it is not ours to xfree. */
|
|
1029
|
+
if ( ! ca->embed_alias_eligible ) {
|
|
1030
|
+
xfree(ca->ptr);
|
|
1031
|
+
}
|
|
281
1032
|
ca->ptr = NULL;
|
|
282
1033
|
ca_detach(ca->parent);
|
|
283
1034
|
}
|
|
284
1035
|
|
|
1036
|
+
/* Fast path body for xfer_all, shared by the warm and the cold-parent case.
|
|
1037
|
+
Both the embed path and the engine dispatch read ca->parent->ptr directly,
|
|
1038
|
+
so the caller must have made it available.
|
|
1039
|
+
|
|
1040
|
+
CAREFUL: neither this nor ca_window_func_xfer_all may call
|
|
1041
|
+
ca_attach(parent) on a parent that would have to materialise to answer.
|
|
1042
|
+
A transfer slot that silently does that duplicates the whole parent
|
|
1043
|
+
behind the caller's back — the cold case below instead materialises a
|
|
1044
|
+
parent-shaped scratch through ca_xfer_all, which recurses under the
|
|
1045
|
+
same rule.
|
|
1046
|
+
|
|
1047
|
+
The exception is a parent that ca_attach_is_alias accepts: there the
|
|
1048
|
+
attach hands back parent (or root) memory that already exists, so
|
|
1049
|
+
nothing is allocated or copied and the rule has nothing to protect.
|
|
1050
|
+
ca_window_func_fill_data draws the same line. */
|
|
285
1051
|
static void
|
|
286
|
-
|
|
1052
|
+
ca_window_func_run_fast_path (CAWindow *ca, char *data, int dir)
|
|
287
1053
|
{
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
1054
|
+
if ( dir == CA_XFER_GET ) {
|
|
1055
|
+
if ( ca->embed_eligible ) {
|
|
1056
|
+
ca_window_attach_embed(ca, data);
|
|
1057
|
+
} else {
|
|
1058
|
+
ca_axis_desc_t desc[CA_RANK_MAX];
|
|
1059
|
+
ca_size_t pdims[CA_RANK_MAX];
|
|
1060
|
+
ca_window_describe_axes(ca, desc, pdims);
|
|
1061
|
+
ca_axis_dispatch_gather(ca->parent, pdims, desc, ca->ndim, ca->bytes,
|
|
1062
|
+
ca->elements, ca->fill, data);
|
|
1063
|
+
}
|
|
1064
|
+
} else {
|
|
1065
|
+
if ( ca->embed_eligible ) {
|
|
1066
|
+
ca_window_sync_embed(ca, data);
|
|
1067
|
+
} else {
|
|
1068
|
+
ca_axis_desc_t desc[CA_RANK_MAX];
|
|
1069
|
+
ca_size_t pdims[CA_RANK_MAX];
|
|
1070
|
+
ca_window_describe_axes(ca, desc, pdims);
|
|
1071
|
+
ca_axis_dispatch_scatter(ca->parent, pdims, desc, ca->ndim, ca->bytes,
|
|
1072
|
+
ca->elements, data);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
295
1075
|
}
|
|
296
1076
|
|
|
297
1077
|
static void
|
|
298
|
-
|
|
1078
|
+
ca_window_func_xfer_all (void *ap, void *data, int dir)
|
|
299
1079
|
{
|
|
300
1080
|
CAWindow *ca = (CAWindow *) ap;
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
1081
|
+
if ( ca->parent->ptr ) {
|
|
1082
|
+
ca_window_func_run_fast_path(ca, (char *) data, dir);
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
/* Cold parent that has memory to lend (entity, or a CAStride-family view
|
|
1086
|
+
whose composed strides alias a ptr-bearing root): borrow it. The attach
|
|
1087
|
+
costs no allocation and no copy, and on the alias path ca->parent->ptr
|
|
1088
|
+
points into the root, so a PUT lands where the scratch path would have
|
|
1089
|
+
had to copy it back. Without this the window duplicates the entire
|
|
1090
|
+
parent on every transfer, which is what an ordinary `a[nil, nil]`,
|
|
1091
|
+
`a.refer` or `a.transpose.transpose` parent used to pay. */
|
|
1092
|
+
if ( ca_attach_is_alias(ca->parent) ) {
|
|
1093
|
+
ca_attach(ca->parent);
|
|
1094
|
+
ca_window_func_run_fast_path(ca, (char *) data, dir);
|
|
1095
|
+
if ( dir == CA_XFER_PUT ) {
|
|
1096
|
+
ca_sync(ca->parent);
|
|
1097
|
+
}
|
|
1098
|
+
ca_detach(ca->parent);
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
/* Second chance: ca_attach_is_alias reads the leaf view's own strides, so
|
|
1102
|
+
it declines a chain that is only contiguous once composed (a.transpose
|
|
1103
|
+
.transpose is the plain case). ca_resolve_attached_root_via_identity
|
|
1104
|
+
folds the chain and answers the same question about the root, and when
|
|
1105
|
+
it succeeds the parent's flat byte addressing IS the root's — so the
|
|
1106
|
+
root ptr can stand in for the parent's exactly as the scratch does
|
|
1107
|
+
below, with no copy in either direction. ca_window_func_xfer_addrs
|
|
1108
|
+
already resolves its parent this way. */
|
|
1109
|
+
{
|
|
1110
|
+
CArray *root = ca_resolve_attached_root_via_identity(ca->parent);
|
|
1111
|
+
if ( root != ca->parent && root->ptr ) {
|
|
1112
|
+
CArray *parent = ca->parent;
|
|
1113
|
+
parent->ptr = root->ptr;
|
|
1114
|
+
ca_window_func_run_fast_path(ca, (char *) data, dir);
|
|
1115
|
+
parent->ptr = NULL;
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
/* Cold parent with nothing to lend: materialise it into a scratch buffer
|
|
1121
|
+
via ca_xfer_all, then run the normal fast path with the scratch standing
|
|
1122
|
+
in for parent->ptr. */
|
|
1123
|
+
{
|
|
1124
|
+
volatile VALUE holder;
|
|
1125
|
+
CArray *parent = ca->parent;
|
|
1126
|
+
ca_size_t plen = parent->elements * parent->bytes;
|
|
1127
|
+
char *parent_scratch = ALLOCV_N(char, holder, plen);
|
|
1128
|
+
char *parent_ptr_saved = parent->ptr;
|
|
1129
|
+
|
|
1130
|
+
/* GET path needs parent data; PUT path will overwrite parent so we
|
|
1131
|
+
still need to read existing parent state if the view's fast path
|
|
1132
|
+
does partial writes (e.g., embed_sync overwrites only the embed
|
|
1133
|
+
rectangle, OOB cells untouched in parent). Safe default: always
|
|
1134
|
+
GET first. */
|
|
1135
|
+
ca_xfer_all(parent, parent_scratch, CA_XFER_GET);
|
|
1136
|
+
parent->ptr = parent_scratch;
|
|
1137
|
+
|
|
1138
|
+
ca_window_func_run_fast_path(ca, (char *) data, dir);
|
|
1139
|
+
|
|
1140
|
+
if ( dir == CA_XFER_PUT ) {
|
|
1141
|
+
/* Push back scratch (modified by scatter) to parent. */
|
|
1142
|
+
ca_xfer_all(parent, parent_scratch, CA_XFER_PUT);
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
parent->ptr = parent_ptr_saved;
|
|
1146
|
+
ALLOCV_END(holder);
|
|
1147
|
+
}
|
|
308
1148
|
}
|
|
309
1149
|
|
|
1150
|
+
/* fill_data (= view.fill(scalar) / view[] = scalar).
|
|
1151
|
+
|
|
1152
|
+
Wrapping the window in a CAStride and filling that is markedly faster than
|
|
1153
|
+
the engine path, because compose-fold reaches the entity and the inner loop
|
|
1154
|
+
collapses to a per-row memset. So for an embed-eligible window we do the
|
|
1155
|
+
same thing directly: build a transient CAStride header matching
|
|
1156
|
+
ca_window_compose_fold's synthetic strides and dispatch to CAStride's
|
|
1157
|
+
fill_data, which continues compose-fold (covering
|
|
1158
|
+
CAStride-of-CAWindow-of-CAStride chains) and runs the merge + tight-fill
|
|
1159
|
+
inner loop. PERIODIC / REFLECT windows fall through to the engine path. */
|
|
310
1160
|
static void
|
|
311
1161
|
ca_window_func_fill_data (void *ap, void *ptr)
|
|
312
1162
|
{
|
|
313
1163
|
CAWindow *ca = (CAWindow *) ap;
|
|
1164
|
+
|
|
1165
|
+
/* embed_eligible (= no PERIODIC/REFLECT) covers both interior-only
|
|
1166
|
+
windows (embed_covers_all == 1) and OOB-bearing ones such as a CAShift
|
|
1167
|
+
with a fill_value (embed_covers_all == 0, embed_is_empty == 0). In the
|
|
1168
|
+
OOB case the synth is restricted to the interior region (embed_count
|
|
1169
|
+
cells starting at embed_parent_start in the parent): the view's OOB
|
|
1170
|
+
cells have no parent cell and must be skipped, which the restricted
|
|
1171
|
+
synth does by construction. A wholly-OOB window (embed_is_empty == 1)
|
|
1172
|
+
is a no-op. */
|
|
1173
|
+
if ( ca->embed_eligible && ! ca->embed_is_empty ) {
|
|
1174
|
+
ca_size_t synth_strides[CA_RANK_MAX];
|
|
1175
|
+
ca_size_t synth_base;
|
|
1176
|
+
ca_size_t s;
|
|
1177
|
+
int8_t k;
|
|
1178
|
+
ca_size_t synth_elements;
|
|
1179
|
+
|
|
1180
|
+
/* Row-major byte strides over parent. */
|
|
1181
|
+
s = ca->bytes;
|
|
1182
|
+
for ( k = ca->ndim - 1; k >= 0; k-- ) {
|
|
1183
|
+
synth_strides[k] = s;
|
|
1184
|
+
s *= ca->parent->dim[k];
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
/* Origin: embedded region start in parent's byte space. */
|
|
1188
|
+
synth_base = 0;
|
|
1189
|
+
for ( k = 0; k < ca->ndim; k++ ) {
|
|
1190
|
+
synth_base += ca->embed_parent_start[k] * synth_strides[k];
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
/* synth dims = interior count per axis (= the cells we actually
|
|
1194
|
+
write to; OOB view cells are skipped by construction). */
|
|
1195
|
+
synth_elements = 1;
|
|
1196
|
+
for ( k = 0; k < ca->ndim; k++ ) {
|
|
1197
|
+
synth_elements *= ca->embed_count[k];
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
/* Stack-allocated transient CAStride header. Dispatched via the
|
|
1201
|
+
op table since ca_stride_func_fill_data is static in
|
|
1202
|
+
ca_obj_stride.c. It reads only struct fields (parent, ndim,
|
|
1203
|
+
dim, strides, base_offset, bytes, elements) and never
|
|
1204
|
+
registers / persists this pointer. */
|
|
1205
|
+
CAStride synth;
|
|
1206
|
+
memset(&synth, 0, sizeof(synth));
|
|
1207
|
+
synth.obj_type = CA_OBJ_STRIDE;
|
|
1208
|
+
synth.data_type = ca->data_type;
|
|
1209
|
+
synth.ndim = ca->ndim;
|
|
1210
|
+
synth.bytes = ca->bytes;
|
|
1211
|
+
synth.elements = synth_elements;
|
|
1212
|
+
synth.dim = ca->embed_count;
|
|
1213
|
+
synth.parent = ca->parent;
|
|
1214
|
+
synth.strides = synth_strides;
|
|
1215
|
+
synth.base_offset = synth_base;
|
|
1216
|
+
ca_func[CA_OBJ_STRIDE].fill_data(&synth, ptr);
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
/* Engine path: PERIODIC / REFLECT, or pure-OOB window (no-op via
|
|
1221
|
+
engine's OOB-skip). Bound_fill writes to view's OOB cells are
|
|
1222
|
+
not propagated to parent (no cells to write to). */
|
|
1223
|
+
ca_axis_desc_t desc[CA_RANK_MAX];
|
|
1224
|
+
ca_size_t pdims[CA_RANK_MAX];
|
|
1225
|
+
|
|
1226
|
+
ca_window_describe_axes(ca, desc, pdims);
|
|
1227
|
+
|
|
1228
|
+
/* A wrapping window writes the cells it lands on, but a whole-parent attach
|
|
1229
|
+
and sync carries the rest of the parent with it — and over a lossy layer
|
|
1230
|
+
those cells do not come back the same. Hand each slab to the parent as a
|
|
1231
|
+
region instead, as the other views on the descriptor engine do. */
|
|
1232
|
+
if ( !ca_is_attached(ca->parent) && !ca_attach_is_alias(ca->parent) ) {
|
|
1233
|
+
ca_axis_dispatch_fill_value_via_parent(ca->parent, pdims, desc, ca->ndim,
|
|
1234
|
+
ca->bytes, ca->elements, ptr);
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
314
1238
|
ca_attach(ca->parent);
|
|
315
|
-
|
|
1239
|
+
ca_axis_dispatch_fill_value(ca->parent, pdims, desc, ca->ndim, ca->bytes,
|
|
1240
|
+
ca->elements, ptr);
|
|
316
1241
|
ca_sync(ca->parent);
|
|
317
1242
|
ca_detach(ca->parent);
|
|
318
1243
|
}
|
|
@@ -322,392 +1247,97 @@ ca_window_func_create_mask (void *ap)
|
|
|
322
1247
|
{
|
|
323
1248
|
CAWindow *ca = (CAWindow *) ap;
|
|
324
1249
|
boolean8_t fill;
|
|
325
|
-
|
|
1250
|
+
uint8_t mbounds[CA_RANK_MAX];
|
|
1251
|
+
int8_t i;
|
|
1252
|
+
int any_mask;
|
|
326
1253
|
|
|
327
1254
|
ca_update_mask(ca->parent);
|
|
328
1255
|
if ( ! ca->parent->mask ) {
|
|
329
1256
|
ca_create_mask(ca->parent);
|
|
330
1257
|
}
|
|
331
1258
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
1259
|
+
/* Any MASK axis of the view becomes FILL in the mask sub-view, with the
|
|
1260
|
+
mask cell forced to 1 (= "masked"). Other axes keep their policy. When
|
|
1261
|
+
every axis shares MASK this is exactly "OOB cells are masked". */
|
|
1262
|
+
any_mask = 0;
|
|
1263
|
+
for (i = 0; i < ca->ndim; i++) {
|
|
1264
|
+
if ( ca->bounds[i] == CA_BOUNDS_MASK ) {
|
|
1265
|
+
mbounds[i] = CA_BOUNDS_FILL;
|
|
1266
|
+
any_mask = 1;
|
|
1267
|
+
} else {
|
|
1268
|
+
mbounds[i] = ca->bounds[i];
|
|
1269
|
+
}
|
|
338
1270
|
}
|
|
1271
|
+
fill = any_mask ? 1 : 0;
|
|
339
1272
|
|
|
340
1273
|
ca->mask = (CArray *) ca_window_new(ca->parent->mask,
|
|
341
|
-
ca->start, ca->count,
|
|
1274
|
+
ca->start, ca->count, mbounds, (char*)&fill);
|
|
342
1275
|
}
|
|
343
1276
|
|
|
344
1277
|
ca_operation_function_t ca_window_func = {
|
|
345
1278
|
-1, /* CA_OBJ_WINDOW */
|
|
346
|
-
|
|
1279
|
+
CA_VIEW_ARRAY,
|
|
347
1280
|
free_ca_window,
|
|
348
1281
|
ca_window_func_clone,
|
|
349
|
-
ca_window_func_ptr_at_addr,
|
|
350
|
-
ca_window_func_ptr_at_index,
|
|
351
|
-
NULL,
|
|
352
|
-
ca_window_func_fetch_index,
|
|
353
|
-
NULL,
|
|
354
|
-
ca_window_func_store_index,
|
|
355
1282
|
ca_window_func_allocate,
|
|
356
1283
|
ca_window_func_attach,
|
|
357
1284
|
ca_window_func_sync,
|
|
358
1285
|
ca_window_func_detach,
|
|
359
|
-
ca_window_func_copy_data,
|
|
360
|
-
ca_window_func_sync_data,
|
|
361
1286
|
ca_window_func_fill_data,
|
|
362
1287
|
ca_window_func_create_mask,
|
|
1288
|
+
ca_window_func_xfer_index,
|
|
1289
|
+
ca_window_func_xfer_addrs,
|
|
1290
|
+
ca_window_func_fold_stride,
|
|
1291
|
+
ca_window_func_xfer_stride,
|
|
1292
|
+
ca_window_func_xfer_all,
|
|
363
1293
|
};
|
|
364
1294
|
|
|
365
1295
|
/* ------------------------------------------------------------------- */
|
|
1296
|
+
/* Producer interface: emits one descriptor per axis. The engine
|
|
1297
|
+
(ca_axis_dispatch.c) consumes these and applies the per-axis bounds policy
|
|
1298
|
+
via ca_bounds_normalize_index, writing ca->fill for cells that resolve
|
|
1299
|
+
out of range.
|
|
366
1300
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
idx[level] = 0; \
|
|
371
|
-
p = ca_ptr_at_index((CArray*)cb, idx); \
|
|
372
|
-
v = (type*)cb->fill; \
|
|
373
|
-
for (i=0; i<count; i++, p++) { \
|
|
374
|
-
*p = *v; \
|
|
375
|
-
} \
|
|
376
|
-
} \
|
|
377
|
-
else { \
|
|
378
|
-
CArray *parent = cb->parent; \
|
|
379
|
-
ca_size_t start = cb->start[level]; \
|
|
380
|
-
ca_size_t size0 = cb->size0[level]; \
|
|
381
|
-
type *p, *q, *v; \
|
|
382
|
-
idx[level] = 0; \
|
|
383
|
-
p = (type*)ca_ptr_at_index((CArray*)cb, idx); \
|
|
384
|
-
v = (type*)cb->fill; \
|
|
385
|
-
i = 0; \
|
|
386
|
-
while ( start+i<0 && i<count ) { \
|
|
387
|
-
k = start + i; \
|
|
388
|
-
k = ca_bounds_normalize_index(cb->bounds, size0, k); \
|
|
389
|
-
if ( k < 0 || k >= size0 ) { \
|
|
390
|
-
*p = *v; \
|
|
391
|
-
} \
|
|
392
|
-
else { \
|
|
393
|
-
idx0[level] = k; \
|
|
394
|
-
*p = *(type*) ca_ptr_at_index(parent, idx0); \
|
|
395
|
-
} \
|
|
396
|
-
i++; p++; \
|
|
397
|
-
} \
|
|
398
|
-
idx0[level] = start+i; \
|
|
399
|
-
q = (type*)ca_ptr_at_index(parent, idx0);\
|
|
400
|
-
while ( start+i<size0 && i < count ) { \
|
|
401
|
-
*p = *q; \
|
|
402
|
-
i++, p++, q++; \
|
|
403
|
-
} \
|
|
404
|
-
while ( i < count ) { \
|
|
405
|
-
k = start + i; \
|
|
406
|
-
k = ca_bounds_normalize_index(cb->bounds, size0, k); \
|
|
407
|
-
if ( k < 0 || k >= size0 ) { \
|
|
408
|
-
*p = *v; \
|
|
409
|
-
} \
|
|
410
|
-
else { \
|
|
411
|
-
idx0[level] = k; \
|
|
412
|
-
*p = *(type*) ca_ptr_at_index(parent, idx0); \
|
|
413
|
-
} \
|
|
414
|
-
i++, p++; \
|
|
415
|
-
} \
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
static void
|
|
419
|
-
ca_window_attach_loop (CAWindow *cb, int8_t level,
|
|
420
|
-
ca_size_t *idx, ca_size_t *idx0, int fill)
|
|
421
|
-
{
|
|
422
|
-
ca_size_t count = cb->count[level];
|
|
423
|
-
ca_size_t i, k;
|
|
424
|
-
|
|
425
|
-
if ( level == cb->ndim - 1 ) {
|
|
426
|
-
switch ( cb->data_type ) {
|
|
427
|
-
case CA_BOOLEAN:
|
|
428
|
-
case CA_INT8: proc_window_attach_get(int8_t); break;
|
|
429
|
-
case CA_UINT8: proc_window_attach_get(uint8_t); break;
|
|
430
|
-
case CA_INT16: proc_window_attach_get(int16_t); break;
|
|
431
|
-
case CA_UINT16: proc_window_attach_get(uint16_t); break;
|
|
432
|
-
case CA_INT32: proc_window_attach_get(int32_t); break;
|
|
433
|
-
case CA_UINT32: proc_window_attach_get(uint32_t); break;
|
|
434
|
-
case CA_INT64: proc_window_attach_get(int64_t); break;
|
|
435
|
-
case CA_UINT64: proc_window_attach_get(uint64_t); break;
|
|
436
|
-
case CA_FLOAT32: proc_window_attach_get(float32_t); break;
|
|
437
|
-
case CA_FLOAT64: proc_window_attach_get(float64_t); break;
|
|
438
|
-
case CA_FLOAT128: proc_window_attach_get(float128_t); break;
|
|
439
|
-
#ifdef HAVE_COMPLEX_H
|
|
440
|
-
case CA_CMPLX64: proc_window_attach_get(cmplx64_t); break;
|
|
441
|
-
case CA_CMPLX128: proc_window_attach_get(cmplx128_t); break;
|
|
442
|
-
case CA_CMPLX256: proc_window_attach_get(cmplx256_t); break;
|
|
443
|
-
#endif
|
|
444
|
-
default:
|
|
445
|
-
if ( fill ) {
|
|
446
|
-
for (i=0; i<count; i++) {
|
|
447
|
-
idx[level] = i;
|
|
448
|
-
memcpy(ca_ptr_at_index((CArray*)cb, idx), cb->fill, cb->bytes);
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
else {
|
|
452
|
-
ca_size_t start = cb->start[level];
|
|
453
|
-
ca_size_t size0 = cb->size0[level];
|
|
454
|
-
for (i=0; i<count; i++) {
|
|
455
|
-
idx[level] = i;
|
|
456
|
-
k = start + i;
|
|
457
|
-
if ( k < 0 || k >= size0 ) {
|
|
458
|
-
k = ca_bounds_normalize_index(cb->bounds, size0, k);
|
|
459
|
-
if ( k < 0 || k >= size0 ) {
|
|
460
|
-
memcpy(ca_ptr_at_index((CArray*)cb, idx), cb->fill, cb->bytes);
|
|
461
|
-
continue;
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
idx0[level] = k;
|
|
465
|
-
memcpy(ca_ptr_at_index((CArray*)cb, idx), ca_ptr_at_index(cb->parent, idx0), cb->bytes);
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
else {
|
|
471
|
-
if ( fill ) {
|
|
472
|
-
for (i=0; i<count; i++) {
|
|
473
|
-
idx[level] = i;
|
|
474
|
-
ca_window_attach_loop(cb, level+1, idx, idx0, 1);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
else {
|
|
478
|
-
ca_size_t start = cb->start[level];
|
|
479
|
-
ca_size_t size0 = cb->size0[level];
|
|
480
|
-
for (i=0; i<count; i++) {
|
|
481
|
-
idx[level] = i;
|
|
482
|
-
k = start + i;
|
|
483
|
-
if ( k < 0 || k >= size0 ) {
|
|
484
|
-
k = ca_bounds_normalize_index(cb->bounds, size0, k);
|
|
485
|
-
if ( k < 0 || k >= size0 ) {
|
|
486
|
-
ca_window_attach_loop(cb, level+1, idx, idx0, 1); /* fill */
|
|
487
|
-
continue;
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
idx0[level] = k;
|
|
491
|
-
ca_window_attach_loop(cb, level+1, idx, idx0, 0); /* not-fill */
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
}
|
|
1301
|
+
CAWindow's step is implicitly 1 (count cells starting at start, sliding by
|
|
1302
|
+
1); the engine's SHIFT-axis offset computation is parent_index = start + i,
|
|
1303
|
+
with bounds normalisation per policy.
|
|
496
1304
|
|
|
1305
|
+
Reached only from the non-embed paths: an embed_eligible view attaches and
|
|
1306
|
+
syncs through ca_window_attach_embed / _sync_embed and never gets here. */
|
|
497
1307
|
void
|
|
498
|
-
|
|
1308
|
+
ca_window_describe_axes (void *ap, ca_axis_desc_t *out,
|
|
1309
|
+
ca_size_t *out_parent_dims)
|
|
499
1310
|
{
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
k
|
|
514
|
-
k
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
i++; p++; q++; \
|
|
529
|
-
} \
|
|
530
|
-
while ( i<count ) { \
|
|
531
|
-
k = start + i; \
|
|
532
|
-
k = ca_bounds_normalize_index(cb->bounds, size0, k); \
|
|
533
|
-
if ( k < 0 || k >= size0 ) { \
|
|
534
|
-
; \
|
|
535
|
-
} \
|
|
536
|
-
else { \
|
|
537
|
-
idx0[level] = k; \
|
|
538
|
-
*(type*)ca_ptr_at_index(parent, idx0) = *p; \
|
|
539
|
-
} \
|
|
540
|
-
i++; p++; \
|
|
541
|
-
} \
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
static void
|
|
545
|
-
ca_window_sync_loop (CAWindow *cb, int8_t level,
|
|
546
|
-
ca_size_t *idx, ca_size_t *idx0)
|
|
547
|
-
{
|
|
548
|
-
ca_size_t count = cb->count[level];
|
|
549
|
-
ca_size_t start = cb->start[level];
|
|
550
|
-
ca_size_t size0 = cb->size0[level];
|
|
551
|
-
ca_size_t i, k;
|
|
552
|
-
|
|
553
|
-
if ( level == cb->ndim - 1 ) {
|
|
554
|
-
switch ( cb->data_type ) {
|
|
555
|
-
case CA_BOOLEAN:
|
|
556
|
-
case CA_INT8: proc_window_sync_set(int8_t); break;
|
|
557
|
-
case CA_UINT8: proc_window_sync_set(uint8_t); break;
|
|
558
|
-
case CA_INT16: proc_window_sync_set(int16_t); break;
|
|
559
|
-
case CA_UINT16: proc_window_sync_set(uint16_t); break;
|
|
560
|
-
case CA_INT32: proc_window_sync_set(int32_t); break;
|
|
561
|
-
case CA_UINT32: proc_window_sync_set(uint32_t); break;
|
|
562
|
-
case CA_INT64: proc_window_sync_set(int64_t); break;
|
|
563
|
-
case CA_UINT64: proc_window_sync_set(uint64_t); break;
|
|
564
|
-
case CA_FLOAT32: proc_window_sync_set(float32_t); break;
|
|
565
|
-
case CA_FLOAT64: proc_window_sync_set(float64_t); break;
|
|
566
|
-
case CA_FLOAT128: proc_window_sync_set(float128_t); break;
|
|
567
|
-
#ifdef HAVE_COMPLEX_H
|
|
568
|
-
case CA_CMPLX64: proc_window_sync_set(cmplx64_t); break;
|
|
569
|
-
case CA_CMPLX128: proc_window_sync_set(cmplx128_t); break;
|
|
570
|
-
case CA_CMPLX256: proc_window_sync_set(cmplx256_t); break;
|
|
571
|
-
#endif
|
|
572
|
-
default:
|
|
573
|
-
for (i=0; i<count; i++) {
|
|
574
|
-
idx[level] = i;
|
|
575
|
-
k = start + i;
|
|
576
|
-
if ( k < 0 || k >= size0 ) {
|
|
577
|
-
k = ca_bounds_normalize_index(cb->bounds, size0, k);
|
|
578
|
-
if ( k < 0 || k >= size0 ) {
|
|
579
|
-
continue;
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
idx0[level] = k;
|
|
583
|
-
memcpy(ca_ptr_at_index(cb->parent, idx0), ca_ptr_at_index((CArray*)cb, idx), cb->bytes);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
else {
|
|
588
|
-
for (i=0; i<count; i++) {
|
|
589
|
-
idx[level] = i;
|
|
590
|
-
k = start + i;
|
|
591
|
-
if ( k < 0 || k >= size0 ) {
|
|
592
|
-
k = ca_bounds_normalize_index(cb->bounds, size0, k);
|
|
593
|
-
if ( k < 0 || k >= size0 ) {
|
|
594
|
-
continue;
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
idx0[level] = k;
|
|
598
|
-
ca_window_sync_loop(cb, level+1, idx, idx0);
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
void
|
|
604
|
-
ca_window_sync (CAWindow *cb)
|
|
605
|
-
{
|
|
606
|
-
ca_size_t idx[CA_RANK_MAX];
|
|
607
|
-
ca_size_t idx0[CA_RANK_MAX];
|
|
608
|
-
ca_window_sync_loop(cb, (int8_t) 0, idx, idx0);
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
#define proc_window_fill_set(type) \
|
|
612
|
-
{ \
|
|
613
|
-
CArray *parent = cb->parent; \
|
|
614
|
-
type *q; \
|
|
615
|
-
i = 0; \
|
|
616
|
-
while ( start+i<0 && i<count ) { \
|
|
617
|
-
k = start + i; \
|
|
618
|
-
k = ca_bounds_normalize_index(cb->bounds, size0, k); \
|
|
619
|
-
if ( k < 0 || k >= size0 ) { \
|
|
620
|
-
; \
|
|
621
|
-
} \
|
|
622
|
-
else { \
|
|
623
|
-
idx0[level] = k; \
|
|
624
|
-
*(type *)ca_ptr_at_index(parent, idx0) = *ptr; \
|
|
625
|
-
} \
|
|
626
|
-
i++; \
|
|
627
|
-
}\
|
|
628
|
-
idx0[level] = start + i; \
|
|
629
|
-
q = (type*)ca_ptr_at_index(parent, idx0);\
|
|
630
|
-
while ( start+i < size0 && i<count ) { \
|
|
631
|
-
*q = *ptr; \
|
|
632
|
-
i++; q++; \
|
|
633
|
-
} \
|
|
634
|
-
while ( i<count ) { \
|
|
635
|
-
k = start + i; \
|
|
636
|
-
k = ca_bounds_normalize_index(cb->bounds, size0, k); \
|
|
637
|
-
if ( k < 0 || k >= size0 ) { \
|
|
638
|
-
; \
|
|
639
|
-
} \
|
|
640
|
-
else { \
|
|
641
|
-
idx0[level] = k; \
|
|
642
|
-
*(type*)ca_ptr_at_index(parent, idx0) = *ptr; \
|
|
643
|
-
} \
|
|
644
|
-
i++; \
|
|
645
|
-
} \
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
static void
|
|
649
|
-
ca_window_fill_loop (CAWindow *cb, char *ptr,
|
|
650
|
-
int8_t level, ca_size_t *idx0)
|
|
651
|
-
{
|
|
652
|
-
ca_size_t count = cb->count[level];
|
|
653
|
-
ca_size_t start = cb->start[level];
|
|
654
|
-
ca_size_t size0 = cb->size0[level];
|
|
655
|
-
ca_size_t i, k;
|
|
656
|
-
|
|
657
|
-
if ( level == cb->ndim - 1 ) {
|
|
658
|
-
switch ( cb->data_type ) {
|
|
659
|
-
case CA_BOOLEAN:
|
|
660
|
-
case CA_INT8: proc_window_fill_set(int8_t); break;
|
|
661
|
-
case CA_UINT8: proc_window_fill_set(uint8_t); break;
|
|
662
|
-
case CA_INT16: proc_window_fill_set(int16_t); break;
|
|
663
|
-
case CA_UINT16: proc_window_fill_set(uint16_t); break;
|
|
664
|
-
case CA_INT32: proc_window_fill_set(int32_t); break;
|
|
665
|
-
case CA_UINT32: proc_window_fill_set(uint32_t); break;
|
|
666
|
-
case CA_INT64: proc_window_fill_set(int64_t); break;
|
|
667
|
-
case CA_UINT64: proc_window_fill_set(uint64_t); break;
|
|
668
|
-
case CA_FLOAT32: proc_window_fill_set(float32_t); break;
|
|
669
|
-
case CA_FLOAT64: proc_window_fill_set(float64_t); break;
|
|
670
|
-
case CA_FLOAT128: proc_window_fill_set(float128_t); break;
|
|
671
|
-
#ifdef HAVE_COMPLEX_H
|
|
672
|
-
case CA_CMPLX64: proc_window_fill_set(cmplx64_t); break;
|
|
673
|
-
case CA_CMPLX128: proc_window_fill_set(cmplx128_t); break;
|
|
674
|
-
case CA_CMPLX256: proc_window_fill_set(cmplx256_t); break;
|
|
675
|
-
#endif
|
|
676
|
-
default:
|
|
677
|
-
for (i=0; i<count; i++) {
|
|
678
|
-
k = start + i;
|
|
679
|
-
if ( k < 0 || k >= size0 ) {
|
|
680
|
-
k = ca_bounds_normalize_index(cb->bounds, size0, k);
|
|
681
|
-
if ( k < 0 || k >= size0 ) {
|
|
682
|
-
continue;
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
idx0[level] = k;
|
|
686
|
-
memcpy(ca_ptr_at_index(cb->parent, idx0), ptr, cb->bytes);
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
else {
|
|
691
|
-
for (i=0; i<count; i++) {
|
|
692
|
-
k = start + i;
|
|
693
|
-
if ( k < 0 || k >= size0 ) {
|
|
694
|
-
k = ca_bounds_normalize_index(cb->bounds, size0, k);
|
|
695
|
-
if ( k < 0 || k >= size0 ) {
|
|
696
|
-
continue;
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
idx0[level] = k;
|
|
700
|
-
ca_window_fill_loop(cb, ptr, level+1, idx0);
|
|
1311
|
+
CAWindow *ca = (CAWindow *) ap;
|
|
1312
|
+
int8_t k;
|
|
1313
|
+
for ( k = 0; k < ca->ndim; k++ ) {
|
|
1314
|
+
out_parent_dims[k] = ca->parent->dim[k];
|
|
1315
|
+
/* Interior-only axes are promoted to STRIDE kind. An axis touches no
|
|
1316
|
+
boundary cell iff start[k] >= 0 and start[k] + count[k] <=
|
|
1317
|
+
parent->dim[k] (step is implicitly 1); it is then a pure strided slice
|
|
1318
|
+
of the parent, so emitting STRIDE lets the engine take its strided fast
|
|
1319
|
+
paths (slab fusion, axis-merge, alias) and skip the per-cell
|
|
1320
|
+
ca_bounds_normalize_index + OOB check that SHIFT costs. */
|
|
1321
|
+
if ( ca->start[k] >= 0
|
|
1322
|
+
&& ca->start[k] + ca->count[k] <= ca->parent->dim[k] ) {
|
|
1323
|
+
out[k].kind = CA_AXIS_KIND_STRIDE;
|
|
1324
|
+
out[k].count = ca->count[k];
|
|
1325
|
+
out[k].start = ca->start[k];
|
|
1326
|
+
out[k].step = 1;
|
|
1327
|
+
out[k].indices = NULL;
|
|
1328
|
+
/* size0 / policy unused for STRIDE — set defaults for hygiene. */
|
|
1329
|
+
out[k].size0 = ca->size0[k];
|
|
1330
|
+
out[k].policy = ca->bounds[k];
|
|
1331
|
+
} else {
|
|
1332
|
+
out[k].kind = CA_AXIS_KIND_SHIFT;
|
|
1333
|
+
out[k].count = ca->count[k];
|
|
1334
|
+
out[k].start = ca->start[k];
|
|
1335
|
+
out[k].step = 1;
|
|
1336
|
+
out[k].indices = NULL;
|
|
1337
|
+
out[k].size0 = ca->size0[k];
|
|
1338
|
+
out[k].policy = ca->bounds[k];
|
|
701
1339
|
}
|
|
702
1340
|
}
|
|
703
|
-
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
void
|
|
707
|
-
ca_window_fill (CAWindow *cb, char *ptr)
|
|
708
|
-
{
|
|
709
|
-
ca_size_t idx0[CA_RANK_MAX];
|
|
710
|
-
ca_window_fill_loop(cb, ptr, (int8_t) 0, idx0);
|
|
711
1341
|
}
|
|
712
1342
|
|
|
713
1343
|
/* ------------------------------------------------------------------- */
|
|
@@ -716,29 +1346,26 @@ VALUE
|
|
|
716
1346
|
rb_ca_window_new (VALUE cary,
|
|
717
1347
|
ca_size_t *start, ca_size_t *count, int8_t bounds, char *fill)
|
|
718
1348
|
{
|
|
1349
|
+
/* Scalar-bounds wrapper for the Ruby surface: fan the single policy out
|
|
1350
|
+
to a per-axis array before calling ca_window_new. */
|
|
719
1351
|
volatile VALUE obj;
|
|
720
1352
|
CArray *parent;
|
|
721
1353
|
CAWindow *ca;
|
|
1354
|
+
uint8_t bounds_arr[CA_RANK_MAX];
|
|
1355
|
+
int8_t i;
|
|
722
1356
|
rb_check_carray_object(cary);
|
|
723
1357
|
TypedData_Get_Struct(cary, CArray, &carray_data_type, parent);
|
|
724
|
-
|
|
1358
|
+
for (i = 0; i < parent->ndim; i++) bounds_arr[i] = (uint8_t) bounds;
|
|
1359
|
+
ca = ca_window_new(parent, start, count, bounds_arr, fill);
|
|
725
1360
|
obj = ca_wrap_struct(ca);
|
|
726
1361
|
rb_ca_set_parent(obj, cary);
|
|
727
|
-
rb_ca_data_type_inherit(obj, cary);
|
|
728
1362
|
return obj;
|
|
729
1363
|
}
|
|
730
1364
|
|
|
731
|
-
/* yard:
|
|
732
|
-
class CArray
|
|
733
|
-
def window (*argv)
|
|
734
|
-
end
|
|
735
|
-
end
|
|
736
|
-
*/
|
|
737
|
-
|
|
738
1365
|
VALUE
|
|
739
1366
|
rb_ca_window (int argc, VALUE *argv, VALUE self)
|
|
740
1367
|
{
|
|
741
|
-
volatile VALUE obj, ropt, rfval =
|
|
1368
|
+
volatile VALUE obj, ropt, rfval = CA_UNSPECIFIED, rbounds = Qnil, rcs;
|
|
742
1369
|
CArray *ca;
|
|
743
1370
|
CScalar *cs;
|
|
744
1371
|
ca_size_t start[CA_RANK_MAX];
|
|
@@ -770,16 +1397,13 @@ rb_ca_window (int argc, VALUE *argv, VALUE self)
|
|
|
770
1397
|
count[i] = len;
|
|
771
1398
|
}
|
|
772
1399
|
|
|
773
|
-
if (
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
}
|
|
778
|
-
else {
|
|
779
|
-
/* rb_warn(":fill_value option for CArray#window will be obsoleted."); */
|
|
1400
|
+
if ( rb_block_given_p() ) {
|
|
1401
|
+
rb_raise(rb_eArgError,
|
|
1402
|
+
"window: block form for fill value removed in 3.0; "
|
|
1403
|
+
"use fill_value: kwarg (e.g. window(-1..1, fill_value: UNDEF))");
|
|
780
1404
|
}
|
|
781
1405
|
|
|
782
|
-
if ( rfval ==
|
|
1406
|
+
if ( rfval == CA_UNSPECIFIED ) {
|
|
783
1407
|
;
|
|
784
1408
|
}
|
|
785
1409
|
else if ( rfval == CA_UNDEF ) {
|
|
@@ -809,14 +1433,19 @@ rb_ca_window (int argc, VALUE *argv, VALUE self)
|
|
|
809
1433
|
bounds = CA_BOUNDS_NEAREST;
|
|
810
1434
|
}
|
|
811
1435
|
else if ( ! strncmp(cbounds, "periodic", 8) ) {
|
|
812
|
-
|
|
1436
|
+
rb_raise(rb_eArgError,
|
|
1437
|
+
"bounds=>'periodic' removed in 3.0; "
|
|
1438
|
+
"use CArray#roll(...) for cyclic shift "
|
|
1439
|
+
"(returns a CARoll view)");
|
|
813
1440
|
}
|
|
814
1441
|
else if ( ! strncmp(cbounds, "reflect", 7) ) {
|
|
815
|
-
|
|
1442
|
+
rb_raise(rb_eArgError,
|
|
1443
|
+
"bounds=>'reflect' removed in 3.0; "
|
|
1444
|
+
"there is no view-based alternative");
|
|
816
1445
|
}
|
|
817
1446
|
else if ( ! strncmp(cbounds, "mask", 4) ) {
|
|
818
1447
|
rb_warn("CAWindow option :bounds=>\"mask\" will be obsolete");
|
|
819
|
-
rb_warn("use ca.window(
|
|
1448
|
+
rb_warn("use ca.window(..., fill_value: UNDEF)");
|
|
820
1449
|
bounds = CA_BOUNDS_MASK;
|
|
821
1450
|
}
|
|
822
1451
|
else if ( ! strncmp(cbounds, "fill", 4) ) {
|
|
@@ -837,6 +1466,7 @@ rb_ca_window (int argc, VALUE *argv, VALUE self)
|
|
|
837
1466
|
|
|
838
1467
|
obj = rb_ca_window_new(self, start, count, bounds, fill);
|
|
839
1468
|
|
|
1469
|
+
CA_WRAPPER_LIFT(obj, self, ca);
|
|
840
1470
|
return obj;
|
|
841
1471
|
}
|
|
842
1472
|
|
|
@@ -857,18 +1487,17 @@ rb_ca_window_initialize_copy (VALUE self, VALUE other)
|
|
|
857
1487
|
TypedData_Get_Struct(self, CAWindow, &cawindow_data_type, ca);
|
|
858
1488
|
TypedData_Get_Struct(other, CAWindow, &cawindow_data_type, cs);
|
|
859
1489
|
|
|
1490
|
+
/* `self` came from rb_ca_window_s_allocate (TypedData_Make_Struct,
|
|
1491
|
+
_pool == NULL). Attach the pool before setup so the ndim-sized
|
|
1492
|
+
tail fields skip ALLOC_N. */
|
|
1493
|
+
if ( ca_func[CA_OBJ_WINDOW].pool_init ) {
|
|
1494
|
+
ca_array_pool_alloc(ca, CA_OBJ_WINDOW, cs->ndim);
|
|
1495
|
+
}
|
|
860
1496
|
ca_window_setup(ca, cs->parent, cs->start, cs->count, cs->bounds, cs->fill);
|
|
861
1497
|
|
|
862
1498
|
return self;
|
|
863
1499
|
}
|
|
864
1500
|
|
|
865
|
-
/* yard:
|
|
866
|
-
class CAWindow
|
|
867
|
-
def index2addr0 (idx)
|
|
868
|
-
end
|
|
869
|
-
end
|
|
870
|
-
*/
|
|
871
|
-
|
|
872
1501
|
static VALUE
|
|
873
1502
|
rb_ca_window_idx2addr0 (int argc, VALUE *argv, VALUE self)
|
|
874
1503
|
{
|
|
@@ -899,13 +1528,6 @@ rb_ca_window_idx2addr0 (int argc, VALUE *argv, VALUE self)
|
|
|
899
1528
|
}
|
|
900
1529
|
}
|
|
901
1530
|
|
|
902
|
-
/* yard:
|
|
903
|
-
class CAWindow
|
|
904
|
-
def addr2addr0 (addr)
|
|
905
|
-
end
|
|
906
|
-
end
|
|
907
|
-
*/
|
|
908
|
-
|
|
909
1531
|
static VALUE
|
|
910
1532
|
rb_ca_window_addr2addr0 (VALUE self, VALUE raddr)
|
|
911
1533
|
{
|
|
@@ -924,43 +1546,13 @@ rb_ca_window_addr2addr0 (VALUE self, VALUE raddr)
|
|
|
924
1546
|
addr += cw->start[i] + idx[i];
|
|
925
1547
|
}
|
|
926
1548
|
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
static VALUE
|
|
932
|
-
rb_ca_window_move (int argc, VALUE *argv, VALUE self)
|
|
933
|
-
{
|
|
934
|
-
CAWindow *cw;
|
|
935
|
-
ca_size_t start;
|
|
936
|
-
int8_t i;
|
|
937
|
-
|
|
938
|
-
TypedData_Get_Struct(self, CAWindow, &cawindow_data_type, cw);
|
|
939
|
-
|
|
940
|
-
if ( argc != cw->ndim ) {
|
|
941
|
-
rb_raise(rb_eArgError, "invalid # of arguments");
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
ca_update_mask(cw);
|
|
945
|
-
for (i=0; i<cw->ndim; i++) {
|
|
946
|
-
start = NUM2SIZE(argv[i]);
|
|
947
|
-
cw->start[i] = start;
|
|
948
|
-
if ( cw->mask ) {
|
|
949
|
-
((CAWindow*)(cw->mask))->start[i] = start;
|
|
950
|
-
}
|
|
1549
|
+
if ( addr < 0 || addr >= cw->parent->elements ) {
|
|
1550
|
+
return Qnil;
|
|
951
1551
|
}
|
|
952
1552
|
|
|
953
|
-
return
|
|
1553
|
+
return SIZE2NUM(addr);
|
|
954
1554
|
}
|
|
955
1555
|
|
|
956
|
-
/* yard:
|
|
957
|
-
class CAWindow
|
|
958
|
-
def fill_value
|
|
959
|
-
end
|
|
960
|
-
def fill_value= (val)
|
|
961
|
-
end
|
|
962
|
-
end
|
|
963
|
-
*/
|
|
964
1556
|
|
|
965
1557
|
static VALUE
|
|
966
1558
|
rb_ca_window_set_fill_value (VALUE self, VALUE rfval)
|
|
@@ -982,9 +1574,12 @@ rb_ca_window_get_fill_value (VALUE self)
|
|
|
982
1574
|
static VALUE
|
|
983
1575
|
rb_ca_window_get_bounds (VALUE self)
|
|
984
1576
|
{
|
|
1577
|
+
/* bounds is per-axis, but the Ruby surface always constructs with a single
|
|
1578
|
+
scalar policy fanned out across all axes, so axis 0 reproduces the value
|
|
1579
|
+
that was passed in. */
|
|
985
1580
|
CAWindow *cw;
|
|
986
1581
|
TypedData_Get_Struct(self, CAWindow, &cawindow_data_type, cw);
|
|
987
|
-
return SIZE2NUM(cw->bounds);
|
|
1582
|
+
return SIZE2NUM(cw->bounds[0]);
|
|
988
1583
|
}
|
|
989
1584
|
|
|
990
1585
|
#define rb_cw_get_attr_ary(name) \
|
|
@@ -1001,36 +1596,73 @@ rb_ca_window_get_bounds (VALUE self)
|
|
|
1001
1596
|
return ary; \
|
|
1002
1597
|
}
|
|
1003
1598
|
|
|
1004
|
-
/* yard:
|
|
1005
|
-
class CAWindow
|
|
1006
|
-
def size0
|
|
1007
|
-
end
|
|
1008
|
-
def start
|
|
1009
|
-
end
|
|
1010
|
-
def step
|
|
1011
|
-
end
|
|
1012
|
-
def count
|
|
1013
|
-
end
|
|
1014
|
-
def offset
|
|
1015
|
-
end
|
|
1016
|
-
end
|
|
1017
|
-
*/
|
|
1018
|
-
|
|
1019
1599
|
static VALUE rb_cw_get_attr_ary(start);
|
|
1020
1600
|
static VALUE rb_cw_get_attr_ary(count);
|
|
1021
1601
|
static VALUE rb_cw_get_attr_ary(size0);
|
|
1022
1602
|
|
|
1603
|
+
#ifdef CARRAY_DEV_BUILD
|
|
1604
|
+
/* Debug accessor (dev-only, stripped in release), returning the embed
|
|
1605
|
+
descriptor as a Hash. The descriptor is an internal implementation detail
|
|
1606
|
+
with no user-facing meaning; this exists so spec_ai can pin its geometry,
|
|
1607
|
+
which nothing else can observe (a stale descriptor produces wrong data, not
|
|
1608
|
+
an error). Gated by CARRAY_DEV_BUILD, enabled via
|
|
1609
|
+
`extconf.rb --enable-dev-build` or `CARRAY_DEV=1 rake build_ext`.
|
|
1610
|
+
|
|
1611
|
+
Polymorphic over CAWindow / CAShift: both share the same C struct layout
|
|
1612
|
+
(CAShift is a typedef of CAWindow) but use distinct TypedData types.
|
|
1613
|
+
DATA_PTR is safe here because Ruby method dispatch has already restricted
|
|
1614
|
+
self to one of those two classes. */
|
|
1615
|
+
VALUE
|
|
1616
|
+
rb_ca_window_embed_descriptor (VALUE self)
|
|
1617
|
+
{
|
|
1618
|
+
CAWindow *ca = (CAWindow *) DATA_PTR(self);
|
|
1619
|
+
VALUE hash, ps_ary, ec_ary, oo_ary;
|
|
1620
|
+
int8_t k;
|
|
1621
|
+
|
|
1622
|
+
ps_ary = rb_ary_new_capa(ca->ndim);
|
|
1623
|
+
ec_ary = rb_ary_new_capa(ca->ndim);
|
|
1624
|
+
oo_ary = rb_ary_new_capa(ca->ndim);
|
|
1625
|
+
for ( k = 0; k < ca->ndim; k++ ) {
|
|
1626
|
+
rb_ary_push(ps_ary, SIZE2NUM(ca->embed_parent_start[k]));
|
|
1627
|
+
rb_ary_push(ec_ary, SIZE2NUM(ca->embed_count[k]));
|
|
1628
|
+
rb_ary_push(oo_ary, SIZE2NUM(ca->embed_output_offset[k]));
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
hash = rb_hash_new();
|
|
1632
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("parent_start")), ps_ary);
|
|
1633
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("count")), ec_ary);
|
|
1634
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("output_offset")), oo_ary);
|
|
1635
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("is_empty")),
|
|
1636
|
+
ca->embed_is_empty ? Qtrue : Qfalse);
|
|
1637
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("covers_all")),
|
|
1638
|
+
ca->embed_covers_all ? Qtrue : Qfalse);
|
|
1639
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("eligible")),
|
|
1640
|
+
ca->embed_eligible ? Qtrue : Qfalse);
|
|
1641
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("alias_eligible")),
|
|
1642
|
+
ca->embed_alias_eligible ? Qtrue : Qfalse);
|
|
1643
|
+
return hash;
|
|
1644
|
+
}
|
|
1645
|
+
#endif /* CARRAY_DEV_BUILD */
|
|
1646
|
+
|
|
1023
1647
|
void
|
|
1024
|
-
Init_ca_obj_window ()
|
|
1648
|
+
Init_ca_obj_window (void)
|
|
1025
1649
|
{
|
|
1026
1650
|
|
|
1027
|
-
rb_cCAWindow = rb_define_class("CAWindow",
|
|
1651
|
+
rb_cCAWindow = rb_define_class("CAWindow", rb_cCAView);
|
|
1028
1652
|
rb_cCAWindowMask = rb_define_class("CAWindowMask", rb_cCAWindow);
|
|
1029
1653
|
|
|
1030
|
-
|
|
1654
|
+
/* Pool framework: seven ndim-sized tail fields live in one _pool buffer
|
|
1655
|
+
(fill stays separate, bytes-sized). Set on the global ca_window_func
|
|
1656
|
+
before ca_install_obj_type copies it into ca_func[], and before
|
|
1657
|
+
Init_ca_obj_shift copies ca_window_func into ca_shift_func. */
|
|
1658
|
+
ca_window_func.struct_size = sizeof(CAWindow);
|
|
1659
|
+
ca_window_func.pool_bytes = ca_window_pool_bytes;
|
|
1660
|
+
ca_window_func.pool_init = ca_window_pool_init;
|
|
1661
|
+
|
|
1662
|
+
CA_OBJ_WINDOW = ca_install_obj_type(rb_cCAWindow,
|
|
1031
1663
|
&cawindow_data_type,
|
|
1032
1664
|
rb_cCAWindowMask,
|
|
1033
|
-
&cawindow_mask_data_type, ca_window_func);
|
|
1665
|
+
&cawindow_mask_data_type, &ca_window_func, sizeof(ca_window_func));
|
|
1034
1666
|
rb_define_const(rb_cObject, "CA_OBJ_WINDOW", INT2NUM(CA_OBJ_WINDOW));
|
|
1035
1667
|
|
|
1036
1668
|
rb_define_method(rb_cCArray, "window", rb_ca_window, -1);
|
|
@@ -1039,7 +1671,6 @@ Init_ca_obj_window ()
|
|
|
1039
1671
|
rb_define_method(rb_cCAWindow, "initialize_copy",
|
|
1040
1672
|
rb_ca_window_initialize_copy, 1);
|
|
1041
1673
|
|
|
1042
|
-
rb_define_method(rb_cCAWindow, "move", rb_ca_window_move, -1);
|
|
1043
1674
|
|
|
1044
1675
|
rb_define_method(rb_cCAWindow, "index2addr0", rb_ca_window_idx2addr0, -1);
|
|
1045
1676
|
rb_define_method(rb_cCAWindow, "addr2addr0", rb_ca_window_addr2addr0, 1);
|
|
@@ -1053,5 +1684,11 @@ Init_ca_obj_window ()
|
|
|
1053
1684
|
rb_define_method(rb_cCAWindow, "count", rb_cw_count, 0);
|
|
1054
1685
|
rb_define_method(rb_cCAWindow, "size0", rb_cw_size0, 0);
|
|
1055
1686
|
|
|
1687
|
+
#ifdef CARRAY_DEV_BUILD
|
|
1688
|
+
/* debug accessor (dev-only, stripped in release) */
|
|
1689
|
+
rb_define_method(rb_cCAWindow, "_embed_descriptor",
|
|
1690
|
+
rb_ca_window_embed_descriptor, 0);
|
|
1691
|
+
#endif
|
|
1692
|
+
|
|
1056
1693
|
}
|
|
1057
1694
|
|