carray 3.0.5 → 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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +233 -0
  3. data/README.md +1 -1
  4. data/carray.gemspec +3 -2
  5. data/ext/ca_axis_dispatch.c +6 -0
  6. data/ext/ca_axis_group.c +1 -1
  7. data/ext/ca_categorical_iterator.c +7 -7
  8. data/ext/ca_kernel_iterator.c +116 -231
  9. data/ext/ca_kernel_iterator.h +410 -254
  10. data/ext/ca_obj_bincmp.c +9 -1
  11. data/ext/ca_obj_binop.c +10 -1
  12. data/ext/ca_obj_bitfield.c +2 -0
  13. data/ext/ca_obj_byte_swap.c +2 -0
  14. data/ext/ca_obj_face.c +2 -0
  15. data/ext/ca_obj_fake.c +3 -1
  16. data/ext/ca_obj_grid.c +167 -12
  17. data/ext/ca_obj_meld.c +69 -113
  18. data/ext/ca_obj_moncmp.c +9 -1
  19. data/ext/ca_obj_monop.c +40 -2
  20. data/ext/ca_obj_object.c +54 -20
  21. data/ext/ca_obj_reduce.c +15 -1
  22. data/ext/ca_obj_refer.c +65 -20
  23. data/ext/ca_obj_remap.c +69 -10
  24. data/ext/ca_obj_roll.c +1 -0
  25. data/ext/ca_obj_select.c +4 -1
  26. data/ext/ca_obj_select_axis.c +1 -0
  27. data/ext/ca_obj_shift.c +1 -0
  28. data/ext/ca_obj_stack.c +310 -111
  29. data/ext/ca_obj_stride.c +131 -45
  30. data/ext/ca_obj_string.c +18 -2
  31. data/ext/ca_obj_tile.c +1 -0
  32. data/ext/ca_obj_triop.c +9 -1
  33. data/ext/ca_obj_window.c +1 -0
  34. data/ext/ca_op_ipower.c +8 -8
  35. data/ext/caframe_csv_reader.c +1 -1
  36. data/ext/carray.h +2 -1
  37. data/ext/carray_access.c +27 -64
  38. data/ext/carray_address_basis.c +134 -0
  39. data/ext/carray_attribute.c +8 -0
  40. data/ext/carray_broadcast.c +3 -4
  41. data/ext/carray_build_flags.h +1 -1
  42. data/ext/carray_cast.c +8 -11
  43. data/ext/carray_conversion.c +9 -5
  44. data/ext/carray_core.c +811 -72
  45. data/ext/carray_element.c +13 -9
  46. data/ext/carray_histogram.c +25 -3
  47. data/ext/carray_internal.h +147 -12
  48. data/ext/carray_kernels_bincmp.c +2007 -3
  49. data/ext/carray_kernels_binop.c +25365 -5199
  50. data/ext/carray_kernels_init.c +8 -8
  51. data/ext/carray_kernels_monop.c +10696 -3678
  52. data/ext/carray_kernels_reduce_aggregate.c +4429 -702
  53. data/ext/carray_kernels_reduce_boolean.c +1533 -15
  54. data/ext/carray_kernels_reduce_cumulative.c +1808 -248
  55. data/ext/carray_kernels_reduce_extreme.c +4508 -511
  56. data/ext/carray_kernels_reduce_variance.c +2284 -152
  57. data/ext/carray_kernels_scan.c +240 -240
  58. data/ext/carray_kernels_search.c +4211 -264
  59. data/ext/carray_kernels_sort.c +367 -450
  60. data/ext/carray_lazy.c +47 -0
  61. data/ext/carray_loop.c +1 -1
  62. data/ext/carray_mask.c +130 -139
  63. data/ext/carray_median_percentile.c +4 -4
  64. data/ext/carray_memory_view.c +13 -7
  65. data/ext/carray_operator.c +147 -44
  66. data/ext/carray_order.c +3 -2
  67. data/ext/carray_random.c +4 -1
  68. data/ext/carray_slab.c +97 -21
  69. data/ext/carray_sort.c +1 -1
  70. data/ext/carray_test.c +74 -19
  71. data/ext/carray_time_text.c +2 -2
  72. data/ext/extconf.rb +11 -0
  73. data/ext/mkkernel.rb +346 -94
  74. data/ext/ruby_carray.c +4 -1
  75. data/ext/version.h +4 -4
  76. data/lib/carray/attribute.rb +25 -1
  77. data/lib/carray/autoload_carray.rb +3 -3
  78. data/lib/carray/autoload_method_extension.rb +5 -3
  79. data/lib/carray/axis_group.rb +13 -7
  80. data/lib/carray/basics.rb +1 -1
  81. data/lib/carray/block_iterator.rb +17 -3
  82. data/lib/carray/categorical.rb +6 -8
  83. data/lib/carray/categorical_iterator.rb +2 -2
  84. data/lib/carray/complex.rb +2 -2
  85. data/lib/carray/conditional.rb +3 -2
  86. data/lib/carray/frame/column_verbs.rb +45 -14
  87. data/lib/carray/frame/convert.rb +19 -0
  88. data/lib/carray/frame/csv_parser.rb +32 -32
  89. data/lib/carray/frame/describe.rb +4 -2
  90. data/lib/carray/frame/frame.rb +399 -78
  91. data/lib/carray/frame/group.rb +679 -53
  92. data/lib/carray/frame/io.rb +209 -71
  93. data/lib/carray/frame/join.rb +146 -37
  94. data/lib/carray/frame/records.rb +3 -3
  95. data/lib/carray/frame/reshape.rb +89 -11
  96. data/lib/carray/frame/sort.rb +57 -9
  97. data/lib/carray/frame/verbs.rb +384 -49
  98. data/lib/carray/frame/windows.rb +44 -6
  99. data/lib/carray/inspect.rb +7 -2
  100. data/lib/carray/iterator.rb +32 -0
  101. data/lib/carray/lazy.rb +17 -8
  102. data/lib/carray/mask_gap_fill.rb +37 -17
  103. data/lib/carray/meld_reduce.rb +1 -1
  104. data/lib/carray/methods/choose.rb +3 -2
  105. data/lib/carray/methods/meshgrid.rb +3 -1
  106. data/lib/carray/runtime.rb +47 -8
  107. data/lib/carray/segment_iterator.rb +23 -8
  108. data/lib/carray/serialize.rb +34 -17
  109. data/lib/carray/stack.rb +18 -6
  110. data/lib/carray/time.rb +5 -0
  111. data/lib/carray/window_iterator.rb +16 -4
  112. data/yard-stubs/ca_obj_array.rb +26 -1
  113. data/yard-stubs/carray_cast.rb +8 -5
  114. data/yard-stubs/carray_conversion.rb +8 -2
  115. data/yard-stubs/carray_mask.rb +20 -1
  116. data/yard-stubs/carray_math.rb +31 -16
  117. data/yard-stubs/carray_memory_view.rb +7 -0
  118. data/yard-stubs/carray_test.rb +7 -4
  119. metadata +18 -4
