carray 3.0.2 → 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 (218) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +304 -0
  3. data/README.md +3 -4
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +225 -0
  6. data/ext/ca_axis_group.c +177 -99
  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 +66 -71
  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 +663 -214
  14. data/ext/ca_kernel_iterator.h +231 -167
  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 +132 -22
  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 +1 -0
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +96 -25
  25. data/ext/ca_obj_face.c +406 -64
  26. data/ext/ca_obj_face.h +92 -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 +21 -21
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +189 -107
  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 +543 -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 +47 -49
  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 -118
  46. data/ext/ca_obj_stride.c +195 -34
  47. data/ext/ca_obj_string.c +17 -18
  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 +149 -27
  54. data/ext/ca_op_ipower.c +24 -11
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_sort_kernels.h +70 -0
  58. data/ext/ca_sweep_engine.c +102 -79
  59. data/ext/ca_sweep_engine.h +7 -20
  60. data/ext/ca_triop_dispatch.c +4 -1
  61. data/ext/ca_triop_dispatch.h +5 -2
  62. data/ext/caframe_csv_reader.c +463 -0
  63. data/ext/carray.h +70 -27
  64. data/ext/carray_access.c +455 -211
  65. data/ext/carray_address_basis.c +177 -36
  66. data/ext/carray_attribute.c +45 -3
  67. data/ext/carray_bincount.c +32 -2
  68. data/ext/carray_broadcast.c +46 -14
  69. data/ext/carray_call_cfunc.c +0 -28
  70. data/ext/carray_cast.c +1444 -209
  71. data/ext/carray_cast_func.rb +34 -21
  72. data/ext/carray_conversion.c +197 -73
  73. data/ext/carray_copy.c +32 -11
  74. data/ext/carray_core.c +514 -122
  75. data/ext/carray_count.c +140 -87
  76. data/ext/carray_element.c +18 -27
  77. data/ext/carray_factorize.c +569 -277
  78. data/ext/carray_generate.c +76 -40
  79. data/ext/carray_histogram.c +152 -94
  80. data/ext/carray_hold.c +2 -1
  81. data/ext/carray_index_classifier.c +63 -96
  82. data/ext/carray_internal.h +385 -3
  83. data/ext/carray_lazy.c +834 -79
  84. data/ext/carray_loop.c +81 -65
  85. data/ext/carray_mask.c +682 -316
  86. data/ext/carray_math_kernel.h +1 -0
  87. data/ext/carray_median_percentile.c +221 -76
  88. data/ext/carray_memory_view.c +254 -248
  89. data/ext/carray_operator.c +1089 -359
  90. data/ext/carray_order.c +133 -69
  91. data/ext/carray_partition.c +102 -56
  92. data/ext/carray_random.c +273 -239
  93. data/ext/carray_scatter.c +308 -65
  94. data/ext/carray_segment.c +257 -0
  95. data/ext/carray_slab.c +170 -70
  96. data/ext/carray_sort.c +215 -218
  97. data/ext/carray_sort_kernel.c +8 -1
  98. data/ext/carray_struct.c +99 -21
  99. data/ext/carray_test.c +167 -64
  100. data/ext/carray_time_text.c +1182 -0
  101. data/ext/carray_utils.c +363 -98
  102. data/ext/extconf.rb +27 -2
  103. data/ext/mk_call_cfunc.rb +0 -2
  104. data/ext/mk_pow5_table.rb +35 -0
  105. data/ext/mkkernel.rb +1076 -846
  106. data/ext/ruby_carray.c +7 -1
  107. data/ext/version.h +4 -4
  108. data/ext/version.rb +4 -2
  109. data/lib/carray/attribute.rb +39 -0
  110. data/lib/carray/autoload_carray.rb +19 -0
  111. data/lib/carray/autoload_method_extension.rb +6 -0
  112. data/lib/carray/axis_group.rb +174 -26
  113. data/lib/carray/basics.rb +91 -25
  114. data/lib/carray/bincount_nd.rb +6 -0
  115. data/lib/carray/block_iterator.rb +118 -48
  116. data/lib/carray/boolean_reduce.rb +21 -29
  117. data/lib/carray/categorical.rb +94 -26
  118. data/lib/carray/categorical_iterator.rb +333 -826
  119. data/lib/carray/complex.rb +36 -29
  120. data/lib/carray/conditional.rb +94 -7
  121. data/lib/carray/const_string.rb +46 -0
  122. data/lib/carray/frame/concat.rb +157 -6
  123. data/lib/carray/frame/convert.rb +13 -7
  124. data/lib/carray/frame/csv_parser.rb +589 -82
  125. data/lib/carray/frame/describe.rb +79 -0
  126. data/lib/carray/frame/frame.rb +124 -25
  127. data/lib/carray/frame/group.rb +189 -63
  128. data/lib/carray/frame/io.rb +412 -72
  129. data/lib/carray/frame/join.rb +134 -46
  130. data/lib/carray/frame/records.rb +37 -16
  131. data/lib/carray/frame/reshape.rb +448 -0
  132. data/lib/carray/frame/sort.rb +26 -2
  133. data/lib/carray/frame/verbs.rb +715 -48
  134. data/lib/carray/frame.rb +2 -0
  135. data/lib/carray/fuse_source.rb +111 -2
  136. data/lib/carray/fusion.rb +229 -38
  137. data/lib/carray/inspect.rb +29 -11
  138. data/lib/carray/iterator.rb +14 -2
  139. data/lib/carray/lazy.rb +54 -30
  140. data/lib/carray/mask_gap_fill.rb +66 -52
  141. data/lib/carray/math.rb +32 -19
  142. data/lib/carray/meld_reduce.rb +40 -7
  143. data/lib/carray/methods/align_addr.rb +8 -4
  144. data/lib/carray/methods/bincount.rb +2 -2
  145. data/lib/carray/methods/bit_string.rb +14 -3
  146. data/lib/carray/methods/composition.rb +16 -10
  147. data/lib/carray/methods/factorize.rb +2 -2
  148. data/lib/carray/methods/gather_nd.rb +13 -2
  149. data/lib/carray/methods/insert_block.rb +9 -14
  150. data/lib/carray/methods/is_in.rb +117 -4
  151. data/lib/carray/methods/join.rb +1 -1
  152. data/lib/carray/methods/locate_addr.rb +177 -36
  153. data/lib/carray/methods/mode.rb +18 -1
  154. data/lib/carray/methods/repeat.rb +1 -20
  155. data/lib/carray/methods/resize.rb +21 -23
  156. data/lib/carray/methods/snap.rb +34 -12
  157. data/lib/carray/methods/unique.rb +11 -2
  158. data/lib/carray/record_order.rb +208 -0
  159. data/lib/carray/runtime.rb +14 -11
  160. data/lib/carray/segment_iterator.rb +990 -0
  161. data/lib/carray/serialize.rb +197 -27
  162. data/lib/carray/slab_iterator.rb +51 -27
  163. data/lib/carray/stack.rb +10 -4
  164. data/lib/carray/string_operation_extension.rb +4 -3
  165. data/lib/carray/struct.rb +142 -51
  166. data/lib/carray/struct_builder.rb +110 -14
  167. data/lib/carray/table.rb +2 -2
  168. data/lib/carray/time.rb +517 -103
  169. data/lib/carray/window_iterator.rb +127 -72
  170. data/lib/carray.rb +2 -2
  171. data/yard-stubs/ca_obj_array.rb +4 -4
  172. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  173. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  174. data/yard-stubs/ca_obj_fake.rb +1 -1
  175. data/yard-stubs/ca_obj_field.rb +1 -1
  176. data/yard-stubs/ca_obj_refer.rb +3 -3
  177. data/yard-stubs/ca_obj_roll.rb +0 -1
  178. data/yard-stubs/ca_obj_stride.rb +10 -7
  179. data/yard-stubs/ca_obj_transpose.rb +1 -1
  180. data/yard-stubs/ca_obj_window.rb +1 -2
  181. data/yard-stubs/carray_access.rb +5 -4
  182. data/yard-stubs/carray_broadcast.rb +1 -1
  183. data/yard-stubs/carray_cast.rb +78 -43
  184. data/yard-stubs/carray_conversion.rb +13 -1
  185. data/yard-stubs/carray_copy.rb +8 -3
  186. data/yard-stubs/carray_core.rb +3 -3
  187. data/yard-stubs/carray_count.rb +9 -0
  188. data/yard-stubs/carray_lazy.rb +5 -1
  189. data/yard-stubs/carray_loop.rb +4 -1
  190. data/yard-stubs/carray_mask.rb +34 -10
  191. data/yard-stubs/carray_math.rb +25 -13
  192. data/yard-stubs/carray_median_percentile.rb +10 -6
  193. data/yard-stubs/carray_order.rb +34 -13
  194. data/yard-stubs/carray_scatter.rb +10 -3
  195. data/yard-stubs/carray_segment.rb +72 -0
  196. data/yard-stubs/carray_slab.rb +2 -1
  197. data/yard-stubs/carray_sort.rb +43 -3
  198. data/yard-stubs/carray_test.rb +10 -0
  199. data/yard-stubs/carray_utils.rb +0 -26
  200. metadata +11 -19
  201. data/ext/ca_for_each_element.h +0 -239
  202. data/ext/carray_build_flags.h +0 -3
  203. data/ext/carray_config.h +0 -28
  204. data/ext/carray_kernels_bincmp.c +0 -4446
  205. data/ext/carray_kernels_binop.c +0 -11001
  206. data/ext/carray_kernels_init.c +0 -1131
  207. data/ext/carray_kernels_map.c +0 -3467
  208. data/ext/carray_kernels_moncmp.c +0 -2097
  209. data/ext/carray_kernels_monop.c +0 -18313
  210. data/ext/carray_kernels_reduce_aggregate.c +0 -26005
  211. data/ext/carray_kernels_reduce_boolean.c +0 -330
  212. data/ext/carray_kernels_reduce_cumulative.c +0 -14862
  213. data/ext/carray_kernels_reduce_extreme.c +0 -17494
  214. data/ext/carray_kernels_reduce_variance.c +0 -3910
  215. data/ext/carray_kernels_scan.c +0 -3693
  216. data/ext/carray_kernels_search.c +0 -32218
  217. data/ext/carray_kernels_sort.c +0 -10626
  218. data/ext/carray_kernels_triop.c +0 -1392
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
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
  }
