carray 3.0.5 → 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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +233 -0
  3. data/README.md +1 -1
  4. data/carray.gemspec +3 -2
  5. data/ext/ca_axis_dispatch.c +6 -0
  6. data/ext/ca_axis_group.c +1 -1
  7. data/ext/ca_categorical_iterator.c +7 -7
  8. data/ext/ca_kernel_iterator.c +116 -231
  9. data/ext/ca_kernel_iterator.h +410 -254
  10. data/ext/ca_obj_bincmp.c +9 -1
  11. data/ext/ca_obj_binop.c +10 -1
  12. data/ext/ca_obj_bitfield.c +2 -0
  13. data/ext/ca_obj_byte_swap.c +2 -0
  14. data/ext/ca_obj_face.c +2 -0
  15. data/ext/ca_obj_fake.c +3 -1
  16. data/ext/ca_obj_grid.c +167 -12
  17. data/ext/ca_obj_meld.c +69 -113
  18. data/ext/ca_obj_moncmp.c +9 -1
  19. data/ext/ca_obj_monop.c +40 -2
  20. data/ext/ca_obj_object.c +54 -20
  21. data/ext/ca_obj_reduce.c +15 -1
  22. data/ext/ca_obj_refer.c +65 -20
  23. data/ext/ca_obj_remap.c +69 -10
  24. data/ext/ca_obj_roll.c +1 -0
  25. data/ext/ca_obj_select.c +4 -1
  26. data/ext/ca_obj_select_axis.c +1 -0
  27. data/ext/ca_obj_shift.c +1 -0
  28. data/ext/ca_obj_stack.c +310 -111
  29. data/ext/ca_obj_stride.c +131 -45
  30. data/ext/ca_obj_string.c +18 -2
  31. data/ext/ca_obj_tile.c +1 -0
  32. data/ext/ca_obj_triop.c +9 -1
  33. data/ext/ca_obj_window.c +1 -0
  34. data/ext/ca_op_ipower.c +8 -8
  35. data/ext/caframe_csv_reader.c +1 -1
  36. data/ext/carray.h +2 -1
  37. data/ext/carray_access.c +27 -64
  38. data/ext/carray_address_basis.c +134 -0
  39. data/ext/carray_attribute.c +8 -0
  40. data/ext/carray_broadcast.c +3 -4
  41. data/ext/carray_build_flags.h +1 -1
  42. data/ext/carray_cast.c +8 -11
  43. data/ext/carray_conversion.c +9 -5
  44. data/ext/carray_core.c +811 -72
  45. data/ext/carray_element.c +13 -9
  46. data/ext/carray_histogram.c +25 -3
  47. data/ext/carray_internal.h +147 -12
  48. data/ext/carray_kernels_bincmp.c +2007 -3
  49. data/ext/carray_kernels_binop.c +25365 -5199
  50. data/ext/carray_kernels_init.c +8 -8
  51. data/ext/carray_kernels_monop.c +10696 -3678
  52. data/ext/carray_kernels_reduce_aggregate.c +4429 -702
  53. data/ext/carray_kernels_reduce_boolean.c +1533 -15
  54. data/ext/carray_kernels_reduce_cumulative.c +1808 -248
  55. data/ext/carray_kernels_reduce_extreme.c +4508 -511
  56. data/ext/carray_kernels_reduce_variance.c +2284 -152
  57. data/ext/carray_kernels_scan.c +240 -240
  58. data/ext/carray_kernels_search.c +4211 -264
  59. data/ext/carray_kernels_sort.c +367 -450
  60. data/ext/carray_lazy.c +47 -0
  61. data/ext/carray_loop.c +1 -1
  62. data/ext/carray_mask.c +130 -139
  63. data/ext/carray_median_percentile.c +4 -4
  64. data/ext/carray_memory_view.c +13 -7
  65. data/ext/carray_operator.c +147 -44
  66. data/ext/carray_order.c +3 -2
  67. data/ext/carray_random.c +4 -1
  68. data/ext/carray_slab.c +97 -21
  69. data/ext/carray_sort.c +1 -1
  70. data/ext/carray_test.c +74 -19
  71. data/ext/carray_time_text.c +2 -2
  72. data/ext/extconf.rb +11 -0
  73. data/ext/mkkernel.rb +346 -94
  74. data/ext/ruby_carray.c +4 -1
  75. data/ext/version.h +4 -4
  76. data/lib/carray/attribute.rb +25 -1
  77. data/lib/carray/autoload_carray.rb +3 -3
  78. data/lib/carray/autoload_method_extension.rb +5 -3
  79. data/lib/carray/axis_group.rb +13 -7
  80. data/lib/carray/basics.rb +1 -1
  81. data/lib/carray/block_iterator.rb +17 -3
  82. data/lib/carray/categorical.rb +6 -8
  83. data/lib/carray/categorical_iterator.rb +2 -2
  84. data/lib/carray/complex.rb +2 -2
  85. data/lib/carray/conditional.rb +3 -2
  86. data/lib/carray/frame/column_verbs.rb +45 -14
  87. data/lib/carray/frame/convert.rb +19 -0
  88. data/lib/carray/frame/csv_parser.rb +32 -32
  89. data/lib/carray/frame/describe.rb +4 -2
  90. data/lib/carray/frame/frame.rb +399 -78
  91. data/lib/carray/frame/group.rb +679 -53
  92. data/lib/carray/frame/io.rb +209 -71
  93. data/lib/carray/frame/join.rb +146 -37
  94. data/lib/carray/frame/records.rb +3 -3
  95. data/lib/carray/frame/reshape.rb +89 -11
  96. data/lib/carray/frame/sort.rb +57 -9
  97. data/lib/carray/frame/verbs.rb +384 -49
  98. data/lib/carray/frame/windows.rb +44 -6
  99. data/lib/carray/inspect.rb +7 -2
  100. data/lib/carray/iterator.rb +32 -0
  101. data/lib/carray/lazy.rb +17 -8
  102. data/lib/carray/mask_gap_fill.rb +37 -17
  103. data/lib/carray/meld_reduce.rb +1 -1
  104. data/lib/carray/methods/choose.rb +3 -2
  105. data/lib/carray/methods/meshgrid.rb +3 -1
  106. data/lib/carray/runtime.rb +47 -8
  107. data/lib/carray/segment_iterator.rb +23 -8
  108. data/lib/carray/serialize.rb +34 -17
  109. data/lib/carray/stack.rb +18 -6
  110. data/lib/carray/time.rb +5 -0
  111. data/lib/carray/window_iterator.rb +16 -4
  112. data/yard-stubs/ca_obj_array.rb +26 -1
  113. data/yard-stubs/carray_cast.rb +8 -5
  114. data/yard-stubs/carray_conversion.rb +8 -2
  115. data/yard-stubs/carray_mask.rb +20 -1
  116. data/yard-stubs/carray_math.rb +31 -16
  117. data/yard-stubs/carray_memory_view.rb +7 -0
  118. data/yard-stubs/carray_test.rb +7 -4
  119. metadata +18 -4
