carray 3.0.1 → 3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +824 -0
  3. data/README.md +5 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +258 -4
  6. data/ext/ca_axis_group.c +368 -184
  7. data/ext/ca_binop_dispatch.c +54 -32
  8. data/ext/ca_binop_dispatch.h +1 -0
  9. data/ext/ca_categorical_iterator.c +90 -41
  10. data/ext/ca_compare.c +8 -2
  11. data/ext/ca_for_buffer.h +21 -68
  12. data/ext/ca_group_iter.c +27 -11
  13. data/ext/ca_kernel_iterator.c +960 -245
  14. data/ext/ca_kernel_iterator.h +352 -181
  15. data/ext/ca_monop_dispatch.c +4 -4
  16. data/ext/ca_monop_dispatch.h +4 -0
  17. data/ext/ca_obj_array.c +194 -42
  18. data/ext/ca_obj_bincmp.c +152 -121
  19. data/ext/ca_obj_binop.c +107 -210
  20. data/ext/ca_obj_bitarray.c +37 -24
  21. data/ext/ca_obj_bitfield.c +95 -62
  22. data/ext/ca_obj_block.c +5 -4
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +180 -50
  25. data/ext/ca_obj_face.c +428 -62
  26. data/ext/ca_obj_face.h +107 -47
  27. data/ext/ca_obj_fake.c +21 -50
  28. data/ext/ca_obj_farray.c +1 -0
  29. data/ext/ca_obj_field.c +16 -3
  30. data/ext/ca_obj_fixlen_string.c +39 -26
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +235 -55
  33. data/ext/ca_obj_moncmp.c +61 -61
  34. data/ext/ca_obj_monop.c +125 -107
  35. data/ext/ca_obj_object.c +551 -233
  36. data/ext/ca_obj_record.c +51 -16
  37. data/ext/ca_obj_reduce.c +5 -8
  38. data/ext/ca_obj_refer.c +117 -33
  39. data/ext/ca_obj_remap.c +57 -17
  40. data/ext/ca_obj_repeat.c +21 -5
  41. data/ext/ca_obj_roll.c +115 -11
  42. data/ext/ca_obj_select.c +88 -75
  43. data/ext/ca_obj_select_axis.c +58 -60
  44. data/ext/ca_obj_shift.c +22 -102
  45. data/ext/ca_obj_stack.c +223 -123
  46. data/ext/ca_obj_stride.c +267 -35
  47. data/ext/ca_obj_string.c +25 -22
  48. data/ext/ca_obj_tile.c +32 -16
  49. data/ext/ca_obj_time.c +23 -25
  50. data/ext/ca_obj_timedelta.c +21 -23
  51. data/ext/ca_obj_transpose.c +12 -3
  52. data/ext/ca_obj_triop.c +110 -117
  53. data/ext/ca_obj_window.c +157 -29
  54. data/ext/ca_op_ipower.c +25 -13
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_rng_normal.h +42 -0
  58. data/ext/ca_rng_xoshiro256pp.h +105 -0
  59. data/ext/ca_sort_kernels.h +70 -0
  60. data/ext/ca_sweep_engine.c +385 -198
  61. data/ext/ca_sweep_engine.h +31 -23
  62. data/ext/ca_triop_dispatch.c +4 -1
  63. data/ext/ca_triop_dispatch.h +5 -2
  64. data/ext/caframe_csv_reader.c +463 -0
  65. data/ext/carray.h +90 -28
  66. data/ext/carray_access.c +487 -231
  67. data/ext/carray_address_basis.c +731 -0
  68. data/ext/carray_attribute.c +45 -3
  69. data/ext/carray_bincount.c +32 -2
  70. data/ext/carray_broadcast.c +49 -17
  71. data/ext/carray_call_cfunc.c +667 -511
  72. data/ext/carray_cast.c +1556 -247
  73. data/ext/carray_cast_func.rb +34 -21
  74. data/ext/carray_conversion.c +197 -73
  75. data/ext/carray_copy.c +84 -38
  76. data/ext/carray_core.c +591 -119
  77. data/ext/carray_count.c +148 -96
  78. data/ext/carray_element.c +18 -27
  79. data/ext/carray_factorize.c +604 -291
  80. data/ext/carray_generate.c +76 -40
  81. data/ext/carray_histogram.c +152 -94
  82. data/ext/carray_hold.c +2 -1
  83. data/ext/carray_index_classifier.c +63 -96
  84. data/ext/carray_internal.h +400 -1
  85. data/ext/carray_lazy.c +834 -79
  86. data/ext/carray_loop.c +88 -66
  87. data/ext/carray_mask.c +705 -324
  88. data/ext/carray_math_kernel.h +1 -0
  89. data/ext/carray_median_percentile.c +263 -63
  90. data/ext/carray_memory_view.c +254 -248
  91. data/ext/carray_operator.c +1093 -363
  92. data/ext/carray_order.c +133 -69
  93. data/ext/carray_partition.c +102 -56
  94. data/ext/carray_random.c +582 -204
  95. data/ext/carray_scatter.c +308 -65
  96. data/ext/carray_segment.c +257 -0
  97. data/ext/carray_slab.c +183 -70
  98. data/ext/carray_sort.c +235 -240
  99. data/ext/carray_sort_kernel.c +8 -1
  100. data/ext/carray_struct.c +99 -21
  101. data/ext/carray_test.c +167 -64
  102. data/ext/carray_time_text.c +1182 -0
  103. data/ext/carray_utils.c +363 -98
  104. data/ext/extconf.rb +27 -2
  105. data/ext/mk_call_cfunc.rb +103 -118
  106. data/ext/mk_pow5_table.rb +35 -0
  107. data/ext/mkkernel.rb +1349 -851
  108. data/ext/ruby_carray.c +17 -2
  109. data/ext/version.h +4 -4
  110. data/ext/version.rb +4 -2
  111. data/lib/carray/attribute.rb +39 -0
  112. data/lib/carray/autoload_carray.rb +24 -3
  113. data/lib/carray/autoload_method_extension.rb +18 -0
  114. data/lib/carray/axis_group.rb +245 -20
  115. data/lib/carray/basics.rb +95 -25
  116. data/lib/carray/bincount_nd.rb +6 -0
  117. data/lib/carray/block_iterator.rb +200 -54
  118. data/lib/carray/boolean_reduce.rb +21 -29
  119. data/lib/carray/categorical.rb +244 -59
  120. data/lib/carray/categorical_iterator.rb +391 -757
  121. data/lib/carray/complex.rb +36 -29
  122. data/lib/carray/conditional.rb +94 -7
  123. data/lib/carray/const_string.rb +177 -27
  124. data/lib/carray/construct.rb +40 -0
  125. data/lib/carray/data_type_extension.rb +3 -0
  126. data/lib/carray/data_type_limits.rb +91 -0
  127. data/lib/carray/fixlen_string.rb +1 -1
  128. data/lib/carray/frame/concat.rb +157 -6
  129. data/lib/carray/frame/convert.rb +13 -7
  130. data/lib/carray/frame/csv_parser.rb +590 -76
  131. data/lib/carray/frame/describe.rb +79 -0
  132. data/lib/carray/frame/frame.rb +205 -35
  133. data/lib/carray/frame/group.rb +199 -40
  134. data/lib/carray/frame/io.rb +452 -60
  135. data/lib/carray/frame/join.rb +134 -46
  136. data/lib/carray/frame/records.rb +54 -19
  137. data/lib/carray/frame/reshape.rb +448 -0
  138. data/lib/carray/frame/sort.rb +26 -2
  139. data/lib/carray/frame/verbs.rb +726 -56
  140. data/lib/carray/frame.rb +2 -0
  141. data/lib/carray/fuse_source.rb +111 -2
  142. data/lib/carray/fusion.rb +229 -38
  143. data/lib/carray/inspect.rb +71 -20
  144. data/lib/carray/iterator.rb +157 -2
  145. data/lib/carray/lazy.rb +54 -67
  146. data/lib/carray/mask_gap_fill.rb +66 -50
  147. data/lib/carray/math.rb +32 -19
  148. data/lib/carray/meld_reduce.rb +40 -7
  149. data/lib/carray/methods/align_addr.rb +8 -4
  150. data/lib/carray/methods/bincount.rb +2 -2
  151. data/lib/carray/methods/bit_string.rb +14 -3
  152. data/lib/carray/methods/composition.rb +16 -10
  153. data/lib/carray/methods/discovery_along.rb +74 -0
  154. data/lib/carray/methods/factorize.rb +50 -0
  155. data/lib/carray/methods/gather_nd.rb +13 -2
  156. data/lib/carray/methods/insert_block.rb +9 -14
  157. data/lib/carray/methods/is_in.rb +130 -6
  158. data/lib/carray/methods/join.rb +1 -1
  159. data/lib/carray/methods/locate_addr.rb +235 -21
  160. data/lib/carray/methods/mask_duplicates.rb +35 -1
  161. data/lib/carray/methods/mode.rb +18 -1
  162. data/lib/carray/methods/nunique.rb +22 -1
  163. data/lib/carray/methods/repeat.rb +91 -0
  164. data/lib/carray/methods/resize.rb +21 -23
  165. data/lib/carray/methods/snap.rb +34 -12
  166. data/lib/carray/methods/unique.rb +52 -3
  167. data/lib/carray/record_order.rb +208 -0
  168. data/lib/carray/rng.rb +86 -0
  169. data/lib/carray/runtime.rb +14 -11
  170. data/lib/carray/segment_iterator.rb +990 -0
  171. data/lib/carray/serialize.rb +197 -27
  172. data/lib/carray/slab_iterator.rb +109 -40
  173. data/lib/carray/stack.rb +10 -4
  174. data/lib/carray/string_operation_extension.rb +9 -4
  175. data/lib/carray/struct.rb +142 -51
  176. data/lib/carray/struct_builder.rb +110 -14
  177. data/lib/carray/table.rb +2 -2
  178. data/lib/carray/time.rb +535 -105
  179. data/lib/carray/window_iterator.rb +259 -82
  180. data/lib/carray.rb +4 -2
  181. data/yard-stubs/ca_obj_array.rb +4 -4
  182. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  183. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  184. data/yard-stubs/ca_obj_block.rb +2 -7
  185. data/yard-stubs/ca_obj_fake.rb +1 -1
  186. data/yard-stubs/ca_obj_field.rb +1 -1
  187. data/yard-stubs/ca_obj_refer.rb +3 -3
  188. data/yard-stubs/ca_obj_roll.rb +0 -1
  189. data/yard-stubs/ca_obj_stride.rb +10 -7
  190. data/yard-stubs/ca_obj_transpose.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +11 -4
  192. data/yard-stubs/carray_access.rb +6 -5
  193. data/yard-stubs/carray_broadcast.rb +2 -2
  194. data/yard-stubs/carray_cast.rb +78 -43
  195. data/yard-stubs/carray_conversion.rb +13 -1
  196. data/yard-stubs/carray_copy.rb +8 -3
  197. data/yard-stubs/carray_core.rb +3 -83
  198. data/yard-stubs/carray_count.rb +16 -2
  199. data/yard-stubs/carray_lazy.rb +210 -1
  200. data/yard-stubs/carray_loop.rb +4 -1
  201. data/yard-stubs/carray_mask.rb +34 -10
  202. data/yard-stubs/carray_math.rb +1499 -4
  203. data/yard-stubs/carray_median_percentile.rb +26 -8
  204. data/yard-stubs/carray_order.rb +43 -82
  205. data/yard-stubs/carray_scatter.rb +10 -3
  206. data/yard-stubs/carray_segment.rb +72 -0
  207. data/yard-stubs/carray_slab.rb +11 -8
  208. data/yard-stubs/carray_sort.rb +50 -8
  209. data/yard-stubs/carray_test.rb +10 -0
  210. data/yard-stubs/carray_utils.rb +0 -26
  211. metadata +19 -19
  212. data/ext/ca_for_each_element.h +0 -239
  213. data/ext/carray_build_flags.h +0 -3
  214. data/ext/carray_config.h +0 -28
  215. data/ext/carray_kernels_bincmp.c +0 -4446
  216. data/ext/carray_kernels_binop.c +0 -11001
  217. data/ext/carray_kernels_init.c +0 -1131
  218. data/ext/carray_kernels_map.c +0 -3467
  219. data/ext/carray_kernels_moncmp.c +0 -2097
  220. data/ext/carray_kernels_monop.c +0 -18313
  221. data/ext/carray_kernels_reduce_aggregate.c +0 -25837
  222. data/ext/carray_kernels_reduce_boolean.c +0 -330
  223. data/ext/carray_kernels_reduce_cumulative.c +0 -14593
  224. data/ext/carray_kernels_reduce_extreme.c +0 -16948
  225. data/ext/carray_kernels_reduce_variance.c +0 -3910
  226. data/ext/carray_kernels_scan.c +0 -3693
  227. data/ext/carray_kernels_search.c +0 -32138
  228. data/ext/carray_kernels_sort.c +0 -10626
  229. data/ext/carray_kernels_triop.c +0 -1392
