carray 3.0.3 → 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 (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +264 -0
  3. data/README.md +3 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +1 -6
  7. data/ext/ca_axis_group.c +27 -27
  8. data/ext/ca_binop_dispatch.c +1 -29
  9. data/ext/ca_binop_dispatch.h +0 -1
  10. data/ext/ca_categorical_iterator.c +205 -134
  11. data/ext/ca_compare.c +7 -18
  12. data/ext/ca_group_iter.c +16 -17
  13. data/ext/ca_kernel_iterator.c +1234 -1994
  14. data/ext/ca_kernel_iterator.h +391 -437
  15. data/ext/ca_monop_dispatch.c +82 -59
  16. data/ext/ca_monop_dispatch.h +1 -0
  17. data/ext/ca_obj_array.c +96 -39
  18. data/ext/ca_obj_bincmp.c +24 -21
  19. data/ext/ca_obj_binop.c +15 -26
  20. data/ext/ca_obj_bitarray.c +3 -2
  21. data/ext/ca_obj_bitfield.c +5 -6
  22. data/ext/ca_obj_block.c +18 -9
  23. data/ext/ca_obj_byte_swap.c +5 -6
  24. data/ext/ca_obj_const_string.c +173 -19
  25. data/ext/ca_obj_face.c +238 -57
  26. data/ext/ca_obj_face.h +19 -0
  27. data/ext/ca_obj_fake.c +15 -29
  28. data/ext/ca_obj_farray.c +6 -3
  29. data/ext/ca_obj_field.c +17 -5
  30. data/ext/ca_obj_fixlen_string.c +2 -3
  31. data/ext/ca_obj_grid.c +406 -132
  32. data/ext/ca_obj_meld.c +172 -34
  33. data/ext/ca_obj_moncmp.c +23 -8
  34. data/ext/ca_obj_monop.c +67 -57
  35. data/ext/ca_obj_object.c +157 -51
  36. data/ext/ca_obj_record.c +12 -7
  37. data/ext/ca_obj_reduce.c +13 -8
  38. data/ext/ca_obj_refer.c +25 -27
  39. data/ext/ca_obj_remap.c +30 -9
  40. data/ext/ca_obj_repeat.c +9 -10
  41. data/ext/ca_obj_roll.c +6 -5
  42. data/ext/ca_obj_select.c +14 -9
  43. data/ext/ca_obj_select_axis.c +15 -47
  44. data/ext/ca_obj_shift.c +11 -1
  45. data/ext/ca_obj_stack.c +179 -34
  46. data/ext/ca_obj_stride.c +162 -115
  47. data/ext/ca_obj_string.c +71 -4
  48. data/ext/ca_obj_tile.c +6 -5
  49. data/ext/ca_obj_time.c +22 -6
  50. data/ext/ca_obj_timedelta.c +22 -6
  51. data/ext/ca_obj_transpose.c +5 -3
  52. data/ext/ca_obj_triop.c +23 -32
  53. data/ext/ca_obj_window.c +190 -26
  54. data/ext/ca_sweep_engine.c +2 -2
  55. data/ext/caframe_csv_reader.c +80 -1
  56. data/ext/carray.h +181 -75
  57. data/ext/carray_access.c +184 -96
  58. data/ext/carray_address_basis.c +343 -87
  59. data/ext/carray_attribute.c +89 -6
  60. data/ext/carray_bincount.c +10 -8
  61. data/ext/carray_broadcast.c +13 -8
  62. data/ext/carray_build_flags.h +3 -0
  63. data/ext/carray_call_cfunc.c +108 -0
  64. data/ext/carray_cast.c +142 -24
  65. data/ext/carray_cast_func.rb +46 -6
  66. data/ext/carray_class.c +4 -3
  67. data/ext/carray_config.h +28 -0
  68. data/ext/carray_conversion.c +16 -7
  69. data/ext/carray_copy.c +51 -14
  70. data/ext/carray_core.c +147 -55
  71. data/ext/carray_count.c +50 -20
  72. data/ext/carray_element.c +76 -33
  73. data/ext/carray_factorize.c +118 -64
  74. data/ext/carray_generate.c +191 -50
  75. data/ext/carray_histogram.c +62 -17
  76. data/ext/carray_hold.c +12 -15
  77. data/ext/carray_index_classifier.c +388 -176
  78. data/ext/carray_internal.h +336 -29
  79. data/ext/carray_kernels_bincmp.c +4453 -0
  80. data/ext/carray_kernels_binop.c +11368 -0
  81. data/ext/carray_kernels_init.c +1734 -0
  82. data/ext/carray_kernels_moncmp.c +2104 -0
  83. data/ext/carray_kernels_monop.c +18369 -0
  84. data/ext/carray_kernels_reduce_aggregate.c +23818 -0
  85. data/ext/carray_kernels_reduce_boolean.c +406 -0
  86. data/ext/carray_kernels_reduce_cumulative.c +10086 -0
  87. data/ext/carray_kernels_reduce_extreme.c +17634 -0
  88. data/ext/carray_kernels_reduce_variance.c +3849 -0
  89. data/ext/carray_kernels_scan.c +4302 -0
  90. data/ext/carray_kernels_search.c +33829 -0
  91. data/ext/carray_kernels_sort.c +10906 -0
  92. data/ext/carray_kernels_triop.c +1864 -0
  93. data/ext/carray_key_windows.c +216 -0
  94. data/ext/carray_lazy.c +170 -111
  95. data/ext/carray_mask.c +292 -72
  96. data/ext/carray_median_percentile.c +82 -28
  97. data/ext/carray_memory_view.c +669 -561
  98. data/ext/carray_operator.c +124 -47
  99. data/ext/carray_order.c +384 -76
  100. data/ext/carray_partition.c +28 -27
  101. data/ext/carray_random.c +67 -13
  102. data/ext/carray_round.c +273 -0
  103. data/ext/carray_scatter.c +63 -38
  104. data/ext/carray_segment.c +3 -3
  105. data/ext/carray_slab.c +91 -28
  106. data/ext/carray_sort.c +35 -35
  107. data/ext/carray_struct.c +19 -18
  108. data/ext/carray_test.c +26 -5
  109. data/ext/carray_undef.c +4 -0
  110. data/ext/carray_utils.c +246 -45
  111. data/ext/mk_call_cfunc.rb +19 -2
  112. data/ext/mkkernel.rb +198 -240
  113. data/ext/ruby_carray.c +10 -4
  114. data/ext/version.h +4 -4
  115. data/lib/carray/attribute.rb +126 -22
  116. data/lib/carray/autoload_carray.rb +8 -31
  117. data/lib/carray/autoload_method_extension.rb +14 -5
  118. data/lib/carray/axis_group.rb +255 -106
  119. data/lib/carray/basics.rb +78 -10
  120. data/lib/carray/bincount_nd.rb +12 -2
  121. data/lib/carray/block_iterator.rb +126 -23
  122. data/lib/carray/boolean_reduce.rb +17 -17
  123. data/lib/carray/categorical.rb +165 -43
  124. data/lib/carray/categorical_iterator.rb +130 -27
  125. data/lib/carray/complex.rb +5 -5
  126. data/lib/carray/conditional.rb +67 -18
  127. data/lib/carray/const_string.rb +14 -5
  128. data/lib/carray/construct.rb +15 -11
  129. data/lib/carray/data_type_extension.rb +1 -0
  130. data/lib/carray/distance_limit.rb +77 -0
  131. data/lib/carray/fixlen_string.rb +7 -1
  132. data/lib/carray/frame/column_verbs.rb +70 -0
  133. data/lib/carray/frame/convert.rb +69 -0
  134. data/lib/carray/frame/csv_parser.rb +111 -17
  135. data/lib/carray/frame/describe.rb +3 -2
  136. data/lib/carray/frame/frame.rb +246 -21
  137. data/lib/carray/frame/group.rb +510 -95
  138. data/lib/carray/frame/io.rb +115 -28
  139. data/lib/carray/frame/join.rb +312 -51
  140. data/lib/carray/frame/records.rb +83 -7
  141. data/lib/carray/frame/reshape.rb +42 -35
  142. data/lib/carray/frame/sort.rb +7 -19
  143. data/lib/carray/frame/verbs.rb +188 -48
  144. data/lib/carray/frame/windows.rb +117 -0
  145. data/lib/carray/frame.rb +2 -0
  146. data/lib/carray/fuse_source.rb +11 -4
  147. data/lib/carray/fusion.rb +40 -16
  148. data/lib/carray/histogram.rb +6 -1
  149. data/lib/carray/inspect.rb +31 -8
  150. data/lib/carray/iterator.rb +85 -7
  151. data/lib/carray/key_windows.rb +620 -0
  152. data/lib/carray/lazy.rb +182 -183
  153. data/lib/carray/mask_gap_fill.rb +63 -57
  154. data/lib/carray/meld_reduce.rb +12 -14
  155. data/lib/carray/methods/bin.rb +26 -18
  156. data/lib/carray/methods/bincount.rb +16 -0
  157. data/lib/carray/methods/bit_string.rb +1 -1
  158. data/lib/carray/methods/choose.rb +16 -3
  159. data/lib/carray/methods/composition.rb +3 -1
  160. data/lib/carray/methods/gather_nd.rb +14 -5
  161. data/lib/carray/methods/insert_block.rb +2 -2
  162. data/lib/carray/methods/is_between.rb +59 -0
  163. data/lib/carray/methods/is_in.rb +53 -36
  164. data/lib/carray/methods/join.rb +1 -6
  165. data/lib/carray/methods/lag.rb +49 -0
  166. data/lib/carray/methods/locate_addr.rb +47 -51
  167. data/lib/carray/methods/mode.rb +1 -1
  168. data/lib/carray/methods/nunique.rb +11 -6
  169. data/lib/carray/methods/resize.rb +3 -3
  170. data/lib/carray/methods/value_counts.rb +4 -11
  171. data/lib/carray/record_order.rb +3 -3
  172. data/lib/carray/rng.rb +3 -0
  173. data/lib/carray/runtime.rb +53 -8
  174. data/lib/carray/segment_iterator.rb +313 -82
  175. data/lib/carray/serialize.rb +24 -10
  176. data/lib/carray/slab_iterator.rb +50 -0
  177. data/lib/carray/string.rb +105 -5
  178. data/lib/carray/string_operation_extension.rb +8 -1
  179. data/lib/carray/struct.rb +9 -21
  180. data/lib/carray/struct_builder.rb +19 -8
  181. data/lib/carray/time.rb +726 -104
  182. data/lib/carray/window_iterator.rb +122 -16
  183. data/lib/carray.rb +7 -0
  184. data/yard-stubs/ca_obj_bitfield.rb +1 -1
  185. data/yard-stubs/ca_obj_field.rb +4 -0
  186. data/yard-stubs/ca_obj_grid.rb +21 -10
  187. data/yard-stubs/ca_obj_refer.rb +2 -1
  188. data/yard-stubs/ca_obj_roll.rb +2 -2
  189. data/yard-stubs/ca_obj_shift.rb +5 -1
  190. data/yard-stubs/ca_obj_tile.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +8 -3
  192. data/yard-stubs/carray_access.rb +6 -1
  193. data/yard-stubs/carray_attribute.rb +0 -5
  194. data/yard-stubs/carray_cast.rb +10 -0
  195. data/yard-stubs/carray_conversion.rb +2 -1
  196. data/yard-stubs/carray_copy.rb +4 -2
  197. data/yard-stubs/carray_count.rb +4 -4
  198. data/yard-stubs/carray_element.rb +10 -4
  199. data/yard-stubs/carray_generate.rb +21 -1
  200. data/yard-stubs/carray_mask.rb +26 -2
  201. data/yard-stubs/carray_math.rb +27 -1
  202. data/yard-stubs/carray_median_percentile.rb +5 -2
  203. data/yard-stubs/carray_memory_view.rb +40 -23
  204. data/yard-stubs/carray_order.rb +29 -18
  205. data/yard-stubs/carray_random.rb +13 -4
  206. data/yard-stubs/carray_scatter.rb +14 -9
  207. data/yard-stubs/carray_slab.rb +9 -2
  208. data/yard-stubs/carray_sort.rb +6 -5
  209. data/yard-stubs/carray_test.rb +5 -3
  210. metadata +25 -3
  211. data/lib/carray/arrow_tensor.rb +0 -401
  212. data/lib/carray/table.rb +0 -108
data/ext/ca_obj_stride.c CHANGED
@@ -20,7 +20,7 @@
20
20
  ---------------------------------------------------------------------------- */
21
21
 
22
22
  #include "carray.h"
23
- #include "carray_internal.h" /* ca_axis_integer */
23
+ #include "carray_internal.h" /* ca_normalize_axis_arg */
24
24
  #include "ca_iter_substrate.h"
25
25
  #include "ca_obj_face.h" /* ca_is_face, used by the compose-fold walk */
26
26
 
@@ -94,17 +94,15 @@ int8_t CA_OBJ_STRIDE; /* assigned at Init time via ca_install_obj_type */
94
94
  override pattern and makes the C-level dispatch wire up correctly
95
95
  on the first try. */
96
96
  int
97
- ca_stride_setup (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)
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)
101
101
  {
102
102
  ca_size_t elements;
103
103
  int i;
104
104
 
105
- if (ndim < 0 || ndim > CA_RANK_MAX) {
106
- rb_raise(rb_eArgError, "invalid ndim %d", (int) ndim);
107
- }
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,
@@ -144,10 +142,23 @@ ca_stride_setup (CAStride *ca, int8_t obj_type, CArray *parent,
144
142
  ca->strides[i] = strides[i];
145
143
  }
146
144
 
147
- if (parent && ca_has_mask(parent)) {
148
- ca_create_mask(ca);
149
- }
145
+ return 0;
146
+ }
150
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
+ }
151
162
  return 0;
