carray 3.0.4 → 3.0.6

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 (155) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +438 -0
  3. data/README.md +1 -1
  4. data/carray.gemspec +3 -2
  5. data/ext/ca_array_pool.c +25 -1
  6. data/ext/ca_axis_dispatch.c +19 -20
  7. data/ext/ca_axis_group.c +1 -1
  8. data/ext/ca_categorical_iterator.c +34 -27
  9. data/ext/ca_compare.c +4 -4
  10. data/ext/ca_composite_dispatch.c +9 -9
  11. data/ext/ca_kernel_iterator.c +122 -237
  12. data/ext/ca_kernel_iterator.h +522 -337
  13. data/ext/ca_obj_array.c +69 -19
  14. data/ext/ca_obj_bincmp.c +10 -1
  15. data/ext/ca_obj_binop.c +11 -1
  16. data/ext/ca_obj_bitarray.c +5 -1
  17. data/ext/ca_obj_bitfield.c +60 -43
  18. data/ext/ca_obj_byte_swap.c +4 -1
  19. data/ext/ca_obj_const_string.c +15 -3
  20. data/ext/ca_obj_face.c +44 -1
  21. data/ext/ca_obj_fake.c +3 -1
  22. data/ext/ca_obj_field.c +13 -3
  23. data/ext/ca_obj_grid.c +219 -17
  24. data/ext/ca_obj_meld.c +69 -113
  25. data/ext/ca_obj_moncmp.c +9 -1
  26. data/ext/ca_obj_monop.c +86 -12
  27. data/ext/ca_obj_object.c +118 -32
  28. data/ext/ca_obj_record.c +3 -0
  29. data/ext/ca_obj_reduce.c +19 -1
  30. data/ext/ca_obj_refer.c +80 -26
  31. data/ext/ca_obj_remap.c +69 -10
  32. data/ext/ca_obj_roll.c +27 -6
  33. data/ext/ca_obj_select.c +38 -6
  34. data/ext/ca_obj_select_axis.c +1 -0
  35. data/ext/ca_obj_shift.c +32 -7
  36. data/ext/ca_obj_stack.c +311 -111
  37. data/ext/ca_obj_stride.c +159 -64
  38. data/ext/ca_obj_string.c +27 -2
  39. data/ext/ca_obj_tile.c +28 -7
  40. data/ext/ca_obj_transpose.c +8 -4
  41. data/ext/ca_obj_triop.c +12 -1
  42. data/ext/ca_obj_window.c +80 -50
  43. data/ext/ca_op_ipower.c +12 -12
  44. data/ext/ca_sort_kernels.h +6 -3
  45. data/ext/caframe_csv_reader.c +1 -1
  46. data/ext/carray.h +35 -1
  47. data/ext/carray_access.c +43 -67
  48. data/ext/carray_address_basis.c +134 -0
  49. data/ext/carray_attribute.c +32 -0
  50. data/ext/carray_broadcast.c +3 -4
  51. data/ext/carray_build_flags.h +1 -1
  52. data/ext/carray_cast.c +251 -93
  53. data/ext/carray_cast_func.rb +56 -26
  54. data/ext/carray_conversion.c +11 -7
  55. data/ext/carray_core.c +814 -72
  56. data/ext/carray_count.c +6 -0
  57. data/ext/carray_element.c +22 -13
  58. data/ext/carray_factorize.c +20 -20
  59. data/ext/carray_generate.c +8 -2
  60. data/ext/carray_histogram.c +26 -4
  61. data/ext/carray_index_classifier.c +4 -4
  62. data/ext/carray_internal.h +159 -12
  63. data/ext/carray_kernels_bincmp.c +2174 -170
  64. data/ext/carray_kernels_binop.c +25653 -5487
  65. data/ext/carray_kernels_init.c +8 -8
  66. data/ext/carray_kernels_moncmp.c +96 -96
  67. data/ext/carray_kernels_monop.c +12253 -5235
  68. data/ext/carray_kernels_reduce_aggregate.c +6150 -2423
  69. data/ext/carray_kernels_reduce_boolean.c +1533 -15
  70. data/ext/carray_kernels_reduce_cumulative.c +2442 -882
  71. data/ext/carray_kernels_reduce_extreme.c +5658 -1661
  72. data/ext/carray_kernels_reduce_variance.c +2534 -402
  73. data/ext/carray_kernels_scan.c +368 -368
  74. data/ext/carray_kernels_search.c +4551 -604
  75. data/ext/carray_kernels_sort.c +659 -751
  76. data/ext/carray_kernels_triop.c +114 -114
  77. data/ext/carray_key_windows.c +4 -0
  78. data/ext/carray_lazy.c +47 -0
  79. data/ext/carray_loop.c +1 -1
  80. data/ext/carray_mask.c +175 -161
  81. data/ext/carray_median_percentile.c +4 -4
  82. data/ext/carray_memory_view.c +15 -9
  83. data/ext/carray_operator.c +152 -49
  84. data/ext/carray_order.c +230 -10
  85. data/ext/carray_random.c +22 -19
  86. data/ext/carray_scatter.c +24 -7
  87. data/ext/carray_slab.c +128 -29
  88. data/ext/carray_sort.c +224 -16
  89. data/ext/carray_sort_kernel.c +5 -7
  90. data/ext/carray_struct.c +35 -43
  91. data/ext/carray_test.c +74 -19
  92. data/ext/carray_time_text.c +6 -4
  93. data/ext/carray_utils.c +63 -21
  94. data/ext/extconf.rb +11 -0
  95. data/ext/mkkernel.rb +546 -199
  96. data/ext/ruby_carray.c +4 -1
  97. data/ext/version.h +4 -4
  98. data/lib/carray/attribute.rb +42 -5
  99. data/lib/carray/autoload_carray.rb +3 -3
  100. data/lib/carray/autoload_method_extension.rb +5 -3
  101. data/lib/carray/axis_group.rb +13 -7
  102. data/lib/carray/basics.rb +28 -8
  103. data/lib/carray/block_iterator.rb +17 -3
  104. data/lib/carray/categorical.rb +35 -23
  105. data/lib/carray/categorical_iterator.rb +8 -8
  106. data/lib/carray/complex.rb +2 -2
  107. data/lib/carray/conditional.rb +3 -2
  108. data/lib/carray/const_string.rb +10 -0
  109. data/lib/carray/data_type_extension.rb +8 -1
  110. data/lib/carray/fixlen_string.rb +41 -2
  111. data/lib/carray/frame/column_verbs.rb +48 -16
  112. data/lib/carray/frame/concat.rb +17 -3
  113. data/lib/carray/frame/convert.rb +19 -0
  114. data/lib/carray/frame/csv_parser.rb +32 -32
  115. data/lib/carray/frame/describe.rb +4 -2
  116. data/lib/carray/frame/frame.rb +405 -85
  117. data/lib/carray/frame/group.rb +777 -64
  118. data/lib/carray/frame/io.rb +212 -72
  119. data/lib/carray/frame/join.rb +146 -37
  120. data/lib/carray/frame/records.rb +3 -3
  121. data/lib/carray/frame/reshape.rb +89 -11
  122. data/lib/carray/frame/sort.rb +57 -9
  123. data/lib/carray/frame/verbs.rb +384 -49
  124. data/lib/carray/frame/windows.rb +44 -6
  125. data/lib/carray/fuse_source.rb +31 -2
  126. data/lib/carray/inspect.rb +7 -2
  127. data/lib/carray/iterator.rb +32 -0
  128. data/lib/carray/lazy.rb +52 -8
  129. data/lib/carray/mask_gap_fill.rb +37 -17
  130. data/lib/carray/meld_reduce.rb +1 -1
  131. data/lib/carray/methods/choose.rb +3 -2
  132. data/lib/carray/methods/is_in.rb +8 -1
  133. data/lib/carray/methods/meshgrid.rb +3 -1
  134. data/lib/carray/runtime.rb +47 -8
  135. data/lib/carray/segment_iterator.rb +35 -16
  136. data/lib/carray/serialize.rb +53 -19
  137. data/lib/carray/stack.rb +33 -18
  138. data/lib/carray/string.rb +8 -0
  139. data/lib/carray/string_operation_extension.rb +13 -5
  140. data/lib/carray/struct_builder.rb +5 -3
  141. data/lib/carray/time.rb +112 -68
  142. data/lib/carray/window_iterator.rb +16 -4
  143. data/yard-stubs/ca_obj_array.rb +26 -1
  144. data/yard-stubs/ca_obj_transpose.rb +5 -4
  145. data/yard-stubs/carray_attribute.rb +3 -1
  146. data/yard-stubs/carray_cast.rb +10 -5
  147. data/yard-stubs/carray_conversion.rb +13 -3
  148. data/yard-stubs/carray_element.rb +7 -6
  149. data/yard-stubs/carray_mask.rb +28 -4
  150. data/yard-stubs/carray_math.rb +45 -20
  151. data/yard-stubs/carray_memory_view.rb +7 -0
  152. data/yard-stubs/carray_order.rb +100 -4
  153. data/yard-stubs/carray_scatter.rb +7 -5
  154. data/yard-stubs/carray_test.rb +7 -4
  155. metadata +18 -4
