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
@@ -1,4 +1,5 @@
1
1
  #include "carray.h"
2
+ #include "carray_internal.h"
2
3
 
3
4
  /* ---------------------------------------------------------------------------
4
5
 
@@ -8,8 +9,8 @@
8
9
  writes a[i] from its own loop, so what it needs from CArray is not element
9
10
  delivery but an addressing basis: a pointer, already shifted to cell zero,
10
11
  and one byte stride per axis. That is why this does not sit on the kernel
11
- iterator (per-cell / per-slab delivery, and no N-ary form) or on the sweep
12
- ELEMENT family (which flattens the array and cannot recover the axis
12
+ iterator (per-cell / per-slab delivery, and no N-ary form) or on the
13
+ call_cfunc families (which flatten the array and cannot recover the axis
13
14
  structure a stencil needs).
14
15
 
15
16
  This is a runtime facility at the same layer as ca_attach, not a user
@@ -79,6 +80,8 @@ typedef struct {
79
80
  VALUE bases;
80
81
  VALUE box_starts; /* per array, per axis; nil for "all of it" */
81
82
  VALUE box_counts;
83
+ int body_done; /* the block returned without raising */
84
+ volatile VALUE put_error; /* the first write-back that raised, or nil */
82
85
  } open_state;
83
86
 
84
87
  /* The view's own row-major byte layout, which is the address space
@@ -94,6 +97,15 @@ native_steps (CArray *ca, ca_size_t *steps)
94
97
  }
95
98
  }
96
99
 
100
+ /* Whether [start, start + count) leaves [0, dim). Written so that no sum
101
+ is formed: a start and a count near the top of the integer range add up
102
+ to a negative number, which `start + count > dim` lets through. */
103
+ static int
104
+ outside_axis (ca_size_t start, ca_size_t count, ca_size_t dim)
105
+ {
106
+ return start < 0 || count < 0 || start > dim || count > dim - start;
107
+ }
108
+
97
109
  /* Checks one array's box description, all of it: that it is one start and one
98
110
  count per axis, that they are numbers, and that the box they describe is
99
111
  inside the array.
@@ -124,9 +136,11 @@ verify_box (VALUE box_starts, VALUE box_counts, int index, CArray *ca)
124
136
  "this array has %d", (int) ca->ndim);
125
137
  }
126
138
  for ( k = 0; k < ca->ndim; k++ ) {
127
- ca_size_t start = NUM2LL(rb_ary_entry(starts, k));
128
- ca_size_t count = NUM2LL(rb_ary_entry(counts, k));
129
- if ( start < 0 || count < 0 || start + count > ca->dim[k] ) {
139
+ ca_size_t start = ca_integer_arg(rb_ary_entry(starts, k), "a region start",
140
+ "CArray::AddressBasis.open");
141
+ ca_size_t count = ca_integer_arg(rb_ary_entry(counts, k), "a region count",
142
+ "CArray::AddressBasis.open");
143
+ if ( outside_axis(start, count, ca->dim[k]) ) {
130
144
  rb_raise(rb_eArgError,
131
145
  "the requested region falls outside the array on axis %d", (int) k);
132
146
  }
@@ -156,7 +170,7 @@ read_box (open_state *state, int index, CArray *ca,
156
170
  starts[k] = NUM2LL(rb_ary_entry(per_array_start, k));
157
171
  counts[k] = NUM2LL(rb_ary_entry(per_array_count, k));
158
172
  }
159
- if ( starts[k] < 0 || counts[k] < 0 || starts[k] + counts[k] > ca->dim[k] ) {
173
+ if ( outside_axis(starts[k], counts[k], ca->dim[k]) ) {
160
174
  rb_raise(rb_eArgError,
161
175
  "the requested region falls outside the array on axis %d", (int) k);
162
176
  }
@@ -217,6 +231,28 @@ folds_to_an_entity (CArray *ca)
217
231
  return ca_is_entity(root);
218
232
  }
219
233
 
234
+ /* A view that reinterprets the element size -- refer(CA_INT32, ...) over
235
+ a float64 array -- gets a mask of its own shape, but one mask cell of it
236
+ covers a fraction of a parent cell, so writing cell i's mask also marks
237
+ its neighbour. A per-cell kernel writes cells independently and cannot
238
+ express that. Checked for every tier: a box transfer moves the same
239
+ mask, and the neighbour is marked when it is written back. */
240
+ static void
241
+ verify_mask_cells (VALUE object, CArray *ca)
242
+ {
243
+ CArray *root;
244
+ ca_size_t strides[CA_RANK_MAX];
245
+ ca_size_t base = 0;
246
+ if ( ! ca->mask || ! ca_is_stride_family(ca) ) return;
247
+ ca_stride_compose_to_root((CAStride *) ca, &root, strides, &base);
248
+ if ( ca->bytes != root->bytes ) {
249
+ rb_raise(rb_eArgError,
250
+ "%"PRIsVALUE" reinterprets the element size and carries a mask; "
251
+ "its mask cells do not map one to one onto the parent's",
252
+ rb_obj_class(object));
253
+ }
254
+ }
255
+
220
256
  static int
