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
@@ -51,7 +51,6 @@
51
51
  #include <string.h>
52
52
  #include <math.h>
53
53
 
54
- #define CA_SORT_INSERTION_THRESHOLD 16
55
54
 
56
55
  /* ---------------------------------------------------------------------------
57
56
  DEFINE_SORT_MERGE(TYPE, SUFFIX)
@@ -616,5 +615,13 @@ DEFINE_PARTITION_QUICK(uint64_t, u64);
616
615
  DEFINE_PARTITION_QUICK(float32_t, f32);
617
616
  DEFINE_PARTITION_QUICK(double, f64);
618
617
 
618
+ /* The pair mergesort above for object pairs, which have no `<`: every
619
+ comparison goes through cmp (see CA_SORT_MERGE_BY_CMP). */
620
+ void
621
+ ca_sort_merge_pair_object (ca_pair_object *a, ca_pair_object *aux,
622
+ ca_size_t n,
623
+ int (*cmp)(const void *, const void *))
624
+ CA_SORT_MERGE_BY_CMP(ca_pair_object)
625
+
619
626
  /* No Init function: this file exposes only C kernels (via ca_sort_kernels.h);
620
627
  it registers no Ruby methods. */
data/ext/carray_struct.c CHANGED
@@ -16,7 +16,8 @@
16
16
  (primitive / bitfield / endian-swap)
17
17
  DISPATCH_TABLE hit -> Proc invoked via rb_funcall("call")
18
18
  (nested struct / CArray template / fixlen)
19
- miss -> send(name) / send(name=)
19
+ a Symbol, or a name that is not a member, raises: a member is
20
+ s["name"] or s[position], and its accessor s.name
20
21
 
21
22
  ---------------------------------------------------------------------------- */
22
23
 
@@ -54,7 +55,7 @@ ca_struct_read_primitive_fast (const char *p, int type_code)
54
55
  case CA_BOOLEAN: {
55
56
  int8_t v;
56
57
  memcpy(&v, p, 1);
57
- return v ? INT2FIX(1) : INT2FIX(0);
58
+ return v ? Qtrue : Qfalse;
58
59
  }