data/ext/carray_struct.c CHANGED
@@ -194,20 +194,40 @@ ca_struct_write_primitive_fast (char *p, int type_code, VALUE val)
194
194
  to keep neighbouring bits untouched.
195
195
  --------------------------------------------------------------------- */
196
196
 
197
+ /* The word's bytes are read and written least significant first on
198
+ every host: the layout numbers the bits byte by byte from the record's
199
+ start, so a field read through a wider word than its neighbour still
200
+ sees the same bits. */
201
+ static uint64_t
202
+ ca_struct_load_le (const char *p, int n)
203
+ {
204
+ uint64_t word = 0;
205
+ int i;
206
+ for (i = 0; i < n; i++) {
207
+ word |= (uint64_t) (uint8_t) p[i] << (8 * i);
208
+ }
209
+ return word;
210
+ }
211
+
212
+ static void
213
+ ca_struct_store_le (char *p, int n, uint64_t word)
214
+ {
215
+ int i;
216
+ for (i = 0; i < n; i++) {
217
+ p[i] = (char) (word >> (8 * i));
218
+ }
219
+ }
220
+
197
221
  static VALUE
198
222
  ca_struct_read_bitfield_fast (const char *p, int view_bytes,
199
223
  int bit_in_word, int bits)
200
224
  {
201
- uint64_t word = 0;
202
- switch (view_bytes) {
203
- case 1: { uint8_t v; memcpy(&v, p, 1); word = v; break; }
204
- case 2: { uint16_t v; memcpy(&v, p, 2); word = v; break; }
205
- case 4: { uint32_t v; memcpy(&v, p, 4); word = v; break; }
206
- case 8: { uint64_t v; memcpy(&v, p, 8); word = v; break; }
207
- default:
225
+ uint64_t word;
226
+ if ( view_bytes != 1 && view_bytes != 2 && view_bytes != 4 && view_bytes != 8 ) {
208
227
  rb_raise(rb_eRuntimeError,
209
228
  "[BUG] bitfield view_bytes %d not in {1,2,4,8}", view_bytes);
210
229
  }
230
+ word = ca_struct_load_le(p, view_bytes);
211
231
  uint64_t mask = (bits >= 64) ? ~(uint64_t)0
212
232
  : ((uint64_t)1 << bits) - 1;
213
233
  uint64_t val = (word >> bit_in_word) & mask;
@@ -224,7 +244,11 @@ static void
224
244
  ca_struct_write_bitfield_fast (char *p, int view_bytes,
225
245
  int bit_in_word, int bits, VALUE val)
226
246
  {
227
- uint64_t v;
247
+ uint64_t v, word;
248
+ if ( view_bytes != 1 && view_bytes != 2 && view_bytes != 4 && view_bytes != 8 ) {
249
+ rb_raise(rb_eRuntimeError,
250
+ "[BUG] bitfield view_bytes %d not in {1,2,4,8}", view_bytes);
251
+ }
228
252
  if ( bits == 1 ) {
229
253
  if ( val == Qtrue || val == INT2FIX(1) ) v = 1;
230
254
  else if ( val == Qfalse || val == INT2FIX(0) ) v = 0;
@@ -240,41 +264,9 @@ ca_struct_write_bitfield_fast (char *p, int view_bytes,
240
264
  uint64_t mask = (bits >= 64) ? ~(uint64_t)0
241
265
  : ((uint64_t)1 << bits) - 1;
242
266
  v &= mask;
243
- uint64_t shifted_mask = mask << bit_in_word;
244
- uint64_t shifted_val = v << bit_in_word;
245
- switch (view_bytes) {
246
- case 1: {
247
- uint8_t word;
248
- memcpy(&word, p, 1);
249
- word = (uint8_t)((word & ~(uint8_t)shifted_mask) | (uint8_t)shifted_val);
250
- memcpy(p, &word, 1);
251
- return;
252
- }
253
- case 2: {
254
- uint16_t word;
255
- memcpy(&word, p, 2);
256
- word = (uint16_t)((word & ~(uint16_t)shifted_mask) | (uint16_t)shifted_val);
257
- memcpy(p, &word, 2);
258
- return;
259
- }
260
- case 4: {
261
- uint32_t word;
262
- memcpy(&word, p, 4);
263
- word = (uint32_t)((word & ~(uint32_t)shifted_mask) | (uint32_t)shifted_val);
264
- memcpy(p, &word, 4);
265
- return;
266
- }
267
- case 8: {
268
- uint64_t word;
269
- memcpy(&word, p, 8);
270
- word = (word & ~shifted_mask) | shifted_val;
271
- memcpy(p, &word, 8);
272
- return;
273
- }
274
- default:
275
- rb_raise(rb_eRuntimeError,
276
- "[BUG] bitfield view_bytes %d not in {1,2,4,8}", view_bytes);
277
- }
267
+ word = ca_struct_load_le(p, view_bytes);
268
+ word = (word & ~(mask << bit_in_word)) | (v << bit_in_word);
269
+ ca_struct_store_le(p, view_bytes, word);
278
270
  }
279
271
 
280
272
  /* ---------------------------------------------------------------------
data/ext/carray_test.c CHANGED
@@ -274,10 +274,23 @@ eql_## type (const char *a, const char *b, ca_size_t bytes) \
274
274
  return ( *(const type*)a == *(const type*)b ); \
275
275
  }
276
276
 
277
+ /* == compares contents: a NaN equals a NaN (whatever its payload), as the
278
+ same array read back or copied holds the same NaN; -0.0 equals 0.0 as
279
+ values do. eql? stays bitwise. */
280
+ static int
281
+ ca_object_cells_equal (VALUE x, VALUE y)
282
+ {
283
+ if ( RB_FLOAT_TYPE_P(x) && RB_FLOAT_TYPE_P(y) &&
284
+ isnan(RFLOAT_VALUE(x)) && isnan(RFLOAT_VALUE(y)) ) {
285
+ return 1;
286
+ }
287
+ return RTEST(rb_funcall(x, id_equal, 1, y)) ? 1 : 0;
288
+ }
289
+
277
290
  static int
278
291
  eql_VALUE (const char *a, const char *b, ca_size_t bytes)
279
292
  {
280
- return RTEST(rb_funcall(*(const VALUE*)a, id_equal, 1, *(const VALUE*)b)) ? 1 : 0;
293
+ return ca_object_cells_equal(*(const VALUE*)a, *(const VALUE*)b);
281
294
  }
282
295
 
283
296
  static int
@@ -295,10 +308,28 @@ eql_type(int32_t)
295
308
  eql_type(uint32_t)
296
309
  eql_type(int64_t)
297
310
  eql_type(uint64_t)
298
- eql_type(float32_t)
299
- eql_type(float64_t)
300
- eql_type(cmplx64_t)
301
- eql_type(cmplx128_t)
311
+ #define eql_float(type) \
312
+ static int \
313
+ eql_## type (const char *a, const char *b, ca_size_t bytes) \
314
+ { \
315
+ type x = *(const type*)a, y = *(const type*)b; \
316
+ return ( x == y ) || ( isnan(x) && isnan(y) ); \
317
+ }
318
+
319
+ #define eql_cmplx(type, part) \
320
+ static int \
321
+ eql_## type (const char *a, const char *b, ca_size_t bytes) \
322
+ { \
323
+ type x = *(const type*)a, y = *(const type*)b; \
324
+ part xr = creal(x), xi = cimag(x), yr = creal(y), yi = cimag(y); \
325
+ return ( ( xr == yr ) || ( isnan(xr) && isnan(yr) ) ) && \
326
+ ( ( xi == yi ) || ( isnan(xi) && isnan(yi) ) ); \
327
+ }
328
+
329
+ eql_float(float32_t)
330
+ eql_float(float64_t)
331
+ eql_cmplx(cmplx64_t, float)
332
+ eql_cmplx(cmplx128_t, double)
302
333
 
303
334
  /* Placeholder for CA_FLOAT128 / CA_CMPLX256 slots (ca_valid[12] =
304
335
  ca_valid[15] = 0, so never invoked). */
@@ -371,8 +402,8 @@ ca_compare_body (VALUE arg)
371
402
  comparison is bitwise (= NaN.eql?(NaN) yields true since their bit
372
403
  patterns match), so memcmp gives the right semantics. For non-strict
373
404
  (==) of numeric data_types, type-equality is byte-equality EXCEPT for
374
- floats where -0.0 == +0.0 (different bit pattern, same value) and
375
- NaN != NaN (same bit pattern, unequal value) -- so we must keep the
405
+ floats where -0.0 == +0.0 (different bit pattern, same value) and any
406
+ NaN equals any NaN (payloads may differ) -- so we must keep the
376
407
  per-element comparator there.
377
408
 
378
409
  Integer / bool / fixlen data_types are byte-exact, so memcmp matches both
@@ -380,13 +411,14 @@ ca_compare_body (VALUE arg)
380
411
  the comparator. */
381
412
  if ( ! masked_a && ! masked_b ) {
382
413
  if ( is_object ) {
383
- ID id = strict ? id_eql : id_equal;
384
414
  VALUE *va = (VALUE *) pa;
385
415
  VALUE *vb = (VALUE *) pb;
386
416
  for (i=0; i<ca->elements; i++) {
387
417
  volatile VALUE x = va[i];
388
418
  volatile VALUE y = vb[i];
389
- if ( ! RTEST(rb_funcall(x, id, 1, y)) ) {
419
+ int eq = strict ? RTEST(rb_funcall(x, id_eql, 1, y))
420
+ : ca_object_cells_equal(x, y);
421
+ if ( ! eq ) {
390
422
  flag = 0;
391
423
  break;
392
424
  }
@@ -427,8 +459,8 @@ ca_compare_body (VALUE arg)
427
459
  if ( is_object ) {
428
460
  volatile VALUE x = *(VALUE *) pa;
429
461
  volatile VALUE y = *(VALUE *) pb;
430
- ID id = strict ? id_eql : id_equal;
431
- eq = RTEST(rb_funcall(x, id, 1, y));
462
+ eq = strict ? RTEST(rb_funcall(x, id_eql, 1, y))
463
+ : ca_object_cells_equal(x, y);
432
464
  }
433
465
  else if ( strict
434
466
  || ca->data_type <= CA_UINT64
@@ -451,6 +483,19 @@ ca_compare_body (VALUE arg)
451
483
  return Qnil;
452
484
  }
453
485
 
486
+ typedef struct {
487
+ CArray **list;
488
+ ca_compare_ctx_t *c;
489
+ } ca_compare_run_t;
490
+
491
+ static VALUE
492
+ ca_compare_run (VALUE arg)
493
+ {
494
+ ca_compare_run_t *r = (ca_compare_run_t *) arg;
495
+ ca_attach_window(2, r->list, "rr", ca_compare_body, (VALUE) r->c);
496
+ return Qnil;
497
+ }
498
+
454
499
  /* ca_compare_common(a, b, strict) — shared body of ca_equal (==) and
455
500
  * ca_eql_strict (eql?). Returns 1 if arrays match under the selected
456
501
  * semantics, else 0.
@@ -461,7 +506,7 @@ ca_compare_body (VALUE arg)
461
506
  * four masked/unmasked combinations.
462
507
  *
463
508
  * The only difference is the per-element comparator:
464
- * == : ca_eql[data_type] (type-specific, so float NaN != NaN)
509
+ * == : ca_eql[data_type] (by value: NaN equals NaN, -0.0 equals 0.0)
465
510
  * eql?: memcmp for numeric, rb_funcall(:eql?) for CA_OBJECT
466
511
  *
467
512
  * The unmasked-unmasked branch takes a bulk memcmp or per-cell
@@ -503,8 +548,14 @@ ca_compare_common (void *ap, void *bp, int strict)
503
548
  c.flag = 1;
504
549
  list[0] = ca;
505
550
  list[1] = cb;
506
- /* The object lane calls #== / #eql? with both arrays attached. */
507
- ca_attach_window(2, list, "rr", ca_compare_body, (VALUE) &c);
551
+ /* The object lane calls #== / #eql? with both arrays attached, and
552
+ recorded as compared over (ca_computing_run), so a cell holding one
553
+ of them is refused as cyclic rather than compared again without
554
+ end. */
555
+ ca_compare_run_t r;
556
+ r.list = list;
557
+ r.c = &c;
558
+ ca_computing_run(2, list, ca_compare_run, (VALUE) &r);
508
559
  return c.flag;
509
560
  }
510
561
  }
@@ -522,9 +573,10 @@ ca_eql_storage (void *ap, void *bp)
522
573
  }
523
574
 
524
575
  /* CArray#==(other) — elementwise equality returning true / false.
525
- * data_class, data_type, shape, and mask state must all match; NaN
526
- * compares unequal per IEEE semantics. Non-CArray other returns
527
- * false. */
576
+ * data_class, data_type, shape, and mask state must all match. It asks
577
+ * whether the contents are the same, so a NaN equals a NaN and -0.0
578
+ * equals 0.0; the IEEE comparison of each element is #eq. Non-CArray
579
+ * other returns false. */
528
580
  static VALUE
529
581
  rb_ca_equal (VALUE self, VALUE other)
530
582
  {
@@ -795,7 +847,8 @@ without_read_only_ensure (VALUE arg)
795
847
  * the bridge owns.
796
848
  *
797
849
  * Mechanics: clears CA_FLAG_READ_ONLY on self and on its mask for the
798
- * duration of the block; rb_ensure restores both on normal return AND on
850
+ * duration of the block, except on a mask that is a wrap: that mask borrows
851
+ * its bits from a producer (wrap_memory_view's mask:), which set the flag; rb_ensure restores both on normal return AND on
799
852
  * raise, setting the flag on whatever mask self has by then. (A read-only
800
853
  * array's mask carries the flag once CArray#mask has handed it out.) Frozen
801
854
  * objects still raise FrozenError. Does not walk parent chains.
@@ -822,7 +875,9 @@ rb_ca_without_read_only_flag (VALUE self)
822
875
  s.was_readonly = ca_test_flag(ca, CA_FLAG_READ_ONLY);
823
876
  if (s.was_readonly) {
824
877
  ca_unset_flag(ca, CA_FLAG_READ_ONLY);
825
- if (ca->mask) {
878
+ /* A mask that is a wrap borrows its bits (the mask: of
879
+ wrap_memory_view); its flag is the lender's and stays. */
880
+ if (ca->mask && ca->mask->obj_type != CA_OBJ_ARRAY_WRAP) {
826
881
  ca_unset_flag(ca->mask, CA_FLAG_READ_ONLY);
827
882
  }
828
883
  }
@@ -234,7 +234,9 @@ ca_time_text_read (VALUE str, ca_time_text_t *t, int wide)
234
234
  }
235
235
  }
236
236
  }
237
- if ( hour > 24 || min > 59 || sec > 59 ) {
237
+ /* Second 60 is the leap second; it carries into the next minute below,
238
+ as CArray.time reads it. */
239
+ if ( hour > 24 || min > 59 || sec > 60 ) {
238
240
  return CA_TIME_TEXT_UNREADABLE;
239
241
  }
240
242
  /* Hour 24 is the end of the day, 24:00:00, and nothing past it, as in
@@ -359,7 +361,7 @@ rb_ca_parse_time_text (int argc, VALUE *argv, VALUE self)
359
361
  boolean8_t *m_in, *m_out;
360
362
  int64_t *days, *nsod, *months, *ticks;
361
363
  int64_t per_day = 1, ns_per_tick = 1;
362
- ID unit;
364
+ ID unit = 0;
363
365
  int clock = 0, fraction = 0, coarse = 2, any = 0, wide;
364
366
  int calendar = 0; /* 1 ticks of months, 2 of years */
365
367
  ca_size_t i;
@@ -381,7 +383,7 @@ rb_ca_parse_time_text (int argc, VALUE *argv, VALUE self)
381
383
  if ( ca->data_type != CA_OBJECT ) {
382
384
  rb_raise(rb_eArgError, "time text reading needs an object array");
383
385
  }
384
- if ( ! ca_is_entity(ca) ) {
386
+ if ( ! ca_is_entity(ca) || ! ca->ptr ) {
385
387
  src = rb_ca_copy(src);
386
388
  TypedData_Get_Struct(src, CArray, &carray_data_type, ca);
387
389
  }
@@ -1075,7 +1077,7 @@ rb_ca_strptime_fields (VALUE self, VALUE rformat, VALUE rstrict)
1075
1077
  if ( ca->data_type != CA_OBJECT ) {
1076
1078
  rb_raise(rb_eArgError, "strptime reading needs an object array");
1077
1079
  }
1078
- if ( ! ca_is_entity(ca) ) {
1080
+ if ( ! ca_is_entity(ca) || ! ca->ptr ) {
1079
1081
  src = rb_ca_copy(src);
1080
1082
  TypedData_Get_Struct(src, CArray, &carray_data_type, ca);
1081
1083
  }
data/ext/carray_utils.c CHANGED
@@ -371,6 +371,10 @@ ca_parse_range_without_check (VALUE arg, ca_size_t size,
371
371
  last = NUM2SIZE(x.end);
372
372
  excl = RTEST(x.exclude_end);
373
373
  step = NUM2SIZE(x.step);
374
+ if ( step == 0 ) {
375
+ rb_raise(rb_eArgError, "%s: step should not be 0",
376
+ ca_calling_method_name());
377
+ }
374
378
  if ( excl ) {
375
379
  last += ( (last>=start) ? -1 : 1 );
376
380
  }
@@ -391,6 +395,10 @@ ca_parse_range_without_check (VALUE arg, ca_size_t size,
391
395
  if ( NIL_P(arg0) ) { /* [nil,k] */
392
396
  start = 0;
393
397
  step = NUM2SIZE(arg1);
398
+ if ( step == 0 ) {
399
+ rb_raise(rb_eArgError, "%s: step should not be 0",
400
+ ca_calling_method_name());
401
+ }
394
402
  count = (size-1)/llabs(step) + 1;
395
403
  *poffset = start;
396
404
  *pcount = count;
@@ -401,6 +409,10 @@ ca_parse_range_without_check (VALUE arg, ca_size_t size,
401
409
  last = NUM2SIZE(RANGE_END(arg0));
402
410
  excl = RTEST(RANGE_EXCL(arg0));
403
411
  step = NUM2SIZE(arg1);
412
+ if ( step == 0 ) {
413
+ rb_raise(rb_eArgError, "%s: step should not be 0",
414
+ ca_calling_method_name());
415
+ }
404
416
  if ( excl ) {
405
417
  last += ( (last>=start) ? -1 : 1 );
406
418
  }
@@ -418,6 +430,10 @@ ca_parse_range_without_check (VALUE arg, ca_size_t size,
418
430
  last = NUM2SIZE(x.end);
419
431
  excl = RTEST(x.exclude_end);
420
432
  step = NUM2SIZE(x.step);
433
+ if ( step == 0 ) {
434
+ rb_raise(rb_eArgError, "%s: step should not be 0",
435
+ ca_calling_method_name());
436
+ }
421
437
  if ( excl ) {
422
438
  last += ( (last>=start) ? -1 : 1 );
423
439
  }
@@ -922,6 +938,15 @@ ca_reduce_fill (VALUE result, VALUE fill, int whole)
922
938
  if ( ca_has_mask(cr) ) {
923
939
  result = rb_ca_mask_fill_copy(result, fill);
924
940
  }
941
+ else if ( cr->elements > 0 ) {
942
+ /* No cell needed the fill, but a fill the result cannot hold is
943
+ refused all the same, not only when some cell happens to be
944
+ undefined: it goes into a one-cell array of the result's kind. */
945
+ volatile VALUE one = rb_funcall(rb_funcall(result, rb_intern("flatten"), 0),
946
+ rb_intern("[]"), 1,
947
+ rb_range_new(INT2FIX(0), INT2FIX(0), 0));
948
+ rb_ca_store_addr(rb_ca_template(one), 0, fill);
949
+ }
925
950
  return whole ? rb_ca_fetch_addr(result, 0) : result;
926
951
  }
927
952
  return ( result == CA_UNDEF ) ? fill : result;
@@ -1253,11 +1278,36 @@ rb_hash_has_key(VALUE hash, VALUE key)
1253
1278
  return Qfalse;
1254
1279
  }
1255
1280
 
1281
+ /* The next comma-separated key of a spec, its length in *len, or NULL
1282
+ past the last key. The spec is read in place: a copy to cut up would
1283
+ be a C allocation, and its failure has nowhere to go but to drop every
1284
+ keyword. */
1285
+ static const char *
1286
+ option_key (const char **sp, long *len)
1287
+ {
1288
+ const char *p = *sp, *end;
1289
+
1290
+ if ( p == NULL ) {
1291
+ return NULL;
1292
+ }
1293
+ end = strchr(p, ',');
1294
+ if ( end ) {
1295
+ *len = end - p;
1296
+ *sp = end + 1;
1297
+ }
1298
+ else {
1299
+ *len = (long) strlen(p);
1300
+ *sp = NULL;
1301
+ }
1302
+ return p;
1303
+ }
1304
+
1256
1305
  void
1257
1306
  rb_scan_options (VALUE ropt, const char *spec_in, ...)
1258
1307
  {
1259
1308
  VALUE *vp;
1260
- char *sp, *tok, *spec;
1309
+ const char *sp, *tok;
1310
+ long len;
1261
1311
  int has_option = 0;
1262
1312
  va_list vals;
1263
1313
 
@@ -1272,22 +1322,19 @@ rb_scan_options (VALUE ropt, const char *spec_in, ...)
1272
1322
  }
1273
1323
 
1274
1324
  va_start(vals, spec_in);
1275
- sp = spec = strdup(spec_in);
1276
- tok = strsep1(&sp, ',');
1277
- while ( tok != NULL ) {
1325
+ sp = spec_in;
1326
+ while ( ( tok = option_key(&sp, &len) ) != NULL ) {
1278
1327
  vp = va_arg(vals, VALUE*);
1279
1328
  if ( has_option ) {
1280
- VALUE key = ID2SYM(rb_intern(tok));
1329
+ VALUE key = ID2SYM(rb_intern2(tok, len));
1281
1330
  if ( rb_hash_has_key(ropt, key) ) {
1282
1331
  *vp = rb_hash_aref(ropt, key);
1283
1332
  }
1284
1333
  /* key absent: leave *vp unchanged (caller's pre-initialized default) */
1285
1334
  }
1286
1335
  /* opts == Qnil: leave all *vp unchanged */
1287
- tok = strsep1(&sp, ',');
1288
1336
  }
1289
1337
  va_end(vals);
1290
- free(spec);
1291
1338
 
1292
1339
  /* Strict: reject any key not named in spec, matching Ruby's native
1293
1340
  keyword-argument behaviour ("unknown keyword: :foo"). Every call
@@ -1300,15 +1347,12 @@ rb_scan_options (VALUE ropt, const char *spec_in, ...)
1300
1347
  VALUE key = rb_ary_entry(keys, i);
1301
1348
  VALUE kname = SYMBOL_P(key) ? rb_sym2str(key) : rb_obj_as_string(key);
1302
1349
  const char *kn = StringValueCStr(kname);
1303
- char *sp2, *tok2, *spec2;
1350
+ long kn_len = RSTRING_LEN(kname);
1304
1351
  int found = 0;
1305
- sp2 = spec2 = strdup(spec_in);
1306
- tok2 = strsep1(&sp2, ',');
1307
- while ( tok2 != NULL ) {
1308
- if ( strcmp(tok2, kn) == 0 ) { found = 1; break; }
1309
- tok2 = strsep1(&sp2, ',');
1352
+ sp = spec_in;
1353
+ while ( ( tok = option_key(&sp, &len) ) != NULL ) {
1354
+ if ( len == kn_len && memcmp(tok, kn, len) == 0 ) { found = 1; break; }
1310
1355
  }
1311
- free(spec2);
1312
1356
  if ( ! found ) {
1313
1357
  rb_raise(rb_eArgError, "unknown keyword: %"PRIsVALUE, rb_inspect(key));
1314
1358
  }
@@ -1341,7 +1385,8 @@ void
1341
1385
  rb_set_options (VALUE ropt, const char *spec_in, ...)
1342
1386
  {
1343
1387
  VALUE rval;
1344
- char *sp, *tok, *spec;
1388
+ const char *sp, *tok;
1389
+ long len;
1345
1390
  int has_option = 0;
1346
1391
  va_list vals;
1347
1392
 
@@ -1356,17 +1401,14 @@ rb_set_options (VALUE ropt, const char *spec_in, ...)
1356
1401
  }
1357
1402
 
1358
1403
  va_start(vals, spec_in);
1359
- sp = spec = strdup(spec_in);
1360
- tok = strsep1(&sp, ',');
1361
- while ( tok != NULL ) {
1404
+ sp = spec_in;
1405
+ while ( ( tok = option_key(&sp, &len) ) != NULL ) {
1362
1406
  rval = va_arg(vals, VALUE);
1363
1407
  if ( has_option ) {
1364
- rb_hash_aset(ropt, ID2SYM(rb_intern(tok)), rval);
1408
+ rb_hash_aset(ropt, ID2SYM(rb_intern2(tok, len)), rval);
1365
1409
  }
1366
- tok = strsep1(&sp, ',');
1367
1410
  }
1368
1411
  va_end(vals);
1369
- free(spec);
1370
1412
 
1371
1413
  return;
1372
1414
  }
data/ext/extconf.rb CHANGED
@@ -118,6 +118,17 @@ if try_compile("int main(void) { return 0; }", "-fwrapv")
118
118
  $carray_build_flags << "-fwrapv"
119
119
  end
120
120
 
121
+ # --- math functions do not set errno (`-fno-math-errno`)
122
+ #
123
+ # Nothing in carray reads errno after a math call. With errno kept, GCC
124
+ # must call sqrt() for every cell instead of using the vector square root,
125
+ # so `sqrt` ran 2-4x slower than it could. The values do not change.
126
+ # Apple clang already defaults to this.
127
+ if try_compile("int main(void) { return 0; }", "-fno-math-errno")
128
+ $CFLAGS += " -fno-math-errno"
129
+ $carray_build_flags << "-fno-math-errno"
130
+ end
131
+
121
132
  # --- probe for `-fopenmp-simd` (SL.1.0, PROPOSAL_REDUCTION_SIMD_LICENSE)
122
133
  #
123
134
  # `-fopenmp-simd` enables `#pragma omp simd` (and its `reduction(+:acc)`