152
163
  }
153
164
 
@@ -158,11 +169,12 @@ ca_stride_new (int8_t obj_type, CArray *parent,
158
169
  ca_size_t *strides, ca_size_t base_offset)
159
170
  {
160
171
  CAStride *ca;
161
- ca_stride_setup(NULL, obj_type, parent,
162
- data_type, bytes, ndim, dim, strides, base_offset);
172
+ ca_stride_setup_view(NULL, obj_type, parent,
173
+ data_type, bytes, ndim, dim, strides, base_offset);
163
174
  ca = (CAStride *) ca_array_alloc(obj_type, ndim);
164
- ca_stride_setup(ca, obj_type, parent,
165
- data_type, bytes, ndim, dim, strides, base_offset);
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);
166
178
  return ca;
167
179
  }
168
180
 
@@ -443,6 +455,8 @@ ca_stride_compose_to_root (CAStride *leaf,
443
455
 
444
456
  "Identity compose-fold" semantics:
445
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)
446
460
  - composed_base == 0
447
461
  - composed_strides[k] match row-major over cand->dim with cand->bytes
448
462
 
@@ -471,6 +485,7 @@ ca_resolve_attached_root_via_identity (CArray *cand)
471
485
  ca_stride_compose_to_root(cs, &root, composed_strides, &composed_base);
