carray 3.0.1 → 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 (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +824 -0
  3. data/README.md +5 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +258 -4
  6. data/ext/ca_axis_group.c +368 -184
  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 +90 -41
  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 +960 -245
  14. data/ext/ca_kernel_iterator.h +352 -181
  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 +194 -42
  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 +5 -4
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +180 -50
  25. data/ext/ca_obj_face.c +428 -62
  26. data/ext/ca_obj_face.h +107 -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 +39 -26
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +235 -55
  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 +551 -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 +88 -75
  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 -123
  46. data/ext/ca_obj_stride.c +267 -35
  47. data/ext/ca_obj_string.c +25 -22
  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 +157 -29
  54. data/ext/ca_op_ipower.c +25 -13
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_rng_normal.h +42 -0
  58. data/ext/ca_rng_xoshiro256pp.h +105 -0
  59. data/ext/ca_sort_kernels.h +70 -0
  60. data/ext/ca_sweep_engine.c +385 -198
  61. data/ext/ca_sweep_engine.h +31 -23
  62. data/ext/ca_triop_dispatch.c +4 -1
  63. data/ext/ca_triop_dispatch.h +5 -2
  64. data/ext/caframe_csv_reader.c +463 -0
  65. data/ext/carray.h +90 -28
  66. data/ext/carray_access.c +487 -231
  67. data/ext/carray_address_basis.c +731 -0
  68. data/ext/carray_attribute.c +45 -3
  69. data/ext/carray_bincount.c +32 -2
  70. data/ext/carray_broadcast.c +49 -17
  71. data/ext/carray_call_cfunc.c +667 -511
  72. data/ext/carray_cast.c +1556 -247
  73. data/ext/carray_cast_func.rb +34 -21
  74. data/ext/carray_conversion.c +197 -73
  75. data/ext/carray_copy.c +84 -38
  76. data/ext/carray_core.c +591 -119
  77. data/ext/carray_count.c +148 -96
  78. data/ext/carray_element.c +18 -27
  79. data/ext/carray_factorize.c +604 -291
  80. data/ext/carray_generate.c +76 -40
  81. data/ext/carray_histogram.c +152 -94
  82. data/ext/carray_hold.c +2 -1
  83. data/ext/carray_index_classifier.c +63 -96
  84. data/ext/carray_internal.h +400 -1
  85. data/ext/carray_lazy.c +834 -79
  86. data/ext/carray_loop.c +88 -66
  87. data/ext/carray_mask.c +705 -324
  88. data/ext/carray_math_kernel.h +1 -0
  89. data/ext/carray_median_percentile.c +263 -63
  90. data/ext/carray_memory_view.c +254 -248
  91. data/ext/carray_operator.c +1093 -363
  92. data/ext/carray_order.c +133 -69
  93. data/ext/carray_partition.c +102 -56
  94. data/ext/carray_random.c +582 -204
  95. data/ext/carray_scatter.c +308 -65
  96. data/ext/carray_segment.c +257 -0
  97. data/ext/carray_slab.c +183 -70
  98. data/ext/carray_sort.c +235 -240
  99. data/ext/carray_sort_kernel.c +8 -1
  100. data/ext/carray_struct.c +99 -21
  101. data/ext/carray_test.c +167 -64
  102. data/ext/carray_time_text.c +1182 -0
  103. data/ext/carray_utils.c +363 -98
  104. data/ext/extconf.rb +27 -2
  105. data/ext/mk_call_cfunc.rb +103 -118
  106. data/ext/mk_pow5_table.rb +35 -0
  107. data/ext/mkkernel.rb +1349 -851
  108. data/ext/ruby_carray.c +17 -2
  109. data/ext/version.h +4 -4
  110. data/ext/version.rb +4 -2
  111. data/lib/carray/attribute.rb +39 -0
  112. data/lib/carray/autoload_carray.rb +24 -3
  113. data/lib/carray/autoload_method_extension.rb +18 -0
  114. data/lib/carray/axis_group.rb +245 -20
  115. data/lib/carray/basics.rb +95 -25
  116. data/lib/carray/bincount_nd.rb +6 -0
  117. data/lib/carray/block_iterator.rb +200 -54
  118. data/lib/carray/boolean_reduce.rb +21 -29
  119. data/lib/carray/categorical.rb +244 -59
  120. data/lib/carray/categorical_iterator.rb +391 -757
  121. data/lib/carray/complex.rb +36 -29
  122. data/lib/carray/conditional.rb +94 -7
  123. data/lib/carray/const_string.rb +177 -27
  124. data/lib/carray/construct.rb +40 -0
  125. data/lib/carray/data_type_extension.rb +3 -0
  126. data/lib/carray/data_type_limits.rb +91 -0
  127. data/lib/carray/fixlen_string.rb +1 -1
  128. data/lib/carray/frame/concat.rb +157 -6
  129. data/lib/carray/frame/convert.rb +13 -7
  130. data/lib/carray/frame/csv_parser.rb +590 -76
  131. data/lib/carray/frame/describe.rb +79 -0
  132. data/lib/carray/frame/frame.rb +205 -35
  133. data/lib/carray/frame/group.rb +199 -40
  134. data/lib/carray/frame/io.rb +452 -60
  135. data/lib/carray/frame/join.rb +134 -46
  136. data/lib/carray/frame/records.rb +54 -19
  137. data/lib/carray/frame/reshape.rb +448 -0
  138. data/lib/carray/frame/sort.rb +26 -2
  139. data/lib/carray/frame/verbs.rb +726 -56
  140. data/lib/carray/frame.rb +2 -0
  141. data/lib/carray/fuse_source.rb +111 -2
  142. data/lib/carray/fusion.rb +229 -38
  143. data/lib/carray/inspect.rb +71 -20
  144. data/lib/carray/iterator.rb +157 -2
  145. data/lib/carray/lazy.rb +54 -67
  146. data/lib/carray/mask_gap_fill.rb +66 -50
  147. data/lib/carray/math.rb +32 -19
  148. data/lib/carray/meld_reduce.rb +40 -7
  149. data/lib/carray/methods/align_addr.rb +8 -4
  150. data/lib/carray/methods/bincount.rb +2 -2
  151. data/lib/carray/methods/bit_string.rb +14 -3
  152. data/lib/carray/methods/composition.rb +16 -10
  153. data/lib/carray/methods/discovery_along.rb +74 -0
  154. data/lib/carray/methods/factorize.rb +50 -0
  155. data/lib/carray/methods/gather_nd.rb +13 -2
  156. data/lib/carray/methods/insert_block.rb +9 -14
  157. data/lib/carray/methods/is_in.rb +130 -6
  158. data/lib/carray/methods/join.rb +1 -1
  159. data/lib/carray/methods/locate_addr.rb +235 -21
  160. data/lib/carray/methods/mask_duplicates.rb +35 -1
  161. data/lib/carray/methods/mode.rb +18 -1
  162. data/lib/carray/methods/nunique.rb +22 -1
  163. data/lib/carray/methods/repeat.rb +91 -0
  164. data/lib/carray/methods/resize.rb +21 -23
  165. data/lib/carray/methods/snap.rb +34 -12
  166. data/lib/carray/methods/unique.rb +52 -3
  167. data/lib/carray/record_order.rb +208 -0
  168. data/lib/carray/rng.rb +86 -0
  169. data/lib/carray/runtime.rb +14 -11
  170. data/lib/carray/segment_iterator.rb +990 -0
  171. data/lib/carray/serialize.rb +197 -27
  172. data/lib/carray/slab_iterator.rb +109 -40
  173. data/lib/carray/stack.rb +10 -4
  174. data/lib/carray/string_operation_extension.rb +9 -4
  175. data/lib/carray/struct.rb +142 -51
  176. data/lib/carray/struct_builder.rb +110 -14
  177. data/lib/carray/table.rb +2 -2
  178. data/lib/carray/time.rb +535 -105
  179. data/lib/carray/window_iterator.rb +259 -82
  180. data/lib/carray.rb +4 -2
  181. data/yard-stubs/ca_obj_array.rb +4 -4
  182. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  183. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  184. data/yard-stubs/ca_obj_block.rb +2 -7
  185. data/yard-stubs/ca_obj_fake.rb +1 -1
  186. data/yard-stubs/ca_obj_field.rb +1 -1
  187. data/yard-stubs/ca_obj_refer.rb +3 -3
  188. data/yard-stubs/ca_obj_roll.rb +0 -1
  189. data/yard-stubs/ca_obj_stride.rb +10 -7
  190. data/yard-stubs/ca_obj_transpose.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +11 -4
  192. data/yard-stubs/carray_access.rb +6 -5
  193. data/yard-stubs/carray_broadcast.rb +2 -2
  194. data/yard-stubs/carray_cast.rb +78 -43
  195. data/yard-stubs/carray_conversion.rb +13 -1
  196. data/yard-stubs/carray_copy.rb +8 -3
  197. data/yard-stubs/carray_core.rb +3 -83
  198. data/yard-stubs/carray_count.rb +16 -2
  199. data/yard-stubs/carray_lazy.rb +210 -1
  200. data/yard-stubs/carray_loop.rb +4 -1
  201. data/yard-stubs/carray_mask.rb +34 -10
  202. data/yard-stubs/carray_math.rb +1499 -4
  203. data/yard-stubs/carray_median_percentile.rb +26 -8
  204. data/yard-stubs/carray_order.rb +43 -82
  205. data/yard-stubs/carray_scatter.rb +10 -3
  206. data/yard-stubs/carray_segment.rb +72 -0
  207. data/yard-stubs/carray_slab.rb +11 -8
  208. data/yard-stubs/carray_sort.rb +50 -8
  209. data/yard-stubs/carray_test.rb +10 -0
  210. data/yard-stubs/carray_utils.rb +0 -26
  211. metadata +19 -19
  212. data/ext/ca_for_each_element.h +0 -239
  213. data/ext/carray_build_flags.h +0 -3
  214. data/ext/carray_config.h +0 -28
  215. data/ext/carray_kernels_bincmp.c +0 -4446
  216. data/ext/carray_kernels_binop.c +0 -11001
  217. data/ext/carray_kernels_init.c +0 -1131
  218. data/ext/carray_kernels_map.c +0 -3467
  219. data/ext/carray_kernels_moncmp.c +0 -2097
  220. data/ext/carray_kernels_monop.c +0 -18313
  221. data/ext/carray_kernels_reduce_aggregate.c +0 -25837
  222. data/ext/carray_kernels_reduce_boolean.c +0 -330
  223. data/ext/carray_kernels_reduce_cumulative.c +0 -14593
  224. data/ext/carray_kernels_reduce_extreme.c +0 -16948
  225. data/ext/carray_kernels_reduce_variance.c +0 -3910
  226. data/ext/carray_kernels_scan.c +0 -3693
  227. data/ext/carray_kernels_search.c +0 -32138
  228. data/ext/carray_kernels_sort.c +0 -10626
  229. data/ext/carray_kernels_triop.c +0 -1392
data/ext/ca_obj_face.h CHANGED
@@ -4,16 +4,12 @@
4
4
 
5
5
  Face mechanism — shared op helper signatures + lift/strip entry points.
6
6
 
7
- Phase 1 (= PROPOSAL_CAFACE_PHASE_1.md): skeleton.
8
- Phase 2 (= PROPOSAL_CAFACE_PHASE_2.md F.2.1): replace shared op helpers
9
- with thin-forward to parent.
10
-
11
7
  This header provides signatures for (1) shared op helpers (= thin-forward
12
8
  for storage ops, alias-based since Face is data-layout identical to
13
9
  parent), (2) `rb_ca_face_template` (struct duplicate + parent swap),
14
10
  (3) `ca_face_lift` (= wrap hook at the tail of user-facing access
15
- methods), (4) `ca_strip_face` (= Face-stripping helper at kernel_iterator
16
- entry).
11
+ methods), (4) `ca_strip_face` (= Face-stripping helper for code that
12
+ must see the storage).
17
13
 