data/ext/ca_obj_object.c CHANGED
@@ -34,6 +34,7 @@
34
34
  */
35
35
 
36
36
  #include "carray.h"
37
+ #include "carray_internal.h"
37
38
  #include "ca_obj_face.h" /* ca_face_* helpers for Face mode shortcuts */
38
39
 
39
40
  static size_t
@@ -82,11 +83,21 @@ ca_objectmask_dsize (const void *ap)
82
83
  return sizeof(CAObjectMask) + ca->ndim * sizeof(ca_size_t);
83
84
  }
84
85
 
86
+ /* The mask reads and writes through its CAObject, and its cache is the
87
+ CAObject's; it keeps the CAObject alive. */
88
+ static void
89
+ ca_objmask_mark (void *ap)
90
+ {
91
+ CAObjectMask *ca = (CAObjectMask *) ap;
92
+ rb_gc_mark(ca->array);
93
+ ca_mark(ap);
94
+ }
95
+
85
96
  const rb_data_type_t caobjectmask_data_type = {
86
97
  .parent = &carray_data_type,
87
98
  .wrap_struct_name = "CAObjectMask",
88
99
  .function = {
89
- .dmark = ca_mark,
100
+ .dmark = ca_objmask_mark,
90
101
  .dfree = ca_free,
91
102
  .dsize = ca_objectmask_dsize,
92
103
  .dcompact = NULL
@@ -110,6 +121,8 @@ ca_objmask_new (VALUE array, int8_t ndim, ca_size_t *dim)
110
121
  ca_wrap_setup_null((CArray *)ca, CA_BOOLEAN, ndim, dim, 0, NULL);
111
122
  ca->obj_type = CA_OBJ_OBJECT_MASK;
112
123
  ca->array = array;
124
+ ca->cache = NULL;
125
+ ca->holds = 0;
113
126
 
114
127
  return ca;
115
128
  }
@@ -122,125 +135,358 @@ ca_objmask_mask_data (void *ap)
122
135
  rb_intern("mask"), 0);
123
136
  }
124
137
 
138
+ /* Calls recv.mid(arg(ap)) with *slot pointing at a caller's buffer for
139
+ the duration, and puts *slot back however the call leaves. The bulk
140
+ bridge lends the buffer this way so that copy_data / sync_data write
141
+ straight into it; a raise that skipped the restore would leave the
142
+ array pointing at a buffer its caller is about to free. */
143
+
144
+ typedef struct {
145
+ char **slot;
146
+ char *saved;
147
+ VALUE recv;
148
+ ID mid;
149
+ VALUE (*arg)(void *);
150
+ void *ap;
151
+ } ca_object_lend_t;
152
+
153
+ static VALUE
154
+ ca_object_lend_body (VALUE varg)
155
+ {
156
+ ca_object_lend_t *l = (ca_object_lend_t *) varg;
157
+ return rb_funcall(l->recv, l->mid, 1, l->arg(l->ap));
158
+ }
159
+
160
+ static VALUE
161
+ ca_object_lend_restore (VALUE varg)
162
+ {
163
+ ca_object_lend_t *l = (ca_object_lend_t *) varg;
164
+ *l->slot = l->saved;
165
+ return Qnil;
166
+ }
167
+
168
+ static void
169
+ ca_object_call_lending (char **slot, void *ptr, VALUE recv, const char *mid,
170
+ VALUE (*arg)(void *), void *ap)
171
+ {
172
+ ca_object_lend_t l;
173
+ l.slot = slot;
174
+ l.saved = *slot;
175
+ l.recv = recv;
176
+ l.mid = rb_intern(mid);
177
+ l.arg = arg;
178
+ l.ap = ap;
179
+ *slot = (char *) ptr;
180
+ rb_ensure(ca_object_lend_body, (VALUE) &l,
181
+ ca_object_lend_restore, (VALUE) &l);
182
+ }
183
+
184
+ /* Calls recv.mid(*argv), where some of argv are transient wraps of a
185
+ caller's buffer (ca_object_wrap_transient), and takes the buffer back
186
+ from each of them however the call leaves. The caller frees its buffer
187
+ when it returns, and a wrap outlives the call: the collector scans the
188
+ machine stack conservatively and still finds it there, and an object
189
+ wrap is marked cell by cell -- through a buffer that is gone. A wrap
190
+ taken back points at nothing; a hook that kept it holds an empty
191
+ handle, not a stale one. */
192
+
193
+ typedef struct {
194
+ VALUE recv;
195
+ ID mid;
196
+ int argc;
197
+ const VALUE *argv;
198
+ int ntrans;
199
+ const VALUE *trans;
200
+ } ca_object_hook_t;
201
+
202
+ static VALUE
203
+ ca_object_hook_body (VALUE varg)
204
+ {
205
+ ca_object_hook_t *h = (ca_object_hook_t *) varg;
206
+ return rb_funcallv(h->recv, h->mid, h->argc, h->argv);
207
+ }
208
+
209
+ static VALUE
210
+ ca_object_hook_release (VALUE varg)
211
+ {
212
+ ca_object_hook_t *h = (ca_object_hook_t *) varg;
213
+ int i;
214
+ for ( i = 0; i < h->ntrans; i++ ) {
215
+ ((CArray *) DATA_PTR(h->trans[i]))->ptr = NULL;
216
+ }
217
+ return Qnil;
218
+ }
219
+
220
+ static void
221
+ ca_object_call_transient (VALUE recv, const char *mid,
222
+ int argc, const VALUE *argv,
223
+ int ntrans, const VALUE *trans)
224
+ {
225
+ ca_object_hook_t h;
226
+ h.recv = recv;
227
+ h.mid = rb_intern(mid);
228
+ h.argc = argc;
229
+ h.argv = argv;
230
+ h.ntrans = ntrans;
231
+ h.trans = trans;
232
+ rb_ensure(ca_object_hook_body, (VALUE) &h,
233
+ ca_object_hook_release, (VALUE) &h);
234
+ }
235
+
236
+ static VALUE
237
+ ca_object_data_of (void *ap)
238
+ {
239
+ return rb_ivar_get(((CAObject *) ap)->self, rb_intern("__data__"));
240
+ }
241
+
242
+
243
+ /* Forward declaration: ca_object_wrap_transient (defined further down,
244
+ near the data-side xfer functions). */
245
+ static VALUE
246
+ ca_object_wrap_transient (int8_t data_type, ca_size_t bytes,
247
+ int8_t ndim, ca_size_t *dim, void *data, int dir);
248
+
125
249
  static void *
