carray 3.0.0 → 3.0.2

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 (202) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -1
  3. data/CHANGELOG.md +845 -3
  4. data/{NEWS.md → CHANGELOG.v1.md} +1 -1
  5. data/README.md +33 -33
  6. data/carray.gemspec +12 -8
  7. data/ext/ca_axis_dispatch.c +33 -4
  8. data/ext/ca_axis_group.c +256 -114
  9. data/ext/ca_binop_dispatch.c +5 -6
  10. data/ext/ca_binop_dispatch.h +0 -7
  11. data/ext/ca_categorical_iterator.c +121 -67
  12. data/ext/ca_for_each_element.h +6 -8
  13. data/ext/ca_group_iter.c +1 -1
  14. data/ext/ca_kernel_iterator.c +386 -74
  15. data/ext/ca_kernel_iterator.h +145 -45
  16. data/ext/ca_obj_array.c +62 -20
  17. data/ext/ca_obj_bincmp.c +57 -27
  18. data/ext/ca_obj_binop.c +86 -30
  19. data/ext/ca_obj_block.c +4 -4
  20. data/ext/ca_obj_const_string.c +88 -27
  21. data/ext/ca_obj_face.c +104 -0
  22. data/ext/ca_obj_face.h +48 -1
  23. data/ext/ca_obj_fake.c +11 -0
  24. data/ext/ca_obj_fixlen_string.c +21 -6
  25. data/ext/ca_obj_grid.c +11 -3
  26. data/ext/ca_obj_meld.c +125 -22
  27. data/ext/ca_obj_moncmp.c +33 -14
  28. data/ext/ca_obj_monop.c +19 -7
  29. data/ext/ca_obj_object.c +107 -4
  30. data/ext/ca_obj_record.c +3 -1
  31. data/ext/ca_obj_refer.c +30 -6
  32. data/ext/ca_obj_roll.c +13 -5
  33. data/ext/ca_obj_select.c +49 -34
  34. data/ext/ca_obj_select_axis.c +16 -6
  35. data/ext/ca_obj_shift.c +3 -3
  36. data/ext/ca_obj_stack.c +3 -8
  37. data/ext/ca_obj_stride.c +178 -24
  38. data/ext/ca_obj_string.c +11 -5
  39. data/ext/ca_obj_tile.c +13 -5
  40. data/ext/ca_obj_time.c +3 -1
  41. data/ext/ca_obj_timedelta.c +3 -1
  42. data/ext/ca_obj_transpose.c +1 -1
  43. data/ext/ca_obj_triop.c +62 -23
  44. data/ext/ca_obj_window.c +94 -19
  45. data/ext/ca_op_cmplx64.h +123 -0
  46. data/ext/ca_op_ipower.c +1 -5
  47. data/ext/ca_rng_normal.h +42 -0
  48. data/ext/ca_rng_xoshiro256pp.h +105 -0
  49. data/ext/ca_sort_kernels.h +5 -5
  50. data/ext/ca_sweep_engine.c +362 -155
  51. data/ext/ca_sweep_engine.h +39 -9
  52. data/ext/ca_transform_common.c +7 -0
  53. data/ext/carray.h +148 -132
  54. data/ext/carray_access.c +83 -49
  55. data/ext/carray_address_basis.c +590 -0
  56. data/ext/carray_bincount.c +8 -8
  57. data/ext/carray_broadcast.c +102 -9
  58. data/ext/carray_build_flags.h +3 -0
  59. data/ext/carray_call_cfunc.c +2010 -335
  60. data/ext/carray_call_cfunc.h +153 -0
  61. data/ext/carray_cast.c +184 -72
  62. data/ext/carray_conversion.c +22 -22
  63. data/ext/carray_copy.c +67 -32
  64. data/ext/carray_core.c +140 -13
  65. data/ext/carray_count.c +10 -11
  66. data/ext/carray_element.c +7 -7
  67. data/ext/carray_factorize.c +72 -51
  68. data/ext/carray_hold.c +1 -1
  69. data/ext/carray_index_classifier.c +7 -21
  70. data/ext/carray_internal.h +36 -3
  71. data/ext/carray_kernels_bincmp.c +1 -0
  72. data/ext/carray_kernels_binop.c +320 -298
  73. data/ext/carray_kernels_init.c +1095 -0
  74. data/ext/carray_kernels_map.c +1 -0
  75. data/ext/carray_kernels_moncmp.c +1 -0
  76. data/ext/carray_kernels_monop.c +412 -411
  77. data/ext/carray_kernels_reduce_aggregate.c +256 -87
  78. data/ext/carray_kernels_reduce_boolean.c +1 -0
  79. data/ext/carray_kernels_reduce_cumulative.c +324 -54
  80. data/ext/carray_kernels_reduce_extreme.c +605 -58
  81. data/ext/carray_kernels_reduce_variance.c +1 -0
  82. data/ext/carray_kernels_scan.c +5 -4
  83. data/ext/carray_kernels_search.c +187 -106
  84. data/ext/carray_kernels_sort.c +1 -0
  85. data/ext/carray_kernels_triop.c +1 -0
  86. data/ext/carray_lazy.c +175 -5
  87. data/ext/carray_loop.c +7 -1
  88. data/ext/carray_mask.c +29 -13
  89. data/ext/carray_median_percentile.c +56 -1
  90. data/ext/carray_memory_view.c +46 -21
  91. data/ext/carray_operator.c +50 -53
  92. data/ext/carray_order.c +1 -1
  93. data/ext/carray_partition.c +4 -4
  94. data/ext/carray_random.c +391 -47
  95. data/ext/carray_scatter.c +1 -1
  96. data/ext/carray_slab.c +13 -0
  97. data/ext/carray_sort.c +22 -24
  98. data/ext/carray_sort_kernel.c +10 -10
  99. data/ext/carray_test.c +1 -1
  100. data/ext/extconf.rb +18 -0
  101. data/ext/mk_call_cfunc.rb +323 -145
  102. data/ext/mkkernel.rb +651 -129
  103. data/ext/ruby_carray.c +28 -17
  104. data/ext/version.h +4 -4
  105. data/lib/carray/autoload_carray.rb +24 -3
  106. data/lib/carray/autoload_method_extension.rb +14 -1
  107. data/lib/carray/axis_group.rb +84 -7
  108. data/lib/carray/basics.rb +63 -59
  109. data/lib/carray/bincount_nd.rb +12 -12
  110. data/lib/carray/block_iterator.rb +111 -25
  111. data/lib/carray/categorical.rb +154 -37
  112. data/lib/carray/categorical_iterator.rb +300 -151
  113. data/lib/carray/conditional.rb +14 -14
  114. data/lib/carray/const_string.rb +131 -27
  115. data/lib/carray/construct.rb +61 -0
  116. data/lib/carray/core_extensions.rb +62 -5
  117. data/lib/carray/data_type_extension.rb +21 -1
  118. data/lib/carray/data_type_limits.rb +91 -0
  119. data/lib/carray/fixlen_string.rb +1 -1
  120. data/lib/carray/frame/concat.rb +3 -3
  121. data/lib/carray/frame/convert.rb +1 -1
  122. data/lib/carray/frame/csv_parser.rb +11 -4
  123. data/lib/carray/frame/frame.rb +85 -21
  124. data/lib/carray/frame/group.rb +36 -3
  125. data/lib/carray/frame/io.rb +224 -15
  126. data/lib/carray/frame/records.rb +18 -4
  127. data/lib/carray/frame/sort.rb +1 -1
  128. data/lib/carray/frame/verbs.rb +32 -12
  129. data/lib/carray/fuse_source.rb +123 -0
  130. data/lib/carray/fusion.rb +218 -0
  131. data/lib/carray/histogram.rb +16 -16
  132. data/lib/carray/inspect.rb +43 -16
  133. data/lib/carray/iterator.rb +147 -3
  134. data/lib/carray/lazy.rb +88 -73
  135. data/lib/carray/mask_gap_fill.rb +3 -1
  136. data/lib/carray/meld_reduce.rb +2 -2
  137. data/lib/carray/methods/align_addr.rb +1 -1
  138. data/lib/carray/methods/composition.rb +1 -1
  139. data/lib/carray/methods/discovery_along.rb +74 -0
  140. data/lib/carray/methods/factorize.rb +50 -0
  141. data/lib/carray/methods/is_in.rb +25 -14
  142. data/lib/carray/methods/locate_addr.rb +81 -3
  143. data/lib/carray/methods/mask_duplicates.rb +36 -2
  144. data/lib/carray/methods/meshgrid.rb +4 -5
  145. data/lib/carray/methods/mode.rb +2 -2
  146. data/lib/carray/methods/nunique.rb +23 -2
  147. data/lib/carray/methods/repeat.rb +110 -0
  148. data/lib/carray/methods/snap.rb +7 -2
  149. data/lib/carray/methods/unique.rb +44 -4
  150. data/lib/carray/methods/value_counts.rb +2 -2
  151. data/lib/carray/rng.rb +86 -0
  152. data/lib/carray/runtime.rb +0 -19
  153. data/lib/carray/slab_iterator.rb +77 -19
  154. data/lib/carray/string_operation_extension.rb +10 -6
  155. data/lib/carray/time.rb +906 -491
  156. data/lib/carray/window_iterator.rb +421 -27
  157. data/lib/carray.rb +7 -5
  158. data/yard-stubs/ca_obj_array.rb +385 -0
  159. data/yard-stubs/ca_obj_bitarray.rb +38 -0
  160. data/yard-stubs/ca_obj_bitfield.rb +43 -0
  161. data/yard-stubs/ca_obj_block.rb +68 -0
  162. data/yard-stubs/ca_obj_byte_swap.rb +56 -0
  163. data/yard-stubs/ca_obj_fake.rb +31 -0
  164. data/yard-stubs/ca_obj_farray.rb +32 -0
  165. data/yard-stubs/ca_obj_field.rb +45 -0
  166. data/yard-stubs/ca_obj_grid.rb +35 -0
  167. data/yard-stubs/ca_obj_refer.rb +72 -0
  168. data/yard-stubs/ca_obj_roll.rb +45 -0
  169. data/yard-stubs/ca_obj_shift.rb +43 -0
  170. data/yard-stubs/ca_obj_stride.rb +181 -0
  171. data/yard-stubs/ca_obj_tile.rb +29 -0
  172. data/yard-stubs/ca_obj_transpose.rb +40 -0
  173. data/yard-stubs/ca_obj_window.rb +57 -0
  174. data/yard-stubs/carray_access.rb +131 -0
  175. data/yard-stubs/carray_attribute.rb +246 -0
  176. data/yard-stubs/carray_broadcast.rb +37 -0
  177. data/yard-stubs/carray_cast.rb +489 -0
  178. data/yard-stubs/carray_class.rb +65 -0
  179. data/yard-stubs/carray_conversion.rb +76 -0
  180. data/yard-stubs/carray_copy.rb +79 -0
  181. data/yard-stubs/carray_core.rb +34 -0
  182. data/yard-stubs/carray_count.rb +84 -0
  183. data/yard-stubs/carray_element.rb +108 -0
  184. data/yard-stubs/carray_generate.rb +66 -0
  185. data/yard-stubs/carray_lazy.rb +228 -0
  186. data/yard-stubs/carray_loop.rb +140 -0
  187. data/yard-stubs/carray_mask.rb +259 -0
  188. data/yard-stubs/carray_math.rb +1615 -0
  189. data/yard-stubs/carray_mathfunc.rb +45 -0
  190. data/yard-stubs/carray_median_percentile.rb +103 -0
  191. data/yard-stubs/carray_memory_view.rb +163 -0
  192. data/yard-stubs/carray_order.rb +252 -0
  193. data/yard-stubs/carray_random.rb +89 -0
  194. data/yard-stubs/carray_scatter.rb +106 -0
  195. data/yard-stubs/carray_slab.rb +59 -0
  196. data/yard-stubs/carray_sort.rb +165 -0
  197. data/yard-stubs/carray_test.rb +85 -0
  198. data/yard-stubs/carray_undef.rb +64 -0
  199. data/yard-stubs/carray_utils.rb +97 -0
  200. data/yard-stubs/ruby_carray.rb +193 -0
  201. metadata +67 -10
  202. data/ext/ca_obj_unbound_repeat.c +0 -496