18
14
  A Face is a "mask of a semantic type" (= identity layered on a storage
19
15
  type such as int64, with no value conversion), sibling to CAFake which
@@ -57,20 +53,27 @@ void ca_face_xfer_all (void *ap, void *data, int dir);
57
53
 
58
54
  /* -- lift / strip helpers -- */
59
55
 
60
- /* Struct duplicate + parent + shape swap (= MEMO §4.3; consumed by Face
61
- subclass lift / clone / dup machinery in Phase 2). */
56
+ /* Struct duplicate + parent + shape swap. The primitive under
57
+ ca_face_lift; also called directly where a result is re-wrapped under
58
+ a Face without going through a view (partition, generated reduce). */
62
59
  VALUE rb_ca_face_template (VALUE original_face, CArray *new_parent,
63
60
  ca_size_t *new_dim);
64
61
 
65
- /* At the tail of user-facing access methods, `if parent is a Face, wrap
66
- the result as a Face`. Deployed in Phase 1 for `ca[...]`
67
- (= rb_ca_aref / rb_ca_store); planned for block / refer / reshape /
68
- transpose / sort etc. in Phase 2. */
62
+ /* At the tail of view-creating methods, `if the receiver is a Face, wrap
63
+ the result as a Face`. Reached through CA_FACE_LIFT_IF_FACE, which
64
+ each view-creating method deploys at its return path. */
69
65
  VALUE ca_face_lift (VALUE view, VALUE face_parent);
