carray 3.0.2 → 3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +304 -0
  3. data/README.md +3 -4
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +225 -0
  6. data/ext/ca_axis_group.c +177 -99
  7. data/ext/ca_binop_dispatch.c +54 -32
  8. data/ext/ca_binop_dispatch.h +1 -0
  9. data/ext/ca_categorical_iterator.c +66 -71
  10. data/ext/ca_compare.c +8 -2
  11. data/ext/ca_for_buffer.h +21 -68
  12. data/ext/ca_group_iter.c +27 -11
  13. data/ext/ca_kernel_iterator.c +663 -214
  14. data/ext/ca_kernel_iterator.h +231 -167
  15. data/ext/ca_monop_dispatch.c +4 -4
  16. data/ext/ca_monop_dispatch.h +4 -0
  17. data/ext/ca_obj_array.c +132 -22
  18. data/ext/ca_obj_bincmp.c +152 -121
  19. data/ext/ca_obj_binop.c +107 -210
  20. data/ext/ca_obj_bitarray.c +37 -24
  21. data/ext/ca_obj_bitfield.c +95 -62
  22. data/ext/ca_obj_block.c +1 -0
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +96 -25
  25. data/ext/ca_obj_face.c +406 -64
  26. data/ext/ca_obj_face.h +92 -47
  27. data/ext/ca_obj_fake.c +21 -50
  28. data/ext/ca_obj_farray.c +1 -0
  29. data/ext/ca_obj_field.c +16 -3
  30. data/ext/ca_obj_fixlen_string.c +21 -21
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +189 -107
  33. data/ext/ca_obj_moncmp.c +61 -61
  34. data/ext/ca_obj_monop.c +125 -107
  35. data/ext/ca_obj_object.c +543 -233
  36. data/ext/ca_obj_record.c +51 -16
  37. data/ext/ca_obj_reduce.c +5 -8
  38. data/ext/ca_obj_refer.c +117 -33
  39. data/ext/ca_obj_remap.c +57 -17
  40. data/ext/ca_obj_repeat.c +21 -5
  41. data/ext/ca_obj_roll.c +115 -11
  42. data/ext/ca_obj_select.c +47 -49
  43. data/ext/ca_obj_select_axis.c +58 -60
  44. data/ext/ca_obj_shift.c +22 -102
  45. data/ext/ca_obj_stack.c +223 -118
  46. data/ext/ca_obj_stride.c +195 -34
  47. data/ext/ca_obj_string.c +17 -18
  48. data/ext/ca_obj_tile.c +32 -16
  49. data/ext/ca_obj_time.c +23 -25
  50. data/ext/ca_obj_timedelta.c +21 -23
  51. data/ext/ca_obj_transpose.c +12 -3
  52. data/ext/ca_obj_triop.c +110 -117
  53. data/ext/ca_obj_window.c +149 -27
  54. data/ext/ca_op_ipower.c +24 -11
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_sort_kernels.h +70 -0
  58. data/ext/ca_sweep_engine.c +102 -79
  59. data/ext/ca_sweep_engine.h +7 -20
  60. data/ext/ca_triop_dispatch.c +4 -1
  61. data/ext/ca_triop_dispatch.h +5 -2
  62. data/ext/caframe_csv_reader.c +463 -0
  63. data/ext/carray.h +70 -27
  64. data/ext/carray_access.c +455 -211
  65. data/ext/carray_address_basis.c +177 -36
  66. data/ext/carray_attribute.c +45 -3
  67. data/ext/carray_bincount.c +32 -2
  68. data/ext/carray_broadcast.c +46 -14
  69. data/ext/carray_call_cfunc.c +0 -28
  70. data/ext/carray_cast.c +1444 -209
  71. data/ext/carray_cast_func.rb +34 -21
  72. data/ext/carray_conversion.c +197 -73
  73. data/ext/carray_copy.c +32 -11
  74. data/ext/carray_core.c +514 -122
  75. data/ext/carray_count.c +140 -87
  76. data/ext/carray_element.c +18 -27
  77. data/ext/carray_factorize.c +569 -277
  78. data/ext/carray_generate.c +76 -40
  79. data/ext/carray_histogram.c +152 -94
  80. data/ext/carray_hold.c +2 -1
  81. data/ext/carray_index_classifier.c +63 -96
  82. data/ext/carray_internal.h +385 -3
  83. data/ext/carray_lazy.c +834 -79
  84. data/ext/carray_loop.c +81 -65
  85. data/ext/carray_mask.c +682 -316
  86. data/ext/carray_math_kernel.h +1 -0
  87. data/ext/carray_median_percentile.c +221 -76
  88. data/ext/carray_memory_view.c +254 -248
  89. data/ext/carray_operator.c +1089 -359
  90. data/ext/carray_order.c +133 -69
  91. data/ext/carray_partition.c +102 -56
  92. data/ext/carray_random.c +273 -239
  93. data/ext/carray_scatter.c +308 -65
  94. data/ext/carray_segment.c +257 -0
  95. data/ext/carray_slab.c +170 -70
  96. data/ext/carray_sort.c +215 -218
  97. data/ext/carray_sort_kernel.c +8 -1
  98. data/ext/carray_struct.c +99 -21
  99. data/ext/carray_test.c +167 -64
  100. data/ext/carray_time_text.c +1182 -0
  101. data/ext/carray_utils.c +363 -98
  102. data/ext/extconf.rb +27 -2
  103. data/ext/mk_call_cfunc.rb +0 -2
  104. data/ext/mk_pow5_table.rb +35 -0
  105. data/ext/mkkernel.rb +1076 -846
  106. data/ext/ruby_carray.c +7 -1
  107. data/ext/version.h +4 -4
  108. data/ext/version.rb +4 -2
  109. data/lib/carray/attribute.rb +39 -0
  110. data/lib/carray/autoload_carray.rb +19 -0
  111. data/lib/carray/autoload_method_extension.rb +6 -0
  112. data/lib/carray/axis_group.rb +174 -26
  113. data/lib/carray/basics.rb +91 -25
  114. data/lib/carray/bincount_nd.rb +6 -0
  115. data/lib/carray/block_iterator.rb +118 -48
  116. data/lib/carray/boolean_reduce.rb +21 -29
  117. data/lib/carray/categorical.rb +94 -26
  118. data/lib/carray/categorical_iterator.rb +333 -826
  119. data/lib/carray/complex.rb +36 -29
  120. data/lib/carray/conditional.rb +94 -7
  121. data/lib/carray/const_string.rb +46 -0
  122. data/lib/carray/frame/concat.rb +157 -6
  123. data/lib/carray/frame/convert.rb +13 -7
  124. data/lib/carray/frame/csv_parser.rb +589 -82
  125. data/lib/carray/frame/describe.rb +79 -0
  126. data/lib/carray/frame/frame.rb +124 -25
  127. data/lib/carray/frame/group.rb +189 -63
  128. data/lib/carray/frame/io.rb +412 -72
  129. data/lib/carray/frame/join.rb +134 -46
  130. data/lib/carray/frame/records.rb +37 -16
  131. data/lib/carray/frame/reshape.rb +448 -0
  132. data/lib/carray/frame/sort.rb +26 -2
  133. data/lib/carray/frame/verbs.rb +715 -48
  134. data/lib/carray/frame.rb +2 -0
  135. data/lib/carray/fuse_source.rb +111 -2
  136. data/lib/carray/fusion.rb +229 -38
  137. data/lib/carray/inspect.rb +29 -11
  138. data/lib/carray/iterator.rb +14 -2
  139. data/lib/carray/lazy.rb +54 -30
  140. data/lib/carray/mask_gap_fill.rb +66 -52
  141. data/lib/carray/math.rb +32 -19
  142. data/lib/carray/meld_reduce.rb +40 -7
  143. data/lib/carray/methods/align_addr.rb +8 -4
  144. data/lib/carray/methods/bincount.rb +2 -2
  145. data/lib/carray/methods/bit_string.rb +14 -3
  146. data/lib/carray/methods/composition.rb +16 -10
  147. data/lib/carray/methods/factorize.rb +2 -2
  148. data/lib/carray/methods/gather_nd.rb +13 -2
  149. data/lib/carray/methods/insert_block.rb +9 -14
  150. data/lib/carray/methods/is_in.rb +117 -4
  151. data/lib/carray/methods/join.rb +1 -1
  152. data/lib/carray/methods/locate_addr.rb +177 -36
  153. data/lib/carray/methods/mode.rb +18 -1
  154. data/lib/carray/methods/repeat.rb +1 -20
  155. data/lib/carray/methods/resize.rb +21 -23
  156. data/lib/carray/methods/snap.rb +34 -12
  157. data/lib/carray/methods/unique.rb +11 -2
  158. data/lib/carray/record_order.rb +208 -0
  159. data/lib/carray/runtime.rb +14 -11
  160. data/lib/carray/segment_iterator.rb +990 -0
  161. data/lib/carray/serialize.rb +197 -27
  162. data/lib/carray/slab_iterator.rb +51 -27
  163. data/lib/carray/stack.rb +10 -4
  164. data/lib/carray/string_operation_extension.rb +4 -3
  165. data/lib/carray/struct.rb +142 -51
  166. data/lib/carray/struct_builder.rb +110 -14
  167. data/lib/carray/table.rb +2 -2
  168. data/lib/carray/time.rb +517 -103
  169. data/lib/carray/window_iterator.rb +127 -72
  170. data/lib/carray.rb +2 -2
  171. data/yard-stubs/ca_obj_array.rb +4 -4
  172. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  173. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  174. data/yard-stubs/ca_obj_fake.rb +1 -1
  175. data/yard-stubs/ca_obj_field.rb +1 -1
  176. data/yard-stubs/ca_obj_refer.rb +3 -3
  177. data/yard-stubs/ca_obj_roll.rb +0 -1
  178. data/yard-stubs/ca_obj_stride.rb +10 -7
  179. data/yard-stubs/ca_obj_transpose.rb +1 -1
  180. data/yard-stubs/ca_obj_window.rb +1 -2
  181. data/yard-stubs/carray_access.rb +5 -4
  182. data/yard-stubs/carray_broadcast.rb +1 -1
  183. data/yard-stubs/carray_cast.rb +78 -43
  184. data/yard-stubs/carray_conversion.rb +13 -1
  185. data/yard-stubs/carray_copy.rb +8 -3
  186. data/yard-stubs/carray_core.rb +3 -3
  187. data/yard-stubs/carray_count.rb +9 -0
  188. data/yard-stubs/carray_lazy.rb +5 -1
  189. data/yard-stubs/carray_loop.rb +4 -1
  190. data/yard-stubs/carray_mask.rb +34 -10
  191. data/yard-stubs/carray_math.rb +25 -13
  192. data/yard-stubs/carray_median_percentile.rb +10 -6
  193. data/yard-stubs/carray_order.rb +34 -13
  194. data/yard-stubs/carray_scatter.rb +10 -3
  195. data/yard-stubs/carray_segment.rb +72 -0
  196. data/yard-stubs/carray_slab.rb +2 -1
  197. data/yard-stubs/carray_sort.rb +43 -3
  198. data/yard-stubs/carray_test.rb +10 -0
  199. data/yard-stubs/carray_utils.rb +0 -26
  200. metadata +11 -19
  201. data/ext/ca_for_each_element.h +0 -239
  202. data/ext/carray_build_flags.h +0 -3
  203. data/ext/carray_config.h +0 -28
  204. data/ext/carray_kernels_bincmp.c +0 -4446
  205. data/ext/carray_kernels_binop.c +0 -11001
  206. data/ext/carray_kernels_init.c +0 -1131
  207. data/ext/carray_kernels_map.c +0 -3467
  208. data/ext/carray_kernels_moncmp.c +0 -2097
  209. data/ext/carray_kernels_monop.c +0 -18313
  210. data/ext/carray_kernels_reduce_aggregate.c +0 -26005
  211. data/ext/carray_kernels_reduce_boolean.c +0 -330
  212. data/ext/carray_kernels_reduce_cumulative.c +0 -14862
  213. data/ext/carray_kernels_reduce_extreme.c +0 -17494
  214. data/ext/carray_kernels_reduce_variance.c +0 -3910
  215. data/ext/carray_kernels_scan.c +0 -3693
  216. data/ext/carray_kernels_search.c +0 -32218
  217. data/ext/carray_kernels_sort.c +0 -10626
  218. data/ext/carray_kernels_triop.c +0 -1392
@@ -102,15 +102,113 @@
102
102
  discovery surface rather than this gate (see
103
103
  devel/PROPOSAL_DISCOVERY_FAMILY_FACE_GATE.md §4). */
104
104
 