126
250
  ca_objmask_func_clone (void *ap)
127
251
  {
128
252
  CAObjectMask *ca = (CAObjectMask *) ap;
129
- return ca_objmask_new(ca->array, ca->ndim, ca->dim);
253
+ CAObjectMask *co = ca_objmask_new(ca->array, ca->ndim, ca->dim);
254
+ co->cache = ca->cache;
255
+ return co;
130
256
  }
131
257
 
132
- /* per-cell cores: objmask reads/writes its own CA_BOOLEAN bit storage
133
- (a contiguous wrap over the shared mask bytes) AND mirrors through the
134
- user's Ruby mask_fetch_* / mask_store_* callbacks. Entity bit access
135
- goes through ca_array_func_xfer_index / xfer_addrs. GET/PUT
136
- direction-unified; the slots below forward. */
137
- static void
138
- ca_objmask_xfer_addr_one (CAObjectMask *ca, ca_size_t addr, void *ptr, int dir)
258
+ /* Whether the CAObject answers one of the mask hooks. A copy made by dup
259
+ holds its own bits and answers none (array is nil). */
260
+ static int
261
+ ca_objmask_answers (CAObjectMask *ca, const char *mid)
139
262
  {
140
- volatile VALUE ridx, raddr, rval;
263
+ return ! NIL_P(ca->array) && rb_obj_respond_to(ca->array, rb_intern(mid), Qtrue);
264
+ }
265
+
266
+ static VALUE
267
+ ca_objmask_index_of (CAObjectMask *ca, ca_size_t addr)
268
+ {
269
+ ca_size_t idx[CA_RANK_MAX];
270
+ VALUE ridx;
141
271
  int i;
142
- if ( dir == CA_XFER_GET ) {
143
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_fetch_addr"), Qtrue) ) {
144
- raddr = SIZE2NUM(addr);
145
- rval = rb_funcall(ca->array, rb_intern("mask_fetch_addr"), 1, raddr);
146
- *(uint8_t*) ptr = NUM2INT(rval) == 0 ? 0 : 1;
147
- ca_array_func_xfer_addrs(ca, 1, &addr, ptr, CA_XFER_PUT);
148
- }
149
- else if ( rb_obj_respond_to(ca->array, rb_intern("mask_fetch_index"), Qtrue) ) {
150
- ca_size_t idx[CA_RANK_MAX];
151
- ca_addr2index((CArray *)ca, addr, idx);
152
- ridx = rb_ary_new2(ca->ndim);
153
- for (i=0; i<ca->ndim; i++) {
154
- rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
155
- }
156
- rval = rb_funcall(ca->array, rb_intern("mask_fetch_index"), 1, ridx);
157
- *(uint8_t*) ptr = NUM2INT(rval) == 0 ? 0 : 1;
158
- ca_array_func_xfer_index(ca, idx, ptr, CA_XFER_PUT);
159
- }
160
- else {
161
- ca_array_func_xfer_addrs(ca, 1, &addr, ptr, CA_XFER_GET);
162
- }
272
+ ca_addr2index((CArray *) ca, addr, idx);
273
+ ridx = rb_ary_new2(ca->ndim);
274
+ for (i=0; i<ca->ndim; i++) {
275
+ rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
276
+ }
277
+ return ridx;
278
+ }
279
+
280
+ /* The bit the per-cell hooks give for addr (1 / 0 / true / false), or -1
281
+ when the object has neither mask_fetch_addr nor mask_fetch_index. */
282
+ static int
283
+ ca_objmask_fetch_one (CAObjectMask *ca, ca_size_t addr)
284
+ {
285
+ volatile VALUE rval;
286
+ if ( ca_objmask_answers(ca, "mask_fetch_addr") ) {
287
+ rval = rb_funcall(ca->array, rb_intern("mask_fetch_addr"), 1, SIZE2NUM(addr));
288
+ }
289
+ else if ( ca_objmask_answers(ca, "mask_fetch_index") ) {
290
+ rval = rb_funcall(ca->array, rb_intern("mask_fetch_index"), 1,
291
+ ca_objmask_index_of(ca, addr));
163
292
  }
164
293
  else {
165
- ca_array_func_xfer_addrs(ca, 1, &addr, ptr, CA_XFER_PUT);
166
- rval = INT2NUM( *(uint8_t*)ptr );
167
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_store_addr"), Qtrue) ) {
168
- raddr = SIZE2NUM(addr);
169
- rb_funcall(ca->array, rb_intern("mask_store_addr"), 2, raddr, rval);
294
+ return -1;
295
+ }
296
+ if ( rval == Qtrue || rval == Qfalse ) {
297
+ return RTEST(rval);
298
+ }
299
+ return NUM2INT(rval) != 0;
300
+ }
301
+
302
+ /* Hands one bit to the per-cell hooks; 0 when the object has neither
303
+ mask_store_addr nor mask_store_index. */
304
+ static int
305
+ ca_objmask_store_one (CAObjectMask *ca, ca_size_t addr, boolean8_t bit)
306
+ {
307
+ if ( ca_objmask_answers(ca, "mask_store_addr") ) {
308
+ rb_funcall(ca->array, rb_intern("mask_store_addr"), 2,
309
+ SIZE2NUM(addr), INT2NUM(bit ? 1 : 0));
310
+ return 1;
311
+ }
312
+ if ( ca_objmask_answers(ca, "mask_store_index") ) {
313
+ rb_funcall(ca->array, rb_intern("mask_store_index"), 2,
314
+ ca_objmask_index_of(ca, addr), INT2NUM(bit ? 1 : 0));
315
+ return 1;
316
+ }
317
+ return 0;
318
+ }
319
+
320
+ static int
321
+ ca_objmask_stores_cells (CAObjectMask *ca)
322
+ {
323
+ return ca_objmask_answers(ca, "mask_store_addr") ||
324
+ ca_objmask_answers(ca, "mask_store_index");
325
+ }
326
+
327
+ /* Brings the cache up to date from the hooks that give the mask: the bulk
328
+ one, else the per-cell one. With neither, the cache is the mask. */
329
+ static void
330
+ ca_objmask_refresh (CAObjectMask *ca)
331
+ {
332
+ boolean8_t *c = (boolean8_t *) ca->cache;
333
+ ca_size_t addr;
334
+ int bit;
335
+ if ( ca_objmask_answers(ca, "mask_copy_data") ) {
336
+ rb_funcall(ca->array, rb_intern("mask_copy_data"), 1,
337
+ ca_objmask_mask_data(ca));
338
+ return;
339
+ }
340
+ for ( addr = 0; addr < ca->elements; addr++ ) {
341
+ if ( ( bit = ca_objmask_fetch_one(ca, addr) ) < 0 ) {
342
+ return;
170
343
  }
171
- else if ( rb_obj_respond_to(ca->array, rb_intern("mask_store_index"), Qtrue) ) {
172
- ca_size_t idx[CA_RANK_MAX];
173
- ca_addr2index((CArray *)ca, addr, idx);
174
- ridx = rb_ary_new2(ca->ndim);
175
- for (i=0; i<ca->ndim; i++) {
176
- rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
177
- }
178
- rb_funcall(ca->array, rb_intern("mask_store_index"), 2, ridx, rval);
344
+ c[addr] = (boolean8_t) bit;
345
+ }
346
+ }
347
+
348
+ /* Hands the whole cache to the hooks that take the mask: the bulk one,
349
+ else the per-cell one. */
350
+ static void
351
+ ca_objmask_push (CAObjectMask *ca)
352
+ {
353
+ const boolean8_t *c = (const boolean8_t *) ca->cache;
354
+ ca_size_t addr;
355
+ if ( ca_objmask_answers(ca, "mask_sync_data") ) {
356
+ rb_funcall(ca->array, rb_intern("mask_sync_data"), 1,
357
+ ca_objmask_mask_data(ca));
358
+ return;
359
+ }
360
+ for ( addr = 0; addr < ca->elements; addr++ ) {
361
+ if ( ! ca_objmask_store_one(ca, addr, c[addr]) ) {
362
+ return;
179
363
  }
180
364
  }
181
365
  }
182
366
 
367
+ /* Reads n bits at addrs while the mask is closed: mask_copy_addrs, else
368
+ the per-cell hooks, else the whole mask brought up to date. */
183
369
  static void
184
- ca_objmask_xfer_index_one (CAObjectMask *ca, ca_size_t *idx, void *ptr, int dir)
370
+ ca_objmask_get_cells (CAObjectMask *ca, ca_size_t n, ca_size_t *addrs,
371
+ boolean8_t *bits)
185
372
  {
186
- volatile VALUE ridx, raddr, rval;
187
- int i;
188
- if ( dir == CA_XFER_GET ) {
189
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_fetch_index"), Qtrue) ) {
190
- ridx = rb_ary_new2(ca->ndim);
191
- for (i=0; i<ca->ndim; i++) {
192
- rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
193
- }
194
- rval = rb_funcall(ca->array, rb_intern("mask_fetch_index"), 1, ridx);
195
- *(uint8_t*) ptr = NUM2INT(rval) == 0 ? 0 : 1;
196
- ca_array_func_xfer_index(ca, idx, ptr, CA_XFER_PUT);
373
+ boolean8_t *c = (boolean8_t *) ca->cache;
374
+ ca_size_t i;
375
+ int bit;
376
+ if ( n > 0 && ca_objmask_answers(ca, "mask_copy_addrs") ) {
377
+ volatile VALUE raddrs, rdata;
378
+ ca_size_t dim1[1] = { n };
379
+ raddrs = ca_object_wrap_transient(CA_SIZE, sizeof(ca_size_t),
380
+ 1, dim1, addrs, CA_XFER_PUT);
381
+ rdata = ca_object_wrap_transient(CA_BOOLEAN, 1, 1, dim1, bits,
382
+ CA_XFER_GET);
383
+ {
384
+ VALUE args[2];
385
+ args[0] = raddrs;
386
+ args[1] = rdata;
387
+ ca_object_call_transient(ca->array, "mask_copy_addrs", 2, args, 2, args);
197
388
  }
198
- else if ( rb_obj_respond_to(ca->array, rb_intern("mask_fetch_addr"), Qtrue) ) {
199
- ca_size_t addr = ca_index2addr((CArray *)ca, idx);
200
- raddr = SIZE2NUM(addr);
201
- rval = rb_funcall(ca->array, rb_intern("mask_fetch_addr"), 1, raddr);
202
- *(uint8_t*) ptr = NUM2INT(rval) == 0 ? 0 : 1;
203
- ca_array_func_xfer_addrs(ca, 1, &addr, ptr, CA_XFER_PUT);
389
+ return;
390
+ }
391
+ if ( n > 0 && ( bit = ca_objmask_fetch_one(ca, addrs[0]) ) >= 0 ) {
392
+ c[addrs[0]] = bits[0] = (boolean8_t) bit;
393
+ for ( i = 1; i < n; i++ ) {
394
+ c[addrs[i]] = bits[i] = (boolean8_t) ca_objmask_fetch_one(ca, addrs[i]);
204
395
  }
205
- else {
206
- ca_array_func_xfer_index(ca, idx, ptr, CA_XFER_GET);
396
+ return;
397
+ }
398
+ ca_objmask_refresh(ca);
399
+ for ( i = 0; i < n; i++ ) {
400
+ bits[i] = c[addrs[i]];
401
+ }
402
+ }
403
+
404
+ /* Writes n bits at addrs while the mask is closed, and hands them over:
405
+ one by one to the per-cell hooks, as a list to mask_sync_addrs, or else
406
+ the whole mask to mask_sync_data -- brought up to date first, so that
407
+ the cells not written go back as they were. */
408
+ static void
409
+ ca_objmask_put_cells (CAObjectMask *ca, ca_size_t n, ca_size_t *addrs,
410
+ const boolean8_t *bits)
411
+ {
412
+ boolean8_t *c = (boolean8_t *) ca->cache;
413
+ ca_size_t i;
414
+ if ( ca_objmask_stores_cells(ca) ) {
415
+ for ( i = 0; i < n; i++ ) {
416
+ c[addrs[i]] = bits[i] ? 1 : 0;
417
+ ca_objmask_store_one(ca, addrs[i], c[addrs[i]]);
207
418
  }
419
+ return;
208
420
  }
209
- else {
210
- ca_array_func_xfer_index(ca, idx, ptr, CA_XFER_PUT);
211
- rval = INT2NUM( *(uint8_t*)ptr );
212
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_store_index"), Qtrue) ) {
213
- ridx = rb_ary_new2(ca->ndim);
214
- for (i=0; i<ca->ndim; i++) {
215
- rb_ary_store(ridx, i, SIZE2NUM(idx[i]));
216
- }
217
- rb_funcall(ca->array, rb_intern("mask_store_index"), 2, ridx, rval);
421
+ if ( n > 0 && ca_objmask_answers(ca, "mask_sync_addrs") ) {
422
+ volatile VALUE raddrs, rdata;
423
+ ca_size_t dim1[1] = { n };
424
+ for ( i = 0; i < n; i++ ) {
425
+ c[addrs[i]] = bits[i] ? 1 : 0;
218
426
  }
219
- else if ( rb_obj_respond_to(ca->array, rb_intern("mask_store_addr"), Qtrue) ) {
220
- ca_size_t addr = ca_index2addr((CArray *)ca, idx);
221
- raddr = SIZE2NUM(addr);
222
- rb_funcall(ca->array, rb_intern("mask_store_addr"), 2, raddr, rval);
427
+ raddrs = ca_object_wrap_transient(CA_SIZE, sizeof(ca_size_t),
428
+ 1, dim1, addrs, CA_XFER_PUT);
429
+ rdata = ca_object_wrap_transient(CA_BOOLEAN, 1, 1, dim1, (void *) bits,
430
+ CA_XFER_PUT);
431
+ {
432
+ VALUE args[2];
433
+ args[0] = raddrs;
434
+ args[1] = rdata;
435
+ ca_object_call_transient(ca->array, "mask_sync_addrs", 2, args, 2, args);
223
436
  }
437
+ return;
438
+ }
439
+ if ( ca_objmask_answers(ca, "mask_sync_data") ) {
440
+ ca_objmask_refresh(ca);
441
+ }
442
+ for ( i = 0; i < n; i++ ) {
443
+ c[addrs[i]] = bits[i] ? 1 : 0;
224
444
  }
445
+ ca_objmask_push(ca);
446
+ }
447
+
448
+ /* Open: the cache is published in ptr, and it is the mask until the sync
449
+ hands it over. The first attach reads the mask in; an allocate does
450
+ not, since whoever allocates writes every cell. */
451
+ static void
452
+ ca_objmask_open (CAObjectMask *ca, int refresh)
453
+ {
454
+ if ( ca->holds == 0 ) {
455
+ if ( refresh ) {
456
+ ca_objmask_refresh(ca);
457
+ }
458
+ ca->ptr = ca->cache;
459
+ }
460
+ ca->holds++;
461
+ }
462
+
463
+ static void
464
+ ca_objmask_func_allocate (void *ap)
465
+ {
466
+ ca_objmask_open((CAObjectMask *) ap, 0);
225
467
  }
226
468
 
227
469
  static void
228
470
  ca_objmask_func_attach (void *ap)
471
+ {
472
+ ca_objmask_open((CAObjectMask *) ap, 1);
473
+ }
474
+
475
+ static void
476
+ ca_objmask_func_sync (void *ap)
229
477
  {
230
478
  CAObjectMask *ca = (CAObjectMask *) ap;
231
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_copy_data"), Qtrue) ) {
232
- rb_funcall(ca->array, rb_intern("mask_copy_data"),
233
- 1, ca_objmask_mask_data(ca));
479
+ if ( ca->ptr ) {
480
+ ca_objmask_push(ca);
234
481
  }
235
482
  }
236
483
 
237
- void
238
- ca_objmask_func_sync (void *ap)
484
+ static void
485
+ ca_objmask_func_detach (void *ap)
239
486
  {
240
487
  CAObjectMask *ca = (CAObjectMask *) ap;
241
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_sync_data"), Qtrue) ) {
242
- rb_funcall(ca->array, rb_intern("mask_sync_data"),
243
- 1, ca_objmask_mask_data(ca));
488
+ if ( ca->holds > 0 && --ca->holds == 0 ) {
489
+ ca->ptr = NULL;
244
490
  }
245
491
  }
246
492
 
@@ -248,83 +494,76 @@ static void
248
494
  ca_objmask_func_xfer_all (void *ap, void *data, int dir)
249
495
  {
250
496
  CAObjectMask *ca = (CAObjectMask *) ap;
497
+ size_t len = (size_t) ca->elements * (size_t) ca->bytes;
251
498
  if ( dir == CA_XFER_GET ) {
252
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_copy_data"), Qtrue) ) {
253
- char *ptr0 = ca->ptr;
254
- ca->ptr = (char *) data;
255
- rb_funcall(ca->array, rb_intern("mask_copy_data"),
256
- 1, ca_objmask_mask_data(ca));
257
- ca->ptr = ptr0;
258
- }
259
- else {
260
- ca_array_func_xfer_all(ca, data, CA_XFER_GET);
499
+ if ( ! ca->ptr ) {
500
+ ca_objmask_refresh(ca);
261
501
  }
502
+ memcpy(data, ca->cache, len);
262
503
  }
263
504
  else {
264
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_copy_data"), Qtrue) ) {
265
- char *ptr0 = ca->ptr;
266
- ca->ptr = (char *) data;
267
- rb_funcall(ca->array, rb_intern("mask_sync_data"),
268
- 1, ca_objmask_mask_data(ca));
269
- ca->ptr = ptr0;
270
- }
271
- else {
272
- ca_array_func_xfer_all(ca, data, CA_XFER_PUT);
505
+ memcpy(ca->cache, data, len);
506
+ if ( ! ca->ptr ) {
507
+ ca_objmask_push(ca);
273
508
  }
274
509
  }
275
510
  }
276
511
 
277
- void
278
- ca_objmask_func_fill_data (void *ap, void *val)
512
+ static void
513
+ ca_objmask_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
514
+ void *data, int dir)
279
515
  {
280
516
  CAObjectMask *ca = (CAObjectMask *) ap;
281
- ca_array_func_fill_data(ca, val);
282
- if ( rb_obj_respond_to(ca->array, rb_intern("mask_fill_data"), Qtrue) ) {
283
- rb_funcall(ca->array, rb_intern("mask_fill_data"),
284
- 1, INT2NUM(*(uint8_t*)val));
517
+ boolean8_t *c = (boolean8_t *) ca->cache;
518
+ boolean8_t *d = (boolean8_t *) data;
519
+ ca_size_t i;
520
+ if ( ca->ptr ) {
521
+ for ( i = 0; i < n; i++ ) {
522
+ if ( dir == CA_XFER_GET ) { d[i] = c[addrs[i]]; }
523
+ else { c[addrs[i]] = d[i] ? 1 : 0; }
524
+ }
525
+ return;
526
+ }
527
+ if ( dir == CA_XFER_GET ) {
528
+ ca_objmask_get_cells(ca, n, addrs, d);
529
+ }
530
+ else {
531
+ ca_objmask_put_cells(ca, n, addrs, d);
285
532
  }
286
533
  }
287
534
 
288
535
  static void
289
536
  ca_objmask_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
290
537
  {
291
- ca_objmask_xfer_index_one((CAObjectMask *) ap, idx, data, dir);
538
+ ca_size_t addr = ca_index2addr((CArray *) ap, idx);
539
+ ca_objmask_func_xfer_addrs(ap, 1, &addr, data, dir);
292
540
  }
293
541
 
294
- /* Forward declaration: ca_object_wrap_transient (defined further down,
295
- near the data-side xfer functions). */
296
- static VALUE
297
- ca_object_wrap_transient (int8_t data_type, ca_size_t bytes,
298
- int8_t ndim, ca_size_t *dim, void *data, int dir);
299
-
300
- /* Mask parallel of the bulk-addrs path. When `mask_copy_addrs` /
301
- `mask_sync_addrs` is defined, 1-call bulk dispatch; else per-cell loop. */
302
542
  static void
303
- ca_objmask_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
304
- void *data, int dir)
543
+ ca_objmask_func_fill_data (void *ap, void *val)
305
544
  {
306
545
  CAObjectMask *ca = (CAObjectMask *) ap;
307
- ID mid = (dir == CA_XFER_GET) ? rb_intern("mask_copy_addrs")
308
- : rb_intern("mask_sync_addrs");
309
-
310
- if ( n > 0 && rb_obj_respond_to(ca->array, mid, Qtrue) ) {
311
- volatile VALUE raddrs, rdata;
312
- ca_size_t dim1[1] = { n };
313
- raddrs = ca_object_wrap_transient(CA_SIZE, sizeof(ca_size_t),
314
- 1, dim1, addrs, CA_XFER_PUT);
315
- rdata = ca_object_wrap_transient(ca->data_type, ca->bytes,
316
- 1, dim1, data, dir);
317
- rb_funcall(ca->array, mid, 2, raddrs, rdata);
546
+ boolean8_t bit = *(boolean8_t *) val ? 1 : 0;
547
+ memset(ca->cache, bit, (size_t) ca->elements);
548
+ if ( ca->ptr ) {
318
549
  return;
319
550
  }
320
-
321
- {
322
- char *d = (char *) data;
323
- ca_size_t i;
324
- for ( i = 0; i < n; i++ ) {
325
- ca_objmask_xfer_addr_one(ca, addrs[i], d + i * ca->bytes, dir);
326
- }
551
+ if ( ca_objmask_answers(ca, "mask_fill_data") ) {
552
+ rb_funcall(ca->array, rb_intern("mask_fill_data"), 1, INT2NUM(bit));
553
+ return;
327
554
  }
555
+ ca_objmask_push(ca);
556
+ }
557
+
558
+ static void
559
+ ca_objmask_func_fill_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *val)
560
+ {
561
+ CAObjectMask *ca = (CAObjectMask *) ap;
562
+ volatile VALUE hold = 0;
563
+ boolean8_t *bits = ALLOCV_N(boolean8_t, hold, n > 0 ? n : 1);
564
+ memset(bits, *(boolean8_t *) val ? 1 : 0, (size_t) n);
565
+ ca_objmask_func_xfer_addrs(ca, n, addrs, bits, CA_XFER_PUT);
566
+ ALLOCV_END(hold);
328
567
  }
329
568
 
330
569
  /* Mask parallel of the partial-region path. Same per-axis index-step
@@ -335,6 +574,7 @@ static void
335
574
  ca_objmask_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
336
575
  ca_size_t *strides, void *data, int dir)
337
576
  {
577
+ if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
338
578
  CAObjectMask *ca = (CAObjectMask *) ap;
339
579
  int8_t ndim = ca->ndim;
340
580
  ca_size_t native[CA_RANK_MAX], steps[CA_RANK_MAX], idx[CA_RANK_MAX];
@@ -351,7 +591,8 @@ ca_objmask_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
351
591
 
352
592
  if ( n == 0 ) return;
353
593
 
354
- if ( rb_obj_respond_to(ca->array, mid_block, Qtrue) ) {
594
+ if ( ! ca->ptr && ca_objmask_answers(ca, dir == CA_XFER_GET ? "mask_copy_block"
595
+ : "mask_sync_block") ) {
355
596
  /* The request is over the view's addresses, so a transposed / flat
356
597
  request is legal; the per-axis copy_block dispatch below would
357
598
  misread it. See ca_xfer_stride_request_is_axis_box (carray.h). */
@@ -372,7 +613,15 @@ ca_objmask_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
372
613
  }
373
614
  rdata = ca_object_wrap_transient(ca->data_type, ca->bytes,
374
615
  ndim, counts, data, dir);
375
- rb_funcall(ca->array, mid_block, 4, rstarts, rcounts, rsteps, rdata);
616
+ {
617
+ VALUE args[4];
618
+ args[0] = rstarts;
619
+ args[1] = rcounts;
620
+ args[2] = rsteps;
621
+ args[3] = rdata;
622
+ ca_object_call_transient(ca->array, rb_id2name(mid_block),
623
+ 4, args, 1, args + 3);
624
+ }
376
625
  return;
377
626
  }
378
627
  }
@@ -390,15 +639,27 @@ ca_objmask_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
390
639
  ALLOCV_END(holder);
391
640
  }
392
641
 
642
+ /* A mask copied by dup holds its own bits, and frees them. */
643
+ static void
644
+ ca_objmask_free (void *ap)
645
+ {
646
+ CAObjectMask *ca = (CAObjectMask *) ap;
647
+ if ( ca != NULL && NIL_P(ca->array) && ca->cache ) {
648
+ xfree(ca->cache);
649
+ ca->cache = NULL;
650
+ }
651
+ free_ca_wrap(ap);
652
+ }
653
+
393
654
  static ca_operation_function_t ca_objmask_func = {
394
655
  -1, /* CA_OBJ_OBJECT_MASK */
395
656
  CA_REAL_ARRAY,
396
- free_ca_wrap,
657
+ ca_objmask_free,
397
658
  ca_objmask_func_clone,
398
- ca_array_func_allocate,
659
+ ca_objmask_func_allocate,
399
660
  ca_objmask_func_attach,
400
661
  ca_objmask_func_sync,
401
- ca_array_func_detach,
662
+ ca_objmask_func_detach,
402
663
  ca_objmask_func_fill_data,
403
664
  ca_array_func_create_mask,
404
665
  ca_objmask_func_xfer_index,
@@ -406,6 +667,7 @@ static ca_operation_function_t ca_objmask_func = {
406
667
  NULL, /* fold_stride: never-fold (callback boundary) */
407
668
  ca_objmask_func_xfer_stride,
408
669
  ca_objmask_func_xfer_all,
670
+ .fill_addrs = ca_objmask_func_fill_addrs,
409
671
  };
410
672
 
411
673
  static VALUE
@@ -421,11 +683,18 @@ rb_ca_objmask_initialize_copy (VALUE self, VALUE other)
421
683
  CAObjectMask *ca, *cs;
422
684
 
423
685
  TypedData_Get_Struct(self, CAObjectMask, &caobjectmask_data_type, ca);
686
+ ca_check_uninitialized(ca);
424
687
  TypedData_Get_Struct(other, CAObjectMask, &caobjectmask_data_type, cs);
425
688
 
689
+ /* A copy of the mask as it reads now, with bits of its own: writing to
690
+ it does not reach the CAObject's hooks. */
426
691
  carray_setup((CArray *)ca, CA_BOOLEAN, cs->ndim, cs->dim, 0, NULL);
427
692
  ca->obj_type = CA_OBJ_OBJECT_MASK;
428
- ca->array = cs->array;
693
+ ca->array = Qnil;
694
+ ca->cache = ca->ptr;
695
+ ca->holds = 0;
696
+ ca_copy_data((CArray *) cs, ca->cache);
697
+ ca->ptr = NULL;
429
698
 
430
699
  return self;
431
700
  }
@@ -437,7 +706,6 @@ ca_object_setup (CAObject *ca,
437
706
  int8_t data_type, int8_t ndim, ca_size_t *dim, ca_size_t bytes)
438
707
  {
439
708
  ca_size_t elements;
440
- double length;
441
709
  int8_t i;
442
710
 
443
711
  /* check arguments */
@@ -449,16 +717,7 @@ ca_object_setup (CAObject *ca,
449
717
 
450
718
  /* calculate total number of elements */
451
719
 
452
- elements = 1;
453
- length = bytes;
454
- for (i=0; i<ndim; i++) {
455
- elements *= dim[i];
456
- length *= dim[i];
457
- }
458
-
459
- if ( length > CA_LENGTH_MAX ) {
460
- rb_raise(rb_eRuntimeError, "too large byte length");
461
- }
720
+ elements = ca_shape_elements(ndim, dim, bytes);
462
721
 
463
722
  ca->obj_type = CA_OBJ_OBJECT;
464
723
  ca->data_type = data_type;
@@ -519,6 +778,17 @@ ca_object_func_clone (void *ap)
519
778
  #define ca_object_func_ptr_at_addr ca_array_func_ptr_at_addr
520
779
  #define ca_object_func_ptr_at_index ca_array_func_ptr_at_index
521
780
 
781
+ /* The bytes of this array's mask. A fetcher that answers UNDEF marks the
782
+ cell there, whether or not the mask is open. */
783
+ static boolean8_t *
784
+ ca_object_mask_bytes (CAObject *ca)
785
+ {
786
+ if ( ca->mask->obj_type == CA_OBJ_OBJECT_MASK ) {
787
+ return (boolean8_t *) ((CAObjectMask *) ca->mask)->cache;
788
+ }
789
+ return (boolean8_t *) ca->mask->ptr;
790
+ }
791
+
522
792
  /* per-cell cores: the addr- and index-primary Ruby-callback bridge logic,
523
793
  direction-unified. The fetch_* / store_* slots below are thin
524
794
  forwarders onto these. */
@@ -532,18 +802,15 @@ ca_object_xfer_addr_one (CAObject *ca, ca_size_t addr, void *ptr, int dir)
532
802
  raddr = SIZE2NUM(addr);
533
803
  rval = rb_funcall(ca->self, rb_intern("fetch_addr"), 1, raddr);
534
804
  if ( rval == CA_UNDEF ) {
535
- ca_update_mask(ca);
536
- if ( ! ca->mask ) {
537
- ca_create_mask(ca);
538
- }
539
- *((boolean8_t*)ca->mask->ptr + addr) = 1;
805
+ ca_create_mask(ca);
806
+ ca_object_mask_bytes(ca)[addr] = 1;
540
807
  if ( ca->data_type == CA_OBJECT ) {
541
808
  rb_ca_obj2ptr(ca->self, INT2NUM(0), ptr);
542
809
  }
543
810
  }
544
811
  else {
545
812
  if ( ca_has_mask(ca) ) {
546
- *((boolean8_t*)ca->mask->ptr + addr) = 0;
813
+ ca_object_mask_bytes(ca)[addr] = 0;
547
814
  }
548
815
  rb_ca_obj2ptr(ca->self, rval, ptr);
549
816
  }
@@ -557,18 +824,15 @@ ca_object_xfer_addr_one (CAObject *ca, ca_size_t addr, void *ptr, int dir)
557
824
  }
558
825
  rval = rb_funcall(ca->self, rb_intern("fetch_index"), 1, ridx);
559
826
  if ( rval == CA_UNDEF ) {
560
- ca_update_mask(ca);
561
- if ( ! ca->mask ) {
562
- ca_create_mask(ca);
563
- }
564
- *((boolean8_t*)ca->mask->ptr + ca_index2addr(ca->mask, idx)) = 1;
827
+ ca_create_mask(ca);
828
+ ca_object_mask_bytes(ca)[ca_index2addr(ca->mask, idx)] = 1;
565
829
  if ( ca->data_type == CA_OBJECT ) {
566
830
  rb_ca_obj2ptr(ca->self, INT2NUM(0), ptr);
567
831
  }
568
832
  }
569
833
  else {
570
834
  if ( ca_has_mask(ca) ) {
571
- *((boolean8_t*)ca->mask->ptr + ca_index2addr(ca->mask, idx)) = 0;
835
+ ca_object_mask_bytes(ca)[ca_index2addr(ca->mask, idx)] = 0;
572
836
  }
573
837
  rb_ca_obj2ptr(ca->self, rval, ptr);
574
838
  }
@@ -606,18 +870,15 @@ ca_object_xfer_index_one (CAObject *ca, ca_size_t *idx, void *ptr, int dir)
606
870
  }
607
871
  rval = rb_funcall(ca->self, rb_intern("fetch_index"), 1, ridx);
608
872
  if ( rval == CA_UNDEF ) {
609
- ca_update_mask(ca);
610
- if ( ! ca->mask ) {
611
- ca_create_mask(ca);
612
- }
613
- *((boolean8_t*)ca->mask->ptr + ca_index2addr(ca->mask, idx)) = 1;
873
+ ca_create_mask(ca);
874
+ ca_object_mask_bytes(ca)[ca_index2addr(ca->mask, idx)] = 1;
614
875
  if ( ca->data_type == CA_OBJECT ) {
615
876
  rb_ca_obj2ptr(ca->self, INT2NUM(0), ptr);
616
877
  }
617
878
  }
618
879
  else {
619
880
  if ( ca_has_mask(ca) ) {
620
- *((boolean8_t*)ca->mask->ptr + ca_index2addr(ca->mask, idx)) = 0;
881
+ ca_object_mask_bytes(ca)[ca_index2addr(ca->mask, idx)] = 0;
621
882
  }
622
883
  rb_ca_obj2ptr(ca->self, rval, ptr);
623
884
  }
@@ -627,18 +888,15 @@ ca_object_xfer_index_one (CAObject *ca, ca_size_t *idx, void *ptr, int dir)
627
888
  raddr = SIZE2NUM(addr);
628
889
  rval = rb_funcall(ca->self, rb_intern("fetch_addr"), 1, raddr);
629
890
  if ( rval == CA_UNDEF ) {
630
- ca_update_mask(ca);
631
- if ( ! ca->mask ) {
632
- ca_create_mask(ca);
633
- }
634
- *((boolean8_t*)ca->mask->ptr + addr) = 1;
891
+ ca_create_mask(ca);
892
+ ca_object_mask_bytes(ca)[addr] = 1;
635
893
  if ( ca->data_type == CA_OBJECT ) {
636
894
  rb_ca_obj2ptr(ca->self, INT2NUM(0), ptr);
637
895
  }
638
896
  }
639
897
  else {
640
898
  if ( ca_has_mask(ca) ) {
641
- *((boolean8_t*)ca->mask->ptr + addr) = 0;
899
+ ca_object_mask_bytes(ca)[addr] = 0;
642
900
  }
643
901
  rb_ca_obj2ptr(ca->self, rval, ptr);
644
902
  }
@@ -697,11 +955,8 @@ static void
697
955
  ca_object_dispatch_copy (CAObject *ca, void *ptr)
698
956
  {
699
957
  if ( rb_obj_respond_to(ca->self, rb_intern("copy_data"), Qtrue) ) {
700
- char *ptr0 = ca->data->ptr;
701
- ca->data->ptr = ptr;
702
- rb_funcall(ca->self, rb_intern("copy_data"),
703
- 1, rb_ivar_get(ca->self, rb_intern("__data__")));
704
- ca->data->ptr = ptr0;
958
+ ca_object_call_lending(&ca->data->ptr, ptr, ca->self, "copy_data",
959
+ ca_object_data_of, ca);
705
960
  }
706
961
  else {
707
962
  ca_size_t addr;
@@ -716,11 +971,8 @@ static void
716
971
  ca_object_dispatch_sync (CAObject *ca, void *ptr)
717
972
  {
718
973
  if ( rb_obj_respond_to(ca->self, rb_intern("sync_data"), Qtrue) ) {
719
- char *ptr0 = ca->data->ptr;
720
- ca->data->ptr = ptr;
721
- rb_funcall(ca->self, rb_intern("sync_data"),
722
- 1, rb_ivar_get(ca->self, rb_intern("__data__")));
723
- ca->data->ptr = ptr0;
974
+ ca_object_call_lending(&ca->data->ptr, ptr, ca->self, "sync_data",
975
+ ca_object_data_of, ca);
724
976
  }
725
977
  else {
726
978
  ca_size_t addr;
@@ -738,6 +990,14 @@ ca_object_dispatch_fill (CAObject *ca, void *ptr)
738
990
  volatile VALUE rval = rb_ca_ptr2obj(ca->self, ptr);
739
991
  rb_funcall(ca->self, rb_intern("fill_data"), 1, rval);
740
992
  }
993
+ else if ( rb_obj_respond_to(ca->self, rb_intern("fill_block"), Qtrue)
994
+ || rb_obj_respond_to(ca->self, rb_intern("fill_addrs"), Qtrue) ) {
995
+ /* An author who wrote the region slots but not fill_data would otherwise
996
+ get the per-cell default for the one request that is easiest to batch.
997
+ Hand the whole extent to the region path, which reaches `fill_block` in
998
+ one call or `fill_addrs` in address windows. */
999
+ ca_fill_stride_whole(ca, ptr);
1000
+ }
741
1001
  else {
742
1002
  ca_size_t addr;
743
1003
  for ( addr = 0; addr < ca->elements; addr++ ) {
@@ -770,10 +1030,7 @@ ca_object_func_attach (void *ap)
770
1030
  ca->ptr = ca->data->ptr;
771
1031
  ca_object_dispatch_copy(ca, ca->ptr);
772
1032
  if ( ca_has_mask(ca->data) ) {
773
- ca_update_mask(ca);
774
- if ( ! ca->mask ) {
775
- ca_create_mask(ca);
776
- }
1033
+ ca_create_mask(ca);
777
1034
  }
778
1035
  }
779
1036
 
@@ -814,7 +1071,8 @@ ca_object_func_detach (void *ap)
814
1071
  [n] for addrs) before invoking copy_block / sync_block / copy_addrs /
815
1072
  sync_addrs. Lifecycle: buffer is owned by the slot caller; the wrapper
816
1073
  installs free_ca_wrap as dfree, so the underlying ptr is NOT freed when
817
- the wrapper is GC'd.
1074
+ the wrapper is GC'd. Call the hook through ca_object_call_transient,
1075
+ which takes the buffer back from the wrapper when the hook returns.
818
1076
 
819
1077
  Object data_type GC safety: for GET (dir == CA_XFER_GET) the buffer may be
820
1078
  uninitialized garbage on entry, so we INT2NUM(0) zero-fill before
@@ -872,7 +1130,12 @@ ca_object_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
872
1130
  1, dim1, addrs, CA_XFER_PUT);
873
1131
  rdata = ca_object_wrap_transient(ca->data_type, ca->bytes,
874
1132
  1, dim1, data, dir);
875
- rb_funcall(ca->self, mid, 2, raddrs, rdata);
1133
+ {
1134
+ VALUE args[2];
1135
+ args[0] = raddrs;
1136
+ args[1] = rdata;
1137
+ ca_object_call_transient(ca->self, rb_id2name(mid), 2, args, 2, args);
1138
+ }
876
1139
  return;
877
1140
  }
878
1141
 
@@ -909,6 +1172,7 @@ static void
909
1172
  ca_object_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
910
1173
  ca_size_t *strides, void *data, int dir)
911
1174
  {
1175
+ if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
912
1176
  CAObject *ca = (CAObject *) ap;
913
1177
  int8_t ndim;
914
1178
  ca_size_t native[CA_RANK_MAX], steps[CA_RANK_MAX], idx[CA_RANK_MAX];
@@ -961,7 +1225,15 @@ ca_object_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
961
1225
  }
962
1226
  rdata = ca_object_wrap_transient(ca->data_type, ca->bytes,
963
1227
  ndim, counts, data, dir);
964
- rb_funcall(ca->self, mid_block, 4, rstarts, rcounts, rsteps, rdata);
1228
+ {
1229
+ VALUE args[4];
1230
+ args[0] = rstarts;
1231
+ args[1] = rcounts;
1232
+ args[2] = rsteps;
1233
+ args[3] = rdata;
1234
+ ca_object_call_transient(ca->self, rb_id2name(mid_block),
1235
+ 4, args, 1, args + 3);
1236
+ }
965
1237
  return;
966
1238
  }
967
1239
  }