data/ext/ca_obj_monop.c CHANGED
@@ -453,8 +453,9 @@ pull_leaf_with_optional_cast (CArray *leaf, uint16_t innermost_op,
453
453
  /* Evaluate (GET) or write (PUT, writable views only) the cells a request
454
454
  names; `data` holds them packed. */
455
455
  static void
456
- 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)
457
457
  {
458
+ CAMonOp *mo = (CAMonOp *) node;
458
459
  uint16_t op_chain[CA_MAX_LAZY_DEPTH];
459
460
  int chain_len = 0;
460
461
  CArray *leaf;
@@ -641,6 +642,26 @@ ca_monop_eval (CAMonOp *mo, const ca_lazy_req_t *req, void *data, int dir)
641
642
  }
642
643
  }
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);
663
+ }
664
+
644
665
  static void
645
666
  ca_monop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
646
667
  ca_size_t *strides, void *data, int dir)
@@ -776,7 +797,7 @@ ca_monop_func_sync (void *ap)
776
797
  if ( ca->mask ) {
777
798
  ca_cast_block_with_mask(ca->elements, ca, ca->ptr,
778
799
  ca->parent, ca->parent->ptr,
779
- (boolean8_t *) ca->parent->mask->ptr);
800
+ (boolean8_t *) ca->mask->ptr);
780
801
  }
