carray 3.0.2 → 3.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +568 -0
  3. data/README.md +6 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +220 -0
  7. data/ext/ca_axis_group.c +201 -123
  8. data/ext/ca_binop_dispatch.c +46 -52
  9. data/ext/ca_binop_dispatch.h +1 -1
  10. data/ext/ca_categorical_iterator.c +249 -183
  11. data/ext/ca_compare.c +7 -12
  12. data/ext/ca_for_buffer.h +21 -68
  13. data/ext/ca_group_iter.c +38 -23
  14. data/ext/ca_kernel_iterator.c +1765 -2076
  15. data/ext/ca_kernel_iterator.h +584 -566
  16. data/ext/ca_monop_dispatch.c +86 -63
  17. data/ext/ca_monop_dispatch.h +5 -0
  18. data/ext/ca_obj_array.c +223 -56
  19. data/ext/ca_obj_bincmp.c +172 -138
  20. data/ext/ca_obj_binop.c +116 -230
  21. data/ext/ca_obj_bitarray.c +40 -26
  22. data/ext/ca_obj_bitfield.c +100 -68
  23. data/ext/ca_obj_block.c +19 -9
  24. data/ext/ca_obj_byte_swap.c +45 -46
  25. data/ext/ca_obj_const_string.c +269 -44
  26. data/ext/ca_obj_face.c +612 -89
  27. data/ext/ca_obj_face.h +111 -47
  28. data/ext/ca_obj_fake.c +36 -79
  29. data/ext/ca_obj_farray.c +7 -3
  30. data/ext/ca_obj_field.c +33 -8
  31. data/ext/ca_obj_fixlen_string.c +23 -24
  32. data/ext/ca_obj_grid.c +541 -210
  33. data/ext/ca_obj_meld.c +331 -111
  34. data/ext/ca_obj_moncmp.c +80 -65
  35. data/ext/ca_obj_monop.c +177 -149
  36. data/ext/ca_obj_object.c +664 -248
  37. data/ext/ca_obj_record.c +63 -23
  38. data/ext/ca_obj_reduce.c +18 -16
  39. data/ext/ca_obj_refer.c +140 -58
  40. data/ext/ca_obj_remap.c +85 -24
  41. data/ext/ca_obj_repeat.c +24 -9
  42. data/ext/ca_obj_roll.c +121 -16
  43. data/ext/ca_obj_select.c +61 -58
  44. data/ext/ca_obj_select_axis.c +73 -107
  45. data/ext/ca_obj_shift.c +32 -102
  46. data/ext/ca_obj_stack.c +372 -122
  47. data/ext/ca_obj_stride.c +348 -140
  48. data/ext/ca_obj_string.c +88 -22
  49. data/ext/ca_obj_tile.c +38 -21
  50. data/ext/ca_obj_time.c +45 -31
  51. data/ext/ca_obj_timedelta.c +43 -29
  52. data/ext/ca_obj_transpose.c +17 -6
  53. data/ext/ca_obj_triop.c +125 -141
  54. data/ext/ca_obj_window.c +330 -44
  55. data/ext/ca_op_ipower.c +24 -11
  56. data/ext/ca_op_powi.h +57 -30
  57. data/ext/ca_pow5_table.h +656 -0
  58. data/ext/ca_sort_kernels.h +70 -0
  59. data/ext/ca_sweep_engine.c +104 -81
  60. data/ext/ca_sweep_engine.h +7 -20
  61. data/ext/ca_triop_dispatch.c +4 -1
  62. data/ext/ca_triop_dispatch.h +5 -2
  63. data/ext/caframe_csv_reader.c +542 -0
  64. data/ext/carray.h +241 -92
  65. data/ext/carray_access.c +608 -276
  66. data/ext/carray_address_basis.c +494 -97
  67. data/ext/carray_attribute.c +129 -4
  68. data/ext/carray_bincount.c +39 -7
  69. data/ext/carray_broadcast.c +56 -19
  70. data/ext/carray_build_flags.h +1 -1
  71. data/ext/carray_call_cfunc.c +108 -28
  72. data/ext/carray_cast.c +1576 -223
  73. data/ext/carray_cast_func.rb +77 -24
  74. data/ext/carray_class.c +4 -3
  75. data/ext/carray_conversion.c +208 -75
  76. data/ext/carray_copy.c +82 -24
  77. data/ext/carray_core.c +646 -162
  78. data/ext/carray_count.c +171 -88
  79. data/ext/carray_element.c +94 -60
  80. data/ext/carray_factorize.c +670 -324
  81. data/ext/carray_generate.c +260 -83
  82. data/ext/carray_histogram.c +209 -106
  83. data/ext/carray_hold.c +14 -16
  84. data/ext/carray_index_classifier.c +422 -243
  85. data/ext/carray_internal.h +692 -3
  86. data/ext/carray_kernels_bincmp.c +7 -0
  87. data/ext/carray_kernels_binop.c +487 -120
  88. data/ext/carray_kernels_init.c +653 -50
  89. data/ext/carray_kernels_moncmp.c +15 -8
  90. data/ext/carray_kernels_monop.c +99 -43
  91. data/ext/carray_kernels_reduce_aggregate.c +3925 -6112
  92. data/ext/carray_kernels_reduce_boolean.c +112 -36
  93. data/ext/carray_kernels_reduce_cumulative.c +2127 -6903
  94. data/ext/carray_kernels_reduce_extreme.c +3032 -2892
  95. data/ext/carray_kernels_reduce_variance.c +439 -500
  96. data/ext/carray_kernels_scan.c +1097 -488
  97. data/ext/carray_kernels_search.c +3787 -2176
  98. data/ext/carray_kernels_sort.c +1200 -920
  99. data/ext/carray_kernels_triop.c +479 -7
  100. data/ext/carray_key_windows.c +216 -0
  101. data/ext/carray_lazy.c +967 -153
  102. data/ext/carray_loop.c +81 -65
  103. data/ext/carray_mask.c +910 -324
  104. data/ext/carray_math_kernel.h +1 -0
  105. data/ext/carray_median_percentile.c +299 -100
  106. data/ext/carray_memory_view.c +872 -758
  107. data/ext/carray_operator.c +1171 -364
  108. data/ext/carray_order.c +505 -133
  109. data/ext/carray_partition.c +112 -65
  110. data/ext/carray_random.c +327 -239
  111. data/ext/carray_round.c +273 -0
  112. data/ext/carray_scatter.c +346 -78
  113. data/ext/carray_segment.c +257 -0
  114. data/ext/carray_slab.c +249 -86
  115. data/ext/carray_sort.c +224 -227
  116. data/ext/carray_sort_kernel.c +8 -1
  117. data/ext/carray_struct.c +118 -39
  118. data/ext/carray_test.c +193 -69
  119. data/ext/carray_time_text.c +1182 -0
  120. data/ext/carray_undef.c +4 -0
  121. data/ext/carray_utils.c +579 -113
  122. data/ext/extconf.rb +27 -2
  123. data/ext/mk_call_cfunc.rb +19 -4
  124. data/ext/mk_pow5_table.rb +35 -0
  125. data/ext/mkkernel.rb +1202 -1014
  126. data/ext/ruby_carray.c +17 -5
  127. data/ext/version.h +4 -4
  128. data/ext/version.rb +4 -2
  129. data/lib/carray/attribute.rb +160 -17
  130. data/lib/carray/autoload_carray.rb +23 -27
  131. data/lib/carray/autoload_method_extension.rb +20 -5
  132. data/lib/carray/axis_group.rb +360 -63
  133. data/lib/carray/basics.rb +162 -28
  134. data/lib/carray/bincount_nd.rb +18 -2
  135. data/lib/carray/block_iterator.rb +231 -58
  136. data/lib/carray/boolean_reduce.rb +34 -42
  137. data/lib/carray/categorical.rb +242 -52
  138. data/lib/carray/categorical_iterator.rb +437 -827
  139. data/lib/carray/complex.rb +41 -34
  140. data/lib/carray/conditional.rb +150 -14
  141. data/lib/carray/const_string.rb +59 -4
  142. data/lib/carray/construct.rb +15 -11
  143. data/lib/carray/data_type_extension.rb +1 -0
  144. data/lib/carray/distance_limit.rb +77 -0
  145. data/lib/carray/fixlen_string.rb +7 -1
  146. data/lib/carray/frame/column_verbs.rb +70 -0
  147. data/lib/carray/frame/concat.rb +157 -6
  148. data/lib/carray/frame/convert.rb +82 -7
  149. data/lib/carray/frame/csv_parser.rb +688 -87
  150. data/lib/carray/frame/describe.rb +80 -0
  151. data/lib/carray/frame/frame.rb +369 -45
  152. data/lib/carray/frame/group.rb +631 -90
  153. data/lib/carray/frame/io.rb +506 -79
  154. data/lib/carray/frame/join.rb +411 -62
  155. data/lib/carray/frame/records.rb +116 -19
  156. data/lib/carray/frame/reshape.rb +455 -0
  157. data/lib/carray/frame/sort.rb +30 -18
  158. data/lib/carray/frame/verbs.rb +868 -61
  159. data/lib/carray/frame/windows.rb +117 -0
  160. data/lib/carray/frame.rb +4 -0
  161. data/lib/carray/fuse_source.rb +118 -2
  162. data/lib/carray/fusion.rb +257 -42
  163. data/lib/carray/histogram.rb +6 -1
  164. data/lib/carray/inspect.rb +54 -13
  165. data/lib/carray/iterator.rb +99 -9
  166. data/lib/carray/key_windows.rb +620 -0
  167. data/lib/carray/lazy.rb +216 -193
  168. data/lib/carray/mask_gap_fill.rb +112 -92
  169. data/lib/carray/math.rb +32 -19
  170. data/lib/carray/meld_reduce.rb +52 -21
  171. data/lib/carray/methods/align_addr.rb +8 -4
  172. data/lib/carray/methods/bin.rb +26 -18
  173. data/lib/carray/methods/bincount.rb +18 -2
  174. data/lib/carray/methods/bit_string.rb +15 -4
  175. data/lib/carray/methods/choose.rb +16 -3
  176. data/lib/carray/methods/composition.rb +18 -10
  177. data/lib/carray/methods/factorize.rb +2 -2
  178. data/lib/carray/methods/gather_nd.rb +25 -5
  179. data/lib/carray/methods/insert_block.rb +9 -14
  180. data/lib/carray/methods/is_between.rb +59 -0
  181. data/lib/carray/methods/is_in.rb +140 -10
  182. data/lib/carray/methods/join.rb +2 -7
  183. data/lib/carray/methods/lag.rb +49 -0
  184. data/lib/carray/methods/locate_addr.rb +182 -45
  185. data/lib/carray/methods/mode.rb +18 -1
  186. data/lib/carray/methods/nunique.rb +11 -6
  187. data/lib/carray/methods/repeat.rb +1 -20
  188. data/lib/carray/methods/resize.rb +21 -23
  189. data/lib/carray/methods/snap.rb +34 -12
  190. data/lib/carray/methods/unique.rb +11 -2
  191. data/lib/carray/methods/value_counts.rb +4 -11
  192. data/lib/carray/record_order.rb +208 -0
  193. data/lib/carray/rng.rb +3 -0
  194. data/lib/carray/runtime.rb +65 -17
  195. data/lib/carray/segment_iterator.rb +1221 -0
  196. data/lib/carray/serialize.rb +214 -30
  197. data/lib/carray/slab_iterator.rb +101 -27
  198. data/lib/carray/stack.rb +10 -4
  199. data/lib/carray/string.rb +105 -5
  200. data/lib/carray/string_operation_extension.rb +12 -4
  201. data/lib/carray/struct.rb +137 -58
  202. data/lib/carray/struct_builder.rb +124 -17
  203. data/lib/carray/time.rb +1222 -186
  204. data/lib/carray/window_iterator.rb +242 -81
  205. data/lib/carray.rb +9 -2
  206. data/yard-stubs/ca_obj_array.rb +4 -4
  207. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  208. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  209. data/yard-stubs/ca_obj_fake.rb +1 -1
  210. data/yard-stubs/ca_obj_field.rb +5 -1
  211. data/yard-stubs/ca_obj_grid.rb +21 -10
  212. data/yard-stubs/ca_obj_refer.rb +5 -4
  213. data/yard-stubs/ca_obj_roll.rb +2 -3
  214. data/yard-stubs/ca_obj_shift.rb +5 -1
  215. data/yard-stubs/ca_obj_stride.rb +10 -7
  216. data/yard-stubs/ca_obj_tile.rb +1 -1
  217. data/yard-stubs/ca_obj_transpose.rb +1 -1
  218. data/yard-stubs/ca_obj_window.rb +9 -5
  219. data/yard-stubs/carray_access.rb +11 -5
  220. data/yard-stubs/carray_attribute.rb +0 -5
  221. data/yard-stubs/carray_broadcast.rb +1 -1
  222. data/yard-stubs/carray_cast.rb +88 -43
  223. data/yard-stubs/carray_conversion.rb +15 -2
  224. data/yard-stubs/carray_copy.rb +12 -5
  225. data/yard-stubs/carray_core.rb +3 -3
  226. data/yard-stubs/carray_count.rb +9 -0
  227. data/yard-stubs/carray_element.rb +10 -4
  228. data/yard-stubs/carray_generate.rb +21 -1
  229. data/yard-stubs/carray_lazy.rb +5 -1
  230. data/yard-stubs/carray_loop.rb +4 -1
  231. data/yard-stubs/carray_mask.rb +60 -12
  232. data/yard-stubs/carray_math.rb +52 -14
  233. data/yard-stubs/carray_median_percentile.rb +15 -8
  234. data/yard-stubs/carray_memory_view.rb +40 -23
  235. data/yard-stubs/carray_order.rb +61 -29
  236. data/yard-stubs/carray_random.rb +13 -4
  237. data/yard-stubs/carray_scatter.rb +21 -9
  238. data/yard-stubs/carray_segment.rb +72 -0
  239. data/yard-stubs/carray_slab.rb +10 -2
  240. data/yard-stubs/carray_sort.rb +49 -8
  241. data/yard-stubs/carray_test.rb +15 -3
  242. data/yard-stubs/carray_utils.rb +0 -26
  243. metadata +19 -5
  244. data/ext/ca_for_each_element.h +0 -239
  245. data/ext/carray_kernels_map.c +0 -3467
  246. data/lib/carray/arrow_tensor.rb +0 -401
  247. data/lib/carray/table.rb +0 -108
