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.
Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +438 -0
  3. data/README.md +1 -1
  4. data/carray.gemspec +3 -2
  5. data/ext/ca_array_pool.c +25 -1
  6. data/ext/ca_axis_dispatch.c +19 -20
  7. data/ext/ca_axis_group.c +1 -1
  8. data/ext/ca_categorical_iterator.c +34 -27
  9. data/ext/ca_compare.c +4 -4
  10. data/ext/ca_composite_dispatch.c +9 -9
  11. data/ext/ca_kernel_iterator.c +122 -237
  12. data/ext/ca_kernel_iterator.h +522 -337
  13. data/ext/ca_obj_array.c +69 -19
  14. data/ext/ca_obj_bincmp.c +10 -1
  15. data/ext/ca_obj_binop.c +11 -1
  16. data/ext/ca_obj_bitarray.c +5 -1
  17. data/ext/ca_obj_bitfield.c +60 -43
  18. data/ext/ca_obj_byte_swap.c +4 -1
  19. data/ext/ca_obj_const_string.c +15 -3
  20. data/ext/ca_obj_face.c +44 -1
  21. data/ext/ca_obj_fake.c +3 -1
  22. data/ext/ca_obj_field.c +13 -3
  23. data/ext/ca_obj_grid.c +219 -17
  24. data/ext/ca_obj_meld.c +69 -113
  25. data/ext/ca_obj_moncmp.c +9 -1
  26. data/ext/ca_obj_monop.c +86 -12
  27. data/ext/ca_obj_object.c +118 -32
  28. data/ext/ca_obj_record.c +3 -0
  29. data/ext/ca_obj_reduce.c +19 -1
  30. data/ext/ca_obj_refer.c +80 -26
  31. data/ext/ca_obj_remap.c +69 -10
  32. data/ext/ca_obj_roll.c +27 -6
  33. data/ext/ca_obj_select.c +38 -6
  34. data/ext/ca_obj_select_axis.c +1 -0
  35. data/ext/ca_obj_shift.c +32 -7
  36. data/ext/ca_obj_stack.c +311 -111
  37. data/ext/ca_obj_stride.c +159 -64
  38. data/ext/ca_obj_string.c +27 -2
  39. data/ext/ca_obj_tile.c +28 -7
  40. data/ext/ca_obj_transpose.c +8 -4
  41. data/ext/ca_obj_triop.c +12 -1
  42. data/ext/ca_obj_window.c +80 -50
  43. data/ext/ca_op_ipower.c +12 -12
  44. data/ext/ca_sort_kernels.h +6 -3
  45. data/ext/caframe_csv_reader.c +1 -1
  46. data/ext/carray.h +35 -1
  47. data/ext/carray_access.c +43 -67
  48. data/ext/carray_address_basis.c +134 -0
  49. data/ext/carray_attribute.c +32 -0
  50. data/ext/carray_broadcast.c +3 -4
  51. data/ext/carray_build_flags.h +1 -1
  52. data/ext/carray_cast.c +251 -93
  53. data/ext/carray_cast_func.rb +56 -26
  54. data/ext/carray_conversion.c +11 -7
  55. data/ext/carray_core.c +814 -72
  56. data/ext/carray_count.c +6 -0
  57. data/ext/carray_element.c +22 -13
  58. data/ext/carray_factorize.c +20 -20
  59. data/ext/carray_generate.c +8 -2
  60. data/ext/carray_histogram.c +26 -4
  61. data/ext/carray_index_classifier.c +4 -4
  62. data/ext/carray_internal.h +159 -12
  63. data/ext/carray_kernels_bincmp.c +2174 -170
  64. data/ext/carray_kernels_binop.c +25653 -5487
  65. data/ext/carray_kernels_init.c +8 -8
  66. data/ext/carray_kernels_moncmp.c +96 -96
  67. data/ext/carray_kernels_monop.c +12253 -5235
  68. data/ext/carray_kernels_reduce_aggregate.c +6150 -2423
  69. data/ext/carray_kernels_reduce_boolean.c +1533 -15
  70. data/ext/carray_kernels_reduce_cumulative.c +2442 -882
  71. data/ext/carray_kernels_reduce_extreme.c +5658 -1661
  72. data/ext/carray_kernels_reduce_variance.c +2534 -402
  73. data/ext/carray_kernels_scan.c +368 -368
  74. data/ext/carray_kernels_search.c +4551 -604
  75. data/ext/carray_kernels_sort.c +659 -751
  76. data/ext/carray_kernels_triop.c +114 -114
  77. data/ext/carray_key_windows.c +4 -0
  78. data/ext/carray_lazy.c +47 -0
  79. data/ext/carray_loop.c +1 -1
  80. data/ext/carray_mask.c +175 -161
  81. data/ext/carray_median_percentile.c +4 -4
  82. data/ext/carray_memory_view.c +15 -9
  83. data/ext/carray_operator.c +152 -49
  84. data/ext/carray_order.c +230 -10
  85. data/ext/carray_random.c +22 -19
  86. data/ext/carray_scatter.c +24 -7
  87. data/ext/carray_slab.c +128 -29
  88. data/ext/carray_sort.c +224 -16
  89. data/ext/carray_sort_kernel.c +5 -7
  90. data/ext/carray_struct.c +35 -43
  91. data/ext/carray_test.c +74 -19
  92. data/ext/carray_time_text.c +6 -4
  93. data/ext/carray_utils.c +63 -21
  94. data/ext/extconf.rb +11 -0
  95. data/ext/mkkernel.rb +546 -199
  96. data/ext/ruby_carray.c +4 -1
  97. data/ext/version.h +4 -4
  98. data/lib/carray/attribute.rb +42 -5
  99. data/lib/carray/autoload_carray.rb +3 -3
  100. data/lib/carray/autoload_method_extension.rb +5 -3
  101. data/lib/carray/axis_group.rb +13 -7
  102. data/lib/carray/basics.rb +28 -8
  103. data/lib/carray/block_iterator.rb +17 -3
  104. data/lib/carray/categorical.rb +35 -23
  105. data/lib/carray/categorical_iterator.rb +8 -8
  106. data/lib/carray/complex.rb +2 -2
  107. data/lib/carray/conditional.rb +3 -2
  108. data/lib/carray/const_string.rb +10 -0
  109. data/lib/carray/data_type_extension.rb +8 -1
  110. data/lib/carray/fixlen_string.rb +41 -2
  111. data/lib/carray/frame/column_verbs.rb +48 -16
  112. data/lib/carray/frame/concat.rb +17 -3
  113. data/lib/carray/frame/convert.rb +19 -0
  114. data/lib/carray/frame/csv_parser.rb +32 -32
  115. data/lib/carray/frame/describe.rb +4 -2
  116. data/lib/carray/frame/frame.rb +405 -85
  117. data/lib/carray/frame/group.rb +777 -64
  118. data/lib/carray/frame/io.rb +212 -72
  119. data/lib/carray/frame/join.rb +146 -37
  120. data/lib/carray/frame/records.rb +3 -3
  121. data/lib/carray/frame/reshape.rb +89 -11
  122. data/lib/carray/frame/sort.rb +57 -9
  123. data/lib/carray/frame/verbs.rb +384 -49
  124. data/lib/carray/frame/windows.rb +44 -6
  125. data/lib/carray/fuse_source.rb +31 -2
  126. data/lib/carray/inspect.rb +7 -2
  127. data/lib/carray/iterator.rb +32 -0
  128. data/lib/carray/lazy.rb +52 -8
  129. data/lib/carray/mask_gap_fill.rb +37 -17
  130. data/lib/carray/meld_reduce.rb +1 -1
  131. data/lib/carray/methods/choose.rb +3 -2
  132. data/lib/carray/methods/is_in.rb +8 -1
  133. data/lib/carray/methods/meshgrid.rb +3 -1
  134. data/lib/carray/runtime.rb +47 -8
  135. data/lib/carray/segment_iterator.rb +35 -16
  136. data/lib/carray/serialize.rb +53 -19
  137. data/lib/carray/stack.rb +33 -18
  138. data/lib/carray/string.rb +8 -0
  139. data/lib/carray/string_operation_extension.rb +13 -5
  140. data/lib/carray/struct_builder.rb +5 -3
  141. data/lib/carray/time.rb +112 -68
  142. data/lib/carray/window_iterator.rb +16 -4
  143. data/yard-stubs/ca_obj_array.rb +26 -1
  144. data/yard-stubs/ca_obj_transpose.rb +5 -4
  145. data/yard-stubs/carray_attribute.rb +3 -1
  146. data/yard-stubs/carray_cast.rb +10 -5
  147. data/yard-stubs/carray_conversion.rb +13 -3
  148. data/yard-stubs/carray_element.rb +7 -6
  149. data/yard-stubs/carray_mask.rb +28 -4
  150. data/yard-stubs/carray_math.rb +45 -20
  151. data/yard-stubs/carray_memory_view.rb +7 -0
  152. data/yard-stubs/carray_order.rb +100 -4
  153. data/yard-stubs/carray_scatter.rb +7 -5
  154. data/yard-stubs/carray_test.rb +7 -4
  155. 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