105
+ /* The value-hash family decides whether two cells hold the same value: every
106
+ NaN is one value, and -0.0 is the same value as +0.0. A float cell's key
107
+ is the bytes of its canonical form (one NaN, +0.0 for a zero), so equal
108
+ values have equal keys; fz_canonical_* writes that form back as a value. */
109
+ #define FZ_NAN_KEY_DOUBLE 0x7ff8000000000000ULL
110
+ #define FZ_NAN_KEY_FLOAT 0x7fc00000UL
111
+
112
+ static inline uint64_t
113
+ fz_key_double (double v)
114
+ {
115
+ uint64_t b;
116
+ if ( v != v ) return FZ_NAN_KEY_DOUBLE;
117
+ if ( v == 0.0 ) v = 0.0;
118
+ memcpy(&b, &v, sizeof(b));
119
+ return b;
120
+ }
121
+
122
+ static inline uint64_t
123
+ fz_key_float (float v)
124
+ {
125
+ uint32_t b;
126
+ if ( v != v ) return FZ_NAN_KEY_FLOAT;
127
+ if ( v == 0.0f ) v = 0.0f;
128
+ memcpy(&b, &v, sizeof(b));
129
+ return (uint64_t) b;
130
+ }
131
+
132
+ static inline double
133
+ fz_canonical_double (double v)
134
+ {
135
+ uint64_t b = fz_key_double(v);
136
+ memcpy(&v, &b, sizeof(v));
137
+ return v;
138
+ }
139
+
140
+ static inline float
141
+ fz_canonical_float (float v)
142
+ {
143
+ uint32_t b = (uint32_t) fz_key_float(v);
144
+ memcpy(&v, &b, sizeof(v));
145
+ return v;
146
+ }
147
+
148
+ /* For the kernel macros, which take the cell type T. */
149
+ #define FZ_FLOAT_KEY(T, v) \
150
+ _Generic((T) 0, float: fz_key_float, double: fz_key_double)(v)
151
+
152
+ /* A complex array as hash keys: its cells as fixlen blobs of the same
153
+ width, each component written so that equal values have equal bytes
154
+ (-0.0 as 0.0, every NaN as one NaN), with the mask kept. The fixlen lane
155
+ then answers equality for complex values. */
156
+ static VALUE
157
+ fz_complex_key (VALUE self)
158
+ {
159
+ volatile VALUE out;
160
+ CArray *ca, *co;
161
+ ca_size_t i, n;
162
+ GetCArray(self, ca);
163
+ out = rb_carray_new(CA_FIXLEN, ca->ndim, ca->dim, ca->bytes, NULL);
164
+ GetCArray(out, co);
165
+ ca_copy_data(ca, co->ptr);
166
+ n = 2 * ca->elements;
167
+ if ( ca->data_type == CA_CMPLX128 ) {
168
+ double *p = (double *) co->ptr;
169
+ for ( i = 0; i < n; i++ ) p[i] = fz_canonical_double(p[i]);
170
+ }
171
+ else {
172
+ float *p = (float *) co->ptr;
173
+ for ( i = 0; i < n; i++ ) p[i] = fz_canonical_float(p[i]);
174
+ }
175
+ if ( ca_has_mask(ca) ) {
176
+ ca_copy_mask(co, ca);
177
+ }
178
+ return out;
179
+ }
180
+
181
+ /* The members whose output carries no values (a mask, a count) read a
182
+ complex array through its keys and need nothing back. */
183
+ static VALUE
184
+ fz_complex_as_key (VALUE self)
185
+ {
186
+ CArray *ca;
187
+ GetCArray(self, ca);
188
+ if ( ! ca_is_face(ca) && ca_is_complex_type(ca) ) {
189
+ return fz_complex_key(self);
190
+ }
191
+ return self;
192
+ }
193
+
105
194
  /* Descend `self` when it is an ORDERABLE Face; *pface keeps the pre-strip Face
106
- for the re-lift (Qnil when there is nothing to put back). */
195
+ for the re-lift (Qnil when there is nothing to put back). A complex array
196
+ is turned into hash keys (fz_complex_key) and *pface keeps it, so the
197
+ re-lift reads the values back as complex. */
107
198
  static VALUE
108
199
  fz_face_descend (VALUE self, volatile VALUE *pface, const char *name)
109
200
  {
110
201
  CArray *ca;
111
202
  GetCArray(self, ca);
203
+ if ( ! ca_is_face(ca) && ca_is_complex_type(ca) ) {
204
+ *pface = self;
205
+ return fz_complex_key(self);
206
+ }
112
207
  if ( ca_is_face(ca) ) {
113
- if ( ca_test_flag(ca, CA_FLAG_FACE_ORDERABLE_STORAGE) ) {
208
+ /* A record is its bytes: two records are the same value exactly when
209
+ their bytes are, so the fixlen lane answers for it. */
210
+ if ( ca_test_flag(ca, CA_FLAG_FACE_ORDERABLE_STORAGE)
211
+ || ca->obj_type == CA_OBJ_RECORD ) {
114
212
  *pface = self;
115
213
  return rb_ca_strip_face_value(self);
116
214
  }
@@ -123,23 +221,18 @@ fz_face_descend (VALUE self, volatile VALUE *pface, const char *name)
123
221
  argument. */
124
222
  rb_raise(rb_eArgError,
125
223
  "%s: %s is not orderable by storage (a cell encodes the value "
126
- "rather than being it); convert it first -- #to_string for a "
127
- "string Face -- or pass .parent to work in storage space",
128
- name, rb_obj_classname(self));
224
+ "rather than being it); convert it first -- %s -- or pass "
225
+ ".parent to work in storage space",
226
+ name, rb_obj_classname(self), ca_face_conversion_hint(self));
129
227
  }
130
228
  *pface = Qnil;
131
229
  return self;
132
230
  }
133
231
 
134
232
  /* Bring `operand` into the space of `reference` (the *pre-strip* Face) and
135
- descend it, so both sides hash in one storage space.
136
-
137
- `to_comparable` exists because a Face can carry a *unit* -- an alternative
138
- space for the same value -- so a reference that defines it reconciles any
139
- operand type through it (and refuses what it cannot convert, which is how a
140
- bare storage number stays out). A reference with one space only (COMPARABLE,
141
- or a Face with no unit algebra at all, e.g. CAString) has nothing to
142
- reconcile between: descending both sides is the whole job. */
233
+ descend it, so both sides hash in one storage space
234
+ (ca_face_reconcile_operand). A complex reference instead turns the
235
+ operand into keys of its own complex type. */
143
236
  static VALUE
144
237
  fz_face_reconcile (VALUE reference, VALUE operand, const char *name)
145
238
  {
@@ -148,22 +241,37 @@ fz_face_reconcile (VALUE reference, VALUE operand, const char *name)
148
241
  return operand;
149
242
  }
150
243
  GetCArray(reference, ca);
151
- if ( ! ca_test_flag(ca, CA_FLAG_FACE_COMPARABLE_STORAGE)
152
- && rb_respond_to(reference, rb_intern("to_comparable")) ) {
153
- return rb_ca_strip_face_value(rb_funcall(reference,
154
- rb_intern("to_comparable"),
155
- 1, operand));
244
+ if ( ! ca_is_face(ca) ) {
245
+ /* A complex reference: the operand becomes keys of the same kind, in the
246
+ reference's complex type. */
247
+ CArray *op;
248
+ if ( ! rb_obj_is_carray(operand) ) {
249
+ return operand;
250
+ }
251
+ GetCArray(operand, op);
252
+ if ( op->data_type != ca->data_type ) {
253
+ operand = rb_ca_to_type(operand, INT2NUM(ca->data_type), Qnil);
254
+ }
255
+ return fz_complex_key(operand);
156
256
  }
157
- return ca_face_operand_descend(operand, name);
257
+ return ca_face_reconcile_operand(reference, operand, name);
158
258
  }
159
259
 
160
- /* Put the Face back on a value-carrying output. */
260
+ /* Put the Face back on a value-carrying output, or read complex keys back
261
+ as complex values. */
161
262
  static VALUE
162
263
  fz_face_relift (VALUE out, VALUE face)
163
264
  {
265
+ CArray *cf, *co;
164
266
  if ( NIL_P(face) ) {
165
267
  return out;
166
268
  }
269
+ GetCArray(face, cf);
270
+ if ( ! ca_is_face(cf) ) {
271
+ GetCArray(out, co);
272
+ return rb_ca_copy(rb_ca_refer_new(out, cf->data_type, co->ndim, co->dim,
273
+ cf->bytes, 0));
274
+ }
167
275
  return ca_face_lift(out, face);
168
276
  }
169
277
 
@@ -176,16 +284,95 @@ typedef struct {
176
284
  VALUE *val; /* object lane only: the interned VALUE, kept for an eql? re-check
177
285
  on a hash collision (NULL in the numeric lane, where the widened
178
286
  key is lossless so a key match already proves value equality).
179
- Its VALUEs are always elements of the live receiver, so the GC
180
- reaches them through `self`; this array is not separately marked. */
287
+ A receiver that computes its cells (a CAObject, a lazy
288
+ expression) hands each fiber over in a scratch that is refilled,
289
+ so the owner marks this array (fz_tmp_values). */
181
290
  char *raw; /* fixlen lane only: cap*esz bytes, the interned element bytes kept
182
291
  for a memcmp re-check on a hash collision (NULL otherwise). */
183
292
  int esz; /* fixlen lane: element width in bytes (0 otherwise) */
293
+ int has_val; /* object lane: val is kept (0 otherwise) */
184
294
  ca_size_t cap; /* power of two */
185
295
  ca_size_t n; /* distinct keys interned so far */
186
296
  int shift; /* 64 - log2(cap); the home slot is the top log2(cap) bits */
297
+ VALUE hbuf; /* owner of the tables, one block (fz_tmp_alloc) */
187
298
  } fz_hash;
188
299
 
300
+ /* The tables live in buffers owned by hidden Ruby objects rather than bare
301
+ xmalloc. The object lane calls #hash and #eql? for every cell, and a raise
302
+ there leaves the tables with nothing to free them; the owner is reclaimed
303
+ by the GC once the frame holding it is gone, and frees its buffer then.
304
+ An owner whose buffer keeps VALUEs (fz_tmp_values) marks them: the object
305
+ lane's cells come from a scratch the walk refills, not only from the
306
+ receiver, so nothing else holds the ones already interned. */
307
+ typedef struct {
308
+ char *buf;
309
+ ca_size_t voff; /* byte offset of the VALUE run in buf */
310
+ ca_size_t nvals; /* VALUEs in the run (0: nothing to mark) */
311
+ } fz_buffer;
312
+
313
+ static void
314
+ fz_buffer_mark (void *ptr)
315
+ {
316
+ fz_buffer *b = (fz_buffer *) ptr;
317
+ if ( b->buf && b->nvals > 0 ) {
318
+ VALUE *v = (VALUE *) (b->buf + b->voff);
319
+ ca_size_t i;
320
+ for ( i = 0; i < b->nvals; i++ ) rb_gc_mark(v[i]);
321
+ }
322
+ }
323
+
324
+ static void
325
+ fz_buffer_free (void *ptr)
326
+ {
327
+ fz_buffer *b = (fz_buffer *) ptr;
328
+ xfree(b->buf);
329
+ xfree(b);
330
+ }
331
+
332
+ static const rb_data_type_t fz_buffer_type = {
333
+ "carray_factorize_buffer",
334
+ { fz_buffer_mark, fz_buffer_free, NULL, },
335
+ NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY,
336
+ };
337
+
338
+ static void *
339
+ fz_tmp_alloc (VALUE *store, size_t bytes)
340
+ {
341
+ fz_buffer *b;
342
+ *store = TypedData_Make_Struct(0, fz_buffer, &fz_buffer_type, b);
343
+ b->buf = (char *) xmalloc(bytes);
344
+ return b->buf;
345
+ }
346
+
347
+ static void *
348
+ fz_tmp_realloc (VALUE store, size_t bytes)
349
+ {
350
+ fz_buffer *b = (fz_buffer *) DATA_PTR(store);
351
+ b->buf = (char *) xrealloc(b->buf, bytes);
352
+ return b->buf;
353
+ }
354
+
355
+ /* Mark the n VALUEs at vals (inside store's buffer); they must be valid. */
356
+ static void
357
+ fz_tmp_values (VALUE store, void *vals, ca_size_t n)
358
+ {
359
+ fz_buffer *b = (fz_buffer *) DATA_PTR(store);
360
+ b->voff = (ca_size_t) ((char *) vals - b->buf);
361
+ b->nvals = n;
362
+ }
363
+
364
+ static void
365
+ fz_tmp_free (VALUE *store)
366
+ {
367
+ if ( *store ) {
368
+ fz_buffer *b = (fz_buffer *) DATA_PTR(*store);
369
+ b->nvals = 0;
370
+ xfree(b->buf);
371
+ b->buf = NULL;
372
+ *store = 0;
373
+ }
374
+ }
375
+
189
376
  /* Multiplicative hashing (Knuth, TAOCP vol. 3 sec. 6.4): multiply by
190
377
  2^64 / golden-ratio and take the TOP log2(cap) bits of the product -- the
191
378
  high bits are where a multiply mixes best (the low bits carry little
@@ -195,19 +382,49 @@ typedef struct {
195
382
  #define FZ_GOLDEN 0x9E3779B97F4A7C15ULL
196
383
  #define FZ_HOME(h, key) ((ca_size_t) (((uint64_t)(key) * FZ_GOLDEN) >> (h)->shift))
197
384
 
385
+ /* Points the tables of h at one new block for cap slots: key, then the
386
+ object lane's val, code, used, and the fixlen lane's raw bytes. */
198
387
  static void
199
- fz_hash_init (fz_hash *h)
388
+ fz_hash_alloc (fz_hash *h, ca_size_t cap)
389
+ {
390
+ int has_val = h->has_val;
391
+ size_t nkey = sizeof(uint64_t) * (size_t) cap;
392
+ size_t nval = has_val ? sizeof(VALUE) * (size_t) cap : 0;
393
+ size_t ncode = sizeof(int32_t) * (size_t) cap;
394
+ size_t nused = sizeof(uint8_t) * (size_t) cap;
395
+ size_t nraw = (size_t) cap * (size_t) h->esz;
396
+ char *b = fz_tmp_alloc(&h->hbuf, nkey + nval + ncode + nused + nraw);
397
+ h->key = (uint64_t *) b; b += nkey;
398
+ h->val = has_val ? (VALUE *) b : NULL; b += nval;
399
+ h->code = (int32_t *) b; b += ncode;
400
+ h->used = (uint8_t *) b; b += nused;
401
+ h->raw = h->esz ? b : NULL;
402
+ h->cap = cap;
403
+ MEMZERO(h->used, uint8_t, cap);
404
+ if ( has_val ) {
405
+ ca_size_t i;
406
+ for ( i = 0; i < cap; i++ ) h->val[i] = Qnil;
407
+ fz_tmp_values(h->hbuf, h->val, cap);
408
+ }
409
+ }
410
+
411
+ /* has_val: the object lane (a slot keeps its VALUE); esz > 0: the fixlen
412
+ lane (a slot keeps esz bytes). */
413
+ static void
414
+ fz_hash_setup (fz_hash *h, int has_val, int esz)
200
415
  {
201
- h->cap = 1024;
202
416
  h->shift = 64 - 10; /* log2(1024) = 10 */
203
417
  h->n = 0;
204
- h->key = ALLOC_N(uint64_t, h->cap);
205
- h->code = ALLOC_N(int32_t, h->cap);
206
- h->used = ALLOC_N(uint8_t, h->cap);
207
- h->val = NULL; /* numeric lane: no VALUE re-check needed */
208
- h->raw = NULL;
209
- h->esz = 0;
210
- MEMZERO(h->used, uint8_t, h->cap);
418
+ h->hbuf = 0;
419
+ h->has_val = has_val;
420
+ h->esz = esz;
421
+ fz_hash_alloc(h, 1024);
422
+ }
423
+
424
+ static void
425
+ fz_hash_init (fz_hash *h)
426
+ {
427
+ fz_hash_setup(h, 0, 0); /* numeric lane: no re-check needed */
211
428
  }