221
257
  tier_for (CArray *ca)
222
258
  {
@@ -239,7 +275,7 @@ acquire_basis (open_state *state, int index, ca_size_t *strides)
239
275
  switch ( state->tier[index] ) {
240
276
  case TIER_ENTITY: {
241
277
  CArray *root = ca;
242
- ca_attach(root);
278
+ ca_attach(root); /* window: released by open_ensure */
243
279
  state->roots[index] = root;
244
280
  state->attached_root[index] = 1;
245
281
  row_major_strides(ca, strides);
@@ -250,18 +286,7 @@ acquire_basis (open_state *state, int index, ca_size_t *strides)
250
286
  case TIER_STRIDE: {
251
287
  CArray *root;
252
288
  ca_stride_compose_to_root((CAStride *) ca, &root, strides, &base);
253
- /* A view that reinterprets the element size -- refer(CA_INT32, ...) over
254
- a float64 array -- gets a mask of its own shape, but one mask cell of
255
- it covers a fraction of a parent cell, so writing cell i's mask also
256
- marks its neighbour. A per-cell kernel writes cells independently and
257
- cannot express that. */
258
- if ( ca->mask && ca->bytes != root->bytes ) {
259
- rb_raise(rb_eArgError,
260
- "%"PRIsVALUE" reinterprets the element size and carries a mask; "
261
- "its mask cells do not map one to one onto the parent's",
262
- rb_obj_class(rb_ary_entry(state->arrays, index)));
263
- }
264
- ca_attach(root);
289
+ ca_attach(root); /* window: released by open_ensure */
265
290
  state->roots[index] = root;
266
291
  state->attached_root[index] = 1;
267
292
  pointer = root->ptr + base;
@@ -287,12 +312,26 @@ acquire_basis (open_state *state, int index, ca_size_t *strides)
287
312
  step *= counts[k];
288
313
  }
289
314
 
290
- buffer = ALLOC_N(char, (elements > 0 ? elements : 1) * ca->bytes);
315
+ /* Recorded before the read: the read can raise, and open_ensure
316
+ frees what state->region holds. The counts stay zero until the
317
+ read is done, so a buffer that was never filled is not written
318
+ back. */
319
+ /* A writable box keeps a second copy of what it read, so that the
320
+ write-back can send only the cells the kernel changed (see
321
+ put_changed). */
322
+ buffer = ALLOC_N(char, (state->writable[index] ? 2 : 1)
323
+ * (elements > 0 ? elements : 1) * ca->bytes);
324
+ for ( k = 0; k < ca->ndim; k++ ) {
325
+ state->region_count[index * CA_RANK_MAX + k] = 0;
326
+ }
327
+ state->region[index] = buffer;
291
328
  if ( elements > 0 ) {
292
329
  ca_xfer_stride(ca, starts, counts, steps, buffer, CA_XFER_GET);
330
+ if ( state->writable[index] ) {
331
+ memcpy(buffer + elements * ca->bytes, buffer, elements * ca->bytes);
332
+ }
293
333
  }
294
334
 
295
- state->region[index] = buffer;
296
335
  for ( k = 0; k < ca->ndim; k++ ) {
297
336
  state->region_start[index * CA_RANK_MAX + k] = starts[k];
298
337
  state->region_count[index * CA_RANK_MAX + k] = counts[k];
@@ -350,10 +389,73 @@ open_body (VALUE argument)
350
389
  }
351
390
  rb_ary_push(state->bases, basis);
352
391
  }
353
- return rb_yield(state->bases);
392
+ {
393
+ VALUE result = rb_yield(state->bases);
394
+ state->body_done = 1;
395
+ return result;
396
+ }
397
+ }
398
+
399
+ typedef struct {
400
+ CArray *ca;
401
+ ca_size_t *start;
402
+ ca_size_t *count;
403
+ ca_size_t *steps;
404
+ char *data;
405
+ char *read; /* the box as it was read */
406
+ } write_back_args;
407
+
408
+ /* Sends back the cells whose bytes differ from what was read, one run
409
+ along the last axis at a time. Sending the whole box would also send the
410
+ cells the kernel never wrote, and those do not come back unchanged
411
+ through a view that converts -- a float64 array seen through an int32
412
+ view reads 1.75 as 1 and would be left holding 1.0. It would also
413
+ overwrite what the kernel wrote to the same cells through another basis
414
+ that addresses them in place. */
415
+ static VALUE
416
+ write_back (VALUE argument)
417
+ {
418
+ write_back_args *w = (write_back_args *) argument;
419
+ CArray *ca = w->ca;
420
+ int8_t nd = ca->ndim;
421
+ size_t bytes = ca->bytes;
422
+ ca_size_t inner = w->count[nd - 1];
423
+ ca_size_t outer = 1;
424
+ ca_size_t idx[CA_RANK_MAX], start[CA_RANK_MAX], count[CA_RANK_MAX];
425
+ ca_size_t o, j, e;
426
+ int8_t k;
427
+
428
+ for ( k = 0; k < nd - 1; k++ ) {
429
+ outer *= w->count[k];
430
+ idx[k] = 0;
431
+ count[k] = 1;
432
+ }
433
+ for ( o = 0; o < outer; o++ ) {
434
+ char *row = w->data + o * inner * bytes;
435
+ char *read = w->read + o * inner * bytes;
436
+ for ( k = 0; k < nd - 1; k++ ) start[k] = w->start[k] + idx[k];
437
+ j = 0;
438
+ while ( j < inner ) {
439
+ if ( ! memcmp(row + j * bytes, read + j * bytes, bytes) ) { j++; continue; }
440
+ e = j + 1;
441
+ while ( e < inner && memcmp(row + e * bytes, read + e * bytes, bytes) ) e++;
442
+ start[nd - 1] = w->start[nd - 1] + j;
443
+ count[nd - 1] = e - j;
444
+ ca_xfer_stride(ca, start, count, w->steps, row + j * bytes, CA_XFER_PUT);
445
+ j = e;
446
+ }
447
+ for ( k = nd - 2; k >= 0; k-- ) {
448
+ if ( ++idx[k] < w->count[k] ) break;
449
+ idx[k] = 0;
450
+ }
451
+ }
452
+ return Qnil;
354
453
  }
355
454
 
356
- /* Closes in reverse order, and runs whether or not the kernel raised. */
455
+ /* Closes in reverse order, and runs whether or not the kernel raised. A
456
+ write-back that raises -- the array's own transfer slot failing -- is
457
+ set aside so that every other region is still written and freed and
458
+ every root detached; the block's own exception, if it raised, wins. */
357
459
  static VALUE
358
460
  open_ensure (VALUE argument)
359
461
  {
@@ -371,11 +473,23 @@ open_ensure (VALUE argument)
371
473
  elements *= state->region_count[i * CA_RANK_MAX + k];
372
474
  }
373
475
  if ( state->writable[i] && elements > 0 ) {
374
- ca_size_t steps[CA_RANK_MAX];
476
+ ca_size_t steps[CA_RANK_MAX];
477
+ write_back_args w;
478
+ int tag = 0;
375
479
  native_steps(ca, steps);
376
- ca_xfer_stride(ca, &state->region_start[i * CA_RANK_MAX],
377
- &state->region_count[i * CA_RANK_MAX],
378
- steps, state->region[i], CA_XFER_PUT);
480
+ w.ca = ca;
481
+ w.start = &state->region_start[i * CA_RANK_MAX];
482
+ w.count = &state->region_count[i * CA_RANK_MAX];
483
+ w.steps = steps;
484
+ w.data = state->region[i];
485
+ w.read = state->region[i] + elements * ca->bytes;
486
+ rb_protect(write_back, (VALUE) &w, &tag);
487
+ if ( tag ) {
488
+ if ( NIL_P(state->put_error) ) {
489
+ state->put_error = rb_errinfo();
490
+ }
491
+ rb_set_errinfo(Qnil);
492
+ }
379
493
  }
380
494
  xfree(state->region[i]);
381
495
  }
