carray 3.0.6 → 3.0.7

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 (139) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +110 -0
  3. data/README.md +24 -3
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_group.c +6 -6
  6. data/ext/ca_binop_dispatch.c +8 -0
  7. data/ext/ca_binop_dispatch.h +1 -0
  8. data/ext/ca_categorical_iterator.c +7 -7
  9. data/ext/ca_kernel_iterator.h +6 -4
  10. data/ext/ca_obj_array.c +4 -4
  11. data/ext/ca_obj_bincmp.c +19 -3
  12. data/ext/ca_obj_binop.c +36 -11
  13. data/ext/ca_obj_bitarray.c +2 -4
  14. data/ext/ca_obj_bitfield.c +86 -4
  15. data/ext/ca_obj_byte_swap.c +2 -4
  16. data/ext/ca_obj_const_string.c +422 -257
  17. data/ext/ca_obj_face.c +71 -33
  18. data/ext/ca_obj_fake.c +112 -13
  19. data/ext/ca_obj_farray.c +7 -1
  20. data/ext/ca_obj_fixlen_string.c +51 -17
  21. data/ext/ca_obj_grid.c +12 -7
  22. data/ext/ca_obj_meld.c +5 -5
  23. data/ext/ca_obj_monop.c +65 -39
  24. data/ext/ca_obj_object.c +9 -9
  25. data/ext/ca_obj_reduce.c +2 -4
  26. data/ext/ca_obj_refer.c +104 -10
  27. data/ext/ca_obj_repeat.c +1 -1
  28. data/ext/ca_obj_roll.c +2 -4
  29. data/ext/ca_obj_select.c +8 -6
  30. data/ext/ca_obj_select_axis.c +8 -6
  31. data/ext/ca_obj_stack.c +5 -5
  32. data/ext/ca_obj_stride.c +46 -8
  33. data/ext/ca_obj_string.c +2 -2
  34. data/ext/ca_obj_tile.c +2 -4
  35. data/ext/ca_obj_triop.c +15 -12
  36. data/ext/ca_obj_window.c +22 -15
  37. data/ext/ca_op_ipower.c +63 -15
  38. data/ext/ca_op_powi.h +6 -29
  39. data/ext/ca_sort_kernels.h +3 -0
  40. data/ext/ca_sweep_engine.c +90 -23
  41. data/ext/ca_sweep_engine.h +5 -6
  42. data/ext/caframe_csv_reader.c +1 -1
  43. data/ext/carray.h +3 -1
  44. data/ext/carray_access.c +98 -39
  45. data/ext/carray_address_basis.c +98 -24
  46. data/ext/carray_broadcast.c +2 -2
  47. data/ext/carray_call_cfunc.c +5 -8
  48. data/ext/carray_cast.c +311 -37
  49. data/ext/carray_cast_func.rb +37 -5
  50. data/ext/carray_conversion.c +56 -2
  51. data/ext/carray_copy.c +24 -0
  52. data/ext/carray_core.c +69 -6
  53. data/ext/carray_count.c +28 -9
  54. data/ext/carray_element.c +9 -3
  55. data/ext/carray_factorize.c +11 -11
  56. data/ext/carray_histogram.c +2 -2
  57. data/ext/carray_index_classifier.c +7 -5
  58. data/ext/carray_internal.h +94 -0
  59. data/ext/carray_kernels_binop.c +55 -55
  60. data/ext/carray_kernels_init.c +14 -14
  61. data/ext/carray_kernels_monop.c +90 -140
  62. data/ext/carray_lazy.c +16 -1
  63. data/ext/carray_loop.c +9 -2
  64. data/ext/carray_mask.c +118 -11
  65. data/ext/carray_median_percentile.c +13 -8
  66. data/ext/carray_memory_view.c +41 -22
  67. data/ext/carray_operator.c +72 -11
  68. data/ext/carray_order.c +52 -9
  69. data/ext/carray_partition.c +1 -1
  70. data/ext/carray_random.c +1 -1
  71. data/ext/carray_round.c +24 -1
  72. data/ext/carray_scatter.c +4 -0
  73. data/ext/carray_segment.c +1 -1
  74. data/ext/carray_slab.c +13 -4
  75. data/ext/carray_sort.c +6 -3
  76. data/ext/carray_sort_kernel.c +24 -0
  77. data/ext/carray_struct.c +21 -6
  78. data/ext/carray_test.c +85 -39
  79. data/ext/carray_time_text.c +6 -0
  80. data/ext/carray_utils.c +124 -54
  81. data/ext/mk_call_cfunc.rb +6 -11
  82. data/ext/mkkernel.rb +37 -27
  83. data/ext/version.h +4 -4
  84. data/lib/carray/axis_group.rb +9 -6
  85. data/lib/carray/basics.rb +26 -8
  86. data/lib/carray/bincount_nd.rb +8 -2
  87. data/lib/carray/block_iterator.rb +48 -21
  88. data/lib/carray/categorical.rb +21 -8
  89. data/lib/carray/categorical_iterator.rb +2 -1
  90. data/lib/carray/conditional.rb +52 -4
  91. data/lib/carray/const_string.rb +74 -0
  92. data/lib/carray/construct.rb +2 -2
  93. data/lib/carray/core_extensions.rb +19 -68
  94. data/lib/carray/data_type_extension.rb +1 -0
  95. data/lib/carray/fixlen_string.rb +83 -1
  96. data/lib/carray/frame/column_verbs.rb +61 -6
  97. data/lib/carray/frame/concat.rb +50 -6
  98. data/lib/carray/frame/csv_parser.rb +13 -4
  99. data/lib/carray/frame/frame.rb +38 -6
  100. data/lib/carray/frame/group.rb +67 -40
  101. data/lib/carray/frame/io.rb +10 -5
  102. data/lib/carray/frame/join.rb +20 -6
  103. data/lib/carray/frame/records.rb +6 -4
  104. data/lib/carray/frame/reshape.rb +25 -5
  105. data/lib/carray/frame/verbs.rb +78 -25
  106. data/lib/carray/frame/windows.rb +30 -6
  107. data/lib/carray/fuse_source.rb +4 -2
  108. data/lib/carray/iterator.rb +8 -0
  109. data/lib/carray/key_windows.rb +10 -4
  110. data/lib/carray/lazy.rb +12 -17
  111. data/lib/carray/methods/bin.rb +3 -0
  112. data/lib/carray/methods/composition.rb +1 -1
  113. data/lib/carray/methods/factorize.rb +2 -0
  114. data/lib/carray/methods/gather_nd.rb +4 -1
  115. data/lib/carray/methods/is_in.rb +23 -3
  116. data/lib/carray/methods/locate_addr.rb +63 -12
  117. data/lib/carray/methods/repeat.rb +11 -1
  118. data/lib/carray/record_order.rb +35 -6
  119. data/lib/carray/runtime.rb +10 -0
  120. data/lib/carray/segment_iterator.rb +19 -16
  121. data/lib/carray/serialize.rb +22 -0
  122. data/lib/carray/slab_iterator.rb +1 -1
  123. data/lib/carray/stack.rb +3 -3
  124. data/lib/carray/string.rb +6 -6
  125. data/lib/carray/string_operation_extension.rb +25 -4
  126. data/lib/carray/struct_builder.rb +4 -1
  127. data/lib/carray/time.rb +190 -46
  128. data/lib/carray/window_iterator.rb +48 -12
  129. data/yard-stubs/ca_obj_bitfield.rb +4 -1
  130. data/yard-stubs/ca_obj_refer.rb +3 -0
  131. data/yard-stubs/ca_obj_window.rb +2 -1
  132. data/yard-stubs/carray_access.rb +4 -2
  133. data/yard-stubs/carray_cast.rb +6 -0
  134. data/yard-stubs/carray_count.rb +6 -3
  135. data/yard-stubs/carray_math.rb +5 -0
  136. data/yard-stubs/carray_order.rb +6 -0
  137. data/yard-stubs/carray_scatter.rb +2 -1
  138. data/yard-stubs/carray_test.rb +18 -5
  139. metadata +2 -2
