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_record.c CHANGED
@@ -20,6 +20,7 @@
20
20
 
21
21
  #include "carray.h"
22
22
  #include "ca_obj_face.h"
23
+ #include "carray_internal.h"
23
24
 
24
25
  typedef struct {
25
26
  /* === CAView prefix === */
@@ -175,18 +176,6 @@ ca_record_func_allocate (void *ap)
175
176
  ca->ptr = ca->parent->ptr;
176
177
  }
177
178
 
178
- static void
179
- ca_record_func_create_mask (void *ap)
180
- {
181
- CARecord *ca = (CARecord *) ap;
182
- ca_update_mask(ca->parent);
183
- if ( ! ca->parent->mask ) {
184
- ca_create_mask(ca->parent);
185
- }
186
- ca->mask = (CArray *) ca_refer_new(ca->parent->mask,
187
- CA_BOOLEAN, ca->ndim, ca->dim, 0, 0);
188
- }
189
-
190
179
  ca_operation_function_t ca_record_func = {
191
180
  -1, /* CA_OBJ_RECORD — filled by ca_install_obj_type */
192
181
  CA_VIEW_ARRAY,
@@ -197,7 +186,7 @@ ca_operation_function_t ca_record_func = {
197
186
  ca_face_sync,
198
187
  ca_face_detach,
199
188
  ca_face_fill_data,
200
- ca_record_func_create_mask,
189
+ ca_view_func_create_mask,
201
190
  ca_face_xfer_index,
202
191
  ca_face_xfer_addrs,
203
192
  NULL, /* fold_stride: identity Face is not foldable */
@@ -230,6 +219,19 @@ ca_record_build (VALUE klass, VALUE data_class, VALUE parent_val)
230
219
  "CARecord requires a CA_FIXLEN parent (got data_type=%d)",
231
220
  parent_ca->data_type);
232
221
  }
222
+ /* Each record is read and written at the data_class's member offsets,
223
+ so the parent's cells have to be exactly that size. */
224
+ {
225
+ ca_size_t record_bytes =
226
+ NUM2SIZE(rb_const_get(data_class, rb_intern("DATA_SIZE")));
227
+ if ( parent_ca->bytes != record_bytes ) {
228
+ rb_raise(rb_eArgError,
229
+ "CARecord: %"PRIsVALUE" records are %lld bytes, the array's "
230
+ "elements are %lld bytes",
231
+ data_class, (long long) record_bytes,
232
+ (long long) parent_ca->bytes);
233
+ }
234
+ }
233
235
  ca = ca_record_new(parent_ca, data_class);
234
236
  obj = TypedData_Wrap_Struct(klass, &carecord_data_type, ca);
235
237
  rb_ca_set_parent(obj, parent_val);
@@ -280,7 +282,9 @@ rb_ca_record_s_new (int argc, VALUE *argv, VALUE klass)
280
282
  }
281
283
  bytes = NUM2SIZE(rb_const_get(data_class, rb_intern("DATA_SIZE")));
282
284
 
283
- parent_val = rb_carray_new(CA_FIXLEN, ndim, dim, bytes, NULL);
285
+ /* Zero-filled, as CArray.new is: a new record holds zeros, not the bytes
286
+ the allocator last handed out. */
287
+ parent_val = rb_carray_new_safe(CA_FIXLEN, ndim, dim, bytes, NULL);
284
288
  return ca_record_build(klass, data_class, parent_val);
285
289
  }
286
290
 
@@ -328,7 +332,7 @@ rb_ca_record_s_data_class_dsl (int argc, VALUE *argv, VALUE klass)
328
332
  }
329
333
  rb_scan_args(argc, argv, "1", &given);
