carray 3.0.2 → 3.0.3
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 +304 -0
- data/README.md +3 -4
- data/carray.gemspec +1 -1
- data/ext/ca_axis_dispatch.c +225 -0
- data/ext/ca_axis_group.c +177 -99
- data/ext/ca_binop_dispatch.c +54 -32
- data/ext/ca_binop_dispatch.h +1 -0
- data/ext/ca_categorical_iterator.c +66 -71
- data/ext/ca_compare.c +8 -2
- data/ext/ca_for_buffer.h +21 -68
- data/ext/ca_group_iter.c +27 -11
- data/ext/ca_kernel_iterator.c +663 -214
- data/ext/ca_kernel_iterator.h +231 -167
- data/ext/ca_monop_dispatch.c +4 -4
- data/ext/ca_monop_dispatch.h +4 -0
- data/ext/ca_obj_array.c +132 -22
- data/ext/ca_obj_bincmp.c +152 -121
- data/ext/ca_obj_binop.c +107 -210
- data/ext/ca_obj_bitarray.c +37 -24
- data/ext/ca_obj_bitfield.c +95 -62
- data/ext/ca_obj_block.c +1 -0
- data/ext/ca_obj_byte_swap.c +40 -40
- data/ext/ca_obj_const_string.c +96 -25
- data/ext/ca_obj_face.c +406 -64
- data/ext/ca_obj_face.h +92 -47
- data/ext/ca_obj_fake.c +21 -50
- data/ext/ca_obj_farray.c +1 -0
- data/ext/ca_obj_field.c +16 -3
- data/ext/ca_obj_fixlen_string.c +21 -21
- data/ext/ca_obj_grid.c +181 -124
- data/ext/ca_obj_meld.c +189 -107
- data/ext/ca_obj_moncmp.c +61 -61
- data/ext/ca_obj_monop.c +125 -107
- data/ext/ca_obj_object.c +543 -233
- data/ext/ca_obj_record.c +51 -16
- data/ext/ca_obj_reduce.c +5 -8
- data/ext/ca_obj_refer.c +117 -33
- data/ext/ca_obj_remap.c +57 -17
- data/ext/ca_obj_repeat.c +21 -5
- data/ext/ca_obj_roll.c +115 -11
- data/ext/ca_obj_select.c +47 -49
- data/ext/ca_obj_select_axis.c +58 -60
- data/ext/ca_obj_shift.c +22 -102
- data/ext/ca_obj_stack.c +223 -118
- data/ext/ca_obj_stride.c +195 -34
- data/ext/ca_obj_string.c +17 -18
- data/ext/ca_obj_tile.c +32 -16
- data/ext/ca_obj_time.c +23 -25
- data/ext/ca_obj_timedelta.c +21 -23
- data/ext/ca_obj_transpose.c +12 -3
- data/ext/ca_obj_triop.c +110 -117
- data/ext/ca_obj_window.c +149 -27
- 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 +102 -79
- 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 +463 -0
- data/ext/carray.h +70 -27
- data/ext/carray_access.c +455 -211
- data/ext/carray_address_basis.c +177 -36
- data/ext/carray_attribute.c +45 -3
- data/ext/carray_bincount.c +32 -2
- data/ext/carray_broadcast.c +46 -14
- data/ext/carray_call_cfunc.c +0 -28
- data/ext/carray_cast.c +1444 -209
- data/ext/carray_cast_func.rb +34 -21
- data/ext/carray_conversion.c +197 -73
- data/ext/carray_copy.c +32 -11
- data/ext/carray_core.c +514 -122
- data/ext/carray_count.c +140 -87
- data/ext/carray_element.c +18 -27
- data/ext/carray_factorize.c +569 -277
- data/ext/carray_generate.c +76 -40
- data/ext/carray_histogram.c +152 -94
- data/ext/carray_hold.c +2 -1
- data/ext/carray_index_classifier.c +63 -96
- data/ext/carray_internal.h +385 -3
- data/ext/carray_lazy.c +834 -79
- data/ext/carray_loop.c +81 -65
- data/ext/carray_mask.c +682 -316
- data/ext/carray_math_kernel.h +1 -0
- data/ext/carray_median_percentile.c +221 -76
- data/ext/carray_memory_view.c +254 -248
- data/ext/carray_operator.c +1089 -359
- data/ext/carray_order.c +133 -69
- data/ext/carray_partition.c +102 -56
- data/ext/carray_random.c +273 -239
- data/ext/carray_scatter.c +308 -65
- data/ext/carray_segment.c +257 -0
- data/ext/carray_slab.c +170 -70
- data/ext/carray_sort.c +215 -218
- data/ext/carray_sort_kernel.c +8 -1
- data/ext/carray_struct.c +99 -21
- data/ext/carray_test.c +167 -64
- data/ext/carray_time_text.c +1182 -0
- data/ext/carray_utils.c +363 -98
- data/ext/extconf.rb +27 -2
- data/ext/mk_call_cfunc.rb +0 -2
- data/ext/mk_pow5_table.rb +35 -0
- data/ext/mkkernel.rb +1076 -846
- data/ext/ruby_carray.c +7 -1
- data/ext/version.h +4 -4
- data/ext/version.rb +4 -2
- data/lib/carray/attribute.rb +39 -0
- data/lib/carray/autoload_carray.rb +19 -0
- data/lib/carray/autoload_method_extension.rb +6 -0
- data/lib/carray/axis_group.rb +174 -26
- data/lib/carray/basics.rb +91 -25
- data/lib/carray/bincount_nd.rb +6 -0
- data/lib/carray/block_iterator.rb +118 -48
- data/lib/carray/boolean_reduce.rb +21 -29
- data/lib/carray/categorical.rb +94 -26
- data/lib/carray/categorical_iterator.rb +333 -826
- data/lib/carray/complex.rb +36 -29
- data/lib/carray/conditional.rb +94 -7
- data/lib/carray/const_string.rb +46 -0
- data/lib/carray/frame/concat.rb +157 -6
- data/lib/carray/frame/convert.rb +13 -7
- data/lib/carray/frame/csv_parser.rb +589 -82
- data/lib/carray/frame/describe.rb +79 -0
- data/lib/carray/frame/frame.rb +124 -25
- data/lib/carray/frame/group.rb +189 -63
- data/lib/carray/frame/io.rb +412 -72
- data/lib/carray/frame/join.rb +134 -46
- data/lib/carray/frame/records.rb +37 -16
- data/lib/carray/frame/reshape.rb +448 -0
- data/lib/carray/frame/sort.rb +26 -2
- data/lib/carray/frame/verbs.rb +715 -48
- data/lib/carray/frame.rb +2 -0
- data/lib/carray/fuse_source.rb +111 -2
- data/lib/carray/fusion.rb +229 -38
- data/lib/carray/inspect.rb +29 -11
- data/lib/carray/iterator.rb +14 -2
- data/lib/carray/lazy.rb +54 -30
- data/lib/carray/mask_gap_fill.rb +66 -52
- data/lib/carray/math.rb +32 -19
- data/lib/carray/meld_reduce.rb +40 -7
- data/lib/carray/methods/align_addr.rb +8 -4
- data/lib/carray/methods/bincount.rb +2 -2
- data/lib/carray/methods/bit_string.rb +14 -3
- data/lib/carray/methods/composition.rb +16 -10
- data/lib/carray/methods/factorize.rb +2 -2
- data/lib/carray/methods/gather_nd.rb +13 -2
- data/lib/carray/methods/insert_block.rb +9 -14
- data/lib/carray/methods/is_in.rb +117 -4
- data/lib/carray/methods/join.rb +1 -1
- data/lib/carray/methods/locate_addr.rb +177 -36
- data/lib/carray/methods/mode.rb +18 -1
- 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/record_order.rb +208 -0
- data/lib/carray/runtime.rb +14 -11
- data/lib/carray/segment_iterator.rb +990 -0
- data/lib/carray/serialize.rb +197 -27
- data/lib/carray/slab_iterator.rb +51 -27
- data/lib/carray/stack.rb +10 -4
- data/lib/carray/string_operation_extension.rb +4 -3
- data/lib/carray/struct.rb +142 -51
- data/lib/carray/struct_builder.rb +110 -14
- data/lib/carray/table.rb +2 -2
- data/lib/carray/time.rb +517 -103
- data/lib/carray/window_iterator.rb +127 -72
- data/lib/carray.rb +2 -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 +1 -1
- data/yard-stubs/ca_obj_refer.rb +3 -3
- data/yard-stubs/ca_obj_roll.rb +0 -1
- data/yard-stubs/ca_obj_stride.rb +10 -7
- data/yard-stubs/ca_obj_transpose.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +1 -2
- data/yard-stubs/carray_access.rb +5 -4
- data/yard-stubs/carray_broadcast.rb +1 -1
- data/yard-stubs/carray_cast.rb +78 -43
- data/yard-stubs/carray_conversion.rb +13 -1
- data/yard-stubs/carray_copy.rb +8 -3
- data/yard-stubs/carray_core.rb +3 -3
- data/yard-stubs/carray_count.rb +9 -0
- data/yard-stubs/carray_lazy.rb +5 -1
- data/yard-stubs/carray_loop.rb +4 -1
- data/yard-stubs/carray_mask.rb +34 -10
- data/yard-stubs/carray_math.rb +25 -13
- data/yard-stubs/carray_median_percentile.rb +10 -6
- data/yard-stubs/carray_order.rb +34 -13
- data/yard-stubs/carray_scatter.rb +10 -3
- data/yard-stubs/carray_segment.rb +72 -0
- data/yard-stubs/carray_slab.rb +2 -1
- data/yard-stubs/carray_sort.rb +43 -3
- data/yard-stubs/carray_test.rb +10 -0
- data/yard-stubs/carray_utils.rb +0 -26
- metadata +11 -19
- data/ext/ca_for_each_element.h +0 -239
- data/ext/carray_build_flags.h +0 -3
- data/ext/carray_config.h +0 -28
- data/ext/carray_kernels_bincmp.c +0 -4446
- data/ext/carray_kernels_binop.c +0 -11001
- data/ext/carray_kernels_init.c +0 -1131
- data/ext/carray_kernels_map.c +0 -3467
- data/ext/carray_kernels_moncmp.c +0 -2097
- data/ext/carray_kernels_monop.c +0 -18313
- data/ext/carray_kernels_reduce_aggregate.c +0 -26005
- data/ext/carray_kernels_reduce_boolean.c +0 -330
- data/ext/carray_kernels_reduce_cumulative.c +0 -14862
- data/ext/carray_kernels_reduce_extreme.c +0 -17494
- data/ext/carray_kernels_reduce_variance.c +0 -3910
- data/ext/carray_kernels_scan.c +0 -3693
- data/ext/carray_kernels_search.c +0 -32218
- data/ext/carray_kernels_sort.c +0 -10626
- data/ext/carray_kernels_triop.c +0 -1392
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,6 +29,7 @@
|
|
|
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"
|
|
30
34
|
#include <string.h>
|
|
31
35
|
|
|
@@ -55,12 +59,10 @@ typedef struct {
|
|
|
55
59
|
|
|
56
60
|
/* ---------------- obj_type strategy ---------------- */
|
|
57
61
|
|
|
58
|
-
/* Strategy = label only
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
structurally unreachable -- ca_mv_check_alias_chain rejects every
|
|
63
|
-
obj_type that maps to it. */
|
|
62
|
+
/* Strategy = label only. The export is alias-only and is decided by
|
|
63
|
+
ca_mv_compose_to_entity alone; the labels say which classes get a
|
|
64
|
+
MemoryView registration and keep ca_mv_runtime_types[] readable.
|
|
65
|
+
Every class labelled CA_MV_ATTACH is refused by that resolver. */
|
|
64
66
|
typedef enum {
|
|
65
67
|
CA_MV_REJECT,
|
|
66
68
|
CA_MV_DIRECT, /* entity / wrap / scalar; ca->ptr valid as-is */
|
|
@@ -72,9 +74,8 @@ typedef enum {
|
|
|
72
74
|
CA_MV_ATTACH, /* unreachable: rejected by alias-chain check */
|
|
73
75
|
} ca_mv_strategy_t;
|
|
74
76
|
|
|
75
|
-
/* Resolved at Init_carray_memory_view.
|
|
76
|
-
|
|
77
|
-
ca_mv_strategy_for can match them with a simple linear scan. */
|
|
77
|
+
/* Resolved at Init_carray_memory_view. Runtime obj_type ids are stored
|
|
78
|
+
here so ca_mv_reject_reason_for can name the class it refuses. */
|
|
78
79
|
static ca_mv_runtime_type_t ca_mv_runtime_types[CA_MV_NUM_RUNTIME_OBJ_TYPES] = {
|
|
79
80
|
{ "CAFarray", "CA_OBJ_FARRAY", -1, Qnil, /* CA_MV_FARRAY */ 4 },
|
|
80
81
|
{ "CATranspose", "CA_OBJ_TRANSPOSE", -1, Qnil, /* CA_MV_TRANS */ 5 },
|
|
@@ -143,69 +144,68 @@ ca_mv_format_for (int8_t data_type)
|
|
|
143
144
|
}
|
|
144
145
|
}
|
|
145
146
|
|
|
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. */
|
|
147
|
+
/* The Ruby MemoryView specifier for one record member, written into `out`
|
|
148
|
+
with its byte order as ruby/memory_view.h writes it: a suffix ('<' / '>')
|
|
149
|
+
on an integer, its own letter on a float ('e' / 'E' little, 'g' / 'G'
|
|
150
|
+
big). A member without a declared byte order is in the machine's.
|
|
151
|
+
Returns 0 for a member the format has no item for: a nested struct, an
|
|
152
|
+
array member, :fixlen, :object, a bit member, or a complex member with a
|
|
153
|
+
declared byte order (Ruby has no complex specifier to suffix). */
|
|
184
154
|
static int
|
|
185
|
-
|
|
155
|
+
ca_mv_record_member_spec (VALUE info, char *out, size_t outlen,
|
|
156
|
+
ca_size_t *bytes_out)
|
|
186
157
|
{
|
|
187
158
|
VALUE type = rb_ary_entry(info, 1);
|
|
159
|
+
VALUE opts = rb_ary_entry(info, 2);
|
|
160
|
+
VALUE endian = Qnil;
|
|
161
|
+
const char *base;
|
|
162
|
+
int8_t dt;
|
|
188
163
|
if (!SYMBOL_P(type)) return 0;
|
|
189
164
|
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
165
|
if (type_id == rb_intern("bitfield") ||
|
|
193
166
|
type_id == rb_intern("fixlen") ||
|
|
194
167
|
type_id == rb_intern("object")) {
|
|
195
168
|
return 0;
|
|
196
169
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (!
|
|
200
|
-
|
|
170
|
+
dt = rb_ca_guess_type(rb_sym2str(type));
|
|
171
|
+
base = ca_mv_format_for(dt);
|
|
172
|
+
if (!base) return 0;
|
|
173
|
+
if (RB_TYPE_P(opts, T_HASH)) {
|
|
174
|
+
endian = rb_hash_aref(opts, ID2SYM(rb_intern("endian")));
|
|
175
|
+
}
|
|
176
|
+
if ( SYMBOL_P(endian) && ( SYM2ID(endian) == rb_intern("big")
|
|
177
|
+
|| SYM2ID(endian) == rb_intern("little") ) ) {
|
|
178
|
+
int big = ( SYM2ID(endian) == rb_intern("big") );
|
|
179
|
+
switch (dt) {
|
|
180
|
+
case CA_FLOAT32: base = big ? "g" : "e"; break;
|
|
181
|
+
case CA_FLOAT64: base = big ? "G" : "E"; break;
|
|
182
|
+
case CA_CMPLX64: case CA_CMPLX128: return 0;
|
|
183
|
+
case CA_BOOLEAN: case CA_INT8: case CA_UINT8: break; /* one byte */
|
|
184
|
+
default:
|
|
185
|
+
snprintf(out, outlen, "%s%s", base, big ? ">" : "<");
|
|
186
|
+
*bytes_out = (ca_size_t) ca_sizeof[dt];
|
|
187
|
+
return 1;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
snprintf(out, outlen, "%s", base);
|
|
201
191
|
*bytes_out = (ca_size_t) ca_sizeof[dt];
|
|
202
192
|
return 1;
|
|
203
193
|
}
|
|
204
194
|
|
|
195
|
+
/* Build (or fetch from cache) the Ruby MemoryView format of a record: the
|
|
196
|
+
members' specifiers in their byte order in the record, with "x<N>" for
|
|
197
|
+
the padding between them and after the last. Ruby's format has no
|
|
198
|
+
member names; the record array gives them as #memory_view_field_names,
|
|
199
|
+
one per specifier. Returns Qnil for a record the format cannot describe:
|
|
200
|
+
a member it has no item for (see above), or members that overlap or come
|
|
201
|
+
out of offset order (a union).
|
|
202
|
+
|
|
203
|
+
The result is frozen and cached on the data_class, so the pointer
|
|
204
|
+
handed to a view stays valid while the class lives. */
|
|
205
205
|
static VALUE
|
|
206
206
|
ca_mv_struct_format_for_data_class (VALUE data_class)
|
|
207
207
|
{
|
|
208
|
-
ID iv = rb_intern("@
|
|
208
|
+
ID iv = rb_intern("@__mv_record_format__");
|
|
209
209
|
if (rb_ivar_defined(data_class, iv)) {
|
|
210
210
|
VALUE cached = rb_ivar_get(data_class, iv);
|
|
211
211
|
/* Qfalse is a sentinel meaning "tried, found unsupported". */
|
|
@@ -216,61 +216,36 @@ ca_mv_struct_format_for_data_class (VALUE data_class)
|
|
|
216
216
|
VALUE table = rb_const_get(data_class, rb_intern("MEMBER_TABLE"));
|
|
217
217
|
long n = RARRAY_LEN(members);
|
|
218
218
|
long i;
|
|
219
|
-
VALUE buf = rb_str_buf_new(
|
|
219
|
+
VALUE buf = rb_str_buf_new(4 * n + 8);
|
|
220
220
|
ca_size_t cursor = 0;
|
|
221
221
|
ca_size_t data_size = NUM2SIZE(rb_const_get(data_class, rb_intern("DATA_SIZE")));
|
|
222
|
-
|
|
223
|
-
rb_str_buf_cat_ascii(buf, "T{");
|
|
222
|
+
char spec[16], pad[32];
|
|
224
223
|
|
|
225
224
|
for (i = 0; i < n; i++) {
|
|
226
|
-
VALUE
|
|
227
|
-
|
|
225
|
+
VALUE info = rb_hash_aref(table, rb_ary_entry(members, i));
|
|
226
|
+
ca_size_t offset, mb;
|
|
228
227
|
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. */
|
|
228
|
+
offset = NUM2SIZE(rb_ary_entry(info, 0));
|
|
229
|
+
if (offset < cursor) goto unsupported;
|
|
230
|
+
if (!ca_mv_record_member_spec(info, spec, sizeof(spec), &mb)) goto unsupported;
|
|
241
231
|
if (offset > cursor) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
(long long) (offset - cursor));
|
|
245
|
-
rb_str_buf_cat_ascii(buf, pad_decl);
|
|
246
|
-
cursor = offset;
|
|
232
|
+
snprintf(pad, sizeof(pad), "x%lld", (long long) (offset - cursor));
|
|
233
|
+
rb_str_buf_cat_ascii(buf, pad);
|
|
247
234
|
}
|
|
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;
|
|
235
|
+
rb_str_buf_cat_ascii(buf, spec);
|
|
236
|
+
cursor = offset + mb;
|
|
255
237
|
}
|
|
256
|
-
|
|
257
|
-
/* Trailing padding (DATA_SIZE > last member's end). Elided form
|
|
258
|
-
(see the alignment-gap branch above for rationale). */
|
|
238
|
+
if (cursor > data_size) goto unsupported;
|
|
259
239
|
if (cursor < data_size) {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
(long long) (data_size - cursor));
|
|
263
|
-
rb_str_buf_cat_ascii(buf, pad_decl);
|
|
240
|
+
snprintf(pad, sizeof(pad), "x%lld", (long long) (data_size - cursor));
|
|
241
|
+
rb_str_buf_cat_ascii(buf, pad);
|
|
264
242
|
}
|
|
265
243
|
|
|
266
|
-
rb_str_buf_cat_ascii(buf, "}");
|
|
267
244
|
rb_obj_freeze(buf);
|
|
268
245
|
rb_ivar_set(data_class, iv, buf);
|
|
269
246
|
return buf;
|
|
270
247
|
|
|
271
248
|
unsupported:
|
|
272
|
-
/* Cache Qfalse to remember "we tried and gave up" — avoids
|
|
273
|
-
re-walking MEMBER_TABLE on every wrap call. */
|
|
274
249
|
rb_ivar_set(data_class, iv, Qfalse);
|
|
275
250
|
return Qnil;
|
|
276
251
|
}
|
|
@@ -282,7 +257,7 @@ ca_mv_data_type_exportable (int8_t data_type)
|
|
|
282
257
|
}
|
|
283
258
|
|
|
284
259
|
/* Same as ca_mv_data_type_exportable, but also accepts CA_FIXLEN:
|
|
285
|
-
- with a data_class -> emit
|
|
260
|
+
- with a data_class -> emit the record's Ruby format (members in order)
|
|
286
261
|
- without a data_class -> emit PEP 3118 "Ns" fixed-bytes format */
|
|
287
262
|
static int
|
|
288
263
|
ca_mv_ca_exportable (CArray *ca, VALUE obj)
|
|
@@ -424,35 +399,6 @@ ca_mv_carray_bytes_for (int8_t data_type, ssize_t item_size)
|
|
|
424
399
|
: (ca_size_t) ca_sizeof[data_type];
|
|
425
400
|
}
|
|
426
401
|
|
|
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
402
|
/* ---------------- helpers ---------------- */
|
|
457
403
|
|
|
458
404
|
static int
|
|
@@ -507,6 +453,8 @@ typedef struct {
|
|
|
507
453
|
int depth; /* 0 = the view itself, 1 = parent, 2 = grand-, ... */
|
|
508
454
|
const char *class_name; /* class name of the offending link */
|
|
509
455
|
const char *reason; /* short explanation */
|
|
456
|
+
int face; /* refused because it is a Face: a copy is
|
|
457
|
+
refused too, so no copy is suggested */
|
|
510
458
|
} ca_mv_reject_t;
|
|
511
459
|
|
|
512
460
|
static bool ca_mv_stride_is_contig (CAStride *ca); /* defined below */
|
|
@@ -551,6 +499,18 @@ ca_mv_reject_reason_for (CArray *ca)
|
|
|
551
499
|
return "this kind of view is not alias-capable";
|
|
552
500
|
}
|
|
553
501
|
|
|
502
|
+
/* The entities whose bytes the producer exports: CArray, CAWrap and
|
|
503
|
+
CScalar. An entity installed from outside the core (ca_install_obj_type,
|
|
504
|
+
e.g. a CASource subclass) may hold its bytes elsewhere (cold at rest),
|
|
505
|
+
so neither it nor a view onto it is exported. */
|
|
506
|
+
static inline bool
|
|
507
|
+
ca_mv_is_core_entity (CArray *ca)
|
|
508
|
+
{
|
|
509
|
+
return ca->obj_type == CA_OBJ_ARRAY ||
|
|
510
|
+
ca->obj_type == CA_OBJ_ARRAY_WRAP ||
|
|
511
|
+
ca->obj_type == CA_OBJ_SCALAR;
|
|
512
|
+
}
|
|
513
|
+
|
|
554
514
|
/* Resolve ca to an (entity, composed_strides, composed_base) triple
|
|
555
515
|
suitable for zero-copy strided export. Uses ca_stride_compose_to_root
|
|
556
516
|
to fold the CAStride parent chain (alias-eligible OR not, as long
|
|
@@ -569,15 +529,31 @@ ca_mv_compose_to_entity (CArray *ca, ca_mv_reject_t *rej,
|
|
|
569
529
|
ca_size_t *out_strides,
|
|
570
530
|
ca_size_t *out_base)
|
|
571
531
|
{
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
532
|
+
rej->face = 0;
|
|
533
|
+
/* A Face is a 1:1 alias of its storage, so it is exported as that
|
|
534
|
+
storage -- but only a Face that registered its bytes as carrying its
|
|
535
|
+
values (ca_face_register_memory_view). The format comes from the Face
|
|
536
|
+
(data_class for a CARecord); the layout from the storage below. */
|
|
577
537
|
if (ca_is_face(ca)) {
|
|
538
|
+
if (! ca_face_memory_view_exportable(ca)) {
|
|
539
|
+
rej->depth = 0;
|
|
540
|
+
rej->class_name = ca_mv_class_name_of(ca);
|
|
541
|
+
rej->reason = "a Face whose storage bytes do not carry its values "
|
|
542
|
+
"without the Face; export its storage instead "
|
|
543
|
+
"(e.g. .ticks, .codes)";
|
|
544
|
+
rej->face = 1;
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
578
547
|
ca = ca_strip_face(ca);
|
|
579
548
|
}
|
|
580
549
|
if (ca_is_entity(ca)) {
|
|
550
|
+
if (! ca_mv_is_core_entity(ca)) {
|
|
551
|
+
rej->depth = 0;
|
|
552
|
+
rej->class_name = ca_mv_class_name_of(ca);
|
|
553
|
+
rej->reason = "installed outside the core; its bytes are not "
|
|
554
|
+
"exported";
|
|
555
|
+
return false;
|
|
556
|
+
}
|
|
581
557
|
*out_entity = ca;
|
|
582
558
|
*out_base = 0;
|
|
583
559
|
return true;
|
|
@@ -601,6 +577,12 @@ ca_mv_compose_to_entity (CArray *ca, ca_mv_reject_t *rej,
|
|
|
601
577
|
}
|
|
602
578
|
return false;
|
|
603
579
|
}
|
|
580
|
+
if (! ca_mv_is_core_entity(root)) {
|
|
581
|
+
rej->depth = -1;
|
|
582
|
+
rej->class_name = ca_mv_class_name_of(root);
|
|
583
|
+
rej->reason = "installed outside the core; its bytes are not exported";
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
604
586
|
*out_entity = root;
|
|
605
587
|
return true;
|
|
606
588
|
}
|
|
@@ -641,30 +623,46 @@ ca_mv_available_p (VALUE obj)
|
|
|
641
623
|
if (! ca_mv_extract(obj, &ca)) return false;
|
|
642
624
|
if (! ca_mv_ca_exportable(ca, obj)) return false;
|
|
643
625
|
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
|
|
626
|
+
/* The same resolver decides the export in ca_mv_get, so what this
|
|
627
|
+
says is what the export does. Wrap-via-MV exports are zero-copy:
|
|
628
|
+
require an alias chain to a core entity. Non-alias views (CASelect, CAFake, etc.) and CAStride
|
|
655
629
|
chains broken by a non-contig link cannot be wrapped without a
|
|
656
630
|
snapshot, which has wrong semantics for MV consumers. */
|
|
657
631
|
return ca_mv_check_alias_chain(ca, &rej);
|
|
658
632
|
}
|
|
659
633
|
|
|
634
|
+
/* "Ns" format for a plain CA_FIXLEN of N bytes. The Strings are kept in
|
|
635
|
+
a table keyed by N for the life of the process, so the pointer stays
|
|
636
|
+
valid for any view, and a frozen array (which cannot take an ivar) is
|
|
637
|
+
exported like any other. */
|
|
638
|
+
static VALUE ca_mv_fixlen_formats = Qnil;
|
|
639
|
+
|
|
640
|
+
static VALUE
|
|
641
|
+
ca_mv_fixlen_format (ca_size_t bytes)
|
|
642
|
+
{
|
|
643
|
+
VALUE key = SIZE2NUM(bytes);
|
|
644
|
+
VALUE fmt;
|
|
645
|
+
if ( NIL_P(ca_mv_fixlen_formats) ) {
|
|
646
|
+
ca_mv_fixlen_formats = rb_hash_new();
|
|
647
|
+
rb_gc_register_mark_object(ca_mv_fixlen_formats);
|
|
648
|
+
}
|
|
649
|
+
fmt = rb_hash_lookup(ca_mv_fixlen_formats, key);
|
|
650
|
+
if ( NIL_P(fmt) ) {
|
|
651
|
+
fmt = rb_obj_freeze(rb_sprintf("%lds", (long) bytes));
|
|
652
|
+
rb_hash_aset(ca_mv_fixlen_formats, key, fmt);
|
|
653
|
+
}
|
|
654
|
+
return fmt;
|
|
655
|
+
}
|
|
656
|
+
|
|
660
657
|
static bool
|
|
661
658
|
ca_mv_get (VALUE obj, rb_memory_view_t *view, int flags)
|
|
662
659
|
{
|
|
663
660
|
CArray *ca;
|
|
664
|
-
|
|
661
|
+
CArray *storage;
|
|
665
662
|
ssize_t *shape = NULL;
|
|
666
663
|
ssize_t *strides = NULL;
|
|
667
664
|
void *data_ptr = NULL;
|
|
665
|
+
const char *format;
|
|
668
666
|
int writable_request;
|
|
669
667
|
|
|
670
668
|
if (! ca_mv_extract(obj, &ca)) return false;
|
|
@@ -678,11 +676,10 @@ ca_mv_get (VALUE obj, rb_memory_view_t *view, int flags)
|
|
|
678
676
|
Object#freeze on a subclass). */
|
|
679
677
|
if (writable_request && OBJ_FROZEN(obj)) return false;
|
|
680
678
|
|
|
681
|
-
/* Resolve the chain to (entity, composed_strides, composed_base)
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
ca->ptr already, no compose needed). */
|
|
679
|
+
/* Resolve the chain to (entity, composed_strides, composed_base) with
|
|
680
|
+
the resolver available_p uses, so the two cannot disagree. The
|
|
681
|
+
layout is the storage's: a Face that got through is a 1:1 alias of
|
|
682
|
+
it. */
|
|
686
683
|
CArray *resolved_entity = NULL;
|
|
687
684
|
ca_size_t composed_strides[CA_RANK_MAX];
|
|
688
685
|
ca_size_t composed_base = 0;
|
|
@@ -693,73 +690,66 @@ ca_mv_get (VALUE obj, rb_memory_view_t *view, int flags)
|
|
|
693
690
|
return false;
|
|
694
691
|
}
|
|
695
692
|
}
|
|
693
|
+
storage = ca_is_face(ca) ? ca_strip_face(ca) : ca;
|
|
696
694
|
|
|
697
695
|
/* SIMPLE consumer on non-contig view would need a snapshot -- not
|
|
698
696
|
offered (consumers wanting a snapshot use from_memory_view). */
|
|
699
697
|
{
|
|
700
698
|
int wants_strides_early = (flags & RUBY_MEMORY_VIEW_STRIDES) ? 1 : 0;
|
|
701
699
|
if (!wants_strides_early &&
|
|
702
|
-
ca_mv_is_castride_family(
|
|
703
|
-
!ca_mv_stride_is_contig((CAStride *)
|
|
700
|
+
ca_mv_is_castride_family(storage) &&
|
|
701
|
+
!ca_mv_stride_is_contig((CAStride *) storage)) {
|
|
704
702
|
return false;
|
|
705
703
|
}
|
|
706
704
|
}
|
|
707
705
|
|
|
708
|
-
|
|
706
|
+
/* Format string, resolved before shape / strides are allocated since
|
|
707
|
+
building it can raise. Primitive types use the top-level table;
|
|
708
|
+
CA_FIXLEN + data_class emits the record's format, cached on the class,
|
|
709
|
+
so the pointer is stable until the class is GC'd. */
|
|
710
|
+
if (ca->data_type == CA_FIXLEN && RTEST(rb_ca_has_data_class(obj))) {
|
|
711
|
+
VALUE klass = rb_ca_data_class(obj);
|
|
712
|
+
VALUE fmt = ca_mv_struct_format_for_data_class(klass);
|
|
713
|
+
format = RSTRING_PTR(fmt);
|
|
714
|
+
}
|
|
715
|
+
else if (ca->data_type == CA_FIXLEN) {
|
|
716
|
+
/* Plain CA_FIXLEN (no data_class): PEP 3118 "Ns" fixed-bytes. */
|
|
717
|
+
format = RSTRING_PTR(ca_mv_fixlen_format(ca->bytes));
|
|
718
|
+
}
|
|
719
|
+
else {
|
|
720
|
+
format = ca_mv_format_for(ca->data_type);
|
|
721
|
+
}
|
|
709
722
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
data_ptr =
|
|
715
|
-
if (ca->ndim >= 1) {
|
|
723
|
+
if (ca_is_entity(storage)) {
|
|
724
|
+
/* Entity / wrap / scalar: its own buffer, always valid. No attach
|
|
725
|
+
needed. A CScalar is exported with ndim 0 and no shape, so none is
|
|
726
|
+
taken: release frees only what the view carries. */
|
|
727
|
+
data_ptr = storage->ptr;
|
|
728
|
+
if (ca->ndim >= 1 && storage->obj_type != CA_OBJ_SCALAR) {
|
|
716
729
|
shape = ca_mv_alloc_shape(ca);
|
|
717
730
|
strides = ca_mv_alloc_strides_contiguous(ca, shape, true);
|
|
718
731
|
}
|
|
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: {
|
|
732
|
+
}
|
|
733
|
+
else {
|
|
734
|
+
/* A CAStride-family view (CARefer / CABlock / CAFarray / CATranspose
|
|
735
|
+
/ CARepeat / CAField / plain CAStride) composed to an entity.
|
|
736
|
+
Expose `entity->ptr + composed_base` as data_ptr and
|
|
737
|
+
composed_strides as strides, both in entity byte space. The entity
|
|
738
|
+
owns its bytes (always valid via view->obj's parent chain while the
|
|
739
|
+
consumer holds the view).
|
|
740
|
+
|
|
741
|
+
CAREFUL: do not ca_attach the view here (nor ca_sync / ca_detach at
|
|
742
|
+
release). Exposure is alias-only: the consumer writes directly into
|
|
743
|
+
root bytes, so a sync would scatter bytes back to root as a no-op
|
|
744
|
+
chain -- and that chain triggers ca_update_mask / ca_sync(mask)
|
|
745
|
+
recursion that can raise on an invariant violation. Raising from a
|
|
746
|
+
release callback is fatal during GC sweep (newobj_of during sweep ->
|
|
747
|
+
BUG). */
|
|
741
748
|
int8_t k;
|
|
742
749
|
shape = ca_mv_alloc_shape(ca);
|
|
743
750
|
strides = (ssize_t *) xmalloc(sizeof(ssize_t) * ca->ndim);
|
|
744
751
|
for (k = 0; k < ca->ndim; k++) strides[k] = (ssize_t) composed_strides[k];
|
|
745
752
|
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
753
|
}
|
|
764
754
|
|
|
765
755
|
view->obj = obj;
|
|
@@ -770,37 +760,11 @@ ca_mv_get (VALUE obj, rb_memory_view_t *view, int flags)
|
|
|
770
760
|
views compute Σ (shape[k]-1)*|strides[k]| + item_size themselves. */
|
|
771
761
|
view->byte_size = (ssize_t) ca_length(ca);
|
|
772
762
|
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
|
-
}
|
|
763
|
+
view->format = format;
|
|
800
764
|
view->item_size = (ssize_t) ca->bytes;
|
|
801
765
|
view->item_desc.components = NULL;
|
|
802
766
|
view->item_desc.length = 0;
|
|
803
|
-
view->ndim = (
|
|
767
|
+
view->ndim = (storage->obj_type == CA_OBJ_SCALAR) ? 0 : (ssize_t) ca->ndim;
|
|
804
768
|
view->shape = (view->ndim == 0) ? NULL : shape;
|
|
805
769
|
view->strides = (view->ndim == 0) ? NULL : strides;
|
|
806
770
|
view->sub_offsets = NULL;
|
|
@@ -1075,6 +1039,25 @@ ca_mv_extract_type_and_mask_kwargs (int argc, VALUE *argv,
|
|
|
1075
1039
|
return data_type;
|
|
1076
1040
|
}
|
|
1077
1041
|
|
|
1042
|
+
/* ca_mv_acquire_and_validate as an rb_protect body. */
|
|
1043
|
+
typedef struct {
|
|
1044
|
+
VALUE src;
|
|
1045
|
+
rb_memory_view_t *view;
|
|
1046
|
+
int8_t explicit_data_type;
|
|
1047
|
+
int8_t *data_type;
|
|
1048
|
+
int8_t *ndim;
|
|
1049
|
+
ca_size_t *dim;
|
|
1050
|
+
} ca_mv_acquire_args_t;
|
|
1051
|
+
|
|
1052
|
+
static VALUE
|
|
1053
|
+
ca_mv_acquire_body (VALUE arg)
|
|
1054
|
+
{
|
|
1055
|
+
ca_mv_acquire_args_t *a = (ca_mv_acquire_args_t *) arg;
|
|
1056
|
+
ca_mv_acquire_and_validate(a->src, a->view, a->explicit_data_type,
|
|
1057
|
+
a->data_type, a->ndim, a->dim);
|
|
1058
|
+
return Qnil;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1078
1061
|
/* Acquire and validate a mask MemoryView against the data side's shape.
|
|
1079
1062
|
Returns an allocated holder (caller owns; release via the imported
|
|
1080
1063
|
holder TypedData dfree path) with view active on success.
|
|
@@ -1255,8 +1238,21 @@ rb_ca_s_wrap_memory_view (int argc, VALUE *argv, VALUE klass)
|
|
|
1255
1238
|
holder = ALLOC(ca_mv_imported_holder_t);
|
|
1256
1239
|
holder->valid = false;
|
|
1257
1240
|
|
|
1258
|
-
|
|
1259
|
-
|
|
1241
|
+
/* Acquisition raises on a producer it cannot take (and releases the
|
|
1242
|
+
view itself first); the holder is not a Ruby object yet, so it is
|
|
1243
|
+
freed here. */
|
|
1244
|
+
{
|
|
1245
|
+
ca_mv_acquire_args_t a;
|
|
1246
|
+
int tag = 0;
|
|
1247
|
+
a.src = obj; a.view = &holder->view;
|
|
1248
|
+
a.explicit_data_type = explicit_data_type;
|
|
1249
|
+
a.data_type = &data_type; a.ndim = &ndim; a.dim = dim;
|
|
1250
|
+
rb_protect(ca_mv_acquire_body, (VALUE) &a, &tag);
|
|
1251
|
+
if ( tag ) {
|
|
1252
|
+
xfree(holder);
|
|
1253
|
+
rb_jump_tag(tag);
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1260
1256
|
holder->valid = true;
|
|
1261
1257
|
v = &holder->view;
|
|
1262
1258
|
|
|
@@ -1530,17 +1526,18 @@ rb_ca_s_memory_view_reject_reason (VALUE klass, VALUE obj)
|
|
|
1530
1526
|
}
|
|
1531
1527
|
if (! ca_mv_check_alias_chain(ca, &rej)) {
|
|
1532
1528
|
const char *where;
|
|
1533
|
-
if (rej.depth
|
|
1529
|
+
if (rej.depth < 0) where = "the root";
|
|
1530
|
+
else if (rej.depth == 0) where = "this view";
|
|
1534
1531
|
else if (rej.depth == 1) where = "parent";
|
|
1535
1532
|
else where = NULL; /* ancestor[N] */
|
|
1536
1533
|
if (where) {
|
|
1537
|
-
return rb_sprintf("%s is %s (%s); zero-copy wrap not possible
|
|
1538
|
-
|
|
1539
|
-
|
|
1534
|
+
return rb_sprintf("%s is %s (%s); zero-copy wrap not possible.%s",
|
|
1535
|
+
where, rej.class_name, rej.reason,
|
|
1536
|
+
rej.face ? "" : " Export arr.copy for a snapshot.");
|
|
1540
1537
|
} else {
|
|
1541
|
-
return rb_sprintf("ancestor[%d] is %s (%s); zero-copy wrap not possible
|
|
1542
|
-
|
|
1543
|
-
rej.
|
|
1538
|
+
return rb_sprintf("ancestor[%d] is %s (%s); zero-copy wrap not possible.%s",
|
|
1539
|
+
rej.depth - 1, rej.class_name, rej.reason,
|
|
1540
|
+
rej.face ? "" : " Export arr.copy for a snapshot.");
|
|
1544
1541
|
}
|
|
1545
1542
|
}
|
|
1546
1543
|
return Qnil;
|
|
@@ -1566,8 +1563,8 @@ rb_ca_s_parse_memory_view_format (VALUE klass, VALUE fmt_v, VALUE item_size_v)
|
|
|
1566
1563
|
|
|
1567
1564
|
/* Test hook: return the MV format string the producer would emit
|
|
1568
1565
|
for `obj`, without actually building the rb_memory_view_t. Used
|
|
1569
|
-
by spec_ai to verify
|
|
1570
|
-
|
|
1566
|
+
by spec_ai to verify the record format emitted for CAStruct arrays.
|
|
1567
|
+
Returns nil if obj isn't exportable. */
|
|
1571
1568
|
static VALUE
|
|
1572
1569
|
rb_ca_s_memory_view_format (VALUE klass, VALUE obj)
|
|
1573
1570
|
{
|
|
@@ -1578,24 +1575,30 @@ rb_ca_s_memory_view_format (VALUE klass, VALUE obj)
|
|
|
1578
1575
|
return ca_mv_struct_format_for_data_class(rb_ca_data_class(obj));
|
|
1579
1576
|
}
|
|
1580
1577
|
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;
|
|
1578
|
+
/* Plain CA_FIXLEN: the same "Ns" String the producer (ca_mv_get)
|
|
1579
|
+
hands out via view->format. */
|
|
1580
|
+
return ca_mv_fixlen_format(ca->bytes);
|
|
1594
1581
|
}
|
|
1595
1582
|
const char *fmt = ca_mv_format_for(ca->data_type);
|
|
1596
1583
|
return fmt ? rb_str_new_cstr(fmt) : Qnil;
|
|
1597
1584
|
}
|
|
1598
1585
|
|
|
1586
|
+
/* CARecord#memory_view_field_names: the names of the record's members,
|
|
1587
|
+
one per specifier of the record's MemoryView format, in that order.
|
|
1588
|
+
Ruby's format has no place for them, so a bridge that writes a format
|
|
1589
|
+
which does (PEP 3118 "T{...}") asks the exporting object for them under
|
|
1590
|
+
this name. nil when the record has no MemoryView format. */
|
|
1591
|
+
static VALUE
|
|
1592
|
+
rb_ca_record_memory_view_field_names (VALUE self)
|
|
1593
|
+
{
|
|
1594
|
+
VALUE data_class = rb_ca_data_class(self);
|
|
1595
|
+
if ( NIL_P(data_class)
|
|
1596
|
+
|| NIL_P(ca_mv_struct_format_for_data_class(data_class)) ) {
|
|
1597
|
+
return Qnil;
|
|
1598
|
+
}
|
|
1599
|
+
return rb_ary_dup(rb_const_get(data_class, rb_intern("MEMBERS")));
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1599
1602
|
/* ---------------- Init ---------------- */
|
|
1600
1603
|
|
|
1601
1604
|
static const rb_memory_view_entry_t ca_memory_view_entry = {
|
|
@@ -1647,4 +1650,7 @@ Init_carray_memory_view (void)
|
|
|
1647
1650
|
rb_ca_s_parse_memory_view_format, 2);
|
|
1648
1651
|
rb_define_singleton_method(rb_cCArray, "__memory_view_format__",
|
|
1649
1652
|
rb_ca_s_memory_view_format, 1);
|
|
1653
|
+
rb_define_method(rb_const_get(rb_cObject, rb_intern("CARecord")),
|
|
1654
|
+
"memory_view_field_names",
|
|
1655
|
+
rb_ca_record_memory_view_field_names, 0);
|
|
1650
1656
|
}
|