carray 3.0.2 → 3.0.3

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 (218) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +304 -0
  3. data/README.md +3 -4
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +225 -0
  6. data/ext/ca_axis_group.c +177 -99
  7. data/ext/ca_binop_dispatch.c +54 -32
  8. data/ext/ca_binop_dispatch.h +1 -0
  9. data/ext/ca_categorical_iterator.c +66 -71
  10. data/ext/ca_compare.c +8 -2
  11. data/ext/ca_for_buffer.h +21 -68
  12. data/ext/ca_group_iter.c +27 -11
  13. data/ext/ca_kernel_iterator.c +663 -214
  14. data/ext/ca_kernel_iterator.h +231 -167
  15. data/ext/ca_monop_dispatch.c +4 -4
  16. data/ext/ca_monop_dispatch.h +4 -0
  17. data/ext/ca_obj_array.c +132 -22
  18. data/ext/ca_obj_bincmp.c +152 -121
  19. data/ext/ca_obj_binop.c +107 -210
  20. data/ext/ca_obj_bitarray.c +37 -24
  21. data/ext/ca_obj_bitfield.c +95 -62
  22. data/ext/ca_obj_block.c +1 -0
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +96 -25
  25. data/ext/ca_obj_face.c +406 -64
  26. data/ext/ca_obj_face.h +92 -47
  27. data/ext/ca_obj_fake.c +21 -50
  28. data/ext/ca_obj_farray.c +1 -0
  29. data/ext/ca_obj_field.c +16 -3
  30. data/ext/ca_obj_fixlen_string.c +21 -21
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +189 -107
  33. data/ext/ca_obj_moncmp.c +61 -61
  34. data/ext/ca_obj_monop.c +125 -107
  35. data/ext/ca_obj_object.c +543 -233
  36. data/ext/ca_obj_record.c +51 -16
  37. data/ext/ca_obj_reduce.c +5 -8
  38. data/ext/ca_obj_refer.c +117 -33
  39. data/ext/ca_obj_remap.c +57 -17
  40. data/ext/ca_obj_repeat.c +21 -5
  41. data/ext/ca_obj_roll.c +115 -11
  42. data/ext/ca_obj_select.c +47 -49
  43. data/ext/ca_obj_select_axis.c +58 -60
  44. data/ext/ca_obj_shift.c +22 -102
  45. data/ext/ca_obj_stack.c +223 -118
  46. data/ext/ca_obj_stride.c +195 -34
  47. data/ext/ca_obj_string.c +17 -18
  48. data/ext/ca_obj_tile.c +32 -16
  49. data/ext/ca_obj_time.c +23 -25
  50. data/ext/ca_obj_timedelta.c +21 -23
  51. data/ext/ca_obj_transpose.c +12 -3
  52. data/ext/ca_obj_triop.c +110 -117
  53. data/ext/ca_obj_window.c +149 -27
  54. data/ext/ca_op_ipower.c +24 -11
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_sort_kernels.h +70 -0
  58. data/ext/ca_sweep_engine.c +102 -79
  59. data/ext/ca_sweep_engine.h +7 -20
  60. data/ext/ca_triop_dispatch.c +4 -1
  61. data/ext/ca_triop_dispatch.h +5 -2
  62. data/ext/caframe_csv_reader.c +463 -0
  63. data/ext/carray.h +70 -27
  64. data/ext/carray_access.c +455 -211
  65. data/ext/carray_address_basis.c +177 -36
  66. data/ext/carray_attribute.c +45 -3
  67. data/ext/carray_bincount.c +32 -2
  68. data/ext/carray_broadcast.c +46 -14
  69. data/ext/carray_call_cfunc.c +0 -28
  70. data/ext/carray_cast.c +1444 -209
  71. data/ext/carray_cast_func.rb +34 -21
  72. data/ext/carray_conversion.c +197 -73
  73. data/ext/carray_copy.c +32 -11
  74. data/ext/carray_core.c +514 -122
  75. data/ext/carray_count.c +140 -87
  76. data/ext/carray_element.c +18 -27
  77. data/ext/carray_factorize.c +569 -277
  78. data/ext/carray_generate.c +76 -40
  79. data/ext/carray_histogram.c +152 -94
  80. data/ext/carray_hold.c +2 -1
  81. data/ext/carray_index_classifier.c +63 -96
  82. data/ext/carray_internal.h +385 -3
  83. data/ext/carray_lazy.c +834 -79
  84. data/ext/carray_loop.c +81 -65
  85. data/ext/carray_mask.c +682 -316
  86. data/ext/carray_math_kernel.h +1 -0
  87. data/ext/carray_median_percentile.c +221 -76
  88. data/ext/carray_memory_view.c +254 -248
  89. data/ext/carray_operator.c +1089 -359
  90. data/ext/carray_order.c +133 -69
  91. data/ext/carray_partition.c +102 -56
  92. data/ext/carray_random.c +273 -239
  93. data/ext/carray_scatter.c +308 -65
  94. data/ext/carray_segment.c +257 -0
  95. data/ext/carray_slab.c +170 -70
  96. data/ext/carray_sort.c +215 -218
  97. data/ext/carray_sort_kernel.c +8 -1
  98. data/ext/carray_struct.c +99 -21
  99. data/ext/carray_test.c +167 -64
  100. data/ext/carray_time_text.c +1182 -0
  101. data/ext/carray_utils.c +363 -98
  102. data/ext/extconf.rb +27 -2
  103. data/ext/mk_call_cfunc.rb +0 -2
  104. data/ext/mk_pow5_table.rb +35 -0
  105. data/ext/mkkernel.rb +1076 -846
  106. data/ext/ruby_carray.c +7 -1
  107. data/ext/version.h +4 -4
  108. data/ext/version.rb +4 -2
  109. data/lib/carray/attribute.rb +39 -0
  110. data/lib/carray/autoload_carray.rb +19 -0
  111. data/lib/carray/autoload_method_extension.rb +6 -0
  112. data/lib/carray/axis_group.rb +174 -26
  113. data/lib/carray/basics.rb +91 -25
  114. data/lib/carray/bincount_nd.rb +6 -0
  115. data/lib/carray/block_iterator.rb +118 -48
  116. data/lib/carray/boolean_reduce.rb +21 -29
  117. data/lib/carray/categorical.rb +94 -26
  118. data/lib/carray/categorical_iterator.rb +333 -826
  119. data/lib/carray/complex.rb +36 -29
  120. data/lib/carray/conditional.rb +94 -7
  121. data/lib/carray/const_string.rb +46 -0
  122. data/lib/carray/frame/concat.rb +157 -6
  123. data/lib/carray/frame/convert.rb +13 -7
  124. data/lib/carray/frame/csv_parser.rb +589 -82
  125. data/lib/carray/frame/describe.rb +79 -0
  126. data/lib/carray/frame/frame.rb +124 -25
  127. data/lib/carray/frame/group.rb +189 -63
  128. data/lib/carray/frame/io.rb +412 -72
  129. data/lib/carray/frame/join.rb +134 -46
  130. data/lib/carray/frame/records.rb +37 -16
  131. data/lib/carray/frame/reshape.rb +448 -0
  132. data/lib/carray/frame/sort.rb +26 -2
  133. data/lib/carray/frame/verbs.rb +715 -48
  134. data/lib/carray/frame.rb +2 -0
  135. data/lib/carray/fuse_source.rb +111 -2
  136. data/lib/carray/fusion.rb +229 -38
  137. data/lib/carray/inspect.rb +29 -11
  138. data/lib/carray/iterator.rb +14 -2
  139. data/lib/carray/lazy.rb +54 -30
  140. data/lib/carray/mask_gap_fill.rb +66 -52
  141. data/lib/carray/math.rb +32 -19
  142. data/lib/carray/meld_reduce.rb +40 -7
  143. data/lib/carray/methods/align_addr.rb +8 -4
  144. data/lib/carray/methods/bincount.rb +2 -2
  145. data/lib/carray/methods/bit_string.rb +14 -3
  146. data/lib/carray/methods/composition.rb +16 -10
  147. data/lib/carray/methods/factorize.rb +2 -2
  148. data/lib/carray/methods/gather_nd.rb +13 -2
  149. data/lib/carray/methods/insert_block.rb +9 -14
  150. data/lib/carray/methods/is_in.rb +117 -4
  151. data/lib/carray/methods/join.rb +1 -1
  152. data/lib/carray/methods/locate_addr.rb +177 -36
  153. data/lib/carray/methods/mode.rb +18 -1
  154. data/lib/carray/methods/repeat.rb +1 -20
  155. data/lib/carray/methods/resize.rb +21 -23
  156. data/lib/carray/methods/snap.rb +34 -12
  157. data/lib/carray/methods/unique.rb +11 -2
  158. data/lib/carray/record_order.rb +208 -0
  159. data/lib/carray/runtime.rb +14 -11
  160. data/lib/carray/segment_iterator.rb +990 -0
  161. data/lib/carray/serialize.rb +197 -27
  162. data/lib/carray/slab_iterator.rb +51 -27
  163. data/lib/carray/stack.rb +10 -4
  164. data/lib/carray/string_operation_extension.rb +4 -3
  165. data/lib/carray/struct.rb +142 -51
  166. data/lib/carray/struct_builder.rb +110 -14
  167. data/lib/carray/table.rb +2 -2
  168. data/lib/carray/time.rb +517 -103
  169. data/lib/carray/window_iterator.rb +127 -72
  170. data/lib/carray.rb +2 -2
  171. data/yard-stubs/ca_obj_array.rb +4 -4
  172. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  173. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  174. data/yard-stubs/ca_obj_fake.rb +1 -1
  175. data/yard-stubs/ca_obj_field.rb +1 -1
  176. data/yard-stubs/ca_obj_refer.rb +3 -3
  177. data/yard-stubs/ca_obj_roll.rb +0 -1
  178. data/yard-stubs/ca_obj_stride.rb +10 -7
  179. data/yard-stubs/ca_obj_transpose.rb +1 -1
  180. data/yard-stubs/ca_obj_window.rb +1 -2
  181. data/yard-stubs/carray_access.rb +5 -4
  182. data/yard-stubs/carray_broadcast.rb +1 -1
  183. data/yard-stubs/carray_cast.rb +78 -43
  184. data/yard-stubs/carray_conversion.rb +13 -1
  185. data/yard-stubs/carray_copy.rb +8 -3
  186. data/yard-stubs/carray_core.rb +3 -3
  187. data/yard-stubs/carray_count.rb +9 -0
  188. data/yard-stubs/carray_lazy.rb +5 -1
  189. data/yard-stubs/carray_loop.rb +4 -1
  190. data/yard-stubs/carray_mask.rb +34 -10
  191. data/yard-stubs/carray_math.rb +25 -13
  192. data/yard-stubs/carray_median_percentile.rb +10 -6
  193. data/yard-stubs/carray_order.rb +34 -13
  194. data/yard-stubs/carray_scatter.rb +10 -3
  195. data/yard-stubs/carray_segment.rb +72 -0
  196. data/yard-stubs/carray_slab.rb +2 -1
  197. data/yard-stubs/carray_sort.rb +43 -3
  198. data/yard-stubs/carray_test.rb +10 -0
  199. data/yard-stubs/carray_utils.rb +0 -26
  200. metadata +11 -19
  201. data/ext/ca_for_each_element.h +0 -239
  202. data/ext/carray_build_flags.h +0 -3
  203. data/ext/carray_config.h +0 -28
  204. data/ext/carray_kernels_bincmp.c +0 -4446
  205. data/ext/carray_kernels_binop.c +0 -11001
  206. data/ext/carray_kernels_init.c +0 -1131
  207. data/ext/carray_kernels_map.c +0 -3467
  208. data/ext/carray_kernels_moncmp.c +0 -2097
  209. data/ext/carray_kernels_monop.c +0 -18313
  210. data/ext/carray_kernels_reduce_aggregate.c +0 -26005
  211. data/ext/carray_kernels_reduce_boolean.c +0 -330
  212. data/ext/carray_kernels_reduce_cumulative.c +0 -14862
  213. data/ext/carray_kernels_reduce_extreme.c +0 -17494
  214. data/ext/carray_kernels_reduce_variance.c +0 -3910
  215. data/ext/carray_kernels_scan.c +0 -3693
  216. data/ext/carray_kernels_search.c +0 -32218
  217. data/ext/carray_kernels_sort.c +0 -10626
  218. data/ext/carray_kernels_triop.c +0 -1392
