carray 3.0.1 → 3.0.3

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 (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +824 -0
  3. data/README.md +5 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +258 -4
  6. data/ext/ca_axis_group.c +368 -184
  7. data/ext/ca_binop_dispatch.c +54 -32
  8. data/ext/ca_binop_dispatch.h +1 -0
  9. data/ext/ca_categorical_iterator.c +90 -41
  10. data/ext/ca_compare.c +8 -2
  11. data/ext/ca_for_buffer.h +21 -68
  12. data/ext/ca_group_iter.c +27 -11
  13. data/ext/ca_kernel_iterator.c +960 -245
  14. data/ext/ca_kernel_iterator.h +352 -181
  15. data/ext/ca_monop_dispatch.c +4 -4
  16. data/ext/ca_monop_dispatch.h +4 -0
  17. data/ext/ca_obj_array.c +194 -42
  18. data/ext/ca_obj_bincmp.c +152 -121
  19. data/ext/ca_obj_binop.c +107 -210
  20. data/ext/ca_obj_bitarray.c +37 -24
  21. data/ext/ca_obj_bitfield.c +95 -62
  22. data/ext/ca_obj_block.c +5 -4
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +180 -50
  25. data/ext/ca_obj_face.c +428 -62
  26. data/ext/ca_obj_face.h +107 -47
  27. data/ext/ca_obj_fake.c +21 -50
  28. data/ext/ca_obj_farray.c +1 -0
  29. data/ext/ca_obj_field.c +16 -3
  30. data/ext/ca_obj_fixlen_string.c +39 -26
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +235 -55
  33. data/ext/ca_obj_moncmp.c +61 -61
  34. data/ext/ca_obj_monop.c +125 -107
  35. data/ext/ca_obj_object.c +551 -233
  36. data/ext/ca_obj_record.c +51 -16
  37. data/ext/ca_obj_reduce.c +5 -8
  38. data/ext/ca_obj_refer.c +117 -33
  39. data/ext/ca_obj_remap.c +57 -17
  40. data/ext/ca_obj_repeat.c +21 -5
  41. data/ext/ca_obj_roll.c +115 -11
  42. data/ext/ca_obj_select.c +88 -75
  43. data/ext/ca_obj_select_axis.c +58 -60
  44. data/ext/ca_obj_shift.c +22 -102
  45. data/ext/ca_obj_stack.c +223 -123
  46. data/ext/ca_obj_stride.c +267 -35
  47. data/ext/ca_obj_string.c +25 -22
  48. data/ext/ca_obj_tile.c +32 -16
  49. data/ext/ca_obj_time.c +23 -25
  50. data/ext/ca_obj_timedelta.c +21 -23
  51. data/ext/ca_obj_transpose.c +12 -3
  52. data/ext/ca_obj_triop.c +110 -117
  53. data/ext/ca_obj_window.c +157 -29
  54. data/ext/ca_op_ipower.c +25 -13
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_rng_normal.h +42 -0
  58. data/ext/ca_rng_xoshiro256pp.h +105 -0
  59. data/ext/ca_sort_kernels.h +70 -0
  60. data/ext/ca_sweep_engine.c +385 -198
  61. data/ext/ca_sweep_engine.h +31 -23
  62. data/ext/ca_triop_dispatch.c +4 -1
  63. data/ext/ca_triop_dispatch.h +5 -2
  64. data/ext/caframe_csv_reader.c +463 -0
  65. data/ext/carray.h +90 -28
  66. data/ext/carray_access.c +487 -231
  67. data/ext/carray_address_basis.c +731 -0
  68. data/ext/carray_attribute.c +45 -3
  69. data/ext/carray_bincount.c +32 -2
  70. data/ext/carray_broadcast.c +49 -17
  71. data/ext/carray_call_cfunc.c +667 -511
  72. data/ext/carray_cast.c +1556 -247
  73. data/ext/carray_cast_func.rb +34 -21
  74. data/ext/carray_conversion.c +197 -73
  75. data/ext/carray_copy.c +84 -38
  76. data/ext/carray_core.c +591 -119
  77. data/ext/carray_count.c +148 -96
  78. data/ext/carray_element.c +18 -27
  79. data/ext/carray_factorize.c +604 -291
  80. data/ext/carray_generate.c +76 -40
  81. data/ext/carray_histogram.c +152 -94
  82. data/ext/carray_hold.c +2 -1
  83. data/ext/carray_index_classifier.c +63 -96
  84. data/ext/carray_internal.h +400 -1
  85. data/ext/carray_lazy.c +834 -79
  86. data/ext/carray_loop.c +88 -66
  87. data/ext/carray_mask.c +705 -324
  88. data/ext/carray_math_kernel.h +1 -0
  89. data/ext/carray_median_percentile.c +263 -63
  90. data/ext/carray_memory_view.c +254 -248
  91. data/ext/carray_operator.c +1093 -363
  92. data/ext/carray_order.c +133 -69
  93. data/ext/carray_partition.c +102 -56
  94. data/ext/carray_random.c +582 -204
  95. data/ext/carray_scatter.c +308 -65
  96. data/ext/carray_segment.c +257 -0
  97. data/ext/carray_slab.c +183 -70
  98. data/ext/carray_sort.c +235 -240
  99. data/ext/carray_sort_kernel.c +8 -1
  100. data/ext/carray_struct.c +99 -21
  101. data/ext/carray_test.c +167 -64
  102. data/ext/carray_time_text.c +1182 -0
  103. data/ext/carray_utils.c +363 -98
  104. data/ext/extconf.rb +27 -2
  105. data/ext/mk_call_cfunc.rb +103 -118
  106. data/ext/mk_pow5_table.rb +35 -0
  107. data/ext/mkkernel.rb +1349 -851
  108. data/ext/ruby_carray.c +17 -2
  109. data/ext/version.h +4 -4
  110. data/ext/version.rb +4 -2
  111. data/lib/carray/attribute.rb +39 -0
  112. data/lib/carray/autoload_carray.rb +24 -3
  113. data/lib/carray/autoload_method_extension.rb +18 -0
  114. data/lib/carray/axis_group.rb +245 -20
  115. data/lib/carray/basics.rb +95 -25
  116. data/lib/carray/bincount_nd.rb +6 -0
  117. data/lib/carray/block_iterator.rb +200 -54
  118. data/lib/carray/boolean_reduce.rb +21 -29
  119. data/lib/carray/categorical.rb +244 -59
  120. data/lib/carray/categorical_iterator.rb +391 -757
  121. data/lib/carray/complex.rb +36 -29
  122. data/lib/carray/conditional.rb +94 -7
  123. data/lib/carray/const_string.rb +177 -27
  124. data/lib/carray/construct.rb +40 -0
  125. data/lib/carray/data_type_extension.rb +3 -0
  126. data/lib/carray/data_type_limits.rb +91 -0
  127. data/lib/carray/fixlen_string.rb +1 -1
  128. data/lib/carray/frame/concat.rb +157 -6
  129. data/lib/carray/frame/convert.rb +13 -7
  130. data/lib/carray/frame/csv_parser.rb +590 -76
  131. data/lib/carray/frame/describe.rb +79 -0
  132. data/lib/carray/frame/frame.rb +205 -35
  133. data/lib/carray/frame/group.rb +199 -40
  134. data/lib/carray/frame/io.rb +452 -60
  135. data/lib/carray/frame/join.rb +134 -46
  136. data/lib/carray/frame/records.rb +54 -19
  137. data/lib/carray/frame/reshape.rb +448 -0
  138. data/lib/carray/frame/sort.rb +26 -2
  139. data/lib/carray/frame/verbs.rb +726 -56
  140. data/lib/carray/frame.rb +2 -0
  141. data/lib/carray/fuse_source.rb +111 -2
  142. data/lib/carray/fusion.rb +229 -38
  143. data/lib/carray/inspect.rb +71 -20
  144. data/lib/carray/iterator.rb +157 -2
  145. data/lib/carray/lazy.rb +54 -67
  146. data/lib/carray/mask_gap_fill.rb +66 -50
  147. data/lib/carray/math.rb +32 -19
  148. data/lib/carray/meld_reduce.rb +40 -7
  149. data/lib/carray/methods/align_addr.rb +8 -4
  150. data/lib/carray/methods/bincount.rb +2 -2
  151. data/lib/carray/methods/bit_string.rb +14 -3
  152. data/lib/carray/methods/composition.rb +16 -10
  153. data/lib/carray/methods/discovery_along.rb +74 -0
  154. data/lib/carray/methods/factorize.rb +50 -0
  155. data/lib/carray/methods/gather_nd.rb +13 -2
  156. data/lib/carray/methods/insert_block.rb +9 -14
  157. data/lib/carray/methods/is_in.rb +130 -6
  158. data/lib/carray/methods/join.rb +1 -1
  159. data/lib/carray/methods/locate_addr.rb +235 -21
  160. data/lib/carray/methods/mask_duplicates.rb +35 -1
  161. data/lib/carray/methods/mode.rb +18 -1
  162. data/lib/carray/methods/nunique.rb +22 -1
  163. data/lib/carray/methods/repeat.rb +91 -0
  164. data/lib/carray/methods/resize.rb +21 -23
  165. data/lib/carray/methods/snap.rb +34 -12
  166. data/lib/carray/methods/unique.rb +52 -3
  167. data/lib/carray/record_order.rb +208 -0
  168. data/lib/carray/rng.rb +86 -0
  169. data/lib/carray/runtime.rb +14 -11
  170. data/lib/carray/segment_iterator.rb +990 -0
  171. data/lib/carray/serialize.rb +197 -27
  172. data/lib/carray/slab_iterator.rb +109 -40
  173. data/lib/carray/stack.rb +10 -4
  174. data/lib/carray/string_operation_extension.rb +9 -4
  175. data/lib/carray/struct.rb +142 -51
  176. data/lib/carray/struct_builder.rb +110 -14
  177. data/lib/carray/table.rb +2 -2
  178. data/lib/carray/time.rb +535 -105
  179. data/lib/carray/window_iterator.rb +259 -82
  180. data/lib/carray.rb +4 -2
  181. data/yard-stubs/ca_obj_array.rb +4 -4
  182. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  183. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  184. data/yard-stubs/ca_obj_block.rb +2 -7
  185. data/yard-stubs/ca_obj_fake.rb +1 -1
  186. data/yard-stubs/ca_obj_field.rb +1 -1
  187. data/yard-stubs/ca_obj_refer.rb +3 -3
  188. data/yard-stubs/ca_obj_roll.rb +0 -1
  189. data/yard-stubs/ca_obj_stride.rb +10 -7
  190. data/yard-stubs/ca_obj_transpose.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +11 -4
  192. data/yard-stubs/carray_access.rb +6 -5
  193. data/yard-stubs/carray_broadcast.rb +2 -2
  194. data/yard-stubs/carray_cast.rb +78 -43
  195. data/yard-stubs/carray_conversion.rb +13 -1
  196. data/yard-stubs/carray_copy.rb +8 -3
  197. data/yard-stubs/carray_core.rb +3 -83
  198. data/yard-stubs/carray_count.rb +16 -2
  199. data/yard-stubs/carray_lazy.rb +210 -1
  200. data/yard-stubs/carray_loop.rb +4 -1
  201. data/yard-stubs/carray_mask.rb +34 -10
  202. data/yard-stubs/carray_math.rb +1499 -4
  203. data/yard-stubs/carray_median_percentile.rb +26 -8
  204. data/yard-stubs/carray_order.rb +43 -82
  205. data/yard-stubs/carray_scatter.rb +10 -3
  206. data/yard-stubs/carray_segment.rb +72 -0
  207. data/yard-stubs/carray_slab.rb +11 -8
  208. data/yard-stubs/carray_sort.rb +50 -8
  209. data/yard-stubs/carray_test.rb +10 -0
  210. data/yard-stubs/carray_utils.rb +0 -26
  211. metadata +19 -19
  212. data/ext/ca_for_each_element.h +0 -239
  213. data/ext/carray_build_flags.h +0 -3
  214. data/ext/carray_config.h +0 -28
  215. data/ext/carray_kernels_bincmp.c +0 -4446
  216. data/ext/carray_kernels_binop.c +0 -11001
  217. data/ext/carray_kernels_init.c +0 -1131
  218. data/ext/carray_kernels_map.c +0 -3467
  219. data/ext/carray_kernels_moncmp.c +0 -2097
  220. data/ext/carray_kernels_monop.c +0 -18313
  221. data/ext/carray_kernels_reduce_aggregate.c +0 -25837
  222. data/ext/carray_kernels_reduce_boolean.c +0 -330
  223. data/ext/carray_kernels_reduce_cumulative.c +0 -14593
  224. data/ext/carray_kernels_reduce_extreme.c +0 -16948
  225. data/ext/carray_kernels_reduce_variance.c +0 -3910
  226. data/ext/carray_kernels_scan.c +0 -3693
  227. data/ext/carray_kernels_search.c +0 -32138
  228. data/ext/carray_kernels_sort.c +0 -10626
  229. data/ext/carray_kernels_triop.c +0 -1392
data/ext/carray_access.c CHANGED
@@ -19,7 +19,7 @@ static ID id_begin, id_end, id_excl_end;
19
19
  #define RANGE_EXCL(r) (rb_funcall(r, id_excl_end, 0))
20
20
 
21
21
  static ID id_to_ca;
22
- static VALUE sym_star, sym_perc, sym_under, sym_gt, sym_tilde;
22
+ static VALUE sym_perc, sym_under, sym_gt, sym_tilde;
23
23
  static VALUE S_CAInfo;
24
24
 
25
25
  VALUE
@@ -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
79
  out = rb_ca_ptr2obj(self, v);
91
- xfree(v);
80
+ ALLOCV_END(holder);
92
81
  }
