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/extconf.rb CHANGED
@@ -107,6 +107,17 @@ if enable_config("dev-build", false)
107
107
  $stderr.puts "carray: CARRAY_DEV_BUILD enabled (smoke surface available)"
108
108
  end
109
109
 
110
+ # --- signed overflow wraps (`-fwrapv`)
111
+ #
112
+ # An integer kernel that overflows answers modulo 2^N. C leaves signed
113
+ # overflow undefined, and a compiler that sees several operations at once
114
+ # rewrites them as if it could not happen -- `(x + 1) > x` becomes true --
115
+ # which carray-jit's fused expressions, built with these flags, would show.
116
+ if try_compile("int main(void) { return 0; }", "-fwrapv")
117
+ $CFLAGS += " -fwrapv"
118
+ $carray_build_flags << "-fwrapv"
119
+ end
120
+
110
121
  # --- probe for `-fopenmp-simd` (SL.1.0, PROPOSAL_REDUCTION_SIMD_LICENSE)
111
122
  #
112
123
  # `-fopenmp-simd` enables `#pragma omp simd` (and its `reduction(+:acc)`
@@ -257,6 +268,8 @@ $INSTALLFILES << ['carray_config.h', '$(archdir)']
257
268
  # (b) Not in the closure, but written for ext authors to include
258
269
  # explicitly. ca_obj_face.h is the Face authoring surface: an
259
270
  # external gem defines its own Face by including it directly.
271
+ # ca_for_buffer.h is whole-buffer delivery to an external library;
272
+ # it pulls ca_sweep_engine.h.
260
273
  #
261
274
  # A header that is neither is internal, however useful it looks in-tree.
262
275
  # ca_op_powi.h (op_powi_<type> helpers for the generated kernels) and
@@ -272,11 +285,14 @@ $INSTALLFILES << ['carray_config.h', '$(archdir)']
272
285
  ca_axis_descriptor.h
273
286
  ca_monop_dispatch.h
274
287
  ca_binop_dispatch.h
288
+ ca_triop_dispatch.h
275
289
  ca_bincmp_dispatch.h
276
290
  ca_moncmp_dispatch.h
277
291
  ca_kernel_iterator.h
278
292
  ca_iter_substrate.h
279
293
  ca_obj_face.h
294
+ ca_for_buffer.h
295
+ ca_sweep_engine.h
280
296
  ].each do |h|
281
297
  $INSTALLFILES << [h, '$(archdir)']
282
298
  end
@@ -324,11 +340,14 @@ end
324
340
  # compiling static sources, which is one pass with no race.
325
341
  #
326
342
  # carray_kernels.stamp is the mtime witness:
327
- # - here: regenerate and touch it when mkkernel.rb is newer
343
+ # - here: regenerate and touch it when mkkernel.rb or ca_op_powi.h (whose
344
+ # helper text the generator copies) is newer
328
345
  # - in the Rakefile (rake build_ext): the same stamp triggers need_extconf
329
346
  CARRAY_KERNELS_STAMP = "carray_kernels.stamp"
330
347
  if ( not File.exist?(CARRAY_KERNELS_STAMP) ) or
331
- File.stat("mkkernel.rb").mtime > File.stat(CARRAY_KERNELS_STAMP).mtime
348
+ %w[mkkernel.rb ca_op_powi.h].any? { |f|
349
+ File.stat(f).mtime > File.stat(CARRAY_KERNELS_STAMP).mtime
350
+ }
332
351
  # passing "." makes the generator write the split files into ext/
333
352
  unless system("ruby mkkernel.rb .")
334
353
  raise "mkkernel.rb failed to generate split kernel files"
@@ -346,6 +365,12 @@ if Dir.glob("carray_kernels_*.c").empty?
346
365
  raise "no carray_kernels_*.c files found after mkkernel.rb; check the generator"
347
366
  end
348
367
 
368
+ # The 5^q table the decimal-to-double reader (carray_cast.c) multiplies by.
369
+ if ( not File.exist?("ca_pow5_table.h") ) or
370
+ File.stat("mk_pow5_table.rb").mtime > File.stat("ca_pow5_table.h").mtime
371
+ system("ruby mk_pow5_table.rb")
372
+ end
373
+
349
374
  if ( not File.exist?("carray_call_cfunc.c") ) or
350
375
  ( not File.exist?("carray_call_cfunc.h") ) or
