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/carray_access.c CHANGED
@@ -36,10 +36,7 @@ rb_ca_store_index (VALUE self, ca_size_t *idx, VALUE rval)
36
36
  }
37
37
 
38
38
  if ( rval == CA_UNDEF ) { /* set mask of the element at the index 'idx' */
39
- ca_update_mask(ca);
40
- if ( ! ca->mask ) {
41
- ca_create_mask(ca);
42
- }
39
+ ca_create_mask(ca);
43
40
  ca_store_index(ca->mask, idx, &one);
44
41
  }
45
42
  else { /* unset mask and set value of the element at the index 'idx' */
@@ -51,16 +48,12 @@ rb_ca_store_index (VALUE self, ca_size_t *idx, VALUE rval)
51
48
  }
52
49
 
53
50
  /* store value */
54
- if ( ca->bytes <= 64) {
55
- char v[64];
56
- rb_ca_obj2ptr(self, rval, v);
57
- ca_store_index(ca, idx, v);
58
- }
59
- else {
60
- char *v = xmalloc(ca->bytes);
51
+ {
52
+ volatile VALUE holder;
53
+ char *v = ALLOCV_N(char, holder, ca->bytes);
61
54
  rb_ca_obj2ptr(self, rval, v);
62
55
  ca_store_index(ca, idx, v);
63
- xfree(v);
56
+ ALLOCV_END(holder);
64
57
  }
65
58
  }
66
59
 
@@ -79,16 +72,12 @@ rb_ca_fetch_index (VALUE self, ca_size_t *idx)
79
72
  }
80
73
 
81
74
  /* fetch value from the element */
82
- if ( ca->bytes <= 64) {
83
- char v[64];
84
- ca_fetch_index(ca, idx, v);
85
- out = rb_ca_ptr2obj(self, v);
86
- }
87
- else {
88
- char *v = xmalloc(ca->bytes);
75
+ {
76
+ volatile VALUE holder;
77
+ char *v = ALLOCV_N(char, holder, ca->bytes);
89
78
  ca_fetch_index(ca, idx, v);
90
- out = rb_ca_ptr2obj(self, v);
91
- xfree(v);
79
+ out = rb_ca_ptr2obj_storage(self, v);
80
+ ALLOCV_END(holder);
92
81
  }
93
82
 
94
83
  /* check if the element is masked */
@@ -101,11 +90,11 @@ rb_ca_fetch_index (VALUE self, ca_size_t *idx)
101
90
  }
102
91
  }
103
92
 
104
- /* Face scalar decode (invoked if the subclass defines storage_to_scalar(raw)) */
105
- CA_FACE_STORAGE_TO_SCALAR_IF_FACE(out, self, ca);
93
+ /* Face scalar decode (invoked if the subclass defines storage_to_element(raw)) */
94
+ CA_FACE_STORAGE_TO_ELEMENT_IF_FACE(out, self, ca);
106
95
 
107
96
  /* Boolean scalar access yields true/false, not Integer 0/1. Bulk paths
108
- (to_a / cast / serialize via rb_ca_ptr2obj) keep 0/1. Gate on the raw
97
+ (cast / serialize) keep 0/1. Gate on the raw
109
98
  INT2FIX(0/1) so a boolean-storage Face that decoded to its own object is
110
99
  left untouched; note INT2FIX(0) is truthy in Ruby, so map by value, not
111
100
  by RTEST. */
@@ -131,10 +120,7 @@ rb_ca_store_addr (VALUE self, ca_size_t addr, VALUE rval)
131
120
  }
132
121
 
133
122
  if ( rval == CA_UNDEF ) { /* set mask at the element */
134
- ca_update_mask(ca);
135
- if ( ! ca->mask ) {
136
- ca_create_mask(ca);
137
- }
123
+ ca_create_mask(ca);
138
124
  ca_store_addr(ca->mask, addr, &one);
139
125
  }
140
126
  else { /* set value at the element */
@@ -144,16 +130,12 @@ rb_ca_store_addr (VALUE self, ca_size_t addr, VALUE rval)
144
130
  }
145
131
 
146
132
  /* store value */
147
- if ( ca->bytes <= 64) {
148
- char v[64];
149
- rb_ca_obj2ptr(self, rval, v);
150
- ca_store_addr(ca, addr, v);
151
- }
152
- else {
153
- char *v = xmalloc(ca->bytes);
133
+ {
134
+ volatile VALUE holder;
135
+ char *v = ALLOCV_N(char, holder, ca->bytes);
154
136
  rb_ca_obj2ptr(self, rval, v);
155
137
  ca_store_addr(ca, addr, v);
156
- xfree(v);
138
+ ALLOCV_END(holder);
157
139
  }
158
140
  }
159
141
 
@@ -172,16 +154,12 @@ rb_ca_fetch_addr (VALUE self, ca_size_t addr)
172
154
  }
173
155
 
174
156
  /* fetch value from the element */
175
- if ( ca->bytes <= 64) {
176
- char v[64];
177
- ca_fetch_addr(ca, addr, v);
178
- out = rb_ca_ptr2obj(self, v);
179
- }
180
- else {
181
- char *v = xmalloc(ca->bytes);
157
+ {
158
+ volatile VALUE holder;
159
+ char *v = ALLOCV_N(char, holder, ca->bytes);
182
160
  ca_fetch_addr(ca, addr, v);
183
- out = rb_ca_ptr2obj(self, v);
184
- xfree(v);
161
+ out = rb_ca_ptr2obj_storage(self, v);
162
+ ALLOCV_END(holder);
185
163
  }
186
164
 
187
165
  /* check if the element is masked */
@@ -195,7 +173,7 @@ rb_ca_fetch_addr (VALUE self, ca_size_t addr)
195
173
  }
196
174
 
197
175
  /* Face scalar decode */
198
- CA_FACE_STORAGE_TO_SCALAR_IF_FACE(out, self, ca);
176
+ CA_FACE_STORAGE_TO_ELEMENT_IF_FACE(out, self, ca);
199
177
 
200
178
  /* Boolean scalar access yields true/false (see rb_ca_fetch_index). */
