carray 3.0.2 → 3.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +568 -0
  3. data/README.md +6 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +220 -0
  7. data/ext/ca_axis_group.c +201 -123
  8. data/ext/ca_binop_dispatch.c +46 -52
  9. data/ext/ca_binop_dispatch.h +1 -1
  10. data/ext/ca_categorical_iterator.c +249 -183
  11. data/ext/ca_compare.c +7 -12
  12. data/ext/ca_for_buffer.h +21 -68
  13. data/ext/ca_group_iter.c +38 -23
  14. data/ext/ca_kernel_iterator.c +1765 -2076
  15. data/ext/ca_kernel_iterator.h +584 -566
  16. data/ext/ca_monop_dispatch.c +86 -63
  17. data/ext/ca_monop_dispatch.h +5 -0
  18. data/ext/ca_obj_array.c +223 -56
  19. data/ext/ca_obj_bincmp.c +172 -138
  20. data/ext/ca_obj_binop.c +116 -230
  21. data/ext/ca_obj_bitarray.c +40 -26
  22. data/ext/ca_obj_bitfield.c +100 -68
  23. data/ext/ca_obj_block.c +19 -9
  24. data/ext/ca_obj_byte_swap.c +45 -46
  25. data/ext/ca_obj_const_string.c +269 -44
  26. data/ext/ca_obj_face.c +612 -89
  27. data/ext/ca_obj_face.h +111 -47
  28. data/ext/ca_obj_fake.c +36 -79
  29. data/ext/ca_obj_farray.c +7 -3
  30. data/ext/ca_obj_field.c +33 -8
  31. data/ext/ca_obj_fixlen_string.c +23 -24
  32. data/ext/ca_obj_grid.c +541 -210
  33. data/ext/ca_obj_meld.c +331 -111
  34. data/ext/ca_obj_moncmp.c +80 -65
  35. data/ext/ca_obj_monop.c +177 -149
  36. data/ext/ca_obj_object.c +664 -248
  37. data/ext/ca_obj_record.c +63 -23
  38. data/ext/ca_obj_reduce.c +18 -16
  39. data/ext/ca_obj_refer.c +140 -58
  40. data/ext/ca_obj_remap.c +85 -24
  41. data/ext/ca_obj_repeat.c +24 -9
  42. data/ext/ca_obj_roll.c +121 -16
  43. data/ext/ca_obj_select.c +61 -58
  44. data/ext/ca_obj_select_axis.c +73 -107
  45. data/ext/ca_obj_shift.c +32 -102
  46. data/ext/ca_obj_stack.c +372 -122
  47. data/ext/ca_obj_stride.c +348 -140
  48. data/ext/ca_obj_string.c +88 -22
  49. data/ext/ca_obj_tile.c +38 -21
  50. data/ext/ca_obj_time.c +45 -31
  51. data/ext/ca_obj_timedelta.c +43 -29
  52. data/ext/ca_obj_transpose.c +17 -6
  53. data/ext/ca_obj_triop.c +125 -141
  54. data/ext/ca_obj_window.c +330 -44
  55. data/ext/ca_op_ipower.c +24 -11
  56. data/ext/ca_op_powi.h +57 -30
  57. data/ext/ca_pow5_table.h +656 -0
  58. data/ext/ca_sort_kernels.h +70 -0
  59. data/ext/ca_sweep_engine.c +104 -81
  60. data/ext/ca_sweep_engine.h +7 -20
  61. data/ext/ca_triop_dispatch.c +4 -1
  62. data/ext/ca_triop_dispatch.h +5 -2
  63. data/ext/caframe_csv_reader.c +542 -0
  64. data/ext/carray.h +241 -92
  65. data/ext/carray_access.c +608 -276
  66. data/ext/carray_address_basis.c +494 -97
  67. data/ext/carray_attribute.c +129 -4
  68. data/ext/carray_bincount.c +39 -7
  69. data/ext/carray_broadcast.c +56 -19
  70. data/ext/carray_build_flags.h +1 -1
  71. data/ext/carray_call_cfunc.c +108 -28
  72. data/ext/carray_cast.c +1576 -223
  73. data/ext/carray_cast_func.rb +77 -24
  74. data/ext/carray_class.c +4 -3
  75. data/ext/carray_conversion.c +208 -75
  76. data/ext/carray_copy.c +82 -24
  77. data/ext/carray_core.c +646 -162
  78. data/ext/carray_count.c +171 -88
  79. data/ext/carray_element.c +94 -60
  80. data/ext/carray_factorize.c +670 -324
  81. data/ext/carray_generate.c +260 -83
  82. data/ext/carray_histogram.c +209 -106
  83. data/ext/carray_hold.c +14 -16
  84. data/ext/carray_index_classifier.c +422 -243
  85. data/ext/carray_internal.h +692 -3
  86. data/ext/carray_kernels_bincmp.c +7 -0
  87. data/ext/carray_kernels_binop.c +487 -120
  88. data/ext/carray_kernels_init.c +653 -50
  89. data/ext/carray_kernels_moncmp.c +15 -8
  90. data/ext/carray_kernels_monop.c +99 -43
  91. data/ext/carray_kernels_reduce_aggregate.c +3925 -6112
  92. data/ext/carray_kernels_reduce_boolean.c +112 -36
  93. data/ext/carray_kernels_reduce_cumulative.c +2127 -6903
  94. data/ext/carray_kernels_reduce_extreme.c +3032 -2892
  95. data/ext/carray_kernels_reduce_variance.c +439 -500
  96. data/ext/carray_kernels_scan.c +1097 -488
  97. data/ext/carray_kernels_search.c +3787 -2176
  98. data/ext/carray_kernels_sort.c +1200 -920
  99. data/ext/carray_kernels_triop.c +479 -7
  100. data/ext/carray_key_windows.c +216 -0
  101. data/ext/carray_lazy.c +967 -153
  102. data/ext/carray_loop.c +81 -65
  103. data/ext/carray_mask.c +910 -324
  104. data/ext/carray_math_kernel.h +1 -0
  105. data/ext/carray_median_percentile.c +299 -100
  106. data/ext/carray_memory_view.c +872 -758
  107. data/ext/carray_operator.c +1171 -364
  108. data/ext/carray_order.c +505 -133
  109. data/ext/carray_partition.c +112 -65
  110. data/ext/carray_random.c +327 -239
  111. data/ext/carray_round.c +273 -0
  112. data/ext/carray_scatter.c +346 -78
  113. data/ext/carray_segment.c +257 -0
  114. data/ext/carray_slab.c +249 -86
  115. data/ext/carray_sort.c +224 -227
  116. data/ext/carray_sort_kernel.c +8 -1
  117. data/ext/carray_struct.c +118 -39
  118. data/ext/carray_test.c +193 -69
  119. data/ext/carray_time_text.c +1182 -0
  120. data/ext/carray_undef.c +4 -0
  121. data/ext/carray_utils.c +579 -113
  122. data/ext/extconf.rb +27 -2
  123. data/ext/mk_call_cfunc.rb +19 -4
  124. data/ext/mk_pow5_table.rb +35 -0
  125. data/ext/mkkernel.rb +1202 -1014
  126. data/ext/ruby_carray.c +17 -5
  127. data/ext/version.h +4 -4
  128. data/ext/version.rb +4 -2
  129. data/lib/carray/attribute.rb +160 -17
  130. data/lib/carray/autoload_carray.rb +23 -27
  131. data/lib/carray/autoload_method_extension.rb +20 -5
  132. data/lib/carray/axis_group.rb +360 -63
  133. data/lib/carray/basics.rb +162 -28
  134. data/lib/carray/bincount_nd.rb +18 -2
  135. data/lib/carray/block_iterator.rb +231 -58
  136. data/lib/carray/boolean_reduce.rb +34 -42
  137. data/lib/carray/categorical.rb +242 -52
  138. data/lib/carray/categorical_iterator.rb +437 -827
  139. data/lib/carray/complex.rb +41 -34
  140. data/lib/carray/conditional.rb +150 -14
  141. data/lib/carray/const_string.rb +59 -4
  142. data/lib/carray/construct.rb +15 -11
  143. data/lib/carray/data_type_extension.rb +1 -0
  144. data/lib/carray/distance_limit.rb +77 -0
  145. data/lib/carray/fixlen_string.rb +7 -1
  146. data/lib/carray/frame/column_verbs.rb +70 -0
  147. data/lib/carray/frame/concat.rb +157 -6
  148. data/lib/carray/frame/convert.rb +82 -7
  149. data/lib/carray/frame/csv_parser.rb +688 -87
  150. data/lib/carray/frame/describe.rb +80 -0
  151. data/lib/carray/frame/frame.rb +369 -45
  152. data/lib/carray/frame/group.rb +631 -90
  153. data/lib/carray/frame/io.rb +506 -79
  154. data/lib/carray/frame/join.rb +411 -62
  155. data/lib/carray/frame/records.rb +116 -19
  156. data/lib/carray/frame/reshape.rb +455 -0
  157. data/lib/carray/frame/sort.rb +30 -18
  158. data/lib/carray/frame/verbs.rb +868 -61
  159. data/lib/carray/frame/windows.rb +117 -0
  160. data/lib/carray/frame.rb +4 -0
  161. data/lib/carray/fuse_source.rb +118 -2
  162. data/lib/carray/fusion.rb +257 -42
  163. data/lib/carray/histogram.rb +6 -1
  164. data/lib/carray/inspect.rb +54 -13
  165. data/lib/carray/iterator.rb +99 -9
  166. data/lib/carray/key_windows.rb +620 -0
  167. data/lib/carray/lazy.rb +216 -193
  168. data/lib/carray/mask_gap_fill.rb +112 -92
  169. data/lib/carray/math.rb +32 -19
  170. data/lib/carray/meld_reduce.rb +52 -21
  171. data/lib/carray/methods/align_addr.rb +8 -4
  172. data/lib/carray/methods/bin.rb +26 -18
  173. data/lib/carray/methods/bincount.rb +18 -2
  174. data/lib/carray/methods/bit_string.rb +15 -4
  175. data/lib/carray/methods/choose.rb +16 -3
  176. data/lib/carray/methods/composition.rb +18 -10
  177. data/lib/carray/methods/factorize.rb +2 -2
  178. data/lib/carray/methods/gather_nd.rb +25 -5
  179. data/lib/carray/methods/insert_block.rb +9 -14
  180. data/lib/carray/methods/is_between.rb +59 -0
  181. data/lib/carray/methods/is_in.rb +140 -10
  182. data/lib/carray/methods/join.rb +2 -7
  183. data/lib/carray/methods/lag.rb +49 -0
  184. data/lib/carray/methods/locate_addr.rb +182 -45
  185. data/lib/carray/methods/mode.rb +18 -1
  186. data/lib/carray/methods/nunique.rb +11 -6
  187. data/lib/carray/methods/repeat.rb +1 -20
  188. data/lib/carray/methods/resize.rb +21 -23
  189. data/lib/carray/methods/snap.rb +34 -12
  190. data/lib/carray/methods/unique.rb +11 -2
  191. data/lib/carray/methods/value_counts.rb +4 -11
  192. data/lib/carray/record_order.rb +208 -0
  193. data/lib/carray/rng.rb +3 -0
  194. data/lib/carray/runtime.rb +65 -17
  195. data/lib/carray/segment_iterator.rb +1221 -0
  196. data/lib/carray/serialize.rb +214 -30
  197. data/lib/carray/slab_iterator.rb +101 -27
  198. data/lib/carray/stack.rb +10 -4
  199. data/lib/carray/string.rb +105 -5
  200. data/lib/carray/string_operation_extension.rb +12 -4
  201. data/lib/carray/struct.rb +137 -58
  202. data/lib/carray/struct_builder.rb +124 -17
  203. data/lib/carray/time.rb +1222 -186
  204. data/lib/carray/window_iterator.rb +242 -81
  205. data/lib/carray.rb +9 -2
  206. data/yard-stubs/ca_obj_array.rb +4 -4
  207. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  208. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  209. data/yard-stubs/ca_obj_fake.rb +1 -1
  210. data/yard-stubs/ca_obj_field.rb +5 -1
  211. data/yard-stubs/ca_obj_grid.rb +21 -10
  212. data/yard-stubs/ca_obj_refer.rb +5 -4
  213. data/yard-stubs/ca_obj_roll.rb +2 -3
  214. data/yard-stubs/ca_obj_shift.rb +5 -1
  215. data/yard-stubs/ca_obj_stride.rb +10 -7
  216. data/yard-stubs/ca_obj_tile.rb +1 -1
  217. data/yard-stubs/ca_obj_transpose.rb +1 -1
  218. data/yard-stubs/ca_obj_window.rb +9 -5
  219. data/yard-stubs/carray_access.rb +11 -5
  220. data/yard-stubs/carray_attribute.rb +0 -5
  221. data/yard-stubs/carray_broadcast.rb +1 -1
  222. data/yard-stubs/carray_cast.rb +88 -43
  223. data/yard-stubs/carray_conversion.rb +15 -2
  224. data/yard-stubs/carray_copy.rb +12 -5
  225. data/yard-stubs/carray_core.rb +3 -3
  226. data/yard-stubs/carray_count.rb +9 -0
  227. data/yard-stubs/carray_element.rb +10 -4
  228. data/yard-stubs/carray_generate.rb +21 -1
  229. data/yard-stubs/carray_lazy.rb +5 -1
  230. data/yard-stubs/carray_loop.rb +4 -1
  231. data/yard-stubs/carray_mask.rb +60 -12
  232. data/yard-stubs/carray_math.rb +52 -14
  233. data/yard-stubs/carray_median_percentile.rb +15 -8
  234. data/yard-stubs/carray_memory_view.rb +40 -23
  235. data/yard-stubs/carray_order.rb +61 -29
  236. data/yard-stubs/carray_random.rb +13 -4
  237. data/yard-stubs/carray_scatter.rb +21 -9
  238. data/yard-stubs/carray_segment.rb +72 -0
  239. data/yard-stubs/carray_slab.rb +10 -2
  240. data/yard-stubs/carray_sort.rb +49 -8
  241. data/yard-stubs/carray_test.rb +15 -3
  242. data/yard-stubs/carray_utils.rb +0 -26
  243. metadata +19 -5
  244. data/ext/ca_for_each_element.h +0 -239
  245. data/ext/carray_kernels_map.c +0 -3467
  246. data/lib/carray/arrow_tensor.rb +0 -401
  247. data/lib/carray/table.rb +0 -108
