carray 3.0.3 → 3.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +264 -0
  3. data/README.md +3 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +1 -6
  7. data/ext/ca_axis_group.c +27 -27
  8. data/ext/ca_binop_dispatch.c +1 -29
  9. data/ext/ca_binop_dispatch.h +0 -1
  10. data/ext/ca_categorical_iterator.c +205 -134
  11. data/ext/ca_compare.c +7 -18
  12. data/ext/ca_group_iter.c +16 -17
  13. data/ext/ca_kernel_iterator.c +1234 -1994
  14. data/ext/ca_kernel_iterator.h +391 -437
  15. data/ext/ca_monop_dispatch.c +82 -59
  16. data/ext/ca_monop_dispatch.h +1 -0
  17. data/ext/ca_obj_array.c +96 -39
  18. data/ext/ca_obj_bincmp.c +24 -21
  19. data/ext/ca_obj_binop.c +15 -26
  20. data/ext/ca_obj_bitarray.c +3 -2
  21. data/ext/ca_obj_bitfield.c +5 -6
  22. data/ext/ca_obj_block.c +18 -9
  23. data/ext/ca_obj_byte_swap.c +5 -6
  24. data/ext/ca_obj_const_string.c +173 -19
  25. data/ext/ca_obj_face.c +238 -57
  26. data/ext/ca_obj_face.h +19 -0
  27. data/ext/ca_obj_fake.c +15 -29
  28. data/ext/ca_obj_farray.c +6 -3
  29. data/ext/ca_obj_field.c +17 -5
  30. data/ext/ca_obj_fixlen_string.c +2 -3
  31. data/ext/ca_obj_grid.c +406 -132
  32. data/ext/ca_obj_meld.c +172 -34
  33. data/ext/ca_obj_moncmp.c +23 -8
  34. data/ext/ca_obj_monop.c +67 -57
  35. data/ext/ca_obj_object.c +157 -51
  36. data/ext/ca_obj_record.c +12 -7
  37. data/ext/ca_obj_reduce.c +13 -8
  38. data/ext/ca_obj_refer.c +25 -27
  39. data/ext/ca_obj_remap.c +30 -9
  40. data/ext/ca_obj_repeat.c +9 -10
  41. data/ext/ca_obj_roll.c +6 -5
  42. data/ext/ca_obj_select.c +14 -9
  43. data/ext/ca_obj_select_axis.c +15 -47
  44. data/ext/ca_obj_shift.c +11 -1
  45. data/ext/ca_obj_stack.c +179 -34
  46. data/ext/ca_obj_stride.c +162 -115
  47. data/ext/ca_obj_string.c +71 -4
  48. data/ext/ca_obj_tile.c +6 -5
  49. data/ext/ca_obj_time.c +22 -6
  50. data/ext/ca_obj_timedelta.c +22 -6
  51. data/ext/ca_obj_transpose.c +5 -3
  52. data/ext/ca_obj_triop.c +23 -32
  53. data/ext/ca_obj_window.c +190 -26
  54. data/ext/ca_sweep_engine.c +2 -2
  55. data/ext/caframe_csv_reader.c +80 -1
  56. data/ext/carray.h +181 -75
  57. data/ext/carray_access.c +184 -96
  58. data/ext/carray_address_basis.c +343 -87
  59. data/ext/carray_attribute.c +89 -6
  60. data/ext/carray_bincount.c +10 -8
  61. data/ext/carray_broadcast.c +13 -8
  62. data/ext/carray_build_flags.h +3 -0
  63. data/ext/carray_call_cfunc.c +108 -0
  64. data/ext/carray_cast.c +142 -24
  65. data/ext/carray_cast_func.rb +46 -6
  66. data/ext/carray_class.c +4 -3
  67. data/ext/carray_config.h +28 -0
  68. data/ext/carray_conversion.c +16 -7
  69. data/ext/carray_copy.c +51 -14
  70. data/ext/carray_core.c +147 -55
  71. data/ext/carray_count.c +50 -20
  72. data/ext/carray_element.c +76 -33
  73. data/ext/carray_factorize.c +118 -64
  74. data/ext/carray_generate.c +191 -50
  75. data/ext/carray_histogram.c +62 -17
  76. data/ext/carray_hold.c +12 -15
  77. data/ext/carray_index_classifier.c +388 -176
  78. data/ext/carray_internal.h +336 -29
  79. data/ext/carray_kernels_bincmp.c +4453 -0
  80. data/ext/carray_kernels_binop.c +11368 -0
  81. data/ext/carray_kernels_init.c +1734 -0
  82. data/ext/carray_kernels_moncmp.c +2104 -0
  83. data/ext/carray_kernels_monop.c +18369 -0
  84. data/ext/carray_kernels_reduce_aggregate.c +23818 -0
  85. data/ext/carray_kernels_reduce_boolean.c +406 -0
  86. data/ext/carray_kernels_reduce_cumulative.c +10086 -0
  87. data/ext/carray_kernels_reduce_extreme.c +17634 -0
  88. data/ext/carray_kernels_reduce_variance.c +3849 -0
  89. data/ext/carray_kernels_scan.c +4302 -0
  90. data/ext/carray_kernels_search.c +33829 -0
  91. data/ext/carray_kernels_sort.c +10906 -0
  92. data/ext/carray_kernels_triop.c +1864 -0
  93. data/ext/carray_key_windows.c +216 -0
  94. data/ext/carray_lazy.c +170 -111
  95. data/ext/carray_mask.c +292 -72
  96. data/ext/carray_median_percentile.c +82 -28
  97. data/ext/carray_memory_view.c +669 -561
  98. data/ext/carray_operator.c +124 -47
  99. data/ext/carray_order.c +384 -76
  100. data/ext/carray_partition.c +28 -27
  101. data/ext/carray_random.c +67 -13
  102. data/ext/carray_round.c +273 -0
  103. data/ext/carray_scatter.c +63 -38
  104. data/ext/carray_segment.c +3 -3
  105. data/ext/carray_slab.c +91 -28
  106. data/ext/carray_sort.c +35 -35
  107. data/ext/carray_struct.c +19 -18
  108. data/ext/carray_test.c +26 -5
  109. data/ext/carray_undef.c +4 -0
  110. data/ext/carray_utils.c +246 -45
  111. data/ext/mk_call_cfunc.rb +19 -2
  112. data/ext/mkkernel.rb +198 -240
  113. data/ext/ruby_carray.c +10 -4
  114. data/ext/version.h +4 -4
  115. data/lib/carray/attribute.rb +126 -22
  116. data/lib/carray/autoload_carray.rb +8 -31
  117. data/lib/carray/autoload_method_extension.rb +14 -5
  118. data/lib/carray/axis_group.rb +255 -106
  119. data/lib/carray/basics.rb +78 -10
  120. data/lib/carray/bincount_nd.rb +12 -2
  121. data/lib/carray/block_iterator.rb +126 -23
  122. data/lib/carray/boolean_reduce.rb +17 -17
  123. data/lib/carray/categorical.rb +165 -43
  124. data/lib/carray/categorical_iterator.rb +130 -27
  125. data/lib/carray/complex.rb +5 -5
  126. data/lib/carray/conditional.rb +67 -18
  127. data/lib/carray/const_string.rb +14 -5
  128. data/lib/carray/construct.rb +15 -11
  129. data/lib/carray/data_type_extension.rb +1 -0
  130. data/lib/carray/distance_limit.rb +77 -0
  131. data/lib/carray/fixlen_string.rb +7 -1
  132. data/lib/carray/frame/column_verbs.rb +70 -0
  133. data/lib/carray/frame/convert.rb +69 -0
  134. data/lib/carray/frame/csv_parser.rb +111 -17
  135. data/lib/carray/frame/describe.rb +3 -2
  136. data/lib/carray/frame/frame.rb +246 -21
  137. data/lib/carray/frame/group.rb +510 -95
  138. data/lib/carray/frame/io.rb +115 -28
  139. data/lib/carray/frame/join.rb +312 -51
  140. data/lib/carray/frame/records.rb +83 -7
  141. data/lib/carray/frame/reshape.rb +42 -35
  142. data/lib/carray/frame/sort.rb +7 -19
  143. data/lib/carray/frame/verbs.rb +188 -48
  144. data/lib/carray/frame/windows.rb +117 -0
  145. data/lib/carray/frame.rb +2 -0
  146. data/lib/carray/fuse_source.rb +11 -4
  147. data/lib/carray/fusion.rb +40 -16
  148. data/lib/carray/histogram.rb +6 -1
  149. data/lib/carray/inspect.rb +31 -8
  150. data/lib/carray/iterator.rb +85 -7
  151. data/lib/carray/key_windows.rb +620 -0
  152. data/lib/carray/lazy.rb +182 -183
  153. data/lib/carray/mask_gap_fill.rb +63 -57
  154. data/lib/carray/meld_reduce.rb +12 -14
  155. data/lib/carray/methods/bin.rb +26 -18
  156. data/lib/carray/methods/bincount.rb +16 -0
  157. data/lib/carray/methods/bit_string.rb +1 -1
  158. data/lib/carray/methods/choose.rb +16 -3
  159. data/lib/carray/methods/composition.rb +3 -1
  160. data/lib/carray/methods/gather_nd.rb +14 -5
  161. data/lib/carray/methods/insert_block.rb +2 -2
  162. data/lib/carray/methods/is_between.rb +59 -0
  163. data/lib/carray/methods/is_in.rb +53 -36
  164. data/lib/carray/methods/join.rb +1 -6
  165. data/lib/carray/methods/lag.rb +49 -0
  166. data/lib/carray/methods/locate_addr.rb +47 -51
  167. data/lib/carray/methods/mode.rb +1 -1
  168. data/lib/carray/methods/nunique.rb +11 -6
  169. data/lib/carray/methods/resize.rb +3 -3
  170. data/lib/carray/methods/value_counts.rb +4 -11
  171. data/lib/carray/record_order.rb +3 -3
  172. data/lib/carray/rng.rb +3 -0
  173. data/lib/carray/runtime.rb +53 -8
  174. data/lib/carray/segment_iterator.rb +313 -82
  175. data/lib/carray/serialize.rb +24 -10
  176. data/lib/carray/slab_iterator.rb +50 -0
  177. data/lib/carray/string.rb +105 -5
  178. data/lib/carray/string_operation_extension.rb +8 -1
  179. data/lib/carray/struct.rb +9 -21
  180. data/lib/carray/struct_builder.rb +19 -8
  181. data/lib/carray/time.rb +726 -104
  182. data/lib/carray/window_iterator.rb +122 -16
  183. data/lib/carray.rb +7 -0
  184. data/yard-stubs/ca_obj_bitfield.rb +1 -1
  185. data/yard-stubs/ca_obj_field.rb +4 -0
  186. data/yard-stubs/ca_obj_grid.rb +21 -10
  187. data/yard-stubs/ca_obj_refer.rb +2 -1
  188. data/yard-stubs/ca_obj_roll.rb +2 -2
  189. data/yard-stubs/ca_obj_shift.rb +5 -1
  190. data/yard-stubs/ca_obj_tile.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +8 -3
  192. data/yard-stubs/carray_access.rb +6 -1
  193. data/yard-stubs/carray_attribute.rb +0 -5
  194. data/yard-stubs/carray_cast.rb +10 -0
  195. data/yard-stubs/carray_conversion.rb +2 -1
  196. data/yard-stubs/carray_copy.rb +4 -2
  197. data/yard-stubs/carray_count.rb +4 -4
  198. data/yard-stubs/carray_element.rb +10 -4
  199. data/yard-stubs/carray_generate.rb +21 -1
  200. data/yard-stubs/carray_mask.rb +26 -2
  201. data/yard-stubs/carray_math.rb +27 -1
  202. data/yard-stubs/carray_median_percentile.rb +5 -2
  203. data/yard-stubs/carray_memory_view.rb +40 -23
  204. data/yard-stubs/carray_order.rb +29 -18
  205. data/yard-stubs/carray_random.rb +13 -4
  206. data/yard-stubs/carray_scatter.rb +14 -9
  207. data/yard-stubs/carray_slab.rb +9 -2
  208. data/yard-stubs/carray_sort.rb +6 -5
  209. data/yard-stubs/carray_test.rb +5 -3
  210. metadata +25 -3
  211. data/lib/carray/arrow_tensor.rb +0 -401
  212. data/lib/carray/table.rb +0 -108
