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
@@ -12,12 +12,13 @@
12
12
  - single INDIRECT axis (boolean), other axes AP (nil/Integer/Range)
13
13
  - parent must be an entity CArray (no CAStride parent)
14
14
  - selector copied at construction (fully static, fragility-free)
15
- - selector must not have a mask (hard reject)
15
+ - a masked selector cell selects nothing (same rule as CASelect)
16
16
  - slab copy uses straightforward nested-loop memcpy (no SIMD fast paths)
17
17
 
18
18
  ---------------------------------------------------------------------------- */
19
19
 
20
20
  #include "carray.h"
21
+ #include "carray_internal.h" /* ca_select_snapshot */
21
22
 
22
23
  typedef struct {
23
24
  /* CAView prefix (mirrors carray.h ca_virtual layout) */
@@ -112,9 +113,11 @@ static int8_t CA_OBJ_SELECT_AXIS;
112
113
 
113
114
  /* Benchmark-only bypass flag: when non-zero, rb_ca_grid skips the
114
115
  CASelectAxis dispatch hook and falls through to the pre-CSA CAGrid path.
115
- Exposed to Ruby as CArray._csa_bypass=. Production code never touches it. */
116
+ Exposed to Ruby as CArray._csa_bypass= in development builds.
117
+ Production code never touches it. */
116
118
  int ca_csa_dispatch_bypass = 0;
117
119
 
120
+ #ifdef CARRAY_DEV_BUILD
118
121
  static VALUE
119
122
  rb_ca_s_csa_bypass_eq (VALUE klass, VALUE val)
120
123
  {
@@ -129,6 +132,7 @@ rb_ca_s_csa_bypass_p (VALUE klass)
129
132
  (void) klass;
130
133
  return ca_csa_dispatch_bypass ? Qtrue : Qfalse;
131
134
  }
135
+ #endif
132
136
 
133
137
  static VALUE rb_cCASelectAxis;
134
138
  static VALUE rb_cCASelectAxisMask;
@@ -175,22 +179,18 @@ ca_select_axis_setup (CASelectAxis *ca, CArray *parent, int8_t indirect_axis,
175
179
  (int) indirect_axis,
176
180
  (long long) parent->dim[indirect_axis]);
177
181
  }
178
- if ( ca_has_mask(selector) ) {
179
- rb_raise(rb_eArgError,
180
- "CASelectAxis: selector boolean array must not have a mask. "
181
- "Use `selector.strip_mask(false)` etc. to flatten first.");
182
- }
183
-
184
182
  /* Validate AP axes */