472
486
  if ( !root->ptr ) return cand;
473
487
  if ( cs->bytes != root->bytes ) return cand;
488
+ if ( cs->data_type != root->data_type ) return cand;
474
489
  if ( composed_base != 0 ) return cand;
475
490
 
476
491
  s = root->bytes;
@@ -610,7 +625,7 @@ ca_stride_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
610
625
  ca_size_t composed_base;
611
626
  ca_size_t rbytes;
612
627
  ca_size_t *paddrs;
613
- ca_size_t i, base;
628
+ ca_size_t i;
614
629
  int8_t k;
615
630
  int all_aligned = 1;
616
631
  volatile VALUE holder;
@@ -619,18 +634,12 @@ ca_stride_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
619
634
  ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
620
635
  rbytes = root->bytes;
621
636
 
622
- /* Fast path: identity transform detection -- when the addr remap composed_base +
623
- Σ idx[k]*composed_strides[k] reduces to an identity mapping over the
624
- root's flat byte space (= simple reshape, same bytes, row-major
625
- composed_strides), forward addrs as-is to root without the ALLOCV +
626
- per-cell remap loop. Cascades through chain a[idx_2d] (= outer
627
- reshape -> CAGrid -> inner reshape -> entity): outer CARefer is a
628
- simple reshape so addrs pass through, then CAGrid Y.1.b fast path
629
- triggers on the recursive call, then inner CARefer simple reshape
630
- pass-through to entity. Detection is O(ndim) + O(n). */
631
- if ( n == ca->elements
632
- && ca->bytes == rbytes && composed_base == 0
633
- && 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 ) {
634
643
  ca_size_t s = rbytes;
635
644
  int is_identity = 1;
636
645
  for ( k = ca->ndim - 1; k >= 0; k-- ) {
@@ -643,36 +652,51 @@ ca_stride_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
643
652
  }
644
653
  }