@@ -218,7 +218,7 @@ ca_chunked_run_acquire (ca_chunked_run_t *run, int k)
218
218
  }
219
219
 
220
220
  static VALUE
221
- ca_chunked_run_body (VALUE arg)
221
+ ca_chunked_run_loop (VALUE arg)
222
222
  {
223
223
  ca_chunked_run_t *run = (ca_chunked_run_t *) arg;
224
224
  char *p[CA_CHUNKED_MAX_OPERANDS];
@@ -245,6 +245,15 @@ ca_chunked_run_body (VALUE arg)
245
245
  return Qnil;
246
246
  }
247
247
 
248
+ /* The object arrays among the operands are recorded as computed over
249
+ (ca_computing_run) while the run reads them. */
250
+ static VALUE
251
+ ca_chunked_run_body (VALUE arg)
252
+ {
253
+ ca_chunked_run_t *run = (ca_chunked_run_t *) arg;
254
+ return ca_computing_run(run->n_op, run->ca, ca_chunked_run_loop, arg);
255
+ }
256
+
248
257
  static VALUE
249
258
  ca_chunked_run_release (VALUE arg)
250
259
  {
@@ -450,13 +459,21 @@ typedef struct {
450
459
  int n;
451
460
  CArray **held;
452
461
  const char *mode; /* per held array: 'r' read, 'w' written in place */
462
+ CArray **ops; /* every operand, attached or gathered */
453
463
  } ca_op_guard_t;
454
464
 
465
+ static VALUE
466
+ ca_op_guard_run (VALUE varg)
467
+ {
468
+ ca_op_guard_t *g = (ca_op_guard_t *) varg;
469
+ return g->run(g->arg);
470
+ }
471
+
455
472
  static VALUE
456
473
  ca_op_guard_body (VALUE varg)
457
474
  {
458
475
  ca_op_guard_t *g = (ca_op_guard_t *) varg;
459
- g->run(g->arg);
476
+ ca_computing_run(g->n, g->ops, ca_op_guard_run, varg);
460
477
  g->done = 1;
461
478
  return Qnil;
462
479
  }
@@ -496,10 +513,13 @@ ca_op_guard_release (VALUE varg)
496
513
  }
497
514
 
498
515
  /* Runs the kernel packed in `call` with `held` attached; see above. NULL
499
- entries in held are skipped. */
516
+ entries in held are skipped. ops lists every operand the kernel reads
517
+ or writes, attached or gathered into a buffer, so that the object
518
+ arrays among them are recorded as computed over (ca_computing_run)
519
+ while the kernel runs; NULL means the operands are the held arrays. */
500
520
  static void