data/ext/ca_obj_meld.c CHANGED
@@ -883,8 +883,8 @@ ca_meld_func_allocate (void *ap)
883
883
  {
884
884
  CAMeld *ca = (CAMeld *) ap;
885
885
  ca_meld_mask_refresh(ca);
886
- ca_attach_all(ca->parents, ca->n_parents);
887
- ca->ptr = ca_alloc_cells((CArray *) ca);
886
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, ca->n_parents,
887
+ ca->parents);
888
888
  }
889
889
 
890
890
  static VALUE
@@ -1129,7 +1129,7 @@ rb_ca_meld_new (VALUE parents_ary, int8_t meld_axis)
1129
1129
  }
1130
1130
  parents = ALLOCV_N(CArray *, holder, n);
1131
1131
  for ( i = 0; i < n; i++ ) {
1132
- VALUE p = rb_ary_entry(parents_ary, i);
1132
+ volatile VALUE p = rb_ary_entry(parents_ary, i);
1133
1133
  rb_check_carray_object(p);
1134
1134
  TypedData_Get_Struct(p, CArray, &carray_data_type, parents[i]);
1135
1135
  ca_multi_parent_check_part(parents[i], "meld");
@@ -1185,7 +1185,7 @@ rb_ca_meld_initialize (int argc, VALUE *argv, VALUE self)
1185
1185
  }