@@ -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
@@ -77,8 +78,16 @@ typedef struct {
77
78
  ca_size_t *region_start; /* count * CA_RANK_MAX */
78
79
  ca_size_t *region_count;
79
80
  VALUE bases;
80
- VALUE box_starts; /* per array, per axis; nil for "all of it" */
81
- 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 */
85
+ int body_done; /* the block returned without raising */
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;
82
91
  } open_state;
83
92
 
84
93
  /* The view's own row-major byte layout, which is the address space
@@ -94,6 +103,15 @@ native_steps (CArray *ca, ca_size_t *steps)
94
103
  }
95
104
  }
96
105
 
106
+ /* Whether [start, start + count) leaves [0, dim). Written so that no sum
107
+ is formed: a start and a count near the top of the integer range add up
108
+ to a negative number, which `start + count > dim` lets through. */
109
+ static int
110
+ outside_axis (ca_size_t start, ca_size_t count, ca_size_t dim)
111
+ {
112
+ return start < 0 || count < 0 || start > dim || count > dim - start;
113
+ }
114
+
97
115
  /* Checks one array's box description, all of it: that it is one start and one
98
116
  count per axis, that they are numbers, and that the box they describe is
99
117
  inside the array.
@@ -124,39 +142,34 @@ verify_box (VALUE box_starts, VALUE box_counts, int index, CArray *ca)
124
142
  "this array has %d", (int) ca->ndim);
125
143
  }
126
144
  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] ) {
145
+ ca_size_t start = ca_integer_arg(rb_ary_entry(starts, k), "a region start",
146
+ "CArray::AddressBasis.open");
147
+ ca_size_t count = ca_integer_arg(rb_ary_entry(counts, k), "a region count",
148
+ "CArray::AddressBasis.open");
149
+ if ( outside_axis(start, count, ca->dim[k]) ) {
130
150
  rb_raise(rb_eArgError,
131
151
  "the requested region falls outside the array on axis %d", (int) k);
132
152
  }
133
153
  }
134
154
  }
135
155
 
136
- /* Reads one array's box out of the Ruby-side description, defaulting to the
137
- whole array. verify_box checked it, and checks it again here because this
138
- 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. */
139
159
  static void