212
429
 
213
430
  /* Object lane: the widened key is `rb_hash` (lossy), so a slot also stores the
@@ -216,8 +433,7 @@ fz_hash_init (fz_hash *h)
216
433
  static void
217
434
  fz_hash_init_obj (fz_hash *h)
218
435
  {
219
- fz_hash_init(h);
220
- h->val = ALLOC_N(VALUE, h->cap);
436
+ fz_hash_setup(h, 1, 0);
221
437
  }
222
438
 
223
439
  /* Fixlen lane: the key is a byte-hash of the esz-wide element (lossy), so a slot
@@ -227,19 +443,18 @@ fz_hash_init_obj (fz_hash *h)
227
443
  static void
228
444
  fz_hash_init_mem (fz_hash *h, int esz)
229
445
  {
230
- fz_hash_init(h);
231
- h->esz = esz;
232
- h->raw = ALLOC_N(char, h->cap * esz);
446
+ fz_hash_setup(h, 0, esz);
233
447
  }
234
448
 
235
449
  static void
236
450
  fz_hash_free (fz_hash *h)
237
451
  {
238
- if ( h->key ) { xfree(h->key); h->key = NULL; }
239
- if ( h->code ) { xfree(h->code); h->code = NULL; }
240
- if ( h->used ) { xfree(h->used); h->used = NULL; }
241
- if ( h->val ) { xfree(h->val); h->val = NULL; }
242
- if ( h->raw ) { xfree(h->raw); h->raw = NULL; }
452
+ fz_tmp_free(&h->hbuf);
453
+ h->key = NULL;
454
+ h->code = NULL;
455
+ h->used = NULL;
456
+ h->val = NULL;
457
+ h->raw = NULL;
243
458
  }
244
459
 
245
460
  /* FNV-1a over esz bytes: a lossy 64-bit key for the fixlen lane. */
@@ -272,14 +487,10 @@ fz_hash_grow (fz_hash *h)
272
487
  VALUE *ov = h->val;
273
488
  char *orw = h->raw;
274
489
  int esz = h->esz;
275
- h->cap = newcap;
276
- h->shift -= 1; /* log2(cap) grew by one */
277
- h->key = ALLOC_N(uint64_t, newcap);
278
- h->code = ALLOC_N(int32_t, newcap);
279
- h->used = ALLOC_N(uint8_t, newcap);
280
- if ( ov ) { h->val = ALLOC_N(VALUE, newcap); }
281
- if ( orw ) { h->raw = ALLOC_N(char, newcap * esz); }
282
- MEMZERO(h->used, uint8_t, newcap);
490
+ VALUE hold = h->hbuf; /* keeps the old block until it is freed below */
491
+ h->hbuf = 0;
492
+ h->shift -= 1; /* log2(cap) grew by one */
493
+ fz_hash_alloc(h, newcap);
283
494
  ca_size_t mask = newcap - 1;
284
495
  for ( ca_size_t s = 0; s < oldcap; s++ ) {
285
496
  if ( ! ou[s] ) { continue; }
@@ -291,9 +502,7 @@ fz_hash_grow (fz_hash *h)
291
502
  if ( ov ) { h->val[slot] = ov[s]; }
292
503
  if ( orw ) { memcpy(h->raw + slot * esz, orw + s * esz, esz); }
293
504
  }
294
- xfree(ok); xfree(oc); xfree(ou);
295
- if ( ov ) { xfree(ov); }
296
- if ( orw ) { xfree(orw); }
505
+ fz_tmp_free(&hold);
297
506
  }
298
507
 
299
508
  /* True when v is a Float holding NaN (any bit pattern). */