@@ -384,6 +498,10 @@ open_ensure (VALUE argument)
384
498
  }
385
499
  }
386
500
  xfree(state->block);
501
+ if ( state->body_done && RTEST(rb_obj_is_kind_of(state->put_error,
502
+ rb_eException)) ) {
503
+ rb_exc_raise(state->put_error);
504
+ }
387
505
  return Qnil;
388
506
  }
389
507
 
@@ -451,7 +569,12 @@ packed_body (VALUE argument)
451
569
  }
452
570
  }
453
571
 
454
- return rb_yield_values(4, pointers, strides, mask_pointers, mask_strides);
572
+ {
573
+ VALUE result = rb_yield_values(4, pointers, strides, mask_pointers,
574
+ mask_strides);
575
+ state->body_done = 1;
576
+ return result;
577
+ }
455
578
  }
456
579
 
457
580
  /*
@@ -489,9 +612,25 @@ address_basis_open (int argc, VALUE *argv, VALUE module)
489
612
  rb_raise(rb_eArgError, "one writable flag per array is required");
490
613
  }
491
614
 
615
+ /* Every argument is checked before the block below is allocated: a
616
+ refusal raises, and it must find nothing to free. */
617
+ for ( i = 0; i < (int) RARRAY_LEN(arrays); i++ ) {
618
+ VALUE object = rb_ary_entry(arrays, i);
619
+ CArray *ca;
620
+ GetCArray(object, ca);
621
+ /* A view takes its mask from its parent when first asked for it; one
622
+ added to the parent after the view was made is not on ca->mask yet. */
623
+ ca_update_mask(ca);
624
+ verify_usable(object, ca, RTEST(rb_ary_entry(writable_flags, i)));
625
+ verify_mask_cells(object, ca);
626
+ verify_box(box_start, box_count, i, ca);
627
+ }
628
+
492
629
  state.count = (int) RARRAY_LEN(arrays);