140
160
  read_box (open_state *state, int index, CArray *ca,
141
161
  ca_size_t *starts, ca_size_t *counts)
142
162
  {
143
- VALUE per_array_start = Qnil, per_array_count = Qnil;
144
163
  int8_t k;
145
-
146
- if ( ! NIL_P(state->box_starts) ) {
147
- per_array_start = rb_ary_entry(state->box_starts, index);
148
- per_array_count = rb_ary_entry(state->box_counts, index);
149
- }
150
-
151
164
  for ( k = 0; k < ca->ndim; k++ ) {
152
- 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 {
153
169
  starts[k] = 0;
154
170
  counts[k] = ca->dim[k];
155
- } else {
156
- starts[k] = NUM2LL(rb_ary_entry(per_array_start, k));
157
- counts[k] = NUM2LL(rb_ary_entry(per_array_count, k));
158
171
  }
159
- if ( starts[k] < 0 || counts[k] < 0 || starts[k] + counts[k] > ca->dim[k] ) {
172
+ if ( outside_axis(starts[k], counts[k], ca->dim[k]) ) {
160
173
  rb_raise(rb_eArgError,
161
174
  "the requested region falls outside the array on axis %d", (int) k);
162
175
  }
@@ -188,14 +201,14 @@ size_array (ca_size_t *values, int8_t count)
188
201
  /* Refuses what a generated kernel cannot express, rather than letting it
189
202
  produce quietly wrong numbers. */
190
203
  static void
191
- verify_usable (VALUE object, CArray *ca, int writable)
204
+ verify_usable (VALUE klass, CArray *ca, int writable)
192
205
  {
193
206
  if ( ca->data_type == CA_OBJECT ) {
194
207
  rb_raise(rb_eArgError, "object arrays hold Ruby values, not numbers");
195
208
  }
196
209
  if ( writable && ca_is_readonly(ca) ) {
197
- rb_raise(rb_eRuntimeError, "%"PRIsVALUE" is read-only",
198
- 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);
199
212
  }
200
213
  }
201
214
 
@@ -217,6 +230,96 @@ folds_to_an_entity (CArray *ca)
217
230
  return ca_is_entity(root);
218
231
  }
219
232
 
233
+ /* A view that reinterprets the element size -- refer(CA_INT32, ...) over
234
+ a float64 array -- gets a mask of its own shape, but one mask cell of it
235
+ covers a fraction of a parent cell, so writing cell i's mask also marks
236
+ its neighbour. A per-cell kernel writes cells independently and cannot
237
+ express that. Checked for every tier: a box transfer moves the same
238
+ mask, and the neighbour is marked when it is written back. */
239
+ static int
240
+ mask_cells_split (CArray *ca)
241
+ {
242
+ CArray *root;
243
+ ca_size_t strides[CA_RANK_MAX];
244
+ ca_size_t base = 0;
245
+ if ( ! ca->mask || ! ca_is_stride_family(ca) ) return 0;
246
+ ca_stride_compose_to_root((CAStride *) ca, &root, strides, &base);
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) ) {
254
+ rb_raise(rb_eArgError,
255
+ "%"PRIsVALUE" reinterprets the element size and carries a mask; "
256
+ "its mask cells do not map one to one onto the parent's",
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);
320
+ }
321
+ }
322
+
220
323
  static int
