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_stride.c CHANGED
@@ -20,6 +20,7 @@
20
20
  ---------------------------------------------------------------------------- */
21
21
 
22
22
  #include "carray.h"
23
+ #include "carray_internal.h" /* ca_normalize_axis_arg */
23
24
  #include "ca_iter_substrate.h"
24
25
  #include "ca_obj_face.h" /* ca_is_face, used by the compose-fold walk */
25
26
 
@@ -93,24 +94,27 @@ int8_t CA_OBJ_STRIDE; /* assigned at Init time via ca_install_obj_type */
93
94
  override pattern and makes the C-level dispatch wire up correctly
94
95
  on the first try. */
95
96
  int
96
- ca_stride_setup (CAStride *ca, int8_t obj_type, CArray *parent,
97
- int8_t data_type, ca_size_t bytes,
98
- int8_t ndim, ca_size_t *dim,
99
- ca_size_t *strides, ca_size_t base_offset)
97
+ ca_stride_setup_view (CAStride *ca, int8_t obj_type, CArray *parent,
98
+ int8_t data_type, ca_size_t bytes,
99
+ int8_t ndim, ca_size_t *dim,
100
+ ca_size_t *strides, ca_size_t base_offset)
100
101
  {
101
102
  ca_size_t elements;
102
103
  int i;
103
104
 
104
- if (ndim < 0 || ndim > CA_RANK_MAX) {
105
- rb_raise(rb_eArgError, "invalid ndim %d", (int) ndim);
106
- }
107
- elements = 1;
105
+ ca_check_rank_fits(ndim, NULL);
108
106
  for (i = 0; i < ndim; i++) {
109
107
  if (dim[i] < 0) {
110
108
  rb_raise(rb_eIndexError,
111
109
  "invalid size for %i-th dimension (negative)", i);
112
110
  }
113
- elements *= dim[i];
111
+ }
112
+ elements = ca_shape_elements(ndim, dim, bytes);
113
+
114
+ /* ca == NULL asks for the checks above only, so that a constructor can
115
+ run them before it allocates. */
116
+ if ( ca == NULL ) {
117
+ return 0;
114
118
  }
115
119
 
116
120
  ca->obj_type = obj_type;
@@ -138,10 +142,23 @@ ca_stride_setup (CAStride *ca, int8_t obj_type, CArray *parent,
138
142
  ca->strides[i] = strides[i];
139
143
  }
140
144
 
141
- if (parent && ca_has_mask(parent)) {
142
- ca_create_mask(ca);
143
- }
145
+ return 0;
146
+ }
144
147
 
148
+ /* The public form also takes the parent's mask. A struct that no Ruby
149
+ object owns yet is lost if the parent's answer raises; the in-tree
150
+ constructors use ca_stride_setup_view and ca_view_inherit_mask. */
151
+ int
152
+ ca_stride_setup (CAStride *ca, int8_t obj_type, CArray *parent,
153
+ int8_t data_type, ca_size_t bytes,
154
+ int8_t ndim, ca_size_t *dim,
155
+ ca_size_t *strides, ca_size_t base_offset)
156
+ {
157
+ ca_stride_setup_view(ca, obj_type, parent, data_type, bytes,
158
+ ndim, dim, strides, base_offset);
159
+ if ( ca != NULL ) {
160
+ ca_view_inherit_mask((VALUE) ca);
161
+ }
145
162
  return 0;
146
163
  }
147
164
 
@@ -151,9 +168,13 @@ ca_stride_new (int8_t obj_type, CArray *parent,
151
168
  int8_t ndim, ca_size_t *dim,
152
169
  ca_size_t *strides, ca_size_t base_offset)
153
170
  {
154
- CAStride *ca = (CAStride *) ca_array_alloc(obj_type, ndim);
155
- ca_stride_setup(ca, obj_type, parent,
156
- data_type, bytes, ndim, dim, strides, base_offset);
171
+ CAStride *ca;
172
+ ca_stride_setup_view(NULL, obj_type, parent,
173
+ data_type, bytes, ndim, dim, strides, base_offset);
174
+ ca = (CAStride *) ca_array_alloc(obj_type, ndim);
175
+ ca_stride_setup_view(ca, obj_type, parent,
176
+ data_type, bytes, ndim, dim, strides, base_offset);
177
+ ca_fill_or_free((CArray *) ca, ca_view_inherit_mask, (VALUE) ca);
157
178
  return ca;
158
179
  }
159
180
 
@@ -292,6 +313,38 @@ ca_stride_compose_through (CAStride *leaf, CAStride *parent,
292
313
  out_strides[k] = composed;
293
314
  }
294
315
 
316
+ /* A leaf cell wider than the remainder of its first parent element
317
+ (CARefer reinterpreting two float64 as one complex128) covers several
318
+ consecutive parent elements, and the folded access reads the cell as
319
+ one run of bytes from out_base. That is only true when those parent
320
+ elements are adjacent in parent->parent's memory as well: every cell
321
+ must fit inside the parent's trailing row-major contiguous block.
322
+ Cell starts are flat0 + sum idx[k]*advance[k], so their residues
323
+ modulo the block size C step by g = gcd(C, advance...); every cell
324
+ fits when (flat0 mod g) + span <= g. */
325
+ if ( sub_byte + leaf->bytes > parent->bytes ) {
326
+ ca_size_t span = (sub_byte + leaf->bytes + parent->bytes - 1)
327
+ / parent->bytes;
328
+ ca_size_t block = 1;
329
+ ca_size_t g, flat0 = leaf->base_offset / parent->bytes;
330
+ if ( parent->ndim > 0
331
+ && parent->strides[parent->ndim - 1] == parent->bytes ) {
332
+ block = parent->dim[parent->ndim - 1];
333
+ for (m = parent->ndim - 2; m >= 0; m--) {
334
+ if ( parent->strides[m] != parent->strides[m + 1] * parent->dim[m + 1] )
335
+ break;
336
+ block *= parent->dim[m];
337
+ }
338
+ }
339
+ g = block;
340
+ for (k = 0; k < leaf->ndim; k++) {
341
+ ca_size_t a = leaf->strides[k] / parent->bytes;
342
+ if ( a < 0 ) a = -a;
343
+ while ( a != 0 ) { ca_size_t r = g % a; g = a; a = r; }
344
+ }
345
+ if ( g == 0 || (flat0 % g) + span > g ) return 0;
346
+ }
347
+
295
348
  return 1;