93
82
 
94
83
  /* check if the element is masked */
@@ -101,8 +90,8 @@ 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
97
  (to_a / cast / serialize via rb_ca_ptr2obj) keep 0/1. Gate on the raw
@@ -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
161
  out = rb_ca_ptr2obj(self, v);
184
- xfree(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;
@@ -254,7 +230,7 @@ ary_guess_shape (VALUE ary, int level, int *max_level, ca_size_t *dim)
254
230
  {
255
231
  volatile VALUE ary0;
256
232
  if ( level > CA_RANK_MAX ) {
257
- rb_raise(rb_eRuntimeError, "too deep level array for conversion to carray");
233
+ rb_raise(rb_eArgError, "too deep level array for conversion to carray");
258
234
  }
259
235
 
260
236
  if ( TYPE(ary) == T_ARRAY ) {
@@ -304,7 +280,7 @@ ary_flatten_upto_level (VALUE ary, int max_level, int level,
304
280
  int32_t i;
305
281
 
306
282
  if ( TYPE(ary) != T_ARRAY ) {
307
- rb_raise(rb_eRuntimeError, "invalid shape array for conversion to carray");
283
+ rb_raise(rb_eArgError, "invalid shape array for conversion to carray");
308
284
  }
309
285
 
310
286
  if ( level == max_level ) {
@@ -321,7 +297,7 @@ ary_flatten_upto_level (VALUE ary, int max_level, int level,
321
297
  }
322
298
  }
323
299
 
324
- static VALUE
300
+ VALUE
325
301
  rb_ary_flatten_for_elements (VALUE ary, ca_size_t elements, void *ap)
326
302
  {
327
303
  CArray *ca = (CArray *) ap;
@@ -348,7 +324,7 @@ rb_ary_flatten_for_elements (VALUE ary, ca_size_t elements, void *ap)
348
324
  }
349
325
 
350
326
  if ( total != ca->elements ) {
351
- rb_raise(rb_eRuntimeError, "invalid shape array for conversion to carray");
327
+ rb_raise(rb_eArgError, "invalid shape array for conversion to carray");
352
328
  }
353
329
  else {
354
330
  volatile VALUE out = rb_ary_new2(0);
@@ -500,6 +476,184 @@ ca_id_expression_evaluator (void)
500
476
  return id;
501
477
  }
502
478
 
479
+ /* `a[:sym, args] = v` computes the index by calling Ruby with self held
480
+ attached for the reads, then stores through it. */
481
+
482
+ typedef struct {
483
+ VALUE self;
484
+ VALUE dest; /* what is stored into: self, or a Face's storage */
485
+ ID mid;
486
+ int argc;
487
+ VALUE *argv;
488
+ VALUE rval;
489
+ } ca_store_method_call_ctx_t;
490
+
491
+ static VALUE
492
+ ca_store_method_call_body (VALUE arg)
493
+ {
494
+ ca_store_method_call_ctx_t *c = (ca_store_method_call_ctx_t *) arg;
495
+ volatile VALUE idx = rb_funcallv_public(c->self, c->mid, c->argc, c->argv);
496
+ return rb_ca_store(c->dest, idx, c->rval);
497
+ }
498
+
499
+ /* The casting stores below convert into the destination inside a window,
500
+ and a conversion from Ruby objects can raise part way. Such a window
501
+ attaches rather than allocates: the cells converted before the raise
502
+ are then pushed back with the others left as they were -- what an
503
+ entity destination, which has no separate buffer, shows as well. A
504
+ cast that cannot raise keeps the cheaper allocate. */
505
+
506
+ typedef struct {
507
+ CArray *ca;
508
+ CArray *cv; /* CArray source, delivered into scratch */
509
+ char *scratch;
510
+ boolean8_t *mscratch; /* cv's mask, or NULL when it has none */
511
+ CArray *ico; /* Ruby Array source: an object descriptor */
512
+ volatile VALUE list;
513
+ int has_mask;
514
+ } ca_store_all_ctx_t;
515
+
516
+ static const char *
517
+ ca_store_all_mode (CArray *cv)
518
+ {
519
+ return ( cv->data_type == CA_OBJECT ) ? "w" : "a";
520
+ }
521
+
522
+ /* The window has attached ca's mask, and the one created here joins it,
523
+ so the mask is written in its buffer. */
524
+ static VALUE
525
+ ca_store_all_cast_body (VALUE arg)
526
+ {
527
+ ca_store_all_ctx_t *c = (ca_store_all_ctx_t *) arg;
528
+ CArray *ca = c->ca;
529
+ if ( c->mscratch ) {
530
+ if ( ! ca->mask ) {
531
+ ca_create_mask(ca);
532
+ }
533
+ memcpy(ca->mask->ptr, c->mscratch, ca->elements);
534
+ }
535
+ else if ( ca->mask ) {
536
+ memset(ca->mask->ptr, 0, ca->elements);
537
+ }
538
+ if ( ca->mask ) {
539
+ ca_cast_block_with_mask(ca->elements, c->cv, c->scratch, ca, ca->ptr,
540
+ (boolean8_t*)ca->mask->ptr);
541
+ }
542
+ else {
543
+ ca_cast_block(ca->elements, c->cv, c->scratch, ca, ca->ptr);
544
+ }
545
+ return Qnil;
546
+ }
547
+
548
+ static VALUE
549
+ ca_store_all_list_body (VALUE arg)
550
+ {
551
+ ca_store_all_ctx_t *c = (ca_store_all_ctx_t *) arg;
552
+ CArray *ca = c->ca;
553
+ VALUE list = c->list;
554
+ ca_size_t i;
555
+ /* Face has surface != storage; the cast runs in the storage
556
+ data_type. Passing ca directly makes the cast table see FIXLEN
557
+ and report non-implemented, so pass a shadow CArray (storage
558
+ data_type, ptr aliased to ca). */
559
+ CArray shadow;
560
+ CArray *cast_target = ca;
561
+ memset(&shadow, 0, sizeof(CArray));
562
+ if ( ca_is_face(ca) ) {
563
+ CArray *root = ca_strip_face(ca);
564
+ if (root) {
565
+ shadow.data_type = root->data_type;
566
+ shadow.bytes = ca->bytes;
567
+ shadow.elements = ca->elements;
568
+ shadow.ptr = ca->ptr;
569
+ cast_target = &shadow;
570
+ }
571
+ }
572
+ if ( c->has_mask ) {
573
+ boolean8_t *m;
574
+ m = (boolean8_t *)ca->mask->ptr;
575
+ for (i=0; i<ca->elements; i++) {
576
+ if ( rb_ary_entry(list,i) == CA_UNDEF ) {
577
+ *m = 1;
578
+ }
579
+ else {
580
+ *m = 0;
581
+ }
582
+ m++;
583
+ }
584
+ ca_cast_block_with_mask(ca->elements, c->ico, (VALUE *)RARRAY_CONST_PTR(list),
585
+ cast_target, ca->ptr,
586
+ (boolean8_t*)ca->mask->ptr);
587
+ }
588
+ else {
589
+ ca_cast_block(ca->elements, c->ico, (VALUE *)RARRAY_CONST_PTR(list), cast_target, ca->ptr);
590
+ }
591
+ return Qnil;
592
+ }
593
+
594
+ /* A Face source stored into an array that is not the same Face. Its cells
595
+ are surface values, and its storage means nothing to the destination: a
596
+ CATime's ticks are not times to an object array, and a categorical's codes
597
+ are not its labels. So an object destination, or a Face that does not
598
+ reconcile a Face source itself (a fixlen string taking a const string),
599
+ receives the values (to_type(:object)), and any other plain destination
600
+ refuses rather than receive storage bytes. A destination of the same Face
601
+ class, or one that reconciles a Face (to_comparable), is left to
602
+ ca_face_convert_store_rval. */
603
+ static VALUE
604
+ ca_face_source_for_store (VALUE self, CArray *ca, VALUE rval)
605
+ {
606
+ CArray *cr;
607
+
608
+ if ( ! rb_obj_is_carray(rval) ) {
609
+ return rval;
610
+ }
611
+ GetCArray(rval, cr);
612
+ if ( ! ca_is_face(cr) ) {
613
+ return rval;
614
+ }
615
+ if ( ca_is_face(ca) && ca->obj_type == CA_OBJ_RECORD ) {
616
+ /* A record is read at its data_class's member offsets, so records of
617
+ another data_class (of the same size or not) are refused; the class
618
+ of the record array (a subclass or CARecord itself) does not matter. */
619
+ if ( cr->obj_type == CA_OBJ_RECORD
620
+ && rb_ca_data_class(self) == rb_ca_data_class(rval) ) {
621
+ return rval;
622
+ }
623
+ if ( cr->obj_type == CA_OBJ_RECORD ) {
624
+ rb_raise(rb_eTypeError,
625
+ "can not store a record array of %"PRIsVALUE
626
+ " into a record array of %"PRIsVALUE,
627
+ rb_ca_data_class(rval), rb_ca_data_class(self));
628
+ }
629
+ rb_raise(rb_eTypeError,
630
+ "can not store a %s array into a record array of %"PRIsVALUE,
631
+ rb_obj_classname(rval), rb_ca_data_class(self));
632
+ }
633
+ if ( ca_is_face(ca) ) {
634
+ if ( rb_obj_class(self) == rb_obj_class(rval)
635
+ || rb_respond_to(self, rb_intern("to_comparable")) ) {
636
+ return rval;
637
+ }
638
+ }
639
+ else if ( ca->data_type != CA_OBJECT ) {
640
+ /* A fixlen string or a record is its bytes: storage and surface are
641
+ the same, so a plain array of that type takes them as they are. A
642
+ const string's bytes are ranges into a pool of its own, and it is the
643
+ read-only Face that says so. */
644
+ CArray *storage = ca_strip_face(cr);
645
+ if ( storage->data_type == cr->data_type && storage->bytes == cr->bytes
646
+ && ! ca_is_readonly(cr) ) {
647
+ return rval;
648
+ }
649
+ rb_raise(rb_eTypeError,
650
+ "can not store a %s array into %s array "
651
+ "(to_type(:object) gives its values)",
652
+ rb_obj_classname(rval), ca_type_name[ca->data_type]);
653
+ }
654
+ return rb_funcall(rval, rb_intern("to_type"), 1, INT2NUM(CA_OBJECT));
655
+ }
656
+
503
657
  VALUE
504
658
  rb_ca_store_all (VALUE self, VALUE rval)
505
659
  {
@@ -516,6 +670,7 @@ rb_ca_store_all (VALUE self, VALUE rval)
516
670
  }
517
671
 
518
672
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
673
+ rval = ca_face_source_for_store(self, ca, rval);
519
674
 
520
675
  retry:
521
676
 
@@ -552,6 +707,17 @@ rb_ca_store_all (VALUE self, VALUE rval)
552
707
  ca_broadcast_to_destination(self, &rval);
553
708
  TypedData_Get_Struct(rval, CArray, &carray_data_type, cv);
554
709
 
710
+ /* Fixlen cells of another width are fitted cell by cell, padded with
711
+ NUL or cut to the destination's width, as a String or an Array of
712
+ Strings stored into fixlen cells is. Copied as they are, the bytes
713
+ would be read past the end of a narrower source. */
714
+ if ( ca->data_type == CA_FIXLEN && cv->data_type == CA_FIXLEN
715
+ && ca->bytes != cv->bytes ) {
716
+ rval = rb_ca_to_type(rb_ca_strip_face_value(rval), INT2NUM(CA_FIXLEN),
717
+ SIZE2NUM(ca->bytes));
718
+ TypedData_Get_Struct(rval, CArray, &carray_data_type, cv);
719
+ }
720
+
555
721
  /* Source delivery via ca_xfer_all into a local scratch instead of
556
722
  ca_attach(cv). This routes through ca_*_func_xfer_all's per-region
557
723
  partial-materialise path, avoiding a ca_attach(root) silent
@@ -564,29 +730,50 @@ rb_ca_store_all (VALUE self, VALUE rval)
564
730
  scales with the view, not the root. (The dst-side ca_sync_data is
565
731
  already an xfer_all PUT.) */
566
732
  {
567
- volatile VALUE scratch_holder;
733
+ volatile VALUE scratch_holder, mscratch_holder = 0;
568
734
  ca_size_t cv_bytes = cv->bytes * cv->elements;
569
735
  char *scratch = ALLOCV_N(char, scratch_holder, cv_bytes);
736
+ boolean8_t *mscratch = NULL;
570
737
  ca_xfer_all(cv, scratch, CA_XFER_GET);
571
738
 
739
+ /* The source's mask is taken now as well, before anything is
740
+ written: the source may read through to the destination's mask
741
+ (`c[] = c.flip(0)`, or a lazy expression over c whose mask is
742
+ built from c's on demand), and the writes below clear it. */
743
+ if ( ca_has_mask(cv) ) {
744
+ mscratch = ALLOCV_N(boolean8_t, mscratch_holder, cv->elements);
745
+ ca_xfer_all(cv->mask, mscratch, CA_XFER_GET);
746
+ }
747
+
572
748
  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);
749
+ ca_store_all_ctx_t c;
750
+ c.ca = ca;
751
+ c.cv = cv;
752
+ c.scratch = scratch;
753
+ c.mscratch = mscratch;
754
+ ca_attach_window(1, &ca, ca_store_all_mode(cv), ca_store_all_cast_body,
755
+ (VALUE) &c);
756
+ }
757
+ else {
758
+ if ( mscratch ) {
759
+ if ( ! ca->mask ) {
760
+ ca_create_mask(ca);
761
+ }
762
+ ca_sync_data(ca->mask, (char *) mscratch);
578
763
  }
579
764
  else {
580
- ca_cast_block(ca->elements, cv, scratch, ca, ca->ptr);
765
+ ca_update_mask(ca);
766
+ if ( ca->mask ) {
767
+ boolean8_t zero = 0;
768
+ ca_fill(ca->mask, &zero);
769
+ }
581
770
  }
582
- ca_sync(ca);
583
- ca_detach(ca);
584
- }
585
- else {
586
- ca_copy_mask_overwrite(ca, ca->elements, 1, cv);
587
771
  ca_sync_data(ca, scratch);
588
772
  }
589
773
 
774
+ if ( mscratch ) {
775
+ ALLOCV_END(mscratch_holder);
776
+ }
590
777
  ALLOCV_END(scratch_holder);
591
778
  }
592
779
  }
@@ -595,7 +782,7 @@ rb_ca_store_all (VALUE self, VALUE rval)
595
782
  rb_ary_flatten_for_elements(rval, ca->elements, ca);
596
783
  ca_size_t i;
597
784
  if ( NIL_P(list) ) {
598
- rb_raise(rb_eRuntimeError,
785
+ rb_raise(rb_eArgError,
599
786
  "failed to guess data size of given array");
600
787
  }
601
788
  else {
@@ -612,54 +799,19 @@ rb_ca_store_all (VALUE self, VALUE rval)
612
799
  break;
613
800
  }
614
801
  }
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);
802
+ /* Every element is replaced, so there would be no need to gather the
803
+ old values -- but converting a Ruby object can raise part way, and
804
+ then the cells already converted are pushed back with the rest
805
+ left as they were, which needs the old values in the buffer. So
806
+ this window attaches (see ca_store_all_mode). */
622
807
  {
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
- }
808
+ ca_store_all_ctx_t c;
809
+ c.ca = ca;
810
+ c.ico = &ico;
811
+ c.list = list;
812
+ c.has_mask = has_mask;
813
+ ca_attach_window(1, &ca, "w", ca_store_all_list_body, (VALUE) &c);
660
814
  }