59
60
  case CA_INT8: {
60
61
  int8_t v;
@@ -116,7 +117,15 @@ ca_struct_write_primitive_fast (char *p, int type_code, VALUE val)
116
117
  {
117
118
  switch (type_code) {
118
119
  case CA_BOOLEAN: {
119
- int8_t v = RTEST(val) && val != INT2FIX(0) ? 1 : 0;
120
+ /* The values a boolean array takes: true / false / 1 / 0. */
121
+ int8_t v;
122
+ if ( val == Qtrue || val == INT2FIX(1) ) v = 1;
123
+ else if ( val == Qfalse || val == INT2FIX(0) ) v = 0;
124
+ else {
125
+ rb_raise(rb_eCADataTypeError,
126
+ "can't cast object '%"PRIsVALUE"' to <boolean>",
127
+ rb_inspect(val));
128
+ }
120
129
  memcpy(p, &v, 1);
121
130
  return;
122
131
  }
@@ -201,8 +210,10 @@ ca_struct_read_bitfield_fast (const char *p, int view_bytes,
201
210
  uint64_t mask = (bits >= 64) ? ~(uint64_t)0
202
211
  : ((uint64_t)1 << bits) - 1;
203
212
  uint64_t val = (word >> bit_in_word) & mask;
204
- /* Returned as an unsigned Integer; bit-fields don't carry a sign
213
+ /* A 1-bit field is a boolean, as a record array's column of it is.
214
+ Wider fields are unsigned Integers; bit-fields don't carry a sign
205
215
  bit in carray's CABitfield convention either. */
216
+ if (bits == 1) return val ? Qtrue : Qfalse;
206
217
  if (bits <= 31) return INT2FIX((long) val);
207
218
  if (bits <= 32) return ULONG2NUM((unsigned long) val);
208
219
  return ULL2NUM(val);
@@ -212,7 +223,19 @@ static void
212
223
  ca_struct_write_bitfield_fast (char *p, int view_bytes,
213
224
  int bit_in_word, int bits, VALUE val)
214
225
  {
215
- uint64_t v = NUM2ULL(val);
226
+ uint64_t v;
227
+ if ( bits == 1 ) {
228
+ if ( val == Qtrue || val == INT2FIX(1) ) v = 1;
229
+ else if ( val == Qfalse || val == INT2FIX(0) ) v = 0;
230
+ else {
231
+ rb_raise(rb_eCADataTypeError,
232
+ "can't cast object '%"PRIsVALUE"' to <boolean>",
233
+ rb_inspect(val));
234
+ }
235
+ }
236
+ else {
237
+ v = NUM2ULL(val);
238
+ }
216
239
  uint64_t mask = (bits >= 64) ? ~(uint64_t)0
217
240
  : ((uint64_t)1 << bits) - 1;
218
241
  v &= mask;
@@ -384,6 +407,45 @@ ca_struct_resolve_name (VALUE klass, VALUE name)
384
407
  return rb_funcall(name, rb_intern("to_s"), 0);
385
408
  }
386
409
 
410
+ /* The member an element's `[]` / `[]=` names: a String naming a member,
411
+ or an Integer position. A Symbol is not a member name here -- the
412
+ member is s["name"] or the accessor s.name -- and a name that is not a
413
+ member raises rather than calling a method of that name. */
414
+ static VALUE
415
+ ca_struct_member_name (VALUE klass, VALUE name)
416
+ {
417
+ VALUE name_s;
418
+ if ( SYMBOL_P(name) ) {
419
+ VALUE str = rb_sym2str(name);
420
+ if ( ! NIL_P(rb_hash_aref(rb_const_get(klass, rb_intern("MEMBER_TABLE")),
421
+ str)) ) {
422
+ rb_raise(rb_eTypeError,
423
+ "a struct member is named by a String (s[\"%"PRIsVALUE"\"]) or "
424
+ "read by its accessor (s.%"PRIsVALUE"), not by a Symbol",
425
+ str, str);
426
+ }
427
+ rb_raise(rb_eTypeError,
428
+ "a struct member is named by a String, not by a Symbol "
429
+ "(%"PRIsVALUE" has no member %"PRIsVALUE" either)",
430
+ klass, rb_inspect(str));
431
+ }
432
+ if ( ! RB_TYPE_P(name, T_STRING) && ! RB_TYPE_P(name, T_FIXNUM) ) {
433
+ rb_raise(rb_eTypeError,
434
+ "a struct member is named by a String or an Integer position "
435
+ "(got %s)", rb_obj_classname(name));
436
+ }
437
+ name_s = ca_struct_resolve_name(klass, name);
438
+ if ( NIL_P(name_s)
439
+ || NIL_P(rb_hash_aref(rb_const_get(klass, rb_intern("MEMBER_TABLE")),
440
+ name_s)) ) {
441
+ rb_raise(rb_eArgError, "%"PRIsVALUE" has no member %"PRIsVALUE
442
+ " (members: %"PRIsVALUE")",
443
+ klass, rb_inspect(name),
444
+ rb_inspect(rb_const_get(klass, rb_intern("MEMBERS"))));
445
+ }
446
+ return name_s;
447
+ }
448
+
387
449
  /* ---------------------------------------------------------------------
388
450
  Shared write-dispatch helper reused by `[]=` and `initialize`.
389
451
  Returns 1 if the member was written, 0 if neither table contained
@@ -446,7 +508,7 @@ static VALUE
446
508
  rb_ca_struct_aref (VALUE self, VALUE name)
447
509
  {
448
510
  VALUE klass = rb_obj_class(self);
449
- VALUE name_s = ca_struct_resolve_name(klass, name);
511
+ VALUE name_s = ca_struct_member_name(klass, name);
450
512
 
451
513
  /* Fast path. FAST_PRIMITIVES may be absent on subclasses built
452
514
  outside Builder — fall through to the dispatch table in that
@@ -454,12 +516,14 @@ rb_ca_struct_aref (VALUE self, VALUE name)
454
516
  if (rb_const_defined(klass, rb_intern("FAST_PRIMITIVES"))) {
455
517
  VALUE fast_table = rb_const_get(klass, rb_intern("FAST_PRIMITIVES"));
456
518
  VALUE entry = rb_hash_aref(fast_table, name_s);
457
- if (!NIL_P(entry)) {
519
+ VALUE data_val = rb_ivar_get(self, rb_intern("@data"));
520
+ CArray *ca;
521
+ TypedData_Get_Struct(data_val, CArray, &carray_data_type, ca);
522
+ /* The fast path reads ca->ptr, which only an entity holds. A nested
523
+ element's @data is a view on its parent's bytes and goes through the
524
+ dispatch procs below, which reach them through field views. */
525
+ if (!NIL_P(entry) && ca_is_entity(ca)) {
458
526
  int kind = NUM2INT(rb_ary_entry(entry, 0));
459
- VALUE data_val = rb_ivar_get(self, rb_intern("@data"));
460
- CArray *ca;
461
- TypedData_Get_Struct(data_val, CArray, &carray_data_type, ca);
462
- /* @data is a CScalar entity, so ca->ptr is always valid. */
463
527
  switch (kind) {
464
528
  case FAST_KIND_PRIMITIVE: {
465
529
  /* [kind, offset, type_code] */
@@ -502,8 +566,9 @@ rb_ca_struct_aref (VALUE self, VALUE name)
502
566
  return rb_funcall(reader, rb_intern("call"), 1, data_val);
503
567
  }
504
568
 
505
- /* Unknown name: fall back to send(name) for computed members. */
506
- return rb_funcall(self, rb_to_id(name), 0);
569
+ rb_raise(rb_eArgError, "%"PRIsVALUE" has no reader for member %"PRIsVALUE,
570
+ klass, name_s);
571
+ return Qnil;
507
572
  }
508
573
 
509
574
  /* ---------------------------------------------------------------------
@@ -514,18 +579,31 @@ static VALUE
514
579
  rb_ca_struct_aset (VALUE self, VALUE name, VALUE val)
515
580
  {
516
581
  VALUE klass = rb_obj_class(self);
517
- VALUE name_s = ca_struct_resolve_name(klass, name);
582
+ VALUE name_s;
583
+
584
+ /* The fast path writes into the record's bytes directly. */
585
+ rb_check_frozen(self);
586
+ name_s = ca_struct_member_name(klass, name);
587
+
588
+ /* An element is one record of values: it has no mask to hold UNDEF. A
589
+ record is masked in its record array (a[i] = UNDEF). */
590
+ if ( val == CA_UNDEF ) {
591
+ rb_raise(rb_eTypeError,
592
+ "a struct element has no masked state; mask the record in its "
593
+ "record array instead");
594
+ }
518
595
 
519
596
  /* Fast path (see rb_ca_struct_aref for the absent-FAST_PRIMITIVES
520
597
  rationale and entry-shape contract). */
521
598
  if (rb_const_defined(klass, rb_intern("FAST_PRIMITIVES"))) {
522
599
  VALUE fast_table = rb_const_get(klass, rb_intern("FAST_PRIMITIVES"));
523
600
  VALUE entry = rb_hash_aref(fast_table, name_s);
524
- if (!NIL_P(entry)) {
601
+ VALUE data_val = rb_ivar_get(self, rb_intern("@data"));
602
+ CArray *ca;
603
+ TypedData_Get_Struct(data_val, CArray, &carray_data_type, ca);
604
+ /* Only an entity @data is written through ca->ptr (see the reader). */
605
+ if (!NIL_P(entry) && ca_is_entity(ca)) {
525
606
  int kind = NUM2INT(rb_ary_entry(entry, 0));
526
- VALUE data_val = rb_ivar_get(self, rb_intern("@data"));
527
- CArray *ca;
528
- TypedData_Get_Struct(data_val, CArray, &carray_data_type, ca);
529
607
  switch (kind) {
530
608
  case FAST_KIND_PRIMITIVE: {
531
609
  ca_size_t offset = NUM2SIZE(rb_ary_entry(entry, 1));
@@ -565,9 +643,9 @@ rb_ca_struct_aset (VALUE self, VALUE name, VALUE val)
565
643
  return val;
566
644
  }
567
645
 
568
- /* Unknown name: fall back to send("name=", val). */
569
- VALUE setter_name = rb_str_plus(rb_obj_as_string(name), rb_str_new_cstr("="));
570
- return rb_funcall(self, rb_intern_str(setter_name), 1, val);
646
+ rb_raise(rb_eArgError, "%"PRIsVALUE" has no writer for member %"PRIsVALUE,
647
+ klass, name_s);
648
+ return Qnil;
571
649
  }
572
650
 
573
651
  /* ---------------------------------------------------------------------
data/ext/carray_test.c CHANGED
@@ -10,6 +10,8 @@
10
10
  ---------------------------------------------------------------------------- */
11
11
 
12
12
  #include "carray.h"
13
+ #include "carray_internal.h" /* ca_attach_window */
14
+ #include "ca_obj_face.h" /* ca_face_equal */
13
15
 
14
16
  static ID id_equal;
15
17
  static ID id_eql;
@@ -26,13 +28,34 @@ ca_check_type (void *ap, int8_t data_type)
26
28
  }
27
29
  }
28
30
 
31
+ /* For `initialize` and `initialize_copy`: raises unless the struct is as
32
+ the allocator left it. Setting up a struct that is already set up
33
+ would overwrite what it owns (its buffer, its shape arrays, its parent
34
+ list) without freeing it.
35
+
36
+ What is tested is dim: NULL means the struct owns nothing yet. That
37
+ holds on three conditions, which a new array class has to keep:
38
+ - its allocator zero-fills the struct (TypedData_Make_Struct does);
39
+ - its setup gives the struct a dim (there is no array without one);
40
+ - its `initialize` runs the argument checks before it takes the pool
41
+ or anything else (ca_array_pool_alloc sets dim), so that a rejected
42
+ argument leaves dim NULL and the object can be initialized again. */
43
+ void
44
+ ca_check_uninitialized (void *ap)
45
+ {
46
+ CArray *ca = (CArray *) ap;
47
+ if ( ca->dim != NULL ) {
48
+ rb_raise(rb_eTypeError, "already initialized array");
49
+ }
50
+ }
51
+
29
52
  void
30
53
  ca_check_ndim (void *ap, int ndim)
31
54
  {
32
55
  CArray *ca = (CArray *) ap;
33
56
  if ( ! ca_is_scalar(ca) ) {
34
57
  if ( ca->ndim != ndim ) {
35
- rb_raise(rb_eRuntimeError, "ndim mismatch");
58
+ rb_raise(rb_eArgError, "ndim mismatch");
36
59
  }
37
60
  }
38
61
  }
@@ -44,11 +67,11 @@ ca_check_shape (void *ap, int ndim, ca_size_t *dim)
44
67
  int i;
45
68
  if ( ! ca_is_scalar(ca) ) {
46
69
  if ( ca->ndim != ndim ) {
47
- rb_raise(rb_eRuntimeError, "shape mismatch");
70
+ rb_raise(rb_eArgError, "shape mismatch");
48
71
  }
49
72
  for (i=0; i<ndim; i++) {
50
73
  if ( ca->dim[i] != dim[i] ) {
51
- rb_raise(rb_eRuntimeError, "shape mismatch");
74
+ rb_raise(rb_eArgError, "shape mismatch");
52
75
  }
53
76
  }
54
77
  }
@@ -70,7 +93,7 @@ ca_check_same_ndim (void *ap1, void *ap2)
70
93
  CArray *ca1 = (CArray *) ap1;
71
94
  CArray *ca2 = (CArray *) ap2;
72
95
  if ( ca1->ndim != ca2->ndim ) {
73
- rb_raise(rb_eRuntimeError, "ndim mismatch");
96
+ rb_raise(rb_eArgError, "ndim mismatch");
74
97
  }
75
98
  }
76
99
 
@@ -80,7 +103,7 @@ ca_check_same_elements (void *ap1, void *ap2)
80
103
  CArray *ca1 = (CArray *) ap1;
81
104
  CArray *ca2 = (CArray *) ap2;
82
105
  if ( ca1->elements != ca2->elements ) {
83
- rb_raise(rb_eRuntimeError, "elements mismatch");
106
+ rb_raise(rb_eArgError, "elements mismatch");
84
107
  }
85
108
  }
86
109
 
@@ -92,11 +115,11 @@ ca_check_same_shape (void *ap1, void *ap2)
92
115
  int i;
93
116
  if ( ( ! ca_is_scalar(ca1) ) && ( ! ca_is_scalar(ca2) ) ) {
94
117
  if ( ca1->ndim != ca2->ndim ) {
95
- rb_raise(rb_eRuntimeError, "shape mismatch");
118
+ rb_raise(rb_eArgError, "shape mismatch");
96
119
  }
97
120
  for (i=0; i<ca1->ndim; i++) {
98
121
  if ( ca1->dim[i] != ca2->dim[i] ) {
99
- rb_raise(rb_eRuntimeError, "shape mismatch");
122
+ rb_raise(rb_eArgError, "shape mismatch");
100
123
  }
101
124
  }
102
125
  }
@@ -109,7 +132,7 @@ ca_check_index (void *ap, ca_size_t *idx)
109
132
  int i;
110
133
  for (i=0; i<ca->ndim; i++) {
111
134
  if ( idx[i] < 0 || idx[i] >= ca->dim[i] ) {
112
- rb_raise(rb_eRuntimeError, "invalid index");
135
+ rb_raise(rb_eIndexError, "invalid index");
113
136
  }
114
137
  }
115
138
  }
@@ -118,7 +141,7 @@ void
118
141
  rb_check_carray_object (VALUE arg)
119
142
  {
120
143
  if ( ! rb_obj_is_carray(arg) ) {
121
- rb_raise(rb_eRuntimeError, "CArray required");
144
+ rb_raise(rb_eTypeError, "CArray required");
122
145
  }
123
146
  }
124
147
 
@@ -166,7 +189,7 @@ rb_ca_is_type (VALUE arg, int type)
166
189
  {
167
190
  CArray *ca;
168
191
  if ( ! rb_obj_is_carray(arg) ) {
169
- rb_raise(rb_eRuntimeError, "CArray required");
192
+ rb_raise(rb_eTypeError, "CArray required");
170
193
  }
171
194
  TypedData_Get_Struct(arg, CArray, &carray_data_type, ca);
172
195
  return ca->data_type == type;
@@ -296,25 +319,20 @@ ca_eql[CA_NTYPE] = {
296
319
  eql_VALUE,
297
320
  };
298
321
 
299
- /* ca_compare_common(a, b, strict) — shared body of ca_equal (==) and
300
- * ca_eql_strict (eql?). Returns 1 if arrays match under the selected
301
- * semantics, else 0.
302
- *
303
- * Common structure: metadata pre-check (scalar / data_type / bytes /
304
- * ndim / elements / dim), mask attach via ca_attach_n (recurses into
305
- * mask), unified loop over the four masked/unmasked combinations.
306
- *
307
- * The only difference is the per-element comparator:
308
- * == : ca_eql[data_type] (type-specific, so float NaN != NaN)
309
- * eql?: memcmp for numeric, rb_funcall(:eql?) for CA_OBJECT
310
- *
311
- * The unmasked-unmasked branch takes a bulk memcmp or per-cell
312
- * shortcut to amortise per-element function pointer dispatch. */
313
- static int
314
- ca_compare_common (void *ap, void *bp, int strict)
322
+ typedef struct {
323
+ CArray *ca;
324
+ CArray *cb;
325
+ int strict;
326
+ int flag;
327
+ } ca_compare_ctx_t;
328
+
329
+ static VALUE
330
+ ca_compare_body (VALUE arg)
315
331
  {
316
- CArray *ca = (CArray *) ap;
317
- CArray *cb = (CArray *) bp;
332
+ ca_compare_ctx_t *c = (ca_compare_ctx_t *) arg;
333
+ CArray *ca = c->ca;
334
+ CArray *cb = c->cb;
335
+ int strict = c->strict;
318
336
  int flag = 1;
319
337
  int masked_a, masked_b;
320
338
  boolean8_t *ma, *mb;
@@ -325,28 +343,6 @@ ca_compare_common (void *ap, void *bp, int strict)
325
343
  ca_eql_func eql = NULL; /* used only when !strict */
326
344
  int is_object;
327
345
 
328
- if ( ca_is_scalar(ca) ^ ca_is_scalar(cb) ) {
329
- return 0;
330
- }
331
- if ( ca->data_type != cb->data_type ) {
332
- return 0;
333
- }
334
- if ( ca->bytes != cb->bytes ) {
335
- return 0;
336
- }
337
- if ( ca->ndim != cb->ndim ) {
338
- return 0;
339
- }
340
- if ( ca->elements != cb->elements ) {
341
- return 0;
342
- }
343
- for (i=0; i<ca->ndim; i++) {
344
- if ( ca->dim[i] != cb->dim[i] ) {
345
- return 0;
346
- }
347
- }
348
-
349
- ca_attach_n(2, ca, cb); /* recurses into ca->mask / cb->mask */
350
346
 
351
347
  masked_a = ca_is_any_masked(ca);
352
348
  masked_b = ca_is_any_masked(cb);
@@ -441,9 +437,66 @@ ca_compare_common (void *ap, void *bp, int strict)
441
437
  }
442
438
  }
443
439
 
444
- ca_detach_n(2, ca, cb);
440
+ c->flag = flag;
441
+ return Qnil;
442
+ }
443
+
444
+ /* ca_compare_common(a, b, strict) — shared body of ca_equal (==) and
445
+ * ca_eql_strict (eql?). Returns 1 if arrays match under the selected
446
+ * semantics, else 0.
447
+ *
448
+ * Common structure: metadata pre-check (scalar / data_type / bytes /
449
+ * ndim / elements / dim), then ca_compare_body in a window over both
450
+ * arrays (attaching an array attaches its mask), a unified loop over the
451
+ * four masked/unmasked combinations.
452
+ *
453
+ * The only difference is the per-element comparator:
454
+ * == : ca_eql[data_type] (type-specific, so float NaN != NaN)
455
+ * eql?: memcmp for numeric, rb_funcall(:eql?) for CA_OBJECT
456
+ *
457
+ * The unmasked-unmasked branch takes a bulk memcmp or per-cell
458
+ * shortcut to amortise per-element function pointer dispatch. */
459
+ static int
460
+ ca_compare_common (void *ap, void *bp, int strict)
461
+ {
462
+ CArray *ca = (CArray *) ap;
463
+ CArray *cb = (CArray *) bp;
464
+ int8_t i;
465
+
466
+ if ( ca_is_scalar(ca) ^ ca_is_scalar(cb) ) {
467
+ return 0;
468
+ }
469
+ if ( ca->data_type != cb->data_type ) {
470
+ return 0;
471
+ }
472
+ if ( ca->bytes != cb->bytes ) {
473
+ return 0;
474
+ }
475
+ if ( ca->ndim != cb->ndim ) {
476
+ return 0;
477
+ }
478
+ if ( ca->elements != cb->elements ) {
479
+ return 0;
480
+ }
481
+ for (i=0; i<ca->ndim; i++) {
482
+ if ( ca->dim[i] != cb->dim[i] ) {
483
+ return 0;
484
+ }
485
+ }
445
486
 
446
- return flag;
487
+ {
488
+ ca_compare_ctx_t c;
489
+ CArray *list[2];
490
+ c.ca = ca;
491
+ c.cb = cb;
492
+ c.strict = strict;
493
+ c.flag = 1;
494
+ list[0] = ca;
495
+ list[1] = cb;
496
+ /* The object lane calls #== / #eql? with both arrays attached. */
497
+ ca_attach_window(2, list, "rr", ca_compare_body, (VALUE) &c);
498
+ return c.flag;
499
+ }
447
500
  }
448
501
 
449
502
  int
@@ -452,8 +505,8 @@ ca_equal (void *ap, void *bp)
452
505
  return ca_compare_common(ap, bp, 0);
453
506
  }
454
507
 
455
- static int
456
- ca_eql_strict (void *ap, void *bp)
508
+ int
509
+ ca_eql_storage (void *ap, void *bp)
457
510
  {
458
511
  return ca_compare_common(ap, bp, 1);
459
512
  }
@@ -487,6 +540,10 @@ rb_ca_equal (VALUE self, VALUE other)
487
540
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
488
541
  TypedData_Get_Struct(other, CArray, &carray_data_type, cb);
489
542
 
543
+ if ( ! rb_ca_has_data_class(self) && ( ca_is_face(ca) || ca_is_face(cb) ) ) {
544
+ return ca_face_equal(self, other, 0) ? Qtrue : Qfalse;
545
+ }
546
+
490
547
  return ( ca_equal(ca, cb) ) ? Qtrue : Qfalse;
491
548
  }
