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
data/ext/ca_obj_grid.c CHANGED
@@ -14,6 +14,7 @@
14
14
  ---------------------------------------------------------------------------- */
15
15
 
16
16
  #include "carray.h"
17
+ #include "carray_internal.h" /* ca_attach_window */
17
18
 
18
19
  /* Per-axis tagged kind. Each axis is either STRIDE (start/step/count,
19
20
  no allocation) or INDEX (an owned ca_size_t index snapshot). A Range
@@ -34,9 +35,14 @@ typedef struct {
34
35
  /* INDEX-only. Owned in the non-share case (xfree'd by free_ca_grid),
35
36
  aliased in the share case (parent owns; CA_FLAG_SHARE_INDEX). */
36
37
  ca_size_t *indices;
38
+ /* INDEX-only, NULL when no cell is a hole. holes[i] marks a position
39
+ whose index cell was masked: it selects no parent cell. Its entry in
40
+ indices is 0 and is never range-checked. Owned and aliased as
41
+ indices is. */
42
+ uint8_t *holes;
37
43
  } cag_axis_t;
38
44
 
39
- typedef struct {
45
+ typedef struct _CAGrid {
40
46
  int16_t obj_type;
41
47
  int8_t data_type;
42
48
  int8_t ndim;
@@ -52,6 +58,18 @@ typedef struct {
52
58
  uint8_t nosync;
53
59
  /* -------------*/
54
60
  cag_axis_t *axes; /* [ndim], owned (non-share) or aliased (share) */
61
+ /* A grid with holes (cag_axis_t.holes): set on its mask grid, whose
62
+ holes read as masked. The data grid's holes read the cell at index 0
63
+ of each holed axis, as project and gather_nd do. */
64
+ int8_t holes_read_masked;
65
+ /* A grid with holes over a parent that has no mask: the all-false mask
66
+ its mask grid reads instead, so that showing the holes gives the
67
+ parent no mask. Owned by this grid; NULL otherwise. */
68
+ CArray *mask_standin;
69
+ /* On a mask grid: the grid whose mask it is, which holds the stand-in
70
+ and the parent whose mask replaces it. A Ruby copy of the mask keeps
71
+ that grid alive (rb_ca_mask_array sets masked_array). */
72
+ struct _CAGrid *mask_owner;
55
73
  } CAGrid;
56
74
 
57
75
  static size_t
@@ -69,6 +87,9 @@ ca_grid_dsize (const void *ap)
69
87
  if (ca->axes[k].kind == CAG_AXIS_INDEX && ca->axes[k].indices) {
70
88
  total += ca->axes[k].count * sizeof(ca_size_t);
71
89
  }
90
+ if (ca->axes[k].kind == CAG_AXIS_INDEX && ca->axes[k].holes) {
91
+ total += ca->axes[k].count;
92
+ }
72
93
  }
73
94
  return total;
74
95
  }
@@ -122,53 +143,83 @@ static VALUE rb_cCAGridMask;
122
143
 
123
144
  static int8_t CA_OBJ_GRID;
124
145
 