296
349
  }
297
350
 
@@ -402,6 +455,8 @@ ca_stride_compose_to_root (CAStride *leaf,
402
455
 
403
456
  "Identity compose-fold" semantics:
404
457
  - cand->bytes == root->bytes (no byte reinterpret)
458
+ - cand->data_type == root->data_type (no type reinterpret: a caller
459
+ may read the root's cells as typed values)
405
460
  - composed_base == 0
406
461
  - composed_strides[k] match row-major over cand->dim with cand->bytes
407
462
 
@@ -430,6 +485,7 @@ ca_resolve_attached_root_via_identity (CArray *cand)
430
485
  ca_stride_compose_to_root(cs, &root, composed_strides, &composed_base);
431
486
  if ( !root->ptr ) return cand;
432
487
  if ( cs->bytes != root->bytes ) return cand;
488
+ if ( cs->data_type != root->data_type ) return cand;
433
489
  if ( composed_base != 0 ) return cand;
434
490
 
435
491
  s = root->bytes;
@@ -512,19 +568,32 @@ ca_stride_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
512
568
  ca_xfer_index(ca->parent, pidx, data, dir);
513
569
  }
514
570
  else {
515
- char buf[64]; /* parent cell width <= 16 in practice */
516
- char *scratch = (pbytes <= (ca_size_t) sizeof(buf)) ? buf : xmalloc(pbytes);
571
+ /* Byte-mismatched reinterpret. The cell covers the parent elements
572
+ from off / pbytes through the one holding its last byte: one for a
573
+ narrower cell (CAField .real / .imag), several for a wider one
574
+ (CARefer reading two float64 as one complex128). Read all of
575
+ them; a store writes back all of them.
576
+ ALLOCV: the parent's fetch/store can raise (a CAObject parent runs
577
+ Ruby), and a raise must not leak the scratch. */
578
+ volatile VALUE holder;
579
+ ca_size_t addr = off / pbytes;
580
+ ca_size_t sub = off % pbytes;
581
+ ca_size_t span = (sub + ca->bytes + pbytes - 1) / pbytes;
582
+ ca_size_t i;
583
+ char *scratch = ALLOCV_N(char, holder, span * pbytes);
584
+ for (i = 0; i < span; i++) {
585
+ ca_fetch_addr(ca->parent, addr + i, scratch + i * pbytes);
586
+ }
517
587
  if (dir == CA_XFER_GET) {
518
- ca_fetch_addr(ca->parent, off / pbytes, scratch);
519
- memcpy(data, scratch + (off % pbytes), ca->bytes);
588
+ memcpy(data, scratch + sub, ca->bytes);
520
589
  }
521
590
  else {
522
- /* read-modify-write the parent cell for sub-byte reinterpret */
523
- ca_fetch_addr(ca->parent, off / pbytes, scratch);
524
- memcpy(scratch + (off % pbytes), data, ca->bytes);
525
- ca_store_addr(ca->parent, off / pbytes, scratch);
591
+ memcpy(scratch + sub, data, ca->bytes);
592
+ for (i = 0; i < span; i++) {
593
+ ca_store_addr(ca->parent, addr + i, scratch + i * pbytes);
594
+ }
526
595
  }
527
- if (scratch != buf) xfree(scratch);
596
+ ALLOCV_END(holder);
528
597
  }
529
598
  }
530
599
  }
@@ -556,7 +625,7 @@ ca_stride_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
556
625
  ca_size_t composed_base;
557
626
  ca_size_t rbytes;
558
627
  ca_size_t *paddrs;
559
- ca_size_t i, base;
628
+ ca_size_t i;
560
629
  int8_t k;
561
630
  int all_aligned = 1;
562
631
  volatile VALUE holder;
@@ -565,18 +634,12 @@ ca_stride_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
565
634
  ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
566
635
  rbytes = root->bytes;
567
636
 