661
- ca_sync(ca);
662
- ca_detach(ca);
663
815
  }
664
816
  }
665
817
  else if ( rb_respond_to(rval, id_to_ca) ) {
@@ -835,9 +987,8 @@ rb_ca_ref_block (VALUE self, CAIndexInfo *info)
835
987
  data_type to the storage data_type (chain bottom) so the write path
836
988
  casts in the storage type. */
837
989
  if ( ca_is_face(ca) ) {
838
- CArray *blk_ca, *root = ca;
990
+ CArray *blk_ca, *root = ca_strip_face(ca);
839
991
  TypedData_Get_Struct(blk, CArray, &carray_data_type, blk_ca);
840
- while (root && ca_is_face(root)) root = ((CAView *) root)->parent;
841
992
  if (root && root->data_type != blk_ca->data_type) {
842
993
  blk_ca->data_type = root->data_type;
843
994
  }
@@ -894,7 +1045,13 @@ rb_ca_fancy_index_chain (VALUE self, VALUE rmapper)
894
1045
  return rb_ca_remap_new(self, rmapper);
895
1046
  }
896
1047
 
897
- /* (b) Normalize chain fallback. */
1048
+ /* (b) Normalize chain fallback. A Face is flattened through its storage
1049
+ and put on top of the result, so no Face is left in the middle of the
1050
+ chain (CAFace.md 8.3). */
1051
+ if ( ca_is_face(ca) ) {
1052
+ result = rb_ca_fancy_index_chain(rb_ca_strip_face_value(self), rmapper);
1053
+ return ca_face_lift(result, self);
1054
+ }
898
1055
  flat_self = rb_ca_refer_new_flatten(self);
899
1056
  flat_mapper = (cm->ndim == 1) ? rmapper : rb_ca_flatten(rmapper);
900
1057
  grid_argv[0] = flat_mapper;
@@ -1014,8 +1171,20 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
1014
1171
  /* Re-enter against a flattened view of self. rb_ca_refer_new_flatten
1015
1172
  is an internal builder and hands back a bare refer, so a marker
1016
1173
  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.) */
1174
+ at the tail. Put it back on before the re-entry.
1175
+
1176
+ A Face is flattened through its storage and put on top once at the
1177
+ end; re-entering against the flattened Face would build the view on
1178
+ it and leave a Face in the middle of the chain (CAFace.md 8.3). */
1179
+ if ( ca_is_face(ca) ) {
1180
+ volatile VALUE flat =
1181
+ rb_ca_refer_new_flatten(rb_ca_strip_face_value(self));
1182
+ obj = rb_ca_fetch_method(argc, argv, flat);
1183
+ if ( rb_obj_is_kind_of(obj, rb_cCArray) ) {
1184
+ obj = ca_face_lift(obj, self);
1185
+ }
1186
+ return obj;
1187
+ }
1019
1188
  self = rb_ca_refer_new_flatten(self);
1020
1189
  if ( ca_is_lazy_marker(ca) ) {
1021
1190
  extern VALUE rb_ca_lazy_marker_new (VALUE cary);
@@ -1053,8 +1222,10 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
1053
1222
  obj = rb_ca_grid(argc, argv, self);
1054
1223
  break;
1055
1224
  case CA_REG_METHOD_CALL: {
1225
+ /* a[:eq, 0] is a[a.eq(0)]: a public method of the array, as a call
1226
+ written out would reach (a[:puts] does not run Kernel#puts). */
1056
1227
  volatile VALUE idx;
1057
- idx = rb_funcall2(self, SYM2ID(info.symbol), argc-1, argv+1);
1228
+ idx = rb_funcallv_public(self, SYM2ID(info.symbol), argc-1, argv+1);
1058
1229
  obj = rb_ca_fetch(self, idx);
1059
1230
  break;
1060
1231
  }
@@ -1089,11 +1260,8 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
1089
1260
  already-a-Face case (some builders lift their own result) is handled
1090
1261
  inside ca_wrapper_lift.
1091
1262
 
1092
- Every CA_REG_* branch converges here, so this one line covers all of
1093
- them -- but not every index form: `a[:*, nil]` builds a CAUnboundRepeat,
1094
- whose shape stays open until an operand binds it, and a marker copies
1095
- shape at construction. ca_wrapper_lift refuses that one, in one place,
1096
- because `unbound_repeat` reaches it by a second route. */
1263
+ Every CA_REG_* branch converges here, so this one line covers every
1264
+ index form that returns a CArray. */
1097
1265
  CA_WRAPPER_LIFT(obj, self, ca);
1098
1266
 
1099
1267
  return obj;
@@ -1107,6 +1275,8 @@ rb_ca_fetch_newaxis (int argc, VALUE *argv, VALUE self, CArray *ca)
1107
1275
  {
1108
1276
  int i;
1109
1277
  int nclean = 0;
1278
+ int nscalar = 0;
1279
+ int nunder = 0;
1110
1280
  VALUE clean[CA_RANK_MAX];
1111
1281
  volatile VALUE base;
1112
1282
  ca_size_t final_dims[CA_RANK_MAX];
@@ -1118,11 +1288,17 @@ rb_ca_fetch_newaxis (int argc, VALUE *argv, VALUE self, CArray *ca)
1118
1288
  /* Build clean argv (strip :_); reject disjoint sigils + rubber dim. */
1119
1289
  for (i = 0; i < argc; i++) {
1120
1290
  VALUE a = argv[i];
1121
- if ( a == sym_under ) continue;
1122
- if ( a == sym_gt || a == sym_star || a == sym_perc
1291
+ if ( a == sym_under ) {
1292
+ nunder++;
1293
+ continue;
1294
+ }
1295
+ if ( FIXNUM_P(a) || RB_TYPE_P(a, T_BIGNUM) ) {
1296
+ nscalar++;
1297
+ }
1298
+ if ( a == sym_gt || a == sym_perc
1123
1299
  || a == Qfalse || a == sym_tilde ) {
1124
1300
  rb_raise(rb_eIndexError,
1125
- "newaxis (:_) cannot be combined with :>, :*, :%%, "
1301
+ "newaxis (:_) cannot be combined with :>, :%%, "
1126
1302
  "or rubber dim (false / :~)");
1127
1303
  }
1128
1304
  if ( nclean >= CA_RANK_MAX ) {
@@ -1140,18 +1316,26 @@ rb_ca_fetch_newaxis (int argc, VALUE *argv, VALUE self, CArray *ca)
1140
1316
  nclean, (int) ca->ndim);
1141
1317
  }
1142
1318
 
1143
- base = rb_ca_fetch_method(nclean, clean, self);
1144
-
1145
- /* Degenerate: all real axes scalar -> base is a scalar element, not a
1146
- CArray. Out of scope (rare + degenerate); user indexes the element
1147
- and reshapes explicitly. IndexError keeps it catchable + consistent
1148
- with the other newaxis rejects. */
1149
- if ( ! rb_obj_is_kind_of(base, rb_cCArray) ) {
1150
- rb_raise(rb_eIndexError,
1151
- "newaxis (:_) needs at least one non-scalar axis "
1152
- "(all-scalar indexing yields a single element)");
1319
+ /* Every real axis indexed by a scalar: fetching `clean` would yield the
1320
+ element itself, and there would be nothing left to reshape. Ask for a
1321
+ length-1 block on each axis instead, so the base stays a CArray and the
1322
+ result is a view into self. The result carries exactly the axes :_ asked
1323
+ for, all of length 1 -- an operand that states rank nunder and one
1324
+ element, which is what the shape rules then hold it to. */
1325
+ if ( nscalar == nclean ) {
1326
+ VALUE blocks[CA_RANK_MAX];
1327
+ for (i = 0; i < nclean; i++) {
1328
+ blocks[i] = rb_assoc_new(clean[i], INT2FIX(1));
1329
+ }
1330
+ base = rb_ca_fetch_method(nclean, blocks, self);
1331
+ for (i = 0; i < nunder; i++) {
1332
+ dim_argv[i] = INT2FIX(1);
1333
+ }
1334
+ return rb_ca_reshape(nunder, dim_argv, base);
1153
1335
  }
1154
1336
 
1337
+ base = rb_ca_fetch_method(nclean, clean, self);
1338
+
1155
1339
  TypedData_Get_Struct(base, CArray, &carray_data_type, cb);
1156
1340
 
1157
1341
  /* Reconstruct final shape: walk original argv. :_ -> size-1, scalar
@@ -1345,7 +1529,7 @@ rb_cs_fetch_method (int argc, VALUE *argv, VALUE self)
1345
1529
  break;
1346
1530
  case CA_REG_METHOD_CALL: {
1347
1531
  volatile VALUE idx;
1348
- idx = rb_funcall2(self, SYM2ID(info.symbol), argc-1, argv+1);
1532
+ idx = rb_funcallv_public(self, SYM2ID(info.symbol), argc-1, argv+1);
1349
1533
  obj = rb_ca_fetch(self, idx);
1350
1534
  break;
1351
1535
  }
@@ -1390,7 +1574,7 @@ rb_cs_fetch_method (int argc, VALUE *argv, VALUE self)
1390
1574
  of letting the storage cast truncate it, and the bulk path must not be
1391
1575
  the looser door.
1392
1576
  - an Array is mapped structure-preserving (leaves converted),
1393
- - a scalar goes through the write hook (ca_face_scalar_to_storage), which
1577
+ - a scalar goes through the write hook (ca_face_element_to_storage), which
1394
1578
  leaves a bare Integer / String unchanged.
1395
1579
 
1396
1580
  Idempotent: an already-converted Integer passes through, so re-running on
@@ -1401,15 +1585,26 @@ ca_face_convert_store_rval (VALUE self, CArray *ca, VALUE val)
1401
1585
  if ( rb_obj_is_carray(val) ) {
1402
1586
  CArray *cval;
1403
1587
  GetCArray(val, cval);
1588
+ /* An object array holding struct elements (a record array's
1589
+ to_type(:object)) goes in as its values, each through the write
1590
+ hook; masked cells come back as UNDEF and stay masked. */
1591
+ if ( ca->obj_type == CA_OBJ_RECORD && cval->data_type == CA_OBJECT ) {
1592
+ return ca_face_convert_store_rval(self, ca,
1593
+ rb_funcall(val, rb_intern("to_a"), 0));
1594
+ }
1404
1595
  /* Face RHS: reconcile to self's unit and descend to storage. Mirror of
1405
- the scalar path (ca_face_scalar_to_storage) for a bulk CArray source.
1596
+ the scalar path (ca_face_element_to_storage) for a bulk CArray source.
1406
1597
  Guarded on self having to_comparable so a Face without a reconcile
1407
1598
  algebra (e.g. a fixlen-storage Face, whose same-storage store already
1408
1599
  takes the storage xfer branch) is left to the existing store path. */
1409
1600
  if ( ca_is_face(cval) && rb_respond_to(self, rb_intern("to_comparable")) ) {
1410
- volatile VALUE reconciled = rb_funcall(self, rb_intern("to_comparable"),
1411
- 1, val);
1412
- return rb_funcall(reconciled, rb_intern("parent"), 0);
1601
+ return ca_face_reconcile_operand(self, val, "[]=");
1602
+ }
1603
+ /* A Face of the same class and a compatible state holds values of the
1604
+ same kind in the same encoding: it goes in as its storage. */
1605
+ if ( ca_is_face(cval) && rb_obj_class(val) == rb_obj_class(self)
1606
+ && ca_face_state_compatible(self, ca, val, cval) ) {
1607
+ return rb_ca_strip_face_value(val);
1413
1608
  }
1414
1609
  /* Bare storage escape, integer storage: integer cells only (see above). */
1415
1610
  {
@@ -1432,15 +1627,16 @@ ca_face_convert_store_rval (VALUE self, CArray *ca, VALUE val)
1432
1627
  }
1433
1628
  return out;
1434
1629
  }
1435
- return ca_face_scalar_to_storage(self, ca, val);
1630
+ return ca_face_element_to_storage(self, ca, val);
1436
1631
  }
1437
1632
 
1438
1633
  static VALUE
1439
1634
  rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1440
1635
  {
1441
- volatile VALUE obj = Qnil, rval;
1636
+ volatile VALUE obj = Qnil, rval, dest;
1442
1637
  CArray *ca;
1443
1638
  CAIndexInfo info;
1639
+ int converted = 0; /* rval has been through the Face's write hook */
1444
1640
 
1445
1641
  rb_ca_modify(self);
1446
1642
 
@@ -1450,12 +1646,31 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1450
1646
  retry:
1451
1647
 
1452
1648
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1649
+ rval = ca_face_source_for_store(self, ca, rval);
1453
1650
 
1454
1651
  /* Face store: bring a surface value object (Scalar / Time / DateTime) into
1455
1652
  the storage domain while self is still the Face. Store-side views strip
1456
1653
  the Face to storage, so this is the only point that sees it. */
1457
1654
  if ( ca_is_face(ca) ) {
1458
- rval = ca_face_convert_store_rval(self, ca, rval);
1655
+ /* Once: a retry below comes back here with rval already converted. */
1656
+ if ( ! converted ) {
1657
+ rval = ca_face_convert_store_rval(self, ca, rval);
1658
+ converted = 1;
1659
+ }
1660
+ /* A CArray source is storage now, so the destination has to be too.
1661
+ A block store descends on its own (its view is built on the storage);
1662
+ a whole, boolean or address store kept the Face and cast the storage
1663
+ to the Face's surface type, which an int64 tick cannot become. A Face
1664
+ whose surface type is its storage's (a string or record Face) needs
1665
+ no descent, and keeps its own index forms (a record's field name). */
1666
+ if ( rb_obj_is_carray(rval) ) {
1667
+ CArray *cr;
1668
+ GetCArray(rval, cr);
1669
+ if ( ! ca_is_face(cr) && ca_strip_face(ca)->data_type != ca->data_type ) {
1670
+ self = rb_ca_strip_face_value(self);
1671
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1672
+ }
1673
+ }
1459
1674
  }
1460
1675
 
1461
1676
  /* newaxis (:_) interception on store. Build the newaxis view (a
@@ -1479,31 +1694,40 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1479
1694
  info.range_check = 1;
1480
1695
  rb_ca_scan_index(ca->ndim, ca->dim, ca->elements, argc, argv, &info);
1481
1696
 
1697
+ /* A value or a Ruby Array converted above is in the storage domain now.
1698
+ Stored through the Face it would meet the write hook a second time
1699
+ (rb_ca_obj2ptr converts too), so a positional store goes into the
1700
+ storage, which has the Face's shape. A predicate (:sym) is still
1701
+ asked of the Face; a member store keeps the Face for its data_class. */
1702
+ dest = self;
1703
+ if ( converted && ca_is_face(ca) && ! rb_obj_is_carray(rval) ) {
1704
+ dest = rb_ca_strip_face_value(self);
1705
+ }
1706
+
1482
1707
  switch ( info.type ) {
1483
1708
  case CA_REG_ADDRESS_COMPLEX:
1484
1709
  self = rb_ca_refer_new_flatten(self);
1485
1710
  goto retry;
1486
1711
  case CA_REG_ADDRESS:
1487
- obj = rb_ca_store_address(self, &info, rval);
1712
+ obj = rb_ca_store_address(dest, &info, rval);
1488
1713
  break;
1489
1714
  case CA_REG_FLATTEN:
1490
- self = rb_ca_refer_new_flatten(self);
1491
- obj = rb_ca_store_all(self, rval);
1715
+ obj = rb_ca_store_all(rb_ca_refer_new_flatten(dest), rval);
1492
1716
  break;
1493
1717
  case CA_REG_POINT:
1494
- obj = rb_ca_store_point(self, &info, rval);
1718
+ obj = rb_ca_store_point(dest, &info, rval);
1495
1719
  break;
1496
1720
  case CA_REG_ALL:
1497
- obj = rb_ca_store_all(self, rval);
1721
+ obj = rb_ca_store_all(dest, rval);
1498
1722
  break;
1499
1723
  case CA_REG_BLOCK: {
1500
1724
  volatile VALUE block;
1501
- block = rb_ca_ref_block(self, &info);
1725
+ block = rb_ca_ref_block(dest, &info);
1502
1726
  obj = rb_ca_store_all(block, rval);
1503
1727
  break;
1504
1728
  }
1505
1729
  case CA_REG_SELECT: {
1506
- obj = rb_ca_select_new(self, argv[0]);
1730
+ obj = rb_ca_select_new(dest, argv[0]);
1507
1731
  obj = rb_ca_store_all(obj, rval);
1508
1732
  break;
1509
1733
  }
@@ -1516,27 +1740,30 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1516
1740
  break;
1517
1741
  }
1518
1742
  case CA_REG_REPEAT: {
1519
- obj = rb_ca_repeat(argc, argv, self);
1743
+ obj = rb_ca_repeat(argc, argv, dest);
1520
1744
  obj = rb_ca_store_all(obj, rval);
1521
1745
  break;
1522
1746
  }
1523
1747
  case CA_REG_MAPPING: {
1524
- obj = rb_ca_fancy_index_chain(self, argv[0]);
1748
+ obj = rb_ca_fancy_index_chain(dest, argv[0]);
1525
1749
  obj = rb_ca_store_all(obj, rval);
1526
1750
  break;
1527
1751
  }
1528
1752
  case CA_REG_GRID: {
1529
- obj = rb_ca_grid(argc, argv, self);
1753
+ obj = rb_ca_grid(argc, argv, dest);
1530
1754
  obj = rb_ca_store_all(obj, rval);
1531
1755
  break;
1532
1756
  }
1533
1757
  case CA_REG_METHOD_CALL: {
1534
- volatile VALUE idx;
1758
+ ca_store_method_call_ctx_t c;
1535
1759
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1536
- ca_attach(ca);
1537
- idx = rb_funcall2(self, SYM2ID(info.symbol), (int)(argc-1), argv+1);
1538
- obj = rb_ca_store(self, idx, rval);
1539
- ca_detach(ca);
1760
+ c.self = self;
1761
+ c.dest = dest;
1762
+ c.mid = SYM2ID(info.symbol);
1763
+ c.argc = (int)(argc-1);
1764
+ c.argv = argv+1;
1765
+ c.rval = rval;
1766
+ obj = ca_attach_window(1, &ca, "r", ca_store_method_call_body, (VALUE) &c);
1540
1767
  break;
1541
1768
  }
1542
1769
  case CA_REG_MEMBER: {
@@ -1614,15 +1841,13 @@ rb_ca_s_scan_index (VALUE self, VALUE rdim, VALUE ridx)
1614
1841
  Check_Type(rdim, T_ARRAY);
1615
1842
  Check_Type(ridx, T_ARRAY);
1616
1843
 
1617
- elements = 1;
1618
1844
  ndim = (int) RARRAY_LEN(rdim);
1845
+ CA_CHECK_RANK(ndim);
1619
1846
  for (i=0; i<ndim; i++) {
1620
1847
  dim[i] = NUM2SIZE(rb_ary_entry(rdim, i));
1621
- elements *= dim[i];
1622
1848
  }
1623
-
1624
- CA_CHECK_RANK(ndim);
1625
1849
  CA_CHECK_DIM(ndim, dim);
1850
+ elements = ca_shape_elements(ndim, dim, 1);
1626
1851
 
1627
1852
  info.range_check = 1;
1628
1853
  rb_ca_scan_index(ndim, dim, elements,
@@ -1686,7 +1911,7 @@ rb_ca_s_scan_index (VALUE self, VALUE rdim, VALUE ridx)
1686
1911
  info.index[i].symbol.spec));
1687
1912
  break;
1688
1913
  default:
1689
- rb_raise(rb_eRuntimeError, "unknown index spec");
1914
+ rb_raise(rb_eIndexError, "unknown index spec");
1690
1915
  }
1691
1916
  }
1692
1917
  break;
@@ -1754,7 +1979,7 @@ rb_ca_normalize_index (VALUE self, VALUE ridx)
1754
1979
  rb_ary_store(rindex, i, ID2SYM(info.index[i].symbol.id));
1755
1980
  break;
1756
1981
  default:
1757
- rb_raise(rb_eRuntimeError, "unknown index spec");
1982
+ rb_raise(rb_eIndexError, "unknown index spec");
1758
1983
  }
1759
1984
  }