@@ -1358,7 +1529,7 @@ rb_cs_fetch_method (int argc, VALUE *argv, VALUE self)
1358
1529
  break;
1359
1530
  case CA_REG_METHOD_CALL: {
1360
1531
  volatile VALUE idx;
1361
- idx = rb_funcall2(self, SYM2ID(info.symbol), argc-1, argv+1);
1532
+ idx = rb_funcallv_public(self, SYM2ID(info.symbol), argc-1, argv+1);
1362
1533
  obj = rb_ca_fetch(self, idx);
1363
1534
  break;
1364
1535
  }
@@ -1403,7 +1574,7 @@ rb_cs_fetch_method (int argc, VALUE *argv, VALUE self)
1403
1574
  of letting the storage cast truncate it, and the bulk path must not be
1404
1575
  the looser door.
1405
1576
  - an Array is mapped structure-preserving (leaves converted),
1406
- - 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
1407
1578
  leaves a bare Integer / String unchanged.
1408
1579
 
1409
1580
  Idempotent: an already-converted Integer passes through, so re-running on
@@ -1414,15 +1585,26 @@ ca_face_convert_store_rval (VALUE self, CArray *ca, VALUE val)
1414
1585
  if ( rb_obj_is_carray(val) ) {
1415
1586
  CArray *cval;
1416
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
+ }
1417
1595
  /* 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.
1596
+ the scalar path (ca_face_element_to_storage) for a bulk CArray source.
1419
1597
  Guarded on self having to_comparable so a Face without a reconcile
1420
1598
  algebra (e.g. a fixlen-storage Face, whose same-storage store already
1421
1599
  takes the storage xfer branch) is left to the existing store path. */