781
802
  else {
782
803
  ca_cast_block(ca->elements, ca, ca->ptr, ca->parent, ca->parent->ptr);
@@ -790,7 +811,9 @@ ca_monop_func_sync (void *ap)
790
811
  ca_byte_swap_buffer(ca->data_type, ca->bytes, ca->elements,
791
812
  ca->parent->ptr);
792
813
  }
814
+ ca_view_pass_mask_to_parent(ca);
793
815
  ca_sync(ca->parent);
816
+ ca_view_sync_mask_after_parent(ca);
794
817
  }
795
818
 
796
819
  static void
@@ -847,6 +870,21 @@ ca_monop_func_create_mask (void *ap)
847
870
  }
848
871
  }
849
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
+
850
888
  ca_operation_function_t ca_monop_func = {
851
889
  -1, /* CA_OBJ_MONOP, set at install time */
852
890
  CA_VIEW_ARRAY,
data/ext/ca_obj_object.c CHANGED
@@ -249,7 +249,7 @@ ca_object_lend_restore (VALUE varg)
249
249
  return Qnil;
250
250
  }
251
251
 
252
- static void
252
+ static VALUE
253
253
  ca_object_call_lending (CAObject *ca, void *ptr, const char *mid)
254
254
  {
255
255
  ca_object_lend_t l;
@@ -272,7 +272,7 @@ ca_object_call_lending (CAObject *ca, void *ptr, const char *mid)
272
272
  data->ptr = (char *) ptr;
273
273
  rb_ensure(ca_object_lend_body, (VALUE) &l,
274
274
  ca_object_lend_restore, (VALUE) &l);
275
- RB_GC_GUARD(l.wrap);
275
+ return l.wrap;
276
276
  }
277
277
 
278
278
  /* Calls recv.mid(*argv), where some of argv are transient wraps of a
@@ -889,11 +889,10 @@ ca_object_mask_bytes (CAObject *ca)
889
889
  return (boolean8_t *) ca->mask->ptr;
890
890
  }
891
891
 
892
- /* Whether the mask of this object comes from its mask_* hooks. Then a
893
- fetched value says nothing about the mask: a cell the hooks mask stays
894
- masked when its value is read, and only a fetcher answering UNDEF marks
895
- one. Without hooks the mask is what the fetches have answered, so a
896
- 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. */
897
896
  static int
898
897
  ca_object_mask_from_hooks (CAObject *ca)
899
898
  {
@@ -922,9 +921,6 @@ ca_object_xfer_addr_one (CAObject *ca, ca_size_t addr, void *ptr, int dir)
922
921
  }
923
922
  }
924
923
  else {
925
- if ( ! ca_object_mask_from_hooks(ca) && ca_has_mask(ca) ) {
926
- ca_object_mask_bytes(ca)[addr] = 0;
927
- }
928
924
  rb_ca_obj2ptr(ca->self, rval, ptr);
929
925
  }
930
926
  }
@@ -944,9 +940,6 @@ ca_object_xfer_addr_one (CAObject *ca, ca_size_t addr, void *ptr, int dir)
944
940
  }
945
941
  }
946
942
  else {
947
- if ( ! ca_object_mask_from_hooks(ca) && ca_has_mask(ca) ) {
948
- ca_object_mask_bytes(ca)[ca_index2addr(ca->mask, idx)] = 0;
949
- }
950
943
  rb_ca_obj2ptr(ca->self, rval, ptr);
951
944
  }