data/ext/carray_core.c CHANGED
@@ -323,12 +323,13 @@ ca_install_obj_type (VALUE klass,
323
323
  const ca_operation_function_t *func,
324
324
  size_t func_size)
325
325
  {
326
- int obj_type = ca_obj_num++;
326
+ int obj_type;
327
327
 
328
- if ( ca_obj_num >= CA_OBJ_TYPE_MAX ) {
328
+ /* The obj_type parameters of ca_array_alloc / ca_stride_setup are
329
+ int8_t, so a number of 128 or above cannot be passed to them. */
330
+ if ( ca_obj_num > INT8_MAX || ca_obj_num + 1 >= CA_OBJ_TYPE_MAX ) {
329
331
  rb_raise(rb_eRuntimeError,
330
- "internal: too many CArray object types installed <CA_OBJ_TYPE_MAX = %i>",
331
- CA_OBJ_TYPE_MAX);
332
+ "internal: too many CArray object types installed (an obj_type is numbered below 128)");
332
333
  }
333
334
 
334
335
  if ( func_size > sizeof(ca_operation_function_t) ) {
@@ -338,16 +339,20 @@ ca_install_obj_type (VALUE klass,
338
339
  func_size, sizeof(ca_operation_function_t));
339
340
  }
340
341
 
341
- /* Everything from xfer_index on was appended after the table's first
342
- shape, so a caller may legitimately stop short of it. Anything shorter
343
- than that cannot dispatch at all. */
344
- if ( func_size < offsetof(ca_operation_function_t, xfer_index) ) {
342
+ /* xfer_index, xfer_addrs and xfer_all have no default, so the table
343
+ reaches through xfer_all; the slots after it fall back when NULL. */
344
+ if ( func_size < offsetof(ca_operation_function_t, xfer_all) +
345
+ sizeof(((ca_operation_function_t *) 0)->xfer_all) ) {
345
346
  rb_raise(rb_eRuntimeError,
346
347
  "operation table is too small to dispatch (%zu < %zu)",
347
348
  func_size,
348
- (size_t) offsetof(ca_operation_function_t, xfer_index));
349
+ (size_t) (offsetof(ca_operation_function_t, xfer_all) +
350
+ sizeof(((ca_operation_function_t *) 0)->xfer_all)));
349
351
  }
350
352
 
353
+ /* A refused table takes no number. */
354
+ obj_type = ca_obj_num++;
355
+
351
356
  /* Copy by the caller's length and zero the rest: slots this build knows
352
357
  about but the caller does not are NULL, which every dispatcher already
353
358
  reads as "not provided". */
@@ -425,7 +430,6 @@ ca_attach_is_alias (void *ap)
425
430
  CArray *ca = (CArray *) ap;
426
431
  extern ca_operation_function_t ca_stride_func;
427
432
  extern ca_operation_function_t ca_lazy_marker_func;
428
- extern int ca_stride_is_contiguous (CAStride *ca);
429
433
  extern int ca_stride_attach_aliases_root (CAStride *ca);
430
434
 
431
435
  if ( ca == NULL ) return 0;
@@ -440,21 +444,13 @@ ca_attach_is_alias (void *ap)
440
444
  return ca_attach_is_alias(((CAView *) ca)->parent);
441
445
  }
442
446
 
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. */
447
+ /* CAStride family share ca_stride_func.attach, which aliases the root's
448
+ memory iff the strides composed to the root are row-major contiguous
449
+ and the root has memory to lend. Otherwise attach builds its own
450
+ buffer and writes through ca->ptr reach the root only via ca_sync, so
451
+ saying "alias" would let a caller write and skip the sync. */
455
452
  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);
453
+ return ca_stride_attach_aliases_root((CAStride *) ca);
458
454
  }
459
455
  return 0;
460
456
  }