221
324
  tier_for (CArray *ca)
222
325
  {
@@ -239,7 +342,7 @@ acquire_basis (open_state *state, int index, ca_size_t *strides)
239
342
  switch ( state->tier[index] ) {
240
343
  case TIER_ENTITY: {
241
344
  CArray *root = ca;
242
- ca_attach(root);
345
+ ca_attach(root); /* window: released by open_ensure */
243
346
  state->roots[index] = root;
244
347
  state->attached_root[index] = 1;
245
348
  row_major_strides(ca, strides);
@@ -250,18 +353,7 @@ acquire_basis (open_state *state, int index, ca_size_t *strides)
250
353
  case TIER_STRIDE: {
251
354
  CArray *root;
252
355
  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);
356
+ ca_attach(root); /* window: released by open_ensure */
265
357
  state->roots[index] = root;
266
358
  state->attached_root[index] = 1;
267
359
  pointer = root->ptr + base;
@@ -287,12 +379,26 @@ acquire_basis (open_state *state, int index, ca_size_t *strides)
287
379
  step *= counts[k];
288
380
  }
289
381
 
290
- buffer = ALLOC_N(char, (elements > 0 ? elements : 1) * ca->bytes);
382
+ /* Recorded before the read: the read can raise, and open_ensure
383
+ frees what state->region holds. The counts stay zero until the
384
+ read is done, so a buffer that was never filled is not written
385
+ back. */
386
+ /* A writable box keeps a second copy of what it read, so that the
387
+ write-back can send only the cells the kernel changed (see
388
+ put_changed). */
389
+ buffer = ALLOC_N(char, (state->writable[index] ? 2 : 1)
390
+ * (elements > 0 ? elements : 1) * ca->bytes);
391
+ for ( k = 0; k < ca->ndim; k++ ) {
392
+ state->region_count[index * CA_RANK_MAX + k] = 0;
393
+ }
394
+ state->region[index] = buffer;
291
395
  if ( elements > 0 ) {
292
396
  ca_xfer_stride(ca, starts, counts, steps, buffer, CA_XFER_GET);
397
+ if ( state->writable[index] ) {
398
+ memcpy(buffer + elements * ca->bytes, buffer, elements * ca->bytes);
399
+ }
293
400
  }
294
401
 
295
- state->region[index] = buffer;
296
402
  for ( k = 0; k < ca->ndim; k++ ) {
297
403
  state->region_start[index * CA_RANK_MAX + k] = starts[k];
298
404
  state->region_count[index * CA_RANK_MAX + k] = counts[k];
@@ -350,20 +456,96 @@ open_body (VALUE argument)
350
456
  }
351
457
  rb_ary_push(state->bases, basis);
352
458
  }
353
- return rb_yield(state->bases);
459
+ {
460
+ VALUE result;
461
+ state->lent = 1;
462
+ result = rb_yield(state->bases);
463
+ state->body_done = 1;
464
+ return result;
465
+ }
466
+ }
467
+
468
+ typedef struct {
469
+ CArray *ca;
470
+ ca_size_t *start;
471
+ ca_size_t *count;
472
+ ca_size_t *steps;
473
+ char *data;
474
+ char *read; /* the box as it was read */
475
+ } write_back_args;
476
+
477
+ /* Sends back the cells whose bytes differ from what was read, one run
478
+ along the last axis at a time. Sending the whole box would also send the
479
+ cells the kernel never wrote, and those do not come back unchanged
480
+ through a view that converts -- a float64 array seen through an int32
481
+ view reads 1.75 as 1 and would be left holding 1.0. It would also
482
+ overwrite what the kernel wrote to the same cells through another basis
483
+ that addresses them in place. */
484
+ static VALUE
485
+ write_back (VALUE argument)
486
+ {
487
+ write_back_args *w = (write_back_args *) argument;
488
+ CArray *ca = w->ca;
489
+ int8_t nd = ca->ndim;
490
+ size_t bytes = ca->bytes;
491
+ ca_size_t inner = w->count[nd - 1];
492
+ ca_size_t outer = 1;
493
+ ca_size_t idx[CA_RANK_MAX], start[CA_RANK_MAX], count[CA_RANK_MAX];
494
+ ca_size_t o, j, e;
495
+ int8_t k;
496
+
497
+ for ( k = 0; k < nd - 1; k++ ) {
498
+ outer *= w->count[k];
499
+ idx[k] = 0;
500
+ count[k] = 1;
501
+ }
502
+ for ( o = 0; o < outer; o++ ) {
503
+ char *row = w->data + o * inner * bytes;
504
+ char *read = w->read + o * inner * bytes;
505
+ for ( k = 0; k < nd - 1; k++ ) start[k] = w->start[k] + idx[k];
506
+ j = 0;
507
+ while ( j < inner ) {
508
+ if ( ! memcmp(row + j * bytes, read + j * bytes, bytes) ) { j++; continue; }
509
+ e = j + 1;
510
+ while ( e < inner && memcmp(row + e * bytes, read + e * bytes, bytes) ) e++;
511
+ start[nd - 1] = w->start[nd - 1] + j;
512
+ count[nd - 1] = e - j;
513
+ ca_xfer_stride(ca, start, count, w->steps, row + j * bytes, CA_XFER_PUT);
514
+ j = e;
515
+ }
516
+ for ( k = nd - 2; k >= 0; k-- ) {
517
+ if ( ++idx[k] < w->count[k] ) break;
518
+ idx[k] = 0;
519
+ }
520
+ }
521
+ return Qnil;
522
+ }
523
+
524
+ /* Closes in reverse order, and runs whether or not the kernel raised. A
525
+ write-back that raises -- the array's own transfer slot failing -- is
526
+ set aside so that every other region is still written and freed and
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;
354
533
  }
355
534
 
356
- /* Closes in reverse order, and runs whether or not the kernel raised. */
357
535
  static VALUE