645
654
 
646
- paddrs = ALLOCV_N(ca_size_t, holder, n);
647
- for ( i = 0; i < n; i++ ) {
648
- ca_size_t idx[CA_RANK_MAX];
649
- ca_size_t off = composed_base;
650
- 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 ) {
651
657
  for ( k = 0; k < ca->ndim; k++ ) {
652
- off += idx[k] * composed_strides[k];
653
- }
654
- if ( ca->bytes == rbytes && off % rbytes == 0 ) {
655
- paddrs[i] = off / rbytes;
656
- }
657
- else {
658
- all_aligned = 0;
659
- break;
658
+ if ( composed_strides[k] % rbytes != 0 ) { all_aligned = 0; break; }
660
659
  }
661
660
  }
661
+ else {
662
+ all_aligned = 0;
663
+ }
662
664
 
663
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
+ }
664
688
  ca_xfer_addrs(root, n, paddrs, data, dir);
689
+ ALLOCV_END(holder);
690
+ return;
665
691
  }
666
- else {
667
- /* byte-mismatched reinterpret: deliver cell by cell via xfer_index
668
- (still no whole-view attach -- delegates one cell at a time). */
669
- for ( i = 0; i < n; i++ ) {
670
- ca_size_t idx[CA_RANK_MAX];
671
- ca_addr2index((CArray *) ca, addrs[i], idx);
672
- ca_stride_func_xfer_index(ca, idx, d + i * ca->bytes, dir);
673
- }
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);
674
699
  }
675
- ALLOCV_END(holder);
676
700
  }
677
701
 
678
702
  /* Optimised region delivery. Reached when ca->ptr == NULL (the central dispatcher handles the alias / attached case).
@@ -833,6 +857,7 @@ ca_stride_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
833
857
  if (!aligned || !root->ptr) {
834
858
  ca_size_t idx[CA_RANK_MAX], doff = 0;
835
859
  for (k = 0; k < ndim; k++) idx[k] = 0;
860
+ /* empty: the slot returns on an empty region at its entry */
836
861
  while (1) {
837
862
  ca_size_t off = base_addr, vmidx[CA_RANK_MAX];
838
863
  for (k = 0; k < ndim; k++) off += idx[k] * strides[k];
@@ -1085,8 +1110,9 @@ static int ca_layout_is_contiguous (int8_t ndim, const ca_size_t *dim,
1085
1110
  composes a source down to its root.
1086
1111
 
1087
1112
  Entities hold their own buffer. A CAStride-family root is one the fold
1088
- declined to walk through (a byte reinterpret, say); its own attach folds
1089
- 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
1090
1116
  boundary views that compute or fetch their contents -- CAObject, the lazy
1091
1117
  per-element transforms, whatever a companion gem installed -- and of those,
1092
1118
  only the ones with an xfer_stride slot can answer a region request at all.
@@ -1098,7 +1124,17 @@ ca_root_lends_no_memory (void *ap)
1098
1124
  CArray *root = (CArray *) ap;
1099
1125
 
1100
1126
  if ( ca_is_entity(root) ) return 0;
1101
- 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
+ }
1102
1138
  return ca_func[root->obj_type].xfer_stride != NULL;
1103
1139
  }