568
- /* Fast path: identity transform detection -- when the addr remap composed_base +
569
- Σ idx[k]*composed_strides[k] reduces to an identity mapping over the
570
- root's flat byte space (= simple reshape, same bytes, row-major
571
- composed_strides), forward addrs as-is to root without the ALLOCV +
572
- per-cell remap loop. Cascades through chain a[idx_2d] (= outer
573
- reshape -> CAGrid -> inner reshape -> entity): outer CARefer is a
574
- simple reshape so addrs pass through, then CAGrid Y.1.b fast path
575
- triggers on the recursive call, then inner CARefer simple reshape
576
- pass-through to entity. Detection is O(ndim) + O(n). */
577
- if ( n == ca->elements
578
- && ca->bytes == rbytes && composed_base == 0
579
- && ca_xfer_addrs_is_sequential_run(n, addrs, &base) && base == 0 ) {
637
+ /* Identity: same cell size, no offset, row-major composed strides (a
638
+ simple reshape / flatten of the root). A view address is then the
639
+ root address, whatever the addresses are, so the list goes through
640
+ as-is. Cascades through chains such as a[idx_2d] (outer reshape ->
641
+ CAGrid -> inner reshape -> entity). */
642
+ if ( ca->bytes == rbytes && composed_base == 0 ) {
580
643
  ca_size_t s = rbytes;
581
644
  int is_identity = 1;
582
645
  for ( k = ca->ndim - 1; k >= 0; k-- ) {
@@ -589,36 +652,51 @@ ca_stride_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
589
652
  }
590
653
  }
591
654
 
592
- paddrs = ALLOCV_N(ca_size_t, holder, n);
593
- for ( i = 0; i < n; i++ ) {
594
- ca_size_t idx[CA_RANK_MAX];
595
- ca_size_t off = composed_base;
596
- ca_addr2index((CArray *) ca, addrs[i], idx);
655
+ /* Whole-cell strides: translate in cell units, with no per-cell division. */
656
+ if ( ca->bytes == rbytes && composed_base % rbytes == 0 ) {
597
657
  for ( k = 0; k < ca->ndim; k++ ) {
598
- off += idx[k] * composed_strides[k];
599
- }
600
- if ( ca->bytes == rbytes && off % rbytes == 0 ) {
601
- paddrs[i] = off / rbytes;
602
- }
603
- else {
604
- all_aligned = 0;
605
- break;
658
+ if ( composed_strides[k] % rbytes != 0 ) { all_aligned = 0; break; }
606
659
  }
607
660
  }
661
+ else {
662
+ all_aligned = 0;
663
+ }
608
664
 
609
665
  if ( all_aligned ) {
666
+ ca_size_t esteps[CA_RANK_MAX];
667
+ ca_size_t ebase = composed_base / rbytes;
668
+ for ( k = 0; k < ca->ndim; k++ ) {
669
+ esteps[k] = composed_strides[k] / rbytes;
670
+ }
671
+ paddrs = ALLOCV_N(ca_size_t, holder, n);
672
+ if ( ca->ndim == 1 ) {
673
+ for ( i = 0; i < n; i++ ) {
674
+ paddrs[i] = ebase + addrs[i] * esteps[0];
675
+ }
676
+ }
677
+ else {
678
+ for ( i = 0; i < n; i++ ) {
679
+ ca_size_t idx[CA_RANK_MAX];
680
+ ca_size_t off = ebase;
681
+ ca_addr2index((CArray *) ca, addrs[i], idx);
682
+ for ( k = 0; k < ca->ndim; k++ ) {
683
+ off += idx[k] * esteps[k];
684
+ }
685
+ paddrs[i] = off;
686
+ }
687
+ }
610
688
  ca_xfer_addrs(root, n, paddrs, data, dir);
689
+ ALLOCV_END(holder);
690
+ return;
611
691
  }
612
- else {
613
- /* byte-mismatched reinterpret: deliver cell by cell via xfer_index
614
- (still no whole-view attach -- delegates one cell at a time). */
615
- for ( i = 0; i < n; i++ ) {
616
- ca_size_t idx[CA_RANK_MAX];
617
- ca_addr2index((CArray *) ca, addrs[i], idx);
618
- ca_stride_func_xfer_index(ca, idx, d + i * ca->bytes, dir);
619
- }
692
+
693
+ /* Byte-mismatched reinterpret: deliver cell by cell via xfer_index
694
+ (still no whole-view attach -- delegates one cell at a time). */
695
+ for ( i = 0; i < n; i++ ) {
696
+ ca_size_t idx[CA_RANK_MAX];
697
+ ca_addr2index((CArray *) ca, addrs[i], idx);
698
+ ca_stride_func_xfer_index(ca, idx, d + i * ca->bytes, dir);
620
699
  }
621
- ALLOCV_END(holder);
622
700
  }
623
701
 
624
702
  /* Optimised region delivery. Reached when ca->ptr == NULL (the central dispatcher handles the alias / attached case).
@@ -656,6 +734,7 @@ static void
656
734
  ca_stride_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
657
735
  ca_size_t *strides, void *data, int dir)
658
736
  {
737
+ if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
659
738
  CAStride *ca = (CAStride *) ap;
660
739
  CArray *root;
661
740
  ca_size_t composed_strides[CA_RANK_MAX];
@@ -736,16 +815,49 @@ ca_stride_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
736
815
  }
737
816
  }
738
817
 
818
+ /* A reshape over a 1-D cold root (ndim != root->ndim): the request has no
819
+ box in the root's single axis, but the root cells it touches lie in one
820
+ flat span. Ask the root for that span once and walk the request over
821
+ it, as xfer_all does for the whole view. A PUT reads the span first so
822
+ the cells between the requested ones go back unchanged. */
823
+ if (aligned && !root->ptr && ca_func[root->obj_type].xfer_stride
824
+ && ca->bytes == root->bytes && root->ndim == 1
825
+ && root_base % root->bytes == 0) {
826
+ ca_size_t span_lo = root_base;
827
+ ca_size_t span_hi = root_base + ca->bytes;
828
+ for (k = 0; k < ndim; k++) {
829
+ ca_size_t ext = (counts[k] - 1) * root_stride[k];
830
+ if ( root_stride[k] >= 0 ) span_hi += ext;
831
+ else span_lo += ext;
832
+ }
833
+ if ( span_lo >= 0 && span_hi <= root->elements * root->bytes ) {
834
+ volatile VALUE holder;
835
+ ca_size_t rlo = span_lo / root->bytes;
836
+ ca_size_t rcount = (span_hi - span_lo) / root->bytes;
837
+ ca_size_t rstep = root->bytes;
838
+ char *scratch = ALLOCV_N(char, holder, rcount * root->bytes);
839
+ ca_xfer_stride(root, &rlo, &rcount, &rstep, scratch, CA_XFER_GET);
840
+ ca_xfer_strided_walk(scratch + (root_base - span_lo), ca->bytes, ndim,
841
+ counts, root_stride, d, dir);
842
+ if ( dir == CA_XFER_PUT ) {
843
+ ca_xfer_stride(root, &rlo, &rcount, &rstep, scratch, CA_XFER_PUT);
844
+ }
845
+ ALLOCV_END(holder);
846
+ return;
847
+ }
848
+ }
849
+
739
850
  /* Per-cell fallback (correct, no whole-view attach): byte-mismatch
740
851
  reinterpret (CAField), a region that is not a box over our axes (a
741
852
  transposed request onto a degenerate axis, a flat index over several
742
- axes), or a cold non-entity root the branch above could not hand a
743
- region to (its ndim differs from the view's -- e.g. a reshape over a
744
- boundary -- or it has no region slot). ca_stride_func_xfer_index
745
- composes one hop and delegates to the parent. */
853
+ axes), or a cold non-entity root the branches above could not hand a
854
+ region to (a reshape over a root of two or more axes, or one with no
855
+ region slot). ca_stride_func_xfer_index composes one hop and
856
+ delegates to the parent. */
746
857
  if (!aligned || !root->ptr) {
747
858
  ca_size_t idx[CA_RANK_MAX], doff = 0;
748
859
  for (k = 0; k < ndim; k++) idx[k] = 0;
860
+ /* empty: the slot returns on an empty region at its entry */
749
861
  while (1) {
750
862
  ca_size_t off = base_addr, vmidx[CA_RANK_MAX];
751
863
  for (k = 0; k < ndim; k++) off += idx[k] * strides[k];
@@ -942,6 +1054,8 @@ ca_stride_func_fill_stride (void *ap, ca_size_t base, int8_t ndim,
942
1054
  }
943
1055
 
944
1056
  extern int ca_stride_is_contiguous (CAStride *ca); /* defined below; non-static for Tier A */
1057
+ static int ca_layout_is_contiguous (int8_t ndim, const ca_size_t *dim,
1058
+ const ca_size_t *strides, ca_size_t bytes);
945
1059
 
946
1060
  /* Alias fast path:
947
1061
  When the view's strides describe a contiguous row-major run, the
@@ -996,8 +1110,9 @@ extern int ca_stride_is_contiguous (CAStride *ca); /* defined below; non-stati
996
1110
  composes a source down to its root.
997
1111
 
998
1112
  Entities hold their own buffer. A CAStride-family root is one the fold
999
- declined to walk through (a byte reinterpret, say); its own attach folds
1000
- onward as it always has, so leave that chain alone. What is left is the
1113
+ declined to walk through (a reshape over a transpose); its own attach
1114
+ folds onward from itself, so the answer is the one for the root it folds
1115
+ to. What is left is the
1001
1116
  boundary views that compute or fetch their contents -- CAObject, the lazy
1002
1117
  per-element transforms, whatever a companion gem installed -- and of those,
1003
1118
  only the ones with an xfer_stride slot can answer a region request at all.
@@ -1009,10 +1124,41 @@ ca_root_lends_no_memory (void *ap)
1009
1124
  CArray *root = (CArray *) ap;
1010
1125
 
1011
1126
  if ( ca_is_entity(root) ) return 0;
1012
- if ( ca_func[root->obj_type].attach == ca_stride_func.attach ) return 0;
1127
+ if ( ca_func[root->obj_type].attach == ca_stride_func.attach ) {
1128
+ /* The fold stopped at a CAStride (a reshape over a transpose). Its
1129
+ own attach folds onward from itself, so it lends memory exactly when
1130
+ the root it folds to does: over a computed root, attaching it
1131
+ produces all of it. */
1132
+ CArray *below;
1133
+ ca_size_t cs[CA_RANK_MAX];
1134
+ ca_size_t base;
1135
+ ca_stride_compose_to_root((CAStride *) root, &below, cs, &base);
1136
+ return ca_root_lends_no_memory(below);
1137
+ }
1013
1138
  return ca_func[root->obj_type].xfer_stride != NULL;
1014
1139
  }
1015
1140
 
1141
+ /* The same question for the parent of a view that is not itself in the
1142
+ CAStride family (CAGrid, ...): whether the array its cells come from --
1143
+ the parent, or the root a CAStride parent folds to -- has no memory to
1144
+ lend. Like ca_root_lends_no_memory it asks only what those arrays are,
1145
+ so the lifecycle slots can rely on getting the same answer from attach
1146
+ to detach. */
1147
+ int
1148
+ ca_parent_lends_no_memory (void *ap)
1149
+ {
1150
+ CArray *parent = (CArray *) ap;
1151
+
1152
+ if ( ca_is_stride_family(parent) ) {
1153
+ CArray *root;
1154
+ ca_size_t cs[CA_RANK_MAX];
1155
+ ca_size_t base;
1156
+ ca_stride_compose_to_root((CAStride *) parent, &root, cs, &base);
1157
+ return ca_root_lends_no_memory(root);
1158
+ }
1159
+ return ca_root_lends_no_memory(parent);
1160
+ }
1161
+
1016
1162
  /* ca_attach_is_alias asks the same question from carray_core.c: a view whose
1017
1163
  attach owns its buffer does not alias its parent, so writes through
1018
1164
  ca->ptr need a ca_sync and callers must not assume otherwise. */
@@ -1032,7 +1178,16 @@ ca_stride_attach_aliases_root (CAStride *ca)
1032
1178
  return 1;
1033
1179
  }
