carray 2.0.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +5 -25
- data/CHANGELOG.md +16 -0
- data/LICENSE +1 -1
- data/NEWS.md +3 -0
- data/README.md +128 -44
- data/carray.gemspec +22 -24
- data/ext/ca_array_pool.c +91 -0
- data/ext/ca_axis_descriptor.h +186 -0
- data/ext/ca_axis_dispatch.c +924 -0
- data/ext/ca_axis_group.c +1208 -0
- data/ext/ca_bincmp_dispatch.c +76 -0
- data/ext/ca_bincmp_dispatch.h +85 -0
- data/ext/ca_binop_dispatch.c +125 -0
- data/ext/ca_binop_dispatch.h +159 -0
- data/ext/ca_categorical_iterator.c +1375 -0
- data/ext/ca_compare.c +94 -0
- data/ext/ca_compare.h +26 -0
- data/ext/ca_composite_dispatch.c +414 -0
- data/ext/ca_composite_dispatch.h +116 -0
- data/ext/ca_for_buffer.h +96 -0
- data/ext/ca_for_each_element.h +241 -0
- data/ext/ca_group_iter.c +304 -0
- data/ext/ca_iter_substrate.h +325 -0
- data/ext/ca_kernel_iterator.c +4321 -0
- data/ext/ca_kernel_iterator.h +2603 -0
- data/ext/ca_moncmp_dispatch.c +37 -0
- data/ext/ca_moncmp_dispatch.h +62 -0
- data/ext/ca_monop_dispatch.c +200 -0
- data/ext/ca_monop_dispatch.h +235 -0
- data/ext/ca_obj_array.c +355 -359
- data/ext/ca_obj_bincmp.c +809 -0
- data/ext/ca_obj_binop.c +892 -0
- data/ext/ca_obj_bitarray.c +369 -164
- data/ext/ca_obj_bitfield.c +294 -234
- data/ext/ca_obj_block.c +189 -711
- data/ext/ca_obj_byte_swap.c +766 -0
- data/ext/ca_obj_const_string.c +965 -0
- data/ext/ca_obj_face.c +670 -0
- data/ext/ca_obj_face.h +247 -0
- data/ext/ca_obj_fake.c +228 -100
- data/ext/ca_obj_farray.c +54 -441
- data/ext/ca_obj_field.c +82 -529
- data/ext/ca_obj_fixlen_string.c +306 -0
- data/ext/ca_obj_grid.c +858 -440
- data/ext/ca_obj_meld.c +1034 -0
- data/ext/ca_obj_moncmp.c +569 -0
- data/ext/ca_obj_monop.c +1111 -0
- data/ext/ca_obj_object.c +772 -297
- data/ext/ca_obj_record.c +468 -0
- data/ext/ca_obj_reduce.c +97 -82
- data/ext/ca_obj_refer.c +569 -459
- data/ext/ca_obj_remap.c +475 -0
- data/ext/ca_obj_repeat.c +92 -477
- data/ext/ca_obj_roll.c +616 -0
- data/ext/ca_obj_select.c +344 -296
- data/ext/ca_obj_select_axis.c +1296 -0
- data/ext/ca_obj_shift.c +229 -791
- data/ext/ca_obj_source.c +78 -0
- data/ext/ca_obj_stack.c +1173 -0
- data/ext/ca_obj_stride.c +2501 -0
- data/ext/ca_obj_string.c +268 -0
- data/ext/ca_obj_tile.c +614 -0
- data/ext/ca_obj_time.c +546 -0
- data/ext/ca_obj_timedelta.c +435 -0
- data/ext/ca_obj_transpose.c +62 -516
- data/ext/ca_obj_triop.c +746 -0
- data/ext/ca_obj_unbound_repeat.c +207 -240
- data/ext/ca_obj_window.c +1131 -563
- data/ext/ca_op_byte_swap.c +175 -0
- data/ext/ca_op_ipower.c +319 -0
- data/ext/ca_op_powi.h +88 -0
- data/ext/ca_sort_kernels.h +132 -0
- data/ext/ca_sweep_engine.c +430 -0
- data/ext/ca_sweep_engine.h +157 -0
- data/ext/ca_transform_common.c +228 -0
- data/ext/ca_triop_dispatch.c +55 -0
- data/ext/ca_triop_dispatch.h +62 -0
- data/ext/carray.h +795 -402
- data/ext/carray_access.c +831 -711
- data/ext/carray_attribute.c +98 -329
- data/ext/carray_bincount.c +255 -0
- data/ext/carray_broadcast.c +283 -0
- data/ext/carray_call_cfunc.c +1360 -828
- data/ext/carray_call_cfunc.h +160 -0
- data/ext/carray_cast.c +1212 -301
- data/ext/carray_cast_func.rb +81 -40
- data/ext/carray_class.c +53 -63
- data/ext/carray_config.h +28 -0
- data/ext/carray_conversion.c +350 -346
- data/ext/carray_copy.c +156 -268
- data/ext/carray_core.c +1341 -198
- data/ext/carray_count.c +312 -0
- data/ext/carray_data_type.c +43 -19
- data/ext/carray_element.c +585 -213
- data/ext/carray_factorize.c +2542 -0
- data/ext/carray_generate.c +230 -559
- data/ext/carray_histogram.c +490 -0
- data/ext/carray_hold.c +228 -0
- data/ext/carray_index_classifier.c +1035 -0
- data/ext/carray_index_classifier.h +27 -0
- data/ext/carray_internal.h +120 -0
- data/ext/carray_kernels_bincmp.c +4445 -0
- data/ext/carray_kernels_binop.c +10979 -0
- data/ext/carray_kernels_init.c +36 -0
- data/ext/carray_kernels_map.c +3466 -0
- data/ext/carray_kernels_moncmp.c +2096 -0
- data/ext/carray_kernels_monop.c +18312 -0
- data/ext/carray_kernels_reduce_aggregate.c +25836 -0
- data/ext/carray_kernels_reduce_boolean.c +329 -0
- data/ext/carray_kernels_reduce_cumulative.c +14592 -0
- data/ext/carray_kernels_reduce_extreme.c +16947 -0
- data/ext/carray_kernels_reduce_variance.c +3909 -0
- data/ext/carray_kernels_scan.c +3692 -0
- data/ext/carray_kernels_search.c +32137 -0
- data/ext/carray_kernels_sort.c +10625 -0
- data/ext/carray_kernels_triop.c +1391 -0
- data/ext/carray_lazy.c +567 -0
- data/ext/carray_loop.c +88 -200
- data/ext/carray_mask.c +848 -154
- data/ext/carray_math_kernel.h +120 -0
- data/ext/carray_mathfunc.c +10 -241
- data/ext/carray_median_percentile.c +1257 -0
- data/ext/carray_memory_view.c +1625 -0
- data/ext/carray_operator.c +1526 -318
- data/ext/carray_order.c +664 -1394
- data/ext/carray_partition.c +416 -0
- data/ext/carray_random.c +518 -0
- data/ext/carray_scatter.c +357 -0
- data/ext/carray_slab.c +1219 -0
- data/ext/carray_slab.h +84 -0
- data/ext/carray_sort.c +829 -0
- data/ext/carray_sort_kernel.c +620 -0
- data/ext/carray_struct.c +695 -0
- data/ext/carray_test.c +343 -229
- data/ext/carray_undef.c +34 -17
- data/ext/carray_utils.c +175 -74
- data/ext/extconf.rb +216 -55
- data/ext/mk_call_cfunc.rb +480 -0
- data/ext/mkkernel.rb +8842 -0
- data/ext/ruby_carray.c +202 -101
- data/ext/version.h +5 -15
- data/ext/version.rb +5 -13
- data/lib/carray/arrow_tensor.rb +401 -0
- data/lib/carray/attribute.rb +166 -0
- data/lib/carray/autoload_carray.rb +220 -0
- data/lib/carray/autoload_method_extension.rb +44 -0
- data/lib/carray/axis_group.rb +711 -0
- data/lib/carray/basics.rb +481 -0
- data/lib/carray/bincount_nd.rb +358 -0
- data/lib/carray/block_iterator.rb +604 -0
- data/lib/carray/boolean_reduce.rb +109 -0
- data/lib/carray/categorical.rb +561 -0
- data/lib/carray/categorical_iterator.rb +1062 -0
- data/lib/carray/complex.rb +150 -0
- data/lib/carray/conditional.rb +216 -0
- data/lib/carray/const_string.rb +228 -0
- data/lib/carray/construct.rb +139 -328
- data/lib/carray/core_extensions.rb +240 -0
- data/lib/carray/data_type_extension.rb +233 -0
- data/lib/carray/fixlen_string.rb +95 -0
- data/lib/carray/frame/concat.rb +132 -0
- data/lib/carray/frame/convert.rb +95 -0
- data/lib/carray/frame/csv_parser.rb +211 -0
- data/lib/carray/frame/frame.rb +649 -0
- data/lib/carray/frame/group.rb +186 -0
- data/lib/carray/frame/io.rb +164 -0
- data/lib/carray/frame/join.rb +248 -0
- data/lib/carray/frame/records.rb +99 -0
- data/lib/carray/frame/sort.rb +113 -0
- data/lib/carray/frame/verbs.rb +299 -0
- data/lib/carray/frame.rb +16 -0
- data/lib/carray/histogram.rb +512 -0
- data/lib/carray/inspect.rb +37 -20
- data/lib/carray/iterator.rb +57 -349
- data/lib/carray/lazy.rb +889 -0
- data/lib/carray/mask_gap_fill.rb +200 -0
- data/lib/carray/math.rb +78 -342
- data/lib/carray/meld_reduce.rb +289 -0
- data/lib/carray/methods/align_addr.rb +116 -0
- data/lib/carray/methods/bin.rb +128 -0
- data/lib/carray/methods/bincount.rb +87 -0
- data/lib/carray/methods/bit_string.rb +92 -0
- data/lib/carray/methods/broadcast.rb +63 -0
- data/lib/carray/methods/choose.rb +39 -0
- data/lib/carray/methods/composition.rb +280 -0
- data/lib/carray/methods/gather_nd.rb +206 -0
- data/lib/carray/methods/index.rb +39 -0
- data/lib/carray/methods/insert_block.rb +99 -0
- data/lib/carray/methods/is_in.rb +141 -0
- data/lib/carray/methods/join.rb +90 -0
- data/lib/carray/methods/locate_addr.rb +47 -0
- data/lib/carray/methods/mask_duplicates.rb +41 -0
- data/lib/carray/methods/meshgrid.rb +91 -0
- data/lib/carray/methods/mode.rb +126 -0
- data/lib/carray/methods/nunique.rb +46 -0
- data/lib/carray/methods/resize.rb +56 -0
- data/lib/carray/methods/snap.rb +156 -0
- data/lib/carray/methods/string_format.rb +57 -0
- data/lib/carray/methods/unique.rb +47 -0
- data/lib/carray/methods/value_counts.rb +71 -0
- data/lib/carray/mkmf.rb +124 -101
- data/lib/carray/runtime.rb +108 -0
- data/lib/carray/serialize.rb +478 -167
- data/lib/carray/slab_iterator.rb +292 -0
- data/lib/carray/stack.rb +291 -0
- data/lib/carray/string.rb +56 -180
- data/lib/carray/string_operation_extension.rb +289 -0
- data/lib/carray/struct.rb +335 -323
- data/lib/carray/struct_builder.rb +697 -0
- data/lib/carray/table.rb +41 -2
- data/lib/carray/time.rb +2255 -38
- data/lib/carray/window_iterator.rb +655 -0
- data/lib/carray.rb +55 -57
- metadata +163 -130
- data/Rakefile +0 -51
- data/TODO.md +0 -18
- data/ext/ca_iter_block.c +0 -257
- data/ext/ca_iter_dimension.c +0 -299
- data/ext/ca_iter_window.c +0 -214
- data/ext/ca_obj_mapping.c +0 -644
- data/ext/carray_iterator.c +0 -641
- data/ext/carray_math.rb +0 -850
- data/ext/carray_numeric.c +0 -259
- data/ext/carray_sort_addr.c +0 -254
- data/ext/carray_stat.c +0 -2100
- data/ext/carray_stat_proc.rb +0 -1999
- data/ext/mkmath.rb +0 -741
- data/ext/ruby_ccomplex.c +0 -509
- data/ext/ruby_float_func.c +0 -86
- data/lib/carray/array.rb +0 -8
- data/lib/carray/autoload/autoload_base.rb +0 -19
- data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
- data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
- data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
- data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
- data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
- data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
- data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
- data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
- data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
- data/lib/carray/autoload/autoload_gem_random.rb +0 -8
- data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
- data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
- data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
- data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
- data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
- data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
- data/lib/carray/autoload/autoload_object_link.rb +0 -1
- data/lib/carray/autoload/autoload_object_pack.rb +0 -2
- data/lib/carray/autoload.rb +0 -141
- data/lib/carray/basic.rb +0 -191
- data/lib/carray/broadcast.rb +0 -101
- data/lib/carray/compose.rb +0 -315
- data/lib/carray/convert.rb +0 -115
- data/lib/carray/info.rb +0 -110
- data/lib/carray/io/imagemagick.rb +0 -235
- data/lib/carray/mask.rb +0 -102
- data/lib/carray/math/histogram.rb +0 -177
- data/lib/carray/math/recurrence.rb +0 -93
- data/lib/carray/object/ca_obj_iterator.rb +0 -50
- data/lib/carray/object/ca_obj_link.rb +0 -50
- data/lib/carray/object/ca_obj_pack.rb +0 -99
- data/lib/carray/obsolete.rb +0 -256
- data/lib/carray/ordering.rb +0 -181
- data/lib/carray/testing.rb +0 -51
- data/lib/carray/transform.rb +0 -109
- data/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/carray_core.c
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/* ---------------------------------------------------------------------------
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Copyright (C) 2005-2020 Hiroki Motoyoshi
|
|
3
|
+
Core runtime: obj_type registration (ca_install_obj_type), the
|
|
4
|
+
ca_func / ca_class / ca_typeddata dispatch tables, TypedData mark /
|
|
5
|
+
free, the attach lifecycle, and the per-cell / per-region / whole-view
|
|
6
|
+
transfer primitives (ca_xfer_index / _addrs / _stride / _all).
|
|
8
7
|
|
|
9
8
|
---------------------------------------------------------------------------- */
|
|
10
9
|
|
|
11
10
|
#include "carray.h"
|
|
11
|
+
#include "carray_internal.h" /* ca_lazy_arena_enter / _exit */
|
|
12
|
+
#include "ca_iter_substrate.h"
|
|
12
13
|
#include <stdarg.h>
|
|
13
14
|
|
|
14
15
|
/* definition of ca_endian */
|
|
@@ -92,11 +93,7 @@ ca_valid[CA_NTYPE] = {
|
|
|
92
93
|
#else
|
|
93
94
|
0,
|
|
94
95
|
#endif
|
|
95
|
-
|
|
96
|
-
1,
|
|
97
|
-
#else
|
|
98
|
-
0,
|
|
99
|
-
#endif
|
|
96
|
+
0, /* CA_FLOAT128 (not built) */
|
|
100
97
|
#ifdef HAVE_TYPE_CMPLX64_T
|
|
101
98
|
1,
|
|
102
99
|
#else
|
|
@@ -107,11 +104,7 @@ ca_valid[CA_NTYPE] = {
|
|
|
107
104
|
#else
|
|
108
105
|
0,
|
|
109
106
|
#endif
|
|
110
|
-
|
|
111
|
-
1,
|
|
112
|
-
#else
|
|
113
|
-
0,
|
|
114
|
-
#endif
|
|
107
|
+
0, /* CA_CMPLX256 (not built) */
|
|
115
108
|
1
|
|
116
109
|
};
|
|
117
110
|
|
|
@@ -131,10 +124,10 @@ ca_sizeof[CA_NTYPE] = {
|
|
|
131
124
|
sizeof(uint64_t),
|
|
132
125
|
sizeof(float32_t),
|
|
133
126
|
sizeof(float64_t),
|
|
134
|
-
|
|
127
|
+
0, /* float128_t (not built) */
|
|
135
128
|
sizeof(cmplx64_t),
|
|
136
129
|
sizeof(cmplx128_t),
|
|
137
|
-
|
|
130
|
+
0, /* cmplx256_t (not built) */
|
|
138
131
|
sizeof(VALUE),
|
|
139
132
|
};
|
|
140
133
|
|
|
@@ -154,10 +147,10 @@ ca_type_name[CA_NTYPE] = {
|
|
|
154
147
|
"uint64",
|
|
155
148
|
"float32",
|
|
156
149
|
"float64",
|
|
157
|
-
"float128",
|
|
150
|
+
"(retired:float128)",
|
|
158
151
|
"cmplx64",
|
|
159
152
|
"cmplx128",
|
|
160
|
-
"cmplx256",
|
|
153
|
+
"(retired:cmplx256)",
|
|
161
154
|
"object",
|
|
162
155
|
};
|
|
163
156
|
|
|
@@ -242,16 +235,14 @@ ca_cast_table2[CA_NTYPE][CA_NTYPE] = {
|
|
|
242
235
|
*/
|
|
243
236
|
|
|
244
237
|
void
|
|
245
|
-
ca_init_obj_type ()
|
|
238
|
+
ca_init_obj_type (void)
|
|
246
239
|
{
|
|
247
240
|
extern ca_operation_function_t ca_array_func;
|
|
248
241
|
extern ca_operation_function_t ca_wrap_func;
|
|
249
242
|
extern ca_operation_function_t ca_scalar_func;
|
|
250
|
-
extern ca_operation_function_t ca_refer_func;
|
|
251
|
-
extern ca_operation_function_t ca_block_func;
|
|
252
243
|
extern ca_operation_function_t ca_select_func;
|
|
253
244
|
extern ca_operation_function_t ca_object_func;
|
|
254
|
-
extern ca_operation_function_t
|
|
245
|
+
extern ca_operation_function_t ca_stride_func;
|
|
255
246
|
extern ca_operation_function_t ca_ubrep_func;
|
|
256
247
|
|
|
257
248
|
/* CArray */
|
|
@@ -275,15 +266,22 @@ ca_init_obj_type ()
|
|
|
275
266
|
ca_mask_class[CA_OBJ_SCALAR] = rb_cCArrayMask;
|
|
276
267
|
ca_mask_typeddata[CA_OBJ_SCALAR] = &carray_mask_data_type;
|
|
277
268
|
|
|
278
|
-
/* CARefer
|
|
279
|
-
|
|
269
|
+
/* CARefer (CAStride subclass). The function table is
|
|
270
|
+
installed as ca_stride_func here as a baseline; Init_ca_obj_refer
|
|
271
|
+
overrides ca_func[CA_OBJ_REFER] with a copy that has custom
|
|
272
|
+
free_object (frees the mask0 tail) and custom create_mask
|
|
273
|
+
(handles byte-reinterpret cases). */
|
|
274
|
+
ca_func[CA_OBJ_REFER] = ca_stride_func;
|
|
280
275
|
ca_class[CA_OBJ_REFER] = rb_cCARefer;
|
|
281
276
|
ca_typeddata[CA_OBJ_REFER] = &carefer_data_type;
|
|
282
277
|
ca_mask_class[CA_OBJ_REFER] = rb_cCAReferMask;
|
|
283
278
|
ca_mask_typeddata[CA_OBJ_REFER] = &carefer_mask_data_type;
|
|
284
279
|
|
|
285
|
-
/* CABlock
|
|
286
|
-
|
|
280
|
+
/* CABlock (CAStride subclass). Baseline registered to
|
|
281
|
+
ca_stride_func; Init_ca_obj_block overrides with a copy that has
|
|
282
|
+
custom free_object (frees the tail arrays) and custom create_mask
|
|
283
|
+
(builds the mask as a CABlock with matching block parameters). */
|
|
284
|
+
ca_func[CA_OBJ_BLOCK] = ca_stride_func;
|
|
287
285
|
ca_class[CA_OBJ_BLOCK] = rb_cCABlock;
|
|
288
286
|
ca_typeddata[CA_OBJ_BLOCK] = &cablock_data_type;
|
|
289
287
|
ca_mask_class[CA_OBJ_BLOCK] = rb_cCABlockMask;
|
|
@@ -303,12 +301,13 @@ ca_init_obj_type ()
|
|
|
303
301
|
ca_mask_class[CA_OBJ_OBJECT] = rb_cCArrayMask;
|
|
304
302
|
ca_mask_typeddata[CA_OBJ_OBJECT] = &carray_mask_data_type;
|
|
305
303
|
|
|
306
|
-
/* CARepeat
|
|
307
|
-
|
|
304
|
+
/* CARepeat (subclass of CAStride; shares its function table and
|
|
305
|
+
TypedData entirely). */
|
|
306
|
+
ca_func[CA_OBJ_REPEAT] = ca_stride_func;
|
|
308
307
|
ca_class[CA_OBJ_REPEAT] = rb_cCARepeat;
|
|
309
|
-
ca_typeddata[CA_OBJ_REPEAT] = &
|
|
308
|
+
ca_typeddata[CA_OBJ_REPEAT] = &castride_data_type;
|
|
310
309
|
ca_mask_class[CA_OBJ_REPEAT] = rb_cCARepeatMask;
|
|
311
|
-
ca_mask_typeddata[CA_OBJ_REPEAT] = &
|
|
310
|
+
ca_mask_typeddata[CA_OBJ_REPEAT] = &castride_mask_data_type;
|
|
312
311
|
|
|
313
312
|
/* CAUnboundRepeat */
|
|
314
313
|
ca_func[CA_OBJ_UNBOUND_REPEAT] = ca_ubrep_func;
|
|
@@ -325,11 +324,12 @@ ca_init_obj_type ()
|
|
|
325
324
|
*/
|
|
326
325
|
|
|
327
326
|
int
|
|
328
|
-
ca_install_obj_type (VALUE klass,
|
|
329
|
-
const rb_data_type_t *typeddata,
|
|
330
|
-
VALUE mask_klass,
|
|
331
|
-
const rb_data_type_t *mask_typeddata,
|
|
332
|
-
ca_operation_function_t func
|
|
327
|
+
ca_install_obj_type (VALUE klass,
|
|
328
|
+
const rb_data_type_t *typeddata,
|
|
329
|
+
VALUE mask_klass,
|
|
330
|
+
const rb_data_type_t *mask_typeddata,
|
|
331
|
+
const ca_operation_function_t *func,
|
|
332
|
+
size_t func_size)
|
|
333
333
|
{
|
|
334
334
|
int obj_type = ca_obj_num++;
|
|
335
335
|
|
|
@@ -339,10 +339,31 @@ ca_install_obj_type (VALUE klass,
|
|
|
339
339
|
CA_OBJ_TYPE_MAX);
|
|
340
340
|
}
|
|
341
341
|
|
|
342
|
-
|
|
342
|
+
if ( func_size > sizeof(ca_operation_function_t) ) {
|
|
343
|
+
rb_raise(rb_eRuntimeError,
|
|
344
|
+
"operation table is larger than this carray's (%zu > %zu); "
|
|
345
|
+
"the caller was built against a newer carray",
|
|
346
|
+
func_size, sizeof(ca_operation_function_t));
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/* Everything from xfer_index on was appended after the table's first
|
|
350
|
+
shape, so a caller may legitimately stop short of it. Anything shorter
|
|
351
|
+
than that cannot dispatch at all. */
|
|
352
|
+
if ( func_size < offsetof(ca_operation_function_t, xfer_index) ) {
|
|
353
|
+
rb_raise(rb_eRuntimeError,
|
|
354
|
+
"operation table is too small to dispatch (%zu < %zu)",
|
|
355
|
+
func_size,
|
|
356
|
+
(size_t) offsetof(ca_operation_function_t, xfer_index));
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/* Copy by the caller's length and zero the rest: slots this build knows
|
|
360
|
+
about but the caller does not are NULL, which every dispatcher already
|
|
361
|
+
reads as "not provided". */
|
|
362
|
+
MEMZERO(&ca_func[obj_type], ca_operation_function_t, 1);
|
|
363
|
+
memcpy(&ca_func[obj_type], func, func_size);
|
|
364
|
+
ca_func[obj_type].obj_type = obj_type;
|
|
343
365
|
|
|
344
366
|
ca_class[obj_type] = klass;
|
|
345
|
-
ca_func[obj_type] = func;
|
|
346
367
|
ca_typeddata[obj_type] = typeddata;
|
|
347
368
|
ca_mask_class[obj_type] = mask_klass;
|
|
348
369
|
ca_mask_typeddata[obj_type] = mask_typeddata;
|
|
@@ -396,47 +417,401 @@ ca_free_nop (void *ap)
|
|
|
396
417
|
|
|
397
418
|
/* ------------------------------------------------------------------- */
|
|
398
419
|
|
|
420
|
+
/* Returns true iff ca_attach(ca) is essentially O(1) (= no malloc /
|
|
421
|
+
copy):
|
|
422
|
+
- entity arrays (CA_REAL_ARRAY: already attached, ca->ptr valid)
|
|
423
|
+
- CAStride-family views whose composed strides are row-major
|
|
424
|
+
contiguous (the alias path takes parent->ptr + base_offset
|
|
425
|
+
without allocating)
|
|
426
|
+
Used by kernel_iterator's L1 alias decision (ca_iter_can_alias level 1)
|
|
427
|
+
and by overlay view dispatch. The name says "is_alias" (a structural
|
|
428
|
+
property) rather than "is_cheap" (a cost claim): the predicate checks
|
|
429
|
+
whether ca can be aliased without materialise. */
|
|
430
|
+
int
|
|
431
|
+
ca_attach_is_alias (void *ap)
|
|
432
|
+
{
|
|
433
|
+
CArray *ca = (CArray *) ap;
|
|
434
|
+
extern ca_operation_function_t ca_stride_func;
|
|
435
|
+
extern int ca_stride_is_contiguous (CAStride *ca);
|
|
436
|
+
extern int ca_stride_attach_aliases_root (CAStride *ca);
|
|
437
|
+
|
|
438
|
+
if ( ca == NULL ) return 0;
|
|
439
|
+
if ( ca_is_entity(ca) ) return 1;
|
|
440
|
+
/* CAStride family share ca_stride_func.attach (= ca_stride_func_attach).
|
|
441
|
+
The alias-attach fast path is taken iff composed strides are
|
|
442
|
+
row-major contiguous. ca_stride_is_contiguous checks the leaf
|
|
443
|
+
view's own strides, which is what we want — the compose-fold to
|
|
444
|
+
root happens during attach itself; if leaf is contig and parent
|
|
445
|
+
chain is too (transitively, since each CAStride's strides are
|
|
446
|
+
composed against parent's), the alias path fires.
|
|
447
|
+
|
|
448
|
+
...and iff there is parent memory to alias at the end of that fold.
|
|
449
|
+
A non-entity root has none to lend, so attach builds its own buffer
|
|
450
|
+
and writes through ca->ptr reach the root only via ca_sync. Saying
|
|
451
|
+
"alias" there would let a caller write and skip the sync. */
|
|
452
|
+
if ( ca_func[ca->obj_type].attach == ca_stride_func.attach ) {
|
|
453
|
+
return ca_stride_is_contiguous((CAStride *) ca)
|
|
454
|
+
&& ca_stride_attach_aliases_root((CAStride *) ca);
|
|
455
|
+
}
|
|
456
|
+
return 0;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/* ------------------------------------------------------------------- */
|
|
460
|
+
|
|
461
|
+
/* Allocate a reduction-output CArray for kernel_iterator authors.
|
|
462
|
+
|
|
463
|
+
Arguments:
|
|
464
|
+
self Ruby VALUE wrapping the input CArray (source of the reduction).
|
|
465
|
+
slab_axes sort-ascending list of axis indices that the kernel
|
|
466
|
+
will walk per slab (= the axes removed from the output).
|
|
467
|
+
May contain any K in [1..self.ndim]; the helper
|
|
468
|
+
validates range and uniqueness.
|
|
469
|
+
naxes length of slab_axes. Must satisfy 0 < naxes <= self.ndim.
|
|
470
|
+
data_type output data_type (CA_INT32 / CA_FLOAT64 / ... — any numeric
|
|
471
|
+
data_type with non-zero ca_sizeof[]). May differ from
|
|
472
|
+
self's data_type (e.g. mean of int32 → float64).
|
|
473
|
+
|
|
474
|
+
Output shape:
|
|
475
|
+
- Partial reduction (naxes < self.ndim): self.dim with slab axes
|
|
476
|
+
removed in ascending order, ndim = self.ndim - naxes.
|
|
477
|
+
- Full reduction (naxes == self.ndim): shape [1] 1-D CArray
|
|
478
|
+
(kernel writes op[0] and the author wraps the result to a Ruby
|
|
479
|
+
Float / CScalar at their own discretion).
|
|
480
|
+
|
|
481
|
+
Same axis-validation rules as init_l2 CA_SLAB_AXES (= duplicates and
|
|
482
|
+
out-of-range raise ArgumentError so author input bugs surface here
|
|
483
|
+
rather than at the slab walk). Mask is NULL on the output (=
|
|
484
|
+
reduction kernels populate it themselves if needed). */
|
|
485
|
+
VALUE
|
|
486
|
+
rb_ca_new_reduced_bytes (VALUE self, int8_t *slab_axes, int8_t naxes,
|
|
487
|
+
int32_t data_type, ca_size_t bytes, int keep_axis)
|
|
488
|
+
{
|
|
489
|
+
CArray *ca;
|
|
490
|
+
int8_t k;
|
|
491
|
+
|
|
492
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
493
|
+
|
|
494
|
+
if ( naxes <= 0 || naxes > ca->ndim ) {
|
|
495
|
+
rb_raise(rb_eArgError,
|
|
496
|
+
"rb_ca_new_reduced: naxes=%d invalid for ndim=%d",
|
|
497
|
+
(int) naxes, (int) ca->ndim);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
int8_t in_slab[CA_RANK_MAX];
|
|
501
|
+
for ( k = 0; k < CA_RANK_MAX; k++ ) in_slab[k] = 0;
|
|
502
|
+
for ( k = 0; k < naxes; k++ ) {
|
|
503
|
+
int8_t ax = slab_axes[k];
|
|
504
|
+
if ( ax < 0 || ax >= ca->ndim ) {
|
|
505
|
+
rb_raise(rb_eArgError,
|
|
506
|
+
"rb_ca_new_reduced: slab_axes[%d]=%d out of range [0, %d)",
|
|
507
|
+
(int) k, (int) ax, (int) ca->ndim);
|
|
508
|
+
}
|
|
509
|
+
if ( in_slab[ax] ) {
|
|
510
|
+
rb_raise(rb_eArgError,
|
|
511
|
+
"rb_ca_new_reduced: duplicate axis %d in slab_axes", (int) ax);
|
|
512
|
+
}
|
|
513
|
+
in_slab[ax] = 1;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
ca_size_t out_dim[CA_RANK_MAX];
|
|
517
|
+
int8_t out_ndim = 0;
|
|
518
|
+
for ( k = 0; k < ca->ndim; k++ ) {
|
|
519
|
+
if ( !in_slab[k] ) {
|
|
520
|
+
out_dim[out_ndim++] = ca->dim[k];
|
|
521
|
+
}
|
|
522
|
+
else if ( keep_axis ) {
|
|
523
|
+
/* keep_axis: retain each reduced axis as a length-1 axis instead
|
|
524
|
+
of dropping it (= automation of view[..., :_]).
|
|
525
|
+
Element count and row-major order are unchanged, so the kernel
|
|
526
|
+
slab walk writes the output identically. */
|
|
527
|
+
out_dim[out_ndim++] = 1;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
if ( out_ndim == 0 ) {
|
|
531
|
+
/* Full reduction without keep_axis: collapse to a 1-element array.
|
|
532
|
+
(With keep_axis, full reduction already produced [1, 1, ..., 1]
|
|
533
|
+
above so out_ndim == ca->ndim and this branch is not taken.) */
|
|
534
|
+
out_dim[0] = 1;
|
|
535
|
+
out_ndim = 1;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if ( data_type < 0 || data_type >= CA_NTYPE ) {
|
|
539
|
+
rb_raise(rb_eArgError,
|
|
540
|
+
"rb_ca_new_reduced: data_type=%d out of range", (int) data_type);
|
|
541
|
+
}
|
|
542
|
+
if ( bytes <= 0 ) {
|
|
543
|
+
rb_raise(rb_eArgError,
|
|
544
|
+
"rb_ca_new_reduced: bytes=%ld invalid for data_type=%d",
|
|
545
|
+
(long) bytes, (int) data_type);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
return rb_carray_new(data_type, out_ndim, out_dim, bytes, NULL);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/* Fixed-element-size wrapper: the byte width is looked up from ca_sizeof,
|
|
552
|
+
which rejects CA_FIXLEN / CA_OBJECT (runtime-width / VALUE cells). A
|
|
553
|
+
reduction whose output is a runtime-width data_type (fixlen min / max)
|
|
554
|
+
must call rb_ca_new_reduced_bytes with the source's own byte width. */
|
|
555
|
+
VALUE
|
|
556
|
+
rb_ca_new_reduced (VALUE self, int8_t *slab_axes, int8_t naxes, int32_t data_type,
|
|
557
|
+
int keep_axis)
|
|
558
|
+
{
|
|
559
|
+
if ( data_type < 0 || data_type >= CA_NTYPE ) {
|
|
560
|
+
rb_raise(rb_eArgError,
|
|
561
|
+
"rb_ca_new_reduced: data_type=%d out of range", (int) data_type);
|
|
562
|
+
}
|
|
563
|
+
ca_size_t bytes = ca_sizeof[data_type];
|
|
564
|
+
if ( bytes <= 0 ) {
|
|
565
|
+
rb_raise(rb_eArgError,
|
|
566
|
+
"rb_ca_new_reduced: data_type=%d unsupported (CA_FIXLEN/OBJECT not supported)",
|
|
567
|
+
(int) data_type);
|
|
568
|
+
}
|
|
569
|
+
return rb_ca_new_reduced_bytes(self, slab_axes, naxes, data_type, bytes, keep_axis);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/* ------------------------------------------------------------------- */
|
|
573
|
+
|
|
574
|
+
/* Parse the variadic axis argument of a reduction kernel into a
|
|
575
|
+
sort-ascending
|
|
576
|
+
int8_t array, with full validation.
|
|
577
|
+
|
|
578
|
+
Accepts:
|
|
579
|
+
- Integer args: kernel(0, 2, 3) → axes = {0, 2, 3}
|
|
580
|
+
- Single Array arg: kernel([0, 2, 3]) → axes = {0, 2, 3}
|
|
581
|
+
- Negative axes (Python-style): -1 = innermost, normalised to
|
|
582
|
+
positive in-range indices before validation
|
|
583
|
+
|
|
584
|
+
Validation (raises ArgumentError on failure):
|
|
585
|
+
- argc == 0 → no axes given
|
|
586
|
+
- naxes > ca->ndim → too many axes
|
|
587
|
+
- any axis out of [0, ca->ndim) → range
|
|
588
|
+
- duplicate axes → duplicate
|
|
589
|
+
|
|
590
|
+
Returns the validated naxes (= count of axes written to out_axes[]).
|
|
591
|
+
out_axes[] is filled with the parsed axes in *input order* (= NOT
|
|
592
|
+
pre-sorted; canonicalisation to ascending order happens inside
|
|
593
|
+
init_l2 CA_SLAB_AXES, so callers can pass the user's order directly). */
|
|
594
|
+
/* Core validation: takes raw items[] (each must be Integer / Symbol-able
|
|
595
|
+
to NUM2SIZE) of length `count`, normalises + range-checks + duplicate-
|
|
596
|
+
checks, fills out_axes[] in input order. Shared between the legacy
|
|
597
|
+
variadic entry (rb_ca_parse_reduce_axes) and the kwarg entry
|
|
598
|
+
(rb_ca_parse_reduce_axes_kw). ctx is a short label embedded in error
|
|
599
|
+
messages so callers can disambiguate which entry raised. */
|
|
600
|
+
static int8_t
|
|
601
|
+
parse_axes_items (const VALUE *items, int count, CArray *ca,
|
|
602
|
+
int8_t *out_axes, const char *ctx)
|
|
603
|
+
{
|
|
604
|
+
int i;
|
|
605
|
+
int8_t seen[CA_RANK_MAX];
|
|
606
|
+
|
|
607
|
+
if ( count <= 0 ) {
|
|
608
|
+
rb_raise(rb_eArgError, "%s: empty axes array", ctx);
|
|
609
|
+
}
|
|
610
|
+
if ( count > CA_RANK_MAX ) {
|
|
611
|
+
rb_raise(rb_eArgError,
|
|
612
|
+
"%s: too many axes (%d > CA_RANK_MAX=%d)",
|
|
613
|
+
ctx, count, CA_RANK_MAX);
|
|
614
|
+
}
|
|
615
|
+
if ( count > ca->ndim ) {
|
|
616
|
+
rb_raise(rb_eArgError,
|
|
617
|
+
"%s: too many axes (%d > ndim=%d)",
|
|
618
|
+
ctx, count, (int) ca->ndim);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
for ( i = 0; i < CA_RANK_MAX; i++ ) seen[i] = 0;
|
|
622
|
+
for ( i = 0; i < count; i++ ) {
|
|
623
|
+
ca_size_t a = NUM2SIZE(items[i]);
|
|
624
|
+
if ( a < 0 ) a += ca->ndim;
|
|
625
|
+
if ( a < 0 || a >= ca->ndim ) {
|
|
626
|
+
rb_raise(rb_eIndexError,
|
|
627
|
+
"%s: axis %ld out of range [0, %d)",
|
|
628
|
+
ctx, (long) a, (int) ca->ndim);
|
|
629
|
+
}
|
|
630
|
+
if ( seen[a] ) {
|
|
631
|
+
rb_raise(rb_eArgError,
|
|
632
|
+
"%s: duplicate axis %ld", ctx, (long) a);
|
|
633
|
+
}
|
|
634
|
+
seen[a] = 1;
|
|
635
|
+
out_axes[i] = (int8_t) a;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
return (int8_t) count;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
int8_t
|
|
642
|
+
rb_ca_parse_reduce_axes (int argc, VALUE *argv, CArray *ca, int8_t *out_axes)
|
|
643
|
+
{
|
|
644
|
+
int i;
|
|
645
|
+
|
|
646
|
+
/* argc == 0 means "full reduction over all axes" -- matches legacy
|
|
647
|
+
CArray#sum etc. This contract keeps the ki kernels drop-in
|
|
648
|
+
replacements for the legacy stat dispatchers. */
|
|
649
|
+
if ( argc <= 0 ) {
|
|
650
|
+
for ( i = 0; i < ca->ndim; i++ ) {
|
|
651
|
+
out_axes[i] = (int8_t) i;
|
|
652
|
+
}
|
|
653
|
+
return (int8_t) ca->ndim;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/* Detect single-Array call form: foo([0, 2]) */
|
|
657
|
+
const VALUE *items = (const VALUE *) argv;
|
|
658
|
+
int count = argc;
|
|
659
|
+
if ( argc == 1 && TYPE(argv[0]) == T_ARRAY ) {
|
|
660
|
+
VALUE arr = argv[0];
|
|
661
|
+
count = (int) RARRAY_LEN(arr);
|
|
662
|
+
items = (const VALUE *) RARRAY_CONST_PTR(arr);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
return parse_axes_items(items, count, ca, out_axes,
|
|
666
|
+
"rb_ca_parse_reduce_axes");
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/* Kwarg form of rb_ca_parse_reduce_axes — accepts the `axis:` value as
|
|
670
|
+
extracted by the caller via rb_scan_args(..., "0:", &kw_hash) +
|
|
671
|
+
rb_get_kwargs (or equivalent), and dispatches:
|
|
672
|
+
|
|
673
|
+
axis_val == Qnil or Qundef → full reduction (= all axes)
|
|
674
|
+
axis_val Integer → single axis (negative normalised)
|
|
675
|
+
axis_val Array of Integer → multiple axes in input order
|
|
676
|
+
anything else → TypeError
|
|
677
|
+
|
|
678
|
+
Validation (range / duplicates / overflow) is identical to the
|
|
679
|
+
variadic entry. out_axes[] receives axes in input order. Returns
|
|
680
|
+
the validated naxes. */
|
|
681
|
+
int8_t
|
|
682
|
+
rb_ca_parse_reduce_axes_kw_ctx (VALUE axis_val, CArray *ca, int8_t *out_axes,
|
|
683
|
+
const char *ctx)
|
|
684
|
+
{
|
|
685
|
+
int i;
|
|
686
|
+
|
|
687
|
+
if ( axis_val == Qnil || axis_val == Qundef ) {
|
|
688
|
+
for ( i = 0; i < ca->ndim; i++ ) {
|
|
689
|
+
out_axes[i] = (int8_t) i;
|
|
690
|
+
}
|
|
691
|
+
return (int8_t) ca->ndim;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
if ( TYPE(axis_val) == T_ARRAY ) {
|
|
695
|
+
int count = (int) RARRAY_LEN(axis_val);
|
|
696
|
+
const VALUE *items = (const VALUE *) RARRAY_CONST_PTR(axis_val);
|
|
697
|
+
return parse_axes_items(items, count, ca, out_axes, ctx);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
if ( rb_obj_is_kind_of(axis_val, rb_cInteger) ) {
|
|
701
|
+
return parse_axes_items(&axis_val, 1, ca, out_axes, ctx);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
rb_raise(rb_eTypeError,
|
|
705
|
+
"%s: axis: must be nil, Integer, or "
|
|
706
|
+
"Array of Integer (got %"PRIsVALUE")",
|
|
707
|
+
ctx, rb_obj_class(axis_val));
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
int8_t
|
|
711
|
+
rb_ca_parse_reduce_axes_kw (VALUE axis_val, CArray *ca, int8_t *out_axes)
|
|
712
|
+
{
|
|
713
|
+
return rb_ca_parse_reduce_axes_kw_ctx(axis_val, ca, out_axes,
|
|
714
|
+
"rb_ca_parse_reduce_axes_kw");
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/* ------------------------------------------------------------------- */
|
|
718
|
+
|
|
719
|
+
/* api: ca_wrap_struct_as
|
|
720
|
+
wraps CArray struct in C -> Ruby's object, with the Ruby class chosen
|
|
721
|
+
by the caller instead of taken from ca_class[obj_type].
|
|
722
|
+
|
|
723
|
+
The TypedData tag still comes from obj_type. Only the class is the
|
|
724
|
+
caller's; the tag is what GetCArray and every dispatch path look at,
|
|
725
|
+
so decoupling the two leaves those paths untouched. klass must be a
|
|
726
|
+
subclass of the class registered for obj_type -- that is the caller's
|
|
727
|
+
responsibility, not checked here.
|
|
728
|
+
*/
|
|
729
|
+
|
|
730
|
+
VALUE
|
|
731
|
+
ca_wrap_struct_as (void *ap, VALUE klass)
|
|
732
|
+
{
|
|
733
|
+
CArray *ca = (CArray *) ap;
|
|
734
|
+
return TypedData_Wrap_Struct(klass, ca_typeddata[ca->obj_type], ca);
|
|
735
|
+
}
|
|
736
|
+
|
|
399
737
|
/* api: ca_wrap_struct
|
|
400
|
-
wraps CArray struct in C -> Ruby's object
|
|
738
|
+
wraps CArray struct in C -> Ruby's object
|
|
401
739
|
*/
|
|
402
740
|
|
|
403
741
|
VALUE
|
|
404
742
|
ca_wrap_struct (void *ap)
|
|
405
743
|
{
|
|
406
744
|
CArray *ca = (CArray *) ap;
|
|
407
|
-
return
|
|
745
|
+
return ca_wrap_struct_as(ap, ca_class[ca->obj_type]);
|
|
408
746
|
}
|
|
409
747
|
|
|
410
748
|
/* ------------------------------------------------------------------- */
|
|
411
749
|
|
|
412
|
-
/* calculate index from address
|
|
750
|
+
/* calculate index from address.
|
|
751
|
+
Hot path: called per fetch_addr dispatch when the view has no
|
|
752
|
+
dedicated fetch_addr slot (= CAStride family and others that only
|
|
753
|
+
implement fetch_index). 1-D / 2-D / 3-D fast paths skip the
|
|
754
|
+
generic divmod loop; higher-ndim falls through. */
|
|
413
755
|
|
|
414
756
|
void
|
|
415
757
|
ca_addr2index (void *ap, ca_size_t addr, ca_size_t *idx)
|
|
416
758
|
{
|
|
417
759
|
CArray *ca = (CArray *) ap;
|
|
418
760
|
ca_size_t *dim = ca->dim;
|
|
761
|
+
int8_t ndim = ca->ndim;
|
|
419
762
|
int8_t i;
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
763
|
+
switch (ndim) {
|
|
764
|
+
case 1:
|
|
765
|
+
idx[0] = addr;
|
|
766
|
+
return;
|
|
767
|
+
case 2: {
|
|
768
|
+
ca_size_t d1 = dim[1];
|
|
769
|
+
idx[1] = addr % d1;
|
|
770
|
+
idx[0] = addr / d1;
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
case 3: {
|
|
774
|
+
ca_size_t d1 = dim[1], d2 = dim[2];
|
|
775
|
+
idx[2] = addr % d2;
|
|
776
|
+
addr /= d2;
|
|
777
|
+
idx[1] = addr % d1;
|
|
778
|
+
idx[0] = addr / d1;
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
default:
|
|
782
|
+
for (i = ndim - 1; i >= 0; i--) {
|
|
783
|
+
idx[i] = addr % dim[i];
|
|
784
|
+
addr /= dim[i];
|
|
785
|
+
}
|
|
423
786
|
}
|
|
424
787
|
}
|
|
425
788
|
|
|
426
|
-
/* calculate address from index
|
|
789
|
+
/* calculate address from index. 1-D / 2-D / 3-D fast paths skip the
|
|
790
|
+
loop entirely. Compiler typically inlines the generic loop fine
|
|
791
|
+
but the fast paths help where this function is called via pointer. */
|
|
427
792
|
|
|
428
793
|
ca_size_t
|
|
429
794
|
ca_index2addr (void *ap, ca_size_t *idx)
|
|
430
795
|
{
|
|
431
796
|
CArray *ca = (CArray *) ap;
|
|
432
797
|
ca_size_t *dim = ca->dim;
|
|
433
|
-
int8_t
|
|
434
|
-
|
|
435
|
-
n
|
|
436
|
-
|
|
437
|
-
|
|
798
|
+
int8_t ndim = ca->ndim;
|
|
799
|
+
int8_t i;
|
|
800
|
+
ca_size_t n;
|
|
801
|
+
switch (ndim) {
|
|
802
|
+
case 1:
|
|
803
|
+
return idx[0];
|
|
804
|
+
case 2:
|
|
805
|
+
return dim[1] * idx[0] + idx[1];
|
|
806
|
+
case 3:
|
|
807
|
+
return (dim[1] * idx[0] + idx[1]) * dim[2] + idx[2];
|
|
808
|
+
default:
|
|
809
|
+
n = idx[0];
|
|
810
|
+
for (i = 1; i < ndim; i++) {
|
|
811
|
+
n = dim[i] * n + idx[i];
|
|
812
|
+
}
|
|
813
|
+
return n;
|
|
438
814
|
}
|
|
439
|
-
return n;
|
|
440
815
|
}
|
|
441
816
|
|
|
442
817
|
/* ------------------------------------------------------------------- */
|
|
@@ -489,169 +864,706 @@ ca_test_cyclic_check(void *ap, void *ptr)
|
|
|
489
864
|
}
|
|
490
865
|
|
|
491
866
|
/* ------------------------------------------------------------------- */
|
|
867
|
+
/* CArray offers no per-cell ptr accessor (ca_ptr_at_addr /
|
|
868
|
+
ca_ptr_at_index): such a slot is structurally unsafe -- CABitarray /
|
|
869
|
+
CABitfield have no byte-addressable cell, and CAByteSwap / CAFake would
|
|
870
|
+
hand back bytes in the wrong data_type / endian. Internal code that
|
|
871
|
+
already holds an attached view uses direct `ca->ptr + ca->bytes * addr`
|
|
872
|
+
arithmetic; external ext gems use ca_fetch_addr / ca_fetch_index
|
|
873
|
+
(data_type-correct via the xfer_addrs / xfer_index dispatch) for
|
|
874
|
+
per-cell access. */
|
|
875
|
+
|
|
876
|
+
/* fetch / store at a single linear address: thin wrappers over
|
|
877
|
+
ca_xfer_addrs. */
|
|
492
878
|
|
|
493
|
-
|
|
879
|
+
void
|
|
880
|
+
ca_fetch_addr (void *ap, ca_size_t addr, void *pval)
|
|
881
|
+
{
|
|
882
|
+
ca_xfer_addrs(ap, 1, &addr, pval, CA_XFER_GET);
|
|
883
|
+
}
|
|
494
884
|
|
|
495
|
-
void
|
|
496
|
-
|
|
885
|
+
void
|
|
886
|
+
ca_store_addr (void *ap, ca_size_t addr, void *pval)
|
|
497
887
|
{
|
|
498
|
-
|
|
888
|
+
ca_xfer_addrs(ap, 1, &addr, pval, CA_XFER_PUT);
|
|
889
|
+
}
|
|
499
890
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
case CA_OBJ_REFER:
|
|
505
|
-
return ((CARefer*)ca)->parent->ptr + ca->bytes * addr;
|
|
506
|
-
default:
|
|
507
|
-
return ca->ptr + ca->bytes * addr;
|
|
508
|
-
}
|
|
509
|
-
}
|
|
891
|
+
/* per-cell transfer by multi-dim index. ca_xfer_index is the primary
|
|
892
|
+
entry; ca_fetch_index / ca_store_index are thin wrappers (kept as
|
|
893
|
+
public C-API for external ext gems). Every view supplies an
|
|
894
|
+
xfer_index slot. */
|
|
510
895
|
|
|
511
|
-
|
|
896
|
+
static inline void
|
|
897
|
+
ca_xfer_index_dispatch (CArray *ca, ca_size_t *idx, void *data, int dir)
|
|
898
|
+
{
|
|
899
|
+
if ( ! ca_func[ca->obj_type].xfer_index ) {
|
|
900
|
+
rb_raise(rb_eRuntimeError,
|
|
901
|
+
"[BUG] xfer_index not defined for object type <%i>",
|
|
902
|
+
ca->obj_type);
|
|
903
|
+
}
|
|
904
|
+
ca_func[ca->obj_type].xfer_index(ca, idx, data, dir);
|
|
512
905
|
}
|
|
513
906
|
|
|
514
|
-
|
|
907
|
+
struct ca_xfer_index_args {
|
|
908
|
+
CArray *ca;
|
|
909
|
+
ca_size_t *idx;
|
|
910
|
+
char *ptr;
|
|
911
|
+
};
|
|
515
912
|
|
|
516
|
-
|
|
517
|
-
|
|
913
|
+
static VALUE
|
|
914
|
+
ca_xfer_index_get_body (VALUE arg)
|
|
518
915
|
{
|
|
519
|
-
|
|
520
|
-
|
|
916
|
+
struct ca_xfer_index_args *d = (struct ca_xfer_index_args *) arg;
|
|
917
|
+
ca_xfer_index_dispatch(d->ca, d->idx, d->ptr, CA_XFER_GET);
|
|
918
|
+
ca_test_cyclic_check(d->ca, d->ptr);
|
|
919
|
+
return Qnil;
|
|
521
920
|
}
|
|
522
921
|
|
|
523
|
-
/* fetch data of the element at given address to memory pointed by pval */
|
|
524
|
-
|
|
525
922
|
void
|
|
526
|
-
|
|
923
|
+
ca_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
|
|
527
924
|
{
|
|
528
925
|
CArray *ca = (CArray *) ap;
|
|
529
|
-
char *ptr = (char *)pval;
|
|
530
926
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
if ( ca->ptr ) {
|
|
534
|
-
memcpy(ptr, ca->ptr + ca->bytes * addr, ca->bytes);
|
|
927
|
+
if ( dir == CA_XFER_PUT && ca_is_readonly(ca) ) {
|
|
928
|
+
rb_raise(rb_eRuntimeError, "can not store data to read-only array");
|
|
535
929
|
}
|
|
536
|
-
|
|
537
|
-
|
|
930
|
+
|
|
931
|
+
/* Fast path: non-CA_OBJECT element type needs no GC protection. */
|
|
932
|
+
if ( ca->data_type != CA_OBJECT ) {
|
|
933
|
+
ca_xfer_index_dispatch(ca, idx, data, dir);
|
|
934
|
+
return;
|
|
538
935
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
936
|
+
|
|
937
|
+
/* CA_OBJECT slow path: cyclic check (+ rb_protect on GET). */
|
|
938
|
+
if ( dir == CA_XFER_GET ) {
|
|
939
|
+
struct ca_xfer_index_args args;
|
|
940
|
+
int state = 0;
|
|
941
|
+
args.ca = ca;
|
|
942
|
+
args.idx = idx;
|
|
943
|
+
args.ptr = (char *) data;
|
|
944
|
+
ca_set_cyclic_check(ca);
|
|
945
|
+
rb_protect(ca_xfer_index_get_body, (VALUE) &args, &state);
|
|
946
|
+
ca_clear_cyclic_check(ca);
|
|
947
|
+
if ( state ) {
|
|
948
|
+
rb_jump_tag(state);
|
|
949
|
+
}
|
|
543
950
|
}
|
|
544
951
|
else {
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
ca->obj_type);
|
|
952
|
+
ca_set_cyclic_check(ca);
|
|
953
|
+
ca_xfer_index_dispatch(ca, idx, data, CA_XFER_PUT);
|
|
954
|
+
ca_clear_cyclic_check(ca);
|
|
549
955
|
}
|
|
956
|
+
}
|
|
550
957
|
|
|
551
|
-
|
|
958
|
+
/* fetch data of the element at given index to memory pointed by pval */
|
|
552
959
|
|
|
553
|
-
|
|
960
|
+
void
|
|
961
|
+
ca_fetch_index (void *ap, ca_size_t *idx, void *pval)
|
|
962
|
+
{
|
|
963
|
+
ca_xfer_index(ap, idx, pval, CA_XFER_GET);
|
|
554
964
|
}
|
|
555
965
|
|
|
556
|
-
/* store value pointed by pval to the element at given
|
|
966
|
+
/* store value pointed by pval to the element at given index */
|
|
557
967
|
|
|
558
968
|
void
|
|
559
|
-
|
|
969
|
+
ca_store_index (void *ap, ca_size_t *idx, void *pval)
|
|
560
970
|
{
|
|
561
|
-
|
|
562
|
-
|
|
971
|
+
ca_xfer_index(ap, idx, pval, CA_XFER_PUT);
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
/* gather / scatter over a list of linear addresses. ca_xfer_addrs is the
|
|
975
|
+
primary addr entry; ca_fetch_addr / ca_store_addr are thin wrappers
|
|
976
|
+
(kept as public C-API for external ext gems).
|
|
977
|
+
|
|
978
|
+
Dispatch core (no GC protection; caller handles CA_OBJECT):
|
|
979
|
+
1. ca->ptr present (entity / attached / alias) -> direct memcpy at addr.
|
|
980
|
+
2. xfer_addrs slot -> use it (every view supplies one). */
|
|
563
981
|
|
|
564
|
-
|
|
982
|
+
/* Detect a sequential addr run (addrs[i] == addrs[0] + i for all i) so a
|
|
983
|
+
single bulk memcpy replaces the per-cell loop. O(n) integer compare
|
|
984
|
+
with early-exit on first mismatch. This fires for dominant-true mask
|
|
985
|
+
workloads (ca[:is_not_masked] += v etc.): when the boolean is mostly
|
|
986
|
+
TRUE, the view->parent addr remap degenerates to [0..n-1]. */
|
|
987
|
+
int
|
|
988
|
+
ca_xfer_addrs_is_sequential_run (ca_size_t n, ca_size_t *addrs,
|
|
989
|
+
ca_size_t *base_out)
|
|
990
|
+
{
|
|
991
|
+
ca_size_t base, i;
|
|
992
|
+
if ( n == 0 ) { *base_out = 0; return 1; }
|
|
993
|
+
base = addrs[0];
|
|
994
|
+
for ( i = 1; i < n; i++ ) {
|
|
995
|
+
if ( addrs[i] != base + i ) return 0;
|
|
996
|
+
}
|
|
997
|
+
*base_out = base;
|
|
998
|
+
return 1;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
static void
|
|
1002
|
+
ca_xfer_addrs_dispatch (CArray *ca, ca_size_t n, ca_size_t *addrs,
|
|
1003
|
+
void *data, int dir)
|
|
1004
|
+
{
|
|
1005
|
+
char *d = (char *) data;
|
|
1006
|
+
ca_size_t i, base;
|
|
1007
|
+
|
|
1008
|
+
if ( ca->ptr ) { /* attached / entity / alias: fast path */
|
|
1009
|
+
if ( ca_xfer_addrs_is_sequential_run(n, addrs, &base) ) {
|
|
1010
|
+
/* Sequential-run fast path: single bulk memcpy. Triggered by any
|
|
1011
|
+
sub-region run ([k..k+m-1] form), not whole-view limited. Safe:
|
|
1012
|
+
detection is view-structural (the addr shape, not a workload tag)
|
|
1013
|
+
and does not call xfer_all. */
|
|
1014
|
+
char *p = ca->ptr + ca->bytes * base;
|
|
1015
|
+
ca_size_t nbytes = n * ca->bytes;
|
|
1016
|
+
if ( dir == CA_XFER_GET ) memcpy(d, p, nbytes);
|
|
1017
|
+
else memcpy(p, d, nbytes);
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
/* Per-cell loop for arbitrary (non-sequential) addrs:
|
|
1021
|
+
fancy gather/scatter from CASelect 2-D mapper, CSA sparse mask, etc. */
|
|
1022
|
+
for ( i = 0; i < n; i++ ) {
|
|
1023
|
+
char *p = ca->ptr + ca->bytes * addrs[i];
|
|
1024
|
+
if ( dir == CA_XFER_GET ) memcpy(d + i * ca->bytes, p, ca->bytes);
|
|
1025
|
+
else memcpy(p, d + i * ca->bytes, ca->bytes);
|
|
1026
|
+
}
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
if ( ! ca_func[ca->obj_type].xfer_addrs ) {
|
|
565
1031
|
rb_raise(rb_eRuntimeError,
|
|
566
|
-
"
|
|
1032
|
+
"[BUG] xfer_addrs not defined for object type <%i>",
|
|
1033
|
+
ca->obj_type);
|
|
567
1034
|
}
|
|
1035
|
+
ca_func[ca->obj_type].xfer_addrs(ca, n, addrs, data, dir);
|
|
1036
|
+
}
|
|
568
1037
|
|
|
569
|
-
|
|
1038
|
+
struct ca_xfer_addrs_args {
|
|
1039
|
+
CArray *ca;
|
|
1040
|
+
ca_size_t n;
|
|
1041
|
+
ca_size_t *addrs;
|
|
1042
|
+
char *data;
|
|
1043
|
+
};
|
|
570
1044
|
|
|
571
|
-
|
|
572
|
-
|
|
1045
|
+
static VALUE
|
|
1046
|
+
ca_xfer_addrs_get_body (VALUE arg)
|
|
1047
|
+
{
|
|
1048
|
+
struct ca_xfer_addrs_args *d = (struct ca_xfer_addrs_args *) arg;
|
|
1049
|
+
ca_size_t i;
|
|
1050
|
+
ca_xfer_addrs_dispatch(d->ca, d->n, d->addrs, d->data, CA_XFER_GET);
|
|
1051
|
+
for ( i = 0; i < d->n; i++ ) {
|
|
1052
|
+
ca_test_cyclic_check(d->ca, d->data + i * d->ca->bytes);
|
|
573
1053
|
}
|
|
574
|
-
|
|
575
|
-
|
|
1054
|
+
return Qnil;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
void
|
|
1058
|
+
ca_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *data, int dir)
|
|
1059
|
+
{
|
|
1060
|
+
CArray *ca = (CArray *) ap;
|
|
1061
|
+
|
|
1062
|
+
if ( dir == CA_XFER_PUT && ca_is_readonly(ca) ) {
|
|
1063
|
+
rb_raise(rb_eRuntimeError, "can not store data to read-only array");
|
|
576
1064
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
1065
|
+
|
|
1066
|
+
/* Fast path: non-CA_OBJECT element type needs no GC protection. */
|
|
1067
|
+
if ( ca->data_type != CA_OBJECT ) {
|
|
1068
|
+
ca_xfer_addrs_dispatch(ca, n, addrs, data, dir);
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/* CA_OBJECT slow path: cyclic check (+ rb_protect on GET). */
|
|
1073
|
+
if ( dir == CA_XFER_GET ) {
|
|
1074
|
+
struct ca_xfer_addrs_args args;
|
|
1075
|
+
int state = 0;
|
|
1076
|
+
args.ca = ca;
|
|
1077
|
+
args.n = n;
|
|
1078
|
+
args.addrs = addrs;
|
|
1079
|
+
args.data = (char *) data;
|
|
1080
|
+
ca_set_cyclic_check(ca);
|
|
1081
|
+
rb_protect(ca_xfer_addrs_get_body, (VALUE) &args, &state);
|
|
1082
|
+
ca_clear_cyclic_check(ca);
|
|
1083
|
+
if ( state ) {
|
|
1084
|
+
rb_jump_tag(state);
|
|
1085
|
+
}
|
|
581
1086
|
}
|
|
582
1087
|
else {
|
|
1088
|
+
ca_set_cyclic_check(ca);
|
|
1089
|
+
ca_xfer_addrs_dispatch(ca, n, addrs, data, CA_XFER_PUT);
|
|
583
1090
|
ca_clear_cyclic_check(ca);
|
|
584
|
-
rb_raise(rb_eRuntimeError,
|
|
585
|
-
"[BUG] store_addr or store_index "\
|
|
586
|
-
"are not defined for object type <%i>",
|
|
587
|
-
ca->obj_type);
|
|
588
1091
|
}
|
|
589
|
-
|
|
590
|
-
ca_clear_cyclic_check(ca);
|
|
591
|
-
|
|
592
1092
|
}
|
|
593
1093
|
|
|
594
|
-
/*
|
|
595
|
-
|
|
1094
|
+
/* gather / scatter over a STRIDED region of the view. The region is
|
|
1095
|
+
described in the view's own byte space:
|
|
1096
|
+
|
|
1097
|
+
base = Σ starts[k] * native_byte_stride[k] (the region's first cell,
|
|
1098
|
+
starts[] given as a per-axis index into the view's row-major layout)
|
|
1099
|
+
cell(idx) byte offset = base + Σ idx[k] * strides[k] for idx in [0,counts)
|
|
1100
|
+
strides[] = SRC access byte strides into the view (NOT a contiguous region;
|
|
1101
|
+
carries sub-sampling / transpose from the composed leaf access)
|
|
1102
|
+
|
|
1103
|
+
data is a CONTIGUOUS caller buffer in row-major order over counts[]. Local
|
|
1104
|
+
materialise of the requested region only -- never the whole view.
|
|
1105
|
+
|
|
1106
|
+
One example is CASelect (step = strides[0]/bytes is a view
|
|
1107
|
+
access step, data delivered contiguously to the parent). The boundary wiring
|
|
1108
|
+
passes a CAStride leaf's composed access (composed_base, composed_strides,
|
|
1109
|
+
leaf->dim) straight through.
|
|
1110
|
+
|
|
1111
|
+
Dispatch core (no GC protection; caller handles CA_OBJECT):
|
|
1112
|
+
1. ca->ptr present (entity / attached / alias) -> strided memcpy.
|
|
1113
|
+
2. xfer_stride slot -> the view delivers its own region (recurse / cast /
|
|
1114
|
+
gather-translate).
|
|
1115
|
+
3. else per cell: byte offset -> flat addr -> index -> ca_xfer_index_dispatch
|
|
1116
|
+
(universal fallback, no whole-view attach). */
|
|
1117
|
+
|
|
1118
|
+
/* Cache-tiled 2-D transpose fast path (helper for ca_xfer_stride_dispatch).
|
|
1119
|
+
|
|
1120
|
+
------------------------------------------------------------------------
|
|
1121
|
+
PROBLEM
|
|
1122
|
+
------------------------------------------------------------------------
|
|
1123
|
+
When the dispatcher detects that slab merge fails (innermost stride is
|
|
1124
|
+
not contig at ca->bytes) AND the access pattern looks like a 2-D
|
|
1125
|
+
transpose (outer view axis IS source-contig: strides[0] == bytes, inner
|
|
1126
|
+
view axis is non-contig: strides[1] != bytes), the naive prefix
|
|
1127
|
+
odometer issues counts[0] * counts[1] independent memcpy(_,_,bytes)
|
|
1128
|
+
calls. Each call reads one cell of `bytes` from ca->ptr at a different
|
|
1129
|
+
row of source -- with strides[1] huge (e.g. N * 8 for f64), every read
|
|
1130
|
+
touches a different cache line and often a different 4 KB page.
|
|
1131
|
+
|
|
1132
|
+
At N=2000 / bytes=8 (parent data_type = float64), this is 4M random
|
|
1133
|
+
reads. DRAM random-access bandwidth bottoms out around 1-2 GB/s, so
|
|
1134
|
+
the per-cell loop is dominated by cache-line / page misses on a large
|
|
1135
|
+
working set.
|
|
1136
|
+
|
|
1137
|
+
------------------------------------------------------------------------
|
|
1138
|
+
TECHNIQUE: cache-tiled transpose with L1-resident scratch
|
|
1139
|
+
------------------------------------------------------------------------
|
|
1140
|
+
Process the iteration space in 32x32 tiles, staging each tile through
|
|
1141
|
+
a stack-allocated `scratch` buffer that fits in L1 (32 * 32 * 16 =
|
|
1142
|
+
16 KB; bytes <= 16 ceiling).
|
|
1143
|
+
|
|
1144
|
+
Load pass:
|
|
1145
|
+
Read `Tj` rows of source contiguously, each `Ti * bytes` long, into
|
|
1146
|
+
scratch[j_t * Ti + i_t]. Per-tile DRAM traffic: Tj sequential
|
|
1147
|
+
reads of small (256 B at bytes=8) runs. Outer loop carries source
|
|
1148
|
+
row band [sr0..sr0+Tj) -- those rows stay resident in L2 across
|
|
1149
|
+
the inner sc0 sweep (32 rows * N * bytes = 512 KB at N=2000, fits
|
|
1150
|
+
in any modern L2).
|
|
1151
|
+
|
|
1152
|
+
Store pass:
|
|
1153
|
+
For each output row (sc0+i_t), write `Tj` cells contiguously to
|
|
1154
|
+
the data buffer. The source side is the L1-resident scratch read
|
|
1155
|
+
at byte stride `Ti * bytes` -- a small constant stride into a
|
|
1156
|
+
16 KB region, effectively free.
|
|
1157
|
+
|
|
1158
|
+
The key invariant: BOTH DRAM-facing transfers (the load-pass source
|
|
1159
|
+
read and the store-pass data write) are sequential. Random access is
|
|
1160
|
+
confined to the L1 scratch.
|
|
1161
|
+
|
|
1162
|
+
------------------------------------------------------------------------
|
|
1163
|
+
bytes specialisation
|
|
1164
|
+
------------------------------------------------------------------------
|
|
1165
|
+
The store pass's inner loop is the hottest path (Ti * Tj memcpy calls
|
|
1166
|
+
per tile). memcpy(_,_,bytes) with a runtime `bytes` defeats the
|
|
1167
|
+
compiler's small-constant inlining heuristic, so we dispatch on
|
|
1168
|
+
bytes ∈ {1, 2, 4, 8} to a TILED_*_TYPED macro that uses typed pointer
|
|
1169
|
+
arithmetic and explicit stores. At bytes=8 (float64 / int64 -- the
|
|
1170
|
+
dominant case for large 2-D workloads) Clang / gcc generate vectorised
|
|
1171
|
+
loads/stores for the strided scratch reads.
|
|
1172
|
+
|
|
1173
|
+
bytes=16 (cmplx128) and other unusual widths fall through to a generic
|
|
1174
|
+
memcpy loop; correctness is preserved, only the typed-store benefit
|
|
1175
|
+
is lost.
|
|
1176
|
+
|
|
1177
|
+
------------------------------------------------------------------------
|
|
1178
|
+
Why not always tile?
|
|
1179
|
+
------------------------------------------------------------------------
|
|
1180
|
+
When strides[1] IS contig (== bytes), the slab merge already collapses
|
|
1181
|
+
the iteration to a single bulk memcpy. When strides[0] is also non-
|
|
1182
|
+
contig (e.g. strided sub-sampling on BOTH axes), tiling still helps
|
|
1183
|
+
but the gains are smaller; we conservatively limit the trigger to
|
|
1184
|
+
strides[0] == bytes to keep the fast-path predicate cheap and the
|
|
1185
|
+
guarantees unambiguous.
|
|
1186
|
+
|
|
1187
|
+
------------------------------------------------------------------------
|
|
1188
|
+
No attach inside xfer_stride
|
|
1189
|
+
------------------------------------------------------------------------
|
|
1190
|
+
CAREFUL: xfer_stride is a per-region delivery primitive; it must not
|
|
1191
|
+
invoke ca_attach on `ca` itself or any ancestor. Doing so would
|
|
1192
|
+
short-circuit CAStack's multi-parent design and the general "partial
|
|
1193
|
+
materialise instead of whole-view attach" goal. This helper operates
|
|
1194
|
+
strictly on ca->ptr in place. */
|
|
1195
|
+
|
|
1196
|
+
#define CA_TILED_TRANSPOSE_2D_TILE 32
|
|
1197
|
+
|
|
1198
|
+
/* Non-static so cross-file callers (ca_obj_stride.c) can reuse the same
|
|
1199
|
+
tile-block algorithm. `src_base` points at the strided side's [0,0]
|
|
1200
|
+
cell; `dst` is the row-major contig side (M x N over bytes). `strides[0]`
|
|
1201
|
+
= source-contig stride (must equal `bytes`), `strides[1]` = source-strided
|
|
1202
|
+
stride. dir = CA_XFER_GET (strided->contig) / CA_XFER_PUT (contig->strided).
|
|
1203
|
+
|
|
1204
|
+
3 caller sites:
|
|
1205
|
+
- ca_xfer_stride_dispatch ca->ptr path (this file)
|
|
1206
|
+
- ca_stride_func_xfer_stride root-direct (ca_obj_stride.c)
|
|
1207
|
+
- ca_stride_xfer_with_layout general driver (ca_obj_stride.c) */
|
|
596
1208
|
void
|
|
597
|
-
|
|
1209
|
+
ca_xfer_stride_tiled_transpose_2d (char *src_base,
|
|
1210
|
+
ca_size_t bytes_,
|
|
1211
|
+
ca_size_t *counts,
|
|
1212
|
+
ca_size_t *strides,
|
|
1213
|
+
char *data,
|
|
1214
|
+
int dir)
|
|
598
1215
|
{
|
|
599
|
-
|
|
600
|
-
char *
|
|
1216
|
+
enum { TILE = CA_TILED_TRANSPOSE_2D_TILE };
|
|
1217
|
+
char scratch[TILE * TILE * 16]; /* L1-resident, max bytes = 16 */
|
|
1218
|
+
ca_size_t M = counts[0]; /* view outer = source contig direction */
|
|
1219
|
+
ca_size_t N = counts[1]; /* view inner = source non-contig direction */
|
|
1220
|
+
ca_size_t sr0, sc0, i_t, j_t;
|
|
1221
|
+
|
|
1222
|
+
for ( sr0 = 0; sr0 < N; sr0 += TILE ) {
|
|
1223
|
+
ca_size_t Tj = (N - sr0 < TILE) ? (N - sr0) : TILE;
|
|
1224
|
+
for ( sc0 = 0; sc0 < M; sc0 += TILE ) {
|
|
1225
|
+
ca_size_t Ti = (M - sc0 < TILE) ? (M - sc0) : TILE;
|
|
1226
|
+
|
|
1227
|
+
if ( dir == CA_XFER_GET ) {
|
|
1228
|
+
/* Load pass: contig source reads -> scratch[j_t * Ti + i_t]. */
|
|
1229
|
+
for ( j_t = 0; j_t < Tj; j_t++ ) {
|
|
1230
|
+
char *src_row = src_base
|
|
1231
|
+
+ (sr0 + j_t) * strides[1]
|
|
1232
|
+
+ sc0 * bytes_;
|
|
1233
|
+
memcpy(scratch + j_t * Ti * bytes_, src_row, Ti * bytes_);
|
|
1234
|
+
}
|
|
1235
|
+
/* Store pass: strided read from L1 scratch + contig write to data
|
|
1236
|
+
buffer, dispatched by element width. bytes={1,2,4,8} use
|
|
1237
|
+
typed pointer arithmetic so the compiler can vectorise. */
|
|
1238
|
+
#define TILED_GET_TYPED(T) \
|
|
1239
|
+
do { \
|
|
1240
|
+
T *scr = (T *) scratch; \
|
|
1241
|
+
for ( i_t = 0; i_t < Ti; i_t++ ) { \
|
|
1242
|
+
T *out = (T *) (data + ((sc0 + i_t) * N + sr0) * sizeof(T)); \
|
|
1243
|
+
for ( j_t = 0; j_t < Tj; j_t++ ) { \
|
|
1244
|
+
out[j_t] = scr[j_t * Ti + i_t]; \
|
|
1245
|
+
} \
|
|
1246
|
+
} \
|
|
1247
|
+
} while (0)
|
|
1248
|
+
switch ( bytes_ ) {
|
|
1249
|
+
case 1: TILED_GET_TYPED(uint8_t); break;
|
|
1250
|
+
case 2: TILED_GET_TYPED(uint16_t); break;
|
|
1251
|
+
case 4: TILED_GET_TYPED(uint32_t); break;
|
|
1252
|
+
case 8: TILED_GET_TYPED(uint64_t); break;
|
|
1253
|
+
default:
|
|
1254
|
+
for ( i_t = 0; i_t < Ti; i_t++ ) {
|
|
1255
|
+
char *out_row = data + ((sc0 + i_t) * N + sr0) * bytes_;
|
|
1256
|
+
for ( j_t = 0; j_t < Tj; j_t++ ) {
|
|
1257
|
+
memcpy(out_row + j_t * bytes_,
|
|
1258
|
+
scratch + (j_t * Ti + i_t) * bytes_, bytes_);
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
break;
|
|
1262
|
+
}
|
|
1263
|
+
#undef TILED_GET_TYPED
|
|
1264
|
+
}
|
|
1265
|
+
else { /* CA_XFER_PUT: mirror of GET, data drives writes to ca->ptr. */
|
|
1266
|
+
/* Load pass: contig data reads -> scratch (transposed layout). */
|
|
1267
|
+
#define TILED_PUT_TYPED(T) \
|
|
1268
|
+
do { \
|
|
1269
|
+
T *scr = (T *) scratch; \
|
|
1270
|
+
for ( i_t = 0; i_t < Ti; i_t++ ) { \
|
|
1271
|
+
T *in = (T *) (data + ((sc0 + i_t) * N + sr0) * sizeof(T)); \
|
|
1272
|
+
for ( j_t = 0; j_t < Tj; j_t++ ) { \
|
|
1273
|
+
scr[j_t * Ti + i_t] = in[j_t]; \
|
|
1274
|
+
} \
|
|
1275
|
+
} \
|
|
1276
|
+
} while (0)
|
|
1277
|
+
switch ( bytes_ ) {
|
|
1278
|
+
case 1: TILED_PUT_TYPED(uint8_t); break;
|
|
1279
|
+
case 2: TILED_PUT_TYPED(uint16_t); break;
|
|
1280
|
+
case 4: TILED_PUT_TYPED(uint32_t); break;
|
|
1281
|
+
case 8: TILED_PUT_TYPED(uint64_t); break;
|
|
1282
|
+
default:
|
|
1283
|
+
for ( i_t = 0; i_t < Ti; i_t++ ) {
|
|
1284
|
+
char *data_row = data + ((sc0 + i_t) * N + sr0) * bytes_;
|
|
1285
|
+
for ( j_t = 0; j_t < Tj; j_t++ ) {
|
|
1286
|
+
memcpy(scratch + (j_t * Ti + i_t) * bytes_,
|
|
1287
|
+
data_row + j_t * bytes_, bytes_);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
break;
|
|
1291
|
+
}
|
|
1292
|
+
#undef TILED_PUT_TYPED
|
|
1293
|
+
/* Store pass: contig source writes from scratch. */
|
|
1294
|
+
for ( j_t = 0; j_t < Tj; j_t++ ) {
|
|
1295
|
+
char *src_row = src_base
|
|
1296
|
+
+ (sr0 + j_t) * strides[1]
|
|
1297
|
+
+ sc0 * bytes_;
|
|
1298
|
+
memcpy(src_row, scratch + j_t * Ti * bytes_, Ti * bytes_);
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
601
1304
|
|
|
602
|
-
|
|
1305
|
+
/* Shared strided-region walker for the dispatcher (this file) and the CAStride
|
|
1306
|
+
root-direct path (ca_obj_stride.c::ca_stride_func_xfer_stride). This
|
|
1307
|
+
helper consolidates the slab-merge + tile-block + general-driver logic
|
|
1308
|
+
both paths use.
|
|
1309
|
+
|
|
1310
|
+
Callers responsibility: compute `src_base` to already include any per-axis
|
|
1311
|
+
base offset, supply `src_strides[]` as byte strides matching `counts[]`,
|
|
1312
|
+
and provide `data` as a row-major contig buffer over counts in `bytes`-
|
|
1313
|
+
per-cell layout. `dir` is CA_XFER_GET (src -> data) or CA_XFER_PUT
|
|
1314
|
+
(data -> src).
|
|
1315
|
+
|
|
1316
|
+
Inner-loop strategy: slab merge (innermost contig run) + 2-D tile-block
|
|
1317
|
+
transpose at the inner pair + outer-prefix odometer with per-iter memcpy.
|
|
1318
|
+
Inner-loop strategy is NOT shared with ca_stride_xfer_with_layout, which
|
|
1319
|
+
uses ca_stride_gather_run / scatter_run typed runs -- intentionally kept
|
|
1320
|
+
separate to avoid abstraction over two structurally distinct inner
|
|
1321
|
+
strategies. */
|
|
1322
|
+
void
|
|
1323
|
+
ca_xfer_strided_walk (char *src_base,
|
|
1324
|
+
ca_size_t bytes,
|
|
1325
|
+
int8_t ndim,
|
|
1326
|
+
const ca_size_t *counts,
|
|
1327
|
+
const ca_size_t *src_strides,
|
|
1328
|
+
char *data,
|
|
1329
|
+
int dir)
|
|
1330
|
+
{
|
|
1331
|
+
ca_size_t idx[CA_RANK_MAX];
|
|
1332
|
+
ca_size_t doff = 0;
|
|
1333
|
+
int8_t k;
|
|
1334
|
+
|
|
1335
|
+
/* slab merge -- scan innermost contig run (src_strides[k] equals the
|
|
1336
|
+
accumulated slab byte size). This collapses per-cell memcpy(_,_,
|
|
1337
|
+
bytes) loops into per-slab memcpy when the source region is row-major
|
|
1338
|
+
contig. Critical for transform views (CAFake / CAByteSwap / CATile)
|
|
1339
|
+
calling parent.xfer_stride on (N-2)x(N-2)-style interior regions: the
|
|
1340
|
+
inner axis is parent-contig and merges into a single row memcpy. */
|
|
1341
|
+
int8_t slab_start = ndim;
|
|
1342
|
+
ca_size_t slab_bytes = bytes;
|
|
1343
|
+
for ( k = ndim - 1; k >= 0; k-- ) {
|
|
1344
|
+
if ( src_strides[k] != slab_bytes ) break;
|
|
1345
|
+
slab_bytes *= counts[k];
|
|
1346
|
+
slab_start = k;
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
if ( slab_start == 0 ) { /* whole region is one contig slab */
|
|
1350
|
+
if ( dir == CA_XFER_GET ) memcpy(data, src_base, slab_bytes);
|
|
1351
|
+
else memcpy(src_base, data, slab_bytes);
|
|
1352
|
+
return;
|
|
1353
|
+
}
|
|
603
1354
|
|
|
604
|
-
|
|
605
|
-
|
|
1355
|
+
/* Innermost-2-axis tile-block transpose (ndim >= 2 generalisation).
|
|
1356
|
+
When slab merge cannot
|
|
1357
|
+
collapse the innermost axis but the innermost-1 axis is source-contig
|
|
1358
|
+
(= transpose-like at the inner pair), iterate the outer (ndim-2) axes
|
|
1359
|
+
on an odometer and apply the 2-D cache-tiled helper to each inner
|
|
1360
|
+
(counts[ndim-2] x counts[ndim-1]) block. ndim == 2 reduces to
|
|
1361
|
+
outer_n == 0 -- the odometer runs exactly once with soff == 0 (relative
|
|
1362
|
+
to src_base) -- so it is byte-equivalent to the 2-D-only case.
|
|
1363
|
+
Helper operates in place on src_base; no attach is invoked. */
|
|
1364
|
+
if ( ndim >= 2 && bytes <= 16 &&
|
|
1365
|
+
slab_start == ndim &&
|
|
1366
|
+
src_strides[ndim-2] == bytes && src_strides[ndim-1] != bytes ) {
|
|
1367
|
+
int8_t outer_n = ndim - 2;
|
|
1368
|
+
ca_size_t inner_counts[2] = { counts[ndim-2], counts[ndim-1] };
|
|
1369
|
+
ca_size_t inner_strides[2] = { src_strides[ndim-2], src_strides[ndim-1] };
|
|
1370
|
+
ca_size_t inner_dst_bytes = counts[ndim-2] * counts[ndim-1] * bytes;
|
|
1371
|
+
|
|
1372
|
+
for ( k = 0; k < outer_n; k++ ) idx[k] = 0;
|
|
1373
|
+
while ( 1 ) {
|
|
1374
|
+
ca_size_t soff = 0;
|
|
1375
|
+
for ( k = 0; k < outer_n; k++ ) soff += idx[k] * src_strides[k];
|
|
1376
|
+
ca_xfer_stride_tiled_transpose_2d(src_base + soff, bytes,
|
|
1377
|
+
inner_counts, inner_strides,
|
|
1378
|
+
data + doff, dir);
|
|
1379
|
+
doff += inner_dst_bytes;
|
|
1380
|
+
if ( outer_n == 0 ) break;
|
|
1381
|
+
k = outer_n - 1;
|
|
1382
|
+
while ( k >= 0 ) { if ( ++idx[k] < counts[k] ) break; idx[k] = 0; k--; }
|
|
1383
|
+
if ( k < 0 ) break;
|
|
1384
|
+
}
|
|
1385
|
+
return;
|
|
606
1386
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
1387
|
+
|
|
1388
|
+
/* prefix axes [0..slab_start-1] on odometer, slab-sized memcpy per iter. */
|
|
1389
|
+
for ( k = 0; k < slab_start; k++ ) idx[k] = 0;
|
|
1390
|
+
while ( 1 ) {
|
|
1391
|
+
ca_size_t soff = 0;
|
|
1392
|
+
for ( k = 0; k < slab_start; k++ ) soff += idx[k] * src_strides[k];
|
|
1393
|
+
if ( dir == CA_XFER_GET ) memcpy(data + doff, src_base + soff, slab_bytes);
|
|
1394
|
+
else memcpy(src_base + soff, data + doff, slab_bytes);
|
|
1395
|
+
doff += slab_bytes;
|
|
1396
|
+
k = slab_start - 1;
|
|
1397
|
+
while ( k >= 0 ) { if ( ++idx[k] < counts[k] ) break; idx[k] = 0; k--; }
|
|
1398
|
+
if ( k < 0 ) break;
|
|
610
1399
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
static void
|
|
1403
|
+
ca_xfer_stride_dispatch (CArray *ca, ca_size_t *starts, ca_size_t *counts,
|
|
1404
|
+
ca_size_t *strides, void *data, int dir)
|
|
1405
|
+
{
|
|
1406
|
+
char *d = (char *) data;
|
|
1407
|
+
int8_t ndim = ca->ndim;
|
|
1408
|
+
ca_size_t native[CA_RANK_MAX];
|
|
1409
|
+
ca_size_t base = 0;
|
|
1410
|
+
ca_size_t doff = 0;
|
|
1411
|
+
ca_size_t s;
|
|
1412
|
+
int8_t k;
|
|
1413
|
+
ca_size_t idx[CA_RANK_MAX];
|
|
1414
|
+
|
|
1415
|
+
s = ca->bytes;
|
|
1416
|
+
for ( k = ndim - 1; k >= 0; k-- ) { native[k] = s; s *= ca->dim[k]; }
|
|
1417
|
+
for ( k = 0; k < ndim; k++ ) base += starts[k] * native[k];
|
|
1418
|
+
|
|
1419
|
+
if ( ca->ptr && d != (char *)ca->ptr + base ) {
|
|
1420
|
+
/* attached / entity / alias: strided memcpy.
|
|
1421
|
+
CAREFUL: the `d != (char *)ca->ptr + base` guard in the branch
|
|
1422
|
+
condition above is load-bearing. It blocks the lazy-view self-fill
|
|
1423
|
+
pattern where data == ca->ptr + base would degenerate into a
|
|
1424
|
+
self-memcpy and leave the buffer garbage. Lazy-view attach funcs
|
|
1425
|
+
bypass this dispatcher, but the guard catches any future caller that
|
|
1426
|
+
re-introduces the same category error. */
|
|
1427
|
+
ca_xfer_strided_walk(ca->ptr + base, ca->bytes, ndim, counts, strides,
|
|
1428
|
+
d, dir);
|
|
1429
|
+
return;
|
|
617
1430
|
}
|
|
618
1431
|
|
|
619
|
-
|
|
1432
|
+
if ( ca_func[ca->obj_type].xfer_stride ) {
|
|
1433
|
+
ca_func[ca->obj_type].xfer_stride(ca, starts, counts, strides, data, dir);
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
620
1436
|
|
|
621
|
-
|
|
1437
|
+
/* fallback: per-cell via byte offset -> flat addr -> index -> xfer_index. */
|
|
1438
|
+
for ( k = 0; k < ndim; k++ ) idx[k] = 0;
|
|
1439
|
+
while ( 1 ) {
|
|
1440
|
+
ca_size_t soff = base, vidx[CA_RANK_MAX];
|
|
1441
|
+
for ( k = 0; k < ndim; k++ ) soff += idx[k] * strides[k];
|
|
1442
|
+
ca_addr2index(ca, soff / ca->bytes, vidx);
|
|
1443
|
+
ca_xfer_index_dispatch(ca, vidx, d + doff, dir);
|
|
1444
|
+
doff += ca->bytes;
|
|
1445
|
+
k = ndim - 1;
|
|
1446
|
+
while ( k >= 0 ) { if ( ++idx[k] < counts[k] ) break; idx[k] = 0; k--; }
|
|
1447
|
+
if ( k < 0 ) break;
|
|
1448
|
+
}
|
|
622
1449
|
}
|
|
623
1450
|
|
|
624
|
-
|
|
1451
|
+
struct ca_xfer_stride_args {
|
|
1452
|
+
CArray *ca;
|
|
1453
|
+
ca_size_t *starts;
|
|
1454
|
+
ca_size_t *counts;
|
|
1455
|
+
ca_size_t *strides;
|
|
1456
|
+
char *data;
|
|
1457
|
+
};
|
|
1458
|
+
|
|
1459
|
+
static VALUE
|
|
1460
|
+
ca_xfer_stride_get_body (VALUE arg)
|
|
1461
|
+
{
|
|
1462
|
+
struct ca_xfer_stride_args *a = (struct ca_xfer_stride_args *) arg;
|
|
1463
|
+
ca_size_t n = 1, i;
|
|
1464
|
+
int8_t k;
|
|
1465
|
+
ca_xfer_stride_dispatch(a->ca, a->starts, a->counts, a->strides, a->data,
|
|
1466
|
+
CA_XFER_GET);
|
|
1467
|
+
/* cyclic check over the delivered cells (CA_OBJECT only). dst is contiguous
|
|
1468
|
+
row-major over counts (semantics b), so cell i is at data + i*bytes. */
|
|
1469
|
+
for ( k = 0; k < a->ca->ndim; k++ ) n *= a->counts[k];
|
|
1470
|
+
for ( i = 0; i < n; i++ ) {
|
|
1471
|
+
ca_test_cyclic_check(a->ca, a->data + i * a->ca->bytes);
|
|
1472
|
+
}
|
|
1473
|
+
return Qnil;
|
|
1474
|
+
}
|
|
625
1475
|
|
|
626
1476
|
void
|
|
627
|
-
|
|
1477
|
+
ca_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
|
|
1478
|
+
ca_size_t *strides, void *data, int dir)
|
|
628
1479
|
{
|
|
629
1480
|
CArray *ca = (CArray *) ap;
|
|
630
|
-
char *ptr = (char *) pval;
|
|
631
1481
|
|
|
632
|
-
if ( ca_is_readonly(ca) ) {
|
|
633
|
-
rb_raise(rb_eRuntimeError,
|
|
634
|
-
"can not store data to read-only array");
|
|
1482
|
+
if ( dir == CA_XFER_PUT && ca_is_readonly(ca) ) {
|
|
1483
|
+
rb_raise(rb_eRuntimeError, "can not store data to read-only array");
|
|
635
1484
|
}
|
|
636
1485
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
ca_func[ca->obj_type].store_index(ca, idx, ptr);
|
|
1486
|
+
if ( ca->data_type != CA_OBJECT ) {
|
|
1487
|
+
ca_xfer_stride_dispatch(ca, starts, counts, strides, data, dir);
|
|
1488
|
+
return;
|
|
641
1489
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
1490
|
+
|
|
1491
|
+
if ( dir == CA_XFER_GET ) {
|
|
1492
|
+
struct ca_xfer_stride_args args;
|
|
1493
|
+
int state = 0;
|
|
1494
|
+
args.ca = ca; args.starts = starts; args.counts = counts;
|
|
1495
|
+
args.strides = strides; args.data = (char *) data;
|
|
1496
|
+
ca_set_cyclic_check(ca);
|
|
1497
|
+
rb_protect(ca_xfer_stride_get_body, (VALUE) &args, &state);
|
|
1498
|
+
ca_clear_cyclic_check(ca);
|
|
1499
|
+
if ( state ) rb_jump_tag(state);
|
|
645
1500
|
}
|
|
646
1501
|
else {
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
ca->obj_type);
|
|
1502
|
+
ca_set_cyclic_check(ca);
|
|
1503
|
+
ca_xfer_stride_dispatch(ca, starts, counts, strides, data, CA_XFER_PUT);
|
|
1504
|
+
ca_clear_cyclic_check(ca);
|
|
651
1505
|
}
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
/* whole-view transfer: direction-unified replacement of copy_data /
|
|
1509
|
+
sync_data. Pure dispatch to the view's xfer_all slot. Readonly /
|
|
1510
|
+
nosync policy lives in ca_sync_data (the PUT entry), not here -- this
|
|
1511
|
+
is the raw dispatcher.
|
|
652
1512
|
|
|
653
|
-
|
|
1513
|
+
CAREFUL: the dispatcher is a thin wrapper (no ca_attach here), and each
|
|
1514
|
+
view's xfer_all slot must not ca_attach(parent) either. That is what
|
|
1515
|
+
gives ca_xfer_all and all internal callers (ca_update / ca_copy_data /
|
|
1516
|
+
ca_sync_data / kernel_iterator SRC_ATTACH path) their cheap-attach
|
|
1517
|
+
semantics; re-adding an attach into a slot silently reintroduces a
|
|
1518
|
+
whole-parent materialise. External ext gems calling ca_xfer_all should
|
|
1519
|
+
likewise expect a thin dispatcher. */
|
|
654
1520
|
|
|
1521
|
+
typedef struct {
|
|
1522
|
+
CArray *ca;
|
|
1523
|
+
void *data;
|
|
1524
|
+
int dir;
|
|
1525
|
+
} ca_xfer_all_args_t;
|
|
1526
|
+
|
|
1527
|
+
static VALUE
|
|
1528
|
+
ca_xfer_all_body (VALUE arg)
|
|
1529
|
+
{
|
|
1530
|
+
ca_xfer_all_args_t *a = (ca_xfer_all_args_t *) arg;
|
|
1531
|
+
ca_func[a->ca->obj_type].xfer_all(a->ca, a->data, a->dir);
|
|
1532
|
+
return Qnil;
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
static VALUE
|
|
1536
|
+
ca_xfer_all_ensure (VALUE arg)
|
|
1537
|
+
{
|
|
1538
|
+
(void) arg;
|
|
1539
|
+
ca_lazy_arena_exit();
|
|
1540
|
+
return Qnil;
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
void
|
|
1544
|
+
ca_xfer_all (void *ap, void *data, int dir)
|
|
1545
|
+
{
|
|
1546
|
+
CArray *ca = (CArray *) ap;
|
|
1547
|
+
ca_xfer_all_args_t args;
|
|
1548
|
+
if ( ! ca_func[ca->obj_type].xfer_all ) {
|
|
1549
|
+
rb_raise(rb_eRuntimeError,
|
|
1550
|
+
"[BUG] xfer_all not defined for object type <%i>",
|
|
1551
|
+
ca->obj_type);
|
|
1552
|
+
}
|
|
1553
|
+
/* Universal arena lifetime hook. ca_xfer_all is the single universal
|
|
1554
|
+
entry for materialise (to_ca -> ca_copy -> ca_copy_data ->
|
|
1555
|
+
ca_xfer_all), so wrapping here covers every outermost view type,
|
|
1556
|
+
including an affine view wrapping a lazy view ((a.lazy+b).transpose).
|
|
1557
|
+
|
|
1558
|
+
CAREFUL: the arena _exit must run under rb_ensure. If an exception
|
|
1559
|
+
skips it, the arena depth stays stuck at +1 and the reset trigger
|
|
1560
|
+
(a depth==0 entry) never fires -- a silent failure. With rb_ensure
|
|
1561
|
+
the depth returns to 0 on exit and the reset fires correctly at the
|
|
1562
|
+
next entry. */
|
|
1563
|
+
ca_lazy_arena_enter();
|
|
1564
|
+
args.ca = ca; args.data = data; args.dir = dir;
|
|
1565
|
+
rb_ensure(ca_xfer_all_body, (VALUE) &args,
|
|
1566
|
+
ca_xfer_all_ensure, Qnil);
|
|
655
1567
|
}
|
|
656
1568
|
|
|
657
1569
|
/* ------------------------------------------------------------------- */
|
|
@@ -667,12 +1579,12 @@ ca_allocate (void *ap)
|
|
|
667
1579
|
return;
|
|
668
1580
|
}
|
|
669
1581
|
|
|
670
|
-
if (
|
|
1582
|
+
if ( ca_is_view(ca) ) { /* view array */
|
|
671
1583
|
|
|
672
|
-
|
|
673
|
-
if (
|
|
1584
|
+
CAVIEW(ca)->attach += 1; /* increments attach level */
|
|
1585
|
+
if ( CAVIEW(ca)->attach > CA_ATTACH_MAX ) {
|
|
674
1586
|
rb_raise(rb_eRuntimeError,
|
|
675
|
-
"too large attach count of
|
|
1587
|
+
"too large attach count of view array");
|
|
676
1588
|
}
|
|
677
1589
|
|
|
678
1590
|
if ( ! ca->ptr ) {
|
|
@@ -707,12 +1619,12 @@ ca_attach (void *ap)
|
|
|
707
1619
|
return;
|
|
708
1620
|
}
|
|
709
1621
|
|
|
710
|
-
if (
|
|
1622
|
+
if ( ca_is_view(ca) ) { /* view array */
|
|
711
1623
|
|
|
712
|
-
|
|
713
|
-
if (
|
|
1624
|
+
CAVIEW(ca)->attach += 1; /* increments attach level */
|
|
1625
|
+
if ( CAVIEW(ca)->attach > CA_ATTACH_MAX ) {
|
|
714
1626
|
rb_raise(rb_eRuntimeError,
|
|
715
|
-
"too large attach count of
|
|
1627
|
+
"too large attach count of view array");
|
|
716
1628
|
}
|
|
717
1629
|
|
|
718
1630
|
if ( ! ca->ptr ) {
|
|
@@ -738,13 +1650,13 @@ ca_update (void *ap)
|
|
|
738
1650
|
return;
|
|
739
1651
|
}
|
|
740
1652
|
|
|
741
|
-
if (
|
|
1653
|
+
if ( ca_is_view(ca) ) { /* view array */
|
|
742
1654
|
|
|
743
1655
|
if ( ca->ptr ) {
|
|
744
|
-
|
|
1656
|
+
ca_xfer_all(ca, ca->ptr, CA_XFER_GET); /* re-gather into own ptr (step 4) */
|
|
745
1657
|
}
|
|
746
1658
|
else {
|
|
747
|
-
rb_raise(rb_eRuntimeError,
|
|
1659
|
+
rb_raise(rb_eRuntimeError,
|
|
748
1660
|
"[BUG] ca_update() called for not-attached virtal array");
|
|
749
1661
|
}
|
|
750
1662
|
|
|
@@ -778,8 +1690,8 @@ ca_sync (void *ap)
|
|
|
778
1690
|
ca_update_mask(ca);
|
|
779
1691
|
ca_sync(ca->mask);
|
|
780
1692
|
|
|
781
|
-
if (
|
|
782
|
-
if ( !
|
|
1693
|
+
if ( ca_is_view(ca) ) { /* view array */
|
|
1694
|
+
if ( ! CAVIEW(ca)->nosync ) { /* FIXME : */
|
|
783
1695
|
ca_func[ca->obj_type].sync(ap);
|
|
784
1696
|
}
|
|
785
1697
|
}
|
|
@@ -805,11 +1717,11 @@ ca_detach (void *ap)
|
|
|
805
1717
|
"[BUG] tried to detach a detached array");
|
|
806
1718
|
}
|
|
807
1719
|
|
|
808
|
-
if (
|
|
809
|
-
if (
|
|
1720
|
+
if ( ca_is_view(ca) ) { /* view array */
|
|
1721
|
+
if ( CAVIEW(ca)->attach == 1 ) {
|
|
810
1722
|
ca_func[ca->obj_type].detach(ap);
|
|
811
1723
|
}
|
|
812
|
-
|
|
1724
|
+
CAVIEW(ca)->attach -= 1;
|
|
813
1725
|
}
|
|
814
1726
|
else { /* entity array */
|
|
815
1727
|
ca_func[ca->obj_type].detach(ap);
|
|
@@ -882,8 +1794,7 @@ ca_detach_n (int n, ...)
|
|
|
882
1794
|
void
|
|
883
1795
|
ca_copy_data (void *ap, char *ptr)
|
|
884
1796
|
{
|
|
885
|
-
|
|
886
|
-
ca_func[ca->obj_type].copy_data(ap, ptr); /* delegate */
|
|
1797
|
+
ca_xfer_all(ap, ptr, CA_XFER_GET); /* whole-view gather (step 4) */
|
|
887
1798
|
}
|
|
888
1799
|
|
|
889
1800
|
/* synchronize the data pointed by given pointer to parent's data */
|
|
@@ -898,16 +1809,12 @@ ca_sync_data (void *ap, char *ptr)
|
|
|
898
1809
|
"can not sync data to read-only array");
|
|
899
1810
|
}
|
|
900
1811
|
|
|
901
|
-
if (
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
}
|
|
905
|
-
else {
|
|
906
|
-
ca_func[ca->obj_type].sync_data(ap, ptr);
|
|
907
|
-
}
|
|
1812
|
+
if ( ca_is_view(ca) && CAVIEW(ca)->nosync ) {
|
|
1813
|
+
/* ca is to be attached: treat ca->ptr as an owned entity buffer */
|
|
1814
|
+
ca_array_func_xfer_all(ap, ptr, CA_XFER_PUT);
|
|
908
1815
|
}
|
|
909
|
-
else {
|
|
910
|
-
|
|
1816
|
+
else {
|
|
1817
|
+
ca_xfer_all(ap, ptr, CA_XFER_PUT); /* whole-view scatter (step 4) */
|
|
911
1818
|
}
|
|
912
1819
|
}
|
|
913
1820
|
|
|
@@ -924,7 +1831,7 @@ ca_fill_data (void *ap, void *aptr)
|
|
|
924
1831
|
"can not fill data to read-only array");
|
|
925
1832
|
}
|
|
926
1833
|
|
|
927
|
-
if (
|
|
1834
|
+
if ( ca_is_view(ca) ) { /* view array */
|
|
928
1835
|
if ( ca_is_attached(ca) ) { /* ca is to be attached */
|
|
929
1836
|
ca_func[CA_OBJ_ARRAY].fill_data(ap, ptr);
|
|
930
1837
|
}
|
|
@@ -937,6 +1844,174 @@ ca_fill_data (void *ap, void *aptr)
|
|
|
937
1844
|
}
|
|
938
1845
|
}
|
|
939
1846
|
|
|
1847
|
+
/* Write one value into part of a view.
|
|
1848
|
+
|
|
1849
|
+
The default walks the region and hands each cell to xfer_index, which
|
|
1850
|
+
composes one hop and delegates to the parent. It is per-cell, so it is the
|
|
1851
|
+
floor rather than the path: a view that can pass the region on fills in the
|
|
1852
|
+
slot and the walk never happens. What the default guarantees is that a view
|
|
1853
|
+
with no slot still touches only the region. */
|
|
1854
|
+
|
|
1855
|
+
void
|
|
1856
|
+
ca_fill_stride_default (void *ap, ca_size_t base, int8_t ndim,
|
|
1857
|
+
ca_size_t *counts, ca_size_t *steps, void *ptr)
|
|
1858
|
+
{
|
|
1859
|
+
CArray *ca = (CArray *) ap;
|
|
1860
|
+
ca_size_t idx[CA_RANK_MAX];
|
|
1861
|
+
int8_t k;
|
|
1862
|
+
|
|
1863
|
+
for ( k = 0; k < ndim; k++ ) idx[k] = 0;
|
|
1864
|
+
while ( 1 ) {
|
|
1865
|
+
ca_size_t addr = base, vidx[CA_RANK_MAX];
|
|
1866
|
+
for ( k = 0; k < ndim; k++ ) addr += idx[k] * steps[k];
|
|
1867
|
+
ca_addr2index(ca, addr, vidx);
|
|
1868
|
+
ca_xfer_index_dispatch(ca, vidx, ptr, CA_XFER_PUT);
|
|
1869
|
+
k = ndim - 1;
|
|
1870
|
+
while ( k >= 0 ) { if ( ++idx[k] < counts[k] ) break; idx[k] = 0; k--; }
|
|
1871
|
+
if ( k < 0 ) break;
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
void
|
|
1876
|
+
ca_fill_stride (void *ap, ca_size_t base, int8_t ndim,
|
|
1877
|
+
ca_size_t *counts, ca_size_t *steps, void *ptr)
|
|
1878
|
+
{
|
|
1879
|
+
CArray *ca = (CArray *) ap;
|
|
1880
|
+
int8_t k;
|
|
1881
|
+
|
|
1882
|
+
if ( ca_is_readonly(ca) ) {
|
|
1883
|
+
rb_raise(rb_eRuntimeError, "can not fill data to read-only array");
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
for ( k = 0; k < ndim; k++ ) {
|
|
1887
|
+
if ( counts[k] <= 0 ) return;
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
if ( ca_func[ca->obj_type].fill_stride ) {
|
|
1891
|
+
ca_func[ca->obj_type].fill_stride(ap, base, ndim, counts, steps, ptr);
|
|
1892
|
+
return;
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
ca_fill_stride_default(ap, base, ndim, counts, steps, ptr);
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
/* True if the region is exactly `ca`'s own extent in row-major order. A view
|
|
1899
|
+
that composes its axes into its parent's space can only do so for the whole
|
|
1900
|
+
of itself: a sub-box arrives as addresses, and recovering which axis each
|
|
1901
|
+
step belongs to is not something addresses can answer once the view has
|
|
1902
|
+
reordered or dropped axes. In practice that is the only region a view is
|
|
1903
|
+
asked for -- ca_fill_stride_whole is the caller -- so the check is a
|
|
1904
|
+
precondition rather than a fast path. */
|
|
1905
|
+
|
|
1906
|
+
int
|
|
1907
|
+
ca_fill_stride_is_whole (void *ap, ca_size_t base, int8_t ndim,
|
|
1908
|
+
ca_size_t *counts, ca_size_t *steps)
|
|
1909
|
+
{
|
|
1910
|
+
CArray *ca = (CArray *) ap;
|
|
1911
|
+
ca_size_t s = 1;
|
|
1912
|
+
int8_t k;
|
|
1913
|
+
|
|
1914
|
+
if ( base != 0 || ndim != ca->ndim ) return 0;
|
|
1915
|
+
for ( k = ndim - 1; k >= 0; k-- ) {
|
|
1916
|
+
if ( counts[k] != ca->dim[k] || steps[k] != s ) return 0;
|
|
1917
|
+
s *= ca->dim[k];
|
|
1918
|
+
}
|
|
1919
|
+
return 1;
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
/* "All of me" as a region: the whole extent in row-major order. */
|
|
1923
|
+
|
|
1924
|
+
void
|
|
1925
|
+
ca_fill_stride_whole (void *ap, void *ptr)
|
|
1926
|
+
{
|
|
1927
|
+
CArray *ca = (CArray *) ap;
|
|
1928
|
+
ca_size_t counts[CA_RANK_MAX], steps[CA_RANK_MAX];
|
|
1929
|
+
ca_size_t s = 1;
|
|
1930
|
+
int8_t k;
|
|
1931
|
+
|
|
1932
|
+
for ( k = ca->ndim - 1; k >= 0; k-- ) {
|
|
1933
|
+
counts[k] = ca->dim[k];
|
|
1934
|
+
steps[k] = s;
|
|
1935
|
+
s *= ca->dim[k];
|
|
1936
|
+
}
|
|
1937
|
+
ca_fill_stride(ap, 0, ca->ndim, counts, steps, ptr);
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
void
|
|
1941
|
+
ca_fill_addrs_default (void *ap, ca_size_t n, ca_size_t *addrs, void *ptr)
|
|
1942
|
+
{
|
|
1943
|
+
CArray *ca = (CArray *) ap;
|
|
1944
|
+
ca_size_t i;
|
|
1945
|
+
|
|
1946
|
+
for ( i = 0; i < n; i++ ) {
|
|
1947
|
+
ca_size_t vidx[CA_RANK_MAX];
|
|
1948
|
+
ca_addr2index(ca, addrs[i], vidx);
|
|
1949
|
+
ca_xfer_index_dispatch(ca, vidx, ptr, CA_XFER_PUT);
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
/* Walk a region and hand its addresses on in windows.
|
|
1954
|
+
|
|
1955
|
+
For a view whose fill is a read-modify-write of the parent -- the sub-byte
|
|
1956
|
+
ones, where a cell carries bits the fill must leave alone -- there is no
|
|
1957
|
+
region to pass down: the parent has to be read before it can be written.
|
|
1958
|
+
What there is to save is being asked for it one cell at a time, each cell
|
|
1959
|
+
descending the chain on its own. The batched address slot already does the
|
|
1960
|
+
read and the write in one call each; this only feeds it.
|
|
1961
|
+
|
|
1962
|
+
The window is fixed so the scratch does not follow the region's size. */
|
|
1963
|
+
|
|
1964
|
+
#define CA_FILL_ADDR_WINDOW 1024
|
|
1965
|
+
|
|
1966
|
+
void
|
|
1967
|
+
ca_fill_stride_via_addrs (void *ap, ca_size_t base, int8_t ndim,
|
|
1968
|
+
ca_size_t *counts, ca_size_t *steps, void *ptr)
|
|
1969
|
+
{
|
|
1970
|
+
CArray *ca = (CArray *) ap;
|
|
1971
|
+
ca_size_t idx[CA_RANK_MAX];
|
|
1972
|
+
ca_size_t window[CA_FILL_ADDR_WINDOW];
|
|
1973
|
+
ca_size_t n = 0;
|
|
1974
|
+
int8_t k;
|
|
1975
|
+
|
|
1976
|
+
for ( k = 0; k < ndim; k++ ) {
|
|
1977
|
+
if ( counts[k] <= 0 ) return;
|
|
1978
|
+
idx[k] = 0;
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
while ( 1 ) {
|
|
1982
|
+
ca_size_t addr = base;
|
|
1983
|
+
for ( k = 0; k < ndim; k++ ) addr += idx[k] * steps[k];
|
|
1984
|
+
window[n++] = addr;
|
|
1985
|
+
if ( n == CA_FILL_ADDR_WINDOW ) {
|
|
1986
|
+
ca_fill_addrs(ca, n, window, ptr);
|
|
1987
|
+
n = 0;
|
|
1988
|
+
}
|
|
1989
|
+
k = ndim - 1;
|
|
1990
|
+
while ( k >= 0 ) { if ( ++idx[k] < counts[k] ) break; idx[k] = 0; k--; }
|
|
1991
|
+
if ( k < 0 ) break;
|
|
1992
|
+
}
|
|
1993
|
+
if ( n ) {
|
|
1994
|
+
ca_fill_addrs(ca, n, window, ptr);
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
void
|
|
1999
|
+
ca_fill_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *ptr)
|
|
2000
|
+
{
|
|
2001
|
+
CArray *ca = (CArray *) ap;
|
|
2002
|
+
|
|
2003
|
+
if ( ca_is_readonly(ca) ) {
|
|
2004
|
+
rb_raise(rb_eRuntimeError, "can not fill data to read-only array");
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
if ( ca_func[ca->obj_type].fill_addrs ) {
|
|
2008
|
+
ca_func[ca->obj_type].fill_addrs(ap, n, addrs, ptr);
|
|
2009
|
+
return;
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
ca_fill_addrs_default(ap, n, addrs, ptr);
|
|
2013
|
+
}
|
|
2014
|
+
|
|
940
2015
|
/* ------------------------------------------------------------------- */
|
|
941
2016
|
|
|
942
2017
|
/* clone CArray struct */
|
|
@@ -975,9 +2050,9 @@ rb_ca_attach_i (VALUE self)
|
|
|
975
2050
|
if ( rb_obj_is_carray(self) ) {
|
|
976
2051
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
977
2052
|
ca_attach(ca);
|
|
978
|
-
if (
|
|
979
|
-
|
|
980
|
-
if (
|
|
2053
|
+
if ( ca_is_view(ca) ) {
|
|
2054
|
+
CAVIEW(ca)->nosync += 1;
|
|
2055
|
+
if ( CAVIEW(ca)->nosync > 64 ) {
|
|
981
2056
|
rb_raise(rb_eRuntimeError, "nosync count exceeds 64");
|
|
982
2057
|
}
|
|
983
2058
|
}
|
|
@@ -990,10 +2065,10 @@ rb_ca_sync_i (VALUE self)
|
|
|
990
2065
|
CArray *ca;
|
|
991
2066
|
if ( rb_obj_is_carray(self) ) {
|
|
992
2067
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
993
|
-
if (
|
|
994
|
-
|
|
2068
|
+
if ( ca_is_view(ca) ) {
|
|
2069
|
+
CAVIEW(ca)->nosync -= 1;
|
|
995
2070
|
ca_sync(ca);
|
|
996
|
-
|
|
2071
|
+
CAVIEW(ca)->nosync += 1;
|
|
997
2072
|
}
|
|
998
2073
|
else {
|
|
999
2074
|
ca_sync(ca);
|
|
@@ -1007,8 +2082,8 @@ rb_ca_detach_i (VALUE self)
|
|
|
1007
2082
|
CArray *ca;
|
|
1008
2083
|
if ( rb_obj_is_carray(self) ) {
|
|
1009
2084
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
1010
|
-
if (
|
|
1011
|
-
|
|
2085
|
+
if ( ca_is_view(ca) ) { /* view array */
|
|
2086
|
+
CAVIEW(ca)->nosync -= 1;
|
|
1012
2087
|
ca_detach(ca);
|
|
1013
2088
|
}
|
|
1014
2089
|
else { /* entity array */
|
|
@@ -1092,7 +2167,7 @@ rb_ca_s_attach_bang (int argc, VALUE *argv, VALUE self)
|
|
|
1092
2167
|
|
|
1093
2168
|
for (i=0; i<RARRAY_LEN(list); i++) {
|
|
1094
2169
|
obj = rb_ary_entry(list, i);
|
|
1095
|
-
|
|
2170
|
+
rb_check_frozen(obj);
|
|
1096
2171
|
rb_ca_attach_i(obj);
|
|
1097
2172
|
}
|
|
1098
2173
|
|
|
@@ -1142,7 +2217,7 @@ It is ensured the syncing the memory block at the end of the block evaluation.
|
|
|
1142
2217
|
static VALUE
|
|
1143
2218
|
rb_ca_attach_bang (VALUE self)
|
|
1144
2219
|
{
|
|
1145
|
-
|
|
2220
|
+
rb_check_frozen(self);
|
|
1146
2221
|
rb_ca_attach_i(self);
|
|
1147
2222
|
return rb_ensure(rb_yield, self, rb_ca_ensure_sync_detach, self);
|
|
1148
2223
|
}
|
|
@@ -1168,7 +2243,7 @@ rb_ca__attach__ (VALUE self)
|
|
|
1168
2243
|
static VALUE
|
|
1169
2244
|
rb_ca__sync__ (VALUE self)
|
|
1170
2245
|
{
|
|
1171
|
-
|
|
2246
|
+
rb_check_frozen(self);
|
|
1172
2247
|
rb_ca_sync_i(self);
|
|
1173
2248
|
return self;
|
|
1174
2249
|
}
|
|
@@ -1232,23 +2307,39 @@ rb_ca_members (VALUE self)
|
|
|
1232
2307
|
}
|
|
1233
2308
|
}
|
|
1234
2309
|
|
|
2310
|
+
/* Projects a struct member of `self` to its CAField view. data_class
|
|
2311
|
+
lives only on a Face, so this accepts both a Face and a plain FIXLEN
|
|
2312
|
+
entity. For a Face, the field-view receiver is swapped to the parent
|
|
2313
|
+
(= Face strip): the @member cache lives on self (the Face) while the
|
|
2314
|
+
actual field view is the CAField on the parent. A plain FIXLEN entity
|
|
2315
|
+
keeps self == receiver. */
|
|
1235
2316
|
VALUE
|
|
1236
|
-
|
|
2317
|
+
rb_ca_face_field (VALUE self, VALUE sym)
|
|
1237
2318
|
{
|
|
1238
2319
|
volatile VALUE data_class = rb_ca_data_class(self);
|
|
1239
2320
|
volatile VALUE member;
|
|
1240
2321
|
volatile VALUE obj;
|
|
2322
|
+
volatile VALUE receiver;
|
|
2323
|
+
CArray *ca;
|
|
1241
2324
|
|
|
1242
2325
|
if ( NIL_P(data_class) ) {
|
|
1243
2326
|
rb_raise(rb_eRuntimeError, "carray doesn't have data class");
|
|
1244
2327
|
}
|
|
1245
2328
|
|
|
2329
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
2330
|
+
receiver = ca_is_face(ca) ? rb_ca_parent(self) : self;
|
|
2331
|
+
|
|
1246
2332
|
member = rb_ivar_get(self, rb_intern("member"));
|
|
1247
2333
|
|
|
1248
2334
|
if ( NIL_P(member) ) {
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
2335
|
+
/* Derived CARecord views (= arr[range] / arr.transpose etc.) inherit
|
|
2336
|
+
data_class via inherit_data_class but not @member, since only
|
|
2337
|
+
ca_record_build initialises it. Lazy-init here so chain field
|
|
2338
|
+
projection (arr[range]["lat"]) works without a [BUG] raise. Cache
|
|
2339
|
+
is local to the derived view; field views are still on its parent
|
|
2340
|
+
(which itself is on the entity). */
|
|
2341
|
+
member = rb_hash_new();
|
|
2342
|
+
rb_ivar_set(self, rb_intern("member"), member);
|
|
1252
2343
|
}
|
|
1253
2344
|
|
|
1254
2345
|
if ( rb_obj_is_kind_of(sym, rb_cInteger) ) {
|
|
@@ -1278,7 +2369,45 @@ rb_ca_field_as_member (VALUE self, VALUE sym)
|
|
|
1278
2369
|
"can't find data_member named <%s>", StringValuePtr(sym));
|
|
1279
2370
|
}
|
|
1280
2371
|
Check_Type(info, T_ARRAY);
|
|
1281
|
-
|
|
2372
|
+
/* Bit-typed members route through a CAField power-of-2 byte
|
|
2373
|
+
projection + CABitfield, mirroring the
|
|
2374
|
+
per-record dispatch in CAStruct#[]. MEMBER_TABLE entry shape
|
|
2375
|
+
for bits is `[byte_offset, :bitfield, {bits:, bit_offset:}]`
|
|
2376
|
+
where bit_offset is the struct-relative *bit* offset. Plain
|
|
2377
|
+
byte-typed members fall through to the original `.field(...)`
|
|
2378
|
+
path. */
|
|
2379
|
+
{
|
|
2380
|
+
volatile VALUE type_val = rb_ary_entry(info, 1);
|
|
2381
|
+
if ( SYMBOL_P(type_val) &&
|
|
2382
|
+
SYM2ID(type_val) == rb_intern("bitfield") ) {
|
|
2383
|
+
volatile VALUE opts = rb_ary_entry(info, 2);
|
|
2384
|
+
volatile VALUE word_view, range, vtype_sym;
|
|
2385
|
+
ca_size_t bit_offset = NUM2SIZE(rb_hash_aref(opts,
|
|
2386
|
+
ID2SYM(rb_intern("bit_offset"))));
|
|
2387
|
+
ca_size_t bits = NUM2SIZE(rb_hash_aref(opts,
|
|
2388
|
+
ID2SYM(rb_intern("bits"))));
|
|
2389
|
+
ca_size_t start_byte = bit_offset / 8;
|
|
2390
|
+
int bit_in_word = (int)(bit_offset % 8);
|
|
2391
|
+
ca_size_t end_byte_excl = (bit_offset + bits + 7) / 8;
|
|
2392
|
+
ca_size_t span = end_byte_excl - start_byte;
|
|
2393
|
+
int view_bytes;
|
|
2394
|
+
const char *vtype_name;
|
|
2395
|
+
if (span <= 1) { view_bytes = 1; vtype_name = "uint8"; }
|
|
2396
|
+
else if (span <= 2) { view_bytes = 2; vtype_name = "uint16"; }
|
|
2397
|
+
else if (span <= 4) { view_bytes = 4; vtype_name = "uint32"; }
|
|
2398
|
+
else { view_bytes = 8; vtype_name = "uint64"; }
|
|
2399
|
+
(void) view_bytes;
|
|
2400
|
+
vtype_sym = ID2SYM(rb_intern(vtype_name));
|
|
2401
|
+
word_view = rb_funcall(receiver, rb_intern("field"), 2,
|
|
2402
|
+
SIZE2NUM(start_byte), vtype_sym);
|
|
2403
|
+
range = rb_range_new(LONG2NUM(bit_in_word),
|
|
2404
|
+
SIZE2NUM(bit_in_word + bits - 1), 0);
|
|
2405
|
+
obj = rb_funcall(word_view, rb_intern("bitfield"), 1, range);
|
|
2406
|
+
rb_hash_aset(member, sym, obj);
|
|
2407
|
+
return obj;
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
obj = rb_apply(receiver, rb_intern("field"), info);
|
|
1282
2411
|
rb_hash_aset(member, sym, obj);
|
|
1283
2412
|
return obj;
|
|
1284
2413
|
}
|
|
@@ -1302,7 +2431,7 @@ rb_ca_fields (VALUE self)
|
|
|
1302
2431
|
list = rb_ary_new2(RARRAY_LEN(member_names));
|
|
1303
2432
|
for (i=0; i<RARRAY_LEN(member_names); i++) {
|
|
1304
2433
|
VALUE name = rb_ary_entry(member_names, i);
|
|
1305
|
-
rb_ary_store(list, i,
|
|
2434
|
+
rb_ary_store(list, i, rb_ca_face_field(self, name));
|
|
1306
2435
|
}
|
|
1307
2436
|
return list;
|
|
1308
2437
|
}
|
|
@@ -1325,15 +2454,14 @@ rb_ca_fields_at (int argc, VALUE *argv, VALUE self)
|
|
|
1325
2454
|
list = rb_ary_new2(RARRAY_LEN(member_names));
|
|
1326
2455
|
for (i=0; i<RARRAY_LEN(member_names); i++) {
|
|
1327
2456
|
VALUE name = rb_ary_entry(member_names, i);
|
|
1328
|
-
rb_ary_store(list, i,
|
|
2457
|
+
rb_ary_store(list, i, rb_ca_face_field(self, name));
|
|
1329
2458
|
}
|
|
1330
2459
|
return list;
|
|
1331
2460
|
}
|
|
1332
2461
|
|
|
1333
|
-
/* ------------------------------------------------------------------- */
|
|
1334
2462
|
|
|
1335
2463
|
void
|
|
1336
|
-
Init_carray_core ()
|
|
2464
|
+
Init_carray_core (void)
|
|
1337
2465
|
{
|
|
1338
2466
|
id_decode = rb_intern("decode");
|
|
1339
2467
|
id_encode = rb_intern("encode");
|
|
@@ -1354,5 +2482,20 @@ Init_carray_core ()
|
|
|
1354
2482
|
|
|
1355
2483
|
rb_define_method(rb_cCArray, "fields", rb_ca_fields, 0);
|
|
1356
2484
|
rb_define_method(rb_cCArray, "fields_at", rb_ca_fields_at, -1);
|
|
2485
|
+
|
|
1357
2486
|
}
|
|
1358
2487
|
|
|
2488
|
+
|
|
2489
|
+
/* ------------------------------------------------------------------- */
|
|
2490
|
+
|
|
2491
|
+
/* The out-of-build form of the ca_is_entity macro (see carray.h). Defined
|
|
2492
|
+
last so the macro stays in force for the rest of this file. */
|
|
2493
|
+
|
|
2494
|
+
#undef ca_is_entity
|
|
2495
|
+
|
|
2496
|
+
int
|
|
2497
|
+
ca_is_entity (const void *ap)
|
|
2498
|
+
{
|
|
2499
|
+
const CArray *ca = (const CArray *) ap;
|
|
2500
|
+
return ( ca_func[ca->obj_type].entity_type == CA_REAL_ARRAY );
|
|
2501
|
+
}
|