data/ext/carray_core.c CHANGED
@@ -425,7 +425,6 @@ ca_attach_is_alias (void *ap)
425
425
  CArray *ca = (CArray *) ap;
426
426
  extern ca_operation_function_t ca_stride_func;
427
427
  extern ca_operation_function_t ca_lazy_marker_func;
428
- extern int ca_stride_is_contiguous (CAStride *ca);
429
428
  extern int ca_stride_attach_aliases_root (CAStride *ca);
430
429
 
431
430
  if ( ca == NULL ) return 0;
@@ -440,21 +439,13 @@ ca_attach_is_alias (void *ap)
440
439
  return ca_attach_is_alias(((CAView *) ca)->parent);
441
440
  }
442
441
 
443
- /* CAStride family share ca_stride_func.attach (= ca_stride_func_attach).
444
- The alias-attach fast path is taken iff composed strides are
445
- row-major contiguous. ca_stride_is_contiguous checks the leaf
446
- view's own strides, which is what we want — the compose-fold to
447
- root happens during attach itself; if leaf is contig and parent
448
- chain is too (transitively, since each CAStride's strides are
449
- composed against parent's), the alias path fires.
450
-
451
- ...and iff there is parent memory to alias at the end of that fold.
452
- A non-entity root has none to lend, so attach builds its own buffer
453
- and writes through ca->ptr reach the root only via ca_sync. Saying
454
- "alias" there would let a caller write and skip the sync. */
442
+ /* CAStride family share ca_stride_func.attach, which aliases the root's
443
+ memory iff the strides composed to the root are row-major contiguous
444
+ and the root has memory to lend. Otherwise attach builds its own
445
+ buffer and writes through ca->ptr reach the root only via ca_sync, so
446
+ saying "alias" would let a caller write and skip the sync. */
455
447
  if ( ca_func[ca->obj_type].attach == ca_stride_func.attach ) {
456
- return ca_stride_is_contiguous((CAStride *) ca)
457
- && ca_stride_attach_aliases_root((CAStride *) ca);
448
+ return ca_stride_attach_aliases_root((CAStride *) ca);
458
449
  }
459
450
  return 0;
460
451
  }
@@ -598,8 +589,8 @@ rb_ca_new_reduced (VALUE self, int8_t *slab_axes, int8_t naxes, int32_t data_typ
598
589
  to NUM2SIZE) of length `count`, normalises + range-checks + duplicate-
599
590
  checks, fills out_axes[] in input order. Shared between the legacy
600
591
  variadic entry (rb_ca_parse_reduce_axes) and the kwarg entry