493
630
  state.slots = state.count * 2;
494
- state.arrays = arrays;
631
+ /* Our own copy: the caller's Array can be emptied inside the block, and
632
+ the arrays it held are attached until open_ensure has run. */
633
+ state.arrays = rb_ary_dup(arrays);
495
634
  state.bases = rb_ary_new_capa(state.count);
496
635
  /* One allocation, carved up, rather than eight. Every array here is the
497
636
  same length in slots and lives exactly as long as the open, so there is
@@ -521,15 +660,15 @@ address_basis_open (int argc, VALUE *argv, VALUE module)
521
660
  }
522
661
  state.box_starts = box_start;
523
662
  state.box_counts = box_count;
663
+ state.body_done = 0;
664
+ state.put_error = Qnil;
524
665
 
525
666
  for ( i = 0; i < state.count; i++ ) {
526
- VALUE object = rb_ary_entry(arrays, i);
667
+ VALUE object = rb_ary_entry(state.arrays, i);
527
668
  CArray *ca;
528
669
  GetCArray(object, ca);
529
670
  state.carrays[i] = ca;
530
671
  state.writable[i] = RTEST(rb_ary_entry(writable_flags, i));
531
- verify_usable(object, ca, state.writable[i]);
532
- verify_box(box_start, box_count, i, ca);
533
672
  state.tier[i] = tier_for(ca);
534
673
 
535
674
  if ( ca->mask ) {
@@ -540,10 +679,12 @@ address_basis_open (int argc, VALUE *argv, VALUE module)
540
679
  }
541
680
  }
542
681
 
543
- if ( RTEST(packed) ) {
544
- return rb_ensure(packed_body, (VALUE) &state, open_ensure, (VALUE) &state);
545
- } else {
546
- return rb_ensure(open_body, (VALUE) &state, open_ensure, (VALUE) &state);
682
+ {
683
+ VALUE result = RTEST(packed)
684
+ ? rb_ensure(packed_body, (VALUE) &state, open_ensure, (VALUE) &state)
685
+ : rb_ensure(open_body, (VALUE) &state, open_ensure, (VALUE) &state);
686
+ RB_GC_GUARD(state.arrays);
687
+ return result;
547
688
  }
548
689
  }
549
690
 
@@ -13,6 +13,7 @@
13
13
 
14
14
  #include "carray.h"
15
15
  #include "ca_obj_face.h" /* CA_OBJ_RECORD + rb_ca_record_get_data_class */