125
- /* Setup: caller passes ndim + per-axis protos (cag_axis_t). Copies the
126
- fields and, for INDEX axes, snapshots the indices into an owned buffer.
127
- The share path (share=1, from initialize_copy and create_mask) aliases
128
- the caller's protos buffer instead, tying its lifetime to the source
129
- CAGrid (free_ca_grid skips the xfree under CA_FLAG_SHARE_INDEX). */
130
- int
131
- ca_grid_setup (CAGrid *ca, CArray *parent, int8_t ndim,
132
- cag_axis_t *protos, int share)
146
+ /* Raises when protos cannot describe a view of parent: an ndim that is
147
+ not parent's, a negative count, a byte length over the limit, or a
148
+ parent index out of range. Nothing is allocated here, so the caller
149
+ runs it before taking memory. */
150
+ static void
151
+ ca_grid_check_protos (CArray *parent, int8_t ndim, const cag_axis_t *protos)
133
152
  {
134
153
  int8_t k;
135
- int8_t data_type;
136
- ca_size_t elements, bytes;
137
- double length;
138
- ca_size_t *dim0;
139
-
140
- CA_ASSUME(ndim >= 0 && ndim <= CA_RANK_MAX); /* bound loops/allocs over [CA_RANK_MAX] arrays */
141
- data_type = parent->data_type;
142
- bytes = parent->bytes;
143
- dim0 = parent->dim;
154
+ ca_size_t j;
155
+ double length;
144
156
 
145
- /* parent->ndim should equal ndim (= number of axes provided).
146
- rb_ca_grid pads with nil when fewer args are given, so by the
147
- time we get here ndim == parent->ndim. */
157
+ /* rb_ca_grid pads with nil when fewer args are given, so ndim ==
158
+ parent->ndim for every in-tree caller. */
148
159
  if ( ndim != parent->ndim ) {
149
160
  rb_raise(rb_eArgError,
150
161
  "CAGrid: ndim mismatch (%d args vs parent->ndim %d)",
151
162
  (int) ndim, (int) parent->ndim);
152
163
  }
153
164
 
154
- elements = 1;
155
- length = bytes;
165
+ length = parent->bytes;
156
166
  for (k = 0; k < ndim; k++) {
157
167
  if (protos[k].count < 0) {
158
- rb_raise(rb_eRuntimeError, "negative size for %d-th dimension", k);
168
+ rb_raise(rb_eArgError, "negative size for %d-th dimension", k);
159
169
  }
160
- elements *= protos[k].count;
161
170
  length *= protos[k].count;
162
171
  }
163
172
  if (length > CA_LENGTH_MAX) {
164
- rb_raise(rb_eRuntimeError, "too large byte length");
173
+ rb_raise(rb_eArgError, "too large byte length");
174
+ }
175
+
176
+ for (k = 0; k < ndim; k++) {
177
+ if ( protos[k].count <= 0 ) {
178
+ continue;
179
+ }
180
+ if (protos[k].kind == CAG_AXIS_STRIDE) {
181
+ ca_size_t first = protos[k].start;
182
+ ca_size_t last = first + (protos[k].count - 1) * protos[k].step;
183
+ ca_index_in_range_at(first, parent->dim[k], k);
184
+ if (protos[k].count > 1) ca_index_in_range_at(last, parent->dim[k], k);
185
+ }
186
+ else {
187
+ for (j = 0; j < protos[k].count; j++) {
188
+ if ( protos[k].holes && protos[k].holes[j] ) {
189
+ continue;
190
+ }
191
+ ca_index_in_range_at(protos[k].indices[j], parent->dim[k], k);
192
+ }
193
+ }
194
+ }
195
+ }
196
+
197
+ /* Setup: caller passes ndim + per-axis protos (cag_axis_t) that
198
+ ca_grid_check_protos has accepted. Copies the fields and, for INDEX
199
+ axes, snapshots the indices into an owned buffer. The share path
200
+ (share=1, used only by create_mask) aliases the caller's protos buffer
201
+ instead: the mask grid is owned and freed by the grid whose axes it
202
+ aliases (free_ca_grid skips the xfree under CA_FLAG_SHARE_INDEX). */
203
+ static int
204
+ ca_grid_setup (CAGrid *ca, CArray *parent, int8_t ndim,
205
+ cag_axis_t *protos, int share)
206
+ {
207
+ int8_t k;
208
+ ca_size_t elements;
209
+
210
+ CA_ASSUME(ndim >= 0 && ndim <= CA_RANK_MAX); /* bound loops/allocs over [CA_RANK_MAX] arrays */
211
+
212
+ {
213
+ ca_size_t counts[CA_RANK_MAX];
214
+ for (k = 0; k < ndim; k++) counts[k] = protos[k].count;
215
+ elements = ca_shape_elements(ndim, counts, parent->bytes);
165
216
  }
166
217
 
167
218
  ca->obj_type = CA_OBJ_GRID;
168
- ca->data_type = data_type;
219
+ ca->data_type = parent->data_type;
169
220
  ca->flags = 0;
170
221
  ca->ndim = ndim;
171
- ca->bytes = bytes;
222
+ ca->bytes = parent->bytes;
172
223
  ca->elements = elements;
173
224
  ca->ptr = NULL;
174
225
  ca->mask = NULL;
@@ -178,6 +229,9 @@ ca_grid_setup (CAGrid *ca, CArray *parent, int8_t ndim,
178
229
  ca->parent = parent;
179
230
  ca->attach = 0;
180
231
  ca->nosync = 0;
232
+ ca->holes_read_masked = 0;
233
+ ca->mask_standin = NULL;
234
+ ca->mask_owner = NULL;
181
235
 
182
236
  for (k = 0; k < ndim; k++) ca->dim[k] = protos[k].count;
183
237
 
@@ -196,48 +250,104 @@ ca_grid_setup (CAGrid *ca, CArray *parent, int8_t ndim,
196
250
  ca->axes[k].start = protos[k].start;
197
251
  ca->axes[k].step = protos[k].step;
198
252
  ca->axes[k].indices = NULL;
199
- /* Bounds check: first and last parent index must be in range. */
200
- if (protos[k].count > 0) {
201
- ca_size_t first = protos[k].start;
202
- ca_size_t last = first + (protos[k].count - 1) * protos[k].step;
203
- CA_CHECK_INDEX(first, dim0[k]);
204
- if (protos[k].count > 1) CA_CHECK_INDEX(last, dim0[k]);
205
- }
253
+ ca->axes[k].holes = NULL;
206
254
  } else {
207
255
  ca->axes[k].start = 0;
208
256
  ca->axes[k].step = 0;
209
257
  ca->axes[k].indices = ALLOC_N(ca_size_t,
210
258
  protos[k].count > 0 ? protos[k].count : 1);
259
+ ca->axes[k].holes = NULL;
211
260
  ca_size_t j;
261
+ if ( protos[k].holes ) {
262
+ for (j = 0; j < protos[k].count && ! protos[k].holes[j]; j++) ;
263
+ if ( j < protos[k].count ) {
264
+ ca->axes[k].holes = ALLOC_N(uint8_t, protos[k].count);
265
+ memcpy(ca->axes[k].holes, protos[k].holes, protos[k].count);
266
+ }
267
+ }
212
268
  for (j = 0; j < protos[k].count; j++) {
213
269
  ca_size_t v = protos[k].indices[j];
214
- CA_CHECK_INDEX(v, dim0[k]);
215
- ca->axes[k].indices[j] = v;
270
+ if ( ca->axes[k].holes && ca->axes[k].holes[j] ) {
271
+ ca->axes[k].indices[j] = 0;
272
+ continue;
273
+ }
274
+ ca->axes[k].indices[j] = ( v < 0 ) ? v + parent->dim[k] : v;
216
275
  }
217
276
  }
218
277
  }
219
278
  }
220
279
 
221
- if ( ca_has_mask(parent) ) {
222
- ca_create_mask(ca);
223
- }
280
+ return 0;
281
+ }
224
282
 
283
+ /* Whether any position of the grid is a hole (cag_axis_t.holes). */
284
+ static int
285
+ cag_has_holes (const CAGrid *ca)
286
+ {
287
+ int8_t k;
288
+ for (k = 0; k < ca->ndim; k++) {
289
+ if ( ca->axes[k].kind == CAG_AXIS_INDEX && ca->axes[k].holes ) {
290
+ return 1;
291
+ }
292
+ }
225
293
  return 0;
226
294
  }
227
295
 
228
- CAGrid *
229
- ca_grid_new (CArray *parent, int8_t ndim, cag_axis_t *protos)
296
+ int
297
+ ca_grid_has_holes (void *ap)
230
298
  {
231
- CAGrid *ca = (CAGrid *) ca_array_alloc(CA_OBJ_GRID, ndim);
299
+ return cag_has_holes((const CAGrid *) ap);
300
+ }
301
+
302
+ /* A grid with holes has a mask from the start: its holes read as masked
303
+ whatever the parent holds. A mask grid (holes_read_masked) takes none.
304
+ A view of a value array or a mask array can never carry a mask, so it
305
+ can not show a hole. */
306
+ static VALUE
307
+ ca_grid_inherit_mask (VALUE view)
308
+ {
309
+ CAGrid *ca = (CAGrid *) view;
310
+ if ( cag_has_holes(ca) && ! ca->holes_read_masked ) {
311
+ if ( ca_is_value_array(ca) || ca_is_mask_array(ca) ) {
312
+ rb_raise(rb_eArgError,
313
+ "a masked index cell reads as masked, which a view of the %s "
314
+ "array can not hold", ca_is_value_array(ca) ? "value" : "mask");
315
+ }
316
+ ca_create_mask((CArray *) ca);
317
+ return Qnil;
318
+ }
319
+ return ca_view_inherit_mask(view);
320
+ }
321
+
322
+ /* holes_read_masked is the copied grid's: a copy of a mask grid is one. */
323
+ static CAGrid *
324
+ ca_grid_new_as (CArray *parent, int8_t ndim, cag_axis_t *protos,
325
+ int8_t holes_read_masked, CAGrid *mask_owner)
326
+ {
327
+ CAGrid *ca;
328
+ ca_grid_check_protos(parent, ndim, protos);
329
+ /* check: ca_grid_check_protos above */
330
+ ca = (CAGrid *) ca_array_alloc(CA_OBJ_GRID, ndim);
232
331
  ca_grid_setup(ca, parent, ndim, protos, 0);
332
+ ca->holes_read_masked = holes_read_masked;
333
+ ca->mask_owner = mask_owner;
334
+ ca_fill_or_free((CArray *) ca, ca_grid_inherit_mask, (VALUE) ca);
233
335
  return ca;
234
336
  }