1422
1600
  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);
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);
1426
1608
  }
1427
1609
  /* Bare storage escape, integer storage: integer cells only (see above). */
1428
1610
  {
@@ -1445,15 +1627,16 @@ ca_face_convert_store_rval (VALUE self, CArray *ca, VALUE val)
1445
1627
  }
1446
1628
  return out;
1447
1629
  }
1448
- return ca_face_scalar_to_storage(self, ca, val);
1630
+ return ca_face_element_to_storage(self, ca, val);
1449
1631
  }
1450
1632
 
1451
1633
  static VALUE
1452
1634
  rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1453
1635
  {
1454
- volatile VALUE obj = Qnil, rval;
1636
+ volatile VALUE obj = Qnil, rval, dest;
1455
1637
  CArray *ca;
1456
1638
  CAIndexInfo info;
1639
+ int converted = 0; /* rval has been through the Face's write hook */
1457
1640
 
1458
1641
  rb_ca_modify(self);
1459
1642
 
@@ -1463,12 +1646,31 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1463
1646
  retry:
1464
1647
 
1465
1648
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1649
+ rval = ca_face_source_for_store(self, ca, rval);
1466
1650
 
1467
1651
  /* Face store: bring a surface value object (Scalar / Time / DateTime) into
1468
1652
  the storage domain while self is still the Face. Store-side views strip
1469
1653
  the Face to storage, so this is the only point that sees it. */
1470
1654
  if ( ca_is_face(ca) ) {
1471
- 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
+ }
1472
1674
  }