@@ -566,7 +562,7 @@ rb_ca_new_reduced (VALUE self, int8_t *slab_axes, int8_t naxes, int32_t data_typ
566
562
  ca_size_t bytes = ca_sizeof[data_type];
567
563
  if ( bytes <= 0 ) {
568
564
  rb_raise(rb_eArgError,
569
- "rb_ca_new_reduced: data_type=%d unsupported (CA_FIXLEN/OBJECT not supported)",
565
+ "rb_ca_new_reduced: data_type=%d has no fixed width (use rb_ca_new_reduced_bytes)",
570
566
  (int) data_type);
571
567
  }
572
568
  return rb_ca_new_reduced_bytes(self, slab_axes, naxes, data_type, bytes, keep_axis);
@@ -574,9 +570,8 @@ rb_ca_new_reduced (VALUE self, int8_t *slab_axes, int8_t naxes, int32_t data_typ
574
570
 
575
571
  /* ------------------------------------------------------------------- */
576
572
 
577
- /* Parse the variadic axis argument of a reduction kernel into a
578
- sort-ascending
579
- int8_t array, with full validation.
573
+ /* Parse the variadic axis argument of a reduction kernel into an int8_t
574
+ array, with full validation.
580
575
 
581
576
  Accepts:
582
577
  - Integer args: kernel(0, 2, 3) → axes = {0, 2, 3}
@@ -584,8 +579,9 @@ rb_ca_new_reduced (VALUE self, int8_t *slab_axes, int8_t naxes, int32_t data_typ
584
579
  - Negative axes (Python-style): -1 = innermost, normalised to
585
580
  positive in-range indices before validation
586
581
 
582
+ argc == 0 means every axis (a full reduction).
583
+
587
584
  Validation (raises ArgumentError on failure):
588
- - argc == 0 → no axes given
589
585
  - naxes > ca->ndim → too many axes
590
586
  - any axis out of [0, ca->ndim) → range
591
587
  - duplicate axes → duplicate
@@ -598,8 +594,12 @@ rb_ca_new_reduced (VALUE self, int8_t *slab_axes, int8_t naxes, int32_t data_typ
598
594
  to NUM2SIZE) of length `count`, normalises + range-checks + duplicate-
599
595
  checks, fills out_axes[] in input order. Shared between the legacy
600
596
  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. */
597
+ (rb_ca_parse_reduce_axes_kw). ctx names the method in error messages;
598
+ the two public entries pass the method the user called. */
599
+ /* ctx NULL names the method the user called, looked up only when a
600
+ message is built: the lookup walks the frames. */
601
+ #define CA_CTX_NAME(ctx) ( (ctx) ? (ctx) : ca_calling_method_name() )
602
+
603
603
  static int8_t
604
604
  parse_axes_items (const VALUE *items, int count, CArray *ca,
605
605
  int8_t *out_axes, const char *ctx)
@@ -608,31 +608,25 @@ parse_axes_items (const VALUE *items, int count, CArray *ca,
608
608
  int8_t seen[CA_RANK_MAX];
609
609
 
610
610
  if ( count <= 0 ) {
611
- rb_raise(rb_eArgError, "%s: empty axes array", ctx);
611
+ rb_raise(rb_eArgError, "%s: empty axes array", CA_CTX_NAME(ctx));
612
612
  }
613
- if ( count > CA_RANK_MAX ) {
613
+ if ( count > CA_RANK_MAX ) { /* rank-check: a list of axes, no longer than the rank */
614
614
  rb_raise(rb_eArgError,
615
615
  "%s: too many axes (%d > CA_RANK_MAX=%d)",
616
- ctx, count, CA_RANK_MAX);
616
+ CA_CTX_NAME(ctx), count, CA_RANK_MAX);
617
617
  }
618
618
  if ( count > ca->ndim ) {
619
619
  rb_raise(rb_eArgError,
620
620
  "%s: too many axes (%d > ndim=%d)",
621
- ctx, count, (int) ca->ndim);
621
+ CA_CTX_NAME(ctx), count, (int) ca->ndim);
622
622
  }
623
623
 
624
624
  for ( i = 0; i < CA_RANK_MAX; i++ ) seen[i] = 0;
625
625
  for ( i = 0; i < count; i++ ) {
626
- ca_size_t a = NUM2SIZE(items[i]);
627
- if ( a < 0 ) a += ca->ndim;
628
- 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);
632
- }
626
+ ca_size_t a = ca_normalize_axis_arg(items[i], (int) ca->ndim, ctx);
633
627
  if ( seen[a] ) {
634
628
  rb_raise(rb_eArgError,
635
- "%s: duplicate axis %ld", ctx, (long) a);
629
+ "%s: duplicate axis %ld", CA_CTX_NAME(ctx), (long) a);
636
630
  }
637
631
  seen[a] = 1;
638
632
  out_axes[i] = (int8_t) a;
@@ -665,8 +659,7 @@ rb_ca_parse_reduce_axes (int argc, VALUE *argv, CArray *ca, int8_t *out_axes)
665
659
  items = (const VALUE *) RARRAY_CONST_PTR(arr);
666
660
  }
667
661
 
668
- return parse_axes_items(items, count, ca, out_axes,
669
- "rb_ca_parse_reduce_axes");
662
+ return parse_axes_items(items, count, ca, out_axes, NULL);
670
663
  }
671
664
 
672
665
  /* Kwarg form of rb_ca_parse_reduce_axes — accepts the `axis:` value as
@@ -705,16 +698,15 @@ rb_ca_parse_reduce_axes_kw_ctx (VALUE axis_val, CArray *ca, int8_t *out_axes,
705
698
  }
706
699
 
707
700
  rb_raise(rb_eTypeError,
708
- "%s: axis: must be nil, Integer, or "
709
- "Array of Integer (got %"PRIsVALUE")",
710
- ctx, rb_obj_class(axis_val));
701
+ "%s: axis must be nil, an Integer or an Array of Integer "
702
+ "(got %"PRIsVALUE")",
703
+ CA_CTX_NAME(ctx), rb_obj_class(axis_val));
711
704
  }
712
705
 
713
706
  int8_t
714
707
  rb_ca_parse_reduce_axes_kw (VALUE axis_val, CArray *ca, int8_t *out_axes)
715
708
  {
716
- return rb_ca_parse_reduce_axes_kw_ctx(axis_val, ca, out_axes,
717
- "rb_ca_parse_reduce_axes_kw");
709
+ return rb_ca_parse_reduce_axes_kw_ctx(axis_val, ca, out_axes, NULL);
718
710
  }
719
711
 
720
712
  /* ------------------------------------------------------------------- */
@@ -832,7 +824,7 @@ ca_set_cyclic_check(void *ap)
832
824
  /* ca.data_type == CA_OBJECT */
833
825
  if ( ca_is_object_type(ca) ) {
834
826
  if ( ca->flags & CA_FLAG_CYCLE_CHECK ) {
835
- rb_raise(rb_eRuntimeError, "cyclic reference is not allowed in CArray");
827
+ rb_raise(rb_eArgError, "cyclic reference is not allowed in CArray");
836
828
  }
837
829
  ca_set_flag(ca, CA_FLAG_CYCLE_CHECK);
838
830
  }
@@ -860,12 +852,36 @@ ca_test_cyclic_check(void *ap, void *ptr)
860
852
  CArray *cv;
861
853
  TypedData_Get_Struct(rval, CArray, &carray_data_type, cv);
862
854
  if ( ca_test_flag(cv, CA_FLAG_CYCLE_CHECK) ) {
863
- rb_raise(rb_eRuntimeError, "cyclic reference is not allowed in CArray");
855
+ rb_raise(rb_eArgError, "cyclic reference is not allowed in CArray");
864
856
  }
865
857
  }
866
858
  }
867
859
  }
868
860
 
861
+ /* Runs body(arg) -- one transfer of an object cell or cells -- with ca's
862
+ cycle check set, and clears the check however body leaves. Either
863
+ direction can call into Ruby: a GET through a CAObject slot or the
864
+ check of what it delivered, a PUT that converts the caller's value. A
865
+ raise that skipped the clear would leave every later access to ca
866
+ reporting a cyclic reference. A PUT into an entity only copies VALUEs
867
+ and cannot raise, so it runs unprotected. */
868
+ static void
869
+ ca_with_cyclic_check (CArray *ca, int dir, VALUE (*body)(VALUE), VALUE arg)
870
+ {
871
+ int state = 0;
872
+ ca_set_cyclic_check(ca);
873
+ if ( dir == CA_XFER_PUT && ca_is_entity(ca) ) {
874
+ body(arg);
875
+ }
876
+ else {
877
+ rb_protect(body, arg, &state);
878
+ }
879
+ ca_clear_cyclic_check(ca);
880
+ if ( state ) {
881
+ rb_jump_tag(state);
882
+ }
883
+ }
884
+
869
885
  /* ------------------------------------------------------------------- */
870
886
  /* CArray offers no per-cell ptr accessor (ca_ptr_at_addr /
871
887
  ca_ptr_at_index): such a slot is structurally unsafe -- CABitarray /
@@ -911,14 +927,17 @@ struct ca_xfer_index_args {
911
927
  CArray *ca;
912
928
  ca_size_t *idx;
913
929
  char *ptr;
930
+ int dir;
914
931
  };
915
932
 
916
933
  static VALUE
917
- ca_xfer_index_get_body (VALUE arg)
934
+ ca_xfer_index_body (VALUE arg)
918
935
  {
919
936
  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);
937
+ ca_xfer_index_dispatch(d->ca, d->idx, d->ptr, d->dir);
938
+ if ( d->dir == CA_XFER_GET ) {
939
+ ca_test_cyclic_check(d->ca, d->ptr);
940
+ }
922
941
  return Qnil;
923
942
  }
924
943
 
@@ -928,7 +947,7 @@ ca_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
928
947
  CArray *ca = (CArray *) ap;
929
948
 
930
949
  if ( dir == CA_XFER_PUT && ca_is_readonly(ca) ) {
931
- rb_raise(rb_eRuntimeError, "can not store data to read-only array");
950
+ ca_refuse_read_only();
932
951
  }
933
952
 
934
953
  /* Fast path: non-CA_OBJECT element type needs no GC protection. */