16
+ #include "carray_internal.h" /* rb_ca_inherit_attr */
16
17
 
17
18
  /* ------------------------------------------------------------------- */
18
19
 
@@ -436,6 +437,8 @@ rb_ca_is_object_type (VALUE self)
436
437
  /* ------------------------------------------------------------------- */
437
438
 
438
439
  static ID id_parent;
440
+ static ID id_attr;
441
+ static ID id_attrs;
439
442
 
440
443
  VALUE
441
444
  rb_ca_parent (VALUE self)
@@ -443,13 +446,41 @@ rb_ca_parent (VALUE self)
443
446
  return rb_ivar_get(self, id_parent);
444
447
  }
445
448
 
449
+ /* Give dst the attributes (lib/carray/attribute.rb) that src shows, as its
450
+ own Hash. Used by the methods that produce a new entity holding the
451
+ same values -- copy, to_type. The parent walk here only decides
452
+ whether any array on the chain has an @attr at all; which of them
453
+ src actually shows (a lazy op view stops the chain) is left to #attrs,
454
+ so the rule lives in one place. */
455
+ void
456
+ rb_ca_inherit_attr (VALUE dst, VALUE src)
457
+ {
458
+ VALUE obj = src, attrs;
459
+ int depth;
460
+
461
+ for (depth = 0; depth < 64 && ! NIL_P(obj); depth++) {
462
+ if ( rb_ivar_defined(obj, id_attr) ) {
463
+ break;
464
+ }
465
+ obj = rb_ivar_get(obj, id_parent);
466
+ }
467
+ if ( depth == 64 || NIL_P(obj) ) {
468
+ return;
469
+ }
470
+ attrs = rb_funcall(src, id_attrs, 0);
471
+ if ( RHASH_SIZE(attrs) > 0 ) {
472
+ rb_ivar_set(dst, id_attr, rb_hash_dup(attrs));
473
+ }
474
+ }
475
+
476
+ /* A view of a frozen array is not frozen itself: building it may still set
477
+ it up after this (an ivar, a lift that moves it onto another parent), and
478
+ writes through it are refused by ca_is_readonly, which walks to the
479
+ frozen root and finds CA_FLAG_READ_ONLY there. */
446
480
  VALUE