1104
1140
 
@@ -1164,14 +1200,14 @@ ca_stride_func_allocate (void *ap)
1164
1200
 
1165
1201
  ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
1166
1202
  if (ca_root_lends_no_memory(root)) {
1167
- ca->ptr = xmalloc(ca_length(ca));
1203
+ ca->ptr = ca_alloc_cells((CArray *) ca);
1168
1204
  return;
1169
1205
  }
1170
1206
  ca_attach(root);
1171
1207
  if (ca_layout_is_contiguous(ca->ndim, ca->dim, composed_strides, ca->bytes)) {
1172
1208
  ca->ptr = root->ptr + composed_base;
1173
1209
  } else {
1174
- ca->ptr = xmalloc(ca_length(ca));
1210
+ ca->ptr = ca_alloc_cells((CArray *) ca);
1175
1211
  }
1176
1212
  }
1177
1213
 
@@ -1200,14 +1236,9 @@ ca_stride_func_attach (void *ap)
1200
1236
  transfer slots. */
1201
1237
  char *buf = xmalloc(ca_length(ca));
1202
1238
  void *args[2];
1203
- int tag = 0;
1204
1239
  args[0] = ca;
1205
1240
  args[1] = buf;
1206
- rb_protect(ca_stride_cold_fill, (VALUE) args, &tag); /* root stays cold */
1207
- if (tag) {
1208
- xfree(buf);
1209
- rb_jump_tag(tag);
1210
- }
1241
+ ca_fill_or_xfree(buf, ca_stride_cold_fill, (VALUE) args); /* root stays cold */
1211
1242
  ca->ptr = buf;
1212
1243
  return;
1213
1244
  }
@@ -1215,7 +1246,7 @@ ca_stride_func_attach (void *ap)
1215
1246
  if (ca_layout_is_contiguous(ca->ndim, ca->dim, composed_strides, ca->bytes)) {
1216
1247
  ca->ptr = root->ptr + composed_base;
1217
1248
  } else {
1218
- ca->ptr = xmalloc(ca_length(ca));
1249
+ ca->ptr = ca_alloc_cells((CArray *) ca);
1219
1250
  ca_stride_xfer_with_layout(ca, 0, root->ptr + composed_base, composed_strides);
1220
1251
  }
1221
1252
  }
@@ -1342,6 +1373,8 @@ ca_stride_func_xfer_all (void *ap, void *data, int dir)
1342
1373
  char *ptr0 = ca->ptr;
1343
1374
  char *ptr = (char *) data;
1344
1375
 
1376
+ if ( ca->elements == 0 ) return;
1377
+
1345
1378
  ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
1346
1379
 
1347
1380
  /* Partial materialise: when the fold stops at a cold boundary view that knows how to deliver a region (has an
@@ -1458,6 +1491,7 @@ ca_stride_func_xfer_all (void *ap, void *data, int dir)
1458
1491
  ca_size_t idx[CA_RANK_MAX], n = 0;
1459
1492
  int8_t k;
1460
1493
  for (k = 0; k < ca->ndim; k++) idx[k] = 0;
1494
+ /* empty: the slot returns on an empty view at its entry */
1461
1495
  while (1) {
1462
1496
  ca_size_t off = composed_base;
1463
1497
  for (k = 0; k < ca->ndim; k++) off += idx[k] * composed_strides[k];
@@ -1471,6 +1505,52 @@ ca_stride_func_xfer_all (void *ap, void *data, int dir)
1471
1505
  return;
1472
1506
  }