@@ -937,24 +956,14 @@ ca_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
937
956
  return;
938
957
  }
939
958
 
940
- /* CA_OBJECT slow path: cyclic check (+ rb_protect on GET). */
941
- if ( dir == CA_XFER_GET ) {
959
+ /* CA_OBJECT slow path: cyclic check. */
960
+ {
942
961
  struct ca_xfer_index_args args;
943
- int state = 0;
944
962
  args.ca = ca;
945
963
  args.idx = idx;
946
964
  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);
965
+ args.dir = dir;
966
+ ca_with_cyclic_check(ca, dir, ca_xfer_index_body, (VALUE) &args);
958
967
  }
959
968
  }
960
969
 
@@ -1043,16 +1052,19 @@ struct ca_xfer_addrs_args {
1043
1052
  ca_size_t n;
1044
1053
  ca_size_t *addrs;
1045
1054
  char *data;
1055
+ int dir;
1046
1056
  };
1047
1057
 
1048
1058
  static VALUE
1049
- ca_xfer_addrs_get_body (VALUE arg)
1059
+ ca_xfer_addrs_body (VALUE arg)
1050
1060
  {
1051
1061
  struct ca_xfer_addrs_args *d = (struct ca_xfer_addrs_args *) arg;
1052
1062
  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);
1063
+ ca_xfer_addrs_dispatch(d->ca, d->n, d->addrs, d->data, d->dir);
1064
+ if ( d->dir == CA_XFER_GET ) {
1065
+ for ( i = 0; i < d->n; i++ ) {
1066
+ ca_test_cyclic_check(d->ca, d->data + i * d->ca->bytes);
1067
+ }
1056
1068
  }
1057
1069
  return Qnil;
1058
1070
  }
@@ -1063,7 +1075,7 @@ ca_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *data, int dir)
1063
1075
  CArray *ca = (CArray *) ap;
1064
1076
 
1065
1077
  if ( dir == CA_XFER_PUT && ca_is_readonly(ca) ) {
1066
- rb_raise(rb_eRuntimeError, "can not store data to read-only array");
1078
+ ca_refuse_read_only();
1067
1079
  }
1068
1080
 
1069
1081
  /* Fast path: non-CA_OBJECT element type needs no GC protection. */
@@ -1072,25 +1084,15 @@ ca_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *data, int dir)
1072
1084
  return;
1073
1085
  }
1074
1086
 
1075
- /* CA_OBJECT slow path: cyclic check (+ rb_protect on GET). */
1076
- if ( dir == CA_XFER_GET ) {
1087
+ /* CA_OBJECT slow path: cyclic check. */
1088
+ {
1077
1089
  struct ca_xfer_addrs_args args;
1078
- int state = 0;
1079
1090
  args.ca = ca;
1080
1091
  args.n = n;
1081
1092
  args.addrs = addrs;
1082
1093
  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);
1094
+ args.dir = dir;
1095
+ ca_with_cyclic_check(ca, dir, ca_xfer_addrs_body, (VALUE) &args);
1094
1096
  }
1095
1097
  }
1096
1098
 
