tigerbeetle 0.0.36 → 0.0.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. checksums.yaml +4 -4
  2. data/ext/tb_client/extconf.rb +13 -13
  3. data/ext/tb_client/tigerbeetle/LICENSE +177 -0
  4. data/ext/tb_client/tigerbeetle/build.zig +2327 -0
  5. data/ext/tb_client/tigerbeetle/src/aof.zig +1000 -0
  6. data/ext/tb_client/tigerbeetle/src/build_multiversion.zig +808 -0
  7. data/ext/tb_client/tigerbeetle/src/cdc/amqp/protocol.zig +1283 -0
  8. data/ext/tb_client/tigerbeetle/src/cdc/amqp/spec.zig +1704 -0
  9. data/ext/tb_client/tigerbeetle/src/cdc/amqp/types.zig +341 -0
  10. data/ext/tb_client/tigerbeetle/src/cdc/amqp.zig +1450 -0
  11. data/ext/tb_client/tigerbeetle/src/cdc/runner.zig +1659 -0
  12. data/ext/tb_client/tigerbeetle/src/clients/c/samples/main.c +406 -0
  13. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/context.zig +1084 -0
  14. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/echo_client.zig +286 -0
  15. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/packet.zig +158 -0
  16. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal.zig +229 -0
  17. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal_fuzz.zig +110 -0
  18. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.h +386 -0
  19. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.zig +34 -0
  20. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_exports.zig +281 -0
  21. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header.zig +312 -0
  22. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header_test.zig +138 -0
  23. data/ext/tb_client/tigerbeetle/src/clients/c/test.zig +466 -0
  24. data/ext/tb_client/tigerbeetle/src/clients/docs_samples.zig +157 -0
  25. data/ext/tb_client/tigerbeetle/src/clients/docs_types.zig +90 -0
  26. data/ext/tb_client/tigerbeetle/src/clients/dotnet/ci.zig +203 -0
  27. data/ext/tb_client/tigerbeetle/src/clients/dotnet/docs.zig +79 -0
  28. data/ext/tb_client/tigerbeetle/src/clients/dotnet/dotnet_bindings.zig +542 -0
  29. data/ext/tb_client/tigerbeetle/src/clients/go/ci.zig +109 -0
  30. data/ext/tb_client/tigerbeetle/src/clients/go/docs.zig +86 -0
  31. data/ext/tb_client/tigerbeetle/src/clients/go/go_bindings.zig +370 -0
  32. data/ext/tb_client/tigerbeetle/src/clients/go/pkg/native/tb_client.h +386 -0
  33. data/ext/tb_client/tigerbeetle/src/clients/java/ci.zig +167 -0
  34. data/ext/tb_client/tigerbeetle/src/clients/java/docs.zig +126 -0
  35. data/ext/tb_client/tigerbeetle/src/clients/java/java_bindings.zig +996 -0
  36. data/ext/tb_client/tigerbeetle/src/clients/java/src/client.zig +748 -0
  37. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni.zig +3238 -0
  38. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_tests.zig +1718 -0
  39. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_thread_cleaner.zig +190 -0
  40. data/ext/tb_client/tigerbeetle/src/clients/node/ci.zig +104 -0
  41. data/ext/tb_client/tigerbeetle/src/clients/node/docs.zig +75 -0
  42. data/ext/tb_client/tigerbeetle/src/clients/node/node.zig +522 -0
  43. data/ext/tb_client/tigerbeetle/src/clients/node/node_bindings.zig +267 -0
  44. data/ext/tb_client/tigerbeetle/src/clients/node/src/c.zig +3 -0
  45. data/ext/tb_client/tigerbeetle/src/clients/node/src/translate.zig +379 -0
  46. data/ext/tb_client/tigerbeetle/src/clients/python/ci.zig +131 -0
  47. data/ext/tb_client/tigerbeetle/src/clients/python/docs.zig +63 -0
  48. data/ext/tb_client/tigerbeetle/src/clients/python/python_bindings.zig +588 -0
  49. data/ext/tb_client/tigerbeetle/src/clients/rust/assets/tb_client.h +386 -0
  50. data/ext/tb_client/tigerbeetle/src/clients/rust/ci.zig +73 -0
  51. data/ext/tb_client/tigerbeetle/src/clients/rust/docs.zig +106 -0
  52. data/ext/tb_client/tigerbeetle/src/clients/rust/rust_bindings.zig +305 -0
  53. data/ext/tb_client/tigerbeetle/src/config.zig +296 -0
  54. data/ext/tb_client/tigerbeetle/src/constants.zig +790 -0
  55. data/ext/tb_client/tigerbeetle/src/copyhound.zig +202 -0
  56. data/ext/tb_client/tigerbeetle/src/counting_allocator.zig +72 -0
  57. data/ext/tb_client/tigerbeetle/src/direction.zig +11 -0
  58. data/ext/tb_client/tigerbeetle/src/docs_website/build.zig +158 -0
  59. data/ext/tb_client/tigerbeetle/src/docs_website/src/content.zig +156 -0
  60. data/ext/tb_client/tigerbeetle/src/docs_website/src/docs.zig +252 -0
  61. data/ext/tb_client/tigerbeetle/src/docs_website/src/file_checker.zig +313 -0
  62. data/ext/tb_client/tigerbeetle/src/docs_website/src/html.zig +87 -0
  63. data/ext/tb_client/tigerbeetle/src/docs_website/src/page_writer.zig +63 -0
  64. data/ext/tb_client/tigerbeetle/src/docs_website/src/redirects.zig +47 -0
  65. data/ext/tb_client/tigerbeetle/src/docs_website/src/search_index_writer.zig +28 -0
  66. data/ext/tb_client/tigerbeetle/src/docs_website/src/service_worker_writer.zig +61 -0
  67. data/ext/tb_client/tigerbeetle/src/docs_website/src/single_page_writer.zig +169 -0
  68. data/ext/tb_client/tigerbeetle/src/docs_website/src/website.zig +46 -0
  69. data/ext/tb_client/tigerbeetle/src/ewah.zig +445 -0
  70. data/ext/tb_client/tigerbeetle/src/ewah_benchmark.zig +128 -0
  71. data/ext/tb_client/tigerbeetle/src/ewah_fuzz.zig +171 -0
  72. data/ext/tb_client/tigerbeetle/src/fuzz_tests.zig +179 -0
  73. data/ext/tb_client/tigerbeetle/src/integration_tests.zig +662 -0
  74. data/ext/tb_client/tigerbeetle/src/io/common.zig +155 -0
  75. data/ext/tb_client/tigerbeetle/src/io/darwin.zig +1093 -0
  76. data/ext/tb_client/tigerbeetle/src/io/linux.zig +1880 -0
  77. data/ext/tb_client/tigerbeetle/src/io/test.zig +1005 -0
  78. data/ext/tb_client/tigerbeetle/src/io/windows.zig +1598 -0
  79. data/ext/tb_client/tigerbeetle/src/io.zig +34 -0
  80. data/ext/tb_client/tigerbeetle/src/iops.zig +134 -0
  81. data/ext/tb_client/tigerbeetle/src/list.zig +236 -0
  82. data/ext/tb_client/tigerbeetle/src/lsm/binary_search.zig +848 -0
  83. data/ext/tb_client/tigerbeetle/src/lsm/binary_search_benchmark.zig +179 -0
  84. data/ext/tb_client/tigerbeetle/src/lsm/cache_map.zig +424 -0
  85. data/ext/tb_client/tigerbeetle/src/lsm/cache_map_fuzz.zig +420 -0
  86. data/ext/tb_client/tigerbeetle/src/lsm/compaction.zig +2117 -0
  87. data/ext/tb_client/tigerbeetle/src/lsm/composite_key.zig +182 -0
  88. data/ext/tb_client/tigerbeetle/src/lsm/forest.zig +1119 -0
  89. data/ext/tb_client/tigerbeetle/src/lsm/forest_fuzz.zig +1102 -0
  90. data/ext/tb_client/tigerbeetle/src/lsm/forest_table_iterator.zig +200 -0
  91. data/ext/tb_client/tigerbeetle/src/lsm/groove.zig +1495 -0
  92. data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge.zig +739 -0
  93. data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge_benchmark.zig +166 -0
  94. data/ext/tb_client/tigerbeetle/src/lsm/manifest.zig +754 -0
  95. data/ext/tb_client/tigerbeetle/src/lsm/manifest_level.zig +1294 -0
  96. data/ext/tb_client/tigerbeetle/src/lsm/manifest_level_fuzz.zig +510 -0
  97. data/ext/tb_client/tigerbeetle/src/lsm/manifest_log.zig +1263 -0
  98. data/ext/tb_client/tigerbeetle/src/lsm/manifest_log_fuzz.zig +628 -0
  99. data/ext/tb_client/tigerbeetle/src/lsm/node_pool.zig +247 -0
  100. data/ext/tb_client/tigerbeetle/src/lsm/scan_buffer.zig +116 -0
  101. data/ext/tb_client/tigerbeetle/src/lsm/scan_builder.zig +543 -0
  102. data/ext/tb_client/tigerbeetle/src/lsm/scan_fuzz.zig +938 -0
  103. data/ext/tb_client/tigerbeetle/src/lsm/scan_lookup.zig +293 -0
  104. data/ext/tb_client/tigerbeetle/src/lsm/scan_merge.zig +362 -0
  105. data/ext/tb_client/tigerbeetle/src/lsm/scan_range.zig +99 -0
  106. data/ext/tb_client/tigerbeetle/src/lsm/scan_state.zig +17 -0
  107. data/ext/tb_client/tigerbeetle/src/lsm/scan_tree.zig +1036 -0
  108. data/ext/tb_client/tigerbeetle/src/lsm/schema.zig +617 -0
  109. data/ext/tb_client/tigerbeetle/src/lsm/scratch_memory.zig +84 -0
  110. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array.zig +1500 -0
  111. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_benchmark.zig +149 -0
  112. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_fuzz.zig +7 -0
  113. data/ext/tb_client/tigerbeetle/src/lsm/set_associative_cache.zig +865 -0
  114. data/ext/tb_client/tigerbeetle/src/lsm/table.zig +607 -0
  115. data/ext/tb_client/tigerbeetle/src/lsm/table_memory.zig +843 -0
  116. data/ext/tb_client/tigerbeetle/src/lsm/table_value_iterator.zig +105 -0
  117. data/ext/tb_client/tigerbeetle/src/lsm/timestamp_range.zig +40 -0
  118. data/ext/tb_client/tigerbeetle/src/lsm/tree.zig +630 -0
  119. data/ext/tb_client/tigerbeetle/src/lsm/tree_fuzz.zig +933 -0
  120. data/ext/tb_client/tigerbeetle/src/lsm/zig_zag_merge.zig +557 -0
  121. data/ext/tb_client/tigerbeetle/src/message_buffer.zig +469 -0
  122. data/ext/tb_client/tigerbeetle/src/message_bus.zig +1214 -0
  123. data/ext/tb_client/tigerbeetle/src/message_bus_fuzz.zig +936 -0
  124. data/ext/tb_client/tigerbeetle/src/message_pool.zig +343 -0
  125. data/ext/tb_client/tigerbeetle/src/multiversion.zig +2195 -0
  126. data/ext/tb_client/tigerbeetle/src/queue.zig +390 -0
  127. data/ext/tb_client/tigerbeetle/src/repl/completion.zig +201 -0
  128. data/ext/tb_client/tigerbeetle/src/repl/parser.zig +1356 -0
  129. data/ext/tb_client/tigerbeetle/src/repl/terminal.zig +496 -0
  130. data/ext/tb_client/tigerbeetle/src/repl.zig +1034 -0
  131. data/ext/tb_client/tigerbeetle/src/scripts/amqp.zig +973 -0
  132. data/ext/tb_client/tigerbeetle/src/scripts/cfo.zig +1866 -0
  133. data/ext/tb_client/tigerbeetle/src/scripts/changelog.zig +304 -0
  134. data/ext/tb_client/tigerbeetle/src/scripts/ci.zig +227 -0
  135. data/ext/tb_client/tigerbeetle/src/scripts/client_readmes.zig +658 -0
  136. data/ext/tb_client/tigerbeetle/src/scripts/devhub.zig +466 -0
  137. data/ext/tb_client/tigerbeetle/src/scripts/release.zig +1058 -0
  138. data/ext/tb_client/tigerbeetle/src/scripts.zig +105 -0
  139. data/ext/tb_client/tigerbeetle/src/shell.zig +1195 -0
  140. data/ext/tb_client/tigerbeetle/src/stack.zig +260 -0
  141. data/ext/tb_client/tigerbeetle/src/state_machine/auditor.zig +911 -0
  142. data/ext/tb_client/tigerbeetle/src/state_machine/workload.zig +2079 -0
  143. data/ext/tb_client/tigerbeetle/src/state_machine.zig +4872 -0
  144. data/ext/tb_client/tigerbeetle/src/state_machine_fuzz.zig +288 -0
  145. data/ext/tb_client/tigerbeetle/src/state_machine_tests.zig +3128 -0
  146. data/ext/tb_client/tigerbeetle/src/static_allocator.zig +82 -0
  147. data/ext/tb_client/tigerbeetle/src/stdx/bit_set.zig +157 -0
  148. data/ext/tb_client/tigerbeetle/src/stdx/bounded_array.zig +292 -0
  149. data/ext/tb_client/tigerbeetle/src/stdx/debug.zig +65 -0
  150. data/ext/tb_client/tigerbeetle/src/stdx/flags.zig +1414 -0
  151. data/ext/tb_client/tigerbeetle/src/stdx/mlock.zig +92 -0
  152. data/ext/tb_client/tigerbeetle/src/stdx/prng.zig +677 -0
  153. data/ext/tb_client/tigerbeetle/src/stdx/radix.zig +336 -0
  154. data/ext/tb_client/tigerbeetle/src/stdx/ring_buffer.zig +511 -0
  155. data/ext/tb_client/tigerbeetle/src/stdx/sort_test.zig +112 -0
  156. data/ext/tb_client/tigerbeetle/src/stdx/stdx.zig +1160 -0
  157. data/ext/tb_client/tigerbeetle/src/stdx/testing/low_level_hash_vectors.zig +142 -0
  158. data/ext/tb_client/tigerbeetle/src/stdx/testing/snaptest.zig +361 -0
  159. data/ext/tb_client/tigerbeetle/src/stdx/time_units.zig +275 -0
  160. data/ext/tb_client/tigerbeetle/src/stdx/unshare.zig +295 -0
  161. data/ext/tb_client/tigerbeetle/src/stdx/vendored/aegis.zig +436 -0
  162. data/ext/tb_client/tigerbeetle/src/stdx/windows.zig +48 -0
  163. data/ext/tb_client/tigerbeetle/src/stdx/zipfian.zig +402 -0
  164. data/ext/tb_client/tigerbeetle/src/storage.zig +489 -0
  165. data/ext/tb_client/tigerbeetle/src/storage_fuzz.zig +180 -0
  166. data/ext/tb_client/tigerbeetle/src/testing/bench.zig +146 -0
  167. data/ext/tb_client/tigerbeetle/src/testing/cluster/grid_checker.zig +53 -0
  168. data/ext/tb_client/tigerbeetle/src/testing/cluster/journal_checker.zig +61 -0
  169. data/ext/tb_client/tigerbeetle/src/testing/cluster/manifest_checker.zig +76 -0
  170. data/ext/tb_client/tigerbeetle/src/testing/cluster/message_bus.zig +110 -0
  171. data/ext/tb_client/tigerbeetle/src/testing/cluster/network.zig +412 -0
  172. data/ext/tb_client/tigerbeetle/src/testing/cluster/state_checker.zig +331 -0
  173. data/ext/tb_client/tigerbeetle/src/testing/cluster/storage_checker.zig +458 -0
  174. data/ext/tb_client/tigerbeetle/src/testing/cluster.zig +1198 -0
  175. data/ext/tb_client/tigerbeetle/src/testing/exhaustigen.zig +128 -0
  176. data/ext/tb_client/tigerbeetle/src/testing/fixtures.zig +181 -0
  177. data/ext/tb_client/tigerbeetle/src/testing/fuzz.zig +144 -0
  178. data/ext/tb_client/tigerbeetle/src/testing/id.zig +97 -0
  179. data/ext/tb_client/tigerbeetle/src/testing/io.zig +317 -0
  180. data/ext/tb_client/tigerbeetle/src/testing/marks.zig +126 -0
  181. data/ext/tb_client/tigerbeetle/src/testing/packet_simulator.zig +533 -0
  182. data/ext/tb_client/tigerbeetle/src/testing/reply_sequence.zig +154 -0
  183. data/ext/tb_client/tigerbeetle/src/testing/state_machine.zig +389 -0
  184. data/ext/tb_client/tigerbeetle/src/testing/storage.zig +1247 -0
  185. data/ext/tb_client/tigerbeetle/src/testing/table.zig +249 -0
  186. data/ext/tb_client/tigerbeetle/src/testing/time.zig +98 -0
  187. data/ext/tb_client/tigerbeetle/src/testing/tmp_tigerbeetle.zig +212 -0
  188. data/ext/tb_client/tigerbeetle/src/testing/vortex/constants.zig +26 -0
  189. data/ext/tb_client/tigerbeetle/src/testing/vortex/faulty_network.zig +580 -0
  190. data/ext/tb_client/tigerbeetle/src/testing/vortex/java_driver/ci.zig +39 -0
  191. data/ext/tb_client/tigerbeetle/src/testing/vortex/logged_process.zig +214 -0
  192. data/ext/tb_client/tigerbeetle/src/testing/vortex/rust_driver/ci.zig +34 -0
  193. data/ext/tb_client/tigerbeetle/src/testing/vortex/supervisor.zig +766 -0
  194. data/ext/tb_client/tigerbeetle/src/testing/vortex/workload.zig +543 -0
  195. data/ext/tb_client/tigerbeetle/src/testing/vortex/zig_driver.zig +181 -0
  196. data/ext/tb_client/tigerbeetle/src/tidy.zig +1448 -0
  197. data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_driver.zig +227 -0
  198. data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_load.zig +1069 -0
  199. data/ext/tb_client/tigerbeetle/src/tigerbeetle/cli.zig +1422 -0
  200. data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect.zig +1658 -0
  201. data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect_integrity.zig +518 -0
  202. data/ext/tb_client/tigerbeetle/src/tigerbeetle/libtb_client.zig +36 -0
  203. data/ext/tb_client/tigerbeetle/src/tigerbeetle/main.zig +646 -0
  204. data/ext/tb_client/tigerbeetle/src/tigerbeetle.zig +958 -0
  205. data/ext/tb_client/tigerbeetle/src/time.zig +236 -0
  206. data/ext/tb_client/tigerbeetle/src/trace/event.zig +745 -0
  207. data/ext/tb_client/tigerbeetle/src/trace/statsd.zig +462 -0
  208. data/ext/tb_client/tigerbeetle/src/trace.zig +556 -0
  209. data/ext/tb_client/tigerbeetle/src/unit_tests.zig +321 -0
  210. data/ext/tb_client/tigerbeetle/src/vopr.zig +1785 -0
  211. data/ext/tb_client/tigerbeetle/src/vortex.zig +101 -0
  212. data/ext/tb_client/tigerbeetle/src/vsr/checkpoint_trailer.zig +473 -0
  213. data/ext/tb_client/tigerbeetle/src/vsr/checksum.zig +208 -0
  214. data/ext/tb_client/tigerbeetle/src/vsr/checksum_benchmark.zig +43 -0
  215. data/ext/tb_client/tigerbeetle/src/vsr/client.zig +768 -0
  216. data/ext/tb_client/tigerbeetle/src/vsr/client_replies.zig +532 -0
  217. data/ext/tb_client/tigerbeetle/src/vsr/client_sessions.zig +338 -0
  218. data/ext/tb_client/tigerbeetle/src/vsr/clock.zig +1019 -0
  219. data/ext/tb_client/tigerbeetle/src/vsr/fault_detector.zig +279 -0
  220. data/ext/tb_client/tigerbeetle/src/vsr/free_set.zig +1381 -0
  221. data/ext/tb_client/tigerbeetle/src/vsr/free_set_fuzz.zig +315 -0
  222. data/ext/tb_client/tigerbeetle/src/vsr/grid.zig +1460 -0
  223. data/ext/tb_client/tigerbeetle/src/vsr/grid_blocks_missing.zig +757 -0
  224. data/ext/tb_client/tigerbeetle/src/vsr/grid_scrubber.zig +797 -0
  225. data/ext/tb_client/tigerbeetle/src/vsr/journal.zig +2586 -0
  226. data/ext/tb_client/tigerbeetle/src/vsr/marzullo.zig +308 -0
  227. data/ext/tb_client/tigerbeetle/src/vsr/message_header.zig +1777 -0
  228. data/ext/tb_client/tigerbeetle/src/vsr/multi_batch.zig +715 -0
  229. data/ext/tb_client/tigerbeetle/src/vsr/multi_batch_fuzz.zig +185 -0
  230. data/ext/tb_client/tigerbeetle/src/vsr/repair_budget.zig +333 -0
  231. data/ext/tb_client/tigerbeetle/src/vsr/replica.zig +12355 -0
  232. data/ext/tb_client/tigerbeetle/src/vsr/replica_format.zig +416 -0
  233. data/ext/tb_client/tigerbeetle/src/vsr/replica_reformat.zig +165 -0
  234. data/ext/tb_client/tigerbeetle/src/vsr/replica_test.zig +2910 -0
  235. data/ext/tb_client/tigerbeetle/src/vsr/routing.zig +1075 -0
  236. data/ext/tb_client/tigerbeetle/src/vsr/superblock.zig +1603 -0
  237. data/ext/tb_client/tigerbeetle/src/vsr/superblock_fuzz.zig +484 -0
  238. data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums.zig +405 -0
  239. data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +355 -0
  240. data/ext/tb_client/tigerbeetle/src/vsr/sync.zig +29 -0
  241. data/ext/tb_client/tigerbeetle/src/vsr.zig +1727 -0
  242. data/lib/tb_client/shared_lib.rb +12 -5
  243. data/lib/tigerbeetle/platforms.rb +9 -0
  244. data/lib/tigerbeetle/version.rb +1 -1
  245. data/tigerbeetle.gemspec +22 -5
  246. metadata +242 -3
  247. data/ext/tb_client/pkg.tar.gz +0 -0