1473
1675
 
1474
1676
  /* newaxis (:_) interception on store. Build the newaxis view (a
@@ -1492,31 +1694,40 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1492
1694
  info.range_check = 1;
1493
1695
  rb_ca_scan_index(ca->ndim, ca->dim, ca->elements, argc, argv, &info);
1494
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
+
1495
1707
  switch ( info.type ) {
1496
1708
  case CA_REG_ADDRESS_COMPLEX:
1497
1709
  self = rb_ca_refer_new_flatten(self);
1498
1710
  goto retry;
1499
1711
  case CA_REG_ADDRESS:
1500
- obj = rb_ca_store_address(self, &info, rval);
1712
+ obj = rb_ca_store_address(dest, &info, rval);
1501
1713
  break;
1502
1714
  case CA_REG_FLATTEN:
1503
- self = rb_ca_refer_new_flatten(self);
1504
- obj = rb_ca_store_all(self, rval);
1715
+ obj = rb_ca_store_all(rb_ca_refer_new_flatten(dest), rval);
1505
1716
  break;
1506
1717
  case CA_REG_POINT:
1507
- obj = rb_ca_store_point(self, &info, rval);
1718
+ obj = rb_ca_store_point(dest, &info, rval);
1508
1719
  break;
1509
1720
  case CA_REG_ALL:
1510
- obj = rb_ca_store_all(self, rval);
1721
+ obj = rb_ca_store_all(dest, rval);
1511
1722
  break;
1512
1723
  case CA_REG_BLOCK: {
1513
1724
  volatile VALUE block;
1514
- block = rb_ca_ref_block(self, &info);
1725
+ block = rb_ca_ref_block(dest, &info);
1515
1726
  obj = rb_ca_store_all(block, rval);
1516
1727
  break;
1517
1728
  }
1518
1729
  case CA_REG_SELECT: {
1519
- obj = rb_ca_select_new(self, argv[0]);
1730
+ obj = rb_ca_select_new(dest, argv[0]);
1520
1731
  obj = rb_ca_store_all(obj, rval);
1521
1732
  break;
1522
1733
  }
@@ -1529,27 +1740,30 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1529
1740
  break;
1530
1741
  }
1531
1742
  case CA_REG_REPEAT: {
1532
- obj = rb_ca_repeat(argc, argv, self);
1743
+ obj = rb_ca_repeat(argc, argv, dest);
1533
1744
  obj = rb_ca_store_all(obj, rval);
1534
1745
  break;
1535
1746
  }
1536
1747
  case CA_REG_MAPPING: {
1537
- obj = rb_ca_fancy_index_chain(self, argv[0]);
1748
+ obj = rb_ca_fancy_index_chain(dest, argv[0]);
1538
1749
  obj = rb_ca_store_all(obj, rval);
1539
1750
  break;
1540
1751
  }
1541
1752
  case CA_REG_GRID: {
1542
- obj = rb_ca_grid(argc, argv, self);
1753
+ obj = rb_ca_grid(argc, argv, dest);
1543
1754
  obj = rb_ca_store_all(obj, rval);
1544
1755
  break;
1545
1756
  }
1546
1757
  case CA_REG_METHOD_CALL: {
1547
- volatile VALUE idx;
1758
+ ca_store_method_call_ctx_t c;
1548
1759
  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);
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);
1553
1767
  break;
1554
1768
  }
1555
1769
  case CA_REG_MEMBER: {
@@ -1627,15 +1841,13 @@ rb_ca_s_scan_index (VALUE self, VALUE rdim, VALUE ridx)
1627
1841
  Check_Type(rdim, T_ARRAY);
1628
1842
  Check_Type(ridx, T_ARRAY);
1629
1843
 
1630
- elements = 1;
1631
1844
  ndim = (int) RARRAY_LEN(rdim);
1845
+ CA_CHECK_RANK(ndim);
1632
1846
  for (i=0; i<ndim; i++) {
1633
1847
  dim[i] = NUM2SIZE(rb_ary_entry(rdim, i));
1634
- elements *= dim[i];
1635
1848
  }
1636
-
1637
- CA_CHECK_RANK(ndim);
1638
1849
  CA_CHECK_DIM(ndim, dim);
1850
+ elements = ca_shape_elements(ndim, dim, 1);
1639
1851
 
1640
1852
  info.range_check = 1;
1641
1853
  rb_ca_scan_index(ndim, dim, elements,
@@ -1699,7 +1911,7 @@ rb_ca_s_scan_index (VALUE self, VALUE rdim, VALUE ridx)
1699
1911
  info.index[i].symbol.spec));
1700
1912
  break;
1701
1913
  default:
1702
- rb_raise(rb_eRuntimeError, "unknown index spec");
1914
+ rb_raise(rb_eIndexError, "unknown index spec");
1703
1915
  }
1704
1916
  }
1705
1917
  break;
@@ -1767,7 +1979,7 @@ rb_ca_normalize_index (VALUE self, VALUE ridx)
1767
1979
  rb_ary_store(rindex, i, ID2SYM(info.index[i].symbol.id));
1768
1980
  break;
1769
1981
  default:
1770
- rb_raise(rb_eRuntimeError, "unknown index spec");
1982
+ rb_raise(rb_eIndexError, "unknown index spec");
1771
1983
  }
1772
1984
  }
1773
1985
  return rindex;
@@ -1814,8 +2026,8 @@ parse_shape_kwarg (VALUE rshape, int *out_ndim, ca_size_t *dim,
1814
2026
  rb_raise(rb_eArgError, "negative dim %" PRId64 " at axis %d",
1815
2027
  (ca_size_t) dim[i], i);
1816
2028
  }
1817
- elements *= dim[i];
1818
2029
  }
2030
+ elements = ca_shape_elements(ndim, dim, 1);
1819
2031
  *out_ndim = ndim;
1820
2032
  *out_elements = elements;
1821
2033
  }
@@ -1829,12 +2041,7 @@ addr2index_do (int ndim, ca_size_t *dim, ca_size_t elements, VALUE raddr)
1829
2041
  /* Scalar path (Integer input): returns Ruby Array of N Integers.
1830
2042
  * Preserves legacy shape for callers like `i, j = ca.addr2index(k)`. */
