tigerbeetle 0.0.40 → 0.17.8

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 (293) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +0 -25
  3. data/README.md +670 -80
  4. data/docs/migration.md +201 -0
  5. data/sig/tigerbeetle.rbs +271 -0
  6. data/src/ext/tigerbeetle/extconf.rb +47 -0
  7. data/src/ext/tigerbeetle/lib/aarch64-linux-gnu.2.27/libtb_client.so +0 -0
  8. data/src/ext/tigerbeetle/lib/aarch64-linux-musl/libtb_client.so +0 -0
  9. data/src/ext/tigerbeetle/lib/aarch64-macos/libtb_client.dylib +0 -0
  10. data/src/ext/tigerbeetle/lib/x86_64-linux-gnu.2.27/libtb_client.so +0 -0
  11. data/src/ext/tigerbeetle/lib/x86_64-linux-musl/libtb_client.so +0 -0
  12. data/src/ext/tigerbeetle/lib/x86_64-macos/libtb_client.dylib +0 -0
  13. data/src/ext/tigerbeetle/lib/x86_64-windows/tb_client.dll +0 -0
  14. data/src/ext/tigerbeetle/rb_tb_gen.h +458 -0
  15. data/{ext/tb_client/tigerbeetle/src/clients/rust/assets → src/ext/tigerbeetle}/tb_client.h +18 -16
  16. data/src/ext/tigerbeetle/tigerbeetle.c +310 -0
  17. data/src/tigerbeetle/bindings.rb +347 -0
  18. data/src/tigerbeetle/client.rb +129 -0
  19. data/src/tigerbeetle/completion_dispatcher.rb +108 -0
  20. data/src/tigerbeetle/id.rb +40 -0
  21. data/src/tigerbeetle/tb.rb +3 -0
  22. data/src/tigerbeetle/version.rb +3 -0
  23. data/src/tigerbeetle.rb +39 -0
  24. metadata +33 -350
  25. data/CHANGELOG.md +0 -162
  26. data/ext/tb_client/extconf.rb +0 -41
  27. data/ext/tb_client/tigerbeetle/LICENSE +0 -177
  28. data/ext/tb_client/tigerbeetle/build.zig +0 -2296
  29. data/ext/tb_client/tigerbeetle/src/aof.zig +0 -1000
  30. data/ext/tb_client/tigerbeetle/src/build/fetch.zig +0 -112
  31. data/ext/tb_client/tigerbeetle/src/build_multiversion.zig +0 -808
  32. data/ext/tb_client/tigerbeetle/src/cdc/amqp/protocol.zig +0 -1283
  33. data/ext/tb_client/tigerbeetle/src/cdc/amqp/spec.zig +0 -1704
  34. data/ext/tb_client/tigerbeetle/src/cdc/amqp/types.zig +0 -341
  35. data/ext/tb_client/tigerbeetle/src/cdc/amqp.zig +0 -1450
  36. data/ext/tb_client/tigerbeetle/src/cdc/runner.zig +0 -1659
  37. data/ext/tb_client/tigerbeetle/src/clients/c/samples/main.c +0 -406
  38. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/context.zig +0 -1092
  39. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/echo_client.zig +0 -286
  40. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/packet.zig +0 -158
  41. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal.zig +0 -229
  42. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal_fuzz.zig +0 -110
  43. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.h +0 -386
  44. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.zig +0 -34
  45. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_exports.zig +0 -281
  46. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header.zig +0 -312
  47. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header_test.zig +0 -138
  48. data/ext/tb_client/tigerbeetle/src/clients/c/test.zig +0 -466
  49. data/ext/tb_client/tigerbeetle/src/clients/docs_samples.zig +0 -157
  50. data/ext/tb_client/tigerbeetle/src/clients/docs_types.zig +0 -90
  51. data/ext/tb_client/tigerbeetle/src/clients/dotnet/ci.zig +0 -203
  52. data/ext/tb_client/tigerbeetle/src/clients/dotnet/docs.zig +0 -79
  53. data/ext/tb_client/tigerbeetle/src/clients/dotnet/dotnet_bindings.zig +0 -542
  54. data/ext/tb_client/tigerbeetle/src/clients/go/ci.zig +0 -109
  55. data/ext/tb_client/tigerbeetle/src/clients/go/docs.zig +0 -86
  56. data/ext/tb_client/tigerbeetle/src/clients/go/go_bindings.zig +0 -370
  57. data/ext/tb_client/tigerbeetle/src/clients/go/pkg/native/tb_client.h +0 -386
  58. data/ext/tb_client/tigerbeetle/src/clients/java/ci.zig +0 -167
  59. data/ext/tb_client/tigerbeetle/src/clients/java/docs.zig +0 -126
  60. data/ext/tb_client/tigerbeetle/src/clients/java/java_bindings.zig +0 -996
  61. data/ext/tb_client/tigerbeetle/src/clients/java/src/client.zig +0 -748
  62. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni.zig +0 -3238
  63. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_tests.zig +0 -1718
  64. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_thread_cleaner.zig +0 -190
  65. data/ext/tb_client/tigerbeetle/src/clients/node/ci.zig +0 -104
  66. data/ext/tb_client/tigerbeetle/src/clients/node/docs.zig +0 -75
  67. data/ext/tb_client/tigerbeetle/src/clients/node/node.zig +0 -522
  68. data/ext/tb_client/tigerbeetle/src/clients/node/node_bindings.zig +0 -267
  69. data/ext/tb_client/tigerbeetle/src/clients/node/src/c.zig +0 -3
  70. data/ext/tb_client/tigerbeetle/src/clients/node/src/translate.zig +0 -379
  71. data/ext/tb_client/tigerbeetle/src/clients/python/ci.zig +0 -131
  72. data/ext/tb_client/tigerbeetle/src/clients/python/docs.zig +0 -63
  73. data/ext/tb_client/tigerbeetle/src/clients/python/python_bindings.zig +0 -588
  74. data/ext/tb_client/tigerbeetle/src/clients/rust/ci.zig +0 -73
  75. data/ext/tb_client/tigerbeetle/src/clients/rust/docs.zig +0 -106
  76. data/ext/tb_client/tigerbeetle/src/clients/rust/rust_bindings.zig +0 -305
  77. data/ext/tb_client/tigerbeetle/src/config.zig +0 -296
  78. data/ext/tb_client/tigerbeetle/src/constants.zig +0 -790
  79. data/ext/tb_client/tigerbeetle/src/copyhound.zig +0 -202
  80. data/ext/tb_client/tigerbeetle/src/counting_allocator.zig +0 -72
  81. data/ext/tb_client/tigerbeetle/src/direction.zig +0 -120
  82. data/ext/tb_client/tigerbeetle/src/docs_website/build.zig +0 -158
  83. data/ext/tb_client/tigerbeetle/src/docs_website/src/content.zig +0 -156
  84. data/ext/tb_client/tigerbeetle/src/docs_website/src/docs.zig +0 -252
  85. data/ext/tb_client/tigerbeetle/src/docs_website/src/file_checker.zig +0 -313
  86. data/ext/tb_client/tigerbeetle/src/docs_website/src/html.zig +0 -87
  87. data/ext/tb_client/tigerbeetle/src/docs_website/src/page_writer.zig +0 -63
  88. data/ext/tb_client/tigerbeetle/src/docs_website/src/redirects.zig +0 -47
  89. data/ext/tb_client/tigerbeetle/src/docs_website/src/search_index_writer.zig +0 -28
  90. data/ext/tb_client/tigerbeetle/src/docs_website/src/service_worker_writer.zig +0 -61
  91. data/ext/tb_client/tigerbeetle/src/docs_website/src/single_page_writer.zig +0 -169
  92. data/ext/tb_client/tigerbeetle/src/docs_website/src/website.zig +0 -46
  93. data/ext/tb_client/tigerbeetle/src/ewah.zig +0 -445
  94. data/ext/tb_client/tigerbeetle/src/ewah_benchmark.zig +0 -128
  95. data/ext/tb_client/tigerbeetle/src/ewah_fuzz.zig +0 -171
  96. data/ext/tb_client/tigerbeetle/src/fuzz_tests.zig +0 -179
  97. data/ext/tb_client/tigerbeetle/src/integration_tests.zig +0 -662
  98. data/ext/tb_client/tigerbeetle/src/io/common.zig +0 -155
  99. data/ext/tb_client/tigerbeetle/src/io/darwin.zig +0 -1093
  100. data/ext/tb_client/tigerbeetle/src/io/linux.zig +0 -1880
  101. data/ext/tb_client/tigerbeetle/src/io/test.zig +0 -1005
  102. data/ext/tb_client/tigerbeetle/src/io/windows.zig +0 -1598
  103. data/ext/tb_client/tigerbeetle/src/io.zig +0 -34
  104. data/ext/tb_client/tigerbeetle/src/iops.zig +0 -134
  105. data/ext/tb_client/tigerbeetle/src/list.zig +0 -236
  106. data/ext/tb_client/tigerbeetle/src/lsm/binary_search.zig +0 -848
  107. data/ext/tb_client/tigerbeetle/src/lsm/binary_search_benchmark.zig +0 -179
  108. data/ext/tb_client/tigerbeetle/src/lsm/cache_map.zig +0 -424
  109. data/ext/tb_client/tigerbeetle/src/lsm/cache_map_fuzz.zig +0 -420
  110. data/ext/tb_client/tigerbeetle/src/lsm/compaction.zig +0 -2114
  111. data/ext/tb_client/tigerbeetle/src/lsm/composite_key.zig +0 -185
  112. data/ext/tb_client/tigerbeetle/src/lsm/forest.zig +0 -1146
  113. data/ext/tb_client/tigerbeetle/src/lsm/forest_fuzz.zig +0 -1102
  114. data/ext/tb_client/tigerbeetle/src/lsm/forest_table_iterator.zig +0 -200
  115. data/ext/tb_client/tigerbeetle/src/lsm/groove.zig +0 -1495
  116. data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge.zig +0 -739
  117. data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge_benchmark.zig +0 -166
  118. data/ext/tb_client/tigerbeetle/src/lsm/manifest.zig +0 -754
  119. data/ext/tb_client/tigerbeetle/src/lsm/manifest_level.zig +0 -1294
  120. data/ext/tb_client/tigerbeetle/src/lsm/manifest_level_fuzz.zig +0 -510
  121. data/ext/tb_client/tigerbeetle/src/lsm/manifest_log.zig +0 -1241
  122. data/ext/tb_client/tigerbeetle/src/lsm/manifest_log_fuzz.zig +0 -628
  123. data/ext/tb_client/tigerbeetle/src/lsm/node_pool.zig +0 -247
  124. data/ext/tb_client/tigerbeetle/src/lsm/scan_buffer.zig +0 -116
  125. data/ext/tb_client/tigerbeetle/src/lsm/scan_builder.zig +0 -543
  126. data/ext/tb_client/tigerbeetle/src/lsm/scan_fuzz.zig +0 -938
  127. data/ext/tb_client/tigerbeetle/src/lsm/scan_lookup.zig +0 -293
  128. data/ext/tb_client/tigerbeetle/src/lsm/scan_merge.zig +0 -359
  129. data/ext/tb_client/tigerbeetle/src/lsm/scan_range.zig +0 -99
  130. data/ext/tb_client/tigerbeetle/src/lsm/scan_state.zig +0 -17
  131. data/ext/tb_client/tigerbeetle/src/lsm/scan_tree.zig +0 -962
  132. data/ext/tb_client/tigerbeetle/src/lsm/schema.zig +0 -617
  133. data/ext/tb_client/tigerbeetle/src/lsm/scratch_memory.zig +0 -84
  134. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array.zig +0 -1500
  135. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_benchmark.zig +0 -149
  136. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_fuzz.zig +0 -7
  137. data/ext/tb_client/tigerbeetle/src/lsm/set_associative_cache.zig +0 -865
  138. data/ext/tb_client/tigerbeetle/src/lsm/table.zig +0 -607
  139. data/ext/tb_client/tigerbeetle/src/lsm/table_memory.zig +0 -843
  140. data/ext/tb_client/tigerbeetle/src/lsm/table_value_iterator.zig +0 -90
  141. data/ext/tb_client/tigerbeetle/src/lsm/timestamp_range.zig +0 -40
  142. data/ext/tb_client/tigerbeetle/src/lsm/tree.zig +0 -629
  143. data/ext/tb_client/tigerbeetle/src/lsm/tree_fuzz.zig +0 -933
  144. data/ext/tb_client/tigerbeetle/src/lsm/zig_zag_merge.zig +0 -534
  145. data/ext/tb_client/tigerbeetle/src/message_buffer.zig +0 -469
  146. data/ext/tb_client/tigerbeetle/src/message_bus.zig +0 -1219
  147. data/ext/tb_client/tigerbeetle/src/message_bus_fuzz.zig +0 -936
  148. data/ext/tb_client/tigerbeetle/src/message_pool.zig +0 -343
  149. data/ext/tb_client/tigerbeetle/src/multiversion.zig +0 -2195
  150. data/ext/tb_client/tigerbeetle/src/queue.zig +0 -390
  151. data/ext/tb_client/tigerbeetle/src/repl/completion.zig +0 -201
  152. data/ext/tb_client/tigerbeetle/src/repl/parser.zig +0 -1356
  153. data/ext/tb_client/tigerbeetle/src/repl/terminal.zig +0 -496
  154. data/ext/tb_client/tigerbeetle/src/repl.zig +0 -1034
  155. data/ext/tb_client/tigerbeetle/src/scripts/amqp.zig +0 -973
  156. data/ext/tb_client/tigerbeetle/src/scripts/cfo.zig +0 -1866
  157. data/ext/tb_client/tigerbeetle/src/scripts/changelog.zig +0 -304
  158. data/ext/tb_client/tigerbeetle/src/scripts/ci.zig +0 -227
  159. data/ext/tb_client/tigerbeetle/src/scripts/client_readmes.zig +0 -658
  160. data/ext/tb_client/tigerbeetle/src/scripts/devhub.zig +0 -466
  161. data/ext/tb_client/tigerbeetle/src/scripts/release.zig +0 -1058
  162. data/ext/tb_client/tigerbeetle/src/scripts.zig +0 -105
  163. data/ext/tb_client/tigerbeetle/src/shell.zig +0 -1195
  164. data/ext/tb_client/tigerbeetle/src/stack.zig +0 -260
  165. data/ext/tb_client/tigerbeetle/src/state_machine/auditor.zig +0 -911
  166. data/ext/tb_client/tigerbeetle/src/state_machine/workload.zig +0 -2079
  167. data/ext/tb_client/tigerbeetle/src/state_machine.zig +0 -4872
  168. data/ext/tb_client/tigerbeetle/src/state_machine_fuzz.zig +0 -288
  169. data/ext/tb_client/tigerbeetle/src/state_machine_tests.zig +0 -3128
  170. data/ext/tb_client/tigerbeetle/src/static_allocator.zig +0 -82
  171. data/ext/tb_client/tigerbeetle/src/stdx/bit_set.zig +0 -157
  172. data/ext/tb_client/tigerbeetle/src/stdx/bounded_array.zig +0 -292
  173. data/ext/tb_client/tigerbeetle/src/stdx/debug.zig +0 -65
  174. data/ext/tb_client/tigerbeetle/src/stdx/flags.zig +0 -1414
  175. data/ext/tb_client/tigerbeetle/src/stdx/huge_page_allocator.zig +0 -115
  176. data/ext/tb_client/tigerbeetle/src/stdx/mlock.zig +0 -92
  177. data/ext/tb_client/tigerbeetle/src/stdx/prng.zig +0 -677
  178. data/ext/tb_client/tigerbeetle/src/stdx/radix.zig +0 -336
  179. data/ext/tb_client/tigerbeetle/src/stdx/ring_buffer.zig +0 -511
  180. data/ext/tb_client/tigerbeetle/src/stdx/sort_test.zig +0 -112
  181. data/ext/tb_client/tigerbeetle/src/stdx/stdx.zig +0 -1163
  182. data/ext/tb_client/tigerbeetle/src/stdx/testing/low_level_hash_vectors.zig +0 -142
  183. data/ext/tb_client/tigerbeetle/src/stdx/testing/snaptest.zig +0 -361
  184. data/ext/tb_client/tigerbeetle/src/stdx/time_units.zig +0 -275
  185. data/ext/tb_client/tigerbeetle/src/stdx/unshare.zig +0 -295
  186. data/ext/tb_client/tigerbeetle/src/stdx/vendored/aegis.zig +0 -436
  187. data/ext/tb_client/tigerbeetle/src/stdx/windows.zig +0 -48
  188. data/ext/tb_client/tigerbeetle/src/stdx/zipfian.zig +0 -402
  189. data/ext/tb_client/tigerbeetle/src/storage.zig +0 -489
  190. data/ext/tb_client/tigerbeetle/src/storage_fuzz.zig +0 -180
  191. data/ext/tb_client/tigerbeetle/src/testing/bench.zig +0 -146
  192. data/ext/tb_client/tigerbeetle/src/testing/cluster/grid_checker.zig +0 -53
  193. data/ext/tb_client/tigerbeetle/src/testing/cluster/journal_checker.zig +0 -61
  194. data/ext/tb_client/tigerbeetle/src/testing/cluster/manifest_checker.zig +0 -76
  195. data/ext/tb_client/tigerbeetle/src/testing/cluster/message_bus.zig +0 -110
  196. data/ext/tb_client/tigerbeetle/src/testing/cluster/network.zig +0 -412
  197. data/ext/tb_client/tigerbeetle/src/testing/cluster/state_checker.zig +0 -331
  198. data/ext/tb_client/tigerbeetle/src/testing/cluster/storage_checker.zig +0 -458
  199. data/ext/tb_client/tigerbeetle/src/testing/cluster.zig +0 -1198
  200. data/ext/tb_client/tigerbeetle/src/testing/exhaustigen.zig +0 -128
  201. data/ext/tb_client/tigerbeetle/src/testing/fixtures.zig +0 -181
  202. data/ext/tb_client/tigerbeetle/src/testing/fuzz.zig +0 -144
  203. data/ext/tb_client/tigerbeetle/src/testing/id.zig +0 -97
  204. data/ext/tb_client/tigerbeetle/src/testing/io.zig +0 -317
  205. data/ext/tb_client/tigerbeetle/src/testing/marks.zig +0 -126
  206. data/ext/tb_client/tigerbeetle/src/testing/packet_simulator.zig +0 -533
  207. data/ext/tb_client/tigerbeetle/src/testing/reply_sequence.zig +0 -154
  208. data/ext/tb_client/tigerbeetle/src/testing/state_machine.zig +0 -389
  209. data/ext/tb_client/tigerbeetle/src/testing/storage.zig +0 -1247
  210. data/ext/tb_client/tigerbeetle/src/testing/table.zig +0 -249
  211. data/ext/tb_client/tigerbeetle/src/testing/time.zig +0 -98
  212. data/ext/tb_client/tigerbeetle/src/testing/tmp_tigerbeetle.zig +0 -212
  213. data/ext/tb_client/tigerbeetle/src/testing/vortex/constants.zig +0 -26
  214. data/ext/tb_client/tigerbeetle/src/testing/vortex/faulty_network.zig +0 -579
  215. data/ext/tb_client/tigerbeetle/src/testing/vortex/java_driver/ci.zig +0 -39
  216. data/ext/tb_client/tigerbeetle/src/testing/vortex/logged_process.zig +0 -214
  217. data/ext/tb_client/tigerbeetle/src/testing/vortex/rust_driver/ci.zig +0 -34
  218. data/ext/tb_client/tigerbeetle/src/testing/vortex/supervisor.zig +0 -785
  219. data/ext/tb_client/tigerbeetle/src/testing/vortex/workload.zig +0 -543
  220. data/ext/tb_client/tigerbeetle/src/testing/vortex/zig_driver.zig +0 -181
  221. data/ext/tb_client/tigerbeetle/src/tidy.zig +0 -1449
  222. data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_driver.zig +0 -227
  223. data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_load.zig +0 -1069
  224. data/ext/tb_client/tigerbeetle/src/tigerbeetle/cli.zig +0 -1422
  225. data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect.zig +0 -1658
  226. data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect_integrity.zig +0 -518
  227. data/ext/tb_client/tigerbeetle/src/tigerbeetle/libtb_client.zig +0 -36
  228. data/ext/tb_client/tigerbeetle/src/tigerbeetle/main.zig +0 -646
  229. data/ext/tb_client/tigerbeetle/src/tigerbeetle.zig +0 -958
  230. data/ext/tb_client/tigerbeetle/src/time.zig +0 -236
  231. data/ext/tb_client/tigerbeetle/src/trace/event.zig +0 -745
  232. data/ext/tb_client/tigerbeetle/src/trace/statsd.zig +0 -462
  233. data/ext/tb_client/tigerbeetle/src/trace.zig +0 -556
  234. data/ext/tb_client/tigerbeetle/src/unit_tests.zig +0 -321
  235. data/ext/tb_client/tigerbeetle/src/vopr.zig +0 -1785
  236. data/ext/tb_client/tigerbeetle/src/vortex.zig +0 -101
  237. data/ext/tb_client/tigerbeetle/src/vsr/checkpoint_trailer.zig +0 -473
  238. data/ext/tb_client/tigerbeetle/src/vsr/checksum.zig +0 -208
  239. data/ext/tb_client/tigerbeetle/src/vsr/checksum_benchmark.zig +0 -43
  240. data/ext/tb_client/tigerbeetle/src/vsr/client.zig +0 -768
  241. data/ext/tb_client/tigerbeetle/src/vsr/client_replies.zig +0 -532
  242. data/ext/tb_client/tigerbeetle/src/vsr/client_sessions.zig +0 -338
  243. data/ext/tb_client/tigerbeetle/src/vsr/clock.zig +0 -1019
  244. data/ext/tb_client/tigerbeetle/src/vsr/fault_detector.zig +0 -279
  245. data/ext/tb_client/tigerbeetle/src/vsr/free_set.zig +0 -1381
  246. data/ext/tb_client/tigerbeetle/src/vsr/free_set_fuzz.zig +0 -315
  247. data/ext/tb_client/tigerbeetle/src/vsr/grid.zig +0 -1460
  248. data/ext/tb_client/tigerbeetle/src/vsr/grid_blocks_missing.zig +0 -757
  249. data/ext/tb_client/tigerbeetle/src/vsr/grid_scrubber.zig +0 -797
  250. data/ext/tb_client/tigerbeetle/src/vsr/journal.zig +0 -2586
  251. data/ext/tb_client/tigerbeetle/src/vsr/marzullo.zig +0 -308
  252. data/ext/tb_client/tigerbeetle/src/vsr/message_header.zig +0 -1777
  253. data/ext/tb_client/tigerbeetle/src/vsr/multi_batch.zig +0 -715
  254. data/ext/tb_client/tigerbeetle/src/vsr/multi_batch_fuzz.zig +0 -185
  255. data/ext/tb_client/tigerbeetle/src/vsr/repair_budget.zig +0 -333
  256. data/ext/tb_client/tigerbeetle/src/vsr/replica.zig +0 -12356
  257. data/ext/tb_client/tigerbeetle/src/vsr/replica_format.zig +0 -416
  258. data/ext/tb_client/tigerbeetle/src/vsr/replica_reformat.zig +0 -165
  259. data/ext/tb_client/tigerbeetle/src/vsr/replica_test.zig +0 -2928
  260. data/ext/tb_client/tigerbeetle/src/vsr/routing.zig +0 -1075
  261. data/ext/tb_client/tigerbeetle/src/vsr/superblock.zig +0 -1603
  262. data/ext/tb_client/tigerbeetle/src/vsr/superblock_fuzz.zig +0 -484
  263. data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums.zig +0 -405
  264. data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +0 -355
  265. data/ext/tb_client/tigerbeetle/src/vsr/sync.zig +0 -29
  266. data/ext/tb_client/tigerbeetle/src/vsr.zig +0 -1727
  267. data/lib/tb_client/shared_lib.rb +0 -66
  268. data/lib/tb_client.rb +0 -282
  269. data/lib/tigerbeetle/account.rb +0 -38
  270. data/lib/tigerbeetle/account_balance.rb +0 -23
  271. data/lib/tigerbeetle/account_filter.rb +0 -31
  272. data/lib/tigerbeetle/atomic_counter.rb +0 -14
  273. data/lib/tigerbeetle/client.rb +0 -214
  274. data/lib/tigerbeetle/converters/account.rb +0 -63
  275. data/lib/tigerbeetle/converters/account_balance.rb +0 -31
  276. data/lib/tigerbeetle/converters/account_filter.rb +0 -32
  277. data/lib/tigerbeetle/converters/base.rb +0 -35
  278. data/lib/tigerbeetle/converters/create_accounts_result.rb +0 -21
  279. data/lib/tigerbeetle/converters/create_transfers_result.rb +0 -21
  280. data/lib/tigerbeetle/converters/query_filter.rb +0 -33
  281. data/lib/tigerbeetle/converters/time.rb +0 -23
  282. data/lib/tigerbeetle/converters/transfer.rb +0 -64
  283. data/lib/tigerbeetle/converters/uint_128.rb +0 -24
  284. data/lib/tigerbeetle/converters.rb +0 -12
  285. data/lib/tigerbeetle/error.rb +0 -4
  286. data/lib/tigerbeetle/id.rb +0 -30
  287. data/lib/tigerbeetle/platforms.rb +0 -9
  288. data/lib/tigerbeetle/query_filter.rb +0 -31
  289. data/lib/tigerbeetle/request.rb +0 -7
  290. data/lib/tigerbeetle/transfer.rb +0 -40
  291. data/lib/tigerbeetle/version.rb +0 -4
  292. data/lib/tigerbeetle.rb +0 -13
  293. data/tigerbeetle.gemspec +0 -60