@@ -1335,6 +1337,8 @@ ca_xfer_strided_walk (char *src_base,
1335
1337
  ca_size_t doff = 0;
1336
1338
  int8_t k;
1337
1339
 
1340
+ if ( ca_region_is_empty(ndim, counts) ) return;
1341
+
1338
1342
  /* slab merge -- scan innermost contig run (src_strides[k] equals the
1339
1343
  accumulated slab byte size). This collapses per-cell memcpy(_,_,
1340
1344
  bytes) loops into per-slab memcpy when the source region is row-major
@@ -1373,6 +1377,7 @@ ca_xfer_strided_walk (char *src_base,
1373
1377
  ca_size_t inner_dst_bytes = counts[ndim-2] * counts[ndim-1] * bytes;
1374
1378
 
1375
1379
  for ( k = 0; k < outer_n; k++ ) idx[k] = 0;
1380
+ /* empty: the walk returns on an empty region at its entry */
1376
1381
  while ( 1 ) {
1377
1382
  ca_size_t soff = 0;
1378
1383
  for ( k = 0; k < outer_n; k++ ) soff += idx[k] * src_strides[k];
@@ -1390,6 +1395,7 @@ ca_xfer_strided_walk (char *src_base,
1390
1395
 
1391
1396
  /* prefix axes [0..slab_start-1] on odometer, slab-sized memcpy per iter. */
1392
1397
  for ( k = 0; k < slab_start; k++ ) idx[k] = 0;
1398
+ /* empty: the walk returns on an empty region at its entry */
1393
1399
  while ( 1 ) {
1394
1400
  ca_size_t soff = 0;
1395
1401
  for ( k = 0; k < slab_start; k++ ) soff += idx[k] * src_strides[k];
@@ -1463,6 +1469,7 @@ ca_xfer_stride_dispatch (CArray *ca, ca_size_t *starts, ca_size_t *counts,
1463
1469
 
1464
1470
  /* fallback: per-cell via byte offset -> flat addr -> index -> xfer_index. */
1465
1471
  for ( k = 0; k < ndim; k++ ) idx[k] = 0;
1472
+ /* empty: its one caller, ca_xfer_stride, returns on an empty region */
1466
1473
  while ( 1 ) {
1467
1474
  ca_size_t soff = base, vidx[CA_RANK_MAX];
1468
1475
  for ( k = 0; k < ndim; k++ ) soff += idx[k] * strides[k];
@@ -1481,16 +1488,20 @@ struct ca_xfer_stride_args {
1481
1488
  ca_size_t *counts;
1482
1489
  ca_size_t *strides;
1483
1490
  char *data;
1491
+ int dir;
1484
1492
  };
1485
1493
 
1486
1494
  static VALUE
1487
- ca_xfer_stride_get_body (VALUE arg)
1495
+ ca_xfer_stride_body (VALUE arg)
1488
1496
  {
1489
1497
  struct ca_xfer_stride_args *a = (struct ca_xfer_stride_args *) arg;
1490
1498
  ca_size_t n = 1, i;
1491
1499
  int8_t k;
1492
1500
  ca_xfer_stride_dispatch(a->ca, a->starts, a->counts, a->strides, a->data,
1493
- CA_XFER_GET);
1501
+ a->dir);
1502
+ if ( a->dir != CA_XFER_GET ) {
1503
+ return Qnil;
1504
+ }
1494
1505
  /* cyclic check over the delivered cells (CA_OBJECT only). dst is contiguous
1495
1506
  row-major over counts (semantics b), so cell i is at data + i*bytes. */
1496
1507
  for ( k = 0; k < a->ca->ndim; k++ ) n *= a->counts[k];
@@ -1507,28 +1518,21 @@ ca_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
1507
1518
  CArray *ca = (CArray *) ap;
1508
1519
 
1509
1520
  if ( dir == CA_XFER_PUT && ca_is_readonly(ca) ) {
1510
- rb_raise(rb_eRuntimeError, "can not store data to read-only array");
1521
+ ca_refuse_read_only();
1511
1522
  }
1512
1523
 
1524
+ if ( ca_region_is_empty(ca->ndim, counts) ) return;
1525
+
1513
1526
  if ( ca->data_type != CA_OBJECT ) {
1514
1527
  ca_xfer_stride_dispatch(ca, starts, counts, strides, data, dir);
1515
1528
  return;
1516
1529
  }
1517
1530
 
1518
- if ( dir == CA_XFER_GET ) {
1531
+ {
1519
1532
  struct ca_xfer_stride_args args;
1520
- int state = 0;
1521
1533
  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);
1534
+ args.strides = strides; args.data = (char *) data; args.dir = dir;
1535
+ ca_with_cyclic_check(ca, dir, ca_xfer_stride_body, (VALUE) &args);
1532
1536
  }
1533
1537
  }
1534
1538
 
@@ -1580,6 +1584,10 @@ ca_xfer_all (void *ap, void *data, int dir)
1580
1584
  "[BUG] xfer_all not defined for object type <%i>",
1581
1585
  ca->obj_type);
1582
1586
  }
1587
+ /* An empty view moves nothing; see ca_xfer_stride. */
1588
+ if ( ca->elements == 0 ) {
1589
+ return;
1590
+ }
1583
1591
  /* Universal arena lifetime hook. ca_xfer_all is the single universal
1584
1592
  entry for materialise (to_ca -> ca_copy -> ca_copy_data ->
1585
1593
  ca_xfer_all), so wrapping here covers every outermost view type,
@@ -1603,9 +1611,7 @@ ca_xfer_all (void *ap, void *data, int dir)
1603
1611
  written by nested transfers land inside it. */
1604
1612
  if ( ca->data_type == CA_OBJECT && dir == CA_XFER_GET ) {
1605
1613
  if ( data != (void *) ca->ptr ) {
1606
- VALUE *p = (VALUE *) data;
1607
- ca_size_t i;
1608
- for ( i = 0; i < ca->elements; i++ ) *p++ = Qnil;
1614
+ ca_object_cells_clear(data, ca->elements);
1609
1615
  }
1610
1616
  guard = ca_gc_hold_push(data, ca->elements);
1611
1617
  }
@@ -1615,8 +1621,83 @@ ca_xfer_all (void *ap, void *data, int dir)
1615
1621
 
1616
1622
  /* ------------------------------------------------------------------- */
1617
1623
 
1624
+ /* What follows the slot in ca_allocate: the object cells cleared, then the
1625
+ mask. Clearing the mask can create it, which for a CAObject calls Ruby.
1626
+ mask0 is the mask the array had before; see ca_attach_view_first. */
1627
+ static void
1628
+ ca_allocate_rest (CArray *ca, CArray *mask0)
1629
+ {
1630
+ if ( ca->data_type == CA_OBJECT ) { /* protection against GC */
1631
+ volatile VALUE rzero = INT2NUM(0);
1632
+ VALUE *p = (VALUE*)ca->ptr;
1633
+ ca_size_t i;
1634
+ for (i=0; i<ca->elements; i++) {
1635
+ *p++ = rzero;
1636
+ }
1637
+ }
1638
+
1639
+ ca_clear_mask(ca); /* ca_update_mask called in ca_clear_mask */
1640
+ if ( ca->mask == mask0 ) {
1641
+ ca_allocate(ca->mask);
1642
+ }
1643
+ }
1644
+
1645
+ /* The view half of ca_allocate, after the attach level is taken. Run
1646
+ under rb_protect by ca_allocate. */
1647
+ static VALUE
1648
+ ca_allocate_view (VALUE arg)
1649
+ {
1650
+ CArray *ca = (CArray *) arg;
1651
+ CArray *mask0 = ca->mask;
1652
+ if ( ! ca->ptr ) {
1653
+ ca_func[ca->obj_type].allocate(ca);
1654
+ }
1655
+ ca_allocate_rest(ca, mask0);
1656
+ return Qnil;
1657
+ }
1658
+
1618
1659
  /* make ca->ptr to point the allocated memory block */
1619
1660
 
1661
+ /* Steps a view's attach level by d. A development build also counts the
1662
+ views whose level is above zero, and the spec compares that count
1663
+ before and after every test: a window left open shows as a view that
1664
+ stays counted. */
1665
+ #ifdef CARRAY_DEV_BUILD
1666
+ static long ca_attached_views = 0;
1667
+
1668
+ #define CA_VIEW_ATTACH_STEP(ca, d) do { \
1669
+ uint32_t attach_before_ = CAVIEW(ca)->attach; \
1670
+ CAVIEW(ca)->attach += (d); \
1671
+ __atomic_add_fetch(&ca_attached_views, \
1672
+ ( CAVIEW(ca)->attach > 0 ) - ( attach_before_ > 0 ), \
1673
+ __ATOMIC_RELAXED); \
1674
+ } while (0)
1675
+
1676
+ static VALUE
1677
+ rb_ca_s_attached_views (VALUE klass)
1678
+ {
1679
+ return LONG2NUM(__atomic_load_n(&ca_attached_views, __ATOMIC_RELAXED));
1680
+ }
1681
+
1682
+ /* Set once at load from CARRAY_GENERAL_PATH; see carray_internal.h. */
1683
+ int ca_fast_paths_on = 1;
1684
+ int ca_iter_transfer_only_on = 0;
1685
+
1686
+ static VALUE
1687
+ rb_ca_s_fast_paths_enabled (VALUE klass)
1688
+ {
1689
+ return ca_fast_paths_on ? Qtrue : Qfalse;
1690
+ }
1691
+
1692
+ static VALUE
1693
+ rb_ca_s_iter_by_transfer_only (VALUE klass)
1694
+ {
1695
+ return ca_iter_transfer_only_on ? Qtrue : Qfalse;
1696
+ }
1697
+ #else
1698
+ #define CA_VIEW_ATTACH_STEP(ca, d) ( CAVIEW(ca)->attach += (d) )
1699
+ #endif
1700
+
1620
1701
  void
1621
1702
  ca_allocate (void *ap)
1622
1703
  {
@@ -1628,31 +1709,36 @@ ca_allocate (void *ap)
1628
1709
 
1629
1710
  if ( ca_is_view(ca) ) { /* view array */
1630
1711
 
1631
- CAVIEW(ca)->attach += 1; /* increments attach level */
1632
- if ( CAVIEW(ca)->attach > CA_ATTACH_MAX ) {
1712
+ if ( CAVIEW(ca)->attach >= CA_ATTACH_MAX ) {
1633
1713
  rb_raise(rb_eRuntimeError,
1634
1714
  "too large attach count of view array");
1635
1715
  }
1636
-
1637
- if ( ! ca->ptr ) {
1638
- ca_func[ca->obj_type].allocate(ap);
1716
+ CA_VIEW_ATTACH_STEP(ca, 1); /* increments attach level */
1717
+
1718
+ /* Undo the level if the slot or the mask raises, as ca_attach does.
1719
+ Left in place, the level is invisible while ptr is NULL, and the
1720
+ next attach / detach pair then leaves the view attached for good.
1721
+ A slot that got as far as publishing ca->ptr has taken what its
1722
+ detach slot releases; one that raised earlier has published
1723
+ nothing. */
1724
+ {
1725
+ char *ptr0 = ca->ptr;
1726
+ int tag = 0;
1727
+ rb_protect(ca_allocate_view, (VALUE) ca, &tag);
1728
+ if ( tag ) {
1729
+ if ( ! ptr0 && ca->ptr ) {
1730
+ ca_func[ca->obj_type].detach(ca);
1731
+ ca->ptr = NULL;
1732
+ }
1733
+ CA_VIEW_ATTACH_STEP(ca, -1);
1734
+ rb_jump_tag(tag);
1735
+ }
1639
1736
  }
1640
1737
  }
1641
1738
  else { /* entity array */
1642
1739
  ca_func[ca->obj_type].allocate(ap);
1740
+ ca_allocate_rest(ca, ca->mask); /* an entity's new mask is not yet allocated */
1643
1741
  }
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
1742
  }
1657
1743
 
1658
1744
  /* The first attach of a view: materialise through the type's slot, then
@@ -1660,10 +1746,33 @@ ca_allocate (void *ap)
1660
1746
  static VALUE
1661
1747
  ca_attach_view_first (VALUE arg)
1662
1748
  {
1663
- CArray *ca = (CArray *) arg;
1749
+ CArray *ca = (CArray *) arg;
1750
+ CArray *mask0 = ca->mask;
1751
+ /* The mask moves in step with its view: attach, allocate and detach
1752
+ each take it one level with the view. A mask created here -- by the
1753
+ slot (a CAObject makes its mask while it attaches) or by
1754
+ ca_update_mask -- is already at the view's level, since
1755
+ ca_create_mask attaches a new mask to match an attached view. So
1756
+ only a mask that was there before the slot takes the step; stepping a
1757
+ new one too leaves it a level above the view, still attached after
1758
+ the view's last detach. */
1664
1759
  ca_func[ca->obj_type].attach(ca);
1665
1760
  ca_update_mask(ca);
1666
- ca_attach(ca->mask);
1761
+ if ( ca->mask == mask0 ) {
1762
+ ca_attach(ca->mask);
1763
+ }
1764
+ return Qnil;
1765
+ }
1766
+
1767
+ static VALUE
1768
+ ca_attach_mask_step (VALUE arg)
1769
+ {
1770
+ CArray *ca = (CArray *) arg;
1771
+ CArray *mask0 = ca->mask;
1772
+ ca_update_mask(ca);
1773
+ if ( ca->mask == mask0 ) { /* see ca_attach_view_first */
1774
+ ca_attach(ca->mask);
1775
+ }
1667
1776
  return Qnil;
1668
1777
  }
1669
1778
 
@@ -1684,7 +1793,7 @@ ca_attach (void *ap)
1684
1793
  rb_raise(rb_eRuntimeError,
1685
1794
  "too large attach count of view array");
1686
1795
  }
1687
- CAVIEW(ca)->attach += 1; /* increments attach level */
1796
+ CA_VIEW_ATTACH_STEP(ca, 1); /* increments attach level */
1688
1797
 
1689
1798
  if ( ! ca->ptr ) {
1690
1799
  /* A slot can raise part way (a conversion meeting a value it cannot
@@ -1700,7 +1809,7 @@ ca_attach (void *ap)
1700
1809
  ca_func[ca->obj_type].detach(ca);
1701
1810
  ca->ptr = NULL;
1702
1811
  }
1703
- CAVIEW(ca)->attach -= 1;
1812
+ CA_VIEW_ATTACH_STEP(ca, -1);
1704
1813
  rb_jump_tag(tag);
1705
1814
  }
1706
1815
  return;
@@ -1710,8 +1819,21 @@ ca_attach (void *ap)
1710
1819
  ca_func[ca->obj_type].attach(ap);
1711
1820
  }
1712
1821
 
1713
- ca_update_mask(ca);
1714
- ca_attach(ca->mask);
1822
+ {
1823
+ /* Making the mask can raise (a CAObject's mask hooks); the attach
1824
+ taken above is given back then. */
1825
+ int tag = 0;
1826
+ rb_protect(ca_attach_mask_step, (VALUE) ca, &tag);
1827
+ if ( tag ) {
1828
+ if ( ca_is_view(ca) ) {
1829
+ CA_VIEW_ATTACH_STEP(ca, -1);
1830
+ }
1831
+ else {
1832
+ ca_func[ca->obj_type].detach(ap);
1833
+ }
1834
+ rb_jump_tag(tag);
1835
+ }
1836
+ }
1715
1837
  }
1716
1838
 
1717
1839
  /* Attach every array in list[0..n-1], or, if one of those attaches
@@ -1751,7 +1873,226 @@ ca_attach_all (CArray **list, int32_t n)
1751
1873
  }
1752
1874
  }
1753
1875
 
1754
- /* attach parent's data to ca->ptr */
1876
+ static VALUE
1877
+ ca_sync_all_step (VALUE arg)
1878
+ {
1879
+ ca_sync((void *) arg);
1880
+ return Qnil;
1881
+ }
1882
+
1883
+ /* Syncs every one of list[0..n-1] in turn, carrying on past one that
1884
+ raises so that the others still receive what was written, then raises
1885
+ the first exception. A throw or break cannot wait, and goes at once. */
1886
+ void
1887
+ ca_sync_all (CArray **list, int32_t n)
1888
+ {
1889
+ volatile VALUE error = Qnil;
1890
+ int32_t k;
1891
+ for ( k = 0; k < n; k++ ) {
1892
+ int tag = 0;
1893
+ rb_protect(ca_sync_all_step, (VALUE) list[k], &tag);
1894
+ if ( tag ) {
1895
+ VALUE err = rb_errinfo();
1896
+ if ( ! RTEST(rb_obj_is_kind_of(err, rb_eException)) ) {
1897
+ rb_jump_tag(tag);
1898
+ }
1899
+ if ( NIL_P(error) ) {
1900
+ error = err;
1901
+ }
1902
+ rb_set_errinfo(Qnil);
1903
+ }
1904
+ }
1905
+ if ( ! NIL_P(error) ) {
1906
+ rb_exc_raise(error);
1907
+ }
1908
+ }
1909
+
1910
+ /* An attach window over list[0..n-1] that closes however body leaves.
1911
+
1912
+ modes holds one letter per array:
1913
+ 'r' attach; detach on close
1914
+ 'w' attach; sync, then detach on close
1915
+ 'a' allocate (write only); sync, then detach on close
1916
+
1917
+ Opening takes all of the arrays or none. Closing syncs the 'w' arrays
1918
+ even when body raised or broke out -- the cells body wrote reach the
1919
+ parent, as they would for an entity, which has no separate buffer. An
1920
+ 'a' array is not synced after a raise: allocate did not gather, so its
1921
+ cells body never wrote hold nothing faithful to push back. Allocate
1922
+ clears the mask as it opens, and an entity (or the parent under a view)
1923
+ has no separate mask to fall back on, so the mask of an 'a' array is
1924
+ copied before it opens and written back when body raises.
1925
+
1926
+ Every array is detached, in reverse order, whatever happens. body's
1927
+ own exception wins over one raised by a sync; when body returned and a
1928
+ sync raised, that exception propagates once everything is detached. */
1929
+
1930
+ typedef struct {
1931
+ int32_t n;
1932
+ CArray **list;
1933
+ const char *modes;
1934
+ int32_t opened;
1935
+ int body_done; /* body returned without raising */
1936
+ int sync_tag;
1937
+ volatile VALUE sync_err;
1938
+ VALUE (*body)(VALUE);
1939
+ VALUE arg;
1940
+ volatile VALUE masks; /* per array: the mask bytes of an 'a' array, or nil */
1941
+ } ca_window_ctx_t;
1942
+
1943
+ /* Copy the masks of the 'a' arrays before they open. */
1944
+ static void
1945
+ ca_attach_window_save_masks (ca_window_ctx_t *w)
1946
+ {
1947
+ int32_t k;
1948
+ w->masks = Qnil;
1949
+ for ( k = 0; k < w->n; k++ ) {
1950
+ CArray *ca = w->list[k];
1951
+ if ( w->modes[k] != 'a' || ! ca || ! ca_has_mask(ca) ) {
1952
+ continue;
1953
+ }
1954
+ if ( NIL_P(w->masks) ) {
1955
+ w->masks = rb_ary_new_capa(w->n);
1956
+ }
1957
+ ca_update_mask(ca);
1958
+ {
1959
+ VALUE buf = rb_str_new(NULL, (long) ca->elements);
1960
+ ca_copy_data(ca->mask, RSTRING_PTR(buf));
1961
+ rb_ary_store(w->masks, k, buf);
1962
+ }
1963
+ }
1964
+ }
1965
+
1966
+ /* Write the copied masks back, once every array is detached. */
1967
+ static void
1968
+ ca_attach_window_restore_masks (ca_window_ctx_t *w)
1969
+ {
1970
+ int32_t k;
1971
+ if ( NIL_P(w->masks) ) {
1972
+ return;
1973
+ }
1974
+ for ( k = 0; k < w->n; k++ ) {
1975
+ VALUE buf = rb_ary_entry(w->masks, k);
1976
+ CArray *ca = w->list[k];
1977
+ if ( NIL_P(buf) ) {
1978
+ continue;
1979
+ }
1980
+ ca_update_mask(ca);
1981
+ if ( ! ca->mask ) {
1982
+ ca_create_mask(ca);
1983
+ }
1984
+ ca_xfer_all(ca->mask, RSTRING_PTR(buf), CA_XFER_PUT);
1985
+ /* held only by w->masks, where compaction can move it; the write may
1986
+ call into Ruby (a CAObject's mask hooks) */
1987
+ RB_GC_GUARD(buf);
1988
+ }
1989
+ }
1990
+
1991
+ static VALUE
1992
+ ca_attach_window_open (VALUE varg)
1993
+ {
1994
+ ca_window_ctx_t *w = (ca_window_ctx_t *) varg;
1995
+ for ( ; w->opened < w->n; w->opened++ ) {
1996
+ if ( w->modes[w->opened] == 'a' ) {
1997
+ ca_allocate(w->list[w->opened]);
1998
+ }
1999
+ else {
2000
+ ca_attach(w->list[w->opened]);
2001
+ }
2002
+ }
2003
+ return Qnil;
2004
+ }
2005
+
2006
+ static VALUE
2007
+ ca_attach_window_body (VALUE varg)
2008
+ {
2009
+ ca_window_ctx_t *w = (ca_window_ctx_t *) varg;
2010
+ VALUE result = w->body(w->arg);
2011
+ w->body_done = 1;
2012
+ return result;
2013
+ }
2014
+
2015
+ static VALUE
2016
+ ca_attach_window_sync (VALUE varg)
2017
+ {
2018
+ ca_sync((void *) varg);
2019
+ return Qnil;
2020
+ }
2021
+
2022
+ /* The ensure half. rb_ensure keeps the exception body raised (or the
2023
+ break it took) across this call, so a sync raising in here is caught
2024
+ and set aside rather than let replace it. */
2025
+ static VALUE
2026
+ ca_attach_window_close (VALUE varg)
2027
+ {
2028
+ ca_window_ctx_t *w = (ca_window_ctx_t *) varg;
2029
+ int32_t k;
2030
+ for ( k = w->opened - 1; k >= 0; k-- ) {
2031
+ CArray *ca = w->list[k];
2032
+ char mode = w->modes[k];
2033
+ if ( ! ca ) {
2034
+ continue;
2035
+ }
2036
+ if ( ca->ptr && ( mode == 'w' || ( mode == 'a' && w->body_done ) ) ) {
2037
+ int tag = 0;
2038
+ rb_protect(ca_attach_window_sync, (VALUE) ca, &tag);
2039
+ if ( tag && ! w->sync_tag ) {
2040
+ w->sync_tag = tag;
2041
+ w->sync_err = rb_errinfo();
2042
+ }
2043
+ rb_set_errinfo(Qnil);
2044
+ }
2045
+ if ( ca->ptr ) {
2046
+ ca_detach(ca);
2047
+ }
2048
+ }
2049
+ if ( ! w->body_done ) {
2050
+ ca_attach_window_restore_masks(w);
2051
+ }
2052
+ return Qnil;
2053
+ }
2054
+
2055
+ VALUE
2056
+ ca_attach_window (int32_t n, CArray **list, const char *modes,
2057
+ VALUE (*body)(VALUE), VALUE arg)
2058
+ {
2059
+ ca_window_ctx_t w;
2060
+ VALUE result;
2061
+ int tag = 0;
2062
+
2063
+ w.n = n;
2064
+ w.list = list;
2065
+ w.modes = modes;
2066
+ w.opened = 0;
2067
+ w.body_done = 0;
2068
+ w.sync_tag = 0;
2069
+ w.sync_err = Qnil;
2070
+ w.body = body;
2071
+ w.arg = arg;
2072
+ ca_attach_window_save_masks(&w);
2073
+
2074
+ rb_protect(ca_attach_window_open, (VALUE) &w, &tag);
2075
+ if ( tag ) {
2076
+ while ( w.opened > 0 ) {
2077
+ w.opened--;
2078
+ if ( w.list[w.opened] ) {
2079
+ ca_detach(w.list[w.opened]);
2080
+ }
2081
+ }
2082
+ ca_attach_window_restore_masks(&w);
2083
+ rb_jump_tag(tag);
2084
+ }
2085
+
2086
+ result = rb_ensure(ca_attach_window_body, (VALUE) &w,
2087
+ ca_attach_window_close, (VALUE) &w);
2088
+ if ( w.sync_tag && RB_TYPE_P(w.sync_err, T_OBJECT) &&
2089
+ RTEST(rb_obj_is_kind_of(w.sync_err, rb_eException)) ) {
2090
+ rb_exc_raise(w.sync_err);
2091
+ }
2092
+ return result;
2093
+ }
2094
+
2095
+ /* re-gather an attached view's buffer from its parent */
1755
2096
 
1756
2097
  void
1757
2098
  ca_update (void *ap)
@@ -1769,7 +2110,7 @@ ca_update (void *ap)
1769
2110
  }