@@ -7,7 +7,7 @@
7
7
  Siblings:
8
8
  carray_copy.c — CArray-to-CArray copy / template family
9
9
  carray_element.c — element-level fetch/store (rb_ca_ptr2obj / obj2ptr)
10
- carray_cast_func.c — dtype cast table used by rb_ca_ptr2obj
10
+ carray_cast_func.c — data type cast table used by rb_ca_ptr2obj
11
11
 
12
12
  ---------------------------------------------------------------------------- */
13
13
 
@@ -16,15 +16,15 @@
16
16
  #include "ruby/io.h"
17
17
 
18
18
  /* to_a dispatch:
19
- numeric / boolean dtype + entity or attach-able view -> fast path
20
- (dtype-direct leaf loop over the attached contig buffer)
19
+ numeric / boolean data type + entity or attach-able view -> fast path
20
+ (data type-direct leaf loop over the attached contig buffer)
21
21
  CA_OBJECT / CA_FIXLEN -> universal
22
22
  (rb_ca_fetch_index per cell; ptr is VALUE or byte string, not
23
23
  directly castable through the leaf switch)
24
24
 
25
25
  Masked cells materialise as CA_UNDEF on both paths. */
26
26
 
27
- /* Leaf macro: dtype-specialized inner loop. Produces `n` VALUE objects
27
+ /* Leaf macro: data type-specialized inner loop. Produces `n` VALUE objects
28
28
  from a contig ptr base + element width, storing into `dst` (Ruby Array). */
