tigerbeetle 0.0.34 → 0.0.37

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 (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/ext/tb_client/extconf.rb +13 -13
  4. data/ext/tb_client/tigerbeetle/LICENSE +177 -0
  5. data/ext/tb_client/tigerbeetle/build.zig +2327 -0
  6. data/ext/tb_client/tigerbeetle/src/aof.zig +1000 -0
  7. data/ext/tb_client/tigerbeetle/src/build_multiversion.zig +808 -0
  8. data/ext/tb_client/tigerbeetle/src/cdc/amqp/protocol.zig +1283 -0
  9. data/ext/tb_client/tigerbeetle/src/cdc/amqp/spec.zig +1704 -0
  10. data/ext/tb_client/tigerbeetle/src/cdc/amqp/types.zig +341 -0
  11. data/ext/tb_client/tigerbeetle/src/cdc/amqp.zig +1450 -0
  12. data/ext/tb_client/tigerbeetle/src/cdc/runner.zig +1659 -0
  13. data/ext/tb_client/tigerbeetle/src/clients/c/samples/main.c +406 -0
  14. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/context.zig +1084 -0
  15. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/echo_client.zig +286 -0
  16. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/packet.zig +158 -0
  17. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal.zig +229 -0
  18. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal_fuzz.zig +110 -0
  19. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.h +386 -0
  20. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.zig +34 -0
  21. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_exports.zig +281 -0
  22. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header.zig +312 -0
  23. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header_test.zig +138 -0
  24. data/ext/tb_client/tigerbeetle/src/clients/c/test.zig +466 -0
  25. data/ext/tb_client/tigerbeetle/src/clients/docs_samples.zig +157 -0
  26. data/ext/tb_client/tigerbeetle/src/clients/docs_types.zig +90 -0
  27. data/ext/tb_client/tigerbeetle/src/clients/dotnet/ci.zig +203 -0
  28. data/ext/tb_client/tigerbeetle/src/clients/dotnet/docs.zig +79 -0
  29. data/ext/tb_client/tigerbeetle/src/clients/dotnet/dotnet_bindings.zig +542 -0
  30. data/ext/tb_client/tigerbeetle/src/clients/go/ci.zig +109 -0
  31. data/ext/tb_client/tigerbeetle/src/clients/go/docs.zig +86 -0
  32. data/ext/tb_client/tigerbeetle/src/clients/go/go_bindings.zig +370 -0
  33. data/ext/tb_client/tigerbeetle/src/clients/go/pkg/native/tb_client.h +386 -0
  34. data/ext/tb_client/tigerbeetle/src/clients/java/ci.zig +167 -0
  35. data/ext/tb_client/tigerbeetle/src/clients/java/docs.zig +126 -0
  36. data/ext/tb_client/tigerbeetle/src/clients/java/java_bindings.zig +996 -0
  37. data/ext/tb_client/tigerbeetle/src/clients/java/src/client.zig +748 -0
  38. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni.zig +3238 -0
  39. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_tests.zig +1718 -0
  40. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_thread_cleaner.zig +190 -0
  41. data/ext/tb_client/tigerbeetle/src/clients/node/ci.zig +104 -0
  42. data/ext/tb_client/tigerbeetle/src/clients/node/docs.zig +75 -0
  43. data/ext/tb_client/tigerbeetle/src/clients/node/node.zig +522 -0
  44. data/ext/tb_client/tigerbeetle/src/clients/node/node_bindings.zig +267 -0
  45. data/ext/tb_client/tigerbeetle/src/clients/node/src/c.zig +3 -0
  46. data/ext/tb_client/tigerbeetle/src/clients/node/src/translate.zig +379 -0
  47. data/ext/tb_client/tigerbeetle/src/clients/python/ci.zig +131 -0
  48. data/ext/tb_client/tigerbeetle/src/clients/python/docs.zig +63 -0
  49. data/ext/tb_client/tigerbeetle/src/clients/python/python_bindings.zig +588 -0
  50. data/ext/tb_client/tigerbeetle/src/clients/rust/assets/tb_client.h +386 -0
  51. data/ext/tb_client/tigerbeetle/src/clients/rust/ci.zig +73 -0
  52. data/ext/tb_client/tigerbeetle/src/clients/rust/docs.zig +106 -0
  53. data/ext/tb_client/tigerbeetle/src/clients/rust/rust_bindings.zig +305 -0
  54. data/ext/tb_client/tigerbeetle/src/config.zig +296 -0
  55. data/ext/tb_client/tigerbeetle/src/constants.zig +790 -0
  56. data/ext/tb_client/tigerbeetle/src/copyhound.zig +202 -0
  57. data/ext/tb_client/tigerbeetle/src/counting_allocator.zig +72 -0
  58. data/ext/tb_client/tigerbeetle/src/direction.zig +11 -0
  59. data/ext/tb_client/tigerbeetle/src/docs_website/build.zig +158 -0
  60. data/ext/tb_client/tigerbeetle/src/docs_website/src/content.zig +156 -0
  61. data/ext/tb_client/tigerbeetle/src/docs_website/src/docs.zig +252 -0
  62. data/ext/tb_client/tigerbeetle/src/docs_website/src/file_checker.zig +313 -0
  63. data/ext/tb_client/tigerbeetle/src/docs_website/src/html.zig +87 -0
  64. data/ext/tb_client/tigerbeetle/src/docs_website/src/page_writer.zig +63 -0
  65. data/ext/tb_client/tigerbeetle/src/docs_website/src/redirects.zig +47 -0
  66. data/ext/tb_client/tigerbeetle/src/docs_website/src/search_index_writer.zig +28 -0
  67. data/ext/tb_client/tigerbeetle/src/docs_website/src/service_worker_writer.zig +61 -0
  68. data/ext/tb_client/tigerbeetle/src/docs_website/src/single_page_writer.zig +169 -0
  69. data/ext/tb_client/tigerbeetle/src/docs_website/src/website.zig +46 -0
  70. data/ext/tb_client/tigerbeetle/src/ewah.zig +445 -0
  71. data/ext/tb_client/tigerbeetle/src/ewah_benchmark.zig +128 -0
  72. data/ext/tb_client/tigerbeetle/src/ewah_fuzz.zig +171 -0
  73. data/ext/tb_client/tigerbeetle/src/fuzz_tests.zig +179 -0
  74. data/ext/tb_client/tigerbeetle/src/integration_tests.zig +662 -0
  75. data/ext/tb_client/tigerbeetle/src/io/common.zig +155 -0
  76. data/ext/tb_client/tigerbeetle/src/io/darwin.zig +1093 -0
  77. data/ext/tb_client/tigerbeetle/src/io/linux.zig +1880 -0
  78. data/ext/tb_client/tigerbeetle/src/io/test.zig +1005 -0
  79. data/ext/tb_client/tigerbeetle/src/io/windows.zig +1598 -0
  80. data/ext/tb_client/tigerbeetle/src/io.zig +34 -0
  81. data/ext/tb_client/tigerbeetle/src/iops.zig +134 -0
  82. data/ext/tb_client/tigerbeetle/src/list.zig +236 -0
  83. data/ext/tb_client/tigerbeetle/src/lsm/binary_search.zig +848 -0
  84. data/ext/tb_client/tigerbeetle/src/lsm/binary_search_benchmark.zig +179 -0
  85. data/ext/tb_client/tigerbeetle/src/lsm/cache_map.zig +424 -0
  86. data/ext/tb_client/tigerbeetle/src/lsm/cache_map_fuzz.zig +420 -0
  87. data/ext/tb_client/tigerbeetle/src/lsm/compaction.zig +2117 -0
  88. data/ext/tb_client/tigerbeetle/src/lsm/composite_key.zig +182 -0
  89. data/ext/tb_client/tigerbeetle/src/lsm/forest.zig +1119 -0
  90. data/ext/tb_client/tigerbeetle/src/lsm/forest_fuzz.zig +1102 -0
  91. data/ext/tb_client/tigerbeetle/src/lsm/forest_table_iterator.zig +200 -0
  92. data/ext/tb_client/tigerbeetle/src/lsm/groove.zig +1495 -0
  93. data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge.zig +739 -0
  94. data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge_benchmark.zig +166 -0
  95. data/ext/tb_client/tigerbeetle/src/lsm/manifest.zig +754 -0
  96. data/ext/tb_client/tigerbeetle/src/lsm/manifest_level.zig +1294 -0
  97. data/ext/tb_client/tigerbeetle/src/lsm/manifest_level_fuzz.zig +510 -0
  98. data/ext/tb_client/tigerbeetle/src/lsm/manifest_log.zig +1263 -0
  99. data/ext/tb_client/tigerbeetle/src/lsm/manifest_log_fuzz.zig +628 -0
  100. data/ext/tb_client/tigerbeetle/src/lsm/node_pool.zig +247 -0
  101. data/ext/tb_client/tigerbeetle/src/lsm/scan_buffer.zig +116 -0
  102. data/ext/tb_client/tigerbeetle/src/lsm/scan_builder.zig +543 -0
  103. data/ext/tb_client/tigerbeetle/src/lsm/scan_fuzz.zig +938 -0
  104. data/ext/tb_client/tigerbeetle/src/lsm/scan_lookup.zig +293 -0
  105. data/ext/tb_client/tigerbeetle/src/lsm/scan_merge.zig +362 -0
  106. data/ext/tb_client/tigerbeetle/src/lsm/scan_range.zig +99 -0
  107. data/ext/tb_client/tigerbeetle/src/lsm/scan_state.zig +17 -0
  108. data/ext/tb_client/tigerbeetle/src/lsm/scan_tree.zig +1036 -0
  109. data/ext/tb_client/tigerbeetle/src/lsm/schema.zig +617 -0
  110. data/ext/tb_client/tigerbeetle/src/lsm/scratch_memory.zig +84 -0
  111. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array.zig +1500 -0
  112. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_benchmark.zig +149 -0
  113. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_fuzz.zig +7 -0
  114. data/ext/tb_client/tigerbeetle/src/lsm/set_associative_cache.zig +865 -0
  115. data/ext/tb_client/tigerbeetle/src/lsm/table.zig +607 -0
  116. data/ext/tb_client/tigerbeetle/src/lsm/table_memory.zig +843 -0
  117. data/ext/tb_client/tigerbeetle/src/lsm/table_value_iterator.zig +105 -0
  118. data/ext/tb_client/tigerbeetle/src/lsm/timestamp_range.zig +40 -0
  119. data/ext/tb_client/tigerbeetle/src/lsm/tree.zig +630 -0
  120. data/ext/tb_client/tigerbeetle/src/lsm/tree_fuzz.zig +933 -0
  121. data/ext/tb_client/tigerbeetle/src/lsm/zig_zag_merge.zig +557 -0
  122. data/ext/tb_client/tigerbeetle/src/message_buffer.zig +469 -0
  123. data/ext/tb_client/tigerbeetle/src/message_bus.zig +1214 -0
  124. data/ext/tb_client/tigerbeetle/src/message_bus_fuzz.zig +936 -0
  125. data/ext/tb_client/tigerbeetle/src/message_pool.zig +343 -0
  126. data/ext/tb_client/tigerbeetle/src/multiversion.zig +2195 -0
  127. data/ext/tb_client/tigerbeetle/src/queue.zig +390 -0
  128. data/ext/tb_client/tigerbeetle/src/repl/completion.zig +201 -0
  129. data/ext/tb_client/tigerbeetle/src/repl/parser.zig +1356 -0
  130. data/ext/tb_client/tigerbeetle/src/repl/terminal.zig +496 -0
  131. data/ext/tb_client/tigerbeetle/src/repl.zig +1034 -0
  132. data/ext/tb_client/tigerbeetle/src/scripts/amqp.zig +973 -0
  133. data/ext/tb_client/tigerbeetle/src/scripts/cfo.zig +1866 -0
  134. data/ext/tb_client/tigerbeetle/src/scripts/changelog.zig +304 -0
  135. data/ext/tb_client/tigerbeetle/src/scripts/ci.zig +227 -0
  136. data/ext/tb_client/tigerbeetle/src/scripts/client_readmes.zig +658 -0
  137. data/ext/tb_client/tigerbeetle/src/scripts/devhub.zig +466 -0
  138. data/ext/tb_client/tigerbeetle/src/scripts/release.zig +1058 -0
  139. data/ext/tb_client/tigerbeetle/src/scripts.zig +105 -0
  140. data/ext/tb_client/tigerbeetle/src/shell.zig +1195 -0
  141. data/ext/tb_client/tigerbeetle/src/stack.zig +260 -0
  142. data/ext/tb_client/tigerbeetle/src/state_machine/auditor.zig +911 -0
  143. data/ext/tb_client/tigerbeetle/src/state_machine/workload.zig +2079 -0
  144. data/ext/tb_client/tigerbeetle/src/state_machine.zig +4872 -0
  145. data/ext/tb_client/tigerbeetle/src/state_machine_fuzz.zig +288 -0
  146. data/ext/tb_client/tigerbeetle/src/state_machine_tests.zig +3128 -0
  147. data/ext/tb_client/tigerbeetle/src/static_allocator.zig +82 -0
  148. data/ext/tb_client/tigerbeetle/src/stdx/bit_set.zig +157 -0
  149. data/ext/tb_client/tigerbeetle/src/stdx/bounded_array.zig +292 -0
  150. data/ext/tb_client/tigerbeetle/src/stdx/debug.zig +65 -0
  151. data/ext/tb_client/tigerbeetle/src/stdx/flags.zig +1414 -0
  152. data/ext/tb_client/tigerbeetle/src/stdx/mlock.zig +92 -0
  153. data/ext/tb_client/tigerbeetle/src/stdx/prng.zig +677 -0
  154. data/ext/tb_client/tigerbeetle/src/stdx/radix.zig +336 -0
  155. data/ext/tb_client/tigerbeetle/src/stdx/ring_buffer.zig +511 -0
  156. data/ext/tb_client/tigerbeetle/src/stdx/sort_test.zig +112 -0
  157. data/ext/tb_client/tigerbeetle/src/stdx/stdx.zig +1160 -0
  158. data/ext/tb_client/tigerbeetle/src/stdx/testing/low_level_hash_vectors.zig +142 -0
  159. data/ext/tb_client/tigerbeetle/src/stdx/testing/snaptest.zig +361 -0
  160. data/ext/tb_client/tigerbeetle/src/stdx/time_units.zig +275 -0
  161. data/ext/tb_client/tigerbeetle/src/stdx/unshare.zig +295 -0
  162. data/ext/tb_client/tigerbeetle/src/stdx/vendored/aegis.zig +436 -0
  163. data/ext/tb_client/tigerbeetle/src/stdx/windows.zig +48 -0
  164. data/ext/tb_client/tigerbeetle/src/stdx/zipfian.zig +402 -0
  165. data/ext/tb_client/tigerbeetle/src/storage.zig +489 -0
  166. data/ext/tb_client/tigerbeetle/src/storage_fuzz.zig +180 -0
  167. data/ext/tb_client/tigerbeetle/src/testing/bench.zig +146 -0
  168. data/ext/tb_client/tigerbeetle/src/testing/cluster/grid_checker.zig +53 -0
  169. data/ext/tb_client/tigerbeetle/src/testing/cluster/journal_checker.zig +61 -0
  170. data/ext/tb_client/tigerbeetle/src/testing/cluster/manifest_checker.zig +76 -0
  171. data/ext/tb_client/tigerbeetle/src/testing/cluster/message_bus.zig +110 -0
  172. data/ext/tb_client/tigerbeetle/src/testing/cluster/network.zig +412 -0
  173. data/ext/tb_client/tigerbeetle/src/testing/cluster/state_checker.zig +331 -0
  174. data/ext/tb_client/tigerbeetle/src/testing/cluster/storage_checker.zig +458 -0
  175. data/ext/tb_client/tigerbeetle/src/testing/cluster.zig +1198 -0
  176. data/ext/tb_client/tigerbeetle/src/testing/exhaustigen.zig +128 -0
  177. data/ext/tb_client/tigerbeetle/src/testing/fixtures.zig +181 -0
  178. data/ext/tb_client/tigerbeetle/src/testing/fuzz.zig +144 -0
  179. data/ext/tb_client/tigerbeetle/src/testing/id.zig +97 -0
  180. data/ext/tb_client/tigerbeetle/src/testing/io.zig +317 -0
  181. data/ext/tb_client/tigerbeetle/src/testing/marks.zig +126 -0
  182. data/ext/tb_client/tigerbeetle/src/testing/packet_simulator.zig +533 -0
  183. data/ext/tb_client/tigerbeetle/src/testing/reply_sequence.zig +154 -0
  184. data/ext/tb_client/tigerbeetle/src/testing/state_machine.zig +389 -0
  185. data/ext/tb_client/tigerbeetle/src/testing/storage.zig +1247 -0
  186. data/ext/tb_client/tigerbeetle/src/testing/table.zig +249 -0
  187. data/ext/tb_client/tigerbeetle/src/testing/time.zig +98 -0
  188. data/ext/tb_client/tigerbeetle/src/testing/tmp_tigerbeetle.zig +212 -0
  189. data/ext/tb_client/tigerbeetle/src/testing/vortex/constants.zig +26 -0
  190. data/ext/tb_client/tigerbeetle/src/testing/vortex/faulty_network.zig +580 -0
  191. data/ext/tb_client/tigerbeetle/src/testing/vortex/java_driver/ci.zig +39 -0
  192. data/ext/tb_client/tigerbeetle/src/testing/vortex/logged_process.zig +214 -0
  193. data/ext/tb_client/tigerbeetle/src/testing/vortex/rust_driver/ci.zig +34 -0
  194. data/ext/tb_client/tigerbeetle/src/testing/vortex/supervisor.zig +766 -0
  195. data/ext/tb_client/tigerbeetle/src/testing/vortex/workload.zig +543 -0
  196. data/ext/tb_client/tigerbeetle/src/testing/vortex/zig_driver.zig +181 -0
  197. data/ext/tb_client/tigerbeetle/src/tidy.zig +1448 -0
  198. data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_driver.zig +227 -0
  199. data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_load.zig +1069 -0
  200. data/ext/tb_client/tigerbeetle/src/tigerbeetle/cli.zig +1422 -0
  201. data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect.zig +1658 -0
  202. data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect_integrity.zig +518 -0
  203. data/ext/tb_client/tigerbeetle/src/tigerbeetle/libtb_client.zig +36 -0
  204. data/ext/tb_client/tigerbeetle/src/tigerbeetle/main.zig +646 -0
  205. data/ext/tb_client/tigerbeetle/src/tigerbeetle.zig +958 -0
  206. data/ext/tb_client/tigerbeetle/src/time.zig +236 -0
  207. data/ext/tb_client/tigerbeetle/src/trace/event.zig +745 -0
  208. data/ext/tb_client/tigerbeetle/src/trace/statsd.zig +462 -0
  209. data/ext/tb_client/tigerbeetle/src/trace.zig +556 -0
  210. data/ext/tb_client/tigerbeetle/src/unit_tests.zig +321 -0
  211. data/ext/tb_client/tigerbeetle/src/vopr.zig +1785 -0
  212. data/ext/tb_client/tigerbeetle/src/vortex.zig +101 -0
  213. data/ext/tb_client/tigerbeetle/src/vsr/checkpoint_trailer.zig +473 -0
  214. data/ext/tb_client/tigerbeetle/src/vsr/checksum.zig +208 -0
  215. data/ext/tb_client/tigerbeetle/src/vsr/checksum_benchmark.zig +43 -0
  216. data/ext/tb_client/tigerbeetle/src/vsr/client.zig +768 -0
  217. data/ext/tb_client/tigerbeetle/src/vsr/client_replies.zig +532 -0
  218. data/ext/tb_client/tigerbeetle/src/vsr/client_sessions.zig +338 -0
  219. data/ext/tb_client/tigerbeetle/src/vsr/clock.zig +1019 -0
  220. data/ext/tb_client/tigerbeetle/src/vsr/fault_detector.zig +279 -0
  221. data/ext/tb_client/tigerbeetle/src/vsr/free_set.zig +1381 -0
  222. data/ext/tb_client/tigerbeetle/src/vsr/free_set_fuzz.zig +315 -0
  223. data/ext/tb_client/tigerbeetle/src/vsr/grid.zig +1460 -0
  224. data/ext/tb_client/tigerbeetle/src/vsr/grid_blocks_missing.zig +757 -0
  225. data/ext/tb_client/tigerbeetle/src/vsr/grid_scrubber.zig +797 -0
  226. data/ext/tb_client/tigerbeetle/src/vsr/journal.zig +2586 -0
  227. data/ext/tb_client/tigerbeetle/src/vsr/marzullo.zig +308 -0
  228. data/ext/tb_client/tigerbeetle/src/vsr/message_header.zig +1777 -0
  229. data/ext/tb_client/tigerbeetle/src/vsr/multi_batch.zig +715 -0
  230. data/ext/tb_client/tigerbeetle/src/vsr/multi_batch_fuzz.zig +185 -0
  231. data/ext/tb_client/tigerbeetle/src/vsr/repair_budget.zig +333 -0
  232. data/ext/tb_client/tigerbeetle/src/vsr/replica.zig +12355 -0
  233. data/ext/tb_client/tigerbeetle/src/vsr/replica_format.zig +416 -0
  234. data/ext/tb_client/tigerbeetle/src/vsr/replica_reformat.zig +165 -0
  235. data/ext/tb_client/tigerbeetle/src/vsr/replica_test.zig +2910 -0
  236. data/ext/tb_client/tigerbeetle/src/vsr/routing.zig +1075 -0
  237. data/ext/tb_client/tigerbeetle/src/vsr/superblock.zig +1603 -0
  238. data/ext/tb_client/tigerbeetle/src/vsr/superblock_fuzz.zig +484 -0
  239. data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums.zig +405 -0
  240. data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +355 -0
  241. data/ext/tb_client/tigerbeetle/src/vsr/sync.zig +29 -0
  242. data/ext/tb_client/tigerbeetle/src/vsr.zig +1727 -0
  243. data/lib/tb_client/shared_lib.rb +12 -5
  244. data/lib/tigerbeetle/client.rb +1 -1
  245. data/lib/tigerbeetle/platforms.rb +9 -0
  246. data/lib/tigerbeetle/version.rb +2 -2
  247. data/tigerbeetle.gemspec +22 -5
  248. metadata +242 -3
  249. data/ext/tb_client/pkg.tar.gz +0 -0
@@ -0,0 +1,557 @@
1
+ const std = @import("std");
2
+ const assert = std.debug.assert;
3
+ const math = std.math;
4
+ const mem = std.mem;
5
+
6
+ const stdx = @import("stdx");
7
+
8
+ const Direction = @import("../direction.zig").Direction;
9
+ const Pending = error{Pending};
10
+
11
+ /// ZigZag merge join.
12
+ /// Resources:
13
+ /// https://github.com/objectify/objectify/wiki/Concepts#indexes.
14
+ /// https://youtu.be/AgaL6NGpkB8?t=26m10s
15
+ pub fn ZigZagMergeIteratorType(
16
+ comptime Context: type,
17
+ comptime Key: type,
18
+ comptime Value: type,
19
+ comptime key_from_value: fn (*const Value) callconv(.@"inline") Key,
20
+ comptime streams_max: u32,
21
+ /// Peek the next key in the stream identified by `stream_index`.
22
+ /// For example, `peek(stream_index=2)` returns `user_streams[2][0]`.
23
+ /// Returns `Pending` if the stream was consumed and must be refilled
24
+ /// before calling `peek()` again.
25
+ /// Returns null if the stream was fully consumed and reached the end.
26
+ comptime stream_peek: fn (
27
+ context: *Context,
28
+ stream_index: u32,
29
+ ) Pending!?Key,
30
+ /// Consumes the current value and moves the stream identified by `stream_index`.
31
+ /// Pop is always called after `peek()`, it is not expected that the stream be `Empty`
32
+ /// or `Pending`.
33
+ comptime stream_pop: fn (context: *Context, stream_index: u32) Value,
34
+ /// Probes the stream identified by `stream_index` causing it to move to the next value such
35
+ /// that `value.key >= probe_key` (ascending) or `value.key <= probe_key` (descending).
36
+ /// Should not be called when the current key already matches the probe.
37
+ /// The stream may become empty or `Pending` _after_ probing.
38
+ comptime stream_probe: fn (context: *Context, stream_index: u32, probe_key: Key) void,
39
+ ) type {
40
+ return struct {
41
+ const ZigZagMergeIterator = @This();
42
+ const BitSet = std.bit_set.IntegerBitSet(streams_max);
43
+
44
+ context: *Context,
45
+ streams_count: u32,
46
+ direction: Direction,
47
+ probe_key_previous: ?Key = null,
48
+ key_popped: ?Key = null,
49
+
50
+ /// At least two scans are required for zig-zag merge.
51
+ pub fn init(
52
+ context: *Context,
53
+ streams_count: u32,
54
+ direction: Direction,
55
+ ) ZigZagMergeIterator {
56
+ assert(streams_count <= streams_max);
57
+ assert(streams_count > 1);
58
+
59
+ return .{
60
+ .context = context,
61
+ .streams_count = streams_count,
62
+ .direction = direction,
63
+ };
64
+ }
65
+
66
+ // Resets the iterator when the underlying streams are moved.
67
+ // It's not necessary for ZigZagMerge, but it follows the same API for all MergeIterators.
68
+ pub fn reset(it: *ZigZagMergeIterator) void {
69
+ _ = it;
70
+ }
71
+
72
+ pub fn pop(it: *ZigZagMergeIterator) Pending!?Value {
73
+ const key = try it.peek_key() orelse
74
+ return null;
75
+
76
+ if (it.key_popped) |previous| {
77
+ switch (std.math.order(previous, key)) {
78
+ .lt => assert(it.direction == .ascending),
79
+ // Duplicate values are not expected.
80
+ .eq => unreachable,
81
+ .gt => assert(it.direction == .descending),
82
+ }
83
+ }
84
+ it.key_popped = key;
85
+
86
+ const value = stream_pop(it.context, 0);
87
+ assert(key_from_value(&value) == key);
88
+ for (1..it.streams_count) |stream_index| {
89
+ const value_other = stream_pop(it.context, @intCast(stream_index));
90
+ assert(key_from_value(&value_other) == key);
91
+
92
+ // Differently from K-way merge, there's no precedence between streams
93
+ // in Zig-Zag merge. It's assumed that all streams will produce the same
94
+ // value during a key intersection.
95
+ assert(stdx.equal_bytes(Value, &value, &value_other));
96
+ }
97
+
98
+ return value;
99
+ }
100
+
101
+ fn peek_key(it: *ZigZagMergeIterator) Pending!?Key {
102
+ assert(it.streams_count <= streams_max);
103
+ assert(it.streams_count > 1);
104
+
105
+ const key_min: Key = switch (it.direction) {
106
+ .ascending => 0,
107
+ .descending => std.math.maxInt(Key),
108
+ };
109
+
110
+ var pending: BitSet = BitSet.initEmpty();
111
+ var probe_key: Key = key_min;
112
+
113
+ var probing: BitSet = BitSet.initFull();
114
+ while (probing.count() > 0) {
115
+ // Looking into all non-pending streams for a match, while accumulating
116
+ // the most ahead key to probe the streams behind.
117
+ probing = BitSet.initEmpty();
118
+ for (0..it.streams_count) |stream_index| {
119
+ if (pending.isSet(stream_index)) continue;
120
+
121
+ const key = stream_peek(it.context, @intCast(stream_index)) catch |err| {
122
+ switch (err) {
123
+ // Skipping `Pending` streams. The goal is to match all buffered streams
124
+ // first so that the pending ones can read from a narrower key range.
125
+ error.Pending => {
126
+ pending.set(stream_index);
127
+ continue;
128
+ },
129
+ }
130
+ } orelse
131
+ // Return immediately on empty streams.
132
+ // If any one stream is empty, then there can be no value remaining
133
+ // in the intersection.
134
+ return null;
135
+
136
+ // The stream cannot regress.
137
+ assert(
138
+ it.probe_key_previous == null or
139
+ key == it.probe_key_previous.? or
140
+ it.key_ahead(.{
141
+ .key_after = key,
142
+ .key_before = it.probe_key_previous.?,
143
+ }),
144
+ );
145
+
146
+ // The keys match, continuing to the next stream.
147
+ if (key == probe_key) continue;
148
+
149
+ if (it.key_ahead(.{ .key_after = key, .key_before = probe_key })) {
150
+ // The stream is ahead, it will be the probe key,
151
+ // meaning all streams before must be probed.
152
+ probe_key = key;
153
+
154
+ // Setting all previous streams as `true` except the pending ones.
155
+ probing.setRangeValue(.{ .start = 0, .end = stream_index }, true);
156
+ probing.setIntersection(pending.complement());
157
+ assert(!probing.isSet(stream_index));
158
+ } else {
159
+ // The stream is behind and needs to be probed.
160
+ probing.set(stream_index);
161
+ }
162
+ }
163
+
164
+ // Probing the buffered streams that did not match the key.
165
+ var probing_iterator = probing.iterator(.{ .kind = .set });
166
+ while (probing_iterator.next()) |stream_index| {
167
+ stream_probe(it.context, @intCast(stream_index), probe_key);
168
+
169
+ const key = stream_peek(it.context, @intCast(stream_index)) catch |err| {
170
+ switch (err) {
171
+ error.Pending => {
172
+ pending.set(stream_index);
173
+ probing.unset(stream_index);
174
+ continue;
175
+ },
176
+ }
177
+ } orelse return null;
178
+
179
+ // After probed, the stream must either match the key or be ahead.
180
+ if (key == probe_key) {
181
+ probing.unset(stream_index);
182
+ } else {
183
+ assert(it.key_ahead(.{ .key_after = key, .key_before = probe_key }));
184
+ }
185
+ }
186
+ }
187
+
188
+ if (pending.count() == it.streams_count) {
189
+ // Can't probe if all streams are pending.
190
+ assert(probe_key == key_min);
191
+ return error.Pending;
192
+ }
193
+
194
+ assert(probe_key != key_min);
195
+ for (0..it.streams_count) |stream_index| {
196
+ if (pending.isSet(stream_index)) {
197
+ // Probing the pending stream will update the key range for the next read.
198
+ stream_probe(it.context, @intCast(stream_index), probe_key);
199
+ // The stream must remain pending after being probed.
200
+ assert(stream_peek(it.context, @intCast(stream_index)) == Pending.Pending);
201
+ } else {
202
+ // At this point, all the buffered streams must have produced a matching key.
203
+ assert((stream_peek(it.context, @intCast(stream_index)) catch unreachable) ==
204
+ probe_key);
205
+ }
206
+ }
207
+
208
+ // The iterator cannot regress.
209
+ assert(it.probe_key_previous == null or
210
+ probe_key == it.probe_key_previous.? or
211
+ it.key_ahead(.{ .key_after = probe_key, .key_before = it.probe_key_previous.? }));
212
+
213
+ it.probe_key_previous = probe_key;
214
+ return if (pending.count() == 0) probe_key else error.Pending;
215
+ }
216
+
217
+ /// Returns true if `key_after` is ahead of `key_before` depending on the direction,
218
+ /// that is `key_after > key_before` (ascending) or `key_after < key_before` (descending).
219
+ inline fn key_ahead(
220
+ it: *const ZigZagMergeIterator,
221
+ keys: struct { key_after: Key, key_before: Key },
222
+ ) bool {
223
+ return switch (it.direction) {
224
+ .ascending => keys.key_after > keys.key_before,
225
+ .descending => keys.key_after < keys.key_before,
226
+ };
227
+ }
228
+ };
229
+ }
230
+
231
+ fn TestContextType(comptime streams_max: u32) type {
232
+ const testing = std.testing;
233
+
234
+ return struct {
235
+ const TestContext = @This();
236
+
237
+ // Using `u128` simplifies the fuzzer, avoiding undesirable matches
238
+ // and duplicate elements when generating random values.
239
+ const Key = u128;
240
+ const Value = u128;
241
+
242
+ inline fn key_from_value(value: *const Value) Key {
243
+ return value.*;
244
+ }
245
+
246
+ streams: [streams_max][]const Value,
247
+ direction: Direction,
248
+
249
+ fn stream_peek(
250
+ context: *const TestContext,
251
+ stream_index: u32,
252
+ ) Pending!?Key {
253
+ const stream = context.streams[stream_index];
254
+ if (stream.len == 0) return null;
255
+ return switch (context.direction) {
256
+ .ascending => key_from_value(&stream[0]),
257
+ .descending => key_from_value(&stream[stream.len - 1]),
258
+ };
259
+ }
260
+
261
+ fn stream_pop(context: *TestContext, stream_index: u32) Value {
262
+ const stream = context.streams[stream_index];
263
+
264
+ switch (context.direction) {
265
+ .ascending => {
266
+ context.streams[stream_index] = stream[1..];
267
+ return stream[0];
268
+ },
269
+ .descending => {
270
+ context.streams[stream_index] = stream[0 .. stream.len - 1];
271
+ return stream[stream.len - 1];
272
+ },
273
+ }
274
+ }
275
+
276
+ fn stream_probe(context: *TestContext, stream_index: u32, probe_key: Key) void {
277
+ while (true) {
278
+ const key = stream_peek(context, stream_index) catch |err| switch (err) {
279
+ error.Pending => unreachable,
280
+ } orelse return;
281
+
282
+ if (switch (context.direction) {
283
+ .ascending => key >= probe_key,
284
+ .descending => key <= probe_key,
285
+ }) break;
286
+
287
+ const value = stream_pop(context, stream_index);
288
+ assert(key == key_from_value(&value));
289
+ }
290
+ }
291
+
292
+ fn merge(
293
+ streams: []const []const Value,
294
+ expect: []const Value,
295
+ ) !void {
296
+ const ZigZagMerge = ZigZagMergeIteratorType(
297
+ TestContext,
298
+ Key,
299
+ Value,
300
+ key_from_value,
301
+ streams_max,
302
+ stream_peek,
303
+ stream_pop,
304
+ stream_probe,
305
+ );
306
+
307
+ for (std.enums.values(Direction)) |direction| {
308
+ var actual = std.ArrayList(Value).init(testing.allocator);
309
+ defer actual.deinit();
310
+
311
+ var context: TestContext = .{
312
+ .streams = undefined,
313
+ .direction = direction,
314
+ };
315
+ for (streams, 0..) |stream, i| {
316
+ context.streams[i] = stream;
317
+ }
318
+
319
+ var it = ZigZagMerge.init(&context, @intCast(streams.len), direction);
320
+ while (try it.pop()) |value| {
321
+ try actual.append(value);
322
+ }
323
+
324
+ if (direction == .descending) std.mem.reverse(Value, actual.items);
325
+ try testing.expectEqualSlices(Value, expect, actual.items);
326
+ }
327
+ }
328
+
329
+ fn fuzz(prng: *stdx.PRNG, stream_key_count_max: u32) !void {
330
+ const allocator = testing.allocator;
331
+ var streams: [streams_max][]Value = undefined;
332
+
333
+ const streams_buffer = try allocator.alloc(Value, streams_max * stream_key_count_max);
334
+ defer allocator.free(streams_buffer);
335
+
336
+ const intersection_buffer = try allocator.alloc(Value, stream_key_count_max);
337
+ defer allocator.free(intersection_buffer);
338
+
339
+ const intersection_len_min = 5;
340
+ for (2..streams_max + 1) |streams_count| {
341
+ var stream_len_min: u32 = stream_key_count_max;
342
+ for (0..streams_count) |stream_index| {
343
+ const len = prng.range_inclusive(
344
+ u32,
345
+ intersection_len_min,
346
+ stream_key_count_max,
347
+ );
348
+ if (len < stream_len_min) stream_len_min = len;
349
+
350
+ streams[stream_index] =
351
+ streams_buffer[stream_index * stream_key_count_max ..][0..len];
352
+ }
353
+
354
+ const intersection = intersection_buffer[0..prng.range_inclusive(
355
+ u32,
356
+ intersection_len_min,
357
+ stream_len_min,
358
+ )];
359
+ assert(intersection.len >= intersection_len_min and
360
+ intersection.len <= stream_len_min);
361
+
362
+ fuzz_make_intersection(
363
+ prng,
364
+ streams[0..streams_count],
365
+ intersection,
366
+ );
367
+
368
+ // Positive space.
369
+ try merge(streams[0..streams_count], intersection);
370
+
371
+ // Negative space: disjoint stream.
372
+ {
373
+ var dummy: [10]Value = .{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
374
+ const replaced = streams[streams_count - 1];
375
+ defer streams[streams_count - 1] = replaced;
376
+
377
+ streams[streams_count - 1] = &dummy;
378
+ try merge(streams[0..streams_count], &.{});
379
+ }
380
+
381
+ // Negative space: empty stream.
382
+ {
383
+ const empty: [0]Value = .{};
384
+ const replaced = streams[streams_count - 1];
385
+ defer streams[streams_count - 1] = replaced;
386
+
387
+ streams[streams_count - 1] = &empty;
388
+ try merge(streams[0..streams_count], &.{});
389
+ }
390
+ }
391
+ }
392
+
393
+ fn fuzz_make_intersection(
394
+ prng: *stdx.PRNG,
395
+ streams: []const []Value,
396
+ intersection: []Value,
397
+ ) void {
398
+ const less_than = struct {
399
+ fn less_than(_: void, lhs: Value, rhs: Value) bool {
400
+ return lhs < rhs;
401
+ }
402
+ }.less_than;
403
+
404
+ // Starting with the values we want to be the intersection:
405
+ prng.fill(mem.sliceAsBytes(intersection));
406
+ std.mem.sort(
407
+ Value,
408
+ intersection,
409
+ {},
410
+ less_than,
411
+ );
412
+
413
+ // Then injecting the intersection into the each stream and filling the rest with
414
+ // random values:
415
+ for (streams) |stream| {
416
+ assert(intersection.len <= stream.len);
417
+ stdx.copy_disjoint(.exact, Value, stream[0..intersection.len], intersection);
418
+ if (stream.len > intersection.len) {
419
+ prng.fill(mem.sliceAsBytes(stream[intersection.len..]));
420
+ }
421
+ std.mem.sort(
422
+ Value,
423
+ stream,
424
+ {},
425
+ less_than,
426
+ );
427
+ }
428
+ }
429
+ };
430
+ }
431
+
432
+ test "zig_zag_merge: unit" {
433
+ const Context = TestContextType(10);
434
+
435
+ // Equal streams:
436
+ try Context.merge(
437
+ &[_][]const Context.Value{
438
+ &.{ 1, 2, 3, 4, 5 },
439
+ &.{ 1, 2, 3, 4, 5 },
440
+ &.{ 1, 2, 3, 4, 5 },
441
+ },
442
+ &.{ 1, 2, 3, 4, 5 },
443
+ );
444
+
445
+ // Disjoint streams:
446
+ try Context.merge(
447
+ &[_][]const Context.Value{
448
+ &.{ 1, 3, 5, 7, 9 },
449
+ &.{ 2, 4, 6, 8, 10 },
450
+ },
451
+ &.{},
452
+ );
453
+
454
+ // Equal and disjoint streams:
455
+ try Context.merge(
456
+ &[_][]const Context.Value{
457
+ &.{ 1, 3, 5, 7, 9 },
458
+ &.{ 1, 3, 5, 7, 9 },
459
+ &.{ 2, 4, 6, 8, 10 },
460
+ &.{ 2, 4, 6, 8, 10 },
461
+ },
462
+ &.{},
463
+ );
464
+
465
+ // Intersection with an empty stream:
466
+ try Context.merge(
467
+ &[_][]const Context.Value{
468
+ &.{ 2, 4, 6, 8, 10 },
469
+ &.{ 2, 4, 6, 8, 10 },
470
+ &.{},
471
+ },
472
+ &.{},
473
+ );
474
+
475
+ // Partial intersection:
476
+ try Context.merge(
477
+ &[_][]const Context.Value{
478
+ &.{ 1, 2, 3, 4, 5 },
479
+ &.{ 2, 3, 4, 5, 6 },
480
+ &.{ 3, 4, 5, 6, 7 },
481
+ &.{ 4, 5, 6, 7, 8 },
482
+ },
483
+ &.{ 4, 5 },
484
+ );
485
+
486
+ // Intersection with streams of different sizes:
487
+ try Context.merge(
488
+ &[_][]const Context.Value{
489
+ // {1, 2, 3, ..., 1000}.
490
+ comptime blk: {
491
+ @setEvalBranchQuota(2_000);
492
+ var array: [1000]Context.Value = undefined;
493
+ for (0..1000) |i| array[i] = @intCast(i + 1);
494
+ break :blk stdx.comptime_slice(&array, array.len);
495
+ },
496
+ // {10, 20, 30, ..., 1000}.
497
+ comptime blk: {
498
+ var array: [100]Context.Value = undefined;
499
+ for (0..100) |i| array[i] = @intCast(10 * (i + 1));
500
+ break :blk stdx.comptime_slice(&array, array.len);
501
+ },
502
+ // {1, 10, 100, 1000, ..., 10 ^ 10}.
503
+ comptime blk: {
504
+ var array: [10]Context.Value = undefined;
505
+ for (0..10) |i| array[i] = std.math.pow(Context.Value, 10, i);
506
+ break :blk stdx.comptime_slice(&array, array.len);
507
+ },
508
+ },
509
+ &.{ 10, 100, 1000 },
510
+ );
511
+
512
+ // Sparse matching values: {1, 2, 3, ..., 100} ∩ {100, 101, 102, ..., 199} = {100}.
513
+ try Context.merge(
514
+ &[_][]const Context.Value{
515
+ // {1, 2, 3, ..., 100}.
516
+ comptime blk: {
517
+ var array: [100]Context.Value = undefined;
518
+ for (0..100) |i| array[i] = @intCast(i + 1);
519
+ break :blk stdx.comptime_slice(&array, array.len);
520
+ },
521
+ // {100, 101, 102, ..., 199}.
522
+ comptime blk: {
523
+ var array: [100]Context.Value = undefined;
524
+ for (0..100) |i| array[i] = @intCast(i + 100);
525
+ break :blk stdx.comptime_slice(&array, array.len);
526
+ },
527
+ },
528
+ &.{100},
529
+ );
530
+
531
+ // Sparse matching values: {100, 101, 102, ..., 199} ∩ {1, 2, 3, ..., 100} = {100}.
532
+ try Context.merge(
533
+ &[_][]const Context.Value{
534
+ // {100, 101, 102, ..., 199}.
535
+ comptime blk: {
536
+ var array: [100]Context.Value = undefined;
537
+ for (0..100) |i| array[i] = @intCast(i + 100);
538
+ break :blk stdx.comptime_slice(&array, array.len);
539
+ },
540
+ // {1, 2, 3, ..., 100}.
541
+ comptime blk: {
542
+ var array: [100]Context.Value = undefined;
543
+ for (0..100) |i| array[i] = @intCast(i + 1);
544
+ break :blk stdx.comptime_slice(&array, array.len);
545
+ },
546
+ },
547
+ &.{100},
548
+ );
549
+ }
550
+
551
+ test "zig_zag_merge: fuzz" {
552
+ const seed = std.crypto.random.int(u64);
553
+ errdefer std.debug.print("\nTEST FAILED: seed = {}\n", .{seed});
554
+
555
+ var prng = stdx.PRNG.from_seed(seed);
556
+ try TestContextType(32).fuzz(&prng, 256);
557
+ }