447
481
  rb_ca_set_parent (VALUE self, VALUE obj)
448
482
  {
449
483
  rb_ivar_set(self, id_parent, obj);
450
- if ( OBJ_FROZEN(obj) ) {
451
- rb_ca_freeze(self);
452
- }
453
484
  return obj;
454
485
  }
455
486
 
@@ -521,6 +552,15 @@ ca_root_array (void *ap)
521
552
  }
522
553
  }
523
554
 
555
+ /* Whether two arrays may share memory: their cells live in the same
556
+ root. An operation that writes one while reading the other must read
557
+ from a copy taken before it writes. */
558
+ int
559
+ ca_shares_root (void *ap, void *aq)
560
+ {
561
+ return ca_root_array(ap) == ca_root_array(aq);
562
+ }
563
+
524
564
  static VALUE
525
565
  rb_ca_root_array (VALUE self)
526
566
  {
@@ -584,6 +624,8 @@ void
584
624
  Init_carray_attribute (void)
585
625
  {
586
626
  id_parent = rb_intern("parent");
627
+ id_attr = rb_intern("@attr");
628
+ id_attrs = rb_intern("attrs");
587
629
 
588
630
  rb_define_method(rb_cCArray, "obj_type", rb_ca_obj_type, 0);
589
631
  rb_define_method(rb_cCArray, "data_type", rb_ca_data_type, 0);
@@ -72,6 +72,34 @@
72
72
  } \
73
73
  } while (0)
74
74
 
75
+ /* Checked before a window opens: the dispatches above raise on a type
76
+ they do not cover, which would leave the labels (and weights) attached. */
77
+ static void
78
+ bincount_check_types (CArray *cl, CArray *cw)
79
+ {
80
+ switch ( cl->data_type ) {
81
+ case CA_INT8: case CA_INT16: case CA_INT32: case CA_INT64:
82
+ case CA_UINT8: case CA_UINT16: case CA_UINT32: case CA_UINT64:
83
+ break;
84
+ default:
85
+ rb_raise(rb_eCADataTypeError,
86
+ "bincount: integer label array required (got %d)",
87
+ cl->data_type);
88
+ }
89
+ if ( ! cw ) {
90
+ return;
91
+ }
92
+ switch ( cw->data_type ) {
93
+ case CA_FLOAT64: case CA_FLOAT32:
94
+ case CA_INT8: case CA_INT16: case CA_INT32: case CA_INT64:
95
+ case CA_UINT8: case CA_UINT16: case CA_UINT32: case CA_UINT64:
96
+ break;
97
+ default:
98
+ rb_raise(rb_eCADataTypeError,
99
+ "bincount: weights must be numeric (got %d)", cw->data_type);
100
+ }
101
+ }
102
+
75
103
  /* __bincount_count__(length) -- count occurrences of each label in
76
104
  self. Allocates a zero-filled UInt32 (or UInt64 if length >= 2^32)
77
105
  output of size `length`, then runs the 8-way label dispatch. */
@@ -104,7 +132,8 @@ rb_ca_bincount_count_kernel (VALUE self, VALUE rlength)
104
132
  return vout;
105
133
  }
106
134
 
107
- ca_attach(cl);
135
+ bincount_check_types(cl, NULL);
136
+ ca_attach(cl); /* window: nothing raises inside */
108
137
  n = cl->elements;
109
138
  mlabel = cl->mask ? (boolean8_t *) cl->mask->ptr : NULL;
110
139
 
@@ -213,7 +242,8 @@ rb_ca_bincount_weighted_kernel (VALUE self, VALUE rweights, VALUE rlength)
213
242
  return vout;