1831
2043
  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
- }
2044
+ ca_size_t addr = ca_index_in_range(NUM2SIZE(raddr), elements, "address");
1838
2045
  out = rb_ary_new2(ndim);
1839
2046
  for (i = ndim - 1; i >= 0; i--) {
1840
2047
  rb_ary_store(out, i, SIZE2NUM(addr % dim[i]));
@@ -1852,8 +2059,9 @@ addr2index_do (int ndim, ca_size_t *dim, ca_size_t elements, VALUE raddr)
1852
2059
  ca_size_t j, n;
1853
2060
  volatile VALUE objs[CA_RANK_MAX];
1854
2061
 
2062
+ ca_check_index_array(raddr);
1855
2063
  cin = ca_wrap_readonly(raddr, CA_SIZE);
1856
- ca_attach(cin);
2064
+ ca_attach(cin); /* window: detached before its one raise */
1857
2065
 
1858
2066
  out = rb_ary_new2(ndim);
1859
2067
  for (i = 0; i < ndim; i++) {
@@ -1882,12 +2090,11 @@ addr2index_do (int ndim, ca_size_t *dim, ca_size_t elements, VALUE raddr)
1882
2090
  continue;
1883
2091
  }
1884
2092
  addr = src[j];
1885
- if ( addr < 0 || addr >= elements ) {
2093
+ if ( addr < -elements || addr >= elements ) {
1886
2094
  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));
2095
+ ca_index_out_of_range(addr, elements, "address");
1890
2096
  }
