carray 3.0.3 → 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 +264 -0
- data/README.md +3 -2
- data/carray.gemspec +1 -1
- data/ext/ca_axis_descriptor.h +27 -24
- data/ext/ca_axis_dispatch.c +1 -6
- data/ext/ca_axis_group.c +27 -27
- data/ext/ca_binop_dispatch.c +1 -29
- data/ext/ca_binop_dispatch.h +0 -1
- data/ext/ca_categorical_iterator.c +205 -134
- data/ext/ca_compare.c +7 -18
- data/ext/ca_group_iter.c +16 -17
- data/ext/ca_kernel_iterator.c +1234 -1994
- data/ext/ca_kernel_iterator.h +391 -437
- data/ext/ca_monop_dispatch.c +82 -59
- data/ext/ca_monop_dispatch.h +1 -0
- data/ext/ca_obj_array.c +96 -39
- data/ext/ca_obj_bincmp.c +24 -21
- data/ext/ca_obj_binop.c +15 -26
- data/ext/ca_obj_bitarray.c +3 -2
- data/ext/ca_obj_bitfield.c +5 -6
- data/ext/ca_obj_block.c +18 -9
- data/ext/ca_obj_byte_swap.c +5 -6
- data/ext/ca_obj_const_string.c +173 -19
- data/ext/ca_obj_face.c +238 -57
- data/ext/ca_obj_face.h +19 -0
- data/ext/ca_obj_fake.c +15 -29
- data/ext/ca_obj_farray.c +6 -3
- data/ext/ca_obj_field.c +17 -5
- data/ext/ca_obj_fixlen_string.c +2 -3
- data/ext/ca_obj_grid.c +406 -132
- data/ext/ca_obj_meld.c +172 -34
- data/ext/ca_obj_moncmp.c +23 -8
- data/ext/ca_obj_monop.c +67 -57
- data/ext/ca_obj_object.c +157 -51
- data/ext/ca_obj_record.c +12 -7
- data/ext/ca_obj_reduce.c +13 -8
- data/ext/ca_obj_refer.c +25 -27
- data/ext/ca_obj_remap.c +30 -9
- data/ext/ca_obj_repeat.c +9 -10
- data/ext/ca_obj_roll.c +6 -5
- data/ext/ca_obj_select.c +14 -9
- data/ext/ca_obj_select_axis.c +15 -47
- data/ext/ca_obj_shift.c +11 -1
- data/ext/ca_obj_stack.c +179 -34
- data/ext/ca_obj_stride.c +162 -115
- data/ext/ca_obj_string.c +71 -4
- data/ext/ca_obj_tile.c +6 -5
- data/ext/ca_obj_time.c +22 -6
- data/ext/ca_obj_timedelta.c +22 -6
- data/ext/ca_obj_transpose.c +5 -3
- data/ext/ca_obj_triop.c +23 -32
- data/ext/ca_obj_window.c +190 -26
- data/ext/ca_sweep_engine.c +2 -2
- data/ext/caframe_csv_reader.c +80 -1
- data/ext/carray.h +181 -75
- data/ext/carray_access.c +184 -96
- data/ext/carray_address_basis.c +343 -87
- data/ext/carray_attribute.c +89 -6
- data/ext/carray_bincount.c +10 -8
- data/ext/carray_broadcast.c +13 -8
- data/ext/carray_build_flags.h +3 -0
- data/ext/carray_call_cfunc.c +108 -0
- data/ext/carray_cast.c +142 -24
- data/ext/carray_cast_func.rb +46 -6
- data/ext/carray_class.c +4 -3
- data/ext/carray_config.h +28 -0
- data/ext/carray_conversion.c +16 -7
- data/ext/carray_copy.c +51 -14
- data/ext/carray_core.c +147 -55
- data/ext/carray_count.c +50 -20
- data/ext/carray_element.c +76 -33
- data/ext/carray_factorize.c +118 -64
- data/ext/carray_generate.c +191 -50
- data/ext/carray_histogram.c +62 -17
- data/ext/carray_hold.c +12 -15
- data/ext/carray_index_classifier.c +388 -176
- data/ext/carray_internal.h +336 -29
- data/ext/carray_kernels_bincmp.c +4453 -0
- data/ext/carray_kernels_binop.c +11368 -0
- data/ext/carray_kernels_init.c +1734 -0
- data/ext/carray_kernels_moncmp.c +2104 -0
- data/ext/carray_kernels_monop.c +18369 -0
- data/ext/carray_kernels_reduce_aggregate.c +23818 -0
- data/ext/carray_kernels_reduce_boolean.c +406 -0
- data/ext/carray_kernels_reduce_cumulative.c +10086 -0
- data/ext/carray_kernels_reduce_extreme.c +17634 -0
- data/ext/carray_kernels_reduce_variance.c +3849 -0
- data/ext/carray_kernels_scan.c +4302 -0
- data/ext/carray_kernels_search.c +33829 -0
- data/ext/carray_kernels_sort.c +10906 -0
- data/ext/carray_kernels_triop.c +1864 -0
- data/ext/carray_key_windows.c +216 -0
- data/ext/carray_lazy.c +170 -111
- data/ext/carray_mask.c +292 -72
- data/ext/carray_median_percentile.c +82 -28
- data/ext/carray_memory_view.c +669 -561
- data/ext/carray_operator.c +124 -47
- data/ext/carray_order.c +384 -76
- data/ext/carray_partition.c +28 -27
- data/ext/carray_random.c +67 -13
- data/ext/carray_round.c +273 -0
- data/ext/carray_scatter.c +63 -38
- data/ext/carray_segment.c +3 -3
- data/ext/carray_slab.c +91 -28
- data/ext/carray_sort.c +35 -35
- data/ext/carray_struct.c +19 -18
- data/ext/carray_test.c +26 -5
- data/ext/carray_undef.c +4 -0
- data/ext/carray_utils.c +246 -45
- data/ext/mk_call_cfunc.rb +19 -2
- data/ext/mkkernel.rb +198 -240
- data/ext/ruby_carray.c +10 -4
- data/ext/version.h +4 -4
- data/lib/carray/attribute.rb +126 -22
- data/lib/carray/autoload_carray.rb +8 -31
- data/lib/carray/autoload_method_extension.rb +14 -5
- data/lib/carray/axis_group.rb +255 -106
- data/lib/carray/basics.rb +78 -10
- data/lib/carray/bincount_nd.rb +12 -2
- data/lib/carray/block_iterator.rb +126 -23
- data/lib/carray/boolean_reduce.rb +17 -17
- data/lib/carray/categorical.rb +165 -43
- data/lib/carray/categorical_iterator.rb +130 -27
- data/lib/carray/complex.rb +5 -5
- data/lib/carray/conditional.rb +67 -18
- data/lib/carray/const_string.rb +14 -5
- 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/convert.rb +69 -0
- data/lib/carray/frame/csv_parser.rb +111 -17
- data/lib/carray/frame/describe.rb +3 -2
- data/lib/carray/frame/frame.rb +246 -21
- data/lib/carray/frame/group.rb +510 -95
- data/lib/carray/frame/io.rb +115 -28
- data/lib/carray/frame/join.rb +312 -51
- data/lib/carray/frame/records.rb +83 -7
- data/lib/carray/frame/reshape.rb +42 -35
- data/lib/carray/frame/sort.rb +7 -19
- data/lib/carray/frame/verbs.rb +188 -48
- data/lib/carray/frame/windows.rb +117 -0
- data/lib/carray/frame.rb +2 -0
- data/lib/carray/fuse_source.rb +11 -4
- data/lib/carray/fusion.rb +40 -16
- data/lib/carray/histogram.rb +6 -1
- data/lib/carray/inspect.rb +31 -8
- data/lib/carray/iterator.rb +85 -7
- data/lib/carray/key_windows.rb +620 -0
- data/lib/carray/lazy.rb +182 -183
- data/lib/carray/mask_gap_fill.rb +63 -57
- data/lib/carray/meld_reduce.rb +12 -14
- data/lib/carray/methods/bin.rb +26 -18
- data/lib/carray/methods/bincount.rb +16 -0
- data/lib/carray/methods/bit_string.rb +1 -1
- data/lib/carray/methods/choose.rb +16 -3
- data/lib/carray/methods/composition.rb +3 -1
- data/lib/carray/methods/gather_nd.rb +14 -5
- data/lib/carray/methods/insert_block.rb +2 -2
- data/lib/carray/methods/is_between.rb +59 -0
- data/lib/carray/methods/is_in.rb +53 -36
- data/lib/carray/methods/join.rb +1 -6
- data/lib/carray/methods/lag.rb +49 -0
- data/lib/carray/methods/locate_addr.rb +47 -51
- data/lib/carray/methods/mode.rb +1 -1
- data/lib/carray/methods/nunique.rb +11 -6
- data/lib/carray/methods/resize.rb +3 -3
- data/lib/carray/methods/value_counts.rb +4 -11
- data/lib/carray/record_order.rb +3 -3
- data/lib/carray/rng.rb +3 -0
- data/lib/carray/runtime.rb +53 -8
- data/lib/carray/segment_iterator.rb +313 -82
- data/lib/carray/serialize.rb +24 -10
- data/lib/carray/slab_iterator.rb +50 -0
- data/lib/carray/string.rb +105 -5
- data/lib/carray/string_operation_extension.rb +8 -1
- data/lib/carray/struct.rb +9 -21
- data/lib/carray/struct_builder.rb +19 -8
- data/lib/carray/time.rb +726 -104
- data/lib/carray/window_iterator.rb +122 -16
- data/lib/carray.rb +7 -0
- data/yard-stubs/ca_obj_bitfield.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +4 -0
- data/yard-stubs/ca_obj_grid.rb +21 -10
- data/yard-stubs/ca_obj_refer.rb +2 -1
- data/yard-stubs/ca_obj_roll.rb +2 -2
- data/yard-stubs/ca_obj_shift.rb +5 -1
- data/yard-stubs/ca_obj_tile.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +8 -3
- data/yard-stubs/carray_access.rb +6 -1
- data/yard-stubs/carray_attribute.rb +0 -5
- data/yard-stubs/carray_cast.rb +10 -0
- data/yard-stubs/carray_conversion.rb +2 -1
- data/yard-stubs/carray_copy.rb +4 -2
- data/yard-stubs/carray_count.rb +4 -4
- data/yard-stubs/carray_element.rb +10 -4
- data/yard-stubs/carray_generate.rb +21 -1
- data/yard-stubs/carray_mask.rb +26 -2
- data/yard-stubs/carray_math.rb +27 -1
- data/yard-stubs/carray_median_percentile.rb +5 -2
- data/yard-stubs/carray_memory_view.rb +40 -23
- data/yard-stubs/carray_order.rb +29 -18
- data/yard-stubs/carray_random.rb +13 -4
- data/yard-stubs/carray_scatter.rb +14 -9
- data/yard-stubs/carray_slab.rb +9 -2
- data/yard-stubs/carray_sort.rb +6 -5
- data/yard-stubs/carray_test.rb +5 -3
- metadata +25 -3
- data/lib/carray/arrow_tensor.rb +0 -401
- data/lib/carray/table.rb +0 -108
data/ext/carray_memory_view.c
CHANGED
|
@@ -31,10 +31,14 @@
|
|
|
31
31
|
#include "ca_obj_face.h" /* ca_strip_face for Face producers */
|
|
32
32
|
#include "carray_internal.h"
|
|
33
33
|
#include "ruby/memory_view.h"
|
|
34
|
+
#include "ruby/st.h"
|
|
35
|
+
#include "ruby/thread_native.h"
|
|
36
|
+
#include <ctype.h>
|
|
34
37
|
#include <string.h>
|
|
35
38
|
|
|
36
|
-
/* private_data
|
|
37
|
-
|
|
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. */
|
|
38
42
|
|
|
39
43
|
/* ---------------- forward declarations for non-public structs ----------------
|
|
40
44
|
CABlock, CARefer, CARepeat are declared in carray.h. CAFarray
|
|
@@ -79,8 +83,6 @@ typedef enum {
|
|
|
79
83
|
static ca_mv_runtime_type_t ca_mv_runtime_types[CA_MV_NUM_RUNTIME_OBJ_TYPES] = {
|
|
80
84
|
{ "CAFarray", "CA_OBJ_FARRAY", -1, Qnil, /* CA_MV_FARRAY */ 4 },
|
|
81
85
|
{ "CATranspose", "CA_OBJ_TRANSPOSE", -1, Qnil, /* CA_MV_TRANS */ 5 },
|
|
82
|
-
/* CAMapping is retired; a[mapper] now builds a CAGrid/CAStride chain
|
|
83
|
-
whose layers are covered above. */
|
|
84
86
|
{ "CAGrid", "CA_OBJ_GRID", -1, Qnil, /* CA_MV_ATTACH */ 7 },
|
|
85
87
|
{ "CAShift", "CA_OBJ_SHIFT", -1, Qnil, /* CA_MV_ATTACH */ 7 },
|
|
86
88
|
{ "CAWindow", "CA_OBJ_WINDOW", -1, Qnil, /* CA_MV_ATTACH */ 7 },
|
|
@@ -200,8 +202,9 @@ ca_mv_record_member_spec (VALUE info, char *out, size_t outlen,
|
|
|
200
202
|
a member it has no item for (see above), or members that overlap or come
|
|
201
203
|
out of offset order (a union).
|
|
202
204
|
|
|
203
|
-
The result is frozen and cached on the data_class
|
|
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
|
{
|
|
@@ -277,33 +280,80 @@ ca_mv_ca_exportable (CArray *ca, VALUE obj)
|
|
|
277
280
|
}
|
|
278
281
|
|
|
279
282
|
/* Reverse mapping for the importer (Postel consumer; see
|
|
280
|
-
docs/interop/MemoryViewFormat.md for the contract)
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
-
|
|
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.
|
|
288
291
|
- Synonyms accepted but not emitted: 'C'/1 maps to UINT8 (so other
|
|
289
292
|
producers' bool buffers are received as UINT8); 'ff' / 'dd' map to
|
|
290
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
|
+
|
|
291
308
|
static int8_t
|
|
292
309
|
ca_mv_data_type_from_format (const char *format, ssize_t item_size)
|
|
293
310
|
{
|
|
311
|
+
const int host = (ca_endian == CA_LITTLE_ENDIAN) ? CA_MV_ORDER_LITTLE
|
|
312
|
+
: CA_MV_ORDER_BIG;
|
|
294
313
|
const char *fmt = format ? format : "";
|
|
295
|
-
|
|
314
|
+
char buf[16];
|
|
315
|
+
size_t len;
|
|
316
|
+
int order = CA_MV_ORDER_NONE;
|
|
317
|
+
int one_byte;
|
|
318
|
+
|
|
296
319
|
if (*fmt == '|') fmt++;
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
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;
|
|
305
350
|
}
|
|
306
|
-
|
|
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;
|
|
355
|
+
}
|
|
356
|
+
fmt = buf;
|
|
307
357
|
|
|
308
358
|
/* single-byte formats */
|
|
309
359
|
if (strcmp(fmt, "c") == 0 && item_size == 1) return CA_INT8;
|
|
@@ -316,24 +366,32 @@ ca_mv_data_type_from_format (const char *format, ssize_t item_size)
|
|
|
316
366
|
if ((strcmp(fmt, "s") == 0 || strcmp(fmt, "s!") == 0 ||
|
|
317
367
|
strcmp(fmt, "h") == 0) && item_size == 2) return CA_INT16;
|
|
318
368
|
if ((strcmp(fmt, "S") == 0 || strcmp(fmt, "S!") == 0 ||
|
|
319
|
-
strcmp(fmt, "H") == 0
|
|
369
|
+
strcmp(fmt, "H") == 0 || strcmp(fmt, "v") == 0 ||
|
|
370
|
+
strcmp(fmt, "n") == 0) && item_size == 2) return CA_UINT16;
|
|
320
371
|
|
|
321
372
|
/* 32-bit */
|
|
322
373
|
if ((strcmp(fmt, "l") == 0 || strcmp(fmt, "i") == 0 ||
|
|
323
|
-
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;
|
|
324
376
|
if ((strcmp(fmt, "L") == 0 || strcmp(fmt, "I") == 0 ||
|
|
325
|
-
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;
|
|
326
380
|
|
|
327
381
|
/* 64-bit. `l`/`L` at item_size 8 accepted as LP64 native long
|
|
328
382
|
(numpy default for `np.array([1,2,3]).dtype == int64`). */
|
|
329
383
|
if ((strcmp(fmt, "q") == 0 || strcmp(fmt, "q!") == 0 ||
|
|
330
|
-
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;
|
|
331
386
|
if ((strcmp(fmt, "Q") == 0 || strcmp(fmt, "Q!") == 0 ||
|
|
332
|
-
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;
|
|
333
389
|
|
|
334
|
-
/* floats
|
|
335
|
-
if (strcmp(fmt, "f") == 0
|
|
336
|
-
|
|
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;
|
|
337
395
|
|
|
338
396
|
/* complex: PEP 3118 'Zf' / 'Zd' canonical. */
|
|
339
397
|
if (strcmp(fmt, "Zf") == 0 && item_size == 8) return CA_CMPLX64;
|
|
@@ -631,26 +689,36 @@ ca_mv_available_p (VALUE obj)
|
|
|
631
689
|
return ca_mv_check_alias_chain(ca, &rej);
|
|
632
690
|
}
|
|
633
691
|
|
|
634
|
-
/* "Ns" format for a plain CA_FIXLEN of N bytes. The
|
|
635
|
-
a table keyed by N for the life of the process, so the pointer stays
|
|
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
|
|
636
694
|
valid for any view, and a frozen array (which cannot take an ivar) is
|
|
637
|
-
exported like any other.
|
|
638
|
-
|
|
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;
|
|
639
699
|
|
|
640
|
-
static
|
|
700
|
+
static const char *
|
|
641
701
|
ca_mv_fixlen_format (ca_size_t bytes)
|
|
642
702
|
{
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
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;
|
|
648
717
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
fmt = rb_obj_freeze(rb_sprintf("%lds", (long) bytes));
|
|
652
|
-
rb_hash_aset(ca_mv_fixlen_formats, key, fmt);
|
|
718
|
+
else {
|
|
719
|
+
st_insert(ca_mv_fixlen_formats, (st_data_t) bytes, (st_data_t) fmt);
|
|
653
720
|
}
|
|
721
|
+
rb_nativethread_lock_unlock(&ca_mv_fixlen_formats_lock);
|
|
654
722
|
return fmt;
|
|
655
723
|
}
|
|
656
724
|
|
|
@@ -663,6 +731,7 @@ ca_mv_get (VALUE obj, rb_memory_view_t *view, int flags)
|
|
|
663
731
|
ssize_t *strides = NULL;
|
|
664
732
|
void *data_ptr = NULL;
|
|
665
733
|
const char *format;
|
|
734
|
+
char *record_format = NULL;
|
|
666
735
|
int writable_request;
|
|
667
736
|
|
|
668
737
|
if (! ca_mv_extract(obj, &ca)) return false;
|
|
@@ -705,16 +774,22 @@ ca_mv_get (VALUE obj, rb_memory_view_t *view, int flags)
|
|
|
705
774
|
|
|
706
775
|
/* Format string, resolved before shape / strides are allocated since
|
|
707
776
|
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
|
-
|
|
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. */
|
|
710
780
|
if (ca->data_type == CA_FIXLEN && RTEST(rb_ca_has_data_class(obj))) {
|
|
711
781
|
VALUE klass = rb_ca_data_class(obj);
|
|
712
782
|
VALUE fmt = ca_mv_struct_format_for_data_class(klass);
|
|
713
|
-
|
|
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;
|
|
714
789
|
}
|
|
715
790
|
else if (ca->data_type == CA_FIXLEN) {
|
|
716
791
|
/* Plain CA_FIXLEN (no data_class): PEP 3118 "Ns" fixed-bytes. */
|
|
717
|
-
format =
|
|
792
|
+
format = ca_mv_fixlen_format(ca->bytes);
|
|
718
793
|
}
|
|
719
794
|
else {
|
|
720
795
|
format = ca_mv_format_for(ca->data_type);
|
|
@@ -768,7 +843,7 @@ ca_mv_get (VALUE obj, rb_memory_view_t *view, int flags)
|
|
|
768
843
|
view->shape = (view->ndim == 0) ? NULL : shape;
|
|
769
844
|
view->strides = (view->ndim == 0) ? NULL : strides;
|
|
770
845
|
view->sub_offsets = NULL;
|
|
771
|
-
view->private_data =
|
|
846
|
+
view->private_data = record_format;
|
|
772
847
|
|
|
773
848
|
/* Enforce caller's layout request. */
|
|
774
849
|
{
|
|
@@ -791,6 +866,11 @@ reject:
|
|
|
791
866
|
return false; there is no attach state to unwind. */
|
|
792
867
|
if (shape) { xfree(shape); view->shape = NULL; }
|
|
793
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
|
+
}
|
|
794
874
|
return false;
|
|
795
875
|
}
|
|
796
876
|
|
|
@@ -812,7 +892,10 @@ ca_mv_release (VALUE obj, rb_memory_view_t *view)
|
|
|
812
892
|
ca_sync(ca->mask) recursion and raise; raising from a release
|
|
813
893
|
callback is fatal during GC sweep. See ca_mv_get's CAStride-family
|
|
814
894
|
case comment for the full rationale. */
|
|
815
|
-
view->private_data
|
|
895
|
+
if (view->private_data) {
|
|
896
|
+
xfree(view->private_data);
|
|
897
|
+
view->private_data = NULL;
|
|
898
|
+
}
|
|
816
899
|
return true;
|
|
817
900
|
}
|
|
818
901
|
|
|
@@ -831,9 +914,25 @@ ca_mv_release (VALUE obj, rb_memory_view_t *view)
|
|
|
831
914
|
a TypedData holder (also stored as an ivar) releases the view in its
|
|
832
915
|
dfree callback when the CAWrap is collected. */
|
|
833
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. */
|
|
834
927
|
typedef struct {
|
|
835
928
|
rb_memory_view_t view;
|
|
836
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;
|
|
837
936
|
} ca_mv_imported_holder_t;
|
|
838
937
|
|
|
839
938
|
static void
|
|
@@ -841,8 +940,21 @@ ca_mv_imported_holder_free (void *p)
|
|
|
841
940
|
{
|
|
842
941
|
ca_mv_imported_holder_t *h = (ca_mv_imported_holder_t *) p;
|
|
843
942
|
if (h->valid) {
|
|
844
|
-
rb_memory_view_release(&h->view);
|
|
845
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
|
+
}
|
|
846
958
|
}
|
|
847
959
|
xfree(h);
|
|
848
960
|
}
|
|
@@ -864,149 +976,311 @@ static const rb_data_type_t ca_mv_imported_holder_data_type = {
|
|
|
864
976
|
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
|
865
977
|
};
|
|
866
978
|
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
producers that advertise only contiguous still work. On any
|
|
870
|
-
failure, releases the view (if acquired) and raises ArgumentError;
|
|
871
|
-
on success, the view is left active and the caller owns the
|
|
872
|
-
responsibility to release it.
|
|
873
|
-
|
|
874
|
-
`explicit_data_type` is the caller-supplied target data_type (>= 0) or -1
|
|
875
|
-
if the caller has no preference. Behaviour:
|
|
876
|
-
- explicit_data_type < 0, format != NULL: derive data_type from format.
|
|
877
|
-
- explicit_data_type < 0, format == NULL: reject (typeless source
|
|
878
|
-
without consumer-supplied type cannot be interpreted).
|
|
879
|
-
- explicit_data_type >= 0, format != NULL: must match the format's
|
|
880
|
-
data_type; mismatch -> reject.
|
|
881
|
-
- explicit_data_type >= 0, format == NULL: use explicit_data_type; the
|
|
882
|
-
byte buffer is reinterpreted as that data_type. ndim is forced
|
|
883
|
-
to 1 (multi-dim from a typeless source is ambiguous; user
|
|
884
|
-
chains .reshape).
|
|
885
|
-
*/
|
|
886
|
-
/* Row-major contiguity test that tolerates a NULL shape array. ndim-0
|
|
887
|
-
scalars and SIMPLE-form producers (which omit the shape array for a
|
|
888
|
-
contiguous 1-D buffer, e.g. red-arrow's primitive arrays) are
|
|
889
|
-
contiguous by construction; rb_memory_view_is_row_major_contiguous
|
|
890
|
-
dereferences view->shape, so it must not be reached with shape NULL. */
|
|
891
|
-
static bool
|
|
892
|
-
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)
|
|
893
981
|
{
|
|
894
|
-
|
|
895
|
-
|
|
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);
|
|
896
995
|
}
|
|
897
|
-
return rb_memory_view_is_row_major_contiguous(view);
|
|
898
996
|
}
|
|
899
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. */
|
|
900
1002
|
static void
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
int8_t *out_data_type, int8_t *out_ndim,
|
|
904
|
-
ca_size_t *out_dim)
|
|
1003
|
+
ca_mv_holder_acquire (ca_mv_imported_holder_t *h, VALUE src, int writable,
|
|
1004
|
+
const char *who)
|
|
905
1005
|
{
|
|
906
|
-
|
|
907
|
-
|
|
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
|
+
}
|
|
908
1029
|
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
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;
|
|
916
1043
|
|
|
917
|
-
|
|
918
|
-
|
|
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 ) {
|
|
919
1056
|
rb_raise(rb_eArgError,
|
|
920
|
-
"
|
|
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];
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
h->elements = ca_shape_elements(h->ndim, h->dim, (ca_size_t) item_size);
|
|
1074
|
+
|
|
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
|
+
}
|
|
921
1107
|
}
|
|
922
1108
|
}
|
|
923
1109
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
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 ) {
|
|
930
1139
|
rb_raise(rb_eArgError,
|
|
931
1140
|
"typeless MemoryView (format=NULL, %ld bytes); "
|
|
932
1141
|
"specify the target data_type via data_type: kwarg or use a "
|
|
933
1142
|
"concrete CArray::<Dtype> factory class",
|
|
934
|
-
(long)
|
|
1143
|
+
(long) v->byte_size);
|
|
935
1144
|
}
|
|
936
|
-
if (ca_mv_format_for(
|
|
937
|
-
rb_memory_view_release(view);
|
|
1145
|
+
if ( ca_mv_format_for(data_type) == NULL ) {
|
|
938
1146
|
rb_raise(rb_eArgError,
|
|
939
1147
|
"target data_type is not representable via MemoryView");
|
|
940
1148
|
}
|
|
941
|
-
|
|
942
|
-
if (
|
|
943
|
-
|
|
944
|
-
|
|
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 ) {
|
|
945
1157
|
rb_raise(rb_eArgError,
|
|
946
1158
|
"byte_size %ld is not a multiple of target item_size %ld",
|
|
947
|
-
(long)
|
|
1159
|
+
(long) v->byte_size, (long) target);
|
|
948
1160
|
}
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
/* If the caller specified a data_type too, it must match. */
|
|
968
|
-
if (explicit_data_type >= 0 && explicit_data_type != data_type) {
|
|
969
|
-
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 ) {
|
|
970
1177
|
rb_raise(rb_eArgError,
|
|
971
1178
|
"explicit data_type does not match producer format "
|
|
972
|
-
"(producer: %s, requested data_type %
|
|
973
|
-
|
|
1179
|
+
"(producer: %s, requested data_type %s)",
|
|
1180
|
+
v->format, ca_type_name[explicit_data_type]);
|
|
974
1181
|
}
|
|
1182
|
+
ca_mv_holder_read_geometry(h, v->item_size, "");
|
|
1183
|
+
return data_type;
|
|
1184
|
+
}
|
|
975
1185
|
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
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;
|
|
1194
|
+
|
|
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
|
+
}
|
|
979
1202
|
}
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
if (view->ndim > 1) {
|
|
994
|
-
long got = (long) view->ndim;
|
|
995
|
-
rb_memory_view_release(view);
|
|
996
|
-
rb_raise(rb_eArgError,
|
|
997
|
-
"MemoryView reports ndim %ld but provides no shape array",
|
|
998
|
-
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]);
|
|
999
1216
|
}
|
|
1000
|
-
*out_ndim = 1;
|
|
1001
|
-
out_dim[0] = (ca_size_t) (view->item_size ? view->byte_size / view->item_size : 0);
|
|
1002
1217
|
}
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
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];
|
|
1007
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);
|
|
1008
1283
|
}
|
|
1009
|
-
*out_data_type = data_type;
|
|
1010
1284
|
}
|
|
1011
1285
|
|
|
1012
1286
|
/* Extract the optional data_type: and mask: keywords from argv.
|
|
@@ -1039,453 +1313,284 @@ ca_mv_extract_type_and_mask_kwargs (int argc, VALUE *argv,
|
|
|
1039
1313
|
return data_type;
|
|
1040
1314
|
}
|
|
1041
1315
|
|
|
1042
|
-
/* ca_mv_acquire_and_validate as an rb_protect body. */
|
|
1043
1316
|
typedef struct {
|
|
1044
|
-
VALUE
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
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. */
|
|
1052
1326
|
static VALUE
|
|
1053
|
-
|
|
1327
|
+
ca_mv_wrap_body (VALUE arg)
|
|
1054
1328
|
{
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
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;
|
|
1060
1336
|
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
On validation failure: releases the mask view, frees the mask holder,
|
|
1066
|
-
AND ALSO releases the supplied `data_view_to_release_on_fail` + frees
|
|
1067
|
-
the supplied `data_holder_to_free_on_fail` before raising. This
|
|
1068
|
-
avoids leaking the caller's already-acquired data side when mask
|
|
1069
|
-
validation aborts. Pass NULL for the data cleanup args if not
|
|
1070
|
-
applicable (e.g. mask validated for a stack-allocated data view in
|
|
1071
|
-
the copy path). */
|
|
1072
|
-
static ca_mv_imported_holder_t *
|
|
1073
|
-
ca_mv_acquire_mask_view (VALUE mask_src, int8_t data_ndim,
|
|
1074
|
-
const ca_size_t *data_dim,
|
|
1075
|
-
ca_mv_imported_holder_t *data_holder_to_free_on_fail,
|
|
1076
|
-
rb_memory_view_t *data_view_to_release_on_fail)
|
|
1077
|
-
{
|
|
1078
|
-
ca_mv_imported_holder_t *holder;
|
|
1079
|
-
rb_memory_view_t *mv;
|
|
1080
|
-
int i;
|
|
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);
|
|
1081
1340
|
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
if (data_holder_to_free_on_fail) { \
|
|
1091
|
-
data_holder_to_free_on_fail->valid = false; \
|
|
1092
|
-
xfree(data_holder_to_free_on_fail); \
|
|
1093
|
-
} \
|
|
1094
|
-
rb_raise(rb_eArgError, msg, ##__VA_ARGS__); \
|
|
1095
|
-
} while (0)
|
|
1096
|
-
|
|
1097
|
-
holder = ALLOC(ca_mv_imported_holder_t);
|
|
1098
|
-
holder->valid = false;
|
|
1099
|
-
|
|
1100
|
-
/* Zero-init before get: same apache/arrow#45187 defense as the data
|
|
1101
|
-
path (release frees view->item_desc.components unconditionally). */
|
|
1102
|
-
memset(&holder->view, 0, sizeof(holder->view));
|
|
1103
|
-
|
|
1104
|
-
if (! rb_memory_view_get(mask_src, &holder->view, RUBY_MEMORY_VIEW_STRIDES)) {
|
|
1105
|
-
if (! rb_memory_view_get(mask_src, &holder->view, RUBY_MEMORY_VIEW_SIMPLE)) {
|
|
1106
|
-
MV_MASK_CLEANUP_AND_RAISE("mask: object does not support MemoryView");
|
|
1107
|
-
}
|
|
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));
|
|
1108
1349
|
}
|
|
1109
|
-
holder->valid = true;
|
|
1110
|
-
mv = &holder->view;
|
|
1111
1350
|
|
|
1112
|
-
if (
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
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");
|
|
1117
1358
|
}
|
|
1118
|
-
/*
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
char
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
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
|
+
}
|
|
1126
1369
|
}
|
|
1370
|
+
ca_mv_check_bits(m, "mask: ");
|
|
1127
1371
|
}
|
|
1128
|
-
if (
|
|
1129
|
-
|
|
1130
|
-
MV_MASK_CLEANUP_AND_RAISE("mask: item_size must be 1 (got %ld)",
|
|
1131
|
-
(long) got);
|
|
1132
|
-
}
|
|
1133
|
-
if (mv->ndim != data_ndim) {
|
|
1134
|
-
int got = (int) mv->ndim;
|
|
1135
|
-
MV_MASK_CLEANUP_AND_RAISE("mask: ndim (%d) does not match data ndim (%d)",
|
|
1136
|
-
got, (int) data_ndim);
|
|
1137
|
-
}
|
|
1138
|
-
if (mv->shape == NULL) {
|
|
1139
|
-
/* SIMPLE-form mask (contiguous 1-D, shape omitted). item_size is 1
|
|
1140
|
-
(checked above), so byte_size is the element count. */
|
|
1141
|
-
ca_size_t mlen = (ca_size_t) mv->byte_size;
|
|
1142
|
-
if (data_ndim != 1 || mlen != data_dim[0]) {
|
|
1143
|
-
long ms = (long) mlen, ds = (long) (data_ndim == 1 ? data_dim[0] : -1);
|
|
1144
|
-
MV_MASK_CLEANUP_AND_RAISE(
|
|
1145
|
-
"mask: length (%ld) does not match data shape (%ld)", ms, ds);
|
|
1146
|
-
}
|
|
1372
|
+
if ( data_type == CA_BOOLEAN ) {
|
|
1373
|
+
ca_mv_check_bits(d, "");
|
|
1147
1374
|
}
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
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);
|
|
1156
1394
|
}
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
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;
|
|
1162
1404
|
}
|
|
1163
1405
|
|
|
1164
|
-
|
|
1165
|
-
|
|
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;
|
|
1166
1435
|
}
|
|
1167
1436
|
|
|
1168
|
-
/* Copy a strided MemoryView into a contiguous row-major destination buffer.
|
|
1169
|
-
Walks indices in row-major order and reads from view->data using
|
|
1170
|
-
view->strides at each step. Assumes view->ndim >= 1, view->strides
|
|
1171
|
-
non-NULL, dst sized to hold elements * item_size. */
|
|
1172
1437
|
static void
|
|
1173
|
-
|
|
1174
|
-
ca_size_t elements)
|
|
1438
|
+
ca_mv_import_args_init (ca_mv_import_args_t *a, VALUE obj, VALUE mask_obj)
|
|
1175
1439
|
{
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
for (n = 0; n < elements; n++) {
|
|
1185
|
-
ssize_t src_off = 0;
|
|
1186
|
-
for (k = 0; k < ndim; k++) {
|
|
1187
|
-
src_off += (ssize_t) idx[k] * view->strides[k];
|
|
1188
|
-
}
|
|
1189
|
-
memcpy(dst, (const char *) view->data + src_off, (size_t) item_size);
|
|
1190
|
-
dst += item_size;
|
|
1191
|
-
/* increment row-major: last dim varies fastest */
|
|
1192
|
-
for (k = ndim - 1; k >= 0; k--) {
|
|
1193
|
-
idx[k]++;
|
|
1194
|
-
if (idx[k] < (ca_size_t) view->shape[k]) break;
|
|
1195
|
-
idx[k] = 0;
|
|
1196
|
-
}
|
|
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);
|
|
1197
1447
|
}
|
|
1198
1448
|
}
|
|
1199
1449
|
|
|
1200
|
-
/*
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
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.
|
|
1205
1483
|
|
|
1206
1484
|
When the producer is typeless (format=NULL), `data_type:` must be
|
|
1207
1485
|
supplied; the byte buffer is reinterpreted as that data_type, ndim=1.
|
|
1208
1486
|
|
|
1209
1487
|
The receiver picks the class of the result. Called on CArray itself
|
|
1210
|
-
it builds a CAWrap
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
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. */
|
|
1215
1493
|
static VALUE
|
|
1216
1494
|
rb_ca_s_wrap_memory_view (int argc, VALUE *argv, VALUE klass)
|
|
1217
1495
|
{
|
|
1218
|
-
|
|
1219
|
-
ca_mv_imported_holder_t *mask_holder = NULL;
|
|
1220
|
-
rb_memory_view_t *v;
|
|
1221
|
-
int8_t data_type, ndim;
|
|
1222
|
-
ca_size_t dim[CA_RANK_MAX];
|
|
1223
|
-
VALUE wrap, holder_obj, obj, mask_obj, target_class;
|
|
1496
|
+
VALUE obj, mask_obj, target_class;
|
|
1224
1497
|
int8_t explicit_data_type =
|
|
1225
1498
|
ca_mv_extract_type_and_mask_kwargs(argc, argv,
|
|
1226
1499
|
(VALUE *) &obj, (VALUE *) &mask_obj);
|
|
1227
1500
|
|
|
1228
|
-
/* Resolve the class before anything is acquired, so this rejection
|
|
1229
|
-
needs no cleanup. CArray means CAWrap; any other receiver has to
|
|
1230
|
-
be a CAWrap the caller can actually be handed. */
|
|
1231
1501
|
target_class = ( klass == rb_cCArray ) ? rb_cCAWrap : klass;
|
|
1232
1502
|
if ( ! RTEST(rb_class_inherited_p(target_class, rb_cCAWrap)) ) {
|
|
1233
1503
|
rb_raise(rb_eTypeError,
|
|
1234
1504
|
"wrap_memory_view builds a CAWrap; %s is not a subclass of it",
|
|
1235
1505
|
rb_class2name(target_class));
|
|
1236
1506
|
}
|
|
1507
|
+
return ca_mv_wrap(obj, mask_obj, target_class, explicit_data_type, -1, 0);
|
|
1508
|
+
}
|
|
1237
1509
|
|
|
1238
|
-
|
|
1239
|
-
|
|
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;
|
|
1240
1522
|
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
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
|
-
}
|
|
1256
|
-
holder->valid = true;
|
|
1257
|
-
v = &holder->view;
|
|
1258
|
-
|
|
1259
|
-
/* Whether the producer is strided is only knowable once the view is
|
|
1260
|
-
acquired, and a strided one is returned as a CAStride wrapping an
|
|
1261
|
-
inner CAWrap -- so a caller who asked for a class would get back
|
|
1262
|
-
something that is not it. Refuse instead, and release first: this
|
|
1263
|
-
runs before the mask is acquired, so the holder is all there is to
|
|
1264
|
-
free. */
|
|
1265
|
-
if ( target_class != rb_cCAWrap &&
|
|
1266
|
-
v->ndim >= 1 && v->format != NULL && ! ca_mv_view_is_contiguous(v) ) {
|
|
1267
|
-
rb_memory_view_release(v);
|
|
1268
|
-
holder->valid = false;
|
|
1269
|
-
xfree(holder);
|
|
1270
|
-
rb_raise(rb_eArgError,
|
|
1271
|
-
"%s was requested, but this producer is strided, so the result "
|
|
1272
|
-
"would be a CAStride; pass a row-major contiguous producer, or "
|
|
1273
|
-
"use CArray.from_memory_view for a copy",
|
|
1274
|
-
rb_class2name(target_class));
|
|
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);
|
|
1275
1526
|
}
|
|
1276
1527
|
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
if (! NIL_P(mask_obj)) {
|
|
1281
|
-
mask_holder = ca_mv_acquire_mask_view(mask_obj, ndim, dim,
|
|
1282
|
-
holder, &holder->view);
|
|
1283
|
-
/* Strided data side combined with mask: is not supported in this
|
|
1284
|
-
release. Catch this AFTER mask acquisition so the cleanup path can release
|
|
1285
|
-
both views uniformly. */
|
|
1286
|
-
if (v->ndim >= 1 && v->format != NULL &&
|
|
1287
|
-
! ca_mv_view_is_contiguous(v)) {
|
|
1288
|
-
rb_memory_view_release(&mask_holder->view);
|
|
1289
|
-
mask_holder->valid = false;
|
|
1290
|
-
xfree(mask_holder);
|
|
1291
|
-
rb_memory_view_release(v);
|
|
1292
|
-
holder->valid = false;
|
|
1293
|
-
xfree(holder);
|
|
1294
|
-
rb_raise(rb_eArgError,
|
|
1295
|
-
"mask: combining mask: with a strided data source is not "
|
|
1296
|
-
"supported in this release; pass a row-major contiguous "
|
|
1297
|
-
"data source");
|
|
1298
|
-
}
|
|
1299
|
-
}
|
|
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);
|
|
1300
1531
|
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
lifecycle (memory_view_source + memory_view_holder ivars); the
|
|
1304
|
-
outer CAStride is what the user sees, with full strides
|
|
1305
|
-
(including negative) preserved. */
|
|
1306
|
-
if (v->ndim >= 1 && v->format != NULL &&
|
|
1307
|
-
! ca_mv_view_is_contiguous(v)) {
|
|
1308
|
-
ca_size_t inner_dim[1] = { 1 };
|
|
1309
|
-
volatile VALUE inner_wrap;
|
|
1310
|
-
ca_size_t strides_arr[CA_RANK_MAX];
|
|
1311
|
-
int i;
|
|
1312
|
-
|
|
1313
|
-
inner_wrap = rb_ca_wrap_new(data_type, 1, inner_dim,
|
|
1314
|
-
ca_mv_carray_bytes_for(data_type, v->item_size),
|
|
1315
|
-
NULL, (char *) v->data);
|
|
1316
|
-
rb_ivar_set(inner_wrap, rb_intern("memory_view_source"), obj);
|
|
1317
|
-
holder_obj = TypedData_Wrap_Struct(rb_cObject,
|
|
1318
|
-
&ca_mv_imported_holder_data_type,
|
|
1319
|
-
holder);
|
|
1320
|
-
rb_ivar_set(inner_wrap, rb_intern("memory_view_holder"), holder_obj);
|
|
1321
|
-
if (v->readonly) {
|
|
1322
|
-
CArray *iw;
|
|
1323
|
-
if (ca_mv_extract(inner_wrap, &iw)) {
|
|
1324
|
-
ca_set_flag(iw, CA_FLAG_READ_ONLY);
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1327
|
-
for (i = 0; i < ndim; i++) {
|
|
1328
|
-
strides_arr[i] = (ca_size_t) v->strides[i];
|
|
1329
|
-
}
|
|
1330
|
-
{
|
|
1331
|
-
VALUE result =
|
|
1332
|
-
rb_ca_stride_new(inner_wrap, data_type,
|
|
1333
|
-
ca_mv_carray_bytes_for(data_type, v->item_size),
|
|
1334
|
-
ndim, dim, strides_arr, /* base_offset */ 0);
|
|
1335
|
-
if (v->readonly) {
|
|
1336
|
-
CArray *rs;
|
|
1337
|
-
if (ca_mv_extract(result, &rs)) {
|
|
1338
|
-
ca_set_flag(rs, CA_FLAG_READ_ONLY);
|
|
1339
|
-
}
|
|
1340
|
-
}
|
|
1341
|
-
return result;
|
|
1342
|
-
}
|
|
1532
|
+
if ( d->contiguous ) {
|
|
1533
|
+
memcpy(ca->ptr, v->data, (size_t) ca_length(ca));
|
|
1343
1534
|
}
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
ca_mv_carray_bytes_for(data_type, v->item_size),
|
|
1353
|
-
NULL, (char *) v->data),
|
|
1354
|
-
target_class);
|
|
1355
|
-
|
|
1356
|
-
rb_ivar_set(wrap, rb_intern("memory_view_source"), obj);
|
|
1357
|
-
holder_obj = TypedData_Wrap_Struct(rb_cObject,
|
|
1358
|
-
&ca_mv_imported_holder_data_type,
|
|
1359
|
-
holder);
|
|
1360
|
-
rb_ivar_set(wrap, rb_intern("memory_view_holder"), holder_obj);
|
|
1361
|
-
|
|
1362
|
-
if (v->readonly) {
|
|
1363
|
-
CArray *ca;
|
|
1364
|
-
if (ca_mv_extract(wrap, &ca)) {
|
|
1365
|
-
ca_set_flag(ca, CA_FLAG_READ_ONLY);
|
|
1366
|
-
}
|
|
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);
|
|
1367
1543
|
}
|
|
1368
1544
|
|
|
1369
|
-
/*
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
CArray *data_ca, *mask_ca;
|
|
1378
|
-
rb_memory_view_t *mv = &mask_holder->view;
|
|
1379
|
-
VALUE mask_holder_obj;
|
|
1380
|
-
if (! ca_mv_extract(wrap, &data_ca)) {
|
|
1381
|
-
rb_memory_view_release(mv);
|
|
1382
|
-
mask_holder->valid = false;
|
|
1383
|
-
xfree(mask_holder);
|
|
1384
|
-
rb_raise(rb_eRuntimeError,
|
|
1385
|
-
"internal: rb_ca_wrap_new did not return a CArray");
|
|
1386
|
-
}
|
|
1387
|
-
mask_ca = (CArray *) ca_wrap_new(CA_BOOLEAN, ndim, dim, 1, NULL,
|
|
1388
|
-
(char *) mv->data);
|
|
1389
|
-
ca_set_flag(mask_ca, CA_FLAG_MASK_ARRAY);
|
|
1390
|
-
if (mv->readonly) {
|
|
1391
|
-
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;
|
|
1392
1553
|
}
|
|
1393
|
-
data_ca->mask = mask_ca;
|
|
1394
|
-
|
|
1395
|
-
rb_ivar_set(wrap, rb_intern("mask_memory_view_source"), mask_obj);
|
|
1396
|
-
mask_holder_obj = TypedData_Wrap_Struct(rb_cObject,
|
|
1397
|
-
&ca_mv_imported_holder_data_type,
|
|
1398
|
-
mask_holder);
|
|
1399
|
-
rb_ivar_set(wrap, rb_intern("mask_memory_view_holder"), mask_holder_obj);
|
|
1400
1554
|
}
|
|
1555
|
+
return result;
|
|
1556
|
+
}
|
|
1401
1557
|
|
|
1402
|
-
|
|
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;
|
|
1403
1565
|
}
|
|
1404
1566
|
|
|
1405
|
-
/* CArray.from_memory_view(obj, data_type: nil) -- copy into an
|
|
1406
|
-
Accepts strided typed sources (gather copy) and
|
|
1407
|
-
|
|
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. */
|
|
1408
1573
|
static VALUE
|
|
1409
1574
|
rb_ca_s_from_memory_view (int argc, VALUE *argv, VALUE klass)
|
|
1410
1575
|
{
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
ca_size_t dim[CA_RANK_MAX];
|
|
1415
|
-
VALUE result, obj, mask_obj;
|
|
1416
|
-
CArray *ca;
|
|
1417
|
-
bool contig;
|
|
1576
|
+
ca_mv_import_args_t a;
|
|
1577
|
+
VALUE obj, mask_obj;
|
|
1578
|
+
volatile VALUE result;
|
|
1418
1579
|
int8_t explicit_data_type =
|
|
1419
1580
|
ca_mv_extract_type_and_mask_kwargs(argc, argv,
|
|
1420
1581
|
(VALUE *) &obj, (VALUE *) &mask_obj);
|
|
1421
1582
|
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
NULL);
|
|
1434
|
-
if (! ca_mv_extract(result, &ca)) {
|
|
1435
|
-
rb_memory_view_release(&view);
|
|
1436
|
-
rb_raise(rb_eRuntimeError, "internal: rb_carray_new did not return a CArray");
|
|
1437
|
-
}
|
|
1438
|
-
|
|
1439
|
-
/* Typeless producers (format == NULL) and SIMPLE-form producers (shape
|
|
1440
|
-
omitted) are 1-D contiguous by construction; ca_mv_view_is_contiguous
|
|
1441
|
-
handles the NULL shape that would crash the raw contiguity helper. */
|
|
1442
|
-
contig = (view.format == NULL) || ca_mv_view_is_contiguous(&view);
|
|
1443
|
-
|
|
1444
|
-
if (contig) {
|
|
1445
|
-
size_t total_bytes = (size_t) ca_length(ca);
|
|
1446
|
-
/* For typeless producers byte_size and total_bytes are equal by
|
|
1447
|
-
construction; for typed producers we already validated alignment. */
|
|
1448
|
-
if (view.byte_size < (ssize_t) total_bytes) {
|
|
1449
|
-
rb_memory_view_release(&view);
|
|
1450
|
-
if (mask_holder) {
|
|
1451
|
-
rb_memory_view_release(&mask_holder->view);
|
|
1452
|
-
mask_holder->valid = false;
|
|
1453
|
-
xfree(mask_holder);
|
|
1454
|
-
}
|
|
1455
|
-
rb_raise(rb_eRuntimeError,
|
|
1456
|
-
"internal: byte_size mismatch (carray=%lu, view=%ld)",
|
|
1457
|
-
(unsigned long) total_bytes, (long) view.byte_size);
|
|
1458
|
-
}
|
|
1459
|
-
memcpy(ca->ptr, view.data, total_bytes);
|
|
1460
|
-
}
|
|
1461
|
-
else {
|
|
1462
|
-
/* Strided typed source: walk indices in row-major order and gather. */
|
|
1463
|
-
ca_mv_copy_strided_to_contiguous(ca->ptr, &view, ca->elements);
|
|
1464
|
-
}
|
|
1465
|
-
|
|
1466
|
-
rb_memory_view_release(&view);
|
|
1467
|
-
|
|
1468
|
-
/* mask: materialise the canonical CA_BOOLEAN mask slot via ca_create_mask,
|
|
1469
|
-
then memcpy bytes from the (validated row-major contig 1-byte/elem) mask
|
|
1470
|
-
buffer. Source format ? / B / b all use byte values where any non-zero
|
|
1471
|
-
means masked; that semantic matches CArray's mask convention directly
|
|
1472
|
-
when we coerce non-zero to 1. */
|
|
1473
|
-
if (mask_holder) {
|
|
1474
|
-
rb_memory_view_t *mv = &mask_holder->view;
|
|
1475
|
-
size_t n = (size_t) ca->elements;
|
|
1476
|
-
size_t i;
|
|
1477
|
-
const uint8_t *src = (const uint8_t *) mv->data;
|
|
1478
|
-
boolean8_t *dst;
|
|
1479
|
-
ca_create_mask(ca);
|
|
1480
|
-
dst = (boolean8_t *) ca->mask->ptr;
|
|
1481
|
-
for (i = 0; i < n; i++) {
|
|
1482
|
-
dst[i] = (src[i] != 0) ? 1 : 0;
|
|
1483
|
-
}
|
|
1484
|
-
rb_memory_view_release(mv);
|
|
1485
|
-
mask_holder->valid = false;
|
|
1486
|
-
xfree(mask_holder);
|
|
1487
|
-
}
|
|
1488
|
-
|
|
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);
|
|
1489
1594
|
return result;
|
|
1490
1595
|
}
|
|
1491
1596
|
|
|
@@ -1508,7 +1613,7 @@ rb_ca_s_memory_view_reject_reason (VALUE klass, VALUE obj)
|
|
|
1508
1613
|
CArray *ca;
|
|
1509
1614
|
ca_mv_reject_t rej;
|
|
1510
1615
|
if (! ca_mv_extract(obj, &ca)) {
|
|
1511
|
-
return
|
|
1616
|
+
return Qnil;
|
|
1512
1617
|
}
|
|
1513
1618
|
if (! ca_mv_ca_exportable(ca, obj)) {
|
|
1514
1619
|
if (ca->data_type == CA_FIXLEN && RTEST(rb_ca_has_data_class(obj))) {
|
|
@@ -1577,7 +1682,7 @@ rb_ca_s_memory_view_format (VALUE klass, VALUE obj)
|
|
|
1577
1682
|
if (ca->data_type == CA_FIXLEN) {
|
|
1578
1683
|
/* Plain CA_FIXLEN: the same "Ns" String the producer (ca_mv_get)
|
|
1579
1684
|
hands out via view->format. */
|
|
1580
|
-
return ca_mv_fixlen_format(ca->bytes);
|
|
1685
|
+
return rb_obj_freeze(rb_str_new_cstr(ca_mv_fixlen_format(ca->bytes)));
|
|
1581
1686
|
}
|
|
1582
1687
|
const char *fmt = ca_mv_format_for(ca->data_type);
|
|
1583
1688
|
return fmt ? rb_str_new_cstr(fmt) : Qnil;
|
|
@@ -1610,6 +1715,8 @@ static const rb_memory_view_entry_t ca_memory_view_entry = {
|
|
|
1610
1715
|
void
|
|
1611
1716
|
Init_carray_memory_view (void)
|
|
1612
1717
|
{
|
|
1718
|
+
ca_mv_fixlen_formats = st_init_numtable();
|
|
1719
|
+
rb_nativethread_lock_initialize(&ca_mv_fixlen_formats_lock);
|
|
1613
1720
|
/* Statically known classes. */
|
|
1614
1721
|
rb_memory_view_register(rb_cCArray, &ca_memory_view_entry);
|
|
1615
1722
|
rb_memory_view_register(rb_cCAWrap, &ca_memory_view_entry);
|
|
@@ -1643,13 +1750,14 @@ Init_carray_memory_view (void)
|
|
|
1643
1750
|
rb_ca_s_from_memory_view, -1);
|
|
1644
1751
|
rb_define_singleton_method(rb_cCArray, "wrap_memory_view",
|
|
1645
1752
|
rb_ca_s_wrap_memory_view, -1);
|
|
1646
|
-
/*
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
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);
|
|
1653
1761
|
rb_define_method(rb_const_get(rb_cObject, rb_intern("CARecord")),
|
|
1654
1762
|
"memory_view_field_names",
|
|
1655
1763
|
rb_ca_record_memory_view_field_names, 0);
|