carray 3.0.2 → 3.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +568 -0
  3. data/README.md +6 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +220 -0
  7. data/ext/ca_axis_group.c +201 -123
  8. data/ext/ca_binop_dispatch.c +46 -52
  9. data/ext/ca_binop_dispatch.h +1 -1
  10. data/ext/ca_categorical_iterator.c +249 -183
  11. data/ext/ca_compare.c +7 -12
  12. data/ext/ca_for_buffer.h +21 -68
  13. data/ext/ca_group_iter.c +38 -23
  14. data/ext/ca_kernel_iterator.c +1765 -2076
  15. data/ext/ca_kernel_iterator.h +584 -566
  16. data/ext/ca_monop_dispatch.c +86 -63
  17. data/ext/ca_monop_dispatch.h +5 -0
  18. data/ext/ca_obj_array.c +223 -56
  19. data/ext/ca_obj_bincmp.c +172 -138
  20. data/ext/ca_obj_binop.c +116 -230
  21. data/ext/ca_obj_bitarray.c +40 -26
  22. data/ext/ca_obj_bitfield.c +100 -68
  23. data/ext/ca_obj_block.c +19 -9
  24. data/ext/ca_obj_byte_swap.c +45 -46
  25. data/ext/ca_obj_const_string.c +269 -44
  26. data/ext/ca_obj_face.c +612 -89
  27. data/ext/ca_obj_face.h +111 -47
  28. data/ext/ca_obj_fake.c +36 -79
  29. data/ext/ca_obj_farray.c +7 -3
  30. data/ext/ca_obj_field.c +33 -8
  31. data/ext/ca_obj_fixlen_string.c +23 -24
  32. data/ext/ca_obj_grid.c +541 -210
  33. data/ext/ca_obj_meld.c +331 -111
  34. data/ext/ca_obj_moncmp.c +80 -65
  35. data/ext/ca_obj_monop.c +177 -149
  36. data/ext/ca_obj_object.c +664 -248
  37. data/ext/ca_obj_record.c +63 -23
  38. data/ext/ca_obj_reduce.c +18 -16
  39. data/ext/ca_obj_refer.c +140 -58
  40. data/ext/ca_obj_remap.c +85 -24
  41. data/ext/ca_obj_repeat.c +24 -9
  42. data/ext/ca_obj_roll.c +121 -16
  43. data/ext/ca_obj_select.c +61 -58
  44. data/ext/ca_obj_select_axis.c +73 -107
  45. data/ext/ca_obj_shift.c +32 -102
  46. data/ext/ca_obj_stack.c +372 -122
  47. data/ext/ca_obj_stride.c +348 -140
  48. data/ext/ca_obj_string.c +88 -22
  49. data/ext/ca_obj_tile.c +38 -21
  50. data/ext/ca_obj_time.c +45 -31
  51. data/ext/ca_obj_timedelta.c +43 -29
  52. data/ext/ca_obj_transpose.c +17 -6
  53. data/ext/ca_obj_triop.c +125 -141
  54. data/ext/ca_obj_window.c +330 -44
  55. data/ext/ca_op_ipower.c +24 -11
  56. data/ext/ca_op_powi.h +57 -30
  57. data/ext/ca_pow5_table.h +656 -0
  58. data/ext/ca_sort_kernels.h +70 -0
  59. data/ext/ca_sweep_engine.c +104 -81
  60. data/ext/ca_sweep_engine.h +7 -20
  61. data/ext/ca_triop_dispatch.c +4 -1
  62. data/ext/ca_triop_dispatch.h +5 -2
  63. data/ext/caframe_csv_reader.c +542 -0
  64. data/ext/carray.h +241 -92
  65. data/ext/carray_access.c +608 -276
  66. data/ext/carray_address_basis.c +494 -97
  67. data/ext/carray_attribute.c +129 -4
  68. data/ext/carray_bincount.c +39 -7
  69. data/ext/carray_broadcast.c +56 -19
  70. data/ext/carray_build_flags.h +1 -1
  71. data/ext/carray_call_cfunc.c +108 -28
  72. data/ext/carray_cast.c +1576 -223
  73. data/ext/carray_cast_func.rb +77 -24
  74. data/ext/carray_class.c +4 -3
  75. data/ext/carray_conversion.c +208 -75
  76. data/ext/carray_copy.c +82 -24
  77. data/ext/carray_core.c +646 -162
  78. data/ext/carray_count.c +171 -88
  79. data/ext/carray_element.c +94 -60
  80. data/ext/carray_factorize.c +670 -324
  81. data/ext/carray_generate.c +260 -83
  82. data/ext/carray_histogram.c +209 -106
  83. data/ext/carray_hold.c +14 -16
  84. data/ext/carray_index_classifier.c +422 -243
  85. data/ext/carray_internal.h +692 -3
  86. data/ext/carray_kernels_bincmp.c +7 -0
  87. data/ext/carray_kernels_binop.c +487 -120
  88. data/ext/carray_kernels_init.c +653 -50
  89. data/ext/carray_kernels_moncmp.c +15 -8
  90. data/ext/carray_kernels_monop.c +99 -43
  91. data/ext/carray_kernels_reduce_aggregate.c +3925 -6112
  92. data/ext/carray_kernels_reduce_boolean.c +112 -36
  93. data/ext/carray_kernels_reduce_cumulative.c +2127 -6903
  94. data/ext/carray_kernels_reduce_extreme.c +3032 -2892
  95. data/ext/carray_kernels_reduce_variance.c +439 -500
  96. data/ext/carray_kernels_scan.c +1097 -488
  97. data/ext/carray_kernels_search.c +3787 -2176
  98. data/ext/carray_kernels_sort.c +1200 -920
  99. data/ext/carray_kernels_triop.c +479 -7
  100. data/ext/carray_key_windows.c +216 -0
  101. data/ext/carray_lazy.c +967 -153
  102. data/ext/carray_loop.c +81 -65
  103. data/ext/carray_mask.c +910 -324
  104. data/ext/carray_math_kernel.h +1 -0
  105. data/ext/carray_median_percentile.c +299 -100
  106. data/ext/carray_memory_view.c +872 -758
  107. data/ext/carray_operator.c +1171 -364
  108. data/ext/carray_order.c +505 -133
  109. data/ext/carray_partition.c +112 -65
  110. data/ext/carray_random.c +327 -239
  111. data/ext/carray_round.c +273 -0
  112. data/ext/carray_scatter.c +346 -78
  113. data/ext/carray_segment.c +257 -0
  114. data/ext/carray_slab.c +249 -86
  115. data/ext/carray_sort.c +224 -227
  116. data/ext/carray_sort_kernel.c +8 -1
  117. data/ext/carray_struct.c +118 -39
  118. data/ext/carray_test.c +193 -69
  119. data/ext/carray_time_text.c +1182 -0
  120. data/ext/carray_undef.c +4 -0
  121. data/ext/carray_utils.c +579 -113
  122. data/ext/extconf.rb +27 -2
  123. data/ext/mk_call_cfunc.rb +19 -4
  124. data/ext/mk_pow5_table.rb +35 -0
  125. data/ext/mkkernel.rb +1202 -1014
  126. data/ext/ruby_carray.c +17 -5
  127. data/ext/version.h +4 -4
  128. data/ext/version.rb +4 -2
  129. data/lib/carray/attribute.rb +160 -17
  130. data/lib/carray/autoload_carray.rb +23 -27
  131. data/lib/carray/autoload_method_extension.rb +20 -5
  132. data/lib/carray/axis_group.rb +360 -63
  133. data/lib/carray/basics.rb +162 -28
  134. data/lib/carray/bincount_nd.rb +18 -2
  135. data/lib/carray/block_iterator.rb +231 -58
  136. data/lib/carray/boolean_reduce.rb +34 -42
  137. data/lib/carray/categorical.rb +242 -52
  138. data/lib/carray/categorical_iterator.rb +437 -827
  139. data/lib/carray/complex.rb +41 -34
  140. data/lib/carray/conditional.rb +150 -14
  141. data/lib/carray/const_string.rb +59 -4
  142. data/lib/carray/construct.rb +15 -11
  143. data/lib/carray/data_type_extension.rb +1 -0
  144. data/lib/carray/distance_limit.rb +77 -0
  145. data/lib/carray/fixlen_string.rb +7 -1
  146. data/lib/carray/frame/column_verbs.rb +70 -0
  147. data/lib/carray/frame/concat.rb +157 -6
  148. data/lib/carray/frame/convert.rb +82 -7
  149. data/lib/carray/frame/csv_parser.rb +688 -87
  150. data/lib/carray/frame/describe.rb +80 -0
  151. data/lib/carray/frame/frame.rb +369 -45
  152. data/lib/carray/frame/group.rb +631 -90
  153. data/lib/carray/frame/io.rb +506 -79
  154. data/lib/carray/frame/join.rb +411 -62
  155. data/lib/carray/frame/records.rb +116 -19
  156. data/lib/carray/frame/reshape.rb +455 -0
  157. data/lib/carray/frame/sort.rb +30 -18
  158. data/lib/carray/frame/verbs.rb +868 -61
  159. data/lib/carray/frame/windows.rb +117 -0
  160. data/lib/carray/frame.rb +4 -0
  161. data/lib/carray/fuse_source.rb +118 -2
  162. data/lib/carray/fusion.rb +257 -42
  163. data/lib/carray/histogram.rb +6 -1
  164. data/lib/carray/inspect.rb +54 -13
  165. data/lib/carray/iterator.rb +99 -9
  166. data/lib/carray/key_windows.rb +620 -0
  167. data/lib/carray/lazy.rb +216 -193
  168. data/lib/carray/mask_gap_fill.rb +112 -92
  169. data/lib/carray/math.rb +32 -19
  170. data/lib/carray/meld_reduce.rb +52 -21
  171. data/lib/carray/methods/align_addr.rb +8 -4
  172. data/lib/carray/methods/bin.rb +26 -18
  173. data/lib/carray/methods/bincount.rb +18 -2
  174. data/lib/carray/methods/bit_string.rb +15 -4
  175. data/lib/carray/methods/choose.rb +16 -3
  176. data/lib/carray/methods/composition.rb +18 -10
  177. data/lib/carray/methods/factorize.rb +2 -2
  178. data/lib/carray/methods/gather_nd.rb +25 -5
  179. data/lib/carray/methods/insert_block.rb +9 -14
  180. data/lib/carray/methods/is_between.rb +59 -0
  181. data/lib/carray/methods/is_in.rb +140 -10
  182. data/lib/carray/methods/join.rb +2 -7
  183. data/lib/carray/methods/lag.rb +49 -0
  184. data/lib/carray/methods/locate_addr.rb +182 -45
  185. data/lib/carray/methods/mode.rb +18 -1
  186. data/lib/carray/methods/nunique.rb +11 -6
  187. data/lib/carray/methods/repeat.rb +1 -20
  188. data/lib/carray/methods/resize.rb +21 -23
  189. data/lib/carray/methods/snap.rb +34 -12
  190. data/lib/carray/methods/unique.rb +11 -2
  191. data/lib/carray/methods/value_counts.rb +4 -11
  192. data/lib/carray/record_order.rb +208 -0
  193. data/lib/carray/rng.rb +3 -0
  194. data/lib/carray/runtime.rb +65 -17
  195. data/lib/carray/segment_iterator.rb +1221 -0
  196. data/lib/carray/serialize.rb +214 -30
  197. data/lib/carray/slab_iterator.rb +101 -27
  198. data/lib/carray/stack.rb +10 -4
  199. data/lib/carray/string.rb +105 -5
  200. data/lib/carray/string_operation_extension.rb +12 -4
  201. data/lib/carray/struct.rb +137 -58
  202. data/lib/carray/struct_builder.rb +124 -17
  203. data/lib/carray/time.rb +1222 -186
  204. data/lib/carray/window_iterator.rb +242 -81
  205. data/lib/carray.rb +9 -2
  206. data/yard-stubs/ca_obj_array.rb +4 -4
  207. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  208. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  209. data/yard-stubs/ca_obj_fake.rb +1 -1
  210. data/yard-stubs/ca_obj_field.rb +5 -1
  211. data/yard-stubs/ca_obj_grid.rb +21 -10
  212. data/yard-stubs/ca_obj_refer.rb +5 -4
  213. data/yard-stubs/ca_obj_roll.rb +2 -3
  214. data/yard-stubs/ca_obj_shift.rb +5 -1
  215. data/yard-stubs/ca_obj_stride.rb +10 -7
  216. data/yard-stubs/ca_obj_tile.rb +1 -1
  217. data/yard-stubs/ca_obj_transpose.rb +1 -1
  218. data/yard-stubs/ca_obj_window.rb +9 -5
  219. data/yard-stubs/carray_access.rb +11 -5
  220. data/yard-stubs/carray_attribute.rb +0 -5
  221. data/yard-stubs/carray_broadcast.rb +1 -1
  222. data/yard-stubs/carray_cast.rb +88 -43
  223. data/yard-stubs/carray_conversion.rb +15 -2
  224. data/yard-stubs/carray_copy.rb +12 -5
  225. data/yard-stubs/carray_core.rb +3 -3
  226. data/yard-stubs/carray_count.rb +9 -0
  227. data/yard-stubs/carray_element.rb +10 -4
  228. data/yard-stubs/carray_generate.rb +21 -1
  229. data/yard-stubs/carray_lazy.rb +5 -1
  230. data/yard-stubs/carray_loop.rb +4 -1
  231. data/yard-stubs/carray_mask.rb +60 -12
  232. data/yard-stubs/carray_math.rb +52 -14
  233. data/yard-stubs/carray_median_percentile.rb +15 -8
  234. data/yard-stubs/carray_memory_view.rb +40 -23
  235. data/yard-stubs/carray_order.rb +61 -29
  236. data/yard-stubs/carray_random.rb +13 -4
  237. data/yard-stubs/carray_scatter.rb +21 -9
  238. data/yard-stubs/carray_segment.rb +72 -0
  239. data/yard-stubs/carray_slab.rb +10 -2
  240. data/yard-stubs/carray_sort.rb +49 -8
  241. data/yard-stubs/carray_test.rb +15 -3
  242. data/yard-stubs/carray_utils.rb +0 -26
  243. metadata +19 -5
  244. data/ext/ca_for_each_element.h +0 -239
  245. data/ext/carray_kernels_map.c +0 -3467
  246. data/lib/carray/arrow_tensor.rb +0 -401
  247. data/lib/carray/table.rb +0 -108