952
945
  }
@@ -990,9 +983,6 @@ ca_object_xfer_index_one (CAObject *ca, ca_size_t *idx, void *ptr, int dir)
990
983
  }
991
984
  }
992
985
  else {
993
- if ( ! ca_object_mask_from_hooks(ca) && ca_has_mask(ca) ) {
994
- ca_object_mask_bytes(ca)[ca_index2addr(ca->mask, idx)] = 0;
995
- }
996
986
  rb_ca_obj2ptr(ca->self, rval, ptr);
997
987
  }
998
988
  }
@@ -1008,9 +998,6 @@ ca_object_xfer_index_one (CAObject *ca, ca_size_t *idx, void *ptr, int dir)
1008
998
  }
1009
999
  }
1010
1000
  else {
1011
- if ( ! ca_object_mask_from_hooks(ca) && ca_has_mask(ca) ) {
1012
- ca_object_mask_bytes(ca)[addr] = 0;
1013
- }
1014
1001
  rb_ca_obj2ptr(ca->self, rval, ptr);
1015
1002
  }
1016
1003
  }
@@ -1094,6 +1081,34 @@ ca_object_copy_bare_restore (VALUE arg)
1094
1081
  return Qnil;
1095
1082
  }
1096
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
+
1097
1112
  static void
1098
1113
  ca_object_dispatch_copy (CAObject *ca, void *ptr)
1099
1114
  {
@@ -1108,7 +1123,26 @@ ca_object_dispatch_copy (CAObject *ca, void *ptr)
1108
1123
  ca_object_copy_bare_restore, (VALUE) &c);
1109
1124
  return;
1110
1125
  }
1111
- 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"));
1112
1146
  }
1113
1147
  else {
1114
1148
  ca_size_t addr;
data/ext/ca_obj_reduce.c CHANGED
@@ -127,7 +127,11 @@ ca_reduce_func_clone (void *ap)
127
127
  /* addr-list logic lives in xfer_addrs: GET = OR-reduce `count` adjacent parent
128
128
  bytes per addr; PUT = broadcast the value to all `count` parent cells.
129
129
  fetch_addr/store_addr forward (n==1), xfer_index = index2addr then xfer_addrs.
130
- 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. */
131
135
  static void
132
136
  ca_reduce_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
133
137
  void *data, int dir)
@@ -147,6 +151,16 @@ ca_reduce_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
147
151
  *(char*)cell = v;
148
152
  }
149
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
+ }
150
164
  for (i=0; i<ca->count; i++) {
151
165
  ca_store_addr(ca->parent, addr*ca->count + i + ca->offset, cell);
152
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,9 +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
- /* A refer of a scalar stays one only while it has the one cell: read as
149
- narrower elements it is an array of several. */
150
- if (ca_is_scalar(parent) && ca->elements == 1) {
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) {
151
153
  ca_set_flag(ca, CA_FLAG_SCALAR);
152
154
  }
153
155
  return 0;
@@ -336,6 +338,59 @@ ca_keep_chain_flags (CArray *view, CArray *src)
336
338
  }
337
339
  }
338
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
+
339
394
  static int
340
395
  ca_reshape_try_strides (CArray *parent,
341
396
  const ca_size_t *new_dim,
@@ -721,15 +776,8 @@ rb_ca_reshape (int argc, VALUE *argv, VALUE self)
721
776
  ca_size_t out_base;
722
777
  if (ca_reshape_try_strides(ca, new_dim, (int8_t) argc,
723
778
  &out_root, out_strides, &out_base)) {
724
- CAStride *cs = ca_stride_new(CA_OBJ_STRIDE, out_root,
725
- ca->data_type, ca->bytes,
726
- (int8_t) argc, new_dim,
727
- out_strides, out_base);
728
- ca_keep_chain_flags((CArray *) cs, ca);
729
- obj = ca_wrap_struct(cs);
730
- rb_ca_set_parent(obj, self);
731
- CA_WRAPPER_LIFT(obj, self, ca);
732
- return obj;
779
+ return ca_reshape_folded(self, ca, out_root, (int8_t) argc, new_dim,
780
+ out_strides, out_base);
733
781
  }
734
782
  }