601
- (rb_ca_parse_reduce_axes_kw). ctx is a short label embedded in error
602
- messages so callers can disambiguate which entry raised. */
592
+ (rb_ca_parse_reduce_axes_kw). ctx names the method in error messages;
593
+ the two public entries pass the method the user called. */
603
594
  static int8_t
604
595
  parse_axes_items (const VALUE *items, int count, CArray *ca,
605
596
  int8_t *out_axes, const char *ctx)
@@ -623,12 +614,12 @@ parse_axes_items (const VALUE *items, int count, CArray *ca,
623
614
 
624
615
  for ( i = 0; i < CA_RANK_MAX; i++ ) seen[i] = 0;
625
616
  for ( i = 0; i < count; i++ ) {
626
- ca_size_t a = NUM2SIZE(items[i]);
627
- if ( a < 0 ) a += ca->ndim;
617
+ ca_size_t raw = ca_axis_integer(items[i], NULL);
618
+ ca_size_t a = ( raw < 0 ) ? raw + ca->ndim : raw;
628
619
  if ( a < 0 || a >= ca->ndim ) {
629
- rb_raise(rb_eIndexError,
630
- "%s: axis %ld out of range [0, %d)",
631
- ctx, (long) a, (int) ca->ndim);
620
+ rb_raise(rb_eArgError,
621
+ "%s: axis %ld out of range for ndim %d",
622
+ ctx, (long) raw, (int) ca->ndim);
632
623
  }
633
624
  if ( seen[a] ) {
634
625
  rb_raise(rb_eArgError,
@@ -666,7 +657,7 @@ rb_ca_parse_reduce_axes (int argc, VALUE *argv, CArray *ca, int8_t *out_axes)
666
657
  }
667
658
 
668
659
  return parse_axes_items(items, count, ca, out_axes,
669
- "rb_ca_parse_reduce_axes");
660
+ ca_calling_method_name());
670
661
  }
671
662
 
672
663
  /* Kwarg form of rb_ca_parse_reduce_axes — accepts the `axis:` value as
@@ -705,8 +696,8 @@ rb_ca_parse_reduce_axes_kw_ctx (VALUE axis_val, CArray *ca, int8_t *out_axes,
705
696
  }
706
697
 
707
698
  rb_raise(rb_eTypeError,
708
- "%s: axis: must be nil, Integer, or "
709
- "Array of Integer (got %"PRIsVALUE")",
699
+ "%s: axis must be nil, an Integer or an Array of Integer "
700
+ "(got %"PRIsVALUE")",
710
701
  ctx, rb_obj_class(axis_val));
711
702
  }
712
703
 
@@ -714,7 +705,7 @@ int8_t
714
705
  rb_ca_parse_reduce_axes_kw (VALUE axis_val, CArray *ca, int8_t *out_axes)
715
706
  {
716
707
  return rb_ca_parse_reduce_axes_kw_ctx(axis_val, ca, out_axes,
717
- "rb_ca_parse_reduce_axes_kw");
708
+ ca_calling_method_name());
718
709
  }
719
710
 
720
711
  /* ------------------------------------------------------------------- */
@@ -832,7 +823,7 @@ ca_set_cyclic_check(void *ap)
832
823
  /* ca.data_type == CA_OBJECT */
833
824
  if ( ca_is_object_type(ca) ) {
834
825
  if ( ca->flags & CA_FLAG_CYCLE_CHECK ) {
835
- rb_raise(rb_eRuntimeError, "cyclic reference is not allowed in CArray");
826
+ rb_raise(rb_eArgError, "cyclic reference is not allowed in CArray");
836
827
  }
837
828
  ca_set_flag(ca, CA_FLAG_CYCLE_CHECK);
838
829
  }
@@ -860,12 +851,36 @@ ca_test_cyclic_check(void *ap, void *ptr)
860
851
  CArray *cv;
861
852
  TypedData_Get_Struct(rval, CArray, &carray_data_type, cv);
862
853
  if ( ca_test_flag(cv, CA_FLAG_CYCLE_CHECK) ) {
863
- rb_raise(rb_eRuntimeError, "cyclic reference is not allowed in CArray");
854
+ rb_raise(rb_eArgError, "cyclic reference is not allowed in CArray");
864
855
  }
865
856
  }
866
857
  }
867
858
  }
868
859
 
860
+ /* Runs body(arg) -- one transfer of an object cell or cells -- with ca's
861
+ cycle check set, and clears the check however body leaves. Either
862
+ direction can call into Ruby: a GET through a CAObject slot or the
863
+ check of what it delivered, a PUT that converts the caller's value. A
864
+ raise that skipped the clear would leave every later access to ca
865
+ reporting a cyclic reference. A PUT into an entity only copies VALUEs
866
+ and cannot raise, so it runs unprotected. */
867
+ static void
868
+ ca_with_cyclic_check (CArray *ca, int dir, VALUE (*body)(VALUE), VALUE arg)
869
+ {
870
+ int state = 0;
871
+ ca_set_cyclic_check(ca);
872
+ if ( dir == CA_XFER_PUT && ca_is_entity(ca) ) {
873
+ body(arg);
874
+ }
875
+ else {
876
+ rb_protect(body, arg, &state);
877
+ }
878
+ ca_clear_cyclic_check(ca);
879
+ if ( state ) {
880
+ rb_jump_tag(state);
881
+ }
882
+ }
883
+
869
884
  /* ------------------------------------------------------------------- */
870
885
  /* CArray offers no per-cell ptr accessor (ca_ptr_at_addr /
871
886
  ca_ptr_at_index): such a slot is structurally unsafe -- CABitarray /
@@ -911,14 +926,17 @@ struct ca_xfer_index_args {
911
926
  CArray *ca;
912
927
  ca_size_t *idx;
913
928
  char *ptr;
929
+ int dir;
914
930
  };
915
931
 
916
932
  static VALUE
917
- ca_xfer_index_get_body (VALUE arg)
933
+ ca_xfer_index_body (VALUE arg)
918
934
  {
919
935
  struct ca_xfer_index_args *d = (struct ca_xfer_index_args *) arg;
920
- ca_xfer_index_dispatch(d->ca, d->idx, d->ptr, CA_XFER_GET);
921
- ca_test_cyclic_check(d->ca, d->ptr);
936
+ ca_xfer_index_dispatch(d->ca, d->idx, d->ptr, d->dir);
937
+ if ( d->dir == CA_XFER_GET ) {
938
+ ca_test_cyclic_check(d->ca, d->ptr);
939
+ }
922
940
  return Qnil;
923
941
  }
924
942
 
@@ -937,24 +955,14 @@ ca_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
937
955
  return;
938
956
  }
939
957
 
940
- /* CA_OBJECT slow path: cyclic check (+ rb_protect on GET). */
941
- if ( dir == CA_XFER_GET ) {
958
+ /* CA_OBJECT slow path: cyclic check. */
959
+ {
942
960
  struct ca_xfer_index_args args;
943
- int state = 0;
944
961
  args.ca = ca;
945
962
  args.idx = idx;
946
963
  args.ptr = (char *) data;
947
- ca_set_cyclic_check(ca);
948
- rb_protect(ca_xfer_index_get_body, (VALUE) &args, &state);
949
- ca_clear_cyclic_check(ca);
950
- if ( state ) {
951
- rb_jump_tag(state);
952
- }
953
- }
954
- else {
955
- ca_set_cyclic_check(ca);
956
- ca_xfer_index_dispatch(ca, idx, data, CA_XFER_PUT);
957
- ca_clear_cyclic_check(ca);
964
+ args.dir = dir;
965
+ ca_with_cyclic_check(ca, dir, ca_xfer_index_body, (VALUE) &args);
958
966
  }
959
967
  }