1770
2111
  else {
1771
2112
  rb_raise(rb_eRuntimeError,
1772
- "[BUG] ca_update() called for not-attached virtal array");
2113
+ "[BUG] ca_update() called for not-attached virtual array");
1773
2114
  }
1774
2115
 
1775
2116
  }
@@ -1795,12 +2136,16 @@ ca_sync (void *ap)
1795
2136
  }
1796
2137
 
1797
2138
  if ( ca_is_readonly(ca) ) {
1798
- rb_raise(rb_eRuntimeError,
1799
- "can not modify read-only array");
2139
+ ca_refuse_read_only();
1800
2140
  }
1801
2141
 
1802
- ca_update_mask(ca);
1803
- ca_sync(ca->mask);
2142
+ /* Only a mask that exists can hold writes: one made inside the window
2143
+ is attached with the view (ca_create_mask). Making one here would
2144
+ read the parent's mask while the window closes, and a parent that
2145
+ cannot answer would leave the window open. */
2146
+ if ( ca->mask ) {
2147
+ ca_sync(ca->mask);
2148
+ }
1804
2149
 
1805
2150
  if ( ca_is_view(ca) ) { /* view array */
1806
2151
  if ( ! CAVIEW(ca)->nosync ) { /* FIXME : */
@@ -1813,6 +2158,42 @@ ca_sync (void *ap)
1813
2158
 
1814
2159
  }
1815
2160
 
2161
+ /* Sync ap and detach it -- the close of a window a frame opened with
2162
+ ca_attach or ca_allocate. The detach happens even when the sync raises
2163
+ (a read-only array, a backing whose write fails); the sync's exception
2164
+ then propagates. Written as two calls, a raising sync skips the detach
2165
+ and the window stays open.
2166
+
2167
+ Not a replacement for every ca_sync: a view's sync slot syncs its parent
2168
+ without detaching it, because that attach belongs to the view and its
2169
+ detach slot releases it. */
2170
+
2171
+ static VALUE
2172
+ ca_sync_body (VALUE arg)
2173
+ {
2174
+ ca_sync((void *) arg);
2175
+ return Qnil;
2176
+ }
2177
+
2178
+ void
2179
+ ca_sync_detach (void *ap)
2180
+ {
2181
+ CArray *ca = (CArray *) ap;
2182
+ int tag = 0;
2183
+
2184
+ if ( ! ca ) {
2185
+ return;
2186
+ }
2187
+
2188
+ rb_protect(ca_sync_body, (VALUE) ca, &tag);
2189
+ if ( ca->ptr ) {
2190
+ ca_detach(ca);
2191
+ }
2192
+ if ( tag ) {
2193
+ rb_jump_tag(tag);
2194
+ }
2195
+ }
2196
+
1816
2197
  /* make ca->ptr to be detached */
1817
2198
 
1818
2199
  void
@@ -1833,38 +2214,86 @@ ca_detach (void *ap)
1833
2214
  if ( CAVIEW(ca)->attach == 1 ) {
1834
2215
  ca_func[ca->obj_type].detach(ap);
1835
2216
  }
1836
- CAVIEW(ca)->attach -= 1;
2217
+ CA_VIEW_ATTACH_STEP(ca, -1);
1837
2218
  }