330
334
  if ( ! NIL_P(current) ) {
331
- rb_raise(rb_eRuntimeError,
335
+ rb_raise(rb_eArgError,
332
336
  "data_class already fixed to %"PRIsVALUE
333
337
  "; CARecord subclass data_class is immutable once set",
334
338
  current);
@@ -352,11 +356,16 @@ rb_ca_record_initialize_copy (VALUE self, VALUE other)
352
356
  {
353
357
  CARecord *ca, *cs;
354
358
  TypedData_Get_Struct(self, CARecord, &carecord_data_type, ca);
359
+ ca_check_uninitialized(ca);
355
360
  TypedData_Get_Struct(other, CARecord, &carecord_data_type, cs);
356
361
  if ( ca_func[CA_OBJ_RECORD].pool_init ) {
357
362
  ca_array_pool_alloc(ca, CA_OBJ_RECORD, cs->parent->ndim);
358
363
  }
359
364
  ca_record_setup(ca, cs->parent, cs->data_class);
365
+ /* Marshal loads into an empty instance through here without copying
366
+ ivars, so the parent pin is set from `other`, not inherited. */
367
+ rb_ca_set_parent(self, rb_ca_parent(other));
368
+ rb_ivar_set(self, rb_intern("member"), rb_hash_new());
360
369
  return self;
361
370
  }
362
371
 
@@ -401,7 +410,7 @@ ca_check_data_class (VALUE rtype)
401
410
  {
402
411
  if ( ! rb_obj_is_data_class(rtype) ) {
403
412
  VALUE inspect = rb_inspect(rtype);
404
- rb_raise(rb_eRuntimeError,
413
+ rb_raise(rb_eTypeError,
405
414
  "<%s> is not a data_class, which should has the features\n" \
406
415
  " * constant data_class::DATA_SIZE -> integer\n" \
407
416
  " * constant data_class::MEMBERS -> array of string\n" \
@@ -429,6 +438,26 @@ ca_record_state_compatible (CArray *a, CArray *b)
429
438
  return (ra->data_class == rb->data_class) ? 1 : 0;
430
439
  }
431
440
 
441
+ /* Write hook: a struct element becomes its bytes, on every store path (a
442
+ block or index store writes to the storage, below the Face, where the
443
+ element itself could not be cast). A struct of another data_class is
444
+ refused; anything else is left to the fixlen store (a String is bytes). */
445
+ static VALUE
446
+ ca_record_element_to_storage (VALUE self, VALUE obj)
447
+ {
448
+ volatile VALUE data_class = rb_ca_data_class(self);
449
+ if ( rb_obj_is_kind_of(obj, data_class) ) {
450
+ return rb_funcall(obj, rb_intern("encode"), 0);
451
+ }
452
+ if ( RB_TYPE_P(obj, T_OBJECT) && ! RB_TYPE_P(obj, T_STRING)
453
+ && RTEST(rb_obj_is_data_class(rb_obj_class(obj))) ) {
454
+ rb_raise(rb_eTypeError,
455
+ "a %"PRIsVALUE" is not an element of a record array of %"PRIsVALUE,
456
+ rb_obj_class(obj), data_class);
457
+ }
458
+ return obj;
459
+ }
460
+
432
461
  /* ------------------------------------------------------------------------- */
433
462
 
434
463
  void
@@ -454,6 +483,12 @@ Init_ca_obj_record (void)
454
483
  ca_face_register_state_compatible(CA_OBJ_RECORD,
455
484
  ca_record_state_compatible);
456
485
 
486
+ /* The record's bytes are its members: a MemoryView consumer reads them
487
+ through the T{...} struct format built from the data_class. */
488
+ ca_face_register_memory_view(CA_OBJ_RECORD, 1);
489
+ ca_face_register_element_to_storage(CA_OBJ_RECORD,
490
+ ca_record_element_to_storage);
491
+
457
492
  rb_define_alloc_func(rb_cCARecord, rb_ca_record_s_allocate);
458
493
  rb_define_method(rb_cCARecord, "initialize_copy",
459
494
  rb_ca_record_initialize_copy, 1);
data/ext/ca_obj_reduce.c CHANGED
@@ -162,6 +162,7 @@ static void
162
162
  ca_reduce_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
163
163
  ca_size_t *strides, void *data, int dir)
164
164
  {
165
+ if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
165
166
  CAReduce *ca = (CAReduce *) ap;
166
167
  int8_t ndim = ca->ndim;
167
168
  ca_size_t native[CA_RANK_MAX], idx[CA_RANK_MAX];
@@ -224,8 +225,7 @@ ca_reduce_func_sync (void *ap)
224
225
  ca_reduce_func_xfer_addrs(ca, 1, &i, p, CA_XFER_PUT);
225
226
  p++;
226
227
  }
227
- ca_sync(ca->parent);
228
- ca_detach(ca->parent);
228
+ ca_sync_detach(ca->parent);
229
229
  }
230
230
 
231
231
  static void
@@ -264,8 +264,7 @@ ca_reduce_func_fill_data (void *ap, void *ptr)
264
264
  for (i=0; i<ca->elements; i++) {
265
265
  ca_reduce_func_xfer_addrs(ca, 1, &i, ptr, CA_XFER_PUT);
266
266
  }
267
- ca_sync(ca->parent);
268
- ca_detach(ca->parent);
267
+ ca_sync_detach(ca->parent);
269
268
  }
270
269
 
271
270
  static void
@@ -273,10 +272,7 @@ ca_reduce_func_create_mask (void *ap)
273
272
  {
274
273
  CAReduce *ca = (CAReduce *) ap;
275
274
 
276
- ca_update_mask(ca->parent);
277
- if ( ! ca->parent->mask ) {
278
- ca_create_mask(ca->parent);
279
- }
275
+ ca_create_mask(ca->parent);
280
276
 
281
277
  ca->mask = (CArray *) ca_reduce_new(ca->parent->mask, ca->count, ca->offset);
282
278
  }
@@ -314,6 +310,7 @@ rb_ca_reduce_initialize_copy (VALUE self, VALUE other)
314
310
  CAReduce *ca, *cs;
315
311
 
316
312
  TypedData_Get_Struct(self, CAReduce, &careduce_data_type, ca);
313
+ ca_check_uninitialized(ca);
317
314
  TypedData_Get_Struct(other, CAReduce, &careduce_data_type, cs);
318
315
 
319
316
  ca_reduce_setup(ca, cs->parent, cs->count, cs->offset);
data/ext/ca_obj_refer.c CHANGED
@@ -79,29 +79,38 @@ ca_refer_setup (CARefer *ca, CArray *parent,
79
79
  CA_CHECK_DIM(ndim, dim);
80
80
  CA_CHECK_BYTES(data_type, bytes);
81
81
 
82
+ /* An object cell is a VALUE the GC marks: reading other bytes as one
83
+ marks garbage, and reading one as bytes exposes an address. */
82
84
  if (ca_is_object_type(parent) && data_type != CA_OBJECT) {
83
- rb_raise(rb_eRuntimeError,
85
+ rb_raise(rb_eCADataTypeError,
84
86
  "object array can't be referred by other data type");
85
87
  }
88
+ if (! ca_is_object_type(parent) && data_type == CA_OBJECT) {
89
+ rb_raise(rb_eCADataTypeError,
90
+ "non-object array can't be referred as object data type");
91
+ }
86
92
  if (parent->elements && bytes > parent_bytes * parent->elements) {
87
- rb_raise(rb_eRuntimeError, "bytes exceeds the data size of referent");
93
+ rb_raise(rb_eArgError, "bytes exceeds the data size of referent");
88
94
  }
89
95
  if (bytes < parent_bytes && parent_bytes % bytes != 0) {
90
- rb_raise(rb_eRuntimeError,
96
+ rb_raise(rb_eArgError,
91
97
  "bytes of reference array must be a multiple of that of referent");
92
98
  }
93
99
  if (bytes > parent_bytes && bytes % parent_bytes != 0) {
94
- rb_raise(rb_eRuntimeError,
100
+ rb_raise(rb_eArgError,
95
101
  "bytes of reference array must be a multiple of that of referent");
96
102
  }
97
103
  if (offset < 0) {
98
- rb_raise(rb_eRuntimeError, "negative offset is not permitted for CARefer");
104
+ rb_raise(rb_eArgError, "negative offset is not permitted for CARefer");
105
+ }
106
+ elements = ca_shape_elements(ndim, dim, bytes);
107
+ if (offset > parent->elements) {
108
+ rb_raise(rb_eArgError,
109
+ "data size of reference array must not exceed that of referent");
99
110
  }
100
- elements = 1;
101
- for (i = 0; i < ndim; i++) elements *= dim[i];
102
111
  if ((bytes * elements + parent_bytes * offset) >
103
112
  (parent_bytes * parent->elements)) {
104
- rb_raise(rb_eRuntimeError,
113
+ rb_raise(rb_eArgError,
105
114
  "data size of reference array must not exceed that of referent");
106
115
  }
107
116
 
@@ -114,6 +123,12 @@ ca_refer_setup (CARefer *ca, CArray *parent,
114
123
  /* base_offset is in bytes: `offset` is in parent-element units. */
115
124
  base_offset = offset * parent_bytes;
116
125
 
126
+ /* ca == NULL asks for the checks above only, so that a constructor can
127
+ run them before it allocates. */
128
+ if ( ca == NULL ) {
129
+ return 0;
130
+ }
131
+
117
132
  /* CAREFUL: initialise mask0 before ca_stride_setup — the setup
118
133
  may dispatch to ca_refer_func_create_mask, which writes to
119
134
  mask0. Leaving it uninitialised risks freeing a garbage
@@ -134,7 +149,9 @@ ca_refer_new (CArray *parent,
134
149
  int8_t data_type, int8_t ndim, ca_size_t *dim, ca_size_t bytes,
135
150
  ca_size_t offset)
136
151
  {
137
- CARefer *ca = (CARefer *) ca_array_alloc(CA_OBJ_REFER, ndim);
152
+ CARefer *ca;
153
+ ca_refer_setup(NULL, parent, data_type, ndim, dim, bytes, offset);
154
+ ca = (CARefer *) ca_array_alloc(CA_OBJ_REFER, ndim);
138
155
  ca_refer_setup(ca, parent, data_type, ndim, dim, bytes, offset);
139
156
  return ca;
140
157
  }
@@ -172,10 +189,7 @@ ca_refer_func_create_mask (void *ap)
172
189
  ca_size_t parent_bytes = ca->parent->bytes;
173
190
  ca_size_t parent_offset;
174
191
 
175
- ca_update_mask(ca->parent);
176
- if (! ca->parent->mask) {
177
- ca_create_mask(ca->parent);
178
- }
192
+ ca_create_mask(ca->parent);
179
193
 
180
194
  parent_offset = ca->base_offset / parent_bytes;
181
195
 
@@ -200,9 +214,12 @@ ca_refer_func_create_mask (void *ap)
200
214
  ca->mask0 =
201
215
  (CArray *) ca_repeat_new(ca->parent->mask, ca->parent->ndim + 1, count);
202
216
  ca_unset_flag(ca->mask0, CA_FLAG_READ_ONLY);
217
+ /* mask0 has ratio cells per parent element, so the offset is counted
218
+ in those. */
203
219
  ca->mask =
204
220
  (CArray *) ca_refer_new(ca->mask0,
205
- CA_BOOLEAN, ca->ndim, ca->dim, 0, parent_offset);
221
+ CA_BOOLEAN, ca->ndim, ca->dim, 0,
222
+ parent_offset * ratio);
206
223
  }
207
224
  else {
208
225
  /* Spanned reinterpret: ratio = bytes / parent_bytes parent
@@ -232,6 +249,7 @@ rb_ca_refer_initialize_copy (VALUE self, VALUE other)
232
249
  {
233
250
  CARefer *ca, *cs;
234
251
  TypedData_Get_Struct(self, CARefer, &carefer_data_type, ca);
252
+ ca_check_uninitialized(ca);
235
253
  TypedData_Get_Struct(other, CARefer, &carefer_data_type, cs);
236
254
  if ( ca_func[CA_OBJ_REFER].pool_init ) {
237
255
  ca_array_pool_alloc(ca, CA_OBJ_REFER, cs->ndim);
@@ -275,6 +293,34 @@ rb_ca_refer_initialize_copy (VALUE self, VALUE other)
275
293
  * 2. Match ranges of parent axes to new axes so element products
276
294
  * agree. Inside each matched range, root_strides must be
277
295
  * inter-axis contiguous. */
296
+ /* Three properties of an array are answered by walking its C parent chain:
297
+ ca_is_value_array, ca_is_readonly and ca_is_mask_array. An array that is
298
+ built somewhere other than as a child of `src` -- a reshape that folds
299
+ onto the root, the copy that dup / clone makes -- does not have `src` on
300
+ that chain, so carry each property across from `src`. Without this a
301
+ reshape or a dup of x.value reads the mask of x again (and takes UNDEF
302
+ into it), and a reshape of a broadcast view becomes writable. */
303
+ void
304
+ ca_keep_chain_flags (CArray *view, CArray *src)
305
+ {
306
+ if ( ca_is_value_array(src) ) {
307
+ if ( view->mask ) {
308
+ ca_free(view->mask);
309
+ view->mask = NULL;
310
+ }
311
+ ca_set_flag(view, CA_FLAG_VALUE_ARRAY);
312
+ }
313
+ if ( ca_is_readonly(src) ) {
314
+ ca_set_flag(view, CA_FLAG_READ_ONLY);
315
+ if ( view->mask ) {
316
+ ca_set_flag(view->mask, CA_FLAG_READ_ONLY);
317
+ }
318
+ }
319
+ if ( ca_is_mask_array(src) ) {
320
+ ca_set_flag(view, CA_FLAG_MASK_ARRAY);
321
+ }
322
+ }
323
+
278
324
  static int
279
325
  ca_reshape_try_strides (CArray *parent,
280
326
  const ca_size_t *new_dim,
@@ -401,8 +447,11 @@ ca_reshape_try_strides (CArray *parent,
401
447
  return 1;
402
448
  }
403
449
 
450
+ /* `refer` with its options given apart from argv, for callers in C that
451
+ build the options themselves (a Hash at the end of argv is read as
452
+ options only when the Ruby caller passed keywords). */
404
453
  static VALUE
405
- rb_ca_refer (int argc, VALUE *argv, VALUE self)
454
+ rb_ca_refer_kw (int argc, VALUE *argv, VALUE ropt, VALUE self)
406
455
  {
407
456
  volatile VALUE obj = Qnil;
408
457
  CArray *ca;
@@ -415,7 +464,7 @@ rb_ca_refer (int argc, VALUE *argv, VALUE self)
415
464
 
416
465
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
417
466
 
418
- if (argc == 0) {
467
+ if (argc == 0 && NIL_P(ropt)) {
419
468
  data_type = ca->data_type;
420
469
  bytes = ca->bytes;
421
470
  ndim = ca->ndim;
@@ -425,39 +474,57 @@ rb_ca_refer (int argc, VALUE *argv, VALUE self)
425
474
  rb_ca_set_parent(obj, self);
426
475
  }
427
476
  else {
428
- volatile VALUE rtype, rdim, ropt, rbytes = Qnil, roffset = Qnil;
429
- ropt = rb_pop_options(&argc, &argv);
477
+ volatile VALUE rtype, rdim, rbytes = Qnil, roffset = Qnil;
430
478
  rb_scan_args(argc, argv, "11", (VALUE *) &rtype, (VALUE *) &rdim);
431
479
  rb_scan_options(ropt, "bytes,offset", &rbytes, &roffset);
432
480
  if (NIL_P(rbytes)) rbytes = rb_ca_bytes(self);
433
481
  rb_ca_guess_type_and_bytes(rtype, rbytes, &data_type, &bytes);
434
482
  if (NIL_P(rdim)) {
435
483
  if (ca->bytes != bytes) {
436
- rb_raise(rb_eRuntimeError,
484
+ rb_raise(rb_eArgError,
437
485
  "specify dimension shape for different byte size");
438
486
  } else {
439
487
  rdim = rb_ca_dim(self);
440
488
  }
441
489
  }
442
490
  Check_Type(rdim, T_ARRAY);
491
+ CA_CHECK_RANK(RARRAY_LEN(rdim)); /* before dim[] is filled */
443
492
  ndim = RARRAY_LEN(rdim);
444
493
  for (i = 0; i < ndim; i++) dim[i] = NUM2SIZE(rb_ary_entry(rdim, i));
445
494
  if (! NIL_P(roffset)) offset = NUM2SIZE(roffset);
495
+ /* When rtype is a data_class the result is a record array. It is
496
+ built on the storage, never on a Face, so that a Face stays on top
497
+ of the chain: re-reading a record array as its own data_class lifts
498
+ it (keeping its class), any other data_class wraps a new CARecord. */
499
+ if ( rb_obj_is_data_class(rtype) ) {
500
+ volatile VALUE base = rb_ca_strip_face_value(self);
501
+ CArray *base_ca;
502
+ TypedData_Get_Struct(base, CArray, &carray_data_type, base_ca);
503
+ cr = ca_refer_new(base_ca, data_type, ndim, dim, bytes, offset);
504
+ obj = ca_wrap_struct(cr);
505
+ rb_ca_set_parent(obj, base);
506
+ if ( ca->obj_type == CA_OBJ_RECORD
507
+ && rb_ca_data_class(self) == rtype ) {
508
+ return ca_face_lift(obj, self);
509
+ }
510
+ return rb_funcall(rb_const_get(rb_cObject, rb_intern("CARecord")),
511
+ rb_intern("wrap"), 2, obj, rtype);
512
+ }
446
513
  cr = ca_refer_new((CArray *) ca, data_type, ndim, dim, bytes, offset);
447
514
  obj = ca_wrap_struct(cr);
448
515
  rb_ca_set_parent(obj, self);
449
- /* When rtype is a data_class, wrap the result in CARecord so
450
- the data_class is carried even if self is not itself a Face. */
451
- if ( rb_obj_is_data_class(rtype) ) {
452
- obj = rb_funcall(rb_const_get(rb_cObject, rb_intern("CARecord")),
453
- rb_intern("wrap"), 2, obj, rtype);
454
- return obj;
455
- }
456
516
  }
457
517
  CA_WRAPPER_LIFT(obj, self, ca);
458
518
  return obj;
459
519
  }
460
520
 
521
+ static VALUE
522
+ rb_ca_refer (int argc, VALUE *argv, VALUE self)
523
+ {
524
+ volatile VALUE ropt = rb_pop_options(&argc, &argv);
525
+ return rb_ca_refer_kw(argc, argv, ropt, self);
526
+ }
527
+
461
528
  VALUE
462
529
  rb_ca_refer_new (VALUE self,
463
530
  int8_t data_type, int8_t ndim, ca_size_t *dim, ca_size_t bytes,
@@ -483,7 +550,7 @@ rb_ca_refer_new (VALUE self,
483
550
  rb_ary_store(list, 2, ropt);
484
551
 
485
552
  {
486
- volatile VALUE obj = rb_ca_refer(3, (VALUE *) RARRAY_CONST_PTR(list), self);
553
+ volatile VALUE obj = rb_ca_refer_kw(2, (VALUE *) RARRAY_CONST_PTR(list), ropt, self);
487
554
  CArray *co;
488
555
 
489
556
  /* CAREFUL: this is the internal builder -- some fifteen call sites want
@@ -535,6 +602,7 @@ rb_ca_reshape (int argc, VALUE *argv, VALUE self)
535
602
  CARefer *cr;
536
603
  ca_size_t new_dim[CA_RANK_MAX];
537
604
  ca_size_t prod = 1;
605
+ int overflow = 0, zero = 0;
538
606
  int placeholder_idx = -1; /* axis with `-1` or `:~` infer placeholder */
539
607
  int i;
540
608
  volatile VALUE obj;
@@ -553,7 +621,7 @@ rb_ca_reshape (int argc, VALUE *argv, VALUE self)
553
621
  if ( (FIXNUM_P(argv[i]) && FIX2LONG(argv[i]) == -1)
554
622
  || argv[i] == sym_tilde ) {
555
623
  if (placeholder_idx >= 0) {
556
- rb_raise(rb_eRuntimeError,
624
+ rb_raise(rb_eArgError,
557
625
  "reshape: only one auto-infer placeholder (-1 / :~) allowed");
558
626
  }
559
627
  placeholder_idx = i;
@@ -578,7 +646,7 @@ rb_ca_reshape (int argc, VALUE *argv, VALUE self)
578
646
  src = (i < ca->ndim) ? i : ca->ndim - (argc - i);
579
647
  }
580
648
  if (src < 0 || src >= ca->ndim) {
581
- rb_raise(rb_eRuntimeError,
649
+ rb_raise(rb_eArgError,
582
650
  "reshape: nil at axis %d has no source dim", i);
583
651
  }
584
652
  new_dim[i] = ca->dim[src];
@@ -586,13 +654,27 @@ rb_ca_reshape (int argc, VALUE *argv, VALUE self)
586
654
  else {
587
655
  new_dim[i] = NUM2SIZE(argv[i]);
588
656
  }
589
- prod *= new_dim[i];
657
+ /* A product too large to count cannot equal the element count; an
658
+ extent of zero anywhere makes it zero whatever it overflowed to. */
659
+ if ( __builtin_mul_overflow(prod, new_dim[i], &prod) ) {
660
+ overflow = 1;
661
+ }
662
+ if ( new_dim[i] == 0 ) {
663
+ zero = 1;
664
+ }
665
+ }
666
+ if ( zero ) {
667
+ prod = 0;
668
+ }
669
+ else if ( overflow ) {
670
+ rb_raise(rb_eArgError,
671
+ "reshape: the shape has more elements than an array can hold");
590
672
  }
591
673
 
592
674
  /* Resolve placeholder dim from the leftover total. */
593
675
  if (placeholder_idx >= 0) {
594
676
  if (prod == 0 || ca->elements % prod != 0) {
595
- rb_raise(rb_eRuntimeError,
677
+ rb_raise(rb_eArgError,
596
678
  "reshape: cannot infer dim for `-1` placeholder");
597
679
  }
598
680
  new_dim[placeholder_idx] = ca->elements / prod;
@@ -604,7 +686,7 @@ rb_ca_reshape (int argc, VALUE *argv, VALUE self)
604
686
  a subset), and on non-contiguous parents the compose-fold
605
687
  path corrupts data outright — hence the raise. */
606
688
  if (prod != ca->elements) {
607
- rb_raise(rb_eRuntimeError,
689
+ rb_raise(rb_eArgError,
608
690
  "reshape: cannot reshape array of %lld elements into a shape "
609
691
  "of %lld elements (use -1 / :~ to infer a dim)",
610
692
  (long long) ca->elements, (long long) prod);
@@ -627,6 +709,7 @@ rb_ca_reshape (int argc, VALUE *argv, VALUE self)
627
709
  ca->data_type, ca->bytes,
628
710
  (int8_t) argc, new_dim,
629
711
  out_strides, out_base);
712
+ ca_keep_chain_flags((CArray *) cs, ca);
630
713
  obj = ca_wrap_struct(cs);
631
714
  rb_ca_set_parent(obj, self);
632
715
  CA_WRAPPER_LIFT(obj, self, ca);
@@ -664,6 +747,7 @@ rb_ca_flatten (VALUE self)
664
747
  CAStride *cs = ca_stride_new(CA_OBJ_STRIDE, out_root,
665
748
  ca->data_type, ca->bytes,
666
749
  1, dim, out_strides, out_base);
750
+ ca_keep_chain_flags((CArray *) cs, ca);
667
751
  obj = ca_wrap_struct(cs);
668
752
  rb_ca_set_parent(obj, self);
669
753
  CA_WRAPPER_LIFT(obj, self, ca);
@@ -725,7 +809,7 @@ rb_ca_insert_axis (int argc, VALUE *argv, VALUE self)
725
809
  gap_count[i] = 0;
726
810
  }
727
811
  for (i = 0; i < argc; i++) {
728
- int raw = NUM2INT(argv[i]);
812
+ int raw = (int) ca_axis_integer(argv[i], "insert_axis");
729
813
  int g = (raw < 0) ? raw + ngap : raw;
730
814
  if (g < 0 || g >= ngap) {
731
815
  rb_raise(rb_eArgError,
data/ext/ca_obj_remap.c CHANGED
@@ -7,9 +7,9 @@
7
7
  Constraints:
8
8
  - ref.dim == idx.dim (same shape, same ndim, per-axis dim match)
9
9
  - idx.data_type == CA_SIZE (caller responsible)
10
- - No public constructor — there is no `CArray#remap(idx)` or
11
- `CARemap.new` surface; the class is only reachable via internal
12
- helpers and the `ca[idx]` same-shape indexer fast path.
10
+ - No public constructor — there is no `CArray#remap(idx)` surface; a
11
+ view is built by internal helpers and the `ca[idx]` same-shape
12
+ indexer fast path. `dup` / `clone` copy an existing one.
13
13
  - The CARemap Ruby class IS rb_define_class'd: visibility lets users
14
14
  see "this view came from a same-shape gather" when they inspect
15
15
  `.class`. CA_OBJ_REMAP integer remains unexported as a Ruby
@@ -18,6 +18,7 @@
18
18
  ---------------------------------------------------------------------------- */
19
19
 
20
20
  #include "carray.h"
21
+ #include "carray_internal.h"
21
22
 
22
23
  typedef struct {
23
24
  int16_t obj_type;
@@ -216,9 +217,7 @@ ca_remap_func_allocate (void *ap)
216
217
  static void
217
218
  ca_remap_func_attach (void *ap)
218
219
  {
219
- CARemap *ca = (CARemap *) ap;
220
- ca->ptr = xmalloc(ca_length(ca));
221
- ca_remap_func_xfer_all(ca, ca->ptr, CA_XFER_GET);
220
+ ca_axis_view_attach_owned(ap);
222
221
  }
223
222
 
224
223
  static void
@@ -265,10 +264,7 @@ static void
265
264
  ca_remap_func_create_mask (void *ap)
266
265
  {
267
266
  CARemap *ca = (CARemap *) ap;
268
- ca_update_mask(ca->parent);
269
- if ( ! ca->parent->mask ) {
270
- ca_create_mask(ca->parent);
271
- }
267
+ ca_create_mask(ca->parent);
272
268
  /* Mask is a CARemap over (parent->mask, idx) — same shape, boolean. */
273
269
  ca->mask = (CArray *) ca_remap_new(ca->parent->mask, ca->idx);
274
270
  }
@@ -282,7 +278,7 @@ ca_remap_func_create_mask (void *ap)
282
278
  2. ca_xfer_addrs(ca->idx, n, addrs, paddrs, GET) — one batched
283
279
  read of the parent flat addresses through ca->idx's own
284
280
  xfer_addrs dispatcher (entity, view, view chain — all
285
- transparent).
281
+ transparent), each address then checked against the parent.
286
282
  3. ca_xfer_addrs(ca->parent, n, paddrs, data, dir) — one batched
287
283
  gather/scatter of the user payload against parent.
288
284
 
@@ -295,13 +291,20 @@ ca_remap_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
295
291
  {
296
292
  CARemap *ca = (CARemap *) ap;
297
293
  ca_size_t *paddrs;
294
+ ca_size_t i;
298
295
  volatile VALUE holder;
299
296
 
300
297
  if ( n == 0 ) return;
301
298
 
302
299
  paddrs = ALLOCV_N(ca_size_t, holder, n);
303
- ca_xfer_addrs(ca->idx, n, addrs, paddrs, CA_XFER_GET);
304
- ca_xfer_addrs(ca->parent, n, paddrs, data, dir);
300
+ ca_xfer_addrs(ca->idx, n, addrs, paddrs, CA_XFER_GET);
301
+ /* idx is read live, so its values are checked here, not at
302
+ construction: negative counts from the end, out of range raises
303
+ before the parent is touched. */
304
+ for ( i = 0; i < n; i++ ) {
305
+ CA_CHECK_INDEX(paddrs[i], ca->parent->elements);
306
+ }
307
+ ca_xfer_addrs(ca->parent, n, paddrs, data, dir);
305
308
  ALLOCV_END(holder);
306
309
  }
307
310
 
@@ -329,6 +332,7 @@ static void
329
332
  ca_remap_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
330
333
  ca_size_t *strides, void *data, int dir)
331
334
  {
335
+ if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
332
336
  CARemap *ca = (CARemap *) ap;
333
337
  int8_t ndim = ca->ndim;
334
338
  ca_size_t native[CA_RANK_MAX], idx[CA_RANK_MAX];
@@ -443,18 +447,54 @@ rb_ca_remap_mapper (VALUE self)
443
447
  }
444
448
 
445
449
 
450
+ /* `dup` / `clone`. CARemap has no allocator (Ruby cannot build one), so
451
+ Object#dup cannot copy it; these build the copy the way the indexer
452
+ does: a new view over the same parent and the same index array. */
453
+ static VALUE
454
+ rb_ca_remap_dup (VALUE self)
455
+ {
456
+ return rb_ca_remap_new(rb_ca_parent(self),
457
+ rb_ivar_get(self, rb_intern("remap_idx")));
458
+ }
459
+
460
+ static VALUE
461
+ rb_ca_remap_clone (int argc, VALUE *argv, VALUE self)
462
+ {
463
+ VALUE opts = Qnil, vfreeze = Qnil;
464
+ volatile VALUE obj;
465
+ rb_scan_args(argc, argv, "0:", &opts);
466
+ if ( ! NIL_P(opts) ) {
467
+ /* rb_get_kwargs refuses any key but freeze:, as Kernel#clone does. */
468
+ ID kw[1];
469
+ VALUE val[1];
470
+ kw[0] = rb_intern("freeze");
471
+ rb_get_kwargs(opts, kw, 0, 1, val);
472
+ if ( val[0] != Qundef ) {
473
+ vfreeze = val[0];
474
+ }
475
+ }
476
+ obj = rb_ca_remap_dup(self);
477
+ if ( RTEST(vfreeze) || ( NIL_P(vfreeze) && OBJ_FROZEN(self) ) ) {
478
+ rb_obj_freeze(obj);
479
+ }
480
+ return obj;
481
+ }
482
+
446
483
  void
447
484
  Init_ca_obj_remap (void)
448
485
  {
449
486
  /* CARemap is a proper Ruby class so users can see "this view is a
450
- per-element gather" on .class. No allocator, no initialize, no
451
- `new` reachable from Ruby — construction is C-internal only.
452
- CA_OBJ_REMAP integer remains unexported (it's an internal type
453
- tag, not user-meaningful). */
487
+ per-element gather" on .class. A view is built from C only (the
488
+ indexer and sort / partition): there is no allocator, and `dup` /
489
+ `clone` are defined here instead of going through one. CA_OBJ_REMAP
490
+ integer remains unexported (it's an internal type tag, not
491
+ user-meaningful). */
454
492
  rb_cCARemap = rb_define_class("CARemap", rb_cCAView);
455
493
  rb_cCARemapMask = rb_define_class("CARemapMask", rb_cCARemap);
456
494
  rb_undef_alloc_func(rb_cCARemap);
457
495
  rb_undef_alloc_func(rb_cCARemapMask);
496
+ rb_define_method(rb_cCARemap, "dup", rb_ca_remap_dup, 0);
497
+ rb_define_method(rb_cCARemap, "clone", rb_ca_remap_clone, -1);
458
498
 
459
499
  ca_remap_func.struct_size = sizeof(CARemap);
460
500
  ca_remap_func.pool_bytes = ca_remap_pool_bytes;