70
66
 
71
- /* Follow the Face flag down to parent, e.g. at kernel_iterator entry.
72
- No call sites in Phase 1; wired into SRC_* path init_l2 in Phase 2
73
- (F.2.6). */
67
+ /* Lift a fresh array `result` (one a reduction, a scan or a partition built
68
+ from the Face's storage) into the Face `face`: the same Face class over
69
+ `result`, carrying the subclass tail, `result`'s mask and the state
70
+ copy_state carries. `result` stays reachable through the new Face. */
71
+ VALUE ca_face_relift_entity (VALUE face, VALUE result);
72
+
73
+ /* Follow the Face flag down to the first non-Face parent, for code that
74
+ must talk to the storage (store, mask, MemoryView export). The kernel
75
+ iterator strips Faces at entry with its own loop, which also strips
76
+ lazy markers. */
74
77
  CArray *ca_strip_face (CArray *src);
75
78
 
76
79
  /* VALUE-level counterpart: walks the Face parent ivar chain to the root
@@ -101,15 +104,11 @@ void Init_ca_face (void);
101
104
  extern int8_t CA_OBJ_RECORD;
102
105
  VALUE rb_ca_record_get_data_class (CArray *ca);
103
106
 
104
- /* -- Convenience macro for view-creating method deployment (F.2.13) --
107
+ /* -- Convenience macro for view-creating method deployment --
105
108
  Insert at the tail of each view-creating Ruby method (= rb_ca_transpose
106
109
  / rb_ca_reshape / etc.) just before return. `ca` is self's CArray *,
107
110
  `obj` is the view result, `self` is the calling VALUE. Achieves Face
108
- transparency in a single C-level line, subclass-agnostic.
109
-
110
- Macroises the pattern hand-written at the tail of `rb_ca_fetch_method`
111
- in Phase 1; applied uniformly to view-creating methods from Phase 2
112
- F.2.13 onward. */
111
+ transparency in a single C-level line, subclass-agnostic. */
113
112
  #define CA_FACE_LIFT_IF_FACE(obj, self, ca) do { \