235
337
 
338
+ CAGrid *
339
+ ca_grid_new (CArray *parent, int8_t ndim, cag_axis_t *protos)
340
+ {
341
+ return ca_grid_new_as(parent, ndim, protos, 0, NULL);
342
+ }
343
+
236
344
  CAGrid *
237
345
  ca_grid_new_share (CArray *parent, int8_t ndim, cag_axis_t *protos)
238
346
  {
347
+ /* check: the mask grid copies its own grid's checked axes */
239
348
  CAGrid *ca = (CAGrid *) ca_array_alloc(CA_OBJ_GRID, ndim);
240
349
  ca_grid_setup(ca, parent, ndim, protos, 1);
350
+ ca_fill_or_free((CArray *) ca, ca_view_inherit_mask, (VALUE) ca);
241
351
  return ca;
242
352
  }
243
353
 
@@ -247,12 +357,16 @@ free_ca_grid (void *ap)
247
357
  CAGrid *ca = (CAGrid *) ap;
248
358
  if ( ca != NULL ) {
249
359
  ca_free(ca->mask);
360
+ ca_free(ca->mask_standin);
250
361
  if ( ! (ca->flags & CA_FLAG_SHARE_INDEX) ) {
251
362
  int8_t k;
252
363
  for (k = 0; k < ca->ndim; k++) {
253
364
  if ( ca->axes[k].kind == CAG_AXIS_INDEX && ca->axes[k].indices ) {
254
365
  xfree(ca->axes[k].indices);
255
366
  }
367
+ if ( ca->axes[k].kind == CAG_AXIS_INDEX && ca->axes[k].holes ) {
368
+ xfree(ca->axes[k].holes);
369
+ }
256
370
  }
257
371
  xfree(ca->axes);
258
372
  }
@@ -278,7 +392,8 @@ static void *
278
392
  ca_grid_func_clone (void *ap)
279
393
  {
280
394
  CAGrid *ca = (CAGrid *) ap;
281
- return ca_grid_new_share(ca->parent, ca->ndim, ca->axes);
395
+ return ca_grid_new_as(ca->parent, ca->ndim, ca->axes, ca->holes_read_masked,
396
+ ca->mask_owner);
282
397
  }
283
398
 
284
399
  /* Per-axis parent-index lookup. STRIDE = one multiply, INDEX = one
@@ -292,6 +407,124 @@ cag_parent_index_for (const cag_axis_t *ax, ca_size_t i)
292
407
  return ax->indices[i];
293
408
  }
294
409
 
410
+ /* A mask grid over its owner's stand-in (ca_mask_standin_current): what
411
+ it reads from now on. */
412
+ static void
413
+ cag_mask_current (CAGrid *m)
414
+ {
415
+ CAGrid *o = m->mask_owner;
416
+ if ( o != NULL ) {
417
+ m->parent = ca_mask_standin_current((CArray *) m, m->parent, o->parent,
418
+ o->mask_standin);
419
+ }
420
+ }
421
+
422
+ /* After a write of n cells of `data` to a mask grid: a true that went
423
+ into the stand-in goes on to the parent's mask. */
424
+ static void
425
+ cag_mask_settle_after (CAGrid *m, const void *data, ca_size_t n)
426
+ {
427
+ CAGrid *o = m->mask_owner;
428
+ if ( o != NULL && m->parent == o->mask_standin && ca_mask_any_true(data, n) ) {
429
+ ca_mask_standin_settle(o->parent, o->mask_standin);
430
+ }
431
+ }
432
+
433
+ /* What the GET of a hole writes when it reads no parent cell: true in a
434
+ mask grid, an empty cell otherwise (a parent with no cell to read). */
435
+ static void
436
+ cag_hole_cells (const CAGrid *ca, char *data, ca_size_t n)
437
+ {
438
+ ca_size_t i;
439
+ if ( ca->holes_read_masked ) {
440
+ memset(data, 1, (size_t) n);
441
+ }
442
+ else if ( ca->data_type == CA_OBJECT ) {
443
+ for (i = 0; i < n; i++) ((VALUE *) data)[i] = INT2FIX(0);
444
+ }
445
+ else {
446
+ memset(data, 0, (size_t) (n * ca->bytes));
447
+ }
448
+ }
449
+
450
+ /* The parent address of the view cell at idx. A hole takes index 0 on its
451
+ holed axes; the return says whether the cell is one. */
452
+ static int
453
+ cag_parent_addr (const CAGrid *ca, const ca_size_t *idx, ca_size_t *paddr)
454
+ {
455
+ ca_size_t pa = 0;
456
+ int hole = 0;
457
+ int8_t k;
458
+ for (k = 0; k < ca->ndim; k++) {
459
+ const cag_axis_t *ax = &ca->axes[k];
460
+ if ( ax->kind == CAG_AXIS_INDEX && ax->holes && ax->holes[idx[k]] ) {
461
+ hole = 1;
462
+ }
463
+ pa = pa * ca->parent->dim[k] + cag_parent_index_for(ax, idx[k]);
464
+ }
465
+ *paddr = pa;
466
+ return hole;
467
+ }
468
+
469
+ /* The transfer of a grid with holes, for the view cells at addrs (NULL:
470
+ the whole view in order). The parent is asked for one batch of
471
+ addresses. A GET reads every cell, a hole reading the cell at index 0
472
+ of its holed axes (in a mask grid it is then set true); a PUT leaves
473
+ the holes out. */
474
+ static void
475
+ ca_grid_holed_xfer (CAGrid *ca, ca_size_t n, const ca_size_t *addrs,
476
+ char *data, int dir)
477
+ {
478
+ CArray *parent;
479
+ ca_size_t bytes = ca->bytes;
480
+ volatile VALUE h_addr = 0, h_hole = 0, h_data = 0;
481
+ ca_size_t *paddrs;
482
+ uint8_t *hole;
483
+ ca_size_t i, m, idx[CA_RANK_MAX];
484
+
485
+ if ( n <= 0 ) {
486
+ return;
487
+ }
488
+ cag_mask_current(ca);
489
+ parent = ca->parent;
490
+ paddrs = ALLOCV_N(ca_size_t, h_addr, n);
491
+ hole = ALLOCV_N(uint8_t, h_hole, n);
492
+ for (i = 0; i < n; i++) {
493
+ ca_addr2index((CArray *) ca, addrs ? addrs[i] : i, idx);
494
+ hole[i] = (uint8_t) cag_parent_addr(ca, idx, &paddrs[i]);
495
+ }
496
+
497
+ if ( dir == CA_XFER_GET ) {
498
+ if ( parent->elements == 0 ) {
499
+ cag_hole_cells(ca, data, n); /* every cell is a hole */
500
+ }
501
+ else {
502
+ ca_xfer_addrs(parent, n, paddrs, data, CA_XFER_GET);
503
+ if ( ca->holes_read_masked ) {
504
+ for (i = 0; i < n; i++) {
505
+ if ( hole[i] ) cag_hole_cells(ca, data + i * bytes, 1);
506
+ }
507
+ }
508
+ }
509
+ }
510
+ else {
511
+ char *pdata = ALLOCV_N(char, h_data, n * bytes);
512
+ for (i = 0, m = 0; i < n; i++) {
513
+ if ( hole[i] ) continue;
514
+ paddrs[m] = paddrs[i];
515
+ memcpy(pdata + m * bytes, data + i * bytes, (size_t) bytes);
516
+ m++;
517
+ }
518
+ if ( m > 0 ) {
519
+ ca_xfer_addrs(parent, m, paddrs, pdata, CA_XFER_PUT);
520
+ cag_mask_settle_after(ca, pdata, m);
521
+ }
522
+ ALLOCV_END(h_data);
523
+ }
524
+ ALLOCV_END(h_hole);
525
+ ALLOCV_END(h_addr);
526
+ }
527
+
295
528
  /* per-cell logic lives in ca_grid_func_xfer_index; forward-declared for
296
529
  the region-delivery paths below. */
297
530
  static void ca_grid_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir);