1034
1180
  ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
1035
- return !ca_root_lends_no_memory(root);
1181
+ /* The same test ca_stride_func_attach makes, and then the same question of
1182
+ the fold boundary. The leaf's own strides are in its parent's address
1183
+ space -- a block over a flip is contiguous there and not in the root --
1184
+ and a boundary that is itself a CAStride (a reshape over a transpose
1185
+ stops the fold) is one whose attach may build a buffer of its own, which
1186
+ this view would then alias without reaching the memory below it. */
1187
+ return !ca_root_lends_no_memory(root)
1188
+ && ca_layout_is_contiguous(ca->ndim, ca->dim, composed_strides,
1189
+ ca->bytes)
1190
+ && ca_attach_is_alias(root);
1036
1191
  }
1037
1192
 
1038
1193
  static void
@@ -1045,14 +1200,14 @@ ca_stride_func_allocate (void *ap)
1045
1200
 
1046
1201
  ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
1047
1202
  if (ca_root_lends_no_memory(root)) {
1048
- ca->ptr = xmalloc(ca_length(ca));
1203
+ ca->ptr = ca_alloc_cells((CArray *) ca);
1049
1204
  return;
1050
1205
  }
1051
1206
  ca_attach(root);
1052
1207
  if (ca_layout_is_contiguous(ca->ndim, ca->dim, composed_strides, ca->bytes)) {
1053
1208
  ca->ptr = root->ptr + composed_base;
1054
1209
  } else {
1055
- ca->ptr = xmalloc(ca_length(ca));
1210
+ ca->ptr = ca_alloc_cells((CArray *) ca);
1056
1211
  }