@@ -0,0 +1,1160 @@
1
+ //! Extensions to the standard library -- things which could have been in std, but aren't.
2
+ //!
3
+ //! Unlike std, the namespacing is relatively flat: `stdx.PRNG` rather than `stdx.random.PRNG`.
4
+ //! We don't care about backwards compatibility and prefer directness to scalability. Hierarchy can
5
+ //! always be introduced later, when/if stdx grows too large.
6
+
7
+ const std = @import("std");
8
+ const builtin = @import("builtin");
9
+ const assert = std.debug.assert;
10
+
11
+ pub const BitSetType = @import("bit_set.zig").BitSetType;
12
+ pub const BoundedArrayType = @import("bounded_array.zig").BoundedArrayType;
13
+ pub const PRNG = @import("prng.zig");
14
+ pub const RingBufferType = @import("ring_buffer.zig").RingBufferType;
15
+ pub const Snap = @import("testing/snaptest.zig").Snap;
16
+ pub const ZipfianGenerator = @import("zipfian.zig").ZipfianGenerator;
17
+ pub const ZipfianShuffled = @import("zipfian.zig").ZipfianShuffled;
18
+
19
+ pub const aegis = @import("vendored/aegis.zig");
20
+ pub const dbg = @import("debug.zig").dbg;
21
+ pub const flags = @import("flags.zig").parse;
22
+ pub const parse_flag_value_fuzz = @import("flags.zig").parse_flag_value_fuzz;
23
+ pub const memory_lock_allocated = @import("mlock.zig").memory_lock_allocated;
24
+ pub const timeit = @import("debug.zig").timeit;
25
+ pub const unshare = @import("unshare.zig");
26
+ pub const windows = @import("windows.zig");
27
+ pub const radix_sort = @import("radix.zig").sort;
28
+
29
+ pub const Instant = @import("time_units.zig").Instant;
30
+ pub const Duration = @import("time_units.zig").Duration;
31
+ pub const InstantUnix = @import("time_units.zig").InstantUnix;
32
+
33
+ // Import these as `const GiB = stdx.GiB;`
34
+ pub const KiB = 1 << 10;
35
+ pub const MiB = 1 << 20;
36
+ pub const GiB = 1 << 30;
37
+ pub const TiB = 1 << 40;
38
+ pub const PiB = 1 << 50;
39
+ // pub const NiB = "Some people say my love cannot be true";
40
+
41
+ comptime {
42
+ assert(KiB == 1024);
43
+ assert(MiB == 1024 * KiB);
44
+ assert(GiB == 1024 * MiB);
45
+ assert(TiB == 1024 * GiB);
46
+ assert(PiB == 1024 * TiB);
47
+ }
48
+
49
+ pub inline fn div_ceil(numerator: anytype, denominator: anytype) @TypeOf(numerator, denominator) {
50
+ comptime {
51
+ switch (@typeInfo(@TypeOf(numerator))) {
52
+ .int => |int| assert(int.signedness == .unsigned),
53
+ .comptime_int => assert(numerator >= 0),
54
+ else => @compileError("div_ceil: invalid numerator type"),
55
+ }
56
+
57
+ switch (@typeInfo(@TypeOf(denominator))) {
58
+ .int => |int| assert(int.signedness == .unsigned),
59
+ .comptime_int => assert(denominator > 0),
60
+ else => @compileError("div_ceil: invalid denominator type"),
61
+ }
62
+ }
63
+
64
+ assert(denominator > 0);
65
+
66
+ if (numerator == 0) return 0;
67
+ return @divFloor(numerator - 1, denominator) + 1;
68
+ }
69
+
70
+ test "div_ceil" {
71
+ // Comptime ints.
72
+ try std.testing.expectEqual(div_ceil(0, 8), 0);
73
+ try std.testing.expectEqual(div_ceil(1, 8), 1);
74
+ try std.testing.expectEqual(div_ceil(7, 8), 1);
75
+ try std.testing.expectEqual(div_ceil(8, 8), 1);
76
+ try std.testing.expectEqual(div_ceil(9, 8), 2);
77
+
78
+ // Unsized ints
79
+ const max = std.math.maxInt(u64);
80
+ try std.testing.expectEqual(div_ceil(@as(u64, 0), 8), 0);
81
+ try std.testing.expectEqual(div_ceil(@as(u64, 1), 8), 1);
82
+ try std.testing.expectEqual(div_ceil(@as(u64, max), 2), max / 2 + 1);
83
+ try std.testing.expectEqual(div_ceil(@as(u64, max) - 1, 2), max / 2);
84
+ try std.testing.expectEqual(div_ceil(@as(u64, max) - 2, 2), max / 2);
85
+ }
86
+
87
+ pub const SizePrecision = enum { exact, inexact };
88
+
89
+ pub inline fn copy_left(
90
+ comptime precision: SizePrecision,
91
+ comptime T: type,
92
+ target: []T,
93
+ source: []const T,
94
+ ) void {
95
+ switch (precision) {
96
+ .exact => assert(target.len == source.len),
97
+ .inexact => assert(target.len >= source.len),
98
+ }
99
+
100
+ if (!disjoint_slices(T, T, target, source)) {
101
+ assert(@intFromPtr(target.ptr) < @intFromPtr(source.ptr));
102
+ }
103
+
104
+ // (Bypass tidy's ban.)
105
+ const copyForwards = std.mem.copyForwards;
106
+ copyForwards(T, target, source);
107
+ }
108
+
109
+ test "copy_left" {
110
+ const a = try std.testing.allocator.alloc(usize, 8);
111
+ defer std.testing.allocator.free(a);
112
+
113
+ for (a, 0..) |*v, i| v.* = i;
114
+ copy_left(.exact, usize, a[0..6], a[2..]);
115
+ try std.testing.expect(std.mem.eql(usize, a, &.{ 2, 3, 4, 5, 6, 7, 6, 7 }));
116
+ }
117
+
118
+ pub inline fn copy_right(
119
+ comptime precision: SizePrecision,
120
+ comptime T: type,
121
+ target: []T,
122
+ source: []const T,
123
+ ) void {
124
+ switch (precision) {
125
+ .exact => assert(target.len == source.len),
126
+ .inexact => assert(target.len >= source.len),
127
+ }
128
+
129
+ if (!disjoint_slices(T, T, target, source)) {
130
+ assert(@intFromPtr(target.ptr) > @intFromPtr(source.ptr));
131
+ }
132
+
133
+ // (Bypass tidy's ban.)
134
+ const copyBackwards = std.mem.copyBackwards;
135
+ copyBackwards(T, target, source);
136
+ }
137
+
138
+ test "copy_right" {
139
+ const a = try std.testing.allocator.alloc(usize, 8);
140
+ defer std.testing.allocator.free(a);
141
+
142
+ for (a, 0..) |*v, i| v.* = i;
143
+ copy_right(.exact, usize, a[2..], a[0..6]);
144
+ try std.testing.expect(std.mem.eql(usize, a, &.{ 0, 1, 0, 1, 2, 3, 4, 5 }));
145
+ }
146
+
147
+ pub inline fn copy_disjoint(
148
+ comptime precision: SizePrecision,
149
+ comptime T: type,
150
+ target: []T,
151
+ source: []const T,
152
+ ) void {
153
+ switch (precision) {
154
+ .exact => assert(target.len == source.len),
155
+ .inexact => assert(target.len >= source.len),
156
+ }
157
+
158
+ // disjoint_slices() doesn't work in comptime, because of limitations with @intFromPtr:
159
+ // https://github.com/ziglang/zig/issues/23072.
160
+ //
161
+ // It's also possible to construct slices into an array at comptime that are _not_ disjoint,
162
+ // which would violate the intention of this function, so it can't just be skipped.
163
+ assert(!@inComptime());
164
+ assert(disjoint_slices(T, T, target, source));
165
+
166
+ @memcpy // Bypass tidy ban.
167
+ (target[0..source.len], source);
168
+ }
169
+
170
+ pub inline fn disjoint_slices(comptime A: type, comptime B: type, a: []const A, b: []const B) bool {
171
+ return @intFromPtr(a.ptr) + a.len * @sizeOf(A) <= @intFromPtr(b.ptr) or
172
+ @intFromPtr(b.ptr) + b.len * @sizeOf(B) <= @intFromPtr(a.ptr);
173
+ }
174
+
175
+ test "disjoint_slices" {
176
+ const a = try std.testing.allocator.alignedAlloc(u8, @sizeOf(u32), 8 * @sizeOf(u32));
177
+ defer std.testing.allocator.free(a);
178
+
179
+ const b = try std.testing.allocator.alloc(u32, 8);
180
+ defer std.testing.allocator.free(b);
181
+
182
+ try std.testing.expectEqual(true, disjoint_slices(u8, u32, a, b));
183
+ try std.testing.expectEqual(true, disjoint_slices(u32, u8, b, a));
184
+
185
+ try std.testing.expectEqual(true, disjoint_slices(u8, u8, a, a[0..0]));
186
+ try std.testing.expectEqual(true, disjoint_slices(u32, u32, b, b[0..0]));
187
+
188
+ try std.testing.expectEqual(false, disjoint_slices(u8, u8, a, a[0..1]));
189
+ try std.testing.expectEqual(false, disjoint_slices(u8, u8, a, a[a.len - 1 .. a.len]));
190
+
191
+ try std.testing.expectEqual(false, disjoint_slices(u32, u32, b, b[0..1]));
192
+ try std.testing.expectEqual(false, disjoint_slices(u32, u32, b, b[b.len - 1 .. b.len]));
193
+
194
+ try std.testing.expectEqual(false, disjoint_slices(u8, u32, a, std.mem.bytesAsSlice(u32, a)));
195
+ try std.testing.expectEqual(false, disjoint_slices(u32, u8, b, std.mem.sliceAsBytes(b)));
196
+ }
197
+
198
+ /// Checks that a byteslice is zeroed.
199
+ pub fn zeroed(bytes: []const u8) bool {
200
+ // This implementation already gets vectorized
201
+ // https://godbolt.org/z/46cMsPKPc
202
+ var byte_bits: u8 = 0;
203
+ for (bytes) |byte| {
204
+ byte_bits |= byte;
205
+ }
206
+ return byte_bits == 0;
207
+ }
208
+
209
+ /// Similar to `std.mem.bytesAsSlice`, but allows buffers with inexact sizes,
210
+ /// returning the largest possible slice that is less than or equal to the buffer length.
211
+ /// Differently from `std.mem.bytesAsSlice` that can return `[]align(1) T`, this function
212
+ /// always `@alignCast` the result.
213
+ pub fn bytes_as_slice(
214
+ comptime precision: SizePrecision,
215
+ comptime T: type,
216
+ bytes: anytype,
217
+ ) type: {
218
+ const type_info = @typeInfo(@TypeOf(bytes));
219
+ switch (type_info) {
220
+ .pointer => |info| switch (info.size) {
221
+ .one => switch (@typeInfo(info.child)) {
222
+ .array => |array_info| assert(array_info.child == u8),
223
+ else => unreachable,
224
+ },
225
+ .slice => assert(info.child == u8),
226
+ else => unreachable,
227
+ },
228
+ else => unreachable,
229
+ }
230
+
231
+ break :type if (type_info.pointer.is_const) []const T else []T;
232
+ } {
233
+ switch (precision) {
234
+ .exact => {
235
+ assert(bytes.len % @sizeOf(T) == 0);
236
+ return @alignCast(std.mem.bytesAsSlice(T, bytes));
237
+ },
238
+ .inexact => {
239
+ const size = @divFloor(bytes.len, @sizeOf(T)) * @sizeOf(T);
240
+ return @alignCast(std.mem.bytesAsSlice(T, bytes[0..size]));
241
+ },
242
+ }
243
+ }
244
+
245
+ test bytes_as_slice {
246
+ var buffer: [64]u8 = undefined;
247
+ const T10 = extern struct { content: [10]u8 };
248
+ const T16 = extern struct { content: [16]u8 };
249
+
250
+ try std.testing.expectEqual(
251
+ @as(usize, 4),
252
+ bytes_as_slice(.exact, T16, buffer[0..]).len,
253
+ );
254
+ try std.testing.expectEqual(
255
+ @as(usize, 6),
256
+ bytes_as_slice(.exact, T10, buffer[0..60]).len,
257
+ );
258
+
259
+ try std.testing.expectEqual(
260
+ @as(usize, 6),
261
+ bytes_as_slice(.inexact, T10, buffer[0..]).len,
262
+ );
263
+ try std.testing.expectEqual(
264
+ @as(usize, 4),
265
+ bytes_as_slice(.inexact, T16, buffer[0..]).len,
266
+ );
267
+ try std.testing.expectEqual(
268
+ @as(usize, 6),
269
+ bytes_as_slice(.inexact, T10, buffer[0 .. buffer.len - 1]).len,
270
+ );
271
+ try std.testing.expectEqual(
272
+ @as(usize, 3),
273
+ bytes_as_slice(.inexact, T16, buffer[0 .. buffer.len - 1]).len,
274
+ );
275
+ try std.testing.expectEqual(
276
+ @as(usize, 5),
277
+ bytes_as_slice(.inexact, T10, buffer[0 .. buffer.len - 10]).len,
278
+ );
279
+ try std.testing.expectEqual(
280
+ @as(usize, 3),
281
+ bytes_as_slice(.inexact, T16, buffer[0 .. buffer.len - 10]).len,
282
+ );
283
+ }
284
+
285
+ /// Splits the `haystack` around the first occurrence of `needle`, returning parts before and after.
286
+ ///
287
+ /// This is a Zig version of Go's `string.Cut` / Rust's `str::split_once`. Cut turns out to be a
288
+ /// surprisingly versatile primitive for ad-hoc string processing. Often `std.mem.indexOf` and
289
+ /// `std.mem.split` can be replaced with a shorter and clearer code using `cut`.
290
+ pub fn cut(haystack: []const u8, needle: []const u8) ?struct { []const u8, []const u8 } {
291
+ const index = std.mem.indexOf(u8, haystack, needle) orelse return null;
292
+
293
+ return .{ haystack[0..index], haystack[index + needle.len ..] };
294
+ }
295
+
296
+ test cut {
297
+ try std.testing.expectEqualStrings("he", cut("hello world", "l").?[0]);
298
+ try std.testing.expectEqualStrings("lo world", cut("hello world", "l").?[1]);
299
+ assert(null == cut("hello world", "x"));
300
+ }
301
+
302
+ pub fn cut_prefix(haystack: []const u8, needle: []const u8) ?[]const u8 {
303
+ if (std.mem.startsWith(u8, haystack, needle)) {
304
+ return haystack[needle.len..];
305
+ }
306
+ return null;
307
+ }
308
+
309
+ test cut_prefix {
310
+ try std.testing.expectEqualStrings(" world", cut_prefix("hello world", "hello").?);
311
+ assert(null == cut_prefix("hello world", "hellnope"));
312
+ }
313
+
314
+ pub fn cut_suffix(haystack: []const u8, needle: []const u8) ?[]const u8 {
315
+ if (std.mem.endsWith(u8, haystack, needle)) {
316
+ return haystack[0 .. haystack.len - needle.len];
317
+ }
318
+ return null;
319
+ }
320
+
321
+ test cut_suffix {
322
+ try std.testing.expectEqualStrings("hello ", cut_suffix("hello world", "world").?);
323
+ assert(null == cut_suffix("hello world", "hello"));
324
+ }
325
+
326
+ /// `maybe` is the dual of `assert`: it signals that condition is sometimes true
327
+ /// and sometimes false.
328
+ ///
329
+ /// Currently we use it for documentation, but maybe one day we plug it into
330
+ /// coverage.
331
+ pub fn maybe(ok: bool) void {
332
+ assert(ok or !ok);
333
+ }
334
+
335
+ pub const log = if (builtin.is_test)
336
+ // Downgrade `err` to `warn` for tests.
337
+ // Zig fails any test that does `log.err`, but we want to test those code paths here.
338
+ struct {
339
+ pub fn scoped(comptime scope: @Type(.enum_literal)) type {
340
+ const base = std.log.scoped(scope);
341
+ return struct {
342
+ pub const err = warn;
343
+ pub const warn = base.warn;
344
+ pub const info = base.info;
345
+ pub const debug = base.debug;
346
+ };
347
+ }
348
+ }
349
+ else
350
+ std.log;
351
+
352
+ /// An alternative to the default logFn from `std.log`, which prepends a UTC timestamp.
353
+ pub fn log_with_timestamp(
354
+ comptime message_level: std.log.Level,
355
+ comptime scope: @Type(.enum_literal),
356
+ comptime format: []const u8,
357
+ args: anytype,
358
+ ) void {
359
+ const level_text = comptime message_level.asText();
360
+ const scope_prefix = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
361
+ const instant_unix = InstantUnix.now();
362
+
363
+ const stderr = std.io.getStdErr().writer();
364
+ var buffered_writer = std.io.bufferedWriter(stderr);
365
+ const writer = buffered_writer.writer();
366
+
367
+ nosuspend {
368
+ instant_unix.format("", .{}, writer) catch return;
369
+ writer.print(" " ++ level_text ++ scope_prefix ++ format ++ "\n", args) catch return;
370
+ buffered_writer.flush() catch return;
371
+ }
372
+ }
373
+
374
+ /// Compare two values by directly comparing the underlying memory.
375
+ ///
376
+ /// Assert at compile time that this is a reasonable thing to do for a given `T`. That is, check
377
+ /// that:
378
+ /// - `T` doesn't have any non-deterministic padding,
379
+ /// - `T` doesn't embed any pointers.
380
+ pub fn equal_bytes(comptime T: type, a: *const T, b: *const T) bool {
381
+ comptime assert(has_unique_representation(T));
382
+ comptime assert(!has_pointers(T));
383
+ comptime assert(@sizeOf(T) * 8 == @bitSizeOf(T));
384
+
385
+ // Pick the biggest "word" for word-wise comparison, and don't try to early-return on the first
386
+ // mismatch, so that a compiler can vectorize the loop.
387
+
388
+ const Word = comptime for (.{ u64, u32, u16, u8 }) |Word| {
389
+ if (@alignOf(T) >= @alignOf(Word) and @sizeOf(T) % @sizeOf(Word) == 0) break Word;
390
+ } else unreachable;
391
+
392
+ const a_words = std.mem.bytesAsSlice(Word, std.mem.asBytes(a));
393
+ const b_words = std.mem.bytesAsSlice(Word, std.mem.asBytes(b));
394
+ assert(a_words.len == b_words.len);
395
+
396
+ var total: Word = 0;
397
+ for (a_words, b_words) |a_word, b_word| {
398
+ total |= a_word ^ b_word;
399
+ }
400
+
401
+ return total == 0;
402
+ }
403
+
404
+ fn has_pointers(comptime T: type) bool {
405
+ switch (@typeInfo(T)) {
406
+ .pointer => return true,
407
+ // Be conservative.
408
+ else => return true,
409
+
410
+ .bool, .int, .@"enum" => return false,
411
+
412
+ .array => |info| return comptime has_pointers(info.child),
413
+ .@"struct" => |info| {
414
+ inline for (info.fields) |field| {
415
+ if (comptime has_pointers(field.type)) return true;
416
+ }
417
+ return false;
418
+ },
419
+ }
420
+ }
421
+
422
+ /// Checks that a type does not have implicit padding.
423
+ pub fn no_padding(comptime T: type) bool {
424
+ comptime switch (@typeInfo(T)) {
425
+ .void => return true,
426
+ .int => return @bitSizeOf(T) == 8 * @sizeOf(T),
427
+ .array => |info| return no_padding(info.child),
428
+ .@"struct" => |info| {
429
+ switch (info.layout) {
430
+ .auto => return false,
431
+ .@"extern" => {
432
+ for (info.fields) |field| {
433
+ if (!no_padding(field.type)) return false;
434
+ }
435
+
436
+ // Check offsets of u128 and pseudo-u256 fields.
437
+ for (info.fields) |field| {
438
+ if (field.type == u128) {
439
+ const offset = @offsetOf(T, field.name);
440
+ if (offset % @sizeOf(u128) != 0) return false;
441
+
442
+ if (@hasField(T, field.name ++ "_padding")) {
443
+ if (offset % @sizeOf(u256) != 0) return false;
444
+ if (offset + @sizeOf(u128) !=
445
+ @offsetOf(T, field.name ++ "_padding"))
446
+ {
447
+ return false;
448
+ }
449
+ }
450
+ }
451
+ }
452
+
453
+ var offset = 0;
454
+ for (info.fields) |field| {
455
+ const field_offset = @offsetOf(T, field.name);
456
+ if (offset != field_offset) return false;
457
+ offset += @sizeOf(field.type);
458
+ }
459
+ return offset == @sizeOf(T);
460
+ },
461
+ .@"packed" => return @bitSizeOf(T) == 8 * @sizeOf(T),
462
+ }
463
+ },
464
+ .@"enum" => |info| {
465
+ maybe(info.is_exhaustive);
466
+ return no_padding(info.tag_type);
467
+ },
468
+ .pointer => return false,
469
+ .@"union" => return false,
470
+ else => return false,
471
+ };
472
+ }
473
+
474
+ test no_padding {
475
+ comptime for (.{
476
+ u8,
477
+ extern struct { x: u8 },
478
+ packed struct { x: u7, y: u1 },
479
+ extern struct { x: extern struct { y: u64, z: u64 } },
480
+ enum(u8) { x },
481
+ }) |T| {
482
+ assert(no_padding(T));
483
+ };
484
+
485
+ comptime for (.{
486
+ u7,
487
+ struct { x: u7 },
488
+ struct { x: u8 },
489
+ struct { x: u64, y: u32 },
490
+ extern struct { x: extern struct { y: u64, z: u32 } },
491
+ packed struct { x: u7 },
492
+ enum(u7) { x },
493
+ }) |T| {
494
+ assert(!no_padding(T));
495
+ };
496
+ }
497
+
498
+ pub inline fn hash_inline(value: anytype) u64 {
499
+ comptime {
500
+ assert(no_padding(@TypeOf(value)));
501
+ assert(has_unique_representation(@TypeOf(value)));
502
+ }
503
+ return low_level_hash(0, switch (@typeInfo(@TypeOf(value))) {
504
+ .@"struct", .int => std.mem.asBytes(&value),
505
+ else => @compileError("unsupported hashing for " ++ @typeName(@TypeOf(value))),
506
+ });
507
+ }
508
+
509
+ /// Inline version of Google Abseil "LowLevelHash" (inspired by wyhash).
510
+ /// https://github.com/abseil/abseil-cpp/blob/master/absl/hash/internal/low_level_hash.cc
511
+ inline fn low_level_hash(seed: u64, input: anytype) u64 {
512
+ const salt = [_]u64{
513
+ 0xa0761d6478bd642f,
514
+ 0xe7037ed1a0b428db,
515
+ 0x8ebc6af09c88c6e3,
516
+ 0x589965cc75374cc3,
517
+ 0x1d8e4e27c47d124f,
518
+ };
519
+
520
+ var in: []const u8 = input;
521
+ var state = seed ^ salt[0];
522
+ const starting_len = input.len;
523
+
524
+ if (in.len > 64) {
525
+ var dup = [_]u64{ state, state };
526
+ defer state = dup[0] ^ dup[1];
527
+
528
+ while (in.len > 64) : (in = in[64..]) {
529
+ for (@as([2][4]u64, @bitCast(in[0..64].*)), 0..) |chunk, i| {
530
+ const mix1 = @as(u128, chunk[0] ^ salt[(i * 2) + 1]) *% (chunk[1] ^ dup[i]);
531
+ const mix2 = @as(u128, chunk[2] ^ salt[(i * 2) + 2]) *% (chunk[3] ^ dup[i]);
532
+ dup[i] = @as(u64, @truncate(mix1 ^ (mix1 >> 64)));
533
+ dup[i] ^= @as(u64, @truncate(mix2 ^ (mix2 >> 64)));
534
+ }
535
+ }
536
+ }
537
+
538
+ while (in.len > 16) : (in = in[16..]) {
539
+ const chunk = @as([2]u64, @bitCast(in[0..16].*));
540
+ const mixed = @as(u128, chunk[0] ^ salt[1]) *% (chunk[1] ^ state);
541
+ state = @as(u64, @truncate(mixed ^ (mixed >> 64)));
542
+ }
543
+
544
+ var chunk: [2]u64 = .{ 0, 0 };
545
+ if (in.len > 8) {
546
+ chunk[0] = @as(u64, @bitCast(in[0..8].*));
547
+ chunk[1] = @as(u64, @bitCast(in[in.len - 8 ..][0..8].*));
548
+ } else if (in.len > 3) {
549
+ chunk[0] = @as(u32, @bitCast(in[0..4].*));
550
+ chunk[1] = @as(u32, @bitCast(in[in.len - 4 ..][0..4].*));
551
+ } else if (in.len > 0) {
552
+ chunk[0] = (@as(u64, in[0]) << 16) | (@as(u64, in[in.len / 2]) << 8) | in[in.len - 1];
553
+ }
554
+
555
+ var mixed = @as(u128, chunk[0] ^ salt[1]) *% (chunk[1] ^ state);
556
+ mixed = @as(u64, @truncate(mixed ^ (mixed >> 64)));
557
+ mixed *%= (@as(u64, starting_len) ^ salt[1]);
558
+ return @as(u64, @truncate(mixed ^ (mixed >> 64)));
559
+ }
560
+
561
+ test "hash_inline" {
562
+ for (@import("testing/low_level_hash_vectors.zig").cases) |case| {
563
+ var buffer: [0x100]u8 = undefined;
564
+
565
+ const b64 = std.base64.standard;
566
+ const input = buffer[0..try b64.Decoder.calcSizeForSlice(case.b64)];
567
+ try b64.Decoder.decode(input, case.b64);
568
+
569
+ const hash = low_level_hash(case.seed, input);
570
+ try std.testing.expectEqual(case.hash, hash);
571
+ }
572
+ }
573
+
574
+ /// Returns a copy of `base` with fields changed according to `diff`.
575
+ ///
576
+ /// Intended exclusively for table-driven prototype-based tests. Write
577
+ /// updates explicitly in production code.
578
+ pub fn update(base: anytype, diff: anytype) @TypeOf(base) {
579
+ assert(builtin.is_test);
580
+ assert(@typeInfo(@TypeOf(base)) == .@"struct");
581
+
582
+ var updated = base;
583
+ inline for (std.meta.fields(@TypeOf(diff))) |f| {
584
+ @field(updated, f.name) = @field(diff, f.name);
585
+ }
586
+ return updated;
587
+ }
588
+
589
+ // std.SemanticVersion requires there be no extra characters after the
590
+ // major/minor/patch numbers. But when we try to parse `uname
591
+ // --kernel-release` (note: while Linux doesn't follow semantic
592
+ // versioning, it doesn't violate it either), some distributions have
593
+ // extra characters, such as this Fedora one: 6.3.8-100.fc37.x86_64, and
594
+ // this WSL one has more than three dots:
595
+ // 5.15.90.1-microsoft-standard-WSL2.
596
+ pub fn parse_dirty_semver(dirty_release: []const u8) !std.SemanticVersion {
597
+ const release = blk: {
598
+ var last_valid_version_character_index: usize = 0;
599
+ var dots_found: u8 = 0;
600
+ for (dirty_release) |c| {
601
+ if (c == '.') dots_found += 1;
602
+ if (dots_found == 3) {
603
+ break;
604
+ }
605
+
606
+ if (c == '.' or (c >= '0' and c <= '9')) {
607
+ last_valid_version_character_index += 1;
608
+ continue;
609
+ }
610
+
611
+ break;
612
+ }
613
+
614
+ break :blk dirty_release[0..last_valid_version_character_index];
615
+ };
616
+
617
+ return std.SemanticVersion.parse(release);
618
+ }
619
+
620
+ test "stdx.zig: parse_dirty_semver" {
621
+ const SemverTestCase = struct {
622
+ dirty_release: []const u8,
623
+ expected_version: std.SemanticVersion,
624
+ };
625
+
626
+ const cases = &[_]SemverTestCase{
627
+ .{
628
+ .dirty_release = "1.2.3",
629
+ .expected_version = std.SemanticVersion{ .major = 1, .minor = 2, .patch = 3 },
630
+ },
631
+ .{
632
+ .dirty_release = "1001.843.909",
633
+ .expected_version = std.SemanticVersion{ .major = 1001, .minor = 843, .patch = 909 },
634
+ },
635
+ .{
636
+ .dirty_release = "6.3.8-100.fc37.x86_64",
637
+ .expected_version = std.SemanticVersion{ .major = 6, .minor = 3, .patch = 8 },
638
+ },
639
+ .{
640
+ .dirty_release = "5.15.90.1-microsoft-standard-WSL2",
641
+ .expected_version = std.SemanticVersion{ .major = 5, .minor = 15, .patch = 90 },
642
+ },
643
+ };
644
+ for (cases) |case| {
645
+ const version = try parse_dirty_semver(case.dirty_release);
646
+ try std.testing.expectEqual(case.expected_version, version);
647
+ }
648
+ }
649
+
650
+ // TODO(zig): std doesn't have the statfs / fstatfs syscalls to get the type of a filesystem.
651
+ // Once those are available, this can be removed.
652
+ // The `statfs` definition used by the Linux kernel, and the magic number for tmpfs, from
653
+ // `man 2 fstatfs`.
654
+ const fsblkcnt64_t = u64;
655
+ const fsfilcnt64_t = u64;
656
+ const fsword_t = i64;
657
+ const fsid_t = u64;
658
+
659
+ pub const TmpfsMagic = 0x01021994;
660
+ pub const StatFs = extern struct {
661
+ f_type: fsword_t,
662
+ f_bsize: fsword_t,
663
+ f_blocks: fsblkcnt64_t,
664
+ f_bfree: fsblkcnt64_t,
665
+ f_bavail: fsblkcnt64_t,
666
+ f_files: fsfilcnt64_t,
667
+ f_ffree: fsfilcnt64_t,
668
+ f_fsid: fsid_t,
669
+ f_namelen: fsword_t,
670
+ f_frsize: fsword_t,
671
+ f_flags: fsword_t,
672
+ f_spare: [4]fsword_t,
673
+ };
674
+
675
+ pub fn fstatfs(fd: i32, statfs_buf: *StatFs) usize {
676
+ return std.os.linux.syscall2(
677
+ if (@hasField(std.os.linux.SYS, "fstatfs64")) .fstatfs64 else .fstatfs,
678
+ @as(usize, @bitCast(@as(isize, fd))),
679
+ @intFromPtr(statfs_buf),
680
+ );
681
+ }
682
+
683
+ /// True if every value of the type `T` has a unique bit pattern representing it.
684
+ /// In other words, `T` has no unused bits and no padding.
685
+ pub fn has_unique_representation(comptime T: type) bool {
686
+ switch (@typeInfo(T)) {
687
+ else => return false, // TODO can we know if it's true for some of these types ?
688
+
689
+ .@"enum",
690
+ .error_set,
691
+ .@"fn",
692
+ => return true,
693
+
694
+ .bool => return false,
695
+
696
+ .int => |info| return @sizeOf(T) * 8 == info.bits,
697
+
698
+ .pointer => |info| return info.size != .slice,
699
+
700
+ .array => |info| return comptime has_unique_representation(info.child),
701
+
702
+ .@"struct" => |info| {
703
+ // Only consider packed structs unique if they are byte aligned.
704
+ if (info.backing_integer) |backing_integer| {
705
+ return @sizeOf(T) * 8 == @bitSizeOf(backing_integer);
706
+ }
707
+
708
+ var sum_size = @as(usize, 0);
709
+
710
+ inline for (info.fields) |field| {
711
+ const FieldType = field.type;
712
+ if (comptime !has_unique_representation(FieldType)) return false;
713
+ sum_size += @sizeOf(FieldType);
714
+ }
715
+
716
+ return @sizeOf(T) == sum_size;
717
+ },
718
+
719
+ .vector => |info| return comptime has_unique_representation(info.child) and
720
+ @sizeOf(T) == @sizeOf(info.child) * info.len,
721
+ }
722
+ }
723
+
724
+ // Test vectors mostly from upstream, with some added to test the packed struct case.
725
+ test "has_unique_representation" {
726
+ const TestStruct1 = struct {
727
+ a: u32,
728
+ b: u32,
729
+ };
730
+
731
+ try std.testing.expect(has_unique_representation(TestStruct1));
732
+
733
+ const TestStruct2 = struct {
734
+ a: u32,
735
+ b: u16,
736
+ };
737
+
738
+ try std.testing.expect(!has_unique_representation(TestStruct2));
739
+
740
+ const TestStruct3 = struct {
741
+ a: u32,
742
+ b: u32,
743
+ };
744
+
745
+ try std.testing.expect(has_unique_representation(TestStruct3));
746
+
747
+ const TestStruct4 = struct { a: []const u8 };
748
+
749
+ try std.testing.expect(!has_unique_representation(TestStruct4));
750
+
751
+ const TestStruct5 = struct { a: TestStruct4 };
752
+
753
+ try std.testing.expect(!has_unique_representation(TestStruct5));
754
+
755
+ const TestStruct6 = packed struct {
756
+ a: u32,
757
+ b: u31,
758
+ };
759
+
760
+ try std.testing.expect(!has_unique_representation(TestStruct6));
761
+
762
+ const TestStruct7 = struct {
763
+ a: u64,
764
+ b: TestStruct6,
765
+ };
766
+
767
+ try std.testing.expect(!has_unique_representation(TestStruct7));
768
+
769
+ const TestStruct8 = packed struct {
770
+ a: u32,
771
+ b: u32,
772
+ };
773
+
774
+ try std.testing.expect(has_unique_representation(TestStruct8));
775
+
776
+ const TestStruct9 = struct {
777
+ a: u64,
778
+ b: TestStruct8,
779
+ };
780
+
781
+ try std.testing.expect(has_unique_representation(TestStruct9));
782
+
783
+ const TestStruct10 = packed struct {
784
+ a: TestStruct8,
785
+ b: TestStruct8,
786
+ };
787
+
788
+ try std.testing.expect(has_unique_representation(TestStruct10));
789
+
790
+ const TestUnion1 = packed union {
791
+ a: u32,
792
+ b: u16,
793
+ };
794
+
795
+ try std.testing.expect(!has_unique_representation(TestUnion1));
796
+
797
+ const TestUnion2 = extern union {
798
+ a: u32,
799
+ b: u16,
800
+ };
801
+
802
+ try std.testing.expect(!has_unique_representation(TestUnion2));
803
+
804
+ const TestUnion3 = union {
805
+ a: u32,
806
+ b: u16,
807
+ };
808
+
809
+ try std.testing.expect(!has_unique_representation(TestUnion3));
810
+
811
+ const TestUnion4 = union(enum) {
812
+ a: u32,
813
+ b: u16,
814
+ };
815
+
816
+ try std.testing.expect(!has_unique_representation(TestUnion4));
817
+
818
+ inline for ([_]type{ i0, u8, i16, u32, i64 }) |T| {
819
+ try std.testing.expect(has_unique_representation(T));
820
+ }
821
+ inline for ([_]type{ i1, u9, i17, u33, i24 }) |T| {
822
+ try std.testing.expect(!has_unique_representation(T));
823
+ }
824
+
825
+ try std.testing.expect(!has_unique_representation([]u8));
826
+ try std.testing.expect(!has_unique_representation([]const u8));
827
+
828
+ try std.testing.expect(has_unique_representation(@Vector(4, u16)));
829
+ }
830
+
831
+ /// Construct a `union(Enum)` type, where each union "value" type is defined in terms of the
832
+ /// variant.
833
+ ///
834
+ /// That is, `EnumUnionType(Enum, TypeForVariant)` is equivalent to:
835
+ ///
836
+ /// union(Enum) {
837
+ /// // For every `e` in `Enum`:
838
+ /// e: TypeForVariant(e),
839
+ /// }
840
+ ///
841
+ pub fn EnumUnionType(
842
+ comptime Enum: type,
843
+ comptime TypeForVariant: fn (comptime variant: Enum) type,
844
+ ) type {
845
+ const UnionField = std.builtin.Type.UnionField;
846
+
847
+ var fields: [std.enums.values(Enum).len]UnionField = undefined;
848
+ for (std.enums.values(Enum), 0..) |enum_variant, i| {
849
+ fields[i] = .{
850
+ .name = @tagName(enum_variant),
851
+ .type = TypeForVariant(enum_variant),
852
+ .alignment = @alignOf(TypeForVariant(enum_variant)),
853
+ };
854
+ }
855
+
856
+ return @Type(.{ .@"union" = .{
857
+ .layout = .auto,
858
+ .fields = &fields,
859
+ .decls = &.{},
860
+ .tag_type = Enum,
861
+ } });
862
+ }
863
+
864
+ /// Creates a slice to a comptime slice without triggering
865
+ /// `error: runtime value contains reference to comptime var`
866
+ pub fn comptime_slice(comptime slice: anytype, comptime len: usize) []const @TypeOf(slice[0]) {
867
+ return &@as([len]@TypeOf(slice[0]), slice[0..len].*);
868
+ }
869
+
870
+ /// Return a Formatter for a u64 value representing a file size.
871
+ /// This formatter statically checks that the number is a multiple of 1024,
872
+ /// and represents it using the IEC measurement units (KiB, MiB, GiB, ...).
873
+ pub fn fmt_int_size_bin_exact(comptime value: u64) std.fmt.Formatter(format_int_size_bin_exact) {
874
+ comptime assert(value < 1024 or value % 1024 == 0);
875
+ return .{ .data = value };
876
+ }
877
+
878
+ fn format_int_size_bin_exact(
879
+ value: u64,
880
+ comptime fmt: []const u8,
881
+ options: std.fmt.FormatOptions,
882
+ writer: anytype,
883
+ ) !void {
884
+ _ = fmt;
885
+ if (value == 0) {
886
+ return std.fmt.formatBuf("0B", options, writer);
887
+ }
888
+
889
+ // The worst case in terms of space needed is 20 bytes,
890
+ // since `maxInt(u64)` is the highest number,
891
+ // + 3 bytes for the measurement units suffix.
892
+ comptime assert(std.fmt.comptimePrint("{}GiB", .{std.math.maxInt(u64)}).len == 23);
893
+ var buf: [23]u8 = undefined;
894
+
895
+ var magnitude: u8 = 0;
896
+ var value_unit = value;
897
+ while (value_unit % 1024 == 0) : (magnitude += 1) {
898
+ value_unit = @divExact(value_unit, 1024);
899
+ }
900
+
901
+ const magnitudes_iec = "BKMGTPEZY";
902
+ const suffix = magnitudes_iec[magnitude];
903
+
904
+ const length: usize = length: {
905
+ const i = std.fmt.formatIntBuf(&buf, value_unit, 10, .lower, .{});
906
+ if (magnitude == 0) {
907
+ buf[i] = suffix;
908
+ break :length i + 1;
909
+ } else {
910
+ buf[i..][0..3].* = [_]u8{ suffix, 'i', 'B' };
911
+ break :length i + 3;
912
+ }
913
+ };
914
+
915
+ return std.fmt.formatBuf(buf[0..length], options, writer);
916
+ }
917
+
918
+ test fmt_int_size_bin_exact {
919
+ try std.testing.expectFmt("0B", "{}", .{fmt_int_size_bin_exact(0)});
920
+ try std.testing.expectFmt("128B", "{}", .{fmt_int_size_bin_exact(128)});
921
+ try std.testing.expectFmt("8KiB", "{}", .{fmt_int_size_bin_exact(8 * 1024)});
922
+ try std.testing.expectFmt("1025KiB", "{}", .{fmt_int_size_bin_exact(1025 * 1024)});
923
+ try std.testing.expectFmt("12345KiB", "{}", .{fmt_int_size_bin_exact(12345 * 1024)});
924
+ try std.testing.expectFmt("42MiB", "{}", .{fmt_int_size_bin_exact(42 * 1024 * 1024)});
925
+ try std.testing.expectFmt("18014398509481983KiB", "{}", .{
926
+ fmt_int_size_bin_exact(std.math.maxInt(u64) - 1023),
927
+ });
928
+ }
929
+
930
+ /// Like std.fmt.bufPrint, but checks, at compile time, that the buffer is sufficiently large.
931
+ pub fn array_print(
932
+ comptime n: usize,
933
+ buffer: *[n]u8,
934
+ comptime fmt: []const u8,
935
+ args: anytype,
936
+ ) []const u8 {
937
+ const Args = @TypeOf(args);
938
+ const ArgsStruct = @typeInfo(Args).@"struct";
939
+ comptime assert(ArgsStruct.is_tuple);
940
+
941
+ comptime {
942
+ var args_worst_case: Args = undefined;
943
+ for (ArgsStruct.fields, 0..) |field, index| {
944
+ const arg_worst_case = switch (field.type) {
945
+ u8, u16, u32, u64, u128 => std.math.maxInt(field.type),
946
+ else => @compileError("array_print: unsupported type: " ++ @typeName(field.type)),
947
+ };
948
+ args_worst_case[index] = arg_worst_case;
949
+ }
950
+ const buffer_size = std.fmt.count(fmt, args_worst_case);
951
+ assert(n >= buffer_size); // array_print buffer too small
952
+ }
953
+
954
+ return std.fmt.bufPrint(buffer, fmt, args) catch |err| switch (err) {
955
+ error.NoSpaceLeft => unreachable,
956
+ };
957
+ }
958
+
959
+ /// Like std.posix version, but log unconditionally, not just when mode=Debug.
960
+ /// The added `label` argument works around the absence of stack traces in ReleaseSafe builds.
961
+ pub fn unexpected_errno(label: []const u8, err: std.posix.system.E) std.posix.UnexpectedError {
962
+ log.scoped(.stdx).err("unexpected errno: {s}: code={d} name={?s}", .{
963
+ label,
964
+ @intFromEnum(err),
965
+ std.enums.tagName(std.posix.system.E, err),
966
+ });
967
+
968
+ if (builtin.mode == .Debug) {
969
+ std.debug.dumpCurrentStackTrace(null);
970
+ }
971
+ return error.Unexpected;
972
+ }
973
+
974
+ pub fn unique_u128() u128 {
975
+ const value = std.crypto.random.int(u128);
976
+
977
+ // Broken CSPRNG is the likeliest explanation for zero or all ones.
978
+ assert(value != 0);
979
+ assert(value != std.math.maxInt(u128));
980
+
981
+ return value;
982
+ }
983
+
984
+ /// NB: intended for parsing CLI arguments where we care to preserve the user-specified unit.
985
+ /// Use `size: u64` for all other use-cases.
986
+ pub const ByteSize = struct {
987
+ value: u64,
988
+ unit: Unit = .bytes,
989
+
990
+ const Unit = enum(u64) {
991
+ bytes = 1,
992
+ kib = KiB,
993
+ mib = MiB,
994
+ gib = GiB,
995
+ tib = TiB,
996
+ };
997
+
998
+ pub fn parse_flag_value(
999
+ string: []const u8,
1000
+ static_diagnostic: *?[]const u8,
1001
+ ) error{InvalidFlagValue}!ByteSize {
1002
+ assert(string.len != 0);
1003
+
1004
+ const split_index = for (string, 0..) |c, index| {
1005
+ if (std.ascii.isDigit(c) or c == '_') {
1006
+ // Numeric part continues.
1007
+ } else break index;
1008
+ } else string.len;
1009
+
1010
+ const string_amount = string[0..split_index];
1011
+ const string_unit = string[split_index..];
1012
+ maybe(string_amount.len == 0);
1013
+ maybe(string_unit.len == 0);
1014
+
1015
+ const amount = std.fmt.parseUnsigned(u64, string_amount, 10) catch |err| switch (err) {
1016
+ error.Overflow => {
1017
+ static_diagnostic.* = "value exceeds 64-bit unsigned integer:";
1018
+ return error.InvalidFlagValue;
1019
+ },
1020
+ error.InvalidCharacter => {
1021
+ static_diagnostic.* = "expected a size, but found:";
1022
+ return error.InvalidFlagValue;
1023
+ },
1024
+ };
1025
+
1026
+ const unit = if (string_unit.len == 0)
1027
+ .bytes
1028
+ else inline for (comptime std.enums.values(Unit)) |tag| {
1029
+ if (std.ascii.eqlIgnoreCase(string_unit, @tagName(tag))) break tag;
1030
+ } else {
1031
+ static_diagnostic.* = "invalid unit in size, needed KiB, MiB, GiB or TiB:";
1032
+ return error.InvalidFlagValue;
1033
+ };
1034
+
1035
+ _ = std.math.mul(u64, amount, @intFromEnum(unit)) catch {
1036
+ static_diagnostic.* = "size in bytes exceeds 64-bit unsigned integer:";
1037
+ return error.InvalidFlagValue;
1038
+ };
1039
+
1040
+ return .{ .value = amount, .unit = unit };
1041
+ }
1042
+
1043
+ pub fn bytes(size: *const ByteSize) u64 {
1044
+ return std.math.mul(
1045
+ u64,
1046
+ size.value,
1047
+ @intFromEnum(size.unit),
1048
+ ) catch unreachable;
1049
+ }
1050
+
1051
+ pub fn suffix(size: *const ByteSize) []const u8 {
1052
+ return switch (size.unit) {
1053
+ .bytes => "",
1054
+ .kib => "KiB",
1055
+ .mib => "MiB",
1056
+ .gib => "GiB",
1057
+ .tib => "TiB",
1058
+ };
1059
+ }
1060
+ };
1061
+
1062
+ test "ByteSize.parse_flag_value" {
1063
+ try parse_flag_value_fuzz(ByteSize, ByteSize.parse_flag_value, .{
1064
+ .ok = &.{
1065
+ .{ "0", .{ .value = 0, .unit = .bytes } },
1066
+ .{ "1", .{ .value = 1, .unit = .bytes } },
1067
+
1068
+ .{ "140737488355328", .{ .value = 140737488355328, .unit = .bytes } },
1069
+ .{ "128TiB", .{ .value = 128, .unit = .tib } },
1070
+ .{ "1TiB", .{ .value = 1, .unit = .tib } },
1071
+ .{ "10tib", .{ .value = 10, .unit = .tib } },
1072
+ .{ "1GiB", .{ .value = 1, .unit = .gib } },
1073
+ .{ "10gib", .{ .value = 10, .unit = .gib } },
1074
+ .{ "1MiB", .{ .value = 1, .unit = .mib } },
1075
+ .{ "10mib", .{ .value = 10, .unit = .mib } },
1076
+ .{ "1KiB", .{ .value = 1, .unit = .kib } },
1077
+ .{ "10kib", .{ .value = 10, .unit = .kib } },
1078
+ .{ "1_0kib", .{ .value = 10, .unit = .kib } },
1079
+ },
1080
+ .err = &.{
1081
+ .{ "18446744073709551616", "value exceeds 64-bit unsigned integer" },
1082
+ .{ "MiB", "expected a size, but found" },
1083
+ .{ "_MiB", "expected a size" },
1084
+ .{ "10bananas", "invalid unit in size, needed KiB, MiB, GiB or TiB" },
1085
+ .{ "10GB", "invalid unit in size" },
1086
+ .{ "18446744073709551GiB", "size in bytes exceeds 64-bit unsigned integer" },
1087
+ },
1088
+ });
1089
+ }
1090
+
1091
+ // Fast alternative to modulo reduction (Note, it is not the same as modulo).
1092
+ // See https://github.com/lemire/fastrange/ and
1093
+ // https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
1094
+ pub inline fn fastrange(word: u64, p: u64) u64 {
1095
+ const lword: u128 = @intCast(word);
1096
+ const lp: u128 = @intCast(p);
1097
+ const ln: u128 = lword *% lp;
1098
+ return @truncate(ln >> 64);
1099
+ }
1100
+
1101
+ // For Zig 14.1 the compiler generates branchless code as is: https://godbolt.org/z/hc563WPKP
1102
+ pub inline fn branchless_select(comptime T: type, flag: bool, a: T, b: T) T {
1103
+ @branchHint(.unpredictable);
1104
+ return if (flag) a else b;
1105
+ }
1106
+
1107
+ const snap = Snap.snap_fn("src/stdx");
1108
+
1109
+ test fastrange {
1110
+ var prng = PRNG.from_seed(42);
1111
+ var distribution: [8]u32 = @splat(0);
1112
+ for (0..10_000) |_| {
1113
+ const key = prng.int(u64);
1114
+ distribution[fastrange(key, 8)] += 1;
1115
+ }
1116
+ try snap(@src(),
1117
+ \\{ 1263, 1273, 1244, 1226, 1228, 1276, 1169, 1321 }
1118
+ ).diff_fmt("{d}", .{distribution});
1119
+ }
1120
+
1121
+ // This test shows that fastrange is not equivalent to modulo, but rather an alternative method.
1122
+ // It is best used uniformly distributed hashes or random numbers across the full range.
1123
+ test "fastrange not modulo" {
1124
+ var distribution: [8]u32 = @splat(0);
1125
+ for (0..10_000) |key| {
1126
+ distribution[fastrange(key, 8)] += 1;
1127
+ }
1128
+ try snap(@src(),
1129
+ \\{ 10000, 0, 0, 0, 0, 0, 0, 0 }
1130
+ ).diff_fmt("{d}", .{distribution});
1131
+ }
1132
+
1133
+ /// `status` is a waitpid() status result.
1134
+ pub fn term_from_status(status: u32) std.process.Child.Term {
1135
+ const Term = std.process.Child.Term;
1136
+ return if (std.posix.W.IFEXITED(status))
1137
+ Term{ .Exited = std.posix.W.EXITSTATUS(status) }
1138
+ else if (std.posix.W.IFSIGNALED(status))
1139
+ Term{ .Signal = std.posix.W.TERMSIG(status) }
1140
+ else if (std.posix.W.IFSTOPPED(status))
1141
+ Term{ .Stopped = std.posix.W.STOPSIG(status) }
1142
+ else
1143
+ Term{ .Unknown = status };
1144
+ }
1145
+
1146
+ comptime {
1147
+ _ = @import("vendored/aegis.zig");
1148
+ _ = @import("bit_set.zig");
1149
+ _ = @import("bounded_array.zig");
1150
+ _ = @import("flags.zig");
1151
+ _ = @import("prng.zig");
1152
+ _ = @import("radix.zig");
1153
+ _ = @import("ring_buffer.zig");
1154
+ _ = @import("sort_test.zig");
1155
+ _ = @import("stdx.zig");
1156
+ _ = @import("testing/snaptest.zig");
1157
+ _ = @import("time_units.zig");
1158
+ _ = @import("unshare.zig");
1159
+ _ = @import("zipfian.zig");
1160
+ }