tigerbeetle 0.0.34 → 0.0.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/ext/tb_client/extconf.rb +13 -13
  4. data/ext/tb_client/tigerbeetle/LICENSE +177 -0
  5. data/ext/tb_client/tigerbeetle/build.zig +2327 -0
  6. data/ext/tb_client/tigerbeetle/src/aof.zig +1000 -0
  7. data/ext/tb_client/tigerbeetle/src/build_multiversion.zig +808 -0
  8. data/ext/tb_client/tigerbeetle/src/cdc/amqp/protocol.zig +1283 -0
  9. data/ext/tb_client/tigerbeetle/src/cdc/amqp/spec.zig +1704 -0
  10. data/ext/tb_client/tigerbeetle/src/cdc/amqp/types.zig +341 -0
  11. data/ext/tb_client/tigerbeetle/src/cdc/amqp.zig +1450 -0
  12. data/ext/tb_client/tigerbeetle/src/cdc/runner.zig +1659 -0
  13. data/ext/tb_client/tigerbeetle/src/clients/c/samples/main.c +406 -0
  14. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/context.zig +1084 -0
  15. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/echo_client.zig +286 -0
  16. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/packet.zig +158 -0
  17. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal.zig +229 -0
  18. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client/signal_fuzz.zig +110 -0
  19. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.h +386 -0
  20. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client.zig +34 -0
  21. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_exports.zig +281 -0
  22. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header.zig +312 -0
  23. data/ext/tb_client/tigerbeetle/src/clients/c/tb_client_header_test.zig +138 -0
  24. data/ext/tb_client/tigerbeetle/src/clients/c/test.zig +466 -0
  25. data/ext/tb_client/tigerbeetle/src/clients/docs_samples.zig +157 -0
  26. data/ext/tb_client/tigerbeetle/src/clients/docs_types.zig +90 -0
  27. data/ext/tb_client/tigerbeetle/src/clients/dotnet/ci.zig +203 -0
  28. data/ext/tb_client/tigerbeetle/src/clients/dotnet/docs.zig +79 -0
  29. data/ext/tb_client/tigerbeetle/src/clients/dotnet/dotnet_bindings.zig +542 -0
  30. data/ext/tb_client/tigerbeetle/src/clients/go/ci.zig +109 -0
  31. data/ext/tb_client/tigerbeetle/src/clients/go/docs.zig +86 -0
  32. data/ext/tb_client/tigerbeetle/src/clients/go/go_bindings.zig +370 -0
  33. data/ext/tb_client/tigerbeetle/src/clients/go/pkg/native/tb_client.h +386 -0
  34. data/ext/tb_client/tigerbeetle/src/clients/java/ci.zig +167 -0
  35. data/ext/tb_client/tigerbeetle/src/clients/java/docs.zig +126 -0
  36. data/ext/tb_client/tigerbeetle/src/clients/java/java_bindings.zig +996 -0
  37. data/ext/tb_client/tigerbeetle/src/clients/java/src/client.zig +748 -0
  38. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni.zig +3238 -0
  39. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_tests.zig +1718 -0
  40. data/ext/tb_client/tigerbeetle/src/clients/java/src/jni_thread_cleaner.zig +190 -0
  41. data/ext/tb_client/tigerbeetle/src/clients/node/ci.zig +104 -0
  42. data/ext/tb_client/tigerbeetle/src/clients/node/docs.zig +75 -0
  43. data/ext/tb_client/tigerbeetle/src/clients/node/node.zig +522 -0
  44. data/ext/tb_client/tigerbeetle/src/clients/node/node_bindings.zig +267 -0
  45. data/ext/tb_client/tigerbeetle/src/clients/node/src/c.zig +3 -0
  46. data/ext/tb_client/tigerbeetle/src/clients/node/src/translate.zig +379 -0
  47. data/ext/tb_client/tigerbeetle/src/clients/python/ci.zig +131 -0
  48. data/ext/tb_client/tigerbeetle/src/clients/python/docs.zig +63 -0
  49. data/ext/tb_client/tigerbeetle/src/clients/python/python_bindings.zig +588 -0
  50. data/ext/tb_client/tigerbeetle/src/clients/rust/assets/tb_client.h +386 -0
  51. data/ext/tb_client/tigerbeetle/src/clients/rust/ci.zig +73 -0
  52. data/ext/tb_client/tigerbeetle/src/clients/rust/docs.zig +106 -0
  53. data/ext/tb_client/tigerbeetle/src/clients/rust/rust_bindings.zig +305 -0
  54. data/ext/tb_client/tigerbeetle/src/config.zig +296 -0
  55. data/ext/tb_client/tigerbeetle/src/constants.zig +790 -0
  56. data/ext/tb_client/tigerbeetle/src/copyhound.zig +202 -0
  57. data/ext/tb_client/tigerbeetle/src/counting_allocator.zig +72 -0
  58. data/ext/tb_client/tigerbeetle/src/direction.zig +11 -0
  59. data/ext/tb_client/tigerbeetle/src/docs_website/build.zig +158 -0
  60. data/ext/tb_client/tigerbeetle/src/docs_website/src/content.zig +156 -0
  61. data/ext/tb_client/tigerbeetle/src/docs_website/src/docs.zig +252 -0
  62. data/ext/tb_client/tigerbeetle/src/docs_website/src/file_checker.zig +313 -0
  63. data/ext/tb_client/tigerbeetle/src/docs_website/src/html.zig +87 -0
  64. data/ext/tb_client/tigerbeetle/src/docs_website/src/page_writer.zig +63 -0
  65. data/ext/tb_client/tigerbeetle/src/docs_website/src/redirects.zig +47 -0
  66. data/ext/tb_client/tigerbeetle/src/docs_website/src/search_index_writer.zig +28 -0
  67. data/ext/tb_client/tigerbeetle/src/docs_website/src/service_worker_writer.zig +61 -0
  68. data/ext/tb_client/tigerbeetle/src/docs_website/src/single_page_writer.zig +169 -0
  69. data/ext/tb_client/tigerbeetle/src/docs_website/src/website.zig +46 -0
  70. data/ext/tb_client/tigerbeetle/src/ewah.zig +445 -0
  71. data/ext/tb_client/tigerbeetle/src/ewah_benchmark.zig +128 -0
  72. data/ext/tb_client/tigerbeetle/src/ewah_fuzz.zig +171 -0
  73. data/ext/tb_client/tigerbeetle/src/fuzz_tests.zig +179 -0
  74. data/ext/tb_client/tigerbeetle/src/integration_tests.zig +662 -0
  75. data/ext/tb_client/tigerbeetle/src/io/common.zig +155 -0
  76. data/ext/tb_client/tigerbeetle/src/io/darwin.zig +1093 -0
  77. data/ext/tb_client/tigerbeetle/src/io/linux.zig +1880 -0
  78. data/ext/tb_client/tigerbeetle/src/io/test.zig +1005 -0
  79. data/ext/tb_client/tigerbeetle/src/io/windows.zig +1598 -0
  80. data/ext/tb_client/tigerbeetle/src/io.zig +34 -0
  81. data/ext/tb_client/tigerbeetle/src/iops.zig +134 -0
  82. data/ext/tb_client/tigerbeetle/src/list.zig +236 -0
  83. data/ext/tb_client/tigerbeetle/src/lsm/binary_search.zig +848 -0
  84. data/ext/tb_client/tigerbeetle/src/lsm/binary_search_benchmark.zig +179 -0
  85. data/ext/tb_client/tigerbeetle/src/lsm/cache_map.zig +424 -0
  86. data/ext/tb_client/tigerbeetle/src/lsm/cache_map_fuzz.zig +420 -0
  87. data/ext/tb_client/tigerbeetle/src/lsm/compaction.zig +2117 -0
  88. data/ext/tb_client/tigerbeetle/src/lsm/composite_key.zig +182 -0
  89. data/ext/tb_client/tigerbeetle/src/lsm/forest.zig +1119 -0
  90. data/ext/tb_client/tigerbeetle/src/lsm/forest_fuzz.zig +1102 -0
  91. data/ext/tb_client/tigerbeetle/src/lsm/forest_table_iterator.zig +200 -0
  92. data/ext/tb_client/tigerbeetle/src/lsm/groove.zig +1495 -0
  93. data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge.zig +739 -0
  94. data/ext/tb_client/tigerbeetle/src/lsm/k_way_merge_benchmark.zig +166 -0
  95. data/ext/tb_client/tigerbeetle/src/lsm/manifest.zig +754 -0
  96. data/ext/tb_client/tigerbeetle/src/lsm/manifest_level.zig +1294 -0
  97. data/ext/tb_client/tigerbeetle/src/lsm/manifest_level_fuzz.zig +510 -0
  98. data/ext/tb_client/tigerbeetle/src/lsm/manifest_log.zig +1263 -0
  99. data/ext/tb_client/tigerbeetle/src/lsm/manifest_log_fuzz.zig +628 -0
  100. data/ext/tb_client/tigerbeetle/src/lsm/node_pool.zig +247 -0
  101. data/ext/tb_client/tigerbeetle/src/lsm/scan_buffer.zig +116 -0
  102. data/ext/tb_client/tigerbeetle/src/lsm/scan_builder.zig +543 -0
  103. data/ext/tb_client/tigerbeetle/src/lsm/scan_fuzz.zig +938 -0
  104. data/ext/tb_client/tigerbeetle/src/lsm/scan_lookup.zig +293 -0
  105. data/ext/tb_client/tigerbeetle/src/lsm/scan_merge.zig +362 -0
  106. data/ext/tb_client/tigerbeetle/src/lsm/scan_range.zig +99 -0
  107. data/ext/tb_client/tigerbeetle/src/lsm/scan_state.zig +17 -0
  108. data/ext/tb_client/tigerbeetle/src/lsm/scan_tree.zig +1036 -0
  109. data/ext/tb_client/tigerbeetle/src/lsm/schema.zig +617 -0
  110. data/ext/tb_client/tigerbeetle/src/lsm/scratch_memory.zig +84 -0
  111. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array.zig +1500 -0
  112. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_benchmark.zig +149 -0
  113. data/ext/tb_client/tigerbeetle/src/lsm/segmented_array_fuzz.zig +7 -0
  114. data/ext/tb_client/tigerbeetle/src/lsm/set_associative_cache.zig +865 -0
  115. data/ext/tb_client/tigerbeetle/src/lsm/table.zig +607 -0
  116. data/ext/tb_client/tigerbeetle/src/lsm/table_memory.zig +843 -0
  117. data/ext/tb_client/tigerbeetle/src/lsm/table_value_iterator.zig +105 -0
  118. data/ext/tb_client/tigerbeetle/src/lsm/timestamp_range.zig +40 -0
  119. data/ext/tb_client/tigerbeetle/src/lsm/tree.zig +630 -0
  120. data/ext/tb_client/tigerbeetle/src/lsm/tree_fuzz.zig +933 -0
  121. data/ext/tb_client/tigerbeetle/src/lsm/zig_zag_merge.zig +557 -0
  122. data/ext/tb_client/tigerbeetle/src/message_buffer.zig +469 -0
  123. data/ext/tb_client/tigerbeetle/src/message_bus.zig +1214 -0
  124. data/ext/tb_client/tigerbeetle/src/message_bus_fuzz.zig +936 -0
  125. data/ext/tb_client/tigerbeetle/src/message_pool.zig +343 -0
  126. data/ext/tb_client/tigerbeetle/src/multiversion.zig +2195 -0
  127. data/ext/tb_client/tigerbeetle/src/queue.zig +390 -0
  128. data/ext/tb_client/tigerbeetle/src/repl/completion.zig +201 -0
  129. data/ext/tb_client/tigerbeetle/src/repl/parser.zig +1356 -0
  130. data/ext/tb_client/tigerbeetle/src/repl/terminal.zig +496 -0
  131. data/ext/tb_client/tigerbeetle/src/repl.zig +1034 -0
  132. data/ext/tb_client/tigerbeetle/src/scripts/amqp.zig +973 -0
  133. data/ext/tb_client/tigerbeetle/src/scripts/cfo.zig +1866 -0
  134. data/ext/tb_client/tigerbeetle/src/scripts/changelog.zig +304 -0
  135. data/ext/tb_client/tigerbeetle/src/scripts/ci.zig +227 -0
  136. data/ext/tb_client/tigerbeetle/src/scripts/client_readmes.zig +658 -0
  137. data/ext/tb_client/tigerbeetle/src/scripts/devhub.zig +466 -0
  138. data/ext/tb_client/tigerbeetle/src/scripts/release.zig +1058 -0
  139. data/ext/tb_client/tigerbeetle/src/scripts.zig +105 -0
  140. data/ext/tb_client/tigerbeetle/src/shell.zig +1195 -0
  141. data/ext/tb_client/tigerbeetle/src/stack.zig +260 -0
  142. data/ext/tb_client/tigerbeetle/src/state_machine/auditor.zig +911 -0
  143. data/ext/tb_client/tigerbeetle/src/state_machine/workload.zig +2079 -0
  144. data/ext/tb_client/tigerbeetle/src/state_machine.zig +4872 -0
  145. data/ext/tb_client/tigerbeetle/src/state_machine_fuzz.zig +288 -0
  146. data/ext/tb_client/tigerbeetle/src/state_machine_tests.zig +3128 -0
  147. data/ext/tb_client/tigerbeetle/src/static_allocator.zig +82 -0
  148. data/ext/tb_client/tigerbeetle/src/stdx/bit_set.zig +157 -0
  149. data/ext/tb_client/tigerbeetle/src/stdx/bounded_array.zig +292 -0
  150. data/ext/tb_client/tigerbeetle/src/stdx/debug.zig +65 -0
  151. data/ext/tb_client/tigerbeetle/src/stdx/flags.zig +1414 -0
  152. data/ext/tb_client/tigerbeetle/src/stdx/mlock.zig +92 -0
  153. data/ext/tb_client/tigerbeetle/src/stdx/prng.zig +677 -0
  154. data/ext/tb_client/tigerbeetle/src/stdx/radix.zig +336 -0
  155. data/ext/tb_client/tigerbeetle/src/stdx/ring_buffer.zig +511 -0
  156. data/ext/tb_client/tigerbeetle/src/stdx/sort_test.zig +112 -0
  157. data/ext/tb_client/tigerbeetle/src/stdx/stdx.zig +1160 -0
  158. data/ext/tb_client/tigerbeetle/src/stdx/testing/low_level_hash_vectors.zig +142 -0
  159. data/ext/tb_client/tigerbeetle/src/stdx/testing/snaptest.zig +361 -0
  160. data/ext/tb_client/tigerbeetle/src/stdx/time_units.zig +275 -0
  161. data/ext/tb_client/tigerbeetle/src/stdx/unshare.zig +295 -0
  162. data/ext/tb_client/tigerbeetle/src/stdx/vendored/aegis.zig +436 -0
  163. data/ext/tb_client/tigerbeetle/src/stdx/windows.zig +48 -0
  164. data/ext/tb_client/tigerbeetle/src/stdx/zipfian.zig +402 -0
  165. data/ext/tb_client/tigerbeetle/src/storage.zig +489 -0
  166. data/ext/tb_client/tigerbeetle/src/storage_fuzz.zig +180 -0
  167. data/ext/tb_client/tigerbeetle/src/testing/bench.zig +146 -0
  168. data/ext/tb_client/tigerbeetle/src/testing/cluster/grid_checker.zig +53 -0
  169. data/ext/tb_client/tigerbeetle/src/testing/cluster/journal_checker.zig +61 -0
  170. data/ext/tb_client/tigerbeetle/src/testing/cluster/manifest_checker.zig +76 -0
  171. data/ext/tb_client/tigerbeetle/src/testing/cluster/message_bus.zig +110 -0
  172. data/ext/tb_client/tigerbeetle/src/testing/cluster/network.zig +412 -0
  173. data/ext/tb_client/tigerbeetle/src/testing/cluster/state_checker.zig +331 -0
  174. data/ext/tb_client/tigerbeetle/src/testing/cluster/storage_checker.zig +458 -0
  175. data/ext/tb_client/tigerbeetle/src/testing/cluster.zig +1198 -0
  176. data/ext/tb_client/tigerbeetle/src/testing/exhaustigen.zig +128 -0
  177. data/ext/tb_client/tigerbeetle/src/testing/fixtures.zig +181 -0
  178. data/ext/tb_client/tigerbeetle/src/testing/fuzz.zig +144 -0
  179. data/ext/tb_client/tigerbeetle/src/testing/id.zig +97 -0
  180. data/ext/tb_client/tigerbeetle/src/testing/io.zig +317 -0
  181. data/ext/tb_client/tigerbeetle/src/testing/marks.zig +126 -0
  182. data/ext/tb_client/tigerbeetle/src/testing/packet_simulator.zig +533 -0
  183. data/ext/tb_client/tigerbeetle/src/testing/reply_sequence.zig +154 -0
  184. data/ext/tb_client/tigerbeetle/src/testing/state_machine.zig +389 -0
  185. data/ext/tb_client/tigerbeetle/src/testing/storage.zig +1247 -0
  186. data/ext/tb_client/tigerbeetle/src/testing/table.zig +249 -0
  187. data/ext/tb_client/tigerbeetle/src/testing/time.zig +98 -0
  188. data/ext/tb_client/tigerbeetle/src/testing/tmp_tigerbeetle.zig +212 -0
  189. data/ext/tb_client/tigerbeetle/src/testing/vortex/constants.zig +26 -0
  190. data/ext/tb_client/tigerbeetle/src/testing/vortex/faulty_network.zig +580 -0
  191. data/ext/tb_client/tigerbeetle/src/testing/vortex/java_driver/ci.zig +39 -0
  192. data/ext/tb_client/tigerbeetle/src/testing/vortex/logged_process.zig +214 -0
  193. data/ext/tb_client/tigerbeetle/src/testing/vortex/rust_driver/ci.zig +34 -0
  194. data/ext/tb_client/tigerbeetle/src/testing/vortex/supervisor.zig +766 -0
  195. data/ext/tb_client/tigerbeetle/src/testing/vortex/workload.zig +543 -0
  196. data/ext/tb_client/tigerbeetle/src/testing/vortex/zig_driver.zig +181 -0
  197. data/ext/tb_client/tigerbeetle/src/tidy.zig +1448 -0
  198. data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_driver.zig +227 -0
  199. data/ext/tb_client/tigerbeetle/src/tigerbeetle/benchmark_load.zig +1069 -0
  200. data/ext/tb_client/tigerbeetle/src/tigerbeetle/cli.zig +1422 -0
  201. data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect.zig +1658 -0
  202. data/ext/tb_client/tigerbeetle/src/tigerbeetle/inspect_integrity.zig +518 -0
  203. data/ext/tb_client/tigerbeetle/src/tigerbeetle/libtb_client.zig +36 -0
  204. data/ext/tb_client/tigerbeetle/src/tigerbeetle/main.zig +646 -0
  205. data/ext/tb_client/tigerbeetle/src/tigerbeetle.zig +958 -0
  206. data/ext/tb_client/tigerbeetle/src/time.zig +236 -0
  207. data/ext/tb_client/tigerbeetle/src/trace/event.zig +745 -0
  208. data/ext/tb_client/tigerbeetle/src/trace/statsd.zig +462 -0
  209. data/ext/tb_client/tigerbeetle/src/trace.zig +556 -0
  210. data/ext/tb_client/tigerbeetle/src/unit_tests.zig +321 -0
  211. data/ext/tb_client/tigerbeetle/src/vopr.zig +1785 -0
  212. data/ext/tb_client/tigerbeetle/src/vortex.zig +101 -0
  213. data/ext/tb_client/tigerbeetle/src/vsr/checkpoint_trailer.zig +473 -0
  214. data/ext/tb_client/tigerbeetle/src/vsr/checksum.zig +208 -0
  215. data/ext/tb_client/tigerbeetle/src/vsr/checksum_benchmark.zig +43 -0
  216. data/ext/tb_client/tigerbeetle/src/vsr/client.zig +768 -0
  217. data/ext/tb_client/tigerbeetle/src/vsr/client_replies.zig +532 -0
  218. data/ext/tb_client/tigerbeetle/src/vsr/client_sessions.zig +338 -0
  219. data/ext/tb_client/tigerbeetle/src/vsr/clock.zig +1019 -0
  220. data/ext/tb_client/tigerbeetle/src/vsr/fault_detector.zig +279 -0
  221. data/ext/tb_client/tigerbeetle/src/vsr/free_set.zig +1381 -0
  222. data/ext/tb_client/tigerbeetle/src/vsr/free_set_fuzz.zig +315 -0
  223. data/ext/tb_client/tigerbeetle/src/vsr/grid.zig +1460 -0
  224. data/ext/tb_client/tigerbeetle/src/vsr/grid_blocks_missing.zig +757 -0
  225. data/ext/tb_client/tigerbeetle/src/vsr/grid_scrubber.zig +797 -0
  226. data/ext/tb_client/tigerbeetle/src/vsr/journal.zig +2586 -0
  227. data/ext/tb_client/tigerbeetle/src/vsr/marzullo.zig +308 -0
  228. data/ext/tb_client/tigerbeetle/src/vsr/message_header.zig +1777 -0
  229. data/ext/tb_client/tigerbeetle/src/vsr/multi_batch.zig +715 -0
  230. data/ext/tb_client/tigerbeetle/src/vsr/multi_batch_fuzz.zig +185 -0
  231. data/ext/tb_client/tigerbeetle/src/vsr/repair_budget.zig +333 -0
  232. data/ext/tb_client/tigerbeetle/src/vsr/replica.zig +12355 -0
  233. data/ext/tb_client/tigerbeetle/src/vsr/replica_format.zig +416 -0
  234. data/ext/tb_client/tigerbeetle/src/vsr/replica_reformat.zig +165 -0
  235. data/ext/tb_client/tigerbeetle/src/vsr/replica_test.zig +2910 -0
  236. data/ext/tb_client/tigerbeetle/src/vsr/routing.zig +1075 -0
  237. data/ext/tb_client/tigerbeetle/src/vsr/superblock.zig +1603 -0
  238. data/ext/tb_client/tigerbeetle/src/vsr/superblock_fuzz.zig +484 -0
  239. data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums.zig +405 -0
  240. data/ext/tb_client/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +355 -0
  241. data/ext/tb_client/tigerbeetle/src/vsr/sync.zig +29 -0
  242. data/ext/tb_client/tigerbeetle/src/vsr.zig +1727 -0
  243. data/lib/tb_client/shared_lib.rb +12 -5
  244. data/lib/tigerbeetle/client.rb +1 -1
  245. data/lib/tigerbeetle/platforms.rb +9 -0
  246. data/lib/tigerbeetle/version.rb +2 -2
  247. data/tigerbeetle.gemspec +22 -5
  248. metadata +242 -3
  249. data/ext/tb_client/pkg.tar.gz +0 -0