@@ -304,10 +537,28 @@ ca_grid_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
304
537
  CAGrid *ca = (CAGrid *) ap;
305
538
  ca_size_t idx0[CA_RANK_MAX];
306
539
  int8_t i;
540
+ int hole = 0;
307
541
  for (i = 0; i < ca->ndim; i++) {
308
- idx0[i] = cag_parent_index_for(&ca->axes[i], idx[i]);
542
+ const cag_axis_t *ax = &ca->axes[i];
543
+ idx0[i] = cag_parent_index_for(ax, idx[i]);
544
+ if ( ax->kind == CAG_AXIS_INDEX && ax->holes && ax->holes[idx[i]] ) {
545
+ hole = 1;
546
+ }
309
547
  }
548
+ if ( hole ) {
549
+ if ( dir == CA_XFER_PUT ) {
550
+ return;
551
+ }
552
+ if ( ca->holes_read_masked || ca->parent->elements == 0 ) {
553
+ cag_hole_cells(ca, (char *) data, 1);
554
+ return;
555
+ }
556
+ }
557
+ cag_mask_current(ca);
310
558
  ca_xfer_index(ca->parent, idx0, data, dir);
559
+ if ( dir == CA_XFER_PUT ) {
560
+ cag_mask_settle_after(ca, data, 1);
561
+ }
311
562
  }
312
563
 
313
564
  /* Batched address gather/scatter. Per-axis index remap
@@ -331,6 +582,11 @@ ca_grid_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
331
582
  int8_t k;
332
583
  volatile VALUE holder;
333
584
 
585
+ if ( cag_has_holes(ca) ) {
586
+ ca_grid_holed_xfer(ca, n, addrs, (char *) data, dir);
587
+ return;
588
+ }
589
+
334
590
  /* Opportunistic fast path. ca_resolve_attached_root_via_identity lifts
335
591
  parent->ptr through an identity CAStride compose-fold (handles a
336
592
  flatten[idx].reshape(*) chain etc.). */
@@ -386,8 +642,9 @@ ca_grid_func_fold_stride (void *ap, ca_fold_t *f, void **next_parent)
386
642
  int8_t k;
387
643
 
388
644
  for (k = 0; k < g->ndim; k++) {
389
- if (g->axes[k].kind == CAG_AXIS_INDEX && g->axes[k].count != 1) {
390
- return 0; /* multi-INDEX axis -> true gather, decline */
645
+ if (g->axes[k].kind == CAG_AXIS_INDEX
646
+ && (g->axes[k].count != 1 || g->axes[k].holes)) {
647
+ return 0; /* multi-INDEX axis -> true gather, decline; a hole is no stride */
391
648
  }
392
649
  }
393
650
 
@@ -451,6 +708,7 @@ static void
451
708
  ca_grid_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
452
709
  ca_size_t *strides, void *data, int dir)