1838
2219
  else { /* entity array */
1839
2220
  ca_func[ca->obj_type].detach(ap);
1840
2221
  }
1841
2222
 
1842
- ca_update_mask(ca);
1843
- ca_detach(ca->mask);
2223
+ /* The mask, if there is one, is at the view's level (see
2224
+ ca_attach_view_first). A mask that does not exist yet is not made
2225
+ here: it would be made unattached, and making it reads the parent's
2226
+ mask, which may raise with the view half detached. */
2227
+ if ( ca->mask ) {
2228
+ ca_detach(ca->mask);
2229
+ }
1844
2230
  }
1845
2231
 
1846
2232
  /* multiple versions of ca_allocate, ca_attach, ca_sync, ca_detach */
1847
2233
 
2234
+ /* ca_allocate_n and ca_attach_n take all of their arrays or none: when
2235
+ one raises, the ones already taken are detached before the raise
2236
+ propagates. */
2237
+
2238
+ typedef struct {
2239
+ CArray **list;
2240
+ int32_t n;
2241
+ int32_t done;
2242
+ } ca_allocate_all_ctx_t;
2243
+
2244
+ static VALUE
2245
+ ca_allocate_all_body (VALUE arg)
2246
+ {
2247
+ ca_allocate_all_ctx_t *c = (ca_allocate_all_ctx_t *) arg;
2248
+ for ( ; c->done < c->n; c->done++ ) {
2249
+ ca_allocate(c->list[c->done]);
2250
+ }
2251
+ return Qnil;
2252
+ }
2253
+
2254
+ static void
2255
+ ca_allocate_all (CArray **list, int32_t n)
2256
+ {
2257
+ ca_allocate_all_ctx_t c;
2258
+ int tag = 0;
2259
+ c.list = list;
2260
+ c.n = n;
2261
+ c.done = 0;
2262
+ rb_protect(ca_allocate_all_body, (VALUE) &c, &tag);
2263
+ if ( tag ) {
2264
+ while ( c.done > 0 ) {
2265
+ ca_detach(list[--c.done]);
2266
+ }
2267
+ rb_jump_tag(tag);
2268
+ }
2269
+ }
2270
+
1848
2271
  void
1849
2272
  ca_allocate_n (int n, ...)