358
536
  open_ensure (VALUE argument)
359
537
  {
360
538
  open_state *state = (open_state *) argument;
361
539
  int i;
540
+ /* Raised before the block was entered: by lending an array */
541
+ if ( ! state->lent ) mark_failure(rb_errinfo());
362
542
  for ( i = state->slots - 1; i >= 0; i-- ) {
363
543
  CArray *ca = state->carrays[i];
364
544
  if ( ca == NULL ) continue;
365
- /* A tier-1 or tier-2 basis addresses the root's own memory, so a write is
366
- 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. */
367
549
  if ( state->region[i] ) {
368
550
  ca_size_t elements = 1;
369
551
  int8_t k;
@@ -371,19 +553,48 @@ open_ensure (VALUE argument)
371
553
  elements *= state->region_count[i * CA_RANK_MAX + k];
372
554
  }
373
555
  if ( state->writable[i] && elements > 0 ) {
374
- ca_size_t steps[CA_RANK_MAX];
556
+ ca_size_t steps[CA_RANK_MAX];
557
+ write_back_args w;
558
+ int tag = 0;
375
559
  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);
560
+ w.ca = ca;
561
+ w.start = &state->region_start[i * CA_RANK_MAX];
562
+ w.count = &state->region_count[i * CA_RANK_MAX];
563
+ w.steps = steps;
564
+ w.data = state->region[i];
565
+ w.read = state->region[i] + elements * ca->bytes;
566
+ /* protect: keeps the error so that every region is written back first */
567
+ rb_protect(write_back, (VALUE) &w, &tag);
568
+ if ( tag ) {
569
+ if ( NIL_P(state->put_error) ) {
570
+ state->put_error = rb_errinfo();
571
+ }
572
+ rb_set_errinfo(Qnil);
573
+ }
379
574
  }
380
575
  xfree(state->region[i]);
381
576
  }