1057
1212
  }
1058
1213
 
@@ -1081,14 +1236,9 @@ ca_stride_func_attach (void *ap)
1081
1236
  transfer slots. */
1082
1237
  char *buf = xmalloc(ca_length(ca));
1083
1238
  void *args[2];
1084
- int tag = 0;
1085
1239
  args[0] = ca;
1086
1240
  args[1] = buf;
1087
- rb_protect(ca_stride_cold_fill, (VALUE) args, &tag); /* root stays cold */
1088
- if (tag) {
1089
- xfree(buf);
1090
- rb_jump_tag(tag);
1091
- }
1241
+ ca_fill_or_xfree(buf, ca_stride_cold_fill, (VALUE) args); /* root stays cold */
1092
1242
  ca->ptr = buf;
1093
1243
  return;
1094
1244
  }
@@ -1096,7 +1246,7 @@ ca_stride_func_attach (void *ap)
1096
1246
  if (ca_layout_is_contiguous(ca->ndim, ca->dim, composed_strides, ca->bytes)) {
1097
1247
  ca->ptr = root->ptr + composed_base;
1098
1248
  } else {
1099
- ca->ptr = xmalloc(ca_length(ca));
1249
+ ca->ptr = ca_alloc_cells((CArray *) ca);
1100
1250
  ca_stride_xfer_with_layout(ca, 0, root->ptr + composed_base, composed_strides);
1101
1251
  }
1102
1252
  }
@@ -1223,6 +1373,8 @@ ca_stride_func_xfer_all (void *ap, void *data, int dir)
1223
1373
  char *ptr0 = ca->ptr;
1224
1374
  char *ptr = (char *) data;
1225
1375
 
1376
+ if ( ca->elements == 0 ) return;
1377
+
1226
1378
  ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
1227
1379
 
1228
1380
  /* Partial materialise: when the fold stops at a cold boundary view that knows how to deliver a region (has an
@@ -1324,6 +1476,81 @@ ca_stride_func_xfer_all (void *ap, void *data, int dir)
1324
1476
  return;
1325
1477
  }
1326
1478
 
1479
+ /* A request the bridges above could not express as a region goes to the
1480
+ root as the list of its cells. A write never makes a round trip of
1481
+ the whole root: a root that repeats its cells (CATile) would have the
1482
+ stale copies the caller never wrote land after the written ones, and a
1483
+ root that converts on the way would bring the unwritten cells back
1484
+ changed. A read does not make the root produce cells it was not asked
1485
+ for, unless it is asked for as many as it has. The root's addresses
1486
+ are whole elements here, as in the region hand-off above. */
1487
+ if ( ( dir == CA_XFER_PUT || ca->elements < root->elements )
1488
+ && ca->bytes == root->bytes && composed_base % root->bytes == 0 ) {
1489
+ volatile VALUE holder;
1490
+ ca_size_t *addrs = ALLOCV_N(ca_size_t, holder, ca->elements);
1491
+ ca_size_t idx[CA_RANK_MAX], n = 0;
1492
+ int8_t k;
1493
+ for (k = 0; k < ca->ndim; k++) idx[k] = 0;
1494
+ /* empty: the slot returns on an empty view at its entry */
1495
+ while (1) {
1496
+ ca_size_t off = composed_base;
1497
+ for (k = 0; k < ca->ndim; k++) off += idx[k] * composed_strides[k];
1498
+ addrs[n++] = off / root->bytes;
1499
+ k = ca->ndim - 1;
1500
+ while (k >= 0) { if (++idx[k] < ca->dim[k]) break; idx[k] = 0; k--; }
1501
+ if (k < 0) break;
1502
+ }
1503
+ ca_xfer_addrs(root, n, addrs, ptr, dir);
1504
+ ALLOCV_END(holder);
1505
+ return;
1506
+ }
1507
+
1508
+ /* A read through a byte reinterpret (CAField, a refer to another width)
1509
+ over a cold root: the root's addresses are not this view's cells, but
1510
+ each cell lies in a run of whole root elements. Ask the root for those
1511
+ elements and cut the cell's bytes out of them. A write keeps the
1512
+ whole-root round trip below: two cells can share a root element, and
1513
+ writing back each one's copy of it would drop the other's bytes. */
1514
+ if ( dir == CA_XFER_GET && ca->elements > 0 && ca->bytes != root->bytes
1515
+ && ca_func[root->obj_type].xfer_stride ) {
1516
+ volatile VALUE ah = 0, sh = 0;
1517
+ ca_size_t rb = root->bytes, cb = ca->bytes;
1518
+ ca_size_t per = (cb + rb - 1) / rb + 1;
1519
+ ca_size_t *addrs = ALLOCV_N(ca_size_t, ah, ca->elements * per);
1520
+ char *scratch;
1521
+ ca_size_t idx[CA_RANK_MAX], n = 0, i;
1522
+ ca_size_t *first = ALLOCV_N(ca_size_t, sh, ca->elements);
1523
+ int8_t k;
1524
+ int ok = 1;
1525
+ for (k = 0; k < ca->ndim; k++) idx[k] = 0;
1526
+ for (i = 0; ok; i++) {
1527
+ ca_size_t off = composed_base, e, e_end;
1528
+ for (k = 0; k < ca->ndim; k++) off += idx[k] * composed_strides[k];
1529
+ if ( off < 0 || off + cb > root->elements * rb ) { ok = 0; break; }
1530
+ first[i] = n * rb + off % rb;
1531
+ for (e = off / rb, e_end = (off + cb - 1) / rb; e <= e_end; e++) {
1532
+ addrs[n++] = e;
1533
+ }
1534
+ k = ca->ndim - 1;
1535
+ while (k >= 0) { if (++idx[k] < ca->dim[k]) break; idx[k] = 0; k--; }
1536
+ if (k < 0) break;
1537
+ }
1538
+ if ( ok && n < root->elements ) {
1539
+ volatile VALUE dh = 0;
1540
+ scratch = ALLOCV_N(char, dh, n * rb);
1541
+ ca_xfer_addrs(root, n, addrs, scratch, CA_XFER_GET);
1542
+ for (i = 0; i < ca->elements; i++) {
1543
+ memcpy(ptr + i * cb, scratch + first[i], cb);
1544
+ }
1545
+ ALLOCV_END(dh);
1546
+ ALLOCV_END(sh);
1547
+ ALLOCV_END(ah);
1548
+ return;
1549
+ }
1550
+ ALLOCV_END(sh);
1551
+ ALLOCV_END(ah);
1552
+ }
1553
+
1327
1554
  /* Cold root without an xfer_stride slot, or an ndim mismatch the bridges
1328
1555
  above could not express: materialise the root into scratch via
1329
1556
  ca_xfer_all and run the direct strided gather/scatter against that.
@@ -1342,11 +1569,14 @@ ca_stride_func_xfer_all (void *ap, void *data, int dir)
1342
1569
  ca_stride_xfer_with_layout(ca, (dir == CA_XFER_PUT) ? 1 : 0,
1343
1570
  root->ptr + composed_base, composed_strides);
1344
1571
  ca->ptr = ptr0;
1572
+ /* Put root->ptr back before the PUT: the PUT can raise (a lazy
1573
+ backing's sync failing), and the scratch is gone once this frame
1574
+ is. */
1575
+ root->ptr = root_ptr_saved;
1345
1576
  if ( dir == CA_XFER_PUT ) {
1346
1577
  /* Push back scratch (modified by scatter) to root. */
1347
1578
  ca_xfer_all(root, root_scratch, CA_XFER_PUT);
1348
1579
  }