- /* Minimal CArray-shaped stub used as ca_cast_block's data_type-only carrier.
374
- ca_cast_block only reads data_type to index into ca_cast_func_table. */
375
- typedef struct {
376
- int16_t obj_type;
377
- int8_t data_type;
378
- } ca_data_type_stub_t;
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
- ca_data_type_stub_t src_stub = { 0, leaf->data_type };
432
- ca_data_type_stub_t dst_stub = { 0, target_dt };
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
- ca_monop_eval (CAMonOp *mo, const ca_lazy_req_t *req, void *data, int dir)
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
- ca_data_type_stub_t src_stub = { 0, cur_dt };
580
- ca_data_type_stub_t dst_stub = { 0, target_dt };
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->parent->mask->ptr);
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. The wrap
176
- shares the object's mask (the data's), and whatever mask it holds after
177
- the call becomes the data's: a hook that writes masked values masks the
178
- object's cells. The data points at the buffer for the duration too, for
179
- a hook that reads the object back while it runs. */
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 void
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
- w->mask = data->mask;
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
- RB_GC_GUARD(l.wrap);
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
- ca_object_empty_wrap((CArray *) DATA_PTR(h->trans[i]));
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. Then a
842
- fetched value says nothing about the mask: a cell the hooks mask stays
843
- masked when its value is read, and only a fetcher answering UNDEF marks
844
- one. Without hooks the mask is what the fetches have answered, so a
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
- ca_object_call_lending(ca, ptr, "copy_data");
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
- All forward to a single home; the legacy slots are removed at step 5. */
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
- if (ca_is_scalar(parent)) {
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
- CARepeat that broadcasts each parent mask bit across the
215
- ratio sub-positions, then refer-reshape it to the view's
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 count[CA_RANK_MAX];
219
- int8_t i;
220
- for (i = 0; i < ca->parent->ndim; i++) count[i] = 0;
221
- count[ca->parent->ndim] = ratio;
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 *) ca_repeat_new(ca->parent->mask, ca->parent->ndim + 1, count);
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
- CAStride *cs = ca_stride_new(CA_OBJ_STRIDE, out_root,
716
- ca->data_type, ca->bytes,
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
- CAStride *cs = ca_stride_new(CA_OBJ_STRIDE, out_root,
755
- ca->data_type, ca->bytes,
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;