carray 3.0.3 → 3.0.4

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 (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +264 -0
  3. data/README.md +3 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +1 -6
  7. data/ext/ca_axis_group.c +27 -27
  8. data/ext/ca_binop_dispatch.c +1 -29
  9. data/ext/ca_binop_dispatch.h +0 -1
  10. data/ext/ca_categorical_iterator.c +205 -134
  11. data/ext/ca_compare.c +7 -18
  12. data/ext/ca_group_iter.c +16 -17
  13. data/ext/ca_kernel_iterator.c +1234 -1994
  14. data/ext/ca_kernel_iterator.h +391 -437
  15. data/ext/ca_monop_dispatch.c +82 -59
  16. data/ext/ca_monop_dispatch.h +1 -0
  17. data/ext/ca_obj_array.c +96 -39
  18. data/ext/ca_obj_bincmp.c +24 -21
  19. data/ext/ca_obj_binop.c +15 -26
  20. data/ext/ca_obj_bitarray.c +3 -2
  21. data/ext/ca_obj_bitfield.c +5 -6
  22. data/ext/ca_obj_block.c +18 -9
  23. data/ext/ca_obj_byte_swap.c +5 -6
  24. data/ext/ca_obj_const_string.c +173 -19
  25. data/ext/ca_obj_face.c +238 -57
  26. data/ext/ca_obj_face.h +19 -0
  27. data/ext/ca_obj_fake.c +15 -29
  28. data/ext/ca_obj_farray.c +6 -3
  29. data/ext/ca_obj_field.c +17 -5
  30. data/ext/ca_obj_fixlen_string.c +2 -3
  31. data/ext/ca_obj_grid.c +406 -132
  32. data/ext/ca_obj_meld.c +172 -34
  33. data/ext/ca_obj_moncmp.c +23 -8
  34. data/ext/ca_obj_monop.c +67 -57
  35. data/ext/ca_obj_object.c +157 -51
  36. data/ext/ca_obj_record.c +12 -7
  37. data/ext/ca_obj_reduce.c +13 -8
  38. data/ext/ca_obj_refer.c +25 -27
  39. data/ext/ca_obj_remap.c +30 -9
  40. data/ext/ca_obj_repeat.c +9 -10
  41. data/ext/ca_obj_roll.c +6 -5
  42. data/ext/ca_obj_select.c +14 -9
  43. data/ext/ca_obj_select_axis.c +15 -47
  44. data/ext/ca_obj_shift.c +11 -1
  45. data/ext/ca_obj_stack.c +179 -34
  46. data/ext/ca_obj_stride.c +162 -115
  47. data/ext/ca_obj_string.c +71 -4
  48. data/ext/ca_obj_tile.c +6 -5
  49. data/ext/ca_obj_time.c +22 -6
  50. data/ext/ca_obj_timedelta.c +22 -6
  51. data/ext/ca_obj_transpose.c +5 -3
  52. data/ext/ca_obj_triop.c +23 -32
  53. data/ext/ca_obj_window.c +190 -26
  54. data/ext/ca_sweep_engine.c +2 -2
  55. data/ext/caframe_csv_reader.c +80 -1
  56. data/ext/carray.h +181 -75
  57. data/ext/carray_access.c +184 -96
  58. data/ext/carray_address_basis.c +343 -87
  59. data/ext/carray_attribute.c +89 -6
  60. data/ext/carray_bincount.c +10 -8
  61. data/ext/carray_broadcast.c +13 -8
  62. data/ext/carray_build_flags.h +3 -0
  63. data/ext/carray_call_cfunc.c +108 -0
  64. data/ext/carray_cast.c +142 -24
  65. data/ext/carray_cast_func.rb +46 -6
  66. data/ext/carray_class.c +4 -3
  67. data/ext/carray_config.h +28 -0
  68. data/ext/carray_conversion.c +16 -7
  69. data/ext/carray_copy.c +51 -14
  70. data/ext/carray_core.c +147 -55
  71. data/ext/carray_count.c +50 -20
  72. data/ext/carray_element.c +76 -33
  73. data/ext/carray_factorize.c +118 -64
  74. data/ext/carray_generate.c +191 -50
  75. data/ext/carray_histogram.c +62 -17
  76. data/ext/carray_hold.c +12 -15
  77. data/ext/carray_index_classifier.c +388 -176
  78. data/ext/carray_internal.h +336 -29
  79. data/ext/carray_kernels_bincmp.c +4453 -0
  80. data/ext/carray_kernels_binop.c +11368 -0
  81. data/ext/carray_kernels_init.c +1734 -0
  82. data/ext/carray_kernels_moncmp.c +2104 -0
  83. data/ext/carray_kernels_monop.c +18369 -0
  84. data/ext/carray_kernels_reduce_aggregate.c +23818 -0
  85. data/ext/carray_kernels_reduce_boolean.c +406 -0
  86. data/ext/carray_kernels_reduce_cumulative.c +10086 -0
  87. data/ext/carray_kernels_reduce_extreme.c +17634 -0
  88. data/ext/carray_kernels_reduce_variance.c +3849 -0
  89. data/ext/carray_kernels_scan.c +4302 -0
  90. data/ext/carray_kernels_search.c +33829 -0
  91. data/ext/carray_kernels_sort.c +10906 -0
  92. data/ext/carray_kernels_triop.c +1864 -0
  93. data/ext/carray_key_windows.c +216 -0
  94. data/ext/carray_lazy.c +170 -111
  95. data/ext/carray_mask.c +292 -72
  96. data/ext/carray_median_percentile.c +82 -28
  97. data/ext/carray_memory_view.c +669 -561
  98. data/ext/carray_operator.c +124 -47
  99. data/ext/carray_order.c +384 -76
  100. data/ext/carray_partition.c +28 -27
  101. data/ext/carray_random.c +67 -13
  102. data/ext/carray_round.c +273 -0
  103. data/ext/carray_scatter.c +63 -38
  104. data/ext/carray_segment.c +3 -3
  105. data/ext/carray_slab.c +91 -28
  106. data/ext/carray_sort.c +35 -35
  107. data/ext/carray_struct.c +19 -18
  108. data/ext/carray_test.c +26 -5
  109. data/ext/carray_undef.c +4 -0
  110. data/ext/carray_utils.c +246 -45
  111. data/ext/mk_call_cfunc.rb +19 -2
  112. data/ext/mkkernel.rb +198 -240
  113. data/ext/ruby_carray.c +10 -4
  114. data/ext/version.h +4 -4
  115. data/lib/carray/attribute.rb +126 -22
  116. data/lib/carray/autoload_carray.rb +8 -31
  117. data/lib/carray/autoload_method_extension.rb +14 -5
  118. data/lib/carray/axis_group.rb +255 -106
  119. data/lib/carray/basics.rb +78 -10
  120. data/lib/carray/bincount_nd.rb +12 -2
  121. data/lib/carray/block_iterator.rb +126 -23
  122. data/lib/carray/boolean_reduce.rb +17 -17
  123. data/lib/carray/categorical.rb +165 -43
  124. data/lib/carray/categorical_iterator.rb +130 -27
  125. data/lib/carray/complex.rb +5 -5
  126. data/lib/carray/conditional.rb +67 -18
  127. data/lib/carray/const_string.rb +14 -5
  128. data/lib/carray/construct.rb +15 -11
  129. data/lib/carray/data_type_extension.rb +1 -0
  130. data/lib/carray/distance_limit.rb +77 -0
  131. data/lib/carray/fixlen_string.rb +7 -1
  132. data/lib/carray/frame/column_verbs.rb +70 -0
  133. data/lib/carray/frame/convert.rb +69 -0
  134. data/lib/carray/frame/csv_parser.rb +111 -17
  135. data/lib/carray/frame/describe.rb +3 -2
  136. data/lib/carray/frame/frame.rb +246 -21
  137. data/lib/carray/frame/group.rb +510 -95
  138. data/lib/carray/frame/io.rb +115 -28
  139. data/lib/carray/frame/join.rb +312 -51
  140. data/lib/carray/frame/records.rb +83 -7
  141. data/lib/carray/frame/reshape.rb +42 -35
  142. data/lib/carray/frame/sort.rb +7 -19
  143. data/lib/carray/frame/verbs.rb +188 -48
  144. data/lib/carray/frame/windows.rb +117 -0
  145. data/lib/carray/frame.rb +2 -0
  146. data/lib/carray/fuse_source.rb +11 -4
  147. data/lib/carray/fusion.rb +40 -16
  148. data/lib/carray/histogram.rb +6 -1
  149. data/lib/carray/inspect.rb +31 -8
  150. data/lib/carray/iterator.rb +85 -7
  151. data/lib/carray/key_windows.rb +620 -0
  152. data/lib/carray/lazy.rb +182 -183
  153. data/lib/carray/mask_gap_fill.rb +63 -57
  154. data/lib/carray/meld_reduce.rb +12 -14
  155. data/lib/carray/methods/bin.rb +26 -18
  156. data/lib/carray/methods/bincount.rb +16 -0
  157. data/lib/carray/methods/bit_string.rb +1 -1
  158. data/lib/carray/methods/choose.rb +16 -3
  159. data/lib/carray/methods/composition.rb +3 -1
  160. data/lib/carray/methods/gather_nd.rb +14 -5
  161. data/lib/carray/methods/insert_block.rb +2 -2
  162. data/lib/carray/methods/is_between.rb +59 -0
  163. data/lib/carray/methods/is_in.rb +53 -36
  164. data/lib/carray/methods/join.rb +1 -6
  165. data/lib/carray/methods/lag.rb +49 -0
  166. data/lib/carray/methods/locate_addr.rb +47 -51
  167. data/lib/carray/methods/mode.rb +1 -1
  168. data/lib/carray/methods/nunique.rb +11 -6
  169. data/lib/carray/methods/resize.rb +3 -3
  170. data/lib/carray/methods/value_counts.rb +4 -11
  171. data/lib/carray/record_order.rb +3 -3
  172. data/lib/carray/rng.rb +3 -0
  173. data/lib/carray/runtime.rb +53 -8
  174. data/lib/carray/segment_iterator.rb +313 -82
  175. data/lib/carray/serialize.rb +24 -10
  176. data/lib/carray/slab_iterator.rb +50 -0
  177. data/lib/carray/string.rb +105 -5
  178. data/lib/carray/string_operation_extension.rb +8 -1
  179. data/lib/carray/struct.rb +9 -21
  180. data/lib/carray/struct_builder.rb +19 -8
  181. data/lib/carray/time.rb +726 -104
  182. data/lib/carray/window_iterator.rb +122 -16
  183. data/lib/carray.rb +7 -0
  184. data/yard-stubs/ca_obj_bitfield.rb +1 -1
  185. data/yard-stubs/ca_obj_field.rb +4 -0
  186. data/yard-stubs/ca_obj_grid.rb +21 -10
  187. data/yard-stubs/ca_obj_refer.rb +2 -1
  188. data/yard-stubs/ca_obj_roll.rb +2 -2
  189. data/yard-stubs/ca_obj_shift.rb +5 -1
  190. data/yard-stubs/ca_obj_tile.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +8 -3
  192. data/yard-stubs/carray_access.rb +6 -1
  193. data/yard-stubs/carray_attribute.rb +0 -5
  194. data/yard-stubs/carray_cast.rb +10 -0
  195. data/yard-stubs/carray_conversion.rb +2 -1
  196. data/yard-stubs/carray_copy.rb +4 -2
  197. data/yard-stubs/carray_count.rb +4 -4
  198. data/yard-stubs/carray_element.rb +10 -4
  199. data/yard-stubs/carray_generate.rb +21 -1
  200. data/yard-stubs/carray_mask.rb +26 -2
  201. data/yard-stubs/carray_math.rb +27 -1
  202. data/yard-stubs/carray_median_percentile.rb +5 -2
  203. data/yard-stubs/carray_memory_view.rb +40 -23
  204. data/yard-stubs/carray_order.rb +29 -18
  205. data/yard-stubs/carray_random.rb +13 -4
  206. data/yard-stubs/carray_scatter.rb +14 -9
  207. data/yard-stubs/carray_slab.rb +9 -2
  208. data/yard-stubs/carray_sort.rb +6 -5
  209. data/yard-stubs/carray_test.rb +5 -3
  210. metadata +25 -3
  211. data/lib/carray/arrow_tensor.rb +0 -401
  212. data/lib/carray/table.rb +0 -108
data/ext/carray_utils.c CHANGED
@@ -17,6 +17,8 @@
17
17
 
18
18
  #include "ruby/st.h"
19
19
  #include "ruby/thread_native.h"
20
+ #include "ruby/ractor.h"
21
+ #include "ruby/debug.h" /* rb_profile_frames: the method the user called */
20
22
 
21
23
  static ID id_begin, id_end, id_excl_end;
22
24
  #define RANGE_BEG(r) (rb_funcall(r, id_begin, 0))
@@ -56,7 +58,8 @@ ca_set_iterator (int n, ...)
56
58
  max = ca->elements;
57
59
  }