201
179
  if ( ca->data_type == CA_BOOLEAN &&
@@ -220,21 +198,19 @@ rb_ca_fill (VALUE self, VALUE rval)
220
198
 
221
199
  if ( rval == CA_UNDEF ) {
222
200
  boolean8_t one = 1;
223
- ca_update_mask(ca);
224
- if ( ! ca->mask ) {
225
- ca_create_mask(ca);
226
- }
201
+ ca_create_mask(ca);
227
202
  ca_fill(ca->mask, &one);
228
203
  }
229
204
  else {
230
- char *fval = xmalloc(ca->bytes);
205
+ volatile VALUE holder;
206
+ char *fval = ALLOCV_N(char, holder, ca->bytes);
231
207
  boolean8_t zero = 0;
232
208
  rb_ca_obj2ptr(self, rval, fval);
233
209
  if ( ca_has_mask(ca) ) {
234
210
  ca_fill(ca->mask, &zero);
235
211
  }
236
212
  ca_fill(ca, fval);
237
- xfree(fval);
213
+ ALLOCV_END(holder);
238
214
  }
239
215
 
240
216
  return self;
@@ -253,9 +229,7 @@ static void
253
229
  ary_guess_shape (VALUE ary, int level, int *max_level, ca_size_t *dim)
254
230
  {
255
231
  volatile VALUE ary0;
256
- if ( level > CA_RANK_MAX ) {
257
- rb_raise(rb_eRuntimeError, "too deep level array for conversion to carray");
258
- }
232
+ ca_check_rank_fits(level + 1, NULL);
259
233
 
260
234
  if ( TYPE(ary) == T_ARRAY ) {
261
235
  if ( RARRAY_LEN(ary) == 0 && level == 0 ) {
@@ -304,7 +278,7 @@ ary_flatten_upto_level (VALUE ary, int max_level, int level,
304
278
  int32_t i;
305
279
 
306
280
  if ( TYPE(ary) != T_ARRAY ) {
307
- rb_raise(rb_eRuntimeError, "invalid shape array for conversion to carray");
281
+ rb_raise(rb_eArgError, "invalid shape array for conversion to carray");
308
282
  }
309
283
 
310
284
  if ( level == max_level ) {
@@ -321,7 +295,7 @@ ary_flatten_upto_level (VALUE ary, int max_level, int level,
321
295
  }
322
296
  }
323
297
 
324
- static VALUE
298
+ VALUE
325
299
  rb_ary_flatten_for_elements (VALUE ary, ca_size_t elements, void *ap)
326
300
  {
327
301
  CArray *ca = (CArray *) ap;
@@ -348,7 +322,7 @@ rb_ary_flatten_for_elements (VALUE ary, ca_size_t elements, void *ap)
348
322
  }
349
323
 
350
324
  if ( total != ca->elements ) {
351
- rb_raise(rb_eRuntimeError, "invalid shape array for conversion to carray");
325
+ rb_raise(rb_eArgError, "invalid shape array for conversion to carray");
352
326
  }
353
327
  else {
354
328
  volatile VALUE out = rb_ary_new2(0);
@@ -500,6 +474,200 @@ ca_id_expression_evaluator (void)
500
474
  return id;
501
475
  }
502
476
 
477
+ /* The casting stores below convert into the destination inside a window,
478
+ and a conversion from Ruby objects can raise part way. Such a window
479
+ attaches rather than allocates: the cells converted before the raise
480
+ are then pushed back with the others left as they were -- what an
481
+ entity destination, which has no separate buffer, shows as well. A
482
+ cast that cannot raise keeps the cheaper allocate. */
483
+
484
+ typedef struct {
485
+ CArray *ca;
486
+ CArray *cv; /* CArray source, delivered into scratch */
487
+ char *scratch;
488
+ boolean8_t *mscratch; /* cv's mask, or NULL when it has none */
489
+ CArray *ico; /* Ruby Array source: an object descriptor */
490
+ volatile VALUE list;
491
+ int has_mask;
492
+ } ca_store_all_ctx_t;
493
+
494
+ static const char *
495
+ ca_store_all_mode (CArray *cv)
496
+ {
497
+ return ( cv->data_type == CA_OBJECT ) ? "w" : "a";
498
+ }
499
+
500
+ /* The window has attached ca's mask, and the one created here joins it,
501
+ so the mask is written in its buffer. */
502
+ static VALUE
503
+ ca_store_all_cast_body (VALUE arg)
504
+ {
505
+ ca_store_all_ctx_t *c = (ca_store_all_ctx_t *) arg;
506
+ CArray *ca = c->ca;
507
+ if ( c->mscratch ) {
508
+ if ( ! ca->mask ) {
509
+ ca_create_mask(ca);
510
+ }
511
+ memcpy(ca->mask->ptr, c->mscratch, ca->elements);
512
+ }
513
+ else if ( ca->mask ) {
514
+ memset(ca->mask->ptr, 0, ca->elements);
515
+ }
516
+ if ( ca->mask ) {
517
+ ca_cast_block_with_mask(ca->elements, c->cv, c->scratch, ca, ca->ptr,
518
+ (boolean8_t*)ca->mask->ptr);
519
+ }
520
+ else {
521
+ ca_cast_block(ca->elements, c->cv, c->scratch, ca, ca->ptr);
522
+ }
523
+ return Qnil;
524
+ }
525
+
526
+ static VALUE
527
+ ca_store_all_list_body (VALUE arg)
528
+ {
529
+ ca_store_all_ctx_t *c = (ca_store_all_ctx_t *) arg;
530
+ CArray *ca = c->ca;
531
+ VALUE list = c->list;
532
+ ca_size_t i;
533
+ /* Face has surface != storage; the cast runs in the storage
534
+ data_type. Passing ca directly makes the cast table see FIXLEN
535
+ and report non-implemented, so pass a shadow CArray (storage
536
+ data_type, ptr aliased to ca). */
537
+ CArray shadow;
538
+ CArray *cast_target = ca;
539
+ memset(&shadow, 0, sizeof(CArray));
540
+ if ( ca_is_face(ca) ) {
541
+ CArray *root = ca_strip_face(ca);
542
+ if (root) {
543
+ shadow.data_type = root->data_type;
544
+ shadow.bytes = ca->bytes;
545
+ shadow.elements = ca->elements;
546
+ shadow.ptr = ca->ptr;
547
+ cast_target = &shadow;
548
+ }
549
+ }
550
+ if ( c->has_mask ) {
551
+ boolean8_t *m;
552
+ m = (boolean8_t *)ca->mask->ptr;
553
+ for (i=0; i<ca->elements; i++) {
554
+ if ( rb_ary_entry(list,i) == CA_UNDEF ) {
555
+ *m = 1;
556
+ }
557
+ else {
558
+ *m = 0;
559
+ }
560
+ m++;
561
+ }
562
+ ca_cast_block_with_mask(ca->elements, c->ico, (VALUE *)RARRAY_CONST_PTR(list),
563
+ cast_target, ca->ptr,
564
+ (boolean8_t*)ca->mask->ptr);
565
+ }
566
+ else {
567
+ /* Every cell gets a value, so none stays masked. */
568
+ if ( ca->mask ) {
569
+ memset(ca->mask->ptr, 0, ca->elements);
570
+ }
571
+ ca_cast_block(ca->elements, c->ico, (VALUE *)RARRAY_CONST_PTR(list), cast_target, ca->ptr);
572
+ }
573
+ return Qnil;
574
+ }
575
+
576
+ /* A Face source stored into an array that is not the same Face. Its cells
577
+ are surface values, and its storage means nothing to the destination: a
578
+ CATime's ticks are not times to an object array, and a categorical's codes
579
+ are not its labels. So an object destination, or a Face that does not
580
+ reconcile a Face source itself (a fixlen string taking a const string),
581
+ receives the values (to_type(:object)), and any other plain destination
582
+ refuses rather than receive storage bytes. A destination of the same Face
583
+ class, or one that reconciles a Face (to_comparable), is left to
584
+ ca_face_convert_store_rval. */
585
+ static VALUE
586
+ ca_face_source_for_store (VALUE self, CArray *ca, VALUE rval)
587
+ {
588
+ CArray *cr;
589
+
590
+ if ( ! rb_obj_is_carray(rval) ) {
591
+ return rval;
592
+ }
593
+ GetCArray(rval, cr);
594
+ if ( ! ca_is_face(cr) ) {
595
+ return rval;
596
+ }
597
+ if ( ca_is_face(ca) && ca->obj_type == CA_OBJ_RECORD ) {
598
+ /* A record is read at its data_class's member offsets, so records of
599
+ another data_class (of the same size or not) are refused; the class
600
+ of the record array (a subclass or CARecord itself) does not matter. */
601
+ if ( cr->obj_type == CA_OBJ_RECORD
602
+ && rb_ca_data_class(self) == rb_ca_data_class(rval) ) {
603
+ return rval;
604
+ }
605
+ if ( cr->obj_type == CA_OBJ_RECORD ) {
606
+ rb_raise(rb_eTypeError,
607
+ "can not store a record array of %"PRIsVALUE
608
+ " into a record array of %"PRIsVALUE,
609
+ rb_ca_data_class(rval), rb_ca_data_class(self));
610
+ }
611
+ rb_raise(rb_eTypeError,
612
+ "can not store a %s array into a record array of %"PRIsVALUE,
613
+ rb_obj_classname(rval), rb_ca_data_class(self));
614
+ }
615
+ if ( ca_is_face(ca) ) {
616
+ if ( rb_obj_class(self) == rb_obj_class(rval)
617
+ || rb_respond_to(self, rb_intern("to_comparable")) ) {
618
+ return rval;
619
+ }
620
+ }
621
+ else if ( ca->data_type != CA_OBJECT ) {
622
+ /* A fixlen string or a record is its bytes: storage and surface are
623
+ the same, so a plain array of that type takes them as they are. A
624
+ const string's bytes are ranges into a pool of its own, and it is the
625
+ read-only Face that says so. */
626
+ CArray *storage = ca_strip_face(cr);
627
+ if ( storage->data_type == cr->data_type && storage->bytes == cr->bytes
628
+ && ! ca_is_readonly(cr) ) {
629
+ return rval;
630
+ }
631
+ rb_raise(rb_eTypeError,
632
+ "can not store a %s array into %s array "
633
+ "(to_type(:object) gives its values)",
634
+ rb_obj_classname(rval), ca_type_name[ca->data_type]);
635
+ }
636
+ return rb_funcall(rval, rb_intern("to_type"), 1, INT2NUM(CA_OBJECT));
637
+ }
638
+
639
+ /* A store into no cells writes nothing, but the value is checked as it
640
+ would be for any cells: the same statement does not raise or pass
641
+ depending on how many cells the index picks. */
642
+ static void
643
+ ca_store_all_check_empty (VALUE self, VALUE rval)
644
+ {
645
+ CArray *ca;
646
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
647
+ if ( rb_obj_is_cscalar(rval) ) {
648
+ rval = rb_ca_fetch_addr(rval, 0);
649
+ }
650
+ rval = ca_face_source_for_store(self, ca, rval);
651
+ if ( ! rb_obj_is_carray(rval) && TYPE(rval) != T_ARRAY &&
652
+ rb_respond_to(rval, id_to_ca) ) {
653
+ rval = rb_funcall(rval, id_to_ca, 0);
654
+ }
655
+ if ( rb_obj_is_carray(rval) ) {
656
+ ca_broadcast_to_destination(self, (volatile VALUE *) &rval);
657
+ }
658
+ else if ( TYPE(rval) == T_ARRAY ) {
659
+ if ( NIL_P(rb_ary_flatten_for_elements(rval, ca->elements, ca)) ) {
660
+ rb_raise(rb_eArgError, "failed to guess data size of given array");
661
+ }
662
+ }
663
+ else if ( rval != CA_UNDEF ) {
664
+ volatile VALUE holder;
665
+ char *fval = ALLOCV_N(char, holder, ca->bytes);
666
+ rb_ca_obj2ptr(self, rval, fval);
667
+ ALLOCV_END(holder);
668
+ }
669
+ }
670
+
503
671
  VALUE
504
672
  rb_ca_store_all (VALUE self, VALUE rval)
505
673
  {
@@ -507,15 +675,20 @@ rb_ca_store_all (VALUE self, VALUE rval)
507
675
 
508
676
  rb_ca_modify(self);
509
677
 
510
- if ( self == rval || rb_ca_is_empty(self) ) {
678
+ if ( self == rval ) {
511
679
  return rval;
512
680
  }
513
-
681
+ if ( rb_ca_is_empty(self) ) {
682
+ ca_store_all_check_empty(self, rval);
683
+ return rval;
684
+ }
685
+
514
686
  if ( rb_obj_is_cscalar(rval) ) {
515
687
  rval = rb_ca_fetch_addr(rval, 0);
516
688
  }
517
689
 
518
690
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
691
+ rval = ca_face_source_for_store(self, ca, rval);
519
692
 
520
693
  retry:
521
694
 
@@ -534,6 +707,7 @@ rb_ca_store_all (VALUE self, VALUE rval)
534
707
  && ca->data_type == cv->data_type
535
708
  && ca->ndim == cv->ndim
536
709
  && ca->elements == cv->elements
710
+ && ca_in_main_ractor()
537
711
  && RTEST(rb_attr_get(rb_cCArray, ca_id_expression_evaluator())) ) {
538
712
  int8_t k;
539
713
  int same = 1;
@@ -552,6 +726,17 @@ rb_ca_store_all (VALUE self, VALUE rval)
552
726
  ca_broadcast_to_destination(self, &rval);
553
727
  TypedData_Get_Struct(rval, CArray, &carray_data_type, cv);
554
728
 
729
+ /* Fixlen cells of another width are fitted cell by cell, padded with
730
+ NUL or cut to the destination's width, as a String or an Array of
731
+ Strings stored into fixlen cells is. Copied as they are, the bytes
732
+ would be read past the end of a narrower source. */
733
+ if ( ca->data_type == CA_FIXLEN && cv->data_type == CA_FIXLEN
734
+ && ca->bytes != cv->bytes ) {
735
+ rval = rb_ca_to_type(rb_ca_strip_face_value(rval), INT2NUM(CA_FIXLEN),
736
+ SIZE2NUM(ca->bytes));
737
+ TypedData_Get_Struct(rval, CArray, &carray_data_type, cv);
738
+ }
739
+
555
740
  /* Source delivery via ca_xfer_all into a local scratch instead of
556
741
  ca_attach(cv). This routes through ca_*_func_xfer_all's per-region
557
742
  partial-materialise path, avoiding a ca_attach(root) silent
@@ -564,29 +749,50 @@ rb_ca_store_all (VALUE self, VALUE rval)
564
749
  scales with the view, not the root. (The dst-side ca_sync_data is
565
750
  already an xfer_all PUT.) */
566
751
  {
567
- volatile VALUE scratch_holder;
752
+ volatile VALUE scratch_holder, mscratch_holder = 0;
568
753
  ca_size_t cv_bytes = cv->bytes * cv->elements;
569
754
  char *scratch = ALLOCV_N(char, scratch_holder, cv_bytes);
755
+ boolean8_t *mscratch = NULL;
570
756
  ca_xfer_all(cv, scratch, CA_XFER_GET);
571
757
 
758
+ /* The source's mask is taken now as well, before anything is
759
+ written: the source may read through to the destination's mask
760
+ (`c[] = c.flip(0)`, or a lazy expression over c whose mask is
761
+ built from c's on demand), and the writes below clear it. */
762
+ if ( ca_has_mask(cv) ) {
763
+ mscratch = ALLOCV_N(boolean8_t, mscratch_holder, cv->elements);
764
+ ca_xfer_all(cv->mask, mscratch, CA_XFER_GET);
765
+ }
766
+
572
767
  if ( ca->data_type != cv->data_type ) {
573
- ca_allocate(ca);
574
- ca_copy_mask_overwrite(ca, ca->elements, 1, cv);
575
- if ( ca->mask ) {
576
- ca_cast_block_with_mask(ca->elements, cv, scratch, ca, ca->ptr,
577
- (boolean8_t*)ca->mask->ptr);
768
+ ca_store_all_ctx_t c;
769
+ c.ca = ca;
770
+ c.cv = cv;
771
+ c.scratch = scratch;
772
+ c.mscratch = mscratch;
773
+ ca_attach_window(1, &ca, ca_store_all_mode(cv), ca_store_all_cast_body,
774
+ (VALUE) &c);
775
+ }
776
+ else {
777
+ if ( mscratch ) {
778
+ if ( ! ca->mask ) {
779
+ ca_create_mask(ca);
780
+ }
781
+ ca_sync_data(ca->mask, (char *) mscratch);
578
782
  }
579
783
  else {
580
- ca_cast_block(ca->elements, cv, scratch, ca, ca->ptr);
784
+ ca_update_mask(ca);
785
+ if ( ca->mask ) {
786
+ boolean8_t zero = 0;
787
+ ca_fill(ca->mask, &zero);
788
+ }
581
789
  }
582
- ca_sync(ca);
583
- ca_detach(ca);
584
- }
585
- else {
586
- ca_copy_mask_overwrite(ca, ca->elements, 1, cv);
587
790
  ca_sync_data(ca, scratch);
588
791
  }
589
792
 
793
+ if ( mscratch ) {
794
+ ALLOCV_END(mscratch_holder);
795
+ }
590
796
  ALLOCV_END(scratch_holder);
591
797
  }
592
798
  }
@@ -595,7 +801,7 @@ rb_ca_store_all (VALUE self, VALUE rval)
595
801
  rb_ary_flatten_for_elements(rval, ca->elements, ca);
596
802
  ca_size_t i;
597
803
  if ( NIL_P(list) ) {
598
- rb_raise(rb_eRuntimeError,
804
+ rb_raise(rb_eArgError,
599
805
  "failed to guess data size of given array");
600
806
  }
601
807
  else {
@@ -612,54 +818,19 @@ rb_ca_store_all (VALUE self, VALUE rval)
612
818
  break;
613
819
  }
614
820
  }
615
- /* Use ca_allocate() instead of ca_attach() here because rb_ca_store_all()
616
- overwrites ALL elements of the array from a Ruby Array. There is no need
617
- to copy parent data (which ca_attach would do), since every element will
618
- be replaced. Mask handling is also safe: ca_create_mask() above and
619
- ca_copy_mask_overwrite() handle mask allocation and propagation internally,
620
- so ca_allocate()'s simpler path (no parent data copy) is sufficient. */
621
- ca_allocate(ca);
821
+ /* Every element is replaced, so there would be no need to gather the
822
+ old values -- but converting a Ruby object can raise part way, and
823
+ then the cells already converted are pushed back with the rest
824
+ left as they were, which needs the old values in the buffer. So
825
+ this window attaches (see ca_store_all_mode). */
622
826
  {
623
- /* Face has surface != storage; the cast runs in the storage
624
- data_type. Passing ca directly makes the cast table see FIXLEN
625
- and report non-implemented, so pass a shadow CArray (storage
626
- data_type, ptr aliased to ca). */
627
- CArray shadow;
628
- CArray *cast_target = ca;
629
- memset(&shadow, 0, sizeof(CArray));
630
- if ( ca_is_face(ca) ) {
631
- CArray *root = ca;
632
- while (root && ca_is_face(root)) root = ((CAView *) root)->parent;
633
- if (root) {
634
- shadow.data_type = root->data_type;
635
- shadow.bytes = ca->bytes;
636
- shadow.elements = ca->elements;
637
- shadow.ptr = ca->ptr;
638
- cast_target = &shadow;
639
- }
640
- }
641
- if ( has_mask ) {
642
- boolean8_t *m;
643
- m = (boolean8_t *)ca->mask->ptr;
644
- for (i=0; i<ca->elements; i++) {
645
- if ( rb_ary_entry(list,i) == CA_UNDEF ) {
646
- *m = 1;
647
- }
648
- else {
649
- *m = 0;
650
- }
651
- m++;
652
- }
653
- ca_cast_block_with_mask(ca->elements, &ico, (VALUE *)RARRAY_CONST_PTR(list),
654
- cast_target, ca->ptr,
655
- (boolean8_t*)ca->mask->ptr);
656
- }
657
- else {
658
- ca_cast_block(ca->elements, &ico, (VALUE *)RARRAY_CONST_PTR(list), cast_target, ca->ptr);
659
- }
827
+ ca_store_all_ctx_t c;
828
+ c.ca = ca;
829
+ c.ico = &ico;
830
+ c.list = list;
831
+ c.has_mask = has_mask;
832
+ ca_attach_window(1, &ca, "w", ca_store_all_list_body, (VALUE) &c);
660
833
  }
661
- ca_sync(ca);
662
- ca_detach(ca);
663
834
  }
664
835
  }
665
836
  else if ( rb_respond_to(rval, id_to_ca) ) {
@@ -835,9 +1006,8 @@ rb_ca_ref_block (VALUE self, CAIndexInfo *info)
835
1006
  data_type to the storage data_type (chain bottom) so the write path
836
1007
  casts in the storage type. */
837
1008
  if ( ca_is_face(ca) ) {
838
- CArray *blk_ca, *root = ca;
1009
+ CArray *blk_ca, *root = ca_strip_face(ca);
839
1010
  TypedData_Get_Struct(blk, CArray, &carray_data_type, blk_ca);
840
- while (root && ca_is_face(root)) root = ((CAView *) root)->parent;
841
1011
  if (root && root->data_type != blk_ca->data_type) {
842
1012
  blk_ca->data_type = root->data_type;
843
1013
  }
@@ -871,7 +1041,9 @@ extern VALUE rb_ca_remap_new (VALUE cary, VALUE rmapper);
871
1041
  (b) Otherwise -> normalize chain (a.flatten[mapper.flatten]
872
1042
  .reshape). A 1-D mapper is already routed to CAGrid upstream.
873
1043
 
874
- Both routes raise on a masked mapper. */
1044
+ A masked mapper cell selects nothing: the cell keeps its position and
1045
+ reads as masked (a CAGrid hole); the same-shape fast path is left to a
1046
+ mapper with no masked cell. */
875
1047
  static VALUE
876
1048
  rb_ca_fancy_index_chain (VALUE self, VALUE rmapper)
877
1049
  {
@@ -883,18 +1055,33 @@ rb_ca_fancy_index_chain (VALUE self, VALUE rmapper)
883
1055
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
884
1056
  TypedData_Get_Struct(rmapper, CArray, &carray_data_type, cm);
885
1057
 
886
- if ( ca_is_any_masked(cm) ) {
887
- rb_raise(rb_eArgError, "mapper in ca[mapper] should not be masked");
888
- }
889
-
890
- /* (a) same-shape fast path: ndim + per-axis dim + CA_SIZE data_type. */
1058
+ /* (a) same-shape fast path: ndim + per-axis dim + CA_SIZE data_type.
1059
+ The view reads the index array it is given, so it is given a copy: the
1060
+ indices are the ones the caller passed at this call, as every other
1061
+ route reads them, and each is checked against self now, so an index out
1062
+ of range raises here and not at the first read. */
891
1063
  if ( cm->data_type == CA_SIZE
1064
+ && ! ca_is_any_masked(cm)
892
1065
  && cm->ndim == ca->ndim
893
1066
  && memcmp(cm->dim, ca->dim, ca->ndim * sizeof(ca_size_t)) == 0 ) {
894
- return rb_ca_remap_new(self, rmapper);
1067
+ volatile VALUE snapshot = rb_ca_copy(rmapper);
1068
+ CArray *cs;
1069
+ ca_size_t k, *p;
1070
+ TypedData_Get_Struct(snapshot, CArray, &carray_data_type, cs);
1071
+ p = (ca_size_t *) cs->ptr;
1072
+ for (k = 0; k < cs->elements; k++) {
1073
+ CA_CHECK_INDEX(p[k], ca->elements);
1074
+ }
1075
+ return rb_ca_remap_new(self, snapshot);
895
1076
  }
896
1077
 
897
- /* (b) Normalize chain fallback. */
1078
+ /* (b) Normalize chain fallback. A Face is flattened through its storage
1079
+ and put on top of the result, so no Face is left in the middle of the
1080
+ chain (CAFace.md 8.3). */
1081
+ if ( ca_is_face(ca) ) {
1082
+ result = rb_ca_fancy_index_chain(rb_ca_strip_face_value(self), rmapper);
1083
+ return ca_face_lift(result, self);
1084
+ }
898
1085
  flat_self = rb_ca_refer_new_flatten(self);
899
1086
  flat_mapper = (cm->ndim == 1) ? rmapper : rb_ca_flatten(rmapper);
900
1087
  grid_argv[0] = flat_mapper;
@@ -913,10 +1100,9 @@ rb_ca_fancy_index_chain (VALUE self, VALUE rmapper)
913
1100
  return result;
914
1101
  }
915
1102
 
916
- /* The CAMapping class is gone; rb_ca_mapping_new and rb_ca_mapping build
917
- the normalize chain via rb_ca_fancy_index_chain. The public C API
918
- signatures are preserved so external ext gems keep linking; the runtime
919
- class of the returned VALUE is CARefer (chain outermost), not CAMapping. */
1103
+ /* rb_ca_mapping_new and rb_ca_mapping index cary by a mapper array through
1104
+ rb_ca_fancy_index_chain: the result is a CAGrid over the flattened array,
1105
+ reshaped (CARefer, the outermost layer) to the mapper's shape. */
920
1106
 
921
1107
  VALUE
922
1108
  rb_ca_mapping_new (VALUE cary, CArray *mapper)
@@ -942,6 +1128,18 @@ rb_ca_mapping (int argc, VALUE *argv, VALUE self)
942
1128
  the optimal view class (CAStride for stride bases, CARefer for fancy
943
1129
  bases). */
944
1130
 
1131
+ /* a[:name, args] calls a method of the array, and its arguments are the
1132
+ method's, whatever they are: the scans for :_, :> and group indices
1133
+ below look at index keys only. */
1134
+ static int
1135
+ ca_argv_is_method_call (int argc, VALUE *argv)
1136
+ {
1137
+ const char *name;
1138
+ if ( argc < 1 || ! SYMBOL_P(argv[0]) ) return 0;
1139
+ name = rb_id2name(SYM2ID(argv[0]));
1140
+ return ( name != NULL && strlen(name) > 1 );
1141
+ }
1142
+
945
1143
  static int
946
1144
  ca_argv_has_newaxis (int argc, VALUE *argv)
947
1145
  {
@@ -983,6 +1181,10 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
983
1181
 
984
1182
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
985
1183
 
1184
+ if ( ca_argv_is_method_call(argc, argv) ) {
1185
+ goto classify;
1186
+ }
1187
+
986
1188
  /* newaxis (:_) interception (cheap pointer scan; zero overhead beyond
987
1189
  the loop when no :_ is present). */
988
1190
  if ( ca_argv_has_newaxis(argc, argv) ) {
@@ -1006,6 +1208,7 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
1006
1208
  return rb_ca_fetch_group(argc, argv, self);
1007
1209
  }
1008
1210
 
1211
+ classify:
1009
1212
  info.range_check = 1;
1010
1213
  rb_ca_scan_index(ca->ndim, ca->dim, ca->elements, argc, argv, &info);
1011
1214
 
@@ -1014,8 +1217,20 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
1014
1217
  /* Re-enter against a flattened view of self. rb_ca_refer_new_flatten
1015
1218
  is an internal builder and hands back a bare refer, so a marker
1016
1219
  receiver would be dropped here and the result would never be lifted
1017
- at the tail. Put it back on before the re-entry. (A Face survives
1018
- on its own -- the internal builder still lifts those.) */
1220
+ at the tail. Put it back on before the re-entry.
1221
+
1222
+ A Face is flattened through its storage and put on top once at the
1223
+ end; re-entering against the flattened Face would build the view on
1224
+ it and leave a Face in the middle of the chain (CAFace.md 8.3). */
1225
+ if ( ca_is_face(ca) ) {
1226
+ volatile VALUE flat =
1227
+ rb_ca_refer_new_flatten(rb_ca_strip_face_value(self));
1228
+ obj = rb_ca_fetch_method(argc, argv, flat);
1229
+ if ( rb_obj_is_kind_of(obj, rb_cCArray) ) {
1230
+ obj = ca_face_lift(obj, self);
1231
+ }
1232
+ return obj;
1233
+ }
1019
1234
  self = rb_ca_refer_new_flatten(self);
1020
1235
  if ( ca_is_lazy_marker(ca) ) {
1021
1236
  extern VALUE rb_ca_lazy_marker_new (VALUE cary);
@@ -1053,8 +1268,10 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
1053
1268
  obj = rb_ca_grid(argc, argv, self);
1054
1269
  break;
1055
1270
  case CA_REG_METHOD_CALL: {
1271
+ /* a[:eq, 0] is a[a.eq(0)]: a public method of the array, as a call
1272
+ written out would reach (a[:puts] does not run Kernel#puts). */
1056
1273
  volatile VALUE idx;
1057
- idx = rb_funcall2(self, SYM2ID(info.symbol), argc-1, argv+1);
1274
+ idx = rb_funcallv_public(self, SYM2ID(info.symbol), argc-1, argv+1);
1058
1275
  obj = rb_ca_fetch(self, idx);
1059
1276
  break;
1060
1277
  }
@@ -1073,11 +1290,6 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
1073
1290
  }
1074
1291
  break;
1075
1292
  }
1076
- case CA_REG_ATTRIBUTE: {
1077
- obj = rb_funcall(self, rb_intern("attribute"), 0);
1078
- obj = rb_hash_aref(obj, info.symbol);
1079
- break;
1080
- }
1081
1293
  default:
1082
1294
  rb_raise(rb_eIndexError, "invalid index specified");
1083
1295
  }
@@ -1121,7 +1333,7 @@ rb_ca_fetch_newaxis (int argc, VALUE *argv, VALUE self, CArray *ca)
1121
1333
  nunder++;
1122
1334
  continue;
1123
1335
  }
1124
- if ( FIXNUM_P(a) || RB_TYPE_P(a, T_BIGNUM) ) {
1336
+ if ( ca_index_key_is_scalar(a) ) {
1125
1337
  nscalar++;
1126
1338
  }
1127
1339
  if ( a == sym_gt || a == sym_perc
@@ -1130,7 +1342,7 @@ rb_ca_fetch_newaxis (int argc, VALUE *argv, VALUE self, CArray *ca)
1130
1342
  "newaxis (:_) cannot be combined with :>, :%%, "
1131
1343
  "or rubber dim (false / :~)");
1132
1344
  }
1133
- if ( nclean >= CA_RANK_MAX ) {
1345
+ if ( nclean >= CA_RANK_MAX ) { /* rank-check: a count of indices */
1134
1346
  rb_raise(rb_eIndexError, "too many indices");
1135
1347
  }
1136
1348
  clean[nclean++] = a;
@@ -1168,21 +1380,40 @@ rb_ca_fetch_newaxis (int argc, VALUE *argv, VALUE self, CArray *ca)
1168
1380
  TypedData_Get_Struct(base, CArray, &carray_data_type, cb);
1169
1381
 
1170
1382
  /* Reconstruct final shape: walk original argv. :_ -> size-1, scalar
1171
- (Integer) -> dropped (skip), else -> base.dim[cursor]. */
1172
- base_cursor = 0;
1173
- for (i = 0; i < argc; i++) {
1174
- VALUE a = argv[i];
1175
- if ( a == sym_under ) {
1176
- final_dims[nfinal++] = 1;
1177
- }
1178
- else if ( FIXNUM_P(a) || RB_TYPE_P(a, T_BIGNUM) ) {
1179
- /* scalar index -> axis dropped in base; contributes no output axis */
1383
+ (Integer) -> dropped (skip), else -> base.dim[cursor]. A key gives one
1384
+ axis, except an integer CArray read alone (a mapping), which gives as
1385
+ many as it has: the axes the base has beyond one per key are its. */
1386
+ {
1387
+ int nkeys = 0, extra, ncarray = 0;
1388
+ for (i = 0; i < nclean; i++) {
1389
+ if ( ! ca_index_key_is_scalar(clean[i]) ) {
1390
+ nkeys++;
1391
+ if ( rb_obj_is_carray(clean[i]) ) ncarray++;
1392
+ }
1180
1393
  }
1181
- else {
1182
- if ( base_cursor >= cb->ndim ) {
1183
- rb_raise(rb_eIndexError, "newaxis: base axis count mismatch");
1394
+ extra = cb->ndim - nkeys;
1395
+ base_cursor = ( extra != 0 && ncarray != 1 ) ? -1 : 0;
1396
+ for (i = 0; i < argc && base_cursor >= 0; i++) {
1397
+ VALUE a = argv[i];
1398
+ int take = 1, t;
1399
+ if ( a == sym_under ) {
1400
+ final_dims[nfinal++] = 1;
1401
+ continue;
1402
+ }
1403
+ if ( ca_index_key_is_scalar(a) ) {
1404
+ /* scalar index -> axis dropped in base; contributes no output axis */
1405
+ continue;
1406
+ }
1407
+ if ( rb_obj_is_carray(a) ) {
1408
+ take += extra;
1409
+ }
1410
+ for (t = 0; t < take; t++) {
1411
+ if ( base_cursor >= cb->ndim || nfinal >= CA_RANK_MAX ) { /* rank-check: a count of indices */
1412
+ base_cursor = -1;
1413
+ break;
1414
+ }
1415
+ final_dims[nfinal++] = cb->dim[base_cursor++];
1184
1416
  }
1185
- final_dims[nfinal++] = cb->dim[base_cursor++];
1186
1417
  }
1187
1418
  }
1188
1419
  if ( base_cursor != cb->ndim ) {
@@ -1226,7 +1457,7 @@ rb_ca_slab_iter_new (VALUE self, CAIndexInfo *info)
1226
1457
  align with the post-expansion ndim, replace :> with nil (full axis),
1227
1458
  promote Integer to length-1 Range so the base view preserves ndim,
1228
1459
  recurse through the regular indexer dispatch to build the base
1229
- reference (CABlock / CASelectAxis / CAGrid / CAMapping / ...), then
1460
+ reference (CABlock / CASelectAxis / CAGrid / ...), then
1230
1461
  wrap in CASlabIterator with the recorded slab_axes positions. */
1231
1462
  static VALUE
1232
1463
  rb_ca_fetch_slab_sigil (int argc, VALUE *argv, VALUE self)
@@ -1280,7 +1511,7 @@ rb_ca_fetch_slab_sigil (int argc, VALUE *argv, VALUE self)
1280
1511
  eargv = expanded;
1281
1512
  }
1282
1513
 
1283
- if ( eargc > CA_RANK_MAX ) {
1514
+ if ( eargc > CA_RANK_MAX ) { /* rank-check: a count of indices */
1284
1515
  rb_raise(rb_eIndexError, "too many indices");
1285
1516
  }
1286
1517
 
@@ -1290,7 +1521,7 @@ rb_ca_fetch_slab_sigil (int argc, VALUE *argv, VALUE self)
1290
1521
  rb_ary_push(slab_axes, INT2NUM(i));
1291
1522
  clean[i] = Qnil;
1292
1523
  }
1293
- else if ( FIXNUM_P(a) || RB_TYPE_P(a, T_BIGNUM) ) {
1524
+ else if ( ca_index_key_is_scalar(a) ) {
1294
1525
  /* Promote Integer to length-1 Range so the recursed view keeps
1295
1526
  the axis (matches the existing :> dispatcher semantics where
1296
1527
  scalars become length-1 BLOCKs via finalize_reg). */
@@ -1358,7 +1589,7 @@ rb_cs_fetch_method (int argc, VALUE *argv, VALUE self)
1358
1589
  break;
1359
1590
  case CA_REG_METHOD_CALL: {
1360
1591
  volatile VALUE idx;
1361
- idx = rb_funcall2(self, SYM2ID(info.symbol), argc-1, argv+1);
1592
+ idx = rb_funcallv_public(self, SYM2ID(info.symbol), argc-1, argv+1);
1362
1593
  obj = rb_ca_fetch(self, idx);
1363
1594
  break;
1364
1595
  }
@@ -1374,11 +1605,6 @@ rb_cs_fetch_method (int argc, VALUE *argv, VALUE self)
1374
1605
  }
1375
1606
  break;
1376
1607
  }
1377
- case CA_REG_ATTRIBUTE: {
1378
- obj = rb_funcall(self, rb_intern("attribute"), 0);
1379
- obj = rb_hash_aref(obj, info.symbol);
1380
- break;
1381
- }
1382
1608
  default:
1383
1609
  rb_raise(rb_eIndexError, "invalid index specified");
1384
1610
  }
@@ -1403,7 +1629,7 @@ rb_cs_fetch_method (int argc, VALUE *argv, VALUE self)
1403
1629
  of letting the storage cast truncate it, and the bulk path must not be
1404
1630
  the looser door.
1405
1631
  - an Array is mapped structure-preserving (leaves converted),
1406
- - a scalar goes through the write hook (ca_face_scalar_to_storage), which
1632
+ - a scalar goes through the write hook (ca_face_element_to_storage), which
1407
1633
  leaves a bare Integer / String unchanged.
1408
1634
 
1409
1635
  Idempotent: an already-converted Integer passes through, so re-running on
@@ -1414,25 +1640,59 @@ ca_face_convert_store_rval (VALUE self, CArray *ca, VALUE val)
1414
1640
  if ( rb_obj_is_carray(val) ) {
1415
1641
  CArray *cval;
1416
1642
  GetCArray(val, cval);
1643
+ /* An object array holding struct elements (a record array's
1644
+ to_type(:object)) goes in as its values, each through the write
1645
+ hook; masked cells come back as UNDEF and stay masked. */
1646
+ if ( ca->obj_type == CA_OBJ_RECORD && cval->data_type == CA_OBJECT ) {
1647
+ return ca_face_convert_store_rval(self, ca,
1648
+ rb_funcall(val, rb_intern("to_a"), 0));
1649
+ }
1650
+ /* A CAString takes Strings. A text Face goes in as the CAString its
1651
+ to_string gives, and an object array as itself wrapped in a CAString,
1652
+ which checks every cell that is not masked; either keeps its shape,
1653
+ so it meets the shape rule of a store as an object array would. Any
1654
+ other array goes in as its values, each through the write hook. */
1655
+ if ( ca->obj_type == CA_OBJ_STRING && cval->obj_type != CA_OBJ_STRING ) {
1656
+ if ( ca_is_face(cval) && rb_respond_to(val, rb_intern("to_string")) ) {
1657
+ return ca_face_convert_store_rval(self, ca,
1658
+ rb_funcall(val, rb_intern("to_string"), 0));
1659
+ }
1660
+ if ( ! ca_is_face(cval) && cval->data_type == CA_OBJECT ) {
1661
+ return ca_face_convert_store_rval(self, ca,
1662
+ rb_funcall(rb_obj_class(self), rb_intern("wrap"), 1, val));
1663
+ }
1664
+ return ca_face_convert_store_rval(self, ca,
1665
+ rb_funcall(val, rb_intern("to_a"), 0));
1666
+ }
1417
1667
  /* Face RHS: reconcile to self's unit and descend to storage. Mirror of
1418
- the scalar path (ca_face_scalar_to_storage) for a bulk CArray source.
1668
+ the scalar path (ca_face_element_to_storage) for a bulk CArray source.
1419
1669
  Guarded on self having to_comparable so a Face without a reconcile
1420
1670
  algebra (e.g. a fixlen-storage Face, whose same-storage store already
1421
1671
  takes the storage xfer branch) is left to the existing store path. */
1422
1672
  if ( ca_is_face(cval) && rb_respond_to(self, rb_intern("to_comparable")) ) {
1423
- volatile VALUE reconciled = rb_funcall(self, rb_intern("to_comparable"),
1424
- 1, val);
1425
- return rb_funcall(reconciled, rb_intern("parent"), 0);
1673
+ return ca_face_reconcile_operand(self, val, "[]=");
1674
+ }
1675
+ /* A Face of the same class and a compatible state holds values of the
1676
+ same kind in the same encoding: it goes in as its storage. */
1677
+ if ( ca_is_face(cval) && rb_obj_class(val) == rb_obj_class(self)
1678
+ && ca_face_state_compatible(self, ca, val, cval) ) {
1679
+ return rb_ca_strip_face_value(val);
1426
1680
  }
1427
1681
  /* Bare storage escape, integer storage: integer cells only (see above). */
1428
1682
  {
1429
1683
  CArray *storage = ca_strip_face(ca);
1430
1684
  if ( storage && ca_is_integer_type(storage) && ! ca_is_integer_type(cval) ) {
1685
+ const char *method = ca_face_storage_method(self);
1686
+ if ( method ) {
1687
+ rb_raise(rb_eTypeError,
1688
+ "%s cannot store a bare %s array as raw storage "
1689
+ "(write the %s storage, which #%s gives, directly)",
1690
+ rb_obj_classname(self), ca_type_name[cval->data_type],
1691
+ ca_type_name[storage->data_type], method);
1692
+ }
1431
1693
  rb_raise(rb_eTypeError,
1432
- "%s cannot store a bare %s array as raw storage "
1433
- "(use ca.parent to write the %s storage directly)",
1434
- rb_obj_classname(self), ca_type_name[cval->data_type],
1435
- ca_type_name[storage->data_type]);
1694
+ "%s cannot store a bare %s array as raw storage",
1695
+ rb_obj_classname(self), ca_type_name[cval->data_type]);
1436
1696
  }
1437
1697
  }
1438
1698
  return val;
@@ -1445,15 +1705,16 @@ ca_face_convert_store_rval (VALUE self, CArray *ca, VALUE val)
1445
1705
  }
1446
1706
  return out;
1447
1707
  }
1448
- return ca_face_scalar_to_storage(self, ca, val);
1708
+ return ca_face_element_to_storage(self, ca, val);
1449
1709
  }
1450
1710
 
1451
1711
  static VALUE
1452
1712
  rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1453
1713
  {
1454
- volatile VALUE obj = Qnil, rval;
1714
+ volatile VALUE obj = Qnil, rval, dest;
1455
1715
  CArray *ca;
1456
1716
  CAIndexInfo info;
1717
+ int converted = 0; /* rval has been through the Face's write hook */
1457
1718
 
1458
1719
  rb_ca_modify(self);
1459
1720
 
@@ -1463,18 +1724,37 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1463
1724
  retry:
1464
1725
 
1465
1726
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1727
+ rval = ca_face_source_for_store(self, ca, rval);
1466
1728
 
1467
1729
  /* Face store: bring a surface value object (Scalar / Time / DateTime) into
1468
1730
  the storage domain while self is still the Face. Store-side views strip
1469
1731
  the Face to storage, so this is the only point that sees it. */
1470
1732
  if ( ca_is_face(ca) ) {
1471
- rval = ca_face_convert_store_rval(self, ca, rval);
1733
+ /* Once: a retry below comes back here with rval already converted. */
1734
+ if ( ! converted ) {
1735
+ rval = ca_face_convert_store_rval(self, ca, rval);
1736
+ converted = 1;
1737
+ }
1738
+ /* A CArray source is storage now, so the destination has to be too.
1739
+ A block store descends on its own (its view is built on the storage);
1740
+ a whole, boolean or address store kept the Face and cast the storage
1741
+ to the Face's surface type, which an int64 tick cannot become. A Face
1742
+ whose surface type is its storage's (a string or record Face) needs
1743
+ no descent, and keeps its own index forms (a record's field name). */
1744
+ if ( rb_obj_is_carray(rval) ) {
1745
+ CArray *cr;
1746
+ GetCArray(rval, cr);
1747
+ if ( ! ca_is_face(cr) && ca_strip_face(ca)->data_type != ca->data_type ) {
1748
+ self = rb_ca_strip_face_value(self);
1749
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1750
+ }
1751
+ }
1472
1752
  }
1473
1753
 
1474
1754
  /* newaxis (:_) interception on store. Build the newaxis view (a
1475
1755
  writable reshape aliasing self), then assign rval into the whole view
1476
1756
  (writes propagate to self). */
1477
- if ( ca_argv_has_newaxis(argc, argv) ) {
1757
+ if ( ! ca_argv_is_method_call(argc, argv) && ca_argv_has_newaxis(argc, argv) ) {
1478
1758
  volatile VALUE view = rb_ca_fetch_newaxis(argc, argv, self, ca);
1479
1759
  rb_ca_store_all(view, rval);
1480
1760
  return rval;
@@ -1483,7 +1763,7 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1483
1763
  /* slab sigil on store is rejected regardless of outer-slot type. Catch
1484
1764
  :> before scan_index so mask/fancy outer slots get the same "not
1485
1765
  supported" message instead of a misleading TypeError. */
1486
- if ( ca_argv_has_slab_sigil(argc, argv) ) {
1766
+ if ( ! ca_argv_is_method_call(argc, argv) && ca_argv_has_slab_sigil(argc, argv) ) {
1487
1767
  rb_raise(rb_eIndexError,
1488
1768
  "assignment through a slab iterator (:>) is not supported "
1489
1769
  "(use a block index, e.g. ca[range, nil] = val, or each_slab)");
@@ -1492,31 +1772,50 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1492
1772
  info.range_check = 1;
1493
1773
  rb_ca_scan_index(ca->ndim, ca->dim, ca->elements, argc, argv, &info);
1494
1774
 
1775
+ /* A value or a Ruby Array converted above is in the storage domain now.
1776
+ Stored through the Face it would meet the write hook a second time
1777
+ (rb_ca_obj2ptr converts too), so a positional store goes into the
1778
+ storage, which has the Face's shape. A predicate (:sym) is still
1779
+ asked of the Face; a member store keeps the Face for its data_class. */
1780
+ dest = self;
1781
+ if ( converted && ca_is_face(ca) && ! rb_obj_is_carray(rval) ) {
1782
+ dest = rb_ca_strip_face_value(self);
1783
+ }
1784
+
1785
+ /* One cell of a CAString holds a String. Stored into the object storage,
1786
+ a Ruby Array would become the cell's value, which a plain object array
1787
+ allows. */
1788
+ if ( ca->obj_type == CA_OBJ_STRING && TYPE(rval) == T_ARRAY &&
1789
+ ( info.type == CA_REG_ADDRESS || info.type == CA_REG_POINT ) ) {
1790
+ rb_raise(rb_eTypeError,
1791
+ "%s: an Array is not a String (one cell of a CAString holds "
1792
+ "a String; UNDEF masks a cell)", ca_calling_method_name());
1793
+ }
1794
+
1495
1795
  switch ( info.type ) {
1496
1796
  case CA_REG_ADDRESS_COMPLEX:
1497
1797
  self = rb_ca_refer_new_flatten(self);
1498
1798
  goto retry;
1499
1799
  case CA_REG_ADDRESS:
1500
- obj = rb_ca_store_address(self, &info, rval);
1800
+ obj = rb_ca_store_address(dest, &info, rval);
1501
1801
  break;
1502
1802
  case CA_REG_FLATTEN:
1503
- self = rb_ca_refer_new_flatten(self);
1504
- obj = rb_ca_store_all(self, rval);
1803
+ obj = rb_ca_store_all(rb_ca_refer_new_flatten(dest), rval);
1505
1804
  break;
1506
1805
  case CA_REG_POINT:
1507
- obj = rb_ca_store_point(self, &info, rval);
1806
+ obj = rb_ca_store_point(dest, &info, rval);
1508
1807
  break;
1509
1808
  case CA_REG_ALL:
1510
- obj = rb_ca_store_all(self, rval);
1809
+ obj = rb_ca_store_all(dest, rval);
1511
1810
  break;
1512
1811
  case CA_REG_BLOCK: {
1513
1812
  volatile VALUE block;
1514
- block = rb_ca_ref_block(self, &info);
1813
+ block = rb_ca_ref_block(dest, &info);
1515
1814
  obj = rb_ca_store_all(block, rval);
1516
1815
  break;
1517
1816
  }
1518
1817
  case CA_REG_SELECT: {
1519
- obj = rb_ca_select_new(self, argv[0]);
1818
+ obj = rb_ca_select_new(dest, argv[0]);
1520
1819
  obj = rb_ca_store_all(obj, rval);
1521
1820
  break;
1522
1821
  }
@@ -1529,27 +1828,27 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1529
1828
  break;
1530
1829
  }
1531
1830
  case CA_REG_REPEAT: {
1532
- obj = rb_ca_repeat(argc, argv, self);
1831
+ obj = rb_ca_repeat(argc, argv, dest);
1533
1832
  obj = rb_ca_store_all(obj, rval);
1534
1833
  break;
1535
1834
  }
1536
1835
  case CA_REG_MAPPING: {
1537
- obj = rb_ca_fancy_index_chain(self, argv[0]);
1836
+ obj = rb_ca_fancy_index_chain(dest, argv[0]);
1538
1837
  obj = rb_ca_store_all(obj, rval);
1539
1838
  break;
1540
1839
  }
1541
1840
  case CA_REG_GRID: {
1542
- obj = rb_ca_grid(argc, argv, self);
1841
+ obj = rb_ca_grid(argc, argv, dest);
1543
1842
  obj = rb_ca_store_all(obj, rval);
1544
1843
  break;
1545
1844
  }
1546
1845
  case CA_REG_METHOD_CALL: {
1846
+ /* a[:eq, 0] = v is a[a.eq(0)] = v: the index is computed first and the
1847
+ store then goes through it, as the read does -- only the cells it
1848
+ picks are written. */
1547
1849
  volatile VALUE idx;
1548
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1549
- ca_attach(ca);
1550
- idx = rb_funcall2(self, SYM2ID(info.symbol), (int)(argc-1), argv+1);
1551
- obj = rb_ca_store(self, idx, rval);
1552
- ca_detach(ca);
1850
+ idx = rb_funcallv_public(self, SYM2ID(info.symbol), argc-1, argv+1);
1851
+ obj = rb_ca_store(dest, idx, rval);
1553
1852
  break;
1554
1853
  }
1555
1854
  case CA_REG_MEMBER: {
@@ -1564,11 +1863,6 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1564
1863
  }
1565
1864
  break;
1566
1865
  }
1567
- case CA_REG_ATTRIBUTE: {
1568
- obj = rb_funcall(self, rb_intern("attribute"), 0);
1569
- obj = rb_hash_aset(obj, info.symbol, rval);
1570
- break;
1571
- }
1572
1866
  }
1573
1867
 
1574
1868
  return obj;
@@ -1598,7 +1892,9 @@ rb_ca_store (VALUE self, VALUE index, VALUE rval)
1598
1892
  case T_ARRAY:
1599
1893
  index = rb_obj_clone(index);
1600
1894
  rb_ary_push(index, rval);
1601
- return rb_ca_store_method((int)RARRAY_LEN(index), (VALUE *)RARRAY_CONST_PTR(index), self);
1895
+ rval = rb_ca_store_method((int)RARRAY_LEN(index), (VALUE *)RARRAY_CONST_PTR(index), self);
1896
+ RB_GC_GUARD(index); /* the store reads its arguments out of index */
1897
+ return rval;
1602
1898
  default: {
1603
1899
  VALUE rindex[2] = { index, rval };
1604
1900
  return rb_ca_store_method(2, rindex, self);
@@ -1611,7 +1907,9 @@ rb_ca_store2 (VALUE self, int n, VALUE *rindex, VALUE rval)
1611
1907
  {
1612
1908
  volatile VALUE index = rb_ary_new4(n, rindex);
1613
1909
  rb_ary_push(index, rval);
1614
- return rb_ca_store_method((int)RARRAY_LEN(index), (VALUE *)RARRAY_CONST_PTR(index), self);
1910
+ rval = rb_ca_store_method((int)RARRAY_LEN(index), (VALUE *)RARRAY_CONST_PTR(index), self);
1911
+ RB_GC_GUARD(index); /* the store reads its arguments out of index */
1912
+ return rval;
1615
1913
  }
1616
1914
 
1617
1915
  static VALUE
@@ -1627,15 +1925,13 @@ rb_ca_s_scan_index (VALUE self, VALUE rdim, VALUE ridx)
1627
1925
  Check_Type(rdim, T_ARRAY);
1628
1926
  Check_Type(ridx, T_ARRAY);
1629
1927
 
1630
- elements = 1;
1631
1928
  ndim = (int) RARRAY_LEN(rdim);
1929
+ CA_CHECK_RANK_GIVEN(ndim);
1632
1930
  for (i=0; i<ndim; i++) {
1633
1931
  dim[i] = NUM2SIZE(rb_ary_entry(rdim, i));
1634
- elements *= dim[i];
1635
1932
  }
1636
-
1637
- CA_CHECK_RANK(ndim);
1638
- CA_CHECK_DIM(ndim, dim);
1933
+ CA_CHECK_DIM_GIVEN(ndim, dim);
1934
+ elements = ca_shape_elements(ndim, dim, 1);
1639
1935
 
1640
1936
  info.range_check = 1;
1641
1937
  rb_ca_scan_index(ndim, dim, elements,
@@ -1699,7 +1995,7 @@ rb_ca_s_scan_index (VALUE self, VALUE rdim, VALUE ridx)
1699
1995
  info.index[i].symbol.spec));
1700
1996
  break;
1701
1997
  default:
1702
- rb_raise(rb_eRuntimeError, "unknown index spec");
1998
+ rb_raise(rb_eIndexError, "unknown index spec");
1703
1999
  }
1704
2000
  }
1705
2001
  break;
@@ -1707,8 +2003,7 @@ rb_ca_s_scan_index (VALUE self, VALUE rdim, VALUE ridx)
1707
2003
  case CA_REG_GRID:
1708
2004
  case CA_REG_MAPPING:
1709
2005
  case CA_REG_METHOD_CALL:
1710
- case CA_REG_MEMBER:
1711
- case CA_REG_ATTRIBUTE:
2006
+ case CA_REG_MEMBER:
1712
2007
  break;
1713
2008
  default:
1714
2009
  rb_raise(rb_eArgError, "unknown index specification");
@@ -1767,7 +2062,7 @@ rb_ca_normalize_index (VALUE self, VALUE ridx)
1767
2062
  rb_ary_store(rindex, i, ID2SYM(info.index[i].symbol.id));
1768
2063
  break;
1769
2064
  default:
1770
- rb_raise(rb_eRuntimeError, "unknown index spec");
2065
+ rb_raise(rb_eIndexError, "unknown index spec");
1771
2066
  }
1772
2067
  }
1773
2068
  return rindex;
@@ -1804,18 +2099,19 @@ parse_shape_kwarg (VALUE rshape, int *out_ndim, ca_size_t *dim,
1804
2099
 
1805
2100
  Check_Type(rshape, T_ARRAY);
1806
2101
  ndim = (int) RARRAY_LEN(rshape);
1807
- if ( ndim < 1 || ndim > CA_RANK_MAX ) {
2102
+ if ( ndim < 1 ) {
1808
2103
  rb_raise(rb_eArgError, "invalid shape ndim %d (must be 1..%d)",
1809
2104
  ndim, CA_RANK_MAX);
1810
2105
  }
2106
+ ca_check_rank_fits(ndim, NULL);
1811
2107
  for (i = 0; i < ndim; i++) {
1812
2108
  dim[i] = NUM2SIZE(rb_ary_entry(rshape, i));
1813
2109
  if ( dim[i] < 0 ) {
1814
2110
  rb_raise(rb_eArgError, "negative dim %" PRId64 " at axis %d",
1815
2111
  (ca_size_t) dim[i], i);
1816
2112
  }
1817
- elements *= dim[i];
1818
2113
  }
2114
+ elements = ca_shape_elements(ndim, dim, 1);
1819
2115
  *out_ndim = ndim;
1820
2116
  *out_elements = elements;
1821
2117
  }
@@ -1829,12 +2125,7 @@ addr2index_do (int ndim, ca_size_t *dim, ca_size_t elements, VALUE raddr)
1829
2125
  /* Scalar path (Integer input): returns Ruby Array of N Integers.
1830
2126
  * Preserves legacy shape for callers like `i, j = ca.addr2index(k)`. */
1831
2127
  if ( rb_obj_is_kind_of(raddr, rb_cInteger) ) {
1832
- ca_size_t addr = NUM2SIZE(raddr);
1833
- if ( addr < 0 || addr >= elements ) {
1834
- rb_raise(rb_eArgError,
1835
- "address %" PRId64 " is out of range (0..%" PRId64 ")",
1836
- (ca_size_t) addr, (ca_size_t) (elements - 1));
1837
- }
2128
+ ca_size_t addr = ca_index_in_range(NUM2SIZE(raddr), elements, "address");
1838
2129
  out = rb_ary_new2(ndim);
1839
2130
  for (i = ndim - 1; i >= 0; i--) {
1840
2131
  rb_ary_store(out, i, SIZE2NUM(addr % dim[i]));
@@ -1852,8 +2143,9 @@ addr2index_do (int ndim, ca_size_t *dim, ca_size_t elements, VALUE raddr)
1852
2143
  ca_size_t j, n;
1853
2144
  volatile VALUE objs[CA_RANK_MAX];
1854
2145
 
2146
+ ca_check_index_array(raddr);
1855
2147
  cin = ca_wrap_readonly(raddr, CA_SIZE);
1856
- ca_attach(cin);
2148
+ ca_attach(cin); /* window: detached before its one raise */
1857
2149
 
1858
2150
  out = rb_ary_new2(ndim);
1859
2151
  for (i = 0; i < ndim; i++) {
@@ -1882,12 +2174,11 @@ addr2index_do (int ndim, ca_size_t *dim, ca_size_t elements, VALUE raddr)
1882
2174
  continue;
1883
2175
  }
1884
2176
  addr = src[j];
1885
- if ( addr < 0 || addr >= elements ) {
2177
+ if ( addr < -elements || addr >= elements ) {
1886
2178
  ca_detach(cin);
1887
- rb_raise(rb_eArgError,
1888
- "address %" PRId64 " is out of range (0..%" PRId64 ")",
1889
- (ca_size_t) addr, (ca_size_t) (elements - 1));
2179
+ ca_index_out_of_range(addr, elements, "address");
1890
2180
  }
2181
+ addr = ca_index_in_range(addr, elements, "address");
1891
2182
  for (k = ndim - 1; k >= 0; k--) {
1892
2183
  p_out[k][j] = addr % dim[k];
1893
2184
  addr /= dim[k];
@@ -1896,18 +2187,92 @@ addr2index_do (int ndim, ca_size_t *dim, ca_size_t elements, VALUE raddr)
1896
2187
  }
1897
2188
 
1898
2189
  ca_detach(cin);
2190
+ RB_GC_GUARD(raddr); /* the CA_SIZE view is reached only through cin */
1899
2191
  return out;
1900
2192
  }
1901
2193
  }
1902
2194
 
2195
+ /* The vector path of index2addr reads every index array inside one
2196
+ window: an index out of range raises part way through. */
2197
+
2198
+ typedef struct {
2199
+ int ndim;
2200
+ ca_size_t *dim;
2201
+ CArray **cidx;
2202
+ int out_ndim;
2203
+ ca_size_t *out_dim;
2204
+ ca_size_t out_elements;
2205
+ } ca_index2addr_ctx_t;
2206
+
1903
2207
  static VALUE
1904
- index2addr_do (int ndim, ca_size_t *dim, int argc, VALUE *argv)
2208
+ ca_index2addr_body (VALUE arg)
1905
2209
  {
2210
+ ca_index2addr_ctx_t *c = (ca_index2addr_ctx_t *) arg;
2211
+ int ndim = c->ndim;
2212
+ ca_size_t *dim = c->dim;
2213
+ CArray **cidx = c->cidx;
2214
+ ca_size_t out_elements = c->out_elements;
1906
2215
  volatile VALUE obj;
1907
- CArray *co, *cidx[CA_RANK_MAX];
2216
+ CArray *co;
1908
2217
  ca_size_t *q, *p[CA_RANK_MAX], s[CA_RANK_MAX];
1909
2218
  ca_size_t addr, k, n;
1910
2219
  boolean8_t *m;
2220
+ int i;
2221
+
2222
+ for (i = 0; i < ndim; i++) {
2223
+ ca_set_iterator(1, cidx[i], &p[i], &s[i]);
2224
+ }
2225
+
2226
+ obj = rb_carray_new(CA_SIZE, c->out_ndim, c->out_dim, 0, NULL);
2227
+ TypedData_Get_Struct(obj, CArray, &carray_data_type, co);
2228
+
2229
+ q = (ca_size_t *) co->ptr;
2230
+
2231
+ ca_copy_mask_overwrite_n(co, out_elements, ndim, cidx);
2232
+ m = ( co->mask ) ? (boolean8_t *) co->mask->ptr : NULL;
2233
+
2234
+ if ( m ) {
2235
+ n = out_elements;
2236
+ while ( n-- ) {
2237
+ if ( ! *m ) {
2238
+ addr = 0;
2239
+ for (i = 0; i < ndim; i++) {
2240
+ k = *(p[i]);
2241
+ p[i] += s[i];
2242
+ CA_CHECK_INDEX(k, dim[i]);
2243
+ addr = dim[i] * addr + k;
2244
+ }
2245
+ *q = addr;
2246
+ }
2247
+ else {
2248
+ for (i = 0; i < ndim; i++) { p[i] += s[i]; }
2249
+ }
2250
+ m++; q++;
2251
+ }
2252
+ }
2253
+ else {
2254
+ n = out_elements;
2255
+ while ( n-- ) {
2256
+ addr = 0;
2257
+ for (i = 0; i < ndim; i++) {
2258
+ k = *(p[i]);
2259
+ p[i] += s[i];
2260
+ CA_CHECK_INDEX(k, dim[i]);
2261
+ addr = dim[i] * addr + k;
2262
+ }
2263
+ *q = addr;
2264
+ q++;
2265
+ }
2266
+ }
2267
+
2268
+ return obj;
2269
+ }
2270
+
2271
+ static VALUE
2272
+ index2addr_do (int ndim, ca_size_t *dim, int argc, VALUE *argv)
2273
+ {
2274
+ CArray *cidx[CA_RANK_MAX];
2275
+ ca_size_t addr, k;
1911
2276
  int i, all_number = 1;
1912
2277
  int out_ndim = 1;
1913
2278
  ca_size_t out_dim[CA_RANK_MAX];
@@ -1941,6 +2306,7 @@ index2addr_do (int ndim, ca_size_t *dim, int argc, VALUE *argv)
1941
2306
  * the first non-scalar CArray input; other non-scalar inputs must
1942
2307
  * match that shape. */
1943
2308
  for (i = 0; i < ndim; i++) {
2309
+ ca_check_index_array(argv[i]);
1944
2310
  cidx[i] = ca_wrap_readonly(argv[i], CA_SIZE);
1945
2311
  if ( ! ca_is_scalar(cidx[i]) ) {
1946
2312
  if ( shape_from < 0 ) {
@@ -1969,56 +2335,19 @@ index2addr_do (int ndim, ca_size_t *dim, int argc, VALUE *argv)
1969
2335
  }
1970
2336
  }
1971
2337
 
1972
- for (i = 0; i < ndim; i++) {
1973
- ca_attach(cidx[i]);
1974
- ca_set_iterator(1, cidx[i], &p[i], &s[i]);
1975
- }
1976
-
1977
- obj = rb_carray_new(CA_SIZE, out_ndim, out_dim, 0, NULL);
1978
- TypedData_Get_Struct(obj, CArray, &carray_data_type, co);
1979
-
1980
- q = (ca_size_t *) co->ptr;
1981
-
1982
- ca_copy_mask_overwrite_n(co, out_elements, ndim, cidx);
1983
- m = ( co->mask ) ? (boolean8_t *) co->mask->ptr : NULL;
1984
-
1985
- if ( m ) {
1986
- n = out_elements;
1987
- while ( n-- ) {
1988
- if ( ! *m ) {
1989
- addr = 0;
1990
- for (i = 0; i < ndim; i++) {
1991
- k = *(p[i]);
1992
- p[i] += s[i];
1993
- CA_CHECK_INDEX(k, dim[i]);
1994
- addr = dim[i] * addr + k;
1995
- }
1996
- *q = addr;
1997
- }
1998
- else {
1999
- for (i = 0; i < ndim; i++) { p[i] += s[i]; }
2000
- }
2001
- m++; q++;
2002
- }
2003
- }
2004
- else {
2005
- n = out_elements;
2006
- while ( n-- ) {
2007
- addr = 0;
2008
- for (i = 0; i < ndim; i++) {
2009
- k = *(p[i]);
2010
- p[i] += s[i];
2011
- CA_CHECK_INDEX(k, dim[i]);
2012
- addr = dim[i] * addr + k;
2013
- }
2014
- *q = addr;
2015
- q++;
2016
- }
2338
+ {
2339
+ ca_index2addr_ctx_t c;
2340
+ char modes[CA_RANK_MAX + 1];
2341
+ memset(modes, 'r', (size_t) ndim);
2342
+ modes[ndim] = '\0';
2343
+ c.ndim = ndim;
2344
+ c.dim = dim;
2345
+ c.cidx = cidx;
2346
+ c.out_ndim = out_ndim;
2347
+ c.out_dim = out_dim;
2348
+ c.out_elements = out_elements;
2349
+ return ca_attach_window(ndim, cidx, modes, ca_index2addr_body, (VALUE) &c);
2017
2350
  }
2018
-
2019
- for (i = 0; i < ndim; i++) { ca_detach(cidx[i]); }
2020
-
2021
- return obj;
2022
2351
  }
2023
2352
 
2024
2353
  VALUE
@@ -2062,7 +2391,7 @@ rb_ca_s_addr2index (int argc, VALUE *argv, VALUE klass)
2062
2391
  static VALUE
2063
2392
  rb_ca_s_index2addr (int argc, VALUE *argv, VALUE klass)
2064
2393
  {
2065
- VALUE rest, opts;
2394
+ VALUE rest, opts, out;
2066
2395
  ID kw_ids[1];
2067
2396
  VALUE kw_vals[1];
2068
2397
  int ndim;
@@ -2076,7 +2405,10 @@ rb_ca_s_index2addr (int argc, VALUE *argv, VALUE klass)
2076
2405
  }
2077
2406
  rb_get_kwargs(opts, kw_ids, 1, 0, kw_vals);
2078
2407
  parse_shape_kwarg(kw_vals[0], &ndim, dim, &elements);
2079
- return index2addr_do(ndim, dim, (int) RARRAY_LEN(rest), RARRAY_PTR(rest));
2408
+ /* index2addr_do reads, and replaces, its arguments in rest */
2409
+ out = index2addr_do(ndim, dim, (int) RARRAY_LEN(rest), RARRAY_PTR(rest));
2410
+ RB_GC_GUARD(rest);
2411
+ return out;
2080
2412
  }
2081
2413
 
2082
2414