1850
2273
  {
2274
+ CArray **list = ALLOCA_N(CArray *, n > 0 ? n : 1);
1851
2275
  va_list args;
2276
+ int i;
1852
2277
  va_start(args, n);
1853
- while ( n-- ) {
1854
- ca_allocate(va_arg(args, CArray *));
2278
+ for ( i = 0; i < n; i++ ) {
2279
+ list[i] = va_arg(args, CArray *);
1855
2280
  }
1856
2281
  va_end(args);
2282
+ ca_allocate_all(list, n);
1857
2283
  }
1858
2284
 
1859
2285
  void
1860
2286
  ca_attach_n (int n, ...)
1861
2287
  {
2288
+ CArray **list = ALLOCA_N(CArray *, n > 0 ? n : 1);
1862
2289
  va_list args;
2290
+ int i;
1863
2291
  va_start(args, n);
1864
- while ( n-- ) {
1865
- ca_attach(va_arg(args, CArray *));
2292
+ for ( i = 0; i < n; i++ ) {
2293
+ list[i] = va_arg(args, CArray *);
1866
2294
  }
1867
2295
  va_end(args);
2296
+ ca_attach_all(list, n);
1868
2297
  }
1869
2298
 
1870
2299
  void
@@ -1917,8 +2346,7 @@ ca_sync_data (void *ap, char *ptr)
1917
2346
  CArray *ca = (CArray *) ap;
1918
2347
 
1919
2348
  if ( ca_is_readonly(ca) ) {
1920
- rb_raise(rb_eRuntimeError,
1921
- "can not sync data to read-only array");
2349
+ ca_refuse_read_only();
1922
2350
  }
1923
2351
 
1924
2352
  if ( ca_is_view(ca) && CAVIEW(ca)->nosync ) {
@@ -1939,8 +2367,7 @@ ca_fill_data (void *ap, void *aptr)
1939
2367
  char *ptr = (char *) aptr;
1940
2368
 
1941
2369
  if ( ca_is_readonly(ca) ) {
1942
- rb_raise(rb_eRuntimeError,
1943
- "can not fill data to read-only array");
2370
+ ca_refuse_read_only();
1944
2371
  }
1945
2372
 
1946
2373
  if ( ca_is_view(ca) ) { /* view array */
@@ -1972,7 +2399,10 @@ ca_fill_stride_default (void *ap, ca_size_t base, int8_t ndim,
1972
2399
  ca_size_t idx[CA_RANK_MAX];
1973
2400
  int8_t k;
1974
2401
 
2402
+ if ( ca_region_is_empty(ndim, counts) ) return;
2403
+
1975
2404
  for ( k = 0; k < ndim; k++ ) idx[k] = 0;
2405
+ /* empty: the function returns on an empty region at its entry */
1976
2406
  while ( 1 ) {
1977
2407
  ca_size_t addr = base, vidx[CA_RANK_MAX];
1978
2408
  for ( k = 0; k < ndim; k++ ) addr += idx[k] * steps[k];
@@ -1989,15 +2419,12 @@ ca_fill_stride (void *ap, ca_size_t base, int8_t ndim,
1989
2419
  ca_size_t *counts, ca_size_t *steps, void *ptr)
1990
2420
  {
1991
2421
  CArray *ca = (CArray *) ap;
1992
- int8_t k;
1993
2422
 
1994
2423
  if ( ca_is_readonly(ca) ) {
1995
- rb_raise(rb_eRuntimeError, "can not fill data to read-only array");
2424
+ ca_refuse_read_only();
1996
2425
  }
1997
2426
 
1998
- for ( k = 0; k < ndim; k++ ) {
1999
- if ( counts[k] <= 0 ) return;
2000
- }
2427
+ if ( ca_region_is_empty(ndim, counts) ) return;
2001
2428
 
2002
2429
  if ( ca_func[ca->obj_type].fill_stride ) {
2003
2430
  ca_func[ca->obj_type].fill_stride(ap, base, ndim, counts, steps, ptr);
@@ -2090,6 +2517,7 @@ ca_fill_stride_via_addrs (void *ap, ca_size_t base, int8_t ndim,
2090
2517
  idx[k] = 0;
2091
2518
  }
2092
2519
 
2520
+ /* empty: an axis of count 0 returns above */
2093
2521
  while ( 1 ) {
2094
2522
  ca_size_t addr = base;
2095
2523
  for ( k = 0; k < ndim; k++ ) addr += idx[k] * steps[k];
@@ -2113,7 +2541,7 @@ ca_fill_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *ptr)
2113
2541
  CArray *ca = (CArray *) ap;
2114
2542
 
2115
2543
  if ( ca_is_readonly(ca) ) {
2116
- rb_raise(rb_eRuntimeError, "can not fill data to read-only array");
2544
+ ca_refuse_read_only();
2117
2545
  }
2118
2546
 
2119
2547
  if ( ca_func[ca->obj_type].fill_addrs ) {
@@ -2144,7 +2572,7 @@ ca_fill (void *ap, void *aptr)
2144
2572
  char *ptr = (char *) aptr;
2145
2573
 
2146
2574
  if ( ca_is_readonly(ca) ) {
2147
- rb_raise(rb_eRuntimeError, "can't fill read-only carray");
2575
+ ca_refuse_read_only();
2148
2576
  }
2149
2577
 
2150
2578
  ca_fill_data(ap, ptr);
@@ -2425,7 +2853,7 @@ rb_ca_members (VALUE self)
2425
2853
  {
2426
2854
  volatile VALUE data_class = rb_ca_data_class(self);
2427
2855
  if ( NIL_P(data_class) ) {
2428
- rb_raise(rb_eRuntimeError, "carray doesn't have data class");
2856
+ rb_raise(rb_eTypeError, "carray doesn't have data class");
2429
2857
  }
2430
2858
  else {
2431
2859
  return rb_obj_clone(rb_const_get(data_class, rb_intern("MEMBERS")));
@@ -2438,8 +2866,8 @@ rb_ca_members (VALUE self)
2438
2866
  (= Face strip): the @member cache lives on self (the Face) while the
2439
2867
  actual field view is the CAField on the parent. A plain FIXLEN entity
2440
2868
  keeps self == receiver. */
2441
- VALUE
2442
- rb_ca_face_field (VALUE self, VALUE sym)
2869
+ static VALUE
2870
+ ca_face_field_lookup (VALUE self, VALUE sym)
2443
2871
  {
2444
2872
  volatile VALUE data_class = rb_ca_data_class(self);
2445
2873
  volatile VALUE member;
@@ -2448,7 +2876,7 @@ rb_ca_face_field (VALUE self, VALUE sym)
2448
2876
  CArray *ca;
2449
2877
 
2450
2878
  if ( NIL_P(data_class) ) {
2451
- rb_raise(rb_eRuntimeError, "carray doesn't have data class");
2879
+ rb_raise(rb_eTypeError, "carray doesn't have data class");
2452
2880
  }
2453
2881
 
2454
2882
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
@@ -2464,7 +2892,9 @@ rb_ca_face_field (VALUE self, VALUE sym)
2464
2892
  is local to the derived view; field views are still on its parent
2465
2893
  (which itself is on the entity). */
2466
2894
  member = rb_hash_new();
2467
- rb_ivar_set(self, rb_intern("member"), member);
2895
+ if ( ! OBJ_FROZEN(self) ) {
2896
+ rb_ivar_set(self, rb_intern("member"), member);
2897
+ }
2468
2898
  }
2469
2899
 
2470
2900
  if ( rb_obj_is_kind_of(sym, rb_cInteger) ) {
@@ -2490,7 +2920,7 @@ rb_ca_face_field (VALUE self, VALUE sym)
2490
2920
  if ( TYPE(sym) != T_STRING ) {
2491
2921
  sym = rb_funcall(sym, rb_intern("to_s"), 0);
2492
2922
  }
2493
- rb_raise(rb_eRuntimeError,
2923
+ rb_raise(rb_eArgError,
2494
2924
  "can't find data_member named <%s>", StringValuePtr(sym));
2495
2925
  }
2496
2926
  Check_Type(info, T_ARRAY);
@@ -2532,12 +2962,53 @@ rb_ca_face_field (VALUE self, VALUE sym)
2532
2962
  return obj;
2533
2963
  }
2534
2964
  }
2965
+ /* `endian:` is not an option of #field; the member's byte order is a
2966
+ view over the field, as CAStruct#[] reads it. */
2967
+ {
2968
+ volatile VALUE opts = rb_ary_entry(info, 2);
2969
+ volatile VALUE endian = Qnil;
2970
+ if ( RB_TYPE_P(opts, T_HASH) ) {
2971
+ endian = rb_hash_aref(opts, ID2SYM(rb_intern("endian")));
2972
+ }
2973
+ if ( ! NIL_P(endian) ) {
2974
+ volatile VALUE field_opts = rb_hash_dup(opts);
2975
+ rb_hash_delete(field_opts, ID2SYM(rb_intern("endian")));
2976
+ if ( RHASH_SIZE(field_opts) > 0 ) {
2977
+ obj = rb_funcall(receiver, rb_intern("field"), 3,
2978
+ rb_ary_entry(info, 0), rb_ary_entry(info, 1),
2979
+ field_opts);
2980
+ }
2981
+ else {
2982
+ obj = rb_funcall(receiver, rb_intern("field"), 2,
2983
+ rb_ary_entry(info, 0), rb_ary_entry(info, 1));
2984
+ }
2985
+ obj = rb_funcall(obj, rb_intern("endian"), 1, endian);
2986
+ rb_hash_aset(member, sym, obj);
2987
+ return obj;
2988
+ }
2989
+ }
2535
2990
  obj = rb_apply(receiver, rb_intern("field"), info);
2536
2991
  rb_hash_aset(member, sym, obj);
2537
2992
  return obj;
2538
2993
  }
2539
2994
  }
2540
2995
 
2996
+ /* A field is a view on the record array's storage, below the Face, so the
2997
+ read-only state of the record array (frozen, or a read-only view) is not
2998
+ on its parent chain; the field carries it itself. */
2999
+ VALUE
3000
+ rb_ca_face_field (VALUE self, VALUE sym)
3001
+ {
3002
+ volatile VALUE obj = ca_face_field_lookup(self, sym);
3003
+ CArray *ca, *co;
3004
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
3005
+ if ( ca_is_readonly(ca) && rb_obj_is_carray(obj) ) {
3006
+ TypedData_Get_Struct(obj, CArray, &carray_data_type, co);
3007
+ ca_set_flag(co, CA_FLAG_READ_ONLY);
3008
+ }
3009
+ return obj;
3010
+ }
3011
+
2541
3012
  /* @overload fields
2542
3013
 
2543
3014
  (Reference) Returns an array of data class members (fields)
@@ -2550,7 +3021,7 @@ rb_ca_fields (VALUE self)
2550
3021
  volatile VALUE member_names, list;
2551
3022
  int i;
2552
3023
  if ( NIL_P(data_class) ) {
2553
- rb_raise(rb_eRuntimeError, "carray doesn't have data class");
3024
+ rb_raise(rb_eTypeError, "carray doesn't have data class");
2554
3025
  }
2555
3026
  member_names = rb_const_get(data_class, rb_intern("MEMBERS"));
2556
3027
  list = rb_ary_new2(RARRAY_LEN(member_names));
@@ -2573,7 +3044,7 @@ rb_ca_fields_at (int argc, VALUE *argv, VALUE self)
2573
3044
  volatile VALUE member_names, list;
2574
3045
  int i;
2575
3046
  if ( NIL_P(data_class) ) {
2576
- rb_raise(rb_eRuntimeError, "carray doesn't have data class");
3047
+ rb_raise(rb_eTypeError, "carray doesn't have data class");
2577
3048
  }
2578
3049
  member_names = rb_ary_new4(argc, argv);
2579
3050
  list = rb_ary_new2(RARRAY_LEN(member_names));
@@ -2603,6 +3074,19 @@ Init_carray_core (void)
2603
3074
  rb_define_method(rb_cCArray, "__attach__", rb_ca__attach__, 0);
2604
3075
  rb_define_method(rb_cCArray, "__sync__", rb_ca__sync__, 0);
2605
3076
  rb_define_method(rb_cCArray, "__detach__", rb_ca__detach__, 0);
3077
+
3078
+ rb_define_singleton_method(rb_cCArray, "__attached_views__",
3079
+ rb_ca_s_attached_views, 0);
3080
+
3081
+ {
3082
+ const char *general = getenv("CARRAY_GENERAL_PATH");
3083
+ ca_fast_paths_on = !( general && general[0] && strcmp(general, "0") );
3084
+ ca_iter_transfer_only_on = ( general && ! strcmp(general, "2") );
3085
+ }
3086
+ rb_define_singleton_method(rb_cCArray, "__fast_paths_enabled__",
3087
+ rb_ca_s_fast_paths_enabled, 0);
3088
+ rb_define_singleton_method(rb_cCArray, "__iter_by_transfer_only__",
3089
+ rb_ca_s_iter_by_transfer_only, 0);
2606
3090
  #endif
2607
3091
 
2608
3092
  rb_define_method(rb_cCArray, "members", rb_ca_members, 0);