185
183
  for ( k = 0; k < ndim; k++ ) {
186
184
  if ( k == indirect_axis ) continue;
187
185
  if ( ap_step[k] == 0 ) {
188
186
  rb_raise(rb_eArgError, "CASelectAxis: ap_step[%d] is 0", (int) k);
189
187
  }
190
- if ( ap_count[k] <= 0 ) {
191
- rb_raise(rb_eArgError, "CASelectAxis: ap_count[%d] must be positive", (int) k);
188
+ if ( ap_count[k] < 0 ) {
189
+ rb_raise(rb_eArgError, "CASelectAxis: ap_count[%d] is negative", (int) k);
192
190
  }
193
- {
191
+ /* A zero count (an axis of length zero) selects no cells: the view is
192
+ empty and there is no first or last index to check. */
193
+ if ( ap_count[k] > 0 ) {
194
194
  ca_size_t start_v = ap_start[k];
195
195
  ca_size_t last_v = ap_start[k] + (ap_count[k] - 1) * ap_step[k];
196
196
  if ( start_v < 0 || start_v >= parent->dim[k] ) {
@@ -222,8 +222,9 @@ ca_select_axis_setup (CASelectAxis *ca, CArray *parent, int8_t indirect_axis,
222
222
 
223
223
  /* Copy selector (view owns its own copy; immune to external mutation).
224
224
  The selector is small (1 axis length), so the copy cost is acceptable,
225
- and it avoids the fragility CASelect has from live references. */
226
- ca->selector = ca_copy(selector);
225
+ and it avoids the fragility CASelect has from live references. A
226
+ masked selector cell selects nothing, as it does for CASelect. */
227
+ ca->selector = ca_select_snapshot(selector);
227
228
 
228
229
  /* Allocate dim and AP arrays (pool path: already wired by pool_init). */
229
230
  if ( ! ca->_pool ) {
@@ -310,6 +311,7 @@ ca_select_axis_new (CArray *parent, int8_t indirect_axis,
310
311
  CArray *selector,
311
312
  ca_size_t *ap_start, ca_size_t *ap_count, ca_size_t *ap_step)
312
313
  {
314
+ /* check: the callers (clone, create_mask) pass an existing view's checked selector */
313
315
  CASelectAxis *ca = (CASelectAxis *) ca_array_alloc(CA_OBJ_SELECT_AXIS, parent->ndim);
314
316
  ca_select_axis_setup(ca, parent, indirect_axis, selector,
315
317
  ap_start, ap_count, ap_step);
@@ -548,6 +550,7 @@ static void
548
550
  ca_select_axis_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
549
551
  ca_size_t *strides, void *data, int dir)
550
552
  {
553
+ if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
551
554
  CASelectAxis *ca = (CASelectAxis *) ap;
552
555
  CArray *parent = ca->parent;
553
556
  int8_t ndim = ca->ndim;
@@ -588,6 +591,7 @@ ca_select_axis_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
588
591
  ca_size_t idx[CA_RANK_MAX], doff = 0, base = 0;
589
592
  for (k = 0; k < ndim; k++) base += starts[k] * cnative[k];
590
593
  for (k = 0; k < ndim; k++) idx[k] = 0;
594
+ /* empty: the slot returns on an empty region at its entry */
591
595
  while (1) {
592
596
  ca_size_t coff = base, cidx[CA_RANK_MAX];
593
597
  for (k = 0; k < ndim; k++) coff += idx[k] * strides[k];
@@ -671,6 +675,7 @@ ca_select_axis_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
671
675
  ca_xfer_stride/ca_xfer_addrs. */
672
676
  for (k = 0; k < ndim; k++) o[k] = 0;
673
677
 
678
+ /* empty: the slot returns on an empty region at its entry */
674
679
  while (1) {
675
680
  ca_size_t pbase = 0, doff = 0;
676
681
  for (k = 0; k < inner; k++) {
@@ -710,25 +715,34 @@ ca_select_axis_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
710
715
  }
711
716
  }
712
717
 
718
+ /* A parent with no memory to lend (ca_parent_lends_no_memory) is never
719
+ attached: the view owns its buffer, filled and drained through xfer_all,
720
+ which asks the parent for the selected cells only. The predicate asks
721
+ what the parent is, not whether it is attached now, so attach, sync and
722
+ detach all take the same branch. */
723
+
713
724
  static void
714
725
  ca_select_axis_func_allocate (void *ap)
715
726
  {
716
727
  CASelectAxis *ca = (CASelectAxis *) ap;
717
- ca_attach(ca->parent);
718
- ca->ptr = xmalloc(ca_length(ca));
728
+ if ( ! ca_parent_lends_no_memory(ca->parent) ) {
729
+ ca_attach(ca->parent);
730
+ }
731
+ ca->ptr = ca_alloc_cells((CArray *) ca);
719
732
  }
720
733
 
721
- /* attach / sync / copy_data / sync_data / fill_data all go through the
722
- common descriptor engine. This file holds only describe_axes for
723
- translating CSA state into the shared descriptor; the engine in
724
- ext/ca_axis_dispatch.c does the rest. */
725
-
726
734
  static void
727
735
  ca_select_axis_func_attach (void *ap)
728
736
  {
729
737
  CASelectAxis *ca = (CASelectAxis *) ap;
730
738
  ca_axis_desc_t desc[CA_RANK_MAX];
731
739
  ca_size_t pdims[CA_RANK_MAX];
740
+
741
+ if ( ca_parent_lends_no_memory(ca->parent) ) {
742
+ ca_axis_view_attach_owned(ca);
743
+ return;
744
+ }
745
+
732
746
  ca_attach(ca->parent);
733
747
  ca_select_axis_describe_axes(ca, desc, pdims);
734
748
  ca->ptr = ca_axis_dispatch_attach(ca->parent, pdims, desc, ca->ndim,
@@ -741,6 +755,12 @@ ca_select_axis_func_sync (void *ap)
741
755
  CASelectAxis *ca = (CASelectAxis *) ap;
742
756
  ca_axis_desc_t desc[CA_RANK_MAX];
743
757
  ca_size_t pdims[CA_RANK_MAX];
758
+
759
+ if ( ca_parent_lends_no_memory(ca->parent) ) {
760
+ ca_axis_view_sync_owned(ca);
761
+ return;
762
+ }
763
+
744
764
  ca_select_axis_describe_axes(ca, desc, pdims);
745
765
  ca_axis_dispatch_scatter(ca->parent, pdims, desc, ca->ndim, ca->bytes,
746
766
  ca->elements, ca->ptr);
@@ -756,16 +776,16 @@ ca_select_axis_func_detach (void *ap)
756
776
  ca->ptr = NULL;
757
777
  }
758
778
  /* indices[] lives for the view's lifetime; freed in free_ca_select_axis. */
759
- ca_detach(ca->parent);
779
+ if ( ! ca_parent_lends_no_memory(ca->parent) ) {
780
+ ca_detach(ca->parent);
781
+ }
760
782
  }
761
783
 
762
- /* xfer_all: fast path on parent->ptr, with no silent transitive attach
763
- of the parent. A cold parent uses a proper 2-pass (materialise the
764
- parent into scratch, expose it as parent->ptr, run the fast path, then
765
- write scratch back for PUT). */
784
+ /* The transfer against parent->ptr, for ca_axis_view_xfer_all. */
766
785
  static void
767
- ca_select_axis_func_run_fast_path (CASelectAxis *ca, char *data, int dir)
786
+ ca_select_axis_func_run_fast_path (void *ap, char *data, int dir)
768
787
  {
788
+ CASelectAxis *ca = (CASelectAxis *) ap;
769
789
  ca_axis_desc_t desc[CA_RANK_MAX];
770
790
  ca_size_t pdims[CA_RANK_MAX];
771
791
  ca_select_axis_describe_axes(ca, desc, pdims);
@@ -781,27 +801,7 @@ ca_select_axis_func_run_fast_path (CASelectAxis *ca, char *data, int dir)
781
801
  static void
782
802
  ca_select_axis_func_xfer_all (void *ap, void *data, int dir)
783
803
  {
784
- CASelectAxis *ca = (CASelectAxis *) ap;
785
- if ( ca->parent->ptr ) {
786
- ca_select_axis_func_run_fast_path(ca, (char *) data, dir);
787
- return;
788
- }
789
- /* Proper 2-pass cold fallback. */
790
- {
791
- volatile VALUE holder;
792
- CArray *parent = ca->parent;
793
- ca_size_t plen = parent->elements * parent->bytes;
794
- char *parent_scratch = ALLOCV_N(char, holder, plen);
795
- char *parent_ptr_saved = parent->ptr;
796
- ca_xfer_all(parent, parent_scratch, CA_XFER_GET);
797
- parent->ptr = parent_scratch;
798
- ca_select_axis_func_run_fast_path(ca, (char *) data, dir);
799
- if ( dir == CA_XFER_PUT ) {
800
- ca_xfer_all(parent, parent_scratch, CA_XFER_PUT);
801
- }
802
- parent->ptr = parent_ptr_saved;
803
- ALLOCV_END(holder);
804
- }
804
+ ca_axis_view_xfer_all(ap, ca_select_axis_func_run_fast_path, 1, data, dir);
805
805
  }
806
806
 
807
807
  static void
@@ -824,21 +824,17 @@ ca_select_axis_func_fill_data (void *ap, void *val)
824
824
  return;
825
825
  }
826
826
 
827
- ca_attach(ca->parent);
827
+ ca_attach(ca->parent); /* window: nothing raises inside */
828
828
  ca_axis_dispatch_fill_value(ca->parent, pdims, desc, ca->ndim, ca->bytes,
829
829
  ca->elements, val);
830
- ca_sync(ca->parent);
831
- ca_detach(ca->parent);
830
+ ca_sync_detach(ca->parent);
832
831
  }
833
832
 
834
833
  static void
835
834
  ca_select_axis_func_create_mask (void *ap)
836
835
  {
837
836
  CASelectAxis *ca = (CASelectAxis *) ap;
838
- ca_update_mask(ca->parent);
839
- if ( ! ca->parent->mask ) {
840
- ca_create_mask(ca->parent);
841
- }
837
+ ca_create_mask(ca->parent);
842
838
  ca->mask = ca_select_axis_new(ca->parent->mask, ca->indirect_axis,
843
839
  ca->selector,
844
840
  ca->ap_start, ca->ap_count, ca->ap_step);
@@ -878,6 +874,7 @@ rb_ca_select_axis_initialize_copy (VALUE self, VALUE other)
878
874
  {
879
875
  CASelectAxis *ca, *cs;
880
876
  TypedData_Get_Struct(self, CASelectAxis, &caselectaxis_data_type, ca);
877
+ ca_check_uninitialized(ca);
881
878
  TypedData_Get_Struct(other, CASelectAxis, &caselectaxis_data_type, cs);
882
879
  if ( ca_func[CA_OBJ_SELECT_AXIS].pool_init ) {
883
880
  ca_array_pool_alloc(ca, CA_OBJ_SELECT_AXIS, cs->parent->ndim);
@@ -887,6 +884,7 @@ rb_ca_select_axis_initialize_copy (VALUE self, VALUE other)
887
884
  return self;
888
885
  }
889
886
 
887
+ #ifdef CARRAY_DEV_BUILD
890
888
  /* Debug-only Ruby ctor:
891
889
  CASelectAxis._new_debug(parent, indirect_axis, selector,
892
890
  ap_start_arr, ap_count_arr, ap_step_arr)
@@ -900,6 +898,7 @@ rb_ca_select_axis_s_new_debug (VALUE klass, VALUE rparent, VALUE rindirect,
900
898
  VALUE rap_count, VALUE rap_step)
901
899
  {
902
900
  CArray *parent, *selector;
901
+ /* fits: an axis number from the view that made it */
903
902
  int8_t indirect_axis = (int8_t) NUM2INT(rindirect);
904
903
  int8_t ndim;
905
904
  int8_t k;
@@ -937,6 +936,7 @@ rb_ca_select_axis_s_new_debug (VALUE klass, VALUE rparent, VALUE rindirect,
937
936
  rb_ivar_set(obj, rb_intern("_selector"), rselector);
938
937
  return obj;
939
938
  }
939
+ #endif
940
940
 
941
941
  /* ------------------------------------------------------------------- */
942
942
  /* Indexer dispatch hook: called from rb_ca_grid */
@@ -970,15 +970,15 @@ rb_ca_select_axis_eligible_p (int argc, VALUE *argv, VALUE self)
970
970
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
971
971
  if ( argc != ca->ndim ) return 0;
972
972
 
973
- /* axis 0 is the boolean (indirect). Walk rest; require nil/Integer/Range. */
973
+ /* axis 0 is the boolean (indirect). The rest are read as the indexer
974
+ reads them; a run of more than one step, or of no cells, goes to
975
+ CAGrid. */
974
976
  for ( i = 1; i < argc; i++ ) {
975
977
  VALUE arg = argv[i];
976
- if ( NIL_P(arg) ) continue;
977
- if ( FIXNUM_P(arg) ) continue;
978
- if ( TYPE(arg) == T_DATA ) return 0; /* CArray at axis > 0 → bail */
979
- if ( rb_obj_is_kind_of(arg, rb_cRange) ) continue;
980
- if ( rb_obj_is_kind_of(arg, rb_cInteger) ) continue;
981
- return 0;
978
+ ca_size_t start, count, step;
979
+ if ( rb_obj_is_carray(arg) ) return 0; /* CArray at axis > 0 → bail */
980
+ ca_index_axis_key(arg, ca->dim[i], i, &start, &count, &step);
981
+ if ( step != 1 || count < 1 ) return 0;
982
982
  }
983
983
 
984
984
  return 1;
@@ -1010,45 +1010,9 @@ rb_ca_select_axis (int argc, VALUE *argv, VALUE self)
1010
1010
  for ( i = 0; i < argc; i++ ) {
1011
1011
  VALUE arg = argv[i];
1012
1012
 
1013
- if ( NIL_P(arg) ) {
1014
- ap_start[i] = 0;
1015
- ap_count[i] = parent->dim[i];
1016
- ap_step[i] = 1;
1017
- }
1018
- else if ( FIXNUM_P(arg) || rb_obj_is_kind_of(arg, rb_cInteger) ) {
1019
- ca_size_t n = NUM2LL(arg);
1020
- if ( n < 0 ) n += parent->dim[i];
1021
- if ( n < 0 || n >= parent->dim[i] ) {
1022
- rb_raise(rb_eIndexError,
1023
- "CASelectAxis: index %lld out of range at axis %d",
1024
- (long long) n, i);
1025
- }
1026
- ap_start[i] = n;
1027
- ap_count[i] = 1;
1028
- ap_step[i] = 1;
1029
- }
1030
- else if ( rb_obj_is_kind_of(arg, rb_cRange) ) {
1031
- VALUE rfirst = rb_funcall(arg, rb_intern("first"), 0);
1032
- VALUE rlast = rb_funcall(arg, rb_intern("last"), 0);
1033
- int exclusive = RTEST(rb_funcall(arg, rb_intern("exclude_end?"), 0));
1034
- ca_size_t a = NUM2LL(rfirst);
1035
- ca_size_t b = NUM2LL(rlast);
1036
- if ( a < 0 ) a += parent->dim[i];
1037
- if ( b < 0 ) b += parent->dim[i];
1038
- ca_size_t cnt = exclusive ? (b - a) : (b - a + 1);
1039
- if ( cnt <= 0 ) {
1040
- rb_raise(rb_eIndexError,
1041
- "CASelectAxis: empty range at axis %d", i);
1042
- }
1043
- if ( a < 0 || a >= parent->dim[i] ||
1044
- (a + cnt - 1) < 0 || (a + cnt - 1) >= parent->dim[i] ) {
1045
- rb_raise(rb_eIndexError,
1046
- "CASelectAxis: range [%lld..%lld] out of bounds at axis %d",
1047
- (long long) a, (long long) (a + cnt - 1), i);
1048
- }
1049
- ap_start[i] = a;
1050
- ap_count[i] = cnt;
1051
- ap_step[i] = 1;
1013
+ if ( ! rb_obj_is_carray(arg) ) {
1014
+ ca_index_axis_key(arg, parent->dim[i], i,
1015
+ &ap_start[i], &ap_count[i], &ap_step[i]);
1052
1016
  }
1053
1017
  else if ( rb_obj_is_carray(arg) && rb_ca_is_boolean_type(arg) ) {
1054
1018
  TypedData_Get_Struct(arg, CArray, &carray_data_type, selector);
@@ -1188,7 +1152,7 @@ rb_ca_select_axis_dispatch_attach_debug (VALUE self)
1188
1152
  TypedData_Get_Struct(self, CASelectAxis, &caselectaxis_data_type, ca);
1189
1153
  ca_select_axis_describe_axes(ca, desc, pdims);
1190
1154
 
1191
- ca_attach(ca->parent);
1155
+ ca_attach(ca->parent); /* window: nothing raises inside */
1192
1156
  buf = ca_axis_dispatch_attach(ca->parent, pdims, desc, ca->ndim, ca->bytes,
1193
1157
  ca->elements, NULL);
1194
1158
  ca_detach(ca->parent);
@@ -1222,11 +1186,10 @@ rb_ca_select_axis_dispatch_scatter_debug (VALUE self, VALUE in_str)
1222
1186
  ca_size_t pdims[CA_RANK_MAX];
1223
1187
  ca_select_axis_describe_axes(ca, desc, pdims);
1224
1188
 
1225
- ca_attach(ca->parent);
1189
+ ca_attach(ca->parent); /* window: nothing raises inside */
1226
1190
  ca_axis_dispatch_scatter(ca->parent, pdims, desc, ca->ndim, ca->bytes,
1227
1191
  ca->elements, RSTRING_PTR(in_str));
1228
- ca_sync(ca->parent);
1229
- ca_detach(ca->parent);
1192
+ ca_sync_detach(ca->parent);
1230
1193
  return Qnil;
1231
1194
  }
1232
1195
 
@@ -1250,11 +1213,10 @@ rb_ca_select_axis_dispatch_fill_value_debug (VALUE self, VALUE val_str)
1250
1213
  ca_size_t pdims[CA_RANK_MAX];
1251
1214
  ca_select_axis_describe_axes(ca, desc, pdims);
1252
1215
 
1253
- ca_attach(ca->parent);
1216
+ ca_attach(ca->parent); /* window: nothing raises inside */
1254
1217
  ca_axis_dispatch_fill_value(ca->parent, pdims, desc, ca->ndim, ca->bytes,
1255
1218
  ca->elements, RSTRING_PTR(val_str));
1256
- ca_sync(ca->parent);
1257
- ca_detach(ca->parent);
1219
+ ca_sync_detach(ca->parent);
1258
1220
  return Qnil;
1259
1221
  }
1260
1222
  #endif /* CARRAY_DEV_BUILD */
@@ -1281,9 +1243,11 @@ Init_ca_obj_select_axis (void)
1281
1243
  rb_define_alloc_func(rb_cCASelectAxis, rb_ca_select_axis_s_allocate);
1282
1244
  rb_define_method(rb_cCASelectAxis, "initialize_copy",
1283
1245
  rb_ca_select_axis_initialize_copy, 1);
1246
+ #ifdef CARRAY_DEV_BUILD
1284
1247
  /* Debug-only ctor for testing; not part of the public API. */
1285
1248
  rb_define_singleton_method(rb_cCASelectAxis, "_new_debug",
1286
1249
  rb_ca_select_axis_s_new_debug, 6);
1250
+ #endif
1287
1251
 
1288
1252
  #ifdef CARRAY_DEV_BUILD
1289
1253
  /* debug accessors (dev-only, stripped in release).
@@ -1300,7 +1264,9 @@ Init_ca_obj_select_axis (void)
1300
1264
  rb_ca_select_axis_dispatch_fill_value_debug, 1);
1301
1265
  #endif
1302
1266
 
1267
+ #ifdef CARRAY_DEV_BUILD
1303
1268
  /* Benchmark-only bypass setter on CArray class. */
1304
1269
  rb_define_singleton_method(rb_cCArray, "_csa_bypass=", rb_ca_s_csa_bypass_eq, 1);
1305
1270
  rb_define_singleton_method(rb_cCArray, "_csa_bypass?", rb_ca_s_csa_bypass_p, 0);
1271
+ #endif
1306
1272
  }
data/ext/ca_obj_shift.c CHANGED
@@ -8,14 +8,15 @@
8
8
  sync_data / fill_data / ptr_at_* / fetch_index / store_index) are
9
9
  shared with CAWindow's per-axis bounds engine via the `ca_shift_func`
10
10
  table, which is built by copying `ca_window_func` and only overriding
11
- free / clone / create_mask so that CAShift-typed instances are produced
12
- on cloning and mask-projection.
11
+ clone, so that cloning produces a CAShift. create_mask is shared: it
12
+ gives the mask the view's own obj_type.
13
13
 
14
14
  User surface (CArray#shift) is documented in yard-stubs/ca_obj_shift.rb.
15
15
 
16
16
  ---------------------------------------------------------------------------- */
17
17
 
18
18
  #include "carray.h"
19
+ #include "carray_internal.h" /* ca_window_create_bounds_mask */
19
20
  #include "ca_obj_face.h" /* CA_FACE_LIFT_IF_FACE */
20
21
 
21
22
  /* External: CAWindow's operation table + setup are the basis for CAShift. */
@@ -31,11 +32,13 @@ extern int ca_window_setup (CAWindow *ca, CArray *parent,
31
32
  (rb_data_type_t *) cast would write into .rodata and SIGBUS on macOS. */
32
33
  static size_t ca_shift_dsize (const void *ap);
33
34
 
35
+ extern void ca_window_mark (void *ap); /* ca_obj_window.c: marks the fill value */
36
+
34
37
  const rb_data_type_t cashift_data_type = {
35
- .parent = &caview_data_type,
38
+ .parent = &cawindow_data_type,
36
39
  .wrap_struct_name = "CAShift",
37
40
  .function = {
38
- .dmark = ca_mark,
41
+ .dmark = ca_window_mark,
39
42
  .dfree = ca_free,
40
43
  .dsize = ca_shift_dsize,
41
44
  .dcompact = NULL
@@ -44,7 +47,7 @@ const rb_data_type_t cashift_data_type = {
44
47
  };
45
48
 
46
49
  /* CAREFUL: mask TypedData uses ca_free_nop. The mask CArray is owned
47
- by the parent CAShift's `ca->mask` field and freed by free_ca_shift's
50
+ by the parent CAShift's `ca->mask` field and freed by the shared window free's
48
51
  ca_free(ca->mask). If the wrapped Ruby VALUE (from the `ca.mask`
49
52
  accessor / rb_ca_mask_array) also freed it, the result is a
50
53
  double-free that explodes under GC stress. */
@@ -105,10 +108,13 @@ ca_shift_setup (CAShift *ca, CArray *parent,
105
108
  /* Delegate to CAWindow setup. */
106
109
  ca_window_setup((CAWindow *) ca, parent, start, count, bounds, fill);
107
110
 
111
+ if ( ca == NULL ) { /* checks only, see ca_window_setup */
112
+ return 0;
113
+ }
114
+
108
115
  /* Override obj_type so dispatch (ca_func[obj_type]) lands on the
109
116
  CAShift-specific table (which differs from CAWindow only in
110
- free / clone / create_mask, all forwarding to a CAShift-typed
111
- result). */
117
+ clone). */
112
118
  ca->obj_type = CA_OBJ_SHIFT;
113
119
 
114
120
  return 0;
@@ -118,37 +124,14 @@ CAShift *
118
124
  ca_shift_new (CArray *parent, ca_size_t *shift, char *fill, int8_t *roll,
119
125
  int fill_mask)
120
126
  {
121
- CAShift *ca = (CAShift *) ca_array_alloc(CA_OBJ_SHIFT, parent->ndim);
127
+ CAShift *ca;
128
+ ca_shift_setup(NULL, parent, shift, fill, roll, fill_mask);
129
+ ca = (CAShift *) ca_array_alloc(CA_OBJ_SHIFT, parent->ndim);
122
130
  ca_shift_setup(ca, parent, shift, fill, roll, fill_mask);
131
+ ca_fill_or_free((CArray *) ca, ca_window_create_bounds_mask, (VALUE) ca);
123
132
  return ca;
124
133
  }
125
134
 
126
- /* Free: same fields as CAWindow (same struct). CAShift goes through
127
- ca_window_setup, so the same seven ndim-sized fields live in ca->_pool
128
- (fill stays separate). */
129
- static void
130
- free_ca_shift (void *ap)
131
- {
132
- CAShift *ca = (CAShift *) ap;
133
- if ( ca != NULL ) {
134
- ca_free(ca->mask);
135
- xfree(ca->fill); /* always separate (bytes-sized) */
136
- if ( ca->_pool ) {
137
- ca_array_free(ca);
138
- }
139
- else {
140
- xfree(ca->bounds);
141
- xfree(ca->start);
142
- xfree(ca->count);
143
- xfree(ca->size0);
144
- xfree(ca->embed_parent_start);
145
- xfree(ca->embed_count);
146
- xfree(ca->embed_output_offset);
147
- xfree(ca);
148
- }
149
- }
150
- }
151
-
152
135
  /* Clone: produce a CAShift (not CAWindow) by rebuilding via setup.
153
136
  Recover (shift[], roll[], fill_mask) from current (start[], bounds[]). */
154
137
  static void *
@@ -171,73 +154,14 @@ ca_shift_func_clone (void *ap)
171
154
  }
172
155
  }
173
156
 
174
- return ca_shift_new(ca->parent, shift, ca->fill, roll, fill_mask);
175
- }
176
-
177
- /* create_mask: produce a CAShift-typed mask (not CAWindow-typed).
178
- Following the original ca_shift_func_create_mask semantics:
179
- - Ensure parent has mask
180
- - Build mask sub-view using same shift/roll, fill = 1 if fill_mask
181
- else 0 (i.e. range-outside cells get masked when fill_mask was on,
182
- unmasked otherwise) */
183
- static void
184
- ca_shift_func_create_mask (void *ap)
185
- {
186
- CAShift *ca = (CAShift *) ap;
187
- ca_size_t shift[CA_RANK_MAX];
188
- int8_t roll[CA_RANK_MAX];
189
- boolean8_t fill_val;
190
- int fill_mask_was_on = 0;
191
- int8_t k;
192
-
193
- ca_update_mask(ca->parent);
194
- if ( ! ca->parent->mask ) {
195
- ca_create_mask(ca->parent);
196
- }
197
-
198
- for (k = 0; k < ca->ndim; k++) {
199
- shift[k] = -ca->start[k];
200
- if ( ca->bounds[k] == CA_BOUNDS_PERIODIC ) {
201
- roll[k] = 1;
202
- } else {
203
- roll[k] = 0;
204
- if ( ca->bounds[k] == CA_BOUNDS_MASK ) fill_mask_was_on = 1;
205
- }
206
- }
207
-
208
- /* If MASK policy was set, the mask sub-view should mark out-of-range
209
- cells as "masked" (fill value = 1). Otherwise out-of-range cells
210
- get the fill value (fill = 0) and aren't masked. */
211
- fill_val = fill_mask_was_on ? 1 : 0;
212
-
213
- /* Build the mask as CAShift with same shift/roll but using fill_mask=0
214
- (the mask itself shouldn't recursively create a mask) and fill = 0/1
215
- boolean. Bounds for mask: replace MASK→FILL so the mask values are
216
- written, not the mask's mask. */
217
157
  {
218
- /* Construct mask via direct ca_window_new with adjusted bounds. */
219
- uint8_t mbounds[CA_RANK_MAX];
220
- ca_size_t mstart[CA_RANK_MAX];
221
- ca_size_t mcount[CA_RANK_MAX];
222
- CAShift *m;
223
- CA_ASSUME(ca->ndim >= 0 && ca->ndim <= CA_RANK_MAX); /* bound loop over [CA_RANK_MAX] arrays */
224
- for (k = 0; k < ca->ndim; k++) {
225
- mstart[k] = ca->start[k];
226
- mcount[k] = ca->count[k];
227
- mbounds[k] = ( ca->bounds[k] == CA_BOUNDS_MASK )
228
- ? CA_BOUNDS_FILL : ca->bounds[k];
229
- }
230
- /* CAShift-typed mask: allocate, setup as CAWindow with adjusted
231
- bounds, bump obj_type. */
232
- m = (CAShift *) ca_array_alloc(CA_OBJ_SHIFT, ca->ndim);
233
- ca_window_setup((CAWindow *) m, ca->parent->mask,
234
- mstart, mcount, mbounds, (char *) &fill_val);
235
- m->obj_type = CA_OBJ_SHIFT;
236
- ca->mask = (CArray *) m;
158
+ CAShift *cp = ca_shift_new(ca->parent, shift, ca->fill, roll, fill_mask);
159
+ cp->mask_owner = ca->mask_owner;
160
+ return cp;
237
161
  }
238
- (void) shift; (void) roll; /* recovered above but unused in this block */
239
162
  }
240
163
 
164
+
241
165
  /* ------------------------------------------------------------------- */
242
166
 
243
167
  VALUE
@@ -319,9 +243,12 @@ rb_ca_shift (int argc, VALUE *argv, VALUE self)
319
243
  fill_mask = 1;
320
244
  }
321
245
  else {
322
- rcs = rb_cscalar_new_with_value(ca->data_type, ca->bytes, rfval);
246
+ /* The fill is a value of the array (a struct, an instant for a Face):
247
+ rb_ca_obj2ptr writes it as one cell of the storage. */
248
+ rcs = rb_cscalar_new(ca_strip_face(ca)->data_type, ca->bytes, NULL);
323
249
  TypedData_Get_Struct(rcs, CScalar, &cscalar_data_type, cs);
324
250
  fill = cs->ptr;
251
+ rb_ca_obj2ptr(self, rfval, fill);
325
252
  }
326
253
 
327
254
  if ( NIL_P(rroll) ) {
@@ -366,6 +293,7 @@ rb_ca_shift_initialize_copy (VALUE self, VALUE other)
366
293
  int8_t k;
367
294
 
368
295
  TypedData_Get_Struct(self, CAShift, &cashift_data_type, ca);
296
+ ca_check_uninitialized(ca);
369
297
  TypedData_Get_Struct(other, CAShift, &cashift_data_type, cs);
370
298
 
371
299
  /* Recover shift/roll/fill_mask from cs (same scheme as clone). */
@@ -384,6 +312,10 @@ rb_ca_shift_initialize_copy (VALUE self, VALUE other)
384
312
  ca_array_pool_alloc(ca, CA_OBJ_SHIFT, cs->ndim);
385
313
  }
386
314
  ca_shift_setup(ca, cs->parent, shift, cs->fill, roll, fill_mask);
315
+ ca_window_create_bounds_mask((VALUE) ca);
316
+ /* a copy of a mask over a stand-in gives way to the parent's mask as
317
+ the original does */
318
+ ca->mask_owner = cs->mask_owner;
387
319
 
388
320
  return self;
389
321
  }
@@ -395,12 +327,10 @@ rb_ca_shift_initialize_copy (VALUE self, VALUE other)
395
327
  void
396
328
  Init_ca_obj_shift (void)
397
329
  {
398
- /* Build the CAShift op table by copying CAWindow's and overriding
399
- just the slots that need to produce CAShift-typed results. */
330
+ /* Build the CAShift op table by copying CAWindow's; only clone has to
331
+ rebuild a CAShift. */
400
332
  ca_shift_func = ca_window_func;
401
- ca_shift_func.free_object = free_ca_shift;
402
333
  ca_shift_func.clone = ca_shift_func_clone;
403
- ca_shift_func.create_mask = ca_shift_func_create_mask;
404
334
 
405
335
  rb_cCAShift = rb_define_class("CAShift", rb_cCAWindow);
406
336
  rb_cCAShiftMask = rb_define_class("CAShiftMask", rb_cCAShift);