960
968
 
@@ -1043,16 +1051,19 @@ struct ca_xfer_addrs_args {
1043
1051
  ca_size_t n;
1044
1052
  ca_size_t *addrs;
1045
1053
  char *data;
1054
+ int dir;
1046
1055
  };
1047
1056
 
1048
1057
  static VALUE
1049
- ca_xfer_addrs_get_body (VALUE arg)
1058
+ ca_xfer_addrs_body (VALUE arg)
1050
1059
  {
1051
1060
  struct ca_xfer_addrs_args *d = (struct ca_xfer_addrs_args *) arg;
1052
1061
  ca_size_t i;
1053
- ca_xfer_addrs_dispatch(d->ca, d->n, d->addrs, d->data, CA_XFER_GET);
1054
- for ( i = 0; i < d->n; i++ ) {
1055
- ca_test_cyclic_check(d->ca, d->data + i * d->ca->bytes);
1062
+ ca_xfer_addrs_dispatch(d->ca, d->n, d->addrs, d->data, d->dir);
1063
+ if ( d->dir == CA_XFER_GET ) {
1064
+ for ( i = 0; i < d->n; i++ ) {
1065
+ ca_test_cyclic_check(d->ca, d->data + i * d->ca->bytes);
1066
+ }
1056
1067
  }
1057
1068
  return Qnil;
1058
1069
  }
@@ -1072,25 +1083,15 @@ ca_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *data, int dir)
1072
1083
  return;
1073
1084
  }
1074
1085
 
1075
- /* CA_OBJECT slow path: cyclic check (+ rb_protect on GET). */
1076
- if ( dir == CA_XFER_GET ) {
1086
+ /* CA_OBJECT slow path: cyclic check. */
1087
+ {
1077
1088
  struct ca_xfer_addrs_args args;
1078
- int state = 0;
1079
1089
  args.ca = ca;
1080
1090
  args.n = n;
1081
1091
  args.addrs = addrs;
1082
1092
  args.data = (char *) data;
1083
- ca_set_cyclic_check(ca);
1084
- rb_protect(ca_xfer_addrs_get_body, (VALUE) &args, &state);
1085
- ca_clear_cyclic_check(ca);
1086
- if ( state ) {
1087
- rb_jump_tag(state);
1088
- }
1089
- }
1090
- else {
1091
- ca_set_cyclic_check(ca);
1092
- ca_xfer_addrs_dispatch(ca, n, addrs, data, CA_XFER_PUT);
1093
- ca_clear_cyclic_check(ca);
1093
+ args.dir = dir;
1094
+ ca_with_cyclic_check(ca, dir, ca_xfer_addrs_body, (VALUE) &args);
1094
1095
  }
1095
1096
  }
1096
1097
 
@@ -1335,6 +1336,8 @@ ca_xfer_strided_walk (char *src_base,
1335
1336
  ca_size_t doff = 0;
1336
1337
  int8_t k;
1337
1338
 
1339
+ if ( ca_region_is_empty(ndim, counts) ) return;
1340
+
1338
1341
  /* slab merge -- scan innermost contig run (src_strides[k] equals the
1339
1342
  accumulated slab byte size). This collapses per-cell memcpy(_,_,
1340
1343
  bytes) loops into per-slab memcpy when the source region is row-major
@@ -1481,16 +1484,20 @@ struct ca_xfer_stride_args {
1481
1484
  ca_size_t *counts;
1482
1485
  ca_size_t *strides;
1483
1486
  char *data;
1487
+ int dir;
1484
1488
  };
1485
1489
 
1486
1490
  static VALUE
1487
- ca_xfer_stride_get_body (VALUE arg)
1491
+ ca_xfer_stride_body (VALUE arg)
1488
1492
  {
1489
1493
  struct ca_xfer_stride_args *a = (struct ca_xfer_stride_args *) arg;
1490
1494
  ca_size_t n = 1, i;
1491
1495
  int8_t k;
1492
1496
  ca_xfer_stride_dispatch(a->ca, a->starts, a->counts, a->strides, a->data,
1493
- CA_XFER_GET);
1497
+ a->dir);
1498
+ if ( a->dir != CA_XFER_GET ) {
1499
+ return Qnil;
1500
+ }
1494
1501
  /* cyclic check over the delivered cells (CA_OBJECT only). dst is contiguous
1495
1502
  row-major over counts (semantics b), so cell i is at data + i*bytes. */
1496
1503
  for ( k = 0; k < a->ca->ndim; k++ ) n *= a->counts[k];
@@ -1510,25 +1517,18 @@ ca_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
1510
1517
  rb_raise(rb_eRuntimeError, "can not store data to read-only array");
1511
1518
  }
1512
1519
 
1520
+ if ( ca_region_is_empty(ca->ndim, counts) ) return;
1521
+
1513
1522
  if ( ca->data_type != CA_OBJECT ) {
1514
1523
  ca_xfer_stride_dispatch(ca, starts, counts, strides, data, dir);
1515
1524
  return;
1516
1525
  }
1517
1526
 
1518
- if ( dir == CA_XFER_GET ) {
1527
+ {
1519
1528
  struct ca_xfer_stride_args args;
1520
- int state = 0;
1521
1529
  args.ca = ca; args.starts = starts; args.counts = counts;
1522
- args.strides = strides; args.data = (char *) data;
1523
- ca_set_cyclic_check(ca);
1524
- rb_protect(ca_xfer_stride_get_body, (VALUE) &args, &state);
1525
- ca_clear_cyclic_check(ca);
1526
- if ( state ) rb_jump_tag(state);
1527
- }
1528
- else {
1529
- ca_set_cyclic_check(ca);
1530
- ca_xfer_stride_dispatch(ca, starts, counts, strides, data, CA_XFER_PUT);
1531
- ca_clear_cyclic_check(ca);
1530
+ args.strides = strides; args.data = (char *) data; args.dir = dir;
1531
+ ca_with_cyclic_check(ca, dir, ca_xfer_stride_body, (VALUE) &args);
1532
1532
  }
1533
1533
  }
1534
1534
 
@@ -1580,6 +1580,10 @@ ca_xfer_all (void *ap, void *data, int dir)
1580
1580
  "[BUG] xfer_all not defined for object type <%i>",
1581
1581
  ca->obj_type);
1582
1582
  }
1583
+ /* An empty view moves nothing; see ca_xfer_stride. */
1584
+ if ( ca->elements == 0 ) {
1585
+ return;
1586
+ }
1583
1587
  /* Universal arena lifetime hook. ca_xfer_all is the single universal
1584
1588
  entry for materialise (to_ca -> ca_copy -> ca_copy_data ->
1585
1589
  ca_xfer_all), so wrapping here covers every outermost view type,
@@ -1615,8 +1619,74 @@ ca_xfer_all (void *ap, void *data, int dir)
1615
1619
 
1616
1620
  /* ------------------------------------------------------------------- */
1617
1621
 