1349
- root->ptr = root_ptr_saved;
1350
1580
  ALLOCV_END(holder);
1351
1581
  }
1352
1582
  }
@@ -1849,6 +2079,7 @@ rb_cs_initialize_copy (VALUE self, VALUE other)
1849
2079
  {
1850
2080
  CAStride *ca, *cs;
1851
2081
  TypedData_Get_Struct(self, CAStride, &castride_data_type, ca);
2082
+ ca_check_uninitialized(ca);
1852
2083
  TypedData_Get_Struct(other, CAStride, &castride_data_type, cs);
1853
2084
  /* Pool framework: self was created by rb_cs_s_allocate (= TypedData_Make_Struct),
1854
2085
  so ca->_pool is NULL. Wire up the pool before ca_stride_setup so the
@@ -1927,9 +2158,10 @@ rb_ca_as_strided (int argc, VALUE *argv, VALUE self)
1927
2158
  "shape (%ld) and strides (%ld) length mismatch",
1928
2159
  len, RARRAY_LEN(rstrides));
1929
2160
  }
1930
- if (len <= 0 || len > CA_RANK_MAX) {
2161
+ if (len <= 0) {
1931
2162
  rb_raise(rb_eArgError, "invalid ndim %ld", len);
1932
2163
  }
2164
+ ca_check_rank_fits(len, NULL);
1933
2165
  ndim = (int8_t) len;
1934
2166
  for (i = 0; i < len; i++) {
1935
2167
  shape[i] = NUM2SIZE(RARRAY_AREF(rshape, i));
@@ -1939,8 +2171,10 @@ rb_ca_as_strided (int argc, VALUE *argv, VALUE self)
1939
2171
  base_offset = NUM2SIZE(roffset);
1940
2172
  }
1941
2173
 
1942
- return rb_ca_stride_new(self, parent->data_type, parent->bytes,
1943
- ndim, shape, strides, base_offset);
2174
+ VALUE obj = rb_ca_stride_new(self, parent->data_type, parent->bytes,
2175
+ ndim, shape, strides, base_offset);
2176
+ CA_WRAPPER_LIFT(obj, self, parent);
2177
+ return obj;
1944
2178
  }
1945
2179
 
1946
2180
  /* sliding_windows(*window, step: nil) -- overlapping-window view over every
@@ -1965,11 +2199,7 @@ rb_ca_sliding_windows (int argc, VALUE *argv, VALUE self)
1965
2199
  TypedData_Get_Struct(self, CArray, &carray_data_type, parent);
1966
2200
 
1967
2201
  ndim = parent->ndim;
1968
- if (2 * (int) ndim > CA_RANK_MAX) {
1969
- rb_raise(rb_eArgError,
1970
- "sliding_windows: result rank %d exceeds CA_RANK_MAX (%d)",
1971
- 2 * (int) ndim, CA_RANK_MAX);
1972
- }
2202
+ ca_check_rank_fits(2 * (long) ndim, "sliding_windows");
1973
2203
 
1974
2204
  rb_scan_args(argc, argv, "*:", (VALUE *) &rposary, (VALUE *) &ropt);
1975
2205
  if (! NIL_P(ropt)) {
@@ -2051,8 +2281,10 @@ rb_ca_sliding_windows (int argc, VALUE *argv, VALUE self)
2051
2281
  outstrides[ndim + i] = parent_byte_stride[i];
2052
2282
  }
2053
2283
 
2054
- return rb_ca_stride_new(self, parent->data_type, parent->bytes,
2055
- (int8_t)(2 * ndim), outdim, outstrides, 0);
2284
+ VALUE obj = rb_ca_stride_new(self, parent->data_type, parent->bytes,
2285
+ (int8_t)(2 * ndim), outdim, outstrides, 0);
2286
+ CA_WRAPPER_LIFT(obj, self, parent);
2287
+ return obj;
2056
2288
  }
2057
2289
 
2058
2290
  /* unfold(*window, step: nil) -- sliding_windows over the leading `S` axes
@@ -2111,11 +2343,7 @@ rb_ca_unfold (int argc, VALUE *argv, VALUE self)
2111
2343
 
2112
2344
  ntrail = ndim - nspatial;
2113
2345
  outrank = (int) ndim + (int) nspatial;
2114
- if (outrank > CA_RANK_MAX) {
2115
- rb_raise(rb_eArgError,
2116
- "unfold: result rank %d exceeds CA_RANK_MAX (%d)",
2117
- outrank, CA_RANK_MAX);
2118
- }
2346
+ ca_check_rank_fits(outrank, "unfold");
2119
2347
 
2120
2348
  if (NIL_P(rstep)) {
2121
2349
  for (i = 0; i < nspatial; i++) step[i] = 1;
@@ -2173,8 +2401,10 @@ rb_ca_unfold (int argc, VALUE *argv, VALUE self)
2173
2401
  outstrides[2 * nspatial + i] = parent_byte_stride[nspatial + i];
2174
2402
  }
2175
2403
 
2176
- return rb_ca_stride_new(self, parent->data_type, parent->bytes,
2177
- (int8_t) outrank, outdim, outstrides, 0);
2404
+ VALUE obj = rb_ca_stride_new(self, parent->data_type, parent->bytes,
2405
+ (int8_t) outrank, outdim, outstrides, 0);
2406
+ CA_WRAPPER_LIFT(obj, self, parent);
2407
+ return obj;
2178
2408
  }
2179
2409
 
2180
2410
  /* block_view(*block) -- non-overlapping tile view. Parent [d0..dN-1]
@@ -2197,11 +2427,7 @@ rb_ca_block_view (int argc, VALUE *argv, VALUE self)
2197
2427
  TypedData_Get_Struct(self, CArray, &carray_data_type, parent);
2198
2428
 
2199
2429
  ndim = parent->ndim;
2200
- if (2 * (int) ndim > CA_RANK_MAX) {
2201
- rb_raise(rb_eArgError,
2202
- "block_view: result rank %d exceeds CA_RANK_MAX (%d)",
2203
- 2 * (int) ndim, CA_RANK_MAX);
2204
- }
2430
+ ca_check_rank_fits(2 * (long) ndim, "block_view");
2205
2431
 
2206
2432
  rb_scan_args(argc, argv, "*:", (VALUE *) &rposary, (VALUE *) &ropt);
2207
2433
  rb_reject_options(ropt);
@@ -2258,8 +2484,12 @@ rb_ca_block_view (int argc, VALUE *argv, VALUE self)
2258
2484
  outstrides[ndim + i] = parent_byte_stride[i];
2259
2485
  }
2260
2486
 
2261
- return rb_ca_stride_new(self, parent->data_type, parent->bytes,
2262
- (int8_t)(2 * ndim), outdim, outstrides, 0);
2487
+ VALUE obj = rb_ca_stride_new(self, parent->data_type, parent->bytes,
2488
+ (int8_t)(2 * ndim), outdim, outstrides, 0);
2489
+ /* The Face stays on top, as for every view: a tile reduction over a
2490
+ bare block_view compared the Face's surface bytes. */
2491
+ CA_WRAPPER_LIFT(obj, self, parent);
2492
+ return obj;
2263
2493
  }
