carray 3.0.2 → 3.0.4
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/CHANGELOG.md +568 -0
- data/README.md +6 -6
- data/carray.gemspec +1 -1
- data/ext/ca_axis_descriptor.h +27 -24
- data/ext/ca_axis_dispatch.c +220 -0
- data/ext/ca_axis_group.c +201 -123
- data/ext/ca_binop_dispatch.c +46 -52
- data/ext/ca_binop_dispatch.h +1 -1
- data/ext/ca_categorical_iterator.c +249 -183
- data/ext/ca_compare.c +7 -12
- data/ext/ca_for_buffer.h +21 -68
- data/ext/ca_group_iter.c +38 -23
- data/ext/ca_kernel_iterator.c +1765 -2076
- data/ext/ca_kernel_iterator.h +584 -566
- data/ext/ca_monop_dispatch.c +86 -63
- data/ext/ca_monop_dispatch.h +5 -0
- data/ext/ca_obj_array.c +223 -56
- data/ext/ca_obj_bincmp.c +172 -138
- data/ext/ca_obj_binop.c +116 -230
- data/ext/ca_obj_bitarray.c +40 -26
- data/ext/ca_obj_bitfield.c +100 -68
- data/ext/ca_obj_block.c +19 -9
- data/ext/ca_obj_byte_swap.c +45 -46
- data/ext/ca_obj_const_string.c +269 -44
- data/ext/ca_obj_face.c +612 -89
- data/ext/ca_obj_face.h +111 -47
- data/ext/ca_obj_fake.c +36 -79
- data/ext/ca_obj_farray.c +7 -3
- data/ext/ca_obj_field.c +33 -8
- data/ext/ca_obj_fixlen_string.c +23 -24
- data/ext/ca_obj_grid.c +541 -210
- data/ext/ca_obj_meld.c +331 -111
- data/ext/ca_obj_moncmp.c +80 -65
- data/ext/ca_obj_monop.c +177 -149
- data/ext/ca_obj_object.c +664 -248
- data/ext/ca_obj_record.c +63 -23
- data/ext/ca_obj_reduce.c +18 -16
- data/ext/ca_obj_refer.c +140 -58
- data/ext/ca_obj_remap.c +85 -24
- data/ext/ca_obj_repeat.c +24 -9
- data/ext/ca_obj_roll.c +121 -16
- data/ext/ca_obj_select.c +61 -58
- data/ext/ca_obj_select_axis.c +73 -107
- data/ext/ca_obj_shift.c +32 -102
- data/ext/ca_obj_stack.c +372 -122
- data/ext/ca_obj_stride.c +348 -140
- data/ext/ca_obj_string.c +88 -22
- data/ext/ca_obj_tile.c +38 -21
- data/ext/ca_obj_time.c +45 -31
- data/ext/ca_obj_timedelta.c +43 -29
- data/ext/ca_obj_transpose.c +17 -6
- data/ext/ca_obj_triop.c +125 -141
- data/ext/ca_obj_window.c +330 -44
- data/ext/ca_op_ipower.c +24 -11
- data/ext/ca_op_powi.h +57 -30
- data/ext/ca_pow5_table.h +656 -0
- data/ext/ca_sort_kernels.h +70 -0
- data/ext/ca_sweep_engine.c +104 -81
- data/ext/ca_sweep_engine.h +7 -20
- data/ext/ca_triop_dispatch.c +4 -1
- data/ext/ca_triop_dispatch.h +5 -2
- data/ext/caframe_csv_reader.c +542 -0
- data/ext/carray.h +241 -92
- data/ext/carray_access.c +608 -276
- data/ext/carray_address_basis.c +494 -97
- data/ext/carray_attribute.c +129 -4
- data/ext/carray_bincount.c +39 -7
- data/ext/carray_broadcast.c +56 -19
- data/ext/carray_build_flags.h +1 -1
- data/ext/carray_call_cfunc.c +108 -28
- data/ext/carray_cast.c +1576 -223
- data/ext/carray_cast_func.rb +77 -24
- data/ext/carray_class.c +4 -3
- data/ext/carray_conversion.c +208 -75
- data/ext/carray_copy.c +82 -24
- data/ext/carray_core.c +646 -162
- data/ext/carray_count.c +171 -88
- data/ext/carray_element.c +94 -60
- data/ext/carray_factorize.c +670 -324
- data/ext/carray_generate.c +260 -83
- data/ext/carray_histogram.c +209 -106
- data/ext/carray_hold.c +14 -16
- data/ext/carray_index_classifier.c +422 -243
- data/ext/carray_internal.h +692 -3
- data/ext/carray_kernels_bincmp.c +7 -0
- data/ext/carray_kernels_binop.c +487 -120
- data/ext/carray_kernels_init.c +653 -50
- data/ext/carray_kernels_moncmp.c +15 -8
- data/ext/carray_kernels_monop.c +99 -43
- data/ext/carray_kernels_reduce_aggregate.c +3925 -6112
- data/ext/carray_kernels_reduce_boolean.c +112 -36
- data/ext/carray_kernels_reduce_cumulative.c +2127 -6903
- data/ext/carray_kernels_reduce_extreme.c +3032 -2892
- data/ext/carray_kernels_reduce_variance.c +439 -500
- data/ext/carray_kernels_scan.c +1097 -488
- data/ext/carray_kernels_search.c +3787 -2176
- data/ext/carray_kernels_sort.c +1200 -920
- data/ext/carray_kernels_triop.c +479 -7
- data/ext/carray_key_windows.c +216 -0
- data/ext/carray_lazy.c +967 -153
- data/ext/carray_loop.c +81 -65
- data/ext/carray_mask.c +910 -324
- data/ext/carray_math_kernel.h +1 -0
- data/ext/carray_median_percentile.c +299 -100
- data/ext/carray_memory_view.c +872 -758
- data/ext/carray_operator.c +1171 -364
- data/ext/carray_order.c +505 -133
- data/ext/carray_partition.c +112 -65
- data/ext/carray_random.c +327 -239
- data/ext/carray_round.c +273 -0
- data/ext/carray_scatter.c +346 -78
- data/ext/carray_segment.c +257 -0
- data/ext/carray_slab.c +249 -86
- data/ext/carray_sort.c +224 -227
- data/ext/carray_sort_kernel.c +8 -1
- data/ext/carray_struct.c +118 -39
- data/ext/carray_test.c +193 -69
- data/ext/carray_time_text.c +1182 -0
- data/ext/carray_undef.c +4 -0
- data/ext/carray_utils.c +579 -113
- data/ext/extconf.rb +27 -2
- data/ext/mk_call_cfunc.rb +19 -4
- data/ext/mk_pow5_table.rb +35 -0
- data/ext/mkkernel.rb +1202 -1014
- data/ext/ruby_carray.c +17 -5
- data/ext/version.h +4 -4
- data/ext/version.rb +4 -2
- data/lib/carray/attribute.rb +160 -17
- data/lib/carray/autoload_carray.rb +23 -27
- data/lib/carray/autoload_method_extension.rb +20 -5
- data/lib/carray/axis_group.rb +360 -63
- data/lib/carray/basics.rb +162 -28
- data/lib/carray/bincount_nd.rb +18 -2
- data/lib/carray/block_iterator.rb +231 -58
- data/lib/carray/boolean_reduce.rb +34 -42
- data/lib/carray/categorical.rb +242 -52
- data/lib/carray/categorical_iterator.rb +437 -827
- data/lib/carray/complex.rb +41 -34
- data/lib/carray/conditional.rb +150 -14
- data/lib/carray/const_string.rb +59 -4
- data/lib/carray/construct.rb +15 -11
- data/lib/carray/data_type_extension.rb +1 -0
- data/lib/carray/distance_limit.rb +77 -0
- data/lib/carray/fixlen_string.rb +7 -1
- data/lib/carray/frame/column_verbs.rb +70 -0
- data/lib/carray/frame/concat.rb +157 -6
- data/lib/carray/frame/convert.rb +82 -7
- data/lib/carray/frame/csv_parser.rb +688 -87
- data/lib/carray/frame/describe.rb +80 -0
- data/lib/carray/frame/frame.rb +369 -45
- data/lib/carray/frame/group.rb +631 -90
- data/lib/carray/frame/io.rb +506 -79
- data/lib/carray/frame/join.rb +411 -62
- data/lib/carray/frame/records.rb +116 -19
- data/lib/carray/frame/reshape.rb +455 -0
- data/lib/carray/frame/sort.rb +30 -18
- data/lib/carray/frame/verbs.rb +868 -61
- data/lib/carray/frame/windows.rb +117 -0
- data/lib/carray/frame.rb +4 -0
- data/lib/carray/fuse_source.rb +118 -2
- data/lib/carray/fusion.rb +257 -42
- data/lib/carray/histogram.rb +6 -1
- data/lib/carray/inspect.rb +54 -13
- data/lib/carray/iterator.rb +99 -9
- data/lib/carray/key_windows.rb +620 -0
- data/lib/carray/lazy.rb +216 -193
- data/lib/carray/mask_gap_fill.rb +112 -92
- data/lib/carray/math.rb +32 -19
- data/lib/carray/meld_reduce.rb +52 -21
- data/lib/carray/methods/align_addr.rb +8 -4
- data/lib/carray/methods/bin.rb +26 -18
- data/lib/carray/methods/bincount.rb +18 -2
- data/lib/carray/methods/bit_string.rb +15 -4
- data/lib/carray/methods/choose.rb +16 -3
- data/lib/carray/methods/composition.rb +18 -10
- data/lib/carray/methods/factorize.rb +2 -2
- data/lib/carray/methods/gather_nd.rb +25 -5
- data/lib/carray/methods/insert_block.rb +9 -14
- data/lib/carray/methods/is_between.rb +59 -0
- data/lib/carray/methods/is_in.rb +140 -10
- data/lib/carray/methods/join.rb +2 -7
- data/lib/carray/methods/lag.rb +49 -0
- data/lib/carray/methods/locate_addr.rb +182 -45
- data/lib/carray/methods/mode.rb +18 -1
- data/lib/carray/methods/nunique.rb +11 -6
- data/lib/carray/methods/repeat.rb +1 -20
- data/lib/carray/methods/resize.rb +21 -23
- data/lib/carray/methods/snap.rb +34 -12
- data/lib/carray/methods/unique.rb +11 -2
- data/lib/carray/methods/value_counts.rb +4 -11
- data/lib/carray/record_order.rb +208 -0
- data/lib/carray/rng.rb +3 -0
- data/lib/carray/runtime.rb +65 -17
- data/lib/carray/segment_iterator.rb +1221 -0
- data/lib/carray/serialize.rb +214 -30
- data/lib/carray/slab_iterator.rb +101 -27
- data/lib/carray/stack.rb +10 -4
- data/lib/carray/string.rb +105 -5
- data/lib/carray/string_operation_extension.rb +12 -4
- data/lib/carray/struct.rb +137 -58
- data/lib/carray/struct_builder.rb +124 -17
- data/lib/carray/time.rb +1222 -186
- data/lib/carray/window_iterator.rb +242 -81
- data/lib/carray.rb +9 -2
- data/yard-stubs/ca_obj_array.rb +4 -4
- data/yard-stubs/ca_obj_bitarray.rb +7 -0
- data/yard-stubs/ca_obj_bitfield.rb +23 -12
- data/yard-stubs/ca_obj_fake.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +5 -1
- data/yard-stubs/ca_obj_grid.rb +21 -10
- data/yard-stubs/ca_obj_refer.rb +5 -4
- data/yard-stubs/ca_obj_roll.rb +2 -3
- data/yard-stubs/ca_obj_shift.rb +5 -1
- data/yard-stubs/ca_obj_stride.rb +10 -7
- data/yard-stubs/ca_obj_tile.rb +1 -1
- data/yard-stubs/ca_obj_transpose.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +9 -5
- data/yard-stubs/carray_access.rb +11 -5
- data/yard-stubs/carray_attribute.rb +0 -5
- data/yard-stubs/carray_broadcast.rb +1 -1
- data/yard-stubs/carray_cast.rb +88 -43
- data/yard-stubs/carray_conversion.rb +15 -2
- data/yard-stubs/carray_copy.rb +12 -5
- data/yard-stubs/carray_core.rb +3 -3
- data/yard-stubs/carray_count.rb +9 -0
- data/yard-stubs/carray_element.rb +10 -4
- data/yard-stubs/carray_generate.rb +21 -1
- data/yard-stubs/carray_lazy.rb +5 -1
- data/yard-stubs/carray_loop.rb +4 -1
- data/yard-stubs/carray_mask.rb +60 -12
- data/yard-stubs/carray_math.rb +52 -14
- data/yard-stubs/carray_median_percentile.rb +15 -8
- data/yard-stubs/carray_memory_view.rb +40 -23
- data/yard-stubs/carray_order.rb +61 -29
- data/yard-stubs/carray_random.rb +13 -4
- data/yard-stubs/carray_scatter.rb +21 -9
- data/yard-stubs/carray_segment.rb +72 -0
- data/yard-stubs/carray_slab.rb +10 -2
- data/yard-stubs/carray_sort.rb +49 -8
- data/yard-stubs/carray_test.rb +15 -3
- data/yard-stubs/carray_utils.rb +0 -26
- metadata +19 -5
- data/ext/ca_for_each_element.h +0 -239
- data/ext/carray_kernels_map.c +0 -3467
- data/lib/carray/arrow_tensor.rb +0 -401
- data/lib/carray/table.rb +0 -108
data/ext/carray_memory_view.c
CHANGED
|
@@ -16,8 +16,11 @@
|
|
|
16
16
|
- CArray.wrap_memory_view (zero-copy; contiguous only)
|
|
17
17
|
|
|
18
18
|
Format-string contract:
|
|
19
|
-
- Producer emits
|
|
20
|
-
(
|
|
19
|
+
- Producer emits Ruby's vocabulary (ruby/memory_view.h), records
|
|
20
|
+
included; only the types Ruby cannot spell ('?', 'Zf' / 'Zd', "Ns")
|
|
21
|
+
stay PEP 3118. The consumer is Postel-permissive (accepts synonyms
|
|
22
|
+
other producers emit). A record's member names, which Ruby's format
|
|
23
|
+
cannot carry, are CARecord#memory_view_field_names.
|
|
21
24
|
- See docs/interop/MemoryViewFormat.md for the format-string contract.
|
|
22
25
|
- See devel/DESIGN_MemoryView.md for the design rationale.
|
|
23
26
|
- See docs/interop/MemoryView.md for the user-facing MV API.
|
|
@@ -26,11 +29,16 @@
|
|
|
26
29
|
|
|
27
30
|
#include "carray.h"
|
|
28
31
|
#include "ca_obj_face.h" /* ca_strip_face for Face producers */
|
|
32
|
+
#include "carray_internal.h"
|
|
29
33
|
#include "ruby/memory_view.h"
|
|
34
|
+
#include "ruby/st.h"
|
|
35
|
+
#include "ruby/thread_native.h"
|
|
36
|
+
#include <ctype.h>
|
|
30
37
|
#include <string.h>
|
|
31
38
|
|
|
32
|
-
/* private_data
|
|
33
|
-
|
|
39
|
+
/* private_data holds the view's own copy of a record format (see ca_mv_get)
|
|
40
|
+
and is NULL otherwise; ca_mv_release frees it. No other per-view state:
|
|
41
|
+
ca_mv_get is alias-only. */
|
|
34
42
|
|
|
35
43
|
/* ---------------- forward declarations for non-public structs ----------------
|
|
36
44
|
CABlock, CARefer, CARepeat are declared in carray.h. CAFarray
|
|
@@ -55,12 +63,10 @@ typedef struct {
|
|
|
55
63
|
|
|
56
64
|
/* ---------------- obj_type strategy ---------------- */
|
|
57
65
|
|
|
58
|
-
/* Strategy = label only
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
structurally unreachable -- ca_mv_check_alias_chain rejects every
|
|
63
|
-
obj_type that maps to it. */
|
|
66
|
+
/* Strategy = label only. The export is alias-only and is decided by
|
|
67
|
+
ca_mv_compose_to_entity alone; the labels say which classes get a
|
|
68
|
+
MemoryView registration and keep ca_mv_runtime_types[] readable.
|
|
69
|
+
Every class labelled CA_MV_ATTACH is refused by that resolver. */
|
|
64
70
|
typedef enum {
|
|
65
71
|
CA_MV_REJECT,
|
|
66
72
|
CA_MV_DIRECT, /* entity / wrap / scalar; ca->ptr valid as-is */
|
|
@@ -72,14 +78,11 @@ typedef enum {
|
|
|
72
78
|
CA_MV_ATTACH, /* unreachable: rejected by alias-chain check */
|
|
73
79
|
} ca_mv_strategy_t;
|
|
74
80
|
|
|
75
|
-
/* Resolved at Init_carray_memory_view.
|
|
76
|
-
|
|
77
|
-
ca_mv_strategy_for can match them with a simple linear scan. */
|
|
81
|
+
/* Resolved at Init_carray_memory_view. Runtime obj_type ids are stored
|
|
82
|
+
here so ca_mv_reject_reason_for can name the class it refuses. */
|
|
78
83
|
static ca_mv_runtime_type_t ca_mv_runtime_types[CA_MV_NUM_RUNTIME_OBJ_TYPES] = {
|
|
79
84
|
{ "CAFarray", "CA_OBJ_FARRAY", -1, Qnil, /* CA_MV_FARRAY */ 4 },
|
|
80
85
|
{ "CATranspose", "CA_OBJ_TRANSPOSE", -1, Qnil, /* CA_MV_TRANS */ 5 },
|
|
81
|
-
/* CAMapping is retired; a[mapper] now builds a CAGrid/CAStride chain
|
|
82
|
-
whose layers are covered above. */
|
|
83
86
|
{ "CAGrid", "CA_OBJ_GRID", -1, Qnil, /* CA_MV_ATTACH */ 7 },
|
|
84
87
|
{ "CAShift", "CA_OBJ_SHIFT", -1, Qnil, /* CA_MV_ATTACH */ 7 },
|
|
85
88
|
{ "CAWindow", "CA_OBJ_WINDOW", -1, Qnil, /* CA_MV_ATTACH */ 7 },
|
|
@@ -143,69 +146,69 @@ ca_mv_format_for (int8_t data_type)
|
|
|
143
146
|
}
|
|
144
147
|
}
|
|
145
148
|
|
|
146
|
-
/*
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
Returns NULL for non-primitive (CA_FIXLEN / CA_OBJECT) or for the
|
|
155
|
-
retired float128 / cmplx256 enum slots. */
|
|
156
|
-
static const char *
|
|
157
|
-
ca_mv_pep3118_char (int8_t data_type)
|
|
158
|
-
{
|
|
159
|
-
switch (data_type) {
|
|
160
|
-
case CA_INT8: return "b";
|
|
161
|
-
case CA_UINT8: return "B";
|
|
162
|
-
case CA_INT16: return "h";
|
|
163
|
-
case CA_UINT16: return "H";
|
|
164
|
-
default: return ca_mv_format_for(data_type);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/* Build (or fetch from cache) the PEP 3118 struct format string for
|
|
169
|
-
a data_class. Walks MEMBER_TABLE in MEMBERS order and emits
|
|
170
|
-
"T{<fmt>:<name>:<fmt>:<name>:}". Returns Qnil if the class
|
|
171
|
-
contains a member type the producer cannot express (nested
|
|
172
|
-
sub-struct class, CArray template, :fixlen, :object).
|
|
173
|
-
|
|
174
|
-
The result is frozen and cached on the data_class via
|
|
175
|
-
@__mv_struct_format__ so successive calls are O(1) and the
|
|
176
|
-
underlying RSTRING_PTR is stable (the cached String is rooted by
|
|
177
|
-
the class, which outlives any MV produced from it). */
|
|
178
|
-
/* Helper: resolve a MEMBER_TABLE member-type slot to (fmt, bytes).
|
|
179
|
-
Returns 1 on success, 0 if the type is not expressible in PEP 3118
|
|
180
|
-
struct format (nested class, CArray template, :fixlen, :object,
|
|
181
|
-
:bitfield). Does NOT raise — :bitfield in particular is checked
|
|
182
|
-
before rb_ca_guess_type so the struct-format walk can give up
|
|
183
|
-
gracefully on bit-bearing structs. */
|
|
149
|
+
/* The Ruby MemoryView specifier for one record member, written into `out`
|
|
150
|
+
with its byte order as ruby/memory_view.h writes it: a suffix ('<' / '>')
|
|
151
|
+
on an integer, its own letter on a float ('e' / 'E' little, 'g' / 'G'
|
|
152
|
+
big). A member without a declared byte order is in the machine's.
|
|
153
|
+
Returns 0 for a member the format has no item for: a nested struct, an
|
|
154
|
+
array member, :fixlen, :object, a bit member, or a complex member with a
|
|
155
|
+
declared byte order (Ruby has no complex specifier to suffix). */
|
|
184
156
|
static int
|
|
185
|
-
|
|
157
|
+
ca_mv_record_member_spec (VALUE info, char *out, size_t outlen,
|
|
158
|
+
ca_size_t *bytes_out)
|
|
186
159
|
{
|
|
187
160
|
VALUE type = rb_ary_entry(info, 1);
|
|
161
|
+
VALUE opts = rb_ary_entry(info, 2);
|
|
162
|
+
VALUE endian = Qnil;
|
|
163
|
+
const char *base;
|
|
164
|
+
int8_t dt;
|
|
188
165
|
if (!SYMBOL_P(type)) return 0;
|
|
189
166
|
ID type_id = SYM2ID(type);
|
|
190
|
-
/* Reject explicitly unsupported symbol types up front (so
|
|
191
|
-
rb_ca_guess_type never sees them and never raises). */
|
|
192
167
|
if (type_id == rb_intern("bitfield") ||
|
|
193
168
|
type_id == rb_intern("fixlen") ||
|
|
194
169
|
type_id == rb_intern("object")) {
|
|
195
170
|
return 0;
|
|
196
171
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (!
|
|
200
|
-
|
|
172
|
+
dt = rb_ca_guess_type(rb_sym2str(type));
|
|
173
|
+
base = ca_mv_format_for(dt);
|
|
174
|
+
if (!base) return 0;
|
|
175
|
+
if (RB_TYPE_P(opts, T_HASH)) {
|
|
176
|
+
endian = rb_hash_aref(opts, ID2SYM(rb_intern("endian")));
|
|
177
|
+
}
|
|
178
|
+
if ( SYMBOL_P(endian) && ( SYM2ID(endian) == rb_intern("big")
|
|
179
|
+
|| SYM2ID(endian) == rb_intern("little") ) ) {
|
|
180
|
+
int big = ( SYM2ID(endian) == rb_intern("big") );
|
|
181
|
+
switch (dt) {
|
|
182
|
+
case CA_FLOAT32: base = big ? "g" : "e"; break;
|
|
183
|
+
case CA_FLOAT64: base = big ? "G" : "E"; break;
|
|
184
|
+
case CA_CMPLX64: case CA_CMPLX128: return 0;
|
|
185
|
+
case CA_BOOLEAN: case CA_INT8: case CA_UINT8: break; /* one byte */
|
|
186
|
+
default:
|
|
187
|
+
snprintf(out, outlen, "%s%s", base, big ? ">" : "<");
|
|
188
|
+
*bytes_out = (ca_size_t) ca_sizeof[dt];
|
|
189
|
+
return 1;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
snprintf(out, outlen, "%s", base);
|
|
201
193
|
*bytes_out = (ca_size_t) ca_sizeof[dt];
|
|
202
194
|
return 1;
|
|
203
195
|
}
|
|
204
196
|
|
|
197
|
+
/* Build (or fetch from cache) the Ruby MemoryView format of a record: the
|
|
198
|
+
members' specifiers in their byte order in the record, with "x<N>" for
|
|
199
|
+
the padding between them and after the last. Ruby's format has no
|
|
200
|
+
member names; the record array gives them as #memory_view_field_names,
|
|
201
|
+
one per specifier. Returns Qnil for a record the format cannot describe:
|
|
202
|
+
a member it has no item for (see above), or members that overlap or come
|
|
203
|
+
out of offset order (a union).
|
|
204
|
+
|
|
205
|
+
The result is frozen and cached on the data_class. It is an ordinary
|
|
206
|
+
String that compaction can move, so a view takes a copy of its bytes
|
|
207
|
+
rather than its pointer (ca_mv_get). */
|
|
205
208
|
static VALUE
|
|
206
209
|
ca_mv_struct_format_for_data_class (VALUE data_class)
|
|
207
210
|
{
|
|
208
|
-
ID iv = rb_intern("@
|
|
211
|
+
ID iv = rb_intern("@__mv_record_format__");
|
|
209
212
|
if (rb_ivar_defined(data_class, iv)) {
|
|
210
213
|
VALUE cached = rb_ivar_get(data_class, iv);
|
|
211
214
|
/* Qfalse is a sentinel meaning "tried, found unsupported". */
|
|
@@ -216,61 +219,36 @@ ca_mv_struct_format_for_data_class (VALUE data_class)
|
|
|
216
219
|
VALUE table = rb_const_get(data_class, rb_intern("MEMBER_TABLE"));
|
|
217
220
|
long n = RARRAY_LEN(members);
|
|
218
221
|
long i;
|
|
219
|
-
VALUE buf = rb_str_buf_new(
|
|
222
|
+
VALUE buf = rb_str_buf_new(4 * n + 8);
|
|
220
223
|
ca_size_t cursor = 0;
|
|
221
224
|
ca_size_t data_size = NUM2SIZE(rb_const_get(data_class, rb_intern("DATA_SIZE")));
|
|
222
|
-
|
|
223
|
-
rb_str_buf_cat_ascii(buf, "T{");
|
|
225
|
+
char spec[16], pad[32];
|
|
224
226
|
|
|
225
227
|
for (i = 0; i < n; i++) {
|
|
226
|
-
VALUE
|
|
227
|
-
|
|
228
|
+
VALUE info = rb_hash_aref(table, rb_ary_entry(members, i));
|
|
229
|
+
ca_size_t offset, mb;
|
|
228
230
|
if (NIL_P(info)) goto unsupported;
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
ca_size_t mb;
|
|
233
|
-
if (!ca_mv_struct_member_resolve(info, &fmt, &mb)) goto unsupported;
|
|
234
|
-
|
|
235
|
-
/* If alignment or explicit padding has left a gap before this
|
|
236
|
-
member, emit "<N>x:" to account for it. PEP 3118 uses 'x'
|
|
237
|
-
as the pad-byte specifier. The canonical form elides the name
|
|
238
|
-
slot after the pad spec (padding is anonymous by definition);
|
|
239
|
-
consumers MUST accept both elided and named (legacy "<N>x:_:")
|
|
240
|
-
forms. */
|
|
231
|
+
offset = NUM2SIZE(rb_ary_entry(info, 0));
|
|
232
|
+
if (offset < cursor) goto unsupported;
|
|
233
|
+
if (!ca_mv_record_member_spec(info, spec, sizeof(spec), &mb)) goto unsupported;
|
|
241
234
|
if (offset > cursor) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
(long long) (offset - cursor));
|
|
245
|
-
rb_str_buf_cat_ascii(buf, pad_decl);
|
|
246
|
-
cursor = offset;
|
|
235
|
+
snprintf(pad, sizeof(pad), "x%lld", (long long) (offset - cursor));
|
|
236
|
+
rb_str_buf_cat_ascii(buf, pad);
|
|
247
237
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
rb_str_buf_cat_ascii(buf, ":");
|
|
251
|
-
/* MEMBERS entries are strings; emit verbatim. */
|
|
252
|
-
rb_str_buf_append(buf, rb_String(name));
|
|
253
|
-
rb_str_buf_cat_ascii(buf, ":");
|
|
254
|
-
cursor += mb;
|
|
238
|
+
rb_str_buf_cat_ascii(buf, spec);
|
|
239
|
+
cursor = offset + mb;
|
|
255
240
|
}
|
|
256
|
-
|
|
257
|
-
/* Trailing padding (DATA_SIZE > last member's end). Elided form
|
|
258
|
-
(see the alignment-gap branch above for rationale). */
|
|
241
|
+
if (cursor > data_size) goto unsupported;
|
|
259
242
|
if (cursor < data_size) {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
(long long) (data_size - cursor));
|
|
263
|
-
rb_str_buf_cat_ascii(buf, pad_decl);
|
|
243
|
+
snprintf(pad, sizeof(pad), "x%lld", (long long) (data_size - cursor));
|
|
244
|
+
rb_str_buf_cat_ascii(buf, pad);
|
|
264
245
|
}
|
|
265
246
|
|
|
266
|
-
rb_str_buf_cat_ascii(buf, "}");
|
|
267
247
|
rb_obj_freeze(buf);
|
|
268
248
|
rb_ivar_set(data_class, iv, buf);
|
|
269
249
|
return buf;
|
|
270
250
|
|
|
271
251
|
unsupported:
|
|
272
|
-
/* Cache Qfalse to remember "we tried and gave up" — avoids
|
|
273
|
-
re-walking MEMBER_TABLE on every wrap call. */
|
|
274
252
|
rb_ivar_set(data_class, iv, Qfalse);
|
|
275
253
|
return Qnil;
|
|
276
254
|
}
|
|
@@ -282,7 +260,7 @@ ca_mv_data_type_exportable (int8_t data_type)
|
|
|
282
260
|
}
|
|
283
261
|
|
|
284
262
|
/* Same as ca_mv_data_type_exportable, but also accepts CA_FIXLEN:
|
|
285
|
-
- with a data_class -> emit
|
|
263
|
+
- with a data_class -> emit the record's Ruby format (members in order)
|
|
286
264
|
- without a data_class -> emit PEP 3118 "Ns" fixed-bytes format */
|
|
287
265
|
static int
|
|
288
266
|
ca_mv_ca_exportable (CArray *ca, VALUE obj)
|
|
@@ -302,33 +280,80 @@ ca_mv_ca_exportable (CArray *ca, VALUE obj)
|
|
|
302
280
|
}
|
|
303
281
|
|
|
304
282
|
/* Reverse mapping for the importer (Postel consumer; see
|
|
305
|
-
docs/interop/MemoryViewFormat.md for the contract)
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
-
|
|
283
|
+
docs/interop/MemoryViewFormat.md for the contract). Both vocabularies
|
|
284
|
+
are read: Ruby's (byte order as a suffix, "i<", "s!>"; '_' for '!';
|
|
285
|
+
'e' 'E' 'g' 'G' 'n' 'N' 'v' 'V' carrying their own order; 'j' 'J' for
|
|
286
|
+
intptr) and PEP 3118's (order as a prefix '<' '>' '!' '=' '@'; 'b' 'B'
|
|
287
|
+
'h' 'H' '?'; 'Zf' 'Zd'). The alignment modifier '|' is skipped.
|
|
288
|
+
- CArray stores host order, so an order that disagrees with the host
|
|
289
|
+
is refused. A one-byte type has no order, so any is accepted.
|
|
290
|
+
- Dispatch on the (type, item_size) tuple.
|
|
313
291
|
- Synonyms accepted but not emitted: 'C'/1 maps to UINT8 (so other
|
|
314
292
|
producers' bool buffers are received as UINT8); 'ff' / 'dd' map to
|
|
315
293
|
complex64/128 (producers that split complex into two floats). */
|
|
294
|
+
|
|
295
|
+
/* Byte order named in a format: 0 none, 1 little, 2 big. */
|
|
296
|
+
#define CA_MV_ORDER_NONE 0
|
|
297
|
+
#define CA_MV_ORDER_LITTLE 1
|
|
298
|
+
#define CA_MV_ORDER_BIG 2
|
|
299
|
+
|
|
300
|
+
static int
|
|
301
|
+
ca_mv_order_merge (int a, int b)
|
|
302
|
+
{
|
|
303
|
+
if ( a == CA_MV_ORDER_NONE ) return b;
|
|
304
|
+
if ( b == CA_MV_ORDER_NONE ) return a;
|
|
305
|
+
return ( a == b ) ? a : -1;
|
|
306
|
+
}
|
|
307
|
+
|
|
316
308
|
static int8_t
|
|
317
309
|
ca_mv_data_type_from_format (const char *format, ssize_t item_size)
|
|
318
310
|
{
|
|
311
|
+
const int host = (ca_endian == CA_LITTLE_ENDIAN) ? CA_MV_ORDER_LITTLE
|
|
312
|
+
: CA_MV_ORDER_BIG;
|
|
319
313
|
const char *fmt = format ? format : "";
|
|
320
|
-
|
|
314
|
+
char buf[16];
|
|
315
|
+
size_t len;
|
|
316
|
+
int order = CA_MV_ORDER_NONE;
|
|
317
|
+
int one_byte;
|
|
318
|
+
|
|
321
319
|
if (*fmt == '|') fmt++;
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
320
|
+
switch (*fmt) {
|
|
321
|
+
case '<': order = CA_MV_ORDER_LITTLE; fmt++; break;
|
|
322
|
+
case '>': case '!': order = CA_MV_ORDER_BIG; fmt++; break;
|
|
323
|
+
case '=': case '@': order = host; fmt++; break;
|
|
324
|
+
}
|
|
325
|
+
len = strlen(fmt);
|
|
326
|
+
if ( len == 0 || len >= sizeof(buf) ) return -1;
|
|
327
|
+
memcpy(buf, fmt, len + 1);
|
|
328
|
+
|
|
329
|
+
/* Ruby's suffix form on a single type: "i<", "s!>", "q_<". */
|
|
330
|
+
if ( len >= 2 && len <= 3 && isalpha((unsigned char) buf[0]) &&
|
|
331
|
+
( buf[len - 1] == '<' || buf[len - 1] == '>' ) ) {
|
|
332
|
+
order = ca_mv_order_merge(order, buf[len - 1] == '<' ? CA_MV_ORDER_LITTLE
|
|
333
|
+
: CA_MV_ORDER_BIG);
|
|
334
|
+
if ( order < 0 ) return -1;
|
|
335
|
+
buf[--len] = '\0';
|
|
336
|
+
}
|
|
337
|
+
/* '_' is Ruby's spelling of the native-size modifier '!'. */
|
|
338
|
+
if ( len == 2 && buf[1] == '_' ) {
|
|
339
|
+
buf[1] = '!';
|
|
340
|
+
}
|
|
341
|
+
/* Types that carry their own byte order. */
|
|
342
|
+
if ( len == 1 ) {
|
|
343
|
+
int own = CA_MV_ORDER_NONE;
|
|
344
|
+
switch (buf[0]) {
|
|
345
|
+
case 'e': case 'E': case 'v': case 'V': own = CA_MV_ORDER_LITTLE; break;
|
|
346
|
+
case 'g': case 'G': case 'n': case 'N': own = CA_MV_ORDER_BIG; break;
|
|
347
|
+
}
|
|
348
|
+
order = ca_mv_order_merge(order, own);
|
|
349
|
+
if ( order < 0 ) return -1;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
one_byte = ( len == 1 && strchr("cCbB?", buf[0]) != NULL );
|
|
353
|
+
if ( order != CA_MV_ORDER_NONE && order != host && ! one_byte ) {
|
|
354
|
+
return -1;
|
|
330
355
|
}
|
|
331
|
-
|
|
356
|
+
fmt = buf;
|
|
332
357
|
|
|
333
358
|
/* single-byte formats */
|
|
334
359
|
if (strcmp(fmt, "c") == 0 && item_size == 1) return CA_INT8;
|
|
@@ -341,24 +366,32 @@ ca_mv_data_type_from_format (const char *format, ssize_t item_size)
|
|
|
341
366
|
if ((strcmp(fmt, "s") == 0 || strcmp(fmt, "s!") == 0 ||
|
|
342
367
|
strcmp(fmt, "h") == 0) && item_size == 2) return CA_INT16;
|
|
343
368
|
if ((strcmp(fmt, "S") == 0 || strcmp(fmt, "S!") == 0 ||
|
|
344
|
-
strcmp(fmt, "H") == 0
|
|
369
|
+
strcmp(fmt, "H") == 0 || strcmp(fmt, "v") == 0 ||
|
|
370
|
+
strcmp(fmt, "n") == 0) && item_size == 2) return CA_UINT16;
|
|
345
371
|
|
|
346
372
|
/* 32-bit */
|
|
347
373
|
if ((strcmp(fmt, "l") == 0 || strcmp(fmt, "i") == 0 ||
|
|
348
|
-
strcmp(fmt, "i!") == 0 || strcmp(fmt, "l!") == 0
|
|
374
|
+
strcmp(fmt, "i!") == 0 || strcmp(fmt, "l!") == 0 ||
|
|
375
|
+
strcmp(fmt, "j") == 0) && item_size == 4) return CA_INT32;
|
|
349
376
|
if ((strcmp(fmt, "L") == 0 || strcmp(fmt, "I") == 0 ||
|
|
350
|
-
strcmp(fmt, "I!") == 0 || strcmp(fmt, "L!") == 0
|
|
377
|
+
strcmp(fmt, "I!") == 0 || strcmp(fmt, "L!") == 0 ||
|
|
378
|
+
strcmp(fmt, "J") == 0 || strcmp(fmt, "V") == 0 ||
|
|
379
|
+
strcmp(fmt, "N") == 0) && item_size == 4) return CA_UINT32;
|
|
351
380
|
|
|
352
381
|
/* 64-bit. `l`/`L` at item_size 8 accepted as LP64 native long
|
|
353
382
|
(numpy default for `np.array([1,2,3]).dtype == int64`). */
|
|
354
383
|
if ((strcmp(fmt, "q") == 0 || strcmp(fmt, "q!") == 0 ||
|
|
355
|
-
strcmp(fmt, "l!") == 0 || strcmp(fmt, "l") == 0
|
|
384
|
+
strcmp(fmt, "l!") == 0 || strcmp(fmt, "l") == 0 ||
|
|
385
|
+
strcmp(fmt, "j") == 0) && item_size == 8) return CA_INT64;
|
|
356
386
|
if ((strcmp(fmt, "Q") == 0 || strcmp(fmt, "Q!") == 0 ||
|
|
357
|
-
strcmp(fmt, "L!") == 0 || strcmp(fmt, "L") == 0
|
|
387
|
+
strcmp(fmt, "L!") == 0 || strcmp(fmt, "L") == 0 ||
|
|
388
|
+
strcmp(fmt, "J") == 0) && item_size == 8) return CA_UINT64;
|
|
358
389
|
|
|
359
|
-
/* floats
|
|
360
|
-
if (strcmp(fmt, "f") == 0
|
|
361
|
-
|
|
390
|
+
/* floats */
|
|
391
|
+
if ((strcmp(fmt, "f") == 0 || strcmp(fmt, "e") == 0 ||
|
|
392
|
+
strcmp(fmt, "g") == 0) && item_size == 4) return CA_FLOAT32;
|
|
393
|
+
if ((strcmp(fmt, "d") == 0 || strcmp(fmt, "E") == 0 ||
|
|
394
|
+
strcmp(fmt, "G") == 0) && item_size == 8) return CA_FLOAT64;
|
|
362
395
|
|
|
363
396
|
/* complex: PEP 3118 'Zf' / 'Zd' canonical. */
|
|
364
397
|
if (strcmp(fmt, "Zf") == 0 && item_size == 8) return CA_CMPLX64;
|
|
@@ -424,35 +457,6 @@ ca_mv_carray_bytes_for (int8_t data_type, ssize_t item_size)
|
|
|
424
457
|
: (ca_size_t) ca_sizeof[data_type];
|
|
425
458
|
}
|
|
426
459
|
|
|
427
|
-
/* ---------------- strategy lookup ---------------- */
|
|
428
|
-
|
|
429
|
-
static ca_mv_strategy_t
|
|
430
|
-
ca_mv_strategy_for (int16_t obj_type)
|
|
431
|
-
{
|
|
432
|
-
/* compile-time enum members */
|
|
433
|
-
if (obj_type == CA_OBJ_ARRAY ||
|
|
434
|
-
obj_type == CA_OBJ_ARRAY_WRAP ||
|
|
435
|
-
obj_type == CA_OBJ_SCALAR) return CA_MV_DIRECT;
|
|
436
|
-
if (obj_type == CA_OBJ_REFER) return CA_MV_REFER;
|
|
437
|
-
if (obj_type == CA_OBJ_BLOCK) return CA_MV_BLOCK;
|
|
438
|
-
if (obj_type == CA_OBJ_SELECT) return CA_MV_ATTACH;
|
|
439
|
-
if (obj_type == CA_OBJ_REPEAT) return CA_MV_REPEAT;
|
|
440
|
-
if (obj_type == CA_OBJ_OBJECT) return CA_MV_REJECT;
|
|
441
|
-
/* Plain CAStride (created via #as_strided, or wrap_memory_view from
|
|
442
|
-
a strided producer). Reuse the CA_MV_TRANS handler since it
|
|
443
|
-
already reads strides[] / base_offset directly from the CAStride
|
|
444
|
-
layout -- no CATranspose-specific code in that path anymore. */
|
|
445
|
-
if (obj_type == CA_OBJ_STRIDE) return CA_MV_TRANS;
|
|
446
|
-
|
|
447
|
-
/* runtime-assigned obj_types */
|
|
448
|
-
for (int i = 0; i < CA_MV_NUM_RUNTIME_OBJ_TYPES; i++) {
|
|
449
|
-
if (ca_mv_runtime_types[i].id == obj_type) {
|
|
450
|
-
return (ca_mv_strategy_t) ca_mv_runtime_types[i].strategy;
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
return CA_MV_REJECT;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
460
|
/* ---------------- helpers ---------------- */
|
|
457
461
|
|
|
458
462
|
static int
|
|
@@ -507,6 +511,8 @@ typedef struct {
|
|
|
507
511
|
int depth; /* 0 = the view itself, 1 = parent, 2 = grand-, ... */
|
|
508
512
|
const char *class_name; /* class name of the offending link */
|
|
509
513
|
const char *reason; /* short explanation */
|
|
514
|
+
int face; /* refused because it is a Face: a copy is
|
|
515
|
+
refused too, so no copy is suggested */
|
|
510
516
|
} ca_mv_reject_t;
|
|
511
517
|
|
|
512
518
|
static bool ca_mv_stride_is_contig (CAStride *ca); /* defined below */
|
|
@@ -551,6 +557,18 @@ ca_mv_reject_reason_for (CArray *ca)
|
|
|
551
557
|
return "this kind of view is not alias-capable";
|
|
552
558
|
}
|
|
553
559
|
|
|
560
|
+
/* The entities whose bytes the producer exports: CArray, CAWrap and
|
|
561
|
+
CScalar. An entity installed from outside the core (ca_install_obj_type,
|
|
562
|
+
e.g. a CASource subclass) may hold its bytes elsewhere (cold at rest),
|
|
563
|
+
so neither it nor a view onto it is exported. */
|
|
564
|
+
static inline bool
|
|
565
|
+
ca_mv_is_core_entity (CArray *ca)
|
|
566
|
+
{
|
|
567
|
+
return ca->obj_type == CA_OBJ_ARRAY ||
|
|
568
|
+
ca->obj_type == CA_OBJ_ARRAY_WRAP ||
|
|
569
|
+
ca->obj_type == CA_OBJ_SCALAR;
|
|
570
|
+
}
|
|
571
|
+
|
|
554
572
|
/* Resolve ca to an (entity, composed_strides, composed_base) triple
|
|
555
573
|
suitable for zero-copy strided export. Uses ca_stride_compose_to_root
|
|
556
574
|
to fold the CAStride parent chain (alias-eligible OR not, as long
|
|
@@ -569,15 +587,31 @@ ca_mv_compose_to_entity (CArray *ca, ca_mv_reject_t *rej,
|
|
|
569
587
|
ca_size_t *out_strides,
|
|
570
588
|
ca_size_t *out_base)
|
|
571
589
|
{
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
590
|
+
rej->face = 0;
|
|
591
|
+
/* A Face is a 1:1 alias of its storage, so it is exported as that
|
|
592
|
+
storage -- but only a Face that registered its bytes as carrying its
|
|
593
|
+
values (ca_face_register_memory_view). The format comes from the Face
|
|
594
|
+
(data_class for a CARecord); the layout from the storage below. */
|
|
577
595
|
if (ca_is_face(ca)) {
|
|
596
|
+
if (! ca_face_memory_view_exportable(ca)) {
|
|
597
|
+
rej->depth = 0;
|
|
598
|
+
rej->class_name = ca_mv_class_name_of(ca);
|
|
599
|
+
rej->reason = "a Face whose storage bytes do not carry its values "
|
|
600
|
+
"without the Face; export its storage instead "
|
|
601
|
+
"(e.g. .ticks, .codes)";
|
|
602
|
+
rej->face = 1;
|
|
603
|
+
return false;
|
|
604
|
+
}
|
|
578
605
|
ca = ca_strip_face(ca);
|
|
579
606
|
}
|
|
580
607
|
if (ca_is_entity(ca)) {
|
|
608
|
+
if (! ca_mv_is_core_entity(ca)) {
|
|
609
|
+
rej->depth = 0;
|
|
610
|
+
rej->class_name = ca_mv_class_name_of(ca);
|
|
611
|
+
rej->reason = "installed outside the core; its bytes are not "
|
|
612
|
+
"exported";
|
|
613
|
+
return false;
|
|
614
|
+
}
|
|
581
615
|
*out_entity = ca;
|
|
582
616
|
*out_base = 0;
|
|
583
617
|
return true;
|
|
@@ -601,6 +635,12 @@ ca_mv_compose_to_entity (CArray *ca, ca_mv_reject_t *rej,
|
|
|
601
635
|
}
|
|
602
636
|
return false;
|
|
603
637
|
}
|
|
638
|
+
if (! ca_mv_is_core_entity(root)) {
|
|
639
|
+
rej->depth = -1;
|
|
640
|
+
rej->class_name = ca_mv_class_name_of(root);
|
|
641
|
+
rej->reason = "installed outside the core; its bytes are not exported";
|
|
642
|
+
return false;
|
|
643
|
+
}
|
|
604
644
|
*out_entity = root;
|
|
605
645
|
return true;
|
|
606
646
|
}
|
|
@@ -641,30 +681,57 @@ ca_mv_available_p (VALUE obj)
|
|
|
641
681
|
if (! ca_mv_extract(obj, &ca)) return false;
|
|
642
682
|
if (! ca_mv_ca_exportable(ca, obj)) return false;
|
|
643
683
|
if (ca_has_mask(ca)) return false;
|
|
644
|
-
/*
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
ca_mv_get would then reject it — say so here rather than advertising
|
|
648
|
-
an export that cannot be produced. Built-in entities (CArray /
|
|
649
|
-
CAWrap / CScalar) map to DIRECT and are unaffected. */
|
|
650
|
-
if (ca_is_entity(ca) && ca_mv_strategy_for(ca->obj_type) == CA_MV_REJECT) {
|
|
651
|
-
return false;
|
|
652
|
-
}
|
|
653
|
-
/* Wrap-via-MV exports are zero-copy: require an alias chain to
|
|
654
|
-
entity. Non-alias views (CASelect, CAFake, etc.) and CAStride
|
|
684
|
+
/* The same resolver decides the export in ca_mv_get, so what this
|
|
685
|
+
says is what the export does. Wrap-via-MV exports are zero-copy:
|
|
686
|
+
require an alias chain to a core entity. Non-alias views (CASelect, CAFake, etc.) and CAStride
|
|
655
687
|
chains broken by a non-contig link cannot be wrapped without a
|
|
656
688
|
snapshot, which has wrong semantics for MV consumers. */
|
|
657
689
|
return ca_mv_check_alias_chain(ca, &rej);
|
|
658
690
|
}
|
|
659
691
|
|
|
692
|
+
/* "Ns" format for a plain CA_FIXLEN of N bytes. The C strings are kept
|
|
693
|
+
in a table keyed by N for the life of the process, so the pointer stays
|
|
694
|
+
valid for any view, and a frozen array (which cannot take an ivar) is
|
|
695
|
+
exported like any other. The table is shared by every Ractor, hence
|
|
696
|
+
the lock. */
|
|
697
|
+
static st_table *ca_mv_fixlen_formats = NULL;
|
|
698
|
+
static rb_nativethread_lock_t ca_mv_fixlen_formats_lock;
|
|
699
|
+
|
|
700
|
+
static const char *
|
|
701
|
+
ca_mv_fixlen_format (ca_size_t bytes)
|
|
702
|
+
{
|
|
703
|
+
st_data_t found;
|
|
704
|
+
char *fmt;
|
|
705
|
+
rb_nativethread_lock_lock(&ca_mv_fixlen_formats_lock);
|
|
706
|
+
if ( st_lookup(ca_mv_fixlen_formats, (st_data_t) bytes, &found) ) {
|
|
707
|
+
rb_nativethread_lock_unlock(&ca_mv_fixlen_formats_lock);
|
|
708
|
+
return (const char *) found;
|
|
709
|
+
}
|
|
710
|
+
rb_nativethread_lock_unlock(&ca_mv_fixlen_formats_lock);
|
|
711
|
+
fmt = ALLOC_N(char, 24);
|
|
712
|
+
snprintf(fmt, 24, "%llds", (long long) bytes);
|
|
713
|
+
rb_nativethread_lock_lock(&ca_mv_fixlen_formats_lock);
|
|
714
|
+
if ( st_lookup(ca_mv_fixlen_formats, (st_data_t) bytes, &found) ) {
|
|
715
|
+
xfree(fmt);
|
|
716
|
+
fmt = (char *) found;
|
|
717
|
+
}
|
|
718
|
+
else {
|
|
719
|
+
st_insert(ca_mv_fixlen_formats, (st_data_t) bytes, (st_data_t) fmt);
|
|
720
|
+
}
|
|
721
|
+
rb_nativethread_lock_unlock(&ca_mv_fixlen_formats_lock);
|
|
722
|
+
return fmt;
|
|
723
|
+
}
|
|
724
|
+
|
|
660
725
|
static bool
|
|
661
726
|
ca_mv_get (VALUE obj, rb_memory_view_t *view, int flags)
|
|
662
727
|
{
|
|
663
728
|
CArray *ca;
|
|
664
|
-
|
|
729
|
+
CArray *storage;
|
|
665
730
|
ssize_t *shape = NULL;
|
|
666
731
|
ssize_t *strides = NULL;
|
|
667
732
|
void *data_ptr = NULL;
|
|
733
|
+
const char *format;
|
|
734
|
+
char *record_format = NULL;
|
|
668
735
|
int writable_request;
|
|
669
736
|
|
|
670
737
|
if (! ca_mv_extract(obj, &ca)) return false;
|
|
@@ -678,11 +745,10 @@ ca_mv_get (VALUE obj, rb_memory_view_t *view, int flags)
|
|
|
678
745
|
Object#freeze on a subclass). */
|
|
679
746
|
if (writable_request && OBJ_FROZEN(obj)) return false;
|
|
680
747
|
|
|
681
|
-
/* Resolve the chain to (entity, composed_strides, composed_base)
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
ca->ptr already, no compose needed). */
|
|
748
|
+
/* Resolve the chain to (entity, composed_strides, composed_base) with
|
|
749
|
+
the resolver available_p uses, so the two cannot disagree. The
|
|
750
|
+
layout is the storage's: a Face that got through is a 1:1 alias of
|
|
751
|
+
it. */
|
|
686
752
|
CArray *resolved_entity = NULL;
|
|
687
753
|
ca_size_t composed_strides[CA_RANK_MAX];
|
|
688
754
|
ca_size_t composed_base = 0;
|
|
@@ -693,73 +759,72 @@ ca_mv_get (VALUE obj, rb_memory_view_t *view, int flags)
|
|
|
693
759
|
return false;
|
|
694
760
|
}
|
|
695
761
|
}
|
|
762
|
+
storage = ca_is_face(ca) ? ca_strip_face(ca) : ca;
|
|
696
763
|
|
|
697
764
|
/* SIMPLE consumer on non-contig view would need a snapshot -- not
|
|
698
765
|
offered (consumers wanting a snapshot use from_memory_view). */
|
|
699
766
|
{
|
|
700
767
|
int wants_strides_early = (flags & RUBY_MEMORY_VIEW_STRIDES) ? 1 : 0;
|
|
701
768
|
if (!wants_strides_early &&
|
|
702
|
-
ca_mv_is_castride_family(
|
|
703
|
-
!ca_mv_stride_is_contig((CAStride *)
|
|
769
|
+
ca_mv_is_castride_family(storage) &&
|
|
770
|
+
!ca_mv_stride_is_contig((CAStride *) storage)) {
|
|
704
771
|
return false;
|
|
705
772
|
}
|
|
706
773
|
}
|
|
707
774
|
|
|
708
|
-
|
|
775
|
+
/* Format string, resolved before shape / strides are allocated since
|
|
776
|
+
building it can raise. Primitive types use the top-level table;
|
|
777
|
+
CA_FIXLEN + data_class emits the record's format, cached on the class
|
|
778
|
+
as a String. The String can move, so the view carries its own copy in
|
|
779
|
+
private_data, freed by ca_mv_release. */
|
|
780
|
+
if (ca->data_type == CA_FIXLEN && RTEST(rb_ca_has_data_class(obj))) {
|
|
781
|
+
VALUE klass = rb_ca_data_class(obj);
|
|
782
|
+
VALUE fmt = ca_mv_struct_format_for_data_class(klass);
|
|
783
|
+
long len = RSTRING_LEN(fmt);
|
|
784
|
+
record_format = ALLOC_N(char, len + 1);
|
|
785
|
+
memcpy(record_format, RSTRING_PTR(fmt), len);
|
|
786
|
+
record_format[len] = '\0';
|
|
787
|
+
RB_GC_GUARD(fmt);
|
|
788
|
+
format = record_format;
|
|
789
|
+
}
|
|
790
|
+
else if (ca->data_type == CA_FIXLEN) {
|
|
791
|
+
/* Plain CA_FIXLEN (no data_class): PEP 3118 "Ns" fixed-bytes. */
|
|
792
|
+
format = ca_mv_fixlen_format(ca->bytes);
|
|
793
|
+
}
|
|
794
|
+
else {
|
|
795
|
+
format = ca_mv_format_for(ca->data_type);
|
|
796
|
+
}
|
|
709
797
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
data_ptr =
|
|
715
|
-
if (ca->ndim >= 1) {
|
|
798
|
+
if (ca_is_entity(storage)) {
|
|
799
|
+
/* Entity / wrap / scalar: its own buffer, always valid. No attach
|
|
800
|
+
needed. A CScalar is exported with ndim 0 and no shape, so none is
|
|
801
|
+
taken: release frees only what the view carries. */
|
|
802
|
+
data_ptr = storage->ptr;
|
|
803
|
+
if (ca->ndim >= 1 && storage->obj_type != CA_OBJ_SCALAR) {
|
|
716
804
|
shape = ca_mv_alloc_shape(ca);
|
|
717
805
|
strides = ca_mv_alloc_strides_contiguous(ca, shape, true);
|
|
718
806
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
BUG). */
|
|
736
|
-
case CA_MV_REFER:
|
|
737
|
-
case CA_MV_BLOCK:
|
|
738
|
-
case CA_MV_FARRAY:
|
|
739
|
-
case CA_MV_TRANS:
|
|
740
|
-
case CA_MV_REPEAT: {
|
|
807
|
+
}
|
|
808
|
+
else {
|
|
809
|
+
/* A CAStride-family view (CARefer / CABlock / CAFarray / CATranspose
|
|
810
|
+
/ CARepeat / CAField / plain CAStride) composed to an entity.
|
|
811
|
+
Expose `entity->ptr + composed_base` as data_ptr and
|
|
812
|
+
composed_strides as strides, both in entity byte space. The entity
|
|
813
|
+
owns its bytes (always valid via view->obj's parent chain while the
|
|
814
|
+
consumer holds the view).
|
|
815
|
+
|
|
816
|
+
CAREFUL: do not ca_attach the view here (nor ca_sync / ca_detach at
|
|
817
|
+
release). Exposure is alias-only: the consumer writes directly into
|
|
818
|
+
root bytes, so a sync would scatter bytes back to root as a no-op
|
|
819
|
+
chain -- and that chain triggers ca_update_mask / ca_sync(mask)
|
|
820
|
+
recursion that can raise on an invariant violation. Raising from a
|
|
821
|
+
release callback is fatal during GC sweep (newobj_of during sweep ->
|
|
822
|
+
BUG). */
|
|
741
823
|
int8_t k;
|
|
742
824
|
shape = ca_mv_alloc_shape(ca);
|
|
743
825
|
strides = (ssize_t *) xmalloc(sizeof(ssize_t) * ca->ndim);
|
|
744
826
|
for (k = 0; k < ca->ndim; k++) strides[k] = (ssize_t) composed_strides[k];
|
|
745
827
|
data_ptr = resolved_entity->ptr + composed_base;
|
|
746
|
-
break;
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
case CA_MV_ATTACH:
|
|
750
|
-
/* Should be unreachable: ca_mv_check_alias_chain rejects every
|
|
751
|
-
obj_type that maps to ATTACH (CASelect / CAMapping / CAGrid /
|
|
752
|
-
CAShift / CAWindow / CAFake / CAReduce). Materialise is no
|
|
753
|
-
longer offered via wrap_memory_view; from_memory_view gives a
|
|
754
|
-
snapshot copy instead. */
|
|
755
|
-
rb_raise(rb_eRuntimeError,
|
|
756
|
-
"[BUG] ATTACH strategy reached in ca_mv_get for %s; "
|
|
757
|
-
"alias-chain check should have rejected it",
|
|
758
|
-
ca_mv_class_name_of(ca));
|
|
759
|
-
|
|
760
|
-
case CA_MV_REJECT:
|
|
761
|
-
default:
|
|
762
|
-
return false;
|
|
763
828
|
}
|
|
764
829
|
|
|
765
830
|
view->obj = obj;
|
|
@@ -770,41 +835,15 @@ ca_mv_get (VALUE obj, rb_memory_view_t *view, int flags)
|
|
|
770
835
|
views compute Σ (shape[k]-1)*|strides[k]| + item_size themselves. */
|
|
771
836
|
view->byte_size = (ssize_t) ca_length(ca);
|
|
772
837
|
view->readonly = ca_is_readonly(ca) ? true : false;
|
|
773
|
-
|
|
774
|
-
CA_FIXLEN + data_class emits a PEP 3118 "T{...}" struct format
|
|
775
|
-
whose lifetime is bound to the class via @__mv_struct_format__,
|
|
776
|
-
so the view->format pointer is stable until the class is GC'd. */
|
|
777
|
-
if (ca->data_type == CA_FIXLEN && RTEST(rb_ca_has_data_class(obj))) {
|
|
778
|
-
VALUE klass = rb_ca_data_class(obj);
|
|
779
|
-
VALUE fmt = ca_mv_struct_format_for_data_class(klass);
|
|
780
|
-
view->format = RSTRING_PTR(fmt);
|
|
781
|
-
}
|
|
782
|
-
else if (ca->data_type == CA_FIXLEN) {
|
|
783
|
-
/* Plain CA_FIXLEN (no data_class): emit PEP 3118 "Ns" fixed-bytes.
|
|
784
|
-
Cache the format String on the source object so RSTRING_PTR stays
|
|
785
|
-
stable for the view's lifetime; the source object is anchored as
|
|
786
|
-
view->obj. */
|
|
787
|
-
VALUE fmt_str = rb_attr_get(obj, rb_intern("__mv_fixlen_format__"));
|
|
788
|
-
if (NIL_P(fmt_str)) {
|
|
789
|
-
char fmt_buf[32];
|
|
790
|
-
snprintf(fmt_buf, sizeof(fmt_buf), "%lds", (long) ca->bytes);
|
|
791
|
-
fmt_str = rb_str_new_cstr(fmt_buf);
|
|
792
|
-
rb_obj_freeze(fmt_str);
|
|
793
|
-
rb_ivar_set(obj, rb_intern("__mv_fixlen_format__"), fmt_str);
|
|
794
|
-
}
|
|
795
|
-
view->format = RSTRING_PTR(fmt_str);
|
|
796
|
-
}
|
|
797
|
-
else {
|
|
798
|
-
view->format = ca_mv_format_for(ca->data_type);
|
|
799
|
-
}
|
|
838
|
+
view->format = format;
|
|
800
839
|
view->item_size = (ssize_t) ca->bytes;
|
|
801
840
|
view->item_desc.components = NULL;
|
|
802
841
|
view->item_desc.length = 0;
|
|
803
|
-
view->ndim = (
|
|
842
|
+
view->ndim = (storage->obj_type == CA_OBJ_SCALAR) ? 0 : (ssize_t) ca->ndim;
|
|
804
843
|
view->shape = (view->ndim == 0) ? NULL : shape;
|
|
805
844
|
view->strides = (view->ndim == 0) ? NULL : strides;
|
|
806
845
|
view->sub_offsets = NULL;
|
|
807
|
-
view->private_data =
|
|
846
|
+
view->private_data = record_format;
|
|
808
847
|
|
|
809
848
|
/* Enforce caller's layout request. */
|
|
810
849
|
{
|
|
@@ -827,6 +866,11 @@ reject:
|
|
|
827
866
|
return false; there is no attach state to unwind. */
|
|
828
867
|
if (shape) { xfree(shape); view->shape = NULL; }
|
|
829
868
|
if (strides) { xfree(strides); view->strides = NULL; }
|
|
869
|
+
if (record_format) {
|
|
870
|
+
xfree(record_format);
|
|
871
|
+
view->format = NULL;
|
|
872
|
+
view->private_data = NULL;
|
|
873
|
+
}
|
|
830
874
|
return false;
|
|
831
875
|
}
|
|
832
876
|
|
|
@@ -848,7 +892,10 @@ ca_mv_release (VALUE obj, rb_memory_view_t *view)
|
|
|
848
892
|
ca_sync(ca->mask) recursion and raise; raising from a release
|
|
849
893
|
callback is fatal during GC sweep. See ca_mv_get's CAStride-family
|
|
850
894
|
case comment for the full rationale. */
|
|
851
|
-
view->private_data
|
|
895
|
+
if (view->private_data) {
|
|
896
|
+
xfree(view->private_data);
|
|
897
|
+
view->private_data = NULL;
|
|
898
|
+
}
|
|
852
899
|
return true;
|
|
853
900
|
}
|
|
854
901
|
|
|
@@ -867,9 +914,25 @@ ca_mv_release (VALUE obj, rb_memory_view_t *view)
|
|
|
867
914
|
a TypedData holder (also stored as an ivar) releases the view in its
|
|
868
915
|
dfree callback when the CAWrap is collected. */
|
|
869
916
|
|
|
917
|
+
/* One borrowed view and the geometry read from it. The geometry is
|
|
918
|
+
copied out and normalised once (a 0-d view as one element, omitted
|
|
919
|
+
shape or strides filled in as row-major), so nothing below reads the
|
|
920
|
+
producer's shape or strides arrays again.
|
|
921
|
+
|
|
922
|
+
The holder is a Ruby object from before the view is taken, so a raise
|
|
923
|
+
at any later point loses nothing: the importers release it at once
|
|
924
|
+
(rb_protect / rb_ensure), and the holder's dfree releases it in any
|
|
925
|
+
case. `lo` / `hi` bound the bytes the elements reach, relative to
|
|
926
|
+
view.data. */
|
|
870
927
|
typedef struct {
|
|
871
928
|
rb_memory_view_t view;
|
|
872
929
|
bool valid;
|
|
930
|
+
int8_t ndim;
|
|
931
|
+
ca_size_t dim[CA_RANK_MAX];
|
|
932
|
+
ssize_t strides[CA_RANK_MAX];
|
|
933
|
+
ca_size_t elements;
|
|
934
|
+
bool contiguous;
|
|
935
|
+
ssize_t lo, hi;
|
|
873
936
|
} ca_mv_imported_holder_t;
|
|
874
937
|
|
|
875
938
|
static void
|
|
@@ -877,8 +940,21 @@ ca_mv_imported_holder_free (void *p)
|
|
|
877
940
|
{
|
|
878
941
|
ca_mv_imported_holder_t *h = (ca_mv_imported_holder_t *) p;
|
|
879
942
|
if (h->valid) {
|
|
880
|
-
rb_memory_view_release(&h->view);
|
|
881
943
|
h->valid = false;
|
|
944
|
+
/* While a view is exported Ruby keeps the producer alive, so in an
|
|
945
|
+
ordinary sweep it is still here. At exit, though, the objects
|
|
946
|
+
left are freed in heap order and the producer may have gone first
|
|
947
|
+
(its slot is T_NONE with the class still set). Its release
|
|
948
|
+
callback would then get a dead object, and one that checks the
|
|
949
|
+
type raises, which is fatal during GC. Skip the release there:
|
|
950
|
+
the process is ending, so only the components are freed. */
|
|
951
|
+
if (! RB_SPECIAL_CONST_P(h->view.obj) &&
|
|
952
|
+
RB_BUILTIN_TYPE(h->view.obj) == RUBY_T_NONE) {
|
|
953
|
+
xfree((void *) h->view.item_desc.components);
|
|
954
|
+
}
|
|
955
|
+
else {
|
|
956
|
+
rb_memory_view_release(&h->view);
|
|
957
|
+
}
|
|
882
958
|
}
|
|
883
959
|
xfree(h);
|
|
884
960
|
}
|
|
@@ -900,149 +976,311 @@ static const rb_data_type_t ca_mv_imported_holder_data_type = {
|
|
|
900
976
|
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
|
901
977
|
};
|
|
902
978
|
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
producers that advertise only contiguous still work. On any
|
|
906
|
-
failure, releases the view (if acquired) and raises ArgumentError;
|
|
907
|
-
on success, the view is left active and the caller owns the
|
|
908
|
-
responsibility to release it.
|
|
909
|
-
|
|
910
|
-
`explicit_data_type` is the caller-supplied target data_type (>= 0) or -1
|
|
911
|
-
if the caller has no preference. Behaviour:
|
|
912
|
-
- explicit_data_type < 0, format != NULL: derive data_type from format.
|
|
913
|
-
- explicit_data_type < 0, format == NULL: reject (typeless source
|
|
914
|
-
without consumer-supplied type cannot be interpreted).
|
|
915
|
-
- explicit_data_type >= 0, format != NULL: must match the format's
|
|
916
|
-
data_type; mismatch -> reject.
|
|
917
|
-
- explicit_data_type >= 0, format == NULL: use explicit_data_type; the
|
|
918
|
-
byte buffer is reinterpreted as that data_type. ndim is forced
|
|
919
|
-
to 1 (multi-dim from a typeless source is ambiguous; user
|
|
920
|
-
chains .reshape).
|
|
921
|
-
*/
|
|
922
|
-
/* Row-major contiguity test that tolerates a NULL shape array. ndim-0
|
|
923
|
-
scalars and SIMPLE-form producers (which omit the shape array for a
|
|
924
|
-
contiguous 1-D buffer, e.g. red-arrow's primitive arrays) are
|
|
925
|
-
contiguous by construction; rb_memory_view_is_row_major_contiguous
|
|
926
|
-
dereferences view->shape, so it must not be reached with shape NULL. */
|
|
927
|
-
static bool
|
|
928
|
-
ca_mv_view_is_contiguous (const rb_memory_view_t *view)
|
|
979
|
+
static VALUE
|
|
980
|
+
ca_mv_holder_new (ca_mv_imported_holder_t **out)
|
|
929
981
|
{
|
|
930
|
-
|
|
931
|
-
|
|
982
|
+
/* Zero-filled: valid is false and the view's item_desc.components is
|
|
983
|
+
NULL, which release frees unconditionally (red-arrow 24.0.0 leaves
|
|
984
|
+
that field unwritten, apache/arrow#45187). */
|
|
985
|
+
return TypedData_Make_Struct(rb_cObject, ca_mv_imported_holder_t,
|
|
986
|
+
&ca_mv_imported_holder_data_type, *out);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
static void
|
|
990
|
+
ca_mv_holder_release (ca_mv_imported_holder_t *h)
|
|
991
|
+
{
|
|
992
|
+
if (h != NULL && h->valid) {
|
|
993
|
+
h->valid = false;
|
|
994
|
+
rb_memory_view_release(&h->view);
|
|
932
995
|
}
|
|
933
|
-
return rb_memory_view_is_row_major_contiguous(view);
|
|
934
996
|
}
|
|
935
997
|
|
|
998
|
+
/* Take a view from src: STRIDES first, SIMPLE for producers that offer
|
|
999
|
+
only a contiguous buffer. `writable` asks the producer for a writable
|
|
1000
|
+
view; a producer that refuses that but gives a read-only one is a
|
|
1001
|
+
read-only source. */
|
|
936
1002
|
static void
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
int8_t *out_data_type, int8_t *out_ndim,
|
|
940
|
-
ca_size_t *out_dim)
|
|
1003
|
+
ca_mv_holder_acquire (ca_mv_imported_holder_t *h, VALUE src, int writable,
|
|
1004
|
+
const char *who)
|
|
941
1005
|
{
|
|
942
|
-
|
|
943
|
-
|
|
1006
|
+
int extra = writable ? RUBY_MEMORY_VIEW_WRITABLE : 0;
|
|
1007
|
+
memset(&h->view, 0, sizeof(h->view));
|
|
1008
|
+
if ( ! rb_memory_view_get(src, &h->view, RUBY_MEMORY_VIEW_STRIDES | extra) ) {
|
|
1009
|
+
memset(&h->view, 0, sizeof(h->view));
|
|
1010
|
+
if ( ! rb_memory_view_get(src, &h->view, RUBY_MEMORY_VIEW_SIMPLE | extra) ) {
|
|
1011
|
+
if ( writable ) {
|
|
1012
|
+
memset(&h->view, 0, sizeof(h->view));
|
|
1013
|
+
if ( rb_memory_view_get(src, &h->view, RUBY_MEMORY_VIEW_STRIDES) ||
|
|
1014
|
+
rb_memory_view_get(src, &h->view, RUBY_MEMORY_VIEW_SIMPLE) ) {
|
|
1015
|
+
h->valid = true;
|
|
1016
|
+
rb_raise(rb_eRuntimeError,
|
|
1017
|
+
"MemoryView source is read-only; can't wrap as writable");
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
rb_raise(rb_eArgError, "%sobject does not support MemoryView", who);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
h->valid = true;
|
|
1024
|
+
if ( writable && h->view.readonly ) {
|
|
1025
|
+
rb_raise(rb_eRuntimeError,
|
|
1026
|
+
"MemoryView source is read-only; can't wrap as writable");
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
944
1029
|
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
1030
|
+
/* Read the view's geometry for elements of `item_size` bytes, and check
|
|
1031
|
+
it against the buffer. The buffer is [data, data + byte_size). For a
|
|
1032
|
+
strided view byte_size is the element count times item_size (the
|
|
1033
|
+
convention CArray's own producer, NumPy and Arrow follow), which does
|
|
1034
|
+
not bound where the strides reach, so only that count is checked; for
|
|
1035
|
+
a contiguous view the shape is checked against the bytes. */
|
|
1036
|
+
static void
|
|
1037
|
+
ca_mv_holder_read_geometry (ca_mv_imported_holder_t *h, ssize_t item_size,
|
|
1038
|
+
const char *who)
|
|
1039
|
+
{
|
|
1040
|
+
const rb_memory_view_t *v = &h->view;
|
|
1041
|
+
ssize_t expect, need;
|
|
1042
|
+
int8_t k;
|
|
952
1043
|
|
|
953
|
-
|
|
954
|
-
|
|
1044
|
+
/* rank-check: a producer's rank, named by which MemoryView it is */
|
|
1045
|
+
if ( v->ndim < 0 || v->ndim > CA_RANK_MAX ) {
|
|
1046
|
+
rb_raise(rb_eArgError, "%sMemoryView ndim (%ld) exceeds CArray CA_RANK_MAX (%d)",
|
|
1047
|
+
who, (long) v->ndim, CA_RANK_MAX);
|
|
1048
|
+
}
|
|
1049
|
+
if ( v->ndim == 0 ) {
|
|
1050
|
+
h->ndim = 1;
|
|
1051
|
+
h->dim[0] = 1;
|
|
1052
|
+
}
|
|
1053
|
+
else if ( v->shape == NULL ) {
|
|
1054
|
+
/* Only 1-D is readable without a shape: length = byte_size / item_size. */
|
|
1055
|
+
if ( v->ndim > 1 ) {
|
|
955
1056
|
rb_raise(rb_eArgError,
|
|
956
|
-
"
|
|
1057
|
+
"%sMemoryView reports ndim %ld but provides no shape array",
|
|
1058
|
+
who, (long) v->ndim);
|
|
1059
|
+
}
|
|
1060
|
+
h->ndim = 1;
|
|
1061
|
+
h->dim[0] = (ca_size_t) (item_size > 0 ? v->byte_size / item_size : 0);
|
|
1062
|
+
}
|
|
1063
|
+
else {
|
|
1064
|
+
h->ndim = (int8_t) v->ndim;
|
|
1065
|
+
for (k = 0; k < h->ndim; k++) {
|
|
1066
|
+
if ( v->shape[k] < 0 ) {
|
|
1067
|
+
rb_raise(rb_eArgError, "%sMemoryView shape[%d] is negative (%ld)",
|
|
1068
|
+
who, (int) k, (long) v->shape[k]);
|
|
1069
|
+
}
|
|
1070
|
+
h->dim[k] = (ca_size_t) v->shape[k];
|
|
957
1071
|
}
|
|
958
1072
|
}
|
|
1073
|
+
h->elements = ca_shape_elements(h->ndim, h->dim, (ca_size_t) item_size);
|
|
959
1074
|
|
|
960
|
-
if (
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1075
|
+
if ( v->ndim >= 1 && v->shape != NULL && v->strides != NULL ) {
|
|
1076
|
+
for (k = 0; k < h->ndim; k++) {
|
|
1077
|
+
h->strides[k] = v->strides[k];
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
else {
|
|
1081
|
+
expect = item_size;
|
|
1082
|
+
for (k = h->ndim - 1; k >= 0; k--) {
|
|
1083
|
+
h->strides[k] = expect;
|
|
1084
|
+
expect *= (ssize_t) h->dim[k];
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
h->contiguous = true;
|
|
1089
|
+
expect = item_size;
|
|
1090
|
+
for (k = h->ndim - 1; k >= 0; k--) {
|
|
1091
|
+
if ( h->dim[k] > 1 && h->strides[k] != expect ) {
|
|
1092
|
+
h->contiguous = false;
|
|
1093
|
+
}
|
|
1094
|
+
expect *= (ssize_t) h->dim[k];
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
h->lo = h->hi = 0;
|
|
1098
|
+
if ( h->elements > 0 ) {
|
|
1099
|
+
h->hi = item_size;
|
|
1100
|
+
for (k = 0; k < h->ndim; k++) {
|
|
1101
|
+
ssize_t reach;
|
|
1102
|
+
if ( __builtin_mul_overflow((ssize_t) h->dim[k] - 1, h->strides[k], &reach) ||
|
|
1103
|
+
( reach < 0 ? __builtin_add_overflow(h->lo, reach, &h->lo)
|
|
1104
|
+
: __builtin_add_overflow(h->hi, reach, &h->hi) ) ) {
|
|
1105
|
+
rb_raise(rb_eArgError, "%sMemoryView strides reach too far", who);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
need = (ssize_t) h->elements * item_size;
|
|
1111
|
+
if ( v->byte_size < need ) {
|
|
1112
|
+
rb_raise(rb_eArgError,
|
|
1113
|
+
"%sMemoryView reports %ld bytes, but its shape needs %ld",
|
|
1114
|
+
who, (long) v->byte_size, (long) need);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
/* Take the data side's view and resolve its data_type.
|
|
1119
|
+
`explicit_data_type` (the data_type: keyword) must agree with a typed
|
|
1120
|
+
producer's format and names the type of a typeless one.
|
|
1121
|
+
`typeless_data_type` names the type only when the producer is typeless
|
|
1122
|
+
(wrap_readonly / wrap_writable, which adapt a typed producer
|
|
1123
|
+
afterwards). A typeless producer's bytes are read as one 1-D run, so
|
|
1124
|
+
one whose own geometry is strided is refused. */
|
|
1125
|
+
static int8_t
|
|
1126
|
+
ca_mv_holder_take_data (ca_mv_imported_holder_t *h, VALUE src,
|
|
1127
|
+
int8_t explicit_data_type, int8_t typeless_data_type,
|
|
1128
|
+
int writable)
|
|
1129
|
+
{
|
|
1130
|
+
rb_memory_view_t *v = &h->view;
|
|
1131
|
+
int8_t data_type;
|
|
1132
|
+
|
|
1133
|
+
ca_mv_holder_acquire(h, src, writable, "");
|
|
1134
|
+
|
|
1135
|
+
if ( v->format == NULL ) {
|
|
1136
|
+
ssize_t target;
|
|
1137
|
+
data_type = ( explicit_data_type >= 0 ) ? explicit_data_type : typeless_data_type;
|
|
1138
|
+
if ( data_type < 0 ) {
|
|
966
1139
|
rb_raise(rb_eArgError,
|
|
967
1140
|
"typeless MemoryView (format=NULL, %ld bytes); "
|
|
968
1141
|
"specify the target data_type via data_type: kwarg or use a "
|
|
969
1142
|
"concrete CArray::<Dtype> factory class",
|
|
970
|
-
(long)
|
|
1143
|
+
(long) v->byte_size);
|
|
971
1144
|
}
|
|
972
|
-
if (ca_mv_format_for(
|
|
973
|
-
rb_memory_view_release(view);
|
|
1145
|
+
if ( ca_mv_format_for(data_type) == NULL ) {
|
|
974
1146
|
rb_raise(rb_eArgError,
|
|
975
1147
|
"target data_type is not representable via MemoryView");
|
|
976
1148
|
}
|
|
977
|
-
|
|
978
|
-
if (
|
|
979
|
-
|
|
980
|
-
|
|
1149
|
+
ca_mv_holder_read_geometry(h, v->item_size > 0 ? v->item_size : 1, "");
|
|
1150
|
+
if ( ! h->contiguous ) {
|
|
1151
|
+
rb_raise(rb_eArgError,
|
|
1152
|
+
"typeless MemoryView is strided; its bytes cannot be read as "
|
|
1153
|
+
"one run of %s", ca_type_name[data_type]);
|
|
1154
|
+
}
|
|
1155
|
+
target = (ssize_t) ca_sizeof[data_type];
|
|
1156
|
+
if ( v->byte_size % target != 0 ) {
|
|
981
1157
|
rb_raise(rb_eArgError,
|
|
982
1158
|
"byte_size %ld is not a multiple of target item_size %ld",
|
|
983
|
-
(long)
|
|
1159
|
+
(long) v->byte_size, (long) target);
|
|
984
1160
|
}
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
/* If the caller specified a data_type too, it must match. */
|
|
1004
|
-
if (explicit_data_type >= 0 && explicit_data_type != data_type) {
|
|
1005
|
-
rb_memory_view_release(view);
|
|
1161
|
+
h->ndim = 1;
|
|
1162
|
+
h->dim[0] = (ca_size_t) (v->byte_size / target);
|
|
1163
|
+
h->strides[0] = target;
|
|
1164
|
+
h->elements = h->dim[0];
|
|
1165
|
+
h->contiguous = true;
|
|
1166
|
+
h->lo = 0;
|
|
1167
|
+
h->hi = v->byte_size;
|
|
1168
|
+
return data_type;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
data_type = ca_mv_data_type_from_format(v->format, v->item_size);
|
|
1172
|
+
if ( data_type < 0 ) {
|
|
1173
|
+
rb_raise(rb_eArgError, "unsupported MemoryView format: %s (item_size=%ld)",
|
|
1174
|
+
v->format, (long) v->item_size);
|
|
1175
|
+
}
|
|
1176
|
+
if ( explicit_data_type >= 0 && explicit_data_type != data_type ) {
|
|
1006
1177
|
rb_raise(rb_eArgError,
|
|
1007
1178
|
"explicit data_type does not match producer format "
|
|
1008
|
-
"(producer: %s, requested data_type %
|
|
1009
|
-
|
|
1179
|
+
"(producer: %s, requested data_type %s)",
|
|
1180
|
+
v->format, ca_type_name[explicit_data_type]);
|
|
1010
1181
|
}
|
|
1182
|
+
ca_mv_holder_read_geometry(h, v->item_size, "");
|
|
1183
|
+
return data_type;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
/* Take the mask's view: one byte per element (bool / uint8 / int8),
|
|
1187
|
+
contiguous, of the data side's shape. */
|
|
1188
|
+
static void
|
|
1189
|
+
ca_mv_holder_take_mask (ca_mv_imported_holder_t *m, VALUE src,
|
|
1190
|
+
const ca_mv_imported_holder_t *d)
|
|
1191
|
+
{
|
|
1192
|
+
rb_memory_view_t *v = &m->view;
|
|
1193
|
+
int8_t k;
|
|
1011
1194
|
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1195
|
+
ca_mv_holder_acquire(m, src, 0, "mask: ");
|
|
1196
|
+
if ( v->format != NULL ) {
|
|
1197
|
+
int8_t dt = ca_mv_data_type_from_format(v->format, v->item_size);
|
|
1198
|
+
if ( dt != CA_BOOLEAN && dt != CA_UINT8 && dt != CA_INT8 ) {
|
|
1199
|
+
rb_raise(rb_eArgError, "mask: format must be bool/uint8/int8 (got %s)",
|
|
1200
|
+
v->format);
|
|
1201
|
+
}
|
|
1015
1202
|
}
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
if (view->ndim > 1) {
|
|
1030
|
-
long got = (long) view->ndim;
|
|
1031
|
-
rb_memory_view_release(view);
|
|
1032
|
-
rb_raise(rb_eArgError,
|
|
1033
|
-
"MemoryView reports ndim %ld but provides no shape array",
|
|
1034
|
-
got);
|
|
1203
|
+
if ( v->item_size != 1 ) {
|
|
1204
|
+
rb_raise(rb_eArgError, "mask: item_size must be 1 (got %ld)",
|
|
1205
|
+
(long) v->item_size);
|
|
1206
|
+
}
|
|
1207
|
+
ca_mv_holder_read_geometry(m, 1, "mask: ");
|
|
1208
|
+
if ( m->ndim != d->ndim ) {
|
|
1209
|
+
rb_raise(rb_eArgError, "mask: ndim (%d) does not match data ndim (%d)",
|
|
1210
|
+
(int) m->ndim, (int) d->ndim);
|
|
1211
|
+
}
|
|
1212
|
+
for (k = 0; k < d->ndim; k++) {
|
|
1213
|
+
if ( m->dim[k] != d->dim[k] ) {
|
|
1214
|
+
rb_raise(rb_eArgError, "mask: shape[%d] (%ld) does not match data shape (%ld)",
|
|
1215
|
+
(int) k, (long) m->dim[k], (long) d->dim[k]);
|
|
1035
1216
|
}
|
|
1036
|
-
*out_ndim = 1;
|
|
1037
|
-
out_dim[0] = (ca_size_t) (view->item_size ? view->byte_size / view->item_size : 0);
|
|
1038
1217
|
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1218
|
+
if ( ! m->contiguous ) {
|
|
1219
|
+
rb_raise(rb_eArgError,
|
|
1220
|
+
"mask: strided masks are not supported in this release; "
|
|
1221
|
+
"pass a row-major contiguous mask buffer");
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
/* Walk the elements of a view in row-major order, calling `fn` with each
|
|
1226
|
+
element's address. Returns false as soon as fn does. */
|
|
1227
|
+
static bool
|
|
1228
|
+
ca_mv_holder_each (const ca_mv_imported_holder_t *h,
|
|
1229
|
+
bool (*fn)(const char *elem, void *arg), void *arg)
|
|
1230
|
+
{
|
|
1231
|
+
ca_size_t idx[CA_RANK_MAX];
|
|
1232
|
+
ca_size_t n;
|
|
1233
|
+
int8_t k;
|
|
1234
|
+
const char *base = (const char *) h->view.data;
|
|
1235
|
+
|
|
1236
|
+
for (k = 0; k < h->ndim; k++) idx[k] = 0;
|
|
1237
|
+
for (n = 0; n < h->elements; n++) {
|
|
1238
|
+
ssize_t off = 0;
|
|
1239
|
+
for (k = 0; k < h->ndim; k++) {
|
|
1240
|
+
off += (ssize_t) idx[k] * h->strides[k];
|
|
1043
1241
|
}
|
|
1242
|
+
if ( ! fn(base + off, arg) ) {
|
|
1243
|
+
return false;
|
|
1244
|
+
}
|
|
1245
|
+
for (k = h->ndim - 1; k >= 0; k--) {
|
|
1246
|
+
if ( ++idx[k] < h->dim[k] ) break;
|
|
1247
|
+
idx[k] = 0;
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
return true;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
static bool
|
|
1254
|
+
ca_mv_byte_is_bit (const char *elem, void *arg)
|
|
1255
|
+
{
|
|
1256
|
+
(void) arg;
|
|
1257
|
+
return *(const uint8_t *) elem <= 1;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
typedef struct {
|
|
1261
|
+
char *dst;
|
|
1262
|
+
size_t item_size;
|
|
1263
|
+
} ca_mv_gather_t;
|
|
1264
|
+
|
|
1265
|
+
static bool
|
|
1266
|
+
ca_mv_gather_one (const char *elem, void *arg)
|
|
1267
|
+
{
|
|
1268
|
+
ca_mv_gather_t *g = (ca_mv_gather_t *) arg;
|
|
1269
|
+
memcpy(g->dst, elem, g->item_size);
|
|
1270
|
+
g->dst += g->item_size;
|
|
1271
|
+
return true;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
/* A boolean CArray holds 0 or 1 in each byte. A copy can normalise other
|
|
1275
|
+
bytes; a wrap shares them, so it refuses a buffer that holds any. */
|
|
1276
|
+
static void
|
|
1277
|
+
ca_mv_check_bits (const ca_mv_imported_holder_t *h, const char *who)
|
|
1278
|
+
{
|
|
1279
|
+
if ( ! ca_mv_holder_each(h, ca_mv_byte_is_bit, NULL) ) {
|
|
1280
|
+
rb_raise(rb_eArgError,
|
|
1281
|
+
"%sboolean MemoryView holds bytes other than 0 and 1; "
|
|
1282
|
+
"a wrap cannot normalise them (CArray.from_memory_view can)", who);
|
|
1044
1283
|
}
|
|
1045
|
-
*out_data_type = data_type;
|
|
1046
1284
|
}
|
|
1047
1285
|
|
|
1048
1286
|
/* Extract the optional data_type: and mask: keywords from argv.
|
|
@@ -1075,421 +1313,284 @@ ca_mv_extract_type_and_mask_kwargs (int argc, VALUE *argv,
|
|
|
1075
1313
|
return data_type;
|
|
1076
1314
|
}
|
|
1077
1315
|
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
ca_mv_acquire_mask_view (VALUE mask_src, int8_t data_ndim,
|
|
1091
|
-
const ca_size_t *data_dim,
|
|
1092
|
-
ca_mv_imported_holder_t *data_holder_to_free_on_fail,
|
|
1093
|
-
rb_memory_view_t *data_view_to_release_on_fail)
|
|
1316
|
+
typedef struct {
|
|
1317
|
+
VALUE obj, mask_obj, target_class;
|
|
1318
|
+
int8_t explicit_data_type, typeless_data_type;
|
|
1319
|
+
int writable;
|
|
1320
|
+
ca_mv_imported_holder_t *data, *mask;
|
|
1321
|
+
VALUE data_holder_obj, mask_holder_obj;
|
|
1322
|
+
} ca_mv_import_args_t;
|
|
1323
|
+
|
|
1324
|
+
/* The body of wrap_memory_view. Runs under rb_protect; the caller
|
|
1325
|
+
releases both views if it raises. */
|
|
1326
|
+
static VALUE
|
|
1327
|
+
ca_mv_wrap_body (VALUE arg)
|
|
1094
1328
|
{
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1329
|
+
ca_mv_import_args_t *a = (ca_mv_import_args_t *) arg;
|
|
1330
|
+
ca_mv_imported_holder_t *d = a->data, *m = a->mask;
|
|
1331
|
+
rb_memory_view_t *v = &d->view;
|
|
1332
|
+
ca_size_t bytes;
|
|
1333
|
+
int8_t data_type;
|
|
1334
|
+
volatile VALUE wrap;
|
|
1335
|
+
CArray *ca;
|
|
1098
1336
|
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
rb_raise(rb_eArgError, msg, ##__VA_ARGS__); \
|
|
1112
|
-
} while (0)
|
|
1113
|
-
|
|
1114
|
-
holder = ALLOC(ca_mv_imported_holder_t);
|
|
1115
|
-
holder->valid = false;
|
|
1116
|
-
|
|
1117
|
-
/* Zero-init before get: same apache/arrow#45187 defense as the data
|
|
1118
|
-
path (release frees view->item_desc.components unconditionally). */
|
|
1119
|
-
memset(&holder->view, 0, sizeof(holder->view));
|
|
1120
|
-
|
|
1121
|
-
if (! rb_memory_view_get(mask_src, &holder->view, RUBY_MEMORY_VIEW_STRIDES)) {
|
|
1122
|
-
if (! rb_memory_view_get(mask_src, &holder->view, RUBY_MEMORY_VIEW_SIMPLE)) {
|
|
1123
|
-
MV_MASK_CLEANUP_AND_RAISE("mask: object does not support MemoryView");
|
|
1124
|
-
}
|
|
1337
|
+
data_type = ca_mv_holder_take_data(d, a->obj, a->explicit_data_type,
|
|
1338
|
+
a->typeless_data_type, a->writable);
|
|
1339
|
+
bytes = ca_mv_carray_bytes_for(data_type, v->item_size);
|
|
1340
|
+
|
|
1341
|
+
/* A strided producer comes back as a CAStride, so a caller who asked
|
|
1342
|
+
for a class would get something that is not it. */
|
|
1343
|
+
if ( a->target_class != rb_cCAWrap && ! d->contiguous ) {
|
|
1344
|
+
rb_raise(rb_eArgError,
|
|
1345
|
+
"%s was requested, but this producer is strided, so the result "
|
|
1346
|
+
"would be a CAStride; pass a row-major contiguous producer, or "
|
|
1347
|
+
"use CArray.from_memory_view for a copy",
|
|
1348
|
+
rb_class2name(a->target_class));
|
|
1125
1349
|
}
|
|
1126
|
-
holder->valid = true;
|
|
1127
|
-
mv = &holder->view;
|
|
1128
1350
|
|
|
1129
|
-
if (
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1351
|
+
if ( m != NULL ) {
|
|
1352
|
+
ca_mv_holder_take_mask(m, a->mask_obj, d);
|
|
1353
|
+
if ( ! d->contiguous ) {
|
|
1354
|
+
rb_raise(rb_eArgError,
|
|
1355
|
+
"mask: combining mask: with a strided data source is not "
|
|
1356
|
+
"supported in this release; pass a row-major contiguous "
|
|
1357
|
+
"data source");
|
|
1134
1358
|
}
|
|
1135
|
-
/*
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
char
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1359
|
+
/* A mask that shares bytes with the values would take every write
|
|
1360
|
+
to a value as a write to the mask. */
|
|
1361
|
+
if ( d->elements > 0 ) {
|
|
1362
|
+
const char *dlo = (const char *) v->data + d->lo;
|
|
1363
|
+
const char *dhi = (const char *) v->data + d->hi;
|
|
1364
|
+
const char *mlo = (const char *) m->view.data;
|
|
1365
|
+
const char *mhi = mlo + m->elements;
|
|
1366
|
+
if ( mlo < dhi && dlo < mhi ) {
|
|
1367
|
+
rb_raise(rb_eArgError, "mask: the mask buffer overlaps the data buffer");
|
|
1368
|
+
}
|
|
1143
1369
|
}
|
|
1370
|
+
ca_mv_check_bits(m, "mask: ");
|
|
1144
1371
|
}
|
|
1145
|
-
if (
|
|
1146
|
-
|
|
1147
|
-
MV_MASK_CLEANUP_AND_RAISE("mask: item_size must be 1 (got %ld)",
|
|
1148
|
-
(long) got);
|
|
1149
|
-
}
|
|
1150
|
-
if (mv->ndim != data_ndim) {
|
|
1151
|
-
int got = (int) mv->ndim;
|
|
1152
|
-
MV_MASK_CLEANUP_AND_RAISE("mask: ndim (%d) does not match data ndim (%d)",
|
|
1153
|
-
got, (int) data_ndim);
|
|
1154
|
-
}
|
|
1155
|
-
if (mv->shape == NULL) {
|
|
1156
|
-
/* SIMPLE-form mask (contiguous 1-D, shape omitted). item_size is 1
|
|
1157
|
-
(checked above), so byte_size is the element count. */
|
|
1158
|
-
ca_size_t mlen = (ca_size_t) mv->byte_size;
|
|
1159
|
-
if (data_ndim != 1 || mlen != data_dim[0]) {
|
|
1160
|
-
long ms = (long) mlen, ds = (long) (data_ndim == 1 ? data_dim[0] : -1);
|
|
1161
|
-
MV_MASK_CLEANUP_AND_RAISE(
|
|
1162
|
-
"mask: length (%ld) does not match data shape (%ld)", ms, ds);
|
|
1163
|
-
}
|
|
1372
|
+
if ( data_type == CA_BOOLEAN ) {
|
|
1373
|
+
ca_mv_check_bits(d, "");
|
|
1164
1374
|
}
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1375
|
+
|
|
1376
|
+
if ( ! d->contiguous ) {
|
|
1377
|
+
/* The foreign bytes the elements reach, [data + lo, data + hi), as a
|
|
1378
|
+
byte array, with the user's CAStride over it. The parent covers
|
|
1379
|
+
every byte a stride can land on, so a mask made for the view (one
|
|
1380
|
+
mask byte per parent byte) has a place for every element whatever
|
|
1381
|
+
the strides are. */
|
|
1382
|
+
ca_size_t span = (ca_size_t) (d->hi - d->lo);
|
|
1383
|
+
ca_size_t strides_arr[CA_RANK_MAX];
|
|
1384
|
+
volatile VALUE inner_wrap;
|
|
1385
|
+
CArray *iw;
|
|
1386
|
+
int8_t k;
|
|
1387
|
+
|
|
1388
|
+
inner_wrap = rb_ca_wrap_new(CA_UINT8, 1, &span, 1, NULL,
|
|
1389
|
+
(char *) v->data + d->lo);
|
|
1390
|
+
rb_ivar_set(inner_wrap, rb_intern("memory_view_source"), a->obj);
|
|
1391
|
+
rb_ivar_set(inner_wrap, rb_intern("memory_view_holder"), a->data_holder_obj);
|
|
1392
|
+
if ( v->readonly && ca_mv_extract(inner_wrap, &iw) ) {
|
|
1393
|
+
ca_set_flag(iw, CA_FLAG_READ_ONLY);
|
|
1173
1394
|
}
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1395
|
+
for (k = 0; k < d->ndim; k++) {
|
|
1396
|
+
strides_arr[k] = (ca_size_t) d->strides[k];
|
|
1397
|
+
}
|
|
1398
|
+
wrap = rb_ca_stride_new(inner_wrap, data_type, bytes, d->ndim, d->dim,
|
|
1399
|
+
strides_arr, (ca_size_t) -d->lo);
|
|
1400
|
+
if ( v->readonly && ca_mv_extract(wrap, &ca) ) {
|
|
1401
|
+
ca_set_flag(ca, CA_FLAG_READ_ONLY);
|
|
1402
|
+
}
|
|
1403
|
+
return wrap;
|
|
1179
1404
|
}
|
|
1180
1405
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1406
|
+
/* Same struct rb_ca_wrap_new would build, wrapped in the class the
|
|
1407
|
+
receiver asked for. */
|
|
1408
|
+
wrap = ca_wrap_struct_as(ca_wrap_new(data_type, d->ndim, d->dim, bytes,
|
|
1409
|
+
NULL, (char *) v->data),
|
|
1410
|
+
a->target_class);
|
|
1411
|
+
rb_ivar_set(wrap, rb_intern("memory_view_source"), a->obj);
|
|
1412
|
+
rb_ivar_set(wrap, rb_intern("memory_view_holder"), a->data_holder_obj);
|
|
1413
|
+
ca_mv_extract(wrap, &ca);
|
|
1414
|
+
if ( v->readonly ) {
|
|
1415
|
+
ca_set_flag(ca, CA_FLAG_READ_ONLY);
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
/* Borrowed mask: a CAWrap over the mask bytes, anchored on the same
|
|
1419
|
+
wrap so its lifetime is the data side's. A store writes the mask
|
|
1420
|
+
cell of every element it stores, so a read-only mask makes the whole
|
|
1421
|
+
array read-only. */
|
|
1422
|
+
if ( m != NULL ) {
|
|
1423
|
+
CArray *mask_ca = (CArray *) ca_wrap_new(CA_BOOLEAN, d->ndim, d->dim, 1,
|
|
1424
|
+
NULL, (char *) m->view.data);
|
|
1425
|
+
ca_set_flag(mask_ca, CA_FLAG_MASK_ARRAY);
|
|
1426
|
+
if ( m->view.readonly ) {
|
|
1427
|
+
ca_set_flag(mask_ca, CA_FLAG_READ_ONLY);
|
|
1428
|
+
ca_set_flag(ca, CA_FLAG_READ_ONLY);
|
|
1429
|
+
}
|
|
1430
|
+
ca->mask = mask_ca;
|
|
1431
|
+
rb_ivar_set(wrap, rb_intern("mask_memory_view_source"), a->mask_obj);
|
|
1432
|
+
rb_ivar_set(wrap, rb_intern("mask_memory_view_holder"), a->mask_holder_obj);
|
|
1433
|
+
}
|
|
1434
|
+
return wrap;
|
|
1183
1435
|
}
|
|
1184
1436
|
|
|
1185
|
-
/* Copy a strided MemoryView into a contiguous row-major destination buffer.
|
|
1186
|
-
Walks indices in row-major order and reads from view->data using
|
|
1187
|
-
view->strides at each step. Assumes view->ndim >= 1, view->strides
|
|
1188
|
-
non-NULL, dst sized to hold elements * item_size. */
|
|
1189
1437
|
static void
|
|
1190
|
-
|
|
1191
|
-
ca_size_t elements)
|
|
1438
|
+
ca_mv_import_args_init (ca_mv_import_args_t *a, VALUE obj, VALUE mask_obj)
|
|
1192
1439
|
{
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
for (n = 0; n < elements; n++) {
|
|
1202
|
-
ssize_t src_off = 0;
|
|
1203
|
-
for (k = 0; k < ndim; k++) {
|
|
1204
|
-
src_off += (ssize_t) idx[k] * view->strides[k];
|
|
1205
|
-
}
|
|
1206
|
-
memcpy(dst, (const char *) view->data + src_off, (size_t) item_size);
|
|
1207
|
-
dst += item_size;
|
|
1208
|
-
/* increment row-major: last dim varies fastest */
|
|
1209
|
-
for (k = ndim - 1; k >= 0; k--) {
|
|
1210
|
-
idx[k]++;
|
|
1211
|
-
if (idx[k] < (ca_size_t) view->shape[k]) break;
|
|
1212
|
-
idx[k] = 0;
|
|
1213
|
-
}
|
|
1440
|
+
a->obj = obj;
|
|
1441
|
+
a->mask_obj = mask_obj;
|
|
1442
|
+
a->data_holder_obj = ca_mv_holder_new(&a->data);
|
|
1443
|
+
a->mask = NULL;
|
|
1444
|
+
a->mask_holder_obj = Qnil;
|
|
1445
|
+
if ( ! NIL_P(mask_obj) ) {
|
|
1446
|
+
a->mask_holder_obj = ca_mv_holder_new(&a->mask);
|
|
1214
1447
|
}
|
|
1215
1448
|
}
|
|
1216
1449
|
|
|
1217
|
-
/*
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1450
|
+
/* Wrap obj's MemoryView without copying. `explicit_data_type` and
|
|
1451
|
+
`typeless_data_type` are as for ca_mv_holder_take_data; `writable`
|
|
1452
|
+
asks the producer for a writable view (wrap_writable). Every view
|
|
1453
|
+
taken is released before a raise leaves. */
|
|
1454
|
+
VALUE
|
|
1455
|
+
ca_mv_wrap (VALUE obj, VALUE mask_obj, VALUE target_class,
|
|
1456
|
+
int8_t explicit_data_type, int8_t typeless_data_type, int writable)
|
|
1457
|
+
{
|
|
1458
|
+
ca_mv_import_args_t a;
|
|
1459
|
+
volatile VALUE result;
|
|
1460
|
+
int tag = 0;
|
|
1461
|
+
|
|
1462
|
+
ca_mv_import_args_init(&a, obj, mask_obj);
|
|
1463
|
+
a.target_class = target_class;
|
|
1464
|
+
a.explicit_data_type = explicit_data_type;
|
|
1465
|
+
a.typeless_data_type = typeless_data_type;
|
|
1466
|
+
a.writable = writable;
|
|
1467
|
+
/* protect: the views are released at once rather than when the holders are collected */
|
|
1468
|
+
result = rb_protect(ca_mv_wrap_body, (VALUE) &a, &tag);
|
|
1469
|
+
if ( tag ) {
|
|
1470
|
+
ca_mv_holder_release(a.mask);
|
|
1471
|
+
ca_mv_holder_release(a.data);
|
|
1472
|
+
rb_jump_tag(tag);
|
|
1473
|
+
}
|
|
1474
|
+
RB_GC_GUARD(a.data_holder_obj);
|
|
1475
|
+
RB_GC_GUARD(a.mask_holder_obj);
|
|
1476
|
+
return result;
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
/* CArray.wrap_memory_view(obj, data_type: nil, mask: nil) -- zero-copy.
|
|
1480
|
+
A row-major contiguous producer comes back as a CAWrap; a strided one
|
|
1481
|
+
as a CAStride over the foreign bytes, its strides (negative ones
|
|
1482
|
+
included) kept.
|
|
1222
1483
|
|
|
1223
1484
|
When the producer is typeless (format=NULL), `data_type:` must be
|
|
1224
1485
|
supplied; the byte buffer is reinterpreted as that data_type, ndim=1.
|
|
1225
1486
|
|
|
1226
1487
|
The receiver picks the class of the result. Called on CArray itself
|
|
1227
|
-
it builds a CAWrap
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1488
|
+
it builds a CAWrap; called on a subclass of CAWrap it builds that
|
|
1489
|
+
subclass, so a gem bridging a foreign buffer can name where the array
|
|
1490
|
+
came from without writing a C extension of its own. The class marks
|
|
1491
|
+
the provenance of this object only -- a view derived from it is a
|
|
1492
|
+
CABlock or a CAStride like any other. */
|
|
1232
1493
|
static VALUE
|
|
1233
1494
|
rb_ca_s_wrap_memory_view (int argc, VALUE *argv, VALUE klass)
|
|
1234
1495
|
{
|
|
1235
|
-
|
|
1236
|
-
ca_mv_imported_holder_t *mask_holder = NULL;
|
|
1237
|
-
rb_memory_view_t *v;
|
|
1238
|
-
int8_t data_type, ndim;
|
|
1239
|
-
ca_size_t dim[CA_RANK_MAX];
|
|
1240
|
-
VALUE wrap, holder_obj, obj, mask_obj, target_class;
|
|
1496
|
+
VALUE obj, mask_obj, target_class;
|
|
1241
1497
|
int8_t explicit_data_type =
|
|
1242
1498
|
ca_mv_extract_type_and_mask_kwargs(argc, argv,
|
|
1243
1499
|
(VALUE *) &obj, (VALUE *) &mask_obj);
|
|
1244
1500
|
|
|
1245
|
-
/* Resolve the class before anything is acquired, so this rejection
|
|
1246
|
-
needs no cleanup. CArray means CAWrap; any other receiver has to
|
|
1247
|
-
be a CAWrap the caller can actually be handed. */
|
|
1248
1501
|
target_class = ( klass == rb_cCArray ) ? rb_cCAWrap : klass;
|
|
1249
1502
|
if ( ! RTEST(rb_class_inherited_p(target_class, rb_cCAWrap)) ) {
|
|
1250
1503
|
rb_raise(rb_eTypeError,
|
|
1251
1504
|
"wrap_memory_view builds a CAWrap; %s is not a subclass of it",
|
|
1252
1505
|
rb_class2name(target_class));
|
|
1253
1506
|
}
|
|
1507
|
+
return ca_mv_wrap(obj, mask_obj, target_class, explicit_data_type, -1, 0);
|
|
1508
|
+
}
|
|
1254
1509
|
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
runs before the mask is acquired, so the holder is all there is to
|
|
1268
|
-
free. */
|
|
1269
|
-
if ( target_class != rb_cCAWrap &&
|
|
1270
|
-
v->ndim >= 1 && v->format != NULL && ! ca_mv_view_is_contiguous(v) ) {
|
|
1271
|
-
rb_memory_view_release(v);
|
|
1272
|
-
holder->valid = false;
|
|
1273
|
-
xfree(holder);
|
|
1274
|
-
rb_raise(rb_eArgError,
|
|
1275
|
-
"%s was requested, but this producer is strided, so the result "
|
|
1276
|
-
"would be a CAStride; pass a row-major contiguous producer, or "
|
|
1277
|
-
"use CArray.from_memory_view for a copy",
|
|
1278
|
-
rb_class2name(target_class));
|
|
1279
|
-
}
|
|
1510
|
+
/* The body of from_memory_view. Runs under rb_ensure, which releases
|
|
1511
|
+
both views however it leaves. */
|
|
1512
|
+
static VALUE
|
|
1513
|
+
ca_mv_from_body (VALUE arg)
|
|
1514
|
+
{
|
|
1515
|
+
ca_mv_import_args_t *a = (ca_mv_import_args_t *) arg;
|
|
1516
|
+
ca_mv_imported_holder_t *d = a->data, *m = a->mask;
|
|
1517
|
+
rb_memory_view_t *v = &d->view;
|
|
1518
|
+
int8_t data_type;
|
|
1519
|
+
volatile VALUE result;
|
|
1520
|
+
CArray *ca;
|
|
1521
|
+
ca_size_t i;
|
|
1280
1522
|
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
if (! NIL_P(mask_obj)) {
|
|
1285
|
-
mask_holder = ca_mv_acquire_mask_view(mask_obj, ndim, dim,
|
|
1286
|
-
holder, &holder->view);
|
|
1287
|
-
/* Strided data side combined with mask: is not supported in this
|
|
1288
|
-
release. Catch this AFTER mask acquisition so the cleanup path can release
|
|
1289
|
-
both views uniformly. */
|
|
1290
|
-
if (v->ndim >= 1 && v->format != NULL &&
|
|
1291
|
-
! ca_mv_view_is_contiguous(v)) {
|
|
1292
|
-
rb_memory_view_release(&mask_holder->view);
|
|
1293
|
-
mask_holder->valid = false;
|
|
1294
|
-
xfree(mask_holder);
|
|
1295
|
-
rb_memory_view_release(v);
|
|
1296
|
-
holder->valid = false;
|
|
1297
|
-
xfree(holder);
|
|
1298
|
-
rb_raise(rb_eArgError,
|
|
1299
|
-
"mask: combining mask: with a strided data source is not "
|
|
1300
|
-
"supported in this release; pass a row-major contiguous "
|
|
1301
|
-
"data source");
|
|
1302
|
-
}
|
|
1523
|
+
data_type = ca_mv_holder_take_data(d, a->obj, a->explicit_data_type, -1, 0);
|
|
1524
|
+
if ( m != NULL ) {
|
|
1525
|
+
ca_mv_holder_take_mask(m, a->mask_obj, d);
|
|
1303
1526
|
}
|
|
1304
1527
|
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
outer CAStride is what the user sees, with full strides
|
|
1309
|
-
(including negative) preserved. */
|
|
1310
|
-
if (v->ndim >= 1 && v->format != NULL &&
|
|
1311
|
-
! ca_mv_view_is_contiguous(v)) {
|
|
1312
|
-
ca_size_t inner_dim[1] = { 1 };
|
|
1313
|
-
volatile VALUE inner_wrap;
|
|
1314
|
-
ca_size_t strides_arr[CA_RANK_MAX];
|
|
1315
|
-
int i;
|
|
1316
|
-
|
|
1317
|
-
inner_wrap = rb_ca_wrap_new(data_type, 1, inner_dim,
|
|
1318
|
-
ca_mv_carray_bytes_for(data_type, v->item_size),
|
|
1319
|
-
NULL, (char *) v->data);
|
|
1320
|
-
rb_ivar_set(inner_wrap, rb_intern("memory_view_source"), obj);
|
|
1321
|
-
holder_obj = TypedData_Wrap_Struct(rb_cObject,
|
|
1322
|
-
&ca_mv_imported_holder_data_type,
|
|
1323
|
-
holder);
|
|
1324
|
-
rb_ivar_set(inner_wrap, rb_intern("memory_view_holder"), holder_obj);
|
|
1325
|
-
if (v->readonly) {
|
|
1326
|
-
CArray *iw;
|
|
1327
|
-
if (ca_mv_extract(inner_wrap, &iw)) {
|
|
1328
|
-
ca_set_flag(iw, CA_FLAG_READ_ONLY);
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
for (i = 0; i < ndim; i++) {
|
|
1332
|
-
strides_arr[i] = (ca_size_t) v->strides[i];
|
|
1333
|
-
}
|
|
1334
|
-
{
|
|
1335
|
-
VALUE result =
|
|
1336
|
-
rb_ca_stride_new(inner_wrap, data_type,
|
|
1337
|
-
ca_mv_carray_bytes_for(data_type, v->item_size),
|
|
1338
|
-
ndim, dim, strides_arr, /* base_offset */ 0);
|
|
1339
|
-
if (v->readonly) {
|
|
1340
|
-
CArray *rs;
|
|
1341
|
-
if (ca_mv_extract(result, &rs)) {
|
|
1342
|
-
ca_set_flag(rs, CA_FLAG_READ_ONLY);
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
return result;
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1528
|
+
result = rb_carray_new(data_type, d->ndim, d->dim,
|
|
1529
|
+
ca_mv_carray_bytes_for(data_type, v->item_size), NULL);
|
|
1530
|
+
ca_mv_extract(result, &ca);
|
|
1348
1531
|
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
rb_ivar_set(wrap, rb_intern("memory_view_source"), obj);
|
|
1361
|
-
holder_obj = TypedData_Wrap_Struct(rb_cObject,
|
|
1362
|
-
&ca_mv_imported_holder_data_type,
|
|
1363
|
-
holder);
|
|
1364
|
-
rb_ivar_set(wrap, rb_intern("memory_view_holder"), holder_obj);
|
|
1365
|
-
|
|
1366
|
-
if (v->readonly) {
|
|
1367
|
-
CArray *ca;
|
|
1368
|
-
if (ca_mv_extract(wrap, &ca)) {
|
|
1369
|
-
ca_set_flag(ca, CA_FLAG_READ_ONLY);
|
|
1370
|
-
}
|
|
1532
|
+
if ( d->contiguous ) {
|
|
1533
|
+
memcpy(ca->ptr, v->data, (size_t) ca_length(ca));
|
|
1534
|
+
}
|
|
1535
|
+
else {
|
|
1536
|
+
ca_mv_gather_t g;
|
|
1537
|
+
g.dst = ca->ptr;
|
|
1538
|
+
g.item_size = (size_t) v->item_size;
|
|
1539
|
+
ca_mv_holder_each(d, ca_mv_gather_one, &g);
|
|
1540
|
+
}
|
|
1541
|
+
if ( data_type == CA_BOOLEAN ) {
|
|
1542
|
+
ca_boolean_normalize_bytes(ca->ptr, ca->elements);
|
|
1371
1543
|
}
|
|
1372
1544
|
|
|
1373
|
-
/*
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
CArray *data_ca, *mask_ca;
|
|
1382
|
-
rb_memory_view_t *mv = &mask_holder->view;
|
|
1383
|
-
VALUE mask_holder_obj;
|
|
1384
|
-
if (! ca_mv_extract(wrap, &data_ca)) {
|
|
1385
|
-
rb_memory_view_release(mv);
|
|
1386
|
-
mask_holder->valid = false;
|
|
1387
|
-
xfree(mask_holder);
|
|
1388
|
-
rb_raise(rb_eRuntimeError,
|
|
1389
|
-
"internal: rb_ca_wrap_new did not return a CArray");
|
|
1390
|
-
}
|
|
1391
|
-
mask_ca = (CArray *) ca_wrap_new(CA_BOOLEAN, ndim, dim, 1, NULL,
|
|
1392
|
-
(char *) mv->data);
|
|
1393
|
-
ca_set_flag(mask_ca, CA_FLAG_MASK_ARRAY);
|
|
1394
|
-
if (mv->readonly) {
|
|
1395
|
-
ca_set_flag(mask_ca, CA_FLAG_READ_ONLY);
|
|
1545
|
+
/* Mask bytes: any non-zero byte means masked. */
|
|
1546
|
+
if ( m != NULL ) {
|
|
1547
|
+
const uint8_t *src = (const uint8_t *) m->view.data;
|
|
1548
|
+
boolean8_t *dst;
|
|
1549
|
+
ca_create_mask(ca);
|
|
1550
|
+
dst = (boolean8_t *) ca->mask->ptr;
|
|
1551
|
+
for (i = 0; i < ca->elements; i++) {
|
|
1552
|
+
dst[i] = ( src[i] != 0 ) ? 1 : 0;
|
|
1396
1553
|
}
|
|
1397
|
-
data_ca->mask = mask_ca;
|
|
1398
|
-
|
|
1399
|
-
rb_ivar_set(wrap, rb_intern("mask_memory_view_source"), mask_obj);
|
|
1400
|
-
mask_holder_obj = TypedData_Wrap_Struct(rb_cObject,
|
|
1401
|
-
&ca_mv_imported_holder_data_type,
|
|
1402
|
-
mask_holder);
|
|
1403
|
-
rb_ivar_set(wrap, rb_intern("mask_memory_view_holder"), mask_holder_obj);
|
|
1404
1554
|
}
|
|
1555
|
+
return result;
|
|
1556
|
+
}
|
|
1405
1557
|
|
|
1406
|
-
|
|
1558
|
+
static VALUE
|
|
1559
|
+
ca_mv_from_release (VALUE arg)
|
|
1560
|
+
{
|
|
1561
|
+
ca_mv_import_args_t *a = (ca_mv_import_args_t *) arg;
|
|
1562
|
+
ca_mv_holder_release(a->mask);
|
|
1563
|
+
ca_mv_holder_release(a->data);
|
|
1564
|
+
return Qnil;
|
|
1407
1565
|
}
|
|
1408
1566
|
|
|
1409
|
-
/* CArray.from_memory_view(obj, data_type: nil) -- copy into an
|
|
1410
|
-
Accepts strided typed sources (gather copy) and
|
|
1411
|
-
|
|
1567
|
+
/* CArray.from_memory_view(obj, data_type: nil, mask: nil) -- copy into an
|
|
1568
|
+
independent CArray. Accepts strided typed sources (gather copy) and
|
|
1569
|
+
typeless sources (bytes reinterpreted as `data_type:`). The result is
|
|
1570
|
+
a plain CArray whatever the receiver: a copy no longer borrows anything
|
|
1571
|
+
for a class to name. A receiver that is not CArray or a CAWrap could
|
|
1572
|
+
not be returned, so it is refused. */
|
|
1412
1573
|
static VALUE
|
|
1413
1574
|
rb_ca_s_from_memory_view (int argc, VALUE *argv, VALUE klass)
|
|
1414
1575
|
{
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
ca_size_t dim[CA_RANK_MAX];
|
|
1419
|
-
VALUE result, obj, mask_obj;
|
|
1420
|
-
CArray *ca;
|
|
1421
|
-
bool contig;
|
|
1576
|
+
ca_mv_import_args_t a;
|
|
1577
|
+
VALUE obj, mask_obj;
|
|
1578
|
+
volatile VALUE result;
|
|
1422
1579
|
int8_t explicit_data_type =
|
|
1423
1580
|
ca_mv_extract_type_and_mask_kwargs(argc, argv,
|
|
1424
1581
|
(VALUE *) &obj, (VALUE *) &mask_obj);
|
|
1425
1582
|
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
NULL);
|
|
1438
|
-
if (! ca_mv_extract(result, &ca)) {
|
|
1439
|
-
rb_memory_view_release(&view);
|
|
1440
|
-
rb_raise(rb_eRuntimeError, "internal: rb_carray_new did not return a CArray");
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1443
|
-
/* Typeless producers (format == NULL) and SIMPLE-form producers (shape
|
|
1444
|
-
omitted) are 1-D contiguous by construction; ca_mv_view_is_contiguous
|
|
1445
|
-
handles the NULL shape that would crash the raw contiguity helper. */
|
|
1446
|
-
contig = (view.format == NULL) || ca_mv_view_is_contiguous(&view);
|
|
1447
|
-
|
|
1448
|
-
if (contig) {
|
|
1449
|
-
size_t total_bytes = (size_t) ca_length(ca);
|
|
1450
|
-
/* For typeless producers byte_size and total_bytes are equal by
|
|
1451
|
-
construction; for typed producers we already validated alignment. */
|
|
1452
|
-
if (view.byte_size < (ssize_t) total_bytes) {
|
|
1453
|
-
rb_memory_view_release(&view);
|
|
1454
|
-
if (mask_holder) {
|
|
1455
|
-
rb_memory_view_release(&mask_holder->view);
|
|
1456
|
-
mask_holder->valid = false;
|
|
1457
|
-
xfree(mask_holder);
|
|
1458
|
-
}
|
|
1459
|
-
rb_raise(rb_eRuntimeError,
|
|
1460
|
-
"internal: byte_size mismatch (carray=%lu, view=%ld)",
|
|
1461
|
-
(unsigned long) total_bytes, (long) view.byte_size);
|
|
1462
|
-
}
|
|
1463
|
-
memcpy(ca->ptr, view.data, total_bytes);
|
|
1464
|
-
}
|
|
1465
|
-
else {
|
|
1466
|
-
/* Strided typed source: walk indices in row-major order and gather. */
|
|
1467
|
-
ca_mv_copy_strided_to_contiguous(ca->ptr, &view, ca->elements);
|
|
1468
|
-
}
|
|
1469
|
-
|
|
1470
|
-
rb_memory_view_release(&view);
|
|
1471
|
-
|
|
1472
|
-
/* mask: materialise the canonical CA_BOOLEAN mask slot via ca_create_mask,
|
|
1473
|
-
then memcpy bytes from the (validated row-major contig 1-byte/elem) mask
|
|
1474
|
-
buffer. Source format ? / B / b all use byte values where any non-zero
|
|
1475
|
-
means masked; that semantic matches CArray's mask convention directly
|
|
1476
|
-
when we coerce non-zero to 1. */
|
|
1477
|
-
if (mask_holder) {
|
|
1478
|
-
rb_memory_view_t *mv = &mask_holder->view;
|
|
1479
|
-
size_t n = (size_t) ca->elements;
|
|
1480
|
-
size_t i;
|
|
1481
|
-
const uint8_t *src = (const uint8_t *) mv->data;
|
|
1482
|
-
boolean8_t *dst;
|
|
1483
|
-
ca_create_mask(ca);
|
|
1484
|
-
dst = (boolean8_t *) ca->mask->ptr;
|
|
1485
|
-
for (i = 0; i < n; i++) {
|
|
1486
|
-
dst[i] = (src[i] != 0) ? 1 : 0;
|
|
1487
|
-
}
|
|
1488
|
-
rb_memory_view_release(mv);
|
|
1489
|
-
mask_holder->valid = false;
|
|
1490
|
-
xfree(mask_holder);
|
|
1491
|
-
}
|
|
1492
|
-
|
|
1583
|
+
if ( klass != rb_cCArray &&
|
|
1584
|
+
! RTEST(rb_class_inherited_p(klass, rb_cCAWrap)) ) {
|
|
1585
|
+
rb_raise(rb_eTypeError,
|
|
1586
|
+
"from_memory_view builds a CArray; %s cannot be returned from it",
|
|
1587
|
+
rb_class2name(klass));
|
|
1588
|
+
}
|
|
1589
|
+
ca_mv_import_args_init(&a, obj, mask_obj);
|
|
1590
|
+
a.explicit_data_type = explicit_data_type;
|
|
1591
|
+
result = rb_ensure(ca_mv_from_body, (VALUE) &a, ca_mv_from_release, (VALUE) &a);
|
|
1592
|
+
RB_GC_GUARD(a.data_holder_obj);
|
|
1593
|
+
RB_GC_GUARD(a.mask_holder_obj);
|
|
1493
1594
|
return result;
|
|
1494
1595
|
}
|
|
1495
1596
|
|
|
@@ -1512,7 +1613,7 @@ rb_ca_s_memory_view_reject_reason (VALUE klass, VALUE obj)
|
|
|
1512
1613
|
CArray *ca;
|
|
1513
1614
|
ca_mv_reject_t rej;
|
|
1514
1615
|
if (! ca_mv_extract(obj, &ca)) {
|
|
1515
|
-
return
|
|
1616
|
+
return Qnil;
|
|
1516
1617
|
}
|
|
1517
1618
|
if (! ca_mv_ca_exportable(ca, obj)) {
|
|
1518
1619
|
if (ca->data_type == CA_FIXLEN && RTEST(rb_ca_has_data_class(obj))) {
|
|
@@ -1530,17 +1631,18 @@ rb_ca_s_memory_view_reject_reason (VALUE klass, VALUE obj)
|
|
|
1530
1631
|
}
|
|
1531
1632
|
if (! ca_mv_check_alias_chain(ca, &rej)) {
|
|
1532
1633
|
const char *where;
|
|
1533
|
-
if (rej.depth
|
|
1634
|
+
if (rej.depth < 0) where = "the root";
|
|
1635
|
+
else if (rej.depth == 0) where = "this view";
|
|
1534
1636
|
else if (rej.depth == 1) where = "parent";
|
|
1535
1637
|
else where = NULL; /* ancestor[N] */
|
|
1536
1638
|
if (where) {
|
|
1537
|
-
return rb_sprintf("%s is %s (%s); zero-copy wrap not possible
|
|
1538
|
-
|
|
1539
|
-
|
|
1639
|
+
return rb_sprintf("%s is %s (%s); zero-copy wrap not possible.%s",
|
|
1640
|
+
where, rej.class_name, rej.reason,
|
|
1641
|
+
rej.face ? "" : " Export arr.copy for a snapshot.");
|
|
1540
1642
|
} else {
|
|
1541
|
-
return rb_sprintf("ancestor[%d] is %s (%s); zero-copy wrap not possible
|
|
1542
|
-
|
|
1543
|
-
rej.
|
|
1643
|
+
return rb_sprintf("ancestor[%d] is %s (%s); zero-copy wrap not possible.%s",
|
|
1644
|
+
rej.depth - 1, rej.class_name, rej.reason,
|
|
1645
|
+
rej.face ? "" : " Export arr.copy for a snapshot.");
|
|
1544
1646
|
}
|
|
1545
1647
|
}
|
|
1546
1648
|
return Qnil;
|
|
@@ -1566,8 +1668,8 @@ rb_ca_s_parse_memory_view_format (VALUE klass, VALUE fmt_v, VALUE item_size_v)
|
|
|
1566
1668
|
|
|
1567
1669
|
/* Test hook: return the MV format string the producer would emit
|
|
1568
1670
|
for `obj`, without actually building the rb_memory_view_t. Used
|
|
1569
|
-
by spec_ai to verify
|
|
1570
|
-
|
|
1671
|
+
by spec_ai to verify the record format emitted for CAStruct arrays.
|
|
1672
|
+
Returns nil if obj isn't exportable. */
|
|
1571
1673
|
static VALUE
|
|
1572
1674
|
rb_ca_s_memory_view_format (VALUE klass, VALUE obj)
|
|
1573
1675
|
{
|
|
@@ -1578,24 +1680,30 @@ rb_ca_s_memory_view_format (VALUE klass, VALUE obj)
|
|
|
1578
1680
|
return ca_mv_struct_format_for_data_class(rb_ca_data_class(obj));
|
|
1579
1681
|
}
|
|
1580
1682
|
if (ca->data_type == CA_FIXLEN) {
|
|
1581
|
-
/* Plain CA_FIXLEN:
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
via view->format, which test code may rely on. */
|
|
1585
|
-
VALUE fmt_str = rb_attr_get(obj, rb_intern("__mv_fixlen_format__"));
|
|
1586
|
-
if (NIL_P(fmt_str)) {
|
|
1587
|
-
char fmt_buf[32];
|
|
1588
|
-
snprintf(fmt_buf, sizeof(fmt_buf), "%lds", (long) ca->bytes);
|
|
1589
|
-
fmt_str = rb_str_new_cstr(fmt_buf);
|
|
1590
|
-
rb_obj_freeze(fmt_str);
|
|
1591
|
-
rb_ivar_set(obj, rb_intern("__mv_fixlen_format__"), fmt_str);
|
|
1592
|
-
}
|
|
1593
|
-
return fmt_str;
|
|
1683
|
+
/* Plain CA_FIXLEN: the same "Ns" String the producer (ca_mv_get)
|
|
1684
|
+
hands out via view->format. */
|
|
1685
|
+
return rb_obj_freeze(rb_str_new_cstr(ca_mv_fixlen_format(ca->bytes)));
|
|
1594
1686
|
}
|
|
1595
1687
|
const char *fmt = ca_mv_format_for(ca->data_type);
|
|
1596
1688
|
return fmt ? rb_str_new_cstr(fmt) : Qnil;
|
|
1597
1689
|
}
|
|
1598
1690
|
|
|
1691
|
+
/* CARecord#memory_view_field_names: the names of the record's members,
|
|
1692
|
+
one per specifier of the record's MemoryView format, in that order.
|
|
1693
|
+
Ruby's format has no place for them, so a bridge that writes a format
|
|
1694
|
+
which does (PEP 3118 "T{...}") asks the exporting object for them under
|
|
1695
|
+
this name. nil when the record has no MemoryView format. */
|
|
1696
|
+
static VALUE
|
|
1697
|
+
rb_ca_record_memory_view_field_names (VALUE self)
|
|
1698
|
+
{
|
|
1699
|
+
VALUE data_class = rb_ca_data_class(self);
|
|
1700
|
+
if ( NIL_P(data_class)
|
|
1701
|
+
|| NIL_P(ca_mv_struct_format_for_data_class(data_class)) ) {
|
|
1702
|
+
return Qnil;
|
|
1703
|
+
}
|
|
1704
|
+
return rb_ary_dup(rb_const_get(data_class, rb_intern("MEMBERS")));
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1599
1707
|
/* ---------------- Init ---------------- */
|
|
1600
1708
|
|
|
1601
1709
|
static const rb_memory_view_entry_t ca_memory_view_entry = {
|
|
@@ -1607,6 +1715,8 @@ static const rb_memory_view_entry_t ca_memory_view_entry = {
|
|
|
1607
1715
|
void
|
|
1608
1716
|
Init_carray_memory_view (void)
|
|
1609
1717
|
{
|
|
1718
|
+
ca_mv_fixlen_formats = st_init_numtable();
|
|
1719
|
+
rb_nativethread_lock_initialize(&ca_mv_fixlen_formats_lock);
|
|
1610
1720
|
/* Statically known classes. */
|
|
1611
1721
|
rb_memory_view_register(rb_cCArray, &ca_memory_view_entry);
|
|
1612
1722
|
rb_memory_view_register(rb_cCAWrap, &ca_memory_view_entry);
|
|
@@ -1640,11 +1750,15 @@ Init_carray_memory_view (void)
|
|
|
1640
1750
|
rb_ca_s_from_memory_view, -1);
|
|
1641
1751
|
rb_define_singleton_method(rb_cCArray, "wrap_memory_view",
|
|
1642
1752
|
rb_ca_s_wrap_memory_view, -1);
|
|
1643
|
-
/*
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1753
|
+
/* The format parser and the format an array exports, for the specs
|
|
1754
|
+
(test_format_parser.rb, test_mv_struct_format.rb). Private. */
|
|
1755
|
+
rb_define_private_method(rb_singleton_class(rb_cCArray),
|
|
1756
|
+
"parse_memory_view_format",
|
|
1757
|
+
rb_ca_s_parse_memory_view_format, 2);
|
|
1758
|
+
rb_define_private_method(rb_singleton_class(rb_cCArray),
|
|
1759
|
+
"memory_view_format_of",
|
|
1760
|
+
rb_ca_s_memory_view_format, 1);
|
|
1761
|
+
rb_define_method(rb_const_get(rb_cObject, rb_intern("CARecord")),
|
|
1762
|
+
"memory_view_field_names",
|
|
1763
|
+
rb_ca_record_memory_view_field_names, 0);
|
|
1650
1764
|
}
|