1473
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
+
1474
1554
  /* Cold root without an xfer_stride slot, or an ndim mismatch the bridges
1475
1555
  above could not express: materialise the root into scratch via
1476
1556
  ca_xfer_all and run the direct strided gather/scatter against that.
@@ -2078,9 +2158,10 @@ rb_ca_as_strided (int argc, VALUE *argv, VALUE self)
2078
2158
  "shape (%ld) and strides (%ld) length mismatch",
2079
2159
  len, RARRAY_LEN(rstrides));
2080
2160
  }
2081
- if (len <= 0 || len > CA_RANK_MAX) {
2161
+ if (len <= 0) {
2082
2162
  rb_raise(rb_eArgError, "invalid ndim %ld", len);
2083
2163
  }
2164
+ ca_check_rank_fits(len, NULL);
2084
2165
  ndim = (int8_t) len;
2085
2166
  for (i = 0; i < len; i++) {
2086
2167
  shape[i] = NUM2SIZE(RARRAY_AREF(rshape, i));
@@ -2118,11 +2199,7 @@ rb_ca_sliding_windows (int argc, VALUE *argv, VALUE self)
2118
2199
  TypedData_Get_Struct(self, CArray, &carray_data_type, parent);
2119
2200
 
2120
2201
  ndim = parent->ndim;
2121
- if (2 * (int) ndim > CA_RANK_MAX) {
2122
- rb_raise(rb_eArgError,
2123
- "sliding_windows: result rank %d exceeds CA_RANK_MAX (%d)",
2124
- 2 * (int) ndim, CA_RANK_MAX);
2125
- }
2202
+ ca_check_rank_fits(2 * (long) ndim, "sliding_windows");
2126
2203
 
2127
2204
  rb_scan_args(argc, argv, "*:", (VALUE *) &rposary, (VALUE *) &ropt);
2128
2205
  if (! NIL_P(ropt)) {
@@ -2266,11 +2343,7 @@ rb_ca_unfold (int argc, VALUE *argv, VALUE self)
2266
2343
 
2267
2344
  ntrail = ndim - nspatial;
2268
2345
  outrank = (int) ndim + (int) nspatial;
2269
- if (outrank > CA_RANK_MAX) {
2270
- rb_raise(rb_eArgError,
2271
- "unfold: result rank %d exceeds CA_RANK_MAX (%d)",
2272
- outrank, CA_RANK_MAX);
2273
- }
2346
+ ca_check_rank_fits(outrank, "unfold");
2274
2347
 
2275
2348
  if (NIL_P(rstep)) {
2276
2349
  for (i = 0; i < nspatial; i++) step[i] = 1;
@@ -2354,11 +2427,7 @@ rb_ca_block_view (int argc, VALUE *argv, VALUE self)
2354
2427
  TypedData_Get_Struct(self, CArray, &carray_data_type, parent);
2355
2428
 
2356
2429
  ndim = parent->ndim;
2357
- if (2 * (int) ndim > CA_RANK_MAX) {
2358
- rb_raise(rb_eArgError,
2359
- "block_view: result rank %d exceeds CA_RANK_MAX (%d)",
2360
- 2 * (int) ndim, CA_RANK_MAX);
2361
- }
2430
+ ca_check_rank_fits(2 * (long) ndim, "block_view");
2362
2431
 
2363
2432
  rb_scan_args(argc, argv, "*:", (VALUE *) &rposary, (VALUE *) &ropt);
2364
2433
  rb_reject_options(ropt);
@@ -2461,7 +2530,7 @@ rb_ca_dim_view (int argc, VALUE *argv, VALUE self)
2461
2530
  (int) n_iter, (int) ndim);
2462
2531
  }
2463
2532
  for (i = 0; i < n_iter; i++) {
2464
- iter_axes[i] = ca_axis_integer(RARRAY_AREF(aary, i), NULL);
2533
+ iter_axes[i] = ca_normalize_axis_arg(RARRAY_AREF(aary, i), ndim, NULL);
2465
2534
  }
2466
2535
  }
2467
2536
  else if (nargs >= 1) {
@@ -2472,7 +2541,7 @@ rb_ca_dim_view (int argc, VALUE *argv, VALUE self)
2472
2541
  }
2473
2542
  n_iter = (int8_t) nargs;
2474
2543
  for (i = 0; i < n_iter; i++) {
2475
- iter_axes[i] = ca_axis_integer(RARRAY_AREF(rposary, i), NULL);
2544
+ iter_axes[i] = ca_normalize_axis_arg(RARRAY_AREF(rposary, i), ndim, NULL);
2476
2545
  }
2477
2546
  }
2478
2547
  else {
@@ -2480,23 +2549,16 @@ rb_ca_dim_view (int argc, VALUE *argv, VALUE self)
2480
2549
  "dim_view: at least one iteration axis required");
2481
2550
  }
2482
2551
 
2483
- /* Normalize negative indices and validate range / distinctness. */
2552
+ /* The axes are normalized as they are read; check distinctness. */
2484
2553
  for (i = 0; i < ndim; i++) seen[i] = 0;
2485
2554
  for (i = 0; i < n_iter; i++) {
2486
2555
  ca_size_t a = iter_axes[i];
2487
- if (a < 0) a += ndim;
2488
- if (a < 0 || a >= ndim) {
2489
- rb_raise(rb_eArgError,
2490
- "dim_view: axis %lld out of range for ndim %d",
2491
- (long long) iter_axes[i], (int) ndim);
2492
- }
2493
2556
  if (seen[a]) {
2494
2557
  rb_raise(rb_eArgError,
2495
2558
  "dim_view: duplicate iteration axis %lld",
2496
2559
  (long long) a);
2497
2560
  }
2498
2561
  seen[a] = 1;
2499
- iter_axes[i] = a;
2500
2562
  }
2501
2563
 
2502
2564
  /* imap: iter axes first (in given order), then remaining axes
@@ -2568,12 +2630,8 @@ rb_ca_flip_axis (VALUE self, long axis)
2568
2630
  rb_check_carray_object(self);
2569
2631
  TypedData_Get_Struct(self, CArray, &carray_data_type, parent);
2570
2632
  int8_t ndim = parent->ndim;
2571
- long a = (axis < 0) ? (axis + ndim) : axis;
2572
- if (a < 0 || a >= ndim) {
2573
- rb_raise(rb_eArgError,
2574
- "flip_axis: axis %ld out of range for ndim %d",
2575
- axis, (int) ndim);
2576
- }
2633
+ long a = rb_ca_normalize_axis_for_ndim(axis, ndim, /* axis-check: a long from a C caller */
2634
+ "flip_axis");
2577
2635
  int8_t flip[CA_RANK_MAX] = {0};
2578
2636
  flip[a] = 1;