382
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
+ }
383
589
  ca_detach(state->roots[i]);
384
590
  }
385
591
  }
386
592
  xfree(state->block);
593
+ if ( state->body_done && RTEST(rb_obj_is_kind_of(state->put_error,
594
+ rb_eException)) ) {
595
+ mark_failure(state->put_error);
596
+ rb_exc_raise(state->put_error);
597
+ }
387
598
  return Qnil;
388
599
  }
389
600
 
@@ -450,8 +661,98 @@ packed_body (VALUE argument)
450
661
  for ( k = 0; k < ca->ndim; k++ ) *mask_stride_slot++ = 0;
451
662
  }
452
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);
453
670
 
454
- return rb_yield_values(4, pointers, strides, mask_pointers, mask_strides);
671
+ {
672
+ VALUE result;
673
+ state->lent = 1;
674
+ result = rb_yield_values(4, pointers, strides, mask_pointers, mask_strides);
675
+ state->body_done = 1;
676
+ return result;
677
+ }
678
+ }
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
+ }
455
756
  }
456
757
 
457
758
  /*
@@ -468,7 +769,7 @@ address_basis_open (int argc, VALUE *argv, VALUE module)
468
769
  {
469
770
  VALUE arrays, writable_flags, box_start, box_count, packed;
470
771
  open_state state;
471
- int i;
772
+ int i, n;
472
773
 
473
774
  rb_scan_args(argc, argv, "23", &arrays, &writable_flags, &box_start,
474
775
  &box_count, &packed);
@@ -488,63 +789,140 @@ address_basis_open (int argc, VALUE *argv, VALUE module)
488
789
  if ( RARRAY_LEN(arrays) != RARRAY_LEN(writable_flags) ) {
489
790
  rb_raise(rb_eArgError, "one writable flag per array is required");
490
791
  }
792
+ n = (int) RARRAY_LEN(arrays);
491
793
 
492
- state.count = (int) RARRAY_LEN(arrays);
493
- state.slots = state.count * 2;
494
- state.arrays = arrays;
495
- state.bases = rb_ary_new_capa(state.count);
496
- /* One allocation, carved up, rather than eight. Every array here is the
497
- same length in slots and lives exactly as long as the open, so there is
498
- nothing for eight separate lifetimes to buy -- and this is a per-call
499
- cost on a path whose whole point is to be cheap enough to cross often.
500
- Zeroed once as a block, which is also what the slots want to start as:
501
- a NULL array, a NULL root, not writable, not attached, no region. */
502
- {
503
- size_t n = (size_t) state.slots + 1;
504
- size_t pointers = ROUND_UP_8(n * sizeof(CArray *));
505
- size_t chars = ROUND_UP_8(n * sizeof(char *));
506
- size_t sizes = ROUND_UP_8(n * CA_RANK_MAX * sizeof(ca_size_t));
507
- size_t ints = ROUND_UP_8(n * sizeof(int));
508
- size_t total = 2 * pointers + chars + 2 * sizes + 3 * ints;
509
- char *p;
510
- state.block = ALLOC_N(char, total);
511
- MEMZERO(state.block, char, total);
512
- p = state.block;
513
- state.carrays = (CArray **) p; p += pointers;
514
- state.roots = (CArray **) p; p += pointers;
515
- state.region = (char **) p; p += chars;
516
- state.region_start = (ca_size_t *) p; p += sizes;
517
- state.region_count = (ca_size_t *) p; p += sizes;
518
- state.tier = (int *) p; p += ints;
519
- state.writable = (int *) p; p += ints;
520
- state.attached_root = (int *) p;
521
- }
522
- state.box_starts = box_start;
523
- state.box_counts = box_count;
524
-
525
- for ( i = 0; i < state.count; 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++ ) {
526
797
  VALUE object = rb_ary_entry(arrays, i);
527
798
  CArray *ca;
528
799
  GetCArray(object, ca);
529
- state.carrays[i] = ca;
530
- state.writable[i] = RTEST(rb_ary_entry(writable_flags, i));
531
- verify_usable(object, ca, state.writable[i]);
800
+ verify_array(rb_obj_class(object), ca, RTEST(rb_ary_entry(writable_flags, i)));
532
801
  verify_box(box_start, box_count, i, ca);
533
- state.tier[i] = tier_for(ca);
802
+ }
534
803
 