@@ -1032,7 +1304,12 @@ ca_object_func_fill_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *ptr)
1032
1304
  raddrs = ca_object_wrap_transient(CA_SIZE, sizeof(ca_size_t),
1033
1305
  1, dim1, addrs, CA_XFER_PUT);
1034
1306
  rval = rb_ca_ptr2obj(ca->self, ptr);
1035
- rb_funcall(ca->self, mid, 2, raddrs, rval);
1307
+ {
1308
+ VALUE args[2];
1309
+ args[0] = raddrs;
1310
+ args[1] = rval;
1311
+ ca_object_call_transient(ca->self, rb_id2name(mid), 2, args, 1, args);
1312
+ }
1036
1313
  return;
1037
1314
  }
1038
1315
 
@@ -1100,31 +1377,32 @@ ca_object_func_create_mask (void *ap)
1100
1377
  CAObject *ca = (CAObject *) ap;
1101
1378
  volatile VALUE rmask;
1102
1379
  if ( ca_is_face(ca) ) {
1103
- /* Face: the mask refers to the parent's mask (same as the C-level Face
1104
- create_mask, e.g. ca_time_func_create_mask). Storage is the
1105
- parent, so the mask must be the parent's, not the internal __data__. */
1106
- ca_update_mask(ca->parent);
1107
- if ( ! ca->parent->mask ) {
1108
- ca_create_mask(ca->parent);
1109
- }
1110
- ca->mask = (CArray *) ca_refer_new(ca->parent->mask,
1111
- CA_BOOLEAN, ca->ndim, ca->dim, 0, 0);
1380
+ /* Face: storage is the parent, so the mask is the parent's (as for the
1381
+ C-level Faces), not the internal __data__'s. */
1382
+ ca_view_func_create_mask(ca);
1112
1383
  return;
1113
1384
  }