735
783
 
@@ -760,14 +808,8 @@ rb_ca_flatten (VALUE self)
760
808
  ca_size_t out_strides[1];
761
809
  ca_size_t out_base;
762
810
  if (ca_reshape_try_strides(ca, dim, 1, &out_root, out_strides, &out_base)) {
763
- CAStride *cs = ca_stride_new(CA_OBJ_STRIDE, out_root,
764
- ca->data_type, ca->bytes,
765
- 1, dim, out_strides, out_base);
766
- ca_keep_chain_flags((CArray *) cs, ca);
767
- obj = ca_wrap_struct(cs);
768
- rb_ca_set_parent(obj, self);
769
- CA_WRAPPER_LIFT(obj, self, ca);
770
- return obj;
811
+ return ca_reshape_folded(self, ca, out_root, 1, dim,
812
+ out_strides, out_base);
771
813
  }
772
814
  }
773
815
 
@@ -847,6 +889,9 @@ Init_ca_obj_refer (void)
847
889
  /* rb_cCARefer, CA_OBJ_REFER are defined in ruby_carray.c /
848
890
  carray_core.c. Build the custom op table and override the
849
891
  baseline registration installed by Init_carray_core. */
892
+ ca_fold_sources = st_init_numtable();
893
+ rb_native_mutex_initialize(&ca_fold_lock);
894
+
850
895
  ca_refer_func = ca_stride_func;
851
896
  ca_refer_func.free_object = free_ca_refer;
852
897
  ca_refer_func.clone = ca_refer_func_clone;
data/ext/ca_obj_remap.c CHANGED
@@ -304,8 +304,9 @@ ca_remap_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
304
304
 
305
305
  paddrs = ALLOCV_N(ca_size_t, holder, n);
306
306
  ca_xfer_addrs(ca->idx, n, addrs, paddrs, CA_XFER_GET);
307
- /* idx is read live, so its values are checked here, not at
308
- construction: negative counts from the end, out of range raises
307
+ /* idx is read-only once the view holds it, but its values are still
308
+ checked on every transfer (the private without_read_only_flag can
309
+ lift the flag): negative counts from the end, out of range raises
309
310
  before the parent is touched. */
310
311
  for ( i = 0; i < n; i++ ) {
311
312
  CA_CHECK_INDEX(paddrs[i], ca->parent->elements);
@@ -366,9 +367,19 @@ ca_remap_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
366
367
  ALLOCV_END(holder);
367
368
  }
368
369
 
369
- /* xfer_all: whole-view delivery. Sequential view-flat addresses
370
- [0..elements) routed through xfer_addrs -- one ALLOCV for the addr
371
- list, then two batched ca_xfer_addrs calls inside xfer_addrs.
370
+ /* xfer_all: whole-view delivery. The whole view reads every cell of idx
371
+ in order, so when idx holds its cells in memory those cells are the
372
+ parent addresses: they are checked in one pass, a negative one counting
373
+ from the end of the parent.
374
+
375
+ idx itself is handed to the parent only when the parent holds its cells
376
+ in memory and shares no root with idx: that transfer is a plain copy, so
377
+ no Ruby runs while the parent reads the list and a scatter cannot
378
+ rewrite it. Any other parent (a view, a CAObject whose hooks run Ruby)
379
+ gets a copy of the addresses taken in the checking pass, so nothing it
380
+ does can change an address after it was checked, or change idx.
381
+ Without idx in memory, the sequential view-flat addresses [0..elements)
382
+ go through xfer_addrs.
372
383
 
373
384
  No ca_attach(parent) / ca_attach(idx). */