data/ext/ca_obj_window.c CHANGED
@@ -30,6 +30,7 @@
30
30
  #include "carray.h"
31
31
  #include "ca_composite_dispatch.h"
32
32
  #include "ca_obj_face.h" /* CA_FACE_LIFT_IF_FACE, used by rb_ca_window */
33
+ #include "carray_internal.h" /* ca_axis_view_attach_owned */
33
34
 
34
35
  /* should not be static variable as used by CAIteratorWindow */
35
36
 
@@ -82,11 +83,23 @@ ca_window_pool_init (void *ap, int8_t ndim)
82
83
  ca->dim = ca->count; /* alias; ca_window_setup re-sets */
83
84
  }
84
85
 
86
+ /* A window over an object array keeps its fill value, a VALUE, in a
87
+ buffer of its own, so it marks it; ca_mark covers only ca->ptr. */
88
+ void
89
+ ca_window_mark (void *ap)
90
+ {
91
+ CAWindow *ca = (CAWindow *) ap;
92
+ ca_mark(ca);
93
+ if ( ca->fill != NULL && ca_is_object_type(ca) ) {
94
+ rb_gc_mark(*(VALUE *) ca->fill);
95
+ }
96
+ }
97
+
85
98
  const rb_data_type_t cawindow_data_type = {
86
99
  .parent = &caview_data_type,
87
100
  .wrap_struct_name = "CAWindow",
88
101
  .function = {
89
- .dmark = ca_mark,
102
+ .dmark = ca_window_mark,
90
103
  .dfree = ca_free,
91
104
  .dsize = ca_window_dsize,
92
105
  .dcompact = NULL
@@ -255,10 +268,19 @@ ca_window_compose_fold (void *win_ap,
255
268
  for the window-over-parent mapping (ca_window_compose_fold) and composes
256
269
  f through it (ca_stride_compose_through). Declines (returns 0) when the
257
270
  window is not interior-only, making the window the fold boundary. */
271
+ static void ca_window_mask_current (CAWindow *ca);
272
+ static int ca_window_mask_reads_standin (CAWindow *ca);
273
+
258
274
  static int
259
275
  ca_window_func_fold_stride (void *ap, ca_fold_t *f, void **next_parent)
260
276
  {
261
277
  CAWindow *w = (CAWindow *) ap;
278
+ ca_window_mask_current(w);
279
+ /* A write folded through to a stand-in would bypass the settle in this
280
+ window's write slots. */
281
+ if ( ca_window_mask_reads_standin(w) ) {
282
+ return 0;
283
+ }
262
284
  ca_size_t synth_strides[CA_RANK_MAX];
263
285
  ca_size_t synth_base;
264
286
  ca_size_t *synth_dim;
@@ -314,7 +336,9 @@ static void
314
336
  ca_window_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
315
337
  ca_size_t *strides, void *data, int dir)
316
338
  {
339
+ if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
317
340
  CAWindow *w = (CAWindow *) ap;
341
+ ca_window_mask_current(w);
318
342
  CArray *parent = w->parent;
319
343
  int8_t ndim = w->ndim;
320
344
  int8_t inner = ndim - 1;
@@ -355,6 +379,7 @@ ca_window_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
355
379
  ca_size_t idx[CA_RANK_MAX], doff = 0, base = 0;
356
380
  for (k = 0; k < ndim; k++) base += starts[k] * wnative[k];
357
381
  for (k = 0; k < ndim; k++) idx[k] = 0;
382
+ /* empty: the slot returns on an empty region at its entry */
358
383
  while (1) {
359
384
  ca_size_t woff = base, widx[CA_RANK_MAX];
360
385
  for (k = 0; k < ndim; k++) woff += idx[k] * strides[k];
@@ -658,7 +683,6 @@ ca_window_setup (CAWindow *ca, CArray *parent,
658
683
  ca_size_t *dim;
659
684
  ca_size_t bytes, elements;
660
685
  int i;
661
- int any_mask;
662
686
 
663
687
  data_type = parent->data_type;
664
688
  ndim = parent->ndim;
@@ -667,11 +691,38 @@ ca_window_setup (CAWindow *ca, CArray *parent,
667
691
 
668
692
  elements = 1;
669
693
  for (i=0; i<ndim; i++) {
670
- if ( count[i] <= 0 ) {
694
+ if ( count[i] < 0 ) { /* zero is an empty window */
671
695
  rb_raise(rb_eIndexError,
672
- "invalid size for %i-th dimension (negative or zero)", i);
696
+ "invalid size for %i-th dimension (negative)", i);
697
+ }
698
+ }
699
+ elements = ca_shape_elements(ndim, count, bytes);
700
+
701
+ /* Under "ruby" or "strict" an index outside the parent raises. The
702
+ parent's shape is fixed, so a window that reaches outside it would raise
703
+ on every whole read or write, and a write would have changed the cells
704
+ before the one that raised. It is refused here instead. */
705
+ for (i=0; i<ndim; i++) {
706
+ ca_size_t lo, hi, size = dim[i];
707
+ if ( count[i] == 0 ) {
708
+ continue;
709
+ }
710
+ lo = start[i];
711
+ hi = start[i] + count[i] - 1;
712
+ if ( bounds[i] == CA_BOUNDS_STRICT ) {
713
+ if ( lo < 0 ) ca_index_out_of_range(lo, size, "index");
714
+ if ( hi >= size ) ca_index_out_of_range(hi, size, "index");
673
715
  }
674
- elements *= count[i];
716
+ else if ( bounds[i] == CA_BOUNDS_RUBY ) {
717
+ if ( lo < -size ) ca_index_out_of_range(lo, size, "index");
718
+ if ( hi >= size ) ca_index_out_of_range(hi, size, "index");
719
+ }
720
+ }
721
+
722
+ /* ca == NULL asks for the checks above only, so that a constructor can
723
+ run them before it allocates. */
724
+ if ( ca == NULL ) {
725
+ return 0;
675
726
  }
676
727
 
677
728
  ca->obj_type = CA_OBJ_WINDOW;
@@ -687,6 +738,8 @@ ca_window_setup (CAWindow *ca, CArray *parent,
687
738
  ca->parent = parent;
688
739
  ca->attach = 0;
689
740
  ca->nosync = 0;
741
+ ca->mask_standin = NULL;
742
+ ca->mask_owner = NULL;
690
743
  /* Pool path: bounds/start/count/size0/embed_* are already wired into
691
744
  ca->_pool by ca_window_pool_init. Legacy path: ALLOC_N each. `fill`
692
745
  is bytes-sized (not ndim) so it stays on its own ALLOC_N in both
@@ -727,27 +780,36 @@ ca_window_setup (CAWindow *ca, CArray *parent,
727
780
  }
728
781
  }
729
782
 
730
- /* Mask is needed if ANY axis uses MASK policy (per-axis). */
731
- any_mask = 0;
732
- for (i=0; i<ndim; i++) {
783
+ return 0;
784
+ }
785
+
786
+ /* A window with an axis of MASK bounds has a mask of its own. Making it
787
+ asks the parent for its mask, which can raise (a CAObject answers in
788
+ Ruby), so the constructor makes it after the setup under
789
+ ca_fill_or_free; see ca_view_inherit_mask. */
790
+ VALUE
791
+ ca_window_create_bounds_mask (VALUE view)
792
+ {
793
+ CAWindow *ca = (CAWindow *) view;
794
+ int8_t i;
795
+ for (i = 0; i < ca->ndim; i++) {
733
796
  if ( ca->bounds[i] == CA_BOUNDS_MASK ) {
734
- any_mask = 1;
797
+ ca_create_mask((CArray *) ca);
735
798
  break;
736
799
  }
737
800
  }
738
- if ( any_mask ) {
739
- ca_create_mask(ca);
740
- }
741
-
742
- return 0;
801
+ return Qnil;
743
802
  }
744
803
 
745
804
  CAWindow *
746
805
  ca_window_new (CArray *parent,
747
806
  ca_size_t *start, ca_size_t *count, uint8_t *bounds, char *fill)
748
807
  {
749
- CAWindow *ca = (CAWindow *) ca_array_alloc(CA_OBJ_WINDOW, parent->ndim);
808
+ CAWindow *ca;
809
+ ca_window_setup(NULL, parent, start, count, bounds, fill);
810
+ ca = (CAWindow *) ca_array_alloc(CA_OBJ_WINDOW, parent->ndim);
750
811
  ca_window_setup(ca, parent, start, count, bounds, fill);
812
+ ca_fill_or_free((CArray *) ca, ca_window_create_bounds_mask, (VALUE) ca);
751
813
  return ca;
752
814
  }
753
815
 
@@ -757,6 +819,7 @@ free_ca_window (void *ap)
757
819
  CAWindow *ca = (CAWindow *) ap;
758
820
  if ( ca != NULL ) {
759
821
  ca_free(ca->mask);
822
+ ca_free(ca->mask_standin);
760
823
  xfree(ca->fill); /* always separate (bytes-sized) */
761
824
  if ( ca->_pool ) {
762
825
  /* bounds/start/count/size0/embed_* all live in ca->_pool. */
@@ -796,7 +859,39 @@ static void *
796
859
  ca_window_func_clone (void *ap)
797
860
  {
798
861
  CAWindow *ca = (CAWindow *) ap;
799
- return ca_window_new(ca->parent, ca->start, ca->count, ca->bounds, ca->fill);
862
+ CAWindow *cp = ca_window_new(ca->parent, ca->start, ca->count, ca->bounds,
863
+ ca->fill);
864
+ cp->mask_owner = ca->mask_owner;
865
+ return cp;
866
+ }
867
+
868
+ /* A mask window over its owner's stand-in (ca_mask_standin_current): what
869
+ it reads from now on. */
870
+ static void
871
+ ca_window_mask_current (CAWindow *ca)
872
+ {
873
+ CAWindow *o = (CAWindow *) ca->mask_owner;
874
+ if ( o != NULL ) {
875
+ ca->parent = ca_mask_standin_current((CArray *) ca, ca->parent, o->parent,
876
+ o->mask_standin);
877
+ }
878
+ }
879
+
880
+ static int
881
+ ca_window_mask_reads_standin (CAWindow *ca)
882
+ {
883
+ CAWindow *o = (CAWindow *) ca->mask_owner;
884
+ return o != NULL && o->mask_standin != NULL && ca->parent == o->mask_standin;
885
+ }
886
+
887
+ /* After a write that may have put a true into the stand-in: it goes on to
888
+ the parent's mask. */
889
+ static void
890
+ ca_window_mask_settle (CAWindow *ca)
891
+ {
892
+ if ( ca_window_mask_reads_standin(ca) ) {
893
+ ca_mask_standin_settle(((CAWindow *) ca->mask_owner)->parent, ca->parent);
894
+ }
800
895
  }
801
896
 
802
897
  /* Per-cell access. GET and PUT share the bound-normalised index walk; an OOB
@@ -806,6 +901,7 @@ static void
806
901
  ca_window_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
807
902
  {
808
903
  CAWindow *ca = (CAWindow *) ap;
904
+ ca_window_mask_current(ca);
809
905
  ca_size_t *start = ca->start;
810
906
  ca_size_t *size0 = ca->size0;
811
907
  ca_size_t idx0[CA_RANK_MAX];
@@ -834,6 +930,7 @@ ca_window_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
834
930
  void *data, int dir)
835
931
  {
836
932
  CAWindow *ca = (CAWindow *) ap;
933
+ ca_window_mask_current(ca);
837
934
  ca_size_t *start = ca->start;
838
935
  ca_size_t *size0 = ca->size0;
839
936
  char *d = (char *) data;
@@ -952,13 +1049,53 @@ ca_window_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
952
1049
  ALLOCV_END(h1);
953
1050
  }
954
1051
 
1052
+ /* Whether this window over a parent with no memory to lend owns its buffer
1053
+ rather than attaching the parent. Only FILL / MASK bounds qualify: their
1054
+ xfer_stride hands the parent one region per row, where the other bounds
1055
+ go cell by cell -- for a Ruby-backed parent, a call per cell, which costs
1056
+ more than producing the parent once. Both inputs are fixed for the
1057
+ life of the view, so attach, sync and detach take the same branch. */
1058
+ static int
1059
+ ca_window_owns_buffer (CAWindow *ca)
1060
+ {
1061
+ int8_t k;
1062
+ for ( k = 0; k < ca->ndim; k++ ) {
1063
+ if ( ca->bounds[k] != CA_BOUNDS_FILL && ca->bounds[k] != CA_BOUNDS_MASK ) {
1064
+ return 0;
1065
+ }
1066
+ }
1067
+ return ca_parent_lends_no_memory(ca->parent);
1068
+ }
1069
+
1070
+ /* The parent's storage the alias path points into: inner axes full and
1071
+ interior, so the window is one contiguous run of the attached parent. */
1072
+ static char *
1073
+ ca_window_alias_ptr (CAWindow *ca)
1074
+ {
1075
+ ca_size_t parent_row_stride = ca->bytes;
1076
+ int8_t k;
1077
+ for (k = ca->ndim - 1; k >= 1; k--) parent_row_stride *= ca->parent->dim[k];
1078
+ return ca->parent->ptr + ca->start[0] * parent_row_stride;
1079
+ }
1080
+
1081
+ /* Agrees with attach on the alias path: sync has nothing to write back
1082
+ and detach frees nothing there, so a buffer of our own would take the
1083
+ writes and be dropped. */
955
1084
  static void
956
1085
  ca_window_func_allocate (void *ap)
957
1086
  {
958
1087
  CAWindow *ca = (CAWindow *) ap;
1088
+ ca_window_mask_current(ca);
1089
+ if ( ca_window_owns_buffer(ca) ) {
1090
+ ca->ptr = ca_alloc_cells((CArray *) ca);
1091
+ return;
1092
+ }
959
1093
  ca_attach(ca->parent);
960
- /* ca->ptr = ALLOC_N(char, ca_length(ca)); */
961
- ca->ptr = xmalloc(ca_length(ca));
1094
+ if ( ca->embed_alias_eligible ) {
1095
+ ca->ptr = ca_window_alias_ptr(ca);
1096
+ return;
1097
+ }
1098
+ ca->ptr = ca_alloc_cells((CArray *) ca);
962
1099
  }
963
1100
 
964
1101
  /* The engine paths below emit a per-axis descriptor (ca_window_describe_axes)
@@ -973,6 +1110,11 @@ static void
973
1110
  ca_window_func_attach (void *ap)
974
1111
  {
975
1112
  CAWindow *ca = (CAWindow *) ap;
1113
+ ca_window_mask_current(ca);
1114
+ if ( ca_window_owns_buffer(ca) ) {
1115
+ ca_axis_view_attach_owned(ca);
1116
+ return;
1117
+ }
976
1118
  ca_attach(ca->parent);
977
1119
  if ( ca->embed_alias_eligible ) {
978
1120
  /* Alias path: inner axes full + interior, so the embedded region is a
@@ -981,14 +1123,10 @@ ca_window_func_attach (void *ap)
981
1123
  CAREFUL: sync and detach must agree with this — sync has nothing to
982
1124
  scatter back (the writes already landed in the parent) and detach must
983
1125
  not xfree a pointer it does not own. */
984
- ca_size_t parent_row_stride = ca->bytes;
985
- int8_t k;
986
- for (k = ca->ndim - 1; k >= 1; k--) parent_row_stride *= ca->parent->dim[k];
987
- ca->ptr = ca->parent->ptr + ca->start[0] * parent_row_stride;
1126
+ ca->ptr = ca_window_alias_ptr(ca);
988
1127
  } else if ( ca->embed_eligible ) {
989
1128
  /* Embed path: allocate, then 1 fill + 1 strided memcpy. */
990
- ca_size_t out_len = ca->elements * ca->bytes;
991
- ca->ptr = xmalloc(out_len > 0 ? out_len : 1);
1129
+ ca->ptr = ca_alloc_cells((CArray *) ca);
992
1130
  ca_window_attach_embed(ca, ca->ptr);
993
1131
  } else {
994
1132
  /* Fallback: PERIODIC / REFLECT / NEAREST / RUBY / STRICT go through
@@ -1005,6 +1143,10 @@ static void
1005
1143
  ca_window_func_sync (void *ap)
1006
1144
  {
1007
1145
  CAWindow *ca = (CAWindow *) ap;
1146
+ if ( ca_window_owns_buffer(ca) ) {
1147
+ ca_axis_view_sync_owned(ca);
1148
+ return;
1149
+ }
1008
1150
  if ( ca->embed_alias_eligible ) {
1009
1151
  /* Alias path: ca->ptr aliases the parent, so the writes already landed
1010
1152
  in parent storage. Nothing to scatter back. */
@@ -1019,12 +1161,18 @@ ca_window_func_sync (void *ap)
1019
1161
  ca->elements, ca->ptr);
1020
1162
  }
1021
1163
  ca_sync(ca->parent);
1164
+ ca_window_mask_settle(ca);
1022
1165
  }
1023
1166
 
1024
1167
  static void
1025
1168
  ca_window_func_detach (void *ap)
1026
1169
  {
1027
1170
  CAWindow *ca = (CAWindow *) ap;
1171
+ if ( ca_window_owns_buffer(ca) ) {
1172
+ xfree(ca->ptr);
1173
+ ca->ptr = NULL;
1174
+ return;
1175
+ }
1028
1176
  /* Alias path: ca->ptr aliases parent storage; it is not ours to xfree. */
1029
1177
  if ( ! ca->embed_alias_eligible ) {
1030
1178
  xfree(ca->ptr);
@@ -1078,6 +1226,7 @@ static void
1078
1226
  ca_window_func_xfer_all (void *ap, void *data, int dir)
1079
1227
  {
1080
1228
  CAWindow *ca = (CAWindow *) ap;
1229
+ ca_window_mask_current(ca);
1081
1230
  if ( ca->parent->ptr ) {
1082
1231
  ca_window_func_run_fast_path(ca, (char *) data, dir);
1083
1232
  return;
@@ -1117,9 +1266,25 @@ ca_window_func_xfer_all (void *ap, void *data, int dir)
1117
1266
  }
1118
1267
  }
1119
1268
 
1120
- /* Cold parent with nothing to lend: materialise it into a scratch buffer
1121
- via ca_xfer_all, then run the normal fast path with the scratch standing
1122
- in for parent->ptr. */
1269
+ /* A parent with no memory to lend is asked for the cells of the window
1270
+ only, through xfer_stride over the whole view, when the bounds let that
1271
+ go row by row (see ca_window_owns_buffer). */
1272
+ if ( ca_window_owns_buffer(ca) ) {
1273
+ ca_size_t starts[CA_RANK_MAX], strides[CA_RANK_MAX], s;
1274
+ int8_t k;
1275
+ s = ca->bytes;
1276
+ for ( k = ca->ndim - 1; k >= 0; k-- ) {
1277
+ starts[k] = 0;
1278
+ strides[k] = s;
1279
+ s *= ca->dim[k];
1280
+ }
1281
+ ca_window_func_xfer_stride(ca, starts, ca->dim, strides, data, dir);
1282
+ return;
1283
+ }
1284
+
1285
+ /* Any other cold parent: materialise it into a scratch buffer via
1286
+ ca_xfer_all, then run the normal fast path with the scratch standing in
1287
+ for parent->ptr. */
1123
1288
  {
1124
1289
  volatile VALUE holder;
1125
1290
  CArray *parent = ca->parent;
@@ -1137,12 +1302,14 @@ ca_window_func_xfer_all (void *ap, void *data, int dir)
1137
1302
 
1138
1303
  ca_window_func_run_fast_path(ca, (char *) data, dir);
1139
1304
 
1305
+ /* Put parent->ptr back before the PUT: the PUT can raise (a lazy
1306
+ backing's sync failing), and the scratch is gone once this
1307
+ frame is. */
1308
+ parent->ptr = parent_ptr_saved;
1140
1309
  if ( dir == CA_XFER_PUT ) {
1141
1310
  /* Push back scratch (modified by scatter) to parent. */
1142
1311
  ca_xfer_all(parent, parent_scratch, CA_XFER_PUT);
1143
1312
  }
1144
-
1145
- parent->ptr = parent_ptr_saved;
1146
1313
  ALLOCV_END(holder);
1147
1314
  }
1148
1315
  }
@@ -1161,6 +1328,7 @@ static void
1161
1328
  ca_window_func_fill_data (void *ap, void *ptr)
1162
1329
  {
1163
1330
  CAWindow *ca = (CAWindow *) ap;
1331
+ ca_window_mask_current(ca);
1164
1332
 
1165
1333
  /* embed_eligible (= no PERIODIC/REFLECT) covers both interior-only
1166
1334
  windows (embed_covers_all == 1) and OOB-bearing ones such as a CAShift
@@ -1238,22 +1406,32 @@ ca_window_func_fill_data (void *ap, void *ptr)
1238
1406
  ca_attach(ca->parent);
1239
1407
  ca_axis_dispatch_fill_value(ca->parent, pdims, desc, ca->ndim, ca->bytes,
1240
1408
  ca->elements, ptr);
1241
- ca_sync(ca->parent);
1242
- ca_detach(ca->parent);
1409
+ ca_sync_detach(ca->parent);
1243
1410
  }
1244
1411
 
1245
1412
  static void
1246
1413
  ca_window_func_create_mask (void *ap)
1247
1414
  {
1248
1415
  CAWindow *ca = (CAWindow *) ap;
1416
+ CArray *pmask;
1249
1417
  boolean8_t fill;
1250
1418
  uint8_t mbounds[CA_RANK_MAX];
1251
1419
  int8_t i;
1252
1420
  int any_mask;
1253
1421
 
1254
- ca_update_mask(ca->parent);
1255
- if ( ! ca->parent->mask ) {
1422
+ /* The mask lies over the parent's mask, or, while the parent has none,
1423
+ over an all-false stand-in this window holds: masking the
1424
+ out-of-bounds cells gives the parent no mask
1425
+ (ca_mask_standin_new). */
1426
+ if ( ca_has_mask(ca->parent) ) {
1256
1427
  ca_create_mask(ca->parent);
1428
+ pmask = ca->parent->mask;
1429
+ }
1430
+ else {
1431
+ if ( ca->mask_standin == NULL ) {
1432
+ ca->mask_standin = ca_mask_standin_new(ca->parent);
1433
+ }
1434
+ pmask = ca->mask_standin;
1257
1435
  }
1258
1436
 
1259
1437
  /* Any MASK axis of the view becomes FILL in the mask sub-view, with the
@@ -1270,8 +1448,77 @@ ca_window_func_create_mask (void *ap)
1270
1448
  }
1271
1449
  fill = any_mask ? 1 : 0;
1272
1450
 
1273
- ca->mask = (CArray *) ca_window_new(ca->parent->mask,
1274
- ca->start, ca->count, mbounds, (char*)&fill);
1451
+ /* The mask has the view's own type, so CAShift (which shares this table)
1452
+ gets a CAShiftMask. */
1453
+ {
1454
+ /* check: the mask window copies its own window's checked ranges */
1455
+ CAWindow *m = (CAWindow *) ca_array_alloc(ca->obj_type, ca->ndim);
1456
+ ca_window_setup(m, pmask, ca->start, ca->count, mbounds, (char*)&fill);
1457
+ m->obj_type = ca->obj_type;
1458
+ m->mask_owner = (CArray *) ca;
1459
+ if ( pmask == ca->mask_standin ) {
1460
+ /* writes inside an attach window reach the stand-in through the
1461
+ sync, where they can give way to the parent's mask */
1462
+ m->embed_alias_eligible = 0;
1463
+ }
1464
+ ca->mask = (CArray *) m;
1465
+ }
1466
+ }
1467
+
1468
+ /* The table's write slots: on the mask of a window still reading a
1469
+ stand-in, a write that may have put a true into it ends with the settle
1470
+ (ca_mask_standin_settle). */
1471
+ static void
1472
+ ca_window_put_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
1473
+ {
1474
+ ca_window_func_xfer_index(ap, idx, data, dir);
1475
+ if ( dir == CA_XFER_PUT && ca_window_mask_reads_standin((CAWindow *) ap)
1476
+ && ca_mask_any_true(data, 1) ) {
1477
+ ca_window_mask_settle((CAWindow *) ap);
1478
+ }
1479
+ }
1480
+
1481
+ static void
1482
+ ca_window_put_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *data,
1483
+ int dir)
1484
+ {
1485
+ ca_window_func_xfer_addrs(ap, n, addrs, data, dir);
1486
+ if ( dir == CA_XFER_PUT && ca_window_mask_reads_standin((CAWindow *) ap)
1487
+ && ca_mask_any_true(data, n) ) {
1488
+ ca_window_mask_settle((CAWindow *) ap);
1489
+ }
1490
+ }
1491
+
1492
+ static void
1493
+ ca_window_put_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
1494
+ ca_size_t *strides, void *data, int dir)
1495
+ {
1496
+ ca_window_func_xfer_stride(ap, starts, counts, strides, data, dir);
1497
+ if ( dir == CA_XFER_PUT && ca_window_mask_reads_standin((CAWindow *) ap)
1498
+ && ca_mask_any_true(data,
1499
+ ca_shape_elements(((CArray *) ap)->ndim, counts, 1)) ) {
1500
+ ca_window_mask_settle((CAWindow *) ap);
1501
+ }
1502
+ }
1503
+
1504
+ static void
1505
+ ca_window_put_xfer_all (void *ap, void *data, int dir)
1506
+ {
1507
+ ca_window_func_xfer_all(ap, data, dir);
1508
+ if ( dir == CA_XFER_PUT && ca_window_mask_reads_standin((CAWindow *) ap)
1509
+ && ca_mask_any_true(data, ((CArray *) ap)->elements) ) {
1510
+ ca_window_mask_settle((CAWindow *) ap);
1511
+ }
1512
+ }
1513
+
1514
+ static void
1515
+ ca_window_put_fill_data (void *ap, void *ptr)
1516
+ {
1517
+ ca_window_func_fill_data(ap, ptr);
1518
+ if ( ca_window_mask_reads_standin((CAWindow *) ap)
1519
+ && ca_mask_any_true(ptr, 1) ) {
1520
+ ca_window_mask_settle((CAWindow *) ap);
1521
+ }
1275
1522
  }
1276
1523
 
1277
1524
  ca_operation_function_t ca_window_func = {
@@ -1283,13 +1530,13 @@ ca_operation_function_t ca_window_func = {
1283
1530
  ca_window_func_attach,
1284
1531
  ca_window_func_sync,
1285
1532
  ca_window_func_detach,
1286
- ca_window_func_fill_data,
1533
+ ca_window_put_fill_data,
1287
1534
  ca_window_func_create_mask,
1288
- ca_window_func_xfer_index,
1289
- ca_window_func_xfer_addrs,
1535
+ ca_window_put_xfer_index,
1536
+ ca_window_put_xfer_addrs,
1290
1537
  ca_window_func_fold_stride,
1291
- ca_window_func_xfer_stride,
1292
- ca_window_func_xfer_all,
1538
+ ca_window_put_xfer_stride,
1539
+ ca_window_put_xfer_all,
1293
1540
  };
1294
1541
 
1295
1542
  /* ------------------------------------------------------------------- */
@@ -1309,6 +1556,7 @@ ca_window_describe_axes (void *ap, ca_axis_desc_t *out,
1309
1556
  ca_size_t *out_parent_dims)
1310
1557
  {
1311
1558
  CAWindow *ca = (CAWindow *) ap;
1559
+ ca_window_mask_current(ca);
1312
1560
  int8_t k;
1313
1561
  for ( k = 0; k < ca->ndim; k++ ) {
1314
1562
  out_parent_dims[k] = ca->parent->dim[k];
@@ -1410,9 +1658,13 @@ rb_ca_window (int argc, VALUE *argv, VALUE self)
1410
1658
  bounds = CA_BOUNDS_MASK;
1411
1659
  }
1412
1660
  else {
1413
- rcs = rb_cscalar_new_with_value(ca->data_type, ca->bytes, rfval);
1661
+ /* The fill is a value of the array (a String for a CAString, an
1662
+ instant for a time array): rb_ca_obj2ptr writes it as one cell of
1663
+ the storage, through the Face. */
1664
+ rcs = rb_cscalar_new(ca_strip_face(ca)->data_type, ca->bytes, NULL);
1414
1665
  TypedData_Get_Struct(rcs, CScalar, &cscalar_data_type, cs);
1415
1666
  fill = cs->ptr;
1667
+ rb_ca_obj2ptr(self, rfval, fill);
1416
1668
  }
1417
1669
 
1418
1670
  if ( ! NIL_P(rbounds) ) {
@@ -1427,7 +1679,7 @@ rb_ca_window (int argc, VALUE *argv, VALUE self)
1427
1679
  cbounds = StringValuePtr(rbounds);
1428
1680
  if ( rfval == CA_UNDEF && strncmp(cbounds, "fill", 4)
1429
1681
  && strncmp(cbounds, "mask", 4) ) {
1430
- rb_raise(rb_eRuntimeError, "conflicted bounds and fill_value");
1682
+ rb_raise(rb_eArgError, "conflicted bounds and fill_value");
1431
1683
  }
1432
1684
  if ( ! strncmp(cbounds, "ruby", 4) ) {
1433
1685
  bounds = CA_BOUNDS_RUBY;
@@ -1458,7 +1710,7 @@ rb_ca_window (int argc, VALUE *argv, VALUE self)
1458
1710
  bounds = CA_BOUNDS_FILL;
1459
1711
  }
1460
1712
  else {
1461
- rb_raise(rb_eRuntimeError,
1713
+ rb_raise(rb_eArgError,
1462
1714
  "unknown option value '%s' for :bounds", cbounds);
1463
1715
  }
1464
1716
  break;
@@ -1466,7 +1718,7 @@ rb_ca_window (int argc, VALUE *argv, VALUE self)
1466
1718
  bounds = NUM2INT(rbounds);
1467
1719
  break;
1468
1720
  default:
1469
- rb_raise(rb_eRuntimeError, "invalid option value for :bounds");
1721
+ rb_raise(rb_eArgError, "invalid option value for :bounds");
1470
1722
  }
1471
1723
  }
1472
1724
 
@@ -1491,6 +1743,7 @@ rb_ca_window_initialize_copy (VALUE self, VALUE other)
1491
1743
  CAWindow *ca, *cs;
1492
1744
 
1493
1745
  TypedData_Get_Struct(self, CAWindow, &cawindow_data_type, ca);
1746
+ ca_check_uninitialized(ca);
1494
1747
  TypedData_Get_Struct(other, CAWindow, &cawindow_data_type, cs);
1495
1748
 
1496
1749
  /* `self` came from rb_ca_window_s_allocate (TypedData_Make_Struct,
@@ -1500,6 +1753,10 @@ rb_ca_window_initialize_copy (VALUE self, VALUE other)
1500
1753
  ca_array_pool_alloc(ca, CA_OBJ_WINDOW, cs->ndim);
1501
1754
  }
1502
1755
  ca_window_setup(ca, cs->parent, cs->start, cs->count, cs->bounds, cs->fill);
1756
+ ca_window_create_bounds_mask((VALUE) ca);
1757
+ /* a copy of a mask over a stand-in gives way to the parent's mask as
1758
+ the original does */
1759
+ ca->mask_owner = cs->mask_owner;
1503
1760
 
1504
1761
  return self;
1505
1762
  }
@@ -1588,6 +1845,34 @@ rb_ca_window_get_bounds (VALUE self)
1588
1845
  return SIZE2NUM(cw->bounds[0]);
1589
1846
  }
1590
1847
 
1848
+ /* The boundary rule on each axis, one Symbol per axis. `bounds` answers
1849
+ for axis 0 only, which is all CArray#window sets; a shift with `roll:`
1850
+ on some axes sets them one by one. */
1851
+ static VALUE
1852
+ rb_ca_window_axis_bounds (VALUE self)
1853
+ {
1854
+ volatile VALUE ary;
1855
+ CAWindow *cw;
1856
+ const char *name;
1857
+ int8_t i;
1858
+ TypedData_Get_Struct(self, CAWindow, &cawindow_data_type, cw);
1859
+ ary = rb_ary_new2(cw->ndim);
1860
+ for (i=0; i<cw->ndim; i++) {
1861
+ switch ( cw->bounds[i] ) {
1862
+ case CA_BOUNDS_RUBY: name = "ruby"; break;
1863
+ case CA_BOUNDS_STRICT: name = "strict"; break;
1864
+ case CA_BOUNDS_NEAREST: name = "nearest"; break;
1865
+ case CA_BOUNDS_PERIODIC: name = "periodic"; break;
1866
+ case CA_BOUNDS_REFLECT: name = "reflect"; break;
1867
+ case CA_BOUNDS_FILL: name = "fill"; break;
1868
+ case CA_BOUNDS_MASK: name = "mask"; break;
1869
+ default: name = "unknown"; break;
1870
+ }
1871
+ rb_ary_store(ary, i, ID2SYM(rb_intern(name)));
1872
+ }
1873
+ return ary;
1874
+ }
1875
+
1591
1876
  #define rb_cw_get_attr_ary(name) \
1592
1877
  rb_cw_## name (VALUE self) \
1593
1878
  { \
@@ -1688,6 +1973,7 @@ Init_ca_obj_window (void)
1688
1973
  /* No `count` accessor: the per-axis window widths are what `shape`
1689
1974
  already answers, and the name belongs to CArray#count. */
1690
1975
  rb_define_method(rb_cCAWindow, "start", rb_cw_start, 0);
1976
+ rb_define_method(rb_cCAWindow, "__boundary_rule_of_each_axis__", rb_ca_window_axis_bounds, 0);
1691
1977
  rb_define_method(rb_cCAWindow, "size0", rb_cw_size0, 0);
1692
1978
 
1693
1979
  #ifdef CARRAY_DEV_BUILD