carray 3.0.2 → 3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +304 -0
  3. data/README.md +3 -4
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +225 -0
  6. data/ext/ca_axis_group.c +177 -99
  7. data/ext/ca_binop_dispatch.c +54 -32
  8. data/ext/ca_binop_dispatch.h +1 -0
  9. data/ext/ca_categorical_iterator.c +66 -71
  10. data/ext/ca_compare.c +8 -2
  11. data/ext/ca_for_buffer.h +21 -68
  12. data/ext/ca_group_iter.c +27 -11
  13. data/ext/ca_kernel_iterator.c +663 -214
  14. data/ext/ca_kernel_iterator.h +231 -167
  15. data/ext/ca_monop_dispatch.c +4 -4
  16. data/ext/ca_monop_dispatch.h +4 -0
  17. data/ext/ca_obj_array.c +132 -22
  18. data/ext/ca_obj_bincmp.c +152 -121
  19. data/ext/ca_obj_binop.c +107 -210
  20. data/ext/ca_obj_bitarray.c +37 -24
  21. data/ext/ca_obj_bitfield.c +95 -62
  22. data/ext/ca_obj_block.c +1 -0
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +96 -25
  25. data/ext/ca_obj_face.c +406 -64
  26. data/ext/ca_obj_face.h +92 -47
  27. data/ext/ca_obj_fake.c +21 -50
  28. data/ext/ca_obj_farray.c +1 -0
  29. data/ext/ca_obj_field.c +16 -3
  30. data/ext/ca_obj_fixlen_string.c +21 -21
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +189 -107
  33. data/ext/ca_obj_moncmp.c +61 -61
  34. data/ext/ca_obj_monop.c +125 -107
  35. data/ext/ca_obj_object.c +543 -233
  36. data/ext/ca_obj_record.c +51 -16
  37. data/ext/ca_obj_reduce.c +5 -8
  38. data/ext/ca_obj_refer.c +117 -33
  39. data/ext/ca_obj_remap.c +57 -17
  40. data/ext/ca_obj_repeat.c +21 -5
  41. data/ext/ca_obj_roll.c +115 -11
  42. data/ext/ca_obj_select.c +47 -49
  43. data/ext/ca_obj_select_axis.c +58 -60
  44. data/ext/ca_obj_shift.c +22 -102
  45. data/ext/ca_obj_stack.c +223 -118
  46. data/ext/ca_obj_stride.c +195 -34
  47. data/ext/ca_obj_string.c +17 -18
  48. data/ext/ca_obj_tile.c +32 -16
  49. data/ext/ca_obj_time.c +23 -25
  50. data/ext/ca_obj_timedelta.c +21 -23
  51. data/ext/ca_obj_transpose.c +12 -3
  52. data/ext/ca_obj_triop.c +110 -117
  53. data/ext/ca_obj_window.c +149 -27
  54. data/ext/ca_op_ipower.c +24 -11
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_sort_kernels.h +70 -0
  58. data/ext/ca_sweep_engine.c +102 -79
  59. data/ext/ca_sweep_engine.h +7 -20
  60. data/ext/ca_triop_dispatch.c +4 -1
  61. data/ext/ca_triop_dispatch.h +5 -2
  62. data/ext/caframe_csv_reader.c +463 -0
  63. data/ext/carray.h +70 -27
  64. data/ext/carray_access.c +455 -211
  65. data/ext/carray_address_basis.c +177 -36
  66. data/ext/carray_attribute.c +45 -3
  67. data/ext/carray_bincount.c +32 -2
  68. data/ext/carray_broadcast.c +46 -14
  69. data/ext/carray_call_cfunc.c +0 -28
  70. data/ext/carray_cast.c +1444 -209
  71. data/ext/carray_cast_func.rb +34 -21
  72. data/ext/carray_conversion.c +197 -73
  73. data/ext/carray_copy.c +32 -11
  74. data/ext/carray_core.c +514 -122
  75. data/ext/carray_count.c +140 -87
  76. data/ext/carray_element.c +18 -27
  77. data/ext/carray_factorize.c +569 -277
  78. data/ext/carray_generate.c +76 -40
  79. data/ext/carray_histogram.c +152 -94
  80. data/ext/carray_hold.c +2 -1
  81. data/ext/carray_index_classifier.c +63 -96
  82. data/ext/carray_internal.h +385 -3
  83. data/ext/carray_lazy.c +834 -79
  84. data/ext/carray_loop.c +81 -65
  85. data/ext/carray_mask.c +682 -316
  86. data/ext/carray_math_kernel.h +1 -0
  87. data/ext/carray_median_percentile.c +221 -76
  88. data/ext/carray_memory_view.c +254 -248
  89. data/ext/carray_operator.c +1089 -359
  90. data/ext/carray_order.c +133 -69
  91. data/ext/carray_partition.c +102 -56
  92. data/ext/carray_random.c +273 -239
  93. data/ext/carray_scatter.c +308 -65
  94. data/ext/carray_segment.c +257 -0
  95. data/ext/carray_slab.c +170 -70
  96. data/ext/carray_sort.c +215 -218
  97. data/ext/carray_sort_kernel.c +8 -1
  98. data/ext/carray_struct.c +99 -21
  99. data/ext/carray_test.c +167 -64
  100. data/ext/carray_time_text.c +1182 -0
  101. data/ext/carray_utils.c +363 -98
  102. data/ext/extconf.rb +27 -2
  103. data/ext/mk_call_cfunc.rb +0 -2
  104. data/ext/mk_pow5_table.rb +35 -0
  105. data/ext/mkkernel.rb +1076 -846
  106. data/ext/ruby_carray.c +7 -1
  107. data/ext/version.h +4 -4
  108. data/ext/version.rb +4 -2
  109. data/lib/carray/attribute.rb +39 -0
  110. data/lib/carray/autoload_carray.rb +19 -0
  111. data/lib/carray/autoload_method_extension.rb +6 -0
  112. data/lib/carray/axis_group.rb +174 -26
  113. data/lib/carray/basics.rb +91 -25
  114. data/lib/carray/bincount_nd.rb +6 -0
  115. data/lib/carray/block_iterator.rb +118 -48
  116. data/lib/carray/boolean_reduce.rb +21 -29
  117. data/lib/carray/categorical.rb +94 -26
  118. data/lib/carray/categorical_iterator.rb +333 -826
  119. data/lib/carray/complex.rb +36 -29
  120. data/lib/carray/conditional.rb +94 -7
  121. data/lib/carray/const_string.rb +46 -0
  122. data/lib/carray/frame/concat.rb +157 -6
  123. data/lib/carray/frame/convert.rb +13 -7
  124. data/lib/carray/frame/csv_parser.rb +589 -82
  125. data/lib/carray/frame/describe.rb +79 -0
  126. data/lib/carray/frame/frame.rb +124 -25
  127. data/lib/carray/frame/group.rb +189 -63
  128. data/lib/carray/frame/io.rb +412 -72
  129. data/lib/carray/frame/join.rb +134 -46
  130. data/lib/carray/frame/records.rb +37 -16
  131. data/lib/carray/frame/reshape.rb +448 -0
  132. data/lib/carray/frame/sort.rb +26 -2
  133. data/lib/carray/frame/verbs.rb +715 -48
  134. data/lib/carray/frame.rb +2 -0
  135. data/lib/carray/fuse_source.rb +111 -2
  136. data/lib/carray/fusion.rb +229 -38
  137. data/lib/carray/inspect.rb +29 -11
  138. data/lib/carray/iterator.rb +14 -2
  139. data/lib/carray/lazy.rb +54 -30
  140. data/lib/carray/mask_gap_fill.rb +66 -52
  141. data/lib/carray/math.rb +32 -19
  142. data/lib/carray/meld_reduce.rb +40 -7
  143. data/lib/carray/methods/align_addr.rb +8 -4
  144. data/lib/carray/methods/bincount.rb +2 -2
  145. data/lib/carray/methods/bit_string.rb +14 -3
  146. data/lib/carray/methods/composition.rb +16 -10
  147. data/lib/carray/methods/factorize.rb +2 -2
  148. data/lib/carray/methods/gather_nd.rb +13 -2
  149. data/lib/carray/methods/insert_block.rb +9 -14
  150. data/lib/carray/methods/is_in.rb +117 -4
  151. data/lib/carray/methods/join.rb +1 -1
  152. data/lib/carray/methods/locate_addr.rb +177 -36
  153. data/lib/carray/methods/mode.rb +18 -1
  154. data/lib/carray/methods/repeat.rb +1 -20
  155. data/lib/carray/methods/resize.rb +21 -23
  156. data/lib/carray/methods/snap.rb +34 -12
  157. data/lib/carray/methods/unique.rb +11 -2
  158. data/lib/carray/record_order.rb +208 -0
  159. data/lib/carray/runtime.rb +14 -11
  160. data/lib/carray/segment_iterator.rb +990 -0
  161. data/lib/carray/serialize.rb +197 -27
  162. data/lib/carray/slab_iterator.rb +51 -27
  163. data/lib/carray/stack.rb +10 -4
  164. data/lib/carray/string_operation_extension.rb +4 -3
  165. data/lib/carray/struct.rb +142 -51
  166. data/lib/carray/struct_builder.rb +110 -14
  167. data/lib/carray/table.rb +2 -2
  168. data/lib/carray/time.rb +517 -103
  169. data/lib/carray/window_iterator.rb +127 -72
  170. data/lib/carray.rb +2 -2
  171. data/yard-stubs/ca_obj_array.rb +4 -4
  172. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  173. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  174. data/yard-stubs/ca_obj_fake.rb +1 -1
  175. data/yard-stubs/ca_obj_field.rb +1 -1
  176. data/yard-stubs/ca_obj_refer.rb +3 -3
  177. data/yard-stubs/ca_obj_roll.rb +0 -1
  178. data/yard-stubs/ca_obj_stride.rb +10 -7
  179. data/yard-stubs/ca_obj_transpose.rb +1 -1
  180. data/yard-stubs/ca_obj_window.rb +1 -2
  181. data/yard-stubs/carray_access.rb +5 -4
  182. data/yard-stubs/carray_broadcast.rb +1 -1
  183. data/yard-stubs/carray_cast.rb +78 -43
  184. data/yard-stubs/carray_conversion.rb +13 -1
  185. data/yard-stubs/carray_copy.rb +8 -3
  186. data/yard-stubs/carray_core.rb +3 -3
  187. data/yard-stubs/carray_count.rb +9 -0
  188. data/yard-stubs/carray_lazy.rb +5 -1
  189. data/yard-stubs/carray_loop.rb +4 -1
  190. data/yard-stubs/carray_mask.rb +34 -10
  191. data/yard-stubs/carray_math.rb +25 -13
  192. data/yard-stubs/carray_median_percentile.rb +10 -6
  193. data/yard-stubs/carray_order.rb +34 -13
  194. data/yard-stubs/carray_scatter.rb +10 -3
  195. data/yard-stubs/carray_segment.rb +72 -0
  196. data/yard-stubs/carray_slab.rb +2 -1
  197. data/yard-stubs/carray_sort.rb +43 -3
  198. data/yard-stubs/carray_test.rb +10 -0
  199. data/yard-stubs/carray_utils.rb +0 -26
  200. metadata +11 -19
  201. data/ext/ca_for_each_element.h +0 -239
  202. data/ext/carray_build_flags.h +0 -3
  203. data/ext/carray_config.h +0 -28
  204. data/ext/carray_kernels_bincmp.c +0 -4446
  205. data/ext/carray_kernels_binop.c +0 -11001
  206. data/ext/carray_kernels_init.c +0 -1131
  207. data/ext/carray_kernels_map.c +0 -3467
  208. data/ext/carray_kernels_moncmp.c +0 -2097
  209. data/ext/carray_kernels_monop.c +0 -18313
  210. data/ext/carray_kernels_reduce_aggregate.c +0 -26005
  211. data/ext/carray_kernels_reduce_boolean.c +0 -330
  212. data/ext/carray_kernels_reduce_cumulative.c +0 -14862
  213. data/ext/carray_kernels_reduce_extreme.c +0 -17494
  214. data/ext/carray_kernels_reduce_variance.c +0 -3910
  215. data/ext/carray_kernels_scan.c +0 -3693
  216. data/ext/carray_kernels_search.c +0 -32218
  217. data/ext/carray_kernels_sort.c +0 -10626
  218. data/ext/carray_kernels_triop.c +0 -1392
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);
224
441
  }