1622
+ /* What follows the slot in ca_allocate: the object cells cleared, then the
1623
+ mask. Clearing the mask can create it, which for a CAObject calls Ruby.
1624
+ mask0 is the mask the array had before; see ca_attach_view_first. */
1625
+ static void
1626
+ ca_allocate_rest (CArray *ca, CArray *mask0)
1627
+ {
1628
+ if ( ca->data_type == CA_OBJECT ) { /* protection against GC */
1629
+ volatile VALUE rzero = INT2NUM(0);
1630
+ VALUE *p = (VALUE*)ca->ptr;
1631
+ ca_size_t i;
1632
+ for (i=0; i<ca->elements; i++) {
1633
+ *p++ = rzero;
1634
+ }
1635
+ }
1636
+
1637
+ ca_clear_mask(ca); /* ca_update_mask called in ca_clear_mask */
1638
+ if ( ca->mask == mask0 ) {
1639
+ ca_allocate(ca->mask);
1640
+ }
1641
+ }
1642
+
1643
+ /* The view half of ca_allocate, after the attach level is taken. Run
1644
+ under rb_protect by ca_allocate. */
1645
+ static VALUE
1646
+ ca_allocate_view (VALUE arg)
1647
+ {
1648
+ CArray *ca = (CArray *) arg;
1649
+ CArray *mask0 = ca->mask;
1650
+ if ( ! ca->ptr ) {
1651
+ ca_func[ca->obj_type].allocate(ca);
1652
+ }
1653
+ ca_allocate_rest(ca, mask0);
1654
+ return Qnil;
1655
+ }
1656
+
1618
1657
  /* make ca->ptr to point the allocated memory block */
1619
1658
 
1659
+ /* Steps a view's attach level by d. A development build also counts the
1660
+ views whose level is above zero, and the spec compares that count
1661
+ before and after every test: a window left open shows as a view that
1662
+ stays counted. */
1663
+ #ifdef CARRAY_DEV_BUILD
1664
+ static long ca_attached_views = 0;
1665
+
1666
+ #define CA_VIEW_ATTACH_STEP(ca, d) do { \
1667
+ uint32_t attach_before_ = CAVIEW(ca)->attach; \
1668
+ CAVIEW(ca)->attach += (d); \
1669
+ ca_attached_views += ( CAVIEW(ca)->attach > 0 ) - ( attach_before_ > 0 ); \
1670
+ } while (0)
1671
+
1672
+ static VALUE
1673
+ rb_ca_s_attached_views (VALUE klass)
1674
+ {
1675
+ return LONG2NUM(ca_attached_views);
1676
+ }
1677
+
1678
+ /* Set once at load from CARRAY_GENERAL_PATH; see carray_internal.h. */
1679
+ int ca_fast_paths_on = 1;
1680
+
1681
+ static VALUE
1682
+ rb_ca_s_fast_paths_enabled (VALUE klass)
1683
+ {
1684
+ return ca_fast_paths_on ? Qtrue : Qfalse;
1685
+ }
1686
+ #else
1687
+ #define CA_VIEW_ATTACH_STEP(ca, d) ( CAVIEW(ca)->attach += (d) )
1688
+ #endif
1689
+
1620
1690
  void
1621
1691
  ca_allocate (void *ap)
1622
1692
  {
@@ -1628,31 +1698,36 @@ ca_allocate (void *ap)
1628
1698
 
1629
1699
  if ( ca_is_view(ca) ) { /* view array */
1630
1700
 
1631
- CAVIEW(ca)->attach += 1; /* increments attach level */
1632
- if ( CAVIEW(ca)->attach > CA_ATTACH_MAX ) {
1701
+ if ( CAVIEW(ca)->attach >= CA_ATTACH_MAX ) {
1633
1702
  rb_raise(rb_eRuntimeError,
1634
1703
  "too large attach count of view array");
1635
1704
  }
1636
-
1637
- if ( ! ca->ptr ) {
1638
- ca_func[ca->obj_type].allocate(ap);
1705
+ CA_VIEW_ATTACH_STEP(ca, 1); /* increments attach level */
1706
+
1707
+ /* Undo the level if the slot or the mask raises, as ca_attach does.
1708
+ Left in place, the level is invisible while ptr is NULL, and the
1709
+ next attach / detach pair then leaves the view attached for good.
1710
+ A slot that got as far as publishing ca->ptr has taken what its
1711
+ detach slot releases; one that raised earlier has published
1712
+ nothing. */
1713
+ {
1714
+ char *ptr0 = ca->ptr;
1715
+ int tag = 0;
1716
+ rb_protect(ca_allocate_view, (VALUE) ca, &tag);
1717
+ if ( tag ) {
1718
+ if ( ! ptr0 && ca->ptr ) {
1719
+ ca_func[ca->obj_type].detach(ca);
1720
+ ca->ptr = NULL;
1721
+ }
1722
+ CA_VIEW_ATTACH_STEP(ca, -1);
1723
+ rb_jump_tag(tag);
1724
+ }
1639
1725
  }
1640
1726
  }
1641
1727
  else { /* entity array */
1642
1728
  ca_func[ca->obj_type].allocate(ap);
1729
+ ca_allocate_rest(ca, ca->mask); /* an entity's new mask is not yet allocated */
1643
1730
  }
1644
-
1645
- if ( ca->data_type == CA_OBJECT ) { /* protection against GC */
1646
- volatile VALUE rzero = INT2NUM(0);
1647
- VALUE *p = (VALUE*)ca->ptr;
1648
- ca_size_t i;
1649
- for (i=0; i<ca->elements; i++) {
1650
- *p++ = rzero;
1651
- }
1652
- }
1653
-
1654
- ca_clear_mask(ca); /* ca_update_mask called in ca_clear_mask */
1655
- ca_allocate(ca->mask);
1656
1731
  }
1657
1732
 
1658
1733
  /* The first attach of a view: materialise through the type's slot, then
@@ -1660,10 +1735,21 @@ ca_allocate (void *ap)
1660
1735
  static VALUE
1661
1736
  ca_attach_view_first (VALUE arg)
1662
1737
  {
1663
- CArray *ca = (CArray *) arg;
1738
+ CArray *ca = (CArray *) arg;
1739
+ CArray *mask0 = ca->mask;
1740
+ /* The mask moves in step with its view: attach, allocate and detach
1741
+ each take it one level with the view. A mask created here -- by the
1742
+ slot (a CAObject makes its mask while it attaches) or by
1743
+ ca_update_mask -- is already at the view's level, since
1744
+ ca_create_mask attaches a new mask to match an attached view. So
1745
+ only a mask that was there before the slot takes the step; stepping a
1746
+ new one too leaves it a level above the view, still attached after
1747
+ the view's last detach. */
1664
1748
  ca_func[ca->obj_type].attach(ca);
1665
1749
  ca_update_mask(ca);
1666
- ca_attach(ca->mask);
1750
+ if ( ca->mask == mask0 ) {
1751
+ ca_attach(ca->mask);
1752
+ }
1667
1753
  return Qnil;
1668
1754
  }
1669
1755
 
@@ -1684,7 +1770,7 @@ ca_attach (void *ap)
1684
1770
  rb_raise(rb_eRuntimeError,
1685
1771
  "too large attach count of view array");
1686
1772
  }
1687
- CAVIEW(ca)->attach += 1; /* increments attach level */
1773
+ CA_VIEW_ATTACH_STEP(ca, 1); /* increments attach level */
1688
1774
 
1689
1775
  if ( ! ca->ptr ) {
1690
1776
  /* A slot can raise part way (a conversion meeting a value it cannot
@@ -1700,7 +1786,7 @@ ca_attach (void *ap)
1700
1786
  ca_func[ca->obj_type].detach(ca);
1701
1787
  ca->ptr = NULL;
1702
1788
  }
1703
- CAVIEW(ca)->attach -= 1;
1789
+ CA_VIEW_ATTACH_STEP(ca, -1);
1704
1790
  rb_jump_tag(tag);
1705
1791
  }
1706
1792
  return;
@@ -1710,8 +1796,13 @@ ca_attach (void *ap)
1710
1796
  ca_func[ca->obj_type].attach(ap);
1711
1797
  }
1712
1798
 
1713
- ca_update_mask(ca);
1714
- ca_attach(ca->mask);
1799
+ {
1800
+ CArray *mask0 = ca->mask;
1801
+ ca_update_mask(ca);
1802
+ if ( ca->mask == mask0 ) { /* see ca_attach_view_first */
1803
+ ca_attach(ca->mask);
1804
+ }
1805
+ }
1715
1806
  }