501
521
  ca_op_guarded (VALUE (*run)(VALUE), void *call,
502
- int n, CArray **held, const char *mode)
522
+ int n, CArray **held, const char *mode, CArray **ops)
503
523
  {
504
524
  ca_op_guard_t g;
505
525
  g.run = run;
@@ -508,6 +528,7 @@ ca_op_guarded (VALUE (*run)(VALUE), void *call,
508
528
  g.n = n;
509
529
  g.held = held;
510
530
  g.mode = mode;
531
+ g.ops = ops ? ops : held;
511
532
  rb_ensure(ca_op_guard_body, (VALUE) &g, ca_op_guard_release, (VALUE) &g);
512
533
  }
513
534
 
@@ -556,6 +577,13 @@ ca_mask_overlay_or_body (VALUE arg)
556
577
  Each operand other than a scalar has ca_out's number of elements
557
578
  (ArgumentError otherwise); at most 8 operands.
558
579
 
580
+ A bang operation's ca_out can share its root with an operand, so its
581
+ mask buffer may be the root's mask that a later operand's mask reads
582
+ (`r.fma!(m, r.flip(0))`). When more than one operand is read and one
583
+ of them shares ca_out's root, the operands' masks are folded into a
584
+ scratch first and ORed into ca_out's mask at the end, so every operand
585
+ is read before ca_out's mask changes.
586
+
559
587
  This gathers the full mask once; a per-chunk mask gather inside the
560
588
  chunked branch is a possible future optimisation. */
561
589
  void
@@ -563,7 +591,7 @@ ca_mask_overlay_safe (CArray *ca_out, int n, ...)
563
591
  {
564
592
  va_list ap;
565
593
  CArray *slist[8];
566
- int i, any_mask = 0;
594
+ int i, any_mask = 0, n_read = 0, shares = 0;
567
595
 
568
596
  if ( n < 0 || n > 8 ) {
569
597
  rb_raise(rb_eRuntimeError, "ca_mask_overlay_safe: n out of range");
@@ -589,23 +617,46 @@ ca_mask_overlay_safe (CArray *ca_out, int n, ...)
589
617
 
590
618
  if ( ! any_mask ) return;
591
619
 
620
+ for ( i = 0; i < n; i++ ) {
621
+ CArray *cs = slist[i];
622
+ if ( ! cs || cs == ca_out ) {
623
+ continue;
624
+ }
625
+ n_read++;
626
+ if ( ca_shares_root(ca_out, cs) ) {
627
+ shares = 1;
628
+ }
629
+ }
630
+
592
631
  ca_update_mask(ca_out);
593
632
  {
594
633
  int created_new = 0;
634
+ volatile VALUE hacc = 0;
635
+ boolean8_t *mout, *ma;
636
+ ca_size_t elements = ca_out->elements;
637
+
595
638
  if ( ! ca_out->mask ) {
596
639
  ca_create_mask(ca_out);
597
640
  created_new = 1;
598
641
  }
599
642
 
600
- boolean8_t *ma = (boolean8_t *) ca_out->mask->ptr;
601
- ca_size_t elements = ca_out->elements;
643
+ mout = (boolean8_t *) ca_out->mask->ptr;
644
+ if ( n_read > 1 && shares ) {
645
+ ma = ALLOCV_N(boolean8_t, hacc, elements);
646
+ memset(ma, 0, elements);
647
+ }
648
+ else {
649
+ ma = mout;
650
+ }
602
651
 
603
652
  /* Fresh mask → zero-init. Existing mask (= bang variant where
604
653
  ca_out IS one of the operands) → preserve as initial accumulator
605
654
  (= ca_out's own contribution is already in `ma`, OR in others).
606
655
  This is structurally equivalent to ca_copy_mask_overlay_n's
607
656
  behavior which OR'd into existing mask without clearing. */
608
- if ( created_new ) memset(ma, 0, elements);
657
+ if ( created_new ) {
658
+ memset(mout, 0, elements);
659
+ }
609
660
 
610
661
  for ( i = 0; i < n; i++ ) {
611
662
  CArray *cs = slist[i];
@@ -627,6 +678,14 @@ ca_mask_overlay_safe (CArray *ca_out, int n, ...)
627
678
  ca_lazy_arena_release_held, (VALUE) &o);
628
679
  }
629
680
  }
681
+
682
+ if ( ma != mout ) {
683
+ ca_size_t j;
684
+ for (j=0; j<elements; j++) {
685
+ mout[j] |= ma[j];
686
+ }
687
+ ALLOCV_END(hacc);
688
+ }
630
689
  }
631
690
  }
632
691
 
@@ -707,7 +766,7 @@ rb_ca_call_monop (VALUE self, ca_monop_func_t func[])
707
766
  ca_attach(ca1); /* window: the kernel runs through ca_op_guarded when it can raise */
708
767
  k.p1 = ca1->ptr; k.i1 = 1;
709
768
  if ( ca_monop_may_raise(func, ca1->data_type) ) {
710
- ca_op_guarded(ca_monop_call, &k, 1, &ca1, "r");
769
+ ca_op_guarded(ca_monop_call, &k, 1, &ca1, "r", NULL);
711
770
  }