442
+ for ( i = 0; i < n; i++ ) {
443
+ c[addrs[i]] = bits[i] ? 1 : 0;
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;
@@ -778,10 +1030,7 @@ ca_object_func_attach (void *ap)
778
1030
  ca->ptr = ca->data->ptr;
779
1031
  ca_object_dispatch_copy(ca, ca->ptr);
780
1032
  if ( ca_has_mask(ca->data) ) {
781
- ca_update_mask(ca);
782
- if ( ! ca->mask ) {
783
- ca_create_mask(ca);
784
- }
1033
+ ca_create_mask(ca);
785
1034
  }
786
1035
  }
787
1036
 
@@ -822,7 +1071,8 @@ ca_object_func_detach (void *ap)
822
1071
  [n] for addrs) before invoking copy_block / sync_block / copy_addrs /
823
1072
  sync_addrs. Lifecycle: buffer is owned by the slot caller; the wrapper
824
1073
  installs free_ca_wrap as dfree, so the underlying ptr is NOT freed when
825
- 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.
826
1076
 
827
1077
  Object data_type GC safety: for GET (dir == CA_XFER_GET) the buffer may be
828
1078
  uninitialized garbage on entry, so we INT2NUM(0) zero-fill before
@@ -880,7 +1130,12 @@ ca_object_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
880
1130
  1, dim1, addrs, CA_XFER_PUT);