1716
1807
 
1717
1808
  /* Attach every array in list[0..n-1], or, if one of those attaches
@@ -1751,6 +1842,168 @@ ca_attach_all (CArray **list, int32_t n)
1751
1842
  }
1752
1843
  }
1753
1844
 
1845
+ static VALUE
1846
+ ca_sync_all_step (VALUE arg)
1847
+ {
1848
+ ca_sync((void *) arg);
1849
+ return Qnil;
1850
+ }
1851
+
1852
+ /* Syncs every one of list[0..n-1] in turn, carrying on past one that
1853
+ raises so that the others still receive what was written, then raises
1854
+ the first exception. A throw or break cannot wait, and goes at once. */
1855
+ void
1856
+ ca_sync_all (CArray **list, int32_t n)
1857
+ {
1858
+ volatile VALUE error = Qnil;
1859
+ int32_t k;
1860
+ for ( k = 0; k < n; k++ ) {
1861
+ int tag = 0;
1862
+ rb_protect(ca_sync_all_step, (VALUE) list[k], &tag);
1863
+ if ( tag ) {
1864
+ VALUE err = rb_errinfo();
1865
+ if ( ! RTEST(rb_obj_is_kind_of(err, rb_eException)) ) {
1866
+ rb_jump_tag(tag);
1867
+ }
1868
+ if ( NIL_P(error) ) {
1869
+ error = err;
1870
+ }
1871
+ rb_set_errinfo(Qnil);
1872
+ }
1873
+ }
1874
+ if ( ! NIL_P(error) ) {
1875
+ rb_exc_raise(error);
1876
+ }
1877
+ }
1878
+
1879
+ /* An attach window over list[0..n-1] that closes however body leaves.
1880
+
1881
+ modes holds one letter per array:
1882
+ 'r' attach; detach on close
1883
+ 'w' attach; sync, then detach on close
1884
+ 'a' allocate (write only); sync, then detach on close
1885
+
1886
+ Opening takes all of the arrays or none. Closing syncs the 'w' arrays
1887
+ even when body raised or broke out -- the cells body wrote reach the
1888
+ parent, as they would for an entity, which has no separate buffer. An
1889
+ 'a' array is not synced after a raise: allocate did not gather, so its
1890
+ cells body never wrote hold nothing faithful to push back.
1891
+
1892
+ Every array is detached, in reverse order, whatever happens. body's
1893
+ own exception wins over one raised by a sync; when body returned and a
1894
+ sync raised, that exception propagates once everything is detached. */
1895
+
1896
+ typedef struct {
1897
+ int32_t n;
1898
+ CArray **list;
1899
+ const char *modes;
1900
+ int32_t opened;
1901
+ int body_done; /* body returned without raising */
1902
+ int sync_tag;
1903
+ volatile VALUE sync_err;
1904
+ VALUE (*body)(VALUE);
1905
+ VALUE arg;
1906
+ } ca_window_ctx_t;
1907
+
1908
+ static VALUE
1909
+ ca_attach_window_open (VALUE varg)
1910
+ {
1911
+ ca_window_ctx_t *w = (ca_window_ctx_t *) varg;
1912
+ for ( ; w->opened < w->n; w->opened++ ) {
1913
+ if ( w->modes[w->opened] == 'a' ) {
1914
+ ca_allocate(w->list[w->opened]);
1915
+ }
1916
+ else {
1917
+ ca_attach(w->list[w->opened]);
1918
+ }
1919
+ }
1920
+ return Qnil;
1921
+ }
1922
+
1923
+ static VALUE
1924
+ ca_attach_window_body (VALUE varg)
1925
+ {
1926
+ ca_window_ctx_t *w = (ca_window_ctx_t *) varg;
1927
+ VALUE result = w->body(w->arg);
1928
+ w->body_done = 1;
1929
+ return result;
1930
+ }
1931
+
1932
+ static VALUE
1933
+ ca_attach_window_sync (VALUE varg)
1934
+ {
1935
+ ca_sync((void *) varg);
1936
+ return Qnil;
1937
+ }
1938
+
1939
+ /* The ensure half. rb_ensure keeps the exception body raised (or the
1940
+ break it took) across this call, so a sync raising in here is caught
1941
+ and set aside rather than let replace it. */
1942
+ static VALUE
1943
+ ca_attach_window_close (VALUE varg)
1944
+ {
1945
+ ca_window_ctx_t *w = (ca_window_ctx_t *) varg;
1946
+ int32_t k;
1947
+ for ( k = w->opened - 1; k >= 0; k-- ) {
1948
+ CArray *ca = w->list[k];
1949
+ char mode = w->modes[k];
1950
+ if ( ! ca ) {
1951
+ continue;
1952
+ }
1953
+ if ( ca->ptr && ( mode == 'w' || ( mode == 'a' && w->body_done ) ) ) {
1954
+ int tag = 0;
1955
+ rb_protect(ca_attach_window_sync, (VALUE) ca, &tag);
1956
+ if ( tag && ! w->sync_tag ) {
1957
+ w->sync_tag = tag;
1958
+ w->sync_err = rb_errinfo();
1959
+ }
1960
+ rb_set_errinfo(Qnil);
1961
+ }
1962
+ if ( ca->ptr ) {
1963
+ ca_detach(ca);
1964
+ }
1965
+ }
1966
+ return Qnil;
1967
+ }
1968
+
1969
+ VALUE
1970
+ ca_attach_window (int32_t n, CArray **list, const char *modes,
1971
+ VALUE (*body)(VALUE), VALUE arg)
1972
+ {
1973
+ ca_window_ctx_t w;
1974
+ VALUE result;
1975
+ int tag = 0;
1976
+
1977
+ w.n = n;
1978
+ w.list = list;
1979
+ w.modes = modes;
1980
+ w.opened = 0;
1981
+ w.body_done = 0;
1982
+ w.sync_tag = 0;
1983
+ w.sync_err = Qnil;
1984
+ w.body = body;
1985
+ w.arg = arg;
1986
+
1987
+ rb_protect(ca_attach_window_open, (VALUE) &w, &tag);
1988
+ if ( tag ) {
1989
+ while ( w.opened > 0 ) {
1990
+ w.opened--;
1991
+ if ( w.list[w.opened] ) {
1992
+ ca_detach(w.list[w.opened]);
1993
+ }
1994
+ }
1995
+ rb_jump_tag(tag);
1996
+ }
1997
+
1998
+ result = rb_ensure(ca_attach_window_body, (VALUE) &w,
1999
+ ca_attach_window_close, (VALUE) &w);
2000
+ if ( w.sync_tag && RB_TYPE_P(w.sync_err, T_OBJECT) &&
2001
+ RTEST(rb_obj_is_kind_of(w.sync_err, rb_eException)) ) {
2002
+ rb_exc_raise(w.sync_err);
2003
+ }
2004
+ return result;
2005
+ }
2006
+
1754
2007
  /* attach parent's data to ca->ptr */
1755
2008
 
1756
2009
  void
@@ -1799,8 +2052,13 @@ ca_sync (void *ap)
1799
2052
  "can not modify read-only array");
1800
2053
  }
1801
2054
 