58
60
  else if ( max != ca->elements ) {
59
- rb_raise(rb_eArgError, "data size mismatch in operation");
61
+ rb_raise(rb_eArgError, "%s: data size mismatch in operation",
62
+ ca_calling_method_name());
60
63
  }
61
64
  }
62
65
  }
@@ -158,7 +161,7 @@ ca_parse_range (VALUE arg, ca_size_t size,
158
161
  }
159
162
  *poffset = start;
160
163
  *pcount = llabs(last - start) + 1;
161
- *pstep = 1;
164
+ *pstep = ( last >= start ) ? 1 : -1;
162
165
  }
163
166
  #ifdef HAVE_RB_ARITHMETIC_SEQUENCE_EXTRACT
164
167
  else if ( rb_obj_is_kind_of(arg, rb_cArithSeq) ) { /* ca[--,ArithSeq,--]*/
@@ -172,6 +175,7 @@ ca_parse_range (VALUE arg, ca_size_t size,
172
175
  if ( step == 0 ) {
173
176
  rb_raise(rb_eArgError, "step should not be 0");
174
177
  }
178
+ CA_CHECK_INDEX(start, size);
175
179
  if ( last < 0 ) {
176
180
  last += size;
177
181
  }
@@ -181,7 +185,6 @@ ca_parse_range (VALUE arg, ca_size_t size,
181
185
  if ( last < 0 || last >= size ) {
182
186
  ca_index_out_of_range(last, size, "index");
183
187
  }
184
- CA_CHECK_INDEX(start, size);
185
188
  if ( (last - start) * step < 0 ) {
186
189
  count = 1;
187
190
  }
@@ -189,7 +192,9 @@ ca_parse_range (VALUE arg, ca_size_t size,
189
192
  count = llabs(last - start)/llabs(step) + 1;
190
193
  }
191
194
  bound = start + (count - 1)*step;
192
- CA_CHECK_INDEX(bound, size);
195
+ if ( count > 0 && ( bound < 0 || bound >= size ) ) {
196
+ ca_index_out_of_range(bound, size, "index");
197
+ }
193
198
  *poffset = start;
194
199
  *pcount = count;
195
200
  *pstep = step;
@@ -211,8 +216,9 @@ ca_parse_range (VALUE arg, ca_size_t size,
211
216
  start = 0;
212
217
  count = (size-1)/llabs(step) + 1;
213
218
  bound = start + (count - 1)*step;
214
- CA_CHECK_INDEX(start, size);
215
- CA_CHECK_INDEX(bound, size);
219
+ if ( count > 0 && ( bound < 0 || bound >= size ) ) {
220
+ ca_index_out_of_range(bound, size, "index");
221
+ }
216
222
  *poffset = start;
217
223
  *pcount = count;
218
224
  *pstep = step;
@@ -225,6 +231,7 @@ ca_parse_range (VALUE arg, ca_size_t size,
225
231
  if ( step == 0 ) {
226
232
  rb_raise(rb_eArgError, "step should not be 0");
227
233
  }
234
+ CA_CHECK_INDEX(start, size);
228
235
  if ( last < 0 ) {
229
236
  last += size;
230
237
  }
@@ -234,7 +241,6 @@ ca_parse_range (VALUE arg, ca_size_t size,
234
241
  if ( last < 0 || last >= size ) {
235
242
  ca_index_out_of_range(last, size, "index");
236
243
  }
237
- CA_CHECK_INDEX(start, size);
238
244
  if ( (last - start) * step < 0 ) {
239
245
  count = 1;
240
246
  }
@@ -242,7 +248,9 @@ ca_parse_range (VALUE arg, ca_size_t size,
242
248
  count = llabs(last - start)/llabs(step) + 1;
243
249
  }
244
250
  bound = start + (count - 1)*step;
245
- CA_CHECK_INDEX(bound, size);
251
+ if ( count > 0 && ( bound < 0 || bound >= size ) ) {
252
+ ca_index_out_of_range(bound, size, "index");
253
+ }
246
254
  *poffset = start;
247
255
  *pcount = count;
248
256
  *pstep = step;
@@ -259,6 +267,7 @@ ca_parse_range (VALUE arg, ca_size_t size,
259
267
  if ( step == 0 ) {
260
268
  rb_raise(rb_eArgError, "step should not be 0");
261
269
  }
270
+ CA_CHECK_INDEX(start, size);
262
271
  if ( last < 0 ) {
263
272
  last += size;
264
273
  }
@@ -268,7 +277,6 @@ ca_parse_range (VALUE arg, ca_size_t size,
268
277
  if ( last < 0 || last >= size ) {
269
278
  ca_index_out_of_range(last, size, "index");
270
279
  }
271
- CA_CHECK_INDEX(start, size);
272
280
  if ( (last - start) * step < 0 ) {
273
281
  count = 1;
274
282
  }
@@ -276,7 +284,9 @@ ca_parse_range (VALUE arg, ca_size_t size,
276
284
  count = llabs(last - start)/llabs(step) + 1;
277
285
  }
278
286
  bound = start + (count - 1)*step;
279
- CA_CHECK_INDEX(bound, size);
287
+ if ( count > 0 && ( bound < 0 || bound >= size ) ) {
288
+ ca_index_out_of_range(bound, size, "index");
289
+ }
280
290
  *poffset = start;
281
291
  *pcount = count;
282
292
  *pstep = step;
@@ -285,9 +295,11 @@ ca_parse_range (VALUE arg, ca_size_t size,
285
295
  else { /* [i,n] */
286
296
  start = NUM2SIZE(arg0);
287
297
  count = NUM2SIZE(arg1);
288
- bound = start + (count - 1);
289
298
  CA_CHECK_INDEX(start, size);
290
- CA_CHECK_INDEX(bound, size);
299
+ bound = start + (count - 1);
300
+ if ( count > 0 && ( bound < 0 || bound >= size ) ) {
301
+ ca_index_out_of_range(bound, size, "index");
302
+ }
291
303
  *poffset = start;
292
304
  *pcount = count;
293
305
  *pstep = 1;
@@ -300,9 +312,11 @@ ca_parse_range (VALUE arg, ca_size_t size,
300
312
  if ( step == 0 ) {
301
313
  rb_raise(rb_eArgError, "step should not be 0");
302
314
  }
303
- bound = start + (count - 1)*step;
304
315
  CA_CHECK_INDEX(start, size);
305
- CA_CHECK_INDEX(bound, size);
316
+ bound = start + (count - 1)*step;
317
+ if ( count > 0 && ( bound < 0 || bound >= size ) ) {
318
+ ca_index_out_of_range(bound, size, "index");
319
+ }
306
320
  *poffset = start;
307
321
  *pcount = count;
308
322
  *pstep = step;
@@ -535,27 +549,44 @@ ca_stable_id_name (ID id)
535
549
  /* The method a delegating method answers for. A method that does its work
536
550
  by calling others (median calls partition_index, mode calls sort) runs
537
551
  them under ca_answer_as_caller, so an error they raise names the method
538
- the user called. The outermost one wins. */
539
- static _Thread_local const char *ca_answering_name = NULL;
552
+ the user called. The outermost one wins. The name is kept with the
553
+ Ruby thread (Thread#[], so per fiber): Ractors share native threads under
554
+ the M:N scheduler, and a native-thread-local variable leaks one Ractor's
555
+ name into another's message. */
556
+ static ID id_answering;
557
+
558
+ static const char *
559
+ ca_answering_name (void)
560
+ {
561
+ VALUE name = rb_thread_local_aref(rb_thread_current(), id_answering);
562
+ return NIL_P(name) ? NULL : ca_stable_id_name(SYM2ID(name));
563
+ }
564
+
565
+ static void
566
+ ca_set_answering_name (const char *name)
567
+ {
568
+ rb_thread_local_aset(rb_thread_current(), id_answering,
569
+ name ? ID2SYM(rb_intern(name)) : Qnil);
570
+ }
540
571
 
541
572
  static VALUE
542
573
  ca_answer_restore (VALUE unused)
543
574
  {
544
- ca_answering_name = NULL;
575
+ ca_set_answering_name(NULL);
545
576
  return Qnil;
546
577
  }
547
578
 
579
+ /* A C method that does its work by calling others runs them through here.
580
+ It used to fix the name at entry; the name is now found from the frames
581
+ when an error is raised (ca_user_called_method_id), which also reaches
582
+ the outer method, so nothing is fixed and nothing is paid on the way in. */
548
583
  VALUE
549
584
  ca_answer_as_caller (VALUE (*body)(VALUE), VALUE arg)
550
585
  {
551
- if ( ca_answering_name ) {
552
- return body(arg);
553
- }
554
- ca_answering_name = ca_calling_method_name();
555
- return rb_ensure(body, arg, ca_answer_restore, Qnil);
586
+ return body(arg);
556
587
  }
557
588
 
558
- /* CArray.__answer_as__(name) { ... }: the same for a method written in Ruby. */
589
+ /* CArray.__name_errors_after_method__(name) { ... }: the same for a method written in Ruby. */
559
590
  static VALUE
560
591
  rb_ca_s_answer_as_body (VALUE unused)
561
592
  {
@@ -565,23 +596,137 @@ rb_ca_s_answer_as_body (VALUE unused)
565
596
  static VALUE
566
597
  rb_ca_s_answer_as (VALUE klass, VALUE name)
567
598
  {
568
- if ( ca_answering_name ) {
599
+ if ( ca_answering_name() ) {
569
600
  return rb_yield(Qnil);
570
601
  }
571
- ca_answering_name = ca_stable_id_name(rb_to_id(name));
602
+ ca_set_answering_name(ca_stable_id_name(rb_to_id(name)));
572
603
  return rb_ensure(rb_ca_s_answer_as_body, Qnil, ca_answer_restore, Qnil);
573
604
  }
574
605
 
606
+ /* The directory the library's Ruby files are loaded from (lib/), set once
607
+ by lib/carray.rb as it loads, read only afterwards. */
608
+ static char *ca_library_root = NULL;
609
+ static long ca_library_root_len = 0;
610
+
611
+ /* CArray.library_root = dir (private): where the frames of the library's
612
+ own Ruby methods are. The first call wins. */
613
+ static VALUE
614
+ rb_ca_s_set_library_root (VALUE klass, VALUE dir)
615
+ {
616
+ if ( ca_library_root == NULL ) {
617
+ long len = RSTRING_LEN(StringValue(dir));
618
+ char *copy = ALLOC_N(char, len + 1);
619
+ memcpy(copy, RSTRING_PTR(dir), len);
620
+ copy[len] = '\0';
621
+ ca_library_root_len = len;
622
+ ca_library_root = copy;
623
+ }
624
+ return Qnil;
625
+ }
626
+
627
+ /* Whether a frame without a source file -- a C method -- is one of the
628
+ library's: a method of a CA* or CScalar class, or a CA_* conversion
629
+ function. A method of a core class (Array#map, Integer#times) is not. */
630
+ static int
631
+ ca_frame_is_library_cfunc (VALUE frame, VALUE name)
632
+ {
633
+ VALUE path = rb_profile_frame_classpath(frame);
634
+ if ( ! NIL_P(path) ) {
635
+ const char *p = RSTRING_PTR(path);
636
+ long n = RSTRING_LEN(path);
637
+ if ( ( n >= 2 && strncmp(p, "CA", 2) == 0 )
638
+ || ( n >= 7 && strncmp(p, "CScalar", 7) == 0 ) ) {
639
+ return 1;
640
+ }
641
+ }
642
+ return RSTRING_LEN(name) > 3 && strncmp(RSTRING_PTR(name), "CA_", 3) == 0;
643
+ }
644
+
645
+ /* The method the user called: going out from the innermost frame, the last
646
+ frame of the library -- a Ruby method under ca_library_root, or one of
647
+ its C methods -- before the user's own code (a block included) is
648
+ reached. A core C method the library calls on the way (Array#map) is
649
+ passed through. 0 when no frame of the library is found. */
650
+ #define CA_NAMING_FRAMES 64
651
+
652
+ static ID
653
+ ca_user_called_method_id (void)
654
+ {
655
+ ID id_initialize = rb_intern("initialize");
656
+ ID id_new = rb_intern("new");
657
+ VALUE frames[CA_NAMING_FRAMES];
658
+ int lines[CA_NAMING_FRAMES];
659
+ int n, i;
660
+ ID found = 0;
661
+ if ( ca_library_root == NULL ) {
662
+ return 0;
663
+ }
664
+ n = rb_profile_frames(0, CA_NAMING_FRAMES, frames, lines);
665
+ for (i = 0; i < n; i++) {
666
+ VALUE file = rb_profile_frame_absolute_path(frames[i]);
667
+ VALUE name, label;
668
+ if ( NIL_P(file) ) {
669
+ /* code evaluated with a file name (class_eval in the library) and the
670
+ core written in Ruby ("<internal:numeric>") have a path but no
671
+ absolute path; -e has a path outside the library */
672
+ file = rb_profile_frame_path(frames[i]);
673
+ }
674
+ if ( ! NIL_P(file) && strcmp(StringValueCStr(file), "<cfunc>") == 0 ) {
675
+ name = rb_profile_frame_method_name(frames[i]);
676
+ if ( NIL_P(name) ) {
677
+ continue;
678
+ }
679
+ if ( ca_frame_is_library_cfunc(frames[i], name) ) {
680
+ found = rb_to_id(name);
681
+ }
682
+ else if ( found == id_initialize && rb_to_id(name) == id_new ) {
683
+ found = id_new; /* Class#new around our initialize */
684
+ }
685
+ continue; /* a core C method passes */
686
+ }
687
+ if ( ! NIL_P(file) && strncmp(StringValueCStr(file), "<internal:", 10) == 0 ) {
688
+ continue; /* core written in Ruby (Integer#times) */
689
+ }
690
+ if ( NIL_P(file) /* eval without a file: the user's */
691
+ || RSTRING_LEN(file) <= ca_library_root_len
692
+ || strncmp(RSTRING_PTR(file), ca_library_root, ca_library_root_len) != 0
693
+ || RSTRING_PTR(file)[ca_library_root_len] != '/' ) {
694
+ break;
695
+ }
696
+ /* A block of the library (an each inside a method, the autoload stub)
697
+ names nothing: the method around it is further out. A block whose
698
+ method is a class body is a method made by define_method, and its
699
+ frame does not carry the method's name, so the walk gives up and the
700
+ innermost C method is named, as before the walk. */
701
+ label = rb_profile_frame_label(frames[i]);
702
+ if ( ! NIL_P(label) && strncmp(StringValueCStr(label), "block ", 6) == 0 ) {
703
+ VALUE base = rb_profile_frame_base_label(frames[i]);
704
+ if ( ! NIL_P(base) && RSTRING_LEN(base) > 0 && RSTRING_PTR(base)[0] == '<' ) {
705
+ return 0;
706
+ }
707
+ continue;
708
+ }
709
+ name = rb_profile_frame_method_name(frames[i]);
710
+ if ( ! NIL_P(name) ) {
711
+ found = rb_to_id(name);
712
+ }
713
+ }
714
+ return found;
715
+ }
716
+
575
717
  const char *
576
718
  ca_calling_method_name (void)
577
719
  {
578
720
  ID id;
579
721
  const char *s;
580
722
  size_t n;
581
- if ( ca_answering_name ) {
582
- return ca_answering_name;
723
+ if ( (s = ca_answering_name()) ) {
724
+ return s;
725
+ }
726
+ id = ca_user_called_method_id();
727
+ if ( ! id ) {
728
+ id = rb_frame_this_func();
583
729
  }
584
- id = rb_frame_this_func();
585
730
  if ( ! id ) {
586
731
  return "axis";
587
732
  }
@@ -624,15 +769,22 @@ ca_shape_elements (int8_t ndim, const ca_size_t *dim, ca_size_t bytes)
624
769
  return elements;
625
770
  }
626
771
 
627
- /* An index array is read as ca_size_t. A uint64 one holding 2**63 or more
628
- would turn negative on the way and count from the end of the axis, so it
629
- is refused here as the out-of-range index it is. */
772
+ /* An index array is read as ca_size_t. A float or complex one would be
773
+ cut to an integer on the way, so it is refused, as the indexer refuses
774
+ it. A uint64 one holding 2**63 or more would turn negative and count
775
+ from the end of the axis, so it is refused as the out-of-range index it
776
+ is. */
630
777
  void
631
778
  ca_check_index_array (VALUE v)
632
779
  {
633
780
  CArray *ca;
634
781
  if ( ! rb_obj_is_carray(v) ) return;
635
782
  GetCArray(v, ca);
783
+ if ( ca_is_float_type(ca) || ca_is_complex_type(ca) ) {
784
+ rb_raise(rb_eIndexError,
785
+ "data_type %s is invalid for an index (should be integer)",
786
+ ca_type_name[ca->data_type]);
787
+ }
636
788
  if ( ca->data_type != CA_UINT64 || ca->elements == 0 ) return;
637
789
  {
638
790
  VALUE max = rb_funcall(v, rb_intern("max"), 0);
@@ -698,7 +850,7 @@ ca_min_count_arg (VALUE v, const char *name)
698
850
  return n;
699
851
  }
700
852
 
701
- /* CArray.__min_count_need__(op, min_count, least): how many present cells a
853
+ /* CArray.__present_cells_needed_per_result_cell__(op, min_count, least): how many present cells a
702
854
  result cell of `op` needs (ca_too_few_present), for a reduction written
703
855
  in Ruby; it masks the cells whose count is below it. */
704
856
  static VALUE
@@ -719,11 +871,10 @@ int
719
871
  ca_symbol_choice (VALUE v, const char *arg, const char *c0, const char *c1,
720
872
  const char *name)
721
873
  {
722
- const char *nm = name ? name : ca_calling_method_name();
723
874
  ID id;
724
875
  if ( ! SYMBOL_P(v) ) {
725
876
  rb_raise(rb_eTypeError, "%s: %s must be a Symbol (got %"PRIsVALUE")",
726
- nm, arg, rb_obj_class(v));
877
+ name ? name : ca_calling_method_name(), arg, rb_obj_class(v));
727
878
  }
728
879
  id = SYM2ID(v);
729
880
  if ( id == rb_intern(c0) ) {
@@ -733,7 +884,7 @@ ca_symbol_choice (VALUE v, const char *arg, const char *c0, const char *c1,
733
884
  return 1;
734
885
  }
735
886
  rb_raise(rb_eArgError, "%s: unknown %s :%"PRIsVALUE" (expected :%s or :%s)",
736
- nm, arg, rb_id2str(id), c0, c1);
887
+ name ? name : ca_calling_method_name(), arg, rb_id2str(id), c0, c1);
737
888
  return 0;
738
889
  }
739
890
 
@@ -776,7 +927,7 @@ ca_reduce_fill (VALUE result, VALUE fill, int whole)
776
927
  return ( result == CA_UNDEF ) ? fill : result;
777
928
  }
778
929
 
779
- /* CArray.__reduce_fill__(result, fill_value): ca_reduce_fill for a
930
+ /* CArray.__fill_undefined_result_cells__(result, fill_value): ca_reduce_fill for a
780
931
  reduction written in Ruby. A fill_value of nil or UNDEF leaves the
781
932
  undefined cells as they are. */
782
933
  static VALUE
@@ -817,6 +968,38 @@ rb_ca_normalize_axis_for_ndim (long raw, int ndim, const char *name)
817
968
  return (int) axis;
818
969
  }
819
970
 
971
+ /* An array has at most CA_RANK_MAX axes, and every dim[] and strides[]
972
+ array in the library is that long. A constructor or view whose result
973
+ would have more -- a shape given, a rank computed -- calls this before it
974
+ writes a single dim, and is refused with ArgumentError naming the method,
975
+ the rank and the limit. `name` NULL names the method the user called. */
976
+ void
977
+ ca_check_rank_fits (long ndim, const char *name)
978
+ {
979
+ if ( ndim > CA_RANK_MAX ) {
980
+ rb_raise(rb_eArgError, "%s: ndim %ld exceeds CA_RANK_MAX %d",
981
+ name ? name : ca_calling_method_name(), ndim, CA_RANK_MAX);
982
+ }
983
+ }
984
+
985
+ /* The axis an `axis` argument names among `ndim` axes: the one rule every
986
+ entry that takes an axis from Ruby keeps. An Integer, negative counting
987
+ from the end; anything else raises TypeError, and an Integer outside
988
+ [-ndim, ndim) raises ArgumentError naming the value given -- a Bignum
989
+ too, since no ndim reaches it. `name` and the insertion-position use of
990
+ `ndim` are as for rb_ca_normalize_axis_for_ndim. */
991
+ int
992
+ ca_normalize_axis_arg (VALUE raxis, int ndim, const char *name)
993
+ {
994
+ ca_check_rank_fits(ndim, name); /* no array has the axes to name */
995
+ if ( RB_INTEGER_TYPE_P(raxis) && ! RB_FIXNUM_P(raxis) ) {
996
+ rb_raise(rb_eArgError,
997
+ "%s: axis %"PRIsVALUE" out of range for ndim %d",
998
+ name ? name : ca_calling_method_name(), raxis, ndim);
999
+ }
1000
+ return rb_ca_normalize_axis_for_ndim(ca_axis_integer(raxis, name), ndim, name);
1001
+ }
1002
+
820
1003
  /* Self-bound wrapper: delegate to the kernel using self.ndim. Kept
821
1004
  for backward compatibility with the existing instance method
822
1005
  `CArray#normalize_axis`. */
@@ -825,7 +1008,7 @@ rb_ca_normalize_axis_value (VALUE self, VALUE raxis, const char *name)
825
1008
  {
826
1009
  CArray *ca;
827
1010
  GetCArray(self, ca);
828
- return rb_ca_normalize_axis_for_ndim(ca_axis_integer(raxis, name), (int) ca->ndim, name);
1011
+ return ca_normalize_axis_arg(raxis, (int) ca->ndim, name);
829
1012
  }
830
1013
 
831
1014
  /* Ruby-facing `CArray#normalize_axis(axis, name=nil)` — returns the
@@ -860,7 +1043,7 @@ rb_ca_s_normalize_axis (int argc, VALUE *argv, VALUE klass)
860
1043
  int k;
861
1044
  rb_scan_args(argc, argv, "21", &raxis, &rndim, &rname);
862
1045
  name = NIL_P(rname) ? NULL : StringValueCStr(rname);
863
- k = rb_ca_normalize_axis_for_ndim(ca_axis_integer(raxis, name), NUM2INT(rndim), name);
1046
+ k = ca_normalize_axis_arg(raxis, NUM2INT(rndim), name);
864
1047
  return INT2NUM(k);
865
1048
  }
866
1049
 
@@ -880,7 +1063,7 @@ rb_ca_normalize_axes (int argc, VALUE *argv, VALUE self)
880
1063
  const char *name;
881
1064
 
882
1065
  rb_scan_args(argc, argv, "11", &raxes, &rname);
883
- name = NIL_P(rname) ? ca_calling_method_name() : StringValueCStr(rname);
1066
+ name = NIL_P(rname) ? NULL : StringValueCStr(rname); /* NULL: looked up on error */
884
1067
  GetCArray(self, ca);
885
1068
 
886
1069
  if ( NIL_P(raxes) ) {
@@ -941,7 +1124,8 @@ rb_ca_guess_type (VALUE obj)
941
1124
  /* Check before narrowing to int8_t, so the message names the id given. */
942
1125
  long id = FIX2LONG(obj);
943
1126
  if ( id <= CA_NONE || id >= CA_NTYPE ) {
944
- rb_raise(rb_eArgError, "invalid data_type id %ld", id);
1127
+ rb_raise(rb_eArgError, "%s: invalid data_type id %ld",
1128
+ ca_calling_method_name(), id);
945
1129
  }
946
1130
  return (int8_t) id;
947
1131
  }
@@ -968,7 +1152,8 @@ rb_ca_guess_type (VALUE obj)
968
1152
 
969
1153
  inspect = rb_inspect(obj);
970
1154
  rb_raise(rb_eArgError,
971
- "<%s> is unknown data_type representation", StringValuePtr(inspect));
1155
+ "%s: <%s> is unknown data_type representation",
1156
+ ca_calling_method_name(), StringValuePtr(inspect));
972
1157
  }
973
1158
 
974
1159
  void
@@ -991,7 +1176,7 @@ rb_ca_guess_type_and_bytes (VALUE rtype, VALUE rbytes,
991
1176
  }
992
1177
  }
993
1178
  else {
994
- CA_CHECK_DATA_TYPE(*data_type);
1179
+ CA_CHECK_DATA_TYPE_GIVEN(*data_type);
995
1180
  *bytes = ca_sizeof[*data_type];
996
1181
  }
997
1182
  }
@@ -1187,11 +1372,25 @@ rb_set_options (VALUE ropt, const char *spec_in, ...)
1187
1372
  }
1188
1373
 
1189
1374
 
1375
+ /* Whether this runs in the main Ractor: Init does, and leaves a value in
1376
+ its Ractor-local storage that no other Ractor has. */
1377
+ static rb_ractor_local_key_t ca_main_ractor_key;
1378
+
1379
+ int
1380
+ ca_in_main_ractor (void)
1381
+ {
1382
+ VALUE mark;
1383
+ return rb_ractor_local_storage_value_lookup(ca_main_ractor_key, &mark);
1384
+ }
1385
+
1190
1386
  void
1191
1387
  Init_carray_utils (void)
1192
1388
  {
1389
+ ca_main_ractor_key = rb_ractor_local_storage_value_newkey();
1390
+ rb_ractor_local_storage_value_set(ca_main_ractor_key, Qtrue);
1193
1391
  ca_id_names = st_init_numtable();
1194
1392
  rb_nativethread_lock_initialize(&ca_id_names_lock);
1393
+ id_answering = rb_intern("__carray_answering_method__");
1195
1394
  id_begin = rb_intern("begin");
1196
1395
  id_end = rb_intern("end");
1197
1396
  id_excl_end = rb_intern("exclude_end?");
@@ -1203,11 +1402,13 @@ Init_carray_utils (void)
1203
1402
  rb_define_method(rb_cCArray, "normalize_axes", rb_ca_normalize_axes, -1);
1204
1403
  rb_define_singleton_method(rb_cCArray, "normalize_axis",
1205
1404
  rb_ca_s_normalize_axis, -1);
1206
- rb_define_singleton_method(rb_cCArray, "__min_count_need__",
1405
+ rb_define_singleton_method(rb_cCArray, "__present_cells_needed_per_result_cell__",
1207
1406
  rb_ca_s_min_count_need, 3);
1208
- rb_define_singleton_method(rb_cCArray, "__reduce_fill__",
1407
+ rb_define_singleton_method(rb_cCArray, "__fill_undefined_result_cells__",
1209
1408
  rb_ca_s_reduce_fill, 2);
1210
- rb_define_singleton_method(rb_cCArray, "__answer_as__",
1409
+ rb_define_private_method(rb_singleton_class(rb_cCArray), "library_root=",
1410
+ rb_ca_s_set_library_root, 1);
1411
+ rb_define_singleton_method(rb_cCArray, "__name_errors_after_method__",
1211
1412
  rb_ca_s_answer_as, 1);
1212
1413
 
1213
1414
  }
data/ext/mk_call_cfunc.rb CHANGED
@@ -127,6 +127,17 @@ def indent(s, prefix = " ")
127
127
  s.each_line.map { |l| l.strip.empty? ? l : prefix + l }.join
128
128
  end
129
129
 
130
+ # The operands are read through cx[] only once their structs are taken, and
131
+ # a caller may pass a view it made for the call and holds nowhere else
132
+ # (rb_ca_wrap_readonly(x, ...) as an argument): keep each one reachable until
133
+ # the walk is done. rcx0 is the return value and needs no guard.
134
+ def emit_operand_guards(n)
135
+ (1...n).each do |k|
136
+ $src.puts " RB_GC_GUARD(rcx#{k});"
137
+ end
138
+ $src.puts "" if n > 1
139
+ end
140
+
130
141
  # --- raw ca_call_cfunc_N ----------------------------------------------------
131
142
  #
132
143
  # The per-cell loop runs in a walker function of its own, handed to
@@ -198,7 +209,7 @@ def emit_raw_common(n, r:)
198
209
  $src.puts " TypedData_Get_Struct(rcx#{k}, CArray, &carray_data_type, cx[#{k}]);"
199
210
  end
200
211
  $src.puts ""
201
- $src.puts <<~END_C
212
+ $src.puts indent(<<~END_C)
202
213
  /* sweep engine: per-operand acquire (alias / xmalloc + ca_xfer_all),
203
214
  operand pairing, mask OR across INPUTs, mask propagate to OUTPUTs.
204
215
  Lifecycle template lives in ext/ca_sweep_engine.{c,h}. */
@@ -218,6 +229,9 @@ def emit_raw_common(n, r:)
218
229
  ctx.userdata = #{r ? "userdata" : "NULL"};
219
230
  ca_sweep_run(&state, #{name}_walk, (VALUE) &ctx);
220
231
 
232
+ END_C
233
+ emit_operand_guards(n)
234
+ $src.puts <<~END_C
221
235
  return rcx0;
222
236
  }
223
237
 
@@ -396,7 +410,7 @@ def emit_slab_body(n, name, walker, userdata)
396
410
  $src.puts " TypedData_Get_Struct(rcx#{k}, CArray, &carray_data_type, cx[#{k}]);"
397
411
  end
398
412
  $src.puts ""
399
- $src.puts <<~END_C
413
+ $src.puts indent(<<~END_C)
400
414
  /* sweep engine, chunked path: per-operand acquire (attach for OUTPUT
401
415
  and alias INPUT, arena chunk scratch for non-alias INPUT), broadcast
402
416
  shape check, mask OR across INPUTs, mask propagate to OUTPUTs at
@@ -421,6 +435,9 @@ def emit_slab_body(n, name, walker, userdata)
421
435
  ctx.userdata = #{userdata};
422
436
  ca_sweep_run_chunked(&state, #{walker}, (VALUE) &ctx);
423
437
 
438
+ END_C
439
+ emit_operand_guards(n)
440
+ $src.puts <<~END_C
424
441
  return rcx0;
425
442
  }
426
443