29
29
  #define TO_A_LEAF_LOOP(T, TO_VALUE) do { \
30
30
  const T *src = (const T *) base; \
@@ -42,7 +42,7 @@
42
42
 
43
43
  #define TO_A_DISPATCH_LEAF(LOOP) do { \
44
44
  ca_size_t li; \
45
- switch ( dtype ) { \
45
+ switch ( data_type ) { \
46
46
  case CA_FLOAT64: LOOP(double, rb_float_new); break; \
47
47
  case CA_FLOAT32: LOOP(float, rb_float_new); break; \
48
48
  case CA_INT64: LOOP(int64_t, LL2NUM); break; \
@@ -67,13 +67,13 @@
67
67
  } \
68
68
  break; \
69
69
  default: \
70
- rb_bug("to_a fast path: unexpected dtype %d", dtype); \
70
+ rb_bug("to_a fast path: unexpected data type %d", data_type); \
71
71
  } \
72
72
  } while (0)
73
73
 
74
74
  #define TO_A_DISPATCH_LEAF_MASKED(LOOP) do { \
75
75
  ca_size_t li; \
76
- switch ( dtype ) { \
76
+ switch ( data_type ) { \
77
77
  case CA_FLOAT64: LOOP(double, rb_float_new); break; \
78
78
  case CA_FLOAT32: LOOP(float, rb_float_new); break; \
79
79
  case CA_INT64: LOOP(int64_t, LL2NUM); break; \
@@ -95,7 +95,7 @@
95
95
  } \
96
96
  break; \
97
97
  default: \
98
- rb_bug("to_a fast path masked: unexpected dtype %d", dtype); \
98
+ rb_bug("to_a fast path masked: unexpected data type %d", data_type); \
99
99
  } \
100
100
  } while (0)
101
101
 
@@ -105,7 +105,7 @@
105
105
  static void
106
106
  to_a_fast_recurse (CArray *ca, int32_t level, const char *base,
107
107
  const boolean8_t *mask_base, VALUE dst,
108
- int dtype, ca_size_t bytes, int has_mask)
108
+ int data_type, ca_size_t bytes, int has_mask)
109
109
  {
110
110
  ca_size_t i, n, slab_elements, slab_bytes;
111
111
 
@@ -135,7 +135,7 @@ to_a_fast_recurse (CArray *ca, int32_t level, const char *base,
135
135
  to_a_fast_recurse(ca, level + 1,
136
136
  base + i * slab_bytes,
137
137
  has_mask ? mask_base + i * slab_elements : NULL,
138
- child, dtype, bytes, has_mask);
138
+ child, data_type, bytes, has_mask);
139
139
  }
140
140
  }
141
141
 
@@ -172,8 +172,8 @@ rb_ca_to_a_loop_universal (VALUE self, int32_t level, ca_size_t *idx, VALUE ary)
172
172
  * ndim-1 nesting: shape [2, 3] -> 2-element Array of 3-element Arrays.
173
173
  * Masked cells become CA_UNDEF; the mask itself is not preserved on the
174
174
  * result. ca_attach(self) supplies a contig row-major buffer for the
175
- * fast path, so all view kinds land in the dtype-direct leaf loop when
176
- * the dtype is numeric or boolean; CA_OBJECT / CA_FIXLEN fall through
175
+ * fast path, so all view kinds land in the data type-direct leaf loop when
176
+ * the data type is numeric or boolean; CA_OBJECT / CA_FIXLEN fall through
177
177
  * to the universal fetch_index walk. */
178
178
  VALUE
179
179
  rb_ca_to_a (VALUE self)
@@ -181,7 +181,7 @@ rb_ca_to_a (VALUE self)
181
181
  volatile VALUE ary;
182
182
  CArray *ca;
183
183
  ca_size_t idx[CA_RANK_MAX];
184
- int dtype, fast_eligible;
184
+ int data_type, fast_eligible;
185
185
 
186
186
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
187
187
  ary = rb_ca_is_empty(self) ? rb_ary_new() : rb_ary_new2(ca->dim[0]);
@@ -199,22 +199,22 @@ rb_ca_to_a (VALUE self)
199
199
  numeric-storage Face -- CATimedelta serials instead of CATimedelta