1802
- ca_update_mask(ca);
1803
- ca_sync(ca->mask);
2055
+ /* Only a mask that exists can hold writes: one made inside the window
2056
+ is attached with the view (ca_create_mask). Making one here would
2057
+ read the parent's mask while the window closes, and a parent that
2058
+ cannot answer would leave the window open. */
2059
+ if ( ca->mask ) {
2060
+ ca_sync(ca->mask);
2061
+ }
1804
2062
 
1805
2063
  if ( ca_is_view(ca) ) { /* view array */
1806
2064
  if ( ! CAVIEW(ca)->nosync ) { /* FIXME : */
@@ -1813,6 +2071,42 @@ ca_sync (void *ap)
1813
2071
 
1814
2072
  }
1815
2073
 
2074
+ /* Sync ap and detach it -- the close of a window a frame opened with
2075
+ ca_attach or ca_allocate. The detach happens even when the sync raises
2076
+ (a read-only array, a backing whose write fails); the sync's exception
2077
+ then propagates. Written as two calls, a raising sync skips the detach
2078
+ and the window stays open.
2079
+
2080
+ Not a replacement for every ca_sync: a view's sync slot syncs its parent
2081
+ without detaching it, because that attach belongs to the view and its
2082
+ detach slot releases it. */
2083
+
2084
+ static VALUE
2085
+ ca_sync_body (VALUE arg)
2086
+ {
2087
+ ca_sync((void *) arg);
2088
+ return Qnil;
2089
+ }
2090
+
2091
+ void
2092
+ ca_sync_detach (void *ap)
2093
+ {
2094
+ CArray *ca = (CArray *) ap;
2095
+ int tag = 0;
2096
+
2097
+ if ( ! ca ) {
2098
+ return;
2099
+ }
2100
+
2101
+ rb_protect(ca_sync_body, (VALUE) ca, &tag);
2102
+ if ( ca->ptr ) {
2103
+ ca_detach(ca);
2104
+ }
2105
+ if ( tag ) {
2106
+ rb_jump_tag(tag);
2107
+ }
2108
+ }
2109
+
1816
2110
  /* make ca->ptr to be detached */
1817
2111
 
1818
2112
  void
@@ -1833,38 +2127,86 @@ ca_detach (void *ap)
1833
2127
  if ( CAVIEW(ca)->attach == 1 ) {
1834
2128
  ca_func[ca->obj_type].detach(ap);
1835
2129
  }
1836
- CAVIEW(ca)->attach -= 1;
2130
+ CA_VIEW_ATTACH_STEP(ca, -1);
1837
2131
  }
1838
2132
  else { /* entity array */
1839
2133
  ca_func[ca->obj_type].detach(ap);
1840
2134
  }
1841
2135
 
1842
- ca_update_mask(ca);
1843
- ca_detach(ca->mask);
2136
+ /* The mask, if there is one, is at the view's level (see
2137
+ ca_attach_view_first). A mask that does not exist yet is not made
2138
+ here: it would be made unattached, and making it reads the parent's
2139
+ mask, which may raise with the view half detached. */
2140
+ if ( ca->mask ) {
2141
+ ca_detach(ca->mask);
2142
+ }
1844
2143
  }
1845
2144
 
1846
2145
  /* multiple versions of ca_allocate, ca_attach, ca_sync, ca_detach */
1847
2146
 
2147
+ /* ca_allocate_n and ca_attach_n take all of their arrays or none: when
2148
+ one raises, the ones already taken are detached before the raise
2149
+ propagates. */
2150
+
2151
+ typedef struct {
2152
+ CArray **list;
2153
+ int32_t n;
2154
+ int32_t done;
2155
+ } ca_allocate_all_ctx_t;
2156
+
2157
+ static VALUE
2158
+ ca_allocate_all_body (VALUE arg)
2159
+ {
2160
+ ca_allocate_all_ctx_t *c = (ca_allocate_all_ctx_t *) arg;
2161
+ for ( ; c->done < c->n; c->done++ ) {
2162
+ ca_allocate(c->list[c->done]);
2163
+ }
2164
+ return Qnil;
2165
+ }
2166
+
2167
+ static void
2168
+ ca_allocate_all (CArray **list, int32_t n)
2169
+ {
2170
+ ca_allocate_all_ctx_t c;
2171
+ int tag = 0;
2172
+ c.list = list;
2173
+ c.n = n;
2174
+ c.done = 0;
2175
+ rb_protect(ca_allocate_all_body, (VALUE) &c, &tag);
2176
+ if ( tag ) {
2177
+ while ( c.done > 0 ) {
2178
+ ca_detach(list[--c.done]);
2179
+ }
2180
+ rb_jump_tag(tag);
2181
+ }
2182
+ }
2183
+
1848
2184
  void
1849
2185
  ca_allocate_n (int n, ...)