1186
1186
  if ( ! NIL_P(axis_val) ) {
1187
1187
  CArray *ref;
1188
- VALUE first = rb_ary_entry(list, 0);
1188
+ volatile VALUE first = rb_ary_entry(list, 0);
1189
1189
  rb_check_carray_object(first);
1190
1190
  TypedData_Get_Struct(first, CArray, &carray_data_type, ref);
1191
1191
  meld_axis = (int8_t) ca_normalize_axis_arg(
@@ -1195,7 +1195,7 @@ rb_ca_meld_initialize (int argc, VALUE *argv, VALUE self)
1195
1195
  ca_check_uninitialized(ca);
1196
1196
  parents = ALLOCV_N(CArray *, holder, n);
1197
1197
  for ( i = 0; i < n; i++ ) {
1198
- VALUE p = rb_ary_entry(list, i);
1198
+ volatile VALUE p = rb_ary_entry(list, i);
1199
1199
  rb_check_carray_object(p);
1200
1200
  TypedData_Get_Struct(p, CArray, &carray_data_type, parents[i]);
1201
1201
  ca_multi_parent_check_part(parents[i], "meld");
data/ext/ca_obj_monop.c CHANGED
@@ -64,6 +64,7 @@ extern int8_t CA_OBJ_LAZY_MARKER;
64
64
  extern int8_t CA_OBJ_BINOP;
65
65
  extern int8_t CA_OBJ_BINCMP;
66
66
  extern int8_t CA_OBJ_MONCMP;
67
+ extern int8_t CA_OBJ_TRIOP;
67
68
 
68
69
  typedef struct CAMonOp {
69
70
  int16_t obj_type;
@@ -169,6 +170,7 @@ ca_monop_view_is_single_cast (CArray *view)
169
170
  (so `1/zero` zero-check works). */
170
171
  return ( p->obj_type != CA_OBJ_MONOP
171
172
  && p->obj_type != CA_OBJ_BINOP
173
+ && p->obj_type != CA_OBJ_TRIOP
172
174
  && p->obj_type != CA_OBJ_BINCMP
173
175
  && p->obj_type != CA_OBJ_MONCMP
174
176
  && p->obj_type != CA_OBJ_LAZY_MARKER );
@@ -338,14 +340,17 @@ ca_size_t ca_monop_materialise_call_count = 0;
338
340
  #define CA_MAX_LAZY_DEPTH 256
339
341
  #endif
340
342
 
341
- /* Whether a cast from `from` to `to` raises on a NaN cell: a float or
342
- complex value has no integer for NaN. */
343
+ /* Whether a cast from `from` to `to` can raise on a cell, so that it has to
344
+ skip the masked ones, whose values are not defined: a float or complex
345
+ value has no integer for NaN, a boolean takes only 0 and 1, and an object
346
+ cell may hold anything. */
343
347
  static int
344
- ca_monop_cast_refuses_nan (int8_t from, int8_t to)
348
+ ca_monop_cast_can_raise (int8_t from, int8_t to)
345
349
  {
346
350
  int from_float = ( from == CA_FLOAT32 || from == CA_FLOAT64 ||
347
351
  from == CA_CMPLX64 || from == CA_CMPLX128 );
348
- return from_float && to >= CA_INT8 && to <= CA_UINT64;
352
+ return from == CA_OBJECT || to == CA_BOOLEAN
353
+ || ( from_float && to >= CA_INT8 && to <= CA_UINT64 );
349
354
  }
350
355
 
351
356
  /* Apply a single op in-place on `data` (src == dst, unit element stride).
@@ -384,7 +389,8 @@ ca_data_type_stub (int8_t data_type)
384
389
  return stub;
385
390
  }
386
391
 
387
- /* Pull leaf into the output buffer. Three paths:
392
+ /* Pull leaf into the output buffer. `mask` marks the cells a cast must
393
+ skip, or is NULL. Three paths:
388
394
 
389
395
  1. No size change (leaf.bytes == out.bytes): pull directly into
390
396
  `data` using the caller's `strides` (= output byte layout).
@@ -411,7 +417,8 @@ ca_data_type_stub (int8_t data_type)
411
417
  static int8_t
412
418
  pull_leaf_with_optional_cast (CArray *leaf, uint16_t innermost_op,
413
419
  int has_cast_at_leaf, int8_t out_bytes,
414
- const ca_lazy_req_t *req, void *data)
420
+ const ca_lazy_req_t *req, void *data,
421
+ boolean8_t *mask)
415
422
  {
416
423
  ca_size_t leaf_bytes = leaf->bytes;
417
424
  ca_size_t slab_n = req->n;
@@ -437,7 +444,9 @@ pull_leaf_with_optional_cast (CArray *leaf, uint16_t innermost_op,
437
444
  int8_t target_dt = (int8_t)(innermost_op - CA_MONOP_CAST_BASE);
438
445
  CArray src_stub = ca_data_type_stub(leaf->data_type);
439
446
  CArray dst_stub = ca_data_type_stub(target_dt);
440
- ca_cast_block(slab_n, &src_stub, scratch, &dst_stub, data);
447
+ ca_cast_block_skip_masked(slab_n, &src_stub, scratch, &dst_stub, data,
448
+ ca_monop_cast_can_raise(leaf->data_type, target_dt)
449
+ ? mask : NULL);
441
450
  result_dt = target_dt;
442
451
  } else {
443
452
  /* Path 3: memcpy scratch → start of data. Mid-chain cast will
@@ -465,7 +474,7 @@ ca_monop_eval_cells (void *node, const ca_lazy_req_t *req, void *data, int dir)
465
474
  int has_cast_at_leaf = 0;
466
475
  int chain_apply_count;
467
476
  boolean8_t *slab_mask = NULL;
468
- int mask_taken = 0;
477
+ int may_raise = 0;
469
478
 
470
479
  /* Writable-view op_ids (cast / byte_swap) use CAFake-style xfer_stride
471
480
  for both GET and PUT. This avoids the chain-materialise inline path
@@ -483,11 +492,10 @@ ca_monop_eval_cells (void *node, const ca_lazy_req_t *req, void *data, int dir)
483
492
  v = ALLOCV_N(char, holder, n * parent_bytes);
484
493
  if ( dir == CA_XFER_GET ) {
485
494
  ca_lazy_req_pull(mo->parent, req, v, CA_XFER_GET);
486
- if ( is_cast && ca_monop_cast_refuses_nan(mo->parent->data_type,
495
+ if ( is_cast && ca_monop_cast_can_raise(mo->parent->data_type,
487
496
  mo->data_type) ) {
488
- /* The cast raises on a NaN cell it is asked to convert; a masked
489
- cell (mask_invalid keeps the NaN under the mask) is skipped.
490
- The mask is copied off the arena first so a raise leaves no
497
+ /* The cast raises on a cell it cannot convert; a masked cell
498
+ (mask_invalid keeps a NaN under the mask) is skipped. The mask is copied off the arena first so a raise leaves no
491
499
  arena slot held. */
492
500
  CArray *parent = mo->parent;
493
501
  boolean8_t *arena_mask = ca_lazy_req_mask(req, 1, &parent);
@@ -498,7 +506,7 @@ ca_monop_eval_cells (void *node, const ca_lazy_req_t *req, void *data, int dir)
498
506
  memcpy(m, arena_mask, n);
499
507
  ca_lazy_arena_release(arena_mask);
500
508
  }
501
- ca_cast_block_with_mask(n, mo->parent, v, (CArray *) mo, data, m);
509
+ ca_cast_block_skip_masked(n, mo->parent, v, (CArray *) mo, data, m);
502
510
  if ( m ) {
503
511
  ALLOCV_END(mholder);
504
512
  }
@@ -567,25 +575,39 @@ ca_monop_eval_cells (void *node, const ca_lazy_req_t *req, void *data, int dir)
567
575
  widest type along it: the leaf, or a step that widens before a later
568
576
  one narrows (cmplx128.lazy.abs pulls 16-byte cells and answers 8-byte
569
577
  ones). When that is wider than the output, run in a scratch and copy
570
- the answer into `data` at the end. */
578
+ the answer into `data` at the end. The same walk finds whether a step
579
+ can raise on a cell, so that it has to skip the masked ones. */
571
580
  void *out = data;
572
581
  void *work = NULL;
573
582
  {
574
583
  ca_size_t max_bytes = leaf->bytes;
575
584
  int8_t dt = leaf->data_type;
576
585
  for ( i = chain_len - 1; i >= 0; i-- ) {
577
- dt = ca_monop_is_cast(op_chain[i])
578
- ? (int8_t)(op_chain[i] - CA_MONOP_CAST_BASE)
579
- : ca_lazy_promote_monop(op_chain[i], dt);
586
+ int8_t next = ca_monop_is_cast(op_chain[i])
587
+ ? (int8_t)(op_chain[i] - CA_MONOP_CAST_BASE)
588
+ : ca_lazy_promote_monop(op_chain[i], dt);
589
+ if ( ca_monop_is_cast(op_chain[i])
590
+ ? ca_monop_cast_can_raise(dt, next)
591
+ : ca_op_raises_on_cell(CA_OP_KIND_MONOP, op_chain[i], dt) ) {
592
+ may_raise = 1;
593
+ }
594
+ dt = next;
580
595
  if ( ca_sizeof[dt] > max_bytes ) max_bytes = ca_sizeof[dt];
581
596
  }
582
597
  if ( max_bytes > mo->bytes ) {
583
- work = ( leaf->data_type == CA_OBJECT )
598
+ /* Object cells stay in the buffer until a cast takes them to numbers;
599
+ a cast at the leaf takes them before they reach it. */
600
+ work = ( leaf->data_type == CA_OBJECT && ! has_cast_at_leaf )
584
601
  ? ca_lazy_arena_acquire_object((slab_n * max_bytes + sizeof(VALUE) - 1) / sizeof(VALUE))
585
602
  : ca_lazy_arena_acquire(slab_n * max_bytes);
586
603
  data = work;
587
604
  }
588
605
  }
606
+ /* raises: the walk above asks ca_op_raises_on_cell of each operation; a
607
+ cast, which is not one, has ca_monop_cast_can_raise */
608
+ if ( may_raise ) {
609
+ slab_mask = ca_lazy_req_mask(req, 1, &leaf);
610
+ }
589
611
 
590
612
  /* === 2. pull leaf into output buffer (with optional cast) === */
591
613
  cur_dt = pull_leaf_with_optional_cast(
@@ -593,7 +615,7 @@ ca_monop_eval_cells (void *node, const ca_lazy_req_t *req, void *data, int dir)
593
615
  has_cast_at_leaf ? op_chain[chain_len - 1] : 0,
594
616
  has_cast_at_leaf,
595
617
  (int8_t) mo->bytes,
596
- req, data
618
+ req, data, slab_mask
597
619
  );
598
620
 
599
621
  /* === 3. apply remaining chain in reverse (leaf side → outermost) ===
@@ -620,14 +642,15 @@ ca_monop_eval_cells (void *node, const ca_lazy_req_t *req, void *data, int dir)
620
642
  : ca_lazy_arena_acquire(total_bytes);
621
643
  ca_monop_scratch_acquire_count++;
622
644
  memcpy(scratch, data, total_bytes);
623
- ca_cast_block(slab_n, &src_stub, scratch, &dst_stub, data);
645
+ if ( work ) {
646
+ ca_lazy_arena_untag_objects(work);
647
+ }
648
+ ca_cast_block_skip_masked(slab_n, &src_stub, scratch, &dst_stub, data,
649
+ ca_monop_cast_can_raise(cur_dt, target_dt)
650
+ ? slab_mask : NULL);
624
651
  ca_lazy_arena_release(scratch);
625
652
  cur_dt = target_dt;
626
653
  } else {
627
- if ( ! mask_taken && ca_op_raises_on_cell(CA_OP_KIND_MONOP, op_id, cur_dt) ) {
628
- slab_mask = ca_lazy_req_mask(req, 1, &leaf);
629
- mask_taken = 1;
630
- }
631
654
  apply_monop_in_place(op_id, cur_dt, slab_n, data,
632
655
  ca_op_raises_on_cell(CA_OP_KIND_MONOP, op_id, cur_dt) ? slab_mask : NULL);
633
656
  cur_dt = ca_lazy_promote_monop(op_id, cur_dt);
@@ -645,16 +668,18 @@ ca_monop_eval_cells (void *node, const ca_lazy_req_t *req, void *data, int dir)
645
668
  /* A chain is evaluated from its leaf without evaluating the nodes under
646
669
  the top one, so those nodes are recorded with it: a cell that holds an
647
670
  expression over one of them is caught when it comes back, before a
648
- longer chain over the same cells is built. */
671
+ longer chain over the same cells is built. The walk stops where the
672
+ evaluation leaves the chain: a writable view evaluates its parent through
673
+ the transfer, as a leaf does, and that node records itself. */
649
674
  static void
650
675
  ca_monop_eval (CAMonOp *mo, const ca_lazy_req_t *req, void *data, int dir)
651
676
  {
652
677
  CArray *chain[CA_MAX_LAZY_DEPTH];
653
678
  CArray *leaf = mo->parent;
654
679
  int nchain = 0;
655
- while ( nchain < CA_MAX_LAZY_DEPTH &&
656
- ( leaf->obj_type == CA_OBJ_MONOP ||
657
- leaf->obj_type == CA_OBJ_LAZY_MARKER ) ) {
680
+ while ( ! ca_monop_is_writable_view(mo->op_id) &&
681
+ nchain < CA_MAX_LAZY_DEPTH &&
682
+ leaf->obj_type == CA_OBJ_MONOP ) {
658
683
  chain[nchain++] = leaf;
659
684
  leaf = ((CAView *) leaf)->parent;
660
685
  }
@@ -711,9 +736,11 @@ ca_monop_func_allocate (void *ap)
711
736
  count. Total ca_attach(parent) calls per view lifecycle = 1. CAFake
712
737
  uses this same pattern. */
713
738
  if ( ca_monop_is_writable_view(ca->op_id) ) {
714
- ca_attach(ca->parent);
739
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
740
+ }
741
+ else {
742
+ ca->ptr = ca_alloc_cells((CArray *) ca);
715
743
  }
716
- ca->ptr = ca_alloc_cells((CArray *) ca);
717
744
  }
718
745
 
719
746
  static void
@@ -741,20 +768,19 @@ ca_monop_func_attach (void *ap)
741
768
 
742
769
  Mirrors CAFake.attach. */
743
770
  if ( ca_monop_is_writable_view(ca->op_id) ) {
744
- ca_attach(ca->parent);
745
- ca->ptr = ca_alloc_cells((CArray *) ca);
771
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
746
772
  if ( ca_monop_is_cast(ca->op_id) ) {
747
773
  /* Cast parent.ptr (parent data_type) → ca.ptr (cast target data_type).
748
- A cast that raises on NaN skips the masked cells; the parent's
774
+ A cast that can raise skips the masked cells; the parent's
749
775
  mask is attached with the parent. A raise here is undone by
750
776
  ca_attach, which calls the detach slot once ca->ptr is set. */
751
777
  boolean8_t *m = NULL;
752
- if ( ca_monop_cast_refuses_nan(ca->parent->data_type, ca->data_type)
778
+ if ( ca_monop_cast_can_raise(ca->parent->data_type, ca->data_type)
753
779
  && ca_has_mask(ca->parent) ) {
754
780
  m = (boolean8_t *) ca->parent->mask->ptr;
755
781
  }
756
- ca_cast_block_with_mask(ca->elements, ca->parent, ca->parent->ptr,
757
- (CArray *) ca, ca->ptr, m);
782
+ ca_cast_block_skip_masked(ca->elements, ca->parent, ca->parent->ptr,
783
+ (CArray *) ca, ca->ptr, m);
758
784
  }
759
785
  else {
760
786
  /* byte_swap: memcpy parent.ptr → ca.ptr (same data_type same bytes),
@@ -795,9 +821,9 @@ ca_monop_func_sync (void *ap)
795
821
  /* Cast view → parent: reverse-cast bulk. */
796
822
  ca_update_mask(ca);
797
823
  if ( ca->mask ) {
798
- ca_cast_block_with_mask(ca->elements, ca, ca->ptr,
799
- ca->parent, ca->parent->ptr,
800
- (boolean8_t *) ca->mask->ptr);
824
+ ca_cast_block_skip_masked(ca->elements, ca, ca->ptr,
825
+ ca->parent, ca->parent->ptr,
826
+ (boolean8_t *) ca->mask->ptr);
801
827
  }
802
828
  else {
803
829
  ca_cast_block(ca->elements, ca, ca->ptr, ca->parent, ca->parent->ptr);
data/ext/ca_obj_object.c CHANGED
@@ -311,13 +311,13 @@ ca_object_hook_release (VALUE varg)
311
311
  }
312
312
 
313
313
  static void
314
- ca_object_call_transient (VALUE recv, const char *mid,
314
+ ca_object_call_transient (VALUE recv, ID mid,
315
315
  int argc, const VALUE *argv,
316
316
  int ntrans, const VALUE *trans)
317
317
  {
318
318
  ca_object_hook_t h;
319
319
  h.recv = recv;
320
- h.mid = rb_intern(mid);
320
+ h.mid = mid;
321
321
  h.argc = argc;
322
322
  h.argv = argv;
323
323
  h.ntrans = ntrans;
@@ -472,7 +472,7 @@ ca_objmask_get_cells (CAObjectMask *ca, ca_size_t n, ca_size_t *addrs,
472
472
  VALUE args[2];
473
473
  args[0] = raddrs;
474
474
  args[1] = rdata;
475
- ca_object_call_transient(ca->array, "mask_copy_addrs", 2, args, 2, args);
475
+ ca_object_call_transient(ca->array, rb_intern("mask_copy_addrs"), 2, args, 2, args);
476
476
  }
477
477
  return;
478
478
  }
@@ -520,7 +520,7 @@ ca_objmask_put_cells (CAObjectMask *ca, ca_size_t n, ca_size_t *addrs,
520
520
  VALUE args[2];
521
521
  args[0] = raddrs;
522
522
  args[1] = rdata;
523
- ca_object_call_transient(ca->array, "mask_sync_addrs", 2, args, 2, args);
523
+ ca_object_call_transient(ca->array, rb_intern("mask_sync_addrs"), 2, args, 2, args);
524
524
  }
525
525
  return;
526
526
  }
@@ -707,7 +707,7 @@ ca_objmask_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
707
707
  args[1] = rcounts;
708
708
  args[2] = rsteps;
709
709
  args[3] = rdata;
710
- ca_object_call_transient(ca->array, rb_id2name(mid_block),
710
+ ca_object_call_transient(ca->array, mid_block,
711
711
  4, args, 1, args + 3);
712
712
  }
713
713
  return;
@@ -1320,7 +1320,7 @@ ca_object_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
1320
1320
  VALUE args[2];
1321
1321
  args[0] = raddrs;
1322
1322
  args[1] = rdata;
1323
- ca_object_call_transient(ca->self, rb_id2name(mid), 2, args, 2, args);
1323
+ ca_object_call_transient(ca->self, mid, 2, args, 2, args);
1324
1324
  }
1325
1325
  return;
1326
1326
  }
@@ -1417,7 +1417,7 @@ ca_object_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
1417
1417
  args[1] = rcounts;
1418
1418
  args[2] = rsteps;
1419
1419
  args[3] = rdata;
1420
- ca_object_call_transient(ca->self, rb_id2name(mid_block),
1420
+ ca_object_call_transient(ca->self, mid_block,
1421
1421
  4, args, 1, args + 3);
1422
1422
  }
1423
1423
  return;
@@ -1494,7 +1494,7 @@ ca_object_func_fill_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *ptr)
1494
1494
  VALUE args[2];
1495
1495
  args[0] = raddrs;
1496
1496
  args[1] = rval;
1497
- ca_object_call_transient(ca->self, rb_id2name(mid), 2, args, 1, args);
1497
+ ca_object_call_transient(ca->self, mid, 2, args, 1, args);
1498
1498
  }
1499
1499
  return;
1500
1500
  }
@@ -1763,7 +1763,7 @@ rb_ca_object_initialize (int argc, VALUE *argv, VALUE self)
1763
1763
 
1764
1764
  ndim = RARRAY_LEN(rdim);
1765
1765
  for (i=0; i<ndim; i++) {
1766
- dim[i] = NUM2SIZE(rb_ary_entry(rdim, i));
1766
+ dim[i] = ca_integer_arg(rb_ary_entry(rdim, i), "a dimension", NULL);
1767
1767
  }
1768
1768
 
1769
1769
  TypedData_Get_Struct(self, CAObject, &caobject_data_type, ca);
data/ext/ca_obj_reduce.c CHANGED
@@ -216,8 +216,7 @@ static void
216
216
  ca_reduce_func_allocate (void *ap)
217
217
  {
218
218
  CAReduce *ca = (CAReduce *) ap;
219
- ca_attach(ca->parent);
220
- ca->ptr = ca_alloc_cells((CArray *) ca);
219
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
221
220
  }
222
221
 
223
222
  static void
@@ -226,8 +225,7 @@ ca_reduce_func_attach (void *ap)
226
225
  CAReduce *ca = (CAReduce *) ap;
227
226
  char *p;
228
227
  ca_size_t i;
229
- ca_attach(ca->parent);
230
- ca->ptr = ca_alloc_cells((CArray *) ca);
228
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
231
229
  p = ca->ptr;
232
230
  for (i=0; i<ca->elements; i++) {
233
231
  ca_reduce_func_xfer_addrs(ca, 1, &i, p, CA_XFER_GET);
data/ext/ca_obj_refer.c CHANGED
@@ -339,16 +339,75 @@ ca_keep_chain_flags (CArray *view, CArray *src)
339
339
  }
340
340
 
341
341
  /* The sources of folded views (CA_FLAG_FOLDED), keyed by the view. Views are
342
- made and freed in any Ractor, so the table is taken under a lock. */
342
+ made and freed in any Ractor, so the table is taken under a lock.
343
+
344
+ st_insert can grow the table through ruby_xmalloc, which can run a GC, and
345
+ the GC frees folded views through ca_fold_forget. ca_fold_forget therefore
346
+ never waits for the lock: when it is held (by the inserting thread itself,
347
+ or by a Ractor stopped for this GC) the view is put on a list instead, and
348
+ whoever takes the lock next removes the listed views first. A freed view's
349
+ address can be reused by a new view only once that view is recorded, which
350
+ takes the lock and so empties the list first. */
343
351
  static st_table *ca_fold_sources;
344
352
  static rb_nativethread_lock_t ca_fold_lock;
345
353
 
354
+ typedef struct ca_fold_gone {
355
+ struct ca_fold_gone *next;
356
+ st_data_t key;
357
+ } ca_fold_gone_t;
358
+
359
+ static ca_fold_gone_t *ca_fold_gone_list = NULL;
360
+
361
+ /* With the lock held: remove the views freed while it was held elsewhere.
362
+ The list is allocated with malloc, not xmalloc: it is grown inside a GC. */
363
+ static void
364
+ ca_fold_forget_gone (void)
365
+ {
366
+ ca_fold_gone_t *g = __atomic_exchange_n(&ca_fold_gone_list, NULL, __ATOMIC_ACQ_REL);
367
+ while ( g ) {
368
+ ca_fold_gone_t *next = g->next;
369
+ st_data_t key = g->key;
370
+ st_delete(ca_fold_sources, &key, NULL);
371
+ free(g);
372
+ g = next;
373
+ }
374
+ }
375
+
376
+ static void
377
+ ca_fold_lock_table (void)
378
+ {
379
+ ca_native_lock_without_gvl(&ca_fold_lock);
380
+ ca_fold_forget_gone();
381
+ }
382
+
383
+ typedef struct {
384
+ CArray *view;
385
+ CArray *src;
386
+ } ca_fold_note_arg_t;
387
+
388
+ static VALUE
389
+ ca_fold_insert (VALUE varg)
390
+ {
391
+ ca_fold_note_arg_t *a = (ca_fold_note_arg_t *) varg;
392
+ st_insert(ca_fold_sources, (st_data_t) a->view, (st_data_t) a->src);
393
+ return Qnil;
394
+ }
395
+
396
+ /* The growth of the table can raise NoMemoryError; the lock is released
397
+ before that raise goes on. */
346
398
  void
347
399
  ca_fold_note_source (CArray *view, CArray *src)
348
400
  {
349
- rb_native_mutex_lock(&ca_fold_lock);
350
- st_insert(ca_fold_sources, (st_data_t) view, (st_data_t) src);
401
+ ca_fold_note_arg_t arg = { view, src };
402
+ int state = 0;
403
+ /* lock: st_insert can run a GC; a view it frees is listed, not waited for */
404
+ ca_fold_lock_table();
405
+ /* protect: releases a native lock; no array is held */
406
+ rb_protect(ca_fold_insert, (VALUE) &arg, &state);
351
407
  rb_native_mutex_unlock(&ca_fold_lock);
408
+ if ( state ) {
409
+ rb_jump_tag(state);
410
+ }
352
411
  ca_set_flag(view, CA_FLAG_FOLDED);
353
412
  }
354
413
 
@@ -356,7 +415,7 @@ CArray *
356
415
  ca_fold_source (CArray *view)
357
416
  {
358
417
  st_data_t src = 0;
359
- rb_native_mutex_lock(&ca_fold_lock);
418
+ ca_fold_lock_table();
360
419
  st_lookup(ca_fold_sources, (st_data_t) view, &src);
361
420
  rb_native_mutex_unlock(&ca_fold_lock);
362
421
  return (CArray *) src;
@@ -366,9 +425,30 @@ void
366
425
  ca_fold_forget (CArray *view)
367
426
  {
368
427
  st_data_t key = (st_data_t) view;
369
- rb_native_mutex_lock(&ca_fold_lock);
370
- st_delete(ca_fold_sources, &key, NULL);
371
- rb_native_mutex_unlock(&ca_fold_lock);
428
+ ca_fold_gone_t *g;
429
+ if ( rb_native_mutex_trylock(&ca_fold_lock) == 0 ) {
430
+ ca_fold_forget_gone();
431
+ st_delete(ca_fold_sources, &key, NULL);
432
+ rb_native_mutex_unlock(&ca_fold_lock);
433
+ return;
434
+ }
435
+ g = (ca_fold_gone_t *) malloc(sizeof(*g));
436
+ if ( g == NULL ) {
437
+ /* No memory for the list: wait for the lock, as there is no other way
438
+ to drop the record. */
439
+ /* lock: only with no memory for the list; the wait may block the GC */
440
+ rb_native_mutex_lock(&ca_fold_lock);
441
+ ca_fold_forget_gone();
442
+ st_delete(ca_fold_sources, &key, NULL);
443
+ rb_native_mutex_unlock(&ca_fold_lock);
444
+ return;
445
+ }
446
+ g->key = key;
447
+ g->next = __atomic_load_n(&ca_fold_gone_list, __ATOMIC_ACQUIRE);
448
+ while ( ! __atomic_compare_exchange_n(&ca_fold_gone_list, &g->next, g, 0,
449
+ __ATOMIC_RELEASE, __ATOMIC_ACQUIRE) ) {
450
+ /* g->next now holds the head that won; try again on top of it */
451
+ }
372
452
  }
373
453
 
374
454
  /* Builds the folded view of `ca` for reshape / flatten: a CAStride over
@@ -557,11 +637,25 @@ rb_ca_refer_kw (int argc, VALUE *argv, VALUE ropt, VALUE self)
557
637
  rdim = rb_ca_dim(self);
558
638
  }
559
639
  }
640
+ if ( ca_is_face(ca) && ! rb_obj_is_data_class(rtype)
641
+ && ( data_type == CA_OBJECT || ca->data_type == CA_FIXLEN ) ) {
642
+ /* A Face's cells do not mean their storage bytes, and the view below
643
+ would be lifted back to a Face reading cells of the wrong width. */
644
+ CArray *storage = ca_strip_face(ca);
645
+ if ( ! ( data_type == ca->data_type && bytes == ca->bytes )
646
+ && ! ( data_type == storage->data_type
647
+ && bytes == storage->bytes ) ) {
648
+ rb_raise(rb_eTypeError,
649
+ "%"PRIsVALUE" has no view of its values in another data_type: "
650
+ "#to_type gives the values, #parent.refer the raw storage",
651
+ rb_obj_class(self));
652
+ }
653
+ }
560
654
  Check_Type(rdim, T_ARRAY);
561
655
  CA_CHECK_RANK_GIVEN(RARRAY_LEN(rdim)); /* before dim[] is filled */
562
656
  ndim = RARRAY_LEN(rdim);
563
- for (i = 0; i < ndim; i++) dim[i] = NUM2SIZE(rb_ary_entry(rdim, i));
564
- if (! NIL_P(roffset)) offset = NUM2SIZE(roffset);
657
+ for (i = 0; i < ndim; i++) dim[i] = ca_integer_arg(rb_ary_entry(rdim, i), "a dimension", NULL);
658
+ if (! NIL_P(roffset)) offset = ca_integer_arg(roffset, "the offset", NULL);
565
659
  /* When rtype is a data_class the result is a record array. It is
566
660
  built on the storage, never on a Face, so that a Face stays on top
567
661
  of the chain: re-reading a record array as its own data_class lifts
@@ -723,7 +817,7 @@ rb_ca_reshape (int argc, VALUE *argv, VALUE self)
723
817
  new_dim[i] = ca->dim[src];
724
818
  }
725
819
  else {
726
- new_dim[i] = NUM2SIZE(argv[i]);
820
+ new_dim[i] = ca_integer_arg(argv[i], "a dimension", "reshape");
727
821
  }
728
822
  /* A product too large to count cannot equal the element count; an
729
823
  extent of zero anywhere makes it zero whatever it overflowed to. */
data/ext/ca_obj_repeat.c CHANGED
@@ -233,7 +233,7 @@ rb_ca_repeat (int argc, VALUE *argv, VALUE self)
233
233
  }
234
234
  }