200
200
  Elements -- while ca[i] / each / to_type(:object) all decode. The
201
201
  universal loop goes through rb_ca_fetch_addr, which decodes. */
202
- dtype = ca->data_type;
202
+ data_type = ca->data_type;
203
203
  fast_eligible = (ca->elements > 0)
204
204
  && ( ! ca_is_face(ca) )
205
- && (dtype == CA_FLOAT64 || dtype == CA_FLOAT32
206
- || dtype == CA_INT64 || dtype == CA_INT32
207
- || dtype == CA_INT16 || dtype == CA_INT8
208
- || dtype == CA_UINT64 || dtype == CA_UINT32
209
- || dtype == CA_UINT16 || dtype == CA_UINT8
210
- || dtype == CA_BOOLEAN);
205
+ && (data_type == CA_FLOAT64 || data_type == CA_FLOAT32
206
+ || data_type == CA_INT64 || data_type == CA_INT32
207
+ || data_type == CA_INT16 || data_type == CA_INT8
208
+ || data_type == CA_UINT64 || data_type == CA_UINT32
209
+ || data_type == CA_UINT16 || data_type == CA_UINT8
210
+ || data_type == CA_BOOLEAN);
211
211
 
212
212
  if ( fast_eligible ) {
213
213
  int has_mask = (ca->mask != NULL);
214
214
  const boolean8_t *mask_base = has_mask
215
215
  ? (const boolean8_t *) ca->mask->ptr : NULL;
216
216
  to_a_fast_recurse(ca, 0, ca->ptr, mask_base, ary,
217
- dtype, ca->bytes, has_mask);
217
+ data_type, ca->bytes, has_mask);
218
218
  }