1114
1385
  if ( rb_obj_respond_to(ca->self, rb_intern("create_mask"), Qtrue) ) {
1115
1386
  rb_funcall(ca->self, rb_intern("create_mask"), 0);
1116
1387
  }
1117
1388
  else {
1118
- rb_raise(rb_eRuntimeError, "can't create mask for CAObject");
1389
+ rb_raise(rb_eTypeError, "can't create mask for CAObject");
1119
1390
  }
1120
- ca_update_mask(ca->data);
1121
- if ( ! ca->data->mask ) {
1122
- ca_create_mask(ca->data);
1391
+ ca_create_mask(ca->data);
1392
+ {
1393
+ CAObjectMask *cm = ca_objmask_new(ca->self, ca->ndim, ca->dim);
1394
+ cm->cache = ca->data->mask->ptr;
1395
+ ca->mask = (CArray *) cm;
1396
+ rmask = ca_wrap_struct(ca->mask);
1397
+ rb_ivar_set(ca->self, rb_intern("mask"), rmask);
1398
+ /* Made while this array is attached, the mask joins it at its level:
1399
+ ca_create_mask attaches it once more when this returns. */
1400
+ if ( ca->attach > 1 ) {
1401
+ ca_objmask_refresh(cm);
1402
+ cm->ptr = cm->cache;
1403
+ cm->holds = ca->attach - 1;
1404
+ }
1123
1405
  }
1124
- ca->mask = (CArray*) ca_objmask_new(ca->self, ca->ndim, ca->dim);
1125
- ca->mask->ptr = ca->data->mask->ptr;
1126
- rmask = ca_wrap_struct(ca->mask);
1127
- rb_ivar_set(ca->self, rb_intern("mask"), rmask);
1128
1406
  }