881
1131
  rdata = ca_object_wrap_transient(ca->data_type, ca->bytes,
882
1132
  1, dim1, data, dir);
883
- 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
+ }
884
1139
  return;
885
1140
  }
886
1141
 
@@ -917,6 +1172,7 @@ static void
917
1172
  ca_object_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
918
1173
  ca_size_t *strides, void *data, int dir)
919
1174
  {
1175
+ if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
920
1176
  CAObject *ca = (CAObject *) ap;
921
1177
  int8_t ndim;
922
1178
  ca_size_t native[CA_RANK_MAX], steps[CA_RANK_MAX], idx[CA_RANK_MAX];
@@ -969,7 +1225,15 @@ ca_object_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
969
1225
  }
970
1226
  rdata = ca_object_wrap_transient(ca->data_type, ca->bytes,
971
1227
  ndim, counts, data, dir);
972
- 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
+ }
973
1237
  return;
974
1238
  }
975
1239
  }
@@ -1040,7 +1304,12 @@ ca_object_func_fill_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *ptr)
1040
1304
  raddrs = ca_object_wrap_transient(CA_SIZE, sizeof(ca_size_t),
1041
1305
  1, dim1, addrs, CA_XFER_PUT);
1042
1306
  rval = rb_ca_ptr2obj(ca->self, ptr);