219
219
  else if ( ca->elements > 0 ) {
220
220
  rb_ca_to_a_loop_universal(self, 0, idx, ary);
@@ -229,7 +229,7 @@ rb_ca_to_a (VALUE self)
229
229
  #undef TO_A_DISPATCH_LEAF_MASKED
230
230
 
231
231
  /* CArray#convert(data_type=nil, bytes: nil) { |elem| ... } — map every cell
232
- * through the block, into a new array whose dtype is chosen by argv via
232
+ * through the block, into a new array whose data type is chosen by argv via
233
233
  * CArray#template (shape is inherited from self).
234
234
  *
235
235
  * Masked cells skip the block; if the block returns CA_UNDEF, the
data/ext/carray_copy.c CHANGED
@@ -10,6 +10,8 @@
10
10
 
11
11
  #include "carray.h"
12
12
  #include "ca_obj_face.h" /* CA_FACE_LIFT_IF_FACE */
13
+ #include "carray_internal.h" /* ca_gc_hold_push / ca_gc_hold_pop_to */
14
+ #include "ca_sweep_engine.h" /* ca_sweep_same_shape / ca_sweep_refuse_shapes */
13
15
  #include <stdarg.h>
14
16
 
15
17
  /* ------------------------------------------------------------------- */
@@ -18,22 +20,27 @@
18
20
  * and bytes as `ca`, copies the element payload, and reproduces the
19
21
  * mask state. Always copies even when `ca` is an entity; for view
20
22
  * sources the data path goes through `ca_copy_data`, otherwise a flat
21
- * memcpy of `ca_length(ca)` bytes.
23
+ * memcpy of `ca_length(ca)` bytes. If reading the source raises, the
24
+ * copy is freed before the raise propagates.
22
25
  *
23
26
  * Backs `CArray#copy` and is also reachable as a C-level utility from
24
27
  * other ext files that need an owned snapshot. */
25
- CArray *
26
- ca_copy (void *ap)
28
+ void
29
+ ca_fill_or_free (CArray *co, VALUE (*fill)(VALUE), VALUE arg)
27
30
  {
28
- CArray *ca = (CArray *) ap;
29
- CArray *co;
30
-
31
- if ( ca_is_scalar(ca) ) {
32
- co = (CArray *) cscalar_new(ca->data_type, ca->bytes, 0);
33
- }
34
- else {
35
- co = carray_new(ca->data_type, ca->ndim, ca->dim, ca->bytes, 0);
31
+ int tag = 0;
32
+ rb_protect(fill, arg, &tag);
33
+ if ( tag ) {
34
+ ca_free(co);
35
+ rb_jump_tag(tag);
36
36
  }
37
+ }
38
+
39
+ static VALUE
40
+ ca_copy_fill (VALUE arg)
41
+ {
42
+ CArray **pair = (CArray **) arg;
43
+ CArray *ca = pair[0], *co = pair[1];
37
44
 
38
45
  if ( ca_is_attached(ca) ) {
39
46
  memcpy(co->ptr, ca->ptr, ca_length(ca));
@@ -46,6 +53,26 @@ ca_copy (void *ap)
46
53
  if ( ca->mask ) {
47
54
  ca_copy_mask(co, ca);
48
55
  }
56
+ return Qnil;
57
+ }
58
+
59
+ CArray *
60
+ ca_copy (void *ap)
61
+ {
62
+ CArray *ca = (CArray *) ap;
63
+ CArray *co;
64
+ CArray *pair[2];
65
+
66
+ if ( ca_is_scalar(ca) ) {
67
+ co = (CArray *) cscalar_new(ca->data_type, ca->bytes, 0);
68
+ }
69
+ else {
70
+ co = carray_new(ca->data_type, ca->ndim, ca->dim, ca->bytes, 0);
71
+ }
72
+
73
+ pair[0] = ca;
74
+ pair[1] = co;
75
+ ca_fill_or_free(co, ca_copy_fill, (VALUE) pair);
49
76
 
50
77
  return co;
51
78
  }
@@ -57,9 +84,18 @@ VALUE
57
84
  rb_ca_copy (VALUE self)
58
85
  {
59
86
  volatile VALUE obj;
60
- CArray *ca;
87
+ CArray *ca, *co;
88
+ int guard = -1;
61
89
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
62
- obj = ca_wrap_struct(ca_copy(ca));
90
+ co = ca_copy(ca);
91
+ /* The copy holds VALUEs that so far exist only in co->ptr, and
92
+ ca_wrap_struct allocates. Keep them reachable until the wrapper
93
+ that owns them exists. */
94
+ if ( ca_is_object_type(co) ) {
95
+ guard = ca_gc_hold_push(co->ptr, co->elements);
96
+ }
97
+ obj = ca_wrap_struct(co);
98
+ ca_gc_hold_pop_to(guard);
63
99
  CA_FACE_LIFT_IF_FACE(obj, self, ca);
64
100
  return obj;
65
101
  }
@@ -262,16 +298,16 @@ rb_ca_template_with_type (VALUE self, VALUE rtype, VALUE rbytes)
262
298
  return rb_ca_template_method(2, args, self);
263
299
  }
264
300
 
265
- /* Picks the largest-shape CArray among `n` variadic CArray arguments
266
- * and returns `template(largest)`. Used by `carray_call_cfunc.c` to
267
- * size the output of vectorised scalar C-function calls, where the
268
- * inputs may be a mix of scalars and arrays. */
301
+ /* Returns `template` of the first non-scalar among `n` variadic CArray
302
+ * arguments (of the first argument when all are scalars). Used by
303
+ * `carray_call_cfunc.c` to size the output of vectorised scalar
304
+ * C-function calls: a scalar pairs with any array, two arrays only when
305
+ * their shapes agree. */
269
306
  VALUE
270
307
  rb_ca_template_n (int n, ...)
271
308
  {
272
- volatile VALUE varg, obj;
273
- CArray *ca;
274
- ca_size_t elements = -1;
309
+ volatile VALUE varg, obj = Qnil;
310
+ CArray *ca, *donor = NULL;
275
311
  va_list vargs;
276
312
  int i;
277
313
 
@@ -279,24 +315,23 @@ rb_ca_template_n (int n, ...)
279
315
  for (i=0; i<n; i++) {
280
316
  varg = va_arg(vargs, VALUE);
281
317
  if ( ! rb_obj_is_carray(varg) ) {
318
+ va_end(vargs);
282
319
  rb_raise(rb_eRuntimeError, "[BUG] not-carray object given to rb_ca_template_n");
283
320
  }
284
321
  TypedData_Get_Struct(varg, CArray, &carray_data_type, ca);
285
322
  if ( i == 0 ) {
286
323
  obj = varg;
287
- elements = ca->elements;
288
324
  }
289
- else {
290
- if ( rb_obj_is_cscalar(varg) ) {
291
- continue;
292
- }
293
- if ( rb_obj_is_cscalar(obj) ) {
294
- obj = varg;
295
- elements = ca->elements;
296
- }
297
- else if ( ca->elements != elements ) {
298
- rb_raise(rb_eRuntimeError, "size mismatch");
299
- }
325
+ if ( ca_is_scalar(ca) ) {
326
+ continue;
327
+ }
328
+ if ( ! donor ) {
329
+ donor = ca;
330
+ obj = varg;
331
+ }
332
+ else if ( ! ca_sweep_same_shape(donor, ca) ) {
333
+ va_end(vargs);
334
+ ca_sweep_refuse_shapes(donor, ca);
300
335
  }
301
336
  }
302
337
  va_end(vargs);
data/ext/carray_core.c CHANGED
@@ -231,7 +231,6 @@ ca_cast_table2[CA_NTYPE][CA_NTYPE] = {
231
231
  * CASelect
232
232
  * CAObject
233
233
  * CARepeat
234
- * CAUnboundRepeat
235
234
  */
236
235
 
237
236
  void
@@ -243,7 +242,6 @@ ca_init_obj_type (void)
243
242
  extern ca_operation_function_t ca_select_func;
244
243
  extern ca_operation_function_t ca_object_func;
245
244
  extern ca_operation_function_t ca_stride_func;
246
- extern ca_operation_function_t ca_ubrep_func;
247
245
 
248
246
  /* CArray */
249
247
  ca_func[CA_OBJ_ARRAY] = ca_array_func;
@@ -309,12 +307,6 @@ ca_init_obj_type (void)
309
307
  ca_mask_class[CA_OBJ_REPEAT] = rb_cCARepeatMask;
310
308
  ca_mask_typeddata[CA_OBJ_REPEAT] = &castride_mask_data_type;
311
309
 
312
- /* CAUnboundRepeat */
313
- ca_func[CA_OBJ_UNBOUND_REPEAT] = ca_ubrep_func;
314
- ca_class[CA_OBJ_UNBOUND_REPEAT] = rb_cCAUnboundRepeat;
315
- ca_typeddata[CA_OBJ_UNBOUND_REPEAT] = &caunboundrepeat_data_type;
316
- ca_mask_class[CA_OBJ_UNBOUND_REPEAT] = rb_cCAUnboundRepeatMask;
317
- ca_mask_typeddata[CA_OBJ_UNBOUND_REPEAT] = &caunboundrepeat_mask_data_type;
318
310
 
319
311
  ca_obj_num = 9;
320
312
  }
@@ -432,11 +424,22 @@ ca_attach_is_alias (void *ap)
432
424
  {
433
425
  CArray *ca = (CArray *) ap;
434
426
  extern ca_operation_function_t ca_stride_func;
427
+ extern ca_operation_function_t ca_lazy_marker_func;
435
428
  extern int ca_stride_is_contiguous (CAStride *ca);
436
429
  extern int ca_stride_attach_aliases_root (CAStride *ca);
437
430
 
438
431
  if ( ca == NULL ) return 0;
439
432
  if ( ca_is_entity(ca) ) return 1;
433
+
434
+ /* CALazyMarker's attach is literally `ca->ptr = ca->parent->ptr` after
435
+ attaching the parent — it adds no layout of its own — so it aliases
436
+ exactly when its parent does. Without this a marker looks expensive
437
+ to every caller and views built on it fall onto materialising paths,
438
+ even though there is nothing between the marker and real memory. */
439
+ if ( ca_func[ca->obj_type].attach == ca_lazy_marker_func.attach ) {
440
+ return ca_attach_is_alias(((CAView *) ca)->parent);
441
+ }
442
+
440
443
  /* CAStride family share ca_stride_func.attach (= ca_stride_func_attach).
441
444
  The alias-attach fast path is taken iff composed strides are
442
445
  row-major contiguous. ca_stride_is_contiguous checks the leaf
@@ -1399,6 +1402,30 @@ ca_xfer_strided_walk (char *src_base,
1399
1402
  }
1400
1403
  }
1401
1404
 
1405
+ /* See the comment on the prototype in carray.h. */
1406
+ int
1407
+ ca_xfer_stride_request_is_axis_box (void *ap, ca_size_t *starts,
1408
+ ca_size_t *counts, ca_size_t *strides)
1409
+ {
1410
+ CArray *ca = (CArray *) ap;
1411
+ ca_size_t native[CA_RANK_MAX];
1412
+ ca_size_t s = ca->bytes;
1413
+ int8_t ndim = ca->ndim, k;
1414
+
1415
+ for ( k = ndim - 1; k >= 0; k-- ) { native[k] = s; s *= ca->dim[k]; }
1416
+
1417
+ for ( k = 0; k < ndim; k++ ) {
1418
+ ca_size_t q;
1419
+ if ( counts[k] <= 1 ) continue; /* moves nothing */
1420
+ if ( strides[k] <= 0 ) return 0; /* zero / negative: not an axis walk */
1421
+ if ( strides[k] % native[k] != 0 ) return 0;
1422
+ q = strides[k] / native[k];
1423
+ if ( q < 1 ) return 0;
1424
+ if ( starts[k] + (counts[k] - 1) * q >= ca->dim[k] ) return 0; /* runs off axis k */
1425
+ }
1426
+ return 1;
1427
+ }
1428
+
1402
1429
  static void
1403
1430
  ca_xfer_stride_dispatch (CArray *ca, ca_size_t *starts, ca_size_t *counts,
1404
1431
  ca_size_t *strides, void *data, int dir)
@@ -1532,10 +1559,12 @@ ca_xfer_all_body (VALUE arg)
1532
1559
  return Qnil;
1533
1560
  }
1534
1561
 
1562
+ /* `arg` carries the hold depth to unwind to, so nested ca_xfer_all calls
1563
+ each drop their own hold rather than the whole stack. */
1535
1564
  static VALUE
1536
1565
  ca_xfer_all_ensure (VALUE arg)
1537
1566
  {
1538
- (void) arg;
1567
+ ca_gc_hold_pop_to(NUM2INT(arg));
1539
1568
  ca_lazy_arena_exit();
1540
1569
  return Qnil;
1541
1570
  }
@@ -1545,6 +1574,7 @@ ca_xfer_all (void *ap, void *data, int dir)
1545
1574
  {
1546
1575
  CArray *ca = (CArray *) ap;
1547
1576
  ca_xfer_all_args_t args;
1577
+ int guard = -1;
1548
1578
  if ( ! ca_func[ca->obj_type].xfer_all ) {
1549
1579
  rb_raise(rb_eRuntimeError,
1550
1580
  "[BUG] xfer_all not defined for object type <%i>",
@@ -1562,8 +1592,25 @@ ca_xfer_all (void *ap, void *data, int dir)
1562
1592
  next entry. */
1563
1593
  ca_lazy_arena_enter();
1564
1594
  args.ca = ca; args.data = data; args.dir = dir;
1595
+
1596
+ /* A CA_OBJECT cell is a VALUE, and this buffer belongs to no Ruby
1597
+ object yet -- it is the destination `copy` will hand out, or a
1598
+ view's own freshly allocated one. The object lane calls rb_funcall
1599
+ per cell, so a collection partway through would free what has been
1600
+ written so far. ca_xfer_all is the whole-view entry, so the window
1601
+ is exactly ca->elements contiguous cells: the one place where the
1602
+ extent is known without trusting a caller's strides. Sub-windows
1603
+ written by nested transfers land inside it. */
1604
+ if ( ca->data_type == CA_OBJECT && dir == CA_XFER_GET ) {
1605
+ if ( data != (void *) ca->ptr ) {
1606
+ VALUE *p = (VALUE *) data;
1607
+ ca_size_t i;
1608
+ for ( i = 0; i < ca->elements; i++ ) *p++ = Qnil;
1609
+ }
1610
+ guard = ca_gc_hold_push(data, ca->elements);
1611
+ }
1565
1612
  rb_ensure(ca_xfer_all_body, (VALUE) &args,
1566
- ca_xfer_all_ensure, Qnil);
1613
+ ca_xfer_all_ensure, INT2NUM(guard));
1567
1614
  }
1568
1615
 
1569
1616
  /* ------------------------------------------------------------------- */
@@ -1608,6 +1655,18 @@ ca_allocate (void *ap)
1608
1655
  ca_allocate(ca->mask);
1609
1656
  }
1610
1657
 
1658
+ /* The first attach of a view: materialise through the type's slot, then
1659
+ its mask. Run under rb_protect by ca_attach. */
1660
+ static VALUE
1661
+ ca_attach_view_first (VALUE arg)
1662
+ {
1663
+ CArray *ca = (CArray *) arg;
1664
+ ca_func[ca->obj_type].attach(ca);
1665
+ ca_update_mask(ca);
1666
+ ca_attach(ca->mask);
1667
+ return Qnil;
1668
+ }
1669
+
1611
1670
  /* attach parent's data to ca->ptr */
1612
1671
 
1613
1672
  void
@@ -1621,14 +1680,30 @@ ca_attach (void *ap)
1621
1680
 
1622
1681
  if ( ca_is_view(ca) ) { /* view array */
1623
1682
 
1624
- CAVIEW(ca)->attach += 1; /* increments attach level */
1625
- if ( CAVIEW(ca)->attach > CA_ATTACH_MAX ) {
1683
+ if ( CAVIEW(ca)->attach >= CA_ATTACH_MAX ) {
1626
1684
  rb_raise(rb_eRuntimeError,
1627
1685
  "too large attach count of view array");
1628
1686
  }
1687
+ CAVIEW(ca)->attach += 1; /* increments attach level */
1629
1688
 
1630
1689
  if ( ! ca->ptr ) {
1631
- ca_func[ca->obj_type].attach(ap);
1690
+ /* A slot can raise part way (a conversion meeting a value it cannot
1691
+ hold). Undo the attach then, or the next attach finds ca->ptr
1692
+ set, skips the slot and reads a half-filled buffer. A slot that
1693
+ got as far as publishing ca->ptr has also attached what its
1694
+ detach slot releases; one that raised earlier has published
1695
+ nothing and released its own parent attach on the way out. */
1696
+ int tag = 0;
1697
+ rb_protect(ca_attach_view_first, (VALUE) ca, &tag);
1698
+ if ( tag ) {
1699
+ if ( ca->ptr ) {
1700
+ ca_func[ca->obj_type].detach(ca);
1701
+ ca->ptr = NULL;
1702
+ }
1703
+ CAVIEW(ca)->attach -= 1;
1704
+ rb_jump_tag(tag);
1705
+ }
1706
+ return;
1632
1707
  }
1633
1708
  }
1634
1709
  else { /* entity array */
@@ -1639,6 +1714,43 @@ ca_attach (void *ap)
1639
1714
  ca_attach(ca->mask);
1640
1715
  }
1641
1716
 
1717
+ /* Attach every array in list[0..n-1], or, if one of those attaches
1718
+ raises, none of them: the ones already attached are detached before the
1719
+ raise propagates. For views over several parents. */
1720
+
1721
+ typedef struct {
1722
+ CArray **list;
1723
+ int32_t n;
1724
+ int32_t done;
1725
+ } ca_attach_all_ctx_t;
1726
+
1727
+ static VALUE
1728
+ ca_attach_all_body (VALUE arg)
1729
+ {
1730
+ ca_attach_all_ctx_t *c = (ca_attach_all_ctx_t *) arg;
1731
+ for ( ; c->done < c->n; c->done++ ) {
1732
+ ca_attach(c->list[c->done]);
1733
+ }
1734
+ return Qnil;
1735
+ }
1736
+
1737
+ void
1738
+ ca_attach_all (CArray **list, int32_t n)
1739
+ {
1740
+ ca_attach_all_ctx_t c;
1741
+ int tag = 0;
1742
+ c.list = list;
1743
+ c.n = n;
1744
+ c.done = 0;
1745
+ rb_protect(ca_attach_all_body, (VALUE) &c, &tag);
1746
+ if ( tag ) {
1747
+ while ( c.done > 0 ) {
1748
+ ca_detach(list[--c.done]);
1749
+ }
1750
+ rb_jump_tag(tag);
1751
+ }
1752
+ }
1753
+
1642
1754
  /* attach parent's data to ca->ptr */
1643
1755
 
1644
1756
  void
@@ -2040,6 +2152,17 @@ ca_fill (void *ap, void *aptr)
2040
2152
 
2041
2153
  /* ------------------------------------------------------------------- */
2042
2154
 
2155
+ #ifdef CARRAY_DEV_BUILD
2156
+
2157
+ /* The Ruby attach surface is built into development builds only. Ruby has no
2158
+ raw pointer, so the only thing a block can do inside an attach window is call
2159
+ back into the array API -- and which door that takes is invisible in the
2160
+ syntax: v[0] reads the attached buffer while v[] and v[0..1] compose past it
2161
+ to the root (R5, see the contract beside ca_attach in carray.h). Per-cell and
2162
+ whole-array writes reach the buffer and survive the sync; a write through a
2163
+ derived view is discarded. The in-tree tests drive the healthy spellings, so
2164
+ the surface stays reachable here and out of released builds. */
2165
+
2043
2166
  /* subroutines for CArray.attach, CArray.attach!
2044
2167
  CArray#attach, CArray#attach! */
2045
2168
 
@@ -2260,6 +2383,8 @@ rb_ca__detach__ (VALUE self)
2260
2383
  return self;
2261
2384
  }
2262
2385
 
2386
+ #endif /* CARRAY_DEV_BUILD */
2387
+
2263
2388
  /* ------------------------------------------------------------------- */
2264
2389
 
2265
2390
  static ID id_decode, id_encode;
@@ -2468,6 +2593,7 @@ Init_carray_core (void)
2468
2593
 
2469
2594
  ca_init_obj_type();
2470
2595
 
2596
+ #ifdef CARRAY_DEV_BUILD
2471
2597
  rb_define_singleton_method(rb_cCArray, "attach", rb_ca_s_attach, -1);
2472
2598
  rb_define_singleton_method(rb_cCArray, "attach!", rb_ca_s_attach_bang, -1);
2473
2599
 
@@ -2477,6 +2603,7 @@ Init_carray_core (void)
2477
2603
  rb_define_method(rb_cCArray, "__attach__", rb_ca__attach__, 0);
2478
2604
  rb_define_method(rb_cCArray, "__sync__", rb_ca__sync__, 0);
2479
2605
  rb_define_method(rb_cCArray, "__detach__", rb_ca__detach__, 0);
2606
+ #endif
2480
2607
 
2481
2608
  rb_define_method(rb_cCArray, "members", rb_ca_members, 0);
2482
2609
 
data/ext/carray_count.c CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  CArray#count dispatcher (= value-comparison family entry point).
4
4
  Hand-routed because the dispatch decision depends on (self.data_type,
5
- v's runtime class), which the per-dtype mkkernel kernels can't see;
5
+ v's runtime class), which the per-type mkkernel kernels can't see;
6
6
  the actual counting is delegated to the kernel-iterator helpers
7
7
  listed below.
8
8
 
@@ -39,11 +39,12 @@ VALUE rb_ca_count_not_masked (int argc, VALUE *argv, VALUE self);
39
39
  - v is a CArray -> broadcast: count each v[k] and stack
40
40
  (rejected for boolean self; see below)
41
41
  - self is boolean, v scalar -> v must be true/false literal
42
- - self is numeric, v scalar -> count_equal_ki (numeric equality)
42
+ - self is numeric or object, v scalar -> count_equal_ki (numeric ==
43
+ for a number, Ruby == for an object)
43
44
 
44
- FIXLEN and OBJECT data_types raise CArray::DataTypeError (the
45
- kernel-iterator helpers do not implement them). Extend
46
- count_equal_ki if demand returns. */
45
+ - self is fixlen, v scalar -> count_equal_ki (memcmp over the whole
46
+ cell; a short String query is padded
47
+ out to the cell width) */
47
48
 
48
49
  static VALUE
49
50
  rb_ca_count (int argc, VALUE *argv, VALUE self)
@@ -95,11 +96,7 @@ rb_ca_count (int argc, VALUE *argv, VALUE self)
95
96
  }
96
97
  if ( ca_test_flag(src, CA_FLAG_FACE_COMPARABLE_STORAGE) ) {
97
98
  if ( ! query_was_scalar ) {
98
- CArray *cv;
99
- GetCArray(rval, cv);
100
- if ( ca_is_face(cv) ) {
101
- rval = rb_ca_strip_face_value(rval);
102
- }
99
+ rval = ca_face_operand_descend(rval, "count(v)");
103
100
  }
104
101
  }
105
102
  else if ( rb_respond_to(self_ref, rb_intern("to_comparable")) ) {
@@ -290,7 +287,9 @@ rb_ca_count (int argc, VALUE *argv, VALUE self)
290
287
  rb_obj_classname(rval));
291
288
  }
292
289
  } else {
293
- if ( rval == Qtrue || rval == Qfalse ) {
290
+ /* true / false are the boolean array's own domain, not a number --
291
+ except in an object array, where they are ordinary stored values. */
292
+ if ( ( rval == Qtrue || rval == Qfalse ) && src->data_type != CA_OBJECT ) {
294
293
  rb_raise(rb_eTypeError,
295
294
  "count(v) on numeric array: v must be numeric, got %s",
296
295
  rb_obj_classname(rval));
data/ext/carray_element.c CHANGED
@@ -254,10 +254,10 @@ rb_ca_elem_store (VALUE self, VALUE ridx, VALUE obj)
254
254
  from_array = 0;
255
255
  }
256
256
 
257
- /* Super-hot path: entity + no mask + common numeric dtype + Numeric obj.
257
+ /* Super-hot path: entity + no mask + common numeric data type + Numeric obj.
258
258
  Bypasses rb_ca_store_index/addr -> ca_update_mask + rb_ca_obj2ptr ->
259
259
  cast_table dispatch. Falls through to general path for: views,
260
- existing mask, non-numeric dtype, UNDEF, or non-Numeric obj. */
260
+ existing mask, non-numeric data type, UNDEF, or non-Numeric obj. */
261
261
  if ( ca_is_entity(ca) && ! ca->mask && obj != CA_UNDEF ) {
262
262
  int8_t dt = ca->data_type;
263
263
  if ( dt == CA_FLOAT64 || dt == CA_FLOAT32 ||
@@ -269,7 +269,7 @@ rb_ca_elem_store (VALUE self, VALUE ridx, VALUE obj)
269
269
  /* Compute flat addr if T_ARRAY index path. */
270
270
  if ( from_array ) addr = elem_entity_addr(ca, idx);
271
271
  char *p = ca->ptr + ca->bytes * addr;
272
- /* Convert obj to dtype + store. Restrict to Fixnum / Float / true
272
+ /* Convert obj to data type + store. Restrict to Fixnum / Float / true
273
273
  / false to avoid Bignum / Rational / Complex coercion that the
274
274
  cast_table handles in the general path. */
275
275
  switch ( dt ) {
@@ -311,7 +311,7 @@ rb_ca_elem_store (VALUE self, VALUE ridx, VALUE obj)
311
311
  if ( FIXNUM_P(obj) ) { *(uint8_t *)p = (FIX2LONG(obj) != 0); return obj; }
312
312
  break;
313
313
  }
314
- /* Fall through: dtype matches but obj kind needs general coercion. */
314
+ /* Fall through: data type matches but obj kind needs general coercion. */
315
315
  }
316
316
  }
317
317
 
@@ -344,7 +344,7 @@ rb_ca_elem_fetch (VALUE self, VALUE ridx)
344
344
  volatile VALUE out;
345
345
  elem_parse_idx_array(ca, ridx, idx);
346
346
 
347
- /* Super-hot path: entity + common numeric dtype + no mask.
347
+ /* Super-hot path: entity + common numeric data type + no mask.
348
348
  Bypass ca_fetch_index dispatch AND rb_ca_ptr2obj cast-table dispatch.
349
349
  Covers the bench's float64 / int64 / int32 cases. */
350
350
  if ( ca_is_entity(ca) && ! ca->mask ) {
@@ -454,7 +454,7 @@ rb_ca_elem_incr (VALUE self, VALUE ridx1)
454
454
 
455
455
  has_mask = elem_probe_mask(ca);
456
456
 
457
- /* Super-hot path: entity + no mask + integer dtype.
457
+ /* Super-hot path: entity + no mask + integer data type.
458
458
  Reads p directly, modifies in place — no separate fetch/store dispatch. */
459
459
  if ( ca_is_entity(ca) && ! has_mask ) {
460
460
  ca_size_t addr;
@@ -818,7 +818,7 @@ rb_ca_elem_min (VALUE self, VALUE ridx, VALUE rval)
818
818
  "elem_min requires a numeric array");
819
819
  }
820
820
 
821
- /* Super-hot path: entity + numeric dtype + Fixnum/Float rval. */
821
+ /* Super-hot path: entity + numeric data type + Fixnum/Float rval. */
822
822
  ELEM_MINMAX_BODY(<);
823
823
 
824
824
  /* General path: view / Bignum-Rational-Complex rval / fall-through.