@@ -78,10 +78,16 @@ typedef struct {
78
78
  ca_size_t *region_start; /* count * CA_RANK_MAX */
79
79
  ca_size_t *region_count;
80
80
  VALUE bases;
81
- VALUE box_starts; /* per array, per axis; nil for "all of it" */
82
- VALUE box_counts;
81
+ int *has_box; /* per slot: 1 when box_start/box_count hold a box */
82
+ ca_size_t *box_start; /* slots * CA_RANK_MAX; the whole array when */
83
+ ca_size_t *box_count; /* has_box is 0 */
84
+ int lent; /* every basis was acquired and the block entered */
83
85
  int body_done; /* the block returned without raising */
84
86
  volatile VALUE put_error; /* the first write-back that raised, or nil */
87
+ /* ca_with_address_bases: the caller's body and what it is handed */
88
+ VALUE (*c_body)(ca_address_basis_t *bases, VALUE arg);
89
+ VALUE c_arg;
90
+ ca_address_basis_t *c_bases;
85
91
  } open_state;
86
92
 
87
93
  /* The view's own row-major byte layout, which is the address space
@@ -147,28 +153,21 @@ verify_box (VALUE box_starts, VALUE box_counts, int index, CArray *ca)
147
153
  }
148
154
  }
149
155
 
150
- /* Reads one array's box out of the Ruby-side description, defaulting to the
151
- whole array. verify_box checked it, and checks it again here because this
152
- is the last thing between a caller's numbers and pointer arithmetic. */
156
+ /* Reads one array's box, the whole array when none was given. The box was
157
+ checked when it was given, and is checked again here because this is the
158
+ last thing between a caller's numbers and pointer arithmetic. */
153
159
  static void