1760
1985
  return rindex;
@@ -1801,8 +2026,8 @@ parse_shape_kwarg (VALUE rshape, int *out_ndim, ca_size_t *dim,
1801
2026
  rb_raise(rb_eArgError, "negative dim %" PRId64 " at axis %d",
1802
2027
  (ca_size_t) dim[i], i);
1803
2028
  }
1804
- elements *= dim[i];
1805
2029
  }
2030
+ elements = ca_shape_elements(ndim, dim, 1);
1806
2031
  *out_ndim = ndim;
1807
2032
  *out_elements = elements;
1808
2033
  }
@@ -1816,12 +2041,7 @@ addr2index_do (int ndim, ca_size_t *dim, ca_size_t elements, VALUE raddr)
1816
2041
  /* Scalar path (Integer input): returns Ruby Array of N Integers.
1817
2042
  * Preserves legacy shape for callers like `i, j = ca.addr2index(k)`. */
1818
2043
  if ( rb_obj_is_kind_of(raddr, rb_cInteger) ) {
1819
- ca_size_t addr = NUM2SIZE(raddr);
1820
- if ( addr < 0 || addr >= elements ) {
1821
- rb_raise(rb_eArgError,
1822
- "address %" PRId64 " is out of range (0..%" PRId64 ")",
1823
- (ca_size_t) addr, (ca_size_t) (elements - 1));
1824
- }
2044
+ ca_size_t addr = ca_index_in_range(NUM2SIZE(raddr), elements, "address");
1825
2045
  out = rb_ary_new2(ndim);
1826
2046
  for (i = ndim - 1; i >= 0; i--) {
1827
2047
  rb_ary_store(out, i, SIZE2NUM(addr % dim[i]));
@@ -1839,8 +2059,9 @@ addr2index_do (int ndim, ca_size_t *dim, ca_size_t elements, VALUE raddr)
1839
2059
  ca_size_t j, n;
1840
2060
  volatile VALUE objs[CA_RANK_MAX];
1841
2061
 
2062
+ ca_check_index_array(raddr);
1842
2063
  cin = ca_wrap_readonly(raddr, CA_SIZE);
1843
- ca_attach(cin);
2064
+ ca_attach(cin); /* window: detached before its one raise */
1844
2065
 
1845
2066
  out = rb_ary_new2(ndim);
1846
2067
  for (i = 0; i < ndim; i++) {
@@ -1869,12 +2090,11 @@ addr2index_do (int ndim, ca_size_t *dim, ca_size_t elements, VALUE raddr)
1869
2090
  continue;
1870
2091
  }
1871
2092
  addr = src[j];
1872
- if ( addr < 0 || addr >= elements ) {
2093
+ if ( addr < -elements || addr >= elements ) {
1873
2094
  ca_detach(cin);
1874
- rb_raise(rb_eArgError,
1875
- "address %" PRId64 " is out of range (0..%" PRId64 ")",
1876
- (ca_size_t) addr, (ca_size_t) (elements - 1));
2095
+ ca_index_out_of_range(addr, elements, "address");
1877
2096
  }
2097
+ addr = ca_index_in_range(addr, elements, "address");
1878
2098
  for (k = ndim - 1; k >= 0; k--) {
1879
2099
  p_out[k][j] = addr % dim[k];
1880
2100
  addr /= dim[k];
@@ -1887,14 +2107,87 @@ addr2index_do (int ndim, ca_size_t *dim, ca_size_t elements, VALUE raddr)
1887
2107
  }
1888
2108
  }
1889
2109
 
2110
+ /* The vector path of index2addr reads every index array inside one
2111
+ window: an index out of range raises part way through. */
2112
+
2113
+ typedef struct {
2114
+ int ndim;
2115
+ ca_size_t *dim;
2116
+ CArray **cidx;
2117
+ int out_ndim;
2118
+ ca_size_t *out_dim;
2119
+ ca_size_t out_elements;
2120
+ } ca_index2addr_ctx_t;
2121
+
1890
2122
  static VALUE
1891
- index2addr_do (int ndim, ca_size_t *dim, int argc, VALUE *argv)
2123
+ ca_index2addr_body (VALUE arg)
1892
2124
  {
2125
+ ca_index2addr_ctx_t *c = (ca_index2addr_ctx_t *) arg;
2126
+ int ndim = c->ndim;
2127
+ ca_size_t *dim = c->dim;
2128
+ CArray **cidx = c->cidx;
2129
+ ca_size_t out_elements = c->out_elements;
1893
2130
  volatile VALUE obj;
1894
- CArray *co, *cidx[CA_RANK_MAX];
2131
+ CArray *co;
1895
2132
  ca_size_t *q, *p[CA_RANK_MAX], s[CA_RANK_MAX];
1896
2133
  ca_size_t addr, k, n;
1897
2134
  boolean8_t *m;
2135
+ int i;
2136
+
2137
+ for (i = 0; i < ndim; i++) {
2138
+ ca_set_iterator(1, cidx[i], &p[i], &s[i]);
2139
+ }
2140
+
2141
+ obj = rb_carray_new(CA_SIZE, c->out_ndim, c->out_dim, 0, NULL);
2142
+ TypedData_Get_Struct(obj, CArray, &carray_data_type, co);
2143
+
2144
+ q = (ca_size_t *) co->ptr;
2145
+
2146
+ ca_copy_mask_overwrite_n(co, out_elements, ndim, cidx);
2147
+ m = ( co->mask ) ? (boolean8_t *) co->mask->ptr : NULL;
2148
+
2149
+ if ( m ) {
2150
+ n = out_elements;
2151
+ while ( n-- ) {
2152
+ if ( ! *m ) {
2153
+ addr = 0;
2154
+ for (i = 0; i < ndim; i++) {
2155
+ k = *(p[i]);
2156
+ p[i] += s[i];
2157
+ CA_CHECK_INDEX(k, dim[i]);
2158
+ addr = dim[i] * addr + k;
2159
+ }
2160
+ *q = addr;
2161
+ }
2162
+ else {
2163
+ for (i = 0; i < ndim; i++) { p[i] += s[i]; }
2164
+ }
2165
+ m++; q++;
2166
+ }
2167
+ }
2168
+ else {
2169
+ n = out_elements;
2170
+ while ( n-- ) {
2171
+ addr = 0;
2172
+ for (i = 0; i < ndim; i++) {
2173
+ k = *(p[i]);
2174
+ p[i] += s[i];
2175
+ CA_CHECK_INDEX(k, dim[i]);
2176
+ addr = dim[i] * addr + k;
2177
+ }
2178
+ *q = addr;
2179
+ q++;
2180
+ }
2181
+ }
2182
+
2183
+ return obj;
2184
+ }
2185
+
2186
+ static VALUE
2187
+ index2addr_do (int ndim, ca_size_t *dim, int argc, VALUE *argv)
2188
+ {
2189
+ CArray *cidx[CA_RANK_MAX];
2190
+ ca_size_t addr, k;
1898
2191
  int i, all_number = 1;
1899
2192
  int out_ndim = 1;
1900
2193
  ca_size_t out_dim[CA_RANK_MAX];
@@ -1928,6 +2221,7 @@ index2addr_do (int ndim, ca_size_t *dim, int argc, VALUE *argv)
1928
2221
  * the first non-scalar CArray input; other non-scalar inputs must
1929
2222
  * match that shape. */
1930
2223
  for (i = 0; i < ndim; i++) {
2224
+ ca_check_index_array(argv[i]);
1931
2225
  cidx[i] = ca_wrap_readonly(argv[i], CA_SIZE);
1932
2226
  if ( ! ca_is_scalar(cidx[i]) ) {
1933
2227
  if ( shape_from < 0 ) {
@@ -1956,56 +2250,19 @@ index2addr_do (int ndim, ca_size_t *dim, int argc, VALUE *argv)
1956
2250
  }
1957
2251
  }
1958
2252
 
1959
- for (i = 0; i < ndim; i++) {
1960
- ca_attach(cidx[i]);
1961
- ca_set_iterator(1, cidx[i], &p[i], &s[i]);
1962
- }
1963
-
1964
- obj = rb_carray_new(CA_SIZE, out_ndim, out_dim, 0, NULL);
1965
- TypedData_Get_Struct(obj, CArray, &carray_data_type, co);
1966
-
1967
- q = (ca_size_t *) co->ptr;
1968
-
1969
- ca_copy_mask_overwrite_n(co, out_elements, ndim, cidx);
1970
- m = ( co->mask ) ? (boolean8_t *) co->mask->ptr : NULL;
1971
-
1972
- if ( m ) {
1973
- n = out_elements;
1974
- while ( n-- ) {
1975
- if ( ! *m ) {
1976
- addr = 0;
1977
- for (i = 0; i < ndim; i++) {
1978
- k = *(p[i]);
1979
- p[i] += s[i];
1980
- CA_CHECK_INDEX(k, dim[i]);
1981
- addr = dim[i] * addr + k;
1982
- }
1983
- *q = addr;
1984
- }
1985
- else {
1986
- for (i = 0; i < ndim; i++) { p[i] += s[i]; }
1987
- }
1988
- m++; q++;
1989
- }
1990
- }
1991
- else {
1992
- n = out_elements;
1993
- while ( n-- ) {
1994
- addr = 0;
1995
- for (i = 0; i < ndim; i++) {
1996
- k = *(p[i]);
1997
- p[i] += s[i];
1998
- CA_CHECK_INDEX(k, dim[i]);
1999
- addr = dim[i] * addr + k;
2000
- }
2001
- *q = addr;
2002
- q++;
2003
- }
2253
+ {
2254
+ ca_index2addr_ctx_t c;
2255
+ char modes[CA_RANK_MAX + 1];
2256
+ memset(modes, 'r', (size_t) ndim);
2257
+ modes[ndim] = '\0';
2258
+ c.ndim = ndim;
2259
+ c.dim = dim;
2260
+ c.cidx = cidx;
2261
+ c.out_ndim = out_ndim;
2262
+ c.out_dim = out_dim;
2263
+ c.out_elements = out_elements;
2264
+ return ca_attach_window(ndim, cidx, modes, ca_index2addr_body, (VALUE) &c);
2004
2265
  }
2005
-
2006
- for (i = 0; i < ndim; i++) { ca_detach(cidx[i]); }
2007
-
2008
- return obj;
2009
2266
  }
2010
2267
 
2011
2268
  VALUE
@@ -2076,7 +2333,6 @@ Init_carray_access (void)
2076
2333
  id_excl_end = rb_intern("exclude_end?");
2077
2334
 
2078
2335
  id_to_ca = rb_intern("to_ca");
2079
- sym_star = ID2SYM(rb_intern("*"));
2080
2336
  sym_perc = ID2SYM(rb_intern("%"));
2081
2337
  sym_under = ID2SYM(rb_intern("_"));
2082
2338
  sym_gt = ID2SYM(rb_intern(">"));