2264
2494
 
2265
2495
  /* defined in ca_obj_transpose.c */
@@ -2300,7 +2530,7 @@ rb_ca_dim_view (int argc, VALUE *argv, VALUE self)
2300
2530
  (int) n_iter, (int) ndim);
2301
2531
  }
2302
2532
  for (i = 0; i < n_iter; i++) {
2303
- iter_axes[i] = NUM2SIZE(RARRAY_AREF(aary, i));
2533
+ iter_axes[i] = ca_normalize_axis_arg(RARRAY_AREF(aary, i), ndim, NULL);
2304
2534
  }
2305
2535
  }
2306
2536
  else if (nargs >= 1) {
@@ -2311,7 +2541,7 @@ rb_ca_dim_view (int argc, VALUE *argv, VALUE self)
2311
2541
  }
2312
2542
  n_iter = (int8_t) nargs;
2313
2543
  for (i = 0; i < n_iter; i++) {
2314
- iter_axes[i] = NUM2SIZE(RARRAY_AREF(rposary, i));
2544
+ iter_axes[i] = ca_normalize_axis_arg(RARRAY_AREF(rposary, i), ndim, NULL);
2315
2545
  }
2316
2546
  }
2317
2547
  else {
@@ -2319,23 +2549,16 @@ rb_ca_dim_view (int argc, VALUE *argv, VALUE self)
2319
2549
  "dim_view: at least one iteration axis required");
2320
2550
  }
2321
2551
 
2322
- /* Normalize negative indices and validate range / distinctness. */
2552
+ /* The axes are normalized as they are read; check distinctness. */
2323
2553
  for (i = 0; i < ndim; i++) seen[i] = 0;
2324
2554
  for (i = 0; i < n_iter; i++) {
2325
2555
  ca_size_t a = iter_axes[i];
2326
- if (a < 0) a += ndim;
2327
- if (a < 0 || a >= ndim) {
2328
- rb_raise(rb_eArgError,
2329
- "dim_view: axis %lld out of range for ndim %d",
2330
- (long long) iter_axes[i], (int) ndim);
2331
- }
2332
2556
  if (seen[a]) {
2333
2557
  rb_raise(rb_eArgError,
2334
2558
  "dim_view: duplicate iteration axis %lld",
2335
2559
  (long long) a);
2336
2560
  }
2337
2561
  seen[a] = 1;
2338
- iter_axes[i] = a;
2339
2562
  }