1850
2186
  {
2187
+ CArray **list = ALLOCA_N(CArray *, n > 0 ? n : 1);
1851
2188
  va_list args;
2189
+ int i;
1852
2190
  va_start(args, n);
1853
- while ( n-- ) {
1854
- ca_allocate(va_arg(args, CArray *));
2191
+ for ( i = 0; i < n; i++ ) {
2192
+ list[i] = va_arg(args, CArray *);
1855
2193
  }
1856
2194
  va_end(args);
2195
+ ca_allocate_all(list, n);
1857
2196
  }
1858
2197
 
1859
2198
  void
1860
2199
  ca_attach_n (int n, ...)
1861
2200
  {
2201
+ CArray **list = ALLOCA_N(CArray *, n > 0 ? n : 1);
1862
2202
  va_list args;
2203
+ int i;
1863
2204
  va_start(args, n);
1864
- while ( n-- ) {
1865
- ca_attach(va_arg(args, CArray *));
2205
+ for ( i = 0; i < n; i++ ) {
2206
+ list[i] = va_arg(args, CArray *);
1866
2207
  }
1867
2208
  va_end(args);
2209
+ ca_attach_all(list, n);
1868
2210
  }
1869
2211
 
1870
2212
  void
@@ -1989,15 +2331,12 @@ ca_fill_stride (void *ap, ca_size_t base, int8_t ndim,
1989
2331
  ca_size_t *counts, ca_size_t *steps, void *ptr)
1990
2332
  {
1991
2333
  CArray *ca = (CArray *) ap;
1992
- int8_t k;
1993
2334
 
1994
2335
  if ( ca_is_readonly(ca) ) {
1995
2336
  rb_raise(rb_eRuntimeError, "can not fill data to read-only array");
1996
2337
  }
1997
2338
 
1998
- for ( k = 0; k < ndim; k++ ) {
1999
- if ( counts[k] <= 0 ) return;
2000
- }
2339
+ if ( ca_region_is_empty(ndim, counts) ) return;
2001
2340
 
2002
2341
  if ( ca_func[ca->obj_type].fill_stride ) {
2003
2342
  ca_func[ca->obj_type].fill_stride(ap, base, ndim, counts, steps, ptr);
@@ -2144,7 +2483,7 @@ ca_fill (void *ap, void *aptr)
2144
2483
  char *ptr = (char *) aptr;
2145
2484
 
2146
2485
  if ( ca_is_readonly(ca) ) {
2147
- rb_raise(rb_eRuntimeError, "can't fill read-only carray");
2486
+ rb_raise(rb_eRuntimeError, "can not fill data to read-only array");
2148
2487
  }
2149
2488
 
2150
2489
  ca_fill_data(ap, ptr);
@@ -2425,7 +2764,7 @@ rb_ca_members (VALUE self)
2425
2764
  {
2426
2765
  volatile VALUE data_class = rb_ca_data_class(self);
2427
2766
  if ( NIL_P(data_class) ) {
2428
- rb_raise(rb_eRuntimeError, "carray doesn't have data class");
2767
+ rb_raise(rb_eTypeError, "carray doesn't have data class");
2429
2768
  }
2430
2769
  else {
2431
2770
  return rb_obj_clone(rb_const_get(data_class, rb_intern("MEMBERS")));
@@ -2438,8 +2777,8 @@ rb_ca_members (VALUE self)
2438
2777
  (= Face strip): the @member cache lives on self (the Face) while the
2439
2778
  actual field view is the CAField on the parent. A plain FIXLEN entity
2440
2779
  keeps self == receiver. */
2441
- VALUE
2442
- rb_ca_face_field (VALUE self, VALUE sym)
2780
+ static VALUE
2781
+ ca_face_field_lookup (VALUE self, VALUE sym)
2443
2782
  {
2444
2783
  volatile VALUE data_class = rb_ca_data_class(self);
2445
2784
  volatile VALUE member;
@@ -2448,7 +2787,7 @@ rb_ca_face_field (VALUE self, VALUE sym)
2448
2787
  CArray *ca;
2449
2788
 
2450
2789
  if ( NIL_P(data_class) ) {
2451
- rb_raise(rb_eRuntimeError, "carray doesn't have data class");
2790
+ rb_raise(rb_eTypeError, "carray doesn't have data class");
2452
2791
  }
2453
2792
 
2454
2793
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
@@ -2464,7 +2803,9 @@ rb_ca_face_field (VALUE self, VALUE sym)
2464
2803
  is local to the derived view; field views are still on its parent
2465
2804
  (which itself is on the entity). */
2466
2805
  member = rb_hash_new();
2467
- rb_ivar_set(self, rb_intern("member"), member);
2806
+ if ( ! OBJ_FROZEN(self) ) {
2807
+ rb_ivar_set(self, rb_intern("member"), member);
2808
+ }
2468
2809
  }
2469
2810
 
2470
2811
  if ( rb_obj_is_kind_of(sym, rb_cInteger) ) {
@@ -2490,7 +2831,7 @@ rb_ca_face_field (VALUE self, VALUE sym)
2490
2831
  if ( TYPE(sym) != T_STRING ) {
2491
2832
  sym = rb_funcall(sym, rb_intern("to_s"), 0);
2492
2833
  }
2493
- rb_raise(rb_eRuntimeError,
2834
+ rb_raise(rb_eArgError,
2494
2835
  "can't find data_member named <%s>", StringValuePtr(sym));
2495
2836
  }
2496
2837
  Check_Type(info, T_ARRAY);
@@ -2532,12 +2873,53 @@ rb_ca_face_field (VALUE self, VALUE sym)
2532
2873
  return obj;
2533
2874
  }
2534
2875
  }
2876
+ /* `endian:` is not an option of #field; the member's byte order is a
2877
+ view over the field, as CAStruct#[] reads it. */
2878
+ {
2879
+ volatile VALUE opts = rb_ary_entry(info, 2);
2880
+ volatile VALUE endian = Qnil;
2881
+ if ( RB_TYPE_P(opts, T_HASH) ) {
2882
+ endian = rb_hash_aref(opts, ID2SYM(rb_intern("endian")));
2883
+ }
2884
+ if ( ! NIL_P(endian) ) {
2885
+ volatile VALUE field_opts = rb_hash_dup(opts);
2886
+ rb_hash_delete(field_opts, ID2SYM(rb_intern("endian")));
2887
+ if ( RHASH_SIZE(field_opts) > 0 ) {
2888
+ obj = rb_funcall(receiver, rb_intern("field"), 3,
2889
+ rb_ary_entry(info, 0), rb_ary_entry(info, 1),
2890
+ field_opts);
2891
+ }
2892
+ else {
2893
+ obj = rb_funcall(receiver, rb_intern("field"), 2,
2894
+ rb_ary_entry(info, 0), rb_ary_entry(info, 1));
2895
+ }
2896
+ obj = rb_funcall(obj, rb_intern("endian"), 1, endian);
2897
+ rb_hash_aset(member, sym, obj);
2898
+ return obj;
2899
+ }
2900
+ }
2535
2901
  obj = rb_apply(receiver, rb_intern("field"), info);
2536
2902
  rb_hash_aset(member, sym, obj);
2537
2903
  return obj;
2538
2904
  }
2539
2905
  }
2540
2906
 
2907
+ /* A field is a view on the record array's storage, below the Face, so the
2908
+ read-only state of the record array (frozen, or a read-only view) is not
2909
+ on its parent chain; the field carries it itself. */
2910
+ VALUE
2911
+ rb_ca_face_field (VALUE self, VALUE sym)
2912
+ {
2913
+ volatile VALUE obj = ca_face_field_lookup(self, sym);
2914
+ CArray *ca, *co;
2915
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
2916
+ if ( ca_is_readonly(ca) && rb_obj_is_carray(obj) ) {
2917
+ TypedData_Get_Struct(obj, CArray, &carray_data_type, co);
2918
+ ca_set_flag(co, CA_FLAG_READ_ONLY);
2919
+ }
2920
+ return obj;
2921
+ }
2922
+
2541
2923
  /* @overload fields
2542
2924
 
2543
2925
  (Reference) Returns an array of data class members (fields)
@@ -2550,7 +2932,7 @@ rb_ca_fields (VALUE self)
2550
2932
  volatile VALUE member_names, list;
2551
2933
  int i;
2552
2934
  if ( NIL_P(data_class) ) {
2553
- rb_raise(rb_eRuntimeError, "carray doesn't have data class");
2935
+ rb_raise(rb_eTypeError, "carray doesn't have data class");
2554
2936
  }
2555
2937
  member_names = rb_const_get(data_class, rb_intern("MEMBERS"));
2556
2938
  list = rb_ary_new2(RARRAY_LEN(member_names));
@@ -2573,7 +2955,7 @@ rb_ca_fields_at (int argc, VALUE *argv, VALUE self)
2573
2955
  volatile VALUE member_names, list;
2574
2956
  int i;
2575
2957
  if ( NIL_P(data_class) ) {
2576
- rb_raise(rb_eRuntimeError, "carray doesn't have data class");
2958
+ rb_raise(rb_eTypeError, "carray doesn't have data class");
2577
2959
  }
2578
2960
  member_names = rb_ary_new4(argc, argv);
2579
2961
  list = rb_ary_new2(RARRAY_LEN(member_names));
@@ -2603,6 +2985,16 @@ Init_carray_core (void)
2603
2985
  rb_define_method(rb_cCArray, "__attach__", rb_ca__attach__, 0);
2604
2986
  rb_define_method(rb_cCArray, "__sync__", rb_ca__sync__, 0);
2605
2987
  rb_define_method(rb_cCArray, "__detach__", rb_ca__detach__, 0);
2988
+
2989
+ rb_define_singleton_method(rb_cCArray, "__attached_views__",
2990
+ rb_ca_s_attached_views, 0);
2991
+
2992
+ {
2993
+ const char *general = getenv("CARRAY_GENERAL_PATH");
2994
+ ca_fast_paths_on = !( general && general[0] && strcmp(general, "0") );
2995
+ }
2996
+ rb_define_singleton_method(rb_cCArray, "__fast_paths_enabled__",
2997
+ rb_ca_s_fast_paths_enabled, 0);
2606
2998
  #endif
2607
2999
 
2608
3000
  rb_define_method(rb_cCArray, "members", rb_ca_members, 0);