154
160
  read_box (open_state *state, int index, CArray *ca,
155
161
  ca_size_t *starts, ca_size_t *counts)
156
162
  {
157
- VALUE per_array_start = Qnil, per_array_count = Qnil;
158
163
  int8_t k;
159
-
160
- if ( ! NIL_P(state->box_starts) ) {
161
- per_array_start = rb_ary_entry(state->box_starts, index);
162
- per_array_count = rb_ary_entry(state->box_counts, index);
163
- }
164
-
165
164
  for ( k = 0; k < ca->ndim; k++ ) {
166
- if ( NIL_P(per_array_start) ) {
165
+ if ( state->has_box[index] ) {
166
+ starts[k] = state->box_start[index * CA_RANK_MAX + k];
167
+ counts[k] = state->box_count[index * CA_RANK_MAX + k];
168
+ } else {
167
169
  starts[k] = 0;
168
170
  counts[k] = ca->dim[k];
169
- } else {
170
- starts[k] = NUM2LL(rb_ary_entry(per_array_start, k));
171
- counts[k] = NUM2LL(rb_ary_entry(per_array_count, k));
172
171
  }
173
172
  if ( outside_axis(starts[k], counts[k], ca->dim[k]) ) {
174
173
  rb_raise(rb_eArgError,
@@ -202,14 +201,14 @@ size_array (ca_size_t *values, int8_t count)
202
201
  /* Refuses what a generated kernel cannot express, rather than letting it
203
202
  produce quietly wrong numbers. */
204
203
  static void
205
- verify_usable (VALUE object, CArray *ca, int writable)
204
+ verify_usable (VALUE klass, CArray *ca, int writable)
206
205
  {
207
206
  if ( ca->data_type == CA_OBJECT ) {
208
207
  rb_raise(rb_eArgError, "object arrays hold Ruby values, not numbers");
209
208
  }
210
209
  if ( writable && ca_is_readonly(ca) ) {
211
- rb_raise(rb_eRuntimeError, "%"PRIsVALUE" is read-only",
212
- rb_obj_class(object));
210
+ /* read-only-check: names which array a basis was asked to write through */
211
+ rb_raise(rb_eRuntimeError, "%"PRIsVALUE" is read-only", klass);
213
212
  }
214
213
  }
215
214
 
@@ -237,19 +236,87 @@ folds_to_an_entity (CArray *ca)
237
236
  its neighbour. A per-cell kernel writes cells independently and cannot
238
237
  express that. Checked for every tier: a box transfer moves the same
239
238
  mask, and the neighbour is marked when it is written back. */
240
- static void
241
- verify_mask_cells (VALUE object, CArray *ca)
239
+ static int
240
+ mask_cells_split (CArray *ca)
242
241
  {
243
242
  CArray *root;
244
243
  ca_size_t strides[CA_RANK_MAX];
245
244
  ca_size_t base = 0;
246
- if ( ! ca->mask || ! ca_is_stride_family(ca) ) return;
245
+ if ( ! ca->mask || ! ca_is_stride_family(ca) ) return 0;
247
246
  ca_stride_compose_to_root((CAStride *) ca, &root, strides, &base);
248
- if ( ca->bytes != root->bytes ) {
247
+ return ca->bytes != root->bytes;
248
+ }
249
+
250
+ static void
251
+ verify_mask_cells (VALUE klass, CArray *ca)
252
+ {
253
+ if ( mask_cells_split(ca) ) {
249
254
  rb_raise(rb_eArgError,
250
255
  "%"PRIsVALUE" reinterprets the element size and carries a mask; "
251
256
  "its mask cells do not map one to one onto the parent's",
252
- rb_obj_class(object));
257
+ klass);
258
+ }
259
+ }
260
+
261
+ /* Why an array would be refused for reading, or NULL. */
262
+ static const char *
263
+ read_refusal (CArray *ca)
264
+ {
265
+ if ( ca->data_type == CA_OBJECT ) {
266
+ return "object arrays hold Ruby values, not numbers";
267
+ }
268
+ if ( mask_cells_split(ca) ) {
269
+ return "it reinterprets the element size and carries a mask";
270
+ }
271
+ return NULL;
272
+ }
273
+
274
+ /* CArray::AddressBasis::Failure. An exception raised while the arrays are
275
+ checked, lent or taken back is extended with it; one raised by the block
276
+ they are lent to is not. Something that hands the arrays to code of its
277
+ own can then tell a refusal or a failing array (a CAObject whose backing
278
+ cannot be read) from a fault of that code. */
279
+ static VALUE mFailure;
280
+
281
+ static void
282
+ mark_failure (VALUE error)
283
+ {
284
+ if ( RTEST(rb_obj_is_kind_of(error, rb_eException)) && ! OBJ_FROZEN(error) ) {
285
+ rb_extend_object(error, mFailure);
286
+ }
287
+ }
288
+
289
+ typedef struct {
290
+ VALUE klass;
291
+ CArray *ca;
292
+ int writable;
293
+ } verify_args;
294
+
295
+ static VALUE
296
+ verify_array_body (VALUE argument)
297
+ {
298
+ verify_args *a = (verify_args *) argument;
299
+ /* A view takes its mask from its parent when first asked for it; one
300
+ added to the parent after the view was made is not on ca->mask yet. */
301
+ ca_update_mask(a->ca);
302
+ verify_usable(a->klass, a->ca, a->writable);
303
+ verify_mask_cells(a->klass, a->ca);
304
+ return Qnil;
305
+ }
306
+
307
+ static void
308
+ verify_array (VALUE klass, CArray *ca, int writable)
309
+ {
310
+ verify_args a;
311
+ int tag = 0;
312
+ a.klass = klass;
313
+ a.ca = ca;
314
+ a.writable = writable;
315
+ /* protect: marks the exception as a Failure and raises it again; nothing is held */
316
+ rb_protect(verify_array_body, (VALUE) &a, &tag);
317
+ if ( tag ) {
318
+ mark_failure(rb_errinfo());
319
+ rb_jump_tag(tag);
253
320
  }
254
321
  }
255
322
 
@@ -390,7 +457,9 @@ open_body (VALUE argument)
390
457
  rb_ary_push(state->bases, basis);
391
458
  }
392
459
  {
393
- VALUE result = rb_yield(state->bases);
460
+ VALUE result;
461
+ state->lent = 1;
462
+ result = rb_yield(state->bases);
394
463
  state->body_done = 1;
395
464
  return result;
396
465
  }
@@ -456,16 +525,27 @@ write_back (VALUE argument)
456
525
  write-back that raises -- the array's own transfer slot failing -- is
457
526
  set aside so that every other region is still written and freed and
458
527
  every root detached; the block's own exception, if it raised, wins. */
528
+ static VALUE
529
+ sync_root (VALUE root)
530
+ {
531
+ ca_sync((CArray *) root);
532
+ return Qnil;
533
+ }
534
+
459
535
  static VALUE
460
536
  open_ensure (VALUE argument)
461
537
  {
462
538
  open_state *state = (open_state *) argument;
463
539
  int i;
540
+ /* Raised before the block was entered: by lending an array */
541
+ if ( ! state->lent ) mark_failure(rb_errinfo());
464
542
  for ( i = state->slots - 1; i >= 0; i-- ) {
465
543
  CArray *ca = state->carrays[i];
466
544
  if ( ca == NULL ) continue;
467
- /* A tier-1 or tier-2 basis addresses the root's own memory, so a write is
468
- already where it belongs. Only a region buffer has to be sent back. */
545
+ /* A tier-1 or tier-2 basis addresses the root's own memory; the root is
546
+ synced before it is detached, for the entity that keeps its cells
547
+ elsewhere while attached (the mask of a CAObject that has mask
548
+ hooks). A region buffer is sent back cell by cell. */
469
549
  if ( state->region[i] ) {
470
550
  ca_size_t elements = 1;
471
551
  int8_t k;
@@ -483,6 +563,7 @@ open_ensure (VALUE argument)
483
563
  w.steps = steps;
484
564
  w.data = state->region[i];
485
565
  w.read = state->region[i] + elements * ca->bytes;
566
+ /* protect: keeps the error so that every region is written back first */
486
567
  rb_protect(write_back, (VALUE) &w, &tag);
487
568
  if ( tag ) {
488
569
  if ( NIL_P(state->put_error) ) {
@@ -494,12 +575,24 @@ open_ensure (VALUE argument)
494
575
  xfree(state->region[i]);
495
576
  }
496
577
  if ( state->attached_root[i] ) {
578
+ if ( ! ca_is_readonly(state->roots[i]) ) {
579
+ int tag = 0;
580
+ /* protect: keeps the error so that every root is detached first */
581
+ rb_protect(sync_root, (VALUE) state->roots[i], &tag);
582
+ if ( tag ) {
583
+ if ( NIL_P(state->put_error) ) {
584
+ state->put_error = rb_errinfo();
585
+ }
586
+ rb_set_errinfo(Qnil);
587
+ }
588
+ }
497
589
  ca_detach(state->roots[i]);
498
590
  }
499
591
  }
500
592
  xfree(state->block);
501
593
  if ( state->body_done && RTEST(rb_obj_is_kind_of(state->put_error,
502
594
  rb_eException)) ) {
595
+ mark_failure(state->put_error);
503
596
  rb_exc_raise(state->put_error);
504
597
  }
505
598
  return Qnil;
@@ -568,15 +661,100 @@ packed_body (VALUE argument)
568
661
  for ( k = 0; k < ca->ndim; k++ ) *mask_stride_slot++ = 0;
569
662
  }
570
663
  }
664
+ /* The slots point into the four Strings, and acquire_basis allocates:
665
+ a String the stack holds is pinned, so compaction leaves it be. */
666
+ RB_GC_GUARD(pointers);
667
+ RB_GC_GUARD(strides);
668
+ RB_GC_GUARD(mask_pointers);
669
+ RB_GC_GUARD(mask_strides);
571
670
 
572
671
  {
573
- VALUE result = rb_yield_values(4, pointers, strides, mask_pointers,
574
- mask_strides);
672
+ VALUE result;
673
+ state->lent = 1;
674
+ result = rb_yield_values(4, pointers, strides, mask_pointers, mask_strides);
575
675
  state->body_done = 1;
576
676
  return result;
577
677
  }
578
678
  }
579
679
 
680
+ /* Allocates the state for n arrays and fills it: the arrays and their masks,
681
+ their tiers, which ones are written, and the boxes. The caller has
682
+ checked every argument already: nothing here raises, so a refusal never
683
+ finds a block to free. `box_start` / `box_count` may be NULL (every
684
+ array whole), and an entry of them may be NULL (that array whole). */
685
+ static void
686
+ ab_prepare (open_state *state, int n, CArray **arrays, const int *writable,
687
+ const ca_size_t *const *box_start, const ca_size_t *const *box_count)
688
+ {
689
+ int i;
690
+
691
+ state->count = n;
692
+ state->slots = n * 2;
693
+ /* One allocation, carved up. Every array here is the same length in
694
+ slots and lives exactly as long as the open, so there is nothing for
695
+ separate lifetimes to buy -- and this is a per-call cost on a path
696
+ whose whole point is to be cheap enough to cross often. Zeroed once
697
+ as a block, which is also what the slots want to start as: a NULL
698
+ array, a NULL root, not writable, not attached, no region, no box. */
699
+ {
700
+ size_t m = (size_t) state->slots + 1;
701
+ size_t pointers = ROUND_UP_8(m * sizeof(CArray *));
702
+ size_t chars = ROUND_UP_8(m * sizeof(char *));
703
+ size_t sizes = ROUND_UP_8(m * CA_RANK_MAX * sizeof(ca_size_t));
704
+ size_t ints = ROUND_UP_8(m * sizeof(int));
705
+ size_t bases = ROUND_UP_8((size_t) (n + 1) * sizeof(ca_address_basis_t));
706
+ size_t total = 2 * pointers + chars + 4 * sizes + 4 * ints + bases;
707
+ char *p;
708
+ state->block = ALLOC_N(char, total);
709
+ MEMZERO(state->block, char, total);
710
+ p = state->block;
711
+ state->carrays = (CArray **) p; p += pointers;
712
+ state->roots = (CArray **) p; p += pointers;
713
+ state->region = (char **) p; p += chars;
714
+ state->region_start = (ca_size_t *) p; p += sizes;
715
+ state->region_count = (ca_size_t *) p; p += sizes;
716
+ state->box_start = (ca_size_t *) p; p += sizes;
717
+ state->box_count = (ca_size_t *) p; p += sizes;
718
+ state->tier = (int *) p; p += ints;
719
+ state->writable = (int *) p; p += ints;
720
+ state->attached_root = (int *) p; p += ints;
721
+ state->has_box = (int *) p; p += ints;
722
+ state->c_bases = (ca_address_basis_t *) p;
723
+ }
724
+ state->lent = 0;
725
+ state->body_done = 0;
726
+ state->put_error = Qnil;
727
+
728
+ for ( i = 0; i < n; i++ ) {
729
+ CArray *ca = arrays[i];
730
+ state->carrays[i] = ca;
731
+ state->writable[i] = writable ? writable[i] : 0;
732
+ state->tier[i] = tier_for(ca);
733
+ if ( box_start && box_start[i] ) {
734
+ state->has_box[i] = 1;
735
+ memcpy(&state->box_start[i * CA_RANK_MAX], box_start[i],
736
+ ca->ndim * sizeof(ca_size_t));
737
+ memcpy(&state->box_count[i * CA_RANK_MAX], box_count[i],
738
+ ca->ndim * sizeof(ca_size_t));
739
+ }
740
+ if ( ca->mask ) {
741
+ CArray *mask = ca->mask;
742
+ state->carrays[n + i] = mask;
743
+ state->writable[n + i] = state->writable[i];
744
+ state->tier[n + i] = tier_for(mask);
745
+ /* The mask is the array's shape, so the array's box is its box: a
746
+ tier-3 mask moves the cells the kernel asked for, not all of them. */
747
+ if ( state->has_box[i] ) {
748
+ state->has_box[n + i] = 1;
749
+ memcpy(&state->box_start[(n + i) * CA_RANK_MAX], box_start[i],
750
+ ca->ndim * sizeof(ca_size_t));
751
+ memcpy(&state->box_count[(n + i) * CA_RANK_MAX], box_count[i],
752
+ ca->ndim * sizeof(ca_size_t));
753
+ }
754
+ }
755
+ }
756
+ }
757
+
580
758
  /*
581
759
  * Opens every array, yields what it opened, and closes them all on the way
582
760
  * out -- including when the block raises.
@@ -591,7 +769,7 @@ address_basis_open (int argc, VALUE *argv, VALUE module)
591
769
  {
592
770
  VALUE arrays, writable_flags, box_start, box_count, packed;
593
771
  open_state state;
594
- int i;
772
+ int i, n;
595
773
 
596
774
  rb_scan_args(argc, argv, "23", &arrays, &writable_flags, &box_start,
597
775
  &box_count, &packed);
@@ -611,73 +789,57 @@ address_basis_open (int argc, VALUE *argv, VALUE module)
611
789
  if ( RARRAY_LEN(arrays) != RARRAY_LEN(writable_flags) ) {
612
790
  rb_raise(rb_eArgError, "one writable flag per array is required");
613
791
  }
792
+ n = (int) RARRAY_LEN(arrays);
614
793
 
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++ ) {
794
+ /* Every argument is checked before the state is allocated: a refusal
795
+ raises, and it must find nothing to free. */
796
+ for ( i = 0; i < n; i++ ) {
618
797
  VALUE object = rb_ary_entry(arrays, i);
619
798
  CArray *ca;
620
799
  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);
800
+ verify_array(rb_obj_class(object), ca, RTEST(rb_ary_entry(writable_flags, i)));
626
801
  verify_box(box_start, box_count, i, ca);
627
802
  }
628
803
 
629
- state.count = (int) RARRAY_LEN(arrays);
630
- state.slots = state.count * 2;
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);
634
- state.bases = rb_ary_new_capa(state.count);
635
- /* One allocation, carved up, rather than eight. Every array here is the
636
- same length in slots and lives exactly as long as the open, so there is
637
- nothing for eight separate lifetimes to buy -- and this is a per-call
638
- cost on a path whose whole point is to be cheap enough to cross often.
639
- Zeroed once as a block, which is also what the slots want to start as:
640
- a NULL array, a NULL root, not writable, not attached, no region. */
641
804
  {
642
- size_t n = (size_t) state.slots + 1;
643
- size_t pointers = ROUND_UP_8(n * sizeof(CArray *));
644
- size_t chars = ROUND_UP_8(n * sizeof(char *));
645
- size_t sizes = ROUND_UP_8(n * CA_RANK_MAX * sizeof(ca_size_t));
646
- size_t ints = ROUND_UP_8(n * sizeof(int));
647
- size_t total = 2 * pointers + chars + 2 * sizes + 3 * ints;
648
- char *p;
649
- state.block = ALLOC_N(char, total);
650
- MEMZERO(state.block, char, total);
651
- p = state.block;
652
- state.carrays = (CArray **) p; p += pointers;
653
- state.roots = (CArray **) p; p += pointers;
654
- state.region = (char **) p; p += chars;
655
- state.region_start = (ca_size_t *) p; p += sizes;
656
- state.region_count = (ca_size_t *) p; p += sizes;
657
- state.tier = (int *) p; p += ints;
658
- state.writable = (int *) p; p += ints;
659
- state.attached_root = (int *) p;
660
- }
661
- state.box_starts = box_start;
662
- state.box_counts = box_count;
663
- state.body_done = 0;
664
- state.put_error = Qnil;
665
-
666
- for ( i = 0; i < state.count; i++ ) {
667
- VALUE object = rb_ary_entry(state.arrays, i);
668
- CArray *ca;
669
- GetCArray(object, ca);
670
- state.carrays[i] = ca;
671
- state.writable[i] = RTEST(rb_ary_entry(writable_flags, i));
672
- state.tier[i] = tier_for(ca);
673
-
674
- if ( ca->mask ) {
675
- CArray *mask = ca->mask;
676
- state.carrays[state.count + i] = mask;
677
- state.writable[state.count + i] = state.writable[i];
678
- state.tier[state.count + i] = tier_for(mask);
805
+ /* The boxes and flags in C form, on the stack: they are copied into
806
+ the state, and nothing below raises before that. */
807
+ /* Laid out widest first, so every part is aligned. */
808
+ VALUE vholder = 0;
809
+ CArray **carrays = (CArray **) ALLOCV(vholder, (size_t) n * (sizeof(CArray *)
810
+ + 2 * sizeof(ca_size_t *)
811
+ + 2 * CA_RANK_MAX * sizeof(ca_size_t)
812
+ + sizeof(int)) + 1);
813
+ const ca_size_t **bs = (const ca_size_t **) (carrays + n);
814
+ const ca_size_t **bc = bs + n;
815
+ ca_size_t *cells = (ca_size_t *) (bc + n);
816
+ int *flags = (int *) (cells + (size_t) n * 2 * CA_RANK_MAX);
817
+ for ( i = 0; i < n; i++ ) {
818
+ VALUE object = rb_ary_entry(arrays, i);
819
+ VALUE starts = NIL_P(box_start) ? Qnil : rb_ary_entry(box_start, i);
820
+ VALUE counts = NIL_P(box_count) ? Qnil : rb_ary_entry(box_count, i);
821
+ int8_t k;
822
+ GetCArray(object, carrays[i]);
823
+ flags[i] = RTEST(rb_ary_entry(writable_flags, i));
824
+ bs[i] = bc[i] = NULL;
825
+ if ( ! NIL_P(starts) ) {
826
+ ca_size_t *s0 = cells + (size_t) i * 2 * CA_RANK_MAX;
827
+ ca_size_t *c0 = s0 + CA_RANK_MAX;
828
+ for ( k = 0; k < carrays[i]->ndim; k++ ) {
829
+ s0[k] = NUM2LL(rb_ary_entry(starts, k));
830
+ c0[k] = NUM2LL(rb_ary_entry(counts, k));
831
+ }
832
+ bs[i] = s0;
833
+ bc[i] = c0;
834
+ }
679
835
  }
836
+ ab_prepare(&state, n, carrays, flags, bs, bc);
837
+ ALLOCV_END(vholder);
680
838
  }
839
+ /* Our own copy: the caller's Array can be emptied inside the block, and
840
+ the arrays it held are attached until open_ensure has run. */
841
+ state.arrays = rb_ary_dup(arrays);
842
+ state.bases = rb_ary_new_capa(n);
681
843
 
682
844
  {
683
845
  VALUE result = RTEST(packed)
@@ -688,6 +850,81 @@ address_basis_open (int argc, VALUE *argv, VALUE module)
688
850
  }
689
851
  }
690
852
 
853
+ /* The body of ca_with_address_bases: lends each array's basis, then runs
854
+ the caller's body on them. */
855
+ static VALUE
856
+ c_body (VALUE argument)
857
+ {
858
+ open_state *state = (open_state *) argument;
859
+ int i;
860
+ for ( i = 0; i < state->count; i++ ) {
861
+ ca_address_basis_t *b = &state->c_bases[i];
862
+ b->ptr = acquire_basis(state, i, b->strides);
863
+ b->writable = state->writable[i];
864
+ if ( state->carrays[state->count + i] ) {
865
+ b->mask = (boolean8_t *) acquire_basis(state, state->count + i,
866
+ b->mask_strides);
867
+ }
868
+ }
869
+ {
870
+ VALUE result;
871
+ state->lent = 1;
872
+ result = state->c_body(state->c_bases, state->c_arg);
873
+ state->body_done = 1;
874
+ return result;
875
+ }
876
+ }
877
+
878
+ VALUE
879
+ ca_with_address_bases (int32_t n, CArray **arrays, const int *writable,
880
+ const ca_size_t *const *box_start,
881
+ const ca_size_t *const *box_count,
882
+ VALUE (*body)(ca_address_basis_t *bases, VALUE arg),
883
+ VALUE arg)
884
+ {
885
+ open_state state;
886
+ int32_t i;
887
+ int8_t k;
888
+
889
+ if ( n < 0 || (n > 0 && arrays == NULL) || body == NULL ) {
890
+ rb_raise(rb_eArgError, "ca_with_address_bases: no arrays or no body");
891
+ }
892
+ if ( (box_start == NULL) != (box_count == NULL) ) {
893
+ rb_raise(rb_eArgError, "a region needs both starts and counts");
894
+ }
895
+ /* Every argument is checked before the state is allocated. */
896
+ for ( i = 0; i < n; i++ ) {
897
+ CArray *ca = arrays[i];
898
+ VALUE klass;
899
+ if ( ca == NULL ) {
900
+ rb_raise(rb_eArgError, "ca_with_address_bases: array %d is NULL", (int) i);
901
+ }
902
+ /* a CAObject (a Face among them) is named by its own class */
903
+ klass = ca_is_caobject(ca) ? rb_obj_class(((CAObject *) ca)->self)
904
+ : ca_class[ca->obj_type];
905
+ verify_array(klass, ca, writable ? writable[i] : 0);
906
+ if ( box_start && ((box_start[i] == NULL) != (box_count[i] == NULL)) ) {
907
+ rb_raise(rb_eArgError, "a region needs both starts and counts");
908
+ }
909
+ if ( box_start && box_start[i] ) {
910
+ for ( k = 0; k < ca->ndim; k++ ) {
911
+ if ( outside_axis(box_start[i][k], box_count[i][k], ca->dim[k]) ) {
912
+ rb_raise(rb_eArgError,
913
+ "the requested region falls outside the array on axis %d",
914
+ (int) k);
915
+ }
916
+ }
917
+ }
918
+ }
919
+
920
+ ab_prepare(&state, (int) n, arrays, writable, box_start, box_count);
921
+ state.arrays = Qnil;
922
+ state.bases = Qnil;
923
+ state.c_body = body;
924
+ state.c_arg = arg;
925
+ return rb_ensure(c_body, (VALUE) &state, open_ensure, (VALUE) &state);
926
+ }
927
+
691
928
  /* Reports how an array would be opened, without opening it. */
692
929
  static VALUE
693
930
  address_basis_classify (VALUE module, VALUE object)
@@ -697,6 +934,7 @@ address_basis_classify (VALUE module, VALUE object)
697
934
  int tier;
698
935
 
699
936
  GetCArray(object, ca);
937
+ ca_update_mask(ca);
700
938
  tier = tier_for(ca);
701
939
 
702
940
  result = rb_hash_new();
@@ -709,9 +947,24 @@ address_basis_classify (VALUE module, VALUE object)
709
947
  rb_hash_aset(result, ID2SYM(rb_intern("dim")), size_array(ca->dim, ca->ndim));
710
948
  rb_hash_aset(result, ID2SYM(rb_intern("bytes")), LL2NUM((long long) ca->bytes));
711
949
  rb_hash_aset(result, ID2SYM(rb_intern("data_type")), INT2NUM(ca->data_type));
950
+ {
951
+ const char *refusal = read_refusal(ca);
952
+ rb_hash_aset(result, ID2SYM(rb_intern("refusal")),
953
+ refusal ? rb_str_new_cstr(refusal) : Qnil);
954
+ }
712
955
  return result;
713
956
  }
714
957
 
958
+ /* The tier alone, for a caller that only wants to know which of the three
959
+ an array would be opened at: classify builds a Hash to say it. */
960
+ static VALUE
961
+ address_basis_tier (VALUE module, VALUE object)
962
+ {
963
+ CArray *ca;
964
+ GetCArray(object, ca);
965
+ return INT2NUM(tier_for(ca));
966
+ }
967
+
715
968
  /* ------------------------------------------------------------------- */
716
969
  /* Init_carray_address_basis */
717
970
  /* ------------------------------------------------------------------- */
@@ -724,8 +977,11 @@ Init_carray_address_basis (void)
724
977
  rb_define_singleton_method(mAddressBasis, "open", address_basis_open, -1);
725
978
  rb_define_singleton_method(mAddressBasis, "classify",
726
979
  address_basis_classify, 1);
980
+ rb_define_singleton_method(mAddressBasis, "tier", address_basis_tier, 1);
727
981
 
728
982
  rb_define_const(mAddressBasis, "TIER_ENTITY", INT2NUM(TIER_ENTITY));
729
983
  rb_define_const(mAddressBasis, "TIER_STRIDE", INT2NUM(TIER_STRIDE));
730
984
  rb_define_const(mAddressBasis, "TIER_XFER", INT2NUM(TIER_XFER));
985
+
986
+ mFailure = rb_define_module_under(mAddressBasis, "Failure");
731
987
  }