2340
2563
 
2341
2564
  /* imap: iter axes first (in given order), then remaining axes
@@ -2407,12 +2630,8 @@ rb_ca_flip_axis (VALUE self, long axis)
2407
2630
  rb_check_carray_object(self);
2408
2631
  TypedData_Get_Struct(self, CArray, &carray_data_type, parent);
2409
2632
  int8_t ndim = parent->ndim;
2410
- long a = (axis < 0) ? (axis + ndim) : axis;
2411
- if (a < 0 || a >= ndim) {
2412
- rb_raise(rb_eArgError,
2413
- "flip_axis: axis %ld out of range for ndim %d",
2414
- axis, (int) ndim);
2415
- }
2633
+ long a = rb_ca_normalize_axis_for_ndim(axis, ndim, /* axis-check: a long from a C caller */
2634
+ "flip_axis");
2416
2635
  int8_t flip[CA_RANK_MAX] = {0};
2417
2636
  flip[a] = 1;
2418
2637
  return rb_ca_flip_build_view(self, parent, flip);
@@ -2439,8 +2658,8 @@ rb_ca_flip (int argc, VALUE *argv, VALUE self)
2439
2658
 
2440
2659
  for (i = 0; i < ndim; i++) flip[i] = 0;
2441
2660
 
2442
- if (nargs == 0) {
2443
- /* No args: flip every axis. */
2661
+ if (nargs == 0 || (nargs == 1 && NIL_P(RARRAY_AREF(rposary, 0)))) {
2662
+ /* No args, or a lone nil: flip every axis. */
2444
2663
  for (i = 0; i < ndim; i++) flip[i] = 1;
2445
2664
  n_axes = ndim;
2446
2665
  }
@@ -2454,7 +2673,7 @@ rb_ca_flip (int argc, VALUE *argv, VALUE self)
2454
2673
  (int) n_axes, (int) ndim);
2455
2674
  }
2456
2675
  for (i = 0; i < n_axes; i++) {
2457
- axes[i] = NUM2SIZE(RARRAY_AREF(aary, i));
2676
+ axes[i] = ca_normalize_axis_arg(RARRAY_AREF(aary, i), ndim, NULL);
2458
2677
  }
2459
2678
  }
2460
2679
  else {
@@ -2465,18 +2684,12 @@ rb_ca_flip (int argc, VALUE *argv, VALUE self)
2465
2684
  }
2466
2685
  n_axes = (int8_t) nargs;
2467
2686
  for (i = 0; i < n_axes; i++) {
2468
- axes[i] = NUM2SIZE(RARRAY_AREF(rposary, i));
2687
+ axes[i] = ca_normalize_axis_arg(RARRAY_AREF(rposary, i), ndim, NULL);
2469
2688
  }
2470
2689
  }
2471
2690
 
2472
2691
  for (i = 0; i < n_axes; i++) {
2473
2692
  ca_size_t a = axes[i];
2474
- if (a < 0) a += ndim;
2475
- if (a < 0 || a >= ndim) {
2476
- rb_raise(rb_eArgError,
2477
- "flip: axis %lld out of range for ndim %d",
2478
- (long long) axes[i], (int) ndim);
2479
- }
2480
2693
  if (flip[a]) {
2481
2694
  rb_raise(rb_eArgError,
2482
2695
  "flip: duplicate axis %lld",
@@ -2550,18 +2763,8 @@ rb_ca_diagonal (int argc, VALUE *argv, VALUE self)
2550
2763
  rb_raise(rb_eArgError,
2551
2764
  "diagonal: axis: must be an Array of 2 integers");
2552
2765
  }
2553
- ai = NUM2SIZE(RARRAY_AREF(raxis, 0));
2554
- aj = NUM2SIZE(RARRAY_AREF(raxis, 1));
2555
- if (ai < 0) ai += ndim;
2556
- if (aj < 0) aj += ndim;
2557
- if (ai < 0 || ai >= ndim) {
2558
- rb_raise(rb_eArgError, "diagonal: axis[0] out of range for ndim %d",
2559
- (int) ndim);
2560
- }
2561
- if (aj < 0 || aj >= ndim) {
2562
- rb_raise(rb_eArgError, "diagonal: axis[1] out of range for ndim %d",
2563
- (int) ndim);
2564
- }
2766
+ ai = ca_normalize_axis_arg(RARRAY_AREF(raxis, 0), ndim, NULL);
2767
+ aj = ca_normalize_axis_arg(RARRAY_AREF(raxis, 1), ndim, NULL);
2565
2768
  if (ai == aj) {
2566
2769
  rb_raise(rb_eArgError, "diagonal: axis[0] and axis[1] must be distinct");
2567
2770
  }
@@ -2589,7 +2792,9 @@ rb_ca_diagonal (int argc, VALUE *argv, VALUE self)
2589
2792
  base_offset = offset * parent_byte_stride[aj];
2590
2793
  }
2591
2794
  else {
2592
- ca_size_t neg = -offset;
2795
+ /* compare before negating: -offset overflows for the most negative
2796
+ offset */
2797
+ ca_size_t neg = ( offset <= -parent->dim[ai] ) ? parent->dim[ai] : -offset;
2593
2798
  if (neg >= parent->dim[ai]) {
2594
2799
  diag_len = 0;
2595
2800
  }
@@ -2600,6 +2805,9 @@ rb_ca_diagonal (int argc, VALUE *argv, VALUE self)
2600
2805
  }
2601
2806
  base_offset = neg * parent_byte_stride[ai];
2602
2807
  }
2808
+ if (diag_len == 0) {
2809
+ base_offset = 0; /* an offset past the edge can overflow the product */
2810
+ }
2603
2811
 
2604
2812
  /* build output: kept axes (in original order), then diagonal axis */
2605
2813
  out_k = 0;