235
235
  else {
236
- count[i] = NUM2SIZE(argv[i]);
236
+ count[i] = ca_integer_arg(argv[i], "a repeat count", "repeat");
237
237
  if ( count[i] == 0 ) {
238
238
  rb_raise(rb_eArgError,
239
239
  "zero repeat count specified in creating CARepeat object");
data/ext/ca_obj_roll.c CHANGED
@@ -525,16 +525,14 @@ static void
525
525
  ca_roll_func_allocate (void *ap)
526
526
  {
527
527
  CARoll *ca = (CARoll *) ap;
528
- ca_attach(ca->parent);
529
- ca->ptr = ca_alloc_cells((CArray *) ca);
528
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
530
529
  }
531
530
 
532
531
  static void
533
532
  ca_roll_func_attach (void *ap)
534
533
  {
535
534
  CARoll *ca = (CARoll *) ap;
536
- ca_attach(ca->parent);
537
- ca->ptr = ca_alloc_cells((CArray *) ca);
535
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
538
536
  ca_roll_iterate_regions(ca, ca->ptr, 0);
539
537
  }
540
538
 
data/ext/ca_obj_select.c CHANGED
@@ -465,9 +465,11 @@ ca_select_func_allocate (void *ap)
465
465
  {
466
466
  CASelect *ca = (CASelect *) ap;
467
467
  if ( ! ca_parent_lends_no_memory(ca->parent) ) {
468
- ca_attach(ca->parent);
468
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
469
+ }
470
+ else {
471
+ ca->ptr = ca_alloc_cells((CArray *) ca);
469
472
  }
470
- ca->ptr = ca_alloc_cells((CArray *) ca);
471
473
  }
472
474
 
473
475
  static void
@@ -482,10 +484,10 @@ ca_select_func_attach (void *ap)
482
484
  return;
483
485
  }
484
486
 
485
- ca_attach(ca->parent);
487
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
486
488
  ca_select_describe_axes(ca, desc, pdims);
487
- ca->ptr = ca_axis_dispatch_attach(ca->parent, pdims, desc, ca->ndim,
488
- ca->bytes, ca->elements, NULL);
489
+ ca_axis_dispatch_gather(ca->parent, pdims, desc, ca->ndim,
490
+ ca->bytes, ca->elements, NULL, ca->ptr);
489
491
  }