374
385
  static void
@@ -382,6 +393,46 @@ ca_remap_func_xfer_all (void *ap, void *data, int dir)
382
393
 
383
394
  if ( n == 0 ) return;
384
395
 
396
+ if ( ca->idx->ptr ) {
397
+ ca_size_t *iaddrs = (ca_size_t *) ca->idx->ptr;
398
+ ca_size_t pn = ca->parent->elements;
399
+ ca_size_t *paddrs;
400
+
401
+ if ( ca->parent->ptr && ! ca_shares_root(ca->parent, ca->idx) ) {
402
+ int lendable = 1;
403
+ for ( i = 0; i < n; i++ ) {
404
+ ca_size_t a = iaddrs[i];
405
+ if ( a < 0 ) {
406
+ lendable = 0;
407
+ a += pn;
408
+ }
409
+ if ( a < 0 || a >= pn ) {
410
+ ca_index_out_of_range(iaddrs[i], pn, "index");
411
+ }
412
+ }
413
+ if ( lendable ) {
414
+ ca_xfer_addrs(ca->parent, n, iaddrs, data, dir);
415
+ return;
416
+ }
417
+ }
418
+
419
+ paddrs = ALLOCV_N(ca_size_t, holder, n);
420
+ for ( i = 0; i < n; i++ ) {
421
+ ca_size_t a = iaddrs[i];
422
+ if ( a < 0 ) {
423
+ a += pn;
424
+ }
425
+ if ( a < 0 || a >= pn ) {
426
+ ALLOCV_END(holder);
427
+ ca_index_out_of_range(iaddrs[i], pn, "index");
428
+ }
429
+ paddrs[i] = a;
430
+ }
431
+ ca_xfer_addrs(ca->parent, n, paddrs, data, dir);
432
+ ALLOCV_END(holder);
433
+ return;
434
+ }
435
+
385
436
  vaddrs = ALLOCV_N(ca_size_t, holder, n);
386
437
  for ( i = 0; i < n; i++ ) vaddrs[i] = i;
387
438
  ca_remap_func_xfer_addrs(ca, n, vaddrs, data, dir);
@@ -417,7 +468,13 @@ ca_operation_function_t ca_remap_func = {
417
468
  carray_partition.c). Callers are expected to have already checked the
418
469
  routing preconditions (same shape, idx data_type == CA_SIZE);
419
470
  ca_remap_setup raises ArgumentError on mismatch as a defensive
420
- fallback. */
471
+ fallback.
472
+
473
+ The view takes rmapper over: every caller hands a fresh array no one
474
+ else holds, and it is marked read-only here, so the mapping a view was
475
+ made with cannot be rewritten through #mapper. CAGrid and CASelect
476
+ keep their indices where Ruby cannot reach them; this is the same
477
+ promise for an index array that stays visible. */
421
478
  VALUE
422
479
  rb_ca_remap_new (VALUE cary, VALUE rmapper)
423
480
  {
@@ -437,13 +494,14 @@ rb_ca_remap_new (VALUE cary, VALUE rmapper)
437
494
  covers the view's. ca->idx is a raw pointer into idx's struct;
438
495
  keeping rmapper reachable through the view keeps it alive. */
439
496
  rb_ivar_set(obj, rb_intern("remap_idx"), rmapper);
497
+ rb_ca_set_read_only_flag(rmapper);
440
498
  return obj;
441
499
  }
442
500
 
443
- /* Read-only accessor for the per-cell index CArray that defines this
444
- CARemap's mapping: the array the view reads. For ca[mapper] it is a
445
- copy of mapper taken at the call, so a later change to mapper does
446
- not reach the view. Companion to
501
+ /* Accessor for the per-cell index CArray that defines this CARemap's
502
+ mapping: the array the view reads, itself read-only. For ca[mapper]
503
+ it is a copy of mapper taken at the call, so a later change to mapper
504
+ does not reach the view. Companion to
447
505
  CABlock#start / CAStride#strides / CAWindow#count etc. — the view
448
506
  hierarchy is preserved so each named view class exposes its own
449
507
  defining semantic state. */