351
376
  File.stat("mk_call_cfunc.rb").mtime > File.stat("carray_call_cfunc.c").mtime
data/ext/mk_call_cfunc.rb CHANGED
@@ -128,83 +128,61 @@ def indent(s, prefix = " ")
128
128
  end
129
129
 
130
130
  # --- raw ca_call_cfunc_N ----------------------------------------------------
131
+ #
132
+ # The per-cell loop runs in a walker function of its own, handed to
133
+ # ca_sweep_run: a callback that raises part way through gives back the
134
+ # operands and scratch the engine holds, rather than leaving them behind.
135
+ #
136
+ # The `_r` variant (reentrant: + void *userdata at signature tail) passes a
137
+ # trailing `void *userdata` through to every per-cell `func(...)`
138
+ # invocation as its last argument. Naming follows POSIX convention
139
+ # (qsort_r / bsearch_r / strtok_r), where `_r` marks a reentrant form that
140
+ # takes a thunk so the callback no longer depends on file-static / global
141
+ # state. Use when the callback needs to share state with the caller (e.g.
142
+ # accumulators, configuration flags, library plan handles) without
143
+ # resorting to file-static plumbing.
144
+ def emit_raw_common(n, r:)
145
+ name = r ? "ca_call_cfunc_#{n}_r" : "ca_call_cfunc_#{n}"
146
+ fsig = r ? void_p_list_r(n) : void_p_list(n)
147
+ call = "c->func(#{args_p(n)}#{r ? ", c->userdata" : ""})"
131
148
 