492
549
 
@@ -519,13 +576,20 @@ rb_ca_eql (VALUE self, VALUE other)
519
576
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
520
577
  TypedData_Get_Struct(other, CArray, &carray_data_type, cb);
521
578
 
522
- return ( ca_eql_strict(ca, cb) ) ? Qtrue : Qfalse;
579
+ if ( ! rb_ca_has_data_class(self) && ( ca_is_face(ca) || ca_is_face(cb) ) ) {
580
+ return ca_face_equal(self, other, 1) ? Qtrue : Qfalse;
581
+ }
582
+
583
+ return ( ca_eql_storage(ca, cb) ) ? Qtrue : Qfalse;
523
584
  }
524
585
 
525
586
  /* ca_hash(ca) — cheap Hash key for CArray. Mixes metadata (data_type,
526
587
  * ndim, bytes, elements, scalar-ness, shape, mask presence) and, for
527
- * unmasked arrays only, samples the leading CA_HASH_SAMPLE_BYTES of
528
- * data. Masked arrays skip the data sample entirely — otherwise
588
+ * unmasked arrays only, samples the leading cells (CA_HASH_SAMPLE_BYTES
589
+ * of data, at least one cell), read by address so a view is not
590
+ * materialised for them. An object array samples the #hash of those
591
+ * cells, matching eql?, which compares them with #eql?. Masked arrays
592
+ * skip the data sample entirely — otherwise
529
593
  * masked positions would have to be zeroed in the sample to preserve
530
594
  * the eql?→hash invariant, and masked CArrays are rare as Hash keys.
531
595
  * The collision rate cost buys no ca->mask->ptr access and no
@@ -557,9 +621,36 @@ ca_hash (CArray *ca)
557
621
  h ^= rb_memhash(&masked_flag, sizeof(masked_flag));
558
622
 
559
623
  if ( ! masked_flag && sample > 0 ) {
560
- ca_attach(ca);
561
- h ^= rb_memhash(ca->ptr, sample);
562
- ca_detach(ca);
624
+ ca_size_t addrs[CA_HASH_SAMPLE_BYTES];
625
+ ca_size_t ncell = sample / ca->bytes;
626
+ ca_size_t i;
627
+ if ( ncell < 1 ) {
628
+ ncell = 1;
629
+ }
630
+ if ( ncell > ca->elements ) {
631
+ ncell = ca->elements;
632
+ }
633
+ for (i = 0; i < ncell; i++) {
634
+ addrs[i] = i;
635
+ }
636
+ if ( ca_is_object_type(ca) ) {
637
+ /* eql? compares object cells with #eql?, so the sample is their
638
+ #hash, not the VALUE bits. The cells sit on the stack, where the
639
+ GC sees them while #hash runs. */
640
+ VALUE cells[CA_HASH_SAMPLE_BYTES / sizeof(VALUE)];
641
+ ca_xfer_addrs(ca, ncell, addrs, cells, CA_XFER_GET);
642
+ for (i = 0; i < ncell; i++) {
643
+ h = rb_hash_uint(h, (st_index_t) NUM2LONG(rb_hash(cells[i])));
644
+ }
645
+ RB_GC_GUARD(cells[0]);
646
+ }
647
+ else {
648
+ volatile VALUE holder;
649
+ char *buf = ALLOCV_N(char, holder, ncell * ca->bytes);
650
+ ca_xfer_addrs(ca, ncell, addrs, buf, CA_XFER_GET);
651
+ h ^= rb_memhash(buf, ncell * ca->bytes);
652
+ ALLOCV_END(holder);
653
+ }
563
654
  }
