carray 3.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +2 -1
- data/CHANGELOG.md +325 -3
- data/{NEWS.md → CHANGELOG.v1.md} +1 -1
- data/README.md +32 -32
- data/carray.gemspec +12 -8
- data/ext/ca_axis_group.c +55 -19
- data/ext/ca_binop_dispatch.c +5 -6
- data/ext/ca_binop_dispatch.h +0 -7
- data/ext/ca_categorical_iterator.c +13 -13
- data/ext/ca_for_each_element.h +6 -8
- data/ext/ca_group_iter.c +1 -1
- data/ext/ca_kernel_iterator.c +69 -23
- data/ext/ca_kernel_iterator.h +3 -10
- data/ext/ca_obj_bincmp.c +57 -27
- data/ext/ca_obj_binop.c +86 -30
- data/ext/ca_obj_const_string.c +3 -1
- data/ext/ca_obj_face.c +80 -0
- data/ext/ca_obj_face.h +33 -1
- data/ext/ca_obj_fake.c +11 -0
- data/ext/ca_obj_fixlen_string.c +3 -1
- data/ext/ca_obj_grid.c +11 -3
- data/ext/ca_obj_meld.c +13 -8
- data/ext/ca_obj_moncmp.c +33 -14
- data/ext/ca_obj_monop.c +19 -7
- data/ext/ca_obj_object.c +99 -4
- data/ext/ca_obj_record.c +3 -1
- data/ext/ca_obj_refer.c +30 -6
- data/ext/ca_obj_roll.c +13 -5
- data/ext/ca_obj_select_axis.c +16 -6
- data/ext/ca_obj_shift.c +3 -3
- data/ext/ca_obj_stride.c +106 -23
- data/ext/ca_obj_string.c +3 -1
- data/ext/ca_obj_tile.c +13 -5
- data/ext/ca_obj_time.c +3 -1
- data/ext/ca_obj_timedelta.c +3 -1
- data/ext/ca_obj_transpose.c +1 -1
- data/ext/ca_obj_triop.c +62 -23
- data/ext/ca_obj_window.c +86 -17
- data/ext/ca_op_cmplx64.h +123 -0
- data/ext/ca_op_ipower.c +0 -3
- data/ext/ca_sort_kernels.h +5 -5
- data/ext/ca_sweep_engine.c +78 -35
- data/ext/ca_sweep_engine.h +13 -4
- data/ext/ca_transform_common.c +7 -0
- data/ext/carray.h +127 -130
- data/ext/carray_access.c +56 -34
- data/ext/carray_bincount.c +8 -8
- data/ext/carray_broadcast.c +99 -6
- data/ext/carray_build_flags.h +3 -0
- data/ext/carray_call_cfunc.c +1491 -0
- data/ext/carray_call_cfunc.h +153 -0
- data/ext/carray_cast.c +69 -31
- data/ext/carray_conversion.c +22 -22
- data/ext/carray_copy.c +12 -2
- data/ext/carray_core.c +57 -10
- data/ext/carray_count.c +1 -1
- data/ext/carray_element.c +7 -7
- data/ext/carray_factorize.c +28 -28
- data/ext/carray_hold.c +1 -1
- data/ext/carray_index_classifier.c +7 -21
- data/ext/carray_internal.h +19 -3
- data/ext/carray_kernels_bincmp.c +1 -0
- data/ext/carray_kernels_binop.c +320 -298
- data/ext/carray_kernels_init.c +1095 -0
- data/ext/carray_kernels_map.c +1 -0
- data/ext/carray_kernels_moncmp.c +1 -0
- data/ext/carray_kernels_monop.c +412 -411
- data/ext/carray_kernels_reduce_aggregate.c +88 -87
- data/ext/carray_kernels_reduce_boolean.c +1 -0
- data/ext/carray_kernels_reduce_cumulative.c +54 -53
- data/ext/carray_kernels_reduce_extreme.c +51 -50
- data/ext/carray_kernels_reduce_variance.c +1 -0
- data/ext/carray_kernels_scan.c +1 -0
- data/ext/carray_kernels_search.c +93 -92
- data/ext/carray_kernels_sort.c +1 -0
- data/ext/carray_kernels_triop.c +1 -0
- data/ext/carray_lazy.c +175 -5
- data/ext/carray_mask.c +6 -5
- data/ext/carray_median_percentile.c +1 -1
- data/ext/carray_memory_view.c +46 -21
- data/ext/carray_operator.c +46 -49
- data/ext/carray_partition.c +4 -4
- data/ext/carray_random.c +7 -7
- data/ext/carray_scatter.c +1 -1
- data/ext/carray_sort.c +3 -3
- data/ext/carray_sort_kernel.c +10 -10
- data/ext/carray_test.c +1 -1
- data/ext/extconf.rb +18 -0
- data/ext/mk_call_cfunc.rb +243 -52
- data/ext/mkkernel.rb +354 -100
- data/ext/ruby_carray.c +18 -16
- data/ext/version.h +4 -4
- data/lib/carray/autoload_carray.rb +19 -0
- data/lib/carray/autoload_method_extension.rb +2 -1
- data/lib/carray/axis_group.rb +7 -7
- data/lib/carray/basics.rb +59 -59
- data/lib/carray/bincount_nd.rb +12 -12
- data/lib/carray/block_iterator.rb +24 -14
- data/lib/carray/categorical.rb +4 -4
- data/lib/carray/categorical_iterator.rb +98 -76
- data/lib/carray/conditional.rb +14 -14
- data/lib/carray/construct.rb +21 -0
- data/lib/carray/core_extensions.rb +62 -5
- data/lib/carray/data_type_extension.rb +18 -1
- data/lib/carray/frame/concat.rb +3 -3
- data/lib/carray/frame/convert.rb +1 -1
- data/lib/carray/frame/frame.rb +4 -11
- data/lib/carray/frame/io.rb +159 -2
- data/lib/carray/frame/sort.rb +1 -1
- data/lib/carray/frame/verbs.rb +18 -1
- data/lib/carray/fuse_source.rb +123 -0
- data/lib/carray/fusion.rb +218 -0
- data/lib/carray/histogram.rb +16 -16
- data/lib/carray/inspect.rb +1 -7
- data/lib/carray/iterator.rb +4 -3
- data/lib/carray/lazy.rb +125 -73
- data/lib/carray/meld_reduce.rb +2 -2
- data/lib/carray/methods/align_addr.rb +1 -1
- data/lib/carray/methods/composition.rb +1 -1
- data/lib/carray/methods/is_in.rb +12 -12
- data/lib/carray/methods/locate_addr.rb +6 -1
- data/lib/carray/methods/mask_duplicates.rb +1 -1
- data/lib/carray/methods/meshgrid.rb +4 -5
- data/lib/carray/methods/mode.rb +2 -2
- data/lib/carray/methods/nunique.rb +1 -1
- data/lib/carray/methods/snap.rb +7 -2
- data/lib/carray/methods/unique.rb +3 -3
- data/lib/carray/methods/value_counts.rb +2 -2
- data/lib/carray/runtime.rb +0 -19
- data/lib/carray/slab_iterator.rb +20 -7
- data/lib/carray/string_operation_extension.rb +5 -5
- data/lib/carray/time.rb +890 -491
- data/lib/carray/window_iterator.rb +285 -13
- data/lib/carray.rb +5 -5
- data/yard-stubs/ca_obj_array.rb +385 -0
- data/yard-stubs/ca_obj_bitarray.rb +38 -0
- data/yard-stubs/ca_obj_bitfield.rb +43 -0
- data/yard-stubs/ca_obj_block.rb +73 -0
- data/yard-stubs/ca_obj_byte_swap.rb +56 -0
- data/yard-stubs/ca_obj_fake.rb +31 -0
- data/yard-stubs/ca_obj_farray.rb +32 -0
- data/yard-stubs/ca_obj_field.rb +45 -0
- data/yard-stubs/ca_obj_grid.rb +35 -0
- data/yard-stubs/ca_obj_refer.rb +72 -0
- data/yard-stubs/ca_obj_roll.rb +45 -0
- data/yard-stubs/ca_obj_shift.rb +43 -0
- data/yard-stubs/ca_obj_stride.rb +181 -0
- data/yard-stubs/ca_obj_tile.rb +29 -0
- data/yard-stubs/ca_obj_transpose.rb +40 -0
- data/yard-stubs/ca_obj_window.rb +49 -0
- data/yard-stubs/carray_access.rb +131 -0
- data/yard-stubs/carray_attribute.rb +246 -0
- data/yard-stubs/carray_broadcast.rb +37 -0
- data/yard-stubs/carray_cast.rb +489 -0
- data/yard-stubs/carray_class.rb +65 -0
- data/yard-stubs/carray_conversion.rb +76 -0
- data/yard-stubs/carray_copy.rb +79 -0
- data/yard-stubs/carray_core.rb +114 -0
- data/yard-stubs/carray_count.rb +79 -0
- data/yard-stubs/carray_element.rb +108 -0
- data/yard-stubs/carray_generate.rb +66 -0
- data/yard-stubs/carray_lazy.rb +23 -0
- data/yard-stubs/carray_loop.rb +140 -0
- data/yard-stubs/carray_mask.rb +259 -0
- data/yard-stubs/carray_math.rb +132 -0
- data/yard-stubs/carray_mathfunc.rb +45 -0
- data/yard-stubs/carray_median_percentile.rb +89 -0
- data/yard-stubs/carray_memory_view.rb +163 -0
- data/yard-stubs/carray_order.rb +312 -0
- data/yard-stubs/carray_random.rb +89 -0
- data/yard-stubs/carray_scatter.rb +106 -0
- data/yard-stubs/carray_slab.rb +57 -0
- data/yard-stubs/carray_sort.rb +163 -0
- data/yard-stubs/carray_test.rb +85 -0
- data/yard-stubs/carray_undef.rb +64 -0
- data/yard-stubs/carray_utils.rb +97 -0
- data/yard-stubs/ruby_carray.rb +193 -0
- metadata +59 -10
- data/ext/ca_obj_unbound_repeat.c +0 -496
data/ext/carray_kernels_sort.c
CHANGED
data/ext/carray_kernels_triop.c
CHANGED
data/ext/carray_lazy.c
CHANGED
|
@@ -93,6 +93,10 @@ ca_lazy_marker_setup (CALazyMarker *ca, CArray *parent)
|
|
|
93
93
|
the parent and violate the shadow semantics. */
|
|
94
94
|
ca_set_flag(ca, CA_FLAG_READ_ONLY);
|
|
95
95
|
|
|
96
|
+
/* Storage-identical wrapper: the kernel_iterator entry strip and the
|
|
97
|
+
view-creation lift both ask for this. */
|
|
98
|
+
ca_set_flag(ca, CA_FLAG_IS_LAZY_MARKER);
|
|
99
|
+
|
|
96
100
|
return 0;
|
|
97
101
|
}
|
|
98
102
|
|
|
@@ -253,6 +257,105 @@ rb_ca_lazy (VALUE self)
|
|
|
253
257
|
return rb_ca_lazy_marker_new(self);
|
|
254
258
|
}
|
|
255
259
|
|
|
260
|
+
/* ------------------------------------------------------------------- */
|
|
261
|
+
/* CA_OBJECT GC guard */
|
|
262
|
+
/* ------------------------------------------------------------------- */
|
|
263
|
+
|
|
264
|
+
/* A CA_OBJECT cell is a VALUE. While a view materialises into a raw
|
|
265
|
+
buffer -- the destination `copy` will hand out, a view's own attach
|
|
266
|
+
buffer, or an arena scratch -- those VALUEs live somewhere the GC
|
|
267
|
+
cannot see: an xmalloc'd block owned by no Ruby object. For every
|
|
268
|
+
other data_type that is harmless, but an object-lane kernel calls
|
|
269
|
+
rb_funcall per cell, and any of those can collect. The results
|
|
270
|
+
written so far are then freed under the buffer, and the array comes
|
|
271
|
+
back holding recycled slots: wrong values, and a crash once a slot is
|
|
272
|
+
reused as something else.
|
|
273
|
+
|
|
274
|
+
So such buffers are registered here for the duration and marked from
|
|
275
|
+
a hidden guard object that lives as long as the process. Two kinds
|
|
276
|
+
are registered:
|
|
277
|
+
|
|
278
|
+
holds a contiguous run of cells, pushed and popped around a
|
|
279
|
+
whole-view transfer or the handoff to a Ruby owner
|
|
280
|
+
slots an arena scratch, tagged at acquire and held until release
|
|
281
|
+
|
|
282
|
+
Only contiguous runs are held. A strided window would cover the same
|
|
283
|
+
cells but its extent has to be taken on trust from the caller's
|
|
284
|
+
strides, and a window wider than the buffer behind it would be filled
|
|
285
|
+
and marked out of bounds. The whole-view entry knows the extent
|
|
286
|
+
exactly, and every partial transfer writes inside it. */
|
|
287
|
+
|
|
288
|
+
#define CA_GC_HOLD_MAX 64
|
|
289
|
+
|
|
290
|
+
typedef struct ca_gc_hold {
|
|
291
|
+
VALUE *ptr;
|
|
292
|
+
ca_size_t elements;
|
|
293
|
+
} ca_gc_hold_t;
|
|
294
|
+
|
|
295
|
+
static ca_gc_hold_t ca_gc_holds[CA_GC_HOLD_MAX];
|
|
296
|
+
static int ca_gc_hold_depth = 0;
|
|
297
|
+
|
|
298
|
+
static void ca_lazy_arena_mark_object_slots (void);
|
|
299
|
+
|
|
300
|
+
/* Returns the depth to hand back to ca_gc_hold_pop_to, so nested holds
|
|
301
|
+
unwind in order. Pushing is best-effort at the ceiling: a chain
|
|
302
|
+
deeper than CA_GC_HOLD_MAX loses the protection for its innermost
|
|
303
|
+
buffers rather than raising in the middle of a materialise. The cells
|
|
304
|
+
must already be valid VALUEs -- the caller fills a fresh buffer with
|
|
305
|
+
Qnil first. */
|
|
306
|
+
int
|
|
307
|
+
ca_gc_hold_push (void *ptr, ca_size_t n_elements)
|
|
308
|
+
{
|
|
309
|
+
int depth = ca_gc_hold_depth;
|
|
310
|
+
|
|
311
|
+
if ( depth >= CA_GC_HOLD_MAX || n_elements <= 0 || ptr == NULL ) {
|
|
312
|
+
return -1;
|
|
313
|
+
}
|
|
314
|
+
ca_gc_holds[depth].ptr = (VALUE *) ptr;
|
|
315
|
+
ca_gc_holds[depth].elements = n_elements;
|
|
316
|
+
ca_gc_hold_depth = depth + 1;
|
|
317
|
+
return depth;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
void
|
|
321
|
+
ca_gc_hold_pop_to (int depth)
|
|
322
|
+
{
|
|
323
|
+
if ( depth >= 0 && depth < ca_gc_hold_depth ) {
|
|
324
|
+
ca_gc_hold_depth = depth;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
static VALUE ca_gc_guard = Qnil;
|
|
329
|
+
|
|
330
|
+
/* The wrapped pointer must be non-NULL: Ruby's GC skips the mark
|
|
331
|
+
function of a TypedData whose data pointer is NULL. */
|
|
332
|
+
static int ca_gc_guard_body = 0;
|
|
333
|
+
|
|
334
|
+
static void
|
|
335
|
+
ca_gc_guard_mark (void *ptr)
|
|
336
|
+
{
|
|
337
|
+
int i;
|
|
338
|
+
(void) ptr;
|
|
339
|
+
for ( i = 0; i < ca_gc_hold_depth; i++ ) {
|
|
340
|
+
VALUE *p = ca_gc_holds[i].ptr;
|
|
341
|
+
ca_size_t n = ca_gc_holds[i].elements;
|
|
342
|
+
while ( n-- ) rb_gc_mark(*p++);
|
|
343
|
+
}
|
|
344
|
+
ca_lazy_arena_mark_object_slots();
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
static void
|
|
348
|
+
ca_gc_guard_free (void *ptr)
|
|
349
|
+
{
|
|
350
|
+
(void) ptr;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
static const rb_data_type_t ca_gc_guard_data_type = {
|
|
354
|
+
"carray_object_gc_guard",
|
|
355
|
+
{ ca_gc_guard_mark, ca_gc_guard_free, NULL, },
|
|
356
|
+
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
|
357
|
+
};
|
|
358
|
+
|
|
256
359
|
/* ------------------------------------------------------------------- */
|
|
257
360
|
/* ca_lazy_arena */
|
|
258
361
|
/* ------------------------------------------------------------------- */
|
|
@@ -277,8 +380,9 @@ rb_ca_lazy (VALUE self)
|
|
|
277
380
|
programming error or a pathological chain rather than a graceful
|
|
278
381
|
fallback.
|
|
279
382
|
|
|
280
|
-
CAREFUL: single-thread only
|
|
281
|
-
goal
|
|
383
|
+
CAREFUL: single-thread only -- thread-safety across concurrent access
|
|
384
|
+
is not a goal (see guides/devel/04_attach_lifecycle.md). The arena is
|
|
385
|
+
process-global static state. */
|
|
282
386
|
|
|
283
387
|
#define CA_LAZY_ARENA_SLOTS 32
|
|
284
388
|
|
|
@@ -286,6 +390,7 @@ typedef struct ca_lazy_arena_slot {
|
|
|
286
390
|
void *ptr; /* xmalloc'd buffer, NULL = unused-and-unallocated */
|
|
287
391
|
ca_size_t bytes; /* allocated size of ptr (= capacity of this slot) */
|
|
288
392
|
int in_use; /* 1 = currently acquired, 0 = available */
|
|
393
|
+
ca_size_t object_elements; /* >0 = holds VALUEs; marked while in_use */
|
|
289
394
|
} ca_lazy_arena_slot_t;
|
|
290
395
|
|
|
291
396
|
typedef struct ca_lazy_arena {
|
|
@@ -308,6 +413,7 @@ ca_lazy_arena_enter (void)
|
|
|
308
413
|
int i;
|
|
309
414
|
for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
|
|
310
415
|
ca_lazy_arena.slots[i].in_use = 0;
|
|
416
|
+
ca_lazy_arena.slots[i].object_elements = 0;
|
|
311
417
|
}
|
|
312
418
|
}
|
|
313
419
|
ca_lazy_arena.depth++;
|
|
@@ -319,7 +425,16 @@ ca_lazy_arena_exit (void)
|
|
|
319
425
|
if ( ca_lazy_arena.depth > 0 ) {
|
|
320
426
|
ca_lazy_arena.depth--;
|
|
321
427
|
}
|
|
322
|
-
/* Keep all slot buffers
|
|
428
|
+
/* Keep all slot buffers, and leave in_use and the object tags alone.
|
|
429
|
+
A scratch can be acquired outside any enter/exit bracket -- a lazy
|
|
430
|
+
view's attach path does exactly that -- so an inner materialise
|
|
431
|
+
dropping to depth 0 is not evidence that nothing is held. Recovery
|
|
432
|
+
from an abandoned slot happens on the next depth-0 entry instead.
|
|
433
|
+
|
|
434
|
+
An abandoned object slot stays marked until then, which is a leak
|
|
435
|
+
but not a hazard: acquire only ever hands out a slot that is not in
|
|
436
|
+
use, so nothing overwrites it, and marking is what keeps its cells
|
|
437
|
+
from being collected in the first place. */
|
|
323
438
|
}
|
|
324
439
|
|
|
325
440
|
void *
|
|
@@ -350,6 +465,7 @@ ca_lazy_arena_acquire (ca_size_t bytes)
|
|
|
350
465
|
}
|
|
351
466
|
if ( best >= 0 ) {
|
|
352
467
|
ca_lazy_arena.slots[best].in_use = 1;
|
|
468
|
+
ca_lazy_arena.slots[best].object_elements = 0;
|
|
353
469
|
ca_lazy_arena.debug_reuse_count++;
|
|
354
470
|
return ca_lazy_arena.slots[best].ptr;
|
|
355
471
|
}
|
|
@@ -367,6 +483,7 @@ ca_lazy_arena_acquire (ca_size_t bytes)
|
|
|
367
483
|
ca_lazy_arena.slots[empty].ptr = xmalloc(bytes);
|
|
368
484
|
ca_lazy_arena.slots[empty].bytes = bytes;
|
|
369
485
|
ca_lazy_arena.slots[empty].in_use = 1;
|
|
486
|
+
ca_lazy_arena.slots[empty].object_elements = 0;
|
|
370
487
|
ca_lazy_arena.debug_xmalloc_count++;
|
|
371
488
|
return ca_lazy_arena.slots[empty].ptr;
|
|
372
489
|
}
|
|
@@ -380,6 +497,7 @@ ca_lazy_arena_acquire (ca_size_t bytes)
|
|
|
380
497
|
ca_lazy_arena.slots[i].ptr = xmalloc(bytes);
|
|
381
498
|
ca_lazy_arena.slots[i].bytes = bytes;
|
|
382
499
|
ca_lazy_arena.slots[i].in_use = 1;
|
|
500
|
+
ca_lazy_arena.slots[i].object_elements = 0;
|
|
383
501
|
ca_lazy_arena.debug_xmalloc_count++;
|
|
384
502
|
return ca_lazy_arena.slots[i].ptr;
|
|
385
503
|
}
|
|
@@ -402,6 +520,7 @@ ca_lazy_arena_release (void *ptr)
|
|
|
402
520
|
for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
|
|
403
521
|
if ( ca_lazy_arena.slots[i].ptr == ptr ) {
|
|
404
522
|
ca_lazy_arena.slots[i].in_use = 0;
|
|
523
|
+
ca_lazy_arena.slots[i].object_elements = 0;
|
|
405
524
|
return;
|
|
406
525
|
}
|
|
407
526
|
}
|
|
@@ -410,6 +529,52 @@ ca_lazy_arena_release (void *ptr)
|
|
|
410
529
|
genuine bugs at unrelated callsites. */
|
|
411
530
|
}
|
|
412
531
|
|
|
532
|
+
/* Acquire a scratch that will hold `n_elements` VALUEs.
|
|
533
|
+
|
|
534
|
+
Unlike the byte-sized acquire this one Qnil-fills the buffer and tags
|
|
535
|
+
the slot, so the cells stay markable for as long as the caller holds
|
|
536
|
+
it. Object-lane kernels run rb_funcall over their operands, so a
|
|
537
|
+
scratch pulled from a lazy operand holds VALUEs that exist nowhere
|
|
538
|
+
else until the kernel has consumed them. */
|
|
539
|
+
void *
|
|
540
|
+
ca_lazy_arena_acquire_object (ca_size_t n_elements)
|
|
541
|
+
{
|
|
542
|
+
void *ptr;
|
|
543
|
+
int i;
|
|
544
|
+
|
|
545
|
+
if ( n_elements < 0 ) n_elements = 0;
|
|
546
|
+
ptr = ca_lazy_arena_acquire(n_elements * (ca_size_t) sizeof(VALUE));
|
|
547
|
+
|
|
548
|
+
{
|
|
549
|
+
VALUE *p = (VALUE *) ptr;
|
|
550
|
+
ca_size_t n = n_elements;
|
|
551
|
+
while ( n-- ) *p++ = Qnil;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
|
|
555
|
+
if ( ca_lazy_arena.slots[i].ptr == ptr ) {
|
|
556
|
+
ca_lazy_arena.slots[i].object_elements = n_elements;
|
|
557
|
+
break;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
return ptr;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
static void
|
|
564
|
+
ca_lazy_arena_mark_object_slots (void)
|
|
565
|
+
{
|
|
566
|
+
int i;
|
|
567
|
+
for ( i = 0; i < CA_LAZY_ARENA_SLOTS; i++ ) {
|
|
568
|
+
ca_lazy_arena_slot_t *s = &ca_lazy_arena.slots[i];
|
|
569
|
+
ca_size_t n;
|
|
570
|
+
VALUE *p;
|
|
571
|
+
if ( ! s->in_use || s->object_elements <= 0 ) continue;
|
|
572
|
+
p = (VALUE *) s->ptr;
|
|
573
|
+
n = s->object_elements;
|
|
574
|
+
while ( n-- ) rb_gc_mark(*p++);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
413
578
|
/* Test instrumentation — exposed to Ruby for the arena smoke and
|
|
414
579
|
regression assertions. Not user-facing API. */
|
|
415
580
|
static VALUE
|
|
@@ -490,8 +655,7 @@ rb_ca_lazy_arena_s_slot_capacities (VALUE klass)
|
|
|
490
655
|
* just let the scalar choose)
|
|
491
656
|
* numeric self -> self_ca->data_type
|
|
492
657
|
*
|
|
493
|
-
* Callers broadcast against the array via stride 0 in xfer_stride
|
|
494
|
-
* (or wrap the scalar in CAUnboundRepeat when the chain needs it). */
|
|
658
|
+
* Callers broadcast against the array via stride 0 in xfer_stride. */
|
|
495
659
|
VALUE
|
|
496
660
|
ca_lazy_wrap_scalar (VALUE other, CArray *self_ca)
|
|
497
661
|
{
|
|
@@ -543,6 +707,12 @@ Init_carray_lazy (void)
|
|
|
543
707
|
|
|
544
708
|
rb_define_method(rb_cCArray, "lazy", rb_ca_lazy, 0);
|
|
545
709
|
|
|
710
|
+
/* Hidden, never collected: its mark function is what keeps the
|
|
711
|
+
in-flight CA_OBJECT buffers reachable. */
|
|
712
|
+
ca_gc_guard = TypedData_Wrap_Struct(rb_cObject, &ca_gc_guard_data_type,
|
|
713
|
+
&ca_gc_guard_body);
|
|
714
|
+
rb_gc_register_mark_object(ca_gc_guard);
|
|
715
|
+
|
|
546
716
|
rb_define_alloc_func(rb_cCALazyMarker, rb_ca_lazy_marker_s_allocate);
|
|
547
717
|
rb_define_method(rb_cCALazyMarker, "initialize_copy",
|
|
548
718
|
rb_ca_lazy_marker_initialize_copy, 1);
|
data/ext/carray_mask.c
CHANGED
|
@@ -607,7 +607,7 @@ ca_count_not_masked (void *ap)
|
|
|
607
607
|
/* Scalar-fill every masked cell with fill_value, then clear its mask bit.
|
|
608
608
|
This depends only on the cell BYTE WIDTH, not the data type (the work is
|
|
609
609
|
a width-sized copy), so a single width switch covers every numeric /
|
|
610
|
-
complex / object
|
|
610
|
+
complex / object data type; the default arm handles fixlen and any other
|
|
611
611
|
width via memcpy. The typed arms let the compiler emit a plain store. */
|
|
612
612
|
#define proc_fill_bang_width(T) \
|
|
613
613
|
{ \
|
|
@@ -894,6 +894,7 @@ rb_ca_value_array (VALUE self)
|
|
|
894
894
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
895
895
|
|
|
896
896
|
obj = rb_ca_refer_new(self, ca->data_type, ca->ndim, ca->dim, ca->bytes, 0);
|
|
897
|
+
|
|
897
898
|
TypedData_Get_Struct(obj, CArray, &carray_data_type, co);
|
|
898
899
|
|
|
899
900
|
/* Value arrays ignore the mask by definition. ca_stride_setup
|
|
@@ -1112,7 +1113,7 @@ The returned array doesn't have the mask array.
|
|
|
1112
1113
|
static VALUE
|
|
1113
1114
|
rb_ca_unmask_method (int argc, VALUE *argv, VALUE self)
|
|
1114
1115
|
{
|
|
1115
|
-
volatile VALUE rfval =
|
|
1116
|
+
volatile VALUE rfval = CA_UNSPECIFIED, rcs;
|
|
1116
1117
|
CArray *ca;
|
|
1117
1118
|
CScalar *cv;
|
|
1118
1119
|
char *fval = NULL;
|
|
@@ -1125,7 +1126,7 @@ rb_ca_unmask_method (int argc, VALUE *argv, VALUE self)
|
|
|
1125
1126
|
|
|
1126
1127
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
1127
1128
|
|
|
1128
|
-
if ( rfval !=
|
|
1129
|
+
if ( rfval != CA_UNSPECIFIED ) {
|
|
1129
1130
|
/* Face has surface != storage; the fill value is cast in the storage data_type. */
|
|
1130
1131
|
int8_t conv_type = ca->data_type;
|
|
1131
1132
|
if ( ca_is_face(ca) ) {
|
|
@@ -1171,7 +1172,7 @@ The returned array doesn't have the mask array.
|
|
|
1171
1172
|
static VALUE
|
|
1172
1173
|
rb_ca_unmask_copy_method (int argc, VALUE *argv, VALUE self)
|
|
1173
1174
|
{
|
|
1174
|
-
volatile VALUE obj, rfval =
|
|
1175
|
+
volatile VALUE obj, rfval = CA_UNSPECIFIED, rcs;
|
|
1175
1176
|
CArray *ca, *co;
|
|
1176
1177
|
CScalar *cv;
|
|
1177
1178
|
char *fval = NULL;
|
|
@@ -1182,7 +1183,7 @@ rb_ca_unmask_copy_method (int argc, VALUE *argv, VALUE self)
|
|
|
1182
1183
|
|
|
1183
1184
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
1184
1185
|
|
|
1185
|
-
if ( rfval !=
|
|
1186
|
+
if ( rfval != CA_UNSPECIFIED ) {
|
|
1186
1187
|
/* Face has surface != storage; the fill value is cast in the storage data_type. */
|
|
1187
1188
|
int8_t conv_type = ca->data_type;
|
|
1188
1189
|
if ( ca_is_face(ca) ) {
|
|
@@ -152,7 +152,7 @@ typedef enum { PCT_LOWER_ONLY, PCT_UPPER_ONLY, PCT_BOTH } pct_need_t;
|
|
|
152
152
|
|
|
153
153
|
/* [shared] which of lower (sorted[k]) / upper (sorted[k+1]) the method
|
|
154
154
|
needs. Lets the caller skip the fetch it won't use. Used by both
|
|
155
|
-
lanes (the need classification is
|
|
155
|
+
lanes (the need classification is data type-independent). */
|
|
156
156
|
static pct_need_t
|
|
157
157
|
pct_need (VALUE method, long k, double r, long n)
|
|
158
158
|
{
|
data/ext/carray_memory_view.c
CHANGED
|
@@ -99,46 +99,70 @@ static ca_mv_runtime_type_t ca_mv_runtime_types[CA_MV_NUM_RUNTIME_OBJ_TYPES] = {
|
|
|
99
99
|
|
|
100
100
|
/* ---------------- format / data_type mapping ----------------
|
|
101
101
|
Bidirectional table. Outbound table (ca_mv_format_for) picks the
|
|
102
|
-
|
|
103
|
-
table (ca_mv_data_type_from_format) is Postel:
|
|
104
|
-
set of synonyms that other producers emit (they
|
|
105
|
-
differently).
|
|
102
|
+
specifier each CArray data_type publishes at the top level of a
|
|
103
|
+
MemoryView. Inbound table (ca_mv_data_type_from_format) is Postel:
|
|
104
|
+
it accepts a wider set of synonyms that other producers emit (they
|
|
105
|
+
describe int32 differently).
|
|
106
|
+
|
|
107
|
+
The outbound spellings follow ruby/memory_view.h, which documents
|
|
108
|
+
`format` as a sequence of pack-derived specifiers and expects
|
|
109
|
+
item_size to equal rb_memory_view_item_size_from_format(format).
|
|
110
|
+
PEP 3118 reuses several of those letters for other types, so the two
|
|
111
|
+
vocabularies agree on `i` / `I` / `q` / `Q` / `f` / `d` and disagree
|
|
112
|
+
below 32 bits: what PEP calls `b` / `B` / `h` / `H`, Ruby calls
|
|
113
|
+
`c` / `C` / `s` / `S`. Emitting Ruby's spelling is what lets a
|
|
114
|
+
generic Ruby consumer -- Fiddle::MemoryView, say -- read elements out
|
|
115
|
+
of a CArray at all; PEP-speaking consumers are reached across the
|
|
116
|
+
language boundary, where pycall-memoryview translates.
|
|
117
|
+
|
|
118
|
+
Three types have no Ruby spelling at all: boolean and the two
|
|
119
|
+
complex widths. They keep their PEP form, which Ruby's parser
|
|
120
|
+
rejects, because the alternative ("C" for a boolean, "dd" for a
|
|
121
|
+
complex) would misdescribe the data rather than merely fail to
|
|
122
|
+
describe it. */
|
|
106
123
|
|
|
107
124
|
static const char *
|
|
108
125
|
ca_mv_format_for (int8_t data_type)
|
|
109
126
|
{
|
|
110
127
|
switch (data_type) {
|
|
111
|
-
case CA_BOOLEAN: return "?";
|
|
112
|
-
case CA_INT8: return "
|
|
113
|
-
case CA_UINT8: return "
|
|
114
|
-
case CA_INT16: return "
|
|
115
|
-
case CA_UINT16: return "
|
|
128
|
+
case CA_BOOLEAN: return "?"; /* no Ruby spelling */
|
|
129
|
+
case CA_INT8: return "c";
|
|
130
|
+
case CA_UINT8: return "C";
|
|
131
|
+
case CA_INT16: return "s";
|
|
132
|
+
case CA_UINT16: return "S";
|
|
116
133
|
case CA_INT32: return "i";
|
|
117
134
|
case CA_UINT32: return "I";
|
|
118
135
|
case CA_INT64: return "q";
|
|
119
136
|
case CA_UINT64: return "Q";
|
|
120
137
|
case CA_FLOAT32: return "f";
|
|
121
138
|
case CA_FLOAT64: return "d";
|
|
122
|
-
case CA_CMPLX64: return "Zf";
|
|
123
|
-
case CA_CMPLX128: return "Zd";
|
|
139
|
+
case CA_CMPLX64: return "Zf"; /* no Ruby spelling */
|
|
140
|
+
case CA_CMPLX128: return "Zd"; /* no Ruby spelling */
|
|
124
141
|
default:
|
|
125
142
|
return NULL;
|
|
126
143
|
}
|
|
127
144
|
}
|
|
128
145
|
|
|
129
146
|
/* PEP 3118 single-character specifier for a primitive data_type, for
|
|
130
|
-
use inside a `T{...}` struct format body.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
147
|
+
use inside a `T{...}` struct format body. This is the context the
|
|
148
|
+
distinct entry point was kept for: `T{...}` is a PEP 3118 construct
|
|
149
|
+
with no Ruby counterpart, so a record body stays in PEP's vocabulary
|
|
150
|
+
even though the top level now publishes Ruby's. A consumer reading
|
|
151
|
+
the body reads it as PEP, and a bridge that forwards the record
|
|
152
|
+
whole -- as pycall-memoryview does -- keeps it intact.
|
|
135
153
|
|
|
136
154
|
Returns NULL for non-primitive (CA_FIXLEN / CA_OBJECT) or for the
|
|
137
155
|
retired float128 / cmplx256 enum slots. */
|
|
138
156
|
static const char *
|
|
139
157
|
ca_mv_pep3118_char (int8_t data_type)
|
|
140
158
|
{
|
|
141
|
-
|
|
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
|
+
}
|
|
142
166
|
}
|
|
143
167
|
|
|
144
168
|
/* Build (or fetch from cache) the PEP 3118 struct format string for
|
|
@@ -362,7 +386,7 @@ ca_mv_data_type_from_format (const char *format, ssize_t item_size)
|
|
|
362
386
|
}
|
|
363
387
|
|
|
364
388
|
/* Derive the data_type a MemoryView producer would import as, by parsing its
|
|
365
|
-
format — without importing the data. This is the canonical "what
|
|
389
|
+
format — without importing the data. This is the canonical "what data type is
|
|
366
390
|
this MV" question, decoupled from the import strategy (copy via
|
|
367
391
|
from_memory_view vs zero-copy via wrap_memory_view). Used by
|
|
368
392
|
ca_arg_to_data_type so CArray.result_type / promote_list can treat an MV
|
|
@@ -414,7 +438,6 @@ ca_mv_strategy_for (int16_t obj_type)
|
|
|
414
438
|
if (obj_type == CA_OBJ_SELECT) return CA_MV_ATTACH;
|
|
415
439
|
if (obj_type == CA_OBJ_REPEAT) return CA_MV_REPEAT;
|
|
416
440
|
if (obj_type == CA_OBJ_OBJECT) return CA_MV_REJECT;
|
|
417
|
-
if (obj_type == CA_OBJ_UNBOUND_REPEAT) return CA_MV_REJECT;
|
|
418
441
|
/* Plain CAStride (created via #as_strided, or wrap_memory_view from
|
|
419
442
|
a strided producer). Reuse the CA_MV_TRANS handler since it
|
|
420
443
|
already reads strides[] / base_offset directly from the CAStride
|
|
@@ -510,7 +533,6 @@ ca_mv_reject_reason_for (CArray *ca)
|
|
|
510
533
|
/* Compile-time obj_types we know about */
|
|
511
534
|
if (ca->obj_type == CA_OBJ_SELECT) return "boolean-mask selection (positions not expressible as strides)";
|
|
512
535
|
if (ca->obj_type == CA_OBJ_OBJECT) return "stores Ruby VALUEs, not raw bytes";
|
|
513
|
-
if (ca->obj_type == CA_OBJ_UNBOUND_REPEAT) return "shape is not bound";
|
|
514
536
|
/* Runtime-assigned obj_types: look up by name in the strategy table */
|
|
515
537
|
for (int i = 0; i < CA_MV_NUM_RUNTIME_OBJ_TYPES; i++) {
|
|
516
538
|
if (ca_mv_runtime_types[i].id == ca->obj_type) {
|
|
@@ -1110,7 +1132,10 @@ ca_mv_acquire_mask_view (VALUE mask_src, int8_t data_ndim,
|
|
|
1110
1132
|
if (c == '<' || c == '>' || c == '=' || c == '!' || c == '@') {
|
|
1111
1133
|
c = mv->format[1];
|
|
1112
1134
|
}
|
|
1113
|
-
|
|
1135
|
+
/* One byte per element, under either vocabulary: PEP 3118 spells the
|
|
1136
|
+
integer widths 'b' / 'B', Ruby's MemoryView spells them 'c' / 'C'.
|
|
1137
|
+
'?' is PEP's boolean, which Ruby has no spelling for. */
|
|
1138
|
+
if (c != '?' && c != 'B' && c != 'b' && c != 'C' && c != 'c') {
|
|
1114
1139
|
char fmt_buf[64];
|
|
1115
1140
|
snprintf(fmt_buf, sizeof(fmt_buf), "%s", mv->format);
|
|
1116
1141
|
MV_MASK_CLEANUP_AND_RAISE("mask: format must be bool/uint8/int8 (got %s)",
|