2097
+ addr = ca_index_in_range(addr, elements, "address");
1891
2098
  for (k = ndim - 1; k >= 0; k--) {
1892
2099
  p_out[k][j] = addr % dim[k];
1893
2100
  addr /= dim[k];
@@ -1900,14 +2107,87 @@ addr2index_do (int ndim, ca_size_t *dim, ca_size_t elements, VALUE raddr)
1900
2107
  }
1901
2108
  }
1902
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
+
1903
2122
  static VALUE
1904
- index2addr_do (int ndim, ca_size_t *dim, int argc, VALUE *argv)
2123
+ ca_index2addr_body (VALUE arg)
1905
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;
1906
2130
  volatile VALUE obj;
1907
- CArray *co, *cidx[CA_RANK_MAX];
2131
+ CArray *co;
1908
2132
  ca_size_t *q, *p[CA_RANK_MAX], s[CA_RANK_MAX];
1909
2133
  ca_size_t addr, k, n;
1910
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;
1911
2191
  int i, all_number = 1;
1912
2192
  int out_ndim = 1;
1913
2193
  ca_size_t out_dim[CA_RANK_MAX];
@@ -1941,6 +2221,7 @@ index2addr_do (int ndim, ca_size_t *dim, int argc, VALUE *argv)
1941
2221
  * the first non-scalar CArray input; other non-scalar inputs must
1942
2222
  * match that shape. */
1943
2223
  for (i = 0; i < ndim; i++) {
2224
+ ca_check_index_array(argv[i]);
1944
2225
  cidx[i] = ca_wrap_readonly(argv[i], CA_SIZE);
1945
2226
  if ( ! ca_is_scalar(cidx[i]) ) {
1946
2227
  if ( shape_from < 0 ) {
@@ -1969,56 +2250,19 @@ index2addr_do (int ndim, ca_size_t *dim, int argc, VALUE *argv)
1969
2250
  }
1970
2251
  }
1971
2252
 
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
- }
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);
2017
2265
  }
2018
-
2019
- for (i = 0; i < ndim; i++) { ca_detach(cidx[i]); }
2020
-
2021
- return obj;
2022
2266
  }
2023
2267
 
2024
2268
  VALUE