564
655
 
565
656
  return h;
@@ -642,6 +733,9 @@ without_read_only_ensure (VALUE arg)
642
733
  struct without_read_only_state *s = (struct without_read_only_state *) arg;
643
734
  if (s->was_readonly) {
644
735
  ca_set_flag(s->ca, CA_FLAG_READ_ONLY);
736
+ if (s->ca->mask) { /* the block may have replaced it */
737
+ ca_set_flag(s->ca->mask, CA_FLAG_READ_ONLY);
738
+ }
645
739
  }
646
740
  return Qnil;
647
741
  }
@@ -679,10 +773,16 @@ without_read_only_ensure (VALUE arg)
679
773
  * immutable (producer's contract) but the mask slot is CArray-side metadata
680
774
  * the bridge owns.
681
775
  *
682
- * Mechanics: clears CA_FLAG_READ_ONLY on self for the duration of the block;
683
- * rb_ensure restores the flag on normal return AND on raise. Frozen objects
684
- * still raise FrozenError. Self only: does not walk parent chains, does not
685
- * touch ca->mask's flag.
776
+ * Mechanics: clears CA_FLAG_READ_ONLY on self and on its mask for the
777
+ * duration of the block; rb_ensure restores both on normal return AND on
778
+ * raise, setting the flag on whatever mask self has by then. (A read-only
779
+ * array's mask carries the flag once CArray#mask has handed it out.) Frozen
780
+ * objects still raise FrozenError. Does not walk parent chains.
781
+ *
782
+ * The lift covers every write, not only the mask: a value write inside the
783
+ * block reaches the producer's buffer, which case (a) declares immutable (a
784
+ * frozen CArray wrapped through MemoryView changes). Keeping the block to
785
+ * the mask is the caller's part, like the choice among (a) / (b) / (c).
686
786
  *
687
787
  * Caller discipline (not enforced by the primitive): confine usage to
688
788
  * entities (CArray / CScalar / CAWrap). On a view of a read-only parent,
@@ -701,6 +801,9 @@ rb_ca_without_read_only_flag (VALUE self)
701
801
  s.was_readonly = ca_test_flag(ca, CA_FLAG_READ_ONLY);
702
802
  if (s.was_readonly) {
703
803
  ca_unset_flag(ca, CA_FLAG_READ_ONLY);
804
+ if (ca->mask) {
805
+ ca_unset_flag(ca->mask, CA_FLAG_READ_ONLY);
806
+ }
704
807
  }
705
808
  return rb_ensure(without_read_only_body, Qnil,
706
809
  without_read_only_ensure, (VALUE) &s);