1043
- 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
+ }
1044
1313
  return;
1045
1314
  }
1046
1315
 
@@ -1108,31 +1377,32 @@ ca_object_func_create_mask (void *ap)
1108
1377
  CAObject *ca = (CAObject *) ap;
1109
1378
  volatile VALUE rmask;
1110
1379
  if ( ca_is_face(ca) ) {
1111
- /* Face: the mask refers to the parent's mask (same as the C-level Face
1112
- create_mask, e.g. ca_time_func_create_mask). Storage is the
1113
- parent, so the mask must be the parent's, not the internal __data__. */
1114
- ca_update_mask(ca->parent);
1115
- if ( ! ca->parent->mask ) {
1116
- ca_create_mask(ca->parent);
1117
- }
1118
- ca->mask = (CArray *) ca_refer_new(ca->parent->mask,
1119
- 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);
1120
1383
  return;
1121
1384
  }
1122
1385
  if ( rb_obj_respond_to(ca->self, rb_intern("create_mask"), Qtrue) ) {
1123
1386
  rb_funcall(ca->self, rb_intern("create_mask"), 0);
1124
1387
  }
1125
1388
  else {
1126
- rb_raise(rb_eRuntimeError, "can't create mask for CAObject");
1389
+ rb_raise(rb_eTypeError, "can't create mask for CAObject");
1127
1390
  }
1128
- ca_update_mask(ca->data);
1129
- if ( ! ca->data->mask ) {
1130
- 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
+ }
1131
1405
  }
1132
- ca->mask = (CArray*) ca_objmask_new(ca->self, ca->ndim, ca->dim);
1133
- ca->mask->ptr = ca->data->mask->ptr;
1134
- rmask = ca_wrap_struct(ca->mask);
1135
- rb_ivar_set(ca->self, rb_intern("mask"), rmask);
1136
1406
  }
1137
1407
 