453
710
  {
711
+ if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
454
712
  CAGrid *g = (CAGrid *) ap;
455
713
  CArray *parent = g->parent;
456
714
  int8_t ndim = g->ndim;
@@ -481,8 +739,9 @@ ca_grid_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
481
739
  /* The request is over the view's addresses, so a transposed / flat request
482
740
  is legal and must not be composed axis-by-axis; see
483
741
  ca_xfer_stride_request_is_axis_box (carray.h). */
484
- if ( ! ca_xfer_stride_request_is_axis_box(g, starts, counts, strides) ) {
485
- aligned = 0;
742
+ if ( cag_has_holes(g)
743
+ || ! ca_xfer_stride_request_is_axis_box(g, starts, counts, strides) ) {
744
+ aligned = 0; /* a grid with holes goes cell by cell (xfer_index) */
486
745
  }
487
746
  else {
488
747
  for (k = 0; k < ndim; k++) {
@@ -495,6 +754,7 @@ ca_grid_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
495
754
  ca_size_t idx[CA_RANK_MAX], doff = 0, base = 0;
496
755
  for (k = 0; k < ndim; k++) base += starts[k] * gnative[k];
497
756
  for (k = 0; k < ndim; k++) idx[k] = 0;
757
+ /* empty: the slot returns on an empty region at its entry */
498
758
  while (1) {
499
759
  ca_size_t goff = base, gidx[CA_RANK_MAX];
500
760
  for (k = 0; k < ndim; k++) goff += idx[k] * strides[k];
@@ -583,6 +843,7 @@ ca_grid_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
583
843
  ca_xfer_stride/ca_xfer_addrs. */
584
844
  for (k = 0; k < ndim; k++) o[k] = 0;
585
845
 
846
+ /* empty: the slot returns on an empty region at its entry */
586
847
  while (1) {
587
848
  ca_size_t pbase = 0, doff = 0;
588
849
  for (k = 0; k < inner; k++) {
@@ -624,24 +885,47 @@ ca_grid_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
624
885
  }
625
886
  }
626
887
 
888
+ /* attach / sync / copy_data / sync_data / fill_data all go through the
889
+ descriptor engine (ca_axis_dispatch.c). This file supplies only
890
+ describe_axes; the engine does the rest.
891
+
892
+ A parent with no memory to lend (ca_parent_lends_no_memory) is never
893
+ attached: attaching it would produce all of its cells to serve the few
894
+ this grid selects. The grid then owns its buffer, filled and drained
895
+ through xfer_all, which asks the parent for the selected cells only.
896
+ The predicate asks what the parent is, not whether it is attached now,
897
+ so attach, sync and detach all take the same branch. */
898
+
899
+ /* A grid with holes fills and writes back its buffer through its own
900
+ transfers too: the descriptor engine has no hole. */
901
+ static int
902
+ cag_owns_buffer (CAGrid *ca)
903
+ {
904
+ return cag_has_holes(ca) || ca_parent_lends_no_memory(ca->parent);
905
+ }
906
+
627
907
  static void
628
908
  ca_grid_func_allocate (void *ap)
629
909
  {
630
910
  CAGrid *ca = (CAGrid *) ap;
631
- ca_attach(ca->parent);
632
- ca->ptr = xmalloc(ca_length(ca));
911
+ if ( ! cag_owns_buffer(ca) ) {
912
+ ca_attach(ca->parent);
913
+ }
914
+ ca->ptr = ca_alloc_cells((CArray *) ca);
633
915
  }
634
916
 
635
- /* attach / sync / copy_data / sync_data / fill_data all go through the
636
- descriptor engine (ca_axis_dispatch.c). This file supplies only
637
- describe_axes; the engine does the rest. */
638
-
639
917
  static void
640
918
  ca_grid_func_attach (void *ap)
641
919
  {
642
920
  CAGrid *ca = (CAGrid *) ap;
643
921
  ca_axis_desc_t desc[CA_RANK_MAX];
644
922
  ca_size_t pdims[CA_RANK_MAX];
923
+
924
+ if ( cag_owns_buffer(ca) ) {
925
+ ca_axis_view_attach_owned(ca);
926
+ return;
927
+ }
928
+
645
929
  ca_attach(ca->parent);
646
930
  ca_grid_describe_axes(ca, desc, pdims);
647
931
  ca->ptr = ca_axis_dispatch_attach(ca->parent, pdims, desc, ca->ndim,
@@ -654,6 +938,12 @@ ca_grid_func_sync (void *ap)
654
938
  CAGrid *ca = (CAGrid *) ap;
655
939
  ca_axis_desc_t desc[CA_RANK_MAX];
656
940
  ca_size_t pdims[CA_RANK_MAX];
941
+
942
+ if ( cag_owns_buffer(ca) ) {
943
+ ca_axis_view_sync_owned(ca);
944
+ return;
945
+ }
946
+
657
947
  ca_grid_describe_axes(ca, desc, pdims);
658
948
  ca_axis_dispatch_scatter(ca->parent, pdims, desc, ca->ndim, ca->bytes,
659
949
  ca->elements, ca->ptr);
@@ -666,16 +956,16 @@ ca_grid_func_detach (void *ap)
666
956
  CAGrid *ca = (CAGrid *) ap;
667
957
  xfree(ca->ptr);
668
958
  ca->ptr = NULL;
669
- ca_detach(ca->parent);
959
+ if ( ! cag_owns_buffer(ca) ) {
960
+ ca_detach(ca->parent);
961
+ }
670
962
  }
671
963
 
672
- /* xfer_all: fast path on parent->ptr, with no silent transitive attach
673
- of the parent. A cold parent uses a proper 2-pass -- materialise the
674
- parent into scratch via ca_xfer_all, temporarily expose it as
675
- parent->ptr, run the fast path, then (for PUT) write scratch back. */
964
+ /* The transfer against parent->ptr, for ca_axis_view_xfer_all. */
676
965
  static void
677
- ca_grid_func_run_fast_path (CAGrid *ca, char *data, int dir)
966
+ ca_grid_func_run_fast_path (void *ap, char *data, int dir)
678
967
  {
968
+ CAGrid *ca = (CAGrid *) ap;
679
969
  ca_axis_desc_t desc[CA_RANK_MAX];
680
970
  ca_size_t pdims[CA_RANK_MAX];
681
971
  ca_grid_describe_axes(ca, desc, pdims);
@@ -692,26 +982,38 @@ static void
692
982
  ca_grid_func_xfer_all (void *ap, void *data, int dir)
693
983
  {
694
984
  CAGrid *ca = (CAGrid *) ap;
695
- if ( ca->parent->ptr ) {
696
- ca_grid_func_run_fast_path(ca, (char *) data, dir);
985
+ if ( cag_has_holes(ca) ) {
986
+ ca_grid_holed_xfer(ca, ca->elements, NULL, (char *) data, dir);
697
987
  return;
698
988
  }
699
- /* Proper 2-pass cold fallback. */
700
- {
701
- volatile VALUE holder;
702
- CArray *parent = ca->parent;
703
- ca_size_t plen = parent->elements * parent->bytes;
704
- char *parent_scratch = ALLOCV_N(char, holder, plen);
705
- char *parent_ptr_saved = parent->ptr;
706
- ca_xfer_all(parent, parent_scratch, CA_XFER_GET);
707
- parent->ptr = parent_scratch;
708
- ca_grid_func_run_fast_path(ca, (char *) data, dir);
709
- if ( dir == CA_XFER_PUT ) {
710
- ca_xfer_all(parent, parent_scratch, CA_XFER_PUT);
989
+ ca_axis_view_xfer_all(ap, ca_grid_func_run_fast_path, 1, data, dir);
990
+ }
991
+
992
+ /* The fill of a grid with holes: the parent cells of the positions that
993
+ are not holes, as one address batch. */
994
+ static void
995
+ ca_grid_holed_fill (CAGrid *ca, void *ptr)
996
+ {
997
+ volatile VALUE holder = 0;
998
+ ca_size_t *paddrs;
999
+ ca_size_t i, m = 0, pa, idx[CA_RANK_MAX];
1000
+
1001
+ if ( ca->elements <= 0 ) {
1002
+ return;
1003
+ }
1004
+ cag_mask_current(ca);
1005
+ paddrs = ALLOCV_N(ca_size_t, holder, ca->elements);
1006
+ for (i = 0; i < ca->elements; i++) {
1007
+ ca_addr2index((CArray *) ca, i, idx);
1008
+ if ( ! cag_parent_addr(ca, idx, &pa) ) {
1009
+ paddrs[m++] = pa;
711
1010
  }
712
- parent->ptr = parent_ptr_saved;
713
- ALLOCV_END(holder);
714
1011
  }
1012
+ if ( m > 0 ) {
1013
+ ca_fill_addrs(ca->parent, m, paddrs, ptr);
1014
+ cag_mask_settle_after(ca, ptr, 1);
1015
+ }
1016
+ ALLOCV_END(holder);
715
1017
  }
716
1018
 
717
1019
  static void
@@ -721,6 +1023,11 @@ ca_grid_func_fill_data (void *ap, void *ptr)
721
1023
  ca_axis_desc_t desc[CA_RANK_MAX];
722
1024
  ca_size_t pdims[CA_RANK_MAX];
723
1025
 
1026
+ if ( cag_has_holes(ca) ) {
1027
+ ca_grid_holed_fill(ca, ptr);
1028
+ return;
1029
+ }
1030
+
724
1031
  ca_grid_describe_axes(ca, desc, pdims);
725
1032
 
726
1033
  /* Writing the selected cells used to go through a whole-parent attach and
@@ -734,25 +1041,38 @@ ca_grid_func_fill_data (void *ap, void *ptr)
734
1041
  return;
735
1042
  }
736
1043
 
737
- ca_attach(ca->parent);
1044
+ ca_attach(ca->parent); /* window: nothing raises inside */
738
1045
  ca_axis_dispatch_fill_value(ca->parent, pdims, desc, ca->ndim, ca->bytes,
739
1046
  ca->elements, ptr);
740
- ca_sync(ca->parent);
741
- ca_detach(ca->parent);
1047
+ ca_sync_detach(ca->parent);
742
1048
  }
743
1049
 
1050
+ /* The mask is a grid over the parent's mask with the same axes, holes
1051
+ included; there the holes read as masked. */
744
1052
  static void
745
1053
  ca_grid_func_create_mask (void *ap)
746
1054
  {
747
1055
  CAGrid *ca = (CAGrid *) ap;
748
-
749
- ca_update_mask(ca->parent);
750
- if ( ! ca->parent->mask ) {
1056
+ CArray *mparent;
1057
+ int holed = cag_has_holes(ca);
1058
+
1059
+ /* A grid with holes shows them without giving a parent that has no mask
1060
+ one: its mask grid reads an all-false stand-in until a true is written
1061
+ outside the holes (ca_mask_standin_new). */
1062
+ if ( holed && ! ca_has_mask(ca->parent) ) {
1063
+ if ( ! ca->mask_standin ) {
1064
+ ca->mask_standin = ca_mask_standin_new(ca->parent);
1065
+ }
1066
+ mparent = ca->mask_standin;
1067
+ }
1068
+ else {
751
1069
  ca_create_mask(ca->parent);
1070
+ mparent = ca->parent->mask;
752
1071
  }
753
1072
 
754
- ca->mask = (CArray *) ca_grid_new_share(ca->parent->mask,
755
- ca->ndim, ca->axes);
1073
+ ca->mask = (CArray *) ca_grid_new_share(mparent, ca->ndim, ca->axes);
1074
+ ((CAGrid *) ca->mask)->holes_read_masked = (int8_t) holed;
1075
+ ((CAGrid *) ca->mask)->mask_owner = holed ? ca : NULL;
756
1076
  }
757
1077
 
758
1078
  ca_operation_function_t ca_grid_func = {
@@ -789,82 +1109,110 @@ rb_ca_grid_new (VALUE cary, int8_t ndim, cag_axis_t *protos)
789
1109
  return obj;
790
1110
  }
791
1111
 
792
- /* Detect a plain Ruby Range (Integer first/last, exclude_end?, step=1
793
- implicit) and convert it to STRIDE (start, count, step=1).
794
- Returns 1 on success (fills *out_start, *out_count), 0 on fallback
795
- (caller should materialise to INDEX). Negative endpoints are
796
- normalised against dim_size like everywhere else in CArray. */
797
- static int
798
- cag_range_to_stride (VALUE range, ca_size_t dim_size,
799
- ca_size_t *out_start, ca_size_t *out_count)
1112
+ /* The INDEX axes of rb_ca_grid, read with their index arrays attached,
1113
+ and the view built from them. */
1114
+
1115
+ typedef struct {
1116
+ VALUE self;
1117
+ CArray *ca;
1118
+ CArray **ci;
1119
+ cag_axis_t *protos;
1120
+ } ca_grid_build_ctx_t;
1121
+
1122
+ static VALUE
1123
+ ca_grid_build_body (VALUE arg)
800
1124
  {
801
- VALUE rb_first, rb_last;
802
- ca_size_t first, last, count;
803
- int exclude_end;
804
-
805
- if ( ! rb_obj_is_kind_of(range, rb_cRange) ) return 0;
806
- rb_first = rb_funcall(range, rb_intern("first"), 0);
807
- rb_last = rb_funcall(range, rb_intern("last"), 0);
808
- if ( ! FIXNUM_P(rb_first) || ! FIXNUM_P(rb_last) ) return 0;
809
- exclude_end = RTEST(rb_funcall(range, rb_intern("exclude_end?"), 0));
810
-
811
- first = NUM2SIZE(rb_first);
812
- last = NUM2SIZE(rb_last);
813
- if (first < 0) first += dim_size;
814
- if (last < 0) last += dim_size;
815
- if (exclude_end) last -= 1;
816
-
817
- if (first < 0 || first >= dim_size) return 0;
818
- if (last < first - 1) return 0; /* empty range */
819
- if (last >= dim_size) return 0;
820
- count = last - first + 1;
821
-
822
- *out_start = first;
823
- *out_count = count;
824
- return 1;
1125
+ ca_grid_build_ctx_t *c = (ca_grid_build_ctx_t *) arg;
1126
+ int8_t i;
1127
+
1128
+ for (i = 0; i < c->ca->ndim; i++) {
1129
+ CArray *ci = c->ci[i];
1130
+ if ( ! ci ) {
1131
+ continue;
1132
+ }
1133
+ c->protos[i].kind = CAG_AXIS_INDEX;
1134
+ c->protos[i].count = ci->elements;
1135
+ c->protos[i].indices = (ca_size_t *) ci->ptr;
1136
+ c->protos[i].holes = NULL;
1137
+ if ( ca_is_any_masked(ci) ) {
1138
+ /* A masked index cell selects nothing: its position stays, as a hole
1139
+ (masked on read, skipped on write). setup copies the mask into
1140
+ its own buffer. */
1141
+ c->protos[i].holes = (uint8_t *) ci->mask->ptr;
1142
+ }
1143
+ c->protos[i].start = 0;
1144
+ c->protos[i].step = 0;
1145
+ }
1146
+
1147
+ return rb_ca_grid_new(c->self, c->ca->ndim, c->protos);
1148
+ }
1149
+
1150
+ /* A scalar key (an Integer or a Float) picks one cell along its axis, and that axis is
1151
+ not in the result -- as it is not in a block reference. The view is
1152
+ built with the axis at length 1 and the axis then dropped. */
1153
+ static VALUE
1154
+ ca_grid_drop_scalar_axes (VALUE obj, int argc, VALUE *argv)
1155
+ {
1156
+ VALUE idx[CA_RANK_MAX];
1157
+ int i, any = 0;
1158
+ for (i = 0; i < argc; i++) {
1159
+ if ( ca_index_key_is_scalar(argv[i]) ) {
1160
+ idx[i] = INT2FIX(0);
1161
+ any = 1;
1162
+ }
1163
+ else {
1164
+ idx[i] = Qnil;
1165
+ }
1166
+ }
1167
+ if ( ! any ) {
1168
+ return obj;
1169
+ }
1170
+ return rb_ca_fetch2(obj, argc, idx);
825
1171
  }
826
1172
 
1173
+ /* ca[k0, k1, ...] with a CArray among the keys. An integer CArray picks
1174
+ the cells it lists along its axis, a boolean one the cells it marks (it
1175
+ must be as long as the axis). Every other key is read as the indexer
1176
+ reads it (ca_index_axis_key): an Integer picks one cell and drops the
1177
+ axis, a Range / ArithmeticSequence / Array form a run, nil the axis. */
827
1178
  VALUE
828
1179
  rb_ca_grid (int argc, VALUE *argv, VALUE self)
829
1180
  {
830
- volatile VALUE obj, ridx, rval;
1181
+ volatile VALUE obj, rval;
831
1182
  volatile VALUE list = rb_ary_new();
832
1183
  CArray *ca;
833
1184
  CArray *ci[CA_RANK_MAX]; /* CAWrap holders for INDEX axes; NULL for STRIDE */
834
1185
  cag_axis_t protos[CA_RANK_MAX];
1186
+ VALUE expanded[CA_RANK_MAX];
1187
+ long n;
835
1188
  ca_size_t i;
836
1189
 
837
- /* Dispatch hook: route mixed AP + single boolean to CASelectAxis
838
- (slab-copy optimised path). Falls through to per-cell CAGrid gather
839
- when not eligible (integer index, multiple INDIRECT axes, indirect
840
- not on axis 0). Inline argv[0] type check avoids the function call
841
- when axis 0 is nil/Integer (most patterns with INDIRECT on inner axes). */
842
- extern int ca_csa_dispatch_bypass;
843
- if ( ! ca_csa_dispatch_bypass && argc > 0 && TYPE(argv[0]) == T_DATA ) {
844
- if ( rb_ca_select_axis_eligible_p(argc, argv, self) ) {
845
- return rb_ca_select_axis(argc, argv, self);
846
- }
847
- }
848
-
849
1190
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
850
1191
 
851
- ridx = rb_ary_new4(argc, argv);
1192
+ n = ca_index_expand_rubber(ca->ndim, argc, argv, expanded);
1193
+ if ( n >= 0 ) {
1194
+ argc = (int) n;
1195
+ argv = expanded;
1196
+ }
852
1197
 
853
- if ( RARRAY_LEN(ridx) > ca->ndim ) {
1198
+ if ( argc > ca->ndim ) {
854
1199
  rb_raise(rb_eArgError, "# of arguments doesn't equal to the ndim");
855
1200
  }
856
- else if ( RARRAY_LEN(ridx) < ca->ndim ) {
1201
+ else if ( argc < ca->ndim ) {
857
1202
  volatile VALUE ref;
858
1203
  CArray *cv;
859
1204
  ca_size_t rdim[CA_RANK_MAX];
860
- ca_size_t rndim = RARRAY_LEN(ridx);
1205
+ ca_size_t rndim = argc;
861
1206
  ca_size_t j = 0, k;
862
1207
  for (i=0; i<rndim; i++) {
863
- rval = rb_ary_entry(ridx, i);
1208
+ rval = argv[i];
864
1209
  if ( rb_obj_is_carray(rval) ) {
865
1210
  TypedData_Get_Struct(rval, CArray, &carray_data_type, cv);
866
1211
  rdim[i] = 1;
867
1212
  for (k=0; k<cv->ndim; k++) {
1213
+ if ( j >= ca->ndim ) {
1214
+ rb_raise(rb_eArgError, "invalid total ndim of args");
1215
+ }
868
1216
  rdim[i] *= ca->dim[j];
869
1217
  j += 1;
870
1218
  }
@@ -881,87 +1229,69 @@ rb_ca_grid (int argc, VALUE *argv, VALUE self)
881
1229
  return rb_ca_grid(argc, argv, ref);
882
1230
  }
883
1231
 
884
- /* Build per-axis protos:
885
- nil -> STRIDE(0, parent.dim, 1), no CArray
886
- Range -> STRIDE if plain integer Range, else fall through to INDEX
887
- CArray -> INDEX with indices = (ca_size_t*)ca_wrap_readonly(...)->ptr */
1232
+ /* Dispatch hook: route a boolean on axis 0 with plain keys elsewhere to
1233
+ CASelectAxis (slab-copy optimised path). Falls through to CAGrid when
1234
+ not eligible. The inline argv[0] type check avoids the call when axis
1235
+ 0 is nil / Integer (most patterns with the CArray on an inner axis). */
1236
+ extern int ca_csa_dispatch_bypass;
1237
+ if ( ! ca_csa_dispatch_bypass && argc > 0 && TYPE(argv[0]) == T_DATA ) {
1238
+ if ( rb_ca_select_axis_eligible_p(argc, argv, self) ) {
1239
+ return ca_grid_drop_scalar_axes(rb_ca_select_axis(argc, argv, self),
1240
+ argc, argv);
1241
+ }
1242
+ }
1243
+
888
1244
  for (i = 0; i < ca->ndim; i++) {
889
1245
  ca_size_t dim_size = ca->dim[i];
890
- rval = rb_ary_entry(ridx, i);
1246
+ rval = argv[i];
891
1247
  ci[i] = NULL;
892
- if ( NIL_P(rval) ) {
1248
+ if ( ! rb_obj_is_carray(rval) ) {
1249
+ ca_size_t start, count, step;
1250
+ ca_index_axis_key(rval, dim_size, (int) i, &start, &count, &step);
893
1251
  protos[i].kind = CAG_AXIS_STRIDE;
894
- protos[i].count = dim_size;
895
- protos[i].start = 0;
896
- protos[i].step = 1;
1252
+ protos[i].count = count;
1253
+ protos[i].start = start;
1254
+ protos[i].step = step;
897
1255
  protos[i].indices = NULL;
1256
+ protos[i].holes = NULL;
898
1257
  continue;
899
1258
  }
900
- if ( rb_obj_is_kind_of(rval, rb_cRange) ) {
901
- ca_size_t start, count;
902
- if ( cag_range_to_stride(rval, dim_size, &start, &count) ) {
903
- protos[i].kind = CAG_AXIS_STRIDE;
904
- protos[i].count = count;
905
- protos[i].start = start;
906
- protos[i].step = 1;
907
- protos[i].indices = NULL;
908
- continue;
909
- }
910
- /* fallback: materialise to integer array */
911
- rval = rb_funcall(rb_mKernel, rb_intern("CA_SIZE"), 1, rval);
912
- }
913
- else if ( rb_obj_is_carray(rval) ) {
914
- if ( rb_ca_is_boolean_type(rval) ) {
915
- rval = rb_ca_where(rval);
1259
+ if ( rb_ca_is_boolean_type(rval) ) {
1260
+ CArray *cb;
1261
+ TypedData_Get_Struct(rval, CArray, &carray_data_type, cb);
1262
+ if ( cb->elements != dim_size ) {
1263
+ rb_raise(rb_eArgError,
1264
+ "mismatch of # of elements ( %lld <=> %lld ) in reference "
1265
+ "by selection on axis %d",
1266
+ (long long) cb->elements, (long long) dim_size, (int) i);
916
1267
  }
917
- }
918
- else if ( TYPE(rval) == T_ARRAY ) {
919
- rb_raise(rb_eRuntimeError, "not implemented for this index");
1268
+ rval = rb_ca_where(rval);
920
1269
  }
921
1270
 
922
- /* INDEX axis */
1271
+ /* INDEX axis: converted here, read once the window below opens */
1272
+ ca_check_index_array(rval);
923
1273
  ci[i] = ca_wrap_readonly(rval, CA_SIZE);
924
1274
  rb_ary_push(list, rval);
925
- ca_attach(ci[i]);
926
-
927
- protos[i].kind = CAG_AXIS_INDEX;
928
- if ( ca_is_any_masked(ci[i]) ) {
929
- /* mask-filtered snapshot: skip masked cells. We point protos[i]
930
- .indices at a temp buffer alloc'd here and freed after setup
931
- copies it (setup snapshots into its own ALLOC). Caller-side
932
- lifetime: the temp lives until ca_grid_new returns. */
933
- ca_size_t gsize = ci[i]->elements - ca_count_masked(ci[i]);
934
- ca_size_t *src = (ca_size_t *) ci[i]->ptr;
935
- boolean8_t *m = (boolean8_t *) ci[i]->mask->ptr;
936
- ca_size_t *tmp = ALLOC_N(ca_size_t, gsize > 0 ? gsize : 1);
937
- ca_size_t j, n = 0;
938
- for (j = 0; j < ci[i]->elements; j++) {
939
- if ( ! m[j] ) tmp[n++] = src[j];
940
- }
941
- protos[i].count = gsize;
942
- protos[i].indices = tmp;
943
- } else {
944
- protos[i].count = ci[i]->elements;
945
- protos[i].indices = (ca_size_t *) ci[i]->ptr;
946
- }
947
- protos[i].start = 0;
948
- protos[i].step = 0;
949
1275
  }
950
1276
 
951
- obj = rb_ca_grid_new(self, ca->ndim, protos);
952
-
953
- /* Cleanup: detach CAWraps + free temp masked-snapshot buffers (setup
954
- has already copied indices into its own ALLOC). */
955
- for (i = 0; i < ca->ndim; i++) {
956
- if ( ci[i] ) {
957
- if ( ca_is_any_masked(ci[i]) ) {
958
- xfree(protos[i].indices);
959
- }
960
- ca_detach(ci[i]);
961
- }
1277
+ /* The index arrays are read with all of them attached, and building
1278
+ the view checks every index against the parent -- an index out of
1279
+ range raises there. So the reads and the build are a window body,
1280
+ which detaches the caller's index arrays however it ends. */
1281
+ {
1282
+ ca_grid_build_ctx_t c;
1283
+ char modes[CA_RANK_MAX + 1];
1284
+ memset(modes, 'r', (size_t) ca->ndim);
1285
+ modes[ca->ndim] = '\0';
1286
+ c.self = self;
1287
+ c.ca = ca;
1288
+ c.ci = ci;
1289
+ c.protos = protos;
1290
+ obj = ca_attach_window(ca->ndim, ci, modes, ca_grid_build_body,
1291
+ (VALUE) &c);
962
1292
  }
963
1293
 
964
- return obj;
1294
+ return ca_grid_drop_scalar_axes(obj, argc, argv);
965
1295
  }
966
1296
 
967
1297
  static VALUE
@@ -977,14 +1307,17 @@ rb_ca_grid_initialize_copy (VALUE self, VALUE other)
977
1307
  CAGrid *ca, *cs;
978
1308
 
979
1309
  TypedData_Get_Struct(self, CAGrid, &cagrid_data_type, ca);
1310
+ ca_check_uninitialized(ca);
980
1311
  TypedData_Get_Struct(other, CAGrid, &cagrid_data_type, cs);
981
1312
 
982
1313
  if ( ca_func[CA_OBJ_GRID].pool_init ) {
983
1314
  ca_array_pool_alloc(ca, CA_OBJ_GRID, cs->ndim);
984
1315
  }
985
- /* Share the source's axes buffer (CA_FLAG_SHARE_INDEX semantics:
986
- source CAGrid owns the lifetime; clone aliases it). */
987
- ca_grid_setup(ca, cs->parent, cs->ndim, cs->axes, 1);
1316
+ /* Copy the axes: the source may be collected before the copy. */
1317
+ ca_grid_setup(ca, cs->parent, cs->ndim, cs->axes, 0);
1318
+ ca->holes_read_masked = cs->holes_read_masked;
1319
+ ca->mask_owner = cs->mask_owner;
1320
+ ca_grid_inherit_mask((VALUE) ca);
988
1321
 
989
1322
  return self;
990
1323
  }
@@ -1087,7 +1420,7 @@ rb_ca_grid_dispatch_attach_debug (VALUE self)
1087
1420
  TypedData_Get_Struct(self, CAGrid, &cagrid_data_type, ca);
1088
1421
  ca_grid_describe_axes(ca, desc, pdims);
1089
1422
 
1090
- ca_attach(ca->parent);
1423
+ ca_attach(ca->parent); /* window: nothing raises inside */
1091
1424
  buf = ca_axis_dispatch_attach(ca->parent, pdims, desc, ca->ndim, ca->bytes,
1092
1425
  ca->elements, NULL);
1093
1426
  ca_detach(ca->parent);
@@ -1118,11 +1451,10 @@ rb_ca_grid_dispatch_scatter_debug (VALUE self, VALUE in_str)
1118
1451
  ca_size_t pdims[CA_RANK_MAX];
1119
1452
  ca_grid_describe_axes(ca, desc, pdims);
1120
1453
 
1121
- ca_attach(ca->parent);
1454
+ ca_attach(ca->parent); /* window: nothing raises inside */
1122
1455
  ca_axis_dispatch_scatter(ca->parent, pdims, desc, ca->ndim, ca->bytes,
1123
1456
  ca->elements, RSTRING_PTR(in_str));
1124
- ca_sync(ca->parent);
1125
- ca_detach(ca->parent);
1457
+ ca_sync_detach(ca->parent);
1126
1458
  return Qnil;
1127
1459
  }
1128
1460
 
@@ -1145,11 +1477,10 @@ rb_ca_grid_dispatch_fill_value_debug (VALUE self, VALUE val_str)
1145
1477
  ca_size_t pdims[CA_RANK_MAX];
1146
1478
  ca_grid_describe_axes(ca, desc, pdims);
1147
1479
 
1148
- ca_attach(ca->parent);
1480
+ ca_attach(ca->parent); /* window: nothing raises inside */
1149
1481
  ca_axis_dispatch_fill_value(ca->parent, pdims, desc, ca->ndim, ca->bytes,
1150
1482
  ca->elements, RSTRING_PTR(val_str));
1151
- ca_sync(ca->parent);
1152
- ca_detach(ca->parent);
1483
+ ca_sync_detach(ca->parent);
1153
1484
  return Qnil;
1154
1485
  }
1155
1486
  #endif /* CARRAY_DEV_BUILD */