@@ -0,0 +1,496 @@
1
+ const std = @import("std");
2
+ const assert = std.debug.assert;
3
+ const posix = std.posix;
4
+ const windows = std.os.windows;
5
+
6
+ const builtin = @import("builtin");
7
+
8
+ pub const Terminal = struct {
9
+ const ModeStart = switch (builtin.os.tag) {
10
+ .linux, .macos => posix.termios,
11
+ .windows => WindowsConsoleMode,
12
+ else => unreachable,
13
+ };
14
+
15
+ mode_start: ?ModeStart,
16
+ stdin: std.io.BufferedReader(4096, std.fs.File.Reader),
17
+ // These are made optional so that printing on failure can be disabled in tests expecting them.
18
+ stdout: ?std.fs.File.Writer,
19
+ stderr: ?std.fs.File.Writer,
20
+
21
+ pub fn init(
22
+ self: *Terminal,
23
+ interactive: bool,
24
+ ) !void {
25
+ const stdout = std.io.getStdOut();
26
+ if (interactive and !stdout.getOrEnableAnsiEscapeSupport()) {
27
+ std.debug.print("ANSI escape sequences not supported.\n", .{});
28
+ std.process.exit(1);
29
+ }
30
+
31
+ const stdin = std.io.getStdIn();
32
+ var mode_start: ?ModeStart = null;
33
+ if (interactive) {
34
+ if (builtin.os.tag == .windows) {
35
+ var mode_stdin: u32 = 0;
36
+ if (windows.kernel32.GetConsoleMode(stdin.handle, &mode_stdin) == 0) {
37
+ return windows.unexpectedError(windows.kernel32.GetLastError());
38
+ }
39
+
40
+ var mode_stdout: u32 = 0;
41
+ if (windows.kernel32.GetConsoleMode(stdout.handle, &mode_stdout) == 0) {
42
+ return windows.unexpectedError(windows.kernel32.GetLastError());
43
+ }
44
+
45
+ mode_start = WindowsConsoleMode{
46
+ .stdin = mode_stdin,
47
+ .stdout = mode_stdout,
48
+ };
49
+ } else {
50
+ mode_start = try posix.tcgetattr(stdin.handle);
51
+ }
52
+ }
53
+
54
+ self.* = Terminal{
55
+ .mode_start = mode_start,
56
+ .stdin = std.io.bufferedReader(stdin.reader()),
57
+ .stdout = stdout.writer(),
58
+ .stderr = std.io.getStdErr().writer(),
59
+ };
60
+ }
61
+
62
+ pub fn print(
63
+ self: *const Terminal,
64
+ comptime format: []const u8,
65
+ arguments: anytype,
66
+ ) !void {
67
+ if (self.stdout) |stdout| {
68
+ try stdout.print(format, arguments);
69
+ }
70
+ }
71
+
72
+ pub fn print_error(
73
+ self: *const Terminal,
74
+ comptime format: []const u8,
75
+ arguments: anytype,
76
+ ) !void {
77
+ if (self.stderr) |stderr| {
78
+ try stderr.print(format, arguments);
79
+ }
80
+ }
81
+
82
+ pub fn read_user_input(self: *Terminal) !?UserInput {
83
+ assert(self.mode_start != null);
84
+ const stdin = self.stdin.reader();
85
+
86
+ // NB: Many control codes have names unrelated to their modern function.
87
+ // https://en.wikipedia.org/wiki/C0_and_C1_control_codes
88
+ switch (try stdin.readByte()) {
89
+ std.ascii.control_code.eot => return .ctrld,
90
+ std.ascii.control_code.etx => return .ctrlc,
91
+ std.ascii.control_code.ff => return .ctrll,
92
+ std.ascii.control_code.soh => return .ctrla,
93
+ std.ascii.control_code.enq => return .ctrle,
94
+ std.ascii.control_code.stx => return .ctrlb,
95
+ std.ascii.control_code.ack => return .ctrlf,
96
+ std.ascii.control_code.dle => return .ctrlp,
97
+ std.ascii.control_code.so => return .ctrln,
98
+ std.ascii.control_code.vt => return .ctrlk,
99
+ std.ascii.control_code.cr, std.ascii.control_code.lf => return .newline,
100
+ std.ascii.control_code.bs, std.ascii.control_code.del => return .backspace,
101
+ std.ascii.control_code.ht => return .tab,
102
+ std.ascii.control_code.esc => {
103
+ // TODO: It would be nice to fully parse unhandled escape codes, and not just give
104
+ // up partway through and return `.unhandled` - but ansi escape codes are extremely
105
+ // complicated, so that may not be completely possible.
106
+ const second_byte = try stdin.readByte();
107
+ switch (second_byte) {
108
+ '[' => {
109
+ const third_byte = try stdin.readByte();
110
+ switch (third_byte) {
111
+ 'A' => return .up,
112
+ 'B' => return .down,
113
+ 'C' => return .right,
114
+ 'D' => return .left,
115
+ 'H' => return .home,
116
+ 'F' => return .end,
117
+ '1' => {
118
+ const fourth_byte = try stdin.readByte();
119
+ switch (fourth_byte) {
120
+ ';' => {
121
+ const fifth_byte = try stdin.readByte();
122
+ switch (fifth_byte) {
123
+ '5' => {
124
+ const sixth_byte = try stdin.readByte();
125
+ switch (sixth_byte) {
126
+ 'C' => return .ctrlright,
127
+ 'D' => return .ctrlleft,
128
+ else => return .unhandled,
129
+ }
130
+ },
131
+ else => return .unhandled,
132
+ }
133
+ },
134
+
135
+ else => return .unhandled,
136
+ }
137
+ },
138
+ '3' => {
139
+ const fourth_byte = try stdin.readByte();
140
+ switch (fourth_byte) {
141
+ '~' => {
142
+ // This is just one of multiple non-standard escape codes
143
+ // for delete.
144
+ return .delete;
145
+ },
146
+ else => return .unhandled,
147
+ }
148
+ },
149
+ else => return .unhandled,
150
+ }
151
+ },
152
+ 'b' => return .altb,
153
+ 'f' => return .altf,
154
+ else => return .unhandled,
155
+ }
156
+ },
157
+ else => |byte| {
158
+ if (std.ascii.isPrint(byte)) {
159
+ return .{ .printable = byte };
160
+ }
161
+ return .unhandled;
162
+ },
163
+ }
164
+ }
165
+
166
+ pub fn prompt_mode_set(self: *const Terminal) anyerror!void {
167
+ assert(self.mode_start != null);
168
+ const stdin = std.io.getStdIn();
169
+ if (builtin.os.tag == .windows) {
170
+ const console_mode = self.mode_start.?;
171
+
172
+ var mode_stdin: u32 = console_mode.stdin;
173
+ mode_stdin &= ~@intFromEnum(WindowsConsoleMode.Input.enable_processed_input);
174
+ mode_stdin &= ~@intFromEnum(WindowsConsoleMode.Input.enable_line_input);
175
+ mode_stdin &= ~@intFromEnum(WindowsConsoleMode.Input.enable_echo_input);
176
+ mode_stdin |= @intFromEnum(WindowsConsoleMode.Input.enable_virtual_terminal_input);
177
+ if (windows.kernel32.SetConsoleMode(stdin.handle, mode_stdin) == 0) {
178
+ return windows.unexpectedError(windows.kernel32.GetLastError());
179
+ }
180
+
181
+ var mode_stdout: u32 = console_mode.stdout;
182
+ mode_stdout |= @intFromEnum(WindowsConsoleMode.Output.enable_processed_output);
183
+ mode_stdout |= @intFromEnum(WindowsConsoleMode.Output.enable_wrap_at_eol_output);
184
+ mode_stdout |= @intFromEnum(
185
+ WindowsConsoleMode.Output.enable_virtual_terminal_processing,
186
+ );
187
+ mode_stdout &= ~@intFromEnum(WindowsConsoleMode.Output.disable_newline_auto_return);
188
+ if (windows.kernel32.SetConsoleMode(std.io.getStdOut().handle, mode_stdout) == 0) {
189
+ return windows.unexpectedError(windows.kernel32.GetLastError());
190
+ }
191
+ } else {
192
+ var termios_new = self.mode_start.?;
193
+ termios_new.lflag.ECHO = false;
194
+ termios_new.lflag.ISIG = false;
195
+ termios_new.lflag.ICANON = false;
196
+ termios_new.cc[@intFromEnum(posix.V.MIN)] = 1;
197
+ termios_new.cc[@intFromEnum(posix.V.TIME)] = 0;
198
+ try posix.tcsetattr(stdin.handle, .NOW, termios_new);
199
+ }
200
+ }
201
+
202
+ pub fn prompt_mode_unset(self: *const Terminal) !void {
203
+ assert(self.mode_start != null);
204
+ const stdin = std.io.getStdIn();
205
+ if (builtin.os.tag == .windows) {
206
+ const console_mode = self.mode_start.?;
207
+ if (windows.kernel32.SetConsoleMode(stdin.handle, console_mode.stdin) == 0) {
208
+ return windows.unexpectedError(windows.kernel32.GetLastError());
209
+ }
210
+ const stdout = std.io.getStdOut();
211
+ if (windows.kernel32.SetConsoleMode(stdout.handle, console_mode.stdout) == 0) {
212
+ return windows.unexpectedError(windows.kernel32.GetLastError());
213
+ }
214
+ } else {
215
+ const termios = self.mode_start.?;
216
+ try posix.tcsetattr(std.io.getStdIn().handle, .NOW, termios);
217
+ }
218
+ }
219
+
220
+ fn get_cursor_position(
221
+ self: *Terminal,
222
+ ) !struct { row: usize, column: usize } {
223
+ // Obtaining the cursor's position relies on sending a request payload to stdout. The
224
+ // response is read from stdin, but it may have been altered by user input, so we keep
225
+ // retrying until successful.
226
+ const stdin = self.stdin.reader();
227
+ while (true) {
228
+ // The terminal needs to read control codes, but the exact input capacity
229
+ // is unknown; this should be more than enough.
230
+ var buffer: [256]u8 = undefined;
231
+ var buffer_in = std.io.fixedBufferStream(&buffer);
232
+ // The response is of the form `<ESC>[{row};{col}R`.
233
+ try self.print("\x1b[6n", .{});
234
+ buffer_in.reset();
235
+ stdin.streamUntilDelimiter(
236
+ buffer_in.writer(),
237
+ '[',
238
+ buffer.len,
239
+ ) catch |err| {
240
+ switch (err) {
241
+ anyerror.StreamTooLong => continue,
242
+ else => return err,
243
+ }
244
+ };
245
+
246
+ buffer_in.reset();
247
+ stdin.streamUntilDelimiter(
248
+ buffer_in.writer(),
249
+ ';',
250
+ buffer.len,
251
+ ) catch |err| {
252
+ switch (err) {
253
+ anyerror.StreamTooLong => continue,
254
+ else => return err,
255
+ }
256
+ };
257
+ const row = std.fmt.parseInt(usize, buffer_in.getWritten(), 10) catch continue;
258
+
259
+ buffer_in.reset();
260
+ stdin.streamUntilDelimiter(
261
+ buffer_in.writer(),
262
+ 'R',
263
+ buffer.len,
264
+ ) catch |err| {
265
+ switch (err) {
266
+ anyerror.StreamTooLong => continue,
267
+ else => return err,
268
+ }
269
+ };
270
+ const column = std.fmt.parseInt(usize, buffer_in.getWritten(), 10) catch continue;
271
+
272
+ return .{
273
+ .row = row,
274
+ .column = column,
275
+ };
276
+ }
277
+ }
278
+
279
+ pub fn get_screen(
280
+ self: *Terminal,
281
+ ) !Screen {
282
+ // We move the cursor to a location that is unlikely to exist (2^16th row and column).
283
+ // Terminals usually handle this by placing the cursor at their end position, which we can
284
+ // use to obtain its resolution/size.
285
+ const cursor_start = try self.get_cursor_position();
286
+ try self.print("\x1b[{};{}H", .{ std.math.maxInt(u16), std.math.maxInt(u16) });
287
+
288
+ const cursor_end = try self.get_cursor_position();
289
+ try self.print("\x1b[{};{}H", .{ cursor_start.row, cursor_start.column });
290
+
291
+ return Screen{
292
+ .rows = cursor_end.row,
293
+ .columns = cursor_end.column,
294
+ .cursor_row = cursor_start.row,
295
+ .cursor_column = cursor_start.column,
296
+ };
297
+ }
298
+ };
299
+
300
+ const Screen = struct {
301
+ rows: usize,
302
+ columns: usize,
303
+ cursor_row: usize,
304
+ cursor_column: usize,
305
+
306
+ pub fn update_cursor_position(self: *Screen, delta: isize) void {
307
+ if (delta == 0) return;
308
+
309
+ // Rows and columns in terminals are one-based indexed. For simple manipulation across the
310
+ // grid, we map it to a zero-based index array of cells. When experiencing overflows on
311
+ // either end, we always saturate them such that they remain within the first or last row of
312
+ // the terminal (assuming a fixed size).
313
+ const cell_total: isize = @intCast(self.rows * self.columns);
314
+ const cell_index_current: isize = @intCast(
315
+ (self.cursor_row - 1) * self.columns + (self.cursor_column - 1),
316
+ );
317
+ assert(cell_index_current >= 0 and cell_index_current < cell_total);
318
+
319
+ const cell_index_last: isize = @intCast(cell_total - 1);
320
+ const column_total: isize = @intCast(self.columns);
321
+
322
+ var cell_index_after_delta: isize = cell_index_current + delta;
323
+ if (cell_index_after_delta > cell_index_last) {
324
+ const cell_index_row_n_col_1 = cell_total - column_total;
325
+ const cell_extra = cell_index_after_delta - cell_index_last;
326
+ cell_index_after_delta = cell_index_row_n_col_1 + @rem((cell_extra - 1), column_total);
327
+ } else if (cell_index_after_delta < 0) {
328
+ const cell_index_row_1_col_n = column_total - 1;
329
+ const cell_extra: isize = @intCast(@abs(cell_index_after_delta));
330
+ cell_index_after_delta = cell_index_row_1_col_n - @rem((cell_extra - 1), column_total);
331
+ }
332
+
333
+ const row_after_delta = @divTrunc(cell_index_after_delta, column_total) + 1;
334
+ const column_after_delta = @rem(cell_index_after_delta, column_total) + 1;
335
+
336
+ assert(row_after_delta >= 1 and row_after_delta <= self.rows);
337
+ assert(column_after_delta >= 1 and column_after_delta <= self.columns);
338
+
339
+ self.cursor_row = @intCast(row_after_delta);
340
+ self.cursor_column = @intCast(column_after_delta);
341
+ }
342
+ };
343
+
344
+ const UserInput = union(enum) {
345
+ printable: u8,
346
+ ctrld,
347
+ ctrlc,
348
+ ctrll,
349
+ ctrla,
350
+ ctrle,
351
+ ctrlb,
352
+ ctrlf,
353
+ ctrlp,
354
+ ctrln,
355
+ ctrlk,
356
+ newline,
357
+ backspace,
358
+ delete,
359
+ tab,
360
+ left,
361
+ right,
362
+ up,
363
+ down,
364
+ altf,
365
+ altb,
366
+ ctrlleft,
367
+ ctrlright,
368
+ home,
369
+ end,
370
+ unhandled,
371
+ };
372
+
373
+ const WindowsConsoleMode = struct {
374
+ stdin: u32,
375
+ stdout: u32,
376
+
377
+ const Input = enum(u32) {
378
+ enable_processed_input = 0x0001,
379
+ enable_line_input = 0x0002,
380
+ enable_echo_input = 0x0004,
381
+ enable_virtual_terminal_input = 0x0200,
382
+ };
383
+
384
+ const Output = enum(u32) {
385
+ enable_processed_output = 0x0001,
386
+ enable_wrap_at_eol_output = 0x0002,
387
+ enable_virtual_terminal_processing = 0x0004,
388
+ disable_newline_auto_return = 0x0008,
389
+ };
390
+ };
391
+
392
+ test "terminal.zig: Terminal cursor position change is valid" {
393
+ const tests = [_]struct {
394
+ rows: usize,
395
+ columns: usize,
396
+ row_source: usize,
397
+ column_source: usize,
398
+ delta: isize,
399
+ row_destination: usize,
400
+ column_destination: usize,
401
+ }{
402
+ .{
403
+ .rows = 10,
404
+ .columns = 10,
405
+ .row_source = 1,
406
+ .column_source = 1,
407
+ .delta = -32,
408
+ .row_destination = 1,
409
+ .column_destination = 9,
410
+ },
411
+ .{
412
+ .rows = 10,
413
+ .columns = 10,
414
+ .row_source = 1,
415
+ .column_source = 1,
416
+ .delta = -1,
417
+ .row_destination = 1,
418
+ .column_destination = 10,
419
+ },
420
+ .{
421
+ .rows = 10,
422
+ .columns = 10,
423
+ .row_source = 1,
424
+ .column_source = 2,
425
+ .delta = -1,
426
+ .row_destination = 1,
427
+ .column_destination = 1,
428
+ },
429
+ .{
430
+ .rows = 10,
431
+ .columns = 10,
432
+ .row_source = 3,
433
+ .column_source = 8,
434
+ .delta = -26,
435
+ .row_destination = 1,
436
+ .column_destination = 2,
437
+ },
438
+ .{
439
+ .rows = 10,
440
+ .columns = 10,
441
+ .row_source = 2,
442
+ .column_source = 9,
443
+ .delta = 0,
444
+ .row_destination = 2,
445
+ .column_destination = 9,
446
+ },
447
+ .{
448
+ .rows = 10,
449
+ .columns = 10,
450
+ .row_source = 2,
451
+ .column_source = 9,
452
+ .delta = 61,
453
+ .row_destination = 8,
454
+ .column_destination = 10,
455
+ },
456
+ .{
457
+ .rows = 10,
458
+ .columns = 10,
459
+ .row_source = 10,
460
+ .column_source = 9,
461
+ .delta = 1,
462
+ .row_destination = 10,
463
+ .column_destination = 10,
464
+ },
465
+ .{
466
+ .rows = 10,
467
+ .columns = 10,
468
+ .row_source = 10,
469
+ .column_source = 10,
470
+ .delta = 1,
471
+ .row_destination = 10,
472
+ .column_destination = 1,
473
+ },
474
+ .{
475
+ .rows = 10,
476
+ .columns = 10,
477
+ .row_source = 10,
478
+ .column_source = 10,
479
+ .delta = 21,
480
+ .row_destination = 10,
481
+ .column_destination = 1,
482
+ },
483
+ };
484
+
485
+ for (tests) |t| {
486
+ var screen = Screen{
487
+ .rows = t.rows,
488
+ .columns = t.columns,
489
+ .cursor_row = t.row_source,
490
+ .cursor_column = t.column_source,
491
+ };
492
+ screen.update_cursor_position(t.delta);
493
+ try std.testing.expectEqual(screen.cursor_row, t.row_destination);
494
+ try std.testing.expectEqual(screen.cursor_column, t.column_destination);
495
+ }
496
+ }