2579
2637
  return rb_ca_flip_build_view(self, parent, flip);
@@ -2615,7 +2673,7 @@ rb_ca_flip (int argc, VALUE *argv, VALUE self)
2615
2673
  (int) n_axes, (int) ndim);
2616
2674
  }
2617
2675
  for (i = 0; i < n_axes; i++) {
2618
- axes[i] = ca_axis_integer(RARRAY_AREF(aary, i), NULL);
2676
+ axes[i] = ca_normalize_axis_arg(RARRAY_AREF(aary, i), ndim, NULL);
2619
2677
  }
2620
2678
  }
2621
2679
  else {
@@ -2626,18 +2684,12 @@ rb_ca_flip (int argc, VALUE *argv, VALUE self)
2626
2684
  }
2627
2685
  n_axes = (int8_t) nargs;
2628
2686
  for (i = 0; i < n_axes; i++) {
2629
- axes[i] = ca_axis_integer(RARRAY_AREF(rposary, i), NULL);
2687
+ axes[i] = ca_normalize_axis_arg(RARRAY_AREF(rposary, i), ndim, NULL);
2630
2688
  }
2631
2689
  }
2632
2690
 
2633
2691
  for (i = 0; i < n_axes; i++) {
2634
2692
  ca_size_t a = axes[i];
2635
- if (a < 0) a += ndim;
2636
- if (a < 0 || a >= ndim) {
2637
- rb_raise(rb_eArgError,
2638
- "flip: axis %lld out of range for ndim %d",
2639
- (long long) axes[i], (int) ndim);
2640
- }
2641
2693
  if (flip[a]) {
2642
2694
  rb_raise(rb_eArgError,
2643
2695
  "flip: duplicate axis %lld",
@@ -2711,18 +2763,8 @@ rb_ca_diagonal (int argc, VALUE *argv, VALUE self)
2711
2763
  rb_raise(rb_eArgError,
2712
2764
  "diagonal: axis: must be an Array of 2 integers");
2713
2765
  }
2714
- ai = ca_axis_integer(RARRAY_AREF(raxis, 0), NULL);
2715
- aj = ca_axis_integer(RARRAY_AREF(raxis, 1), NULL);
2716
- if (ai < 0) ai += ndim;
2717
- if (aj < 0) aj += ndim;
2718
- if (ai < 0 || ai >= ndim) {
2719
- rb_raise(rb_eArgError, "diagonal: axis[0] out of range for ndim %d",
2720
- (int) ndim);
2721
- }
2722
- if (aj < 0 || aj >= ndim) {
2723
- rb_raise(rb_eArgError, "diagonal: axis[1] out of range for ndim %d",
2724
- (int) ndim);
2725
- }
2766
+ ai = ca_normalize_axis_arg(RARRAY_AREF(raxis, 0), ndim, NULL);
2767
+ aj = ca_normalize_axis_arg(RARRAY_AREF(raxis, 1), ndim, NULL);
2726
2768
  if (ai == aj) {
2727
2769
  rb_raise(rb_eArgError, "diagonal: axis[0] and axis[1] must be distinct");
2728
2770
  }
@@ -2750,7 +2792,9 @@ rb_ca_diagonal (int argc, VALUE *argv, VALUE self)
2750
2792
  base_offset = offset * parent_byte_stride[aj];
2751
2793
  }
2752
2794
  else {
2753
- 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;
2754
2798
  if (neg >= parent->dim[ai]) {
2755
2799
  diag_len = 0;
2756
2800
  }
@@ -2761,6 +2805,9 @@ rb_ca_diagonal (int argc, VALUE *argv, VALUE self)
2761
2805
  }
2762
2806
  base_offset = neg * parent_byte_stride[ai];
2763
2807
  }
2808
+ if (diag_len == 0) {
2809
+ base_offset = 0; /* an offset past the edge can overflow the product */
2810
+ }
2764
2811
 
2765
2812
  /* build output: kept axes (in original order), then diagonal axis */
2766
2813
  out_k = 0;
data/ext/ca_obj_string.c CHANGED
@@ -76,7 +76,7 @@ const rb_data_type_t castring_data_type = {
76
76
  .flags = RUBY_TYPED_FREE_IMMEDIATELY
77
77
  };
78
78
 
79
- static int8_t CA_OBJ_STRING;
79
+ int8_t CA_OBJ_STRING; /* non-static: read by the store path in carray_access.c */
80
80
  static VALUE rb_cCAString;
81
81
 
82
82
  /* ------------------------------------------------------------------- */
@@ -119,9 +119,6 @@ ca_string_setup (CAString *ca, CArray *parent)
119
119
  ca->attach = 0;
120
120
  ca->nosync = 0;
121
121
 
122
- if ( ca_has_mask(parent) ) {
123
- ca_create_mask((CArray *) ca);
124
- }
125
122
 