1129
1407
 
1130
1408
  ca_operation_function_t ca_object_func = {
@@ -1163,6 +1441,7 @@ rb_ca_object_initialize_copy (VALUE self, VALUE other)
1163
1441
  CAObject *ca, *cs;
1164
1442
 
1165
1443
  TypedData_Get_Struct(self, CAObject, &caobject_data_type, ca);
1444
+ ca_check_uninitialized(ca);
1166
1445
  TypedData_Get_Struct(other, CAObject, &caobject_data_type, cs);
1167
1446
 
1168
1447
  if ( ca_func[CA_OBJ_OBJECT].pool_init ) {
@@ -1204,20 +1483,21 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
1204
1483
  {
1205
1484
  volatile VALUE rtype, rdim, ropt, rbytes = Qnil, rrdonly = Qnil,
1206
1485
  rparent = Qnil, rface = Qnil, rstorage = Qnil, rdata,
1207
- rorderable = Qnil, rcomparable = Qnil;
1486
+ rorderable = Qnil, rcomparable = Qnil, rnumeric = Qnil;
1208
1487
  CAObject *ca;
1209
1488
  int8_t data_type, ndim;
1210
1489
  ca_size_t dim[CA_RANK_MAX];
1211
1490
  ca_size_t bytes;
1212
1491
  int i;
1492
+ int storage_named = 0; /* :storage given by the caller */
1213
1493
 
1214
1494
  rb_scan_args(argc, argv, "21", (VALUE *) &rtype, (VALUE *) &rdim, (VALUE *) &ropt);
1215
- rb_scan_options(ropt, "bytes,read_only,parent,face,storage,orderable_storage,comparable_storage",
1495
+ rb_scan_options(ropt, "bytes,read_only,parent,face,storage,orderable_storage,comparable_storage,numeric",
1216
1496
  &rbytes, &rrdonly, &rparent, &rface, &rstorage,
1217
- &rorderable, &rcomparable);
1497
+ &rorderable, &rcomparable, &rnumeric);
1218
1498
 
1219
1499
  if ( ( ! NIL_P(rparent) ) && ( ! rb_obj_is_carray(rparent) ) ) {
1220
- rb_raise(rb_eRuntimeError, "option :parent should be a carray");
1500
+ rb_raise(rb_eTypeError, "option :parent should be a carray");
1221
1501
  }
1222
1502
 
1223
1503
  /* :storage is meaningful only under face: true (= it documents the
@@ -1238,6 +1518,16 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
1238
1518
  "meaningful with face: true");
1239
1519
  }
1240
1520
 
1521
+ if ( ! NIL_P(rnumeric) && ! RTEST(rface) ) {
1522
+ rb_raise(rb_eArgError,
1523
+ "CAObject: :numeric is only meaningful with face: true");
1524
+ }
1525
+ if ( ! NIL_P(rnumeric) && ! NIL_P(rstorage) ) {
1526
+ rb_raise(rb_eArgError,
1527
+ "CAObject: give either :numeric or :storage, not both "
1528
+ "(:storage declares a surface of its own)");
1529
+ }
1530
+
1241
1531
  /* face: true requires :parent. Under Face semantics the parent is the
1242
1532
  true storage owner and CAObject itself carries the semantic identity.
1243
1533
  The declared data_type is the SURFACE (= what dispatch sees);
@@ -1254,7 +1544,30 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
1254
1544
 
1255
1545
  rb_ca_guess_type_and_bytes(rtype, rbytes, &data_type, &bytes);
1256
1546
 
1547
+ /* Without :storage the data_type argument names the storage of a Face.
1548
+ A numeric storage is gated by default: the surface becomes CA_FIXLEN,
1549
+ so arithmetic, math and reductions raise instead of computing on the
1550
+ stored numbers, which the Face exists to give another meaning.
1551
+ numeric: true lets them through. An object or fixlen storage keeps
1552
+ its own data_type: fixlen kernels already raise, and an object cell
1553
+ may not be read as bytes. */
1554
+ storage_named = ! NIL_P(rstorage);
1555
+ if ( RTEST(rface) && NIL_P(rstorage) ) {
1556
+ if ( data_type == CA_OBJECT || data_type == CA_FIXLEN ) {
1557
+ if ( ! NIL_P(rnumeric) ) {
1558
+ rb_raise(rb_eArgError,
1559
+ "CAObject Face: :numeric applies to a numeric storage only "
1560
+ "(the storage is %s)", ca_type_name[data_type]);
1561
+ }
1562
+ }
1563
+ else if ( ! RTEST(rnumeric) ) {
1564
+ rstorage = INT2NUM(data_type);
1565
+ data_type = CA_FIXLEN;
1566
+ }
1567
+ }
1568
+
1257
1569
  Check_Type(rdim, T_ARRAY);
1570
+ CA_CHECK_RANK(RARRAY_LEN(rdim)); /* before dim[] is filled */
1258
1571
 
1259
1572
  ndim = RARRAY_LEN(rdim);
1260
1573
  for (i=0; i<ndim; i++) {
@@ -1262,6 +1575,7 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
1262
1575
  }
1263
1576
 
1264
1577
  TypedData_Get_Struct(self, CAObject, &caobject_data_type, ca);
1578
+ ca_check_uninitialized(ca);
1265
1579
  ca_object_setup(ca, data_type, ndim, dim, bytes);
1266
1580
  ca->self = self;
1267
1581
 
@@ -1279,17 +1593,11 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
1279
1593
  ca->parent = cp;
1280
1594
  rb_ca_set_parent(self, rparent);
1281
1595
 
1282
- /* Face mode invariants. The check the author opts into depends on
1283
- whether :storage is explicit:
1284
- - :storage omitted → "surface == storage" contract:
1285
- surface (= declared data_type) must equal
1286
- parent.data_type. This catches typos for
1287
- Numeric Face authors who didn't intend
1288
- surface/storage to differ.
1289
- - :storage given → "surface != storage" opted-in:
1290
- surface is free (= e.g. CA_FIXLEN for
1291
- NonNumeric Face), :storage must agree with
1292
- parent.data_type, and bytes must match. */
1596
+ /* Face mode invariants: bytes match the parent's, and the storage
1597
+ (:storage, or the data_type argument when :storage was omitted)
1598
+ is the parent's data_type. The surface is the data_type argument
1599
+ under :storage, CA_FIXLEN for a gated numeric storage (decided
1600
+ above), and the storage itself otherwise. */
1293
1601
  if ( RTEST(rface) ) {
1294
1602
  if ( bytes != cp->bytes ) {
1295
1603
  rb_raise(rb_eTypeError,
@@ -1298,12 +1606,12 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
1298
1606
  (long long) bytes, (long long) cp->bytes);
1299
1607
  }
1300
1608
  if ( NIL_P(rstorage) ) {
1301
- /* surface == storage contract */
1609
+ /* The data_type argument names the storage. */
1302
1610
  if ( cp->data_type != data_type ) {
1303
1611
  rb_raise(rb_eTypeError,
1304
1612
  "CAObject Face: parent data_type mismatch "
1305
1613
  "(declared=%d, parent=%d). Use :storage option to "
1306
- "opt into surface != storage (NonNumeric Face).",
1614
+ "declare a surface that differs from the storage.",
1307
1615
  (int) data_type, (int) cp->data_type);
1308
1616
  }
1309
1617
  }
@@ -1315,10 +1623,20 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
1315
1623
  &declared_storage, &declared_storage_bytes);
1316
1624
  if ( cp->data_type != declared_storage ) {
1317
1625
  rb_raise(rb_eTypeError,
1318
- "CAObject Face: :storage mismatch "
1319
- "(declared=%d, parent.data_type=%d)",
1626
+ storage_named
1627
+ ? "CAObject Face: :storage mismatch "
1628
+ "(declared=%d, parent.data_type=%d)"
1629
+ : "CAObject Face: parent data_type mismatch "
1630
+ "(declared=%d, parent=%d)",
1320
1631
  (int) declared_storage, (int) cp->data_type);
1321
1632
  }
1633
+ /* An object cell is a VALUE: reading it under another surface hands
1634
+ out its address, and writing one forges it. */
1635
+ if ( ca_is_object_type(cp) && data_type != CA_OBJECT ) {
1636
+ rb_raise(rb_eTypeError,
1637
+ "CAObject Face: an object storage can't be given "
1638
+ "another surface data_type");
1639
+ }
1322
1640
  }
1323
1641
  ca_set_flag(ca, CA_FLAG_IS_FACE);
1324
1642
  if ( RTEST(rorderable) ) {