@@ -1,1500 +0,0 @@
1
- const std = @import("std");
2
-
3
- const assert = std.debug.assert;
4
- const math = std.math;
5
- const mem = std.mem;
6
-
7
- const stdx = @import("stdx");
8
- const div_ceil = stdx.div_ceil;
9
- const binary_search_values_upsert_index =
10
- @import("binary_search.zig").binary_search_values_upsert_index;
11
- const binary_search_keys = @import("binary_search.zig").binary_search_keys;
12
- const Direction = @import("../direction.zig").Direction;
13
-
14
- /// A "segmented array" is an array with efficient (amortized) random-insert/remove operations.
15
- /// Also known as an "unrolled linked list": https://en.wikipedia.org/wiki/Unrolled_linked_list
16
- ///
17
- /// The structure consists of an array list of "nodes". Each node is a non-empty array of T.
18
- /// When a node fills, it is split into two adjacent, partially-full nodes.
19
- /// When a node empties, it is joined with a nearby node.
20
- ///
21
- /// An absolute index is offset from the start of the segmented array.
22
- /// A relative index is offset from the start of a node.
23
- pub fn SegmentedArrayType(
24
- comptime T: type,
25
- comptime NodePool: type,
26
- comptime element_count_max: u32,
27
- comptime options: Options,
28
- ) type {
29
- return SegmentedArrayBaseType(T, NodePool, element_count_max, null, {}, options);
30
- }
31
-
32
- pub fn SortedSegmentedArrayType(
33
- comptime T: type,
34
- comptime NodePool: type,
35
- comptime element_count_max: u32,
36
- comptime Key: type,
37
- comptime key_from_value: fn (*const T) callconv(.@"inline") Key,
38
- comptime options: Options,
39
- ) type {
40
- return SegmentedArrayBaseType(T, NodePool, element_count_max, Key, key_from_value, options);
41
- }
42
-
43
- pub const Options = struct {
44
- /// Assert all invariants before/after every public function.
45
- /// Very expensive - only enable for debugging/fuzzing.
46
- verify: bool = false,
47
- };
48
-
49
- fn SegmentedArrayBaseType(
50
- comptime T: type,
51
- comptime NodePool: type,
52
- comptime element_count_max: u32,
53
- // Set when the SegmentedArray is ordered:
54
- comptime Key: ?type,
55
- comptime key_from_value: if (Key) |K| (fn (*const T) callconv(.@"inline") K) else void,
56
- comptime options: Options,
57
- ) type {
58
- comptime assert(Key == null or @typeInfo(Key.?) == .int or @typeInfo(Key.?) == .comptime_int);
59
-
60
- return struct {
61
- const SegmentedArray = @This();
62
-
63
- pub const Cursor = struct {
64
- node: u32,
65
- relative_index: u32,
66
- };
67
-
68
- // We can't use @divExact() here as we store TableInfo structs of various sizes in this
69
- // data structure. This means that there may be padding at the end of the node.
70
- pub const node_capacity = blk: {
71
- const max = @divFloor(NodePool.node_size, @sizeOf(T));
72
-
73
- // We require that the node capacity is evenly divisible by 2 to simplify our code
74
- // that splits/joins nodes at the midpoint.
75
- const capacity = if (max % 2 == 0) max else max - 1;
76
-
77
- assert(capacity >= 2);
78
- assert(capacity % 2 == 0);
79
- break :blk capacity;
80
- };
81
-
82
- comptime {
83
- // If this assert fails, we should be using a non-segmented array instead!
84
- assert(element_count_max > node_capacity);
85
-
86
- // We use u32 for indexes and counts.
87
- assert(element_count_max <= std.math.maxInt(u32));
88
-
89
- // The buffers returned from the node_pool must be able to store T with correct
90
- // alignment.
91
- assert(NodePool.node_alignment >= @alignOf(T));
92
- }
93
-
94
- pub const node_count_max_naive = blk: {
95
- // If a node fills up it is divided into two new nodes. Therefore,
96
- // the worst possible space overhead is when all nodes are half full.
97
- // This uses flooring division, we want to examine the worst case here.
98
- const elements_per_node_min = @divExact(node_capacity, 2);
99
- break :blk div_ceil(element_count_max, elements_per_node_min);
100
- };
101
-
102
- // We can't always actually reach node_count_max_naive in all configurations.
103
- // If we're at node_count_max_naive-1 nodes, in order to split one more node we need:
104
- pub const node_count_max = if (element_count_max >=
105
- // * The node that we split must be full.
106
- node_capacity +
107
- // * The last node must have at least one element.
108
- 1 +
109
- // * All other nodes must be at least half-full.
110
- ((node_count_max_naive -| 3) * @divExact(node_capacity, 2)) +
111
- // * And then we insert one more element into the full node.
112
- 1)
113
- node_count_max_naive
114
- else
115
- node_count_max_naive - 1;
116
-
117
- node_count: u32 = 0,
118
- /// This is the segmented array. The first node_count pointers are non-null.
119
- /// The rest are null. We only use optional pointers here to get safety checks.
120
- nodes: *[node_count_max]?*[node_capacity]T,
121
- /// Since nodes in a segmented array are usually not full, computing the absolute index
122
- /// of an element in the full array is O(N) over the number of nodes. To avoid this cost
123
- /// we precompute the absolute index of the first element of each node.
124
- /// To avoid a separate counts field, we derive the number of elements in a node from the
125
- /// index of that node and the next node.
126
- /// To avoid special casing the count() function for the last node, we increase the array
127
- /// length by 1 and store the total element count in the last slot.
128
- indexes: *[node_count_max + 1]u32,
129
-
130
- pub fn init(allocator: mem.Allocator) !SegmentedArray {
131
- const nodes = try allocator.create([node_count_max]?*[node_capacity]T);
132
- errdefer allocator.destroy(nodes);
133
-
134
- const indexes = try allocator.create([node_count_max + 1]u32);
135
- errdefer allocator.destroy(indexes);
136
-
137
- @memset(nodes, null);
138
- indexes[0] = 0;
139
-
140
- const array = SegmentedArray{
141
- .nodes = nodes,
142
- .indexes = indexes,
143
- };
144
-
145
- if (options.verify) array.verify();
146
-
147
- return array;
148
- }
149
-
150
- pub fn deinit(array: SegmentedArray, allocator: mem.Allocator, node_pool: *NodePool) void {
151
- if (options.verify) array.verify();
152
-
153
- for (array.nodes[0..array.node_count]) |node| {
154
- node_pool.release(@ptrCast(@alignCast(node.?)));
155
- }
156
- allocator.free(array.nodes);
157
- allocator.free(array.indexes);
158
- }
159
-
160
- pub fn reset(array: *SegmentedArray, node_pool: *NodePool) void {
161
- if (options.verify) array.verify();
162
-
163
- for (array.nodes[0..array.node_count]) |node| {
164
- node_pool.release(@ptrCast(@alignCast(node.?)));
165
- }
166
- @memset(array.nodes, null);
167
-
168
- array.indexes[0] = 0;
169
- array.* = .{
170
- .nodes = array.nodes,
171
- .indexes = array.indexes,
172
- };
173
-
174
- if (options.verify) array.verify();
175
- }
176
-
177
- pub fn verify(array: SegmentedArray) void {
178
- assert(array.node_count <= node_count_max);
179
- for (array.nodes, 0..) |node, node_index| {
180
- if (node_index < array.node_count) {
181
- // The first node_count pointers are non-null.
182
- assert(node != null);
183
- } else {
184
- // The rest are non-null.
185
- assert(node == null);
186
- }
187
- }
188
- for (array.nodes[0..array.node_count], 0..) |_, node_index| {
189
- const c = array.count(@intCast(node_index));
190
- // Every node is at most full.
191
- assert(c <= node_capacity);
192
- // Every node is at least half-full, except the last.
193
- if (node_index < array.node_count - 1) {
194
- assert(c >= @divTrunc(node_capacity, 2));
195
- }
196
- }
197
- if (Key) |K| {
198
- // If Key is not null then the elements must be sorted by key_from_value (but not
199
- // necessarily unique).
200
- var key_prior_or_null: ?K = null;
201
- for (array.nodes[0..array.node_count], 0..) |_, node_index| {
202
- for (array.node_elements(@intCast(node_index))) |*value| {
203
- const key = key_from_value(value);
204
- if (key_prior_or_null) |key_prior| {
205
- assert(key_prior <= key);
206
- }
207
- key_prior_or_null = key;
208
- }
209
- }
210
- }
211
- }
212
-
213
- /// Returns the absolute index of the element being inserted.
214
- /// Available only when `Key != null`.
215
- pub fn insert_element(
216
- array: *SegmentedArray,
217
- node_pool: *NodePool,
218
- element: T,
219
- ) u32 {
220
- comptime assert(Key != null);
221
- if (options.verify) array.verify();
222
-
223
- const count_before = array.len();
224
-
225
- const cursor = array.search(key_from_value(&element));
226
- const absolute_index = array.absolute_index_for_cursor(cursor);
227
- array.insert_elements_at_absolute_index(node_pool, absolute_index, &[_]T{element});
228
-
229
- if (options.verify) array.verify();
230
-
231
- const count_after = array.len();
232
- assert(count_after == count_before + 1);
233
-
234
- return absolute_index;
235
- }
236
-
237
- /// Available only when `Key == null`.
238
- pub fn insert_elements(
239
- array: *SegmentedArray,
240
- node_pool: *NodePool,
241
- absolute_index: u32,
242
- elements: []const T,
243
- ) void {
244
- comptime assert(Key == null);
245
- if (options.verify) array.verify();
246
-
247
- const count_before = array.len();
248
- array.insert_elements_at_absolute_index(
249
- node_pool,
250
- absolute_index,
251
- elements,
252
- );
253
-
254
- const count_after = array.len();
255
- assert(count_after == count_before + elements.len);
256
-
257
- if (options.verify) array.verify();
258
- }
259
-
260
- fn insert_elements_at_absolute_index(
261
- array: *SegmentedArray,
262
- node_pool: *NodePool,
263
- absolute_index: u32,
264
- elements: []const T,
265
- ) void {
266
- assert(elements.len > 0);
267
- assert(absolute_index + elements.len <= element_count_max);
268
-
269
- var i: u32 = 0;
270
- while (i < elements.len) {
271
- const batch = @min(node_capacity, elements.len - i);
272
- array.insert_elements_batch(
273
- node_pool,
274
- absolute_index + i,
275
- elements[i..][0..batch],
276
- );
277
- i += batch;
278
- }
279
- assert(i == elements.len);
280
- }
281
-
282
- fn insert_elements_batch(
283
- array: *SegmentedArray,
284
- node_pool: *NodePool,
285
- absolute_index: u32,
286
- elements: []const T,
287
- ) void {
288
- assert(elements.len > 0);
289
- assert(elements.len <= node_capacity);
290
- assert(absolute_index + elements.len <= element_count_max);
291
-
292
- if (array.node_count == 0) {
293
- assert(absolute_index == 0);
294
-
295
- array.insert_empty_node_at(node_pool, 0);
296
-
297
- assert(array.node_count == 1);
298
- assert(array.nodes[0] != null);
299
- assert(array.indexes[0] == 0);
300
- assert(array.indexes[1] == 0);
301
- }
302
-
303
- const cursor = array.cursor_for_absolute_index(absolute_index);
304
- assert(cursor.node < array.node_count);
305
-
306
- const a = cursor.node;
307
- const a_pointer = array.nodes[a].?;
308
- assert(cursor.relative_index <= array.count(a));
309
-
310
- const total = array.count(a) + @as(u32, @intCast(elements.len));
311
- if (total <= node_capacity) {
312
- stdx.copy_right(
313
- .inexact,
314
- T,
315
- a_pointer[cursor.relative_index + elements.len ..],
316
- a_pointer[cursor.relative_index..array.count(a)],
317
- );
318
- stdx.copy_disjoint(.inexact, T, a_pointer[cursor.relative_index..], elements);
319
-
320
- array.increment_indexes_after(a, @intCast(elements.len));
321
- return;
322
- }
323
-
324
- // Insert a new node after the node being split.
325
- const b = a + 1;
326
- array.insert_empty_node_at(node_pool, b);
327
- const b_pointer = array.nodes[b].?;
328
-
329
- const a_half = div_ceil(total, 2);
330
- const b_half = total - a_half;
331
- assert(a_half >= b_half);
332
- assert(a_half + b_half == total);
333
-
334
- // The 1st case can be seen as a special case of the 2nd.
335
- // The 5th case can be seen as a special case of the 4th.
336
- //
337
- // elements: [yyyyyy], relative_index: 0
338
- // [xxxxx_][______]
339
- // [______][xxxxx_] // after first copy_backwards
340
- // [______][xxxxx_] // skip mem.copyBackwards (a_half >= relative_index)
341
- // [yyyyyy][xxxxx_] // after second copy_backwards
342
- //
343
- // elements: [yy], relative_index: 1
344
- // [xxxxx_][______]
345
- // [x__x__][xxx___] // after first copy_backwards
346
- // [x__x__][xxx___] // skip mem.copyBackwards (a_half >= relative_index)
347
- // [xyyx__][xxx___] // after second copy_backwards
348
- //
349
- // elements: [yy], relative_index: 2
350
- // [xxx_][____]
351
- // [xx__][_x__] // after first copy_backwards
352
- // [xx__][_x__] // skip mem.copyBackwards (a_half >= relative_index)
353
- // [xxy_][yx__] // after second copy_backwards
354
- //
355
- // elements: [yy], relative_index: 5
356
- // [xxxxxx][______]
357
- // [xxxxx_][___x__] // after first copy_backwards
358
- // [xxxx__][x__x__] // after mem.copyBackwards (a_half < relative_index)
359
- // [xxxx__][xyyx__] // after second copy_backwards
360
- //
361
- // elements: [yyyyy_], relative_index: 5
362
- // [xxxxx_][______]
363
- // [xxxxx_][______] // after first copy_backwards
364
- // [xxxxx_][______] // skip mem.copyBackwards (a_half >= relative_index)
365
- // [xxxxx_][yyyyy_] // after second copy_backwards
366
-
367
- const a_half_pointer = a_pointer[0..a_half];
368
- const b_half_pointer = b_pointer[0..b_half];
369
-
370
- // Move part of `a` forwards to make space for elements.
371
- copy_backwards(
372
- a_half_pointer,
373
- b_half_pointer,
374
- cursor.relative_index + elements.len,
375
- a_pointer[cursor.relative_index..array.count(a)],
376
- );
377
-
378
- if (a_half < cursor.relative_index) {
379
- // Move the part of `a` that is past the half-way point into `b`.
380
- stdx.copy_right(
381
- .inexact,
382
- T,
383
- b_half_pointer,
384
- a_pointer[a_half..cursor.relative_index],
385
- );
386
- }
387
-
388
- // Move `elements` into `a` and/or `b`.
389
- copy_backwards(
390
- a_half_pointer,
391
- b_half_pointer,
392
- cursor.relative_index,
393
- elements,
394
- );
395
-
396
- array.indexes[b] = array.indexes[a] + a_half;
397
- array.increment_indexes_after(b, @intCast(elements.len));
398
- }
399
-
400
- /// Behaves like mem.copyBackwards, but as if `a` and `b` were a single contiguous slice.
401
- /// `target` is the destination index within the concatenation of `a` and `b`.
402
- fn copy_backwards(
403
- a: []T,
404
- b: []T,
405
- target: usize,
406
- source: []const T,
407
- ) void {
408
- assert(target + source.len <= a.len + b.len);
409
- const target_a = a[@min(target, a.len)..@min(target + source.len, a.len)];
410
- const target_b = b[target -| a.len..(target + source.len) -| a.len];
411
- assert(target_a.len + target_b.len == source.len);
412
- const source_a = source[0..target_a.len];
413
- const source_b = source[target_a.len..];
414
- if (target_b.ptr != source_b.ptr) {
415
- stdx.copy_right(.exact, T, target_b, source_b);
416
- }
417
- if (target_a.ptr != source_a.ptr) {
418
- stdx.copy_right(.exact, T, target_a, source_a);
419
- }
420
- }
421
-
422
- /// Insert an empty node at index `node`.
423
- fn insert_empty_node_at(array: *SegmentedArray, node_pool: *NodePool, node: u32) void {
424
- assert(node <= array.node_count);
425
- assert(array.node_count + 1 <= node_count_max);
426
-
427
- stdx.copy_right(
428
- .exact,
429
- ?*[node_capacity]T,
430
- array.nodes[node + 1 .. array.node_count + 1],
431
- array.nodes[node..array.node_count],
432
- );
433
- stdx.copy_right(
434
- .exact,
435
- u32,
436
- array.indexes[node + 1 .. array.node_count + 2],
437
- array.indexes[node .. array.node_count + 1],
438
- );
439
-
440
- array.node_count += 1;
441
- const node_pointer = node_pool.acquire();
442
- comptime {
443
- // @ptrCast does not check that the size or alignment agree
444
- assert(std.meta.alignment(@TypeOf(node_pointer)) >= @alignOf(T));
445
- assert(@sizeOf(@TypeOf(node_pointer.*)) >= @sizeOf([node_capacity]T));
446
- }
447
- array.nodes[node] = @ptrCast(@alignCast(node_pointer));
448
- assert(array.indexes[node] == array.indexes[node + 1]);
449
- }
450
-
451
- pub fn remove_elements(
452
- array: *SegmentedArray,
453
- node_pool: *NodePool,
454
- absolute_index: u32,
455
- remove_count: u32,
456
- ) void {
457
- if (options.verify) array.verify();
458
-
459
- assert(array.node_count > 0);
460
- assert(remove_count > 0);
461
- assert(absolute_index + remove_count <= element_count_max);
462
- assert(absolute_index + remove_count <= array.indexes[array.node_count]);
463
-
464
- const half = @divExact(node_capacity, 2);
465
-
466
- var i: u32 = remove_count;
467
- while (i > 0) {
468
- const batch = @min(half, i);
469
- array.remove_elements_batch(node_pool, absolute_index, batch);
470
- i -= batch;
471
- }
472
-
473
- if (options.verify) array.verify();
474
- }
475
-
476
- fn remove_elements_batch(
477
- array: *SegmentedArray,
478
- node_pool: *NodePool,
479
- absolute_index: u32,
480
- remove_count: u32,
481
- ) void {
482
- assert(array.node_count > 0);
483
-
484
- // Restricting the batch size to half node capacity ensures that elements
485
- // are removed from at most two nodes.
486
- const half = @divExact(node_capacity, 2);
487
- assert(remove_count <= half);
488
- assert(remove_count > 0);
489
-
490
- assert(absolute_index + remove_count <= element_count_max);
491
- assert(absolute_index + remove_count <= array.indexes[array.node_count]);
492
-
493
- const cursor = array.cursor_for_absolute_index(absolute_index);
494
- assert(cursor.node < array.node_count);
495
-
496
- const a = cursor.node;
497
- const a_pointer = array.nodes[a].?;
498
- const a_remaining = cursor.relative_index;
499
-
500
- // Remove elements from exactly one node:
501
- if (a_remaining + remove_count <= array.count(a)) {
502
- stdx.copy_left(
503
- .inexact,
504
- T,
505
- a_pointer[a_remaining..],
506
- a_pointer[a_remaining + remove_count .. array.count(a)],
507
- );
508
-
509
- array.decrement_indexes_after(a, remove_count);
510
-
511
- array.maybe_remove_or_merge_node_with_next(node_pool, a);
512
- return;
513
- }
514
-
515
- // Remove elements from exactly two nodes:
516
-
517
- const b = a + 1;
518
- const b_pointer = array.nodes[b].?;
519
- const b_remaining = b_pointer[remove_count -
520
- (array.count(a) - a_remaining) .. array.count(b)];
521
-
522
- assert(@intFromPtr(b_remaining.ptr) > @intFromPtr(b_pointer));
523
-
524
- // Only one of these nodes may become empty, as we limit batch size to
525
- // half node capacity.
526
- assert(a_remaining > 0 or b_remaining.len > 0);
527
-
528
- if (a_remaining >= half) {
529
- stdx.copy_left(.inexact, T, b_pointer, b_remaining);
530
-
531
- array.indexes[b] = array.indexes[a] + a_remaining;
532
- array.decrement_indexes_after(b, remove_count);
533
-
534
- array.maybe_remove_or_merge_node_with_next(node_pool, b);
535
- } else if (b_remaining.len >= half) {
536
- assert(a_remaining < half);
537
-
538
- array.indexes[b] = array.indexes[a] + a_remaining;
539
- array.decrement_indexes_after(b, remove_count);
540
-
541
- array.maybe_merge_nodes(node_pool, a, b_remaining);
542
- } else {
543
- assert(a_remaining < half and b_remaining.len < half);
544
- assert(a_remaining + b_remaining.len <= node_capacity);
545
-
546
- stdx.copy_disjoint(.inexact, T, a_pointer[a_remaining..], b_remaining);
547
-
548
- array.indexes[b] =
549
- array.indexes[a] + a_remaining + @as(u32, @intCast(b_remaining.len));
550
- array.decrement_indexes_after(b, remove_count);
551
-
552
- array.remove_empty_node_at(node_pool, b);
553
-
554
- // Either:
555
- // * `b` was the last node so now `a` is the last node
556
- // * both `a` and `b` were at least half-full so now `a` is at least half-full
557
- assert(b == array.node_count or array.count(a) >= half);
558
- }
559
- }
560
-
561
- fn maybe_remove_or_merge_node_with_next(
562
- array: *SegmentedArray,
563
- node_pool: *NodePool,
564
- node: u32,
565
- ) void {
566
- assert(node < array.node_count);
567
-
568
- if (array.count(node) == 0) {
569
- array.remove_empty_node_at(node_pool, node);
570
- return;
571
- }
572
-
573
- if (node == array.node_count - 1) return;
574
-
575
- const next_elements = array.nodes[node + 1].?[0..array.count(node + 1)];
576
- array.maybe_merge_nodes(node_pool, node, next_elements);
577
- }
578
-
579
- fn maybe_merge_nodes(
580
- array: *SegmentedArray,
581
- node_pool: *NodePool,
582
- node: u32,
583
- elements_next_node: []T,
584
- ) void {
585
- const half = @divExact(node_capacity, 2);
586
-
587
- const a = node;
588
- const a_pointer = array.nodes[a].?;
589
- assert(array.count(a) <= node_capacity);
590
-
591
- // The elements_next_node slice may not be at the start of the node,
592
- // but the length of the slice will match count(b).
593
- const b = a + 1;
594
- const b_pointer = array.nodes[b].?;
595
- const b_elements = elements_next_node;
596
- assert(b_elements.len == array.count(b));
597
- assert(b_elements.len > 0);
598
- assert(b_elements.len >= half or b == array.node_count - 1);
599
- assert(b_elements.len <= node_capacity);
600
- assert(@intFromPtr(b_elements.ptr) >= @intFromPtr(b_pointer));
601
-
602
- // Our function would still be correct if this assert fails, but we would
603
- // unnecessarily copy all elements of b to node a and then delete b
604
- // instead of simply deleting a.
605
- assert(!(array.count(a) == 0 and b_pointer == b_elements.ptr));
606
-
607
- const total = array.count(a) + @as(u32, @intCast(b_elements.len));
608
- if (total <= node_capacity) {
609
- stdx.copy_disjoint(.inexact, T, a_pointer[array.count(a)..], b_elements);
610
-
611
- array.indexes[b] = array.indexes[b + 1];
612
- array.remove_empty_node_at(node_pool, b);
613
-
614
- assert(array.count(a) >= half or a == array.node_count - 1);
615
- } else if (array.count(a) < half) {
616
- const a_half = div_ceil(total, 2);
617
- const b_half = total - a_half;
618
- assert(a_half >= b_half);
619
- assert(a_half + b_half == total);
620
-
621
- stdx.copy_disjoint(
622
- .exact,
623
- T,
624
- a_pointer[array.count(a)..a_half],
625
- b_elements[0 .. a_half - array.count(a)],
626
- );
627
- stdx.copy_left(.inexact, T, b_pointer, b_elements[a_half - array.count(a) ..]);
628
-
629
- array.indexes[b] = array.indexes[a] + a_half;
630
-
631
- assert(array.count(a) >= half);
632
- assert(array.count(b) >= half);
633
- } else {
634
- assert(b_pointer == b_elements.ptr);
635
- assert(array.indexes[b] + b_elements.len == array.indexes[b + 1]);
636
- }
637
- }
638
-
639
- /// Remove an empty node at index `node`.
640
- fn remove_empty_node_at(array: *SegmentedArray, node_pool: *NodePool, node: u32) void {
641
- assert(array.node_count > 0);
642
- assert(node < array.node_count);
643
- assert(array.count(node) == 0);
644
-
645
- node_pool.release(@ptrCast(@alignCast(array.nodes[node].?)));
646
-
647
- stdx.copy_left(
648
- .exact,
649
- ?*[node_capacity]T,
650
- array.nodes[node .. array.node_count - 1],
651
- array.nodes[node + 1 .. array.node_count],
652
- );
653
- stdx.copy_left(
654
- .exact,
655
- u32,
656
- array.indexes[node..array.node_count],
657
- array.indexes[node + 1 .. array.node_count + 1],
658
- );
659
-
660
- array.node_count -= 1;
661
- array.nodes[array.node_count] = null;
662
- array.indexes[array.node_count + 1] = undefined;
663
- }
664
-
665
- inline fn count(array: SegmentedArray, node: u32) u32 {
666
- const result = array.indexes[node + 1] - array.indexes[node];
667
- assert(result <= node_capacity);
668
- return result;
669
- }
670
-
671
- inline fn increment_indexes_after(array: *SegmentedArray, node: u32, delta: u32) void {
672
- for (array.indexes[node + 1 .. array.node_count + 1]) |*i| i.* += delta;
673
- }
674
-
675
- inline fn decrement_indexes_after(array: *SegmentedArray, node: u32, delta: u32) void {
676
- for (array.indexes[node + 1 .. array.node_count + 1]) |*i| i.* -= delta;
677
- }
678
-
679
- pub inline fn node_elements(array: SegmentedArray, node: u32) []T {
680
- assert(node < array.node_count);
681
- return array.nodes[node].?[0..array.count(node)];
682
- }
683
-
684
- pub inline fn node_last_element(array: SegmentedArray, node: u32) T {
685
- return array.node_elements(node)[array.count(node) - 1];
686
- }
687
-
688
- pub inline fn element_at_cursor(array: SegmentedArray, cursor: Cursor) T {
689
- return array.node_elements(cursor.node)[cursor.relative_index];
690
- }
691
-
692
- pub inline fn first(_: SegmentedArray) Cursor {
693
- return .{
694
- .node = 0,
695
- .relative_index = 0,
696
- };
697
- }
698
-
699
- pub inline fn last(array: SegmentedArray) Cursor {
700
- if (array.node_count == 0) return array.first();
701
-
702
- return .{
703
- .node = array.node_count - 1,
704
- .relative_index = array.count(array.node_count - 1) - 1,
705
- };
706
- }
707
-
708
- pub inline fn len(array: SegmentedArray) u32 {
709
- const result = array.indexes[array.node_count];
710
- assert(result <= element_count_max);
711
- return result;
712
- }
713
-
714
- // TODO Consider enabling ReleaseFast for this once tested.
715
- pub fn absolute_index_for_cursor(array: SegmentedArray, cursor: Cursor) u32 {
716
- if (array.node_count == 0) {
717
- assert(cursor.node == 0);
718
- assert(cursor.relative_index == 0);
719
- return 0;
720
- }
721
- assert(cursor.node < array.node_count);
722
- if (cursor.node == array.node_count - 1) {
723
- // Insertion may target the index one past the end of the array.
724
- assert(cursor.relative_index <= array.count(cursor.node));
725
- } else {
726
- assert(cursor.relative_index < array.count(cursor.node));
727
- }
728
- return array.indexes[cursor.node] + cursor.relative_index;
729
- }
730
-
731
- fn cursor_for_absolute_index(array: SegmentedArray, absolute_index: u32) Cursor {
732
- // This function could handle node_count == 0 by returning a zero Cursor.
733
- // However, this is an internal function and we don't require this behavior.
734
- assert(array.node_count > 0);
735
-
736
- assert(absolute_index < element_count_max);
737
- assert(absolute_index <= array.len());
738
-
739
- const result = binary_search_keys(
740
- u32,
741
- array.indexes[0..array.node_count],
742
- absolute_index,
743
- .{},
744
- );
745
-
746
- if (result.exact) {
747
- return .{
748
- .node = result.index,
749
- .relative_index = 0,
750
- };
751
- } else {
752
- const node = result.index - 1;
753
- const relative_index = absolute_index - array.indexes[node];
754
- if (node == array.node_count - 1) {
755
- // Insertion may target the index one past the end of the array.
756
- assert(relative_index <= array.count(node));
757
- } else {
758
- assert(relative_index < array.count(node));
759
- }
760
- return .{
761
- .node = node,
762
- .relative_index = relative_index,
763
- };
764
- }
765
- }
766
-
767
- pub const Iterator = struct {
768
- array: *const SegmentedArray,
769
- direction: Direction,
770
-
771
- cursor: Cursor,
772
-
773
- /// The user may set this early to stop iteration. For example,
774
- /// if the returned table info is outside the key range.
775
- done: bool = false,
776
-
777
- pub fn next(it: *Iterator) ?*T {
778
- if (it.done) return null;
779
-
780
- assert(it.cursor.node < it.array.node_count);
781
-
782
- const elements = it.array.node_elements(it.cursor.node);
783
- const element = &elements[it.cursor.relative_index];
784
-
785
- switch (it.direction) {
786
- .ascending => {
787
- if (it.cursor.relative_index == elements.len - 1) {
788
- if (it.cursor.node == it.array.node_count - 1) {
789
- it.done = true;
790
- } else {
791
- it.cursor.node += 1;
792
- it.cursor.relative_index = 0;
793
- }
794
- } else {
795
- it.cursor.relative_index += 1;
796
- }
797
- },
798
- .descending => {
799
- if (it.cursor.relative_index == 0) {
800
- if (it.cursor.node == 0) {
801
- it.done = true;
802
- } else {
803
- it.cursor.node -= 1;
804
- it.cursor.relative_index = it.array.count(it.cursor.node) - 1;
805
- }
806
- } else {
807
- it.cursor.relative_index -= 1;
808
- }
809
- },
810
- }
811
-
812
- return element;
813
- }
814
- };
815
-
816
- pub fn iterator_from_cursor(
817
- array: *const SegmentedArray,
818
- /// First element of iteration.
819
- cursor: Cursor,
820
- direction: Direction,
821
- ) Iterator {
822
- if (array.node_count == 0) {
823
- assert(cursor.node == 0);
824
- assert(cursor.relative_index == 0);
825
-
826
- return .{
827
- .array = array,
828
- .direction = direction,
829
- .cursor = .{ .node = 0, .relative_index = 0 },
830
- .done = true,
831
- };
832
- } else if (cursor.node == array.node_count - 1 and
833
- cursor.relative_index == array.count(cursor.node))
834
- {
835
- return switch (direction) {
836
- .ascending => .{
837
- .array = array,
838
- .direction = direction,
839
- .cursor = cursor,
840
- .done = true,
841
- },
842
- .descending => .{
843
- .array = array,
844
- .direction = direction,
845
- .cursor = .{
846
- .node = cursor.node,
847
- .relative_index = cursor.relative_index - 1,
848
- },
849
- },
850
- };
851
- } else {
852
- assert(cursor.node < array.node_count);
853
- assert(cursor.relative_index < array.count(cursor.node));
854
-
855
- return .{
856
- .array = array,
857
- .direction = direction,
858
- .cursor = cursor,
859
- };
860
- }
861
- }
862
-
863
- pub fn iterator_from_index(
864
- array: *const SegmentedArray,
865
- /// First element of iteration.
866
- absolute_index: u32,
867
- direction: Direction,
868
- ) Iterator {
869
- assert(absolute_index < element_count_max);
870
-
871
- if (array.node_count == 0) {
872
- assert(absolute_index == 0);
873
-
874
- return Iterator{
875
- .array = array,
876
- .direction = direction,
877
- .cursor = .{ .node = 0, .relative_index = 0 },
878
- .done = true,
879
- };
880
- } else {
881
- assert(absolute_index < array.len());
882
-
883
- return Iterator{
884
- .array = array,
885
- .direction = direction,
886
- .cursor = array.cursor_for_absolute_index(absolute_index),
887
- };
888
- }
889
- }
890
-
891
- /// Returns a cursor to the index of the key either exactly equal to the target key or,
892
- /// if there is no exact match, the next greatest key.
893
- /// Available only when `Key != null`.
894
- pub fn search(
895
- array: *const SegmentedArray,
896
- key: K: {
897
- assert(Key != null);
898
- break :K Key.?;
899
- },
900
- ) Cursor {
901
- const K = Key.?;
902
- if (array.node_count == 0) {
903
- return .{
904
- .node = 0,
905
- .relative_index = 0,
906
- };
907
- }
908
-
909
- var offset: usize = 0;
910
- var length: usize = array.node_count;
911
- while (length > 1) {
912
- const half = length / 2;
913
- const mid = offset + half;
914
-
915
- const node = &array.nodes[mid].?[0];
916
-
917
- if (key_from_value(node) < key) {
918
- @branchHint(.unpredictable);
919
- offset = mid;
920
- }
921
-
922
- length -= half;
923
- }
924
-
925
- // Unlike a normal binary search, don't increment the offset when "key" is higher
926
- // than the element — "round down" to the previous node.
927
- // This guarantees that the node result is never "== node_count".
928
- //
929
- // (If there are two adjacent nodes starting with keys A and C, and we search B,
930
- // we want to pick the A node.)
931
- const node: u32 = @intCast(offset);
932
- assert(node < array.node_count);
933
-
934
- const relative_index = binary_search_values_upsert_index(
935
- K,
936
- T,
937
- key_from_value,
938
- array.node_elements(node),
939
- key,
940
- .{},
941
- );
942
-
943
- // Follow the same rule as absolute_index_for_cursor:
944
- // only return relative_index==array.count() at the last node.
945
- if (node + 1 < array.node_count and
946
- relative_index == array.count(node))
947
- {
948
- return .{
949
- .node = node + 1,
950
- .relative_index = 0,
951
- };
952
- } else {
953
- return .{
954
- .node = node,
955
- .relative_index = relative_index,
956
- };
957
- }
958
- }
959
- };
960
- }
961
-
962
- test "SortedSegmentedArray duplicate elements" {
963
- // Create [0, 0, 0, 100, 100, 100, ~0, ~0, ~0] array, verify that the search is left-biased.
964
- const testing = std.testing;
965
-
966
- const NodePoolType = @import("node_pool.zig").NodePoolType;
967
- const TestPool = NodePoolType(128 * @sizeOf(u32), 2 * @alignOf(u32));
968
- const TestArray = SortedSegmentedArrayType(
969
- u32,
970
- TestPool,
971
- 1024,
972
- u32,
973
- struct {
974
- inline fn key_from_value(value: *const u32) u32 {
975
- return value.*;
976
- }
977
- }.key_from_value,
978
- .{ .verify = true },
979
- );
980
-
981
- var pool: TestPool = undefined;
982
- try pool.init(testing.allocator, TestArray.node_count_max);
983
- defer pool.deinit(testing.allocator);
984
-
985
- var array = try TestArray.init(testing.allocator);
986
- defer array.deinit(testing.allocator, &pool);
987
-
988
- for (0..3) |index| {
989
- // Elements are inserted to the left of a row of duplicates.
990
- var inserted_at = array.insert_element(&pool, 0);
991
- try testing.expectEqual(inserted_at, 0);
992
-
993
- inserted_at = array.insert_element(&pool, 100);
994
- try testing.expectEqual(inserted_at, @as(u32, @intCast(index + 1)));
995
-
996
- inserted_at = array.insert_element(&pool, math.maxInt(u32));
997
- try testing.expectEqual(inserted_at, @as(u32, @intCast((index + 1) * 2)));
998
- }
999
- try testing.expectEqual(array.len(), 9);
1000
-
1001
- // Search finds the leftmost element.
1002
- try testing.expectEqual(array.absolute_index_for_cursor(array.search(0)), 0);
1003
- try testing.expectEqual(array.absolute_index_for_cursor(array.search(100)), 3);
1004
- try testing.expectEqual(array.absolute_index_for_cursor(array.search(math.maxInt(u32))), 6);
1005
-
1006
- // Ascending iterators pick the leftmost element.
1007
- // Descending iterators are weird --- they _also_ pick the leftmost element, although the
1008
- // rightmost makes more sense.
1009
- {
1010
- const target: u32 = 0;
1011
- var it = array.iterator_from_cursor(array.search(target), .ascending);
1012
- try testing.expectEqual(it.next().?.*, 0);
1013
- try testing.expectEqual(it.next().?.*, 0);
1014
- try testing.expectEqual(it.next().?.*, 0);
1015
- try testing.expectEqual(it.next().?.*, 100);
1016
-
1017
- it = array.iterator_from_cursor(array.search(target), .descending);
1018
- try testing.expectEqual(it.next().?.*, 0);
1019
- try testing.expectEqual(it.next(), null);
1020
- }
1021
-
1022
- {
1023
- const target: u32 = 100;
1024
- var it = array.iterator_from_cursor(array.search(target), .ascending);
1025
- try testing.expectEqual(it.next().?.*, 100);
1026
- try testing.expectEqual(it.next().?.*, 100);
1027
- try testing.expectEqual(it.next().?.*, 100);
1028
- try testing.expectEqual(it.next().?.*, math.maxInt(u32));
1029
-
1030
- it = array.iterator_from_cursor(array.search(target), .descending);
1031
- try testing.expectEqual(it.next().?.*, 100);
1032
- try testing.expectEqual(it.next().?.*, 0);
1033
- }
1034
-
1035
- {
1036
- const target: u32 = math.maxInt(u32);
1037
- var it = array.iterator_from_cursor(array.search(target), .ascending);
1038
- try testing.expectEqual(it.next().?.*, math.maxInt(u32));
1039
- try testing.expectEqual(it.next().?.*, math.maxInt(u32));
1040
- try testing.expectEqual(it.next().?.*, math.maxInt(u32));
1041
- try testing.expectEqual(it.next(), null);
1042
-
1043
- it = array.iterator_from_cursor(array.search(target), .descending);
1044
- try testing.expectEqual(it.next().?.*, math.maxInt(u32));
1045
- try testing.expectEqual(it.next().?.*, 100);
1046
- }
1047
- }
1048
-
1049
- /// In order to avoid making internal details of segmented array public, the fuzzing code is defined
1050
- /// in this file an is driven by =segmented_array_fuzz.zig`.
1051
- fn FuzzContextType(
1052
- comptime T: type,
1053
- comptime node_size: u32,
1054
- comptime element_count_max: u32,
1055
- comptime Key: type,
1056
- comptime key_from_value: fn (*const T) callconv(.@"inline") Key,
1057
- comptime element_order: enum { sorted, unsorted },
1058
- comptime options: Options,
1059
- ) type {
1060
- return struct {
1061
- const FuzzContext = @This();
1062
-
1063
- const testing = std.testing;
1064
- const log = false;
1065
-
1066
- const NodePoolType = @import("node_pool.zig").NodePoolType;
1067
-
1068
- // Test overaligned nodes to catch compile errors for missing @alignCast()
1069
- const TestPool = NodePoolType(node_size, 2 * @alignOf(T));
1070
- const TestArray = switch (element_order) {
1071
- .sorted => SortedSegmentedArrayType(
1072
- T,
1073
- TestPool,
1074
- element_count_max,
1075
- Key,
1076
- key_from_value,
1077
- options,
1078
- ),
1079
- .unsorted => SegmentedArrayType(T, TestPool, element_count_max, options),
1080
- };
1081
-
1082
- prng: *stdx.PRNG,
1083
-
1084
- pool: TestPool,
1085
- array: TestArray,
1086
-
1087
- reference: std.ArrayList(T),
1088
-
1089
- inserts: u64 = 0,
1090
- removes: u64 = 0,
1091
-
1092
- fn init(
1093
- context: *FuzzContext,
1094
- allocator: std.mem.Allocator,
1095
- prng: *stdx.PRNG,
1096
- ) !void {
1097
- context.* = .{
1098
- .prng = prng,
1099
-
1100
- .pool = undefined,
1101
- .array = undefined,
1102
- .reference = undefined,
1103
- };
1104
-
1105
- try context.pool.init(allocator, TestArray.node_count_max);
1106
- errdefer context.pool.deinit(allocator);
1107
-
1108
- context.array = try TestArray.init(allocator);
1109
- errdefer context.array.deinit(allocator, &context.pool);
1110
-
1111
- context.reference = std.ArrayList(T).init(allocator);
1112
- errdefer context.reference.deinit();
1113
- try context.reference.ensureTotalCapacity(element_count_max);
1114
- }
1115
-
1116
- fn deinit(context: *FuzzContext, allocator: std.mem.Allocator) void {
1117
- context.array.deinit(allocator, &context.pool);
1118
- context.pool.deinit(allocator);
1119
-
1120
- context.reference.deinit();
1121
- }
1122
-
1123
- fn run(context: *FuzzContext) !void {
1124
- const Action = enum { insert, remove };
1125
- {
1126
- var i: usize = 0;
1127
- while (i < element_count_max * 2) : (i += 1) {
1128
- switch (context.prng.enum_weighted(
1129
- Action,
1130
- .{
1131
- .insert = 60,
1132
- .remove = 40,
1133
- },
1134
- )) {
1135
- .insert => try context.insert(),
1136
- .remove => try context.remove(),
1137
- }
1138
- }
1139
- }
1140
-
1141
- {
1142
- var i: usize = 0;
1143
- while (i < element_count_max * 2) : (i += 1) {
1144
- switch (context.prng.enum_weighted(
1145
- Action,
1146
- .{
1147
- .insert = 40,
1148
- .remove = 60,
1149
- },
1150
- )) {
1151
- .insert => try context.insert(),
1152
- .remove => try context.remove(),
1153
- }
1154
- }
1155
- }
1156
-
1157
- // Rarely, the code above won't generate an insert at all.
1158
- if (context.inserts > 0) {
1159
- try context.remove_all();
1160
- }
1161
-
1162
- if (element_order == .unsorted) {
1163
- // Insert at the beginning of the array until the array is full.
1164
- while (context.array.len() < element_count_max) {
1165
- try context.insert_before_first();
1166
- }
1167
- assert(context.array.node_count >= TestArray.node_count_max - 1);
1168
-
1169
- // Remove all-but-one elements from the last node and insert them into the first
1170
- // node.
1171
- const element_count_last = context.array.count(context.array.node_count - 1);
1172
- var element_index: usize = 0;
1173
- while (element_index < element_count_last - 1) : (element_index += 1) {
1174
- try context.remove_last();
1175
- try context.insert_before_first();
1176
- }
1177
-
1178
- // We should now have maxed out our node count.
1179
- assert(context.array.node_count == TestArray.node_count_max);
1180
-
1181
- try context.remove_all();
1182
- }
1183
- }
1184
-
1185
- fn insert(context: *FuzzContext) !void {
1186
- const reference_len: u32 = @intCast(context.reference.items.len);
1187
- const count_free = element_count_max - reference_len;
1188
-
1189
- if (count_free == 0) return;
1190
-
1191
- var buffer: [TestArray.node_capacity * 3]T = undefined;
1192
- const count_max = @min(count_free, TestArray.node_capacity * 3);
1193
- const count = context.prng.range_inclusive(u32, 1, count_max);
1194
- context.prng.fill(mem.sliceAsBytes(buffer[0..count]));
1195
-
1196
- assert(context.reference.items.len <= element_count_max);
1197
-
1198
- switch (element_order) {
1199
- .unsorted => {
1200
- const index = context.prng.int_inclusive(u32, reference_len);
1201
-
1202
- context.array.insert_elements(&context.pool, index, buffer[0..count]);
1203
- // TODO the standard library could use an AssumeCapacity variant of this.
1204
- context.reference.insertSlice(index, buffer[0..count]) catch unreachable;
1205
- },
1206
- .sorted => {
1207
- for (buffer[0..count]) |value| {
1208
- const index_actual = context.array.insert_element(&context.pool, value);
1209
- const index_expect = context.reference_index(key_from_value(&value));
1210
- context.reference.insert(index_expect, value) catch unreachable;
1211
- try std.testing.expectEqual(index_expect, index_actual);
1212
- }
1213
- },
1214
- }
1215
- context.inserts += count;
1216
-
1217
- try context.verify();
1218
- }
1219
-
1220
- fn remove(context: *FuzzContext) !void {
1221
- const reference_len: u32 = @intCast(context.reference.items.len);
1222
- if (reference_len == 0) return;
1223
-
1224
- const count_max = @min(reference_len, TestArray.node_capacity * 3);
1225
- const count = context.prng.range_inclusive(u32, 1, count_max);
1226
-
1227
- assert(context.reference.items.len <= element_count_max);
1228
- const index = context.prng.int_inclusive(u32, reference_len - count);
1229
-
1230
- context.array.remove_elements(&context.pool, index, count);
1231
-
1232
- context.reference.replaceRange(index, count, &[0]T{}) catch unreachable;
1233
-
1234
- context.removes += count;
1235
-
1236
- try context.verify();
1237
- }
1238
-
1239
- fn insert_before_first(context: *FuzzContext) !void {
1240
- assert(element_order == .unsorted);
1241
-
1242
- const insert_index = context.array.absolute_index_for_cursor(context.array.first());
1243
-
1244
- var element: T = undefined;
1245
- context.prng.fill(mem.asBytes(&element));
1246
-
1247
- context.array.insert_elements(&context.pool, insert_index, &.{element});
1248
- context.reference.insert(insert_index, element) catch unreachable;
1249
-
1250
- context.inserts += 1;
1251
-
1252
- try context.verify();
1253
- }
1254
-
1255
- fn remove_last(context: *FuzzContext) !void {
1256
- assert(element_order == .unsorted);
1257
-
1258
- const remove_index = context.array.absolute_index_for_cursor(context.array.last());
1259
-
1260
- context.array.remove_elements(&context.pool, remove_index, 1);
1261
- context.reference.replaceRange(remove_index, 1, &[0]T{}) catch unreachable;
1262
-
1263
- context.removes += 1;
1264
-
1265
- try context.verify();
1266
- }
1267
-
1268
- fn remove_all(context: *FuzzContext) !void {
1269
- while (context.reference.items.len > 0) try context.remove();
1270
-
1271
- try testing.expectEqual(@as(u32, 0), context.array.len());
1272
- try testing.expect(context.inserts > 0);
1273
- try testing.expect(context.inserts == context.removes);
1274
-
1275
- if (log) {
1276
- std.debug.print("\ninserts: {}, removes: {}\n", .{
1277
- context.inserts,
1278
- context.removes,
1279
- });
1280
- }
1281
-
1282
- try context.verify();
1283
- }
1284
-
1285
- fn verify(context: *FuzzContext) !void {
1286
- if (log) {
1287
- std.debug.print("expect: ", .{});
1288
- for (context.reference.items) |i| std.debug.print("{}, ", .{i});
1289
-
1290
- std.debug.print("\nactual: ", .{});
1291
- var it = context.array.iterator_from_index(0, .ascending);
1292
- while (it.next()) |i| std.debug.print("{}, ", .{i.*});
1293
- std.debug.print("\n", .{});
1294
- }
1295
-
1296
- try testing.expectEqual(context.reference.items.len, context.array.len());
1297
-
1298
- {
1299
- var it = context.array.iterator_from_index(0, .ascending);
1300
-
1301
- for (context.reference.items) |expect| {
1302
- const actual = it.next() orelse return error.TestUnexpectedResult;
1303
- try testing.expectEqual(expect, actual.*);
1304
- }
1305
- try testing.expectEqual(@as(?*const T, null), it.next());
1306
- }
1307
-
1308
- {
1309
- var it = context.array.iterator_from_index(
1310
- @as(u32, @intCast(context.reference.items.len)) -| 1,
1311
- .descending,
1312
- );
1313
-
1314
- var i = context.reference.items.len;
1315
- while (i > 0) {
1316
- i -= 1;
1317
-
1318
- const expect = context.reference.items[i];
1319
- const actual = it.next() orelse return error.TestUnexpectedResult;
1320
- try testing.expectEqual(expect, actual.*);
1321
- }
1322
- try testing.expectEqual(@as(?*const T, null), it.next());
1323
- }
1324
-
1325
- {
1326
- for (context.reference.items, 0..) |_, i| {
1327
- try testing.expect(std.meta.eql(
1328
- i,
1329
- context.array.absolute_index_for_cursor(
1330
- context.array.cursor_for_absolute_index(@intCast(i)),
1331
- ),
1332
- ));
1333
- }
1334
- }
1335
-
1336
- if (element_order == .sorted) {
1337
- for (context.reference.items, 0..) |*expect, i| {
1338
- if (i == 0) continue;
1339
- try testing.expect(key_from_value(&context.reference.items[i - 1]) <=
1340
- key_from_value(expect));
1341
- }
1342
- }
1343
-
1344
- if (context.array.len() == 0) {
1345
- try testing.expectEqual(@as(u32, 0), context.array.node_count);
1346
- }
1347
-
1348
- for (context.array.nodes[context.array.node_count..]) |node| {
1349
- try testing.expectEqual(@as(?*[TestArray.node_capacity]T, null), node);
1350
- }
1351
-
1352
- {
1353
- var i: u32 = 0;
1354
- while (i < context.array.node_count -| 1) : (i += 1) {
1355
- try testing.expect(context.array.count(i) >=
1356
- @divExact(TestArray.node_capacity, 2));
1357
- }
1358
- }
1359
- if (element_order == .sorted) try context.verify_search();
1360
- }
1361
-
1362
- fn verify_search(context: *FuzzContext) !void {
1363
- var queries: [20]Key = undefined;
1364
- context.prng.fill(mem.sliceAsBytes(&queries));
1365
-
1366
- // Test min/max exceptional values on different SegmentedArray shapes.
1367
- queries[0] = 0;
1368
- queries[1] = math.maxInt(Key);
1369
-
1370
- for (queries) |query| {
1371
- try testing.expectEqual(
1372
- context.reference_index(query),
1373
- context.array.absolute_index_for_cursor(context.array.search(query)),
1374
- );
1375
- }
1376
-
1377
- {
1378
- var iterator_end = context.array.iterator_from_cursor(
1379
- context.array.search(math.maxInt(Key)),
1380
- .ascending,
1381
- );
1382
- while (iterator_end.next()) |item| {
1383
- try testing.expectEqual(key_from_value(item), math.maxInt(Key));
1384
- }
1385
- }
1386
-
1387
- {
1388
- // 0 is not symmetric with maxInt, because `array.search` doesn't take direction
1389
- // into account.
1390
- var iterator_start = context.array.iterator_from_cursor(
1391
- context.array.search(0),
1392
- .descending,
1393
- );
1394
- if (context.reference.items.len == 0) {
1395
- try testing.expectEqual(iterator_start.next(), null);
1396
- } else {
1397
- try testing.expect(iterator_start.next() != null);
1398
- try testing.expectEqual(iterator_start.next(), null);
1399
- }
1400
- }
1401
- }
1402
-
1403
- fn reference_index(context: *const FuzzContext, key: Key) u32 {
1404
- return binary_search_values_upsert_index(
1405
- Key,
1406
- T,
1407
- key_from_value,
1408
- context.reference.items,
1409
- key,
1410
- .{},
1411
- );
1412
- }
1413
- };
1414
- }
1415
-
1416
- pub fn run_fuzz(allocator: std.mem.Allocator, seed: u64, comptime options: Options) !void {
1417
- var prng = stdx.PRNG.from_seed(seed);
1418
-
1419
- const CompositeKey = @import("composite_key.zig").CompositeKeyType(u64);
1420
- const TableType = @import("table.zig").TableType;
1421
- const TableInfoType = @import("manifest.zig").TreeTableInfoType;
1422
- const TableInfo = TableInfoType(TableType(
1423
- CompositeKey.Key,
1424
- CompositeKey,
1425
- CompositeKey.key_from_value,
1426
- CompositeKey.sentinel_key,
1427
- CompositeKey.tombstone,
1428
- CompositeKey.tombstone_from_key,
1429
- 1, // Doesn't matter for this test.
1430
- .general,
1431
- ));
1432
-
1433
- const CompareInt = struct {
1434
- inline fn key_from_value(value: *const u32) u32 {
1435
- return value.*;
1436
- }
1437
- };
1438
-
1439
- const CompareTable = struct {
1440
- inline fn key_from_value(value: *const TableInfo) u64 {
1441
- return value.address;
1442
- }
1443
- };
1444
-
1445
- const TestOptions = struct {
1446
- element_type: type,
1447
- node_size: u32,
1448
- element_count_max: u32,
1449
- };
1450
-
1451
- var tested_padding = false;
1452
- var tested_node_capacity_min = false;
1453
-
1454
- // We want to explore not just the bottom boundary but also the surrounding area
1455
- // as it may also have interesting edge cases.
1456
- inline for (.{
1457
- TestOptions{ .element_type = u32, .node_size = 8, .element_count_max = 3 },
1458
- TestOptions{ .element_type = u32, .node_size = 8, .element_count_max = 4 },
1459
- TestOptions{ .element_type = u32, .node_size = 8, .element_count_max = 5 },
1460
- TestOptions{ .element_type = u32, .node_size = 8, .element_count_max = 6 },
1461
- TestOptions{ .element_type = u32, .node_size = 8, .element_count_max = 1024 },
1462
- TestOptions{ .element_type = u32, .node_size = 16, .element_count_max = 1024 },
1463
- TestOptions{ .element_type = u32, .node_size = 32, .element_count_max = 1024 },
1464
- TestOptions{ .element_type = u32, .node_size = 64, .element_count_max = 1024 },
1465
- TestOptions{ .element_type = TableInfo, .node_size = 256, .element_count_max = 3 },
1466
- TestOptions{ .element_type = TableInfo, .node_size = 256, .element_count_max = 4 },
1467
- TestOptions{ .element_type = TableInfo, .node_size = 256, .element_count_max = 1024 },
1468
- TestOptions{ .element_type = TableInfo, .node_size = 512, .element_count_max = 1024 },
1469
- TestOptions{ .element_type = TableInfo, .node_size = 1024, .element_count_max = 1024 },
1470
- }) |test_options| {
1471
- inline for (.{ .sorted, .unsorted }) |order| {
1472
- const FuzzContext = FuzzContextType(
1473
- test_options.element_type,
1474
- test_options.node_size,
1475
- test_options.element_count_max,
1476
- if (test_options.element_type == u32) u32 else u64,
1477
- if (test_options.element_type == u32)
1478
- CompareInt.key_from_value
1479
- else
1480
- CompareTable.key_from_value,
1481
- order,
1482
- options,
1483
- );
1484
-
1485
- var context: FuzzContext = undefined;
1486
- try context.init(allocator, &prng);
1487
- defer context.deinit(allocator);
1488
-
1489
- try context.run();
1490
-
1491
- if (test_options.node_size % @sizeOf(test_options.element_type) != 0) {
1492
- tested_padding = true;
1493
- }
1494
- if (FuzzContext.TestArray.node_capacity == 2) tested_node_capacity_min = true;
1495
- }
1496
- }
1497
-
1498
- assert(tested_padding);
1499
- assert(tested_node_capacity_min);
1500
- }