535
- if ( ca->mask ) {
536
- CArray *mask = ca->mask;
537
- state.carrays[state.count + i] = mask;
538
- state.writable[state.count + i] = state.writable[i];
539
- state.tier[state.count + i] = tier_for(mask);
804
+ {
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
+ }
835
+ }
836
+ ab_prepare(&state, n, carrays, flags, bs, bc);
837
+ ALLOCV_END(vholder);
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);
843
+
844
+ {
845
+ VALUE result = RTEST(packed)
846
+ ? rb_ensure(packed_body, (VALUE) &state, open_ensure, (VALUE) &state)
847
+ : rb_ensure(open_body, (VALUE) &state, open_ensure, (VALUE) &state);
848
+ RB_GC_GUARD(state.arrays);
849
+ return result;
850
+ }
851
+ }
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);
540
867
  }
541
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
+ }
542
877
 
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);
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");
547
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);
548
926
  }
549
927
 
550
928
  /* Reports how an array would be opened, without opening it. */
@@ -556,6 +934,7 @@ address_basis_classify (VALUE module, VALUE object)
556
934
  int tier;
557
935
 
558
936
  GetCArray(object, ca);
937
+ ca_update_mask(ca);
559
938
  tier = tier_for(ca);
560
939
 
561
940
  result = rb_hash_new();
