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,1381 +0,0 @@
1
- const std = @import("std");
2
- const assert = std.debug.assert;
3
- const mem = std.mem;
4
-
5
- const DynamicBitSetUnmanaged = std.bit_set.DynamicBitSetUnmanaged;
6
- const MaskInt = DynamicBitSetUnmanaged.MaskInt;
7
-
8
- const vsr = @import("../vsr.zig");
9
- const stdx = vsr.stdx;
10
- const KiB = stdx.KiB;
11
- const ewah = vsr.ewah(FreeSet.Word);
12
- const constants = vsr.constants;
13
-
14
- const div_ceil = stdx.div_ceil;
15
- const maybe = stdx.maybe;
16
-
17
- /// This is logically a range of addresses within the FreeSet, but its actual fields are block
18
- /// indexes for ease of calculation.
19
- ///
20
- /// A reservation covers a range of both free and acquired blocks — when it is first created,
21
- /// it is guaranteed to cover exactly as many free blocks as were requested by `reserve()`.
22
- pub const Reservation = struct {
23
- block_base: usize,
24
- block_count: usize,
25
- /// An identifier for each reservation cycle, to verify that old reservations are not reused.
26
- session: usize,
27
- };
28
-
29
- /// The 0 address is reserved for usage as a sentinel and will never be returned by acquire().
30
- ///
31
- /// Concurrent callers must reserve free blocks before acquiring them to ensure that
32
- /// acquisition order is deterministic despite concurrent jobs acquiring blocks in
33
- /// nondeterministic order.
34
- ///
35
- /// The reservation lifecycle is:
36
- ///
37
- /// 1. Reserve: In deterministic order, each job (e.g. compaction) calls `reserve()` to
38
- /// reserve the upper bound of blocks that it may need to acquire to complete.
39
- /// 2. Acquire: The jobs run concurrently. Each job acquires blocks only from its respective
40
- /// reservation (via `acquire()`).
41
- /// 3. Forfeit: When a job finishes, it calls `forfeit()` to drop its reservation.
42
- /// 4. Done: When all pending reservations are forfeited, the reserved (but unacquired) space
43
- /// is reclaimed.
44
- ///
45
- pub const FreeSet = struct {
46
- pub const Word = u64;
47
- pub const BitsetKind = enum {
48
- blocks_acquired,
49
- blocks_released,
50
- };
51
- const BlocksReleasedPriorCheckpointDurability = std.AutoArrayHashMapUnmanaged(u64, void);
52
-
53
- // Free set is stored in the grid (see `CheckpointTrailer`) and is not available until the
54
- // relevant blocks are fetched from disk (or other replicas) and decoded.
55
- //
56
- // Without the free set, only blocks belonging to the free set might be read and no blocks can
57
- // be written.
58
- opened: bool = false,
59
-
60
- /// Whether the current checkpoint is durable.
61
- checkpoint_durable: bool = false,
62
-
63
- /// If a shard has any free blocks, the corresponding index bit is zero.
64
- /// If a shard has no free blocks, the corresponding index bit is one.
65
- index: DynamicBitSetUnmanaged,
66
-
67
- /// The maximum number of blocks the free set is allowed to reserve (driven by --limit-storage).
68
- blocks_count_limit: u64,
69
-
70
- /// Set bits indicate acquired blocks; unset bits indicate free blocks.
71
- blocks_acquired: DynamicBitSetUnmanaged,
72
-
73
- /// Set bits indicate blocks released in the current checkpoint, to be freed when the next
74
- /// checkpoint becomes durable.
75
- blocks_released: DynamicBitSetUnmanaged,
76
-
77
- /// Temporarily holds blocks released prior durability of the current checkpoint, to be freed
78
- /// when the next checkpoint becomes durable. These blocks are moved to blocks_released once the
79
- /// current checkpoint becomes durable.
80
- blocks_released_prior_checkpoint_durability: BlocksReleasedPriorCheckpointDurability,
81
-
82
- /// The number of blocks that are reserved, counting both acquired and free blocks
83
- /// from the start of `blocks_acquired`.
84
- /// Alternatively, the index of the first non-reserved block in `blocks_acquired`.
85
- reservation_blocks: usize = 0,
86
-
87
- /// The number of active reservations.
88
- reservation_count: usize = 0,
89
-
90
- /// Verify that when the caller transitions from creating reservations to forfeiting them,
91
- /// all reservations must be forfeited before additional reservations are made.
92
- reservation_state: enum {
93
- reserving,
94
- forfeiting,
95
- } = .reserving,
96
-
97
- /// Verifies that reservations are not allocated from or forfeited when they should not be.
98
- reservation_session: usize = 1,
99
-
100
- // Each shard is 8 cache lines because the CPU line fill buffer can fetch 10 lines in parallel.
101
- // And 8 is fast for division when computing the shard of a block.
102
- // Since the shard is scanned sequentially, the prefetching amortizes the cost of the single
103
- // cache miss. It also reduces the size of the index.
104
- //
105
- // e.g. 10TiB disk ÷ 64KiB/block ÷ 512*8 blocks/shard ÷ 8 shards/byte = 5120B index
106
- const shard_cache_lines = 8;
107
- pub const shard_bits = shard_cache_lines * constants.cache_line_size * @bitSizeOf(u8);
108
- comptime {
109
- assert(shard_bits == 4096);
110
- assert(@bitSizeOf(MaskInt) == 64);
111
- // Ensure there are no wasted padding bits at the end of the index.
112
- assert(shard_bits % @bitSizeOf(MaskInt) == 0);
113
- }
114
-
115
- pub fn init(allocator: mem.Allocator, options: struct {
116
- grid_size_limit: usize,
117
- blocks_released_prior_checkpoint_durability_max: usize,
118
- }) !FreeSet {
119
- const blocks_count = block_count_max(options.grid_size_limit);
120
- assert(blocks_count % shard_bits == 0);
121
- assert(blocks_count % @bitSizeOf(Word) == 0);
122
-
123
- // Every block bit is covered by exactly one index bit.
124
- const shards_count = @divExact(blocks_count, shard_bits);
125
- var index = try DynamicBitSetUnmanaged.initEmpty(allocator, shards_count);
126
- errdefer index.deinit(allocator);
127
-
128
- var blocks_acquired = try DynamicBitSetUnmanaged.initEmpty(allocator, blocks_count);
129
- errdefer blocks_acquired.deinit(allocator);
130
-
131
- var blocks_released = try DynamicBitSetUnmanaged.initEmpty(allocator, blocks_count);
132
- errdefer blocks_released.deinit(allocator);
133
-
134
- var released_prior_checkpoint_durability: BlocksReleasedPriorCheckpointDurability = .{};
135
- try released_prior_checkpoint_durability.ensureTotalCapacity(
136
- allocator,
137
- options.blocks_released_prior_checkpoint_durability_max +
138
- // `blocks_released` and `blocks_acquired` encoded in the CheckpointTrailer are
139
- // released at checkpoint (see `mark_checkpoint_not_durable` in grid.zig).
140
- 2 * vsr.checkpoint_trailer.block_count_for_trailer_size(
141
- ewah.encode_size_max(blocks_count),
142
- ),
143
- );
144
- errdefer released_prior_checkpoint_durability.deinit();
145
-
146
- assert(index.count() == 0);
147
- assert(blocks_acquired.count() == 0);
148
- assert(blocks_released.count() == 0);
149
- assert(released_prior_checkpoint_durability.count() == 0);
150
-
151
- return FreeSet{
152
- .index = index,
153
- .blocks_count_limit = @divFloor(options.grid_size_limit, constants.block_size),
154
- .blocks_acquired = blocks_acquired,
155
- .blocks_released = blocks_released,
156
- .blocks_released_prior_checkpoint_durability = released_prior_checkpoint_durability,
157
- };
158
- }
159
- pub fn deinit(set: *FreeSet, allocator: mem.Allocator) void {
160
- set.index.deinit(allocator);
161
- set.blocks_acquired.deinit(allocator);
162
- set.blocks_released.deinit(allocator);
163
- set.blocks_released_prior_checkpoint_durability.deinit(allocator);
164
- }
165
-
166
- pub fn reset(set: *FreeSet) void {
167
- for ([_]*DynamicBitSetUnmanaged{
168
- &set.index,
169
- &set.blocks_acquired,
170
- &set.blocks_released,
171
- }) |bitset| {
172
- var it = bitset.iterator(.{});
173
- while (it.next()) |bit| bitset.unset(bit);
174
- }
175
-
176
- set.blocks_released_prior_checkpoint_durability.clearRetainingCapacity();
177
-
178
- set.* = .{
179
- .index = set.index,
180
- .blocks_count_limit = set.blocks_count_limit,
181
- .blocks_acquired = set.blocks_acquired,
182
- .blocks_released = set.blocks_released,
183
- .blocks_released_prior_checkpoint_durability = set
184
- .blocks_released_prior_checkpoint_durability,
185
- .reservation_session = set.reservation_session +% 1,
186
- };
187
-
188
- assert(set.index.count() == 0);
189
- assert(set.blocks_acquired.count() == 0);
190
- assert(set.blocks_released.count() == 0);
191
- assert(set.blocks_released_prior_checkpoint_durability.count() == 0);
192
-
193
- assert(!set.opened);
194
- }
195
-
196
- /// Opens a free set. Needs two inputs:
197
- ///
198
- /// - the byte buffers with the ewah-encoded acquired and released bitsets,
199
- /// - the list of block addresses used to store both the encoded bitsets in the grid.
200
- ///
201
- /// Block addresses themselves are not a part of the encoded bitset for acquired blocks,
202
- /// see CheckpointTrailer for details.
203
- pub fn open(set: *FreeSet, options: struct {
204
- encoded: struct {
205
- blocks_acquired: []const []align(@alignOf(Word)) const u8,
206
- blocks_released: []const []align(@alignOf(Word)) const u8,
207
- },
208
- free_set_block_addresses: struct {
209
- blocks_acquired: []const u64,
210
- blocks_released: []const u64,
211
- },
212
- }) void {
213
- assert(!set.opened);
214
- assert((options.encoded.blocks_acquired.len == 0 and
215
- options.encoded.blocks_released.len == 0) ==
216
- (options.free_set_block_addresses.blocks_acquired.len == 0 and
217
- options.free_set_block_addresses.blocks_released.len == 0));
218
- set.decode_chunks(
219
- options.encoded.blocks_acquired,
220
- options.encoded.blocks_released,
221
- );
222
- set.mark_released(options.free_set_block_addresses.blocks_acquired);
223
- set.mark_released(options.free_set_block_addresses.blocks_released);
224
- set.opened = true;
225
- }
226
-
227
- // A shortcut to initialize an empty free set for tests.
228
- pub fn init_empty(allocator: mem.Allocator, blocks_count: usize) !FreeSet {
229
- comptime assert(constants.verify);
230
- var set = try init(allocator, .{
231
- .grid_size_limit = blocks_count * constants.block_size,
232
- .blocks_released_prior_checkpoint_durability_max = 0,
233
- });
234
- errdefer set.deinit(allocator);
235
-
236
- assert(!set.opened);
237
- assert(!set.checkpoint_durable);
238
- return set;
239
- }
240
-
241
- // A shortcut to initialize and open an empty free set for tests.
242
- pub fn open_empty(allocator: mem.Allocator, blocks_count: usize) !FreeSet {
243
- comptime assert(constants.verify);
244
- var set = try init(allocator, .{
245
- .grid_size_limit = blocks_count * constants.block_size,
246
- .blocks_released_prior_checkpoint_durability_max = 0,
247
- });
248
- errdefer set.deinit(allocator);
249
-
250
- set.open(.{
251
- .encoded = .{ .blocks_acquired = &.{}, .blocks_released = &.{} },
252
- .free_set_block_addresses = .{ .blocks_acquired = &.{}, .blocks_released = &.{} },
253
- });
254
- // Mark checkpoint as durable so tests use blocks_released for block releases.
255
- // blocks_released_prior_checkpoint_durable is required to ensure correctness across
256
- // multiple replicas, while tests check the following flows in a single process:
257
- // * Block acquisition-release
258
- // * Bitset encoding-decoding
259
- set.checkpoint_durable = true;
260
-
261
- assert(set.opened);
262
- assert(set.count_free() == blocks_count);
263
- assert(set.count_released() == 0);
264
- return set;
265
- }
266
-
267
- fn verify_index(set: *const FreeSet) void {
268
- for (0..set.index.bit_length) |shard| {
269
- assert((set.find_free_block_in_shard(shard) == null) == set.index.isSet(shard));
270
- }
271
- }
272
-
273
- /// Returns the number of active reservations.
274
- pub fn count_reservations(set: FreeSet) usize {
275
- assert(set.opened);
276
- return set.reservation_count;
277
- }
278
-
279
- /// Returns the number of free blocks.
280
- pub fn count_free(set: FreeSet) usize {
281
- assert(set.opened);
282
- return set.blocks_acquired.capacity() - set.blocks_acquired.count();
283
- }
284
-
285
- /// Returns the number of acquired blocks.
286
- pub fn count_acquired(set: FreeSet) usize {
287
- assert(set.opened);
288
- return set.blocks_acquired.count();
289
- }
290
-
291
- /// Returns the number of released blocks.
292
- pub fn count_released(set: FreeSet) usize {
293
- assert(set.opened);
294
- return set.blocks_released.count() +
295
- set.blocks_released_prior_checkpoint_durability.count();
296
- }
297
-
298
- /// Returns the address of the highest acquired block.
299
- pub fn highest_address_acquired(set: FreeSet) ?u64 {
300
- assert(set.opened);
301
- var it = set.blocks_acquired.iterator(.{
302
- .kind = .set,
303
- .direction = .reverse,
304
- });
305
-
306
- if (it.next()) |block| {
307
- const address = block + 1;
308
- return address;
309
- } else {
310
- // All blocks are free.
311
- assert(set.blocks_acquired.count() == 0);
312
- return null;
313
- }
314
- }
315
-
316
- /// Returns the address of the highest released block.
317
- pub fn highest_address_released(set: FreeSet) ?u64 {
318
- assert(set.opened);
319
- var it = set.blocks_released.iterator(.{
320
- .kind = .set,
321
- .direction = .reverse,
322
- });
323
-
324
- if (it.next()) |block| {
325
- const address = block + 1;
326
- return address;
327
- } else {
328
- assert(set.count_released() == 0);
329
- return null;
330
- }
331
- }
332
-
333
- /// Reserve `reserve_count` free blocks. The blocks are not acquired yet.
334
- ///
335
- /// Invariants:
336
- ///
337
- /// - If a reservation is returned, it covers exactly `reserve_count` free blocks, along with
338
- /// any interleaved already-acquired blocks.
339
- /// - Active reservations are exclusive (i.e. disjoint).
340
- /// (A reservation is active until `forfeit()` is called.)
341
- ///
342
- /// Returns null if there are not enough blocks free and vacant.
343
- /// Returns a reservation which can be used with `acquire()`:
344
- /// - The caller should consider the returned Reservation as opaque and immutable.
345
- /// - Each `reserve()` call which returns a non-null Reservation must correspond to exactly one
346
- /// `forfeit()` call.
347
- pub fn reserve(set: *FreeSet, reserve_count: usize) ?Reservation {
348
- assert(set.opened);
349
- assert(set.reservation_state == .reserving);
350
- assert(reserve_count > 0);
351
-
352
- const shard_start = find_bit(
353
- set.index,
354
- @divFloor(set.reservation_blocks, shard_bits),
355
- set.index.bit_length,
356
- .unset,
357
- ) orelse return null;
358
-
359
- // The reservation may cover (and ignore) already-acquired blocks due to fragmentation.
360
- var block = @max(shard_start * shard_bits, set.reservation_blocks);
361
- for (0..reserve_count) |_| {
362
- block = 1 + (find_bit(
363
- set.blocks_acquired,
364
- block,
365
- set.blocks_acquired.bit_length,
366
- .unset,
367
- ) orelse return null);
368
-
369
- // The free block from the `blocks_acquired` bit set may be past the total number of
370
- // blocks that this free set is allowed to acquire (see `block_count_max`).
371
- if (block > set.blocks_count_limit) return null;
372
- }
373
- const block_base = set.reservation_blocks;
374
- const block_count = block - set.reservation_blocks;
375
- set.reservation_blocks += block_count;
376
- set.reservation_count += 1;
377
-
378
- return Reservation{
379
- .block_base = block_base,
380
- .block_count = block_count,
381
- .session = set.reservation_session,
382
- };
383
- }
384
-
385
- /// After invoking `forfeit()`, the reservation must never be used again.
386
- pub fn forfeit(set: *FreeSet, reservation: Reservation) void {
387
- assert(set.opened);
388
- assert(set.reservation_session == reservation.session);
389
-
390
- set.reservation_count -= 1;
391
- if (set.reservation_count == 0) {
392
- // All reservations have been dropped.
393
- set.reservation_blocks = 0;
394
- set.reservation_session +%= 1;
395
- set.reservation_state = .reserving;
396
- } else {
397
- set.reservation_state = .forfeiting;
398
- }
399
- }
400
-
401
- /// Marks a free block from the reservation as allocated, and returns the address.
402
- /// The reservation must not have been forfeited yet.
403
- /// The reservation must belong to the current cycle of reservations.
404
- ///
405
- /// Invariants:
406
- ///
407
- /// - An acquired block cannot be acquired again until it has been released and the release
408
- /// has been checkpointed.
409
- ///
410
- /// Returns null if no free block is available in the reservation.
411
- pub fn acquire(set: *FreeSet, reservation: Reservation) ?u64 {
412
- assert(set.opened);
413
- assert(set.reservation_count > 0);
414
- assert(reservation.block_count > 0);
415
- assert(reservation.block_base < set.reservation_blocks);
416
- assert(reservation.block_base + reservation.block_count <= set.reservation_blocks);
417
- assert(reservation.session == set.reservation_session);
418
-
419
- const shard_start = find_bit(
420
- set.index,
421
- @divFloor(reservation.block_base, shard_bits),
422
- div_ceil(reservation.block_base + reservation.block_count, shard_bits),
423
- .unset,
424
- ) orelse return null;
425
- assert(!set.index.isSet(shard_start));
426
-
427
- const reservation_start = @max(
428
- shard_start * shard_bits,
429
- reservation.block_base,
430
- );
431
- const reservation_end = reservation.block_base + reservation.block_count;
432
- const block = find_bit(
433
- set.blocks_acquired,
434
- reservation_start,
435
- reservation_end,
436
- .unset,
437
- ) orelse return null;
438
- assert(block >= reservation.block_base);
439
- assert(block <= reservation.block_base + reservation.block_count);
440
- assert(!set.blocks_acquired.isSet(block));
441
- assert(!set.blocks_released.isSet(block));
442
- assert(!set.blocks_released_prior_checkpoint_durability.contains(block));
443
-
444
- // Even if "shard_start" has free blocks, we might acquire our block from a later shard.
445
- // (This is possible because our reservation begins part-way through the shard.)
446
- const shard = @divFloor(block, shard_bits);
447
- maybe(shard == shard_start);
448
- assert(shard >= shard_start);
449
-
450
- set.blocks_acquired.set(block);
451
- // Update the index when every block in the shard is acquired.
452
- if (set.find_free_block_in_shard(shard) == null) set.index.set(shard);
453
- const address = block + 1;
454
- return address;
455
- }
456
-
457
- fn find_free_block_in_shard(set: FreeSet, shard: usize) ?usize {
458
- maybe(set.opened);
459
- const shard_start = shard * shard_bits;
460
- const shard_end = shard_start + shard_bits;
461
- assert(shard_start < set.blocks_acquired.bit_length);
462
-
463
- return find_bit(set.blocks_acquired, shard_start, shard_end, .unset);
464
- }
465
-
466
- pub fn is_free(set: FreeSet, address: u64) bool {
467
- if (set.opened) {
468
- const block = address - 1;
469
- return !set.blocks_acquired.isSet(block);
470
- } else {
471
- // When the free set is not open, conservatively assume that the block is acquired.
472
- //
473
- // This path is hit only when the replica opens the free set, reading its blocks from
474
- // the grid.
475
- return false;
476
- }
477
- }
478
-
479
- pub fn is_released(set: *const FreeSet, address: u64) bool {
480
- assert(set.opened);
481
- const block = address - 1;
482
- return set.blocks_released_prior_checkpoint_durability.contains(block) or
483
- set.blocks_released.isSet(block);
484
- }
485
-
486
- /// Returns `true` if the block at the given address would be freed when the current checkpoint
487
- /// becomes durable (when checkpoint_durable is set to `true`).
488
- ///
489
- /// Calling this function is only valid while the current checkpoint is not durable. During this
490
- /// period, blocks are marked as released in `blocks_released_prior_checkpoint_durability`;
491
- /// `blocks_released` remains unchanged and contains blocks released during the previous
492
- /// checkpoint interval.
493
- pub fn to_be_freed_at_checkpoint_durability(set: *const FreeSet, address: u64) bool {
494
- const block = address - 1;
495
-
496
- assert(set.opened);
497
- assert(!set.checkpoint_durable);
498
-
499
- // Block address must be acquired, but is not necessarily released.
500
- assert(set.blocks_acquired.isSet(block));
501
- assert(!set.blocks_released.isSet(block) or
502
- !set.blocks_released_prior_checkpoint_durability.contains(block));
503
- maybe(set.blocks_released.isSet(block));
504
- maybe(set.blocks_released_prior_checkpoint_durability.contains(block));
505
-
506
- return set.blocks_released.isSet(block);
507
- }
508
-
509
- /// Leave the address acquired for now, but free it when the next checkpoint becomes durable.
510
- /// This ensures that it will not be overwritten during the current checkpoint — the block may
511
- /// still be needed if we crash and recover from the current checkpoint.
512
- /// (TODO) If the block was created since the last checkpoint then it's safe to free
513
- /// immediately. This may reduce space amplification, especially for smaller datasets.
514
- /// (Note: This must be careful not to release while any reservations are held
515
- /// to avoid making the reservation's acquire()s nondeterministic).
516
- pub fn release(set: *FreeSet, address: u64) void {
517
- assert(set.opened);
518
-
519
- const block = address - 1;
520
- assert(set.blocks_acquired.isSet(block));
521
- assert(!set.blocks_released.isSet(block));
522
- assert(!set.blocks_released_prior_checkpoint_durability.contains(block));
523
-
524
- // `blocks_released` remains unchanged while the current checkpoint is not durable,
525
- // since it contains blocks released in the previous checkpoint. These blocks must not be
526
- // freed till the current checkpoint is durable, so as to maintain the durability of these
527
- // blocks on a commit quorum of replicas.
528
- if (set.checkpoint_durable) {
529
- set.blocks_released.set(block);
530
- } else {
531
- set.blocks_released_prior_checkpoint_durability.putAssumeCapacity(block, {});
532
- }
533
- }
534
-
535
- /// Mark the given addresses as allocated in the current checkpoint, but free in the next one.
536
- ///
537
- /// This is used only when reading a free set from the grid. On disk representation of the
538
- /// free set doesn't include the blocks storing the free set itself, and these blocks must be
539
- /// manually patched in after decoding. As the next checkpoint will have a completely different
540
- /// free set, the blocks can be simultaneously released.
541
- fn mark_released(set: *FreeSet, addresses: []const u64) void {
542
- assert(!set.opened);
543
- assert(!set.checkpoint_durable);
544
-
545
- var address_previous: u64 = 0;
546
- for (addresses) |address| {
547
- assert(address > 0);
548
-
549
- // Assert that addresses are sorted and unique. Sortedness is not a requirement, but
550
- // a consequence of "first free" allocation algorithm.
551
- assert(address > address_previous);
552
- address_previous = address;
553
-
554
- const block = address - 1;
555
-
556
- assert(!set.blocks_acquired.isSet(block));
557
- assert(!set.blocks_released.isSet(block));
558
- assert(!set.blocks_released_prior_checkpoint_durability.contains(block));
559
-
560
- set.blocks_acquired.set(block);
561
-
562
- const shard = @divFloor(block, shard_bits);
563
- // Update the index when every block in the shard is acquired.
564
- if (set.find_free_block_in_shard(shard) == null) set.index.set(shard);
565
-
566
- set.blocks_released_prior_checkpoint_durability.putAssumeCapacity(block, {});
567
- }
568
- }
569
-
570
- /// Given the address, marks an acquired block as free.
571
- fn free(set: *FreeSet, address: u64) void {
572
- assert(set.opened);
573
- assert(set.checkpoint_durable);
574
-
575
- const block = address - 1;
576
- assert(set.blocks_acquired.isSet(block));
577
- assert(set.blocks_released.isSet(block));
578
- assert(!set.blocks_released_prior_checkpoint_durability.contains(block));
579
-
580
- assert(set.reservation_count == 0);
581
- assert(set.reservation_blocks == 0);
582
-
583
- set.index.unset(@divFloor(block, shard_bits));
584
- set.blocks_acquired.unset(block);
585
- set.blocks_released.unset(block);
586
- }
587
-
588
- pub fn mark_checkpoint_not_durable(set: *FreeSet) void {
589
- assert(set.opened);
590
- assert(set.checkpoint_durable);
591
- assert(set.blocks_released_prior_checkpoint_durability.count() == 0);
592
- set.checkpoint_durable = false;
593
- }
594
-
595
- /// Now that the checkpoint is durable on a commit quorum of replicas:
596
- /// 1. Mark the current checkpoint as durable.
597
- /// 2. Mark all released blocks in `blocks_released` as free.
598
- /// 3. Move released blocks from `blocks_released_prior_checkpoint_durability` to
599
- /// `blocks_released`.
600
- pub fn mark_checkpoint_durable(set: *FreeSet) void {
601
- assert(set.opened);
602
- assert(!set.checkpoint_durable);
603
-
604
- set.checkpoint_durable = true;
605
-
606
- var it = set.blocks_released.iterator(.{ .kind = .set });
607
- while (it.next()) |block| set.free(block + 1);
608
-
609
- assert(set.blocks_released.count() == 0);
610
-
611
- // Block releases from the current checkpoint that were temporarily recorded in
612
- // blocks_released_prior_checkpoint_durability can now be moved to blocks_released.
613
- while (set.blocks_released_prior_checkpoint_durability.pop()) |block_entry| {
614
- const block = block_entry.key;
615
- set.blocks_released.set(block);
616
- }
617
- assert(set.blocks_released_prior_checkpoint_durability.count() == 0);
618
-
619
- // Index verification is O(blocks.bit_length) so do it only when checkpoint is marked
620
- // durable, which is also linear (as we free released blocks in `blocks_released`).
621
- set.verify_index();
622
- }
623
-
624
- /// Decodes the compressed bitset chunks in `source_chunks` into `target_bitset`.
625
- /// Panics if the `source_chunks` encoding is invalid.
626
- fn decode(
627
- set: *FreeSet,
628
- target_bitset: FreeSet.BitsetKind,
629
- source_chunks: []const []align(@alignOf(Word)) const u8,
630
- ) void {
631
- assert(!set.opened);
632
- assert(!set.checkpoint_durable);
633
-
634
- var source_size: usize = 0;
635
-
636
- for (source_chunks) |source_chunk| source_size += source_chunk.len;
637
-
638
- const target_bitset_words = switch (target_bitset) {
639
- .blocks_acquired => bit_set_masks(set.blocks_acquired),
640
- .blocks_released => bit_set_masks(set.blocks_released),
641
- };
642
-
643
- var decoder = ewah.decode_chunks(target_bitset_words, source_size);
644
-
645
- var words_decoded: usize = 0;
646
- for (source_chunks) |source_chunk| {
647
- words_decoded += decoder.decode_chunk(source_chunk);
648
- }
649
- assert(decoder.done());
650
-
651
- assert(@bitSizeOf(Word) == @bitSizeOf(MaskInt));
652
- assert(words_decoded * @bitSizeOf(Word) <= set.blocks_acquired.bit_length);
653
-
654
- // The encoder does not encode trailing 0s, so everything past words_decoded must be zeroed.
655
- assert(stdx.zeroed(std.mem.sliceAsBytes(target_bitset_words[words_decoded..])));
656
- // TODO: uncomment on the next release:
657
- // if (words_decoded > 0) assert(target_bitset_words[words_decoded - 1] != 0);
658
- }
659
-
660
- pub fn decode_chunks(
661
- set: *FreeSet,
662
- source_chunks_blocks_acquired: []const []align(@alignOf(Word)) const u8,
663
- source_chunks_blocks_released: []const []align(@alignOf(Word)) const u8,
664
- ) void {
665
- assert(!set.opened);
666
- assert(!set.checkpoint_durable);
667
-
668
- // Verify that this FreeSet is entirely unallocated.
669
- assert(set.index.count() == 0);
670
- assert(set.blocks_acquired.count() == 0);
671
- assert(set.blocks_released.count() == 0);
672
- assert(set.blocks_released_prior_checkpoint_durability.count() == 0);
673
-
674
- assert(set.reservation_count == 0);
675
- assert(set.reservation_blocks == 0);
676
-
677
- set.decode(.blocks_acquired, source_chunks_blocks_acquired);
678
- set.decode(.blocks_released, source_chunks_blocks_released);
679
-
680
- for (0..set.index.bit_length) |shard| {
681
- if (set.find_free_block_in_shard(shard) == null) set.index.set(shard);
682
- }
683
-
684
- set.verify_index();
685
- }
686
-
687
- /// Returns the number of blocks that the free set can physically reference via the acquired
688
- /// and released bitsets. Logically, the limit on the number of blocks that can be acquired by
689
- /// the free set is imposed by --limit-storage.
690
- pub fn block_count_max(grid_size_limit: usize) usize {
691
- const block_count_limit = @divFloor(grid_size_limit, constants.block_size);
692
- return stdx.div_ceil(block_count_limit, shard_bits) * shard_bits;
693
- }
694
-
695
- /// Returns the maximum number of bytes needed for encoding the acquired/released bitset.
696
- pub fn encode_size_max(set: *const FreeSet) usize {
697
- assert(set.blocks_acquired.bit_length == set.blocks_released.bit_length);
698
-
699
- const blocks_count = set.blocks_acquired.bit_length;
700
- assert(blocks_count % shard_bits == 0);
701
- assert(blocks_count % @bitSizeOf(usize) == 0);
702
-
703
- return ewah.encode_size_max(@divExact(blocks_count, @bitSizeOf(Word)));
704
- }
705
-
706
- fn encode(
707
- set: *const FreeSet,
708
- source_bitset: FreeSet.BitsetKind,
709
- target_chunks: []const []align(@alignOf(Word)) u8,
710
- ) usize {
711
- assert(set.opened);
712
- assert(set.checkpoint_durable);
713
-
714
- var encoder = switch (source_bitset) {
715
- .blocks_acquired => ewah.encode_chunks(bit_set_masks(set.blocks_acquired)),
716
- .blocks_released => ewah.encode_chunks(bit_set_masks(set.blocks_released)),
717
- };
718
- defer assert(encoder.done());
719
-
720
- var bytes_encoded_total: u64 = 0;
721
- for (target_chunks) |chunk| {
722
- const bytes_encoded =
723
- @as(u32, @intCast(encoder.encode_chunk(chunk)));
724
- assert(bytes_encoded > 0);
725
-
726
- bytes_encoded_total += bytes_encoded;
727
-
728
- if (encoder.done()) break;
729
- } else unreachable;
730
-
731
- // Don't explicitly encode trailing zeros to ensure that the encoding is the same regardless
732
- // of the runtime-configurable capacity of the bit set (driven by --limit-storage).
733
- const bytes_trailing_zero_runs = encoder.trailing_zero_runs_count * @sizeOf(ewah.Marker);
734
-
735
- return bytes_encoded_total - bytes_trailing_zero_runs;
736
- }
737
-
738
- pub fn encode_chunks(
739
- set: *const FreeSet,
740
- target_chunks_blocks_acquired: []const []align(@alignOf(Word)) u8,
741
- target_chunks_blocks_released: []const []align(@alignOf(Word)) u8,
742
- ) struct { encoded_size_blocks_acquired: u64, encoded_size_blocks_released: u64 } {
743
- assert(set.opened);
744
- assert(set.checkpoint_durable);
745
- assert(set.reservation_count == 0);
746
- assert(set.reservation_blocks == 0);
747
-
748
- return .{
749
- .encoded_size_blocks_acquired = set.encode(
750
- .blocks_acquired,
751
- target_chunks_blocks_acquired,
752
- ),
753
- .encoded_size_blocks_released = set.encode(
754
- .blocks_released,
755
- target_chunks_blocks_released,
756
- ),
757
- };
758
- }
759
- };
760
-
761
- fn bit_set_masks(bit_set: DynamicBitSetUnmanaged) []MaskInt {
762
- const len = div_ceil(bit_set.bit_length, @bitSizeOf(MaskInt));
763
- return bit_set.masks[0..len];
764
- }
765
-
766
- test "FreeSet block shard count" {
767
- if (constants.block_size != 64 * KiB) return;
768
- const blocks_in_tb = @divExact(1 << 40, constants.block_size);
769
- try test_block_shards_count(5120 * 8, 10 * blocks_in_tb);
770
- try test_block_shards_count(5120 * 8 - 1, 10 * blocks_in_tb - FreeSet.shard_bits);
771
- try test_block_shards_count(1, FreeSet.shard_bits); // Must be at least one index bit.
772
- }
773
-
774
- fn test_block_shards_count(expect_shards_count: usize, blocks_count: usize) !void {
775
- const gpa = std.testing.allocator;
776
-
777
- var set = try FreeSet.open_empty(gpa, blocks_count);
778
- defer set.deinit(gpa);
779
-
780
- try std.testing.expectEqual(expect_shards_count, set.index.bit_length);
781
- }
782
-
783
- test "FreeSet highest_address_acquired" {
784
- const expectEqual = std.testing.expectEqual;
785
- const blocks_count = FreeSet.shard_bits;
786
- const gpa = std.testing.allocator;
787
-
788
- var set = try FreeSet.open_empty(gpa, blocks_count);
789
- defer set.deinit(gpa);
790
-
791
- {
792
- const reservation = set.reserve(6).?;
793
- defer set.forfeit(reservation);
794
-
795
- try expectEqual(@as(?u64, null), set.highest_address_acquired());
796
- try expectEqual(@as(?u64, 1), set.acquire(reservation));
797
- try expectEqual(@as(?u64, 2), set.acquire(reservation));
798
- try expectEqual(@as(?u64, 3), set.acquire(reservation));
799
- }
800
-
801
- try expectEqual(@as(?u64, 3), set.highest_address_acquired());
802
-
803
- set.release(2);
804
- set.free(2);
805
- try expectEqual(@as(?u64, 3), set.highest_address_acquired());
806
-
807
- set.release(3);
808
- set.free(3);
809
- try expectEqual(@as(?u64, 1), set.highest_address_acquired());
810
-
811
- set.release(1);
812
- set.free(1);
813
- try expectEqual(@as(?u64, null), set.highest_address_acquired());
814
-
815
- {
816
- const reservation = set.reserve(6).?;
817
- defer set.forfeit(reservation);
818
-
819
- try expectEqual(@as(?u64, 1), set.acquire(reservation));
820
- try expectEqual(@as(?u64, 2), set.acquire(reservation));
821
- try expectEqual(@as(?u64, 3), set.acquire(reservation));
822
- }
823
-
824
- {
825
- set.release(3);
826
- try expectEqual(@as(?u64, 3), set.highest_address_acquired());
827
-
828
- set.free(3);
829
- try expectEqual(@as(?u64, 2), set.highest_address_acquired());
830
- }
831
- }
832
-
833
- test "FreeSet acquire/release" {
834
- try test_acquire_release(FreeSet.shard_bits);
835
- try test_acquire_release(2 * FreeSet.shard_bits);
836
- try test_acquire_release(63 * FreeSet.shard_bits);
837
- try test_acquire_release(64 * FreeSet.shard_bits);
838
- try test_acquire_release(65 * FreeSet.shard_bits);
839
- }
840
-
841
- fn test_acquire_release(blocks_count: usize) !void {
842
- const gpa = std.testing.allocator;
843
- const expectEqual = std.testing.expectEqual;
844
- // Acquire everything, then release, then acquire again.
845
- var set = try FreeSet.open_empty(gpa, blocks_count);
846
- defer set.deinit(gpa);
847
-
848
- var empty = try FreeSet.open_empty(gpa, blocks_count);
849
- defer empty.deinit(gpa);
850
-
851
- {
852
- const reservation = set.reserve(blocks_count).?;
853
- defer set.forfeit(reservation);
854
-
855
- for (0..blocks_count) |i| {
856
- try expectEqual(@as(?u64, i + 1), set.acquire(reservation));
857
- }
858
- try expectEqual(@as(?u64, null), set.acquire(reservation));
859
- }
860
-
861
- try expectEqual(@as(u64, set.blocks_acquired.bit_length), set.count_acquired());
862
- try expectEqual(@as(u64, 0), set.count_free());
863
-
864
- {
865
- for (0..blocks_count) |i| {
866
- set.release(@as(u64, i + 1));
867
- set.free(@as(u64, i + 1));
868
- }
869
- try expect_free_set_equal(empty, set);
870
- }
871
-
872
- try expectEqual(@as(u64, 0), set.count_acquired());
873
- try expectEqual(@as(u64, set.blocks_acquired.bit_length), set.count_free());
874
-
875
- {
876
- const reservation = set.reserve(blocks_count).?;
877
- defer set.forfeit(reservation);
878
-
879
- for (0..blocks_count) |i| {
880
- try expectEqual(@as(?u64, i + 1), set.acquire(reservation));
881
- }
882
- try expectEqual(@as(?u64, null), set.acquire(reservation));
883
- }
884
- }
885
-
886
- test "FreeSet.reserve/acquire" {
887
- const gpa = std.testing.allocator;
888
- const blocks_count_total = 4096;
889
- var set = try FreeSet.open_empty(gpa, blocks_count_total);
890
- defer set.deinit(gpa);
891
-
892
- // At most `blocks_count_total` blocks are initially available for reservation.
893
- try std.testing.expectEqual(set.reserve(blocks_count_total + 1), null);
894
- const r1 = set.reserve(blocks_count_total - 1);
895
- const r2 = set.reserve(1);
896
- try std.testing.expectEqual(set.reserve(1), null);
897
- set.forfeit(r1.?);
898
- set.forfeit(r2.?);
899
-
900
- var address: usize = 1; // Start at 1 because addresses are >0.
901
- {
902
- const reservation = set.reserve(2).?;
903
- defer set.forfeit(reservation);
904
-
905
- try std.testing.expectEqual(set.acquire(reservation), address + 0);
906
- try std.testing.expectEqual(set.acquire(reservation), address + 1);
907
- try std.testing.expectEqual(set.acquire(reservation), null);
908
- }
909
- address += 2;
910
-
911
- {
912
- // Blocks are acquired from the target reservation.
913
- const reservation_1 = set.reserve(2).?;
914
- const reservation_2 = set.reserve(2).?;
915
- defer set.forfeit(reservation_1);
916
- defer set.forfeit(reservation_2);
917
-
918
- try std.testing.expectEqual(set.acquire(reservation_1), address + 0);
919
- try std.testing.expectEqual(set.acquire(reservation_2), address + 2);
920
- try std.testing.expectEqual(set.acquire(reservation_1), address + 1);
921
- try std.testing.expectEqual(set.acquire(reservation_1), null);
922
- try std.testing.expectEqual(set.acquire(reservation_2), address + 3);
923
- try std.testing.expectEqual(set.acquire(reservation_2), null);
924
- }
925
- address += 4;
926
- }
927
-
928
- test "FreeSet checkpoint" {
929
- const gpa = std.testing.allocator;
930
- const expectEqual = std.testing.expectEqual;
931
- const blocks_count = FreeSet.shard_bits;
932
- var set = try FreeSet.open_empty(gpa, blocks_count);
933
- defer set.deinit(gpa);
934
-
935
- var empty = try FreeSet.open_empty(gpa, blocks_count);
936
- defer empty.deinit(gpa);
937
-
938
- var full = try FreeSet.open_empty(gpa, blocks_count);
939
- defer full.deinit(gpa);
940
-
941
- {
942
- // Acquire all of `full`'s blocks.
943
- const reservation = full.reserve(blocks_count).?;
944
- defer full.forfeit(reservation);
945
-
946
- for (0..full.blocks_acquired.bit_length) |i| {
947
- try expectEqual(@as(?u64, i + 1), full.acquire(reservation));
948
- }
949
- }
950
-
951
- {
952
- // Acquire & stage-release every block.
953
- const reservation = set.reserve(blocks_count).?;
954
- defer set.forfeit(reservation);
955
-
956
- for (0..set.blocks_acquired.bit_length) |i| {
957
- try expectEqual(@as(?u64, i + 1), set.acquire(reservation));
958
- set.release(i + 1);
959
-
960
- // These count functions treat staged blocks as acquired.
961
- try expectEqual(@as(u64, i + 1), set.count_acquired());
962
- try expectEqual(@as(u64, set.blocks_acquired.bit_length - i - 1), set.count_free());
963
- }
964
- // All blocks are still acquired, though staged to release at the next checkpoint.
965
- try expectEqual(@as(?u64, null), set.acquire(reservation));
966
- }
967
-
968
- // Perform checkpoint-related operations.
969
- set.mark_checkpoint_not_durable();
970
- set.mark_checkpoint_durable();
971
-
972
- try expect_free_set_equal(empty, set);
973
- try expectEqual(@as(usize, 0), set.blocks_released.count());
974
-
975
- {
976
- // Allocate & stage-release all blocks again.
977
- const reservation = set.reserve(blocks_count).?;
978
- defer set.forfeit(reservation);
979
-
980
- for (0..set.blocks_acquired.bit_length) |i| {
981
- try expectEqual(@as(?u64, i + 1), set.acquire(reservation));
982
- set.release(i + 1);
983
- }
984
- }
985
-
986
- const set_encoded_blocks_acquired = try gpa.alignedAlloc(
987
- u8,
988
- @alignOf(FreeSet.Word),
989
- set.encode_size_max(),
990
- );
991
- const set_encoded_blocks_released = try gpa.alignedAlloc(
992
- u8,
993
- @alignOf(FreeSet.Word),
994
- set.encode_size_max(),
995
- );
996
-
997
- defer gpa.free(set_encoded_blocks_acquired);
998
- defer gpa.free(set_encoded_blocks_released);
999
-
1000
- var set_decoded = try FreeSet.init_empty(gpa, blocks_count);
1001
-
1002
- defer set_decoded.deinit(gpa);
1003
-
1004
- {
1005
- const free_set_encoded = set.encode_chunks(
1006
- &.{set_encoded_blocks_acquired},
1007
- &.{set_encoded_blocks_released},
1008
- );
1009
-
1010
- set_decoded.decode_chunks(
1011
- &.{set_encoded_blocks_acquired[0..free_set_encoded.encoded_size_blocks_acquired]},
1012
- &.{set_encoded_blocks_released[0..free_set_encoded.encoded_size_blocks_released]},
1013
- );
1014
- try expect_free_set_equal(set, set_decoded);
1015
- }
1016
-
1017
- {
1018
- const free_set_encoded = full.encode_chunks(
1019
- &.{set_encoded_blocks_acquired},
1020
- &.{set_encoded_blocks_released},
1021
- );
1022
-
1023
- set_decoded.reset();
1024
- set_decoded.decode_chunks(
1025
- &.{set_encoded_blocks_acquired[0..free_set_encoded.encoded_size_blocks_acquired]},
1026
- &.{set_encoded_blocks_released[0..free_set_encoded.encoded_size_blocks_released]},
1027
- );
1028
- try expect_free_set_equal(full, set_decoded);
1029
- }
1030
- }
1031
-
1032
- test "FreeSet encode, decode, encode" {
1033
- const shard_bits = FreeSet.shard_bits / @bitSizeOf(usize);
1034
- const gpa = std.testing.allocator;
1035
-
1036
- // Uniform.
1037
- try test_encode(&.{.{ .fill = .uniform_ones, .words = shard_bits }});
1038
- try test_encode(&.{.{ .fill = .uniform_zeros, .words = shard_bits }});
1039
- try test_encode(&.{.{ .fill = .literal, .words = shard_bits }});
1040
- try test_encode(&.{.{ .fill = .uniform_ones, .words = std.math.maxInt(u16) + 1 }});
1041
-
1042
- // Mixed.
1043
- try test_encode(&.{
1044
- .{ .fill = .uniform_ones, .words = shard_bits / 4 },
1045
- .{ .fill = .uniform_zeros, .words = shard_bits / 4 },
1046
- .{ .fill = .literal, .words = shard_bits / 4 },
1047
- .{ .fill = .uniform_ones, .words = shard_bits / 4 },
1048
- });
1049
-
1050
- // Random.
1051
- const seed = std.crypto.random.int(u64);
1052
- var prng = stdx.PRNG.from_seed(seed);
1053
-
1054
- const fills = [_]TestPatternFill{ .uniform_ones, .uniform_zeros, .literal };
1055
- for (0..10) |_| {
1056
- var patterns = std.ArrayList(TestPattern).init(gpa);
1057
- defer patterns.deinit();
1058
-
1059
- for (0..shard_bits) |_| {
1060
- try patterns.append(.{
1061
- .fill = fills[prng.index(fills)],
1062
- .words = 1,
1063
- });
1064
- }
1065
- try test_encode(patterns.items);
1066
- }
1067
- }
1068
-
1069
- const TestPattern = struct {
1070
- fill: TestPatternFill,
1071
- words: usize,
1072
- };
1073
-
1074
- const TestPatternFill = enum { uniform_ones, uniform_zeros, literal };
1075
-
1076
- fn test_encode(patterns: []const TestPattern) !void {
1077
- const gpa = std.testing.allocator;
1078
- const seed = std.crypto.random.int(u64);
1079
- var prng = stdx.PRNG.from_seed(seed);
1080
-
1081
- var blocks_count: usize = 0;
1082
- for (patterns) |pattern| blocks_count += pattern.words * @bitSizeOf(usize);
1083
-
1084
- var decoded_expect = try FreeSet.open_empty(gpa, blocks_count);
1085
- defer decoded_expect.deinit(gpa);
1086
-
1087
- {
1088
- // The `index` will start out one-filled. Every pattern containing a zero will update the
1089
- // corresponding index bit with a zero (probably multiple times) to ensure it ends up synced
1090
- // with `blocks`.
1091
- decoded_expect.index.toggleAll();
1092
- assert(decoded_expect.index.count() == decoded_expect.index.capacity());
1093
-
1094
- // Fill the bitset according to the patterns.
1095
- var blocks = bit_set_masks(decoded_expect.blocks_acquired);
1096
- var blocks_offset: usize = 0;
1097
- for (patterns) |pattern| {
1098
- for (0..pattern.words) |_| {
1099
- blocks[blocks_offset] = switch (pattern.fill) {
1100
- .uniform_ones => ~@as(usize, 0),
1101
- .uniform_zeros => 0,
1102
- .literal => prng.range_inclusive(usize, 1, std.math.maxInt(usize) - 1),
1103
- };
1104
- const index_bit = blocks_offset * @bitSizeOf(usize) / FreeSet.shard_bits;
1105
- if (pattern.fill != .uniform_ones) decoded_expect.index.unset(index_bit);
1106
- blocks_offset += 1;
1107
- }
1108
- }
1109
- assert(blocks_offset == blocks.len);
1110
- }
1111
-
1112
- var encoded = try gpa.alignedAlloc(
1113
- u8,
1114
- @alignOf(FreeSet.Word),
1115
- decoded_expect.encode_size_max(),
1116
- );
1117
- defer gpa.free(encoded);
1118
-
1119
- try std.testing.expectEqual(encoded.len % 8, 0);
1120
- const encoded_length = decoded_expect.encode(.blocks_acquired, &.{encoded});
1121
-
1122
- var decoded_actual = try FreeSet.init_empty(gpa, blocks_count);
1123
- defer decoded_actual.deinit(gpa);
1124
-
1125
- decoded_actual.decode_chunks(&.{encoded[0..encoded_length]}, &.{});
1126
- try expect_free_set_equal(decoded_expect, decoded_actual);
1127
- }
1128
-
1129
- fn expect_free_set_equal(a: FreeSet, b: FreeSet) !void {
1130
- try expect_bit_set_equal(a.blocks_acquired, b.blocks_acquired);
1131
- try expect_bit_set_equal(a.blocks_released, b.blocks_released);
1132
- try expect_bit_set_equal(a.index, b.index);
1133
-
1134
- try std.testing.expectEqual(
1135
- a.blocks_released_prior_checkpoint_durability.count(),
1136
- b.blocks_released_prior_checkpoint_durability.count(),
1137
- );
1138
-
1139
- for (
1140
- a.blocks_released_prior_checkpoint_durability.keys(),
1141
- b.blocks_released_prior_checkpoint_durability.keys(),
1142
- ) |address_a, address_b| {
1143
- assert(address_a == address_b);
1144
- }
1145
- }
1146
-
1147
- fn expect_bit_set_equal(a: DynamicBitSetUnmanaged, b: DynamicBitSetUnmanaged) !void {
1148
- try std.testing.expectEqual(a.bit_length, b.bit_length);
1149
- const a_masks = bit_set_masks(a);
1150
- const b_masks = bit_set_masks(b);
1151
- for (a_masks, 0..) |aw, i| try std.testing.expectEqual(aw, b_masks[i]);
1152
- }
1153
-
1154
- test "FreeSet decode small bitset into large bitset" {
1155
- const gpa = std.testing.allocator;
1156
- const shard_bits = FreeSet.shard_bits;
1157
- var small_set = try FreeSet.open_empty(gpa, shard_bits);
1158
- defer small_set.deinit(gpa);
1159
-
1160
- {
1161
- // Set up a small bitset (with blocks_count==shard_bits) with no free blocks.
1162
- const reservation = small_set.reserve(small_set.blocks_acquired.bit_length).?;
1163
- defer small_set.forfeit(reservation);
1164
-
1165
- for (0..small_set.blocks_acquired.bit_length) |_| {
1166
- _ = small_set.acquire(reservation);
1167
- }
1168
- }
1169
-
1170
- var small_buffer = try gpa.alignedAlloc(
1171
- u8,
1172
- @alignOf(usize),
1173
- small_set.encode_size_max(),
1174
- );
1175
- defer gpa.free(small_buffer);
1176
-
1177
- const small_buffer_written = small_set.encode(.blocks_acquired, &.{small_buffer});
1178
-
1179
- // Decode the serialized small bitset into a larger bitset (with blocks_count==2*shard_bits).
1180
- var big_set = try FreeSet.init_empty(gpa, 2 * shard_bits);
1181
- defer big_set.deinit(gpa);
1182
-
1183
- big_set.decode(.blocks_acquired, &.{small_buffer[0..small_buffer_written]});
1184
- big_set.opened = true;
1185
-
1186
- for (0..2 * shard_bits) |block| {
1187
- const address = block + 1;
1188
- try std.testing.expectEqual(shard_bits <= block, big_set.is_free(address));
1189
- }
1190
- }
1191
-
1192
- test "FreeSet encode/decode manual" {
1193
- const encoded_expect = mem.sliceAsBytes(&[_]usize{
1194
- // Mask 1: run of 2 words of 0s, then 3 literals
1195
- 0 | (2 << 1) | (3 << 32),
1196
- 0b10101010_10101010_10101010_10101010_10101010_10101010_10101010_10101010, // literal 1
1197
- 0b01010101_01010101_01010101_01010101_01010101_01010101_01010101_01010101, // literal 2
1198
- 0b10101010_10101010_10101010_10101010_10101010_10101010_10101010_10101010, // literal 3
1199
- // Mask 2: run of 59 words of 1s, then 0 literals
1200
- //
1201
- // 59 is chosen so that because the blocks_count must be a multiple of the shard size:
1202
- // shard_bits = 4096 bits = 64 words × 64 bits/word = (2+3+59)*64
1203
- 1 | ((64 - 5) << 1),
1204
- });
1205
- const decoded_expect = [_]usize{
1206
- 0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000, // run 1
1207
- 0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000,
1208
- 0b10101010_10101010_10101010_10101010_10101010_10101010_10101010_10101010, // literal 1
1209
- 0b01010101_01010101_01010101_01010101_01010101_01010101_01010101_01010101, // literal 2
1210
- 0b10101010_10101010_10101010_10101010_10101010_10101010_10101010_10101010, // literal 3
1211
- } ++ ([1]usize{~@as(usize, 0)} ** (64 - 5));
1212
- const blocks_count = decoded_expect.len * @bitSizeOf(usize);
1213
-
1214
- const gpa = std.testing.allocator;
1215
- // Test decode.
1216
- var decoded_actual = try FreeSet.init_empty(gpa, blocks_count);
1217
- defer decoded_actual.deinit(gpa);
1218
-
1219
- decoded_actual.decode(.blocks_acquired, &.{encoded_expect});
1220
-
1221
- try std.testing.expectEqual(
1222
- decoded_expect.len,
1223
- bit_set_masks(decoded_actual.blocks_acquired).len,
1224
- );
1225
- try std.testing.expectEqualSlices(
1226
- usize,
1227
- &decoded_expect,
1228
- bit_set_masks(decoded_actual.blocks_acquired),
1229
- );
1230
-
1231
- // Test encode.
1232
- const encoded_actual = try gpa.alignedAlloc(
1233
- u8,
1234
- @alignOf(usize),
1235
- decoded_actual.encode_size_max(),
1236
- );
1237
- defer gpa.free(encoded_actual);
1238
-
1239
- // Pretend `opened` and `checkpoint_durable` are True as it is asserted in `encode`.
1240
- decoded_actual.opened = true;
1241
- decoded_actual.checkpoint_durable = true;
1242
- const encoded_actual_length = decoded_actual.encode(.blocks_acquired, &.{encoded_actual});
1243
- try std.testing.expectEqual(encoded_expect.len, encoded_actual_length);
1244
- }
1245
-
1246
- /// Returns the index of the first set/unset bit (relative to the start of the bitset) within
1247
- /// the range bit_min…bit_max (inclusive…exclusive).
1248
- fn find_bit(
1249
- bit_set: DynamicBitSetUnmanaged,
1250
- bit_min: usize,
1251
- bit_max: usize,
1252
- comptime bit_kind: std.bit_set.IteratorOptions.Type,
1253
- ) ?usize {
1254
- assert(bit_max >= bit_min);
1255
- assert(bit_max <= bit_set.bit_length);
1256
-
1257
- const word_start = @divFloor(bit_min, @bitSizeOf(MaskInt)); // Inclusive.
1258
- const word_offset = @mod(bit_min, @bitSizeOf(MaskInt));
1259
- const word_end = div_ceil(bit_max, @bitSizeOf(MaskInt)); // Exclusive.
1260
- const words_total = div_ceil(bit_set.bit_length, @bitSizeOf(MaskInt));
1261
- if (word_end == word_start) return null;
1262
- assert(word_end > word_start);
1263
-
1264
- // Only iterate over the subset of bits that were requested.
1265
- var iterator = bit_set.iterator(.{ .kind = bit_kind });
1266
- iterator.words_remain = bit_set.masks[word_start + 1 .. word_end];
1267
-
1268
- const mask = ~@as(MaskInt, 0);
1269
- var word = bit_set.masks[word_start];
1270
- if (bit_kind == .unset) word = ~word;
1271
- iterator.bits_remain = word & std.math.shl(MaskInt, mask, word_offset);
1272
-
1273
- if (word_end != words_total) iterator.last_word_mask = mask;
1274
-
1275
- const b = bit_min - word_offset + (iterator.next() orelse return null);
1276
- return if (b < bit_max) b else null;
1277
- }
1278
-
1279
- test "find_bit" {
1280
- var prng = stdx.PRNG.from_seed_testing();
1281
-
1282
- const gpa = std.testing.allocator;
1283
- for (1..(@bitSizeOf(std.DynamicBitSetUnmanaged.MaskInt) * 4) + 1) |bit_length| {
1284
- var bit_set = try std.DynamicBitSetUnmanaged.initEmpty(gpa, bit_length);
1285
- defer bit_set.deinit(gpa);
1286
-
1287
- const p = prng.int_inclusive(usize, 100);
1288
-
1289
- for (0..bit_length) |b| bit_set.setValue(b, p < prng.int_inclusive(usize, 100));
1290
-
1291
- for (0..20) |_| try test_find_bit(&prng, bit_set, .set);
1292
- for (20..40) |_| try test_find_bit(&prng, bit_set, .unset);
1293
- }
1294
- }
1295
-
1296
- fn test_find_bit(
1297
- prng: *stdx.PRNG,
1298
- bit_set: DynamicBitSetUnmanaged,
1299
- comptime bit_kind: std.bit_set.IteratorOptions.Type,
1300
- ) !void {
1301
- const bit_min = prng.int_inclusive(usize, bit_set.bit_length - 1);
1302
- const bit_max = prng.range_inclusive(usize, bit_min, bit_set.bit_length);
1303
- assert(bit_max >= bit_min);
1304
- assert(bit_max <= bit_set.bit_length);
1305
-
1306
- const bit_actual = find_bit(bit_set, bit_min, bit_max, bit_kind);
1307
- if (bit_actual) |bit| {
1308
- assert(bit_set.isSet(bit) == (bit_kind == .set));
1309
- assert(bit >= bit_min);
1310
- assert(bit < bit_max);
1311
- }
1312
-
1313
- var iterator = bit_set.iterator(.{ .kind = bit_kind });
1314
- while (iterator.next()) |bit| {
1315
- if (bit_min <= bit and bit < bit_max) {
1316
- try std.testing.expectEqual(bit_actual, bit);
1317
- break;
1318
- }
1319
- } else {
1320
- try std.testing.expectEqual(bit_actual, null);
1321
- }
1322
- }
1323
-
1324
- test "FreeSet.acquire part-way through a shard" {
1325
- const gpa = std.testing.allocator;
1326
- var set = try FreeSet.open_empty(gpa, FreeSet.shard_bits * 3);
1327
- defer set.deinit(gpa);
1328
-
1329
- const reservation_a = set.reserve(1).?;
1330
- defer set.forfeit(reservation_a);
1331
-
1332
- const reservation_b = set.reserve(2 * FreeSet.shard_bits).?;
1333
- defer set.forfeit(reservation_b);
1334
-
1335
- // Acquire all of reservation B.
1336
- // At the end, the first shard still has a bit free (reserved by A).
1337
- for (0..reservation_b.block_count) |i| {
1338
- const address = set.acquire(reservation_b).?;
1339
- try std.testing.expectEqual(address - 1, reservation_a.block_count + i);
1340
- set.verify_index();
1341
- }
1342
- try std.testing.expectEqual(set.acquire(reservation_b), null);
1343
- }
1344
-
1345
- test "FreeSet decode big bitset into small bitset" {
1346
- const shard_bits = FreeSet.shard_bits;
1347
-
1348
- const gpa = std.testing.allocator;
1349
- var big_set = try FreeSet.open_empty(gpa, 2 * shard_bits);
1350
- defer big_set.deinit(gpa);
1351
-
1352
- {
1353
- // Set up a big bitset (with blocks_count==2*shard_bits) with half the blocks free.
1354
- const acquired_block_count = @divFloor(big_set.blocks_acquired.bit_length, 2);
1355
- const reservation = big_set.reserve(acquired_block_count).?;
1356
- defer big_set.forfeit(reservation);
1357
-
1358
- for (0..acquired_block_count) |_| {
1359
- _ = big_set.acquire(reservation);
1360
- }
1361
- }
1362
-
1363
- var big_buffer = try gpa.alignedAlloc(
1364
- u8,
1365
- @alignOf(usize),
1366
- big_set.encode_size_max(),
1367
- );
1368
- defer gpa.free(big_buffer);
1369
-
1370
- const big_buffer_written = big_set.encode(.blocks_acquired, &.{big_buffer});
1371
-
1372
- // Decode the serialized big bitset into a smaller bitset (with blocks_count==shard_bits).
1373
- var small_set = try FreeSet.init_empty(gpa, shard_bits);
1374
- defer small_set.deinit(gpa);
1375
-
1376
- small_set.decode(.blocks_acquired, &.{big_buffer[0..big_buffer_written]});
1377
- for (0..shard_bits) |block| {
1378
- const address = block + 1;
1379
- try std.testing.expectEqual(big_set.is_free(address), false);
1380
- }
1381
- }