132
- def emit_raw(n)
133
- $src.puts sig_raw(n)
134
- $src.puts "{"
135
- $src.puts indent(<<~END_C)
136
- CArray *cx[#{n}];
137
- char *base[#{n}];
138
- ca_size_t stride[#{n}];
139
- char *owned_buf[#{n}];
140
- int attached[#{n}];
141
- ca_sweep_state_t state;
142
- int k_op;
143
-
144
- END_C
145
- # extract CArray* from VALUE
146
- (0...n).each do |k|
147
- $src.puts " TypedData_Get_Struct(rcx#{k}, CArray, &carray_data_type, cx[#{k}]);"
148
- end
149
- $src.puts ""
150
149
  $src.puts <<~END_C
151
- /* sweep engine: per-operand acquire (alias / xmalloc + ca_xfer_all),
152
- broadcast shape check, mask OR across INPUTs, mask propagate to
153
- OUTPUTs. Lifecycle template lives in ext/ca_sweep_engine.{c,h}. */
154
- state.n_ops = #{n};
155
- state.fsync = fsync;
156
- state.cx = cx;
157
- state.base = base;
158
- state.stride = stride;
159
- state.owned_buf = owned_buf;
160
- state.attached = attached;
161
- state.no_mask = 0;
162
- state.src_label = "ca_call_cfunc_#{n}";
163
-
164
- ca_sweep_acquire(&state);
165
-
166
- /* inner loop: advance per-cell ptrs and invoke user kernel func */
167
- {
168
- char *p[#{n}];
169
- ca_size_t k;
170
- if ( state.m0 ) {
171
- for ( k = 0; k < state.n_kernel; k++ ) {
172
- if ( ! state.m0[k] ) {
173
- for ( k_op = 0; k_op < #{n}; k_op++ ) {
174
- p[k_op] = base[k_op] + k * stride[k_op];
175
- }
176
- func(#{args_p(n)});
177
- }
178
- }
179
- } else {
180
- for ( k = 0; k < state.n_kernel; k++ ) {
150
+ typedef struct {
151
+ ca_sweep_state_t *st;
152
+ void (*func)(#{fsig});
153
+ void *userdata;
154
+ } #{name}_ctx_t;
155
+
156
+ static VALUE
157
+ #{name}_walk (VALUE arg)
158
+ {
159
+ #{name}_ctx_t *c = (#{name}_ctx_t *) arg;
160
+ ca_sweep_state_t *st = c->st;
161
+ char *p[#{n}];
162
+ ca_size_t k;
163
+ int k_op;
164
+ if ( st->m0 ) {
165
+ for ( k = 0; k < st->n_kernel; k++ ) {
166
+ if ( ! st->m0[k] ) {
181
167
  for ( k_op = 0; k_op < #{n}; k_op++ ) {
182
- p[k_op] = base[k_op] + k * stride[k_op];
168
+ p[k_op] = st->base[k_op] + k * st->stride[k_op];
183
169
  }
184
- func(#{args_p(n)});
170
+ #{call};
171
+ }
172
+ }
173
+ } else {
174
+ for ( k = 0; k < st->n_kernel; k++ ) {
175
+ for ( k_op = 0; k_op < #{n}; k_op++ ) {
176
+ p[k_op] = st->base[k_op] + k * st->stride[k_op];
185
177
  }
178
+ #{call};
186
179
  }
187
180
  }
188
-
189
- ca_sweep_release(&state);
190
-
191
- return rcx0;
181
+ return Qnil;
192
182
  }
193
183
 
194
184
  END_C
195
- end
196
-
197
- # --- raw ca_call_cfunc_N_r (reentrant: + void *userdata at signature tail) --
198
- #
199
- # Variant of emit_raw with a trailing `void *userdata` parameter passed
200
- # through to every per-cell `func(...)` invocation as its last argument.
201
- # Naming follows POSIX convention (qsort_r / bsearch_r / strtok_r), where
202
- # `_r` marks a reentrant form that takes a thunk so the callback no longer
203
- # depends on file-static / global state. Use when the callback needs to
204
- # share state with the caller (e.g. accumulators, configuration flags,
205
- # library plan handles) without resorting to file-static plumbing.
206
- def emit_raw_r(n)
207
- $src.puts sig_raw_r(n)
185
+ $src.puts(r ? sig_raw_r(n) : sig_raw(n))
208
186
  $src.puts "{"
209
187
  $src.puts indent(<<~END_C)
210
188
  CArray *cx[#{n}];
@@ -213,7 +191,7 @@ def emit_raw_r(n)
213
191
  char *owned_buf[#{n}];
214
192
  int attached[#{n}];
215
193
  ca_sweep_state_t state;
216
- int k_op;
194
+ #{name}_ctx_t ctx;
217
195
 
218
196
  END_C
219
197
  (0...n).each do |k|
@@ -221,8 +199,9 @@ def emit_raw_r(n)
221
199
  end
222
200
  $src.puts ""
223
201
  $src.puts <<~END_C
224
- /* sweep engine: same lifecycle as ca_call_cfunc_#{n}; the difference is
225
- the per-cell `func(...)` call has `userdata` as its last argument. */
202
+ /* sweep engine: per-operand acquire (alias / xmalloc + ca_xfer_all),
203
+ operand pairing, mask OR across INPUTs, mask propagate to OUTPUTs.
204
+ Lifecycle template lives in ext/ca_sweep_engine.{c,h}. */
226
205
  state.n_ops = #{n};
227
206
  state.fsync = fsync;
228
207
  state.cx = cx;
@@ -230,34 +209,14 @@ def emit_raw_r(n)
230
209
  state.stride = stride;
231
210
  state.owned_buf = owned_buf;
232
211
  state.attached = attached;
233
- state.no_mask = 0;
234
- state.src_label = "ca_call_cfunc_#{n}_r";
212
+ state.src_label = "#{name}";
235
213
 
236
214
  ca_sweep_acquire(&state);
237
215
 
238
- {
239
- char *p[#{n}];
240
- ca_size_t k;
241
- if ( state.m0 ) {
242
- for ( k = 0; k < state.n_kernel; k++ ) {
243
- if ( ! state.m0[k] ) {
244
- for ( k_op = 0; k_op < #{n}; k_op++ ) {
245
- p[k_op] = base[k_op] + k * stride[k_op];
246
- }
247
- func(#{args_p_r(n)});
248
- }
249
- }
250
- } else {
251
- for ( k = 0; k < state.n_kernel; k++ ) {
252
- for ( k_op = 0; k_op < #{n}; k_op++ ) {
253
- p[k_op] = base[k_op] + k * stride[k_op];
254
- }
255
- func(#{args_p_r(n)});
256
- }
257
- }
258
- }
259
-
260
- ca_sweep_release(&state);
216
+ ctx.st = &state;
217
+ ctx.func = func;
218
+ ctx.userdata = #{r ? "userdata" : "NULL"};
219
+ ca_sweep_run(&state, #{name}_walk, (VALUE) &ctx);
261
220
 
262
221
  return rcx0;
263
222
  }
@@ -265,6 +224,9 @@ def emit_raw_r(n)
265
224
  END_C
266
225
  end
267
226
 
227
+ def emit_raw(n) ; emit_raw_common(n, r: false); end
228
+ def emit_raw_r(n) ; emit_raw_common(n, r: true); end
229
+
268
230
  # --- typed dispatchers: M outputs, N inputs ---------------------------------
269
231
  #
270
232
  # The four typed emitters (cfunc / cfunc_r / cslab / cslab_r) differ in one
@@ -418,7 +380,7 @@ def sig_slab_r(n)
418
380
  "VALUE\nca_call_cslab_#{n}_r (ca_cslab_r_t func, const char *fsync,\n #{value_param_list(n)},\n void *userdata)"
419
381
  end
420
382
 
421
- def emit_slab_body(n, name, call)
383
+ def emit_slab_body(n, name, walker, userdata)
422
384
  $src.puts indent(<<~END_C)
423
385
  CArray *cx[#{n}];
424
386
  char *base[#{n}];
@@ -427,6 +389,7 @@ def emit_slab_body(n, name, call)
427
389
  char *owned_buf[#{n}];
428
390
  int attached[#{n}];
429
391
  ca_sweep_state_t state;
392
+ ca_cslab_ctx_t ctx;
430
393
 
431
394
  END_C
432
395
  (0...n).each do |k|
@@ -448,21 +411,15 @@ def emit_slab_body(n, name, call)
448
411
  state.stride = stride;
449
412
  state.owned_buf = owned_buf;
450
413
  state.attached = attached;
451
- state.no_mask = 0;
452
414
  state.src_label = "#{name}";
453
415
 
454
416
  ca_sweep_acquire_chunked(&state);
455
417
 
456
- /* outer loop: hand the author one chunk at a time. base[] is rewritten
457
- per chunk by ca_sweep_next_chunk -- for a non-alias INPUT it points
458
- at the arena scratch the chunk was just gathered into, which is
459
- packed, so stride[] is the element size and the author's inner loop
460
- sees contiguous data. */
461
- while ( ca_sweep_next_chunk(&state) ) {
462
- #{call};
463
- }
464
-
465
- ca_sweep_release_chunked(&state);
418
+ ctx.st = &state;
419
+ ctx.func = #{userdata == "NULL" ? "func" : "NULL"};
420
+ ctx.func_r = #{userdata == "NULL" ? "NULL" : "func"};
421
+ ctx.userdata = #{userdata};
422
+ ca_sweep_run_chunked(&state, #{walker}, (VALUE) &ctx);
466
423
 
467
424
  return rcx0;
468
425
  }
@@ -473,8 +430,7 @@ end
473
430
  def emit_slab(n)
474
431
  $src.puts sig_slab(n)
475
432
  $src.puts "{"
476
- emit_slab_body(n, "ca_call_cslab_#{n}",
477
- "func(base, stride, state.chunk_n, ca_sweep_chunk_mask(&state))")
433
+ emit_slab_body(n, "ca_call_cslab_#{n}", "ca_cslab_walk", "NULL")
478
434
  end
479
435
 
480
436
  # Variant of emit_slab with a trailing `void *userdata` parameter passed
@@ -483,8 +439,7 @@ end
483
439
  def emit_slab_r(n)
484
440
  $src.puts sig_slab_r(n)
485
441
  $src.puts "{"
486
- emit_slab_body(n, "ca_call_cslab_#{n}_r",
487
- "func(base, stride, state.chunk_n, ca_sweep_chunk_mask(&state), userdata)")
442
+ emit_slab_body(n, "ca_call_cslab_#{n}_r", "ca_cslab_r_walk", "userdata")
488
443
  end
489
444
 
490
445
  # --- header declaration emitters --------------------------------------------
@@ -557,14 +512,44 @@ $src.puts <<~END_C
557
512
  #include "ca_sweep_engine.h"
558
513
  #include <string.h>
559
514
 
560
- /* The chunk's iteration mask, or NULL when no INPUT operand carried one.
561
- m0 is chunk-sized and re-gathered per chunk by ca_sweep_next_chunk, so
562
- it is already the slice -- one byte per cell, indexed 0..chunk_n-1
563
- alongside base[] and stride[]. */
564
- static const boolean8_t *
565
- ca_sweep_chunk_mask (ca_sweep_state_t *st)
515
+ /* Chunk walk for the ca_call_cslab_N family, run by ca_sweep_run_chunked
516
+ so that a callback raising part way through gives back what the engine
517
+ holds. Hands the author one chunk at a time. base[] is rewritten per
518
+ chunk by ca_sweep_next_chunk -- for a non-alias INPUT it points at the
519
+ arena scratch the chunk was just gathered into, which is packed, so
520
+ stride[] is the element size and the author's inner loop sees
521
+ contiguous data. m0 is the chunk's iteration mask, or NULL when no
522
+ INPUT operand carried one: chunk-sized and re-gathered per chunk, so it
523
+ is already the slice -- one byte per cell, indexed 0..chunk_n-1
524
+ alongside base[] and stride[]. The arity does not appear here: the
525
+ operands reach the callback through base[] / stride[]. */
526
+ typedef struct {
527
+ ca_sweep_state_t *st;
528
+ ca_cslab_t func; /* ca_call_cslab_N */
529
+ ca_cslab_r_t func_r; /* ca_call_cslab_N_r */
530
+ void *userdata;
531
+ } ca_cslab_ctx_t;
532
+
533
+ static VALUE
534
+ ca_cslab_walk (VALUE arg)
566
535
  {
567
- return st->m0;
536
+ ca_cslab_ctx_t *c = (ca_cslab_ctx_t *) arg;
537
+ ca_sweep_state_t *st = c->st;
538
+ while ( ca_sweep_next_chunk(st) ) {
539
+ c->func(st->base, st->stride, st->chunk_n, st->m0);
540
+ }
541
+ return Qnil;
542
+ }
543
+
544
+ static VALUE
545
+ ca_cslab_r_walk (VALUE arg)
546
+ {
547
+ ca_cslab_ctx_t *c = (ca_cslab_ctx_t *) arg;
548
+ ca_sweep_state_t *st = c->st;
549
+ while ( ca_sweep_next_chunk(st) ) {
550
+ c->func_r(st->base, st->stride, st->chunk_n, st->m0, c->userdata);
551
+ }
552
+ return Qnil;
568
553
  }
569
554
 
570
555
  END_C
@@ -0,0 +1,35 @@
1
+ # Generates ca_pow5_table.h: for each decimal exponent q in [-342, 308], a
2
+ # 128-bit approximation of 5^q scaled by a power of two into [2^127, 2^128),
3
+ # as the Eisel-Lemire decimal-to-binary64 conversion takes it.
4
+ #
5
+ # q >= 0: floor(5^q * 2^k) (exact while 5^q < 2^128, q <= 55)
6
+ # -27 <= q < 0: ceil(2^k / 5^-q)
7
+ # q < -27: floor(2^k / 5^-q)
8
+ # with k chosen so the value has exactly 128 bits.
9
+
10
+ Q_MIN, Q_MAX = -342, 308
11
+ rows = (Q_MIN..Q_MAX).map do |q|
12
+ if q >= 0
13
+ v = 5**q
14
+ v <<= 128 - v.bit_length if v.bit_length < 128
15
+ v >>= v.bit_length - 128 if v.bit_length > 128
16
+ else
17
+ p5 = 5**-q
18
+ k = 127 + p5.bit_length
19
+ v, r = (1 << k).divmod(p5)
20
+ v += 1 if q >= -27 && r != 0
21
+ v >>= v.bit_length - 128 if v.bit_length > 128
22
+ v <<= 128 - v.bit_length if v.bit_length < 128
23
+ end
24
+ raise "bad width at #{q}" unless v.bit_length == 128
25
+ [v >> 64, v & ((1 << 64) - 1)]
26
+ end
27
+
28
+ File.open(File.join(__dir__, "ca_pow5_table.h"), "w") do |f|
29
+ f.puts "/* Generated by mk_pow5_table.rb -- do not edit. */"
30
+ f.puts "#define CA_POW5_Q_MIN (#{Q_MIN})"
31
+ f.puts "#define CA_POW5_Q_MAX #{Q_MAX}"
32
+ f.puts "static const uint64_t ca_pow5_table[#{rows.size * 2}] = {"
33
+ rows.each { |hi, lo| f.puts format(" 0x%016xULL, 0x%016xULL,", hi, lo) }
34
+ f.puts "};"
35
+ end