712
771
  else {
713
772
  ca_monop_call((VALUE) &k);
@@ -724,7 +783,7 @@ rb_ca_call_monop (VALUE self, ca_monop_func_t func[])
724
783
  int strided_path_done = 0;
725
784
  {
726
785
  extern ca_operation_function_t ca_stride_func;
727
- if ( ca_func[ca1->obj_type].attach == ca_stride_func.attach ) {
786
+ if ( ca1->data_type != CA_OBJECT && ca_func[ca1->obj_type].attach == ca_stride_func.attach ) {
728
787
  CAStride *cs1 = (CAStride *) ca1;
729
788
  CArray *root1;
730
789
  ca_size_t strides1[CA_RANK_MAX];
@@ -804,10 +863,21 @@ rb_ca_call_monop (VALUE self, ca_monop_func_t func[])
804
863
  (void) h1;
805
864
  p1 = ALLOCV_N(char, h1, ca1->elements * ca1->bytes);
806
865
  ca_xfer_all(ca1, p1, CA_XFER_GET);
807
- func[ca1->data_type](ca1->elements,
808
- ( ca2->mask ) ? (boolean8_t *)ca2->mask->ptr : NULL,
809
- p1, 1,
810
- ca2->ptr, 1);
866
+ {
867
+ ca_monop_call_t k;
868
+ CArray *none[1] = { NULL };
869
+ k.f = func[ca1->data_type];
870
+ k.n = ca1->elements;
871
+ k.m = ( ca2->mask ) ? (boolean8_t *)ca2->mask->ptr : NULL;
872
+ k.p1 = p1; k.i1 = 1;
873
+ k.p2 = ca2->ptr; k.i2 = 1;
874
+ if ( ca_monop_may_raise(func, ca1->data_type) ) {
875
+ ca_op_guarded(ca_monop_call, &k, 1, none, "r", &ca1);
876
+ }
877
+ else {
878
+ ca_monop_call((VALUE) &k);
879
+ }
880
+ }
811
881
  ALLOCV_END(h1);
812
882
  }
813
883
  }
@@ -838,7 +908,7 @@ rb_ca_call_monop_bang (VALUE self, ca_monop_func_t func[])
838
908
  k.p1 = ca1->ptr; k.i1 = 1;
839
909
  k.p2 = ca1->ptr; k.i2 = 1;
840
910
  if ( ca_monop_may_raise(func, ca1->data_type) ) {
841
- ca_op_guarded(ca_monop_call, &k, 1, &ca1, "w");
911
+ ca_op_guarded(ca_monop_call, &k, 1, &ca1, "w", NULL);
842
912
  }
843
913
  else {
844
914
  ca_monop_call((VALUE) &k);
@@ -889,7 +959,7 @@ rb_ca_call_monop_typed (VALUE self, ca_monop_func_t func[],
889
959
  ca_attach(ca1); /* window: the kernel runs through ca_op_guarded when it can raise */
890
960
  k.p1 = ca1->ptr; k.i1 = 1;
891
961
  if ( ca_monop_may_raise(func, ca1->data_type) ) {
892
- ca_op_guarded(ca_monop_call, &k, 1, &ca1, "r");
962
+ ca_op_guarded(ca_monop_call, &k, 1, &ca1, "r", NULL);
893
963
  }
894
964
  else {
895
965
  ca_monop_call((VALUE) &k);
@@ -903,7 +973,7 @@ rb_ca_call_monop_typed (VALUE self, ca_monop_func_t func[],
903
973
  int strided_path_done = 0;
904
974
  {
905
975
  extern ca_operation_function_t ca_stride_func;
906
- if ( ca_func[ca1->obj_type].attach == ca_stride_func.attach ) {
976
+ if ( ca1->data_type != CA_OBJECT && ca_func[ca1->obj_type].attach == ca_stride_func.attach ) {
907
977
  CAStride *cs1 = (CAStride *) ca1;
908
978
  CArray *root1;
909
979
  ca_size_t strides1[CA_RANK_MAX];
@@ -982,10 +1052,21 @@ rb_ca_call_monop_typed (VALUE self, ca_monop_func_t func[],
982
1052
  (void) h1;
983
1053
  p1 = ALLOCV_N(char, h1, ca1->elements * ca1->bytes);
984
1054
  ca_xfer_all(ca1, p1, CA_XFER_GET);
985
- func[ca1->data_type](ca1->elements,
986
- ( ca2->mask ) ? (boolean8_t *)ca2->mask->ptr : NULL,
987
- p1, 1,
988
- ca2->ptr, 1);
1055
+ {
1056
+ ca_monop_call_t k;
1057
+ CArray *none[1] = { NULL };
1058
+ k.f = func[ca1->data_type];
1059
+ k.n = ca1->elements;
1060
+ k.m = ( ca2->mask ) ? (boolean8_t *)ca2->mask->ptr : NULL;
1061
+ k.p1 = p1; k.i1 = 1;
1062
+ k.p2 = ca2->ptr; k.i2 = 1;
1063
+ if ( ca_monop_may_raise(func, ca1->data_type) ) {
1064
+ ca_op_guarded(ca_monop_call, &k, 1, none, "r", &ca1);
1065
+ }
1066
+ else {
1067
+ ca_monop_call((VALUE) &k);
1068
+ }
1069
+ }
989
1070
  ALLOCV_END(h1);
990
1071
  }
991
1072
  }
@@ -1286,7 +1367,7 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
1286
1367
  k.p2 = ca2->ptr; k.i2 = i2;
1287
1368
  if ( ca_binop_may_raise(func, ca1->data_type) ) {
1288
1369
  held[0] = ca1; held[1] = ca2;
1289
- ca_op_guarded(ca_binop_call, &k, 2, held, "rr");
1370
+ ca_op_guarded(ca_binop_call, &k, 2, held, "rr", NULL);
1290
1371
  }
1291
1372
  else {
1292
1373
  ca_binop_call((VALUE) &k);
@@ -1305,11 +1386,22 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
1305
1386
 
1306
1387
  p_shared = ALLOCV_N(char, h_shared, ca1->elements * ca1->bytes);
1307
1388
  ca_xfer_all(ca1, p_shared, CA_XFER_GET);
1308
- func[ca1->data_type](n_kernel,
1309
- ( ca3->mask ) ? (boolean8_t *) ca3->mask->ptr : NULL,
1310
- p_shared, i1,
1311
- p_shared, i2,
1312
- ca3->ptr, i3);
1389
+ {
1390
+ ca_binop_call_t k;
1391
+ CArray *none[2] = { NULL, NULL };
1392
+ k.f = func[ca1->data_type];
1393
+ k.n = n_kernel;
1394
+ k.m = ( ca3->mask ) ? (boolean8_t *) ca3->mask->ptr : NULL;
1395
+ k.p1 = p_shared; k.i1 = i1;
1396
+ k.p2 = p_shared; k.i2 = i2;
1397
+ k.p3 = ca3->ptr; k.i3 = i3;
1398
+ if ( ca_binop_may_raise(func, ca1->data_type) ) {
1399
+ ca_op_guarded(ca_binop_call, &k, 2, none, "rr", (CArray *[]){ ca1, ca2 });
1400
+ }
1401
+ else {
1402
+ ca_binop_call((VALUE) &k);
1403
+ }
1404
+ }
1313
1405
  ALLOCV_END(h_shared);
1314
1406
  }
1315
1407
  else {
@@ -1353,7 +1445,7 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
1353
1445
  fallback (dead on the success path) but fire for views outside the
1354
1446
  CAStride+entity family (CASelect / CAGrid / CAReduce
1355
1447
  etc.). */
1356
- if ( i1 == 1 && i2 == 1 && nonalias_arrays >= 1 ) {
1448
+ if ( dt != CA_OBJECT && i1 == 1 && i2 == 1 && nonalias_arrays >= 1 ) {
1357
1449
  extern ca_operation_function_t ca_stride_func;
1358
1450
  CArray *root1 = NULL, *root2 = NULL;
1359
1451
  ca_size_t strides1[CA_RANK_MAX], strides2[CA_RANK_MAX];
@@ -1505,7 +1597,7 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
1505
1597
  if ( ca_binop_may_raise(func, dt) ) {
1506
1598
  held[0] = a1 ? ca1 : NULL;
1507
1599
  held[1] = a2 ? ca2 : NULL;
1508
- ca_op_guarded(ca_binop_call, &k, 2, held, "rr");
1600
+ ca_op_guarded(ca_binop_call, &k, 2, held, "rr", (CArray *[]){ ca1, ca2 });
1509
1601
  }
1510
1602
  else {
1511
1603
  ca_binop_call((VALUE) &k);
@@ -1639,7 +1731,7 @@ rb_ca_call_binop_bang (VALUE self, VALUE other, ca_binop_func_t func[])
1639
1731
  if ( ca_binop_may_raise(func, ca1->data_type) ) {
1640
1732
  held[0] = ca1;
1641
1733
  held[1] = a2 ? ca2 : NULL;
1642
- ca_op_guarded(ca_binop_call, &k, 2, held, "wr");
1734
+ ca_op_guarded(ca_binop_call, &k, 2, held, "wr", (CArray *[]){ ca1, ca2 });
1643
1735
  }
1644
1736
  else {
1645
1737
  ca_binop_call((VALUE) &k);
@@ -1866,7 +1958,7 @@ rb_ca_call_triop (VALUE self, VALUE other2, VALUE other3,
1866
1958
  held[0] = attached1 ? ca1 : NULL;
1867
1959
  held[1] = attached2 ? ca2 : NULL;
1868
1960
  held[2] = attached3 ? ca3 : NULL;
1869
- ca_op_guarded(ca_triop_call, &k, 3, held, "rrr");
1961
+ ca_op_guarded(ca_triop_call, &k, 3, held, "rrr", (CArray *[]){ ca1, ca2, ca3 });
1870
1962
  }
1871
1963
  else {
1872
1964
  ca_triop_call((VALUE) &k);
@@ -2032,7 +2124,7 @@ rb_ca_call_triop_bang (VALUE self, VALUE other2, VALUE other3,
2032
2124
  held[0] = ca1;
2033
2125
  held[1] = attached2 ? ca2 : NULL;
2034
2126
  held[2] = attached3 ? ca3 : NULL;
2035
- ca_op_guarded(ca_triop_call, &k, 3, held, "wrr");
2127
+ ca_op_guarded(ca_triop_call, &k, 3, held, "wrr", (CArray *[]){ ca1, ca2, ca3 });
2036
2128
  }
2037
2129
  else {
2038
2130
  ca_triop_call((VALUE) &k);
@@ -2077,7 +2169,7 @@ rb_ca_call_triop_bang (VALUE self, VALUE other2, VALUE other3,
2077
2169
  ca_chunked_run_add(&run, ca3, s3);
2078
2170
  run.kernel = ca_triop_chunk_kernel;
2079
2171
  run.data = &d;
2080
- ca_op_guarded(ca_triop_bang_chunked, &run, 1, &ca1, "w");
2172
+ ca_op_guarded(ca_triop_bang_chunked, &run, 1, &ca1, "w", NULL);
2081
2173
  }
2082
2174
  }
2083
2175
  }
@@ -2141,8 +2233,9 @@ rb_ca_call_moncmp (VALUE self, ca_moncmp_func_t func[])
2141
2233
  k.m = ( ca2->mask ) ? (boolean8_t *) ca2->mask->ptr : NULL;
2142
2234
  k.p1 = p1; k.i1 = 1;
2143
2235
  k.p2 = (boolean8_t *) ca2->ptr; k.i2 = 1;
2144
- if ( attached1 && ca_moncmp_may_raise(func, ca1->data_type) ) {
2145
- ca_op_guarded(ca_moncmp_call, &k, 1, &ca1, "r");
2236
+ if ( ca_moncmp_may_raise(func, ca1->data_type) ) {
2237
+ CArray *none[1] = { NULL };
2238
+ ca_op_guarded(ca_moncmp_call, &k, 1, attached1 ? &ca1 : none, "r", &ca1);
2146
2239
  }
2147
2240
  else {
2148
2241
  ca_moncmp_call((VALUE) &k);
@@ -2431,12 +2524,23 @@ rb_ca_call_bincmp (volatile VALUE self, volatile VALUE other,
2431
2524
  (void) h_shared;
2432
2525
  p_shared = ALLOCV_N(char, h_shared, ca1->elements * ca1->bytes);
2433
2526
  ca_xfer_all(ca1, p_shared, CA_XFER_GET);
2434
- func[ca1->data_type](n_kernel,
2435
- ( ca3->mask ) ? (boolean8_t *) ca3->mask->ptr : NULL,
2436
- p_shared, ca1->bytes, i1,
2437
- p_shared, ca2->bytes, i2,
2438
- ca3->ptr, ca3->bytes, i3,
2439
- tol);
2527
+ {
2528
+ ca_bincmp_call_t k;
2529
+ CArray *none[2] = { NULL, NULL };
2530
+ k.f = func[ca1->data_type];
2531
+ k.n = n_kernel;
2532
+ k.m = ( ca3->mask ) ? (boolean8_t *) ca3->mask->ptr : NULL;
2533
+ k.p1 = p_shared; k.b1 = ca1->bytes; k.i1 = i1;
2534
+ k.p2 = p_shared; k.b2 = ca2->bytes; k.i2 = i2;
2535
+ k.p3 = ca3->ptr; k.b3 = ca3->bytes; k.i3 = i3;
2536
+ k.tol = tol;
2537
+ if ( ca_bincmp_may_raise(func, ca1->data_type) ) {
2538
+ ca_op_guarded(ca_bincmp_call, &k, 2, none, "rr", (CArray *[]){ ca1, ca2 });
2539
+ }
2540
+ else {
2541
+ ca_bincmp_call((VALUE) &k);
2542
+ }
2543
+ }
2440
2544
  ALLOCV_END(h_shared);
2441
2545
  }
2442
2546
  else {
@@ -2470,11 +2574,10 @@ rb_ca_call_bincmp (volatile VALUE self, volatile VALUE other,
2470
2574
  k.p2 = p2; k.b2 = ca2->bytes; k.i2 = i2;
2471
2575
  k.p3 = ca3->ptr; k.b3 = ca3->bytes; k.i3 = i3;
2472
2576
  k.tol = tol;
2473
- if ( ( attached1 || attached2 ) &&
2474
- ca_bincmp_may_raise(func, ca1->data_type) ) {
2577
+ if ( ca_bincmp_may_raise(func, ca1->data_type) ) {
2475
2578
  held[0] = attached1 ? ca1 : NULL;
2476
2579
  held[1] = attached2 ? ca2 : NULL;
2477
- ca_op_guarded(ca_bincmp_call, &k, 2, held, "rr");
2580
+ ca_op_guarded(ca_bincmp_call, &k, 2, held, "rr", (CArray *[]){ ca1, ca2 });
2478
2581
  }
2479
2582
  else {
2480
2583
  ca_bincmp_call((VALUE) &k);
data/ext/carray_order.c CHANGED
@@ -899,8 +899,9 @@ ca_topk_stable (VALUE self, VALUE picked, long axis, int desc)
899
899
  for (k = 0; k < ndim; k++) inv[NUM2INT(perm[k])] = INT2FIX(k);
900
900
  }
901
901
  /* An entity whose fibers already lie along its last axis is read in
902
- place; anything else is copied into that layout. */
903
- if ( last && ca_is_entity(cv) ) {
902
+ place; anything else is copied into that layout, as is an entity
903
+ that leaves ptr NULL at rest (a CASource). */
904
+ if ( last && ca_is_entity(cv) && cv->ptr ) {
904
905
  vals = key;
905
906
  ca_update_mask(cv);
906
907
  }
data/ext/carray_random.c CHANGED
@@ -525,7 +525,10 @@ ca_randomn_bang_fill (VALUE arg)
525
525
  CA_U(double) *p = (CA_U(double) *)ca->ptr;
526
526
  ca_size_t pairs = n / 2;
527
527
  for (i = 0; i < pairs; i++) {
528
- box_muller_pair(&c->source, &p[2*i], &p[2*i+1]);
528
+ double r1, r2;
529
+ box_muller_pair(&c->source, &r1, &r2);
530
+ p[2*i] = r1;
531
+ p[2*i+1] = r2;
529
532
  }
530
533
  if (n % 2 == 1) {
531
534
  double r1, r2;
data/ext/carray_slab.c CHANGED
@@ -13,7 +13,8 @@
13
13
 
14
14
  Internals:
15
15
  - ca_slab_iter_state_t (see carray_slab.h) is the per-call state struct.
16
- - Each entry point stack-allocates a zeroed state, populates it via
16
+ - Each entry point heap-allocates a zeroed state under a window guard
17
+ (slab_state_new), populates it via
17
18
  slab_state_init (= nofail; only validation + scalar field fills),
18
19
  then rb_ensure-runs slab_state_run_body (= acquires T1 substrate +
19
20
  scratch + output, runs per-form loop) and slab_state_finish (= frees
@@ -21,9 +22,8 @@
21
22
  - All heap acquisition lives in run_body so init-time rb_raise paths
22
23
  cannot leak; rb_ensure guarantees finish on both normal and
23
24
  exception exit.
24
- - Conservative stack scan keeps the state struct's VALUE fields alive
25
- across rb_yield; no dmark callback is required because the state is
26
- not Ruby-wrapped.
25
+ - The guard marks the state's VALUE fields, and runs slab_state_finish
26
+ from a finalizer if the block's Fiber is abandoned mid-walk.
27
27
 
28
28
  --------------------------------------------------------------------------- */
29
29
 
@@ -122,9 +122,9 @@ slab_state_init (ca_slab_iter_state_t *st, VALUE source, int8_t form,
122
122
  {
123
123
  CArray *src;
124
124
 
125
- /* Caller stack-allocates with `{0}` which sets VALUE fields to Qfalse
126
- (= 0). Body code uses `== Qnil` to distinguish unset slots from
127
- populated CArray handles, so we must promote them to Qnil here. */
125
+ /* The state arrives zeroed, which leaves VALUE fields Qfalse (= 0).
126
+ Body code uses `== Qnil` to distinguish unset slots from populated
127
+ CArray handles, so we must promote them to Qnil here. */
128
128
  st->self = Qnil;
129
129
  st->slab_view = Qnil;
130
130
  st->carrier = Qnil;
@@ -526,8 +526,12 @@ ca_slab_advance_slab_view (ca_slab_iter_state_t *st,
526
526
  if ( slab_cs->mask != NULL ) {
527
527
  CAStride *mask_cs = (CAStride *) slab_cs->mask;
528
528
  if ( m_in != NULL ) {
529
+ /* m_in points into the walk's copy of the source's mask, not into
530
+ the source's mask itself, so the offset is taken from the data
531
+ cell: it is what the slab reads once the copy is gone. */
529
532
  mask_cs->ptr = (char *) m_in;
530
- mask_cs->base_offset = (ca_size_t) ((char *) m_in - src->mask->ptr);
533
+ mask_cs->base_offset = (ca_size_t) ((p_in - src->ptr) / src->bytes)
534
+ * src->mask->bytes;
531
535
  }
532
536
  else {
533
537
  /* The walk was opened on a source without a mask and the block
@@ -1202,7 +1206,9 @@ slab_state_run_body (VALUE arg)
1202
1206
  held by the carrier, and the internal output view lets go of its
1203
1207
  buffer. The copy is made into the array ca_slab_prepare_backing set
1204
1208
  aside, so nothing here allocates. A slab that is a window onto the
1205
- source itself points into the source and needs nothing. */
1209
+ source keeps reading the source's data, but its mask points into the
1210
+ walk's copy of the mask, which finish frees: it lets go of that copy
1211
+ and reads the source's mask from then on. */
1206
1212
  static void
1207
1213
  slab_state_release_views (ca_slab_iter_state_t *st)
1208
1214
  {
@@ -1211,6 +1217,13 @@ slab_state_release_views (ca_slab_iter_state_t *st)
1211
1217
  TypedData_Get_Struct(st->output_slab_view, CArray, &carray_data_type, ov);
1212
1218
  ov->ptr = NULL;
1213
1219
  }
1220
+ if ( st->carrier == Qnil && st->slab_view != Qnil ) {
1221
+ CArray *cs;
1222
+ TypedData_Get_Struct(st->slab_view, CArray, &carray_data_type, cs);
1223
+ if ( cs->mask ) {
1224
+ cs->mask->ptr = NULL;
1225
+ }
1226
+ }
1214
1227
  if ( st->carrier != Qnil && st->slab_view != Qnil ) {
1215
1228
  CArray *cw, *cs, *backing;
1216
1229
  volatile VALUE bv;
@@ -1275,6 +1288,72 @@ slab_state_finish (VALUE arg)
1275
1288
  /* Entry points: CArray#map_slab / #reduce_slab / #each_slab */
1276
1289
  /* ------------------------------------------------------------------- */
1277
1290
 
1291
+ /* The walk yields to the block with its windows open. The state lives on
1292
+ the heap under a window guard, so that if the block's Fiber is abandoned
1293
+ (an Enumerator stopped with #next and dropped) the guard's finalizer
1294
+ still runs slab_state_finish and the source is let go. */
1295
+
1296
+ static void
1297
+ slab_state_mark (void *p)
1298
+ {
1299
+ ca_slab_iter_state_t *st = (ca_slab_iter_state_t *) p;
1300
+ rb_gc_mark(st->self);
1301
+ rb_gc_mark(st->slab_view);
1302
+ rb_gc_mark(st->carrier);
1303
+ rb_gc_mark(st->mask_carrier);
1304
+ rb_gc_mark(st->output);
1305
+ rb_gc_mark(st->output_slab_view);
1306
+ rb_gc_mark(st->init_val);
1307
+ }
1308
+
1309
+ static void
1310
+ slab_state_finish_abandoned (void *p)
1311
+ {
1312
+ slab_state_finish((VALUE)(uintptr_t) p);
1313
+ }
1314
+
1315
+ typedef struct {
1316
+ ca_slab_iter_state_t *st;
1317
+ VALUE guard;
1318
+ VALUE sentinel;
1319
+ } slab_run_t;
1320
+
1321
+ static VALUE
1322
+ slab_run_body (VALUE arg)
1323
+ {
1324
+ return slab_state_run_body((VALUE)(uintptr_t) ((slab_run_t *) arg)->st);
1325
+ }
1326
+
1327
+ static VALUE
1328
+ slab_run_close (VALUE arg)
1329
+ {
1330
+ slab_run_t *r = (slab_run_t *) arg;
1331
+ ca_window_guard_disarm(r->guard, r->sentinel);
1332
+ return slab_state_finish((VALUE)(uintptr_t) r->st);
1333
+ }
1334
+
1335
+ /* Allocates the state; slab_state_init fills it, then slab_run walks. */
1336
+ static ca_slab_iter_state_t *
1337
+ slab_state_new (slab_run_t *r)
1338
+ {
1339
+ ca_slab_iter_state_t *st = ZALLOC(ca_slab_iter_state_t);
1340
+ st->self = st->slab_view = st->carrier = st->mask_carrier = Qnil;
1341
+ st->output = st->output_slab_view = st->init_val = Qnil;
1342
+ r->st = st;
1343
+ r->guard = ca_window_guard_arm(slab_state_finish_abandoned, slab_state_mark,
1344
+ st, &r->sentinel);
1345
+ return st;
1346
+ }
1347
+
1348
+ static VALUE
1349
+ slab_run (slab_run_t *r)
1350
+ {
1351
+ VALUE result = rb_ensure(slab_run_body, (VALUE) r, slab_run_close, (VALUE) r);
1352
+ RB_GC_GUARD(r->guard);
1353
+ RB_GC_GUARD(r->sentinel);
1354
+ return result;
1355
+ }
1356
+
1278
1357
  /* Shared kwarg parse: returns axis (required) + data_type / init via
1279
1358
  out-params. init_out is set to Qundef if the `:init` key was absent
1280
1359
  (= caller distinguishes per-slab vs per-element reduce forms). */
@@ -1322,7 +1401,7 @@ slab_extract_kwargs (int argc, VALUE *argv, const char *method_name)
1322
1401
  static VALUE
1323
1402
  rb_ca_map_slab (int argc, VALUE *argv, VALUE self)
1324
1403
  {
1325
- ca_slab_iter_state_t st = {0};
1404
+ slab_run_t r;
1326
1405
  VALUE kw, axis, data_type, init_unused;
1327
1406
  VALUE result;
1328
1407
 
@@ -1335,10 +1414,9 @@ rb_ca_map_slab (int argc, VALUE *argv, VALUE self)
1335
1414
  rb_raise(rb_eLocalJumpError, "CArray#map_slab: block required");
1336
1415
  }
1337
1416
 
1338
- slab_state_init(&st, self, CA_SLAB_FORM_MAP, axis, data_type, Qundef);
1417
+ slab_state_init(slab_state_new(&r), self, CA_SLAB_FORM_MAP, axis, data_type, Qundef);
1339
1418
 
1340
- result = rb_ensure(slab_state_run_body, (VALUE)(uintptr_t)&st,
1341
- slab_state_finish, (VALUE)(uintptr_t)&st);
1419
+ result = slab_run(&r);
1342
1420
  RB_GC_GUARD(self);
1343
1421
  return result;
1344
1422
  }
@@ -1346,7 +1424,7 @@ rb_ca_map_slab (int argc, VALUE *argv, VALUE self)
1346
1424
  static VALUE
1347
1425
  rb_ca_reduce_slab (int argc, VALUE *argv, VALUE self)
1348
1426
  {
1349
- ca_slab_iter_state_t st = {0};
1427
+ slab_run_t r;
1350
1428
  VALUE kw, axis, data_type, init_val;
1351
1429
  VALUE result;
1352
1430
  int8_t form;
@@ -1364,10 +1442,9 @@ rb_ca_reduce_slab (int argc, VALUE *argv, VALUE self)
1364
1442
  form = (init_val == Qundef) ? CA_SLAB_FORM_REDUCE_SLAB
1365
1443
  : CA_SLAB_FORM_REDUCE_FIBER;
1366
1444
 
1367
- slab_state_init(&st, self, form, axis, data_type, init_val);
1445
+ slab_state_init(slab_state_new(&r), self, form, axis, data_type, init_val);
1368
1446
 
1369
- result = rb_ensure(slab_state_run_body, (VALUE)(uintptr_t)&st,
1370
- slab_state_finish, (VALUE)(uintptr_t)&st);
1447
+ result = slab_run(&r);
1371
1448
  RB_GC_GUARD(self);
1372
1449
  return result;
1373
1450
  }
@@ -1375,7 +1452,7 @@ rb_ca_reduce_slab (int argc, VALUE *argv, VALUE self)
1375
1452
  static VALUE
1376
1453
  rb_ca_each_slab (int argc, VALUE *argv, VALUE self)
1377
1454
  {
1378
- ca_slab_iter_state_t st = {0};
1455
+ slab_run_t r;
1379
1456
  VALUE kw, axis, data_type_unused, init_unused;
1380
1457
  VALUE result;
1381
1458
 
@@ -1397,10 +1474,9 @@ rb_ca_each_slab (int argc, VALUE *argv, VALUE self)
1397
1474
  2, args, RB_PASS_KEYWORDS);
1398
1475
  }
1399
1476
 
1400
- slab_state_init(&st, self, CA_SLAB_FORM_EACH, axis, Qnil, Qundef);
1477
+ slab_state_init(slab_state_new(&r), self, CA_SLAB_FORM_EACH, axis, Qnil, Qundef);
1401
1478
 
1402
- result = rb_ensure(slab_state_run_body, (VALUE)(uintptr_t)&st,
1403
- slab_state_finish, (VALUE)(uintptr_t)&st);
1479
+ result = slab_run(&r);
1404
1480
  RB_GC_GUARD(self);
1405
1481
  return result;
1406
1482
  }
data/ext/carray_sort.c CHANGED
@@ -156,7 +156,7 @@ static void \
156
156
  msd_refine_##SUFFIX (CArray *key, ca_size_t *perm, void *vbuf, \
157
157
  msd_runs_t *in, msd_runs_t *out, int masked_last) \
158
158
  { \
159
- const TYPE *ptr = (const TYPE *) key->ptr; \
159
+ const CA_U(TYPE) *ptr = (const CA_U(TYPE) *) key->ptr; \
160
160
  const boolean8_t *m = ( key->mask ) ? (boolean8_t *) key->mask->ptr : NULL; \
161
161
  ca_pair_##SUFFIX *buf = (ca_pair_##SUFFIX *) vbuf; \
162
162
  ca_size_t r; \