@@ -526,6 +584,7 @@ Init_ca_obj_remap (void)
526
584
  rb_cCARemapMask,
527
585
  &caremap_mask_data_type,
528
586
  &ca_remap_func, sizeof(ca_remap_func));
587
+ ca_register_cell_moving_view(CA_OBJ_REMAP);
529
588
 
530
589
  /* Defining semantic state accessor — companion to CABlock#start /
531
590
  CAStride#strides / CAWindow#count. The hierarchy-preserving
data/ext/ca_obj_roll.c CHANGED
@@ -733,6 +733,7 @@ Init_ca_obj_roll (void)
733
733
  &caroll_data_type,
734
734
  rb_cCARollMask,
735
735
  &caroll_mask_data_type, &ca_roll_func, sizeof(ca_roll_func));
736
+ ca_register_cell_moving_view(CA_OBJ_ROLL);
736
737
  rb_define_const(rb_cObject, "CA_OBJ_ROLL", INT2NUM(CA_OBJ_ROLL));
737
738
 
738
739
  rb_define_method(rb_cCArray, "roll", rb_ca_roll, -1);
data/ext/ca_obj_select.c CHANGED
@@ -205,7 +205,9 @@ ca_select_setup (CASelect *ca, CArray *parent, CArray *snapshot)
205
205
  ca->dim = &(ca->_dim);
206
206
  ca->dim[0] = ca->elements;
207
207
 
208
- if ( ca_is_scalar(snapshot) ) {
208
+ /* A selection of a scalar is one only while it picks the cell: a scalar
209
+ is one cell along one axis. */
210
+ if ( ca_is_scalar(snapshot) && ca->elements == 1 ) {
209
211
  ca_set_flag(ca, CA_FLAG_SCALAR);
210
212
  }
211
213
 
@@ -668,6 +670,7 @@ rb_cm_initialize_copy (VALUE self, VALUE other)
668
670
  void
669
671
  Init_ca_obj_select (void)
670
672
  {
673
+ ca_register_cell_moving_view(CA_OBJ_SELECT);
671
674
  /* rb_cCASelect and CA_OBJ_SELECT are defined in ruby_carray.c;
672
675
  this file only registers the constant + allocator. */
673
676
  rb_define_const(rb_cObject, "CA_OBJ_SELECT", INT2NUM(CA_OBJ_SELECT));
@@ -1238,6 +1238,7 @@ Init_ca_obj_select_axis (void)
1238
1238
  rb_cCASelectAxisMask,
1239
1239
  &caselectaxis_mask_data_type,
1240
1240
  &ca_select_axis_func, sizeof(ca_select_axis_func));
1241
+ ca_register_cell_moving_view(CA_OBJ_SELECT_AXIS);
1241
1242
  rb_define_const(rb_cObject, "CA_OBJ_SELECT_AXIS", INT2NUM(CA_OBJ_SELECT_AXIS));
1242
1243
 
1243
1244
  rb_define_alloc_func(rb_cCASelectAxis, rb_ca_select_axis_s_allocate);
data/ext/ca_obj_shift.c CHANGED
@@ -363,6 +363,7 @@ Init_ca_obj_shift (void)
363
363
  &cashift_data_type,
364
364
  rb_cCAShiftMask,
365
365
  &cashift_mask_data_type, &ca_shift_func, sizeof(ca_shift_func));
366
+ ca_register_cell_moving_view(CA_OBJ_SHIFT);
366
367
  rb_define_const(rb_cObject, "CA_OBJ_SHIFT", INT2NUM(CA_OBJ_SHIFT));
367
368
 
368
369
  rb_define_method(rb_cCArray, "shift", rb_ca_shift, -1);