1138
1408
  ca_operation_function_t ca_object_func = {
@@ -1171,6 +1441,7 @@ rb_ca_object_initialize_copy (VALUE self, VALUE other)
1171
1441
  CAObject *ca, *cs;
1172
1442
 
1173
1443
  TypedData_Get_Struct(self, CAObject, &caobject_data_type, ca);
1444
+ ca_check_uninitialized(ca);
1174
1445
  TypedData_Get_Struct(other, CAObject, &caobject_data_type, cs);
1175
1446
 
1176
1447
  if ( ca_func[CA_OBJ_OBJECT].pool_init ) {
@@ -1212,20 +1483,21 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
1212
1483
  {
1213
1484
  volatile VALUE rtype, rdim, ropt, rbytes = Qnil, rrdonly = Qnil,
1214
1485
  rparent = Qnil, rface = Qnil, rstorage = Qnil, rdata,
1215
- rorderable = Qnil, rcomparable = Qnil;
1486
+ rorderable = Qnil, rcomparable = Qnil, rnumeric = Qnil;
1216
1487
  CAObject *ca;
1217
1488
  int8_t data_type, ndim;
1218
1489
  ca_size_t dim[CA_RANK_MAX];
1219
1490
  ca_size_t bytes;
1220
1491
  int i;
1492
+ int storage_named = 0; /* :storage given by the caller */
1221
1493
 
1222
1494
  rb_scan_args(argc, argv, "21", (VALUE *) &rtype, (VALUE *) &rdim, (VALUE *) &ropt);
1223
- 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",
1224
1496
  &rbytes, &rrdonly, &rparent, &rface, &rstorage,
1225
- &rorderable, &rcomparable);
1497
+ &rorderable, &rcomparable, &rnumeric);
1226
1498
 
1227
1499
  if ( ( ! NIL_P(rparent) ) && ( ! rb_obj_is_carray(rparent) ) ) {
1228
- rb_raise(rb_eRuntimeError, "option :parent should be a carray");
1500
+ rb_raise(rb_eTypeError, "option :parent should be a carray");
1229
1501
  }
1230
1502
 
1231
1503
  /* :storage is meaningful only under face: true (= it documents the
@@ -1246,6 +1518,16 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
1246
1518
  "meaningful with face: true");
1247
1519
  }
1248
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
+
1249
1531
  /* face: true requires :parent. Under Face semantics the parent is the
1250
1532
  true storage owner and CAObject itself carries the semantic identity.
1251
1533
  The declared data_type is the SURFACE (= what dispatch sees);
@@ -1262,7 +1544,30 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
1262
1544
 
1263
1545
  rb_ca_guess_type_and_bytes(rtype, rbytes, &data_type, &bytes);
1264
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
+
1265
1569
  Check_Type(rdim, T_ARRAY);
1570
+ CA_CHECK_RANK(RARRAY_LEN(rdim)); /* before dim[] is filled */
1266
1571
 
1267
1572
  ndim = RARRAY_LEN(rdim);
1268
1573
  for (i=0; i<ndim; i++) {
@@ -1270,6 +1575,7 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
1270
1575
  }
1271
1576
 
1272
1577
  TypedData_Get_Struct(self, CAObject, &caobject_data_type, ca);
1578
+ ca_check_uninitialized(ca);
1273
1579
  ca_object_setup(ca, data_type, ndim, dim, bytes);
1274
1580
  ca->self = self;
1275
1581
 
@@ -1287,17 +1593,11 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
1287
1593
  ca->parent = cp;
1288
1594
  rb_ca_set_parent(self, rparent);
1289
1595
 
1290
- /* Face mode invariants. The check the author opts into depends on
1291
- whether :storage is explicit:
1292
- - :storage omitted → "surface == storage" contract:
1293
- surface (= declared data_type) must equal
1294
- parent.data_type. This catches typos for
1295
- Numeric Face authors who didn't intend
1296
- surface/storage to differ.
1297
- - :storage given → "surface != storage" opted-in:
1298
- surface is free (= e.g. CA_FIXLEN for
1299
- NonNumeric Face), :storage must agree with
1300
- 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. */
1301
1601
  if ( RTEST(rface) ) {
1302
1602
  if ( bytes != cp->bytes ) {
1303
1603
  rb_raise(rb_eTypeError,
@@ -1306,12 +1606,12 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
1306
1606
  (long long) bytes, (long long) cp->bytes);
1307
1607
  }
1308
1608
  if ( NIL_P(rstorage) ) {
1309
- /* surface == storage contract */
1609
+ /* The data_type argument names the storage. */
1310
1610
  if ( cp->data_type != data_type ) {
1311
1611
  rb_raise(rb_eTypeError,
1312
1612
  "CAObject Face: parent data_type mismatch "
1313
1613
  "(declared=%d, parent=%d). Use :storage option to "
1314
- "opt into surface != storage (NonNumeric Face).",
1614
+ "declare a surface that differs from the storage.",
1315
1615
  (int) data_type, (int) cp->data_type);
1316
1616
  }
1317
1617
  }
@@ -1323,10 +1623,20 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
1323
1623
  &declared_storage, &declared_storage_bytes);
1324
1624
  if ( cp->data_type != declared_storage ) {
1325
1625
  rb_raise(rb_eTypeError,
1326
- "CAObject Face: :storage mismatch "
1327
- "(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)",
1328
1631
  (int) declared_storage, (int) cp->data_type);
1329
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
+ }
1330
1640
  }
1331
1641
  ca_set_flag(ca, CA_FLAG_IS_FACE);
1332
1642
  if ( RTEST(rorderable) ) {