@@ -315,7 +524,7 @@ fz_hash_intern_obj (fz_hash *h, VALUE v, int *is_new)
315
524
  {
316
525
  if ( (h->n + 1) * 10 >= h->cap * 7 ) { fz_hash_grow(h); } /* load factor 0.7 */
317
526
  int v_nan = fz_is_float_nan(v);
318
- uint64_t key = v_nan ? 0x7ff8000000000000ULL
527
+ uint64_t key = v_nan ? FZ_NAN_KEY_DOUBLE
319
528
  : (uint64_t) NUM2LL(rb_hash(v));
320
529
  ca_size_t mask = h->cap - 1;
321
530
  ca_size_t slot = FZ_HOME(h, key);
@@ -413,7 +622,7 @@ static int
413
622
  fz_hash_lookup_obj (fz_hash *h, VALUE v, int32_t *code_out)
414
623
  {
415
624
  int v_nan = fz_is_float_nan(v);
416
- uint64_t key = v_nan ? 0x7ff8000000000000ULL
625
+ uint64_t key = v_nan ? FZ_NAN_KEY_DOUBLE
417
626
  : (uint64_t) NUM2LL(rb_hash(v));
418
627
  ca_size_t mask = h->cap - 1;
419
628
  ca_size_t slot = FZ_HOME(h, key);
@@ -452,23 +661,44 @@ typedef struct {
452
661
  ca_size_t cap; /* capacity in elements */
453
662
  ca_size_t n; /* elements stored */
454
663
  int esz; /* element bytes */
664
+ VALUE hp; /* owner of p (see fz_tmp_alloc) */
665
+ int obj; /* p holds VALUEs, marked by hp */
455
666
  } fz_levels;
456
667
 
668
+ /* Fill the VALUE slots from..cap with Qnil and mark all cap of them. */
457
669
  static void
458
- fz_levels_init (fz_levels *l, int esz)
670
+ fz_levels_values (fz_levels *l, ca_size_t from)
671
+ {
672
+ VALUE *v = (VALUE *) l->p;
673
+ ca_size_t i;
674
+ for ( i = from; i < l->cap; i++ ) v[i] = Qnil;
675
+ fz_tmp_values(l->hp, l->p, l->cap);
676
+ }
677
+
678
+ static void
679
+ fz_levels_init (fz_levels *l, int esz, int obj)
459
680
  {
460
681
  l->esz = esz;
461
682
  l->cap = 16;
462
683
  l->n = 0;
463
- l->p = ALLOC_N(char, l->cap * esz);
684
+ l->hp = 0;
685
+ l->obj = obj;
686
+ l->p = fz_tmp_alloc(&l->hp, (size_t) l->cap * esz);
687
+ if ( obj ) {
688
+ fz_levels_values(l, 0);
689
+ }
464
690
  }
465
691
 
466
692
  static void
467
693
  fz_levels_push (fz_levels *l, const void *e)
468
694
  {
469
695
  if ( l->n == l->cap ) {
696
+ ca_size_t old = l->cap;
470
697
  l->cap <<= 1;
471
- REALLOC_N(l->p, char, l->cap * l->esz);
698
+ l->p = fz_tmp_realloc(l->hp, (size_t) l->cap * l->esz);
699
+ if ( l->obj ) {
700
+ fz_levels_values(l, old);
701
+ }
472
702
  }
473
703
  memcpy(l->p + l->n * l->esz, e, l->esz);
474
704
  l->n++;
@@ -477,7 +707,35 @@ fz_levels_push (fz_levels *l, const void *e)
477
707
  static void
478
708
  fz_levels_free (fz_levels *l)
479
709
  {
480
- if ( l->p ) { xfree(l->p); l->p = NULL; }
710
+ fz_tmp_free(&l->hp);
711
+ l->p = NULL;
712
+ }
713
+
714
+ /* ---- the frame a discovery method runs in ---------------------------------
715
+
716
+ The object lane calls #hash and #eql? for every cell, and either can raise
717
+ in the middle of a walk. The walks' states live in this frame, outside the
718
+ method's body, and ca_iter_ensure finishes them however the body leaves;
719
+ the tables have owners the unwind collects (fz_tmp_alloc). Slots 0
720
+ and 1 hold a method's own walks, slot 2 the walk of fz_intern_all. */
721
+
722
+ typedef struct {
723
+ ca_iter_state st[3];
724
+ VALUE self;
725
+ VALUE a;
726
+ VALUE b;
727
+ int i;
728
+ } fz_frame;
729
+
730
+ static VALUE
731
+ fz_run (VALUE (*body)(VALUE), VALUE self, VALUE a, VALUE b, int i)
732
+ {
733
+ fz_frame fr;
734
+ fr.self = self;
735
+ fr.a = a;
736
+ fr.b = b;
737
+ fr.i = i;
738
+ return ca_iter_ensure(3, fr.st, body, (VALUE) &fr);
481
739
  }
482
740
 
483
741
  /* @overload __factorize_appearance__
@@ -497,8 +755,10 @@ fz_levels_free (fz_levels *l)
497
755
  (row-major flatten) order.
498
756
  */
499
757
  static VALUE
500
- rb_ca_factorize_appearance (VALUE self)
758
+ fz_factorize_appearance_body (VALUE arg)
501
759
  {
760
+ fz_frame *fr = (fz_frame *) arg;
761
+ VALUE self = fr->self;
502
762
  CArray *ca;
503
763
  volatile VALUE face;
504
764
  self = fz_face_descend(self, &face, "categorize");
@@ -532,11 +792,11 @@ rb_ca_factorize_appearance (VALUE self)
532
792
  if ( dt == CA_OBJECT ) { fz_hash_init_obj(&h); }
533
793
  else if ( dt == CA_FIXLEN ) { fz_hash_init_mem(&h, (int) ca->bytes); }
534
794
  else { fz_hash_init(&h); }
535
- fz_levels_init(&lv, (int) ca->bytes);
795
+ fz_levels_init(&lv, (int) ca->bytes, ca->data_type == CA_OBJECT);
536
796
 
537
797
  int8_t axis = (int8_t) (ca->ndim - 1); /* innermost fiber = row-major flatten */
538
798
 
539
- ca_iter_state st_in, st_out;
799
+ ca_iter_state *st_in = &fr->st[0], *st_out = &fr->st[1];
540
800
  char *p_in, *p_out;
541
801
  boolean8_t *m;
542
802
  ca_size_t n;
@@ -558,25 +818,18 @@ rb_ca_factorize_appearance (VALUE self)
558
818
  } while (0)
559
819
 
560
820
  /* Float lane: reproduce `==` with two value-based exceptions matching the
561
- discovery family -- all NaN collapse to one canonical key (v != v -> NANKEY),
821
+ discovery family -- all NaN collapse to one canonical key (FZ_FLOAT_KEY),
562
822
  and -0.0 == +0.0 (a zero normalizes to +0.0 before the bitwise key). The
563
823
  level pushed is the raw first-seen element (an appearance-first NaN / -0.0 is
564
824
  kept as-is), so masked source cells store the sentinel. */
565
- #define FZ_LOOP_FLOAT(T, UINT, NANKEY) \
825
+ #define FZ_LOOP_FLOAT(T) \
566
826
  do { \
567
827
  const T *ip = (const T *) p_in; \
568
828
  uint32_t *op = (uint32_t *) p_out; \
569
829
  for ( ca_size_t i = 0; i < n; i++ ) { \
570
830
  if ( m && m[i] ) { op[i] = 0xFFFFFFFFu; continue; } \
571
831
  T v = ip[i]; \
572
- uint64_t key; \
573
- if ( v != v ) { key = (NANKEY); } \
574
- else { \
575
- if ( v == (T) 0 ) { v = (T) 0; } \
576
- UINT bits; \
577
- memcpy(&bits, &v, sizeof(bits)); \
578
- key = (uint64_t) bits; \
579
- } \
832
+ uint64_t key = FZ_FLOAT_KEY(T, v); \
580
833
  int is_new; \
581
834
  int32_t code = fz_hash_intern(&h, key, &is_new); \
582
835
  if ( is_new ) { fz_levels_push(&lv, &ip[i]); } \
@@ -615,7 +868,7 @@ rb_ca_factorize_appearance (VALUE self)
615
868
  } \
616
869
  } while (0)
617
870
 
618
- CA_FOR_EACH_FIBER_INOUT_MASKED(st_in, st_out, ca, cu32, axis,
871
+ CA_FOR_EACH_FIBER_INOUT_MASKED(*st_in, *st_out, ca, cu32, axis,
619
872
  CA_KERNEL_READ, p_in, p_out, n, m) {
620
873
  switch ( dt ) {
621
874
  case CA_INT8: FZ_LOOP(int8_t, (int64_t)); break;
@@ -626,8 +879,8 @@ rb_ca_factorize_appearance (VALUE self)
626
879
  case CA_UINT16: FZ_LOOP(uint16_t, (uint64_t)); break;
627
880
  case CA_UINT32: FZ_LOOP(uint32_t, (uint64_t)); break;
628
881
  case CA_UINT64: FZ_LOOP(uint64_t, (uint64_t)); break;
629
- case CA_FLOAT32: FZ_LOOP_FLOAT(float, uint32_t, 0x7fc00000ULL); break;
630
- case CA_FLOAT64: FZ_LOOP_FLOAT(double, uint64_t, 0x7ff8000000000000ULL); break;
882
+ case CA_FLOAT32: FZ_LOOP_FLOAT(float); break;
883
+ case CA_FLOAT64: FZ_LOOP_FLOAT(double); break;
631
884
  case CA_OBJECT: FZ_LOOP_OBJ; break;
632
885
  case CA_FIXLEN: FZ_LOOP_MEM; break;
633
886
  }
@@ -686,6 +939,12 @@ rb_ca_factorize_appearance (VALUE self)
686
939
  return rb_ary_new3(2, vcodes, fz_face_relift(vlev, face));
687
940
  }
688
941
 
942
+ static VALUE
943
+ rb_ca_factorize_appearance (VALUE self)
944
+ {
945
+ return fz_run(fz_factorize_appearance_body, self, Qnil, Qnil, 0);
946
+ }
947
+
689
948
  /* @overload __mask_duplicates__(axis)
690
949
 
691
950
  INTERNAL (mask_duplicates). Mark each cell whose value duplicates an
@@ -706,8 +965,11 @@ rb_ca_factorize_appearance (VALUE self)
706
965
  value (so the second and later NaN are duplicates, as in the numeric lane).
707
966
  */
708
967
  static VALUE
709
- rb_ca_mask_duplicates (VALUE self, VALUE vaxis)
968
+ fz_mask_duplicates_body (VALUE arg)
710
969
  {
970
+ fz_frame *fr = (fz_frame *) arg;
971
+ VALUE self = fr->self;
972
+ VALUE vaxis = fr->a;
711
973
  CArray *ca;
712
974
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
713
975
 
@@ -741,7 +1003,7 @@ rb_ca_mask_duplicates (VALUE self, VALUE vaxis)
741
1003
  else if ( dt == CA_FIXLEN ) { fz_hash_init_mem(&h, (int) ca->bytes); }
742
1004
  else { fz_hash_init(&h); }
743
1005
 
744
- ca_iter_state st_in, st_out;
1006
+ ca_iter_state *st_in = &fr->st[0], *st_out = &fr->st[1];
745
1007
  char *p_in, *p_out;
746
1008
  boolean8_t *m;
747
1009
  ca_size_t n;
@@ -764,24 +1026,17 @@ rb_ca_mask_duplicates (VALUE self, VALUE vaxis)
764
1026
 
765
1027
  /* Float variant: reproduce `==` with two value-based exceptions so the
766
1028
  distinct-value judgement matches uniq / the discovery family. All NaN
767
- collapse to one canonical key (v != v -> NANKEY), so the second and later
1029
+ collapse to one canonical key (FZ_FLOAT_KEY), so the second and later
768
1030
  NaN in a fiber are duplicates; -0.0 == +0.0, so a zero normalizes to +0.0
769
1031
  before the bitwise key. */
770
- #define MD_LOOP_FLOAT(T, UINT, NANKEY) \
1032
+ #define MD_LOOP_FLOAT(T) \
771
1033
  do { \
772
1034
  const T *ip = (const T *) p_in; \
773
1035
  boolean8_t *op = (boolean8_t *) p_out; \
774
1036
  for ( ca_size_t i = 0; i < n; i++ ) { \
775
1037
  if ( m && m[i] ) { op[i] = 0; continue; } \
776
1038
  T v = ip[i]; \
777
- uint64_t key; \
778
- if ( v != v ) { key = (NANKEY); } \
779
- else { \
780
- if ( v == (T) 0 ) { v = (T) 0; } \
781
- UINT bits; \
782
- memcpy(&bits, &v, sizeof(bits)); \
783
- key = (uint64_t) bits; \
784
- } \
1039
+ uint64_t key = FZ_FLOAT_KEY(T, v); \
785
1040
  int is_new; \
786
1041
  fz_hash_intern(&h, key, &is_new); \
787
1042
  op[i] = is_new ? 0 : 1; \
@@ -814,7 +1069,7 @@ rb_ca_mask_duplicates (VALUE self, VALUE vaxis)
814
1069
  } \
815
1070
  } while (0)
816
1071
 
817
- CA_FOR_EACH_FIBER_INOUT_MASKED(st_in, st_out, ca, cout, (int8_t) axis,
1072
+ CA_FOR_EACH_FIBER_INOUT_MASKED(*st_in, *st_out, ca, cout, (int8_t) axis,
818
1073
  CA_KERNEL_READ, p_in, p_out, n, m) {
819
1074
  fz_hash_reset(&h); /* independent seen-set per fiber */
820
1075
  switch ( dt ) {
@@ -826,8 +1081,8 @@ rb_ca_mask_duplicates (VALUE self, VALUE vaxis)
826
1081
  case CA_UINT16: MD_LOOP(uint16_t, (uint64_t)); break;
827
1082
  case CA_UINT32: MD_LOOP(uint32_t, (uint64_t)); break;
828
1083
  case CA_UINT64: MD_LOOP(uint64_t, (uint64_t)); break;
829
- case CA_FLOAT32: MD_LOOP_FLOAT(float, uint32_t, 0x7fc00000ULL); break;
830
- case CA_FLOAT64: MD_LOOP_FLOAT(double, uint64_t, 0x7ff8000000000000ULL); break;
1084
+ case CA_FLOAT32: MD_LOOP_FLOAT(float); break;
1085
+ case CA_FLOAT64: MD_LOOP_FLOAT(double); break;
831
1086
  case CA_OBJECT: MD_LOOP_OBJ; break;
832
1087
  case CA_FIXLEN: MD_LOOP_MEM; break;
833
1088
  }
@@ -841,6 +1096,12 @@ rb_ca_mask_duplicates (VALUE self, VALUE vaxis)
841
1096
  return vout;
842
1097
  }
843
1098
 
1099
+ static VALUE
1100
+ rb_ca_mask_duplicates (VALUE self, VALUE vaxis)
1101
+ {
1102
+ return fz_run(fz_mask_duplicates_body, fz_complex_as_key(self), vaxis, Qnil, 0);
1103
+ }
1104
+
844
1105
  /* @overload __unique_flat__
845
1106
 
846
1107
  INTERNAL (CArray#unique). Collect the distinct values of self in
@@ -865,8 +1126,10 @@ rb_ca_mask_duplicates (VALUE self, VALUE vaxis)
865
1126
  uniform-width binary cells the fixlen seen-set used.
866
1127
  */
867
1128
  static VALUE
868
- rb_ca_unique_flat (VALUE self)
1129
+ fz_unique_flat_body (VALUE arg)
869
1130
  {
1131
+ fz_frame *fr = (fz_frame *) arg;
1132
+ VALUE self = fr->self;
870
1133
  CArray *ca;
871
1134
  volatile VALUE face;
872
1135
  self = fz_face_descend(self, &face, "unique");
@@ -893,11 +1156,11 @@ rb_ca_unique_flat (VALUE self)
893
1156
  if ( dt == CA_OBJECT ) { fz_hash_init_obj(&h); }
894
1157
  else if ( dt == CA_FIXLEN ) { fz_hash_init_mem(&h, (int) ca->bytes); }
895
1158
  else { fz_hash_init(&h); }
896
- fz_levels_init(&lv, (int) ca->bytes);
1159
+ fz_levels_init(&lv, (int) ca->bytes, ca->data_type == CA_OBJECT);
897
1160
 
898
1161
  int8_t axis = (int8_t) (ca->ndim - 1); /* innermost fiber; hash not reset =
899
1162
  one seen-set over the whole array */
900
- ca_iter_state st_in;
1163
+ ca_iter_state *st_in = &fr->st[0];
901
1164
  char *p_in;
902
1165
  boolean8_t *m;
903
1166
  ca_size_t n;
@@ -916,20 +1179,13 @@ rb_ca_unique_flat (VALUE self)
916
1179
 
917
1180
  /* Float: NaN collapses to one canonical key; -0.0 / +0.0 share a key. The
918
1181
  pushed level is the raw first-seen element. */
919
- #define UQ_LOOP_FLOAT(T, UINT, NANKEY) \
1182
+ #define UQ_LOOP_FLOAT(T) \
920
1183
  do { \
921
1184
  const T *ip = (const T *) p_in; \
922
1185
  for ( ca_size_t i = 0; i < n; i++ ) { \
923
1186
  if ( m && m[i] ) { continue; } \
924
1187
  T v = ip[i]; \
925
- uint64_t key; \
926
- if ( v != v ) { key = (NANKEY); } \
927
- else { \
928
- if ( v == (T) 0 ) { v = (T) 0; } \
929
- UINT bits; \
930
- memcpy(&bits, &v, sizeof(bits)); \
931
- key = (uint64_t) bits; \
932
- } \
1188
+ uint64_t key = FZ_FLOAT_KEY(T, v); \
933
1189
  int is_new; \
934
1190
  fz_hash_intern(&h, key, &is_new); \
935
1191
  if ( is_new ) { fz_levels_push(&lv, &ip[i]); } \
@@ -964,7 +1220,7 @@ rb_ca_unique_flat (VALUE self)
964
1220
  } \
965
1221
  } while (0)
966
1222
 
967
- CA_FOR_EACH_FIBER_MASKED(st_in, ca, axis, CA_KERNEL_READ, p_in, n, m) {
1223
+ CA_FOR_EACH_FIBER_MASKED(*st_in, ca, axis, CA_KERNEL_READ, p_in, n, m) {
968
1224
  switch ( dt ) {
969
1225
  case CA_INT8: UQ_LOOP(int8_t, (int64_t)); break;
970
1226
  case CA_INT16: UQ_LOOP(int16_t, (int64_t)); break;
@@ -974,8 +1230,8 @@ rb_ca_unique_flat (VALUE self)
974
1230
  case CA_UINT16: UQ_LOOP(uint16_t, (uint64_t)); break;
975
1231
  case CA_UINT32: UQ_LOOP(uint32_t, (uint64_t)); break;
976
1232
  case CA_UINT64: UQ_LOOP(uint64_t, (uint64_t)); break;
977
- case CA_FLOAT32: UQ_LOOP_FLOAT(float, uint32_t, 0x7fc00000ULL); break;
978
- case CA_FLOAT64: UQ_LOOP_FLOAT(double, uint64_t, 0x7ff8000000000000ULL); break;
1233
+ case CA_FLOAT32: UQ_LOOP_FLOAT(float); break;
1234
+ case CA_FLOAT64: UQ_LOOP_FLOAT(double); break;
979
1235
  case CA_OBJECT: UQ_LOOP_OBJ; break;
980
1236
  case CA_FIXLEN: UQ_LOOP_MEM; break;
981
1237
  }
@@ -1002,6 +1258,12 @@ rb_ca_unique_flat (VALUE self)
1002
1258
  return fz_face_relift(vlev, face); /* distinct *values*: give the Face back */
1003
1259
  }
1004
1260
 
1261
+ static VALUE
1262
+ rb_ca_unique_flat (VALUE self)
1263
+ {
1264
+ return fz_run(fz_unique_flat_body, self, Qnil, Qnil, 0);
1265
+ }
1266
+
1005
1267
  /* Intern every non-masked cell of cv into h, one seen-set over the whole array
1006
1268
  (the hash is not reset between fibers). When lv is non-NULL, the first-seen
1007
1269
  raw element of each distinct value is pushed to it (appearance order), so the
@@ -1011,11 +1273,10 @@ rb_ca_unique_flat (VALUE self)
1011
1273
  fixlen byte-hash + memcmp. Used to build the probe set (is_in, set relations)
1012
1274
  and to accumulate distinct values (union). */
1013
1275
  static void
1014
- fz_intern_all (fz_hash *h, CArray *cv, fz_levels *lv)
1276
+ fz_intern_all (ca_iter_state *st_v, fz_hash *h, CArray *cv, fz_levels *lv)
1015
1277
  {
1016
1278
  int8_t dt = cv->data_type;
1017
1279
  int8_t vaxis = (int8_t) (cv->ndim - 1);
1018
- ca_iter_state st_v;
1019
1280
  char *p_v;
1020
1281
  boolean8_t *mv;
1021
1282
  ca_size_t nv;
@@ -1032,20 +1293,13 @@ fz_intern_all (fz_hash *h, CArray *cv, fz_levels *lv)
1032
1293
  } \
1033
1294
  } while (0)
1034
1295
 
1035
- #define FZ_IA_FLOAT(T, UINT, NANKEY) \
1296
+ #define FZ_IA_FLOAT(T) \
1036
1297
  do { \
1037
1298
  const T *ip = (const T *) p_v; \
1038
1299
  for ( ca_size_t i = 0; i < nv; i++ ) { \
1039
1300
  if ( mv && mv[i] ) { continue; } \
1040
1301
  T v = ip[i]; \
1041
- uint64_t key; \
1042
- if ( v != v ) { key = (NANKEY); } \
1043
- else { \
1044
- if ( v == (T) 0 ) { v = (T) 0; } \
1045
- UINT bits; \
1046
- memcpy(&bits, &v, sizeof(bits)); \
1047
- key = (uint64_t) bits; \
1048
- } \
1302
+ uint64_t key = FZ_FLOAT_KEY(T, v); \
1049
1303
  int is_new; \
1050
1304
  fz_hash_intern(h, key, &is_new); \
1051
1305
  if ( is_new && lv ) { fz_levels_push(lv, &ip[i]); } \
@@ -1075,7 +1329,7 @@ fz_intern_all (fz_hash *h, CArray *cv, fz_levels *lv)
1075
1329
  } \
1076
1330
  } while (0)
1077
1331
 
1078
- CA_FOR_EACH_FIBER_MASKED(st_v, cv, vaxis, CA_KERNEL_READ, p_v, nv, mv) {
1332
+ CA_FOR_EACH_FIBER_MASKED(*st_v, cv, vaxis, CA_KERNEL_READ, p_v, nv, mv) {
1079
1333
  switch ( dt ) {
1080
1334
  case CA_INT8: FZ_IA(int8_t, (int64_t)); break;
1081
1335
  case CA_INT16: FZ_IA(int16_t, (int64_t)); break;
@@ -1085,8 +1339,8 @@ fz_intern_all (fz_hash *h, CArray *cv, fz_levels *lv)
1085
1339
  case CA_UINT16: FZ_IA(uint16_t, (uint64_t)); break;
1086
1340
  case CA_UINT32: FZ_IA(uint32_t, (uint64_t)); break;
1087
1341
  case CA_UINT64: FZ_IA(uint64_t, (uint64_t)); break;
1088
- case CA_FLOAT32: FZ_IA_FLOAT(float, uint32_t, 0x7fc00000ULL); break;
1089
- case CA_FLOAT64: FZ_IA_FLOAT(double, uint64_t, 0x7ff8000000000000ULL); break;
1342
+ case CA_FLOAT32: FZ_IA_FLOAT(float); break;
1343
+ case CA_FLOAT64: FZ_IA_FLOAT(double); break;
1090
1344
  case CA_OBJECT: FZ_IA_OBJ; break;
1091
1345
  case CA_FIXLEN: FZ_IA_MEM; break;
1092
1346
  }
@@ -1115,8 +1369,11 @@ fz_intern_all (fz_hash *h, CArray *cv, fz_levels *lv)
1115
1369
  fixlen byte equality (byte-hash + memcmp).
1116
1370
  */
1117
1371
  static VALUE
1118
- rb_ca_is_in (VALUE self, VALUE rvalues)
1372
+ fz_is_in_body (VALUE arg)
1119
1373
  {
1374
+ fz_frame *fr = (fz_frame *) arg;
1375
+ VALUE self = fr->self;
1376
+ VALUE rvalues = fr->a;
1120
1377
  CArray *ca, *cv;
1121
1378
  volatile VALUE face;
1122
1379
  self = fz_face_descend(self, &face, "is_in");
@@ -1145,12 +1402,12 @@ rb_ca_is_in (VALUE self, VALUE rvalues)
1145
1402
  GetCArray(rvalues, cv);
1146
1403
  if ( cv->data_type != dt ) {
1147
1404
  rb_raise(rb_eCADataTypeError,
1148
- "__is_in__: values data type (%s) must match self (%s)",
1405
+ "is_in: values data type (%s) must match self (%s)",
1149
1406
  ca_type_name[cv->data_type], ca_type_name[dt]);
1150
1407
  }
1151
1408
  if ( dt == CA_FIXLEN && cv->bytes != ca->bytes ) {
1152
1409
  rb_raise(rb_eCADataTypeError,
1153
- "__is_in__: a fixlen value is a cell-width blob, so the values "
1410
+ "is_in: a fixlen value is a cell-width blob, so the values "
1154
1411
  "must be %ld bytes wide like self, not %ld",
1155
1412
  (long) ca->bytes, (long) cv->bytes);
1156
1413
  }
@@ -1164,14 +1421,14 @@ rb_ca_is_in (VALUE self, VALUE rvalues)
1164
1421
  else { fz_hash_init(&h); }
1165
1422
 
1166
1423
  /* ---- Phase 1: build the seen-set from every non-masked cell of `values`. */
1167
- fz_intern_all(&h, cv, NULL);
1424
+ fz_intern_all(&fr->st[2], &h, cv, NULL);
1168
1425
 
1169
1426
  /* ---- Phase 2: probe every cell of self, writing the boolean membership. */
1170
1427
  VALUE vout = rb_carray_new(CA_BOOLEAN, ca->ndim, ca->dim, 0, NULL);
1171
1428
  CArray *cout;
1172
1429
  GetCArray(vout, cout);
1173
1430
  {
1174
- ca_iter_state st_in, st_out;
1431
+ ca_iter_state *st_in = &fr->st[0], *st_out = &fr->st[1];
1175
1432
  char *p_in, *p_out;
1176
1433
  boolean8_t *m;
1177
1434
  ca_size_t n;
@@ -1188,21 +1445,14 @@ rb_ca_is_in (VALUE self, VALUE rvalues)
1188
1445
  } \
1189
1446
  } while (0)
1190
1447
 
1191
- #define IN_PROBE_FLOAT(T, UINT, NANKEY) \
1448
+ #define IN_PROBE_FLOAT(T) \
1192
1449
  do { \
1193
1450
  const T *ip = (const T *) p_in; \
1194
1451
  boolean8_t *op = (boolean8_t *) p_out; \
1195
1452
  for ( ca_size_t i = 0; i < n; i++ ) { \
1196
1453
  if ( m && m[i] ) { op[i] = 0; continue; } \
1197
1454
  T v = ip[i]; \
1198
- uint64_t key; \
1199
- if ( v != v ) { key = (NANKEY); } \
1200
- else { \
1201
- if ( v == (T) 0 ) { v = (T) 0; } \
1202
- UINT bits; \
1203
- memcpy(&bits, &v, sizeof(bits)); \
1204
- key = (uint64_t) bits; \
1205
- } \
1455
+ uint64_t key = FZ_FLOAT_KEY(T, v); \
1206
1456
  op[i] = (boolean8_t) fz_hash_lookup(&h, key, NULL); \
1207
1457
  } \
1208
1458
  } while (0)
@@ -1227,7 +1477,7 @@ rb_ca_is_in (VALUE self, VALUE rvalues)
1227
1477
  } \
1228
1478
  } while (0)
1229
1479
 
1230
- CA_FOR_EACH_FIBER_INOUT_MASKED(st_in, st_out, ca, cout, axis,
1480
+ CA_FOR_EACH_FIBER_INOUT_MASKED(*st_in, *st_out, ca, cout, axis,
1231
1481
  CA_KERNEL_READ, p_in, p_out, n, m) {
1232
1482
  switch ( dt ) {
1233
1483
  case CA_INT8: IN_PROBE(int8_t, (int64_t)); break;
@@ -1238,8 +1488,8 @@ rb_ca_is_in (VALUE self, VALUE rvalues)
1238
1488
  case CA_UINT16: IN_PROBE(uint16_t, (uint64_t)); break;
1239
1489
  case CA_UINT32: IN_PROBE(uint32_t, (uint64_t)); break;
1240
1490
  case CA_UINT64: IN_PROBE(uint64_t, (uint64_t)); break;
1241
- case CA_FLOAT32: IN_PROBE_FLOAT(float, uint32_t, 0x7fc00000ULL); break;
1242
- case CA_FLOAT64: IN_PROBE_FLOAT(double, uint64_t, 0x7ff8000000000000ULL); break;
1491
+ case CA_FLOAT32: IN_PROBE_FLOAT(float); break;
1492
+ case CA_FLOAT64: IN_PROBE_FLOAT(double); break;
1243
1493
  case CA_OBJECT: IN_PROBE_OBJ; break;
1244
1494
  case CA_FIXLEN: IN_PROBE_MEM; break;
1245
1495
  }
@@ -1259,6 +1509,12 @@ rb_ca_is_in (VALUE self, VALUE rvalues)
1259
1509
  return vout;
1260
1510
  }
1261
1511
 
1512
+ static VALUE
1513
+ rb_ca_is_in (VALUE self, VALUE rvalues)
1514
+ {
1515
+ return fz_run(fz_is_in_body, self, rvalues, Qnil, 0);
1516
+ }
1517
+
1262
1518
  /* @overload __locate_addr__(ref)
1263
1519
 
1264
1520
  INTERNAL (CArray#locate_addr, exact hash lane). For each cell of self, the
@@ -1276,8 +1532,11 @@ rb_ca_is_in (VALUE self, VALUE rvalues)
1276
1532
  appearance order (matching the discovery family); on a `ref` with duplicate
1277
1533
  values this is the earliest address, which can differ from the bsearch lane. */
1278
1534
  static VALUE
1279
- rb_ca_locate_addr (VALUE self, VALUE rref)
1535
+ fz_locate_addr_body (VALUE arg)
1280
1536
  {
1537
+ fz_frame *fr = (fz_frame *) arg;
1538
+ VALUE self = fr->self;
1539
+ VALUE rref = fr->a;
1281
1540
  CArray *ca, *cr;
1282
1541
  /* Here the *reference* is the argument (self is the query being placed on
1283
1542
  rref), so the gate runs the other way round: rref reconciles self. The
@@ -1308,9 +1567,16 @@ rb_ca_locate_addr (VALUE self, VALUE rref)
1308
1567
  rb_raise(rb_eArgError, "__locate_addr__: ref must be a CArray");
1309
1568
  }
1310
1569
  GetCArray(rref, cr);
1311
- if ( cr->data_type != dt || (dt == CA_FIXLEN && cr->bytes != ca->bytes) ) {
1570
+ if ( cr->data_type != dt ) {
1312
1571
  rb_raise(rb_eCADataTypeError,
1313
- "__locate_addr__: ref data type must match self (%d)", dt);
1572
+ "locate_addr: ref data type (%s) must match self (%s)",
1573
+ ca_type_name[cr->data_type], ca_type_name[dt]);
1574
+ }
1575
+ if ( dt == CA_FIXLEN && cr->bytes != ca->bytes ) {
1576
+ rb_raise(rb_eCADataTypeError,
1577
+ "locate_addr: a fixlen value is a cell-width blob, so ref "
1578
+ "must be %ld bytes wide like self, not %ld",
1579
+ (long) ca->bytes, (long) cr->bytes);
1314
1580
  }
1315
1581
  if ( cr->ndim < 1 ) {
1316
1582
  rb_raise(rb_eRuntimeError, "__locate_addr__: ref need ndim >= 1");
@@ -1325,11 +1591,11 @@ rb_ca_locate_addr (VALUE self, VALUE rref)
1325
1591
  the ref value carrying dense code `code`. Codes are assigned 0,1,2,... in
1326
1592
  insertion order, so pushing on is_new fills addr.p in code order. */
1327
1593
  fz_levels addr;
1328
- fz_levels_init(&addr, (int) sizeof(int64_t));
1594
+ fz_levels_init(&addr, (int) sizeof(int64_t), 0);
1329
1595
 
1330
1596
  /* ---- Phase 1: build value -> first-address map from ref (row-major). ----- */
1331
1597
  {
1332
- ca_iter_state st_r;
1598
+ ca_iter_state *st_r = &fr->st[0];
1333
1599
  char *p_r;
1334
1600
  boolean8_t *mr;
1335
1601
  ca_size_t nr;
@@ -1349,20 +1615,13 @@ rb_ca_locate_addr (VALUE self, VALUE rref)
1349
1615
  } \
1350
1616
  } while (0)
1351
1617
 
1352
- #define LOC_BUILD_FLOAT(T, UINT, NANKEY) \
1618
+ #define LOC_BUILD_FLOAT(T) \
1353
1619
  do { \
1354
1620
  const T *ip = (const T *) p_r; \
1355
1621
  for ( ca_size_t i = 0; i < nr; i++ ) { \
1356
1622
  if ( mr && mr[i] ) { continue; } \
1357
1623
  T v = ip[i]; \
1358
- uint64_t key; \
1359
- if ( v != v ) { key = (NANKEY); } \
1360
- else { \
1361
- if ( v == (T) 0 ) { v = (T) 0; } \
1362
- UINT bits; \
1363
- memcpy(&bits, &v, sizeof(bits)); \
1364
- key = (uint64_t) bits; \
1365
- } \
1624
+ uint64_t key = FZ_FLOAT_KEY(T, v); \
1366
1625
  int is_new; \
1367
1626
  fz_hash_intern(&h, key, &is_new); \
1368
1627
  if ( is_new ) { int64_t a = (int64_t) (base + i); \
@@ -1394,7 +1653,7 @@ rb_ca_locate_addr (VALUE self, VALUE rref)
1394
1653
  } \
1395
1654
  } while (0)
1396
1655
 
1397
- CA_FOR_EACH_FIBER_MASKED(st_r, cr, raxis, CA_KERNEL_READ, p_r, nr, mr) {
1656
+ CA_FOR_EACH_FIBER_MASKED(*st_r, cr, raxis, CA_KERNEL_READ, p_r, nr, mr) {
1398
1657
  switch ( dt ) {
1399
1658
  case CA_INT8: LOC_BUILD(int8_t, (int64_t)); break;
1400
1659
  case CA_INT16: LOC_BUILD(int16_t, (int64_t)); break;
@@ -1404,8 +1663,8 @@ rb_ca_locate_addr (VALUE self, VALUE rref)
1404
1663
  case CA_UINT16: LOC_BUILD(uint16_t, (uint64_t)); break;
1405
1664
  case CA_UINT32: LOC_BUILD(uint32_t, (uint64_t)); break;
1406
1665
  case CA_UINT64: LOC_BUILD(uint64_t, (uint64_t)); break;
1407
- case CA_FLOAT32: LOC_BUILD_FLOAT(float, uint32_t, 0x7fc00000ULL); break;
1408
- case CA_FLOAT64: LOC_BUILD_FLOAT(double, uint64_t, 0x7ff8000000000000ULL); break;
1666
+ case CA_FLOAT32: LOC_BUILD_FLOAT(float); break;
1667
+ case CA_FLOAT64: LOC_BUILD_FLOAT(double); break;
1409
1668
  case CA_OBJECT: LOC_BUILD_OBJ; break;
1410
1669
  case CA_FIXLEN: LOC_BUILD_MEM; break;
1411
1670
  }
@@ -1422,10 +1681,11 @@ rb_ca_locate_addr (VALUE self, VALUE rref)
1422
1681
  CArray *cout;
1423
1682
  GetCArray(vout, cout);
1424
1683
  int64_t *out = (int64_t *) cout->ptr;
1425
- boolean8_t *um = ALLOC_N(boolean8_t, ca->elements); /* undef flags scratch */
1684
+ volatile VALUE hum = 0;
1685
+ boolean8_t *um = ALLOCV_N(boolean8_t, hum, ca->elements); /* undef flags scratch */
1426
1686
  ca_size_t n_undef = 0;
1427
1687
  {
1428
- ca_iter_state st_in;
1688
+ ca_iter_state *st_in = &fr->st[1];
1429
1689
  char *p_in;
1430
1690
  boolean8_t *m;
1431
1691
  ca_size_t n;
@@ -1446,7 +1706,7 @@ rb_ca_locate_addr (VALUE self, VALUE rref)
1446
1706
  } \
1447
1707
  } while (0)
1448
1708
 
1449
- #define LOC_PROBE_FLOAT(T, UINT, NANKEY) \
1709
+ #define LOC_PROBE_FLOAT(T) \
1450
1710
  do { \
1451
1711
  const T *ip = (const T *) p_in; \
1452
1712
  const int64_t *ab = (const int64_t *) addr.p; \
@@ -1454,14 +1714,7 @@ rb_ca_locate_addr (VALUE self, VALUE rref)
1454
1714
  ca_size_t o = base + i; \
1455
1715
  if ( m && m[i] ) { out[o] = 0; um[o] = 1; n_undef++; continue; } \
1456
1716
  T v = ip[i]; \
1457
- uint64_t key; \
1458
- if ( v != v ) { key = (NANKEY); } \
1459
- else { \
1460
- if ( v == (T) 0 ) { v = (T) 0; } \
1461
- UINT bits; \
1462
- memcpy(&bits, &v, sizeof(bits)); \
1463
- key = (uint64_t) bits; \
1464
- } \
1717
+ uint64_t key = FZ_FLOAT_KEY(T, v); \
1465
1718
  int32_t code; \
1466
1719
  if ( fz_hash_lookup(&h, key, &code) ) { out[o] = ab[code]; um[o] = 0; } \
1467
1720
  else { out[o] = 0; um[o] = 1; n_undef++; } \
@@ -1494,7 +1747,7 @@ rb_ca_locate_addr (VALUE self, VALUE rref)
1494
1747
  } \
1495
1748
  } while (0)
1496
1749
 
1497
- CA_FOR_EACH_FIBER_MASKED(st_in, ca, axis, CA_KERNEL_READ, p_in, n, m) {
1750
+ CA_FOR_EACH_FIBER_MASKED(*st_in, ca, axis, CA_KERNEL_READ, p_in, n, m) {
1498
1751
  switch ( dt ) {
1499
1752
  case CA_INT8: LOC_PROBE(int8_t, (int64_t)); break;
1500
1753
  case CA_INT16: LOC_PROBE(int16_t, (int64_t)); break;
@@ -1504,8 +1757,8 @@ rb_ca_locate_addr (VALUE self, VALUE rref)
1504
1757
  case CA_UINT16: LOC_PROBE(uint16_t, (uint64_t)); break;
1505
1758
  case CA_UINT32: LOC_PROBE(uint32_t, (uint64_t)); break;
1506
1759
  case CA_UINT64: LOC_PROBE(uint64_t, (uint64_t)); break;
1507
- case CA_FLOAT32: LOC_PROBE_FLOAT(float, uint32_t, 0x7fc00000ULL); break;
1508
- case CA_FLOAT64: LOC_PROBE_FLOAT(double, uint64_t, 0x7ff8000000000000ULL); break;
1760
+ case CA_FLOAT32: LOC_PROBE_FLOAT(float); break;
1761
+ case CA_FLOAT64: LOC_PROBE_FLOAT(double); break;
1509
1762
  case CA_OBJECT: LOC_PROBE_OBJ; break;
1510
1763
  case CA_FIXLEN: LOC_PROBE_MEM; break;
1511
1764
  }
@@ -1526,11 +1779,17 @@ rb_ca_locate_addr (VALUE self, VALUE rref)
1526
1779
  ca_create_mask(cout);
1527
1780
  memcpy(cout->mask->ptr, um, (size_t) ca->elements);
1528
1781
  }
1529
- xfree(um);
1782
+ ALLOCV_END(hum);
1530
1783
 
1531
1784
  return vout;
1532
1785
  }
1533
1786
 
1787
+ static VALUE
1788
+ rb_ca_locate_addr (VALUE self, VALUE rref)
1789
+ {
1790
+ return fz_run(fz_locate_addr_body, self, rref, Qnil, 0);
1791
+ }
1792
+
1534
1793
  /* Shared body of __intersection__ (keep_when_hit = 1) and __difference__
1535
1794
  (keep_when_hit = 0): the distinct values of self that are (resp. are not)
1536
1795
  present in `other`, in self's first-appearance order, as a 1-D CArray of
@@ -1540,8 +1799,12 @@ rb_ca_locate_addr (VALUE self, VALUE rref)
1540
1799
  lane (numeric `==` + NaN collapse + -0.0 == +0.0, object hash/eql? + NaN
1541
1800
  collapse, fixlen byte equality). */
1542
1801
  static VALUE
1543
- fz_set_relation (VALUE self, VALUE rother, int keep_when_hit)
1802
+ fz_set_relation_body (VALUE arg)
1544
1803
  {
1804
+ fz_frame *fr = (fz_frame *) arg;
1805
+ VALUE self = fr->self;
1806
+ VALUE rother = fr->a;
1807
+ int keep_when_hit = fr->i;
1545
1808
  CArray *ca, *co;
1546
1809
  volatile VALUE face;
1547
1810
  self = fz_face_descend(self, &face, "set relation");
@@ -1594,15 +1857,15 @@ fz_set_relation (VALUE self, VALUE rother, int keep_when_hit)
1594
1857
  }
1595
1858
 
1596
1859
  fz_levels lv;
1597
- fz_levels_init(&lv, (int) ca->bytes);
1860
+ fz_levels_init(&lv, (int) ca->bytes, ca->data_type == CA_OBJECT);
1598
1861
 
1599
1862
  /* Build the probe set from every non-masked cell of other. */
1600
- fz_intern_all(&hoth, co, NULL);
1863
+ fz_intern_all(&fr->st[2], &hoth, co, NULL);
1601
1864
 
1602
1865
  /* Walk self: hself dedups, so each distinct self value is decided once; keep
1603
1866
  it when its membership in other equals keep_when_hit. */
1604
1867
  int8_t axis = (int8_t) (ca->ndim - 1);
1605
- ca_iter_state st;
1868
+ ca_iter_state *st = &fr->st[0];
1606
1869
  char *p;
1607
1870
  boolean8_t *m;
1608
1871
  ca_size_t n;
@@ -1621,20 +1884,13 @@ fz_set_relation (VALUE self, VALUE rother, int keep_when_hit)
1621
1884
  } \
1622
1885
  } while (0)
1623
1886
 
1624
- #define SR_FLOAT(T, UINT, NANKEY) \
1887
+ #define SR_FLOAT(T) \
1625
1888
  do { \
1626
1889
  const T *ip = (const T *) p; \
1627
1890
  for ( ca_size_t i = 0; i < n; i++ ) { \
1628
1891
  if ( m && m[i] ) { continue; } \
1629
1892
  T v = ip[i]; \
1630
- uint64_t key; \
1631
- if ( v != v ) { key = (NANKEY); } \
1632
- else { \
1633
- if ( v == (T) 0 ) { v = (T) 0; } \
1634
- UINT bits; \
1635
- memcpy(&bits, &v, sizeof(bits)); \
1636
- key = (uint64_t) bits; \
1637
- } \
1893
+ uint64_t key = FZ_FLOAT_KEY(T, v); \
1638
1894
  int is_new; \
1639
1895
  fz_hash_intern(&hself, key, &is_new); \
1640
1896
  if ( is_new && fz_hash_lookup(&hoth, key, NULL) == keep_when_hit ) { \
@@ -1670,7 +1926,7 @@ fz_set_relation (VALUE self, VALUE rother, int keep_when_hit)
1670
1926
  } \
1671
1927
  } while (0)
1672
1928
 
1673
- CA_FOR_EACH_FIBER_MASKED(st, ca, axis, CA_KERNEL_READ, p, n, m) {
1929
+ CA_FOR_EACH_FIBER_MASKED(*st, ca, axis, CA_KERNEL_READ, p, n, m) {
1674
1930
  switch ( dt ) {
1675
1931
  case CA_INT8: SR(int8_t, (int64_t)); break;
1676
1932
  case CA_INT16: SR(int16_t, (int64_t)); break;
@@ -1680,8 +1936,8 @@ fz_set_relation (VALUE self, VALUE rother, int keep_when_hit)
1680
1936
  case CA_UINT16: SR(uint16_t, (uint64_t)); break;
1681
1937
  case CA_UINT32: SR(uint32_t, (uint64_t)); break;
1682
1938
  case CA_UINT64: SR(uint64_t, (uint64_t)); break;
1683
- case CA_FLOAT32: SR_FLOAT(float, uint32_t, 0x7fc00000ULL); break;
1684
- case CA_FLOAT64: SR_FLOAT(double, uint64_t, 0x7ff8000000000000ULL); break;
1939
+ case CA_FLOAT32: SR_FLOAT(float); break;
1940
+ case CA_FLOAT64: SR_FLOAT(double); break;
1685
1941
  case CA_OBJECT: SR_OBJ; break;
1686
1942
  case CA_FIXLEN: SR_MEM; break;
1687
1943
  }
@@ -1708,6 +1964,12 @@ fz_set_relation (VALUE self, VALUE rother, int keep_when_hit)
1708
1964
  return fz_face_relift(vlev, face); /* set *values*: give the Face back */
1709
1965
  }
1710
1966
 
1967
+ static VALUE
1968
+ fz_set_relation (VALUE self, VALUE rother, int keep_when_hit)
1969
+ {
1970
+ return fz_run(fz_set_relation_body, self, rother, Qnil, keep_when_hit);
1971
+ }
1972
+
1711
1973
  /* @overload __intersection__(other)
1712
1974
  INTERNAL (CArray#intersection). 1-D CArray of the distinct values present in
1713
1975
  both self and other, in self's first-appearance order. */
@@ -1733,8 +1995,11 @@ rb_ca_difference (VALUE self, VALUE rother)
1733
1995
  distinct values first, then other's not-yet-seen ones). Masked cells of
1734
1996
  either array do not participate. */
1735
1997
  static VALUE
1736
- rb_ca_set_union (VALUE self, VALUE rother)
1998
+ fz_set_union_body (VALUE arg)
1737
1999
  {
2000
+ fz_frame *fr = (fz_frame *) arg;
2001
+ VALUE self = fr->self;
2002
+ VALUE rother = fr->a;
1738
2003
  CArray *ca, *co;
1739
2004
  volatile VALUE face;
1740
2005
  self = fz_face_descend(self, &face, "union");
@@ -1760,8 +2025,16 @@ rb_ca_set_union (VALUE self, VALUE rother)
1760
2025
  rb_raise(rb_eArgError, "__union__: other must be a CArray");
1761
2026
  }
1762
2027
  GetCArray(rother, co);
1763
- if ( co->data_type != dt || (dt == CA_FIXLEN && co->bytes != ca->bytes) ) {
1764
- rb_raise(rb_eCADataTypeError, "__union__: other data type must match self (%d)", dt);
2028
+ if ( co->data_type != dt ) {
2029
+ rb_raise(rb_eCADataTypeError,
2030
+ "union: other data type (%s) must match self (%s)",
2031
+ ca_type_name[co->data_type], ca_type_name[dt]);
2032
+ }
2033
+ if ( dt == CA_FIXLEN && co->bytes != ca->bytes ) {
2034
+ rb_raise(rb_eCADataTypeError,
2035
+ "union: a fixlen value is a cell-width blob, so other "
2036
+ "must be %ld bytes wide like self, not %ld",
2037
+ (long) ca->bytes, (long) co->bytes);
1765
2038
  }
1766
2039
  if ( co->ndim < 1 ) {
1767
2040
  rb_raise(rb_eRuntimeError, "__union__: other need ndim >= 1");
@@ -1773,10 +2046,10 @@ rb_ca_set_union (VALUE self, VALUE rother)
1773
2046
  else { fz_hash_init(&h); }
1774
2047
 
1775
2048
  fz_levels lv;
1776
- fz_levels_init(&lv, (int) ca->bytes);
2049
+ fz_levels_init(&lv, (int) ca->bytes, ca->data_type == CA_OBJECT);
1777
2050
 
1778
- fz_intern_all(&h, ca, &lv); /* self's distinct values, appearance order */
1779
- fz_intern_all(&h, co, &lv); /* + other's not-yet-seen distinct values */
2051
+ fz_intern_all(&fr->st[2], &h, ca, &lv); /* self's distinct values, appearance order */
2052
+ fz_intern_all(&fr->st[2], &h, co, &lv); /* + other's not-yet-seen distinct values */
1780
2053
 
1781
2054
  ca_size_t k = lv.n;
1782
2055
  fz_hash_free(&h);
@@ -1794,6 +2067,12 @@ rb_ca_set_union (VALUE self, VALUE rother)
1794
2067
  return fz_face_relift(vlev, face); /* set *values*: give the Face back */
1795
2068
  }
1796
2069
 
2070
+ static VALUE
2071
+ rb_ca_set_union (VALUE self, VALUE rother)
2072
+ {
2073
+ return fz_run(fz_set_union_body, self, rother, Qnil, 0);
2074
+ }
2075
+
1797
2076
  /* @overload __value_counts_flat__
1798
2077
 
1799
2078
  INTERNAL (CArray#value_counts). Collect the distinct values of self in
@@ -1810,8 +2089,10 @@ rb_ca_set_union (VALUE self, VALUE rother)
1810
2089
  collapses every Float NaN to one value (their counts add up, as numeric does).
1811
2090
  */
1812
2091
  static VALUE
1813
- rb_ca_value_counts_flat (VALUE self)
2092
+ fz_value_counts_flat_body (VALUE arg)
1814
2093
  {
2094
+ fz_frame *fr = (fz_frame *) arg;
2095
+ VALUE self = fr->self;
1815
2096
  CArray *ca;
1816
2097
  volatile VALUE face;
1817
2098
  self = fz_face_descend(self, &face, "value_counts");
@@ -1839,11 +2120,11 @@ rb_ca_value_counts_flat (VALUE self)
1839
2120
  if ( dt == CA_OBJECT ) { fz_hash_init_obj(&h); }
1840
2121
  else if ( dt == CA_FIXLEN ) { fz_hash_init_mem(&h, (int) ca->bytes); }
1841
2122
  else { fz_hash_init(&h); }
1842
- fz_levels_init(&lv, (int) ca->bytes);
1843
- fz_levels_init(&ct, (int) sizeof(int64_t));
2123
+ fz_levels_init(&lv, (int) ca->bytes, ca->data_type == CA_OBJECT);
2124
+ fz_levels_init(&ct, (int) sizeof(int64_t), 0);
1844
2125
 
1845
2126
  int8_t axis = (int8_t) (ca->ndim - 1); /* one seen-set over the whole array */
1846
- ca_iter_state st_in;
2127
+ ca_iter_state *st_in = &fr->st[0];
1847
2128
  char *p_in;
1848
2129
  boolean8_t *m;
1849
2130
  ca_size_t n;
@@ -1866,20 +2147,13 @@ rb_ca_value_counts_flat (VALUE self)
1866
2147
  } \
1867
2148
  } while (0)
1868
2149
 
1869
- #define VC_LOOP_FLOAT(T, UINT, NANKEY) \
2150
+ #define VC_LOOP_FLOAT(T) \
1870
2151
  do { \
1871
2152
  const T *ip = (const T *) p_in; \
1872
2153
  for ( ca_size_t i = 0; i < n; i++ ) { \
1873
2154
  if ( m && m[i] ) { continue; } \
1874
2155
  T v = ip[i]; \
1875
- uint64_t key; \
1876
- if ( v != v ) { key = (NANKEY); } \
1877
- else { \
1878
- if ( v == (T) 0 ) { v = (T) 0; } \
1879
- UINT bits; \
1880
- memcpy(&bits, &v, sizeof(bits)); \
1881
- key = (uint64_t) bits; \
1882
- } \
2156
+ uint64_t key = FZ_FLOAT_KEY(T, v); \
1883
2157
  int is_new; \
1884
2158
  int32_t code = fz_hash_intern(&h, key, &is_new); \
1885
2159
  if ( is_new ) { \
@@ -1929,7 +2203,7 @@ rb_ca_value_counts_flat (VALUE self)
1929
2203
  } \
1930
2204
  } while (0)
1931
2205
 
1932
- CA_FOR_EACH_FIBER_MASKED(st_in, ca, axis, CA_KERNEL_READ, p_in, n, m) {
2206
+ CA_FOR_EACH_FIBER_MASKED(*st_in, ca, axis, CA_KERNEL_READ, p_in, n, m) {
1933
2207
  switch ( dt ) {
1934
2208
  case CA_INT8: VC_LOOP(int8_t, (int64_t)); break;
1935
2209
  case CA_INT16: VC_LOOP(int16_t, (int64_t)); break;
@@ -1939,8 +2213,8 @@ rb_ca_value_counts_flat (VALUE self)
1939
2213
  case CA_UINT16: VC_LOOP(uint16_t, (uint64_t)); break;
1940
2214
  case CA_UINT32: VC_LOOP(uint32_t, (uint64_t)); break;
1941
2215
  case CA_UINT64: VC_LOOP(uint64_t, (uint64_t)); break;
1942
- case CA_FLOAT32: VC_LOOP_FLOAT(float, uint32_t, 0x7fc00000ULL); break;
1943
- case CA_FLOAT64: VC_LOOP_FLOAT(double, uint64_t, 0x7ff8000000000000ULL); break;
2216
+ case CA_FLOAT32: VC_LOOP_FLOAT(float); break;
2217
+ case CA_FLOAT64: VC_LOOP_FLOAT(double); break;
1944
2218
  case CA_OBJECT: VC_LOOP_OBJ; break;
1945
2219
  case CA_FIXLEN: VC_LOOP_MEM; break;
1946
2220
  }
@@ -1973,6 +2247,12 @@ rb_ca_value_counts_flat (VALUE self)
1973
2247
  return rb_ary_new3(2, fz_face_relift(vlev, face), vcnt);
1974
2248
  }
1975
2249
 
2250
+ static VALUE
2251
+ rb_ca_value_counts_flat (VALUE self)
2252
+ {
2253
+ return fz_run(fz_value_counts_flat_body, self, Qnil, Qnil, 0);
2254
+ }
2255
+
1976
2256
  /* @overload __nunique__(axis, keep_axis)
1977
2257
 
1978
2258
  INTERNAL (CArray#nunique). Count the distinct values along axis, one linear
@@ -1985,8 +2265,12 @@ rb_ca_value_counts_flat (VALUE self)
1985
2265
  Ruby Hash distinctness; the object lane also collapses every Float NaN to one.
1986
2266
  */
1987
2267
  static VALUE
1988
- rb_ca_nunique (VALUE self, VALUE vaxis, VALUE vkeep)
2268
+ fz_nunique_body (VALUE arg)
1989
2269
  {
2270
+ fz_frame *fr = (fz_frame *) arg;
2271
+ VALUE self = fr->self;
2272
+ VALUE vaxis = fr->a;
2273
+ VALUE vkeep = fr->b;
1990
2274
  CArray *ca;
1991
2275
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1992
2276
 
@@ -2023,8 +2307,8 @@ rb_ca_nunique (VALUE self, VALUE vaxis, VALUE vkeep)
2023
2307
  else if ( dt == CA_FIXLEN ) { fz_hash_init_mem(&h, (int) ca->bytes); }
2024
2308
  else { fz_hash_init(&h); }
2025
2309
 
2026
- ca_iter_state st;
2027
- int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES, &ax, 1, 0);
2310
+ ca_iter_state *st = &fr->st[0];
2311
+ int rc = ca_iter_state_init_l2(st, ca, CA_SLAB_AXES, &ax, 1, 0);
2028
2312
  if ( rc != CA_ITER_OK ) {
2029
2313
  fz_hash_free(&h);
2030
2314
  rb_raise(rb_eRuntimeError, "__nunique__: kernel_iterator init failed rc=%d", rc);
@@ -2040,17 +2324,10 @@ rb_ca_nunique (VALUE self, VALUE vaxis, VALUE vkeep)
2040
2324
  -0.0 before the bitwise key. */
2041
2325
  #define NU_INT(WIDEN) \
2042
2326
  do { int _n; fz_hash_intern(&h, (uint64_t) ((WIDEN) v), &_n); } while (0)
2043
- #define NU_FLOAT(T, UINT, NANKEY) \
2327
+ #define NU_FLOAT(T) \
2044
2328
  do { \
2045
2329
  T _v = v; \
2046
- uint64_t _key; \
2047
- if ( _v != _v ) { _key = (NANKEY); } \
2048
- else { \
2049
- if ( _v == (T) 0 ) { _v = (T) 0; } \
2050
- UINT _b; \
2051
- memcpy(&_b, &_v, sizeof(_b)); \
2052
- _key = (uint64_t) _b; \
2053
- } \
2330
+ uint64_t _key = FZ_FLOAT_KEY(T, _v); \
2054
2331
  int _n; fz_hash_intern(&h, _key, &_n); \
2055
2332
  } while (0)
2056
2333
  /* Object: intern the VALUE via the object lane (v is bound by the macro). */
@@ -2062,43 +2339,43 @@ rb_ca_nunique (VALUE self, VALUE vaxis, VALUE vkeep)
2062
2339
  each esz-wide element via the fixlen lane. */
2063
2340
  #define NU_MEM_WALK \
2064
2341
  do { \
2065
- int8_t K = st.slab_ndim; \
2342
+ int8_t K = st->slab_ndim; \
2066
2343
  ca_size_t idx[CA_RANK_MAX] = { 0 }; \
2067
- ca_size_t total = st.slab_elements; \
2344
+ ca_size_t total = st->slab_elements; \
2068
2345
  for ( ca_size_t e = 0; e < total; e++ ) { \
2069
2346
  ca_size_t doff = 0, moff = 0; \
2070
2347
  for ( int8_t kk = 0; kk < K; kk++ ) { \
2071
- doff += idx[kk] * st.slab_strides[kk]; \
2072
- moff += idx[kk] * st.slab_mask_strides[kk]; \
2348
+ doff += idx[kk] * st->slab_strides[kk]; \
2349
+ moff += idx[kk] * st->slab_mask_strides[kk]; \
2073
2350
  } \
2074
2351
  if ( ! (m && m[moff]) ) { \
2075
2352
  int _n; fz_hash_intern_mem(&h, p + doff, &_n); \
2076
2353
  } \
2077
2354
  for ( int8_t kk = (int8_t)(K - 1); kk >= 0; kk-- ) { \
2078
- if ( ++idx[kk] < st.slab_dims[kk] ) break; \
2355
+ if ( ++idx[kk] < st->slab_dims[kk] ) break; \
2079
2356
  idx[kk] = 0; \
2080
2357
  } \
2081
2358
  } \
2082
2359
  } while (0)
2083
2360
 
2084
- while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
2361
+ while ( ca_iter_state_next_slab_axes(st, &p, &m) ) {
2085
2362
  fz_hash_reset(&h); /* independent seen-set per fiber */
2086
2363
  switch ( dt ) {
2087
- case CA_INT8: CA_SLAB_REDUCE_T(int8_t, st, p, m, dummy, 0, NU_INT(int64_t)); break;
2088
- case CA_INT16: CA_SLAB_REDUCE_T(int16_t, st, p, m, dummy, 0, NU_INT(int64_t)); break;
2089
- case CA_INT32: CA_SLAB_REDUCE_T(int32_t, st, p, m, dummy, 0, NU_INT(int64_t)); break;
2090
- case CA_INT64: CA_SLAB_REDUCE_T(int64_t, st, p, m, dummy, 0, NU_INT(int64_t)); break;
2091
- case CA_BOOLEAN: case CA_UINT8: CA_SLAB_REDUCE_T(uint8_t, st, p, m, dummy, 0, NU_INT(uint64_t)); break;
2092
- case CA_UINT16: CA_SLAB_REDUCE_T(uint16_t, st, p, m, dummy, 0, NU_INT(uint64_t)); break;
2093
- case CA_UINT32: CA_SLAB_REDUCE_T(uint32_t, st, p, m, dummy, 0, NU_INT(uint64_t)); break;
2094
- case CA_UINT64: CA_SLAB_REDUCE_T(uint64_t, st, p, m, dummy, 0, NU_INT(uint64_t)); break;
2364
+ case CA_INT8: CA_SLAB_REDUCE_T(int8_t, *st, p, m, dummy, 0, NU_INT(int64_t)); break;
2365
+ case CA_INT16: CA_SLAB_REDUCE_T(int16_t, *st, p, m, dummy, 0, NU_INT(int64_t)); break;
2366
+ case CA_INT32: CA_SLAB_REDUCE_T(int32_t, *st, p, m, dummy, 0, NU_INT(int64_t)); break;
2367
+ case CA_INT64: CA_SLAB_REDUCE_T(int64_t, *st, p, m, dummy, 0, NU_INT(int64_t)); break;
2368
+ case CA_BOOLEAN: case CA_UINT8: CA_SLAB_REDUCE_T(uint8_t, *st, p, m, dummy, 0, NU_INT(uint64_t)); break;
2369
+ case CA_UINT16: CA_SLAB_REDUCE_T(uint16_t, *st, p, m, dummy, 0, NU_INT(uint64_t)); break;
2370
+ case CA_UINT32: CA_SLAB_REDUCE_T(uint32_t, *st, p, m, dummy, 0, NU_INT(uint64_t)); break;
2371
+ case CA_UINT64: CA_SLAB_REDUCE_T(uint64_t, *st, p, m, dummy, 0, NU_INT(uint64_t)); break;
2095
2372
  case CA_FLOAT32:
2096
- CA_SLAB_REDUCE_T(float, st, p, m, dummy, 0, NU_FLOAT(float, uint32_t, 0x7fc00000ULL));
2373
+ CA_SLAB_REDUCE_T(float, *st, p, m, dummy, 0, NU_FLOAT(float));
2097
2374
  break;
2098
2375
  case CA_FLOAT64:
2099
- CA_SLAB_REDUCE_T(double, st, p, m, dummy, 0, NU_FLOAT(double, uint64_t, 0x7ff8000000000000ULL));
2376
+ CA_SLAB_REDUCE_T(double, *st, p, m, dummy, 0, NU_FLOAT(double));
2100
2377
  break;
2101
- case CA_OBJECT: CA_SLAB_REDUCE_T(VALUE, st, p, m, dummy, 0, NU_OBJ); break;
2378
+ case CA_OBJECT: CA_SLAB_REDUCE_T(VALUE, *st, p, m, dummy, 0, NU_OBJ); break;
2102
2379
  case CA_FIXLEN: NU_MEM_WALK; break;
2103
2380
  }
2104
2381
  op[out_i++] = (int64_t) h.n;
@@ -2109,11 +2386,17 @@ rb_ca_nunique (VALUE self, VALUE vaxis, VALUE vkeep)
2109
2386
  #undef NU_MEM_WALK
2110
2387
  (void) dummy;
2111
2388
 
2112
- ca_iter_state_finish(&st);
2389
+ ca_iter_state_finish(st);
2113
2390
  fz_hash_free(&h);
2114
2391
  return vout;
2115
2392
  }
2116
2393
 
2394
+ static VALUE
2395
+ rb_ca_nunique (VALUE self, VALUE vaxis, VALUE vkeep)
2396
+ {
2397
+ return fz_run(fz_nunique_body, fz_complex_as_key(self), vaxis, vkeep, 0);
2398
+ }
2399
+
2117
2400
  /* @overload __is_mode__(axis)
2118
2401
 
2119
2402
  INTERNAL (CArray#is_mode). Mark each cell that holds a modal value along axis
@@ -2132,8 +2415,11 @@ rb_ca_nunique (VALUE self, VALUE vaxis, VALUE vkeep)
2132
2415
  Callers pass flat input by flattening first (a single fiber over axis 0).
2133
2416
  */
2134
2417
  static VALUE
2135
- rb_ca_is_mode (VALUE self, VALUE vaxis)
2418
+ fz_is_mode_body (VALUE arg)
2136
2419
  {
2420
+ fz_frame *fr = (fz_frame *) arg;
2421
+ VALUE self = fr->self;
2422
+ VALUE vaxis = fr->a;
2137
2423
  CArray *ca;
2138
2424
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
2139
2425
 
@@ -2166,9 +2452,9 @@ rb_ca_is_mode (VALUE self, VALUE vaxis)
2166
2452
  if ( dt == CA_OBJECT ) { fz_hash_init_obj(&h); }
2167
2453
  else if ( dt == CA_FIXLEN ) { fz_hash_init_mem(&h, (int) ca->bytes); }
2168
2454
  else { fz_hash_init(&h); }
2169
- fz_levels_init(&ct, (int) sizeof(int64_t));
2455
+ fz_levels_init(&ct, (int) sizeof(int64_t), 0);
2170
2456
 
2171
- ca_iter_state st_in, st_out;
2457
+ ca_iter_state *st_in = &fr->st[0], *st_out = &fr->st[1];
2172
2458
  char *p_in, *p_out;
2173
2459
  boolean8_t *m;
2174
2460
  ca_size_t n;
@@ -2201,7 +2487,7 @@ rb_ca_is_mode (VALUE self, VALUE vaxis)
2201
2487
  } while (0)
2202
2488
 
2203
2489
  /* Float: NaN collapses to one canonical key; -0.0 / +0.0 share a key. */
2204
- #define IM_BODY_FLOAT(T, UINT, NANKEY) \
2490
+ #define IM_BODY_FLOAT(T) \
2205
2491
  do { \
2206
2492
  const T *ip = (const T *) p_in; \
2207
2493
  boolean8_t *op = (boolean8_t *) p_out; \
@@ -2216,14 +2502,7 @@ rb_ca_is_mode (VALUE self, VALUE vaxis)
2216
2502
  for ( ca_size_t i = 0; i < n; i++ ) { \
2217
2503
  if ( m && m[i] ) { if ( pass == 1 ) { op[i] = 0; } continue; } \
2218
2504
  T v = ip[i]; \
2219
- uint64_t key; \
2220
- if ( v != v ) { key = (NANKEY); } \
2221
- else { \
2222
- if ( v == (T) 0 ) { v = (T) 0; } \
2223
- UINT bits; \
2224
- memcpy(&bits, &v, sizeof(bits)); \
2225
- key = (uint64_t) bits; \
2226
- } \
2505
+ uint64_t key = FZ_FLOAT_KEY(T, v); \
2227
2506
  int is_new; \
2228
2507
  int32_t code = fz_hash_intern(&h, key, &is_new); \
2229
2508
  if ( pass == 0 ) { \
@@ -2287,7 +2566,7 @@ rb_ca_is_mode (VALUE self, VALUE vaxis)
2287
2566
  } \
2288
2567
  } while (0)
2289
2568
 
2290
- CA_FOR_EACH_FIBER_INOUT_MASKED(st_in, st_out, ca, cout, (int8_t) axis,
2569
+ CA_FOR_EACH_FIBER_INOUT_MASKED(*st_in, *st_out, ca, cout, (int8_t) axis,
2291
2570
  CA_KERNEL_READ, p_in, p_out, n, m) {
2292
2571
  fz_hash_reset(&h); /* independent frequency table per fiber */
2293
2572
  ct.n = 0;
@@ -2300,8 +2579,8 @@ rb_ca_is_mode (VALUE self, VALUE vaxis)
2300
2579
  case CA_UINT16: IM_BODY(uint16_t, (uint64_t)); break;
2301
2580
  case CA_UINT32: IM_BODY(uint32_t, (uint64_t)); break;
2302
2581
  case CA_UINT64: IM_BODY(uint64_t, (uint64_t)); break;
2303
- case CA_FLOAT32: IM_BODY_FLOAT(float, uint32_t, 0x7fc00000ULL); break;
2304
- case CA_FLOAT64: IM_BODY_FLOAT(double, uint64_t, 0x7ff8000000000000ULL); break;
2582
+ case CA_FLOAT32: IM_BODY_FLOAT(float); break;
2583
+ case CA_FLOAT64: IM_BODY_FLOAT(double); break;
2305
2584
  case CA_OBJECT: IM_BODY_OBJ; break;
2306
2585
  case CA_FIXLEN: IM_BODY_MEM; break;
2307
2586
  }
@@ -2316,6 +2595,12 @@ rb_ca_is_mode (VALUE self, VALUE vaxis)
2316
2595
  return vout;
2317
2596
  }
2318
2597
 
2598
+ static VALUE
2599
+ rb_ca_is_mode (VALUE self, VALUE vaxis)
2600
+ {
2601
+ return fz_run(fz_is_mode_body, fz_complex_as_key(self), vaxis, Qnil, 0);
2602
+ }
2603
+
2319
2604
  /* Ascending comparison for the modal-value sort, NaN ordered last so a
2320
2605
  collapsed NaN key (at most one per fiber) trails the real values, matching
2321
2606
  CArray#sort. */
@@ -2349,8 +2634,11 @@ static int mode_gt_f64 (double a, double b) { if ( a != a ) return b == b; if (
2349
2634
  its sign), matching the discovery family.
2350
2635
  */
2351
2636
  static VALUE
2352
- rb_ca_mode_axis (VALUE self, VALUE vaxis)
2637
+ fz_mode_axis_body (VALUE arg)
2353
2638
  {
2639
+ fz_frame *fr = (fz_frame *) arg;
2640
+ VALUE self = fr->self;
2641
+ VALUE vaxis = fr->a;
2354
2642
  CArray *ca;
2355
2643
  volatile VALUE face;
2356
2644
  self = fz_face_descend(self, &face, "mode");
@@ -2387,20 +2675,21 @@ rb_ca_mode_axis (VALUE self, VALUE vaxis)
2387
2675
  fz_levels mod; /* per-fiber modal values, collected then sorted (reset) */
2388
2676
  fz_levels flat; /* all fibers' modal values concatenated, fiber order */
2389
2677
  fz_hash_init(&h);
2390
- fz_levels_init(&lv, esz);
2391
- fz_levels_init(&ct, (int) sizeof(int64_t));
2392
- fz_levels_init(&mod, esz);
2393
- fz_levels_init(&flat, esz);
2678
+ fz_levels_init(&lv, esz, 0);
2679
+ fz_levels_init(&ct, (int) sizeof(int64_t), 0);
2680
+ fz_levels_init(&mod, esz, 0);
2681
+ fz_levels_init(&flat, esz, 0);
2394
2682
 
2395
- ca_size_t *foff = ALLOC_N(ca_size_t, M + 1); /* prefix offsets into flat */
2683
+ volatile VALUE hfoff = 0;
2684
+ ca_size_t *foff = ALLOCV_N(ca_size_t, hfoff, M + 1); /* prefix offsets into flat */
2396
2685
  foff[0] = 0;
2397
2686
 
2398
2687
  int8_t ax = (int8_t) axis;
2399
- ca_iter_state st;
2400
- int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES, &ax, 1, 0);
2688
+ ca_iter_state *st = &fr->st[0];
2689
+ int rc = ca_iter_state_init_l2(st, ca, CA_SLAB_AXES, &ax, 1, 0);
2401
2690
  if ( rc != CA_ITER_OK ) {
2402
2691
  fz_hash_free(&h); fz_levels_free(&lv); fz_levels_free(&ct);
2403
- fz_levels_free(&mod); fz_levels_free(&flat); xfree(foff);
2692
+ fz_levels_free(&mod); fz_levels_free(&flat); ALLOCV_END(hfoff);
2404
2693
  rb_raise(rb_eRuntimeError, "__mode_axis__: kernel_iterator init failed rc=%d", rc);
2405
2694
  }
2406
2695
 
@@ -2421,12 +2710,9 @@ rb_ca_mode_axis (VALUE self, VALUE vaxis)
2421
2710
  fz_levels_push(&ct, &one); } \
2422
2711
  else { ((int64_t *) ct.p)[code]++; } \
2423
2712
  } while (0)
2424
- #define MB_FLOAT(T, UINT, NANKEY) \
2713
+ #define MB_FLOAT(T) \
2425
2714
  do { \
2426
- T vn = v; uint64_t key; \
2427
- if ( vn != vn ) { key = (NANKEY); } \
2428
- else { if ( vn == (T) 0 ) { vn = (T) 0; } \
2429
- UINT bits; memcpy(&bits, &vn, sizeof(bits)); key = (uint64_t) bits; } \
2715
+ uint64_t key = FZ_FLOAT_KEY(T, v); \
2430
2716
  int is_new; int32_t code = fz_hash_intern(&h, key, &is_new); \
2431
2717
  if ( is_new ) { int64_t one = 1; fz_levels_push(&lv, &v); \
2432
2718
  fz_levels_push(&ct, &one); } \
@@ -2450,24 +2736,24 @@ rb_ca_mode_axis (VALUE self, VALUE vaxis)
2450
2736
  for ( ca_size_t x = 0; x < cm; x++ ) { fz_levels_push(&flat, &dp[x]); } \
2451
2737
  } while (0)
2452
2738
 
2453
- while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) {
2739
+ while ( ca_iter_state_next_slab_axes(st, &p, &m) ) {
2454
2740
  fz_hash_reset(&h);
2455
2741
  lv.n = 0; ct.n = 0; mod.n = 0;
2456
2742
 
2457
2743
  switch ( dt ) {
2458
- case CA_INT8: CA_SLAB_REDUCE_T(int8_t, st, p, m, dummy, 0, MB_INT(int64_t)); break;
2459
- case CA_INT16: CA_SLAB_REDUCE_T(int16_t, st, p, m, dummy, 0, MB_INT(int64_t)); break;
2460
- case CA_INT32: CA_SLAB_REDUCE_T(int32_t, st, p, m, dummy, 0, MB_INT(int64_t)); break;
2461
- case CA_INT64: CA_SLAB_REDUCE_T(int64_t, st, p, m, dummy, 0, MB_INT(int64_t)); break;
2462
- case CA_BOOLEAN: case CA_UINT8: CA_SLAB_REDUCE_T(uint8_t, st, p, m, dummy, 0, MB_INT(uint64_t)); break;
2463
- case CA_UINT16: CA_SLAB_REDUCE_T(uint16_t, st, p, m, dummy, 0, MB_INT(uint64_t)); break;
2464
- case CA_UINT32: CA_SLAB_REDUCE_T(uint32_t, st, p, m, dummy, 0, MB_INT(uint64_t)); break;
2465
- case CA_UINT64: CA_SLAB_REDUCE_T(uint64_t, st, p, m, dummy, 0, MB_INT(uint64_t)); break;
2744
+ case CA_INT8: CA_SLAB_REDUCE_T(int8_t, *st, p, m, dummy, 0, MB_INT(int64_t)); break;
2745
+ case CA_INT16: CA_SLAB_REDUCE_T(int16_t, *st, p, m, dummy, 0, MB_INT(int64_t)); break;
2746
+ case CA_INT32: CA_SLAB_REDUCE_T(int32_t, *st, p, m, dummy, 0, MB_INT(int64_t)); break;
2747
+ case CA_INT64: CA_SLAB_REDUCE_T(int64_t, *st, p, m, dummy, 0, MB_INT(int64_t)); break;
2748
+ case CA_BOOLEAN: case CA_UINT8: CA_SLAB_REDUCE_T(uint8_t, *st, p, m, dummy, 0, MB_INT(uint64_t)); break;
2749
+ case CA_UINT16: CA_SLAB_REDUCE_T(uint16_t, *st, p, m, dummy, 0, MB_INT(uint64_t)); break;
2750
+ case CA_UINT32: CA_SLAB_REDUCE_T(uint32_t, *st, p, m, dummy, 0, MB_INT(uint64_t)); break;
2751
+ case CA_UINT64: CA_SLAB_REDUCE_T(uint64_t, *st, p, m, dummy, 0, MB_INT(uint64_t)); break;
2466
2752
  case CA_FLOAT32:
2467
- CA_SLAB_REDUCE_T(float, st, p, m, dummy, 0, MB_FLOAT(float, uint32_t, 0x7fc00000ULL));
2753
+ CA_SLAB_REDUCE_T(float, *st, p, m, dummy, 0, MB_FLOAT(float));
2468
2754
  break;
2469
2755
  case CA_FLOAT64:
2470
- CA_SLAB_REDUCE_T(double, st, p, m, dummy, 0, MB_FLOAT(double, uint64_t, 0x7ff8000000000000ULL));
2756
+ CA_SLAB_REDUCE_T(double, *st, p, m, dummy, 0, MB_FLOAT(double));
2471
2757
  break;
2472
2758
  }
2473
2759
 
@@ -2499,7 +2785,7 @@ rb_ca_mode_axis (VALUE self, VALUE vaxis)
2499
2785
  #undef MODE_EMIT
2500
2786
  (void) dummy;
2501
2787
 
2502
- ca_iter_state_finish(&st);
2788
+ ca_iter_state_finish(st);
2503
2789
  fz_hash_free(&h);
2504
2790
  fz_levels_free(&lv);
2505
2791
  fz_levels_free(&ct);
@@ -2529,10 +2815,16 @@ rb_ca_mode_axis (VALUE self, VALUE vaxis)
2529
2815
  }
2530
2816
 
2531
2817
  fz_levels_free(&flat);
2532
- xfree(foff);
2818
+ ALLOCV_END(hfoff);
2533
2819
  return result;
2534
2820
  }
2535
2821
 
2822
+ static VALUE
2823
+ rb_ca_mode_axis (VALUE self, VALUE vaxis)
2824
+ {
2825
+ return fz_run(fz_mode_axis_body, self, vaxis, Qnil, 0);
2826
+ }
2827
+
2536
2828
  void
2537
2829
  Init_carray_factorize (void)
2538
2830
  {