214
243
  }
215
244
 
216
- ca_attach_n(2, cl, cw);
245
+ bincount_check_types(cl, cw);
246
+ ca_attach_n(2, cl, cw); /* window: nothing raises inside */
217
247
  n = cl->elements;
218
248
  mlabel = cl->mask ? (boolean8_t *) cl->mask->ptr : NULL;
219
249
  mweight = cw->mask ? (boolean8_t *) cw->mask->ptr : NULL;
@@ -30,6 +30,7 @@
30
30
  ---------------------------------------------------------------------------- */
31
31
 
32
32
  #include "carray.h"
33
+ #include "ca_obj_face.h" /* CA_WRAPPER_LIFT */
33
34
 
34
35
  NORETURN(static void ca_broadcast_refuse_write (VALUE dst, VALUE src));
35
36
 
@@ -39,7 +40,7 @@ ca_broadcast_refuse_write (VALUE dst, VALUE src)
39
40
  {
40
41
  volatile VALUE dst_s = rb_inspect(rb_funcall(dst, rb_intern("shape"), 0));
41
42
  volatile VALUE src_s = rb_inspect(rb_funcall(src, rb_intern("shape"), 0));
42
- rb_raise(rb_eRuntimeError,
43
+ rb_raise(rb_eArgError,
43
44
  "shape mismatch writing to carray (%s <- %s); shapes must agree "
44
45
  "once size-1 axes are dropped, or one side must be 1-D, or the "
45
46
  "source must be smaller and broadcastable -- use .flatten to "
@@ -119,7 +120,7 @@ ca_broadcast_view (VALUE src, int8_t ndim, ca_size_t *target_dim)
119
120
  TypedData_Get_Struct(src, CArray, &carray_data_type, cs);
120
121
 
121
122
  if (cs->ndim != ndim) {
122
- rb_raise(rb_eRuntimeError,
123
+ rb_raise(rb_eArgError,
123
124
  "broadcast: ndim mismatch (%d vs %d)",
124
125
  (int) cs->ndim, (int) ndim);
125
126
  }
@@ -143,7 +144,7 @@ ca_broadcast_view (VALUE src, int8_t ndim, ca_size_t *target_dim)
143
144
  needs_broadcast = 1;
144
145
  }
145
146
  else {
146
- rb_raise(rb_eRuntimeError,
147
+ rb_raise(rb_eArgError,
147
148
  "broadcast: cannot broadcast axis %d (%lld vs %lld)",
148
149
  i, (long long) cs->dim[i], (long long) target_dim[i]);
149
150
  }
@@ -172,20 +173,48 @@ ca_broadcast_view (VALUE src, int8_t ndim, ca_size_t *target_dim)
172
173
  raises). */
173
174
  NORETURN(static void ca_broadcast_refuse (VALUE self, VALUE other));
174
175
 
175
- /* Report a pair that cannot be brought to a common shape. Naming both
176
- shapes is the point of the message: the caller wrote two arrays that
177
- cannot be combined, and the only remedy is to say which two. */
176
+ /* Report a pair that cannot be brought to a common shape. The message
177
+ names both shapes and points to the remedy that fits the pair: when
178
+ the ndim differs, the missing axes are added with :_; when the
179
+ element counts are equal, .flatten pairs the values in the order
180
+ they lie. */
178
181
  static void
179
182
  ca_broadcast_refuse (VALUE self, VALUE other)
180
183
  {
184
+ CArray *ca, *cb;
181
185
  volatile VALUE self_s = rb_inspect(rb_funcall(self, rb_intern("shape"), 0));
182
186
  volatile VALUE other_s = rb_inspect(rb_funcall(other, rb_intern("shape"), 0));
187
+ const char *flatten_hint;
188
+ int i;
189
+
190
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
191
+ TypedData_Get_Struct(other, CArray, &carray_data_type, cb);
192
+
193
+ flatten_hint = (ca->elements == cb->elements)
194
+ ? "; to pair the values in the order they lie, use .flatten on both sides"
195
+ : "";
196
+
197
+ if (ca->ndim != cb->ndim) {
198
+ rb_raise(rb_eArgError,
199
+ "shape mismatch between operands (%s and %s): the operands "
200
+ "have different numbers of axes, and axes are not added "
201
+ "implicitly -- add size-1 axes with :_ so both have the same "
202
+ "number (x[:_, nil] makes a 1-D x a row, x[nil, :_] a column)%s",
203
+ StringValueCStr(self_s), StringValueCStr(other_s),
204
+ flatten_hint);
205
+ }
206
+
207
+ for (i = 0; i < ca->ndim; i++) {
208
+ if (ca->dim[i] != cb->dim[i] && ca->dim[i] != 1 && cb->dim[i] != 1) {
209
+ break;
210
+ }
211
+ }
183
212
  rb_raise(rb_eArgError,
184
- "shape mismatch between operands (%s and %s); shapes must "
185
- "agree, or differ only in size-1 axes with equal ndim -- use "
186
- ".flatten on both sides to operate on the values in the order "
187
- "they lie",
188
- StringValueCStr(self_s), StringValueCStr(other_s));
213
+ "shape mismatch between operands (%s and %s): axis %d has "
214
+ "sizes %lld and %lld; each axis must have the same size, or "
215
+ "size 1 on one side%s",
216
+ StringValueCStr(self_s), StringValueCStr(other_s), i,
217
+ (long long) ca->dim[i], (long long) cb->dim[i], flatten_hint);
189
218
  }
190
219
 
191
220
  void
@@ -272,7 +301,7 @@ rb_ca_broadcast_to (int argc, VALUE *argv, VALUE self)
272
301
  target_ndim = (int8_t) argc;
273
302
 
274
303
  if (target_ndim < cs->ndim) {
275
- rb_raise(rb_eRuntimeError,
304
+ rb_raise(rb_eArgError,
276
305
  "broadcast_to: target ndim %d smaller than source ndim %d",
277
306
  (int) target_ndim, (int) cs->ndim);
278
307
  }
@@ -303,6 +332,7 @@ rb_ca_broadcast_to (int argc, VALUE *argv, VALUE self)
303
332
  }
304
333
  obj = ca_wrap_struct(view);
305
334
  rb_ca_set_parent(obj, self);
335
+ CA_WRAPPER_LIFT(obj, self, cs);
306
336
  return obj;
307
337
  }
308
338
 
@@ -340,7 +370,7 @@ rb_ca_broadcast_to (int argc, VALUE *argv, VALUE self)
340
370
  else {
341
371
  /* sd == -1 (source exhausted) with dd > 1, or sd > 1 with
342
372
  dd > 1 and not equal: cannot broadcast. */
343
- rb_raise(rb_eRuntimeError,
373
+ rb_raise(rb_eArgError,
344
374
  "broadcast_to: cannot broadcast axis %d "
345
375
  "(source %s, target %lld)",
346
376
  t_idx,
@@ -351,7 +381,7 @@ rb_ca_broadcast_to (int argc, VALUE *argv, VALUE self)
351
381
  }
352
382
 
353
383
  if (s_idx >= 0) {
354
- rb_raise(rb_eRuntimeError,
384
+ rb_raise(rb_eArgError,
355
385
  "broadcast_to: %d source axes left unmatched", s_idx + 1);
356
386
  }
357
387
 
@@ -366,6 +396,8 @@ rb_ca_broadcast_to (int argc, VALUE *argv, VALUE self)
366
396
  }
367
397
  obj = ca_wrap_struct(view);
368
398
  rb_ca_set_parent(obj, self);
399
+ /* The Face stays on top, as for every view. */
400
+ CA_WRAPPER_LIFT(obj, self, cs);
369
401
  return obj;
370
402
  }
371
403