490
492
 
491
493
  static void
@@ -600,7 +602,7 @@ ca_operation_function_t ca_select_func = {
600
602
  /* ------------------------------------------------------------------- */
601
603
 
602
604
  VALUE
603
- rb_ca_select_new (VALUE cary, VALUE select)
605
+ rb_ca_select_new (VALUE cary, volatile VALUE select)
604
606
  {
605
607
  volatile VALUE obj;
606
608
  CArray *parent, *cselect;
@@ -726,9 +726,11 @@ ca_select_axis_func_allocate (void *ap)
726
726
  {
727
727
  CASelectAxis *ca = (CASelectAxis *) ap;
728
728
  if ( ! ca_parent_lends_no_memory(ca->parent) ) {
729
- ca_attach(ca->parent);
729
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
730
+ }
731
+ else {
732
+ ca->ptr = ca_alloc_cells((CArray *) ca);
730
733
  }
731
- ca->ptr = ca_alloc_cells((CArray *) ca);
732
734
  }
733
735
 
734
736
  static void
@@ -743,10 +745,10 @@ ca_select_axis_func_attach (void *ap)
743
745
  return;
744
746
  }
745
747
 
746
- ca_attach(ca->parent);
748
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
747
749
  ca_select_axis_describe_axes(ca, desc, pdims);
748
- ca->ptr = ca_axis_dispatch_attach(ca->parent, pdims, desc, ca->ndim,
749
- ca->bytes, ca->elements, NULL);
750
+ ca_axis_dispatch_gather(ca->parent, pdims, desc, ca->ndim,
751
+ ca->bytes, ca->elements, NULL, ca->ptr);
750
752
  }
751
753
 
752
754
  static void
@@ -1008,7 +1010,7 @@ rb_ca_select_axis (int argc, VALUE *argv, VALUE self)
1008
1010
  }
1009
1011
 
1010
1012
  for ( i = 0; i < argc; i++ ) {
1011
- VALUE arg = argv[i];
1013
+ volatile VALUE arg = argv[i];
1012
1014
 
1013
1015
  if ( ! rb_obj_is_carray(arg) ) {
1014
1016
  ca_index_axis_key(arg, parent->dim[i], i,