126
123
  if ( ca_is_scalar(parent) ) {
127
124
  ca_set_flag(ca, CA_FLAG_SCALAR);
@@ -141,6 +138,7 @@ ca_string_new (CArray *parent)
141
138
  ca_string_setup(NULL, parent);
142
139
  ca = (CAString *) ca_array_alloc(CA_OBJ_STRING, parent->ndim);
143
140
  ca_string_setup(ca, parent);
141
+ ca_fill_or_free((CArray *) ca, ca_view_inherit_mask, (VALUE) ca);
144
142
  return ca;
145
143
  }
146
144
 
@@ -200,6 +198,69 @@ ca_operation_function_t ca_string_func = {
200
198
 
201
199
  /* ------------------------------------------------------------------- */
202
200
 
201
+ /* A CAString cell is a String; UNDEF (a masked cell) is no value. Refuse
202
+ anything else, naming what it is, so a cell of a CAString always answers
203
+ the String operations. */
204
+ NORETURN(static void ca_string_refuse_cell (VALUE v, const char *where));
205
+
206
+ static void
207
+ ca_string_refuse_cell (VALUE v, const char *where)
208
+ {
209
+ if ( NIL_P(v) ) {
210
+ rb_raise(rb_eTypeError,
211
+ "%s is nil, not a String (a CAString holds Strings; "
212
+ "UNDEF masks a cell)", where);
213
+ }
214
+ rb_raise(rb_eTypeError,
215
+ "%s is of class %s, not a String (a CAString holds Strings; "
216
+ "UNDEF masks a cell)", where, rb_obj_classname(v));
217
+ }
218
+
219
+ /* Every cell of `parent` that is not masked is a String. */
220
+ static void
221
+ ca_string_check_cells (VALUE parent_val, CArray *parent)
222
+ {
223
+ ca_size_t i;
224
+ char where[64];
225
+ if ( ca_is_entity(parent) && parent->ptr ) {
226
+ VALUE *p = (VALUE *) parent->ptr;
227
+ boolean8_t *m = ( parent->mask && parent->mask->ptr )
228
+ ? (boolean8_t *) parent->mask->ptr : NULL;
229
+ for (i = 0; i < parent->elements; i++) {
230
+ if ( m && m[i] ) continue;
231
+ if ( ! RB_TYPE_P(p[i], T_STRING) ) {
232
+ snprintf(where, sizeof(where), "cell %" PRId64, (int64_t) i);
233
+ ca_string_refuse_cell(p[i], where);
234
+ }
235
+ }
236
+ }
237
+ else {
238
+ volatile VALUE list =
239
+ rb_funcall(rb_funcall(parent_val, rb_intern("flatten"), 0),
240
+ rb_intern("to_a"), 0);
241
+ long n = RARRAY_LEN(list), j;
242
+ for (j = 0; j < n; j++) {
243
+ VALUE v = rb_ary_entry(list, j);
244
+ if ( v == CA_UNDEF ) continue;
245
+ if ( ! RB_TYPE_P(v, T_STRING) ) {
246
+ snprintf(where, sizeof(where), "cell %ld", j);
247
+ ca_string_refuse_cell(v, where);
248
+ }
249
+ }
250
+ }
251
+ }
252
+
253
+ /* The write hook: a value stored into a CAString is a String. */
254
+ static VALUE
255
+ ca_string_element_to_storage (VALUE self, VALUE v)
256
+ {
257
+ (void) self;
258
+ if ( ! RB_TYPE_P(v, T_STRING) ) {
259
+ ca_string_refuse_cell(v, "a value stored into a CAString");
260
+ }
261
+ return v;
262
+ }
263
+
203
264
  /* CAString.wrap(object_array) — zero-copy Face wrap of a CA_OBJECT array */
204
265
  VALUE
205
266
  rb_ca_string_wrap (VALUE parent_val)
@@ -211,6 +272,8 @@ rb_ca_string_wrap (VALUE parent_val)
211
272
  rb_check_carray_object(parent_val);
212
273
  TypedData_Get_Struct(parent_val, CArray, &carray_data_type, parent);
213
274
 
275
+ ca_string_setup(NULL, parent);
276
+ ca_string_check_cells(parent_val, parent);
214
277
  ca = ca_string_new(parent);
215
278
  obj = ca_wrap_struct(ca);
216
279
  rb_ca_set_parent(obj, parent_val);
@@ -242,6 +305,7 @@ rb_ca_string_initialize_copy (VALUE self, VALUE other)
242
305
  ca_array_pool_alloc(ca, CA_OBJ_STRING, cs->parent->ndim);
243
306
  }
244
307
  ca_string_setup(ca, cs->parent);
308
+ ca_view_inherit_mask((VALUE) ca);
245
309
  return self;
246
310
  }
247
311
 
@@ -266,6 +330,9 @@ Init_ca_obj_string (void)
266
330
  rb_ca_string_initialize_copy, 1);
267
331
  rb_define_singleton_method(rb_cCAString, "wrap", rb_ca_string_wrap_method, 1);
268
332
 
333
+ ca_face_register_element_to_storage(CA_OBJ_STRING,
334
+ ca_string_element_to_storage);
335
+
269
336
  /* A cell is the String itself, so the Face is the same over any parent:
270
337
  CAStack / CAMeld / concatenate can carry it (unlike CAConstString, whose
271
338
  cells point into one parent's buffer). */