carray 3.0.4 → 3.0.6
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 +438 -0
- data/README.md +1 -1
- data/carray.gemspec +3 -2
- data/ext/ca_array_pool.c +25 -1
- data/ext/ca_axis_dispatch.c +19 -20
- data/ext/ca_axis_group.c +1 -1
- data/ext/ca_categorical_iterator.c +34 -27
- data/ext/ca_compare.c +4 -4
- data/ext/ca_composite_dispatch.c +9 -9
- data/ext/ca_kernel_iterator.c +122 -237
- data/ext/ca_kernel_iterator.h +522 -337
- data/ext/ca_obj_array.c +69 -19
- data/ext/ca_obj_bincmp.c +10 -1
- data/ext/ca_obj_binop.c +11 -1
- data/ext/ca_obj_bitarray.c +5 -1
- data/ext/ca_obj_bitfield.c +60 -43
- data/ext/ca_obj_byte_swap.c +4 -1
- data/ext/ca_obj_const_string.c +15 -3
- data/ext/ca_obj_face.c +44 -1
- data/ext/ca_obj_fake.c +3 -1
- data/ext/ca_obj_field.c +13 -3
- data/ext/ca_obj_grid.c +219 -17
- data/ext/ca_obj_meld.c +69 -113
- data/ext/ca_obj_moncmp.c +9 -1
- data/ext/ca_obj_monop.c +86 -12
- data/ext/ca_obj_object.c +118 -32
- data/ext/ca_obj_record.c +3 -0
- data/ext/ca_obj_reduce.c +19 -1
- data/ext/ca_obj_refer.c +80 -26
- data/ext/ca_obj_remap.c +69 -10
- data/ext/ca_obj_roll.c +27 -6
- data/ext/ca_obj_select.c +38 -6
- data/ext/ca_obj_select_axis.c +1 -0
- data/ext/ca_obj_shift.c +32 -7
- data/ext/ca_obj_stack.c +311 -111
- data/ext/ca_obj_stride.c +159 -64
- data/ext/ca_obj_string.c +27 -2
- data/ext/ca_obj_tile.c +28 -7
- data/ext/ca_obj_transpose.c +8 -4
- data/ext/ca_obj_triop.c +12 -1
- data/ext/ca_obj_window.c +80 -50
- data/ext/ca_op_ipower.c +12 -12
- data/ext/ca_sort_kernels.h +6 -3
- data/ext/caframe_csv_reader.c +1 -1
- data/ext/carray.h +35 -1
- data/ext/carray_access.c +43 -67
- data/ext/carray_address_basis.c +134 -0
- data/ext/carray_attribute.c +32 -0
- data/ext/carray_broadcast.c +3 -4
- data/ext/carray_build_flags.h +1 -1
- data/ext/carray_cast.c +251 -93
- data/ext/carray_cast_func.rb +56 -26
- data/ext/carray_conversion.c +11 -7
- data/ext/carray_core.c +814 -72
- data/ext/carray_count.c +6 -0
- data/ext/carray_element.c +22 -13
- data/ext/carray_factorize.c +20 -20
- data/ext/carray_generate.c +8 -2
- data/ext/carray_histogram.c +26 -4
- data/ext/carray_index_classifier.c +4 -4
- data/ext/carray_internal.h +159 -12
- data/ext/carray_kernels_bincmp.c +2174 -170
- data/ext/carray_kernels_binop.c +25653 -5487
- data/ext/carray_kernels_init.c +8 -8
- data/ext/carray_kernels_moncmp.c +96 -96
- data/ext/carray_kernels_monop.c +12253 -5235
- data/ext/carray_kernels_reduce_aggregate.c +6150 -2423
- data/ext/carray_kernels_reduce_boolean.c +1533 -15
- data/ext/carray_kernels_reduce_cumulative.c +2442 -882
- data/ext/carray_kernels_reduce_extreme.c +5658 -1661
- data/ext/carray_kernels_reduce_variance.c +2534 -402
- data/ext/carray_kernels_scan.c +368 -368
- data/ext/carray_kernels_search.c +4551 -604
- data/ext/carray_kernels_sort.c +659 -751
- data/ext/carray_kernels_triop.c +114 -114
- data/ext/carray_key_windows.c +4 -0
- data/ext/carray_lazy.c +47 -0
- data/ext/carray_loop.c +1 -1
- data/ext/carray_mask.c +175 -161
- data/ext/carray_median_percentile.c +4 -4
- data/ext/carray_memory_view.c +15 -9
- data/ext/carray_operator.c +152 -49
- data/ext/carray_order.c +230 -10
- data/ext/carray_random.c +22 -19
- data/ext/carray_scatter.c +24 -7
- data/ext/carray_slab.c +128 -29
- data/ext/carray_sort.c +224 -16
- data/ext/carray_sort_kernel.c +5 -7
- data/ext/carray_struct.c +35 -43
- data/ext/carray_test.c +74 -19
- data/ext/carray_time_text.c +6 -4
- data/ext/carray_utils.c +63 -21
- data/ext/extconf.rb +11 -0
- data/ext/mkkernel.rb +546 -199
- data/ext/ruby_carray.c +4 -1
- data/ext/version.h +4 -4
- data/lib/carray/attribute.rb +42 -5
- data/lib/carray/autoload_carray.rb +3 -3
- data/lib/carray/autoload_method_extension.rb +5 -3
- data/lib/carray/axis_group.rb +13 -7
- data/lib/carray/basics.rb +28 -8
- data/lib/carray/block_iterator.rb +17 -3
- data/lib/carray/categorical.rb +35 -23
- data/lib/carray/categorical_iterator.rb +8 -8
- data/lib/carray/complex.rb +2 -2
- data/lib/carray/conditional.rb +3 -2
- data/lib/carray/const_string.rb +10 -0
- data/lib/carray/data_type_extension.rb +8 -1
- data/lib/carray/fixlen_string.rb +41 -2
- data/lib/carray/frame/column_verbs.rb +48 -16
- data/lib/carray/frame/concat.rb +17 -3
- data/lib/carray/frame/convert.rb +19 -0
- data/lib/carray/frame/csv_parser.rb +32 -32
- data/lib/carray/frame/describe.rb +4 -2
- data/lib/carray/frame/frame.rb +405 -85
- data/lib/carray/frame/group.rb +777 -64
- data/lib/carray/frame/io.rb +212 -72
- data/lib/carray/frame/join.rb +146 -37
- data/lib/carray/frame/records.rb +3 -3
- data/lib/carray/frame/reshape.rb +89 -11
- data/lib/carray/frame/sort.rb +57 -9
- data/lib/carray/frame/verbs.rb +384 -49
- data/lib/carray/frame/windows.rb +44 -6
- data/lib/carray/fuse_source.rb +31 -2
- data/lib/carray/inspect.rb +7 -2
- data/lib/carray/iterator.rb +32 -0
- data/lib/carray/lazy.rb +52 -8
- data/lib/carray/mask_gap_fill.rb +37 -17
- data/lib/carray/meld_reduce.rb +1 -1
- data/lib/carray/methods/choose.rb +3 -2
- data/lib/carray/methods/is_in.rb +8 -1
- data/lib/carray/methods/meshgrid.rb +3 -1
- data/lib/carray/runtime.rb +47 -8
- data/lib/carray/segment_iterator.rb +35 -16
- data/lib/carray/serialize.rb +53 -19
- data/lib/carray/stack.rb +33 -18
- data/lib/carray/string.rb +8 -0
- data/lib/carray/string_operation_extension.rb +13 -5
- data/lib/carray/struct_builder.rb +5 -3
- data/lib/carray/time.rb +112 -68
- data/lib/carray/window_iterator.rb +16 -4
- data/yard-stubs/ca_obj_array.rb +26 -1
- data/yard-stubs/ca_obj_transpose.rb +5 -4
- data/yard-stubs/carray_attribute.rb +3 -1
- data/yard-stubs/carray_cast.rb +10 -5
- data/yard-stubs/carray_conversion.rb +13 -3
- data/yard-stubs/carray_element.rb +7 -6
- data/yard-stubs/carray_mask.rb +28 -4
- data/yard-stubs/carray_math.rb +45 -20
- data/yard-stubs/carray_memory_view.rb +7 -0
- data/yard-stubs/carray_order.rb +100 -4
- data/yard-stubs/carray_scatter.rb +7 -5
- data/yard-stubs/carray_test.rb +7 -4
- metadata +18 -4
data/ext/ca_obj_monop.c
CHANGED
|
@@ -370,12 +370,19 @@ apply_monop_in_place (uint16_t op_id, int8_t cur_dt,
|
|
|
370
370
|
fn(slab_n, mask, (char *) data, 1, (char *) data, 1);
|
|
371
371
|
}
|
|
372
372
|
|
|
373
|
-
/*
|
|
374
|
-
ca_cast_block
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
373
|
+
/* A zeroed CArray carrying only data_type and bytes, handed to
|
|
374
|
+
ca_cast_block in place of a real array. It is a whole CArray, not a
|
|
375
|
+
shorter struct cast to one: ca_cast_block reads data_type through a
|
|
376
|
+
CArray pointer, and a fixlen cast reads bytes as well. */
|
|
377
|
+
static CArray
|
|
378
|
+
ca_data_type_stub (int8_t data_type)
|
|
379
|
+
{
|
|
380
|
+
CArray stub;
|
|
381
|
+
memset(&stub, 0, sizeof(stub));
|
|
382
|
+
stub.data_type = data_type;
|
|
383
|
+
stub.bytes = ca_sizeof[data_type];
|
|
384
|
+
return stub;
|
|
385
|
+
}
|
|
379
386
|
|
|
380
387
|
/* Pull leaf into the output buffer. Three paths:
|
|
381
388
|
|
|
@@ -428,8 +435,8 @@ pull_leaf_with_optional_cast (CArray *leaf, uint16_t innermost_op,
|
|
|
428
435
|
if ( has_cast_at_leaf ) {
|
|
429
436
|
/* Path 2: cast scratch → data with output layout in target data_type. */
|
|
430
437
|
int8_t target_dt = (int8_t)(innermost_op - CA_MONOP_CAST_BASE);
|
|
431
|
-
|
|
432
|
-
|
|
438
|
+
CArray src_stub = ca_data_type_stub(leaf->data_type);
|
|
439
|
+
CArray dst_stub = ca_data_type_stub(target_dt);
|
|
433
440
|
ca_cast_block(slab_n, &src_stub, scratch, &dst_stub, data);
|
|
434
441
|
result_dt = target_dt;
|
|
435
442
|
} else {
|
|
@@ -446,8 +453,9 @@ pull_leaf_with_optional_cast (CArray *leaf, uint16_t innermost_op,
|
|
|
446
453
|
/* Evaluate (GET) or write (PUT, writable views only) the cells a request
|
|
447
454
|
names; `data` holds them packed. */
|
|
448
455
|
static void
|
|
449
|
-
|
|
456
|
+
ca_monop_eval_cells (void *node, const ca_lazy_req_t *req, void *data, int dir)
|
|
450
457
|
{
|
|
458
|
+
CAMonOp *mo = (CAMonOp *) node;
|
|
451
459
|
uint16_t op_chain[CA_MAX_LAZY_DEPTH];
|
|
452
460
|
int chain_len = 0;
|
|
453
461
|
CArray *leaf;
|
|
@@ -555,6 +563,30 @@ ca_monop_eval (CAMonOp *mo, const ca_lazy_req_t *req, void *data, int dir)
|
|
|
555
563
|
chain_apply_count = chain_len - 1;
|
|
556
564
|
}
|
|
557
565
|
|
|
566
|
+
/* The chain runs in place, so the buffer must hold slab_n cells of the
|
|
567
|
+
widest type along it: the leaf, or a step that widens before a later
|
|
568
|
+
one narrows (cmplx128.lazy.abs pulls 16-byte cells and answers 8-byte
|
|
569
|
+
ones). When that is wider than the output, run in a scratch and copy
|
|
570
|
+
the answer into `data` at the end. */
|
|
571
|
+
void *out = data;
|
|
572
|
+
void *work = NULL;
|
|
573
|
+
{
|
|
574
|
+
ca_size_t max_bytes = leaf->bytes;
|
|
575
|
+
int8_t dt = leaf->data_type;
|
|
576
|
+
for ( i = chain_len - 1; i >= 0; i-- ) {
|
|
577
|
+
dt = ca_monop_is_cast(op_chain[i])
|
|
578
|
+
? (int8_t)(op_chain[i] - CA_MONOP_CAST_BASE)
|
|
579
|
+
: ca_lazy_promote_monop(op_chain[i], dt);
|
|
580
|
+
if ( ca_sizeof[dt] > max_bytes ) max_bytes = ca_sizeof[dt];
|
|
581
|
+
}
|
|
582
|
+
if ( max_bytes > mo->bytes ) {
|
|
583
|
+
work = ( leaf->data_type == CA_OBJECT )
|
|
584
|
+
? ca_lazy_arena_acquire_object((slab_n * max_bytes + sizeof(VALUE) - 1) / sizeof(VALUE))
|
|
585
|
+
: ca_lazy_arena_acquire(slab_n * max_bytes);
|
|
586
|
+
data = work;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
558
590
|
/* === 2. pull leaf into output buffer (with optional cast) === */
|
|
559
591
|
cur_dt = pull_leaf_with_optional_cast(
|
|
560
592
|
leaf,
|
|
@@ -576,8 +608,8 @@ ca_monop_eval (CAMonOp *mo, const ca_lazy_req_t *req, void *data, int dir)
|
|
|
576
608
|
ca_size_t src_bytes = ca_sizeof[cur_dt];
|
|
577
609
|
ca_size_t total_bytes = slab_n * src_bytes;
|
|
578
610
|
void *scratch;
|
|
579
|
-
|
|
580
|
-
|
|
611
|
+
CArray src_stub = ca_data_type_stub(cur_dt);
|
|
612
|
+
CArray dst_stub = ca_data_type_stub(target_dt);
|
|
581
613
|
|
|
582
614
|
/* Copy current data → scratch (in src data_type), then cast scratch
|
|
583
615
|
→ data (in target data_type). Required because in-place cast
|
|
@@ -604,6 +636,30 @@ ca_monop_eval (CAMonOp *mo, const ca_lazy_req_t *req, void *data, int dir)
|
|
|
604
636
|
if ( slab_mask ) {
|
|
605
637
|
ca_lazy_arena_release(slab_mask);
|
|
606
638
|
}
|
|
639
|
+
if ( work ) {
|
|
640
|
+
memcpy(out, work, slab_n * mo->bytes);
|
|
641
|
+
ca_lazy_arena_release(work);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/* A chain is evaluated from its leaf without evaluating the nodes under
|
|
646
|
+
the top one, so those nodes are recorded with it: a cell that holds an
|
|
647
|
+
expression over one of them is caught when it comes back, before a
|
|
648
|
+
longer chain over the same cells is built. */
|
|
649
|
+
static void
|
|
650
|
+
ca_monop_eval (CAMonOp *mo, const ca_lazy_req_t *req, void *data, int dir)
|
|
651
|
+
{
|
|
652
|
+
CArray *chain[CA_MAX_LAZY_DEPTH];
|
|
653
|
+
CArray *leaf = mo->parent;
|
|
654
|
+
int nchain = 0;
|
|
655
|
+
while ( nchain < CA_MAX_LAZY_DEPTH &&
|
|
656
|
+
( leaf->obj_type == CA_OBJ_MONOP ||
|
|
657
|
+
leaf->obj_type == CA_OBJ_LAZY_MARKER ) ) {
|
|
658
|
+
chain[nchain++] = leaf;
|
|
659
|
+
leaf = ((CAView *) leaf)->parent;
|
|
660
|
+
}
|
|
661
|
+
ca_lazy_eval_recorded(mo, 1, &leaf, nchain, chain, ca_monop_eval_cells,
|
|
662
|
+
req, data, dir);
|
|
607
663
|
}
|
|
608
664
|
|
|
609
665
|
static void
|
|
@@ -741,7 +797,7 @@ ca_monop_func_sync (void *ap)
|
|
|
741
797
|
if ( ca->mask ) {
|
|
742
798
|
ca_cast_block_with_mask(ca->elements, ca, ca->ptr,
|
|
743
799
|
ca->parent, ca->parent->ptr,
|
|
744
|
-
(boolean8_t *) ca->
|
|
800
|
+
(boolean8_t *) ca->mask->ptr);
|
|
745
801
|
}
|
|
746
802
|
else {
|
|
747
803
|
ca_cast_block(ca->elements, ca, ca->ptr, ca->parent, ca->parent->ptr);
|
|
@@ -755,7 +811,9 @@ ca_monop_func_sync (void *ap)
|
|
|
755
811
|
ca_byte_swap_buffer(ca->data_type, ca->bytes, ca->elements,
|
|
756
812
|
ca->parent->ptr);
|
|
757
813
|
}
|
|
814
|
+
ca_view_pass_mask_to_parent(ca);
|
|
758
815
|
ca_sync(ca->parent);
|
|
816
|
+
ca_view_sync_mask_after_parent(ca);
|
|
759
817
|
}
|
|
760
818
|
|
|
761
819
|
static void
|
|
@@ -812,6 +870,21 @@ ca_monop_func_create_mask (void *ap)
|
|
|
812
870
|
}
|
|
813
871
|
}
|
|
814
872
|
|
|
873
|
+
/* For a writable cast: 1 and the data type it writes its parent in. 0 for
|
|
874
|
+
any other array. */
|
|
875
|
+
int
|
|
876
|
+
ca_monop_writable_cast_target (CArray *ca, int8_t *target)
|
|
877
|
+
{
|
|
878
|
+
CAMonOp *mo = (CAMonOp *) ca;
|
|
879
|
+
if ( ca->obj_type != CA_OBJ_MONOP
|
|
880
|
+
|| ! ca_monop_is_writable_view(mo->op_id)
|
|
881
|
+
|| ! ca_monop_is_cast(mo->op_id) ) {
|
|
882
|
+
return 0;
|
|
883
|
+
}
|
|
884
|
+
*target = mo->parent->data_type;
|
|
885
|
+
return 1;
|
|
886
|
+
}
|
|
887
|
+
|
|
815
888
|
ca_operation_function_t ca_monop_func = {
|
|
816
889
|
-1, /* CA_OBJ_MONOP, set at install time */
|
|
817
890
|
CA_VIEW_ARRAY,
|
|
@@ -1116,6 +1189,7 @@ Init_ca_obj_monop (void)
|
|
|
1116
1189
|
rb_define_const(rb_cCAMonOp, "OP_RAD_PI", INT2NUM(CA_MONOP_RAD_PI));
|
|
1117
1190
|
rb_define_const(rb_cCAMonOp, "OP_SIGN", INT2NUM(CA_MONOP_SIGN));
|
|
1118
1191
|
rb_define_const(rb_cCAMonOp, "OP_ARG_I", INT2NUM(CA_MONOP_ARG_I));
|
|
1192
|
+
rb_define_const(rb_cCAMonOp, "OP_BYTE_SWAP", INT2NUM(CA_MONOP_BYTE_SWAP));
|
|
1119
1193
|
rb_define_const(rb_cCAMonOp, "CAST_BASE", INT2NUM(CA_MONOP_CAST_BASE));
|
|
1120
1194
|
|
|
1121
1195
|
rb_define_alloc_func(rb_cCAMonOp, rb_ca_monop_s_allocate);
|
data/ext/ca_obj_object.c
CHANGED
|
@@ -171,18 +171,66 @@ ca_object_empty_wrap (CArray *w)
|
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
+
/* Moves the cells of a wrap (or of its lent mask, `w`) into an array of
|
|
175
|
+
their own, held by `holder` under `name`, and points `w` at them. The
|
|
176
|
+
cells are those the hook left in the caller's buffer. */
|
|
177
|
+
static void
|
|
178
|
+
ca_object_keep_cells (VALUE holder, const char *name, CArray *w)
|
|
179
|
+
{
|
|
180
|
+
volatile VALUE cells;
|
|
181
|
+
CArray *c;
|
|
182
|
+
cells = rb_carray_new(w->data_type, w->ndim, w->dim, w->bytes, NULL);
|
|
183
|
+
c = (CArray *) DATA_PTR(cells);
|
|
184
|
+
memcpy(c->ptr, w->ptr, (size_t) w->elements * w->bytes);
|
|
185
|
+
rb_ivar_set(holder, rb_intern(name), cells);
|
|
186
|
+
w->ptr = c->ptr;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* Takes the caller's buffer back from a wrap the hook was lent, together
|
|
190
|
+
with `lent_mask`, the wrap of the data's mask bits it was lent with (or
|
|
191
|
+
NULL). When the hook took a view of either, the view outlives the call,
|
|
192
|
+
so both keep their cells; otherwise both are left with none. A mask the
|
|
193
|
+
hook made on the wrap is the wrap's own, with cells of its own. */
|
|
194
|
+
static void
|
|
195
|
+
ca_object_take_back (VALUE wrap, CArray *lent_mask)
|
|
196
|
+
{
|
|
197
|
+
CArray *w = (CArray *) DATA_PTR(wrap);
|
|
198
|
+
CArray *m = ( lent_mask != NULL && w->mask == lent_mask ) ? lent_mask : NULL;
|
|
199
|
+
int viewed = ca_test_flag(w, CA_FLAG_LENT_VIEWED) ||
|
|
200
|
+
( m != NULL && ca_test_flag(m, CA_FLAG_LENT_VIEWED) );
|
|
201
|
+
ca_unset_flag(w, CA_FLAG_LENT | CA_FLAG_LENT_VIEWED);
|
|
202
|
+
if ( lent_mask != NULL ) {
|
|
203
|
+
ca_unset_flag(lent_mask, CA_FLAG_LENT | CA_FLAG_LENT_VIEWED);
|
|
204
|
+
}
|
|
205
|
+
if ( viewed ) {
|
|
206
|
+
ca_object_keep_cells(wrap, "kept_cells", w);
|
|
207
|
+
if ( m != NULL ) {
|
|
208
|
+
ca_object_keep_cells(wrap, "kept_mask_cells", m);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
if ( m != NULL ) {
|
|
213
|
+
ca_object_empty_wrap(m);
|
|
214
|
+
}
|
|
215
|
+
ca_object_empty_wrap(w);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
174
219
|
/* Calls copy_data / sync_data with a wrap of a caller's buffer, shaped as
|
|
175
|
-
the object, and takes the buffer back however the call leaves.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
220
|
+
the object, and takes the buffer back however the call leaves. When the
|
|
221
|
+
data has a mask, the wrap's mask is a wrap of its bits: a hook that writes
|
|
222
|
+
masked values masks the object's cells, and the data keeps its mask
|
|
223
|
+
whatever the hook does with the wrap's. A mask the hook makes on a wrap
|
|
224
|
+
that had none is the wrap's own and goes with it. The data points at the
|
|
225
|
+
buffer for the duration too, for a hook that reads the object back while
|
|
226
|
+
it runs. */
|
|
180
227
|
|
|
181
228
|
typedef struct {
|
|
182
229
|
CAObject *ca;
|
|
183
230
|
char *saved;
|
|
184
231
|
ID mid;
|
|
185
232
|
VALUE wrap;
|
|
233
|
+
CArray *mask; /* the wrap of the data's mask bits, or NULL */
|
|
186
234
|
} ca_object_lend_t;
|
|
187
235
|
|
|
188
236
|
static VALUE
|
|
@@ -196,15 +244,12 @@ static VALUE
|
|
|
196
244
|
ca_object_lend_restore (VALUE varg)
|
|
197
245
|
{
|
|
198
246
|
ca_object_lend_t *l = (ca_object_lend_t *) varg;
|
|
199
|
-
CArray *w = (CArray *) DATA_PTR(l->wrap);
|
|
200
|
-
l->ca->data->mask = w->mask;
|
|
201
|
-
w->mask = NULL;
|
|
202
|
-
ca_object_empty_wrap(w);
|
|
203
247
|
l->ca->data->ptr = l->saved;
|
|
248
|
+
ca_object_take_back(l->wrap, l->mask);
|
|
204
249
|
return Qnil;
|
|
205
250
|
}
|
|
206
251
|
|
|
207
|
-
static
|
|
252
|
+
static VALUE
|
|
208
253
|
ca_object_call_lending (CAObject *ca, void *ptr, const char *mid)
|
|
209
254
|
{
|
|
210
255
|
ca_object_lend_t l;
|
|
@@ -213,14 +258,21 @@ ca_object_call_lending (CAObject *ca, void *ptr, const char *mid)
|
|
|
213
258
|
w = ca_wrap_new(data->data_type, data->ndim, data->dim, data->bytes,
|
|
214
259
|
NULL, (char *) ptr);
|
|
215
260
|
l.wrap = ca_wrap_struct(w);
|
|
216
|
-
|
|
261
|
+
l.mask = NULL;
|
|
262
|
+
if ( data->mask != NULL ) {
|
|
263
|
+
l.mask = (CArray *) ca_wrap_new(CA_BOOLEAN, data->ndim, data->dim, 0,
|
|
264
|
+
NULL, data->mask->ptr);
|
|
265
|
+
ca_set_flag(l.mask, CA_FLAG_LENT);
|
|
266
|
+
w->mask = l.mask;
|
|
267
|
+
}
|
|
268
|
+
ca_set_flag(w, CA_FLAG_LENT);
|
|
217
269
|
l.ca = ca;
|
|
218
270
|
l.saved = data->ptr;
|
|
219
271
|
l.mid = rb_intern(mid);
|
|
220
272
|
data->ptr = (char *) ptr;
|
|
221
273
|
rb_ensure(ca_object_lend_body, (VALUE) &l,
|
|
222
274
|
ca_object_lend_restore, (VALUE) &l);
|
|
223
|
-
|
|
275
|
+
return l.wrap;
|
|
224
276
|
}
|
|
225
277
|
|
|
226
278
|
/* Calls recv.mid(*argv), where some of argv are transient wraps of a
|
|
@@ -253,7 +305,7 @@ ca_object_hook_release (VALUE varg)
|
|
|
253
305
|
ca_object_hook_t *h = (ca_object_hook_t *) varg;
|
|
254
306
|
int i;
|
|
255
307
|
for ( i = 0; i < h->ntrans; i++ ) {
|
|
256
|
-
|
|
308
|
+
ca_object_take_back(h->trans[i], NULL);
|
|
257
309
|
}
|
|
258
310
|
return Qnil;
|
|
259
311
|
}
|
|
@@ -742,7 +794,6 @@ ca_object_setup (CAObject *ca,
|
|
|
742
794
|
int8_t data_type, int8_t ndim, ca_size_t *dim, ca_size_t bytes)
|
|
743
795
|
{
|
|
744
796
|
ca_size_t elements;
|
|
745
|
-
int8_t i;
|
|
746
797
|
|
|
747
798
|
/* check arguments */
|
|
748
799
|
|
|
@@ -838,11 +889,10 @@ ca_object_mask_bytes (CAObject *ca)
|
|
|
838
889
|
return (boolean8_t *) ca->mask->ptr;
|
|
839
890
|
}
|
|
840
891
|
|
|
841
|
-
/* Whether the mask of this object comes from its mask_* hooks.
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
value fetched clears the cell. */
|
|
892
|
+
/* Whether the mask of this object comes from its mask_* hooks. A fetched
|
|
893
|
+
value says nothing about the mask either way: a cell masked -- by the
|
|
894
|
+
hooks, or without hooks by a write of UNDEF or `mask =` -- stays masked
|
|
895
|
+
when its value is read, and only a fetcher answering UNDEF marks one. */
|
|
846
896
|
static int
|
|
847
897
|
ca_object_mask_from_hooks (CAObject *ca)
|
|
848
898
|
{
|
|
@@ -871,9 +921,6 @@ ca_object_xfer_addr_one (CAObject *ca, ca_size_t addr, void *ptr, int dir)
|
|
|
871
921
|
}
|
|
872
922
|
}
|
|
873
923
|
else {
|
|
874
|
-
if ( ! ca_object_mask_from_hooks(ca) && ca_has_mask(ca) ) {
|
|
875
|
-
ca_object_mask_bytes(ca)[addr] = 0;
|
|
876
|
-
}
|
|
877
924
|
rb_ca_obj2ptr(ca->self, rval, ptr);
|
|
878
925
|
}
|
|
879
926
|
}
|
|
@@ -893,9 +940,6 @@ ca_object_xfer_addr_one (CAObject *ca, ca_size_t addr, void *ptr, int dir)
|
|
|
893
940
|
}
|
|
894
941
|
}
|
|
895
942
|
else {
|
|
896
|
-
if ( ! ca_object_mask_from_hooks(ca) && ca_has_mask(ca) ) {
|
|
897
|
-
ca_object_mask_bytes(ca)[ca_index2addr(ca->mask, idx)] = 0;
|
|
898
|
-
}
|
|
899
943
|
rb_ca_obj2ptr(ca->self, rval, ptr);
|
|
900
944
|
}
|
|
901
945
|
}
|
|
@@ -939,9 +983,6 @@ ca_object_xfer_index_one (CAObject *ca, ca_size_t *idx, void *ptr, int dir)
|
|
|
939
983
|
}
|
|
940
984
|
}
|
|
941
985
|
else {
|
|
942
|
-
if ( ! ca_object_mask_from_hooks(ca) && ca_has_mask(ca) ) {
|
|
943
|
-
ca_object_mask_bytes(ca)[ca_index2addr(ca->mask, idx)] = 0;
|
|
944
|
-
}
|
|
945
986
|
rb_ca_obj2ptr(ca->self, rval, ptr);
|
|
946
987
|
}
|
|
947
988
|
}
|
|
@@ -957,9 +998,6 @@ ca_object_xfer_index_one (CAObject *ca, ca_size_t *idx, void *ptr, int dir)
|
|
|
957
998
|
}
|
|
958
999
|
}
|
|
959
1000
|
else {
|
|
960
|
-
if ( ! ca_object_mask_from_hooks(ca) && ca_has_mask(ca) ) {
|
|
961
|
-
ca_object_mask_bytes(ca)[addr] = 0;
|
|
962
|
-
}
|
|
963
1001
|
rb_ca_obj2ptr(ca->self, rval, ptr);
|
|
964
1002
|
}
|
|
965
1003
|
}
|
|
@@ -1043,6 +1081,34 @@ ca_object_copy_bare_restore (VALUE arg)
|
|
|
1043
1081
|
return Qnil;
|
|
1044
1082
|
}
|
|
1045
1083
|
|
|
1084
|
+
/* copy_data of an object with no mask yet: an UNDEF the call writes lands
|
|
1085
|
+
on a mask of the wrap's own, which the wrap takes away with it. The
|
|
1086
|
+
cells it marks are masked on the object, as a fetcher answering UNDEF
|
|
1087
|
+
masks its cell, so a bulk read and a cell read give the same answer. */
|
|
1088
|
+
static void
|
|
1089
|
+
ca_object_adopt_undef_cells (CAObject *ca, VALUE wrap)
|
|
1090
|
+
{
|
|
1091
|
+
CArray *wm = ((CArray *) DATA_PTR(wrap))->mask;
|
|
1092
|
+
boolean8_t *bits;
|
|
1093
|
+
ca_size_t i, n;
|
|
1094
|
+
if ( wm == NULL || wm->ptr == NULL ) {
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
n = ( wm->elements < ca->elements ) ? wm->elements : ca->elements;
|
|
1098
|
+
for ( i = 0; i < n && ! wm->ptr[i]; i++ ) {
|
|
1099
|
+
;
|
|
1100
|
+
}
|
|
1101
|
+
if ( i == n ) {
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
ca_create_mask(ca);
|
|
1105
|
+
bits = ca_object_mask_bytes(ca);
|
|
1106
|
+
for ( ; i < n; i++ ) {
|
|
1107
|
+
if ( wm->ptr[i] ) bits[i] = 1;
|
|
1108
|
+
}
|
|
1109
|
+
RB_GC_GUARD(wrap);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1046
1112
|
static void
|
|
1047
1113
|
ca_object_dispatch_copy (CAObject *ca, void *ptr)
|
|
1048
1114
|
{
|
|
@@ -1057,7 +1123,26 @@ ca_object_dispatch_copy (CAObject *ca, void *ptr)
|
|
|
1057
1123
|
ca_object_copy_bare_restore, (VALUE) &c);
|
|
1058
1124
|
return;
|
|
1059
1125
|
}
|
|
1060
|
-
|
|
1126
|
+
if ( ca->data->mask != NULL ) {
|
|
1127
|
+
/* Without hooks the mask is the object's own: a store of the values
|
|
1128
|
+
into the array copy_data is handed (d[] = src) would clear it, so
|
|
1129
|
+
the cells it held masked are masked again after the call, beside
|
|
1130
|
+
those the call marks. */
|
|
1131
|
+
CArray *m = ca->data->mask;
|
|
1132
|
+
volatile VALUE vkept = rb_str_new(m->ptr, (long) m->elements);
|
|
1133
|
+
const char *kept;
|
|
1134
|
+
ca_size_t i;
|
|
1135
|
+
ca_object_call_lending(ca, ptr, "copy_data");
|
|
1136
|
+
kept = RSTRING_PTR(vkept);
|
|
1137
|
+
m = ca->data->mask;
|
|
1138
|
+
for ( i = 0; m != NULL && i < m->elements; i++ ) {
|
|
1139
|
+
if ( kept[i] ) m->ptr[i] = 1;
|
|
1140
|
+
}
|
|
1141
|
+
RB_GC_GUARD(vkept);
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
ca_object_adopt_undef_cells(ca,
|
|
1145
|
+
ca_object_call_lending(ca, ptr, "copy_data"));
|
|
1061
1146
|
}
|
|
1062
1147
|
else {
|
|
1063
1148
|
ca_size_t addr;
|
|
@@ -1194,6 +1279,7 @@ ca_object_wrap_transient (int8_t data_type, ca_size_t bytes,
|
|
|
1194
1279
|
}
|
|
1195
1280
|
|
|
1196
1281
|
wrap = ca_wrap_new(data_type, ndim, dim, bytes, NULL, (char *) data);
|
|
1282
|
+
ca_set_flag(wrap, CA_FLAG_LENT);
|
|
1197
1283
|
return ca_wrap_struct(wrap);
|
|
1198
1284
|
}
|
|
1199
1285
|
|
data/ext/ca_obj_record.c
CHANGED
|
@@ -309,6 +309,9 @@ rb_ca_record_s_wrap (int argc, VALUE *argv, VALUE klass)
|
|
|
309
309
|
rb_scan_args(argc, argv, "2", &entity_val, &data_class);
|
|
310
310
|
}
|
|
311
311
|
rb_check_carray_object(entity_val);
|
|
312
|
+
/* A record array (or another Face) is wrapped from its storage, so the
|
|
313
|
+
new record is not a Face over a Face. */
|
|
314
|
+
entity_val = rb_ca_strip_face_value(entity_val);
|
|
312
315
|
return ca_record_build(klass, data_class, entity_val);
|
|
313
316
|
}
|
|
314
317
|
|
data/ext/ca_obj_reduce.c
CHANGED
|
@@ -56,6 +56,10 @@ ca_reduce_setup (CAReduce *ca, CArray *parent, ca_size_t count, ca_size_t offset
|
|
|
56
56
|
"[BUG] CAReduce can't inherit other than boolean array");
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
if ( count <= 0 ) {
|
|
60
|
+
rb_raise(rb_eArgError, "CAReduce: count should be positive");
|
|
61
|
+
}
|
|
62
|
+
|
|
59
63
|
/* ca == NULL asks for the checks above only, so that a constructor can
|
|
60
64
|
run them before it allocates. */
|
|
61
65
|
if ( ca == NULL ) {
|
|
@@ -123,7 +127,11 @@ ca_reduce_func_clone (void *ap)
|
|
|
123
127
|
/* addr-list logic lives in xfer_addrs: GET = OR-reduce `count` adjacent parent
|
|
124
128
|
bytes per addr; PUT = broadcast the value to all `count` parent cells.
|
|
125
129
|
fetch_addr/store_addr forward (n==1), xfer_index = index2addr then xfer_addrs.
|
|
126
|
-
|
|
130
|
+
|
|
131
|
+
A true PUT onto a cell that already reads true leaves the parent cells
|
|
132
|
+
as they are. Every sync of a view over a CAReduce sends back its cells,
|
|
133
|
+
changed or not; broadcasting an unchanged true would mask the parent
|
|
134
|
+
cells under it that were not masked. */
|
|
127
135
|
static void
|
|
128
136
|
ca_reduce_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
|
|
129
137
|
void *data, int dir)
|
|
@@ -143,6 +151,16 @@ ca_reduce_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
|
|
|
143
151
|
*(char*)cell = v;
|
|
144
152
|
}
|
|
145
153
|
else {
|
|
154
|
+
if ( *(char *) cell ) {
|
|
155
|
+
char q, v = 0;
|
|
156
|
+
for (i=0; i<ca->count; i++) {
|
|
157
|
+
ca_fetch_addr(ca->parent, addr*ca->count + i + ca->offset, &q);
|
|
158
|
+
if ( q ) { v = 1; break; }
|
|
159
|
+
}
|
|
160
|
+
if ( v ) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
146
164
|
for (i=0; i<ca->count; i++) {
|
|
147
165
|
ca_store_addr(ca->parent, addr*ca->count + i + ca->offset, cell);
|
|
148
166
|
}
|
data/ext/ca_obj_refer.c
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
#include "carray.h"
|
|
17
17
|
#include "carray_internal.h" /* per-obj_type view constructors */
|
|
18
18
|
#include "ca_obj_face.h" /* CA_FACE_LIFT_IF_FACE for CARecord parent */
|
|
19
|
+
#include "ruby/thread_native.h"
|
|
19
20
|
|
|
20
21
|
extern ca_operation_function_t ca_stride_func;
|
|
21
22
|
|
|
@@ -145,7 +146,10 @@ ca_refer_setup (CARefer *ca, CArray *parent,
|
|
|
145
146
|
ca_stride_setup_view((CAStride *) ca, CA_OBJ_REFER, parent,
|
|
146
147
|
data_type, bytes, ndim, dim, strides, base_offset);
|
|
147
148
|
|
|
148
|
-
|
|
149
|
+
/* A refer of a scalar stays one only while it is one cell along one
|
|
150
|
+
axis, as a scalar is: read as narrower elements it is an array of
|
|
151
|
+
several, and read under another shape it is an array of that shape. */
|
|
152
|
+
if (ca_is_scalar(parent) && ca->elements == 1 && ca->ndim == 1) {
|
|
149
153
|
ca_set_flag(ca, CA_FLAG_SCALAR);
|
|
150
154
|
}
|
|
151
155
|
return 0;
|
|
@@ -211,16 +215,20 @@ ca_refer_func_create_mask (void *ap)
|
|
|
211
215
|
else if (ca->bytes < parent_bytes) {
|
|
212
216
|
/* Divided reinterpret: one parent element splits into
|
|
213
217
|
ratio = parent_bytes / bytes view elements. Build a
|
|
214
|
-
|
|
215
|
-
ratio sub-positions, then
|
|
216
|
-
actual shape.
|
|
218
|
+
repeat of two axes, (parent elements, ratio), that broadcasts
|
|
219
|
+
each parent mask bit across the ratio sub-positions, then
|
|
220
|
+
refer-reshape it to the view's actual shape. Two axes whatever
|
|
221
|
+
the parent's rank, so a parent at CA_RANK_MAX needs no extra
|
|
222
|
+
one. */
|
|
217
223
|
ca_size_t ratio = parent_bytes / ca->bytes;
|
|
218
|
-
ca_size_t
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
224
|
+
ca_size_t mdim[2], mstrides[2];
|
|
225
|
+
mdim[0] = ca->parent->elements;
|
|
226
|
+
mdim[1] = ratio;
|
|
227
|
+
mstrides[0] = 1;
|
|
228
|
+
mstrides[1] = 0;
|
|
222
229
|
ca->mask0 =
|
|
223
|
-
(CArray *)
|
|
230
|
+
(CArray *) ca_stride_new(CA_OBJ_REPEAT, ca->parent->mask, CA_BOOLEAN, 1,
|
|
231
|
+
2, mdim, mstrides, 0);
|
|
224
232
|
ca_unset_flag(ca->mask0, CA_FLAG_READ_ONLY);
|
|
225
233
|
/* mask0 has ratio cells per parent element, so the offset is counted
|
|
226
234
|
in those. */
|
|
@@ -330,6 +338,59 @@ ca_keep_chain_flags (CArray *view, CArray *src)
|
|
|
330
338
|
}
|
|
331
339
|
}
|
|
332
340
|
|
|
341
|
+
/* The sources of folded views (CA_FLAG_FOLDED), keyed by the view. Views are
|
|
342
|
+
made and freed in any Ractor, so the table is taken under a lock. */
|
|
343
|
+
static st_table *ca_fold_sources;
|
|
344
|
+
static rb_nativethread_lock_t ca_fold_lock;
|
|
345
|
+
|
|
346
|
+
void
|
|
347
|
+
ca_fold_note_source (CArray *view, CArray *src)
|
|
348
|
+
{
|
|
349
|
+
rb_native_mutex_lock(&ca_fold_lock);
|
|
350
|
+
st_insert(ca_fold_sources, (st_data_t) view, (st_data_t) src);
|
|
351
|
+
rb_native_mutex_unlock(&ca_fold_lock);
|
|
352
|
+
ca_set_flag(view, CA_FLAG_FOLDED);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
CArray *
|
|
356
|
+
ca_fold_source (CArray *view)
|
|
357
|
+
{
|
|
358
|
+
st_data_t src = 0;
|
|
359
|
+
rb_native_mutex_lock(&ca_fold_lock);
|
|
360
|
+
st_lookup(ca_fold_sources, (st_data_t) view, &src);
|
|
361
|
+
rb_native_mutex_unlock(&ca_fold_lock);
|
|
362
|
+
return (CArray *) src;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
void
|
|
366
|
+
ca_fold_forget (CArray *view)
|
|
367
|
+
{
|
|
368
|
+
st_data_t key = (st_data_t) view;
|
|
369
|
+
rb_native_mutex_lock(&ca_fold_lock);
|
|
370
|
+
st_delete(ca_fold_sources, &key, NULL);
|
|
371
|
+
rb_native_mutex_unlock(&ca_fold_lock);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/* Builds the folded view of `ca` for reshape / flatten: a CAStride over
|
|
375
|
+
`root`. When `ca` is not `root` itself, it is recorded as the view's
|
|
376
|
+
source. */
|
|
377
|
+
static VALUE
|
|
378
|
+
ca_reshape_folded (VALUE self, CArray *ca, CArray *root, int8_t ndim,
|
|
379
|
+
ca_size_t *dim, ca_size_t *strides, ca_size_t base)
|
|
380
|
+
{
|
|
381
|
+
volatile VALUE obj;
|
|
382
|
+
CAStride *cs = ca_stride_new(CA_OBJ_STRIDE, root, ca->data_type, ca->bytes,
|
|
383
|
+
ndim, dim, strides, base);
|
|
384
|
+
ca_keep_chain_flags((CArray *) cs, ca);
|
|
385
|
+
obj = ca_wrap_struct(cs);
|
|
386
|
+
rb_ca_set_parent(obj, self);
|
|
387
|
+
if ( root != ca ) {
|
|
388
|
+
ca_fold_note_source((CArray *) cs, ca);
|
|
389
|
+
}
|
|
390
|
+
CA_WRAPPER_LIFT(obj, self, ca);
|
|
391
|
+
return obj;
|
|
392
|
+
}
|
|
393
|
+
|
|
333
394
|
static int
|
|
334
395
|
ca_reshape_try_strides (CArray *parent,
|
|
335
396
|
const ca_size_t *new_dim,
|
|
@@ -619,6 +680,9 @@ rb_ca_reshape (int argc, VALUE *argv, VALUE self)
|
|
|
619
680
|
|
|
620
681
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
621
682
|
|
|
683
|
+
if ( argc == 0 ) {
|
|
684
|
+
rb_raise(rb_eArgError, "reshape: give at least one axis");
|
|
685
|
+
}
|
|
622
686
|
ca_check_rank_fits(argc, "reshape");
|
|
623
687
|
|
|
624
688
|
/* Locate the (at most one) auto-infer placeholder. `-1` is the
|
|
@@ -712,15 +776,8 @@ rb_ca_reshape (int argc, VALUE *argv, VALUE self)
|
|
|
712
776
|
ca_size_t out_base;
|
|
713
777
|
if (ca_reshape_try_strides(ca, new_dim, (int8_t) argc,
|
|
714
778
|
&out_root, out_strides, &out_base)) {
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
(int8_t) argc, new_dim,
|
|
718
|
-
out_strides, out_base);
|
|
719
|
-
ca_keep_chain_flags((CArray *) cs, ca);
|
|
720
|
-
obj = ca_wrap_struct(cs);
|
|
721
|
-
rb_ca_set_parent(obj, self);
|
|
722
|
-
CA_WRAPPER_LIFT(obj, self, ca);
|
|
723
|
-
return obj;
|
|
779
|
+
return ca_reshape_folded(self, ca, out_root, (int8_t) argc, new_dim,
|
|
780
|
+
out_strides, out_base);
|
|
724
781
|
}
|
|
725
782
|
}
|
|
726
783
|
|
|
@@ -751,14 +808,8 @@ rb_ca_flatten (VALUE self)
|
|
|
751
808
|
ca_size_t out_strides[1];
|
|
752
809
|
ca_size_t out_base;
|
|
753
810
|
if (ca_reshape_try_strides(ca, dim, 1, &out_root, out_strides, &out_base)) {
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
1, dim, out_strides, out_base);
|
|
757
|
-
ca_keep_chain_flags((CArray *) cs, ca);
|
|
758
|
-
obj = ca_wrap_struct(cs);
|
|
759
|
-
rb_ca_set_parent(obj, self);
|
|
760
|
-
CA_WRAPPER_LIFT(obj, self, ca);
|
|
761
|
-
return obj;
|
|
811
|
+
return ca_reshape_folded(self, ca, out_root, 1, dim,
|
|
812
|
+
out_strides, out_base);
|
|
762
813
|
}
|
|
763
814
|
}
|
|
764
815
|
|
|
@@ -838,6 +889,9 @@ Init_ca_obj_refer (void)
|
|
|
838
889
|
/* rb_cCARefer, CA_OBJ_REFER are defined in ruby_carray.c /
|
|
839
890
|
carray_core.c. Build the custom op table and override the
|
|
840
891
|
baseline registration installed by Init_carray_core. */
|
|
892
|
+
ca_fold_sources = st_init_numtable();
|
|
893
|
+
rb_native_mutex_initialize(&ca_fold_lock);
|
|
894
|
+
|
|
841
895
|
ca_refer_func = ca_stride_func;
|
|
842
896
|
ca_refer_func.free_object = free_ca_refer;
|
|
843
897
|
ca_refer_func.clone = ca_refer_func_clone;
|