@@ -568,9 +947,24 @@ address_basis_classify (VALUE module, VALUE object)
568
947
  rb_hash_aset(result, ID2SYM(rb_intern("dim")), size_array(ca->dim, ca->ndim));
569
948
  rb_hash_aset(result, ID2SYM(rb_intern("bytes")), LL2NUM((long long) ca->bytes));
570
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
+ }
571
955
  return result;
572
956
  }
573
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
+
574
968
  /* ------------------------------------------------------------------- */
575
969
  /* Init_carray_address_basis */
576
970
  /* ------------------------------------------------------------------- */
@@ -583,8 +977,11 @@ Init_carray_address_basis (void)
583
977
  rb_define_singleton_method(mAddressBasis, "open", address_basis_open, -1);
584
978
  rb_define_singleton_method(mAddressBasis, "classify",
585
979
  address_basis_classify, 1);
980
+ rb_define_singleton_method(mAddressBasis, "tier", address_basis_tier, 1);
586
981
 
587
982
  rb_define_const(mAddressBasis, "TIER_ENTITY", INT2NUM(TIER_ENTITY));
588
983
  rb_define_const(mAddressBasis, "TIER_STRIDE", INT2NUM(TIER_STRIDE));
589
984
  rb_define_const(mAddressBasis, "TIER_XFER", INT2NUM(TIER_XFER));
985
+
986
+ mFailure = rb_define_module_under(mAddressBasis, "Failure");
590
987
  }