114
113
  if ( ca_is_face(ca) && rb_obj_is_kind_of((obj), rb_cCArray) ) { \
115
114
  (obj) = ca_face_lift((obj), (self)); \
@@ -127,14 +126,13 @@ VALUE rb_ca_record_get_data_class (CArray *ca);
127
126
 
128
127
  The flag test comes first and covers both bits in one mask, so an
129
128
  ordinary array leaves through a single AND. That matters: `[]` is the
130
- hottest method this is deployed on (see devel/bench_index_percall.rb).
129
+ hottest method this is deployed on.
131
130
 
132
131
  NOT interchangeable with CA_FACE_LIFT_IF_FACE. Face is lifted at ~24
133
132
  sites, including ones a marker must not follow it through -- `copy` owns
134
133
  its data, `sort` reorders values, `value` and `strip_mask` change what
135
134
  the mask means. Deploy this only where the result is a view whose shape
136
- was fixed at construction and which only moves positions
137
- (PROPOSAL_LAZY_MARKER_LIFT section 4). */
135
+ was fixed at construction and which only moves positions. */
138
136
  #define CA_WRAPPER_LIFT(obj, self, ca) do { \
139
137
  if ( ca_test_flag((ca), CA_FLAG_IS_FACE | CA_FLAG_IS_LAZY_MARKER) \
140
138
  && rb_obj_is_kind_of((obj), rb_cCArray) ) { \
@@ -147,10 +145,10 @@ VALUE ca_wrapper_lift (VALUE view, VALUE wrapper, void *wrapper_ca);
147
145
  /* -- Scalar fetch decode hook (storage -> surface) --
148
146
  On the scalar-return path (= tail of rb_ca_fetch_index /
149
147
  rb_ca_fetch_addr), if a Face-derived subclass defines a
150
- `storage_to_scalar(raw_value)` Ruby callback, invoke it. This is a
148
+ `storage_to_element(raw_value)` Ruby callback, invoke it. This is a
151
149
  decode (copy the storage value out and construct a new value object),
152
150
  not a wrap (a zero-copy lens over live memory). Its write-direction
153
- counterpart is `scalar_to_storage` (below).
151
+ counterpart is `element_to_storage` (below).
154
152
 
155
153
  Use cases:
156
154
  - `dt[i]` for CATime / CATimedelta → Scalar decode
@@ -163,39 +161,39 @@ VALUE ca_wrapper_lift (VALUE view, VALUE wrapper, void *wrapper_ca);
163
161
  - !is_a?(CArray): CArray results go through the lift path
164
162
  (= CA_FACE_LIFT_IF_FACE)
165
163
  - !UNDEF: don't decode the masked-element sentinel
166
- - respond_to?(:storage_to_scalar): activates once the subclass adopts
164
+ - respond_to?(:storage_to_element): activates once the subclass adopts
167
165
  the convention */
168
- /* Face-local C-level dispatch table for storage_to_scalar, separate
166
+ /* Face-local C-level dispatch table for storage_to_element, separate
169
167
  from the global ca_operation_function_t (no API change to existing
170
168
  subsystems). A Face subclass that wants the per-cell scalar fetch hot
171
169
  path to skip rb_funcall registers its C function via
172
- ca_face_register_storage_to_scalar(obj_type, fn) at Init time.
170
+ ca_face_register_storage_to_element(obj_type, fn) at Init time.
173
171
  Unregistered obj_types (= external gem Faces that only define the Ruby
174
172
  method) fall through to the rb_funcall branch. The Ruby
175
- `storage_to_scalar` method is still defined as a regular method so
173
+ `storage_to_element` method is still defined as a regular method so
176
174
  user-facing Ruby code may call it directly. */
177
- typedef VALUE (*ca_face_storage_to_scalar_fn)(VALUE self, VALUE raw);
175
+ typedef VALUE (*ca_face_storage_to_element_fn)(VALUE self, VALUE raw);
178
176
 
179
- extern ca_face_storage_to_scalar_fn ca_face_storage_to_scalar_table[];
177
+ extern ca_face_storage_to_element_fn ca_face_storage_to_element_table[];
180
178
 
181
- void ca_face_register_storage_to_scalar (int obj_type,
182
- ca_face_storage_to_scalar_fn fn);
179
+ void ca_face_register_storage_to_element (int obj_type,
180
+ ca_face_storage_to_element_fn fn);
183
181
 
184
182
  /* -- Scalar store encode hook (surface -> storage), mirror of the decode --
185
- The write-direction counterpart of storage_to_scalar. Given a surface
183
+ The write-direction counterpart of storage_to_element. Given a surface
186
184
  value object (a Scalar / Time / DateTime), a Face-owned function returns
187
185
  a storage-domain value (for datetime/timedelta: an Integer in self's
188
186
  unit) that the storage cast in rb_ca_obj2ptr can consume directly. A
189
187
  Face that needs no conversion (surface == storage) simply does not
190
188
  register. Faces may either register a C fast-path function here or
191
- define the Ruby method `scalar_to_storage`; the C table is consulted
189
+ define the Ruby method `element_to_storage`; the C table is consulted
192
190
  first, then the Ruby fallback. */
193
- typedef VALUE (*ca_face_scalar_to_storage_fn)(VALUE self, VALUE surface);
191
+ typedef VALUE (*ca_face_element_to_storage_fn)(VALUE self, VALUE surface);
194
192
 
195
- extern ca_face_scalar_to_storage_fn ca_face_scalar_to_storage_table[];
193
+ extern ca_face_element_to_storage_fn ca_face_element_to_storage_table[];
196
194
 
197
- void ca_face_register_scalar_to_storage (int obj_type,
198
- ca_face_scalar_to_storage_fn fn);
195
+ void ca_face_register_element_to_storage (int obj_type,
196
+ ca_face_element_to_storage_fn fn);
199
197
 
200
198
  /* Single source of truth for the surface -> storage-domain conversion.
201
199
  Returns `obj` unchanged when `ca` is not a Face or when no hook is
@@ -203,7 +201,7 @@ void ca_face_register_scalar_to_storage (int obj_type,
203
201
  existing storage cast). Otherwise returns the storage-domain value the
204
202
  Face produced. Called from rb_ca_obj2ptr (single-cell / fill / masked /
205
203
  range stores) and from the Array-of-scalars bulk store branch. */
206
- VALUE ca_face_scalar_to_storage (VALUE self, CArray *ca, VALUE obj);
204
+ VALUE ca_face_element_to_storage (VALUE self, CArray *ca, VALUE obj);
207
205
 
208
206
  /* -- F.S0: Face state homogeneity check (= prerequisite for multi-Face APIs
209
207
  such as CAStack lift) --
@@ -259,18 +257,80 @@ void ca_face_register_state_portable (int obj_type, int portable);
259
257
  (= falls through to default). */
260
258
  int ca_face_state_portable (int obj_type, VALUE klass);
261
259
 
262
- #define CA_FACE_STORAGE_TO_SCALAR_IF_FACE(obj, self, ca) do { \
260
+ /* Check a list of arrays about to be combined into one (CAStack, CAMeld,
261
+ promote_list). Returns the Face class every element shares, or Qnil
262
+ when none of them is a Face; the combined array is then lifted into
263
+ that class. Raises ArgumentError for a mix of Face and non-Face arrays
264
+ or of two Face classes, for two or more of a Face whose state is not
265
+ portable, and for elements whose state differs from the first one's.
266
+ `who` names the caller at the head of the message. */
267
+ VALUE ca_face_list_class (VALUE list, const char *who);
268
+
269
+ /* A Face array's storage-side parent, one level down; any other array
270
+ as it is. */
271
+ VALUE ca_face_strip_one (VALUE v);
272
+
273
+ /* Bring a Face *operand* into the reference's storage space, or refuse.
274
+
275
+ The gate asks `fz_face_descend`'s question -- "is your storage your
276
+ surface?" -- of the receiver, and for a long time asked nothing at all of
277
+ the operand: a COMPARABLE receiver stripped any Face handed to it, and got
278
+ back that Face's encoding rather than its values. Where the two encodings
279
+ happened to be the same width the comparison then answered, wrongly and
280
+ silently. Call this instead of stripping an operand by hand.
281
+
282
+ A Face passes only if it declares COMPARABLE_STORAGE, i.e. its storage
283
+ cells *are* the values it shows. Anything else -- a byte range, a code, a
284
+ tick -- is refused rather than compared as bytes. Non-Face operands are
285
+ returned untouched. `name` opens the message. */
286
+ /* Refuse to order (verb "order") or count by value (verb "count") a Face
287
+ whose storage does not hold its values in order, naming the conversion. */
288
+ NORETURN(void ca_face_refuse_order (const char *name, VALUE face,
289
+ const char *verb));
290
+
291
+ /* The conversion a refusal of a Face whose cells encode its values points
292
+ at (#to_string for a string Face, to_type(:object) for any other). */
293
+ const char *ca_face_conversion_hint (VALUE face);
294
+
295
+ /* Raise eclass naming a Face that does not define the numeric operation
296
+ `name`; returns when self is not a Face. See ca_obj_face.c. */
297
+ void ca_face_refuse_numeric (VALUE self, const char *name, VALUE eclass);
298
+
299
+ VALUE ca_face_operand_descend (VALUE operand, const char *name);
300
+
301
+ /* Bring an operand into the storage space of a Face reference (a record's
302
+ own data_class, a COMPARABLE Face's plain values, or the reference's
303
+ to_comparable), refusing what the reference cannot read. See
304
+ ca_obj_face.c. */
305
+ VALUE ca_face_reconcile_operand (VALUE reference, VALUE operand,
306
+ const char *name);
307
+
308
+ /* `==` (strict = 0) / `eql?` (strict = 1) when an operand is a Face other
309
+ than a record array. See ca_obj_face.c. */
310
+ int ca_face_equal (VALUE va, VALUE vb, int strict);
311
+
312
+ /* The storage value of a cell read through a Face. A gated (CA_FIXLEN)
313
+ surface over a numeric storage delivers the cell as its bytes; they are
314
+ read back as the storage data_type (a boolean as true / false). A
315
+ fixlen or object storage, and anything not a String, comes back as it
316
+ came. This is what a Ruby storage_to_element receives, and the element
317
+ of a Face that defines none. A C function registered for the obj_type
318
+ receives the cell as it was read, the bytes under a gated surface. */
319
+ VALUE ca_face_storage_value (CArray *ca, VALUE raw);
320
+
321
+ #define CA_FACE_STORAGE_TO_ELEMENT_IF_FACE(obj, self, ca) do { \
263
322
  if ( ca_is_face(ca) && (obj) != CA_UNDEF && (obj) != Qnil \
264
323
  && ! rb_obj_is_kind_of((obj), rb_cCArray) ) { \
265
- ca_face_storage_to_scalar_fn _decode_fn = ca_face_storage_to_scalar_table[(ca)->obj_type]; \
324
+ ca_face_storage_to_element_fn _decode_fn = ca_face_storage_to_element_table[(ca)->obj_type]; \
266
325
  if ( _decode_fn != NULL ) { \
267
326
  (obj) = _decode_fn((self), (obj)); \
268
327
  } \
269
328
  else { \
270
- static ID _id_storage_to_scalar = 0; \
271
- if ( _id_storage_to_scalar == 0 ) _id_storage_to_scalar = rb_intern("storage_to_scalar"); \
272
- if ( rb_respond_to((self), _id_storage_to_scalar) ) { \
273
- (obj) = rb_funcall((self), _id_storage_to_scalar, 1, (obj)); \
329
+ static ID _id_storage_to_element = 0; \
330
+ (obj) = ca_face_storage_value((ca), (obj)); \
331
+ if ( _id_storage_to_element == 0 ) _id_storage_to_element = rb_intern("storage_to_element"); \
332
+ if ( rb_respond_to((self), _id_storage_to_element) ) { \
333
+ (obj) = rb_funcall((self), _id_storage_to_element, 1, (obj)); \
274
334
  } \
275
335
  } \
276
336
  } \
data/ext/ca_obj_fake.c CHANGED
@@ -154,8 +154,8 @@ static void
154
154
  ca_fake_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
155
155
  {
156
156
  CAFake *ca = (CAFake *) ap;
157
- char sbuf[32];
158
- char *v = (ca->parent->bytes <= 32) ? sbuf : xmalloc(ca->parent->bytes);
157
+ volatile VALUE holder;
158
+ char *v = ALLOCV_N(char, holder, ca->parent->bytes);
159
159
  if ( dir == CA_XFER_GET ) {
160
160
  ca_fetch_index(ca->parent, idx, v);
161
161
  ca_ptr2ptr(ca->parent, v, ca, data);
@@ -164,7 +164,7 @@ ca_fake_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
164
164
  ca_ptr2ptr(ca, data, ca->parent, v);
165
165
  ca_store_index(ca->parent, idx, v);
166
166
  }
167
- if ( v != sbuf ) xfree(v);
167
+ ALLOCV_END(holder);
168
168
  }
169
169
 
170
170
  /* Batched address gather/scatter. CAFake is a 1:1 reinterpret-cast
@@ -237,6 +237,7 @@ static void
237
237
  ca_fake_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
238
238
  ca_size_t *strides, void *data, int dir)
239
239
  {
240
+ if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
240
241
  CAFake *ca = (CAFake *) ap;
241
242
  int8_t ndim = ca->ndim;
242
243
  ca_size_t pstrides[CA_RANK_MAX];
@@ -365,17 +366,11 @@ static void
365
366
  ca_fake_func_fill_data (void *ap, void *ptr)
366
367
  {
367
368
  CAFake *ca = (CAFake *) ap;
368
- if ( ca->parent->bytes <= 32 ) {
369
- char v[32];
370
- ca_ptr2ptr(ca, ptr, ca->parent, v);
371
- ca_fill(ca->parent, v);
372
- }
373
- else {
374
- char *v = xmalloc(ca->parent->bytes);
375
- ca_ptr2ptr(ca, ptr, ca->parent, v);
376
- ca_fill(ca->parent, v);
377
- xfree(v);
378
- }
369
+ volatile VALUE holder;
370
+ char *v = ALLOCV_N(char, holder, ca->parent->bytes);
371
+ ca_ptr2ptr(ca, ptr, ca->parent, v);
372
+ ca_fill(ca->parent, v);
373
+ ALLOCV_END(holder);
379
374
  }
380
375
 
381
376
  /* Same as fill_data, with a region: convert the one value once and pass the
@@ -389,47 +384,22 @@ ca_fake_func_fill_stride (void *ap, ca_size_t base, int8_t ndim,
389
384
  ca_size_t *counts, ca_size_t *steps, void *ptr)
390
385
  {
391
386
  CAFake *ca = (CAFake *) ap;
392
- if ( ca->parent->bytes <= 32 ) {
393
- char v[32];
394
- ca_ptr2ptr(ca, ptr, ca->parent, v);
395
- ca_fill_stride(ca->parent, base, ndim, counts, steps, v);
396
- }
397
- else {
398
- char *v = xmalloc(ca->parent->bytes);
399
- ca_ptr2ptr(ca, ptr, ca->parent, v);
400
- ca_fill_stride(ca->parent, base, ndim, counts, steps, v);
401
- xfree(v);
402
- }
387
+ volatile VALUE holder;
388
+ char *v = ALLOCV_N(char, holder, ca->parent->bytes);
389
+ ca_ptr2ptr(ca, ptr, ca->parent, v);
390
+ ca_fill_stride(ca->parent, base, ndim, counts, steps, v);
391
+ ALLOCV_END(holder);
403
392
  }
404
393
 
405
394
  static void
406
395
  ca_fake_func_fill_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *ptr)
407
396
  {
408
397
  CAFake *ca = (CAFake *) ap;
409
- if ( ca->parent->bytes <= 32 ) {
410
- char v[32];
411
- ca_ptr2ptr(ca, ptr, ca->parent, v);
412
- ca_fill_addrs(ca->parent, n, addrs, v);
413
- }
414
- else {
415
- char *v = xmalloc(ca->parent->bytes);
416
- ca_ptr2ptr(ca, ptr, ca->parent, v);
417
- ca_fill_addrs(ca->parent, n, addrs, v);
418
- xfree(v);
419
- }
420
- }
421
-
422
- static void
423
- ca_fake_func_create_mask (void *ap)
424
- {
425
- CAFake *ca = (CAFake *) ap;
426
- ca_update_mask(ca->parent);
427
- if ( ! ca->parent->mask ) {
428
- ca_create_mask(ca->parent);
429
- }
430
- ca->mask =
431
- (CArray *) ca_refer_new(ca->parent->mask,
432
- CA_BOOLEAN, ca->ndim, ca->dim, 0, 0);
398
+ volatile VALUE holder;
399
+ char *v = ALLOCV_N(char, holder, ca->parent->bytes);
400
+ ca_ptr2ptr(ca, ptr, ca->parent, v);
401
+ ca_fill_addrs(ca->parent, n, addrs, v);
402
+ ALLOCV_END(holder);
433
403
  }
434
404
 
435
405
  ca_operation_function_t ca_fake_func = {
@@ -442,7 +412,7 @@ ca_operation_function_t ca_fake_func = {
442
412
  ca_fake_func_sync,
443
413
  ca_fake_func_detach,
444
414
  ca_fake_func_fill_data,
445
- ca_fake_func_create_mask,
415
+ ca_view_func_create_mask,
446
416
  ca_fake_func_xfer_index,
447
417
  ca_fake_func_xfer_addrs,
448
418
  NULL, /* fold_stride: never-fold (transform boundary) */
@@ -522,6 +492,7 @@ rb_ca_fake_initialize_copy (VALUE self, VALUE other)
522
492
  CAFake *ca, *cs;
523
493
 
524
494
  TypedData_Get_Struct(self, CAFake, &cafake_data_type, ca);
495
+ ca_check_uninitialized(ca);
525
496
  TypedData_Get_Struct(other, CAFake, &cafake_data_type, cs);
526
497
 
527
498
  if ( ca_func[CA_OBJ_FAKE].pool_init ) {
data/ext/ca_obj_farray.c CHANGED
@@ -92,6 +92,7 @@ rb_ca_farray_initialize_copy (VALUE self, VALUE other)
92
92
  {
93
93
  CAStride *ca, *cs;
94
94
  TypedData_Get_Struct(self, CAStride, &castride_data_type, ca);
95
+ ca_check_uninitialized(ca);
95
96
  TypedData_Get_Struct(other, CAStride, &castride_data_type, cs);
96
97
  if ( ca_func[CA_OBJ_FARRAY].pool_init ) {
97
98
  ca_array_pool_alloc(ca, CA_OBJ_FARRAY, cs->ndim);
data/ext/ca_obj_field.c CHANGED
@@ -36,14 +36,18 @@ ca_field_setup (CAStride *ca, CArray *parent,
36
36
  CA_CHECK_BYTES(data_type, bytes);
37
37
 
38
38
  if ( offset < 0 ) {
39
- rb_raise(rb_eRuntimeError, "negative offset");
39
+ rb_raise(rb_eArgError, "negative offset");
40
40
  }
41
41
  if ( data_type == CA_OBJECT ) {
42
42
  rb_raise(rb_eCADataTypeError,
43
43
  "CA_OBJECT can not to be a data_type for CAField");
44
44
  }
45
+ if ( ca_is_object_type(parent) ) {
46
+ rb_raise(rb_eCADataTypeError,
47
+ "a field can not be taken from an object array");
48
+ }
45
49
  if ( parent->bytes < offset + bytes ) {
46
- rb_raise(rb_eRuntimeError, "offset or bytes out of range");
50
+ rb_raise(rb_eArgError, "offset or bytes out of range");
47
51
  }
48
52
 
49
53
  /* strides[k] = parent->bytes × Π_{i>k} parent->dim[i]. Same shape
@@ -57,6 +61,12 @@ ca_field_setup (CAStride *ca, CArray *parent,
57
61
  }
58
62
  }
59
63
 
64
+ /* ca == NULL asks for the checks above only, so that a constructor can
65
+ run them before it allocates. */
66
+ if ( ca == NULL ) {
67
+ return 0;
68
+ }
69
+
60
70
  ca_stride_setup(ca, CA_OBJ_FIELD, parent,
61
71
  data_type, bytes,
62
72
  parent->ndim, parent->dim, strides, offset);
@@ -67,7 +77,9 @@ ca_field_setup (CAStride *ca, CArray *parent,
67
77
  CAStride *
68
78
  ca_field_new (CArray *parent, ca_size_t offset, int8_t data_type, ca_size_t bytes)
69
79
  {
70
- CAStride *ca = (CAStride *) ca_array_alloc(CA_OBJ_FIELD, parent->ndim);
80
+ CAStride *ca;
81
+ ca_field_setup(NULL, parent, offset, data_type, bytes);
82
+ ca = (CAStride *) ca_array_alloc(CA_OBJ_FIELD, parent->ndim);
71
83
  ca_field_setup(ca, parent, offset, data_type, bytes);
72
84
  return ca;
73
85
  }
@@ -160,6 +172,7 @@ rb_ca_field_initialize_copy (VALUE self, VALUE other)
160
172
  CAStride *ca, *cs;
161
173
 
162
174
  TypedData_Get_Struct(self, CAStride, &castride_data_type, ca);
175
+ ca_check_uninitialized(ca);
163
176
  TypedData_Get_Struct(other, CAStride, &castride_data_type, cs);
164
177
 
165
178
  if ( ca_func[CA_OBJ_FIELD].pool_init ) {
@@ -22,6 +22,7 @@
22
22
 
23
23
  #include "carray.h"
24
24
  #include "ca_obj_face.h"
25
+ #include "carray_internal.h"
25
26
 
26
27
  typedef struct {
27
28
  /* === CAView prefix === */
@@ -89,13 +90,32 @@ ca_fixlen_string_setup (CAFixlenString *ca, CArray *parent)
89
90
  "CAFixlenString requires fixlen storage (parent.data_type != CA_FIXLEN)");
90
91
  }
91
92
 
93
+ /* ca == NULL asks for the checks above only, so that a constructor can
94
+ run them before it allocates. */
95
+ if ( ca == NULL ) {
96
+ return 0;
97
+ }
98
+
92
99
  ca->obj_type = CA_OBJ_FIXLEN_STRING;
93
100
  ca->data_type = CA_FIXLEN;
94
- /* ORDERABLE + COMPARABLE, and both hold by construction: this Face's surface
95
- IS its storage, byte for byte (a cell decodes to its own bytes, padding
96
- included), so the descent is the identity map. memcmp order is therefore
97
- String#<=> order for these cells, and byte equality is cell equality --
98
- which is what the equality families need (docs/topics/CAFace.md §6.3).
101
+ /* ORDERABLE + COMPARABLE. The descent is NOT the identity map -- the
102
+ scalar decode below strips trailing NUL -- but both flags still hold,
103
+ for a reason worth stating exactly, since it is what a Face author
104
+ copying this would need:
105
+
106
+ ORDERABLE: every cell is padded to the same K with NUL, and NUL is
107
+ the smallest byte, so memcmp on the padded cells orders them the same
108
+ way String#<=> orders the stripped strings (a prefix sorts before any
109
+ continuation, which is what the padding reproduces). Stripping is
110
+ order-preserving; it is not order-irrelevant by being absent.
111
+
112
+ COMPARABLE: a String query is padded out to the cell width before it
113
+ is compared, so byte equality on the padded form is equality on the
114
+ stripped form -- for a query that fits. A query longer than K is
115
+ truncated to its own first K bytes by that same padding step, and
116
+ then matches a cell it is not equal to.
117
+
118
+ Which is what the equality families need (docs/topics/CAFace.md §6.3).
99
119
  Without the flags the sort family still worked (it exempts CA_FIXLEN
100
120
  storage from the gate and orders by memcmp), but the value-hash family
101
121
  handed its results back as a plain fixlen array, and search refused a
@@ -135,8 +155,9 @@ ca_fixlen_string_setup (CAFixlenString *ca, CArray *parent)
135
155
  CAFixlenString *
136
156
  ca_fixlen_string_new (CArray *parent)
137
157
  {
138
- CAFixlenString *ca =
139
- (CAFixlenString *) ca_array_alloc(CA_OBJ_FIXLEN_STRING, parent->ndim);
158
+ CAFixlenString *ca;
159
+ ca_fixlen_string_setup(NULL, parent);
160
+ ca = (CAFixlenString *) ca_array_alloc(CA_OBJ_FIXLEN_STRING, parent->ndim);
140
161
  ca_fixlen_string_setup(ca, parent);
141
162
  return ca;
142
163
  }
@@ -175,18 +196,6 @@ ca_fixlen_string_func_allocate (void *ap)
175
196
  ca->ptr = ca->parent->ptr;
176
197
  }
177
198
 
178
- static void
179
- ca_fixlen_string_func_create_mask (void *ap)
180
- {
181
- CAFixlenString *ca = (CAFixlenString *) ap;
182
- ca_update_mask(ca->parent);
183
- if ( ! ca->parent->mask ) {
184
- ca_create_mask(ca->parent);
185
- }
186
- ca->mask = (CArray *) ca_refer_new(ca->parent->mask,
187
- CA_BOOLEAN, ca->ndim, ca->dim, 0, 0);
188
- }
189
-
190
199
  ca_operation_function_t ca_fixlen_string_func = {
191
200
  -1, /* CA_OBJ_FIXLEN_STRING — filled by ca_install_obj_type */
192
201
  CA_VIEW_ARRAY,
@@ -197,7 +206,7 @@ ca_operation_function_t ca_fixlen_string_func = {
197
206
  ca_face_sync,
198
207
  ca_face_detach,
199
208
  ca_face_fill_data,
200
- ca_fixlen_string_func_create_mask,
209
+ ca_view_func_create_mask,
201
210
  ca_face_xfer_index,
202
211
  ca_face_xfer_addrs,
203
212
  NULL, /* fold_stride: identity Face is not foldable */
@@ -240,7 +249,7 @@ rb_ca_fixlen_string_wrap_method (VALUE klass, VALUE raw)
240
249
  search / MemoryView still see the raw padded bytes. Genuine trailing NULs
241
250
  cannot survive here; use a raw CA_FIXLEN array for binary blobs. */
242
251
  static VALUE
243
- rb_ca_fixlen_string_storage_to_scalar (VALUE self, VALUE raw)
252
+ rb_ca_fixlen_string_storage_to_element (VALUE self, VALUE raw)
244
253
  {
245
254
  (void) self;
246
255
  if ( TYPE(raw) == T_STRING ) {
@@ -267,6 +276,7 @@ rb_ca_fixlen_string_initialize_copy (VALUE self, VALUE other)
267
276
  {
268
277
  CAFixlenString *ca, *cs;
269
278
  TypedData_Get_Struct(self, CAFixlenString, &cafixlen_string_data_type, ca);
279
+ ca_check_uninitialized(ca);
270
280
  TypedData_Get_Struct(other, CAFixlenString, &cafixlen_string_data_type, cs);
271
281
  if ( ca_func[CA_OBJ_FIXLEN_STRING].pool_init ) {
272
282
  ca_array_pool_alloc(ca, CA_OBJ_FIXLEN_STRING, cs->parent->ndim);
@@ -296,13 +306,16 @@ Init_ca_obj_fixlen_string (void)
296
306
  rb_ca_fixlen_string_initialize_copy, 1);
297
307
  rb_define_singleton_method(rb_cCAFixlenString, "wrap",
298
308
  rb_ca_fixlen_string_wrap_method, 1);
299
- rb_define_method(rb_cCAFixlenString, "storage_to_scalar",
300
- rb_ca_fixlen_string_storage_to_scalar, 1);
301
- ca_face_register_storage_to_scalar(CA_OBJ_FIXLEN_STRING,
302
- rb_ca_fixlen_string_storage_to_scalar);
309
+ rb_define_method(rb_cCAFixlenString, "storage_to_element",
310
+ rb_ca_fixlen_string_storage_to_element, 1);
311
+ ca_face_register_storage_to_element(CA_OBJ_FIXLEN_STRING,
312
+ rb_ca_fixlen_string_storage_to_element);
303
313
 
304
314
  /* CA_FIXLEN is portable storage (fixed-width bytes, no per-process VALUE
305
315
  or per-parent buffer), so the Face can be carried across multi-parent
306
- constructions like CAStack / Marshal / MemoryView. */
316
+ constructions like CAStack. */
307
317
  ca_face_register_state_portable(CA_OBJ_FIXLEN_STRING, 1);
318
+
319
+ /* The bytes are the strings: a MemoryView consumer reads them as "Ns". */
320
+ ca_face_register_memory_view(CA_OBJ_FIXLEN_STRING, 1);
308
321
  }