diff --git a/tools/Cargo.lock b/tools/Cargo.lock index a1d2151959..3926c1d6fb 100644 --- a/tools/Cargo.lock +++ b/tools/Cargo.lock @@ -92,6 +92,27 @@ version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" +[[package]] +name = "assert_cmd" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c5bcfa8749ac45dd12cb11055aeeb6b27a3895560d60d71e3c23bf979e60514" +dependencies = [ + "anstyle", + "bstr", + "libc", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + [[package]] name = "backtrace" version = "0.3.76" @@ -116,6 +137,21 @@ dependencies = [ "backtrace", ] +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitflags" version = "2.10.0" @@ -370,6 +406,24 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "datatest-stable" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a867d7322eb69cf3a68a5426387a25b45cb3b9c5ee41023ee6cea92e2afadd82" +dependencies = [ + "camino", + "fancy-regex", + "libtest-mimic", + "walkdir", +] + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "either" version = "1.15.0" @@ -398,6 +452,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "escape8259" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5692dd7b5a1978a5aeb0ce83b7655c58ca8efdcb79d21036ea249da95afec2c6" + [[package]] name = "eyre" version = "0.6.12" @@ -408,6 +468,32 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fancy-regex" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298" +dependencies = [ + "bit-set", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "float-cmp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" +dependencies = [ + "num-traits", +] + [[package]] name = "generate-readme" version = "0.0.0" @@ -415,6 +501,18 @@ dependencies = [ "regex", ] +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + [[package]] name = "gimli" version = "0.32.3" @@ -444,17 +542,21 @@ name = "hermes" version = "0.0.0" dependencies = [ "anyhow", + "assert_cmd", "cargo_metadata 0.23.1", "clap", "clap-cargo", "dashmap", + "datatest-stable", "log", "miette", + "predicates", "proc-macro2", "rayon", "serde", "serde_json", "syn", + "tempfile", "thiserror 2.0.18", "ui_test", "walkdir", @@ -535,6 +637,18 @@ version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +[[package]] +name = "libtest-mimic" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5297962ef19edda4ce33aaa484386e0a5b3d7f2f4e037cbeee00503ef6b29d33" +dependencies = [ + "anstream", + "anstyle", + "clap", + "escape8259", +] + [[package]] name = "linux-raw-sys" version = "0.11.0" @@ -601,6 +715,21 @@ dependencies = [ "adler2", ] +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "object" version = "0.37.3" @@ -653,6 +782,36 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +[[package]] +name = "predicates" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" +dependencies = [ + "anstyle", + "difflib", + "float-cmp", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" + +[[package]] +name = "predicates-tree" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "prettydiff" version = "0.9.0" @@ -680,6 +839,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "rayon" version = "1.11.0" @@ -925,6 +1090,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + [[package]] name = "terminal_size" version = "0.4.3" @@ -935,6 +1113,12 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + [[package]] name = "textwrap" version = "0.16.2" @@ -1152,6 +1336,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -1162,6 +1355,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" version = "0.2.108" @@ -1324,6 +1526,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + [[package]] name = "zmij" version = "1.0.19" diff --git a/tools/hermes/Cargo.toml b/tools/hermes/Cargo.toml index 9872937ae7..7decac4f00 100644 --- a/tools/hermes/Cargo.toml +++ b/tools/hermes/Cargo.toml @@ -26,3 +26,11 @@ walkdir = "2.5.0" syn = { version = "2.0.114", features = ["printing", "full", "visit", "extra-traits", "parsing"] } proc-macro2 = { version = "1.0.105", features = ["span-locations"] } ui_test = "0.30.4" +assert_cmd = "2.1.2" +tempfile = "3.24.0" +predicates = "3.1.3" +datatest-stable = "0.3.3" + +[[test]] +name = "integration" +harness = false diff --git a/tools/hermes/src/main.rs b/tools/hermes/src/main.rs index 95432db668..438867a469 100644 --- a/tools/hermes/src/main.rs +++ b/tools/hermes/src/main.rs @@ -5,25 +5,33 @@ mod shadow; mod transform; mod ui_test_shim; -use std::{env, process::exit}; - use clap::Parser; /// Hermes: A Literate Verification Toolchain #[derive(Parser, Debug)] #[command(name = "hermes", version, about, long_about = None)] struct Cli { - #[command(flatten)] - resolve: resolve::Args, + #[command(subcommand)] + command: Commands, +} + +#[derive(clap::Subcommand, Debug)] +enum Commands { + /// Verify a crate + Verify(resolve::Args), } fn main() -> anyhow::Result<()> { - if env::var("HERMES_UI_TEST_MODE").is_ok() { + if std::env::var("HERMES_UI_TEST_MODE").is_ok() { ui_test_shim::run(); return Ok(()); } let args = Cli::parse(); - let roots = resolve::resolve_roots(&args.resolve)?; - shadow::build_shadow_crate(&roots) + match args.command { + Commands::Verify(resolve_args) => { + let roots = resolve::resolve_roots(&resolve_args)?; + shadow::build_shadow_crate(&roots) + } + } } diff --git a/tools/hermes/src/shadow.rs b/tools/hermes/src/shadow.rs index 72d78c0d20..3cb54beada 100644 --- a/tools/hermes/src/shadow.rs +++ b/tools/hermes/src/shadow.rs @@ -7,7 +7,6 @@ use std::{ use anyhow::{Context, Result}; use dashmap::DashSet; -use rayon::prelude::*; use walkdir::WalkDir; use crate::{parse, resolve::Roots, transform}; diff --git a/tools/hermes/tests/fixtures/broken_doc_block/expected_status.txt b/tools/hermes/tests/fixtures/broken_doc_block/expected_status.txt new file mode 100644 index 0000000000..e75544fd26 --- /dev/null +++ b/tools/hermes/tests/fixtures/broken_doc_block/expected_status.txt @@ -0,0 +1,2 @@ +failure + diff --git a/tools/hermes/tests/fixtures/broken_doc_block/expected_stderr.txt b/tools/hermes/tests/fixtures/broken_doc_block/expected_stderr.txt new file mode 100644 index 0000000000..dfbd02bd09 --- /dev/null +++ b/tools/hermes/tests/fixtures/broken_doc_block/expected_stderr.txt @@ -0,0 +1 @@ +Unclosed ```lean block \ No newline at end of file diff --git a/tools/hermes/tests/fixtures/broken_doc_block/hermes.toml b/tools/hermes/tests/fixtures/broken_doc_block/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/broken_doc_block/source/Cargo.toml b/tools/hermes/tests/fixtures/broken_doc_block/source/Cargo.toml new file mode 100644 index 0000000000..fd5e64ee6a --- /dev/null +++ b/tools/hermes/tests/fixtures/broken_doc_block/source/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "broken_doc" +version = "0.1.0" +edition = "2021" + diff --git a/tools/hermes/tests/fixtures/broken_doc_block/source/src/lib.rs b/tools/hermes/tests/fixtures/broken_doc_block/source/src/lib.rs new file mode 100644 index 0000000000..34f349f6c6 --- /dev/null +++ b/tools/hermes/tests/fixtures/broken_doc_block/source/src/lib.rs @@ -0,0 +1,3 @@ +/// ```lean +/// unclosed block +fn foo() {} diff --git a/tools/hermes/tests/fixtures/crlf_endings/expected/src/lib.rs b/tools/hermes/tests/fixtures/crlf_endings/expected/src/lib.rs new file mode 100644 index 0000000000..c8a22ec9e9 --- /dev/null +++ b/tools/hermes/tests/fixtures/crlf_endings/expected/src/lib.rs @@ -0,0 +1 @@ +fn windows() {} diff --git a/tools/hermes/tests/fixtures/crlf_endings/hermes.toml b/tools/hermes/tests/fixtures/crlf_endings/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/crlf_endings/source/Cargo.toml b/tools/hermes/tests/fixtures/crlf_endings/source/Cargo.toml new file mode 100644 index 0000000000..4e91842247 --- /dev/null +++ b/tools/hermes/tests/fixtures/crlf_endings/source/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "crlf" +version = "0.1.0" +edition = "2021" + diff --git a/tools/hermes/tests/fixtures/crlf_endings/source/src/lib.rs b/tools/hermes/tests/fixtures/crlf_endings/source/src/lib.rs new file mode 100644 index 0000000000..c8a22ec9e9 --- /dev/null +++ b/tools/hermes/tests/fixtures/crlf_endings/source/src/lib.rs @@ -0,0 +1 @@ +fn windows() {} diff --git a/tools/hermes/tests/fixtures/custom_path_mod/expected/src/sys/unix.rs b/tools/hermes/tests/fixtures/custom_path_mod/expected/src/sys/unix.rs new file mode 100644 index 0000000000..370111009b --- /dev/null +++ b/tools/hermes/tests/fixtures/custom_path_mod/expected/src/sys/unix.rs @@ -0,0 +1,2 @@ +fn syscall() {} + diff --git a/tools/hermes/tests/fixtures/custom_path_mod/hermes.toml b/tools/hermes/tests/fixtures/custom_path_mod/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/custom_path_mod/source/Cargo.toml b/tools/hermes/tests/fixtures/custom_path_mod/source/Cargo.toml new file mode 100644 index 0000000000..7507a85cd9 --- /dev/null +++ b/tools/hermes/tests/fixtures/custom_path_mod/source/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "custom" +version = "0.1.0" +edition = "2021" + diff --git a/tools/hermes/tests/fixtures/custom_path_mod/source/src/lib.rs b/tools/hermes/tests/fixtures/custom_path_mod/source/src/lib.rs new file mode 100644 index 0000000000..ed1515ab50 --- /dev/null +++ b/tools/hermes/tests/fixtures/custom_path_mod/source/src/lib.rs @@ -0,0 +1,3 @@ +#[path = "sys/unix.rs"] +mod sys; + diff --git a/tools/hermes/tests/fixtures/custom_path_mod/source/src/sys/unix.rs b/tools/hermes/tests/fixtures/custom_path_mod/source/src/sys/unix.rs new file mode 100644 index 0000000000..370111009b --- /dev/null +++ b/tools/hermes/tests/fixtures/custom_path_mod/source/src/sys/unix.rs @@ -0,0 +1,2 @@ +fn syscall() {} + diff --git a/tools/hermes/tests/fixtures/deep_invocation/cwd.txt b/tools/hermes/tests/fixtures/deep_invocation/cwd.txt new file mode 100644 index 0000000000..d9d1aff66d --- /dev/null +++ b/tools/hermes/tests/fixtures/deep_invocation/cwd.txt @@ -0,0 +1,2 @@ +src/nested + diff --git a/tools/hermes/tests/fixtures/deep_invocation/expected/src/nested/mod.rs b/tools/hermes/tests/fixtures/deep_invocation/expected/src/nested/mod.rs new file mode 100644 index 0000000000..6287e851b2 --- /dev/null +++ b/tools/hermes/tests/fixtures/deep_invocation/expected/src/nested/mod.rs @@ -0,0 +1,2 @@ +pub fn deep_fn() {} + diff --git a/tools/hermes/tests/fixtures/deep_invocation/hermes.toml b/tools/hermes/tests/fixtures/deep_invocation/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/deep_invocation/source/Cargo.toml b/tools/hermes/tests/fixtures/deep_invocation/source/Cargo.toml new file mode 100644 index 0000000000..bb363dab52 --- /dev/null +++ b/tools/hermes/tests/fixtures/deep_invocation/source/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "deep" +version = "0.1.0" +edition = "2021" + diff --git a/tools/hermes/tests/fixtures/deep_invocation/source/src/lib.rs b/tools/hermes/tests/fixtures/deep_invocation/source/src/lib.rs new file mode 100644 index 0000000000..07fa10bb81 --- /dev/null +++ b/tools/hermes/tests/fixtures/deep_invocation/source/src/lib.rs @@ -0,0 +1,2 @@ +pub mod nested; + diff --git a/tools/hermes/tests/fixtures/deep_invocation/source/src/nested/mod.rs b/tools/hermes/tests/fixtures/deep_invocation/source/src/nested/mod.rs new file mode 100644 index 0000000000..6287e851b2 --- /dev/null +++ b/tools/hermes/tests/fixtures/deep_invocation/source/src/nested/mod.rs @@ -0,0 +1,2 @@ +pub fn deep_fn() {} + diff --git a/tools/hermes/tests/fixtures/dir_mod/expected/src/bar/mod.rs b/tools/hermes/tests/fixtures/dir_mod/expected/src/bar/mod.rs new file mode 100644 index 0000000000..c03f2998df --- /dev/null +++ b/tools/hermes/tests/fixtures/dir_mod/expected/src/bar/mod.rs @@ -0,0 +1,2 @@ +fn deep() {} + diff --git a/tools/hermes/tests/fixtures/dir_mod/hermes.toml b/tools/hermes/tests/fixtures/dir_mod/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/dir_mod/source/Cargo.toml b/tools/hermes/tests/fixtures/dir_mod/source/Cargo.toml new file mode 100644 index 0000000000..2083dae77b --- /dev/null +++ b/tools/hermes/tests/fixtures/dir_mod/source/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "dir_mod" +version = "0.1.0" +edition = "2021" + diff --git a/tools/hermes/tests/fixtures/dir_mod/source/src/bar/mod.rs b/tools/hermes/tests/fixtures/dir_mod/source/src/bar/mod.rs new file mode 100644 index 0000000000..c03f2998df --- /dev/null +++ b/tools/hermes/tests/fixtures/dir_mod/source/src/bar/mod.rs @@ -0,0 +1,2 @@ +fn deep() {} + diff --git a/tools/hermes/tests/fixtures/dir_mod/source/src/lib.rs b/tools/hermes/tests/fixtures/dir_mod/source/src/lib.rs new file mode 100644 index 0000000000..7dc9f06012 --- /dev/null +++ b/tools/hermes/tests/fixtures/dir_mod/source/src/lib.rs @@ -0,0 +1,2 @@ +mod bar; + diff --git a/tools/hermes/tests/fixtures/dirty_target_dir/expected/src/lib.rs b/tools/hermes/tests/fixtures/dirty_target_dir/expected/src/lib.rs new file mode 100644 index 0000000000..6d8e9b7b69 --- /dev/null +++ b/tools/hermes/tests/fixtures/dirty_target_dir/expected/src/lib.rs @@ -0,0 +1,2 @@ +fn clean() {} + diff --git a/tools/hermes/tests/fixtures/dirty_target_dir/hermes.toml b/tools/hermes/tests/fixtures/dirty_target_dir/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/dirty_target_dir/source/Cargo.toml b/tools/hermes/tests/fixtures/dirty_target_dir/source/Cargo.toml new file mode 100644 index 0000000000..ddd21f7175 --- /dev/null +++ b/tools/hermes/tests/fixtures/dirty_target_dir/source/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "cleaner" +version = "0.1.0" +edition = "2021" + diff --git a/tools/hermes/tests/fixtures/dirty_target_dir/source/src/lib.rs b/tools/hermes/tests/fixtures/dirty_target_dir/source/src/lib.rs new file mode 100644 index 0000000000..6d8e9b7b69 --- /dev/null +++ b/tools/hermes/tests/fixtures/dirty_target_dir/source/src/lib.rs @@ -0,0 +1,2 @@ +fn clean() {} + diff --git a/tools/hermes/tests/fixtures/duplicate_blocks/expected_status.txt b/tools/hermes/tests/fixtures/duplicate_blocks/expected_status.txt new file mode 100644 index 0000000000..7a4059ef83 --- /dev/null +++ b/tools/hermes/tests/fixtures/duplicate_blocks/expected_status.txt @@ -0,0 +1 @@ +failure diff --git a/tools/hermes/tests/fixtures/duplicate_blocks/expected_stderr.txt b/tools/hermes/tests/fixtures/duplicate_blocks/expected_stderr.txt new file mode 100644 index 0000000000..e3cf208e06 --- /dev/null +++ b/tools/hermes/tests/fixtures/duplicate_blocks/expected_stderr.txt @@ -0,0 +1 @@ +Multiple lean blocks found diff --git a/tools/hermes/tests/fixtures/duplicate_blocks/hermes.toml b/tools/hermes/tests/fixtures/duplicate_blocks/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/duplicate_blocks/source/Cargo.toml b/tools/hermes/tests/fixtures/duplicate_blocks/source/Cargo.toml new file mode 100644 index 0000000000..c0d216987f --- /dev/null +++ b/tools/hermes/tests/fixtures/duplicate_blocks/source/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "dup" +version = "0.1.0" +edition = "2021" diff --git a/tools/hermes/tests/fixtures/duplicate_blocks/source/src/lib.rs b/tools/hermes/tests/fixtures/duplicate_blocks/source/src/lib.rs new file mode 100644 index 0000000000..d8daa6ac0c --- /dev/null +++ b/tools/hermes/tests/fixtures/duplicate_blocks/source/src/lib.rs @@ -0,0 +1,7 @@ +/// ```lean +/// block 1 +/// ``` +/// ```lean +/// block 2 +/// ``` +fn double_doc() {} diff --git a/tools/hermes/tests/fixtures/empty_file/expected/src/lib.rs b/tools/hermes/tests/fixtures/empty_file/expected/src/lib.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/empty_file/hermes.toml b/tools/hermes/tests/fixtures/empty_file/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/empty_file/source/Cargo.toml b/tools/hermes/tests/fixtures/empty_file/source/Cargo.toml new file mode 100644 index 0000000000..6cba386926 --- /dev/null +++ b/tools/hermes/tests/fixtures/empty_file/source/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "empty" +version = "0.1.0" +edition = "2021" diff --git a/tools/hermes/tests/fixtures/empty_file/source/src/lib.rs b/tools/hermes/tests/fixtures/empty_file/source/src/lib.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/external_dep_failure/cwd.txt b/tools/hermes/tests/fixtures/external_dep_failure/cwd.txt new file mode 100644 index 0000000000..58341d9a29 --- /dev/null +++ b/tools/hermes/tests/fixtures/external_dep_failure/cwd.txt @@ -0,0 +1,2 @@ +app + diff --git a/tools/hermes/tests/fixtures/external_dep_failure/expected_status.txt b/tools/hermes/tests/fixtures/external_dep_failure/expected_status.txt new file mode 100644 index 0000000000..e75544fd26 --- /dev/null +++ b/tools/hermes/tests/fixtures/external_dep_failure/expected_status.txt @@ -0,0 +1,2 @@ +failure + diff --git a/tools/hermes/tests/fixtures/external_dep_failure/expected_stderr.txt b/tools/hermes/tests/fixtures/external_dep_failure/expected_stderr.txt new file mode 100644 index 0000000000..2280ec54b3 --- /dev/null +++ b/tools/hermes/tests/fixtures/external_dep_failure/expected_stderr.txt @@ -0,0 +1,2 @@ +Unsupported external dependency + diff --git a/tools/hermes/tests/fixtures/external_dep_failure/hermes.toml b/tools/hermes/tests/fixtures/external_dep_failure/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/external_dep_failure/source/app/Cargo.toml b/tools/hermes/tests/fixtures/external_dep_failure/source/app/Cargo.toml new file mode 100644 index 0000000000..011d73ba64 --- /dev/null +++ b/tools/hermes/tests/fixtures/external_dep_failure/source/app/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "app" +version = "0.1.0" +edition = "2021" + +[dependencies] +utils = { path = "../utils" } + diff --git a/tools/hermes/tests/fixtures/external_dep_failure/source/app/src/lib.rs b/tools/hermes/tests/fixtures/external_dep_failure/source/app/src/lib.rs new file mode 100644 index 0000000000..cd52f649e4 --- /dev/null +++ b/tools/hermes/tests/fixtures/external_dep_failure/source/app/src/lib.rs @@ -0,0 +1,2 @@ +fn main() {} + diff --git a/tools/hermes/tests/fixtures/external_dep_failure/source/utils/Cargo.toml b/tools/hermes/tests/fixtures/external_dep_failure/source/utils/Cargo.toml new file mode 100644 index 0000000000..b7f00898a6 --- /dev/null +++ b/tools/hermes/tests/fixtures/external_dep_failure/source/utils/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "utils" +version = "0.1.0" +edition = "2021" + diff --git a/tools/hermes/tests/fixtures/external_dep_failure/source/utils/src/lib.rs b/tools/hermes/tests/fixtures/external_dep_failure/source/utils/src/lib.rs new file mode 100644 index 0000000000..c5433559d9 --- /dev/null +++ b/tools/hermes/tests/fixtures/external_dep_failure/source/utils/src/lib.rs @@ -0,0 +1 @@ +fn util_func() {} diff --git a/tools/hermes/tests/fixtures/file_mod/expected/src/foo.rs b/tools/hermes/tests/fixtures/file_mod/expected/src/foo.rs new file mode 100644 index 0000000000..07e1be6bcd --- /dev/null +++ b/tools/hermes/tests/fixtures/file_mod/expected/src/foo.rs @@ -0,0 +1,2 @@ +fn sub() {} + diff --git a/tools/hermes/tests/fixtures/file_mod/expected/src/lib.rs b/tools/hermes/tests/fixtures/file_mod/expected/src/lib.rs new file mode 100644 index 0000000000..09f2c5c498 --- /dev/null +++ b/tools/hermes/tests/fixtures/file_mod/expected/src/lib.rs @@ -0,0 +1,2 @@ +mod foo; + diff --git a/tools/hermes/tests/fixtures/file_mod/hermes.toml b/tools/hermes/tests/fixtures/file_mod/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/file_mod/source/Cargo.toml b/tools/hermes/tests/fixtures/file_mod/source/Cargo.toml new file mode 100644 index 0000000000..1672409748 --- /dev/null +++ b/tools/hermes/tests/fixtures/file_mod/source/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "file_mod" +version = "0.1.0" +edition = "2021" + diff --git a/tools/hermes/tests/fixtures/file_mod/source/src/foo.rs b/tools/hermes/tests/fixtures/file_mod/source/src/foo.rs new file mode 100644 index 0000000000..07e1be6bcd --- /dev/null +++ b/tools/hermes/tests/fixtures/file_mod/source/src/foo.rs @@ -0,0 +1,2 @@ +fn sub() {} + diff --git a/tools/hermes/tests/fixtures/file_mod/source/src/lib.rs b/tools/hermes/tests/fixtures/file_mod/source/src/lib.rs new file mode 100644 index 0000000000..09f2c5c498 --- /dev/null +++ b/tools/hermes/tests/fixtures/file_mod/source/src/lib.rs @@ -0,0 +1,2 @@ +mod foo; + diff --git a/tools/hermes/tests/fixtures/ignored_files/expected/src/lib.rs b/tools/hermes/tests/fixtures/ignored_files/expected/src/lib.rs new file mode 100644 index 0000000000..11377e80cc --- /dev/null +++ b/tools/hermes/tests/fixtures/ignored_files/expected/src/lib.rs @@ -0,0 +1,2 @@ +fn keep() {} + diff --git a/tools/hermes/tests/fixtures/ignored_files/hermes.toml b/tools/hermes/tests/fixtures/ignored_files/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/ignored_files/source/.gitignore b/tools/hermes/tests/fixtures/ignored_files/source/.gitignore new file mode 100644 index 0000000000..2f49903769 --- /dev/null +++ b/tools/hermes/tests/fixtures/ignored_files/source/.gitignore @@ -0,0 +1,2 @@ +/trash + diff --git a/tools/hermes/tests/fixtures/ignored_files/source/Cargo.toml b/tools/hermes/tests/fixtures/ignored_files/source/Cargo.toml new file mode 100644 index 0000000000..aea8245f30 --- /dev/null +++ b/tools/hermes/tests/fixtures/ignored_files/source/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "ignored" +version = "0.1.0" +edition = "2021" + diff --git a/tools/hermes/tests/fixtures/ignored_files/source/src/lib.rs b/tools/hermes/tests/fixtures/ignored_files/source/src/lib.rs new file mode 100644 index 0000000000..11377e80cc --- /dev/null +++ b/tools/hermes/tests/fixtures/ignored_files/source/src/lib.rs @@ -0,0 +1,2 @@ +fn keep() {} + diff --git a/tools/hermes/tests/fixtures/inline_mod/expected/src/lib.rs b/tools/hermes/tests/fixtures/inline_mod/expected/src/lib.rs new file mode 100644 index 0000000000..7e9b9ef310 --- /dev/null +++ b/tools/hermes/tests/fixtures/inline_mod/expected/src/lib.rs @@ -0,0 +1,4 @@ +mod foo { + fn bar() {} +} + diff --git a/tools/hermes/tests/fixtures/inline_mod/hermes.toml b/tools/hermes/tests/fixtures/inline_mod/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/inline_mod/source/Cargo.toml b/tools/hermes/tests/fixtures/inline_mod/source/Cargo.toml new file mode 100644 index 0000000000..49d2ca3dd0 --- /dev/null +++ b/tools/hermes/tests/fixtures/inline_mod/source/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "inline" +version = "0.1.0" +edition = "2021" + diff --git a/tools/hermes/tests/fixtures/inline_mod/source/src/lib.rs b/tools/hermes/tests/fixtures/inline_mod/source/src/lib.rs new file mode 100644 index 0000000000..7e9b9ef310 --- /dev/null +++ b/tools/hermes/tests/fixtures/inline_mod/source/src/lib.rs @@ -0,0 +1,4 @@ +mod foo { + fn bar() {} +} + diff --git a/tools/hermes/tests/fixtures/large_assets/expected/assets/image.png b/tools/hermes/tests/fixtures/large_assets/expected/assets/image.png new file mode 100644 index 0000000000..b976e96a2c --- /dev/null +++ b/tools/hermes/tests/fixtures/large_assets/expected/assets/image.png @@ -0,0 +1 @@ +fake binary content diff --git a/tools/hermes/tests/fixtures/large_assets/hermes.toml b/tools/hermes/tests/fixtures/large_assets/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/large_assets/source/Cargo.toml b/tools/hermes/tests/fixtures/large_assets/source/Cargo.toml new file mode 100644 index 0000000000..0461cec0ee --- /dev/null +++ b/tools/hermes/tests/fixtures/large_assets/source/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "assets" +version = "0.1.0" +edition = "2021" + diff --git a/tools/hermes/tests/fixtures/large_assets/source/assets/image.png b/tools/hermes/tests/fixtures/large_assets/source/assets/image.png new file mode 100644 index 0000000000..b976e96a2c --- /dev/null +++ b/tools/hermes/tests/fixtures/large_assets/source/assets/image.png @@ -0,0 +1 @@ +fake binary content diff --git a/tools/hermes/tests/fixtures/large_assets/source/src/lib.rs b/tools/hermes/tests/fixtures/large_assets/source/src/lib.rs new file mode 100644 index 0000000000..cad79259ab --- /dev/null +++ b/tools/hermes/tests/fixtures/large_assets/source/src/lib.rs @@ -0,0 +1,2 @@ +fn code() {} + diff --git a/tools/hermes/tests/fixtures/missing_cfg_mod/expected/src/lib.rs b/tools/hermes/tests/fixtures/missing_cfg_mod/expected/src/lib.rs new file mode 100644 index 0000000000..f6af9373e9 --- /dev/null +++ b/tools/hermes/tests/fixtures/missing_cfg_mod/expected/src/lib.rs @@ -0,0 +1,3 @@ +#[cfg(target_os = "fake_os")] +mod fake; + diff --git a/tools/hermes/tests/fixtures/missing_cfg_mod/hermes.toml b/tools/hermes/tests/fixtures/missing_cfg_mod/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/missing_cfg_mod/source/Cargo.toml b/tools/hermes/tests/fixtures/missing_cfg_mod/source/Cargo.toml new file mode 100644 index 0000000000..02a3378f49 --- /dev/null +++ b/tools/hermes/tests/fixtures/missing_cfg_mod/source/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "missing" +version = "0.1.0" +edition = "2021" + diff --git a/tools/hermes/tests/fixtures/missing_cfg_mod/source/src/lib.rs b/tools/hermes/tests/fixtures/missing_cfg_mod/source/src/lib.rs new file mode 100644 index 0000000000..f6af9373e9 --- /dev/null +++ b/tools/hermes/tests/fixtures/missing_cfg_mod/source/src/lib.rs @@ -0,0 +1,3 @@ +#[cfg(target_os = "fake_os")] +mod fake; + diff --git a/tools/hermes/tests/fixtures/multi_artifact/expected/src/lib.rs b/tools/hermes/tests/fixtures/multi_artifact/expected/src/lib.rs new file mode 100644 index 0000000000..a1e8785adc --- /dev/null +++ b/tools/hermes/tests/fixtures/multi_artifact/expected/src/lib.rs @@ -0,0 +1,2 @@ +pub fn shared() {} + diff --git a/tools/hermes/tests/fixtures/multi_artifact/hermes.toml b/tools/hermes/tests/fixtures/multi_artifact/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/multi_artifact/source/Cargo.toml b/tools/hermes/tests/fixtures/multi_artifact/source/Cargo.toml new file mode 100644 index 0000000000..2c7904ec7c --- /dev/null +++ b/tools/hermes/tests/fixtures/multi_artifact/source/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "multi" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["rlib", "cdylib"] + diff --git a/tools/hermes/tests/fixtures/multi_artifact/source/src/lib.rs b/tools/hermes/tests/fixtures/multi_artifact/source/src/lib.rs new file mode 100644 index 0000000000..a1e8785adc --- /dev/null +++ b/tools/hermes/tests/fixtures/multi_artifact/source/src/lib.rs @@ -0,0 +1,2 @@ +pub fn shared() {} + diff --git a/tools/hermes/tests/fixtures/multiple_errors/expected_status.txt b/tools/hermes/tests/fixtures/multiple_errors/expected_status.txt new file mode 100644 index 0000000000..e75544fd26 --- /dev/null +++ b/tools/hermes/tests/fixtures/multiple_errors/expected_status.txt @@ -0,0 +1,2 @@ +failure + diff --git a/tools/hermes/tests/fixtures/multiple_errors/expected_stderr.txt b/tools/hermes/tests/fixtures/multiple_errors/expected_stderr.txt new file mode 100644 index 0000000000..83304d8ef0 --- /dev/null +++ b/tools/hermes/tests/fixtures/multiple_errors/expected_stderr.txt @@ -0,0 +1 @@ +Aborting due to diff --git a/tools/hermes/tests/fixtures/multiple_errors/hermes.toml b/tools/hermes/tests/fixtures/multiple_errors/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/multiple_errors/source/Cargo.toml b/tools/hermes/tests/fixtures/multiple_errors/source/Cargo.toml new file mode 100644 index 0000000000..43d5fbadf9 --- /dev/null +++ b/tools/hermes/tests/fixtures/multiple_errors/source/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "multi_err" +version = "0.1.0" +edition = "2021" + diff --git a/tools/hermes/tests/fixtures/multiple_errors/source/src/a.rs b/tools/hermes/tests/fixtures/multiple_errors/source/src/a.rs new file mode 100644 index 0000000000..f267da095b --- /dev/null +++ b/tools/hermes/tests/fixtures/multiple_errors/source/src/a.rs @@ -0,0 +1,2 @@ +fn broken( { + diff --git a/tools/hermes/tests/fixtures/multiple_errors/source/src/b.rs b/tools/hermes/tests/fixtures/multiple_errors/source/src/b.rs new file mode 100644 index 0000000000..59293b8470 --- /dev/null +++ b/tools/hermes/tests/fixtures/multiple_errors/source/src/b.rs @@ -0,0 +1,3 @@ +/// ```lean +/// unclosed +fn foo() {} diff --git a/tools/hermes/tests/fixtures/multiple_errors/source/src/lib.rs b/tools/hermes/tests/fixtures/multiple_errors/source/src/lib.rs new file mode 100644 index 0000000000..0e6915fecc --- /dev/null +++ b/tools/hermes/tests/fixtures/multiple_errors/source/src/lib.rs @@ -0,0 +1,3 @@ +mod a; +mod b; + diff --git a/tools/hermes/tests/fixtures/non_standard_layout/expected/code/custom.rs b/tools/hermes/tests/fixtures/non_standard_layout/expected/code/custom.rs new file mode 100644 index 0000000000..57024ceadc --- /dev/null +++ b/tools/hermes/tests/fixtures/non_standard_layout/expected/code/custom.rs @@ -0,0 +1,2 @@ +pub fn check() {} + diff --git a/tools/hermes/tests/fixtures/non_standard_layout/hermes.toml b/tools/hermes/tests/fixtures/non_standard_layout/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/non_standard_layout/source/Cargo.toml b/tools/hermes/tests/fixtures/non_standard_layout/source/Cargo.toml new file mode 100644 index 0000000000..6888497b11 --- /dev/null +++ b/tools/hermes/tests/fixtures/non_standard_layout/source/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "custom_layout" +version = "0.1.0" +edition = "2021" + +[lib] +path = "code/custom.rs" + diff --git a/tools/hermes/tests/fixtures/non_standard_layout/source/code/custom.rs b/tools/hermes/tests/fixtures/non_standard_layout/source/code/custom.rs new file mode 100644 index 0000000000..57024ceadc --- /dev/null +++ b/tools/hermes/tests/fixtures/non_standard_layout/source/code/custom.rs @@ -0,0 +1,2 @@ +pub fn check() {} + diff --git a/tools/hermes/tests/fixtures/syntax_error/expected_status.txt b/tools/hermes/tests/fixtures/syntax_error/expected_status.txt new file mode 100644 index 0000000000..e75544fd26 --- /dev/null +++ b/tools/hermes/tests/fixtures/syntax_error/expected_status.txt @@ -0,0 +1,2 @@ +failure + diff --git a/tools/hermes/tests/fixtures/syntax_error/expected_stderr.txt b/tools/hermes/tests/fixtures/syntax_error/expected_stderr.txt new file mode 100644 index 0000000000..60ad0f27fc --- /dev/null +++ b/tools/hermes/tests/fixtures/syntax_error/expected_stderr.txt @@ -0,0 +1 @@ +cannot parse string into token stream diff --git a/tools/hermes/tests/fixtures/syntax_error/hermes.toml b/tools/hermes/tests/fixtures/syntax_error/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/syntax_error/source/Cargo.toml b/tools/hermes/tests/fixtures/syntax_error/source/Cargo.toml new file mode 100644 index 0000000000..f1a5aba9a8 --- /dev/null +++ b/tools/hermes/tests/fixtures/syntax_error/source/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "broken" +version = "0.1.0" +edition = "2021" + diff --git a/tools/hermes/tests/fixtures/syntax_error/source/src/lib.rs b/tools/hermes/tests/fixtures/syntax_error/source/src/lib.rs new file mode 100644 index 0000000000..f267da095b --- /dev/null +++ b/tools/hermes/tests/fixtures/syntax_error/source/src/lib.rs @@ -0,0 +1,2 @@ +fn broken( { + diff --git a/tools/hermes/tests/fixtures/target_selection/args.txt b/tools/hermes/tests/fixtures/target_selection/args.txt new file mode 100644 index 0000000000..29b873875d --- /dev/null +++ b/tools/hermes/tests/fixtures/target_selection/args.txt @@ -0,0 +1,2 @@ +verify --bin selector + diff --git a/tools/hermes/tests/fixtures/target_selection/expected/src/main.rs b/tools/hermes/tests/fixtures/target_selection/expected/src/main.rs new file mode 100644 index 0000000000..cd52f649e4 --- /dev/null +++ b/tools/hermes/tests/fixtures/target_selection/expected/src/main.rs @@ -0,0 +1,2 @@ +fn main() {} + diff --git a/tools/hermes/tests/fixtures/target_selection/hermes.toml b/tools/hermes/tests/fixtures/target_selection/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/target_selection/source/Cargo.toml b/tools/hermes/tests/fixtures/target_selection/source/Cargo.toml new file mode 100644 index 0000000000..6b4869e1ef --- /dev/null +++ b/tools/hermes/tests/fixtures/target_selection/source/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "selector" +version = "0.1.0" +edition = "2021" + diff --git a/tools/hermes/tests/fixtures/target_selection/source/src/lib.rs b/tools/hermes/tests/fixtures/target_selection/source/src/lib.rs new file mode 100644 index 0000000000..f541b876d8 --- /dev/null +++ b/tools/hermes/tests/fixtures/target_selection/source/src/lib.rs @@ -0,0 +1,3 @@ +// Should NOT be present in expected +pub fn library_logic() {} + diff --git a/tools/hermes/tests/fixtures/target_selection/source/src/main.rs b/tools/hermes/tests/fixtures/target_selection/source/src/main.rs new file mode 100644 index 0000000000..cd52f649e4 --- /dev/null +++ b/tools/hermes/tests/fixtures/target_selection/source/src/main.rs @@ -0,0 +1,2 @@ +fn main() {} + diff --git a/tools/hermes/tests/fixtures/unsafe_redaction/expected/src/lib.rs b/tools/hermes/tests/fixtures/unsafe_redaction/expected/src/lib.rs new file mode 100644 index 0000000000..c9ecff088b --- /dev/null +++ b/tools/hermes/tests/fixtures/unsafe_redaction/expected/src/lib.rs @@ -0,0 +1,6 @@ +/// ```lean +/// model foo +/// ``` + fn foo() -> i32 + + diff --git a/tools/hermes/tests/fixtures/unsafe_redaction/hermes.toml b/tools/hermes/tests/fixtures/unsafe_redaction/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/unsafe_redaction/source/Cargo.toml b/tools/hermes/tests/fixtures/unsafe_redaction/source/Cargo.toml new file mode 100644 index 0000000000..c56327a031 --- /dev/null +++ b/tools/hermes/tests/fixtures/unsafe_redaction/source/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "unsafe_test" +version = "0.1.0" +edition = "2021" + diff --git a/tools/hermes/tests/fixtures/unsafe_redaction/source/src/lib.rs b/tools/hermes/tests/fixtures/unsafe_redaction/source/src/lib.rs new file mode 100644 index 0000000000..31e1f13b80 --- /dev/null +++ b/tools/hermes/tests/fixtures/unsafe_redaction/source/src/lib.rs @@ -0,0 +1,6 @@ +/// ```lean +/// model foo +/// ``` +unsafe fn foo() -> i32 { + 1 + 1 +} diff --git a/tools/hermes/tests/fixtures/virtual_workspace/expected/crates/a/Cargo.toml b/tools/hermes/tests/fixtures/virtual_workspace/expected/crates/a/Cargo.toml new file mode 100644 index 0000000000..ed0ff1eefd --- /dev/null +++ b/tools/hermes/tests/fixtures/virtual_workspace/expected/crates/a/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "a" +version = "0.1.0" +edition = "2021" + +[dependencies] + diff --git a/tools/hermes/tests/fixtures/virtual_workspace/expected/crates/a/src/lib.rs b/tools/hermes/tests/fixtures/virtual_workspace/expected/crates/a/src/lib.rs new file mode 100644 index 0000000000..44e36138cb --- /dev/null +++ b/tools/hermes/tests/fixtures/virtual_workspace/expected/crates/a/src/lib.rs @@ -0,0 +1,2 @@ +pub fn foo() {} + diff --git a/tools/hermes/tests/fixtures/virtual_workspace/hermes.toml b/tools/hermes/tests/fixtures/virtual_workspace/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/virtual_workspace/source/Cargo.toml b/tools/hermes/tests/fixtures/virtual_workspace/source/Cargo.toml new file mode 100644 index 0000000000..0f9670cd42 --- /dev/null +++ b/tools/hermes/tests/fixtures/virtual_workspace/source/Cargo.toml @@ -0,0 +1,4 @@ +[workspace] +members = ["crates/a"] +resolver = "2" + diff --git a/tools/hermes/tests/fixtures/virtual_workspace/source/crates/a/Cargo.toml b/tools/hermes/tests/fixtures/virtual_workspace/source/crates/a/Cargo.toml new file mode 100644 index 0000000000..ed0ff1eefd --- /dev/null +++ b/tools/hermes/tests/fixtures/virtual_workspace/source/crates/a/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "a" +version = "0.1.0" +edition = "2021" + +[dependencies] + diff --git a/tools/hermes/tests/fixtures/virtual_workspace/source/crates/a/src/lib.rs b/tools/hermes/tests/fixtures/virtual_workspace/source/crates/a/src/lib.rs new file mode 100644 index 0000000000..44e36138cb --- /dev/null +++ b/tools/hermes/tests/fixtures/virtual_workspace/source/crates/a/src/lib.rs @@ -0,0 +1,2 @@ +pub fn foo() {} + diff --git a/tools/hermes/tests/fixtures/weird_functions/expected/src/lib.rs b/tools/hermes/tests/fixtures/weird_functions/expected/src/lib.rs new file mode 100644 index 0000000000..c687ef3183 --- /dev/null +++ b/tools/hermes/tests/fixtures/weird_functions/expected/src/lib.rs @@ -0,0 +1,14 @@ +/// ```lean +/// model async_foo +/// ``` +async fn async_foo() -> i32 + +/// ```lean +/// model const_foo +/// ``` +const fn const_foo() -> i32 + +/// ```lean +/// model extern_foo +/// ``` + extern "C" fn extern_foo() -> i32 diff --git a/tools/hermes/tests/fixtures/weird_functions/hermes.toml b/tools/hermes/tests/fixtures/weird_functions/hermes.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/hermes/tests/fixtures/weird_functions/source/Cargo.toml b/tools/hermes/tests/fixtures/weird_functions/source/Cargo.toml new file mode 100644 index 0000000000..690202a40c --- /dev/null +++ b/tools/hermes/tests/fixtures/weird_functions/source/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "weird" +version = "0.1.0" +edition = "2021" diff --git a/tools/hermes/tests/fixtures/weird_functions/source/src/lib.rs b/tools/hermes/tests/fixtures/weird_functions/source/src/lib.rs new file mode 100644 index 0000000000..15c7da8851 --- /dev/null +++ b/tools/hermes/tests/fixtures/weird_functions/source/src/lib.rs @@ -0,0 +1,14 @@ +/// ```lean +/// model async_foo +/// ``` +async unsafe fn async_foo() -> i32 { 0 } + +/// ```lean +/// model const_foo +/// ``` +const unsafe fn const_foo() -> i32 { 0 } + +/// ```lean +/// model extern_foo +/// ``` +unsafe extern "C" fn extern_foo() -> i32 { 0 } diff --git a/tools/hermes/tests/fixtures/workspace_path_dep/args.txt b/tools/hermes/tests/fixtures/workspace_path_dep/args.txt new file mode 100644 index 0000000000..49cc58b31f --- /dev/null +++ b/tools/hermes/tests/fixtures/workspace_path_dep/args.txt @@ -0,0 +1 @@ +verify -p app diff --git a/tools/hermes/tests/fixtures/workspace_path_dep/expected/app/src/lib.rs b/tools/hermes/tests/fixtures/workspace_path_dep/expected/app/src/lib.rs new file mode 100644 index 0000000000..3f340b3cab --- /dev/null +++ b/tools/hermes/tests/fixtures/workspace_path_dep/expected/app/src/lib.rs @@ -0,0 +1,4 @@ +/// ```lean +/// model main +/// ``` + fn main() diff --git a/tools/hermes/tests/fixtures/workspace_path_dep/expected/util/src/lib.rs b/tools/hermes/tests/fixtures/workspace_path_dep/expected/util/src/lib.rs new file mode 100644 index 0000000000..7e82c7f408 --- /dev/null +++ b/tools/hermes/tests/fixtures/workspace_path_dep/expected/util/src/lib.rs @@ -0,0 +1 @@ +pub fn help() {} diff --git a/tools/hermes/tests/fixtures/workspace_path_dep/source/Cargo.toml b/tools/hermes/tests/fixtures/workspace_path_dep/source/Cargo.toml new file mode 100644 index 0000000000..a2058a6583 --- /dev/null +++ b/tools/hermes/tests/fixtures/workspace_path_dep/source/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +members = ["crates/app", "crates/utils"] +resolver = "2" diff --git a/tools/hermes/tests/fixtures/workspace_path_dep/source/crates/app/Cargo.toml b/tools/hermes/tests/fixtures/workspace_path_dep/source/crates/app/Cargo.toml new file mode 100644 index 0000000000..15da8f2b60 --- /dev/null +++ b/tools/hermes/tests/fixtures/workspace_path_dep/source/crates/app/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "app" +version = "0.1.0" +edition = "2021" + +[dependencies] +utils = { path = "../utils" } diff --git a/tools/hermes/tests/fixtures/workspace_path_dep/source/crates/app/src/lib.rs b/tools/hermes/tests/fixtures/workspace_path_dep/source/crates/app/src/lib.rs new file mode 100644 index 0000000000..035b0948a1 --- /dev/null +++ b/tools/hermes/tests/fixtures/workspace_path_dep/source/crates/app/src/lib.rs @@ -0,0 +1,4 @@ +/// ```lean +/// model main +/// ``` +unsafe fn main() {} diff --git a/tools/hermes/tests/fixtures/workspace_path_dep/source/crates/utils/Cargo.toml b/tools/hermes/tests/fixtures/workspace_path_dep/source/crates/utils/Cargo.toml new file mode 100644 index 0000000000..bd645df12e --- /dev/null +++ b/tools/hermes/tests/fixtures/workspace_path_dep/source/crates/utils/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "utils" +version = "0.1.0" +edition = "2021" diff --git a/tools/hermes/tests/fixtures/workspace_path_dep/source/crates/utils/src/lib.rs b/tools/hermes/tests/fixtures/workspace_path_dep/source/crates/utils/src/lib.rs new file mode 100644 index 0000000000..7e82c7f408 --- /dev/null +++ b/tools/hermes/tests/fixtures/workspace_path_dep/source/crates/utils/src/lib.rs @@ -0,0 +1 @@ +pub fn help() {} diff --git a/tools/hermes/tests/integration.rs b/tools/hermes/tests/integration.rs new file mode 100644 index 0000000000..93a50e91bd --- /dev/null +++ b/tools/hermes/tests/integration.rs @@ -0,0 +1,137 @@ +use std::{fs, path::Path}; + +use tempfile::tempdir; +use walkdir::WalkDir; + +datatest_stable::harness! { { test = run_integration_test, root = "tests/fixtures", pattern = "hermes.toml$" } } + +fn run_integration_test(path: &Path) -> datatest_stable::Result<()> { + // `path` is `tests/fixtures//hermes.toml` + let test_case_root = path.parent().unwrap(); + let test_name = test_case_root.file_name().unwrap().to_str().unwrap(); + let source_dir = test_case_root.join("source"); + + // Perform all work in a sandbox directory. This ensures that we don't + // get interference from `Cargo.toml` files in any parent directories. + let temp = tempdir()?; + let sandbox_root = temp.path().join(test_name); + copy_dir_contents(&source_dir, &sandbox_root)?; + + let mut cmd = assert_cmd::cargo_bin_cmd!("hermes"); + cmd.env("CARGO_TARGET_DIR", sandbox_root.join("target")) + .env_remove("RUSTFLAGS") + .env("HERMES_TEST_SHADOW_NAME", "hermes_shadow"); + + // Tests can specify the cwd to invoke from. + let cwd_file = test_case_root.join("cwd.txt"); + if cwd_file.exists() { + let rel_path = fs::read_to_string(cwd_file)?; + cmd.current_dir(sandbox_root.join(rel_path.trim())); + } else { + cmd.current_dir(&sandbox_root); + } + + // Tests can specify the arguments to pass to `hermes verify`. + let args_file = test_case_root.join("args.txt"); + if args_file.exists() { + let args_str = fs::read_to_string(args_file)?; + cmd.args(args_str.trim().split_whitespace()); + } else { + cmd.arg("verify"); + } + + let assert = cmd.assert(); + + // Tests can specify the expected exit status. + let expected_status_file = test_case_root.join("expected_status.txt"); + let assert = if expected_status_file.exists() { + let status = fs::read_to_string(&expected_status_file)?; + if status.trim() == "failure" { + assert.failure() + } else { + assert.success() + } + } else { + // Default to expecting success + assert.success() + }; + + // Tests can specify the expected stderr. + let expected_stderr_file = test_case_root.join("expected_stderr.txt"); + if expected_stderr_file.exists() { + let needle = fs::read_to_string(expected_stderr_file)?; + assert.stderr(predicates::str::contains(needle.trim())); + } + + // Tests can specify the expected shadow crate content. + let actual_shadow = sandbox_root.join("target/hermes_shadow"); + let expected_shadow = test_case_root.join("expected"); + + if expected_shadow.exists() { + assert_directories_match(&expected_shadow, &actual_shadow)?; + } else { + // If the test expects failure, we may not have a shadow crate to check. + // Only warn if we expected success but found no 'expected' dir. + if !expected_status_file.exists() + || fs::read_to_string(&expected_status_file)?.trim() != "failure" + { + eprintln!("WARNING: No 'expected' directory found for test case '{}'", test_name); + } + } + + Ok(()) +} + +/// Recursively checks that every file in 'expected' exists in 'actual' +/// and has identical content. +fn assert_directories_match(expected: &Path, actual: &Path) -> std::io::Result<()> { + for entry in WalkDir::new(expected) { + let entry = entry?; + if !entry.file_type().is_file() { + continue; + } + + let expected_path = entry.path(); + let relative_path = expected_path.strip_prefix(expected).unwrap(); + let actual_path = actual.join(relative_path); + + if !actual_path.exists() { + panic!( + "Missing File in Shadow Crate!\nFile: {:?}\nTest Case: {:?}", + relative_path, + expected.parent().unwrap().file_name() + ); + } + + let expected_content = fs::read_to_string(expected_path)?; + let actual_content = fs::read_to_string(&actual_path)?; + + // Normalize line endings to prevent Windows/Linux CI failures + let expected_norm = expected_content.replace("\r\n", "\n"); + let actual_norm = actual_content.replace("\r\n", "\n"); + + if expected_norm != actual_norm { + eprintln!("=== CONTENT MISMATCH: {:?} ===", relative_path); + eprintln!("--- Expected ---\n{}", expected_norm); + eprintln!("--- Actual ---\n{}", actual_norm); + panic!("Mismatch in {:?}", relative_path); + } + } + + Ok(()) +} + +/// Copies the *contents* of src into dst recursively. +fn copy_dir_contents(src: &Path, dst: &Path) -> std::io::Result<()> { + fs::create_dir_all(dst)?; + for entry in fs::read_dir(src)? { + let entry = entry?; + let ty = entry.file_type()?; + if ty.is_dir() { + copy_dir_contents(&entry.path(), &dst.join(entry.file_name()))?; + } else { + fs::copy(entry.path(), dst.join(entry.file_name()))?; + } + } + Ok(()) +} diff --git a/tools/vendor/assert_cmd/.cargo-checksum.json b/tools/vendor/assert_cmd/.cargo-checksum.json new file mode 100644 index 0000000000..f006afd335 --- /dev/null +++ b/tools/vendor/assert_cmd/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"9c9369ea0be85fd5056583a338822b6f02181329f7a34e56612f29178aa741b8","Cargo.lock":"2761c162e07d3da0caa53081acacd3754d36b5c4753dea804892784a1d873fd7","Cargo.toml":"c95500d51a82bce98d160f4e85a80459ff9981827386978c3a3a1190717c1cff","Cargo.toml.orig":"f63b410fbb799088b70a3a0a40bb148d9b8cff64d078fba5eb9fee45c4fc80d4","LICENSE-APACHE":"c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08","LICENSE-MIT":"6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6","README.md":"9871537ac9044736680a8f622eadcf409fc03b6ea4c102200cc9a7ca0c896105","build.rs":"be712cec2877871db11ef2691fa425b9fd95a70f860831cf33e77092c97d2ab8","examples/example_fixture.rs":"86804439aa40b1d4367d2fd98cf0f0e7201a91e16f06baefeb24c5f5388c3523","examples/failure.rs":"3d078a18fd6390724498397518952a154951d4c213c14ff96a35124a859a8694","src/assert.rs":"ec7d6aa52b9a97f29654c68dba26d805c02d826e825408fd209420fc09895f8c","src/bin/bin_fixture.rs":"189b69338b367deabb87b76bcdd632b3a6fab056923614a90de8056134c11a0e","src/cargo.rs":"b67f7e88bd7b81ef824b11e20ff5e8074d24f8fce882b0bb0283a4b4e7870475","src/cmd.rs":"e012226c0f67b5b0f1cb15b858c00a28487fa2237af2e5a09dbce82971ca1a84","src/color.rs":"ae88bba289d59883d9e3aabb02bacb2be1bd6c36c422695070135e53a381c083","src/lib.rs":"4ff85286658a98b9f5bd4a70d5360e67e2fafce7f964c605a8b3eae4cc7df2ef","src/macros.rs":"07e92a52872347d07ed3100bc5dc9d44503e46a5ed8cae6d23bc2600e1baa7fa","src/output.rs":"004d4faf1cffe771f08b4c378ff8cb39093df90e2ade0f0c0ff4afc7f4938105"},"package":"9c5bcfa8749ac45dd12cb11055aeeb6b27a3895560d60d71e3c23bf979e60514"} \ No newline at end of file diff --git a/tools/vendor/assert_cmd/.cargo_vcs_info.json b/tools/vendor/assert_cmd/.cargo_vcs_info.json new file mode 100644 index 0000000000..bc13bd31bc --- /dev/null +++ b/tools/vendor/assert_cmd/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "edda0b2c39ad7281659467f2f93de5668064a083" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/tools/vendor/assert_cmd/Cargo.lock b/tools/vendor/assert_cmd/Cargo.lock new file mode 100644 index 0000000000..9ad005cb49 --- /dev/null +++ b/tools/vendor/assert_cmd/Cargo.lock @@ -0,0 +1,371 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anstream" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd2405b3ac1faab2990b74d728624cd9fd115651fcecc7c2d8daf01376275ba" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "assert_cmd" +version = "2.1.2" +dependencies = [ + "anstream", + "anstyle", + "automod", + "bstr", + "escargot", + "libc", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + +[[package]] +name = "automod" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebb4bd301db2e2ca1f5be131c24eb8ebf2d9559bc3744419e93baf8ddea7e670" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "bstr" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fca0852af221f458706eb0725c03e4ed6c46af9ac98e6a689d5e634215d594dd" +dependencies = [ + "memchr", + "once_cell", + "regex-automata", + "serde", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + +[[package]] +name = "escargot" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11c3aea32bc97b500c9ca6a72b768a26e558264303d101d3409cf6d57a9ed0cf" +dependencies = [ + "log", + "serde", + "serde_json", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" + +[[package]] +name = "libc" +version = "0.2.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "predicates" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9" +dependencies = [ + "anstyle", + "difflib", + "itertools", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" + +[[package]] +name = "predicates-tree" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + +[[package]] +name = "serde" +version = "1.0.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.103", +] + +[[package]] +name = "serde_json" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termtree" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" + +[[package]] +name = "unicode-ident" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/tools/vendor/assert_cmd/Cargo.toml b/tools/vendor/assert_cmd/Cargo.toml new file mode 100644 index 0000000000..0b1e578d60 --- /dev/null +++ b/tools/vendor/assert_cmd/Cargo.toml @@ -0,0 +1,225 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.74" +name = "assert_cmd" +version = "2.1.2" +authors = [ + "Pascal Hertleif ", + "Ed Page ", +] +build = "build.rs" +include = [ + "build.rs", + "src/**/*", + "Cargo.toml", + "Cargo.lock", + "LICENSE*", + "README.md", + "examples/**/*", +] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Test CLI Applications." +homepage = "https://github.com/assert-rs/assert_cmd" +documentation = "http://docs.rs/assert_cmd/" +readme = "README.md" +keywords = [ + "cli", + "test", + "assert", + "command", + "duct", +] +categories = ["development-tools::testing"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/assert-rs/assert_cmd.git" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--generate-link-to-definition"] + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +search = "Unreleased" +replace = "{{version}}" +min = 1 + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +search = '\.\.\.HEAD' +replace = "...{{tag_name}}" +exactly = 1 + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +search = "ReleaseDate" +replace = "{{date}}" +min = 1 + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +search = "" +replace = """ + +## [Unreleased] - ReleaseDate +""" +exactly = 1 + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +search = "" +replace = """ + +[Unreleased]: https://github.com/assert-rs/assert_cmd/compare/{{tag_name}}...HEAD""" +exactly = 1 + +[features] +color = [ + "dep:anstream", + "predicates/color", +] +color-auto = ["color"] + +[lib] +name = "assert_cmd" +path = "src/lib.rs" + +[[bin]] +name = "bin_fixture" +path = "src/bin/bin_fixture.rs" + +[[example]] +name = "example_fixture" +path = "examples/example_fixture.rs" + +[[example]] +name = "failure" +path = "examples/failure.rs" + +[dependencies.anstream] +version = "0.6.7" +optional = true + +[dependencies.anstyle] +version = "1.0.0" + +[dependencies.bstr] +version = "1.0.1" + +[dependencies.predicates] +version = "3.0.1" +features = ["diff"] +default-features = false + +[dependencies.predicates-core] +version = "1.0.6" + +[dependencies.predicates-tree] +version = "1.0.1" + +[dependencies.wait-timeout] +version = "0.2.0" + +[dev-dependencies.automod] +version = "1.0.14" + +[dev-dependencies.escargot] +version = "0.5" + +[target."cfg(any())".dependencies.libc] +version = "0.2.137" + +[lints.clippy] +bool_assert_comparison = "allow" +branches_sharing_code = "allow" +checked_conversions = "warn" +collapsible_else_if = "allow" +create_dir = "warn" +dbg_macro = "warn" +debug_assert_with_mut_call = "warn" +doc_markdown = "warn" +empty_enum = "warn" +enum_glob_use = "warn" +expl_impl_clone_on_copy = "warn" +explicit_deref_methods = "warn" +explicit_into_iter_loop = "warn" +fallible_impl_from = "warn" +filter_map_next = "warn" +flat_map_option = "warn" +float_cmp_const = "warn" +fn_params_excessive_bools = "warn" +from_iter_instead_of_collect = "warn" +if_same_then_else = "allow" +implicit_clone = "warn" +imprecise_flops = "warn" +inconsistent_struct_constructor = "warn" +inefficient_to_string = "warn" +infinite_loop = "warn" +invalid_upcast_comparisons = "warn" +large_digit_groups = "warn" +large_stack_arrays = "warn" +large_types_passed_by_value = "warn" +let_and_return = "allow" +linkedlist = "warn" +lossy_float_literal = "warn" +macro_use_imports = "warn" +mem_forget = "warn" +mutex_integer = "warn" +needless_continue = "allow" +needless_for_each = "warn" +negative_feature_names = "warn" +path_buf_push_overwrite = "warn" +ptr_as_ptr = "warn" +rc_mutex = "warn" +redundant_feature_names = "warn" +ref_option_ref = "warn" +rest_pat_in_fully_bound_structs = "warn" +result_large_err = "allow" +same_functions_in_if_condition = "warn" +self_named_module_files = "warn" +semicolon_if_nothing_returned = "warn" +str_to_string = "warn" +string_add = "warn" +string_add_assign = "warn" +string_lit_as_bytes = "warn" +string_to_string = "warn" +todo = "warn" +trait_duplication_in_bounds = "warn" +uninlined_format_args = "warn" +verbose_file_reads = "warn" +wildcard_imports = "warn" +zero_sized_map_values = "warn" + +[lints.rust] +unnameable_types = "warn" +unreachable_pub = "warn" +unsafe_op_in_unsafe_fn = "warn" +unused_lifetimes = "warn" +unused_macro_rules = "warn" +unused_qualifications = "warn" + +[lints.rust.rust_2018_idioms] +level = "warn" +priority = -1 + +[profile.dev] +panic = "abort" + +[profile.release] +lto = true +codegen-units = 1 +panic = "abort" diff --git a/tools/vendor/assert_cmd/Cargo.toml.orig b/tools/vendor/assert_cmd/Cargo.toml.orig new file mode 100644 index 0000000000..c5a4d1a98c --- /dev/null +++ b/tools/vendor/assert_cmd/Cargo.toml.orig @@ -0,0 +1,151 @@ +[workspace] +resolver = "2" + +[workspace.package] +repository = "https://github.com/assert-rs/assert_cmd.git" +license = "MIT OR Apache-2.0" +edition = "2021" +rust-version = "1.74" # MSRV +include = [ + "build.rs", + "src/**/*", + "Cargo.toml", + "Cargo.lock", + "LICENSE*", + "README.md", + "examples/**/*" +] + +[workspace.lints.rust] +rust_2018_idioms = { level = "warn", priority = -1 } +unnameable_types = "warn" +unreachable_pub = "warn" +unsafe_op_in_unsafe_fn = "warn" +unused_lifetimes = "warn" +unused_macro_rules = "warn" +unused_qualifications = "warn" + +[workspace.lints.clippy] +bool_assert_comparison = "allow" +branches_sharing_code = "allow" +checked_conversions = "warn" +collapsible_else_if = "allow" +create_dir = "warn" +dbg_macro = "warn" +debug_assert_with_mut_call = "warn" +doc_markdown = "warn" +empty_enum = "warn" +enum_glob_use = "warn" +expl_impl_clone_on_copy = "warn" +explicit_deref_methods = "warn" +explicit_into_iter_loop = "warn" +fallible_impl_from = "warn" +filter_map_next = "warn" +flat_map_option = "warn" +float_cmp_const = "warn" +fn_params_excessive_bools = "warn" +from_iter_instead_of_collect = "warn" +if_same_then_else = "allow" +implicit_clone = "warn" +imprecise_flops = "warn" +inconsistent_struct_constructor = "warn" +inefficient_to_string = "warn" +infinite_loop = "warn" +invalid_upcast_comparisons = "warn" +large_digit_groups = "warn" +large_stack_arrays = "warn" +large_types_passed_by_value = "warn" +let_and_return = "allow" # sometimes good to name what you are returning +linkedlist = "warn" +lossy_float_literal = "warn" +macro_use_imports = "warn" +mem_forget = "warn" +mutex_integer = "warn" +needless_continue = "allow" +needless_for_each = "warn" +negative_feature_names = "warn" +path_buf_push_overwrite = "warn" +ptr_as_ptr = "warn" +rc_mutex = "warn" +redundant_feature_names = "warn" +ref_option_ref = "warn" +rest_pat_in_fully_bound_structs = "warn" +result_large_err = "allow" +same_functions_in_if_condition = "warn" +self_named_module_files = "warn" +semicolon_if_nothing_returned = "warn" +str_to_string = "warn" +string_add = "warn" +string_add_assign = "warn" +string_lit_as_bytes = "warn" +string_to_string = "warn" +todo = "warn" +trait_duplication_in_bounds = "warn" +uninlined_format_args = "warn" +verbose_file_reads = "warn" +wildcard_imports = "warn" +zero_sized_map_values = "warn" + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" +codegen-units = 1 +lto = true +# debug = "line-tables-only" # requires Cargo 1.71 + +[package] +name = "assert_cmd" +version = "2.1.2" +description = "Test CLI Applications." +authors = ["Pascal Hertleif ", "Ed Page "] +homepage = "https://github.com/assert-rs/assert_cmd" +documentation = "http://docs.rs/assert_cmd/" +readme = "README.md" +categories = ["development-tools::testing"] +keywords = ["cli", "test", "assert", "command", "duct"] +repository.workspace = true +license.workspace = true +edition.workspace = true +rust-version.workspace = true +include.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--generate-link-to-definition"] + +[package.metadata.release] +pre-release-replacements = [ + {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, + {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, + {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, + {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, + {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/assert-rs/assert_cmd/compare/{{tag_name}}...HEAD", exactly=1}, +] + +[features] +color = ["dep:anstream", "predicates/color"] +color-auto = ["color"] + +[[bin]] +name = "bin_fixture" + +[dependencies] +predicates = { version = "3.0.1", default-features = false, features = ["diff"] } +predicates-core = "1.0.6" +predicates-tree = "1.0.1" +wait-timeout = "0.2.0" +bstr = "1.0.1" +anstream = { version = "0.6.7", optional = true } +anstyle = "1.0.0" + +[target.'cfg(any())'.dependencies] +libc = "0.2.137" # HACK: bad minimal dep in wait-timeout + +[dev-dependencies] +escargot = "0.5" +automod = "1.0.14" + +[lints] +workspace = true diff --git a/tools/vendor/assert_cmd/LICENSE-APACHE b/tools/vendor/assert_cmd/LICENSE-APACHE new file mode 100644 index 0000000000..8f71f43fee --- /dev/null +++ b/tools/vendor/assert_cmd/LICENSE-APACHE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/tools/vendor/assert_cmd/LICENSE-MIT b/tools/vendor/assert_cmd/LICENSE-MIT new file mode 100644 index 0000000000..a2d01088b6 --- /dev/null +++ b/tools/vendor/assert_cmd/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) Individual contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/tools/vendor/assert_cmd/README.md b/tools/vendor/assert_cmd/README.md new file mode 100644 index 0000000000..53bbfda886 --- /dev/null +++ b/tools/vendor/assert_cmd/README.md @@ -0,0 +1,82 @@ +# assert_cmd + +> **Assert `process::Command`** - Easy command initialization and assertions. + +[![Documentation](https://img.shields.io/badge/docs-master-blue.svg)][Documentation] +![License](https://img.shields.io/crates/l/assert_cmd.svg) +[![Crates Status](https://img.shields.io/crates/v/assert_cmd.svg)][Crates.io] + +`assert_cmd` aims to simplify the process for doing integration testing of CLIs, including: +- Finding your crate's binary to test +- Assert on the result of your program's run. + +## Example + +Here's a trivial example: + +```rust,no_run +use assert_cmd::Command; + +let mut cmd = Command::cargo_bin("bin_fixture").unwrap(); +cmd.assert().success(); +``` + +See the [docs](http://docs.rs/assert_cmd) for more. + +## Relevant crates + +Other crates that might be useful in testing command line programs. +* [escargot][escargot] for more control over configuring the crate's binary. +* [duct][duct] for orchestrating multiple processes. + * or [commandspec] for easier writing of commands +* [rexpect][rexpect] for testing interactive programs. +* [`assert_fs`][assert_fs] for filesystem fixtures and assertions. + * or [tempfile][tempfile] for scratchpad directories. +* [dir-diff][dir-diff] for testing file side-effects. +* [cross][cross] for cross-platform testing. + +[escargot]: http://docs.rs/escargot +[rexpect]: https://crates.io/crates/rexpect +[dir-diff]: https://crates.io/crates/dir-diff +[tempfile]: https://crates.io/crates/tempfile +[duct]: https://crates.io/crates/duct +[assert_fs]: https://crates.io/crates/assert_fs +[commandspec]: https://crates.io/crates/commandspec +[cross]: https://github.com/cross-rs/cross + +## License + +Licensed under either of + +* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) +* MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. + +## Testimonials + +fitzgen +> assert_cmd is just such a pleasure to use every single time, I fall in love all over again +> +> bravo bravo WG-cli + +passcod +> Running commands and dealing with output can be complex in many many ways, so assert_cmd smoothing that is excellent, very much welcome, and improves ergonomics significantly. + +volks73 +> I have used [assert_cmd] in other projects and I am extremely pleased with it + +coreyja +> [assert_cmd] pretty much IS my testing strategy so far, though my app under test is pretty small. +> +> This library has made it really easy to add some test coverage to my project, even when I am just learning how to write Rust! + +## Contribution + +Unless you explicitly state otherwise, any contribution intentionally +submitted for inclusion in the work by you, as defined in the Apache-2.0 +license, shall be dual-licensed as above, without any additional terms or +conditions. + +[Crates.io]: https://crates.io/crates/assert_cmd +[Documentation]: https://docs.rs/assert_cmd diff --git a/tools/vendor/assert_cmd/build.rs b/tools/vendor/assert_cmd/build.rs new file mode 100644 index 0000000000..31f6ab5134 --- /dev/null +++ b/tools/vendor/assert_cmd/build.rs @@ -0,0 +1,17 @@ +use std::env; +use std::fs; +use std::io::Write; +use std::path; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + // env::ARCH doesn't include the full triplet, and as far as I know there isn't a cleaner way of getting the full triplet + // (see cargo.rs for the rest of this implementation) + let out = path::PathBuf::from(env::var_os("OUT_DIR").expect("run within cargo")) + .join("current_target.txt"); + let default_target = env::var("TARGET").expect("run as cargo build script"); + let mut file = fs::File::create(out).expect("can write to OUT_DIR"); + file.write_all(default_target.as_bytes()) + .expect("can write to OUT_DIR"); +} diff --git a/tools/vendor/assert_cmd/examples/example_fixture.rs b/tools/vendor/assert_cmd/examples/example_fixture.rs new file mode 100644 index 0000000000..218cb44479 --- /dev/null +++ b/tools/vendor/assert_cmd/examples/example_fixture.rs @@ -0,0 +1,35 @@ +#![allow(clippy::exit)] + +use std::env; +use std::error::Error; +use std::io; +use std::io::Write; +use std::process; + +fn run() -> Result<(), Box> { + if let Ok(text) = env::var("stdout") { + println!("{text}"); + } + if let Ok(text) = env::var("stderr") { + eprintln!("{text}"); + } + + let code = env::var("exit") + .ok() + .map(|v| v.parse::()) + .map(|r| r.map(Some)) + .unwrap_or(Ok(None))? + .unwrap_or(0); + process::exit(code); +} + +fn main() { + let code = match run() { + Ok(_) => 0, + Err(ref e) => { + write!(&mut io::stderr(), "{e}").expect("writing to stderr won't fail"); + 1 + } + }; + process::exit(code); +} diff --git a/tools/vendor/assert_cmd/examples/failure.rs b/tools/vendor/assert_cmd/examples/failure.rs new file mode 100644 index 0000000000..7c7393f8bb --- /dev/null +++ b/tools/vendor/assert_cmd/examples/failure.rs @@ -0,0 +1,11 @@ +#[allow(clippy::wildcard_imports)] // false positive +use assert_cmd::prelude::*; + +use std::process::Command; + +fn main() { + Command::new("ls") + .args(["non-existent"]) + .assert() + .code(&[3, 42] as &[i32]); +} diff --git a/tools/vendor/assert_cmd/src/assert.rs b/tools/vendor/assert_cmd/src/assert.rs new file mode 100644 index 0000000000..52b40a02a2 --- /dev/null +++ b/tools/vendor/assert_cmd/src/assert.rs @@ -0,0 +1,1191 @@ +//! [`std::process::Output`] assertions. + +use std::borrow::Cow; +use std::error::Error; +use std::fmt; +use std::process; +use std::str; + +#[cfg(feature = "color")] +use anstream::panic; +use predicates::str::PredicateStrExt; +use predicates_tree::CaseTreeExt; + +use crate::output::output_fmt; +use crate::output::DebugBytes; + +/// Assert the state of an [`Output`]. +/// +/// # Examples +/// +/// ```rust,no_run +/// use assert_cmd::prelude::*; +/// +/// use std::process::Command; +/// +/// let mut cmd = Command::cargo_bin("bin_fixture") +/// .unwrap(); +/// cmd.assert() +/// .success(); +/// ``` +/// +/// [`Output`]: std::process::Output +pub trait OutputAssertExt { + /// Wrap with an interface for that provides assertions on the [`Output`]. + /// + /// # Examples + /// + /// ```rust,no_run + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// + /// let mut cmd = Command::cargo_bin("bin_fixture") + /// .unwrap(); + /// cmd.assert() + /// .success(); + /// ``` + /// + /// [`Output`]: std::process::Output + #[must_use] + fn assert(self) -> Assert; +} + +impl OutputAssertExt for process::Output { + fn assert(self) -> Assert { + Assert::new(self) + } +} + +impl OutputAssertExt for &mut process::Command { + fn assert(self) -> Assert { + let output = match self.output() { + Ok(output) => output, + Err(err) => { + panic!("Failed to spawn {self:?}: {err}"); + } + }; + Assert::new(output).append_context("command", format!("{self:?}")) + } +} + +/// Assert the state of an [`Output`]. +/// +/// Create an `Assert` through the [`OutputAssertExt`] trait. +/// +/// # Examples +/// +/// ```rust,no_run +/// use assert_cmd::prelude::*; +/// +/// use std::process::Command; +/// +/// let mut cmd = Command::cargo_bin("bin_fixture") +/// .unwrap(); +/// cmd.assert() +/// .success(); +/// ``` +/// +/// [`Output`]: std::process::Output +pub struct Assert { + output: process::Output, + context: Vec<(&'static str, Box)>, +} + +impl Assert { + /// Create an `Assert` for a given [`Output`]. + /// + /// [`Output`]: std::process::Output + #[must_use] + pub fn new(output: process::Output) -> Self { + Self { + output, + context: vec![], + } + } + + fn into_error(self, reason: AssertReason) -> AssertError { + AssertError { + assert: self, + reason, + } + } + + /// Clarify failures with additional context. + /// + /// # Examples + /// + /// ```rust,no_run + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// + /// Command::cargo_bin("bin_fixture") + /// .unwrap() + /// .assert() + /// .append_context("main", "no args") + /// .success(); + /// ``` + #[must_use] + pub fn append_context(mut self, name: &'static str, context: D) -> Self + where + D: fmt::Display + Send + Sync + 'static, + { + self.context.push((name, Box::new(context))); + self + } + + /// Access the contained [`Output`]. + /// + /// [`Output`]: std::process::Output + pub fn get_output(&self) -> &process::Output { + &self.output + } + + /// Ensure the command succeeded. + /// + /// # Examples + /// + /// ```rust,no_run + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// + /// Command::cargo_bin("bin_fixture") + /// .unwrap() + /// .assert() + /// .success(); + /// ``` + #[track_caller] + pub fn success(self) -> Self { + self.try_success().unwrap_or_else(AssertError::panic) + } + + /// `try_` variant of [`Assert::success`]. + pub fn try_success(self) -> AssertResult { + if !self.output.status.success() { + let actual_code = self.output.status.code(); + return Err(self.into_error(AssertReason::UnexpectedFailure { actual_code })); + } + Ok(self) + } + + /// Ensure the command failed. + /// + /// # Examples + /// + /// ```rust,no_run + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// + /// Command::cargo_bin("bin_fixture") + /// .unwrap() + /// .env("exit", "1") + /// .assert() + /// .failure(); + /// ``` + #[track_caller] + pub fn failure(self) -> Self { + self.try_failure().unwrap_or_else(AssertError::panic) + } + + /// Variant of [`Assert::failure`] that returns an [`AssertResult`]. + pub fn try_failure(self) -> AssertResult { + if self.output.status.success() { + return Err(self.into_error(AssertReason::UnexpectedSuccess)); + } + Ok(self) + } + + /// Ensure the command aborted before returning a code. + #[track_caller] + pub fn interrupted(self) -> Self { + self.try_interrupted().unwrap_or_else(AssertError::panic) + } + + /// Variant of [`Assert::interrupted`] that returns an [`AssertResult`]. + pub fn try_interrupted(self) -> AssertResult { + if self.output.status.code().is_some() { + return Err(self.into_error(AssertReason::UnexpectedCompletion)); + } + Ok(self) + } + + /// Ensure the command returned the expected code. + /// + /// This uses [`IntoCodePredicate`] to provide short-hands for common cases. + /// + /// See [`predicates`] for more predicates. + /// + /// # Examples + /// + /// Accepting a predicate: + /// ```rust,no_run + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// use predicates::prelude::*; + /// + /// Command::cargo_bin("bin_fixture") + /// .unwrap() + /// .env("exit", "42") + /// .assert() + /// .code(predicate::eq(42)); + /// ``` + /// + /// Accepting an exit code: + /// ```rust,no_run + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// + /// Command::cargo_bin("bin_fixture") + /// .unwrap() + /// .env("exit", "42") + /// .assert() + /// .code(42); + /// ``` + /// + /// Accepting multiple exit codes: + /// ```rust,no_run + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// + /// Command::cargo_bin("bin_fixture") + /// .unwrap() + /// .env("exit", "42") + /// .assert() + /// .code(&[2, 42] as &[i32]); + /// ``` + /// + #[track_caller] + pub fn code(self, pred: I) -> Self + where + I: IntoCodePredicate

, + P: predicates_core::Predicate, + { + self.try_code(pred).unwrap_or_else(AssertError::panic) + } + + /// Variant of [`Assert::code`] that returns an [`AssertResult`]. + pub fn try_code(self, pred: I) -> AssertResult + where + I: IntoCodePredicate

, + P: predicates_core::Predicate, + { + self.code_impl(&pred.into_code()) + } + + fn code_impl(self, pred: &dyn predicates_core::Predicate) -> AssertResult { + let actual_code = if let Some(actual_code) = self.output.status.code() { + actual_code + } else { + return Err(self.into_error(AssertReason::CommandInterrupted)); + }; + if let Some(case) = pred.find_case(false, &actual_code) { + return Err(self.into_error(AssertReason::UnexpectedReturnCode { + case_tree: CaseTree(case.tree()), + })); + } + Ok(self) + } + + /// Ensure the command wrote the expected data to `stdout`. + /// + /// This uses [`IntoOutputPredicate`] to provide short-hands for common cases. + /// + /// See [`predicates`] for more predicates. + /// + /// # Examples + /// + /// Accepting a bytes predicate: + /// ```rust,no_run + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// use predicates::prelude::*; + /// + /// Command::cargo_bin("bin_fixture") + /// .unwrap() + /// .env("stdout", "hello") + /// .env("stderr", "world") + /// .assert() + /// .stdout(predicate::eq(b"hello\n" as &[u8])); + /// ``` + /// + /// Accepting a `str` predicate: + /// ```rust,no_run + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// use predicates::prelude::*; + /// + /// Command::cargo_bin("bin_fixture") + /// .unwrap() + /// .env("stdout", "hello") + /// .env("stderr", "world") + /// .assert() + /// .stdout(predicate::str::diff("hello\n")); + /// ``` + /// + /// Accepting bytes: + /// ```rust,no_run + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// + /// Command::cargo_bin("bin_fixture") + /// .unwrap() + /// .env("stdout", "hello") + /// .env("stderr", "world") + /// .assert() + /// .stdout(b"hello\n" as &[u8]); + /// ``` + /// + /// Accepting a `str`: + /// ```rust,no_run + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// + /// Command::cargo_bin("bin_fixture") + /// .unwrap() + /// .env("stdout", "hello") + /// .env("stderr", "world") + /// .assert() + /// .stdout("hello\n"); + /// ``` + /// + #[track_caller] + pub fn stdout(self, pred: I) -> Self + where + I: IntoOutputPredicate

, + P: predicates_core::Predicate<[u8]>, + { + self.try_stdout(pred).unwrap_or_else(AssertError::panic) + } + + /// Variant of [`Assert::stdout`] that returns an [`AssertResult`]. + pub fn try_stdout(self, pred: I) -> AssertResult + where + I: IntoOutputPredicate

, + P: predicates_core::Predicate<[u8]>, + { + self.stdout_impl(&pred.into_output()) + } + + fn stdout_impl(self, pred: &dyn predicates_core::Predicate<[u8]>) -> AssertResult { + { + let actual = &self.output.stdout; + if let Some(case) = pred.find_case(false, actual) { + return Err(self.into_error(AssertReason::UnexpectedStdout { + case_tree: CaseTree(case.tree()), + })); + } + } + Ok(self) + } + + /// Ensure the command wrote the expected data to `stderr`. + /// + /// This uses [`IntoOutputPredicate`] to provide short-hands for common cases. + /// + /// See [`predicates`] for more predicates. + /// + /// # Examples + /// + /// Accepting a bytes predicate: + /// ```rust,no_run + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// use predicates::prelude::*; + /// + /// Command::cargo_bin("bin_fixture") + /// .unwrap() + /// .env("stdout", "hello") + /// .env("stderr", "world") + /// .assert() + /// .stderr(predicate::eq(b"world\n" as &[u8])); + /// ``` + /// + /// Accepting a `str` predicate: + /// ```rust,no_run + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// use predicates::prelude::*; + /// + /// Command::cargo_bin("bin_fixture") + /// .unwrap() + /// .env("stdout", "hello") + /// .env("stderr", "world") + /// .assert() + /// .stderr(predicate::str::diff("world\n")); + /// ``` + /// + /// Accepting bytes: + /// ```rust,no_run + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// + /// Command::cargo_bin("bin_fixture") + /// .unwrap() + /// .env("stdout", "hello") + /// .env("stderr", "world") + /// .assert() + /// .stderr(b"world\n" as &[u8]); + /// ``` + /// + /// Accepting a `str`: + /// ```rust,no_run + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// + /// Command::cargo_bin("bin_fixture") + /// .unwrap() + /// .env("stdout", "hello") + /// .env("stderr", "world") + /// .assert() + /// .stderr("world\n"); + /// ``` + /// + #[track_caller] + pub fn stderr(self, pred: I) -> Self + where + I: IntoOutputPredicate

, + P: predicates_core::Predicate<[u8]>, + { + self.try_stderr(pred).unwrap_or_else(AssertError::panic) + } + + /// Variant of [`Assert::stderr`] that returns an [`AssertResult`]. + pub fn try_stderr(self, pred: I) -> AssertResult + where + I: IntoOutputPredicate

, + P: predicates_core::Predicate<[u8]>, + { + self.stderr_impl(&pred.into_output()) + } + + fn stderr_impl(self, pred: &dyn predicates_core::Predicate<[u8]>) -> AssertResult { + { + let actual = &self.output.stderr; + if let Some(case) = pred.find_case(false, actual) { + return Err(self.into_error(AssertReason::UnexpectedStderr { + case_tree: CaseTree(case.tree()), + })); + } + } + Ok(self) + } +} + +impl fmt::Display for Assert { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::color(); + for (name, context) in &self.context { + writeln!(f, "{:#}=`{:#}`", palette.key(name), palette.value(context))?; + } + output_fmt(&self.output, f) + } +} + +impl fmt::Debug for Assert { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Assert") + .field("output", &self.output) + .finish() + } +} + +/// Used by [`Assert::code`] to convert `Self` into the needed +/// [`predicates_core::Predicate`]. +/// +/// # Examples +/// +/// ```rust,no_run +/// use assert_cmd::prelude::*; +/// +/// use std::process::Command; +/// use predicates::prelude::*; +/// +/// Command::cargo_bin("bin_fixture") +/// .unwrap() +/// .env("exit", "42") +/// .assert() +/// .code(predicate::eq(42)); +/// +/// // which can be shortened to: +/// Command::cargo_bin("bin_fixture") +/// .unwrap() +/// .env("exit", "42") +/// .assert() +/// .code(42); +/// ``` +pub trait IntoCodePredicate

+where + P: predicates_core::Predicate, +{ + /// The type of the predicate being returned. + type Predicate; + + /// Convert to a predicate for testing a program's exit code. + fn into_code(self) -> P; +} + +impl

IntoCodePredicate

for P +where + P: predicates_core::Predicate, +{ + type Predicate = P; + + fn into_code(self) -> Self::Predicate { + self + } +} + +/// Keep `predicates` concrete Predicates out of our public API. +/// [`predicates_core::Predicate`] used by [`IntoCodePredicate`] for code. +/// +/// # Example +/// +/// ```rust,no_run +/// use assert_cmd::prelude::*; +/// +/// use std::process::Command; +/// +/// Command::cargo_bin("bin_fixture") +/// .unwrap() +/// .env("exit", "42") +/// .assert() +/// .code(42); +/// ``` +#[derive(Debug)] +pub struct EqCodePredicate(predicates::ord::EqPredicate); + +impl EqCodePredicate { + pub(crate) fn new(value: i32) -> Self { + let pred = predicates::ord::eq(value); + EqCodePredicate(pred) + } +} + +impl predicates_core::reflection::PredicateReflection for EqCodePredicate { + fn parameters<'a>( + &'a self, + ) -> Box> + 'a> { + self.0.parameters() + } + + /// Nested `Predicate`s of the current `Predicate`. + fn children<'a>( + &'a self, + ) -> Box> + 'a> { + self.0.children() + } +} + +impl predicates_core::Predicate for EqCodePredicate { + fn eval(&self, item: &i32) -> bool { + self.0.eval(item) + } + + fn find_case<'a>( + &'a self, + expected: bool, + variable: &i32, + ) -> Option> { + self.0.find_case(expected, variable) + } +} + +impl fmt::Display for EqCodePredicate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl IntoCodePredicate for i32 { + type Predicate = EqCodePredicate; + + fn into_code(self) -> Self::Predicate { + Self::Predicate::new(self) + } +} + +/// Keep `predicates` concrete Predicates out of our public API. +/// [`predicates_core::Predicate`] used by [`IntoCodePredicate`] for iterables of codes. +/// +/// # Example +/// +/// ```rust,no_run +/// use assert_cmd::prelude::*; +/// +/// use std::process::Command; +/// +/// Command::cargo_bin("bin_fixture") +/// .unwrap() +/// .env("exit", "42") +/// .assert() +/// .code(&[2, 42] as &[i32]); +/// ``` +#[derive(Debug)] +pub struct InCodePredicate(predicates::iter::InPredicate); + +impl InCodePredicate { + pub(crate) fn new>(value: I) -> Self { + let pred = predicates::iter::in_iter(value); + InCodePredicate(pred) + } +} + +impl predicates_core::reflection::PredicateReflection for InCodePredicate { + fn parameters<'a>( + &'a self, + ) -> Box> + 'a> { + self.0.parameters() + } + + /// Nested `Predicate`s of the current `Predicate`. + fn children<'a>( + &'a self, + ) -> Box> + 'a> { + self.0.children() + } +} + +impl predicates_core::Predicate for InCodePredicate { + fn eval(&self, item: &i32) -> bool { + self.0.eval(item) + } + + fn find_case<'a>( + &'a self, + expected: bool, + variable: &i32, + ) -> Option> { + self.0.find_case(expected, variable) + } +} + +impl fmt::Display for InCodePredicate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl IntoCodePredicate for Vec { + type Predicate = InCodePredicate; + + fn into_code(self) -> Self::Predicate { + Self::Predicate::new(self) + } +} + +impl IntoCodePredicate for &'static [i32] { + type Predicate = InCodePredicate; + + fn into_code(self) -> Self::Predicate { + Self::Predicate::new(self.iter().cloned()) + } +} + +/// Used by [`Assert::stdout`] and [`Assert::stderr`] to convert Self +/// into the needed [`predicates_core::Predicate<[u8]>`]. +/// +/// # Examples +/// +/// ```rust,no_run +/// use assert_cmd::prelude::*; +/// +/// use std::process::Command; +/// use predicates::prelude::*; +/// +/// Command::cargo_bin("bin_fixture") +/// .unwrap() +/// .env("stdout", "hello") +/// .env("stderr", "world") +/// .assert() +/// .stdout(predicate::str::diff("hello\n").from_utf8()); +/// +/// // which can be shortened to: +/// Command::cargo_bin("bin_fixture") +/// .unwrap() +/// .env("stdout", "hello") +/// .env("stderr", "world") +/// .assert() +/// .stdout("hello\n"); +/// ``` +pub trait IntoOutputPredicate

+where + P: predicates_core::Predicate<[u8]>, +{ + /// The type of the predicate being returned. + type Predicate; + + /// Convert to a predicate for testing a path. + fn into_output(self) -> P; +} + +impl

IntoOutputPredicate

for P +where + P: predicates_core::Predicate<[u8]>, +{ + type Predicate = P; + + fn into_output(self) -> Self::Predicate { + self + } +} + +/// Keep `predicates` concrete Predicates out of our public API. +/// [`predicates_core::Predicate`] used by [`IntoOutputPredicate`] for bytes. +/// +/// # Example +/// +/// ```rust,no_run +/// use assert_cmd::prelude::*; +/// +/// use std::process::Command; +/// +/// Command::cargo_bin("bin_fixture") +/// .unwrap() +/// .env("stdout", "hello") +/// .env("stderr", "world") +/// .assert() +/// .stderr(b"world\n" as &[u8]); +/// ``` +#[derive(Debug)] +pub struct BytesContentOutputPredicate(Cow<'static, [u8]>); + +impl BytesContentOutputPredicate { + pub(crate) fn new(value: &'static [u8]) -> Self { + BytesContentOutputPredicate(Cow::from(value)) + } + + pub(crate) fn from_vec(value: Vec) -> Self { + BytesContentOutputPredicate(Cow::from(value)) + } +} + +impl predicates_core::reflection::PredicateReflection for BytesContentOutputPredicate {} + +impl predicates_core::Predicate<[u8]> for BytesContentOutputPredicate { + fn eval(&self, item: &[u8]) -> bool { + self.0.as_ref() == item + } + + fn find_case( + &self, + expected: bool, + variable: &[u8], + ) -> Option> { + let actual = self.eval(variable); + if expected == actual { + Some(predicates_core::reflection::Case::new(Some(self), actual)) + } else { + None + } + } +} + +impl fmt::Display for BytesContentOutputPredicate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + predicates::ord::eq(self.0.as_ref()).fmt(f) + } +} + +impl IntoOutputPredicate for Vec { + type Predicate = BytesContentOutputPredicate; + + fn into_output(self) -> Self::Predicate { + Self::Predicate::from_vec(self) + } +} + +impl IntoOutputPredicate for &'static [u8] { + type Predicate = BytesContentOutputPredicate; + + fn into_output(self) -> Self::Predicate { + Self::Predicate::new(self) + } +} + +/// Keep `predicates` concrete Predicates out of our public API. +/// [`predicates_core::Predicate`] used by [`IntoOutputPredicate`] for [`str`]. +/// +/// # Example +/// +/// ```rust,no_run +/// use assert_cmd::prelude::*; +/// +/// use std::process::Command; +/// +/// Command::cargo_bin("bin_fixture") +/// .unwrap() +/// .env("stdout", "hello") +/// .env("stderr", "world") +/// .assert() +/// .stderr("world\n"); +/// ``` +/// +/// [`str`]: https://doc.rust-lang.org/std/primitive.str.html +#[derive(Debug, Clone)] +pub struct StrContentOutputPredicate( + predicates::str::Utf8Predicate, +); + +impl StrContentOutputPredicate { + pub(crate) fn from_str(value: &'static str) -> Self { + let pred = predicates::str::diff(value).from_utf8(); + StrContentOutputPredicate(pred) + } + + pub(crate) fn from_string(value: String) -> Self { + let pred = predicates::str::diff(value).from_utf8(); + StrContentOutputPredicate(pred) + } +} + +impl predicates_core::reflection::PredicateReflection for StrContentOutputPredicate { + fn parameters<'a>( + &'a self, + ) -> Box> + 'a> { + self.0.parameters() + } + + /// Nested `Predicate`s of the current `Predicate`. + fn children<'a>( + &'a self, + ) -> Box> + 'a> { + self.0.children() + } +} + +impl predicates_core::Predicate<[u8]> for StrContentOutputPredicate { + fn eval(&self, item: &[u8]) -> bool { + self.0.eval(item) + } + + fn find_case<'a>( + &'a self, + expected: bool, + variable: &[u8], + ) -> Option> { + self.0.find_case(expected, variable) + } +} + +impl fmt::Display for StrContentOutputPredicate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl IntoOutputPredicate for String { + type Predicate = StrContentOutputPredicate; + + fn into_output(self) -> Self::Predicate { + Self::Predicate::from_string(self) + } +} + +impl IntoOutputPredicate for &'static str { + type Predicate = StrContentOutputPredicate; + + fn into_output(self) -> Self::Predicate { + Self::Predicate::from_str(self) + } +} + +// Keep `predicates` concrete Predicates out of our public API. +/// [`predicates_core::Predicate`] used by [`IntoOutputPredicate`] for +/// [`Predicate`][predicates_core::Predicate]. +/// +/// # Example +/// +/// ```rust,no_run +/// use assert_cmd::prelude::*; +/// +/// use std::process::Command; +/// use predicates::prelude::*; +/// +/// Command::cargo_bin("bin_fixture") +/// .unwrap() +/// .env("stdout", "hello") +/// .env("stderr", "world") +/// .assert() +/// .stderr(predicate::str::diff("world\n")); +/// ``` +#[derive(Debug, Clone)] +pub struct StrOutputPredicate>( + predicates::str::Utf8Predicate

, +); + +impl

StrOutputPredicate

+where + P: predicates_core::Predicate, +{ + pub(crate) fn new(pred: P) -> Self { + let pred = pred.from_utf8(); + StrOutputPredicate(pred) + } +} + +impl

predicates_core::reflection::PredicateReflection for StrOutputPredicate

+where + P: predicates_core::Predicate, +{ + fn parameters<'a>( + &'a self, + ) -> Box> + 'a> { + self.0.parameters() + } + + /// Nested `Predicate`s of the current `Predicate`. + fn children<'a>( + &'a self, + ) -> Box> + 'a> { + self.0.children() + } +} + +impl

predicates_core::Predicate<[u8]> for StrOutputPredicate

+where + P: predicates_core::Predicate, +{ + fn eval(&self, item: &[u8]) -> bool { + self.0.eval(item) + } + + fn find_case<'a>( + &'a self, + expected: bool, + variable: &[u8], + ) -> Option> { + self.0.find_case(expected, variable) + } +} + +impl

fmt::Display for StrOutputPredicate

+where + P: predicates_core::Predicate, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl

IntoOutputPredicate> for P +where + P: predicates_core::Predicate, +{ + type Predicate = StrOutputPredicate

; + + fn into_output(self) -> Self::Predicate { + Self::Predicate::new(self) + } +} + +/// [`Assert`] represented as a [`Result`]. +/// +/// Produced by the `try_` variants the [`Assert`] methods. +/// +/// # Example +/// +/// ```rust +/// use assert_cmd::prelude::*; +/// +/// use std::process::Command; +/// +/// let result = Command::new("echo") +/// .assert() +/// .try_success(); +/// assert!(result.is_ok()); +/// ``` +/// +/// [`Result`]: std::result::Result +pub type AssertResult = Result; + +/// [`Assert`] error (see [`AssertResult`]). +#[derive(Debug)] +pub struct AssertError { + assert: Assert, + reason: AssertReason, +} + +#[derive(Debug)] +enum AssertReason { + UnexpectedFailure { actual_code: Option }, + UnexpectedSuccess, + UnexpectedCompletion, + CommandInterrupted, + UnexpectedReturnCode { case_tree: CaseTree }, + UnexpectedStdout { case_tree: CaseTree }, + UnexpectedStderr { case_tree: CaseTree }, +} + +impl AssertError { + #[track_caller] + fn panic(self) -> T { + panic!("{}", self) + } + + /// Returns the [`Assert`] wrapped into the [`Result`] produced by + /// the `try_` variants of the [`Assert`] methods. + /// + /// # Examples + /// + /// ```rust,no_run + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// use predicates::prelude::*; + /// + /// let result = Command::new("echo") + /// .assert(); + /// + /// match result.try_success() { + /// Ok(assert) => { + /// assert.stdout(predicate::eq(b"Success\n" as &[u8])); + /// } + /// Err(err) => { + /// err.assert().stdout(predicate::eq(b"Err but some specific output you might want to check\n" as &[u8])); + /// } + /// } + /// ``` + pub fn assert(self) -> Assert { + self.assert + } +} + +impl Error for AssertError {} + +impl fmt::Display for AssertError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.reason { + AssertReason::UnexpectedFailure { actual_code } => writeln!( + f, + "Unexpected failure.\ncode={}\nstderr=```{}```", + actual_code + .map(|actual_code| actual_code.to_string()) + .unwrap_or_else(|| "".to_owned()), + DebugBytes::new(&self.assert.output.stderr), + ), + AssertReason::UnexpectedSuccess => { + writeln!(f, "Unexpected success") + } + AssertReason::UnexpectedCompletion => { + writeln!(f, "Unexpected completion") + } + AssertReason::CommandInterrupted => { + writeln!(f, "Command interrupted") + } + AssertReason::UnexpectedReturnCode { case_tree } => { + writeln!(f, "Unexpected return code, failed {case_tree}") + } + AssertReason::UnexpectedStdout { case_tree } => { + writeln!(f, "Unexpected stdout, failed {case_tree}") + } + AssertReason::UnexpectedStderr { case_tree } => { + writeln!(f, "Unexpected stderr, failed {case_tree}") + } + }?; + write!(f, "{}", self.assert) + } +} + +struct CaseTree(predicates_tree::CaseTree); + +impl fmt::Display for CaseTree { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ::fmt(&self.0, f) + } +} + +// Work around `Debug` not being implemented for `predicates_tree::CaseTree`. +impl fmt::Debug for CaseTree { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ::fmt(&self.0, f) + } +} + +#[cfg(test)] +mod test { + use super::*; + + use predicates::prelude::*; + + // Since IntoCodePredicate exists solely for conversion, test it under that scenario to ensure + // it works as expected. + fn convert_code(pred: I) -> P + where + I: IntoCodePredicate

, + P: Predicate, + { + pred.into_code() + } + + #[test] + fn into_code_from_pred() { + let pred = convert_code(predicate::eq(10)); + assert!(pred.eval(&10)); + } + + #[test] + fn into_code_from_i32() { + let pred = convert_code(10); + assert!(pred.eval(&10)); + } + + #[test] + fn into_code_from_vec() { + let pred = convert_code(vec![3, 10]); + assert!(pred.eval(&10)); + } + + #[test] + fn into_code_from_array() { + let pred = convert_code(&[3, 10] as &[i32]); + assert!(pred.eval(&10)); + } + + // Since IntoOutputPredicate exists solely for conversion, test it under that scenario to ensure + // it works as expected. + fn convert_output(pred: I) -> P + where + I: IntoOutputPredicate

, + P: Predicate<[u8]>, + { + pred.into_output() + } + + #[test] + fn into_output_from_pred() { + let pred = convert_output(predicate::eq(b"Hello" as &[u8])); + assert!(pred.eval(b"Hello" as &[u8])); + } + + #[test] + fn into_output_from_bytes() { + let pred = convert_output(b"Hello" as &[u8]); + assert!(pred.eval(b"Hello" as &[u8])); + } + + #[test] + fn into_output_from_vec() { + let pred = convert_output(vec![b'H', b'e', b'l', b'l', b'o']); + assert!(pred.eval(b"Hello" as &[u8])); + } + + #[test] + fn into_output_from_str() { + let pred = convert_output("Hello"); + assert!(pred.eval(b"Hello" as &[u8])); + } +} diff --git a/tools/vendor/assert_cmd/src/bin/bin_fixture.rs b/tools/vendor/assert_cmd/src/bin/bin_fixture.rs new file mode 100644 index 0000000000..0c30c7db54 --- /dev/null +++ b/tools/vendor/assert_cmd/src/bin/bin_fixture.rs @@ -0,0 +1,39 @@ +#![allow(clippy::exit)] + +use std::env; +use std::error::Error; +use std::io; +use std::io::Write; +use std::process; + +fn run() -> Result<(), Box> { + if let Ok(text) = env::var("stdout") { + println!("{text}"); + } + if let Ok(text) = env::var("stderr") { + eprintln!("{text}"); + } + + if let Some(timeout) = env::var("sleep").ok().and_then(|s| s.parse().ok()) { + std::thread::sleep(std::time::Duration::from_secs(timeout)); + } + + let code = env::var("exit") + .ok() + .map(|v| v.parse::()) + .map(|r| r.map(Some)) + .unwrap_or(Ok(None))? + .unwrap_or(0); + process::exit(code); +} + +fn main() { + let code = match run() { + Ok(_) => 0, + Err(ref e) => { + write!(&mut io::stderr(), "{e}").expect("writing to stderr won't fail"); + 1 + } + }; + process::exit(code); +} diff --git a/tools/vendor/assert_cmd/src/cargo.rs b/tools/vendor/assert_cmd/src/cargo.rs new file mode 100644 index 0000000000..0e6433471f --- /dev/null +++ b/tools/vendor/assert_cmd/src/cargo.rs @@ -0,0 +1,254 @@ +//! Simplify running `bin`s in a Cargo project. +//! +//! [`CommandCargoExt`] is an extension trait for [`Command`] to easily launch a crate's +//! binaries. +//! +//! # Examples +//! +//! Simple case: +//! +//! ```rust,no_run +//! use assert_cmd::prelude::*; +//! use assert_cmd::pkg_name; +//! +//! use std::process::Command; +//! +//! let mut cmd = Command::cargo_bin(pkg_name!()) +//! .unwrap(); +//! let output = cmd.unwrap(); +//! ``` +//! +//! # Limitations +//! +//! - Only works within the context of integration tests. See [`escargot`] for a more +//! flexible API. +//! - Only reuses your existing feature flags, targets, or build mode. +//! - Only works with cargo binaries (`cargo test` ensures they are built). +//! +//! If you run into these limitations, we recommend trying out [`escargot`]: +//! +//! ```rust,no_run +//! use assert_cmd::prelude::*; +//! +//! use std::process::Command; +//! +//! let bin_under_test = escargot::CargoBuild::new() +//! .bin("bin_fixture") +//! .current_release() +//! .current_target() +//! .run() +//! .unwrap(); +//! let mut cmd = bin_under_test.command(); +//! let output = cmd.unwrap(); +//! println!("{:?}", output); +//! ``` +//! +//! Notes: +//! - There is a [noticeable per-call overhead][cargo-overhead] for `CargoBuild`. We recommend +//! caching the binary location (`.path()` instead of `.command()`) with [`lazy_static`]. +//! - `.current_target()` improves platform coverage at the cost of [slower test runs if you don't +//! explicitly pass `--target ` on the command line][first-call]. +//! +//! [`lazy_static`]: https://crates.io/crates/lazy_static +//! [`Command`]: std::process::Command +//! [`escargot`]: https://crates.io/crates/escargot +//! [cargo-overhead]: https://github.com/assert-rs/assert_cmd/issues/6 +//! [first-call]: https://github.com/assert-rs/assert_cmd/issues/57 + +use std::env; +use std::error::Error; +use std::fmt; +use std::path; +use std::process; + +#[doc(inline)] +pub use crate::cargo_bin; +#[doc(inline)] +pub use crate::cargo_bin_cmd; + +/// Create a [`Command`] for a `bin` in the Cargo project. +/// +/// `CommandCargoExt` is an extension trait for [`Command`][std::process::Command] to easily launch a crate's +/// binaries. +/// +/// See the [`cargo` module documentation][super::cargo] for caveats and workarounds. +/// +/// # Examples +/// +/// ```rust,no_run +/// use assert_cmd::prelude::*; +/// use assert_cmd::pkg_name; +/// +/// use std::process::Command; +/// +/// let mut cmd = Command::cargo_bin(pkg_name!()) +/// .unwrap(); +/// let output = cmd.unwrap(); +/// println!("{:?}", output); +/// ``` +/// +/// [`Command`]: std::process::Command +pub trait CommandCargoExt +where + Self: Sized, +{ + /// Create a [`Command`] to run a specific binary of the current crate. + /// + /// See the [`cargo` module documentation][crate::cargo] for caveats and workarounds. + /// + /// The [`Command`] created with this method may run the binary through a runner, as configured + /// in the `CARGO_TARGET__RUNNER` environment variable. This is useful for running + /// binaries that can't be launched directly, such as cross-compiled binaries. When using + /// this method with [cross](https://github.com/cross-rs/cross), no extra configuration is + /// needed. + /// + /// **NOTE:** Prefer [`cargo_bin!`] as this makes assumptions about cargo + /// + /// # Examples + /// + /// ```rust,no_run + /// use assert_cmd::prelude::*; + /// use assert_cmd::pkg_name; + /// + /// use std::process::Command; + /// + /// let mut cmd = Command::cargo_bin(pkg_name!()) + /// .unwrap(); + /// let output = cmd.unwrap(); + /// println!("{:?}", output); + /// ``` + /// + /// ```rust,no_run + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// + /// let mut cmd = Command::cargo_bin("bin_fixture") + /// .unwrap(); + /// let output = cmd.unwrap(); + /// println!("{:?}", output); + /// ``` + /// + /// [`Command`]: std::process::Command + #[deprecated( + since = "2.1.0", + note = "incompatible with a custom cargo build-dir, see instead `cargo::cargo_bin!`" + )] + fn cargo_bin>(name: S) -> Result; +} + +impl CommandCargoExt for crate::cmd::Command { + fn cargo_bin>(name: S) -> Result { + #[allow(deprecated)] + crate::cmd::Command::cargo_bin(name) + } +} + +impl CommandCargoExt for process::Command { + fn cargo_bin>(name: S) -> Result { + cargo_bin_cmd(name) + } +} + +pub(crate) fn cargo_bin_cmd>(name: S) -> Result { + #[allow(deprecated)] + let path = cargo_bin(name); + if path.is_file() { + if let Some(runner) = cargo_runner() { + let mut cmd = process::Command::new(&runner[0]); + cmd.args(&runner[1..]).arg(path); + Ok(cmd) + } else { + Ok(process::Command::new(path)) + } + } else { + Err(CargoError::with_cause(NotFoundError { path })) + } +} + +pub(crate) fn cargo_runner() -> Option> { + let runner_env = format!( + "CARGO_TARGET_{}_RUNNER", + CURRENT_TARGET.replace('-', "_").to_uppercase() + ); + let runner = env::var(runner_env).ok()?; + Some(runner.split(' ').map(str::to_string).collect()) +} + +/// Error when finding crate binary. +#[derive(Debug)] +pub struct CargoError { + cause: Option>, +} + +impl CargoError { + /// Wrap the underlying error for passing up. + pub fn with_cause(cause: E) -> Self + where + E: Error + Send + Sync + 'static, + { + let cause = Box::new(cause); + Self { cause: Some(cause) } + } +} + +impl Error for CargoError {} + +impl fmt::Display for CargoError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(ref cause) = self.cause { + writeln!(f, "Cause: {cause}")?; + } + Ok(()) + } +} + +/// Error when finding crate binary. +#[derive(Debug)] +struct NotFoundError { + path: path::PathBuf, +} + +impl Error for NotFoundError {} + +impl fmt::Display for NotFoundError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "Cargo command not found: {}", self.path.display()) + } +} + +// Adapted from +// https://github.com/rust-lang/cargo/blob/485670b3983b52289a2f353d589c57fae2f60f82/tests/testsuite/support/mod.rs#L507 +fn target_dir() -> path::PathBuf { + env::current_exe() + .ok() + .map(|mut path| { + path.pop(); + if path.ends_with("deps") { + path.pop(); + } + path + }) + .expect("this should only be used where a `current_exe` can be set") +} + +/// Look up the path to a cargo-built binary within an integration test. +/// +/// **NOTE:** Prefer [`cargo_bin!`] as this makes assumptions about cargo +#[deprecated( + since = "2.1.0", + note = "incompatible with a custom cargo build-dir, see instead `cargo::cargo_bin!`" +)] +pub fn cargo_bin>(name: S) -> path::PathBuf { + cargo_bin_str(name.as_ref()) +} + +fn cargo_bin_str(name: &str) -> path::PathBuf { + let env_var = format!("CARGO_BIN_EXE_{name}"); + env::var_os(env_var) + .map(|p| p.into()) + .unwrap_or_else(|| target_dir().join(format!("{}{}", name, env::consts::EXE_SUFFIX))) +} + +/// The current process' target triplet. +const CURRENT_TARGET: &str = include_str!(concat!(env!("OUT_DIR"), "/current_target.txt")); diff --git a/tools/vendor/assert_cmd/src/cmd.rs b/tools/vendor/assert_cmd/src/cmd.rs new file mode 100644 index 0000000000..1d50a79797 --- /dev/null +++ b/tools/vendor/assert_cmd/src/cmd.rs @@ -0,0 +1,669 @@ +//! [`std::process::Command`] customized for testing. + +use std::ffi; +use std::io; +use std::io::{Read, Write}; +use std::ops::Deref; +use std::path; +use std::process; + +use crate::assert::Assert; +use crate::assert::OutputAssertExt; +use crate::output::DebugBuffer; +use crate::output::DebugBytes; +use crate::output::OutputError; +use crate::output::OutputOkExt; +use crate::output::OutputResult; + +/// [`std::process::Command`] customized for testing. +#[derive(Debug)] +pub struct Command { + cmd: process::Command, + stdin: Option, + timeout: Option, +} + +impl Command { + /// Constructs a new `Command` from a `std` `Command`. + pub fn from_std(cmd: process::Command) -> Self { + Self { + cmd, + stdin: None, + timeout: None, + } + } + + /// Create a `Command` to run a specific binary of the current crate. + /// + /// See the [`cargo` module documentation][crate::cargo] for caveats and workarounds. + /// + /// **NOTE:** Prefer [`cargo_bin!`][crate::cargo::cargo_bin!] as this makes assumptions about cargo + /// + /// # Examples + /// + /// ```rust,no_run + /// use assert_cmd::Command; + /// use assert_cmd::pkg_name; + /// + /// let mut cmd = Command::cargo_bin(pkg_name!()) + /// .unwrap(); + /// let output = cmd.unwrap(); + /// println!("{:?}", output); + /// ``` + /// + /// ```rust,no_run + /// use assert_cmd::Command; + /// + /// let mut cmd = Command::cargo_bin("bin_fixture") + /// .unwrap(); + /// let output = cmd.unwrap(); + /// println!("{:?}", output); + /// ``` + /// + #[deprecated( + since = "2.1.0", + note = "incompatible with a custom cargo build-dir, see instead `cargo::cargo_bin_cmd!`" + )] + pub fn cargo_bin>(name: S) -> Result { + let cmd = crate::cargo::cargo_bin_cmd(name)?; + Ok(Self::from_std(cmd)) + } + + /// Write `buffer` to `stdin` when the `Command` is run. + /// + /// # Examples + /// + /// ```rust + /// use assert_cmd::Command; + /// + /// let mut cmd = Command::new("cat") + /// .arg("-et") + /// .write_stdin("42") + /// .assert() + /// .stdout("42"); + /// ``` + pub fn write_stdin(&mut self, buffer: S) -> &mut Self + where + S: Into>, + { + self.stdin = Some(bstr::BString::from(buffer.into())); + self + } + + /// Error out if a timeout is reached + /// + /// ```rust,no_run + /// use assert_cmd::Command; + /// + /// let assert = Command::cargo_bin("bin_fixture") + /// .unwrap() + /// .timeout(std::time::Duration::from_secs(1)) + /// .env("sleep", "100") + /// .assert(); + /// assert.failure(); + /// ``` + pub fn timeout(&mut self, timeout: std::time::Duration) -> &mut Self { + self.timeout = Some(timeout); + self + } + + /// Write `path`s content to `stdin` when the `Command` is run. + /// + /// Paths are relative to the [`env::current_dir`][env_current_dir] and not + /// [`Command::current_dir`][Command_current_dir]. + /// + /// [env_current_dir]: std::env::current_dir() + /// [Command_current_dir]: std::process::Command::current_dir() + pub fn pipe_stdin

(&mut self, file: P) -> io::Result<&mut Self> + where + P: AsRef, + { + let buffer = std::fs::read(file)?; + Ok(self.write_stdin(buffer)) + } + + /// Run a `Command`, returning an [`OutputResult`]. + /// + /// # Examples + /// + /// ```rust + /// use assert_cmd::Command; + /// + /// let result = Command::new("echo") + /// .args(&["42"]) + /// .ok(); + /// assert!(result.is_ok()); + /// ``` + /// + pub fn ok(&mut self) -> OutputResult { + OutputOkExt::ok(self) + } + + /// Run a `Command`, unwrapping the [`OutputResult`]. + /// + /// # Examples + /// + /// ```rust + /// use assert_cmd::Command; + /// + /// let output = Command::new("echo") + /// .args(&["42"]) + /// .unwrap(); + /// ``` + /// + pub fn unwrap(&mut self) -> process::Output { + OutputOkExt::unwrap(self) + } + + /// Run a `Command`, unwrapping the error in the [`OutputResult`]. + /// + /// # Examples + /// + /// ```rust,no_run + /// use assert_cmd::Command; + /// + /// let err = Command::new("a-command") + /// .args(&["--will-fail"]) + /// .unwrap_err(); + /// ``` + /// + /// [Output]: std::process::Output + pub fn unwrap_err(&mut self) -> OutputError { + OutputOkExt::unwrap_err(self) + } + + /// Run a `Command` and make assertions on the [`Output`]. + /// + /// # Examples + /// + /// ```rust,no_run + /// use assert_cmd::Command; + /// + /// let mut cmd = Command::cargo_bin("bin_fixture") + /// .unwrap() + /// .assert() + /// .success(); + /// ``` + /// + /// [`Output`]: std::process::Output + #[must_use] + pub fn assert(&mut self) -> Assert { + OutputAssertExt::assert(self) + } +} + +/// Mirror [`std::process::Command`]'s API +impl Command { + /// Constructs a new `Command` for launching the program at + /// path `program`, with the following default configuration: + /// + /// * No arguments to the program + /// * Inherit the current process's environment + /// * Inherit the current process's working directory + /// * Inherit stdin/stdout/stderr for `spawn` or `status`, but create pipes for `output` + /// + /// Builder methods are provided to change these defaults and + /// otherwise configure the process. + /// + /// If `program` is not an absolute path, the `PATH` will be searched in + /// an OS-defined way. + /// + /// The search path to be used may be controlled by setting the + /// `PATH` environment variable on the Command, + /// but this has some implementation limitations on Windows + /// (see issue #37519). + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use assert_cmd::Command; + /// + /// Command::new("sh").unwrap(); + /// ``` + pub fn new>(program: S) -> Self { + let cmd = process::Command::new(program); + Self::from_std(cmd) + } + + /// Adds an argument to pass to the program. + /// + /// Only one argument can be passed per use. So instead of: + /// + /// ```no_run + /// # assert_cmd::Command::new("sh") + /// .arg("-C /path/to/repo") + /// # ; + /// ``` + /// + /// usage would be: + /// + /// ```no_run + /// # assert_cmd::Command::new("sh") + /// .arg("-C") + /// .arg("/path/to/repo") + /// # ; + /// ``` + /// + /// To pass multiple arguments see [`args`]. + /// + /// [`args`]: Command::args() + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use assert_cmd::Command; + /// + /// Command::new("ls") + /// .arg("-l") + /// .arg("-a") + /// .unwrap(); + /// ``` + pub fn arg>(&mut self, arg: S) -> &mut Self { + self.cmd.arg(arg); + self + } + + /// Adds multiple arguments to pass to the program. + /// + /// To pass a single argument see [`arg`]. + /// + /// [`arg`]: Command::arg() + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use assert_cmd::Command; + /// + /// Command::new("ls") + /// .args(&["-l", "-a"]) + /// .unwrap(); + /// ``` + pub fn args(&mut self, args: I) -> &mut Self + where + I: IntoIterator, + S: AsRef, + { + self.cmd.args(args); + self + } + + /// Inserts or updates an environment variable mapping. + /// + /// Note that environment variable names are case-insensitive (but case-preserving) on Windows, + /// and case-sensitive on all other platforms. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use assert_cmd::Command; + /// + /// Command::new("ls") + /// .env("PATH", "/bin") + /// .unwrap_err(); + /// ``` + pub fn env(&mut self, key: K, val: V) -> &mut Self + where + K: AsRef, + V: AsRef, + { + self.cmd.env(key, val); + self + } + + /// Adds or updates multiple environment variable mappings. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use assert_cmd::Command; + /// use std::process::Stdio; + /// use std::env; + /// use std::collections::HashMap; + /// + /// let filtered_env : HashMap = + /// env::vars().filter(|&(ref k, _)| + /// k == "TERM" || k == "TZ" || k == "LANG" || k == "PATH" + /// ).collect(); + /// + /// Command::new("printenv") + /// .env_clear() + /// .envs(&filtered_env) + /// .unwrap(); + /// ``` + pub fn envs(&mut self, vars: I) -> &mut Self + where + I: IntoIterator, + K: AsRef, + V: AsRef, + { + self.cmd.envs(vars); + self + } + + /// Removes an environment variable mapping. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use assert_cmd::Command; + /// + /// Command::new("ls") + /// .env_remove("PATH") + /// .unwrap_err(); + /// ``` + pub fn env_remove>(&mut self, key: K) -> &mut Self { + self.cmd.env_remove(key); + self + } + + /// Clears the entire environment map for the child process. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use assert_cmd::Command; + /// + /// Command::new("ls") + /// .env_clear() + /// .unwrap_err(); + /// ``` + pub fn env_clear(&mut self) -> &mut Self { + self.cmd.env_clear(); + self + } + + /// Sets the working directory for the child process. + /// + /// # Platform-specific behavior + /// + /// If the program path is relative (e.g., `"./script.sh"`), it's ambiguous + /// whether it should be interpreted relative to the parent's working + /// directory or relative to `current_dir`. The behavior in this case is + /// platform specific and unstable, and it's recommended to use + /// [`canonicalize`] to get an absolute program path instead. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use assert_cmd::Command; + /// + /// Command::new("ls") + /// .current_dir("/bin") + /// .unwrap(); + /// ``` + /// + /// [`canonicalize`]: std::fs::canonicalize() + pub fn current_dir>(&mut self, dir: P) -> &mut Self { + self.cmd.current_dir(dir); + self + } + + /// Executes the `Command` as a child process, waiting for it to finish and collecting all of its + /// output. + /// + /// By default, stdout and stderr are captured (and used to provide the resulting output). + /// Stdin is not inherited from the parent and any attempt by the child process to read from + /// the stdin stream will result in the stream immediately closing. + /// + /// # Examples + /// + /// ```should_panic + /// use assert_cmd::Command; + /// use std::io::{self, Write}; + /// let output = Command::new("/bin/cat") + /// .arg("file.txt") + /// .output() + /// .expect("failed to execute process"); + /// + /// println!("status: {}", output.status); + /// io::stdout().write_all(&output.stdout).unwrap(); + /// io::stderr().write_all(&output.stderr).unwrap(); + /// + /// assert!(output.status.success()); + /// ``` + pub fn output(&mut self) -> io::Result { + let spawn = self.spawn()?; + Self::wait_with_input_output(spawn, self.stdin.as_deref().cloned(), self.timeout) + } + + /// If `input`, write it to `child`'s stdin while also reading `child`'s + /// stdout and stderr, then wait on `child` and return its status and output. + /// + /// This was lifted from `std::process::Child::wait_with_output` and modified + /// to also write to stdin. + fn wait_with_input_output( + mut child: process::Child, + input: Option>, + timeout: Option, + ) -> io::Result { + #![allow(clippy::unwrap_used)] // changes behavior in some tests + + fn read(mut input: R) -> std::thread::JoinHandle>> + where + R: Read + Send + 'static, + { + std::thread::spawn(move || { + let mut ret = Vec::new(); + input.read_to_end(&mut ret).map(|_| ret) + }) + } + + let stdin = input.and_then(|i| { + child + .stdin + .take() + .map(|mut stdin| std::thread::spawn(move || stdin.write_all(&i))) + }); + let stdout = child.stdout.take().map(read); + let stderr = child.stderr.take().map(read); + + // Finish writing stdin before waiting, because waiting drops stdin. + stdin.and_then(|t| t.join().unwrap().ok()); + let status = if let Some(timeout) = timeout { + wait_timeout::ChildExt::wait_timeout(&mut child, timeout) + .transpose() + .unwrap_or_else(|| { + let _ = child.kill(); + child.wait() + }) + } else { + child.wait() + }?; + + let stdout = stdout + .and_then(|t| t.join().unwrap().ok()) + .unwrap_or_default(); + let stderr = stderr + .and_then(|t| t.join().unwrap().ok()) + .unwrap_or_default(); + + Ok(process::Output { + status, + stdout, + stderr, + }) + } + + fn spawn(&mut self) -> io::Result { + // stdout/stderr should only be piped for `output` according to `process::Command::new`. + self.cmd.stdin(process::Stdio::piped()); + self.cmd.stdout(process::Stdio::piped()); + self.cmd.stderr(process::Stdio::piped()); + + self.cmd.spawn() + } + + /// Returns the path to the program that was given to [`Command::new`]. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rust + /// use assert_cmd::Command; + /// + /// let cmd = Command::new("echo"); + /// assert_eq!(cmd.get_program(), "echo"); + /// ``` + pub fn get_program(&self) -> &ffi::OsStr { + self.cmd.get_program() + } + + /// Returns an iterator of the arguments that will be passed to the program. + /// + /// This does not include the path to the program as the first argument; + /// it only includes the arguments specified with [`Command::arg`] and + /// [`Command::args`]. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rust + /// use std::ffi::OsStr; + /// use assert_cmd::Command; + /// + /// let mut cmd = Command::new("echo"); + /// cmd.arg("first").arg("second"); + /// let args: Vec<&OsStr> = cmd.get_args().collect(); + /// assert_eq!(args, &["first", "second"]); + /// ``` + pub fn get_args(&self) -> process::CommandArgs<'_> { + self.cmd.get_args() + } + + /// Returns an iterator of the environment variables explicitly set for the child process. + /// + /// Environment variables explicitly set using [`Command::env`], [`Command::envs`], and + /// [`Command::env_remove`] can be retrieved with this method. + /// + /// Note that this output does not include environment variables inherited from the parent + /// process. + /// + /// Each element is a tuple key/value pair `(&OsStr, Option<&OsStr>)`. A [`None`] value + /// indicates its key was explicitly removed via [`Command::env_remove`]. The associated key for + /// the [`None`] value will no longer inherit from its parent process. + /// + /// An empty iterator can indicate that no explicit mappings were added or that + /// [`Command::env_clear`] was called. After calling [`Command::env_clear`], the child process + /// will not inherit any environment variables from its parent process. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rust + /// use std::ffi::OsStr; + /// use assert_cmd::Command; + /// + /// let mut cmd = Command::new("ls"); + /// cmd.env("TERM", "dumb").env_remove("TZ"); + /// let envs: Vec<(&OsStr, Option<&OsStr>)> = cmd.get_envs().collect(); + /// assert_eq!(envs, &[ + /// (OsStr::new("TERM"), Some(OsStr::new("dumb"))), + /// (OsStr::new("TZ"), None) + /// ]); + /// ``` + pub fn get_envs(&self) -> process::CommandEnvs<'_> { + self.cmd.get_envs() + } + + /// Returns the working directory for the child process. + /// + /// This returns [`None`] if the working directory will not be changed. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```rust + /// use std::path::Path; + /// use assert_cmd::Command; + /// + /// let mut cmd = Command::new("ls"); + /// assert_eq!(cmd.get_current_dir(), None); + /// cmd.current_dir("/bin"); + /// assert_eq!(cmd.get_current_dir(), Some(Path::new("/bin"))); + /// ``` + pub fn get_current_dir(&self) -> Option<&path::Path> { + self.cmd.get_current_dir() + } +} + +impl From for Command { + fn from(cmd: process::Command) -> Self { + Command::from_std(cmd) + } +} + +impl OutputOkExt for &mut Command { + fn ok(self) -> OutputResult { + let output = self.output().map_err(OutputError::with_cause)?; + if output.status.success() { + Ok(output) + } else { + let error = OutputError::new(output).set_cmd(format!("{:?}", self.cmd)); + let error = if let Some(stdin) = self.stdin.as_ref() { + error.set_stdin(stdin.deref().clone()) + } else { + error + }; + Err(error) + } + } + + fn unwrap_err(self) -> OutputError { + match self.ok() { + Ok(output) => { + if let Some(stdin) = self.stdin.as_ref() { + panic!( + "Completed successfully:\ncommand=`{:?}`\nstdin=```{}```\nstdout=```{}```", + self.cmd, + DebugBytes::new(stdin), + DebugBytes::new(&output.stdout) + ) + } else { + panic!( + "Completed successfully:\ncommand=`{:?}`\nstdout=```{}```", + self.cmd, + DebugBytes::new(&output.stdout) + ) + } + } + Err(err) => err, + } + } +} + +impl OutputAssertExt for &mut Command { + fn assert(self) -> Assert { + let output = match self.output() { + Ok(output) => output, + Err(err) => { + panic!("Failed to spawn {self:?}: {err}"); + } + }; + let assert = Assert::new(output).append_context("command", format!("{:?}", self.cmd)); + if let Some(stdin) = self.stdin.as_ref() { + assert.append_context("stdin", DebugBuffer::new(stdin.deref().clone())) + } else { + assert + } + } +} diff --git a/tools/vendor/assert_cmd/src/color.rs b/tools/vendor/assert_cmd/src/color.rs new file mode 100644 index 0000000000..ee30e6f5d5 --- /dev/null +++ b/tools/vendor/assert_cmd/src/color.rs @@ -0,0 +1,56 @@ +#[derive(Copy, Clone, Debug, Default)] +pub(crate) struct Palette { + key: anstyle::Style, + value: anstyle::Style, +} + +impl Palette { + pub(crate) fn color() -> Self { + if cfg!(feature = "color") { + Self { + key: anstyle::AnsiColor::Blue.on_default() | anstyle::Effects::BOLD, + value: anstyle::AnsiColor::Yellow.on_default() | anstyle::Effects::BOLD, + } + } else { + Self::plain() + } + } + + pub(crate) fn plain() -> Self { + Self::default() + } + + pub(crate) fn key(self, display: D) -> Styled { + Styled::new(display, self.key) + } + + pub(crate) fn value(self, display: D) -> Styled { + Styled::new(display, self.value) + } +} + +#[derive(Debug)] +pub(crate) struct Styled { + display: D, + style: anstyle::Style, +} + +impl Styled { + pub(crate) fn new(display: D, style: anstyle::Style) -> Self { + Self { display, style } + } +} + +impl std::fmt::Display for Styled { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if f.alternate() { + write!(f, "{}", self.style.render())?; + self.display.fmt(f)?; + write!(f, "{}", self.style.render_reset())?; + Ok(()) + } else { + self.display.fmt(f) + } + } +} diff --git a/tools/vendor/assert_cmd/src/lib.rs b/tools/vendor/assert_cmd/src/lib.rs new file mode 100644 index 0000000000..e8ca5bd0e3 --- /dev/null +++ b/tools/vendor/assert_cmd/src/lib.rs @@ -0,0 +1,128 @@ +//! **Assert [`Command`]** - Easy command initialization and assertions. +//! +//! `assert_cmd` aims to simplify the process for doing integration testing of CLIs, including: +//! - Finding your crate's binary to test +//! - Assert on the result of your program's run. +//! +//! ## Overview +//! +//! Create a [`Command`]: +//! - `Command::new(path)` +//! - `Command::from_std(...)` +//! - `Command::cargo_bin(name)` +//! +//! Configure a [`Command`]: +//! - `arg` / `args` +//! - `current_dir` +//! - `env` / `envs` / `env_remove` / `env_clear` +//! - `write_stdin` / `pipe_stdin` +//! - `timeout` +//! +//! Validate a [`Command`]: +//! - `ok` / `unwrap` / `unwrap_err` +//! - `assert` +//! - `success`, see [`Assert`] +//! - `failure`, see [`Assert`] +//! - `interrupted`, see [`Assert`] +//! - `code`, see [`Assert`] +//! - `stdout`, see [`Assert`] +//! - `stderr`, see [`Assert`] +//! - `get_output` for everything else, see [`Assert`] +//! +//! Note: [`Command`] is provided as a convenience. Extension traits for [`std::process::Command`] +//! and `Output` are provided for interoperability: +//! - [`CommandCargoExt`] +//! - [`OutputOkExt`] +//! - [`OutputAssertExt`] +//! +//! ## Examples +//! +//! Here's a trivial example: +//! ```rust,no_run +//! use assert_cmd::Command; +//! +//! let mut cmd = Command::cargo_bin("bin_fixture").unwrap(); +//! cmd.assert().success(); +//! ``` +//! +//! And a little of everything: +//! ```rust,no_run +//! use assert_cmd::Command; +//! +//! let mut cmd = Command::cargo_bin("bin_fixture").unwrap(); +//! let assert = cmd +//! .arg("-A") +//! .env("stdout", "hello") +//! .env("exit", "42") +//! .write_stdin("42") +//! .assert(); +//! assert +//! .failure() +//! .code(42) +//! .stdout("hello\n"); +//! ``` +//! +//! ## Relevant crates +//! +//! Other crates that might be useful in testing command line programs. +//! * [escargot] for more control over configuring the crate's binary. +//! * [duct] for orchestrating multiple processes. +//! * or [commandspec] for easier writing of commands +//! * [rexpect][rexpect] for testing interactive programs. +//! * [assert_fs] for filesystem fixtures and assertions. +//! * or [tempfile] for scratchpad directories. +//! * [dir-diff] for testing file side-effects. +//! +//! ## Migrating from `assert_cli` v0.6 +//! +//! `assert_cmd` is the successor to [the original `assert_cli`][assert_cli]: +//! - More flexible, reusable assertions (also used by [assert_fs]). +//! - Can integrate with other process-management crates, like `duct`. +//! - Addresses several architectural problems. +//! +//! Key points in migrating from `assert_cli`: +//! - The command-under-test is run eagerly, with assertions happening immediately. +//! - [`success()`] is not implicit and requires being explicitly called. +//! - `stdout`/`stderr` aren't automatically trimmed before being passed to the `Predicate`. +//! +//! [commandspec]: https://crates.io/crates/commandspec +//! [assert_cli]: https://crates.io/crates/assert_cli/0.6.3 +//! [dir-diff]: https://crates.io/crates/dir-diff +//! [tempfile]: https://crates.io/crates/tempfile +//! [escargot]: https://crates.io/crates/escargot +//! [duct]: https://crates.io/crates/duct +//! [assert_fs]: https://crates.io/crates/assert_fs +//! [rexpect]: https://crates.io/crates/rexpect +//! [`Command`]: cmd::Command +//! [`Assert`]: assert::Assert +//! [`success()`]: assert::Assert::success() +//! [`CommandCargoExt`]: cargo::CommandCargoExt +//! [`OutputOkExt`]: output::OutputOkExt +//! [`OutputAssertExt`]: assert::OutputAssertExt + +#![cfg_attr(docsrs, feature(doc_cfg))] +#![warn(clippy::print_stderr)] +#![warn(clippy::print_stdout)] + +mod macros; + +pub mod assert; +pub mod cargo; +pub mod cmd; +pub mod output; + +/// Extension traits that are useful to have available. +pub mod prelude { + pub use crate::assert::OutputAssertExt; + pub use crate::cargo::CommandCargoExt; + pub use crate::output::OutputOkExt; +} + +pub use crate::cmd::Command; + +mod color; +use color::Palette; + +#[doc = include_str!("../README.md")] +#[cfg(doctest)] +pub struct ReadmeDoctests; diff --git a/tools/vendor/assert_cmd/src/macros.rs b/tools/vendor/assert_cmd/src/macros.rs new file mode 100644 index 0000000000..45971eef7c --- /dev/null +++ b/tools/vendor/assert_cmd/src/macros.rs @@ -0,0 +1,92 @@ +/// Allows you to pull the name from your Cargo.toml at compile time. +/// +/// # Examples +/// +/// ```should_panic +/// use assert_cmd::Command; +/// +/// let mut cmd = Command::cargo_bin(assert_cmd::pkg_name!()).unwrap(); +/// let assert = cmd +/// .arg("-A") +/// .env("stdout", "hello") +/// .env("exit", "42") +/// .write_stdin("42") +/// .assert(); +/// assert +/// .failure() +/// .code(42) +/// .stdout("hello\n"); +/// ``` +#[macro_export] +macro_rules! pkg_name { + () => { + env!("CARGO_PKG_NAME") + }; +} + +/// Deprecated, replaced with [`pkg_name`] +#[deprecated(since = "2.1.0", note = "replaced with `pkg_name!`")] +#[macro_export] +macro_rules! crate_name { + () => { + env!("CARGO_PKG_NAME") + }; +} + +/// The absolute path to a binary target's executable. +/// +/// The `bin_target_name` is the name of the binary +/// target, exactly as-is. +/// +/// **NOTE:** This is only set when building an integration test or benchmark. +/// +/// ## Example +/// +/// ```rust,ignore +/// use assert_cmd::prelude::*; +/// use assert_cmd::cargo::cargo_bin; +/// +/// use std::process::Command; +/// +/// let mut cmd = Command::new(cargo_bin!()); +/// let output = cmd.unwrap(); +/// ``` +#[macro_export] +#[doc(hidden)] +macro_rules! cargo_bin { + () => { + $crate::cargo::cargo_bin!($crate::pkg_name!()) + }; + ($bin_target_name:expr) => { + ::std::path::Path::new(env!(concat!("CARGO_BIN_EXE_", $bin_target_name))) + }; +} + +/// A [`Command`][crate::Command] for the binary target's executable. +/// +/// The `bin_target_name` is the name of the binary +/// target, exactly as-is. +/// +/// **NOTE:** This is only set when building an integration test or benchmark. +/// +/// ## Example +/// +/// ```rust,ignore +/// use assert_cmd::prelude::*; +/// use assert_cmd::cargo::cargo_bin_cmd; +/// +/// use std::process::Command; +/// +/// let mut cmd = cargo_bin_cmd!(); +/// let output = cmd.unwrap(); +/// ``` +#[macro_export] +#[doc(hidden)] +macro_rules! cargo_bin_cmd { + () => { + $crate::cargo::cargo_bin_cmd!($crate::pkg_name!()) + }; + ($bin_target_name:expr) => { + $crate::Command::new($crate::cargo::cargo_bin!($bin_target_name)) + }; +} diff --git a/tools/vendor/assert_cmd/src/output.rs b/tools/vendor/assert_cmd/src/output.rs new file mode 100644 index 0000000000..2d49703ae0 --- /dev/null +++ b/tools/vendor/assert_cmd/src/output.rs @@ -0,0 +1,531 @@ +//! Simplify one-off runs of programs. + +use bstr::ByteSlice; +use std::error::Error; +use std::fmt; +use std::process; + +/// Converts a type to an [`OutputResult`]. +/// +/// This is for example implemented on [`std::process::Output`]. +/// +/// # Examples +/// +/// ```rust +/// use assert_cmd::prelude::*; +/// +/// use std::process::Command; +/// +/// let result = Command::new("echo") +/// .args(&["42"]) +/// .ok(); +/// assert!(result.is_ok()); +/// ``` +/// +pub trait OutputOkExt +where + Self: ::std::marker::Sized, +{ + /// Convert an [`Output`] to an [`OutputResult`]. + /// + /// # Examples + /// + /// ```rust + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// + /// let result = Command::new("echo") + /// .args(&["42"]) + /// .ok(); + /// assert!(result.is_ok()); + /// ``` + /// + /// [`Output`]: std::process::Output + fn ok(self) -> OutputResult; + + /// Unwrap a [`Output`] but with a prettier message than `.ok().unwrap()`. + /// + /// # Examples + /// + /// ```rust + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// + /// let output = Command::new("echo") + /// .args(&["42"]) + /// .unwrap(); + /// ``` + /// + /// [`Output`]: std::process::Output + fn unwrap(self) -> process::Output { + match self.ok() { + Ok(output) => output, + Err(err) => panic!("{}", err), + } + } + + /// Unwrap a [`Output`] but with a prettier message than `ok().err().unwrap()`. + /// + /// # Examples + /// + /// ```rust,no_run + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// + /// let err = Command::new("a-command") + /// .args(&["--will-fail"]) + /// .unwrap_err(); + /// ``` + /// + /// [`Output`]: std::process::Output + fn unwrap_err(self) -> OutputError { + match self.ok() { + Ok(output) => panic!( + "Command completed successfully\nstdout=```{}```", + DebugBytes::new(&output.stdout) + ), + Err(err) => err, + } + } +} + +impl OutputOkExt for process::Output { + fn ok(self) -> OutputResult { + if self.status.success() { + Ok(self) + } else { + let error = OutputError::new(self); + Err(error) + } + } +} + +impl OutputOkExt for &mut process::Command { + fn ok(self) -> OutputResult { + let output = self.output().map_err(OutputError::with_cause)?; + if output.status.success() { + Ok(output) + } else { + let error = OutputError::new(output).set_cmd(format!("{self:?}")); + Err(error) + } + } + + fn unwrap_err(self) -> OutputError { + match self.ok() { + Ok(output) => panic!( + "Completed successfully:\ncommand=`{:?}`\nstdout=```{}```", + self, + DebugBytes::new(&output.stdout) + ), + Err(err) => err, + } + } +} + +/// [`Output`] represented as a [`Result`]. +/// +/// Generally produced by [`OutputOkExt`]. +/// +/// # Examples +/// +/// ```rust +/// use assert_cmd::prelude::*; +/// +/// use std::process::Command; +/// +/// let result = Command::new("echo") +/// .args(&["42"]) +/// .ok(); +/// assert!(result.is_ok()); +/// ``` +/// +/// [`Output`]: std::process::Output +/// [`Result`]: std::result::Result +pub type OutputResult = Result; + +/// [`Command`] error. +/// +/// Generally produced by [`OutputOkExt`]. +/// +/// # Examples +/// +/// ```rust,no_run +/// use assert_cmd::prelude::*; +/// +/// use std::process::Command; +/// +/// let err = Command::new("a-command") +/// .args(&["--will-fail"]) +/// .unwrap_err(); +/// ``` +/// +/// [`Command`]: std::process::Command +#[derive(Debug)] +pub struct OutputError { + cmd: Option, + stdin: Option, + cause: OutputCause, +} + +impl OutputError { + /// Convert [`Output`] into an [`Error`]. + /// + /// [`Output`]: std::process::Output + /// [`Error`]: std::error::Error + pub fn new(output: process::Output) -> Self { + Self { + cmd: None, + stdin: None, + cause: OutputCause::Expected(Output { output }), + } + } + + /// For errors that happen in creating a [`Output`]. + /// + /// [`Output`]: std::process::Output + pub fn with_cause(cause: E) -> Self + where + E: Error + Send + Sync + 'static, + { + Self { + cmd: None, + stdin: None, + cause: OutputCause::Unexpected(Box::new(cause)), + } + } + + /// Add the command line for additional context. + pub fn set_cmd(mut self, cmd: String) -> Self { + self.cmd = Some(cmd); + self + } + + /// Add the `stdin` for additional context. + pub fn set_stdin(mut self, stdin: Vec) -> Self { + self.stdin = Some(bstr::BString::from(stdin)); + self + } + + /// Access the contained [`Output`]. + /// + /// # Examples + /// + /// ```rust,no_run + /// use assert_cmd::prelude::*; + /// + /// use std::process::Command; + /// + /// let err = Command::new("a-command") + /// .args(&["--will-fail"]) + /// .unwrap_err(); + /// let output = err + /// .as_output() + /// .unwrap(); + /// assert_eq!(Some(42), output.status.code()); + /// ``` + /// + /// [`Output`]: std::process::Output + pub fn as_output(&self) -> Option<&process::Output> { + match self.cause { + OutputCause::Expected(ref e) => Some(&e.output), + OutputCause::Unexpected(_) => None, + } + } +} + +impl Error for OutputError {} + +impl fmt::Display for OutputError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::color(); + if let Some(ref cmd) = self.cmd { + writeln!(f, "{:#}={:#}", palette.key("command"), palette.value(cmd))?; + } + if let Some(ref stdin) = self.stdin { + writeln!( + f, + "{:#}={:#}", + palette.key("stdin"), + palette.value(DebugBytes::new(stdin)) + )?; + } + write!(f, "{:#}", self.cause) + } +} + +#[derive(Debug)] +enum OutputCause { + Expected(Output), + Unexpected(Box), +} + +impl fmt::Display for OutputCause { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + OutputCause::Expected(ref e) => write!(f, "{e:#}"), + OutputCause::Unexpected(ref e) => write!(f, "{e:#}"), + } + } +} + +#[derive(Debug)] +struct Output { + output: process::Output, +} + +impl fmt::Display for Output { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + output_fmt(&self.output, f) + } +} + +pub(crate) fn output_fmt(output: &process::Output, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::color(); + if let Some(code) = output.status.code() { + writeln!(f, "{:#}={:#}", palette.key("code"), palette.value(code))?; + } else { + writeln!( + f, + "{:#}={:#}", + palette.key("code"), + palette.value("") + )?; + } + + write!( + f, + "{:#}={:#}\n{:#}={:#}\n", + palette.key("stdout"), + palette.value(DebugBytes::new(&output.stdout)), + palette.key("stderr"), + palette.value(DebugBytes::new(&output.stderr)), + )?; + Ok(()) +} + +#[derive(Debug)] +pub(crate) struct DebugBytes<'a> { + bytes: &'a [u8], +} + +impl<'a> DebugBytes<'a> { + pub(crate) fn new(bytes: &'a [u8]) -> Self { + DebugBytes { bytes } + } +} + +impl fmt::Display for DebugBytes<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + format_bytes(self.bytes, f) + } +} + +#[derive(Debug)] +pub(crate) struct DebugBuffer { + buffer: bstr::BString, +} + +impl DebugBuffer { + pub(crate) fn new(buffer: Vec) -> Self { + DebugBuffer { + buffer: buffer.into(), + } + } +} + +impl fmt::Display for DebugBuffer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + format_bytes(&self.buffer, f) + } +} + +fn format_bytes(data: &[u8], f: &mut impl fmt::Write) -> fmt::Result { + #![allow(clippy::assertions_on_constants)] + + const LINES_MIN_OVERFLOW: usize = 80; + const LINES_MAX_START: usize = 20; + const LINES_MAX_END: usize = 40; + const LINES_MAX_PRINTED: usize = LINES_MAX_START + LINES_MAX_END; + + const BYTES_MIN_OVERFLOW: usize = 8192; + const BYTES_MAX_START: usize = 2048; + const BYTES_MAX_END: usize = 2048; + const BYTES_MAX_PRINTED: usize = BYTES_MAX_START + BYTES_MAX_END; + + assert!(LINES_MAX_PRINTED < LINES_MIN_OVERFLOW); + assert!(BYTES_MAX_PRINTED < BYTES_MIN_OVERFLOW); + + let lines_total = data.as_bstr().lines_with_terminator().count(); + let multiline = 1 < lines_total; + + if LINES_MIN_OVERFLOW <= lines_total { + let lines_omitted = lines_total - LINES_MAX_PRINTED; + let start_lines = data.as_bstr().lines_with_terminator().take(LINES_MAX_START); + let end_lines = data + .as_bstr() + .lines_with_terminator() + .skip(LINES_MAX_START + lines_omitted); + writeln!(f, "<{lines_total} lines total>")?; + write_debug_bstrs(f, true, start_lines)?; + writeln!(f, "<{lines_omitted} lines omitted>")?; + write_debug_bstrs(f, true, end_lines) + } else if BYTES_MIN_OVERFLOW <= data.len() { + write!( + f, + "<{} bytes total>{}", + data.len(), + if multiline { "\n" } else { "" } + )?; + write_debug_bstrs( + f, + multiline, + data[..BYTES_MAX_START].lines_with_terminator(), + )?; + write!( + f, + "<{} bytes omitted>{}", + data.len() - BYTES_MAX_PRINTED, + if multiline { "\n" } else { "" } + )?; + write_debug_bstrs( + f, + multiline, + data[data.len() - BYTES_MAX_END..].lines_with_terminator(), + ) + } else { + write_debug_bstrs(f, multiline, data.lines_with_terminator()) + } +} + +fn write_debug_bstrs<'a>( + f: &mut impl fmt::Write, + multiline: bool, + mut lines: impl Iterator, +) -> fmt::Result { + if multiline { + writeln!(f, "```")?; + for mut line in lines { + let mut newline = false; + if line.last() == Some(&b'\n') { + line = &line[..line.len() - 1]; + newline = true; + } + let s = format!("{:?}", line.as_bstr()); + write!( + f, + "{}{}", + &s[1..s.len() - 1], + if newline { "\n" } else { "" } + )?; + } + writeln!(f, "```") + } else { + write!(f, "{:?}", lines.next().unwrap_or(&[]).as_bstr()) + } +} + +#[cfg(test)] +mod test { + #[test] + fn format_bytes() { + let mut s = String::new(); + for i in 0..80 { + s.push_str(&format!("{i}\n")); + } + + let mut buf = String::new(); + super::format_bytes(s.as_bytes(), &mut buf).unwrap(); + + assert_eq!( + "<80 lines total> +``` +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +``` +<20 lines omitted> +``` +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +``` +", + buf + ); + } + + #[test] + fn no_trailing_newline() { + let s = "no\ntrailing\nnewline"; + + let mut buf = String::new(); + super::format_bytes(s.as_bytes(), &mut buf).unwrap(); + + assert_eq!( + "``` +no +trailing +newline``` +", + buf + ); + } +} diff --git a/tools/vendor/autocfg/.cargo-checksum.json b/tools/vendor/autocfg/.cargo-checksum.json new file mode 100644 index 0000000000..944074e2e1 --- /dev/null +++ b/tools/vendor/autocfg/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"bbdbd90be8e01b10f5516e44a9d4a09efc7e0c489eed3bb4e64adcde3b9d91a3","Cargo.lock":"152ebf71feed214951186ccc738958236b493bacf634d48ce422371c92ef0b07","Cargo.toml":"919934158a57ad16895ec2e937c5c14e3cef405b168c191d64dc81b69b9abe4a","Cargo.toml.orig":"958306c591cb50cd4b090194971e2eb213c641b304af82b2b36c31b60ffdc126","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"27995d58ad5c1145c1a8cd86244ce844886958a35eb2b78c6b772748669999ac","README.md":"fbd8762dcc681074fb3862e5d2168299fedb08d08bcadbff2713e2f956a79f82","examples/integers.rs":"589ff4271566dfa322becddf3e2c7b592e6e0bc97b02892ce75619b7e452e930","examples/nightly.rs":"ac8b5a9aa1e04465e44f5053b3c899b635e07af058c73aa8b45176bf4b5912f9","examples/paths.rs":"1b30e466b824ce8df7ad0a55334424131d9d2573d6cf9f7d5d50c09c8901d526","examples/traits.rs":"cbee6a3e1f7db60b02ae25b714926517144a77cb492021f492774cf0e1865a9e","examples/versions.rs":"38535e6d9f5bfae0de474a3db79a40e8f5da8ba9334c5ff4c363de9bc99d4d12","src/error.rs":"fd8ff67c64f7cd1b9f81325a81de4baa34c39d6ae298bdb33f9829cc91acac39","src/lib.rs":"772630d0e09d06f343fd72284c8330eacde87d2a4ee22f70af62a0e2119fbf05","src/rustc.rs":"a8a213ddb64a05c1a1af933bcb331a98879e942b167c33d8f94f9f60ebb14e29","src/tests.rs":"b39f4d880ad343e65307a9e0c381954ea27adce4732f825516ce7952e2e5a91d","src/version.rs":"4f7d23b36f01c7be1871be86c038d6cb4689e145d67c82d3793690e9aa05b133","tests/no_std.rs":"18859dc4992fe1769887bde05f03d28f1ce524eafd17646d3fbcb4379422761a","tests/rustflags.rs":"e8ded4d57ba25379a38ab48456d67df14f82abbbb5f6bb66221c6decbcb517a3","tests/support/mod.rs":"32087d365b438ac3f62df9bb066d8d648b80cb130a5c777afcb2f21fbb68d88e","tests/tests.rs":"39149fe69a45f577eeb531587a5e600d8bc9f8d937480172dac9402939d856f0","tests/wrap_ignored":"a9e241edf584a0702066b25bc15c5bbfd8a1019e14fb655fc4f47a67360065ca","tests/wrappers.rs":"b5137fe36768eaef277fb8650b0fe45b2c675d9e60b1b49d39f2ec95082c285b"},"package":"c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"} \ No newline at end of file diff --git a/tools/vendor/autocfg/.cargo_vcs_info.json b/tools/vendor/autocfg/.cargo_vcs_info.json new file mode 100644 index 0000000000..80087c7071 --- /dev/null +++ b/tools/vendor/autocfg/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "d912169ed67977efe5a465269b0e73cb66060c49" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/tools/vendor/autocfg/Cargo.lock b/tools/vendor/autocfg/Cargo.lock new file mode 100644 index 0000000000..45f1f18ec9 --- /dev/null +++ b/tools/vendor/autocfg/Cargo.lock @@ -0,0 +1,6 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "autocfg" +version = "1.5.0" + diff --git a/tools/vendor/autocfg/Cargo.toml b/tools/vendor/autocfg/Cargo.toml new file mode 100644 index 0000000000..005f56dcdd --- /dev/null +++ b/tools/vendor/autocfg/Cargo.toml @@ -0,0 +1,76 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +rust-version = "1.0" +name = "autocfg" +version = "1.5.0" +authors = ["Josh Stone "] +build = false +exclude = ["/.github/**"] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Automatic cfg for Rust compiler features" +documentation = "https://docs.rs/autocfg/" +readme = "README.md" +keywords = [ + "rustc", + "build", + "autoconf", +] +categories = ["development-tools::build-utils"] +license = "Apache-2.0 OR MIT" +repository = "https://github.com/cuviper/autocfg" + +[lib] +name = "autocfg" +path = "src/lib.rs" + +[[example]] +name = "integers" +path = "examples/integers.rs" + +[[example]] +name = "nightly" +path = "examples/nightly.rs" + +[[example]] +name = "paths" +path = "examples/paths.rs" + +[[example]] +name = "traits" +path = "examples/traits.rs" + +[[example]] +name = "versions" +path = "examples/versions.rs" + +[[test]] +name = "no_std" +path = "tests/no_std.rs" + +[[test]] +name = "rustflags" +path = "tests/rustflags.rs" + +[[test]] +name = "tests" +path = "tests/tests.rs" + +[[test]] +name = "wrappers" +path = "tests/wrappers.rs" + +[dependencies] diff --git a/tools/vendor/autocfg/Cargo.toml.orig b/tools/vendor/autocfg/Cargo.toml.orig new file mode 100644 index 0000000000..75b34ea605 --- /dev/null +++ b/tools/vendor/autocfg/Cargo.toml.orig @@ -0,0 +1,15 @@ +[package] +name = "autocfg" +version = "1.5.0" +authors = ["Josh Stone "] +license = "Apache-2.0 OR MIT" +repository = "https://github.com/cuviper/autocfg" +documentation = "https://docs.rs/autocfg/" +description = "Automatic cfg for Rust compiler features" +readme = "README.md" +keywords = ["rustc", "build", "autoconf"] +categories = ["development-tools::build-utils"] +exclude = ["/.github/**"] +rust-version = "1.0" + +[dependencies] diff --git a/tools/vendor/autocfg/LICENSE-APACHE b/tools/vendor/autocfg/LICENSE-APACHE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/tools/vendor/autocfg/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/tools/vendor/autocfg/LICENSE-MIT b/tools/vendor/autocfg/LICENSE-MIT new file mode 100644 index 0000000000..44fbc4d8b9 --- /dev/null +++ b/tools/vendor/autocfg/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2018 Josh Stone + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/tools/vendor/autocfg/README.md b/tools/vendor/autocfg/README.md new file mode 100644 index 0000000000..a3d8ac4995 --- /dev/null +++ b/tools/vendor/autocfg/README.md @@ -0,0 +1,117 @@ +autocfg +======= + +[![autocfg crate](https://img.shields.io/crates/v/autocfg.svg)](https://crates.io/crates/autocfg) +[![autocfg documentation](https://docs.rs/autocfg/badge.svg)](https://docs.rs/autocfg) +![minimum rustc 1.0](https://img.shields.io/badge/rustc-1.0+-red.svg) +![build status](https://github.com/cuviper/autocfg/workflows/CI/badge.svg) + +A Rust library for build scripts to automatically configure code based on +compiler support. Code snippets are dynamically tested to see if the `rustc` +will accept them, rather than hard-coding specific version support. + + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[build-dependencies] +autocfg = "1" +``` + +Then use it in your `build.rs` script to detect compiler features. For +example, to test for 128-bit integer support, it might look like: + +```rust +extern crate autocfg; + +fn main() { + let ac = autocfg::new(); + ac.emit_has_type("i128"); + + // (optional) We don't need to rerun for anything external. + autocfg::rerun_path("build.rs"); +} +``` + +If the type test succeeds, this will write a `cargo:rustc-cfg=has_i128` line +for Cargo, which translates to Rust arguments `--cfg has_i128`. Then in the +rest of your Rust code, you can add `#[cfg(has_i128)]` conditions on code that +should only be used when the compiler supports it. + + +## Release Notes + +- 1.5.0 (2025-06-17) + + - Add `edition` and `set_edition` to control the Rust edition used in probes. + - Remove probe result files so they don't pollute the output directory. + +- 1.4.0 (2024-09-26) + + - Add `emit_possibility` for Rust 1.80's [checked cfgs], and call that + automatically for methods that conditionally `emit`, by @Techcable. + +[checked cfgs]: https://blog.rust-lang.org/2024/05/06/check-cfg.html + +- 1.3.0 (2024-05-03) + + - Add `probe_raw` for direct control of the code that will be test-compiled. + - Use wrappers when querying the `rustc` version information too. + +- 1.2.0 (2024-03-25) + + - Add `no_std` and `set_no_std` to control the use of `#![no_std]` in probes. + - Use `RUSTC_WRAPPER` and `RUSTC_WORKSPACE_WRAPPER` when they are set. + +- 1.1.0 (2022-02-07) + - Use `CARGO_ENCODED_RUSTFLAGS` when it is set. + +- 1.0.1 (2020-08-20) + - Apply `RUSTFLAGS` for more `--target` scenarios, by @adamreichold. + +- 1.0.0 (2020-01-08) + - 🎉 Release 1.0! 🎉 (no breaking changes) + - Add `probe_expression` and `emit_expression_cfg` to test arbitrary expressions. + - Add `probe_constant` and `emit_constant_cfg` to test arbitrary constant expressions. + +- 0.1.7 (2019-10-20) + - Apply `RUSTFLAGS` when probing `$TARGET != $HOST`, mainly for sysroot, by @roblabla. + +- 0.1.6 (2019-08-19) + - Add `probe`/`emit_sysroot_crate`, by @leo60228. + +- 0.1.5 (2019-07-16) + - Mask some warnings from newer rustc. + +- 0.1.4 (2019-05-22) + - Relax `std`/`no_std` probing to a warning instead of an error. + - Improve `rustc` bootstrap compatibility. + +- 0.1.3 (2019-05-21) + - Auto-detects if `#![no_std]` is needed for the `$TARGET`. + +- 0.1.2 (2019-01-16) + - Add `rerun_env(ENV)` to print `cargo:rerun-if-env-changed=ENV`. + - Add `rerun_path(PATH)` to print `cargo:rerun-if-changed=PATH`. + + +## Minimum Rust version policy + +This crate's minimum supported `rustc` version is `1.0.0`. Compatibility is +its entire reason for existence, so this crate will be extremely conservative +about raising this requirement. If this is ever deemed necessary, it will be +treated as a major breaking change for semver purposes. + + +## License + +This project is licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or + https://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or + https://opensource.org/licenses/MIT) + +at your option. diff --git a/tools/vendor/autocfg/examples/integers.rs b/tools/vendor/autocfg/examples/integers.rs new file mode 100644 index 0000000000..23d4cba6ee --- /dev/null +++ b/tools/vendor/autocfg/examples/integers.rs @@ -0,0 +1,9 @@ +extern crate autocfg; + +fn main() { + // Normally, cargo will set `OUT_DIR` for build scripts. + let ac = autocfg::AutoCfg::with_dir("target").unwrap(); + for i in 3..8 { + ac.emit_has_type(&format!("i{}", 1 << i)); + } +} diff --git a/tools/vendor/autocfg/examples/nightly.rs b/tools/vendor/autocfg/examples/nightly.rs new file mode 100644 index 0000000000..955e63d74c --- /dev/null +++ b/tools/vendor/autocfg/examples/nightly.rs @@ -0,0 +1,18 @@ +extern crate autocfg; + +fn main() { + // Normally, cargo will set `OUT_DIR` for build scripts. + let ac = autocfg::AutoCfg::with_dir("target").unwrap(); + + // When this feature was stabilized, it also renamed the method to + // `chunk_by`, so it's important to *use* the feature in your probe. + let code = r#" + #![feature(slice_group_by)] + pub fn probe(slice: &[i32]) -> impl Iterator { + slice.group_by(|a, b| a == b) + } + "#; + if ac.probe_raw(code).is_ok() { + autocfg::emit("has_slice_group_by"); + } +} diff --git a/tools/vendor/autocfg/examples/paths.rs b/tools/vendor/autocfg/examples/paths.rs new file mode 100644 index 0000000000..b7a6ca7a25 --- /dev/null +++ b/tools/vendor/autocfg/examples/paths.rs @@ -0,0 +1,22 @@ +extern crate autocfg; + +fn main() { + // Normally, cargo will set `OUT_DIR` for build scripts. + let ac = autocfg::AutoCfg::with_dir("target").unwrap(); + + // since ancient times... + ac.emit_has_path("std::vec::Vec"); + ac.emit_path_cfg("std::vec::Vec", "has_vec"); + + // rustc 1.10.0 + ac.emit_has_path("std::panic::PanicInfo"); + ac.emit_path_cfg("std::panic::PanicInfo", "has_panic_info"); + + // rustc 1.20.0 + ac.emit_has_path("std::mem::ManuallyDrop"); + ac.emit_path_cfg("std::mem::ManuallyDrop", "has_manually_drop"); + + // rustc 1.25.0 + ac.emit_has_path("std::ptr::NonNull"); + ac.emit_path_cfg("std::ptr::NonNull", "has_non_null"); +} diff --git a/tools/vendor/autocfg/examples/traits.rs b/tools/vendor/autocfg/examples/traits.rs new file mode 100644 index 0000000000..c1ca00385c --- /dev/null +++ b/tools/vendor/autocfg/examples/traits.rs @@ -0,0 +1,26 @@ +extern crate autocfg; + +fn main() { + // Normally, cargo will set `OUT_DIR` for build scripts. + let ac = autocfg::AutoCfg::with_dir("target").unwrap(); + + // since ancient times... + ac.emit_has_trait("std::ops::Add"); + ac.emit_trait_cfg("std::ops::Add", "has_ops"); + + // trait parameters have to be provided + ac.emit_has_trait("std::borrow::Borrow"); + ac.emit_trait_cfg("std::borrow::Borrow", "has_borrow"); + + // rustc 1.8.0 + ac.emit_has_trait("std::ops::AddAssign"); + ac.emit_trait_cfg("std::ops::AddAssign", "has_assign_ops"); + + // rustc 1.12.0 + ac.emit_has_trait("std::iter::Sum"); + ac.emit_trait_cfg("std::iter::Sum", "has_sum"); + + // rustc 1.28.0 + ac.emit_has_trait("std::alloc::GlobalAlloc"); + ac.emit_trait_cfg("std::alloc::GlobalAlloc", "has_global_alloc"); +} diff --git a/tools/vendor/autocfg/examples/versions.rs b/tools/vendor/autocfg/examples/versions.rs new file mode 100644 index 0000000000..992919b7c6 --- /dev/null +++ b/tools/vendor/autocfg/examples/versions.rs @@ -0,0 +1,9 @@ +extern crate autocfg; + +fn main() { + // Normally, cargo will set `OUT_DIR` for build scripts. + let ac = autocfg::AutoCfg::with_dir("target").unwrap(); + for i in 0..100 { + ac.emit_rustc_version(1, i); + } +} diff --git a/tools/vendor/autocfg/src/error.rs b/tools/vendor/autocfg/src/error.rs new file mode 100644 index 0000000000..7839ee9a2f --- /dev/null +++ b/tools/vendor/autocfg/src/error.rs @@ -0,0 +1,81 @@ +use std::error; +use std::fmt; +use std::io; +use std::num; +use std::process; +use std::str; + +/// A common error type for the `autocfg` crate. +#[derive(Debug)] +pub struct Error { + kind: ErrorKind, +} + +impl error::Error for Error { + fn description(&self) -> &str { + "AutoCfg error" + } + + fn cause(&self) -> Option<&error::Error> { + match self.kind { + ErrorKind::Io(ref e) => Some(e), + ErrorKind::Num(ref e) => Some(e), + ErrorKind::Utf8(ref e) => Some(e), + ErrorKind::Process(_) | ErrorKind::Other(_) => None, + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match self.kind { + ErrorKind::Io(ref e) => e.fmt(f), + ErrorKind::Num(ref e) => e.fmt(f), + ErrorKind::Utf8(ref e) => e.fmt(f), + ErrorKind::Process(ref status) => { + // Same message as the newer `ExitStatusError` + write!(f, "process exited unsuccessfully: {}", status) + } + ErrorKind::Other(s) => s.fmt(f), + } + } +} + +#[derive(Debug)] +enum ErrorKind { + Io(io::Error), + Num(num::ParseIntError), + Process(process::ExitStatus), + Utf8(str::Utf8Error), + Other(&'static str), +} + +pub fn from_exit(status: process::ExitStatus) -> Error { + Error { + kind: ErrorKind::Process(status), + } +} + +pub fn from_io(e: io::Error) -> Error { + Error { + kind: ErrorKind::Io(e), + } +} + +pub fn from_num(e: num::ParseIntError) -> Error { + Error { + kind: ErrorKind::Num(e), + } +} + +pub fn from_utf8(e: str::Utf8Error) -> Error { + Error { + kind: ErrorKind::Utf8(e), + } +} + +pub fn from_str(s: &'static str) -> Error { + Error { + kind: ErrorKind::Other(s), + } +} diff --git a/tools/vendor/autocfg/src/lib.rs b/tools/vendor/autocfg/src/lib.rs new file mode 100644 index 0000000000..69e6ffcf36 --- /dev/null +++ b/tools/vendor/autocfg/src/lib.rs @@ -0,0 +1,636 @@ +//! A Rust library for build scripts to automatically configure code based on +//! compiler support. Code snippets are dynamically tested to see if the `rustc` +//! will accept them, rather than hard-coding specific version support. +//! +//! +//! ## Usage +//! +//! Add this to your `Cargo.toml`: +//! +//! ```toml +//! [build-dependencies] +//! autocfg = "1" +//! ``` +//! +//! Then use it in your `build.rs` script to detect compiler features. For +//! example, to test for 128-bit integer support, it might look like: +//! +//! ```rust +//! extern crate autocfg; +//! +//! fn main() { +//! # // Normally, cargo will set `OUT_DIR` for build scripts. +//! # let exe = std::env::current_exe().unwrap(); +//! # std::env::set_var("OUT_DIR", exe.parent().unwrap()); +//! let ac = autocfg::new(); +//! ac.emit_has_type("i128"); +//! +//! // (optional) We don't need to rerun for anything external. +//! autocfg::rerun_path("build.rs"); +//! } +//! ``` +//! +//! If the type test succeeds, this will write a `cargo:rustc-cfg=has_i128` line +//! for Cargo, which translates to Rust arguments `--cfg has_i128`. Then in the +//! rest of your Rust code, you can add `#[cfg(has_i128)]` conditions on code that +//! should only be used when the compiler supports it. +//! +//! ## Caution +//! +//! Many of the probing methods of `AutoCfg` document the particular template they +//! use, **subject to change**. The inputs are not validated to make sure they are +//! semantically correct for their expected use, so it's _possible_ to escape and +//! inject something unintended. However, such abuse is unsupported and will not +//! be considered when making changes to the templates. + +#![deny(missing_debug_implementations)] +#![deny(missing_docs)] +// allow future warnings that can't be fixed while keeping 1.0 compatibility +#![allow(unknown_lints)] +#![allow(bare_trait_objects)] +#![allow(ellipsis_inclusive_range_patterns)] + +/// Local macro to avoid `std::try!`, deprecated in Rust 1.39. +macro_rules! try { + ($result:expr) => { + match $result { + Ok(value) => value, + Err(error) => return Err(error), + } + }; +} + +use std::env; +use std::ffi::OsString; +use std::fmt::Arguments; +use std::fs; +use std::io::{stderr, Write}; +use std::path::{Path, PathBuf}; +use std::process::Stdio; +#[allow(deprecated)] +use std::sync::atomic::ATOMIC_USIZE_INIT; +use std::sync::atomic::{AtomicUsize, Ordering}; + +mod error; +pub use error::Error; + +mod rustc; +use rustc::Rustc; + +mod version; +use version::Version; + +#[cfg(test)] +mod tests; + +/// Helper to detect compiler features for `cfg` output in build scripts. +#[derive(Clone, Debug)] +pub struct AutoCfg { + out_dir: PathBuf, + rustc: Rustc, + rustc_version: Version, + target: Option, + no_std: bool, + edition: Option, + rustflags: Vec, + uuid: u64, +} + +/// Writes a config flag for rustc on standard out. +/// +/// This looks like: `cargo:rustc-cfg=CFG` +/// +/// Cargo will use this in arguments to rustc, like `--cfg CFG`. +/// +/// This does not automatically call [`emit_possibility`] +/// so the compiler my generate an [`unexpected_cfgs` warning][check-cfg-flags]. +/// However, all the builtin emit methods on [`AutoCfg`] call [`emit_possibility`] automatically. +/// +/// [check-cfg-flags]: https://blog.rust-lang.org/2024/05/06/check-cfg.html +pub fn emit(cfg: &str) { + println!("cargo:rustc-cfg={}", cfg); +} + +/// Writes a line telling Cargo to rerun the build script if `path` changes. +/// +/// This looks like: `cargo:rerun-if-changed=PATH` +/// +/// This requires at least cargo 0.7.0, corresponding to rustc 1.6.0. Earlier +/// versions of cargo will simply ignore the directive. +pub fn rerun_path(path: &str) { + println!("cargo:rerun-if-changed={}", path); +} + +/// Writes a line telling Cargo to rerun the build script if the environment +/// variable `var` changes. +/// +/// This looks like: `cargo:rerun-if-env-changed=VAR` +/// +/// This requires at least cargo 0.21.0, corresponding to rustc 1.20.0. Earlier +/// versions of cargo will simply ignore the directive. +pub fn rerun_env(var: &str) { + println!("cargo:rerun-if-env-changed={}", var); +} + +/// Indicates to rustc that a config flag should not generate an [`unexpected_cfgs` warning][check-cfg-flags] +/// +/// This looks like `cargo:rustc-check-cfg=cfg(VAR)` +/// +/// As of rust 1.80, the compiler does [automatic checking of cfgs at compile time][check-cfg-flags]. +/// All custom configuration flags must be known to rustc, or they will generate a warning. +/// This is done automatically when calling the builtin emit methods on [`AutoCfg`], +/// but not when calling [`autocfg::emit`](crate::emit) directly. +/// +/// Versions before rust 1.80 will simply ignore this directive. +/// +/// This function indicates to the compiler that the config flag never has a value. +/// If this is not desired, see [the blog post][check-cfg]. +/// +/// [check-cfg-flags]: https://blog.rust-lang.org/2024/05/06/check-cfg.html +pub fn emit_possibility(cfg: &str) { + println!("cargo:rustc-check-cfg=cfg({})", cfg); +} + +/// Creates a new `AutoCfg` instance. +/// +/// # Panics +/// +/// Panics if `AutoCfg::new()` returns an error. +pub fn new() -> AutoCfg { + AutoCfg::new().unwrap() +} + +impl AutoCfg { + /// Creates a new `AutoCfg` instance. + /// + /// # Common errors + /// + /// - `rustc` can't be executed, from `RUSTC` or in the `PATH`. + /// - The version output from `rustc` can't be parsed. + /// - `OUT_DIR` is not set in the environment, or is not a writable directory. + /// + pub fn new() -> Result { + match env::var_os("OUT_DIR") { + Some(d) => Self::with_dir(d), + None => Err(error::from_str("no OUT_DIR specified!")), + } + } + + /// Creates a new `AutoCfg` instance with the specified output directory. + /// + /// # Common errors + /// + /// - `rustc` can't be executed, from `RUSTC` or in the `PATH`. + /// - The version output from `rustc` can't be parsed. + /// - `dir` is not a writable directory. + /// + pub fn with_dir>(dir: T) -> Result { + let rustc = Rustc::new(); + let rustc_version = try!(rustc.version()); + + let target = env::var_os("TARGET"); + + // Sanity check the output directory + let dir = dir.into(); + let meta = try!(fs::metadata(&dir).map_err(error::from_io)); + if !meta.is_dir() || meta.permissions().readonly() { + return Err(error::from_str("output path is not a writable directory")); + } + + let mut ac = AutoCfg { + rustflags: rustflags(&target, &dir), + out_dir: dir, + rustc: rustc, + rustc_version: rustc_version, + target: target, + no_std: false, + edition: None, + uuid: new_uuid(), + }; + + // Sanity check with and without `std`. + if ac.probe_raw("").is_err() { + if ac.probe_raw("#![no_std]").is_ok() { + ac.no_std = true; + } else { + // Neither worked, so assume nothing... + let warning = b"warning: autocfg could not probe for `std`\n"; + stderr().write_all(warning).ok(); + } + } + Ok(ac) + } + + /// Returns whether `AutoCfg` is using `#![no_std]` in its probes. + /// + /// This is automatically detected during construction -- if an empty probe + /// fails while one with `#![no_std]` succeeds, then the attribute will be + /// used for all further probes. This is usually only necessary when the + /// `TARGET` lacks `std` altogether. If neither succeeds, `no_std` is not + /// set, but that `AutoCfg` will probably only work for version checks. + /// + /// This attribute changes the implicit [prelude] from `std` to `core`, + /// which may affect the paths you need to use in other probes. It also + /// restricts some types that otherwise get additional methods in `std`, + /// like floating-point trigonometry and slice sorting. + /// + /// See also [`set_no_std`](#method.set_no_std). + /// + /// [prelude]: https://doc.rust-lang.org/reference/names/preludes.html#the-no_std-attribute + pub fn no_std(&self) -> bool { + self.no_std + } + + /// Sets whether `AutoCfg` should use `#![no_std]` in its probes. + /// + /// See also [`no_std`](#method.no_std). + pub fn set_no_std(&mut self, no_std: bool) { + self.no_std = no_std; + } + + /// Returns the `--edition` string that is currently being passed to `rustc`, if any, + /// as configured by the [`set_edition`][Self::set_edition] method. + pub fn edition(&self) -> Option<&str> { + match self.edition { + Some(ref edition) => Some(&**edition), + None => None, + } + } + + /// Sets the `--edition` string that will be passed to `rustc`, + /// or `None` to leave the compiler at its default edition. + /// + /// See also [The Rust Edition Guide](https://doc.rust-lang.org/edition-guide/). + /// + /// **Warning:** Setting an unsupported edition will likely cause **all** subsequent probes to + /// fail! As of this writing, the known editions and their minimum Rust versions are: + /// + /// | Edition | Version | + /// | ------- | ---------- | + /// | 2015 | 1.27.0[^1] | + /// | 2018 | 1.31.0 | + /// | 2021 | 1.56.0 | + /// | 2024 | 1.85.0 | + /// + /// [^1]: Prior to 1.27.0, Rust was effectively 2015 Edition by default, but the concept hadn't + /// been established yet, so the explicit `--edition` flag wasn't supported either. + pub fn set_edition(&mut self, edition: Option) { + self.edition = edition; + } + + /// Tests whether the current `rustc` reports a version greater than + /// or equal to "`major`.`minor`". + pub fn probe_rustc_version(&self, major: usize, minor: usize) -> bool { + self.rustc_version >= Version::new(major, minor, 0) + } + + /// Sets a `cfg` value of the form `rustc_major_minor`, like `rustc_1_29`, + /// if the current `rustc` is at least that version. + pub fn emit_rustc_version(&self, major: usize, minor: usize) { + let cfg_flag = format!("rustc_{}_{}", major, minor); + emit_possibility(&cfg_flag); + if self.probe_rustc_version(major, minor) { + emit(&cfg_flag); + } + } + + /// Returns a new (hopefully unique) crate name for probes. + fn new_crate_name(&self) -> String { + #[allow(deprecated)] + static ID: AtomicUsize = ATOMIC_USIZE_INIT; + + let id = ID.fetch_add(1, Ordering::Relaxed); + format!("autocfg_{:016x}_{}", self.uuid, id) + } + + fn probe_fmt<'a>(&self, source: Arguments<'a>) -> Result<(), Error> { + let crate_name = self.new_crate_name(); + let mut command = self.rustc.command(); + command + .arg("--crate-name") + .arg(&crate_name) + .arg("--crate-type=lib") + .arg("--out-dir") + .arg(&self.out_dir) + .arg("--emit=llvm-ir"); + + if let Some(edition) = self.edition.as_ref() { + command.arg("--edition").arg(edition); + } + + if let Some(target) = self.target.as_ref() { + command.arg("--target").arg(target); + } + + command.args(&self.rustflags); + + command.arg("-").stdin(Stdio::piped()); + let mut child = try!(command.spawn().map_err(error::from_io)); + let mut stdin = child.stdin.take().expect("rustc stdin"); + + try!(stdin.write_fmt(source).map_err(error::from_io)); + drop(stdin); + + match child.wait() { + Ok(status) if status.success() => { + // Try to remove the output file so it doesn't look like a build product for + // systems like bazel -- but this is best-effort, so we can ignore failure. + // The probe itself is already considered successful at this point. + let mut file = self.out_dir.join(crate_name); + file.set_extension("ll"); + let _ = fs::remove_file(file); + + Ok(()) + } + Ok(status) => Err(error::from_exit(status)), + Err(error) => Err(error::from_io(error)), + } + } + + fn probe<'a>(&self, code: Arguments<'a>) -> bool { + let result = if self.no_std { + self.probe_fmt(format_args!("#![no_std]\n{}", code)) + } else { + self.probe_fmt(code) + }; + result.is_ok() + } + + /// Tests whether the given code can be compiled as a Rust library. + /// + /// This will only return `Ok` if the compiler ran and exited successfully, + /// per `ExitStatus::success()`. + /// The code is passed to the compiler exactly as-is, notably not even + /// adding the [`#![no_std]`][Self::no_std] attribute like other probes. + /// + /// Raw probes are useful for testing functionality that's not yet covered + /// by the rest of the `AutoCfg` API. For example, the following attribute + /// **must** be used at the crate level, so it wouldn't work within the code + /// templates used by other `probe_*` methods. + /// + /// ``` + /// # extern crate autocfg; + /// # // Normally, cargo will set `OUT_DIR` for build scripts. + /// # let exe = std::env::current_exe().unwrap(); + /// # std::env::set_var("OUT_DIR", exe.parent().unwrap()); + /// let ac = autocfg::new(); + /// assert!(ac.probe_raw("#![no_builtins]").is_ok()); + /// ``` + /// + /// Rust nightly features could be tested as well -- ideally including a + /// code sample to ensure the unstable feature still works as expected. + /// For example, `slice::group_by` was renamed to `chunk_by` when it was + /// stabilized, even though the feature name was unchanged, so testing the + /// `#![feature(..)]` alone wouldn't reveal that. For larger snippets, + /// [`include_str!`] may be useful to load them from separate files. + /// + /// ``` + /// # extern crate autocfg; + /// # // Normally, cargo will set `OUT_DIR` for build scripts. + /// # let exe = std::env::current_exe().unwrap(); + /// # std::env::set_var("OUT_DIR", exe.parent().unwrap()); + /// let ac = autocfg::new(); + /// let code = r#" + /// #![feature(slice_group_by)] + /// pub fn probe(slice: &[i32]) -> impl Iterator { + /// slice.group_by(|a, b| a == b) + /// } + /// "#; + /// if ac.probe_raw(code).is_ok() { + /// autocfg::emit("has_slice_group_by"); + /// } + /// ``` + pub fn probe_raw(&self, code: &str) -> Result<(), Error> { + self.probe_fmt(format_args!("{}", code)) + } + + /// Tests whether the given sysroot crate can be used. + /// + /// The test code is subject to change, but currently looks like: + /// + /// ```ignore + /// extern crate CRATE as probe; + /// ``` + pub fn probe_sysroot_crate(&self, name: &str) -> bool { + // Note: `as _` wasn't stabilized until Rust 1.33 + self.probe(format_args!("extern crate {} as probe;", name)) + } + + /// Emits a config value `has_CRATE` if `probe_sysroot_crate` returns true. + pub fn emit_sysroot_crate(&self, name: &str) { + let cfg_flag = format!("has_{}", mangle(name)); + emit_possibility(&cfg_flag); + if self.probe_sysroot_crate(name) { + emit(&cfg_flag); + } + } + + /// Tests whether the given path can be used. + /// + /// The test code is subject to change, but currently looks like: + /// + /// ```ignore + /// pub use PATH; + /// ``` + pub fn probe_path(&self, path: &str) -> bool { + self.probe(format_args!("pub use {};", path)) + } + + /// Emits a config value `has_PATH` if `probe_path` returns true. + /// + /// Any non-identifier characters in the `path` will be replaced with + /// `_` in the generated config value. + pub fn emit_has_path(&self, path: &str) { + self.emit_path_cfg(path, &format!("has_{}", mangle(path))); + } + + /// Emits the given `cfg` value if `probe_path` returns true. + pub fn emit_path_cfg(&self, path: &str, cfg: &str) { + emit_possibility(cfg); + if self.probe_path(path) { + emit(cfg); + } + } + + /// Tests whether the given trait can be used. + /// + /// The test code is subject to change, but currently looks like: + /// + /// ```ignore + /// pub trait Probe: TRAIT + Sized {} + /// ``` + pub fn probe_trait(&self, name: &str) -> bool { + self.probe(format_args!("pub trait Probe: {} + Sized {{}}", name)) + } + + /// Emits a config value `has_TRAIT` if `probe_trait` returns true. + /// + /// Any non-identifier characters in the trait `name` will be replaced with + /// `_` in the generated config value. + pub fn emit_has_trait(&self, name: &str) { + self.emit_trait_cfg(name, &format!("has_{}", mangle(name))); + } + + /// Emits the given `cfg` value if `probe_trait` returns true. + pub fn emit_trait_cfg(&self, name: &str, cfg: &str) { + emit_possibility(cfg); + if self.probe_trait(name) { + emit(cfg); + } + } + + /// Tests whether the given type can be used. + /// + /// The test code is subject to change, but currently looks like: + /// + /// ```ignore + /// pub type Probe = TYPE; + /// ``` + pub fn probe_type(&self, name: &str) -> bool { + self.probe(format_args!("pub type Probe = {};", name)) + } + + /// Emits a config value `has_TYPE` if `probe_type` returns true. + /// + /// Any non-identifier characters in the type `name` will be replaced with + /// `_` in the generated config value. + pub fn emit_has_type(&self, name: &str) { + self.emit_type_cfg(name, &format!("has_{}", mangle(name))); + } + + /// Emits the given `cfg` value if `probe_type` returns true. + pub fn emit_type_cfg(&self, name: &str, cfg: &str) { + emit_possibility(cfg); + if self.probe_type(name) { + emit(cfg); + } + } + + /// Tests whether the given expression can be used. + /// + /// The test code is subject to change, but currently looks like: + /// + /// ```ignore + /// pub fn probe() { let _ = EXPR; } + /// ``` + pub fn probe_expression(&self, expr: &str) -> bool { + self.probe(format_args!("pub fn probe() {{ let _ = {}; }}", expr)) + } + + /// Emits the given `cfg` value if `probe_expression` returns true. + pub fn emit_expression_cfg(&self, expr: &str, cfg: &str) { + emit_possibility(cfg); + if self.probe_expression(expr) { + emit(cfg); + } + } + + /// Tests whether the given constant expression can be used. + /// + /// The test code is subject to change, but currently looks like: + /// + /// ```ignore + /// pub const PROBE: () = ((), EXPR).0; + /// ``` + pub fn probe_constant(&self, expr: &str) -> bool { + self.probe(format_args!("pub const PROBE: () = ((), {}).0;", expr)) + } + + /// Emits the given `cfg` value if `probe_constant` returns true. + pub fn emit_constant_cfg(&self, expr: &str, cfg: &str) { + emit_possibility(cfg); + if self.probe_constant(expr) { + emit(cfg); + } + } +} + +fn mangle(s: &str) -> String { + s.chars() + .map(|c| match c { + 'A'...'Z' | 'a'...'z' | '0'...'9' => c, + _ => '_', + }) + .collect() +} + +fn dir_contains_target( + target: &Option, + dir: &Path, + cargo_target_dir: Option, +) -> bool { + target + .as_ref() + .and_then(|target| { + dir.to_str().and_then(|dir| { + let mut cargo_target_dir = cargo_target_dir + .map(PathBuf::from) + .unwrap_or_else(|| PathBuf::from("target")); + cargo_target_dir.push(target); + + cargo_target_dir + .to_str() + .map(|cargo_target_dir| dir.contains(cargo_target_dir)) + }) + }) + .unwrap_or(false) +} + +fn rustflags(target: &Option, dir: &Path) -> Vec { + // Starting with rust-lang/cargo#9601, shipped in Rust 1.55, Cargo always sets + // CARGO_ENCODED_RUSTFLAGS for any host/target build script invocation. This + // includes any source of flags, whether from the environment, toml config, or + // whatever may come in the future. The value is either an empty string, or a + // list of arguments separated by the ASCII unit separator (US), 0x1f. + if let Ok(a) = env::var("CARGO_ENCODED_RUSTFLAGS") { + return if a.is_empty() { + Vec::new() + } else { + a.split('\x1f').map(str::to_string).collect() + }; + } + + // Otherwise, we have to take a more heuristic approach, and we don't + // support values from toml config at all. + // + // Cargo only applies RUSTFLAGS for building TARGET artifact in + // cross-compilation environment. Sadly, we don't have a way to detect + // when we're building HOST artifact in a cross-compilation environment, + // so for now we only apply RUSTFLAGS when cross-compiling an artifact. + // + // See https://github.com/cuviper/autocfg/pull/10#issuecomment-527575030. + if *target != env::var_os("HOST") + || dir_contains_target(target, dir, env::var_os("CARGO_TARGET_DIR")) + { + if let Ok(rustflags) = env::var("RUSTFLAGS") { + // This is meant to match how cargo handles the RUSTFLAGS environment variable. + // See https://github.com/rust-lang/cargo/blob/69aea5b6f69add7c51cca939a79644080c0b0ba0/src/cargo/core/compiler/build_context/target_info.rs#L434-L441 + return rustflags + .split(' ') + .map(str::trim) + .filter(|s| !s.is_empty()) + .map(str::to_string) + .collect(); + } + } + + Vec::new() +} + +/// Generates a numeric ID to use in probe crate names. +/// +/// This attempts to be random, within the constraints of Rust 1.0 and no dependencies. +fn new_uuid() -> u64 { + const FNV_OFFSET_BASIS: u64 = 0xcbf2_9ce4_8422_2325; + const FNV_PRIME: u64 = 0x100_0000_01b3; + + // This set should have an actual random hasher. + let set: std::collections::HashSet = (0..256).collect(); + + // Feed the `HashSet`-shuffled order into FNV-1a. + let mut hash: u64 = FNV_OFFSET_BASIS; + for x in set { + hash = (hash ^ x).wrapping_mul(FNV_PRIME); + } + hash +} diff --git a/tools/vendor/autocfg/src/rustc.rs b/tools/vendor/autocfg/src/rustc.rs new file mode 100644 index 0000000000..e324ed18c1 --- /dev/null +++ b/tools/vendor/autocfg/src/rustc.rs @@ -0,0 +1,89 @@ +use std::env; +use std::ffi::OsString; +use std::path::PathBuf; +use std::process::Command; + +use super::error::Error; +use super::version::Version; + +#[derive(Clone, Debug)] +pub struct Rustc { + rustc: PathBuf, + rustc_wrapper: Option, + rustc_workspace_wrapper: Option, +} + +impl Rustc { + pub fn new() -> Self { + Rustc { + rustc: env::var_os("RUSTC") + .unwrap_or_else(|| "rustc".into()) + .into(), + rustc_wrapper: get_rustc_wrapper(false), + rustc_workspace_wrapper: get_rustc_wrapper(true), + } + } + + /// Build the command with possible wrappers. + pub fn command(&self) -> Command { + let mut rustc = self + .rustc_wrapper + .iter() + .chain(self.rustc_workspace_wrapper.iter()) + .chain(Some(&self.rustc)); + let mut command = Command::new(rustc.next().unwrap()); + for arg in rustc { + command.arg(arg); + } + command + } + + /// Try to get the `rustc` version. + pub fn version(&self) -> Result { + // Some wrappers like clippy-driver don't pass through version commands, + // so we try to fall back to combinations without each wrapper. + macro_rules! try_version { + ($command:expr) => { + if let Ok(value) = Version::from_command($command) { + return Ok(value); + } + }; + } + + let rustc = &self.rustc; + if let Some(ref rw) = self.rustc_wrapper { + if let Some(ref rww) = self.rustc_workspace_wrapper { + try_version!(Command::new(rw).args(&[rww, rustc])); + } + try_version!(Command::new(rw).arg(rustc)); + } + if let Some(ref rww) = self.rustc_workspace_wrapper { + try_version!(Command::new(rww).arg(rustc)); + } + Version::from_command(&mut Command::new(rustc)) + } +} + +fn get_rustc_wrapper(workspace: bool) -> Option { + // We didn't really know whether the workspace wrapper is applicable until Cargo started + // deliberately setting or unsetting it in rust-lang/cargo#9601. We'll use the encoded + // rustflags as a proxy for that change for now, but we could instead check version 1.55. + if workspace && env::var_os("CARGO_ENCODED_RUSTFLAGS").is_none() { + return None; + } + + let name = if workspace { + "RUSTC_WORKSPACE_WRAPPER" + } else { + "RUSTC_WRAPPER" + }; + + if let Some(wrapper) = env::var_os(name) { + // NB: `OsStr` didn't get `len` or `is_empty` until 1.9. + if wrapper != OsString::new() { + return Some(wrapper.into()); + } + } + + None +} diff --git a/tools/vendor/autocfg/src/tests.rs b/tools/vendor/autocfg/src/tests.rs new file mode 100644 index 0000000000..19188c12ca --- /dev/null +++ b/tools/vendor/autocfg/src/tests.rs @@ -0,0 +1,54 @@ +use std::path::Path; + +#[test] +fn version_cmp() { + use super::version::Version; + let v123 = Version::new(1, 2, 3); + + assert!(Version::new(1, 0, 0) < v123); + assert!(Version::new(1, 2, 2) < v123); + assert!(Version::new(1, 2, 3) == v123); + assert!(Version::new(1, 2, 4) > v123); + assert!(Version::new(1, 10, 0) > v123); + assert!(Version::new(2, 0, 0) > v123); +} + +#[test] +fn dir_does_not_contain_target() { + assert!(!super::dir_contains_target( + &Some("x86_64-unknown-linux-gnu".into()), + Path::new("/project/target/debug/build/project-ea75983148559682/out"), + None, + )); +} + +#[test] +fn dir_does_contain_target() { + assert!(super::dir_contains_target( + &Some("x86_64-unknown-linux-gnu".into()), + Path::new( + "/project/target/x86_64-unknown-linux-gnu/debug/build/project-0147aca016480b9d/out" + ), + None, + )); +} + +#[test] +fn dir_does_not_contain_target_with_custom_target_dir() { + assert!(!super::dir_contains_target( + &Some("x86_64-unknown-linux-gnu".into()), + Path::new("/project/custom/debug/build/project-ea75983148559682/out"), + Some("custom".into()), + )); +} + +#[test] +fn dir_does_contain_target_with_custom_target_dir() { + assert!(super::dir_contains_target( + &Some("x86_64-unknown-linux-gnu".into()), + Path::new( + "/project/custom/x86_64-unknown-linux-gnu/debug/build/project-0147aca016480b9d/out" + ), + Some("custom".into()), + )); +} diff --git a/tools/vendor/autocfg/src/version.rs b/tools/vendor/autocfg/src/version.rs new file mode 100644 index 0000000000..9cf5b121fe --- /dev/null +++ b/tools/vendor/autocfg/src/version.rs @@ -0,0 +1,65 @@ +use std::process::Command; +use std::str; + +use super::{error, Error}; + +/// A version structure for making relative comparisons. +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Version { + major: usize, + minor: usize, + patch: usize, +} + +impl Version { + /// Creates a `Version` instance for a specific `major.minor.patch` version. + pub fn new(major: usize, minor: usize, patch: usize) -> Self { + Version { + major: major, + minor: minor, + patch: patch, + } + } + + pub fn from_command(command: &mut Command) -> Result { + // Get rustc's verbose version + let output = try!(command + .args(&["--version", "--verbose"]) + .output() + .map_err(error::from_io)); + if !output.status.success() { + return Err(error::from_str("could not execute rustc")); + } + let output = try!(str::from_utf8(&output.stdout).map_err(error::from_utf8)); + + // Find the release line in the verbose version output. + let release = match output.lines().find(|line| line.starts_with("release: ")) { + Some(line) => &line["release: ".len()..], + None => return Err(error::from_str("could not find rustc release")), + }; + + // Strip off any extra channel info, e.g. "-beta.N", "-nightly" + let version = match release.find('-') { + Some(i) => &release[..i], + None => release, + }; + + // Split the version into semver components. + let mut iter = version.splitn(3, '.'); + let major = try!(iter + .next() + .ok_or_else(|| error::from_str("missing major version"))); + let minor = try!(iter + .next() + .ok_or_else(|| error::from_str("missing minor version"))); + let patch = try!(iter + .next() + .ok_or_else(|| error::from_str("missing patch version"))); + + Ok(Version::new( + try!(major.parse().map_err(error::from_num)), + try!(minor.parse().map_err(error::from_num)), + try!(patch.parse().map_err(error::from_num)), + )) + } +} diff --git a/tools/vendor/autocfg/tests/no_std.rs b/tools/vendor/autocfg/tests/no_std.rs new file mode 100644 index 0000000000..05ab445af3 --- /dev/null +++ b/tools/vendor/autocfg/tests/no_std.rs @@ -0,0 +1,28 @@ +extern crate autocfg; + +use std::env; + +mod support; + +/// Tests that we can control the use of `#![no_std]`. +#[test] +fn no_std() { + // Clear the CI `TARGET`, if any, so we're just dealing with the + // host target which always has `std` available. + env::remove_var("TARGET"); + + // Use the same path as this test binary. + let out = support::out_dir(); + + let mut ac = autocfg::AutoCfg::with_dir(out.as_ref()).unwrap(); + assert!(!ac.no_std()); + assert!(ac.probe_path("std::mem")); + + // `#![no_std]` was stabilized in Rust 1.6 + if ac.probe_rustc_version(1, 6) { + ac.set_no_std(true); + assert!(ac.no_std()); + assert!(!ac.probe_path("std::mem")); + assert!(ac.probe_path("core::mem")); + } +} diff --git a/tools/vendor/autocfg/tests/rustflags.rs b/tools/vendor/autocfg/tests/rustflags.rs new file mode 100644 index 0000000000..8038a0c4d5 --- /dev/null +++ b/tools/vendor/autocfg/tests/rustflags.rs @@ -0,0 +1,34 @@ +extern crate autocfg; + +use std::env; + +mod support; + +/// Tests that autocfg uses the RUSTFLAGS or CARGO_ENCODED_RUSTFLAGS +/// environment variables when running rustc. +#[test] +fn test_with_sysroot() { + let dir = support::exe_dir(); + let out = support::out_dir(); + + // If we have encoded rustflags, they take precedence, even if empty. + env::set_var("CARGO_ENCODED_RUSTFLAGS", ""); + env::set_var("RUSTFLAGS", &format!("-L {}", dir.display())); + let ac = autocfg::AutoCfg::with_dir(out.as_ref()).unwrap(); + assert!(ac.probe_sysroot_crate("std")); + assert!(!ac.probe_sysroot_crate("autocfg")); + + // Now try again with useful encoded args. + env::set_var( + "CARGO_ENCODED_RUSTFLAGS", + &format!("-L\x1f{}", dir.display()), + ); + let ac = autocfg::AutoCfg::with_dir(out.as_ref()).unwrap(); + assert!(ac.probe_sysroot_crate("autocfg")); + + // Try the old-style RUSTFLAGS, ensuring HOST != TARGET. + env::remove_var("CARGO_ENCODED_RUSTFLAGS"); + env::set_var("HOST", "lol"); + let ac = autocfg::AutoCfg::with_dir(out.as_ref()).unwrap(); + assert!(ac.probe_sysroot_crate("autocfg")); +} diff --git a/tools/vendor/autocfg/tests/support/mod.rs b/tools/vendor/autocfg/tests/support/mod.rs new file mode 100644 index 0000000000..f50e7176f5 --- /dev/null +++ b/tools/vendor/autocfg/tests/support/mod.rs @@ -0,0 +1,21 @@ +use std::borrow::Cow; +use std::env; +use std::path::{Path, PathBuf}; + +/// The directory containing this test binary. +pub fn exe_dir() -> PathBuf { + let exe = env::current_exe().unwrap(); + exe.parent().unwrap().to_path_buf() +} + +/// The directory to use for test probes. +pub fn out_dir() -> Cow<'static, Path> { + if let Some(tmpdir) = option_env!("CARGO_TARGET_TMPDIR") { + Cow::Borrowed(tmpdir.as_ref()) + } else if let Some(tmpdir) = env::var_os("TESTS_TARGET_DIR") { + Cow::Owned(tmpdir.into()) + } else { + // Use the same path as this test binary. + Cow::Owned(exe_dir()) + } +} diff --git a/tools/vendor/autocfg/tests/tests.rs b/tools/vendor/autocfg/tests/tests.rs new file mode 100644 index 0000000000..bd069ee472 --- /dev/null +++ b/tools/vendor/autocfg/tests/tests.rs @@ -0,0 +1,189 @@ +extern crate autocfg; + +use autocfg::AutoCfg; + +mod support; + +fn core_std(ac: &AutoCfg, path: &str) -> String { + let krate = if ac.no_std() { "core" } else { "std" }; + format!("{}::{}", krate, path) +} + +fn assert_std(ac: &AutoCfg, probe_result: bool) { + assert_eq!(!ac.no_std(), probe_result); +} + +fn assert_min(ac: &AutoCfg, major: usize, minor: usize, probe_result: bool) { + assert_eq!(ac.probe_rustc_version(major, minor), probe_result); +} + +fn autocfg_for_test() -> AutoCfg { + AutoCfg::with_dir(support::out_dir().as_ref()).unwrap() +} + +#[test] +fn autocfg_version() { + let ac = autocfg_for_test(); + assert!(ac.probe_rustc_version(1, 0)); +} + +#[test] +fn probe_add() { + let ac = autocfg_for_test(); + let add = core_std(&ac, "ops::Add"); + let add_rhs = add.clone() + ""; + let add_rhs_output = add.clone() + ""; + let dyn_add_rhs_output = "dyn ".to_string() + &*add_rhs_output; + assert!(ac.probe_path(&add)); + assert!(ac.probe_trait(&add)); + assert!(ac.probe_trait(&add_rhs)); + assert!(ac.probe_trait(&add_rhs_output)); + assert_min(&ac, 1, 27, ac.probe_type(&dyn_add_rhs_output)); +} + +#[test] +fn probe_as_ref() { + let ac = autocfg_for_test(); + let as_ref = core_std(&ac, "convert::AsRef"); + let as_ref_str = as_ref.clone() + ""; + let dyn_as_ref_str = "dyn ".to_string() + &*as_ref_str; + assert!(ac.probe_path(&as_ref)); + assert!(ac.probe_trait(&as_ref_str)); + assert!(ac.probe_type(&as_ref_str)); + assert_min(&ac, 1, 27, ac.probe_type(&dyn_as_ref_str)); +} + +#[test] +fn probe_i128() { + let ac = autocfg_for_test(); + let i128_path = core_std(&ac, "i128"); + assert_min(&ac, 1, 26, ac.probe_path(&i128_path)); + assert_min(&ac, 1, 26, ac.probe_type("i128")); +} + +#[test] +fn probe_sum() { + let ac = autocfg_for_test(); + let sum = core_std(&ac, "iter::Sum"); + let sum_i32 = sum.clone() + ""; + let dyn_sum_i32 = "dyn ".to_string() + &*sum_i32; + assert_min(&ac, 1, 12, ac.probe_path(&sum)); + assert_min(&ac, 1, 12, ac.probe_trait(&sum)); + assert_min(&ac, 1, 12, ac.probe_trait(&sum_i32)); + assert_min(&ac, 1, 12, ac.probe_type(&sum_i32)); + assert_min(&ac, 1, 27, ac.probe_type(&dyn_sum_i32)); +} + +#[test] +fn probe_std() { + let ac = autocfg_for_test(); + assert_std(&ac, ac.probe_sysroot_crate("std")); +} + +#[test] +fn probe_alloc() { + let ac = autocfg_for_test(); + assert_min(&ac, 1, 36, ac.probe_sysroot_crate("alloc")); +} + +#[test] +fn probe_bad_sysroot_crate() { + let ac = autocfg_for_test(); + assert!(!ac.probe_sysroot_crate("doesnt_exist")); +} + +#[test] +fn probe_no_std() { + let ac = autocfg_for_test(); + assert!(ac.probe_type("i32")); + assert!(ac.probe_type("[i32]")); + assert_std(&ac, ac.probe_type("Vec")); +} + +#[test] +fn probe_expression() { + let ac = autocfg_for_test(); + assert!(ac.probe_expression(r#""test".trim_left()"#)); + assert_min(&ac, 1, 30, ac.probe_expression(r#""test".trim_start()"#)); + assert_std(&ac, ac.probe_expression("[1, 2, 3].to_vec()")); +} + +#[test] +fn probe_constant() { + let ac = autocfg_for_test(); + assert!(ac.probe_constant("1 + 2 + 3")); + assert_min( + &ac, + 1, + 33, + ac.probe_constant("{ let x = 1 + 2 + 3; x * x }"), + ); + assert_min(&ac, 1, 39, ac.probe_constant(r#""test".len()"#)); +} + +#[test] +fn probe_raw() { + let ac = autocfg_for_test(); + let prefix = if ac.no_std() { "#![no_std]\n" } else { "" }; + let f = |s| format!("{}{}", prefix, s); + + // This attribute **must** be used at the crate level. + assert!(ac.probe_raw(&f("#![no_builtins]")).is_ok()); + + assert!(ac.probe_raw(&f("#![deny(dead_code)] fn x() {}")).is_err()); + assert!(ac.probe_raw(&f("#![allow(dead_code)] fn x() {}")).is_ok()); + assert!(ac + .probe_raw(&f("#![deny(dead_code)] pub fn x() {}")) + .is_ok()); +} + +#[test] +fn probe_cleanup() { + let dir = support::out_dir().join("autocfg_test_probe_cleanup"); + std::fs::create_dir(&dir).unwrap(); + + let ac = AutoCfg::with_dir(&dir).unwrap(); + assert!(ac.probe_type("i32")); + + // NB: this is not `remove_dir_all`, so it will only work if the directory + // is empty -- i.e. the probe should have removed any output files. + std::fs::remove_dir(&dir).unwrap(); +} + +#[test] +fn editions() { + let mut ac = autocfg_for_test(); + assert!(ac.edition().is_none()); + assert!(ac.probe_raw("").is_ok()); + + for (edition, minor) in vec![(2015, 27), (2018, 31), (2021, 56), (2024, 85)] { + let edition = edition.to_string(); + ac.set_edition(Some(edition.clone())); + assert_eq!(ac.edition(), Some(&*edition)); + assert_min(&ac, 1, minor, ac.probe_raw("").is_ok()); + } + + ac.set_edition(Some("invalid".into())); + assert_eq!(ac.edition(), Some("invalid")); + assert!(ac.probe_raw("").is_err()); + + ac.set_edition(None); + assert!(ac.edition().is_none()); + assert!(ac.probe_raw("").is_ok()); +} + +#[test] +fn edition_keyword_try() { + let mut ac = autocfg_for_test(); + + if ac.probe_rustc_version(1, 27) { + ac.set_edition(Some(2015.to_string())); + } + assert!(ac.probe_expression("{ let try = 0; try }")); + + if ac.probe_rustc_version(1, 31) { + ac.set_edition(Some(2018.to_string())); + assert!(!ac.probe_expression("{ let try = 0; try }")); + assert!(ac.probe_expression("{ let r#try = 0; r#try }")); + } +} diff --git a/tools/vendor/autocfg/tests/wrap_ignored b/tools/vendor/autocfg/tests/wrap_ignored new file mode 100755 index 0000000000..5e577d0b26 --- /dev/null +++ b/tools/vendor/autocfg/tests/wrap_ignored @@ -0,0 +1,12 @@ +#!/bin/bash + +for arg in "$@"; do + case "$arg" in + # Add our own version so we can check that the wrapper is used for that. + "--version") echo "release: 12345.6789.0" ;; + # Read all input so the writer doesn't get EPIPE when we exit. + "-") read -d "" PROBE ;; + esac +done + +exit 0 diff --git a/tools/vendor/autocfg/tests/wrappers.rs b/tools/vendor/autocfg/tests/wrappers.rs new file mode 100644 index 0000000000..cacca71aae --- /dev/null +++ b/tools/vendor/autocfg/tests/wrappers.rs @@ -0,0 +1,56 @@ +extern crate autocfg; + +use std::env; + +mod support; + +/// Tests that autocfg uses the RUSTC_WRAPPER and/or RUSTC_WORKSPACE_WRAPPER +/// environment variables when running rustc. +#[test] +#[cfg(unix)] // we're using system binaries as wrappers +fn test_wrappers() { + fn set(name: &str, value: Option) { + match value { + Some(true) => env::set_var(name, "/usr/bin/env"), + Some(false) => env::set_var(name, "/bin/false"), + None => env::remove_var(name), + } + } + + let out = support::out_dir(); + + // This is used as a heuristic to detect rust-lang/cargo#9601. + env::set_var("CARGO_ENCODED_RUSTFLAGS", ""); + + // No wrapper, a good pass-through wrapper, and a bad wrapper. + let variants = [None, Some(true), Some(false)]; + + for &workspace in &variants { + for &rustc in &variants { + set("RUSTC_WRAPPER", rustc); + set("RUSTC_WORKSPACE_WRAPPER", workspace); + + let ac = autocfg::AutoCfg::with_dir(out.as_ref()).unwrap(); + if rustc == Some(false) || workspace == Some(false) { + // Everything should fail with bad wrappers. + assert!(!ac.probe_type("usize")); + } else { + // Try known good and bad types for the wrapped rustc. + assert!(ac.probe_type("usize")); + assert!(!ac.probe_type("mesize")); + } + // Either way, we should have found the inner rustc version. + assert!(ac.probe_rustc_version(1, 0)); + } + } + + // Finally, make sure that `RUSTC_WRAPPER` is applied outermost + // by using something that doesn't pass through at all. + env::set_var("RUSTC_WRAPPER", "./tests/wrap_ignored"); + env::set_var("RUSTC_WORKSPACE_WRAPPER", "/bin/false"); + let ac = autocfg::AutoCfg::with_dir(out.as_ref()).unwrap(); + assert!(ac.probe_type("mesize")); // anything goes! + + // Make sure we also got the version from that wrapper. + assert!(ac.probe_rustc_version(12345, 6789)); +} diff --git a/tools/vendor/bit-set/.cargo-checksum.json b/tools/vendor/bit-set/.cargo-checksum.json new file mode 100644 index 0000000000..f80ba3df24 --- /dev/null +++ b/tools/vendor/bit-set/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo-rdme.toml":"62dbf3711ece6a2f162946e36711b233c9dbb6e7077c9cabb357de334e27aee6",".cargo_vcs_info.json":"4d7b1f1a687790fc47cab4d052260d6262e91193a607b69d809b7d02c1eb2b37",".github/workflows/rust.yml":"eb99f7eac4bf67c130df7f5e7f4e7c7b94caba826107f03e0d231476caa2b8a4","Cargo.toml":"f718d1e3b585881b737a689fdcc34b7df64818b14093907d5c4546e29692ea8f","Cargo.toml.orig":"3fe6b7dbe9f63e13ca3be05af54c066386bbf34c05f0bbe003ce9badd9fbb9df","LICENSE-APACHE":"8173d5c29b4f956d532781d2b86e4e30f83e6b7878dce18c919451d6ba707c90","LICENSE-MIT":"f51ac2c59a222f7476ce507ca879960e2b64ea64bb2786eefdbeb7b0b538d1b7","README.md":"b2301e8a7953cc006062459041fc9edaa9e31ad5fc2aa2009ed742380624e4c5","RELEASES.md":"78721919b25f13b36004ff40acde09fb1beaaa4e1d721fe30c7b1eb6f9325190","benches/bench.rs":"b044b6aa529e73d921ade54b85dd1c07543a588999ba69192714774abf80edbf","src/lib.rs":"c090ea3ef7cd0367c8e157418c5d51072a474f660b1c74c94fa68be696a079e0"},"package":"08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"} \ No newline at end of file diff --git a/tools/vendor/bit-set/.cargo-rdme.toml b/tools/vendor/bit-set/.cargo-rdme.toml new file mode 100644 index 0000000000..92e26ae187 --- /dev/null +++ b/tools/vendor/bit-set/.cargo-rdme.toml @@ -0,0 +1 @@ +line-terminator = "lf" diff --git a/tools/vendor/bit-set/.cargo_vcs_info.json b/tools/vendor/bit-set/.cargo_vcs_info.json new file mode 100644 index 0000000000..cf57405954 --- /dev/null +++ b/tools/vendor/bit-set/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "2b8861d23b53d63d326d2694ddabde6e7b19fbd0" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/tools/vendor/bit-set/.github/workflows/rust.yml b/tools/vendor/bit-set/.github/workflows/rust.yml new file mode 100644 index 0000000000..457f307cab --- /dev/null +++ b/tools/vendor/bit-set/.github/workflows/rust.yml @@ -0,0 +1,89 @@ +name: Rust + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose + - name: Run tests for serde feature + run: cargo test --features serde --verbose + + miri: + name: "Miri" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Miri + run: | + rustup toolchain install nightly --component miri + rustup override set nightly + cargo miri setup + - name: Test with Miri + run: MIRIFLAGS=-Zmiri-strict-provenance cargo miri test + - name: Run tests for serde feature + run: MIRIFLAGS=-Zmiri-strict-provenance cargo miri test --features serde + + fmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + components: rustfmt + override: true + - uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + components: clippy + override: true + - uses: actions-rs/clippy-check@v1 + env: + PWD: ${{ env.GITHUB_WORKSPACE }} + with: + token: ${{ secrets.GITHUB_TOKEN }} + args: --workspace --tests --examples + + + docs: + runs-on: ubuntu-latest + env: + RUSTDOCFLAGS: -Dwarnings + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + components: rust-docs + override: true + - uses: swatinem/rust-cache@v1 + - uses: actions-rs/cargo@v1 + with: + command: doc + args: --workspace --no-deps diff --git a/tools/vendor/bit-set/Cargo.toml b/tools/vendor/bit-set/Cargo.toml new file mode 100644 index 0000000000..f0073ab4d8 --- /dev/null +++ b/tools/vendor/bit-set/Cargo.toml @@ -0,0 +1,62 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2015" +name = "bit-set" +version = "0.8.0" +authors = ["Alexis Beingessner "] +build = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "A set of bits" +homepage = "https://github.com/contain-rs/bit-set" +documentation = "https://docs.rs/bit-set/" +readme = "README.md" +keywords = [ + "data-structures", + "bitset", +] +license = "Apache-2.0 OR MIT" +repository = "https://github.com/contain-rs/bit-set" + +[lib] +name = "bit_set" +path = "src/lib.rs" + +[[bench]] +name = "bench" +path = "benches/bench.rs" + +[dependencies.bit-vec] +version = "0.8.0" +default-features = false + +[dependencies.serde] +version = "1.0" +features = ["derive"] +optional = true + +[dev-dependencies.rand] +version = "0.8" + +[dev-dependencies.serde_json] +version = "1.0" + +[features] +default = ["std"] +serde = [ + "dep:serde", + "bit-vec/serde", +] +std = ["bit-vec/std"] diff --git a/tools/vendor/bit-set/Cargo.toml.orig b/tools/vendor/bit-set/Cargo.toml.orig new file mode 100644 index 0000000000..9c343c9799 --- /dev/null +++ b/tools/vendor/bit-set/Cargo.toml.orig @@ -0,0 +1,28 @@ +[package] +name = "bit-set" +version = "0.8.0" +authors = ["Alexis Beingessner "] +license = "Apache-2.0 OR MIT" +description = "A set of bits" +repository = "https://github.com/contain-rs/bit-set" +homepage = "https://github.com/contain-rs/bit-set" +documentation = "https://docs.rs/bit-set/" +keywords = ["data-structures", "bitset"] +readme = "README.md" +edition = "2015" + +[dependencies] +serde = { version = "1.0", features = ["derive"], optional = true } + +[dependencies.bit-vec] +version = "0.8.0" +default-features = false + +[dev-dependencies] +rand = "0.8" +serde_json = "1.0" + +[features] +default = ["std"] +std = ["bit-vec/std"] +serde = ["dep:serde", "bit-vec/serde"] diff --git a/tools/vendor/bit-set/LICENSE-APACHE b/tools/vendor/bit-set/LICENSE-APACHE new file mode 100644 index 0000000000..11069edd79 --- /dev/null +++ b/tools/vendor/bit-set/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/tools/vendor/bit-set/LICENSE-MIT b/tools/vendor/bit-set/LICENSE-MIT new file mode 100644 index 0000000000..40a969bb9a --- /dev/null +++ b/tools/vendor/bit-set/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2023 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/tools/vendor/bit-set/README.md b/tools/vendor/bit-set/README.md new file mode 100644 index 0000000000..28a9adb936 --- /dev/null +++ b/tools/vendor/bit-set/README.md @@ -0,0 +1,111 @@ +

+

bit-set

+

+ A compact set of bits. +

+

+ +[![crates.io][crates.io shield]][crates.io link] +[![Documentation][docs.rs badge]][docs.rs link] +![Rust CI][github ci badge] +[![rustc 1.0+]][Rust 1.0] +
+
+[![Dependency Status][deps.rs status]][deps.rs link] +[![Download Status][shields.io download count]][crates.io link] + +

+
+ +[crates.io shield]: https://img.shields.io/crates/v/bit-set?label=latest +[crates.io link]: https://crates.io/crates/bit-set +[docs.rs badge]: https://docs.rs/bit-set/badge.svg?version=0.8.0 +[docs.rs link]: https://docs.rs/bit-set/0.8.0/bit_set/ +[github ci badge]: https://github.com/contain-rs/linked-hash-map/workflows/Rust/badge.svg?branch=master +[rustc 1.0+]: https://img.shields.io/badge/rustc-1.0%2B-blue.svg +[Rust 1.0]: https://blog.rust-lang.org/2015/05/15/Rust-1.0.html +[deps.rs status]: https://deps.rs/crate/bit-set/0.8.0/status.svg +[deps.rs link]: https://deps.rs/crate/bit-set/0.8.0 +[shields.io download count]: https://img.shields.io/crates/d/bit-set.svg + +## Usage + +Add this to your Cargo.toml: + +```toml +[dependencies] +bit-set = "0.8" +``` + +Since Rust 2018, `extern crate` is no longer mandatory. If your edition is old (Rust 2015), +add this to your crate root: + +```rust +extern crate bit_set; +``` + +If you want to use `serde`, enable it with the `serde` feature: + +```toml +[dependencies] +bit-set = { version = "0.8", features = ["serde"] } +``` + +If you want to use bit-set in a program that has `#![no_std]`, just drop default features: + +```toml +[dependencies] +bit-set = { version = "0.8", default-features = false } +``` + + + +### Description + +An implementation of a set using a bit vector as an underlying +representation for holding unsigned numerical elements. + +It should also be noted that the amount of storage necessary for holding a +set of objects is proportional to the maximum of the objects when viewed +as a `usize`. + +### Examples + +```rust +use bit_set::BitSet; + +// It's a regular set +let mut s = BitSet::new(); +s.insert(0); +s.insert(3); +s.insert(7); + +s.remove(7); + +if !s.contains(7) { + println!("There is no 7"); +} + +// Can initialize from a `BitVec` +let other = BitSet::from_bytes(&[0b11010000]); + +s.union_with(&other); + +// Print 0, 1, 3 in some order +for x in s.iter() { + println!("{}", x); +} + +// Can convert back to a `BitVec` +let bv = s.into_bit_vec(); +assert!(bv[3]); +``` + + + +## License + +Dual-licensed for compatibility with the Rust project. + +Licensed under the Apache License Version 2.0: http://www.apache.org/licenses/LICENSE-2.0, +or the MIT license: http://opensource.org/licenses/MIT, at your option. diff --git a/tools/vendor/bit-set/RELEASES.md b/tools/vendor/bit-set/RELEASES.md new file mode 100644 index 0000000000..4e24136497 --- /dev/null +++ b/tools/vendor/bit-set/RELEASES.md @@ -0,0 +1,10 @@ +Version 0.7.0 (not yet released) (ZERO BREAKING CHANGES) +======================================================== + + + +- `serde::Serialize`, `Deserialize` is derived under the `serde` optional feature +- `impl Display` is implemented +- `impl Debug` has different output (we do not promise stable `Debug` output) +- `fn truncate` is implemented +- `fn get_mut` is implemented diff --git a/tools/vendor/bit-set/benches/bench.rs b/tools/vendor/bit-set/benches/bench.rs new file mode 100644 index 0000000000..9c1b1b76b0 --- /dev/null +++ b/tools/vendor/bit-set/benches/bench.rs @@ -0,0 +1,65 @@ +// Copyright 2012-2024 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(test)] + +extern crate bit_set; +extern crate bit_vec; +extern crate rand; +extern crate test; + +use bit_set::BitSet; +use bit_vec::BitVec; +use rand::{rngs::ThreadRng, thread_rng, RngCore}; + +use test::{black_box, Bencher}; + +const BENCH_BITS: usize = 1 << 14; +const BITS: usize = 32; + +fn rng() -> ThreadRng { + thread_rng() +} + +#[bench] +fn bench_bit_vecset_small(b: &mut Bencher) { + let mut r = rng(); + let mut bit_vec = BitSet::new(); + b.iter(|| { + for _ in 0..100 { + bit_vec.insert((r.next_u32() as usize) % BITS); + } + black_box(&bit_vec); + }); +} + +#[bench] +fn bench_bit_vecset_big(b: &mut Bencher) { + let mut r = rng(); + let mut bit_vec = BitSet::new(); + b.iter(|| { + for _ in 0..100 { + bit_vec.insert((r.next_u32() as usize) % BENCH_BITS); + } + black_box(&bit_vec); + }); +} + +#[bench] +fn bench_bit_vecset_iter(b: &mut Bencher) { + let bit_vec = BitSet::from_bit_vec(BitVec::from_fn(BENCH_BITS, |idx| idx % 3 == 0)); + b.iter(|| { + let mut sum = 0; + for idx in &bit_vec { + sum += idx as usize; + } + sum + }) +} diff --git a/tools/vendor/bit-set/src/lib.rs b/tools/vendor/bit-set/src/lib.rs new file mode 100644 index 0000000000..30bf8ced0a --- /dev/null +++ b/tools/vendor/bit-set/src/lib.rs @@ -0,0 +1,1681 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! # Description +//! +//! An implementation of a set using a bit vector as an underlying +//! representation for holding unsigned numerical elements. +//! +//! It should also be noted that the amount of storage necessary for holding a +//! set of objects is proportional to the maximum of the objects when viewed +//! as a `usize`. +//! +//! # Examples +//! +//! ``` +//! use bit_set::BitSet; +//! +//! // It's a regular set +//! let mut s = BitSet::new(); +//! s.insert(0); +//! s.insert(3); +//! s.insert(7); +//! +//! s.remove(7); +//! +//! if !s.contains(7) { +//! println!("There is no 7"); +//! } +//! +//! // Can initialize from a `BitVec` +//! let other = BitSet::from_bytes(&[0b11010000]); +//! +//! s.union_with(&other); +//! +//! // Print 0, 1, 3 in some order +//! for x in s.iter() { +//! println!("{}", x); +//! } +//! +//! // Can convert back to a `BitVec` +//! let bv = s.into_bit_vec(); +//! assert!(bv[3]); +//! ``` +#![doc(html_root_url = "https://docs.rs/bit-set/0.8.0")] +#![no_std] + +extern crate bit_vec; + +#[cfg(feature = "serde")] +extern crate serde; + +#[cfg(any(test, feature = "std"))] +extern crate std; + +use bit_vec::{BitBlock, BitVec, Blocks}; +use core::cmp; +use core::cmp::Ordering; +use core::fmt; +use core::hash; +use core::iter::{self, Chain, Enumerate, FromIterator, Repeat, Skip, Take}; + +type MatchWords<'a, B> = Chain>, Skip>>>>; + +/// Computes how many blocks are needed to store that many bits +fn blocks_for_bits(bits: usize) -> usize { + // If we want 17 bits, dividing by 32 will produce 0. So we add 1 to make sure we + // reserve enough. But if we want exactly a multiple of 32, this will actually allocate + // one too many. So we need to check if that's the case. We can do that by computing if + // bitwise AND by `32 - 1` is 0. But LLVM should be able to optimize the semantically + // superior modulo operator on a power of two to this. + // + // Note that we can technically avoid this branch with the expression + // `(nbits + BITS - 1) / 32::BITS`, but if nbits is almost usize::MAX this will overflow. + if bits % B::bits() == 0 { + bits / B::bits() + } else { + bits / B::bits() + 1 + } +} + +#[allow(clippy::iter_skip_zero)] +// Take two BitVec's, and return iterators of their words, where the shorter one +// has been padded with 0's +fn match_words<'a, 'b, B: BitBlock>( + a: &'a BitVec, + b: &'b BitVec, +) -> (MatchWords<'a, B>, MatchWords<'b, B>) { + let a_len = a.storage().len(); + let b_len = b.storage().len(); + + // have to uselessly pretend to pad the longer one for type matching + if a_len < b_len { + ( + a.blocks() + .enumerate() + .chain(iter::repeat(B::zero()).enumerate().take(b_len).skip(a_len)), + b.blocks() + .enumerate() + .chain(iter::repeat(B::zero()).enumerate().take(0).skip(0)), + ) + } else { + ( + a.blocks() + .enumerate() + .chain(iter::repeat(B::zero()).enumerate().take(0).skip(0)), + b.blocks() + .enumerate() + .chain(iter::repeat(B::zero()).enumerate().take(a_len).skip(b_len)), + ) + } +} + +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub struct BitSet { + bit_vec: BitVec, +} + +impl Clone for BitSet { + fn clone(&self) -> Self { + BitSet { + bit_vec: self.bit_vec.clone(), + } + } + + fn clone_from(&mut self, other: &Self) { + self.bit_vec.clone_from(&other.bit_vec); + } +} + +impl Default for BitSet { + #[inline] + fn default() -> Self { + BitSet { + bit_vec: Default::default(), + } + } +} + +impl FromIterator for BitSet { + fn from_iter>(iter: I) -> Self { + let mut ret = Self::default(); + ret.extend(iter); + ret + } +} + +impl Extend for BitSet { + #[inline] + fn extend>(&mut self, iter: I) { + for i in iter { + self.insert(i); + } + } +} + +impl PartialOrd for BitSet { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for BitSet { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.iter().cmp(other) + } +} + +impl PartialEq for BitSet { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.iter().eq(other) + } +} + +impl Eq for BitSet {} + +impl BitSet { + /// Creates a new empty `BitSet`. + /// + /// # Examples + /// + /// ``` + /// use bit_set::BitSet; + /// + /// let mut s = BitSet::new(); + /// ``` + #[inline] + pub fn new() -> Self { + Self::default() + } + + /// Creates a new `BitSet` with initially no contents, able to + /// hold `nbits` elements without resizing. + /// + /// # Examples + /// + /// ``` + /// use bit_set::BitSet; + /// + /// let mut s = BitSet::with_capacity(100); + /// assert!(s.capacity() >= 100); + /// ``` + #[inline] + pub fn with_capacity(nbits: usize) -> Self { + let bit_vec = BitVec::from_elem(nbits, false); + Self::from_bit_vec(bit_vec) + } + + /// Creates a new `BitSet` from the given bit vector. + /// + /// # Examples + /// + /// ``` + /// extern crate bit_vec; + /// extern crate bit_set; + /// + /// fn main() { + /// use bit_vec::BitVec; + /// use bit_set::BitSet; + /// + /// let bv = BitVec::from_bytes(&[0b01100000]); + /// let s = BitSet::from_bit_vec(bv); + /// + /// // Print 1, 2 in arbitrary order + /// for x in s.iter() { + /// println!("{}", x); + /// } + /// } + /// ``` + #[inline] + pub fn from_bit_vec(bit_vec: BitVec) -> Self { + BitSet { bit_vec } + } + + pub fn from_bytes(bytes: &[u8]) -> Self { + BitSet { + bit_vec: BitVec::from_bytes(bytes), + } + } +} + +impl BitSet { + /// Returns the capacity in bits for this bit vector. Inserting any + /// element less than this amount will not trigger a resizing. + /// + /// # Examples + /// + /// ``` + /// use bit_set::BitSet; + /// + /// let mut s = BitSet::with_capacity(100); + /// assert!(s.capacity() >= 100); + /// ``` + #[inline] + pub fn capacity(&self) -> usize { + self.bit_vec.capacity() + } + + /// Reserves capacity for the given `BitSet` to contain `len` distinct elements. In the case + /// of `BitSet` this means reallocations will not occur as long as all inserted elements + /// are less than `len`. + /// + /// The collection may reserve more space to avoid frequent reallocations. + /// + /// + /// # Examples + /// + /// ``` + /// use bit_set::BitSet; + /// + /// let mut s = BitSet::new(); + /// s.reserve_len(10); + /// assert!(s.capacity() >= 10); + /// ``` + pub fn reserve_len(&mut self, len: usize) { + let cur_len = self.bit_vec.len(); + if len >= cur_len { + self.bit_vec.reserve(len - cur_len); + } + } + + /// Reserves the minimum capacity for the given `BitSet` to contain `len` distinct elements. + /// In the case of `BitSet` this means reallocations will not occur as long as all inserted + /// elements are less than `len`. + /// + /// Note that the allocator may give the collection more space than it requests. Therefore + /// capacity can not be relied upon to be precisely minimal. Prefer `reserve_len` if future + /// insertions are expected. + /// + /// + /// # Examples + /// + /// ``` + /// use bit_set::BitSet; + /// + /// let mut s = BitSet::new(); + /// s.reserve_len_exact(10); + /// assert!(s.capacity() >= 10); + /// ``` + pub fn reserve_len_exact(&mut self, len: usize) { + let cur_len = self.bit_vec.len(); + if len >= cur_len { + self.bit_vec.reserve_exact(len - cur_len); + } + } + + /// Consumes this set to return the underlying bit vector. + /// + /// # Examples + /// + /// ``` + /// use bit_set::BitSet; + /// + /// let mut s = BitSet::new(); + /// s.insert(0); + /// s.insert(3); + /// + /// let bv = s.into_bit_vec(); + /// assert!(bv[0]); + /// assert!(bv[3]); + /// ``` + #[inline] + pub fn into_bit_vec(self) -> BitVec { + self.bit_vec + } + + /// Returns a reference to the underlying bit vector. + /// + /// # Examples + /// + /// ``` + /// use bit_set::BitSet; + /// + /// let mut set = BitSet::new(); + /// set.insert(0); + /// + /// let bv = set.get_ref(); + /// assert_eq!(bv[0], true); + /// ``` + #[inline] + pub fn get_ref(&self) -> &BitVec { + &self.bit_vec + } + + /// Returns a mutable reference to the underlying bit vector. + /// + /// # Examples + /// + /// ``` + /// use bit_set::BitSet; + /// + /// let mut set = BitSet::new(); + /// set.insert(0); + /// set.insert(3); + /// + /// { + /// let bv = set.get_mut(); + /// bv.set(1, true); + /// } + /// + /// assert!(set.contains(0)); + /// assert!(set.contains(1)); + /// assert!(set.contains(3)); + /// ``` + #[inline] + pub fn get_mut(&mut self) -> &mut BitVec { + &mut self.bit_vec + } + + #[inline] + fn other_op(&mut self, other: &Self, mut f: F) + where + F: FnMut(B, B) -> B, + { + // Unwrap BitVecs + let self_bit_vec = &mut self.bit_vec; + let other_bit_vec = &other.bit_vec; + + let self_len = self_bit_vec.len(); + let other_len = other_bit_vec.len(); + + // Expand the vector if necessary + if self_len < other_len { + self_bit_vec.grow(other_len - self_len, false); + } + + // virtually pad other with 0's for equal lengths + let other_words = { + let (_, result) = match_words(self_bit_vec, other_bit_vec); + result + }; + + // Apply values found in other + for (i, w) in other_words { + let old = self_bit_vec.storage()[i]; + let new = f(old, w); + unsafe { + self_bit_vec.storage_mut()[i] = new; + } + } + } + + /// Truncates the underlying vector to the least length required. + /// + /// # Examples + /// + /// ``` + /// use bit_set::BitSet; + /// + /// let mut s = BitSet::new(); + /// s.insert(3231); + /// s.remove(3231); + /// + /// // Internal storage will probably be bigger than necessary + /// println!("old capacity: {}", s.capacity()); + /// assert!(s.capacity() >= 3231); + /// + /// // Now should be smaller + /// s.shrink_to_fit(); + /// println!("new capacity: {}", s.capacity()); + /// ``` + #[inline] + pub fn shrink_to_fit(&mut self) { + let bit_vec = &mut self.bit_vec; + // Obtain original length + let old_len = bit_vec.storage().len(); + // Obtain coarse trailing zero length + let n = bit_vec + .storage() + .iter() + .rev() + .take_while(|&&n| n == B::zero()) + .count(); + // Truncate away all empty trailing blocks, then shrink_to_fit + let trunc_len = old_len - n; + unsafe { + bit_vec.storage_mut().truncate(trunc_len); + bit_vec.set_len(trunc_len * B::bits()); + } + bit_vec.shrink_to_fit(); + } + + /// Iterator over each usize stored in the `BitSet`. + /// + /// # Examples + /// + /// ``` + /// use bit_set::BitSet; + /// + /// let s = BitSet::from_bytes(&[0b01001010]); + /// + /// // Print 1, 4, 6 in arbitrary order + /// for x in s.iter() { + /// println!("{}", x); + /// } + /// ``` + #[inline] + pub fn iter(&self) -> Iter { + Iter(BlockIter::from_blocks(self.bit_vec.blocks())) + } + + /// Iterator over each usize stored in `self` union `other`. + /// See [`union_with`] for an efficient in-place version. + /// + /// # Examples + /// + /// ``` + /// use bit_set::BitSet; + /// + /// let a = BitSet::from_bytes(&[0b01101000]); + /// let b = BitSet::from_bytes(&[0b10100000]); + /// + /// // Print 0, 1, 2, 4 in arbitrary order + /// for x in a.union(&b) { + /// println!("{}", x); + /// } + /// ``` + /// + /// [`union_with`]: Self::union_with + #[inline] + pub fn union<'a>(&'a self, other: &'a Self) -> Union<'a, B> { + fn or(w1: B, w2: B) -> B { + w1 | w2 + } + + Union(BlockIter::from_blocks(TwoBitPositions { + set: self.bit_vec.blocks(), + other: other.bit_vec.blocks(), + merge: or, + })) + } + + /// Iterator over each usize stored in `self` intersect `other`. + /// See [`intersect_with`] for an efficient in-place version. + /// + /// # Examples + /// + /// ``` + /// use bit_set::BitSet; + /// + /// let a = BitSet::from_bytes(&[0b01101000]); + /// let b = BitSet::from_bytes(&[0b10100000]); + /// + /// // Print 2 + /// for x in a.intersection(&b) { + /// println!("{}", x); + /// } + /// ``` + /// + /// [`intersect_with`]: Self::intersect_with + #[inline] + pub fn intersection<'a>(&'a self, other: &'a Self) -> Intersection<'a, B> { + fn bitand(w1: B, w2: B) -> B { + w1 & w2 + } + let min = cmp::min(self.bit_vec.len(), other.bit_vec.len()); + + Intersection { + iter: BlockIter::from_blocks(TwoBitPositions { + set: self.bit_vec.blocks(), + other: other.bit_vec.blocks(), + merge: bitand, + }), + n: min, + } + } + + /// Iterator over each usize stored in the `self` setminus `other`. + /// See [`difference_with`] for an efficient in-place version. + /// + /// # Examples + /// + /// ``` + /// use bit_set::BitSet; + /// + /// let a = BitSet::from_bytes(&[0b01101000]); + /// let b = BitSet::from_bytes(&[0b10100000]); + /// + /// // Print 1, 4 in arbitrary order + /// for x in a.difference(&b) { + /// println!("{}", x); + /// } + /// + /// // Note that difference is not symmetric, + /// // and `b - a` means something else. + /// // This prints 0 + /// for x in b.difference(&a) { + /// println!("{}", x); + /// } + /// ``` + /// + /// [`difference_with`]: Self::difference_with + #[inline] + pub fn difference<'a>(&'a self, other: &'a Self) -> Difference<'a, B> { + fn diff(w1: B, w2: B) -> B { + w1 & !w2 + } + + Difference(BlockIter::from_blocks(TwoBitPositions { + set: self.bit_vec.blocks(), + other: other.bit_vec.blocks(), + merge: diff, + })) + } + + /// Iterator over each usize stored in the symmetric difference of `self` and `other`. + /// See [`symmetric_difference_with`] for an efficient in-place version. + /// + /// # Examples + /// + /// ``` + /// use bit_set::BitSet; + /// + /// let a = BitSet::from_bytes(&[0b01101000]); + /// let b = BitSet::from_bytes(&[0b10100000]); + /// + /// // Print 0, 1, 4 in arbitrary order + /// for x in a.symmetric_difference(&b) { + /// println!("{}", x); + /// } + /// ``` + /// + /// [`symmetric_difference_with`]: Self::symmetric_difference_with + #[inline] + pub fn symmetric_difference<'a>(&'a self, other: &'a Self) -> SymmetricDifference<'a, B> { + fn bitxor(w1: B, w2: B) -> B { + w1 ^ w2 + } + + SymmetricDifference(BlockIter::from_blocks(TwoBitPositions { + set: self.bit_vec.blocks(), + other: other.bit_vec.blocks(), + merge: bitxor, + })) + } + + /// Unions in-place with the specified other bit vector. + /// + /// # Examples + /// + /// ``` + /// use bit_set::BitSet; + /// + /// let a = 0b01101000; + /// let b = 0b10100000; + /// let res = 0b11101000; + /// + /// let mut a = BitSet::from_bytes(&[a]); + /// let b = BitSet::from_bytes(&[b]); + /// let res = BitSet::from_bytes(&[res]); + /// + /// a.union_with(&b); + /// assert_eq!(a, res); + /// ``` + #[inline] + pub fn union_with(&mut self, other: &Self) { + self.other_op(other, |w1, w2| w1 | w2); + } + + /// Intersects in-place with the specified other bit vector. + /// + /// # Examples + /// + /// ``` + /// use bit_set::BitSet; + /// + /// let a = 0b01101000; + /// let b = 0b10100000; + /// let res = 0b00100000; + /// + /// let mut a = BitSet::from_bytes(&[a]); + /// let b = BitSet::from_bytes(&[b]); + /// let res = BitSet::from_bytes(&[res]); + /// + /// a.intersect_with(&b); + /// assert_eq!(a, res); + /// ``` + #[inline] + pub fn intersect_with(&mut self, other: &Self) { + self.other_op(other, |w1, w2| w1 & w2); + } + + /// Makes this bit vector the difference with the specified other bit vector + /// in-place. + /// + /// # Examples + /// + /// ``` + /// use bit_set::BitSet; + /// + /// let a = 0b01101000; + /// let b = 0b10100000; + /// let a_b = 0b01001000; // a - b + /// let b_a = 0b10000000; // b - a + /// + /// let mut bva = BitSet::from_bytes(&[a]); + /// let bvb = BitSet::from_bytes(&[b]); + /// let bva_b = BitSet::from_bytes(&[a_b]); + /// let bvb_a = BitSet::from_bytes(&[b_a]); + /// + /// bva.difference_with(&bvb); + /// assert_eq!(bva, bva_b); + /// + /// let bva = BitSet::from_bytes(&[a]); + /// let mut bvb = BitSet::from_bytes(&[b]); + /// + /// bvb.difference_with(&bva); + /// assert_eq!(bvb, bvb_a); + /// ``` + #[inline] + pub fn difference_with(&mut self, other: &Self) { + self.other_op(other, |w1, w2| w1 & !w2); + } + + /// Makes this bit vector the symmetric difference with the specified other + /// bit vector in-place. + /// + /// # Examples + /// + /// ``` + /// use bit_set::BitSet; + /// + /// let a = 0b01101000; + /// let b = 0b10100000; + /// let res = 0b11001000; + /// + /// let mut a = BitSet::from_bytes(&[a]); + /// let b = BitSet::from_bytes(&[b]); + /// let res = BitSet::from_bytes(&[res]); + /// + /// a.symmetric_difference_with(&b); + /// assert_eq!(a, res); + /// ``` + #[inline] + pub fn symmetric_difference_with(&mut self, other: &Self) { + self.other_op(other, |w1, w2| w1 ^ w2); + } + + /* + /// Moves all elements from `other` into `Self`, leaving `other` empty. + /// + /// # Examples + /// + /// ``` + /// use bit_set::BitSet; + /// + /// let mut a = BitSet::new(); + /// a.insert(2); + /// a.insert(6); + /// + /// let mut b = BitSet::new(); + /// b.insert(1); + /// b.insert(3); + /// b.insert(6); + /// + /// a.append(&mut b); + /// + /// assert_eq!(a.len(), 4); + /// assert_eq!(b.len(), 0); + /// assert_eq!(a, BitSet::from_bytes(&[0b01110010])); + /// ``` + pub fn append(&mut self, other: &mut Self) { + self.union_with(other); + other.clear(); + } + + /// Splits the `BitSet` into two at the given key including the key. + /// Retains the first part in-place while returning the second part. + /// + /// # Examples + /// + /// ``` + /// use bit_set::BitSet; + /// + /// let mut a = BitSet::new(); + /// a.insert(2); + /// a.insert(6); + /// a.insert(1); + /// a.insert(3); + /// + /// let b = a.split_off(3); + /// + /// assert_eq!(a.len(), 2); + /// assert_eq!(b.len(), 2); + /// assert_eq!(a, BitSet::from_bytes(&[0b01100000])); + /// assert_eq!(b, BitSet::from_bytes(&[0b00010010])); + /// ``` + pub fn split_off(&mut self, at: usize) -> Self { + let mut other = BitSet::new(); + + if at == 0 { + swap(self, &mut other); + return other; + } else if at >= self.bit_vec.len() { + return other; + } + + // Calculate block and bit at which to split + let w = at / BITS; + let b = at % BITS; + + // Pad `other` with `w` zero blocks, + // append `self`'s blocks in the range from `w` to the end to `other` + other.bit_vec.storage_mut().extend(repeat(0u32).take(w) + .chain(self.bit_vec.storage()[w..].iter().cloned())); + other.bit_vec.nbits = self.bit_vec.nbits; + + if b > 0 { + other.bit_vec.storage_mut()[w] &= !0 << b; + } + + // Sets `bit_vec.len()` and fixes the last block as well + self.bit_vec.truncate(at); + + other + } + */ + + /// Returns the number of set bits in this set. + #[inline] + pub fn len(&self) -> usize { + self.bit_vec.blocks().fold(0, |acc, n| acc + n.count_ones()) + } + + /// Returns whether there are no bits set in this set + #[inline] + pub fn is_empty(&self) -> bool { + self.bit_vec.none() + } + + /// Clears all bits in this set + #[inline] + pub fn clear(&mut self) { + self.bit_vec.clear(); + } + + /// Returns `true` if this set contains the specified integer. + #[inline] + pub fn contains(&self, value: usize) -> bool { + let bit_vec = &self.bit_vec; + value < bit_vec.len() && bit_vec[value] + } + + /// Returns `true` if the set has no elements in common with `other`. + /// This is equivalent to checking for an empty intersection. + #[inline] + pub fn is_disjoint(&self, other: &Self) -> bool { + self.intersection(other).next().is_none() + } + + /// Returns `true` if the set is a subset of another. + #[inline] + pub fn is_subset(&self, other: &Self) -> bool { + let self_bit_vec = &self.bit_vec; + let other_bit_vec = &other.bit_vec; + let other_blocks = blocks_for_bits::(other_bit_vec.len()); + + // Check that `self` intersect `other` is self + self_bit_vec.blocks().zip(other_bit_vec.blocks()).all(|(w1, w2)| w1 & w2 == w1) && + // Make sure if `self` has any more blocks than `other`, they're all 0 + self_bit_vec.blocks().skip(other_blocks).all(|w| w == B::zero()) + } + + /// Returns `true` if the set is a superset of another. + #[inline] + pub fn is_superset(&self, other: &Self) -> bool { + other.is_subset(self) + } + + /// Adds a value to the set. Returns `true` if the value was not already + /// present in the set. + pub fn insert(&mut self, value: usize) -> bool { + if self.contains(value) { + return false; + } + + // Ensure we have enough space to hold the new element + let len = self.bit_vec.len(); + if value >= len { + self.bit_vec.grow(value - len + 1, false); + } + + self.bit_vec.set(value, true); + true + } + + /// Removes a value from the set. Returns `true` if the value was + /// present in the set. + pub fn remove(&mut self, value: usize) -> bool { + if !self.contains(value) { + return false; + } + + self.bit_vec.set(value, false); + + true + } + + /// Excludes `element` and all greater elements from the `BitSet`. + pub fn truncate(&mut self, element: usize) { + self.bit_vec.truncate(element); + } +} + +impl fmt::Debug for BitSet { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_set().entries(self).finish() + } +} + +impl hash::Hash for BitSet { + fn hash(&self, state: &mut H) { + for pos in self { + pos.hash(state); + } + } +} + +#[derive(Clone)] +struct BlockIter { + head: B, + head_offset: usize, + tail: T, +} + +impl BlockIter +where + T: Iterator, +{ + fn from_blocks(mut blocks: T) -> BlockIter { + let h = blocks.next().unwrap_or_else(B::zero); + BlockIter { + tail: blocks, + head: h, + head_offset: 0, + } + } +} + +/// An iterator combining two `BitSet` iterators. +#[derive(Clone)] +struct TwoBitPositions<'a, B: 'a> { + set: Blocks<'a, B>, + other: Blocks<'a, B>, + merge: fn(B, B) -> B, +} + +/// An iterator for `BitSet`. +#[derive(Clone)] +pub struct Iter<'a, B: 'a>(BlockIter, B>); +#[derive(Clone)] +pub struct Union<'a, B: 'a>(BlockIter, B>); +#[derive(Clone)] +pub struct Intersection<'a, B: 'a> { + iter: BlockIter, B>, + // as an optimization, we compute the maximum possible + // number of elements in the intersection, and count it + // down as we return elements. If we reach zero, we can + // stop. + n: usize, +} +#[derive(Clone)] +pub struct Difference<'a, B: 'a>(BlockIter, B>); +#[derive(Clone)] +pub struct SymmetricDifference<'a, B: 'a>(BlockIter, B>); + +impl Iterator for BlockIter +where + T: Iterator, +{ + type Item = usize; + + fn next(&mut self) -> Option { + while self.head == B::zero() { + match self.tail.next() { + Some(w) => self.head = w, + None => return None, + } + self.head_offset += B::bits(); + } + + // from the current block, isolate the + // LSB and subtract 1, producing k: + // a block with a number of set bits + // equal to the index of the LSB + let k = (self.head & (!self.head + B::one())) - B::one(); + // update block, removing the LSB + self.head = self.head & (self.head - B::one()); + // return offset + (index of LSB) + Some(self.head_offset + (B::count_ones(k))) + } + + fn count(self) -> usize { + self.head.count_ones() + self.tail.map(|block| block.count_ones()).sum::() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + match self.tail.size_hint() { + (_, Some(h)) => (0, Some((1 + h) * B::bits())), + _ => (0, None), + } + } +} + +impl<'a, B: BitBlock> Iterator for TwoBitPositions<'a, B> { + type Item = B; + + fn next(&mut self) -> Option { + match (self.set.next(), self.other.next()) { + (Some(a), Some(b)) => Some((self.merge)(a, b)), + (Some(a), None) => Some((self.merge)(a, B::zero())), + (None, Some(b)) => Some((self.merge)(B::zero(), b)), + _ => None, + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (a, au) = self.set.size_hint(); + let (b, bu) = self.other.size_hint(); + + let upper = match (au, bu) { + (Some(au), Some(bu)) => Some(cmp::max(au, bu)), + _ => None, + }; + + (cmp::max(a, b), upper) + } +} + +impl<'a, B: BitBlock> Iterator for Iter<'a, B> { + type Item = usize; + + #[inline] + fn next(&mut self) -> Option { + self.0.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + #[inline] + fn count(self) -> usize { + self.0.count() + } +} + +impl<'a, B: BitBlock> Iterator for Union<'a, B> { + type Item = usize; + + #[inline] + fn next(&mut self) -> Option { + self.0.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + #[inline] + fn count(self) -> usize { + self.0.count() + } +} + +impl<'a, B: BitBlock> Iterator for Intersection<'a, B> { + type Item = usize; + + #[inline] + fn next(&mut self) -> Option { + if self.n != 0 { + self.n -= 1; + self.iter.next() + } else { + None + } + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + // We could invoke self.iter.size_hint() and incorporate that into the hint. + // In practice, that does not seem worthwhile because the lower bound will + // always be zero and the upper bound could only possibly less then n in a + // partially iterated iterator. However, it makes little sense ask for size_hint + // in a partially iterated iterator, so it did not seem worthwhile. + (0, Some(self.n)) + } + #[inline] + fn count(self) -> usize { + self.iter.count() + } +} + +impl<'a, B: BitBlock> Iterator for Difference<'a, B> { + type Item = usize; + + #[inline] + fn next(&mut self) -> Option { + self.0.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + #[inline] + fn count(self) -> usize { + self.0.count() + } +} + +impl<'a, B: BitBlock> Iterator for SymmetricDifference<'a, B> { + type Item = usize; + + #[inline] + fn next(&mut self) -> Option { + self.0.next() + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + #[inline] + fn count(self) -> usize { + self.0.count() + } +} + +impl<'a, B: BitBlock> IntoIterator for &'a BitSet { + type Item = usize; + type IntoIter = Iter<'a, B>; + + fn into_iter(self) -> Iter<'a, B> { + self.iter() + } +} + +#[cfg(test)] +mod tests { + use super::BitSet; + use bit_vec::BitVec; + use std::cmp::Ordering::{Equal, Greater, Less}; + use std::vec::Vec; + use std::{format, vec}; + + #[test] + fn test_bit_set_show() { + let mut s = BitSet::new(); + s.insert(1); + s.insert(10); + s.insert(50); + s.insert(2); + assert_eq!("{1, 2, 10, 50}", format!("{:?}", s)); + } + + #[test] + fn test_bit_set_from_usizes() { + let usizes = vec![0, 2, 2, 3]; + let a: BitSet = usizes.into_iter().collect(); + let mut b = BitSet::new(); + b.insert(0); + b.insert(2); + b.insert(3); + assert_eq!(a, b); + } + + #[test] + fn test_bit_set_iterator() { + let usizes = vec![0, 2, 2, 3]; + let bit_vec: BitSet = usizes.into_iter().collect(); + + let idxs: Vec<_> = bit_vec.iter().collect(); + assert_eq!(idxs, [0, 2, 3]); + assert_eq!(bit_vec.iter().count(), 3); + + let long: BitSet = (0..10000).filter(|&n| n % 2 == 0).collect(); + let real: Vec<_> = (0..10000 / 2).map(|x| x * 2).collect(); + + let idxs: Vec<_> = long.iter().collect(); + assert_eq!(idxs, real); + assert_eq!(long.iter().count(), real.len()); + } + + #[test] + fn test_bit_set_frombit_vec_init() { + let bools = [true, false]; + let lengths = [10, 64, 100]; + for &b in &bools { + for &l in &lengths { + let bitset = BitSet::from_bit_vec(BitVec::from_elem(l, b)); + assert_eq!(bitset.contains(1), b); + assert_eq!(bitset.contains(l - 1), b); + assert!(!bitset.contains(l)); + } + } + } + + #[test] + fn test_bit_vec_masking() { + let b = BitVec::from_elem(140, true); + let mut bs = BitSet::from_bit_vec(b); + assert!(bs.contains(139)); + assert!(!bs.contains(140)); + assert!(bs.insert(150)); + assert!(!bs.contains(140)); + assert!(!bs.contains(149)); + assert!(bs.contains(150)); + assert!(!bs.contains(151)); + } + + #[test] + fn test_bit_set_basic() { + let mut b = BitSet::new(); + assert!(b.insert(3)); + assert!(!b.insert(3)); + assert!(b.contains(3)); + assert!(b.insert(4)); + assert!(!b.insert(4)); + assert!(b.contains(3)); + assert!(b.insert(400)); + assert!(!b.insert(400)); + assert!(b.contains(400)); + assert_eq!(b.len(), 3); + } + + #[test] + fn test_bit_set_intersection() { + let mut a = BitSet::new(); + let mut b = BitSet::new(); + + assert!(a.insert(11)); + assert!(a.insert(1)); + assert!(a.insert(3)); + assert!(a.insert(77)); + assert!(a.insert(103)); + assert!(a.insert(5)); + + assert!(b.insert(2)); + assert!(b.insert(11)); + assert!(b.insert(77)); + assert!(b.insert(5)); + assert!(b.insert(3)); + + let expected = [3, 5, 11, 77]; + let actual: Vec<_> = a.intersection(&b).collect(); + assert_eq!(actual, expected); + assert_eq!(a.intersection(&b).count(), expected.len()); + } + + #[test] + fn test_bit_set_difference() { + let mut a = BitSet::new(); + let mut b = BitSet::new(); + + assert!(a.insert(1)); + assert!(a.insert(3)); + assert!(a.insert(5)); + assert!(a.insert(200)); + assert!(a.insert(500)); + + assert!(b.insert(3)); + assert!(b.insert(200)); + + let expected = [1, 5, 500]; + let actual: Vec<_> = a.difference(&b).collect(); + assert_eq!(actual, expected); + assert_eq!(a.difference(&b).count(), expected.len()); + } + + #[test] + fn test_bit_set_symmetric_difference() { + let mut a = BitSet::new(); + let mut b = BitSet::new(); + + assert!(a.insert(1)); + assert!(a.insert(3)); + assert!(a.insert(5)); + assert!(a.insert(9)); + assert!(a.insert(11)); + + assert!(b.insert(3)); + assert!(b.insert(9)); + assert!(b.insert(14)); + assert!(b.insert(220)); + + let expected = [1, 5, 11, 14, 220]; + let actual: Vec<_> = a.symmetric_difference(&b).collect(); + assert_eq!(actual, expected); + assert_eq!(a.symmetric_difference(&b).count(), expected.len()); + } + + #[test] + fn test_bit_set_union() { + let mut a = BitSet::new(); + let mut b = BitSet::new(); + assert!(a.insert(1)); + assert!(a.insert(3)); + assert!(a.insert(5)); + assert!(a.insert(9)); + assert!(a.insert(11)); + assert!(a.insert(160)); + assert!(a.insert(19)); + assert!(a.insert(24)); + assert!(a.insert(200)); + + assert!(b.insert(1)); + assert!(b.insert(5)); + assert!(b.insert(9)); + assert!(b.insert(13)); + assert!(b.insert(19)); + + let expected = [1, 3, 5, 9, 11, 13, 19, 24, 160, 200]; + let actual: Vec<_> = a.union(&b).collect(); + assert_eq!(actual, expected); + assert_eq!(a.union(&b).count(), expected.len()); + } + + #[test] + fn test_bit_set_subset() { + let mut set1 = BitSet::new(); + let mut set2 = BitSet::new(); + + assert!(set1.is_subset(&set2)); // {} {} + set2.insert(100); + assert!(set1.is_subset(&set2)); // {} { 1 } + set2.insert(200); + assert!(set1.is_subset(&set2)); // {} { 1, 2 } + set1.insert(200); + assert!(set1.is_subset(&set2)); // { 2 } { 1, 2 } + set1.insert(300); + assert!(!set1.is_subset(&set2)); // { 2, 3 } { 1, 2 } + set2.insert(300); + assert!(set1.is_subset(&set2)); // { 2, 3 } { 1, 2, 3 } + set2.insert(400); + assert!(set1.is_subset(&set2)); // { 2, 3 } { 1, 2, 3, 4 } + set2.remove(100); + assert!(set1.is_subset(&set2)); // { 2, 3 } { 2, 3, 4 } + set2.remove(300); + assert!(!set1.is_subset(&set2)); // { 2, 3 } { 2, 4 } + set1.remove(300); + assert!(set1.is_subset(&set2)); // { 2 } { 2, 4 } + } + + #[test] + fn test_bit_set_is_disjoint() { + let a = BitSet::from_bytes(&[0b10100010]); + let b = BitSet::from_bytes(&[0b01000000]); + let c = BitSet::new(); + let d = BitSet::from_bytes(&[0b00110000]); + + assert!(!a.is_disjoint(&d)); + assert!(!d.is_disjoint(&a)); + + assert!(a.is_disjoint(&b)); + assert!(a.is_disjoint(&c)); + assert!(b.is_disjoint(&a)); + assert!(b.is_disjoint(&c)); + assert!(c.is_disjoint(&a)); + assert!(c.is_disjoint(&b)); + } + + #[test] + fn test_bit_set_union_with() { + //a should grow to include larger elements + let mut a = BitSet::new(); + a.insert(0); + let mut b = BitSet::new(); + b.insert(5); + let expected = BitSet::from_bytes(&[0b10000100]); + a.union_with(&b); + assert_eq!(a, expected); + + // Standard + let mut a = BitSet::from_bytes(&[0b10100010]); + let mut b = BitSet::from_bytes(&[0b01100010]); + let c = a.clone(); + a.union_with(&b); + b.union_with(&c); + assert_eq!(a.len(), 4); + assert_eq!(b.len(), 4); + } + + #[test] + fn test_bit_set_intersect_with() { + // Explicitly 0'ed bits + let mut a = BitSet::from_bytes(&[0b10100010]); + let mut b = BitSet::from_bytes(&[0b00000000]); + let c = a.clone(); + a.intersect_with(&b); + b.intersect_with(&c); + assert!(a.is_empty()); + assert!(b.is_empty()); + + // Uninitialized bits should behave like 0's + let mut a = BitSet::from_bytes(&[0b10100010]); + let mut b = BitSet::new(); + let c = a.clone(); + a.intersect_with(&b); + b.intersect_with(&c); + assert!(a.is_empty()); + assert!(b.is_empty()); + + // Standard + let mut a = BitSet::from_bytes(&[0b10100010]); + let mut b = BitSet::from_bytes(&[0b01100010]); + let c = a.clone(); + a.intersect_with(&b); + b.intersect_with(&c); + assert_eq!(a.len(), 2); + assert_eq!(b.len(), 2); + } + + #[test] + fn test_bit_set_difference_with() { + // Explicitly 0'ed bits + let mut a = BitSet::from_bytes(&[0b00000000]); + let b = BitSet::from_bytes(&[0b10100010]); + a.difference_with(&b); + assert!(a.is_empty()); + + // Uninitialized bits should behave like 0's + let mut a = BitSet::new(); + let b = BitSet::from_bytes(&[0b11111111]); + a.difference_with(&b); + assert!(a.is_empty()); + + // Standard + let mut a = BitSet::from_bytes(&[0b10100010]); + let mut b = BitSet::from_bytes(&[0b01100010]); + let c = a.clone(); + a.difference_with(&b); + b.difference_with(&c); + assert_eq!(a.len(), 1); + assert_eq!(b.len(), 1); + } + + #[test] + fn test_bit_set_symmetric_difference_with() { + //a should grow to include larger elements + let mut a = BitSet::new(); + a.insert(0); + a.insert(1); + let mut b = BitSet::new(); + b.insert(1); + b.insert(5); + let expected = BitSet::from_bytes(&[0b10000100]); + a.symmetric_difference_with(&b); + assert_eq!(a, expected); + + let mut a = BitSet::from_bytes(&[0b10100010]); + let b = BitSet::new(); + let c = a.clone(); + a.symmetric_difference_with(&b); + assert_eq!(a, c); + + // Standard + let mut a = BitSet::from_bytes(&[0b11100010]); + let mut b = BitSet::from_bytes(&[0b01101010]); + let c = a.clone(); + a.symmetric_difference_with(&b); + b.symmetric_difference_with(&c); + assert_eq!(a.len(), 2); + assert_eq!(b.len(), 2); + } + + #[test] + fn test_bit_set_eq() { + let a = BitSet::from_bytes(&[0b10100010]); + let b = BitSet::from_bytes(&[0b00000000]); + let c = BitSet::new(); + + assert!(a == a); + assert!(a != b); + assert!(a != c); + assert!(b == b); + assert!(b == c); + assert!(c == c); + } + + #[test] + fn test_bit_set_cmp() { + let a = BitSet::from_bytes(&[0b10100010]); + let b = BitSet::from_bytes(&[0b00000000]); + let c = BitSet::new(); + + assert_eq!(a.cmp(&b), Greater); + assert_eq!(a.cmp(&c), Greater); + assert_eq!(b.cmp(&a), Less); + assert_eq!(b.cmp(&c), Equal); + assert_eq!(c.cmp(&a), Less); + assert_eq!(c.cmp(&b), Equal); + } + + #[test] + fn test_bit_set_shrink_to_fit_new() { + // There was a strange bug where we refused to truncate to 0 + // and this would end up actually growing the array in a way + // that (safely corrupted the state). + let mut a = BitSet::new(); + assert_eq!(a.len(), 0); + assert_eq!(a.capacity(), 0); + a.shrink_to_fit(); + assert_eq!(a.len(), 0); + assert_eq!(a.capacity(), 0); + assert!(!a.contains(1)); + a.insert(3); + assert!(a.contains(3)); + assert_eq!(a.len(), 1); + assert!(a.capacity() > 0); + a.shrink_to_fit(); + assert!(a.contains(3)); + assert_eq!(a.len(), 1); + assert!(a.capacity() > 0); + } + + #[test] + fn test_bit_set_shrink_to_fit() { + let mut a = BitSet::new(); + assert_eq!(a.len(), 0); + assert_eq!(a.capacity(), 0); + a.insert(259); + a.insert(98); + a.insert(3); + assert_eq!(a.len(), 3); + assert!(a.capacity() > 0); + assert!(!a.contains(1)); + assert!(a.contains(259)); + assert!(a.contains(98)); + assert!(a.contains(3)); + + a.shrink_to_fit(); + assert!(!a.contains(1)); + assert!(a.contains(259)); + assert!(a.contains(98)); + assert!(a.contains(3)); + assert_eq!(a.len(), 3); + assert!(a.capacity() > 0); + + let old_cap = a.capacity(); + assert!(a.remove(259)); + a.shrink_to_fit(); + assert!(a.capacity() < old_cap, "{} {}", a.capacity(), old_cap); + assert!(!a.contains(1)); + assert!(!a.contains(259)); + assert!(a.contains(98)); + assert!(a.contains(3)); + assert_eq!(a.len(), 2); + + let old_cap2 = a.capacity(); + a.clear(); + assert_eq!(a.capacity(), old_cap2); + assert_eq!(a.len(), 0); + assert!(!a.contains(1)); + assert!(!a.contains(259)); + assert!(!a.contains(98)); + assert!(!a.contains(3)); + + a.insert(512); + assert!(a.capacity() > 0); + assert_eq!(a.len(), 1); + assert!(a.contains(512)); + assert!(!a.contains(1)); + assert!(!a.contains(259)); + assert!(!a.contains(98)); + assert!(!a.contains(3)); + + a.remove(512); + a.shrink_to_fit(); + assert_eq!(a.capacity(), 0); + assert_eq!(a.len(), 0); + assert!(!a.contains(512)); + assert!(!a.contains(1)); + assert!(!a.contains(259)); + assert!(!a.contains(98)); + assert!(!a.contains(3)); + assert!(!a.contains(0)); + } + + #[test] + fn test_bit_vec_remove() { + let mut a = BitSet::new(); + + assert!(a.insert(1)); + assert!(a.remove(1)); + + assert!(a.insert(100)); + assert!(a.remove(100)); + + assert!(a.insert(1000)); + assert!(a.remove(1000)); + a.shrink_to_fit(); + } + + #[test] + fn test_bit_vec_clone() { + let mut a = BitSet::new(); + + assert!(a.insert(1)); + assert!(a.insert(100)); + assert!(a.insert(1000)); + + let mut b = a.clone(); + + assert!(a == b); + + assert!(b.remove(1)); + assert!(a.contains(1)); + + assert!(a.remove(1000)); + assert!(b.contains(1000)); + } + + #[test] + fn test_truncate() { + let bytes = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; + + let mut s = BitSet::from_bytes(&bytes); + s.truncate(5 * 8); + + assert_eq!(s, BitSet::from_bytes(&bytes[..5])); + assert_eq!(s.len(), 5 * 8); + s.truncate(4 * 8); + assert_eq!(s, BitSet::from_bytes(&bytes[..4])); + assert_eq!(s.len(), 4 * 8); + // Truncating to a size > s.len() should be a noop + s.truncate(5 * 8); + assert_eq!(s, BitSet::from_bytes(&bytes[..4])); + assert_eq!(s.len(), 4 * 8); + s.truncate(8); + assert_eq!(s, BitSet::from_bytes(&bytes[..1])); + assert_eq!(s.len(), 8); + s.truncate(0); + assert_eq!(s, BitSet::from_bytes(&[])); + assert_eq!(s.len(), 0); + } + + #[cfg(feature = "serde")] + #[test] + fn test_serialization() { + let bset: BitSet = BitSet::new(); + let serialized = serde_json::to_string(&bset).unwrap(); + let unserialized: BitSet = serde_json::from_str(&serialized).unwrap(); + assert_eq!(bset, unserialized); + + let elems: Vec = vec![11, 42, 100, 101]; + let bset: BitSet = elems.iter().map(|n| *n).collect(); + let serialized = serde_json::to_string(&bset).unwrap(); + let unserialized = serde_json::from_str(&serialized).unwrap(); + assert_eq!(bset, unserialized); + } + + /* + #[test] + fn test_bit_set_append() { + let mut a = BitSet::new(); + a.insert(2); + a.insert(6); + + let mut b = BitSet::new(); + b.insert(1); + b.insert(3); + b.insert(6); + + a.append(&mut b); + + assert_eq!(a.len(), 4); + assert_eq!(b.len(), 0); + assert!(b.capacity() >= 6); + + assert_eq!(a, BitSet::from_bytes(&[0b01110010])); + } + + #[test] + fn test_bit_set_split_off() { + // Split at 0 + let mut a = BitSet::from_bytes(&[0b10100000, 0b00010010, 0b10010010, + 0b00110011, 0b01101011, 0b10101101]); + + let b = a.split_off(0); + + assert_eq!(a.len(), 0); + assert_eq!(b.len(), 21); + + assert_eq!(b, BitSet::from_bytes(&[0b10100000, 0b00010010, 0b10010010, + 0b00110011, 0b01101011, 0b10101101]); + + // Split behind last element + let mut a = BitSet::from_bytes(&[0b10100000, 0b00010010, 0b10010010, + 0b00110011, 0b01101011, 0b10101101]); + + let b = a.split_off(50); + + assert_eq!(a.len(), 21); + assert_eq!(b.len(), 0); + + assert_eq!(a, BitSet::from_bytes(&[0b10100000, 0b00010010, 0b10010010, + 0b00110011, 0b01101011, 0b10101101])); + + // Split at arbitrary element + let mut a = BitSet::from_bytes(&[0b10100000, 0b00010010, 0b10010010, + 0b00110011, 0b01101011, 0b10101101]); + + let b = a.split_off(34); + + assert_eq!(a.len(), 12); + assert_eq!(b.len(), 9); + + assert_eq!(a, BitSet::from_bytes(&[0b10100000, 0b00010010, 0b10010010, + 0b00110011, 0b01000000])); + assert_eq!(b, BitSet::from_bytes(&[0, 0, 0, 0, + 0b00101011, 0b10101101])); + } + */ +} diff --git a/tools/vendor/bit-vec/.cargo-checksum.json b/tools/vendor/bit-vec/.cargo-checksum.json new file mode 100644 index 0000000000..39862ab9fe --- /dev/null +++ b/tools/vendor/bit-vec/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo-rdme.toml":"62dbf3711ece6a2f162946e36711b233c9dbb6e7077c9cabb357de334e27aee6",".cargo_vcs_info.json":"8abe5e32ec876123956b8c99c4271737b3aca945f435fc4015ed35335b5d8b86",".github/workflows/rust.yml":"a018e42775dffa86b29ea11267726525a26efe123c9f744633cadcc6c4d37e9c",".vscode/settings.json":"4eab174d0cc18c70c10405a9849d8776bd40439ab59a68c6e33a9ab2a806cbef","Cargo.toml":"3ffdb0eaead44d902d6db061fec67c139107b79a1044b974fa56560644cb3a92","Cargo.toml.orig":"99e3debf7264df3cf0b68d10877d3ad57d4a1025cf5aa9bb651336c4ea33cf69","LICENSE-APACHE":"8173d5c29b4f956d532781d2b86e4e30f83e6b7878dce18c919451d6ba707c90","LICENSE-MIT":"f51ac2c59a222f7476ce507ca879960e2b64ea64bb2786eefdbeb7b0b538d1b7","README.md":"5fc245f9be5f4c99931ca018b09603d29f9e376d8f9bc77cb7b156a4bdc7926a","RELEASES.md":"19717f09fe2af669be80801a5702ecd166e6001194c935e81669f72619e4144a","benches/bench.rs":"b0f3cd80ea37456a4ba7dee46f3aef0a143c7ab88418b8ca8e0661b9bb741d2a","crusader.sh":"e656dcb62d5122a64d55f837992e63cfd3beee37cf74c5ab6ff178a3c7ef943e","src/lib.rs":"2c570ee7e33315cb8f1cbb33bbb91aee9b4b9dc8521f488837414e890a149084"},"package":"5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"} \ No newline at end of file diff --git a/tools/vendor/bit-vec/.cargo-rdme.toml b/tools/vendor/bit-vec/.cargo-rdme.toml new file mode 100644 index 0000000000..92e26ae187 --- /dev/null +++ b/tools/vendor/bit-vec/.cargo-rdme.toml @@ -0,0 +1 @@ +line-terminator = "lf" diff --git a/tools/vendor/bit-vec/.cargo_vcs_info.json b/tools/vendor/bit-vec/.cargo_vcs_info.json new file mode 100644 index 0000000000..4fbea4c899 --- /dev/null +++ b/tools/vendor/bit-vec/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "16d8d5ed27e9d379ae5d531ffcf7535cd02ef8f3" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/tools/vendor/bit-vec/.github/workflows/rust.yml b/tools/vendor/bit-vec/.github/workflows/rust.yml new file mode 100644 index 0000000000..fa27d5bee2 --- /dev/null +++ b/tools/vendor/bit-vec/.github/workflows/rust.yml @@ -0,0 +1,87 @@ +name: Rust + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose + - name: Run tests (gecko-ffi) + run: cargo test --tests --verbose + + miri: + name: "Miri" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Install Miri + run: | + rustup toolchain install nightly --component miri + rustup override set nightly + cargo miri setup + - name: Test with Miri + run: MIRIFLAGS=-Zmiri-strict-provenance cargo miri test + + fmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + components: rustfmt + override: true + - uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + components: clippy + override: true + - uses: actions-rs/clippy-check@v1 + env: + PWD: ${{ env.GITHUB_WORKSPACE }} + with: + token: ${{ secrets.GITHUB_TOKEN }} + args: --workspace --tests --examples + + + docs: + runs-on: ubuntu-latest + env: + RUSTDOCFLAGS: -Dwarnings + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + components: rust-docs + override: true + - uses: swatinem/rust-cache@v1 + - uses: actions-rs/cargo@v1 + with: + command: doc + args: --workspace --no-deps diff --git a/tools/vendor/bit-vec/.vscode/settings.json b/tools/vendor/bit-vec/.vscode/settings.json new file mode 100644 index 0000000000..a5682138f2 --- /dev/null +++ b/tools/vendor/bit-vec/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "rust-analyzer.cargo.features": ["serde"] +} diff --git a/tools/vendor/bit-vec/Cargo.toml b/tools/vendor/bit-vec/Cargo.toml new file mode 100644 index 0000000000..4461f4a201 --- /dev/null +++ b/tools/vendor/bit-vec/Cargo.toml @@ -0,0 +1,89 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2015" +name = "bit-vec" +version = "0.8.0" +authors = ["Alexis Beingessner "] +build = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "A vector of bits" +homepage = "https://github.com/contain-rs/bit-vec" +documentation = "https://docs.rs/bit-vec/" +readme = "README.md" +keywords = [ + "data-structures", + "bitvec", + "bitmask", + "bitmap", + "bit", +] +license = "Apache-2.0 OR MIT" +repository = "https://github.com/contain-rs/bit-vec" + +[package.metadata.docs.rs] +features = [ + "borsh", + "serde", + "miniserde", + "nanoserde", +] + +[lib] +name = "bit_vec" +path = "src/lib.rs" + +[[bench]] +name = "bench" +path = "benches/bench.rs" + +[dependencies.borsh] +version = "1.5" +features = ["derive"] +optional = true +default-features = false + +[dependencies.miniserde] +version = "0.1" +optional = true + +[dependencies.nanoserde] +version = "0.1" +optional = true + +[dependencies.serde] +version = "1.0" +features = ["derive"] +optional = true +default-features = false + +[dev-dependencies.rand] +version = "0.8" + +[dev-dependencies.rand_xorshift] +version = "0.3" + +[dev-dependencies.serde_json] +version = "1.0" + +[features] +borsh_std = ["borsh/std"] +default = ["std"] +serde_no_std = ["serde/alloc"] +serde_std = [ + "std", + "serde/std", +] +std = [] diff --git a/tools/vendor/bit-vec/Cargo.toml.orig b/tools/vendor/bit-vec/Cargo.toml.orig new file mode 100644 index 0000000000..1c443c50ab --- /dev/null +++ b/tools/vendor/bit-vec/Cargo.toml.orig @@ -0,0 +1,33 @@ +[package] +name = "bit-vec" +version = "0.8.0" +authors = ["Alexis Beingessner "] +license = "Apache-2.0 OR MIT" +description = "A vector of bits" +repository = "https://github.com/contain-rs/bit-vec" +homepage = "https://github.com/contain-rs/bit-vec" +documentation = "https://docs.rs/bit-vec/" +keywords = ["data-structures", "bitvec", "bitmask", "bitmap", "bit"] +readme = "README.md" +edition = "2015" + +[dependencies] +borsh = { version = "1.5", default-features = false, features = ["derive"], optional = true } +serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } +miniserde = { version = "0.1", optional = true } +nanoserde = { version = "0.1", optional = true } + +[dev-dependencies] +serde_json = "1.0" +rand = "0.8" +rand_xorshift = "0.3" + +[features] +default = ["std"] +serde_std = ["std", "serde/std"] +serde_no_std = ["serde/alloc"] +borsh_std = ["borsh/std"] +std = [] + +[package.metadata.docs.rs] +features = ["borsh", "serde", "miniserde", "nanoserde"] diff --git a/tools/vendor/bit-vec/LICENSE-APACHE b/tools/vendor/bit-vec/LICENSE-APACHE new file mode 100644 index 0000000000..11069edd79 --- /dev/null +++ b/tools/vendor/bit-vec/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/tools/vendor/bit-vec/LICENSE-MIT b/tools/vendor/bit-vec/LICENSE-MIT new file mode 100644 index 0000000000..40a969bb9a --- /dev/null +++ b/tools/vendor/bit-vec/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2023 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/tools/vendor/bit-vec/README.md b/tools/vendor/bit-vec/README.md new file mode 100644 index 0000000000..2d699534ca --- /dev/null +++ b/tools/vendor/bit-vec/README.md @@ -0,0 +1,148 @@ +
+

bit-vec

+

+ A compact vector of bits. +

+

+ +[![crates.io][crates.io shield]][crates.io link] +[![Documentation][docs.rs badge]][docs.rs link] +![Rust CI][github ci badge] +[![rustc 1.0+]][Rust 1.0] +[![serde_derive: rustc 1.31+]][Rust 1.31] +
+
+[![Dependency Status][deps.rs status]][deps.rs link] +[![Download Status][shields.io download count]][crates.io link] + +

+
+ +[crates.io shield]: https://img.shields.io/crates/v/bit-vec?label=latest +[crates.io link]: https://crates.io/crates/bit-vec +[docs.rs badge]: https://docs.rs/bit-vec/badge.svg?version=0.8.0 +[docs.rs link]: https://docs.rs/bit-vec/0.8.0/bit_vec/ +[github ci badge]: https://github.com/contain-rs/linked-hash-map/workflows/Rust/badge.svg?branch=master +[rustc 1.0+]: https://img.shields.io/badge/rustc-1.0%2B-blue.svg +[serde_derive: rustc 1.31+]: https://img.shields.io/badge/serde_derive-rustc_1.31+-lightgray.svg +[Rust 1.0]: https://blog.rust-lang.org/2015/05/15/Rust-1.0.html +[Rust 1.31]: https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html +[deps.rs status]: https://deps.rs/crate/bit-vec/0.8.0/status.svg +[deps.rs link]: https://deps.rs/crate/bit-vec/0.8.0 +[shields.io download count]: https://img.shields.io/crates/d/bit-vec.svg + +## Usage + +Add this to your Cargo.toml: + +```toml +[dependencies] +bit-vec = "0.8" +``` + +Since Rust 2018, `extern crate` is no longer mandatory. If your edition is old (Rust 2015), +add this to your crate root: + +```rust +extern crate bit_vec; +``` + +If you want [serde](https://github.com/serde-rs/serde) support, include the feature like this: + +```toml +[dependencies] +bit-vec = { version = "0.8", features = ["serde"] } +``` + +If you want to use bit-vec in a program that has `#![no_std]`, just drop default features: + +```toml +[dependencies] +bit-vec = { version = "0.8", default-features = false } +``` + +If you want to use serde with the alloc crate instead of std, just use the `serde_no_std` feature: + +```toml +[dependencies] +bit-vec = { version = "0.8", default-features = false, features = ["serde", "serde_no_std"] } +``` + +If you want [borsh-rs](https://github.com/near/borsh-rs) support, include it like this: + +```toml +[dependencies] +bit-vec = { version = "0.8", features = ["borsh"] } +``` + +Other available serialization libraries can be enabled with the +[`miniserde`](https://github.com/dtolnay/miniserde) and +[`nanoserde`](https://github.com/not-fl3/nanoserde) features. + + + +### Description + +Dynamic collections implemented with compact bit vectors. + +### Examples + +This is a simple example of the [Sieve of Eratosthenes][sieve] +which calculates prime numbers up to a given limit. + +[sieve]: http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes + +```rust +use bit_vec::BitVec; + +let max_prime = 10000; + +// Store the primes as a BitVec +let primes = { + // Assume all numbers are prime to begin, and then we + // cross off non-primes progressively + let mut bv = BitVec::from_elem(max_prime, true); + + // Neither 0 nor 1 are prime + bv.set(0, false); + bv.set(1, false); + + for i in 2.. 1 + (max_prime as f64).sqrt() as usize { + // if i is a prime + if bv[i] { + // Mark all multiples of i as non-prime (any multiples below i * i + // will have been marked as non-prime previously) + for j in i.. { + if i * j >= max_prime { + break; + } + bv.set(i * j, false) + } + } + } + bv +}; + +// Simple primality tests below our max bound +let print_primes = 20; +print!("The primes below {} are: ", print_primes); +for x in 0..print_primes { + if primes.get(x).unwrap_or(false) { + print!("{} ", x); + } +} +println!(); + +let num_primes = primes.iter().filter(|x| *x).count(); +println!("There are {} primes below {}", num_primes, max_prime); +assert_eq!(num_primes, 1_229); +``` + + + +## License + +Dual-licensed for compatibility with the Rust project. + +Licensed under the Apache License Version 2.0: http://www.apache.org/licenses/LICENSE-2.0, +or the MIT license: http://opensource.org/licenses/MIT, at your option. diff --git a/tools/vendor/bit-vec/RELEASES.md b/tools/vendor/bit-vec/RELEASES.md new file mode 100644 index 0000000000..879cc2f2c0 --- /dev/null +++ b/tools/vendor/bit-vec/RELEASES.md @@ -0,0 +1,8 @@ +Version 0.8.0 (2024-07-16) +========================== + + + +- `fn insert` is implemented +- `impl Display` is implemented +- `impl Debug` has different output diff --git a/tools/vendor/bit-vec/benches/bench.rs b/tools/vendor/bit-vec/benches/bench.rs new file mode 100644 index 0000000000..871714ea37 --- /dev/null +++ b/tools/vendor/bit-vec/benches/bench.rs @@ -0,0 +1,262 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(test)] +#![feature(hint_assert_unchecked)] + +extern crate bit_vec; +extern crate rand; +extern crate rand_xorshift; +extern crate test; + +use bit_vec::BitVec; +use rand::{Rng, RngCore, SeedableRng}; +use rand_xorshift::XorShiftRng; +use test::{black_box, Bencher}; + +const HUGE_BENCH_BITS: usize = 1 << 20; +const BENCH_BITS: usize = 1 << 14; +const U32_BITS: usize = 32; + +fn small_rng() -> XorShiftRng { + XorShiftRng::from_entropy() +} + +#[bench] +fn bench_usize_small(b: &mut Bencher) { + let mut r = small_rng(); + let mut bit_vec = 0_usize; + b.iter(|| { + for _ in 0..100 { + bit_vec |= 1 << ((r.next_u32() as usize) % U32_BITS); + } + black_box(&bit_vec); + }); +} + +#[bench] +fn bench_bit_set_big_fixed(b: &mut Bencher) { + let mut r = small_rng(); + let mut bit_vec = BitVec::from_elem(BENCH_BITS, false); + b.iter(|| { + for _ in 0..100 { + bit_vec.set((r.next_u32() as usize) % BENCH_BITS, true); + } + black_box(&bit_vec); + }); +} + +#[bench] +fn bench_bit_set_big_variable(b: &mut Bencher) { + let mut r = small_rng(); + let mut bit_vec = BitVec::from_elem(BENCH_BITS, false); + b.iter(|| { + for _ in 0..100 { + bit_vec.set((r.next_u32() as usize) % BENCH_BITS, r.gen()); + } + black_box(&bit_vec); + }); +} + +#[bench] +fn bench_bit_set_small(b: &mut Bencher) { + let mut r = small_rng(); + let mut bit_vec = BitVec::from_elem(U32_BITS, false); + b.iter(|| { + for _ in 0..100 { + bit_vec.set((r.next_u32() as usize) % U32_BITS, true); + } + black_box(&bit_vec); + }); +} + +#[bench] +fn bench_bit_get_checked_small(b: &mut Bencher) { + let mut r = small_rng(); + let size = 200; + let mut bit_vec = BitVec::from_elem(size, false); + for _ in 0..20 { + bit_vec.set((r.next_u32() as usize) % size, true); + } + let bit_vec = black_box(bit_vec); + b.iter(|| { + for _ in 0..100 { + black_box(bit_vec.get((r.next_u32() as usize) % size)); + } + }); +} + +#[bench] +fn bench_bit_get_unchecked_small(b: &mut Bencher) { + let mut r = small_rng(); + let size = 200; + let mut bit_vec = BitVec::from_elem(size, false); + for _ in 0..20 { + bit_vec.set((r.next_u32() as usize) % size, true); + } + let bit_vec = black_box(bit_vec); + b.iter(|| { + for _ in 0..100 { + unsafe { + black_box(bit_vec.get_unchecked((r.next_u32() as usize) % size)); + } + } + }); +} + +#[bench] +fn bench_bit_get_unchecked_small_assume(b: &mut Bencher) { + let mut r = small_rng(); + let size = 200; + let mut bit_vec = BitVec::from_elem(size, false); + for _ in 0..20 { + bit_vec.set((r.next_u32() as usize) % size, true); + } + let bit_vec = black_box(bit_vec); + b.iter(|| { + for _ in 0..100 { + unsafe { + let idx = (r.next_u32() as usize) % size; + ::std::hint::assert_unchecked(!(idx >= bit_vec.len())); + black_box(bit_vec.get(idx)); + } + } + }); +} + +#[bench] +fn bench_bit_vec_big_or(b: &mut Bencher) { + let mut b1 = BitVec::from_elem(BENCH_BITS, false); + let b2 = BitVec::from_elem(BENCH_BITS, false); + b.iter(|| b1.or(&b2)) +} + +#[bench] +fn bench_bit_vec_big_xnor(b: &mut Bencher) { + let mut b1 = BitVec::from_elem(BENCH_BITS, false); + let b2 = BitVec::from_elem(BENCH_BITS, false); + b.iter(|| b1.xnor(&b2)) +} + +#[bench] +fn bench_bit_vec_big_negate_xor(b: &mut Bencher) { + let mut b1 = BitVec::from_elem(BENCH_BITS, false); + let b2 = BitVec::from_elem(BENCH_BITS, false); + b.iter(|| { + let res = b1.xor(&b2); + b1.negate(); + res + }) +} + +#[bench] +fn bench_bit_vec_huge_xnor(b: &mut Bencher) { + let mut b1 = BitVec::from_elem(HUGE_BENCH_BITS, false); + let b2 = BitVec::from_elem(HUGE_BENCH_BITS, false); + b.iter(|| b1.xnor(&b2)) +} + +#[bench] +fn bench_bit_vec_huge_negate_xor(b: &mut Bencher) { + let mut b1 = BitVec::from_elem(HUGE_BENCH_BITS, false); + let b2 = BitVec::from_elem(HUGE_BENCH_BITS, false); + b.iter(|| { + let res = b1.xor(&b2); + b1.negate(); + res + }) +} + +#[bench] +fn bench_bit_vec_small_iter(b: &mut Bencher) { + let bit_vec = BitVec::from_elem(U32_BITS, false); + b.iter(|| { + let mut sum = 0; + for _ in 0..10 { + for pres in &bit_vec { + sum += pres as usize; + } + } + sum + }) +} + +#[bench] +fn bench_bit_vec_big_iter(b: &mut Bencher) { + let bit_vec = BitVec::from_elem(BENCH_BITS, false); + b.iter(|| { + let mut sum = 0; + for pres in &bit_vec { + sum += pres as usize; + } + sum + }) +} + +#[bench] +fn bench_from_elem(b: &mut Bencher) { + let cap = black_box(BENCH_BITS); + let bit = black_box(true); + b.iter(|| { + // create a BitVec and popcount it + BitVec::from_elem(cap, bit) + .blocks() + .fold(0, |acc, b| acc + b.count_ones()) + }); + b.bytes = cap as u64 / 8; +} + +#[bench] +fn bench_erathostenes(b: &mut test::Bencher) { + let mut primes = vec![]; + b.iter(|| { + primes.clear(); + let mut sieve = BitVec::from_elem(1 << 16, true); + black_box(&mut sieve); + let mut i = 2; + while i < sieve.len() { + if sieve[i] { + primes.push(i); + } + let mut j = i; + while j < sieve.len() { + sieve.set(j, false); + j += i; + } + i += 1; + } + black_box(&mut sieve); + }); +} + +#[bench] +fn bench_erathostenes_set_all(b: &mut test::Bencher) { + let mut primes = vec![]; + let mut sieve = BitVec::from_elem(1 << 16, true); + b.iter(|| { + primes.clear(); + black_box(&mut sieve); + sieve.set_all(); + black_box(&mut sieve); + let mut i = 2; + while i < sieve.len() { + if sieve[i] { + primes.push(i); + } + let mut j = i; + while j < sieve.len() { + sieve.set(j, false); + j += i; + } + i += 1; + } + black_box(&mut sieve); + }); +} diff --git a/tools/vendor/bit-vec/crusader.sh b/tools/vendor/bit-vec/crusader.sh new file mode 100755 index 0000000000..8becfed7cc --- /dev/null +++ b/tools/vendor/bit-vec/crusader.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +git clone https://github.com/brson/cargo-crusader +cd cargo-crusader +cargo build --release +export PATH=$PATH:`pwd`/target/release/ +cd ../ + +cargo crusader + +exit diff --git a/tools/vendor/bit-vec/src/lib.rs b/tools/vendor/bit-vec/src/lib.rs new file mode 100644 index 0000000000..4266fffd39 --- /dev/null +++ b/tools/vendor/bit-vec/src/lib.rs @@ -0,0 +1,3186 @@ +// Copyright 2012-2023 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// FIXME(Gankro): BitVec and BitSet are very tightly coupled. Ideally (for +// maintenance), they should be in separate files/modules, with BitSet only +// using BitVec's public API. This will be hard for performance though, because +// `BitVec` will not want to leak its internal representation while its internal +// representation as `u32`s must be assumed for best performance. + +// (1) Be careful, most things can overflow here because the amount of bits in +// memory can overflow `usize`. +// (2) Make sure that the underlying vector has no excess length: +// E. g. `nbits == 16`, `storage.len() == 2` would be excess length, +// because the last word isn't used at all. This is important because some +// methods rely on it (for *CORRECTNESS*). +// (3) Make sure that the unused bits in the last word are zeroed out, again +// other methods rely on it for *CORRECTNESS*. +// (4) `BitSet` is tightly coupled with `BitVec`, so any changes you make in +// `BitVec` will need to be reflected in `BitSet`. + +//! # Description +//! +//! Dynamic collections implemented with compact bit vectors. +//! +//! # Examples +//! +//! This is a simple example of the [Sieve of Eratosthenes][sieve] +//! which calculates prime numbers up to a given limit. +//! +//! [sieve]: http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes +//! +//! ``` +//! use bit_vec::BitVec; +//! +//! let max_prime = 10000; +//! +//! // Store the primes as a BitVec +//! let primes = { +//! // Assume all numbers are prime to begin, and then we +//! // cross off non-primes progressively +//! let mut bv = BitVec::from_elem(max_prime, true); +//! +//! // Neither 0 nor 1 are prime +//! bv.set(0, false); +//! bv.set(1, false); +//! +//! for i in 2.. 1 + (max_prime as f64).sqrt() as usize { +//! // if i is a prime +//! if bv[i] { +//! // Mark all multiples of i as non-prime (any multiples below i * i +//! // will have been marked as non-prime previously) +//! for j in i.. { +//! if i * j >= max_prime { +//! break; +//! } +//! bv.set(i * j, false) +//! } +//! } +//! } +//! bv +//! }; +//! +//! // Simple primality tests below our max bound +//! let print_primes = 20; +//! print!("The primes below {} are: ", print_primes); +//! for x in 0..print_primes { +//! if primes.get(x).unwrap_or(false) { +//! print!("{} ", x); +//! } +//! } +//! println!(); +//! +//! let num_primes = primes.iter().filter(|x| *x).count(); +//! println!("There are {} primes below {}", num_primes, max_prime); +//! assert_eq!(num_primes, 1_229); +//! ``` + +#![doc(html_root_url = "https://docs.rs/bit-vec/0.8.0")] +#![no_std] + +#[cfg(any(test, feature = "std"))] +#[macro_use] +extern crate std; +#[cfg(feature = "std")] +use std::rc::Rc; +#[cfg(feature = "std")] +use std::string::String; +#[cfg(feature = "std")] +use std::vec::Vec; + +#[cfg(feature = "serde")] +extern crate serde; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; +#[cfg(feature = "borsh")] +extern crate borsh; +#[cfg(feature = "miniserde")] +extern crate miniserde; +#[cfg(feature = "nanoserde")] +extern crate nanoserde; +#[cfg(feature = "nanoserde")] +use nanoserde::{DeBin, DeJson, DeRon, SerBin, SerJson, SerRon}; + +#[cfg(not(feature = "std"))] +#[macro_use] +extern crate alloc; +#[cfg(not(feature = "std"))] +use alloc::rc::Rc; +#[cfg(not(feature = "std"))] +use alloc::string::String; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; + +use core::cell::RefCell; +use core::cmp; +use core::cmp::Ordering; +use core::fmt::{self, Write}; +use core::hash; +use core::iter::repeat; +use core::iter::FromIterator; +use core::mem; +use core::ops::*; +use core::slice; + +type MutBlocks<'a, B> = slice::IterMut<'a, B>; + +/// Abstracts over a pile of bits (basically unsigned primitives) +pub trait BitBlock: + Copy + + Add + + Sub + + Shl + + Shr + + Not + + BitAnd + + BitOr + + BitXor + + Rem + + Eq + + Ord + + hash::Hash +{ + /// How many bits it has + fn bits() -> usize; + /// How many bytes it has + #[inline] + fn bytes() -> usize { + Self::bits() / 8 + } + /// Convert a byte into this type (lowest-order bits set) + fn from_byte(byte: u8) -> Self; + /// Count the number of 1's in the bitwise repr + fn count_ones(self) -> usize; + /// Count the number of 0's in the bitwise repr + fn count_zeros(self) -> usize { + Self::bits() - self.count_ones() + } + /// Get `0` + fn zero() -> Self; + /// Get `1` + fn one() -> Self; +} + +macro_rules! bit_block_impl { + ($(($t: ident, $size: expr)),*) => ($( + impl BitBlock for $t { + #[inline] + fn bits() -> usize { $size } + #[inline] + fn from_byte(byte: u8) -> Self { $t::from(byte) } + #[inline] + fn count_ones(self) -> usize { self.count_ones() as usize } + #[inline] + fn count_zeros(self) -> usize { self.count_zeros() as usize } + #[inline] + fn one() -> Self { 1 } + #[inline] + fn zero() -> Self { 0 } + } + )*) +} + +bit_block_impl! { + (u8, 8), + (u16, 16), + (u32, 32), + (u64, 64), + (usize, core::mem::size_of::() * 8) +} + +fn reverse_bits(byte: u8) -> u8 { + let mut result = 0; + for i in 0..u8::bits() { + result |= ((byte >> i) & 1) << (u8::bits() - 1 - i); + } + result +} + +static TRUE: bool = true; +static FALSE: bool = false; + +/// The bitvector type. +/// +/// # Examples +/// +/// ``` +/// use bit_vec::BitVec; +/// +/// let mut bv = BitVec::from_elem(10, false); +/// +/// // insert all primes less than 10 +/// bv.set(2, true); +/// bv.set(3, true); +/// bv.set(5, true); +/// bv.set(7, true); +/// println!("{:?}", bv); +/// println!("total bits set to true: {}", bv.iter().filter(|x| *x).count()); +/// +/// // flip all values in bitvector, producing non-primes less than 10 +/// bv.negate(); +/// println!("{:?}", bv); +/// println!("total bits set to true: {}", bv.iter().filter(|x| *x).count()); +/// +/// // reset bitvector to empty +/// bv.clear(); +/// println!("{:?}", bv); +/// println!("total bits set to true: {}", bv.iter().filter(|x| *x).count()); +/// ``` +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr( + feature = "borsh", + derive(borsh::BorshDeserialize, borsh::BorshSerialize) +)] +#[cfg_attr( + feature = "miniserde", + derive(miniserde::Deserialize, miniserde::Serialize) +)] +#[cfg_attr( + feature = "nanoserde", + derive(DeBin, DeJson, DeRon, SerBin, SerJson, SerRon) +)] +pub struct BitVec { + /// Internal representation of the bit vector + storage: Vec, + /// The number of valid bits in the internal representation + nbits: usize, +} + +// FIXME(Gankro): NopeNopeNopeNopeNope (wait for IndexGet to be a thing) +impl Index for BitVec { + type Output = bool; + + #[inline] + fn index(&self, i: usize) -> &bool { + if self.get(i).expect("index out of bounds") { + &TRUE + } else { + &FALSE + } + } +} + +/// Computes how many blocks are needed to store that many bits +fn blocks_for_bits(bits: usize) -> usize { + // If we want 17 bits, dividing by 32 will produce 0. So we add 1 to make sure we + // reserve enough. But if we want exactly a multiple of 32, this will actually allocate + // one too many. So we need to check if that's the case. We can do that by computing if + // bitwise AND by `32 - 1` is 0. But LLVM should be able to optimize the semantically + // superior modulo operator on a power of two to this. + // + // Note that we can technically avoid this branch with the expression + // `(nbits + U32_BITS - 1) / 32::BITS`, but if nbits is almost usize::MAX this will overflow. + if bits % B::bits() == 0 { + bits / B::bits() + } else { + bits / B::bits() + 1 + } +} + +/// Computes the bitmask for the final word of the vector +fn mask_for_bits(bits: usize) -> B { + // Note especially that a perfect multiple of U32_BITS should mask all 1s. + (!B::zero()) >> ((B::bits() - bits % B::bits()) % B::bits()) +} + +type B = u32; + +impl BitVec { + /// Creates an empty `BitVec`. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// let mut bv = BitVec::new(); + /// ``` + #[inline] + pub fn new() -> Self { + Default::default() + } + + /// Creates a `BitVec` that holds `nbits` elements, setting each element + /// to `bit`. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let mut bv = BitVec::from_elem(10, false); + /// assert_eq!(bv.len(), 10); + /// for x in bv.iter() { + /// assert_eq!(x, false); + /// } + /// ``` + #[inline] + pub fn from_elem(nbits: usize, bit: bool) -> Self { + let nblocks = blocks_for_bits::(nbits); + let mut bit_vec = BitVec { + storage: vec![if bit { !B::zero() } else { B::zero() }; nblocks], + nbits, + }; + bit_vec.fix_last_block(); + bit_vec + } + + /// Constructs a new, empty `BitVec` with the specified capacity. + /// + /// The bitvector will be able to hold at least `capacity` bits without + /// reallocating. If `capacity` is 0, it will not allocate. + /// + /// It is important to note that this function does not specify the + /// *length* of the returned bitvector, but only the *capacity*. + #[inline] + pub fn with_capacity(nbits: usize) -> Self { + BitVec { + storage: Vec::with_capacity(blocks_for_bits::(nbits)), + nbits: 0, + } + } + + /// Transforms a byte-vector into a `BitVec`. Each byte becomes eight bits, + /// with the most significant bits of each byte coming first. Each + /// bit becomes `true` if equal to 1 or `false` if equal to 0. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let bv = BitVec::from_bytes(&[0b10100000, 0b00010010]); + /// assert!(bv.eq_vec(&[true, false, true, false, + /// false, false, false, false, + /// false, false, false, true, + /// false, false, true, false])); + /// ``` + pub fn from_bytes(bytes: &[u8]) -> Self { + let len = bytes + .len() + .checked_mul(u8::bits()) + .expect("capacity overflow"); + let mut bit_vec = BitVec::with_capacity(len); + let complete_words = bytes.len() / B::bytes(); + let extra_bytes = bytes.len() % B::bytes(); + + bit_vec.nbits = len; + + for i in 0..complete_words { + let mut accumulator = B::zero(); + for idx in 0..B::bytes() { + accumulator |= B::from_byte(reverse_bits(bytes[i * B::bytes() + idx])) << (idx * 8) + } + bit_vec.storage.push(accumulator); + } + + if extra_bytes > 0 { + let mut last_word = B::zero(); + for (i, &byte) in bytes[complete_words * B::bytes()..].iter().enumerate() { + last_word |= B::from_byte(reverse_bits(byte)) << (i * 8); + } + bit_vec.storage.push(last_word); + } + + bit_vec + } + + /// Creates a `BitVec` of the specified length where the value at each index + /// is `f(index)`. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let bv = BitVec::from_fn(5, |i| { i % 2 == 0 }); + /// assert!(bv.eq_vec(&[true, false, true, false, true])); + /// ``` + #[inline] + pub fn from_fn(len: usize, mut f: F) -> Self + where + F: FnMut(usize) -> bool, + { + let mut bit_vec = BitVec::from_elem(len, false); + for i in 0..len { + bit_vec.set(i, f(i)); + } + bit_vec + } +} + +impl BitVec { + /// Applies the given operation to the blocks of self and other, and sets + /// self to be the result. This relies on the caller not to corrupt the + /// last word. + #[inline] + fn process(&mut self, other: &BitVec, mut op: F) -> bool + where + F: FnMut(B, B) -> B, + { + assert_eq!(self.len(), other.len()); + debug_assert_eq!(self.storage.len(), other.storage.len()); + let mut changed_bits = B::zero(); + for (a, b) in self.blocks_mut().zip(other.blocks()) { + let w = op(*a, b); + changed_bits = changed_bits | (*a ^ w); + *a = w; + } + changed_bits != B::zero() + } + + /// Iterator over mutable refs to the underlying blocks of data. + #[inline] + fn blocks_mut(&mut self) -> MutBlocks { + // (2) + self.storage.iter_mut() + } + + /// Iterator over the underlying blocks of data + #[inline] + pub fn blocks(&self) -> Blocks { + // (2) + Blocks { + iter: self.storage.iter(), + } + } + + /// Exposes the raw block storage of this `BitVec`. + /// + /// Only really intended for `BitSet`. + #[inline] + pub fn storage(&self) -> &[B] { + &self.storage + } + + /// Exposes the raw block storage of this `BitVec`. + /// + /// # Safety + /// + /// Can probably cause unsafety. Only really intended for `BitSet`. + #[inline] + pub unsafe fn storage_mut(&mut self) -> &mut Vec { + &mut self.storage + } + + /// Helper for procedures involving spare space in the last block. + #[inline] + fn last_block_with_mask(&self) -> Option<(B, B)> { + let extra_bits = self.len() % B::bits(); + if extra_bits > 0 { + let mask = (B::one() << extra_bits) - B::one(); + let storage_len = self.storage.len(); + Some((self.storage[storage_len - 1], mask)) + } else { + None + } + } + + /// Helper for procedures involving spare space in the last block. + #[inline] + fn last_block_mut_with_mask(&mut self) -> Option<(&mut B, B)> { + let extra_bits = self.len() % B::bits(); + if extra_bits > 0 { + let mask = (B::one() << extra_bits) - B::one(); + let storage_len = self.storage.len(); + Some((&mut self.storage[storage_len - 1], mask)) + } else { + None + } + } + + /// An operation might screw up the unused bits in the last block of the + /// `BitVec`. As per (3), it's assumed to be all 0s. This method fixes it up. + fn fix_last_block(&mut self) { + if let Some((last_block, used_bits)) = self.last_block_mut_with_mask() { + *last_block = *last_block & used_bits; + } + } + + /// Operations such as change detection for xnor, nor and nand are easiest + /// to implement when unused bits are all set to 1s. + fn fix_last_block_with_ones(&mut self) { + if let Some((last_block, used_bits)) = self.last_block_mut_with_mask() { + *last_block = *last_block | !used_bits; + } + } + + /// Check whether last block's invariant is fine. + fn is_last_block_fixed(&self) -> bool { + if let Some((last_block, used_bits)) = self.last_block_with_mask() { + last_block & !used_bits == B::zero() + } else { + true + } + } + + /// Ensure the invariant for the last block. + /// + /// An operation might screw up the unused bits in the last block of the + /// `BitVec`. + /// + /// This method fails in case the last block is not fixed. The check + /// is skipped outside testing. + #[inline] + fn ensure_invariant(&self) { + if cfg!(test) { + debug_assert!(self.is_last_block_fixed()); + } + } + + /// Retrieves the value at index `i`, or `None` if the index is out of bounds. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let bv = BitVec::from_bytes(&[0b01100000]); + /// assert_eq!(bv.get(0), Some(false)); + /// assert_eq!(bv.get(1), Some(true)); + /// assert_eq!(bv.get(100), None); + /// + /// // Can also use array indexing + /// assert_eq!(bv[1], true); + /// ``` + #[inline] + pub fn get(&self, i: usize) -> Option { + self.ensure_invariant(); + if i >= self.nbits { + return None; + } + let w = i / B::bits(); + let b = i % B::bits(); + self.storage + .get(w) + .map(|&block| (block & (B::one() << b)) != B::zero()) + } + + /// Retrieves the value at index `i`, without doing bounds checking. + /// + /// For a safe alternative, see `get`. + /// + /// # Safety + /// + /// Calling this method with an out-of-bounds index is undefined behavior + /// even if the resulting reference is not used. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let bv = BitVec::from_bytes(&[0b01100000]); + /// unsafe { + /// assert_eq!(bv.get_unchecked(0), false); + /// assert_eq!(bv.get_unchecked(1), true); + /// } + /// ``` + #[inline] + pub unsafe fn get_unchecked(&self, i: usize) -> bool { + self.ensure_invariant(); + let w = i / B::bits(); + let b = i % B::bits(); + let block = *self.storage.get_unchecked(w); + block & (B::one() << b) != B::zero() + } + + /// Retrieves a smart pointer to the value at index `i`, or `None` if the index is out of bounds. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let mut bv = BitVec::from_bytes(&[0b01100000]); + /// *bv.get_mut(0).unwrap() = true; + /// *bv.get_mut(1).unwrap() = false; + /// assert!(bv.get_mut(100).is_none()); + /// assert_eq!(bv, BitVec::from_bytes(&[0b10100000])); + /// ``` + #[inline] + pub fn get_mut(&mut self, index: usize) -> Option> { + self.get(index).map(move |value| MutBorrowedBit { + vec: Rc::new(RefCell::new(self)), + index, + #[cfg(debug_assertions)] + old_value: value, + new_value: value, + }) + } + + /// Retrieves a smart pointer to the value at index `i`, without doing bounds checking. + /// + /// # Safety + /// + /// Calling this method with out-of-bounds `index` may cause undefined behavior even when + /// the result is not used. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let mut bv = BitVec::from_bytes(&[0b01100000]); + /// unsafe { + /// *bv.get_unchecked_mut(0) = true; + /// *bv.get_unchecked_mut(1) = false; + /// } + /// assert_eq!(bv, BitVec::from_bytes(&[0b10100000])); + /// ``` + #[inline] + pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> MutBorrowedBit { + let value = self.get_unchecked(index); + MutBorrowedBit { + #[cfg(debug_assertions)] + old_value: value, + new_value: value, + vec: Rc::new(RefCell::new(self)), + index, + } + } + + /// Sets the value of a bit at an index `i`. + /// + /// # Panics + /// + /// Panics if `i` is out of bounds. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let mut bv = BitVec::from_elem(5, false); + /// bv.set(3, true); + /// assert_eq!(bv[3], true); + /// ``` + #[inline] + pub fn set(&mut self, i: usize, x: bool) { + self.ensure_invariant(); + assert!( + i < self.nbits, + "index out of bounds: {:?} >= {:?}", + i, + self.nbits + ); + let w = i / B::bits(); + let b = i % B::bits(); + let flag = B::one() << b; + let val = if x { + self.storage[w] | flag + } else { + self.storage[w] & !flag + }; + self.storage[w] = val; + } + + /// Sets all bits to 1. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let before = 0b01100000; + /// let after = 0b11111111; + /// + /// let mut bv = BitVec::from_bytes(&[before]); + /// bv.set_all(); + /// assert_eq!(bv, BitVec::from_bytes(&[after])); + /// ``` + #[inline] + pub fn set_all(&mut self) { + self.ensure_invariant(); + for w in &mut self.storage { + *w = !B::zero(); + } + self.fix_last_block(); + } + + /// Flips all bits. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let before = 0b01100000; + /// let after = 0b10011111; + /// + /// let mut bv = BitVec::from_bytes(&[before]); + /// bv.negate(); + /// assert_eq!(bv, BitVec::from_bytes(&[after])); + /// ``` + #[inline] + pub fn negate(&mut self) { + self.ensure_invariant(); + for w in &mut self.storage { + *w = !*w; + } + self.fix_last_block(); + } + + /// Calculates the union of two bitvectors. This acts like the bitwise `or` + /// function. + /// + /// Sets `self` to the union of `self` and `other`. Both bitvectors must be + /// the same length. Returns `true` if `self` changed. + /// + /// # Panics + /// + /// Panics if the bitvectors are of different lengths. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let a = 0b01100100; + /// let b = 0b01011010; + /// let res = 0b01111110; + /// + /// let mut a = BitVec::from_bytes(&[a]); + /// let b = BitVec::from_bytes(&[b]); + /// + /// assert!(a.union(&b)); + /// assert_eq!(a, BitVec::from_bytes(&[res])); + /// ``` + #[deprecated(since = "0.7.0", note = "Please use the 'or' function instead")] + #[inline] + pub fn union(&mut self, other: &Self) -> bool { + self.or(other) + } + + /// Calculates the intersection of two bitvectors. This acts like the + /// bitwise `and` function. + /// + /// Sets `self` to the intersection of `self` and `other`. Both bitvectors + /// must be the same length. Returns `true` if `self` changed. + /// + /// # Panics + /// + /// Panics if the bitvectors are of different lengths. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let a = 0b01100100; + /// let b = 0b01011010; + /// let res = 0b01000000; + /// + /// let mut a = BitVec::from_bytes(&[a]); + /// let b = BitVec::from_bytes(&[b]); + /// + /// assert!(a.intersect(&b)); + /// assert_eq!(a, BitVec::from_bytes(&[res])); + /// ``` + #[deprecated(since = "0.7.0", note = "Please use the 'and' function instead")] + #[inline] + pub fn intersect(&mut self, other: &Self) -> bool { + self.and(other) + } + + /// Calculates the bitwise `or` of two bitvectors. + /// + /// Sets `self` to the union of `self` and `other`. Both bitvectors must be + /// the same length. Returns `true` if `self` changed. + /// + /// # Panics + /// + /// Panics if the bitvectors are of different lengths. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let a = 0b01100100; + /// let b = 0b01011010; + /// let res = 0b01111110; + /// + /// let mut a = BitVec::from_bytes(&[a]); + /// let b = BitVec::from_bytes(&[b]); + /// + /// assert!(a.or(&b)); + /// assert_eq!(a, BitVec::from_bytes(&[res])); + /// ``` + #[inline] + pub fn or(&mut self, other: &Self) -> bool { + self.ensure_invariant(); + debug_assert!(other.is_last_block_fixed()); + self.process(other, |w1, w2| (w1 | w2)) + } + + /// Calculates the bitwise `and` of two bitvectors. + /// + /// Sets `self` to the intersection of `self` and `other`. Both bitvectors + /// must be the same length. Returns `true` if `self` changed. + /// + /// # Panics + /// + /// Panics if the bitvectors are of different lengths. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let a = 0b01100100; + /// let b = 0b01011010; + /// let res = 0b01000000; + /// + /// let mut a = BitVec::from_bytes(&[a]); + /// let b = BitVec::from_bytes(&[b]); + /// + /// assert!(a.and(&b)); + /// assert_eq!(a, BitVec::from_bytes(&[res])); + /// ``` + #[inline] + pub fn and(&mut self, other: &Self) -> bool { + self.ensure_invariant(); + debug_assert!(other.is_last_block_fixed()); + self.process(other, |w1, w2| (w1 & w2)) + } + + /// Calculates the difference between two bitvectors. + /// + /// Sets each element of `self` to the value of that element minus the + /// element of `other` at the same index. Both bitvectors must be the same + /// length. Returns `true` if `self` changed. + /// + /// # Panics + /// + /// Panics if the bitvectors are of different length. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let a = 0b01100100; + /// let b = 0b01011010; + /// let a_b = 0b00100100; // a - b + /// let b_a = 0b00011010; // b - a + /// + /// let mut bva = BitVec::from_bytes(&[a]); + /// let bvb = BitVec::from_bytes(&[b]); + /// + /// assert!(bva.difference(&bvb)); + /// assert_eq!(bva, BitVec::from_bytes(&[a_b])); + /// + /// let bva = BitVec::from_bytes(&[a]); + /// let mut bvb = BitVec::from_bytes(&[b]); + /// + /// assert!(bvb.difference(&bva)); + /// assert_eq!(bvb, BitVec::from_bytes(&[b_a])); + /// ``` + #[inline] + pub fn difference(&mut self, other: &Self) -> bool { + self.ensure_invariant(); + debug_assert!(other.is_last_block_fixed()); + self.process(other, |w1, w2| (w1 & !w2)) + } + + /// Calculates the xor of two bitvectors. + /// + /// Sets `self` to the xor of `self` and `other`. Both bitvectors must be + /// the same length. Returns `true` if `self` changed. + /// + /// # Panics + /// + /// Panics if the bitvectors are of different length. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let a = 0b01100110; + /// let b = 0b01010100; + /// let res = 0b00110010; + /// + /// let mut a = BitVec::from_bytes(&[a]); + /// let b = BitVec::from_bytes(&[b]); + /// + /// assert!(a.xor(&b)); + /// assert_eq!(a, BitVec::from_bytes(&[res])); + /// ``` + #[inline] + pub fn xor(&mut self, other: &Self) -> bool { + self.ensure_invariant(); + debug_assert!(other.is_last_block_fixed()); + self.process(other, |w1, w2| (w1 ^ w2)) + } + + /// Calculates the nand of two bitvectors. + /// + /// Sets `self` to the nand of `self` and `other`. Both bitvectors must be + /// the same length. Returns `true` if `self` changed. + /// + /// # Panics + /// + /// Panics if the bitvectors are of different length. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let a = 0b01100110; + /// let b = 0b01010100; + /// let res = 0b10111011; + /// + /// let mut a = BitVec::from_bytes(&[a]); + /// let b = BitVec::from_bytes(&[b]); + /// + /// assert!(a.nand(&b)); + /// assert_eq!(a, BitVec::from_bytes(&[res])); + /// ``` + #[inline] + pub fn nand(&mut self, other: &Self) -> bool { + self.ensure_invariant(); + debug_assert!(other.is_last_block_fixed()); + self.fix_last_block_with_ones(); + let result = self.process(other, |w1, w2| !(w1 & w2)); + self.fix_last_block(); + result + } + + /// Calculates the nor of two bitvectors. + /// + /// Sets `self` to the nor of `self` and `other`. Both bitvectors must be + /// the same length. Returns `true` if `self` changed. + /// + /// # Panics + /// + /// Panics if the bitvectors are of different length. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let a = 0b01100110; + /// let b = 0b01010100; + /// let res = 0b10001001; + /// + /// let mut a = BitVec::from_bytes(&[a]); + /// let b = BitVec::from_bytes(&[b]); + /// + /// assert!(a.nor(&b)); + /// assert_eq!(a, BitVec::from_bytes(&[res])); + /// ``` + #[inline] + pub fn nor(&mut self, other: &Self) -> bool { + self.ensure_invariant(); + debug_assert!(other.is_last_block_fixed()); + self.fix_last_block_with_ones(); + let result = self.process(other, |w1, w2| !(w1 | w2)); + self.fix_last_block(); + result + } + + /// Calculates the xnor of two bitvectors. + /// + /// Sets `self` to the xnor of `self` and `other`. Both bitvectors must be + /// the same length. Returns `true` if `self` changed. + /// + /// # Panics + /// + /// Panics if the bitvectors are of different length. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let a = 0b01100110; + /// let b = 0b01010100; + /// let res = 0b11001101; + /// + /// let mut a = BitVec::from_bytes(&[a]); + /// let b = BitVec::from_bytes(&[b]); + /// + /// assert!(a.xnor(&b)); + /// assert_eq!(a, BitVec::from_bytes(&[res])); + /// ``` + #[inline] + pub fn xnor(&mut self, other: &Self) -> bool { + self.ensure_invariant(); + debug_assert!(other.is_last_block_fixed()); + self.fix_last_block_with_ones(); + let result = self.process(other, |w1, w2| !(w1 ^ w2)); + self.fix_last_block(); + result + } + + /// Returns `true` if all bits are 1. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let mut bv = BitVec::from_elem(5, true); + /// assert_eq!(bv.all(), true); + /// + /// bv.set(1, false); + /// assert_eq!(bv.all(), false); + /// ``` + #[inline] + pub fn all(&self) -> bool { + self.ensure_invariant(); + let mut last_word = !B::zero(); + // Check that every block but the last is all-ones... + self.blocks().all(|elem| { + let tmp = last_word; + last_word = elem; + tmp == !B::zero() + // and then check the last one has enough ones + }) && (last_word == mask_for_bits(self.nbits)) + } + + /// Returns the number of ones in the binary representation. + /// + /// Also known as the + /// [Hamming weight](https://en.wikipedia.org/wiki/Hamming_weight). + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let mut bv = BitVec::from_elem(100, true); + /// assert_eq!(bv.count_ones(), 100); + /// + /// bv.set(50, false); + /// assert_eq!(bv.count_ones(), 99); + /// ``` + #[inline] + pub fn count_ones(&self) -> u64 { + self.ensure_invariant(); + // Add the number of ones of each block. + self.blocks().map(|elem| elem.count_ones() as u64).sum() + } + + /// Returns the number of zeros in the binary representation. + /// + /// Also known as the opposite of + /// [Hamming weight](https://en.wikipedia.org/wiki/Hamming_weight). + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let mut bv = BitVec::from_elem(100, false); + /// assert_eq!(bv.count_zeros(), 100); + /// + /// bv.set(50, true); + /// assert_eq!(bv.count_zeros(), 99); + /// ``` + #[inline] + pub fn count_zeros(&self) -> u64 { + self.ensure_invariant(); + // Add the number of zeros of each block. + let extra_zeros = (B::bits() - (self.len() % B::bits())) % B::bits(); + self.blocks() + .map(|elem| elem.count_zeros() as u64) + .sum::() + - extra_zeros as u64 + } + + /// Returns an iterator over the elements of the vector in order. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let bv = BitVec::from_bytes(&[0b01110100, 0b10010010]); + /// assert_eq!(bv.iter().filter(|x| *x).count(), 7); + /// ``` + #[inline] + pub fn iter(&self) -> Iter { + self.ensure_invariant(); + Iter { + bit_vec: self, + range: 0..self.nbits, + } + } + + /// Returns an iterator over mutable smart pointers to the elements of the vector in order. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let mut a = BitVec::from_elem(8, false); + /// a.iter_mut().enumerate().for_each(|(index, mut bit)| { + /// *bit = if index % 2 == 1 { true } else { false }; + /// }); + /// assert!(a.eq_vec(&[ + /// false, true, false, true, false, true, false, true + /// ])); + /// ``` + #[inline] + pub fn iter_mut(&mut self) -> IterMut { + self.ensure_invariant(); + let nbits = self.nbits; + IterMut { + vec: Rc::new(RefCell::new(self)), + range: 0..nbits, + } + } + + /// Moves all bits from `other` into `Self`, leaving `other` empty. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let mut a = BitVec::from_bytes(&[0b10000000]); + /// let mut b = BitVec::from_bytes(&[0b01100001]); + /// + /// a.append(&mut b); + /// + /// assert_eq!(a.len(), 16); + /// assert_eq!(b.len(), 0); + /// assert!(a.eq_vec(&[true, false, false, false, false, false, false, false, + /// false, true, true, false, false, false, false, true])); + /// ``` + pub fn append(&mut self, other: &mut Self) { + self.ensure_invariant(); + debug_assert!(other.is_last_block_fixed()); + + let b = self.len() % B::bits(); + let o = other.len() % B::bits(); + let will_overflow = (b + o > B::bits()) || (o == 0 && b != 0); + + self.nbits += other.len(); + other.nbits = 0; + + if b == 0 { + self.storage.append(&mut other.storage); + } else { + self.storage.reserve(other.storage.len()); + + for block in other.storage.drain(..) { + { + let last = self.storage.last_mut().unwrap(); + *last = *last | (block << b); + } + self.storage.push(block >> (B::bits() - b)); + } + + // Remove additional block if the last shift did not overflow + if !will_overflow { + self.storage.pop(); + } + } + } + + /// Splits the `BitVec` into two at the given bit, + /// retaining the first half in-place and returning the second one. + /// + /// # Panics + /// + /// Panics if `at` is out of bounds. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// let mut a = BitVec::new(); + /// a.push(true); + /// a.push(false); + /// a.push(false); + /// a.push(true); + /// + /// let b = a.split_off(2); + /// + /// assert_eq!(a.len(), 2); + /// assert_eq!(b.len(), 2); + /// assert!(a.eq_vec(&[true, false])); + /// assert!(b.eq_vec(&[false, true])); + /// ``` + pub fn split_off(&mut self, at: usize) -> Self { + self.ensure_invariant(); + assert!(at <= self.len(), "`at` out of bounds"); + + let mut other = BitVec::::default(); + + if at == 0 { + mem::swap(self, &mut other); + return other; + } else if at == self.len() { + return other; + } + + let w = at / B::bits(); + let b = at % B::bits(); + other.nbits = self.nbits - at; + self.nbits = at; + if b == 0 { + // Split at block boundary + other.storage = self.storage.split_off(w); + } else { + other.storage.reserve(self.storage.len() - w); + + { + let mut iter = self.storage[w..].iter(); + let mut last = *iter.next().unwrap(); + for &cur in iter { + other.storage.push((last >> b) | (cur << (B::bits() - b))); + last = cur; + } + other.storage.push(last >> b); + } + + self.storage.truncate(w + 1); + self.fix_last_block(); + } + + other + } + + /// Returns `true` if all bits are 0. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let mut bv = BitVec::from_elem(10, false); + /// assert_eq!(bv.none(), true); + /// + /// bv.set(3, true); + /// assert_eq!(bv.none(), false); + /// ``` + #[inline] + pub fn none(&self) -> bool { + self.blocks().all(|w| w == B::zero()) + } + + /// Returns `true` if any bit is 1. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let mut bv = BitVec::from_elem(10, false); + /// assert_eq!(bv.any(), false); + /// + /// bv.set(3, true); + /// assert_eq!(bv.any(), true); + /// ``` + #[inline] + pub fn any(&self) -> bool { + !self.none() + } + + /// Organises the bits into bytes, such that the first bit in the + /// `BitVec` becomes the high-order bit of the first byte. If the + /// size of the `BitVec` is not a multiple of eight then trailing bits + /// will be filled-in with `false`. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let mut bv = BitVec::from_elem(3, true); + /// bv.set(1, false); + /// + /// assert_eq!(bv.to_bytes(), [0b10100000]); + /// + /// let mut bv = BitVec::from_elem(9, false); + /// bv.set(2, true); + /// bv.set(8, true); + /// + /// assert_eq!(bv.to_bytes(), [0b00100000, 0b10000000]); + /// ``` + pub fn to_bytes(&self) -> Vec { + self.ensure_invariant(); + // Oh lord, we're mapping this to bytes bit-by-bit! + fn bit(bit_vec: &BitVec, byte: usize, bit: usize) -> u8 { + let offset = byte * 8 + bit; + if offset >= bit_vec.nbits { + 0 + } else { + (bit_vec[offset] as u8) << (7 - bit) + } + } + + let len = self.nbits / 8 + if self.nbits % 8 == 0 { 0 } else { 1 }; + (0..len) + .map(|i| { + bit(self, i, 0) + | bit(self, i, 1) + | bit(self, i, 2) + | bit(self, i, 3) + | bit(self, i, 4) + | bit(self, i, 5) + | bit(self, i, 6) + | bit(self, i, 7) + }) + .collect() + } + + /// Compares a `BitVec` to a slice of `bool`s. + /// Both the `BitVec` and slice must have the same length. + /// + /// # Panics + /// + /// Panics if the `BitVec` and slice are of different length. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let bv = BitVec::from_bytes(&[0b10100000]); + /// + /// assert!(bv.eq_vec(&[true, false, true, false, + /// false, false, false, false])); + /// ``` + #[inline] + pub fn eq_vec(&self, v: &[bool]) -> bool { + assert_eq!(self.nbits, v.len()); + self.iter().zip(v.iter().cloned()).all(|(b1, b2)| b1 == b2) + } + + /// Shortens a `BitVec`, dropping excess elements. + /// + /// If `len` is greater than the vector's current length, this has no + /// effect. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let mut bv = BitVec::from_bytes(&[0b01001011]); + /// bv.truncate(2); + /// assert!(bv.eq_vec(&[false, true])); + /// ``` + #[inline] + pub fn truncate(&mut self, len: usize) { + self.ensure_invariant(); + if len < self.len() { + self.nbits = len; + // This fixes (2). + self.storage.truncate(blocks_for_bits::(len)); + self.fix_last_block(); + } + } + + /// Reserves capacity for at least `additional` more bits to be inserted in the given + /// `BitVec`. The collection may reserve more space to avoid frequent reallocations. + /// + /// # Panics + /// + /// Panics if the new capacity overflows `usize`. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let mut bv = BitVec::from_elem(3, false); + /// bv.reserve(10); + /// assert_eq!(bv.len(), 3); + /// assert!(bv.capacity() >= 13); + /// ``` + #[inline] + pub fn reserve(&mut self, additional: usize) { + let desired_cap = self + .len() + .checked_add(additional) + .expect("capacity overflow"); + let storage_len = self.storage.len(); + if desired_cap > self.capacity() { + self.storage + .reserve(blocks_for_bits::(desired_cap) - storage_len); + } + } + + /// Reserves the minimum capacity for exactly `additional` more bits to be inserted in the + /// given `BitVec`. Does nothing if the capacity is already sufficient. + /// + /// Note that the allocator may give the collection more space than it requests. Therefore + /// capacity can not be relied upon to be precisely minimal. Prefer `reserve` if future + /// insertions are expected. + /// + /// # Panics + /// + /// Panics if the new capacity overflows `usize`. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let mut bv = BitVec::from_elem(3, false); + /// bv.reserve(10); + /// assert_eq!(bv.len(), 3); + /// assert!(bv.capacity() >= 13); + /// ``` + #[inline] + pub fn reserve_exact(&mut self, additional: usize) { + let desired_cap = self + .len() + .checked_add(additional) + .expect("capacity overflow"); + let storage_len = self.storage.len(); + if desired_cap > self.capacity() { + self.storage + .reserve_exact(blocks_for_bits::(desired_cap) - storage_len); + } + } + + /// Returns the capacity in bits for this bit vector. Inserting any + /// element less than this amount will not trigger a resizing. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let mut bv = BitVec::new(); + /// bv.reserve(10); + /// assert!(bv.capacity() >= 10); + /// ``` + #[inline] + pub fn capacity(&self) -> usize { + self.storage.capacity().saturating_mul(B::bits()) + } + + /// Grows the `BitVec` in-place, adding `n` copies of `value` to the `BitVec`. + /// + /// # Panics + /// + /// Panics if the new len overflows a `usize`. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let mut bv = BitVec::from_bytes(&[0b01001011]); + /// bv.grow(2, true); + /// assert_eq!(bv.len(), 10); + /// assert_eq!(bv.to_bytes(), [0b01001011, 0b11000000]); + /// ``` + pub fn grow(&mut self, n: usize, value: bool) { + self.ensure_invariant(); + + // Note: we just bulk set all the bits in the last word in this fn in multiple places + // which is technically wrong if not all of these bits are to be used. However, at the end + // of this fn we call `fix_last_block` at the end of this fn, which should fix this. + + let new_nbits = self.nbits.checked_add(n).expect("capacity overflow"); + let new_nblocks = blocks_for_bits::(new_nbits); + let full_value = if value { !B::zero() } else { B::zero() }; + + // Correct the old tail word, setting or clearing formerly unused bits + let num_cur_blocks = blocks_for_bits::(self.nbits); + if self.nbits % B::bits() > 0 { + let mask = mask_for_bits::(self.nbits); + if value { + let block = &mut self.storage[num_cur_blocks - 1]; + *block = *block | !mask; + } else { + // Extra bits are already zero by invariant. + } + } + + // Fill in words after the old tail word + let stop_idx = cmp::min(self.storage.len(), new_nblocks); + for idx in num_cur_blocks..stop_idx { + self.storage[idx] = full_value; + } + + // Allocate new words, if needed + if new_nblocks > self.storage.len() { + let to_add = new_nblocks - self.storage.len(); + self.storage.extend(repeat(full_value).take(to_add)); + } + + // Adjust internal bit count + self.nbits = new_nbits; + + self.fix_last_block(); + } + + /// Removes the last bit from the `BitVec`, and returns it. Returns `None` if the `BitVec` is empty. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let mut bv = BitVec::from_bytes(&[0b01001001]); + /// assert_eq!(bv.pop(), Some(true)); + /// assert_eq!(bv.pop(), Some(false)); + /// assert_eq!(bv.len(), 6); + /// ``` + #[inline] + pub fn pop(&mut self) -> Option { + self.ensure_invariant(); + + if self.is_empty() { + None + } else { + let i = self.nbits - 1; + let ret = self[i]; + // (3) + self.set(i, false); + self.nbits = i; + if self.nbits % B::bits() == 0 { + // (2) + self.storage.pop(); + } + Some(ret) + } + } + + /// Pushes a `bool` onto the end. + /// + /// # Examples + /// + /// ``` + /// use bit_vec::BitVec; + /// + /// let mut bv = BitVec::new(); + /// bv.push(true); + /// bv.push(false); + /// assert!(bv.eq_vec(&[true, false])); + /// ``` + #[inline] + pub fn push(&mut self, elem: bool) { + if self.nbits % B::bits() == 0 { + self.storage.push(B::zero()); + } + let insert_pos = self.nbits; + self.nbits = self.nbits.checked_add(1).expect("Capacity overflow"); + self.set(insert_pos, elem); + } + + /// Returns the total number of bits in this vector + #[inline] + pub fn len(&self) -> usize { + self.nbits + } + + /// Sets the number of bits that this `BitVec` considers initialized. + /// + /// # Safety + /// + /// Almost certainly can cause bad stuff. Only really intended for `BitSet`. + #[inline] + pub unsafe fn set_len(&mut self, len: usize) { + self.nbits = len; + } + + /// Returns true if there are no bits in this vector + #[inline] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Clears all bits in this vector. + #[inline] + pub fn clear(&mut self) { + self.ensure_invariant(); + for w in &mut self.storage { + *w = B::zero(); + } + } + + /// Shrinks the capacity of the underlying storage as much as + /// possible. + /// + /// It will drop down as close as possible to the length but the + /// allocator may still inform the underlying storage that there + /// is space for a few more elements/bits. + pub fn shrink_to_fit(&mut self) { + self.storage.shrink_to_fit(); + } + + /// Inserts a given bit at index `at`, shifting all bits after by one + /// + /// # Panics + /// Panics if `at` is out of bounds for `BitVec`'s length (that is, if `at > BitVec::len()`) + /// + /// # Examples + ///``` + /// use bit_vec::BitVec; + /// + /// let mut b = BitVec::new(); + /// + /// b.push(true); + /// b.push(true); + /// b.insert(1, false); + /// + /// assert!(b.eq_vec(&[true, false, true])); + ///``` + /// + /// # Time complexity + /// Takes O([`len`]) time. All items after the insertion index must be + /// shifted to the right. In the worst case, all elements are shifted when + /// the insertion index is 0. + /// + /// [`len`]: Self::len + pub fn insert(&mut self, at: usize, bit: bool) { + assert!( + at <= self.nbits, + "insertion index (is {at}) should be <= nbits (is {nbits})", + nbits = self.nbits + ); + + let last_block_bits = self.nbits % B::bits(); + let block_at = at / B::bits(); // needed block + let bit_at = at % B::bits(); // index within the block + + if last_block_bits == 0 { + self.storage.push(B::zero()); + } + + self.nbits += 1; + + let mut carry = self.storage[block_at] >> (B::bits() - 1); + let lsbits_mask = (B::one() << bit_at) - B::one(); + let set_bit = if bit { B::one() } else { B::zero() } << bit_at; + self.storage[block_at] = (self.storage[block_at] & lsbits_mask) + | ((self.storage[block_at] & !lsbits_mask) << 1) + | set_bit; + + for block_ref in &mut self.storage[block_at + 1..] { + let curr_carry = *block_ref >> (B::bits() - 1); + *block_ref = *block_ref << 1 | carry; + carry = curr_carry; + } + } +} + +impl Default for BitVec { + #[inline] + fn default() -> Self { + BitVec { + storage: Vec::new(), + nbits: 0, + } + } +} + +impl FromIterator for BitVec { + #[inline] + fn from_iter>(iter: I) -> Self { + let mut ret: Self = Default::default(); + ret.extend(iter); + ret + } +} + +impl Extend for BitVec { + #[inline] + fn extend>(&mut self, iterable: I) { + self.ensure_invariant(); + let iterator = iterable.into_iter(); + let (min, _) = iterator.size_hint(); + self.reserve(min); + for element in iterator { + self.push(element) + } + } +} + +impl Clone for BitVec { + #[inline] + fn clone(&self) -> Self { + self.ensure_invariant(); + BitVec { + storage: self.storage.clone(), + nbits: self.nbits, + } + } + + #[inline] + fn clone_from(&mut self, source: &Self) { + debug_assert!(source.is_last_block_fixed()); + self.nbits = source.nbits; + self.storage.clone_from(&source.storage); + } +} + +impl PartialOrd for BitVec { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for BitVec { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.ensure_invariant(); + debug_assert!(other.is_last_block_fixed()); + let mut a = self.iter(); + let mut b = other.iter(); + loop { + match (a.next(), b.next()) { + (Some(x), Some(y)) => match x.cmp(&y) { + Ordering::Equal => {} + otherwise => return otherwise, + }, + (None, None) => return Ordering::Equal, + (None, _) => return Ordering::Less, + (_, None) => return Ordering::Greater, + } + } + } +} + +impl fmt::Display for BitVec { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + self.ensure_invariant(); + for bit in self { + fmt.write_char(if bit { '1' } else { '0' })?; + } + Ok(()) + } +} + +impl fmt::Debug for BitVec { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + self.ensure_invariant(); + let mut storage = String::with_capacity(self.len() + self.len() / B::bits()); + for (i, bit) in self.iter().enumerate() { + if i != 0 && i % B::bits() == 0 { + storage.push(' '); + } + storage.push(if bit { '1' } else { '0' }); + } + fmt.debug_struct("BitVec") + .field("storage", &storage) + .field("nbits", &self.nbits) + .finish() + } +} + +impl hash::Hash for BitVec { + #[inline] + fn hash(&self, state: &mut H) { + self.ensure_invariant(); + self.nbits.hash(state); + for elem in self.blocks() { + elem.hash(state); + } + } +} + +impl cmp::PartialEq for BitVec { + #[inline] + fn eq(&self, other: &Self) -> bool { + if self.nbits != other.nbits { + self.ensure_invariant(); + other.ensure_invariant(); + return false; + } + self.blocks().zip(other.blocks()).all(|(w1, w2)| w1 == w2) + } +} + +impl cmp::Eq for BitVec {} + +/// An iterator for `BitVec`. +#[derive(Clone)] +pub struct Iter<'a, B: 'a = u32> { + bit_vec: &'a BitVec, + range: Range, +} + +#[derive(Debug)] +pub struct MutBorrowedBit<'a, B: 'a + BitBlock> { + vec: Rc>>, + index: usize, + #[cfg(debug_assertions)] + old_value: bool, + new_value: bool, +} + +/// An iterator for mutable references to the bits in a `BitVec`. +pub struct IterMut<'a, B: 'a + BitBlock = u32> { + vec: Rc>>, + range: Range, +} + +impl<'a, B: 'a + BitBlock> IterMut<'a, B> { + fn get(&mut self, index: Option) -> Option> { + let index = index?; + let value = (*self.vec).borrow().get(index)?; + Some(MutBorrowedBit { + vec: self.vec.clone(), + index, + #[cfg(debug_assertions)] + old_value: value, + new_value: value, + }) + } +} + +impl<'a, B: BitBlock> Deref for MutBorrowedBit<'a, B> { + type Target = bool; + + fn deref(&self) -> &Self::Target { + &self.new_value + } +} + +impl<'a, B: BitBlock> DerefMut for MutBorrowedBit<'a, B> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.new_value + } +} + +impl<'a, B: BitBlock> Drop for MutBorrowedBit<'a, B> { + fn drop(&mut self) { + let mut vec = (*self.vec).borrow_mut(); + #[cfg(debug_assertions)] + debug_assert_eq!( + Some(self.old_value), + vec.get(self.index), + "Mutably-borrowed bit was modified externally!" + ); + vec.set(self.index, self.new_value); + } +} + +impl<'a, B: BitBlock> Iterator for Iter<'a, B> { + type Item = bool; + + #[inline] + fn next(&mut self) -> Option { + // NB: indexing is slow for extern crates when it has to go through &TRUE or &FALSE + // variables. get is more direct, and unwrap is fine since we're sure of the range. + self.range.next().map(|i| self.bit_vec.get(i).unwrap()) + } + + fn size_hint(&self) -> (usize, Option) { + self.range.size_hint() + } +} + +impl<'a, B: BitBlock> Iterator for IterMut<'a, B> { + type Item = MutBorrowedBit<'a, B>; + + #[inline] + fn next(&mut self) -> Option { + let index = self.range.next(); + self.get(index) + } + + fn size_hint(&self) -> (usize, Option) { + self.range.size_hint() + } +} + +impl<'a, B: BitBlock> DoubleEndedIterator for Iter<'a, B> { + #[inline] + fn next_back(&mut self) -> Option { + self.range.next_back().map(|i| self.bit_vec.get(i).unwrap()) + } +} + +impl<'a, B: BitBlock> DoubleEndedIterator for IterMut<'a, B> { + #[inline] + fn next_back(&mut self) -> Option { + let index = self.range.next_back(); + self.get(index) + } +} + +impl<'a, B: BitBlock> ExactSizeIterator for Iter<'a, B> {} + +impl<'a, B: BitBlock> ExactSizeIterator for IterMut<'a, B> {} + +impl<'a, B: BitBlock> IntoIterator for &'a BitVec { + type Item = bool; + type IntoIter = Iter<'a, B>; + + #[inline] + fn into_iter(self) -> Iter<'a, B> { + self.iter() + } +} + +pub struct IntoIter { + bit_vec: BitVec, + range: Range, +} + +impl Iterator for IntoIter { + type Item = bool; + + #[inline] + fn next(&mut self) -> Option { + self.range.next().map(|i| self.bit_vec.get(i).unwrap()) + } +} + +impl DoubleEndedIterator for IntoIter { + #[inline] + fn next_back(&mut self) -> Option { + self.range.next_back().map(|i| self.bit_vec.get(i).unwrap()) + } +} + +impl ExactSizeIterator for IntoIter {} + +impl IntoIterator for BitVec { + type Item = bool; + type IntoIter = IntoIter; + + #[inline] + fn into_iter(self) -> IntoIter { + let nbits = self.nbits; + IntoIter { + bit_vec: self, + range: 0..nbits, + } + } +} + +/// An iterator over the blocks of a `BitVec`. +#[derive(Clone)] +pub struct Blocks<'a, B: 'a> { + iter: slice::Iter<'a, B>, +} + +impl<'a, B: BitBlock> Iterator for Blocks<'a, B> { + type Item = B; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next().cloned() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a, B: BitBlock> DoubleEndedIterator for Blocks<'a, B> { + #[inline] + fn next_back(&mut self) -> Option { + self.iter.next_back().cloned() + } +} + +impl<'a, B: BitBlock> ExactSizeIterator for Blocks<'a, B> {} + +#[cfg(test)] +mod tests { + use super::{BitVec, Iter, Vec}; + + // This is stupid, but I want to differentiate from a "random" 32 + const U32_BITS: usize = 32; + + #[test] + fn test_display_output() { + assert_eq!(format!("{}", BitVec::new()), ""); + assert_eq!(format!("{}", BitVec::from_elem(1, true)), "1"); + assert_eq!(format!("{}", BitVec::from_elem(8, false)), "00000000") + } + + #[test] + fn test_debug_output() { + assert_eq!( + format!("{:?}", BitVec::new()), + "BitVec { storage: \"\", nbits: 0 }" + ); + assert_eq!( + format!("{:?}", BitVec::from_elem(1, true)), + "BitVec { storage: \"1\", nbits: 1 }" + ); + assert_eq!( + format!("{:?}", BitVec::from_elem(8, false)), + "BitVec { storage: \"00000000\", nbits: 8 }" + ); + assert_eq!( + format!("{:?}", BitVec::from_elem(33, true)), + "BitVec { storage: \"11111111111111111111111111111111 1\", nbits: 33 }" + ); + assert_eq!( + format!( + "{:?}", + BitVec::from_bytes(&[0b111, 0b000, 0b1110, 0b0001, 0b11111111, 0b00000000]) + ), + "BitVec { storage: \"00000111000000000000111000000001 1111111100000000\", nbits: 48 }" + ) + } + + #[test] + fn test_0_elements() { + let act = BitVec::new(); + let exp = Vec::new(); + assert!(act.eq_vec(&exp)); + assert!(act.none() && act.all()); + } + + #[test] + fn test_1_element() { + let mut act = BitVec::from_elem(1, false); + assert!(act.eq_vec(&[false])); + assert!(act.none() && !act.all()); + act = BitVec::from_elem(1, true); + assert!(act.eq_vec(&[true])); + assert!(!act.none() && act.all()); + } + + #[test] + fn test_2_elements() { + let mut b = BitVec::from_elem(2, false); + b.set(0, true); + b.set(1, false); + assert_eq!(format!("{}", b), "10"); + assert!(!b.none() && !b.all()); + } + + #[test] + fn test_10_elements() { + // all 0 + + let mut act = BitVec::from_elem(10, false); + assert!( + (act.eq_vec(&[false, false, false, false, false, false, false, false, false, false])) + ); + assert!(act.none() && !act.all()); + // all 1 + + act = BitVec::from_elem(10, true); + assert!((act.eq_vec(&[true, true, true, true, true, true, true, true, true, true]))); + assert!(!act.none() && act.all()); + // mixed + + act = BitVec::from_elem(10, false); + act.set(0, true); + act.set(1, true); + act.set(2, true); + act.set(3, true); + act.set(4, true); + assert!((act.eq_vec(&[true, true, true, true, true, false, false, false, false, false]))); + assert!(!act.none() && !act.all()); + // mixed + + act = BitVec::from_elem(10, false); + act.set(5, true); + act.set(6, true); + act.set(7, true); + act.set(8, true); + act.set(9, true); + assert!((act.eq_vec(&[false, false, false, false, false, true, true, true, true, true]))); + assert!(!act.none() && !act.all()); + // mixed + + act = BitVec::from_elem(10, false); + act.set(0, true); + act.set(3, true); + act.set(6, true); + act.set(9, true); + assert!((act.eq_vec(&[true, false, false, true, false, false, true, false, false, true]))); + assert!(!act.none() && !act.all()); + } + + #[test] + fn test_31_elements() { + // all 0 + + let mut act = BitVec::from_elem(31, false); + assert!(act.eq_vec(&[ + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false + ])); + assert!(act.none() && !act.all()); + // all 1 + + act = BitVec::from_elem(31, true); + assert!(act.eq_vec(&[ + true, true, true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true + ])); + assert!(!act.none() && act.all()); + // mixed + + act = BitVec::from_elem(31, false); + act.set(0, true); + act.set(1, true); + act.set(2, true); + act.set(3, true); + act.set(4, true); + act.set(5, true); + act.set(6, true); + act.set(7, true); + assert!(act.eq_vec(&[ + true, true, true, true, true, true, true, true, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false + ])); + assert!(!act.none() && !act.all()); + // mixed + + act = BitVec::from_elem(31, false); + act.set(16, true); + act.set(17, true); + act.set(18, true); + act.set(19, true); + act.set(20, true); + act.set(21, true); + act.set(22, true); + act.set(23, true); + assert!(act.eq_vec(&[ + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, true, true, true, true, true, true, true, true, false, + false, false, false, false, false, false + ])); + assert!(!act.none() && !act.all()); + // mixed + + act = BitVec::from_elem(31, false); + act.set(24, true); + act.set(25, true); + act.set(26, true); + act.set(27, true); + act.set(28, true); + act.set(29, true); + act.set(30, true); + assert!(act.eq_vec(&[ + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true + ])); + assert!(!act.none() && !act.all()); + // mixed + + act = BitVec::from_elem(31, false); + act.set(3, true); + act.set(17, true); + act.set(30, true); + assert!(act.eq_vec(&[ + false, false, false, true, false, false, false, false, false, false, false, false, + false, false, false, false, false, true, false, false, false, false, false, false, + false, false, false, false, false, false, true + ])); + assert!(!act.none() && !act.all()); + } + + #[test] + fn test_32_elements() { + // all 0 + + let mut act = BitVec::from_elem(32, false); + assert!(act.eq_vec(&[ + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false + ])); + assert!(act.none() && !act.all()); + // all 1 + + act = BitVec::from_elem(32, true); + assert!(act.eq_vec(&[ + true, true, true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true + ])); + assert!(!act.none() && act.all()); + // mixed + + act = BitVec::from_elem(32, false); + act.set(0, true); + act.set(1, true); + act.set(2, true); + act.set(3, true); + act.set(4, true); + act.set(5, true); + act.set(6, true); + act.set(7, true); + assert!(act.eq_vec(&[ + true, true, true, true, true, true, true, true, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false + ])); + assert!(!act.none() && !act.all()); + // mixed + + act = BitVec::from_elem(32, false); + act.set(16, true); + act.set(17, true); + act.set(18, true); + act.set(19, true); + act.set(20, true); + act.set(21, true); + act.set(22, true); + act.set(23, true); + assert!(act.eq_vec(&[ + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, true, true, true, true, true, true, true, true, false, + false, false, false, false, false, false, false + ])); + assert!(!act.none() && !act.all()); + // mixed + + act = BitVec::from_elem(32, false); + act.set(24, true); + act.set(25, true); + act.set(26, true); + act.set(27, true); + act.set(28, true); + act.set(29, true); + act.set(30, true); + act.set(31, true); + assert!(act.eq_vec(&[ + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true + ])); + assert!(!act.none() && !act.all()); + // mixed + + act = BitVec::from_elem(32, false); + act.set(3, true); + act.set(17, true); + act.set(30, true); + act.set(31, true); + assert!(act.eq_vec(&[ + false, false, false, true, false, false, false, false, false, false, false, false, + false, false, false, false, false, true, false, false, false, false, false, false, + false, false, false, false, false, false, true, true + ])); + assert!(!act.none() && !act.all()); + } + + #[test] + fn test_33_elements() { + // all 0 + + let mut act = BitVec::from_elem(33, false); + assert!(act.eq_vec(&[ + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false + ])); + assert!(act.none() && !act.all()); + // all 1 + + act = BitVec::from_elem(33, true); + assert!(act.eq_vec(&[ + true, true, true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true + ])); + assert!(!act.none() && act.all()); + // mixed + + act = BitVec::from_elem(33, false); + act.set(0, true); + act.set(1, true); + act.set(2, true); + act.set(3, true); + act.set(4, true); + act.set(5, true); + act.set(6, true); + act.set(7, true); + assert!(act.eq_vec(&[ + true, true, true, true, true, true, true, true, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false + ])); + assert!(!act.none() && !act.all()); + // mixed + + act = BitVec::from_elem(33, false); + act.set(16, true); + act.set(17, true); + act.set(18, true); + act.set(19, true); + act.set(20, true); + act.set(21, true); + act.set(22, true); + act.set(23, true); + assert!(act.eq_vec(&[ + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, true, true, true, true, true, true, true, true, false, + false, false, false, false, false, false, false, false + ])); + assert!(!act.none() && !act.all()); + // mixed + + act = BitVec::from_elem(33, false); + act.set(24, true); + act.set(25, true); + act.set(26, true); + act.set(27, true); + act.set(28, true); + act.set(29, true); + act.set(30, true); + act.set(31, true); + assert!(act.eq_vec(&[ + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, false + ])); + assert!(!act.none() && !act.all()); + // mixed + + act = BitVec::from_elem(33, false); + act.set(3, true); + act.set(17, true); + act.set(30, true); + act.set(31, true); + act.set(32, true); + assert!(act.eq_vec(&[ + false, false, false, true, false, false, false, false, false, false, false, false, + false, false, false, false, false, true, false, false, false, false, false, false, + false, false, false, false, false, false, true, true, true + ])); + assert!(!act.none() && !act.all()); + } + + #[test] + fn test_equal_differing_sizes() { + let v0 = BitVec::from_elem(10, false); + let v1 = BitVec::from_elem(11, false); + assert_ne!(v0, v1); + } + + #[test] + fn test_equal_greatly_differing_sizes() { + let v0 = BitVec::from_elem(10, false); + let v1 = BitVec::from_elem(110, false); + assert_ne!(v0, v1); + } + + #[test] + fn test_equal_sneaky_small() { + let mut a = BitVec::from_elem(1, false); + a.set(0, true); + + let mut b = BitVec::from_elem(1, true); + b.set(0, true); + + assert_eq!(a, b); + } + + #[test] + fn test_equal_sneaky_big() { + let mut a = BitVec::from_elem(100, false); + for i in 0..100 { + a.set(i, true); + } + + let mut b = BitVec::from_elem(100, true); + for i in 0..100 { + b.set(i, true); + } + + assert_eq!(a, b); + } + + #[test] + fn test_from_bytes() { + let bit_vec = BitVec::from_bytes(&[0b10110110, 0b00000000, 0b11111111]); + let str = concat!("10110110", "00000000", "11111111"); + assert_eq!(format!("{}", bit_vec), str); + } + + #[test] + fn test_to_bytes() { + let mut bv = BitVec::from_elem(3, true); + bv.set(1, false); + assert_eq!(bv.to_bytes(), [0b10100000]); + + let mut bv = BitVec::from_elem(9, false); + bv.set(2, true); + bv.set(8, true); + assert_eq!(bv.to_bytes(), [0b00100000, 0b10000000]); + } + + #[test] + fn test_from_bools() { + let bools = [true, false, true, true]; + let bit_vec: BitVec = bools.iter().copied().collect(); + assert_eq!(format!("{}", bit_vec), "1011"); + } + + #[test] + fn test_to_bools() { + let bools = vec![false, false, true, false, false, true, true, false]; + assert_eq!( + BitVec::from_bytes(&[0b00100110]) + .iter() + .collect::>(), + bools + ); + } + + #[test] + fn test_bit_vec_iterator() { + let bools = vec![true, false, true, true]; + let bit_vec: BitVec = bools.iter().copied().collect(); + + assert_eq!(bit_vec.iter().collect::>(), bools); + + let long: Vec<_> = (0..10000).map(|i| i % 2 == 0).collect(); + let bit_vec: BitVec = long.iter().copied().collect(); + assert_eq!(bit_vec.iter().collect::>(), long) + } + + #[test] + fn test_small_difference() { + let mut b1 = BitVec::from_elem(3, false); + let mut b2 = BitVec::from_elem(3, false); + b1.set(0, true); + b1.set(1, true); + b2.set(1, true); + b2.set(2, true); + assert!(b1.difference(&b2)); + assert!(b1[0]); + assert!(!b1[1]); + assert!(!b1[2]); + } + + #[test] + fn test_big_difference() { + let mut b1 = BitVec::from_elem(100, false); + let mut b2 = BitVec::from_elem(100, false); + b1.set(0, true); + b1.set(40, true); + b2.set(40, true); + b2.set(80, true); + assert!(b1.difference(&b2)); + assert!(b1[0]); + assert!(!b1[40]); + assert!(!b1[80]); + } + + #[test] + fn test_small_xor() { + let mut a = BitVec::from_bytes(&[0b0011]); + let b = BitVec::from_bytes(&[0b0101]); + let c = BitVec::from_bytes(&[0b0110]); + assert!(a.xor(&b)); + assert_eq!(a, c); + } + + #[test] + fn test_small_xnor() { + let mut a = BitVec::from_bytes(&[0b0011]); + let b = BitVec::from_bytes(&[0b1111_0101]); + let c = BitVec::from_bytes(&[0b1001]); + assert!(a.xnor(&b)); + assert_eq!(a, c); + } + + #[test] + fn test_small_nand() { + let mut a = BitVec::from_bytes(&[0b1111_0011]); + let b = BitVec::from_bytes(&[0b1111_0101]); + let c = BitVec::from_bytes(&[0b1110]); + assert!(a.nand(&b)); + assert_eq!(a, c); + } + + #[test] + fn test_small_nor() { + let mut a = BitVec::from_bytes(&[0b0011]); + let b = BitVec::from_bytes(&[0b1111_0101]); + let c = BitVec::from_bytes(&[0b1000]); + assert!(a.nor(&b)); + assert_eq!(a, c); + } + + #[test] + fn test_big_xor() { + let mut a = BitVec::from_bytes(&[ + // 88 bits + 0, 0, 0b00010100, 0, 0, 0, 0, 0b00110100, 0, 0, 0, + ]); + let b = BitVec::from_bytes(&[ + // 88 bits + 0, 0, 0b00010100, 0, 0, 0, 0, 0, 0, 0, 0b00110100, + ]); + let c = BitVec::from_bytes(&[ + // 88 bits + 0, 0, 0, 0, 0, 0, 0, 0b00110100, 0, 0, 0b00110100, + ]); + assert!(a.xor(&b)); + assert_eq!(a, c); + } + + #[test] + fn test_big_xnor() { + let mut a = BitVec::from_bytes(&[ + // 88 bits + 0, 0, 0b00010100, 0, 0, 0, 0, 0b00110100, 0, 0, 0, + ]); + let b = BitVec::from_bytes(&[ + // 88 bits + 0, 0, 0b00010100, 0, 0, 0, 0, 0, 0, 0, 0b00110100, + ]); + let c = BitVec::from_bytes(&[ + // 88 bits + !0, + !0, + !0, + !0, + !0, + !0, + !0, + !0b00110100, + !0, + !0, + !0b00110100, + ]); + assert!(a.xnor(&b)); + assert_eq!(a, c); + } + + #[test] + fn test_small_clear() { + let mut b = BitVec::from_elem(14, true); + assert!(!b.none() && b.all()); + b.clear(); + assert!(b.none() && !b.all()); + } + + #[test] + fn test_big_clear() { + let mut b = BitVec::from_elem(140, true); + assert!(!b.none() && b.all()); + b.clear(); + assert!(b.none() && !b.all()); + } + + #[test] + fn test_bit_vec_lt() { + let mut a = BitVec::from_elem(5, false); + let mut b = BitVec::from_elem(5, false); + + assert!(a >= b && b >= a); + b.set(2, true); + assert!(a < b); + a.set(3, true); + assert!(a < b); + a.set(2, true); + assert!(a >= b && b < a); + b.set(0, true); + assert!(a < b); + } + + #[test] + fn test_ord() { + let mut a = BitVec::from_elem(5, false); + let mut b = BitVec::from_elem(5, false); + + assert!(a == b); + a.set(1, true); + assert!(a > b && a >= b); + assert!(b < a && b <= a); + b.set(1, true); + b.set(2, true); + assert!(b > a && b >= a); + assert!(a < b && a <= b); + } + + #[test] + fn test_small_bit_vec_tests() { + let v = BitVec::from_bytes(&[0]); + assert!(!v.all()); + assert!(!v.any()); + assert!(v.none()); + + let v = BitVec::from_bytes(&[0b00010100]); + assert!(!v.all()); + assert!(v.any()); + assert!(!v.none()); + + let v = BitVec::from_bytes(&[0xFF]); + assert!(v.all()); + assert!(v.any()); + assert!(!v.none()); + } + + #[test] + fn test_big_bit_vec_tests() { + let v = BitVec::from_bytes(&[ + // 88 bits + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ]); + assert!(!v.all()); + assert!(!v.any()); + assert!(v.none()); + + let v = BitVec::from_bytes(&[ + // 88 bits + 0, 0, 0b00010100, 0, 0, 0, 0, 0b00110100, 0, 0, 0, + ]); + assert!(!v.all()); + assert!(v.any()); + assert!(!v.none()); + + let v = BitVec::from_bytes(&[ + // 88 bits + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + ]); + assert!(v.all()); + assert!(v.any()); + assert!(!v.none()); + } + + #[test] + fn test_bit_vec_push_pop() { + let mut s = BitVec::from_elem(5 * U32_BITS - 2, false); + assert_eq!(s.len(), 5 * U32_BITS - 2); + assert!(!s[5 * U32_BITS - 3]); + s.push(true); + s.push(true); + assert!(s[5 * U32_BITS - 2]); + assert!(s[5 * U32_BITS - 1]); + // Here the internal vector will need to be extended + s.push(false); + assert!(!s[5 * U32_BITS]); + s.push(false); + assert!(!s[5 * U32_BITS + 1]); + assert_eq!(s.len(), 5 * U32_BITS + 2); + // Pop it all off + assert_eq!(s.pop(), Some(false)); + assert_eq!(s.pop(), Some(false)); + assert_eq!(s.pop(), Some(true)); + assert_eq!(s.pop(), Some(true)); + assert_eq!(s.len(), 5 * U32_BITS - 2); + } + + #[test] + fn test_bit_vec_truncate() { + let mut s = BitVec::from_elem(5 * U32_BITS, true); + + assert_eq!(s, BitVec::from_elem(5 * U32_BITS, true)); + assert_eq!(s.len(), 5 * U32_BITS); + s.truncate(4 * U32_BITS); + assert_eq!(s, BitVec::from_elem(4 * U32_BITS, true)); + assert_eq!(s.len(), 4 * U32_BITS); + // Truncating to a size > s.len() should be a noop + s.truncate(5 * U32_BITS); + assert_eq!(s, BitVec::from_elem(4 * U32_BITS, true)); + assert_eq!(s.len(), 4 * U32_BITS); + s.truncate(3 * U32_BITS - 10); + assert_eq!(s, BitVec::from_elem(3 * U32_BITS - 10, true)); + assert_eq!(s.len(), 3 * U32_BITS - 10); + s.truncate(0); + assert_eq!(s, BitVec::from_elem(0, true)); + assert_eq!(s.len(), 0); + } + + #[test] + fn test_bit_vec_reserve() { + let mut s = BitVec::from_elem(5 * U32_BITS, true); + // Check capacity + assert!(s.capacity() >= 5 * U32_BITS); + s.reserve(2 * U32_BITS); + assert!(s.capacity() >= 7 * U32_BITS); + s.reserve(7 * U32_BITS); + assert!(s.capacity() >= 12 * U32_BITS); + s.reserve_exact(7 * U32_BITS); + assert!(s.capacity() >= 12 * U32_BITS); + s.reserve(7 * U32_BITS + 1); + assert!(s.capacity() > 12 * U32_BITS); + // Check that length hasn't changed + assert_eq!(s.len(), 5 * U32_BITS); + s.push(true); + s.push(false); + s.push(true); + assert!(s[5 * U32_BITS - 1]); + assert!(s[5 * U32_BITS]); + assert!(!s[5 * U32_BITS + 1]); + assert!(s[5 * U32_BITS + 2]); + } + + #[test] + fn test_bit_vec_grow() { + let mut bit_vec = BitVec::from_bytes(&[0b10110110, 0b00000000, 0b10101010]); + bit_vec.grow(32, true); + assert_eq!( + bit_vec, + BitVec::from_bytes(&[0b10110110, 0b00000000, 0b10101010, 0xFF, 0xFF, 0xFF, 0xFF]) + ); + bit_vec.grow(64, false); + assert_eq!( + bit_vec, + BitVec::from_bytes(&[ + 0b10110110, 0b00000000, 0b10101010, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0 + ]) + ); + bit_vec.grow(16, true); + assert_eq!( + bit_vec, + BitVec::from_bytes(&[ + 0b10110110, 0b00000000, 0b10101010, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, + 0xFF, 0xFF + ]) + ); + } + + #[test] + fn test_bit_vec_extend() { + let mut bit_vec = BitVec::from_bytes(&[0b10110110, 0b00000000, 0b11111111]); + let ext = BitVec::from_bytes(&[0b01001001, 0b10010010, 0b10111101]); + bit_vec.extend(ext.iter()); + assert_eq!( + bit_vec, + BitVec::from_bytes(&[ + 0b10110110, 0b00000000, 0b11111111, 0b01001001, 0b10010010, 0b10111101 + ]) + ); + } + + #[test] + fn test_bit_vec_append() { + // Append to BitVec that holds a multiple of U32_BITS bits + let mut a = BitVec::from_bytes(&[0b10100000, 0b00010010, 0b10010010, 0b00110011]); + let mut b = BitVec::new(); + b.push(false); + b.push(true); + b.push(true); + + a.append(&mut b); + + assert_eq!(a.len(), 35); + assert_eq!(b.len(), 0); + assert!(b.capacity() >= 3); + + assert!(a.eq_vec(&[ + true, false, true, false, false, false, false, false, false, false, false, true, false, + false, true, false, true, false, false, true, false, false, true, false, false, false, + true, true, false, false, true, true, false, true, true + ])); + + // Append to arbitrary BitVec + let mut a = BitVec::new(); + a.push(true); + a.push(false); + + let mut b = + BitVec::from_bytes(&[0b10100000, 0b00010010, 0b10010010, 0b00110011, 0b10010101]); + + a.append(&mut b); + + assert_eq!(a.len(), 42); + assert_eq!(b.len(), 0); + assert!(b.capacity() >= 40); + + assert!(a.eq_vec(&[ + true, false, true, false, true, false, false, false, false, false, false, false, false, + true, false, false, true, false, true, false, false, true, false, false, true, false, + false, false, true, true, false, false, true, true, true, false, false, true, false, + true, false, true + ])); + + // Append to empty BitVec + let mut a = BitVec::new(); + let mut b = + BitVec::from_bytes(&[0b10100000, 0b00010010, 0b10010010, 0b00110011, 0b10010101]); + + a.append(&mut b); + + assert_eq!(a.len(), 40); + assert_eq!(b.len(), 0); + assert!(b.capacity() >= 40); + + assert!(a.eq_vec(&[ + true, false, true, false, false, false, false, false, false, false, false, true, false, + false, true, false, true, false, false, true, false, false, true, false, false, false, + true, true, false, false, true, true, true, false, false, true, false, true, false, + true + ])); + + // Append empty BitVec + let mut a = + BitVec::from_bytes(&[0b10100000, 0b00010010, 0b10010010, 0b00110011, 0b10010101]); + let mut b = BitVec::new(); + + a.append(&mut b); + + assert_eq!(a.len(), 40); + assert_eq!(b.len(), 0); + + assert!(a.eq_vec(&[ + true, false, true, false, false, false, false, false, false, false, false, true, false, + false, true, false, true, false, false, true, false, false, true, false, false, false, + true, true, false, false, true, true, true, false, false, true, false, true, false, + true + ])); + } + + #[test] + fn test_bit_vec_split_off() { + // Split at 0 + let mut a = BitVec::new(); + a.push(true); + a.push(false); + a.push(false); + a.push(true); + + let b = a.split_off(0); + + assert_eq!(a.len(), 0); + assert_eq!(b.len(), 4); + + assert!(b.eq_vec(&[true, false, false, true])); + + // Split at last bit + a.truncate(0); + a.push(true); + a.push(false); + a.push(false); + a.push(true); + + let b = a.split_off(4); + + assert_eq!(a.len(), 4); + assert_eq!(b.len(), 0); + + assert!(a.eq_vec(&[true, false, false, true])); + + // Split at block boundary + let mut a = + BitVec::from_bytes(&[0b10100000, 0b00010010, 0b10010010, 0b00110011, 0b11110011]); + + let b = a.split_off(32); + + assert_eq!(a.len(), 32); + assert_eq!(b.len(), 8); + + assert!(a.eq_vec(&[ + true, false, true, false, false, false, false, false, false, false, false, true, false, + false, true, false, true, false, false, true, false, false, true, false, false, false, + true, true, false, false, true, true + ])); + assert!(b.eq_vec(&[true, true, true, true, false, false, true, true])); + + // Don't split at block boundary + let mut a = BitVec::from_bytes(&[ + 0b10100000, 0b00010010, 0b10010010, 0b00110011, 0b01101011, 0b10101101, + ]); + + let b = a.split_off(13); + + assert_eq!(a.len(), 13); + assert_eq!(b.len(), 35); + + assert!(a.eq_vec(&[ + true, false, true, false, false, false, false, false, false, false, false, true, false + ])); + assert!(b.eq_vec(&[ + false, true, false, true, false, false, true, false, false, true, false, false, false, + true, true, false, false, true, true, false, true, true, false, true, false, true, + true, true, false, true, false, true, true, false, true + ])); + } + + #[test] + fn test_into_iter() { + let bools = [true, false, true, true]; + let bit_vec: BitVec = bools.iter().copied().collect(); + let mut iter = bit_vec.into_iter(); + assert_eq!(Some(true), iter.next()); + assert_eq!(Some(false), iter.next()); + assert_eq!(Some(true), iter.next()); + assert_eq!(Some(true), iter.next()); + assert_eq!(None, iter.next()); + assert_eq!(None, iter.next()); + + let bit_vec: BitVec = bools.iter().copied().collect(); + let mut iter = bit_vec.into_iter(); + assert_eq!(Some(true), iter.next_back()); + assert_eq!(Some(true), iter.next_back()); + assert_eq!(Some(false), iter.next_back()); + assert_eq!(Some(true), iter.next_back()); + assert_eq!(None, iter.next_back()); + assert_eq!(None, iter.next_back()); + + let bit_vec: BitVec = bools.iter().copied().collect(); + let mut iter = bit_vec.into_iter(); + assert_eq!(Some(true), iter.next_back()); + assert_eq!(Some(true), iter.next()); + assert_eq!(Some(false), iter.next()); + assert_eq!(Some(true), iter.next_back()); + assert_eq!(None, iter.next()); + assert_eq!(None, iter.next_back()); + } + + #[test] + fn iter() { + let b = BitVec::with_capacity(10); + let _a: Iter = b.iter(); + } + + #[cfg(feature = "serde")] + #[test] + fn test_serialization() { + let bit_vec: BitVec = BitVec::new(); + let serialized = serde_json::to_string(&bit_vec).unwrap(); + let unserialized: BitVec = serde_json::from_str(&serialized).unwrap(); + assert_eq!(bit_vec, unserialized); + + let bools = vec![true, false, true, true]; + let bit_vec: BitVec = bools.iter().map(|n| *n).collect(); + let serialized = serde_json::to_string(&bit_vec).unwrap(); + let unserialized = serde_json::from_str(&serialized).unwrap(); + assert_eq!(bit_vec, unserialized); + } + + #[cfg(feature = "miniserde")] + #[test] + fn test_miniserde_serialization() { + let bit_vec: BitVec = BitVec::new(); + let serialized = miniserde::json::to_string(&bit_vec); + let unserialized: BitVec = miniserde::json::from_str(&serialized[..]).unwrap(); + assert_eq!(bit_vec, unserialized); + + let bools = vec![true, false, true, true]; + let bit_vec: BitVec = bools.iter().map(|n| *n).collect(); + let serialized = miniserde::json::to_string(&bit_vec); + let unserialized = miniserde::json::from_str(&serialized[..]).unwrap(); + assert_eq!(bit_vec, unserialized); + } + + #[cfg(feature = "nanoserde")] + #[test] + fn test_nanoserde_json_serialization() { + use nanoserde::{DeJson, SerJson}; + + let bit_vec: BitVec = BitVec::new(); + let serialized = bit_vec.serialize_json(); + let unserialized: BitVec = BitVec::deserialize_json(&serialized[..]).unwrap(); + assert_eq!(bit_vec, unserialized); + + let bools = vec![true, false, true, true]; + let bit_vec: BitVec = bools.iter().map(|n| *n).collect(); + let serialized = bit_vec.serialize_json(); + let unserialized = BitVec::deserialize_json(&serialized[..]).unwrap(); + assert_eq!(bit_vec, unserialized); + } + + #[cfg(feature = "borsh")] + #[test] + fn test_borsh_serialization() { + let bit_vec: BitVec = BitVec::new(); + let serialized = borsh::to_vec(&bit_vec).unwrap(); + let unserialized: BitVec = borsh::from_slice(&serialized[..]).unwrap(); + assert_eq!(bit_vec, unserialized); + + let bools = vec![true, false, true, true]; + let bit_vec: BitVec = bools.iter().map(|n| *n).collect(); + let serialized = borsh::to_vec(&bit_vec).unwrap(); + let unserialized = borsh::from_slice(&serialized[..]).unwrap(); + assert_eq!(bit_vec, unserialized); + } + + #[test] + fn test_bit_vec_unaligned_small_append() { + let mut a = BitVec::from_elem(8, false); + a.set(7, true); + + let mut b = BitVec::from_elem(16, false); + b.set(14, true); + + let mut c = BitVec::from_elem(8, false); + c.set(6, true); + c.set(7, true); + + a.append(&mut b); + a.append(&mut c); + + assert_eq!(&[1, 0, 2, 3][..], &*a.to_bytes()); + } + + #[test] + fn test_bit_vec_unaligned_large_append() { + let mut a = BitVec::from_elem(48, false); + a.set(47, true); + + let mut b = BitVec::from_elem(48, false); + b.set(46, true); + + let mut c = BitVec::from_elem(48, false); + c.set(46, true); + c.set(47, true); + + a.append(&mut b); + a.append(&mut c); + + assert_eq!( + &[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03 + ][..], + &*a.to_bytes() + ); + } + + #[test] + fn test_bit_vec_append_aligned_to_unaligned() { + let mut a = BitVec::from_elem(2, true); + let mut b = BitVec::from_elem(32, false); + let mut c = BitVec::from_elem(8, true); + a.append(&mut b); + a.append(&mut c); + assert_eq!(&[0xc0, 0x00, 0x00, 0x00, 0x3f, 0xc0][..], &*a.to_bytes()); + } + + #[test] + fn test_count_ones() { + for i in 0..1000 { + let mut t = BitVec::from_elem(i, true); + let mut f = BitVec::from_elem(i, false); + assert_eq!(i as u64, t.count_ones()); + assert_eq!(0_u64, f.count_ones()); + if i > 20 { + t.set(10, false); + t.set(i - 10, false); + assert_eq!(i - 2, t.count_ones() as usize); + f.set(10, true); + f.set(i - 10, true); + assert_eq!(2, f.count_ones()); + } + } + } + + #[test] + fn test_count_zeros() { + for i in 0..1000 { + let mut tbits = BitVec::from_elem(i, true); + let mut fbits = BitVec::from_elem(i, false); + assert_eq!(i as u64, fbits.count_zeros()); + assert_eq!(0_u64, tbits.count_zeros()); + if i > 20 { + fbits.set(10, true); + fbits.set(i - 10, true); + assert_eq!(i - 2, fbits.count_zeros() as usize); + tbits.set(10, false); + tbits.set(i - 10, false); + assert_eq!(2, tbits.count_zeros()); + } + } + } + + #[test] + fn test_get_mut() { + let mut a = BitVec::from_elem(3, false); + let mut a_bit_1 = a.get_mut(1).unwrap(); + assert!(!*a_bit_1); + *a_bit_1 = true; + drop(a_bit_1); + assert!(a.eq_vec(&[false, true, false])); + } + #[test] + fn test_iter_mut() { + let mut a = BitVec::from_elem(8, false); + a.iter_mut().enumerate().for_each(|(index, mut bit)| { + *bit = index % 2 == 1; + }); + assert!(a.eq_vec(&[false, true, false, true, false, true, false, true])); + } + + #[test] + fn test_insert_at_zero() { + let mut v = BitVec::new(); + + v.insert(0, false); + v.insert(0, true); + v.insert(0, false); + v.insert(0, true); + v.insert(0, false); + v.insert(0, true); + + assert_eq!(v.len(), 6); + assert_eq!(v.storage().len(), 1); + assert!(v.eq_vec(&[true, false, true, false, true, false])); + } + + #[test] + fn test_insert_at_end() { + let mut v = BitVec::new(); + + v.insert(v.len(), true); + v.insert(v.len(), false); + v.insert(v.len(), true); + v.insert(v.len(), false); + v.insert(v.len(), true); + v.insert(v.len(), false); + + assert_eq!(v.storage().len(), 1); + assert_eq!(v.len(), 6); + assert!(v.eq_vec(&[true, false, true, false, true, false])); + } + + #[test] + fn test_insert_at_block_boundaries() { + let mut v = BitVec::from_elem(32, false); + + assert_eq!(v.storage().len(), 1); + + v.insert(31, true); + + assert_eq!(v.len(), 33); + + assert!(matches!(v.get(31), Some(true))); + assert!(v.eq_vec(&[ + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, true, false + ])); + + assert_eq!(v.storage().len(), 2); + } + + #[test] + fn test_insert_at_block_boundaries_1() { + let mut v = BitVec::from_elem(64, false); + + assert_eq!(v.storage().len(), 2); + + v.insert(63, true); + + assert_eq!(v.len(), 65); + + assert!(matches!(v.get(63), Some(true))); + assert!(v.eq_vec(&[ + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, false, false, + false, false, false, true, false + ])); + + assert_eq!(v.storage().len(), 3); + } +} diff --git a/tools/vendor/datatest-stable/.cargo-checksum.json b/tools/vendor/datatest-stable/.cargo-checksum.json new file mode 100644 index 0000000000..e6288bf17f --- /dev/null +++ b/tools/vendor/datatest-stable/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo/config.toml":"2e581595965bd5ea61fd6b08241ffbd1a9068efb5a53aa404aa09b8e15329243",".cargo_vcs_info.json":"f65f67b6c7783485974617cee6a0bc5f40f72c01f6893aa0c90f454447fa8fff",".github/codecov.yml":"5eefd693160c49b8ef5b305d1add6e530ee9ebfd4388a9f6e1348e0cefa88961",".github/renovate.json":"d4389ec97a1bea89b831eaa133a3968a19326dc70fef565653486676b3d00383",".github/workflows/ci.yml":"38e9a5dbb409c45ec67acb0c409a3158297024f09e2bd70ca72cf5c7a1e8f3a7",".github/workflows/coverage.yml":"4791142054b0e918abd8125eec9954b9bc4db7ff6ea6d350a41302dbab13871d",".github/workflows/docs.yml":"5be8fa7a37d30cad63aec5014f0f7d7f45a8b35fe11deb25501bf78e16a10d3b",".github/workflows/release.yml":"caed72ddcefd667d140a7f9cae6b3727654117fbe4cefd065de3338318a3e17a","CHANGELOG.md":"0042a52f29b0366e570abd541f47947ddbf2f0703a3de15821af86df605171a7","CONTRIBUTING.md":"f6e7fdf7c45013d46febf08e7dcefb33213946a18f1e4903c42dc01e0e4c4a9f","Cargo.lock":"31e232aa305f91c97209ce95d53204bb2b574145492783b3694640931bf63a63","Cargo.toml":"0ccdb8499a99c39c9ecba8da29cf8544f54810cb40d0c83ebad46e945aea09ef","Cargo.toml.orig":"59cdd7f8e5cc725ff9485eccae5d9d145786e240c764e09d6eda36303be13f67","Justfile":"3dd52515eecfa38094341211161a6dfc3d3b642863a3ee723bd69a19cabb6994","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"edd4db2ed884da8b6081b7aa5350473ec19470552dd9d5afc41e4b242387fc4d","release.toml":"6c38dc781f6e24af4695359cfcfb0b211d714c4cfb3d04dacc30c05ebde478bd","scripts/cargo-release-publish.sh":"9fe047e78c43a566ca1d5e29ad0ec02da95aea922f00c2b68ffa71e60b34eb48","scripts/fix-readmes.awk":"170edfadcb36e54051360893828e91ad80d7411c7193fdb1bec063ebc0c627d7","src/data_source.rs":"306d7e3ae33251eaa623c814d1528fb0f8e3b7d5408e74f1ca7b2b018fe86503","src/lib.rs":"4c498b5327c63222f757cd2823a22b6b5be56cc0a3f68d0a1d50bec546b59b09","src/macros.rs":"88b141ff13d63dae2d66af02d5458061fb196741d44afce4271e42474baa10c7","src/runner.rs":"678a1d2ac04ead809d1c1650e0870b65968d52dd17dcc7e5e1e90495544e5cb2","tests/compile-fail/empty-arg-list.rs":"89d1b6d79861ee2f13306a1489b7b4d30e0686f94b04d7307ba2e89319618339","tests/compile-fail/empty-arg-list.stderr":"99b42368aa98bd0f15a49877042acd5b81bf8d9bb72cb4bbfb018364971ec83d","tests/compile-fail/extra-args.rs":"faf6b7bb89e7df07f94cad4d11e5a6adc69352db9bd694a109400bdda13b6821","tests/compile-fail/extra-args.stderr":"296060a0af89716077b629a68781c1f2ed8435bf79fd68031424469f29579b11","tests/compile-fail/incorrect-arg-in-pattern.rs":"a1372e77b80eddc191322ae544bac668664b7c5193d48f3cf8fa3e91f520ad76","tests/compile-fail/incorrect-arg-in-pattern.stderr":"f3b24cf64401702991b2359f6ddd510b9be2ce768beb1fa76b6ebceb343c1218","tests/compile-fail/missing-root-comma.rs":"800b1adb6280806e0d31ce55933b5183711ddc3a1b07b35f82e2ce4f528ad26a","tests/compile-fail/missing-root-comma.stderr":"fa2c5d36e15c47d5599890e9628eaf81022d3e5f8da86c5875cc384dbf13b480","tests/compile-fail/missing-root-no-args.rs":"aeac7278a48d24f07071d96bf74fa902c8a0d4e8bc0b7207ed49df415f6e4f2e","tests/compile-fail/missing-root-no-args.stderr":"6383d61a6bf051c755eeff8c3845de2d5195ce3f14098f98b0438b017d7a6e39","tests/compile-fail/missing-root-no-comma.rs":"d81c02da35d51a06fb29b5f2f83a174289a8ae664f6148923b782a927d5904c3","tests/compile-fail/missing-root-no-comma.stderr":"c82b9c3ef6fe0627d55e5fcb5bd7cf0290b9afe24ce67828fe3d2bce3f6aa29a","tests/compile-fail/missing-test-comma.rs":"1af451d2560fe7763e87f15c18bf95d330b08e97281c7148d98658adbc8cf744","tests/compile-fail/missing-test-comma.stderr":"a40c38f50ab2fa27af2b080910e830aa9cad3e5dba2de7391a5aab7cc7482a22","tests/compile-fail/missing-test-no-comma.rs":"33add1e83efc1bba492cf46f97c7f7315a34eeffc74aa464efb62aec5611d7d4","tests/compile-fail/missing-test-no-comma.stderr":"7e1812ec35f8a2b78ae865e17dc3c05631b3d4e6577222d5b39923d758061c6d","tests/compile-fail/old-format.rs":"09b164d1eee1dba8a42146dcf31bbe198b31abffc31311a68bb0b2b3ccb02310","tests/compile-fail/old-format.stderr":"bb6422e0fa5977a675e411729a853715aa3b86f27d0fafcb571b2f5581df86e1","tests/compile-fail/pattern-not-ident.rs":"cf1b736fa7fd673631edd6887d26be1af04446097a7a74c0e75269d8ffacbb73","tests/compile-fail/pattern-not-ident.stderr":"1428a6febdb354c11fe6b908a857c5e2b2c5bf8cc26f247362b19ba4ef783d46","tests/compile-fail/root-not-ident.rs":"db73c8fcba8db200c70512c22f8a5f0f03fea6dd66cf0dea5493af8e54b3f9f6","tests/compile-fail/root-not-ident.stderr":"f649511a69e21121abdd9773bd5732a9902efa61890517b9a72c5a099d46fe0e","tests/compile-fail/root-out-of-order.rs":"c05d9f125f4578f39302fd7aa7d92f1ed43a5fb0d67eaec4cc5046a6c3228f14","tests/compile-fail/root-out-of-order.stderr":"e05d5f6f8be28e106b0a2b8bfd66a01b830d5006d73568677daac3719be2ed14","tests/compile-fail/test-not-ident.rs":"9dc1330401d3453ec10700268208c22669700aec8a10f98cb5649e6375df1552","tests/compile-fail/test-not-ident.stderr":"0e96e3c02c28cae5c2ce08aa236552c793ef2f33b4a1ada23f54021922c46c93","tests/compile-fail/test-out-of-order.rs":"c117f9380150a1d4f5d18e924acfc2f077d91a751f11689078d52509d8fd5ce3","tests/compile-fail/test-out-of-order.stderr":"5e6fe87e6bf462f46bc65cbb4f4bc8f2bf89de58c56ec2b98358c46b7c37a483","tests/example.rs":"c9633944102bfecb5a4b3f1a19207de23b21116fc36256491f067941b9df7262","tests/files/b.txt":"c87e2ca771bab6024c269b933389d2a92d4941c848c52f155b9b84e1f109fe35","tests/files/c.skip.txt":"cf2529f40c22bdeb63880edd3fe8e34753f495a40cd4caa0ad76d29fd880da45","tests/files/dir/a.txt":"14ff85b820e73d36d9d357f8eefb0d754c0d023654b5192ee55624f11f960580","tests/files/other.json":"6f022cde315a6298b4c4d74d7c7ddd019a3cea90955a9c738ede67090e1e8d5c","tests/integration.rs":"754952fe931fb894f6771f83fdfd2082c0d2ffaeded19491f5794b1b447d718d"},"package":"a867d7322eb69cf3a68a5426387a25b45cb3b9c5ee41023ee6cea92e2afadd82"} \ No newline at end of file diff --git a/tools/vendor/datatest-stable/.cargo/config.toml b/tools/vendor/datatest-stable/.cargo/config.toml new file mode 100644 index 0000000000..cf4aac7edb --- /dev/null +++ b/tools/vendor/datatest-stable/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +xfmt = "fmt -- --config imports_granularity=Crate --config group_imports=One" diff --git a/tools/vendor/datatest-stable/.cargo_vcs_info.json b/tools/vendor/datatest-stable/.cargo_vcs_info.json new file mode 100644 index 0000000000..d6aefced1a --- /dev/null +++ b/tools/vendor/datatest-stable/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "9234df379529ba2c25271973494a2e9b1f082d0d" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/tools/vendor/datatest-stable/.github/codecov.yml b/tools/vendor/datatest-stable/.github/codecov.yml new file mode 100644 index 0000000000..cd0b5a570b --- /dev/null +++ b/tools/vendor/datatest-stable/.github/codecov.yml @@ -0,0 +1,8 @@ +coverage: + status: + project: + default: + informational: true + patch: + default: + target: 0% diff --git a/tools/vendor/datatest-stable/.github/renovate.json b/tools/vendor/datatest-stable/.github/renovate.json new file mode 100644 index 0000000000..5513b8eecb --- /dev/null +++ b/tools/vendor/datatest-stable/.github/renovate.json @@ -0,0 +1,3 @@ +{ + "extends": ["github>nextest-rs/renovate"] +} diff --git a/tools/vendor/datatest-stable/.github/workflows/ci.yml b/tools/vendor/datatest-stable/.github/workflows/ci.yml new file mode 100644 index 0000000000..5df3f99f6c --- /dev/null +++ b/tools/vendor/datatest-stable/.github/workflows/ci.yml @@ -0,0 +1,98 @@ +on: + push: + branches: + - main + pull_request: + branches: + - main + +name: CI +env: + RUSTFLAGS: -D warnings + CARGO_TERM_COLOR: always + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + - uses: Swatinem/rust-cache@720f7e45ccee46c12a7b1d7bed2ab733be9be5a1 # v2 + - name: Lint (clippy) + run: cargo clippy --all-features --all-targets + - name: Lint (rustfmt) + run: cargo xfmt --check + - name: Install cargo-sync-rdme and just + uses: taiki-e/install-action@5ab30948b991e8d6aa5a6c1e33c6aea130c6de65 # v2 + with: + tool: cargo-sync-rdme,just + - name: Install nightly toolchain for cargo-sync-rdme + uses: dtolnay/rust-toolchain@nightly + - name: Generate readmes + run: just generate-readmes + - name: Check for differences + run: git diff --exit-code + + build-rustdoc: + name: Build documentation + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + fail-fast: false + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + - uses: Swatinem/rust-cache@720f7e45ccee46c12a7b1d7bed2ab733be9be5a1 # v2 + - name: Build rustdoc + run: cargo doc --all-features + + build: + name: Build and test + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: + - ubuntu-latest + - windows-latest + rust-version: ["1.72", stable] + fail-fast: false + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust-version }} + - uses: Swatinem/rust-cache@720f7e45ccee46c12a7b1d7bed2ab733be9be5a1 # v2 + with: + key: ${{ matrix.rust-version }} + - name: Install tools + uses: taiki-e/install-action@5ab30948b991e8d6aa5a6c1e33c6aea130c6de65 # v2 + with: + tool: cargo-hack,just,nextest + - name: Build + run: just powerset build + - name: Build with all targets + run: just powerset build --all-targets + - name: Run tests + run: just powerset nextest run + - name: Run tests with cargo test + run: just powerset test + + # Remove Cargo.lock to ensure that building with the latest versions works on stable. + - name: Remove Cargo.lock and rebuild on stable + if: matrix.rust-version == 'stable' + run: rm Cargo.lock && cargo build + - name: Build with all targets + if: matrix.rust-version == 'stable' + run: just powerset build --all-targets + - name: Run tests on stable + if: matrix.rust-version == 'stable' + run: just powerset nextest run + - name: Run tests with cargo test on stable + if: matrix.rust-version == 'stable' + run: just powerset test diff --git a/tools/vendor/datatest-stable/.github/workflows/coverage.yml b/tools/vendor/datatest-stable/.github/workflows/coverage.yml new file mode 100644 index 0000000000..6335b61ea1 --- /dev/null +++ b/tools/vendor/datatest-stable/.github/workflows/coverage.yml @@ -0,0 +1,41 @@ +on: + push: + branches: + - main + pull_request: + branches: + - main + +name: Test coverage + +jobs: + coverage: + name: Collect test coverage + runs-on: ubuntu-latest + # nightly rust might break from time to time + continue-on-error: true + env: + RUSTFLAGS: -D warnings + CARGO_TERM_COLOR: always + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + # Nightly Rust is used for cargo llvm-cov --doc below. + - uses: dtolnay/rust-toolchain@nightly + with: + components: llvm-tools-preview + - uses: Swatinem/rust-cache@720f7e45ccee46c12a7b1d7bed2ab733be9be5a1 # v2 + + - name: Install tools + uses: taiki-e/install-action@5ab30948b991e8d6aa5a6c1e33c6aea130c6de65 # v2 + with: + tool: cargo-llvm-cov,just,nextest + + - name: Collect coverage data + run: | + just coverage --lcov --output-path lcov.info + - name: Upload coverage data to codecov + uses: codecov/codecov-action@1e68e06f1dbfde0e4cefc87efeba9e4643565303 # v5 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + with: + files: lcov.info diff --git a/tools/vendor/datatest-stable/.github/workflows/docs.yml b/tools/vendor/datatest-stable/.github/workflows/docs.yml new file mode 100644 index 0000000000..378e23760d --- /dev/null +++ b/tools/vendor/datatest-stable/.github/workflows/docs.yml @@ -0,0 +1,32 @@ +on: + push: + branches: + - main + +name: Docs + +jobs: + docs: + name: Build and deploy documentation + concurrency: ci-${{ github.ref }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: dtolnay/rust-toolchain@stable + - uses: taiki-e/install-action@just + - name: Build rustdoc + run: just rustdoc + - name: Organize + run: | + rm -rf target/gh-pages + mkdir target/gh-pages + mv target/doc/_redirects target/gh-pages + mv target/doc target/gh-pages/rustdoc + - name: Publish + uses: cloudflare/pages-action@1 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + projectName: datatest-stable + directory: target/gh-pages + gitHubToken: ${{ secrets.GITHUB_TOKEN }} diff --git a/tools/vendor/datatest-stable/.github/workflows/release.yml b/tools/vendor/datatest-stable/.github/workflows/release.yml new file mode 100644 index 0000000000..a2a855b235 --- /dev/null +++ b/tools/vendor/datatest-stable/.github/workflows/release.yml @@ -0,0 +1,33 @@ +# adapted from https://github.com/taiki-e/cargo-hack/blob/main/.github/workflows/release.yml + +name: Publish releases to GitHub +on: + push: + tags: + - "*" + +jobs: + datatest-stable-release: + if: github.repository_owner == 'nextest-rs' && startsWith(github.ref_name, 'datatest-stable-') + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + with: + persist-credentials: false + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + - name: Install cargo release + uses: taiki-e/install-action@5ab30948b991e8d6aa5a6c1e33c6aea130c6de65 # v2 + with: + tool: cargo-release@0.25.0 + - uses: taiki-e/create-gh-release-action@26b80501670402f1999aff4b934e1574ef2d3705 # v1 + with: + prefix: datatest-stable + changelog: CHANGELOG.md + title: $prefix $version + branch: main + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - run: ./scripts/cargo-release-publish.sh + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/tools/vendor/datatest-stable/CHANGELOG.md b/tools/vendor/datatest-stable/CHANGELOG.md new file mode 100644 index 0000000000..4103a4e02b --- /dev/null +++ b/tools/vendor/datatest-stable/CHANGELOG.md @@ -0,0 +1,187 @@ +# Changelog + +## [0.3.3] - 2025-09-29 + +### Fixed + +Replaced obsolete `doc_auto_cfg` with `doc_cfg`, to fix Rust nightly builds with the `doc_cfg` flag enabled. + +## [0.3.2] - 2024-12-28 + +### Added + +- `pattern` is now optional in the `harness!` macro. If not specified, the default pattern is + `r".*"` (match all files). + +### Fixed + +- Restored the ability to use absolute paths as the `root` argument. + +## [0.3.1] - 2024-12-25 + +### Fixed + +Fixed documentation for `include_dir` invocations. They must typically be called +via `include_dir!("$CARGO_MANIFEST_DIR/path/to/data")`. + +## [0.3.0] - 2024-12-24 + +### Added + +- Support for embedding data into the test binary via an optional `include-dir` + feature. For more information and recommendations for when to use this, see [the + readme](https://crates.io/crates/datatest-stable). + +### Changed + +- The macro call format has changed to: + + ```rust + datatest_stable::harness! { + { test = fn_name, path = &include_dir!("path/to/data"), pattern = r"^.*$" }, + // ... + } + ``` + + This is both a nicer format for expressing multiple tests, and a signal to + indicate the other breaking changes in this release. + +- Regex patterns now match against the path relative to the *include directory*, rather + than paths with respect to the crate root. This change was made for uniformity with the + `include_dir` implementation. + +- On Windows, paths are now universally normalized to use forward slashes. This change + was made to ensure that test names and paths are consistent across platforms. + +## [0.2.10] - 2024-12-08 + +- Internal dependency updates: update `libtest-mimic` to 0.8.1, and `fancy-regex` to 0.14.0. +- Update MSRV to Rust 1.72. + +## [0.2.9] - 2024-04-25 + +### Added + +Previously, the test functions supported were `fn(&Path) -> Result<()>` and `fn(&Utf8Path) -> Result<()>`. This release adds additional supported functions: + +- `fn(&P, String) -> datatest_stable::Result<()>` where `P` is `Path` or `Utf8Path`. If the + extra `String` parameter is specified, the contents of the file will be loaded and passed in + as a string (erroring out if that failed). +- `fn(&P, Vec) -> datatest_stable::Result<()>` where `P` is `Path` or `Utf8Path`. If the + extra `Vec` parameter is specified, the contents of the file will be + loaded and passed in as a `Vec` (erroring out if that failed). + +## [0.2.8] - 2024-04-24 + +### Fixed + +- Fixed quadratic performance issue with nextest, where datatest-stable would iterate over the + entire list of files for each test. Thanks [@zaneduffield](https://github.com/zaneduffield) for + your first contribution! + +## [0.2.7] - 2024-04-21 + +### Changed + +- Switched to the `fancy-regex` crate, which allows for matching against regexes with + lookahead/behind and backreferences. Thanks [@webbdays](https://github.com/webbdays) for your + first contribution! + +- MSRV updated to Rust 1.66. + +## [0.2.6] - 2024-04-09 + +- Update to `libtest-mimic 0.7.2`, and use the upstream implementation of `ExitCode`. + +## [0.2.5] - 2024-04-08 + +- Exit main via `ExitCode` rather than `std::process::exit()`. This appears to fix coverage on + Windows. + +## [0.2.4] - 2024-04-08 + +This is a periodic maintenance release. + +- Update internal dependency versions, including libtest-mimic to 0.7.0. +- Update "docs (main)" link to the new location at [https://datatest-stable.nexte.st](https://datatest-stable.nexte.st). +- Update MSRV to Rust 1.65. + +## [0.2.3] - 2023-08-29 + +Updated README. + +## [0.2.2] - 2023-08-29 + +### Added + +- Restored compatibility with `fn(&Path) -> Result<()>`. The harness now can take either `fn(&Path) -> Result<()>` or `fn(&Utf8Path) -> Result<()>`. + +## [0.2.1] - 2023-08-29 + +### Changed + +- The test signature is now `fn(&`[`Utf8Path`]`)` rather than `fn(&Path)`. If necessary, a `Utf8Path` can be converted to a `&Path` with [`.as_ref()`] or [`.as_std_path()`]. +- Non-Unicode paths now consistently produce errors. Previously, the treatment of such paths was inconsistent -- they would either be skipped or produce errors. +- Internal dependency update: libtest-mimic updated to version 0.6.1. +- MSRV updated to Rust 1.60. + +[`Utf8Path`]: https://docs.rs/camino/latest/camino/struct.Utf8Path.html +[`.as_ref()`]: https://docs.rs/camino/latest/camino/struct.Utf8Path.html#impl-AsRef%3COsStr%3E-for-Utf8Path +[`.as_std_path()`]: https://docs.rs/camino/latest/camino/struct.Utf8Path.html#method.as_std_path + +## [0.2.0] - 2023-08-29 + +This version had a publishing issue. + +## [0.1.3] - 2022-08-15 + +### Changed + +- Errors are now displayed with the `Debug` implementation, which prints out the full error chain + with libraries like `anyhow` or `eyre`, rather than the `Display` implementation. Thanks + [Alex Badics] for your first contribution! +- MSRV updated to Rust 1.58. + +### Internal improvements + +- datatest-stable now uses libtest-mimic 0.5.2. Thanks [Lukas Kalbertodt] (maintainer of + libtest-mimic) for your first contribution! + +[Alex Badics]: https://github.com/badicsalex +[Lukas]: https://github.com/LukasKalbertodt + +## [0.1.2] - 2022-05-22 + +### Changed + +- New internal implementation, based on top of [libtest-mimic](https://github.com/LukasKalbertodt/libtest-mimic). +- Repository updated to [nextest-rs/datatest-stable](https://github.com/nextest-rs/datatest-stable). +- MSRV updated to Rust 1.56. + +There are no functional changes in this release. + +## [0.1.1] - 2021-04-16 + +### Added + +- Initial release with basic support for data-driven tests. + +(Version 0.1.0 was yanked because of a metadata issue.) + +[0.3.3]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.3.3 +[0.3.2]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.3.2 +[0.3.1]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.3.1 +[0.3.0]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.3.0 +[0.2.10]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.2.10 +[0.2.9]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.2.9 +[0.2.8]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.2.8 +[0.2.7]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.2.7 +[0.2.6]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.2.6 +[0.2.5]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.2.5 +[0.2.4]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.2.4 +[0.2.3]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.2.3 +[0.2.2]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.2.2 +[0.2.1]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.2.1 +[0.1.3]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.1.3 +[0.1.2]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.1.2 +[0.1.1]: https://github.com/nextest-rs/datatest-stable/releases/tag/datatest-stable-0.1.1 diff --git a/tools/vendor/datatest-stable/CONTRIBUTING.md b/tools/vendor/datatest-stable/CONTRIBUTING.md new file mode 100644 index 0000000000..fa9323cc67 --- /dev/null +++ b/tools/vendor/datatest-stable/CONTRIBUTING.md @@ -0,0 +1,50 @@ +## Prerequisites + +datatest-stable is designed to work with both `cargo test` and `cargo nextest`. In either case, +nextest is required for one of the tests. Install it via [the instructions here](https://get.nexte.st). + +## Pull Requests + +If you have a new feature in mind, please discuss the feature in an issue to ensure that your +contributions will be accepted. + +1. Fork the repo and create your branch from `main`. +2. If you've added code that should be tested, add tests. +3. If you've changed APIs, update the documentation. +4. Ensure the test suite passes with `cargo nextest run --all-features`. +5. Run `cargo xfmt` to automatically format your changes (CI will let you know if you missed this). + +## Logically Separate Commits + +As far as possible, please try and make commits +[atomic](https://en.wikipedia.org/wiki/Atomic_commit#Atomic_commit_convention) and logically +separate. We understand that GitHub's pull request model doesn't work well with "stacked diffs", so +if your changes are complex, then a single PR with a series of commits is preferred. + +## Bisectable History + +It is important that the project history is bisectable, so that when regressions are identified we +can easily use `git bisect` to be able to pinpoint the exact commit which introduced the regression. +We'll land your commits with: + +- "Rebase and merge" if your commits are atomic and each commit passes CI. +- "Squash and merge" if they are not. + +## Maintainers Editing Commits + +For efficiency reasons, maintainers may choose to edit your commits before landing them. The commits +will still be credited to you, and the edits will be limited to reasonable changes that are in the +spirit of the PR. (Think of the changes that the maintainers would have done anyway.) + +To make this easier, please check the box that allows maintainers to edit your branch: + +![Screenshot of GitHub new pull request page, showing "Allow edits and access to secrets by maintainers" checked](https://github.com/nextest-rs/quick-junit/assets/180618/9f4074fa-4f52-4735-af19-144464f0fb8d) + +If maintainers need to make changes and that box is checked, then your PR can be marked as "merged" +in the web UI. Otherwise, it will be marked as "closed". + +## License + +By contributing to `datatest-stable`, you agree that your contributions will be dual-licensed under +the terms of the [`LICENSE-MIT`](LICENSE-MIT) and [`LICENSE-APACHE`](LICENSE-APACHE) files in the +root directory of this source tree. diff --git a/tools/vendor/datatest-stable/Cargo.lock b/tools/vendor/datatest-stable/Cargo.lock new file mode 100644 index 0000000000..00d0b8b884 --- /dev/null +++ b/tools/vendor/datatest-stable/Cargo.lock @@ -0,0 +1,765 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon 1.0.2", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon 3.0.6", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" + +[[package]] +name = "camino-tempfile" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb905055fa81e4d427f919b2cd0d76a998267de7d225ea767a1894743a5263c2" +dependencies = [ + "camino", + "tempfile", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb690e81c7840c0d7aade59f242ea3b41b9bc27bcd5997890e7702ae4b32e487" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ed2e96bc16d8d740f6f48d663eddf4b8a0983e79210fd55479b7bcd0a69860e" +dependencies = [ + "anstream 0.3.2", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "datatest-stable" +version = "0.3.3" +dependencies = [ + "camino", + "camino-tempfile", + "fancy-regex", + "fs_extra", + "include_dir", + "libtest-mimic", + "trybuild", + "walkdir", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "escape8259" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4f4911e3666fcd7826997b4745c8224295a6f3072f1418c3067b97a67557ee" +dependencies = [ + "rustversion", +] + +[[package]] +name = "fancy-regex" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298" +dependencies = [ + "bit-set", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "fastrand" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "include_dir" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "indexmap" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libtest-mimic" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5297962ef19edda4ce33aaa484386e0a5b3d7f2f4e037cbeee00503ef6b29d33" +dependencies = [ + "anstream 0.6.18", + "anstyle", + "clap", + "escape8259", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustversion" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.216" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.134" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" +dependencies = [ + "serde", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-triple" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078" + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "toml" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_parser" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" + +[[package]] +name = "trybuild" +version = "1.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ded9fdb81f30a5708920310bfcd9ea7482ff9cba5f54601f7a19a877d5c2392" +dependencies = [ + "glob", + "serde", + "serde_derive", + "serde_json", + "target-triple", + "termcolor", + "toml", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" diff --git a/tools/vendor/datatest-stable/Cargo.toml b/tools/vendor/datatest-stable/Cargo.toml new file mode 100644 index 0000000000..d625d3611e --- /dev/null +++ b/tools/vendor/datatest-stable/Cargo.toml @@ -0,0 +1,94 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.72" +name = "datatest-stable" +version = "0.3.3" +build = false +publish = true +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Data-driven tests that work on stable Rust" +readme = "README.md" +keywords = [ + "datatest", + "data-driven-tests", + "test-harness", +] +categories = ["development-tools::testing"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/nextest-rs/datatest-stable" + +[package.metadata.cargo-sync-rdme.badge.badges] +maintenance = true +license = true +crates-io = true +docs-rs = true +rust-version = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg=doc_cfg"] + +[badges.maintenance] +status = "passively-maintained" + +[features] +include-dir = ["dep:include_dir"] + +[lib] +name = "datatest_stable" +path = "src/lib.rs" + +[[test]] +name = "example" +path = "tests/example.rs" +harness = false + +[[test]] +name = "integration" +path = "tests/integration.rs" +harness = true + +[dependencies.camino] +version = "1.1.9" + +[dependencies.fancy-regex] +version = "0.14.0" + +[dependencies.include_dir] +version = "0.7.4" +optional = true + +[dependencies.libtest-mimic] +version = "0.8.1" + +[dependencies.walkdir] +version = "2.5.0" + +[dev-dependencies.trybuild] +version = "1.0.111" + +[target."cfg(unix)".dev-dependencies.camino-tempfile] +version = "1.1.1" + +[target."cfg(unix)".dev-dependencies.fs_extra] +version = "1.3.0" + +[lints.rust.unexpected_cfgs] +level = "warn" +priority = 0 +check-cfg = ["cfg(doc_cfg)"] diff --git a/tools/vendor/datatest-stable/Cargo.toml.orig b/tools/vendor/datatest-stable/Cargo.toml.orig new file mode 100644 index 0000000000..8204ffa71f --- /dev/null +++ b/tools/vendor/datatest-stable/Cargo.toml.orig @@ -0,0 +1,54 @@ +[package] +name = "datatest-stable" +version = "0.3.3" +description = "Data-driven tests that work on stable Rust" +repository = "https://github.com/nextest-rs/datatest-stable" +license = "MIT OR Apache-2.0" +publish = true +readme = "README.md" +edition = "2021" +categories = ["development-tools::testing"] +keywords = ["datatest", "data-driven-tests", "test-harness"] +rust-version = "1.72" + +[badges] +maintenance = { status = "passively-maintained" } + +[package.metadata.cargo-sync-rdme.badge.badges] +maintenance = true +license = true +crates-io = true +docs-rs = true +rust-version = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg=doc_cfg"] + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(doc_cfg)'] } + +[dependencies] +camino = "1.1.9" +fancy-regex = "0.14.0" +include_dir = { version = "0.7.4", optional = true } +libtest-mimic = "0.8.1" +walkdir = "2.5.0" + +[dev-dependencies] +trybuild = "1.0.111" + +[target.'cfg(unix)'.dev-dependencies] +camino-tempfile = "1.1.1" +fs_extra = "1.3.0" + +[[test]] +name = "example" +harness = false + +[[test]] +name = "integration" +harness = true + +[features] +include-dir = ["dep:include_dir"] diff --git a/tools/vendor/datatest-stable/Justfile b/tools/vendor/datatest-stable/Justfile new file mode 100644 index 0000000000..3f66e58b4a --- /dev/null +++ b/tools/vendor/datatest-stable/Justfile @@ -0,0 +1,27 @@ +# Print a help message. +help: + just --list + + +# Run `cargo hack --feature-powerset` with the given arguments. +powerset *args: + cargo hack --feature-powerset {{args}} + +# Build docs for crates and direct dependencies +rustdoc: + @cargo tree --depth 1 -e normal --prefix none --workspace \ + | gawk '{ gsub(" v", "@", $0); printf("%s\n", $1); }' \ + | xargs printf -- '-p %s\n' \ + | RUSTC_BOOTSTRAP=1 RUSTDOCFLAGS='--cfg=doc_cfg' xargs cargo doc --no-deps --lib --all-features + + echo "/ /rustdoc/datatest_stable/ 301" > target/doc/_redirects + +# Generate README.md files using `cargo-sync-rdme`. +generate-readmes: + cargo sync-rdme --toolchain nightly --all-features + +# Collect coverage, pass in `--html` to get an HTML report +coverage *args: + cargo +nightly llvm-cov --no-report nextest --all-features + cargo +nightly llvm-cov --no-report --doc --all-features + cargo +nightly llvm-cov report --doctests {{args}} diff --git a/tools/vendor/datatest-stable/LICENSE-APACHE b/tools/vendor/datatest-stable/LICENSE-APACHE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/tools/vendor/datatest-stable/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/tools/vendor/datatest-stable/LICENSE-MIT b/tools/vendor/datatest-stable/LICENSE-MIT new file mode 100644 index 0000000000..31aa79387f --- /dev/null +++ b/tools/vendor/datatest-stable/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/tools/vendor/datatest-stable/README.md b/tools/vendor/datatest-stable/README.md new file mode 100644 index 0000000000..5eae3136e0 --- /dev/null +++ b/tools/vendor/datatest-stable/README.md @@ -0,0 +1,249 @@ + +# datatest-stable + + +[![Maintenance: passively-maintained](https://img.shields.io/badge/maintenance-passively--maintained-yellowgreen.svg?)](https://doc.rust-lang.org/cargo/reference/manifest.html#the-badges-section) +![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/datatest-stable.svg?) +[![crates.io](https://img.shields.io/crates/v/datatest-stable.svg?logo=rust)](https://crates.io/crates/datatest-stable) +[![docs.rs](https://img.shields.io/docsrs/datatest-stable.svg?logo=docs.rs)](https://docs.rs/datatest-stable) +[![Rust: ^1.72.0](https://img.shields.io/badge/rust-^1.72.0-93450a.svg?logo=rust)](https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field) + + +`datatest-stable` is a test harness intended to write *file-driven* or *data-driven* tests, +where individual test case fixtures are specified as files and not as code. + +Given: + +* a test `my_test` that accepts a path, and optionally the contents as input +* a directory to look for files (test fixtures) in +* a pattern to match files on + +`datatest-stable` will call the `my_test` function once per matching file in +the directory. Directory traversals are recursive. + +`datatest-stable` works with [cargo nextest](https://nexte.st/), and is part +of the [nextest-rs organization](https://github.com/nextest-rs/) on GitHub. +With nextest, each test case is represented as a separate test, and is run +as a separate process in parallel. + +## Usage + +1. Configure the test target by setting `harness = false` in `Cargo.toml`: + +````toml +[[test]] +name = "" +harness = false +```` + +2. Call the `datatest_stable::harness!` macro as: + +````rust,ignore +datatest_stable::harness! { + { test = my_test, root = "path/to/fixtures", pattern = r".*" }, +} +```` + +* `test` - The test function to be executed on each matching input. This function can be one + of: + + * `fn(&Path) -> datatest_stable::Result<()>` + * `fn(&Utf8Path) -> datatest_stable::Result<()>` ([`Utf8Path`](https://docs.rs/camino/1.1.9/camino/struct.Utf8Path.html) is part of the + [`camino`](https://docs.rs/camino/1.1.9/camino/index.html) library, and is re-exported here for convenience.) + * `fn(&P, String) -> datatest_stable::Result<()>` where `P` is `Path` or `Utf8Path`. If the + extra `String` parameter is specified, the contents of the file will be loaded and passed in + as a string (erroring out if that failed). + * `fn(&P, Vec) -> datatest_stable::Result<()>` where `P` is `Path` or `Utf8Path`. If the + extra `Vec` parameter is specified, the contents of the file will be loaded and passed + in as a `Vec` (erroring out if that failed). +* `root` - The path to the root directory where the input files (fixtures) + live. Relative paths are resolved relative to the crate root (the directory where the crate’s + `Cargo.toml` is located). + + `root` is an arbitrary expression that implements + [`Display`](https://doc.rust-lang.org/nightly/core/fmt/trait.Display.html), such as `&str`, or a function call that + returns a [`Utf8PathBuf`](https://docs.rs/camino/1.1.9/camino/struct.Utf8PathBuf.html). + +* `pattern` - a regex used to match against and select each file to be tested. Extended regexes + with lookaround and backtracking are supported via the [`fancy_regex`](https://docs.rs/fancy-regex/0.14.0/fancy_regex/index.html) crate. + + `pattern` is an arbitrary expression that implements [`Display`](https://doc.rust-lang.org/nightly/core/fmt/trait.Display.html), such as + `&str`, or a function call that returns a `String`. + + `pattern` is optional, and defaults to `r".*"` (match all files). + +The three parameters can be repeated if you have multiple sets of data-driven tests to be run: + +````rust,ignore +datatest_stable::harness! { + { test = testfn1, root = root1, pattern = pattern1 }, + { test = testfn2, root = root2 }, +} +```` + +Trailing commas are optional. + +### Relative and absolute paths + +The `pattern` argument is tested against the **relative** path of each file, +**excluding** the `root` prefix – not the absolute path. + +The `Path` and `Utf8Path` passed into the test functions are created by +joining `root` to the relative path of the file. + +* If `root` is **relative**, the paths passed in are relative to the crate root. +* If `root` is **absolute**, the paths passed in are absolute. + +For uniformity, all relative paths use `/` as the path separator, +including on Windows, and all absolute paths use the platform’s native +separator throughout. + +### Examples + +This is an example test. Use it with `harness = false`. + +````rust +use datatest_stable::Utf8Path; +use std::path::Path; + +fn my_test(path: &Path) -> datatest_stable::Result<()> { + // ... write test here + + Ok(()) +} + +fn my_test_utf8(path: &Utf8Path, contents: String) -> datatest_stable::Result<()> { + // ... write test here + + Ok(()) +} + +datatest_stable::harness! { + { test = my_test, root = "path/to/fixtures" }, + { + test = my_test_utf8, + root = "path/to/fixtures", + pattern = r"^.*\.txt$", + }, +} +```` + +If `path/to/fixtures` contains a file `foo/bar.txt`, then: + +* The pattern `r"^.*/*"` will match `foo/bar.txt`. +* `my_test` and `my_test_utf8` will be called with `"path/to/fixtures/foo/bar.txt"`. + +### Embedding directories at compile time + +With the `include-dir` feature enabled, you can use the +[`include_dir`](https://docs.rs/include_dir) crate’s [`include_dir!`](https://docs.rs/include_dir_macros/0.7.4/include_dir_macros/macro.include_dir.html) macro. +This allows you to embed directories into the binary at compile time. + +This is generally not recommended for rapidly-changing test data, since each +change will force a rebuild. But it can be useful for relatively-unchanging +data suites distributed separately, e.g. on crates.io. + +With the `include-dir` feature enabled, you can use: + +````rust +// The `include_dir!` macro is re-exported for convenience. +use datatest_stable::include_dir; +use std::path::Path; + +fn my_test(path: &Path, contents: Vec) -> datatest_stable::Result<()> { + // ... write test here + Ok(()) +} + +datatest_stable::harness! { + { test = my_test, root = include_dir!("tests/files"), pattern = r"^.*\.json$" }, +} +```` + +You can also use directories published as `static` items in upstream crates: + +````rust +use datatest_stable::{include_dir, Utf8Path}; + +// In the upstream crate: +pub static FIXTURES: include_dir::Dir<'static> = + include_dir!("$CARGO_MANIFEST_DIR/tests/files"); + +// In your test: +fn my_test(path: &Utf8Path, contents: String) -> datatest_stable::Result<()> { + // ... write test here + Ok(()) +} + +datatest_stable::harness! { + { test = my_test, root = &FIXTURES }, +} +```` + +In this case, the passed-in `Path` and `Utf8Path` are always **relative** to +the root of the included directory. Like elsewhere in `datatest-stable`, +these relative paths always use forward slashes as separators, including on +Windows. + +Because the files don’t exist on disk, the test functions must accept their +contents as either a `String` or a `Vec`. If the argument is not +provided, the harness will panic at runtime. + +### Conditionally embedding directories + +It is also possible to conditionally include directories at compile time via +a feature flag. For example, you might have an internal-only `testing` +feature that you turn on locally, but users don’t on crates.io. In that +case, you can use: + +````rust +use datatest_stable::Utf8Path; + +// In the library itself: +pub mod fixtures { + #[cfg(feature = "testing")] + pub static FIXTURES: &str = "tests/files"; + + #[cfg(not(feature = "testing"))] + pub static FIXTURES: include_dir::Dir<'static> = + include_dir::include_dir!("$CARGO_MANIFEST_DIR/tests/files"); +} + +// In the test: +fn my_test(path: &Utf8Path, contents: String) -> datatest_stable::Result<()> { + // ... write test here + Ok(()) +} + +datatest_stable::harness! { + { test = my_test, root = &fixtures::FIXTURES, pattern = r"^inputs/.*$" }, +} +```` + +In this case, note that `path` will be relative to the **crate directory** +(e.g. `tests/files/foo/bar.txt`) if `FIXTURES` is a string, and relative to +the **include directory** (e.g. `foo/bar.txt`) if `FIXTURES` is a +[`Dir`](https://docs.rs/include_dir/0.7.4/include_dir/dir/struct.Dir.html). Your test should be prepared to handle either +case. + +## Features + +* `include-dir`: Enables the `include_dir!` macro, which allows embedding + directories at compile time. This feature is disabled by default. + +## Minimum supported Rust version (MSRV) + +The minimum supported Rust version is **Rust 1.72**. MSRV bumps may be accompanied by a minor +version update; at any time, Rust versions from at least the last 6 months are supported. + +## See also + +* [`datatest`](https://crates.io/crates/datatest): the original inspiration for this crate, with + more features but targeting nightly Rust. +* [Data-driven testing](https://en.wikipedia.org/wiki/Data-driven_testing) + + +## License + +This project is available under the terms of either the [Apache 2.0 license](LICENSE-APACHE) or the [MIT +license](LICENSE-MIT). diff --git a/tools/vendor/datatest-stable/release.toml b/tools/vendor/datatest-stable/release.toml new file mode 100644 index 0000000000..05b867a749 --- /dev/null +++ b/tools/vendor/datatest-stable/release.toml @@ -0,0 +1,8 @@ +sign-tag = true +# Required for templates below to work +consolidate-commits = false +pre-release-commit-message = "[{{crate_name}}] version {{version}}" +tag-message = "[{{crate_name}}] version {{version}}" +tag-name = "datatest-stable-{{version}}" +publish = false +dependent-version = "upgrade" diff --git a/tools/vendor/datatest-stable/scripts/cargo-release-publish.sh b/tools/vendor/datatest-stable/scripts/cargo-release-publish.sh new file mode 100755 index 0000000000..9ccefa91f4 --- /dev/null +++ b/tools/vendor/datatest-stable/scripts/cargo-release-publish.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# Use cargo-release to publish crates to crates.io. + +set -xe -o pipefail + +# cargo-release requires a release off a branch (maybe it shouldn't?) +# Check out this branch, creating it if it doesn't exist. +git checkout -B to-release + +# --execute: actually does the release +# --no-confirm: don't ask for confirmation, since this is a non-interactive script +cargo release publish --publish --execute --no-confirm --workspace "$@" + +git checkout - +git branch -D to-release diff --git a/tools/vendor/datatest-stable/scripts/fix-readmes.awk b/tools/vendor/datatest-stable/scripts/fix-readmes.awk new file mode 100644 index 0000000000..ad87b4b7fc --- /dev/null +++ b/tools/vendor/datatest-stable/scripts/fix-readmes.awk @@ -0,0 +1,27 @@ +# Fix up readmes: +# * Replace ## with # in code blocks. +# * Remove [] without a following () from output. + +BEGIN { + true = 1 + false = 0 + in_block = false +} + +{ + if (!in_block && $0 ~ /^```/) { + in_block = true + } else if (in_block && $0 ~ /^```$/) { + in_block = false + } + + if (in_block) { + sub(/## /, "# ") + print $0 + } else { + # Strip [] without a () that immediately follows them from + # the output. + subbed = gensub(/\[([^\[]+)]([^\(]|$)/, "\\1\\2", "g") + print subbed + } +} diff --git a/tools/vendor/datatest-stable/src/data_source.rs b/tools/vendor/datatest-stable/src/data_source.rs new file mode 100644 index 0000000000..22f9b3b0f9 --- /dev/null +++ b/tools/vendor/datatest-stable/src/data_source.rs @@ -0,0 +1,430 @@ +// Copyright (c) The datatest-stable Contributors +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use camino::{Utf8Component, Utf8Path, Utf8PathBuf}; + +#[derive(Debug)] +#[doc(hidden)] +pub enum DataSource { + // The path has had normalize_slashes applied to it. + Directory(Utf8PathBuf), + #[cfg(feature = "include-dir")] + IncludeDir(std::borrow::Cow<'static, include_dir::Dir<'static>>), +} + +impl DataSource { + /// Iterates over all files in the data source. + /// + /// This returns entries that have just been discovered, so they're expected + /// to exist. + pub(crate) fn walk_files(&self) -> Box> + '_> { + match self { + DataSource::Directory(path) => Box::new(iter_directory(path)), + #[cfg(feature = "include-dir")] + DataSource::IncludeDir(dir) => Box::new(iter_include_dir(dir)), + } + } + + /// Finds a test path from the filter provided. + /// + /// The path might or might not exist -- the caller should call `.exists()` + /// to ensure it does. + /// + /// Used for `--exact` matches. + pub(crate) fn derive_exact(&self, filter: &str, test_name: &str) -> Option { + // include_dir 0.7.4 returns paths with forward slashes, including on + // Windows. But that isn't part of the stable API it seems, so we call + // `rel_path_to_forward_slashes` anyway. + let rel_path = rel_path_to_forward_slashes( + filter.strip_prefix(test_name)?.strip_prefix("::")?.as_ref(), + ); + match self { + DataSource::Directory(path) => Some(TestEntry { + source: TestSource::Path(normalize_slashes(&path.join(&rel_path))), + rel_path, + }), + #[cfg(feature = "include-dir")] + DataSource::IncludeDir(dir) => { + let file = dir.get_file(&rel_path)?; + Some(TestEntry { + source: TestSource::IncludeDir(file), + rel_path, + }) + } + } + } + + /// Returns true if data is not available on disk and must be provided from + /// an in-memory buffer. + pub(crate) fn is_in_memory(&self) -> bool { + match self { + DataSource::Directory(_) => false, + #[cfg(feature = "include-dir")] + DataSource::IncludeDir(_) => true, + } + } + + pub(crate) fn display(&self) -> String { + match self { + DataSource::Directory(path) => format!("directory: `{path}`"), + #[cfg(feature = "include-dir")] + DataSource::IncludeDir(_) => "included directory".to_string(), + } + } +} + +fn iter_directory(root: &Utf8Path) -> impl Iterator> + '_ { + walkdir::WalkDir::new(root) + .into_iter() + .filter(|res| { + // Continue to bubble up all errors to the parent. + res.as_ref().map_or(true, |entry| { + entry.file_type().is_file() + && entry + .file_name() + .to_str() + .is_some_and(|s| !s.starts_with('.')) // Skip hidden files + }) + }) + .map(move |res| match res { + Ok(entry) => { + let path = Utf8PathBuf::try_from(entry.into_path()) + .map_err(|error| error.into_io_error())?; + Ok(TestEntry::from_full_path(root, path)) + } + Err(error) => Err(error.into()), + }) +} + +#[cfg(feature = "include-dir")] +fn iter_include_dir<'a>( + dir: &'a include_dir::Dir<'static>, +) -> impl Iterator> + 'a { + // Need to maintain a stack to do a depth-first traversal. + struct IncludeDirIter<'a> { + stack: Vec<&'a include_dir::DirEntry<'a>>, + } + + impl<'a> Iterator for IncludeDirIter<'a> { + type Item = &'a include_dir::File<'a>; + + fn next(&mut self) -> Option { + while let Some(entry) = self.stack.pop() { + match entry { + include_dir::DirEntry::File(file) => { + return Some(file); + } + include_dir::DirEntry::Dir(dir) => { + self.stack.extend(dir.entries()); + } + } + } + + None + } + } + + IncludeDirIter { + stack: dir.entries().iter().collect(), + } + .map(|file| { + // include_dir 0.7.4 returns paths with forward slashes, including on + // Windows. But that isn't part of the stable API it seems, so we call + // `rel_path_to_forward_slashes` anyway. + let rel_path = match file.path().try_into() { + Ok(path) => rel_path_to_forward_slashes(path), + Err(error) => { + return Err(error.into_io_error()); + } + }; + Ok(TestEntry { + source: TestSource::IncludeDir(file), + rel_path, + }) + }) +} + +#[derive(Debug)] +pub(crate) struct TestEntry { + source: TestSource, + rel_path: Utf8PathBuf, +} + +impl TestEntry { + pub(crate) fn from_full_path(root: &Utf8Path, path: Utf8PathBuf) -> Self { + let path = normalize_slashes(&path); + let rel_path = + rel_path_to_forward_slashes(path.strip_prefix(root).unwrap_or_else(|_| { + panic!("failed to strip root '{}' from path '{}'", root, path) + })); + Self { + source: TestSource::Path(path), + rel_path, + } + } + + pub(crate) fn derive_test_name(&self, test_name: &str) -> String { + format!("{}::{}", test_name, self.rel_path) + } + + pub(crate) fn read(&self) -> crate::Result> { + match &self.source { + TestSource::Path(path) => std::fs::read(path) + .map_err(|err| format!("error reading file '{path}': {err}").into()), + #[cfg(feature = "include-dir")] + TestSource::IncludeDir(file) => Ok(file.contents().to_vec()), + } + } + + pub(crate) fn read_as_string(&self) -> crate::Result { + match &self.source { + TestSource::Path(path) => std::fs::read_to_string(path) + .map_err(|err| format!("error reading file '{path}' as UTF-8: {err}").into()), + #[cfg(feature = "include-dir")] + TestSource::IncludeDir(file) => { + let contents = file.contents().to_vec(); + String::from_utf8(contents).map_err(|err| { + format!( + "error reading included file at '{}' as UTF-8: {err}", + self.rel_path + ) + .into() + }) + } + } + } + + /// Returns the path to match regexes against. + /// + /// This is always the relative path to the file from the include directory. + pub(crate) fn match_path(&self) -> &Utf8Path { + &self.rel_path + } + + /// Returns the path to the test data, as passed into the test function. + /// + /// For directories on disk, this is the relative path after being joined + /// with the include directory. For `include_dir` sources, this is the path + /// relative to the root of the include directory. + pub(crate) fn test_path(&self) -> &Utf8Path { + match &self.source { + TestSource::Path(path) => path, + #[cfg(feature = "include-dir")] + TestSource::IncludeDir(_) => { + // The UTF-8-encoded version of file.path is stored in `rel_path`. + &self.rel_path + } + } + } + + /// Returns the path to the file on disk. + /// + /// If the data source is an `include_dir`, this will return `None`. + pub(crate) fn disk_path(&self) -> Option<&Utf8Path> { + match &self.source { + TestSource::Path(path) => Some(path), + #[cfg(feature = "include-dir")] + TestSource::IncludeDir(_) => None, + } + } + + /// Returns true if the path exists. + pub(crate) fn exists(&self) -> bool { + match &self.source { + TestSource::Path(path) => path.exists(), + #[cfg(feature = "include-dir")] + TestSource::IncludeDir(_) => { + // include_dir files are guaranteed to exist. + true + } + } + } +} + +#[cfg(windows)] +#[track_caller] +fn normalize_slashes(path: &Utf8Path) -> Utf8PathBuf { + if is_truly_relative(path) { + rel_path_to_forward_slashes(path) + } else { + path.as_str().replace('/', "\\").into() + } +} + +#[cfg(windows)] +#[track_caller] +fn rel_path_to_forward_slashes(path: &Utf8Path) -> Utf8PathBuf { + assert!(is_truly_relative(path), "path {path} must be relative"); + path.as_str().replace('\\', "/").into() +} + +#[cfg(not(windows))] +#[track_caller] +fn normalize_slashes(path: &Utf8Path) -> Utf8PathBuf { + path.to_owned() +} + +#[cfg(not(windows))] +#[track_caller] +fn rel_path_to_forward_slashes(path: &Utf8Path) -> Utf8PathBuf { + assert!(is_truly_relative(path), "path {path} must be relative"); + path.to_owned() +} + +/// Returns true if this is a path with no root-dir or prefix components. +/// +/// On Windows, unlike `path.is_relative()`, this rejects paths like "C:temp" +/// and "\temp". +#[track_caller] +fn is_truly_relative(path: &Utf8Path) -> bool { + path.components().all(|c| match c { + Utf8Component::Normal(_) | Utf8Component::CurDir | Utf8Component::ParentDir => true, + Utf8Component::RootDir | Utf8Component::Prefix(_) => false, + }) +} + +#[derive(Debug)] +#[doc(hidden)] +pub(crate) enum TestSource { + /// A data source on disk, with the path being the relative path to the file + /// from the crate root. + Path(Utf8PathBuf), + #[cfg(feature = "include-dir")] + IncludeDir(&'static include_dir::File<'static>), +} + +/// Polymorphic dispatch to resolve data sources +/// +/// This is similar to how `test_kinds` works. Here, we're assuming that +/// `include_dir::Dir` will never implement `ToString`. This isn't provable to +/// the compiler directly, but is a reasonable assumption. +/// +/// This could use auto(de)ref specialization to be more semver-safe, but a +/// `Display` impl on `include_dir::Dir` is exceedingly unlikely by Rust +/// community standards, and meanwhile this produces better error messages. +#[doc(hidden)] +pub mod data_source_kinds { + use super::*; + + mod private { + pub trait AsDirectorySealed {} + #[cfg(feature = "include-dir")] + pub trait AsIncludeDirSealed {} + } + + // -- As directory --- + + pub trait AsDirectory: private::AsDirectorySealed { + fn resolve_data_source(self) -> DataSource; + } + + impl private::AsDirectorySealed for T {} + + impl AsDirectory for T { + fn resolve_data_source(self) -> DataSource { + let s = self.to_string(); + let path = Utf8Path::new(&s); + + DataSource::Directory(normalize_slashes(path)) + } + } + + #[cfg(feature = "include-dir")] + pub trait AsIncludeDir: private::AsIncludeDirSealed { + fn resolve_data_source(self) -> DataSource; + } + + #[cfg(feature = "include-dir")] + impl private::AsIncludeDirSealed for include_dir::Dir<'static> {} + + #[cfg(feature = "include-dir")] + impl AsIncludeDir for include_dir::Dir<'static> { + fn resolve_data_source(self) -> DataSource { + DataSource::IncludeDir(std::borrow::Cow::Owned(self)) + } + } + + #[cfg(feature = "include-dir")] + impl private::AsIncludeDirSealed for &'static include_dir::Dir<'static> {} + + #[cfg(feature = "include-dir")] + impl AsIncludeDir for &'static include_dir::Dir<'static> { + fn resolve_data_source(self) -> DataSource { + DataSource::IncludeDir(std::borrow::Cow::Borrowed(self)) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn missing_test_name() { + assert_eq!(derive_test_path("root".into(), "file", "test_name"), None); + } + + #[test] + fn missing_colons() { + assert_eq!( + derive_test_path("root".into(), "test_name", "test_name"), + None + ); + } + + #[test] + fn is_relative_to_root() { + assert_eq!( + derive_test_path("root".into(), "test_name::file", "test_name"), + Some("root/file".into()) + ); + assert_eq!( + derive_test_path("root2".into(), "test_name::file", "test_name"), + Some("root2/file".into()) + ); + } + + #[test] + fn nested_dirs() { + assert_eq!( + derive_test_path("root".into(), "test_name::dir/dir2/file", "test_name"), + Some("root/dir/dir2/file".into()) + ); + } + + #[test] + fn subsequent_module_separators_remain() { + assert_eq!( + derive_test_path("root".into(), "test_name::mod::file", "test_name"), + Some("root/mod::file".into()) + ); + } + + #[test] + fn inverse_of_derive_test_name() { + let root: Utf8PathBuf = "root".into(); + for (path, test_name) in [ + (root.join("foo/bar.txt"), "test_name"), + (root.join("foo::bar.txt"), "test_name"), + (root.join("foo/bar/baz"), "test_name"), + (root.join("foo"), "test_name::mod"), + (root.join("🦀"), "🚀::🚀"), + ] { + let derived_test_name = derive_test_name(&root, &path, test_name); + assert_eq!( + derive_test_path(&root, &derived_test_name, test_name), + Some(path) + ); + } + } + + fn derive_test_name(root: &Utf8Path, path: &Utf8Path, test_name: &str) -> String { + TestEntry::from_full_path(root, path.to_owned()).derive_test_name(test_name) + } + + fn derive_test_path(root: &Utf8Path, path: &str, test_name: &str) -> Option { + DataSource::Directory(root.to_owned()) + .derive_exact(path, test_name) + .map(|entry| entry.test_path().to_owned()) + } +} diff --git a/tools/vendor/datatest-stable/src/lib.rs b/tools/vendor/datatest-stable/src/lib.rs new file mode 100644 index 0000000000..1331fc457a --- /dev/null +++ b/tools/vendor/datatest-stable/src/lib.rs @@ -0,0 +1,263 @@ +// Copyright (c) The datatest-stable Contributors +// SPDX-License-Identifier: MIT OR Apache-2.0 + +#![forbid(unsafe_code)] + +//! `datatest-stable` is a test harness intended to write *file-driven* or *data-driven* tests, +//! where individual test case fixtures are specified as files and not as code. +//! +//! Given: +//! +//! * a test `my_test` that accepts a path, and optionally the contents as input +//! * a directory to look for files (test fixtures) in +//! * a pattern to match files on +//! +//! `datatest-stable` will call the `my_test` function once per matching file in +//! the directory. Directory traversals are recursive. +//! +//! `datatest-stable` works with [cargo nextest](https://nexte.st/), and is part +//! of the [nextest-rs organization](https://github.com/nextest-rs/) on GitHub. +//! With nextest, each test case is represented as a separate test, and is run +//! as a separate process in parallel. +//! +//! # Usage +//! +//! 1. Configure the test target by setting `harness = false` in `Cargo.toml`: +//! +//! ```toml +//! [[test]] +//! name = "" +//! harness = false +//! ``` +//! +//! 2. Call the `datatest_stable::harness!` macro as: +//! +//! ```rust,ignore +//! datatest_stable::harness! { +//! { test = my_test, root = "path/to/fixtures", pattern = r".*" }, +//! } +//! ``` +//! +//! * `test` - The test function to be executed on each matching input. This function can be one +//! of: +//! * `fn(&Path) -> datatest_stable::Result<()>` +//! * `fn(&Utf8Path) -> datatest_stable::Result<()>` ([`Utf8Path`](camino::Utf8Path) is part of the +//! [`camino`] library, and is re-exported here for convenience.) +//! * `fn(&P, String) -> datatest_stable::Result<()>` where `P` is `Path` or `Utf8Path`. If the +//! extra `String` parameter is specified, the contents of the file will be loaded and passed in +//! as a string (erroring out if that failed). +//! * `fn(&P, Vec) -> datatest_stable::Result<()>` where `P` is `Path` or `Utf8Path`. If the +//! extra `Vec` parameter is specified, the contents of the file will be loaded and passed +//! in as a `Vec` (erroring out if that failed). +//! +//! * `root` - The path to the root directory where the input files (fixtures) +//! live. Relative paths are resolved relative to the crate root (the directory where the crate's +//! `Cargo.toml` is located). +//! +//! `root` is an arbitrary expression that implements +//! [`Display`](std::fmt::Display), such as `&str`, or a function call that +//! returns a [`Utf8PathBuf`](camino::Utf8PathBuf). +//! +//! * `pattern` - a regex used to match against and select each file to be tested. Extended regexes +//! with lookaround and backtracking are supported via the [`fancy_regex`] crate. +//! +//! `pattern` is an arbitrary expression that implements [`Display`](std::fmt::Display), such as +//! `&str`, or a function call that returns a `String`. +//! +//! `pattern` is optional, and defaults to `r".*"` (match all files). +//! +//! The three parameters can be repeated if you have multiple sets of data-driven tests to be run: +//! +//! ```rust,ignore +//! datatest_stable::harness! { +//! { test = testfn1, root = root1, pattern = pattern1 }, +//! { test = testfn2, root = root2 }, +//! } +//! ``` +//! +//! Trailing commas are optional. +//! +//! ## Relative and absolute paths +//! +//! The `pattern` argument is tested against the **relative** path of each file, +//! **excluding** the `root` prefix -- not the absolute path. +//! +//! The `Path` and `Utf8Path` passed into the test functions are created by +//! joining `root` to the relative path of the file. +//! +//! * If `root` is **relative**, the paths passed in are relative to the crate root. +//! * If `root` is **absolute**, the paths passed in are absolute. +//! +//! For uniformity, all relative paths use `/` as the path separator, +//! including on Windows, and all absolute paths use the platform's native +//! separator throughout. +//! +//! ## Examples +//! +//! This is an example test. Use it with `harness = false`. +//! +//! ```rust +//! use datatest_stable::Utf8Path; +//! use std::path::Path; +//! +//! fn my_test(path: &Path) -> datatest_stable::Result<()> { +//! // ... write test here +//! +//! Ok(()) +//! } +//! +//! fn my_test_utf8(path: &Utf8Path, contents: String) -> datatest_stable::Result<()> { +//! // ... write test here +//! +//! Ok(()) +//! } +//! +//! datatest_stable::harness! { +//! { test = my_test, root = "path/to/fixtures" }, +//! { +//! test = my_test_utf8, +//! root = "path/to/fixtures", +//! pattern = r"^.*\.txt$", +//! }, +//! } +//! ``` +//! +//! If `path/to/fixtures` contains a file `foo/bar.txt`, then: +//! +//! * The pattern `r"^.*/*"` will match `foo/bar.txt`. +//! * `my_test` and `my_test_utf8` will be called with `"path/to/fixtures/foo/bar.txt"`. +//! +//! ## Embedding directories at compile time +//! +//! With the `include-dir` feature enabled, you can use the +//! [`include_dir`](https://docs.rs/include_dir) crate's [`include_dir!`] macro. +//! This allows you to embed directories into the binary at compile time. +//! +//! This is generally not recommended for rapidly-changing test data, since each +//! change will force a rebuild. But it can be useful for relatively-unchanging +//! data suites distributed separately, e.g. on crates.io. +//! +//! With the `include-dir` feature enabled, you can use: +//! +#![cfg_attr(feature = "include-dir", doc = "```rust")] +#![cfg_attr(not(feature = "include-dir"), doc = "```rust,ignore")] +//! // The `include_dir!` macro is re-exported for convenience. +//! use datatest_stable::include_dir; +//! use std::path::Path; +//! +//! fn my_test(path: &Path, contents: Vec) -> datatest_stable::Result<()> { +//! // ... write test here +//! Ok(()) +//! } +//! +//! datatest_stable::harness! { +//! { test = my_test, root = include_dir!("tests/files"), pattern = r"^.*\.json$" }, +//! } +//! ``` +//! +//! You can also use directories published as `static` items in upstream crates: +//! +#![cfg_attr(feature = "include-dir", doc = "```rust")] +#![cfg_attr(not(feature = "include-dir"), doc = "```rust,ignore")] +//! use datatest_stable::{include_dir, Utf8Path}; +//! +//! // In the upstream crate: +//! pub static FIXTURES: include_dir::Dir<'static> = +//! include_dir!("$CARGO_MANIFEST_DIR/tests/files"); +//! +//! // In your test: +//! fn my_test(path: &Utf8Path, contents: String) -> datatest_stable::Result<()> { +//! // ... write test here +//! Ok(()) +//! } +//! +//! datatest_stable::harness! { +//! { test = my_test, root = &FIXTURES }, +//! } +//! ``` +//! +//! In this case, the passed-in `Path` and `Utf8Path` are always **relative** to +//! the root of the included directory. Like elsewhere in `datatest-stable`, +//! these relative paths always use forward slashes as separators, including on +//! Windows. +//! +//! Because the files don't exist on disk, the test functions must accept their +//! contents as either a `String` or a `Vec`. If the argument is not +//! provided, the harness will panic at runtime. +//! +//! ## Conditionally embedding directories +//! +//! It is also possible to conditionally include directories at compile time via +//! a feature flag. For example, you might have an internal-only `testing` +//! feature that you turn on locally, but users don't on crates.io. In that +//! case, you can use: +//! +#![cfg_attr(feature = "include-dir", doc = "```rust")] +#![cfg_attr(not(feature = "include-dir"), doc = "```rust,ignore")] +//! use datatest_stable::Utf8Path; +//! +//! // In the library itself: +//! pub mod fixtures { +//! #[cfg(feature = "testing")] +//! pub static FIXTURES: &str = "tests/files"; +//! +//! #[cfg(not(feature = "testing"))] +//! pub static FIXTURES: include_dir::Dir<'static> = +//! include_dir::include_dir!("$CARGO_MANIFEST_DIR/tests/files"); +//! } +//! +//! // In the test: +//! fn my_test(path: &Utf8Path, contents: String) -> datatest_stable::Result<()> { +//! // ... write test here +//! Ok(()) +//! } +//! +//! datatest_stable::harness! { +//! { test = my_test, root = &fixtures::FIXTURES, pattern = r"^inputs/.*$" }, +//! } +//! ``` +//! +//! In this case, note that `path` will be relative to the **crate directory** +//! (e.g. `tests/files/foo/bar.txt`) if `FIXTURES` is a string, and relative to +//! the **include directory** (e.g. `foo/bar.txt`) if `FIXTURES` is a +//! [`Dir`](include_dir::Dir). Your test should be prepared to handle either +//! case. +//! +//! # Features +//! +//! * `include-dir`: Enables the `include_dir!` macro, which allows embedding +//! directories at compile time. This feature is disabled by default. +//! +//! # Minimum supported Rust version (MSRV) +//! +//! The minimum supported Rust version is **Rust 1.72**. MSRV bumps may be accompanied by a minor +//! version update; at any time, Rust versions from at least the last 6 months are supported. +//! +//! # See also +//! +//! * [`datatest`](https://crates.io/crates/datatest): the original inspiration for this crate, with +//! more features but targeting nightly Rust. +//! * [Data-driven testing](https://en.wikipedia.org/wiki/Data-driven_testing) + +#![warn(missing_docs)] +#![cfg_attr(doc_cfg, feature(doc_cfg))] + +mod data_source; +mod macros; +mod runner; + +/// The result type for `datatest-stable` tests. +pub type Result = std::result::Result>; + +#[doc(hidden)] +pub use self::data_source::{data_source_kinds, DataSource}; +/// Not part of the public API, just used for macros. +#[doc(hidden)] +pub use self::runner::{runner, test_kinds, Requirements, TestFn}; +/// A re-export of this type from the `camino` crate, since it forms part of function signatures. +#[doc(no_inline)] +pub use camino::Utf8Path; +/// A re-export of `include_dir!` from the `include_dir` crate, for convenience. +#[cfg(feature = "include-dir")] +#[doc(no_inline)] +pub use include_dir::include_dir; diff --git a/tools/vendor/datatest-stable/src/macros.rs b/tools/vendor/datatest-stable/src/macros.rs new file mode 100644 index 0000000000..45d2907f7d --- /dev/null +++ b/tools/vendor/datatest-stable/src/macros.rs @@ -0,0 +1,193 @@ +// Copyright (c) The datatest-stable Contributors +// SPDX-License-Identifier: MIT OR Apache-2.0 + +/// `datatest-stable` test harness entry point. Should be declared in the test module. +/// +/// Also, `harness` should be set to `false` for that test module in `Cargo.toml` (see [Configuring +/// a target](https://doc.rust-lang.org/cargo/reference/manifest.html#configuring-a-target)). +#[macro_export] +macro_rules! harness { + ( $( { $($args:tt)* } ),+ $(,)* ) => { + fn main() -> ::std::process::ExitCode { + let mut requirements = Vec::new(); + use $crate::data_source_kinds::*; + use $crate::test_kinds::*; + + $( + $crate::harness_collect!(@gather_test requirements, { $($args)*, } => { }); + )+ + + $crate::runner(&requirements) + } + }; + ( $( $name:path, $root:expr, $pattern:expr ),+ $(,)* ) => { + // This is the old format with datatest-stable 0.2. Print a nice message + // in this case. + const _: () = { + compile_error!( +concat!(r"this format is no longer supported -- please switch to specifying as: + +datatest_stable::harness! { +", + $(concat!(" { test = ", stringify!($name), ", root = ", stringify!($root), ", pattern = ", stringify!($pattern), " },\n"),)+ +r"} + +note: patterns are now evaluated relative to the provided root, not to the crate root +")); + }; + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! harness_collect { + // Gather `test` + (@gather_test + $requirements:expr, + // Note: here and below, rest always ends with at least 1 comma + { test = $test:path, $($rest:tt)* } => + { } + ) => { + $crate::harness_collect!(@gather_root + $requirements, + { $($rest)* } => + { test = $test, } + ); + }; + + // `test` not found + (@gather_test + $requirements:expr, + { $key:ident $($rest:tt)* } => + { } + ) => { + compile_error!(concat!("expected `test`, found `", stringify!($key), "`")); + }; + + // No remaining arguments + (@gather_test + $requirements:expr, + { $(,)* } => + { } + ) => { + compile_error!("expected `test`, but ran out of arguments"); + }; + + // Something that isn't an identifier + (@gather_test + $requirements:expr, + { $($rest:tt)* } => + { } + ) => { + compile_error!(concat!("expected `test`, found non-identifier token: (rest: ", stringify!($($rest)*), ")")); + }; + + // Gather `root` + (@gather_root + $requirements:expr, + { root = $root:expr, $($rest:tt)* } => + { $($collected:tt)* } + ) => { + $crate::harness_collect!(@gather_pattern + $requirements, + { $($rest)* } => + { $($collected)* root = $root, } + ); + }; + + // `root` not found + (@gather_root + $requirements:expr, + { $key:ident $($rest:tt)* } => + { $($collected:tt)* } + ) => { + compile_error!(concat!("expected `root`, found `", stringify!($key), "`")); + }; + + // No remaining arguments + (@gather_root + $requirements:expr, + { $(,)* } => + { $($collected:tt)* } + ) => { + compile_error!(concat!("expected `root`, but ran out of arguments (collected: ", stringify!($($collected)*), ")")); + }; + + // Something that isn't an identifier + (@gather_root + $requirements:expr, + { $($rest:tt)* } => + { $($collected:tt)* } + ) => { + compile_error!(concat!("expected `root`, found non-identifier token (rest: ", stringify!($($rest)*), ")")); + }; + + // Gather pattern + (@gather_pattern + $requirements:expr, + { pattern = $pattern:expr, $($rest:tt)* } => + { $($collected:tt)* } + ) => { + $crate::harness_collect!(@finish + $requirements, + { $($rest)* } => + { $($collected)* pattern = $pattern, } + ); + }; + + // `pattern` not found + (@gather_pattern + $requirements:expr, + { $key:ident $($rest:tt)* } => + { $($collected:tt)* } + ) => { + compile_error!(concat!("expected `pattern`, found `", stringify!($key), "`")); + }; + + // `pattern` not found: no remaining arguments + (@gather_pattern + $requirements:expr, + { $(,)* } => + { $($collected:tt)* } + ) => { + $crate::harness_collect!(@finish + $requirements, + { } => + { $($collected)* pattern = ".*", } + ); + }; + + // Something that isn't an identifier + (@gather_pattern + $requirements:expr, + { $($rest:tt)* } => + { $($collected:tt)* } + ) => { + compile_error!(concat!("expected `pattern`, found non-identifier token (rest: ", stringify!($($rest)*), ")")); + }; + + // Finish - no more arguments allowed + (@finish + $requirements:expr, + { $(,)* } => + { test = $test:path, root = $root:expr, pattern = $pattern:expr, } + ) => { + $requirements.push( + $crate::Requirements::new( + $test.kind().resolve($test), + stringify!($test).to_string(), + $root.resolve_data_source(), + $pattern.to_string() + ) + ); + }; + + // Finish - unexpected extra arguments + (@finish + $requirements:expr, + { $($unexpected:tt)+ } => + { $($collected:tt)* } + ) => { + compile_error!(concat!("unexpected extra arguments: ", stringify!($($unexpected)+))); + }; +} diff --git a/tools/vendor/datatest-stable/src/runner.rs b/tools/vendor/datatest-stable/src/runner.rs new file mode 100644 index 0000000000..14072e94f3 --- /dev/null +++ b/tools/vendor/datatest-stable/src/runner.rs @@ -0,0 +1,487 @@ +// Copyright (c) The datatest-stable Contributors +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use crate::{data_source::TestEntry, DataSource, Result}; +use camino::{Utf8Path, Utf8PathBuf}; +use libtest_mimic::{Arguments, Trial}; +use std::{path::Path, process::ExitCode}; + +#[doc(hidden)] +pub fn runner(requirements: &[Requirements]) -> ExitCode { + if let Some(cwd) = custom_cwd() { + std::env::set_current_dir(cwd).expect("set custom working directory"); + } + + let args = Arguments::from_args(); + + let tests = find_tests(&args, requirements); + + let conclusion = libtest_mimic::run(&args, tests); + + // This used to use `Conclusion::exit`, but that exits the process via `std::process::exit` as + // of libtest-mimic 0.7.0. This breaks some things, e.g. llvm-cov on Windows. + // https://github.com/nextest-rs/datatest-stable/issues/20 + // + // Use `std::process::ExitCode` instead, and return it in main. + + conclusion.exit_code() +} + +/// One of our tests requires that a custom working directory be set. This function is used to do +/// that. +fn custom_cwd() -> Option { + std::env::var("__DATATEST_CWD").ok().map(Utf8PathBuf::from) +} + +fn find_tests(args: &Arguments, requirements: &[Requirements]) -> Vec { + let tests: Vec<_> = if let Some(exact_filter) = exact_filter(args) { + let exact_tests: Vec<_> = requirements + .iter() + .filter_map(|req| req.exact(exact_filter)) + .collect(); + + match NextestKind::determine() { + NextestKind::InUse { process_per_test } => { + if exact_tests.is_empty() { + panic!("Failed to find exact match for filter {exact_filter}"); + } + if process_per_test && exact_tests.len() > 1 { + panic!( + "Only expected one but found {} exact matches for filter {exact_filter}", + exact_tests.len() + ); + } + } + NextestKind::NotInUse => {} + } + + exact_tests + } else if is_full_scan_forbidden(args) { + panic!("Exact filter was expected to be used"); + } else { + let mut tests: Vec<_> = requirements.iter().flat_map(|req| req.expand()).collect(); + tests.sort_unstable_by(|a, b| a.name().cmp(b.name())); + tests + }; + tests +} + +#[derive(Clone, Copy, Debug)] +enum NextestKind { + NotInUse, + InUse { process_per_test: bool }, +} + +impl NextestKind { + fn determine() -> Self { + if std::env::var("NEXTEST").as_deref() == Ok("1") { + // Process-per-test means that exactly one test should be run. + let process_per_test = + std::env::var("NEXTEST_EXECUTION_MODE").as_deref() == Ok("process-per-test"); + Self::InUse { process_per_test } + } else { + Self::NotInUse + } + } +} + +fn is_full_scan_forbidden(args: &Arguments) -> bool { + !args.list && std::env::var("__DATATEST_FULL_SCAN_FORBIDDEN").as_deref() == Ok("1") +} + +fn exact_filter(args: &Arguments) -> Option<&str> { + if args.exact && args.skip.is_empty() { + args.filter.as_deref() + } else { + None + } +} + +#[doc(hidden)] +pub struct Requirements { + test: TestFn, + test_name: String, + root: DataSource, + pattern: String, +} + +impl Requirements { + #[doc(hidden)] + pub fn new(test: TestFn, test_name: String, root: DataSource, pattern: String) -> Self { + // include_dir data sources aren't compatible with test functions that + // don't accept the contents as an argument. + if !test.loads_data() && root.is_in_memory() { + panic!( + "test data for '{}' is stored in memory, so it \ + must accept file contents as an argument", + test_name + ); + } + + Self { + test, + test_name, + root, + pattern, + } + } + + fn trial(&self, entry: TestEntry) -> Trial { + let testfn = self.test; + let name = entry.derive_test_name(&self.test_name); + Trial::test(name, move || { + testfn + .call(entry) + .map_err(|err| format!("{:?}", err).into()) + }) + } + + fn exact(&self, filter: &str) -> Option { + let entry = self.root.derive_exact(filter, &self.test_name)?; + entry.exists().then(|| self.trial(entry)) + } + + /// Scans all files in a given directory, finds matching ones and generates a test descriptor + /// for each of them. + fn expand(&self) -> Vec { + let re = fancy_regex::Regex::new(&self.pattern) + .unwrap_or_else(|_| panic!("invalid regular expression: '{}'", self.pattern)); + + let tests: Vec<_> = self + .root + .walk_files() + .filter_map(|entry_res| { + let entry = entry_res.expect("error reading directory"); + let path_str = entry.match_path().as_str(); + if re.is_match(path_str).unwrap_or_else(|error| { + panic!( + "error matching pattern '{}' against path '{}' : {}", + self.pattern, path_str, error + ) + }) { + Some(self.trial(entry)) + } else { + None + } + }) + .collect(); + + // We want to avoid silent fails due to typos in regexp! + if tests.is_empty() { + panic!( + "no test cases found for test '{}' -- scanned {} with pattern '{}'", + self.test_name, + self.root.display(), + self.pattern, + ); + } + + tests + } +} + +// -- Polymorphic dispatch -- + +#[derive(Clone, Copy)] +#[doc(hidden)] +pub enum TestFn { + // Functions that work on paths. + Base(TestFnBase), + /// Test functions that load a file as a string (UTF-8 text). + LoadString(TestFnLoadString), + /// Test functions that load a file as binary data. + LoadBinary(TestFnLoadBinary), +} + +impl TestFn { + fn loads_data(&self) -> bool { + match self { + TestFn::Base(_) => false, + TestFn::LoadString(_) | TestFn::LoadBinary(_) => true, + } + } + + fn call(&self, entry: TestEntry) -> Result<()> { + match self { + TestFn::Base(f) => { + let path = entry + .disk_path() + .expect("test entry being on disk was checked in the constructor"); + f.call(path) + } + TestFn::LoadString(f) => f.call(entry), + TestFn::LoadBinary(f) => f.call(entry), + } + } +} + +#[derive(Clone, Copy)] +#[doc(hidden)] +pub enum TestFnBase { + Path(fn(&Path) -> Result<()>), + Utf8Path(fn(&Utf8Path) -> Result<()>), +} + +impl TestFnBase { + fn call(&self, path: &Utf8Path) -> Result<()> { + match self { + TestFnBase::Path(f) => f(path.as_ref()), + TestFnBase::Utf8Path(f) => f(path), + } + } +} + +#[derive(Clone, Copy)] +#[doc(hidden)] +pub enum TestFnLoadString { + Path(fn(&Path, String) -> Result<()>), + Utf8Path(fn(&Utf8Path, String) -> Result<()>), +} + +impl TestFnLoadString { + fn call(&self, entry: TestEntry) -> Result<()> { + let contents = entry.read_as_string()?; + match self { + TestFnLoadString::Path(f) => f(entry.test_path().as_ref(), contents), + TestFnLoadString::Utf8Path(f) => f(entry.test_path(), contents), + } + } +} + +#[derive(Clone, Copy)] +#[doc(hidden)] +pub enum TestFnLoadBinary { + Path(fn(&Path, Vec) -> Result<()>), + Utf8Path(fn(&Utf8Path, Vec) -> Result<()>), +} + +impl TestFnLoadBinary { + fn call(&self, entry: TestEntry) -> Result<()> { + let contents = entry.read()?; + match self { + TestFnLoadBinary::Path(f) => f(entry.test_path().as_ref(), contents), + TestFnLoadBinary::Utf8Path(f) => f(entry.test_path(), contents), + } + } +} + +/// Implementations to allow `TestFn` to be created with functions of one of several types. +/// +/// datatest-stable supports several options for the shape of test functions. This code allows: +/// +/// * the `harness!` macro to accept any of these types of functions, generating the same syntactic +/// code for each. +/// * for all of those functions to resolve to a single `TestFn` type which can do dynamic dispatch. +/// +/// There are several challenges to overcome here, the main one being that you cannot have multiple +/// different kinds of `Fn`s impl the same trait. For example, rustc will not accept this code +/// ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=007c814fb457bd4e95d0073745b5f8d9)): +/// +/// ```rust,ignore +/// trait MyTrait {} +/// +/// impl MyTrait for F {} +/// impl MyTrait for F {} +/// ``` +/// +/// This means that it is not possible to write code that is type-system generic over different +/// kinds of function types. +/// +/// But since `harness!` is a macro, it can expand to code that's syntactically the same for each of +/// those function types, but semantically resolves to completely different types. That's exactly +/// what we do here. +/// +/// This is a trick very similar to autoref specialization, though we don't use the automatic `&` +/// Rust inserts while dereferencing methods. See [autoref-specialization]. +/// +/// # Notes +/// +/// We can't say `impl PathKind for fn(&Path) -> Result<()>` because Rust won't automatically coerce +/// the concrete function type to the function pointer. (If we could, then we wouldn't need to rely +/// on the macro-ness of `harness!` at all, and could just have a single trait implemented for all +/// the different function pointer types.) To address this, we use a two-step process. +/// +/// * Step 1: Implement `PathKind` for all `F: Fn(&Path) -> Result<()>`. This allows a `.kind()` +/// method to exist which returns a new `PathTag` type. +/// * Step 2: Implement `PathTag::new` which only takes `fn(&Path) -> Result<()>`. *This* type can +/// coerce to the function pointer, which can be stored in the `TestFn` enum. +/// +/// This two-step process is similar to the one documented in [autoref-specialization]. +/// +/// [autoref-specialization]: https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md +#[doc(hidden)] +pub mod test_kinds { + + use super::*; + + mod private { + // We need to define a separate Sealed for each of the tags below, because Rust doesn't allow + // multiple kinds of F: Fn(T) -> Result<()> to implement the same trait. + pub trait PathSealed {} + pub trait Utf8PathSealed {} + pub trait PathStringSealed {} + pub trait Utf8PathStringSealed {} + pub trait PathBytesSealed {} + pub trait Utf8PathBytesSealed {} + } + + // -- Paths -- + + #[doc(hidden)] + pub struct PathTag; + + impl PathTag { + #[inline] + pub fn resolve(self, f: fn(&Path) -> Result<()>) -> TestFn { + TestFn::Base(TestFnBase::Path(f)) + } + } + + #[doc(hidden)] + pub trait PathKind: private::PathSealed { + #[inline] + fn kind(&self) -> PathTag { + PathTag + } + } + + impl Result<()>> private::PathSealed for F {} + impl Result<()>> PathKind for F {} + + // -- UTF-8 paths -- + + #[doc(hidden)] + pub struct Utf8PathTag; + + impl Utf8PathTag { + #[inline] + pub fn resolve(&self, f: fn(&Utf8Path) -> Result<()>) -> TestFn { + TestFn::Base(TestFnBase::Utf8Path(f)) + } + } + + #[doc(hidden)] + pub trait Utf8PathKind: private::Utf8PathSealed { + #[inline] + fn kind(&self) -> Utf8PathTag { + Utf8PathTag + } + } + + impl Result<()>> private::Utf8PathSealed for F {} + impl Result<()>> Utf8PathKind for F {} + + // -- Path, load file as string -- + + #[doc(hidden)] + pub struct PathStringTag; + + impl PathStringTag { + #[inline] + pub fn resolve(self, f: fn(&Path, String) -> Result<()>) -> TestFn { + TestFn::LoadString(TestFnLoadString::Path(f)) + } + } + + #[doc(hidden)] + pub trait PathStringKind: private::PathStringSealed { + #[inline] + fn kind(&self) -> PathStringTag { + PathStringTag + } + } + + impl Result<()>> private::PathStringSealed for F {} + impl Result<()>> PathStringKind for F {} + + // -- Utf8Path, load file as string -- + + #[doc(hidden)] + pub struct Utf8PathStringTag; + + impl Utf8PathStringTag { + #[inline] + pub fn resolve(self, f: fn(&Utf8Path, String) -> Result<()>) -> TestFn { + TestFn::LoadString(TestFnLoadString::Utf8Path(f)) + } + } + + #[doc(hidden)] + pub trait Utf8PathStringKind: private::Utf8PathStringSealed { + #[inline] + fn kind(&self) -> Utf8PathStringTag { + Utf8PathStringTag + } + } + + impl Result<()>> private::Utf8PathStringSealed for F {} + impl Result<()>> Utf8PathStringKind for F {} + + // -- Path, load file as binary -- + + #[doc(hidden)] + pub struct PathBytesTag; + + impl PathBytesTag { + #[inline] + pub fn resolve(self, f: fn(&Path, Vec) -> Result<()>) -> TestFn { + TestFn::LoadBinary(TestFnLoadBinary::Path(f)) + } + } + + #[doc(hidden)] + pub trait PathBytesKind: private::PathBytesSealed { + #[inline] + fn kind(&self) -> PathBytesTag { + PathBytesTag + } + } + + impl) -> Result<()>> private::PathBytesSealed for F {} + impl) -> Result<()>> PathBytesKind for F {} + + // -- Utf8Path, load file as binary -- + + #[doc(hidden)] + pub struct Utf8PathBytesTag; + + impl Utf8PathBytesTag { + #[inline] + pub fn resolve(self, f: fn(&Utf8Path, Vec) -> Result<()>) -> TestFn { + TestFn::LoadBinary(TestFnLoadBinary::Utf8Path(f)) + } + } + + #[doc(hidden)] + pub trait Utf8PathBytesKind: private::Utf8PathBytesSealed { + #[inline] + fn kind(&self) -> Utf8PathBytesTag { + Utf8PathBytesTag + } + } + + impl) -> Result<()>> private::Utf8PathBytesSealed for F {} + impl) -> Result<()>> Utf8PathBytesKind for F {} +} + +#[cfg(all(test, feature = "include-dir"))] +mod include_dir_tests { + use super::*; + use std::borrow::Cow; + + #[test] + #[should_panic = "test data for 'my_test' is stored in memory, \ + so it must accept file contents as an argument"] + fn include_dir_without_arg() { + fn my_test(_: &Path) -> Result<()> { + Ok(()) + } + + Requirements::new( + TestFn::Base(TestFnBase::Path(my_test)), + "my_test".to_owned(), + DataSource::IncludeDir(Cow::Owned(include_dir::include_dir!("tests/files"))), + "xxx".to_owned(), + ); + } +} diff --git a/tools/vendor/datatest-stable/tests/compile-fail/empty-arg-list.rs b/tools/vendor/datatest-stable/tests/compile-fail/empty-arg-list.rs new file mode 100644 index 0000000000..1edddf8c4e --- /dev/null +++ b/tools/vendor/datatest-stable/tests/compile-fail/empty-arg-list.rs @@ -0,0 +1,3 @@ +datatest_stable::harness! { + { } +} diff --git a/tools/vendor/datatest-stable/tests/compile-fail/empty-arg-list.stderr b/tools/vendor/datatest-stable/tests/compile-fail/empty-arg-list.stderr new file mode 100644 index 0000000000..52859c3b5a --- /dev/null +++ b/tools/vendor/datatest-stable/tests/compile-fail/empty-arg-list.stderr @@ -0,0 +1,9 @@ +error: expected `test`, but ran out of arguments + --> tests/compile-fail/empty-arg-list.rs:1:1 + | +1 | / datatest_stable::harness! { +2 | | { } +3 | | } + | |_^ + | + = note: this error originates in the macro `$crate::harness_collect` which comes from the expansion of the macro `datatest_stable::harness` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tools/vendor/datatest-stable/tests/compile-fail/extra-args.rs b/tools/vendor/datatest-stable/tests/compile-fail/extra-args.rs new file mode 100644 index 0000000000..50f29e44a8 --- /dev/null +++ b/tools/vendor/datatest-stable/tests/compile-fail/extra-args.rs @@ -0,0 +1,3 @@ +datatest_stable::harness! { + { test = my_test, root = "abc", pattern = ".*", extra = "xyz" } +} diff --git a/tools/vendor/datatest-stable/tests/compile-fail/extra-args.stderr b/tools/vendor/datatest-stable/tests/compile-fail/extra-args.stderr new file mode 100644 index 0000000000..478a27073a --- /dev/null +++ b/tools/vendor/datatest-stable/tests/compile-fail/extra-args.stderr @@ -0,0 +1,9 @@ +error: unexpected extra arguments: extra = "xyz", + --> tests/compile-fail/extra-args.rs:1:1 + | +1 | / datatest_stable::harness! { +2 | | { test = my_test, root = "abc", pattern = ".*", extra = "xyz" } +3 | | } + | |_^ + | + = note: this error originates in the macro `$crate::harness_collect` which comes from the expansion of the macro `datatest_stable::harness` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tools/vendor/datatest-stable/tests/compile-fail/incorrect-arg-in-pattern.rs b/tools/vendor/datatest-stable/tests/compile-fail/incorrect-arg-in-pattern.rs new file mode 100644 index 0000000000..6159f80352 --- /dev/null +++ b/tools/vendor/datatest-stable/tests/compile-fail/incorrect-arg-in-pattern.rs @@ -0,0 +1,3 @@ +datatest_stable::harness! { + { test = my_test, root = "tests/files", foo = "bar", } +} diff --git a/tools/vendor/datatest-stable/tests/compile-fail/incorrect-arg-in-pattern.stderr b/tools/vendor/datatest-stable/tests/compile-fail/incorrect-arg-in-pattern.stderr new file mode 100644 index 0000000000..72b5c5c00d --- /dev/null +++ b/tools/vendor/datatest-stable/tests/compile-fail/incorrect-arg-in-pattern.stderr @@ -0,0 +1,9 @@ +error: expected `pattern`, found `foo` + --> tests/compile-fail/incorrect-arg-in-pattern.rs:1:1 + | +1 | / datatest_stable::harness! { +2 | | { test = my_test, root = "tests/files", foo = "bar", } +3 | | } + | |_^ + | + = note: this error originates in the macro `$crate::harness_collect` which comes from the expansion of the macro `datatest_stable::harness` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tools/vendor/datatest-stable/tests/compile-fail/missing-root-comma.rs b/tools/vendor/datatest-stable/tests/compile-fail/missing-root-comma.rs new file mode 100644 index 0000000000..cc9a1a5af1 --- /dev/null +++ b/tools/vendor/datatest-stable/tests/compile-fail/missing-root-comma.rs @@ -0,0 +1,6 @@ +datatest_stable::harness! { + { + test = my_test, + pattern = r"^.*(? tests/compile-fail/missing-root-comma.rs:1:1 + | +1 | / datatest_stable::harness! { +2 | | { +3 | | test = my_test, +4 | | pattern = r"^.*(? tests/compile-fail/missing-root-no-args.rs:1:1 + | +1 | / datatest_stable::harness! { +2 | | { +3 | | test = my_test, +4 | | }, +5 | | } + | |_^ + | + = note: this error originates in the macro `$crate::harness_collect` which comes from the expansion of the macro `datatest_stable::harness` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tools/vendor/datatest-stable/tests/compile-fail/missing-root-no-comma.rs b/tools/vendor/datatest-stable/tests/compile-fail/missing-root-no-comma.rs new file mode 100644 index 0000000000..0bcf84c6ba --- /dev/null +++ b/tools/vendor/datatest-stable/tests/compile-fail/missing-root-no-comma.rs @@ -0,0 +1,3 @@ +datatest_stable::harness! { + { test = my_test, pattern = r"^.*(? tests/compile-fail/missing-root-no-comma.rs:1:1 + | +1 | / datatest_stable::harness! { +2 | | { test = my_test, pattern = r"^.*(? tests/compile-fail/missing-test-comma.rs:1:1 + | +1 | / datatest_stable::harness! { +2 | | { +3 | | root = "tests/files", +4 | | pattern = r"^.*(? tests/compile-fail/missing-test-no-comma.rs:1:1 + | +1 | / datatest_stable::harness! { +2 | | { root = "tests/files", test = my_test, pattern = r"^.*(? tests/compile-fail/old-format.rs:1:1 + | +1 | datatest_stable::harness!(foo, "path1", r"^.*/*$", bar::baz, "path2", r"^.*/*$"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `datatest_stable::harness` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tools/vendor/datatest-stable/tests/compile-fail/pattern-not-ident.rs b/tools/vendor/datatest-stable/tests/compile-fail/pattern-not-ident.rs new file mode 100644 index 0000000000..95f1d118d1 --- /dev/null +++ b/tools/vendor/datatest-stable/tests/compile-fail/pattern-not-ident.rs @@ -0,0 +1,3 @@ +datatest_stable::harness! { + { test = my_test, root = "abc", "xyz" = foo } +} diff --git a/tools/vendor/datatest-stable/tests/compile-fail/pattern-not-ident.stderr b/tools/vendor/datatest-stable/tests/compile-fail/pattern-not-ident.stderr new file mode 100644 index 0000000000..f6f7c9cbb5 --- /dev/null +++ b/tools/vendor/datatest-stable/tests/compile-fail/pattern-not-ident.stderr @@ -0,0 +1,9 @@ +error: expected `pattern`, found non-identifier token (rest: "xyz" = foo,) + --> tests/compile-fail/pattern-not-ident.rs:1:1 + | +1 | / datatest_stable::harness! { +2 | | { test = my_test, root = "abc", "xyz" = foo } +3 | | } + | |_^ + | + = note: this error originates in the macro `$crate::harness_collect` which comes from the expansion of the macro `datatest_stable::harness` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tools/vendor/datatest-stable/tests/compile-fail/root-not-ident.rs b/tools/vendor/datatest-stable/tests/compile-fail/root-not-ident.rs new file mode 100644 index 0000000000..d1d2e43321 --- /dev/null +++ b/tools/vendor/datatest-stable/tests/compile-fail/root-not-ident.rs @@ -0,0 +1,3 @@ +datatest_stable::harness! { + { test = my_test, "xyz" = foo } +} diff --git a/tools/vendor/datatest-stable/tests/compile-fail/root-not-ident.stderr b/tools/vendor/datatest-stable/tests/compile-fail/root-not-ident.stderr new file mode 100644 index 0000000000..23b812ec92 --- /dev/null +++ b/tools/vendor/datatest-stable/tests/compile-fail/root-not-ident.stderr @@ -0,0 +1,9 @@ +error: expected `root`, found non-identifier token (rest: "xyz" = foo,) + --> tests/compile-fail/root-not-ident.rs:1:1 + | +1 | / datatest_stable::harness! { +2 | | { test = my_test, "xyz" = foo } +3 | | } + | |_^ + | + = note: this error originates in the macro `$crate::harness_collect` which comes from the expansion of the macro `datatest_stable::harness` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tools/vendor/datatest-stable/tests/compile-fail/root-out-of-order.rs b/tools/vendor/datatest-stable/tests/compile-fail/root-out-of-order.rs new file mode 100644 index 0000000000..652eb5f55b --- /dev/null +++ b/tools/vendor/datatest-stable/tests/compile-fail/root-out-of-order.rs @@ -0,0 +1,3 @@ +datatest_stable::harness! { + { test = my_test, pattern = r"^.*(? tests/compile-fail/root-out-of-order.rs:1:1 + | +1 | / datatest_stable::harness! { +2 | | { test = my_test, pattern = r"^.*(? tests/compile-fail/test-not-ident.rs:1:1 + | +1 | / datatest_stable::harness! { +2 | | { "xyz" = foo } +3 | | } + | |_^ + | + = note: this error originates in the macro `$crate::harness_collect` which comes from the expansion of the macro `datatest_stable::harness` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tools/vendor/datatest-stable/tests/compile-fail/test-out-of-order.rs b/tools/vendor/datatest-stable/tests/compile-fail/test-out-of-order.rs new file mode 100644 index 0000000000..2a5a8d3b09 --- /dev/null +++ b/tools/vendor/datatest-stable/tests/compile-fail/test-out-of-order.rs @@ -0,0 +1,3 @@ +datatest_stable::harness! { + { root = "tests/files", pattern = r"^.*(? tests/compile-fail/test-out-of-order.rs:1:1 + | +1 | / datatest_stable::harness! { +2 | | { root = "tests/files", pattern = r"^.*(? Result<()> { + let mut file = File::open(path)?; + let mut contents = String::new(); + file.read_to_string(&mut contents)?; + + Ok(()) +} + +fn test_artifact_utf8(path: &Utf8Path) -> Result<()> { + test_artifact(path.as_ref()) +} + +fn test_artifact_utf8_abs(path: &Utf8Path) -> Result<()> { + // path must be absolute + assert!(path.is_absolute(), "path must be absolute: {:?}", path); + + // On Windows, the path must be normalized to use backslashes. + #[cfg(windows)] + { + assert!( + !path.as_str().contains('/'), + "path must not contain forward slashes: {:?}", + path + ); + } + + test_artifact(path.as_ref()) +} + +#[cfg(feature = "include-dir")] +#[macro_use] +mod with_contents { + use super::*; + + /// Returns an `include_dir::Dir` instance. + macro_rules! maybe_include_dir { + () => { + include_dir::include_dir!("$CARGO_MANIFEST_DIR/tests/files") + }; + } + + /// A `&'static include_dir::Dir` instance. + pub(crate) static MAYBE_INCLUDE_STATIC: include_dir::Dir = + include_dir::include_dir!("$CARGO_MANIFEST_DIR/tests/files"); + + pub(crate) fn test_artifact_string(path: &Path, contents: String) -> Result<()> { + compare_contents(path, contents.as_bytes()) + } + + pub(crate) fn test_artifact_utf8_string(path: &Utf8Path, contents: String) -> Result<()> { + compare_contents(path.as_std_path(), contents.as_bytes()) + } + + pub(crate) fn test_artifact_bytes(path: &Path, contents: Vec) -> Result<()> { + compare_contents(path, &contents) + } + + pub(crate) fn test_artifact_utf8_bytes(path: &Utf8Path, contents: Vec) -> Result<()> { + compare_contents(path.as_std_path(), &contents) + } + + fn compare_contents(path: &Path, expected: &[u8]) -> Result<()> { + // The path must not begin with "tests/files". + assert!( + !path.to_string_lossy().starts_with("tests/files"), + "path must not start with 'tests/files': {:?}", + path + ); + + // Prepend tests/files to the path to get the expected contents. In + // general we can't verify the contents, but in this case we can do so + // because the paths are also available on disk. + let path = format!("tests/files/{}", path.to_str().unwrap()); + compare(path.as_ref(), expected) + } +} + +#[cfg(not(feature = "include-dir"))] +#[macro_use] +mod with_contents { + use super::*; + + /// Returns an `include_dir::Dir` instance. + macro_rules! maybe_include_dir { + () => { + "tests/files" + }; + } + + /// A `&'static include_dir::Dir` instance. + pub(crate) static MAYBE_INCLUDE_STATIC: &str = "tests/files"; + + pub(crate) fn test_artifact_string(path: &Path, contents: String) -> Result<()> { + compare_contents(path, contents.as_bytes()) + } + + pub(crate) fn test_artifact_utf8_string(path: &Utf8Path, contents: String) -> Result<()> { + compare_contents(path.as_std_path(), contents.as_bytes()) + } + + pub(crate) fn test_artifact_bytes(path: &Path, contents: Vec) -> Result<()> { + compare_contents(path, &contents) + } + + pub(crate) fn test_artifact_utf8_bytes(path: &Utf8Path, contents: Vec) -> Result<()> { + compare_contents(path.as_std_path(), &contents) + } + + fn compare_contents(path: &Path, expected: &[u8]) -> Result<()> { + // The path must begin with "tests/files". + assert!( + path.to_string_lossy().starts_with("tests/files"), + "path must start with 'tests/files': {:?}", + path + ); + compare(&path, expected) + } +} + +fn compare(path: &Path, expected: &[u8]) -> Result<()> { + // The path must be relative. + assert!(path.is_relative(), "path must be relative: {:?}", path); + + // The path must not have any backslashes on Windows. + assert!( + !path.to_string_lossy().contains('\\'), + "path must not contain backslashes: {:?}", + path + ); + + let actual = + std::fs::read(path).map_err(|error| format!("failed to read file: {:?}: {error}", path))?; + + assert_eq!(expected, &actual, "file contents match for {:?}", path); + + Ok(()) +} + +#[cfg(windows)] +static TESTS_FILES_MAIN_SEP: &str = "tests\\files"; + +#[cfg(not(windows))] +static TESTS_FILES_MAIN_SEP: &str = "tests/files"; + +fn tests_files_abs() -> String { + std::env::current_dir() + .expect("current dir obtained") + .join("tests/files") + .to_string_lossy() + .into_owned() +} + +datatest_stable::harness! { + { + test = test_artifact, + root = "tests/files", + // This regex pattern skips .skip.txt files. + pattern = r"^.*(? String { + std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()) +} diff --git a/tools/vendor/difflib/.cargo-checksum.json b/tools/vendor/difflib/.cargo-checksum.json new file mode 100644 index 0000000000..d4bfa40e4e --- /dev/null +++ b/tools/vendor/difflib/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"aed0597f7b6014324ef49cb6502e541e45c35335785345d7d7a5cac46cc64c0f","Cargo.toml.orig":"d541194ec9dbbc63a8e371fcb0a20af128671a73bea0c4734e17e43ed6956e50","examples/example.rs":"2807d9c94f5c9ae53c2948a64de7a36ec27e6973adc35a850ea18cc37a66cfb2","src/differ.rs":"115671b901c439affb816d8a7d4275bf31ebba045d6118596dfac776b78699d3","src/lib.rs":"1d196166d4ee0d7eb6cfe127920c2a314a965098c7f08bef44be163528368bde","src/sequencematcher.rs":"b671c4ee066cc548c98784bb6fd6788c10ef8d75e7eb176694659ad7ad976f12","src/utils.rs":"9ef8aa4219e856745dec81849453ae3ff7edba6a16f470d1a4ba65eb498b1044","tests/tests.rs":"ecd1b3fb91c24a42e506500d84e6028a4f6027266f2ddc2e208825173649a53f"},"package":"6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"} \ No newline at end of file diff --git a/tools/vendor/difflib/Cargo.toml b/tools/vendor/difflib/Cargo.toml new file mode 100644 index 0000000000..29a094f4f8 --- /dev/null +++ b/tools/vendor/difflib/Cargo.toml @@ -0,0 +1,26 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g. crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +name = "difflib" +version = "0.4.0" +authors = ["Dima Kudosh "] +include = ["**/*.rs", "Cargo.toml"] +description = "Port of Python's difflib library to Rust." +homepage = "https://github.com/DimaKudosh/difflib" +documentation = "https://github.com/DimaKudosh/difflib/wiki" +keywords = ["difflib", "text", "diff"] +license = "MIT" +repository = "https://github.com/DimaKudosh/difflib" + +[[test]] +name = "tests" diff --git a/tools/vendor/difflib/Cargo.toml.orig b/tools/vendor/difflib/Cargo.toml.orig new file mode 100644 index 0000000000..2edf6df6a6 --- /dev/null +++ b/tools/vendor/difflib/Cargo.toml.orig @@ -0,0 +1,19 @@ +[package] +name = "difflib" +version = "0.4.0" +authors = ["Dima Kudosh "] +description = "Port of Python's difflib library to Rust." +documentation = "https://github.com/DimaKudosh/difflib/wiki" +homepage = "https://github.com/DimaKudosh/difflib" +repository = "https://github.com/DimaKudosh/difflib" +keywords = ["difflib", "text", "diff"] +license = "MIT" +include = [ + "**/*.rs", + "Cargo.toml", +] + + +[[test]] +name = "tests" + diff --git a/tools/vendor/difflib/examples/example.rs b/tools/vendor/difflib/examples/example.rs new file mode 100644 index 0000000000..3f06d0adb7 --- /dev/null +++ b/tools/vendor/difflib/examples/example.rs @@ -0,0 +1,63 @@ +extern crate difflib; + +use difflib::differ::Differ; +use difflib::sequencematcher::SequenceMatcher; + +fn main() { + // unified_diff + let first_text = "one two three four".split(" ").collect::>(); + let second_text = "zero one tree four".split(" ").collect::>(); + let diff = difflib::unified_diff( + &first_text, + &second_text, + "Original", + "Current", + "2005-01-26 23:30:50", + "2010-04-02 10:20:52", + 3, + ); + for line in &diff { + println!("{:?}", line); + } + + //context_diff + let diff = difflib::context_diff( + &first_text, + &second_text, + "Original", + "Current", + "2005-01-26 23:30:50", + "2010-04-02 10:20:52", + 3, + ); + for line in &diff { + println!("{:?}", line); + } + + //get_close_matches + let words = vec!["ape", "apple", "peach", "puppy"]; + let result = difflib::get_close_matches("appel", words, 3, 0.6); + println!("{:?}", result); + + //Differ examples + let differ = Differ::new(); + let diff = differ.compare(&first_text, &second_text); + for line in &diff { + println!("{:?}", line); + } + + //SequenceMatcher examples + let mut matcher = SequenceMatcher::new("one two three four", "zero one tree four"); + let m = matcher.find_longest_match(0, 18, 0, 18); + println!("{:?}", m); + let all_matches = matcher.get_matching_blocks(); + println!("{:?}", all_matches); + let opcode = matcher.get_opcodes(); + println!("{:?}", opcode); + let grouped_opcodes = matcher.get_grouped_opcodes(2); + println!("{:?}", grouped_opcodes); + let ratio = matcher.ratio(); + println!("{:?}", ratio); + matcher.set_seqs("aaaaa", "aaaab"); + println!("{:?}", matcher.ratio()); +} diff --git a/tools/vendor/difflib/src/differ.rs b/tools/vendor/difflib/src/differ.rs new file mode 100644 index 0000000000..5caef9a7a8 --- /dev/null +++ b/tools/vendor/difflib/src/differ.rs @@ -0,0 +1,340 @@ +use sequencematcher::SequenceMatcher; +use std::cmp; +use utils::{count_leading, str_with_similar_chars}; + +#[derive(Default)] +pub struct Differ { + pub line_junk: Option bool>, + pub char_junk: Option bool>, +} + +impl Differ { + pub fn new() -> Differ { + Differ { + line_junk: None, + char_junk: None, + } + } + + pub fn compare(&self, first_sequence: &[&str], second_sequence: &[&str]) -> Vec { + let mut matcher = SequenceMatcher::new(first_sequence, second_sequence); + matcher.set_is_junk(self.line_junk); + let mut res = Vec::new(); + for opcode in matcher.get_opcodes() { + let mut gen = Vec::new(); + match opcode.tag.as_ref() { + "replace" => { + gen = self.fancy_replace( + first_sequence, + opcode.first_start, + opcode.first_end, + second_sequence, + opcode.second_start, + opcode.second_end, + ) + } + "delete" => { + gen = self.dump("-", first_sequence, opcode.first_start, opcode.first_end) + } + "insert" => { + gen = self.dump("+", second_sequence, opcode.second_start, opcode.second_end) + } + "equal" => { + gen = self.dump(" ", first_sequence, opcode.first_start, opcode.first_end) + } + _ => {} + } + for i in gen { + res.push(i); + } + } + res + } + + fn dump(&self, tag: &str, sequence: &[&str], start: usize, end: usize) -> Vec { + let mut res = Vec::new(); + for i in start..end { + if let Some(s) = sequence.get(i) { + res.push(format!("{} {}", tag, s)) + } + } + res + } + + fn plain_replace( + &self, + first_sequence: &[&str], + first_start: usize, + first_end: usize, + second_sequence: &[&str], + second_start: usize, + second_end: usize, + ) -> Vec { + if !(first_start < first_end && second_start < second_end) { + return Vec::new(); + } + let (mut first, second) = if second_end - second_start < first_end - first_start { + ( + self.dump("+", second_sequence, second_start, second_end), + self.dump("-", first_sequence, first_start, first_end), + ) + } else { + ( + self.dump("-", first_sequence, first_start, first_end), + self.dump("+", second_sequence, second_start, second_end), + ) + }; + for s in second { + first.push(s); + } + first + } + + fn fancy_replace( + &self, + first_sequence: &[&str], + first_start: usize, + first_end: usize, + second_sequence: &[&str], + second_start: usize, + second_end: usize, + ) -> Vec { + let mut res = Vec::new(); + let (mut best_ratio, cutoff) = (0.74, 0.75); + let (mut best_i, mut best_j) = (0, 0); + let mut eqi: Option = None; + let mut eqj: Option = None; + for (j, second_sequence_str) in second_sequence + .iter() + .enumerate() + .take(second_end) + .skip(second_start) + { + for (i, first_sequence_str) in first_sequence + .iter() + .enumerate() + .take(second_end) + .skip(second_start) + { + if first_sequence_str == second_sequence_str { + if eqi.is_none() { + eqi = Some(i); + eqj = Some(j); + } + continue; + } + let (first_sequence_chars, second_sequence_chars) = ( + first_sequence_str.chars().collect::>(), + second_sequence_str.chars().collect::>(), + ); + let mut cruncher = + SequenceMatcher::new(&first_sequence_chars, &second_sequence_chars); + cruncher.set_is_junk(self.char_junk); + if cruncher.ratio() > best_ratio { + best_ratio = cruncher.ratio(); + best_i = i; + best_j = j; + } + } + } + if best_ratio < cutoff { + if eqi.is_none() { + res.extend( + self.plain_replace( + first_sequence, + first_start, + first_end, + second_sequence, + second_start, + second_end, + ).iter() + .cloned(), + ); + return res; + } + best_i = eqi.unwrap(); + best_j = eqj.unwrap(); + } else { + eqi = None; + } + res.extend( + self.fancy_helper( + first_sequence, + first_start, + best_i, + second_sequence, + second_start, + best_j, + ).iter() + .cloned(), + ); + let first_element = &first_sequence[best_i]; + let second_element = &second_sequence[best_j]; + if eqi.is_none() { + let (mut first_tag, mut second_tag) = (String::new(), String::new()); + let first_element_chars: Vec = first_element.chars().collect(); + let second_element_chars: Vec = second_element.chars().collect(); + let mut cruncher = SequenceMatcher::new(&first_element_chars, &second_element_chars); + cruncher.set_is_junk(self.char_junk); + for opcode in &cruncher.get_opcodes() { + let (first_length, second_length) = ( + opcode.first_end - opcode.first_start, + opcode.second_end - opcode.second_start, + ); + match opcode.tag.as_ref() { + "replace" => { + first_tag.push_str(&str_with_similar_chars('^', first_length)); + second_tag.push_str(&str_with_similar_chars('^', second_length)); + } + "delete" => { + first_tag.push_str(&str_with_similar_chars('-', first_length)); + } + "insert" => { + second_tag.push_str(&str_with_similar_chars('+', second_length)); + } + "equal" => { + first_tag.push_str(&str_with_similar_chars(' ', first_length)); + second_tag.push_str(&str_with_similar_chars(' ', second_length)); + } + _ => {} + } + } + res.extend( + self.qformat(&first_element, &second_element, &first_tag, &second_tag) + .iter() + .cloned(), + ); + } else { + let mut s = String::from(" "); + s.push_str(&first_element); + res.push(s); + } + res.extend( + self.fancy_helper( + first_sequence, + best_i + 1, + first_end, + second_sequence, + best_j + 1, + second_end, + ).iter() + .cloned(), + ); + res + } + + fn fancy_helper( + &self, + first_sequence: &[&str], + first_start: usize, + first_end: usize, + second_sequence: &[&str], + second_start: usize, + second_end: usize, + ) -> Vec { + let mut res = Vec::new(); + if first_start < first_end { + if second_start < second_end { + res = self.fancy_replace( + first_sequence, + first_start, + first_end, + second_sequence, + second_start, + second_end, + ); + } else { + res = self.dump("-", first_sequence, first_start, first_end); + } + } else if second_start < second_end { + res = self.dump("+", second_sequence, second_start, second_end); + } + res + } + + fn qformat( + &self, + first_line: &str, + second_line: &str, + first_tags: &str, + second_tags: &str, + ) -> Vec { + let mut res = Vec::new(); + let mut first_tags = first_tags; + let mut second_tags = second_tags; + let mut common = cmp::min( + count_leading(first_line, '\t'), + count_leading(second_line, '\t'), + ); + common = cmp::min(common, count_leading(first_tags.split_at(common).0, ' ')); + common = cmp::min(common, count_leading(first_tags.split_at(common).0, ' ')); + first_tags = first_tags.split_at(common).1.trim_right(); + second_tags = second_tags.split_at(common).1.trim_right(); + let mut s = format!("- {}", first_line); + res.push(s); + if first_tags != "" { + s = format!("? {}{}\n", str_with_similar_chars('\t', common), first_tags); + res.push(s); + } + s = format!("+ {}", second_line); + res.push(s); + if second_tags != "" { + s = format!( + "? {}{}\n", + str_with_similar_chars('\t', common), + second_tags + ); + res.push(s); + } + res + } + + pub fn restore(delta: &[String], which: usize) -> Vec { + if !(which == 1 || which == 2) { + panic!("Second parameter must be 1 or 2"); + } + let mut res = Vec::new(); + let tag = if which == 1 { "- " } else { "+ " }.to_string(); + let prefixes = vec![tag, " ".to_string()]; + for line in delta { + for prefix in &prefixes { + if line.starts_with(prefix) { + res.push(line.split_at(2).1.to_string()); + } + } + } + res + } +} + +#[test] +fn test_fancy_replace() { + let differ = Differ::new(); + let result = differ + .fancy_replace(&vec!["abcDefghiJkl\n"], 0, 1, &vec!["abcdefGhijkl\n"], 0, 1) + .join(""); + assert_eq!( + result, + "- abcDefghiJkl\n? ^ ^ ^\n+ abcdefGhijkl\n? ^ ^ ^\n" + ); +} + +#[test] +fn test_qformat() { + let differ = Differ::new(); + let result = differ.qformat( + "\tabcDefghiJkl\n", + "\tabcdefGhijkl\n", + " ^ ^ ^ ", + " ^ ^ ^ ", + ); + assert_eq!( + result, + vec![ + "- \tabcDefghiJkl\n", + "? \t ^ ^ ^\n", + "+ \tabcdefGhijkl\n", + "? \t ^ ^ ^\n", + ] + ); +} diff --git a/tools/vendor/difflib/src/lib.rs b/tools/vendor/difflib/src/lib.rs new file mode 100644 index 0000000000..ca6b1cc38b --- /dev/null +++ b/tools/vendor/difflib/src/lib.rs @@ -0,0 +1,169 @@ +pub mod differ; +pub mod sequencematcher; +mod utils; + +use sequencematcher::{Sequence, SequenceMatcher}; +use std::collections::HashMap; +use std::fmt::Display; +use utils::{format_range_context, format_range_unified}; + +pub fn get_close_matches<'a>( + word: &str, + possibilities: Vec<&'a str>, + n: usize, + cutoff: f32, +) -> Vec<&'a str> { + if !(0.0 <= cutoff && cutoff <= 1.0) { + panic!("Cutoff must be greater than 0.0 and lower than 1.0"); + } + let mut res: Vec<(f32, &str)> = Vec::new(); + let mut matcher = SequenceMatcher::new("", word); + for i in &possibilities { + matcher.set_first_seq(i); + let ratio = matcher.ratio(); + if ratio >= cutoff { + res.push((ratio, i)); + } + } + res.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap()); + res.truncate(n); + res.iter().map(|x| x.1).collect() +} + +pub fn unified_diff( + first_sequence: &[T], + second_sequence: &[T], + from_file: &str, + to_file: &str, + from_file_date: &str, + to_file_date: &str, + n: usize, +) -> Vec { + let mut res = Vec::new(); + let lineterm = '\n'; + let mut started = false; + let mut matcher = SequenceMatcher::new(first_sequence, second_sequence); + for group in &matcher.get_grouped_opcodes(n) { + if !started { + started = true; + let from_date = format!("\t{}", from_file_date); + let to_date = format!("\t{}", to_file_date); + res.push(format!("--- {}{}{}", from_file, from_date, lineterm)); + res.push(format!("+++ {}{}{}", to_file, to_date, lineterm)); + } + let (first, last) = (group.first().unwrap(), group.last().unwrap()); + let file1_range = format_range_unified(first.first_start, last.first_end); + let file2_range = format_range_unified(first.second_start, last.second_end); + res.push(format!( + "@@ -{} +{} @@{}", + file1_range, file2_range, lineterm + )); + for code in group { + if code.tag == "equal" { + for item in first_sequence + .iter() + .take(code.first_end) + .skip(code.first_start) + { + res.push(format!(" {}", item)); + } + continue; + } + if code.tag == "replace" || code.tag == "delete" { + for item in first_sequence + .iter() + .take(code.first_end) + .skip(code.first_start) + { + res.push(format!("-{}", item)); + } + } + if code.tag == "replace" || code.tag == "insert" { + for item in second_sequence + .iter() + .take(code.second_end) + .skip(code.second_start) + { + res.push(format!("+{}", item)); + } + } + } + } + res +} + +pub fn context_diff( + first_sequence: &[T], + second_sequence: &[T], + from_file: &str, + to_file: &str, + from_file_date: &str, + to_file_date: &str, + n: usize, +) -> Vec { + let mut res = Vec::new(); + let lineterm = '\n'; + let mut prefix: HashMap = HashMap::new(); + prefix.insert(String::from("insert"), String::from("+ ")); + prefix.insert(String::from("delete"), String::from("- ")); + prefix.insert(String::from("replace"), String::from("! ")); + prefix.insert(String::from("equal"), String::from(" ")); + let mut started = false; + let mut matcher = SequenceMatcher::new(first_sequence, second_sequence); + for group in &matcher.get_grouped_opcodes(n) { + if !started { + started = true; + let from_date = format!("\t{}", from_file_date); + let to_date = format!("\t{}", to_file_date); + res.push(format!("*** {}{}{}", from_file, from_date, lineterm)); + res.push(format!("--- {}{}{}", to_file, to_date, lineterm)); + } + let (first, last) = (group.first().unwrap(), group.last().unwrap()); + res.push(format!("***************{}", lineterm)); + let file1_range = format_range_context(first.first_start, last.first_end); + res.push(format!("*** {} ****{}", file1_range, lineterm)); + let mut any = false; + for opcode in group { + if opcode.tag == "replace" || opcode.tag == "delete" { + any = true; + break; + } + } + if any { + for opcode in group { + if opcode.tag != "insert" { + for item in first_sequence + .iter() + .take(opcode.first_end) + .skip(opcode.first_start) + { + res.push(format!("{}{}", &prefix[&opcode.tag], item)); + } + } + } + } + let file2_range = format_range_context(first.second_start, last.second_end); + res.push(format!("--- {} ----{}", file2_range, lineterm)); + any = false; + for opcode in group { + if opcode.tag == "replace" || opcode.tag == "insert" { + any = true; + break; + } + } + if any { + for opcode in group { + if opcode.tag != "delete" { + for item in second_sequence + .iter() + .take(opcode.second_end) + .skip(opcode.second_start) + { + res.push(format!("{}{}", prefix[&opcode.tag], item)); + } + } + } + } + } + res +} diff --git a/tools/vendor/difflib/src/sequencematcher.rs b/tools/vendor/difflib/src/sequencematcher.rs new file mode 100644 index 0000000000..4157808631 --- /dev/null +++ b/tools/vendor/difflib/src/sequencematcher.rs @@ -0,0 +1,346 @@ +use std::cmp::{max, min}; +use std::collections::HashMap; +use std::hash::Hash; +use utils::calculate_ratio; + +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)] +pub struct Match { + pub first_start: usize, + pub second_start: usize, + pub size: usize, +} + +impl Match { + fn new(first_start: usize, second_start: usize, size: usize) -> Match { + Match { + first_start, + second_start, + size, + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Opcode { + pub tag: String, + pub first_start: usize, + pub first_end: usize, + pub second_start: usize, + pub second_end: usize, +} + +impl Opcode { + fn new( + tag: String, + first_start: usize, + first_end: usize, + second_start: usize, + second_end: usize, + ) -> Opcode { + Opcode { + tag, + first_start, + first_end, + second_start, + second_end, + } + } +} + +pub trait Sequence: Eq + Hash {} +impl Sequence for T {} + +pub struct SequenceMatcher<'a, T: 'a + Sequence> { + first_sequence: &'a [T], + second_sequence: &'a [T], + matching_blocks: Option>, + opcodes: Option>, + is_junk: Option bool>, + second_sequence_elements: HashMap<&'a T, Vec>, +} + +impl<'a, T: Sequence> SequenceMatcher<'a, T> { + pub fn new(first_sequence: &'a S, second_sequence: &'a S) -> SequenceMatcher<'a, T> + where + S: AsRef<[T]> + ?Sized, + { + let mut matcher = SequenceMatcher { + first_sequence: first_sequence.as_ref(), + second_sequence: second_sequence.as_ref(), + matching_blocks: None, + opcodes: None, + is_junk: None, + second_sequence_elements: HashMap::new(), + }; + matcher.set_seqs(first_sequence, second_sequence); + matcher + } + + pub fn set_is_junk(&mut self, is_junk: Option bool>) { + self.is_junk = is_junk; + self.matching_blocks = None; + self.opcodes = None; + self.chain_second_seq(); + } + + pub fn set_seqs(&mut self, first_sequence: &'a S, second_sequence: &'a S) + where + S: AsRef<[T]> + ?Sized, + { + self.set_first_seq(first_sequence); + self.set_second_seq(second_sequence); + } + + pub fn set_first_seq(&mut self, sequence: &'a S) + where + S: AsRef<[T]> + ?Sized, + { + self.first_sequence = sequence.as_ref(); + self.matching_blocks = None; + self.opcodes = None; + } + + pub fn set_second_seq(&mut self, sequence: &'a S) + where + S: AsRef<[T]> + ?Sized, + { + self.second_sequence = sequence.as_ref(); + self.matching_blocks = None; + self.opcodes = None; + self.chain_second_seq(); + } + + fn chain_second_seq(&mut self) { + let second_sequence = self.second_sequence; + let mut second_sequence_elements = HashMap::new(); + for (i, item) in second_sequence.iter().enumerate() { + let mut counter = second_sequence_elements + .entry(item) + .or_insert_with(Vec::new); + counter.push(i); + } + if let Some(junk_func) = self.is_junk { + second_sequence_elements = second_sequence_elements + .into_iter() + .filter(|&(element, _)| !junk_func(element)) + .collect(); + } + // Filter out popular elements + let len = second_sequence.len(); + if len >= 200 { + let test_len = (len as f32 / 100.0).floor() as usize + 1; + second_sequence_elements = second_sequence_elements + .into_iter() + .filter(|&(_, ref indexes)| indexes.len() > test_len) + .collect(); + } + self.second_sequence_elements = second_sequence_elements; + } + + pub fn find_longest_match( + &self, + first_start: usize, + first_end: usize, + second_start: usize, + second_end: usize, + ) -> Match { + let first_sequence = &self.first_sequence; + let second_sequence = &self.second_sequence; + let second_sequence_elements = &self.second_sequence_elements; + let (mut best_i, mut best_j, mut best_size) = (first_start, second_start, 0); + let mut j2len: HashMap = HashMap::new(); + for (i, item) in first_sequence + .iter() + .enumerate() + .take(first_end) + .skip(first_start) + { + let mut new_j2len: HashMap = HashMap::new(); + if let Some(indexes) = second_sequence_elements.get(item) { + for j in indexes { + let j = *j; + if j < second_start { + continue; + }; + if j >= second_end { + break; + }; + let mut size = 0; + if j > 0 { + if let Some(k) = j2len.get(&(j - 1)) { + size = *k; + } + } + size += 1; + new_j2len.insert(j, size); + if size > best_size { + best_i = i + 1 - size; + best_j = j + 1 - size; + best_size = size; + } + } + } + j2len = new_j2len; + } + for _ in 0..2 { + while best_i > first_start + && best_j > second_start + && first_sequence.get(best_i - 1) == second_sequence.get(best_j - 1) + { + best_i -= 1; + best_j -= 1; + best_size += 1; + } + while best_i + best_size < first_end + && best_j + best_size < second_end + && first_sequence.get(best_i + best_size) == second_sequence.get(best_j + best_size) + { + best_size += 1; + } + } + Match::new(best_i, best_j, best_size) + } + + pub fn get_matching_blocks(&mut self) -> Vec { + if self.matching_blocks.as_ref().is_some() { + return self.matching_blocks.as_ref().unwrap().clone(); + } + let (first_length, second_length) = (self.first_sequence.len(), self.second_sequence.len()); + let mut matches = Vec::new(); + let mut queue = vec![(0, first_length, 0, second_length)]; + while !queue.is_empty() { + let (first_start, first_end, second_start, second_end) = queue.pop().unwrap(); + let m = self.find_longest_match(first_start, first_end, second_start, second_end); + match m.size { + 0 => {} + _ => { + if first_start < m.first_start && second_start < m.second_start { + queue.push((first_start, m.first_start, second_start, m.second_start)); + } + if m.first_start + m.size < first_end && m.second_start + m.size < second_end { + queue.push(( + m.first_start + m.size, + first_end, + m.second_start + m.size, + second_end, + )); + } + matches.push(m); + } + } + } + matches.sort_by(|a, b| a.cmp(b)); + let (mut first_start, mut second_start, mut size) = (0, 0, 0); + let mut non_adjacent = Vec::new(); + for m in &matches { + if first_start + size == m.first_start && second_start + size == m.second_start { + size += m.size + } else { + if size != 0 { + non_adjacent.push(Match::new(first_start, second_start, size)); + } + first_start = m.first_start; + second_start = m.second_start; + size = m.size; + } + } + if size != 0 { + non_adjacent.push(Match::new(first_start, second_start, size)); + } + non_adjacent.push(Match::new(first_length, second_length, 0)); + self.matching_blocks = Some(non_adjacent); + self.matching_blocks.as_ref().unwrap().clone() + } + + pub fn get_opcodes(&mut self) -> Vec { + if self.opcodes.as_ref().is_some() { + return self.opcodes.as_ref().unwrap().clone(); + } + let mut opcodes = Vec::new(); + let (mut i, mut j) = (0, 0); + for m in self.get_matching_blocks() { + let mut tag = String::new(); + if i < m.first_start && j < m.second_start { + tag = String::from("replace"); + } else if i < m.first_start { + tag = String::from("delete"); + } else if j < m.second_start { + tag = String::from("insert"); + } + if !tag.is_empty() { + opcodes.push(Opcode::new(tag, i, m.first_start, j, m.second_start)); + } + i = m.first_start + m.size; + j = m.second_start + m.size; + if m.size != 0 { + opcodes.push(Opcode::new( + String::from("equal"), + m.first_start, + i, + m.second_start, + j, + )); + } + } + self.opcodes = Some(opcodes); + self.opcodes.as_ref().unwrap().clone() + } + + pub fn get_grouped_opcodes(&mut self, n: usize) -> Vec> { + let mut res = Vec::new(); + let mut codes = self.get_opcodes(); + if codes.is_empty() { + codes.push(Opcode::new("equal".to_string(), 0, 1, 0, 1)); + } + + if codes.first().unwrap().tag == "equal" { + let opcode = codes.first_mut().unwrap(); + opcode.first_start = max(opcode.first_start, opcode.first_end.saturating_sub(n)); + opcode.second_start = max(opcode.second_start, opcode.second_end.saturating_sub(n)); + } + if codes.last().unwrap().tag == "equal" { + let opcode = codes.last_mut().unwrap(); + opcode.first_end = min(opcode.first_start + n, opcode.first_end); + opcode.second_end = min(opcode.second_start + n, opcode.second_end); + } + let nn = n + n; + let mut group = Vec::new(); + for code in &codes { + let (mut first_start, mut second_start) = (code.first_start, code.second_start); + if code.tag == "equal" && code.first_end - code.first_start > nn { + group.push(Opcode::new( + code.tag.clone(), + code.first_start, + min(code.first_end, code.first_start + n), + code.second_start, + min(code.second_end, code.second_start + n), + )); + res.push(group.clone()); + group.clear(); + first_start = max(first_start, code.first_end.saturating_sub(n)); + second_start = max(second_start, code.second_end.saturating_sub(n)); + } + group.push(Opcode::new( + code.tag.clone(), + first_start, + code.first_end, + second_start, + code.second_end, + )); + } + if !(group.len() == 1 && group.first().unwrap().tag == "equal") || group.is_empty() { + res.push(group.clone()); + } + res + } + + pub fn ratio(&mut self) -> f32 { + let matches = self.get_matching_blocks() + .iter() + .fold(0, |res, &m| res + m.size); + calculate_ratio( + matches, + self.first_sequence.len() + self.second_sequence.len(), + ) + } +} diff --git a/tools/vendor/difflib/src/utils.rs b/tools/vendor/difflib/src/utils.rs new file mode 100644 index 0000000000..b711bf82ba --- /dev/null +++ b/tools/vendor/difflib/src/utils.rs @@ -0,0 +1,47 @@ +pub fn calculate_ratio(matches: usize, length: usize) -> f32 { + if length != 0 { + return 2.0 * matches as f32 / length as f32; + } + 1.0 +} + +pub fn str_with_similar_chars(c: char, length: usize) -> String { + let mut s = String::new(); + for _ in 0..length { + s.push_str(&c.to_string()); + } + s +} + +pub fn count_leading(line: &str, c: char) -> usize { + let (mut i, n) = (0, line.len()); + let line: Vec = line.chars().collect(); + while (i < n) && line[i] == c { + i += 1; + } + i +} + +pub fn format_range_unified(start: usize, end: usize) -> String { + let mut beginning = start + 1; + let length = end - start; + if length == 1 { + return beginning.to_string(); + } + if length == 0 { + beginning -= 1; + } + format!("{},{}", beginning, length) +} + +pub fn format_range_context(start: usize, end: usize) -> String { + let mut beginning = start + 1; + let length = end - start; + if length == 0 { + beginning -= 1 + } + if length <= 1 { + return beginning.to_string(); + } + format!("{},{}", beginning, beginning + length - 1) +} diff --git a/tools/vendor/difflib/tests/tests.rs b/tools/vendor/difflib/tests/tests.rs new file mode 100644 index 0000000000..8f49de9eba --- /dev/null +++ b/tools/vendor/difflib/tests/tests.rs @@ -0,0 +1,194 @@ +extern crate difflib; + +use difflib::differ::Differ; +use difflib::sequencematcher::{Match, Opcode, SequenceMatcher}; + +#[test] +fn test_longest_match() { + let matcher = SequenceMatcher::new(" abcd", "abcd abcd"); + let m = matcher.find_longest_match(0, 5, 0, 9); + assert_eq!(m.first_start, 0); + assert_eq!(m.second_start, 4); + assert_eq!(m.size, 5); +} + +#[test] +fn test_all_matches() { + let mut matcher = SequenceMatcher::new("abxcd", "abcd"); + let result = matcher.get_matching_blocks(); + let mut expected_result = Vec::new(); + expected_result.push(Match { + first_start: 0, + second_start: 0, + size: 2, + }); + expected_result.push(Match { + first_start: 3, + second_start: 2, + size: 2, + }); + expected_result.push(Match { + first_start: 5, + second_start: 4, + size: 0, + }); + assert_eq!(result, expected_result); +} + +#[test] +fn test_get_opcodes() { + let mut matcher = SequenceMatcher::new("qabxcd", "abycdf"); + let result = matcher.get_opcodes(); + let mut expected_result = Vec::new(); + expected_result.push(Opcode { + tag: "delete".to_string(), + first_start: 0, + first_end: 1, + second_start: 0, + second_end: 0, + }); + expected_result.push(Opcode { + tag: "equal".to_string(), + first_start: 1, + first_end: 3, + second_start: 0, + second_end: 2, + }); + expected_result.push(Opcode { + tag: "replace".to_string(), + first_start: 3, + first_end: 4, + second_start: 2, + second_end: 3, + }); + expected_result.push(Opcode { + tag: "equal".to_string(), + first_start: 4, + first_end: 6, + second_start: 3, + second_end: 5, + }); + expected_result.push(Opcode { + tag: "insert".to_string(), + first_start: 6, + first_end: 6, + second_start: 5, + second_end: 6, + }); + assert_eq!(result, expected_result); +} + +#[test] +fn test_ratio() { + let mut matcher = SequenceMatcher::new("abcd", "bcde"); + assert_eq!(matcher.ratio(), 0.75); +} + +#[test] +fn test_get_close_matches() { + let words = vec!["ape", "apple", "peach", "puppy"]; + let result = difflib::get_close_matches("appel", words, 3, 0.6); + assert_eq!(result, vec!["apple", "ape"]); +} + +#[test] +fn test_differ_compare() { + let first_text = vec!["one\n", "two\n", "three\n"]; + let second_text = vec!["ore\n", "tree\n", "emu\n"]; + let differ = Differ::new(); + let result = differ.compare(&first_text, &second_text).join(""); + assert_eq!( + result, + "- one\n? ^\n+ ore\n? ^\n- two\n- three\n? -\n+ tree\n+ emu\n" + ); +} + +fn is_junk_char(ch: &char) -> bool { + if *ch == ' ' || *ch == '\t' { + return true; + } + false +} + +#[test] +fn test_differ_compare_with_func() { + let first_text = vec!["one\n", "two\n", "three\n"]; + let second_text = vec!["ore\n", "tree\n", "emu\n"]; + let mut differ = Differ::new(); + differ.char_junk = Some(is_junk_char); + let result = differ.compare(&first_text, &second_text).join(""); + assert_eq!( + result, + "- one\n? ^\n+ ore\n? ^\n- two\n- three\n? -\n+ tree\n+ emu\n" + ); +} + +#[test] +fn test_differ_restore() { + let first_text = vec!["one\n", " two\n", "three\n"]; + let second_text = vec!["ore\n", "tree\n", "emu\n"]; + let differ = Differ::new(); + let diff = differ.compare(&first_text, &second_text); + assert_eq!(first_text, Differ::restore(&diff, 1)); + assert_eq!(second_text, Differ::restore(&diff, 2)); +} + +#[test] +fn test_unified_diff() { + let first_text = "one two three four".split(" ").collect::>(); + let second_text = "zero one tree four".split(" ").collect::>(); + let result = difflib::unified_diff( + &first_text, + &second_text, + "Original", + "Current", + "2005-01-26 23:30:50", + "2010-04-02 10:20:52", + 3, + ).join(""); + assert_eq!( + result, + "--- Original\t2005-01-26 23:30:50\n+++ Current\t2010-04-02 10:20:52\n@@ -1,4 \ + +1,4 @@\n+zero one-two-three+tree four" + ); +} + +#[test] +fn test_context_diff() { + let first_text = "one two three four".split(" ").collect::>(); + let second_text = "zero one tree four".split(" ").collect::>(); + let result = difflib::context_diff( + &first_text, + &second_text, + "Original", + "Current", + "2005-01-26 23:30:50", + "2010-04-02 10:20:52", + 3, + ).join(""); + assert_eq!( + result, + "*** Original\t2005-01-26 23:30:50\n--- Current\t2010-04-02 \ + 10:20:52\n***************\n*** 1,4 ****\n one! two! three four--- 1,4 ----\n+ \ + zero one! tree four" + ); +} + +#[test] +fn test_integer_slice() { + let s1 = vec![1, 2, 3, 4, 5]; + let s2 = vec![5, 4, 3, 2, 1]; + let result = SequenceMatcher::new(&s1, &s2).get_matching_blocks(); + let mut expected_result = Vec::new(); + expected_result.push(Match { + first_start: 0, + second_start: 4, + size: 1, + }); + expected_result.push(Match { + first_start: 5, + second_start: 5, + size: 0, + }); + assert_eq!(result, expected_result); +} diff --git a/tools/vendor/escape8259/.cargo-checksum.json b/tools/vendor/escape8259/.cargo-checksum.json new file mode 100644 index 0000000000..2394692d36 --- /dev/null +++ b/tools/vendor/escape8259/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"f783cf7ff26c39cfa69b625d497c2c736fb253ba906681a6d352e2bfb78fd0a5","Cargo.toml":"2024c51ccef2eba48ad593670000bf7d7b361a69b9f206ccb365376b8c3a2d0d","Cargo.toml.orig":"679017b6a76740919f91d4dd31567021198b840292ba4454e12ba6a51f1ab398","LICENSE":"2031f34a047e6057d8fed9867321271c3c8e27bd9a4ba6e97e5e1c08f0ee933e","README.md":"ee00c97243ba917530fcf946b182eb7b5676727af439d79df457a6fcf3f7723b","src/lib.rs":"e69b14cabcdd6d5db697910cb511601d86ff8bd98cd6108966b2476275a0a266"},"package":"5692dd7b5a1978a5aeb0ce83b7655c58ca8efdcb79d21036ea249da95afec2c6"} \ No newline at end of file diff --git a/tools/vendor/escape8259/.cargo_vcs_info.json b/tools/vendor/escape8259/.cargo_vcs_info.json new file mode 100644 index 0000000000..bbc82fd615 --- /dev/null +++ b/tools/vendor/escape8259/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "da55df5b65157ac2251b3fab6e4232f4d47bfa70" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/tools/vendor/escape8259/Cargo.toml b/tools/vendor/escape8259/Cargo.toml new file mode 100644 index 0000000000..7a6552c9f8 --- /dev/null +++ b/tools/vendor/escape8259/Cargo.toml @@ -0,0 +1,33 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "escape8259" +version = "0.5.3" +authors = ["Eric Seppanen "] +exclude = ["Cranky.toml"] +description = "RFC8259-compliant string escaping and un-escaping" +readme = "README.md" +keywords = [ + "json", + "rfc8259", + "cddl", +] +categories = ["encoding"] +license = "MIT" +repository = "https://github.com/ericseppanen/escape8259" + +[package.metadata.release] +pre-release-commit-message = "release {{version}}" + +[dev-dependencies.rustversion] +version = "1.0" diff --git a/tools/vendor/escape8259/Cargo.toml.orig b/tools/vendor/escape8259/Cargo.toml.orig new file mode 100644 index 0000000000..2b2f7a930c --- /dev/null +++ b/tools/vendor/escape8259/Cargo.toml.orig @@ -0,0 +1,18 @@ +[package] +name = "escape8259" +description = "RFC8259-compliant string escaping and un-escaping" +keywords = ["json", "rfc8259", "cddl"] +categories = ["encoding"] +version = "0.5.3" +repository = "https://github.com/ericseppanen/escape8259" +license = "MIT" +authors = ["Eric Seppanen "] +readme = "README.md" +edition = "2018" +exclude = ["Cranky.toml"] + +[dev-dependencies] +rustversion = "1.0" + +[package.metadata.release] +pre-release-commit-message = "release {{version}}" diff --git a/tools/vendor/escape8259/LICENSE b/tools/vendor/escape8259/LICENSE new file mode 100644 index 0000000000..e89298509b --- /dev/null +++ b/tools/vendor/escape8259/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Eric Seppanen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/tools/vendor/escape8259/README.md b/tools/vendor/escape8259/README.md new file mode 100644 index 0000000000..fb9f2fabd5 --- /dev/null +++ b/tools/vendor/escape8259/README.md @@ -0,0 +1,30 @@ + + +`escape8259` performs RFC8259-compliant string escaping and un-escaping. + +[RFC8259] is a JSON encoding standard. Many JSON encoders exist, but other +RFCs use the same string escaping mechanism, so it's useful to be able to +access the string escaping functions by themselves. + +# Examples + +```rust +use escape8259::{escape, unescape}; + +let s = "A null (\0) and a double-quote (\")"; +assert_eq!(escape(s), r#"A null (\u0000) and a double-quote (\")"#); + +let crab = r#"This is a crab: \ud83e\udd80"#; +assert_eq!(unescape(crab).unwrap(), "This is a crab: 🦀"); + +// We accept encodings that weren't really necessary. +assert_eq!(unescape(r#"\u0041\n"#).unwrap(), "A\n"); + +let multiline = r#"hello + world"#; +assert_eq!(escape(multiline), r#"hello\n world"#); +``` + +[RFC8259]: https://tools.ietf.org/html/rfc8259 + + diff --git a/tools/vendor/escape8259/src/lib.rs b/tools/vendor/escape8259/src/lib.rs new file mode 100644 index 0000000000..19c7f84909 --- /dev/null +++ b/tools/vendor/escape8259/src/lib.rs @@ -0,0 +1,316 @@ +//! `escape8259` performs RFC8259-compliant string escaping and un-escaping. +//! +//! [RFC8259] is a JSON encoding standard. Many JSON encoders exist, but other +//! RFCs use the same string escaping mechanism, so it's useful to be able to +//! access the string escaping functions by themselves. +//! +//! # Examples +//! +//! ``` +//! use escape8259::{escape, unescape}; +//! +//! let s = "A null (\0) and a double-quote (\")"; +//! assert_eq!(escape(s), r#"A null (\u0000) and a double-quote (\")"#); +//! +//! let crab = r#"This is a crab: \ud83e\udd80"#; +//! assert_eq!(unescape(crab).unwrap(), "This is a crab: 🦀"); +//! +//! // We accept encodings that weren't really necessary. +//! assert_eq!(unescape(r#"\u0041\n"#).unwrap(), "A\n"); +//! +//! let multiline = r#"hello +//! world"#; +//! assert_eq!(escape(multiline), r#"hello\n world"#); +//! ``` +//! +//! [RFC8259]: https://tools.ietf.org/html/rfc8259 + +#![warn(missing_docs)] +#![forbid(unsafe_code)] +#![warn(clippy::cast_possible_truncation)] + +use std::char::decode_utf16; +use std::fmt::{Display, Write}; + +/// An error occurred while unescaping. +#[allow(clippy::empty_structs_with_brackets)] // FIXME: correct this if releasing a new major version. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct UnescapeError {} + +impl Display for UnescapeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("failed rfc8259 unescape") + } +} + +impl std::error::Error for UnescapeError {} + +type UnescapeResult = Result; + +// Used to collect output characters and queue u16 values for translation. +struct UnescapeState { + // The accumulated characters + out: String, + // Store a fragment of a large character for later decoding + stash: u16, +} + +impl UnescapeState { + fn new() -> UnescapeState { + UnescapeState { + out: String::new(), + stash: 0, + } + } + + // Collect a new character + fn push_char(&mut self, c: char) -> UnescapeResult<()> { + if self.stash != 0 { + return Err(UnescapeError {}); + } + self.out.push(c); + Ok(()) + } + + // Collect a new UTF16 word. This can either be one whole character, + // or part of a larger character. + fn push_u16(&mut self, x: u16) -> UnescapeResult<()> { + let surrogate = (0xD800..=0xDFFF).contains(&x); + match (self.stash, surrogate) { + (0, false) => { + // The std library only provides utf16 decode of an iterator, + // so to decode a single character we wrap it in an array. + // Hopefully the compiler will elide most of this extra work. + let words = [x]; + match decode_utf16(words.iter().copied()).next() { + Some(Ok(c)) => { + self.out.push(c); + } + _ => return Err(UnescapeError {}), + } + } + (0, true) => self.stash = x, + (_, false) => { + return Err(UnescapeError {}); + } + (w, true) => { + let words = [w, x]; + match decode_utf16(words.iter().copied()).next() { + Some(Ok(c)) => { + self.out.push(c); + self.stash = 0; + } + _ => return Err(UnescapeError {}), + } + } + } + Ok(()) + } + + // If we queued up part of a UTF-16 encoded word but didn't + // finish it, return an error. Otherwise, consume self and + // return the accumulated String. + fn finalize(self) -> UnescapeResult { + if self.stash != 0 { + return Err(UnescapeError {}); + } + Ok(self.out) + } +} + +fn parse_u16(s: &mut S) -> UnescapeResult +where + S: Iterator, +{ + // Placeholder character in case the input doesn't have the 4 chars we want. + let placeholders = std::iter::repeat('\0'); + let hexnum: String = s.chain(placeholders).take(4).collect(); + u16::from_str_radix(&hexnum, 16).map_err(|_| UnescapeError {}) +} + +// RFC8259 says non-escaped characters must be in one of the following ranges: +// %x20-21 / %x23-5B / %x5D-10FFFF +fn is_safe_char(c: char) -> bool { + let safe_ranges = [(0x20..=0x21), (0x23..=0x5B), (0x5D..=0x10FFFF)]; + let cv = c as u32; + + safe_ranges.iter().any(|range| range.contains(&cv)) +} + +/// Un-escape a string, following RFC8259 rules. +/// +/// The only allowed single-character escapes are: +/// `\" \\ \/ /b /f /n /r /t` +/// +/// Any other character may be escaped in UTF-16 form: +/// `\uXXXX` or `\uXXXX\uXXXX` +/// +/// Characters in the ranges `0x20-21`, `0x23-5B`, `0x5D-10FFFF` +/// may appear un-escaped in the input. +#[inline] +pub fn unescape(s: S) -> UnescapeResult +where + S: AsRef, +{ + unescape_inner(s.as_ref()) +} + +fn unescape_inner(s: &str) -> UnescapeResult { + let mut state = UnescapeState::new(); + let mut ins = s.chars(); + + while let Some(c) = ins.next() { + if c == '\\' { + match ins.next() { + None => { + return Err(UnescapeError {}); + } + Some(d) => { + match d { + '"' | '\\' | '/' => state.push_char(d)?, + 'b' => state.push_char('\x08')?, // backspace + 'f' => state.push_char('\x0C')?, // formfeed + 'n' => state.push_char('\n')?, // linefeed + 'r' => state.push_char('\r')?, // carriage return + 't' => state.push_char('\t')?, // tab + 'u' => { + let val = parse_u16(&mut ins)?; + state.push_u16(val)?; + } + _ => { + return Err(UnescapeError {}); + } + } + } + } + } else if is_safe_char(c) { + state.push_char(c)?; + } else { + return Err(UnescapeError {}); + } + } + + state.finalize() +} + +// %x22 / ; " quotation mark U+0022 +// %x5C / ; \ reverse solidus U+005C +// %x2F / ; / solidus U+002F +// %x62 / ; b backspace U+0008 +// %x66 / ; f form feed U+000C +// %x6E / ; n line feed U+000A +// %x72 / ; r carriage return U+000D +// %x74 / ; t tab U+0009 +// %x75 4HEXDIG ) ; uXXXX U+XXXX + +fn force_escape(c: char, out: &mut String) { + let c = c as u32; + match c { + 0x08 => out.push_str("\\b"), + 0x09 => out.push_str("\\t"), + 0x0A => out.push_str("\\n"), + 0x0C => out.push_str("\\f"), + 0x0D => out.push_str("\\r"), + 0x22 => out.push_str("\\\""), + 0x5C => out.push_str("\\\\"), + _ => { + // RFC8259 allows unicode characters natively, so there is no need + // to convert everything into \uXXXX form. The only thing that's + // required to use that form are the ASCII control characters, + // which will never require more than one \uXXXX value. + if c >= 0x20 { + panic!("force_escape unnecessary encoding requested"); + } + write!(out, "\\u{:04x}", c).unwrap(); + } + } +} + +/// Escape a string, following RFC8259 rules. +/// +/// Only characters that require escaping will be escaped: +/// quotation mark `?`, +/// reverse solidus `\` (backslash), +/// and the control characters (`0x00-1F`). +#[inline] +pub fn escape(s: S) -> String +where + S: AsRef, +{ + escape_inner(s.as_ref()) +} + +fn escape_inner(s: &str) -> String { + let mut out = String::new(); + for c in s.chars() { + if is_safe_char(c) { + out.push(c); + } else { + force_escape(c, &mut out); + } + } + out +} + +#[cfg(test)] +mod tests { + use super::*; + + #[rustversion::attr(since(1.46), track_caller)] + fn assert_round_trip(s: &str) { + assert_eq!(s, unescape(&escape(s)).unwrap()); + } + + #[test] + fn test_round_trip() { + assert_round_trip("abc"); + assert_round_trip("\n\r\t\x08\x0C\x00"); + assert_round_trip(r#"\"#); + assert_round_trip(r#"""#); + assert_round_trip("Σ𝄞"); + assert_round_trip(r#"\𝄞"#); + assert_round_trip(r#"(╯°□°)╯︵ ┻━┻"#); + } + + #[test] + fn test_escape() { + assert_eq!(escape("\0"), r#"\u0000"#); + assert_eq!(escape("\n"), r#"\n"#); + assert_eq!(escape(r#"\"#), r#"\\"#); + assert_eq!(escape(r#"""#), r#"\""#); + assert_eq!(escape("Σ"), "Σ"); // U+03A3 + assert_eq!(escape("𝄞"), "𝄞"); // U+1D11E + } + + #[test] + fn test_unescape() { + assert_eq!(unescape(&r#"abc"#), Ok("abc".into())); + assert_eq!(unescape(&r#"ab\nc"#), Ok("ab\nc".into())); + assert_eq!(unescape(r#"ab\zc"#), Err(UnescapeError {})); + assert_eq!(unescape(r#" \"abc\" "#), Ok(" \"abc\" ".into())); + assert_eq!(unescape(r#"𝄞"#), Ok("𝄞".into())); + assert_eq!(unescape(r#"\𝄞"#), Err(UnescapeError {})); + assert_eq!(unescape(r#"\uD834\uDD1E"#), Ok("𝄞".into())); + assert_eq!(unescape(r#"\uD834"#), Err(UnescapeError {})); + assert_eq!(unescape(r#"\uDD1E"#), Err(UnescapeError {})); + assert_eq!(unescape("\t"), Err(UnescapeError {})); + } + + #[test] + fn test_generic_asref() { + assert_eq!(escape("\n"), r#"\n"#); + assert_eq!(escape(String::from("\n")), r#"\n"#); + assert_eq!(escape(&String::from("\n")), r#"\n"#); + + assert_eq!(unescape("abc"), Ok("abc".into())); + assert_eq!(unescape(String::from("abc")), Ok("abc".into())); + assert_eq!(unescape(&String::from("abc")), Ok("abc".into())); + } + + #[test] + fn test_error_impl() { + // This won't compile if UnescapeError doesn't impl Display + Error. + let e = UnescapeError {}; + let _x: Box = e.into(); + } +} diff --git a/tools/vendor/fancy-regex/.cargo-checksum.json b/tools/vendor/fancy-regex/.cargo-checksum.json new file mode 100644 index 0000000000..4991b4963d --- /dev/null +++ b/tools/vendor/fancy-regex/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"cf43644f4417a371f3b570fae27b3a6949dc254c597474a10b7ee23839965437","AUTHORS":"d5fca2653c88ad02d197faead77e22b80c70636b5bec6e02f2805f389a21b86d","CHANGELOG.md":"33e288ac35f2f0045f692c87624de90f10166e3bd0b3b311eeb7d28560a67aa1","CONTRIBUTING.md":"a7e0f3d282301aa5f4b546ed7fc5376159569987a002f24bf57082d49746fde6","Cargo.lock":"8846412681720cb85ae6a79ac3eae84ca145c7cb4a809e0bd52a3cbaed31ee31","Cargo.toml":"fe2d3062136b9c1bdacc09aaf8ad2766a4182de39732498de8bbb21c62f4384a","Cargo.toml.orig":"fc005fab26a48ff9c66f508c538a84b05cb5b5aea0968acf97daf9092687599a","LICENSE":"3bc70e239e91272782006c638fc0452a714d384224f11f0923036b7be07cf9b5","PERFORMANCE.md":"9de4fa787572943e7e350a2688002d571e986190e4869d08587048974bc2fcc6","README.md":"5321b581e5d6bea76875bc3ef93a482184fea9616b45f73781f5ec16542bdf9c","benches/bench.rs":"ec5005c0ec5b5155ae334f9204dffac98b583c377773dbe81e460ecf7404c1d0","codecov.yml":"d6ebfcadb4d940a0f86ab1a99928b673195c7d84cecee17a5eefeaf396c8477b","examples/toy.rs":"33c5f572f2afa1e0d359845708bd984bc95e272b4dcac0eb3ce55668b2a9d03b","src/analyze.rs":"0ea9e0b9661cef8c804a750459d6caf196b298dae1267d52661f1894f88c47d6","src/compile.rs":"54afdafd8004bc3cd27074e949fb1ccf0c81af0992f9c140141bea8f182ed42c","src/error.rs":"931e720cd907f7f62d41c10b69174f286c2944bb4230df8c3c1428aa75056260","src/expand.rs":"6eff8afef144363d8ebbb90922448783cf2ebfc71ef716948bd9da54793663af","src/lib.rs":"5fceb05786b2e860ec2141c8e6bfbb85014c97b4f48cc634d0842b81b0dd6477","src/parse.rs":"8991c0f8b311f404cd63bc8f2beb898055f14c2fd899ccfb86055b396a596229","src/replacer.rs":"5bf1c99d598c27248fd6591dab000e649eedc8f51179f904e00b94ac27919dc3","src/vm.rs":"8442aec0c60680576130f0ebbd6da494bba14d1141e7035f66c68f3849933731","tests/captures.rs":"0b561bbec208af0cdea12c8b10e3c72eab80ef2dbf1a9c1007fdffc7401b4434","tests/common/mod.rs":"b8ab2a7a3a25adad8317c8eb1f4566df6a81978c3a7ab49847ec2f838d77c888","tests/finding.rs":"76e60d370ae71585a304018fe0398e5206fe431fe21bbde6347cc2572afff0e5","tests/matching.rs":"3f8d1e0f6567375866b14610eda6e2dc1e96ed26ba1a451fe0c4bb2bf6f774fb","tests/oniguruma.rs":"78a54ef464d08fa7192959eb50c3de03df7dae0f9d41831b06ecfa170dfcfd65","tests/oniguruma/.gitattributes":"b8d35d419a619abf4283b4f1e8a3daeaca142c87df6634b4cd706b726d29a88d","tests/oniguruma/README.md":"eae6ac089cd32010ffacb4b2f0f8cbac03ad2623e115386af53bfc93e04d1903","tests/oniguruma/test_utf8.c":"1e4813ac64703cfdbcdca359b82856c5c6778abf977e577992f6b34ab26b92de","tests/oniguruma/test_utf8_ignore.c":"88760e1779017c58dfca5b03f382df7b067dceefcb4b52d81aef36f096b918a0","tests/regex_options.rs":"1744e707e8d76f19b999728c6b7de6e0e531342d1cd708af61ffbe74cef84a3f","tests/replace.rs":"689fef80c2cd243a510be212cb8e6a04a30a0b0fa6f1dd6be63b17a9abb158d8","tests/splitting.rs":"55a60e7953e92cf542fff8016c24607d3da4fdf9cbf68712348cc430b2bc0f3f"},"package":"6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298"} \ No newline at end of file diff --git a/tools/vendor/fancy-regex/.cargo_vcs_info.json b/tools/vendor/fancy-regex/.cargo_vcs_info.json new file mode 100644 index 0000000000..59929ecb14 --- /dev/null +++ b/tools/vendor/fancy-regex/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "810a8f3c1662ea33f8dbfb1864d31aa0d2da6c3c" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/tools/vendor/fancy-regex/AUTHORS b/tools/vendor/fancy-regex/AUTHORS new file mode 100644 index 0000000000..880837b9f7 --- /dev/null +++ b/tools/vendor/fancy-regex/AUTHORS @@ -0,0 +1,10 @@ +# This is the list of Fancy Regex authors for copyright purposes. +# +# This does not necessarily list everyone who has contributed code, since in +# some cases, their employer may be the copyright holder. To see the full list +# of contributors, see the revision history in source control. +Google LLC +Raph Levien +Robin Stocker +Keith Hall +Jon Perry \ No newline at end of file diff --git a/tools/vendor/fancy-regex/CHANGELOG.md b/tools/vendor/fancy-regex/CHANGELOG.md new file mode 100644 index 0000000000..68b0c57b3f --- /dev/null +++ b/tools/vendor/fancy-regex/CHANGELOG.md @@ -0,0 +1,206 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). +This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html), +with the exception that 0.x versions can break between minor versions. + +## [0.14.0] - 2024-10-24 +### Added +- Add `split`, `splitn` methods to `Regex` to split a string into substrings (#140) +- Add `case_insensitive` method to `RegexBuilder` to force case-insensitive mode +### Changed +- Bump bit-set dependency to 0.8 (#139) + +## [0.13.0] - 2023-12-22 +### Added +- Support for relative backreferences using `\k<-1>` (-1 references the + previous group) (#121) +- Add `try_replacen` to `Regex` which returns a `Result` instead of panicking + when matching errors (#130) +### Changed +- Switch from regex crate to regex-automata and regex-syntax (lower level APIs) + to simplify internals (#121) +- Allow escaping some letters in character classes, e.g. `[\A]` used to error + but now matches the same as `[A]` (for compatibility with Oniguruma) +- MSRV (minimum supported Rust version) is now 1.66.1 (from 1.61.0) +### Fixed +- Fix index out of bounds panic when parsing unclosed `(?(` (#125) + +## [0.12.0] - 2023-11-11 +### Added +- Support for `no_std` (the `std` feature is enabled by default but can be + disabled if desired) (#111) +- `TryFrom` `&str` and `String` impl for `Regex` (#115) +### Changed +- `Error` and its components are now `Clone` (#116) +- MSRV (minimum supported Rust version) is now 1.61.0 (from 1.42.0) + +## [0.11.0] - 2023-01-12 +### Added +- Support for [conditionals](https://www.regular-expressions.info/conditional.html): using a regex like + `(?a)?b(?(test)c|d)` will try to match `c` after `b` if `a` matched in the capture group named + `test`, otherwise `d` after `b` if `a` wasn't captured into the `test` group. +### Changed +- Updated parse errors to show the position they occurred at. +### Fixed +- Fix panic when backref is used within referenced group itself and + group end index is not known yet (#103) + +## [0.10.0] - 2022-04-28 +### Added +- Support for `\G` ([anchor to end of previous match](https://www.regular-expressions.info/continue.html)): Using a regex + like `\G\w` will match each letter of `foo` in `foo bar` but + nothing else. + +## [0.9.0] - 2022-04-21 +### Added +- Support for `\K` ([keep out](https://www.regular-expressions.info/keep.html)): Using a regex like `@\K\w+` will match + things like `@foo` but the resulting match text will only include + `foo`, keeping out the `@`. + +## [0.8.0] - 2022-02-22 +### Added +- Allow users to disable any of the `unicode` and `perf-*` features of + the regex crate. Disabling these features can reduce compile time + and/or binary size for use cases where these features are not needed. + (All features remain enabled by default.) +### Changed +- MSRV (minimum supported Rust version) is now 1.42.0 (from 1.41.1) + +## [0.7.1] - 2021-07-29 +### Fixed +- Fix panic on incomplete escape sequences in input regexes +- Disallow quantifers on lookarounds and other zero-width assertion + expressions, e.g. the `+` in `(?=hello)+` + +## [0.7.0] - 2021-07-12 +### Added +- `Regex` now has replace methods like the regex crate: + - `replace` - single replacement + - `replace_all` - replace all non-overlapping matches + - `replacen` - configurable number of replacements + +## [0.6.0] - 2021-05-17 +### Added +- `Regex` now implements `Clone`, `Display`, `FromStr` +- `Captures` now implements `Index` to access captures by number + and `Index<&str>` to access by name + +## [0.5.0] - 2021-02-15 +### Added +- Methods `find_iter` and `captures_iter` to iterate over all + non-overlapping matches for a string +- Method `find_from_pos` to `find` starting from a specific position +### Changed +- MSRV (minimum supported Rust version) is now 1.41.1 (from 1.32.0) + +## [0.4.1] - 2020-11-09 +### Added +- `escape` function to escape special characters in a string so that it + matches literally + +## [0.4.0] - 2020-09-27 +### Added +- Support for named groups and backrefs: + - Capture with `(?...)` or `(?P...)` + - Backref with `\k` or `(?P=name)` + - `Captures::name` to get matched group by name + - `Regex::capture_names` to get capture names in regex +- Support for expanding matches using a replacement template string + - `Captures::expand` for regex crate compatible syntax + - See `Expander` for python-compatible syntax and advanced usage +- `Match::range` and some `From` impls for convenience + +## [0.3.5] - 2020-04-28 +### Changed +- Include string snippet in errors for unknown group and invalid escape + to make it easier to identify the problem. + +## [0.3.4] - 2020-04-28 +### Added +- Support comments using `(?# comment)` syntax +- Support unicode escapes like `\u21D2` and `\U0001F60A` + +## [0.3.3] - 2020-02-28 +### Changed +- Optimization: Delegate const-sized suffixes in more cases +- Optimization: Use `captures_read_at` when delegating to regex crate + +## [0.3.2] - 2020-02-05 +### Fixed +- Some regexes with fancy parts in the beginning/middle didn't match + when they should have, e.g. `((?!x)(a|ab))c` didn't match `abc`. + +## [0.3.1] - 2019-12-09 +### Added +- Add `delegate_size_limit` and `delegate_dfa_size_limit` to + `RegexBuilder` to allow configuring these limits for regex crate. + +## [0.3.0] - 2019-11-27 +### Added +- Add limit for backtracking so that execution errors instead of running + for a long time in case of catastrophic backtracking. +- Add `RegexBuilder` with `backtrack_limit` to configure the new + backtrack limit per regex. +- `Error` now implements `std::error::Error` trait +### Fixed +- Fix panic in backref matching with multibyte chars + +## [0.2.0] - 2019-10-19 +### Added +- More documentation and examples +- Support character class nesting and intersections (implemented in + regex crate) +- Support atomic groups, both the the `(?>foo)` group syntax and the + `a++`, `a*+` and `a?+` possessive syntax +- Support `\b`, `\f`, `\t`, `\n`, `\r`, `\v` +- Support look-behind with variable sized alternative +- Implement `Debug` for `Regex` +- More test coverage including running one of Oniguruma's test suites +### Changed +- Change `find` to return a `Match` struct (breaking change) +- Change `Captures` API (breaking change): + - Replace `at` and `pos` with `get` that returns a `Match` struct + - Remove `is_empty` (use `len`) +- Allow unescaped `]` and `}` as literals +- Allow unescaped `{` as literal when not after atom +- Allow escapes such as `\<` or `\e` inside character classes +- Allow up to 8 characters in `\x{...}` escape +- Allow escaping of space to make literal space +- Allow `(a|)` +- Reject invalid backreferences +### Fixed +- Multiple fixes for alternatives in look-arounds +- Fix hex escape to not include letters after "F" +- Fix handling of unescaped `]` in character classes +- Fix case insensitive character classes and other escapes +- Don't ignore spaces in character classes even with "comment mode" + +## [0.1.0] - 2017-02-06 +### Added +- Initial release + + +[0.14.0]: https://github.com/fancy-regex/fancy-regex/compare/0.13.0...0.14.0 +[0.13.0]: https://github.com/fancy-regex/fancy-regex/compare/0.12.0...0.13.0 +[0.12.0]: https://github.com/fancy-regex/fancy-regex/compare/0.11.0...0.12.0 +[0.11.0]: https://github.com/fancy-regex/fancy-regex/compare/0.10.0...0.11.0 +[0.10.0]: https://github.com/fancy-regex/fancy-regex/compare/0.9.0...0.10.0 +[0.9.0]: https://github.com/fancy-regex/fancy-regex/compare/0.8.0...0.9.0 +[0.8.0]: https://github.com/fancy-regex/fancy-regex/compare/0.7.1...0.8.0 +[0.7.1]: https://github.com/fancy-regex/fancy-regex/compare/0.7.0...0.7.1 +[0.7.0]: https://github.com/fancy-regex/fancy-regex/compare/0.6.0...0.7.0 +[0.6.0]: https://github.com/fancy-regex/fancy-regex/compare/0.5.0...0.6.0 +[0.5.0]: https://github.com/fancy-regex/fancy-regex/compare/0.4.1...0.5.0 +[0.4.1]: https://github.com/fancy-regex/fancy-regex/compare/0.4.0...0.4.1 +[0.4.0]: https://github.com/fancy-regex/fancy-regex/compare/0.3.5...0.4.0 +[0.3.5]: https://github.com/fancy-regex/fancy-regex/compare/0.3.4...0.3.5 +[0.3.4]: https://github.com/fancy-regex/fancy-regex/compare/0.3.3...0.3.4 +[0.3.3]: https://github.com/fancy-regex/fancy-regex/compare/0.3.2...0.3.3 +[0.3.2]: https://github.com/fancy-regex/fancy-regex/compare/0.3.1...0.3.2 +[0.3.1]: https://github.com/fancy-regex/fancy-regex/compare/0.3.0...0.3.1 +[0.3.0]: https://github.com/fancy-regex/fancy-regex/compare/0.2.0...0.3.0 +[0.2.0]: https://github.com/fancy-regex/fancy-regex/compare/0.1.0...0.2.0 +[0.1.0]: https://github.com/fancy-regex/fancy-regex/commits/0.1.0 diff --git a/tools/vendor/fancy-regex/CONTRIBUTING.md b/tools/vendor/fancy-regex/CONTRIBUTING.md new file mode 100644 index 0000000000..7b8f3fcb2c --- /dev/null +++ b/tools/vendor/fancy-regex/CONTRIBUTING.md @@ -0,0 +1,39 @@ +# Contributing + +The fancy-regex project is committed to fostering and preserving a +diverse, welcoming community; all participants are expected to +follow the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html). + +Patching processes for this project are somewhat informal, as it's +maintained by a small group. No Contributor License Agreement is needed. + +If this is your first substantive pull request in this repo, feel free +to add yourself to the AUTHORS file. + +Make sure to run `cargo test` and `cargo fmt` to make sure your changes +pass the tests and are formatted as expected. + +When adding support for new syntax (i.e. for better compatibility with +oniguruma syntax), remember to update the "Syntax" section of the +documentation comments in `lib.rs`. + +## Manual testing + +Sometimes it's useful to manually check how regexes are matched, e.g. +to compare to other engines. + +### fancy-regex + +The toy example is useful for playing around with different regexes: + + cargo run --example toy run '[a-z]' 'input text' + +### Oniguruma + +Set up rust-onig which is based on Oniguruma: + + git clone --recurse-submodules https://github.com/rust-onig/rust-onig + +Then match some text: + + echo 'input text' | cargo run --example capturedump -- '[a-z]' diff --git a/tools/vendor/fancy-regex/Cargo.lock b/tools/vendor/fancy-regex/Cargo.lock new file mode 100644 index 0000000000..c5f64d21c6 --- /dev/null +++ b/tools/vendor/fancy-regex/Cargo.lock @@ -0,0 +1,688 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "env_logger" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "fancy-regex" +version = "0.14.0" +dependencies = [ + "bit-set", + "criterion", + "matches", + "quickcheck", + "regex", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "oorandom" +version = "11.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "proc-macro2" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quickcheck" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" +dependencies = [ + "env_logger", + "log", + "rand", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.213" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.213" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" + +[[package]] +name = "web-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/tools/vendor/fancy-regex/Cargo.toml b/tools/vendor/fancy-regex/Cargo.toml new file mode 100644 index 0000000000..a4b4597724 --- /dev/null +++ b/tools/vendor/fancy-regex/Cargo.toml @@ -0,0 +1,84 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +rust-version = "1.66" +name = "fancy-regex" +version = "0.14.0" +authors = [ + "Raph Levien ", + "Robin Stocker ", +] +exclude = [ + "/.github/*", + "/Cargo.lock.msrv", +] +description = "An implementation of regexes, supporting a relatively rich set of features, including backreferences and look-around." +documentation = "https://docs.rs/fancy-regex" +readme = "README.md" +categories = ["text-processing"] +license = "MIT" +repository = "https://github.com/fancy-regex/fancy-regex" + +[[bench]] +name = "bench" +harness = false + +[dependencies.bit-set] +version = "0.8" +default-features = false + +[dependencies.regex-automata] +version = "0.4" +features = [ + "alloc", + "syntax", + "meta", + "nfa", + "dfa", + "hybrid", +] +default-features = false + +[dependencies.regex-syntax] +version = "0.8" +default-features = false + +[dev-dependencies.criterion] +version = "0.5" + +[dev-dependencies.matches] +version = "0.1.10" + +[dev-dependencies.quickcheck] +version = "1.0" + +[dev-dependencies.regex] +version = "1.10" + +[features] +default = [ + "unicode", + "perf", + "std", +] +perf = ["regex-automata/perf"] +std = [ + "regex-automata/std", + "regex-syntax/std", + "bit-set/std", +] +track_caller = [] +unicode = [ + "regex-automata/unicode", + "regex-syntax/unicode", +] diff --git a/tools/vendor/fancy-regex/Cargo.toml.orig b/tools/vendor/fancy-regex/Cargo.toml.orig new file mode 100644 index 0000000000..7184e404f5 --- /dev/null +++ b/tools/vendor/fancy-regex/Cargo.toml.orig @@ -0,0 +1,45 @@ +[package] +name = "fancy-regex" +# remember to update html_root_url +version = "0.14.0" +authors = ["Raph Levien ", "Robin Stocker "] +edition = "2018" +license = "MIT" +description = "An implementation of regexes, supporting a relatively rich set of features, including backreferences and look-around." +readme = "README.md" +repository = "https://github.com/fancy-regex/fancy-regex" +documentation = "https://docs.rs/fancy-regex" +categories = ["text-processing"] +exclude = ["/.github/*", "/Cargo.lock.msrv"] +rust-version = "1.66" + +[features] +default = ["unicode", "perf", "std"] +# Enable #[track_caller] in unit tests. +track_caller = [] +perf = ["regex-automata/perf"] +unicode = ["regex-automata/unicode", "regex-syntax/unicode"] +std = ["regex-automata/std", "regex-syntax/std", "bit-set/std"] + +[dependencies.regex-automata] +version = "0.4" +default-features = false +features = ["alloc", "syntax", "meta", "nfa", "dfa", "hybrid"] + +[dependencies.regex-syntax] +version = "0.8" +default-features = false + +[dependencies.bit-set] +version = "0.8" +default-features = false + +[dev-dependencies] +criterion = "0.5" +matches = "0.1.10" +quickcheck = "1.0" +regex = "1.10" + +[[bench]] +name = "bench" +harness = false diff --git a/tools/vendor/fancy-regex/LICENSE b/tools/vendor/fancy-regex/LICENSE new file mode 100644 index 0000000000..92073bf466 --- /dev/null +++ b/tools/vendor/fancy-regex/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright 2015 The Fancy Regex Authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/tools/vendor/fancy-regex/PERFORMANCE.md b/tools/vendor/fancy-regex/PERFORMANCE.md new file mode 100644 index 0000000000..07f07730c7 --- /dev/null +++ b/tools/vendor/fancy-regex/PERFORMANCE.md @@ -0,0 +1,80 @@ +The README has a quick introduction to the performance of this crate. +This will look at some examples and compare them to the Oniguruma engine. + +## Catastrophic backtracking + +Backtracking engines can have worst-case performance when the regular +expression forces the engine to consider an exponentially increasing +number of sub-cases. + +For a good explanation of that, read +[Runaway Regular Expressions: Catastrophic Backtracking][]. + +Let's look at the regex from the README again: + + (a|b|ab)*bc + +And the input text: + + ababababababababababababababababababababababababababababac + +Python's engine has exponential runtime. The regex crate and fancy-regex +however have no problem with it. + +## Oniguruma + +[Oniguruma][] implements a backtracking +engine. So we'd expect it to have a problem with the above regex too. + +However, in the above case, it quickly finds that there's no match. How +is that possible? The answer is that it has optimizations which +sometimes help it avoid having to do any matching at all: + +In the pattern `(a|b|ab)*bc`, you might notice that if the input doesn't +contain `bc`, the pattern will never match. Oniguruma detects that and, +before it tries to do any matching, tries to find `bc` in the input. + +But what happens if we add `bc` at the end of the input, like this: + + ababababababababababababababababababababababababababababacbc + +Now the optimization doesn't help anymore, and Oniguruma is slow too. + +## fancy-regex + +For `(a|b|ab)*bc` fancy-regex is fast in all cases because it can +delegate to the regex crate which matches it in linear runtime. + +Let's look at another regex, one that makes use of a "fancy" look-ahead: + + (a|b|ab)*(?=c) + +When fancy-regex matches it against this input: + + abababababababababababababababababababababababababababab + +It's slow! The reason is that `(?=c)` is not supported by the regex +crate, so we need to handle it with backtracking. And because +`(a|b|ab)*` is before it, we need to do it with backtracking as well. + +Oniguruma doesn't have a problem with this particular case because its +optimization saves it again: It checks if there's a `c` in the input +before doing any matching. + +There's nothing preventing fancy-regex from adding similar optimizations +in the future, but it's not done yet. + +Note that how much fancy-regex can do without backtracking depends on +the structure of the regex. For example, with `(?=(a|b|ab)*bc)`, the +inner part of the look-ahead can be delegated to regex entirely. + +### Summary + +* If the regex doesn't use fancy features, fancy-regex should have + linear runtime compared to Oniguruma's exponential worst-case. +* Even if the regex doesn't use any fancy features, Oniguruma can be + faster because it is a mature and highly optimized engine. +* With fancy features, Oniguruma can be faster because of optimizations. + +[Runaway Regular Expressions: Catastrophic Backtracking]: https://www.regular-expressions.info/catastrophic.html +[Oniguruma]: https://github.com/kkos/oniguruma diff --git a/tools/vendor/fancy-regex/README.md b/tools/vendor/fancy-regex/README.md new file mode 100644 index 0000000000..ccbedcd7dc --- /dev/null +++ b/tools/vendor/fancy-regex/README.md @@ -0,0 +1,145 @@ +# fancy-regex + +A Rust library for compiling and matching regular expressions. It uses a hybrid +regex implementation designed to support a relatively rich set of features. +In particular, it uses backtracking to implement "fancy" features such as +look-around and backtracking, which are not supported in purely +NFA-based implementations (exemplified by +[RE2](https://github.com/google/re2), and implemented in Rust in the +[regex](https://crates.io/crates/regex) crate). + +[![docs](https://docs.rs/fancy-regex/badge.svg)](https://docs.rs/fancy-regex) +[![crate](https://img.shields.io/crates/v/fancy-regex.svg)](https://crates.io/crates/fancy-regex) +[![ci](https://github.com/fancy-regex/fancy-regex/workflows/ci/badge.svg)](https://github.com/fancy-regex/fancy-regex/actions?query=workflow%3Aci) +[![codecov](https://codecov.io/gh/fancy-regex/fancy-regex/branch/main/graph/badge.svg)](https://codecov.io/gh/fancy-regex/fancy-regex) + +A goal is to be as efficient as possible. For a given regex, the NFA +implementation has asymptotic running time linear in the length of the +input, while in the general case a backtracking implementation has +exponential blowup. An example given in [Static Analysis for Regular +Expression Exponential Runtime via Substructural +Logics](https://arxiv.org/pdf/1405.7058.pdf) is: + +```python +import re +re.compile('(a|b|ab)*bc').match('ab' * 28 + 'ac') +``` + +In Python (tested on both 2.7 and 3.5), this match takes 91s, and +doubles for each additional repeat of 'ab'. + +Thus, many proponents +[advocate](https://swtch.com/~rsc/regexp/regexp1.html) a purely NFA +(nondeterministic finite automaton) based approach. Even so, +backreferences and look-around do add richness to regexes, and they +are commonly used in applications such as syntax highlighting for text +editors. In particular, TextMate's [syntax +definitions](https://manual.macromates.com/en/language_grammars), +based on the [Oniguruma](https://github.com/kkos/oniguruma) +backtracking engine, are now used in a number of other popular +editors, including Sublime Text and Atom. These syntax definitions +routinely use backreferences and look-around. For example, the +following regex captures a single-line Rust raw string: + +``` +r(#*)".*?"\1 +``` + +There is no NFA that can express this simple and useful pattern. Yet, +a backtracking implementation handles it efficiently. + +This package is one of the first that handles both cases well. The +exponential blowup case above is run in 258ns. Thus, it should be a +very appealing alternative for applications that require both richness +and performance. + +## A warning about worst-case performance + +NFA-based approaches give strong guarantees about worst-case +performance. For regexes that contain "fancy" features such as +backreferences and look-around, this module gives no corresponding +guarantee. If an attacker can control the regular expressions that +will be matched against, they will be able to successfully mount a +denial-of-service attack. Be warned. + +See [PERFORMANCE.md](PERFORMANCE.md) for some examples. + +## A hybrid approach + +One workable approach is to detect the presence of "fancy" features, +and choose either an NFA implementation or a backtracker depending on +whether they are used. + +However, this module attempts to be more fine-grained. Instead, it +implements a true hybrid approach. In essence, it is a backtracking VM +(as well explained in [Regular Expression Matching: the Virtual +Machine Approach](https://swtch.com/~rsc/regexp/regexp2.html)) in +which one of the "instructions" in the VM delegates to an inner NFA +implementation (in Rust, the regex crate, though a similar approach +would certainly be possible using RE2 or the Go +[regexp](https://golang.org/pkg/regexp/) package). Then there's an +analysis which decides for each subexpression whether it is "hard", or +can be delegated to the NFA matcher. At the moment, it is eager, and +delegates as much as possible to the NFA engine. + +## Theory + +**(This section is written in a somewhat informal style; I hope to +expand on it)** + +The fundamental idea is that it's a backtracking VM like PCRE, but as +much as possible it delegates to an "inner" RE engine like RE2 (in +this case, the Rust one). For the sublanguage not using fancy +features, the library becomes a thin wrapper. + +Otherwise, you do an analysis to figure out what you can delegate and +what you have to backtrack. I was thinking it might be tricky, but +it's actually quite simple. The first phase, you just label each +subexpression as "hard" (groups that get referenced in a backref, +look-around, etc), and bubble that up. You also do a little extra +analysis, mostly determining whether an expression has constant match +length, and the minimum length. + +The second phase is top down, and you carry a context, also a boolean +indicating whether it's "hard" or not. Intuitively, a hard context is +one in which the match length will affect future backtracking. + +If the subexpression is easy and the context is easy, generate an +instruction in the VM that delegates to the inner NFA implementation. +Otherwise, generate VM code as in a backtracking engine. Most +expression nodes are pretty straightforward; the only interesting case +is concat (a sequence of subexpressions). + +Even that one is not terribly complex. First, determine a prefix of +easy nodes of constant match length (this won't affect backtracking, +so safe to delegate to NFA). Then, if your context is easy, determine +a suffix of easy nodes. Both of these delegate to NFA. For the ones in +between, recursively compile. In an easy context, the last of these +also gets an easy context; everything else is generated in a hard +context. So, conceptually, hard context flows from right to left, and +from parents to children. + +## Current status + +Still in development, though the basic ideas are in place. Currently, +the following features are missing: + +* Procedure calls and recursive expressions + +## Acknowledgements + +Many thanks to [Andrew Gallant](http://blog.burntsushi.net/about/) for +stimulating conversations that inspired this approach, as well as for +creating the excellent regex crate. + +## Authors + +The main author is Raph Levien, with many contributions from Robin Stocker. + +## Contributions + +We gladly accept contributions via GitHub pull requests. Please see +[CONTRIBUTING.md](CONTRIBUTING.md) for more details. + +This project started out as a Google 20% project, but none of the authors currently +work at Google so it has been forked to be community-maintained. diff --git a/tools/vendor/fancy-regex/benches/bench.rs b/tools/vendor/fancy-regex/benches/bench.rs new file mode 100644 index 0000000000..c6e7ea0d3b --- /dev/null +++ b/tools/vendor/fancy-regex/benches/bench.rs @@ -0,0 +1,115 @@ +// Copyright 2016 The Fancy Regex Authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#[macro_use] +extern crate criterion; + +use criterion::Criterion; +use std::time::Duration; + +use fancy_regex::internal::{analyze, compile, run_default}; +use fancy_regex::Expr; +use regex::Regex; + +fn parse_lifetime_re(c: &mut Criterion) { + c.bench_function("parse_lifetime_re", |b| { + b.iter(|| Expr::parse_tree("\\'[a-zA-Z_][a-zA-Z0-9_]*(?!\\')\\b").unwrap()) + }); +} + +fn parse_literal_re(c: &mut Criterion) { + c.bench_function("parse_literal_re", |b| { + b.iter(|| Expr::parse_tree("^\\\\([!-/:-@\\[-`\\{-~aftnrv]|[0-7]{1,3}|x[0-9a-fA-F]{2}|x\\{[0-9a-fA-F]{1,6}\\})").unwrap()) + }); +} + +fn parse_literal_re_regex(c: &mut Criterion) { + c.bench_function("parse_literal_re_regex", |b| { + b.iter(|| Regex::new("^\\\\([!-/:-@\\[-`\\{-~aftnrv]|[0-7]{1,3}|x[0-9a-fA-F]{2}|x\\{[0-9a-fA-F]{1,6}\\})").unwrap()) + }); +} + +fn parse_misc(c: &mut Criterion) { + c.bench_function("parse_misc", |b| { + b.iter(|| Expr::parse_tree("^\\p{L}|\\p{N}|\\s|.|\\d").unwrap()) + }); +} + +fn analyze_literal_re(c: &mut Criterion) { + let re = "^\\\\([!-/:-@\\[-`\\{-~aftnrv]|[0-7]{1,3}|x[0-9a-fA-F]{2}|x\\{[0-9a-fA-F]{1,6}\\})"; + let tree = Expr::parse_tree(re).unwrap(); + c.bench_function("analyze_literal_re", |b| b.iter(|| analyze(&tree).unwrap())); +} + +fn run_backtrack(c: &mut Criterion) { + let tree = Expr::parse_tree("^.*?(([ab]+)\\1b)").unwrap(); + let a = analyze(&tree).unwrap(); + let p = compile(&a).unwrap(); + c.bench_function("run_backtrack", |b| { + b.iter(|| { + let result = run_default(&p, "babab", 0).unwrap(); + assert_eq!(result, Some(vec![0, 5, 0, 2])); + return result; + }) + }); +} + +// The following regex is a pathological case for backtracking +// implementations, see README.md: +fn run_tricky(c: &mut Criterion) { + let tree = Expr::parse_tree("(a|b|ab)*bc").unwrap(); + let a = analyze(&tree).unwrap(); + let p = compile(&a).unwrap(); + let mut s = String::new(); + for _ in 0..28 { + s.push_str("ab"); + } + s.push_str("ac"); + c.bench_function("run_tricky", |b| b.iter(|| run_default(&p, &s, 0).unwrap())); +} + +fn run_backtrack_limit(c: &mut Criterion) { + let tree = Expr::parse_tree("(?i)(a|b|ab)*(?=c)").unwrap(); + let a = analyze(&tree).unwrap(); + let p = compile(&a).unwrap(); + let s = "abababababababababababababababababababababababababababab"; + c.bench_function("run_backtrack_limit", |b| { + b.iter(|| run_default(&p, &s, 0).unwrap_err()) + }); +} + +criterion_group!( + name = benches; + config = Criterion::default().warm_up_time(Duration::from_secs(10)); + targets = parse_lifetime_re, + parse_literal_re, + parse_literal_re_regex, + parse_misc, + analyze_literal_re, + run_backtrack, + run_tricky, +); +criterion_group!( + name = slow_benches; + config = Criterion::default().sample_size(10); + targets = run_backtrack_limit, +); + +criterion_main!(benches, slow_benches); diff --git a/tools/vendor/fancy-regex/codecov.yml b/tools/vendor/fancy-regex/codecov.yml new file mode 100644 index 0000000000..081431e7bc --- /dev/null +++ b/tools/vendor/fancy-regex/codecov.yml @@ -0,0 +1,2 @@ +# Make codecov not add verbose comments to pull requests +comment: false diff --git a/tools/vendor/fancy-regex/examples/toy.rs b/tools/vendor/fancy-regex/examples/toy.rs new file mode 100644 index 0000000000..7dacff55d0 --- /dev/null +++ b/tools/vendor/fancy-regex/examples/toy.rs @@ -0,0 +1,126 @@ +// Copyright 2016 The Fancy Regex Authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//! A simple test app for exercising and debugging the regex engine. + +use fancy_regex::internal::{analyze, compile, run_trace, Insn, Prog}; +use fancy_regex::*; +use std::env; +use std::str::FromStr; + +fn main() { + let mut args = env::args().skip(1); + if let Some(cmd) = args.next() { + if cmd == "parse" { + if let Some(re) = args.next() { + let e = Expr::parse_tree(&re); + println!("{:#?}", e); + } + } else if cmd == "analyze" { + if let Some(re) = args.next() { + let tree = Expr::parse_tree(&re).unwrap(); + let a = analyze(&tree); + println!("{:#?}", a); + } + } else if cmd == "compile" { + if let Some(re) = args.next() { + let r = Regex::new(&re).unwrap(); + r.debug_print(); + } + } else if cmd == "run" { + let re = args.next().expect("expected regexp argument"); + let r = Regex::new(&re).unwrap(); + let text = args.next().expect("expected text argument"); + let mut pos = 0; + if let Some(pos_str) = args.next() { + pos = usize::from_str(&pos_str).unwrap(); + } + if let Some(caps) = r.captures_from_pos(&text, pos).unwrap() { + print!("captures:"); + for i in 0..caps.len() { + print!(" {}:", i); + if let Some(m) = caps.get(i) { + print!("[{}..{}] \"{}\"", m.start(), m.end(), m.as_str()); + } else { + print!("_"); + } + } + println!(""); + for cap in caps.iter() { + println!("iterate {:?}", cap); + } + } else { + println!("no match"); + } + } else if cmd == "trace" { + if let Some(re) = args.next() { + let prog = prog(&re); + if let Some(s) = args.next() { + run_trace(&prog, &s, 0).unwrap(); + } + } + } else if cmd == "trace-inner" { + if let Some(re) = args.next() { + let tree = Expr::parse_tree(&re).unwrap(); + let a = analyze(&tree).unwrap(); + let p = compile(&a).unwrap(); + if let Some(s) = args.next() { + run_trace(&p, &s, 0).unwrap(); + } + } + } else if cmd == "graph" { + let re = args.next().expect("expected regexp argument"); + graph(&re); + } else { + println!("commands: parse|analyze|compile|graph , run|trace|trace-inner "); + } + } +} + +fn graph(re: &str) { + let prog = prog(re); + println!("digraph G {{"); + for (i, insn) in prog.body.iter().enumerate() { + let label = format!("{:?}", insn) + .replace(r#"\"#, r#"\\"#) + .replace(r#"""#, r#"\""#); + println!(r#"{:3} [label="{}: {}"];"#, i, i, label); + match *insn { + Insn::Split(a, b) => { + println!("{:3} -> {};", i, a); + println!("{:3} -> {};", i, b); + } + Insn::Jmp(target) => { + println!("{:3} -> {};", i, target); + } + Insn::End => {} + _ => { + println!("{:3} -> {};", i, i + 1); + } + } + } + println!("}}"); +} + +fn prog(re: &str) -> Prog { + let tree = Expr::parse_tree(re).expect("Expected parsing regex to work"); + let result = analyze(&tree).expect("Expected analyze to succeed"); + compile(&result).expect("Expected compile to succeed") +} diff --git a/tools/vendor/fancy-regex/src/analyze.rs b/tools/vendor/fancy-regex/src/analyze.rs new file mode 100644 index 0000000000..c4f700bd7f --- /dev/null +++ b/tools/vendor/fancy-regex/src/analyze.rs @@ -0,0 +1,282 @@ +// Copyright 2016 The Fancy Regex Authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//! Analysis of regex expressions. + +use alloc::string::String; +use alloc::vec::Vec; +use core::cmp::min; + +use bit_set::BitSet; + +use crate::parse::ExprTree; +use crate::{CompileError, Error, Expr, Result}; + +#[derive(Debug)] +pub struct Info<'a> { + pub(crate) start_group: usize, + pub(crate) end_group: usize, + pub(crate) min_size: usize, + pub(crate) const_size: bool, + pub(crate) hard: bool, + pub(crate) expr: &'a Expr, + pub(crate) children: Vec>, +} + +impl<'a> Info<'a> { + pub(crate) fn is_literal(&self) -> bool { + match *self.expr { + Expr::Literal { casei, .. } => !casei, + Expr::Concat(_) => self.children.iter().all(|child| child.is_literal()), + _ => false, + } + } + + pub(crate) fn push_literal(&self, buf: &mut String) { + match *self.expr { + // could be more paranoid about checking casei + Expr::Literal { ref val, .. } => buf.push_str(val), + Expr::Concat(_) => { + for child in &self.children { + child.push_literal(buf); + } + } + _ => panic!("push_literal called on non-literal"), + } + } +} + +struct Analyzer<'a> { + backrefs: &'a BitSet, + group_ix: usize, +} + +impl<'a> Analyzer<'a> { + fn visit(&mut self, expr: &'a Expr) -> Result> { + let start_group = self.group_ix; + let mut children = Vec::new(); + let mut min_size = 0; + let mut const_size = false; + let mut hard = false; + match *expr { + Expr::Assertion(assertion) if assertion.is_hard() => { + const_size = true; + hard = true; + } + Expr::Empty | Expr::Assertion(_) => { + const_size = true; + } + Expr::Any { .. } => { + min_size = 1; + const_size = true; + } + Expr::Literal { ref val, casei } => { + // right now each character in a literal gets its own node, that might change + min_size = 1; + const_size = literal_const_size(val, casei); + } + Expr::Concat(ref v) => { + const_size = true; + for child in v { + let child_info = self.visit(child)?; + min_size += child_info.min_size; + const_size &= child_info.const_size; + hard |= child_info.hard; + children.push(child_info); + } + } + Expr::Alt(ref v) => { + let child_info = self.visit(&v[0])?; + min_size = child_info.min_size; + const_size = child_info.const_size; + hard = child_info.hard; + children.push(child_info); + for child in &v[1..] { + let child_info = self.visit(child)?; + const_size &= child_info.const_size && min_size == child_info.min_size; + min_size = min(min_size, child_info.min_size); + hard |= child_info.hard; + children.push(child_info); + } + } + Expr::Group(ref child) => { + let group = self.group_ix; + self.group_ix += 1; + let child_info = self.visit(child)?; + min_size = child_info.min_size; + const_size = child_info.const_size; + // If there's a backref to this group, we potentially have to backtrack within the + // group. E.g. with `(x|xy)\1` and input `xyxy`, `x` matches but then the backref + // doesn't, so we have to backtrack and try `xy`. + hard = child_info.hard | self.backrefs.contains(group); + children.push(child_info); + } + Expr::LookAround(ref child, _) => { + let child_info = self.visit(child)?; + // min_size = 0 + const_size = true; + hard = true; + children.push(child_info); + } + Expr::Repeat { + ref child, lo, hi, .. + } => { + let child_info = self.visit(child)?; + min_size = child_info.min_size * lo; + const_size = child_info.const_size && lo == hi; + hard = child_info.hard; + children.push(child_info); + } + Expr::Delegate { size, .. } => { + // currently only used for empty and single-char matches + min_size = size; + const_size = true; + } + Expr::Backref(group) => { + if group >= self.group_ix { + return Err(Error::CompileError(CompileError::InvalidBackref)); + } + hard = true; + } + Expr::AtomicGroup(ref child) => { + let child_info = self.visit(child)?; + min_size = child_info.min_size; + const_size = child_info.const_size; + hard = true; // TODO: possibly could weaken + children.push(child_info); + } + Expr::KeepOut => { + hard = true; + const_size = true; + } + Expr::ContinueFromPreviousMatchEnd => { + hard = true; + const_size = true; + } + Expr::BackrefExistsCondition(group) => { + if group >= self.group_ix { + return Err(Error::CompileError(CompileError::InvalidBackref)); + } + hard = true; + const_size = true; + } + Expr::Conditional { + ref condition, + ref true_branch, + ref false_branch, + } => { + hard = true; + + let child_info_condition = self.visit(condition)?; + let child_info_truth = self.visit(true_branch)?; + let child_info_false = self.visit(false_branch)?; + + min_size = child_info_condition.min_size + + min(child_info_truth.min_size, child_info_false.min_size); + const_size = child_info_condition.const_size + && child_info_truth.const_size + && child_info_false.const_size + // if the condition's size plus the truth branch's size is equal to the false branch's size then it's const size + && child_info_condition.min_size + child_info_truth.min_size == child_info_false.min_size; + + children.push(child_info_condition); + children.push(child_info_truth); + children.push(child_info_false); + } + }; + + Ok(Info { + expr, + children, + start_group, + end_group: self.group_ix, + min_size, + const_size, + hard, + }) + } +} + +fn literal_const_size(_: &str, _: bool) -> bool { + // Right now, regex doesn't do sophisticated case folding, + // test below will fail when that changes, then we need to + // do something fancier here. + true +} + +/// Analyze the parsed expression to determine whether it requires fancy features. +pub fn analyze<'a>(tree: &'a ExprTree) -> Result> { + let mut analyzer = Analyzer { + backrefs: &tree.backrefs, + group_ix: 0, + }; + + analyzer.visit(&tree.expr) +} + +#[cfg(test)] +mod tests { + use super::analyze; + // use super::literal_const_size; + use crate::Expr; + + // #[test] + // fn case_folding_safe() { + // let re = regex::Regex::new("(?i:ß)").unwrap(); + // if re.is_match("SS") { + // assert!(!literal_const_size("ß", true)); + // } + + // // Another tricky example, Armenian ECH YIWN + // let re = regex::Regex::new("(?i:\\x{0587})").unwrap(); + // if re.is_match("\u{0565}\u{0582}") { + // assert!(!literal_const_size("\u{0587}", true)); + // } + // } + + #[test] + fn invalid_backref_1() { + assert!(analyze(&Expr::parse_tree(".\\0").unwrap()).is_err()); + } + + #[test] + fn invalid_backref_2() { + assert!(analyze(&Expr::parse_tree("(.\\1)").unwrap()).is_err()); + } + + #[test] + fn invalid_backref_3() { + assert!(analyze(&Expr::parse_tree("\\1(.)").unwrap()).is_err()); + } + + #[test] + fn is_literal() { + let tree = Expr::parse_tree("abc").unwrap(); + let info = analyze(&tree).unwrap(); + assert_eq!(info.is_literal(), true); + } + + #[test] + fn is_literal_with_repeat() { + let tree = Expr::parse_tree("abc*").unwrap(); + let info = analyze(&tree).unwrap(); + assert_eq!(info.is_literal(), false); + } +} diff --git a/tools/vendor/fancy-regex/src/compile.rs b/tools/vendor/fancy-regex/src/compile.rs new file mode 100644 index 0000000000..bc63293c48 --- /dev/null +++ b/tools/vendor/fancy-regex/src/compile.rs @@ -0,0 +1,733 @@ +// Copyright 2016 The Fancy Regex Authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//! Compilation of regexes to VM. + +use alloc::string::String; +use alloc::vec::Vec; +use core::usize; +use regex_automata::meta::Regex as RaRegex; +use regex_automata::meta::{Builder as RaBuilder, Config as RaConfig}; +#[cfg(all(test, feature = "std"))] +use std::{collections::BTreeMap, sync::RwLock}; + +use crate::analyze::Info; +use crate::vm::{Insn, Prog}; +use crate::LookAround::*; +use crate::{CompileError, Error, Expr, LookAround, RegexOptions, Result}; + +// I'm thinking it probably doesn't make a lot of sense having this split +// out from Compiler. +struct VMBuilder { + prog: Vec, + n_saves: usize, +} + +impl VMBuilder { + fn new(max_group: usize) -> VMBuilder { + VMBuilder { + prog: Vec::new(), + n_saves: max_group * 2, + } + } + + fn build(self) -> Prog { + Prog::new(self.prog, self.n_saves) + } + + fn newsave(&mut self) -> usize { + let result = self.n_saves; + self.n_saves += 1; + result + } + + fn pc(&self) -> usize { + self.prog.len() + } + + // would "emit" be a better name? + fn add(&mut self, insn: Insn) { + self.prog.push(insn); + } + + fn set_jmp_target(&mut self, jmp_pc: usize, target: usize) { + match self.prog[jmp_pc] { + Insn::Jmp(ref mut next) => *next = target, + _ => panic!("mutating instruction other than Jmp"), + } + } + + fn set_split_target(&mut self, split_pc: usize, target: usize, second: bool) { + match self.prog[split_pc] { + Insn::Split(_, ref mut y) if second => *y = target, + Insn::Split(ref mut x, _) => *x = target, + _ => panic!("mutating instruction other than Split"), + } + } + + fn set_repeat_target(&mut self, repeat_pc: usize, target: usize) { + match self.prog[repeat_pc] { + Insn::RepeatGr { ref mut next, .. } + | Insn::RepeatNg { ref mut next, .. } + | Insn::RepeatEpsilonGr { ref mut next, .. } + | Insn::RepeatEpsilonNg { ref mut next, .. } => *next = target, + _ => panic!("mutating instruction other than Repeat"), + } + } +} + +struct Compiler { + b: VMBuilder, + options: RegexOptions, +} + +impl Compiler { + fn new(max_group: usize) -> Compiler { + Compiler { + b: VMBuilder::new(max_group), + options: Default::default(), + } + } + + fn visit(&mut self, info: &Info<'_>, hard: bool) -> Result<()> { + if !hard && !info.hard { + // easy case, delegate entire subexpr + return self.compile_delegate(info); + } + match *info.expr { + Expr::Empty => (), + Expr::Literal { ref val, casei } => { + if !casei { + self.b.add(Insn::Lit(val.clone())); + } else { + self.compile_delegate(info)?; + } + } + Expr::Any { newline: true } => { + self.b.add(Insn::Any); + } + Expr::Any { newline: false } => { + self.b.add(Insn::AnyNoNL); + } + Expr::Concat(_) => { + self.compile_concat(info, hard)?; + } + Expr::Alt(_) => { + let count = info.children.len(); + self.compile_alt(count, |compiler, i| compiler.visit(&info.children[i], hard))?; + } + Expr::Group(_) => { + let group = info.start_group; + self.b.add(Insn::Save(group * 2)); + self.visit(&info.children[0], hard)?; + self.b.add(Insn::Save(group * 2 + 1)); + } + Expr::Repeat { lo, hi, greedy, .. } => { + self.compile_repeat(info, lo, hi, greedy, hard)?; + } + Expr::LookAround(_, la) => { + self.compile_lookaround(info, la)?; + } + Expr::Backref(group) => { + self.b.add(Insn::Backref(group * 2)); + } + Expr::BackrefExistsCondition(group) => { + self.b.add(Insn::BackrefExistsCondition(group)); + } + Expr::AtomicGroup(_) => { + // TODO optimization: atomic insns are not needed if the + // child doesn't do any backtracking. + self.b.add(Insn::BeginAtomic); + self.visit(&info.children[0], false)?; + self.b.add(Insn::EndAtomic); + } + Expr::Delegate { .. } => { + // TODO: might want to have more specialized impls + self.compile_delegate(info)?; + } + Expr::Assertion(assertion) => { + self.b.add(Insn::Assertion(assertion)); + } + Expr::KeepOut => { + self.b.add(Insn::Save(0)); + } + Expr::ContinueFromPreviousMatchEnd => { + self.b.add(Insn::ContinueFromPreviousMatchEnd); + } + Expr::Conditional { .. } => { + self.compile_conditional(|compiler, i| compiler.visit(&info.children[i], hard))?; + } + } + Ok(()) + } + + fn compile_alt(&mut self, count: usize, mut handle_alternative: F) -> Result<()> + where + F: FnMut(&mut Compiler, usize) -> Result<()>, + { + let mut jmps = Vec::new(); + let mut last_pc = usize::MAX; + for i in 0..count { + let has_next = i != count - 1; + let pc = self.b.pc(); + if has_next { + self.b.add(Insn::Split(pc + 1, usize::MAX)); + } + if last_pc != usize::MAX { + self.b.set_split_target(last_pc, pc, true); + } + last_pc = pc; + + handle_alternative(self, i)?; + + if has_next { + // All except the last branch need to jump over instructions of + // other branches. The last branch can just continue to the next + // instruction. + let pc = self.b.pc(); + jmps.push(pc); + self.b.add(Insn::Jmp(0)); + } + } + let next_pc = self.b.pc(); + for jmp_pc in jmps { + self.b.set_jmp_target(jmp_pc, next_pc); + } + Ok(()) + } + + fn compile_conditional(&mut self, mut handle_child: F) -> Result<()> + where + F: FnMut(&mut Compiler, usize) -> Result<()>, + { + // here we use atomic group functionality to be able to remove the program counter + // relating to the split instruction's second position if the conditional succeeds + // This is to ensure that if the condition succeeds, but the "true" branch from the + // conditional fails, that it wouldn't jump to the "false" branch. + self.b.add(Insn::BeginAtomic); + + let split_pc = self.b.pc(); + // add the split instruction - we will update it's second pc later + self.b.add(Insn::Split(split_pc + 1, usize::MAX)); + + // add the conditional expression + handle_child(self, 0)?; + + // mark it as successful to remove the state we added as a split earlier + self.b.add(Insn::EndAtomic); + + // add the truth branch + handle_child(self, 1)?; + // add an instruction to jump over the false branch - we will update the jump target later + let jump_over_false_pc = self.b.pc(); + self.b.add(Insn::Jmp(0)); + + // add the false branch, update the split target + self.b.set_split_target(split_pc, self.b.pc(), true); + handle_child(self, 2)?; + + // update the jump target for jumping over the false branch + self.b.set_jmp_target(jump_over_false_pc, self.b.pc()); + + Ok(()) + } + + fn compile_concat(&mut self, info: &Info<'_>, hard: bool) -> Result<()> { + // First: determine a prefix which is constant size and not hard. + let prefix_end = info + .children + .iter() + .take_while(|c| c.const_size && !c.hard) + .count(); + + // If incoming difficulty is not hard, the suffix after the last + // hard child can be done with NFA. + let suffix_len = if !hard { + info.children[prefix_end..] + .iter() + .rev() + .take_while(|c| !c.hard) + .count() + } else { + // Even for hard, we can delegate a const-sized suffix + info.children[prefix_end..] + .iter() + .rev() + .take_while(|c| c.const_size && !c.hard) + .count() + }; + let suffix_begin = info.children.len() - suffix_len; + + self.compile_delegates(&info.children[..prefix_end])?; + + for child in info.children[prefix_end..suffix_begin].iter() { + self.visit(child, true)?; + } + + self.compile_delegates(&info.children[suffix_begin..]) + } + + fn compile_repeat( + &mut self, + info: &Info<'_>, + lo: usize, + hi: usize, + greedy: bool, + hard: bool, + ) -> Result<()> { + let child = &info.children[0]; + if lo == 0 && hi == 1 { + // e? + let pc = self.b.pc(); + self.b.add(Insn::Split(pc + 1, pc + 1)); + // TODO: do we want to do an epsilon check here? If we do + // it here and in Alt, we might be able to make a good + // bound on stack depth + self.visit(child, hard)?; + let next_pc = self.b.pc(); + self.b.set_split_target(pc, next_pc, greedy); + return Ok(()); + } + let hard = hard | info.hard; + if hi == usize::MAX && child.min_size == 0 { + // Use RepeatEpsilon instructions to prevent empty repeat + let repeat = self.b.newsave(); + let check = self.b.newsave(); + self.b.add(Insn::Save0(repeat)); + let pc = self.b.pc(); + if greedy { + self.b.add(Insn::RepeatEpsilonGr { + lo, + next: usize::MAX, + repeat, + check, + }); + } else { + self.b.add(Insn::RepeatEpsilonNg { + lo, + next: usize::MAX, + repeat, + check, + }); + } + self.visit(child, hard)?; + self.b.add(Insn::Jmp(pc)); + let next_pc = self.b.pc(); + self.b.set_repeat_target(pc, next_pc); + } else if lo == 0 && hi == usize::MAX { + // e* + let pc = self.b.pc(); + self.b.add(Insn::Split(pc + 1, pc + 1)); + self.visit(child, hard)?; + self.b.add(Insn::Jmp(pc)); + let next_pc = self.b.pc(); + self.b.set_split_target(pc, next_pc, greedy); + } else if lo == 1 && hi == usize::MAX { + // e+ + let pc = self.b.pc(); + self.visit(child, hard)?; + let next = self.b.pc() + 1; + let (x, y) = if greedy { (pc, next) } else { (next, pc) }; + self.b.add(Insn::Split(x, y)); + } else { + let repeat = self.b.newsave(); + self.b.add(Insn::Save0(repeat)); + let pc = self.b.pc(); + if greedy { + self.b.add(Insn::RepeatGr { + lo, + hi, + next: usize::MAX, + repeat, + }); + } else { + self.b.add(Insn::RepeatNg { + lo, + hi, + next: usize::MAX, + repeat, + }); + } + self.visit(child, hard)?; + self.b.add(Insn::Jmp(pc)); + let next_pc = self.b.pc(); + self.b.set_repeat_target(pc, next_pc); + } + Ok(()) + } + + fn compile_lookaround(&mut self, info: &Info<'_>, la: LookAround) -> Result<()> { + let inner = &info.children[0]; + match la { + LookBehind => { + if let Info { + const_size: false, + expr: &Expr::Alt(_), + .. + } = inner + { + // Make const size by transforming `(?<=a|bb)` to `(?<=a)|(?<=bb)` + let alternatives = &inner.children; + self.compile_alt(alternatives.len(), |compiler, i| { + let alternative = &alternatives[i]; + compiler.compile_positive_lookaround(alternative, la) + }) + } else { + self.compile_positive_lookaround(inner, la) + } + } + LookBehindNeg => { + if let Info { + const_size: false, + expr: &Expr::Alt(_), + .. + } = inner + { + // Make const size by transforming `(? self.compile_positive_lookaround(inner, la), + LookAheadNeg => self.compile_negative_lookaround(inner, la), + } + } + + fn compile_positive_lookaround(&mut self, inner: &Info<'_>, la: LookAround) -> Result<()> { + let save = self.b.newsave(); + self.b.add(Insn::Save(save)); + self.compile_lookaround_inner(inner, la)?; + self.b.add(Insn::Restore(save)); + Ok(()) + } + + fn compile_negative_lookaround(&mut self, inner: &Info<'_>, la: LookAround) -> Result<()> { + let pc = self.b.pc(); + self.b.add(Insn::Split(pc + 1, usize::MAX)); + self.compile_lookaround_inner(inner, la)?; + self.b.add(Insn::FailNegativeLookAround); + let next_pc = self.b.pc(); + self.b.set_split_target(pc, next_pc, true); + Ok(()) + } + + fn compile_lookaround_inner(&mut self, inner: &Info<'_>, la: LookAround) -> Result<()> { + if la == LookBehind || la == LookBehindNeg { + if !inner.const_size { + return Err(Error::CompileError(CompileError::LookBehindNotConst)); + } + self.b.add(Insn::GoBack(inner.min_size)); + } + self.visit(inner, false) + } + + fn compile_delegates(&mut self, infos: &[Info<'_>]) -> Result<()> { + if infos.is_empty() { + return Ok(()); + } + // TODO: might want to do something similar for case insensitive literals + // (have is_literal return an additional bool for casei) + if infos.iter().all(|e| e.is_literal()) { + let mut val = String::new(); + for info in infos { + info.push_literal(&mut val); + } + self.b.add(Insn::Lit(val)); + return Ok(()); + } + + let mut delegate_builder = DelegateBuilder::new(); + for info in infos { + delegate_builder.push(info); + } + let delegate = delegate_builder.build(&self.options)?; + + self.b.add(delegate); + Ok(()) + } + + fn compile_delegate(&mut self, info: &Info) -> Result<()> { + let insn = if info.is_literal() { + let mut val = String::new(); + info.push_literal(&mut val); + Insn::Lit(val) + } else { + DelegateBuilder::new().push(info).build(&self.options)? + }; + self.b.add(insn); + Ok(()) + } +} + +// Unlike Regex in `regex`, `regex-automata` does not store the pattern string, +// and we cannot retrieve the pattern string using `as_str`. +// Unfortunately we need to get the pattern string in our tests, +// so we just store it in a global map. +#[cfg(all(test, feature = "std"))] +static PATTERN_MAPPING: RwLock> = RwLock::new(BTreeMap::new()); + +pub(crate) fn compile_inner(inner_re: &str, options: &RegexOptions) -> Result { + let mut config = RaConfig::new(); + if let Some(size_limit) = options.delegate_size_limit { + config = config.nfa_size_limit(Some(size_limit)); + } + if let Some(dfa_size_limit) = options.delegate_dfa_size_limit { + config = config.dfa_size_limit(Some(dfa_size_limit)); + } + + let re = RaBuilder::new() + .configure(config) + .syntax(options.syntaxc) + .build(inner_re) + .map_err(CompileError::InnerError) + .map_err(Error::CompileError)?; + + #[cfg(all(test, feature = "std"))] + PATTERN_MAPPING + .write() + .unwrap() + .insert(format!("{:?}", re), inner_re.to_owned()); + + Ok(re) +} + +/// Compile the analyzed expressions into a program. +pub fn compile(info: &Info<'_>) -> Result { + let mut c = Compiler::new(info.end_group); + c.visit(info, false)?; + c.b.add(Insn::End); + Ok(c.b.build()) +} + +struct DelegateBuilder { + re: String, + min_size: usize, + const_size: bool, + start_group: Option, + end_group: usize, +} + +impl DelegateBuilder { + fn new() -> Self { + Self { + re: String::new(), + min_size: 0, + const_size: true, + start_group: None, + end_group: 0, + } + } + + fn push(&mut self, info: &Info<'_>) -> &mut DelegateBuilder { + // TODO: might want to detect case of a group with no captures + // inside, so we can run find() instead of captures() + + self.min_size += info.min_size; + self.const_size &= info.const_size; + if self.start_group.is_none() { + self.start_group = Some(info.start_group); + } + self.end_group = info.end_group; + + // Add expression. The precedence argument has to be 1 here to + // ensure correct grouping in these cases: + // + // If we have multiple expressions, we are building a concat. + // Without grouping, we'd turn ["a", "b|c"] into "^ab|c". But we + // want "^a(?:b|c)". + // + // Even with a single expression, because we add `^` at the + // beginning, we need a group. Otherwise `["a|b"]` would be turned + // into `"^a|b"` instead of `"^(?:a|b)"`. + info.expr.to_str(&mut self.re, 1); + self + } + + fn build(&self, options: &RegexOptions) -> Result { + let start_group = self.start_group.expect("Expected at least one expression"); + let end_group = self.end_group; + + let compiled = compile_inner(&self.re, options)?; + + Ok(Insn::Delegate { + inner: compiled, + start_group, + end_group, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::analyze::analyze; + use crate::parse::ExprTree; + use crate::vm::Insn::*; + use alloc::vec; + use bit_set::BitSet; + use matches::assert_matches; + + #[test] + fn jumps_for_alternation() { + let tree = ExprTree { + expr: Expr::Alt(vec![ + Expr::Literal { + val: "a".into(), + casei: false, + }, + Expr::Literal { + val: "b".into(), + casei: false, + }, + Expr::Literal { + val: "c".into(), + casei: false, + }, + ]), + backrefs: BitSet::new(), + named_groups: Default::default(), + }; + let info = analyze(&tree).unwrap(); + + let mut c = Compiler::new(0); + // Force "hard" so that compiler doesn't just delegate + c.visit(&info, true).unwrap(); + c.b.add(Insn::End); + + let prog = c.b.prog; + + assert_eq!(prog.len(), 8, "prog: {:?}", prog); + assert_matches!(prog[0], Split(1, 3)); + assert_matches!(prog[1], Lit(ref l) if l == "a"); + assert_matches!(prog[2], Jmp(7)); + assert_matches!(prog[3], Split(4, 6)); + assert_matches!(prog[4], Lit(ref l) if l == "b"); + assert_matches!(prog[5], Jmp(7)); + assert_matches!(prog[6], Lit(ref l) if l == "c"); + assert_matches!(prog[7], End); + } + + #[cfg_attr(not(feature = "std"), ignore = "this test need std")] + #[test] + fn look_around_pattern_can_be_delegated() { + let prog = compile_prog("(?=ab*)c"); + + assert_eq!(prog.len(), 5, "prog: {:?}", prog); + assert_matches!(prog[0], Save(0)); + assert_delegate(&prog[1], "ab*"); + assert_matches!(prog[2], Restore(0)); + assert_matches!(prog[3], Lit(ref l) if l == "c"); + assert_matches!(prog[4], End); + } + + #[cfg_attr(not(feature = "std"), ignore = "this test need std")] + #[test] + fn easy_concat_can_delegate_end() { + let prog = compile_prog("(?!x)(?:a|ab)x*"); + + assert_eq!(prog.len(), 5, "prog: {:?}", prog); + assert_matches!(prog[0], Split(1, 3)); + assert_matches!(prog[1], Lit(ref l) if l == "x"); + assert_matches!(prog[2], FailNegativeLookAround); + assert_delegate(&prog[3], "(?:a|ab)x*"); + assert_matches!(prog[4], End); + } + + #[cfg_attr(not(feature = "std"), ignore = "this test need std")] + #[test] + fn hard_concat_can_delegate_const_size_end() { + let prog = compile_prog("(?:(?!x)(?:a|b)c)x*"); + + assert_eq!(prog.len(), 6, "prog: {:?}", prog); + assert_matches!(prog[0], Split(1, 3)); + assert_matches!(prog[1], Lit(ref l) if l == "x"); + assert_matches!(prog[2], FailNegativeLookAround); + assert_delegate(&prog[3], "(?:a|b)c"); + assert_delegate(&prog[4], "x*"); + assert_matches!(prog[5], End); + } + + #[cfg_attr(not(feature = "std"), ignore = "this test need std")] + #[test] + fn hard_concat_can_not_delegate_variable_end() { + let prog = compile_prog("(?:(?!x)(?:a|ab))x*"); + + assert_eq!(prog.len(), 9, "prog: {:?}", prog); + assert_matches!(prog[0], Split(1, 3)); + assert_matches!(prog[1], Lit(ref l) if l == "x"); + assert_matches!(prog[2], FailNegativeLookAround); + assert_matches!(prog[3], Split(4, 6)); + assert_matches!(prog[4], Lit(ref l) if l == "a"); + assert_matches!(prog[5], Jmp(7)); + assert_matches!(prog[6], Lit(ref l) if l == "ab"); + assert_delegate(&prog[7], "x*"); + assert_matches!(prog[8], End); + } + + #[test] + fn conditional_expression_can_be_compiled() { + let prog = compile_prog(r"(?(ab)c|d)"); + + assert_eq!(prog.len(), 8, "prog: {:?}", prog); + + assert_matches!(prog[0], BeginAtomic); + assert_matches!(prog[1], Split(2, 6)); + assert_matches!(prog[2], Lit(ref l) if l == "ab"); + assert_matches!(prog[3], EndAtomic); + assert_matches!(prog[4], Lit(ref l) if l == "c"); + assert_matches!(prog[5], Jmp(7)); + assert_matches!(prog[6], Lit(ref l) if l == "d"); + assert_matches!(prog[7], End); + } + + fn compile_prog(re: &str) -> Vec { + let tree = Expr::parse_tree(re).unwrap(); + let info = analyze(&tree).unwrap(); + let prog = compile(&info).unwrap(); + prog.body + } + + #[cfg(feature = "std")] + fn assert_delegate(insn: &Insn, re: &str) { + match insn { + Insn::Delegate { inner, .. } => { + assert_eq!( + PATTERN_MAPPING + .read() + .unwrap() + .get(&alloc::format!("{:?}", inner)) + .unwrap(), + re + ); + } + _ => { + panic!("Expected Insn::Delegate but was {:#?}", insn); + } + } + } + + #[cfg(not(feature = "std"))] + fn assert_delegate(_: &Insn, _: &str) {} +} diff --git a/tools/vendor/fancy-regex/src/error.rs b/tools/vendor/fancy-regex/src/error.rs new file mode 100644 index 0000000000..9acba42865 --- /dev/null +++ b/tools/vendor/fancy-regex/src/error.rs @@ -0,0 +1,168 @@ +use alloc::string::String; +use core::fmt; +use regex_automata::meta::BuildError as RaBuildError; + +/// Result type for this crate with specific error enum. +pub type Result = ::core::result::Result; + +pub type ParseErrorPosition = usize; + +/// An error as the result of parsing, compiling or running a regex. +#[derive(Clone, Debug)] +#[non_exhaustive] +pub enum Error { + /// An error as a result of parsing a regex pattern, with the position where the error occurred + ParseError(ParseErrorPosition, ParseError), + /// An error as a result of compiling a regex + CompileError(CompileError), + /// An error as a result of running a regex + RuntimeError(RuntimeError), +} + +/// An error for the result of parsing a regex pattern. +#[derive(Clone, Debug)] +#[non_exhaustive] +pub enum ParseError { + /// General parsing error + GeneralParseError(String), + /// Opening parenthesis without closing parenthesis, e.g. `(a|b` + UnclosedOpenParen, + /// Invalid repeat syntax + InvalidRepeat, + /// Pattern too deeply nested + RecursionExceeded, + /// Backslash without following character + TrailingBackslash, + /// Invalid escape + InvalidEscape(String), + /// Unicode escape not closed + UnclosedUnicodeName, + /// Invalid hex escape + InvalidHex, + /// Invalid codepoint for hex or unicode escape + InvalidCodepointValue, + /// Invalid character class + InvalidClass, + /// Unknown group flag + UnknownFlag(String), + /// Disabling Unicode not supported + NonUnicodeUnsupported, + /// Invalid back reference + InvalidBackref, + /// Quantifier on lookaround or other zero-width assertion + TargetNotRepeatable, + /// Couldn't parse group name + InvalidGroupName, + /// Invalid group id in escape sequence + InvalidGroupNameBackref(String), +} + +/// An error as the result of compiling a regex. +#[derive(Clone, Debug)] +#[non_exhaustive] +pub enum CompileError { + /// Regex crate error + InnerError(RaBuildError), + /// Look-behind assertion without constant size + LookBehindNotConst, + /// Couldn't parse group name + InvalidGroupName, + /// Invalid group id in escape sequence + InvalidGroupNameBackref(String), + /// Invalid back reference + InvalidBackref, + /// Once named groups are used you cannot refer to groups by number + NamedBackrefOnly, +} + +/// An error as the result of executing a regex. +#[derive(Clone, Debug)] +#[non_exhaustive] +pub enum RuntimeError { + /// Max stack size exceeded for backtracking while executing regex. + StackOverflow, + /// Max limit for backtracking count exceeded while executing the regex. + /// Configure using + /// [`RegexBuilder::backtrack_limit`](struct.RegexBuilder.html#method.backtrack_limit). + BacktrackLimitExceeded, +} + +#[cfg(feature = "std")] +impl ::std::error::Error for Error {} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + ParseError::GeneralParseError(s) => write!(f, "General parsing error: {}", s), + ParseError::UnclosedOpenParen => { + write!(f, "Opening parenthesis without closing parenthesis") + } + ParseError::InvalidRepeat => write!(f, "Invalid repeat syntax"), + ParseError::RecursionExceeded => write!(f, "Pattern too deeply nested"), + ParseError::TrailingBackslash => write!(f, "Backslash without following character"), + ParseError::InvalidEscape(s) => write!(f, "Invalid escape: {}", s), + ParseError::UnclosedUnicodeName => write!(f, "Unicode escape not closed"), + ParseError::InvalidHex => write!(f, "Invalid hex escape"), + ParseError::InvalidCodepointValue => { + write!(f, "Invalid codepoint for hex or unicode escape") + } + ParseError::InvalidClass => write!(f, "Invalid character class"), + ParseError::UnknownFlag(s) => write!(f, "Unknown group flag: {}", s), + ParseError::NonUnicodeUnsupported => write!(f, "Disabling Unicode not supported"), + ParseError::InvalidBackref => write!(f, "Invalid back reference"), + ParseError::InvalidGroupName => write!(f, "Could not parse group name"), + ParseError::InvalidGroupNameBackref(s) => { + write!(f, "Invalid group name in back reference: {}", s) + } + ParseError::TargetNotRepeatable => write!(f, "Target of repeat operator is invalid"), + } + } +} + +impl fmt::Display for CompileError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + CompileError::InnerError(e) => write!(f, "Regex error: {}", e), + CompileError::LookBehindNotConst => { + write!(f, "Look-behind assertion without constant size") + }, + CompileError::InvalidGroupName => write!(f, "Could not parse group name"), + CompileError::InvalidGroupNameBackref(s) => write!(f, "Invalid group name in back reference: {}", s), + CompileError::InvalidBackref => write!(f, "Invalid back reference"), + CompileError::NamedBackrefOnly => write!(f, "Numbered backref/call not allowed because named group was used, use a named backref instead"), + } + } +} + +impl fmt::Display for RuntimeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + RuntimeError::StackOverflow => write!(f, "Max stack size exceeded for backtracking"), + RuntimeError::BacktrackLimitExceeded => { + write!(f, "Max limit for backtracking count exceeded") + } + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::ParseError(position, parse_error) => { + write!(f, "Parsing error at position {}: {}", position, parse_error) + } + Error::CompileError(compile_error) => { + write!(f, "Error compiling regex: {}", compile_error) + } + Error::RuntimeError(runtime_error) => { + write!(f, "Error executing regex: {}", runtime_error) + } + } + } +} + +impl From for Error { + fn from(compile_error: CompileError) -> Self { + Error::CompileError(compile_error) + } +} diff --git a/tools/vendor/fancy-regex/src/expand.rs b/tools/vendor/fancy-regex/src/expand.rs new file mode 100644 index 0000000000..2574402a00 --- /dev/null +++ b/tools/vendor/fancy-regex/src/expand.rs @@ -0,0 +1,263 @@ +use alloc::borrow::Cow; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; + +use crate::parse::{parse_decimal, parse_id}; +use crate::{Captures, CompileError, Error, ParseError, Regex}; + +/// A set of options for expanding a template string using the contents +/// of capture groups. +#[derive(Debug)] +pub struct Expander { + sub_char: char, + open: &'static str, + close: &'static str, + allow_undelimited_name: bool, +} + +impl Default for Expander { + /// Returns the default expander used by [`Captures::expand`]. + /// + /// [`Captures::expand`]: struct.Captures.html#expand + fn default() -> Self { + Expander { + sub_char: '$', + open: "{", + close: "}", + allow_undelimited_name: true, + } + } +} + +impl Expander { + /// Returns an expander that uses Python-compatible syntax. + /// + /// Expands all instances of `\num` or `\g` in `replacement` + /// to the corresponding capture group `num` or `name`, and writes + /// them to the `dst` buffer given. + /// + /// `name` may be an integer corresponding to the index of the + /// capture group (counted by order of opening parenthesis where `\0` is the + /// entire match) or it can be a name (consisting of letters, digits or + /// underscores) corresponding to a named capture group. + /// + /// `num` must be an integer corresponding to the index of the + /// capture group. + /// + /// If `num` or `name` isn't a valid capture group (whether the name doesn't exist + /// or isn't a valid index), then it is replaced with the empty string. + /// + /// The longest possible number is used. e.g., `\10` looks up capture + /// group 10 and not capture group 1 followed by a literal 0. + /// + /// To write a literal `\`, use `\\`. + pub fn python() -> Expander { + Expander { + sub_char: '\\', + open: "g<", + close: ">", + allow_undelimited_name: false, + } + } + + /// Checks `template` for errors. The following conditions are checked for: + /// + /// - A reference to a numbered group that does not exist in `regex` + /// - A reference to a numbered group (other than 0) when `regex` contains named groups + /// - A reference to a named group that does not occur in `regex` + /// - An opening group name delimiter without a closing delimiter + /// - Using an empty string as a group name + pub fn check(&self, template: &str, regex: &Regex) -> crate::Result<()> { + let on_group_num = |num| { + if num == 0 { + Ok(()) + } else if !regex.named_groups.is_empty() { + Err(Error::CompileError(CompileError::NamedBackrefOnly)) + } else if num < regex.captures_len() { + Ok(()) + } else { + Err(Error::CompileError(CompileError::InvalidBackref)) + } + }; + self.exec(template, |step| match step { + Step::Char(_) => Ok(()), + Step::GroupName(name) => { + if regex.named_groups.contains_key(name) { + Ok(()) + } else if let Ok(num) = name.parse() { + on_group_num(num) + } else { + Err(Error::CompileError(CompileError::InvalidBackref)) + } + } + Step::GroupNum(num) => on_group_num(num), + Step::Error => Err(Error::ParseError( + 0, + ParseError::GeneralParseError( + "parse error in template while expanding".to_string(), + ), + )), + }) + } + + /// Escapes the substitution character in `text` so it appears literally + /// in the output of `expansion`. + /// + /// ``` + /// assert_eq!( + /// fancy_regex::Expander::default().escape("Has a literal $ sign."), + /// "Has a literal $$ sign.", + /// ); + /// ``` + pub fn escape<'a>(&self, text: &'a str) -> Cow<'a, str> { + if text.contains(self.sub_char) { + let mut quoted = String::with_capacity(self.sub_char.len_utf8() * 2); + quoted.push(self.sub_char); + quoted.push(self.sub_char); + Cow::Owned(text.replace(self.sub_char, "ed)) + } else { + Cow::Borrowed(text) + } + } + + #[doc(hidden)] + #[deprecated(since = "0.4.0", note = "Use `escape` instead.")] + pub fn quote<'a>(&self, text: &'a str) -> Cow<'a, str> { + self.escape(text) + } + + /// Expands the template string `template` using the syntax defined + /// by this expander and the values of capture groups from `captures`. + pub fn expansion(&self, template: &str, captures: &Captures<'_>) -> String { + let mut cursor = Vec::with_capacity(template.len()); + #[cfg(feature = "std")] + self.write_expansion(&mut cursor, template, captures) + .expect("expansion succeeded"); + #[cfg(not(feature = "std"))] + self.write_expansion_vec(&mut cursor, template, captures) + .expect("expansion succeeded"); + String::from_utf8(cursor).expect("expansion is UTF-8") + } + + /// Appends the expansion produced by `expansion` to `dst`. Potentially more efficient + /// than calling `expansion` directly and appending to an existing string. + pub fn append_expansion(&self, dst: &mut String, template: &str, captures: &Captures<'_>) { + let mut cursor = core::mem::take(dst).into_bytes(); + #[cfg(feature = "std")] + self.write_expansion(&mut cursor, template, captures) + .expect("expansion succeeded"); + #[cfg(not(feature = "std"))] + self.write_expansion_vec(&mut cursor, template, captures) + .expect("expansion succeeded"); + *dst = String::from_utf8(cursor).expect("expansion is UTF-8"); + } + + /// Writes the expansion produced by `expansion` to `dst`. Potentially more efficient + /// than calling `expansion` directly and writing the result. + #[cfg(feature = "std")] + pub fn write_expansion( + &self, + mut dst: impl std::io::Write, + template: &str, + captures: &Captures<'_>, + ) -> std::io::Result<()> { + self.exec(template, |step| match step { + Step::Char(c) => write!(dst, "{}", c), + Step::GroupName(name) => { + if let Some(m) = captures.name(name) { + write!(dst, "{}", m.as_str()) + } else if let Some(m) = name.parse().ok().and_then(|num| captures.get(num)) { + write!(dst, "{}", m.as_str()) + } else { + Ok(()) + } + } + Step::GroupNum(num) => { + if let Some(m) = captures.get(num) { + write!(dst, "{}", m.as_str()) + } else { + Ok(()) + } + } + Step::Error => Ok(()), + }) + } + + /// Writes the expansion produced by `expansion` to `dst`. Potentially more efficient + /// than calling `expansion` directly and writing the result. + pub fn write_expansion_vec( + &self, + dst: &mut Vec, + template: &str, + captures: &Captures<'_>, + ) -> core::fmt::Result { + self.exec(template, |step| match step { + Step::Char(c) => Ok(dst.extend(c.to_string().as_bytes())), + Step::GroupName(name) => { + if let Some(m) = captures.name(name) { + Ok(dst.extend(m.as_str().as_bytes())) + } else if let Some(m) = name.parse().ok().and_then(|num| captures.get(num)) { + Ok(dst.extend(m.as_str().as_bytes())) + } else { + Ok(()) + } + } + Step::GroupNum(num) => { + if let Some(m) = captures.get(num) { + Ok(dst.extend(m.as_str().as_bytes())) + } else { + Ok(()) + } + } + Step::Error => Ok(()), + }) + } + + fn exec<'t, E>( + &self, + template: &'t str, + mut f: impl FnMut(Step<'t>) -> Result<(), E>, + ) -> Result<(), E> { + debug_assert!(!self.open.is_empty()); + debug_assert!(!self.close.is_empty()); + let mut iter = template.chars(); + while let Some(c) = iter.next() { + if c == self.sub_char { + let tail = iter.as_str(); + let skip = if tail.starts_with(self.sub_char) { + f(Step::Char(self.sub_char))?; + 1 + } else if let Some((id, skip)) = parse_id(tail, self.open, self.close, false) + .or_else(|| { + if self.allow_undelimited_name { + parse_id(tail, "", "", false) + } else { + None + } + }) + { + f(Step::GroupName(id))?; + skip + } else if let Some((skip, num)) = parse_decimal(tail, 0) { + f(Step::GroupNum(num))?; + skip + } else { + f(Step::Error)?; + f(Step::Char(self.sub_char))?; + 0 + }; + iter = iter.as_str()[skip..].chars(); + } else { + f(Step::Char(c))?; + } + } + Ok(()) + } +} + +enum Step<'a> { + Char(char), + GroupName(&'a str), + GroupNum(usize), + Error, +} diff --git a/tools/vendor/fancy-regex/src/lib.rs b/tools/vendor/fancy-regex/src/lib.rs new file mode 100644 index 0000000000..19b02846e2 --- /dev/null +++ b/tools/vendor/fancy-regex/src/lib.rs @@ -0,0 +1,1888 @@ +// Copyright 2016 The Fancy Regex Authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +/*! +An implementation of regexes, supporting a relatively rich set of features, including backreferences +and lookaround. + +It builds on top of the excellent [regex] crate. If you are not +familiar with it, make sure you read its documentation and maybe you don't even need fancy-regex. + +If your regex or parts of it does not use any special features, the matching is delegated to the +regex crate. That means it has linear runtime. But if you use "fancy" features such as +backreferences or look-around, an engine with backtracking needs to be used. In that case, the regex +can be slow and take exponential time to run because of what is called "catastrophic backtracking". +This depends on the regex and the input. + +# Usage + +The API should feel very similar to the regex crate, and involves compiling a regex and then using +it to find matches in text. + +## Example: Matching text + +An example with backreferences to check if a text consists of two identical words: + +```rust +use fancy_regex::Regex; + +let re = Regex::new(r"^(\w+) (\1)$").unwrap(); +let result = re.is_match("foo foo"); + +assert!(result.is_ok()); +let did_match = result.unwrap(); +assert!(did_match); +``` + +Note that like in the regex crate, the regex needs anchors like `^` and `$` to match against the +entire input text. + +## Example: Finding the position of matches + +```rust +use fancy_regex::Regex; + +let re = Regex::new(r"(\d)\1").unwrap(); +let result = re.find("foo 22"); + +assert!(result.is_ok(), "execution was successful"); +let match_option = result.unwrap(); + +assert!(match_option.is_some(), "found a match"); +let m = match_option.unwrap(); + +assert_eq!(m.start(), 4); +assert_eq!(m.end(), 6); +assert_eq!(m.as_str(), "22"); +``` + +## Example: Capturing groups + +```rust +use fancy_regex::Regex; + +let re = Regex::new(r"(? = re.split(target).map(|x| x.unwrap()).collect(); +assert_eq!(fields, vec!["a", "b", "c", "d", "e"]); + +let fields: Vec<&str> = re.splitn(target, 3).map(|x| x.unwrap()).collect(); +assert_eq!(fields, vec!["a", "b", "c\td e"]); +``` + +# Syntax + +The regex syntax is based on the [regex] crate's, with some additional supported syntax. + +Escapes: + +`\h` +: hex digit (`[0-9A-Fa-f]`) \ +`\H` +: not hex digit (`[^0-9A-Fa-f]`) \ +`\e` +: escape control character (`\x1B`) \ +`\K` +: keep text matched so far out of the overall match ([docs](https://www.regular-expressions.info/keep.html))\ +`\G` +: anchor to where the previous match ended ([docs](https://www.regular-expressions.info/continue.html)) + +Backreferences: + +`\1` +: match the exact string that the first capture group matched \ +`\2` +: backref to the second capture group, etc + +Named capture groups: + +`(?exp)` +: match *exp*, creating capture group named *name* \ +`\k` +: match the exact string that the capture group named *name* matched \ +`(?Pexp)` +: same as `(?exp)` for compatibility with Python, etc. \ +`(?P=name)` +: same as `\k` for compatibility with Python, etc. + +Look-around assertions for matching without changing the current position: + +`(?=exp)` +: look-ahead, succeeds if *exp* matches to the right of the current position \ +`(?!exp)` +: negative look-ahead, succeeds if *exp* doesn't match to the right \ +`(?<=exp)` +: look-behind, succeeds if *exp* matches to the left of the current position \ +`(?exp)` to prevent backtracking within `exp`, e.g.: + +``` +# use fancy_regex::Regex; +let re = Regex::new(r"^a(?>bc|b)c$").unwrap(); +assert!(re.is_match("abcc").unwrap()); +// Doesn't match because `|b` is never tried because of the atomic group +assert!(!re.is_match("abc").unwrap()); +``` + +Conditionals - if/then/else: + +`(?(1))` +: continue only if first capture group matched \ +`(?())` +: continue only if capture group named *name* matched \ +`(?(1)true_branch|false_branch)` +: if the first capture group matched then execute the true_branch regex expression, else execute false_branch ([docs](https://www.regular-expressions.info/conditional.html)) \ +`(?(condition)true_branch|false_branch)` +: if the condition matches then execute the true_branch regex expression, else execute false_branch from the point just before the condition was evaluated + +[regex]: https://crates.io/crates/regex +*/ + +#![doc(html_root_url = "https://docs.rs/fancy-regex/0.14.0")] +#![deny(missing_docs)] +#![deny(missing_debug_implementations)] +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +use alloc::borrow::{Cow, ToOwned}; +use alloc::boxed::Box; +use alloc::string::{String, ToString}; +use alloc::sync::Arc; +use alloc::vec; +use alloc::vec::Vec; + +use core::convert::TryFrom; +use core::fmt::{Debug, Formatter}; +use core::ops::{Index, Range}; +use core::str::FromStr; +use core::{fmt, usize}; +use regex_automata::meta::Regex as RaRegex; +use regex_automata::util::captures::Captures as RaCaptures; +use regex_automata::util::syntax::Config as SyntaxConfig; +use regex_automata::Input as RaInput; + +mod analyze; +mod compile; +mod error; +mod expand; +mod parse; +mod replacer; +mod vm; + +use crate::analyze::analyze; +use crate::compile::compile; +use crate::parse::{ExprTree, NamedGroups, Parser}; +use crate::vm::{Prog, OPTION_SKIPPED_EMPTY_MATCH}; + +pub use crate::error::{CompileError, Error, ParseError, Result, RuntimeError}; +pub use crate::expand::Expander; +pub use crate::replacer::{NoExpand, Replacer, ReplacerRef}; + +const MAX_RECURSION: usize = 64; + +// the public API + +/// A builder for a `Regex` to allow configuring options. +#[derive(Debug)] +pub struct RegexBuilder(RegexOptions); + +/// A compiled regular expression. +#[derive(Clone)] +pub struct Regex { + inner: RegexImpl, + named_groups: Arc, +} + +// Separate enum because we don't want to expose any of this +#[derive(Clone)] +enum RegexImpl { + // Do we want to box this? It's pretty big... + Wrap { + inner: RaRegex, + options: RegexOptions, + }, + Fancy { + prog: Prog, + n_groups: usize, + options: RegexOptions, + }, +} + +/// A single match of a regex or group in an input text +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Match<'t> { + text: &'t str, + start: usize, + end: usize, +} + +/// An iterator over all non-overlapping matches for a particular string. +/// +/// The iterator yields a `Result`. The iterator stops when no more +/// matches can be found. +/// +/// `'r` is the lifetime of the compiled regular expression and `'t` is the +/// lifetime of the matched string. +#[derive(Debug)] +pub struct Matches<'r, 't> { + re: &'r Regex, + text: &'t str, + last_end: usize, + last_match: Option, +} + +impl<'r, 't> Matches<'r, 't> { + /// Return the text being searched. + pub fn text(&self) -> &'t str { + self.text + } + + /// Return the underlying regex. + pub fn regex(&self) -> &'r Regex { + &self.re + } +} + +impl<'r, 't> Iterator for Matches<'r, 't> { + type Item = Result>; + + /// Adapted from the `regex` crate. Calls `find_from_pos` repeatedly. + /// Ignores empty matches immediately after a match. + fn next(&mut self) -> Option { + if self.last_end > self.text.len() { + return None; + } + + let option_flags = if let Some(last_match) = self.last_match { + if self.last_end > last_match { + OPTION_SKIPPED_EMPTY_MATCH + } else { + 0 + } + } else { + 0 + }; + let mat = + match self + .re + .find_from_pos_with_option_flags(self.text, self.last_end, option_flags) + { + Err(error) => return Some(Err(error)), + Ok(None) => return None, + Ok(Some(mat)) => mat, + }; + + if mat.start == mat.end { + // This is an empty match. To ensure we make progress, start + // the next search at the smallest possible starting position + // of the next match following this one. + self.last_end = next_utf8(self.text, mat.end); + // Don't accept empty matches immediately following a match. + // Just move on to the next match. + if Some(mat.end) == self.last_match { + return self.next(); + } + } else { + self.last_end = mat.end; + } + + self.last_match = Some(mat.end); + + Some(Ok(mat)) + } +} + +/// An iterator that yields all non-overlapping capture groups matching a +/// particular regular expression. +/// +/// The iterator stops when no more matches can be found. +/// +/// `'r` is the lifetime of the compiled regular expression and `'t` is the +/// lifetime of the matched string. +#[derive(Debug)] +pub struct CaptureMatches<'r, 't>(Matches<'r, 't>); + +impl<'r, 't> CaptureMatches<'r, 't> { + /// Return the text being searched. + pub fn text(&self) -> &'t str { + self.0.text + } + + /// Return the underlying regex. + pub fn regex(&self) -> &'r Regex { + &self.0.re + } +} + +impl<'r, 't> Iterator for CaptureMatches<'r, 't> { + type Item = Result>; + + /// Adapted from the `regex` crate. Calls `captures_from_pos` repeatedly. + /// Ignores empty matches immediately after a match. + fn next(&mut self) -> Option { + if self.0.last_end > self.0.text.len() { + return None; + } + + let captures = match self.0.re.captures_from_pos(self.0.text, self.0.last_end) { + Err(error) => return Some(Err(error)), + Ok(None) => return None, + Ok(Some(captures)) => captures, + }; + + let mat = captures + .get(0) + .expect("`Captures` is expected to have entire match at 0th position"); + if mat.start == mat.end { + self.0.last_end = next_utf8(self.0.text, mat.end); + if Some(mat.end) == self.0.last_match { + return self.next(); + } + } else { + self.0.last_end = mat.end; + } + + self.0.last_match = Some(mat.end); + + Some(Ok(captures)) + } +} + +/// A set of capture groups found for a regex. +#[derive(Debug)] +pub struct Captures<'t> { + inner: CapturesImpl<'t>, + named_groups: Arc, +} + +#[derive(Debug)] +enum CapturesImpl<'t> { + Wrap { + text: &'t str, + locations: RaCaptures, + }, + Fancy { + text: &'t str, + saves: Vec, + }, +} + +/// Iterator for captured groups in order in which they appear in the regex. +#[derive(Debug)] +pub struct SubCaptureMatches<'c, 't> { + caps: &'c Captures<'t>, + i: usize, +} + +/// An iterator over all substrings delimited by a regex. +/// +/// This iterator yields `Result<&'h str>`, where each item is a substring of the +/// target string that is delimited by matches of the regular expression. It stops when there +/// are no more substrings to yield. +/// +/// `'r` is the lifetime of the compiled regular expression, and `'h` is the +/// lifetime of the target string being split. +/// +/// This iterator can be created by the [`Regex::split`] method. +#[derive(Debug)] +pub struct Split<'r, 'h> { + matches: Matches<'r, 'h>, + next_start: usize, + target: &'h str, +} + +impl<'r, 'h> Iterator for Split<'r, 'h> { + type Item = Result<&'h str>; + + /// Returns the next substring that results from splitting the target string by the regex. + /// + /// If no more matches are found, returns the remaining part of the string, + /// or `None` if all substrings have been yielded. + fn next(&mut self) -> Option> { + match self.matches.next() { + None => { + let len = self.target.len(); + if self.next_start > len { + // No more substrings to return + None + } else { + // Return the last part of the target string + // Next call will return None + let part = &self.target[self.next_start..len]; + self.next_start = len + 1; + Some(Ok(part)) + } + } + // Return the next substring + Some(Ok(m)) => { + let part = &self.target[self.next_start..m.start()]; + self.next_start = m.end(); + Some(Ok(part)) + } + Some(Err(e)) => Some(Err(e)), + } + } +} + +impl<'r, 'h> core::iter::FusedIterator for Split<'r, 'h> {} + +/// An iterator over at most `N` substrings delimited by a regex. +/// +/// This iterator yields `Result<&'h str>`, where each item is a substring of the +/// target that is delimited by matches of the regular expression. It stops either when +/// there are no more substrings to yield, or after `N` substrings have been yielded. +/// +/// The `N`th substring is the remaining part of the target. +/// +/// `'r` is the lifetime of the compiled regular expression, and `'h` is the +/// lifetime of the target string being split. +/// +/// This iterator can be created by the [`Regex::splitn`] method. +#[derive(Debug)] +pub struct SplitN<'r, 'h> { + splits: Split<'r, 'h>, + limit: usize, +} + +impl<'r, 'h> Iterator for SplitN<'r, 'h> { + type Item = Result<&'h str>; + + /// Returns the next substring resulting from splitting the target by the regex, + /// limited to `N` splits. + /// + /// Returns `None` if no more matches are found or if the limit is reached after yielding + /// the remaining part of the target. + fn next(&mut self) -> Option> { + if self.limit == 0 { + // Limit reached. No more substrings available. + return None; + } + + // Decrement the limit for each split. + self.limit -= 1; + if self.limit > 0 { + return self.splits.next(); + } + + // Nth split + let len = self.splits.target.len(); + if self.splits.next_start > len { + // No more substrings available. + return None; + } else { + // Return the remaining part of the target + let start = self.splits.next_start; + self.splits.next_start = len + 1; + return Some(Ok(&self.splits.target[start..len])); + } + } + + fn size_hint(&self) -> (usize, Option) { + (0, Some(self.limit)) + } +} + +impl<'r, 'h> core::iter::FusedIterator for SplitN<'r, 'h> {} + +#[derive(Clone, Debug)] +struct RegexOptions { + pattern: String, + syntaxc: SyntaxConfig, + backtrack_limit: usize, + delegate_size_limit: Option, + delegate_dfa_size_limit: Option, +} + +impl Default for RegexOptions { + fn default() -> Self { + RegexOptions { + pattern: String::new(), + syntaxc: SyntaxConfig::default(), + backtrack_limit: 1_000_000, + delegate_size_limit: None, + delegate_dfa_size_limit: None, + } + } +} + +impl RegexBuilder { + /// Create a new regex builder with a regex pattern. + /// + /// If the pattern is invalid, the call to `build` will fail later. + pub fn new(pattern: &str) -> Self { + let mut builder = RegexBuilder(RegexOptions::default()); + builder.0.pattern = pattern.to_string(); + builder + } + + /// Build the `Regex`. + /// + /// Returns an [`Error`](enum.Error.html) if the pattern could not be parsed. + pub fn build(&self) -> Result { + Regex::new_options(self.0.clone()) + } + + /// Override default case insensitive + /// this is to enable/disable casing via builder instead of a flag within + /// the raw string provided to the regex builder + /// + /// Default is false + pub fn case_insensitive(&mut self, yes: bool) -> &mut Self { + let syntaxc = self.0.syntaxc.to_owned(); + self.0.syntaxc = syntaxc.case_insensitive(yes); + self + } + + /// Limit for how many times backtracking should be attempted for fancy regexes (where + /// backtracking is used). If this limit is exceeded, execution returns an error with + /// [`Error::BacktrackLimitExceeded`](enum.Error.html#variant.BacktrackLimitExceeded). + /// This is for preventing a regex with catastrophic backtracking to run for too long. + /// + /// Default is `1_000_000` (1 million). + pub fn backtrack_limit(&mut self, limit: usize) -> &mut Self { + self.0.backtrack_limit = limit; + self + } + + /// Set the approximate size limit of the compiled regular expression. + /// + /// This option is forwarded from the wrapped `regex` crate. Note that depending on the used + /// regex features there may be multiple delegated sub-regexes fed to the `regex` crate. As + /// such the actual limit is closer to ` * delegate_size_limit`. + pub fn delegate_size_limit(&mut self, limit: usize) -> &mut Self { + self.0.delegate_size_limit = Some(limit); + self + } + + /// Set the approximate size of the cache used by the DFA. + /// + /// This option is forwarded from the wrapped `regex` crate. Note that depending on the used + /// regex features there may be multiple delegated sub-regexes fed to the `regex` crate. As + /// such the actual limit is closer to ` * + /// delegate_dfa_size_limit`. + pub fn delegate_dfa_size_limit(&mut self, limit: usize) -> &mut Self { + self.0.delegate_dfa_size_limit = Some(limit); + self + } +} + +impl fmt::Debug for Regex { + /// Shows the original regular expression. + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.as_str()) + } +} + +impl fmt::Display for Regex { + /// Shows the original regular expression + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.as_str()) + } +} + +impl FromStr for Regex { + type Err = Error; + + /// Attempts to parse a string into a regular expression + fn from_str(s: &str) -> Result { + Regex::new(s) + } +} + +impl Regex { + /// Parse and compile a regex with default options, see `RegexBuilder`. + /// + /// Returns an [`Error`](enum.Error.html) if the pattern could not be parsed. + pub fn new(re: &str) -> Result { + let options = RegexOptions { + pattern: re.to_string(), + ..RegexOptions::default() + }; + Self::new_options(options) + } + + fn new_options(options: RegexOptions) -> Result { + let raw_tree = Expr::parse_tree(&options.pattern)?; + + // wrapper to search for re at arbitrary start position, + // and to capture the match bounds + let tree = ExprTree { + expr: Expr::Concat(vec![ + Expr::Repeat { + child: Box::new(Expr::Any { newline: true }), + lo: 0, + hi: usize::MAX, + greedy: false, + }, + Expr::Group(Box::new(raw_tree.expr)), + ]), + ..raw_tree + }; + + let info = analyze(&tree)?; + + let inner_info = &info.children[1].children[0]; // references inner expr + if !inner_info.hard { + // easy case, wrap regex + + // we do our own to_str because escapes are different + let mut re_cooked = String::new(); + // same as raw_tree.expr above, but it was moved, so traverse to find it + let raw_e = match tree.expr { + Expr::Concat(ref v) => match v[1] { + Expr::Group(ref child) => child, + _ => unreachable!(), + }, + _ => unreachable!(), + }; + raw_e.to_str(&mut re_cooked, 0); + let inner = compile::compile_inner(&re_cooked, &options)?; + return Ok(Regex { + inner: RegexImpl::Wrap { inner, options }, + named_groups: Arc::new(tree.named_groups), + }); + } + + let prog = compile(&info)?; + Ok(Regex { + inner: RegexImpl::Fancy { + prog, + n_groups: info.end_group, + options, + }, + named_groups: Arc::new(tree.named_groups), + }) + } + + /// Returns the original string of this regex. + pub fn as_str(&self) -> &str { + match &self.inner { + RegexImpl::Wrap { options, .. } => &options.pattern, + RegexImpl::Fancy { options, .. } => &options.pattern, + } + } + + /// Check if the regex matches the input text. + /// + /// # Example + /// + /// Test if some text contains the same word twice: + /// + /// ```rust + /// # use fancy_regex::Regex; + /// + /// let re = Regex::new(r"(\w+) \1").unwrap(); + /// assert!(re.is_match("mirror mirror on the wall").unwrap()); + /// ``` + pub fn is_match(&self, text: &str) -> Result { + match &self.inner { + RegexImpl::Wrap { ref inner, .. } => Ok(inner.is_match(text)), + RegexImpl::Fancy { + ref prog, options, .. + } => { + let result = vm::run(prog, text, 0, 0, options)?; + Ok(result.is_some()) + } + } + } + + /// Returns an iterator for each successive non-overlapping match in `text`. + /// + /// If you have capturing groups in your regex that you want to extract, use the [Regex::captures_iter()] + /// method. + /// + /// # Example + /// + /// Find all words followed by an exclamation point: + /// + /// ```rust + /// # use fancy_regex::Regex; + /// + /// let re = Regex::new(r"\w+(?=!)").unwrap(); + /// let mut matches = re.find_iter("so fancy! even with! iterators!"); + /// assert_eq!(matches.next().unwrap().unwrap().as_str(), "fancy"); + /// assert_eq!(matches.next().unwrap().unwrap().as_str(), "with"); + /// assert_eq!(matches.next().unwrap().unwrap().as_str(), "iterators"); + /// assert!(matches.next().is_none()); + /// ``` + pub fn find_iter<'r, 't>(&'r self, text: &'t str) -> Matches<'r, 't> { + Matches { + re: &self, + text, + last_end: 0, + last_match: None, + } + } + + /// Find the first match in the input text. + /// + /// If you have capturing groups in your regex that you want to extract, use the [Regex::captures()] + /// method. + /// + /// # Example + /// + /// Find a word that is followed by an exclamation point: + /// + /// ```rust + /// # use fancy_regex::Regex; + /// + /// let re = Regex::new(r"\w+(?=!)").unwrap(); + /// assert_eq!(re.find("so fancy!").unwrap().unwrap().as_str(), "fancy"); + /// ``` + pub fn find<'t>(&self, text: &'t str) -> Result>> { + self.find_from_pos(text, 0) + } + + /// Returns the first match in `text`, starting from the specified byte position `pos`. + /// + /// # Examples + /// + /// Finding match starting at a position: + /// + /// ``` + /// # use fancy_regex::Regex; + /// let re = Regex::new(r"(?m:^)(\d+)").unwrap(); + /// let text = "1 test 123\n2 foo"; + /// let mat = re.find_from_pos(text, 7).unwrap().unwrap(); + /// + /// assert_eq!(mat.start(), 11); + /// assert_eq!(mat.end(), 12); + /// ``` + /// + /// Note that in some cases this is not the same as using the `find` + /// method and passing a slice of the string, see [Regex::captures_from_pos()] for details. + pub fn find_from_pos<'t>(&self, text: &'t str, pos: usize) -> Result>> { + self.find_from_pos_with_option_flags(text, pos, 0) + } + + fn find_from_pos_with_option_flags<'t>( + &self, + text: &'t str, + pos: usize, + option_flags: u32, + ) -> Result>> { + match &self.inner { + RegexImpl::Wrap { inner, .. } => Ok(inner + .search(&RaInput::new(text).span(pos..text.len())) + .map(|m| Match::new(text, m.start(), m.end()))), + RegexImpl::Fancy { prog, options, .. } => { + let result = vm::run(prog, text, pos, option_flags, options)?; + Ok(result.map(|saves| Match::new(text, saves[0], saves[1]))) + } + } + } + + /// Returns an iterator over all the non-overlapping capture groups matched in `text`. + /// + /// # Examples + /// + /// Finding all matches and capturing parts of each: + /// + /// ```rust + /// # use fancy_regex::Regex; + /// + /// let re = Regex::new(r"(\d{4})-(\d{2})").unwrap(); + /// let text = "It was between 2018-04 and 2020-01"; + /// let mut all_captures = re.captures_iter(text); + /// + /// let first = all_captures.next().unwrap().unwrap(); + /// assert_eq!(first.get(1).unwrap().as_str(), "2018"); + /// assert_eq!(first.get(2).unwrap().as_str(), "04"); + /// assert_eq!(first.get(0).unwrap().as_str(), "2018-04"); + /// + /// let second = all_captures.next().unwrap().unwrap(); + /// assert_eq!(second.get(1).unwrap().as_str(), "2020"); + /// assert_eq!(second.get(2).unwrap().as_str(), "01"); + /// assert_eq!(second.get(0).unwrap().as_str(), "2020-01"); + /// + /// assert!(all_captures.next().is_none()); + /// ``` + pub fn captures_iter<'r, 't>(&'r self, text: &'t str) -> CaptureMatches<'r, 't> { + CaptureMatches(self.find_iter(text)) + } + + /// Returns the capture groups for the first match in `text`. + /// + /// If no match is found, then `Ok(None)` is returned. + /// + /// # Examples + /// + /// Finding matches and capturing parts of the match: + /// + /// ```rust + /// # use fancy_regex::Regex; + /// + /// let re = Regex::new(r"(\d{4})-(\d{2})-(\d{2})").unwrap(); + /// let text = "The date was 2018-04-07"; + /// let captures = re.captures(text).unwrap().unwrap(); + /// + /// assert_eq!(captures.get(1).unwrap().as_str(), "2018"); + /// assert_eq!(captures.get(2).unwrap().as_str(), "04"); + /// assert_eq!(captures.get(3).unwrap().as_str(), "07"); + /// assert_eq!(captures.get(0).unwrap().as_str(), "2018-04-07"); + /// ``` + pub fn captures<'t>(&self, text: &'t str) -> Result>> { + self.captures_from_pos(text, 0) + } + + /// Returns the capture groups for the first match in `text`, starting from + /// the specified byte position `pos`. + /// + /// # Examples + /// + /// Finding captures starting at a position: + /// + /// ``` + /// # use fancy_regex::Regex; + /// let re = Regex::new(r"(?m:^)(\d+)").unwrap(); + /// let text = "1 test 123\n2 foo"; + /// let captures = re.captures_from_pos(text, 7).unwrap().unwrap(); + /// + /// let group = captures.get(1).unwrap(); + /// assert_eq!(group.as_str(), "2"); + /// assert_eq!(group.start(), 11); + /// assert_eq!(group.end(), 12); + /// ``` + /// + /// Note that in some cases this is not the same as using the `captures` + /// method and passing a slice of the string, see the capture that we get + /// when we do this: + /// + /// ``` + /// # use fancy_regex::Regex; + /// let re = Regex::new(r"(?m:^)(\d+)").unwrap(); + /// let text = "1 test 123\n2 foo"; + /// let captures = re.captures(&text[7..]).unwrap().unwrap(); + /// assert_eq!(captures.get(1).unwrap().as_str(), "123"); + /// ``` + /// + /// This matched the number "123" because it's at the beginning of the text + /// of the string slice. + /// + pub fn captures_from_pos<'t>(&self, text: &'t str, pos: usize) -> Result>> { + let named_groups = self.named_groups.clone(); + match &self.inner { + RegexImpl::Wrap { inner, .. } => { + let mut locations = inner.create_captures(); + inner.captures(RaInput::new(text).span(pos..text.len()), &mut locations); + Ok(locations.is_match().then(|| Captures { + inner: CapturesImpl::Wrap { text, locations }, + named_groups, + })) + } + RegexImpl::Fancy { + prog, + n_groups, + options, + .. + } => { + let result = vm::run(prog, text, pos, 0, options)?; + Ok(result.map(|mut saves| { + saves.truncate(n_groups * 2); + Captures { + inner: CapturesImpl::Fancy { text, saves }, + named_groups, + } + })) + } + } + } + + /// Returns the number of captures, including the implicit capture of the entire expression. + pub fn captures_len(&self) -> usize { + match &self.inner { + RegexImpl::Wrap { inner, .. } => inner.captures_len(), + RegexImpl::Fancy { n_groups, .. } => *n_groups, + } + } + + /// Returns an iterator over the capture names. + pub fn capture_names(&self) -> CaptureNames { + let mut names = Vec::new(); + names.resize(self.captures_len(), None); + for (name, &i) in self.named_groups.iter() { + names[i] = Some(name.as_str()); + } + CaptureNames(names.into_iter()) + } + + // for debugging only + #[doc(hidden)] + pub fn debug_print(&self) { + match &self.inner { + #[cfg(feature = "std")] + RegexImpl::Wrap { inner, .. } => println!("wrapped {:?}", inner), + #[cfg(not(feature = "std"))] + RegexImpl::Wrap { .. } => {} + RegexImpl::Fancy { prog, .. } => prog.debug_print(), + } + } + + /// Replaces the leftmost-first match with the replacement provided. + /// The replacement can be a regular string (where `$N` and `$name` are + /// expanded to match capture groups) or a function that takes the matches' + /// `Captures` and returns the replaced string. + /// + /// If no match is found, then a copy of the string is returned unchanged. + /// + /// # Replacement string syntax + /// + /// All instances of `$name` in the replacement text is replaced with the + /// corresponding capture group `name`. + /// + /// `name` may be an integer corresponding to the index of the + /// capture group (counted by order of opening parenthesis where `0` is the + /// entire match) or it can be a name (consisting of letters, digits or + /// underscores) corresponding to a named capture group. + /// + /// If `name` isn't a valid capture group (whether the name doesn't exist + /// or isn't a valid index), then it is replaced with the empty string. + /// + /// The longest possible name is used. e.g., `$1a` looks up the capture + /// group named `1a` and not the capture group at index `1`. To exert more + /// precise control over the name, use braces, e.g., `${1}a`. + /// + /// To write a literal `$` use `$$`. + /// + /// # Examples + /// + /// Note that this function is polymorphic with respect to the replacement. + /// In typical usage, this can just be a normal string: + /// + /// ```rust + /// # use fancy_regex::Regex; + /// let re = Regex::new("[^01]+").unwrap(); + /// assert_eq!(re.replace("1078910", ""), "1010"); + /// ``` + /// + /// But anything satisfying the `Replacer` trait will work. For example, + /// a closure of type `|&Captures| -> String` provides direct access to the + /// captures corresponding to a match. This allows one to access + /// capturing group matches easily: + /// + /// ```rust + /// # use fancy_regex::{Regex, Captures}; + /// let re = Regex::new(r"([^,\s]+),\s+(\S+)").unwrap(); + /// let result = re.replace("Springsteen, Bruce", |caps: &Captures| { + /// format!("{} {}", &caps[2], &caps[1]) + /// }); + /// assert_eq!(result, "Bruce Springsteen"); + /// ``` + /// + /// But this is a bit cumbersome to use all the time. Instead, a simple + /// syntax is supported that expands `$name` into the corresponding capture + /// group. Here's the last example, but using this expansion technique + /// with named capture groups: + /// + /// ```rust + /// # use fancy_regex::Regex; + /// let re = Regex::new(r"(?P[^,\s]+),\s+(?P\S+)").unwrap(); + /// let result = re.replace("Springsteen, Bruce", "$first $last"); + /// assert_eq!(result, "Bruce Springsteen"); + /// ``` + /// + /// Note that using `$2` instead of `$first` or `$1` instead of `$last` + /// would produce the same result. To write a literal `$` use `$$`. + /// + /// Sometimes the replacement string requires use of curly braces to + /// delineate a capture group replacement and surrounding literal text. + /// For example, if we wanted to join two words together with an + /// underscore: + /// + /// ```rust + /// # use fancy_regex::Regex; + /// let re = Regex::new(r"(?P\w+)\s+(?P\w+)").unwrap(); + /// let result = re.replace("deep fried", "${first}_$second"); + /// assert_eq!(result, "deep_fried"); + /// ``` + /// + /// Without the curly braces, the capture group name `first_` would be + /// used, and since it doesn't exist, it would be replaced with the empty + /// string. + /// + /// Finally, sometimes you just want to replace a literal string with no + /// regard for capturing group expansion. This can be done by wrapping a + /// byte string with `NoExpand`: + /// + /// ```rust + /// # use fancy_regex::Regex; + /// use fancy_regex::NoExpand; + /// + /// let re = Regex::new(r"(?P[^,\s]+),\s+(\S+)").unwrap(); + /// let result = re.replace("Springsteen, Bruce", NoExpand("$2 $last")); + /// assert_eq!(result, "$2 $last"); + /// ``` + pub fn replace<'t, R: Replacer>(&self, text: &'t str, rep: R) -> Cow<'t, str> { + self.replacen(text, 1, rep) + } + + /// Replaces all non-overlapping matches in `text` with the replacement + /// provided. This is the same as calling `replacen` with `limit` set to + /// `0`. + /// + /// See the documentation for `replace` for details on how to access + /// capturing group matches in the replacement string. + pub fn replace_all<'t, R: Replacer>(&self, text: &'t str, rep: R) -> Cow<'t, str> { + self.replacen(text, 0, rep) + } + + /// Replaces at most `limit` non-overlapping matches in `text` with the + /// replacement provided. If `limit` is 0, then all non-overlapping matches + /// are replaced. + /// + /// Will panic if any errors are encountered. Use `try_replacen`, which this + /// function unwraps, if you want to handle errors. + /// + /// See the documentation for `replace` for details on how to access + /// capturing group matches in the replacement string. + /// + pub fn replacen<'t, R: Replacer>(&self, text: &'t str, limit: usize, rep: R) -> Cow<'t, str> { + self.try_replacen(text, limit, rep).unwrap() + } + + /// Replaces at most `limit` non-overlapping matches in `text` with the + /// replacement provided. If `limit` is 0, then all non-overlapping matches + /// are replaced. + /// + /// Propagates any errors encountered, such as `RuntimeError::BacktrackLimitExceeded`. + /// + /// See the documentation for `replace` for details on how to access + /// capturing group matches in the replacement string. + pub fn try_replacen<'t, R: Replacer>( + &self, + text: &'t str, + limit: usize, + mut rep: R, + ) -> Result> { + // If we know that the replacement doesn't have any capture expansions, + // then we can fast path. The fast path can make a tremendous + // difference: + // + // 1) We use `find_iter` instead of `captures_iter`. Not asking for + // captures generally makes the regex engines faster. + // 2) We don't need to look up all of the capture groups and do + // replacements inside the replacement string. We just push it + // at each match and be done with it. + if let Some(rep) = rep.no_expansion() { + let mut it = self.find_iter(text).enumerate().peekable(); + if it.peek().is_none() { + return Ok(Cow::Borrowed(text)); + } + let mut new = String::with_capacity(text.len()); + let mut last_match = 0; + for (i, m) in it { + let m = m?; + + if limit > 0 && i >= limit { + break; + } + new.push_str(&text[last_match..m.start()]); + new.push_str(&rep); + last_match = m.end(); + } + new.push_str(&text[last_match..]); + return Ok(Cow::Owned(new)); + } + + // The slower path, which we use if the replacement needs access to + // capture groups. + let mut it = self.captures_iter(text).enumerate().peekable(); + if it.peek().is_none() { + return Ok(Cow::Borrowed(text)); + } + let mut new = String::with_capacity(text.len()); + let mut last_match = 0; + for (i, cap) in it { + let cap = cap?; + + if limit > 0 && i >= limit { + break; + } + // unwrap on 0 is OK because captures only reports matches + let m = cap.get(0).unwrap(); + new.push_str(&text[last_match..m.start()]); + rep.replace_append(&cap, &mut new); + last_match = m.end(); + } + new.push_str(&text[last_match..]); + Ok(Cow::Owned(new)) + } + + /// Splits the string by matches of the regex. + /// + /// Returns an iterator over the substrings of the target string + /// that *aren't* matched by the regex. + /// + /// # Example + /// + /// To split a string delimited by arbitrary amounts of spaces or tabs: + /// + /// ```rust + /// # use fancy_regex::Regex; + /// let re = Regex::new(r"[ \t]+").unwrap(); + /// let target = "a b \t c\td e"; + /// let fields: Vec<&str> = re.split(target).map(|x| x.unwrap()).collect(); + /// assert_eq!(fields, vec!["a", "b", "c", "d", "e"]); + /// ``` + pub fn split<'r, 'h>(&'r self, target: &'h str) -> Split<'r, 'h> { + Split { + matches: self.find_iter(target), + next_start: 0, + target, + } + } + + /// Splits the string by matches of the regex at most `limit` times. + /// + /// Returns an iterator over the substrings of the target string + /// that *aren't* matched by the regex. + /// + /// The `N`th substring is the remaining part of the target. + /// + /// # Example + /// + /// To split a string delimited by arbitrary amounts of spaces or tabs + /// 3 times: + /// + /// ```rust + /// # use fancy_regex::Regex; + /// let re = Regex::new(r"[ \t]+").unwrap(); + /// let target = "a b \t c\td e"; + /// let fields: Vec<&str> = re.splitn(target, 3).map(|x| x.unwrap()).collect(); + /// assert_eq!(fields, vec!["a", "b", "c\td e"]); + /// ``` + pub fn splitn<'r, 'h>(&'r self, target: &'h str, limit: usize) -> SplitN<'r, 'h> { + SplitN { + splits: self.split(target), + limit: limit, + } + } +} + +impl TryFrom<&str> for Regex { + type Error = Error; + + /// Attempts to parse a string into a regular expression + fn try_from(s: &str) -> Result { + Self::new(s) + } +} + +impl TryFrom for Regex { + type Error = Error; + + /// Attempts to parse a string into a regular expression + fn try_from(s: String) -> Result { + Self::new(&s) + } +} + +impl<'t> Match<'t> { + /// Returns the starting byte offset of the match in the text. + #[inline] + pub fn start(&self) -> usize { + self.start + } + + /// Returns the ending byte offset of the match in the text. + #[inline] + pub fn end(&self) -> usize { + self.end + } + + /// Returns the range over the starting and ending byte offsets of the match in text. + #[inline] + pub fn range(&self) -> Range { + self.start..self.end + } + + /// Returns the matched text. + #[inline] + pub fn as_str(&self) -> &'t str { + &self.text[self.start..self.end] + } + + /// Creates a new match from the given text and byte offsets. + fn new(text: &'t str, start: usize, end: usize) -> Match<'t> { + Match { text, start, end } + } +} + +impl<'t> From> for &'t str { + fn from(m: Match<'t>) -> &'t str { + m.as_str() + } +} + +impl<'t> From> for Range { + fn from(m: Match<'t>) -> Range { + m.range() + } +} + +#[allow(clippy::len_without_is_empty)] // follow regex's API +impl<'t> Captures<'t> { + /// Get the capture group by its index in the regex. + /// + /// If there is no match for that group or the index does not correspond to a group, `None` is + /// returned. The index 0 returns the whole match. + pub fn get(&self, i: usize) -> Option> { + match &self.inner { + CapturesImpl::Wrap { text, locations } => locations.get_group(i).map(|span| Match { + text, + start: span.start, + end: span.end, + }), + CapturesImpl::Fancy { text, ref saves } => { + let slot = i * 2; + if slot >= saves.len() { + return None; + } + let lo = saves[slot]; + if lo == usize::MAX { + return None; + } + let hi = saves[slot + 1]; + Some(Match { + text, + start: lo, + end: hi, + }) + } + } + } + + /// Returns the match for a named capture group. Returns `None` the capture + /// group did not match or if there is no group with the given name. + pub fn name(&self, name: &str) -> Option> { + self.named_groups.get(name).and_then(|i| self.get(*i)) + } + + /// Expands all instances of `$group` in `replacement` to the corresponding + /// capture group `name`, and writes them to the `dst` buffer given. + /// + /// `group` may be an integer corresponding to the index of the + /// capture group (counted by order of opening parenthesis where `\0` is the + /// entire match) or it can be a name (consisting of letters, digits or + /// underscores) corresponding to a named capture group. + /// + /// If `group` isn't a valid capture group (whether the name doesn't exist + /// or isn't a valid index), then it is replaced with the empty string. + /// + /// The longest possible name is used. e.g., `$1a` looks up the capture + /// group named `1a` and not the capture group at index `1`. To exert more + /// precise control over the name, use braces, e.g., `${1}a`. + /// + /// To write a literal `$`, use `$$`. + /// + /// For more control over expansion, see [`Expander`]. + /// + /// [`Expander`]: expand/struct.Expander.html + pub fn expand(&self, replacement: &str, dst: &mut String) { + Expander::default().append_expansion(dst, replacement, self); + } + + /// Iterate over the captured groups in order in which they appeared in the regex. The first + /// capture corresponds to the whole match. + pub fn iter<'c>(&'c self) -> SubCaptureMatches<'c, 't> { + SubCaptureMatches { caps: self, i: 0 } + } + + /// How many groups were captured. This is always at least 1 because group 0 returns the whole + /// match. + pub fn len(&self) -> usize { + match &self.inner { + CapturesImpl::Wrap { locations, .. } => locations.group_len(), + CapturesImpl::Fancy { saves, .. } => saves.len() / 2, + } + } +} + +/// Get a group by index. +/// +/// `'t` is the lifetime of the matched text. +/// +/// The text can't outlive the `Captures` object if this method is +/// used, because of how `Index` is defined (normally `a[i]` is part +/// of `a` and can't outlive it); to do that, use `get()` instead. +/// +/// # Panics +/// +/// If there is no group at the given index. +impl<'t> Index for Captures<'t> { + type Output = str; + + fn index(&self, i: usize) -> &str { + self.get(i) + .map(|m| m.as_str()) + .unwrap_or_else(|| panic!("no group at index '{}'", i)) + } +} + +/// Get a group by name. +/// +/// `'t` is the lifetime of the matched text and `'i` is the lifetime +/// of the group name (the index). +/// +/// The text can't outlive the `Captures` object if this method is +/// used, because of how `Index` is defined (normally `a[i]` is part +/// of `a` and can't outlive it); to do that, use `name` instead. +/// +/// # Panics +/// +/// If there is no group named by the given value. +impl<'t, 'i> Index<&'i str> for Captures<'t> { + type Output = str; + + fn index<'a>(&'a self, name: &'i str) -> &'a str { + self.name(name) + .map(|m| m.as_str()) + .unwrap_or_else(|| panic!("no group named '{}'", name)) + } +} + +impl<'c, 't> Iterator for SubCaptureMatches<'c, 't> { + type Item = Option>; + + fn next(&mut self) -> Option>> { + if self.i < self.caps.len() { + let result = self.caps.get(self.i); + self.i += 1; + Some(result) + } else { + None + } + } +} + +// TODO: might be nice to implement ExactSizeIterator etc for SubCaptures + +/// Regular expression AST. This is public for now but may change. +#[derive(Debug, PartialEq, Eq)] +pub enum Expr { + /// An empty expression, e.g. the last branch in `(a|b|)` + Empty, + /// Any character, regex `.` + Any { + /// Whether it also matches newlines or not + newline: bool, + }, + /// An assertion + Assertion(Assertion), + /// The string as a literal, e.g. `a` + Literal { + /// The string to match + val: String, + /// Whether match is case-insensitive or not + casei: bool, + }, + /// Concatenation of multiple expressions, must match in order, e.g. `a.` is a concatenation of + /// the literal `a` and `.` for any character + Concat(Vec), + /// Alternative of multiple expressions, one of them must match, e.g. `a|b` is an alternative + /// where either the literal `a` or `b` must match + Alt(Vec), + /// Capturing group of expression, e.g. `(a.)` matches `a` and any character and "captures" + /// (remembers) the match + Group(Box), + /// Look-around (e.g. positive/negative look-ahead or look-behind) with an expression, e.g. + /// `(?=a)` means the next character must be `a` (but the match is not consumed) + LookAround(Box, LookAround), + /// Repeat of an expression, e.g. `a*` or `a+` or `a{1,3}` + Repeat { + /// The expression that is being repeated + child: Box, + /// The minimum number of repetitions + lo: usize, + /// The maximum number of repetitions (or `usize::MAX`) + hi: usize, + /// Greedy means as much as possible is matched, e.g. `.*b` would match all of `abab`. + /// Non-greedy means as little as possible, e.g. `.*?b` would match only `ab` in `abab`. + greedy: bool, + }, + /// Delegate a regex to the regex crate. This is used as a simplification so that we don't have + /// to represent all the expressions in the AST, e.g. character classes. + Delegate { + /// The regex + inner: String, + /// How many characters the regex matches + size: usize, // TODO: move into analysis result + /// Whether the matching is case-insensitive or not + casei: bool, + }, + /// Back reference to a capture group, e.g. `\1` in `(abc|def)\1` references the captured group + /// and the whole regex matches either `abcabc` or `defdef`. + Backref(usize), + /// Atomic non-capturing group, e.g. `(?>ab|a)` in text that contains `ab` will match `ab` and + /// never backtrack and try `a`, even if matching fails after the atomic group. + AtomicGroup(Box), + /// Keep matched text so far out of overall match + KeepOut, + /// Anchor to match at the position where the previous match ended + ContinueFromPreviousMatchEnd, + /// Conditional expression based on whether the numbered capture group matched or not + BackrefExistsCondition(usize), + /// If/Then/Else Condition. If there is no Then/Else, these will just be empty expressions. + Conditional { + /// The conditional expression to evaluate + condition: Box, + /// What to execute if the condition is true + true_branch: Box, + /// What to execute if the condition is false + false_branch: Box, + }, +} + +/// Type of look-around assertion as used for a look-around expression. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum LookAround { + /// Look-ahead assertion, e.g. `(?=a)` + LookAhead, + /// Negative look-ahead assertion, e.g. `(?!a)` + LookAheadNeg, + /// Look-behind assertion, e.g. `(?<=a)` + LookBehind, + /// Negative look-behind assertion, e.g. `(?(vec::IntoIter>); + +impl Debug for CaptureNames<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("") + } +} + +impl<'r> Iterator for CaptureNames<'r> { + type Item = Option<&'r str>; + + fn next(&mut self) -> Option { + self.0.next() + } +} + +// silly to write my own, but this is super-fast for the common 1-digit +// case. +fn push_usize(s: &mut String, x: usize) { + if x >= 10 { + push_usize(s, x / 10); + s.push((b'0' + (x % 10) as u8) as char); + } else { + s.push((b'0' + (x as u8)) as char); + } +} + +fn is_special(c: char) -> bool { + match c { + '\\' | '.' | '+' | '*' | '?' | '(' | ')' | '|' | '[' | ']' | '{' | '}' | '^' | '$' + | '#' => true, + _ => false, + } +} + +fn push_quoted(buf: &mut String, s: &str) { + for c in s.chars() { + if is_special(c) { + buf.push('\\'); + } + buf.push(c); + } +} + +/// Escapes special characters in `text` with '\\'. Returns a string which, when interpreted +/// as a regex, matches exactly `text`. +pub fn escape(text: &str) -> Cow { + // Using bytes() is OK because all special characters are single bytes. + match text.bytes().filter(|&b| is_special(b as char)).count() { + 0 => Cow::Borrowed(text), + n => { + // The capacity calculation is exact because '\\' is a single byte. + let mut buf = String::with_capacity(text.len() + n); + push_quoted(&mut buf, text); + Cow::Owned(buf) + } + } +} + +/// Type of assertions +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Assertion { + /// Start of input text + StartText, + /// End of input text + EndText, + /// Start of a line + StartLine { + /// CRLF mode + crlf: bool, + }, + /// End of a line + EndLine { + /// CRLF mode + crlf: bool, + }, + /// Left word boundary + LeftWordBoundary, + /// Right word boundary + RightWordBoundary, + /// Both word boundaries + WordBoundary, + /// Not word boundary + NotWordBoundary, +} + +impl Assertion { + pub(crate) fn is_hard(&self) -> bool { + use Assertion::*; + matches!( + self, + // these will make regex-automata use PikeVM + LeftWordBoundary | RightWordBoundary | WordBoundary | NotWordBoundary + ) + } +} + +impl Expr { + /// Parse the regex and return an expression (AST) and a bit set with the indexes of groups + /// that are referenced by backrefs. + pub fn parse_tree(re: &str) -> Result { + Parser::parse(re) + } + + /// Convert expression to a regex string in the regex crate's syntax. + /// + /// # Panics + /// + /// Panics for expressions that are hard, i.e. can not be handled by the regex crate. + pub fn to_str(&self, buf: &mut String, precedence: u8) { + match *self { + Expr::Empty => (), + Expr::Any { newline } => buf.push_str(if newline { "(?s:.)" } else { "." }), + Expr::Literal { ref val, casei } => { + if casei { + buf.push_str("(?i:"); + } + push_quoted(buf, val); + if casei { + buf.push_str(")"); + } + } + Expr::Assertion(Assertion::StartText) => buf.push('^'), + Expr::Assertion(Assertion::EndText) => buf.push('$'), + Expr::Assertion(Assertion::StartLine { crlf: false }) => buf.push_str("(?m:^)"), + Expr::Assertion(Assertion::EndLine { crlf: false }) => buf.push_str("(?m:$)"), + Expr::Assertion(Assertion::StartLine { crlf: true }) => buf.push_str("(?Rm:^)"), + Expr::Assertion(Assertion::EndLine { crlf: true }) => buf.push_str("(?Rm:$)"), + Expr::Concat(ref children) => { + if precedence > 1 { + buf.push_str("(?:"); + } + for child in children { + child.to_str(buf, 2); + } + if precedence > 1 { + buf.push(')') + } + } + Expr::Alt(ref children) => { + if precedence > 0 { + buf.push_str("(?:"); + } + for (i, child) in children.iter().enumerate() { + if i != 0 { + buf.push('|'); + } + child.to_str(buf, 1); + } + if precedence > 0 { + buf.push(')'); + } + } + Expr::Group(ref child) => { + buf.push('('); + child.to_str(buf, 0); + buf.push(')'); + } + Expr::Repeat { + ref child, + lo, + hi, + greedy, + } => { + if precedence > 2 { + buf.push_str("(?:"); + } + child.to_str(buf, 3); + match (lo, hi) { + (0, 1) => buf.push('?'), + (0, usize::MAX) => buf.push('*'), + (1, usize::MAX) => buf.push('+'), + (lo, hi) => { + buf.push('{'); + push_usize(buf, lo); + if lo != hi { + buf.push(','); + if hi != usize::MAX { + push_usize(buf, hi); + } + } + buf.push('}'); + } + } + if !greedy { + buf.push('?'); + } + if precedence > 2 { + buf.push(')'); + } + } + Expr::Delegate { + ref inner, casei, .. + } => { + // at the moment, delegate nodes are just atoms + if casei { + buf.push_str("(?i:"); + } + buf.push_str(inner); + if casei { + buf.push_str(")"); + } + } + _ => panic!("attempting to format hard expr"), + } + } +} + +// precondition: ix > 0 +fn prev_codepoint_ix(s: &str, mut ix: usize) -> usize { + let bytes = s.as_bytes(); + loop { + ix -= 1; + // fancy bit magic for ranges 0..0x80 + 0xc0.. + if (bytes[ix] as i8) >= -0x40 { + break; + } + } + ix +} + +fn codepoint_len(b: u8) -> usize { + match b { + b if b < 0x80 => 1, + b if b < 0xe0 => 2, + b if b < 0xf0 => 3, + _ => 4, + } +} + +/// Returns the smallest possible index of the next valid UTF-8 sequence +/// starting after `i`. +/// Adapted from a function with the same name in the `regex` crate. +fn next_utf8(text: &str, i: usize) -> usize { + let b = match text.as_bytes().get(i) { + None => return i + 1, + Some(&b) => b, + }; + i + codepoint_len(b) +} + +// If this returns false, then there is no possible backref in the re + +// Both potential implementations are turned off, because we currently +// always need to do a deeper analysis because of 1-character +// look-behind. If we could call a find_from_pos method of regex::Regex, +// it would make sense to bring this back. +/* +pub fn detect_possible_backref(re: &str) -> bool { + let mut last = b'\x00'; + for b in re.as_bytes() { + if b'0' <= *b && *b <= b'9' && last == b'\\' { return true; } + last = *b; + } + false +} + +pub fn detect_possible_backref(re: &str) -> bool { + let mut bytes = re.as_bytes(); + loop { + match memchr::memchr(b'\\', &bytes[..bytes.len() - 1]) { + Some(i) => { + bytes = &bytes[i + 1..]; + let c = bytes[0]; + if b'0' <= c && c <= b'9' { return true; } + } + None => return false + } + } +} +*/ + +/// The internal module only exists so that the toy example can access internals for debugging and +/// experimenting. +#[doc(hidden)] +pub mod internal { + pub use crate::analyze::analyze; + pub use crate::compile::compile; + pub use crate::vm::{run_default, run_trace, Insn, Prog}; +} + +#[cfg(test)] +mod tests { + use alloc::borrow::Cow; + use alloc::boxed::Box; + use alloc::string::String; + use alloc::{format, vec}; + + use crate::parse::make_literal; + use crate::{Expr, Regex}; + + //use detect_possible_backref; + + // tests for to_str + + fn to_str(e: Expr) -> String { + let mut s = String::new(); + e.to_str(&mut s, 0); + s + } + + #[test] + fn to_str_concat_alt() { + let e = Expr::Concat(vec![ + Expr::Alt(vec![make_literal("a"), make_literal("b")]), + make_literal("c"), + ]); + assert_eq!(to_str(e), "(?:a|b)c"); + } + + #[test] + fn to_str_rep_concat() { + let e = Expr::Repeat { + child: Box::new(Expr::Concat(vec![make_literal("a"), make_literal("b")])), + lo: 2, + hi: 3, + greedy: true, + }; + assert_eq!(to_str(e), "(?:ab){2,3}"); + } + + #[test] + fn to_str_group_alt() { + let e = Expr::Group(Box::new(Expr::Alt(vec![ + make_literal("a"), + make_literal("b"), + ]))); + assert_eq!(to_str(e), "(a|b)"); + } + + #[test] + fn as_str_debug() { + let s = r"(a+)b\1"; + let regex = Regex::new(s).unwrap(); + assert_eq!(s, regex.as_str()); + assert_eq!(s, format!("{:?}", regex)); + } + + #[test] + fn display() { + let s = r"(a+)b\1"; + let regex = Regex::new(s).unwrap(); + assert_eq!(s, format!("{}", regex)); + } + + #[test] + fn from_str() { + let s = r"(a+)b\1"; + let regex = s.parse::().unwrap(); + assert_eq!(regex.as_str(), s); + } + + #[test] + fn to_str_repeat() { + fn repeat(lo: usize, hi: usize, greedy: bool) -> Expr { + Expr::Repeat { + child: Box::new(make_literal("a")), + lo, + hi, + greedy, + } + } + + assert_eq!(to_str(repeat(2, 2, true)), "a{2}"); + assert_eq!(to_str(repeat(2, 2, false)), "a{2}?"); + assert_eq!(to_str(repeat(2, 3, true)), "a{2,3}"); + assert_eq!(to_str(repeat(2, 3, false)), "a{2,3}?"); + assert_eq!(to_str(repeat(2, usize::MAX, true)), "a{2,}"); + assert_eq!(to_str(repeat(2, usize::MAX, false)), "a{2,}?"); + assert_eq!(to_str(repeat(0, 1, true)), "a?"); + assert_eq!(to_str(repeat(0, 1, false)), "a??"); + assert_eq!(to_str(repeat(0, usize::MAX, true)), "a*"); + assert_eq!(to_str(repeat(0, usize::MAX, false)), "a*?"); + assert_eq!(to_str(repeat(1, usize::MAX, true)), "a+"); + assert_eq!(to_str(repeat(1, usize::MAX, false)), "a+?"); + } + + #[test] + fn escape() { + // Check that strings that need no quoting are borrowed, and that non-special punctuation + // is not quoted. + match crate::escape("@foo") { + Cow::Borrowed(s) => assert_eq!(s, "@foo"), + _ => panic!("Value should be borrowed."), + } + + // Check typical usage. + assert_eq!(crate::escape("fo*o").into_owned(), "fo\\*o"); + + // Check that multibyte characters are handled correctly. + assert_eq!(crate::escape("fø*ø").into_owned(), "fø\\*ø"); + } + + /* + #[test] + fn detect_backref() { + assert_eq!(detect_possible_backref("a0a1a2"), false); + assert_eq!(detect_possible_backref("a0a1\\a2"), false); + assert_eq!(detect_possible_backref("a0a\\1a2"), true); + assert_eq!(detect_possible_backref("a0a1a2\\"), false); + } + */ +} diff --git a/tools/vendor/fancy-regex/src/parse.rs b/tools/vendor/fancy-regex/src/parse.rs new file mode 100644 index 0000000000..c04a426fa6 --- /dev/null +++ b/tools/vendor/fancy-regex/src/parse.rs @@ -0,0 +1,1817 @@ +// Copyright 2016 The Fancy Regex Authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//! A regex parser yielding an AST. + +use alloc::boxed::Box; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use alloc::{format, vec}; + +use bit_set::BitSet; +use core::convert::TryInto; +use core::usize; +use regex_syntax::escape_into; + +use crate::{codepoint_len, CompileError, Error, Expr, ParseError, Result, MAX_RECURSION}; +use crate::{Assertion, LookAround::*}; + +const FLAG_CASEI: u32 = 1; +const FLAG_MULTI: u32 = 1 << 1; +const FLAG_DOTNL: u32 = 1 << 2; +const FLAG_SWAP_GREED: u32 = 1 << 3; +const FLAG_IGNORE_SPACE: u32 = 1 << 4; +const FLAG_UNICODE: u32 = 1 << 5; + +#[cfg(not(feature = "std"))] +pub(crate) type NamedGroups = alloc::collections::BTreeMap; +#[cfg(feature = "std")] +pub(crate) type NamedGroups = std::collections::HashMap; + +#[derive(Debug)] +pub struct ExprTree { + pub expr: Expr, + pub backrefs: BitSet, + pub named_groups: NamedGroups, +} + +#[derive(Debug)] +pub(crate) struct Parser<'a> { + re: &'a str, // source + backrefs: BitSet, + flags: u32, + named_groups: NamedGroups, + numeric_backrefs: bool, + curr_group: usize, // need to keep track of which group number we're parsing +} + +impl<'a> Parser<'a> { + /// Parse the regex and return an expression (AST) and a bit set with the indexes of groups + /// that are referenced by backrefs. + pub(crate) fn parse(re: &str) -> Result { + let mut p = Parser::new(re); + let (ix, expr) = p.parse_re(0, 0)?; + if ix < re.len() { + return Err(Error::ParseError( + ix, + ParseError::GeneralParseError("end of string not reached".to_string()), + )); + } + Ok(ExprTree { + expr, + backrefs: Default::default(), + named_groups: p.named_groups, + }) + } + + fn new(re: &str) -> Parser<'_> { + Parser { + re, + backrefs: Default::default(), + named_groups: Default::default(), + numeric_backrefs: false, + flags: FLAG_UNICODE, + curr_group: 0, + } + } + + fn parse_re(&mut self, ix: usize, depth: usize) -> Result<(usize, Expr)> { + let (ix, child) = self.parse_branch(ix, depth)?; + let mut ix = self.optional_whitespace(ix)?; + if self.re[ix..].starts_with('|') { + let mut children = vec![child]; + while self.re[ix..].starts_with('|') { + ix += 1; + let (next, child) = self.parse_branch(ix, depth)?; + children.push(child); + ix = self.optional_whitespace(next)?; + } + return Ok((ix, Expr::Alt(children))); + } + // can't have numeric backrefs and named backrefs + if self.numeric_backrefs && !self.named_groups.is_empty() { + return Err(Error::CompileError(CompileError::NamedBackrefOnly)); + } + Ok((ix, child)) + } + + fn parse_branch(&mut self, ix: usize, depth: usize) -> Result<(usize, Expr)> { + let mut children = Vec::new(); + let mut ix = ix; + while ix < self.re.len() { + let (next, child) = self.parse_piece(ix, depth)?; + if next == ix { + break; + } + if child != Expr::Empty { + children.push(child); + } + ix = next; + } + match children.len() { + 0 => Ok((ix, Expr::Empty)), + 1 => Ok((ix, children.pop().unwrap())), + _ => Ok((ix, Expr::Concat(children))), + } + } + + fn parse_piece(&mut self, ix: usize, depth: usize) -> Result<(usize, Expr)> { + let (ix, child) = self.parse_atom(ix, depth)?; + let mut ix = self.optional_whitespace(ix)?; + if ix < self.re.len() { + // fail when child is empty? + let (lo, hi) = match self.re.as_bytes()[ix] { + b'?' => (0, 1), + b'*' => (0, usize::MAX), + b'+' => (1, usize::MAX), + b'{' => { + match self.parse_repeat(ix) { + Ok((next, lo, hi)) => { + ix = next - 1; + (lo, hi) + } + Err(_) => { + // Invalid repeat syntax, which results in `{` being treated as a literal + return Ok((ix, child)); + } + } + } + _ => return Ok((ix, child)), + }; + if !self.is_repeatable(&child) { + return Err(Error::ParseError(ix, ParseError::TargetNotRepeatable)); + } + ix += 1; + ix = self.optional_whitespace(ix)?; + let mut greedy = true; + if ix < self.re.len() && self.re.as_bytes()[ix] == b'?' { + greedy = false; + ix += 1; + } + greedy ^= self.flag(FLAG_SWAP_GREED); + let mut node = Expr::Repeat { + child: Box::new(child), + lo, + hi, + greedy, + }; + if ix < self.re.len() && self.re.as_bytes()[ix] == b'+' { + ix += 1; + node = Expr::AtomicGroup(Box::new(node)); + } + return Ok((ix, node)); + } + Ok((ix, child)) + } + + fn is_repeatable(&self, child: &Expr) -> bool { + match child { + Expr::LookAround(_, _) => false, + Expr::Empty => false, + Expr::Assertion(_) => false, + _ => true, + } + } + + // ix, lo, hi + fn parse_repeat(&self, ix: usize) -> Result<(usize, usize, usize)> { + let ix = self.optional_whitespace(ix + 1)?; // skip opening '{' + let bytes = self.re.as_bytes(); + if ix == self.re.len() { + return Err(Error::ParseError(ix, ParseError::InvalidRepeat)); + } + let mut end = ix; + let lo = if bytes[ix] == b',' { + 0 + } else if let Some((next, lo)) = parse_decimal(self.re, ix) { + end = next; + lo + } else { + return Err(Error::ParseError(ix, ParseError::InvalidRepeat)); + }; + let ix = self.optional_whitespace(end)?; // past lo number + if ix == self.re.len() { + return Err(Error::ParseError(ix, ParseError::InvalidRepeat)); + } + end = ix; + let hi = match bytes[ix] { + b'}' => lo, + b',' => { + end = self.optional_whitespace(ix + 1)?; // past ',' + if let Some((next, hi)) = parse_decimal(self.re, end) { + end = next; + hi + } else { + usize::MAX + } + } + _ => return Err(Error::ParseError(ix, ParseError::InvalidRepeat)), + }; + let ix = self.optional_whitespace(end)?; // past hi number + if ix == self.re.len() || bytes[ix] != b'}' { + return Err(Error::ParseError(ix, ParseError::InvalidRepeat)); + } + Ok((ix + 1, lo, hi)) + } + + fn parse_atom(&mut self, ix: usize, depth: usize) -> Result<(usize, Expr)> { + let ix = self.optional_whitespace(ix)?; + if ix == self.re.len() { + return Ok((ix, Expr::Empty)); + } + match self.re.as_bytes()[ix] { + b'.' => Ok(( + ix + 1, + Expr::Any { + newline: self.flag(FLAG_DOTNL), + }, + )), + b'^' => Ok(( + ix + 1, + if self.flag(FLAG_MULTI) { + // TODO: support crlf flag + Expr::Assertion(Assertion::StartLine { crlf: false }) + } else { + Expr::Assertion(Assertion::StartText) + }, + )), + b'$' => Ok(( + ix + 1, + if self.flag(FLAG_MULTI) { + // TODO: support crlf flag + Expr::Assertion(Assertion::EndLine { crlf: false }) + } else { + Expr::Assertion(Assertion::EndText) + }, + )), + b'(' => self.parse_group(ix, depth), + b'\\' => { + let (next, expr) = self.parse_escape(ix, false)?; + if let Expr::Backref(group) = expr { + self.backrefs.insert(group); + } + Ok((next, expr)) + } + b'+' | b'*' | b'?' | b'|' | b')' => Ok((ix, Expr::Empty)), + b'[' => self.parse_class(ix), + b => { + // TODO: maybe want to match multiple codepoints? + let next = ix + codepoint_len(b); + Ok(( + next, + Expr::Literal { + val: String::from(&self.re[ix..next]), + casei: self.flag(FLAG_CASEI), + }, + )) + } + } + } + + fn parse_named_backref( + &self, + ix: usize, + open: &str, + close: &str, + allow_relative: bool, + ) -> Result<(usize, Expr)> { + if let Some((id, skip)) = parse_id(&self.re[ix..], open, close, allow_relative) { + let group = if let Some(group) = self.named_groups.get(id) { + Some(*group) + } else if let Ok(group) = id.parse::() { + group.try_into().map_or_else( + |_| { + // relative backref + self.curr_group.checked_add_signed(group + 1) + }, + |group| Some(group), + ) + } else { + None + }; + if let Some(group) = group { + return Ok((ix + skip, Expr::Backref(group))); + } + // here the name is parsed but it is invalid + Err(Error::ParseError( + ix, + ParseError::InvalidGroupNameBackref(id.to_string()), + )) + } else { + // in this case the name can't be parsed + Err(Error::ParseError(ix, ParseError::InvalidGroupName)) + } + } + + fn parse_numbered_backref(&mut self, ix: usize) -> Result<(usize, Expr)> { + if let Some((end, group)) = parse_decimal(self.re, ix) { + // protect BitSet against unreasonably large value + if group < self.re.len() / 2 { + self.numeric_backrefs = true; + return Ok((end, Expr::Backref(group))); + } + } + return Err(Error::ParseError(ix, ParseError::InvalidBackref)); + } + + // ix points to \ character + fn parse_escape(&mut self, ix: usize, in_class: bool) -> Result<(usize, Expr)> { + let bytes = self.re.as_bytes(); + let Some(b) = bytes.get(ix + 1).copied() else { + return Err(Error::ParseError(ix, ParseError::TrailingBackslash)); + }; + let end = ix + 1 + codepoint_len(b); + Ok(if is_digit(b) { + return self.parse_numbered_backref(ix + 1); + } else if matches!(b, b'k') && !in_class { + // Named backref: \k + if bytes.get(end) == Some(&b'\'') { + return self.parse_named_backref(end, "'", "'", true); + } else { + return self.parse_named_backref(end, "<", ">", true); + } + } else if b == b'A' && !in_class { + (end, Expr::Assertion(Assertion::StartText)) + } else if b == b'z' && !in_class { + (end, Expr::Assertion(Assertion::EndText)) + } else if b == b'b' && !in_class { + if bytes.get(end) == Some(&b'{') { + // Support for \b{...} is not implemented yet + return Err(Error::ParseError( + ix, + ParseError::InvalidEscape(format!("\\{}", &self.re[ix + 1..end])), + )); + } + (end, Expr::Assertion(Assertion::WordBoundary)) + } else if b == b'B' && !in_class { + if bytes.get(end) == Some(&b'{') { + // Support for \b{...} is not implemented yet + return Err(Error::ParseError( + ix, + ParseError::InvalidEscape(format!("\\{}", &self.re[ix + 1..end])), + )); + } + (end, Expr::Assertion(Assertion::NotWordBoundary)) + } else if b == b'<' && !in_class { + (end, Expr::Assertion(Assertion::LeftWordBoundary)) + } else if b == b'>' && !in_class { + (end, Expr::Assertion(Assertion::RightWordBoundary)) + } else if matches!(b | 32, b'd' | b's' | b'w') { + ( + end, + Expr::Delegate { + inner: String::from(&self.re[ix..end]), + size: 1, + casei: self.flag(FLAG_CASEI), + }, + ) + } else if (b | 32) == b'h' { + let s = if b == b'h' { + "[0-9A-Fa-f]" + } else { + "[^0-9A-Fa-f]" + }; + ( + end, + Expr::Delegate { + inner: String::from(s), + size: 1, + casei: false, + }, + ) + } else if b == b'x' { + return self.parse_hex(end, 2); + } else if b == b'u' { + return self.parse_hex(end, 4); + } else if b == b'U' { + return self.parse_hex(end, 8); + } else if (b | 32) == b'p' && end != bytes.len() { + let mut end = end; + let b = bytes[end]; + end += codepoint_len(b); + if b == b'{' { + loop { + if end == self.re.len() { + return Err(Error::ParseError(ix, ParseError::UnclosedUnicodeName)); + } + let b = bytes[end]; + if b == b'}' { + end += 1; + break; + } + end += codepoint_len(b); + } + } + ( + end, + Expr::Delegate { + inner: String::from(&self.re[ix..end]), + size: 1, + casei: self.flag(FLAG_CASEI), + }, + ) + } else if b == b'K' && !in_class { + (end, Expr::KeepOut) + } else if b == b'G' && !in_class { + (end, Expr::ContinueFromPreviousMatchEnd) + } else { + // printable ASCII (including space, see issue #29) + ( + end, + make_literal(match b { + b'a' => "\x07", // BEL + b'b' => "\x08", // BS + b'f' => "\x0c", // FF + b'n' => "\n", // LF + b'r' => "\r", // CR + b't' => "\t", // TAB + b'v' => "\x0b", // VT + b'e' => "\x1b", // ESC + b' ' => " ", + b => { + let s = &self.re[ix + 1..end]; + if b.is_ascii_alphabetic() + && !matches!( + b, + b'k' | b'A' | b'z' | b'b' | b'B' | b'<' | b'>' | b'K' | b'G' + ) + { + return Err(Error::ParseError( + ix, + ParseError::InvalidEscape(format!("\\{}", s)), + )); + } else { + s + } + } + }), + ) + }) + } + + // ix points after '\x', eg to 'A0' or '{12345}', or after `\u` or `\U` + fn parse_hex(&self, ix: usize, digits: usize) -> Result<(usize, Expr)> { + if ix >= self.re.len() { + // Incomplete escape sequence + return Err(Error::ParseError(ix, ParseError::InvalidHex)); + } + let bytes = self.re.as_bytes(); + let b = bytes[ix]; + let (end, s) = if ix + digits <= self.re.len() + && bytes[ix..ix + digits].iter().all(|&b| is_hex_digit(b)) + { + let end = ix + digits; + (end, &self.re[ix..end]) + } else if b == b'{' { + let starthex = ix + 1; + let mut endhex = starthex; + loop { + if endhex == self.re.len() { + return Err(Error::ParseError(ix, ParseError::InvalidHex)); + } + let b = bytes[endhex]; + if endhex > starthex && b == b'}' { + break; + } + if is_hex_digit(b) && endhex < starthex + 8 { + endhex += 1; + } else { + return Err(Error::ParseError(ix, ParseError::InvalidHex)); + } + } + (endhex + 1, &self.re[starthex..endhex]) + } else { + return Err(Error::ParseError(ix, ParseError::InvalidHex)); + }; + let codepoint = u32::from_str_radix(s, 16).unwrap(); + if let Some(c) = char::from_u32(codepoint) { + let mut inner = String::with_capacity(4); + inner.push(c); + Ok(( + end, + Expr::Literal { + val: inner, + casei: self.flag(FLAG_CASEI), + }, + )) + } else { + Err(Error::ParseError(ix, ParseError::InvalidCodepointValue)) + } + } + + fn parse_class(&mut self, ix: usize) -> Result<(usize, Expr)> { + let bytes = self.re.as_bytes(); + let mut ix = ix + 1; // skip opening '[' + let mut class = String::new(); + let mut nest = 1; + class.push('['); + + // Negated character class + if bytes.get(ix) == Some(&b'^') { + class.push('^'); + ix += 1; + } + + // `]` does not have to be escaped after opening `[` or `[^` + if bytes.get(ix) == Some(&b']') { + class.push(']'); + ix += 1; + } + + loop { + if ix == self.re.len() { + return Err(Error::ParseError(ix, ParseError::InvalidClass)); + } + let end = match bytes[ix] { + b'\\' => { + // We support more escapes than regex, so parse it ourselves before delegating. + let (end, expr) = self.parse_escape(ix, true)?; + match expr { + Expr::Literal { val, .. } => { + debug_assert_eq!(val.chars().count(), 1); + escape_into(&val, &mut class); + } + Expr::Delegate { inner, .. } => { + class.push_str(&inner); + } + _ => { + return Err(Error::ParseError(ix, ParseError::InvalidClass)); + } + } + end + } + b'[' => { + nest += 1; + class.push('['); + ix + 1 + } + b']' => { + nest -= 1; + class.push(']'); + if nest == 0 { + break; + } + ix + 1 + } + b => { + let end = ix + codepoint_len(b); + class.push_str(&self.re[ix..end]); + end + } + }; + ix = end; + } + let class = Expr::Delegate { + inner: class, + size: 1, + casei: self.flag(FLAG_CASEI), + }; + let ix = ix + 1; // skip closing ']' + Ok((ix, class)) + } + + fn parse_group(&mut self, ix: usize, depth: usize) -> Result<(usize, Expr)> { + let depth = depth + 1; + if depth >= MAX_RECURSION { + return Err(Error::ParseError(ix, ParseError::RecursionExceeded)); + } + let ix = self.optional_whitespace(ix + 1)?; + let (la, skip) = if self.re[ix..].starts_with("?=") { + (Some(LookAhead), 2) + } else if self.re[ix..].starts_with("?!") { + (Some(LookAheadNeg), 2) + } else if self.re[ix..].starts_with("?<=") { + (Some(LookBehind), 3) + } else if self.re[ix..].starts_with("?...) + self.curr_group += 1; + if let Some((id, skip)) = parse_id(&self.re[ix + 1..], "<", ">", false) { + self.named_groups.insert(id.to_string(), self.curr_group); + (None, skip + 1) + } else { + return Err(Error::ParseError(ix, ParseError::InvalidGroupName)); + } + } else if self.re[ix..].starts_with("?P<") { + // Named capture group using Python syntax: (?P...) + self.curr_group += 1; // this is a capture group + if let Some((id, skip)) = parse_id(&self.re[ix + 2..], "<", ">", false) { + self.named_groups.insert(id.to_string(), self.curr_group); + (None, skip + 2) + } else { + return Err(Error::ParseError(ix, ParseError::InvalidGroupName)); + } + } else if self.re[ix..].starts_with("?P=") { + // Backref using Python syntax: (?P=name) + return self.parse_named_backref(ix + 3, "", ")", false); + } else if self.re[ix..].starts_with("?>") { + (None, 2) + } else if self.re[ix..].starts_with("?(") { + return self.parse_conditional(ix + 2, depth); + } else if self.re[ix..].starts_with('?') { + return self.parse_flags(ix, depth); + } else { + self.curr_group += 1; // this is a capture group + (None, 0) + }; + let ix = ix + skip; + let (ix, child) = self.parse_re(ix, depth)?; + let ix = self.check_for_close_paren(ix)?; + let result = match (la, skip) { + (Some(la), _) => Expr::LookAround(Box::new(child), la), + (None, 2) => Expr::AtomicGroup(Box::new(child)), + _ => Expr::Group(Box::new(child)), + }; + Ok((ix, result)) + } + + fn check_for_close_paren(&self, ix: usize) -> Result { + let ix = self.optional_whitespace(ix)?; + if ix == self.re.len() { + return Err(Error::ParseError(ix, ParseError::UnclosedOpenParen)); + } else if self.re.as_bytes()[ix] != b')' { + return Err(Error::ParseError( + ix, + ParseError::GeneralParseError("expected close paren".to_string()), + )); + } + Ok(ix + 1) + } + + // ix points to `?` in `(?` + fn parse_flags(&mut self, ix: usize, depth: usize) -> Result<(usize, Expr)> { + let start = ix + 1; + + fn unknown_flag(re: &str, start: usize, end: usize) -> Error { + let after_end = end + codepoint_len(re.as_bytes()[end]); + let s = format!("(?{}", &re[start..after_end]); + Error::ParseError(start, ParseError::UnknownFlag(s)) + } + + let mut ix = start; + let mut neg = false; + let oldflags = self.flags; + loop { + ix = self.optional_whitespace(ix)?; + if ix == self.re.len() { + return Err(Error::ParseError(ix, ParseError::UnclosedOpenParen)); + } + let b = self.re.as_bytes()[ix]; + match b { + b'i' => self.update_flag(FLAG_CASEI, neg), + b'm' => self.update_flag(FLAG_MULTI, neg), + b's' => self.update_flag(FLAG_DOTNL, neg), + b'U' => self.update_flag(FLAG_SWAP_GREED, neg), + b'x' => self.update_flag(FLAG_IGNORE_SPACE, neg), + b'u' => { + if neg { + return Err(Error::ParseError(ix, ParseError::NonUnicodeUnsupported)); + } + } + b'-' => { + if neg { + return Err(unknown_flag(self.re, start, ix)); + } + neg = true; + } + b')' => { + if ix == start || neg && ix == start + 1 { + return Err(unknown_flag(self.re, start, ix)); + } + return Ok((ix + 1, Expr::Empty)); + } + b':' => { + if neg && ix == start + 1 { + return Err(unknown_flag(self.re, start, ix)); + } + ix += 1; + let (ix, child) = self.parse_re(ix, depth)?; + if ix == self.re.len() { + return Err(Error::ParseError(ix, ParseError::UnclosedOpenParen)); + } else if self.re.as_bytes()[ix] != b')' { + return Err(Error::ParseError( + ix, + ParseError::GeneralParseError("expected close paren".to_string()), + )); + }; + self.flags = oldflags; + return Ok((ix + 1, child)); + } + _ => return Err(unknown_flag(self.re, start, ix)), + } + ix += 1; + } + } + + // ix points to after the last ( in (?( + fn parse_conditional(&mut self, ix: usize, depth: usize) -> Result<(usize, Expr)> { + if ix >= self.re.len() { + return Err(Error::ParseError(ix, ParseError::UnclosedOpenParen)); + } + let bytes = self.re.as_bytes(); + // get the character after the open paren + let b = bytes[ix]; + let (mut next, condition) = if is_digit(b) { + self.parse_numbered_backref(ix)? + } else if b == b'\'' { + self.parse_named_backref(ix, "'", "'", true)? + } else if b == b'<' { + self.parse_named_backref(ix, "<", ">", true)? + } else { + self.parse_re(ix, depth)? + }; + next = self.check_for_close_paren(next)?; + let (end, child) = self.parse_re(next, depth)?; + if end == next { + // Backreference validity checker + if let Expr::Backref(group) = condition { + let after = self.check_for_close_paren(end)?; + return Ok((after, Expr::BackrefExistsCondition(group))); + } else { + return Err(Error::ParseError( + end, + ParseError::GeneralParseError( + "expected conditional to be a backreference or at least an expression for when the condition is true".to_string() + ) + )); + } + } + let if_true: Expr; + let mut if_false: Expr = Expr::Empty; + if let Expr::Alt(mut alternatives) = child { + // the truth branch will be the first alternative + if_true = alternatives.remove(0); + // if there is only one alternative left, take it out the Expr::Alt + if alternatives.len() == 1 { + if_false = alternatives.pop().expect("expected 2 alternatives"); + } else { + // otherwise the remaining branches become the false branch + if_false = Expr::Alt(alternatives); + } + } else { + // there is only one branch - the truth branch. i.e. "if" without "else" + if_true = child; + } + let inner_condition = if let Expr::Backref(group) = condition { + Expr::BackrefExistsCondition(group) + } else { + condition + }; + + let after = self.check_for_close_paren(end)?; + Ok(( + after, + if if_true == Expr::Empty && if_false == Expr::Empty { + inner_condition + } else { + Expr::Conditional { + condition: Box::new(inner_condition), + true_branch: Box::new(if_true), + false_branch: Box::new(if_false), + } + }, + )) + } + + fn flag(&self, flag: u32) -> bool { + (self.flags & flag) != 0 + } + + fn update_flag(&mut self, flag: u32, neg: bool) { + if neg { + self.flags &= !flag; + } else { + self.flags |= flag; + } + } + + fn optional_whitespace(&self, mut ix: usize) -> Result { + let bytes = self.re.as_bytes(); + loop { + if ix == self.re.len() { + return Ok(ix); + } + match bytes[ix] { + b'#' if self.flag(FLAG_IGNORE_SPACE) => { + match bytes[ix..].iter().position(|&c| c == b'\n') { + Some(x) => ix += x + 1, + None => return Ok(self.re.len()), + } + } + b' ' | b'\r' | b'\n' | b'\t' if self.flag(FLAG_IGNORE_SPACE) => ix += 1, + b'(' if bytes[ix..].starts_with(b"(?#") => { + ix += 3; + loop { + if ix >= self.re.len() { + return Err(Error::ParseError(ix, ParseError::UnclosedOpenParen)); + } + match bytes[ix] { + b')' => { + ix += 1; + break; + } + b'\\' => ix += 2, + _ => ix += 1, + } + } + } + _ => return Ok(ix), + } + } + } +} + +// return (ix, value) +pub(crate) fn parse_decimal(s: &str, ix: usize) -> Option<(usize, usize)> { + let mut end = ix; + while end < s.len() && is_digit(s.as_bytes()[end]) { + end += 1; + } + usize::from_str_radix(&s[ix..end], 10) + .ok() + .map(|val| (end, val)) +} + +/// Attempts to parse an identifier between the specified opening and closing +/// delimiters. On success, returns `Some((id, skip))`, where `skip` is how much +/// of the string was used. +pub(crate) fn parse_id<'a>( + s: &'a str, + open: &'_ str, + close: &'_ str, + allow_relative: bool, +) -> Option<(&'a str, usize)> { + debug_assert!(!close.starts_with(is_id_char)); + + if !s.starts_with(open) { + return None; + } + + let id_start = open.len(); + let mut iter = s[id_start..].char_indices().peekable(); + let after_id = if allow_relative && iter.next_if(|(_, ch)| *ch == '-').is_some() { + iter.find(|(_, ch)| !ch.is_ascii_digit()) + } else { + iter.find(|(_, ch)| !is_id_char(*ch)) + }; + let id_len = match after_id.map(|(i, _)| i) { + Some(id_len) if s[id_start + id_len..].starts_with(close) => Some(id_len), + None if close.is_empty() => Some(s.len()), + _ => None, + }; + match id_len { + Some(0) => None, + Some(id_len) => { + let id_end = id_start + id_len; + Some((&s[id_start..id_end], id_end + close.len())) + } + _ => None, + } +} + +fn is_id_char(c: char) -> bool { + c.is_alphanumeric() || c == '_' +} + +fn is_digit(b: u8) -> bool { + b'0' <= b && b <= b'9' +} + +fn is_hex_digit(b: u8) -> bool { + is_digit(b) || (b'a' <= (b | 32) && (b | 32) <= b'f') +} + +pub(crate) fn make_literal(s: &str) -> Expr { + Expr::Literal { + val: String::from(s), + casei: false, + } +} + +#[cfg(test)] +mod tests { + use alloc::boxed::Box; + use alloc::string::{String, ToString}; + use alloc::{format, vec}; + + use crate::parse::{make_literal, parse_id}; + use crate::LookAround::*; + use crate::{Assertion, Expr}; + + fn p(s: &str) -> Expr { + Expr::parse_tree(s).unwrap().expr + } + + #[cfg_attr(feature = "track_caller", track_caller)] + fn fail(s: &str) { + assert!( + Expr::parse_tree(s).is_err(), + "Expected parse error, but was: {:?}", + Expr::parse_tree(s) + ); + } + + #[cfg_attr(feature = "track_caller", track_caller)] + fn assert_error(re: &str, expected_error: &str) { + let result = Expr::parse_tree(re); + assert!( + result.is_err(), + "Expected parse error, but was: {:?}", + result + ); + assert_eq!(&format!("{}", result.err().unwrap()), expected_error); + } + + #[test] + fn empty() { + assert_eq!(p(""), Expr::Empty); + } + + #[test] + fn any() { + assert_eq!(p("."), Expr::Any { newline: false }); + assert_eq!(p("(?s:.)"), Expr::Any { newline: true }); + } + + #[test] + fn start_text() { + assert_eq!(p("^"), Expr::Assertion(Assertion::StartText)); + } + + #[test] + fn end_text() { + assert_eq!(p("$"), Expr::Assertion(Assertion::EndText)); + } + + #[test] + fn literal() { + assert_eq!(p("a"), make_literal("a")); + } + + #[test] + fn literal_special() { + assert_eq!(p("}"), make_literal("}")); + assert_eq!(p("]"), make_literal("]")); + } + + #[test] + fn parse_id_test() { + assert_eq!(parse_id("foo.", "", "", true), Some(("foo", 3))); + assert_eq!(parse_id("{foo}", "{", "}", true), Some(("foo", 5))); + assert_eq!(parse_id("{foo.", "{", "}", true), None); + assert_eq!(parse_id("{foo", "{", "}", true), None); + assert_eq!(parse_id("{}", "{", "}", true), None); + assert_eq!(parse_id("", "", "", true), None); + assert_eq!(parse_id("{-1}", "{", "}", true), Some(("-1", 4))); + assert_eq!(parse_id("{-1}", "{", "}", false), None); + assert_eq!(parse_id("{-a}", "{", "}", true), None); + } + + #[test] + fn literal_unescaped_opening_curly() { + // `{` in position where quantifier is not allowed results in literal `{` + assert_eq!(p("{"), make_literal("{")); + assert_eq!(p("({)"), Expr::Group(Box::new(make_literal("{"),))); + assert_eq!( + p("a|{"), + Expr::Alt(vec![make_literal("a"), make_literal("{"),]) + ); + assert_eq!( + p("{{2}"), + Expr::Repeat { + child: Box::new(make_literal("{")), + lo: 2, + hi: 2, + greedy: true + } + ); + } + + #[test] + fn literal_escape() { + assert_eq!(p("\\'"), make_literal("'")); + assert_eq!(p("\\\""), make_literal("\"")); + assert_eq!(p("\\ "), make_literal(" ")); + assert_eq!(p("\\xA0"), make_literal("\u{A0}")); + assert_eq!(p("\\x{1F4A9}"), make_literal("\u{1F4A9}")); + assert_eq!(p("\\x{000000B7}"), make_literal("\u{B7}")); + assert_eq!(p("\\u21D2"), make_literal("\u{21D2}")); + assert_eq!(p("\\u{21D2}"), make_literal("\u{21D2}")); + assert_eq!(p("\\u21D2x"), p("\u{21D2}x")); + assert_eq!(p("\\U0001F60A"), make_literal("\u{1F60A}")); + assert_eq!(p("\\U{0001F60A}"), make_literal("\u{1F60A}")); + } + + #[test] + fn hex_escape() { + assert_eq!( + p("\\h"), + Expr::Delegate { + inner: String::from("[0-9A-Fa-f]"), + size: 1, + casei: false + } + ); + assert_eq!( + p("\\H"), + Expr::Delegate { + inner: String::from("[^0-9A-Fa-f]"), + size: 1, + casei: false + } + ); + } + + #[test] + fn invalid_escape() { + assert_error( + "\\", + "Parsing error at position 0: Backslash without following character", + ); + assert_error("\\q", "Parsing error at position 0: Invalid escape: \\q"); + assert_error("\\xAG", "Parsing error at position 2: Invalid hex escape"); + assert_error("\\xA", "Parsing error at position 2: Invalid hex escape"); + assert_error("\\x{}", "Parsing error at position 2: Invalid hex escape"); + assert_error("\\x{AG}", "Parsing error at position 2: Invalid hex escape"); + assert_error("\\x{42", "Parsing error at position 2: Invalid hex escape"); + assert_error( + "\\x{D800}", + "Parsing error at position 2: Invalid codepoint for hex or unicode escape", + ); + assert_error( + "\\x{110000}", + "Parsing error at position 2: Invalid codepoint for hex or unicode escape", + ); + assert_error("\\u123", "Parsing error at position 2: Invalid hex escape"); + assert_error("\\u123x", "Parsing error at position 2: Invalid hex escape"); + assert_error("\\u{}", "Parsing error at position 2: Invalid hex escape"); + assert_error( + "\\U1234567", + "Parsing error at position 2: Invalid hex escape", + ); + assert_error("\\U{}", "Parsing error at position 2: Invalid hex escape"); + } + + #[test] + fn concat() { + assert_eq!( + p("ab"), + Expr::Concat(vec![make_literal("a"), make_literal("b"),]) + ); + } + + #[test] + fn alt() { + assert_eq!( + p("a|b"), + Expr::Alt(vec![make_literal("a"), make_literal("b"),]) + ); + } + + #[test] + fn group() { + assert_eq!(p("(a)"), Expr::Group(Box::new(make_literal("a"),))); + } + + #[test] + fn group_repeat() { + assert_eq!( + p("(a){2}"), + Expr::Repeat { + child: Box::new(Expr::Group(Box::new(make_literal("a")))), + lo: 2, + hi: 2, + greedy: true + } + ); + } + + #[test] + fn repeat() { + assert_eq!( + p("a{2,42}"), + Expr::Repeat { + child: Box::new(make_literal("a")), + lo: 2, + hi: 42, + greedy: true + } + ); + assert_eq!( + p("a{2,}"), + Expr::Repeat { + child: Box::new(make_literal("a")), + lo: 2, + hi: usize::MAX, + greedy: true + } + ); + assert_eq!( + p("a{2}"), + Expr::Repeat { + child: Box::new(make_literal("a")), + lo: 2, + hi: 2, + greedy: true + } + ); + assert_eq!( + p("a{,2}"), + Expr::Repeat { + child: Box::new(make_literal("a")), + lo: 0, + hi: 2, + greedy: true + } + ); + + assert_eq!( + p("a{2,42}?"), + Expr::Repeat { + child: Box::new(make_literal("a")), + lo: 2, + hi: 42, + greedy: false + } + ); + assert_eq!( + p("a{2,}?"), + Expr::Repeat { + child: Box::new(make_literal("a")), + lo: 2, + hi: usize::MAX, + greedy: false + } + ); + assert_eq!( + p("a{2}?"), + Expr::Repeat { + child: Box::new(make_literal("a")), + lo: 2, + hi: 2, + greedy: false + } + ); + assert_eq!( + p("a{,2}?"), + Expr::Repeat { + child: Box::new(make_literal("a")), + lo: 0, + hi: 2, + greedy: false + } + ); + } + + #[test] + fn invalid_repeat() { + // Invalid repeat syntax results in literal + assert_eq!( + p("a{"), + Expr::Concat(vec![make_literal("a"), make_literal("{"),]) + ); + assert_eq!( + p("a{6"), + Expr::Concat(vec![ + make_literal("a"), + make_literal("{"), + make_literal("6"), + ]) + ); + assert_eq!( + p("a{6,"), + Expr::Concat(vec![ + make_literal("a"), + make_literal("{"), + make_literal("6"), + make_literal(","), + ]) + ); + } + + #[test] + fn delegate_zero() { + assert_eq!(p("\\b"), Expr::Assertion(Assertion::WordBoundary),); + assert_eq!(p("\\B"), Expr::Assertion(Assertion::NotWordBoundary),); + } + + #[test] + fn delegate_named_group() { + assert_eq!( + p("\\p{Greek}"), + Expr::Delegate { + inner: String::from("\\p{Greek}"), + size: 1, + casei: false + } + ); + assert_eq!( + p("\\pL"), + Expr::Delegate { + inner: String::from("\\pL"), + size: 1, + casei: false + } + ); + assert_eq!( + p("\\P{Greek}"), + Expr::Delegate { + inner: String::from("\\P{Greek}"), + size: 1, + casei: false + } + ); + assert_eq!( + p("\\PL"), + Expr::Delegate { + inner: String::from("\\PL"), + size: 1, + casei: false + } + ); + assert_eq!( + p("(?i)\\p{Ll}"), + Expr::Delegate { + inner: String::from("\\p{Ll}"), + size: 1, + casei: true + } + ); + } + + #[test] + fn backref() { + assert_eq!( + p("(.)\\1"), + Expr::Concat(vec![ + Expr::Group(Box::new(Expr::Any { newline: false })), + Expr::Backref(1), + ]) + ); + } + + #[test] + fn named_backref() { + assert_eq!( + p("(?.)\\k"), + Expr::Concat(vec![ + Expr::Group(Box::new(Expr::Any { newline: false })), + Expr::Backref(1), + ]) + ); + } + + #[test] + fn relative_backref() { + assert_eq!( + p("(a)(.)\\k<-1>"), + Expr::Concat(vec![ + Expr::Group(Box::new(make_literal("a"))), + Expr::Group(Box::new(Expr::Any { newline: false })), + Expr::Backref(2) + ]) + ); + fail("(?P<->.)"); + fail("(.)(?P=-)") + } + + #[test] + fn lookaround() { + assert_eq!( + p("(?=a)"), + Expr::LookAround(Box::new(make_literal("a")), LookAhead) + ); + assert_eq!( + p("(?!a)"), + Expr::LookAround(Box::new(make_literal("a")), LookAheadNeg) + ); + assert_eq!( + p("(?<=a)"), + Expr::LookAround(Box::new(make_literal("a")), LookBehind) + ); + assert_eq!( + p("(?a)"), Expr::AtomicGroup(Box::new(make_literal("a")))); + } + + #[test] + fn possessive() { + assert_eq!( + p("a++"), + Expr::AtomicGroup(Box::new(Expr::Repeat { + child: Box::new(make_literal("a")), + lo: 1, + hi: usize::MAX, + greedy: true + })) + ); + assert_eq!( + p("a*+"), + Expr::AtomicGroup(Box::new(Expr::Repeat { + child: Box::new(make_literal("a")), + lo: 0, + hi: usize::MAX, + greedy: true + })) + ); + assert_eq!( + p("a?+"), + Expr::AtomicGroup(Box::new(Expr::Repeat { + child: Box::new(make_literal("a")), + lo: 0, + hi: 1, + greedy: true + })) + ); + } + + #[test] + fn invalid_backref() { + // only syntactic tests; see similar test in analyze module + fail(".\\12345678"); // unreasonably large number + fail(".\\c"); // not decimal + } + + #[test] + fn invalid_group_name_backref() { + assert_error( + "\\k(?.)", + "Parsing error at position 2: Invalid group name in back reference: id", + ); + } + + #[test] + fn named_backref_only() { + assert_error("(?.)\\1", "Error compiling regex: Numbered backref/call not allowed because named group was used, use a named backref instead"); + assert_error("(a)\\1(?b)", "Error compiling regex: Numbered backref/call not allowed because named group was used, use a named backref instead"); + } + + #[test] + fn invalid_group_name() { + assert_error( + "(?)", + "Parsing error at position 1: Could not parse group name", + ); + assert_error( + "(?<#>)", + "Parsing error at position 1: Could not parse group name", + ); + assert_error( + "\\kxxx", + "Parsing error at position 2: Could not parse group name", + ); + // "-" can only be at the start for a relative backref + assert_error( + "\\k", + "Parsing error at position 2: Could not parse group name", + ); + assert_error( + "\\k<-id>", + "Parsing error at position 2: Could not parse group name", + ); + } + + #[test] + fn unknown_flag() { + assert_error( + "(?-:a)", + "Parsing error at position 2: Unknown group flag: (?-:", + ); + assert_error( + "(?)", + "Parsing error at position 2: Unknown group flag: (?)", + ); + assert_error( + "(?--)", + "Parsing error at position 2: Unknown group flag: (?--", + ); + // Check that we don't split on char boundary + assert_error( + "(?\u{1F60A})", + "Parsing error at position 2: Unknown group flag: (?\u{1F60A}", + ); + } + + #[test] + fn no_quantifiers_on_lookarounds() { + assert_error( + "(?=hello)+", + "Parsing error at position 9: Target of repeat operator is invalid", + ); + assert_error( + "(?h)?(?('h'))"), + Expr::Concat(vec![ + Expr::Repeat { + child: Box::new(Expr::Group(Box::new(make_literal("h")))), + lo: 0, + hi: 1, + greedy: true + }, + Expr::BackrefExistsCondition(1) + ]) + ); + } + + #[test] + fn conditional_non_backref_validity_check_without_branches() { + assert_error( + "(?(foo))", + "Parsing error at position 7: General parsing error: expected conditional to be a backreference or at least an expression for when the condition is true", + ); + } + + #[test] + fn conditional_invalid_target_of_repeat_operator() { + assert_error( + r"(?(?=\d)\w|!)", + "Parsing error at position 3: Target of repeat operator is invalid", + ); + } + + #[test] + fn backref_condition_with_one_two_or_three_branches() { + assert_eq!( + p("(h)?(?(1)i|x)"), + Expr::Concat(vec![ + Expr::Repeat { + child: Box::new(Expr::Group(Box::new(make_literal("h")))), + lo: 0, + hi: 1, + greedy: true + }, + Expr::Conditional { + condition: Box::new(Expr::BackrefExistsCondition(1)), + true_branch: Box::new(make_literal("i")), + false_branch: Box::new(make_literal("x")), + }, + ]) + ); + + assert_eq!( + p("(h)?(?(1)i)"), + Expr::Concat(vec![ + Expr::Repeat { + child: Box::new(Expr::Group(Box::new(make_literal("h")))), + lo: 0, + hi: 1, + greedy: true + }, + Expr::Conditional { + condition: Box::new(Expr::BackrefExistsCondition(1)), + true_branch: Box::new(make_literal("i")), + false_branch: Box::new(Expr::Empty), + }, + ]) + ); + + assert_eq!( + p("(h)?(?(1)ii|xy|z)"), + Expr::Concat(vec![ + Expr::Repeat { + child: Box::new(Expr::Group(Box::new(make_literal("h")))), + lo: 0, + hi: 1, + greedy: true + }, + Expr::Conditional { + condition: Box::new(Expr::BackrefExistsCondition(1)), + true_branch: Box::new(Expr::Concat( + vec![make_literal("i"), make_literal("i"),] + )), + false_branch: Box::new(Expr::Alt(vec![ + Expr::Concat(vec![make_literal("x"), make_literal("y"),]), + make_literal("z"), + ])), + }, + ]) + ); + + assert_eq!( + p("(?h)?(?()ii|xy|z)"), + Expr::Concat(vec![ + Expr::Repeat { + child: Box::new(Expr::Group(Box::new(make_literal("h")))), + lo: 0, + hi: 1, + greedy: true + }, + Expr::Conditional { + condition: Box::new(Expr::BackrefExistsCondition(1)), + true_branch: Box::new(Expr::Concat( + vec![make_literal("i"), make_literal("i"),] + )), + false_branch: Box::new(Expr::Alt(vec![ + Expr::Concat(vec![make_literal("x"), make_literal("y"),]), + make_literal("z"), + ])), + }, + ]) + ); + } + + #[test] + fn conditional() { + assert_eq!( + p("((?(a)b|c))(\\1)"), + Expr::Concat(vec![ + Expr::Group(Box::new(Expr::Conditional { + condition: Box::new(make_literal("a")), + true_branch: Box::new(make_literal("b")), + false_branch: Box::new(make_literal("c")) + })), + Expr::Group(Box::new(Expr::Backref(1))) + ]) + ); + + assert_eq!( + p(r"^(?(\d)abc|\d!)$"), + Expr::Concat(vec![ + Expr::Assertion(Assertion::StartText), + Expr::Conditional { + condition: Box::new(Expr::Delegate { + inner: "\\d".to_string(), + size: 1, + casei: false, + }), + true_branch: Box::new(Expr::Concat(vec![ + make_literal("a"), + make_literal("b"), + make_literal("c"), + ])), + false_branch: Box::new(Expr::Concat(vec![ + Expr::Delegate { + inner: "\\d".to_string(), + size: 1, + casei: false, + }, + make_literal("!"), + ])), + }, + Expr::Assertion(Assertion::EndText), + ]) + ); + + assert_eq!( + p(r"(?((?=\d))\w|!)"), + Expr::Conditional { + condition: Box::new(Expr::LookAround( + Box::new(Expr::Delegate { + inner: "\\d".to_string(), + size: 1, + casei: false + }), + LookAhead + )), + true_branch: Box::new(Expr::Delegate { + inner: "\\w".to_string(), + size: 1, + casei: false, + }), + false_branch: Box::new(make_literal("!")), + }, + ); + + assert_eq!( + p(r"(?((ab))c|d)"), + Expr::Conditional { + condition: Box::new(Expr::Group(Box::new(Expr::Concat(vec![ + make_literal("a"), + make_literal("b"), + ]),))), + true_branch: Box::new(make_literal("c")), + false_branch: Box::new(make_literal("d")), + }, + ); + } + + // found by cargo fuzz, then minimized + #[test] + fn fuzz_1() { + p(r"\ä"); + } + + #[test] + fn fuzz_2() { + p(r"\pä"); + } + + #[test] + fn fuzz_3() { + fail(r"(?()^"); + fail(r#"!w(?()\"Kuz>"#); + } + + #[test] + fn fuzz_4() { + fail(r"\u{2}(?(2)"); + } +} diff --git a/tools/vendor/fancy-regex/src/replacer.rs b/tools/vendor/fancy-regex/src/replacer.rs new file mode 100644 index 0000000000..56094b65a7 --- /dev/null +++ b/tools/vendor/fancy-regex/src/replacer.rs @@ -0,0 +1,162 @@ +use alloc::borrow::Cow; +use alloc::string::String; + +use crate::Captures; + +/// Replacer describes types that can be used to replace matches in a string. +/// +/// In general, users of this crate shouldn't need to implement this trait, +/// since implementations are already provided for `&str` along with other +/// variants of string types and `FnMut(&Captures) -> String` (or any +/// `FnMut(&Captures) -> T` where `T: AsRef`), which covers most use cases. +pub trait Replacer { + /// Appends text to `dst` to replace the current match. + /// + /// The current match is represented by `caps`, which is guaranteed to + /// have a match at capture group `0`. + /// + /// For example, a no-op replacement would be + /// `dst.push_str(caps.get(0).unwrap().as_str())`. + fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String); + + /// Return a fixed unchanging replacement string. + /// + /// When doing replacements, if access to `Captures` is not needed (e.g., + /// the replacement byte string does not need `$` expansion), then it can + /// be beneficial to avoid finding sub-captures. + /// + /// In general, this is called once for every call to `replacen`. + fn no_expansion(&mut self) -> Option> { + None + } + + /// Return a `Replacer` that borrows and wraps this `Replacer`. + /// + /// This is useful when you want to take a generic `Replacer` (which might + /// not be cloneable) and use it without consuming it, so it can be used + /// more than once. + /// + /// # Example + /// + /// ``` + /// use fancy_regex::{Regex, Replacer}; + /// + /// fn replace_all_twice( + /// re: Regex, + /// src: &str, + /// mut rep: R, + /// ) -> String { + /// let dst = re.replace_all(src, rep.by_ref()); + /// let dst = re.replace_all(&dst, rep.by_ref()); + /// dst.into_owned() + /// } + /// ``` + fn by_ref(&mut self) -> ReplacerRef { + ReplacerRef(self) + } +} + +/// By-reference adaptor for a `Replacer` +/// +/// Returned by [`Replacer::by_ref`](trait.Replacer.html#method.by_ref). +#[derive(Debug)] +pub struct ReplacerRef<'a, R: ?Sized>(&'a mut R); + +impl<'a, R: Replacer + ?Sized + 'a> Replacer for ReplacerRef<'a, R> { + fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) { + self.0.replace_append(caps, dst) + } + fn no_expansion(&mut self) -> Option> { + self.0.no_expansion() + } +} + +impl<'a> Replacer for &'a str { + fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) { + caps.expand(*self, dst); + } + + fn no_expansion(&mut self) -> Option> { + no_expansion(self) + } +} + +impl<'a> Replacer for &'a String { + fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) { + self.as_str().replace_append(caps, dst) + } + + fn no_expansion(&mut self) -> Option> { + no_expansion(self) + } +} + +impl Replacer for String { + fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) { + self.as_str().replace_append(caps, dst) + } + + fn no_expansion(&mut self) -> Option> { + no_expansion(self) + } +} + +impl<'a> Replacer for Cow<'a, str> { + fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) { + self.as_ref().replace_append(caps, dst) + } + + fn no_expansion(&mut self) -> Option> { + no_expansion(self) + } +} + +impl<'a> Replacer for &'a Cow<'a, str> { + fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) { + self.as_ref().replace_append(caps, dst) + } + + fn no_expansion(&mut self) -> Option> { + no_expansion(self) + } +} + +fn no_expansion>(t: &T) -> Option> { + let s = t.as_ref(); + if s.contains('$') { + None + } else { + Some(Cow::Borrowed(s)) + } +} + +impl Replacer for F +where + F: FnMut(&Captures<'_>) -> T, + T: AsRef, +{ + fn replace_append(&mut self, caps: &Captures<'_>, dst: &mut String) { + dst.push_str((*self)(caps).as_ref()); + } +} + +/// `NoExpand` indicates literal string replacement. +/// +/// It can be used with `replace` and `replace_all` to do a literal string +/// replacement without expanding `$name` to their corresponding capture +/// groups. This can be both convenient (to avoid escaping `$`, for example) +/// and performant (since capture groups don't need to be found). +/// +/// `'t` is the lifetime of the literal text. +#[derive(Clone, Debug)] +pub struct NoExpand<'t>(pub &'t str); + +impl<'t> Replacer for NoExpand<'t> { + fn replace_append(&mut self, _: &Captures<'_>, dst: &mut String) { + dst.push_str(self.0); + } + + fn no_expansion(&mut self) -> Option> { + Some(Cow::Borrowed(self.0)) + } +} diff --git a/tools/vendor/fancy-regex/src/vm.rs b/tools/vendor/fancy-regex/src/vm.rs new file mode 100644 index 0000000000..06a5db07b6 --- /dev/null +++ b/tools/vendor/fancy-regex/src/vm.rs @@ -0,0 +1,918 @@ +// Copyright 2016 The Fancy Regex Authors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +//! Backtracking VM for implementing fancy regexes. +//! +//! Read for a good introduction for how this works. +//! +//! The VM executes a sequence of instructions (a program) against an input string. It keeps track +//! of a program counter (PC) and an index into the string (IX). Execution can have one or more +//! threads. +//! +//! One of the basic instructions is `Lit`, which matches a string against the input. If it matches, +//! the PC advances to the next instruction and the IX to the position after the matched string. +//! If not, the current thread is stopped because it failed. +//! +//! If execution reaches an `End` instruction, the program is successful because a match was found. +//! If there are no more threads to execute, the program has failed to match. +//! +//! A very simple program for the regex `a`: +//! +//! ```text +//! 0: Lit("a") +//! 1: End +//! ``` +//! +//! The `Split` instruction causes execution to split into two threads. The first thread is executed +//! with the current string index. If it fails, we reset the string index and resume execution with +//! the second thread. That is what "backtracking" refers to. In order to do that, we keep a stack +//! of threads (PC and IX) to try. +//! +//! Example program for the regex `ab|ac`: +//! +//! ```text +//! 0: Split(1, 4) +//! 1: Lit("a") +//! 2: Lit("b") +//! 3: Jmp(6) +//! 4: Lit("a") +//! 5: Lit("c") +//! 6: End +//! ``` +//! +//! The `Jmp` instruction causes execution to jump to the specified instruction. In the example it +//! is needed to separate the two threads. +//! +//! Let's step through execution with that program for the input `ac`: +//! +//! 1. We're at PC 0 and IX 0 +//! 2. `Split(1, 4)` means we save a thread with PC 4 and IX 0 for trying later +//! 3. Continue at `Lit("a")` which matches, so we advance IX to 1 +//! 4. `Lit("b")` doesn't match at IX 1 (`"b" != "c"`), so the thread fails +//! 5. We continue with the previously saved thread at PC 4 and IX 0 (backtracking) +//! 6. Both `Lit("a")` and `Lit("c")` match and we reach `End` -> successful match (index 0 to 2) + +use alloc::collections::BTreeSet; +use alloc::string::String; +use alloc::vec; +use alloc::vec::Vec; +use core::usize; +use regex_automata::meta::Regex; +use regex_automata::util::look::LookMatcher; +use regex_automata::util::primitives::NonMaxUsize; +use regex_automata::Anchored; +use regex_automata::Input; + +use crate::error::RuntimeError; +use crate::prev_codepoint_ix; +use crate::Assertion; +use crate::Error; +use crate::Result; +use crate::{codepoint_len, RegexOptions}; + +/// Enable tracing of VM execution. Only for debugging/investigating. +const OPTION_TRACE: u32 = 1 << 0; +/// When iterating over all matches within a text (e.g. with `find_iter`), empty matches need to be +/// handled specially. If we kept matching at the same position, we'd never stop. So what we do +/// after we've had an empty match, is to advance the position where matching is attempted. +/// If `\G` is used in the pattern, that means it no longer matches. If we didn't tell the VM about +/// the fact that we skipped because of an empty match, it would still treat `\G` as matching. So +/// this option is for communicating that to the VM. Phew. +pub(crate) const OPTION_SKIPPED_EMPTY_MATCH: u32 = 1 << 1; + +// TODO: make configurable +const MAX_STACK: usize = 1_000_000; + +/// Instruction of the VM. +#[derive(Debug, Clone)] +pub enum Insn { + /// Successful end of program + End, + /// Match any character (including newline) + Any, + /// Match any character (not including newline) + AnyNoNL, + /// Assertions + Assertion(Assertion), + /// Match the literal string at the current index + Lit(String), // should be cow? + /// Split execution into two threads. The two fields are positions of instructions. Execution + /// first tries the first thread. If that fails, the second position is tried. + Split(usize, usize), + /// Jump to instruction at position + Jmp(usize), + /// Save the current string index into the specified slot + Save(usize), + /// Save `0` into the specified slot + Save0(usize), + /// Set the string index to the value that was saved in the specified slot + Restore(usize), + /// Repeat greedily (match as much as possible) + RepeatGr { + /// Minimum number of matches + lo: usize, + /// Maximum number of matches + hi: usize, + /// The instruction after the repeat + next: usize, + /// The slot for keeping track of the number of repetitions + repeat: usize, + }, + /// Repeat non-greedily (prefer matching as little as possible) + RepeatNg { + /// Minimum number of matches + lo: usize, + /// Maximum number of matches + hi: usize, + /// The instruction after the repeat + next: usize, + /// The slot for keeping track of the number of repetitions + repeat: usize, + }, + /// Repeat greedily and prevent infinite loops from empty matches + RepeatEpsilonGr { + /// Minimum number of matches + lo: usize, + /// The instruction after the repeat + next: usize, + /// The slot for keeping track of the number of repetitions + repeat: usize, + /// The slot for saving the previous IX to check if we had an empty match + check: usize, + }, + /// Repeat non-greedily and prevent infinite loops from empty matches + RepeatEpsilonNg { + /// Minimum number of matches + lo: usize, + /// The instruction after the repeat + next: usize, + /// The slot for keeping track of the number of repetitions + repeat: usize, + /// The slot for saving the previous IX to check if we had an empty match + check: usize, + }, + /// Negative look-around failed + FailNegativeLookAround, + /// Set IX back by the specified number of characters + GoBack(usize), + /// Back reference to a group number to check + Backref(usize), + /// Begin of atomic group + BeginAtomic, + /// End of atomic group + EndAtomic, + /// Delegate matching to the regex crate + Delegate { + /// The regex + inner: Regex, + /// The first group number that this regex captures (if it contains groups) + start_group: usize, + /// The last group number + end_group: usize, + }, + /// Anchor to match at the position where the previous match ended + ContinueFromPreviousMatchEnd, + /// Continue only if the specified capture group has already been populated as part of the match + BackrefExistsCondition(usize), +} + +/// Sequence of instructions for the VM to execute. +#[derive(Debug, Clone)] +pub struct Prog { + /// Instructions of the program + pub body: Vec, + n_saves: usize, +} + +impl Prog { + pub(crate) fn new(body: Vec, n_saves: usize) -> Prog { + Prog { body, n_saves } + } + + #[doc(hidden)] + pub(crate) fn debug_print(&self) { + #[cfg(feature = "std")] + for (i, insn) in self.body.iter().enumerate() { + println!("{:3}: {:?}", i, insn); + } + } +} + +#[derive(Debug)] +struct Branch { + pc: usize, + ix: usize, + nsave: usize, +} + +#[derive(Debug)] +struct Save { + slot: usize, + value: usize, +} + +struct State { + /// Saved values indexed by slot. Mostly indices to s, but can be repeat values etc. + /// Always contains the saves of the current state. + saves: Vec, + /// Stack of backtrack branches. + stack: Vec, + /// Old saves (slot, value) + oldsave: Vec, + /// Number of saves at the end of `oldsave` that need to be restored to `saves` on pop + nsave: usize, + explicit_sp: usize, + /// Maximum size of the stack. If the size would be exceeded during execution, a `StackOverflow` + /// error is raised. + max_stack: usize, + #[allow(dead_code)] + options: u32, +} + +// Each element in the stack conceptually represents the entire state +// of the machine: the pc (index into prog), the index into the +// string, and the entire vector of saves. However, copying the save +// vector on every push/pop would be inefficient, so instead we use a +// copy-on-write approach for each slot within the save vector. The +// top `nsave` elements in `oldsave` represent the delta from the +// current machine state to the top of stack. + +impl State { + fn new(n_saves: usize, max_stack: usize, options: u32) -> State { + State { + saves: vec![usize::MAX; n_saves], + stack: Vec::new(), + oldsave: Vec::new(), + nsave: 0, + explicit_sp: n_saves, + max_stack, + options, + } + } + + // push a backtrack branch + fn push(&mut self, pc: usize, ix: usize) -> Result<()> { + if self.stack.len() < self.max_stack { + let nsave = self.nsave; + self.stack.push(Branch { pc, ix, nsave }); + self.nsave = 0; + self.trace_stack("push"); + Ok(()) + } else { + Err(Error::RuntimeError(RuntimeError::StackOverflow)) + } + } + + // pop a backtrack branch + fn pop(&mut self) -> (usize, usize) { + for _ in 0..self.nsave { + let Save { slot, value } = self.oldsave.pop().unwrap(); + self.saves[slot] = value; + } + let Branch { pc, ix, nsave } = self.stack.pop().unwrap(); + self.nsave = nsave; + self.trace_stack("pop"); + (pc, ix) + } + + fn save(&mut self, slot: usize, val: usize) { + for i in 0..self.nsave { + // could avoid this iteration with some overhead; worth it? + if self.oldsave[self.oldsave.len() - i - 1].slot == slot { + // already saved, just update + self.saves[slot] = val; + return; + } + } + self.oldsave.push(Save { + slot, + value: self.saves[slot], + }); + self.nsave += 1; + self.saves[slot] = val; + + #[cfg(feature = "std")] + if self.options & OPTION_TRACE != 0 { + println!("saves: {:?}", self.saves); + } + } + + fn get(&self, slot: usize) -> usize { + self.saves[slot] + } + + // push a value onto the explicit stack; note: the entire contents of + // the explicit stack is saved and restored on backtrack. + fn stack_push(&mut self, val: usize) { + if self.saves.len() == self.explicit_sp { + self.saves.push(self.explicit_sp + 1); + } + let explicit_sp = self.explicit_sp; + let sp = self.get(explicit_sp); + if self.saves.len() == sp { + self.saves.push(val); + } else { + self.save(sp, val); + } + self.save(explicit_sp, sp + 1); + } + + // pop a value from the explicit stack + fn stack_pop(&mut self) -> usize { + let explicit_sp = self.explicit_sp; + let sp = self.get(explicit_sp) - 1; + let result = self.get(sp); + self.save(explicit_sp, sp); + result + } + + /// Get the current number of backtrack branches + fn backtrack_count(&self) -> usize { + self.stack.len() + } + + /// Discard backtrack branches that were pushed since the call to `backtrack_count`. + /// + /// What we want: + /// * Keep the current `saves` as they are + /// * Only keep `count` backtrack branches on `stack`, discard the rest + /// * Keep the first `oldsave` for each slot, discard the rest (multiple pushes might have + /// happened with saves to the same slot) + fn backtrack_cut(&mut self, count: usize) { + if self.stack.len() == count { + // no backtrack branches to discard, all good + return; + } + // start and end indexes of old saves for the branch we're cutting to + let (oldsave_start, oldsave_end) = { + let mut end = self.oldsave.len() - self.nsave; + for &Branch { nsave, .. } in &self.stack[count + 1..] { + end -= nsave; + } + let start = end - self.stack[count].nsave; + (start, end) + }; + let mut saved = BTreeSet::new(); + // keep all the old saves of our branch (they're all for different slots) + for &Save { slot, .. } in &self.oldsave[oldsave_start..oldsave_end] { + saved.insert(slot); + } + let mut oldsave_ix = oldsave_end; + // for other old saves, keep them only if they're for a slot that we haven't saved yet + for ix in oldsave_end..self.oldsave.len() { + let Save { slot, .. } = self.oldsave[ix]; + let new_slot = saved.insert(slot); + if new_slot { + // put the save we want to keep (ix) after the ones we already have (oldsave_ix) + // note that it's fine if the indexes are the same (then swapping is a no-op) + self.oldsave.swap(oldsave_ix, ix); + oldsave_ix += 1; + } + } + self.stack.truncate(count); + self.oldsave.truncate(oldsave_ix); + self.nsave = oldsave_ix - oldsave_start; + } + + #[inline] + #[allow(unused_variables)] + fn trace_stack(&self, operation: &str) { + #[cfg(feature = "std")] + if self.options & OPTION_TRACE != 0 { + println!("stack after {}: {:?}", operation, self.stack); + } + } +} + +fn codepoint_len_at(s: &str, ix: usize) -> usize { + codepoint_len(s.as_bytes()[ix]) +} + +#[inline] +fn matches_literal(s: &str, ix: usize, end: usize, literal: &str) -> bool { + // Compare as bytes because the literal might be a single byte char whereas ix + // points to a multibyte char. Comparing with str would result in an error like + // "byte index N is not a char boundary". + end <= s.len() && &s.as_bytes()[ix..end] == literal.as_bytes() +} + +/// Run the program with trace printing for debugging. +pub fn run_trace(prog: &Prog, s: &str, pos: usize) -> Result>> { + run(prog, s, pos, OPTION_TRACE, &RegexOptions::default()) +} + +/// Run the program with default options. +pub fn run_default(prog: &Prog, s: &str, pos: usize) -> Result>> { + run(prog, s, pos, 0, &RegexOptions::default()) +} + +/// Run the program with options. +#[allow(clippy::cognitive_complexity)] +pub(crate) fn run( + prog: &Prog, + s: &str, + pos: usize, + option_flags: u32, + options: &RegexOptions, +) -> Result>> { + let mut state = State::new(prog.n_saves, MAX_STACK, option_flags); + let mut inner_slots: Vec> = Vec::new(); + let look_matcher = LookMatcher::new(); + #[cfg(feature = "std")] + if option_flags & OPTION_TRACE != 0 { + println!("pos\tinstruction"); + } + let mut backtrack_count = 0; + let mut pc = 0; + let mut ix = pos; + loop { + // break from this loop to fail, causes stack to pop + 'fail: loop { + #[cfg(feature = "std")] + if option_flags & OPTION_TRACE != 0 { + println!("{}\t{} {:?}", ix, pc, prog.body[pc]); + } + match prog.body[pc] { + Insn::End => { + // save of end position into slot 1 is now done + // with an explicit group; we might want to + // optimize that. + //state.saves[1] = ix; + #[cfg(feature = "std")] + if option_flags & OPTION_TRACE != 0 { + println!("saves: {:?}", state.saves); + } + if let Some(&slot1) = state.saves.get(1) { + // With some features like keep out (\K), the match start can be after + // the match end. Cap the start to <= end. + if state.get(0) > slot1 { + state.save(0, slot1); + } + } + return Ok(Some(state.saves)); + } + Insn::Any => { + if ix < s.len() { + ix += codepoint_len_at(s, ix); + } else { + break 'fail; + } + } + Insn::AnyNoNL => { + if ix < s.len() && s.as_bytes()[ix] != b'\n' { + ix += codepoint_len_at(s, ix); + } else { + break 'fail; + } + } + Insn::Lit(ref val) => { + let ix_end = ix + val.len(); + if !matches_literal(s, ix, ix_end, val) { + break 'fail; + } + ix = ix_end + } + Insn::Assertion(assertion) => { + if !match assertion { + Assertion::StartText => look_matcher.is_start(s.as_bytes(), ix), + Assertion::EndText => look_matcher.is_end(s.as_bytes(), ix), + Assertion::StartLine { crlf: false } => { + look_matcher.is_start_lf(s.as_bytes(), ix) + } + Assertion::StartLine { crlf: true } => { + look_matcher.is_start_crlf(s.as_bytes(), ix) + } + Assertion::EndLine { crlf: false } => { + look_matcher.is_end_lf(s.as_bytes(), ix) + } + Assertion::EndLine { crlf: true } => { + look_matcher.is_end_crlf(s.as_bytes(), ix) + } + Assertion::LeftWordBoundary => look_matcher + .is_word_start_unicode(s.as_bytes(), ix) + .unwrap(), + Assertion::RightWordBoundary => { + look_matcher.is_word_end_unicode(s.as_bytes(), ix).unwrap() + } + Assertion::WordBoundary => { + look_matcher.is_word_unicode(s.as_bytes(), ix).unwrap() + } + Assertion::NotWordBoundary => look_matcher + .is_word_unicode_negate(s.as_bytes(), ix) + .unwrap(), + } { + break 'fail; + } + } + Insn::Split(x, y) => { + state.push(y, ix)?; + pc = x; + continue; + } + Insn::Jmp(target) => { + pc = target; + continue; + } + Insn::Save(slot) => state.save(slot, ix), + Insn::Save0(slot) => state.save(slot, 0), + Insn::Restore(slot) => ix = state.get(slot), + Insn::RepeatGr { + lo, + hi, + next, + repeat, + } => { + let repcount = state.get(repeat); + if repcount == hi { + pc = next; + continue; + } + state.save(repeat, repcount + 1); + if repcount >= lo { + state.push(next, ix)?; + } + } + Insn::RepeatNg { + lo, + hi, + next, + repeat, + } => { + let repcount = state.get(repeat); + if repcount == hi { + pc = next; + continue; + } + state.save(repeat, repcount + 1); + if repcount >= lo { + state.push(pc + 1, ix)?; + pc = next; + continue; + } + } + Insn::RepeatEpsilonGr { + lo, + next, + repeat, + check, + } => { + let repcount = state.get(repeat); + if repcount > lo && state.get(check) == ix { + // prevent zero-length match on repeat + break 'fail; + } + state.save(repeat, repcount + 1); + if repcount >= lo { + state.save(check, ix); + state.push(next, ix)?; + } + } + Insn::RepeatEpsilonNg { + lo, + next, + repeat, + check, + } => { + let repcount = state.get(repeat); + if repcount > lo && state.get(check) == ix { + // prevent zero-length match on repeat + break 'fail; + } + state.save(repeat, repcount + 1); + if repcount >= lo { + state.save(check, ix); + state.push(pc + 1, ix)?; + pc = next; + continue; + } + } + Insn::GoBack(count) => { + for _ in 0..count { + if ix == 0 { + break 'fail; + } + ix = prev_codepoint_ix(s, ix); + } + } + Insn::FailNegativeLookAround => { + // Reaching this instruction means that the body of the + // look-around matched. Because it's a *negative* look-around, + // that means the look-around itself should fail (not match). + // But before, we need to discard all the states that have + // been pushed with the look-around, because we don't want to + // explore them. + loop { + let (popped_pc, _) = state.pop(); + if popped_pc == pc + 1 { + // We've reached the state that would jump us to + // after the look-around (in case the look-around + // succeeded). That means we popped enough states. + break; + } + } + break 'fail; + } + Insn::Backref(slot) => { + let lo = state.get(slot); + if lo == usize::MAX { + // Referenced group hasn't matched, so the backref doesn't match either + break 'fail; + } + let hi = state.get(slot + 1); + if hi == usize::MAX { + // Referenced group hasn't matched, so the backref doesn't match either + break 'fail; + } + let ref_text = &s[lo..hi]; + let ix_end = ix + ref_text.len(); + if !matches_literal(s, ix, ix_end, ref_text) { + break 'fail; + } + ix = ix_end; + } + Insn::BackrefExistsCondition(group) => { + let lo = state.get(group * 2); + if lo == usize::MAX { + // Referenced group hasn't matched, so the backref doesn't match either + break 'fail; + } + } + Insn::BeginAtomic => { + let count = state.backtrack_count(); + state.stack_push(count); + } + Insn::EndAtomic => { + let count = state.stack_pop(); + state.backtrack_cut(count); + } + Insn::Delegate { + ref inner, + start_group, + end_group, + } => { + let input = Input::new(s).span(ix..s.len()).anchored(Anchored::Yes); + if start_group == end_group { + // No groups, so we can use faster methods + match inner.search_half(&input) { + Some(m) => ix = m.offset(), + _ => break 'fail, + } + } else { + inner_slots.resize((end_group - start_group + 1) * 2, None); + if inner.search_slots(&input, &mut inner_slots).is_some() { + for i in 0..(end_group - start_group) { + let slot = (start_group + i) * 2; + if let Some(start) = inner_slots[(i + 1) * 2] { + let end = inner_slots[(i + 1) * 2 + 1].unwrap(); + state.save(slot, start.get()); + state.save(slot + 1, end.get()); + } else { + state.save(slot, usize::MAX); + state.save(slot + 1, usize::MAX); + } + } + ix = inner_slots[1].unwrap().get(); + } else { + break 'fail; + } + } + } + Insn::ContinueFromPreviousMatchEnd => { + if ix > pos || option_flags & OPTION_SKIPPED_EMPTY_MATCH != 0 { + break 'fail; + } + } + } + pc += 1; + } + #[cfg(feature = "std")] + if option_flags & OPTION_TRACE != 0 { + println!("fail"); + } + // "break 'fail" goes here + if state.stack.is_empty() { + return Ok(None); + } + + backtrack_count += 1; + if backtrack_count > options.backtrack_limit { + return Err(Error::RuntimeError(RuntimeError::BacktrackLimitExceeded)); + } + + let (newpc, newix) = state.pop(); + pc = newpc; + ix = newix; + } +} + +#[cfg(test)] +mod tests { + use super::*; + use quickcheck::{quickcheck, Arbitrary, Gen}; + + #[test] + fn state_push_pop() { + let mut state = State::new(1, MAX_STACK, 0); + + state.push(0, 0).unwrap(); + state.push(1, 1).unwrap(); + assert_eq!(state.pop(), (1, 1)); + assert_eq!(state.pop(), (0, 0)); + assert!(state.stack.is_empty()); + + state.push(2, 2).unwrap(); + assert_eq!(state.pop(), (2, 2)); + assert!(state.stack.is_empty()); + } + + #[test] + fn state_save_override() { + let mut state = State::new(1, MAX_STACK, 0); + state.save(0, 10); + state.push(0, 0).unwrap(); + state.save(0, 20); + assert_eq!(state.pop(), (0, 0)); + assert_eq!(state.get(0), 10); + } + + #[test] + fn state_save_override_twice() { + let mut state = State::new(1, MAX_STACK, 0); + state.save(0, 10); + state.push(0, 0).unwrap(); + state.save(0, 20); + state.push(1, 1).unwrap(); + state.save(0, 30); + + assert_eq!(state.get(0), 30); + assert_eq!(state.pop(), (1, 1)); + assert_eq!(state.get(0), 20); + assert_eq!(state.pop(), (0, 0)); + assert_eq!(state.get(0), 10); + } + + #[test] + fn state_explicit_stack() { + let mut state = State::new(1, MAX_STACK, 0); + state.stack_push(11); + state.stack_push(12); + + state.push(100, 101).unwrap(); + state.stack_push(13); + assert_eq!(state.stack_pop(), 13); + state.stack_push(14); + assert_eq!(state.pop(), (100, 101)); + + // Note: 14 is not there because it was pushed as part of the backtrack branch + assert_eq!(state.stack_pop(), 12); + assert_eq!(state.stack_pop(), 11); + } + + #[test] + fn state_backtrack_cut_simple() { + let mut state = State::new(2, MAX_STACK, 0); + state.save(0, 1); + state.save(1, 2); + + let count = state.backtrack_count(); + + state.push(0, 0).unwrap(); + state.save(0, 3); + assert_eq!(state.backtrack_count(), 1); + + state.backtrack_cut(count); + assert_eq!(state.backtrack_count(), 0); + assert_eq!(state.get(0), 3); + assert_eq!(state.get(1), 2); + } + + #[test] + fn state_backtrack_cut_complex() { + let mut state = State::new(2, MAX_STACK, 0); + state.save(0, 1); + state.save(1, 2); + + state.push(0, 0).unwrap(); + state.save(0, 3); + + let count = state.backtrack_count(); + + state.push(1, 1).unwrap(); + state.save(0, 4); + state.push(2, 2).unwrap(); + state.save(1, 5); + assert_eq!(state.backtrack_count(), 3); + + state.backtrack_cut(count); + assert_eq!(state.backtrack_count(), 1); + assert_eq!(state.get(0), 4); + assert_eq!(state.get(1), 5); + + state.pop(); + assert_eq!(state.backtrack_count(), 0); + // Check that oldsave were set correctly + assert_eq!(state.get(0), 1); + assert_eq!(state.get(1), 2); + } + + #[derive(Clone, Debug)] + enum Operation { + Push, + Pop, + Save(usize, usize), + } + + impl Arbitrary for Operation { + fn arbitrary(g: &mut Gen) -> Self { + match g.choose(&[0, 1, 2]) { + Some(0) => Operation::Push, + Some(1) => Operation::Pop, + _ => Operation::Save( + *g.choose(&[0usize, 1, 2, 3, 4]).unwrap(), + usize::arbitrary(g), + ), + } + } + } + + fn check_saves_for_operations(operations: Vec) -> bool { + let slots = operations + .iter() + .map(|o| match o { + &Operation::Save(slot, _) => slot + 1, + _ => 0, + }) + .max() + .unwrap_or(0); + if slots == 0 { + // No point checking if there's no save instructions + return true; + } + + // Stack with the complete VM state (including saves) + let mut stack = Vec::new(); + let mut saves = vec![usize::MAX; slots]; + + let mut state = State::new(slots, MAX_STACK, 0); + + let mut expected = Vec::new(); + let mut actual = Vec::new(); + + for operation in operations { + match operation { + Operation::Push => { + // We're not checking pc and ix later, so don't bother + // putting in random values. + stack.push((0, 0, saves.clone())); + state.push(0, 0).unwrap(); + } + Operation::Pop => { + // Note that because we generate the operations randomly + // there might be more pops than pushes. So ignore a pop + // if the stack was empty. + if let Some((_, _, previous_saves)) = stack.pop() { + saves = previous_saves; + state.pop(); + } + } + Operation::Save(slot, value) => { + saves[slot] = value; + state.save(slot, value); + } + } + + // Remember state of saves for checking later + expected.push(saves.clone()); + let mut actual_saves = vec![usize::MAX; slots]; + for i in 0..slots { + actual_saves[i] = state.get(i); + } + actual.push(actual_saves); + } + + expected == actual + } + + quickcheck! { + fn state_save_quickcheck(operations: Vec) -> bool { + check_saves_for_operations(operations) + } + } +} diff --git a/tools/vendor/fancy-regex/tests/captures.rs b/tools/vendor/fancy-regex/tests/captures.rs new file mode 100644 index 0000000000..dc559ffc88 --- /dev/null +++ b/tools/vendor/fancy-regex/tests/captures.rs @@ -0,0 +1,371 @@ +use fancy_regex::{Captures, CompileError, Error, Expander, Match, Result}; +use std::borrow::Cow; +use std::ops::Index; + +mod common; + +#[test] +fn capture_names() { + let regex = common::regex("(?)()(?P)"); + let capture_names = regex.capture_names().collect::>(); + assert_eq!(capture_names, vec![None, Some("foo"), None, Some("bar")]); +} + +#[test] +fn captures_fancy() { + let captures = captures(r"\s*(\w+)(?=\.)", "foo bar."); + assert_eq!(captures.len(), 2); + assert_match(captures.get(0), " bar", 3, 7); + assert_match(captures.get(1), "bar", 4, 7); + assert!(captures.get(2).is_none()); +} + +#[test] +fn captures_fancy_named() { + let captures = captures(r"\s*(?\w+)(?=\.)", "foo bar."); + assert_eq!(captures.len(), 2); + assert_match(captures.get(0), " bar", 3, 7); + assert_match(captures.name("name"), "bar", 4, 7); + assert_eq!(captures.index(0), " bar"); + assert_eq!(captures.index("name"), "bar"); + assert!(captures.get(2).is_none()); +} + +#[test] +fn captures_fancy_unmatched_group() { + let captures = captures(r"(\w+)(?=\.)|(\w+)(?=!)", "foo! bar."); + assert_eq!(captures.len(), 3); + assert_match(captures.get(0), "foo", 0, 3); + assert!(captures.get(1).is_none()); + assert_match(captures.get(2), "foo", 0, 3); +} + +#[test] +fn captures_after_lookbehind() { + let captures = captures( + r"\s*(?<=[() ])(@\w+)(\([^)]*\))?\s*", + " @another(foo bar) ", + ); + assert_match(captures.get(1), "@another", 1, 9); + assert_match(captures.get(2), "(foo bar)", 9, 18); +} + +#[test] +fn captures_with_keepout_inside_at_end() { + let captures = captures(r"\s*(\w+\K)(?=\.)", "foo bar."); + assert_eq!(captures.len(), 2); + assert_match(captures.get(0), "", 7, 7); + assert_match(captures.get(1), "bar", 4, 7); + assert!(captures.get(2).is_none()); +} + +#[test] +fn captures_with_keepout_inside_in_middle() { + let captures = captures(r"\s*(b\Kar)(?=\.)", "foo bar."); + assert_eq!(captures.len(), 2); + assert_match(captures.get(0), "ar", 5, 7); + assert_match(captures.get(1), "bar", 4, 7); + assert!(captures.get(2).is_none()); +} + +#[test] +fn captures_with_keepout_between() { + let captures = captures(r"(\w+)\K\s*(\w+)(?=\.)", "foo bar."); + assert_eq!(captures.len(), 3); + assert_match(captures.get(0), " bar", 3, 7); + assert_match(captures.get(1), "foo", 0, 3); + assert_match(captures.get(2), "bar", 4, 7); + assert!(captures.get(3).is_none()); +} + +#[test] +fn captures_with_nested_keepout() { + let captures = captures(r"(\w\K)+\s*(\w+)(?=\.)", "foo bar."); + assert_eq!(captures.len(), 3); + assert_match(captures.get(0), " bar", 3, 7); + assert_match(captures.get(1), "o", 2, 3); + assert_match(captures.get(2), "bar", 4, 7); + assert!(captures.get(3).is_none()); +} + +#[test] +fn captures_with_conditional() { + let captures = captures(r"^(?((ab))c|d)$", "abc"); + assert_eq!(captures.len(), 2); + assert_match(captures.get(0), "abc", 0, 3); + assert_match(captures.get(1), "ab", 0, 2); + assert!(captures.get(2).is_none()); +} + +#[test] +fn captures_iter() { + let text = "11 21 33"; + + for (i, captures) in common::regex(r"(?P\d)\d") + .captures_iter(text) + .enumerate() + { + let captures = captures.unwrap(); + + match i { + 0 => { + assert_eq!(captures.len(), 2); + assert_match(captures.get(0), "11", 0, 2); + assert_match(captures.name("num"), "1", 0, 1); + } + 1 => { + assert_eq!(captures.len(), 2); + assert_match(captures.get(0), "21", 3, 5); + assert_match(captures.name("num"), "2", 3, 4); + } + 2 => { + assert_eq!(captures.len(), 2); + assert_match(captures.get(0), "33", 6, 8); + assert_match(captures.name("num"), "3", 6, 7); + } + i => panic!("Expected 3 captures, got {}", i + 1), + } + } +} + +#[test] +fn captures_iter_attributes() { + let text = "11 21 33"; + let regex = common::regex(r"(?P\d)\d"); + + let all_captures = regex.captures_iter(text); + + assert_eq!(all_captures.text(), text); + assert_eq!(regex.as_str(), all_captures.regex().as_str()); +} + +#[test] +fn captures_from_pos() { + let text = "11 21 33"; + + let regex = common::regex(r"(\d)\d"); + let captures = assert_captures(regex.captures_from_pos(text, 3)); + assert_eq!(captures.len(), 2); + assert_match(captures.get(0), "21", 3, 5); + assert_match(captures.get(1), "2", 3, 4); + let matches: Vec<_> = captures.iter().collect(); + assert_eq!(matches.len(), 2); + assert_match(matches[0], "21", 3, 5); + assert_match(matches[1], "2", 3, 4); + + let regex = common::regex(r"(\d+)\1"); + let captures = assert_captures(regex.captures_from_pos(text, 3)); + assert_eq!(captures.len(), 2); + assert_match(captures.get(0), "33", 6, 8); + assert_match(captures.get(1), "3", 6, 7); + let matches: Vec<_> = captures.iter().collect(); + assert_eq!(matches.len(), 2); + assert_match(matches[0], "33", 6, 8); + assert_match(matches[1], "3", 6, 7); + + let regex = common::regex(r"(?P\d+)\k"); + let captures = assert_captures(regex.captures_from_pos(text, 3)); + assert_eq!(captures.len(), 2); + assert_match(captures.get(0), "33", 6, 8); + assert_match(captures.name("foo"), "3", 6, 7); + let matches: Vec<_> = captures.iter().collect(); + assert_eq!(matches.len(), 2); + assert_match(matches[0], "33", 6, 8); + assert_match(matches[1], "3", 6, 7); + + let regex = common::regex(r"(?P\d+)(?P=foo)"); + let captures = assert_captures(regex.captures_from_pos(text, 3)); + assert_eq!(captures.len(), 2); + assert_match(captures.get(0), "33", 6, 8); + assert_match(captures.name("foo"), "3", 6, 7); + let matches: Vec<_> = captures.iter().collect(); + assert_eq!(matches.len(), 2); + assert_match(matches[0], "33", 6, 8); + assert_match(matches[1], "3", 6, 7); +} + +#[test] +fn captures_from_pos_looking_left() { + let regex = common::regex(r"\b(\w)"); + + // This should *not* match because `\b` doesn't match between a and x + let result = regex.captures_from_pos("ax", 1).unwrap(); + assert!(result.is_none()); + + let captures = assert_captures(regex.captures_from_pos(".x", 1)); + assert_eq!(captures.len(), 2); + assert_match(captures.get(0), "x", 1, 2); + assert_match(captures.get(1), "x", 1, 2); +} + +#[cfg_attr(feature = "track_caller", track_caller)] +fn captures<'a>(re: &str, text: &'a str) -> Captures<'a> { + let regex = common::regex(re); + let result = regex.captures(text); + assert_captures(result) +} + +#[cfg_attr(feature = "track_caller", track_caller)] +fn assert_captures(result: Result>>) -> Captures<'_> { + assert!( + result.is_ok(), + "Expected captures to succeed, but was {:?}", + result + ); + let captures = result.unwrap(); + assert!( + captures.is_some(), + "Expected captures, but was {:?}", + captures + ); + captures.unwrap() +} + +#[cfg_attr(feature = "track_caller", track_caller)] +fn assert_match(m: Option>, expected_text: &str, start: usize, end: usize) { + assert!(m.is_some(), "Expected match, but was {:?}", m); + let m = m.unwrap(); + assert_eq!(m.as_str(), expected_text); + assert_eq!(m.start(), start); + assert_eq!(m.end(), end); +} + +#[test] +fn expand() { + let regex = common::regex("(a)(b)(?<π>c)(?Pd)"); + let cap = regex.captures("abcd").unwrap().expect("matched"); + assert_expansion(&cap, "$0", "abcd"); + assert_expansion(&cap, "$1", "a"); + assert_expansion(&cap, "$2", "b"); + assert_expansion(&cap, "$3", "c"); + assert_expansion(&cap, "$4", "d"); + assert_expansion(&cap, "$π", "c"); + assert_expansion(&cap, "$x", "d"); + assert_expansion(&cap, "$0π", ""); + assert_expansion(&cap, "$1π", ""); + assert_expansion(&cap, "$2π", ""); + assert_expansion(&cap, "$3π", ""); + assert_expansion(&cap, "$4π", ""); + assert_expansion(&cap, "$ππ", ""); + assert_expansion(&cap, "$xπ", ""); + assert_expansion(&cap, "${0}π", "abcdπ"); + assert_expansion(&cap, "${1}π", "aπ"); + assert_expansion(&cap, "${2}π", "bπ"); + assert_expansion(&cap, "${3}π", "cπ"); + assert_expansion(&cap, "${4}π", "dπ"); + assert_expansion(&cap, "${π}π", "cπ"); + assert_expansion(&cap, "${x}π", "dπ"); + assert_expansion(&cap, "$", "$"); + assert_expansion(&cap, "$π√", "c√"); + assert_expansion(&cap, "$x√", "d√"); + assert_expansion(&cap, "$$π", "$π"); + assert_expansion(&cap, "${π", "${π"); + assert_python_expansion(&cap, "\\0", "abcd"); + assert_python_expansion(&cap, "\\1", "a"); + assert_python_expansion(&cap, "\\2", "b"); + assert_python_expansion(&cap, "\\3", "c"); + assert_python_expansion(&cap, "\\4", "d"); + assert_python_expansion(&cap, "\\π", "\\π"); + assert_python_expansion(&cap, "\\x", "\\x"); + assert_python_expansion(&cap, "\\0π", "abcdπ"); + assert_python_expansion(&cap, "\\1π", "aπ"); + assert_python_expansion(&cap, "\\2π", "bπ"); + assert_python_expansion(&cap, "\\3π", "cπ"); + assert_python_expansion(&cap, "\\4π", "dπ"); + assert_python_expansion(&cap, "\\ππ", "\\ππ"); + assert_python_expansion(&cap, "\\xπ", "\\xπ"); + assert_python_expansion(&cap, "\\g<0>π", "abcdπ"); + assert_python_expansion(&cap, "\\g<1>π", "aπ"); + assert_python_expansion(&cap, "\\g<2>π", "bπ"); + assert_python_expansion(&cap, "\\g<3>π", "cπ"); + assert_python_expansion(&cap, "\\g<4>π", "dπ"); + assert_python_expansion(&cap, "\\g<π>π", "cπ"); + assert_python_expansion(&cap, "\\gπ", "dπ"); + assert_python_expansion(&cap, "\\", "\\"); + assert_python_expansion(&cap, "\\\\π", "\\π"); + assert_python_expansion(&cap, "\\g<π", "\\g<π"); +} + +#[cfg_attr(feature = "track_caller", track_caller)] +fn assert_expansion(cap: &Captures, replacement: &str, text: &str) { + let mut buf = "before".to_string(); + cap.expand(replacement, &mut buf); + assert_eq!(buf, format!("before{}", text)); +} + +#[cfg_attr(feature = "track_caller", track_caller)] +fn assert_python_expansion(cap: &Captures, replacement: &str, text: &str) { + assert_eq!(Expander::python().expansion(replacement, cap), text); +} + +#[test] +fn expander_escape() { + match Expander::default().escape("hello") { + Cow::Borrowed(s) => assert_eq!(s, "hello"), + _ => panic!("string should be borrowed"), + } + assert_eq!(Expander::default().escape("a$b\\c"), "a$$b\\c"); + assert_eq!(Expander::python().escape("a$b\\c"), "a$b\\\\c"); +} + +#[test] +fn expander_errors() { + let with_names = common::regex("(?a)"); + let without_names = common::regex("(a)"); + let exp = Expander::default(); + + macro_rules! assert_err { + ($expr:expr, $err:pat) => { + match $expr { + Err($err) => {} + x => panic!("wrong result: {:?}", x), + } + }; + } + + // Substitution char at end of template. + assert_err!(exp.check("$", &with_names), Error::ParseError(_, _)); + + // Substitution char not followed by a name or number. + assert_err!(exp.check("$.", &with_names), Error::ParseError(_, _)); + + // Empty delimiter pair. + assert_err!(exp.check("${}", &with_names), Error::ParseError(_, _)); + + // Unterminated delimiter pair. + assert_err!(exp.check("${", &with_names), Error::ParseError(_, _)); + + // Group 0 is always OK. + assert!(exp.check("$0", &with_names).is_ok()); + assert!(exp.check("$0", &without_names).is_ok()); + + // Can't use numbers with named groups. + assert_err!( + exp.check("$1", &with_names), + Error::CompileError(CompileError::NamedBackrefOnly) + ); + assert_err!( + exp.check("${1}", &with_names), + Error::CompileError(CompileError::NamedBackrefOnly) + ); + + // Unmatched group number. + assert_err!( + exp.check("$2", &without_names), + Error::CompileError(CompileError::InvalidBackref) + ); + assert_err!( + exp.check("${2}", &without_names), + Error::CompileError(CompileError::InvalidBackref) + ); + + // Unmatched group name. + assert_err!( + exp.check("$xx", &with_names), + Error::CompileError(CompileError::InvalidBackref) + ); + assert_err!( + exp.check("${xx}", &with_names), + Error::CompileError(CompileError::InvalidBackref) + ); +} diff --git a/tools/vendor/fancy-regex/tests/common/mod.rs b/tools/vendor/fancy-regex/tests/common/mod.rs new file mode 100644 index 0000000000..2daf249aa5 --- /dev/null +++ b/tools/vendor/fancy-regex/tests/common/mod.rs @@ -0,0 +1,12 @@ +use fancy_regex::Regex; + +pub fn regex(re: &str) -> Regex { + let parse_result = Regex::new(re); + assert!( + parse_result.is_ok(), + "Expected regex '{}' to be compiled successfully, got {:?}", + re, + parse_result.err() + ); + parse_result.unwrap() +} diff --git a/tools/vendor/fancy-regex/tests/finding.rs b/tools/vendor/fancy-regex/tests/finding.rs new file mode 100644 index 0000000000..164544572c --- /dev/null +++ b/tools/vendor/fancy-regex/tests/finding.rs @@ -0,0 +1,291 @@ +mod common; + +use fancy_regex::{Match, Regex}; +use std::ops::Range; + +#[test] +fn match_api() { + let m = find_match(r"(\w+)", "... test").unwrap(); + assert_eq!(m.range(), (4..8)); + assert_eq!(Range::from(m), (4..8)); + assert_eq!(m.as_str(), "test"); +} + +#[test] +fn find_wrap() { + assert_eq!(find(r"(\w+)", "... test"), Some((4, 8))); + assert_eq!(find(r"(?m)^yes$", "foo\nyes\n"), Some((4, 7))); +} + +#[test] +fn find_fancy_case_insensitive() { + assert_eq!(find(r"(x|xy)\1", "XX"), None); + assert_eq!(find(r"(x|xy)\1", "xx"), Some((0, 2))); + assert_eq!(find(r"((?i:x|xy))\1", "XX"), Some((0, 2))); +} + +#[test] +fn lookahead_grouping_single_expression() { + // These would fail if the delegate expression was `^x|a` (if we didn't + // group as `^(?:x|a)`). + assert_eq!(find(r"(?=x|a)", "a"), Some((0, 0))); + assert_eq!(find(r"(?=x|a)", "bbba"), Some((3, 3))); +} + +#[test] +fn lookahead_grouping_multiple_expressions() { + // These would fail if the delegate expression was `^ab|Bc` (if we didn't + // preserve grouping of `(?:b|B)`). + assert_eq!(find(r"(?=(?!x)a(?:b|B)c)", "aBc"), Some((0, 0))); + assert_eq!(find(r"(?=(?!x)a(?:b|B)c)", "Bc"), None); +} + +#[test] +fn lookbehind_grouping_single_expression() { + assert_eq!(find(r"(?<=x|a)", "a"), Some((1, 1))); + assert_eq!(find(r"(?<=x|a)", "ba"), Some((2, 2))); + assert_eq!(find(r"(?<=^a)", "a"), Some((1, 1))); + assert_eq!(find(r"(?<=^a)", "ba"), None); +} + +#[test] +fn lookbehind_variable_sized_alt() { + assert_eq!(find(r"(?<=a|bc)", "xxa"), Some((3, 3))); + assert_eq!(find(r"(?<=a|bc)", "xxbc"), Some((4, 4))); + assert_eq!(find(r"(?<=a|bc)", "xx"), None); + assert_eq!(find(r"(?<=a|bc)", "xxb"), None); + assert_eq!(find(r"(?<=a|bc)", "xxc"), None); + + assert!(Regex::new(r"(?<=a(?:b|cd))").is_err()); + assert!(Regex::new(r"(?<=a+b+))").is_err()); +} + +#[test] +fn negative_lookbehind_variable_sized_alt() { + assert_eq!(find(r"(? assert_eq!((mat.start(), mat.end()), (0, 2)), + 1 => assert_eq!((mat.start(), mat.end()), (3, 5)), + 2 => assert_eq!((mat.start(), mat.end()), (6, 8)), + i => panic!("Expected 3 captures, got {}", i + 1), + } + } +} + +#[test] +fn find_iter_overlapping_lookahead() { + let text = "abcdef"; + + for (i, mat) in common::regex(r"[a-z]{2}(?=[a-z])") + .find_iter(text) + .enumerate() + { + let mat = mat.unwrap(); + + match i { + 0 => assert_eq!((mat.start(), mat.end()), (0, 2)), + 1 => assert_eq!((mat.start(), mat.end()), (2, 4)), + i => panic!("Expected 2 captures, got {}", i + 1), + } + } +} + +#[test] +fn find_iter_zero_length() { + let text = "ab1c2"; + + for (i, mat) in common::regex(r"\d*(?=[a-z])").find_iter(text).enumerate() { + let mat = mat.unwrap(); + + match i { + 0 => assert_eq!((mat.start(), mat.end()), (0, 0)), + 1 => assert_eq!((mat.start(), mat.end()), (1, 1)), + 2 => assert_eq!((mat.start(), mat.end()), (2, 3)), + i => panic!("Expected 3 captures, got {}", i + 1), + } + } +} + +#[test] +fn find_iter_zero_length_longer_codepoint() { + let text = "é1é"; + + for (i, mat) in common::regex(r"\d*(?=é)").find_iter(text).enumerate() { + let mat = mat.unwrap(); + + match i { + 0 => assert_eq!((mat.start(), mat.end()), (0, 0)), + 1 => assert_eq!((mat.start(), mat.end()), (2, 3)), + i => panic!("Expected 2 captures, got {}", i + 1), + } + } +} + +#[test] +fn find_iter_continue_from_previous_match_end() { + let text = "1122 33"; + + for (i, mat) in common::regex(r"\G(\d)\d").find_iter(text).enumerate() { + let mat = mat.unwrap(); + + match i { + 0 => assert_eq!((mat.start(), mat.end()), (0, 2)), + 1 => assert_eq!((mat.start(), mat.end()), (2, 4)), + i => panic!("Expected 2 results, got {}", i + 1), + } + } +} + +#[test] +fn find_iter_continue_from_previous_match_end_with_zero_width_match() { + let text = "1122 33"; + + for (i, mat) in common::regex(r"\G\d*").find_iter(text).enumerate() { + let mat = mat.unwrap(); + + match i { + 0 => assert_eq!((mat.start(), mat.end()), (0, 4)), + i => panic!("Expected 1 result, got {}", i + 1), + } + } +} + +#[test] +fn find_iter_attributes() { + let text = "ab1c2"; + let regex = common::regex(r"\d*(?=[a-z])"); + + let matches = regex.find_iter(text); + + assert_eq!(matches.text(), text); + assert_eq!(regex.as_str(), matches.regex().as_str()); +} + +#[test] +fn find_conditional() { + assert_eq!(find(r"(?(ab)c|d)", "acd"), Some((2, 3))); + assert_eq!(find(r"(a)?b(?(1)c|d)", "abc"), Some((0, 3))); + assert_eq!(find(r"(a)?b(?(1)c|d)", "abd"), Some((1, 3))); +} + +fn find(re: &str, text: &str) -> Option<(usize, usize)> { + find_match(re, text).map(|m| (m.start(), m.end())) +} + +fn find_match<'t>(re: &str, text: &'t str) -> Option> { + let regex = common::regex(re); + let result = regex.find(text); + assert!( + result.is_ok(), + "Expected find to succeed, but was {:?}", + result + ); + result.unwrap() +} + +#[test] +fn incomplete_escape_sequences() { + // See GH-76 + assert!(Regex::new("\\u").is_err()); + assert!(Regex::new("\\U").is_err()); + assert!(Regex::new("\\x").is_err()); +} diff --git a/tools/vendor/fancy-regex/tests/matching.rs b/tools/vendor/fancy-regex/tests/matching.rs new file mode 100644 index 0000000000..0686ff3ee7 --- /dev/null +++ b/tools/vendor/fancy-regex/tests/matching.rs @@ -0,0 +1,187 @@ +use fancy_regex::{Error, RegexBuilder, RuntimeError}; + +mod common; + +#[test] +fn control_character_escapes() { + assert_match(r"\a", "\x07"); + assert_match(r"\e", "\x1B"); + assert_match(r"\f", "\x0C"); + assert_match(r"\n", "\x0A"); + assert_match(r"\r", "\x0D"); + assert_match(r"\t", "\x09"); + assert_match(r"\v", "\x0B"); +} + +#[test] +fn character_class_escapes() { + assert_match(r"[\[]", "["); + assert_match(r"[\^]", "^"); + + // The regex crate would reject the following because it's not necessary to escape them. + // Other engines allow to escape any non-alphanumeric character. + assert_match(r"[\<]", "<"); + assert_match(r"[\>]", ">"); + assert_match(r"[\.]", "."); + assert_match(r"[\ ]", " "); + + // Character class escape + assert_match(r"[\d]", "1"); + + // Control characters + assert_match(r"[\e]", "\x1B"); + assert_match(r"[\n]", "\x0A"); + + // `]` can be unescaped if it's right after `[` + assert_match(r"[]]", "]"); + // `]` can be unescaped even after `[^` + assert_match(r"[^]]", "a"); +} + +#[test] +fn character_class_nested() { + assert_match(r"[[a][bc]]", "c"); + assert_match(r"[a[^b]]", "c"); +} + +#[test] +fn character_class_intersection() { + assert_match(r"[\w&&a-c]", "c"); + assert_no_match(r"[\w&&a-c]", "d"); + + assert_match(r"[[0-9]&&[^4]]", "1"); + assert_no_match(r"[[0-9]&&[^4]]", "4"); +} + +#[test] +fn alternation_with_empty_arm() { + assert_match(r"^(a|)$", "a"); + assert_match(r"^(a|)$", ""); + assert_match(r"^(|a)$", "a"); + assert_match(r"^(|a)$", ""); + assert_match(r"a|", "a"); + assert_match(r"a|", ""); + assert_match(r"|a", "a"); + assert_match(r"|a", ""); + assert_no_match(r"^(a|)$", "b"); +} + +#[test] +fn case_insensitive_character_class() { + assert_match(r"^(?i)[a-z]+$", "aB"); +} + +#[test] +fn case_insensitive_escape() { + // `\x61` is lowercase `a` + assert_match(r"(?i)\x61", "A"); + + // `\p{Ll}` is the "Letter, lowercase" category + assert_match(r"(?i)\p{Ll}", "A"); +} + +#[test] +fn atomic_group() { + assert_match(r"^a(?>bc|b)c$", "abcc"); + assert_no_match(r"^a(?>bc|b)c$", "abc"); + + // Look-ahead forces use of VM + assert_match(r"^a(bc(?=d)|b)cd$", "abcd"); + assert_no_match(r"^a(?>bc(?=d)|b)cd$", "abcd"); +} + +#[test] +fn backtrack_limit() { + let re = RegexBuilder::new("(?i)(a|b|ab)*(?=c)") + .backtrack_limit(100_000) + .build() + .unwrap(); + let s = "abababababababababababababababababababababababababababab"; + let result = re.is_match(s); + assert!(result.is_err()); + match result.err() { + Some(Error::RuntimeError(RuntimeError::BacktrackLimitExceeded)) => {} + _ => panic!("Expected RuntimeError::BacktrackLimitExceeded"), + } +} + +#[test] +fn end_of_hard_expression_cannot_be_delegated() { + assert_match(r"(?!x)(?:a|ab)c", "abc"); + // If `(?:a|ab)` is delegated, there's no backtracking and `a` matches and `ab` is never tried. + assert_match(r"((?!x)(?:a|ab))c", "abc"); +} + +#[test] +fn issue103() { + assert_no_match(r"(([ab]+)\1b)", "babab"); +} + +#[test] +fn backreference_validity_checker() { + assert_match(r"(a)(?(1))", "a"); + assert_match(r"(?a)(?('group1'))b", "ab"); + assert_match(r"(a)(b)?(?(2))", "ab"); + assert_no_match(r"(a)(b)?(?(2))", "a"); + assert_match(r"(a)(b?)(?(2))", "a"); +} + +#[test] +fn conditional_with_backref_validity() { + assert_match(r"(a)?b(?(1)c|d)", "bd"); + assert_match(r"(a)?b(?(1)c|d)", "abc"); + assert_match(r"(?a)?b(?()c|d)", "abc"); + assert_no_match(r"(a)?b(?(1)c|d)", "bc"); + assert_no_match(r"^(a)?b(?(1)c|d)$", "abd"); +} + +#[test] +fn conditional_with_consuming_condition() { + assert_match(r"^(?(ab)c|d)$", "abc"); + assert_no_match(r"^(?(ab)c|d)$", "abd"); + assert_no_match(r"^(?(ab)c|d)$", "ad"); + + assert_match(r"^(?(\d)abc|\d!)$", "5abc"); + assert_no_match(r"^(?(\d)abc|\d!)$", "5!"); +} + +#[test] +fn conditional_with_lookaround_condition() { + assert_match(r"^(?((?=\d))\w+|!)$", "!"); + assert_match(r"^(?((?=\d))\w+|!)$", "5abc"); + assert_match(r"^(?((?=\d))abc)$", ""); + assert_match(r"^(?((?=\w))abc)$", "abc"); + assert_no_match(r"^(?((?=\d))\wabc|\d!)$", "5!"); +} + +#[cfg_attr(feature = "track_caller", track_caller)] +fn assert_match(re: &str, text: &str) { + let result = match_text(re, text); + assert_eq!( + result, true, + "Expected regex '{}' to match text '{}'", + re, text + ); +} + +#[cfg_attr(feature = "track_caller", track_caller)] +fn assert_no_match(re: &str, text: &str) { + let result = match_text(re, text); + assert_eq!( + result, false, + "Expected regex '{}' to not match text '{}'", + re, text + ); +} + +#[cfg_attr(feature = "track_caller", track_caller)] +fn match_text(re: &str, text: &str) -> bool { + let regex = common::regex(re); + let result = regex.is_match(text); + assert!( + result.is_ok(), + "Expected match to succeed, but was {:?}", + result + ); + result.unwrap() +} diff --git a/tools/vendor/fancy-regex/tests/oniguruma.rs b/tools/vendor/fancy-regex/tests/oniguruma.rs new file mode 100644 index 0000000000..d9e448200a --- /dev/null +++ b/tools/vendor/fancy-regex/tests/oniguruma.rs @@ -0,0 +1,240 @@ +//! Run tests from Oniguruma's test suite, see `oniguruma/README.md` + +use std::collections::HashMap; +use std::panic; + +use regex::Regex; + +use fancy_regex::Regex as FancyRegex; + +#[derive(Debug, Eq, Hash, PartialEq)] +struct Test { + source: String, + pattern: String, + text: String, + assertion: Assertion, +} + +#[derive(Debug, Eq, Hash, PartialEq)] +enum Assertion { + Match { + group: usize, + start: usize, + end: usize, + }, + NoMatch, +} + +/// Extract tests from the C source file (or the ignore file). +/// +/// Returns a vec of tuple of the test data and the comment for the test. +fn parse_tests(test_source: &str) -> Vec<(Test, String)> { + let mut tests = Vec::new(); + + let c_string = r#""((?:\\\\|\\"|[^"])*)""#; + let re = Regex::new(&format!( + r"(?m)((?:^ //.*\n)*)^\s*((x2|x3|n)\({},\s*{},?([^\)]+)\);)", + c_string, c_string + )) + .unwrap(); + for caps in re.captures_iter(test_source) { + let comment = caps + .get(1) + .unwrap() + .as_str() + .replace(" // ", "") + .trim() + .to_string(); + let source = caps.get(2).unwrap().as_str().to_string(); + let kind = caps.get(3).unwrap().as_str(); + let pattern = unescape(caps.get(4).unwrap().as_str()); + let text = unescape(caps.get(5).unwrap().as_str()); + let args: Vec = caps + .get(6) + .unwrap() + .as_str() + .split(",") + .map(|s| s.trim().parse().unwrap()) + .collect(); + + let assertion = match kind { + "x2" => Assertion::Match { + start: args[0], + end: args[1], + group: 0, + }, + "x3" => Assertion::Match { + start: args[0], + end: args[1], + group: args[2], + }, + "n" => Assertion::NoMatch, + _ => { + panic!("Unexpected test type {}", kind); + } + }; + + let test = Test { + source, + pattern, + text, + assertion, + }; + + tests.push((test, comment)); + } + tests +} + +/// Unescape a string as it appears in C source. This is probably not a perfect implementation, but +/// it's good enough for these tests. +fn unescape(escaped: &str) -> String { + let mut s: Vec = Vec::new(); + let mut chars = escaped.chars(); + while let Some(c) = chars.next() { + match c { + '\\' => { + let next = chars.next().expect("Expected character after backslash"); + match next { + '\\' => { + s.push(b'\\'); + } + '"' => { + s.push(b'"'); + } + '?' => { + // '?' has to be escaped in C to avoid trigraphs + s.push(b'?'); + } + 'n' => { + s.push(b'\n'); + } + 'r' => { + s.push(b'\r'); + } + '0' => { + // octal escape, e.g. \001 + let mut octal = String::new(); + octal.push(chars.next().expect("Expected character after \\0")); + octal.push(chars.next().expect("Expected second character after \\0")); + let num = + u8::from_str_radix(&octal, 8).expect("Error parsing octal number"); + s.push(num); + } + 'x' => { + // hex escape, e.g. \x1f + let mut hex = String::new(); + hex.push(chars.next().expect("Expected character after \\x")); + hex.push(chars.next().expect("Expected second character after \\x")); + let num = u8::from_str_radix(&hex, 16).expect("Error parsing hex number"); + s.push(num); + } + _ => { + unimplemented!("Unknown escaped character {} in {}", next, escaped); + } + } + } + _ => { + s.append(&mut c.to_string().into_bytes()); + } + } + } + // Some strings in the test are invalid UTF-8. We handle them via ignores. + String::from_utf8_lossy(&s).to_string() +} + +fn run_test(test: &Test) -> Option { + let Test { + pattern, + text, + assertion, + .. + } = test; + + let compile_result = FancyRegex::new(pattern); + let Ok(regex) = compile_result else { + let error = format!("{:?}", compile_result.unwrap_err()); + return Some(format!("Compile failed: {}", error)); + }; + + match *assertion { + Assertion::Match { group, start, end } => { + let captures_result = regex.captures(&text).unwrap(); + + if let Some(captures) = captures_result { + let Some(m) = captures.get(group) else { + return Some("Expected group to exist".to_owned()); + }; + if m.start() != start || m.end() != end { + Some(format!( + "Match found at start {} and end {} (expected {} and {})", + m.start(), + m.end(), + start, + end + )) + } else { + None + } + } else { + Some("No match found".to_string()) + } + } + Assertion::NoMatch => { + let regex = FancyRegex::new(pattern).unwrap(); + let result = regex.find(&text).unwrap(); + if result.is_some() { + Some("Match found".to_string()) + } else { + // We expected it not to match and it didn't -> good + None + } + } + } +} + +#[test] +fn oniguruma() { + let tests: Vec = parse_tests(include_str!("oniguruma/test_utf8.c")) + .into_iter() + .map(|(test, _comment)| test) + .collect(); + let ignore: HashMap = parse_tests(include_str!("oniguruma/test_utf8_ignore.c")) + .into_iter() + .collect(); + + let mut ignored = 0; + let mut success = 0; + + for test in tests { + let result = run_test(&test); + + if let Some(expected_failure) = ignore.get(&test) { + assert!(result.is_some(), + "Expected ignored test to fail, but it succeeded. Remove it from the ignore file: {}", &test.source); + let failure = result.unwrap(); + assert!(failure.starts_with(expected_failure), + "Expected failure differed for test, change it in the ignore file: {}\nExpected: {}\nActual : {}\n", + &test.source, &expected_failure, &failure + ); + ignored += 1; + } else { + if let Some(failure) = result { + // This is a weird way to do the assertions, but the nice thing about it is that we + // can run the tests without an "ignore" file and instead of failing, print the + // content for the ignore file. To do that, disable the assert and enable the print: + + // println!(" // {}\n {}\n", failure, test.source); + assert!(false, "Test {} failed: {}", &test.source, failure); + } else { + // println!("Success: {}", test.source); + success += 1; + } + } + } + + println!( + "{} successful Oniguruma tests, {} ignored", + success, ignored + ); +} diff --git a/tools/vendor/fancy-regex/tests/oniguruma/.gitattributes b/tools/vendor/fancy-regex/tests/oniguruma/.gitattributes new file mode 100644 index 0000000000..7d949a450f --- /dev/null +++ b/tools/vendor/fancy-regex/tests/oniguruma/.gitattributes @@ -0,0 +1,2 @@ +# Imported from oniguruma +*.c linguist-vendored diff --git a/tools/vendor/fancy-regex/tests/oniguruma/README.md b/tools/vendor/fancy-regex/tests/oniguruma/README.md new file mode 100644 index 0000000000..4f7678f61c --- /dev/null +++ b/tools/vendor/fancy-regex/tests/oniguruma/README.md @@ -0,0 +1,44 @@ +Oniguruma tests +=============== + +The tests in here are from the Oniguruma project, namely the file +[`test_utf8.c`](https://github.com/kkos/oniguruma/blob/master/test/test_utf8.c). +See below for that file's license. + +The `test_utf8_ignore.c` file is a subset with tests that were failing +against fancy-regex at the time of writing. + +The test case in `oniguruma.rs` reads both files, and executes the tests +unless they are in the ignore file. + +Some of the ignored tests should be fixed in fancy-regex, others can +probably stay ignored (e.g. supporting `a{3,2}`). + + +Oniguruma LICENSE +----------------- + +Copyright (c) 2002-2019 K.Kosako +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + diff --git a/tools/vendor/fancy-regex/tests/oniguruma/test_utf8.c b/tools/vendor/fancy-regex/tests/oniguruma/test_utf8.c new file mode 100644 index 0000000000..33e03bd994 --- /dev/null +++ b/tools/vendor/fancy-regex/tests/oniguruma/test_utf8.c @@ -0,0 +1,1231 @@ +/* + * test_utf8.c + * Copyright (c) 2019 K.Kosako + */ +#include "config.h" +#ifdef ONIG_ESCAPE_UCHAR_COLLISION +#undef ONIG_ESCAPE_UCHAR_COLLISION +#endif +#include + +#include "oniguruma.h" + +#include + +#define SLEN(s) strlen(s) + +static int nsucc = 0; +static int nfail = 0; +static int nerror = 0; + +static FILE* err_file; + +static OnigRegion* region; + +static void xx(char* pattern, char* str, int from, int to, int mem, int not, + int error_no) +{ + int r; + regex_t* reg; + OnigErrorInfo einfo; + + r = onig_new(®, (UChar* )pattern, (UChar* )(pattern + SLEN(pattern)), + ONIG_OPTION_DEFAULT, ONIG_ENCODING_UTF8, ONIG_SYNTAX_DEFAULT, &einfo); + if (r) { + char s[ONIG_MAX_ERROR_MESSAGE_LEN]; + + if (error_no == 0) { + onig_error_code_to_str((UChar* )s, r, &einfo); + fprintf(err_file, "ERROR: %s /%s/\n", s, pattern); + nerror++; + } + else { + if (r == error_no) { + fprintf(stdout, "OK(ERROR): /%s/ %d\n", pattern, r); + nsucc++; + } + else { + fprintf(stdout, "FAIL(ERROR): /%s/ '%s', %d, %d\n", pattern, str, + error_no, r); + nfail++; + } + } + + return ; + } + + r = onig_search(reg, (UChar* )str, (UChar* )(str + SLEN(str)), + (UChar* )str, (UChar* )(str + SLEN(str)), + region, ONIG_OPTION_NONE); + if (r < ONIG_MISMATCH) { + char s[ONIG_MAX_ERROR_MESSAGE_LEN]; + + if (error_no == 0) { + onig_error_code_to_str((UChar* )s, r); + fprintf(err_file, "ERROR: %s /%s/\n", s, pattern); + nerror++; + } + else { + if (r == error_no) { + fprintf(stdout, "OK(ERROR): /%s/ '%s', %d\n", pattern, str, r); + nsucc++; + } + else { + fprintf(stdout, "FAIL ERROR NO: /%s/ '%s', %d, %d\n", pattern, str, + error_no, r); + nfail++; + } + } + + return ; + } + + if (r == ONIG_MISMATCH) { + if (not) { + fprintf(stdout, "OK(N): /%s/ '%s'\n", pattern, str); + nsucc++; + } + else { + fprintf(stdout, "FAIL: /%s/ '%s'\n", pattern, str); + nfail++; + } + } + else { + if (not) { + fprintf(stdout, "FAIL(N): /%s/ '%s'\n", pattern, str); + nfail++; + } + else { + if (region->beg[mem] == from && region->end[mem] == to) { + fprintf(stdout, "OK: /%s/ '%s'\n", pattern, str); + nsucc++; + } + else { + fprintf(stdout, "FAIL: /%s/ '%s' %d-%d : %d-%d\n", pattern, str, + from, to, region->beg[mem], region->end[mem]); + nfail++; + } + } + } + onig_free(reg); +} + +static void x2(char* pattern, char* str, int from, int to) +{ + xx(pattern, str, from, to, 0, 0, 0); +} + +static void x3(char* pattern, char* str, int from, int to, int mem) +{ + xx(pattern, str, from, to, mem, 0, 0); +} + +static void n(char* pattern, char* str) +{ + xx(pattern, str, 0, 0, 0, 1, 0); +} + +static void e(char* pattern, char* str, int error_no) +{ + xx(pattern, str, 0, 0, 0, 0, error_no); +} + +extern int main(int argc, char* argv[]) +{ + static OnigEncoding use_encs[] = { ONIG_ENCODING_UTF8 }; + + onig_initialize(use_encs, sizeof(use_encs)/sizeof(use_encs[0])); + + err_file = stdout; + + region = onig_region_new(); + + x2("", "", 0, 0); + x2("^", "", 0, 0); + x2("^a", "\na", 1, 2); + x2("$", "", 0, 0); + x2("$\\O", "bb\n", 2, 3); + x2("\\G", "", 0, 0); + x2("\\A", "", 0, 0); + x2("\\Z", "", 0, 0); + x2("\\z", "", 0, 0); + x2("^$", "", 0, 0); + x2("\\ca", "\001", 0, 1); + x2("\\C-b", "\002", 0, 1); + x2("\\c\\\\", "\034", 0, 1); + x2("q[\\c\\\\]", "q\034", 0, 2); + x2("", "a", 0, 0); + x2("a", "a", 0, 1); + x2("\\x61", "a", 0, 1); + x2("aa", "aa", 0, 2); + x2("aaa", "aaa", 0, 3); + x2("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 0, 35); + x2("ab", "ab", 0, 2); + x2("b", "ab", 1, 2); + x2("bc", "abc", 1, 3); + x2("(?i:#RET#)", "#INS##RET#", 5, 10); + x2("\\17", "\017", 0, 1); + x2("\\x1f", "\x1f", 0, 1); + x2("a(?#....\\\\JJJJ)b", "ab", 0, 2); + x2("(?x) G (o O(?-x)oO) g L", "GoOoOgLe", 0, 7); + x2(".", "a", 0, 1); + n(".", ""); + x2("..", "ab", 0, 2); + x2("\\w", "e", 0, 1); + n("\\W", "e"); + x2("\\s", " ", 0, 1); + x2("\\S", "b", 0, 1); + x2("\\d", "4", 0, 1); + n("\\D", "4"); + x2("\\b", "z ", 0, 0); + x2("\\b", " z", 1, 1); + x2("\\b", " z ", 2, 2); + x2("\\B", "zz ", 1, 1); + x2("\\B", "z ", 2, 2); + x2("\\B", " z", 0, 0); + x2("[ab]", "b", 0, 1); + n("[ab]", "c"); + x2("[a-z]", "t", 0, 1); + n("[^a]", "a"); + x2("[^a]", "\n", 0, 1); + x2("[]]", "]", 0, 1); + n("[^]]", "]"); + x2("[\\^]+", "0^^1", 1, 3); + x2("[b-]", "b", 0, 1); + x2("[b-]", "-", 0, 1); + x2("[\\w]", "z", 0, 1); + n("[\\w]", " "); + x2("[\\W]", "b$", 1, 2); + x2("[\\d]", "5", 0, 1); + n("[\\d]", "e"); + x2("[\\D]", "t", 0, 1); + n("[\\D]", "3"); + x2("[\\s]", " ", 0, 1); + n("[\\s]", "a"); + x2("[\\S]", "b", 0, 1); + n("[\\S]", " "); + x2("[\\w\\d]", "2", 0, 1); + n("[\\w\\d]", " "); + x2("[[:upper:]]", "B", 0, 1); + x2("[*[:xdigit:]+]", "+", 0, 1); + x2("[*[:xdigit:]+]", "GHIKK-9+*", 6, 7); + x2("[*[:xdigit:]+]", "-@^+", 3, 4); + n("[[:upper]]", "A"); + x2("[[:upper]]", ":", 0, 1); + x2("[\\044-\\047]", "\046", 0, 1); + x2("[\\x5a-\\x5c]", "\x5b", 0, 1); + x2("[\\x6A-\\x6D]", "\x6c", 0, 1); + n("[\\x6A-\\x6D]", "\x6E"); + n("^[0-9A-F]+ 0+ UNDEF ", "75F 00000000 SECT14A notype () External | _rb_apply"); + x2("[\\[]", "[", 0, 1); + x2("[\\]]", "]", 0, 1); + x2("[&]", "&", 0, 1); + x2("[[ab]]", "b", 0, 1); + x2("[[ab]c]", "c", 0, 1); + n("[[^a]]", "a"); + n("[^[a]]", "a"); + x2("[[ab]&&bc]", "b", 0, 1); + n("[[ab]&&bc]", "a"); + n("[[ab]&&bc]", "c"); + x2("[a-z&&b-y&&c-x]", "w", 0, 1); + n("[^a-z&&b-y&&c-x]", "w"); + x2("[[^a&&a]&&a-z]", "b", 0, 1); + n("[[^a&&a]&&a-z]", "a"); + x2("[[^a-z&&bcdef]&&[^c-g]]", "h", 0, 1); + n("[[^a-z&&bcdef]&&[^c-g]]", "c"); + x2("[^[^abc]&&[^cde]]", "c", 0, 1); + x2("[^[^abc]&&[^cde]]", "e", 0, 1); + n("[^[^abc]&&[^cde]]", "f"); + x2("[a-&&-a]", "-", 0, 1); + n("[a\\-&&\\-a]", "&"); + n("\\wabc", " abc"); + x2("a\\Wbc", "a bc", 0, 4); + x2("a.b.c", "aabbc", 0, 5); + x2(".\\wb\\W..c", "abb bcc", 0, 7); + x2("\\s\\wzzz", " zzzz", 0, 5); + x2("aa.b", "aabb", 0, 4); + n(".a", "ab"); + x2(".a", "aa", 0, 2); + x2("^a", "a", 0, 1); + x2("^a$", "a", 0, 1); + x2("^\\w$", "a", 0, 1); + n("^\\w$", " "); + x2("^\\wab$", "zab", 0, 3); + x2("^\\wabcdef$", "zabcdef", 0, 7); + x2("^\\w...def$", "zabcdef", 0, 7); + x2("\\w\\w\\s\\Waaa\\d", "aa aaa4", 0, 8); + x2("\\A\\Z", "", 0, 0); + x2("\\Axyz", "xyz", 0, 3); + x2("xyz\\Z", "xyz", 0, 3); + x2("xyz\\z", "xyz", 0, 3); + x2("a\\Z", "a", 0, 1); + x2("\\Gaz", "az", 0, 2); + n("\\Gz", "bza"); + n("az\\G", "az"); + n("az\\A", "az"); + n("a\\Az", "az"); + x2("\\^\\$", "^$", 0, 2); + x2("^x?y", "xy", 0, 2); + x2("^(x?y)", "xy", 0, 2); + x2("\\w", "_", 0, 1); + n("\\W", "_"); + x2("(?=z)z", "z", 0, 1); + n("(?=z).", "a"); + x2("(?!z)a", "a", 0, 1); + n("(?!z)a", "z"); + x2("(?i:a)", "a", 0, 1); + x2("(?i:a)", "A", 0, 1); + x2("(?i:A)", "a", 0, 1); + x2("(?i:i)", "I", 0, 1); + x2("(?i:I)", "i", 0, 1); + x2("(?i:[A-Z])", "i", 0, 1); + x2("(?i:[a-z])", "I", 0, 1); + n("(?i:A)", "b"); + x2("(?i:ss)", "ss", 0, 2); + x2("(?i:ss)", "Ss", 0, 2); + x2("(?i:ss)", "SS", 0, 2); + /* 0xc5,0xbf == 017F: # LATIN SMALL LETTER LONG S */ + x2("(?i:ss)", "\xc5\xbfS", 0, 3); + x2("(?i:ss)", "s\xc5\xbf", 0, 3); + /* 0xc3,0x9f == 00DF: # LATIN SMALL LETTER SHARP S */ + x2("(?i:ss)", "\xc3\x9f", 0, 2); + /* 0xe1,0xba,0x9e == 1E9E # LATIN CAPITAL LETTER SHARP S */ + x2("(?i:ss)", "\xe1\xba\x9e", 0, 3); + x2("(?i:xssy)", "xssy", 0, 4); + x2("(?i:xssy)", "xSsy", 0, 4); + x2("(?i:xssy)", "xSSy", 0, 4); + x2("(?i:xssy)", "x\xc5\xbfSy", 0, 5); + x2("(?i:xssy)", "xs\xc5\xbfy", 0, 5); + x2("(?i:xssy)", "x\xc3\x9fy", 0, 4); + x2("(?i:xssy)", "x\xe1\xba\x9ey", 0, 5); + x2("(?i:\xc3\x9f)", "ss", 0, 2); + x2("(?i:\xc3\x9f)", "SS", 0, 2); + x2("(?i:[\xc3\x9f])", "ss", 0, 2); + x2("(?i:[\xc3\x9f])", "SS", 0, 2); + x2("(?i)(?a|abd)c", "abdc"); + x2("(?>abd|a)c", "abdc", 0, 4); + x2("a?|b", "a", 0, 1); + x2("a?|b", "b", 0, 0); + x2("a?|b", "", 0, 0); + x2("a*|b", "aa", 0, 2); + x2("a*|b*", "ba", 0, 0); + x2("a*|b*", "ab", 0, 1); + x2("a+|b*", "", 0, 0); + x2("a+|b*", "bbb", 0, 3); + x2("a+|b*", "abbb", 0, 1); + n("a+|b+", ""); + x2("(a|b)?", "b", 0, 1); + x2("(a|b)*", "ba", 0, 2); + x2("(a|b)+", "bab", 0, 3); + x2("(ab|ca)+", "caabbc", 0, 4); + x2("(ab|ca)+", "aabca", 1, 5); + x2("(ab|ca)+", "abzca", 0, 2); + x2("(a|bab)+", "ababa", 0, 5); + x2("(a|bab)+", "ba", 1, 2); + x2("(a|bab)+", "baaaba", 1, 4); + x2("(?:a|b)(?:a|b)", "ab", 0, 2); + x2("(?:a*|b*)(?:a*|b*)", "aaabbb", 0, 3); + x2("(?:a*|b*)(?:a+|b+)", "aaabbb", 0, 6); + x2("(?:a+|b+){2}", "aaabbb", 0, 6); + x2("h{0,}", "hhhh", 0, 4); + x2("(?:a+|b+){1,2}", "aaabbb", 0, 6); + n("ax{2}*a", "0axxxa1"); + n("a.{0,2}a", "0aXXXa0"); + n("a.{0,2}?a", "0aXXXa0"); + n("a.{0,2}?a", "0aXXXXa0"); + x2("^a{2,}?a$", "aaa", 0, 3); + x2("^[a-z]{2,}?$", "aaa", 0, 3); + x2("(?:a+|\\Ab*)cc", "cc", 0, 2); + n("(?:a+|\\Ab*)cc", "abcc"); + x2("(?:^a+|b+)*c", "aabbbabc", 6, 8); + x2("(?:^a+|b+)*c", "aabbbbc", 0, 7); + x2("a|(?i)c", "C", 0, 1); + x2("(?i)c|a", "C", 0, 1); + x2("(?i)c|a", "A", 0, 1); + x2("a(?i)b|c", "aB", 0, 2); + x2("a(?i)b|c", "aC", 0, 2); + n("a(?i)b|c", "AC"); + n("a(?:(?i)b)|c", "aC"); + x2("(?i:c)|a", "C", 0, 1); + n("(?i:c)|a", "A"); + x2("[abc]?", "abc", 0, 1); + x2("[abc]*", "abc", 0, 3); + x2("[^abc]*", "abc", 0, 0); + n("[^abc]+", "abc"); + x2("a?\?", "aaa", 0, 0); + x2("ba?\?b", "bab", 0, 3); + x2("a*?", "aaa", 0, 0); + x2("ba*?", "baa", 0, 1); + x2("ba*?b", "baab", 0, 4); + x2("a+?", "aaa", 0, 1); + x2("ba+?", "baa", 0, 2); + x2("ba+?b", "baab", 0, 4); + x2("(?:a?)?\?", "a", 0, 0); + x2("(?:a?\?)?", "a", 0, 0); + x2("(?:a?)+?", "aaa", 0, 1); + x2("(?:a+)?\?", "aaa", 0, 0); + x2("(?:a+)?\?b", "aaab", 0, 4); + x2("(?:ab)?{2}", "", 0, 0); + x2("(?:ab)?{2}", "ababa", 0, 4); + x2("(?:ab)*{0}", "ababa", 0, 0); + x2("(?:ab){3,}", "abababab", 0, 8); + n("(?:ab){3,}", "abab"); + x2("(?:ab){2,4}", "ababab", 0, 6); + x2("(?:ab){2,4}", "ababababab", 0, 8); + x2("(?:ab){2,4}?", "ababababab", 0, 4); + x2("(?:ab){,}", "ab{,}", 0, 5); + x2("(?:abc)+?{2}", "abcabcabc", 0, 6); + x2("(?:X*)(?i:xa)", "XXXa", 0, 4); + x2("(d+)([^abc]z)", "dddz", 0, 4); + x2("([^abc]*)([^abc]z)", "dddz", 0, 4); + x2("(\\w+)(\\wz)", "dddz", 0, 4); + x3("(a)", "a", 0, 1, 1); + x3("(ab)", "ab", 0, 2, 1); + x2("((ab))", "ab", 0, 2); + x3("((ab))", "ab", 0, 2, 1); + x3("((ab))", "ab", 0, 2, 2); + x3("((((((((((((((((((((ab))))))))))))))))))))", "ab", 0, 2, 20); + x3("(ab)(cd)", "abcd", 0, 2, 1); + x3("(ab)(cd)", "abcd", 2, 4, 2); + x3("()(a)bc(def)ghijk", "abcdefghijk", 3, 6, 3); + x3("(()(a)bc(def)ghijk)", "abcdefghijk", 3, 6, 4); + x2("(^a)", "a", 0, 1); + x3("(a)|(a)", "ba", 1, 2, 1); + x3("(^a)|(a)", "ba", 1, 2, 2); + x3("(a?)", "aaa", 0, 1, 1); + x3("(a*)", "aaa", 0, 3, 1); + x3("(a*)", "", 0, 0, 1); + x3("(a+)", "aaaaaaa", 0, 7, 1); + x3("(a+|b*)", "bbbaa", 0, 3, 1); + x3("(a+|b?)", "bbbaa", 0, 1, 1); + x3("(abc)?", "abc", 0, 3, 1); + x3("(abc)*", "abc", 0, 3, 1); + x3("(abc)+", "abc", 0, 3, 1); + x3("(xyz|abc)+", "abc", 0, 3, 1); + x3("([xyz][abc]|abc)+", "abc", 0, 3, 1); + x3("((?i:abc))", "AbC", 0, 3, 1); + x2("(abc)(?i:\\1)", "abcABC", 0, 6); + x3("((?m:a.c))", "a\nc", 0, 3, 1); + x3("((?=az)a)", "azb", 0, 1, 1); + x3("abc|(.abd)", "zabd", 0, 4, 1); + x2("(?:abc)|(ABC)", "abc", 0, 3); + x3("(?i:(abc))|(zzz)", "ABC", 0, 3, 1); + x3("a*(.)", "aaaaz", 4, 5, 1); + x3("a*?(.)", "aaaaz", 0, 1, 1); + x3("a*?(c)", "aaaac", 4, 5, 1); + x3("[bcd]a*(.)", "caaaaz", 5, 6, 1); + x3("(\\Abb)cc", "bbcc", 0, 2, 1); + n("(\\Abb)cc", "zbbcc"); + x3("(^bb)cc", "bbcc", 0, 2, 1); + n("(^bb)cc", "zbbcc"); + x3("cc(bb$)", "ccbb", 2, 4, 1); + n("cc(bb$)", "ccbbb"); + n("(\\1)", ""); + n("\\1(a)", "aa"); + n("(a(b)\\1)\\2+", "ababb"); + n("(?:(?:\\1|z)(a))+$", "zaa"); + x2("(?:(?:\\1|z)(a))+$", "zaaa", 0, 4); + x2("(a)(?=\\1)", "aa", 0, 1); + n("(a)$|\\1", "az"); + x2("(a)\\1", "aa", 0, 2); + n("(a)\\1", "ab"); + x2("(a?)\\1", "aa", 0, 2); + x2("(a?\?)\\1", "aa", 0, 0); + x2("(a*)\\1", "aaaaa", 0, 4); + x3("(a*)\\1", "aaaaa", 0, 2, 1); + x2("a(b*)\\1", "abbbb", 0, 5); + x2("a(b*)\\1", "ab", 0, 1); + x2("(a*)(b*)\\1\\2", "aaabbaaabb", 0, 10); + x2("(a*)(b*)\\2", "aaabbbb", 0, 7); + x2("(((((((a*)b))))))c\\7", "aaabcaaa", 0, 8); + x3("(((((((a*)b))))))c\\7", "aaabcaaa", 0, 3, 7); + x2("(a)(b)(c)\\2\\1\\3", "abcbac", 0, 6); + x2("([a-d])\\1", "cc", 0, 2); + x2("(\\w\\d\\s)\\1", "f5 f5 ", 0, 6); + n("(\\w\\d\\s)\\1", "f5 f5"); + x2("(who|[a-c]{3})\\1", "whowho", 0, 6); + x2("...(who|[a-c]{3})\\1", "abcwhowho", 0, 9); + x2("(who|[a-c]{3})\\1", "cbccbc", 0, 6); + x2("(^a)\\1", "aa", 0, 2); + n("(^a)\\1", "baa"); + n("(a$)\\1", "aa"); + n("(ab\\Z)\\1", "ab"); + x2("(a*\\Z)\\1", "a", 1, 1); + x2(".(a*\\Z)\\1", "ba", 1, 2); + x3("(.(abc)\\2)", "zabcabc", 0, 7, 1); + x3("(.(..\\d.)\\2)", "z12341234", 0, 9, 1); + x2("((?i:az))\\1", "AzAz", 0, 4); + n("((?i:az))\\1", "Azaz"); + x2("(?<=a)b", "ab", 1, 2); + n("(?<=a)b", "bb"); + x2("(?<=a|b)b", "bb", 1, 2); + x2("(?<=a|bc)b", "bcb", 2, 3); + x2("(?<=a|bc)b", "ab", 1, 2); + x2("(?<=a|bc||defghij|klmnopq|r)z", "rz", 1, 2); + x3("(?<=(abc))d", "abcd", 0, 3, 1); + x2("(?<=(?i:abc))d", "ABCd", 3, 4); + x2("(a)\\g<1>", "aa", 0, 2); + x2("(?a)", "a", 0, 1); + x2("(?ab)\\g", "abab", 0, 4); + x2("(?.zv.)\\k", "azvbazvb", 0, 8); + x2("(?<=\\g)|-\\zEND (?XyZ)", "XyZ", 3, 3); + x2("(?|a\\g)+", "", 0, 0); + x2("(?|\\(\\g\\))+$", "()(())", 0, 6); + x3("\\g(?.){0}", "X", 0, 1, 1); + x2("\\g(abc|df(?.YZ){2,8}){0}", "XYZ", 0, 3); + x2("\\A(?(a\\g)|)\\z", "aaaa", 0, 4); + x2("(?|\\g\\g)\\z|\\zEND (?a|(b)\\g)", "bbbbabba", 0, 8); + x2("(?\\w+\\sx)a+\\k", " fg xaaaaaaaafg x", 2, 18); + x3("(z)()()(?<_9>a)\\g<_9>", "zaa", 2, 3, 1); + x2("(.)(((?<_>a)))\\k<_>", "zaa", 0, 3); + x2("((?\\d)|(?\\w))(\\k|\\k)", "ff", 0, 2); + x2("(?:(?)|(?efg))\\k", "", 0, 0); + x2("(?:(?abc)|(?efg))\\k", "abcefgefg", 3, 9); + n("(?:(?abc)|(?efg))\\k", "abcefg"); + x2("(?:(?.)|(?..)|(?...)|(?....)|(?.....)|(?......)|(?.......)|(?........)|(?.........)|(?..........)|(?...........)|(?............)|(?.............)|(?..............))\\k$", "a-pyumpyum", 2, 10); + x3("(?:(?.)|(?..)|(?...)|(?....)|(?.....)|(?......)|(?.......)|(?........)|(?.........)|(?..........)|(?...........)|(?............)|(?.............)|(?..............))\\k$", "xxxxabcdefghijklmnabcdefghijklmn", 4, 18, 14); + x3("(?)(?)(?)(?)(?)(?)(?)(?)(?)(?)(?)(?)(?)(?)(?)(?aaa)(?)$", "aaa", 0, 3, 16); + x2("(?a|\\(\\g\\))", "a", 0, 1); + x2("(?a|\\(\\g\\))", "((((((a))))))", 0, 13); + x3("(?a|\\(\\g\\))", "((((((((a))))))))", 0, 17, 1); + x2("\\g|\\zEND(?.*abc$)", "abcxxxabc", 0, 9); + x2("\\g<1>|\\zEND(.a.)", "bac", 0, 3); + x3("\\g<_A>\\g<_A>|\\zEND(.a.)(?<_A>.b.)", "xbxyby", 3, 6, 1); + x2("\\A(?:\\g|\\g|\\zEND (?a|c\\gc)(?b|d\\gd))$", "cdcbcdc", 0, 7); + x2("\\A(?|a\\g)\\z|\\zEND (?\\g)", "aaaa", 0, 4); + x2("(?(a|b\\gc){3,5})", "baaaaca", 1, 5); + x2("(?(a|b\\gc){3,5})", "baaaacaaaaa", 0, 10); + x2("(?\\(([^\\(\\)]++|\\g)*+\\))", "((a))", 0, 5); + x2("()*\\1", "", 0, 0); + x2("(?:()|())*\\1\\2", "", 0, 0); + x2("(?:a*|b*)*c", "abadc", 4, 5); + x3("(?:\\1a|())*", "a", 0, 0, 1); + x2("x((.)*)*x", "0x1x2x3", 1, 6); + x2("x((.)*)*x(?i:\\1)\\Z", "0x1x2x1X2", 1, 9); + x2("(?:()|()|()|()|()|())*\\2\\5", "", 0, 0); + x2("(?:()|()|()|(x)|()|())*\\2b\\5", "b", 0, 1); + x2("[0-9-a]", "-", 0, 1); // PR#44 + n("[0-9-a]", ":"); // PR#44 + x3("(\\(((?:[^(]|\\g<1>)*)\\))", "(abc)(abc)", 1, 4, 2); // PR#43 + x2("\\o{101}", "A", 0, 1); + x2("\\A(a|b\\g<1>c)\\k<1+3>\\z", "bbacca", 0, 6); + n("\\A(a|b\\g<1>c)\\k<1+3>\\z", "bbaccb"); + x2("(?i)\\A(a|b\\g<1>c)\\k<1+2>\\z", "bBACcbac", 0, 8); + x2("(?i)(?aa)|(?bb)\\k", "BBbb", 0, 4); + x2("(?:\\k'+1'B|(A)C)*", "ACAB", 0, 4); // relative backref by postitive number + x2("\\g<+2>(abc)(ABC){0}", "ABCabc", 0, 6); // relative call by positive number + x2("A\\g'0'|B()", "AAAAB", 0, 5); + x3("(A\\g'0')|B", "AAAAB", 0, 5, 1); + x2("(a*)(?(1))aa", "aaaaa", 0, 5); + x2("(a*)(?(-1))aa", "aaaaa", 0, 5); + x2("(?aaa)(?('name'))aa", "aaaaa", 0, 5); + x2("(a)(?(1)aa|bb)a", "aaaaa", 0, 4); + x2("(?:aa|())(?(<1>)aa|bb)a", "aabba", 0, 5); + x2("(?:aa|())(?('1')aa|bb|cc)a", "aacca", 0, 5); + x3("(a*)(?(1)aa|a)b", "aaab", 0, 1, 1); + n("(a)(?(1)a|b)c", "abc"); + x2("(a)(?(1)|)c", "ac", 0, 2); + n("(?()aaa|bbb)", "bbb"); + x2("(a)(?(1+0)b|c)d", "abd", 0, 3); + x2("(?:(?'name'a)|(?'name'b))(?('name')c|d)e", "ace", 0, 3); + x2("(?:(?'name'a)|(?'name'b))(?('name')c|d)e", "bce", 0, 3); + x2("\\R", "\r\n", 0, 2); + x2("\\R", "\r", 0, 1); + x2("\\R", "\n", 0, 1); + x2("\\R", "\x0b", 0, 1); + n("\\R\\n", "\r\n"); + x2("\\R", "\xc2\x85", 0, 2); + x2("\\N", "a", 0, 1); + n("\\N", "\n"); + n("(?m:\\N)", "\n"); + n("(?-m:\\N)", "\n"); + x2("\\O", "a", 0, 1); + x2("\\O", "\n", 0, 1); + x2("(?m:\\O)", "\n", 0, 1); + x2("(?-m:\\O)", "\n", 0, 1); + x2("\\K", "a", 0, 0); + x2("a\\K", "a", 1, 1); + x2("a\\Kb", "ab", 1, 2); + x2("(a\\Kb|ac\\Kd)", "acd", 2, 3); + x2("(a\\Kb|\\Kac\\K)*", "acababacab", 9, 10); + x2("(?:()|())*\\1", "abc", 0, 0); + x2("(?:()|())*\\2", "abc", 0, 0); + x2("(?:()|()|())*\\3\\1", "abc", 0, 0); + x2("(|(?:a(?:\\g'1')*))b|", "abc", 0, 2); + x2("^(\"|)(.*)\\1$", "XX", 0, 2); + x2("(abc|def|ghi|jkl|mno|pqr|stu){0,10}?\\z", "admno", 2, 5); + x2("(abc|(def|ghi|jkl|mno|pqr){0,7}?){5}\\z", "adpqrpqrpqr", 2, 11); // cover OP_REPEAT_INC_NG_SG + x2("(?!abc).*\\z", "abcde", 1, 5); // cover OP_PREC_READ_NOT_END + x2("(.{2,})?", "abcde", 0, 5); // up coverage + x2("((a|b|c|d|e|f|g|h|i|j|k|l|m|n)+)?", "abcde", 0, 5); // up coverage + x2("((a|b|c|d|e|f|g|h|i|j|k|l|m|n){3,})?", "abcde", 0, 5); // up coverage + x2("((?:a(?:b|c|d|e|f|g|h|i|j|k|l|m|n))+)?", "abacadae", 0, 8); // up coverage + x2("((?:a(?:b|c|d|e|f|g|h|i|j|k|l|m|n))+?)?z", "abacadaez", 0, 9); // up coverage + x2("\\A((a|b)\?\?)?z", "bz", 0, 2); // up coverage + x2("((?abc){0}a\\gd)+", "aabcd", 0, 5); // up coverage + x2("((?(abc)true|false))+", "false", 0, 5); // up coverage + x2("((?i:abc)d)+", "abcdABCd", 0, 8); // up coverage + x2("((?ab)(?()a|b)", "aba", 0, 3); // up coverage + x2("(?<=a.b)c", "azbc", 3, 4); // up coverage + n("(?<=(?:abcde){30})z", "abc"); // up coverage + x2("(?<=(?(a)a|bb))z", "aaz", 2, 3); // up coverage + x2("[a]*\\W", "aa@", 0, 3); // up coverage + x2("[a]*[b]", "aab", 0, 3); // up coverage + n("a*\\W", "aaa"); // up coverage + n("(?W)a*\\W", "aaa"); // up coverage + x2("(?<=ab(?<=ab))", "ab", 2, 2); // up coverage + x2("(?a)(?b)(\\k)+", "abbaab", 0, 6); // up coverage + x2("()(\\1)(\\2)", "abc", 0, 0); // up coverage + x2("((?(a)b|c))(\\1)", "abab", 0, 4); // up coverage + x2("(?$|b\\g)", "bbb", 0, 3); // up coverage + x2("(?(?(a)a|b)|c\\g)", "cccb", 0, 4); // up coverage + x2("(a)(?(1)a*|b*)+", "aaaa", 0, 4); // up coverage + x2("[[^abc]&&cde]*", "de", 0, 2); // up coverage + n("(a){10}{10}", "aa"); // up coverage + x2("(?:a?)+", "aa", 0, 2); // up coverage + x2("(?:a?)*?", "a", 0, 0); // up coverage + x2("(?:a*)*?", "a", 0, 0); // up coverage + x2("(?:a+?)*", "a", 0, 1); // up coverage + x2("\\h", "5", 0, 1); // up coverage + x2("\\H", "z", 0, 1); // up coverage + x2("[\\h]", "5", 0, 1); // up coverage + x2("[\\H]", "z", 0, 1); // up coverage + x2("[\\o{101}]", "A", 0, 1); // up coverage + x2("[\\u0041]", "A", 0, 1); // up coverage + + x2("(?~)", "", 0, 0); + x2("(?~)", "A", 0, 0); + x2("aaaaa(?~)", "aaaaaaaaaa", 0, 5); + x2("(?~(?:|aaa))", "aaa", 0, 0); + x2("(?~aaa|)", "aaa", 0, 0); + x2("a(?~(?~)).", "abcdefghijklmnopqrstuvwxyz", 0, 26); // !!! + x2("/\\*(?~\\*/)\\*/", "/* */ */", 0, 5); + x2("(?~\\w+)zzzzz", "zzzzz", 0, 5); + x2("(?~\\w*)zzzzz", "zzzzz", 0, 5); + x2("(?~A.C|B)", "ABC", 0, 0); + x2("(?~XYZ|ABC)a", "ABCa", 1, 4); + x2("(?~XYZ|ABC)a", "aABCa", 0, 1); + x2("<[^>]*>(?~[<>])]*>", "vvv ", 0, 10); + x2("(?~ab)", "ccc\ndab", 0, 5); + x2("(?m:(?~ab))", "ccc\ndab", 0, 5); + x2("(?-m:(?~ab))", "ccc\ndab", 0, 5); + x2("(?~abc)xyz", "xyz012345678901234567890123456789abc", 0, 3); + + // absent with expr + x2("(?~|78|\\d*)", "123456789", 0, 6); + x2("(?~|def|(?:abc|de|f){0,100})", "abcdedeabcfdefabc", 0, 11); + x2("(?~|ab|.*)", "ccc\nddd", 0, 3); + x2("(?~|ab|\\O*)", "ccc\ndab", 0, 5); + x2("(?~|ab|\\O{2,10})", "ccc\ndab", 0, 5); + x2("(?~|ab|\\O{1,10})", "ab", 1, 2); + n("(?~|ab|\\O{2,10})", "ab"); + x2("(?~|abc|\\O{1,10})", "abc", 1, 3); + x2("(?~|ab|\\O{5,10})|abc", "abc", 0, 3); + x2("(?~|ab|\\O{1,10})", "cccccccccccab", 0, 10); + x2("(?~|aaa|)", "aaa", 0, 0); + x2("(?~||a*)", "aaaaaa", 0, 0); + x2("(?~||a*?)", "aaaaaa", 0, 0); + x2("(a)(?~|b|\\1)", "aaaaaa", 0, 2); + x2("(a)(?~|bb|(?:a\\1)*)", "aaaaaa", 0, 5); + x2("(b|c)(?~|abac|(?:a\\1)*)", "abababacabab", 1, 4); + n("(?~|c|a*+)a", "aaaaa"); + x2("(?~|aaaaa|a*+)", "aaaaa", 0, 0); + x2("(?~|aaaaaa|a*+)b", "aaaaaab", 1, 7); + x2("(?~|abcd|(?>))", "zzzabcd", 0, 0); + x2("(?~|abc|a*?)", "aaaabc", 0, 0); + + // absent range cutter + x2("(?~|abc)a*", "aaaaaabc", 0, 5); + x2("(?~|abc)a*z|aaaaaabc", "aaaaaabc", 0, 8); + x2("(?~|aaaaaa)a*", "aaaaaa", 0, 0); + x2("(?~|abc)aaaa|aaaabc", "aaaabc", 0, 6); + x2("(?>(?~|abc))aaaa|aaaabc", "aaaabc", 0, 6); + x2("(?~|)a", "a", 0, 1); + n("(?~|a)a", "a"); + x2("(?~|a)(?~|)a", "a", 0, 1); + x2("(?~|a).*(?~|)a", "bbbbbbbbbbbbbbbbbbbba", 0, 21); + x2("(?~|abc).*(xyz|pqr)(?~|)abc", "aaaaxyzaaapqrabc", 0, 16); + x2("(?~|abc).*(xyz|pqr)(?~|)abc", "aaaaxyzaaaabcpqrabc", 11, 19); + n("\\A(?~|abc).*(xyz|pqrabc)(?~|)abc", "aaaaxyzaaaabcpqrabcabc"); + + x2("", "あ", 0, 0); + x2("あ", "あ", 0, 3); + n("い", "あ"); + x2("うう", "うう", 0, 6); + x2("あいう", "あいう", 0, 9); + x2("こここここここここここここここここここここここここここここここここここ", "こここここここここここここここここここここここここここここここここここ", 0, 105); + x2("あ", "いあ", 3, 6); + x2("いう", "あいう", 3, 9); + x2("\\xca\\xb8", "\xca\xb8", 0, 2); + x2(".", "あ", 0, 3); + x2("..", "かき", 0, 6); + x2("\\w", "お", 0, 3); + n("\\W", "あ"); + x2("[\\W]", "う$", 3, 4); + x2("\\S", "そ", 0, 3); + x2("\\S", "漢", 0, 3); + x2("\\b", "気 ", 0, 0); + x2("\\b", " ほ", 1, 1); + x2("\\B", "せそ ", 3, 3); + x2("\\B", "う ", 4, 4); + x2("\\B", " い", 0, 0); + x2("[たち]", "ち", 0, 3); + n("[なに]", "ぬ"); + x2("[う-お]", "え", 0, 3); + n("[^け]", "け"); + x2("[\\w]", "ね", 0, 3); + n("[\\d]", "ふ"); + x2("[\\D]", "は", 0, 3); + n("[\\s]", "く"); + x2("[\\S]", "へ", 0, 3); + x2("[\\w\\d]", "よ", 0, 3); + x2("[\\w\\d]", " よ", 3, 6); + n("\\w鬼車", " 鬼車"); + x2("鬼\\W車", "鬼 車", 0, 7); + x2("あ.い.う", "ああいいう", 0, 15); + x2(".\\wう\\W..ぞ", "えうう うぞぞ", 0, 19); + x2("\\s\\wこここ", " ここここ", 0, 13); + x2("ああ.け", "ああけけ", 0, 12); + n(".い", "いえ"); + x2(".お", "おお", 0, 6); + x2("^あ", "あ", 0, 3); + x2("^む$", "む", 0, 3); + x2("^\\w$", "に", 0, 3); + x2("^\\wかきくけこ$", "zかきくけこ", 0, 16); + x2("^\\w...うえお$", "zあいううえお", 0, 19); + x2("\\w\\w\\s\\Wおおお\\d", "aお おおお4", 0, 16); + x2("\\Aたちつ", "たちつ", 0, 9); + x2("むめも\\Z", "むめも", 0, 9); + x2("かきく\\z", "かきく", 0, 9); + x2("かきく\\Z", "かきく\n", 0, 9); + x2("\\Gぽぴ", "ぽぴ", 0, 6); + n("\\Gえ", "うえお"); + n("とて\\G", "とて"); + n("まみ\\A", "まみ"); + n("ま\\Aみ", "まみ"); + x2("(?=せ)せ", "せ", 0, 3); + n("(?=う).", "い"); + x2("(?!う)か", "か", 0, 3); + n("(?!と)あ", "と"); + x2("(?i:あ)", "あ", 0, 3); + x2("(?i:ぶべ)", "ぶべ", 0, 6); + n("(?i:い)", "う"); + x2("(?m:よ.)", "よ\n", 0, 4); + x2("(?m:.め)", "ま\nめ", 3, 7); + x2("あ?", "", 0, 0); + x2("変?", "化", 0, 0); + x2("変?", "変", 0, 3); + x2("量*", "", 0, 0); + x2("量*", "量", 0, 3); + x2("子*", "子子子", 0, 9); + x2("馬*", "鹿馬馬馬馬", 0, 0); + n("山+", ""); + x2("河+", "河", 0, 3); + x2("時+", "時時時時", 0, 12); + x2("え+", "ええううう", 0, 6); + x2("う+", "おうううう", 3, 15); + x2(".?", "た", 0, 3); + x2(".*", "ぱぴぷぺ", 0, 12); + x2(".+", "ろ", 0, 3); + x2(".+", "いうえか\n", 0, 12); + x2("あ|い", "あ", 0, 3); + x2("あ|い", "い", 0, 3); + x2("あい|いう", "あい", 0, 6); + x2("あい|いう", "いう", 0, 6); + x2("を(?:かき|きく)", "をかき", 0, 9); + x2("を(?:かき|きく)け", "をきくけ", 0, 12); + x2("あい|(?:あう|あを)", "あを", 0, 6); + x2("あ|い|う", "えう", 3, 6); + x2("あ|い|うえ|おかき|く|けこさ|しすせ|そ|たち|つてとなに|ぬね", "しすせ", 0, 9); + n("あ|い|うえ|おかき|く|けこさ|しすせ|そ|たち|つてとなに|ぬね", "すせ"); + x2("あ|^わ", "ぶあ", 3, 6); + x2("あ|^を", "をあ", 0, 3); + x2("鬼|\\G車", "け車鬼", 6, 9); + x2("鬼|\\G車", "車鬼", 0, 3); + x2("鬼|\\A車", "b車鬼", 4, 7); + x2("鬼|\\A車", "車", 0, 3); + x2("鬼|車\\Z", "車鬼", 3, 6); + x2("鬼|車\\Z", "車", 0, 3); + x2("鬼|車\\Z", "車\n", 0, 3); + x2("鬼|車\\z", "車鬼", 3, 6); + x2("鬼|車\\z", "車", 0, 3); + x2("\\w|\\s", "お", 0, 3); + x2("\\w|%", "%お", 0, 1); + x2("\\w|[&$]", "う&", 0, 3); + x2("[い-け]", "う", 0, 3); + x2("[い-け]|[^か-こ]", "あ", 0, 3); + x2("[い-け]|[^か-こ]", "か", 0, 3); + x2("[^あ]", "\n", 0, 1); + x2("(?:あ|[う-き])|いを", "うを", 0, 3); + x2("(?:あ|[う-き])|いを", "いを", 0, 6); + x2("あいう|(?=けけ)..ほ", "けけほ", 0, 9); + x2("あいう|(?!けけ)..ほ", "あいほ", 0, 9); + x2("(?=をあ)..あ|(?=をを)..あ", "ををあ", 0, 9); + x2("(?<=あ|いう)い", "いうい", 6, 9); + n("(?>あ|あいえ)う", "あいえう"); + x2("(?>あいえ|あ)う", "あいえう", 0, 12); + x2("あ?|い", "あ", 0, 3); + x2("あ?|い", "い", 0, 0); + x2("あ?|い", "", 0, 0); + x2("あ*|い", "ああ", 0, 6); + x2("あ*|い*", "いあ", 0, 0); + x2("あ*|い*", "あい", 0, 3); + x2("[aあ]*|い*", "aあいいい", 0, 4); + x2("あ+|い*", "", 0, 0); + x2("あ+|い*", "いいい", 0, 9); + x2("あ+|い*", "あいいい", 0, 3); + x2("あ+|い*", "aあいいい", 0, 0); + n("あ+|い+", ""); + x2("(あ|い)?", "い", 0, 3); + x2("(あ|い)*", "いあ", 0, 6); + x2("(あ|い)+", "いあい", 0, 9); + x2("(あい|うあ)+", "うああいうえ", 0, 12); + x2("(あい|うえ)+", "うああいうえ", 6, 18); + x2("(あい|うあ)+", "ああいうあ", 3, 15); + x2("(あい|うあ)+", "あいをうあ", 0, 6); + x2("(あい|うあ)+", "$$zzzzあいをうあ", 6, 12); + x2("(あ|いあい)+", "あいあいあ", 0, 15); + x2("(あ|いあい)+", "いあ", 3, 6); + x2("(あ|いあい)+", "いあああいあ", 3, 12); + x2("(?:あ|い)(?:あ|い)", "あい", 0, 6); + x2("(?:あ*|い*)(?:あ*|い*)", "あああいいい", 0, 9); + x2("(?:あ*|い*)(?:あ+|い+)", "あああいいい", 0, 18); + x2("(?:あ+|い+){2}", "あああいいい", 0, 18); + x2("(?:あ+|い+){1,2}", "あああいいい", 0, 18); + x2("(?:あ+|\\Aい*)うう", "うう", 0, 6); + n("(?:あ+|\\Aい*)うう", "あいうう"); + x2("(?:^あ+|い+)*う", "ああいいいあいう", 18, 24); + x2("(?:^あ+|い+)*う", "ああいいいいう", 0, 21); + x2("う{0,}", "うううう", 0, 12); + x2("あ|(?i)c", "C", 0, 1); + x2("(?i)c|あ", "C", 0, 1); + x2("(?i:あ)|a", "a", 0, 1); + n("(?i:あ)|a", "A"); + x2("[あいう]?", "あいう", 0, 3); + x2("[あいう]*", "あいう", 0, 9); + x2("[^あいう]*", "あいう", 0, 0); + n("[^あいう]+", "あいう"); + x2("あ?\?", "あああ", 0, 0); + x2("いあ?\?い", "いあい", 0, 9); + x2("あ*?", "あああ", 0, 0); + x2("いあ*?", "いああ", 0, 3); + x2("いあ*?い", "いああい", 0, 12); + x2("あ+?", "あああ", 0, 3); + x2("いあ+?", "いああ", 0, 6); + x2("いあ+?い", "いああい", 0, 12); + x2("(?:天?)?\?", "天", 0, 0); + x2("(?:天?\?)?", "天", 0, 0); + x2("(?:夢?)+?", "夢夢夢", 0, 3); + x2("(?:風+)?\?", "風風風", 0, 0); + x2("(?:雪+)?\?霜", "雪雪雪霜", 0, 12); + x2("(?:あい)?{2}", "", 0, 0); + x2("(?:鬼車)?{2}", "鬼車鬼車鬼", 0, 12); + x2("(?:鬼車)*{0}", "鬼車鬼車鬼", 0, 0); + x2("(?:鬼車){3,}", "鬼車鬼車鬼車鬼車", 0, 24); + n("(?:鬼車){3,}", "鬼車鬼車"); + x2("(?:鬼車){2,4}", "鬼車鬼車鬼車", 0, 18); + x2("(?:鬼車){2,4}", "鬼車鬼車鬼車鬼車鬼車", 0, 24); + x2("(?:鬼車){2,4}?", "鬼車鬼車鬼車鬼車鬼車", 0, 12); + x2("(?:鬼車){,}", "鬼車{,}", 0, 9); + x2("(?:かきく)+?{2}", "かきくかきくかきく", 0, 18); + x3("(火)", "火", 0, 3, 1); + x3("(火水)", "火水", 0, 6, 1); + x2("((時間))", "時間", 0, 6); + x3("((風水))", "風水", 0, 6, 1); + x3("((昨日))", "昨日", 0, 6, 2); + x3("((((((((((((((((((((量子))))))))))))))))))))", "量子", 0, 6, 20); + x3("(あい)(うえ)", "あいうえ", 0, 6, 1); + x3("(あい)(うえ)", "あいうえ", 6, 12, 2); + x3("()(あ)いう(えおか)きくけこ", "あいうえおかきくけこ", 9, 18, 3); + x3("(()(あ)いう(えおか)きくけこ)", "あいうえおかきくけこ", 9, 18, 4); + x3(".*(フォ)ン・マ(ン()シュタ)イン", "フォン・マンシュタイン", 15, 27, 2); + x2("(^あ)", "あ", 0, 3); + x3("(あ)|(あ)", "いあ", 3, 6, 1); + x3("(^あ)|(あ)", "いあ", 3, 6, 2); + x3("(あ?)", "あああ", 0, 3, 1); + x3("(ま*)", "ままま", 0, 9, 1); + x3("(と*)", "", 0, 0, 1); + x3("(る+)", "るるるるるるる", 0, 21, 1); + x3("(ふ+|へ*)", "ふふふへへ", 0, 9, 1); + x3("(あ+|い?)", "いいいああ", 0, 3, 1); + x3("(あいう)?", "あいう", 0, 9, 1); + x3("(あいう)*", "あいう", 0, 9, 1); + x3("(あいう)+", "あいう", 0, 9, 1); + x3("(さしす|あいう)+", "あいう", 0, 9, 1); + x3("([なにぬ][かきく]|かきく)+", "かきく", 0, 9, 1); + x3("((?i:あいう))", "あいう", 0, 9, 1); + x3("((?m:あ.う))", "あ\nう", 0, 7, 1); + x3("((?=あん)あ)", "あんい", 0, 3, 1); + x3("あいう|(.あいえ)", "んあいえ", 0, 12, 1); + x3("あ*(.)", "ああああん", 12, 15, 1); + x3("あ*?(.)", "ああああん", 0, 3, 1); + x3("あ*?(ん)", "ああああん", 12, 15, 1); + x3("[いうえ]あ*(.)", "えああああん", 15, 18, 1); + x3("(\\Aいい)うう", "いいうう", 0, 6, 1); + n("(\\Aいい)うう", "んいいうう"); + x3("(^いい)うう", "いいうう", 0, 6, 1); + n("(^いい)うう", "んいいうう"); + x3("ろろ(るる$)", "ろろるる", 6, 12, 1); + n("ろろ(るる$)", "ろろるるる"); + x2("(無)\\1", "無無", 0, 6); + n("(無)\\1", "無武"); + x2("(空?)\\1", "空空", 0, 6); + x2("(空?\?)\\1", "空空", 0, 0); + x2("(空*)\\1", "空空空空空", 0, 12); + x3("(空*)\\1", "空空空空空", 0, 6, 1); + x2("あ(い*)\\1", "あいいいい", 0, 15); + x2("あ(い*)\\1", "あい", 0, 3); + x2("(あ*)(い*)\\1\\2", "あああいいあああいい", 0, 30); + x2("(あ*)(い*)\\2", "あああいいいい", 0, 21); + x3("(あ*)(い*)\\2", "あああいいいい", 9, 15, 2); + x2("(((((((ぽ*)ぺ))))))ぴ\\7", "ぽぽぽぺぴぽぽぽ", 0, 24); + x3("(((((((ぽ*)ぺ))))))ぴ\\7", "ぽぽぽぺぴぽぽぽ", 0, 9, 7); + x2("(は)(ひ)(ふ)\\2\\1\\3", "はひふひはふ", 0, 18); + x2("([き-け])\\1", "くく", 0, 6); + x2("(\\w\\d\\s)\\1", "あ5 あ5 ", 0, 10); + n("(\\w\\d\\s)\\1", "あ5 あ5"); + x2("(誰?|[あ-う]{3})\\1", "誰?誰?", 0, 12); + x2("...(誰?|[あ-う]{3})\\1", "あaあ誰?誰?", 0, 19); + x2("(誰?|[あ-う]{3})\\1", "ういうういう", 0, 18); + x2("(^こ)\\1", "ここ", 0, 6); + n("(^む)\\1", "めむむ"); + n("(あ$)\\1", "ああ"); + n("(あい\\Z)\\1", "あい"); + x2("(あ*\\Z)\\1", "あ", 3, 3); + x2(".(あ*\\Z)\\1", "いあ", 3, 6); + x3("(.(やいゆ)\\2)", "zやいゆやいゆ", 0, 19, 1); + x3("(.(..\\d.)\\2)", "あ12341234", 0, 11, 1); + x2("((?i:あvず))\\1", "あvずあvず", 0, 14); + x2("(?<愚か>変|\\(\\g<愚か>\\))", "((((((変))))))", 0, 15); + x2("\\A(?:\\g<阿_1>|\\g<云_2>|\\z終了 (?<阿_1>観|自\\g<云_2>自)(?<云_2>在|菩薩\\g<阿_1>菩薩))$", "菩薩自菩薩自在自菩薩自菩薩", 0, 39); + x2("[[ひふ]]", "ふ", 0, 3); + x2("[[いおう]か]", "か", 0, 3); + n("[[^あ]]", "あ"); + n("[^[あ]]", "あ"); + x2("[^[^あ]]", "あ", 0, 3); + x2("[[かきく]&&きく]", "く", 0, 3); + n("[[かきく]&&きく]", "か"); + n("[[かきく]&&きく]", "け"); + x2("[あ-ん&&い-を&&う-ゑ]", "ゑ", 0, 3); + n("[^あ-ん&&い-を&&う-ゑ]", "ゑ"); + x2("[[^あ&&あ]&&あ-ん]", "い", 0, 3); + n("[[^あ&&あ]&&あ-ん]", "あ"); + x2("[[^あ-ん&&いうえお]&&[^う-か]]", "き", 0, 3); + n("[[^あ-ん&&いうえお]&&[^う-か]]", "い"); + x2("[^[^あいう]&&[^うえお]]", "う", 0, 3); + x2("[^[^あいう]&&[^うえお]]", "え", 0, 3); + n("[^[^あいう]&&[^うえお]]", "か"); + x2("[あ-&&-あ]", "-", 0, 1); + x2("[^[^a-zあいう]&&[^bcdefgうえお]q-w]", "え", 0, 3); + x2("[^[^a-zあいう]&&[^bcdefgうえお]g-w]", "f", 0, 1); + x2("[^[^a-zあいう]&&[^bcdefgうえお]g-w]", "g", 0, 1); + n("[^[^a-zあいう]&&[^bcdefgうえお]g-w]", "2"); + x2("aバージョンのダウンロード<\\/b>", "aバージョンのダウンロード", 0, 44); + x2(".バージョンのダウンロード<\\/b>", "aバージョンのダウンロード", 0, 44); + x2("\\n?\\z", "こんにちは", 15, 15); + x2("(?m).*", "青赤黄", 0, 9); + x2("(?m).*a", "青赤黄a", 0, 10); + + x2("\\p{Hiragana}", "ぴ", 0, 3); + n("\\P{Hiragana}", "ぴ"); + x2("\\p{Emoji}", "\xE2\xAD\x90", 0, 3); + x2("\\p{^Emoji}", "\xEF\xBC\x93", 0, 3); + x2("\\p{Extended_Pictographic}", "\xE2\x9A\xA1", 0, 3); + n("\\p{Extended_Pictographic}", "\xE3\x81\x82"); + + x2("\\p{Word}", "こ", 0, 3); + n("\\p{^Word}", "こ"); + x2("[\\p{Word}]", "こ", 0, 3); + n("[\\p{^Word}]", "こ"); + n("[^\\p{Word}]", "こ"); + x2("[^\\p{^Word}]", "こ", 0, 3); + x2("[^\\p{^Word}&&\\p{ASCII}]", "こ", 0, 3); + x2("[^\\p{^Word}&&\\p{ASCII}]", "a", 0, 1); + n("[^\\p{^Word}&&\\p{ASCII}]", "#"); + x2("[^[\\p{^Word}]&&[\\p{ASCII}]]", "こ", 0, 3); + x2("[^[\\p{ASCII}]&&[^\\p{Word}]]", "こ", 0, 3); + n("[[\\p{ASCII}]&&[^\\p{Word}]]", "こ"); + x2("[^[\\p{^Word}]&&[^\\p{ASCII}]]", "こ", 0, 3); + x2("[^\\x{104a}]", "こ", 0, 3); + x2("[^\\p{^Word}&&[^\\x{104a}]]", "こ", 0, 3); + x2("[^[\\p{^Word}]&&[^\\x{104a}]]", "こ", 0, 3); + n("[^\\p{Word}||[^\\x{104a}]]", "こ"); + + x2("\\p{^Cntrl}", "こ", 0, 3); + n("\\p{Cntrl}", "こ"); + x2("[\\p{^Cntrl}]", "こ", 0, 3); + n("[\\p{Cntrl}]", "こ"); + n("[^\\p{^Cntrl}]", "こ"); + x2("[^\\p{Cntrl}]", "こ", 0, 3); + x2("[^\\p{Cntrl}&&\\p{ASCII}]", "こ", 0, 3); + x2("[^\\p{Cntrl}&&\\p{ASCII}]", "a", 0, 1); + n("[^\\p{^Cntrl}&&\\p{ASCII}]", "#"); + x2("[^[\\p{^Cntrl}]&&[\\p{ASCII}]]", "こ", 0, 3); + x2("[^[\\p{ASCII}]&&[^\\p{Cntrl}]]", "こ", 0, 3); + n("[[\\p{ASCII}]&&[^\\p{Cntrl}]]", "こ"); + n("[^[\\p{^Cntrl}]&&[^\\p{ASCII}]]", "こ"); + n("[^\\p{^Cntrl}&&[^\\x{104a}]]", "こ"); + n("[^[\\p{^Cntrl}]&&[^\\x{104a}]]", "こ"); + n("[^\\p{Cntrl}||[^\\x{104a}]]", "こ"); + + x2("(?-W:\\p{Word})", "こ", 0, 3); + n("(?W:\\p{Word})", "こ"); + x2("(?W:\\p{Word})", "k", 0, 1); + x2("(?-W:[[:word:]])", "こ", 0, 3); + n("(?W:[[:word:]])", "こ"); + x2("(?-D:\\p{Digit})", "3", 0, 3); + n("(?D:\\p{Digit})", "3"); + x2("(?-S:\\p{Space})", "\xc2\x85", 0, 2); + n("(?S:\\p{Space})", "\xc2\x85"); + x2("(?-P:\\p{Word})", "こ", 0, 3); + n("(?P:\\p{Word})", "こ"); + x2("(?-W:\\w)", "こ", 0, 3); + n("(?W:\\w)", "こ"); + x2("(?-W:\\w)", "k", 0, 1); + x2("(?W:\\w)", "k", 0, 1); + n("(?-W:\\W)", "こ"); + x2("(?W:\\W)", "こ", 0, 3); + n("(?-W:\\W)", "k"); + n("(?W:\\W)", "k"); + + x2("(?-W:\\b)", "こ", 0, 0); + n("(?W:\\b)", "こ"); + x2("(?-W:\\b)", "h", 0, 0); + x2("(?W:\\b)", "h", 0, 0); + n("(?-W:\\B)", "こ"); + x2("(?W:\\B)", "こ", 0, 0); + n("(?-W:\\B)", "h"); + n("(?W:\\B)", "h"); + x2("(?-P:\\b)", "こ", 0, 0); + n("(?P:\\b)", "こ"); + x2("(?-P:\\b)", "h", 0, 0); + x2("(?P:\\b)", "h", 0, 0); + n("(?-P:\\B)", "こ"); + x2("(?P:\\B)", "こ", 0, 0); + n("(?-P:\\B)", "h"); + n("(?P:\\B)", "h"); + + x2("\\p{InBasicLatin}", "\x41", 0, 1); + //x2("\\p{Grapheme_Cluster_Break_Regional_Indicator}", "\xF0\x9F\x87\xA9", 0, 4); + //n("\\p{Grapheme_Cluster_Break_Regional_Indicator}", "\xF0\x9F\x87\xA5"); + + // extended grapheme cluster + + // CR + LF + n(".\\y\\O", "\x0d\x0a"); + x2(".\\Y\\O", "\x0d\x0a", 0, 2); + + // LATIN SMALL LETTER G, COMBINING DIAERESIS + n("^.\\y.$", "\x67\xCC\x88"); + x2(".\\Y.", "\x67\xCC\x88", 0, 3); + x2("\\y.\\Y.\\y", "\x67\xCC\x88", 0, 3); + // HANGUL SYLLABLE GAG + x2("\\y.\\y", "\xEA\xB0\x81", 0, 3); + // HANGUL CHOSEONG KIYEOK, HANGUL JUNGSEONG A, HANGUL JONGSEONG KIYEOK + x2("^.\\Y.\\Y.$", "\xE1\x84\x80\xE1\x85\xA1\xE1\x86\xA8", 0, 9); + n("^.\\y.\\Y.$", "\xE1\x84\x80\xE1\x85\xA1\xE1\x86\xA8"); + // TAMIL LETTER NA, TAMIL VOWEL SIGN I, + x2(".\\Y.", "\xE0\xAE\xA8\xE0\xAE\xBF", 0, 6); + n(".\\y.", "\xE0\xAE\xA8\xE0\xAE\xBF"); + // THAI CHARACTER KO KAI, THAI CHARACTER SARA AM + x2(".\\Y.", "\xE0\xB8\x81\xE0\xB8\xB3", 0, 6); + n(".\\y.", "\xE0\xB8\x81\xE0\xB8\xB3"); + // DEVANAGARI LETTER SSA, DEVANAGARI VOWEL SIGN I + x2(".\\Y.", "\xE0\xA4\xB7\xE0\xA4\xBF", 0, 6); + n(".\\y.", "\xE0\xA4\xB7\xE0\xA4\xBF"); + + // {Extended_Pictographic} Extend* ZWJ x {Extended_Pictographic} + x2("..\\Y.", "\xE3\x80\xB0\xE2\x80\x8D\xE2\xAD\x95", 0, 9); + x2("...\\Y.", "\xE3\x80\xB0\xCC\x82\xE2\x80\x8D\xE2\xAD\x95", 0, 11); + n("...\\Y.", "\xE3\x80\xB0\xCD\xB0\xE2\x80\x8D\xE2\xAD\x95"); + + // CR + LF + n("^\\X\\X$", "\x0d\x0a"); + x2("^\\X$", "\x0d\x0a", 0, 2); + // LATIN SMALL LETTER G, COMBINING DIAERESIS + n("^\\X\\X.$", "\x67\xCC\x88"); + x2("^\\X$", "\x67\xCC\x88", 0, 3); + // HANGUL CHOSEONG KIYEOK, HANGUL JUNGSEONG A, HANGUL JONGSEONG KIYEOK + x2("^\\X$", "\xE1\x84\x80\xE1\x85\xA1\xE1\x86\xA8", 0, 9); + n("^\\X\\X\\X$", "\xE1\x84\x80\xE1\x85\xA1\xE1\x86\xA8"); + // TAMIL LETTER NA, TAMIL VOWEL SIGN I, + x2("^\\X$", "\xE0\xAE\xA8\xE0\xAE\xBF", 0, 6); + n("\\X\\X", "\xE0\xAE\xA8\xE0\xAE\xBF"); + // THAI CHARACTER KO KAI, THAI CHARACTER SARA AM + x2("^\\X$", "\xE0\xB8\x81\xE0\xB8\xB3", 0, 6); + n("\\X\\X", "\xE0\xB8\x81\xE0\xB8\xB3"); + // DEVANAGARI LETTER SSA, DEVANAGARI VOWEL SIGN I + x2("^\\X$", "\xE0\xA4\xB7\xE0\xA4\xBF", 0, 6); + n("\\X\\X", "\xE0\xA4\xB7\xE0\xA4\xBF"); + + n("^\\X.$", "\xE0\xAE\xA8\xE0\xAE\xBF"); + + // a + COMBINING GRAVE ACCENT (U+0300) + x2("h\\Xllo", "ha\xCC\x80llo", 0, 7); + + // Text Segment: Extended Grapheme Cluster <-> Word Boundary + x2("(?y{g})\\yabc\\y", "abc", 0, 3); + x2("(?y{g})\\y\\X\\y", "abc", 0, 1); + x2("(?y{w})\\yabc\\y", "abc", 0, 3); // WB1, WB2 + x2("(?y{w})\\X", "\r\n", 0, 2); // WB3 + x2("(?y{w})\\X", "\x0cz", 0, 1); // WB3a + x2("(?y{w})\\X", "q\x0c", 0, 1); // WB3b + x2("(?y{w})\\X", "\xE2\x80\x8D\xE2\x9D\x87", 0, 6); // WB3c + x2("(?y{w})\\X", "\x20\x20", 0, 2); // WB3d + x2("(?y{w})\\X", "a\xE2\x80\x8D", 0, 4); // WB4 + x2("(?y{w})\\y\\X\\y", "abc", 0, 3); // WB5 + x2("(?y{w})\\y\\X\\y", "v\xCE\x87w", 0, 4); // WB6, WB7 + x2("(?y{w})\\y\\X\\y", "\xD7\x93\x27", 0, 3); // WB7a + x2("(?y{w})\\y\\X\\y", "\xD7\x93\x22\xD7\x93", 0, 5); // WB7b, WB7c + x2("(?y{w})\\X", "14 45", 0, 2); // WB8 + x2("(?y{w})\\X", "a14", 0, 3); // WB9 + x2("(?y{w})\\X", "832e", 0, 4); // WB10 + x2("(?y{w})\\X", "8\xEF\xBC\x8C\xDB\xB0", 0, 6); // WB11, WB12 + x2("(?y{w})\\y\\X\\y", "ケン", 0, 6); // WB13 + x2("(?y{w})\\y\\X\\y", "ケン\xE2\x80\xAFタ", 0, 12); // WB13a, WB13b + x2("(?y{w})\\y\\X\\y", "\x21\x23", 0, 1); // WB999 + x2("(?y{w})\\y\\X\\y", "山ア", 0, 3); + x2("(?y{w})\\X", "3.14", 0, 4); + x2("(?y{w})\\X", "3 14", 0, 1); + + x2("\\x40", "@", 0, 1); + x2("\\x1", "\x01", 0, 1); + x2("\\x{1}", "\x01", 0, 1); + x2("\\x{4E38}", "\xE4\xB8\xB8", 0, 3); + x2("\\u4E38", "\xE4\xB8\xB8", 0, 3); + x2("\\u0040", "@", 0, 1); + + x2("c.*\\b", "abc", 2, 3); + x2("\\b.*abc.*\\b", "abc", 0, 3); + x2("((?()0+)+++(((0\\g<0>)0)|())++++((?(1)(0\\g<0>))++++++0*())++++((?(1)(0\\g<1>)+)++++++++++*())++++((?(1)((0)\\g<0>)+)++())+0++*+++(((0\\g<0>))*())++++((?(1)(0\\g<0>)+)++++++++++*|)++++*+++((?(1)((0)\\g<0>)+)+++++++++())++*|)++++((?()0))|", "abcde", 0, 0); // #139 + + n("(*FAIL)", "abcdefg"); + n("abcd(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)(*FAIL)", "abcdefg"); + x2("(?:[ab]|(*MAX{2}).)*", "abcbaaccaaa", 0, 7); + x2("(?:(*COUNT[AB]{X})[ab]|(*COUNT[CD]{X})[cd])*(*CMP{AB,<,CD})", + "abababcdab", 5, 8); + x2("(?(?{....})123|456)", "123", 0, 3); + x2("(?(*FAIL)123|456)", "456", 0, 3); + + x2("\\g'0'++{,0}", "abcdefgh", 0, 0); + x2("\\g'0'++{,0}?", "abcdefgh", 0, 0); + x2("\\g'0'++{,0}b", "abcdefgh", 1, 2); + x2("\\g'0'++{,0}?def", "abcdefgh", 3, 6); + n("a{2,3}?", "a"); + n("a{3,2}a", "aaa"); + x2("a{3,2}b", "aaab", 0, 4); + x2("a{3,2}b", "aaaab", 1, 5); + x2("a{3,2}b", "aab", 0, 3); + x2("a{3,2}?", "", 0, 0); /* == (?:a{3,2})?*/ + x2("a{2,3}+a", "aaa", 0, 3); /* == (?:a{2,3})+*/ + + n(" \xfd", ""); /* https://bugs.php.net/bug.php?id=77370 */ + /* can't use \xfc00.. because compiler error: hex escape sequence out of range */ + n("()0\\xfc00000\\xfc00000\\xfc00000\xfc", ""); /* https://bugs.php.net/bug.php?id=77371 */ + x2("000||0\xfa", "0", 0, 0); /* https://bugs.php.net/bug.php?id=77381 */ + e("(?i)000000000000000000000\xf0", "", ONIGERR_INVALID_CODE_POINT_VALUE); /* https://bugs.php.net/bug.php?id=77382 */ + n("0000\\\xf5", "0"); /* https://bugs.php.net/bug.php?id=77385 */ + n("(?i)FFF00000000000000000\xfd", ""); /* https://bugs.php.net/bug.php?id=77394 */ + + + x2("\\p{Common}", "\xe3\x8b\xbf", 0, 3); /* U+32FF */ + x2("\\p{In_Enclosed_CJK_Letters_and_Months}", "\xe3\x8b\xbf", 0, 3); /* U+32FF */ + + e("\\u040", "@", ONIGERR_INVALID_CODE_POINT_VALUE); + e("(?\\g)", "zzzz", ONIGERR_NEVER_ENDING_RECURSION); + e("(?<=(?>abc))", "abc", ONIGERR_INVALID_LOOK_BEHIND_PATTERN); + e("(*FOO)", "abcdefg", ONIGERR_UNDEFINED_CALLOUT_NAME); + + fprintf(stdout, + "\nRESULT SUCC: %4d, FAIL: %d, ERROR: %d (by Oniguruma %s)\n", + nsucc, nfail, nerror, onig_version()); + + onig_region_free(region, 1); + onig_end(); + + return ((nfail == 0 && nerror == 0) ? 0 : -1); +} diff --git a/tools/vendor/fancy-regex/tests/oniguruma/test_utf8_ignore.c b/tools/vendor/fancy-regex/tests/oniguruma/test_utf8_ignore.c new file mode 100644 index 0000000000..889d0f44b9 --- /dev/null +++ b/tools/vendor/fancy-regex/tests/oniguruma/test_utf8_ignore.c @@ -0,0 +1,765 @@ +// Tests that currently fail when run against fancy-regex, see README.md. +// +// x2 tests check if a pattern matches against an input at the specified start/end positions. +// x3 tests have an additional argument which is the group number to check. + + + // No match found + x2("^a", "\na", 1, 2); + + // Compile failed: ParseError(1, InvalidEscape("\\O")) + x2("$\\O", "bb\n", 2, 3); + + // Compile failed: ParseError(0, InvalidEscape("\\Z")) + x2("\\Z", "", 0, 0); + + // Compile failed: ParseError(0, InvalidEscape("\\c")) + x2("\\ca", "\001", 0, 1); + + // Compile failed: ParseError(0, InvalidEscape("\\C")) + x2("\\C-b", "\002", 0, 1); + + // Compile failed: ParseError(0, InvalidEscape("\\c")) + x2("\\c\\\\", "\034", 0, 1); + + // Compile failed: ParseError(2, InvalidEscape("\\c")) + x2("q[\\c\\\\]", "q\034", 0, 2); + + // Compile failed: ParseError(1, InvalidBackref) + x2("\\17", "\017", 0, 1); + + // No match found + x2("(?x) G (o O(?-x)oO) g L", "GoOoOgLe", 0, 7); + + // Compile failed: ParseError(2, InvalidBackref) + x2("[\\044-\\047]", "\046", 0, 1); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Parse(Error { kind: ClassRangeInvalid, pattern: "[a-&&-a]", span: Span(Position(o: 1, l: 1, c: 2), Position(o: 4, l: 1, c: 5)) }) } })) + x2("[a-&&-a]", "-", 0, 1); + + // Compile failed: ParseError(2, InvalidEscape("\\Z")) + x2("\\A\\Z", "", 0, 0); + + // Compile failed: ParseError(3, InvalidEscape("\\Z")) + x2("xyz\\Z", "xyz", 0, 3); + + // Compile failed: ParseError(1, InvalidEscape("\\Z")) + x2("a\\Z", "a", 0, 1); + + // No match found + x2("(?i:ss)", "\xc3\x9f", 0, 2); + + // No match found + x2("(?i:ss)", "\xe1\xba\x9e", 0, 3); + + // No match found + x2("(?i:xssy)", "x\xc3\x9fy", 0, 4); + + // No match found + x2("(?i:xssy)", "x\xe1\xba\x9ey", 0, 5); + + // No match found + x2("(?i:\xc3\x9f)", "ss", 0, 2); + + // No match found + x2("(?i:\xc3\x9f)", "SS", 0, 2); + + // No match found + x2("(?i:[\xc3\x9f])", "ss", 0, 2); + + // No match found + x2("(?i:[\xc3\x9f])", "SS", 0, 2); + + // No match found + x2("(?m:.)", "\n", 0, 1); + + // No match found + x2("(?m:a.)", "a\n", 0, 2); + + // No match found + x2("(?m:.b)", "a\nb", 1, 3); + + // Compile failed: ParseError(3, InvalidEscape("\\Z")) + x2("a|b\\Z", "ba", 1, 2); + + // Compile failed: ParseError(3, InvalidEscape("\\Z")) + x2("a|b\\Z", "b", 0, 1); + + // Match found at start 1 and end 2 (expected 0 and 2) + x2("a(?i)b|c", "aC", 0, 2); + + // No match found + x2("(?:ab)?{2}", "", 0, 0); + + // No match found + x2("(?:ab)?{2}", "ababa", 0, 4); + + // No match found + x2("(?:ab)*{0}", "ababa", 0, 0); + + // Match found at start 0 and end 2 (expected 0 and 5) + x2("(?:ab){,}", "ab{,}", 0, 5); + + // No match found + x2("(?:abc)+?{2}", "abcabcabc", 0, 6); + + // No match found + x2("(abc)(?i:\\1)", "abcABC", 0, 6); + + // No match found + x3("((?m:a.c))", "a\nc", 0, 3, 1); + + // Compile failed: CompileError(InvalidBackref) + x2("(?:(?:\\1|z)(a))+$", "zaaa", 0, 4); + + // Compile failed: ParseError(3, InvalidEscape("\\Z")) + x2("(a*\\Z)\\1", "a", 1, 1); + + // Compile failed: ParseError(4, InvalidEscape("\\Z")) + x2(".(a*\\Z)\\1", "ba", 1, 2); + + // Compile failed: ParseError(3, InvalidEscape("\\g")) + x2("(a)\\g<1>", "aa", 0, 2); + + // Compile failed: ParseError(13, InvalidEscape("\\g")) + x2("(?ab)\\g", "abab", 0, 4); + + // Compile failed: ParseError(4, InvalidEscape("\\g")) + x2("(?<=\\g)|-\\zEND (?XyZ)", "XyZ", 3, 3); + + // Compile failed: ParseError(7, InvalidEscape("\\g")) + x2("(?|a\\g)+", "", 0, 0); + + // Compile failed: ParseError(8, InvalidEscape("\\g")) + x2("(?|\\(\\g\\))+$", "()(())", 0, 6); + + // Compile failed: ParseError(0, InvalidEscape("\\g")) + x3("\\g(?.){0}", "X", 0, 1, 1); + + // Compile failed: ParseError(0, InvalidEscape("\\g")) + x2("\\g(abc|df(?.YZ){2,8}){0}", "XYZ", 0, 3); + + // Compile failed: ParseError(9, InvalidEscape("\\g")) + x2("\\A(?(a\\g)|)\\z", "aaaa", 0, 4); + + // Compile failed: ParseError(6, InvalidEscape("\\g")) + x2("(?|\\g\\g)\\z|\\zEND (?a|(b)\\g)", "bbbbabba", 0, 8); + + // Compile failed: ParseError(15, InvalidEscape("\\g")) + x3("(z)()()(?<_9>a)\\g<_9>", "zaa", 2, 3, 1); + + // No match found + x2("(?:(?)|(?efg))\\k", "", 0, 0); + + // No match found + x2("(?:(?.)|(?..)|(?...)|(?....)|(?.....)|(?......)|(?.......)|(?........)|(?.........)|(?..........)|(?...........)|(?............)|(?.............)|(?..............))\\k$", "a-pyumpyum", 2, 10); + + // Compile failed: ParseError(11, InvalidEscape("\\g")) + x2("(?a|\\(\\g\\))", "a", 0, 1); + + // Compile failed: ParseError(11, InvalidEscape("\\g")) + x2("(?a|\\(\\g\\))", "((((((a))))))", 0, 13); + + // Compile failed: ParseError(11, InvalidEscape("\\g")) + x3("(?a|\\(\\g\\))", "((((((((a))))))))", 0, 17, 1); + + // Compile failed: ParseError(0, InvalidEscape("\\g")) + x2("\\g|\\zEND(?.*abc$)", "abcxxxabc", 0, 9); + + // Compile failed: ParseError(0, InvalidEscape("\\g")) + x2("\\g<1>|\\zEND(.a.)", "bac", 0, 3); + + // Compile failed: ParseError(0, InvalidEscape("\\g")) + x3("\\g<_A>\\g<_A>|\\zEND(.a.)(?<_A>.b.)", "xbxyby", 3, 6, 1); + + // Compile failed: ParseError(5, InvalidEscape("\\g")) + x2("\\A(?:\\g|\\g|\\zEND (?a|c\\gc)(?b|d\\gd))$", "cdcbcdc", 0, 7); + + // Compile failed: ParseError(9, InvalidEscape("\\g")) + x2("\\A(?|a\\g)\\z|\\zEND (?\\g)", "aaaa", 0, 4); + + // Compile failed: ParseError(9, InvalidEscape("\\g")) + x2("(?(a|b\\gc){3,5})", "baaaaca", 1, 5); + + // Compile failed: ParseError(9, InvalidEscape("\\g")) + x2("(?(a|b\\gc){3,5})", "baaaacaaaaa", 0, 10); + + // Compile failed: ParseError(21, InvalidEscape("\\g")) + x2("(?\\(([^\\(\\)]++|\\g)*+\\))", "((a))", 0, 5); + + // No match found + x2("()*\\1", "", 0, 0); + + // No match found + x2("(?:()|())*\\1\\2", "", 0, 0); + + // Compile failed: CompileError(InvalidBackref) + x3("(?:\\1a|())*", "a", 0, 0, 1); + + // Compile failed: ParseError(16, InvalidEscape("\\Z")) + x2("x((.)*)*x(?i:\\1)\\Z", "0x1x2x1X2", 1, 9); + + // No match found + x2("(?:()|()|()|()|()|())*\\2\\5", "", 0, 0); + + // No match found + x2("(?:()|()|()|(x)|()|())*\\2b\\5", "b", 0, 1); + + // Compile failed: ParseError(12, InvalidEscape("\\g")) + x3("(\\(((?:[^(]|\\g<1>)*)\\))", "(abc)(abc)", 1, 4, 2); + + // Compile failed: ParseError(0, InvalidEscape("\\o")) + x2("\\o{101}", "A", 0, 1); + + // Compile failed: ParseError(6, InvalidEscape("\\g")) + x2("\\A(a|b\\g<1>c)\\k<1+3>\\z", "bbacca", 0, 6); + + // Compile failed: ParseError(10, InvalidEscape("\\g")) + x2("(?i)\\A(a|b\\g<1>c)\\k<1+2>\\z", "bBACcbac", 0, 8); + + // No match found + x2("(?i)(?aa)|(?bb)\\k", "BBbb", 0, 4); + + // Compile failed: ParseError(5, InvalidGroupName) + x2("(?:\\k'+1'B|(A)C)*", "ACAB", 0, 4); + + // Compile failed: ParseError(0, InvalidEscape("\\g")) + x2("\\g<+2>(abc)(ABC){0}", "ABCabc", 0, 6); + + // Compile failed: ParseError(1, InvalidEscape("\\g")) + x2("A\\g'0'|B()", "AAAAB", 0, 5); + + // Compile failed: ParseError(2, InvalidEscape("\\g")) + x3("(A\\g'0')|B", "AAAAB", 0, 5, 1); + + // Compile failed: ParseError(10, GeneralParseError("expected conditional to be a backreference or at least an expression for when the condition is true")) + x2("(a*)(?(-1))aa", "aaaaa", 0, 5); + + // Compile failed: ParseError(7, GeneralParseError("expected close paren")) + x2("(a)(?(1+0)b|c)d", "abd", 0, 3); + + // Compile failed: ParseError(5, UnknownFlag("(?'")) + x2("(?:(?'name'a)|(?'name'b))(?('name')c|d)e", "ace", 0, 3); + + // Compile failed: ParseError(5, UnknownFlag("(?'")) + x2("(?:(?'name'a)|(?'name'b))(?('name')c|d)e", "bce", 0, 3); + + // Compile failed: ParseError(0, InvalidEscape("\\R")) + x2("\\R", "\r\n", 0, 2); + + // Compile failed: ParseError(0, InvalidEscape("\\R")) + x2("\\R", "\r", 0, 1); + + // Compile failed: ParseError(0, InvalidEscape("\\R")) + x2("\\R", "\n", 0, 1); + + // Compile failed: ParseError(0, InvalidEscape("\\R")) + x2("\\R", "\x0b", 0, 1); + + // Compile failed: ParseError(0, InvalidEscape("\\R")) + x2("\\R", "\xc2\x85", 0, 2); + + // Compile failed: ParseError(0, InvalidEscape("\\N")) + x2("\\N", "a", 0, 1); + + // Compile failed: ParseError(0, InvalidEscape("\\O")) + x2("\\O", "a", 0, 1); + + // Compile failed: ParseError(0, InvalidEscape("\\O")) + x2("\\O", "\n", 0, 1); + + // Compile failed: ParseError(4, InvalidEscape("\\O")) + x2("(?m:\\O)", "\n", 0, 1); + + // Compile failed: ParseError(5, InvalidEscape("\\O")) + x2("(?-m:\\O)", "\n", 0, 1); + + // No match found + x2("(?:()|())*\\1", "abc", 0, 0); + + // No match found + x2("(?:()|())*\\2", "abc", 0, 0); + + // No match found + x2("(?:()|()|())*\\3\\1", "abc", 0, 0); + + // Compile failed: ParseError(9, InvalidEscape("\\g")) + x2("(|(?:a(?:\\g'1')*))b|", "abc", 0, 2); + + // Compile failed: ParseError(14, InvalidEscape("\\g")) + x2("((?abc){0}a\\gd)+", "aabcd", 0, 5); + + // Match found at start 0 and end 3 (expected 0 and 6) + x2("(?a)(?b)(\\k)+", "abbaab", 0, 6); + + // Compile failed: ParseError(8, InvalidEscape("\\g")) + x2("(?$|b\\g)", "bbb", 0, 3); + + // Compile failed: ParseError(16, InvalidEscape("\\g")) + x2("(?(?(a)a|b)|c\\g)", "cccb", 0, 4); + + // Compile failed: ParseError(1, InvalidEscape("\\o")) + x2("[\\o{101}]", "A", 0, 1); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~)", "", 0, 0); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~)", "A", 0, 0); + + // Compile failed: ParseError(7, UnknownFlag("(?~")) + x2("aaaaa(?~)", "aaaaaaaaaa", 0, 5); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~(?:|aaa))", "aaa", 0, 0); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~aaa|)", "aaa", 0, 0); + + // Compile failed: ParseError(3, UnknownFlag("(?~")) + x2("a(?~(?~)).", "abcdefghijklmnopqrstuvwxyz", 0, 26); + + // Compile failed: ParseError(5, UnknownFlag("(?~")) + x2("/\\*(?~\\*/)\\*/", "/* */ */", 0, 5); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~\\w+)zzzzz", "zzzzz", 0, 5); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~\\w*)zzzzz", "zzzzz", 0, 5); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~A.C|B)", "ABC", 0, 0); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~XYZ|ABC)a", "ABCa", 1, 4); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~XYZ|ABC)a", "aABCa", 0, 1); + + // Compile failed: ParseError(9, UnknownFlag("(?~")) + x2("<[^>]*>(?~[<>])]*>", "vvv ", 0, 10); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~ab)", "ccc\ndab", 0, 5); + + // Compile failed: ParseError(6, UnknownFlag("(?~")) + x2("(?m:(?~ab))", "ccc\ndab", 0, 5); + + // Compile failed: ParseError(7, UnknownFlag("(?~")) + x2("(?-m:(?~ab))", "ccc\ndab", 0, 5); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~abc)xyz", "xyz012345678901234567890123456789abc", 0, 3); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|78|\\d*)", "123456789", 0, 6); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|def|(?:abc|de|f){0,100})", "abcdedeabcfdefabc", 0, 11); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|ab|.*)", "ccc\nddd", 0, 3); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|ab|\\O*)", "ccc\ndab", 0, 5); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|ab|\\O{2,10})", "ccc\ndab", 0, 5); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|ab|\\O{1,10})", "ab", 1, 2); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|abc|\\O{1,10})", "abc", 1, 3); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|ab|\\O{5,10})|abc", "abc", 0, 3); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|ab|\\O{1,10})", "cccccccccccab", 0, 10); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|aaa|)", "aaa", 0, 0); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~||a*)", "aaaaaa", 0, 0); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~||a*?)", "aaaaaa", 0, 0); + + // Compile failed: ParseError(5, UnknownFlag("(?~")) + x2("(a)(?~|b|\\1)", "aaaaaa", 0, 2); + + // Compile failed: ParseError(5, UnknownFlag("(?~")) + x2("(a)(?~|bb|(?:a\\1)*)", "aaaaaa", 0, 5); + + // Compile failed: ParseError(7, UnknownFlag("(?~")) + x2("(b|c)(?~|abac|(?:a\\1)*)", "abababacabab", 1, 4); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|aaaaa|a*+)", "aaaaa", 0, 0); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|aaaaaa|a*+)b", "aaaaaab", 1, 7); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|abcd|(?>))", "zzzabcd", 0, 0); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|abc|a*?)", "aaaabc", 0, 0); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|abc)a*", "aaaaaabc", 0, 5); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|abc)a*z|aaaaaabc", "aaaaaabc", 0, 8); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|aaaaaa)a*", "aaaaaa", 0, 0); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|abc)aaaa|aaaabc", "aaaabc", 0, 6); + + // Compile failed: ParseError(5, UnknownFlag("(?~")) + x2("(?>(?~|abc))aaaa|aaaabc", "aaaabc", 0, 6); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|)a", "a", 0, 1); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|a)(?~|)a", "a", 0, 1); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|a).*(?~|)a", "bbbbbbbbbbbbbbbbbbbba", 0, 21); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|abc).*(xyz|pqr)(?~|)abc", "aaaaxyzaaapqrabc", 0, 16); + + // Compile failed: ParseError(2, UnknownFlag("(?~")) + x2("(?~|abc).*(xyz|pqr)(?~|)abc", "aaaaxyzaaaabcpqrabc", 11, 19); + + // No match found + x2("\\xca\\xb8", "\xca\xb8", 0, 2); + + // Compile failed: ParseError(9, InvalidEscape("\\Z")) + x2("むめも\\Z", "むめも", 0, 9); + + // Compile failed: ParseError(9, InvalidEscape("\\Z")) + x2("かきく\\Z", "かきく\n", 0, 9); + + // No match found + x2("(?m:よ.)", "よ\n", 0, 4); + + // No match found + x2("(?m:.め)", "ま\nめ", 3, 7); + + // Compile failed: ParseError(7, InvalidEscape("\\Z")) + x2("鬼|車\\Z", "車鬼", 3, 6); + + // Compile failed: ParseError(7, InvalidEscape("\\Z")) + x2("鬼|車\\Z", "車", 0, 3); + + // Compile failed: ParseError(7, InvalidEscape("\\Z")) + x2("鬼|車\\Z", "車\n", 0, 3); + + // No match found + x2("(?:あい)?{2}", "", 0, 0); + + // No match found + x2("(?:鬼車)?{2}", "鬼車鬼車鬼", 0, 12); + + // No match found + x2("(?:鬼車)*{0}", "鬼車鬼車鬼", 0, 0); + + // Match found at start 0 and end 6 (expected 0 and 9) + x2("(?:鬼車){,}", "鬼車{,}", 0, 9); + + // No match found + x2("(?:かきく)+?{2}", "かきくかきくかきく", 0, 18); + + // No match found + x3("((?m:あ.う))", "あ\nう", 0, 7, 1); + + // Compile failed: ParseError(5, InvalidEscape("\\Z")) + x2("(あ*\\Z)\\1", "あ", 3, 3); + + // Compile failed: ParseError(6, InvalidEscape("\\Z")) + x2(".(あ*\\Z)\\1", "いあ", 3, 6); + + // Compile failed: ParseError(16, InvalidEscape("\\g")) + x2("(?<愚か>変|\\(\\g<愚か>\\))", "((((((変))))))", 0, 15); + + // Compile failed: ParseError(5, InvalidEscape("\\g")) + x2("\\A(?:\\g<阿_1>|\\g<云_2>|\\z終了 (?<阿_1>観|自\\g<云_2>自)(?<云_2>在|菩薩\\g<阿_1>菩薩))$", "菩薩自菩薩自在自菩薩自菩薩", 0, 39); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Parse(Error { kind: ClassRangeInvalid, pattern: "[あ-&&-あ]", span: Span(Position(o: 1, l: 1, c: 2), Position(o: 6, l: 1, c: 5)) }) } })) + x2("[あ-&&-あ]", "-", 0, 1); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Translate(Error { kind: UnicodePropertyNotFound, pattern: "\\p{^Emoji}", span: Span(Position(o: 0, l: 1, c: 1), Position(o: 10, l: 1, c: 11)) }) } })) + x2("\\p{^Emoji}", "\xEF\xBC\x93", 0, 3); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Translate(Error { kind: UnicodePropertyNotFound, pattern: "\\p{Word}", span: Span(Position(o: 0, l: 1, c: 1), Position(o: 8, l: 1, c: 9)) }) } })) + x2("\\p{Word}", "こ", 0, 3); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Translate(Error { kind: UnicodePropertyNotFound, pattern: "[\\p{Word}]", span: Span(Position(o: 1, l: 1, c: 2), Position(o: 9, l: 1, c: 10)) }) } })) + x2("[\\p{Word}]", "こ", 0, 3); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Translate(Error { kind: UnicodePropertyNotFound, pattern: "[^\\p{^Word}]", span: Span(Position(o: 2, l: 1, c: 3), Position(o: 11, l: 1, c: 12)) }) } })) + x2("[^\\p{^Word}]", "こ", 0, 3); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Translate(Error { kind: UnicodePropertyNotFound, pattern: "[^\\p{^Word}&&\\p{ASCII}]", span: Span(Position(o: 2, l: 1, c: 3), Position(o: 11, l: 1, c: 12)) }) } })) + x2("[^\\p{^Word}&&\\p{ASCII}]", "こ", 0, 3); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Translate(Error { kind: UnicodePropertyNotFound, pattern: "[^\\p{^Word}&&\\p{ASCII}]", span: Span(Position(o: 2, l: 1, c: 3), Position(o: 11, l: 1, c: 12)) }) } })) + x2("[^\\p{^Word}&&\\p{ASCII}]", "a", 0, 1); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Translate(Error { kind: UnicodePropertyNotFound, pattern: "[^[\\p{^Word}]&&[\\p{ASCII}]]", span: Span(Position(o: 3, l: 1, c: 4), Position(o: 12, l: 1, c: 13)) }) } })) + x2("[^[\\p{^Word}]&&[\\p{ASCII}]]", "こ", 0, 3); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Translate(Error { kind: UnicodePropertyNotFound, pattern: "[^[\\p{ASCII}]&&[^\\p{Word}]]", span: Span(Position(o: 17, l: 1, c: 18), Position(o: 25, l: 1, c: 26)) }) } })) + x2("[^[\\p{ASCII}]&&[^\\p{Word}]]", "こ", 0, 3); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Translate(Error { kind: UnicodePropertyNotFound, pattern: "[^[\\p{^Word}]&&[^\\p{ASCII}]]", span: Span(Position(o: 3, l: 1, c: 4), Position(o: 12, l: 1, c: 13)) }) } })) + x2("[^[\\p{^Word}]&&[^\\p{ASCII}]]", "こ", 0, 3); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Translate(Error { kind: UnicodePropertyNotFound, pattern: "[^\\p{^Word}&&[^၊]]", span: Span(Position(o: 2, l: 1, c: 3), Position(o: 11, l: 1, c: 12)) }) } })) + x2("[^\\p{^Word}&&[^\\x{104a}]]", "こ", 0, 3); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Translate(Error { kind: UnicodePropertyNotFound, pattern: "[^[\\p{^Word}]&&[^၊]]", span: Span(Position(o: 3, l: 1, c: 4), Position(o: 12, l: 1, c: 13)) }) } })) + x2("[^[\\p{^Word}]&&[^\\x{104a}]]", "こ", 0, 3); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Translate(Error { kind: UnicodePropertyNotFound, pattern: "\\p{^Cntrl}", span: Span(Position(o: 0, l: 1, c: 1), Position(o: 10, l: 1, c: 11)) }) } })) + x2("\\p{^Cntrl}", "こ", 0, 3); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Translate(Error { kind: UnicodePropertyNotFound, pattern: "[\\p{^Cntrl}]", span: Span(Position(o: 1, l: 1, c: 2), Position(o: 11, l: 1, c: 12)) }) } })) + x2("[\\p{^Cntrl}]", "こ", 0, 3); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Translate(Error { kind: UnicodePropertyNotFound, pattern: "[^[\\p{^Cntrl}]&&[\\p{ASCII}]]", span: Span(Position(o: 3, l: 1, c: 4), Position(o: 13, l: 1, c: 14)) }) } })) + x2("[^[\\p{^Cntrl}]&&[\\p{ASCII}]]", "こ", 0, 3); + + // Compile failed: ParseError(2, UnknownFlag("(?-W")) + x2("(?-W:\\p{Word})", "こ", 0, 3); + + // Compile failed: ParseError(2, UnknownFlag("(?W")) + x2("(?W:\\p{Word})", "k", 0, 1); + + // Compile failed: ParseError(2, UnknownFlag("(?-W")) + x2("(?-W:[[:word:]])", "こ", 0, 3); + + // Compile failed: ParseError(2, UnknownFlag("(?-D")) + x2("(?-D:\\p{Digit})", "3", 0, 3); + + // Compile failed: ParseError(2, UnknownFlag("(?-S")) + x2("(?-S:\\p{Space})", "\xc2\x85", 0, 2); + + // Compile failed: ParseError(2, UnknownFlag("(?-P")) + x2("(?-P:\\p{Word})", "こ", 0, 3); + + // Compile failed: ParseError(2, UnknownFlag("(?-W")) + x2("(?-W:\\w)", "こ", 0, 3); + + // Compile failed: ParseError(2, UnknownFlag("(?-W")) + x2("(?-W:\\w)", "k", 0, 1); + + // Compile failed: ParseError(2, UnknownFlag("(?W")) + x2("(?W:\\w)", "k", 0, 1); + + // Compile failed: ParseError(2, UnknownFlag("(?W")) + x2("(?W:\\W)", "こ", 0, 3); + + // Compile failed: ParseError(2, UnknownFlag("(?-W")) + x2("(?-W:\\b)", "こ", 0, 0); + + // Compile failed: ParseError(2, UnknownFlag("(?-W")) + x2("(?-W:\\b)", "h", 0, 0); + + // Compile failed: ParseError(2, UnknownFlag("(?W")) + x2("(?W:\\b)", "h", 0, 0); + + // Compile failed: ParseError(2, UnknownFlag("(?W")) + x2("(?W:\\B)", "こ", 0, 0); + + // Compile failed: ParseError(2, UnknownFlag("(?-P")) + x2("(?-P:\\b)", "こ", 0, 0); + + // Compile failed: ParseError(2, UnknownFlag("(?-P")) + x2("(?-P:\\b)", "h", 0, 0); + + // Compile failed: ParseError(2, UnknownFlag("(?P")) + x2("(?P:\\b)", "h", 0, 0); + + // Compile failed: ParseError(2, UnknownFlag("(?P")) + x2("(?P:\\B)", "こ", 0, 0); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Translate(Error { kind: UnicodePropertyNotFound, pattern: "\\p{InBasicLatin}", span: Span(Position(o: 0, l: 1, c: 1), Position(o: 16, l: 1, c: 17)) }) } })) + x2("\\p{InBasicLatin}", "\x41", 0, 1); + + // Compile failed: ParseError(1, InvalidEscape("\\Y")) + x2(".\\Y\\O", "\x0d\x0a", 0, 2); + + // Compile failed: ParseError(1, InvalidEscape("\\Y")) + x2(".\\Y.", "\x67\xCC\x88", 0, 3); + + // Compile failed: ParseError(0, InvalidEscape("\\y")) + x2("\\y.\\Y.\\y", "\x67\xCC\x88", 0, 3); + + // Compile failed: ParseError(0, InvalidEscape("\\y")) + x2("\\y.\\y", "\xEA\xB0\x81", 0, 3); + + // Compile failed: ParseError(2, InvalidEscape("\\Y")) + x2("^.\\Y.\\Y.$", "\xE1\x84\x80\xE1\x85\xA1\xE1\x86\xA8", 0, 9); + + // Compile failed: ParseError(1, InvalidEscape("\\Y")) + x2(".\\Y.", "\xE0\xAE\xA8\xE0\xAE\xBF", 0, 6); + + // Compile failed: ParseError(1, InvalidEscape("\\Y")) + x2(".\\Y.", "\xE0\xB8\x81\xE0\xB8\xB3", 0, 6); + + // Compile failed: ParseError(1, InvalidEscape("\\Y")) + x2(".\\Y.", "\xE0\xA4\xB7\xE0\xA4\xBF", 0, 6); + + // Compile failed: ParseError(2, InvalidEscape("\\Y")) + x2("..\\Y.", "\xE3\x80\xB0\xE2\x80\x8D\xE2\xAD\x95", 0, 9); + + // Compile failed: ParseError(3, InvalidEscape("\\Y")) + x2("...\\Y.", "\xE3\x80\xB0\xCC\x82\xE2\x80\x8D\xE2\xAD\x95", 0, 11); + + // Compile failed: ParseError(1, InvalidEscape("\\X")) + x2("^\\X$", "\x0d\x0a", 0, 2); + + // Compile failed: ParseError(1, InvalidEscape("\\X")) + x2("^\\X$", "\x67\xCC\x88", 0, 3); + + // Compile failed: ParseError(1, InvalidEscape("\\X")) + x2("^\\X$", "\xE1\x84\x80\xE1\x85\xA1\xE1\x86\xA8", 0, 9); + + // Compile failed: ParseError(1, InvalidEscape("\\X")) + x2("^\\X$", "\xE0\xAE\xA8\xE0\xAE\xBF", 0, 6); + + // Compile failed: ParseError(1, InvalidEscape("\\X")) + x2("^\\X$", "\xE0\xB8\x81\xE0\xB8\xB3", 0, 6); + + // Compile failed: ParseError(1, InvalidEscape("\\X")) + x2("^\\X$", "\xE0\xA4\xB7\xE0\xA4\xBF", 0, 6); + + // Compile failed: ParseError(1, InvalidEscape("\\X")) + x2("h\\Xllo", "ha\xCC\x80llo", 0, 7); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{g})\\yabc\\y", "abc", 0, 3); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{g})\\y\\X\\y", "abc", 0, 1); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\yabc\\y", "abc", 0, 3); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\X", "\r\n", 0, 2); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\X", "\x0cz", 0, 1); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\X", "q\x0c", 0, 1); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\X", "\xE2\x80\x8D\xE2\x9D\x87", 0, 6); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\X", "\x20\x20", 0, 2); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\X", "a\xE2\x80\x8D", 0, 4); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\y\\X\\y", "abc", 0, 3); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\y\\X\\y", "v\xCE\x87w", 0, 4); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\y\\X\\y", "\xD7\x93\x27", 0, 3); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\y\\X\\y", "\xD7\x93\x22\xD7\x93", 0, 5); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\X", "14 45", 0, 2); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\X", "a14", 0, 3); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\X", "832e", 0, 4); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\X", "8\xEF\xBC\x8C\xDB\xB0", 0, 6); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\y\\X\\y", "ケン", 0, 6); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\y\\X\\y", "ケン\xE2\x80\xAFタ", 0, 12); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\y\\X\\y", "\x21\x23", 0, 1); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\y\\X\\y", "山ア", 0, 3); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\X", "3.14", 0, 4); + + // Compile failed: ParseError(2, UnknownFlag("(?y")) + x2("(?y{w})\\X", "3 14", 0, 1); + + // Compile failed: ParseError(2, InvalidHex) + x2("\\x1", "\x01", 0, 1); + + // Compile failed: ParseError(10, TargetNotRepeatable) + x2("((?()0+)+++(((0\\g<0>)0)|())++++((?(1)(0\\g<0>))++++++0*())++++((?(1)(0\\g<1>)+)++++++++++*())++++((?(1)((0)\\g<0>)+)++())+0++*+++(((0\\g<0>))*())++++((?(1)(0\\g<0>)+)++++++++++*|)++++*+++((?(1)((0)\\g<0>)+)+++++++++())++*|)++++((?()0))|", "abcde", 0, 0); + + // Compile failed: ParseError(9, TargetNotRepeatable) + x2("(?:[ab]|(*MAX{2}).)*", "abcbaaccaaa", 0, 7); + + // Compile failed: ParseError(4, TargetNotRepeatable) + x2("(?:(*COUNT[AB]{X})[ab]|(*COUNT[CD]{X})[cd])*(*CMP{AB,<,CD})", + "abababcdab", 5, 8); + + // Compile failed: ParseError(3, TargetNotRepeatable) + x2("(?(?{....})123|456)", "123", 0, 3); + + // Compile failed: ParseError(3, TargetNotRepeatable) + x2("(?(*FAIL)123|456)", "456", 0, 3); + + // Compile failed: ParseError(0, InvalidEscape("\\g")) + x2("\\g'0'++{,0}", "abcdefgh", 0, 0); + + // Compile failed: ParseError(0, InvalidEscape("\\g")) + x2("\\g'0'++{,0}?", "abcdefgh", 0, 0); + + // Compile failed: ParseError(0, InvalidEscape("\\g")) + x2("\\g'0'++{,0}b", "abcdefgh", 1, 2); + + // Compile failed: ParseError(0, InvalidEscape("\\g")) + x2("\\g'0'++{,0}?def", "abcdefgh", 3, 6); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Parse(Error { kind: RepetitionCountInvalid, pattern: "a{3,2}b", span: Span(Position(o: 1, l: 1, c: 2), Position(o: 6, l: 1, c: 7)) }) } })) + x2("a{3,2}b", "aaab", 0, 4); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Parse(Error { kind: RepetitionCountInvalid, pattern: "a{3,2}b", span: Span(Position(o: 1, l: 1, c: 2), Position(o: 6, l: 1, c: 7)) }) } })) + x2("a{3,2}b", "aaaab", 1, 5); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Parse(Error { kind: RepetitionCountInvalid, pattern: "a{3,2}b", span: Span(Position(o: 1, l: 1, c: 2), Position(o: 6, l: 1, c: 7)) }) } })) + x2("a{3,2}b", "aab", 0, 3); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Parse(Error { kind: RepetitionCountInvalid, pattern: "a{3,2}?", span: Span(Position(o: 1, l: 1, c: 2), Position(o: 7, l: 1, c: 8)) }) } })) + x2("a{3,2}?", "", 0, 0); + + // No match found + x2("a{2,3}+a", "aaa", 0, 3); + + // Compile failed: CompileError(InnerError(BuildError { kind: Syntax { pid: PatternID(0), err: Translate(Error { kind: UnicodePropertyNotFound, pattern: "\\p{In_Enclosed_CJK_Letters_and_Months}", span: Span(Position(o: 0, l: 1, c: 1), Position(o: 38, l: 1, c: 39)) }) } })) + x2("\\p{In_Enclosed_CJK_Letters_and_Months}", "\xe3\x8b\xbf", 0, 3); diff --git a/tools/vendor/fancy-regex/tests/regex_options.rs b/tools/vendor/fancy-regex/tests/regex_options.rs new file mode 100644 index 0000000000..9fbd4d16fc --- /dev/null +++ b/tools/vendor/fancy-regex/tests/regex_options.rs @@ -0,0 +1,44 @@ +use fancy_regex::RegexBuilder; + +#[test] +fn check_casing_option() { + let builder = RegexBuilder::new(r"TEST foo") + .case_insensitive(false) + .build(); + + match builder { + Ok(regex) => { + assert!(regex.is_match(r"TEST foo").unwrap_or_default()); + assert!(!regex.is_match(r"test foo").unwrap_or_default()); + } + _ => panic!("builder should be able to compile with casing options"), + } +} + +#[test] +fn check_override_casing_option() { + let builder = RegexBuilder::new(r"FOO(?i:bar)quux") + .case_insensitive(false) + .build(); + + match builder { + Ok(regex) => { + assert!(!regex.is_match("FoObarQuUx").unwrap_or_default()); + assert!(!regex.is_match("fooBARquux").unwrap_or_default()); + assert!(regex.is_match("FOObarquux").unwrap_or_default()); + } + _ => panic!("builder should be able to compile with casing options"), + } +} + +#[test] +fn check_casing_insensitive_option() { + let builder = RegexBuilder::new(r"TEST FOO") + .case_insensitive(true) + .build(); + + match builder { + Ok(regex) => assert!(regex.is_match(r"test foo").unwrap_or_default()), + _ => panic!("builder should be able to compile with casing options"), + } +} diff --git a/tools/vendor/fancy-regex/tests/replace.rs b/tools/vendor/fancy-regex/tests/replace.rs new file mode 100644 index 0000000000..deef3c33ec --- /dev/null +++ b/tools/vendor/fancy-regex/tests/replace.rs @@ -0,0 +1,82 @@ +use fancy_regex::{Captures, NoExpand}; +use std::borrow::Cow; + +mod common; + +#[test] +fn replacer_string() { + let regex = common::regex( + r"\b([sS])uc(?:cs|s?)e(ed(?:ed|ing|s?)|ss(?:es|ful(?:ly)?|i(?:ons?|ve(?:ly)?)|ors?)?)\b", + ); + + // Replacer impl for &str + let result = regex.replace("a sucessful b", "${1}ucce$2"); + assert_eq!(result, "a successful b"); + + // Replacer impl for &String + let repl_string = "${1}ucce$2".to_string(); + let result = regex.replace("a Suceeded b", &repl_string); + assert_eq!(result, "a Succeeded b"); + + // Replacer impl for String + let result = regex.replace("a sucessor b", repl_string); + assert_eq!(result, "a successor b"); +} + +#[test] +fn replacer_cow() { + let regex = common::regex(r"\b([oO])mmi(?=t)t?(t(?:ed|ing)|s)\b"); + + // Replacer impl for &Cow + let result = regex.replace("a ommiting b", &Cow::from("${1}mit$2")); + assert_eq!(result, "a omitting b"); + + // Replacer for Cow::Borrowed + let result = regex.replace("a ommited b", Cow::Borrowed("${1}mit$2")); + assert_eq!(result, "a omitted b"); + + // Replacer for Cow::Owned + let result = regex.replace("a Ommits b", Cow::Owned("${1}mit$2".to_string())); + assert_eq!(result, "a Omits b"); +} + +#[test] +fn replacer_noexpand() { + let regex = common::regex(r"\b([aA])n+ull(ar|ments?|s?)\b"); + + // Replacer impl for NoExpand + let result = regex.replace("a anullment b", NoExpand("${1}nnul$2")); + assert_eq!(result, "a ${1}nnul$2 b"); +} + +#[test] +fn replacer_callback() { + let regex = common::regex(r"\b([aA])p(?:p[or]|ro)x\.?(?=[ \)\n])"); + + // Replacer impl for FnMut(&Captures) + let result = regex.replace("a Aprox b", |cap: &Captures| { + format!("{}pprox.", cap.get(1).unwrap().as_str()) + }); + assert_eq!(result, "a Approx. b"); +} + +/// `replace()` does only one replacement +#[test] +fn replace_one() { + let regex = common::regex("bla"); + assert_eq!(regex.replace("blabla", "foo"), "foobla"); +} + +/// `replace_all()` replaces all non-overlapping matches +#[test] +fn replace_all() { + let regex = common::regex("aa"); + assert_eq!(regex.replace_all("aaaa aaa aa a", "xx"), "xxxx xxa xx a"); +} + +/// `replacen()` replaces predefined number of times +#[test] +fn replacen() { + let regex = common::regex("bla"); + assert_eq!(regex.replacen("blablabla", 2, "foo"), "foofoobla"); +} diff --git a/tools/vendor/fancy-regex/tests/splitting.rs b/tools/vendor/fancy-regex/tests/splitting.rs new file mode 100644 index 0000000000..aba0c0cd2f --- /dev/null +++ b/tools/vendor/fancy-regex/tests/splitting.rs @@ -0,0 +1,172 @@ +use fancy_regex::Regex; + +#[cfg(test)] +mod split_tests { + use super::*; + + fn split_to_vec<'a>(re_str: &'a str, target: &'a str) -> Vec<&'a str> { + let re = Regex::new(re_str).unwrap(); + re.split(target).map(|x| x.unwrap()).collect() + } + + #[test] + fn split_left() { + let result: Vec<&str> = split_to_vec("1", "123"); + assert_eq!(result, vec!["", "23"]); + } + + #[test] + fn split_center() { + let result: Vec<&str> = split_to_vec("2", "123"); + assert_eq!(result, vec!["1", "3"]); + } + + #[test] + fn split_right() { + let result: Vec<&str> = split_to_vec("3", "123"); + assert_eq!(result, vec!["12", ""]); + } + + #[test] + fn split_no_matches() { + let result: Vec<&str> = split_to_vec("4", "123"); + assert_eq!(result, vec!["123"]); + } + + #[test] + fn split_empty() { + let result: Vec<&str> = split_to_vec("1", ""); + assert_eq!(result, vec![""]); + } + + #[test] + fn split_by_empty() { + let result: Vec<&str> = split_to_vec("", "123"); + assert_eq!(result, vec!["", "1", "2", "3", ""]); + } + + #[test] + fn split_by_own() { + let result: Vec<&str> = split_to_vec("123", "123"); + assert_eq!(result, vec!["", ""]); + } + + #[test] + fn split_consecutive_matches() { + let result: Vec<&str> = split_to_vec("1", "111"); + assert_eq!(result, vec!["", "", "", ""]); + } + + #[test] + fn split_by_substring() { + let result: Vec<&str> = split_to_vec("123", "123456"); + assert_eq!(result, vec!["", "456"]); + + let result: Vec<&str> = split_to_vec("234|678", "123456789"); + assert_eq!(result, vec!["1", "5", "9"]); + } + + #[test] + fn split_multiple_different_characters() { + let result: Vec<&str> = split_to_vec("[1-3]", "123456"); + assert_eq!(result, vec!["", "", "", "456"]); + } + + #[test] + fn split_mixed_characters() { + let result: Vec<&str> = split_to_vec("[236]", "123456"); + assert_eq!(result, vec!["1", "", "45", ""]); + } + + #[test] + fn split_with_backreferences() { + let result: Vec<&str> = split_to_vec(r"(1|2)\1", "12112122"); + assert_eq!(result, vec!["12", "21", ""]); + } + + #[test] + fn split_with_look_around() { + let result: Vec<&str> = split_to_vec(r"(?<=1)2", "12112122"); + assert_eq!(result, vec!["1", "11", "1", "2"]); + + let result: Vec<&str> = split_to_vec(r"1(?=2)", "12112122"); + assert_eq!(result, vec!["", "21", "2", "22"]); + + let result: Vec<&str> = split_to_vec(r"(?<=2)1(?=2)", "12112122"); + assert_eq!(result, vec!["12112", "22"]); + } +} + +#[cfg(test)] +mod splitn_tests { + use super::*; + + fn splitn_to_vec<'a>(re_str: &'a str, target: &'a str, limit: usize) -> Vec<&'a str> { + let re = Regex::new(re_str).unwrap(); + re.splitn(target, limit).map(|x| x.unwrap()).collect() + } + + #[test] + fn splitn_limit_lt_num_mathes() { + let splitn_test_cases = [ + ("1", "123", vec!["", "23"]), + ("2", "123", vec!["1", "3"]), + ("3", "123", vec!["12", ""]), + ("", "123", vec!["", "1", "2", "3", ""]), + ("1", "", vec![""]), + ]; + + for (re_str, target, expected) in splitn_test_cases { + let result: Vec<&str> = splitn_to_vec(re_str, target, 6); + assert_eq!(result, expected); + } + } + + #[test] + fn splitn_limit_eq_num_mathes() { + let splitn_test_cases = [ + ("1", "123", vec!["", "23"]), + ("2", "123", vec!["1", "3"]), + ("3", "123", vec!["12", ""]), + ("", "123", vec!["", "1", "2", "3", ""]), + ("1", "", vec![""]), + ]; + + for (re_str, target, expected) in splitn_test_cases { + let result: Vec<&str> = splitn_to_vec(re_str, target, expected.len()); + assert_eq!(result, expected); + } + } + + #[test] + fn splitn_limit_st_num_mathes() { + let splitn_test_cases = [ + ("1", "123", vec!["123"]), + ("2", "123", vec!["123"]), + ("3", "123", vec!["123"]), + ("", "123", vec!["123"]), + ]; + + for (re_str, target, expected) in splitn_test_cases { + let result: Vec<&str> = splitn_to_vec(re_str, target, 1); + assert_eq!(result, expected); + } + } + + #[test] + fn splitn_limit_zero() { + let vec_empty: Vec<&str> = Vec::new(); + let splitn_test_cases = [ + ("1", "123", &vec_empty), + ("2", "123", &vec_empty), + ("3", "123", &vec_empty), + ("", "123", &vec_empty), + ("1", "", &vec_empty), + ]; + + for (re_str, target, expected) in splitn_test_cases { + let result: Vec<&str> = splitn_to_vec(re_str, target, 0); + assert_eq!(&result, expected); + } + } +} diff --git a/tools/vendor/fastrand/.cargo-checksum.json b/tools/vendor/fastrand/.cargo-checksum.json new file mode 100644 index 0000000000..116d95bb8f --- /dev/null +++ b/tools/vendor/fastrand/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"975b0cd2494533eaa334b797cdb8f5f9642823f9d538631a0d620a99731a9705","CHANGELOG.md":"22cdcc219d5f384821fedb05d2fbc11bcee08d4e1e301379fa9a0e97035fc0ed","Cargo.toml":"300d90d57922b41007ef4849f1e4defbce4f835e06aba11289b5d4f7d15c5267","Cargo.toml.orig":"86b5da22353de667e400a5b4be28754f697002f79d8e635648eabcc66258ff75","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"ea448b5f285766fff63cfcfdb851408ee2fe561f86b821f16d23fe004a4403ca","benches/bench.rs":"8e3cb6b96bb656731e88b4f70d942fe58eb43fa51d9b6374899c54c06dce244f","src/global_rng.rs":"b72eb898d56aa3763794c3bb271f2899e435aed435b3e09534638c5a158001c0","src/lib.rs":"34c09b1561225b09793721788ee19f1a7933e53405227614fd032286adea86c5","tests/char.rs":"a530b41837f5bf43701d983ef0267d9b44779d455f24cbf30b881cd348de9ee1","tests/smoke.rs":"8eac48144705364d142882538be43b8d69018959579404c3b1e638827888e62e"},"package":"37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"} \ No newline at end of file diff --git a/tools/vendor/fastrand/.cargo_vcs_info.json b/tools/vendor/fastrand/.cargo_vcs_info.json new file mode 100644 index 0000000000..5b996009b1 --- /dev/null +++ b/tools/vendor/fastrand/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "8419f8916f08c63572b4f7cbdc07cec94c1fc5ed" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/tools/vendor/fastrand/CHANGELOG.md b/tools/vendor/fastrand/CHANGELOG.md new file mode 100644 index 0000000000..10ed1fa5f0 --- /dev/null +++ b/tools/vendor/fastrand/CHANGELOG.md @@ -0,0 +1,124 @@ +# Version 2.3.0 + +- Accept `IntoIterator` in `choose_multiple` functions instead of just `Iterator`. (#92) + +# Version 2.2.0 + +- Expose missing `fill` method for the global RNG. (#90) + +# Version 2.1.1 + +- Remove support for 128-bit targets, as they are not supported by rustc yet. (#87) + +# Version 2.1.0 + +- Change the RNG algorithm and the way that the seed is computed. This will cause + the algorithm to emit different constants for different seeds, hence the minor + SemVer change. + - Update to the final WyRand v4.2 constants for better entropy. (#82) + - Remove an unnecessary seed modification. (#73) + +# Version 2.0.2 + +- Slight restructuring of the `with_seed` function. (#79) + +# Version 2.0.1 + +- Clarify documentation for the `fork()` method. (#62) +- Mention `fastrand-contrib` in documentation. (#70) + +# Version 2.0.0 + +- **Breaking:** Remove interior mutability from `Rng`. (#47) +- Add a `fork()` method. (#49) +- Add a `no_std` mode. (#50) +- Add an iterator selection function. (#51) +- Add a `choose_multiple()` function for sampling several elements from an iterator. (#55) +- Use the `getrandom` crate for seeding on WebAssembly targets if the `js` feature is enabled. (#60) + +# Version 1.9.0 + +- Add `Rng::fill()` (#35, #43) +- Add `#[must_use]` to `Rng::with_seed()` (#46) + +# Version 1.8.0 + +- Add `get_seed()` and `Rng::get_seed()` (#33) + +# Version 1.7.0 + +- Add `char()` and `Rng::char()` (#25) + +# Version 1.6.0 + +- Implement `PartialEq` and `Eq` for `Rng` (#23) + +# Version 1.5.0 + +- Switch to Wyrand (#14) + +# Version 1.4.1 + +- Fix bug when generating a signed integer within a range (#16) + +# Version 1.4.0 + +- Add wasm support. + +# Version 1.3.5 + +- Reword docs. +- Add `Rng::with_seed()`. + +# Version 1.3.4 + +- Implement `Clone` for `Rng`. + +# Version 1.3.3 + +- Forbid unsafe code. + +# Version 1.3.2 + +- Support older Rust versions. + +# Version 1.3.1 + +- Tweak Cargo keywords. + +# Version 1.3.0 + +- Add `f32()` and `f64()`. +- Add `lowercase()`, `uppercase()`, `alphabetic()`, and `digit()`. + +# Version 1.2.4 + +- Switch to PCG XSH RR 64/32. +- Fix a bug in `gen_mod_u128`. +- Fix bias in ranges. + +# Version 1.2.3 + +- Support Rust 1.32.0 + +# Version 1.2.2 + +- Use `std::$t::MAX` rather than `$t::MAX` to support older Rust versions. + +# Version 1.2.1 + +- Inline all functions. + +# Version 1.2.0 + +- Add `Rng` struct. + +# Version 1.1.0 + +- Switch to PCG implementation. +- Add `alphanumeric()`. +- Add `seed()`. + +# Version 1.0.0 + +- Initial version diff --git a/tools/vendor/fastrand/Cargo.toml b/tools/vendor/fastrand/Cargo.toml new file mode 100644 index 0000000000..5350ed4d21 --- /dev/null +++ b/tools/vendor/fastrand/Cargo.toml @@ -0,0 +1,89 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +rust-version = "1.36" +name = "fastrand" +version = "2.3.0" +authors = ["Stjepan Glavina "] +build = false +exclude = ["/.*"] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "A simple and fast random number generator" +readme = "README.md" +keywords = [ + "simple", + "fast", + "rand", + "random", + "wyrand", +] +categories = ["algorithms"] +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/fastrand" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = [ + "--cfg", + "docsrs", +] + +[lib] +name = "fastrand" +path = "src/lib.rs" + +[[test]] +name = "char" +path = "tests/char.rs" + +[[test]] +name = "smoke" +path = "tests/smoke.rs" + +[[bench]] +name = "bench" +path = "benches/bench.rs" + +[dev-dependencies.getrandom] +version = "0.2" + +[dev-dependencies.rand] +version = "0.8" + +[dev-dependencies.wyhash] +version = "0.5" + +[features] +alloc = [] +default = ["std"] +js = [ + "std", + "getrandom", +] +std = ["alloc"] + +[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dependencies.getrandom] +version = "0.2" +features = ["js"] +optional = true + +[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dev-dependencies.getrandom] +version = "0.2" +features = ["js"] + +[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dev-dependencies.wasm-bindgen-test] +version = "0.3" diff --git a/tools/vendor/fastrand/Cargo.toml.orig b/tools/vendor/fastrand/Cargo.toml.orig new file mode 100644 index 0000000000..64249ff69b --- /dev/null +++ b/tools/vendor/fastrand/Cargo.toml.orig @@ -0,0 +1,37 @@ +[package] +name = "fastrand" +# When publishing a new version: +# - Update CHANGELOG.md +# - Create "v2.x.y" git tag +version = "2.3.0" +authors = ["Stjepan Glavina "] +edition = "2018" +rust-version = "1.36" +description = "A simple and fast random number generator" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/smol-rs/fastrand" +keywords = ["simple", "fast", "rand", "random", "wyrand"] +categories = ["algorithms"] +exclude = ["/.*"] + +[features] +default = ["std"] +alloc = [] +std = ["alloc"] +js = ["std", "getrandom"] + +[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dependencies] +getrandom = { version = "0.2", features = ["js"], optional = true } + +[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dev-dependencies] +wasm-bindgen-test = "0.3" +getrandom = { version = "0.2", features = ["js"] } + +[dev-dependencies] +rand = "0.8" +wyhash = "0.5" +getrandom = "0.2" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/tools/vendor/fastrand/LICENSE-APACHE b/tools/vendor/fastrand/LICENSE-APACHE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/tools/vendor/fastrand/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/tools/vendor/fastrand/LICENSE-MIT b/tools/vendor/fastrand/LICENSE-MIT new file mode 100644 index 0000000000..31aa79387f --- /dev/null +++ b/tools/vendor/fastrand/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/tools/vendor/fastrand/README.md b/tools/vendor/fastrand/README.md new file mode 100644 index 0000000000..cb05f6cb5d --- /dev/null +++ b/tools/vendor/fastrand/README.md @@ -0,0 +1,110 @@ +# fastrand + +[![Build](https://github.com/smol-rs/fastrand/workflows/CI/badge.svg)]( +https://github.com/smol-rs/fastrand/actions) +[![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( +https://github.com/smol-rs/fastrand) +[![Cargo](https://img.shields.io/crates/v/fastrand.svg)]( +https://crates.io/crates/fastrand) +[![Documentation](https://docs.rs/fastrand/badge.svg)]( +https://docs.rs/fastrand) + +A simple and fast random number generator. + +The implementation uses [Wyrand](https://github.com/wangyi-fudan/wyhash), a simple and fast +generator but **not** cryptographically secure. + +## Examples + +Flip a coin: + +```rust +if fastrand::bool() { + println!("heads"); +} else { + println!("tails"); +} +``` + +Generate a random `i32`: + +```rust +let num = fastrand::i32(..); +``` + +Choose a random element in an array: + +```rust +let v = vec![1, 2, 3, 4, 5]; +let i = fastrand::usize(..v.len()); +let elem = v[i]; +``` + +Sample values from an array with `O(n)` complexity (`n` is the length of array): + +```rust +fastrand::choose_multiple([1, 4, 5], 2); +fastrand::choose_multiple(0..20, 12); +``` + +Shuffle an array: + +```rust +let mut v = vec![1, 2, 3, 4, 5]; +fastrand::shuffle(&mut v); +``` + +Generate a random `Vec` or `String`: + +```rust +use std::iter::repeat_with; + +let v: Vec = repeat_with(|| fastrand::i32(..)).take(10).collect(); +let s: String = repeat_with(fastrand::alphanumeric).take(10).collect(); +``` + +To get reproducible results on every run, initialize the generator with a seed: + +```rust +// Pick an arbitrary number as seed. +fastrand::seed(7); + +// Now this prints the same number on every run: +println!("{}", fastrand::u32(..)); +``` + +To be more efficient, create a new `Rng` instance instead of using the thread-local +generator: + +```rust +use std::iter::repeat_with; + +let rng = fastrand::Rng::new(); +let mut bytes: Vec = repeat_with(|| rng.u8(..)).take(10_000).collect(); +``` + +This crate aims to expose a core set of useful randomness primitives. For more niche algorithms, consider using the [`fastrand-contrib`] crate alongside this one. + +# Features + +- `std` (enabled by default): Enables the `std` library. This is required for the global + generator and global entropy. Without this feature, [`Rng`] can only be instantiated using + the [`with_seed`](https://docs.rs/fastrand/latest/fastrand/struct.Rng.html#method.with_seed) method. +- `js`: Assumes that WebAssembly targets are being run in a JavaScript environment. + +[`fastrand-contrib`]: https://crates.io/crates/fastrand-contrib + +## License + +Licensed under either of + + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +#### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/tools/vendor/fastrand/benches/bench.rs b/tools/vendor/fastrand/benches/bench.rs new file mode 100644 index 0000000000..bf14ee0732 --- /dev/null +++ b/tools/vendor/fastrand/benches/bench.rs @@ -0,0 +1,98 @@ +#![feature(test)] + +extern crate test; + +use rand::prelude::*; +use test::Bencher; +use wyhash::WyRng; + +#[bench] +fn shuffle_wyhash(b: &mut Bencher) { + let mut rng = WyRng::from_rng(thread_rng()).unwrap(); + let mut x = (0..100).collect::>(); + b.iter(|| { + x.shuffle(&mut rng); + x[0] + }) +} + +#[bench] +fn shuffle_fastrand(b: &mut Bencher) { + let mut rng = fastrand::Rng::new(); + let mut x = (0..100).collect::>(); + b.iter(|| { + rng.shuffle(&mut x); + x[0] + }) +} + +#[bench] +fn u8_wyhash(b: &mut Bencher) { + let mut rng = WyRng::from_rng(thread_rng()).unwrap(); + b.iter(|| { + let mut sum = 0u8; + for _ in 0..10_000 { + sum = sum.wrapping_add(rng.gen::()); + } + sum + }) +} + +#[bench] +fn u8_fastrand(b: &mut Bencher) { + let mut rng = fastrand::Rng::new(); + b.iter(|| { + let mut sum = 0u8; + for _ in 0..10_000 { + sum = sum.wrapping_add(rng.u8(..)); + } + sum + }) +} + +#[bench] +fn u32_wyhash(b: &mut Bencher) { + let mut rng = WyRng::from_rng(thread_rng()).unwrap(); + b.iter(|| { + let mut sum = 0u32; + for _ in 0..10_000 { + sum = sum.wrapping_add(rng.gen::()); + } + sum + }) +} + +#[bench] +fn u32_fastrand(b: &mut Bencher) { + let mut rng = fastrand::Rng::new(); + b.iter(|| { + let mut sum = 0u32; + for _ in 0..10_000 { + sum = sum.wrapping_add(rng.u32(..)); + } + sum + }) +} + +#[bench] +fn fill(b: &mut Bencher) { + let mut rng = fastrand::Rng::new(); + b.iter(|| { + // Pick a size that isn't divisible by 8. + let mut bytes = [0u8; 367]; + rng.fill(&mut bytes); + bytes + }) +} + +#[bench] +fn fill_naive(b: &mut Bencher) { + let mut rng = fastrand::Rng::new(); + b.iter(|| { + let mut bytes = [0u8; 367]; + for item in &mut bytes { + *item = rng.u8(..); + } + bytes + }) +} diff --git a/tools/vendor/fastrand/src/global_rng.rs b/tools/vendor/fastrand/src/global_rng.rs new file mode 100644 index 0000000000..0db6c8d33a --- /dev/null +++ b/tools/vendor/fastrand/src/global_rng.rs @@ -0,0 +1,224 @@ +//! A global, thread-local random number generator. + +use crate::Rng; + +use std::cell::Cell; +use std::ops::RangeBounds; +use std::vec::Vec; + +// Chosen by fair roll of the dice. +const DEFAULT_RNG_SEED: u64 = 0xef6f79ed30ba75a; + +impl Default for Rng { + /// Initialize the `Rng` from the system's random number generator. + /// + /// This is equivalent to [`Rng::new()`]. + #[inline] + fn default() -> Rng { + Rng::new() + } +} + +impl Rng { + /// Creates a new random number generator. + #[inline] + pub fn new() -> Rng { + try_with_rng(Rng::fork).unwrap_or_else(|_| Rng::with_seed(0x4d595df4d0f33173)) + } +} + +std::thread_local! { + static RNG: Cell = Cell::new(Rng(random_seed().unwrap_or(DEFAULT_RNG_SEED))); +} + +/// Run an operation with the current thread-local generator. +#[inline] +fn with_rng(f: impl FnOnce(&mut Rng) -> R) -> R { + RNG.with(|rng| { + let current = rng.replace(Rng(0)); + + let mut restore = RestoreOnDrop { rng, current }; + + f(&mut restore.current) + }) +} + +/// Try to run an operation with the current thread-local generator. +#[inline] +fn try_with_rng(f: impl FnOnce(&mut Rng) -> R) -> Result { + RNG.try_with(|rng| { + let current = rng.replace(Rng(0)); + + let mut restore = RestoreOnDrop { rng, current }; + + f(&mut restore.current) + }) +} + +/// Make sure the original RNG is restored even on panic. +struct RestoreOnDrop<'a> { + rng: &'a Cell, + current: Rng, +} + +impl Drop for RestoreOnDrop<'_> { + fn drop(&mut self) { + self.rng.set(Rng(self.current.0)); + } +} + +/// Initializes the thread-local generator with the given seed. +#[inline] +pub fn seed(seed: u64) { + with_rng(|r| r.seed(seed)); +} + +/// Gives back **current** seed that is being held by the thread-local generator. +#[inline] +pub fn get_seed() -> u64 { + with_rng(|r| r.get_seed()) +} + +/// Generates a random `bool`. +#[inline] +pub fn bool() -> bool { + with_rng(|r| r.bool()) +} + +/// Generates a random `char` in ranges a-z and A-Z. +#[inline] +pub fn alphabetic() -> char { + with_rng(|r| r.alphabetic()) +} + +/// Generates a random `char` in ranges a-z, A-Z and 0-9. +#[inline] +pub fn alphanumeric() -> char { + with_rng(|r| r.alphanumeric()) +} + +/// Generates a random `char` in range a-z. +#[inline] +pub fn lowercase() -> char { + with_rng(|r| r.lowercase()) +} + +/// Generates a random `char` in range A-Z. +#[inline] +pub fn uppercase() -> char { + with_rng(|r| r.uppercase()) +} + +/// Choose an item from an iterator at random. +/// +/// This function may have an unexpected result if the `len()` property of the +/// iterator does not match the actual number of items in the iterator. If +/// the iterator is empty, this returns `None`. +#[inline] +pub fn choice(iter: I) -> Option +where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, +{ + with_rng(|r| r.choice(iter)) +} + +/// Generates a random digit in the given `base`. +/// +/// Digits are represented by `char`s in ranges 0-9 and a-z. +/// +/// Panics if the base is zero or greater than 36. +#[inline] +pub fn digit(base: u32) -> char { + with_rng(|r| r.digit(base)) +} + +/// Shuffles a slice randomly. +#[inline] +pub fn shuffle(slice: &mut [T]) { + with_rng(|r| r.shuffle(slice)) +} + +/// Fill a byte slice with random data. +#[inline] +pub fn fill(slice: &mut [u8]) { + with_rng(|r| r.fill(slice)) +} + +macro_rules! integer { + ($t:tt, $doc:tt) => { + #[doc = $doc] + /// + /// Panics if the range is empty. + #[inline] + pub fn $t(range: impl RangeBounds<$t>) -> $t { + with_rng(|r| r.$t(range)) + } + }; +} + +integer!(u8, "Generates a random `u8` in the given range."); +integer!(i8, "Generates a random `i8` in the given range."); +integer!(u16, "Generates a random `u16` in the given range."); +integer!(i16, "Generates a random `i16` in the given range."); +integer!(u32, "Generates a random `u32` in the given range."); +integer!(i32, "Generates a random `i32` in the given range."); +integer!(u64, "Generates a random `u64` in the given range."); +integer!(i64, "Generates a random `i64` in the given range."); +integer!(u128, "Generates a random `u128` in the given range."); +integer!(i128, "Generates a random `i128` in the given range."); +integer!(usize, "Generates a random `usize` in the given range."); +integer!(isize, "Generates a random `isize` in the given range."); +integer!(char, "Generates a random `char` in the given range."); + +/// Generates a random `f32` in range `0..1`. +pub fn f32() -> f32 { + with_rng(|r| r.f32()) +} + +/// Generates a random `f64` in range `0..1`. +pub fn f64() -> f64 { + with_rng(|r| r.f64()) +} + +/// Collects `amount` values at random from the iterable into a vector. +pub fn choose_multiple(source: I, amount: usize) -> Vec { + with_rng(|rng| rng.choose_multiple(source, amount)) +} + +#[cfg(not(all( + any(target_arch = "wasm32", target_arch = "wasm64"), + target_os = "unknown" +)))] +fn random_seed() -> Option { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + use std::thread; + use std::time::Instant; + + let mut hasher = DefaultHasher::new(); + Instant::now().hash(&mut hasher); + thread::current().id().hash(&mut hasher); + Some(hasher.finish()) +} + +#[cfg(all( + any(target_arch = "wasm32", target_arch = "wasm64"), + target_os = "unknown", + feature = "js" +))] +fn random_seed() -> Option { + // TODO(notgull): Failures should be logged somewhere. + let mut seed = [0u8; 8]; + getrandom::getrandom(&mut seed).ok()?; + Some(u64::from_ne_bytes(seed)) +} + +#[cfg(all( + any(target_arch = "wasm32", target_arch = "wasm64"), + target_os = "unknown", + not(feature = "js") +))] +fn random_seed() -> Option { + None +} diff --git a/tools/vendor/fastrand/src/lib.rs b/tools/vendor/fastrand/src/lib.rs new file mode 100644 index 0000000000..51ec997af7 --- /dev/null +++ b/tools/vendor/fastrand/src/lib.rs @@ -0,0 +1,689 @@ +//! A simple and fast random number generator. +//! +//! The implementation uses [Wyrand](https://github.com/wangyi-fudan/wyhash), a simple and fast +//! generator but **not** cryptographically secure. +//! +//! # Examples +//! +//! Flip a coin: +//! +//! ``` +//! if fastrand::bool() { +//! println!("heads"); +//! } else { +//! println!("tails"); +//! } +//! ``` +//! +//! Generate a random `i32`: +//! +//! ``` +//! let num = fastrand::i32(..); +//! ``` +//! +//! Choose a random element in an array: +//! +//! ``` +//! let v = vec![1, 2, 3, 4, 5]; +//! let i = fastrand::usize(..v.len()); +//! let elem = v[i]; +//! ``` +//! +//! Sample values from an array with `O(n)` complexity (`n` is the length of array): +//! +//! ``` +//! fastrand::choose_multiple([1, 4, 5], 2); +//! fastrand::choose_multiple(0..20, 12); +//! ``` +//! +//! +//! Shuffle an array: +//! +//! ``` +//! let mut v = vec![1, 2, 3, 4, 5]; +//! fastrand::shuffle(&mut v); +//! ``` +//! +//! Generate a random [`Vec`] or [`String`]: +//! +//! ``` +//! use std::iter::repeat_with; +//! +//! let v: Vec = repeat_with(|| fastrand::i32(..)).take(10).collect(); +//! let s: String = repeat_with(fastrand::alphanumeric).take(10).collect(); +//! ``` +//! +//! To get reproducible results on every run, initialize the generator with a seed: +//! +//! ``` +//! // Pick an arbitrary number as seed. +//! fastrand::seed(7); +//! +//! // Now this prints the same number on every run: +//! println!("{}", fastrand::u32(..)); +//! ``` +//! +//! To be more efficient, create a new [`Rng`] instance instead of using the thread-local +//! generator: +//! +//! ``` +//! use std::iter::repeat_with; +//! +//! let mut rng = fastrand::Rng::new(); +//! let mut bytes: Vec = repeat_with(|| rng.u8(..)).take(10_000).collect(); +//! ``` +//! +//! This crate aims to expose a core set of useful randomness primitives. For more niche algorithms, +//! consider using the [`fastrand-contrib`] crate alongside this one. +//! +//! # Features +//! +//! - `std` (enabled by default): Enables the `std` library. This is required for the global +//! generator and global entropy. Without this feature, [`Rng`] can only be instantiated using +//! the [`with_seed`](Rng::with_seed) method. +//! - `js`: Assumes that WebAssembly targets are being run in a JavaScript environment. See the +//! [WebAssembly Notes](#webassembly-notes) section for more information. +//! +//! # WebAssembly Notes +//! +//! For non-WASI WASM targets, there is additional sublety to consider when utilizing the global RNG. +//! By default, `std` targets will use entropy sources in the standard library to seed the global RNG. +//! However, these sources are not available by default on WASM targets outside of WASI. +//! +//! If the `js` feature is enabled, this crate will assume that it is running in a JavaScript +//! environment. At this point, the [`getrandom`] crate will be used in order to access the available +//! entropy sources and seed the global RNG. If the `js` feature is not enabled, the global RNG will +//! use a predefined seed. +//! +//! [`fastrand-contrib`]: https://crates.io/crates/fastrand-contrib +//! [`getrandom`]: https://crates.io/crates/getrandom + +#![no_std] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![forbid(unsafe_code)] +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] +#![doc( + html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" +)] + +#[cfg(feature = "alloc")] +extern crate alloc; +#[cfg(feature = "std")] +extern crate std; + +use core::convert::{TryFrom, TryInto}; +use core::ops::{Bound, RangeBounds}; + +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +mod global_rng; + +#[cfg(feature = "std")] +pub use global_rng::*; + +/// A random number generator. +#[derive(Debug, PartialEq, Eq)] +pub struct Rng(u64); + +impl Clone for Rng { + /// Clones the generator by creating a new generator with the same seed. + fn clone(&self) -> Rng { + Rng::with_seed(self.0) + } +} + +impl Rng { + /// Generates a random `u32`. + #[inline] + fn gen_u32(&mut self) -> u32 { + self.gen_u64() as u32 + } + + /// Generates a random `u64`. + #[inline] + fn gen_u64(&mut self) -> u64 { + // Constants for WyRand taken from: https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h#L151 + // Updated for the final v4.2 implementation with improved constants for better entropy output. + const WY_CONST_0: u64 = 0x2d35_8dcc_aa6c_78a5; + const WY_CONST_1: u64 = 0x8bb8_4b93_962e_acc9; + + let s = self.0.wrapping_add(WY_CONST_0); + self.0 = s; + let t = u128::from(s) * u128::from(s ^ WY_CONST_1); + (t as u64) ^ (t >> 64) as u64 + } + + /// Generates a random `u128`. + #[inline] + fn gen_u128(&mut self) -> u128 { + (u128::from(self.gen_u64()) << 64) | u128::from(self.gen_u64()) + } + + /// Generates a random `u32` in `0..n`. + #[inline] + fn gen_mod_u32(&mut self, n: u32) -> u32 { + // Adapted from: https://lemire.me/blog/2016/06/30/fast-random-shuffling/ + let mut r = self.gen_u32(); + let mut hi = mul_high_u32(r, n); + let mut lo = r.wrapping_mul(n); + if lo < n { + let t = n.wrapping_neg() % n; + while lo < t { + r = self.gen_u32(); + hi = mul_high_u32(r, n); + lo = r.wrapping_mul(n); + } + } + hi + } + + /// Generates a random `u64` in `0..n`. + #[inline] + fn gen_mod_u64(&mut self, n: u64) -> u64 { + // Adapted from: https://lemire.me/blog/2016/06/30/fast-random-shuffling/ + let mut r = self.gen_u64(); + let mut hi = mul_high_u64(r, n); + let mut lo = r.wrapping_mul(n); + if lo < n { + let t = n.wrapping_neg() % n; + while lo < t { + r = self.gen_u64(); + hi = mul_high_u64(r, n); + lo = r.wrapping_mul(n); + } + } + hi + } + + /// Generates a random `u128` in `0..n`. + #[inline] + fn gen_mod_u128(&mut self, n: u128) -> u128 { + // Adapted from: https://lemire.me/blog/2016/06/30/fast-random-shuffling/ + let mut r = self.gen_u128(); + let mut hi = mul_high_u128(r, n); + let mut lo = r.wrapping_mul(n); + if lo < n { + let t = n.wrapping_neg() % n; + while lo < t { + r = self.gen_u128(); + hi = mul_high_u128(r, n); + lo = r.wrapping_mul(n); + } + } + hi + } +} + +/// Computes `(a * b) >> 32`. +#[inline] +fn mul_high_u32(a: u32, b: u32) -> u32 { + (((a as u64) * (b as u64)) >> 32) as u32 +} + +/// Computes `(a * b) >> 64`. +#[inline] +fn mul_high_u64(a: u64, b: u64) -> u64 { + (((a as u128) * (b as u128)) >> 64) as u64 +} + +/// Computes `(a * b) >> 128`. +#[inline] +fn mul_high_u128(a: u128, b: u128) -> u128 { + // Adapted from: https://stackoverflow.com/a/28904636 + let a_lo = a as u64 as u128; + let a_hi = (a >> 64) as u64 as u128; + let b_lo = b as u64 as u128; + let b_hi = (b >> 64) as u64 as u128; + let carry = (a_lo * b_lo) >> 64; + let carry = ((a_hi * b_lo) as u64 as u128 + (a_lo * b_hi) as u64 as u128 + carry) >> 64; + a_hi * b_hi + ((a_hi * b_lo) >> 64) + ((a_lo * b_hi) >> 64) + carry +} + +macro_rules! rng_integer { + ($t:tt, $unsigned_t:tt, $gen:tt, $mod:tt, $doc:tt) => { + #[doc = $doc] + /// + /// Panics if the range is empty. + #[inline] + pub fn $t(&mut self, range: impl RangeBounds<$t>) -> $t { + let panic_empty_range = || { + panic!( + "empty range: {:?}..{:?}", + range.start_bound(), + range.end_bound() + ) + }; + + let low = match range.start_bound() { + Bound::Unbounded => core::$t::MIN, + Bound::Included(&x) => x, + Bound::Excluded(&x) => x.checked_add(1).unwrap_or_else(panic_empty_range), + }; + + let high = match range.end_bound() { + Bound::Unbounded => core::$t::MAX, + Bound::Included(&x) => x, + Bound::Excluded(&x) => x.checked_sub(1).unwrap_or_else(panic_empty_range), + }; + + if low > high { + panic_empty_range(); + } + + if low == core::$t::MIN && high == core::$t::MAX { + self.$gen() as $t + } else { + let len = high.wrapping_sub(low).wrapping_add(1); + low.wrapping_add(self.$mod(len as $unsigned_t as _) as $t) + } + } + }; +} + +impl Rng { + /// Creates a new random number generator with the initial seed. + #[inline] + #[must_use = "this creates a new instance of `Rng`; if you want to initialize the thread-local generator, use `fastrand::seed()` instead"] + pub fn with_seed(seed: u64) -> Self { + Rng(seed) + } + + /// Clones the generator by deterministically deriving a new generator based on the initial + /// seed. + /// + /// This function can be used to create a new generator that is a "spinoff" of the old + /// generator. The new generator will not produce the same sequence of values as the + /// old generator. + /// + /// # Example + /// + /// ``` + /// // Seed two generators equally, and clone both of them. + /// let mut base1 = fastrand::Rng::with_seed(0x4d595df4d0f33173); + /// base1.bool(); // Use the generator once. + /// + /// let mut base2 = fastrand::Rng::with_seed(0x4d595df4d0f33173); + /// base2.bool(); // Use the generator once. + /// + /// let mut rng1 = base1.fork(); + /// let mut rng2 = base2.fork(); + /// + /// println!("rng1 returns {}", rng1.u32(..)); + /// println!("rng2 returns {}", rng2.u32(..)); + /// ``` + #[inline] + #[must_use = "this creates a new instance of `Rng`"] + pub fn fork(&mut self) -> Self { + Rng::with_seed(self.gen_u64()) + } + + /// Generates a random `char` in ranges a-z and A-Z. + #[inline] + pub fn alphabetic(&mut self) -> char { + const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + *self.choice(CHARS).unwrap() as char + } + + /// Generates a random `char` in ranges a-z, A-Z and 0-9. + #[inline] + pub fn alphanumeric(&mut self) -> char { + const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + *self.choice(CHARS).unwrap() as char + } + + /// Generates a random `bool`. + #[inline] + pub fn bool(&mut self) -> bool { + self.u8(..) % 2 == 0 + } + + /// Generates a random digit in the given `base`. + /// + /// Digits are represented by `char`s in ranges 0-9 and a-z. + /// + /// Panics if the base is zero or greater than 36. + #[inline] + pub fn digit(&mut self, base: u32) -> char { + if base == 0 { + panic!("base cannot be zero"); + } + if base > 36 { + panic!("base cannot be larger than 36"); + } + let num = self.u8(..base as u8); + if num < 10 { + (b'0' + num) as char + } else { + (b'a' + num - 10) as char + } + } + + /// Generates a random `f32` in range `0..1`. + pub fn f32(&mut self) -> f32 { + let b = 32; + let f = core::f32::MANTISSA_DIGITS - 1; + f32::from_bits((1 << (b - 2)) - (1 << f) + (self.u32(..) >> (b - f))) - 1.0 + } + + /// Generates a random `f64` in range `0..1`. + pub fn f64(&mut self) -> f64 { + let b = 64; + let f = core::f64::MANTISSA_DIGITS - 1; + f64::from_bits((1 << (b - 2)) - (1 << f) + (self.u64(..) >> (b - f))) - 1.0 + } + + /// Collects `amount` values at random from the iterable into a vector. + /// + /// The length of the returned vector equals `amount` unless the iterable + /// contains insufficient elements, in which case it equals the number of + /// elements available. + /// + /// Complexity is `O(n)` where `n` is the length of the iterable. + #[cfg(feature = "alloc")] + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] + pub fn choose_multiple(&mut self, source: I, amount: usize) -> Vec { + // Adapted from: https://docs.rs/rand/latest/rand/seq/trait.IteratorRandom.html#method.choose_multiple + let mut reservoir = Vec::with_capacity(amount); + let mut iter = source.into_iter(); + + reservoir.extend(iter.by_ref().take(amount)); + + // Continue unless the iterator was exhausted + // + // note: this prevents iterators that "restart" from causing problems. + // If the iterator stops once, then so do we. + if reservoir.len() == amount { + for (i, elem) in iter.enumerate() { + let end = i + 1 + amount; + let k = self.usize(0..end); + if let Some(slot) = reservoir.get_mut(k) { + *slot = elem; + } + } + } else { + // If less than one third of the `Vec` was used, reallocate + // so that the unused space is not wasted. There is a corner + // case where `amount` was much less than `self.len()`. + if reservoir.capacity() > 3 * reservoir.len() { + reservoir.shrink_to_fit(); + } + } + reservoir + } + + rng_integer!( + i8, + u8, + gen_u32, + gen_mod_u32, + "Generates a random `i8` in the given range." + ); + + rng_integer!( + i16, + u16, + gen_u32, + gen_mod_u32, + "Generates a random `i16` in the given range." + ); + + rng_integer!( + i32, + u32, + gen_u32, + gen_mod_u32, + "Generates a random `i32` in the given range." + ); + + rng_integer!( + i64, + u64, + gen_u64, + gen_mod_u64, + "Generates a random `i64` in the given range." + ); + + rng_integer!( + i128, + u128, + gen_u128, + gen_mod_u128, + "Generates a random `i128` in the given range." + ); + + #[cfg(target_pointer_width = "16")] + rng_integer!( + isize, + usize, + gen_u32, + gen_mod_u32, + "Generates a random `isize` in the given range." + ); + #[cfg(target_pointer_width = "32")] + rng_integer!( + isize, + usize, + gen_u32, + gen_mod_u32, + "Generates a random `isize` in the given range." + ); + #[cfg(target_pointer_width = "64")] + rng_integer!( + isize, + usize, + gen_u64, + gen_mod_u64, + "Generates a random `isize` in the given range." + ); + + /// Generates a random `char` in range a-z. + #[inline] + pub fn lowercase(&mut self) -> char { + const CHARS: &[u8] = b"abcdefghijklmnopqrstuvwxyz"; + *self.choice(CHARS).unwrap() as char + } + + /// Initializes this generator with the given seed. + #[inline] + pub fn seed(&mut self, seed: u64) { + self.0 = seed; + } + + /// Gives back **current** seed that is being held by this generator. + #[inline] + pub fn get_seed(&self) -> u64 { + self.0 + } + + /// Choose an item from an iterator at random. + /// + /// This function may have an unexpected result if the `len()` property of the + /// iterator does not match the actual number of items in the iterator. If + /// the iterator is empty, this returns `None`. + #[inline] + pub fn choice(&mut self, iter: I) -> Option + where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, + { + let mut iter = iter.into_iter(); + + // Get the item at a random index. + let len = iter.len(); + if len == 0 { + return None; + } + let index = self.usize(0..len); + + iter.nth(index) + } + + /// Shuffles a slice randomly. + #[inline] + pub fn shuffle(&mut self, slice: &mut [T]) { + for i in 1..slice.len() { + slice.swap(i, self.usize(..=i)); + } + } + + /// Fill a byte slice with random data. + #[inline] + pub fn fill(&mut self, slice: &mut [u8]) { + // We fill the slice by chunks of 8 bytes, or one block of + // WyRand output per new state. + let mut chunks = slice.chunks_exact_mut(core::mem::size_of::()); + for chunk in chunks.by_ref() { + let n = self.gen_u64().to_ne_bytes(); + // Safe because the chunks are always 8 bytes exactly. + chunk.copy_from_slice(&n); + } + + let remainder = chunks.into_remainder(); + + // Any remainder will always be less than 8 bytes. + if !remainder.is_empty() { + // Generate one last block of 8 bytes of entropy + let n = self.gen_u64().to_ne_bytes(); + + // Use the remaining length to copy from block + remainder.copy_from_slice(&n[..remainder.len()]); + } + } + + rng_integer!( + u8, + u8, + gen_u32, + gen_mod_u32, + "Generates a random `u8` in the given range." + ); + + rng_integer!( + u16, + u16, + gen_u32, + gen_mod_u32, + "Generates a random `u16` in the given range." + ); + + rng_integer!( + u32, + u32, + gen_u32, + gen_mod_u32, + "Generates a random `u32` in the given range." + ); + + rng_integer!( + u64, + u64, + gen_u64, + gen_mod_u64, + "Generates a random `u64` in the given range." + ); + + rng_integer!( + u128, + u128, + gen_u128, + gen_mod_u128, + "Generates a random `u128` in the given range." + ); + + #[cfg(target_pointer_width = "16")] + rng_integer!( + usize, + usize, + gen_u32, + gen_mod_u32, + "Generates a random `usize` in the given range." + ); + #[cfg(target_pointer_width = "32")] + rng_integer!( + usize, + usize, + gen_u32, + gen_mod_u32, + "Generates a random `usize` in the given range." + ); + #[cfg(target_pointer_width = "64")] + rng_integer!( + usize, + usize, + gen_u64, + gen_mod_u64, + "Generates a random `usize` in the given range." + ); + + /// Generates a random `char` in range A-Z. + #[inline] + pub fn uppercase(&mut self) -> char { + const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + *self.choice(CHARS).unwrap() as char + } + + /// Generates a random `char` in the given range. + /// + /// Panics if the range is empty. + #[inline] + pub fn char(&mut self, range: impl RangeBounds) -> char { + let panic_empty_range = || { + panic!( + "empty range: {:?}..{:?}", + range.start_bound(), + range.end_bound() + ) + }; + + let surrogate_start = 0xd800u32; + let surrogate_len = 0x800u32; + + let low = match range.start_bound() { + Bound::Unbounded => 0u8 as char, + Bound::Included(&x) => x, + Bound::Excluded(&x) => { + let scalar = if x as u32 == surrogate_start - 1 { + surrogate_start + surrogate_len + } else { + x as u32 + 1 + }; + char::try_from(scalar).unwrap_or_else(|_| panic_empty_range()) + } + }; + + let high = match range.end_bound() { + Bound::Unbounded => core::char::MAX, + Bound::Included(&x) => x, + Bound::Excluded(&x) => { + let scalar = if x as u32 == surrogate_start + surrogate_len { + surrogate_start - 1 + } else { + (x as u32).wrapping_sub(1) + }; + char::try_from(scalar).unwrap_or_else(|_| panic_empty_range()) + } + }; + + if low > high { + panic_empty_range(); + } + + let gap = if (low as u32) < surrogate_start && (high as u32) >= surrogate_start { + surrogate_len + } else { + 0 + }; + let range = high as u32 - low as u32 - gap; + let mut val = self.u32(0..=range) + low as u32; + if val >= surrogate_start { + val += gap; + } + val.try_into().unwrap() + } +} diff --git a/tools/vendor/fastrand/tests/char.rs b/tools/vendor/fastrand/tests/char.rs new file mode 100644 index 0000000000..0f48c57708 --- /dev/null +++ b/tools/vendor/fastrand/tests/char.rs @@ -0,0 +1,44 @@ +use std::convert::TryFrom; +use std::ops::RangeBounds; + +fn test_char_coverage(n: usize, range: R) +where + R: Iterator + RangeBounds + Clone, +{ + use std::collections::HashSet; + + let all: HashSet = range.clone().collect(); + let mut covered = HashSet::new(); + for _ in 0..n { + let c = fastrand::char(range.clone()); + assert!(all.contains(&c)); + covered.insert(c); + } + assert_eq!(covered, all); +} + +#[test] +fn test_char() { + // ASCII control chars. + let nul = 0u8 as char; + let soh = 1u8 as char; + let stx = 2u8 as char; + // Some undefined Hangul Jamo codepoints just before + // the surrogate area. + let last_jamo = char::try_from(0xd7ffu32).unwrap(); + let penultimate_jamo = char::try_from(last_jamo as u32 - 1).unwrap(); + // Private-use codepoints just after the surrogate area. + let first_private = char::try_from(0xe000u32).unwrap(); + let second_private = char::try_from(first_private as u32 + 1).unwrap(); + // Private-use codepoints at the end of Unicode space. + let last_private = std::char::MAX; + let penultimate_private = char::try_from(last_private as u32 - 1).unwrap(); + + test_char_coverage(100, nul..stx); + test_char_coverage(100, nul..=soh); + + test_char_coverage(400, penultimate_jamo..second_private); + test_char_coverage(400, penultimate_jamo..=second_private); + + test_char_coverage(100, penultimate_private..=last_private); +} diff --git a/tools/vendor/fastrand/tests/smoke.rs b/tools/vendor/fastrand/tests/smoke.rs new file mode 100644 index 0000000000..7c92ee52ef --- /dev/null +++ b/tools/vendor/fastrand/tests/smoke.rs @@ -0,0 +1,143 @@ +#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +use wasm_bindgen_test::wasm_bindgen_test as test; + +#[cfg(all(target_family = "wasm", not(target_os = "wasi")))] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + +#[test] +fn bool() { + for x in &[false, true] { + while fastrand::bool() != *x {} + } +} + +#[test] +fn u8() { + for x in 0..10 { + while fastrand::u8(..10) != x {} + } + + for x in 200..=u8::MAX { + while fastrand::u8(200..) != x {} + } +} + +#[test] +fn i8() { + for x in -128..-120 { + while fastrand::i8(..-120) != x {} + } + + for x in 120..=127 { + while fastrand::i8(120..) != x {} + } +} + +#[test] +fn u32() { + for n in 1u32..10_000 { + let n = n.wrapping_mul(n); + let n = n.wrapping_mul(n); + if n != 0 { + for _ in 0..1000 { + assert!(fastrand::u32(..n) < n); + } + } + } +} + +#[test] +fn u64() { + for n in 1u64..10_000 { + let n = n.wrapping_mul(n); + let n = n.wrapping_mul(n); + let n = n.wrapping_mul(n); + if n != 0 { + for _ in 0..1000 { + assert!(fastrand::u64(..n) < n); + } + } + } +} + +#[test] +fn u128() { + for n in 1u128..10_000 { + let n = n.wrapping_mul(n); + let n = n.wrapping_mul(n); + let n = n.wrapping_mul(n); + let n = n.wrapping_mul(n); + if n != 0 { + for _ in 0..1000 { + assert!(fastrand::u128(..n) < n); + } + } + } +} + +#[test] +fn fill() { + let mut r = fastrand::Rng::new(); + let mut a = [0u8; 64]; + let mut b = [0u8; 64]; + + r.fill(&mut a); + r.fill(&mut b); + + assert_ne!(a, b); +} + +#[test] +fn rng() { + let mut r = fastrand::Rng::new(); + + assert_ne!(r.u64(..), r.u64(..)); + + r.seed(7); + let a = r.u64(..); + r.seed(7); + let b = r.u64(..); + assert_eq!(a, b); +} + +#[test] +fn rng_init() { + let mut a = fastrand::Rng::new(); + let mut b = fastrand::Rng::new(); + assert_ne!(a.u64(..), b.u64(..)); + + a.seed(7); + b.seed(7); + assert_eq!(a.u64(..), b.u64(..)); +} + +#[test] +fn with_seed() { + let mut a = fastrand::Rng::with_seed(7); + let mut b = fastrand::Rng::new(); + b.seed(7); + assert_eq!(a.u64(..), b.u64(..)); +} + +#[test] +fn choose_multiple() { + let mut a = fastrand::Rng::new(); + let mut elements = (0..20).collect::>(); + + while !elements.is_empty() { + let chosen = a.choose_multiple(0..20, 5); + for &x in &chosen { + elements.retain(|&y| y != x); + } + } +} + +#[test] +fn choice() { + let items = [1, 4, 9, 5, 2, 3, 6, 7, 8, 0]; + let mut r = fastrand::Rng::new(); + + for item in &items { + while r.choice(&items).unwrap() != item {} + } +} diff --git a/tools/vendor/float-cmp/.cargo-checksum.json b/tools/vendor/float-cmp/.cargo-checksum.json new file mode 100644 index 0000000000..da52c2711c --- /dev/null +++ b/tools/vendor/float-cmp/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"6782fa1f5fb6dbf01c2e0f4b098399223c80dbcf1aefb05e82915b9b7332b3d9",".travis.yml":"80a9791cd3fbc359c1acafd79f867edfa2ce3852c3264ff26c93a975070fdb5b","Cargo.toml":"b5a46b715d8b448cfe1079ab15691faff54d64e1f41803ed4c9bc6d255f3afde","Cargo.toml.orig":"dccf72ab6ada2a9640933e6d01af1d3da52963acda5c2e9544aeba6399fb2198","LICENSE":"40be1e77825d7e49485a2e43d89bed29dfff29f8f529e71d3c683656021f0d08","README.md":"bed2395efa694cb1d74092e5614125ba4668201f0c653fe71d0fd567057e5c9b","src/eq.rs":"d66dc620d7911e7e0ee219dc78699094b15f496fb76eb1b0138def67a40a78e6","src/lib.rs":"184244a77ca67047729c7c3214c4e41b34b6b16734ba659606ad5e529bd26286","src/macros.rs":"fa0596b888cbf6609d867b9abb101e6cddda3a80308e9525ab20b6990cb36786","src/ratio.rs":"4246c51d5de4a45bf56c34c3df743e511c20bb676448ff76cb122a5b6781ba26","src/ulps.rs":"eb1376b5de7c9182a9ea8b5f255963f167de39953ad8fdf5790bf8e105cdc528","src/ulps_eq.rs":"3e85bb470143893bd2bb79de5c5172c38f4a9364ff9ebcf91240b9e316876721"},"package":"b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8"} \ No newline at end of file diff --git a/tools/vendor/float-cmp/.cargo_vcs_info.json b/tools/vendor/float-cmp/.cargo_vcs_info.json new file mode 100644 index 0000000000..e36f4e7f9e --- /dev/null +++ b/tools/vendor/float-cmp/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "493f312acf68c860dd6137cc375b41d48916efde" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/tools/vendor/float-cmp/.travis.yml b/tools/vendor/float-cmp/.travis.yml new file mode 100644 index 0000000000..c51e71fc46 --- /dev/null +++ b/tools/vendor/float-cmp/.travis.yml @@ -0,0 +1,2 @@ +language: rust +rust: stable diff --git a/tools/vendor/float-cmp/Cargo.toml b/tools/vendor/float-cmp/Cargo.toml new file mode 100644 index 0000000000..c6e0a92318 --- /dev/null +++ b/tools/vendor/float-cmp/Cargo.toml @@ -0,0 +1,50 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "float-cmp" +version = "0.10.0" +authors = ["Mike Dilger "] +build = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Floating point approximate comparison traits" +documentation = "https://docs.rs/float-cmp" +readme = "README.md" +keywords = [ + "float", + "comparison", + "fuzzy", + "approximate", + "no_std", +] +license = "MIT" +repository = "https://github.com/mikedilger/float-cmp" + +[lib] +name = "float_cmp" +path = "src/lib.rs" +test = true +doctest = true +doc = true + +[dependencies.num-traits] +version = "0.2.1" +optional = true +default-features = false + +[features] +default = ["ratio"] +ratio = ["num-traits"] +std = [] diff --git a/tools/vendor/float-cmp/Cargo.toml.orig b/tools/vendor/float-cmp/Cargo.toml.orig new file mode 100644 index 0000000000..a60d29c66a --- /dev/null +++ b/tools/vendor/float-cmp/Cargo.toml.orig @@ -0,0 +1,26 @@ +[package] +name = "float-cmp" +version = "0.10.0" +authors = ["Mike Dilger "] +description = "Floating point approximate comparison traits" +repository = "https://github.com/mikedilger/float-cmp" +documentation = "https://docs.rs/float-cmp" +readme = "README.md" +keywords = [ "float", "comparison", "fuzzy", "approximate", "no_std" ] +license = "MIT" +edition = "2018" + +[lib] +name = "float_cmp" +path = "src/lib.rs" +test = true +doctest = true +doc = true + +[features] +default = [ "ratio" ] +ratio = [ "num-traits" ] +std = [ ] + +[dependencies] +num-traits = { version = "0.2.1", default-features = false, optional = true } diff --git a/tools/vendor/float-cmp/LICENSE b/tools/vendor/float-cmp/LICENSE new file mode 100644 index 0000000000..e2e600a0eb --- /dev/null +++ b/tools/vendor/float-cmp/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-2020 Optimal Computing (NZ) Ltd + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/tools/vendor/float-cmp/README.md b/tools/vendor/float-cmp/README.md new file mode 100644 index 0000000000..6fd4c6c38b --- /dev/null +++ b/tools/vendor/float-cmp/README.md @@ -0,0 +1,148 @@ +# float-cmp + +[![Build Status](https://travis-ci.org/mikedilger/float-cmp.svg?branch=master)](https://travis-ci.org/mikedilger/float-cmp) +[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) + +Documentation is available at https://docs.rs/float-cmp + +float-cmp defines and implements traits for approximate comparison of floating point types +which have fallen away from exact equality due to the limited precision available within +floating point representations. Implementations of these traits are provided for `f32` +and `f64` types. + +When I was a kid in the '80s, the programming rule was "Never compare floating point +numbers". If you can follow that rule and still get the outcome you desire, then more +power to you. However, if you really do need to compare them, this crate provides a +reasonable way to do so. + +Another crate `efloat` offers another solution by providing a floating point type that +tracks its error bounds as operations are performed on it, and thus can implement the +`ApproxEq` trait in this crate more accurately, without specifying a `Margin`. + +The recommended go-to solution (although it may not be appropriate in all cases) is the +`approx_eq()` function in the `ApproxEq` trait (or better yet, the macros). For `f32` +and `f64`, the `F32Margin` and `F64Margin` types are provided for specifying margins as +both an epsilon value and an ULPs value, and defaults are provided via `Default` +(although there is no perfect default value that is always appropriate, so beware). + +Several other traits are provided including `Ulps`, `ApproxEqUlps`, `ApproxOrdUlps`, and +`ApproxEqRatio`. + +## The problem + +Floating point operations must round answers to the nearest representable number. Multiple +operations may result in an answer different from what you expect. In the following example, +the assert will fail, even though the printed output says "0.45 == 0.45": + +```rust + let a: f32 = 0.15 + 0.15 + 0.15; + let b: f32 = 0.1 + 0.1 + 0.25; + println!("{} == {}", a, b); + assert!(a==b) // Fails, because they are not exactly equal +``` + +This fails because the correct answer to most operations isn't exactly representable, and so +your computer's processor chooses to represent the answer with the closest value it has +available. This introduces error, and this error can accumulate as multiple operations are +performed. + +## The solution + +With `ApproxEq`, we can get the answer we intend: +```rust + let a: f32 = 0.15 + 0.15 + 0.15; + let b: f32 = 0.1 + 0.1 + 0.25; + println!("{} == {}", a, b); + assert!( approx_eq!(f32, a, b, ulps = 2) ); +``` + +## Some explanation + +We use the term ULP (units of least precision, or units in the last place) to mean the +difference between two adjacent floating point representations (adjacent meaning that there is +no floating point number between them). This term is borrowed from prior work (personally I +would have chosen "quanta"). The size of an ULP (measured as a float) varies +depending on the exponents of the floating point numbers in question. That is a good thing, +because as numbers fall away from equality due to the imprecise nature of their representation, +they fall away in ULPs terms, not in absolute terms. Pure epsilon-based comparisons are +absolute and thus don't map well to the nature of the additive error issue. They work fine +for many ranges of numbers, but not for others (consider comparing -0.0000000028 +to +0.00000097). + +## Using this crate + +By default this crate enables the `ratio` module providing the `ApproxEqRatio` trait. This +feature pulls in `num-traits`. If you disable this feature, you'll need to either enable +`num-traits` directly or else enable the `std` feature; otherwise it won't compile. This crate +is `#![no_std]` unless you enable the `std` feature. + +You can use the `ApproxEq` trait directly like so: + +```rust + assert!( a.approx_eq(b, F32Margin { ulps: 2, epsilon: 0.0 }) ); +``` + +We have implemented `From<(f32,i32)>` for `F32Margin` (and similarly for `F64Margin`) +so you can use this shorthand: + +```rust + assert!( a.approx_eq(b, (0.0, 2)) ); +``` + +With macros, it is easier to be explicit about which type of margin you wish to set, +without mentioning the other one (the other one will be zero). But the downside is +that you have to specify the type you are dealing with: + +```rust + assert!( approx_eq!(f32, a, b, ulps = 2) ); + assert!( approx_eq!(f32, a, b, epsilon = 0.00000003) ); + assert!( approx_eq!(f32, a, b, epsilon = 0.00000003, ulps = 2) ); + assert!( approx_eq!(f32, a, b, (0.0, 2)) ); + assert!( approx_eq!(f32, a, b, F32Margin { epsilon: 0.0, ulps: 2 }) ); + assert!( approx_eq!(f32, a, b, F32Margin::default()) ); + assert!( approx_eq!(f32, a, b) ); // uses the default +``` + +For most cases, I recommend you use a smallish integer for the `ulps` parameter (1 to 5 +or so), and a similar small multiple of the floating point's EPSILON constant (1.0 to 5.0 +or so), but there are *plenty* of cases where this is insufficient. + +## Implementing these traits + +You can implement `ApproxEq` for your own complex types like shown below. +The floating point type `F` must be `Copy`, but for large types you can implement +it for references to your type as shown. + +```rust +use float_cmp::ApproxEq; + +pub struct Vec2 { + pub x: F, + pub y: F, +} + +impl<'a, M: Copy, F: Copy + ApproxEq> ApproxEq for &'a Vec2 { + type Margin = M; + + fn approx_eq>(self, other: Self, margin: T) -> bool { + let margin = margin.into(); + self.x.approx_eq(other.x, margin) + && self.y.approx_eq(other.y, margin) + } +} +``` + +## Non floating-point types + +`ApproxEq` can be implemented for non floating-point types as well, since `Margin` is +an associated type. + +The `efloat` crate implements (or soon will implement) `ApproxEq` for a compound type +that tracks floating point error bounds by checking if the error bounds overlap. +In that case `type Margin = ()`. + +## Inspiration + +This crate was inspired by this Random ASCII blog post: + +[https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/) diff --git a/tools/vendor/float-cmp/src/eq.rs b/tools/vendor/float-cmp/src/eq.rs new file mode 100644 index 0000000000..5b6e799e29 --- /dev/null +++ b/tools/vendor/float-cmp/src/eq.rs @@ -0,0 +1,360 @@ +// Copyright 2014-2020 Optimal Computing (NZ) Ltd. +// Licensed under the MIT license. See LICENSE for details. + +use super::Ulps; +use core::{f32, f64}; +#[cfg(feature = "num-traits")] +#[allow(unused_imports)] +use num_traits::float::FloatCore; + +/// A margin specifying a maximum distance two floating point values can be while +/// still being considered equal enough. +pub trait FloatMargin: Copy + Default { + /// A floating-point type used for epsilon values + type F; + + /// An integer type used for ulps values + type I; + + /// Zero margin + fn zero() -> Self; + + /// Set the epsilon value for this margin + fn epsilon(self, epsilon: Self::F) -> Self; + + /// Set the ulps value for this margin + fn ulps(self, ulps: Self::I) -> Self; +} + +/// A trait for approximate equality comparisons. +pub trait ApproxEq: Sized { + /// This type type defines a margin within which two values are to be + /// considered approximately equal. It must implement `Default` so that + /// `approx_eq()` can be called on unknown types. + type Margin: FloatMargin; + + /// This method tests that the `self` and `other` values are equal within `margin` + /// of each other. + fn approx_eq>(self, other: Self, margin: M) -> bool; + + /// This method tests that the `self` and `other` values are not within `margin` + /// of each other. + fn approx_ne>(self, other: Self, margin: M) -> bool { + !self.approx_eq(other, margin) + } +} + +/// This type defines a margin within two `f32` values might be considered equal, +/// and is intended as the associated type for the `ApproxEq` trait. +/// +/// Two tests are used to determine approximate equality. +/// +/// The first test considers two values approximately equal if they differ by <= +/// `epsilon`. This will only succeed for very small numbers. Note that it may +/// succeed even if the parameters are of differing signs, straddling zero. +/// +/// The second test considers how many ULPs (units of least precision, units in +/// the last place, which is the integer number of floating-point representations +/// that the parameters are separated by) different the parameters are and considers +/// them approximately equal if this is <= `ulps`. For large floating-point numbers, +/// an ULP can be a rather large gap, but this kind of comparison is necessary +/// because floating-point operations must round to the nearest representable value +/// and so larger floating-point values accumulate larger errors. +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct F32Margin { + pub epsilon: f32, + pub ulps: i32, +} +impl Default for F32Margin { + #[inline] + fn default() -> F32Margin { + F32Margin { + epsilon: f32::EPSILON, + ulps: 4, + } + } +} +impl FloatMargin for F32Margin { + type F = f32; + type I = i32; + + #[inline] + fn zero() -> F32Margin { + F32Margin { + epsilon: 0.0, + ulps: 0, + } + } + fn epsilon(self, epsilon: f32) -> Self { + F32Margin { epsilon, ..self } + } + fn ulps(self, ulps: i32) -> Self { + F32Margin { ulps, ..self } + } +} +impl From<(f32, i32)> for F32Margin { + fn from(m: (f32, i32)) -> F32Margin { + F32Margin { + epsilon: m.0, + ulps: m.1, + } + } +} + +// no-std compatible abs function +#[inline(always)] +fn f32abs(x: f32) -> f32 { + f32::from_bits(x.to_bits() & !(1 << 31)) +} + +impl ApproxEq for f32 { + type Margin = F32Margin; + + fn approx_eq>(self, other: f32, margin: M) -> bool { + let margin = margin.into(); + + // Check for exact equality first. This is often true, and so we get the + // performance benefit of only doing one compare in most cases. + self == other || { + // Perform epsilon comparison next + let eps = f32abs(self - other); + (eps <= margin.epsilon) || { + // Perform ulps comparison last + let diff: i32 = self.ulps(&other); + saturating_abs_i32!(diff) <= margin.ulps + } + } + } +} + +#[test] +fn f32_approx_eq_test1() { + let f: f32 = 0.0_f32; + let g: f32 = -0.0000000000000005551115123125783_f32; + assert!(f != g); // Should not be directly equal + assert!(f.approx_eq(g, (f32::EPSILON, 0)) == true); +} +#[test] +fn f32_approx_eq_test2() { + let f: f32 = 0.0_f32; + let g: f32 = -0.0_f32; + assert!(f.approx_eq(g, (f32::EPSILON, 0)) == true); +} +#[test] +fn f32_approx_eq_test3() { + let f: f32 = 0.0_f32; + let g: f32 = 0.00000000000000001_f32; + assert!(f.approx_eq(g, (f32::EPSILON, 0)) == true); +} +#[test] +fn f32_approx_eq_test4() { + let f: f32 = 0.00001_f32; + let g: f32 = 0.00000000000000001_f32; + assert!(f.approx_eq(g, (f32::EPSILON, 0)) == false); +} +#[test] +fn f32_approx_eq_test5() { + let f: f32 = 0.1_f32; + let mut sum: f32 = 0.0_f32; + for _ in 0_isize..10_isize { + sum += f; + } + let product: f32 = f * 10.0_f32; + assert!(sum != product); // Should not be directly equal: + assert!(sum.approx_eq(product, (f32::EPSILON, 1)) == true); + assert!(sum.approx_eq(product, F32Margin::zero()) == false); +} +#[test] +fn f32_approx_eq_test6() { + let x: f32 = 1000000_f32; + let y: f32 = 1000000.1_f32; + assert!(x != y); // Should not be directly equal + assert!(x.approx_eq(y, (0.0, 2)) == true); // 2 ulps does it + // epsilon method no good here: + assert!(x.approx_eq(y, (1000.0 * f32::EPSILON, 0)) == false); +} + +/// This type defines a margin within two `f64` values might be considered equal, +/// and is intended as the associated type for the `ApproxEq` trait. +/// +/// Two tests are used to determine approximate equality. +/// +/// The first test considers two values approximately equal if they differ by <= +/// `epsilon`. This will only succeed for very small numbers. Note that it may +/// succeed even if the parameters are of differing signs, straddling zero. +/// +/// The second test considers how many ULPs (units of least precision, units in +/// the last place, which is the integer number of floating-point representations +/// that the parameters are separated by) different the parameters are and considers +/// them approximately equal if this is <= `ulps`. For large floating-point numbers, +/// an ULP can be a rather large gap, but this kind of comparison is necessary +/// because floating-point operations must round to the nearest representable value +/// and so larger floating-point values accumulate larger errors. +#[derive(Debug, Clone, Copy)] +pub struct F64Margin { + pub epsilon: f64, + pub ulps: i64, +} +impl Default for F64Margin { + #[inline] + fn default() -> F64Margin { + F64Margin { + epsilon: f64::EPSILON, + ulps: 4, + } + } +} +impl FloatMargin for F64Margin { + type F = f64; + type I = i64; + + #[inline] + fn zero() -> F64Margin { + F64Margin { + epsilon: 0.0, + ulps: 0, + } + } + fn epsilon(self, epsilon: f64) -> Self { + F64Margin { epsilon, ..self } + } + fn ulps(self, ulps: i64) -> Self { + F64Margin { ulps, ..self } + } +} +impl From<(f64, i64)> for F64Margin { + fn from(m: (f64, i64)) -> F64Margin { + F64Margin { + epsilon: m.0, + ulps: m.1, + } + } +} + +// no-std compatible abs function +#[inline(always)] +fn f64abs(x: f64) -> f64 { + f64::from_bits(x.to_bits() & !(1 << 63)) +} + +impl ApproxEq for f64 { + type Margin = F64Margin; + + fn approx_eq>(self, other: f64, margin: M) -> bool { + let margin = margin.into(); + + // Check for exact equality first. This is often true, and so we get the + // performance benefit of only doing one compare in most cases. + self == other || { + // Perform epsilon comparison next + let eps = f64abs(self - other); + (eps <= margin.epsilon) || { + // Perform ulps comparison last + let diff: i64 = self.ulps(&other); + saturating_abs_i64!(diff) <= margin.ulps + } + } + } +} + +#[test] +fn f64_approx_eq_test1() { + let f: f64 = 0.0_f64; + let g: f64 = -0.0000000000000005551115123125783_f64; + assert!(f != g); // Should not be precisely equal. + assert!(f.approx_eq(g, (3.0 * f64::EPSILON, 0)) == true); // 3e is enough. + // ULPs test won't ever call these equal. +} +#[test] +fn f64_approx_eq_test2() { + let f: f64 = 0.0_f64; + let g: f64 = -0.0_f64; + assert!(f.approx_eq(g, (f64::EPSILON, 0)) == true); +} +#[test] +fn f64_approx_eq_test3() { + let f: f64 = 0.0_f64; + let g: f64 = 1e-17_f64; + assert!(f.approx_eq(g, (f64::EPSILON, 0)) == true); +} +#[test] +fn f64_approx_eq_test4() { + let f: f64 = 0.00001_f64; + let g: f64 = 0.00000000000000001_f64; + assert!(f.approx_eq(g, (f64::EPSILON, 0)) == false); +} +#[test] +fn f64_approx_eq_test5() { + let f: f64 = 0.1_f64; + let mut sum: f64 = 0.0_f64; + for _ in 0_isize..10_isize { + sum += f; + } + let product: f64 = f * 10.0_f64; + assert!(sum != product); // Should not be precisely equally. + assert!(sum.approx_eq(product, (f64::EPSILON, 0)) == true); + assert!(sum.approx_eq(product, (0.0, 1)) == true); +} +#[test] +fn f64_approx_eq_test6() { + let x: f64 = 1000000_f64; + let y: f64 = 1000000.0000000003_f64; + assert!(x != y); // Should not be precisely equal. + assert!(x.approx_eq(y, (0.0, 3)) == true); +} +#[test] +fn f64_code_triggering_issue_20() { + assert_eq!((-25.0f64).approx_eq(25.0, (0.00390625, 1)), false); +} + +impl ApproxEq for &[T] +where + T: Copy + ApproxEq, +{ + type Margin = ::Margin; + + fn approx_eq>(self, other: Self, margin: M) -> bool { + let margin = margin.into(); + if self.len() != other.len() { + return false; + } + self.iter() + .zip(other.iter()) + .all(|(a, b)| a.approx_eq(*b, margin)) + } +} + +#[test] +fn test_slices() { + assert!([1.33, 2.4, 2.5].approx_eq(&[1.33, 2.4, 2.5], (0.0, 0_i64))); + assert!(![1.33, 2.4, 2.6].approx_eq(&[1.33, 2.4, 2.5], (0.0, 0_i64))); + assert!(![1.33, 2.4].approx_eq(&[1.33, 2.4, 2.5], (0.0, 0_i64))); + assert!(![1.33, 2.4, 2.5].approx_eq(&[1.33, 2.4], (0.0, 0_i64))); +} + +impl ApproxEq for Option +where + T: Copy + ApproxEq, +{ + type Margin = ::Margin; + + fn approx_eq>(self, other: Self, margin: M) -> bool { + let margin = margin.into(); + match (self, other) { + (None, None) => true, + (Some(slf), Some(oth)) => slf.approx_eq(oth, margin), + _ => false, + } + } +} + +#[test] +fn test_option() { + let x: Option = None; + assert!(x.approx_eq(None, (0.0, 0_i32))); + assert!(Some(5.3_f32).approx_eq(Some(5.3), (0.0, 0_i32))); + assert!(Some(5.3_f32).approx_ne(Some(5.7), (0.0, 0_i32))); + assert!(Some(5.3_f32).approx_ne(None, (0.0, 0_i32))); + assert!(x.approx_ne(Some(5.3), (0.0, 0_i32))); +} diff --git a/tools/vendor/float-cmp/src/lib.rs b/tools/vendor/float-cmp/src/lib.rs new file mode 100644 index 0000000000..15b1624d4b --- /dev/null +++ b/tools/vendor/float-cmp/src/lib.rs @@ -0,0 +1,195 @@ +// Copyright 2014-2020 Optimal Computing (NZ) Ltd. +// Licensed under the MIT license. See LICENSE for details. + +//! # float-cmp +//! +//! float-cmp defines and implements traits for approximate comparison of floating point types +//! which have fallen away from exact equality due to the limited precision available within +//! floating point representations. Implementations of these traits are provided for `f32` +//! and `f64` types. +//! +//! When I was a kid in the '80s, the programming rule was "Never compare floating point +//! numbers". If you can follow that rule and still get the outcome you desire, then more +//! power to you. However, if you really do need to compare them, this crate provides a +//! reasonable way to do so. +//! +//! Another crate `efloat` offers another solution by providing a floating point type that +//! tracks its error bounds as operations are performed on it, and thus can implement the +//! `ApproxEq` trait in this crate more accurately, without specifying a `Margin`. +//! +//! The recommended go-to solution (although it may not be appropriate in all cases) is the +//! `approx_eq()` function in the `ApproxEq` trait (or better yet, the macros). For `f32` +//! and `f64`, the `F32Margin` and `F64Margin` types are provided for specifying margins as +//! both an epsilon value and an ULPs value, and defaults are provided via `Default` +//! (although there is no perfect default value that is always appropriate, so beware). +//! +//! Several other traits are provided including `Ulps`, `ApproxEqUlps`, `ApproxOrdUlps`, and +//! `ApproxEqRatio`. +//! +//! ## The problem +//! +//! Floating point operations must round answers to the nearest representable number. Multiple +//! operations may result in an answer different from what you expect. In the following example, +//! the assert will fail, even though the printed output says "0.45 == 0.45": +//! +//! ```should_panic +//! # extern crate float_cmp; +//! # use float_cmp::ApproxEq; +//! # fn main() { +//! let a: f32 = 0.15 + 0.15 + 0.15; +//! let b: f32 = 0.1 + 0.1 + 0.25; +//! println!("{} == {}", a, b); +//! assert!(a==b) // Fails, because they are not exactly equal +//! # } +//! ``` +//! +//! This fails because the correct answer to most operations isn't exactly representable, and so +//! your computer's processor chooses to represent the answer with the closest value it has +//! available. This introduces error, and this error can accumulate as multiple operations are +//! performed. +//! +//! ## The solution +//! +//! With `ApproxEq`, we can get the answer we intend: +//! +//! ``` +//! # #[macro_use] +//! # extern crate float_cmp; +//! # use float_cmp::{ApproxEq, F32Margin}; +//! # fn main() { +//! let a: f32 = 0.15 + 0.15 + 0.15; +//! let b: f32 = 0.1 + 0.1 + 0.25; +//! println!("{} == {}", a, b); +//! // They are equal, within 2 ulps +//! assert!( approx_eq!(f32, a, b, ulps = 2) ); +//! # } +//! ``` +//! +//! ## Some explanation +//! +//! We use the term ULP (units of least precision, or units in the last place) to mean the +//! difference between two adjacent floating point representations (adjacent meaning that there is +//! no floating point number between them). This term is borrowed from prior work (personally I +//! would have chosen "quanta"). The size of an ULP (measured as a float) varies +//! depending on the exponents of the floating point numbers in question. That is a good thing, +//! because as numbers fall away from equality due to the imprecise nature of their representation, +//! they fall away in ULPs terms, not in absolute terms. Pure epsilon-based comparisons are +//! absolute and thus don't map well to the nature of the additive error issue. They work fine +//! for many ranges of numbers, but not for others (consider comparing -0.0000000028 +//! to +0.00000097). +//! +//! ## Using this crate +//! +//! By default this crate enables the `ratio` module providing the `ApproxEqRatio` trait. This +//! feature pulls in `num-traits`. If you disable this feature, you'll need to either enable +//! `num-traits` directly or else enable the `std` feature; otherwise it won't compile. This crate +//! is `#![no_std]` unless you enable the `std` feature. +//! +//! You can use the `ApproxEq` trait directly like so: +//! +//! ``` +//! # extern crate float_cmp; +//! # use float_cmp::{ApproxEq, F32Margin}; +//! # fn main() { +//! # let a: f32 = 0.15 + 0.15 + 0.15; +//! # let b: f32 = 0.1 + 0.1 + 0.25; +//! assert!( a.approx_eq(b, F32Margin { ulps: 2, epsilon: 0.0 }) ); +//! # } +//! ``` +//! +//! We have implemented `From<(f32,i32)>` for `F32Margin` (and similarly for `F64Margin`) +//! so you can use this shorthand: +//! +//! ``` +//! # extern crate float_cmp; +//! # use float_cmp::{ApproxEq, F32Margin}; +//! # fn main() { +//! # let a: f32 = 0.15 + 0.15 + 0.15; +//! # let b: f32 = 0.1 + 0.1 + 0.25; +//! assert!( a.approx_eq(b, (0.0, 2)) ); +//! # } +//! ``` +//! +//! With macros, it is easier to be explicit about which type of margin you wish to set, +//! without mentioning the other one (the other one will be zero). But the downside is +//! that you have to specify the type you are dealing with: +//! +//! ``` +//! # #[macro_use] +//! # extern crate float_cmp; +//! # use float_cmp::{ApproxEq, F32Margin}; +//! # fn main() { +//! # let a: f32 = 0.15 + 0.15 + 0.15; +//! # let b: f32 = 0.1 + 0.1 + 0.25; +//! assert!( approx_eq!(f32, a, b, ulps = 2) ); +//! assert!( approx_eq!(f32, a, b, epsilon = 0.00000003) ); +//! assert!( approx_eq!(f32, a, b, epsilon = 0.00000003, ulps = 2) ); +//! assert!( approx_eq!(f32, a, b, (0.0, 2)) ); +//! assert!( approx_eq!(f32, a, b, F32Margin { epsilon: 0.0, ulps: 2 }) ); +//! assert!( approx_eq!(f32, a, b, F32Margin::default()) ); +//! assert!( approx_eq!(f32, a, b) ); // uses the default +//! # } +//! ``` +//! +//! For most cases, I recommend you use a smallish integer for the `ulps` parameter (1 to 5 +//! or so), and a similar small multiple of the floating point's EPSILON constant (1.0 to 5.0 +//! or so), but there are *plenty* of cases where this is insufficient. +//! +//! ## Implementing these traits +//! +//! You can implement `ApproxEq` for your own complex types like shown below. +//! The floating point type `F` must be `Copy`, but for large types you can implement +//! it for references to your type as shown. +//! +//! ``` +//! use float_cmp::{ApproxEq, FloatMargin}; +//! +//! pub struct Vec2 { +//! pub x: F, +//! pub y: F, +//! } +//! +//! impl<'a, M: FloatMargin, F: Copy + ApproxEq> ApproxEq for &'a Vec2 { +//! type Margin = M; +//! +//! fn approx_eq>(self, other: Self, margin: T) -> bool { +//! let margin = margin.into(); +//! self.x.approx_eq(other.x, margin) +//! && self.y.approx_eq(other.y, margin) +//! } +//! } +//! ``` +//! +//! ## Non floating-point types +//! +//! `ApproxEq` can be implemented for non floating-point types as well, since `Margin` is +//! an associated type. +//! +//! The `efloat` crate implements (or soon will implement) `ApproxEq` for a compound type +//! that tracks floating point error bounds by checking if the error bounds overlap. +//! In that case `type Margin = ()`. +//! +//! ## Inspiration +//! +//! This crate was inspired by this Random ASCII blog post: +//! +//! [https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/) + +#![cfg_attr(not(feature = "std"), no_std)] + +#[macro_use] +mod macros; + +mod ulps; +pub use self::ulps::Ulps; + +mod ulps_eq; +pub use self::ulps_eq::ApproxEqUlps; + +mod eq; +pub use self::eq::{ApproxEq, F32Margin, F64Margin, FloatMargin}; + +#[cfg(feature = "ratio")] +mod ratio; +#[cfg(feature = "ratio")] +pub use self::ratio::ApproxEqRatio; diff --git a/tools/vendor/float-cmp/src/macros.rs b/tools/vendor/float-cmp/src/macros.rs new file mode 100644 index 0000000000..563cf93ae7 --- /dev/null +++ b/tools/vendor/float-cmp/src/macros.rs @@ -0,0 +1,230 @@ +#[macro_export] +macro_rules! approx_eq { + ($typ:ty, $lhs:expr, $rhs:expr) => { + { + let m = <$typ as $crate::ApproxEq>::Margin::default(); + <$typ as $crate::ApproxEq>::approx_eq($lhs, $rhs, m) + } + }; + ($typ:ty, $lhs:expr, $rhs:expr $(, $set:ident = $val:expr)*) => { + { + use $crate::FloatMargin; + let m = <$typ as $crate::ApproxEq>::Margin::zero()$(.$set($val))*; + <$typ as $crate::ApproxEq>::approx_eq($lhs, $rhs, m) + } + }; + ($typ:ty, $lhs:expr, $rhs:expr, $marg:expr) => { + { + <$typ as $crate::ApproxEq>::approx_eq($lhs, $rhs, $marg) + } + }; +} + +#[macro_export] +macro_rules! assert_approx_eq { + ($typ:ty, $lhs:expr, $rhs:expr) => { + { + match (&$lhs, &$rhs) { + (left_val, right_val) => { + if !$crate::approx_eq!($typ, *left_val, *right_val) { + panic!( + r#"assertion failed: `(left approx_eq right)` + left: `{:?}`, + right: `{:?}`"#, + left_val, right_val, + ) + } + } + } + } + }; + ($typ:ty, $lhs:expr, $rhs:expr $(, $set:ident = $val:expr)*) => { + { + match (&$lhs, &$rhs) { + (left_val, right_val) => { + if !$crate::approx_eq!($typ, *left_val, *right_val $(, $set = $val)*) { + panic!( + r#"assertion failed: `(left approx_eq right)` + left: `{:?}`, + right: `{:?}`"#, + left_val, right_val, + ) + } + } + } + } + }; + ($typ:ty, $lhs:expr, $rhs:expr, $marg:expr) => { + { + match (&$lhs, &$rhs) { + (left_val, right_val) => { + if !$crate::approx_eq!($typ, *left_val, *right_val, $marg) { + panic!( + r#"assertion failed: `(left approx_eq right)` + left: `{:?}`, + right: `{:?}`"#, + left_val, right_val, + ) + } + } + } + } + }; +} + +// Until saturating_abs() comes out of nightly, we have to code it ourselves. +macro_rules! saturating_abs_i32 { + ($val:expr) => { + if $val.is_negative() { + match $val.checked_neg() { + Some(v) => v, + None => i32::MAX, + } + } else { + $val + } + }; +} +macro_rules! saturating_abs_i64 { + ($val:expr) => { + if $val.is_negative() { + match $val.checked_neg() { + Some(v) => v, + None => i64::MAX, + } + } else { + $val + } + }; +} + +#[test] +fn test_macro() { + let a: f32 = 0.15 + 0.15 + 0.15; + let b: f32 = 0.1 + 0.1 + 0.25; + assert!(approx_eq!(f32, a, b)); // uses the default + assert!(approx_eq!(f32, a, b, ulps = 2)); + assert!(approx_eq!(f32, a, b, epsilon = 0.00000003)); + assert!(approx_eq!(f32, a, b, epsilon = 0.00000003, ulps = 2)); + assert!(approx_eq!(f32, a, b, (0.0, 2))); + + assert_approx_eq!(f32, a, b); // uses the default + assert_approx_eq!(f32, a, b, ulps = 2); + assert_approx_eq!(f32, a, b, epsilon = 0.00000003); + assert_approx_eq!(f32, a, b, epsilon = 0.00000003, ulps = 2); + assert_approx_eq!(f32, a, b, (0.0, 2)); +} + +#[test] +fn test_macro_2() { + assert!(approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64)); + assert!(approx_eq!( + f64, + 1000000_f64, + 1000000.0000000003_f64, + ulps = 3 + )); + assert!(approx_eq!( + f64, + 1000000_f64, + 1000000.0000000003_f64, + epsilon = 0.0000000004 + )); + assert!(approx_eq!( + f64, + 1000000_f64, + 1000000.0000000003_f64, + (0.0000000004, 0) + )); + assert!(approx_eq!( + f64, + 1000000_f64, + 1000000.0000000003_f64, + (0.0, 3) + )); + + assert_approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64); + assert_approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, ulps = 3); + assert_approx_eq!( + f64, + 1000000_f64, + 1000000.0000000003_f64, + epsilon = 0.0000000004 + ); + assert_approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, (0.0000000004, 0)); + assert_approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, (0.0, 3)); +} + +#[test] +fn test_macro_3() { + use crate::F32Margin; + + let a: f32 = 0.15 + 0.15 + 0.15; + let b: f32 = 0.1 + 0.1 + 0.25; + assert!(approx_eq!( + f32, + a, + b, + F32Margin { + epsilon: 0.0, + ulps: 2 + } + )); + assert!(approx_eq!(f32, a, b, F32Margin::default())); + + assert_approx_eq!( + f32, + a, + b, + F32Margin { + epsilon: 0.0, + ulps: 2 + } + ); + assert_approx_eq!(f32, a, b, F32Margin::default()); +} + +#[test] +#[should_panic] +fn test_macro_4() { + let a: f32 = 0.15 + 0.15 + 0.15; + let b: f32 = 1.0; + + assert_approx_eq!(f32, a, b); +} + +#[test] +#[should_panic] +fn test_macro_5() { + let a: f32 = 0.15 + 0.15 + 0.15; + let b: f32 = 1.0; + + assert_approx_eq!(f32, a, b, ulps = 2); +} + +#[test] +#[should_panic] +fn test_macro_6() { + let a: f32 = 0.15 + 0.15 + 0.15; + let b: f32 = 1.0; + + assert_approx_eq!(f32, a, b, epsilon = 0.00000003); +} + +#[test] +#[should_panic] +fn test_macro_7() { + let a: f32 = 0.15 + 0.15 + 0.15; + let b: f32 = 1.0; + + assert_approx_eq!(f32, a, b, epsilon = 0.00000003, ulps = 2); +} + +#[test] +#[should_panic] +fn test_macro_8() { + let a: f32 = 0.15 + 0.15 + 0.15; + let b: f32 = 1.0; + + assert_approx_eq!(f32, a, b, (0.0, 2)); +} diff --git a/tools/vendor/float-cmp/src/ratio.rs b/tools/vendor/float-cmp/src/ratio.rs new file mode 100644 index 0000000000..51cebc7d01 --- /dev/null +++ b/tools/vendor/float-cmp/src/ratio.rs @@ -0,0 +1,141 @@ +// Copyright 2014-2020 Optimal Computing (NZ) Ltd. +// Licensed under the MIT license. See LICENSE for details. + +use core::cmp::PartialOrd; +use core::ops::{Div, Neg, Sub}; +use num_traits::Zero; + +/// ApproxEqRatio is a trait for approximate equality comparisons bounding the ratio +/// of the difference to the larger. +pub trait ApproxEqRatio: + Div + Sub + Neg + PartialOrd + Zero + Sized + Copy +{ + /// This method tests if `self` and `other` are nearly equal by bounding the + /// difference between them to some number much less than the larger of the two. + /// This bound is set as the ratio of the difference to the larger. + fn approx_eq_ratio(&self, other: &Self, ratio: Self) -> bool { + // Not equal if signs are not equal + if *self < Self::zero() && *other > Self::zero() { + return false; + } + if *self > Self::zero() && *other < Self::zero() { + return false; + } + + // Handle all zero cases + match (*self == Self::zero(), *other == Self::zero()) { + (true, true) => return true, + (true, false) => return false, + (false, true) => return false, + _ => {} + } + + // abs + let (s, o) = if *self < Self::zero() { + (-*self, -*other) + } else { + (*self, *other) + }; + + let (smaller, larger) = if s < o { (s, o) } else { (o, s) }; + let difference: Self = larger.sub(smaller); + let actual_ratio: Self = difference.div(larger); + actual_ratio < ratio + } + + /// This method tests if `self` and `other` are not nearly equal by bounding the + /// difference between them to some number much less than the larger of the two. + /// This bound is set as the ratio of the difference to the larger. + #[inline] + fn approx_ne_ratio(&self, other: &Self, ratio: Self) -> bool { + !self.approx_eq_ratio(other, ratio) + } +} + +impl ApproxEqRatio for f32 {} + +#[test] +fn f32_approx_eq_ratio_test1() { + let x: f32 = 0.00004_f32; + let y: f32 = 0.00004001_f32; + assert!(x.approx_eq_ratio(&y, 0.00025)); + assert!(y.approx_eq_ratio(&x, 0.00025)); + assert!(x.approx_ne_ratio(&y, 0.00024)); + assert!(y.approx_ne_ratio(&x, 0.00024)); +} + +#[test] +fn f32_approx_eq_ratio_test2() { + let x: f32 = 0.00000000001_f32; + let y: f32 = 0.00000000005_f32; + assert!(x.approx_eq_ratio(&y, 0.81)); + assert!(y.approx_ne_ratio(&x, 0.79)); +} + +#[test] +fn f32_approx_eq_ratio_test_zero_eq_zero_returns_true() { + let x: f32 = 0.0_f32; + assert!(x.approx_eq_ratio(&x, 0.1) == true); +} + +#[test] +fn f32_approx_eq_ratio_test_zero_ne_zero_returns_false() { + let x: f32 = 0.0_f32; + assert!(x.approx_ne_ratio(&x, 0.1) == false); +} + +#[test] +fn f32_approx_eq_ratio_test_against_a_zero_is_false() { + let x: f32 = 0.0_f32; + let y: f32 = 0.1_f32; + assert!(x.approx_eq_ratio(&y, 0.1) == false); + assert!(y.approx_eq_ratio(&x, 0.1) == false); +} + +#[test] +fn f32_approx_eq_ratio_test_negative_numbers() { + let x: f32 = -3.0_f32; + let y: f32 = -4.0_f32; + // -3 and -4 should not be equal at a ratio of 0.1 + assert!(x.approx_eq_ratio(&y, 0.1) == false); +} + +impl ApproxEqRatio for f64 {} + +#[test] +fn f64_approx_eq_ratio_test1() { + let x: f64 = 0.000000004_f64; + let y: f64 = 0.000000004001_f64; + assert!(x.approx_eq_ratio(&y, 0.00025)); + assert!(y.approx_eq_ratio(&x, 0.00025)); + assert!(x.approx_ne_ratio(&y, 0.00024)); + assert!(y.approx_ne_ratio(&x, 0.00024)); +} + +#[test] +fn f64_approx_eq_ratio_test2() { + let x: f64 = 0.0000000000000001_f64; + let y: f64 = 0.0000000000000005_f64; + assert!(x.approx_eq_ratio(&y, 0.81)); + assert!(y.approx_ne_ratio(&x, 0.79)); +} + +#[test] +fn f64_approx_eq_ratio_test_zero_eq_zero_returns_true() { + let x: f64 = 0.0_f64; + assert!(x.approx_eq_ratio(&x, 0.1) == true); +} + +#[test] +fn f64_approx_eq_ratio_test_zero_ne_zero_returns_false() { + let x: f64 = 0.0_f64; + assert!(x.approx_ne_ratio(&x, 0.1) == false); +} + +#[test] +fn f64_approx_eq_ratio_test_negative_numbers() { + let x: f64 = -3.0_f64; + let y: f64 = -4.0_f64; + // -3 and -4 should not be equal at a ratio of 0.1 + assert!(x.approx_eq_ratio(&y, 0.1) == false); +} diff --git a/tools/vendor/float-cmp/src/ulps.rs b/tools/vendor/float-cmp/src/ulps.rs new file mode 100644 index 0000000000..7194a67db6 --- /dev/null +++ b/tools/vendor/float-cmp/src/ulps.rs @@ -0,0 +1,251 @@ +// Copyright 2014-2020 Optimal Computing (NZ) Ltd. +// Licensed under the MIT license. See LICENSE for details. + +#[cfg(feature = "num-traits")] +use num_traits::NumCast; + +#[inline] +fn f32_ordered_bits(f: f32) -> u32 { + const SIGN_BIT: u32 = 1 << 31; + let bits = f.to_bits(); + if bits & SIGN_BIT != 0 { + !bits + } else { + bits ^ SIGN_BIT + } +} + +#[inline] +fn f64_ordered_bits(f: f64) -> u64 { + const SIGN_BIT: u64 = 1 << 63; + let bits = f.to_bits(); + if bits & SIGN_BIT != 0 { + !bits + } else { + bits ^ SIGN_BIT + } +} + +/// A trait for floating point numbers which computes the number of representable +/// values or ULPs (Units of Least Precision) that separate the two given values. +#[cfg(feature = "num-traits")] +pub trait Ulps { + type U: Copy + NumCast; + + /// The number of representable values or ULPs (Units of Least Precision) that + /// separate `self` and `other`. The result `U` is an integral value, and will + /// be zero if `self` and `other` are exactly equal. + fn ulps(&self, other: &Self) -> ::U; + + /// The next representable number above this one + fn next(&self) -> Self; + + /// The previous representable number below this one + fn prev(&self) -> Self; +} + +#[cfg(not(feature = "num-traits"))] +pub trait Ulps { + type U: Copy; + + /// The number of representable values or ULPs (Units of Least Precision) that + /// separate `self` and `other`. The result `U` is an integral value, and will + /// be zero if `self` and `other` are exactly equal. + fn ulps(&self, other: &Self) -> ::U; + + /// The next representable number above this one + fn next(&self) -> Self; + + /// The previous representable number below this one + fn prev(&self) -> Self; +} + +impl Ulps for f32 { + type U = i32; + + fn ulps(&self, other: &f32) -> i32 { + // IEEE754 defined floating point storage representation to + // maintain their order when their bit patterns are interpreted as + // integers. This is a huge boon to the task at hand, as we can + // reinterpret them as integers to find out how many ULPs apart any + // two floats are + + // Setup integer representations of the input + let ai32: i32 = f32_ordered_bits(*self) as i32; + let bi32: i32 = f32_ordered_bits(*other) as i32; + + ai32.wrapping_sub(bi32) + } + + fn next(&self) -> Self { + if self.is_infinite() && *self > 0.0 { + *self + } else if *self == -0.0 && self.is_sign_negative() { + 0.0 + } else { + let mut u = self.to_bits(); + if *self >= 0.0 { + u += 1; + } else { + u -= 1; + } + f32::from_bits(u) + } + } + + fn prev(&self) -> Self { + if self.is_infinite() && *self < 0.0 { + *self + } else if *self == 0.0 && self.is_sign_positive() { + -0.0 + } else { + let mut u = self.to_bits(); + if *self <= -0.0 { + u += 1; + } else { + u -= 1; + } + f32::from_bits(u) + } + } +} + +#[test] +fn f32_ulps_test1() { + let x: f32 = 1000000_f32; + let y: f32 = 1000000.1_f32; + assert!(x.ulps(&y) == -2); +} + +#[test] +fn f32_ulps_test2() { + let pzero: f32 = f32::from_bits(0x00000000_u32); + let nzero: f32 = f32::from_bits(0x80000000_u32); + assert_eq!(pzero.ulps(&nzero), 1); +} +#[test] +fn f32_ulps_test3() { + let pinf: f32 = f32::from_bits(0x7f800000_u32); + let ninf: f32 = f32::from_bits(0xff800000_u32); + assert_eq!(pinf.ulps(&ninf), -16777215); +} + +#[test] +fn f32_ulps_test4() { + let x: f32 = f32::from_bits(0x63a7f026_u32); + let y: f32 = f32::from_bits(0x63a7f023_u32); + assert!(x.ulps(&y) == 3); +} + +#[test] +fn f32_ulps_test5() { + let x: f32 = 2.0; + let ulps: i32 = x.to_bits() as i32; + let x2: f32 = ::from_bits(ulps as u32); + assert_eq!(x, x2); +} + +#[test] +fn f32_ulps_test6() { + let negzero: f32 = -0.; + let zero: f32 = 0.; + assert_eq!(negzero.next(), zero); + assert_eq!(zero.prev(), negzero); + assert!(negzero.prev() < 0.0); + assert!(zero.next() > 0.0); +} + +impl Ulps for f64 { + type U = i64; + + fn ulps(&self, other: &f64) -> i64 { + // IEEE754 defined floating point storage representation to + // maintain their order when their bit patterns are interpreted as + // integers. This is a huge boon to the task at hand, as we can + // reinterpret them as integers to find out how many ULPs apart any + // two floats are + + // Setup integer representations of the input + let ai64: i64 = f64_ordered_bits(*self) as i64; + let bi64: i64 = f64_ordered_bits(*other) as i64; + + ai64.wrapping_sub(bi64) + } + + fn next(&self) -> Self { + if self.is_infinite() && *self > 0.0 { + *self + } else if *self == -0.0 && self.is_sign_negative() { + 0.0 + } else { + let mut u = self.to_bits(); + if *self >= 0.0 { + u += 1; + } else { + u -= 1; + } + f64::from_bits(u) + } + } + + fn prev(&self) -> Self { + if self.is_infinite() && *self < 0.0 { + *self + } else if *self == 0.0 && self.is_sign_positive() { + -0.0 + } else { + let mut u = self.to_bits(); + if *self <= -0.0 { + u += 1; + } else { + u -= 1; + } + f64::from_bits(u) + } + } +} + +#[test] +fn f64_ulps_test1() { + let x: f64 = 1000000_f64; + let y: f64 = 1000000.00000001_f64; + assert!(x.ulps(&y) == -86); +} + +#[test] +fn f64_ulps_test2() { + let pzero: f64 = f64::from_bits(0x0000000000000000_u64); + let nzero: f64 = f64::from_bits(0x8000000000000000_u64); + assert_eq!(pzero.ulps(&nzero), 1); +} +#[test] +fn f64_ulps_test3() { + let pinf: f64 = f64::from_bits(0x7f80000000000000_u64); + let ninf: f64 = f64::from_bits(0xff80000000000000_u64); + assert_eq!(pinf.ulps(&ninf), -72057594037927935); +} + +#[test] +fn f64_ulps_test4() { + let x: f64 = f64::from_bits(0xd017f6cc63a7f026_u64); + let y: f64 = f64::from_bits(0xd017f6cc63a7f023_u64); + assert!(x.ulps(&y) == -3); +} + +#[test] +fn f64_ulps_test5() { + let x: f64 = 2.0; + let ulps: i64 = x.to_bits() as i64; + let x2: f64 = ::from_bits(ulps as u64); + assert_eq!(x, x2); +} + +#[test] +fn f64_ulps_test6() { + let negzero: f64 = -0.; + let zero: f64 = 0.; + assert_eq!(negzero.next(), zero); + assert_eq!(zero.prev(), negzero); + assert!(negzero.prev() < 0.0); + assert!(zero.next() > 0.0); +} diff --git a/tools/vendor/float-cmp/src/ulps_eq.rs b/tools/vendor/float-cmp/src/ulps_eq.rs new file mode 100644 index 0000000000..8bbea73253 --- /dev/null +++ b/tools/vendor/float-cmp/src/ulps_eq.rs @@ -0,0 +1,130 @@ +// Copyright 2014-2020 Optimal Computing (NZ) Ltd. +// Licensed under the MIT license. See LICENSE for details. + +use super::Ulps; +#[cfg(feature = "num-traits")] +#[allow(unused_imports)] +use num_traits::float::FloatCore; + +/// ApproxEqUlps is a trait for approximate equality comparisons. +/// The associated type Flt is a floating point type which implements Ulps, and is +/// required so that this trait can be implemented for compound types (e.g. vectors), +/// not just for the floats themselves. +pub trait ApproxEqUlps { + type Flt: Ulps; + + /// This method tests for `self` and `other` values to be approximately equal + /// within ULPs (Units of Least Precision) floating point representations. + /// Differing signs are always unequal with this method, and zeroes are only + /// equal to zeroes. Use approx_eq() from the ApproxEq trait if that is more + /// appropriate. + fn approx_eq_ulps(&self, other: &Self, ulps: ::U) -> bool; + + /// This method tests for `self` and `other` values to be not approximately + /// equal within ULPs (Units of Least Precision) floating point representations. + /// Differing signs are always unequal with this method, and zeroes are only + /// equal to zeroes. Use approx_eq() from the ApproxEq trait if that is more + /// appropriate. + #[inline] + fn approx_ne_ulps(&self, other: &Self, ulps: ::U) -> bool { + !self.approx_eq_ulps(other, ulps) + } +} + +impl ApproxEqUlps for f32 { + type Flt = f32; + + fn approx_eq_ulps(&self, other: &f32, ulps: i32) -> bool { + // -0 and +0 are drastically far in ulps terms, so + // we need a special case for that. + if *self == *other { + return true; + } + + // Handle differing signs as a special case, even if + // they are very close, most people consider them + // unequal. + if self.is_sign_positive() != other.is_sign_positive() { + return false; + } + + let diff: i32 = self.ulps(other); + diff >= -ulps && diff <= ulps + } +} + +#[test] +fn f32_approx_eq_ulps_test1() { + let f: f32 = 0.1_f32; + let mut sum: f32 = 0.0_f32; + for _ in 0_isize..10_isize { + sum += f; + } + let product: f32 = f * 10.0_f32; + assert!(sum != product); // Should not be directly equal: + assert!(sum.approx_eq_ulps(&product, 1) == true); // But should be close + assert!(sum.approx_eq_ulps(&product, 0) == false); +} +#[test] +fn f32_approx_eq_ulps_test2() { + let x: f32 = 1000000_f32; + let y: f32 = 1000000.1_f32; + assert!(x != y); // Should not be directly equal + assert!(x.approx_eq_ulps(&y, 2) == true); + assert!(x.approx_eq_ulps(&y, 1) == false); +} +#[test] +fn f32_approx_eq_ulps_test_zeroes() { + let x: f32 = 0.0_f32; + let y: f32 = -0.0_f32; + assert!(x.approx_eq_ulps(&y, 0) == true); +} + +impl ApproxEqUlps for f64 { + type Flt = f64; + + fn approx_eq_ulps(&self, other: &f64, ulps: i64) -> bool { + // -0 and +0 are drastically far in ulps terms, so + // we need a special case for that. + if *self == *other { + return true; + } + + // Handle differing signs as a special case, even if + // they are very close, most people consider them + // unequal. + if self.is_sign_positive() != other.is_sign_positive() { + return false; + } + + let diff: i64 = self.ulps(other); + diff >= -ulps && diff <= ulps + } +} + +#[test] +fn f64_approx_eq_ulps_test1() { + let f: f64 = 0.1_f64; + let mut sum: f64 = 0.0_f64; + for _ in 0_isize..10_isize { + sum += f; + } + let product: f64 = f * 10.0_f64; + assert!(sum != product); // Should not be directly equal: + assert!(sum.approx_eq_ulps(&product, 1) == true); // But should be close + assert!(sum.approx_eq_ulps(&product, 0) == false); +} +#[test] +fn f64_approx_eq_ulps_test2() { + let x: f64 = 1000000_f64; + let y: f64 = 1000000.0000000003_f64; + assert!(x != y); // Should not be directly equal + assert!(x.approx_eq_ulps(&y, 3) == true); + assert!(x.approx_eq_ulps(&y, 2) == false); +} +#[test] +fn f64_approx_eq_ulps_test_zeroes() { + let x: f64 = 0.0_f64; + let y: f64 = -0.0_f64; + assert!(x.approx_eq_ulps(&y, 0) == true); +} diff --git a/tools/vendor/getrandom/.cargo-checksum.json b/tools/vendor/getrandom/.cargo-checksum.json new file mode 100644 index 0000000000..e3d1a4d4aa --- /dev/null +++ b/tools/vendor/getrandom/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"9930208af94a68f65edfe7acb4b4f967c9aaa8bdf8f8e5bb5806539207f9f039","CHANGELOG.md":"3793e1d3c10656813cf278d674fe943c7ec251b30cba434d8f6c7d6b4948a830","Cargo.lock":"2fe4bb173cc0f4b6b515d87feab12aab157ebf12f4f14869aebf24f1a028c8d2","Cargo.toml":"5fd1d61c342777480822b8f8259a7bd8cbf6c2486a3d389b2d5b235f667a7952","Cargo.toml.orig":"4b13c332865245a22cb5fef091e02547ef792a0aa7291dcf2d9c07ae783f19b0","LICENSE-APACHE":"aaff376532ea30a0cd5330b9502ad4a4c8bf769c539c87ffe78819d188a18ebf","LICENSE-MIT":"29e9fe5074bd27e0e5d5d110394fbbcd841baee2651a3c4b4560a632702cede4","README.md":"39a9371cee9b72f7aff7fcbe0018e1a90c1a0692266cacdcaac25166080b9cb0","SECURITY.md":"816ea79f8c7937888ab5a972a1efb270c4bada028b448953a195359fe11d526e","benches/buffer.rs":"20fb7dd9edafe0c182f1fe037b28d7542f8e0ae742d51e97aeebf418edde49aa","build.rs":"d542c9c3bbc2f64a26c758eac8af16178aab52464bdd8bf0b9a767ba87665021","src/backends.rs":"61f8d982cd502f3229f49bef2f9b40e181c7c16fb41d029aa2e77f32b10c10a6","src/backends/apple_other.rs":"f8fa784588f347cfd7c2b5f37a96b23d446f95c3157668069d7146e281fa783b","src/backends/custom.rs":"c8776ff0dda65cef1373b8624043f8a42ce4fd6183fbeb8918f9df5d4a13d457","src/backends/efi_rng.rs":"873b7343a003a5f222753fbd50cba3f2beeacec4386ad3272ec98d654a4466d4","src/backends/esp_idf.rs":"b50d6c582b49488204151c2036a100aac7902b8215fb791f271e1b5fc772db9a","src/backends/fuchsia.rs":"d7c660a62f1da2c41c537ccea085bba6d3f83b82c34e6e63175130feca9dece8","src/backends/getentropy.rs":"a0fdf57546e20be8e76d21ec5f815f07a3a4dfb590d4d693cc2e78b494676304","src/backends/getrandom.rs":"49a7ca8cc47178b47339ee4aeb59e11b629735eb9fedaa21ee2da7073b2574c5","src/backends/hermit.rs":"92e0cf4103b5ddc15ed48cad8d7165ba8e344fde0f07960385b36590466ee67c","src/backends/linux_android_with_fallback.rs":"af1e4fdd6904f8c78c6cef9af21c626bf4ba1b38d4bf8cd081cb739acc0a34b2","src/backends/linux_raw.rs":"2647e9492e46afdb1838c824094f6ab6e14fb4a82fa8274bea8dd2d4151f32dd","src/backends/netbsd.rs":"ca6a6c4c17c04ec987002296debdd7a03b10f5efd5bcb79f159b9a13c4b6821f","src/backends/rdrand.rs":"2a06eb62ad7e97c2486ab1b0159e4b9fe3a1c0a39d479a836d23051f89cb9aed","src/backends/rndr.rs":"bc06a88e3314a06494b6fe70bdae684fe45f9a6a63d28c61dbd90f66595424d0","src/backends/sanitizer.rs":"ea6d986ccacc31d2efdf116e7f0ef509845f596b4f8aced11c622df12a13be25","src/backends/solaris.rs":"b263a3e8bc3c490a0c589d2a1a67d8fdb36119993df1f8d05f43975ff88f9c3e","src/backends/solid.rs":"1c92fc4c5a8ac67b64af560bb7843d6e447efe9785e557f064348fb84b83ceb8","src/backends/unsupported.rs":"a531ab9883b7d7b584c6f37a5c13543ec63d25d3acc36608d57d5d82ef44cbff","src/backends/use_file.rs":"6c1e982be1663e91d8551fa21529710ac5551506bb1af8803ff87b180d091f00","src/backends/vxworks.rs":"64ee8619c5b5aa267761aabc99552ba8df71d14cda7cc66d6c1e4f4fb1ed8d00","src/backends/wasi_p1.rs":"77755c8fefb9f36509b2e6764edb9f8293dc1efa3d5a9bc85d28723ceeb7aec0","src/backends/wasi_p2.rs":"3aff410d753db6ad875b79bc6a94e05e0e98d38f9e0078ef71871f185cbacecf","src/backends/wasm_js.rs":"5913b33ec9cb4076cef65d461ce82af98e9233f621eac8fab44b68fd37080927","src/backends/windows.rs":"49a08dee7b1758954d25f879efe028147a6fc0598c77500ebb2925412eaf7e0b","src/backends/windows_legacy.rs":"b0b7b47c98a03ea017c6cac3531a0f0754a716bcb87b734e3d66c393d4ab72aa","src/error.rs":"43f4918d3c47d211d223543fa4b93074f883dcb7232b673b03c8b7fe19ef8e7a","src/error_std_impls.rs":"4c068e81d876237a7e0a0e91519896bd670c2f999ca729f7fb970caf888cab46","src/lazy.rs":"9c7e3efcc7b4cc2252751d29e42465bae8cbe49461a4a81a006d8d2c45003fd1","src/lib.rs":"f7628991475fd0ef00ec841ee3d28c2e6e2309d0e5c528dd9e6d8044ab296e66","src/util.rs":"207e21353f4e402daf84ff137c8d4aa4ffafa48141194ae7148252e06dcadd9f","src/util_libc.rs":"1d3f16d8996295822e6cbfc848e90912795f378913627c6b765f47b2185d9480","tests/mod.rs":"b8e88300a1ec0b2f6cbc03d40cc66eacae625d1fdc1163f4cf50fae7712d2bdc"},"package":"899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"} \ No newline at end of file diff --git a/tools/vendor/getrandom/.cargo_vcs_info.json b/tools/vendor/getrandom/.cargo_vcs_info.json new file mode 100644 index 0000000000..6985371259 --- /dev/null +++ b/tools/vendor/getrandom/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "38e4ad38309a85b56eef4fc759535ccfc322ba9a" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/tools/vendor/getrandom/CHANGELOG.md b/tools/vendor/getrandom/CHANGELOG.md new file mode 100644 index 0000000000..71f9c7ce80 --- /dev/null +++ b/tools/vendor/getrandom/CHANGELOG.md @@ -0,0 +1,673 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.3.4] - 2025-10-14 + +### Major change to `wasm_js` backend + +Now, when the `wasm_js` feature is enabled, the `wasm_js` backend will be used +by default. Users of `wasm32-unknown-unknown` targeting JavaScript environments +like the Web and Node.js will no longer need to specify: +``` +--cfg getrandom_backend="wasm_js" +``` +in `RUSTFLAGS` for the crate to compile. They can now simple enable a feature. + +Note: this should not affect non-JS users of the `wasm32-unknown-unknown` +target. Using `--cfg getrandom_backend` will still override the source of +randomness _even if_ the `wasm_js` feature is enabled. This includes +`--cfg getrandom_backend=custom` and `--cfg getrandom_backend=unsupported`. + +For more information, see the discussions in [#671], [#675], and [#730]. + +### Added +- `unsupported` opt-in backend [#667] +- `windows_legacy` opt-in backend [#724] + +### Changed +- Implement Memory Sanitizer unpoisoning more precisely [#678] +- Relax MSRV for the `linux_raw` opt-in backend on ARM targets [#688] +- Use `getrandom` syscall on all RISC-V Linux targets [#699] +- Replaced `wasi` dependency with `wasip2` [#721] +- Enable `wasm_js` backend by default if the `wasm_js` feature is enabled [#730] + +### Removed +- Unstable `rustc-dep-of-std` crate feature [#694] + +[#667]: https://github.com/rust-random/getrandom/pull/667 +[#671]: https://github.com/rust-random/getrandom/issues/671 +[#675]: https://github.com/rust-random/getrandom/pull/675 +[#678]: https://github.com/rust-random/getrandom/pull/678 +[#688]: https://github.com/rust-random/getrandom/pull/688 +[#694]: https://github.com/rust-random/getrandom/pull/694 +[#699]: https://github.com/rust-random/getrandom/pull/699 +[#721]: https://github.com/rust-random/getrandom/pull/721 +[#724]: https://github.com/rust-random/getrandom/pull/724 +[#730]: https://github.com/rust-random/getrandom/pull/730 + +## [0.3.3] - 2025-05-09 + +### Changed +- Doc improvements [#632] [#634] [#635] +- Add crate version to docs.rs links used in `compile_error!`s [#639] + +### Fixed +- Error handling in WASI p1 [#661] + +[#632]: https://github.com/rust-random/getrandom/pull/632 +[#634]: https://github.com/rust-random/getrandom/pull/634 +[#635]: https://github.com/rust-random/getrandom/pull/635 +[#639]: https://github.com/rust-random/getrandom/pull/639 +[#661]: https://github.com/rust-random/getrandom/pull/661 + +## [0.3.2] - 2025-03-17 + +### Added +- `efi_rng` opt-in backend [#570] +- `linux_raw` opt-in backend [#572] +- `.cargo/config.toml` example in the crate-level docs [#591] +- `getrandom_test_linux_without_fallback` configuration flag to test that file fallback + is not triggered in the `linux_android_with_fallback` backend [#605] +- Built-in support for `*-linux-none` targets [#618] +- Cygwin support [#626] + +### Changed +- Update `wasi` dependency to v0.14 [#594] +- Add `#[inline]` attribute to the inner functions [#596] +- Update WASI and Emscripten links in the crate-level docs [#597] +- Do not use `dlsym` on MUSL targets in the `linux_android_with_fallback` backend [#602] +- Remove `linux_android.rs` and use `getrandom.rs` instead [#603] +- Always use `RtlGenRandom` on Windows targets when compiling with pre-1.78 Rust [#610] +- Internal representation of the `Error` type [#614] +- Remove `windows-targets` dependency and use [`raw-dylib`] directly [#627] + +### Removed +- `Error::INTERNAL_START` and `Error::CUSTOM_START` associated constants [#614] + +[#570]: https://github.com/rust-random/getrandom/pull/570 +[#572]: https://github.com/rust-random/getrandom/pull/572 +[#591]: https://github.com/rust-random/getrandom/pull/591 +[#594]: https://github.com/rust-random/getrandom/pull/594 +[#596]: https://github.com/rust-random/getrandom/pull/596 +[#597]: https://github.com/rust-random/getrandom/pull/597 +[#602]: https://github.com/rust-random/getrandom/pull/602 +[#603]: https://github.com/rust-random/getrandom/pull/603 +[#605]: https://github.com/rust-random/getrandom/pull/605 +[#610]: https://github.com/rust-random/getrandom/pull/610 +[#614]: https://github.com/rust-random/getrandom/pull/614 +[#618]: https://github.com/rust-random/getrandom/pull/618 +[#626]: https://github.com/rust-random/getrandom/pull/626 +[#627]: https://github.com/rust-random/getrandom/pull/627 +[`raw-dylib`]: https://doc.rust-lang.org/reference/items/external-blocks.html?highlight=link#dylib-versus-raw-dylib + +## [0.3.1] - 2025-01-28 + +### Fixed +- Build error on Android [#588] + +[#588]: https://github.com/rust-random/getrandom/pull/588 + +## [0.3.0] - 2025-01-25 + +### Breaking Changes + +#### Changed +- Bump MSRV to 1.63 [#542] +- Rename `getrandom` and `getrandom_uninit` functions to `fill` and `fill_uninit` respectively [#532] + +#### Removed +- `wasm32-wasi` target support (use `wasm32-wasip1` or `wasm32-wasip2` instead) [#499] +- `linux_disable_fallback`, `rdrand`, `js`, `test-in-browser`, and `custom` crate features + in favor of configuration flags [#504] +- `register_custom_getrandom!` macro [#504] +- Implementation of `From` for `Error` and `Error::code` method [#507] +- Internet Explorer 11 support [#554] +- Target-specific associated `Error` constants [#562] + +### Changed +- Use `ProcessPrng` on Windows 10 and up, and use `RtlGenRandom` on older Windows versions [#415] +- Do not use locale-specific `strerror_r` for retrieving error code descriptions [#440] +- Avoid assuming `usize` is the native word size in the `rdrand` backend [#442] +- Do not read from `errno` when `libc` did not indicate error on Solaris [#448] +- Switch from `libpthread`'s mutex to `futex` on Linux and to `nanosleep`-based wait loop + on other targets in the `use_file` backend [#490] +- Do not retry on `EAGAIN` while polling `/dev/random` on Linux [#522] +- Remove separate codepath for Node.js in the `wasm_js` backend + (bumps minimum supported Node.js version to v19) [#557] +- Use `js_namespace` in the `wasm_js` backend [#559] + +### Added +- `wasm32-wasip1` and `wasm32-wasip2` support [#499] +- `getrandom_backend` configuration flag for selection of opt-in backends [#504] +- `Error::new_custom` method [#507] +- `rndr` opt-in backend [#512] +- Automatic MemorySanitizer support [#521] [#571] +- `u32` and `u64` functions for generating random values of the respective type [#544] +- `wasm32v1-none` support in the `wasm_js` backend [#560] +- `wasm_js` crate feature which allows users to enable the `wasm_js` opt-in backend [#574] + +### Fixed +- NetBSD fallback code based on `KERN_ARND` [#555] + +[#415]: https://github.com/rust-random/getrandom/pull/415 +[#440]: https://github.com/rust-random/getrandom/pull/440 +[#442]: https://github.com/rust-random/getrandom/pull/442 +[#448]: https://github.com/rust-random/getrandom/pull/448 +[#490]: https://github.com/rust-random/getrandom/pull/490 +[#499]: https://github.com/rust-random/getrandom/pull/499 +[#504]: https://github.com/rust-random/getrandom/pull/504 +[#507]: https://github.com/rust-random/getrandom/pull/507 +[#512]: https://github.com/rust-random/getrandom/pull/512 +[#521]: https://github.com/rust-random/getrandom/pull/521 +[#522]: https://github.com/rust-random/getrandom/pull/522 +[#532]: https://github.com/rust-random/getrandom/pull/532 +[#542]: https://github.com/rust-random/getrandom/pull/542 +[#544]: https://github.com/rust-random/getrandom/pull/544 +[#554]: https://github.com/rust-random/getrandom/pull/554 +[#555]: https://github.com/rust-random/getrandom/pull/555 +[#557]: https://github.com/rust-random/getrandom/pull/557 +[#559]: https://github.com/rust-random/getrandom/pull/559 +[#560]: https://github.com/rust-random/getrandom/pull/560 +[#562]: https://github.com/rust-random/getrandom/pull/562 +[#571]: https://github.com/rust-random/getrandom/pull/571 +[#574]: https://github.com/rust-random/getrandom/pull/574 + +## [0.2.16] - 2025-04-22 +### Added +- Cygwin support (backport of [#626]) [#654] + +[#654]: https://github.com/rust-random/getrandom/pull/654 + +## [0.2.15] - 2024-05-06 +### Added +- Apple visionOS support [#410] + +### Changed +- Use `libc::getrandom` on DragonflyBSD, FreeBSD, illumos, and Solaris [#411] [#416] [#417] [#420] +- Unify `libc::getentropy`-based implementations [#418] + +[#410]: https://github.com/rust-random/getrandom/pull/410 +[#411]: https://github.com/rust-random/getrandom/pull/411 +[#416]: https://github.com/rust-random/getrandom/pull/416 +[#417]: https://github.com/rust-random/getrandom/pull/417 +[#418]: https://github.com/rust-random/getrandom/pull/418 +[#420]: https://github.com/rust-random/getrandom/pull/420 + +## [0.2.14] - 2024-04-08 +### Fixed +- Enable `/dev/urandom` fallback for MUSL-based Linux targets [#408] + +[#408]: https://github.com/rust-random/getrandom/pull/408 + +## [0.2.13] - 2024-04-06 +### Added +- `linux_disable_fallback` crate feature to disable `/dev/urandom`-based fallback on Linux and + Android targets. Enabling this feature bumps minimum supported Linux kernel version to 3.17 and + Android API level to 23 (Marshmallow). [#396] + +### Changed +- Disable `/dev/urandom` fallback for Linux targets outside of the following `target_arch`es: + `aarch64`, `arm`, `powerpc`, `powerpc64`, `s390x`, `x86`, `x86_64` [#396] +- Do not catch `EPERM` error code on Android while checking availability of + the `getrandom` syscall [#396] + +[#396]: https://github.com/rust-random/getrandom/pull/396 + +## [0.2.12] - 2024-01-09 +### Fixed +- Custom backend for targets without atomics [#385] + +### Changed +- Improve robustness of the Hermit backend and `sys_fill_exact` [#386] +- Raise minimum supported Apple OS versions to macOS 10.12 and iOS 10 [#388] + +### Added +- Document platform support policy [#387] + +[#385]: https://github.com/rust-random/getrandom/pull/385 +[#386]: https://github.com/rust-random/getrandom/pull/386 +[#387]: https://github.com/rust-random/getrandom/pull/387 +[#388]: https://github.com/rust-random/getrandom/pull/388 + +## [0.2.11] - 2023-11-08 +### Added +- GNU/Hurd support [#370] + +### Changed +- Renamed `__getrandom_internal` to `__GETRANDOM_INTERNAL` [#369] +- Updated link to Hermit docs [#374] + +[#369]: https://github.com/rust-random/getrandom/pull/369 +[#370]: https://github.com/rust-random/getrandom/pull/370 +[#374]: https://github.com/rust-random/getrandom/pull/374 + +## [0.2.10] - 2023-06-06 +### Added +- Support for PS Vita (`armv7-sony-vita-newlibeabihf`) [#359] + +### Changed +- Use getentropy from libc on Emscripten targets [#362] + +[#359]: https://github.com/rust-random/getrandom/pull/359 +[#362]: https://github.com/rust-random/getrandom/pull/362 + +## [0.2.9] - 2023-04-06 +### Added +- AIX support [#282] +- `getrandom_uninit` function [#291] +- `wasm64-unknown-unknown` support [#303] +- tvOS and watchOS support [#317] +- QNX/nto support [#325] +- Support for `getrandom` syscall on NetBSD ≥ 10.0 [#331] +- `RtlGenRandom` fallback for non-UWP Windows [#337] + +### Breaking Changes +- Update MSRV to 1.36 [#291] + +### Fixed +- Solaris/OpenBSD/Dragonfly build [#301] + +### Changed +- Update MSRV to 1.36 [#291] +- Use getentropy on Emscripten [#307] +- Solaris: consistently use `/dev/random` source [#310] +- Move 3ds selection above rdrand/js/custom fallback [#312] +- Remove buffer zeroing from Node.js implementation [#315] +- Use `open` instead of `open64` [#326] +- Remove #cfg from bsd_arandom.rs [#332] +- Hermit: use `sys_read_entropy` syscall [#333] +- Eliminate potential panic in sys_fill_exact [#334] +- rdrand: Remove checking for 0 and !0 and instead check CPU family and do a self-test [#335] +- Move `__getrandom_custom` definition into a const block [#344] +- Switch the custom backend to Rust ABI [#347] + +[#282]: https://github.com/rust-random/getrandom/pull/282 +[#291]: https://github.com/rust-random/getrandom/pull/291 +[#301]: https://github.com/rust-random/getrandom/pull/301 +[#303]: https://github.com/rust-random/getrandom/pull/303 +[#307]: https://github.com/rust-random/getrandom/pull/307 +[#310]: https://github.com/rust-random/getrandom/pull/310 +[#312]: https://github.com/rust-random/getrandom/pull/312 +[#315]: https://github.com/rust-random/getrandom/pull/315 +[#317]: https://github.com/rust-random/getrandom/pull/317 +[#325]: https://github.com/rust-random/getrandom/pull/325 +[#326]: https://github.com/rust-random/getrandom/pull/326 +[#331]: https://github.com/rust-random/getrandom/pull/331 +[#332]: https://github.com/rust-random/getrandom/pull/332 +[#333]: https://github.com/rust-random/getrandom/pull/333 +[#334]: https://github.com/rust-random/getrandom/pull/334 +[#335]: https://github.com/rust-random/getrandom/pull/335 +[#337]: https://github.com/rust-random/getrandom/pull/337 +[#344]: https://github.com/rust-random/getrandom/pull/344 +[#347]: https://github.com/rust-random/getrandom/pull/347 + +## [0.2.8] - 2022-10-20 +### Changed +- The [Web Cryptography API] will now be preferred on `wasm32-unknown-unknown` + when using the `"js"` feature, even on Node.js [#284] [#295] + +### Added +- Added benchmarks to track buffer initialization cost [#272] + +### Fixed +- Use `$crate` in `register_custom_getrandom!` [#270] + +### Documentation +- Add information about enabling `"js"` feature [#280] +- Fix link to `wasm-bindgen` [#278] +- Document the varied implementations for underlying randomness sources [#276] + +[Web Cryptography API]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API +[#284]: https://github.com/rust-random/getrandom/pull/284 +[#295]: https://github.com/rust-random/getrandom/pull/295 +[#272]: https://github.com/rust-random/getrandom/pull/272 +[#270]: https://github.com/rust-random/getrandom/pull/270 +[#280]: https://github.com/rust-random/getrandom/pull/280 +[#278]: https://github.com/rust-random/getrandom/pull/278 +[#276]: https://github.com/rust-random/getrandom/pull/276 + +## [0.2.7] - 2022-06-14 +### Changed +- Update `wasi` dependency to `0.11` [#253] + +### Fixed +- Use `AtomicPtr` instead of `AtomicUsize` for Strict Provenance compatibility. [#263] + +### Documentation +- Add comments explaining use of fallback mechanisms [#257] [#260] + +[#263]: https://github.com/rust-random/getrandom/pull/263 +[#260]: https://github.com/rust-random/getrandom/pull/260 +[#253]: https://github.com/rust-random/getrandom/pull/253 +[#257]: https://github.com/rust-random/getrandom/pull/257 + +## [0.2.6] - 2022-03-28 +### Added +- Nintendo 3DS (`armv6k-nintendo-3ds`) support [#248] + +### Changed +- Retry `open` when interrupted [#252] + +[#248]: https://github.com/rust-random/getrandom/pull/248 +[#252]: https://github.com/rust-random/getrandom/pull/252 + +## [0.2.5] - 2022-02-22 +### Added +- ESP-IDF targets (`*‑espidf`) support [#245] + +### Fixed +- Webpack warning caused by dynamic require [#234] +- Error checking on iOS for `SecRandomCopyBytes` [#244] + +[#234]: https://github.com/rust-random/getrandom/pull/234 +[#244]: https://github.com/rust-random/getrandom/pull/244 +[#245]: https://github.com/rust-random/getrandom/pull/245 + +## [0.2.4] - 2021-12-13 +### Changed +- Use explicit imports in the `js` backend [#220] +- Use `/dev/urandom` on Redox instead of `rand:` [#222] +- Use `NonZeroU32::new_unchecked` to convert wasi error [#233] + +### Added +- SOLID targets (`*-kmc-solid_*`) support [#235] +- Limited Hermit (`x86_64-unknown-hermit`) support [#236] + +[#220]: https://github.com/rust-random/getrandom/pull/220 +[#222]: https://github.com/rust-random/getrandom/pull/222 +[#233]: https://github.com/rust-random/getrandom/pull/233 +[#235]: https://github.com/rust-random/getrandom/pull/235 +[#236]: https://github.com/rust-random/getrandom/pull/236 + +## [0.2.3] - 2021-04-10 +### Changed +- Replace build.rs with link attributes. [#205] +- Add support for getrandom syscall on DragonFly BSD. [#210] +- Improve Node.js detection. [#215] + +[#205]: https://github.com/rust-random/getrandom/pull/205 +[#210]: https://github.com/rust-random/getrandom/pull/210 +[#215]: https://github.com/rust-random/getrandom/pull/215 + +## [0.2.2] - 2021-01-19 +### Changed +- Forward `rustc-dep-of-std` to dependencies. [#198] +- Highlight feature-dependent functionality in documentation using the `doc_cfg` feature. [#200] + +[#198]: https://github.com/rust-random/getrandom/pull/198 +[#200]: https://github.com/rust-random/getrandom/pull/200 + +## [0.2.1] - 2021-01-03 +### Changed +- Update `cfg-if` to v1.0. [#166] +- Update `wasi` to v0.10. [#167] + +### Fixed +- Multithreaded WASM support. [#165] + +### Removed +- Windows XP support. [#177] +- Direct `stdweb` support. [#178] +- CloudABI support. [#184] + +[#165]: https://github.com/rust-random/getrandom/pull/165 +[#166]: https://github.com/rust-random/getrandom/pull/166 +[#167]: https://github.com/rust-random/getrandom/pull/167 +[#177]: https://github.com/rust-random/getrandom/pull/177 +[#178]: https://github.com/rust-random/getrandom/pull/178 +[#184]: https://github.com/rust-random/getrandom/pull/184 + +## [0.2.0] - 2020-09-10 +### Features for using getrandom on unsupported targets + +The following (off by default) Cargo features have been added: +- `"rdrand"` - use the RDRAND instruction on `no_std` `x86`/`x86_64` targets [#133] +- `"js"` - use JavaScript calls on `wasm32-unknown-unknown` [#149] + - Replaces the `stdweb` and `wasm-bindgen` features (which are removed) +- `"custom"` - allows a user to specify a custom implementation [#109] + +### Breaking Changes +- Unsupported targets no longer compile [#107] +- Change/Add `Error` constants [#120] +- Only impl `std` traits when the `"std"` Cargo feature is specified [#106] +- Remove official support for Hermit, L4Re, and UEFI [#133] +- Remove optional `"log"` dependency [#131] +- Update minimum supported Linux kernel to 2.6.32 [#153] +- Update MSRV to 1.34 [#159] + +[#106]: https://github.com/rust-random/getrandom/pull/106 +[#107]: https://github.com/rust-random/getrandom/pull/107 +[#109]: https://github.com/rust-random/getrandom/pull/109 +[#120]: https://github.com/rust-random/getrandom/pull/120 +[#131]: https://github.com/rust-random/getrandom/pull/131 +[#133]: https://github.com/rust-random/getrandom/pull/133 +[#149]: https://github.com/rust-random/getrandom/pull/149 +[#153]: https://github.com/rust-random/getrandom/pull/153 +[#159]: https://github.com/rust-random/getrandom/pull/159 + +## [0.1.16] - 2020-12-31 +### Changed +- Update `cfg-if` to v1.0. [#173] +- Implement `std::error::Error` for the `Error` type on additional targets. [#169] + +### Fixed +- Multithreaded WASM support. [#171] + +[#173]: https://github.com/rust-random/getrandom/pull/173 +[#171]: https://github.com/rust-random/getrandom/pull/171 +[#169]: https://github.com/rust-random/getrandom/pull/169 + +## [0.1.15] - 2020-09-10 +### Changed +- Added support for Internet Explorer 11 [#139] +- Fix Webpack require warning with `wasm-bindgen` [#137] + +[#137]: https://github.com/rust-random/getrandom/pull/137 +[#139]: https://github.com/rust-random/getrandom/pull/139 + +## [0.1.14] - 2020-01-07 +### Changed +- Remove use of spin-locks in the `use_file` module. [#125] +- Update `wasi` to v0.9. [#126] +- Do not read errno value on DragonFlyBSD to fix compilation failure. [#129] + +[#125]: https://github.com/rust-random/getrandom/pull/125 +[#126]: https://github.com/rust-random/getrandom/pull/126 +[#129]: https://github.com/rust-random/getrandom/pull/129 + +## [0.1.13] - 2019-08-25 +### Added +- VxWorks targets support. [#86] + +### Changed +- If zero-length slice is passed to the `getrandom` function, always return +`Ok(())` immediately without doing any calls to the underlying operating +system. [#104] +- Use the `kern.arandom` sysctl on NetBSD. [#115] + +### Fixed +- Bump `cfg-if` minimum version from 0.1.0 to 0.1.2. [#112] +- Typos and bad doc links. [#117] + +[#86]: https://github.com/rust-random/getrandom/pull/86 +[#104]: https://github.com/rust-random/getrandom/pull/104 +[#112]: https://github.com/rust-random/getrandom/pull/112 +[#115]: https://github.com/rust-random/getrandom/pull/115 +[#117]: https://github.com/rust-random/getrandom/pull/117 + +## [0.1.12] - 2019-08-18 +### Changed +- Update wasi dependency from v0.5 to v0.7. [#100] + +[#100]: https://github.com/rust-random/getrandom/pull/100 + +## [0.1.11] - 2019-08-25 +### Fixed +- Implement `std`-dependent traits for selected targets even if `std` +feature is disabled. (backward compatibility with v0.1.8) [#96] + +[#96]: https://github.com/rust-random/getrandom/pull/96 + +## [0.1.10] - 2019-08-18 [YANKED] +### Changed +- Use the dummy implementation on `wasm32-unknown-unknown` even with the +disabled `dummy` feature. [#90] + +### Fixed +- Fix CSP error for `wasm-bindgen`. [#92] + +[#90]: https://github.com/rust-random/getrandom/pull/90 +[#92]: https://github.com/rust-random/getrandom/pull/92 + +## [0.1.9] - 2019-08-14 [YANKED] +### Changed +- Remove `std` dependency for opening and reading files. [#58] +- Use `wasi` instead of `libc` on WASI target. [#64] +- By default emit a compile-time error when built for an unsupported target. +This behaviour can be disabled by using the `dummy` feature. [#71] + +### Added +- Add support for UWP targets. [#69] +- Add unstable `rustc-dep-of-std` feature. [#78] + +[#58]: https://github.com/rust-random/getrandom/pull/58 +[#64]: https://github.com/rust-random/getrandom/pull/64 +[#69]: https://github.com/rust-random/getrandom/pull/69 +[#71]: https://github.com/rust-random/getrandom/pull/71 +[#78]: https://github.com/rust-random/getrandom/pull/78 + +## [0.1.8] - 2019-07-29 +### Changed +- Explicitly specify types to arguments of 'libc::syscall'. [#74] + +[#74]: https://github.com/rust-random/getrandom/pull/74 + +## [0.1.7] - 2019-07-29 +### Added +- Support for hermit and l4re. [#61] +- `Error::raw_os_error` method, `Error::INTERNAL_START` and +`Error::CUSTOM_START` constants. Use `libc` for retrieving OS error descriptions. [#54] + +### Changed +- Remove `lazy_static` dependency and use custom structures for lock-free +initialization. [#51] [#52] +- Try `getrandom()` first on FreeBSD. [#57] + +### Removed +- Bitrig support. [#56] + +### Deprecated +- `Error::UNKNOWN`, `Error::UNAVAILABLE`. [#54] + +[#51]: https://github.com/rust-random/getrandom/pull/51 +[#52]: https://github.com/rust-random/getrandom/pull/52 +[#54]: https://github.com/rust-random/getrandom/pull/54 +[#56]: https://github.com/rust-random/getrandom/pull/56 +[#57]: https://github.com/rust-random/getrandom/pull/57 +[#61]: https://github.com/rust-random/getrandom/pull/61 + +## [0.1.6] - 2019-06-30 +### Changed +- Minor change of RDRAND AMD bug handling. [#48] + +[#48]: https://github.com/rust-random/getrandom/pull/48 + +## [0.1.5] - 2019-06-29 +### Fixed +- Use shared `File` instead of shared file descriptor. [#44] +- Workaround for RDRAND hardware bug present on some AMD CPUs. [#43] + +### Changed +- Try `getentropy` and then fallback to `/dev/random` on macOS. [#38] + +[#38]: https://github.com/rust-random/getrandom/issues/38 +[#43]: https://github.com/rust-random/getrandom/pull/43 +[#44]: https://github.com/rust-random/getrandom/issues/44 + +## [0.1.4] - 2019-06-28 +### Added +- Add support for `x86_64-unknown-uefi` target by using RDRAND with CPUID +feature detection. [#30] + +### Fixed +- Fix long buffer issues on Windows and Linux. [#31] [#32] +- Check `EPERM` in addition to `ENOSYS` on Linux. [#37] + +### Changed +- Improve efficiency by sharing file descriptor across threads. [#13] +- Remove `cloudabi`, `winapi`, and `fuchsia-cprng` dependencies. [#40] +- Improve RDRAND implementation. [#24] +- Don't block during syscall detection on Linux. [#26] +- Increase consistency with libc implementation on FreeBSD. [#36] +- Apply `rustfmt`. [#39] + +[#30]: https://github.com/rust-random/getrandom/pull/30 +[#13]: https://github.com/rust-random/getrandom/issues/13 +[#40]: https://github.com/rust-random/getrandom/pull/40 +[#26]: https://github.com/rust-random/getrandom/pull/26 +[#24]: https://github.com/rust-random/getrandom/pull/24 +[#39]: https://github.com/rust-random/getrandom/pull/39 +[#36]: https://github.com/rust-random/getrandom/pull/36 +[#31]: https://github.com/rust-random/getrandom/issues/31 +[#32]: https://github.com/rust-random/getrandom/issues/32 +[#37]: https://github.com/rust-random/getrandom/issues/37 + +## [0.1.3] - 2019-05-15 +- Update for `wasm32-unknown-wasi` being renamed to `wasm32-wasi`, and for + WASI being categorized as an OS. + +## [0.1.2] - 2019-04-06 +- Add support for `wasm32-unknown-wasi` target. + +## [0.1.1] - 2019-04-05 +- Enable std functionality for CloudABI by default. + +## [0.1.0] - 2019-03-23 +Publish initial implementation. + +## [0.0.0] - 2019-01-19 +Publish an empty template library. + +[0.3.4]: https://github.com/rust-random/getrandom/compare/v0.3.3...v0.3.4 +[0.3.3]: https://github.com/rust-random/getrandom/compare/v0.3.2...v0.3.3 +[0.3.2]: https://github.com/rust-random/getrandom/compare/v0.3.1...v0.3.2 +[0.3.1]: https://github.com/rust-random/getrandom/compare/v0.3.0...v0.3.1 +[0.3.0]: https://github.com/rust-random/getrandom/compare/v0.2.15...v0.3.0 +[0.2.16]: https://github.com/rust-random/getrandom/compare/v0.2.15...v0.2.16 +[0.2.15]: https://github.com/rust-random/getrandom/compare/v0.2.14...v0.2.15 +[0.2.14]: https://github.com/rust-random/getrandom/compare/v0.2.13...v0.2.14 +[0.2.13]: https://github.com/rust-random/getrandom/compare/v0.2.12...v0.2.13 +[0.2.12]: https://github.com/rust-random/getrandom/compare/v0.2.11...v0.2.12 +[0.2.11]: https://github.com/rust-random/getrandom/compare/v0.2.10...v0.2.11 +[0.2.10]: https://github.com/rust-random/getrandom/compare/v0.2.9...v0.2.10 +[0.2.9]: https://github.com/rust-random/getrandom/compare/v0.2.8...v0.2.9 +[0.2.8]: https://github.com/rust-random/getrandom/compare/v0.2.7...v0.2.8 +[0.2.7]: https://github.com/rust-random/getrandom/compare/v0.2.6...v0.2.7 +[0.2.6]: https://github.com/rust-random/getrandom/compare/v0.2.5...v0.2.6 +[0.2.5]: https://github.com/rust-random/getrandom/compare/v0.2.4...v0.2.5 +[0.2.4]: https://github.com/rust-random/getrandom/compare/v0.2.3...v0.2.4 +[0.2.3]: https://github.com/rust-random/getrandom/compare/v0.2.2...v0.2.3 +[0.2.2]: https://github.com/rust-random/getrandom/compare/v0.2.1...v0.2.2 +[0.2.1]: https://github.com/rust-random/getrandom/compare/v0.2.0...v0.2.1 +[0.2.0]: https://github.com/rust-random/getrandom/compare/v0.1.16...v0.2.0 +[0.1.16]: https://github.com/rust-random/getrandom/compare/v0.1.15...v0.1.16 +[0.1.15]: https://github.com/rust-random/getrandom/compare/v0.1.14...v0.1.15 +[0.1.14]: https://github.com/rust-random/getrandom/compare/v0.1.13...v0.1.14 +[0.1.13]: https://github.com/rust-random/getrandom/compare/v0.1.12...v0.1.13 +[0.1.12]: https://github.com/rust-random/getrandom/compare/v0.1.11...v0.1.12 +[0.1.11]: https://github.com/rust-random/getrandom/compare/v0.1.10...v0.1.11 +[0.1.10]: https://github.com/rust-random/getrandom/compare/v0.1.9...v0.1.10 +[0.1.9]: https://github.com/rust-random/getrandom/compare/v0.1.8...v0.1.9 +[0.1.8]: https://github.com/rust-random/getrandom/compare/v0.1.7...v0.1.8 +[0.1.7]: https://github.com/rust-random/getrandom/compare/v0.1.6...v0.1.7 +[0.1.6]: https://github.com/rust-random/getrandom/compare/v0.1.5...v0.1.6 +[0.1.5]: https://github.com/rust-random/getrandom/compare/v0.1.4...v0.1.5 +[0.1.4]: https://github.com/rust-random/getrandom/compare/v0.1.3...v0.1.4 +[0.1.3]: https://github.com/rust-random/getrandom/compare/v0.1.2...v0.1.3 +[0.1.2]: https://github.com/rust-random/getrandom/compare/v0.1.1...v0.1.2 +[0.1.1]: https://github.com/rust-random/getrandom/compare/v0.1.0...v0.1.1 +[0.1.0]: https://github.com/rust-random/getrandom/compare/v0.0.0...v0.1.0 +[0.0.0]: https://github.com/rust-random/getrandom/releases/tag/v0.0.0 diff --git a/tools/vendor/getrandom/Cargo.lock b/tools/vendor/getrandom/Cargo.lock new file mode 100644 index 0000000000..671aa98f33 --- /dev/null +++ b/tools/vendor/getrandom/Cargo.lock @@ -0,0 +1,292 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "cc" +version = "1.2.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "find-msvc-tools" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" + +[[package]] +name = "getrandom" +version = "0.3.4" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", + "wasm-bindgen-test", +] + +[[package]] +name = "js-sys" +version = "0.3.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "minicov" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" +dependencies = [ + "cc", + "walkdir", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b221ff421256839509adbb55998214a70d829d3a28c69b4a6672e9d2a42f67" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-bindgen-test" +version = "0.3.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aee0a0f5343de9221a0d233b04520ed8dc2e6728dce180b1dcd9288ec9d9fa3c" +dependencies = [ + "js-sys", + "minicov", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a369369e4360c2884c3168d22bded735c43cccae97bbc147586d4b480edd138d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "web-sys" +version = "0.3.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbe734895e869dc429d78c4b433f8d17d95f8d05317440b4fad5ab2d33e596dc" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" diff --git a/tools/vendor/getrandom/Cargo.toml b/tools/vendor/getrandom/Cargo.toml new file mode 100644 index 0000000000..b85fdf670b --- /dev/null +++ b/tools/vendor/getrandom/Cargo.toml @@ -0,0 +1,133 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.63" +name = "getrandom" +version = "0.3.4" +authors = ["The Rand Project Developers"] +build = "build.rs" +exclude = [".*"] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "A small cross-platform library for retrieving random data from system source" +documentation = "https://docs.rs/getrandom" +readme = "README.md" +categories = [ + "os", + "no-std", +] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-random/getrandom" + +[package.metadata.docs.rs] +features = ["std"] + +[package.metadata.cross.target.x86_64-unknown-netbsd] +pre-build = [ + "mkdir -p /tmp/netbsd", + "curl -fO https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.3/amd64/binary/sets/base.tar.xz", + "tar -C /tmp/netbsd -xJf base.tar.xz", + "cp /tmp/netbsd/usr/lib/libexecinfo.so /usr/local/x86_64-unknown-netbsd/lib", + "rm base.tar.xz", + "rm -rf /tmp/netbsd", +] + +[features] +std = [] +wasm_js = [ + "dep:wasm-bindgen", + "dep:js-sys", +] + +[lib] +name = "getrandom" +path = "src/lib.rs" + +[[test]] +name = "mod" +path = "tests/mod.rs" + +[[bench]] +name = "buffer" +path = "benches/buffer.rs" + +[dependencies.cfg-if] +version = "1" + +[target.'cfg(all(any(target_os = "linux", target_os = "android"), not(any(all(target_os = "linux", target_env = ""), getrandom_backend = "custom", getrandom_backend = "linux_raw", getrandom_backend = "rdrand", getrandom_backend = "rndr"))))'.dependencies.libc] +version = "0.2.154" +default-features = false + +[target.'cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))'.dependencies.wasm-bindgen] +version = "0.2.98" +optional = true +default-features = false + +[target.'cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))'.dev-dependencies.wasm-bindgen-test] +version = "0.3" + +[target.'cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"), target_feature = "atomics"))'.dependencies.js-sys] +version = "0.3.77" +optional = true +default-features = false + +[target.'cfg(all(target_arch = "wasm32", target_os = "wasi", target_env = "p2"))'.dependencies.wasip2] +version = "1" +default-features = false + +[target.'cfg(all(target_os = "uefi", getrandom_backend = "efi_rng"))'.dependencies.r-efi] +version = "5.1" +default-features = false + +[target.'cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "hurd", target_os = "illumos", target_os = "cygwin", all(target_os = "horizon", target_arch = "arm")))'.dependencies.libc] +version = "0.2.154" +default-features = false + +[target.'cfg(any(target_os = "haiku", target_os = "redox", target_os = "nto", target_os = "aix"))'.dependencies.libc] +version = "0.2.154" +default-features = false + +[target.'cfg(any(target_os = "ios", target_os = "visionos", target_os = "watchos", target_os = "tvos"))'.dependencies.libc] +version = "0.2.154" +default-features = false + +[target.'cfg(any(target_os = "macos", target_os = "openbsd", target_os = "vita", target_os = "emscripten"))'.dependencies.libc] +version = "0.2.154" +default-features = false + +[target.'cfg(target_os = "netbsd")'.dependencies.libc] +version = "0.2.154" +default-features = false + +[target.'cfg(target_os = "solaris")'.dependencies.libc] +version = "0.2.154" +default-features = false + +[target.'cfg(target_os = "vxworks")'.dependencies.libc] +version = "0.2.154" +default-features = false + +[lints.rust.unexpected_cfgs] +level = "warn" +priority = 0 +check-cfg = [ + 'cfg(getrandom_backend, values("custom", "efi_rng", "rdrand", "rndr", "linux_getrandom", "linux_raw", "wasm_js", "windows_legacy", "unsupported"))', + "cfg(getrandom_msan)", + "cfg(getrandom_test_linux_fallback)", + "cfg(getrandom_test_linux_without_fallback)", + "cfg(getrandom_test_netbsd_fallback)", + 'cfg(target_os, values("cygwin"))', +] diff --git a/tools/vendor/getrandom/Cargo.toml.orig b/tools/vendor/getrandom/Cargo.toml.orig new file mode 100644 index 0000000000..933b9d9cb3 --- /dev/null +++ b/tools/vendor/getrandom/Cargo.toml.orig @@ -0,0 +1,100 @@ +[package] +name = "getrandom" +version = "0.3.4" +edition = "2021" +rust-version = "1.63" # Sync tests.yml and README.md. +authors = ["The Rand Project Developers"] +license = "MIT OR Apache-2.0" +description = "A small cross-platform library for retrieving random data from system source" +documentation = "https://docs.rs/getrandom" +repository = "https://github.com/rust-random/getrandom" +categories = ["os", "no-std"] +exclude = [".*"] + +[features] +# Implement std::error::Error for getrandom::Error and +# use std to retrieve OS error descriptions +std = [] + +# Optional backend: wasm_js +# This flag enables the wasm_js backend and uses it by default on wasm32 where +# the target_os is unknown. The getrandom_backend cfg may override this. +# WARNING: It is highly recommended to enable this feature only for binary crates and tests, +# i.e. avoid unconditionally enabling it in library crates. +wasm_js = ["dep:wasm-bindgen", "dep:js-sys"] + +[dependencies] +cfg-if = "1" + +# getrandom / linux_android_with_fallback +[target.'cfg(all(any(target_os = "linux", target_os = "android"), not(any(all(target_os = "linux", target_env = ""), getrandom_backend = "custom", getrandom_backend = "linux_raw", getrandom_backend = "rdrand", getrandom_backend = "rndr"))))'.dependencies] +libc = { version = "0.2.154", default-features = false } + +# apple-other +[target.'cfg(any(target_os = "ios", target_os = "visionos", target_os = "watchos", target_os = "tvos"))'.dependencies] +libc = { version = "0.2.154", default-features = false } + +# efi_rng +[target.'cfg(all(target_os = "uefi", getrandom_backend = "efi_rng"))'.dependencies] +r-efi = { version = "5.1", default-features = false } + +# getentropy +[target.'cfg(any(target_os = "macos", target_os = "openbsd", target_os = "vita", target_os = "emscripten"))'.dependencies] +libc = { version = "0.2.154", default-features = false } + +# getrandom +[target.'cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "hurd", target_os = "illumos", target_os = "cygwin", all(target_os = "horizon", target_arch = "arm")))'.dependencies] +libc = { version = "0.2.154", default-features = false } + +# netbsd +[target.'cfg(target_os = "netbsd")'.dependencies] +libc = { version = "0.2.154", default-features = false } + +# solaris +[target.'cfg(target_os = "solaris")'.dependencies] +libc = { version = "0.2.154", default-features = false } + +# use_file +[target.'cfg(any(target_os = "haiku", target_os = "redox", target_os = "nto", target_os = "aix"))'.dependencies] +libc = { version = "0.2.154", default-features = false } + +# vxworks +[target.'cfg(target_os = "vxworks")'.dependencies] +libc = { version = "0.2.154", default-features = false } + +# wasi_p2 +[target.'cfg(all(target_arch = "wasm32", target_os = "wasi", target_env = "p2"))'.dependencies] +wasip2 = { version = "1", default-features = false } + +# wasm_js +[target.'cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))'.dependencies] +wasm-bindgen = { version = "0.2.98", default-features = false, optional = true } +[target.'cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"), target_feature = "atomics"))'.dependencies] +js-sys = { version = "0.3.77", default-features = false, optional = true } +[target.'cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))'.dev-dependencies] +wasm-bindgen-test = "0.3" + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = [ + 'cfg(getrandom_backend, values("custom", "efi_rng", "rdrand", "rndr", "linux_getrandom", "linux_raw", "wasm_js", "windows_legacy", "unsupported"))', + 'cfg(getrandom_msan)', + 'cfg(getrandom_test_linux_fallback)', + 'cfg(getrandom_test_linux_without_fallback)', + 'cfg(getrandom_test_netbsd_fallback)', + 'cfg(target_os, values("cygwin"))', # TODO(MSRV 1.86): Remove this. +] + +[package.metadata.docs.rs] +features = ["std"] + +# workaround for https://github.com/cross-rs/cross/issues/1345 +[package.metadata.cross.target.x86_64-unknown-netbsd] +pre-build = [ + "mkdir -p /tmp/netbsd", + "curl -fO https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.3/amd64/binary/sets/base.tar.xz", + "tar -C /tmp/netbsd -xJf base.tar.xz", + "cp /tmp/netbsd/usr/lib/libexecinfo.so /usr/local/x86_64-unknown-netbsd/lib", + "rm base.tar.xz", + "rm -rf /tmp/netbsd", +] diff --git a/tools/vendor/getrandom/LICENSE-APACHE b/tools/vendor/getrandom/LICENSE-APACHE new file mode 100644 index 0000000000..17d74680f8 --- /dev/null +++ b/tools/vendor/getrandom/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/tools/vendor/getrandom/LICENSE-MIT b/tools/vendor/getrandom/LICENSE-MIT new file mode 100644 index 0000000000..e54440a9e5 --- /dev/null +++ b/tools/vendor/getrandom/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (c) 2018-2025 The rust-random Project Developers +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/tools/vendor/getrandom/README.md b/tools/vendor/getrandom/README.md new file mode 100644 index 0000000000..13d4388a27 --- /dev/null +++ b/tools/vendor/getrandom/README.md @@ -0,0 +1,386 @@ +# getrandom: system's random number generator + +[![Build Status]][GitHub Actions] +[![Crate]][crates.io] +[![Documentation]][docs.rs] +[![Dependency Status]][deps.rs] +[![Downloads]][crates.io] +[![License]][LICENSE-MIT] + +`getrandom` is a Rust library for retrieving random data from (operating) system sources. + +It is assumed that the system always provides high-quality, cryptographically secure random +data, ideally backed by hardware entropy sources. This crate derives its name from +the Linux `getrandom` syscall but is cross-platform, roughly supporting the same set +of platforms as Rust's `std` library. + +This is a low-level API. Most users should prefer using a higher-level random-number +library like [`rand`]. + +[`rand`]: https://crates.io/crates/rand + +## Usage + +Add the `getrandom` dependency to your `Cargo.toml` file: + +```toml +[dependencies] +getrandom = "0.3" +``` + +Then invoke the `fill` function on a byte buffer to fill it with random data: + +```rust +fn get_random_u128() -> Result { + let mut buf = [0u8; 16]; + getrandom::fill(&mut buf)?; + Ok(u128::from_ne_bytes(buf)) +} +``` + +## Supported targets + +| Target | Target Triple | Implementation +| ------------------ | ------------------ | -------------- +| Linux, Android | `*‑linux‑*` | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after successfully polling `/dev/random` +| Windows 10+ | `*‑windows‑*` | [`ProcessPrng`] on Rust 1.78+, [`RtlGenRandom`] otherwise +| Windows 7, 8 | `*-win7‑windows‑*` | [`RtlGenRandom`] +| macOS | `*‑apple‑darwin` | [`getentropy`][3] +| iOS, tvOS, watchOS | `*‑apple‑{ios,tvos,watchos}` | [`CCRandomGenerateBytes`] +| FreeBSD | `*‑freebsd` | [`getrandom`][5] +| OpenBSD | `*‑openbsd` | [`getentropy`][7] +| NetBSD | `*‑netbsd` | [`getrandom`][16] if available, otherwise [`kern.arandom`][8] +| Dragonfly BSD | `*‑dragonfly` | [`getrandom`][9] +| Solaris | `*‑solaris` | [`getrandom`][11] with `GRND_RANDOM` +| illumos | `*‑illumos` | [`getrandom`][12] +| Fuchsia OS | `*‑fuchsia` | [`cprng_draw`] +| Redox | `*‑redox` | `/dev/urandom` +| Haiku | `*‑haiku` | `/dev/urandom` (identical to `/dev/random`) +| Hermit | `*-hermit` | [`sys_read_entropy`] +| Hurd | `*-hurd-*` | [`getrandom`][17] +| SGX | `x86_64‑*‑sgx` | [`RDRAND`] +| VxWorks | `*‑wrs‑vxworks‑*` | `randABytes` after checking entropy pool initialization with `randSecure` +| Emscripten | `*‑emscripten` | [`getentropy`][13] +| WASI 0.1 | `wasm32‑wasip1` | [`random_get`] +| WASI 0.2 | `wasm32‑wasip2` | [`get-random-u64`] +| SOLID | `*-kmc-solid_*` | `SOLID_RNG_SampleRandomBytes` +| Nintendo 3DS | `*-nintendo-3ds` | [`getrandom`][18] +| ESP-IDF | `*‑espidf` | [`esp_fill_random`] WARNING: see "Early Boot" section below +| PS Vita | `*-vita-*` | [`getentropy`][19] +| QNX Neutrino | `*‑nto-qnx*` | [`/dev/urandom`][14] (identical to `/dev/random`) +| AIX | `*-ibm-aix` | [`/dev/urandom`][15] +| Cygwin | `*-cygwin` | [`getrandom`][20] (based on [`RtlGenRandom`]) + +Pull Requests that add support for new targets to `getrandom` are always welcome. + +### Opt-in backends + +`getrandom` also provides optional (opt-in) backends, which allow users to customize the source +of randomness based on their specific needs: + +| Backend name | Target | Target Triple | Implementation +| ----------------- | -------------------- | ------------------------ | -------------- +| `linux_getrandom` | Linux, Android | `*‑linux‑*` | [`getrandom`][1] system call (without `/dev/urandom` fallback). Bumps minimum supported Linux kernel version to 3.17 and Android API level to 23 (Marshmallow). +| `linux_raw` | Linux, Android | `*‑linux‑*` | Same as `linux_getrandom`, but uses raw `asm!`-based syscalls instead of `libc`. +| `rdrand` | x86, x86-64 | `x86_64-*`, `i686-*` | [`RDRAND`] instruction +| `rndr` | AArch64 | `aarch64-*` | [`RNDR`] register +| `wasm_js` | Web Browser, Node.js | `wasm32‑unknown‑unknown`, `wasm32v1-none` | [`Crypto.getRandomValues`]. Enabled by the `wasm_js` feature ([see below](#webassembly-support)). +| `efi_rng` | UEFI | `*-unknown‑uefi` | [`EFI_RNG_PROTOCOL`] with `EFI_RNG_ALGORITHM_RAW` (requires `std` and Nightly compiler) +| `windows_legacy` | Windows | `*-windows-*` | [`RtlGenRandom`] +| `custom` | All targets | `*` | User-provided custom implementation (see [custom backend]) +| `unsupported` | All targets | `*` | Always returns `Err(Error::UNSUPPORTED)` (see [unsupported backend]) + +Opt-in backends can be enabled using the `getrandom_backend` configuration flag. +The flag can be set either by specifying the `rustflags` field in [`.cargo/config.toml`]: +```toml +# It's recommended to set the flag on a per-target basis: +[target.wasm32-unknown-unknown] +rustflags = ['--cfg', 'getrandom_backend="wasm_js"'] +``` + +Or by using the `RUSTFLAGS` environment variable: + +```sh +RUSTFLAGS='--cfg getrandom_backend="linux_getrandom"' cargo build +``` + +Enabling an opt-in backend will replace the backend used by default. Doing this for +an incorrect target (e.g. using `linux_getrandom` while compiling for a Windows target) +will result in a compilation error. Be extremely careful while using opt-in backends, +as incorrect configuration may result in vulnerable applications or applications +that always panic. + +Note that using an opt-in backend in a library (e.g. for tests or benchmarks) +WILL NOT have any effect on its downstream users. + +[`.cargo/config.toml`]: https://doc.rust-lang.org/cargo/reference/config.html + +### Raw Linux syscall support + +Currently the `linux_raw` backend supports only targets with stabilized `asm!` macro, +i.e. `arm`, `aarch64`, `loongarch64`, `riscv32`, `riscv64`, `s390x`, `x86`, and `x86_64`. + +Note that the raw syscall backend may be slower than backends based on `libc::getrandom`, +e.g. it does not implement vDSO optimizations and on `x86` it uses the infamously slow +`int 0x80` instruction to perform syscall. + +### WebAssembly support + +This crate fully supports the [WASI] and [Emscripten] targets. However, +the `wasm32-unknown-unknown` target (i.e. the target used by `wasm-pack`) +is not automatically supported since, from the target name alone, we cannot deduce +which JavaScript interface should be used (or if JavaScript is available at all). + +We do not include support for this target in the default configuration because +our JS backend (supporting web browsers, web workers and Node.js v19 or later) +requires [`wasm-bindgen`], **bloating `Cargo.lock`** and +**potentially breaking builds** on non-web WASM platforms. + +To enable `getrandom`'s functionality on `wasm32-unknown-unknown` using the Web +Crypto methods [described above][opt-in] via [`wasm-bindgen`], enable the +`wasm_js` feature flag. Setting `RUSTFLAGS='--cfg getrandom_backend="wasm_js"'` +is allowed but is no longer required and does nothing (it was required in a +prior version of this crate). + +WARNING: enabling the `wasm_js` feature will bloat `Cargo.lock` on all platforms +(where [`wasm-bindgen`] is not an existing dependency) and is known to cause +build issues on some non-web WASM platforms, even when a different backend is +selected via `getrandom_backend`. + +### Custom backend + +If this crate does not support your target out of the box or you have to use +a non-default entropy source, then you can provide a custom implementation. +You need to enable the custom backend as described in the +[opt-in backends][opt-in] section. + +Next, you need to define an `extern` function with the following signature: + +```rust +use getrandom::Error; + +#[no_mangle] +unsafe extern "Rust" fn __getrandom_v03_custom( + dest: *mut u8, + len: usize, +) -> Result<(), Error> { + todo!() +} +``` + +This function should, ideally, be defined in the root crate of your project, +e.g. in your `main.rs`. This function MUST be defined only once for your +project, i.e. upstream library crates SHOULD NOT define it outside of +tests and benchmarks. Improper configuration of this backend may result +in linking errors. + +The function accepts a pointer to a buffer that should be filled with random +data and its length in bytes. Note that the buffer MAY be uninitialized. +On success, the function should return `Ok(())` and fully fill the input buffer; +otherwise, it should return an error value. + +While wrapping functions which work with byte slices you should fully initialize +the buffer before passing it to the function: +```rust +use getrandom::Error; + +fn my_entropy_source(buf: &mut [u8]) -> Result<(), getrandom::Error> { + // ... + Ok(()) +} + +#[no_mangle] +unsafe extern "Rust" fn __getrandom_v03_custom( + dest: *mut u8, + len: usize, +) -> Result<(), Error> { + let buf = unsafe { + // fill the buffer with zeros + core::ptr::write_bytes(dest, 0, len); + // create mutable byte slice + core::slice::from_raw_parts_mut(dest, len) + }; + my_entropy_source(buf) +} +``` + +### Unsupported backend + +In some rare scenarios, you might be compiling this crate for an unsupported +target (e.g. `wasm32-unknown-unknown`), but this crate's functionality +is not actually used by your code. If you are confident that `getrandom` is +not used in your project, but it gets pulled nevertheless by one of your +dependencies, then you can enable the `unsupported` backend, which always +returns `Err(Error::UNSUPPORTED)`. + +### Platform Support + +This crate generally supports the same operating system and platform versions +that the Rust standard library does. Additional targets may be supported using +the opt-in custom backend. + +This means that as Rust drops support for old versions of operating systems +(such as old Linux kernel versions, Android API levels, etc.) in stable releases, +`getrandom` may create new patch releases that remove support for +outdated platform versions. + +### `/dev/urandom` fallback on Linux and Android + +On Linux targets, the `/dev/urandom` fallback is present only if either `target_env` +is `musl`, or `target_arch` is one of the following: `aarch64`, `arm`, `powerpc`, +`powerpc64`, `s390x`, `x86`, `x86_64`. Other supported targets [require][platform-support] +kernel versions that support the `getrandom` system call, so the fallback is not needed. + +On Android targets the fallback is present only for the following `target_arch`es: +`aarch64`, `arm`, `x86`, `x86_64`. Other `target_arch`es (e.g. RISC-V) require +sufficiently high API levels. + +The fallback can be disabled by enabling the `linux_getrandom` opt-in backend. +Note that doing so will bump minimum supported Linux kernel version to 3.17 +and Android API level to 23 (Marshmallow). + +### Early boot + +Sometimes, early in the boot process, the OS has not collected enough +entropy to securely seed its RNG. This is especially common on virtual +machines, where standard "random" events are hard to come by. + +Some operating system interfaces always block until the RNG is securely +seeded. This can take anywhere from a few seconds to more than a minute. +A few (Linux, NetBSD and Solaris) offer a choice between blocking and +getting an error; in these cases, we always choose to block. + +On Linux (when the `getrandom` system call is not available), reading from +`/dev/urandom` never blocks, even when the OS hasn't collected enough +entropy yet. To avoid returning low-entropy bytes, we first poll +`/dev/random` and only switch to `/dev/urandom` once this has succeeded. + +On OpenBSD, this kind of entropy accounting isn't available, and on +NetBSD, blocking on it is discouraged. On these platforms, nonblocking +interfaces are used, even when reliable entropy may not be available. +On the platforms where it is used, the reliability of entropy accounting +itself isn't free from controversy. This library provides randomness +sourced according to the platform's best practices, but each platform has +its own limits on the grade of randomness it can promise in environments +with few sources of entropy. + +On ESP-IDF, if `esp_fill_random` is used before enabling WiFi, BT, or the +voltage noise entropy source (SAR ADC), the Hardware RNG will only be seeded +via RC_FAST_CLK. This can occur during early boot unless +`bootloader_random_enable()` is called. For more information see the +[ESP-IDF RNG Docs][esp-idf-rng] or the +[RNG section of the ESP32 Technical Reference Manual][esp-trng-docs]. + +## Error handling + +We always prioritize failure over returning known insecure "random" bytes. +Generally, on supported platforms, failure is highly unlikely, though not +impossible. If an error does occur, it is likely that it will occur +on every call to `getrandom`. Therefore, after the first successful call, +one can be reasonably confident that no errors will occur. + +## Panic handling + +We strive to eliminate all potential panics from our backend implementations. +In other words, when compiled with optimizations enabled, the generated +binary code for `getrandom` functions should not contain any panic branches. +Even if the platform misbehaves and returns an unexpected result, +our code should correctly handle it and return an error, e.g. +[`Error::UNEXPECTED`]. + +## Sanitizer support + +If your code uses [`fill_uninit`] and you enable +[MemorySanitizer](https://doc.rust-lang.org/beta/unstable-book/compiler-flags/sanitizer.html#memorysanitizer) +(i.e. `-Zsanitizer=memory`), we will automatically handle unpoisoning +of the destination buffer filled by `fill_uninit`. + +You can run sanitizer tests for your crate dependent on `getrandom` like this: +```sh +RUSTFLAGS="-Zsanitizer=memory" cargo test -Zbuild-std --target=x86_64-unknown-linux-gnu +``` + +## Minimum Supported Rust Version + +This crate requires Rust 1.63 or later. + +## License + +The `getrandom` library is distributed under either of + + * [Apache License, Version 2.0][LICENSE-APACHE] + * [MIT license][LICENSE-MIT] + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[GitHub Actions]: https://github.com/rust-random/getrandom/actions?query=branch:master +[Build Status]: https://github.com/rust-random/getrandom/actions/workflows/tests.yml/badge.svg?branch=master +[crates.io]: https://crates.io/crates/getrandom +[Crate]: https://img.shields.io/crates/v/getrandom +[docs.rs]: https://docs.rs/getrandom +[Documentation]: https://docs.rs/getrandom/badge.svg +[deps.rs]: https://deps.rs/repo/github/rust-random/getrandom +[Dependency Status]: https://deps.rs/repo/github/rust-random/getrandom/status.svg +[Downloads]: https://img.shields.io/crates/d/getrandom +[License]: https://img.shields.io/crates/l/getrandom + +[//]: # (supported targets) + +[1]: https://manned.org/getrandom.2 +[2]: https://manned.org/urandom.4 +[3]: https://www.unix.com/man-page/mojave/2/getentropy/ +[4]: https://www.unix.com/man-page/mojave/4/urandom/ +[5]: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable +[7]: https://man.openbsd.org/getentropy.2 +[8]: https://man.netbsd.org/sysctl.7 +[9]: https://leaf.dragonflybsd.org/cgi/web-man?command=getrandom +[11]: https://docs.oracle.com/cd/E88353_01/html/E37841/getrandom-2.html +[12]: https://illumos.org/man/2/getrandom +[13]: https://github.com/emscripten-core/emscripten/pull/12240 +[14]: https://www.qnx.com/developers/docs/7.1/index.html#com.qnx.doc.neutrino.utilities/topic/r/random.html +[15]: https://www.ibm.com/docs/en/aix/7.3?topic=files-random-urandom-devices +[16]: https://man.netbsd.org/getrandom.2 +[17]: https://www.gnu.org/software/libc/manual/html_mono/libc.html#index-getrandom +[18]: https://github.com/rust3ds/shim-3ds/commit/b01d2568836dea2a65d05d662f8e5f805c64389d +[19]: https://github.com/vitasdk/newlib/blob/2d869fe47aaf02b8e52d04e9a2b79d5b210fd016/newlib/libc/sys/vita/getentropy.c +[20]: https://github.com/cygwin/cygwin/blob/main/winsup/cygwin/libc/getentropy.cc + +[`ProcessPrng`]: https://learn.microsoft.com/en-us/windows/win32/seccng/processprng +[`RtlGenRandom`]: https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom +[`Crypto.getRandomValues`]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues +[`RDRAND`]: https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide +[`RNDR`]: https://developer.arm.com/documentation/ddi0601/2024-06/AArch64-Registers/RNDR--Random-Number +[`CCRandomGenerateBytes`]: https://opensource.apple.com/source/CommonCrypto/CommonCrypto-60074/include/CommonRandom.h.auto.html +[`cprng_draw`]: https://fuchsia.dev/fuchsia-src/zircon/syscalls/cprng_draw +[`esp_fill_random`]: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/random.html#functions +[esp-idf-rng]: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/random.html +[esp-trng-docs]: https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf#rng +[`EFI_RNG_PROTOCOL`]: https://uefi.org/specs/UEFI/2.10/37_Secure_Technologies.html#efi-rng-protocol +[`random_get`]: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-random_getbuf-pointeru8-buf_len-size---errno +[`get-random-u64`]: https://github.com/WebAssembly/WASI/blob/v0.2.1/wasip2/random/random.wit#L23-L28 +[configuration flags]: #configuration-flags +[custom backend]: #custom-backend +[unsupported backend]: #unsupported-backend +[`wasm-bindgen`]: https://github.com/rustwasm/wasm-bindgen +[`module`]: https://rustwasm.github.io/wasm-bindgen/reference/attributes/on-js-imports/module.html +[`sys_read_entropy`]: https://github.com/hermit-os/kernel/blob/315f58ff5efc81d9bf0618af85a59963ff55f8b1/src/syscalls/entropy.rs#L47-L55 +[platform-support]: https://doc.rust-lang.org/stable/rustc/platform-support.html +[WASI]: https://github.com/WebAssembly/WASI +[Emscripten]: https://emscripten.org +[opt-in]: #opt-in-backends + +[//]: # (licenses) + +[LICENSE-APACHE]: https://github.com/rust-random/getrandom/blob/master/LICENSE-APACHE +[LICENSE-MIT]: https://github.com/rust-random/getrandom/blob/master/LICENSE-MIT + +[`Error::UNEXPECTED`]: https://docs.rs/getrandom/latest/getrandom/struct.Error.html#associatedconstant.UNEXPECTED +[`fill_uninit`]: https://docs.rs/getrandom/latest/getrandom/fn.fill_uninit.html diff --git a/tools/vendor/getrandom/SECURITY.md b/tools/vendor/getrandom/SECURITY.md new file mode 100644 index 0000000000..19bfb9a27a --- /dev/null +++ b/tools/vendor/getrandom/SECURITY.md @@ -0,0 +1,13 @@ +# Security Policy + +## Supported Versions + +Security updates are applied only to the latest release. + +## Reporting a Vulnerability + +If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released. + +Please disclose it at [security advisory](https://github.com/rust-random/getrandom/security/advisories/new). + +This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure. diff --git a/tools/vendor/getrandom/benches/buffer.rs b/tools/vendor/getrandom/benches/buffer.rs new file mode 100644 index 0000000000..0063a453ac --- /dev/null +++ b/tools/vendor/getrandom/benches/buffer.rs @@ -0,0 +1,121 @@ +#![feature(test, maybe_uninit_uninit_array_transpose)] +extern crate test; + +use std::{ + mem::{size_of, MaybeUninit}, + slice, +}; + +// Call getrandom on a zero-initialized stack buffer +#[inline(always)] +fn bench_fill() { + let mut buf = [0u8; N]; + getrandom::fill(&mut buf).unwrap(); + test::black_box(&buf[..]); +} + +// Call fill_uninit on an uninitialized stack buffer +#[inline(always)] +fn bench_fill_uninit() { + let mut uninit = [MaybeUninit::uninit(); N]; + let buf: &[u8] = getrandom::fill_uninit(&mut uninit).unwrap(); + test::black_box(buf); +} + +#[bench] +pub fn bench_u32(b: &mut test::Bencher) { + #[inline(never)] + fn inner() -> u32 { + getrandom::u32().unwrap() + } + b.bytes = 4; + b.iter(inner); +} +#[bench] +pub fn bench_u32_via_fill(b: &mut test::Bencher) { + #[inline(never)] + fn inner() -> u32 { + let mut res = MaybeUninit::::uninit(); + let dst: &mut [MaybeUninit] = + unsafe { slice::from_raw_parts_mut(res.as_mut_ptr().cast(), size_of::()) }; + getrandom::fill_uninit(dst).unwrap(); + unsafe { res.assume_init() } + } + b.bytes = 4; + b.iter(inner); +} + +#[bench] +pub fn bench_u64(b: &mut test::Bencher) { + #[inline(never)] + fn inner() -> u64 { + getrandom::u64().unwrap() + } + b.bytes = 8; + b.iter(inner); +} + +#[bench] +pub fn bench_u64_via_fill(b: &mut test::Bencher) { + #[inline(never)] + fn inner() -> u64 { + let mut res = MaybeUninit::::uninit(); + let dst: &mut [MaybeUninit] = + unsafe { slice::from_raw_parts_mut(res.as_mut_ptr().cast(), size_of::()) }; + getrandom::fill_uninit(dst).unwrap(); + unsafe { res.assume_init() } + } + b.bytes = 8; + b.iter(inner); +} + +// We benchmark using #[inline(never)] "inner" functions for two reasons: +// - Avoiding inlining reduces a source of variance when running benchmarks. +// - It is _much_ easier to get the assembly or IR for the inner loop. +// +// For example, using cargo-show-asm (https://github.com/pacak/cargo-show-asm), +// we can get the assembly for a particular benchmark's inner loop by running: +// cargo asm --bench buffer --release buffer::p384::bench_getrandom::inner +macro_rules! bench { + ( $name:ident, $size:expr ) => { + pub mod $name { + #[bench] + pub fn bench_fill(b: &mut test::Bencher) { + #[inline(never)] + fn inner() { + super::bench_fill::<{ $size }>() + } + + b.bytes = $size as u64; + b.iter(inner); + } + #[bench] + pub fn bench_fill_uninit(b: &mut test::Bencher) { + #[inline(never)] + fn inner() { + super::bench_fill_uninit::<{ $size }>() + } + + b.bytes = $size as u64; + b.iter(inner); + } + } + }; +} + +// 16 bytes (128 bits) is the size of an 128-bit AES key/nonce. +bench!(aes128, 128 / 8); + +// 32 bytes (256 bits) is the seed sized used for rand::thread_rng +// and the `random` value in a ClientHello/ServerHello for TLS. +// This is also the size of a 256-bit AES/HMAC/P-256/Curve25519 key +// and/or nonce. +bench!(p256, 256 / 8); + +// A P-384/HMAC-384 key and/or nonce. +bench!(p384, 384 / 8); + +// Initializing larger buffers is not the primary use case of this library, as +// this should normally be done by a userspace CSPRNG. However, we have a test +// here to see the effects of a lower (amortized) syscall overhead. +bench!(page, 4096); diff --git a/tools/vendor/getrandom/build.rs b/tools/vendor/getrandom/build.rs new file mode 100644 index 0000000000..28068d9774 --- /dev/null +++ b/tools/vendor/getrandom/build.rs @@ -0,0 +1,57 @@ +use std::{env, ffi::OsString, process::Command}; + +/// Tries to get the minor version of the Rust compiler in use. +/// If it fails for any reason, returns `None`. +/// +/// Based on the `rustc_version` crate. +fn rustc_minor_version() -> Option { + let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); + let mut cmd = if let Some(wrapper) = env::var_os("RUSTC_WRAPPER").filter(|w| !w.is_empty()) { + let mut cmd = Command::new(wrapper); + cmd.arg(rustc); + cmd + } else { + Command::new(rustc) + }; + + let out = cmd.arg("-vV").output().ok()?; + + if !out.status.success() { + return None; + } + + let stdout = std::str::from_utf8(&out.stdout).ok()?; + + // Assumes that the first line contains "rustc 1.xx.0-channel (abcdef 2025-01-01)" + // where "xx" is the minor version which we want to extract + let mut lines = stdout.lines(); + let first_line = lines.next()?; + let minor_ver_str = first_line.split('.').nth(1)?; + minor_ver_str.parse().ok() +} + +fn main() { + // Automatically detect cfg(sanitize = "memory") even if cfg(sanitize) isn't + // supported. Build scripts get cfg() info, even if the cfg is unstable. + println!("cargo:rerun-if-changed=build.rs"); + let sanitizers = std::env::var("CARGO_CFG_SANITIZE").unwrap_or_default(); + if sanitizers.contains("memory") { + println!("cargo:rustc-cfg=getrandom_msan"); + } + + // Use `RtlGenRandom` on older compiler versions since win7 targets + // TODO(MSRV 1.78): Remove this check + let target_family = env::var_os("CARGO_CFG_TARGET_FAMILY").and_then(|f| f.into_string().ok()); + if target_family.as_deref() == Some("windows") { + /// Minor version of the Rust compiler in which win7 targets were inroduced + const WIN7_INTRODUCED_MINOR_VER: u64 = 78; + + match rustc_minor_version() { + Some(minor_ver) if minor_ver < WIN7_INTRODUCED_MINOR_VER => { + println!("cargo:rustc-cfg=getrandom_backend=\"windows_legacy\""); + } + None => println!("cargo:warning=Couldn't detect minor version of the Rust compiler"), + _ => {} + } + } +} diff --git a/tools/vendor/getrandom/src/backends.rs b/tools/vendor/getrandom/src/backends.rs new file mode 100644 index 0000000000..991d413b5d --- /dev/null +++ b/tools/vendor/getrandom/src/backends.rs @@ -0,0 +1,209 @@ +//! System-specific implementations. +//! +//! This module should provide `fill_inner` with the signature +//! `fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error>`. +//! The function MUST fully initialize `dest` when `Ok(())` is returned; +//! the function may need to use `sanitizer::unpoison` as well. +//! The function MUST NOT ever write uninitialized bytes into `dest`, +//! regardless of what value it returns. + +cfg_if! { + if #[cfg(getrandom_backend = "custom")] { + mod custom; + pub use custom::*; + } else if #[cfg(getrandom_backend = "linux_getrandom")] { + mod getrandom; + mod sanitizer; + pub use getrandom::*; + } else if #[cfg(getrandom_backend = "linux_raw")] { + mod linux_raw; + mod sanitizer; + pub use linux_raw::*; + } else if #[cfg(getrandom_backend = "rdrand")] { + mod rdrand; + pub use rdrand::*; + } else if #[cfg(getrandom_backend = "rndr")] { + mod rndr; + pub use rndr::*; + } else if #[cfg(getrandom_backend = "efi_rng")] { + mod efi_rng; + pub use efi_rng::*; + } else if #[cfg(getrandom_backend = "windows_legacy")] { + mod windows_legacy; + pub use windows_legacy::*; + } else if #[cfg(getrandom_backend = "wasm_js")] { + cfg_if! { + if #[cfg(feature = "wasm_js")] { + mod wasm_js; + pub use wasm_js::*; + } else { + compile_error!(concat!( + "The \"wasm_js\" backend requires the `wasm_js` feature \ + for `getrandom`. For more information see: \ + https://docs.rs/getrandom/", env!("CARGO_PKG_VERSION"), "/#webassembly-support" + )); + } + } + } else if #[cfg(getrandom_backend = "unsupported")] { + mod unsupported; + pub use unsupported::*; + } else if #[cfg(all(target_os = "linux", target_env = ""))] { + mod linux_raw; + mod sanitizer; + pub use linux_raw::*; + } else if #[cfg(target_os = "espidf")] { + mod esp_idf; + pub use esp_idf::*; + } else if #[cfg(any( + target_os = "haiku", + target_os = "redox", + target_os = "nto", + target_os = "aix", + ))] { + mod use_file; + pub use use_file::*; + } else if #[cfg(any( + target_os = "macos", + target_os = "openbsd", + target_os = "vita", + target_os = "emscripten", + ))] { + mod getentropy; + pub use getentropy::*; + } else if #[cfg(any( + // Rust supports Android API level 19 (KitKat) [0] and the next upgrade targets + // level 21 (Lollipop) [1], while `getrandom(2)` was added only in + // level 23 (Marshmallow). Note that it applies only to the "old" `target_arch`es, + // RISC-V Android targets sufficiently new API level, same will apply for potential + // new Android `target_arch`es. + // [0]: https://blog.rust-lang.org/2023/01/09/android-ndk-update-r25.html + // [1]: https://github.com/rust-lang/rust/pull/120593 + all( + target_os = "android", + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "x86", + target_arch = "x86_64", + ), + ), + // Only on these `target_arch`es Rust supports Linux kernel versions (3.2+) + // that precede the version (3.17) in which `getrandom(2)` was added: + // https://doc.rust-lang.org/stable/rustc/platform-support.html + all( + target_os = "linux", + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "s390x", + target_arch = "x86", + target_arch = "x86_64", + // Minimum supported Linux kernel version for MUSL targets + // is not specified explicitly (as of Rust 1.77) and they + // are used in practice to target pre-3.17 kernels. + all( + target_env = "musl", + not( + any( + target_arch = "riscv64", + target_arch = "riscv32", + ), + ), + ), + ), + ) + ))] { + mod use_file; + mod linux_android_with_fallback; + mod sanitizer; + pub use linux_android_with_fallback::*; + } else if #[cfg(any( + target_os = "android", + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "hurd", + target_os = "illumos", + target_os = "cygwin", + // Check for target_arch = "arm" to only include the 3DS. Does not + // include the Nintendo Switch (which is target_arch = "aarch64"). + all(target_os = "horizon", target_arch = "arm"), + ))] { + mod getrandom; + #[cfg(any(target_os = "android", target_os = "linux"))] + mod sanitizer; + pub use getrandom::*; + } else if #[cfg(target_os = "solaris")] { + mod solaris; + pub use solaris::*; + } else if #[cfg(target_os = "netbsd")] { + mod netbsd; + pub use netbsd::*; + } else if #[cfg(target_os = "fuchsia")] { + mod fuchsia; + pub use fuchsia::*; + } else if #[cfg(any( + target_os = "ios", + target_os = "visionos", + target_os = "watchos", + target_os = "tvos", + ))] { + mod apple_other; + pub use apple_other::*; + } else if #[cfg(all(target_arch = "wasm32", target_os = "wasi"))] { + cfg_if! { + if #[cfg(target_env = "p1")] { + mod wasi_p1; + pub use wasi_p1::*; + } else if #[cfg(target_env = "p2")] { + mod wasi_p2; + pub use wasi_p2::*; + } else { + compile_error!( + "Unknown version of WASI (only previews 1 and 2 are supported) \ + or Rust version older than 1.80 was used" + ); + } + } + } else if #[cfg(target_os = "hermit")] { + mod hermit; + pub use hermit::*; + } else if #[cfg(target_os = "vxworks")] { + mod vxworks; + pub use vxworks::*; + } else if #[cfg(target_os = "solid_asp3")] { + mod solid; + pub use solid::*; + } else if #[cfg(all(windows, target_vendor = "win7"))] { + mod windows_legacy; + pub use windows_legacy::*; + } else if #[cfg(windows)] { + mod windows; + pub use windows::*; + } else if #[cfg(all(target_arch = "x86_64", target_env = "sgx"))] { + mod rdrand; + pub use rdrand::*; + } else if #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] { + cfg_if! { + if #[cfg(feature = "wasm_js")] { + mod wasm_js; + pub use wasm_js::*; + } else { + compile_error!(concat!( + "The wasm32-unknown-unknown targets are not supported by default; \ + you may need to enable the \"wasm_js\" configuration flag. Note \ + that enabling the `wasm_js` feature flag alone is insufficient. \ + For more information see: \ + https://docs.rs/getrandom/", env!("CARGO_PKG_VERSION"), "/#webassembly-support" + )); + } + } + } else { + compile_error!(concat!( + "target is not supported. You may need to define a custom backend see: \ + https://docs.rs/getrandom/", env!("CARGO_PKG_VERSION"), "/#custom-backend" + )); + } +} diff --git a/tools/vendor/getrandom/src/backends/apple_other.rs b/tools/vendor/getrandom/src/backends/apple_other.rs new file mode 100644 index 0000000000..c7b51c0e01 --- /dev/null +++ b/tools/vendor/getrandom/src/backends/apple_other.rs @@ -0,0 +1,21 @@ +//! Implementation for iOS, tvOS, and watchOS where `getentropy` is unavailable. +use crate::Error; +use core::{ffi::c_void, mem::MaybeUninit}; + +pub use crate::util::{inner_u32, inner_u64}; + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + let dst_ptr = dest.as_mut_ptr().cast::(); + let ret = unsafe { libc::CCRandomGenerateBytes(dst_ptr, dest.len()) }; + if ret == libc::kCCSuccess { + Ok(()) + } else { + Err(Error::IOS_RANDOM_GEN) + } +} + +impl Error { + /// Call to `CCRandomGenerateBytes` failed. + pub(crate) const IOS_RANDOM_GEN: Error = Self::new_internal(10); +} diff --git a/tools/vendor/getrandom/src/backends/custom.rs b/tools/vendor/getrandom/src/backends/custom.rs new file mode 100644 index 0000000000..c505481ad0 --- /dev/null +++ b/tools/vendor/getrandom/src/backends/custom.rs @@ -0,0 +1,13 @@ +//! An implementation which calls out to an externally defined function. +use crate::Error; +use core::mem::MaybeUninit; + +pub use crate::util::{inner_u32, inner_u64}; + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + extern "Rust" { + fn __getrandom_v03_custom(dest: *mut u8, len: usize) -> Result<(), Error>; + } + unsafe { __getrandom_v03_custom(dest.as_mut_ptr().cast(), dest.len()) } +} diff --git a/tools/vendor/getrandom/src/backends/efi_rng.rs b/tools/vendor/getrandom/src/backends/efi_rng.rs new file mode 100644 index 0000000000..768c8cc8ca --- /dev/null +++ b/tools/vendor/getrandom/src/backends/efi_rng.rs @@ -0,0 +1,124 @@ +//! Implementation for UEFI using EFI_RNG_PROTOCOL +use crate::Error; +use core::{ + mem::MaybeUninit, + ptr::{self, null_mut, NonNull}, + sync::atomic::{AtomicPtr, Ordering::Relaxed}, +}; +use r_efi::{ + efi::{BootServices, Handle}, + protocols::rng, +}; + +extern crate std; + +pub use crate::util::{inner_u32, inner_u64}; + +#[cfg(not(target_os = "uefi"))] +compile_error!("`efi_rng` backend can be enabled only for UEFI targets!"); + +static RNG_PROTOCOL: AtomicPtr = AtomicPtr::new(null_mut()); + +#[cold] +#[inline(never)] +fn init() -> Result, Error> { + const HANDLE_SIZE: usize = size_of::(); + + let boot_services = std::os::uefi::env::boot_services() + .ok_or(Error::BOOT_SERVICES_UNAVAILABLE)? + .cast::(); + + let mut handles = [ptr::null_mut(); 16]; + // `locate_handle` operates with length in bytes + let mut buf_size = handles.len() * HANDLE_SIZE; + let mut guid = rng::PROTOCOL_GUID; + let ret = unsafe { + ((*boot_services.as_ptr()).locate_handle)( + r_efi::efi::BY_PROTOCOL, + &mut guid, + null_mut(), + &mut buf_size, + handles.as_mut_ptr(), + ) + }; + + if ret.is_error() { + return Err(Error::from_uefi_code(ret.as_usize())); + } + + let handles_len = buf_size / HANDLE_SIZE; + let handles = handles.get(..handles_len).ok_or(Error::UNEXPECTED)?; + + let system_handle = std::os::uefi::env::image_handle(); + for &handle in handles { + let mut protocol: MaybeUninit<*mut rng::Protocol> = MaybeUninit::uninit(); + + let mut protocol_guid = rng::PROTOCOL_GUID; + let ret = unsafe { + ((*boot_services.as_ptr()).open_protocol)( + handle, + &mut protocol_guid, + protocol.as_mut_ptr().cast(), + system_handle.as_ptr(), + ptr::null_mut(), + r_efi::system::OPEN_PROTOCOL_GET_PROTOCOL, + ) + }; + + let protocol = if ret.is_error() { + continue; + } else { + let protocol = unsafe { protocol.assume_init() }; + NonNull::new(protocol).ok_or(Error::UNEXPECTED)? + }; + + // Try to use the acquired protocol handle + let mut buf = [0u8; 8]; + let mut alg_guid = rng::ALGORITHM_RAW; + let ret = unsafe { + ((*protocol.as_ptr()).get_rng)( + protocol.as_ptr(), + &mut alg_guid, + buf.len(), + buf.as_mut_ptr(), + ) + }; + + if ret.is_error() { + continue; + } + + RNG_PROTOCOL.store(protocol.as_ptr(), Relaxed); + return Ok(protocol); + } + Err(Error::NO_RNG_HANDLE) +} + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + let protocol = match NonNull::new(RNG_PROTOCOL.load(Relaxed)) { + Some(p) => p, + None => init()?, + }; + + let mut alg_guid = rng::ALGORITHM_RAW; + let ret = unsafe { + ((*protocol.as_ptr()).get_rng)( + protocol.as_ptr(), + &mut alg_guid, + dest.len(), + dest.as_mut_ptr().cast::(), + ) + }; + + if ret.is_error() { + Err(Error::from_uefi_code(ret.as_usize())) + } else { + Ok(()) + } +} + +impl Error { + pub(crate) const BOOT_SERVICES_UNAVAILABLE: Error = Self::new_internal(10); + pub(crate) const NO_RNG_HANDLE: Error = Self::new_internal(11); +} diff --git a/tools/vendor/getrandom/src/backends/esp_idf.rs b/tools/vendor/getrandom/src/backends/esp_idf.rs new file mode 100644 index 0000000000..4d1689dc72 --- /dev/null +++ b/tools/vendor/getrandom/src/backends/esp_idf.rs @@ -0,0 +1,21 @@ +//! Implementation for ESP-IDF +use crate::Error; +use core::{ffi::c_void, mem::MaybeUninit}; + +pub use crate::util::{inner_u32, inner_u64}; + +extern "C" { + fn esp_fill_random(buf: *mut c_void, len: usize) -> u32; +} + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + // Not that NOT enabling WiFi, BT, or the voltage noise entropy source (via `bootloader_random_enable`) + // will cause ESP-IDF to return pseudo-random numbers based on the voltage noise entropy, after the initial boot process: + // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html + // + // However tracking if some of these entropy sources is enabled is way too difficult to implement here + unsafe { esp_fill_random(dest.as_mut_ptr().cast(), dest.len()) }; + + Ok(()) +} diff --git a/tools/vendor/getrandom/src/backends/fuchsia.rs b/tools/vendor/getrandom/src/backends/fuchsia.rs new file mode 100644 index 0000000000..b5f1ade541 --- /dev/null +++ b/tools/vendor/getrandom/src/backends/fuchsia.rs @@ -0,0 +1,16 @@ +//! Implementation for Fuchsia Zircon +use crate::Error; +use core::mem::MaybeUninit; + +pub use crate::util::{inner_u32, inner_u64}; + +#[link(name = "zircon")] +extern "C" { + fn zx_cprng_draw(buffer: *mut u8, length: usize); +} + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + unsafe { zx_cprng_draw(dest.as_mut_ptr().cast::(), dest.len()) } + Ok(()) +} diff --git a/tools/vendor/getrandom/src/backends/getentropy.rs b/tools/vendor/getrandom/src/backends/getentropy.rs new file mode 100644 index 0000000000..ed181f0197 --- /dev/null +++ b/tools/vendor/getrandom/src/backends/getentropy.rs @@ -0,0 +1,27 @@ +//! Implementation using getentropy(2) +//! +//! Available since: +//! - macOS 10.12 +//! - OpenBSD 5.6 +//! - Emscripten 2.0.5 +//! - vita newlib since Dec 2021 +//! +//! For these targets, we use getentropy(2) because getrandom(2) doesn't exist. +use crate::Error; +use core::{ffi::c_void, mem::MaybeUninit}; + +pub use crate::util::{inner_u32, inner_u64}; + +#[path = "../util_libc.rs"] +mod util_libc; + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + for chunk in dest.chunks_mut(256) { + let ret = unsafe { libc::getentropy(chunk.as_mut_ptr().cast::(), chunk.len()) }; + if ret != 0 { + return Err(util_libc::last_os_error()); + } + } + Ok(()) +} diff --git a/tools/vendor/getrandom/src/backends/getrandom.rs b/tools/vendor/getrandom/src/backends/getrandom.rs new file mode 100644 index 0000000000..f3c33c3cb2 --- /dev/null +++ b/tools/vendor/getrandom/src/backends/getrandom.rs @@ -0,0 +1,39 @@ +//! Implementation using getrandom(2). +//! +//! Available since: +//! - Linux Kernel 3.17, Glibc 2.25, Musl 1.1.20 +//! - Android API level 23 (Marshmallow) +//! - NetBSD 10.0 +//! - FreeBSD 12.0 +//! - illumos since Dec 2018 +//! - DragonFly 5.7 +//! - Hurd Glibc 2.31 +//! - shim-3ds since Feb 2022 +//! +//! For these platforms, we always use the default pool and never set the +//! GRND_RANDOM flag to use the /dev/random pool. On Linux/Android/Hurd, using +//! GRND_RANDOM is not recommended. On NetBSD/FreeBSD/Dragonfly/3ds, it does +//! nothing. On illumos, the default pool is used to implement getentropy(2), +//! so we assume it is acceptable here. +use crate::Error; +use core::mem::MaybeUninit; + +pub use crate::util::{inner_u32, inner_u64}; + +#[path = "../util_libc.rs"] +mod util_libc; + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + util_libc::sys_fill_exact(dest, |buf| unsafe { + let ret = libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0); + + #[cfg(any(target_os = "android", target_os = "linux"))] + #[allow(unused_unsafe)] // TODO(MSRV 1.65): Remove this. + unsafe { + super::sanitizer::unpoison_linux_getrandom_result(buf, ret); + } + + ret + }) +} diff --git a/tools/vendor/getrandom/src/backends/hermit.rs b/tools/vendor/getrandom/src/backends/hermit.rs new file mode 100644 index 0000000000..34d7cdbb96 --- /dev/null +++ b/tools/vendor/getrandom/src/backends/hermit.rs @@ -0,0 +1,53 @@ +//! Implementation for Hermit +use crate::Error; +use core::mem::MaybeUninit; + +extern "C" { + fn sys_read_entropy(buffer: *mut u8, length: usize, flags: u32) -> isize; + // Note that `sys_secure_rand32/64` are implemented using `sys_read_entropy`: + // https://github.com/hermit-os/kernel/blob/430da84/src/syscalls/entropy.rs#L62-L104 + // But this may change in future and can depend on compilation target, + // so to future-proof we use these "syscalls". + fn sys_secure_rand32(value: *mut u32) -> i32; + fn sys_secure_rand64(value: *mut u64) -> i32; +} + +#[inline] +pub fn inner_u32() -> Result { + let mut res = MaybeUninit::uninit(); + let ret = unsafe { sys_secure_rand32(res.as_mut_ptr()) }; + match ret { + 0 => Ok(unsafe { res.assume_init() }), + -1 => Err(Error::UNSUPPORTED), + _ => Err(Error::UNEXPECTED), + } +} + +#[inline] +pub fn inner_u64() -> Result { + let mut res = MaybeUninit::uninit(); + let ret = unsafe { sys_secure_rand64(res.as_mut_ptr()) }; + match ret { + 0 => Ok(unsafe { res.assume_init() }), + -1 => Err(Error::UNSUPPORTED), + _ => Err(Error::UNEXPECTED), + } +} + +#[inline] +pub fn fill_inner(mut dest: &mut [MaybeUninit]) -> Result<(), Error> { + while !dest.is_empty() { + let res = unsafe { sys_read_entropy(dest.as_mut_ptr().cast::(), dest.len(), 0) }; + match res { + res if res > 0 => { + let len = usize::try_from(res).map_err(|_| Error::UNEXPECTED)?; + dest = dest.get_mut(len..).ok_or(Error::UNEXPECTED)?; + } + code => { + let code = i32::try_from(code).map_err(|_| Error::UNEXPECTED)?; + return Err(Error::from_neg_error_code(code)); + } + } + } + Ok(()) +} diff --git a/tools/vendor/getrandom/src/backends/linux_android_with_fallback.rs b/tools/vendor/getrandom/src/backends/linux_android_with_fallback.rs new file mode 100644 index 0000000000..6c9dd0653a --- /dev/null +++ b/tools/vendor/getrandom/src/backends/linux_android_with_fallback.rs @@ -0,0 +1,103 @@ +//! Implementation for Linux / Android with `/dev/urandom` fallback +use super::{sanitizer, use_file}; +use crate::Error; +use core::{ + ffi::c_void, + mem::{transmute, MaybeUninit}, + ptr::NonNull, + sync::atomic::{AtomicPtr, Ordering}, +}; +use use_file::util_libc; + +pub use crate::util::{inner_u32, inner_u64}; + +type GetRandomFn = unsafe extern "C" fn(*mut c_void, libc::size_t, libc::c_uint) -> libc::ssize_t; + +/// Sentinel value which indicates that `libc::getrandom` either not available, +/// or not supported by kernel. +const NOT_AVAILABLE: NonNull = unsafe { NonNull::new_unchecked(usize::MAX as *mut c_void) }; + +static GETRANDOM_FN: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); + +#[cold] +#[inline(never)] +fn init() -> NonNull { + // Use static linking to `libc::getrandom` on MUSL targets and `dlsym` everywhere else + #[cfg(not(target_env = "musl"))] + let raw_ptr = { + static NAME: &[u8] = b"getrandom\0"; + let name_ptr = NAME.as_ptr().cast::(); + unsafe { libc::dlsym(libc::RTLD_DEFAULT, name_ptr) } + }; + #[cfg(target_env = "musl")] + let raw_ptr = { + let fptr: GetRandomFn = libc::getrandom; + unsafe { transmute::(fptr) } + }; + + let res_ptr = match NonNull::new(raw_ptr) { + Some(fptr) => { + let getrandom_fn = unsafe { transmute::, GetRandomFn>(fptr) }; + let dangling_ptr = NonNull::dangling().as_ptr(); + // Check that `getrandom` syscall is supported by kernel + let res = unsafe { getrandom_fn(dangling_ptr, 0, 0) }; + if cfg!(getrandom_test_linux_fallback) { + NOT_AVAILABLE + } else if res.is_negative() { + match util_libc::last_os_error().raw_os_error() { + Some(libc::ENOSYS) => NOT_AVAILABLE, // No kernel support + // The fallback on EPERM is intentionally not done on Android since this workaround + // seems to be needed only for specific Linux-based products that aren't based + // on Android. See https://github.com/rust-random/getrandom/issues/229. + #[cfg(target_os = "linux")] + Some(libc::EPERM) => NOT_AVAILABLE, // Blocked by seccomp + _ => fptr, + } + } else { + fptr + } + } + None => NOT_AVAILABLE, + }; + + #[cfg(getrandom_test_linux_without_fallback)] + if res_ptr == NOT_AVAILABLE { + panic!("Fallback is triggered with enabled `getrandom_test_linux_without_fallback`") + } + + GETRANDOM_FN.store(res_ptr.as_ptr(), Ordering::Release); + res_ptr +} + +// Prevent inlining of the fallback implementation +#[inline(never)] +fn use_file_fallback(dest: &mut [MaybeUninit]) -> Result<(), Error> { + use_file::fill_inner(dest) +} + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + // Despite being only a single atomic variable, we still cannot always use + // Ordering::Relaxed, as we need to make sure a successful call to `init` + // is "ordered before" any data read through the returned pointer (which + // occurs when the function is called). Our implementation mirrors that of + // the one in libstd, meaning that the use of non-Relaxed operations is + // probably unnecessary. + let raw_ptr = GETRANDOM_FN.load(Ordering::Acquire); + let fptr = match NonNull::new(raw_ptr) { + Some(p) => p, + None => init(), + }; + + if fptr == NOT_AVAILABLE { + use_file_fallback(dest) + } else { + // note: `transmute` is currently the only way to convert a pointer into a function reference + let getrandom_fn = unsafe { transmute::, GetRandomFn>(fptr) }; + util_libc::sys_fill_exact(dest, |buf| unsafe { + let ret = getrandom_fn(buf.as_mut_ptr().cast(), buf.len(), 0); + sanitizer::unpoison_linux_getrandom_result(buf, ret); + ret + }) + } +} diff --git a/tools/vendor/getrandom/src/backends/linux_raw.rs b/tools/vendor/getrandom/src/backends/linux_raw.rs new file mode 100644 index 0000000000..2a74e58520 --- /dev/null +++ b/tools/vendor/getrandom/src/backends/linux_raw.rs @@ -0,0 +1,143 @@ +//! Implementation for Linux / Android using `asm!`-based syscalls. +use super::sanitizer; +pub use crate::util::{inner_u32, inner_u64}; +use crate::{Error, MaybeUninit}; + +#[cfg(not(any(target_os = "android", target_os = "linux")))] +compile_error!("`linux_raw` backend can be enabled only for Linux/Android targets!"); + +#[allow(non_upper_case_globals)] +unsafe fn getrandom_syscall(buf: *mut u8, buflen: usize, flags: u32) -> isize { + let r0; + + // Based on `rustix` and `linux-raw-sys` code. + cfg_if! { + if #[cfg(target_arch = "arm")] { + // TODO(MSRV-1.78): Also check `target_abi = "eabi"`. + // In thumb-mode, r7 is the frame pointer and is not permitted to be used in + // an inline asm operand, so we have to use a different register and copy it + // into r7 inside the inline asm. + // Theoretically, we could detect thumb mode in the build script, but several + // register moves are cheap enough compared to the syscall cost, so we do not + // bother with it. + core::arch::asm!( + "mov {tmp}, r7", + // TODO(MSRV-1.82): replace with `nr = const __NR_getrandom,` + "mov r7, #384", + "svc 0", + "mov r7, {tmp}", + tmp = out(reg) _, + inlateout("r0") buf => r0, + in("r1") buflen, + in("r2") flags, + options(nostack, preserves_flags) + ); + } else if #[cfg(target_arch = "aarch64")] { + // TODO(MSRV-1.78): Also check `any(target_abi = "", target_abi = "ilp32")` above. + // According to the ILP32 patch for the kernel that hasn't yet + // been merged into the mainline, "AARCH64/ILP32 ABI uses standard + // syscall table [...] with the exceptions listed below," where + // getrandom is not mentioned as an exception. + const __NR_getrandom: u32 = 278; + core::arch::asm!( + "svc 0", + in("x8") __NR_getrandom, + inlateout("x0") buf => r0, + in("x1") buflen, + in("x2") flags, + options(nostack, preserves_flags) + ); + } else if #[cfg(target_arch = "loongarch64")] { + // TODO(MSRV-1.78): Also check `any(target_abi = "", target_abi = "ilp32")` above. + const __NR_getrandom: u32 = 278; + core::arch::asm!( + "syscall 0", + in("$a7") __NR_getrandom, + inlateout("$a0") buf => r0, + in("$a1") buflen, + in("$a2") flags, + options(nostack, preserves_flags) + ); + } else if #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] { + const __NR_getrandom: u32 = 278; + core::arch::asm!( + "ecall", + in("a7") __NR_getrandom, + inlateout("a0") buf => r0, + in("a1") buflen, + in("a2") flags, + options(nostack, preserves_flags) + ); + } else if #[cfg(target_arch = "s390x")] { + const __NR_getrandom: u32 = 349; + core::arch::asm!( + "svc 0", + in("r1") __NR_getrandom, + inlateout("r2") buf => r0, + in("r3") buflen, + in("r4") flags, + options(nostack, preserves_flags) + ); + } else if #[cfg(target_arch = "x86")] { + const __NR_getrandom: u32 = 355; + // `int 0x80` is famously slow, but implementing vDSO is too complex + // and `sysenter`/`syscall` have their own portability issues, + // so we use the simple "legacy" way of doing syscalls. + core::arch::asm!( + "int $$0x80", + in("eax") __NR_getrandom, + in("ebx") buf, + in("ecx") buflen, + in("edx") flags, + lateout("eax") r0, + options(nostack, preserves_flags) + ); + } else if #[cfg(target_arch = "x86_64")] { + // TODO(MSRV-1.78): Add `any(target_abi = "", target_abi = "x32")` above. + const __X32_SYSCALL_BIT: u32 = 0x40000000; + const OFFSET: u32 = if cfg!(target_pointer_width = "32") { __X32_SYSCALL_BIT } else { 0 }; + const __NR_getrandom: u32 = OFFSET + 318; + + core::arch::asm!( + "syscall", + in("rax") __NR_getrandom, + in("rdi") buf, + in("rsi") buflen, + in("rdx") flags, + lateout("rax") r0, + lateout("rcx") _, + lateout("r11") _, + options(nostack, preserves_flags) + ); + } else { + compile_error!("`linux_raw` backend does not support this target arch"); + } + } + + r0 +} + +#[inline] +pub fn fill_inner(mut dest: &mut [MaybeUninit]) -> Result<(), Error> { + // Value of this error code is stable across all target arches. + const EINTR: isize = -4; + + loop { + let ret = unsafe { getrandom_syscall(dest.as_mut_ptr().cast(), dest.len(), 0) }; + unsafe { sanitizer::unpoison_linux_getrandom_result(dest, ret) }; + match usize::try_from(ret) { + Ok(0) => return Err(Error::UNEXPECTED), + Ok(len) => { + dest = dest.get_mut(len..).ok_or(Error::UNEXPECTED)?; + if dest.is_empty() { + return Ok(()); + } + } + Err(_) if ret == EINTR => continue, + Err(_) => { + let code = i32::try_from(ret).map_err(|_| Error::UNEXPECTED)?; + return Err(Error::from_neg_error_code(code)); + } + } + } +} diff --git a/tools/vendor/getrandom/src/backends/netbsd.rs b/tools/vendor/getrandom/src/backends/netbsd.rs new file mode 100644 index 0000000000..f228a8b138 --- /dev/null +++ b/tools/vendor/getrandom/src/backends/netbsd.rs @@ -0,0 +1,78 @@ +//! Implementation for NetBSD +//! +//! `getrandom(2)` was introduced in NetBSD 10. To support older versions we +//! implement our own weak linkage to it, and provide a fallback based on the +//! KERN_ARND sysctl. +use crate::Error; +use core::{ + cmp, + ffi::c_void, + mem::{self, MaybeUninit}, + ptr, + sync::atomic::{AtomicPtr, Ordering}, +}; + +pub use crate::util::{inner_u32, inner_u64}; + +#[path = "../util_libc.rs"] +mod util_libc; + +unsafe extern "C" fn polyfill_using_kern_arand( + buf: *mut c_void, + buflen: libc::size_t, + flags: libc::c_uint, +) -> libc::ssize_t { + debug_assert_eq!(flags, 0); + + const MIB_LEN: libc::c_uint = 2; + static MIB: [libc::c_int; MIB_LEN as usize] = [libc::CTL_KERN, libc::KERN_ARND]; + + // NetBSD will only return up to 256 bytes at a time, and + // older NetBSD kernels will fail on longer buffers. + let mut len = cmp::min(buflen, 256); + let ret = unsafe { libc::sysctl(MIB.as_ptr(), MIB_LEN, buf, &mut len, ptr::null(), 0) }; + + match ret { + 0 if len <= 256 => libc::ssize_t::try_from(len).expect("len is in the range of 0..=256"), + -1 => -1, + // Zero return result will be converted into `Error::UNEXPECTED` by `sys_fill_exact` + _ => 0, + } +} + +type GetRandomFn = unsafe extern "C" fn(*mut c_void, libc::size_t, libc::c_uint) -> libc::ssize_t; + +static GETRANDOM: AtomicPtr = AtomicPtr::new(ptr::null_mut()); + +#[cold] +#[inline(never)] +fn init() -> *mut c_void { + static NAME: &[u8] = b"getrandom\0"; + let name_ptr = NAME.as_ptr().cast::(); + let mut ptr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, name_ptr) }; + if ptr.is_null() || cfg!(getrandom_test_netbsd_fallback) { + // Verify `polyfill_using_kern_arand` has the right signature. + const POLYFILL: GetRandomFn = polyfill_using_kern_arand; + ptr = POLYFILL as *mut c_void; + } + GETRANDOM.store(ptr, Ordering::Release); + ptr +} + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + // Despite being only a single atomic variable, we still cannot always use + // Ordering::Relaxed, as we need to make sure a successful call to `init` + // is "ordered before" any data read through the returned pointer (which + // occurs when the function is called). Our implementation mirrors that of + // the one in libstd, meaning that the use of non-Relaxed operations is + // probably unnecessary. + let mut fptr = GETRANDOM.load(Ordering::Acquire); + if fptr.is_null() { + fptr = init(); + } + let fptr = unsafe { mem::transmute::<*mut c_void, GetRandomFn>(fptr) }; + util_libc::sys_fill_exact(dest, |buf| unsafe { + fptr(buf.as_mut_ptr().cast::(), buf.len(), 0) + }) +} diff --git a/tools/vendor/getrandom/src/backends/rdrand.rs b/tools/vendor/getrandom/src/backends/rdrand.rs new file mode 100644 index 0000000000..609fcc386f --- /dev/null +++ b/tools/vendor/getrandom/src/backends/rdrand.rs @@ -0,0 +1,182 @@ +//! RDRAND backend for x86(-64) targets +use crate::{util::slice_as_uninit, Error}; +use core::mem::{size_of, MaybeUninit}; + +#[path = "../lazy.rs"] +mod lazy; + +#[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))] +compile_error!("`rdrand` backend can be enabled only for x86 and x86-64 targets!"); + +cfg_if! { + if #[cfg(target_arch = "x86_64")] { + use core::arch::x86_64 as arch; + use arch::_rdrand64_step as rdrand_step; + type Word = u64; + } else if #[cfg(target_arch = "x86")] { + use core::arch::x86 as arch; + use arch::_rdrand32_step as rdrand_step; + type Word = u32; + } +} + +static RDRAND_GOOD: lazy::LazyBool = lazy::LazyBool::new(); + +// Recommendation from "Intel® Digital Random Number Generator (DRNG) Software +// Implementation Guide" - Section 5.2.1 and "Intel® 64 and IA-32 Architectures +// Software Developer’s Manual" - Volume 1 - Section 7.3.17.1. +const RETRY_LIMIT: usize = 10; + +#[target_feature(enable = "rdrand")] +unsafe fn rdrand() -> Option { + for _ in 0..RETRY_LIMIT { + let mut val = 0; + if rdrand_step(&mut val) == 1 { + return Some(val); + } + } + None +} + +// "rdrand" target feature requires "+rdrand" flag, see https://github.com/rust-lang/rust/issues/49653. +#[cfg(all(target_env = "sgx", not(target_feature = "rdrand")))] +compile_error!( + "SGX targets require 'rdrand' target feature. Enable by using -C target-feature=+rdrand." +); + +// Run a small self-test to make sure we aren't repeating values +// Adapted from Linux's test in arch/x86/kernel/cpu/rdrand.c +// Fails with probability < 2^(-90) on 32-bit systems +#[target_feature(enable = "rdrand")] +unsafe fn self_test() -> bool { + // On AMD, RDRAND returns 0xFF...FF on failure, count it as a collision. + let mut prev = !0; // TODO(MSRV 1.43): Move to usize::MAX + let mut fails = 0; + for _ in 0..8 { + match rdrand() { + Some(val) if val == prev => fails += 1, + Some(val) => prev = val, + None => return false, + }; + } + fails <= 2 +} + +fn is_rdrand_good() -> bool { + #[cfg(not(target_feature = "rdrand"))] + { + // SAFETY: All Rust x86 targets are new enough to have CPUID, and we + // check that leaf 1 is supported before using it. + let cpuid0 = unsafe { arch::__cpuid(0) }; + if cpuid0.eax < 1 { + return false; + } + let cpuid1 = unsafe { arch::__cpuid(1) }; + + let vendor_id = [ + cpuid0.ebx.to_le_bytes(), + cpuid0.edx.to_le_bytes(), + cpuid0.ecx.to_le_bytes(), + ]; + if vendor_id == [*b"Auth", *b"enti", *b"cAMD"] { + let mut family = (cpuid1.eax >> 8) & 0xF; + if family == 0xF { + family += (cpuid1.eax >> 20) & 0xFF; + } + // AMD CPUs families before 17h (Zen) sometimes fail to set CF when + // RDRAND fails after suspend. Don't use RDRAND on those families. + // See https://bugzilla.redhat.com/show_bug.cgi?id=1150286 + if family < 0x17 { + return false; + } + } + + const RDRAND_FLAG: u32 = 1 << 30; + if cpuid1.ecx & RDRAND_FLAG == 0 { + return false; + } + } + + // SAFETY: We have already checked that rdrand is available. + unsafe { self_test() } +} + +// TODO: make this function safe when we have feature(target_feature_11) +#[target_feature(enable = "rdrand")] +unsafe fn rdrand_exact(dest: &mut [MaybeUninit]) -> Option<()> { + // We use chunks_exact_mut instead of chunks_mut as it allows almost all + // calls to memcpy to be elided by the compiler. + let mut chunks = dest.chunks_exact_mut(size_of::()); + for chunk in chunks.by_ref() { + let src = rdrand()?.to_ne_bytes(); + chunk.copy_from_slice(slice_as_uninit(&src)); + } + + let tail = chunks.into_remainder(); + let n = tail.len(); + if n > 0 { + let src = rdrand()?.to_ne_bytes(); + tail.copy_from_slice(slice_as_uninit(&src[..n])); + } + Some(()) +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "rdrand")] +unsafe fn rdrand_u32() -> Option { + rdrand().map(crate::util::truncate) +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "rdrand")] +unsafe fn rdrand_u64() -> Option { + rdrand() +} + +#[cfg(target_arch = "x86")] +#[target_feature(enable = "rdrand")] +unsafe fn rdrand_u32() -> Option { + rdrand() +} + +#[cfg(target_arch = "x86")] +#[target_feature(enable = "rdrand")] +unsafe fn rdrand_u64() -> Option { + let a = rdrand()?; + let b = rdrand()?; + Some((u64::from(a) << 32) | u64::from(b)) +} + +#[inline] +pub fn inner_u32() -> Result { + if !RDRAND_GOOD.unsync_init(is_rdrand_good) { + return Err(Error::NO_RDRAND); + } + // SAFETY: After this point, we know rdrand is supported. + unsafe { rdrand_u32() }.ok_or(Error::FAILED_RDRAND) +} + +#[inline] +pub fn inner_u64() -> Result { + if !RDRAND_GOOD.unsync_init(is_rdrand_good) { + return Err(Error::NO_RDRAND); + } + // SAFETY: After this point, we know rdrand is supported. + unsafe { rdrand_u64() }.ok_or(Error::FAILED_RDRAND) +} + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + if !RDRAND_GOOD.unsync_init(is_rdrand_good) { + return Err(Error::NO_RDRAND); + } + // SAFETY: After this point, we know rdrand is supported. + unsafe { rdrand_exact(dest) }.ok_or(Error::FAILED_RDRAND) +} + +impl Error { + /// RDRAND instruction failed due to a hardware issue. + pub(crate) const FAILED_RDRAND: Error = Self::new_internal(10); + /// RDRAND instruction unsupported on this target. + pub(crate) const NO_RDRAND: Error = Self::new_internal(11); +} diff --git a/tools/vendor/getrandom/src/backends/rndr.rs b/tools/vendor/getrandom/src/backends/rndr.rs new file mode 100644 index 0000000000..eea741a2dc --- /dev/null +++ b/tools/vendor/getrandom/src/backends/rndr.rs @@ -0,0 +1,145 @@ +//! RNDR register backend for aarch64 targets +//! +//! Arm Architecture Reference Manual for A-profile architecture: +//! ARM DDI 0487K.a, ID032224, D23.2.147 RNDR, Random Number +use crate::{ + util::{slice_as_uninit, truncate}, + Error, +}; +use core::arch::asm; +use core::mem::{size_of, MaybeUninit}; + +#[cfg(not(target_arch = "aarch64"))] +compile_error!("the `rndr` backend can be enabled only for AArch64 targets!"); + +const RETRY_LIMIT: usize = 5; + +/// Read a random number from the aarch64 RNDR register +/// +/// Callers must ensure that FEAT_RNG is available on the system +/// The function assumes that the RNDR register is available +/// If it fails to read a random number, it will retry up to 5 times +/// After 5 failed reads the function will return `None` +#[target_feature(enable = "rand")] +unsafe fn rndr() -> Option { + for _ in 0..RETRY_LIMIT { + let mut x: u64; + let mut nzcv: u64; + + // AArch64 RNDR register is accessible by s3_3_c2_c4_0 + asm!( + "mrs {x}, RNDR", + "mrs {nzcv}, NZCV", + x = out(reg) x, + nzcv = out(reg) nzcv, + ); + + // If the hardware returns a genuine random number, PSTATE.NZCV is set to 0b0000 + if nzcv == 0 { + return Some(x); + } + } + + None +} + +#[target_feature(enable = "rand")] +unsafe fn rndr_fill(dest: &mut [MaybeUninit]) -> Option<()> { + let mut chunks = dest.chunks_exact_mut(size_of::()); + for chunk in chunks.by_ref() { + let src = rndr()?.to_ne_bytes(); + chunk.copy_from_slice(slice_as_uninit(&src)); + } + + let tail = chunks.into_remainder(); + let n = tail.len(); + if n > 0 { + let src = rndr()?.to_ne_bytes(); + tail.copy_from_slice(slice_as_uninit(&src[..n])); + } + Some(()) +} + +#[cfg(target_feature = "rand")] +fn is_rndr_available() -> bool { + true +} + +#[cfg(not(target_feature = "rand"))] +fn is_rndr_available() -> bool { + #[path = "../lazy.rs"] + mod lazy; + static RNDR_GOOD: lazy::LazyBool = lazy::LazyBool::new(); + + cfg_if::cfg_if! { + if #[cfg(feature = "std")] { + extern crate std; + RNDR_GOOD.unsync_init(|| std::arch::is_aarch64_feature_detected!("rand")) + } else if #[cfg(target_os = "linux")] { + /// Check whether FEAT_RNG is available on the system + /// + /// Requires the caller either be running in EL1 or be on a system supporting MRS + /// emulation. Due to the above, the implementation is currently restricted to Linux. + /// + /// Relying on runtime detection bumps minimum supported Linux kernel version to 4.11. + fn mrs_check() -> bool { + let mut id_aa64isar0: u64; + + // If FEAT_RNG is implemented, ID_AA64ISAR0_EL1.RNDR (bits 60-63) are 0b0001 + // This is okay to do from EL0 in Linux because Linux will emulate MRS as per + // https://docs.kernel.org/arch/arm64/cpu-feature-registers.html + unsafe { + asm!( + "mrs {id}, ID_AA64ISAR0_EL1", + id = out(reg) id_aa64isar0, + ); + } + + (id_aa64isar0 >> 60) & 0xf >= 1 + } + + RNDR_GOOD.unsync_init(mrs_check) + } else { + compile_error!( + "RNDR `no_std` runtime detection is currently supported only on Linux targets. \ + Either enable the `std` crate feature, or `rand` target feature at compile time." + ); + } + } +} + +#[inline] +pub fn inner_u32() -> Result { + if !is_rndr_available() { + return Err(Error::RNDR_NOT_AVAILABLE); + } + // SAFETY: after this point, we know the `rand` target feature is enabled + let res = unsafe { rndr() }; + res.map(truncate).ok_or(Error::RNDR_FAILURE) +} + +#[inline] +pub fn inner_u64() -> Result { + if !is_rndr_available() { + return Err(Error::RNDR_NOT_AVAILABLE); + } + // SAFETY: after this point, we know the `rand` target feature is enabled + let res = unsafe { rndr() }; + res.ok_or(Error::RNDR_FAILURE) +} + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + if !is_rndr_available() { + return Err(Error::RNDR_NOT_AVAILABLE); + } + // SAFETY: after this point, we know the `rand` target feature is enabled + unsafe { rndr_fill(dest).ok_or(Error::RNDR_FAILURE) } +} + +impl Error { + /// RNDR register read failed due to a hardware issue. + pub(crate) const RNDR_FAILURE: Error = Self::new_internal(10); + /// RNDR register is not supported on this target. + pub(crate) const RNDR_NOT_AVAILABLE: Error = Self::new_internal(11); +} diff --git a/tools/vendor/getrandom/src/backends/sanitizer.rs b/tools/vendor/getrandom/src/backends/sanitizer.rs new file mode 100644 index 0000000000..0e074d43ec --- /dev/null +++ b/tools/vendor/getrandom/src/backends/sanitizer.rs @@ -0,0 +1,59 @@ +use core::mem::MaybeUninit; + +/// Unpoisons `buf` if MSAN support is enabled. +/// +/// Most backends do not need to unpoison their output. Rust language- and +/// library- provided functionality unpoisons automatically. Similarly, libc +/// either natively supports MSAN and/or MSAN hooks libc-provided functions +/// to unpoison outputs on success. Only when all of these things are +/// bypassed do we need to do it ourselves. +/// +/// The call to unpoison should be done as close to the write as possible. +/// For example, if the backend partially fills the output buffer in chunks, +/// each chunk should be unpoisoned individually. This way, the correctness of +/// the chunking logic can be validated (in part) using MSAN. +pub unsafe fn unpoison(buf: &mut [MaybeUninit]) { + cfg_if! { + if #[cfg(getrandom_msan)] { + extern "C" { + fn __msan_unpoison(a: *mut core::ffi::c_void, size: usize); + } + let a = buf.as_mut_ptr().cast(); + let size = buf.len(); + #[allow(unused_unsafe)] // TODO(MSRV 1.65): Remove this. + unsafe { + __msan_unpoison(a, size); + } + } else { + let _ = buf; + } + } +} + +/// Interprets the result of the `getrandom` syscall of Linux, unpoisoning any +/// written part of `buf`. +/// +/// `buf` must be the output buffer that was originally passed to the `getrandom` +/// syscall. +/// +/// `ret` must be the result returned by `getrandom`. If `ret` is negative or +/// larger than the length of `buf` then nothing is done. +/// +/// Memory Sanitizer only intercepts `getrandom` on this condition (from its +/// source code): +/// ```c +/// #define SANITIZER_INTERCEPT_GETRANDOM \ +/// ((SI_LINUX && __GLIBC_PREREQ(2, 25)) || SI_FREEBSD || SI_SOLARIS) +/// ``` +/// So, effectively, we have to assume that it is never intercepted on Linux. +#[cfg(any(target_os = "android", target_os = "linux"))] +pub unsafe fn unpoison_linux_getrandom_result(buf: &mut [MaybeUninit], ret: isize) { + if let Ok(bytes_written) = usize::try_from(ret) { + if let Some(written) = buf.get_mut(..bytes_written) { + #[allow(unused_unsafe)] // TODO(MSRV 1.65): Remove this. + unsafe { + unpoison(written) + } + } + } +} diff --git a/tools/vendor/getrandom/src/backends/solaris.rs b/tools/vendor/getrandom/src/backends/solaris.rs new file mode 100644 index 0000000000..c27f91a5ff --- /dev/null +++ b/tools/vendor/getrandom/src/backends/solaris.rs @@ -0,0 +1,42 @@ +//! Solaris implementation using getrandom(2). +//! +//! While getrandom(2) has been available since Solaris 11.3, it has a few +//! quirks not present on other OSes. First, on Solaris 11.3, calls will always +//! fail if bufsz > 1024. Second, it will always either fail or completely fill +//! the buffer (returning bufsz). Third, error is indicated by returning 0, +//! rather than by returning -1. Finally, "if GRND_RANDOM is not specified +//! then getrandom(2) is always a non blocking call". This _might_ imply that +//! in early-boot scenarios with low entropy, getrandom(2) will not properly +//! block. To be safe, we set GRND_RANDOM, mirroring the man page examples. +//! +//! For more information, see the man page linked in lib.rs and this blog post: +//! https://blogs.oracle.com/solaris/post/solaris-new-system-calls-getentropy2-and-getrandom2 +//! which also explains why this crate should not use getentropy(2). +use crate::Error; +use core::{ffi::c_void, mem::MaybeUninit}; + +pub use crate::util::{inner_u32, inner_u64}; + +#[path = "../util_libc.rs"] +mod util_libc; + +const MAX_BYTES: usize = 1024; + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + for chunk in dest.chunks_mut(MAX_BYTES) { + let ptr = chunk.as_mut_ptr().cast::(); + let ret = unsafe { libc::getrandom(ptr, chunk.len(), libc::GRND_RANDOM) }; + // In case the man page has a typo, we also check for negative ret. + // If getrandom(2) succeeds, it should have completely filled chunk. + match usize::try_from(ret) { + // Good. Keep going. + Ok(ret) if ret == chunk.len() => {} + // The syscall failed. + Ok(0) => return Err(util_libc::last_os_error()), + // All other cases should be impossible. + _ => return Err(Error::UNEXPECTED), + } + } + Ok(()) +} diff --git a/tools/vendor/getrandom/src/backends/solid.rs b/tools/vendor/getrandom/src/backends/solid.rs new file mode 100644 index 0000000000..caa773f817 --- /dev/null +++ b/tools/vendor/getrandom/src/backends/solid.rs @@ -0,0 +1,19 @@ +//! Implementation for SOLID +use crate::Error; +use core::mem::MaybeUninit; + +pub use crate::util::{inner_u32, inner_u64}; + +extern "C" { + pub fn SOLID_RNG_SampleRandomBytes(buffer: *mut u8, length: usize) -> i32; +} + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + let ret = unsafe { SOLID_RNG_SampleRandomBytes(dest.as_mut_ptr().cast::(), dest.len()) }; + if ret >= 0 { + Ok(()) + } else { + Err(Error::from_neg_error_code(ret)) + } +} diff --git a/tools/vendor/getrandom/src/backends/unsupported.rs b/tools/vendor/getrandom/src/backends/unsupported.rs new file mode 100644 index 0000000000..4ea381fc40 --- /dev/null +++ b/tools/vendor/getrandom/src/backends/unsupported.rs @@ -0,0 +1,9 @@ +//! Implementation that errors at runtime. +use crate::Error; +use core::mem::MaybeUninit; + +pub use crate::util::{inner_u32, inner_u64}; + +pub fn fill_inner(_dest: &mut [MaybeUninit]) -> Result<(), Error> { + Err(Error::UNSUPPORTED) +} diff --git a/tools/vendor/getrandom/src/backends/use_file.rs b/tools/vendor/getrandom/src/backends/use_file.rs new file mode 100644 index 0000000000..071ce930b9 --- /dev/null +++ b/tools/vendor/getrandom/src/backends/use_file.rs @@ -0,0 +1,234 @@ +//! Implementations that just need to read from a file +use crate::Error; +use core::{ + ffi::c_void, + mem::MaybeUninit, + sync::atomic::{AtomicI32, Ordering}, +}; + +#[cfg(not(any(target_os = "android", target_os = "linux")))] +pub use crate::util::{inner_u32, inner_u64}; + +#[path = "../util_libc.rs"] +pub(super) mod util_libc; + +/// For all platforms, we use `/dev/urandom` rather than `/dev/random`. +/// For more information see the linked man pages in lib.rs. +/// - On Linux, "/dev/urandom is preferred and sufficient in all use cases". +/// - On Redox, only /dev/urandom is provided. +/// - On AIX, /dev/urandom will "provide cryptographically secure output". +/// - On Haiku and QNX Neutrino they are identical. +const FILE_PATH: &[u8] = b"/dev/urandom\0"; + +// File descriptor is a "nonnegative integer", so we can safely use negative sentinel values. +const FD_UNINIT: libc::c_int = -1; +const FD_ONGOING_INIT: libc::c_int = -2; + +// In theory `libc::c_int` could be something other than `i32`, but for the +// targets we currently support that use `use_file`, it is always `i32`. +// If/when we add support for a target where that isn't the case, we may +// need to use a different atomic type or make other accommodations. The +// compiler will let us know if/when that is the case, because the +// `FD.store(fd)` would fail to compile. +// +// The opening of the file, by libc/libstd/etc. may write some unknown +// state into in-process memory. (Such state may include some sanitizer +// bookkeeping, or we might be operating in a unikernal-like environment +// where all the "kernel" file descriptor bookkeeping is done in our +// process.) `get_fd_locked` stores into FD using `Ordering::Release` to +// ensure any such state is synchronized. `get_fd` loads from `FD` with +// `Ordering::Acquire` to synchronize with it. +static FD: AtomicI32 = AtomicI32::new(FD_UNINIT); + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + let mut fd = FD.load(Ordering::Acquire); + if fd == FD_UNINIT || fd == FD_ONGOING_INIT { + fd = open_or_wait()?; + } + util_libc::sys_fill_exact(dest, |buf| unsafe { + libc::read(fd, buf.as_mut_ptr().cast::(), buf.len()) + }) +} + +/// Open a file in read-only mode. +/// +/// # Panics +/// If `path` does not contain any zeros. +// TODO: Move `path` to `CStr` and use `CStr::from_bytes_until_nul` (MSRV 1.69) +// or C-string literals (MSRV 1.77) for statics +fn open_readonly(path: &[u8]) -> Result { + assert!(path.contains(&0)); + loop { + let fd = unsafe { + libc::open( + path.as_ptr().cast::(), + libc::O_RDONLY | libc::O_CLOEXEC, + ) + }; + if fd >= 0 { + return Ok(fd); + } + let err = util_libc::last_os_error(); + // We should try again if open() was interrupted. + if err.raw_os_error() != Some(libc::EINTR) { + return Err(err); + } + } +} + +#[cold] +#[inline(never)] +fn open_or_wait() -> Result { + loop { + match FD.load(Ordering::Acquire) { + FD_UNINIT => { + let res = FD.compare_exchange_weak( + FD_UNINIT, + FD_ONGOING_INIT, + Ordering::AcqRel, + Ordering::Relaxed, + ); + if res.is_ok() { + break; + } + } + FD_ONGOING_INIT => sync::wait(), + fd => return Ok(fd), + } + } + + let res = open_fd(); + let val = match res { + Ok(fd) => fd, + Err(_) => FD_UNINIT, + }; + FD.store(val, Ordering::Release); + + // On non-Linux targets `wait` is just 1 ms sleep, + // so we don't need any explicit wake up in addition + // to updating value of `FD`. + #[cfg(any(target_os = "android", target_os = "linux"))] + sync::wake(); + + res +} + +fn open_fd() -> Result { + #[cfg(any(target_os = "android", target_os = "linux"))] + sync::wait_until_rng_ready()?; + let fd = open_readonly(FILE_PATH)?; + debug_assert!(fd >= 0); + Ok(fd) +} + +#[cfg(not(any(target_os = "android", target_os = "linux")))] +mod sync { + /// Sleep 1 ms before checking `FD` again. + /// + /// On non-Linux targets the critical section only opens file, + /// which should not block, so in the unlikely contended case, + /// we can sleep-wait for the opening operation to finish. + pub(super) fn wait() { + let rqtp = libc::timespec { + tv_sec: 0, + tv_nsec: 1_000_000, + }; + let mut rmtp = libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }; + // We do not care if sleep gets interrupted, so the return value is ignored + unsafe { + libc::nanosleep(&rqtp, &mut rmtp); + } + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +mod sync { + use super::{open_readonly, util_libc::last_os_error, Error, FD, FD_ONGOING_INIT}; + + /// Wait for atomic `FD` to change value from `FD_ONGOING_INIT` to something else. + /// + /// Futex syscall with `FUTEX_WAIT` op puts the current thread to sleep + /// until futex syscall with `FUTEX_WAKE` op gets executed for `FD`. + /// + /// For more information read: https://www.man7.org/linux/man-pages/man2/futex.2.html + pub(super) fn wait() { + let op = libc::FUTEX_WAIT | libc::FUTEX_PRIVATE_FLAG; + let timeout_ptr = core::ptr::null::(); + let ret = unsafe { libc::syscall(libc::SYS_futex, &FD, op, FD_ONGOING_INIT, timeout_ptr) }; + // FUTEX_WAIT should return either 0 or EAGAIN error + debug_assert!({ + match ret { + 0 => true, + -1 => last_os_error().raw_os_error() == Some(libc::EAGAIN), + _ => false, + } + }); + } + + /// Wake up all threads which wait for value of atomic `FD` to change. + pub(super) fn wake() { + let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG; + let ret = unsafe { libc::syscall(libc::SYS_futex, &FD, op, libc::INT_MAX) }; + debug_assert!(ret >= 0); + } + + // Polls /dev/random to make sure it is ok to read from /dev/urandom. + // + // Polling avoids draining the estimated entropy from /dev/random; + // short-lived processes reading even a single byte from /dev/random could + // be problematic if they are being executed faster than entropy is being + // collected. + // + // OTOH, reading a byte instead of polling is more compatible with + // sandboxes that disallow `poll()` but which allow reading /dev/random, + // e.g. sandboxes that assume that `poll()` is for network I/O. This way, + // fewer applications will have to insert pre-sandbox-initialization logic. + // Often (blocking) file I/O is not allowed in such early phases of an + // application for performance and/or security reasons. + // + // It is hard to write a sandbox policy to support `libc::poll()` because + // it may invoke the `poll`, `ppoll`, `ppoll_time64` (since Linux 5.1, with + // newer versions of glibc), and/or (rarely, and probably only on ancient + // systems) `select`. depending on the libc implementation (e.g. glibc vs + // musl), libc version, potentially the kernel version at runtime, and/or + // the target architecture. + // + // BoringSSL and libstd don't try to protect against insecure output from + // `/dev/urandom'; they don't open `/dev/random` at all. + // + // OpenSSL uses `libc::select()` unless the `dev/random` file descriptor + // is too large; if it is too large then it does what we do here. + // + // libsodium uses `libc::poll` similarly to this. + pub(super) fn wait_until_rng_ready() -> Result<(), Error> { + let fd = open_readonly(b"/dev/random\0")?; + let mut pfd = libc::pollfd { + fd, + events: libc::POLLIN, + revents: 0, + }; + + let res = loop { + // A negative timeout means an infinite timeout. + let res = unsafe { libc::poll(&mut pfd, 1, -1) }; + if res >= 0 { + // We only used one fd, and cannot timeout. + debug_assert_eq!(res, 1); + break Ok(()); + } + let err = last_os_error(); + // Assuming that `poll` is called correctly, + // on Linux it can return only EINTR and ENOMEM errors. + match err.raw_os_error() { + Some(libc::EINTR) => continue, + _ => break Err(err), + } + }; + unsafe { libc::close(fd) }; + res + } +} diff --git a/tools/vendor/getrandom/src/backends/vxworks.rs b/tools/vendor/getrandom/src/backends/vxworks.rs new file mode 100644 index 0000000000..5f5e6773b3 --- /dev/null +++ b/tools/vendor/getrandom/src/backends/vxworks.rs @@ -0,0 +1,54 @@ +//! Implementation for VxWorks +use crate::Error; +use core::{ + cmp::Ordering::{Equal, Greater, Less}, + mem::MaybeUninit, + sync::atomic::{AtomicBool, Ordering::Relaxed}, +}; + +#[path = "../util_libc.rs"] +mod util_libc; + +pub use crate::util::{inner_u32, inner_u64}; + +static RNG_INIT: AtomicBool = AtomicBool::new(false); + +#[cold] +fn init() -> Result<(), Error> { + let ret = unsafe { libc::randSecure() }; + match ret.cmp(&0) { + Greater => RNG_INIT.store(true, Relaxed), + Equal => unsafe { + libc::usleep(10); + }, + Less => return Err(Error::VXWORKS_RAND_SECURE), + } + Ok(()) +} + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + while !RNG_INIT.load(Relaxed) { + init()?; + } + + // Prevent overflow of i32 + let chunk_size = usize::try_from(i32::MAX).expect("VxWorks does not support 16-bit targets"); + for chunk in dest.chunks_mut(chunk_size) { + let chunk_len: libc::c_int = chunk + .len() + .try_into() + .expect("chunk size is bounded by i32::MAX"); + let p: *mut libc::c_uchar = chunk.as_mut_ptr().cast(); + let ret = unsafe { libc::randABytes(p, chunk_len) }; + if ret != 0 { + return Err(util_libc::last_os_error()); + } + } + Ok(()) +} + +impl Error { + /// On VxWorks, call to `randSecure` failed (random number generator is not yet initialized). + pub(crate) const VXWORKS_RAND_SECURE: Error = Self::new_internal(10); +} diff --git a/tools/vendor/getrandom/src/backends/wasi_p1.rs b/tools/vendor/getrandom/src/backends/wasi_p1.rs new file mode 100644 index 0000000000..25b5ca3b74 --- /dev/null +++ b/tools/vendor/getrandom/src/backends/wasi_p1.rs @@ -0,0 +1,32 @@ +//! Implementation for WASI Preview 1 +use crate::Error; +use core::mem::MaybeUninit; + +pub use crate::util::{inner_u32, inner_u64}; + +// This linking is vendored from the wasi crate: +// https://docs.rs/wasi/0.11.0+wasi-snapshot-preview1/src/wasi/lib_generated.rs.html#2344-2350 +#[link(wasm_import_module = "wasi_snapshot_preview1")] +extern "C" { + fn random_get(arg0: i32, arg1: i32) -> i32; +} + +/// WASI p1 uses `u16` for error codes in its witx definitions: +/// https://github.com/WebAssembly/WASI/blob/38454e9e/legacy/preview1/witx/typenames.witx#L34-L39 +const MAX_ERROR_CODE: i32 = u16::MAX as i32; + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + // Based on the wasi code: + // https://docs.rs/wasi/0.11.0+wasi-snapshot-preview1/src/wasi/lib_generated.rs.html#2046-2062 + // Note that size of an allocated object can not be bigger than isize::MAX bytes. + // WASI 0.1 supports only 32-bit WASM, so casting length to `i32` is safe. + #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] + let ret = unsafe { random_get(dest.as_mut_ptr() as i32, dest.len() as i32) }; + match ret { + 0 => Ok(()), + // WASI functions should return positive error codes which are smaller than `MAX_ERROR_CODE` + code if code <= MAX_ERROR_CODE => Err(Error::from_neg_error_code(-code)), + _ => Err(Error::UNEXPECTED), + } +} diff --git a/tools/vendor/getrandom/src/backends/wasi_p2.rs b/tools/vendor/getrandom/src/backends/wasi_p2.rs new file mode 100644 index 0000000000..d16fdff99c --- /dev/null +++ b/tools/vendor/getrandom/src/backends/wasi_p2.rs @@ -0,0 +1,47 @@ +//! Implementation for WASI Preview 2. +use crate::Error; +use core::{mem::MaybeUninit, ptr::copy_nonoverlapping}; +use wasip2::random::random::get_random_u64; + +#[inline] +pub fn inner_u32() -> Result { + let val = get_random_u64(); + Ok(crate::util::truncate(val)) +} + +#[inline] +pub fn inner_u64() -> Result { + Ok(get_random_u64()) +} + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + let (prefix, chunks, suffix) = unsafe { dest.align_to_mut::>() }; + + // We use `get_random_u64` instead of `get_random_bytes` because the latter creates + // an allocation due to the Wit IDL [restrictions][0]. This should be fine since + // the main use case of `getrandom` is seed generation. + // + // [0]: https://github.com/WebAssembly/wasi-random/issues/27 + if !prefix.is_empty() { + let val = get_random_u64(); + let src = (&val as *const u64).cast(); + unsafe { + copy_nonoverlapping(src, prefix.as_mut_ptr(), prefix.len()); + } + } + + for dst in chunks { + dst.write(get_random_u64()); + } + + if !suffix.is_empty() { + let val = get_random_u64(); + let src = (&val as *const u64).cast(); + unsafe { + copy_nonoverlapping(src, suffix.as_mut_ptr(), suffix.len()); + } + } + + Ok(()) +} diff --git a/tools/vendor/getrandom/src/backends/wasm_js.rs b/tools/vendor/getrandom/src/backends/wasm_js.rs new file mode 100644 index 0000000000..1320d9fc44 --- /dev/null +++ b/tools/vendor/getrandom/src/backends/wasm_js.rs @@ -0,0 +1,72 @@ +//! Implementation for WASM based on Web and Node.js +use crate::Error; +use core::mem::MaybeUninit; + +pub use crate::util::{inner_u32, inner_u64}; + +#[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))] +compile_error!("`wasm_js` backend can be enabled only for OS-less WASM targets!"); + +use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; + +// Maximum buffer size allowed in `Crypto.getRandomValuesSize` is 65536 bytes. +// See https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues +const MAX_BUFFER_SIZE: usize = 65536; + +#[cfg(not(target_feature = "atomics"))] +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + for chunk in dest.chunks_mut(MAX_BUFFER_SIZE) { + if get_random_values(chunk).is_err() { + return Err(Error::WEB_CRYPTO); + } + } + Ok(()) +} + +#[cfg(target_feature = "atomics")] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + // getRandomValues does not work with all types of WASM memory, + // so we initially write to browser memory to avoid exceptions. + let buf_len = usize::min(dest.len(), MAX_BUFFER_SIZE); + let buf_len_u32 = buf_len + .try_into() + .expect("buffer length is bounded by MAX_BUFFER_SIZE"); + let buf = js_sys::Uint8Array::new_with_length(buf_len_u32); + for chunk in dest.chunks_mut(buf_len) { + let chunk_len = chunk + .len() + .try_into() + .expect("chunk length is bounded by MAX_BUFFER_SIZE"); + // The chunk can be smaller than buf's length, so we call to + // JS to create a smaller view of buf without allocation. + let sub_buf = if chunk_len == buf_len_u32 { + &buf + } else { + &buf.subarray(0, chunk_len) + }; + + if get_random_values(sub_buf).is_err() { + return Err(Error::WEB_CRYPTO); + } + + sub_buf.copy_to_uninit(chunk); + } + Ok(()) +} + +#[wasm_bindgen] +extern "C" { + // Crypto.getRandomValues() + #[cfg(not(target_feature = "atomics"))] + #[wasm_bindgen(js_namespace = ["globalThis", "crypto"], js_name = getRandomValues, catch)] + fn get_random_values(buf: &mut [MaybeUninit]) -> Result<(), JsValue>; + #[cfg(target_feature = "atomics")] + #[wasm_bindgen(js_namespace = ["globalThis", "crypto"], js_name = getRandomValues, catch)] + fn get_random_values(buf: &js_sys::Uint8Array) -> Result<(), JsValue>; +} + +impl Error { + /// The environment does not support the Web Crypto API. + pub(crate) const WEB_CRYPTO: Error = Self::new_internal(10); +} diff --git a/tools/vendor/getrandom/src/backends/windows.rs b/tools/vendor/getrandom/src/backends/windows.rs new file mode 100644 index 0000000000..b1e10e7fc4 --- /dev/null +++ b/tools/vendor/getrandom/src/backends/windows.rs @@ -0,0 +1,61 @@ +//! Implementation for Windows 10 and later +//! +//! On Windows 10 and later, ProcessPrng "is the primary interface to the +//! user-mode per-processor PRNGs" and only requires bcryptprimitives.dll, +//! making it a better option than the other Windows RNG APIs: +//! - BCryptGenRandom: https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom +//! - Requires bcrypt.dll (which loads bcryptprimitives.dll anyway) +//! - Can cause crashes/hangs as BCrypt accesses the Windows Registry: +//! https://github.com/rust-lang/rust/issues/99341 +//! - Causes issues inside sandboxed code: +//! https://issues.chromium.org/issues/40277768 +//! - CryptGenRandom: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom +//! - Deprecated and not available on UWP targets +//! - Requires advapi32.lib/advapi32.dll (in addition to bcryptprimitives.dll) +//! - Thin wrapper around ProcessPrng +//! - RtlGenRandom: https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom +//! - Deprecated and not available on UWP targets +//! - Requires advapi32.dll (in addition to bcryptprimitives.dll) +//! - Requires using name "SystemFunction036" +//! - Thin wrapper around ProcessPrng +//! +//! For more information see the Windows RNG Whitepaper: https://aka.ms/win10rng +use crate::Error; +use core::mem::MaybeUninit; + +pub use crate::util::{inner_u32, inner_u64}; + +// Binding to the Windows.Win32.Security.Cryptography.ProcessPrng API. As +// bcryptprimitives.dll lacks an import library, we use "raw-dylib". This +// was added in Rust 1.65 for x86_64/aarch64 and in Rust 1.71 for x86. +// We don't need MSRV 1.71, as we only use this backend on Rust 1.78 and later. +#[cfg_attr( + target_arch = "x86", + link( + name = "bcryptprimitives", + kind = "raw-dylib", + import_name_type = "undecorated" + ) +)] +#[cfg_attr( + not(target_arch = "x86"), + link(name = "bcryptprimitives", kind = "raw-dylib") +)] +extern "system" { + fn ProcessPrng(pbdata: *mut u8, cbdata: usize) -> BOOL; +} +#[allow(clippy::upper_case_acronyms, clippy::incompatible_msrv)] +type BOOL = core::ffi::c_int; // MSRV 1.64, similarly OK for this backend. +const TRUE: BOOL = 1; + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + let result = unsafe { ProcessPrng(dest.as_mut_ptr().cast::(), dest.len()) }; + // Since Windows 10, calls to the user-mode RNG are guaranteed to never + // fail during runtime (rare windows W); `ProcessPrng` will only ever + // return 1 (which is how windows represents TRUE). + // See the bottom of page 6 of the aforementioned Windows RNG + // whitepaper for more information. + debug_assert!(result == TRUE); + Ok(()) +} diff --git a/tools/vendor/getrandom/src/backends/windows_legacy.rs b/tools/vendor/getrandom/src/backends/windows_legacy.rs new file mode 100644 index 0000000000..4b3f092787 --- /dev/null +++ b/tools/vendor/getrandom/src/backends/windows_legacy.rs @@ -0,0 +1,48 @@ +//! Legacy implementation for Windows XP and later +//! +//! For targets where we cannot use ProcessPrng (added in Windows 10), we use +//! RtlGenRandom. See windows.rs for a more detailed discussion of the Windows +//! RNG APIs (and why we don't use BCryptGenRandom). On versions prior to +//! Windows 10, this implementation is secure. On Windows 10 and later, this +//! implementation behaves identically to the windows.rs implementation, except +//! that it forces the loading of an additional DLL (advapi32.dll). +//! +//! This implementation will not work on UWP targets (which lack advapi32.dll), +//! but such targets require Windows 10, so can use the standard implementation. +use crate::Error; +use core::{ffi::c_void, mem::MaybeUninit}; + +pub use crate::util::{inner_u32, inner_u64}; + +#[cfg(not(windows))] +compile_error!("`windows_legacy` backend can be enabled only for Windows targets!"); + +// Binding to the Windows.Win32.Security.Authentication.Identity.RtlGenRandom +// API. Don't use windows-targets as it doesn't support Windows 7 targets. +#[link(name = "advapi32")] +extern "system" { + #[link_name = "SystemFunction036"] + fn RtlGenRandom(randombuffer: *mut c_void, randombufferlength: u32) -> BOOLEAN; +} +#[allow(clippy::upper_case_acronyms)] +type BOOLEAN = u8; +const TRUE: BOOLEAN = 1u8; + +#[inline] +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + // Prevent overflow of u32 + let chunk_size = usize::try_from(i32::MAX).expect("Windows does not support 16-bit targets"); + for chunk in dest.chunks_mut(chunk_size) { + let chunk_len = u32::try_from(chunk.len()).expect("chunk size is bounded by i32::MAX"); + let ret = unsafe { RtlGenRandom(chunk.as_mut_ptr().cast::(), chunk_len) }; + if ret != TRUE { + return Err(Error::WINDOWS_RTL_GEN_RANDOM); + } + } + Ok(()) +} + +impl Error { + /// Call to Windows [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom) failed. + pub(crate) const WINDOWS_RTL_GEN_RANDOM: Error = Self::new_internal(10); +} diff --git a/tools/vendor/getrandom/src/error.rs b/tools/vendor/getrandom/src/error.rs new file mode 100644 index 0000000000..284533d10a --- /dev/null +++ b/tools/vendor/getrandom/src/error.rs @@ -0,0 +1,212 @@ +#[cfg(feature = "std")] +extern crate std; + +use core::fmt; + +// This private alias mirrors `std::io::RawOsError`: +// https://doc.rust-lang.org/std/io/type.RawOsError.html) +cfg_if::cfg_if!( + if #[cfg(target_os = "uefi")] { + // See the UEFI spec for more information: + // https://uefi.org/specs/UEFI/2.10/Apx_D_Status_Codes.html + type RawOsError = usize; + type NonZeroRawOsError = core::num::NonZeroUsize; + const UEFI_ERROR_FLAG: RawOsError = 1 << (RawOsError::BITS - 1); + } else { + type RawOsError = i32; + type NonZeroRawOsError = core::num::NonZeroI32; + } +); + +/// A small and `no_std` compatible error type +/// +/// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and +/// if so, which error code the OS gave the application. If such an error is +/// encountered, please consult with your system documentation. +/// +/// *If this crate's `"std"` Cargo feature is enabled*, then: +/// - [`getrandom::Error`][Error] implements +/// [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html) +/// - [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) implements +/// [`From`](https://doc.rust-lang.org/std/convert/trait.From.html). + +// note: on non-UEFI targets OS errors are represented as negative integers, +// while on UEFI targets OS errors have the highest bit set to 1. +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct Error(NonZeroRawOsError); + +impl Error { + /// This target/platform is not supported by `getrandom`. + pub const UNSUPPORTED: Error = Self::new_internal(0); + /// The platform-specific `errno` returned a non-positive value. + pub const ERRNO_NOT_POSITIVE: Error = Self::new_internal(1); + /// Encountered an unexpected situation which should not happen in practice. + pub const UNEXPECTED: Error = Self::new_internal(2); + + /// Internal errors can be in the range of 2^16..2^17 + const INTERNAL_START: RawOsError = 1 << 16; + /// Custom errors can be in the range of 2^17..(2^17 + 2^16) + const CUSTOM_START: RawOsError = 1 << 17; + + /// Creates a new instance of an `Error` from a negative error code. + #[cfg(not(target_os = "uefi"))] + #[allow(dead_code)] + pub(super) fn from_neg_error_code(code: RawOsError) -> Self { + if code < 0 { + let code = NonZeroRawOsError::new(code).expect("`code` is negative"); + Self(code) + } else { + Error::UNEXPECTED + } + } + + /// Creates a new instance of an `Error` from an UEFI error code. + #[cfg(target_os = "uefi")] + #[allow(dead_code)] + pub(super) fn from_uefi_code(code: RawOsError) -> Self { + if code & UEFI_ERROR_FLAG != 0 { + let code = NonZeroRawOsError::new(code).expect("The highest bit of `code` is set to 1"); + Self(code) + } else { + Self::UNEXPECTED + } + } + + /// Extract the raw OS error code (if this error came from the OS) + /// + /// This method is identical to [`std::io::Error::raw_os_error()`][1], except + /// that it works in `no_std` contexts. On most targets this method returns + /// `Option`, but some platforms (e.g. UEFI) may use a different primitive + /// type like `usize`. Consult with the [`RawOsError`] docs for more information. + /// + /// If this method returns `None`, the error value can still be formatted via + /// the `Display` implementation. + /// + /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error + /// [`RawOsError`]: https://doc.rust-lang.org/std/io/type.RawOsError.html + #[inline] + pub fn raw_os_error(self) -> Option { + let code = self.0.get(); + + // note: in this method we need to cover only backends which rely on + // `Error::{from_error_code, from_errno, from_uefi_code}` methods, + // on all other backends this method always returns `None`. + + #[cfg(target_os = "uefi")] + { + if code & UEFI_ERROR_FLAG != 0 { + Some(code) + } else { + None + } + } + + #[cfg(not(target_os = "uefi"))] + { + // On most targets `std` expects positive error codes while retrieving error strings: + // - `libc`-based targets use `strerror_r` which expects positive error codes. + // - Hermit relies on the `hermit-abi` crate, which expects positive error codes: + // https://docs.rs/hermit-abi/0.4.0/src/hermit_abi/errno.rs.html#400-532 + // - WASIp1 uses the same conventions as `libc`: + // https://github.com/rust-lang/rust/blob/1.85.0/library/std/src/sys/pal/wasi/os.rs#L57-L67 + // + // The only exception is Solid, `std` expects negative system error codes, see: + // https://github.com/rust-lang/rust/blob/1.85.0/library/std/src/sys/pal/solid/error.rs#L5-L31 + if code >= 0 { + None + } else if cfg!(not(target_os = "solid_asp3")) { + code.checked_neg() + } else { + Some(code) + } + } + } + + /// Creates a new instance of an `Error` from a particular custom error code. + pub const fn new_custom(n: u16) -> Error { + // SAFETY: code > 0 as CUSTOM_START > 0 and adding `n` won't overflow `RawOsError`. + let code = Error::CUSTOM_START + (n as RawOsError); + Error(unsafe { NonZeroRawOsError::new_unchecked(code) }) + } + + /// Creates a new instance of an `Error` from a particular internal error code. + pub(crate) const fn new_internal(n: u16) -> Error { + // SAFETY: code > 0 as INTERNAL_START > 0 and adding `n` won't overflow `RawOsError`. + let code = Error::INTERNAL_START + (n as RawOsError); + Error(unsafe { NonZeroRawOsError::new_unchecked(code) }) + } + + fn internal_desc(&self) -> Option<&'static str> { + let desc = match *self { + Error::UNSUPPORTED => "getrandom: this target is not supported", + Error::ERRNO_NOT_POSITIVE => "errno: did not return a positive value", + Error::UNEXPECTED => "unexpected situation", + #[cfg(any( + target_os = "ios", + target_os = "visionos", + target_os = "watchos", + target_os = "tvos", + ))] + Error::IOS_RANDOM_GEN => "SecRandomCopyBytes: iOS Security framework failure", + #[cfg(all(windows, target_vendor = "win7"))] + Error::WINDOWS_RTL_GEN_RANDOM => "RtlGenRandom: Windows system function failure", + #[cfg(all(feature = "wasm_js", getrandom_backend = "wasm_js"))] + Error::WEB_CRYPTO => "Web Crypto API is unavailable", + #[cfg(target_os = "vxworks")] + Error::VXWORKS_RAND_SECURE => "randSecure: VxWorks RNG module is not initialized", + + #[cfg(any( + getrandom_backend = "rdrand", + all(target_arch = "x86_64", target_env = "sgx") + ))] + Error::FAILED_RDRAND => "RDRAND: failed multiple times: CPU issue likely", + #[cfg(any( + getrandom_backend = "rdrand", + all(target_arch = "x86_64", target_env = "sgx") + ))] + Error::NO_RDRAND => "RDRAND: instruction not supported", + + #[cfg(getrandom_backend = "rndr")] + Error::RNDR_FAILURE => "RNDR: Could not generate a random number", + #[cfg(getrandom_backend = "rndr")] + Error::RNDR_NOT_AVAILABLE => "RNDR: Register not supported", + _ => return None, + }; + Some(desc) + } +} + +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut dbg = f.debug_struct("Error"); + if let Some(errno) = self.raw_os_error() { + dbg.field("os_error", &errno); + #[cfg(feature = "std")] + dbg.field("description", &std::io::Error::from_raw_os_error(errno)); + } else if let Some(desc) = self.internal_desc() { + dbg.field("internal_code", &self.0.get()); + dbg.field("description", &desc); + } else { + dbg.field("unknown_code", &self.0.get()); + } + dbg.finish() + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(errno) = self.raw_os_error() { + cfg_if! { + if #[cfg(feature = "std")] { + std::io::Error::from_raw_os_error(errno).fmt(f) + } else { + write!(f, "OS Error: {errno}") + } + } + } else if let Some(desc) = self.internal_desc() { + f.write_str(desc) + } else { + write!(f, "Unknown Error: {}", self.0.get()) + } + } +} diff --git a/tools/vendor/getrandom/src/error_std_impls.rs b/tools/vendor/getrandom/src/error_std_impls.rs new file mode 100644 index 0000000000..2c326012c8 --- /dev/null +++ b/tools/vendor/getrandom/src/error_std_impls.rs @@ -0,0 +1,15 @@ +extern crate std; + +use crate::Error; +use std::io; + +impl From for io::Error { + fn from(err: Error) -> Self { + match err.raw_os_error() { + Some(errno) => io::Error::from_raw_os_error(errno), + None => io::Error::new(io::ErrorKind::Other, err), + } + } +} + +impl std::error::Error for Error {} diff --git a/tools/vendor/getrandom/src/lazy.rs b/tools/vendor/getrandom/src/lazy.rs new file mode 100644 index 0000000000..b191aa6d7f --- /dev/null +++ b/tools/vendor/getrandom/src/lazy.rs @@ -0,0 +1,64 @@ +//! Helpers built around pointer-sized atomics. +use core::sync::atomic::{AtomicUsize, Ordering}; + +// This structure represents a lazily initialized static usize value. Useful +// when it is preferable to just rerun initialization instead of locking. +// unsync_init will invoke an init() function until it succeeds, then return the +// cached value for future calls. +// +// unsync_init supports init() "failing". If the init() method returns UNINIT, +// that value will be returned as normal, but will not be cached. +// +// Users should only depend on the _value_ returned by init() functions. +// Specifically, for the following init() function: +// fn init() -> usize { +// a(); +// let v = b(); +// c(); +// v +// } +// the effects of c() or writes to shared memory will not necessarily be +// observed and additional synchronization methods may be needed. +struct LazyUsize(AtomicUsize); + +impl LazyUsize { + // The initialization is not completed. + const UNINIT: usize = usize::MAX; + + const fn new() -> Self { + Self(AtomicUsize::new(Self::UNINIT)) + } + + // Runs the init() function at most once, returning the value of some run of + // init(). Multiple callers can run their init() functions in parallel. + // init() should always return the same value, if it succeeds. + fn unsync_init(&self, init: impl FnOnce() -> usize) -> usize { + #[cold] + fn do_init(this: &LazyUsize, init: impl FnOnce() -> usize) -> usize { + let val = init(); + this.0.store(val, Ordering::Relaxed); + val + } + + // Relaxed ordering is fine, as we only have a single atomic variable. + let val = self.0.load(Ordering::Relaxed); + if val != Self::UNINIT { + val + } else { + do_init(self, init) + } + } +} + +// Identical to LazyUsize except with bool instead of usize. +pub(crate) struct LazyBool(LazyUsize); + +impl LazyBool { + pub const fn new() -> Self { + Self(LazyUsize::new()) + } + + pub fn unsync_init(&self, init: impl FnOnce() -> bool) -> bool { + self.0.unsync_init(|| usize::from(init())) != 0 + } +} diff --git a/tools/vendor/getrandom/src/lib.rs b/tools/vendor/getrandom/src/lib.rs new file mode 100644 index 0000000000..adb7e5b7e3 --- /dev/null +++ b/tools/vendor/getrandom/src/lib.rs @@ -0,0 +1,138 @@ +// Overwrite links to crate items with intra-crate links +//! [`Error::UNEXPECTED`]: Error::UNEXPECTED +//! [`fill_uninit`]: fill_uninit + +#![no_std] +#![doc( + html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", + html_favicon_url = "https://www.rust-lang.org/favicon.ico" +)] +#![doc = include_str!("../README.md")] +#![warn(rust_2018_idioms, unused_lifetimes, missing_docs)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(getrandom_backend = "efi_rng", feature(uefi_std))] +#![deny( + clippy::cast_lossless, + clippy::cast_possible_truncation, + clippy::cast_possible_wrap, + clippy::cast_precision_loss, + clippy::cast_ptr_alignment, + clippy::cast_sign_loss, + clippy::char_lit_as_u8, + clippy::checked_conversions, + clippy::fn_to_numeric_cast, + clippy::fn_to_numeric_cast_with_truncation, + clippy::ptr_as_ptr, + clippy::unnecessary_cast, + clippy::useless_conversion +)] + +#[macro_use] +extern crate cfg_if; + +use core::mem::MaybeUninit; + +mod backends; +mod error; +mod util; + +#[cfg(feature = "std")] +mod error_std_impls; + +pub use crate::error::Error; + +/// Fill `dest` with random bytes from the system's preferred random number source. +/// +/// This function returns an error on any failure, including partial reads. We +/// make no guarantees regarding the contents of `dest` on error. If `dest` is +/// empty, `getrandom` immediately returns success, making no calls to the +/// underlying operating system. +/// +/// Blocking is possible, at least during early boot; see module documentation. +/// +/// In general, `getrandom` will be fast enough for interactive usage, though +/// significantly slower than a user-space CSPRNG; for the latter consider +/// [`rand::thread_rng`](https://docs.rs/rand/*/rand/fn.thread_rng.html). +/// +/// # Examples +/// +/// ``` +/// # fn main() -> Result<(), getrandom::Error> { +/// let mut buf = [0u8; 32]; +/// getrandom::fill(&mut buf)?; +/// # Ok(()) } +/// ``` +#[inline] +pub fn fill(dest: &mut [u8]) -> Result<(), Error> { + // SAFETY: The `&mut MaybeUninit<_>` reference doesn't escape, + // and `fill_uninit` guarantees it will never de-initialize + // any part of `dest`. + fill_uninit(unsafe { util::slice_as_uninit_mut(dest) })?; + Ok(()) +} + +/// Fill potentially uninitialized buffer `dest` with random bytes from +/// the system's preferred random number source and return a mutable +/// reference to those bytes. +/// +/// On successful completion this function is guaranteed to return a slice +/// which points to the same memory as `dest` and has the same length. +/// In other words, it's safe to assume that `dest` is initialized after +/// this function has returned `Ok`. +/// +/// No part of `dest` will ever be de-initialized at any point, regardless +/// of what is returned. +/// +/// # Examples +/// +/// ```ignore +/// # // We ignore this test since `uninit_array` is unstable. +/// #![feature(maybe_uninit_uninit_array)] +/// # fn main() -> Result<(), getrandom::Error> { +/// let mut buf = core::mem::MaybeUninit::uninit_array::<1024>(); +/// let buf: &mut [u8] = getrandom::fill_uninit(&mut buf)?; +/// # Ok(()) } +/// ``` +#[inline] +pub fn fill_uninit(dest: &mut [MaybeUninit]) -> Result<&mut [u8], Error> { + if !dest.is_empty() { + backends::fill_inner(dest)?; + } + + #[cfg(getrandom_msan)] + extern "C" { + fn __msan_unpoison(a: *mut core::ffi::c_void, size: usize); + } + + // SAFETY: `dest` has been fully initialized by `imp::fill_inner` + // since it returned `Ok`. + Ok(unsafe { util::slice_assume_init_mut(dest) }) +} + +/// Get random `u32` from the system's preferred random number source. +/// +/// # Examples +/// +/// ``` +/// # fn main() -> Result<(), getrandom::Error> { +/// let rng_seed = getrandom::u32()?; +/// # Ok(()) } +/// ``` +#[inline] +pub fn u32() -> Result { + backends::inner_u32() +} + +/// Get random `u64` from the system's preferred random number source. +/// +/// # Examples +/// +/// ``` +/// # fn main() -> Result<(), getrandom::Error> { +/// let rng_seed = getrandom::u64()?; +/// # Ok(()) } +/// ``` +#[inline] +pub fn u64() -> Result { + backends::inner_u64() +} diff --git a/tools/vendor/getrandom/src/util.rs b/tools/vendor/getrandom/src/util.rs new file mode 100644 index 0000000000..d42c26e75e --- /dev/null +++ b/tools/vendor/getrandom/src/util.rs @@ -0,0 +1,84 @@ +#![allow(dead_code)] +use crate::Error; +use core::{mem::MaybeUninit, ptr, slice}; + +/// Polyfill for `maybe_uninit_slice` feature's +/// `MaybeUninit::slice_assume_init_mut`. Every element of `slice` must have +/// been initialized. +#[inline(always)] +#[allow(unused_unsafe)] // TODO(MSRV 1.65): Remove this. +pub unsafe fn slice_assume_init_mut(slice: &mut [MaybeUninit]) -> &mut [T] { + let ptr = ptr_from_mut::<[MaybeUninit]>(slice) as *mut [T]; + // SAFETY: `MaybeUninit` is guaranteed to be layout-compatible with `T`. + unsafe { &mut *ptr } +} + +#[inline] +pub fn uninit_slice_fill_zero(slice: &mut [MaybeUninit]) -> &mut [u8] { + unsafe { ptr::write_bytes(slice.as_mut_ptr(), 0, slice.len()) }; + unsafe { slice_assume_init_mut(slice) } +} + +#[inline(always)] +pub fn slice_as_uninit(slice: &[T]) -> &[MaybeUninit] { + let ptr = ptr_from_ref::<[T]>(slice) as *const [MaybeUninit]; + // SAFETY: `MaybeUninit` is guaranteed to be layout-compatible with `T`. + unsafe { &*ptr } +} + +/// View an mutable initialized array as potentially-uninitialized. +/// +/// This is unsafe because it allows assigning uninitialized values into +/// `slice`, which would be undefined behavior. +#[inline(always)] +#[allow(unused_unsafe)] // TODO(MSRV 1.65): Remove this. +pub unsafe fn slice_as_uninit_mut(slice: &mut [T]) -> &mut [MaybeUninit] { + let ptr = ptr_from_mut::<[T]>(slice) as *mut [MaybeUninit]; + // SAFETY: `MaybeUninit` is guaranteed to be layout-compatible with `T`. + unsafe { &mut *ptr } +} + +// TODO: MSRV(1.76.0): Replace with `core::ptr::from_mut`. +fn ptr_from_mut(r: &mut T) -> *mut T { + r +} + +// TODO: MSRV(1.76.0): Replace with `core::ptr::from_ref`. +fn ptr_from_ref(r: &T) -> *const T { + r +} + +/// Default implementation of `inner_u32` on top of `fill_uninit` +#[inline] +pub fn inner_u32() -> Result { + let mut res = MaybeUninit::::uninit(); + // SAFETY: the created slice has the same size as `res` + let dst = unsafe { + let p: *mut MaybeUninit = res.as_mut_ptr().cast(); + slice::from_raw_parts_mut(p, core::mem::size_of::()) + }; + crate::fill_uninit(dst)?; + // SAFETY: `dst` has been fully initialized by `imp::fill_inner` + // since it returned `Ok`. + Ok(unsafe { res.assume_init() }) +} + +/// Default implementation of `inner_u64` on top of `fill_uninit` +#[inline] +pub fn inner_u64() -> Result { + let mut res = MaybeUninit::::uninit(); + // SAFETY: the created slice has the same size as `res` + let dst = unsafe { + let p: *mut MaybeUninit = res.as_mut_ptr().cast(); + slice::from_raw_parts_mut(p, core::mem::size_of::()) + }; + crate::fill_uninit(dst)?; + // SAFETY: `dst` has been fully initialized by `imp::fill_inner` + // since it returned `Ok`. + Ok(unsafe { res.assume_init() }) +} + +/// Truncates `u64` and returns the lower 32 bits as `u32` +pub(crate) fn truncate(val: u64) -> u32 { + u32::try_from(val & u64::from(u32::MAX)).expect("The higher 32 bits are masked") +} diff --git a/tools/vendor/getrandom/src/util_libc.rs b/tools/vendor/getrandom/src/util_libc.rs new file mode 100644 index 0000000000..24c53c0c90 --- /dev/null +++ b/tools/vendor/getrandom/src/util_libc.rs @@ -0,0 +1,81 @@ +use crate::Error; +use core::mem::MaybeUninit; + +cfg_if! { + if #[cfg(any(target_os = "netbsd", target_os = "openbsd", target_os = "android", target_os = "cygwin"))] { + use libc::__errno as errno_location; + } else if #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "hurd", target_os = "redox", target_os = "dragonfly"))] { + use libc::__errno_location as errno_location; + } else if #[cfg(any(target_os = "solaris", target_os = "illumos"))] { + use libc::___errno as errno_location; + } else if #[cfg(any(target_os = "macos", target_os = "freebsd"))] { + use libc::__error as errno_location; + } else if #[cfg(target_os = "haiku")] { + use libc::_errnop as errno_location; + } else if #[cfg(target_os = "nto")] { + use libc::__get_errno_ptr as errno_location; + } else if #[cfg(any(all(target_os = "horizon", target_arch = "arm"), target_os = "vita"))] { + extern "C" { + // Not provided by libc: https://github.com/rust-lang/libc/issues/1995 + fn __errno() -> *mut libc::c_int; + } + use __errno as errno_location; + } else if #[cfg(target_os = "aix")] { + use libc::_Errno as errno_location; + } +} + +cfg_if! { + if #[cfg(target_os = "vxworks")] { + use libc::errnoGet as get_errno; + } else { + unsafe fn get_errno() -> libc::c_int { *errno_location() } + } +} + +pub(crate) fn last_os_error() -> Error { + // We assume that on all targets which use the `util_libc` module `c_int` is equal to `i32` + let errno: i32 = unsafe { get_errno() }; + + if errno > 0 { + let code = errno + .checked_neg() + .expect("Positive number can be always negated"); + Error::from_neg_error_code(code) + } else { + Error::ERRNO_NOT_POSITIVE + } +} + +/// Fill a buffer by repeatedly invoking `sys_fill`. +/// +/// The `sys_fill` function: +/// - should return -1 and set errno on failure +/// - should return the number of bytes written on success +#[allow(dead_code)] +pub(crate) fn sys_fill_exact( + mut buf: &mut [MaybeUninit], + sys_fill: impl Fn(&mut [MaybeUninit]) -> libc::ssize_t, +) -> Result<(), Error> { + while !buf.is_empty() { + let res = sys_fill(buf); + match res { + res if res > 0 => { + let len = usize::try_from(res).map_err(|_| Error::UNEXPECTED)?; + buf = buf.get_mut(len..).ok_or(Error::UNEXPECTED)?; + } + -1 => { + let err = last_os_error(); + // We should try again if the call was interrupted. + if err.raw_os_error() != Some(libc::EINTR) { + return Err(err); + } + } + // Negative return codes not equal to -1 should be impossible. + // EOF (ret = 0) should be impossible, as the data we are reading + // should be an infinite stream of random bytes. + _ => return Err(Error::UNEXPECTED), + } + } + Ok(()) +} diff --git a/tools/vendor/getrandom/tests/mod.rs b/tools/vendor/getrandom/tests/mod.rs new file mode 100644 index 0000000000..9f1e633872 --- /dev/null +++ b/tools/vendor/getrandom/tests/mod.rs @@ -0,0 +1,297 @@ +use core::mem::MaybeUninit; +use getrandom::{fill, fill_uninit}; + +#[cfg(all(feature = "wasm_js", target_arch = "wasm32", target_os = "unknown"))] +use wasm_bindgen_test::wasm_bindgen_test as test; + +#[test] +fn test_zero() { + // Test that APIs are happy with zero-length requests + fill(&mut [0u8; 0]).unwrap(); + let res = fill_uninit(&mut []).unwrap(); + assert!(res.is_empty()); +} + +trait DiffBits: Sized { + fn diff_bits(ab: (&Self, &Self)) -> usize; +} + +impl DiffBits for u8 { + fn diff_bits((a, b): (&Self, &Self)) -> usize { + (a ^ b).count_ones() as usize + } +} + +impl DiffBits for u32 { + fn diff_bits((a, b): (&Self, &Self)) -> usize { + (a ^ b).count_ones() as usize + } +} + +impl DiffBits for u64 { + fn diff_bits((a, b): (&Self, &Self)) -> usize { + (a ^ b).count_ones() as usize + } +} + +// Return the number of bits in which s1 and s2 differ +fn num_diff_bits(s1: &[T], s2: &[T]) -> usize { + assert_eq!(s1.len(), s2.len()); + s1.iter().zip(s2.iter()).map(T::diff_bits).sum() +} + +// TODO: use `[const { MaybeUninit::uninit() }; N]` after MSRV is bumped to 1.79+ +// or `MaybeUninit::uninit_array` +fn uninit_vec(n: usize) -> Vec> { + vec![MaybeUninit::uninit(); n] +} + +// Tests the quality of calling getrandom on two large buffers +#[test] +fn test_diff() { + const N: usize = 1000; + let mut v1 = [0u8; N]; + let mut v2 = [0u8; N]; + fill(&mut v1).unwrap(); + fill(&mut v2).unwrap(); + + let mut t1 = uninit_vec(N); + let mut t2 = uninit_vec(N); + let r1 = fill_uninit(&mut t1).unwrap(); + let r2 = fill_uninit(&mut t2).unwrap(); + assert_eq!(r1.len(), N); + assert_eq!(r2.len(), N); + + // Between 3.5 and 4.5 bits per byte should differ. Probability of failure: + // ~ 2^(-94) = 2 * CDF[BinomialDistribution[8000, 0.5], 3500] + let d1 = num_diff_bits(&v1, &v2); + assert!(d1 > 3500); + assert!(d1 < 4500); + let d2 = num_diff_bits(r1, r2); + assert!(d2 > 3500); + assert!(d2 < 4500); +} + +#[test] +fn test_diff_u32() { + const N: usize = 1000 / 4; + let mut v1 = [0u32; N]; + let mut v2 = [0u32; N]; + for v in v1.iter_mut() { + *v = getrandom::u32().unwrap(); + } + for v in v2.iter_mut() { + *v = getrandom::u32().unwrap(); + } + + // Between 3.5 and 4.5 bits per byte should differ. Probability of failure: + // ~ 2^(-94) = 2 * CDF[BinomialDistribution[8000, 0.5], 3500] + let d1 = num_diff_bits(&v1, &v2); + assert!(d1 > 3500); + assert!(d1 < 4500); +} + +#[test] +fn test_diff_u64() { + const N: usize = 1000 / 8; + let mut v1 = [0u64; N]; + let mut v2 = [0u64; N]; + for v in v1.iter_mut() { + *v = getrandom::u64().unwrap(); + } + for v in v2.iter_mut() { + *v = getrandom::u64().unwrap(); + } + + // Between 3.5 and 4.5 bits per byte should differ. Probability of failure: + // ~ 2^(-94) = 2 * CDF[BinomialDistribution[8000, 0.5], 3500] + let d1 = num_diff_bits(&v1, &v2); + assert!(d1 > 3500); + assert!(d1 < 4500); +} + +#[test] +fn test_small() { + const N: usize = 64; + // For each buffer size, get at least 256 bytes and check that between + // 3 and 5 bits per byte differ. Probability of failure: + // ~ 2^(-91) = 64 * 2 * CDF[BinomialDistribution[8*256, 0.5], 3*256] + for size in 1..=N { + let mut num_bytes = 0; + let mut diff_bits = 0; + while num_bytes < 256 { + let mut buf1 = [0u8; N]; + let mut buf2 = [0u8; N]; + + let s1 = &mut buf1[..size]; + let s2 = &mut buf2[..size]; + + fill(s1).unwrap(); + fill(s2).unwrap(); + + num_bytes += size; + diff_bits += num_diff_bits(s1, s2); + } + assert!(diff_bits > 3 * num_bytes); + assert!(diff_bits < 5 * num_bytes); + } +} + +// Tests the quality of calling getrandom repeatedly on small buffers +#[test] +fn test_small_uninit() { + const N: usize = 64; + // For each buffer size, get at least 256 bytes and check that between + // 3 and 5 bits per byte differ. Probability of failure: + // ~ 2^(-91) = 64 * 2 * CDF[BinomialDistribution[8*256, 0.5], 3*256] + for size in 1..=N { + let mut num_bytes = 0; + let mut diff_bits = 0; + while num_bytes < 256 { + let mut buf1 = uninit_vec(N); + let mut buf2 = uninit_vec(N); + + let s1 = &mut buf1[..size]; + let s2 = &mut buf2[..size]; + + let r1 = fill_uninit(s1).unwrap(); + let r2 = fill_uninit(s2).unwrap(); + assert_eq!(r1.len(), size); + assert_eq!(r2.len(), size); + + num_bytes += size; + diff_bits += num_diff_bits(r1, r2); + } + assert!(diff_bits > 3 * num_bytes); + assert!(diff_bits < 5 * num_bytes); + } +} + +#[test] +fn test_huge() { + let mut huge = [0u8; 100_000]; + fill(&mut huge).unwrap(); +} + +#[test] +fn test_huge_uninit() { + const N: usize = 100_000; + let mut huge = uninit_vec(N); + let res = fill_uninit(&mut huge).unwrap(); + assert_eq!(res.len(), N); +} + +#[test] +#[cfg_attr( + target_arch = "wasm32", + ignore = "The thread API always fails/panics on WASM" +)] +fn test_multithreading() { + extern crate std; + use std::{sync::mpsc::channel, thread, vec}; + + let mut txs = vec![]; + for _ in 0..20 { + let (tx, rx) = channel(); + txs.push(tx); + + thread::spawn(move || { + // wait until all the tasks are ready to go. + rx.recv().unwrap(); + let mut v = [0u8; 1000]; + + for _ in 0..100 { + fill(&mut v).unwrap(); + thread::yield_now(); + } + }); + } + + // start all the tasks + for tx in txs.iter() { + tx.send(()).unwrap(); + } +} + +#[cfg(getrandom_backend = "custom")] +mod custom { + use getrandom::Error; + + struct Xoshiro128PlusPlus { + s: [u32; 4], + } + + impl Xoshiro128PlusPlus { + fn new(mut seed: u64) -> Self { + const PHI: u64 = 0x9e3779b97f4a7c15; + let mut s = [0u32; 4]; + for val in s.iter_mut() { + seed = seed.wrapping_add(PHI); + let mut z = seed; + z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9); + z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb); + z = z ^ (z >> 31); + *val = z as u32; + } + Self { s } + } + + fn next_u32(&mut self) -> u32 { + let res = self.s[0] + .wrapping_add(self.s[3]) + .rotate_left(7) + .wrapping_add(self.s[0]); + + let t = self.s[1] << 9; + + self.s[2] ^= self.s[0]; + self.s[3] ^= self.s[1]; + self.s[1] ^= self.s[2]; + self.s[0] ^= self.s[3]; + + self.s[2] ^= t; + + self.s[3] = self.s[3].rotate_left(11); + + res + } + } + + // This implementation uses current timestamp as a PRNG seed. + // + // WARNING: this custom implementation is for testing purposes ONLY! + #[no_mangle] + unsafe extern "Rust" fn __getrandom_v03_custom(dest: *mut u8, len: usize) -> Result<(), Error> { + use std::time::{SystemTime, UNIX_EPOCH}; + + assert_ne!(len, 0); + + if len == 142 { + return Err(Error::new_custom(142)); + } + + let dest_u32 = dest.cast::(); + let ts = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + let mut rng = Xoshiro128PlusPlus::new(ts.as_nanos() as u64); + for i in 0..len / 4 { + let val = rng.next_u32(); + core::ptr::write_unaligned(dest_u32.add(i), val); + } + if len % 4 != 0 { + let start = 4 * (len / 4); + for i in start..len { + let val = rng.next_u32(); + core::ptr::write_unaligned(dest.add(i), val as u8); + } + } + Ok(()) + } + + // Test that enabling the custom feature indeed uses the custom implementation + #[test] + fn test_custom() { + let mut buf = [0u8; 142]; + let res = getrandom::fill(&mut buf); + assert!(res.is_err()); + } +} diff --git a/tools/vendor/libtest-mimic/.cargo-checksum.json b/tools/vendor/libtest-mimic/.cargo-checksum.json new file mode 100644 index 0000000000..e091bf1573 --- /dev/null +++ b/tools/vendor/libtest-mimic/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"70b55fd340c1a9a1fe14ce29f3d5f29c173f9dfe145ed0db1dca5adbf2e77efb","CHANGELOG.md":"adb08dc0e8af30f30b8090659c4e227cc46d623ffe35df63874931a199981410","Cargo.lock":"d4340019d436bca498454ec1f8ee381a8215a355963691adbf2bbb1c73717748","Cargo.toml":"2b2092bbb0d5d9fdd2451bd9a5c509d9c35439c8d3c5ba02456eae15231ba232","Cargo.toml.orig":"907e8630d41b89b95a41b6ea26b2e15d0d9c3ea21b119355e788a7c4aeb7672e","LICENSE-APACHE":"8173d5c29b4f956d532781d2b86e4e30f83e6b7878dce18c919451d6ba707c90","LICENSE-MIT":"6f54826bbf67526f2d41937c1904ae2de63ef5707b80c68deadbf073b5669c40","README.md":"2607b42f3591f387eae97d673b78a70745e6da07abacad0d1290297d59e580a7","examples/README.md":"c75a125e1c76b6e00ef5c23d16517dbde99e8a705d5b126bd9d89a6db5c6ed08","examples/simple.rs":"79b77fff7540cd309d0565696ccb0d1f2b7bbd89292a3f62d065deb9e90791f7","examples/tidy.rs":"145dce4e059083bd75046e05eac69d3f381d3edde01de04cad6f530596ffdd31","src/args.rs":"cf3c467b9a2bbfe2cbd745d14097fd64693b7653721ec456e38fb9297d256acd","src/lib.rs":"5bb13330a4e78a7a357edde89fb9c8d3161893f286327b1df9bd95345c8cdf60","src/printer.rs":"6520a8a05a6e8c2d20ad679525d3caca8b866356bae706900aa9d8070ca2c3cf","tests/all_passing.rs":"f4587e891330b46fa2ef33fba23eb9d712c86940ef98c92605d042fc40b6b779","tests/common/mod.rs":"d3faa378c8df000a3dd07a39cefb7a6992b841b35d833c77cf9abbce4266f934","tests/json-output.json":"e09948e21e614d11532a91583be9371224b5df179cf87849d0ab5112a45c0d21","tests/mixed_bag.rs":"7c7a349b5cba2da89a642b13d794362d17124e365a68c7dc2eac28c62ac09903","tests/panic.rs":"378ea05bb0d4bb8f83f58eb52a678943209d9ab98575f9b5481b5c633de9417b","tests/real/.gitignore":"926f9b5cbcdc8336736a2e912c21237bc4740b7dd9c0ad94d7b7fa3d9e0307d4","tests/real/README.md":"62d50b8a9576d9af7b1fd6bb9ab88874cf05ed158816594e4cee5d61bfcc715e","tests/real/ignored.rs":"4830deb1b09593d69a5b0398270419637be02c958a0238322d987aba07965e7a","tests/real/mixed_bag.rs":"d16c258e09f869f69e8b19be3f7cf0576b7b79cc24aad700b718f7582475ea7c","tests/threads.rs":"dabbbba7dca27a0e927bd751293d684fc549fb1c8591e85ac0f8a38e8f847889"},"package":"5297962ef19edda4ce33aaa484386e0a5b3d7f2f4e037cbeee00503ef6b29d33"} \ No newline at end of file diff --git a/tools/vendor/libtest-mimic/.cargo_vcs_info.json b/tools/vendor/libtest-mimic/.cargo_vcs_info.json new file mode 100644 index 0000000000..a42fdf9a66 --- /dev/null +++ b/tools/vendor/libtest-mimic/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "63e4ec13f15c4adc5b36e3cc0c5f49e19b50f480" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/tools/vendor/libtest-mimic/CHANGELOG.md b/tools/vendor/libtest-mimic/CHANGELOG.md new file mode 100644 index 0000000000..341f3f2584 --- /dev/null +++ b/tools/vendor/libtest-mimic/CHANGELOG.md @@ -0,0 +1,147 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.8.1] - 2024-10-05 +- Fix bug when executing trial on fewer threads than trials (thanks @hanna-kruppe for catching this) + +## [0.8.0] - 2024-10-05 +- **Breaking**: bump MSRV to 1.65 +- Remove `threadpool` dependency, getting rid of memory leaks observed when running under valgrind (thanks @Felix-El) in [#46](https://github.com/LukasKalbertodt/libtest-mimic/pull/46) +- Switch from `termcolor` to `anstream` to get rid of duplicate dependencies (thanks @hanna-kruppe) in [#44](https://github.com/LukasKalbertodt/libtest-mimic/pull/44) +- Bump dev-dependency `fastrand` to `2` (thanks @alexanderkjall) in [#47](https://github.com/LukasKalbertodt/libtest-mimic/pull/47) +- Fix outdated docs + + +## [0.7.3] - 2024-05-10 +- Default to single-threaded tests for WebAssembly (thanks @alexcrichton) in [#41](https://github.com/LukasKalbertodt/libtest-mimic/pull/41) + +## [0.7.2] - 2024-04-09 +- Fix `Conclusion::exit_code` (logic was inverted in 0.7.1) + +## [0.7.1] - 2024-04-09 +- Add `Conclusion::exit_code` and note about destructors/cleanup to docs of `exit` and `exit_if_failed` [`e938e537e`](https://github.com/LukasKalbertodt/libtest-mimic/commit/e938e537e02d8cb9c9791fa63bcb8f4746dc3511) + + +## [0.7.0] - 2024-01-14 +- Also check `kind` when filtering tests (thanks @sunshowers) in [#30](https://github.com/LukasKalbertodt/libtest-mimic/pull/30) + - This is potentially breaking as additional or fewer tests might be executed in some situations. +- Add JSON format output (thanks @PaulWagener and @t-moe) in [#35](https://github.com/LukasKalbertodt/libtest-mimic/pull/35) +- Add no-op flags to add CLI compatibility for IntellJ Rust (thanks @Dinnerbone and @t-moe) [#28](https://github.com/LukasKalbertodt/libtest-mimic/pull/28) / [`70cdc55`](https://github.com/LukasKalbertodt/libtest-mimic/commit/70cdc55ee50df8325d11f5e2cbe53c6bf74d375d) + +## [0.6.1] - 2022-11-05 +### Fixed +- Actually spawn as many threads as specified by `--test-threads` (thanks @hgzimmerman) in [#32](https://github.com/LukasKalbertodt/libtest-mimic/pull/32). +- Fix & improve docs +- Fix badge in README + +### Changed +- Deemphasize MSRV by removing check and note from README in [#24](https://github.com/LukasKalbertodt/libtest-mimic/pull/24). + + +## [0.6.0] - 2022-11-05 +### Changed +- **Breaking**: Updated `clap` to version 4 (thanks @msrd0) +- **Breaking**: Bump MSRV to 1.60 (due to the clap update) + +### Removed +- **Breaking**: Remove `FromStr` impls for `args::{ColorSetting, FormatSetting}` (use `clap::ValueEnum` instead). + +## [0.5.2] - 2022-08-14 +### Added +- Re-add `--nocapture` as a noop argument [#18](https://github.com/LukasKalbertodt/libtest-mimic/pull/18) (thanks @sunshowers) + +### Fixed +- Link in documentation + +## [0.5.1] - 2022-08-13 +### Added +- `Trial::{name, kind, has_ignored_flag, is_test, is_bench}` getters + +## [0.5.0] - 2022-08-13 + +Most parts of this library have been rewritten and the API has changed a lot. +You might be better of just reading the new docs instead of this change log. +I do think the new API is better in many regards. +Apart from an improved API, changes that motivated the rewrite are marked with ⭐. + +### Changed +- **Breaking**: bump MSRV to 1.58 +- **Breaking**: Rename `Test` to `Trial` +- **Breaking**: Rename `run_tests` to `run` +- ⭐ **Breaking**: Make every `Trial` have a runner function instead of `data` + a + global runner function. Thus, the third parameter of `run` is no more. I think + this model is more intuitive. +- **Breaking**: Add `Trial::{test, bench}` constructor functions, use builder + pattern, and make fields private. +- **Breaking**: rename `Args::num_threads` to `test_threads` +- **Breaking**: make fields of `Conclusion` public and remove getter methods +- **Breaking**: remove `RunnerEvent`. This should not have been public. +- ⭐ Tests are now run in main thread when `--test-threads=1` is specified +- ⭐ Reduce number of indirect dependencies considerably +- Fix `rust-version` field in `Cargo.toml` (thanks @hellow554) +- Fix `--ignored` behavior +- Fix some CLI error messages + +### Added +- ⭐Panics in test runners are caught and treated as failure +- ⭐ Lots of integration tests (should make any future development of this library way easier) +- Add `must_use` message for `Conclusion` +- Print total execution time at the end of the run +- Allow benchmarks to run in test mode +- `--include-ignored` + +### Removed +- **Breaking**: remove unsupported CLI options. They were ignored anyway, but + the CLI would accept them. + + +## [0.4.1] - 2022-06-07 + +- Add `rust = "1.56"` to `Cargo.toml`, stating the existing MSRV. +- Update `crossbeam-channel` to deduplicate some indirect dependencies. + +## [0.4.0] - 2022-05-13 +- **Breaking**: Update to Rust 2021, bumping MSRV to 1.56 +- Fix `--list --ignored` behavior + + +## [0.3.0] - 2020-06-28 +### Added +- Add support for running tests in parallel #4 +- Add `Arguments::from_iter` #5 + +## [0.2.0] - 2019-10-02 +### Changed +- Upgrade dependencies #3 +- Flush stdout after printing test name 4a36b3318b69df233b0db7d1af3caf276e6bb070 + +### Fixed +- Fix overflow bug when calculating number of passed tests 264fe6f8a986ab0c02f4a85e64e42ee17596923c + +## 0.1.0 - 2018-07-23 +### Added +- Everything. + + +[Unreleased]: https://github.com/LukasKalbertodt/libtest-mimic/compare/v0.8.1...HEAD +[0.8.1]: https://github.com/LukasKalbertodt/libtest-mimic/compare/v0.8.0...v0.8.1 +[0.8.0]: https://github.com/LukasKalbertodt/libtest-mimic/compare/v0.7.3...v0.8.0 +[0.7.3]: https://github.com/LukasKalbertodt/libtest-mimic/compare/v0.7.2...v0.7.3 +[0.7.2]: https://github.com/LukasKalbertodt/libtest-mimic/compare/v0.7.1...v0.7.2 +[0.7.1]: https://github.com/LukasKalbertodt/libtest-mimic/compare/v0.7.0...v0.7.1 +[0.7.0]: https://github.com/LukasKalbertodt/libtest-mimic/compare/v0.6.1...v0.7.0 +[0.6.1]: https://github.com/LukasKalbertodt/libtest-mimic/compare/v0.6.0...v0.6.1 +[0.6.0]: https://github.com/LukasKalbertodt/libtest-mimic/compare/v0.5.2...v0.6.0 +[0.5.2]: https://github.com/LukasKalbertodt/libtest-mimic/compare/v0.5.1...v0.5.2 +[0.5.1]: https://github.com/LukasKalbertodt/libtest-mimic/compare/v0.5.0...v0.5.1 +[0.5.0]: https://github.com/LukasKalbertodt/libtest-mimic/compare/v0.4.1...v0.5.0 +[0.4.1]: https://github.com/LukasKalbertodt/libtest-mimic/compare/v0.4.0...v0.4.1 +[0.4.0]: https://github.com/LukasKalbertodt/libtest-mimic/compare/v0.3.0...v0.4.0 +[0.3.0]: https://github.com/LukasKalbertodt/libtest-mimic/compare/v0.2.0...v0.3.0 +[0.2.0]: https://github.com/LukasKalbertodt/libtest-mimic/compare/v0.1.0...v0.2.0 +[0.1.1]: https://github.com/LukasKalbertodt/libtest-mimic/compare/v0.1.0...v0.1.1 diff --git a/tools/vendor/libtest-mimic/Cargo.lock b/tools/vendor/libtest-mimic/Cargo.lock new file mode 100644 index 0000000000..5783c064a9 --- /dev/null +++ b/tools/vendor/libtest-mimic/Cargo.lock @@ -0,0 +1,276 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "clap" +version = "4.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "escape8259" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5692dd7b5a1978a5aeb0ce83b7655c58ca8efdcb79d21036ea249da95afec2c6" + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "libtest-mimic" +version = "0.8.1" +dependencies = [ + "anstream", + "anstyle", + "clap", + "escape8259", + "fastrand", + "pretty_assertions", +] + +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" diff --git a/tools/vendor/libtest-mimic/Cargo.toml b/tools/vendor/libtest-mimic/Cargo.toml new file mode 100644 index 0000000000..ae53f79830 --- /dev/null +++ b/tools/vendor/libtest-mimic/Cargo.toml @@ -0,0 +1,55 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.65" +name = "libtest-mimic" +version = "0.8.1" +authors = ["Lukas Kalbertodt "] +exclude = [".github"] +description = """ +Write your own test harness that looks and behaves like the built-in test harness used by `rustc --test` +""" +documentation = "https://docs.rs/libtest-mimic" +readme = "README.md" +keywords = [ + "libtest", + "test", + "built-in", + "framework", + "harness", +] +categories = [ + "development-tools::testing", + "development-tools::build-utils", +] +license = "MIT/Apache-2.0" +repository = "https://github.com/LukasKalbertodt/libtest-mimic" + +[dependencies.anstream] +version = "0.6.14" + +[dependencies.anstyle] +version = "1.0.7" + +[dependencies.clap] +version = "4.0.8" +features = ["derive"] + +[dependencies.escape8259] +version = "0.5.2" + +[dev-dependencies.fastrand] +version = "2.0.0" + +[dev-dependencies.pretty_assertions] +version = "1.2.1" diff --git a/tools/vendor/libtest-mimic/Cargo.toml.orig b/tools/vendor/libtest-mimic/Cargo.toml.orig new file mode 100644 index 0000000000..1f0adb1330 --- /dev/null +++ b/tools/vendor/libtest-mimic/Cargo.toml.orig @@ -0,0 +1,29 @@ +[package] +name = "libtest-mimic" +version = "0.8.1" +authors = ["Lukas Kalbertodt "] +edition = "2021" +rust-version = "1.65" + +description = """ +Write your own test harness that looks and behaves like the built-in test \ +harness used by `rustc --test` +""" +documentation = "https://docs.rs/libtest-mimic" +repository = "https://github.com/LukasKalbertodt/libtest-mimic" +license = "MIT/Apache-2.0" +keywords = ["libtest", "test", "built-in", "framework", "harness"] +categories = ["development-tools::testing", "development-tools::build-utils"] +readme = "README.md" + +exclude = [".github"] + +[dependencies] +clap = { version = "4.0.8", features = ["derive"] } +escape8259 = "0.5.2" +anstream = "0.6.14" +anstyle = "1.0.7" + +[dev-dependencies] +fastrand = "2.0.0" +pretty_assertions = "1.2.1" diff --git a/tools/vendor/libtest-mimic/LICENSE-APACHE b/tools/vendor/libtest-mimic/LICENSE-APACHE new file mode 100644 index 0000000000..11069edd79 --- /dev/null +++ b/tools/vendor/libtest-mimic/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/tools/vendor/libtest-mimic/LICENSE-MIT b/tools/vendor/libtest-mimic/LICENSE-MIT new file mode 100644 index 0000000000..5618ca15e0 --- /dev/null +++ b/tools/vendor/libtest-mimic/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016 Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/tools/vendor/libtest-mimic/README.md b/tools/vendor/libtest-mimic/README.md new file mode 100644 index 0000000000..7d844de659 --- /dev/null +++ b/tools/vendor/libtest-mimic/README.md @@ -0,0 +1,36 @@ +# libtest-mimic + +[CI status of master](https://github.com/LukasKalbertodt/libtest-mimic/actions?query=workflow%3ACI+branch%3Amaster) +[Crates.io Version](https://crates.io/crates/libtest-mimic) +[docs.rs](https://docs.rs/libtest-mimic) + +Write your own test harness that looks and behaves like the built-in test harness (used by `rustc --test`)! + +This is a simple and small testing framework that mimics the original `libtest`. +That means: all output looks pretty much like `cargo test` and most CLI arguments are understood and used. +With that plumbing work out of the way, your test runner can focus on the actual testing. + +See [**the documentation**](https://docs.rs/libtest-mimic) or [the `examples/` folder](/examples) for more information. + + +

+ +

+ + +--- + +## License + +Licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/tools/vendor/libtest-mimic/examples/README.md b/tools/vendor/libtest-mimic/examples/README.md new file mode 100644 index 0000000000..0cb37339c7 --- /dev/null +++ b/tools/vendor/libtest-mimic/examples/README.md @@ -0,0 +1,5 @@ +Examples +======== + +- `simple`: five dummy tests are created and executed +- **`tidy`**: most useful example. Generates a test for each `.rs` file and runs a simply tidy script as test. diff --git a/tools/vendor/libtest-mimic/examples/simple.rs b/tools/vendor/libtest-mimic/examples/simple.rs new file mode 100644 index 0000000000..4c4e165c73 --- /dev/null +++ b/tools/vendor/libtest-mimic/examples/simple.rs @@ -0,0 +1,39 @@ +extern crate libtest_mimic; + +use std::{process::ExitCode, thread, time}; +use libtest_mimic::{Arguments, Trial, Failed}; + + +fn main() -> ExitCode { + let args = Arguments::from_args(); + + let tests = vec![ + Trial::test("check_toph", check_toph), + Trial::test("check_sokka", check_sokka), + Trial::test("long_computation", long_computation).with_ignored_flag(true), + Trial::test("foo", compile_fail_dummy).with_kind("compile-fail"), + Trial::test("check_katara", check_katara), + ]; + + libtest_mimic::run(&args, tests).exit_code() +} + + +// Tests + +fn check_toph() -> Result<(), Failed> { + Ok(()) +} +fn check_katara() -> Result<(), Failed> { + Ok(()) +} +fn check_sokka() -> Result<(), Failed> { + Err("Sokka tripped and fell :(".into()) +} +fn long_computation() -> Result<(), Failed> { + thread::sleep(time::Duration::from_secs(1)); + Ok(()) +} +fn compile_fail_dummy() -> Result<(), Failed> { + Ok(()) +} diff --git a/tools/vendor/libtest-mimic/examples/tidy.rs b/tools/vendor/libtest-mimic/examples/tidy.rs new file mode 100644 index 0000000000..c9e1da646d --- /dev/null +++ b/tools/vendor/libtest-mimic/examples/tidy.rs @@ -0,0 +1,83 @@ +extern crate libtest_mimic; + +use libtest_mimic::{Arguments, Trial, Failed}; + +use std::{ + env, + error::Error, + ffi::OsStr, + fs, + path::Path, process::ExitCode, +}; + + +fn main() -> Result> { + let args = Arguments::from_args(); + let tests = collect_tests()?; + Ok(libtest_mimic::run(&args, tests).exit_code()) +} + +/// Creates one test for each `.rs` file in the current directory or +/// sub-directories of the current directory. +fn collect_tests() -> Result, Box> { + fn visit_dir(path: &Path, tests: &mut Vec) -> Result<(), Box> { + for entry in fs::read_dir(path)? { + let entry = entry?; + let file_type = entry.file_type()?; + + // Handle files + let path = entry.path(); + if file_type.is_file() { + if path.extension() == Some(OsStr::new("rs")) { + let name = path + .strip_prefix(env::current_dir()?)? + .display() + .to_string(); + + let test = Trial::test(name, move || check_file(&path)) + .with_kind("tidy"); + tests.push(test); + } + } else if file_type.is_dir() { + // Handle directories + visit_dir(&path, tests)?; + } + } + + Ok(()) + } + + // We recursively look for `.rs` files, starting from the current + // directory. + let mut tests = Vec::new(); + let current_dir = env::current_dir()?; + visit_dir(¤t_dir, &mut tests)?; + + Ok(tests) +} + +/// Performs a couple of tidy tests. +fn check_file(path: &Path) -> Result<(), Failed> { + let content = fs::read(path).map_err(|e| format!("Cannot read file: {e}"))?; + + // Check that the file is valid UTF-8 + let content = String::from_utf8(content) + .map_err(|_| "The file's contents are not a valid UTF-8 string!")?; + + // Check for `\r`: we only want `\n` line breaks! + if content.contains('\r') { + return Err("Contains '\\r' chars. Please use ' \\n' line breaks only!".into()); + } + + // Check for tab characters `\t` + if content.contains('\t') { + return Err("Contains tab characters ('\\t'). Indent with four spaces!".into()); + } + + // Check for too long lines + if content.lines().any(|line| line.chars().count() > 100) { + return Err("Contains lines longer than 100 codepoints!".into()); + } + + Ok(()) +} diff --git a/tools/vendor/libtest-mimic/src/args.rs b/tools/vendor/libtest-mimic/src/args.rs new file mode 100644 index 0000000000..e94370eaa2 --- /dev/null +++ b/tools/vendor/libtest-mimic/src/args.rs @@ -0,0 +1,214 @@ +use clap::{Parser, ValueEnum}; + +/// Command line arguments. +/// +/// This type represents everything the user can specify via CLI args. The main +/// method is [`from_args`][Arguments::from_args] which reads the global +/// `std::env::args()` and parses them into this type. +/// +/// `libtest-mimic` supports a subset of all args/flags supported by the +/// official test harness. There are also some other minor CLI differences, but +/// the main use cases should work exactly like with the built-in harness. +#[derive(Parser, Debug, Clone, Default)] +#[command( + help_template = "USAGE: [OPTIONS] [FILTER]\n\n{all-args}\n\n\n{after-help}", + disable_version_flag = true, + after_help = "By default, all tests are run in parallel. This can be altered with the \n\ + --test-threads flag when running tests (set it to 1).", +)] +pub struct Arguments { + // ============== FLAGS =================================================== + /// Run ignored and non-ignored tests. + #[arg(long = "include-ignored", help = "Run ignored tests")] + pub include_ignored: bool, + + /// Run only ignored tests. + #[arg(long = "ignored", help = "Run ignored tests")] + pub ignored: bool, + + /// Run tests, but not benchmarks. + #[arg( + long = "test", + conflicts_with = "bench", + help = "Run tests and not benchmarks", + )] + pub test: bool, + + /// Run benchmarks, but not tests. + #[arg(long = "bench", help = "Run benchmarks instead of tests")] + pub bench: bool, + + /// Only list all tests and benchmarks. + #[arg(long = "list", help = "List all tests and benchmarks")] + pub list: bool, + + /// No-op, ignored (libtest-mimic always runs in no-capture mode) + #[arg(long = "nocapture", help = "No-op (libtest-mimic always runs in no-capture mode)")] + pub nocapture: bool, + + /// No-op, ignored. libtest-mimic does not currently capture stdout. + #[arg(long = "show-output")] + pub show_output: bool, + + /// No-op, ignored. Flag only exists for CLI compatibility with libtest. + #[arg(short = 'Z')] + pub unstable_flags: Option, + + /// If set, filters are matched exactly rather than by substring. + #[arg( + long = "exact", + help = "Exactly match filters rather than by substring", + )] + pub exact: bool, + + /// If set, display only one character per test instead of one line. + /// Especially useful for huge test suites. + /// + /// This is an alias for `--format=terse`. If this is set, `format` is + /// `None`. + #[arg( + short = 'q', + long = "quiet", + conflicts_with = "format", + help = "Display one character per test instead of one line. Alias to --format=terse", + )] + pub quiet: bool, + + // ============== OPTIONS ================================================= + /// Number of threads used for parallel testing. + #[arg( + long = "test-threads", + help = "Number of threads used for running tests in parallel. If set to 1, \n\ + all tests are run in the main thread.", + )] + pub test_threads: Option, + + /// Path of the logfile. If specified, everything will be written into the + /// file instead of stdout. + #[arg( + long = "logfile", + value_name = "PATH", + help = "Write logs to the specified file instead of stdout", + )] + pub logfile: Option, + + /// A list of filters. Tests whose names contain parts of any of these + /// filters are skipped. + #[arg( + long = "skip", + value_name = "FILTER", + help = "Skip tests whose names contain FILTER (this flag can be used multiple times)", + )] + pub skip: Vec, + + /// Specifies whether or not to color the output. + #[arg( + long = "color", + value_enum, + value_name = "auto|always|never", + help = "Configure coloring of output: \n\ + - auto = colorize if stdout is a tty and tests are run on serially (default)\n\ + - always = always colorize output\n\ + - never = never colorize output\n", + )] + pub color: Option, + + /// Specifies the format of the output. + #[arg( + long = "format", + value_enum, + value_name = "pretty|terse|json", + help = "Configure formatting of output: \n\ + - pretty = Print verbose output\n\ + - terse = Display one character per test\n\ + - json = Print json events\n", + )] + pub format: Option, + + // ============== POSITIONAL VALUES ======================================= + /// Filter string. Only tests which contain this string are run. + #[arg( + value_name = "FILTER", + help = "The FILTER string is tested against the name of all tests, and only those tests \ + whose names contain the filter are run.", + )] + pub filter: Option, +} + +impl Arguments { + /// Parses the global CLI arguments given to the application. + /// + /// If the parsing fails (due to incorrect CLI args), an error is shown and + /// the application exits. If help is requested (`-h` or `--help`), a help + /// message is shown and the application exits, too. + pub fn from_args() -> Self { + Parser::parse() + } + + /// Like `from_args()`, but operates on an explicit iterator and not the + /// global arguments. Note that the first element is the executable name! + pub fn from_iter(iter: I) -> Self + where + Self: Sized, + I: IntoIterator, + I::Item: Into + Clone, + { + Parser::parse_from(iter) + } +} + +/// Possible values for the `--color` option. +#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)] +pub enum ColorSetting { + /// Colorize output if stdout is a tty and tests are run on serially + /// (default). + Auto, + + /// Always colorize output. + Always, + + /// Never colorize output. + Never, +} + +impl Default for ColorSetting { + fn default() -> Self { + ColorSetting::Auto + } +} + +/// Possible values for the `-Z` option +#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)] +pub enum UnstableFlags { + UnstableOptions, +} + +/// Possible values for the `--format` option. +#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)] +pub enum FormatSetting { + /// One line per test. Output for humans. (default) + Pretty, + + /// One character per test. Usefull for test suites with many tests. + Terse, + + /// Json output + Json, +} + +impl Default for FormatSetting { + fn default() -> Self { + FormatSetting::Pretty + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn verify_cli() { + use clap::CommandFactory; + Arguments::command().debug_assert(); + } +} diff --git a/tools/vendor/libtest-mimic/src/lib.rs b/tools/vendor/libtest-mimic/src/lib.rs new file mode 100644 index 0000000000..177d37c2a1 --- /dev/null +++ b/tools/vendor/libtest-mimic/src/lib.rs @@ -0,0 +1,590 @@ +//! Write your own tests and benchmarks that look and behave like built-in tests! +//! +//! This is a simple and small test harness that mimics the original `libtest` +//! (used by `cargo test`/`rustc --test`). That means: all output looks pretty +//! much like `cargo test` and most CLI arguments are understood and used. With +//! that plumbing work out of the way, your test runner can focus on the actual +//! testing. +//! +//! For a small real world example, see [`examples/tidy.rs`][1]. +//! +//! [1]: https://github.com/LukasKalbertodt/libtest-mimic/blob/master/examples/tidy.rs +//! +//! # Usage +//! +//! To use this, you most likely want to add a manual `[[test]]` section to +//! `Cargo.toml` and set `harness = false`. For example: +//! +//! ```toml +//! [[test]] +//! name = "mytest" +//! path = "tests/mytest.rs" +//! harness = false +//! ``` +//! +//! And in `tests/mytest.rs` you would call [`run`] in the `main` function: +//! +//! ```no_run +//! use libtest_mimic::{Arguments, Trial}; +//! +//! +//! // Parse command line arguments +//! let args = Arguments::from_args(); +//! +//! // Create a list of tests and/or benchmarks (in this case: two dummy tests). +//! let tests = vec![ +//! Trial::test("succeeding_test", move || Ok(())), +//! Trial::test("failing_test", move || Err("Woops".into())), +//! ]; +//! +//! // Run all tests and exit the application appropriatly. +//! libtest_mimic::run(&args, tests).exit(); +//! ``` +//! +//! Instead of returning `Ok` or `Err` directly, you want to actually perform +//! your tests, of course. See [`Trial::test`] for more information on how to +//! define a test. You can of course list all your tests manually. But in many +//! cases it is useful to generate one test per file in a directory, for +//! example. +//! +//! You can then run `cargo test --test mytest` to run it. To see the CLI +//! arguments supported by this crate, run `cargo test --test mytest -- -h`. +//! +//! +//! # Known limitations and differences to the official test harness +//! +//! `libtest-mimic` works on a best-effort basis: it tries to be as close to +//! `libtest` as possible, but there are differences for a variety of reasons. +//! For example, some rarely used features might not be implemented, some +//! features are extremely difficult to implement, and removing minor, +//! unimportant differences is just not worth the hassle. +//! +//! Some of the notable differences: +//! +//! - Output capture and `--nocapture`: simply not supported. The official +//! `libtest` uses internal `std` functions to temporarily redirect output. +//! `libtest-mimic` cannot use those. See [this issue][capture] for more +//! information. +//! - `--format=junit` +//! - Also see [#13](https://github.com/LukasKalbertodt/libtest-mimic/issues/13) +//! +//! [capture]: https://github.com/LukasKalbertodt/libtest-mimic/issues/9 + +#![forbid(unsafe_code)] + +use std::{ + borrow::Cow, + fmt, + process::{self, ExitCode}, + sync::{mpsc, Mutex}, + thread, + time::Instant, +}; + +mod args; +mod printer; + +use printer::Printer; + +pub use crate::args::{Arguments, ColorSetting, FormatSetting}; + + + +/// A single test or benchmark. +/// +/// The original `libtest` often calls benchmarks "tests", which is a bit +/// confusing. So in this library, it is called "trial". +/// +/// A trial is created via [`Trial::test`] or [`Trial::bench`]. The trial's +/// `name` is printed and used for filtering. The `runner` is called when the +/// test/benchmark is executed to determine its outcome. If `runner` panics, +/// the trial is considered "failed". If you need the behavior of +/// `#[should_panic]` you need to catch the panic yourself. You likely want to +/// compare the panic payload to an expected value anyway. +pub struct Trial { + runner: Box Outcome + Send>, + info: TestInfo, +} + +impl Trial { + /// Creates a (non-benchmark) test with the given name and runner. + /// + /// The runner returning `Ok(())` is interpreted as the test passing. If the + /// runner returns `Err(_)`, the test is considered failed. + pub fn test(name: impl Into, runner: R) -> Self + where + R: FnOnce() -> Result<(), Failed> + Send + 'static, + { + Self { + runner: Box::new(move |_test_mode| match runner() { + Ok(()) => Outcome::Passed, + Err(failed) => Outcome::Failed(failed), + }), + info: TestInfo { + name: name.into(), + kind: String::new(), + is_ignored: false, + is_bench: false, + }, + } + } + + /// Creates a benchmark with the given name and runner. + /// + /// If the runner's parameter `test_mode` is `true`, the runner function + /// should run all code just once, without measuring, just to make sure it + /// does not panic. If the parameter is `false`, it should perform the + /// actual benchmark. If `test_mode` is `true` you may return `Ok(None)`, + /// but if it's `false`, you have to return a `Measurement`, or else the + /// benchmark is considered a failure. + /// + /// `test_mode` is `true` if neither `--bench` nor `--test` are set, and + /// `false` when `--bench` is set. If `--test` is set, benchmarks are not + /// ran at all, and both flags cannot be set at the same time. + pub fn bench(name: impl Into, runner: R) -> Self + where + R: FnOnce(bool) -> Result, Failed> + Send + 'static, + { + Self { + runner: Box::new(move |test_mode| match runner(test_mode) { + Err(failed) => Outcome::Failed(failed), + Ok(_) if test_mode => Outcome::Passed, + Ok(Some(measurement)) => Outcome::Measured(measurement), + Ok(None) + => Outcome::Failed("bench runner returned `Ok(None)` in bench mode".into()), + }), + info: TestInfo { + name: name.into(), + kind: String::new(), + is_ignored: false, + is_bench: true, + }, + } + } + + /// Sets the "kind" of this test/benchmark. If this string is not + /// empty, it is printed in brackets before the test name (e.g. + /// `test [my-kind] test_name`). (Default: *empty*) + /// + /// This is the only extension to the original libtest. + pub fn with_kind(self, kind: impl Into) -> Self { + Self { + info: TestInfo { + kind: kind.into(), + ..self.info + }, + ..self + } + } + + /// Sets whether or not this test is considered "ignored". (Default: `false`) + /// + /// With the built-in test suite, you can annotate `#[ignore]` on tests to + /// not execute them by default (for example because they take a long time + /// or require a special environment). If the `--ignored` flag is set, + /// ignored tests are executed, too. + pub fn with_ignored_flag(self, is_ignored: bool) -> Self { + Self { + info: TestInfo { + is_ignored, + ..self.info + }, + ..self + } + } + + /// Returns the name of this trial. + pub fn name(&self) -> &str { + &self.info.name + } + + /// Returns the kind of this trial. If you have not set a kind, this is an + /// empty string. + pub fn kind(&self) -> &str { + &self.info.kind + } + + /// Returns whether this trial has been marked as *ignored*. + pub fn has_ignored_flag(&self) -> bool { + self.info.is_ignored + } + + /// Returns `true` iff this trial is a test (as opposed to a benchmark). + pub fn is_test(&self) -> bool { + !self.info.is_bench + } + + /// Returns `true` iff this trial is a benchmark (as opposed to a test). + pub fn is_bench(&self) -> bool { + self.info.is_bench + } +} + +impl fmt::Debug for Trial { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct OpaqueRunner; + impl fmt::Debug for OpaqueRunner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("") + } + } + + f.debug_struct("Test") + .field("runner", &OpaqueRunner) + .field("name", &self.info.name) + .field("kind", &self.info.kind) + .field("is_ignored", &self.info.is_ignored) + .field("is_bench", &self.info.is_bench) + .finish() + } +} + +#[derive(Debug)] +struct TestInfo { + name: String, + kind: String, + is_ignored: bool, + is_bench: bool, +} + +impl TestInfo { + fn test_name_with_kind(&self) -> Cow<'_, str> { + if self.kind.is_empty() { + Cow::Borrowed(&self.name) + } else { + Cow::Owned(format!("[{}] {}", self.kind, self.name)) + } + } +} + +/// Output of a benchmark. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Measurement { + /// Average time in ns. + pub avg: u64, + + /// Variance in ns. + pub variance: u64, +} + +/// Indicates that a test/benchmark has failed. Optionally carries a message. +/// +/// You usually want to use the `From` impl of this type, which allows you to +/// convert any `T: fmt::Display` (e.g. `String`, `&str`, ...) into `Failed`. +#[derive(Debug, Clone)] +pub struct Failed { + msg: Option, +} + +impl Failed { + /// Creates an instance without message. + pub fn without_message() -> Self { + Self { msg: None } + } + + /// Returns the message of this instance. + pub fn message(&self) -> Option<&str> { + self.msg.as_deref() + } +} + +impl From for Failed { + fn from(msg: M) -> Self { + Self { + msg: Some(msg.to_string()) + } + } +} + + + +/// The outcome of performing a test/benchmark. +#[derive(Debug, Clone)] +enum Outcome { + /// The test passed. + Passed, + + /// The test or benchmark failed. + Failed(Failed), + + /// The test or benchmark was ignored. + Ignored, + + /// The benchmark was successfully run. + Measured(Measurement), +} + +/// Contains information about the entire test run. Is returned by[`run`]. +/// +/// This type is marked as `#[must_use]`. Usually, you just call +/// [`exit()`][Conclusion::exit] on the result of `run` to exit the application +/// with the correct exit code. But you can also store this value and inspect +/// its data. +#[derive(Clone, Debug, PartialEq, Eq)] +#[must_use = "Call `exit()` or `exit_if_failed()` to set the correct return code"] +pub struct Conclusion { + /// Number of tests and benchmarks that were filtered out (either by the + /// filter-in pattern or by `--skip` arguments). + pub num_filtered_out: u64, + + /// Number of passed tests. + pub num_passed: u64, + + /// Number of failed tests and benchmarks. + pub num_failed: u64, + + /// Number of ignored tests and benchmarks. + pub num_ignored: u64, + + /// Number of benchmarks that successfully ran. + pub num_measured: u64, +} + +impl Conclusion { + /// Returns an exit code that can be returned from `main` to signal + /// success/failure to the calling process. + pub fn exit_code(&self) -> ExitCode { + if self.has_failed() { + ExitCode::from(101) + } else { + ExitCode::SUCCESS + } + } + + /// Returns whether there have been any failures. + pub fn has_failed(&self) -> bool { + self.num_failed > 0 + } + + /// Exits the application with an appropriate error code (0 if all tests + /// have passed, 101 if there have been failures). This uses + /// [`process::exit`], meaning that destructors are not ran. Consider + /// using [`Self::exit_code`] instead for a proper program cleanup. + pub fn exit(&self) -> ! { + self.exit_if_failed(); + process::exit(0); + } + + /// Exits the application with error code 101 if there were any failures. + /// Otherwise, returns normally. This uses [`process::exit`], meaning that + /// destructors are not ran. Consider using [`Self::exit_code`] instead for + /// a proper program cleanup. + pub fn exit_if_failed(&self) { + if self.has_failed() { + process::exit(101) + } + } + + fn empty() -> Self { + Self { + num_filtered_out: 0, + num_passed: 0, + num_failed: 0, + num_ignored: 0, + num_measured: 0, + } + } +} + +impl Arguments { + /// Returns `true` if the given test should be ignored. + fn is_ignored(&self, test: &Trial) -> bool { + (test.info.is_ignored && !self.ignored && !self.include_ignored) + || (test.info.is_bench && self.test) + || (!test.info.is_bench && self.bench) + } + + fn is_filtered_out(&self, test: &Trial) -> bool { + let test_name = test.name(); + // Match against the full test name, including the kind. This upholds the invariant that if + // --list prints out: + // + // : test + // + // then "--exact " runs exactly that test. + let test_name_with_kind = test.info.test_name_with_kind(); + + // If a filter was specified, apply this + if let Some(filter) = &self.filter { + match self.exact { + // For exact matches, we want to match against either the test name (to maintain + // backwards compatibility with older versions of libtest-mimic), or the test kind + // (technically more correct with respect to matching against the output of --list.) + true if test_name != filter && &test_name_with_kind != filter => return true, + false if !test_name_with_kind.contains(filter) => return true, + _ => {} + }; + } + + // If any skip pattern were specified, test for all patterns. + for skip_filter in &self.skip { + match self.exact { + // For exact matches, we want to match against either the test name (to maintain + // backwards compatibility with older versions of libtest-mimic), or the test kind + // (technically more correct with respect to matching against the output of --list.) + true if test_name == skip_filter || &test_name_with_kind == skip_filter => { + return true + } + false if test_name_with_kind.contains(skip_filter) => return true, + _ => {} + } + } + + if self.ignored && !test.info.is_ignored { + return true; + } + + false + } +} + +/// Runs all given trials (tests & benchmarks). +/// +/// This is the central function of this crate. It provides the framework for +/// the testing harness. It does all the printing and house keeping. +/// +/// The returned value contains a couple of useful information. See +/// [`Conclusion`] for more information. If `--list` was specified, a list is +/// printed and a dummy `Conclusion` is returned. +pub fn run(args: &Arguments, mut tests: Vec) -> Conclusion { + let start_instant = Instant::now(); + let mut conclusion = Conclusion::empty(); + + // Apply filtering + if args.filter.is_some() || !args.skip.is_empty() || args.ignored { + let len_before = tests.len() as u64; + tests.retain(|test| !args.is_filtered_out(test)); + conclusion.num_filtered_out = len_before - tests.len() as u64; + } + let tests = tests; + + // Create printer which is used for all output. + let mut printer = printer::Printer::new(args, &tests); + + // If `--list` is specified, just print the list and return. + if args.list { + printer.print_list(&tests, args.ignored); + return Conclusion::empty(); + } + + // Print number of tests + printer.print_title(tests.len() as u64); + + let mut failed_tests = Vec::new(); + let mut handle_outcome = |outcome: Outcome, test: TestInfo, printer: &mut Printer| { + printer.print_single_outcome(&test, &outcome); + + // Handle outcome + match outcome { + Outcome::Passed => conclusion.num_passed += 1, + Outcome::Failed(failed) => { + failed_tests.push((test, failed.msg)); + conclusion.num_failed += 1; + }, + Outcome::Ignored => conclusion.num_ignored += 1, + Outcome::Measured(_) => conclusion.num_measured += 1, + } + }; + + // Execute all tests. + let test_mode = !args.bench; + + let num_threads = platform_defaults_to_one_thread() + .then_some(1) + .or(args.test_threads) + .or_else(|| std::thread::available_parallelism().ok().map(Into::into)) + .unwrap_or(1); + + if num_threads == 1 { + // Run test sequentially in main thread + for test in tests { + // Print `test foo ...`, run the test, then print the outcome in + // the same line. + printer.print_test(&test.info); + let outcome = if args.is_ignored(&test) { + Outcome::Ignored + } else { + run_single(test.runner, test_mode) + }; + handle_outcome(outcome, test.info, &mut printer); + } + } else { + // Run test in thread pool. + let (sender, receiver) = mpsc::channel(); + + let num_tests = tests.len(); + // TODO: this should use a mpmc channel, once that's stabilized in std. + let iter = Mutex::new(tests.into_iter()); + thread::scope(|scope| { + // Start worker threads + for _ in 0..num_threads { + scope.spawn(|| { + loop { + // Get next test to process from the iterator. + let Some(trial) = iter.lock().unwrap().next() else { + break; + }; + + let payload = if args.is_ignored(&trial) { + (Outcome::Ignored, trial.info) + } else { + let outcome = run_single(trial.runner, test_mode); + (outcome, trial.info) + }; + + // It's fine to ignore the result of sending. If the + // receiver has hung up, everything will wind down soon + // anyway. + let _ = sender.send(payload); + } + }); + } + + // Print results of tests that already dinished + for (outcome, test_info) in receiver.iter().take(num_tests) { + // In multithreaded mode, we do only print the start of the line + // after the test ran, as otherwise it would lead to terribly + // interleaved output. + printer.print_test(&test_info); + handle_outcome(outcome, test_info, &mut printer); + } + }); + + } + + // Print failures if there were any, and the final summary. + if !failed_tests.is_empty() { + printer.print_failures(&failed_tests); + } + + printer.print_summary(&conclusion, start_instant.elapsed()); + + conclusion +} + +/// Returns whether the current host platform should use a single thread by +/// default rather than a thread pool by default. Some platforms, such as +/// WebAssembly, don't have native support for threading at this time. +fn platform_defaults_to_one_thread() -> bool { + cfg!(target_family = "wasm") +} + +/// Runs the given runner, catching any panics and treating them as a failed test. +fn run_single(runner: Box Outcome + Send>, test_mode: bool) -> Outcome { + use std::panic::{catch_unwind, AssertUnwindSafe}; + + catch_unwind(AssertUnwindSafe(move || runner(test_mode))).unwrap_or_else(|e| { + // The `panic` information is just an `Any` object representing the + // value the panic was invoked with. For most panics (which use + // `panic!` like `println!`), this is either `&str` or `String`. + let payload = e.downcast_ref::() + .map(|s| s.as_str()) + .or(e.downcast_ref::<&str>().map(|s| *s)); + + let msg = match payload { + Some(payload) => format!("test panicked: {payload}"), + None => format!("test panicked"), + }; + Outcome::Failed(msg.into()) + }) +} diff --git a/tools/vendor/libtest-mimic/src/printer.rs b/tools/vendor/libtest-mimic/src/printer.rs new file mode 100644 index 0000000000..7c93eddd3d --- /dev/null +++ b/tools/vendor/libtest-mimic/src/printer.rs @@ -0,0 +1,357 @@ +//! Definition of the `Printer`. +//! +//! This is just an abstraction for everything that is printed to the screen +//! (or logfile, if specified). These parameters influence printing: +//! - `color` +//! - `format` (and `quiet`) +//! - `logfile` + +use std::{fs::File, io::Write, time::Duration}; + +use anstream::AutoStream; +use anstyle::{AnsiColor, Color, Style}; + +use crate::{ + Arguments, ColorSetting, Conclusion, Failed, FormatSetting, Measurement, Outcome, TestInfo, + Trial, +}; + +pub(crate) struct Printer { + out: Box, + format: FormatSetting, + name_width: usize, + kind_width: usize, +} + +impl Printer { + /// Creates a new printer configured by the given arguments (`format`, + /// `quiet`, `color` and `logfile` options). + pub(crate) fn new(args: &Arguments, tests: &[Trial]) -> Self { + let color_arg = args.color.unwrap_or(ColorSetting::Auto); + + // Determine target of all output + let out: Box = if let Some(logfile) = &args.logfile { + let f = File::create(logfile).expect("failed to create logfile"); + if color_arg == ColorSetting::Always { + Box::new(AutoStream::always(f)) + } else { + Box::new(AutoStream::never(f)) + } + } else { + let choice = match color_arg { + ColorSetting::Auto => anstream::ColorChoice::Auto, + ColorSetting::Always => anstream::ColorChoice::Always, + ColorSetting::Never => anstream::ColorChoice::Never, + }; + Box::new(AutoStream::new(std::io::stdout(), choice)) + }; + + // Determine correct format + let format = if args.quiet { + FormatSetting::Terse + } else { + args.format.unwrap_or(FormatSetting::Pretty) + }; + + // Determine max test name length to do nice formatting later. + // + // Unicode is hard and there is no way we can properly align/pad the + // test names and outcomes. Counting the number of code points is just + // a cheap way that works in most cases. Usually, these names are + // ASCII. + let name_width = tests.iter() + .map(|test| test.info.name.chars().count()) + .max() + .unwrap_or(0); + + let kind_width = tests.iter() + .map(|test| { + if test.info.kind.is_empty() { + 0 + } else { + // The two braces [] and one space + test.info.kind.chars().count() + 3 + } + }) + .max() + .unwrap_or(0); + + Self { + out, + format, + name_width, + kind_width, + } + } + + /// Prints the first line "running 3 tests". + pub(crate) fn print_title(&mut self, num_tests: u64) { + match self.format { + FormatSetting::Pretty | FormatSetting::Terse => { + let plural_s = if num_tests == 1 { "" } else { "s" }; + + writeln!(self.out).unwrap(); + writeln!(self.out, "running {} test{}", num_tests, plural_s).unwrap(); + } + FormatSetting::Json => writeln!( + self.out, + r#"{{ "type": "suite", "event": "started", "test_count": {} }}"#, + num_tests + ) + .unwrap(), + } + } + + /// Prints the text announcing the test (e.g. "test foo::bar ... "). Prints + /// nothing in terse mode. + pub(crate) fn print_test(&mut self, info: &TestInfo) { + let TestInfo { name, kind, .. } = info; + match self.format { + FormatSetting::Pretty => { + let kind = if kind.is_empty() { + String::new() + } else { + format!("[{}] ", kind) + }; + + write!( + self.out, + "test {: <2$}{: <3$} ... ", + kind, + name, + self.kind_width, + self.name_width, + ).unwrap(); + self.out.flush().unwrap(); + } + FormatSetting::Terse => { + // In terse mode, nothing is printed before the job. Only + // `print_single_outcome` prints one character. + } + FormatSetting::Json => { + writeln!( + self.out, + r#"{{ "type": "test", "event": "started", "name": "{}" }}"#, + escape8259::escape(name), + ) + .unwrap(); + } + } + } + + /// Prints the outcome of a single tests. `ok` or `FAILED` in pretty mode + /// and `.` or `F` in terse mode. + pub(crate) fn print_single_outcome(&mut self, info: &TestInfo, outcome: &Outcome) { + match self.format { + FormatSetting::Pretty => { + self.print_outcome_pretty(outcome); + writeln!(self.out).unwrap(); + } + FormatSetting::Terse => { + let c = match outcome { + Outcome::Passed => '.', + Outcome::Failed { .. } => 'F', + Outcome::Ignored => 'i', + Outcome::Measured { .. } => { + // Benchmark are never printed in terse mode... for + // some reason. + self.print_outcome_pretty(outcome); + writeln!(self.out).unwrap(); + return; + } + }; + + let style = color_of_outcome(outcome); + write!(self.out, "{style}{}{style:#}", c).unwrap(); + } + FormatSetting::Json => { + if let Outcome::Measured(Measurement { avg, variance }) = outcome { + writeln!( + self.out, + r#"{{ "type": "bench", "name": "{}", "median": {}, "deviation": {} }}"#, + escape8259::escape(&info.name), + avg, + variance, + ) + .unwrap(); + } else { + writeln!( + self.out, + r#"{{ "type": "test", "name": "{}", "event": "{}"{} }}"#, + escape8259::escape(&info.name), + match outcome { + Outcome::Passed => "ok", + Outcome::Failed(_) => "failed", + Outcome::Ignored => "ignored", + Outcome::Measured(_) => unreachable!(), + }, + match outcome { + Outcome::Failed(Failed { msg: Some(msg) }) => { + format!( + r#", "stdout": "Error: \"{}\"\n""#, + escape8259::escape(msg), + ) + } + _ => "".into(), + } + ) + .unwrap(); + } + } + } + } + + /// Prints the summary line after all tests have been executed. + pub(crate) fn print_summary(&mut self, conclusion: &Conclusion, execution_time: Duration) { + match self.format { + FormatSetting::Pretty | FormatSetting::Terse => { + let outcome = if conclusion.has_failed() { + Outcome::Failed(Failed { msg: None }) + } else { + Outcome::Passed + }; + + writeln!(self.out).unwrap(); + write!(self.out, "test result: ").unwrap(); + self.print_outcome_pretty(&outcome); + writeln!( + self.out, + ". {} passed; {} failed; {} ignored; {} measured; \ + {} filtered out; finished in {:.2}s", + conclusion.num_passed, + conclusion.num_failed, + conclusion.num_ignored, + conclusion.num_measured, + conclusion.num_filtered_out, + execution_time.as_secs_f64() + ).unwrap(); + writeln!(self.out).unwrap(); + } + FormatSetting::Json => { + writeln!( + self.out, + concat!( + r#"{{ "type": "suite", "event": "{}", "passed": {}, "failed": {},"#, + r#" "ignored": {}, "measured": {}, "filtered_out": {}, "exec_time": {} }}"#, + ), + if conclusion.num_failed > 0 { "failed" } else { "ok" }, + conclusion.num_passed, + conclusion.num_failed, + conclusion.num_ignored, + conclusion.num_measured, + conclusion.num_filtered_out, + execution_time.as_secs_f64() + ) + .unwrap(); + } + } + } + + /// Prints a list of all tests. Used if `--list` is set. + pub(crate) fn print_list(&mut self, tests: &[Trial], ignored: bool) { + Self::write_list(tests, ignored, &mut self.out).unwrap(); + } + + pub(crate) fn write_list( + tests: &[Trial], + ignored: bool, + mut out: impl std::io::Write, + ) -> std::io::Result<()> { + for test in tests { + // libtest prints out: + // * all tests without `--ignored` + // * just the ignored tests with `--ignored` + if ignored && !test.info.is_ignored { + continue; + } + + let kind = if test.info.kind.is_empty() { + String::new() + } else { + format!("[{}] ", test.info.kind) + }; + + writeln!( + out, + "{}{}: {}", + kind, + test.info.name, + if test.info.is_bench { "bench" } else { "test" }, + )?; + } + + Ok(()) + } + + /// Prints a list of failed tests with their messages. This is only called + /// if there were any failures. + pub(crate) fn print_failures(&mut self, fails: &[(TestInfo, Option)]) { + if self.format == FormatSetting::Json { + return; + } + writeln!(self.out).unwrap(); + writeln!(self.out, "failures:").unwrap(); + writeln!(self.out).unwrap(); + + // Print messages of all tests + for (test_info, msg) in fails { + writeln!(self.out, "---- {} ----", test_info.name).unwrap(); + if let Some(msg) = msg { + writeln!(self.out, "{}", msg).unwrap(); + } + writeln!(self.out).unwrap(); + } + + // Print summary list of failed tests + writeln!(self.out).unwrap(); + writeln!(self.out, "failures:").unwrap(); + for (test_info, _) in fails { + writeln!(self.out, " {}", test_info.name).unwrap(); + } + } + + /// Prints a colored 'ok'/'FAILED'/'ignored'/'bench'. + fn print_outcome_pretty(&mut self, outcome: &Outcome) { + let s = match outcome { + Outcome::Passed => "ok", + Outcome::Failed { .. } => "FAILED", + Outcome::Ignored => "ignored", + Outcome::Measured { .. } => "bench", + }; + + let style = color_of_outcome(outcome); + write!(self.out, "{style}{}{style:#}", s).unwrap(); + + if let Outcome::Measured(Measurement { avg, variance }) = outcome { + write!( + self.out, + ": {:>11} ns/iter (+/- {})", + fmt_with_thousand_sep(*avg), + fmt_with_thousand_sep(*variance), + ).unwrap(); + } + } +} + +/// Formats the given integer with `,` as thousand separator. +pub fn fmt_with_thousand_sep(mut v: u64) -> String { + let mut out = String::new(); + while v >= 1000 { + out = format!(",{:03}{}", v % 1000, out); + v /= 1000; + } + out = format!("{}{}", v, out); + + out +} + +/// Returns the `ColorSpec` associated with the given outcome. +fn color_of_outcome(outcome: &Outcome) -> Style { + let color = match outcome { + Outcome::Passed => AnsiColor::Green, + Outcome::Failed { .. } => AnsiColor::Red, + Outcome::Ignored => AnsiColor::Yellow, + Outcome::Measured { .. } => AnsiColor::Cyan, + }; + Style::new().fg_color(Some(Color::Ansi(color))) +} diff --git a/tools/vendor/libtest-mimic/tests/all_passing.rs b/tools/vendor/libtest-mimic/tests/all_passing.rs new file mode 100644 index 0000000000..b5c5552fa8 --- /dev/null +++ b/tools/vendor/libtest-mimic/tests/all_passing.rs @@ -0,0 +1,162 @@ +use common::{args, check}; +use libtest_mimic::{Trial, Conclusion}; +use pretty_assertions::assert_eq; + +use crate::common::do_run; + +#[macro_use] +mod common; + + +fn tests() -> Vec { + vec![ + Trial::test("foo", || Ok(())), + Trial::test("bar", || Ok(())), + Trial::test("barro", || Ok(())), + ] +} + +#[test] +fn normal() { + check(args([]), tests, 3, + Conclusion { + num_filtered_out: 0, + num_passed: 3, + num_failed: 0, + num_ignored: 0, + num_measured: 0, + }, + " + test foo ... ok + test bar ... ok + test barro ... ok + " + ); +} + +#[test] +fn filter_one() { + check(args(["foo"]), tests, 1, + Conclusion { + num_filtered_out: 2, + num_passed: 1, + num_failed: 0, + num_ignored: 0, + num_measured: 0, + }, + "test foo ... ok", + ); +} + +#[test] +fn filter_two() { + check(args(["bar"]), tests, 2, + Conclusion { + num_filtered_out: 1, + num_passed: 2, + num_failed: 0, + num_ignored: 0, + num_measured: 0, + }, + " + test bar ... ok + test barro ... ok + ", + ); +} + + +#[test] +fn filter_exact() { + check(args(["bar", "--exact"]), tests, 1, + Conclusion { + num_filtered_out: 2, + num_passed: 1, + num_failed: 0, + num_ignored: 0, + num_measured: 0, + }, + "test bar ... ok", + ); +} + +#[test] +fn filter_two_and_skip() { + check(args(["--skip", "barro", "bar"]), tests, 1, + Conclusion { + num_filtered_out: 2, + num_passed: 1, + num_failed: 0, + num_ignored: 0, + num_measured: 0, + }, + "test bar ... ok", + ); +} + +#[test] +fn skip_nothing() { + check(args(["--skip", "peter"]), tests, 3, + Conclusion { + num_filtered_out: 0, + num_passed: 3, + num_failed: 0, + num_ignored: 0, + num_measured: 0, + }, + " + test foo ... ok + test bar ... ok + test barro ... ok + " + ); +} + +#[test] +fn skip_two() { + check(args(["--skip", "bar"]), tests, 1, + Conclusion { + num_filtered_out: 2, + num_passed: 1, + num_failed: 0, + num_ignored: 0, + num_measured: 0, + }, + "test foo ... ok" + ); +} + +#[test] +fn skip_exact() { + check(args(["--exact", "--skip", "bar"]), tests, 2, + Conclusion { + num_filtered_out: 1, + num_passed: 2, + num_failed: 0, + num_ignored: 0, + num_measured: 0, + }, + " + test foo ... ok + test barro ... ok + " + ); +} + +#[test] +fn terse_output() { + let (c, out) = do_run(args(["--format", "terse"]), tests()); + assert_eq!(c, Conclusion { + num_filtered_out: 0, + num_passed: 3, + num_failed: 0, + num_ignored: 0, + num_measured: 0, + }); + assert_log!(out, " + running 3 tests + ... + test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; \ + finished in 0.00s + "); +} diff --git a/tools/vendor/libtest-mimic/tests/common/mod.rs b/tools/vendor/libtest-mimic/tests/common/mod.rs new file mode 100644 index 0000000000..c2c16a26e3 --- /dev/null +++ b/tools/vendor/libtest-mimic/tests/common/mod.rs @@ -0,0 +1,149 @@ +use std::{path::Path, iter::repeat_with, collections::HashMap}; +use pretty_assertions::assert_eq; + +use libtest_mimic::{run, Arguments, Conclusion, Trial}; + + +const TEMPDIR: &str = env!("CARGO_TARGET_TMPDIR"); + +pub fn args(args: [&str; N]) -> Arguments { + let mut v = vec![""]; + v.extend(args); + Arguments::from_iter(v) +} + +pub fn do_run(mut args: Arguments, tests: Vec) -> (Conclusion, String) { + // Create path to temporary file. + let suffix = repeat_with(fastrand::alphanumeric).take(10).collect::(); + let path = Path::new(&TEMPDIR).join(format!("libtest_mimic_output_{suffix}.txt")); + + args.logfile = Some(path.display().to_string()); + + let c = run(&args, tests); + let output = std::fs::read_to_string(&path) + .expect("Can't read temporary logfile"); + std::fs::remove_file(&path) + .expect("Can't remove temporary logfile"); + (c, output) +} + +/// Removes shared indentation so that at least one line has no indentation +/// (no leading spaces). +pub fn clean_expected_log(s: &str) -> String { + let shared_indent = s.lines() + .filter(|l| l.contains(|c| c != ' ')) + .map(|l| l.bytes().take_while(|b| *b == b' ').count()) + .min() + .expect("empty expected"); + + let mut out = String::new(); + for line in s.lines() { + use std::fmt::Write; + let cropped = if line.len() <= shared_indent { + line + } else { + &line[shared_indent..] + }; + writeln!(out, "{}", cropped).unwrap(); + } + + out +} + +/// Best effort tool to check certain things about a log that might have all +/// tests randomly ordered. +pub fn assert_reordered_log(actual: &str, num: u64, expected_lines: &[&str], tail: &str) { + let actual = actual.trim(); + let (first_line, rest) = actual.split_once('\n').expect("log has too few lines"); + let (middle, last_line) = rest.rsplit_once('\n').expect("log has too few lines"); + + + assert_eq!(first_line, &format!("running {} test{}", num, if num == 1 { "" } else { "s" })); + assert!(last_line.contains(tail)); + + let mut actual_lines = HashMap::new(); + for line in middle.lines().map(|l| l.trim()).filter(|l| !l.is_empty()) { + *actual_lines.entry(line).or_insert(0) += 1; + } + + for expected in expected_lines.iter().map(|l| l.trim()).filter(|l| !l.is_empty()) { + match actual_lines.get_mut(expected) { + None | Some(0) => panic!("expected line \"{expected}\" not in log"), + Some(num) => *num -= 1, + } + } + + actual_lines.retain(|_, v| *v != 0); + if !actual_lines.is_empty() { + panic!("Leftover output in log: {actual_lines:#?}"); + } +} + +/// Like `assert_eq`, but cleans the expected string (removes indendation). Also +/// normalizes the "finished in" time if `$expected` ends with "finished in +/// 0.00s". +#[macro_export] +macro_rules! assert_log { + ($actual:expr, $expected:expr) => { + let mut actual = $actual.trim().to_owned(); + let expected = crate::common::clean_expected_log($expected); + let expected = expected.trim(); + + if expected.ends_with("finished in 0.00s") { + // If we don't find that pattern, the assert below will fail anyway. + if let Some(pos) = actual.rfind("finished in") { + actual.truncate(pos); + actual.push_str("finished in 0.00s"); + } + } + + if let Some(pos) = actual.rfind("\"exec_time\":") { + actual.truncate(pos); + actual.push_str("\"exec_time\": 0.000000000 }"); + } + + assert_eq!(actual, expected); + }; +} + +pub fn check( + mut args: Arguments, + mut tests: impl FnMut() -> Vec, + num_running_tests: u64, + expected_conclusion: Conclusion, + expected_output: &str, +) { + // Run in single threaded mode + args.test_threads = Some(1); + let (c, out) = do_run(args.clone(), tests()); + let expected = crate::common::clean_expected_log(expected_output); + let actual = { + let lines = out.trim().lines().skip(1).collect::>(); + lines[..lines.len() - 1].join("\n") + }; + assert_eq!(actual.trim(), expected.trim()); + assert_eq!(c, expected_conclusion); + + // Run in multithreaded mode. + let (c, out) = do_run(args, tests()); + assert_reordered_log( + &out, + num_running_tests, + &expected_output.lines().collect::>(), + &conclusion_to_output(&c), + ); + assert_eq!(c, expected_conclusion); +} + +fn conclusion_to_output(c: &Conclusion) -> String { + let Conclusion { num_filtered_out, num_passed, num_failed, num_ignored, num_measured } = *c; + format!( + "test result: {}. {} passed; {} failed; {} ignored; {} measured; {} filtered out;", + if num_failed > 0 { "FAILED" } else { "ok" }, + num_passed, + num_failed, + num_ignored, + num_measured, + num_filtered_out, + ) +} diff --git a/tools/vendor/libtest-mimic/tests/json-output.json b/tools/vendor/libtest-mimic/tests/json-output.json new file mode 100644 index 0000000000..c1f118a129 --- /dev/null +++ b/tools/vendor/libtest-mimic/tests/json-output.json @@ -0,0 +1,36 @@ +{ "type": "suite", "event": "started", "test_count": 17 } +{ "type": "test", "event": "started", "name": "cat" } +{ "type": "test", "name": "cat", "event": "ok" } +{ "type": "test", "event": "started", "name": "\"ups\"" } +{ "type": "test", "name": "\"ups\"", "event": "failed", "stdout": "Error: \"failed to parse \"abc\"\"\n" } +{ "type": "test", "event": "started", "name": "dog" } +{ "type": "test", "name": "dog", "event": "failed", "stdout": "Error: \"was not a good boy\"\n" } +{ "type": "test", "event": "started", "name": "fox" } +{ "type": "test", "name": "fox", "event": "ok" } +{ "type": "test", "event": "started", "name": "bunny" } +{ "type": "test", "name": "bunny", "event": "failed", "stdout": "Error: \"jumped too high\"\n" } +{ "type": "test", "event": "started", "name": "frog" } +{ "type": "test", "name": "frog", "event": "ignored" } +{ "type": "test", "event": "started", "name": "owl" } +{ "type": "test", "name": "owl", "event": "ignored" } +{ "type": "test", "event": "started", "name": "fly" } +{ "type": "test", "name": "fly", "event": "ignored" } +{ "type": "test", "event": "started", "name": "bear" } +{ "type": "test", "name": "bear", "event": "ignored" } +{ "type": "test", "event": "started", "name": "red" } +{ "type": "test", "name": "red", "event": "ok" } +{ "type": "test", "event": "started", "name": "blue" } +{ "type": "test", "name": "blue", "event": "failed", "stdout": "Error: \"sky fell down\"\n" } +{ "type": "test", "event": "started", "name": "yellow" } +{ "type": "test", "name": "yellow", "event": "ok" } +{ "type": "test", "event": "started", "name": "green" } +{ "type": "test", "name": "green", "event": "failed", "stdout": "Error: \"was poisoned\"\n" } +{ "type": "test", "event": "started", "name": "purple" } +{ "type": "test", "name": "purple", "event": "ignored" } +{ "type": "test", "event": "started", "name": "cyan" } +{ "type": "test", "name": "cyan", "event": "ignored" } +{ "type": "test", "event": "started", "name": "orange" } +{ "type": "test", "name": "orange", "event": "ignored" } +{ "type": "test", "event": "started", "name": "pink" } +{ "type": "test", "name": "pink", "event": "ignored" } +{ "type": "suite", "event": "failed", "passed": 4, "failed": 5, "ignored": 8, "measured": 0, "filtered_out": 0, "exec_time": 0.000000000 } diff --git a/tools/vendor/libtest-mimic/tests/mixed_bag.rs b/tools/vendor/libtest-mimic/tests/mixed_bag.rs new file mode 100644 index 0000000000..1d38135d32 --- /dev/null +++ b/tools/vendor/libtest-mimic/tests/mixed_bag.rs @@ -0,0 +1,639 @@ +use crate::common::{args, check, do_run}; +use libtest_mimic::{Conclusion, Measurement, Trial}; +use pretty_assertions::assert_eq; + +#[macro_use] +mod common; + + +fn tests() -> Vec { + fn meas(avg: u64, variance: u64) -> Option { + Some(Measurement { avg, variance }) + } + + vec![ + Trial::test("cat", || Ok(())), + Trial::test("\"ups\"", || Err("failed to parse \"abc\"".into())), + Trial::test("dog", || Err("was not a good boy".into())), + Trial::test("fox", || Ok(())).with_kind("apple"), + Trial::test("bunny", || Err("jumped too high".into())).with_kind("apple"), + Trial::test("frog", || Ok(())).with_ignored_flag(true), + Trial::test("owl", || Err("broke neck".into())).with_ignored_flag(true), + Trial::test("fly", || Ok(())).with_ignored_flag(true).with_kind("banana"), + Trial::test("bear", || Err("no honey".into())).with_ignored_flag(true).with_kind("banana"), + + Trial::bench("red", |_| Ok(meas(32, 3))), + Trial::bench("blue", |_| Err("sky fell down".into())), + Trial::bench("yellow", |_| Ok(meas(64, 4))).with_kind("kiwi"), + Trial::bench("green", |_| Err("was poisoned".into())).with_kind("kiwi"), + Trial::bench("purple", |_| Ok(meas(100, 5))).with_ignored_flag(true), + Trial::bench("cyan", |_| Err("not creative enough".into())).with_ignored_flag(true), + Trial::bench("orange", |_| Ok(meas(17, 6))).with_ignored_flag(true).with_kind("banana"), + Trial::bench("pink", |_| Err("bad".into())).with_ignored_flag(true).with_kind("banana"), + ] +} + +#[test] +fn normal() { + check(args([]), tests, 17, + Conclusion { + num_filtered_out: 0, + num_passed: 4, + num_failed: 5, + num_ignored: 8, + num_measured: 0, + }, + " + test cat ... ok + test \"ups\" ... FAILED + test dog ... FAILED + test [apple] fox ... ok + test [apple] bunny ... FAILED + test frog ... ignored + test owl ... ignored + test [banana] fly ... ignored + test [banana] bear ... ignored + test red ... ok + test blue ... FAILED + test [kiwi] yellow ... ok + test [kiwi] green ... FAILED + test purple ... ignored + test cyan ... ignored + test [banana] orange ... ignored + test [banana] pink ... ignored + + failures: + + ---- \"ups\" ---- + failed to parse \"abc\" + + ---- dog ---- + was not a good boy + + ---- bunny ---- + jumped too high + + ---- blue ---- + sky fell down + + ---- green ---- + was poisoned + + + failures: + \"ups\" + dog + bunny + blue + green + ", + ); +} + +#[test] +fn test_mode() { + check(args(["--test"]), tests, 17, + Conclusion { + num_filtered_out: 0, + num_passed: 2, + num_failed: 3, + num_ignored: 12, + num_measured: 0, + }, + " + test cat ... ok + test \"ups\" ... FAILED + test dog ... FAILED + test [apple] fox ... ok + test [apple] bunny ... FAILED + test frog ... ignored + test owl ... ignored + test [banana] fly ... ignored + test [banana] bear ... ignored + test red ... ignored + test blue ... ignored + test [kiwi] yellow ... ignored + test [kiwi] green ... ignored + test purple ... ignored + test cyan ... ignored + test [banana] orange ... ignored + test [banana] pink ... ignored + + failures: + + ---- \"ups\" ---- + failed to parse \"abc\" + + ---- dog ---- + was not a good boy + + ---- bunny ---- + jumped too high + + + failures: + \"ups\" + dog + bunny + ", + ); +} + +#[test] +fn bench_mode() { + check(args(["--bench"]), tests, 17, + Conclusion { + num_filtered_out: 0, + num_passed: 0, + num_failed: 2, + num_ignored: 13, + num_measured: 2, + }, + " + test cat ... ignored + test \"ups\" ... ignored + test dog ... ignored + test [apple] fox ... ignored + test [apple] bunny ... ignored + test frog ... ignored + test owl ... ignored + test [banana] fly ... ignored + test [banana] bear ... ignored + test red ... bench: 32 ns/iter (+/- 3) + test blue ... FAILED + test [kiwi] yellow ... bench: 64 ns/iter (+/- 4) + test [kiwi] green ... FAILED + test purple ... ignored + test cyan ... ignored + test [banana] orange ... ignored + test [banana] pink ... ignored + + failures: + + ---- blue ---- + sky fell down + + ---- green ---- + was poisoned + + + failures: + blue + green + ", + ); +} + +#[test] +fn list() { + let (c, out) = common::do_run(args(["--list"]), tests()); + assert_log!(out, " + cat: test + \"ups\": test + dog: test + [apple] fox: test + [apple] bunny: test + frog: test + owl: test + [banana] fly: test + [banana] bear: test + red: bench + blue: bench + [kiwi] yellow: bench + [kiwi] green: bench + purple: bench + cyan: bench + [banana] orange: bench + [banana] pink: bench + "); + assert_eq!(c, Conclusion { + num_filtered_out: 0, + num_passed: 0, + num_failed: 0, + num_ignored: 0, + num_measured: 0, + }); +} + +#[test] +fn list_ignored() { + let (c, out) = common::do_run(args(["--list", "--ignored"]), tests()); + assert_log!(out, " + frog: test + owl: test + [banana] fly: test + [banana] bear: test + purple: bench + cyan: bench + [banana] orange: bench + [banana] pink: bench + "); + assert_eq!(c, Conclusion { + num_filtered_out: 0, + num_passed: 0, + num_failed: 0, + num_ignored: 0, + num_measured: 0, + }); +} + +#[test] +fn list_with_filter() { + let (c, out) = common::do_run(args(["--list", "a"]), tests()); + // Matches all tests that contain "a" in either the name or the kind. + assert_log!(out, " + cat: test + [apple] fox: test + [apple] bunny: test + [banana] fly: test + [banana] bear: test + cyan: bench + [banana] orange: bench + [banana] pink: bench + "); + assert_eq!(c, Conclusion { + num_filtered_out: 0, + num_passed: 0, + num_failed: 0, + num_ignored: 0, + num_measured: 0, + }); +} + +#[test] +fn list_with_filter_exact() { + // --exact matches the test name either with or without the kind. + let (c, out) = common::do_run(args(["--list", "--exact", "[apple] fox"]), tests()); + assert_log!(out, " + [apple] fox: test + "); + assert_eq!(c, Conclusion { + num_filtered_out: 0, + num_passed: 0, + num_failed: 0, + num_ignored: 0, + num_measured: 0, + }); + let (c, out) = common::do_run(args(["--list", "--exact", "fly"]), tests()); + assert_log!(out, " + [banana] fly: test + "); + assert_eq!(c, Conclusion { + num_filtered_out: 0, + num_passed: 0, + num_failed: 0, + num_ignored: 0, + num_measured: 0, + }); + + // --skip --exact can be used to exclude tests. + let (c, out) = common::do_run( + args([ + "--list", + "--exact", + "--skip", + "[apple] fox", + "--skip", + "fly", + ]), + tests(), + ); + assert_log!(out, " + cat: test + \"ups\": test + dog: test + [apple] bunny: test + frog: test + owl: test + [banana] bear: test + red: bench + blue: bench + [kiwi] yellow: bench + [kiwi] green: bench + purple: bench + cyan: bench + [banana] orange: bench + [banana] pink: bench + "); + assert_eq!(c, Conclusion { + num_filtered_out: 0, + num_passed: 0, + num_failed: 0, + num_ignored: 0, + num_measured: 0, + }); + + // --skip --exact matches test names without the kind as well. +} + +#[test] +fn filter_c() { + check(args(["c"]), tests, 2, + Conclusion { + num_filtered_out: 15, + num_passed: 1, + num_failed: 0, + num_ignored: 1, + num_measured: 0, + }, + " + test cat ... ok + test cyan ... ignored + ", + ); +} + +#[test] +fn filter_o_test() { + check(args(["--test", "o"]), tests, 6, + Conclusion { + num_filtered_out: 11, + num_passed: 1, + num_failed: 1, + num_ignored: 4, + num_measured: 0, + }, + " + test dog ... FAILED + test [apple] fox ... ok + test frog ... ignored + test owl ... ignored + test [kiwi] yellow ... ignored + test [banana] orange ... ignored + + failures: + + ---- dog ---- + was not a good boy + + + failures: + dog + ", + ); +} + +#[test] +fn filter_o_test_include_ignored() { + check(args(["--test", "--include-ignored", "o"]), tests, 6, + Conclusion { + num_filtered_out: 11, + num_passed: 2, + num_failed: 2, + num_ignored: 2, + num_measured: 0, + }, + " + test dog ... FAILED + test [apple] fox ... ok + test frog ... ok + test owl ... FAILED + test [kiwi] yellow ... ignored + test [banana] orange ... ignored + + failures: + + ---- dog ---- + was not a good boy + + ---- owl ---- + broke neck + + + failures: + dog + owl + ", + ); +} + +#[test] +fn filter_o_test_ignored() { + check(args(["--test", "--ignored", "o"]), tests, 3, + Conclusion { + num_filtered_out: 14, + num_passed: 1, + num_failed: 1, + num_ignored: 1, + num_measured: 0, + }, + " + test frog ... ok + test owl ... FAILED + test [banana] orange ... ignored + + failures: + + ---- owl ---- + broke neck + + + failures: + owl + ", + ); +} + +#[test] +fn normal_include_ignored() { + check(args(["--include-ignored"]), tests, 17, + Conclusion { + num_filtered_out: 0, + num_passed: 8, + num_failed: 9, + num_ignored: 0, + num_measured: 0, + }, + " + test cat ... ok + test \"ups\" ... FAILED + test dog ... FAILED + test [apple] fox ... ok + test [apple] bunny ... FAILED + test frog ... ok + test owl ... FAILED + test [banana] fly ... ok + test [banana] bear ... FAILED + test red ... ok + test blue ... FAILED + test [kiwi] yellow ... ok + test [kiwi] green ... FAILED + test purple ... ok + test cyan ... FAILED + test [banana] orange ... ok + test [banana] pink ... FAILED + + failures: + + ---- \"ups\" ---- + failed to parse \"abc\" + + ---- dog ---- + was not a good boy + + ---- bunny ---- + jumped too high + + ---- owl ---- + broke neck + + ---- bear ---- + no honey + + ---- blue ---- + sky fell down + + ---- green ---- + was poisoned + + ---- cyan ---- + not creative enough + + ---- pink ---- + bad + + + failures: + \"ups\" + dog + bunny + owl + bear + blue + green + cyan + pink + ", + ); +} + +#[test] +fn normal_ignored() { + check(args(["--ignored"]), tests, 8, + Conclusion { + num_filtered_out: 9, + num_passed: 4, + num_failed: 4, + num_ignored: 0, + num_measured: 0, + }, + " + test frog ... ok + test owl ... FAILED + test [banana] fly ... ok + test [banana] bear ... FAILED + test purple ... ok + test cyan ... FAILED + test [banana] orange ... ok + test [banana] pink ... FAILED + + failures: + + ---- owl ---- + broke neck + + ---- bear ---- + no honey + + ---- cyan ---- + not creative enough + + ---- pink ---- + bad + + + failures: + owl + bear + cyan + pink + ", + ); +} + +#[test] +fn lots_of_flags() { + check(args(["--include-ignored", "--skip", "g", "--test", "o"]), tests, 3, + Conclusion { + num_filtered_out: 14, + num_passed: 1, + num_failed: 1, + num_ignored: 1, + num_measured: 0, + }, + " + test [apple] fox ... ok + test owl ... FAILED + test [kiwi] yellow ... ignored + + failures: + + ---- owl ---- + broke neck + + + failures: + owl + ", + ); +} + +#[test] +fn terse_output() { + let (c, out) = do_run(args(["--format", "terse", "--test-threads", "1"]), tests()); + assert_eq!(c, Conclusion { + num_filtered_out: 0, + num_passed: 4, + num_failed: 5, + num_ignored: 8, + num_measured: 0, + }); + assert_log!(out, " + running 17 tests + .FF.Fiiii.F.Fiiii + failures: + + ---- \"ups\" ---- + failed to parse \"abc\" + + ---- dog ---- + was not a good boy + + ---- bunny ---- + jumped too high + + ---- blue ---- + sky fell down + + ---- green ---- + was poisoned + + + failures: + \"ups\" + dog + bunny + blue + green + + test result: FAILED. 4 passed; 5 failed; 8 ignored; 0 measured; 0 filtered out; \ + finished in 0.00s + "); +} + +#[test] +fn json_output() { + let (c, out) = do_run(args(["--format", "json", "--test-threads", "1"]), tests()); + assert_eq!( + c, + Conclusion { + num_filtered_out: 0, + num_passed: 4, + num_failed: 5, + num_ignored: 8, + num_measured: 0, + } + ); + + assert_log!(out, include_str!("json-output.json")); +} diff --git a/tools/vendor/libtest-mimic/tests/panic.rs b/tools/vendor/libtest-mimic/tests/panic.rs new file mode 100644 index 0000000000..503985ad91 --- /dev/null +++ b/tools/vendor/libtest-mimic/tests/panic.rs @@ -0,0 +1,39 @@ +use common::{args, check}; +use libtest_mimic::{Trial, Conclusion}; + +#[macro_use] +mod common; + + +fn tests() -> Vec { + vec![ + Trial::test("passes", || Ok(())), + Trial::test("panics", || panic!("uh oh")), + ] +} + +#[test] +fn normal() { + check(args([]), tests, 2, + Conclusion { + num_filtered_out: 0, + num_passed: 1, + num_failed: 1, + num_ignored: 0, + num_measured: 0, + }, + " + test passes ... ok + test panics ... FAILED + + failures: + + ---- panics ---- + test panicked: uh oh + + + failures: + panics + " + ); +} diff --git a/tools/vendor/libtest-mimic/tests/real/.gitignore b/tools/vendor/libtest-mimic/tests/real/.gitignore new file mode 100644 index 0000000000..d1211df8df --- /dev/null +++ b/tools/vendor/libtest-mimic/tests/real/.gitignore @@ -0,0 +1,3 @@ +# Compiled binaries +mixed_bad +ignored diff --git a/tools/vendor/libtest-mimic/tests/real/README.md b/tools/vendor/libtest-mimic/tests/real/README.md new file mode 100644 index 0000000000..7d480b0f26 --- /dev/null +++ b/tools/vendor/libtest-mimic/tests/real/README.md @@ -0,0 +1,4 @@ +# Test files to check the behavior of the real libtest + +These are just files that are not actively used but are useful to check the behavior of the real libtest. +Just `rustc --test ` and execute it. diff --git a/tools/vendor/libtest-mimic/tests/real/ignored.rs b/tools/vendor/libtest-mimic/tests/real/ignored.rs new file mode 100644 index 0000000000..fd801cbf36 --- /dev/null +++ b/tools/vendor/libtest-mimic/tests/real/ignored.rs @@ -0,0 +1,10 @@ +#[test] +fn not_ignored() {} + +#[test] +#[ignore] +fn normally_ignored() {} + +#[test] +#[ignore = "special message"] +fn ignored_with_message() {} diff --git a/tools/vendor/libtest-mimic/tests/real/mixed_bag.rs b/tools/vendor/libtest-mimic/tests/real/mixed_bag.rs new file mode 100644 index 0000000000..b19349e15a --- /dev/null +++ b/tools/vendor/libtest-mimic/tests/real/mixed_bag.rs @@ -0,0 +1,45 @@ +#![feature(test)] + +extern crate test; + + +#[test] +fn cat() {} + +#[test] +fn dog() { + panic!("was not a good boy"); +} + +#[test] +#[ignore] +fn frog() {} + +#[test] +#[ignore] +fn owl() { + panic!("broke neck"); +} + + +#[bench] +fn red(b: &mut test::Bencher) { + b.iter(|| std::thread::sleep(std::time::Duration::from_millis(50))); +} + +#[bench] +fn blue(_: &mut test::Bencher) { + panic!("sky fell down"); +} + +#[bench] +#[ignore] +fn purple(b: &mut test::Bencher) { + b.iter(|| {}); +} + +#[bench] +#[ignore] +fn cyan(_: &mut test::Bencher) { + panic!("not creative enough"); +} diff --git a/tools/vendor/libtest-mimic/tests/threads.rs b/tools/vendor/libtest-mimic/tests/threads.rs new file mode 100644 index 0000000000..c234cbe5e3 --- /dev/null +++ b/tools/vendor/libtest-mimic/tests/threads.rs @@ -0,0 +1,65 @@ +use std::time::Duration; +use libtest_mimic::{Trial, Arguments}; + + +#[test] +fn check_test_on_main_thread() { + let outer_thread = std::thread::current().id(); + + let mut args = Arguments::default(); + args.test_threads = Some(1); + let conclusion = libtest_mimic::run(&args, vec![Trial::test("check", move || { + assert_eq!(outer_thread, std::thread::current().id()); + Ok(()) + })]); + + assert_eq!(conclusion.num_passed, 1); +} + +#[test] +fn all_tests_run_on_single_thread() { + let mut args = Arguments::default(); + args.test_threads = Some(1); + let trials = vec![ + Trial::test("a", move || Ok(())), + Trial::test("b", move || Ok(())), + Trial::test("c", move || Ok(())), + ]; + let conclusion = libtest_mimic::run(&args, trials); + assert_eq!(conclusion.num_passed, 3); +} + +#[test] +fn all_tests_run_on_two_threads() { + let mut args = Arguments::default(); + args.test_threads = Some(2); + let trials = vec![ + Trial::test("a", move || Ok(())), + Trial::test("b", move || Ok(())), + Trial::test("c", move || Ok(())), + Trial::test("d", move || Ok(())), + Trial::test("e", move || Ok(())), + ]; + let conclusion = libtest_mimic::run(&args, trials); + assert_eq!(conclusion.num_passed, 5); +} + +// I know this is not a super clean test, but I think spurious failures should +// be veeeery limited. The value this test provides outweights the potential +// jankiness I think. +#[test] +fn multi_threads_are_used() { + let mut args = Arguments::default(); + args.test_threads = Some(4); + let trials = vec![ + Trial::test("a", move || { std::thread::sleep(Duration::from_secs(1)); Ok(()) }), + Trial::test("b", move || { std::thread::sleep(Duration::from_secs(1)); Ok(()) }), + Trial::test("c", move || { std::thread::sleep(Duration::from_secs(1)); Ok(()) }), + Trial::test("d", move || { std::thread::sleep(Duration::from_secs(1)); Ok(()) }), + ]; + let before = std::time::Instant::now(); + let _ = libtest_mimic::run(&args, trials); + if before.elapsed() >= Duration::from_secs(2) { + panic!("Seems like tests are not executed in parallel"); + } +} diff --git a/tools/vendor/normalize-line-endings/.cargo-checksum.json b/tools/vendor/normalize-line-endings/.cargo-checksum.json new file mode 100644 index 0000000000..d1669915fa --- /dev/null +++ b/tools/vendor/normalize-line-endings/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"32851b0f45a58ae06ad84880d21dec7542128bb2071632a8db3ff1014f77d328",".travis.yml":"d91d0d75087934c2d0503a8c04439fea459a19182021bb0b699644d442f5b6fc","Cargo.toml":"80c53a151710a0c13b62bb69189a984c4c7f354bd12d990fdce2a796c57ef63d","Cargo.toml.orig":"557906cb927cc0a21853a4200e566a29a72b64d39c661d63bea96465a3681897","LICENSE":"b40930bbcf80744c86c46a12bc9da056641d722716c378f5659b9e555ef833e1","README.md":"5e3f5e4f32f7267ff8d6bde917cb21b157930a71078048774d85ec7de9f2425e","src/lib.rs":"1ae29ba0393192db102a26bf8db05c15ed05adc7c4474ad1bde05ef2bc937225"},"package":"61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"} \ No newline at end of file diff --git a/tools/vendor/normalize-line-endings/.cargo_vcs_info.json b/tools/vendor/normalize-line-endings/.cargo_vcs_info.json new file mode 100644 index 0000000000..7f9000efd4 --- /dev/null +++ b/tools/vendor/normalize-line-endings/.cargo_vcs_info.json @@ -0,0 +1,5 @@ +{ + "git": { + "sha1": "4616e54a7dc1f9fac74f3bde41a951b576940e56" + } +} diff --git a/tools/vendor/normalize-line-endings/.travis.yml b/tools/vendor/normalize-line-endings/.travis.yml new file mode 100644 index 0000000000..22761ba7ee --- /dev/null +++ b/tools/vendor/normalize-line-endings/.travis.yml @@ -0,0 +1 @@ +language: rust diff --git a/tools/vendor/normalize-line-endings/Cargo.toml b/tools/vendor/normalize-line-endings/Cargo.toml new file mode 100644 index 0000000000..eedc7dc008 --- /dev/null +++ b/tools/vendor/normalize-line-endings/Cargo.toml @@ -0,0 +1,24 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g. crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +name = "normalize-line-endings" +version = "0.3.0" +authors = ["Richard Dodd "] +description = "Takes an iterator over chars and returns a new iterator with all line endings (\\r, \\n, or \\r\\n) as \\n" +documentation = "https://derekdreery.github.io/normalize-line-endings/normalize_line_endings/index.html" +readme = "./README.md" +keywords = ["chars", "line", "ending", "normalize"] +license = "Apache-2.0" +repository = "https://github.com/derekdreery/normalize-line-endings" + +[dependencies] diff --git a/tools/vendor/normalize-line-endings/Cargo.toml.orig b/tools/vendor/normalize-line-endings/Cargo.toml.orig new file mode 100644 index 0000000000..af2140109f --- /dev/null +++ b/tools/vendor/normalize-line-endings/Cargo.toml.orig @@ -0,0 +1,12 @@ +[package] +name = "normalize-line-endings" +description = "Takes an iterator over chars and returns a new iterator with all line endings (\\r, \\n, or \\r\\n) as \\n" +documentation = "https://derekdreery.github.io/normalize-line-endings/normalize_line_endings/index.html" +repository = "https://github.com/derekdreery/normalize-line-endings" +readme = "./README.md" +keywords = ["chars", "line", "ending", "normalize"] +license = "Apache-2.0" +version = "0.3.0" +authors = ["Richard Dodd "] + +[dependencies] diff --git a/tools/vendor/normalize-line-endings/LICENSE b/tools/vendor/normalize-line-endings/LICENSE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/tools/vendor/normalize-line-endings/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/tools/vendor/normalize-line-endings/README.md b/tools/vendor/normalize-line-endings/README.md new file mode 100644 index 0000000000..438543ee1f --- /dev/null +++ b/tools/vendor/normalize-line-endings/README.md @@ -0,0 +1,8 @@ +# Normalize-line-endings + +Consume an `Iterator` and return another with normalized line +endings. + +[documentation] + +[documentation]: https://docs.rs/normalize-line-endings diff --git a/tools/vendor/normalize-line-endings/src/lib.rs b/tools/vendor/normalize-line-endings/src/lib.rs new file mode 100644 index 0000000000..955234a7ec --- /dev/null +++ b/tools/vendor/normalize-line-endings/src/lib.rs @@ -0,0 +1,82 @@ +#![warn(missing_docs)] +//! +//! Normalize line endings +//! +//! This crate provides a `normalize` method that takes a char iterator and returns +//! a new one with `\n` for all line endings + +/// This struct wraps a `std::io::Chars` to normalize line endings. +/// +/// Implements `Iterator` so can be used in place +struct Normalized { + iter: I, + prev_was_cr: bool, +} + +/// Take a Chars and return similar struct with normalized line endings +/// +/// # Example +/// ``` +/// use std::iter::FromIterator; +/// use normalize_line_endings::normalized; +/// +/// let input = "This is a string \n with \r some \n\r\n random newlines\r\r\n\n"; +/// assert_eq!( +/// &String::from_iter(normalized(input.chars())), +/// "This is a string \n with \n some \n\n random newlines\n\n\n" +/// ); +/// ``` +#[inline] +pub fn normalized(iter: impl Iterator) -> impl Iterator { + Normalized { + iter, + prev_was_cr: false, + } +} + +impl Iterator for Normalized +where + I: Iterator, +{ + type Item = char; + fn next(&mut self) -> Option { + match self.iter.next() { + Some('\n') if self.prev_was_cr => { + self.prev_was_cr = false; + match self.iter.next() { + Some('\r') => { + self.prev_was_cr = true; + Some('\n') + } + any => { + self.prev_was_cr = false; + any + } + } + } + Some('\r') => { + self.prev_was_cr = true; + Some('\n') + } + any => { + self.prev_was_cr = false; + any + } + } + } +} + +// tests +#[cfg(test)] +mod tests { + use std::iter::FromIterator; + + #[test] + fn normalized() { + let input = "This is a string \n with \r some \n\r\n random newlines\r\r\n\n"; + assert_eq!( + &String::from_iter(super::normalized(input.chars())), + "This is a string \n with \n some \n\n random newlines\n\n\n" + ); + } +} diff --git a/tools/vendor/num-traits/.cargo-checksum.json b/tools/vendor/num-traits/.cargo-checksum.json new file mode 100644 index 0000000000..c78e2cc13a --- /dev/null +++ b/tools/vendor/num-traits/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"e34bdc2d65c741cc679d2e5c72c8ed8d952d2e20c5712a3c950af4235363ad7b","Cargo.toml":"dd701a0da6cce7e6e047cc944b2330f3c5e48cd64329e10942ea7d9299d435be","Cargo.toml.orig":"172863fc77ccbdd76c7d65660c1030e553cc3629931808f18f4cd4cc73a08c53","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"3fa9b7a124e7bc011758919f6d13fbdce58027852abc6c0851ddf6addfedabd5","RELEASES.md":"eb3298353c12e0cd48c79437199bd7f159c4250da06f8362afcf71004e98fde5","build.rs":"d3969209fc1c9d201c66ed11820d0b328600d75b3971f8ceebeab04900bc0587","src/bounds.rs":"a79325f6a92269ad7df3d11b9ff700d2d13fb1160e28f0c091a644efc4badc2b","src/cast.rs":"d2c9337163c3d594a701bfce21aec56571625fa0117f970f14fd82dcc4504bf6","src/float.rs":"2caf20ca3227422dcd8981accc79898e889bbda97fce1fc7f881c98e7091d8c3","src/identities.rs":"c26d7f2fd6636721930d11d345e0c7103b0a4ac242dc4035dbc956a45eba422f","src/int.rs":"ca214bab6624cb7f19bcb439958aa34596e0b13d8fc9b0090a7b37e89946d124","src/lib.rs":"00973c9c52fd607d6e4b1da4ce4d5caa7d1bc39c8cd6f5d64878075766c8ad33","src/macros.rs":"ee96613a2c73a3bef10ec7ae4d359dbf5f0b41f83e8a87c3d62ccc18dd27e498","src/ops/bytes.rs":"24ade942062566d686c6de5674b5668ea3cd688c7c88bd8e5df7ea7f96d70c6e","src/ops/checked.rs":"01e6379bf1d8eeca9dcf8bb5397e419e898e4043b57b0e2470e225bc27e81e6a","src/ops/euclid.rs":"89c09c2df7fcc4bdba32533d4e7254dceb424085239544ea2a9ae8a606bf7383","src/ops/inv.rs":"dd80b9bd48d815f17855a25842287942317fa49d1fdcdd655b61bd20ef927cda","src/ops/mod.rs":"2b3c396af44cd240205ba8b560625fa00c07cf387139d2c49eeb7869545d976d","src/ops/mul_add.rs":"15bd64d9420c86300c5ea7f57aa736af2ef968e4e5eaaae03f62fd277f124569","src/ops/overflowing.rs":"01f4cd27f8b0e257687170cc537188029e08e5d13e0c552b01153be5d66d5716","src/ops/saturating.rs":"165993c829c10c4f60e32c8cf34434b669ef54284d7f73dc7ec58a22ba65e6fc","src/ops/wrapping.rs":"39d7bc7e074ba7590cd29b40206baed9cb30ae70dca2b7ceb460c6ca7eaad2a8","src/pow.rs":"92c12990d2396b2dabd4ba80e80ad706c0c8fd0f1b967ab3bdd9cb738b150702","src/real.rs":"b45bc1ca54549595c8d4f91a0e192769c1d924b40eda3446af4780f7a07ac8ed","src/sign.rs":"7ca11eebee94b553a33a9e53b7663ba5173db297dee523d1a2600fbbc80ef850","tests/cast.rs":"6fcc0d6653253182e979e42542fe971829cd24ab2c3a21a668e935c23d39f7c0"},"package":"071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"} \ No newline at end of file diff --git a/tools/vendor/num-traits/.cargo_vcs_info.json b/tools/vendor/num-traits/.cargo_vcs_info.json new file mode 100644 index 0000000000..7ee50d6f3e --- /dev/null +++ b/tools/vendor/num-traits/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "7ec3d41d39b28190ec1d42db38021107b3951f3a" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/tools/vendor/num-traits/Cargo.toml b/tools/vendor/num-traits/Cargo.toml new file mode 100644 index 0000000000..97ceb1b6d8 --- /dev/null +++ b/tools/vendor/num-traits/Cargo.toml @@ -0,0 +1,54 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.60" +name = "num-traits" +version = "0.2.19" +authors = ["The Rust Project Developers"] +build = "build.rs" +exclude = [ + "/ci/*", + "/.github/*", +] +description = "Numeric traits for generic mathematics" +homepage = "https://github.com/rust-num/num-traits" +documentation = "https://docs.rs/num-traits" +readme = "README.md" +keywords = [ + "mathematics", + "numerics", +] +categories = [ + "algorithms", + "science", + "no-std", +] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-num/num-traits" + +[package.metadata.docs.rs] +features = ["std"] +rustdoc-args = ["--generate-link-to-definition"] + +[dependencies.libm] +version = "0.2.0" +optional = true + +[build-dependencies.autocfg] +version = "1" + +[features] +default = ["std"] +i128 = [] +libm = ["dep:libm"] +std = [] diff --git a/tools/vendor/num-traits/Cargo.toml.orig b/tools/vendor/num-traits/Cargo.toml.orig new file mode 100644 index 0000000000..226d4164ce --- /dev/null +++ b/tools/vendor/num-traits/Cargo.toml.orig @@ -0,0 +1,34 @@ +[package] +authors = ["The Rust Project Developers"] +description = "Numeric traits for generic mathematics" +documentation = "https://docs.rs/num-traits" +homepage = "https://github.com/rust-num/num-traits" +keywords = ["mathematics", "numerics"] +categories = ["algorithms", "science", "no-std"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-num/num-traits" +name = "num-traits" +version = "0.2.19" +readme = "README.md" +build = "build.rs" +exclude = ["/ci/*", "/.github/*"] +edition = "2021" +rust-version = "1.60" + +[package.metadata.docs.rs] +features = ["std"] +rustdoc-args = ["--generate-link-to-definition"] + +[dependencies] +libm = { version = "0.2.0", optional = true } + +[features] +default = ["std"] +libm = ["dep:libm"] +std = [] + +# vestigial features, now always in effect +i128 = [] + +[build-dependencies] +autocfg = "1" diff --git a/tools/vendor/num-traits/LICENSE-APACHE b/tools/vendor/num-traits/LICENSE-APACHE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/tools/vendor/num-traits/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/tools/vendor/num-traits/LICENSE-MIT b/tools/vendor/num-traits/LICENSE-MIT new file mode 100644 index 0000000000..39d4bdb5ac --- /dev/null +++ b/tools/vendor/num-traits/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 The Rust Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/tools/vendor/num-traits/README.md b/tools/vendor/num-traits/README.md new file mode 100644 index 0000000000..fd420705ff --- /dev/null +++ b/tools/vendor/num-traits/README.md @@ -0,0 +1,58 @@ +# num-traits + +[![crate](https://img.shields.io/crates/v/num-traits.svg)](https://crates.io/crates/num-traits) +[![documentation](https://docs.rs/num-traits/badge.svg)](https://docs.rs/num-traits) +[![minimum rustc 1.60](https://img.shields.io/badge/rustc-1.60+-red.svg)](https://rust-lang.github.io/rfcs/2495-min-rust-version.html) +[![build status](https://github.com/rust-num/num-traits/workflows/master/badge.svg)](https://github.com/rust-num/num-traits/actions) + +Numeric traits for generic mathematics in Rust. + +## Usage + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +num-traits = "0.2" +``` + +## Features + +This crate can be used without the standard library (`#![no_std]`) by disabling +the default `std` feature. Use this in `Cargo.toml`: + +```toml +[dependencies.num-traits] +version = "0.2" +default-features = false +# features = ["libm"] # <--- Uncomment if you wish to use `Float` and `Real` without `std` +``` + +The `Float` and `Real` traits are only available when either `std` or `libm` is enabled. + +The `FloatCore` trait is always available. `MulAdd` and `MulAddAssign` for `f32` +and `f64` also require `std` or `libm`, as do implementations of signed and floating- +point exponents in `Pow`. + +## Releases + +Release notes are available in [RELEASES.md](RELEASES.md). + +## Compatibility + +The `num-traits` crate is tested for rustc 1.60 and greater. + +## License + +Licensed under either of + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/tools/vendor/num-traits/RELEASES.md b/tools/vendor/num-traits/RELEASES.md new file mode 100644 index 0000000000..5f23f56ad5 --- /dev/null +++ b/tools/vendor/num-traits/RELEASES.md @@ -0,0 +1,307 @@ +# Release 0.2.19 (2024-05-03) + +- [Upgrade to 2021 edition, **MSRV 1.60**][310] +- [The new `Float::clamp` limits values by minimum and maximum][305] + +**Contributors**: @cuviper, @michaelciraci + +[305]: https://github.com/rust-num/num-traits/pull/305 +[310]: https://github.com/rust-num/num-traits/pull/310 + +# Release 0.2.18 (2024-02-07) + +- [The new `Euclid::div_rem_euclid` and `CheckedEuclid::checked_div_rem_euclid` methods][291] + compute and return the quotient and remainder at the same time. +- [The new `TotalOrder` trait implements the IEEE 754 `totalOrder` predicate.][295] +- [The new `ConstZero` and `ConstOne` traits offered associated constants][303], + extending the non-const `Zero` and `One` traits for types that have constant values. + +**Contributors**: @andrewjradcliffe, @cuviper, @tarcieri, @tdelabro, @waywardmonkeys + +[291]: https://github.com/rust-num/num-traits/pull/291 +[295]: https://github.com/rust-num/num-traits/pull/295 +[303]: https://github.com/rust-num/num-traits/pull/303 + +# Release 0.2.17 (2023-10-07) + +- [Fix a doc warning about custom classes with newer rustdoc.][286] + +**Contributors**: @robamu + +[286]: https://github.com/rust-num/num-traits/pull/286 + +# Release 0.2.16 (2023-07-20) + +- [Upgrade to 2018 edition, **MSRV 1.31**][240] +- [The new `ToBytes` and `FromBytes` traits][224] convert to and from byte + representations of a value, with little, big, and native-endian options. +- [The new `Float::is_subnormal` method checks for subnormal values][279], with + a non-zero magnitude that is less than the normal minimum positive value. +- Several other improvements to documentation and testing. + +**Contributors**: @ctrlcctrlv, @cuviper, @flier, @GuillaumeGomez, @kaidokert, +@rs017991, @vicsn + +[224]: https://github.com/rust-num/num-traits/pull/224 +[240]: https://github.com/rust-num/num-traits/pull/240 +[279]: https://github.com/rust-num/num-traits/pull/279 + +# Release 0.2.15 (2022-05-02) + +- [The new `Euclid` trait calculates Euclidean division][195], where the + remainder is always positive or zero. +- [The new `LowerBounded` and `UpperBounded` traits][210] separately describe + types with lower and upper bounds. These traits are automatically implemented + for all fully-`Bounded` types. +- [The new `Float::copysign` method copies the sign of the argument][207] to + to the magnitude of `self`. +- [The new `PrimInt::leading_ones` and `trailing_ones` methods][205] are the + complement of the existing methods that count zero bits. +- [The new `PrimInt::reverse_bits` method reverses the order of all bits][202] + of a primitive integer. +- [Improved `Num::from_str_radix` for floats][201], also [ignoring case][214]. +- [`Float` and `FloatCore` use more from `libm`][196] when that is enabled. + +**Contributors**: @alion02, @clarfonthey, @cuviper, @ElectronicRU, +@ibraheemdev, @SparrowLii, @sshilovsky, @tspiteri, @XAMPPRocky, @Xiretza + +[195]: https://github.com/rust-num/num-traits/pull/195 +[196]: https://github.com/rust-num/num-traits/pull/196 +[201]: https://github.com/rust-num/num-traits/pull/201 +[202]: https://github.com/rust-num/num-traits/pull/202 +[205]: https://github.com/rust-num/num-traits/pull/205 +[207]: https://github.com/rust-num/num-traits/pull/207 +[210]: https://github.com/rust-num/num-traits/pull/210 +[214]: https://github.com/rust-num/num-traits/pull/214 + +# Release 0.2.14 (2020-10-29) + +- Clarify the license specification as "MIT OR Apache-2.0". + +**Contributors**: @cuviper + +# Release 0.2.13 (2020-10-29) + +- [The new `OverflowingAdd`, `OverflowingSub`, and `OverflowingMul` traits][180] + return a tuple with the operation result and a `bool` indicating overflow. +- [The "i128" feature now overrides compiler probes for that support][185]. + This may fix scenarios where `autocfg` probing doesn't work properly. +- [Casts from large `f64` values to `f32` now saturate to infinity][186]. They + previously returned `None` because that was once thought to be undefined + behavior, but [rust#15536] resolved that such casts are fine. +- [`Num::from_str_radix` documents requirements for radix support][192], which + are now more relaxed than previously implied. It is suggested to accept at + least `2..=36` without panicking, but `Err` may be returned otherwise. + +**Contributors**: @cuviper, @Enet4, @KaczuH, @martin-t, @newpavlov + +[180]: https://github.com/rust-num/num-traits/pull/180 +[185]: https://github.com/rust-num/num-traits/pull/185 +[186]: https://github.com/rust-num/num-traits/pull/186 +[192]: https://github.com/rust-num/num-traits/issues/192 +[rust#15536]: https://github.com/rust-lang/rust/issues/15536 + +# Release 0.2.12 (2020-06-11) + +- [The new `WrappingNeg` trait][153] will wrap the result if it exceeds the + boundary of the type, e.g. `i32::MIN.wrapping_neg() == i32::MIN`. +- [The new `SaturatingAdd`, `SaturatingSub`, and `SaturatingMul` traits][165] + will saturate at the numeric bounds if the operation would overflow. These + soft-deprecate the existing `Saturating` trait that only has addition and + subtraction methods. +- [Added new constants for logarithms, `FloatConst::{LOG10_2, LOG2_10}`][171]. + +**Contributors**: @cuviper, @ocstl, @trepetti, @vallentin + +[153]: https://github.com/rust-num/num-traits/pull/153 +[165]: https://github.com/rust-num/num-traits/pull/165 +[171]: https://github.com/rust-num/num-traits/pull/171 + +# Release 0.2.11 (2020-01-09) + +- [Added the full circle constant τ as `FloatConst::TAU`][145]. +- [Updated the `autocfg` build dependency to 1.0][148]. + +**Contributors**: @cuviper, @m-ou-se + +[145]: https://github.com/rust-num/num-traits/pull/145 +[148]: https://github.com/rust-num/num-traits/pull/148 + +# Release 0.2.10 (2019-11-22) + +- [Updated the `libm` dependency to 0.2][144]. + +**Contributors**: @CryZe + +[144]: https://github.com/rust-num/num-traits/pull/144 + +# Release 0.2.9 (2019-11-12) + +- [A new optional `libm` dependency][99] enables the `Float` and `Real` traits + in `no_std` builds. +- [The new `clamp_min` and `clamp_max`][122] limit minimum and maximum values + while preserving input `NAN`s. +- [Fixed a panic in floating point `from_str_radix` on invalid signs][126]. +- Miscellaneous documentation updates. + +**Contributors**: @cuviper, @dingelish, @HeroicKatora, @jturner314, @ocstl, +@Shnatsel, @termoshtt, @waywardmonkeys, @yoanlcq + +[99]: https://github.com/rust-num/num-traits/pull/99 +[122]: https://github.com/rust-num/num-traits/pull/122 +[126]: https://github.com/rust-num/num-traits/pull/126 + +# Release 0.2.8 (2019-05-21) + +- [Fixed feature detection on `no_std` targets][116]. + +**Contributors**: @cuviper + +[116]: https://github.com/rust-num/num-traits/pull/116 + +# Release 0.2.7 (2019-05-20) + +- [Documented when `CheckedShl` and `CheckedShr` return `None`][90]. +- [The new `Zero::set_zero` and `One::set_one`][104] will set values to their + identities in place, possibly optimized better than direct assignment. +- [Documented general features and intentions of `PrimInt`][108]. + +**Contributors**: @cuviper, @dvdhrm, @ignatenkobrain, @lcnr, @samueltardieu + +[90]: https://github.com/rust-num/num-traits/pull/90 +[104]: https://github.com/rust-num/num-traits/pull/104 +[108]: https://github.com/rust-num/num-traits/pull/108 + +# Release 0.2.6 (2018-09-13) + +- [Documented that `pow(0, 0)` returns `1`][79]. Mathematically, this is not + strictly defined, but the current behavior is a pragmatic choice that has + precedent in Rust `core` for the primitives and in many other languages. +- [The new `WrappingShl` and `WrappingShr` traits][81] will wrap the shift count + if it exceeds the bit size of the type. + +**Contributors**: @cuviper, @edmccard, @meltinglava + +[79]: https://github.com/rust-num/num-traits/pull/79 +[81]: https://github.com/rust-num/num-traits/pull/81 + +# Release 0.2.5 (2018-06-20) + +- [Documentation for `mul_add` now clarifies that it's not always faster.][70] +- [The default methods in `FromPrimitive` and `ToPrimitive` are more robust.][73] + +**Contributors**: @cuviper, @frewsxcv + +[70]: https://github.com/rust-num/num-traits/pull/70 +[73]: https://github.com/rust-num/num-traits/pull/73 + +# Release 0.2.4 (2018-05-11) + +- [Support for 128-bit integers is now automatically detected and enabled.][69] + Setting the `i128` crate feature now causes the build script to panic if such + support is not detected. + +**Contributors**: @cuviper + +[69]: https://github.com/rust-num/num-traits/pull/69 + +# Release 0.2.3 (2018-05-10) + +- [The new `CheckedNeg` and `CheckedRem` traits][63] perform checked `Neg` and + `Rem`, returning `Some(output)` or `None` on overflow. +- [The `no_std` implementation of `FloatCore::to_degrees` for `f32`][61] now + uses a constant for greater accuracy, mirroring [rust#47919]. (With `std` it + just calls the inherent `f32::to_degrees` in the standard library.) +- [The new `MulAdd` and `MulAddAssign` traits][59] perform a fused multiply- + add. For integer types this is just a convenience, but for floating point + types this produces a more accurate result than the separate operations. +- [All applicable traits are now implemented for 128-bit integers][60] starting + with Rust 1.26, enabled by the new `i128` crate feature. The `FromPrimitive` + and `ToPrimitive` traits now also have corresponding 128-bit methods, which + default to converting via 64-bit integers for compatibility. + +**Contributors**: @cuviper, @LEXUGE, @regexident, @vks + +[59]: https://github.com/rust-num/num-traits/pull/59 +[60]: https://github.com/rust-num/num-traits/pull/60 +[61]: https://github.com/rust-num/num-traits/pull/61 +[63]: https://github.com/rust-num/num-traits/pull/63 +[rust#47919]: https://github.com/rust-lang/rust/pull/47919 + +# Release 0.2.2 (2018-03-18) + +- [Casting from floating point to integers now returns `None` on overflow][52], + avoiding [rustc's undefined behavior][rust-10184]. This applies to the `cast` + function and the traits `NumCast`, `FromPrimitive`, and `ToPrimitive`. + +**Contributors**: @apopiak, @cuviper, @dbarella + +[52]: https://github.com/rust-num/num-traits/pull/52 +[rust-10184]: https://github.com/rust-lang/rust/issues/10184 + + +# Release 0.2.1 (2018-03-01) + +- [The new `FloatCore` trait][32] offers a subset of `Float` for `#![no_std]` use. + [This includes everything][41] except the transcendental functions and FMA. +- [The new `Inv` trait][37] returns the multiplicative inverse, or reciprocal. +- [The new `Pow` trait][37] performs exponentiation, much like the existing `pow` + function, but with generic exponent types. +- [The new `One::is_one` method][39] tests if a value equals 1. Implementers + should override this method if there's a more efficient way to check for 1, + rather than comparing with a temporary `one()`. + +**Contributors**: @clarcharr, @cuviper, @vks + +[32]: https://github.com/rust-num/num-traits/pull/32 +[37]: https://github.com/rust-num/num-traits/pull/37 +[39]: https://github.com/rust-num/num-traits/pull/39 +[41]: https://github.com/rust-num/num-traits/pull/41 + + +# Release 0.2.0 (2018-02-06) + +- **breaking change**: [There is now a `std` feature][30], enabled by default, along + with the implication that building *without* this feature makes this a + `#![no_std]` crate. + - The `Float` and `Real` traits are only available when `std` is enabled. + - Otherwise, the API is unchanged, and num-traits 0.1.43 now re-exports its + items from num-traits 0.2 for compatibility (the [semver-trick]). + +**Contributors**: @cuviper, @termoshtt, @vks + +[semver-trick]: https://github.com/dtolnay/semver-trick +[30]: https://github.com/rust-num/num-traits/pull/30 + + +# Release 0.1.43 (2018-02-06) + +- All items are now [re-exported from num-traits 0.2][31] for compatibility. + +[31]: https://github.com/rust-num/num-traits/pull/31 + + +# Release 0.1.42 (2018-01-22) + +- [num-traits now has its own source repository][num-356] at [rust-num/num-traits][home]. +- [`ParseFloatError` now implements `Display`][22]. +- [The new `AsPrimitive` trait][17] implements generic casting with the `as` operator. +- [The new `CheckedShl` and `CheckedShr` traits][21] implement generic + support for the `checked_shl` and `checked_shr` methods on primitive integers. +- [The new `Real` trait][23] offers a subset of `Float` functionality that may be applicable to more + types, with a blanket implementation for all existing `T: Float` types. + +Thanks to @cuviper, @Enet4, @fabianschuiki, @svartalf, and @yoanlcq for their contributions! + +[home]: https://github.com/rust-num/num-traits +[num-356]: https://github.com/rust-num/num/pull/356 +[17]: https://github.com/rust-num/num-traits/pull/17 +[21]: https://github.com/rust-num/num-traits/pull/21 +[22]: https://github.com/rust-num/num-traits/pull/22 +[23]: https://github.com/rust-num/num-traits/pull/23 + + +# Prior releases + +No prior release notes were kept. Thanks all the same to the many +contributors that have made this crate what it is! diff --git a/tools/vendor/num-traits/build.rs b/tools/vendor/num-traits/build.rs new file mode 100644 index 0000000000..98b06befdb --- /dev/null +++ b/tools/vendor/num-traits/build.rs @@ -0,0 +1,7 @@ +fn main() { + let ac = autocfg::new(); + + ac.emit_expression_cfg("1f64.total_cmp(&2f64)", "has_total_cmp"); // 1.62 + + autocfg::rerun_path("build.rs"); +} diff --git a/tools/vendor/num-traits/src/bounds.rs b/tools/vendor/num-traits/src/bounds.rs new file mode 100644 index 0000000000..acc990ea50 --- /dev/null +++ b/tools/vendor/num-traits/src/bounds.rs @@ -0,0 +1,148 @@ +use core::num::Wrapping; +use core::{f32, f64}; +use core::{i128, i16, i32, i64, i8, isize}; +use core::{u128, u16, u32, u64, u8, usize}; + +/// Numbers which have upper and lower bounds +pub trait Bounded { + // FIXME (#5527): These should be associated constants + /// Returns the smallest finite number this type can represent + fn min_value() -> Self; + /// Returns the largest finite number this type can represent + fn max_value() -> Self; +} + +/// Numbers which have lower bounds +pub trait LowerBounded { + /// Returns the smallest finite number this type can represent + fn min_value() -> Self; +} + +// FIXME: With a major version bump, this should be a supertrait instead +impl LowerBounded for T { + fn min_value() -> T { + Bounded::min_value() + } +} + +/// Numbers which have upper bounds +pub trait UpperBounded { + /// Returns the largest finite number this type can represent + fn max_value() -> Self; +} + +// FIXME: With a major version bump, this should be a supertrait instead +impl UpperBounded for T { + fn max_value() -> T { + Bounded::max_value() + } +} + +macro_rules! bounded_impl { + ($t:ty, $min:expr, $max:expr) => { + impl Bounded for $t { + #[inline] + fn min_value() -> $t { + $min + } + + #[inline] + fn max_value() -> $t { + $max + } + } + }; +} + +bounded_impl!(usize, usize::MIN, usize::MAX); +bounded_impl!(u8, u8::MIN, u8::MAX); +bounded_impl!(u16, u16::MIN, u16::MAX); +bounded_impl!(u32, u32::MIN, u32::MAX); +bounded_impl!(u64, u64::MIN, u64::MAX); +bounded_impl!(u128, u128::MIN, u128::MAX); + +bounded_impl!(isize, isize::MIN, isize::MAX); +bounded_impl!(i8, i8::MIN, i8::MAX); +bounded_impl!(i16, i16::MIN, i16::MAX); +bounded_impl!(i32, i32::MIN, i32::MAX); +bounded_impl!(i64, i64::MIN, i64::MAX); +bounded_impl!(i128, i128::MIN, i128::MAX); + +impl Bounded for Wrapping { + fn min_value() -> Self { + Wrapping(T::min_value()) + } + fn max_value() -> Self { + Wrapping(T::max_value()) + } +} + +bounded_impl!(f32, f32::MIN, f32::MAX); + +macro_rules! for_each_tuple_ { + ( $m:ident !! ) => ( + $m! { } + ); + ( $m:ident !! $h:ident, $($t:ident,)* ) => ( + $m! { $h $($t)* } + for_each_tuple_! { $m !! $($t,)* } + ); +} +macro_rules! for_each_tuple { + ($m:ident) => { + for_each_tuple_! { $m !! A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, } + }; +} + +macro_rules! bounded_tuple { + ( $($name:ident)* ) => ( + impl<$($name: Bounded,)*> Bounded for ($($name,)*) { + #[inline] + fn min_value() -> Self { + ($($name::min_value(),)*) + } + #[inline] + fn max_value() -> Self { + ($($name::max_value(),)*) + } + } + ); +} + +for_each_tuple!(bounded_tuple); +bounded_impl!(f64, f64::MIN, f64::MAX); + +#[test] +fn wrapping_bounded() { + macro_rules! test_wrapping_bounded { + ($($t:ty)+) => { + $( + assert_eq!( as Bounded>::min_value().0, <$t>::min_value()); + assert_eq!( as Bounded>::max_value().0, <$t>::max_value()); + )+ + }; + } + + test_wrapping_bounded!(usize u8 u16 u32 u64 isize i8 i16 i32 i64); +} + +#[test] +fn wrapping_bounded_i128() { + macro_rules! test_wrapping_bounded { + ($($t:ty)+) => { + $( + assert_eq!( as Bounded>::min_value().0, <$t>::min_value()); + assert_eq!( as Bounded>::max_value().0, <$t>::max_value()); + )+ + }; + } + + test_wrapping_bounded!(u128 i128); +} + +#[test] +fn wrapping_is_bounded() { + fn require_bounded(_: &T) {} + require_bounded(&Wrapping(42_u32)); + require_bounded(&Wrapping(-42)); +} diff --git a/tools/vendor/num-traits/src/cast.rs b/tools/vendor/num-traits/src/cast.rs new file mode 100644 index 0000000000..a3e4dd8b7f --- /dev/null +++ b/tools/vendor/num-traits/src/cast.rs @@ -0,0 +1,770 @@ +use core::mem::size_of; +use core::num::Wrapping; +use core::{f32, f64}; +use core::{i128, i16, i32, i64, i8, isize}; +use core::{u128, u16, u32, u64, u8, usize}; + +/// A generic trait for converting a value to a number. +/// +/// A value can be represented by the target type when it lies within +/// the range of scalars supported by the target type. +/// For example, a negative integer cannot be represented by an unsigned +/// integer type, and an `i64` with a very high magnitude might not be +/// convertible to an `i32`. +/// On the other hand, conversions with possible precision loss or truncation +/// are admitted, like an `f32` with a decimal part to an integer type, or +/// even a large `f64` saturating to `f32` infinity. +pub trait ToPrimitive { + /// Converts the value of `self` to an `isize`. If the value cannot be + /// represented by an `isize`, then `None` is returned. + #[inline] + fn to_isize(&self) -> Option { + self.to_i64().as_ref().and_then(ToPrimitive::to_isize) + } + + /// Converts the value of `self` to an `i8`. If the value cannot be + /// represented by an `i8`, then `None` is returned. + #[inline] + fn to_i8(&self) -> Option { + self.to_i64().as_ref().and_then(ToPrimitive::to_i8) + } + + /// Converts the value of `self` to an `i16`. If the value cannot be + /// represented by an `i16`, then `None` is returned. + #[inline] + fn to_i16(&self) -> Option { + self.to_i64().as_ref().and_then(ToPrimitive::to_i16) + } + + /// Converts the value of `self` to an `i32`. If the value cannot be + /// represented by an `i32`, then `None` is returned. + #[inline] + fn to_i32(&self) -> Option { + self.to_i64().as_ref().and_then(ToPrimitive::to_i32) + } + + /// Converts the value of `self` to an `i64`. If the value cannot be + /// represented by an `i64`, then `None` is returned. + fn to_i64(&self) -> Option; + + /// Converts the value of `self` to an `i128`. If the value cannot be + /// represented by an `i128` (`i64` under the default implementation), then + /// `None` is returned. + /// + /// The default implementation converts through `to_i64()`. Types implementing + /// this trait should override this method if they can represent a greater range. + #[inline] + fn to_i128(&self) -> Option { + self.to_i64().map(From::from) + } + + /// Converts the value of `self` to a `usize`. If the value cannot be + /// represented by a `usize`, then `None` is returned. + #[inline] + fn to_usize(&self) -> Option { + self.to_u64().as_ref().and_then(ToPrimitive::to_usize) + } + + /// Converts the value of `self` to a `u8`. If the value cannot be + /// represented by a `u8`, then `None` is returned. + #[inline] + fn to_u8(&self) -> Option { + self.to_u64().as_ref().and_then(ToPrimitive::to_u8) + } + + /// Converts the value of `self` to a `u16`. If the value cannot be + /// represented by a `u16`, then `None` is returned. + #[inline] + fn to_u16(&self) -> Option { + self.to_u64().as_ref().and_then(ToPrimitive::to_u16) + } + + /// Converts the value of `self` to a `u32`. If the value cannot be + /// represented by a `u32`, then `None` is returned. + #[inline] + fn to_u32(&self) -> Option { + self.to_u64().as_ref().and_then(ToPrimitive::to_u32) + } + + /// Converts the value of `self` to a `u64`. If the value cannot be + /// represented by a `u64`, then `None` is returned. + fn to_u64(&self) -> Option; + + /// Converts the value of `self` to a `u128`. If the value cannot be + /// represented by a `u128` (`u64` under the default implementation), then + /// `None` is returned. + /// + /// The default implementation converts through `to_u64()`. Types implementing + /// this trait should override this method if they can represent a greater range. + #[inline] + fn to_u128(&self) -> Option { + self.to_u64().map(From::from) + } + + /// Converts the value of `self` to an `f32`. Overflows may map to positive + /// or negative inifinity, otherwise `None` is returned if the value cannot + /// be represented by an `f32`. + #[inline] + fn to_f32(&self) -> Option { + self.to_f64().as_ref().and_then(ToPrimitive::to_f32) + } + + /// Converts the value of `self` to an `f64`. Overflows may map to positive + /// or negative inifinity, otherwise `None` is returned if the value cannot + /// be represented by an `f64`. + /// + /// The default implementation tries to convert through `to_i64()`, and + /// failing that through `to_u64()`. Types implementing this trait should + /// override this method if they can represent a greater range. + #[inline] + fn to_f64(&self) -> Option { + match self.to_i64() { + Some(i) => i.to_f64(), + None => self.to_u64().as_ref().and_then(ToPrimitive::to_f64), + } + } +} + +macro_rules! impl_to_primitive_int_to_int { + ($SrcT:ident : $( $(#[$cfg:meta])* fn $method:ident -> $DstT:ident ; )*) => {$( + #[inline] + $(#[$cfg])* + fn $method(&self) -> Option<$DstT> { + let min = $DstT::MIN as $SrcT; + let max = $DstT::MAX as $SrcT; + if size_of::<$SrcT>() <= size_of::<$DstT>() || (min <= *self && *self <= max) { + Some(*self as $DstT) + } else { + None + } + } + )*} +} + +macro_rules! impl_to_primitive_int_to_uint { + ($SrcT:ident : $( $(#[$cfg:meta])* fn $method:ident -> $DstT:ident ; )*) => {$( + #[inline] + $(#[$cfg])* + fn $method(&self) -> Option<$DstT> { + let max = $DstT::MAX as $SrcT; + if 0 <= *self && (size_of::<$SrcT>() <= size_of::<$DstT>() || *self <= max) { + Some(*self as $DstT) + } else { + None + } + } + )*} +} + +macro_rules! impl_to_primitive_int { + ($T:ident) => { + impl ToPrimitive for $T { + impl_to_primitive_int_to_int! { $T: + fn to_isize -> isize; + fn to_i8 -> i8; + fn to_i16 -> i16; + fn to_i32 -> i32; + fn to_i64 -> i64; + fn to_i128 -> i128; + } + + impl_to_primitive_int_to_uint! { $T: + fn to_usize -> usize; + fn to_u8 -> u8; + fn to_u16 -> u16; + fn to_u32 -> u32; + fn to_u64 -> u64; + fn to_u128 -> u128; + } + + #[inline] + fn to_f32(&self) -> Option { + Some(*self as f32) + } + #[inline] + fn to_f64(&self) -> Option { + Some(*self as f64) + } + } + }; +} + +impl_to_primitive_int!(isize); +impl_to_primitive_int!(i8); +impl_to_primitive_int!(i16); +impl_to_primitive_int!(i32); +impl_to_primitive_int!(i64); +impl_to_primitive_int!(i128); + +macro_rules! impl_to_primitive_uint_to_int { + ($SrcT:ident : $( $(#[$cfg:meta])* fn $method:ident -> $DstT:ident ; )*) => {$( + #[inline] + $(#[$cfg])* + fn $method(&self) -> Option<$DstT> { + let max = $DstT::MAX as $SrcT; + if size_of::<$SrcT>() < size_of::<$DstT>() || *self <= max { + Some(*self as $DstT) + } else { + None + } + } + )*} +} + +macro_rules! impl_to_primitive_uint_to_uint { + ($SrcT:ident : $( $(#[$cfg:meta])* fn $method:ident -> $DstT:ident ; )*) => {$( + #[inline] + $(#[$cfg])* + fn $method(&self) -> Option<$DstT> { + let max = $DstT::MAX as $SrcT; + if size_of::<$SrcT>() <= size_of::<$DstT>() || *self <= max { + Some(*self as $DstT) + } else { + None + } + } + )*} +} + +macro_rules! impl_to_primitive_uint { + ($T:ident) => { + impl ToPrimitive for $T { + impl_to_primitive_uint_to_int! { $T: + fn to_isize -> isize; + fn to_i8 -> i8; + fn to_i16 -> i16; + fn to_i32 -> i32; + fn to_i64 -> i64; + fn to_i128 -> i128; + } + + impl_to_primitive_uint_to_uint! { $T: + fn to_usize -> usize; + fn to_u8 -> u8; + fn to_u16 -> u16; + fn to_u32 -> u32; + fn to_u64 -> u64; + fn to_u128 -> u128; + } + + #[inline] + fn to_f32(&self) -> Option { + Some(*self as f32) + } + #[inline] + fn to_f64(&self) -> Option { + Some(*self as f64) + } + } + }; +} + +impl_to_primitive_uint!(usize); +impl_to_primitive_uint!(u8); +impl_to_primitive_uint!(u16); +impl_to_primitive_uint!(u32); +impl_to_primitive_uint!(u64); +impl_to_primitive_uint!(u128); + +macro_rules! impl_to_primitive_float_to_float { + ($SrcT:ident : $( fn $method:ident -> $DstT:ident ; )*) => {$( + #[inline] + fn $method(&self) -> Option<$DstT> { + // We can safely cast all values, whether NaN, +-inf, or finite. + // Finite values that are reducing size may saturate to +-inf. + Some(*self as $DstT) + } + )*} +} + +macro_rules! float_to_int_unchecked { + // SAFETY: Must not be NaN or infinite; must be representable as the integer after truncating. + // We already checked that the float is in the exclusive range `(MIN-1, MAX+1)`. + ($float:expr => $int:ty) => { + unsafe { $float.to_int_unchecked::<$int>() } + }; +} + +macro_rules! impl_to_primitive_float_to_signed_int { + ($f:ident : $( $(#[$cfg:meta])* fn $method:ident -> $i:ident ; )*) => {$( + #[inline] + $(#[$cfg])* + fn $method(&self) -> Option<$i> { + // Float as int truncates toward zero, so we want to allow values + // in the exclusive range `(MIN-1, MAX+1)`. + if size_of::<$f>() > size_of::<$i>() { + // With a larger size, we can represent the range exactly. + const MIN_M1: $f = $i::MIN as $f - 1.0; + const MAX_P1: $f = $i::MAX as $f + 1.0; + if *self > MIN_M1 && *self < MAX_P1 { + return Some(float_to_int_unchecked!(*self => $i)); + } + } else { + // We can't represent `MIN-1` exactly, but there's no fractional part + // at this magnitude, so we can just use a `MIN` inclusive boundary. + const MIN: $f = $i::MIN as $f; + // We can't represent `MAX` exactly, but it will round up to exactly + // `MAX+1` (a power of two) when we cast it. + const MAX_P1: $f = $i::MAX as $f; + if *self >= MIN && *self < MAX_P1 { + return Some(float_to_int_unchecked!(*self => $i)); + } + } + None + } + )*} +} + +macro_rules! impl_to_primitive_float_to_unsigned_int { + ($f:ident : $( $(#[$cfg:meta])* fn $method:ident -> $u:ident ; )*) => {$( + #[inline] + $(#[$cfg])* + fn $method(&self) -> Option<$u> { + // Float as int truncates toward zero, so we want to allow values + // in the exclusive range `(-1, MAX+1)`. + if size_of::<$f>() > size_of::<$u>() { + // With a larger size, we can represent the range exactly. + const MAX_P1: $f = $u::MAX as $f + 1.0; + if *self > -1.0 && *self < MAX_P1 { + return Some(float_to_int_unchecked!(*self => $u)); + } + } else { + // We can't represent `MAX` exactly, but it will round up to exactly + // `MAX+1` (a power of two) when we cast it. + // (`u128::MAX as f32` is infinity, but this is still ok.) + const MAX_P1: $f = $u::MAX as $f; + if *self > -1.0 && *self < MAX_P1 { + return Some(float_to_int_unchecked!(*self => $u)); + } + } + None + } + )*} +} + +macro_rules! impl_to_primitive_float { + ($T:ident) => { + impl ToPrimitive for $T { + impl_to_primitive_float_to_signed_int! { $T: + fn to_isize -> isize; + fn to_i8 -> i8; + fn to_i16 -> i16; + fn to_i32 -> i32; + fn to_i64 -> i64; + fn to_i128 -> i128; + } + + impl_to_primitive_float_to_unsigned_int! { $T: + fn to_usize -> usize; + fn to_u8 -> u8; + fn to_u16 -> u16; + fn to_u32 -> u32; + fn to_u64 -> u64; + fn to_u128 -> u128; + } + + impl_to_primitive_float_to_float! { $T: + fn to_f32 -> f32; + fn to_f64 -> f64; + } + } + }; +} + +impl_to_primitive_float!(f32); +impl_to_primitive_float!(f64); + +/// A generic trait for converting a number to a value. +/// +/// A value can be represented by the target type when it lies within +/// the range of scalars supported by the target type. +/// For example, a negative integer cannot be represented by an unsigned +/// integer type, and an `i64` with a very high magnitude might not be +/// convertible to an `i32`. +/// On the other hand, conversions with possible precision loss or truncation +/// are admitted, like an `f32` with a decimal part to an integer type, or +/// even a large `f64` saturating to `f32` infinity. +pub trait FromPrimitive: Sized { + /// Converts an `isize` to return an optional value of this type. If the + /// value cannot be represented by this type, then `None` is returned. + #[inline] + fn from_isize(n: isize) -> Option { + n.to_i64().and_then(FromPrimitive::from_i64) + } + + /// Converts an `i8` to return an optional value of this type. If the + /// value cannot be represented by this type, then `None` is returned. + #[inline] + fn from_i8(n: i8) -> Option { + FromPrimitive::from_i64(From::from(n)) + } + + /// Converts an `i16` to return an optional value of this type. If the + /// value cannot be represented by this type, then `None` is returned. + #[inline] + fn from_i16(n: i16) -> Option { + FromPrimitive::from_i64(From::from(n)) + } + + /// Converts an `i32` to return an optional value of this type. If the + /// value cannot be represented by this type, then `None` is returned. + #[inline] + fn from_i32(n: i32) -> Option { + FromPrimitive::from_i64(From::from(n)) + } + + /// Converts an `i64` to return an optional value of this type. If the + /// value cannot be represented by this type, then `None` is returned. + fn from_i64(n: i64) -> Option; + + /// Converts an `i128` to return an optional value of this type. If the + /// value cannot be represented by this type, then `None` is returned. + /// + /// The default implementation converts through `from_i64()`. Types implementing + /// this trait should override this method if they can represent a greater range. + #[inline] + fn from_i128(n: i128) -> Option { + n.to_i64().and_then(FromPrimitive::from_i64) + } + + /// Converts a `usize` to return an optional value of this type. If the + /// value cannot be represented by this type, then `None` is returned. + #[inline] + fn from_usize(n: usize) -> Option { + n.to_u64().and_then(FromPrimitive::from_u64) + } + + /// Converts an `u8` to return an optional value of this type. If the + /// value cannot be represented by this type, then `None` is returned. + #[inline] + fn from_u8(n: u8) -> Option { + FromPrimitive::from_u64(From::from(n)) + } + + /// Converts an `u16` to return an optional value of this type. If the + /// value cannot be represented by this type, then `None` is returned. + #[inline] + fn from_u16(n: u16) -> Option { + FromPrimitive::from_u64(From::from(n)) + } + + /// Converts an `u32` to return an optional value of this type. If the + /// value cannot be represented by this type, then `None` is returned. + #[inline] + fn from_u32(n: u32) -> Option { + FromPrimitive::from_u64(From::from(n)) + } + + /// Converts an `u64` to return an optional value of this type. If the + /// value cannot be represented by this type, then `None` is returned. + fn from_u64(n: u64) -> Option; + + /// Converts an `u128` to return an optional value of this type. If the + /// value cannot be represented by this type, then `None` is returned. + /// + /// The default implementation converts through `from_u64()`. Types implementing + /// this trait should override this method if they can represent a greater range. + #[inline] + fn from_u128(n: u128) -> Option { + n.to_u64().and_then(FromPrimitive::from_u64) + } + + /// Converts a `f32` to return an optional value of this type. If the + /// value cannot be represented by this type, then `None` is returned. + #[inline] + fn from_f32(n: f32) -> Option { + FromPrimitive::from_f64(From::from(n)) + } + + /// Converts a `f64` to return an optional value of this type. If the + /// value cannot be represented by this type, then `None` is returned. + /// + /// The default implementation tries to convert through `from_i64()`, and + /// failing that through `from_u64()`. Types implementing this trait should + /// override this method if they can represent a greater range. + #[inline] + fn from_f64(n: f64) -> Option { + match n.to_i64() { + Some(i) => FromPrimitive::from_i64(i), + None => n.to_u64().and_then(FromPrimitive::from_u64), + } + } +} + +macro_rules! impl_from_primitive { + ($T:ty, $to_ty:ident) => { + #[allow(deprecated)] + impl FromPrimitive for $T { + #[inline] + fn from_isize(n: isize) -> Option<$T> { + n.$to_ty() + } + #[inline] + fn from_i8(n: i8) -> Option<$T> { + n.$to_ty() + } + #[inline] + fn from_i16(n: i16) -> Option<$T> { + n.$to_ty() + } + #[inline] + fn from_i32(n: i32) -> Option<$T> { + n.$to_ty() + } + #[inline] + fn from_i64(n: i64) -> Option<$T> { + n.$to_ty() + } + #[inline] + fn from_i128(n: i128) -> Option<$T> { + n.$to_ty() + } + + #[inline] + fn from_usize(n: usize) -> Option<$T> { + n.$to_ty() + } + #[inline] + fn from_u8(n: u8) -> Option<$T> { + n.$to_ty() + } + #[inline] + fn from_u16(n: u16) -> Option<$T> { + n.$to_ty() + } + #[inline] + fn from_u32(n: u32) -> Option<$T> { + n.$to_ty() + } + #[inline] + fn from_u64(n: u64) -> Option<$T> { + n.$to_ty() + } + #[inline] + fn from_u128(n: u128) -> Option<$T> { + n.$to_ty() + } + + #[inline] + fn from_f32(n: f32) -> Option<$T> { + n.$to_ty() + } + #[inline] + fn from_f64(n: f64) -> Option<$T> { + n.$to_ty() + } + } + }; +} + +impl_from_primitive!(isize, to_isize); +impl_from_primitive!(i8, to_i8); +impl_from_primitive!(i16, to_i16); +impl_from_primitive!(i32, to_i32); +impl_from_primitive!(i64, to_i64); +impl_from_primitive!(i128, to_i128); +impl_from_primitive!(usize, to_usize); +impl_from_primitive!(u8, to_u8); +impl_from_primitive!(u16, to_u16); +impl_from_primitive!(u32, to_u32); +impl_from_primitive!(u64, to_u64); +impl_from_primitive!(u128, to_u128); +impl_from_primitive!(f32, to_f32); +impl_from_primitive!(f64, to_f64); + +macro_rules! impl_to_primitive_wrapping { + ($( $(#[$cfg:meta])* fn $method:ident -> $i:ident ; )*) => {$( + #[inline] + $(#[$cfg])* + fn $method(&self) -> Option<$i> { + (self.0).$method() + } + )*} +} + +impl ToPrimitive for Wrapping { + impl_to_primitive_wrapping! { + fn to_isize -> isize; + fn to_i8 -> i8; + fn to_i16 -> i16; + fn to_i32 -> i32; + fn to_i64 -> i64; + fn to_i128 -> i128; + + fn to_usize -> usize; + fn to_u8 -> u8; + fn to_u16 -> u16; + fn to_u32 -> u32; + fn to_u64 -> u64; + fn to_u128 -> u128; + + fn to_f32 -> f32; + fn to_f64 -> f64; + } +} + +macro_rules! impl_from_primitive_wrapping { + ($( $(#[$cfg:meta])* fn $method:ident ( $i:ident ); )*) => {$( + #[inline] + $(#[$cfg])* + fn $method(n: $i) -> Option { + T::$method(n).map(Wrapping) + } + )*} +} + +impl FromPrimitive for Wrapping { + impl_from_primitive_wrapping! { + fn from_isize(isize); + fn from_i8(i8); + fn from_i16(i16); + fn from_i32(i32); + fn from_i64(i64); + fn from_i128(i128); + + fn from_usize(usize); + fn from_u8(u8); + fn from_u16(u16); + fn from_u32(u32); + fn from_u64(u64); + fn from_u128(u128); + + fn from_f32(f32); + fn from_f64(f64); + } +} + +/// Cast from one machine scalar to another. +/// +/// # Examples +/// +/// ``` +/// # use num_traits as num; +/// let twenty: f32 = num::cast(0x14).unwrap(); +/// assert_eq!(twenty, 20f32); +/// ``` +/// +#[inline] +pub fn cast(n: T) -> Option { + NumCast::from(n) +} + +/// An interface for casting between machine scalars. +pub trait NumCast: Sized + ToPrimitive { + /// Creates a number from another value that can be converted into + /// a primitive via the `ToPrimitive` trait. If the source value cannot be + /// represented by the target type, then `None` is returned. + /// + /// A value can be represented by the target type when it lies within + /// the range of scalars supported by the target type. + /// For example, a negative integer cannot be represented by an unsigned + /// integer type, and an `i64` with a very high magnitude might not be + /// convertible to an `i32`. + /// On the other hand, conversions with possible precision loss or truncation + /// are admitted, like an `f32` with a decimal part to an integer type, or + /// even a large `f64` saturating to `f32` infinity. + fn from(n: T) -> Option; +} + +macro_rules! impl_num_cast { + ($T:ty, $conv:ident) => { + impl NumCast for $T { + #[inline] + #[allow(deprecated)] + fn from(n: N) -> Option<$T> { + // `$conv` could be generated using `concat_idents!`, but that + // macro seems to be broken at the moment + n.$conv() + } + } + }; +} + +impl_num_cast!(u8, to_u8); +impl_num_cast!(u16, to_u16); +impl_num_cast!(u32, to_u32); +impl_num_cast!(u64, to_u64); +impl_num_cast!(u128, to_u128); +impl_num_cast!(usize, to_usize); +impl_num_cast!(i8, to_i8); +impl_num_cast!(i16, to_i16); +impl_num_cast!(i32, to_i32); +impl_num_cast!(i64, to_i64); +impl_num_cast!(i128, to_i128); +impl_num_cast!(isize, to_isize); +impl_num_cast!(f32, to_f32); +impl_num_cast!(f64, to_f64); + +impl NumCast for Wrapping { + fn from(n: U) -> Option { + T::from(n).map(Wrapping) + } +} + +/// A generic interface for casting between machine scalars with the +/// `as` operator, which admits narrowing and precision loss. +/// Implementers of this trait `AsPrimitive` should behave like a primitive +/// numeric type (e.g. a newtype around another primitive), and the +/// intended conversion must never fail. +/// +/// # Examples +/// +/// ``` +/// # use num_traits::AsPrimitive; +/// let three: i32 = (3.14159265f32).as_(); +/// assert_eq!(three, 3); +/// ``` +/// +/// # Safety +/// +/// **In Rust versions before 1.45.0**, some uses of the `as` operator were not entirely safe. +/// In particular, it was undefined behavior if +/// a truncated floating point value could not fit in the target integer +/// type ([#10184](https://github.com/rust-lang/rust/issues/10184)). +/// +/// ```ignore +/// # use num_traits::AsPrimitive; +/// let x: u8 = (1.04E+17).as_(); // UB +/// ``` +/// +pub trait AsPrimitive: 'static + Copy +where + T: 'static + Copy, +{ + /// Convert a value to another, using the `as` operator. + fn as_(self) -> T; +} + +macro_rules! impl_as_primitive { + (@ $T: ty => $(#[$cfg:meta])* impl $U: ty ) => { + $(#[$cfg])* + impl AsPrimitive<$U> for $T { + #[inline] fn as_(self) -> $U { self as $U } + } + }; + (@ $T: ty => { $( $U: ty ),* } ) => {$( + impl_as_primitive!(@ $T => impl $U); + )*}; + ($T: ty => { $( $U: ty ),* } ) => { + impl_as_primitive!(@ $T => { $( $U ),* }); + impl_as_primitive!(@ $T => { u8, u16, u32, u64, u128, usize }); + impl_as_primitive!(@ $T => { i8, i16, i32, i64, i128, isize }); + }; +} + +impl_as_primitive!(u8 => { char, f32, f64 }); +impl_as_primitive!(i8 => { f32, f64 }); +impl_as_primitive!(u16 => { f32, f64 }); +impl_as_primitive!(i16 => { f32, f64 }); +impl_as_primitive!(u32 => { f32, f64 }); +impl_as_primitive!(i32 => { f32, f64 }); +impl_as_primitive!(u64 => { f32, f64 }); +impl_as_primitive!(i64 => { f32, f64 }); +impl_as_primitive!(u128 => { f32, f64 }); +impl_as_primitive!(i128 => { f32, f64 }); +impl_as_primitive!(usize => { f32, f64 }); +impl_as_primitive!(isize => { f32, f64 }); +impl_as_primitive!(f32 => { f32, f64 }); +impl_as_primitive!(f64 => { f32, f64 }); +impl_as_primitive!(char => { char }); +impl_as_primitive!(bool => {}); diff --git a/tools/vendor/num-traits/src/float.rs b/tools/vendor/num-traits/src/float.rs new file mode 100644 index 0000000000..d0d21a5099 --- /dev/null +++ b/tools/vendor/num-traits/src/float.rs @@ -0,0 +1,2513 @@ +use core::cmp::Ordering; +use core::num::FpCategory; +use core::ops::{Add, Div, Neg}; + +use core::f32; +use core::f64; + +use crate::{Num, NumCast, ToPrimitive}; + +/// Generic trait for floating point numbers that works with `no_std`. +/// +/// This trait implements a subset of the `Float` trait. +pub trait FloatCore: Num + NumCast + Neg + PartialOrd + Copy { + /// Returns positive infinity. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T) { + /// assert!(T::infinity() == x); + /// } + /// + /// check(f32::INFINITY); + /// check(f64::INFINITY); + /// ``` + fn infinity() -> Self; + + /// Returns negative infinity. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T) { + /// assert!(T::neg_infinity() == x); + /// } + /// + /// check(f32::NEG_INFINITY); + /// check(f64::NEG_INFINITY); + /// ``` + fn neg_infinity() -> Self; + + /// Returns NaN. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// + /// fn check() { + /// let n = T::nan(); + /// assert!(n != n); + /// } + /// + /// check::(); + /// check::(); + /// ``` + fn nan() -> Self; + + /// Returns `-0.0`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(n: T) { + /// let z = T::neg_zero(); + /// assert!(z.is_zero()); + /// assert!(T::one() / z == n); + /// } + /// + /// check(f32::NEG_INFINITY); + /// check(f64::NEG_INFINITY); + /// ``` + fn neg_zero() -> Self; + + /// Returns the smallest finite value that this type can represent. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T) { + /// assert!(T::min_value() == x); + /// } + /// + /// check(f32::MIN); + /// check(f64::MIN); + /// ``` + fn min_value() -> Self; + + /// Returns the smallest positive, normalized value that this type can represent. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T) { + /// assert!(T::min_positive_value() == x); + /// } + /// + /// check(f32::MIN_POSITIVE); + /// check(f64::MIN_POSITIVE); + /// ``` + fn min_positive_value() -> Self; + + /// Returns epsilon, a small positive value. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T) { + /// assert!(T::epsilon() == x); + /// } + /// + /// check(f32::EPSILON); + /// check(f64::EPSILON); + /// ``` + fn epsilon() -> Self; + + /// Returns the largest finite value that this type can represent. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T) { + /// assert!(T::max_value() == x); + /// } + /// + /// check(f32::MAX); + /// check(f64::MAX); + /// ``` + fn max_value() -> Self; + + /// Returns `true` if the number is NaN. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, p: bool) { + /// assert!(x.is_nan() == p); + /// } + /// + /// check(f32::NAN, true); + /// check(f32::INFINITY, false); + /// check(f64::NAN, true); + /// check(0.0f64, false); + /// ``` + #[inline] + #[allow(clippy::eq_op)] + fn is_nan(self) -> bool { + self != self + } + + /// Returns `true` if the number is infinite. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, p: bool) { + /// assert!(x.is_infinite() == p); + /// } + /// + /// check(f32::INFINITY, true); + /// check(f32::NEG_INFINITY, true); + /// check(f32::NAN, false); + /// check(f64::INFINITY, true); + /// check(f64::NEG_INFINITY, true); + /// check(0.0f64, false); + /// ``` + #[inline] + fn is_infinite(self) -> bool { + self == Self::infinity() || self == Self::neg_infinity() + } + + /// Returns `true` if the number is neither infinite or NaN. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, p: bool) { + /// assert!(x.is_finite() == p); + /// } + /// + /// check(f32::INFINITY, false); + /// check(f32::MAX, true); + /// check(f64::NEG_INFINITY, false); + /// check(f64::MIN_POSITIVE, true); + /// check(f64::NAN, false); + /// ``` + #[inline] + fn is_finite(self) -> bool { + !(self.is_nan() || self.is_infinite()) + } + + /// Returns `true` if the number is neither zero, infinite, subnormal or NaN. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, p: bool) { + /// assert!(x.is_normal() == p); + /// } + /// + /// check(f32::INFINITY, false); + /// check(f32::MAX, true); + /// check(f64::NEG_INFINITY, false); + /// check(f64::MIN_POSITIVE, true); + /// check(0.0f64, false); + /// ``` + #[inline] + fn is_normal(self) -> bool { + self.classify() == FpCategory::Normal + } + + /// Returns `true` if the number is [subnormal]. + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::f64; + /// + /// let min = f64::MIN_POSITIVE; // 2.2250738585072014e-308_f64 + /// let max = f64::MAX; + /// let lower_than_min = 1.0e-308_f64; + /// let zero = 0.0_f64; + /// + /// assert!(!min.is_subnormal()); + /// assert!(!max.is_subnormal()); + /// + /// assert!(!zero.is_subnormal()); + /// assert!(!f64::NAN.is_subnormal()); + /// assert!(!f64::INFINITY.is_subnormal()); + /// // Values between `0` and `min` are Subnormal. + /// assert!(lower_than_min.is_subnormal()); + /// ``` + /// [subnormal]: https://en.wikipedia.org/wiki/Subnormal_number + #[inline] + fn is_subnormal(self) -> bool { + self.classify() == FpCategory::Subnormal + } + + /// Returns the floating point category of the number. If only one property + /// is going to be tested, it is generally faster to use the specific + /// predicate instead. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// use std::num::FpCategory; + /// + /// fn check(x: T, c: FpCategory) { + /// assert!(x.classify() == c); + /// } + /// + /// check(f32::INFINITY, FpCategory::Infinite); + /// check(f32::MAX, FpCategory::Normal); + /// check(f64::NAN, FpCategory::Nan); + /// check(f64::MIN_POSITIVE, FpCategory::Normal); + /// check(f64::MIN_POSITIVE / 2.0, FpCategory::Subnormal); + /// check(0.0f64, FpCategory::Zero); + /// ``` + fn classify(self) -> FpCategory; + + /// Returns the largest integer less than or equal to a number. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, y: T) { + /// assert!(x.floor() == y); + /// } + /// + /// check(f32::INFINITY, f32::INFINITY); + /// check(0.9f32, 0.0); + /// check(1.0f32, 1.0); + /// check(1.1f32, 1.0); + /// check(-0.0f64, 0.0); + /// check(-0.9f64, -1.0); + /// check(-1.0f64, -1.0); + /// check(-1.1f64, -2.0); + /// check(f64::MIN, f64::MIN); + /// ``` + #[inline] + fn floor(self) -> Self { + let f = self.fract(); + if f.is_nan() || f.is_zero() { + self + } else if self < Self::zero() { + self - f - Self::one() + } else { + self - f + } + } + + /// Returns the smallest integer greater than or equal to a number. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, y: T) { + /// assert!(x.ceil() == y); + /// } + /// + /// check(f32::INFINITY, f32::INFINITY); + /// check(0.9f32, 1.0); + /// check(1.0f32, 1.0); + /// check(1.1f32, 2.0); + /// check(-0.0f64, 0.0); + /// check(-0.9f64, -0.0); + /// check(-1.0f64, -1.0); + /// check(-1.1f64, -1.0); + /// check(f64::MIN, f64::MIN); + /// ``` + #[inline] + fn ceil(self) -> Self { + let f = self.fract(); + if f.is_nan() || f.is_zero() { + self + } else if self > Self::zero() { + self - f + Self::one() + } else { + self - f + } + } + + /// Returns the nearest integer to a number. Round half-way cases away from `0.0`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, y: T) { + /// assert!(x.round() == y); + /// } + /// + /// check(f32::INFINITY, f32::INFINITY); + /// check(0.4f32, 0.0); + /// check(0.5f32, 1.0); + /// check(0.6f32, 1.0); + /// check(-0.4f64, 0.0); + /// check(-0.5f64, -1.0); + /// check(-0.6f64, -1.0); + /// check(f64::MIN, f64::MIN); + /// ``` + #[inline] + fn round(self) -> Self { + let one = Self::one(); + let h = Self::from(0.5).expect("Unable to cast from 0.5"); + let f = self.fract(); + if f.is_nan() || f.is_zero() { + self + } else if self > Self::zero() { + if f < h { + self - f + } else { + self - f + one + } + } else if -f < h { + self - f + } else { + self - f - one + } + } + + /// Return the integer part of a number. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, y: T) { + /// assert!(x.trunc() == y); + /// } + /// + /// check(f32::INFINITY, f32::INFINITY); + /// check(0.9f32, 0.0); + /// check(1.0f32, 1.0); + /// check(1.1f32, 1.0); + /// check(-0.0f64, 0.0); + /// check(-0.9f64, -0.0); + /// check(-1.0f64, -1.0); + /// check(-1.1f64, -1.0); + /// check(f64::MIN, f64::MIN); + /// ``` + #[inline] + fn trunc(self) -> Self { + let f = self.fract(); + if f.is_nan() { + self + } else { + self - f + } + } + + /// Returns the fractional part of a number. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, y: T) { + /// assert!(x.fract() == y); + /// } + /// + /// check(f32::MAX, 0.0); + /// check(0.75f32, 0.75); + /// check(1.0f32, 0.0); + /// check(1.25f32, 0.25); + /// check(-0.0f64, 0.0); + /// check(-0.75f64, -0.75); + /// check(-1.0f64, 0.0); + /// check(-1.25f64, -0.25); + /// check(f64::MIN, 0.0); + /// ``` + #[inline] + fn fract(self) -> Self { + if self.is_zero() { + Self::zero() + } else { + self % Self::one() + } + } + + /// Computes the absolute value of `self`. Returns `FloatCore::nan()` if the + /// number is `FloatCore::nan()`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, y: T) { + /// assert!(x.abs() == y); + /// } + /// + /// check(f32::INFINITY, f32::INFINITY); + /// check(1.0f32, 1.0); + /// check(0.0f64, 0.0); + /// check(-0.0f64, 0.0); + /// check(-1.0f64, 1.0); + /// check(f64::MIN, f64::MAX); + /// ``` + #[inline] + fn abs(self) -> Self { + if self.is_sign_positive() { + return self; + } + if self.is_sign_negative() { + return -self; + } + Self::nan() + } + + /// Returns a number that represents the sign of `self`. + /// + /// - `1.0` if the number is positive, `+0.0` or `FloatCore::infinity()` + /// - `-1.0` if the number is negative, `-0.0` or `FloatCore::neg_infinity()` + /// - `FloatCore::nan()` if the number is `FloatCore::nan()` + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, y: T) { + /// assert!(x.signum() == y); + /// } + /// + /// check(f32::INFINITY, 1.0); + /// check(3.0f32, 1.0); + /// check(0.0f32, 1.0); + /// check(-0.0f64, -1.0); + /// check(-3.0f64, -1.0); + /// check(f64::MIN, -1.0); + /// ``` + #[inline] + fn signum(self) -> Self { + if self.is_nan() { + Self::nan() + } else if self.is_sign_negative() { + -Self::one() + } else { + Self::one() + } + } + + /// Returns `true` if `self` is positive, including `+0.0` and + /// `FloatCore::infinity()`, and `FloatCore::nan()`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, p: bool) { + /// assert!(x.is_sign_positive() == p); + /// } + /// + /// check(f32::INFINITY, true); + /// check(f32::MAX, true); + /// check(0.0f32, true); + /// check(-0.0f64, false); + /// check(f64::NEG_INFINITY, false); + /// check(f64::MIN_POSITIVE, true); + /// check(f64::NAN, true); + /// check(-f64::NAN, false); + /// ``` + #[inline] + fn is_sign_positive(self) -> bool { + !self.is_sign_negative() + } + + /// Returns `true` if `self` is negative, including `-0.0` and + /// `FloatCore::neg_infinity()`, and `-FloatCore::nan()`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, p: bool) { + /// assert!(x.is_sign_negative() == p); + /// } + /// + /// check(f32::INFINITY, false); + /// check(f32::MAX, false); + /// check(0.0f32, false); + /// check(-0.0f64, true); + /// check(f64::NEG_INFINITY, true); + /// check(f64::MIN_POSITIVE, false); + /// check(f64::NAN, false); + /// check(-f64::NAN, true); + /// ``` + #[inline] + fn is_sign_negative(self) -> bool { + let (_, _, sign) = self.integer_decode(); + sign < 0 + } + + /// Returns the minimum of the two numbers. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, y: T, min: T) { + /// assert!(x.min(y) == min); + /// } + /// + /// check(1.0f32, 2.0, 1.0); + /// check(f32::NAN, 2.0, 2.0); + /// check(1.0f64, -2.0, -2.0); + /// check(1.0f64, f64::NAN, 1.0); + /// ``` + #[inline] + fn min(self, other: Self) -> Self { + if self.is_nan() { + return other; + } + if other.is_nan() { + return self; + } + if self < other { + self + } else { + other + } + } + + /// Returns the maximum of the two numbers. + /// + /// If one of the arguments is NaN, then the other argument is returned. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, y: T, max: T) { + /// assert!(x.max(y) == max); + /// } + /// + /// check(1.0f32, 2.0, 2.0); + /// check(1.0f32, f32::NAN, 1.0); + /// check(-1.0f64, 2.0, 2.0); + /// check(-1.0f64, f64::NAN, -1.0); + /// ``` + #[inline] + fn max(self, other: Self) -> Self { + if self.is_nan() { + return other; + } + if other.is_nan() { + return self; + } + if self > other { + self + } else { + other + } + } + + /// A value bounded by a minimum and a maximum + /// + /// If input is less than min then this returns min. + /// If input is greater than max then this returns max. + /// Otherwise this returns input. + /// + /// **Panics** in debug mode if `!(min <= max)`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// + /// fn check(val: T, min: T, max: T, expected: T) { + /// assert!(val.clamp(min, max) == expected); + /// } + /// + /// + /// check(1.0f32, 0.0, 2.0, 1.0); + /// check(1.0f32, 2.0, 3.0, 2.0); + /// check(3.0f32, 0.0, 2.0, 2.0); + /// + /// check(1.0f64, 0.0, 2.0, 1.0); + /// check(1.0f64, 2.0, 3.0, 2.0); + /// check(3.0f64, 0.0, 2.0, 2.0); + /// ``` + fn clamp(self, min: Self, max: Self) -> Self { + crate::clamp(self, min, max) + } + + /// Returns the reciprocal (multiplicative inverse) of the number. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, y: T) { + /// assert!(x.recip() == y); + /// assert!(y.recip() == x); + /// } + /// + /// check(f32::INFINITY, 0.0); + /// check(2.0f32, 0.5); + /// check(-0.25f64, -4.0); + /// check(-0.0f64, f64::NEG_INFINITY); + /// ``` + #[inline] + fn recip(self) -> Self { + Self::one() / self + } + + /// Raise a number to an integer power. + /// + /// Using this function is generally faster than using `powf` + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// + /// fn check(x: T, exp: i32, powi: T) { + /// assert!(x.powi(exp) == powi); + /// } + /// + /// check(9.0f32, 2, 81.0); + /// check(1.0f32, -2, 1.0); + /// check(10.0f64, 20, 1e20); + /// check(4.0f64, -2, 0.0625); + /// check(-1.0f64, std::i32::MIN, 1.0); + /// ``` + #[inline] + fn powi(mut self, mut exp: i32) -> Self { + if exp < 0 { + exp = exp.wrapping_neg(); + self = self.recip(); + } + // It should always be possible to convert a positive `i32` to a `usize`. + // Note, `i32::MIN` will wrap and still be negative, so we need to convert + // to `u32` without sign-extension before growing to `usize`. + super::pow(self, (exp as u32).to_usize().unwrap()) + } + + /// Converts to degrees, assuming the number is in radians. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(rad: T, deg: T) { + /// assert!(rad.to_degrees() == deg); + /// } + /// + /// check(0.0f32, 0.0); + /// check(f32::consts::PI, 180.0); + /// check(f64::consts::FRAC_PI_4, 45.0); + /// check(f64::INFINITY, f64::INFINITY); + /// ``` + fn to_degrees(self) -> Self; + + /// Converts to radians, assuming the number is in degrees. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(deg: T, rad: T) { + /// assert!(deg.to_radians() == rad); + /// } + /// + /// check(0.0f32, 0.0); + /// check(180.0, f32::consts::PI); + /// check(45.0, f64::consts::FRAC_PI_4); + /// check(f64::INFINITY, f64::INFINITY); + /// ``` + fn to_radians(self) -> Self; + + /// Returns the mantissa, base 2 exponent, and sign as integers, respectively. + /// The original number can be recovered by `sign * mantissa * 2 ^ exponent`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::float::FloatCore; + /// use std::{f32, f64}; + /// + /// fn check(x: T, m: u64, e: i16, s:i8) { + /// let (mantissa, exponent, sign) = x.integer_decode(); + /// assert_eq!(mantissa, m); + /// assert_eq!(exponent, e); + /// assert_eq!(sign, s); + /// } + /// + /// check(2.0f32, 1 << 23, -22, 1); + /// check(-2.0f32, 1 << 23, -22, -1); + /// check(f32::INFINITY, 1 << 23, 105, 1); + /// check(f64::NEG_INFINITY, 1 << 52, 972, -1); + /// ``` + fn integer_decode(self) -> (u64, i16, i8); +} + +impl FloatCore for f32 { + constant! { + infinity() -> f32::INFINITY; + neg_infinity() -> f32::NEG_INFINITY; + nan() -> f32::NAN; + neg_zero() -> -0.0; + min_value() -> f32::MIN; + min_positive_value() -> f32::MIN_POSITIVE; + epsilon() -> f32::EPSILON; + max_value() -> f32::MAX; + } + + #[inline] + fn integer_decode(self) -> (u64, i16, i8) { + integer_decode_f32(self) + } + + forward! { + Self::is_nan(self) -> bool; + Self::is_infinite(self) -> bool; + Self::is_finite(self) -> bool; + Self::is_normal(self) -> bool; + Self::is_subnormal(self) -> bool; + Self::clamp(self, min: Self, max: Self) -> Self; + Self::classify(self) -> FpCategory; + Self::is_sign_positive(self) -> bool; + Self::is_sign_negative(self) -> bool; + Self::min(self, other: Self) -> Self; + Self::max(self, other: Self) -> Self; + Self::recip(self) -> Self; + Self::to_degrees(self) -> Self; + Self::to_radians(self) -> Self; + } + + #[cfg(feature = "std")] + forward! { + Self::floor(self) -> Self; + Self::ceil(self) -> Self; + Self::round(self) -> Self; + Self::trunc(self) -> Self; + Self::fract(self) -> Self; + Self::abs(self) -> Self; + Self::signum(self) -> Self; + Self::powi(self, n: i32) -> Self; + } + + #[cfg(all(not(feature = "std"), feature = "libm"))] + forward! { + libm::floorf as floor(self) -> Self; + libm::ceilf as ceil(self) -> Self; + libm::roundf as round(self) -> Self; + libm::truncf as trunc(self) -> Self; + libm::fabsf as abs(self) -> Self; + } + + #[cfg(all(not(feature = "std"), feature = "libm"))] + #[inline] + fn fract(self) -> Self { + self - libm::truncf(self) + } +} + +impl FloatCore for f64 { + constant! { + infinity() -> f64::INFINITY; + neg_infinity() -> f64::NEG_INFINITY; + nan() -> f64::NAN; + neg_zero() -> -0.0; + min_value() -> f64::MIN; + min_positive_value() -> f64::MIN_POSITIVE; + epsilon() -> f64::EPSILON; + max_value() -> f64::MAX; + } + + #[inline] + fn integer_decode(self) -> (u64, i16, i8) { + integer_decode_f64(self) + } + + forward! { + Self::is_nan(self) -> bool; + Self::is_infinite(self) -> bool; + Self::is_finite(self) -> bool; + Self::is_normal(self) -> bool; + Self::is_subnormal(self) -> bool; + Self::clamp(self, min: Self, max: Self) -> Self; + Self::classify(self) -> FpCategory; + Self::is_sign_positive(self) -> bool; + Self::is_sign_negative(self) -> bool; + Self::min(self, other: Self) -> Self; + Self::max(self, other: Self) -> Self; + Self::recip(self) -> Self; + Self::to_degrees(self) -> Self; + Self::to_radians(self) -> Self; + } + + #[cfg(feature = "std")] + forward! { + Self::floor(self) -> Self; + Self::ceil(self) -> Self; + Self::round(self) -> Self; + Self::trunc(self) -> Self; + Self::fract(self) -> Self; + Self::abs(self) -> Self; + Self::signum(self) -> Self; + Self::powi(self, n: i32) -> Self; + } + + #[cfg(all(not(feature = "std"), feature = "libm"))] + forward! { + libm::floor as floor(self) -> Self; + libm::ceil as ceil(self) -> Self; + libm::round as round(self) -> Self; + libm::trunc as trunc(self) -> Self; + libm::fabs as abs(self) -> Self; + } + + #[cfg(all(not(feature = "std"), feature = "libm"))] + #[inline] + fn fract(self) -> Self { + self - libm::trunc(self) + } +} + +// FIXME: these doctests aren't actually helpful, because they're using and +// testing the inherent methods directly, not going through `Float`. + +/// Generic trait for floating point numbers +/// +/// This trait is only available with the `std` feature, or with the `libm` feature otherwise. +#[cfg(any(feature = "std", feature = "libm"))] +pub trait Float: Num + Copy + NumCast + PartialOrd + Neg { + /// Returns the `NaN` value. + /// + /// ``` + /// use num_traits::Float; + /// + /// let nan: f32 = Float::nan(); + /// + /// assert!(nan.is_nan()); + /// ``` + fn nan() -> Self; + /// Returns the infinite value. + /// + /// ``` + /// use num_traits::Float; + /// use std::f32; + /// + /// let infinity: f32 = Float::infinity(); + /// + /// assert!(infinity.is_infinite()); + /// assert!(!infinity.is_finite()); + /// assert!(infinity > f32::MAX); + /// ``` + fn infinity() -> Self; + /// Returns the negative infinite value. + /// + /// ``` + /// use num_traits::Float; + /// use std::f32; + /// + /// let neg_infinity: f32 = Float::neg_infinity(); + /// + /// assert!(neg_infinity.is_infinite()); + /// assert!(!neg_infinity.is_finite()); + /// assert!(neg_infinity < f32::MIN); + /// ``` + fn neg_infinity() -> Self; + /// Returns `-0.0`. + /// + /// ``` + /// use num_traits::{Zero, Float}; + /// + /// let inf: f32 = Float::infinity(); + /// let zero: f32 = Zero::zero(); + /// let neg_zero: f32 = Float::neg_zero(); + /// + /// assert_eq!(zero, neg_zero); + /// assert_eq!(7.0f32/inf, zero); + /// assert_eq!(zero * 10.0, zero); + /// ``` + fn neg_zero() -> Self; + + /// Returns the smallest finite value that this type can represent. + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let x: f64 = Float::min_value(); + /// + /// assert_eq!(x, f64::MIN); + /// ``` + fn min_value() -> Self; + + /// Returns the smallest positive, normalized value that this type can represent. + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let x: f64 = Float::min_positive_value(); + /// + /// assert_eq!(x, f64::MIN_POSITIVE); + /// ``` + fn min_positive_value() -> Self; + + /// Returns epsilon, a small positive value. + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let x: f64 = Float::epsilon(); + /// + /// assert_eq!(x, f64::EPSILON); + /// ``` + /// + /// # Panics + /// + /// The default implementation will panic if `f32::EPSILON` cannot + /// be cast to `Self`. + fn epsilon() -> Self { + Self::from(f32::EPSILON).expect("Unable to cast from f32::EPSILON") + } + + /// Returns the largest finite value that this type can represent. + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let x: f64 = Float::max_value(); + /// assert_eq!(x, f64::MAX); + /// ``` + fn max_value() -> Self; + + /// Returns `true` if this value is `NaN` and false otherwise. + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let nan = f64::NAN; + /// let f = 7.0; + /// + /// assert!(nan.is_nan()); + /// assert!(!f.is_nan()); + /// ``` + fn is_nan(self) -> bool; + + /// Returns `true` if this value is positive infinity or negative infinity and + /// false otherwise. + /// + /// ``` + /// use num_traits::Float; + /// use std::f32; + /// + /// let f = 7.0f32; + /// let inf: f32 = Float::infinity(); + /// let neg_inf: f32 = Float::neg_infinity(); + /// let nan: f32 = f32::NAN; + /// + /// assert!(!f.is_infinite()); + /// assert!(!nan.is_infinite()); + /// + /// assert!(inf.is_infinite()); + /// assert!(neg_inf.is_infinite()); + /// ``` + fn is_infinite(self) -> bool; + + /// Returns `true` if this number is neither infinite nor `NaN`. + /// + /// ``` + /// use num_traits::Float; + /// use std::f32; + /// + /// let f = 7.0f32; + /// let inf: f32 = Float::infinity(); + /// let neg_inf: f32 = Float::neg_infinity(); + /// let nan: f32 = f32::NAN; + /// + /// assert!(f.is_finite()); + /// + /// assert!(!nan.is_finite()); + /// assert!(!inf.is_finite()); + /// assert!(!neg_inf.is_finite()); + /// ``` + fn is_finite(self) -> bool; + + /// Returns `true` if the number is neither zero, infinite, + /// [subnormal][subnormal], or `NaN`. + /// + /// ``` + /// use num_traits::Float; + /// use std::f32; + /// + /// let min = f32::MIN_POSITIVE; // 1.17549435e-38f32 + /// let max = f32::MAX; + /// let lower_than_min = 1.0e-40_f32; + /// let zero = 0.0f32; + /// + /// assert!(min.is_normal()); + /// assert!(max.is_normal()); + /// + /// assert!(!zero.is_normal()); + /// assert!(!f32::NAN.is_normal()); + /// assert!(!f32::INFINITY.is_normal()); + /// // Values between `0` and `min` are Subnormal. + /// assert!(!lower_than_min.is_normal()); + /// ``` + /// [subnormal]: http://en.wikipedia.org/wiki/Subnormal_number + fn is_normal(self) -> bool; + + /// Returns `true` if the number is [subnormal]. + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let min = f64::MIN_POSITIVE; // 2.2250738585072014e-308_f64 + /// let max = f64::MAX; + /// let lower_than_min = 1.0e-308_f64; + /// let zero = 0.0_f64; + /// + /// assert!(!min.is_subnormal()); + /// assert!(!max.is_subnormal()); + /// + /// assert!(!zero.is_subnormal()); + /// assert!(!f64::NAN.is_subnormal()); + /// assert!(!f64::INFINITY.is_subnormal()); + /// // Values between `0` and `min` are Subnormal. + /// assert!(lower_than_min.is_subnormal()); + /// ``` + /// [subnormal]: https://en.wikipedia.org/wiki/Subnormal_number + #[inline] + fn is_subnormal(self) -> bool { + self.classify() == FpCategory::Subnormal + } + + /// Returns the floating point category of the number. If only one property + /// is going to be tested, it is generally faster to use the specific + /// predicate instead. + /// + /// ``` + /// use num_traits::Float; + /// use std::num::FpCategory; + /// use std::f32; + /// + /// let num = 12.4f32; + /// let inf = f32::INFINITY; + /// + /// assert_eq!(num.classify(), FpCategory::Normal); + /// assert_eq!(inf.classify(), FpCategory::Infinite); + /// ``` + fn classify(self) -> FpCategory; + + /// Returns the largest integer less than or equal to a number. + /// + /// ``` + /// use num_traits::Float; + /// + /// let f = 3.99; + /// let g = 3.0; + /// + /// assert_eq!(f.floor(), 3.0); + /// assert_eq!(g.floor(), 3.0); + /// ``` + fn floor(self) -> Self; + + /// Returns the smallest integer greater than or equal to a number. + /// + /// ``` + /// use num_traits::Float; + /// + /// let f = 3.01; + /// let g = 4.0; + /// + /// assert_eq!(f.ceil(), 4.0); + /// assert_eq!(g.ceil(), 4.0); + /// ``` + fn ceil(self) -> Self; + + /// Returns the nearest integer to a number. Round half-way cases away from + /// `0.0`. + /// + /// ``` + /// use num_traits::Float; + /// + /// let f = 3.3; + /// let g = -3.3; + /// + /// assert_eq!(f.round(), 3.0); + /// assert_eq!(g.round(), -3.0); + /// ``` + fn round(self) -> Self; + + /// Return the integer part of a number. + /// + /// ``` + /// use num_traits::Float; + /// + /// let f = 3.3; + /// let g = -3.7; + /// + /// assert_eq!(f.trunc(), 3.0); + /// assert_eq!(g.trunc(), -3.0); + /// ``` + fn trunc(self) -> Self; + + /// Returns the fractional part of a number. + /// + /// ``` + /// use num_traits::Float; + /// + /// let x = 3.5; + /// let y = -3.5; + /// let abs_difference_x = (x.fract() - 0.5).abs(); + /// let abs_difference_y = (y.fract() - (-0.5)).abs(); + /// + /// assert!(abs_difference_x < 1e-10); + /// assert!(abs_difference_y < 1e-10); + /// ``` + fn fract(self) -> Self; + + /// Computes the absolute value of `self`. Returns `Float::nan()` if the + /// number is `Float::nan()`. + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let x = 3.5; + /// let y = -3.5; + /// + /// let abs_difference_x = (x.abs() - x).abs(); + /// let abs_difference_y = (y.abs() - (-y)).abs(); + /// + /// assert!(abs_difference_x < 1e-10); + /// assert!(abs_difference_y < 1e-10); + /// + /// assert!(f64::NAN.abs().is_nan()); + /// ``` + fn abs(self) -> Self; + + /// Returns a number that represents the sign of `self`. + /// + /// - `1.0` if the number is positive, `+0.0` or `Float::infinity()` + /// - `-1.0` if the number is negative, `-0.0` or `Float::neg_infinity()` + /// - `Float::nan()` if the number is `Float::nan()` + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let f = 3.5; + /// + /// assert_eq!(f.signum(), 1.0); + /// assert_eq!(f64::NEG_INFINITY.signum(), -1.0); + /// + /// assert!(f64::NAN.signum().is_nan()); + /// ``` + fn signum(self) -> Self; + + /// Returns `true` if `self` is positive, including `+0.0`, + /// `Float::infinity()`, and `Float::nan()`. + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let nan: f64 = f64::NAN; + /// let neg_nan: f64 = -f64::NAN; + /// + /// let f = 7.0; + /// let g = -7.0; + /// + /// assert!(f.is_sign_positive()); + /// assert!(!g.is_sign_positive()); + /// assert!(nan.is_sign_positive()); + /// assert!(!neg_nan.is_sign_positive()); + /// ``` + fn is_sign_positive(self) -> bool; + + /// Returns `true` if `self` is negative, including `-0.0`, + /// `Float::neg_infinity()`, and `-Float::nan()`. + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let nan: f64 = f64::NAN; + /// let neg_nan: f64 = -f64::NAN; + /// + /// let f = 7.0; + /// let g = -7.0; + /// + /// assert!(!f.is_sign_negative()); + /// assert!(g.is_sign_negative()); + /// assert!(!nan.is_sign_negative()); + /// assert!(neg_nan.is_sign_negative()); + /// ``` + fn is_sign_negative(self) -> bool; + + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding + /// error, yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` can be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. + /// + /// ``` + /// use num_traits::Float; + /// + /// let m = 10.0; + /// let x = 4.0; + /// let b = 60.0; + /// + /// // 100.0 + /// let abs_difference = (m.mul_add(x, b) - (m*x + b)).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn mul_add(self, a: Self, b: Self) -> Self; + /// Take the reciprocal (inverse) of a number, `1/x`. + /// + /// ``` + /// use num_traits::Float; + /// + /// let x = 2.0; + /// let abs_difference = (x.recip() - (1.0/x)).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn recip(self) -> Self; + + /// Raise a number to an integer power. + /// + /// Using this function is generally faster than using `powf` + /// + /// ``` + /// use num_traits::Float; + /// + /// let x = 2.0; + /// let abs_difference = (x.powi(2) - x*x).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn powi(self, n: i32) -> Self; + + /// Raise a number to a floating point power. + /// + /// ``` + /// use num_traits::Float; + /// + /// let x = 2.0; + /// let abs_difference = (x.powf(2.0) - x*x).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn powf(self, n: Self) -> Self; + + /// Take the square root of a number. + /// + /// Returns NaN if `self` is a negative number. + /// + /// ``` + /// use num_traits::Float; + /// + /// let positive = 4.0; + /// let negative = -4.0; + /// + /// let abs_difference = (positive.sqrt() - 2.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// assert!(negative.sqrt().is_nan()); + /// ``` + fn sqrt(self) -> Self; + + /// Returns `e^(self)`, (the exponential function). + /// + /// ``` + /// use num_traits::Float; + /// + /// let one = 1.0; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn exp(self) -> Self; + + /// Returns `2^(self)`. + /// + /// ``` + /// use num_traits::Float; + /// + /// let f = 2.0; + /// + /// // 2^2 - 4 == 0 + /// let abs_difference = (f.exp2() - 4.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn exp2(self) -> Self; + + /// Returns the natural logarithm of the number. + /// + /// ``` + /// use num_traits::Float; + /// + /// let one = 1.0; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn ln(self) -> Self; + + /// Returns the logarithm of the number with respect to an arbitrary base. + /// + /// ``` + /// use num_traits::Float; + /// + /// let ten = 10.0; + /// let two = 2.0; + /// + /// // log10(10) - 1 == 0 + /// let abs_difference_10 = (ten.log(10.0) - 1.0).abs(); + /// + /// // log2(2) - 1 == 0 + /// let abs_difference_2 = (two.log(2.0) - 1.0).abs(); + /// + /// assert!(abs_difference_10 < 1e-10); + /// assert!(abs_difference_2 < 1e-10); + /// ``` + fn log(self, base: Self) -> Self; + + /// Returns the base 2 logarithm of the number. + /// + /// ``` + /// use num_traits::Float; + /// + /// let two = 2.0; + /// + /// // log2(2) - 1 == 0 + /// let abs_difference = (two.log2() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn log2(self) -> Self; + + /// Returns the base 10 logarithm of the number. + /// + /// ``` + /// use num_traits::Float; + /// + /// let ten = 10.0; + /// + /// // log10(10) - 1 == 0 + /// let abs_difference = (ten.log10() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn log10(self) -> Self; + + /// Converts radians to degrees. + /// + /// ``` + /// use std::f64::consts; + /// + /// let angle = consts::PI; + /// + /// let abs_difference = (angle.to_degrees() - 180.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[inline] + fn to_degrees(self) -> Self { + let halfpi = Self::zero().acos(); + let ninety = Self::from(90u8).unwrap(); + self * ninety / halfpi + } + + /// Converts degrees to radians. + /// + /// ``` + /// use std::f64::consts; + /// + /// let angle = 180.0_f64; + /// + /// let abs_difference = (angle.to_radians() - consts::PI).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[inline] + fn to_radians(self) -> Self { + let halfpi = Self::zero().acos(); + let ninety = Self::from(90u8).unwrap(); + self * halfpi / ninety + } + + /// Returns the maximum of the two numbers. + /// + /// ``` + /// use num_traits::Float; + /// + /// let x = 1.0; + /// let y = 2.0; + /// + /// assert_eq!(x.max(y), y); + /// ``` + fn max(self, other: Self) -> Self; + + /// Returns the minimum of the two numbers. + /// + /// ``` + /// use num_traits::Float; + /// + /// let x = 1.0; + /// let y = 2.0; + /// + /// assert_eq!(x.min(y), x); + /// ``` + fn min(self, other: Self) -> Self; + + /// Clamps a value between a min and max. + /// + /// **Panics** in debug mode if `!(min <= max)`. + /// + /// ``` + /// use num_traits::Float; + /// + /// let x = 1.0; + /// let y = 2.0; + /// let z = 3.0; + /// + /// assert_eq!(x.clamp(y, z), 2.0); + /// ``` + fn clamp(self, min: Self, max: Self) -> Self { + crate::clamp(self, min, max) + } + + /// The positive difference of two numbers. + /// + /// * If `self <= other`: `0:0` + /// * Else: `self - other` + /// + /// ``` + /// use num_traits::Float; + /// + /// let x = 3.0; + /// let y = -3.0; + /// + /// let abs_difference_x = (x.abs_sub(1.0) - 2.0).abs(); + /// let abs_difference_y = (y.abs_sub(1.0) - 0.0).abs(); + /// + /// assert!(abs_difference_x < 1e-10); + /// assert!(abs_difference_y < 1e-10); + /// ``` + fn abs_sub(self, other: Self) -> Self; + + /// Take the cubic root of a number. + /// + /// ``` + /// use num_traits::Float; + /// + /// let x = 8.0; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (x.cbrt() - 2.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn cbrt(self) -> Self; + + /// Calculate the length of the hypotenuse of a right-angle triangle given + /// legs of length `x` and `y`. + /// + /// ``` + /// use num_traits::Float; + /// + /// let x = 2.0; + /// let y = 3.0; + /// + /// // sqrt(x^2 + y^2) + /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn hypot(self, other: Self) -> Self; + + /// Computes the sine of a number (in radians). + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let x = f64::consts::PI/2.0; + /// + /// let abs_difference = (x.sin() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn sin(self) -> Self; + + /// Computes the cosine of a number (in radians). + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let x = 2.0*f64::consts::PI; + /// + /// let abs_difference = (x.cos() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn cos(self) -> Self; + + /// Computes the tangent of a number (in radians). + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let x = f64::consts::PI/4.0; + /// let abs_difference = (x.tan() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-14); + /// ``` + fn tan(self) -> Self; + + /// Computes the arcsine of a number. Return value is in radians in + /// the range [-pi/2, pi/2] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let f = f64::consts::PI / 2.0; + /// + /// // asin(sin(pi/2)) + /// let abs_difference = (f.sin().asin() - f64::consts::PI / 2.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn asin(self) -> Self; + + /// Computes the arccosine of a number. Return value is in radians in + /// the range [0, pi] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let f = f64::consts::PI / 4.0; + /// + /// // acos(cos(pi/4)) + /// let abs_difference = (f.cos().acos() - f64::consts::PI / 4.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn acos(self) -> Self; + + /// Computes the arctangent of a number. Return value is in radians in the + /// range [-pi/2, pi/2]; + /// + /// ``` + /// use num_traits::Float; + /// + /// let f = 1.0; + /// + /// // atan(tan(1)) + /// let abs_difference = (f.tan().atan() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn atan(self) -> Self; + + /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`). + /// + /// * `x = 0`, `y = 0`: `0` + /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]` + /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` + /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let pi = f64::consts::PI; + /// // All angles from horizontal right (+x) + /// // 45 deg counter-clockwise + /// let x1 = 3.0; + /// let y1 = -3.0; + /// + /// // 135 deg clockwise + /// let x2 = -3.0; + /// let y2 = 3.0; + /// + /// let abs_difference_1 = (y1.atan2(x1) - (-pi/4.0)).abs(); + /// let abs_difference_2 = (y2.atan2(x2) - 3.0*pi/4.0).abs(); + /// + /// assert!(abs_difference_1 < 1e-10); + /// assert!(abs_difference_2 < 1e-10); + /// ``` + fn atan2(self, other: Self) -> Self; + + /// Simultaneously computes the sine and cosine of the number, `x`. Returns + /// `(sin(x), cos(x))`. + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let x = f64::consts::PI/4.0; + /// let f = x.sin_cos(); + /// + /// let abs_difference_0 = (f.0 - x.sin()).abs(); + /// let abs_difference_1 = (f.1 - x.cos()).abs(); + /// + /// assert!(abs_difference_0 < 1e-10); + /// assert!(abs_difference_0 < 1e-10); + /// ``` + fn sin_cos(self) -> (Self, Self); + + /// Returns `e^(self) - 1` in a way that is accurate even if the + /// number is close to zero. + /// + /// ``` + /// use num_traits::Float; + /// + /// let x = 7.0; + /// + /// // e^(ln(7)) - 1 + /// let abs_difference = (x.ln().exp_m1() - 6.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn exp_m1(self) -> Self; + + /// Returns `ln(1+n)` (natural logarithm) more accurately than if + /// the operations were performed separately. + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let x = f64::consts::E - 1.0; + /// + /// // ln(1 + (e - 1)) == ln(e) == 1 + /// let abs_difference = (x.ln_1p() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn ln_1p(self) -> Self; + + /// Hyperbolic sine function. + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let e = f64::consts::E; + /// let x = 1.0; + /// + /// let f = x.sinh(); + /// // Solving sinh() at 1 gives `(e^2-1)/(2e)` + /// let g = (e*e - 1.0)/(2.0*e); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn sinh(self) -> Self; + + /// Hyperbolic cosine function. + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let e = f64::consts::E; + /// let x = 1.0; + /// let f = x.cosh(); + /// // Solving cosh() at 1 gives this result + /// let g = (e*e + 1.0)/(2.0*e); + /// let abs_difference = (f - g).abs(); + /// + /// // Same result + /// assert!(abs_difference < 1.0e-10); + /// ``` + fn cosh(self) -> Self; + + /// Hyperbolic tangent function. + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let e = f64::consts::E; + /// let x = 1.0; + /// + /// let f = x.tanh(); + /// // Solving tanh() at 1 gives `(1 - e^(-2))/(1 + e^(-2))` + /// let g = (1.0 - e.powi(-2))/(1.0 + e.powi(-2)); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference < 1.0e-10); + /// ``` + fn tanh(self) -> Self; + + /// Inverse hyperbolic sine function. + /// + /// ``` + /// use num_traits::Float; + /// + /// let x = 1.0; + /// let f = x.sinh().asinh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference < 1.0e-10); + /// ``` + fn asinh(self) -> Self; + + /// Inverse hyperbolic cosine function. + /// + /// ``` + /// use num_traits::Float; + /// + /// let x = 1.0; + /// let f = x.cosh().acosh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference < 1.0e-10); + /// ``` + fn acosh(self) -> Self; + + /// Inverse hyperbolic tangent function. + /// + /// ``` + /// use num_traits::Float; + /// use std::f64; + /// + /// let e = f64::consts::E; + /// let f = e.tanh().atanh(); + /// + /// let abs_difference = (f - e).abs(); + /// + /// assert!(abs_difference < 1.0e-10); + /// ``` + fn atanh(self) -> Self; + + /// Returns the mantissa, base 2 exponent, and sign as integers, respectively. + /// The original number can be recovered by `sign * mantissa * 2 ^ exponent`. + /// + /// ``` + /// use num_traits::Float; + /// + /// let num = 2.0f32; + /// + /// // (8388608, -22, 1) + /// let (mantissa, exponent, sign) = Float::integer_decode(num); + /// let sign_f = sign as f32; + /// let mantissa_f = mantissa as f32; + /// let exponent_f = num.powf(exponent as f32); + /// + /// // 1 * 8388608 * 2^(-22) == 2 + /// let abs_difference = (sign_f * mantissa_f * exponent_f - num).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn integer_decode(self) -> (u64, i16, i8); + + /// Returns a number composed of the magnitude of `self` and the sign of + /// `sign`. + /// + /// Equal to `self` if the sign of `self` and `sign` are the same, otherwise + /// equal to `-self`. If `self` is a `NAN`, then a `NAN` with the sign of + /// `sign` is returned. + /// + /// # Examples + /// + /// ``` + /// use num_traits::Float; + /// + /// let f = 3.5_f32; + /// + /// assert_eq!(f.copysign(0.42), 3.5_f32); + /// assert_eq!(f.copysign(-0.42), -3.5_f32); + /// assert_eq!((-f).copysign(0.42), 3.5_f32); + /// assert_eq!((-f).copysign(-0.42), -3.5_f32); + /// + /// assert!(f32::nan().copysign(1.0).is_nan()); + /// ``` + fn copysign(self, sign: Self) -> Self { + if self.is_sign_negative() == sign.is_sign_negative() { + self + } else { + self.neg() + } + } +} + +#[cfg(feature = "std")] +macro_rules! float_impl_std { + ($T:ident $decode:ident) => { + impl Float for $T { + constant! { + nan() -> $T::NAN; + infinity() -> $T::INFINITY; + neg_infinity() -> $T::NEG_INFINITY; + neg_zero() -> -0.0; + min_value() -> $T::MIN; + min_positive_value() -> $T::MIN_POSITIVE; + epsilon() -> $T::EPSILON; + max_value() -> $T::MAX; + } + + #[inline] + #[allow(deprecated)] + fn abs_sub(self, other: Self) -> Self { + <$T>::abs_sub(self, other) + } + + #[inline] + fn integer_decode(self) -> (u64, i16, i8) { + $decode(self) + } + + forward! { + Self::is_nan(self) -> bool; + Self::is_infinite(self) -> bool; + Self::is_finite(self) -> bool; + Self::is_normal(self) -> bool; + Self::is_subnormal(self) -> bool; + Self::classify(self) -> FpCategory; + Self::clamp(self, min: Self, max: Self) -> Self; + Self::floor(self) -> Self; + Self::ceil(self) -> Self; + Self::round(self) -> Self; + Self::trunc(self) -> Self; + Self::fract(self) -> Self; + Self::abs(self) -> Self; + Self::signum(self) -> Self; + Self::is_sign_positive(self) -> bool; + Self::is_sign_negative(self) -> bool; + Self::mul_add(self, a: Self, b: Self) -> Self; + Self::recip(self) -> Self; + Self::powi(self, n: i32) -> Self; + Self::powf(self, n: Self) -> Self; + Self::sqrt(self) -> Self; + Self::exp(self) -> Self; + Self::exp2(self) -> Self; + Self::ln(self) -> Self; + Self::log(self, base: Self) -> Self; + Self::log2(self) -> Self; + Self::log10(self) -> Self; + Self::to_degrees(self) -> Self; + Self::to_radians(self) -> Self; + Self::max(self, other: Self) -> Self; + Self::min(self, other: Self) -> Self; + Self::cbrt(self) -> Self; + Self::hypot(self, other: Self) -> Self; + Self::sin(self) -> Self; + Self::cos(self) -> Self; + Self::tan(self) -> Self; + Self::asin(self) -> Self; + Self::acos(self) -> Self; + Self::atan(self) -> Self; + Self::atan2(self, other: Self) -> Self; + Self::sin_cos(self) -> (Self, Self); + Self::exp_m1(self) -> Self; + Self::ln_1p(self) -> Self; + Self::sinh(self) -> Self; + Self::cosh(self) -> Self; + Self::tanh(self) -> Self; + Self::asinh(self) -> Self; + Self::acosh(self) -> Self; + Self::atanh(self) -> Self; + Self::copysign(self, sign: Self) -> Self; + } + } + }; +} + +#[cfg(all(not(feature = "std"), feature = "libm"))] +macro_rules! float_impl_libm { + ($T:ident $decode:ident) => { + constant! { + nan() -> $T::NAN; + infinity() -> $T::INFINITY; + neg_infinity() -> $T::NEG_INFINITY; + neg_zero() -> -0.0; + min_value() -> $T::MIN; + min_positive_value() -> $T::MIN_POSITIVE; + epsilon() -> $T::EPSILON; + max_value() -> $T::MAX; + } + + #[inline] + fn integer_decode(self) -> (u64, i16, i8) { + $decode(self) + } + + #[inline] + fn fract(self) -> Self { + self - Float::trunc(self) + } + + #[inline] + fn log(self, base: Self) -> Self { + self.ln() / base.ln() + } + + forward! { + Self::is_nan(self) -> bool; + Self::is_infinite(self) -> bool; + Self::is_finite(self) -> bool; + Self::is_normal(self) -> bool; + Self::is_subnormal(self) -> bool; + Self::clamp(self, min: Self, max: Self) -> Self; + Self::classify(self) -> FpCategory; + Self::is_sign_positive(self) -> bool; + Self::is_sign_negative(self) -> bool; + Self::min(self, other: Self) -> Self; + Self::max(self, other: Self) -> Self; + Self::recip(self) -> Self; + Self::to_degrees(self) -> Self; + Self::to_radians(self) -> Self; + } + + forward! { + FloatCore::signum(self) -> Self; + FloatCore::powi(self, n: i32) -> Self; + } + }; +} + +fn integer_decode_f32(f: f32) -> (u64, i16, i8) { + let bits: u32 = f.to_bits(); + let sign: i8 = if bits >> 31 == 0 { 1 } else { -1 }; + let mut exponent: i16 = ((bits >> 23) & 0xff) as i16; + let mantissa = if exponent == 0 { + (bits & 0x7fffff) << 1 + } else { + (bits & 0x7fffff) | 0x800000 + }; + // Exponent bias + mantissa shift + exponent -= 127 + 23; + (mantissa as u64, exponent, sign) +} + +fn integer_decode_f64(f: f64) -> (u64, i16, i8) { + let bits: u64 = f.to_bits(); + let sign: i8 = if bits >> 63 == 0 { 1 } else { -1 }; + let mut exponent: i16 = ((bits >> 52) & 0x7ff) as i16; + let mantissa = if exponent == 0 { + (bits & 0xfffffffffffff) << 1 + } else { + (bits & 0xfffffffffffff) | 0x10000000000000 + }; + // Exponent bias + mantissa shift + exponent -= 1023 + 52; + (mantissa, exponent, sign) +} + +#[cfg(feature = "std")] +float_impl_std!(f32 integer_decode_f32); +#[cfg(feature = "std")] +float_impl_std!(f64 integer_decode_f64); + +#[cfg(all(not(feature = "std"), feature = "libm"))] +impl Float for f32 { + float_impl_libm!(f32 integer_decode_f32); + + #[inline] + #[allow(deprecated)] + fn abs_sub(self, other: Self) -> Self { + libm::fdimf(self, other) + } + + forward! { + libm::floorf as floor(self) -> Self; + libm::ceilf as ceil(self) -> Self; + libm::roundf as round(self) -> Self; + libm::truncf as trunc(self) -> Self; + libm::fabsf as abs(self) -> Self; + libm::fmaf as mul_add(self, a: Self, b: Self) -> Self; + libm::powf as powf(self, n: Self) -> Self; + libm::sqrtf as sqrt(self) -> Self; + libm::expf as exp(self) -> Self; + libm::exp2f as exp2(self) -> Self; + libm::logf as ln(self) -> Self; + libm::log2f as log2(self) -> Self; + libm::log10f as log10(self) -> Self; + libm::cbrtf as cbrt(self) -> Self; + libm::hypotf as hypot(self, other: Self) -> Self; + libm::sinf as sin(self) -> Self; + libm::cosf as cos(self) -> Self; + libm::tanf as tan(self) -> Self; + libm::asinf as asin(self) -> Self; + libm::acosf as acos(self) -> Self; + libm::atanf as atan(self) -> Self; + libm::atan2f as atan2(self, other: Self) -> Self; + libm::sincosf as sin_cos(self) -> (Self, Self); + libm::expm1f as exp_m1(self) -> Self; + libm::log1pf as ln_1p(self) -> Self; + libm::sinhf as sinh(self) -> Self; + libm::coshf as cosh(self) -> Self; + libm::tanhf as tanh(self) -> Self; + libm::asinhf as asinh(self) -> Self; + libm::acoshf as acosh(self) -> Self; + libm::atanhf as atanh(self) -> Self; + libm::copysignf as copysign(self, other: Self) -> Self; + } +} + +#[cfg(all(not(feature = "std"), feature = "libm"))] +impl Float for f64 { + float_impl_libm!(f64 integer_decode_f64); + + #[inline] + #[allow(deprecated)] + fn abs_sub(self, other: Self) -> Self { + libm::fdim(self, other) + } + + forward! { + libm::floor as floor(self) -> Self; + libm::ceil as ceil(self) -> Self; + libm::round as round(self) -> Self; + libm::trunc as trunc(self) -> Self; + libm::fabs as abs(self) -> Self; + libm::fma as mul_add(self, a: Self, b: Self) -> Self; + libm::pow as powf(self, n: Self) -> Self; + libm::sqrt as sqrt(self) -> Self; + libm::exp as exp(self) -> Self; + libm::exp2 as exp2(self) -> Self; + libm::log as ln(self) -> Self; + libm::log2 as log2(self) -> Self; + libm::log10 as log10(self) -> Self; + libm::cbrt as cbrt(self) -> Self; + libm::hypot as hypot(self, other: Self) -> Self; + libm::sin as sin(self) -> Self; + libm::cos as cos(self) -> Self; + libm::tan as tan(self) -> Self; + libm::asin as asin(self) -> Self; + libm::acos as acos(self) -> Self; + libm::atan as atan(self) -> Self; + libm::atan2 as atan2(self, other: Self) -> Self; + libm::sincos as sin_cos(self) -> (Self, Self); + libm::expm1 as exp_m1(self) -> Self; + libm::log1p as ln_1p(self) -> Self; + libm::sinh as sinh(self) -> Self; + libm::cosh as cosh(self) -> Self; + libm::tanh as tanh(self) -> Self; + libm::asinh as asinh(self) -> Self; + libm::acosh as acosh(self) -> Self; + libm::atanh as atanh(self) -> Self; + libm::copysign as copysign(self, sign: Self) -> Self; + } +} + +macro_rules! float_const_impl { + ($(#[$doc:meta] $constant:ident,)+) => ( + #[allow(non_snake_case)] + pub trait FloatConst { + $(#[$doc] fn $constant() -> Self;)+ + #[doc = "Return the full circle constant `τ`."] + #[inline] + fn TAU() -> Self where Self: Sized + Add { + Self::PI() + Self::PI() + } + #[doc = "Return `log10(2.0)`."] + #[inline] + fn LOG10_2() -> Self where Self: Sized + Div { + Self::LN_2() / Self::LN_10() + } + #[doc = "Return `log2(10.0)`."] + #[inline] + fn LOG2_10() -> Self where Self: Sized + Div { + Self::LN_10() / Self::LN_2() + } + } + float_const_impl! { @float f32, $($constant,)+ } + float_const_impl! { @float f64, $($constant,)+ } + ); + (@float $T:ident, $($constant:ident,)+) => ( + impl FloatConst for $T { + constant! { + $( $constant() -> $T::consts::$constant; )+ + TAU() -> 6.28318530717958647692528676655900577; + LOG10_2() -> 0.301029995663981195213738894724493027; + LOG2_10() -> 3.32192809488736234787031942948939018; + } + } + ); +} + +float_const_impl! { + #[doc = "Return Euler’s number."] + E, + #[doc = "Return `1.0 / π`."] + FRAC_1_PI, + #[doc = "Return `1.0 / sqrt(2.0)`."] + FRAC_1_SQRT_2, + #[doc = "Return `2.0 / π`."] + FRAC_2_PI, + #[doc = "Return `2.0 / sqrt(π)`."] + FRAC_2_SQRT_PI, + #[doc = "Return `π / 2.0`."] + FRAC_PI_2, + #[doc = "Return `π / 3.0`."] + FRAC_PI_3, + #[doc = "Return `π / 4.0`."] + FRAC_PI_4, + #[doc = "Return `π / 6.0`."] + FRAC_PI_6, + #[doc = "Return `π / 8.0`."] + FRAC_PI_8, + #[doc = "Return `ln(10.0)`."] + LN_10, + #[doc = "Return `ln(2.0)`."] + LN_2, + #[doc = "Return `log10(e)`."] + LOG10_E, + #[doc = "Return `log2(e)`."] + LOG2_E, + #[doc = "Return Archimedes’ constant `π`."] + PI, + #[doc = "Return `sqrt(2.0)`."] + SQRT_2, +} + +/// Trait for floating point numbers that provide an implementation +/// of the `totalOrder` predicate as defined in the IEEE 754 (2008 revision) +/// floating point standard. +pub trait TotalOrder { + /// Return the ordering between `self` and `other`. + /// + /// Unlike the standard partial comparison between floating point numbers, + /// this comparison always produces an ordering in accordance to + /// the `totalOrder` predicate as defined in the IEEE 754 (2008 revision) + /// floating point standard. The values are ordered in the following sequence: + /// + /// - negative quiet NaN + /// - negative signaling NaN + /// - negative infinity + /// - negative numbers + /// - negative subnormal numbers + /// - negative zero + /// - positive zero + /// - positive subnormal numbers + /// - positive numbers + /// - positive infinity + /// - positive signaling NaN + /// - positive quiet NaN. + /// + /// The ordering established by this function does not always agree with the + /// [`PartialOrd`] and [`PartialEq`] implementations. For example, + /// they consider negative and positive zero equal, while `total_cmp` + /// doesn't. + /// + /// The interpretation of the signaling NaN bit follows the definition in + /// the IEEE 754 standard, which may not match the interpretation by some of + /// the older, non-conformant (e.g. MIPS) hardware implementations. + /// + /// # Examples + /// ``` + /// use num_traits::float::TotalOrder; + /// use std::cmp::Ordering; + /// use std::{f32, f64}; + /// + /// fn check_eq(x: T, y: T) { + /// assert_eq!(x.total_cmp(&y), Ordering::Equal); + /// } + /// + /// check_eq(f64::NAN, f64::NAN); + /// check_eq(f32::NAN, f32::NAN); + /// + /// fn check_lt(x: T, y: T) { + /// assert_eq!(x.total_cmp(&y), Ordering::Less); + /// } + /// + /// check_lt(-f64::NAN, f64::NAN); + /// check_lt(f64::INFINITY, f64::NAN); + /// check_lt(-0.0_f64, 0.0_f64); + /// ``` + fn total_cmp(&self, other: &Self) -> Ordering; +} +macro_rules! totalorder_impl { + ($T:ident, $I:ident, $U:ident, $bits:expr) => { + impl TotalOrder for $T { + #[inline] + #[cfg(has_total_cmp)] + fn total_cmp(&self, other: &Self) -> Ordering { + // Forward to the core implementation + Self::total_cmp(&self, other) + } + #[inline] + #[cfg(not(has_total_cmp))] + fn total_cmp(&self, other: &Self) -> Ordering { + // Backport the core implementation (since 1.62) + let mut left = self.to_bits() as $I; + let mut right = other.to_bits() as $I; + + left ^= (((left >> ($bits - 1)) as $U) >> 1) as $I; + right ^= (((right >> ($bits - 1)) as $U) >> 1) as $I; + + left.cmp(&right) + } + } + }; +} +totalorder_impl!(f64, i64, u64, 64); +totalorder_impl!(f32, i32, u32, 32); + +#[cfg(test)] +mod tests { + use core::f64::consts; + + const DEG_RAD_PAIRS: [(f64, f64); 7] = [ + (0.0, 0.), + (22.5, consts::FRAC_PI_8), + (30.0, consts::FRAC_PI_6), + (45.0, consts::FRAC_PI_4), + (60.0, consts::FRAC_PI_3), + (90.0, consts::FRAC_PI_2), + (180.0, consts::PI), + ]; + + #[test] + fn convert_deg_rad() { + use crate::float::FloatCore; + + for &(deg, rad) in &DEG_RAD_PAIRS { + assert!((FloatCore::to_degrees(rad) - deg).abs() < 1e-6); + assert!((FloatCore::to_radians(deg) - rad).abs() < 1e-6); + + let (deg, rad) = (deg as f32, rad as f32); + assert!((FloatCore::to_degrees(rad) - deg).abs() < 1e-5); + assert!((FloatCore::to_radians(deg) - rad).abs() < 1e-5); + } + } + + #[cfg(any(feature = "std", feature = "libm"))] + #[test] + fn convert_deg_rad_std() { + for &(deg, rad) in &DEG_RAD_PAIRS { + use crate::Float; + + assert!((Float::to_degrees(rad) - deg).abs() < 1e-6); + assert!((Float::to_radians(deg) - rad).abs() < 1e-6); + + let (deg, rad) = (deg as f32, rad as f32); + assert!((Float::to_degrees(rad) - deg).abs() < 1e-5); + assert!((Float::to_radians(deg) - rad).abs() < 1e-5); + } + } + + #[test] + fn to_degrees_rounding() { + use crate::float::FloatCore; + + assert_eq!( + FloatCore::to_degrees(1_f32), + 57.2957795130823208767981548141051703 + ); + } + + #[test] + #[cfg(any(feature = "std", feature = "libm"))] + fn extra_logs() { + use crate::float::{Float, FloatConst}; + + fn check(diff: F) { + let _2 = F::from(2.0).unwrap(); + assert!((F::LOG10_2() - F::log10(_2)).abs() < diff); + assert!((F::LOG10_2() - F::LN_2() / F::LN_10()).abs() < diff); + + let _10 = F::from(10.0).unwrap(); + assert!((F::LOG2_10() - F::log2(_10)).abs() < diff); + assert!((F::LOG2_10() - F::LN_10() / F::LN_2()).abs() < diff); + } + + check::(1e-6); + check::(1e-12); + } + + #[test] + #[cfg(any(feature = "std", feature = "libm"))] + fn copysign() { + use crate::float::Float; + test_copysign_generic(2.0_f32, -2.0_f32, f32::nan()); + test_copysign_generic(2.0_f64, -2.0_f64, f64::nan()); + test_copysignf(2.0_f32, -2.0_f32, f32::nan()); + } + + #[cfg(any(feature = "std", feature = "libm"))] + fn test_copysignf(p: f32, n: f32, nan: f32) { + use crate::float::Float; + use core::ops::Neg; + + assert!(p.is_sign_positive()); + assert!(n.is_sign_negative()); + assert!(nan.is_nan()); + + assert_eq!(p, Float::copysign(p, p)); + assert_eq!(p.neg(), Float::copysign(p, n)); + + assert_eq!(n, Float::copysign(n, n)); + assert_eq!(n.neg(), Float::copysign(n, p)); + + assert!(Float::copysign(nan, p).is_sign_positive()); + assert!(Float::copysign(nan, n).is_sign_negative()); + } + + #[cfg(any(feature = "std", feature = "libm"))] + fn test_copysign_generic(p: F, n: F, nan: F) { + assert!(p.is_sign_positive()); + assert!(n.is_sign_negative()); + assert!(nan.is_nan()); + assert!(!nan.is_subnormal()); + + assert_eq!(p, p.copysign(p)); + assert_eq!(p.neg(), p.copysign(n)); + + assert_eq!(n, n.copysign(n)); + assert_eq!(n.neg(), n.copysign(p)); + + assert!(nan.copysign(p).is_sign_positive()); + assert!(nan.copysign(n).is_sign_negative()); + } + + #[cfg(any(feature = "std", feature = "libm"))] + fn test_subnormal() { + let min_positive = F::min_positive_value(); + let lower_than_min = min_positive / F::from(2.0f32).unwrap(); + assert!(!min_positive.is_subnormal()); + assert!(lower_than_min.is_subnormal()); + } + + #[test] + #[cfg(any(feature = "std", feature = "libm"))] + fn subnormal() { + test_subnormal::(); + test_subnormal::(); + } + + #[test] + fn total_cmp() { + use crate::float::TotalOrder; + use core::cmp::Ordering; + use core::{f32, f64}; + + fn check_eq(x: T, y: T) { + assert_eq!(x.total_cmp(&y), Ordering::Equal); + } + fn check_lt(x: T, y: T) { + assert_eq!(x.total_cmp(&y), Ordering::Less); + } + fn check_gt(x: T, y: T) { + assert_eq!(x.total_cmp(&y), Ordering::Greater); + } + + check_eq(f64::NAN, f64::NAN); + check_eq(f32::NAN, f32::NAN); + + check_lt(-0.0_f64, 0.0_f64); + check_lt(-0.0_f32, 0.0_f32); + + // x87 registers don't preserve the exact value of signaling NaN: + // https://github.com/rust-lang/rust/issues/115567 + #[cfg(not(target_arch = "x86"))] + { + let s_nan = f64::from_bits(0x7ff4000000000000); + let q_nan = f64::from_bits(0x7ff8000000000000); + check_lt(s_nan, q_nan); + + let neg_s_nan = f64::from_bits(0xfff4000000000000); + let neg_q_nan = f64::from_bits(0xfff8000000000000); + check_lt(neg_q_nan, neg_s_nan); + + let s_nan = f32::from_bits(0x7fa00000); + let q_nan = f32::from_bits(0x7fc00000); + check_lt(s_nan, q_nan); + + let neg_s_nan = f32::from_bits(0xffa00000); + let neg_q_nan = f32::from_bits(0xffc00000); + check_lt(neg_q_nan, neg_s_nan); + } + + check_lt(-f64::NAN, f64::NEG_INFINITY); + check_gt(1.0_f64, -f64::NAN); + check_lt(f64::INFINITY, f64::NAN); + check_gt(f64::NAN, 1.0_f64); + + check_lt(-f32::NAN, f32::NEG_INFINITY); + check_gt(1.0_f32, -f32::NAN); + check_lt(f32::INFINITY, f32::NAN); + check_gt(f32::NAN, 1.0_f32); + } +} diff --git a/tools/vendor/num-traits/src/identities.rs b/tools/vendor/num-traits/src/identities.rs new file mode 100644 index 0000000000..c30cd1dc0b --- /dev/null +++ b/tools/vendor/num-traits/src/identities.rs @@ -0,0 +1,238 @@ +use core::num::Wrapping; +use core::ops::{Add, Mul}; + +/// Defines an additive identity element for `Self`. +/// +/// # Laws +/// +/// ```text +/// a + 0 = a ∀ a ∈ Self +/// 0 + a = a ∀ a ∈ Self +/// ``` +pub trait Zero: Sized + Add { + /// Returns the additive identity element of `Self`, `0`. + /// # Purity + /// + /// This function should return the same result at all times regardless of + /// external mutable state, for example values stored in TLS or in + /// `static mut`s. + // This cannot be an associated constant, because of bignums. + fn zero() -> Self; + + /// Sets `self` to the additive identity element of `Self`, `0`. + fn set_zero(&mut self) { + *self = Zero::zero(); + } + + /// Returns `true` if `self` is equal to the additive identity. + fn is_zero(&self) -> bool; +} + +/// Defines an associated constant representing the additive identity element +/// for `Self`. +pub trait ConstZero: Zero { + /// The additive identity element of `Self`, `0`. + const ZERO: Self; +} + +macro_rules! zero_impl { + ($t:ty, $v:expr) => { + impl Zero for $t { + #[inline] + fn zero() -> $t { + $v + } + #[inline] + fn is_zero(&self) -> bool { + *self == $v + } + } + + impl ConstZero for $t { + const ZERO: Self = $v; + } + }; +} + +zero_impl!(usize, 0); +zero_impl!(u8, 0); +zero_impl!(u16, 0); +zero_impl!(u32, 0); +zero_impl!(u64, 0); +zero_impl!(u128, 0); + +zero_impl!(isize, 0); +zero_impl!(i8, 0); +zero_impl!(i16, 0); +zero_impl!(i32, 0); +zero_impl!(i64, 0); +zero_impl!(i128, 0); + +zero_impl!(f32, 0.0); +zero_impl!(f64, 0.0); + +impl Zero for Wrapping +where + Wrapping: Add>, +{ + fn is_zero(&self) -> bool { + self.0.is_zero() + } + + fn set_zero(&mut self) { + self.0.set_zero(); + } + + fn zero() -> Self { + Wrapping(T::zero()) + } +} + +impl ConstZero for Wrapping +where + Wrapping: Add>, +{ + const ZERO: Self = Wrapping(T::ZERO); +} + +/// Defines a multiplicative identity element for `Self`. +/// +/// # Laws +/// +/// ```text +/// a * 1 = a ∀ a ∈ Self +/// 1 * a = a ∀ a ∈ Self +/// ``` +pub trait One: Sized + Mul { + /// Returns the multiplicative identity element of `Self`, `1`. + /// + /// # Purity + /// + /// This function should return the same result at all times regardless of + /// external mutable state, for example values stored in TLS or in + /// `static mut`s. + // This cannot be an associated constant, because of bignums. + fn one() -> Self; + + /// Sets `self` to the multiplicative identity element of `Self`, `1`. + fn set_one(&mut self) { + *self = One::one(); + } + + /// Returns `true` if `self` is equal to the multiplicative identity. + /// + /// For performance reasons, it's best to implement this manually. + /// After a semver bump, this method will be required, and the + /// `where Self: PartialEq` bound will be removed. + #[inline] + fn is_one(&self) -> bool + where + Self: PartialEq, + { + *self == Self::one() + } +} + +/// Defines an associated constant representing the multiplicative identity +/// element for `Self`. +pub trait ConstOne: One { + /// The multiplicative identity element of `Self`, `1`. + const ONE: Self; +} + +macro_rules! one_impl { + ($t:ty, $v:expr) => { + impl One for $t { + #[inline] + fn one() -> $t { + $v + } + #[inline] + fn is_one(&self) -> bool { + *self == $v + } + } + + impl ConstOne for $t { + const ONE: Self = $v; + } + }; +} + +one_impl!(usize, 1); +one_impl!(u8, 1); +one_impl!(u16, 1); +one_impl!(u32, 1); +one_impl!(u64, 1); +one_impl!(u128, 1); + +one_impl!(isize, 1); +one_impl!(i8, 1); +one_impl!(i16, 1); +one_impl!(i32, 1); +one_impl!(i64, 1); +one_impl!(i128, 1); + +one_impl!(f32, 1.0); +one_impl!(f64, 1.0); + +impl One for Wrapping +where + Wrapping: Mul>, +{ + fn set_one(&mut self) { + self.0.set_one(); + } + + fn one() -> Self { + Wrapping(T::one()) + } +} + +impl ConstOne for Wrapping +where + Wrapping: Mul>, +{ + const ONE: Self = Wrapping(T::ONE); +} + +// Some helper functions provided for backwards compatibility. + +/// Returns the additive identity, `0`. +#[inline(always)] +pub fn zero() -> T { + Zero::zero() +} + +/// Returns the multiplicative identity, `1`. +#[inline(always)] +pub fn one() -> T { + One::one() +} + +#[test] +fn wrapping_identities() { + macro_rules! test_wrapping_identities { + ($($t:ty)+) => { + $( + assert_eq!(zero::<$t>(), zero::>().0); + assert_eq!(one::<$t>(), one::>().0); + assert_eq!((0 as $t).is_zero(), Wrapping(0 as $t).is_zero()); + assert_eq!((1 as $t).is_zero(), Wrapping(1 as $t).is_zero()); + )+ + }; + } + + test_wrapping_identities!(isize i8 i16 i32 i64 usize u8 u16 u32 u64); +} + +#[test] +fn wrapping_is_zero() { + fn require_zero(_: &T) {} + require_zero(&Wrapping(42)); +} +#[test] +fn wrapping_is_one() { + fn require_one(_: &T) {} + require_one(&Wrapping(42)); +} diff --git a/tools/vendor/num-traits/src/int.rs b/tools/vendor/num-traits/src/int.rs new file mode 100644 index 0000000000..c6284bf4e8 --- /dev/null +++ b/tools/vendor/num-traits/src/int.rs @@ -0,0 +1,562 @@ +use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; + +use crate::bounds::Bounded; +use crate::ops::checked::*; +use crate::ops::saturating::Saturating; +use crate::{Num, NumCast}; + +/// Generic trait for primitive integers. +/// +/// The `PrimInt` trait is an abstraction over the builtin primitive integer types (e.g., `u8`, +/// `u32`, `isize`, `i128`, ...). It inherits the basic numeric traits and extends them with +/// bitwise operators and non-wrapping arithmetic. +/// +/// The trait explicitly inherits `Copy`, `Eq`, `Ord`, and `Sized`. The intention is that all +/// types implementing this trait behave like primitive types that are passed by value by default +/// and behave like builtin integers. Furthermore, the types are expected to expose the integer +/// value in binary representation and support bitwise operators. The standard bitwise operations +/// (e.g., bitwise-and, bitwise-or, right-shift, left-shift) are inherited and the trait extends +/// these with introspective queries (e.g., `PrimInt::count_ones()`, `PrimInt::leading_zeros()`), +/// bitwise combinators (e.g., `PrimInt::rotate_left()`), and endianness converters (e.g., +/// `PrimInt::to_be()`). +/// +/// All `PrimInt` types are expected to be fixed-width binary integers. The width can be queried +/// via `T::zero().count_zeros()`. The trait currently lacks a way to query the width at +/// compile-time. +/// +/// While a default implementation for all builtin primitive integers is provided, the trait is in +/// no way restricted to these. Other integer types that fulfil the requirements are free to +/// implement the trait was well. +/// +/// This trait and many of the method names originate in the unstable `core::num::Int` trait from +/// the rust standard library. The original trait was never stabilized and thus removed from the +/// standard library. +pub trait PrimInt: + Sized + + Copy + + Num + + NumCast + + Bounded + + PartialOrd + + Ord + + Eq + + Not + + BitAnd + + BitOr + + BitXor + + Shl + + Shr + + CheckedAdd + + CheckedSub + + CheckedMul + + CheckedDiv + + Saturating +{ + /// Returns the number of ones in the binary representation of `self`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::PrimInt; + /// + /// let n = 0b01001100u8; + /// + /// assert_eq!(n.count_ones(), 3); + /// ``` + fn count_ones(self) -> u32; + + /// Returns the number of zeros in the binary representation of `self`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::PrimInt; + /// + /// let n = 0b01001100u8; + /// + /// assert_eq!(n.count_zeros(), 5); + /// ``` + fn count_zeros(self) -> u32; + + /// Returns the number of leading ones in the binary representation + /// of `self`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::PrimInt; + /// + /// let n = 0xF00Du16; + /// + /// assert_eq!(n.leading_ones(), 4); + /// ``` + fn leading_ones(self) -> u32 { + (!self).leading_zeros() + } + + /// Returns the number of leading zeros in the binary representation + /// of `self`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::PrimInt; + /// + /// let n = 0b0101000u16; + /// + /// assert_eq!(n.leading_zeros(), 10); + /// ``` + fn leading_zeros(self) -> u32; + + /// Returns the number of trailing ones in the binary representation + /// of `self`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::PrimInt; + /// + /// let n = 0xBEEFu16; + /// + /// assert_eq!(n.trailing_ones(), 4); + /// ``` + fn trailing_ones(self) -> u32 { + (!self).trailing_zeros() + } + + /// Returns the number of trailing zeros in the binary representation + /// of `self`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::PrimInt; + /// + /// let n = 0b0101000u16; + /// + /// assert_eq!(n.trailing_zeros(), 3); + /// ``` + fn trailing_zeros(self) -> u32; + + /// Shifts the bits to the left by a specified amount, `n`, wrapping + /// the truncated bits to the end of the resulting integer. + /// + /// # Examples + /// + /// ``` + /// use num_traits::PrimInt; + /// + /// let n = 0x0123456789ABCDEFu64; + /// let m = 0x3456789ABCDEF012u64; + /// + /// assert_eq!(n.rotate_left(12), m); + /// ``` + fn rotate_left(self, n: u32) -> Self; + + /// Shifts the bits to the right by a specified amount, `n`, wrapping + /// the truncated bits to the beginning of the resulting integer. + /// + /// # Examples + /// + /// ``` + /// use num_traits::PrimInt; + /// + /// let n = 0x0123456789ABCDEFu64; + /// let m = 0xDEF0123456789ABCu64; + /// + /// assert_eq!(n.rotate_right(12), m); + /// ``` + fn rotate_right(self, n: u32) -> Self; + + /// Shifts the bits to the left by a specified amount, `n`, filling + /// zeros in the least significant bits. + /// + /// This is bitwise equivalent to signed `Shl`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::PrimInt; + /// + /// let n = 0x0123456789ABCDEFu64; + /// let m = 0x3456789ABCDEF000u64; + /// + /// assert_eq!(n.signed_shl(12), m); + /// ``` + fn signed_shl(self, n: u32) -> Self; + + /// Shifts the bits to the right by a specified amount, `n`, copying + /// the "sign bit" in the most significant bits even for unsigned types. + /// + /// This is bitwise equivalent to signed `Shr`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::PrimInt; + /// + /// let n = 0xFEDCBA9876543210u64; + /// let m = 0xFFFFEDCBA9876543u64; + /// + /// assert_eq!(n.signed_shr(12), m); + /// ``` + fn signed_shr(self, n: u32) -> Self; + + /// Shifts the bits to the left by a specified amount, `n`, filling + /// zeros in the least significant bits. + /// + /// This is bitwise equivalent to unsigned `Shl`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::PrimInt; + /// + /// let n = 0x0123456789ABCDEFi64; + /// let m = 0x3456789ABCDEF000i64; + /// + /// assert_eq!(n.unsigned_shl(12), m); + /// ``` + fn unsigned_shl(self, n: u32) -> Self; + + /// Shifts the bits to the right by a specified amount, `n`, filling + /// zeros in the most significant bits. + /// + /// This is bitwise equivalent to unsigned `Shr`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::PrimInt; + /// + /// let n = -8i8; // 0b11111000 + /// let m = 62i8; // 0b00111110 + /// + /// assert_eq!(n.unsigned_shr(2), m); + /// ``` + fn unsigned_shr(self, n: u32) -> Self; + + /// Reverses the byte order of the integer. + /// + /// # Examples + /// + /// ``` + /// use num_traits::PrimInt; + /// + /// let n = 0x0123456789ABCDEFu64; + /// let m = 0xEFCDAB8967452301u64; + /// + /// assert_eq!(n.swap_bytes(), m); + /// ``` + fn swap_bytes(self) -> Self; + + /// Reverses the order of bits in the integer. + /// + /// The least significant bit becomes the most significant bit, second least-significant bit + /// becomes second most-significant bit, etc. + /// + /// # Examples + /// + /// ``` + /// use num_traits::PrimInt; + /// + /// let n = 0x12345678u32; + /// let m = 0x1e6a2c48u32; + /// + /// assert_eq!(n.reverse_bits(), m); + /// assert_eq!(0u32.reverse_bits(), 0); + /// ``` + fn reverse_bits(self) -> Self { + reverse_bits_fallback(self) + } + + /// Convert an integer from big endian to the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are swapped. + /// + /// # Examples + /// + /// ``` + /// use num_traits::PrimInt; + /// + /// let n = 0x0123456789ABCDEFu64; + /// + /// if cfg!(target_endian = "big") { + /// assert_eq!(u64::from_be(n), n) + /// } else { + /// assert_eq!(u64::from_be(n), n.swap_bytes()) + /// } + /// ``` + fn from_be(x: Self) -> Self; + + /// Convert an integer from little endian to the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are swapped. + /// + /// # Examples + /// + /// ``` + /// use num_traits::PrimInt; + /// + /// let n = 0x0123456789ABCDEFu64; + /// + /// if cfg!(target_endian = "little") { + /// assert_eq!(u64::from_le(n), n) + /// } else { + /// assert_eq!(u64::from_le(n), n.swap_bytes()) + /// } + /// ``` + fn from_le(x: Self) -> Self; + + /// Convert `self` to big endian from the target's endianness. + /// + /// On big endian this is a no-op. On little endian the bytes are swapped. + /// + /// # Examples + /// + /// ``` + /// use num_traits::PrimInt; + /// + /// let n = 0x0123456789ABCDEFu64; + /// + /// if cfg!(target_endian = "big") { + /// assert_eq!(n.to_be(), n) + /// } else { + /// assert_eq!(n.to_be(), n.swap_bytes()) + /// } + /// ``` + fn to_be(self) -> Self; + + /// Convert `self` to little endian from the target's endianness. + /// + /// On little endian this is a no-op. On big endian the bytes are swapped. + /// + /// # Examples + /// + /// ``` + /// use num_traits::PrimInt; + /// + /// let n = 0x0123456789ABCDEFu64; + /// + /// if cfg!(target_endian = "little") { + /// assert_eq!(n.to_le(), n) + /// } else { + /// assert_eq!(n.to_le(), n.swap_bytes()) + /// } + /// ``` + fn to_le(self) -> Self; + + /// Raises self to the power of `exp`, using exponentiation by squaring. + /// + /// # Examples + /// + /// ``` + /// use num_traits::PrimInt; + /// + /// assert_eq!(2i32.pow(4), 16); + /// ``` + fn pow(self, exp: u32) -> Self; +} + +fn one_per_byte() -> P { + // i8, u8: return 0x01 + // i16, u16: return 0x0101 = (0x01 << 8) | 0x01 + // i32, u32: return 0x01010101 = (0x0101 << 16) | 0x0101 + // ... + let mut ret = P::one(); + let mut shift = 8; + let mut b = ret.count_zeros() >> 3; + while b != 0 { + ret = (ret << shift) | ret; + shift <<= 1; + b >>= 1; + } + ret +} + +fn reverse_bits_fallback(i: P) -> P { + let rep_01: P = one_per_byte(); + let rep_03 = (rep_01 << 1) | rep_01; + let rep_05 = (rep_01 << 2) | rep_01; + let rep_0f = (rep_03 << 2) | rep_03; + let rep_33 = (rep_03 << 4) | rep_03; + let rep_55 = (rep_05 << 4) | rep_05; + + // code above only used to determine rep_0f, rep_33, rep_55; + // optimizer should be able to do it in compile time + let mut ret = i.swap_bytes(); + ret = ((ret & rep_0f) << 4) | ((ret >> 4) & rep_0f); + ret = ((ret & rep_33) << 2) | ((ret >> 2) & rep_33); + ret = ((ret & rep_55) << 1) | ((ret >> 1) & rep_55); + ret +} + +macro_rules! prim_int_impl { + ($T:ty, $S:ty, $U:ty) => { + impl PrimInt for $T { + #[inline] + fn count_ones(self) -> u32 { + <$T>::count_ones(self) + } + + #[inline] + fn count_zeros(self) -> u32 { + <$T>::count_zeros(self) + } + + #[inline] + fn leading_ones(self) -> u32 { + <$T>::leading_ones(self) + } + + #[inline] + fn leading_zeros(self) -> u32 { + <$T>::leading_zeros(self) + } + + #[inline] + fn trailing_ones(self) -> u32 { + <$T>::trailing_ones(self) + } + + #[inline] + fn trailing_zeros(self) -> u32 { + <$T>::trailing_zeros(self) + } + + #[inline] + fn rotate_left(self, n: u32) -> Self { + <$T>::rotate_left(self, n) + } + + #[inline] + fn rotate_right(self, n: u32) -> Self { + <$T>::rotate_right(self, n) + } + + #[inline] + fn signed_shl(self, n: u32) -> Self { + ((self as $S) << n) as $T + } + + #[inline] + fn signed_shr(self, n: u32) -> Self { + ((self as $S) >> n) as $T + } + + #[inline] + fn unsigned_shl(self, n: u32) -> Self { + ((self as $U) << n) as $T + } + + #[inline] + fn unsigned_shr(self, n: u32) -> Self { + ((self as $U) >> n) as $T + } + + #[inline] + fn swap_bytes(self) -> Self { + <$T>::swap_bytes(self) + } + + #[inline] + fn reverse_bits(self) -> Self { + <$T>::reverse_bits(self) + } + + #[inline] + fn from_be(x: Self) -> Self { + <$T>::from_be(x) + } + + #[inline] + fn from_le(x: Self) -> Self { + <$T>::from_le(x) + } + + #[inline] + fn to_be(self) -> Self { + <$T>::to_be(self) + } + + #[inline] + fn to_le(self) -> Self { + <$T>::to_le(self) + } + + #[inline] + fn pow(self, exp: u32) -> Self { + <$T>::pow(self, exp) + } + } + }; +} + +// prim_int_impl!(type, signed, unsigned); +prim_int_impl!(u8, i8, u8); +prim_int_impl!(u16, i16, u16); +prim_int_impl!(u32, i32, u32); +prim_int_impl!(u64, i64, u64); +prim_int_impl!(u128, i128, u128); +prim_int_impl!(usize, isize, usize); +prim_int_impl!(i8, i8, u8); +prim_int_impl!(i16, i16, u16); +prim_int_impl!(i32, i32, u32); +prim_int_impl!(i64, i64, u64); +prim_int_impl!(i128, i128, u128); +prim_int_impl!(isize, isize, usize); + +#[cfg(test)] +mod tests { + use crate::int::PrimInt; + + #[test] + pub fn reverse_bits() { + use core::{i16, i32, i64, i8}; + + assert_eq!( + PrimInt::reverse_bits(0x0123_4567_89ab_cdefu64), + 0xf7b3_d591_e6a2_c480 + ); + + assert_eq!(PrimInt::reverse_bits(0i8), 0); + assert_eq!(PrimInt::reverse_bits(-1i8), -1); + assert_eq!(PrimInt::reverse_bits(1i8), i8::MIN); + assert_eq!(PrimInt::reverse_bits(i8::MIN), 1); + assert_eq!(PrimInt::reverse_bits(-2i8), i8::MAX); + assert_eq!(PrimInt::reverse_bits(i8::MAX), -2); + + assert_eq!(PrimInt::reverse_bits(0i16), 0); + assert_eq!(PrimInt::reverse_bits(-1i16), -1); + assert_eq!(PrimInt::reverse_bits(1i16), i16::MIN); + assert_eq!(PrimInt::reverse_bits(i16::MIN), 1); + assert_eq!(PrimInt::reverse_bits(-2i16), i16::MAX); + assert_eq!(PrimInt::reverse_bits(i16::MAX), -2); + + assert_eq!(PrimInt::reverse_bits(0i32), 0); + assert_eq!(PrimInt::reverse_bits(-1i32), -1); + assert_eq!(PrimInt::reverse_bits(1i32), i32::MIN); + assert_eq!(PrimInt::reverse_bits(i32::MIN), 1); + assert_eq!(PrimInt::reverse_bits(-2i32), i32::MAX); + assert_eq!(PrimInt::reverse_bits(i32::MAX), -2); + + assert_eq!(PrimInt::reverse_bits(0i64), 0); + assert_eq!(PrimInt::reverse_bits(-1i64), -1); + assert_eq!(PrimInt::reverse_bits(1i64), i64::MIN); + assert_eq!(PrimInt::reverse_bits(i64::MIN), 1); + assert_eq!(PrimInt::reverse_bits(-2i64), i64::MAX); + assert_eq!(PrimInt::reverse_bits(i64::MAX), -2); + } + + #[test] + pub fn reverse_bits_i128() { + use core::i128; + + assert_eq!(PrimInt::reverse_bits(0i128), 0); + assert_eq!(PrimInt::reverse_bits(-1i128), -1); + assert_eq!(PrimInt::reverse_bits(1i128), i128::MIN); + assert_eq!(PrimInt::reverse_bits(i128::MIN), 1); + assert_eq!(PrimInt::reverse_bits(-2i128), i128::MAX); + assert_eq!(PrimInt::reverse_bits(i128::MAX), -2); + } +} diff --git a/tools/vendor/num-traits/src/lib.rs b/tools/vendor/num-traits/src/lib.rs new file mode 100644 index 0000000000..d392e920d8 --- /dev/null +++ b/tools/vendor/num-traits/src/lib.rs @@ -0,0 +1,635 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Numeric traits for generic mathematics +//! +//! ## Compatibility +//! +//! The `num-traits` crate is tested for rustc 1.60 and greater. + +#![doc(html_root_url = "https://docs.rs/num-traits/0.2")] +#![deny(unconditional_recursion)] +#![no_std] + +// Need to explicitly bring the crate in for inherent float methods +#[cfg(feature = "std")] +extern crate std; + +use core::fmt; +use core::num::Wrapping; +use core::ops::{Add, Div, Mul, Rem, Sub}; +use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign}; + +pub use crate::bounds::Bounded; +#[cfg(any(feature = "std", feature = "libm"))] +pub use crate::float::Float; +pub use crate::float::FloatConst; +// pub use real::{FloatCore, Real}; // NOTE: Don't do this, it breaks `use num_traits::*;`. +pub use crate::cast::{cast, AsPrimitive, FromPrimitive, NumCast, ToPrimitive}; +pub use crate::identities::{one, zero, ConstOne, ConstZero, One, Zero}; +pub use crate::int::PrimInt; +pub use crate::ops::bytes::{FromBytes, ToBytes}; +pub use crate::ops::checked::{ + CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr, CheckedSub, +}; +pub use crate::ops::euclid::{CheckedEuclid, Euclid}; +pub use crate::ops::inv::Inv; +pub use crate::ops::mul_add::{MulAdd, MulAddAssign}; +pub use crate::ops::saturating::{Saturating, SaturatingAdd, SaturatingMul, SaturatingSub}; +pub use crate::ops::wrapping::{ + WrappingAdd, WrappingMul, WrappingNeg, WrappingShl, WrappingShr, WrappingSub, +}; +pub use crate::pow::{checked_pow, pow, Pow}; +pub use crate::sign::{abs, abs_sub, signum, Signed, Unsigned}; + +#[macro_use] +mod macros; + +pub mod bounds; +pub mod cast; +pub mod float; +pub mod identities; +pub mod int; +pub mod ops; +pub mod pow; +pub mod real; +pub mod sign; + +/// The base trait for numeric types, covering `0` and `1` values, +/// comparisons, basic numeric operations, and string conversion. +pub trait Num: PartialEq + Zero + One + NumOps { + type FromStrRadixErr; + + /// Convert from a string and radix (typically `2..=36`). + /// + /// # Examples + /// + /// ```rust + /// use num_traits::Num; + /// + /// let result = ::from_str_radix("27", 10); + /// assert_eq!(result, Ok(27)); + /// + /// let result = ::from_str_radix("foo", 10); + /// assert!(result.is_err()); + /// ``` + /// + /// # Supported radices + /// + /// The exact range of supported radices is at the discretion of each type implementation. For + /// primitive integers, this is implemented by the inherent `from_str_radix` methods in the + /// standard library, which **panic** if the radix is not in the range from 2 to 36. The + /// implementation in this crate for primitive floats is similar. + /// + /// For third-party types, it is suggested that implementations should follow suit and at least + /// accept `2..=36` without panicking, but an `Err` may be returned for any unsupported radix. + /// It's possible that a type might not even support the common radix 10, nor any, if string + /// parsing doesn't make sense for that type. + fn from_str_radix(str: &str, radix: u32) -> Result; +} + +/// Generic trait for types implementing basic numeric operations +/// +/// This is automatically implemented for types which implement the operators. +pub trait NumOps: + Add + + Sub + + Mul + + Div + + Rem +{ +} + +impl NumOps for T where + T: Add + + Sub + + Mul + + Div + + Rem +{ +} + +/// The trait for `Num` types which also implement numeric operations taking +/// the second operand by reference. +/// +/// This is automatically implemented for types which implement the operators. +pub trait NumRef: Num + for<'r> NumOps<&'r Self> {} +impl NumRef for T where T: Num + for<'r> NumOps<&'r T> {} + +/// The trait for `Num` references which implement numeric operations, taking the +/// second operand either by value or by reference. +/// +/// This is automatically implemented for all types which implement the operators. It covers +/// every type implementing the operations though, regardless of it being a reference or +/// related to `Num`. +pub trait RefNum: NumOps + for<'r> NumOps<&'r Base, Base> {} +impl RefNum for T where T: NumOps + for<'r> NumOps<&'r Base, Base> {} + +/// Generic trait for types implementing numeric assignment operators (like `+=`). +/// +/// This is automatically implemented for types which implement the operators. +pub trait NumAssignOps: + AddAssign + SubAssign + MulAssign + DivAssign + RemAssign +{ +} + +impl NumAssignOps for T where + T: AddAssign + SubAssign + MulAssign + DivAssign + RemAssign +{ +} + +/// The trait for `Num` types which also implement assignment operators. +/// +/// This is automatically implemented for types which implement the operators. +pub trait NumAssign: Num + NumAssignOps {} +impl NumAssign for T where T: Num + NumAssignOps {} + +/// The trait for `NumAssign` types which also implement assignment operations +/// taking the second operand by reference. +/// +/// This is automatically implemented for types which implement the operators. +pub trait NumAssignRef: NumAssign + for<'r> NumAssignOps<&'r Self> {} +impl NumAssignRef for T where T: NumAssign + for<'r> NumAssignOps<&'r T> {} + +macro_rules! int_trait_impl { + ($name:ident for $($t:ty)*) => ($( + impl $name for $t { + type FromStrRadixErr = ::core::num::ParseIntError; + #[inline] + fn from_str_radix(s: &str, radix: u32) + -> Result + { + <$t>::from_str_radix(s, radix) + } + } + )*) +} +int_trait_impl!(Num for usize u8 u16 u32 u64 u128); +int_trait_impl!(Num for isize i8 i16 i32 i64 i128); + +impl Num for Wrapping +where + Wrapping: NumOps, +{ + type FromStrRadixErr = T::FromStrRadixErr; + fn from_str_radix(str: &str, radix: u32) -> Result { + T::from_str_radix(str, radix).map(Wrapping) + } +} + +#[derive(Debug)] +pub enum FloatErrorKind { + Empty, + Invalid, +} +// FIXME: core::num::ParseFloatError is stable in 1.0, but opaque to us, +// so there's not really any way for us to reuse it. +#[derive(Debug)] +pub struct ParseFloatError { + pub kind: FloatErrorKind, +} + +impl fmt::Display for ParseFloatError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let description = match self.kind { + FloatErrorKind::Empty => "cannot parse float from empty string", + FloatErrorKind::Invalid => "invalid float literal", + }; + + description.fmt(f) + } +} + +fn str_to_ascii_lower_eq_str(a: &str, b: &str) -> bool { + a.len() == b.len() + && a.bytes().zip(b.bytes()).all(|(a, b)| { + let a_to_ascii_lower = a | (((b'A' <= a && a <= b'Z') as u8) << 5); + a_to_ascii_lower == b + }) +} + +// FIXME: The standard library from_str_radix on floats was deprecated, so we're stuck +// with this implementation ourselves until we want to make a breaking change. +// (would have to drop it from `Num` though) +macro_rules! float_trait_impl { + ($name:ident for $($t:ident)*) => ($( + impl $name for $t { + type FromStrRadixErr = ParseFloatError; + + fn from_str_radix(src: &str, radix: u32) + -> Result + { + use self::FloatErrorKind::*; + use self::ParseFloatError as PFE; + + // Special case radix 10 to use more accurate standard library implementation + if radix == 10 { + return src.parse().map_err(|_| PFE { + kind: if src.is_empty() { Empty } else { Invalid }, + }); + } + + // Special values + if str_to_ascii_lower_eq_str(src, "inf") + || str_to_ascii_lower_eq_str(src, "infinity") + { + return Ok(core::$t::INFINITY); + } else if str_to_ascii_lower_eq_str(src, "-inf") + || str_to_ascii_lower_eq_str(src, "-infinity") + { + return Ok(core::$t::NEG_INFINITY); + } else if str_to_ascii_lower_eq_str(src, "nan") { + return Ok(core::$t::NAN); + } else if str_to_ascii_lower_eq_str(src, "-nan") { + return Ok(-core::$t::NAN); + } + + fn slice_shift_char(src: &str) -> Option<(char, &str)> { + let mut chars = src.chars(); + Some((chars.next()?, chars.as_str())) + } + + let (is_positive, src) = match slice_shift_char(src) { + None => return Err(PFE { kind: Empty }), + Some(('-', "")) => return Err(PFE { kind: Empty }), + Some(('-', src)) => (false, src), + Some((_, _)) => (true, src), + }; + + // The significand to accumulate + let mut sig = if is_positive { 0.0 } else { -0.0 }; + // Necessary to detect overflow + let mut prev_sig = sig; + let mut cs = src.chars().enumerate(); + // Exponent prefix and exponent index offset + let mut exp_info = None::<(char, usize)>; + + // Parse the integer part of the significand + for (i, c) in cs.by_ref() { + match c.to_digit(radix) { + Some(digit) => { + // shift significand one digit left + sig *= radix as $t; + + // add/subtract current digit depending on sign + if is_positive { + sig += (digit as isize) as $t; + } else { + sig -= (digit as isize) as $t; + } + + // Detect overflow by comparing to last value, except + // if we've not seen any non-zero digits. + if prev_sig != 0.0 { + if is_positive && sig <= prev_sig + { return Ok(core::$t::INFINITY); } + if !is_positive && sig >= prev_sig + { return Ok(core::$t::NEG_INFINITY); } + + // Detect overflow by reversing the shift-and-add process + if is_positive && (prev_sig != (sig - digit as $t) / radix as $t) + { return Ok(core::$t::INFINITY); } + if !is_positive && (prev_sig != (sig + digit as $t) / radix as $t) + { return Ok(core::$t::NEG_INFINITY); } + } + prev_sig = sig; + }, + None => match c { + 'e' | 'E' | 'p' | 'P' => { + exp_info = Some((c, i + 1)); + break; // start of exponent + }, + '.' => { + break; // start of fractional part + }, + _ => { + return Err(PFE { kind: Invalid }); + }, + }, + } + } + + // If we are not yet at the exponent parse the fractional + // part of the significand + if exp_info.is_none() { + let mut power = 1.0; + for (i, c) in cs.by_ref() { + match c.to_digit(radix) { + Some(digit) => { + // Decrease power one order of magnitude + power /= radix as $t; + // add/subtract current digit depending on sign + sig = if is_positive { + sig + (digit as $t) * power + } else { + sig - (digit as $t) * power + }; + // Detect overflow by comparing to last value + if is_positive && sig < prev_sig + { return Ok(core::$t::INFINITY); } + if !is_positive && sig > prev_sig + { return Ok(core::$t::NEG_INFINITY); } + prev_sig = sig; + }, + None => match c { + 'e' | 'E' | 'p' | 'P' => { + exp_info = Some((c, i + 1)); + break; // start of exponent + }, + _ => { + return Err(PFE { kind: Invalid }); + }, + }, + } + } + } + + // Parse and calculate the exponent + let exp = match exp_info { + Some((c, offset)) => { + let base = match c { + 'E' | 'e' if radix == 10 => 10.0, + 'P' | 'p' if radix == 16 => 2.0, + _ => return Err(PFE { kind: Invalid }), + }; + + // Parse the exponent as decimal integer + let src = &src[offset..]; + let (is_positive, exp) = match slice_shift_char(src) { + Some(('-', src)) => (false, src.parse::()), + Some(('+', src)) => (true, src.parse::()), + Some((_, _)) => (true, src.parse::()), + None => return Err(PFE { kind: Invalid }), + }; + + #[cfg(feature = "std")] + fn pow(base: $t, exp: usize) -> $t { + Float::powi(base, exp as i32) + } + // otherwise uses the generic `pow` from the root + + match (is_positive, exp) { + (true, Ok(exp)) => pow(base, exp), + (false, Ok(exp)) => 1.0 / pow(base, exp), + (_, Err(_)) => return Err(PFE { kind: Invalid }), + } + }, + None => 1.0, // no exponent + }; + + Ok(sig * exp) + } + } + )*) +} +float_trait_impl!(Num for f32 f64); + +/// A value bounded by a minimum and a maximum +/// +/// If input is less than min then this returns min. +/// If input is greater than max then this returns max. +/// Otherwise this returns input. +/// +/// **Panics** in debug mode if `!(min <= max)`. +#[inline] +pub fn clamp(input: T, min: T, max: T) -> T { + debug_assert!(min <= max, "min must be less than or equal to max"); + if input < min { + min + } else if input > max { + max + } else { + input + } +} + +/// A value bounded by a minimum value +/// +/// If input is less than min then this returns min. +/// Otherwise this returns input. +/// `clamp_min(std::f32::NAN, 1.0)` preserves `NAN` different from `f32::min(std::f32::NAN, 1.0)`. +/// +/// **Panics** in debug mode if `!(min == min)`. (This occurs if `min` is `NAN`.) +#[inline] +#[allow(clippy::eq_op)] +pub fn clamp_min(input: T, min: T) -> T { + debug_assert!(min == min, "min must not be NAN"); + if input < min { + min + } else { + input + } +} + +/// A value bounded by a maximum value +/// +/// If input is greater than max then this returns max. +/// Otherwise this returns input. +/// `clamp_max(std::f32::NAN, 1.0)` preserves `NAN` different from `f32::max(std::f32::NAN, 1.0)`. +/// +/// **Panics** in debug mode if `!(max == max)`. (This occurs if `max` is `NAN`.) +#[inline] +#[allow(clippy::eq_op)] +pub fn clamp_max(input: T, max: T) -> T { + debug_assert!(max == max, "max must not be NAN"); + if input > max { + max + } else { + input + } +} + +#[test] +fn clamp_test() { + // Int test + assert_eq!(1, clamp(1, -1, 2)); + assert_eq!(-1, clamp(-2, -1, 2)); + assert_eq!(2, clamp(3, -1, 2)); + assert_eq!(1, clamp_min(1, -1)); + assert_eq!(-1, clamp_min(-2, -1)); + assert_eq!(-1, clamp_max(1, -1)); + assert_eq!(-2, clamp_max(-2, -1)); + + // Float test + assert_eq!(1.0, clamp(1.0, -1.0, 2.0)); + assert_eq!(-1.0, clamp(-2.0, -1.0, 2.0)); + assert_eq!(2.0, clamp(3.0, -1.0, 2.0)); + assert_eq!(1.0, clamp_min(1.0, -1.0)); + assert_eq!(-1.0, clamp_min(-2.0, -1.0)); + assert_eq!(-1.0, clamp_max(1.0, -1.0)); + assert_eq!(-2.0, clamp_max(-2.0, -1.0)); + assert!(clamp(::core::f32::NAN, -1.0, 1.0).is_nan()); + assert!(clamp_min(::core::f32::NAN, 1.0).is_nan()); + assert!(clamp_max(::core::f32::NAN, 1.0).is_nan()); +} + +#[test] +#[should_panic] +#[cfg(debug_assertions)] +fn clamp_nan_min() { + clamp(0., ::core::f32::NAN, 1.); +} + +#[test] +#[should_panic] +#[cfg(debug_assertions)] +fn clamp_nan_max() { + clamp(0., -1., ::core::f32::NAN); +} + +#[test] +#[should_panic] +#[cfg(debug_assertions)] +fn clamp_nan_min_max() { + clamp(0., ::core::f32::NAN, ::core::f32::NAN); +} + +#[test] +#[should_panic] +#[cfg(debug_assertions)] +fn clamp_min_nan_min() { + clamp_min(0., ::core::f32::NAN); +} + +#[test] +#[should_panic] +#[cfg(debug_assertions)] +fn clamp_max_nan_max() { + clamp_max(0., ::core::f32::NAN); +} + +#[test] +fn from_str_radix_unwrap() { + // The Result error must impl Debug to allow unwrap() + + let i: i32 = Num::from_str_radix("0", 10).unwrap(); + assert_eq!(i, 0); + + let f: f32 = Num::from_str_radix("0.0", 10).unwrap(); + assert_eq!(f, 0.0); +} + +#[test] +fn from_str_radix_multi_byte_fail() { + // Ensure parsing doesn't panic, even on invalid sign characters + assert!(f32::from_str_radix("™0.2", 10).is_err()); + + // Even when parsing the exponent sign + assert!(f32::from_str_radix("0.2E™1", 10).is_err()); +} + +#[test] +fn from_str_radix_ignore_case() { + assert_eq!( + f32::from_str_radix("InF", 16).unwrap(), + ::core::f32::INFINITY + ); + assert_eq!( + f32::from_str_radix("InfinitY", 16).unwrap(), + ::core::f32::INFINITY + ); + assert_eq!( + f32::from_str_radix("-InF", 8).unwrap(), + ::core::f32::NEG_INFINITY + ); + assert_eq!( + f32::from_str_radix("-InfinitY", 8).unwrap(), + ::core::f32::NEG_INFINITY + ); + assert!(f32::from_str_radix("nAn", 4).unwrap().is_nan()); + assert!(f32::from_str_radix("-nAn", 4).unwrap().is_nan()); +} + +#[test] +fn wrapping_is_num() { + fn require_num(_: &T) {} + require_num(&Wrapping(42_u32)); + require_num(&Wrapping(-42)); +} + +#[test] +fn wrapping_from_str_radix() { + macro_rules! test_wrapping_from_str_radix { + ($($t:ty)+) => { + $( + for &(s, r) in &[("42", 10), ("42", 2), ("-13.0", 10), ("foo", 10)] { + let w = Wrapping::<$t>::from_str_radix(s, r).map(|w| w.0); + assert_eq!(w, <$t as Num>::from_str_radix(s, r)); + } + )+ + }; + } + + test_wrapping_from_str_radix!(usize u8 u16 u32 u64 isize i8 i16 i32 i64); +} + +#[test] +fn check_num_ops() { + fn compute(x: T, y: T) -> T { + x * y / y % y + y - y + } + assert_eq!(compute(1, 2), 1) +} + +#[test] +fn check_numref_ops() { + fn compute(x: T, y: &T) -> T { + x * y / y % y + y - y + } + assert_eq!(compute(1, &2), 1) +} + +#[test] +fn check_refnum_ops() { + fn compute(x: &T, y: T) -> T + where + for<'a> &'a T: RefNum, + { + &(&(&(&(x * y) / y) % y) + y) - y + } + assert_eq!(compute(&1, 2), 1) +} + +#[test] +fn check_refref_ops() { + fn compute(x: &T, y: &T) -> T + where + for<'a> &'a T: RefNum, + { + &(&(&(&(x * y) / y) % y) + y) - y + } + assert_eq!(compute(&1, &2), 1) +} + +#[test] +fn check_numassign_ops() { + fn compute(mut x: T, y: T) -> T { + x *= y; + x /= y; + x %= y; + x += y; + x -= y; + x + } + assert_eq!(compute(1, 2), 1) +} + +#[test] +fn check_numassignref_ops() { + fn compute(mut x: T, y: &T) -> T { + x *= y; + x /= y; + x %= y; + x += y; + x -= y; + x + } + assert_eq!(compute(1, &2), 1) +} diff --git a/tools/vendor/num-traits/src/macros.rs b/tools/vendor/num-traits/src/macros.rs new file mode 100644 index 0000000000..b97758e42c --- /dev/null +++ b/tools/vendor/num-traits/src/macros.rs @@ -0,0 +1,44 @@ +// not all are used in all features configurations +#![allow(unused)] + +/// Forward a method to an inherent method or a base trait method. +macro_rules! forward { + ($( Self :: $method:ident ( self $( , $arg:ident : $ty:ty )* ) -> $ret:ty ; )*) + => {$( + #[inline] + fn $method(self $( , $arg : $ty )* ) -> $ret { + Self::$method(self $( , $arg )* ) + } + )*}; + ($( $base:ident :: $method:ident ( self $( , $arg:ident : $ty:ty )* ) -> $ret:ty ; )*) + => {$( + #[inline] + fn $method(self $( , $arg : $ty )* ) -> $ret { + ::$method(self $( , $arg )* ) + } + )*}; + ($( $base:ident :: $method:ident ( $( $arg:ident : $ty:ty ),* ) -> $ret:ty ; )*) + => {$( + #[inline] + fn $method( $( $arg : $ty ),* ) -> $ret { + ::$method( $( $arg ),* ) + } + )*}; + ($( $imp:path as $method:ident ( self $( , $arg:ident : $ty:ty )* ) -> $ret:ty ; )*) + => {$( + #[inline] + fn $method(self $( , $arg : $ty )* ) -> $ret { + $imp(self $( , $arg )* ) + } + )*}; +} + +macro_rules! constant { + ($( $method:ident () -> $ret:expr ; )*) + => {$( + #[inline] + fn $method() -> Self { + $ret + } + )*}; +} diff --git a/tools/vendor/num-traits/src/ops/bytes.rs b/tools/vendor/num-traits/src/ops/bytes.rs new file mode 100644 index 0000000000..f6a8030a2b --- /dev/null +++ b/tools/vendor/num-traits/src/ops/bytes.rs @@ -0,0 +1,317 @@ +use core::borrow::{Borrow, BorrowMut}; +use core::cmp::{Eq, Ord, PartialEq, PartialOrd}; +use core::fmt::Debug; +use core::hash::Hash; + +pub trait NumBytes: + Debug + + AsRef<[u8]> + + AsMut<[u8]> + + PartialEq + + Eq + + PartialOrd + + Ord + + Hash + + Borrow<[u8]> + + BorrowMut<[u8]> +{ +} + +impl NumBytes for T where + T: Debug + + AsRef<[u8]> + + AsMut<[u8]> + + PartialEq + + Eq + + PartialOrd + + Ord + + Hash + + Borrow<[u8]> + + BorrowMut<[u8]> + + ?Sized +{ +} + +pub trait ToBytes { + type Bytes: NumBytes; + + /// Return the memory representation of this number as a byte array in big-endian byte order. + /// + /// # Examples + /// + /// ``` + /// use num_traits::ToBytes; + /// + /// let bytes = ToBytes::to_be_bytes(&0x12345678u32); + /// assert_eq!(bytes, [0x12, 0x34, 0x56, 0x78]); + /// ``` + fn to_be_bytes(&self) -> Self::Bytes; + + /// Return the memory representation of this number as a byte array in little-endian byte order. + /// + /// # Examples + /// + /// ``` + /// use num_traits::ToBytes; + /// + /// let bytes = ToBytes::to_le_bytes(&0x12345678u32); + /// assert_eq!(bytes, [0x78, 0x56, 0x34, 0x12]); + /// ``` + fn to_le_bytes(&self) -> Self::Bytes; + + /// Return the memory representation of this number as a byte array in native byte order. + /// + /// As the target platform's native endianness is used, + /// portable code should use [`to_be_bytes`] or [`to_le_bytes`], as appropriate, instead. + /// + /// [`to_be_bytes`]: #method.to_be_bytes + /// [`to_le_bytes`]: #method.to_le_bytes + /// + /// # Examples + /// + /// ``` + /// use num_traits::ToBytes; + /// + /// #[cfg(target_endian = "big")] + /// let expected = [0x12, 0x34, 0x56, 0x78]; + /// + /// #[cfg(target_endian = "little")] + /// let expected = [0x78, 0x56, 0x34, 0x12]; + /// + /// let bytes = ToBytes::to_ne_bytes(&0x12345678u32); + /// assert_eq!(bytes, expected) + /// ``` + fn to_ne_bytes(&self) -> Self::Bytes { + #[cfg(target_endian = "big")] + let bytes = self.to_be_bytes(); + #[cfg(target_endian = "little")] + let bytes = self.to_le_bytes(); + bytes + } +} + +pub trait FromBytes: Sized { + type Bytes: NumBytes + ?Sized; + + /// Create a number from its representation as a byte array in big endian. + /// + /// # Examples + /// + /// ``` + /// use num_traits::FromBytes; + /// + /// let value: u32 = FromBytes::from_be_bytes(&[0x12, 0x34, 0x56, 0x78]); + /// assert_eq!(value, 0x12345678); + /// ``` + fn from_be_bytes(bytes: &Self::Bytes) -> Self; + + /// Create a number from its representation as a byte array in little endian. + /// + /// # Examples + /// + /// ``` + /// use num_traits::FromBytes; + /// + /// let value: u32 = FromBytes::from_le_bytes(&[0x78, 0x56, 0x34, 0x12]); + /// assert_eq!(value, 0x12345678); + /// ``` + fn from_le_bytes(bytes: &Self::Bytes) -> Self; + + /// Create a number from its memory representation as a byte array in native endianness. + /// + /// As the target platform's native endianness is used, + /// portable code likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as appropriate instead. + /// + /// [`from_be_bytes`]: #method.from_be_bytes + /// [`from_le_bytes`]: #method.from_le_bytes + /// + /// # Examples + /// + /// ``` + /// use num_traits::FromBytes; + /// + /// #[cfg(target_endian = "big")] + /// let bytes = [0x12, 0x34, 0x56, 0x78]; + /// + /// #[cfg(target_endian = "little")] + /// let bytes = [0x78, 0x56, 0x34, 0x12]; + /// + /// let value: u32 = FromBytes::from_ne_bytes(&bytes); + /// assert_eq!(value, 0x12345678) + /// ``` + fn from_ne_bytes(bytes: &Self::Bytes) -> Self { + #[cfg(target_endian = "big")] + let this = Self::from_be_bytes(bytes); + #[cfg(target_endian = "little")] + let this = Self::from_le_bytes(bytes); + this + } +} + +macro_rules! float_to_from_bytes_impl { + ($T:ty, $L:expr) => { + impl ToBytes for $T { + type Bytes = [u8; $L]; + + #[inline] + fn to_be_bytes(&self) -> Self::Bytes { + <$T>::to_be_bytes(*self) + } + + #[inline] + fn to_le_bytes(&self) -> Self::Bytes { + <$T>::to_le_bytes(*self) + } + + #[inline] + fn to_ne_bytes(&self) -> Self::Bytes { + <$T>::to_ne_bytes(*self) + } + } + + impl FromBytes for $T { + type Bytes = [u8; $L]; + + #[inline] + fn from_be_bytes(bytes: &Self::Bytes) -> Self { + <$T>::from_be_bytes(*bytes) + } + + #[inline] + fn from_le_bytes(bytes: &Self::Bytes) -> Self { + <$T>::from_le_bytes(*bytes) + } + + #[inline] + fn from_ne_bytes(bytes: &Self::Bytes) -> Self { + <$T>::from_ne_bytes(*bytes) + } + } + }; +} + +macro_rules! int_to_from_bytes_impl { + ($T:ty, $L:expr) => { + impl ToBytes for $T { + type Bytes = [u8; $L]; + + #[inline] + fn to_be_bytes(&self) -> Self::Bytes { + <$T>::to_be_bytes(*self) + } + + #[inline] + fn to_le_bytes(&self) -> Self::Bytes { + <$T>::to_le_bytes(*self) + } + + #[inline] + fn to_ne_bytes(&self) -> Self::Bytes { + <$T>::to_ne_bytes(*self) + } + } + + impl FromBytes for $T { + type Bytes = [u8; $L]; + + #[inline] + fn from_be_bytes(bytes: &Self::Bytes) -> Self { + <$T>::from_be_bytes(*bytes) + } + + #[inline] + fn from_le_bytes(bytes: &Self::Bytes) -> Self { + <$T>::from_le_bytes(*bytes) + } + + #[inline] + fn from_ne_bytes(bytes: &Self::Bytes) -> Self { + <$T>::from_ne_bytes(*bytes) + } + } + }; +} + +int_to_from_bytes_impl!(u8, 1); +int_to_from_bytes_impl!(u16, 2); +int_to_from_bytes_impl!(u32, 4); +int_to_from_bytes_impl!(u64, 8); +int_to_from_bytes_impl!(u128, 16); +#[cfg(target_pointer_width = "64")] +int_to_from_bytes_impl!(usize, 8); +#[cfg(target_pointer_width = "32")] +int_to_from_bytes_impl!(usize, 4); + +int_to_from_bytes_impl!(i8, 1); +int_to_from_bytes_impl!(i16, 2); +int_to_from_bytes_impl!(i32, 4); +int_to_from_bytes_impl!(i64, 8); +int_to_from_bytes_impl!(i128, 16); +#[cfg(target_pointer_width = "64")] +int_to_from_bytes_impl!(isize, 8); +#[cfg(target_pointer_width = "32")] +int_to_from_bytes_impl!(isize, 4); + +float_to_from_bytes_impl!(f32, 4); +float_to_from_bytes_impl!(f64, 8); + +#[cfg(test)] +mod tests { + use super::*; + + macro_rules! check_to_from_bytes { + ($( $ty:ty )+) => {$({ + let n = 1; + let be = <$ty as ToBytes>::to_be_bytes(&n); + let le = <$ty as ToBytes>::to_le_bytes(&n); + let ne = <$ty as ToBytes>::to_ne_bytes(&n); + + assert_eq!(*be.last().unwrap(), 1); + assert_eq!(*le.first().unwrap(), 1); + if cfg!(target_endian = "big") { + assert_eq!(*ne.last().unwrap(), 1); + } else { + assert_eq!(*ne.first().unwrap(), 1); + } + + assert_eq!(<$ty as FromBytes>::from_be_bytes(&be), n); + assert_eq!(<$ty as FromBytes>::from_le_bytes(&le), n); + if cfg!(target_endian = "big") { + assert_eq!(<$ty as FromBytes>::from_ne_bytes(&be), n); + } else { + assert_eq!(<$ty as FromBytes>::from_ne_bytes(&le), n); + } + })+} + } + + #[test] + fn convert_between_int_and_bytes() { + check_to_from_bytes!(u8 u16 u32 u64 u128 usize); + check_to_from_bytes!(i8 i16 i32 i64 i128 isize); + } + + #[test] + fn convert_between_float_and_bytes() { + macro_rules! check_to_from_bytes { + ($( $ty:ty )+) => {$( + let n: $ty = 3.14; + + let be = <$ty as ToBytes>::to_be_bytes(&n); + let le = <$ty as ToBytes>::to_le_bytes(&n); + let ne = <$ty as ToBytes>::to_ne_bytes(&n); + + assert_eq!(<$ty as FromBytes>::from_be_bytes(&be), n); + assert_eq!(<$ty as FromBytes>::from_le_bytes(&le), n); + if cfg!(target_endian = "big") { + assert_eq!(ne, be); + assert_eq!(<$ty as FromBytes>::from_ne_bytes(&be), n); + } else { + assert_eq!(ne, le); + assert_eq!(<$ty as FromBytes>::from_ne_bytes(&le), n); + } + )+} + } + + check_to_from_bytes!(f32 f64); + } +} diff --git a/tools/vendor/num-traits/src/ops/checked.rs b/tools/vendor/num-traits/src/ops/checked.rs new file mode 100644 index 0000000000..da1eb3eae3 --- /dev/null +++ b/tools/vendor/num-traits/src/ops/checked.rs @@ -0,0 +1,261 @@ +use core::ops::{Add, Div, Mul, Rem, Shl, Shr, Sub}; + +/// Performs addition that returns `None` instead of wrapping around on +/// overflow. +pub trait CheckedAdd: Sized + Add { + /// Adds two numbers, checking for overflow. If overflow happens, `None` is + /// returned. + fn checked_add(&self, v: &Self) -> Option; +} + +macro_rules! checked_impl { + ($trait_name:ident, $method:ident, $t:ty) => { + impl $trait_name for $t { + #[inline] + fn $method(&self, v: &$t) -> Option<$t> { + <$t>::$method(*self, *v) + } + } + }; +} + +checked_impl!(CheckedAdd, checked_add, u8); +checked_impl!(CheckedAdd, checked_add, u16); +checked_impl!(CheckedAdd, checked_add, u32); +checked_impl!(CheckedAdd, checked_add, u64); +checked_impl!(CheckedAdd, checked_add, usize); +checked_impl!(CheckedAdd, checked_add, u128); + +checked_impl!(CheckedAdd, checked_add, i8); +checked_impl!(CheckedAdd, checked_add, i16); +checked_impl!(CheckedAdd, checked_add, i32); +checked_impl!(CheckedAdd, checked_add, i64); +checked_impl!(CheckedAdd, checked_add, isize); +checked_impl!(CheckedAdd, checked_add, i128); + +/// Performs subtraction that returns `None` instead of wrapping around on underflow. +pub trait CheckedSub: Sized + Sub { + /// Subtracts two numbers, checking for underflow. If underflow happens, + /// `None` is returned. + fn checked_sub(&self, v: &Self) -> Option; +} + +checked_impl!(CheckedSub, checked_sub, u8); +checked_impl!(CheckedSub, checked_sub, u16); +checked_impl!(CheckedSub, checked_sub, u32); +checked_impl!(CheckedSub, checked_sub, u64); +checked_impl!(CheckedSub, checked_sub, usize); +checked_impl!(CheckedSub, checked_sub, u128); + +checked_impl!(CheckedSub, checked_sub, i8); +checked_impl!(CheckedSub, checked_sub, i16); +checked_impl!(CheckedSub, checked_sub, i32); +checked_impl!(CheckedSub, checked_sub, i64); +checked_impl!(CheckedSub, checked_sub, isize); +checked_impl!(CheckedSub, checked_sub, i128); + +/// Performs multiplication that returns `None` instead of wrapping around on underflow or +/// overflow. +pub trait CheckedMul: Sized + Mul { + /// Multiplies two numbers, checking for underflow or overflow. If underflow + /// or overflow happens, `None` is returned. + fn checked_mul(&self, v: &Self) -> Option; +} + +checked_impl!(CheckedMul, checked_mul, u8); +checked_impl!(CheckedMul, checked_mul, u16); +checked_impl!(CheckedMul, checked_mul, u32); +checked_impl!(CheckedMul, checked_mul, u64); +checked_impl!(CheckedMul, checked_mul, usize); +checked_impl!(CheckedMul, checked_mul, u128); + +checked_impl!(CheckedMul, checked_mul, i8); +checked_impl!(CheckedMul, checked_mul, i16); +checked_impl!(CheckedMul, checked_mul, i32); +checked_impl!(CheckedMul, checked_mul, i64); +checked_impl!(CheckedMul, checked_mul, isize); +checked_impl!(CheckedMul, checked_mul, i128); + +/// Performs division that returns `None` instead of panicking on division by zero and instead of +/// wrapping around on underflow and overflow. +pub trait CheckedDiv: Sized + Div { + /// Divides two numbers, checking for underflow, overflow and division by + /// zero. If any of that happens, `None` is returned. + fn checked_div(&self, v: &Self) -> Option; +} + +checked_impl!(CheckedDiv, checked_div, u8); +checked_impl!(CheckedDiv, checked_div, u16); +checked_impl!(CheckedDiv, checked_div, u32); +checked_impl!(CheckedDiv, checked_div, u64); +checked_impl!(CheckedDiv, checked_div, usize); +checked_impl!(CheckedDiv, checked_div, u128); + +checked_impl!(CheckedDiv, checked_div, i8); +checked_impl!(CheckedDiv, checked_div, i16); +checked_impl!(CheckedDiv, checked_div, i32); +checked_impl!(CheckedDiv, checked_div, i64); +checked_impl!(CheckedDiv, checked_div, isize); +checked_impl!(CheckedDiv, checked_div, i128); + +/// Performs an integral remainder that returns `None` instead of panicking on division by zero and +/// instead of wrapping around on underflow and overflow. +pub trait CheckedRem: Sized + Rem { + /// Finds the remainder of dividing two numbers, checking for underflow, overflow and division + /// by zero. If any of that happens, `None` is returned. + /// + /// # Examples + /// + /// ``` + /// use num_traits::CheckedRem; + /// use std::i32::MIN; + /// + /// assert_eq!(CheckedRem::checked_rem(&10, &7), Some(3)); + /// assert_eq!(CheckedRem::checked_rem(&10, &-7), Some(3)); + /// assert_eq!(CheckedRem::checked_rem(&-10, &7), Some(-3)); + /// assert_eq!(CheckedRem::checked_rem(&-10, &-7), Some(-3)); + /// + /// assert_eq!(CheckedRem::checked_rem(&10, &0), None); + /// + /// assert_eq!(CheckedRem::checked_rem(&MIN, &1), Some(0)); + /// assert_eq!(CheckedRem::checked_rem(&MIN, &-1), None); + /// ``` + fn checked_rem(&self, v: &Self) -> Option; +} + +checked_impl!(CheckedRem, checked_rem, u8); +checked_impl!(CheckedRem, checked_rem, u16); +checked_impl!(CheckedRem, checked_rem, u32); +checked_impl!(CheckedRem, checked_rem, u64); +checked_impl!(CheckedRem, checked_rem, usize); +checked_impl!(CheckedRem, checked_rem, u128); + +checked_impl!(CheckedRem, checked_rem, i8); +checked_impl!(CheckedRem, checked_rem, i16); +checked_impl!(CheckedRem, checked_rem, i32); +checked_impl!(CheckedRem, checked_rem, i64); +checked_impl!(CheckedRem, checked_rem, isize); +checked_impl!(CheckedRem, checked_rem, i128); + +macro_rules! checked_impl_unary { + ($trait_name:ident, $method:ident, $t:ty) => { + impl $trait_name for $t { + #[inline] + fn $method(&self) -> Option<$t> { + <$t>::$method(*self) + } + } + }; +} + +/// Performs negation that returns `None` if the result can't be represented. +pub trait CheckedNeg: Sized { + /// Negates a number, returning `None` for results that can't be represented, like signed `MIN` + /// values that can't be positive, or non-zero unsigned values that can't be negative. + /// + /// # Examples + /// + /// ``` + /// use num_traits::CheckedNeg; + /// use std::i32::MIN; + /// + /// assert_eq!(CheckedNeg::checked_neg(&1_i32), Some(-1)); + /// assert_eq!(CheckedNeg::checked_neg(&-1_i32), Some(1)); + /// assert_eq!(CheckedNeg::checked_neg(&MIN), None); + /// + /// assert_eq!(CheckedNeg::checked_neg(&0_u32), Some(0)); + /// assert_eq!(CheckedNeg::checked_neg(&1_u32), None); + /// ``` + fn checked_neg(&self) -> Option; +} + +checked_impl_unary!(CheckedNeg, checked_neg, u8); +checked_impl_unary!(CheckedNeg, checked_neg, u16); +checked_impl_unary!(CheckedNeg, checked_neg, u32); +checked_impl_unary!(CheckedNeg, checked_neg, u64); +checked_impl_unary!(CheckedNeg, checked_neg, usize); +checked_impl_unary!(CheckedNeg, checked_neg, u128); + +checked_impl_unary!(CheckedNeg, checked_neg, i8); +checked_impl_unary!(CheckedNeg, checked_neg, i16); +checked_impl_unary!(CheckedNeg, checked_neg, i32); +checked_impl_unary!(CheckedNeg, checked_neg, i64); +checked_impl_unary!(CheckedNeg, checked_neg, isize); +checked_impl_unary!(CheckedNeg, checked_neg, i128); + +/// Performs a left shift that returns `None` on shifts larger than +/// or equal to the type width. +pub trait CheckedShl: Sized + Shl { + /// Checked shift left. Computes `self << rhs`, returning `None` + /// if `rhs` is larger than or equal to the number of bits in `self`. + /// + /// ``` + /// use num_traits::CheckedShl; + /// + /// let x: u16 = 0x0001; + /// + /// assert_eq!(CheckedShl::checked_shl(&x, 0), Some(0x0001)); + /// assert_eq!(CheckedShl::checked_shl(&x, 1), Some(0x0002)); + /// assert_eq!(CheckedShl::checked_shl(&x, 15), Some(0x8000)); + /// assert_eq!(CheckedShl::checked_shl(&x, 16), None); + /// ``` + fn checked_shl(&self, rhs: u32) -> Option; +} + +macro_rules! checked_shift_impl { + ($trait_name:ident, $method:ident, $t:ty) => { + impl $trait_name for $t { + #[inline] + fn $method(&self, rhs: u32) -> Option<$t> { + <$t>::$method(*self, rhs) + } + } + }; +} + +checked_shift_impl!(CheckedShl, checked_shl, u8); +checked_shift_impl!(CheckedShl, checked_shl, u16); +checked_shift_impl!(CheckedShl, checked_shl, u32); +checked_shift_impl!(CheckedShl, checked_shl, u64); +checked_shift_impl!(CheckedShl, checked_shl, usize); +checked_shift_impl!(CheckedShl, checked_shl, u128); + +checked_shift_impl!(CheckedShl, checked_shl, i8); +checked_shift_impl!(CheckedShl, checked_shl, i16); +checked_shift_impl!(CheckedShl, checked_shl, i32); +checked_shift_impl!(CheckedShl, checked_shl, i64); +checked_shift_impl!(CheckedShl, checked_shl, isize); +checked_shift_impl!(CheckedShl, checked_shl, i128); + +/// Performs a right shift that returns `None` on shifts larger than +/// or equal to the type width. +pub trait CheckedShr: Sized + Shr { + /// Checked shift right. Computes `self >> rhs`, returning `None` + /// if `rhs` is larger than or equal to the number of bits in `self`. + /// + /// ``` + /// use num_traits::CheckedShr; + /// + /// let x: u16 = 0x8000; + /// + /// assert_eq!(CheckedShr::checked_shr(&x, 0), Some(0x8000)); + /// assert_eq!(CheckedShr::checked_shr(&x, 1), Some(0x4000)); + /// assert_eq!(CheckedShr::checked_shr(&x, 15), Some(0x0001)); + /// assert_eq!(CheckedShr::checked_shr(&x, 16), None); + /// ``` + fn checked_shr(&self, rhs: u32) -> Option; +} + +checked_shift_impl!(CheckedShr, checked_shr, u8); +checked_shift_impl!(CheckedShr, checked_shr, u16); +checked_shift_impl!(CheckedShr, checked_shr, u32); +checked_shift_impl!(CheckedShr, checked_shr, u64); +checked_shift_impl!(CheckedShr, checked_shr, usize); +checked_shift_impl!(CheckedShr, checked_shr, u128); + +checked_shift_impl!(CheckedShr, checked_shr, i8); +checked_shift_impl!(CheckedShr, checked_shr, i16); +checked_shift_impl!(CheckedShr, checked_shr, i32); +checked_shift_impl!(CheckedShr, checked_shr, i64); +checked_shift_impl!(CheckedShr, checked_shr, isize); +checked_shift_impl!(CheckedShr, checked_shr, i128); diff --git a/tools/vendor/num-traits/src/ops/euclid.rs b/tools/vendor/num-traits/src/ops/euclid.rs new file mode 100644 index 0000000000..fa7b317a28 --- /dev/null +++ b/tools/vendor/num-traits/src/ops/euclid.rs @@ -0,0 +1,278 @@ +use core::ops::{Div, Rem}; + +pub trait Euclid: Sized + Div + Rem { + /// Calculates Euclidean division, the matching method for `rem_euclid`. + /// + /// This computes the integer `n` such that + /// `self = n * v + self.rem_euclid(v)`. + /// In other words, the result is `self / v` rounded to the integer `n` + /// such that `self >= n * v`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::Euclid; + /// + /// let a: i32 = 7; + /// let b: i32 = 4; + /// assert_eq!(Euclid::div_euclid(&a, &b), 1); // 7 > 4 * 1 + /// assert_eq!(Euclid::div_euclid(&-a, &b), -2); // -7 >= 4 * -2 + /// assert_eq!(Euclid::div_euclid(&a, &-b), -1); // 7 >= -4 * -1 + /// assert_eq!(Euclid::div_euclid(&-a, &-b), 2); // -7 >= -4 * 2 + /// ``` + fn div_euclid(&self, v: &Self) -> Self; + + /// Calculates the least nonnegative remainder of `self (mod v)`. + /// + /// In particular, the return value `r` satisfies `0.0 <= r < v.abs()` in + /// most cases. However, due to a floating point round-off error it can + /// result in `r == v.abs()`, violating the mathematical definition, if + /// `self` is much smaller than `v.abs()` in magnitude and `self < 0.0`. + /// This result is not an element of the function's codomain, but it is the + /// closest floating point number in the real numbers and thus fulfills the + /// property `self == self.div_euclid(v) * v + self.rem_euclid(v)` + /// approximatively. + /// + /// # Examples + /// + /// ``` + /// use num_traits::Euclid; + /// + /// let a: i32 = 7; + /// let b: i32 = 4; + /// assert_eq!(Euclid::rem_euclid(&a, &b), 3); + /// assert_eq!(Euclid::rem_euclid(&-a, &b), 1); + /// assert_eq!(Euclid::rem_euclid(&a, &-b), 3); + /// assert_eq!(Euclid::rem_euclid(&-a, &-b), 1); + /// ``` + fn rem_euclid(&self, v: &Self) -> Self; + + /// Returns both the quotient and remainder from Euclidean division. + /// + /// By default, it internally calls both `Euclid::div_euclid` and `Euclid::rem_euclid`, + /// but it can be overridden in order to implement some optimization. + /// + /// # Examples + /// + /// ``` + /// # use num_traits::Euclid; + /// let x = 5u8; + /// let y = 3u8; + /// + /// let div = Euclid::div_euclid(&x, &y); + /// let rem = Euclid::rem_euclid(&x, &y); + /// + /// assert_eq!((div, rem), Euclid::div_rem_euclid(&x, &y)); + /// ``` + fn div_rem_euclid(&self, v: &Self) -> (Self, Self) { + (self.div_euclid(v), self.rem_euclid(v)) + } +} + +macro_rules! euclid_forward_impl { + ($($t:ty)*) => {$( + impl Euclid for $t { + #[inline] + fn div_euclid(&self, v: &$t) -> Self { + <$t>::div_euclid(*self, *v) + } + + #[inline] + fn rem_euclid(&self, v: &$t) -> Self { + <$t>::rem_euclid(*self, *v) + } + } + )*} +} + +euclid_forward_impl!(isize i8 i16 i32 i64 i128); +euclid_forward_impl!(usize u8 u16 u32 u64 u128); + +#[cfg(feature = "std")] +euclid_forward_impl!(f32 f64); + +#[cfg(not(feature = "std"))] +impl Euclid for f32 { + #[inline] + fn div_euclid(&self, v: &f32) -> f32 { + let q = ::trunc(self / v); + if self % v < 0.0 { + return if *v > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + #[inline] + fn rem_euclid(&self, v: &f32) -> f32 { + let r = self % v; + if r < 0.0 { + r + ::abs(*v) + } else { + r + } + } +} + +#[cfg(not(feature = "std"))] +impl Euclid for f64 { + #[inline] + fn div_euclid(&self, v: &f64) -> f64 { + let q = ::trunc(self / v); + if self % v < 0.0 { + return if *v > 0.0 { q - 1.0 } else { q + 1.0 }; + } + q + } + + #[inline] + fn rem_euclid(&self, v: &f64) -> f64 { + let r = self % v; + if r < 0.0 { + r + ::abs(*v) + } else { + r + } + } +} + +pub trait CheckedEuclid: Euclid { + /// Performs euclid division that returns `None` instead of panicking on division by zero + /// and instead of wrapping around on underflow and overflow. + fn checked_div_euclid(&self, v: &Self) -> Option; + + /// Finds the euclid remainder of dividing two numbers, checking for underflow, overflow and + /// division by zero. If any of that happens, `None` is returned. + fn checked_rem_euclid(&self, v: &Self) -> Option; + + /// Returns both the quotient and remainder from checked Euclidean division. + /// + /// By default, it internally calls both `CheckedEuclid::checked_div_euclid` and `CheckedEuclid::checked_rem_euclid`, + /// but it can be overridden in order to implement some optimization. + /// # Examples + /// + /// ``` + /// # use num_traits::CheckedEuclid; + /// let x = 5u8; + /// let y = 3u8; + /// + /// let div = CheckedEuclid::checked_div_euclid(&x, &y); + /// let rem = CheckedEuclid::checked_rem_euclid(&x, &y); + /// + /// assert_eq!(Some((div.unwrap(), rem.unwrap())), CheckedEuclid::checked_div_rem_euclid(&x, &y)); + /// ``` + fn checked_div_rem_euclid(&self, v: &Self) -> Option<(Self, Self)> { + Some((self.checked_div_euclid(v)?, self.checked_rem_euclid(v)?)) + } +} + +macro_rules! checked_euclid_forward_impl { + ($($t:ty)*) => {$( + impl CheckedEuclid for $t { + #[inline] + fn checked_div_euclid(&self, v: &$t) -> Option { + <$t>::checked_div_euclid(*self, *v) + } + + #[inline] + fn checked_rem_euclid(&self, v: &$t) -> Option { + <$t>::checked_rem_euclid(*self, *v) + } + } + )*} +} + +checked_euclid_forward_impl!(isize i8 i16 i32 i64 i128); +checked_euclid_forward_impl!(usize u8 u16 u32 u64 u128); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn euclid_unsigned() { + macro_rules! test_euclid { + ($($t:ident)+) => { + $( + { + let x: $t = 10; + let y: $t = 3; + let div = Euclid::div_euclid(&x, &y); + let rem = Euclid::rem_euclid(&x, &y); + assert_eq!(div, 3); + assert_eq!(rem, 1); + assert_eq!((div, rem), Euclid::div_rem_euclid(&x, &y)); + } + )+ + }; + } + + test_euclid!(usize u8 u16 u32 u64); + } + + #[test] + fn euclid_signed() { + macro_rules! test_euclid { + ($($t:ident)+) => { + $( + { + let x: $t = 10; + let y: $t = -3; + assert_eq!(Euclid::div_euclid(&x, &y), -3); + assert_eq!(Euclid::div_euclid(&-x, &y), 4); + assert_eq!(Euclid::rem_euclid(&x, &y), 1); + assert_eq!(Euclid::rem_euclid(&-x, &y), 2); + assert_eq!((Euclid::div_euclid(&x, &y), Euclid::rem_euclid(&x, &y)), Euclid::div_rem_euclid(&x, &y)); + let x: $t = $t::min_value() + 1; + let y: $t = -1; + assert_eq!(Euclid::div_euclid(&x, &y), $t::max_value()); + } + )+ + }; + } + + test_euclid!(isize i8 i16 i32 i64 i128); + } + + #[test] + fn euclid_float() { + macro_rules! test_euclid { + ($($t:ident)+) => { + $( + { + let x: $t = 12.1; + let y: $t = 3.2; + assert!(Euclid::div_euclid(&x, &y) * y + Euclid::rem_euclid(&x, &y) - x + <= 46.4 * <$t as crate::float::FloatCore>::epsilon()); + assert!(Euclid::div_euclid(&x, &-y) * -y + Euclid::rem_euclid(&x, &-y) - x + <= 46.4 * <$t as crate::float::FloatCore>::epsilon()); + assert!(Euclid::div_euclid(&-x, &y) * y + Euclid::rem_euclid(&-x, &y) + x + <= 46.4 * <$t as crate::float::FloatCore>::epsilon()); + assert!(Euclid::div_euclid(&-x, &-y) * -y + Euclid::rem_euclid(&-x, &-y) + x + <= 46.4 * <$t as crate::float::FloatCore>::epsilon()); + assert_eq!((Euclid::div_euclid(&x, &y), Euclid::rem_euclid(&x, &y)), Euclid::div_rem_euclid(&x, &y)); + } + )+ + }; + } + + test_euclid!(f32 f64); + } + + #[test] + fn euclid_checked() { + macro_rules! test_euclid_checked { + ($($t:ident)+) => { + $( + { + assert_eq!(CheckedEuclid::checked_div_euclid(&$t::min_value(), &-1), None); + assert_eq!(CheckedEuclid::checked_rem_euclid(&$t::min_value(), &-1), None); + assert_eq!(CheckedEuclid::checked_div_euclid(&1, &0), None); + assert_eq!(CheckedEuclid::checked_rem_euclid(&1, &0), None); + } + )+ + }; + } + + test_euclid_checked!(isize i8 i16 i32 i64 i128); + } +} diff --git a/tools/vendor/num-traits/src/ops/inv.rs b/tools/vendor/num-traits/src/ops/inv.rs new file mode 100644 index 0000000000..7087d09d01 --- /dev/null +++ b/tools/vendor/num-traits/src/ops/inv.rs @@ -0,0 +1,47 @@ +/// Unary operator for retrieving the multiplicative inverse, or reciprocal, of a value. +pub trait Inv { + /// The result after applying the operator. + type Output; + + /// Returns the multiplicative inverse of `self`. + /// + /// # Examples + /// + /// ``` + /// use std::f64::INFINITY; + /// use num_traits::Inv; + /// + /// assert_eq!(7.0.inv() * 7.0, 1.0); + /// assert_eq!((-0.0).inv(), -INFINITY); + /// ``` + fn inv(self) -> Self::Output; +} + +impl Inv for f32 { + type Output = f32; + #[inline] + fn inv(self) -> f32 { + 1.0 / self + } +} +impl Inv for f64 { + type Output = f64; + #[inline] + fn inv(self) -> f64 { + 1.0 / self + } +} +impl<'a> Inv for &'a f32 { + type Output = f32; + #[inline] + fn inv(self) -> f32 { + 1.0 / *self + } +} +impl<'a> Inv for &'a f64 { + type Output = f64; + #[inline] + fn inv(self) -> f64 { + 1.0 / *self + } +} diff --git a/tools/vendor/num-traits/src/ops/mod.rs b/tools/vendor/num-traits/src/ops/mod.rs new file mode 100644 index 0000000000..2128d86a2d --- /dev/null +++ b/tools/vendor/num-traits/src/ops/mod.rs @@ -0,0 +1,8 @@ +pub mod bytes; +pub mod checked; +pub mod euclid; +pub mod inv; +pub mod mul_add; +pub mod overflowing; +pub mod saturating; +pub mod wrapping; diff --git a/tools/vendor/num-traits/src/ops/mul_add.rs b/tools/vendor/num-traits/src/ops/mul_add.rs new file mode 100644 index 0000000000..51beb55726 --- /dev/null +++ b/tools/vendor/num-traits/src/ops/mul_add.rs @@ -0,0 +1,149 @@ +/// Fused multiply-add. Computes `(self * a) + b` with only one rounding +/// error, yielding a more accurate result than an unfused multiply-add. +/// +/// Using `mul_add` can be more performant than an unfused multiply-add if +/// the target architecture has a dedicated `fma` CPU instruction. +/// +/// Note that `A` and `B` are `Self` by default, but this is not mandatory. +/// +/// # Example +/// +/// ``` +/// use std::f32; +/// +/// let m = 10.0_f32; +/// let x = 4.0_f32; +/// let b = 60.0_f32; +/// +/// // 100.0 +/// let abs_difference = (m.mul_add(x, b) - (m*x + b)).abs(); +/// +/// assert!(abs_difference <= 100.0 * f32::EPSILON); +/// ``` +pub trait MulAdd { + /// The resulting type after applying the fused multiply-add. + type Output; + + /// Performs the fused multiply-add operation `(self * a) + b` + fn mul_add(self, a: A, b: B) -> Self::Output; +} + +/// The fused multiply-add assignment operation `*self = (*self * a) + b` +pub trait MulAddAssign { + /// Performs the fused multiply-add assignment operation `*self = (*self * a) + b` + fn mul_add_assign(&mut self, a: A, b: B); +} + +#[cfg(any(feature = "std", feature = "libm"))] +impl MulAdd for f32 { + type Output = Self; + + #[inline] + fn mul_add(self, a: Self, b: Self) -> Self::Output { + ::mul_add(self, a, b) + } +} + +#[cfg(any(feature = "std", feature = "libm"))] +impl MulAdd for f64 { + type Output = Self; + + #[inline] + fn mul_add(self, a: Self, b: Self) -> Self::Output { + ::mul_add(self, a, b) + } +} + +macro_rules! mul_add_impl { + ($trait_name:ident for $($t:ty)*) => {$( + impl $trait_name for $t { + type Output = Self; + + #[inline] + fn mul_add(self, a: Self, b: Self) -> Self::Output { + (self * a) + b + } + } + )*} +} + +mul_add_impl!(MulAdd for isize i8 i16 i32 i64 i128); +mul_add_impl!(MulAdd for usize u8 u16 u32 u64 u128); + +#[cfg(any(feature = "std", feature = "libm"))] +impl MulAddAssign for f32 { + #[inline] + fn mul_add_assign(&mut self, a: Self, b: Self) { + *self = ::mul_add(*self, a, b) + } +} + +#[cfg(any(feature = "std", feature = "libm"))] +impl MulAddAssign for f64 { + #[inline] + fn mul_add_assign(&mut self, a: Self, b: Self) { + *self = ::mul_add(*self, a, b) + } +} + +macro_rules! mul_add_assign_impl { + ($trait_name:ident for $($t:ty)*) => {$( + impl $trait_name for $t { + #[inline] + fn mul_add_assign(&mut self, a: Self, b: Self) { + *self = (*self * a) + b + } + } + )*} +} + +mul_add_assign_impl!(MulAddAssign for isize i8 i16 i32 i64 i128); +mul_add_assign_impl!(MulAddAssign for usize u8 u16 u32 u64 u128); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn mul_add_integer() { + macro_rules! test_mul_add { + ($($t:ident)+) => { + $( + { + let m: $t = 2; + let x: $t = 3; + let b: $t = 4; + + assert_eq!(MulAdd::mul_add(m, x, b), (m*x + b)); + } + )+ + }; + } + + test_mul_add!(usize u8 u16 u32 u64 isize i8 i16 i32 i64); + } + + #[test] + #[cfg(feature = "std")] + fn mul_add_float() { + macro_rules! test_mul_add { + ($($t:ident)+) => { + $( + { + use core::$t; + + let m: $t = 12.0; + let x: $t = 3.4; + let b: $t = 5.6; + + let abs_difference = (MulAdd::mul_add(m, x, b) - (m*x + b)).abs(); + + assert!(abs_difference <= 46.4 * $t::EPSILON); + } + )+ + }; + } + + test_mul_add!(f32 f64); + } +} diff --git a/tools/vendor/num-traits/src/ops/overflowing.rs b/tools/vendor/num-traits/src/ops/overflowing.rs new file mode 100644 index 0000000000..c7a35a51c1 --- /dev/null +++ b/tools/vendor/num-traits/src/ops/overflowing.rs @@ -0,0 +1,96 @@ +use core::ops::{Add, Mul, Sub}; +use core::{i128, i16, i32, i64, i8, isize}; +use core::{u128, u16, u32, u64, u8, usize}; + +macro_rules! overflowing_impl { + ($trait_name:ident, $method:ident, $t:ty) => { + impl $trait_name for $t { + #[inline] + fn $method(&self, v: &Self) -> (Self, bool) { + <$t>::$method(*self, *v) + } + } + }; +} + +/// Performs addition with a flag for overflow. +pub trait OverflowingAdd: Sized + Add { + /// Returns a tuple of the sum along with a boolean indicating whether an arithmetic overflow would occur. + /// If an overflow would have occurred then the wrapped value is returned. + fn overflowing_add(&self, v: &Self) -> (Self, bool); +} + +overflowing_impl!(OverflowingAdd, overflowing_add, u8); +overflowing_impl!(OverflowingAdd, overflowing_add, u16); +overflowing_impl!(OverflowingAdd, overflowing_add, u32); +overflowing_impl!(OverflowingAdd, overflowing_add, u64); +overflowing_impl!(OverflowingAdd, overflowing_add, usize); +overflowing_impl!(OverflowingAdd, overflowing_add, u128); + +overflowing_impl!(OverflowingAdd, overflowing_add, i8); +overflowing_impl!(OverflowingAdd, overflowing_add, i16); +overflowing_impl!(OverflowingAdd, overflowing_add, i32); +overflowing_impl!(OverflowingAdd, overflowing_add, i64); +overflowing_impl!(OverflowingAdd, overflowing_add, isize); +overflowing_impl!(OverflowingAdd, overflowing_add, i128); + +/// Performs substraction with a flag for overflow. +pub trait OverflowingSub: Sized + Sub { + /// Returns a tuple of the difference along with a boolean indicating whether an arithmetic overflow would occur. + /// If an overflow would have occurred then the wrapped value is returned. + fn overflowing_sub(&self, v: &Self) -> (Self, bool); +} + +overflowing_impl!(OverflowingSub, overflowing_sub, u8); +overflowing_impl!(OverflowingSub, overflowing_sub, u16); +overflowing_impl!(OverflowingSub, overflowing_sub, u32); +overflowing_impl!(OverflowingSub, overflowing_sub, u64); +overflowing_impl!(OverflowingSub, overflowing_sub, usize); +overflowing_impl!(OverflowingSub, overflowing_sub, u128); + +overflowing_impl!(OverflowingSub, overflowing_sub, i8); +overflowing_impl!(OverflowingSub, overflowing_sub, i16); +overflowing_impl!(OverflowingSub, overflowing_sub, i32); +overflowing_impl!(OverflowingSub, overflowing_sub, i64); +overflowing_impl!(OverflowingSub, overflowing_sub, isize); +overflowing_impl!(OverflowingSub, overflowing_sub, i128); + +/// Performs multiplication with a flag for overflow. +pub trait OverflowingMul: Sized + Mul { + /// Returns a tuple of the product along with a boolean indicating whether an arithmetic overflow would occur. + /// If an overflow would have occurred then the wrapped value is returned. + fn overflowing_mul(&self, v: &Self) -> (Self, bool); +} + +overflowing_impl!(OverflowingMul, overflowing_mul, u8); +overflowing_impl!(OverflowingMul, overflowing_mul, u16); +overflowing_impl!(OverflowingMul, overflowing_mul, u32); +overflowing_impl!(OverflowingMul, overflowing_mul, u64); +overflowing_impl!(OverflowingMul, overflowing_mul, usize); +overflowing_impl!(OverflowingMul, overflowing_mul, u128); + +overflowing_impl!(OverflowingMul, overflowing_mul, i8); +overflowing_impl!(OverflowingMul, overflowing_mul, i16); +overflowing_impl!(OverflowingMul, overflowing_mul, i32); +overflowing_impl!(OverflowingMul, overflowing_mul, i64); +overflowing_impl!(OverflowingMul, overflowing_mul, isize); +overflowing_impl!(OverflowingMul, overflowing_mul, i128); + +#[test] +fn test_overflowing_traits() { + fn overflowing_add(a: T, b: T) -> (T, bool) { + a.overflowing_add(&b) + } + fn overflowing_sub(a: T, b: T) -> (T, bool) { + a.overflowing_sub(&b) + } + fn overflowing_mul(a: T, b: T) -> (T, bool) { + a.overflowing_mul(&b) + } + assert_eq!(overflowing_add(5i16, 2), (7, false)); + assert_eq!(overflowing_add(i16::MAX, 1), (i16::MIN, true)); + assert_eq!(overflowing_sub(5i16, 2), (3, false)); + assert_eq!(overflowing_sub(i16::MIN, 1), (i16::MAX, true)); + assert_eq!(overflowing_mul(5i16, 2), (10, false)); + assert_eq!(overflowing_mul(1_000_000_000i32, 10), (1410065408, true)); +} diff --git a/tools/vendor/num-traits/src/ops/saturating.rs b/tools/vendor/num-traits/src/ops/saturating.rs new file mode 100644 index 0000000000..16a0045767 --- /dev/null +++ b/tools/vendor/num-traits/src/ops/saturating.rs @@ -0,0 +1,130 @@ +use core::ops::{Add, Mul, Sub}; + +/// Saturating math operations. Deprecated, use `SaturatingAdd`, `SaturatingSub` and +/// `SaturatingMul` instead. +pub trait Saturating { + /// Saturating addition operator. + /// Returns a+b, saturating at the numeric bounds instead of overflowing. + fn saturating_add(self, v: Self) -> Self; + + /// Saturating subtraction operator. + /// Returns a-b, saturating at the numeric bounds instead of overflowing. + fn saturating_sub(self, v: Self) -> Self; +} + +macro_rules! deprecated_saturating_impl { + ($trait_name:ident for $($t:ty)*) => {$( + impl $trait_name for $t { + #[inline] + fn saturating_add(self, v: Self) -> Self { + Self::saturating_add(self, v) + } + + #[inline] + fn saturating_sub(self, v: Self) -> Self { + Self::saturating_sub(self, v) + } + } + )*} +} + +deprecated_saturating_impl!(Saturating for isize i8 i16 i32 i64 i128); +deprecated_saturating_impl!(Saturating for usize u8 u16 u32 u64 u128); + +macro_rules! saturating_impl { + ($trait_name:ident, $method:ident, $t:ty) => { + impl $trait_name for $t { + #[inline] + fn $method(&self, v: &Self) -> Self { + <$t>::$method(*self, *v) + } + } + }; +} + +/// Performs addition that saturates at the numeric bounds instead of overflowing. +pub trait SaturatingAdd: Sized + Add { + /// Saturating addition. Computes `self + other`, saturating at the relevant high or low boundary of + /// the type. + fn saturating_add(&self, v: &Self) -> Self; +} + +saturating_impl!(SaturatingAdd, saturating_add, u8); +saturating_impl!(SaturatingAdd, saturating_add, u16); +saturating_impl!(SaturatingAdd, saturating_add, u32); +saturating_impl!(SaturatingAdd, saturating_add, u64); +saturating_impl!(SaturatingAdd, saturating_add, usize); +saturating_impl!(SaturatingAdd, saturating_add, u128); + +saturating_impl!(SaturatingAdd, saturating_add, i8); +saturating_impl!(SaturatingAdd, saturating_add, i16); +saturating_impl!(SaturatingAdd, saturating_add, i32); +saturating_impl!(SaturatingAdd, saturating_add, i64); +saturating_impl!(SaturatingAdd, saturating_add, isize); +saturating_impl!(SaturatingAdd, saturating_add, i128); + +/// Performs subtraction that saturates at the numeric bounds instead of overflowing. +pub trait SaturatingSub: Sized + Sub { + /// Saturating subtraction. Computes `self - other`, saturating at the relevant high or low boundary of + /// the type. + fn saturating_sub(&self, v: &Self) -> Self; +} + +saturating_impl!(SaturatingSub, saturating_sub, u8); +saturating_impl!(SaturatingSub, saturating_sub, u16); +saturating_impl!(SaturatingSub, saturating_sub, u32); +saturating_impl!(SaturatingSub, saturating_sub, u64); +saturating_impl!(SaturatingSub, saturating_sub, usize); +saturating_impl!(SaturatingSub, saturating_sub, u128); + +saturating_impl!(SaturatingSub, saturating_sub, i8); +saturating_impl!(SaturatingSub, saturating_sub, i16); +saturating_impl!(SaturatingSub, saturating_sub, i32); +saturating_impl!(SaturatingSub, saturating_sub, i64); +saturating_impl!(SaturatingSub, saturating_sub, isize); +saturating_impl!(SaturatingSub, saturating_sub, i128); + +/// Performs multiplication that saturates at the numeric bounds instead of overflowing. +pub trait SaturatingMul: Sized + Mul { + /// Saturating multiplication. Computes `self * other`, saturating at the relevant high or low boundary of + /// the type. + fn saturating_mul(&self, v: &Self) -> Self; +} + +saturating_impl!(SaturatingMul, saturating_mul, u8); +saturating_impl!(SaturatingMul, saturating_mul, u16); +saturating_impl!(SaturatingMul, saturating_mul, u32); +saturating_impl!(SaturatingMul, saturating_mul, u64); +saturating_impl!(SaturatingMul, saturating_mul, usize); +saturating_impl!(SaturatingMul, saturating_mul, u128); + +saturating_impl!(SaturatingMul, saturating_mul, i8); +saturating_impl!(SaturatingMul, saturating_mul, i16); +saturating_impl!(SaturatingMul, saturating_mul, i32); +saturating_impl!(SaturatingMul, saturating_mul, i64); +saturating_impl!(SaturatingMul, saturating_mul, isize); +saturating_impl!(SaturatingMul, saturating_mul, i128); + +// TODO: add SaturatingNeg for signed integer primitives once the saturating_neg() API is stable. + +#[test] +fn test_saturating_traits() { + fn saturating_add(a: T, b: T) -> T { + a.saturating_add(&b) + } + fn saturating_sub(a: T, b: T) -> T { + a.saturating_sub(&b) + } + fn saturating_mul(a: T, b: T) -> T { + a.saturating_mul(&b) + } + assert_eq!(saturating_add(255, 1), 255u8); + assert_eq!(saturating_add(127, 1), 127i8); + assert_eq!(saturating_add(-128, -1), -128i8); + assert_eq!(saturating_sub(0, 1), 0u8); + assert_eq!(saturating_sub(-128, 1), -128i8); + assert_eq!(saturating_sub(127, -1), 127i8); + assert_eq!(saturating_mul(255, 2), 255u8); + assert_eq!(saturating_mul(127, 2), 127i8); + assert_eq!(saturating_mul(-128, 2), -128i8); +} diff --git a/tools/vendor/num-traits/src/ops/wrapping.rs b/tools/vendor/num-traits/src/ops/wrapping.rs new file mode 100644 index 0000000000..3a8b331167 --- /dev/null +++ b/tools/vendor/num-traits/src/ops/wrapping.rs @@ -0,0 +1,327 @@ +use core::num::Wrapping; +use core::ops::{Add, Mul, Neg, Shl, Shr, Sub}; + +macro_rules! wrapping_impl { + ($trait_name:ident, $method:ident, $t:ty) => { + impl $trait_name for $t { + #[inline] + fn $method(&self, v: &Self) -> Self { + <$t>::$method(*self, *v) + } + } + }; + ($trait_name:ident, $method:ident, $t:ty, $rhs:ty) => { + impl $trait_name<$rhs> for $t { + #[inline] + fn $method(&self, v: &$rhs) -> Self { + <$t>::$method(*self, *v) + } + } + }; +} + +/// Performs addition that wraps around on overflow. +pub trait WrappingAdd: Sized + Add { + /// Wrapping (modular) addition. Computes `self + other`, wrapping around at the boundary of + /// the type. + fn wrapping_add(&self, v: &Self) -> Self; +} + +wrapping_impl!(WrappingAdd, wrapping_add, u8); +wrapping_impl!(WrappingAdd, wrapping_add, u16); +wrapping_impl!(WrappingAdd, wrapping_add, u32); +wrapping_impl!(WrappingAdd, wrapping_add, u64); +wrapping_impl!(WrappingAdd, wrapping_add, usize); +wrapping_impl!(WrappingAdd, wrapping_add, u128); + +wrapping_impl!(WrappingAdd, wrapping_add, i8); +wrapping_impl!(WrappingAdd, wrapping_add, i16); +wrapping_impl!(WrappingAdd, wrapping_add, i32); +wrapping_impl!(WrappingAdd, wrapping_add, i64); +wrapping_impl!(WrappingAdd, wrapping_add, isize); +wrapping_impl!(WrappingAdd, wrapping_add, i128); + +/// Performs subtraction that wraps around on overflow. +pub trait WrappingSub: Sized + Sub { + /// Wrapping (modular) subtraction. Computes `self - other`, wrapping around at the boundary + /// of the type. + fn wrapping_sub(&self, v: &Self) -> Self; +} + +wrapping_impl!(WrappingSub, wrapping_sub, u8); +wrapping_impl!(WrappingSub, wrapping_sub, u16); +wrapping_impl!(WrappingSub, wrapping_sub, u32); +wrapping_impl!(WrappingSub, wrapping_sub, u64); +wrapping_impl!(WrappingSub, wrapping_sub, usize); +wrapping_impl!(WrappingSub, wrapping_sub, u128); + +wrapping_impl!(WrappingSub, wrapping_sub, i8); +wrapping_impl!(WrappingSub, wrapping_sub, i16); +wrapping_impl!(WrappingSub, wrapping_sub, i32); +wrapping_impl!(WrappingSub, wrapping_sub, i64); +wrapping_impl!(WrappingSub, wrapping_sub, isize); +wrapping_impl!(WrappingSub, wrapping_sub, i128); + +/// Performs multiplication that wraps around on overflow. +pub trait WrappingMul: Sized + Mul { + /// Wrapping (modular) multiplication. Computes `self * other`, wrapping around at the boundary + /// of the type. + fn wrapping_mul(&self, v: &Self) -> Self; +} + +wrapping_impl!(WrappingMul, wrapping_mul, u8); +wrapping_impl!(WrappingMul, wrapping_mul, u16); +wrapping_impl!(WrappingMul, wrapping_mul, u32); +wrapping_impl!(WrappingMul, wrapping_mul, u64); +wrapping_impl!(WrappingMul, wrapping_mul, usize); +wrapping_impl!(WrappingMul, wrapping_mul, u128); + +wrapping_impl!(WrappingMul, wrapping_mul, i8); +wrapping_impl!(WrappingMul, wrapping_mul, i16); +wrapping_impl!(WrappingMul, wrapping_mul, i32); +wrapping_impl!(WrappingMul, wrapping_mul, i64); +wrapping_impl!(WrappingMul, wrapping_mul, isize); +wrapping_impl!(WrappingMul, wrapping_mul, i128); + +macro_rules! wrapping_unary_impl { + ($trait_name:ident, $method:ident, $t:ty) => { + impl $trait_name for $t { + #[inline] + fn $method(&self) -> $t { + <$t>::$method(*self) + } + } + }; +} + +/// Performs a negation that does not panic. +pub trait WrappingNeg: Sized { + /// Wrapping (modular) negation. Computes `-self`, + /// wrapping around at the boundary of the type. + /// + /// Since unsigned types do not have negative equivalents + /// all applications of this function will wrap (except for `-0`). + /// For values smaller than the corresponding signed type's maximum + /// the result is the same as casting the corresponding signed value. + /// Any larger values are equivalent to `MAX + 1 - (val - MAX - 1)` where + /// `MAX` is the corresponding signed type's maximum. + /// + /// ``` + /// use num_traits::WrappingNeg; + /// + /// assert_eq!(100i8.wrapping_neg(), -100); + /// assert_eq!((-100i8).wrapping_neg(), 100); + /// assert_eq!((-128i8).wrapping_neg(), -128); // wrapped! + /// ``` + fn wrapping_neg(&self) -> Self; +} + +wrapping_unary_impl!(WrappingNeg, wrapping_neg, u8); +wrapping_unary_impl!(WrappingNeg, wrapping_neg, u16); +wrapping_unary_impl!(WrappingNeg, wrapping_neg, u32); +wrapping_unary_impl!(WrappingNeg, wrapping_neg, u64); +wrapping_unary_impl!(WrappingNeg, wrapping_neg, usize); +wrapping_unary_impl!(WrappingNeg, wrapping_neg, u128); +wrapping_unary_impl!(WrappingNeg, wrapping_neg, i8); +wrapping_unary_impl!(WrappingNeg, wrapping_neg, i16); +wrapping_unary_impl!(WrappingNeg, wrapping_neg, i32); +wrapping_unary_impl!(WrappingNeg, wrapping_neg, i64); +wrapping_unary_impl!(WrappingNeg, wrapping_neg, isize); +wrapping_unary_impl!(WrappingNeg, wrapping_neg, i128); + +macro_rules! wrapping_shift_impl { + ($trait_name:ident, $method:ident, $t:ty) => { + impl $trait_name for $t { + #[inline] + fn $method(&self, rhs: u32) -> $t { + <$t>::$method(*self, rhs) + } + } + }; +} + +/// Performs a left shift that does not panic. +pub trait WrappingShl: Sized + Shl { + /// Panic-free bitwise shift-left; yields `self << mask(rhs)`, + /// where `mask` removes any high order bits of `rhs` that would + /// cause the shift to exceed the bitwidth of the type. + /// + /// ``` + /// use num_traits::WrappingShl; + /// + /// let x: u16 = 0x0001; + /// + /// assert_eq!(WrappingShl::wrapping_shl(&x, 0), 0x0001); + /// assert_eq!(WrappingShl::wrapping_shl(&x, 1), 0x0002); + /// assert_eq!(WrappingShl::wrapping_shl(&x, 15), 0x8000); + /// assert_eq!(WrappingShl::wrapping_shl(&x, 16), 0x0001); + /// ``` + fn wrapping_shl(&self, rhs: u32) -> Self; +} + +wrapping_shift_impl!(WrappingShl, wrapping_shl, u8); +wrapping_shift_impl!(WrappingShl, wrapping_shl, u16); +wrapping_shift_impl!(WrappingShl, wrapping_shl, u32); +wrapping_shift_impl!(WrappingShl, wrapping_shl, u64); +wrapping_shift_impl!(WrappingShl, wrapping_shl, usize); +wrapping_shift_impl!(WrappingShl, wrapping_shl, u128); + +wrapping_shift_impl!(WrappingShl, wrapping_shl, i8); +wrapping_shift_impl!(WrappingShl, wrapping_shl, i16); +wrapping_shift_impl!(WrappingShl, wrapping_shl, i32); +wrapping_shift_impl!(WrappingShl, wrapping_shl, i64); +wrapping_shift_impl!(WrappingShl, wrapping_shl, isize); +wrapping_shift_impl!(WrappingShl, wrapping_shl, i128); + +/// Performs a right shift that does not panic. +pub trait WrappingShr: Sized + Shr { + /// Panic-free bitwise shift-right; yields `self >> mask(rhs)`, + /// where `mask` removes any high order bits of `rhs` that would + /// cause the shift to exceed the bitwidth of the type. + /// + /// ``` + /// use num_traits::WrappingShr; + /// + /// let x: u16 = 0x8000; + /// + /// assert_eq!(WrappingShr::wrapping_shr(&x, 0), 0x8000); + /// assert_eq!(WrappingShr::wrapping_shr(&x, 1), 0x4000); + /// assert_eq!(WrappingShr::wrapping_shr(&x, 15), 0x0001); + /// assert_eq!(WrappingShr::wrapping_shr(&x, 16), 0x8000); + /// ``` + fn wrapping_shr(&self, rhs: u32) -> Self; +} + +wrapping_shift_impl!(WrappingShr, wrapping_shr, u8); +wrapping_shift_impl!(WrappingShr, wrapping_shr, u16); +wrapping_shift_impl!(WrappingShr, wrapping_shr, u32); +wrapping_shift_impl!(WrappingShr, wrapping_shr, u64); +wrapping_shift_impl!(WrappingShr, wrapping_shr, usize); +wrapping_shift_impl!(WrappingShr, wrapping_shr, u128); + +wrapping_shift_impl!(WrappingShr, wrapping_shr, i8); +wrapping_shift_impl!(WrappingShr, wrapping_shr, i16); +wrapping_shift_impl!(WrappingShr, wrapping_shr, i32); +wrapping_shift_impl!(WrappingShr, wrapping_shr, i64); +wrapping_shift_impl!(WrappingShr, wrapping_shr, isize); +wrapping_shift_impl!(WrappingShr, wrapping_shr, i128); + +// Well this is a bit funny, but all the more appropriate. +impl WrappingAdd for Wrapping +where + Wrapping: Add>, +{ + fn wrapping_add(&self, v: &Self) -> Self { + Wrapping(self.0.wrapping_add(&v.0)) + } +} +impl WrappingSub for Wrapping +where + Wrapping: Sub>, +{ + fn wrapping_sub(&self, v: &Self) -> Self { + Wrapping(self.0.wrapping_sub(&v.0)) + } +} +impl WrappingMul for Wrapping +where + Wrapping: Mul>, +{ + fn wrapping_mul(&self, v: &Self) -> Self { + Wrapping(self.0.wrapping_mul(&v.0)) + } +} +impl WrappingNeg for Wrapping +where + Wrapping: Neg>, +{ + fn wrapping_neg(&self) -> Self { + Wrapping(self.0.wrapping_neg()) + } +} +impl WrappingShl for Wrapping +where + Wrapping: Shl>, +{ + fn wrapping_shl(&self, rhs: u32) -> Self { + Wrapping(self.0.wrapping_shl(rhs)) + } +} +impl WrappingShr for Wrapping +where + Wrapping: Shr>, +{ + fn wrapping_shr(&self, rhs: u32) -> Self { + Wrapping(self.0.wrapping_shr(rhs)) + } +} + +#[test] +fn test_wrapping_traits() { + fn wrapping_add(a: T, b: T) -> T { + a.wrapping_add(&b) + } + fn wrapping_sub(a: T, b: T) -> T { + a.wrapping_sub(&b) + } + fn wrapping_mul(a: T, b: T) -> T { + a.wrapping_mul(&b) + } + fn wrapping_neg(a: T) -> T { + a.wrapping_neg() + } + fn wrapping_shl(a: T, b: u32) -> T { + a.wrapping_shl(b) + } + fn wrapping_shr(a: T, b: u32) -> T { + a.wrapping_shr(b) + } + assert_eq!(wrapping_add(255, 1), 0u8); + assert_eq!(wrapping_sub(0, 1), 255u8); + assert_eq!(wrapping_mul(255, 2), 254u8); + assert_eq!(wrapping_neg(255), 1u8); + assert_eq!(wrapping_shl(255, 8), 255u8); + assert_eq!(wrapping_shr(255, 8), 255u8); + assert_eq!(wrapping_add(255, 1), (Wrapping(255u8) + Wrapping(1u8)).0); + assert_eq!(wrapping_sub(0, 1), (Wrapping(0u8) - Wrapping(1u8)).0); + assert_eq!(wrapping_mul(255, 2), (Wrapping(255u8) * Wrapping(2u8)).0); + assert_eq!(wrapping_neg(255), (-Wrapping(255u8)).0); + assert_eq!(wrapping_shl(255, 8), (Wrapping(255u8) << 8).0); + assert_eq!(wrapping_shr(255, 8), (Wrapping(255u8) >> 8).0); +} + +#[test] +fn wrapping_is_wrappingadd() { + fn require_wrappingadd(_: &T) {} + require_wrappingadd(&Wrapping(42)); +} + +#[test] +fn wrapping_is_wrappingsub() { + fn require_wrappingsub(_: &T) {} + require_wrappingsub(&Wrapping(42)); +} + +#[test] +fn wrapping_is_wrappingmul() { + fn require_wrappingmul(_: &T) {} + require_wrappingmul(&Wrapping(42)); +} + +#[test] +fn wrapping_is_wrappingneg() { + fn require_wrappingneg(_: &T) {} + require_wrappingneg(&Wrapping(42)); +} + +#[test] +fn wrapping_is_wrappingshl() { + fn require_wrappingshl(_: &T) {} + require_wrappingshl(&Wrapping(42)); +} + +#[test] +fn wrapping_is_wrappingshr() { + fn require_wrappingshr(_: &T) {} + require_wrappingshr(&Wrapping(42)); +} diff --git a/tools/vendor/num-traits/src/pow.rs b/tools/vendor/num-traits/src/pow.rs new file mode 100644 index 0000000000..ef51c954d7 --- /dev/null +++ b/tools/vendor/num-traits/src/pow.rs @@ -0,0 +1,242 @@ +use crate::{CheckedMul, One}; +use core::num::Wrapping; +use core::ops::Mul; + +/// Binary operator for raising a value to a power. +pub trait Pow { + /// The result after applying the operator. + type Output; + + /// Returns `self` to the power `rhs`. + /// + /// # Examples + /// + /// ``` + /// use num_traits::Pow; + /// assert_eq!(Pow::pow(10u32, 2u32), 100); + /// ``` + fn pow(self, rhs: RHS) -> Self::Output; +} + +macro_rules! pow_impl { + ($t:ty) => { + pow_impl!($t, u8); + pow_impl!($t, usize); + + // FIXME: these should be possible + // pow_impl!($t, u16); + // pow_impl!($t, u32); + // pow_impl!($t, u64); + }; + ($t:ty, $rhs:ty) => { + pow_impl!($t, $rhs, usize, pow); + }; + ($t:ty, $rhs:ty, $desired_rhs:ty, $method:expr) => { + impl Pow<$rhs> for $t { + type Output = $t; + #[inline] + fn pow(self, rhs: $rhs) -> $t { + ($method)(self, <$desired_rhs>::from(rhs)) + } + } + + impl<'a> Pow<&'a $rhs> for $t { + type Output = $t; + #[inline] + fn pow(self, rhs: &'a $rhs) -> $t { + ($method)(self, <$desired_rhs>::from(*rhs)) + } + } + + impl<'a> Pow<$rhs> for &'a $t { + type Output = $t; + #[inline] + fn pow(self, rhs: $rhs) -> $t { + ($method)(*self, <$desired_rhs>::from(rhs)) + } + } + + impl<'a, 'b> Pow<&'a $rhs> for &'b $t { + type Output = $t; + #[inline] + fn pow(self, rhs: &'a $rhs) -> $t { + ($method)(*self, <$desired_rhs>::from(*rhs)) + } + } + }; +} + +pow_impl!(u8, u8, u32, u8::pow); +pow_impl!(u8, u16, u32, u8::pow); +pow_impl!(u8, u32, u32, u8::pow); +pow_impl!(u8, usize); +pow_impl!(i8, u8, u32, i8::pow); +pow_impl!(i8, u16, u32, i8::pow); +pow_impl!(i8, u32, u32, i8::pow); +pow_impl!(i8, usize); +pow_impl!(u16, u8, u32, u16::pow); +pow_impl!(u16, u16, u32, u16::pow); +pow_impl!(u16, u32, u32, u16::pow); +pow_impl!(u16, usize); +pow_impl!(i16, u8, u32, i16::pow); +pow_impl!(i16, u16, u32, i16::pow); +pow_impl!(i16, u32, u32, i16::pow); +pow_impl!(i16, usize); +pow_impl!(u32, u8, u32, u32::pow); +pow_impl!(u32, u16, u32, u32::pow); +pow_impl!(u32, u32, u32, u32::pow); +pow_impl!(u32, usize); +pow_impl!(i32, u8, u32, i32::pow); +pow_impl!(i32, u16, u32, i32::pow); +pow_impl!(i32, u32, u32, i32::pow); +pow_impl!(i32, usize); +pow_impl!(u64, u8, u32, u64::pow); +pow_impl!(u64, u16, u32, u64::pow); +pow_impl!(u64, u32, u32, u64::pow); +pow_impl!(u64, usize); +pow_impl!(i64, u8, u32, i64::pow); +pow_impl!(i64, u16, u32, i64::pow); +pow_impl!(i64, u32, u32, i64::pow); +pow_impl!(i64, usize); + +pow_impl!(u128, u8, u32, u128::pow); +pow_impl!(u128, u16, u32, u128::pow); +pow_impl!(u128, u32, u32, u128::pow); +pow_impl!(u128, usize); + +pow_impl!(i128, u8, u32, i128::pow); +pow_impl!(i128, u16, u32, i128::pow); +pow_impl!(i128, u32, u32, i128::pow); +pow_impl!(i128, usize); + +pow_impl!(usize, u8, u32, usize::pow); +pow_impl!(usize, u16, u32, usize::pow); +pow_impl!(usize, u32, u32, usize::pow); +pow_impl!(usize, usize); +pow_impl!(isize, u8, u32, isize::pow); +pow_impl!(isize, u16, u32, isize::pow); +pow_impl!(isize, u32, u32, isize::pow); +pow_impl!(isize, usize); +pow_impl!(Wrapping); +pow_impl!(Wrapping); +pow_impl!(Wrapping); +pow_impl!(Wrapping); +pow_impl!(Wrapping); +pow_impl!(Wrapping); +pow_impl!(Wrapping); +pow_impl!(Wrapping); +pow_impl!(Wrapping); +pow_impl!(Wrapping); +pow_impl!(Wrapping); +pow_impl!(Wrapping); + +// FIXME: these should be possible +// pow_impl!(u8, u64); +// pow_impl!(i16, u64); +// pow_impl!(i8, u64); +// pow_impl!(u16, u64); +// pow_impl!(u32, u64); +// pow_impl!(i32, u64); +// pow_impl!(u64, u64); +// pow_impl!(i64, u64); +// pow_impl!(usize, u64); +// pow_impl!(isize, u64); + +#[cfg(any(feature = "std", feature = "libm"))] +mod float_impls { + use super::Pow; + use crate::Float; + + pow_impl!(f32, i8, i32, ::powi); + pow_impl!(f32, u8, i32, ::powi); + pow_impl!(f32, i16, i32, ::powi); + pow_impl!(f32, u16, i32, ::powi); + pow_impl!(f32, i32, i32, ::powi); + pow_impl!(f64, i8, i32, ::powi); + pow_impl!(f64, u8, i32, ::powi); + pow_impl!(f64, i16, i32, ::powi); + pow_impl!(f64, u16, i32, ::powi); + pow_impl!(f64, i32, i32, ::powi); + pow_impl!(f32, f32, f32, ::powf); + pow_impl!(f64, f32, f64, ::powf); + pow_impl!(f64, f64, f64, ::powf); +} + +/// Raises a value to the power of exp, using exponentiation by squaring. +/// +/// Note that `0⁰` (`pow(0, 0)`) returns `1`. Mathematically this is undefined. +/// +/// # Example +/// +/// ```rust +/// use num_traits::pow; +/// +/// assert_eq!(pow(2i8, 4), 16); +/// assert_eq!(pow(6u8, 3), 216); +/// assert_eq!(pow(0u8, 0), 1); // Be aware if this case affects you +/// ``` +#[inline] +pub fn pow>(mut base: T, mut exp: usize) -> T { + if exp == 0 { + return T::one(); + } + + while exp & 1 == 0 { + base = base.clone() * base; + exp >>= 1; + } + if exp == 1 { + return base; + } + + let mut acc = base.clone(); + while exp > 1 { + exp >>= 1; + base = base.clone() * base; + if exp & 1 == 1 { + acc = acc * base.clone(); + } + } + acc +} + +/// Raises a value to the power of exp, returning `None` if an overflow occurred. +/// +/// Note that `0⁰` (`checked_pow(0, 0)`) returns `Some(1)`. Mathematically this is undefined. +/// +/// Otherwise same as the `pow` function. +/// +/// # Example +/// +/// ```rust +/// use num_traits::checked_pow; +/// +/// assert_eq!(checked_pow(2i8, 4), Some(16)); +/// assert_eq!(checked_pow(7i8, 8), None); +/// assert_eq!(checked_pow(7u32, 8), Some(5_764_801)); +/// assert_eq!(checked_pow(0u32, 0), Some(1)); // Be aware if this case affect you +/// ``` +#[inline] +pub fn checked_pow(mut base: T, mut exp: usize) -> Option { + if exp == 0 { + return Some(T::one()); + } + + while exp & 1 == 0 { + base = base.checked_mul(&base)?; + exp >>= 1; + } + if exp == 1 { + return Some(base); + } + + let mut acc = base.clone(); + while exp > 1 { + exp >>= 1; + base = base.checked_mul(&base)?; + if exp & 1 == 1 { + acc = acc.checked_mul(&base)?; + } + } + Some(acc) +} diff --git a/tools/vendor/num-traits/src/real.rs b/tools/vendor/num-traits/src/real.rs new file mode 100644 index 0000000000..25ec873258 --- /dev/null +++ b/tools/vendor/num-traits/src/real.rs @@ -0,0 +1,834 @@ +#![cfg(any(feature = "std", feature = "libm"))] + +use core::ops::Neg; + +use crate::{Float, Num, NumCast}; + +// NOTE: These doctests have the same issue as those in src/float.rs. +// They're testing the inherent methods directly, and not those of `Real`. + +/// A trait for real number types that do not necessarily have +/// floating-point-specific characteristics such as NaN and infinity. +/// +/// See [this Wikipedia article](https://en.wikipedia.org/wiki/Real_data_type) +/// for a list of data types that could meaningfully implement this trait. +/// +/// This trait is only available with the `std` feature, or with the `libm` feature otherwise. +pub trait Real: Num + Copy + NumCast + PartialOrd + Neg { + /// Returns the smallest finite value that this type can represent. + /// + /// ``` + /// use num_traits::real::Real; + /// use std::f64; + /// + /// let x: f64 = Real::min_value(); + /// + /// assert_eq!(x, f64::MIN); + /// ``` + fn min_value() -> Self; + + /// Returns the smallest positive, normalized value that this type can represent. + /// + /// ``` + /// use num_traits::real::Real; + /// use std::f64; + /// + /// let x: f64 = Real::min_positive_value(); + /// + /// assert_eq!(x, f64::MIN_POSITIVE); + /// ``` + fn min_positive_value() -> Self; + + /// Returns epsilon, a small positive value. + /// + /// ``` + /// use num_traits::real::Real; + /// use std::f64; + /// + /// let x: f64 = Real::epsilon(); + /// + /// assert_eq!(x, f64::EPSILON); + /// ``` + /// + /// # Panics + /// + /// The default implementation will panic if `f32::EPSILON` cannot + /// be cast to `Self`. + fn epsilon() -> Self; + + /// Returns the largest finite value that this type can represent. + /// + /// ``` + /// use num_traits::real::Real; + /// use std::f64; + /// + /// let x: f64 = Real::max_value(); + /// assert_eq!(x, f64::MAX); + /// ``` + fn max_value() -> Self; + + /// Returns the largest integer less than or equal to a number. + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let f = 3.99; + /// let g = 3.0; + /// + /// assert_eq!(f.floor(), 3.0); + /// assert_eq!(g.floor(), 3.0); + /// ``` + fn floor(self) -> Self; + + /// Returns the smallest integer greater than or equal to a number. + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let f = 3.01; + /// let g = 4.0; + /// + /// assert_eq!(f.ceil(), 4.0); + /// assert_eq!(g.ceil(), 4.0); + /// ``` + fn ceil(self) -> Self; + + /// Returns the nearest integer to a number. Round half-way cases away from + /// `0.0`. + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let f = 3.3; + /// let g = -3.3; + /// + /// assert_eq!(f.round(), 3.0); + /// assert_eq!(g.round(), -3.0); + /// ``` + fn round(self) -> Self; + + /// Return the integer part of a number. + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let f = 3.3; + /// let g = -3.7; + /// + /// assert_eq!(f.trunc(), 3.0); + /// assert_eq!(g.trunc(), -3.0); + /// ``` + fn trunc(self) -> Self; + + /// Returns the fractional part of a number. + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let x = 3.5; + /// let y = -3.5; + /// let abs_difference_x = (x.fract() - 0.5).abs(); + /// let abs_difference_y = (y.fract() - (-0.5)).abs(); + /// + /// assert!(abs_difference_x < 1e-10); + /// assert!(abs_difference_y < 1e-10); + /// ``` + fn fract(self) -> Self; + + /// Computes the absolute value of `self`. Returns `Float::nan()` if the + /// number is `Float::nan()`. + /// + /// ``` + /// use num_traits::real::Real; + /// use std::f64; + /// + /// let x = 3.5; + /// let y = -3.5; + /// + /// let abs_difference_x = (x.abs() - x).abs(); + /// let abs_difference_y = (y.abs() - (-y)).abs(); + /// + /// assert!(abs_difference_x < 1e-10); + /// assert!(abs_difference_y < 1e-10); + /// + /// assert!(::num_traits::Float::is_nan(f64::NAN.abs())); + /// ``` + fn abs(self) -> Self; + + /// Returns a number that represents the sign of `self`. + /// + /// - `1.0` if the number is positive, `+0.0` or `Float::infinity()` + /// - `-1.0` if the number is negative, `-0.0` or `Float::neg_infinity()` + /// - `Float::nan()` if the number is `Float::nan()` + /// + /// ``` + /// use num_traits::real::Real; + /// use std::f64; + /// + /// let f = 3.5; + /// + /// assert_eq!(f.signum(), 1.0); + /// assert_eq!(f64::NEG_INFINITY.signum(), -1.0); + /// + /// assert!(f64::NAN.signum().is_nan()); + /// ``` + fn signum(self) -> Self; + + /// Returns `true` if `self` is positive, including `+0.0`, + /// `Float::infinity()`, and with newer versions of Rust `f64::NAN`. + /// + /// ``` + /// use num_traits::real::Real; + /// use std::f64; + /// + /// let neg_nan: f64 = -f64::NAN; + /// + /// let f = 7.0; + /// let g = -7.0; + /// + /// assert!(f.is_sign_positive()); + /// assert!(!g.is_sign_positive()); + /// assert!(!neg_nan.is_sign_positive()); + /// ``` + fn is_sign_positive(self) -> bool; + + /// Returns `true` if `self` is negative, including `-0.0`, + /// `Float::neg_infinity()`, and with newer versions of Rust `-f64::NAN`. + /// + /// ``` + /// use num_traits::real::Real; + /// use std::f64; + /// + /// let nan: f64 = f64::NAN; + /// + /// let f = 7.0; + /// let g = -7.0; + /// + /// assert!(!f.is_sign_negative()); + /// assert!(g.is_sign_negative()); + /// assert!(!nan.is_sign_negative()); + /// ``` + fn is_sign_negative(self) -> bool; + + /// Fused multiply-add. Computes `(self * a) + b` with only one rounding + /// error, yielding a more accurate result than an unfused multiply-add. + /// + /// Using `mul_add` can be more performant than an unfused multiply-add if + /// the target architecture has a dedicated `fma` CPU instruction. + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let m = 10.0; + /// let x = 4.0; + /// let b = 60.0; + /// + /// // 100.0 + /// let abs_difference = (m.mul_add(x, b) - (m*x + b)).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn mul_add(self, a: Self, b: Self) -> Self; + + /// Take the reciprocal (inverse) of a number, `1/x`. + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let x = 2.0; + /// let abs_difference = (x.recip() - (1.0/x)).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn recip(self) -> Self; + + /// Raise a number to an integer power. + /// + /// Using this function is generally faster than using `powf` + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let x = 2.0; + /// let abs_difference = (x.powi(2) - x*x).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn powi(self, n: i32) -> Self; + + /// Raise a number to a real number power. + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let x = 2.0; + /// let abs_difference = (x.powf(2.0) - x*x).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn powf(self, n: Self) -> Self; + + /// Take the square root of a number. + /// + /// Returns NaN if `self` is a negative floating-point number. + /// + /// # Panics + /// + /// If the implementing type doesn't support NaN, this method should panic if `self < 0`. + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let positive = 4.0; + /// let negative = -4.0; + /// + /// let abs_difference = (positive.sqrt() - 2.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// assert!(::num_traits::Float::is_nan(negative.sqrt())); + /// ``` + fn sqrt(self) -> Self; + + /// Returns `e^(self)`, (the exponential function). + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let one = 1.0; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn exp(self) -> Self; + + /// Returns `2^(self)`. + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let f = 2.0; + /// + /// // 2^2 - 4 == 0 + /// let abs_difference = (f.exp2() - 4.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn exp2(self) -> Self; + + /// Returns the natural logarithm of the number. + /// + /// # Panics + /// + /// If `self <= 0` and this type does not support a NaN representation, this function should panic. + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let one = 1.0; + /// // e^1 + /// let e = one.exp(); + /// + /// // ln(e) - 1 == 0 + /// let abs_difference = (e.ln() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn ln(self) -> Self; + + /// Returns the logarithm of the number with respect to an arbitrary base. + /// + /// # Panics + /// + /// If `self <= 0` and this type does not support a NaN representation, this function should panic. + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let ten = 10.0; + /// let two = 2.0; + /// + /// // log10(10) - 1 == 0 + /// let abs_difference_10 = (ten.log(10.0) - 1.0).abs(); + /// + /// // log2(2) - 1 == 0 + /// let abs_difference_2 = (two.log(2.0) - 1.0).abs(); + /// + /// assert!(abs_difference_10 < 1e-10); + /// assert!(abs_difference_2 < 1e-10); + /// ``` + fn log(self, base: Self) -> Self; + + /// Returns the base 2 logarithm of the number. + /// + /// # Panics + /// + /// If `self <= 0` and this type does not support a NaN representation, this function should panic. + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let two = 2.0; + /// + /// // log2(2) - 1 == 0 + /// let abs_difference = (two.log2() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn log2(self) -> Self; + + /// Returns the base 10 logarithm of the number. + /// + /// # Panics + /// + /// If `self <= 0` and this type does not support a NaN representation, this function should panic. + /// + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let ten = 10.0; + /// + /// // log10(10) - 1 == 0 + /// let abs_difference = (ten.log10() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn log10(self) -> Self; + + /// Converts radians to degrees. + /// + /// ``` + /// use std::f64::consts; + /// + /// let angle = consts::PI; + /// + /// let abs_difference = (angle.to_degrees() - 180.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn to_degrees(self) -> Self; + + /// Converts degrees to radians. + /// + /// ``` + /// use std::f64::consts; + /// + /// let angle = 180.0_f64; + /// + /// let abs_difference = (angle.to_radians() - consts::PI).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn to_radians(self) -> Self; + + /// Returns the maximum of the two numbers. + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let x = 1.0; + /// let y = 2.0; + /// + /// assert_eq!(x.max(y), y); + /// ``` + fn max(self, other: Self) -> Self; + + /// Returns the minimum of the two numbers. + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let x = 1.0; + /// let y = 2.0; + /// + /// assert_eq!(x.min(y), x); + /// ``` + fn min(self, other: Self) -> Self; + + /// The positive difference of two numbers. + /// + /// * If `self <= other`: `0:0` + /// * Else: `self - other` + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let x = 3.0; + /// let y = -3.0; + /// + /// let abs_difference_x = (x.abs_sub(1.0) - 2.0).abs(); + /// let abs_difference_y = (y.abs_sub(1.0) - 0.0).abs(); + /// + /// assert!(abs_difference_x < 1e-10); + /// assert!(abs_difference_y < 1e-10); + /// ``` + fn abs_sub(self, other: Self) -> Self; + + /// Take the cubic root of a number. + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let x = 8.0; + /// + /// // x^(1/3) - 2 == 0 + /// let abs_difference = (x.cbrt() - 2.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn cbrt(self) -> Self; + + /// Calculate the length of the hypotenuse of a right-angle triangle given + /// legs of length `x` and `y`. + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let x = 2.0; + /// let y = 3.0; + /// + /// // sqrt(x^2 + y^2) + /// let abs_difference = (x.hypot(y) - (x.powi(2) + y.powi(2)).sqrt()).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn hypot(self, other: Self) -> Self; + + /// Computes the sine of a number (in radians). + /// + /// ``` + /// use num_traits::real::Real; + /// use std::f64; + /// + /// let x = f64::consts::PI/2.0; + /// + /// let abs_difference = (x.sin() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn sin(self) -> Self; + + /// Computes the cosine of a number (in radians). + /// + /// ``` + /// use num_traits::real::Real; + /// use std::f64; + /// + /// let x = 2.0*f64::consts::PI; + /// + /// let abs_difference = (x.cos() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn cos(self) -> Self; + + /// Computes the tangent of a number (in radians). + /// + /// ``` + /// use num_traits::real::Real; + /// use std::f64; + /// + /// let x = f64::consts::PI/4.0; + /// let abs_difference = (x.tan() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-14); + /// ``` + fn tan(self) -> Self; + + /// Computes the arcsine of a number. Return value is in radians in + /// the range [-pi/2, pi/2] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Panics + /// + /// If this type does not support a NaN representation, this function should panic + /// if the number is outside the range [-1, 1]. + /// + /// ``` + /// use num_traits::real::Real; + /// use std::f64; + /// + /// let f = f64::consts::PI / 2.0; + /// + /// // asin(sin(pi/2)) + /// let abs_difference = (f.sin().asin() - f64::consts::PI / 2.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn asin(self) -> Self; + + /// Computes the arccosine of a number. Return value is in radians in + /// the range [0, pi] or NaN if the number is outside the range + /// [-1, 1]. + /// + /// # Panics + /// + /// If this type does not support a NaN representation, this function should panic + /// if the number is outside the range [-1, 1]. + /// + /// ``` + /// use num_traits::real::Real; + /// use std::f64; + /// + /// let f = f64::consts::PI / 4.0; + /// + /// // acos(cos(pi/4)) + /// let abs_difference = (f.cos().acos() - f64::consts::PI / 4.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn acos(self) -> Self; + + /// Computes the arctangent of a number. Return value is in radians in the + /// range [-pi/2, pi/2]; + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let f = 1.0; + /// + /// // atan(tan(1)) + /// let abs_difference = (f.tan().atan() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn atan(self) -> Self; + + /// Computes the four quadrant arctangent of `self` (`y`) and `other` (`x`). + /// + /// * `x = 0`, `y = 0`: `0` + /// * `x >= 0`: `arctan(y/x)` -> `[-pi/2, pi/2]` + /// * `y >= 0`: `arctan(y/x) + pi` -> `(pi/2, pi]` + /// * `y < 0`: `arctan(y/x) - pi` -> `(-pi, -pi/2)` + /// + /// ``` + /// use num_traits::real::Real; + /// use std::f64; + /// + /// let pi = f64::consts::PI; + /// // All angles from horizontal right (+x) + /// // 45 deg counter-clockwise + /// let x1 = 3.0; + /// let y1 = -3.0; + /// + /// // 135 deg clockwise + /// let x2 = -3.0; + /// let y2 = 3.0; + /// + /// let abs_difference_1 = (y1.atan2(x1) - (-pi/4.0)).abs(); + /// let abs_difference_2 = (y2.atan2(x2) - 3.0*pi/4.0).abs(); + /// + /// assert!(abs_difference_1 < 1e-10); + /// assert!(abs_difference_2 < 1e-10); + /// ``` + fn atan2(self, other: Self) -> Self; + + /// Simultaneously computes the sine and cosine of the number, `x`. Returns + /// `(sin(x), cos(x))`. + /// + /// ``` + /// use num_traits::real::Real; + /// use std::f64; + /// + /// let x = f64::consts::PI/4.0; + /// let f = x.sin_cos(); + /// + /// let abs_difference_0 = (f.0 - x.sin()).abs(); + /// let abs_difference_1 = (f.1 - x.cos()).abs(); + /// + /// assert!(abs_difference_0 < 1e-10); + /// assert!(abs_difference_0 < 1e-10); + /// ``` + fn sin_cos(self) -> (Self, Self); + + /// Returns `e^(self) - 1` in a way that is accurate even if the + /// number is close to zero. + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let x = 7.0; + /// + /// // e^(ln(7)) - 1 + /// let abs_difference = (x.ln().exp_m1() - 6.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn exp_m1(self) -> Self; + + /// Returns `ln(1+n)` (natural logarithm) more accurately than if + /// the operations were performed separately. + /// + /// # Panics + /// + /// If this type does not support a NaN representation, this function should panic + /// if `self-1 <= 0`. + /// + /// ``` + /// use num_traits::real::Real; + /// use std::f64; + /// + /// let x = f64::consts::E - 1.0; + /// + /// // ln(1 + (e - 1)) == ln(e) == 1 + /// let abs_difference = (x.ln_1p() - 1.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn ln_1p(self) -> Self; + + /// Hyperbolic sine function. + /// + /// ``` + /// use num_traits::real::Real; + /// use std::f64; + /// + /// let e = f64::consts::E; + /// let x = 1.0; + /// + /// let f = x.sinh(); + /// // Solving sinh() at 1 gives `(e^2-1)/(2e)` + /// let g = (e*e - 1.0)/(2.0*e); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + fn sinh(self) -> Self; + + /// Hyperbolic cosine function. + /// + /// ``` + /// use num_traits::real::Real; + /// use std::f64; + /// + /// let e = f64::consts::E; + /// let x = 1.0; + /// let f = x.cosh(); + /// // Solving cosh() at 1 gives this result + /// let g = (e*e + 1.0)/(2.0*e); + /// let abs_difference = (f - g).abs(); + /// + /// // Same result + /// assert!(abs_difference < 1.0e-10); + /// ``` + fn cosh(self) -> Self; + + /// Hyperbolic tangent function. + /// + /// ``` + /// use num_traits::real::Real; + /// use std::f64; + /// + /// let e = f64::consts::E; + /// let x = 1.0; + /// + /// let f = x.tanh(); + /// // Solving tanh() at 1 gives `(1 - e^(-2))/(1 + e^(-2))` + /// let g = (1.0 - e.powi(-2))/(1.0 + e.powi(-2)); + /// let abs_difference = (f - g).abs(); + /// + /// assert!(abs_difference < 1.0e-10); + /// ``` + fn tanh(self) -> Self; + + /// Inverse hyperbolic sine function. + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let x = 1.0; + /// let f = x.sinh().asinh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference < 1.0e-10); + /// ``` + fn asinh(self) -> Self; + + /// Inverse hyperbolic cosine function. + /// + /// ``` + /// use num_traits::real::Real; + /// + /// let x = 1.0; + /// let f = x.cosh().acosh(); + /// + /// let abs_difference = (f - x).abs(); + /// + /// assert!(abs_difference < 1.0e-10); + /// ``` + fn acosh(self) -> Self; + + /// Inverse hyperbolic tangent function. + /// + /// ``` + /// use num_traits::real::Real; + /// use std::f64; + /// + /// let e = f64::consts::E; + /// let f = e.tanh().atanh(); + /// + /// let abs_difference = (f - e).abs(); + /// + /// assert!(abs_difference < 1.0e-10); + /// ``` + fn atanh(self) -> Self; +} + +impl Real for T { + forward! { + Float::min_value() -> Self; + Float::min_positive_value() -> Self; + Float::epsilon() -> Self; + Float::max_value() -> Self; + } + forward! { + Float::floor(self) -> Self; + Float::ceil(self) -> Self; + Float::round(self) -> Self; + Float::trunc(self) -> Self; + Float::fract(self) -> Self; + Float::abs(self) -> Self; + Float::signum(self) -> Self; + Float::is_sign_positive(self) -> bool; + Float::is_sign_negative(self) -> bool; + Float::mul_add(self, a: Self, b: Self) -> Self; + Float::recip(self) -> Self; + Float::powi(self, n: i32) -> Self; + Float::powf(self, n: Self) -> Self; + Float::sqrt(self) -> Self; + Float::exp(self) -> Self; + Float::exp2(self) -> Self; + Float::ln(self) -> Self; + Float::log(self, base: Self) -> Self; + Float::log2(self) -> Self; + Float::log10(self) -> Self; + Float::to_degrees(self) -> Self; + Float::to_radians(self) -> Self; + Float::max(self, other: Self) -> Self; + Float::min(self, other: Self) -> Self; + Float::abs_sub(self, other: Self) -> Self; + Float::cbrt(self) -> Self; + Float::hypot(self, other: Self) -> Self; + Float::sin(self) -> Self; + Float::cos(self) -> Self; + Float::tan(self) -> Self; + Float::asin(self) -> Self; + Float::acos(self) -> Self; + Float::atan(self) -> Self; + Float::atan2(self, other: Self) -> Self; + Float::sin_cos(self) -> (Self, Self); + Float::exp_m1(self) -> Self; + Float::ln_1p(self) -> Self; + Float::sinh(self) -> Self; + Float::cosh(self) -> Self; + Float::tanh(self) -> Self; + Float::asinh(self) -> Self; + Float::acosh(self) -> Self; + Float::atanh(self) -> Self; + } +} diff --git a/tools/vendor/num-traits/src/sign.rs b/tools/vendor/num-traits/src/sign.rs new file mode 100644 index 0000000000..a0d6b0fd6c --- /dev/null +++ b/tools/vendor/num-traits/src/sign.rs @@ -0,0 +1,216 @@ +use core::num::Wrapping; +use core::ops::Neg; + +use crate::float::FloatCore; +use crate::Num; + +/// Useful functions for signed numbers (i.e. numbers that can be negative). +pub trait Signed: Sized + Num + Neg { + /// Computes the absolute value. + /// + /// For `f32` and `f64`, `NaN` will be returned if the number is `NaN`. + /// + /// For signed integers, `::MIN` will be returned if the number is `::MIN`. + fn abs(&self) -> Self; + + /// The positive difference of two numbers. + /// + /// Returns `zero` if the number is less than or equal to `other`, otherwise the difference + /// between `self` and `other` is returned. + fn abs_sub(&self, other: &Self) -> Self; + + /// Returns the sign of the number. + /// + /// For `f32` and `f64`: + /// + /// * `1.0` if the number is positive, `+0.0` or `INFINITY` + /// * `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY` + /// * `NaN` if the number is `NaN` + /// + /// For signed integers: + /// + /// * `0` if the number is zero + /// * `1` if the number is positive + /// * `-1` if the number is negative + fn signum(&self) -> Self; + + /// Returns true if the number is positive and false if the number is zero or negative. + fn is_positive(&self) -> bool; + + /// Returns true if the number is negative and false if the number is zero or positive. + fn is_negative(&self) -> bool; +} + +macro_rules! signed_impl { + ($($t:ty)*) => ($( + impl Signed for $t { + #[inline] + fn abs(&self) -> $t { + if self.is_negative() { -*self } else { *self } + } + + #[inline] + fn abs_sub(&self, other: &$t) -> $t { + if *self <= *other { 0 } else { *self - *other } + } + + #[inline] + fn signum(&self) -> $t { + match *self { + n if n > 0 => 1, + 0 => 0, + _ => -1, + } + } + + #[inline] + fn is_positive(&self) -> bool { *self > 0 } + + #[inline] + fn is_negative(&self) -> bool { *self < 0 } + } + )*) +} + +signed_impl!(isize i8 i16 i32 i64 i128); + +impl Signed for Wrapping +where + Wrapping: Num + Neg>, +{ + #[inline] + fn abs(&self) -> Self { + Wrapping(self.0.abs()) + } + + #[inline] + fn abs_sub(&self, other: &Self) -> Self { + Wrapping(self.0.abs_sub(&other.0)) + } + + #[inline] + fn signum(&self) -> Self { + Wrapping(self.0.signum()) + } + + #[inline] + fn is_positive(&self) -> bool { + self.0.is_positive() + } + + #[inline] + fn is_negative(&self) -> bool { + self.0.is_negative() + } +} + +macro_rules! signed_float_impl { + ($t:ty) => { + impl Signed for $t { + /// Computes the absolute value. Returns `NAN` if the number is `NAN`. + #[inline] + fn abs(&self) -> $t { + FloatCore::abs(*self) + } + + /// The positive difference of two numbers. Returns `0.0` if the number is + /// less than or equal to `other`, otherwise the difference between`self` + /// and `other` is returned. + #[inline] + fn abs_sub(&self, other: &$t) -> $t { + if *self <= *other { + 0. + } else { + *self - *other + } + } + + /// # Returns + /// + /// - `1.0` if the number is positive, `+0.0` or `INFINITY` + /// - `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY` + /// - `NAN` if the number is NaN + #[inline] + fn signum(&self) -> $t { + FloatCore::signum(*self) + } + + /// Returns `true` if the number is positive, including `+0.0` and `INFINITY` + #[inline] + fn is_positive(&self) -> bool { + FloatCore::is_sign_positive(*self) + } + + /// Returns `true` if the number is negative, including `-0.0` and `NEG_INFINITY` + #[inline] + fn is_negative(&self) -> bool { + FloatCore::is_sign_negative(*self) + } + } + }; +} + +signed_float_impl!(f32); +signed_float_impl!(f64); + +/// Computes the absolute value. +/// +/// For `f32` and `f64`, `NaN` will be returned if the number is `NaN` +/// +/// For signed integers, `::MIN` will be returned if the number is `::MIN`. +#[inline(always)] +pub fn abs(value: T) -> T { + value.abs() +} + +/// The positive difference of two numbers. +/// +/// Returns zero if `x` is less than or equal to `y`, otherwise the difference +/// between `x` and `y` is returned. +#[inline(always)] +pub fn abs_sub(x: T, y: T) -> T { + x.abs_sub(&y) +} + +/// Returns the sign of the number. +/// +/// For `f32` and `f64`: +/// +/// * `1.0` if the number is positive, `+0.0` or `INFINITY` +/// * `-1.0` if the number is negative, `-0.0` or `NEG_INFINITY` +/// * `NaN` if the number is `NaN` +/// +/// For signed integers: +/// +/// * `0` if the number is zero +/// * `1` if the number is positive +/// * `-1` if the number is negative +#[inline(always)] +pub fn signum(value: T) -> T { + value.signum() +} + +/// A trait for values which cannot be negative +pub trait Unsigned: Num {} + +macro_rules! empty_trait_impl { + ($name:ident for $($t:ty)*) => ($( + impl $name for $t {} + )*) +} + +empty_trait_impl!(Unsigned for usize u8 u16 u32 u64 u128); + +impl Unsigned for Wrapping where Wrapping: Num {} + +#[test] +fn unsigned_wrapping_is_unsigned() { + fn require_unsigned(_: &T) {} + require_unsigned(&Wrapping(42_u32)); +} + +#[test] +fn signed_wrapping_is_signed() { + fn require_signed(_: &T) {} + require_signed(&Wrapping(-42)); +} diff --git a/tools/vendor/num-traits/tests/cast.rs b/tools/vendor/num-traits/tests/cast.rs new file mode 100644 index 0000000000..4f01d749ef --- /dev/null +++ b/tools/vendor/num-traits/tests/cast.rs @@ -0,0 +1,387 @@ +//! Tests of `num_traits::cast`. + +#![cfg_attr(not(feature = "std"), no_std)] + +use num_traits::cast::*; +use num_traits::Bounded; + +use core::{f32, f64}; +use core::{i128, i16, i32, i64, i8, isize}; +use core::{u128, u16, u32, u64, u8, usize}; + +use core::fmt::Debug; +use core::mem; +use core::num::Wrapping; + +#[test] +fn to_primitive_float() { + let f32_toolarge = 1e39f64; + assert_eq!(f32_toolarge.to_f32(), Some(f32::INFINITY)); + assert_eq!((-f32_toolarge).to_f32(), Some(f32::NEG_INFINITY)); + assert_eq!((f32::MAX as f64).to_f32(), Some(f32::MAX)); + assert_eq!((-f32::MAX as f64).to_f32(), Some(-f32::MAX)); + assert_eq!(f64::INFINITY.to_f32(), Some(f32::INFINITY)); + assert_eq!((f64::NEG_INFINITY).to_f32(), Some(f32::NEG_INFINITY)); + assert!((f64::NAN).to_f32().map_or(false, |f| f.is_nan())); +} + +#[test] +fn wrapping_to_primitive() { + macro_rules! test_wrapping_to_primitive { + ($($t:ty)+) => { + $({ + let i: $t = 0; + let w = Wrapping(i); + assert_eq!(i.to_u8(), w.to_u8()); + assert_eq!(i.to_u16(), w.to_u16()); + assert_eq!(i.to_u32(), w.to_u32()); + assert_eq!(i.to_u64(), w.to_u64()); + assert_eq!(i.to_usize(), w.to_usize()); + assert_eq!(i.to_i8(), w.to_i8()); + assert_eq!(i.to_i16(), w.to_i16()); + assert_eq!(i.to_i32(), w.to_i32()); + assert_eq!(i.to_i64(), w.to_i64()); + assert_eq!(i.to_isize(), w.to_isize()); + assert_eq!(i.to_f32(), w.to_f32()); + assert_eq!(i.to_f64(), w.to_f64()); + })+ + }; + } + + test_wrapping_to_primitive!(usize u8 u16 u32 u64 isize i8 i16 i32 i64); +} + +#[test] +fn wrapping_is_toprimitive() { + fn require_toprimitive(_: &T) {} + require_toprimitive(&Wrapping(42)); +} + +#[test] +fn wrapping_is_fromprimitive() { + fn require_fromprimitive(_: &T) {} + require_fromprimitive(&Wrapping(42)); +} + +#[test] +fn wrapping_is_numcast() { + fn require_numcast(_: &T) {} + require_numcast(&Wrapping(42)); +} + +#[test] +fn as_primitive() { + let x: f32 = (1.625f64).as_(); + assert_eq!(x, 1.625f32); + + let x: f32 = (3.14159265358979323846f64).as_(); + assert_eq!(x, 3.1415927f32); + + let x: u8 = (768i16).as_(); + assert_eq!(x, 0); +} + +#[test] +fn float_to_integer_checks_overflow() { + // This will overflow an i32 + let source: f64 = 1.0e+123f64; + + // Expect the overflow to be caught + assert_eq!(cast::(source), None); +} + +#[test] +fn cast_to_int_checks_overflow() { + let big_f: f64 = 1.0e123; + let normal_f: f64 = 1.0; + let small_f: f64 = -1.0e123; + assert_eq!(None, cast::(big_f)); + assert_eq!(None, cast::(big_f)); + assert_eq!(None, cast::(big_f)); + assert_eq!(None, cast::(big_f)); + assert_eq!(None, cast::(big_f)); + + assert_eq!(Some(normal_f as isize), cast::(normal_f)); + assert_eq!(Some(normal_f as i8), cast::(normal_f)); + assert_eq!(Some(normal_f as i16), cast::(normal_f)); + assert_eq!(Some(normal_f as i32), cast::(normal_f)); + assert_eq!(Some(normal_f as i64), cast::(normal_f)); + + assert_eq!(None, cast::(small_f)); + assert_eq!(None, cast::(small_f)); + assert_eq!(None, cast::(small_f)); + assert_eq!(None, cast::(small_f)); + assert_eq!(None, cast::(small_f)); +} + +#[test] +fn cast_to_unsigned_int_checks_overflow() { + let big_f: f64 = 1.0e123; + let normal_f: f64 = 1.0; + let small_f: f64 = -1.0e123; + assert_eq!(None, cast::(big_f)); + assert_eq!(None, cast::(big_f)); + assert_eq!(None, cast::(big_f)); + assert_eq!(None, cast::(big_f)); + assert_eq!(None, cast::(big_f)); + + assert_eq!(Some(normal_f as usize), cast::(normal_f)); + assert_eq!(Some(normal_f as u8), cast::(normal_f)); + assert_eq!(Some(normal_f as u16), cast::(normal_f)); + assert_eq!(Some(normal_f as u32), cast::(normal_f)); + assert_eq!(Some(normal_f as u64), cast::(normal_f)); + + assert_eq!(None, cast::(small_f)); + assert_eq!(None, cast::(small_f)); + assert_eq!(None, cast::(small_f)); + assert_eq!(None, cast::(small_f)); + assert_eq!(None, cast::(small_f)); +} + +#[test] +fn cast_to_i128_checks_overflow() { + let big_f: f64 = 1.0e123; + let normal_f: f64 = 1.0; + let small_f: f64 = -1.0e123; + assert_eq!(None, cast::(big_f)); + assert_eq!(None, cast::(big_f)); + + assert_eq!(Some(normal_f as i128), cast::(normal_f)); + assert_eq!(Some(normal_f as u128), cast::(normal_f)); + + assert_eq!(None, cast::(small_f)); + assert_eq!(None, cast::(small_f)); +} + +#[cfg(feature = "std")] +fn dbg(args: ::core::fmt::Arguments<'_>) { + println!("{}", args); +} + +#[cfg(not(feature = "std"))] +fn dbg(_: ::core::fmt::Arguments) {} + +// Rust 1.8 doesn't handle cfg on macros correctly +macro_rules! dbg { ($($tok:tt)*) => { dbg(format_args!($($tok)*)) } } + +macro_rules! float_test_edge { + ($f:ident -> $($t:ident)+) => { $({ + dbg!("testing cast edge cases for {} -> {}", stringify!($f), stringify!($t)); + + let small = if $t::MIN == 0 || mem::size_of::<$t>() < mem::size_of::<$f>() { + $t::MIN as $f - 1.0 + } else { + ($t::MIN as $f).raw_inc().floor() + }; + let fmin = small.raw_dec(); + dbg!(" testing min {}\n\tvs. {:.0}\n\tand {:.0}", $t::MIN, fmin, small); + assert_eq!(Some($t::MIN), cast::<$f, $t>($t::MIN as $f)); + assert_eq!(Some($t::MIN), cast::<$f, $t>(fmin)); + assert_eq!(None, cast::<$f, $t>(small)); + + let (max, large) = if mem::size_of::<$t>() < mem::size_of::<$f>() { + ($t::MAX, $t::MAX as $f + 1.0) + } else { + let large = $t::MAX as $f; // rounds up! + let max = large.raw_dec() as $t; // the next smallest possible + assert_eq!(max.count_ones(), $f::MANTISSA_DIGITS); + (max, large) + }; + let fmax = large.raw_dec(); + dbg!(" testing max {}\n\tvs. {:.0}\n\tand {:.0}", max, fmax, large); + assert_eq!(Some(max), cast::<$f, $t>(max as $f)); + assert_eq!(Some(max), cast::<$f, $t>(fmax)); + assert_eq!(None, cast::<$f, $t>(large)); + + dbg!(" testing non-finite values"); + assert_eq!(None, cast::<$f, $t>($f::NAN)); + assert_eq!(None, cast::<$f, $t>($f::INFINITY)); + assert_eq!(None, cast::<$f, $t>($f::NEG_INFINITY)); + })+} +} + +trait RawOffset: Sized { + fn raw_inc(self) -> Self; + fn raw_dec(self) -> Self; +} + +impl RawOffset for f32 { + fn raw_inc(self) -> Self { + Self::from_bits(self.to_bits() + 1) + } + + fn raw_dec(self) -> Self { + Self::from_bits(self.to_bits() - 1) + } +} + +impl RawOffset for f64 { + fn raw_inc(self) -> Self { + Self::from_bits(self.to_bits() + 1) + } + + fn raw_dec(self) -> Self { + Self::from_bits(self.to_bits() - 1) + } +} + +#[test] +fn cast_float_to_int_edge_cases() { + float_test_edge!(f32 -> isize i8 i16 i32 i64); + float_test_edge!(f32 -> usize u8 u16 u32 u64); + float_test_edge!(f64 -> isize i8 i16 i32 i64); + float_test_edge!(f64 -> usize u8 u16 u32 u64); +} + +#[test] +fn cast_float_to_i128_edge_cases() { + float_test_edge!(f32 -> i128 u128); + float_test_edge!(f64 -> i128 u128); +} + +macro_rules! int_test_edge { + ($f:ident -> { $($t:ident)+ } with $BigS:ident $BigU:ident ) => { $({ + #[allow(arithmetic_overflow)] // https://github.com/rust-lang/rust/issues/109731 + fn test_edge() { + dbg!("testing cast edge cases for {} -> {}", stringify!($f), stringify!($t)); + + match ($f::MIN as $BigS).cmp(&($t::MIN as $BigS)) { + Greater => { + assert_eq!(Some($f::MIN as $t), cast::<$f, $t>($f::MIN)); + } + Equal => { + assert_eq!(Some($t::MIN), cast::<$f, $t>($f::MIN)); + } + Less => { + let min = $t::MIN as $f; + assert_eq!(Some($t::MIN), cast::<$f, $t>(min)); + assert_eq!(None, cast::<$f, $t>(min - 1)); + } + } + + match ($f::MAX as $BigU).cmp(&($t::MAX as $BigU)) { + Greater => { + let max = $t::MAX as $f; + assert_eq!(Some($t::MAX), cast::<$f, $t>(max)); + assert_eq!(None, cast::<$f, $t>(max + 1)); + } + Equal => { + assert_eq!(Some($t::MAX), cast::<$f, $t>($f::MAX)); + } + Less => { + assert_eq!(Some($f::MAX as $t), cast::<$f, $t>($f::MAX)); + } + } + } + test_edge(); + })+} +} + +#[test] +fn cast_int_to_int_edge_cases() { + use core::cmp::Ordering::*; + + macro_rules! test_edge { + ($( $from:ident )+) => { $({ + int_test_edge!($from -> { isize i8 i16 i32 i64 } with i64 u64); + int_test_edge!($from -> { usize u8 u16 u32 u64 } with i64 u64); + })+} + } + + test_edge!(isize i8 i16 i32 i64); + test_edge!(usize u8 u16 u32 u64); +} + +#[test] +fn cast_int_to_128_edge_cases() { + use core::cmp::Ordering::*; + + macro_rules! test_edge { + ($( $t:ident )+) => { + $( + int_test_edge!($t -> { i128 u128 } with i128 u128); + )+ + int_test_edge!(i128 -> { $( $t )+ } with i128 u128); + int_test_edge!(u128 -> { $( $t )+ } with i128 u128); + } + } + + test_edge!(isize i8 i16 i32 i64 i128); + test_edge!(usize u8 u16 u32 u64 u128); +} + +#[test] +fn newtype_from_primitive() { + #[derive(PartialEq, Debug)] + struct New(T); + + // minimal impl + impl FromPrimitive for New { + fn from_i64(n: i64) -> Option { + T::from_i64(n).map(New) + } + + fn from_u64(n: u64) -> Option { + T::from_u64(n).map(New) + } + } + + macro_rules! assert_eq_from { + ($( $from:ident )+) => {$( + assert_eq!(T::$from(Bounded::min_value()).map(New), + New::::$from(Bounded::min_value())); + assert_eq!(T::$from(Bounded::max_value()).map(New), + New::::$from(Bounded::max_value())); + )+} + } + + fn check() { + assert_eq_from!(from_i8 from_i16 from_i32 from_i64 from_isize); + assert_eq_from!(from_u8 from_u16 from_u32 from_u64 from_usize); + assert_eq_from!(from_f32 from_f64); + } + + macro_rules! check { + ($( $ty:ty )+) => {$( check::<$ty>(); )+} + } + check!(i8 i16 i32 i64 isize); + check!(u8 u16 u32 u64 usize); +} + +#[test] +fn newtype_to_primitive() { + #[derive(PartialEq, Debug)] + struct New(T); + + // minimal impl + impl ToPrimitive for New { + fn to_i64(&self) -> Option { + self.0.to_i64() + } + + fn to_u64(&self) -> Option { + self.0.to_u64() + } + } + + macro_rules! assert_eq_to { + ($( $to:ident )+) => {$( + assert_eq!(T::$to(&Bounded::min_value()), + New::::$to(&New(Bounded::min_value()))); + assert_eq!(T::$to(&Bounded::max_value()), + New::::$to(&New(Bounded::max_value()))); + )+} + } + + fn check() { + assert_eq_to!(to_i8 to_i16 to_i32 to_i64 to_isize); + assert_eq_to!(to_u8 to_u16 to_u32 to_u64 to_usize); + assert_eq_to!(to_f32 to_f64); + } + + macro_rules! check { + ($( $ty:ty )+) => {$( check::<$ty>(); )+} + } + check!(i8 i16 i32 i64 isize); + check!(u8 u16 u32 u64 usize); +} diff --git a/tools/vendor/predicates-core/.cargo-checksum.json b/tools/vendor/predicates-core/.cargo-checksum.json new file mode 100644 index 0000000000..f67c5fb7f0 --- /dev/null +++ b/tools/vendor/predicates-core/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"1278080f1482f4e62180b7057886b507ce3ca30529f8dbf7f9616efa3d1d7ff7","Cargo.lock":"5bf4464fd9c7455ed1853f063a6571e7076c7bdc1f390733dc16c3cc80c0f255","Cargo.toml":"04d6bdedaeccd1b5536dc921680d11f7486da511555934d7bd7712a933c4709f","Cargo.toml.orig":"7e48dce026078191d172153828aa7cda9b2ecb222c150c25be17088ef4329aed","LICENSE-APACHE":"b40930bbcf80744c86c46a12bc9da056641d722716c378f5659b9e555ef833e1","LICENSE-MIT":"6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6","README.md":"86159502694c7d90efa5fb411b36c51fd212f57022a2541063f36c235d827098","src/core.rs":"19c28cbadf83f50a618fed6f378a03c53f1457bde8c2ba7f12ccd311747607b3","src/lib.rs":"bc35906f7e49e44e9188ebd2860dff2ab71fdd6beba306373a4e98ad53b59833","src/reflection.rs":"8c31410c0fa88a352e505dc9a748284994f223111d84f2aafda18b7ebd8fde9a"},"package":"727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa"} \ No newline at end of file diff --git a/tools/vendor/predicates-core/.cargo_vcs_info.json b/tools/vendor/predicates-core/.cargo_vcs_info.json new file mode 100644 index 0000000000..91826d82c7 --- /dev/null +++ b/tools/vendor/predicates-core/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "40045bb526b9efd30f12604af2fe0fd95fec4223" + }, + "path_in_vcs": "crates/core" +} \ No newline at end of file diff --git a/tools/vendor/predicates-core/Cargo.lock b/tools/vendor/predicates-core/Cargo.lock new file mode 100644 index 0000000000..54cefaf564 --- /dev/null +++ b/tools/vendor/predicates-core/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "predicates-core" +version = "1.0.9" diff --git a/tools/vendor/predicates-core/Cargo.toml b/tools/vendor/predicates-core/Cargo.toml new file mode 100644 index 0000000000..eeb0007b11 --- /dev/null +++ b/tools/vendor/predicates-core/Cargo.toml @@ -0,0 +1,95 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.74" +name = "predicates-core" +version = "1.0.9" +authors = ["Nick Stevens "] +build = false +include = [ + "build.rs", + "src/**/*", + "Cargo.toml", + "LICENSE*", + "README.md", + "benches/**/*", + "examples/**/*", +] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "An API for boolean-valued predicate functions." +homepage = "https://github.com/assert-rs/predicates-rs/tree/master/crates/core" +documentation = "https://docs.rs/predicates-core" +readme = "README.md" +keywords = [ + "predicate", + "boolean", + "combinatorial", + "match", + "logic", +] +categories = [ + "data-structures", + "rust-patterns", +] +license = "MIT OR Apache-2.0" +repository = "https://github.com/assert-rs/predicates-rs/tree/master/crates/core" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = [ + "--cfg", + "docsrs", +] + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +min = 1 +replace = "{{version}}" +search = "Unreleased" + +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "CHANGELOG.md" +replace = "...{{tag_name}}" +search = '\.\.\.HEAD' + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +min = 1 +replace = "{{date}}" +search = "ReleaseDate" + +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "CHANGELOG.md" +replace = """ + +## [Unreleased] - ReleaseDate +""" +search = "" + +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "CHANGELOG.md" +replace = """ + +[Unreleased]: https://github.com/assert-rs/predicates-rs/compare/{{tag_name}}...HEAD""" +search = "" + +[lib] +name = "predicates_core" +path = "src/lib.rs" diff --git a/tools/vendor/predicates-core/Cargo.toml.orig b/tools/vendor/predicates-core/Cargo.toml.orig new file mode 100644 index 0000000000..8ab9f61262 --- /dev/null +++ b/tools/vendor/predicates-core/Cargo.toml.orig @@ -0,0 +1,29 @@ +[package] +name = "predicates-core" +version = "1.0.9" +description = "An API for boolean-valued predicate functions." +authors = ["Nick Stevens "] +readme = "README.md" +repository = "https://github.com/assert-rs/predicates-rs/tree/master/crates/core" +homepage = "https://github.com/assert-rs/predicates-rs/tree/master/crates/core" +documentation = "https://docs.rs/predicates-core" +categories = ["data-structures", "rust-patterns"] +keywords = ["predicate", "boolean", "combinatorial", "match", "logic"] +license.workspace = true +edition.workspace = true +rust-version.workspace = true +include.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[package.metadata.release] +pre-release-replacements = [ + {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, + {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, + {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, + {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, + {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/assert-rs/predicates-rs/compare/{{tag_name}}...HEAD", exactly=1}, +] + diff --git a/tools/vendor/predicates-core/LICENSE-APACHE b/tools/vendor/predicates-core/LICENSE-APACHE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/tools/vendor/predicates-core/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/tools/vendor/predicates-core/LICENSE-MIT b/tools/vendor/predicates-core/LICENSE-MIT new file mode 100644 index 0000000000..a2d01088b6 --- /dev/null +++ b/tools/vendor/predicates-core/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) Individual contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/tools/vendor/predicates-core/README.md b/tools/vendor/predicates-core/README.md new file mode 100644 index 0000000000..57ec3ac47d --- /dev/null +++ b/tools/vendor/predicates-core/README.md @@ -0,0 +1,25 @@ +# predicates-core + +> Traits for **boolean-valued predicate functions** in Rust. + +[![Build Status](https://dev.azure.com/assert-rs/assert-rs/_apis/build/status/predicates-rs?branchName=master)](https://dev.azure.com/assert-rs/assert-rs/_build/latest?definitionId=1&branchName=master) +[![Documentation](https://img.shields.io/badge/docs-master-blue.svg)](https://docs.rs/predicates-core) +![License](https://img.shields.io/crates/l/predicates-core.svg) +[![Crates.io](https://img.shields.io/crates/v/predicates-core.svg?maxAge=2592000)](https://crates.io/crates/predicates-core) + +[Changelog](https://github.com/assert-rs/predicates-rs/blob/master/crates/core/CHANGELOG.md) + + +## License + +`predicates-core` is distributed under the terms of both the MIT license and the +Apache License (Version 2.0). + +See LICENSE-APACHE, and LICENSE-MIT for details. + + +## Credits + +Big thanks to [futures-rs](https://github.com/alexcrichton/futures-rs), whose +slick API design informed a lot of decisions made on the API design of this +library. diff --git a/tools/vendor/predicates-core/src/core.rs b/tools/vendor/predicates-core/src/core.rs new file mode 100644 index 0000000000..b5aaa7bcc7 --- /dev/null +++ b/tools/vendor/predicates-core/src/core.rs @@ -0,0 +1,32 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::reflection; + +/// Trait for generically evaluating a type against a dynamically created +/// predicate function. +/// +/// The exact meaning of `eval` depends on the situation, but will usually +/// mean that the evaluated item is in some sort of pre-defined set. This is +/// different from `Ord` and `Eq` in that an `item` will almost never be the +/// same type as the implementing `Predicate` type. +pub trait Predicate: reflection::PredicateReflection { + /// Execute this `Predicate` against `variable`, returning the resulting + /// boolean. + fn eval(&self, variable: &Item) -> bool; + + /// Find a case that proves this predicate as `expected` when run against `variable`. + fn find_case<'a>(&'a self, expected: bool, variable: &Item) -> Option> { + let actual = self.eval(variable); + if expected == actual { + Some(reflection::Case::new(None, actual)) + } else { + None + } + } +} diff --git a/tools/vendor/predicates-core/src/lib.rs b/tools/vendor/predicates-core/src/lib.rs new file mode 100644 index 0000000000..16a8a63e55 --- /dev/null +++ b/tools/vendor/predicates-core/src/lib.rs @@ -0,0 +1,26 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Composable first-order predicate trait. +//! +//! This library implements an interface to "predicates" - boolean-valued +//! functions of one argument. This allows combinatorial logic to be created and +//! assembled at runtime and then used one or more times for evaluating values. +//! This sort of object is really useful when creating filters and checks that +//! can be changed at runtime with user interaction - it allows a clean +//! separation of concerns where the configuration code can be used to build up +//! a predicate, and then that predicate can be given to the code that does the +//! actual filtering without the filtering code knowing anything about user +//! configuration. See the examples for how this can work. + +#![warn(missing_docs, missing_debug_implementations)] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] + +mod core; +pub use crate::core::*; +pub mod reflection; diff --git a/tools/vendor/predicates-core/src/reflection.rs b/tools/vendor/predicates-core/src/reflection.rs new file mode 100644 index 0000000000..0525cd37d2 --- /dev/null +++ b/tools/vendor/predicates-core/src/reflection.rs @@ -0,0 +1,252 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Introspect into the state of a `Predicate`. + +use std::borrow; +use std::fmt; +use std::slice; + +/// Introspect the state of a `Predicate`. +pub trait PredicateReflection: fmt::Display { + /// Parameters of the current `Predicate`. + fn parameters<'a>(&'a self) -> Box> + 'a> { + let params = vec![]; + Box::new(params.into_iter()) + } + + /// Nested `Predicate`s of the current `Predicate`. + fn children<'a>(&'a self) -> Box> + 'a> { + let params = vec![]; + Box::new(params.into_iter()) + } +} + +/// A view of a `Predicate` parameter, provided by reflection. +/// +/// ```rust +/// use predicates_core; +/// +/// let param = predicates_core::reflection::Parameter::new("key", &10); +/// println!("{}", param); +/// ``` +pub struct Parameter<'a>(&'a str, &'a dyn fmt::Display); + +impl<'a> Parameter<'a> { + /// Create a new `Parameter`. + pub fn new(key: &'a str, value: &'a dyn fmt::Display) -> Self { + Self(key, value) + } + + /// Access the `Parameter` name. + pub fn name(&self) -> &str { + self.0 + } + + /// Access the `Parameter` value. + pub fn value(&self) -> &dyn fmt::Display { + self.1 + } +} + +impl fmt::Display for Parameter<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}: {}", self.0, self.1) + } +} + +impl fmt::Debug for Parameter<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({:?}, {})", self.0, self.1) + } +} + +/// A view of a `Predicate` child, provided by reflection. +pub struct Child<'a>(&'a str, &'a dyn PredicateReflection); + +impl<'a> Child<'a> { + /// Create a new `Predicate` child. + pub fn new(key: &'a str, value: &'a dyn PredicateReflection) -> Self { + Self(key, value) + } + + /// Access the `Child`'s name. + pub fn name(&self) -> &str { + self.0 + } + + /// Access the `Child` `Predicate`. + pub fn value(&self) -> &dyn PredicateReflection { + self.1 + } +} + +impl fmt::Display for Child<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}: {}", self.0, self.1) + } +} + +impl fmt::Debug for Child<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({:?}, {})", self.0, self.1) + } +} + +/// A descriptive explanation for why a predicate failed. +pub struct Case<'a> { + predicate: Option<&'a dyn PredicateReflection>, + result: bool, + products: Vec, + children: Vec>, +} + +impl<'a> Case<'a> { + /// Create a new `Case` describing the result of a `Predicate`. + pub fn new(predicate: Option<&'a dyn PredicateReflection>, result: bool) -> Self { + Self { + predicate, + result, + products: Default::default(), + children: Default::default(), + } + } + + /// Add an additional by product to a `Case`. + pub fn add_product(mut self, product: Product) -> Self { + self.products.push(product); + self + } + + /// Add an additional by product to a `Case`. + pub fn add_child(mut self, child: Case<'a>) -> Self { + self.children.push(child); + self + } + + /// The `Predicate` that produced this case. + pub fn predicate(&self) -> Option<&dyn PredicateReflection> { + self.predicate + } + + /// The result of this case. + pub fn result(&self) -> bool { + self.result + } + + /// Access the by-products from determining this case. + pub fn products(&self) -> CaseProducts<'_> { + CaseProducts(self.products.iter()) + } + + /// Access the sub-cases. + pub fn children(&self) -> CaseChildren<'_> { + CaseChildren(self.children.iter()) + } +} + +impl fmt::Debug for Case<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let predicate = if let Some(ref predicate) = self.predicate { + format!("Some({})", predicate) + } else { + "None".to_owned() + }; + f.debug_struct("Case") + .field("predicate", &predicate) + .field("result", &self.result) + .field("products", &self.products) + .field("children", &self.children) + .finish() + } +} + +/// Iterator over a `Case`s by-products. +#[derive(Debug, Clone)] +pub struct CaseProducts<'a>(slice::Iter<'a, Product>); + +impl<'a> Iterator for CaseProducts<'a> { + type Item = &'a Product; + + fn next(&mut self) -> Option<&'a Product> { + self.0.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + + fn count(self) -> usize { + self.0.count() + } +} + +/// Iterator over a `Case`s sub-cases. +#[derive(Debug, Clone)] +pub struct CaseChildren<'a>(slice::Iter<'a, Case<'a>>); + +impl<'a> Iterator for CaseChildren<'a> { + type Item = &'a Case<'a>; + + fn next(&mut self) -> Option<&'a Case<'a>> { + self.0.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + + fn count(self) -> usize { + self.0.count() + } +} + +/// A by-product of a predicate evaluation. +/// +/// ```rust +/// use predicates_core; +/// +/// let product = predicates_core::reflection::Product::new("key", "value"); +/// println!("{}", product); +/// let product = predicates_core::reflection::Product::new(format!("key-{}", 5), 30); +/// println!("{}", product); +/// ``` +pub struct Product(borrow::Cow<'static, str>, Box); + +impl Product { + /// Create a new `Product`. + pub fn new(key: S, value: D) -> Self + where + S: Into>, + D: fmt::Display + 'static, + { + Self(key.into(), Box::new(value)) + } + + /// Access the `Product` name. + pub fn name(&self) -> &str { + self.0.as_ref() + } + + /// Access the `Product` value. + pub fn value(&self) -> &dyn fmt::Display { + &self.1 + } +} + +impl fmt::Display for Product { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}: {}", self.0, self.1) + } +} + +impl fmt::Debug for Product { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({:?}, {})", self.0, self.1) + } +} diff --git a/tools/vendor/predicates-tree/.cargo-checksum.json b/tools/vendor/predicates-tree/.cargo-checksum.json new file mode 100644 index 0000000000..e280ac6131 --- /dev/null +++ b/tools/vendor/predicates-tree/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"4d785b43daa524ef12c72f41fb433835c76247f1fb46856fd185cd0444f4a2d5","Cargo.lock":"215457a83bb5aa67b2872a57e5ef3763fe89e95a13cc66416a062c86f781ef7c","Cargo.toml":"ea5b61c9b28ab769c2507632a0abdebee9ea388f3a959bb1b7aaa3c7bb539f88","Cargo.toml.orig":"395ba1e545484194f63d781d34a9bcb91c083877c7830998a4a60686fc158fb6","LICENSE-APACHE":"b40930bbcf80744c86c46a12bc9da056641d722716c378f5659b9e555ef833e1","LICENSE-MIT":"6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6","README.md":"2e96471579f50e44834a5e58f1f16466c0395661873754ac5b8b342ecd157786","examples/failures.rs":"e5c9bafb7649d2b8aad4b585c28385cfc2d55e32aa190da693c920fc61945275","src/lib.rs":"c9c20ff76d3e6280175b2b16b77f4668670a8955280ab305adb030042657916f"},"package":"72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c"} \ No newline at end of file diff --git a/tools/vendor/predicates-tree/.cargo_vcs_info.json b/tools/vendor/predicates-tree/.cargo_vcs_info.json new file mode 100644 index 0000000000..f7fdceba69 --- /dev/null +++ b/tools/vendor/predicates-tree/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "40045bb526b9efd30f12604af2fe0fd95fec4223" + }, + "path_in_vcs": "crates/tree" +} \ No newline at end of file diff --git a/tools/vendor/predicates-tree/Cargo.lock b/tools/vendor/predicates-tree/Cargo.lock new file mode 100644 index 0000000000..a2489b4f2f --- /dev/null +++ b/tools/vendor/predicates-tree/Cargo.lock @@ -0,0 +1,124 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "float-cmp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" +dependencies = [ + "num-traits", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "predicates" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" +dependencies = [ + "anstyle", + "difflib", + "float-cmp", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" + +[[package]] +name = "predicates-tree" +version = "1.0.12" +dependencies = [ + "predicates", + "predicates-core", + "termtree", +] + +[[package]] +name = "regex" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" diff --git a/tools/vendor/predicates-tree/Cargo.toml b/tools/vendor/predicates-tree/Cargo.toml new file mode 100644 index 0000000000..f821090be1 --- /dev/null +++ b/tools/vendor/predicates-tree/Cargo.toml @@ -0,0 +1,109 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.74" +name = "predicates-tree" +version = "1.0.12" +authors = ["Nick Stevens "] +build = false +include = [ + "build.rs", + "src/**/*", + "Cargo.toml", + "LICENSE*", + "README.md", + "benches/**/*", + "examples/**/*", +] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Render boolean-valued predicate functions results as a tree." +homepage = "https://github.com/assert-rs/predicates-rs/tree/master/crates/tree" +documentation = "https://docs.rs/predicates-tree" +readme = "README.md" +keywords = [ + "predicate", + "boolean", + "combinatorial", + "match", + "logic", +] +categories = [ + "data-structures", + "rust-patterns", +] +license = "MIT OR Apache-2.0" +repository = "https://github.com/assert-rs/predicates-rs/tree/master/crates/tree" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = [ + "--cfg", + "docsrs", +] + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +min = 1 +replace = "{{version}}" +search = "Unreleased" + +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "CHANGELOG.md" +replace = "...{{tag_name}}" +search = '\.\.\.HEAD' + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +min = 1 +replace = "{{date}}" +search = "ReleaseDate" + +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "CHANGELOG.md" +replace = """ + +## [Unreleased] - ReleaseDate +""" +search = "" + +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "CHANGELOG.md" +replace = """ + +[Unreleased]: https://github.com/assert-rs/predicates-rs/compare/{{tag_name}}...HEAD""" +search = "" + +[lib] +name = "predicates_tree" +path = "src/lib.rs" + +[[example]] +name = "failures" +path = "examples/failures.rs" + +[dependencies.predicates-core] +version = "1.0" + +[dependencies.termtree] +version = "0.5.0" + +[dev-dependencies.predicates] +version = "3.1" +features = ["color"] diff --git a/tools/vendor/predicates-tree/Cargo.toml.orig b/tools/vendor/predicates-tree/Cargo.toml.orig new file mode 100644 index 0000000000..e50884cb5f --- /dev/null +++ b/tools/vendor/predicates-tree/Cargo.toml.orig @@ -0,0 +1,35 @@ +[package] +name = "predicates-tree" +version = "1.0.12" +authors = ["Nick Stevens "] +description = "Render boolean-valued predicate functions results as a tree." +readme = "README.md" +repository = "https://github.com/assert-rs/predicates-rs/tree/master/crates/tree" +homepage = "https://github.com/assert-rs/predicates-rs/tree/master/crates/tree" +documentation = "https://docs.rs/predicates-tree" +categories = ["data-structures", "rust-patterns"] +keywords = ["predicate", "boolean", "combinatorial", "match", "logic"] +license.workspace = true +edition.workspace = true +rust-version.workspace = true +include.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[package.metadata.release] +pre-release-replacements = [ + {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, + {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, + {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, + {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, + {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/assert-rs/predicates-rs/compare/{{tag_name}}...HEAD", exactly=1}, +] + +[dependencies] +predicates-core = { version = "1.0", path = "../core" } +termtree = "0.5.0" + +[dev-dependencies] +predicates = { version = "3.1", path = "../..", features = ["color"] } diff --git a/tools/vendor/predicates-tree/LICENSE-APACHE b/tools/vendor/predicates-tree/LICENSE-APACHE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/tools/vendor/predicates-tree/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/tools/vendor/predicates-tree/LICENSE-MIT b/tools/vendor/predicates-tree/LICENSE-MIT new file mode 100644 index 0000000000..a2d01088b6 --- /dev/null +++ b/tools/vendor/predicates-tree/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) Individual contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/tools/vendor/predicates-tree/README.md b/tools/vendor/predicates-tree/README.md new file mode 100644 index 0000000000..4902db19e2 --- /dev/null +++ b/tools/vendor/predicates-tree/README.md @@ -0,0 +1,18 @@ +# predicates-tree + +> Render **boolean-valued predicate functions** results as a tree. + +[![Build Status](https://dev.azure.com/assert-rs/assert-rs/_apis/build/status/predicates-rs?branchName=master)](https://dev.azure.com/assert-rs/assert-rs/_build/latest?definitionId=1&branchName=master) +[![Documentation](https://img.shields.io/badge/docs-master-blue.svg)](https://docs.rs/predicates-tree) +![License](https://img.shields.io/crates/l/predicates-tree.svg) +[![Crates.io](https://img.shields.io/crates/v/predicates-tree.svg?maxAge=2592000)](https://crates.io/crates/predicates-tree) + +[Changelog](https://github.com/assert-rs/predicates-rs/blob/master/predicates-tree/CHANGELOG.md) + + +## License + +`predicates-tree` is distributed under the terms of both the MIT license and the +Apache License (Version 2.0). + +See LICENSE-APACHE, and LICENSE-MIT for details. diff --git a/tools/vendor/predicates-tree/examples/failures.rs b/tools/vendor/predicates-tree/examples/failures.rs new file mode 100644 index 0000000000..9ba640c07c --- /dev/null +++ b/tools/vendor/predicates-tree/examples/failures.rs @@ -0,0 +1,48 @@ +use predicates::prelude::*; +use predicates_tree::CaseTreeExt; + +fn main() { + let expected = 10; + let actual = 15; + let pred = predicates::ord::eq(expected); + if let Some(case) = pred.find_case(false, &actual) { + let tree = case.tree(); + println!("{}", tree); + } + + let expected = [1, 2, 3]; + let actual = 15; + let pred = predicates::iter::in_iter(IntoIterator::into_iter(expected)); + if let Some(case) = pred.find_case(false, &actual) { + let tree = case.tree(); + println!("{}", tree); + } + + let expected = "Hello +World! + +Goodbye!"; + let actual = "Hello +Moon! + +Goodbye!"; + let pred = predicates::ord::eq(expected); + if let Some(case) = pred.find_case(false, &actual) { + let tree = case.tree(); + println!("{}", tree); + } + + let expected = "Hello +World! + +Goodbye!"; + let actual = "Hello +Moon! + +Goodbye!"; + let pred = predicates::str::diff(expected); + if let Some(case) = pred.find_case(false, actual) { + let tree = case.tree(); + println!("{}", tree); + } +} diff --git a/tools/vendor/predicates-tree/src/lib.rs b/tools/vendor/predicates-tree/src/lib.rs new file mode 100644 index 0000000000..d3287a978a --- /dev/null +++ b/tools/vendor/predicates-tree/src/lib.rs @@ -0,0 +1,87 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Render `Case` as a tree. + +#![cfg_attr(docsrs, feature(doc_auto_cfg))] + +use std::fmt; + +use predicates_core::reflection; + +/// Render `Self` as a displayable tree. +pub trait CaseTreeExt { + /// Render `Self` as a displayable tree. + fn tree(&self) -> CaseTree; +} + +impl CaseTreeExt for reflection::Case<'_> { + fn tree(&self) -> CaseTree { + CaseTree(convert(self)) + } +} + +type CaseTreeInner = termtree::Tree; + +fn convert(case: &reflection::Case<'_>) -> CaseTreeInner { + let mut leaves: Vec = vec![]; + + leaves.extend(case.predicate().iter().flat_map(|pred| { + pred.parameters().map(|item| { + let root = Displayable::new(&item); + termtree::Tree::new(root).with_multiline(true) + }) + })); + + leaves.extend(case.products().map(|item| { + let root = Displayable::new(item); + termtree::Tree::new(root).with_multiline(true) + })); + + leaves.extend(case.children().map(convert)); + + let root = case + .predicate() + .map(|p| Displayable::new(&p)) + .unwrap_or_default(); + CaseTreeInner::new(root).with_leaves(leaves) +} + +/// A `Case` rendered as a tree for display. +#[allow(missing_debug_implementations)] +pub struct CaseTree(CaseTreeInner); + +impl fmt::Display for CaseTree { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +#[derive(Default)] +struct Displayable { + primary: String, + alternate: String, +} + +impl Displayable { + fn new(display: &dyn std::fmt::Display) -> Self { + let primary = format!("{}", display); + let alternate = format!("{:#}", display); + Self { primary, alternate } + } +} + +impl fmt::Display for Displayable { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + self.alternate.fmt(f) + } else { + self.primary.fmt(f) + } + } +} diff --git a/tools/vendor/predicates/.cargo-checksum.json b/tools/vendor/predicates/.cargo-checksum.json new file mode 100644 index 0000000000..0f70518f96 --- /dev/null +++ b/tools/vendor/predicates/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"8772456cfc53a8d7760495728ee8ccc00321a9c76c4690c567a2b450513e14fd","Cargo.lock":"a2a92a46c4fe9ced17109fa6541369ed9ad6d3f426ff3d8ba0649d31b4995b2c","Cargo.toml":"98c284bc60c87caeb891c4a046c2495a13ad8c9fc8138cab4744973a1843aa56","Cargo.toml.orig":"a53c2b59de02eb0866901d328ceb9ec08b504b555e89736fe9370b201bfb8ba8","LICENSE-APACHE":"b40930bbcf80744c86c46a12bc9da056641d722716c378f5659b9e555ef833e1","LICENSE-MIT":"6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6","README.md":"016703be2b9d4e506ddc78f10df70a7765ddf2f8c42bc230fc37346bd15b217b","examples/case_tree.rs":"65a20067977ef6ed745554ac8914c93b2b6c67d5d736ac292725d564d1e0e71c","src/boolean.rs":"2aab95ec45891adabd46dfd0c507febb8f79348ac33776fb75b6fc5e88738f68","src/boxed.rs":"0a542664f99d182481ff9ef5805e7659689db257debed59e4ce8b0946f930061","src/color.rs":"3be07503bcd6631275b9e7ef00919175141c7cb88a40d689d01e6f26f376c578","src/constant.rs":"b3c9651948525e9aa5679e14e886117143f4fb9cabbb0fb5b66411de79856867","src/float/close.rs":"c176e80091a8394145ea7ba1ee23e6e98845db4c0ca0262ab5f69bd815b85180","src/float/mod.rs":"e12150122e36b4c508f6668391c9c89adcd7f9b396fc1f4cdab1457355bf933c","src/function.rs":"560e08927a12875fe7c4b4b71a45d659ec518b4af149d98d4ff19bedf118af9c","src/iter.rs":"4a3dac9de86efe929e18dff6dd0b08a8cb2e214944a43cabc1d908a129909c0f","src/lib.rs":"aab83cd765746d20180261ae15682af768e6ccb1301f66f78459e7b75905a251","src/name.rs":"44d66b3dde5a436dece16fdc71ae5c1cc3ede2d3599b088c057629c51744bf3f","src/ord.rs":"6f06ee867a17227afff7c5d604565ea86010c3c3997972ae3e5780c652d6d834","src/path/existence.rs":"4414b209473ad275675b8cddaab70cc306a264f226ad68670a3956e511d8a59a","src/path/fc.rs":"90d38b7e63e28d669f83c0e0c39b04ff020609312ded2b8d17d307116d6823b5","src/path/fs.rs":"fc4d83e2e938826d5f93f9ba335a36c5086d00e93c7086ae65c3c5433752fe3d","src/path/ft.rs":"69b4785928a998b8e16bea60259aded218efbc275f04ec5cbd334d7e3e5ef070","src/path/mod.rs":"8b57b16bcc36c3f7fc02ab9e5900ec2fbce75f9b32d9231e304674b958590646","src/prelude.rs":"d808bec934efbe70de122cbd563007098d236fe201868e8bccea73995405a029","src/str/adapters.rs":"4b98c1e566753ba54eff771c732e65ff28fcb197adf7e9e289dc336cf0a7117f","src/str/basics.rs":"f22c5db34012a605780398a87a7fa6c6c3403afd2cec0c3f57504f2f8dee9fb4","src/str/difference.rs":"5a3afbaa03529365d0e32864577e467e99f292d07b8ffb447e90878b5d3b5b37","src/str/mod.rs":"c810c38a1f85dfd051cee30c4020a5748ae547f8f906459a5e9ef3f322a60870","src/str/normalize.rs":"97c42c49922d4550486831d88d3cfb6e1ab5af6176615e7b46646f2f7e7785b8","src/str/regex.rs":"1f72ed3f614bc8852bfcc9c7c790202cd305f802ceb99b62c90fba197880b9c9","src/utils.rs":"94f675eb891de17c2eb32f7c980baa6506af8e43f1cf5af24f91c1b6d54a1a75"},"package":"a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573"} \ No newline at end of file diff --git a/tools/vendor/predicates/.cargo_vcs_info.json b/tools/vendor/predicates/.cargo_vcs_info.json new file mode 100644 index 0000000000..4ce1666264 --- /dev/null +++ b/tools/vendor/predicates/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "40045bb526b9efd30f12604af2fe0fd95fec4223" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/tools/vendor/predicates/Cargo.lock b/tools/vendor/predicates/Cargo.lock new file mode 100644 index 0000000000..71ad3b3769 --- /dev/null +++ b/tools/vendor/predicates/Cargo.lock @@ -0,0 +1,124 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "float-cmp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" +dependencies = [ + "num-traits", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "predicates" +version = "3.1.3" +dependencies = [ + "anstyle", + "difflib", + "float-cmp", + "normalize-line-endings", + "predicates-core", + "predicates-tree", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" + +[[package]] +name = "predicates-tree" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "regex" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" diff --git a/tools/vendor/predicates/Cargo.toml b/tools/vendor/predicates/Cargo.toml new file mode 100644 index 0000000000..dd361369a7 --- /dev/null +++ b/tools/vendor/predicates/Cargo.toml @@ -0,0 +1,221 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.74" +name = "predicates" +version = "3.1.3" +authors = ["Nick Stevens "] +build = false +include = [ + "build.rs", + "src/**/*", + "Cargo.toml", + "LICENSE*", + "README.md", + "benches/**/*", + "examples/**/*", +] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "An implementation of boolean-valued predicate functions." +homepage = "https://github.com/assert-rs/predicates-rs" +documentation = "https://docs.rs/predicates" +readme = "README.md" +keywords = [ + "predicate", + "boolean", + "combinatorial", + "match", + "logic", +] +categories = [ + "data-structures", + "rust-patterns", +] +license = "MIT OR Apache-2.0" +repository = "https://github.com/assert-rs/predicates-rs" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = [ + "--cfg", + "docsrs", + "--generate-link-to-definition", +] + +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "src/lib.rs" +replace = 'predicates = "{{version}}"' +search = 'predicates = ".*"' + +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "README.md" +replace = 'predicates = "{{version}}"' +search = 'predicates = ".*"' + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +min = 1 +replace = "{{version}}" +search = "Unreleased" + +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "CHANGELOG.md" +replace = "...{{tag_name}}" +search = '\.\.\.HEAD' + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +min = 1 +replace = "{{date}}" +search = "ReleaseDate" + +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "CHANGELOG.md" +replace = """ + +## [Unreleased] - ReleaseDate +""" +search = "" + +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "CHANGELOG.md" +replace = """ + +[Unreleased]: https://github.com/assert-rs/predicates-rs/compare/{{tag_name}}...HEAD""" +search = "" + +[features] +color = [] +default = [ + "diff", + "regex", + "float-cmp", + "normalize-line-endings", + "color", +] +diff = ["dep:difflib"] +unstable = [] + +[lib] +name = "predicates" +path = "src/lib.rs" + +[[example]] +name = "case_tree" +path = "examples/case_tree.rs" + +[dependencies.anstyle] +version = "1.0.0" + +[dependencies.difflib] +version = "0.4" +optional = true + +[dependencies.float-cmp] +version = "0.10" +optional = true + +[dependencies.normalize-line-endings] +version = "0.3.0" +optional = true + +[dependencies.predicates-core] +version = "1.0" + +[dependencies.regex] +version = "1.0" +optional = true + +[dev-dependencies.predicates-tree] +version = "1.0" + +[lints.clippy] +bool_assert_comparison = "allow" +branches_sharing_code = "allow" +checked_conversions = "warn" +collapsible_else_if = "allow" +create_dir = "warn" +dbg_macro = "warn" +debug_assert_with_mut_call = "warn" +doc_markdown = "warn" +empty_enum = "warn" +enum_glob_use = "warn" +expl_impl_clone_on_copy = "warn" +explicit_deref_methods = "warn" +explicit_into_iter_loop = "warn" +fallible_impl_from = "warn" +filter_map_next = "warn" +flat_map_option = "warn" +float_cmp_const = "warn" +fn_params_excessive_bools = "warn" +from_iter_instead_of_collect = "warn" +if_same_then_else = "allow" +implicit_clone = "warn" +imprecise_flops = "warn" +inconsistent_struct_constructor = "warn" +inefficient_to_string = "warn" +infinite_loop = "warn" +invalid_upcast_comparisons = "warn" +large_digit_groups = "warn" +large_stack_arrays = "warn" +large_types_passed_by_value = "warn" +let_and_return = "allow" +linkedlist = "warn" +lossy_float_literal = "warn" +macro_use_imports = "warn" +mem_forget = "warn" +mutex_integer = "warn" +needless_continue = "warn" +needless_for_each = "warn" +negative_feature_names = "warn" +path_buf_push_overwrite = "warn" +ptr_as_ptr = "warn" +rc_mutex = "warn" +redundant_feature_names = "warn" +ref_option_ref = "warn" +rest_pat_in_fully_bound_structs = "warn" +result_large_err = "allow" +same_functions_in_if_condition = "warn" +self_named_module_files = "warn" +semicolon_if_nothing_returned = "warn" +str_to_string = "warn" +string_add = "warn" +string_add_assign = "warn" +string_lit_as_bytes = "warn" +string_to_string = "warn" +todo = "warn" +trait_duplication_in_bounds = "warn" +uninlined_format_args = "warn" +verbose_file_reads = "warn" +wildcard_imports = "warn" +zero_sized_map_values = "warn" + +[lints.rust] +unreachable_pub = "warn" +unsafe_op_in_unsafe_fn = "warn" +unused_lifetimes = "warn" +unused_macro_rules = "warn" +unused_qualifications = "warn" + +[lints.rust.rust_2018_idioms] +level = "warn" +priority = -1 diff --git a/tools/vendor/predicates/Cargo.toml.orig b/tools/vendor/predicates/Cargo.toml.orig new file mode 100644 index 0000000000..012058e659 --- /dev/null +++ b/tools/vendor/predicates/Cargo.toml.orig @@ -0,0 +1,138 @@ +[workspace] +members = ["crates/*"] +resolver = "2" + +[workspace.package] +repository = "https://github.com/assert-rs/predicates-rs" +license = "MIT OR Apache-2.0" +edition = "2021" +rust-version = "1.74" # MSRV +include = [ + "build.rs", + "src/**/*", + "Cargo.toml", + "LICENSE*", + "README.md", + "benches/**/*", + "examples/**/*" +] + +[workspace.lints.rust] +rust_2018_idioms = { level = "warn", priority = -1 } +unreachable_pub = "warn" +unsafe_op_in_unsafe_fn = "warn" +unused_lifetimes = "warn" +unused_macro_rules = "warn" +unused_qualifications = "warn" + +[workspace.lints.clippy] +bool_assert_comparison = "allow" +branches_sharing_code = "allow" +checked_conversions = "warn" +collapsible_else_if = "allow" +create_dir = "warn" +dbg_macro = "warn" +debug_assert_with_mut_call = "warn" +doc_markdown = "warn" +empty_enum = "warn" +enum_glob_use = "warn" +expl_impl_clone_on_copy = "warn" +explicit_deref_methods = "warn" +explicit_into_iter_loop = "warn" +fallible_impl_from = "warn" +filter_map_next = "warn" +flat_map_option = "warn" +float_cmp_const = "warn" +fn_params_excessive_bools = "warn" +from_iter_instead_of_collect = "warn" +if_same_then_else = "allow" +implicit_clone = "warn" +imprecise_flops = "warn" +inconsistent_struct_constructor = "warn" +inefficient_to_string = "warn" +infinite_loop = "warn" +invalid_upcast_comparisons = "warn" +large_digit_groups = "warn" +large_stack_arrays = "warn" +large_types_passed_by_value = "warn" +let_and_return = "allow" # sometimes good to name what you are returning +linkedlist = "warn" +lossy_float_literal = "warn" +macro_use_imports = "warn" +mem_forget = "warn" +mutex_integer = "warn" +needless_continue = "warn" +needless_for_each = "warn" +negative_feature_names = "warn" +path_buf_push_overwrite = "warn" +ptr_as_ptr = "warn" +rc_mutex = "warn" +redundant_feature_names = "warn" +ref_option_ref = "warn" +rest_pat_in_fully_bound_structs = "warn" +result_large_err = "allow" +same_functions_in_if_condition = "warn" +self_named_module_files = "warn" +semicolon_if_nothing_returned = "warn" +str_to_string = "warn" +string_add = "warn" +string_add_assign = "warn" +string_lit_as_bytes = "warn" +string_to_string = "warn" +todo = "warn" +trait_duplication_in_bounds = "warn" +uninlined_format_args = "warn" +verbose_file_reads = "warn" +wildcard_imports = "warn" +zero_sized_map_values = "warn" + +[package] +name = "predicates" +version = "3.1.3" +description = "An implementation of boolean-valued predicate functions." +authors = ["Nick Stevens "] +homepage = "https://github.com/assert-rs/predicates-rs" +documentation = "https://docs.rs/predicates" +readme = "README.md" +categories = ["data-structures", "rust-patterns"] +keywords = ["predicate", "boolean", "combinatorial", "match", "logic"] +repository.workspace = true +license.workspace = true +edition.workspace = true +rust-version.workspace = true +include.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] + +[package.metadata.release] +pre-release-replacements = [ + {file="src/lib.rs", search="predicates = \".*\"", replace="predicates = \"{{version}}\"", exactly=1}, + {file="README.md", search="predicates = \".*\"", replace="predicates = \"{{version}}\"", exactly=1}, + {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, + {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, + {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, + {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, + {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/assert-rs/predicates-rs/compare/{{tag_name}}...HEAD", exactly=1}, +] + +[features] +default = ["diff", "regex", "float-cmp", "normalize-line-endings", "color"] +diff = ["dep:difflib"] +unstable = [] +color = [] + +[dependencies] +predicates-core = { version = "1.0", path = "crates/core" } +difflib = { version = "0.4", optional = true } +normalize-line-endings = { version = "0.3.0", optional = true } +regex = { version="1.0", optional = true } +float-cmp = { version="0.10", optional = true } +anstyle = "1.0.0" + +[dev-dependencies] +predicates-tree = { version = "1.0", path = "crates/tree" } + +[lints] +workspace = true diff --git a/tools/vendor/predicates/LICENSE-APACHE b/tools/vendor/predicates/LICENSE-APACHE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/tools/vendor/predicates/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/tools/vendor/predicates/LICENSE-MIT b/tools/vendor/predicates/LICENSE-MIT new file mode 100644 index 0000000000..a2d01088b6 --- /dev/null +++ b/tools/vendor/predicates/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) Individual contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/tools/vendor/predicates/README.md b/tools/vendor/predicates/README.md new file mode 100644 index 0000000000..575dc0d5cb --- /dev/null +++ b/tools/vendor/predicates/README.md @@ -0,0 +1,44 @@ +# predicates-rs + +> An implementation of **boolean-valued predicate functions** in Rust. + +[![Documentation](https://img.shields.io/badge/docs-master-blue.svg)](https://docs.rs/predicates) +![License](https://img.shields.io/crates/l/predicates.svg) +[![Crates.io](https://img.shields.io/crates/v/predicates.svg?maxAge=2592000)](https://crates.io/crates/predicates) + +[Changelog](https://github.com/assert-rs/predicates-rs/blob/master/CHANGELOG.md) + + +## Usage + +First, add this to your `Cargo.toml`: + +```toml +[dependencies] +predicates = "3.1.3" +``` + +Next, add this to your crate: + +```rust +extern crate predicates; + +use predicates::prelude::*; +``` + +For more information on using predicates, look at the +[documentation](https://docs.rs/predicates) + +## License + +`predicates-rs` is distributed under the terms of both the MIT license and the +Apache License (Version 2.0). + +* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) +* MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +## Credits + +Big thanks to [futures-rs](https://github.com/alexcrichton/futures-rs), whose +slick API design informed a lot of decisions made on the API design of this +library. diff --git a/tools/vendor/predicates/examples/case_tree.rs b/tools/vendor/predicates/examples/case_tree.rs new file mode 100644 index 0000000000..65991173c4 --- /dev/null +++ b/tools/vendor/predicates/examples/case_tree.rs @@ -0,0 +1,13 @@ +use predicates::prelude::*; +use predicates_tree::CaseTreeExt; + +fn main() { + let pred = predicate::ne(5).not().and(predicate::ge(5)); + + let var = 5; + let case = pred.find_case(true, &var); + if let Some(case) = case { + println!("var is {var}"); + println!("{}", case.tree()); + } +} diff --git a/tools/vendor/predicates/src/boolean.rs b/tools/vendor/predicates/src/boolean.rs new file mode 100644 index 0000000000..a730f14fc3 --- /dev/null +++ b/tools/vendor/predicates/src/boolean.rs @@ -0,0 +1,508 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Definition of boolean logic combinators over `Predicate`s. + +use std::fmt; +use std::marker::PhantomData; + +use crate::reflection; +use crate::Predicate; + +/// Predicate that combines two `Predicate`s, returning the AND of the results. +/// +/// This is created by the `Predicate::and` function. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AndPredicate +where + M1: Predicate, + M2: Predicate, + Item: ?Sized, +{ + a: M1, + b: M2, + _phantom: PhantomData, +} + +unsafe impl Send for AndPredicate +where + M1: Predicate + Send, + M2: Predicate + Send, + Item: ?Sized, +{ +} + +unsafe impl Sync for AndPredicate +where + M1: Predicate + Sync, + M2: Predicate + Sync, + Item: ?Sized, +{ +} + +impl AndPredicate +where + M1: Predicate, + M2: Predicate, + Item: ?Sized, +{ + /// Create a new `AndPredicate` over predicates `a` and `b`. + pub fn new(a: M1, b: M2) -> AndPredicate { + AndPredicate { + a, + b, + _phantom: PhantomData, + } + } +} + +impl Predicate for AndPredicate +where + M1: Predicate, + M2: Predicate, + Item: ?Sized, +{ + fn eval(&self, item: &Item) -> bool { + self.a.eval(item) && self.b.eval(item) + } + + fn find_case<'a>(&'a self, expected: bool, variable: &Item) -> Option> { + let child_a = self.a.find_case(expected, variable); + match (expected, child_a) { + (true, Some(child_a)) => self.b.find_case(expected, variable).map(|child_b| { + reflection::Case::new(Some(self), expected) + .add_child(child_a) + .add_child(child_b) + }), + (true, None) => None, + (false, Some(child_a)) => { + Some(reflection::Case::new(Some(self), expected).add_child(child_a)) + } + (false, None) => self + .b + .find_case(expected, variable) + .map(|child_b| reflection::Case::new(Some(self), expected).add_child(child_b)), + } + } +} + +impl reflection::PredicateReflection for AndPredicate +where + M1: Predicate, + M2: Predicate, + Item: ?Sized, +{ + fn children<'a>(&'a self) -> Box> + 'a> { + let params = vec![ + reflection::Child::new("left", &self.a), + reflection::Child::new("right", &self.b), + ]; + Box::new(params.into_iter()) + } +} + +impl fmt::Display for AndPredicate +where + M1: Predicate, + M2: Predicate, + Item: ?Sized, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({} && {})", self.a, self.b) + } +} + +#[cfg(test)] +mod test_and { + use crate::prelude::*; + + #[test] + fn find_case_true() { + assert!(predicate::always() + .and(predicate::always()) + .find_case(true, &5) + .is_some()); + } + + #[test] + fn find_case_true_left_fail() { + assert!(predicate::never() + .and(predicate::always()) + .find_case(true, &5) + .is_none()); + } + + #[test] + fn find_case_true_right_fail() { + assert!(predicate::always() + .and(predicate::never()) + .find_case(true, &5) + .is_none()); + } + + #[test] + fn find_case_true_fails() { + assert!(predicate::never() + .and(predicate::never()) + .find_case(true, &5) + .is_none()); + } + + #[test] + fn find_case_false() { + assert!(predicate::never() + .and(predicate::never()) + .find_case(false, &5) + .is_some()); + } + + #[test] + fn find_case_false_fails() { + assert!(predicate::always() + .and(predicate::always()) + .find_case(false, &5) + .is_none()); + } + + #[test] + fn find_case_false_left_fail() { + assert!(predicate::never() + .and(predicate::always()) + .find_case(false, &5) + .is_some()); + } + + #[test] + fn find_case_false_right_fail() { + assert!(predicate::always() + .and(predicate::never()) + .find_case(false, &5) + .is_some()); + } +} + +/// Predicate that combines two `Predicate`s, returning the OR of the results. +/// +/// This is created by the `Predicate::or` function. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct OrPredicate +where + M1: Predicate, + M2: Predicate, + Item: ?Sized, +{ + a: M1, + b: M2, + _phantom: PhantomData, +} + +unsafe impl Send for OrPredicate +where + M1: Predicate + Send, + M2: Predicate + Send, + Item: ?Sized, +{ +} + +unsafe impl Sync for OrPredicate +where + M1: Predicate + Sync, + M2: Predicate + Sync, + Item: ?Sized, +{ +} + +impl OrPredicate +where + M1: Predicate, + M2: Predicate, + Item: ?Sized, +{ + /// Create a new `OrPredicate` over predicates `a` and `b`. + pub fn new(a: M1, b: M2) -> OrPredicate { + OrPredicate { + a, + b, + _phantom: PhantomData, + } + } +} + +impl Predicate for OrPredicate +where + M1: Predicate, + M2: Predicate, + Item: ?Sized, +{ + fn eval(&self, item: &Item) -> bool { + self.a.eval(item) || self.b.eval(item) + } + + fn find_case<'a>(&'a self, expected: bool, variable: &Item) -> Option> { + let child_a = self.a.find_case(expected, variable); + match (expected, child_a) { + (true, Some(child_a)) => { + Some(reflection::Case::new(Some(self), expected).add_child(child_a)) + } + (true, None) => self + .b + .find_case(expected, variable) + .map(|child_b| reflection::Case::new(Some(self), expected).add_child(child_b)), + (false, Some(child_a)) => self.b.find_case(expected, variable).map(|child_b| { + reflection::Case::new(Some(self), expected) + .add_child(child_a) + .add_child(child_b) + }), + (false, None) => None, + } + } +} + +impl reflection::PredicateReflection for OrPredicate +where + M1: Predicate, + M2: Predicate, + Item: ?Sized, +{ + fn children<'a>(&'a self) -> Box> + 'a> { + let params = vec![ + reflection::Child::new("left", &self.a), + reflection::Child::new("right", &self.b), + ]; + Box::new(params.into_iter()) + } +} + +impl fmt::Display for OrPredicate +where + M1: Predicate, + M2: Predicate, + Item: ?Sized, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({} || {})", self.a, self.b) + } +} + +#[cfg(test)] +mod test_or { + use crate::prelude::*; + + #[test] + fn find_case_true() { + assert!(predicate::always() + .or(predicate::always()) + .find_case(true, &5) + .is_some()); + } + + #[test] + fn find_case_true_left_fail() { + assert!(predicate::never() + .or(predicate::always()) + .find_case(true, &5) + .is_some()); + } + + #[test] + fn find_case_true_right_fail() { + assert!(predicate::always() + .or(predicate::never()) + .find_case(true, &5) + .is_some()); + } + + #[test] + fn find_case_true_fails() { + assert!(predicate::never() + .or(predicate::never()) + .find_case(true, &5) + .is_none()); + } + + #[test] + fn find_case_false() { + assert!(predicate::never() + .or(predicate::never()) + .find_case(false, &5) + .is_some()); + } + + #[test] + fn find_case_false_fails() { + assert!(predicate::always() + .or(predicate::always()) + .find_case(false, &5) + .is_none()); + } + + #[test] + fn find_case_false_left_fail() { + assert!(predicate::never() + .or(predicate::always()) + .find_case(false, &5) + .is_none()); + } + + #[test] + fn find_case_false_right_fail() { + assert!(predicate::always() + .or(predicate::never()) + .find_case(false, &5) + .is_none()); + } +} + +/// Predicate that returns a `Predicate` taking the logical NOT of the result. +/// +/// This is created by the `Predicate::not` function. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct NotPredicate +where + M: Predicate, + Item: ?Sized, +{ + inner: M, + _phantom: PhantomData, +} + +unsafe impl Send for NotPredicate +where + M: Predicate + Send, + Item: ?Sized, +{ +} + +unsafe impl Sync for NotPredicate +where + M: Predicate + Sync, + Item: ?Sized, +{ +} + +impl NotPredicate +where + M: Predicate, + Item: ?Sized, +{ + /// Create a new `NotPredicate` over predicate `inner`. + pub fn new(inner: M) -> NotPredicate { + NotPredicate { + inner, + _phantom: PhantomData, + } + } +} + +impl Predicate for NotPredicate +where + M: Predicate, + Item: ?Sized, +{ + fn eval(&self, item: &Item) -> bool { + !self.inner.eval(item) + } + + fn find_case<'a>(&'a self, expected: bool, variable: &Item) -> Option> { + self.inner + .find_case(!expected, variable) + .map(|child| reflection::Case::new(Some(self), expected).add_child(child)) + } +} + +impl reflection::PredicateReflection for NotPredicate +where + M: Predicate, + Item: ?Sized, +{ + fn children<'a>(&'a self) -> Box> + 'a> { + let params = vec![reflection::Child::new("predicate", &self.inner)]; + Box::new(params.into_iter()) + } +} + +impl fmt::Display for NotPredicate +where + M: Predicate, + Item: ?Sized, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "(! {})", self.inner) + } +} + +/// `Predicate` extension that adds boolean logic. +pub trait PredicateBooleanExt +where + Self: Predicate, +{ + /// Compute the logical AND of two `Predicate` results, returning the result. + /// + /// # Examples + /// + /// ``` + /// use predicates::prelude::*; + /// + /// let predicate_fn1 = predicate::always().and(predicate::always()); + /// let predicate_fn2 = predicate::always().and(predicate::never()); + /// assert_eq!(true, predicate_fn1.eval(&4)); + /// assert_eq!(false, predicate_fn2.eval(&4)); + fn and(self, other: B) -> AndPredicate + where + B: Predicate, + Self: Sized, + { + AndPredicate::new(self, other) + } + + /// Compute the logical OR of two `Predicate` results, returning the result. + /// + /// # Examples + /// + /// ``` + /// use predicates::prelude::*; + /// + /// let predicate_fn1 = predicate::always().or(predicate::always()); + /// let predicate_fn2 = predicate::always().or(predicate::never()); + /// let predicate_fn3 = predicate::never().or(predicate::never()); + /// assert_eq!(true, predicate_fn1.eval(&4)); + /// assert_eq!(true, predicate_fn2.eval(&4)); + /// assert_eq!(false, predicate_fn3.eval(&4)); + fn or(self, other: B) -> OrPredicate + where + B: Predicate, + Self: Sized, + { + OrPredicate::new(self, other) + } + + /// Compute the logical NOT of a `Predicate`, returning the result. + /// + /// # Examples + /// + /// ``` + /// use predicates::prelude::*; + /// + /// let predicate_fn1 = predicate::always().not(); + /// let predicate_fn2 = predicate::never().not(); + /// assert_eq!(false, predicate_fn1.eval(&4)); + /// assert_eq!(true, predicate_fn2.eval(&4)); + fn not(self) -> NotPredicate + where + Self: Sized, + { + NotPredicate::new(self) + } +} + +impl PredicateBooleanExt for P +where + P: Predicate, + Item: ?Sized, +{ +} diff --git a/tools/vendor/predicates/src/boxed.rs b/tools/vendor/predicates/src/boxed.rs new file mode 100644 index 0000000000..2b96e0c201 --- /dev/null +++ b/tools/vendor/predicates/src/boxed.rs @@ -0,0 +1,128 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Predicate that can wrap other dynamically-called predicates in an +//! easy-to-manage type. + +use std::fmt; + +use crate::reflection; +use crate::utils; +use crate::Predicate; + +/// `Predicate` that wraps another `Predicate` as a trait object, allowing +/// sized storage of predicate types. +pub struct BoxPredicate(Box + Send + Sync>); + +impl BoxPredicate +where + Item: ?Sized, +{ + /// Creates a new `BoxPredicate`, a wrapper around a dynamically-dispatched + /// `Predicate` type with useful trait impls. + pub fn new

(inner: P) -> BoxPredicate + where + P: Predicate + Send + Sync + 'static, + { + BoxPredicate(Box::new(inner)) + } +} + +impl fmt::Debug for BoxPredicate +where + Item: ?Sized, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("BoxPredicate").finish() + } +} + +impl reflection::PredicateReflection for BoxPredicate +where + Item: ?Sized, +{ + fn parameters<'a>(&'a self) -> Box> + 'a> { + self.0.parameters() + } + + fn children<'a>(&'a self) -> Box> + 'a> { + self.0.children() + } +} + +impl fmt::Display for BoxPredicate +where + Item: ?Sized, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Predicate for BoxPredicate +where + Item: ?Sized, +{ + fn eval(&self, variable: &Item) -> bool { + self.0.eval(variable) + } + + fn find_case<'a>(&'a self, expected: bool, variable: &Item) -> Option> { + utils::default_find_case(self, expected, variable) + } +} + +/// `Predicate` extension for boxing a `Predicate`. +pub trait PredicateBoxExt +where + Self: Predicate, +{ + /// Returns a `BoxPredicate` wrapper around this `Predicate` type. + /// + /// Returns a `BoxPredicate` wrapper around this `Predicate` type. The + /// `BoxPredicate` type has a number of useful properties: + /// + /// - It stores the inner predicate as a trait object, so the type of + /// `BoxPredicate` will always be the same even if steps are added or + /// removed from the predicate. + /// - It is a common type, allowing it to be stored in vectors or other + /// collection types. + /// - It implements `Debug` and `Display`. + /// + /// # Examples + /// + /// ``` + /// use predicates::prelude::*; + /// + /// let predicates = vec![ + /// predicate::always().boxed(), + /// predicate::never().boxed(), + /// ]; + /// assert_eq!(true, predicates[0].eval(&4)); + /// assert_eq!(false, predicates[1].eval(&4)); + /// ``` + fn boxed(self) -> BoxPredicate + where + Self: Sized + Send + Sync + 'static, + { + BoxPredicate::new(self) + } +} + +impl PredicateBoxExt for P where P: Predicate {} + +#[cfg(test)] +mod test { + use crate::prelude::*; + + #[test] + fn unsized_boxed() { + let p = predicate::always().boxed(); + p.eval("4"); + } +} diff --git a/tools/vendor/predicates/src/color.rs b/tools/vendor/predicates/src/color.rs new file mode 100644 index 0000000000..f1b6a2139a --- /dev/null +++ b/tools/vendor/predicates/src/color.rs @@ -0,0 +1,66 @@ +#[derive(Copy, Clone, Debug, Default)] +pub(crate) struct Palette { + description: anstyle::Style, + var: anstyle::Style, + expected: anstyle::Style, +} + +impl Palette { + pub(crate) fn new(alternate: bool) -> Self { + if alternate && cfg!(feature = "color") { + Self { + description: anstyle::AnsiColor::Blue.on_default() | anstyle::Effects::BOLD, + var: anstyle::AnsiColor::Red.on_default() | anstyle::Effects::BOLD, + expected: anstyle::AnsiColor::Green.on_default() | anstyle::Effects::BOLD, + } + } else { + Self::plain() + } + } + + pub(crate) fn plain() -> Self { + Self { + description: Default::default(), + var: Default::default(), + expected: Default::default(), + } + } + + pub(crate) fn description(self, display: D) -> Styled { + Styled::new(display, self.description) + } + + pub(crate) fn var(self, display: D) -> Styled { + Styled::new(display, self.var) + } + + pub(crate) fn expected(self, display: D) -> Styled { + Styled::new(display, self.expected) + } +} + +#[derive(Debug)] +pub(crate) struct Styled { + display: D, + style: anstyle::Style, +} + +impl Styled { + pub(crate) fn new(display: D, style: anstyle::Style) -> Self { + Self { display, style } + } +} + +impl std::fmt::Display for Styled { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if f.alternate() { + write!(f, "{}", self.style.render())?; + self.display.fmt(f)?; + write!(f, "{}", self.style.render_reset())?; + Ok(()) + } else { + self.display.fmt(f) + } + } +} diff --git a/tools/vendor/predicates/src/constant.rs b/tools/vendor/predicates/src/constant.rs new file mode 100644 index 0000000000..79cbb86643 --- /dev/null +++ b/tools/vendor/predicates/src/constant.rs @@ -0,0 +1,83 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Definition of a constant (always true or always false) `Predicate`. + +use std::fmt; + +use crate::reflection; +use crate::utils; +use crate::Predicate; + +/// Predicate that always returns a constant (boolean) result. +/// +/// This is created by the `predicate::always` and `predicate::never` functions. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct BooleanPredicate { + retval: bool, +} + +impl Predicate for BooleanPredicate { + fn eval(&self, _variable: &Item) -> bool { + self.retval + } + + fn find_case<'a>(&'a self, expected: bool, variable: &Item) -> Option> { + utils::default_find_case(self, expected, variable) + } +} + +impl reflection::PredicateReflection for BooleanPredicate { + fn parameters<'a>(&'a self) -> Box> + 'a> { + let params = vec![reflection::Parameter::new("value", &self.retval)]; + Box::new(params.into_iter()) + } +} + +impl fmt::Display for BooleanPredicate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!(f, "{}", palette.expected(self.retval)) + } +} + +/// Creates a new `Predicate` that always returns `true`. +/// +/// # Examples +/// +/// ``` +/// use predicates::prelude::*; +/// +/// let predicate_fn = predicate::always(); +/// assert_eq!(true, predicate_fn.eval(&5)); +/// assert_eq!(true, predicate_fn.eval(&10)); +/// assert_eq!(true, predicate_fn.eval(&15)); +/// // Won't work - Predicates can only operate on a single type +/// // assert_eq!(true, predicate_fn.eval("hello")) +/// ``` +pub fn always() -> BooleanPredicate { + BooleanPredicate { retval: true } +} + +/// Creates a new `Predicate` that always returns `false`. +/// +/// # Examples +/// +/// ``` +/// use predicates::prelude::*; +/// +/// let predicate_fn = predicate::never(); +/// assert_eq!(false, predicate_fn.eval(&5)); +/// assert_eq!(false, predicate_fn.eval(&10)); +/// assert_eq!(false, predicate_fn.eval(&15)); +/// // Won't work - Predicates can only operate on a single type +/// // assert_eq!(false, predicate_fn.eval("hello")) +/// ``` +pub fn never() -> BooleanPredicate { + BooleanPredicate { retval: false } +} diff --git a/tools/vendor/predicates/src/float/close.rs b/tools/vendor/predicates/src/float/close.rs new file mode 100644 index 0000000000..b216ac77d3 --- /dev/null +++ b/tools/vendor/predicates/src/float/close.rs @@ -0,0 +1,158 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::fmt; + +use float_cmp::ApproxEq; +use float_cmp::Ulps; + +use crate::reflection; +use crate::Predicate; + +/// Predicate that ensures two numbers are "close" enough, understanding that rounding errors +/// occur. +/// +/// This is created by the `predicate::float::is_close`. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct IsClosePredicate { + target: f64, + epsilon: f64, + ulps: ::U, +} + +impl IsClosePredicate { + /// Set the amount of error allowed. + /// + /// Values `1`-`5` should work in most cases. Sometimes more control is needed and you will + /// need to set `IsClosePredicate::epsilon` separately from `IsClosePredicate::ulps`. + /// + /// # Examples + /// + /// ``` + /// use predicates::prelude::*; + /// + /// let a = 0.15_f64 + 0.15_f64 + 0.15_f64; + /// let predicate_fn = predicate::float::is_close(a).distance(5); + /// ``` + pub fn distance(mut self, distance: ::U) -> Self { + self.epsilon = (distance as f64) * f64::EPSILON; + self.ulps = distance; + self + } + + /// Set the absolute deviation allowed. + /// + /// This is meant to handle problems near `0`. Values `1.`-`5.` epislons should work in most + /// cases. + /// + /// # Examples + /// + /// ``` + /// use predicates::prelude::*; + /// + /// let a = 0.15_f64 + 0.15_f64 + 0.15_f64; + /// let predicate_fn = predicate::float::is_close(a).epsilon(5.0 * f64::EPSILON); + /// ``` + pub fn epsilon(mut self, epsilon: f64) -> Self { + self.epsilon = epsilon; + self + } + + /// Set the relative deviation allowed. + /// + /// This is meant to handle large numbers. Values `1`-`5` should work in most cases. + /// + /// # Examples + /// + /// ``` + /// use predicates::prelude::*; + /// + /// let a = 0.15_f64 + 0.15_f64 + 0.15_f64; + /// let predicate_fn = predicate::float::is_close(a).ulps(5); + /// ``` + pub fn ulps(mut self, ulps: ::U) -> Self { + self.ulps = ulps; + self + } +} + +impl Predicate for IsClosePredicate { + fn eval(&self, variable: &f64) -> bool { + variable.approx_eq( + self.target, + float_cmp::F64Margin { + epsilon: self.epsilon, + ulps: self.ulps, + }, + ) + } + + fn find_case<'a>(&'a self, expected: bool, variable: &f64) -> Option> { + let actual = self.eval(variable); + if expected == actual { + Some( + reflection::Case::new(Some(self), actual) + .add_product(reflection::Product::new( + "actual epsilon", + (variable - self.target).abs(), + )) + .add_product(reflection::Product::new( + "actual ulps", + variable.ulps(&self.target).abs(), + )), + ) + } else { + None + } + } +} + +impl reflection::PredicateReflection for IsClosePredicate { + fn parameters<'a>(&'a self) -> Box> + 'a> { + let params = vec![ + reflection::Parameter::new("epsilon", &self.epsilon), + reflection::Parameter::new("ulps", &self.ulps), + ]; + Box::new(params.into_iter()) + } +} + +impl fmt::Display for IsClosePredicate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!( + f, + "{} {} {}", + palette.var("var"), + palette.description("!="), + palette.expected(self.target), + ) + } +} + +/// Create a new `Predicate` that ensures two numbers are "close" enough, understanding that +/// rounding errors occur. +/// +/// # Examples +/// +/// ``` +/// use predicates::prelude::*; +/// +/// let a = 0.15_f64 + 0.15_f64 + 0.15_f64; +/// let b = 0.1_f64 + 0.1_f64 + 0.25_f64; +/// let predicate_fn = predicate::float::is_close(a); +/// assert_eq!(true, predicate_fn.eval(&b)); +/// assert_eq!(false, predicate_fn.distance(0).eval(&b)); +/// ``` +pub fn is_close(target: f64) -> IsClosePredicate { + IsClosePredicate { + target, + epsilon: 2.0 * f64::EPSILON, + ulps: 2, + } +} diff --git a/tools/vendor/predicates/src/float/mod.rs b/tools/vendor/predicates/src/float/mod.rs new file mode 100644 index 0000000000..76c3377d54 --- /dev/null +++ b/tools/vendor/predicates/src/float/mod.rs @@ -0,0 +1,16 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Float Predicates +//! +//! This module contains predicates specific to string handling. + +#[cfg(feature = "float-cmp")] +mod close; +#[cfg(feature = "float-cmp")] +pub use self::close::{is_close, IsClosePredicate}; diff --git a/tools/vendor/predicates/src/function.rs b/tools/vendor/predicates/src/function.rs new file mode 100644 index 0000000000..e6eecaaccf --- /dev/null +++ b/tools/vendor/predicates/src/function.rs @@ -0,0 +1,149 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Definition of `Predicate` for wrapping a `Fn(&T) -> bool` + +use std::fmt; +use std::marker::PhantomData; + +use crate::reflection; +use crate::utils; +use crate::Predicate; + +/// Predicate that wraps a function over a reference that returns a `bool`. +/// This type is returned by the `predicate::function` function. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct FnPredicate +where + F: Fn(&T) -> bool, + T: ?Sized, +{ + function: F, + name: &'static str, + _phantom: PhantomData, +} + +unsafe impl Send for FnPredicate +where + F: Send + Fn(&T) -> bool, + T: ?Sized, +{ +} + +unsafe impl Sync for FnPredicate +where + F: Sync + Fn(&T) -> bool, + T: ?Sized, +{ +} + +impl FnPredicate +where + F: Fn(&T) -> bool, + T: ?Sized, +{ + /// Provide a descriptive name for this function. + /// + /// # Examples + /// + /// ``` + /// use predicates::prelude::*; + /// + /// struct Example { + /// string: String, + /// number: i32, + /// } + /// + /// let string_check = predicate::function(|x: &Example| x.string == "hello") + /// .fn_name("is_hello"); + /// println!("predicate: {}", string_check); + /// ``` + pub fn fn_name(mut self, name: &'static str) -> Self { + self.name = name; + self + } +} + +impl Predicate for FnPredicate +where + F: Fn(&T) -> bool, + T: ?Sized, +{ + fn eval(&self, variable: &T) -> bool { + (self.function)(variable) + } + + fn find_case<'a>(&'a self, expected: bool, variable: &T) -> Option> { + utils::default_find_case(self, expected, variable) + } +} + +impl reflection::PredicateReflection for FnPredicate +where + F: Fn(&T) -> bool, + T: ?Sized, +{ +} + +impl fmt::Display for FnPredicate +where + F: Fn(&T) -> bool, + T: ?Sized, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!( + f, + "{}({})", + palette.description(self.name), + palette.var("var"), + ) + } +} + +/// Creates a new predicate that wraps over the given function. The returned +/// type implements `Predicate` and therefore has all combinators available to +/// it. +/// +/// # Examples +/// +/// ``` +/// use predicates::prelude::*; +/// +/// struct Example { +/// string: String, +/// number: i32, +/// } +/// +/// let string_check = predicate::function(|x: &Example| x.string == "hello"); +/// let number_check = predicate::function(|x: &Example| x.number == 42); +/// let predicate_fn = string_check.and(number_check); +/// let good_example = Example { string: "hello".into(), number: 42 }; +/// assert_eq!(true, predicate_fn.eval(&good_example)); +/// let bad_example = Example { string: "goodbye".into(), number: 0 }; +/// assert_eq!(false, predicate_fn.eval(&bad_example)); +/// ``` +pub fn function(function: F) -> FnPredicate +where + F: Fn(&T) -> bool, + T: ?Sized, +{ + FnPredicate { + function, + name: "fn", + _phantom: PhantomData, + } +} + +#[test] +fn str_function() { + let f = function(|x: &str| x == "hello"); + assert!(f.eval("hello")); + assert!(!f.eval("goodbye")); +} diff --git a/tools/vendor/predicates/src/iter.rs b/tools/vendor/predicates/src/iter.rs new file mode 100644 index 0000000000..03b69b21c3 --- /dev/null +++ b/tools/vendor/predicates/src/iter.rs @@ -0,0 +1,330 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Definition of `Predicate`s for comparisons of membership in a set. + +use std::collections::HashSet; +use std::fmt; +use std::hash::Hash; +use std::iter::FromIterator; + +use crate::reflection; +use crate::utils; +use crate::Predicate; + +/// Predicate that returns `true` if `variable` is a member of the pre-defined +/// set, otherwise returns `false`. +/// +/// Note that this implementation places the fewest restrictions on the +/// underlying `Item` type at the expense of having the least performant +/// implementation (linear search). If the type to be searched is `Hash + Eq`, +/// it is much more efficient to use `HashableInPredicate` and +/// `in_hash`. The implementation-specific predicates will be +/// deprecated when Rust supports trait specialization. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct InPredicate +where + T: PartialEq + fmt::Debug, +{ + inner: utils::DebugAdapter>, +} + +impl InPredicate +where + T: Ord + fmt::Debug, +{ + /// Creates a new predicate that will return `true` when the given `variable` is + /// contained with the set of items provided. + /// + /// Note that this implementation requires `Item` to be `Ord`. The + /// `InPredicate` uses a less efficient search algorithm but only + /// requires `Item` implement `PartialEq`. The implementation-specific + /// predicates will be deprecated when Rust supports trait specialization. + /// + /// # Examples + /// + /// ``` + /// use predicates::prelude::*; + /// + /// let predicate_fn = predicate::in_iter(vec![1, 3, 5]).sort(); + /// assert_eq!(true, predicate_fn.eval(&1)); + /// assert_eq!(false, predicate_fn.eval(&2)); + /// assert_eq!(true, predicate_fn.eval(&3)); + /// + /// let predicate_fn = predicate::in_iter(vec!["a", "c", "e"]).sort(); + /// assert_eq!(true, predicate_fn.eval("a")); + /// assert_eq!(false, predicate_fn.eval("b")); + /// assert_eq!(true, predicate_fn.eval("c")); + /// + /// let predicate_fn = predicate::in_iter(vec![String::from("a"), String::from("c"), String::from("e")]).sort(); + /// assert_eq!(true, predicate_fn.eval("a")); + /// assert_eq!(false, predicate_fn.eval("b")); + /// assert_eq!(true, predicate_fn.eval("c")); + /// ``` + pub fn sort(self) -> OrdInPredicate { + let mut items = self.inner.debug; + items.sort(); + OrdInPredicate { + inner: utils::DebugAdapter::new(items), + } + } +} + +impl Predicate

for InPredicate +where + T: std::borrow::Borrow

+ PartialEq + fmt::Debug, + P: PartialEq + fmt::Debug + ?Sized, +{ + fn eval(&self, variable: &P) -> bool { + self.inner.debug.iter().any(|x| x.borrow() == variable) + } + + fn find_case<'a>(&'a self, expected: bool, variable: &P) -> Option> { + utils::default_find_case(self, expected, variable).map(|case| { + case.add_product(reflection::Product::new( + "var", + utils::DebugAdapter::new(variable).to_string(), + )) + }) + } +} + +impl reflection::PredicateReflection for InPredicate +where + T: PartialEq + fmt::Debug, +{ + fn parameters<'a>(&'a self) -> Box> + 'a> { + let params = vec![reflection::Parameter::new("values", &self.inner)]; + Box::new(params.into_iter()) + } +} + +impl fmt::Display for InPredicate +where + T: PartialEq + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!( + f, + "{} {} {}", + palette.var("var"), + palette.description("in"), + palette.expected("values") + ) + } +} + +/// Creates a new predicate that will return `true` when the given `variable` is +/// contained with the set of items provided. +/// +/// Note that this implementation places the fewest restrictions on the +/// underlying `Item` type at the expense of having the least performant +/// implementation (linear search). If the type to be searched is `Hash + Eq`, +/// it is much more efficient to use `HashableInPredicate` and +/// `in_hash`. The implementation-specific predicates will be +/// deprecated when Rust supports trait specialization. +/// +/// If you need to optimize this +/// - Type is `Ord`, call `sort()` on this predicate. +/// - Type is `Hash`, replace `in_iter` with `in_hash`. +/// +/// # Examples +/// +/// ``` +/// use predicates::prelude::*; +/// +/// let predicate_fn = predicate::in_iter(vec![1, 3, 5]); +/// assert_eq!(true, predicate_fn.eval(&1)); +/// assert_eq!(false, predicate_fn.eval(&2)); +/// assert_eq!(true, predicate_fn.eval(&3)); +/// +/// let predicate_fn = predicate::in_iter(vec!["a", "c", "e"]); +/// assert_eq!(true, predicate_fn.eval("a")); +/// assert_eq!(false, predicate_fn.eval("b")); +/// assert_eq!(true, predicate_fn.eval("c")); +/// +/// let predicate_fn = predicate::in_iter(vec![String::from("a"), String::from("c"), String::from("e")]); +/// assert_eq!(true, predicate_fn.eval("a")); +/// assert_eq!(false, predicate_fn.eval("b")); +/// assert_eq!(true, predicate_fn.eval("c")); +/// ``` +pub fn in_iter(iter: I) -> InPredicate +where + T: PartialEq + fmt::Debug, + I: IntoIterator, +{ + InPredicate { + inner: utils::DebugAdapter::new(Vec::from_iter(iter)), + } +} + +/// Predicate that returns `true` if `variable` is a member of the pre-defined +/// set, otherwise returns `false`. +/// +/// Note that this implementation requires `Item` to be `Ord`. The +/// `InPredicate` uses a less efficient search algorithm but only +/// requires `Item` implement `PartialEq`. The implementation-specific +/// predicates will be deprecated when Rust supports trait specialization. +/// +/// This is created by the `predicate::in_iter(...).sort` function. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct OrdInPredicate +where + T: Ord + fmt::Debug, +{ + inner: utils::DebugAdapter>, +} + +impl Predicate

for OrdInPredicate +where + T: std::borrow::Borrow

+ Ord + fmt::Debug, + P: Ord + fmt::Debug + ?Sized, +{ + fn eval(&self, variable: &P) -> bool { + self.inner + .debug + .binary_search_by(|x| x.borrow().cmp(variable)) + .is_ok() + } + + fn find_case<'a>(&'a self, expected: bool, variable: &P) -> Option> { + utils::default_find_case(self, expected, variable).map(|case| { + case.add_product(reflection::Product::new( + "var", + utils::DebugAdapter::new(variable).to_string(), + )) + }) + } +} + +impl reflection::PredicateReflection for OrdInPredicate +where + T: Ord + fmt::Debug, +{ + fn parameters<'a>(&'a self) -> Box> + 'a> { + let params = vec![reflection::Parameter::new("values", &self.inner)]; + Box::new(params.into_iter()) + } +} + +impl fmt::Display for OrdInPredicate +where + T: Ord + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!( + f, + "{} {} {}", + palette.var("var"), + palette.description("in"), + palette.expected("values") + ) + } +} + +/// Predicate that returns `true` if `variable` is a member of the pre-defined +/// `HashSet`, otherwise returns `false`. +/// +/// Note that this implementation requires `Item` to be `Hash + Eq`. The +/// `InPredicate` uses a less efficient search algorithm but only +/// requires `Item` implement `PartialEq`. The implementation-specific +/// predicates will be deprecated when Rust supports trait specialization. +/// +/// This is created by the `predicate::in_hash` function. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct HashableInPredicate +where + T: Hash + Eq + fmt::Debug, +{ + inner: utils::DebugAdapter>, +} + +impl Predicate

for HashableInPredicate +where + T: std::borrow::Borrow

+ Hash + Eq + fmt::Debug, + P: Hash + Eq + fmt::Debug + ?Sized, +{ + fn eval(&self, variable: &P) -> bool { + self.inner.debug.contains(variable) + } + + fn find_case<'a>(&'a self, expected: bool, variable: &P) -> Option> { + utils::default_find_case(self, expected, variable).map(|case| { + case.add_product(reflection::Product::new( + "var", + utils::DebugAdapter::new(variable).to_string(), + )) + }) + } +} + +impl reflection::PredicateReflection for HashableInPredicate +where + T: Hash + Eq + fmt::Debug, +{ + fn parameters<'a>(&'a self) -> Box> + 'a> { + let params = vec![reflection::Parameter::new("values", &self.inner)]; + Box::new(params.into_iter()) + } +} + +impl fmt::Display for HashableInPredicate +where + T: Hash + Eq + fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!( + f, + "{} {} {}", + palette.var("var"), + palette.description("in"), + palette.expected("values") + ) + } +} + +/// Creates a new predicate that will return `true` when the given `variable` is +/// contained with the set of items provided. +/// +/// Note that this implementation requires `Item` to be `Hash + Eq`. The +/// `InPredicate` uses a less efficient search algorithm but only +/// requires `Item` implement `PartialEq`. The implementation-specific +/// predicates will be deprecated when Rust supports trait specialization. +/// +/// # Examples +/// +/// ``` +/// use predicates::prelude::*; +/// +/// let predicate_fn = predicate::in_hash(vec![1, 3, 5]); +/// assert_eq!(true, predicate_fn.eval(&1)); +/// assert_eq!(false, predicate_fn.eval(&2)); +/// assert_eq!(true, predicate_fn.eval(&3)); +/// +/// let predicate_fn = predicate::in_hash(vec!["a", "c", "e"]); +/// assert_eq!(true, predicate_fn.eval("a")); +/// assert_eq!(false, predicate_fn.eval("b")); +/// assert_eq!(true, predicate_fn.eval("c")); +/// +/// let predicate_fn = predicate::in_hash(vec![String::from("a"), String::from("c"), String::from("e")]); +/// assert_eq!(true, predicate_fn.eval("a")); +/// assert_eq!(false, predicate_fn.eval("b")); +/// assert_eq!(true, predicate_fn.eval("c")); +/// ``` +pub fn in_hash(iter: I) -> HashableInPredicate +where + T: Hash + Eq + fmt::Debug, + I: IntoIterator, +{ + HashableInPredicate { + inner: utils::DebugAdapter::new(HashSet::from_iter(iter)), + } +} diff --git a/tools/vendor/predicates/src/lib.rs b/tools/vendor/predicates/src/lib.rs new file mode 100644 index 0000000000..2c9b19b786 --- /dev/null +++ b/tools/vendor/predicates/src/lib.rs @@ -0,0 +1,229 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Composable first-order predicate functions. +//! +//! This library implements an interface to "predicates" - boolean-valued +//! functions of one argument. This allows combinatorial logic to be created and +//! assembled at runtime and then used one or more times for evaluating values. +//! This sort of object is really useful when creating filters and checks that +//! can be changed at runtime with user interaction - it allows a clean +//! separation of concerns where the configuration code can be used to build up +//! a predicate, and then that predicate can be given to the code that does the +//! actual filtering without the filtering code knowing anything about user +//! configuration. See the examples for how this can work. +//! +//! ## Installation +//! +//! Add this to your `Cargo.toml`: +//! +//! ```toml +//! [dependencies] +//! predicates = "3.1.3" +//! ``` +//! +//! A [prelude] is available to bring in all extension traits as well as providing +//! `prelude::predicate` which focuses on the 90% case of the API. +//! ```rust +//! use predicates::prelude::*; +//! ``` +//! +//! ## Examples +//! +//! The simplest predicates are [`predicate::always`] and [`predicate::never`], which always +//! returns `true` and always returns `false`, respectively. The values are simply ignored when +//! evaluating against these predicates: +//! ```rust +//! use predicates::prelude::*; +//! +//! let always_true = predicate::always(); +//! assert_eq!(true, always_true.eval(&5)); +//! let always_false = predicate::never(); +//! assert_eq!(false, always_false.eval(&5)); +//! ``` +//! +//! Pre-made predicates are available for types that implement the `PartialOrd` and +//! `PartialEq` traits. The following example uses `lt`, but `eq`, `ne`, `le`, `gt`, +//! `ge` are also available. +//! ```rust +//! use predicates::prelude::*; +//! +//! let less_than_ten = predicate::lt(10); +//! assert_eq!(true, less_than_ten.eval(&9)); +//! assert_eq!(false, less_than_ten.eval(&11)); +//! ``` +//! +//! Any function over a reference to the desired `Item` that returns `bool` +//! can easily be made into a `Predicate` using the [`predicate::function`] +//! function. +//! ```rust +//! use predicates::prelude::*; +//! +//! let bound = 5; +//! let predicate_fn = predicate::function(|&x| x >= bound); +//! let between_5_and_10 = predicate_fn.and(predicate::le(10)); +//! assert_eq!(true, between_5_and_10.eval(&7)); +//! assert_eq!(false, between_5_and_10.eval(&3)); +//! ``` +//! +//! The `Predicate` type is actually a trait, and that trait implements a +//! number of useful combinator functions. For example, evaluating for a value +//! between two other values can be accomplished as follows: +//! ```rust +//! use predicates::prelude::*; +//! +//! let between_5_and_10 = predicate::ge(5).and(predicate::le(10)); +//! assert_eq!(true, between_5_and_10.eval(&7)); +//! assert_eq!(false, between_5_and_10.eval(&11)); +//! assert_eq!(false, between_5_and_10.eval(&4)); +//! ``` +//! +//! The `Predicate` trait is pretty simple, the core of it is an +//! implementation of a `eval` function that takes a single argument and +//! returns a `bool`. Implementing a custom `Predicate` still allows all the +//! usual combinators of the `Predicate` trait to work! +//! ```rust +//! use std::fmt; +//! +//! use predicates::prelude::*; +//! +//! struct IsTheAnswer; +//! impl Predicate for IsTheAnswer { +//! fn eval(&self, variable: &i32) -> bool { +//! *variable == 42 +//! } +//! } +//! impl predicates::reflection::PredicateReflection for IsTheAnswer {} +//! impl fmt::Display for IsTheAnswer { +//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +//! write!(f, "var.is_the_answer()") +//! } +//! } +//! +//! assert_eq!(true, IsTheAnswer.eval(&42)); +//! let almost_the_answer = IsTheAnswer.or(predicate::in_iter(vec![41, 43])); +//! assert_eq!(true, almost_the_answer.eval(&41)); +//! ``` +//! +//! ## Choosing a Predicate +//! +//! General predicates +//! - [`predicate::always`] +//! - [`predicate::never`] +//! - [`predicate::function`] +//! - [`predicate::in_iter`]: Specified value must be in the `Iterator`. +//! - [`predicate::in_iter(...).sort`]: Optimization for repeatedly called predicates. +//! - [`predicate::in_hash`]: Optimization for repeatedly called predicates. +//! - [`predicate::eq`] +//! - [`predicate::float::is_close`]: Use this instead of `eq` for floating point values. +//! - [`predicate::ne`] +//! - [`predicate::ge`] +//! - [`predicate::gt`] +//! - [`predicate::le`] +//! - [`predicate::lt`] +//! - [`predicate::name`]: Improve readability of failure reporting by providing a meaningful name. +//! +//! Combinators +//! - [`pred_a.and(pred_b)`]: Both predicates must succeed. +//! - [`pred_a.or(pred_b)`]: One or both predicates must succeed. +//! - [`pred_a.not()`]: The predicate must fail. +//! +//! `String` predicates +//! - [`predicate::str::is_empty`]: Specified string must be empty +//! - [`str_pred = predicate::path::eq_file(...).utf8`]: Specified string must equal the contents +//! of the given file. +//! - [`predicate::str::diff`]: Same as `eq` except report a diff. See [`DifferencePredicate`] +//! for more features. +//! - [`predicate::str::starts_with`]: Specified string must start with the given needle. +//! - [`predicate::str::ends_with`]: Specified string must end with the given needle. +//! - [`predicate::str::contains`]: Specified string must contain the given needle. +//! - [`predicate::str::contains(...).count`]: Required number of times the needle must show up. +//! - [`predicate::str::is_match`]: Specified string must match the given regex. +//! - [`predicate::str::is_match(...).count`]: Required number of times the match must show up. +//! - [`str_pred.trim`]: Trim whitespace before passing it to `str_pred`. +//! - [`str_pred.normalize`]: Normalize the line endings before passing it to `str_pred`. +//! - [`bytes_pred = str_pred.from_utf8()`]: Reuse string predicates in other contexts, like the +//! file system. +//! +//! File system predicates +//! - [`predicate::path::exists`]: Specified path must exist on disk. +//! - [`predicate::path::missing`]: Specified path must not exist on disk. +//! - [`predicate::path::is_dir`]: Specified path is a directory. +//! - [`predicate::path::is_file`]: Specified path is a file. +//! - [`predicate::path::is_symlink`]: Specified path is a symlink. +//! - [`path_pred = predicate::path::eq_file`]: Specified path's contents must equal the contents of the given +//! file. +//! - [`path_pred = bytes_pred.from_file_path`]: Specified path's contents must equal the `bytes_pred`. +//! +//! [`DifferencePredicate`]: crate::str::DifferencePredicate +//! [`bytes_pred = str_pred.from_utf8()`]: prelude::PredicateStrExt::from_utf8() +//! [`path_pred = bytes_pred.from_file_path`]: prelude::PredicateFileContentExt::from_file_path() +//! [`path_pred = predicate::path::eq_file`]: prelude::predicate::path::eq_file() +//! [`pred_a.and(pred_b)`]: boolean::PredicateBooleanExt::and() +//! [`pred_a.not()`]: boolean::PredicateBooleanExt::not() +//! [`pred_a.or(pred_b)`]: boolean::PredicateBooleanExt::or() +//! [`predicate::always`]: constant::always() +//! [`predicate::eq`]: ord::eq() +//! [`predicate::float::is_close`]: prelude::predicate::float::is_close() +//! [`predicate::function`]: function::function() +//! [`predicate::ge`]: ord::ge() +//! [`predicate::gt`]: ord::gt() +//! [`predicate::in_hash`]: iter::in_hash() +//! [`predicate::in_iter(...).sort`]: iter::InPredicate::sort() +//! [`predicate::in_iter`]: iter::in_iter() +//! [`predicate::le`]: ord::le() +//! [`predicate::lt`]: ord::lt() +//! [`predicate::name`]: name::PredicateNameExt::name() +//! [`predicate::ne`]: ord::ne() +//! [`predicate::never`]: constant::never() +//! [`predicate::path::exists`]: prelude::predicate::path::exists() +//! [`predicate::path::is_dir`]: prelude::predicate::path::is_dir() +//! [`predicate::path::is_file`]: prelude::predicate::path::is_file() +//! [`predicate::path::is_symlink`]: prelude::predicate::path::is_symlink() +//! [`predicate::path::missing`]: prelude::predicate::path::missing() +//! [`predicate::str::contains(...).count`]: str::ContainsPredicate::count() +//! [`predicate::str::contains`]: prelude::predicate::str::contains() +//! [`predicate::str::diff`]: prelude::predicate::str::diff() +//! [`predicate::str::ends_with`]: prelude::predicate::str::ends_with() +//! [`predicate::str::is_empty`]: prelude::predicate::str::is_empty() +//! [`predicate::str::is_match(...).count`]: str::RegexPredicate::count() +//! [`predicate::str::is_match`]: prelude::predicate::str::is_match() +//! [`predicate::str::starts_with`]: prelude::predicate::str::starts_with() +//! [`str_pred = predicate::path::eq_file(...).utf8`]: path::BinaryFilePredicate::utf8() +//! [`str_pred.normalize`]: prelude::PredicateStrExt::normalize() +//! [`str_pred.trim`]: prelude::PredicateStrExt::trim() + +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![warn(missing_docs)] +#![warn(clippy::print_stderr)] +#![warn(clippy::print_stdout)] + +pub mod prelude; + +pub use predicates_core::*; +mod boxed; +pub use crate::boxed::*; + +// core predicates +pub mod constant; +pub mod function; +pub mod iter; +pub mod name; +pub mod ord; + +// combinators +pub mod boolean; + +// specialized primitive `Predicate` types +pub mod float; +pub mod path; +pub mod str; + +mod color; +use color::Palette; +mod utils; diff --git a/tools/vendor/predicates/src/name.rs b/tools/vendor/predicates/src/name.rs new file mode 100644 index 0000000000..d204145b12 --- /dev/null +++ b/tools/vendor/predicates/src/name.rs @@ -0,0 +1,115 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Name predicate expressions. + +use std::fmt; +use std::marker::PhantomData; + +use crate::reflection; +use crate::Predicate; + +/// Augment an existing predicate with a name. +/// +/// This is created by the `PredicateNameExt::name` function. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct NamePredicate +where + M: Predicate, + Item: ?Sized, +{ + inner: M, + name: &'static str, + _phantom: PhantomData, +} + +unsafe impl Send for NamePredicate +where + M: Predicate + Send, + Item: ?Sized, +{ +} + +unsafe impl Sync for NamePredicate +where + M: Predicate + Sync, + Item: ?Sized, +{ +} + +impl Predicate for NamePredicate +where + M: Predicate, + Item: ?Sized, +{ + fn eval(&self, item: &Item) -> bool { + self.inner.eval(item) + } + + fn find_case<'a>(&'a self, expected: bool, variable: &Item) -> Option> { + self.inner + .find_case(expected, variable) + .map(|child_case| reflection::Case::new(Some(self), expected).add_child(child_case)) + } +} + +impl reflection::PredicateReflection for NamePredicate +where + M: Predicate, + Item: ?Sized, +{ + fn children<'a>(&'a self) -> Box> + 'a> { + let params = vec![reflection::Child::new(self.name, &self.inner)]; + Box::new(params.into_iter()) + } +} + +impl fmt::Display for NamePredicate +where + M: Predicate, + Item: ?Sized, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!(f, "{}", palette.description(self.name)) + } +} + +/// `Predicate` extension that adds naming predicate expressions. +pub trait PredicateNameExt +where + Self: Predicate, +{ + /// Name a predicate expression. + /// + /// # Examples + /// + /// ``` + /// use predicates::prelude::*; + /// + /// let predicate_fn = predicate::str::is_empty().not().name("non-empty"); + /// println!("{}", predicate_fn); + /// ``` + fn name(self, name: &'static str) -> NamePredicate + where + Self: Sized, + { + NamePredicate { + inner: self, + name, + _phantom: PhantomData, + } + } +} + +impl PredicateNameExt for P +where + P: Predicate, + Item: ?Sized, +{ +} diff --git a/tools/vendor/predicates/src/ord.rs b/tools/vendor/predicates/src/ord.rs new file mode 100644 index 0000000000..4c2305c7a9 --- /dev/null +++ b/tools/vendor/predicates/src/ord.rs @@ -0,0 +1,304 @@ +// Copyright (c) 2018, 2022 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Definition of `Predicate`s for comparisons over `Ord` and `Eq` types. + +use std::fmt; + +use crate::reflection; +use crate::utils; +use crate::Predicate; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum EqOps { + Equal, + NotEqual, +} + +impl fmt::Display for EqOps { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let op = match *self { + EqOps::Equal => "==", + EqOps::NotEqual => "!=", + }; + write!(f, "{op}") + } +} + +/// Predicate that returns `true` if `variable` matches the pre-defined `Eq` +/// value, otherwise returns `false`. +/// +/// This is created by the `predicate::{eq, ne}` functions. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct EqPredicate { + constant: T, + op: EqOps, +} + +impl Predicate

for EqPredicate +where + T: std::borrow::Borrow

+ fmt::Debug, + P: fmt::Debug + PartialEq + ?Sized, +{ + fn eval(&self, variable: &P) -> bool { + match self.op { + EqOps::Equal => variable.eq(self.constant.borrow()), + EqOps::NotEqual => variable.ne(self.constant.borrow()), + } + } + + fn find_case<'a>(&'a self, expected: bool, variable: &P) -> Option> { + utils::default_find_case(self, expected, variable).map(|case| { + case.add_product(reflection::Product::new( + "var", + utils::DebugAdapter::new(variable).to_string(), + )) + }) + } +} + +impl reflection::PredicateReflection for EqPredicate where T: fmt::Debug {} + +impl fmt::Display for EqPredicate +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!( + f, + "{} {} {}", + palette.var("var"), + palette.description(self.op), + palette.expected(utils::DebugAdapter::new(&self.constant)), + ) + } +} + +/// Creates a new predicate that will return `true` when the given `variable` is +/// equal to a pre-defined value. +/// +/// # Examples +/// +/// ``` +/// use predicates::prelude::*; +/// +/// let predicate_fn = predicate::eq(5); +/// assert_eq!(true, predicate_fn.eval(&5)); +/// assert_eq!(false, predicate_fn.eval(&10)); +/// +/// let predicate_fn = predicate::eq("Hello"); +/// assert_eq!(true, predicate_fn.eval("Hello")); +/// assert_eq!(false, predicate_fn.eval("Goodbye")); +/// +/// let predicate_fn = predicate::eq(String::from("Hello")); +/// assert_eq!(true, predicate_fn.eval("Hello")); +/// assert_eq!(false, predicate_fn.eval("Goodbye")); +/// ``` +pub fn eq(constant: T) -> EqPredicate +where + T: fmt::Debug + PartialEq, +{ + EqPredicate { + constant, + op: EqOps::Equal, + } +} + +/// Creates a new predicate that will return `true` when the given `variable` is +/// _not_ equal to a pre-defined value. +/// +/// # Examples +/// +/// ``` +/// use predicates::prelude::*; +/// +/// let predicate_fn = predicate::ne(5); +/// assert_eq!(false, predicate_fn.eval(&5)); +/// assert_eq!(true, predicate_fn.eval(&10)); +/// ``` +pub fn ne(constant: T) -> EqPredicate +where + T: PartialEq + fmt::Debug, +{ + EqPredicate { + constant, + op: EqOps::NotEqual, + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum OrdOps { + LessThan, + LessThanOrEqual, + GreaterThanOrEqual, + GreaterThan, +} + +impl fmt::Display for OrdOps { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let op = match *self { + OrdOps::LessThan => "<", + OrdOps::LessThanOrEqual => "<=", + OrdOps::GreaterThanOrEqual => ">=", + OrdOps::GreaterThan => ">", + }; + write!(f, "{op}") + } +} + +/// Predicate that returns `true` if `variable` matches the pre-defined `Ord` +/// value, otherwise returns `false`. +/// +/// This is created by the `predicate::{gt, ge, lt, le}` functions. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct OrdPredicate { + constant: T, + op: OrdOps, +} + +impl Predicate

for OrdPredicate +where + T: std::borrow::Borrow

+ fmt::Debug, + P: fmt::Debug + PartialOrd + ?Sized, +{ + fn eval(&self, variable: &P) -> bool { + match self.op { + OrdOps::LessThan => variable.lt(self.constant.borrow()), + OrdOps::LessThanOrEqual => variable.le(self.constant.borrow()), + OrdOps::GreaterThanOrEqual => variable.ge(self.constant.borrow()), + OrdOps::GreaterThan => variable.gt(self.constant.borrow()), + } + } + + fn find_case<'a>(&'a self, expected: bool, variable: &P) -> Option> { + utils::default_find_case(self, expected, variable).map(|case| { + case.add_product(reflection::Product::new( + "var", + utils::DebugAdapter::new(variable).to_string(), + )) + }) + } +} + +impl reflection::PredicateReflection for OrdPredicate where T: fmt::Debug {} + +impl fmt::Display for OrdPredicate +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!( + f, + "{} {} {}", + palette.var("var"), + palette.description(self.op), + palette.expected(utils::DebugAdapter::new(&self.constant)), + ) + } +} + +/// Creates a new predicate that will return `true` when the given `variable` is +/// less than a pre-defined value. +/// +/// # Examples +/// +/// ``` +/// use predicates::prelude::*; +/// +/// let predicate_fn = predicate::lt(5); +/// assert_eq!(true, predicate_fn.eval(&4)); +/// assert_eq!(false, predicate_fn.eval(&6)); +/// +/// let predicate_fn = predicate::lt("b"); +/// assert_eq!(true, predicate_fn.eval("a")); +/// assert_eq!(false, predicate_fn.eval("c")); +/// +/// let predicate_fn = predicate::lt(String::from("b")); +/// assert_eq!(true, predicate_fn.eval("a")); +/// assert_eq!(false, predicate_fn.eval("c")); +/// ``` +pub fn lt(constant: T) -> OrdPredicate +where + T: fmt::Debug + PartialOrd, +{ + OrdPredicate { + constant, + op: OrdOps::LessThan, + } +} + +/// Creates a new predicate that will return `true` when the given `variable` is +/// less than or equal to a pre-defined value. +/// +/// # Examples +/// +/// ``` +/// use predicates::prelude::*; +/// +/// let predicate_fn = predicate::le(5); +/// assert_eq!(true, predicate_fn.eval(&4)); +/// assert_eq!(true, predicate_fn.eval(&5)); +/// assert_eq!(false, predicate_fn.eval(&6)); +/// ``` +pub fn le(constant: T) -> OrdPredicate +where + T: PartialOrd + fmt::Debug, +{ + OrdPredicate { + constant, + op: OrdOps::LessThanOrEqual, + } +} + +/// Creates a new predicate that will return `true` when the given `variable` is +/// greater than or equal to a pre-defined value. +/// +/// # Examples +/// +/// ``` +/// use predicates::prelude::*; +/// +/// let predicate = predicate::ge(5); +/// assert_eq!(false, predicate.eval(&4)); +/// assert_eq!(true, predicate.eval(&5)); +/// assert_eq!(true, predicate.eval(&6)); +/// ``` +pub fn ge(constant: T) -> OrdPredicate +where + T: PartialOrd + fmt::Debug, +{ + OrdPredicate { + constant, + op: OrdOps::GreaterThanOrEqual, + } +} + +/// Creates a new predicate that will return `true` when the given `variable` is +/// greater than a pre-defined value. +/// +/// # Examples +/// +/// ``` +/// use predicates::prelude::*; +/// +/// let predicate_fn = predicate::gt(5); +/// assert_eq!(false, predicate_fn.eval(&4)); +/// assert_eq!(false, predicate_fn.eval(&5)); +/// assert_eq!(true, predicate_fn.eval(&6)); +/// ``` +pub fn gt(constant: T) -> OrdPredicate +where + T: PartialOrd + fmt::Debug, +{ + OrdPredicate { + constant, + op: OrdOps::GreaterThan, + } +} diff --git a/tools/vendor/predicates/src/path/existence.rs b/tools/vendor/predicates/src/path/existence.rs new file mode 100644 index 0000000000..343a9c16a2 --- /dev/null +++ b/tools/vendor/predicates/src/path/existence.rs @@ -0,0 +1,85 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::fmt; +use std::path; + +use crate::reflection; +use crate::utils; +use crate::Predicate; + +/// Predicate that checks if a file is present +/// +/// This is created by the `predicate::path::exists` and `predicate::path::missing`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ExistencePredicate { + exists: bool, +} + +impl Predicate for ExistencePredicate { + fn eval(&self, path: &path::Path) -> bool { + path.exists() == self.exists + } + + fn find_case<'a>( + &'a self, + expected: bool, + variable: &path::Path, + ) -> Option> { + utils::default_find_case(self, expected, variable).map(|case| { + case.add_product(reflection::Product::new( + "var", + variable.display().to_string(), + )) + }) + } +} + +impl reflection::PredicateReflection for ExistencePredicate {} + +impl fmt::Display for ExistencePredicate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!( + f, + "{}({})", + palette.description(if self.exists { "exists" } else { "missing" }), + palette.var("var") + ) + } +} + +/// Creates a new `Predicate` that ensures the path exists. +/// +/// # Examples +/// +/// ``` +/// use std::path::Path; +/// use predicates::prelude::*; +/// +/// let predicate_fn = predicate::path::exists(); +/// assert_eq!(true, predicate_fn.eval(Path::new("Cargo.toml"))); +/// ``` +pub fn exists() -> ExistencePredicate { + ExistencePredicate { exists: true } +} + +/// Creates a new `Predicate` that ensures the path doesn't exist. +/// +/// # Examples +/// +/// ``` +/// use std::path::Path; +/// use predicates::prelude::*; +/// +/// let predicate_fn = predicate::path::missing(); +/// assert_eq!(true, predicate_fn.eval(Path::new("non-existent-file.foo"))); +/// ``` +pub fn missing() -> ExistencePredicate { + ExistencePredicate { exists: false } +} diff --git a/tools/vendor/predicates/src/path/fc.rs b/tools/vendor/predicates/src/path/fc.rs new file mode 100644 index 0000000000..f16c156e6f --- /dev/null +++ b/tools/vendor/predicates/src/path/fc.rs @@ -0,0 +1,121 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::fmt; +use std::fs; +use std::io::{self, Read}; +use std::path; + +use crate::reflection; +use crate::Predicate; + +fn read_file(path: &path::Path) -> io::Result> { + let mut buffer = Vec::new(); + fs::File::open(path)?.read_to_end(&mut buffer)?; + Ok(buffer) +} + +/// Predicate adapter that converts a `path` predicate to a byte predicate on its content. +/// +/// This is created by `pred.from_path()`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct FileContentPredicate

+where + P: Predicate<[u8]>, +{ + p: P, +} + +impl

FileContentPredicate

+where + P: Predicate<[u8]>, +{ + fn eval(&self, path: &path::Path) -> io::Result { + let buffer = read_file(path)?; + Ok(self.p.eval(&buffer)) + } +} + +impl

reflection::PredicateReflection for FileContentPredicate

+where + P: Predicate<[u8]>, +{ + fn children<'a>(&'a self) -> Box> + 'a> { + let params = vec![reflection::Child::new("predicate", &self.p)]; + Box::new(params.into_iter()) + } +} + +impl

fmt::Display for FileContentPredicate

+where + P: Predicate<[u8]>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.p.fmt(f) + } +} + +impl

Predicate for FileContentPredicate

+where + P: Predicate<[u8]>, +{ + fn eval(&self, path: &path::Path) -> bool { + self.eval(path).unwrap_or(false) + } + + fn find_case<'a>( + &'a self, + expected: bool, + variable: &path::Path, + ) -> Option> { + let buffer = read_file(variable); + match (expected, buffer) { + (_, Ok(buffer)) => self.p.find_case(expected, &buffer).map(|case| { + case.add_product(reflection::Product::new( + "var", + variable.display().to_string(), + )) + }), + (true, Err(_)) => None, + (false, Err(err)) => Some( + reflection::Case::new(Some(self), false) + .add_product(reflection::Product::new( + "var", + variable.display().to_string(), + )) + .add_product(reflection::Product::new("error", err)), + ), + } + } +} + +/// `Predicate` extension adapting a `slice` Predicate. +pub trait PredicateFileContentExt +where + Self: Predicate<[u8]>, + Self: Sized, +{ + /// Returns a `FileContentPredicate` that adapts `Self` to a file content `Predicate`. + /// + /// # Examples + /// + /// ``` + /// use predicates::prelude::*; + /// use std::path::Path; + /// + /// let predicate_fn = predicate::str::is_empty().not().from_utf8().from_file_path(); + /// assert_eq!(true, predicate_fn.eval(Path::new("./tests/hello_world"))); + /// assert_eq!(false, predicate_fn.eval(Path::new("./tests/empty_file"))); + /// ``` + #[allow(clippy::wrong_self_convention)] + fn from_file_path(self) -> FileContentPredicate { + FileContentPredicate { p: self } + } +} + +impl

PredicateFileContentExt for P where P: Predicate<[u8]> {} diff --git a/tools/vendor/predicates/src/path/fs.rs b/tools/vendor/predicates/src/path/fs.rs new file mode 100644 index 0000000000..9db46b2b3c --- /dev/null +++ b/tools/vendor/predicates/src/path/fs.rs @@ -0,0 +1,179 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::fmt; +use std::fs; +use std::io::{self, Read}; +use std::path; + +use crate::reflection; +use crate::utils; +use crate::Predicate; + +fn read_file(path: &path::Path) -> io::Result> { + let mut buffer = Vec::new(); + fs::File::open(path)?.read_to_end(&mut buffer)?; + Ok(buffer) +} + +/// Predicate that compares file matches +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BinaryFilePredicate { + path: path::PathBuf, + content: utils::DebugAdapter>, +} + +impl BinaryFilePredicate { + fn eval(&self, path: &path::Path) -> io::Result { + let content = read_file(path)?; + Ok(self.content.debug == content) + } + + /// Creates a new `Predicate` that ensures complete equality + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// use predicates::prelude::*; + /// + /// let predicate_file = predicate::path::eq_file(Path::new("Cargo.toml")).utf8().unwrap(); + /// assert_eq!(true, predicate_file.eval(Path::new("Cargo.toml"))); + /// assert_eq!(false, predicate_file.eval(Path::new("Cargo.lock"))); + /// assert_eq!(false, predicate_file.eval(Path::new("src"))); + /// + /// assert_eq!(false, predicate_file.eval("Not a real Cargo.toml file content")); + /// ``` + pub fn utf8(self) -> Option { + let path = self.path; + let content = String::from_utf8(self.content.debug).ok()?; + Some(StrFilePredicate { path, content }) + } +} + +impl Predicate for BinaryFilePredicate { + fn eval(&self, path: &path::Path) -> bool { + self.eval(path).unwrap_or(false) + } + + fn find_case<'a>( + &'a self, + expected: bool, + variable: &path::Path, + ) -> Option> { + utils::default_find_case(self, expected, variable) + } +} + +impl Predicate<[u8]> for BinaryFilePredicate { + fn eval(&self, actual: &[u8]) -> bool { + self.content.debug == actual + } + + fn find_case<'a>(&'a self, expected: bool, variable: &[u8]) -> Option> { + utils::default_find_case(self, expected, variable) + } +} + +impl reflection::PredicateReflection for BinaryFilePredicate { + fn parameters<'a>(&'a self) -> Box> + 'a> { + let params = vec![reflection::Parameter::new("content", &self.content)]; + Box::new(params.into_iter()) + } +} + +impl fmt::Display for BinaryFilePredicate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!( + f, + "{} {} {}", + palette.var("var"), + palette.description("is"), + palette.expected(self.path.display()) + ) + } +} + +/// Creates a new `Predicate` that ensures complete equality +/// +/// # Examples +/// +/// ``` +/// use std::path::Path; +/// use predicates::prelude::*; +/// +/// let predicate_file = predicate::path::eq_file(Path::new("Cargo.toml")); +/// assert_eq!(true, predicate_file.eval(Path::new("Cargo.toml"))); +/// assert_eq!(false, predicate_file.eval(Path::new("src"))); +/// assert_eq!(false, predicate_file.eval(Path::new("src"))); +/// ``` +pub fn eq_file>(path: P) -> BinaryFilePredicate { + let path = path.into(); + let content = utils::DebugAdapter::new(read_file(&path).unwrap()); + BinaryFilePredicate { path, content } +} + +/// Predicate that compares string content of files +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StrFilePredicate { + path: path::PathBuf, + content: String, +} + +impl StrFilePredicate { + fn eval(&self, path: &path::Path) -> Option { + let content = read_file(path).ok()?; + let content = String::from_utf8(content).ok()?; + Some(self.content == content) + } +} + +impl Predicate for StrFilePredicate { + fn eval(&self, path: &path::Path) -> bool { + self.eval(path).unwrap_or(false) + } + + fn find_case<'a>( + &'a self, + expected: bool, + variable: &path::Path, + ) -> Option> { + utils::default_find_case(self, expected, variable) + } +} + +impl Predicate for StrFilePredicate { + fn eval(&self, actual: &str) -> bool { + self.content == actual + } + + fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option> { + utils::default_find_case(self, expected, variable) + } +} + +impl reflection::PredicateReflection for StrFilePredicate { + fn parameters<'a>(&'a self) -> Box> + 'a> { + let params = vec![reflection::Parameter::new("content", &self.content)]; + Box::new(params.into_iter()) + } +} + +impl fmt::Display for StrFilePredicate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!( + f, + "{} {} {}", + palette.var("var"), + palette.description("is"), + palette.expected(self.path.display()) + ) + } +} diff --git a/tools/vendor/predicates/src/path/ft.rs b/tools/vendor/predicates/src/path/ft.rs new file mode 100644 index 0000000000..eec09f78b3 --- /dev/null +++ b/tools/vendor/predicates/src/path/ft.rs @@ -0,0 +1,207 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::fmt; +use std::fs; +use std::io; +use std::path; + +use crate::reflection; +use crate::Predicate; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum FileType { + File, + Dir, + Symlink, +} + +impl FileType { + fn from_path(path: &path::Path, follow: bool) -> io::Result { + let file_type = if follow { + path.metadata() + } else { + path.symlink_metadata() + }? + .file_type(); + if file_type.is_dir() { + return Ok(FileType::Dir); + } + if file_type.is_file() { + return Ok(FileType::File); + } + Ok(FileType::Symlink) + } + + fn eval(self, ft: fs::FileType) -> bool { + match self { + FileType::File => ft.is_file(), + FileType::Dir => ft.is_dir(), + FileType::Symlink => ft.is_symlink(), + } + } +} + +impl fmt::Display for FileType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let t = match *self { + FileType::File => "file", + FileType::Dir => "dir", + FileType::Symlink => "symlink", + }; + write!(f, "{t}") + } +} + +/// Predicate that checks the `std::fs::FileType`. +/// +/// This is created by the `predicate::path::is_file`, `predicate::path::is_dir`, and `predicate::path::is_symlink`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct FileTypePredicate { + ft: FileType, + follow: bool, +} + +impl FileTypePredicate { + /// Follow symbolic links. + /// + /// When yes is true, symbolic links are followed as if they were normal directories and files. + /// + /// Default: disabled. + pub fn follow_links(mut self, yes: bool) -> Self { + self.follow = yes; + self + } + + /// Allow to create an `FileTypePredicate` from a `path` + pub fn from_path(path: &path::Path) -> io::Result { + Ok(FileTypePredicate { + ft: FileType::from_path(path, true)?, + follow: true, + }) + } +} + +impl Predicate for FileTypePredicate { + fn eval(&self, path: &path::Path) -> bool { + let metadata = if self.follow { + path.metadata() + } else { + path.symlink_metadata() + }; + metadata + .map(|m| self.ft.eval(m.file_type())) + .unwrap_or(false) + } + + fn find_case<'a>( + &'a self, + expected: bool, + variable: &path::Path, + ) -> Option> { + let actual_type = FileType::from_path(variable, self.follow); + match (expected, actual_type) { + (_, Ok(actual_type)) => { + let result = self.ft == actual_type; + if result == expected { + Some( + reflection::Case::new(Some(self), result) + .add_product(reflection::Product::new("actual filetype", actual_type)), + ) + } else { + None + } + } + (true, Err(_)) => None, + (false, Err(err)) => Some( + reflection::Case::new(Some(self), false) + .add_product(reflection::Product::new("error", err)), + ), + } + } +} + +impl reflection::PredicateReflection for FileTypePredicate { + fn parameters<'a>(&'a self) -> Box> + 'a> { + let params = vec![reflection::Parameter::new("follow", &self.follow)]; + Box::new(params.into_iter()) + } +} + +impl fmt::Display for FileTypePredicate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!( + f, + "{} {} {}", + palette.var("var"), + palette.description("is"), + palette.expected(self.ft) + ) + } +} + +/// Creates a new `Predicate` that ensures the path points to a file. +/// +/// # Examples +/// +/// ``` +/// use std::path::Path; +/// use predicates::prelude::*; +/// +/// let predicate_fn = predicate::path::is_file(); +/// assert_eq!(true, predicate_fn.eval(Path::new("Cargo.toml"))); +/// assert_eq!(false, predicate_fn.eval(Path::new("src"))); +/// assert_eq!(false, predicate_fn.eval(Path::new("non-existent-file.foo"))); +/// ``` +pub fn is_file() -> FileTypePredicate { + FileTypePredicate { + ft: FileType::File, + follow: false, + } +} + +/// Creates a new `Predicate` that ensures the path points to a directory. +/// +/// # Examples +/// +/// ``` +/// use std::path::Path; +/// use predicates::prelude::*; +/// +/// let predicate_fn = predicate::path::is_dir(); +/// assert_eq!(false, predicate_fn.eval(Path::new("Cargo.toml"))); +/// assert_eq!(true, predicate_fn.eval(Path::new("src"))); +/// assert_eq!(false, predicate_fn.eval(Path::new("non-existent-file.foo"))); +/// ``` +pub fn is_dir() -> FileTypePredicate { + FileTypePredicate { + ft: FileType::Dir, + follow: false, + } +} + +/// Creates a new `Predicate` that ensures the path points to a symlink. +/// +/// # Examples +/// +/// ``` +/// use std::path::Path; +/// use predicates::prelude::*; +/// +/// let predicate_fn = predicate::path::is_symlink(); +/// assert_eq!(false, predicate_fn.eval(Path::new("Cargo.toml"))); +/// assert_eq!(false, predicate_fn.eval(Path::new("src"))); +/// assert_eq!(false, predicate_fn.eval(Path::new("non-existent-file.foo"))); +/// ``` +pub fn is_symlink() -> FileTypePredicate { + FileTypePredicate { + ft: FileType::Symlink, + follow: false, + } +} diff --git a/tools/vendor/predicates/src/path/mod.rs b/tools/vendor/predicates/src/path/mod.rs new file mode 100644 index 0000000000..8c0ed9a03b --- /dev/null +++ b/tools/vendor/predicates/src/path/mod.rs @@ -0,0 +1,20 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Path Predicates +//! +//! This module contains predicates specific to the file system. + +mod existence; +pub use self::existence::{exists, missing, ExistencePredicate}; +mod ft; +pub use self::ft::{is_dir, is_file, is_symlink, FileTypePredicate}; +mod fc; +pub use self::fc::{FileContentPredicate, PredicateFileContentExt}; +mod fs; +pub use self::fs::{eq_file, BinaryFilePredicate, StrFilePredicate}; diff --git a/tools/vendor/predicates/src/prelude.rs b/tools/vendor/predicates/src/prelude.rs new file mode 100644 index 0000000000..8cfd4474bd --- /dev/null +++ b/tools/vendor/predicates/src/prelude.rs @@ -0,0 +1,56 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Module that contains the essentials for working with predicates. + +pub use crate::boolean::PredicateBooleanExt; +pub use crate::boxed::PredicateBoxExt; +pub use crate::name::PredicateNameExt; +pub use crate::path::PredicateFileContentExt; +pub use crate::str::PredicateStrExt; +pub use crate::Predicate; + +/// Predicate factories +pub mod predicate { + // primitive `Predicate` types + pub use crate::constant::{always, never}; + pub use crate::function::function; + pub use crate::iter::{in_hash, in_iter}; + pub use crate::ord::{eq, ge, gt, le, lt, ne}; + + /// `str` Predicate factories + /// + /// This module contains predicates specific to string handling. + pub mod str { + pub use crate::str::is_empty; + pub use crate::str::{contains, ends_with, starts_with}; + + #[cfg(feature = "diff")] + pub use crate::str::diff; + + #[cfg(feature = "regex")] + pub use crate::str::is_match; + } + + /// `Path` Predicate factories + /// + /// This module contains predicates specific to path handling. + pub mod path { + pub use crate::path::eq_file; + pub use crate::path::{exists, missing}; + pub use crate::path::{is_dir, is_file, is_symlink}; + } + + /// `f64` Predicate factories + /// + /// This module contains predicates specific to float handling. + pub mod float { + #[cfg(feature = "float-cmp")] + pub use crate::float::is_close; + } +} diff --git a/tools/vendor/predicates/src/str/adapters.rs b/tools/vendor/predicates/src/str/adapters.rs new file mode 100644 index 0000000000..96856bc30e --- /dev/null +++ b/tools/vendor/predicates/src/str/adapters.rs @@ -0,0 +1,204 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::ffi; +use std::fmt; +use std::str; + +use crate::reflection; +#[cfg(feature = "normalize-line-endings")] +use crate::str::normalize::NormalizedPredicate; +use crate::Predicate; + +/// Predicate adapter that trims the variable being tested. +/// +/// This is created by `pred.trim()`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct TrimPredicate

+where + P: Predicate, +{ + p: P, +} + +impl

Predicate for TrimPredicate

+where + P: Predicate, +{ + fn eval(&self, variable: &str) -> bool { + self.p.eval(variable.trim()) + } + + fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option> { + self.p.find_case(expected, variable.trim()) + } +} + +impl

reflection::PredicateReflection for TrimPredicate

+where + P: Predicate, +{ + fn children<'a>(&'a self) -> Box> + 'a> { + let params = vec![reflection::Child::new("predicate", &self.p)]; + Box::new(params.into_iter()) + } +} + +impl

fmt::Display for TrimPredicate

+where + P: Predicate, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.p.fmt(f) + } +} + +/// Predicate adapter that converts a `str` predicate to byte predicate. +/// +/// This is created by `pred.from_utf8()`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Utf8Predicate

+where + P: Predicate, +{ + p: P, +} + +impl

Predicate for Utf8Predicate

+where + P: Predicate, +{ + fn eval(&self, variable: &ffi::OsStr) -> bool { + variable.to_str().map(|s| self.p.eval(s)).unwrap_or(false) + } + + fn find_case<'a>( + &'a self, + expected: bool, + variable: &ffi::OsStr, + ) -> Option> { + let var_str = variable.to_str(); + match (expected, var_str) { + (_, Some(var_str)) => self.p.find_case(expected, var_str).map(|child| { + child.add_product(reflection::Product::new("var as str", var_str.to_owned())) + }), + (true, None) => None, + (false, None) => Some( + reflection::Case::new(Some(self), false) + .add_product(reflection::Product::new("error", "Invalid UTF-8 string")), + ), + } + } +} + +impl

Predicate<[u8]> for Utf8Predicate

+where + P: Predicate, +{ + fn eval(&self, variable: &[u8]) -> bool { + str::from_utf8(variable) + .map(|s| self.p.eval(s)) + .unwrap_or(false) + } + + fn find_case<'a>(&'a self, expected: bool, variable: &[u8]) -> Option> { + let var_str = str::from_utf8(variable); + match (expected, var_str) { + (_, Ok(var_str)) => self.p.find_case(expected, var_str).map(|child| { + child.add_product(reflection::Product::new("var as str", var_str.to_owned())) + }), + (true, Err(_)) => None, + (false, Err(err)) => Some( + reflection::Case::new(Some(self), false) + .add_product(reflection::Product::new("error", err)), + ), + } + } +} + +impl

reflection::PredicateReflection for Utf8Predicate

+where + P: Predicate, +{ + fn children<'a>(&'a self) -> Box> + 'a> { + let params = vec![reflection::Child::new("predicate", &self.p)]; + Box::new(params.into_iter()) + } +} + +impl

fmt::Display for Utf8Predicate

+where + P: Predicate, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.p.fmt(f) + } +} + +/// `Predicate` extension adapting a `str` Predicate. +pub trait PredicateStrExt +where + Self: Predicate, + Self: Sized, +{ + /// Returns a `TrimPredicate` that ensures the data passed to `Self` is trimmed. + /// + /// # Examples + /// + /// ``` + /// use predicates::prelude::*; + /// + /// let predicate_fn = predicate::str::is_empty().trim(); + /// assert_eq!(true, predicate_fn.eval(" ")); + /// assert_eq!(false, predicate_fn.eval(" Hello ")); + /// ``` + fn trim(self) -> TrimPredicate { + TrimPredicate { p: self } + } + + /// Returns a `Utf8Predicate` that adapts `Self` to a `[u8]` `Predicate`. + /// + /// # Examples + /// + /// ``` + /// use predicates::prelude::*; + /// use std::ffi::OsStr; + /// + /// let predicate_fn = predicate::str::is_empty().not().from_utf8(); + /// assert_eq!(true, predicate_fn.eval(OsStr::new("Hello"))); + /// assert_eq!(false, predicate_fn.eval(OsStr::new(""))); + /// let variable: &[u8] = b""; + /// assert_eq!(false, predicate_fn.eval(variable)); + /// ``` + #[allow(clippy::wrong_self_convention)] + fn from_utf8(self) -> Utf8Predicate { + Utf8Predicate { p: self } + } + + /// Returns a `NormalizedPredicate` that ensures + /// the newlines within the data passed to `Self` is normalised. + /// + /// # Examples + /// + /// ``` + /// use predicates::prelude::*; + /// + /// let predicate_fn = predicate::eq("Hello World!\n").normalize(); + /// assert_eq!(true, predicate_fn.eval("Hello World!\n")); + /// assert_eq!(true, predicate_fn.eval("Hello World!\r")); + /// assert_eq!(true, predicate_fn.eval("Hello World!\r\n")); + /// assert_eq!(false, predicate_fn.eval("Goodbye")); + /// ``` + /// + #[cfg(feature = "normalize-line-endings")] + fn normalize(self) -> NormalizedPredicate { + NormalizedPredicate { p: self } + } +} + +impl

PredicateStrExt for P where P: Predicate {} diff --git a/tools/vendor/predicates/src/str/basics.rs b/tools/vendor/predicates/src/str/basics.rs new file mode 100644 index 0000000000..4128a8ec2d --- /dev/null +++ b/tools/vendor/predicates/src/str/basics.rs @@ -0,0 +1,290 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::fmt; + +use crate::reflection; +use crate::utils; +use crate::Predicate; + +/// Predicate that checks for empty strings. +/// +/// This is created by `predicates::str::is_empty`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct IsEmptyPredicate {} + +impl Predicate for IsEmptyPredicate { + fn eval(&self, variable: &str) -> bool { + variable.is_empty() + } + + fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option> { + utils::default_find_case(self, expected, variable) + .map(|case| case.add_product(reflection::Product::new("var", variable.to_owned()))) + } +} + +impl reflection::PredicateReflection for IsEmptyPredicate {} + +impl fmt::Display for IsEmptyPredicate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!( + f, + "{}.{}()", + palette.var("var"), + palette.description("is_empty"), + ) + } +} + +/// Creates a new `Predicate` that ensures a str is empty +/// +/// # Examples +/// +/// ``` +/// use predicates::prelude::*; +/// +/// let predicate_fn = predicate::str::is_empty(); +/// assert_eq!(true, predicate_fn.eval("")); +/// assert_eq!(false, predicate_fn.eval("Food World")); +/// ``` +pub fn is_empty() -> IsEmptyPredicate { + IsEmptyPredicate {} +} + +/// Predicate checks start of str +/// +/// This is created by `predicates::str::starts_with`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StartsWithPredicate { + pattern: String, +} + +impl Predicate for StartsWithPredicate { + fn eval(&self, variable: &str) -> bool { + variable.starts_with(&self.pattern) + } + + fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option> { + utils::default_find_case(self, expected, variable) + .map(|case| case.add_product(reflection::Product::new("var", variable.to_owned()))) + } +} + +impl reflection::PredicateReflection for StartsWithPredicate {} + +impl fmt::Display for StartsWithPredicate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!( + f, + "{}.{}({:?})", + palette.var("var"), + palette.description("starts_with"), + self.pattern + ) + } +} + +/// Creates a new `Predicate` that ensures a str starts with `pattern` +/// +/// # Examples +/// +/// ``` +/// use predicates::prelude::*; +/// +/// let predicate_fn = predicate::str::starts_with("Hello"); +/// assert_eq!(true, predicate_fn.eval("Hello World")); +/// assert_eq!(false, predicate_fn.eval("Goodbye World")); +/// ``` +pub fn starts_with

(pattern: P) -> StartsWithPredicate +where + P: Into, +{ + StartsWithPredicate { + pattern: pattern.into(), + } +} + +/// Predicate checks end of str +/// +/// This is created by `predicates::str::ends_with`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EndsWithPredicate { + pattern: String, +} + +impl Predicate for EndsWithPredicate { + fn eval(&self, variable: &str) -> bool { + variable.ends_with(&self.pattern) + } + + fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option> { + utils::default_find_case(self, expected, variable) + .map(|case| case.add_product(reflection::Product::new("var", variable.to_owned()))) + } +} + +impl reflection::PredicateReflection for EndsWithPredicate {} + +impl fmt::Display for EndsWithPredicate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!( + f, + "{}.{}({:?})", + palette.var("var"), + palette.description("ends_with"), + self.pattern + ) + } +} + +/// Creates a new `Predicate` that ensures a str ends with `pattern` +/// +/// # Examples +/// +/// ``` +/// use predicates::prelude::*; +/// +/// let predicate_fn = predicate::str::ends_with("World"); +/// assert_eq!(true, predicate_fn.eval("Hello World")); +/// assert_eq!(false, predicate_fn.eval("Hello Moon")); +/// ``` +pub fn ends_with

(pattern: P) -> EndsWithPredicate +where + P: Into, +{ + EndsWithPredicate { + pattern: pattern.into(), + } +} + +/// Predicate that checks for patterns. +/// +/// This is created by `predicates::str:contains`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ContainsPredicate { + pattern: String, +} + +impl ContainsPredicate { + /// Require a specific count of matches. + /// + /// # Examples + /// + /// ``` + /// use predicates::prelude::*; + /// + /// let predicate_fn = predicate::str::contains("Two").count(2); + /// assert_eq!(true, predicate_fn.eval("One Two Three Two One")); + /// assert_eq!(false, predicate_fn.eval("One Two Three")); + /// ``` + pub fn count(self, count: usize) -> MatchesPredicate { + MatchesPredicate { + pattern: self.pattern, + count, + } + } +} + +impl Predicate for ContainsPredicate { + fn eval(&self, variable: &str) -> bool { + variable.contains(&self.pattern) + } + + fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option> { + utils::default_find_case(self, expected, variable) + .map(|case| case.add_product(reflection::Product::new("var", variable.to_owned()))) + } +} + +impl reflection::PredicateReflection for ContainsPredicate {} + +impl fmt::Display for ContainsPredicate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!( + f, + "{}.{}({})", + palette.var("var"), + palette.description("contains"), + palette.expected(&self.pattern), + ) + } +} + +/// Predicate that checks for repeated patterns. +/// +/// This is created by `predicates::str::contains(...).count`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MatchesPredicate { + pattern: String, + count: usize, +} + +impl Predicate for MatchesPredicate { + fn eval(&self, variable: &str) -> bool { + variable.matches(&self.pattern).count() == self.count + } + + fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option> { + let actual_count = variable.matches(&self.pattern).count(); + let result = self.count == actual_count; + if result == expected { + Some( + reflection::Case::new(Some(self), result) + .add_product(reflection::Product::new("var", variable.to_owned())) + .add_product(reflection::Product::new("actual count", actual_count)), + ) + } else { + None + } + } +} + +impl reflection::PredicateReflection for MatchesPredicate { + fn parameters<'a>(&'a self) -> Box> + 'a> { + let params = vec![reflection::Parameter::new("count", &self.count)]; + Box::new(params.into_iter()) + } +} + +impl fmt::Display for MatchesPredicate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!( + f, + "{}.{}({})", + palette.var("var"), + palette.description("contains"), + palette.expected(&self.pattern), + ) + } +} + +/// Creates a new `Predicate` that ensures a str contains `pattern` +/// +/// # Examples +/// +/// ``` +/// use predicates::prelude::*; +/// +/// let predicate_fn = predicate::str::contains("Two"); +/// assert_eq!(true, predicate_fn.eval("One Two Three")); +/// assert_eq!(false, predicate_fn.eval("Four Five Six")); +/// ``` +pub fn contains

(pattern: P) -> ContainsPredicate +where + P: Into, +{ + ContainsPredicate { + pattern: pattern.into(), + } +} diff --git a/tools/vendor/predicates/src/str/difference.rs b/tools/vendor/predicates/src/str/difference.rs new file mode 100644 index 0000000000..0a362dc7ac --- /dev/null +++ b/tools/vendor/predicates/src/str/difference.rs @@ -0,0 +1,130 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::borrow; +use std::fmt; + +use crate::reflection; +use crate::Predicate; + +/// Predicate that diffs two strings. +/// +/// This is created by the `predicate::str::diff`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DifferencePredicate { + orig: borrow::Cow<'static, str>, +} + +impl Predicate for DifferencePredicate { + fn eval(&self, edit: &str) -> bool { + edit == self.orig + } + + fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option> { + let result = variable != self.orig; + if result == expected { + None + } else { + let palette = crate::Palette::new(true); + let orig: Vec<_> = self.orig.lines().map(|l| format!("{l}\n")).collect(); + let variable: Vec<_> = variable.lines().map(|l| format!("{l}\n")).collect(); + let diff = difflib::unified_diff( + &orig, + &variable, + "", + "", + &palette.expected("orig").to_string(), + &palette.var("var").to_string(), + 0, + ); + let mut diff = colorize_diff(diff, palette); + diff.insert(0, "\n".to_owned()); + + Some( + reflection::Case::new(Some(self), result) + .add_product(reflection::Product::new("diff", diff.join(""))), + ) + } + } +} + +impl reflection::PredicateReflection for DifferencePredicate { + fn parameters<'a>(&'a self) -> Box> + 'a> { + let params = vec![reflection::Parameter::new("original", &self.orig)]; + Box::new(params.into_iter()) + } +} + +impl fmt::Display for DifferencePredicate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!( + f, + "{:#} {:#} {:#}", + palette.description("diff"), + palette.expected("original"), + palette.var("var"), + ) + } +} + +/// Creates a new `Predicate` that diffs two strings. +/// +/// # Examples +/// +/// ``` +/// use predicates::prelude::*; +/// +/// let predicate_fn = predicate::str::diff("Hello World"); +/// assert_eq!(true, predicate_fn.eval("Hello World")); +/// assert!(predicate_fn.find_case(false, "Hello World").is_none()); +/// assert_eq!(false, predicate_fn.eval("Goodbye World")); +/// assert!(predicate_fn.find_case(false, "Goodbye World").is_some()); +/// ``` +pub fn diff(orig: S) -> DifferencePredicate +where + S: Into>, +{ + DifferencePredicate { orig: orig.into() } +} + +#[cfg(feature = "color")] +fn colorize_diff(mut lines: Vec, palette: crate::Palette) -> Vec { + for (i, line) in lines.iter_mut().enumerate() { + match (i, line.as_bytes().first()) { + (0, _) => { + if let Some((prefix, body)) = line.split_once(' ') { + *line = format!("{:#} {}", palette.expected(prefix), body); + } + } + (1, _) => { + if let Some((prefix, body)) = line.split_once(' ') { + *line = format!("{:#} {}", palette.var(prefix), body); + } + } + (_, Some(b'-')) => { + let (prefix, body) = line.split_at(1); + *line = format!("{:#}{}", palette.expected(prefix), body); + } + (_, Some(b'+')) => { + let (prefix, body) = line.split_at(1); + *line = format!("{:#}{}", palette.var(prefix), body); + } + (_, Some(b'@')) => { + *line = format!("{:#}", palette.description(&line)); + } + _ => (), + } + } + lines +} + +#[cfg(not(feature = "color"))] +fn colorize_diff(lines: Vec, _palette: crate::Palette) -> Vec { + lines +} diff --git a/tools/vendor/predicates/src/str/mod.rs b/tools/vendor/predicates/src/str/mod.rs new file mode 100644 index 0000000000..0d0dffd111 --- /dev/null +++ b/tools/vendor/predicates/src/str/mod.rs @@ -0,0 +1,30 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! String Predicates +//! +//! This module contains predicates specific to string handling. + +mod basics; +pub use self::basics::*; +mod adapters; +pub use self::adapters::*; + +#[cfg(feature = "diff")] +mod difference; +#[cfg(feature = "diff")] +pub use self::difference::{diff, DifferencePredicate}; +#[cfg(feature = "normalize-line-endings")] +mod normalize; +#[cfg(feature = "normalize-line-endings")] +pub use self::normalize::NormalizedPredicate; + +#[cfg(feature = "regex")] +mod regex; +#[cfg(feature = "regex")] +pub use self::regex::{is_match, RegexError, RegexPredicate}; diff --git a/tools/vendor/predicates/src/str/normalize.rs b/tools/vendor/predicates/src/str/normalize.rs new file mode 100644 index 0000000000..6a65733e39 --- /dev/null +++ b/tools/vendor/predicates/src/str/normalize.rs @@ -0,0 +1,58 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::reflection; +use crate::Predicate; +use std::fmt; + +use normalize_line_endings::normalized; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +/// Predicate adapter that normalizes the newlines contained in the variable being tested. +/// +/// This is created by `pred.normalize()`. +pub struct NormalizedPredicate

+where + P: Predicate, +{ + pub(crate) p: P, +} + +impl

reflection::PredicateReflection for NormalizedPredicate

+where + P: Predicate, +{ + fn children<'a>(&'a self) -> Box> + 'a> { + let params = vec![reflection::Child::new("predicate", &self.p)]; + Box::new(params.into_iter()) + } +} + +impl

Predicate for NormalizedPredicate

+where + P: Predicate, +{ + fn eval(&self, variable: &str) -> bool { + let variable = normalized(variable.chars()).collect::(); + self.p.eval(&variable) + } + + fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option> { + let variable = normalized(variable.chars()).collect::(); + self.p.find_case(expected, &variable) + } +} + +impl

fmt::Display for NormalizedPredicate

+where + P: Predicate, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.p.fmt(f) + } +} diff --git a/tools/vendor/predicates/src/str/regex.rs b/tools/vendor/predicates/src/str/regex.rs new file mode 100644 index 0000000000..abb46f8216 --- /dev/null +++ b/tools/vendor/predicates/src/str/regex.rs @@ -0,0 +1,134 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::fmt; + +use crate::reflection; +use crate::utils; +use crate::Predicate; + +/// An error that occurred during parsing or compiling a regular expression. +pub type RegexError = regex::Error; + +/// Predicate that uses regex matching +/// +/// This is created by the `predicate::str::is_match`. +#[derive(Debug, Clone)] +pub struct RegexPredicate { + re: regex::Regex, +} + +impl RegexPredicate { + /// Require a specific count of matches. + /// + /// # Examples + /// + /// ``` + /// use predicates::prelude::*; + /// + /// let predicate_fn = predicate::str::is_match("T[a-z]*").unwrap().count(3); + /// assert_eq!(true, predicate_fn.eval("One Two Three Two One")); + /// assert_eq!(false, predicate_fn.eval("One Two Three")); + /// ``` + pub fn count(self, count: usize) -> RegexMatchesPredicate { + RegexMatchesPredicate { re: self.re, count } + } +} + +impl Predicate for RegexPredicate { + fn eval(&self, variable: &str) -> bool { + self.re.is_match(variable) + } + + fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option> { + utils::default_find_case(self, expected, variable) + .map(|case| case.add_product(reflection::Product::new("var", variable.to_owned()))) + } +} + +impl reflection::PredicateReflection for RegexPredicate {} + +impl fmt::Display for RegexPredicate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!( + f, + "{}.{}({})", + palette.var("var"), + palette.description("is_match"), + palette.expected(&self.re), + ) + } +} + +/// Predicate that checks for repeated patterns. +/// +/// This is created by `predicates::str::is_match(...).count`. +#[derive(Debug, Clone)] +pub struct RegexMatchesPredicate { + re: regex::Regex, + count: usize, +} + +impl Predicate for RegexMatchesPredicate { + fn eval(&self, variable: &str) -> bool { + self.re.find_iter(variable).count() == self.count + } + + fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option> { + let actual_count = self.re.find_iter(variable).count(); + let result = self.count == actual_count; + if result == expected { + Some( + reflection::Case::new(Some(self), result) + .add_product(reflection::Product::new("var", variable.to_owned())) + .add_product(reflection::Product::new("actual count", actual_count)), + ) + } else { + None + } + } +} + +impl reflection::PredicateReflection for RegexMatchesPredicate { + fn parameters<'a>(&'a self) -> Box> + 'a> { + let params = vec![reflection::Parameter::new("count", &self.count)]; + Box::new(params.into_iter()) + } +} + +impl fmt::Display for RegexMatchesPredicate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let palette = crate::Palette::new(f.alternate()); + write!( + f, + "{}.{}({})", + palette.var("var"), + palette.description("is_match"), + palette.expected(&self.re), + ) + } +} + +/// Creates a new `Predicate` that uses a regular expression to match the string. +/// +/// # Examples +/// +/// ``` +/// use predicates::prelude::*; +/// +/// let predicate_fn = predicate::str::is_match("^Hello.*$").unwrap(); +/// assert_eq!(true, predicate_fn.eval("Hello World")); +/// assert_eq!(false, predicate_fn.eval("Food World")); +/// ``` +pub fn is_match(pattern: S) -> Result +where + S: AsRef, +{ + regex::Regex::new(pattern.as_ref()).map(|re| RegexPredicate { re }) +} diff --git a/tools/vendor/predicates/src/utils.rs b/tools/vendor/predicates/src/utils.rs new file mode 100644 index 0000000000..121f757ad7 --- /dev/null +++ b/tools/vendor/predicates/src/utils.rs @@ -0,0 +1,64 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::fmt; + +use crate::reflection; +use crate::Predicate; + +#[derive(Clone, PartialEq, Eq)] +pub(crate) struct DebugAdapter +where + T: fmt::Debug, +{ + pub(crate) debug: T, +} + +impl DebugAdapter +where + T: fmt::Debug, +{ + pub(crate) fn new(debug: T) -> Self { + Self { debug } + } +} + +impl fmt::Display for DebugAdapter +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:#?}", self.debug) + } +} + +impl fmt::Debug for DebugAdapter +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.debug.fmt(f) + } +} + +pub(crate) fn default_find_case<'a, P, Item>( + pred: &'a P, + expected: bool, + variable: &Item, +) -> Option> +where + P: Predicate, + Item: ?Sized, +{ + let actual = pred.eval(variable); + if expected == actual { + Some(reflection::Case::new(Some(pred), actual)) + } else { + None + } +} diff --git a/tools/vendor/r-efi/.cargo-checksum.json b/tools/vendor/r-efi/.cargo-checksum.json new file mode 100644 index 0000000000..7ebfba2beb --- /dev/null +++ b/tools/vendor/r-efi/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"50e0c72ee0164f182d7b5db0213cdb1626bad950f2443b05d006c28c7f0641b5",".github/workflows/publish.yml":"431c68b9d566180134e8771ea9cb9339d72ec77c3010b4cedfd423e271dc7386",".github/workflows/rust-tests.yml":"ca7ba173da0a6510676770b103119ce57029d424afe9e1027541d529af6c7f3f","AUTHORS":"ff92bed461f50338dd703a9ba9aee496a425957873df1d91192776e4bdf5dda7","Cargo.lock":"cc335892128a13fb1431213f65cfd3281ff098cf47b97882ca097f0206d7e0b0","Cargo.toml":"dba000c0173b09b1e3b69afa42a715b5ab64355e6502ce6d9d1767c7845cf402","Cargo.toml.orig":"57b6d9b9a1b6524f6b4dcc1dd24970ac03e6959786a6cfedaec9ba52878b4d3e","Makefile":"b445cfadd750b564ba8ef072754a4eed5aa127f12faa6e5b15a35b748d28b6fb","NEWS.md":"a8924309f9b5d8cf035b12ee47d4afe99c3ca57ea60061239263ac0b0e2161ba","README.md":"420f65fec3ba2e89d49da51b222d2881d26959037a7b7d3794143db4bd4627b1","examples/freestanding.rs":"0be5eb60397228306aa9f8afc533e32926d210205a7359e14da903159482416d","examples/gop-query.rs":"3257c55d6ae4572db1bfcaf20bc05990ea92d46c5d292eb99c278242f1007e2c","examples/hello-world.rs":"1a398f93a54bfddaab6ba3e0145cf70496266c44b9748a11ff569a9a18063bc4","src/base.rs":"e0f0b69967dc436e302f37230f5aff0e8bf748f6a1159b968180e6d0ac0b9644","src/hii.rs":"d0b19b7cc7b0c2c4d70e2891554ed6d7f941388709476eab6fa7bad129c583ed","src/lib.rs":"289fdfa8384062616aa39b49c4094b93849ff0a2e5f6d7125b40124299a0837e","src/protocols.rs":"a8f1c0d9abf2af2d7962fce68160a3bf8083165bb70861808423afedadee6602","src/protocols/absolute_pointer.rs":"46ad9792ea021f47e4fedf8d3c07769872e056c39936c9647492a52455558b41","src/protocols/block_io.rs":"4f70d38453f057bb240988d7fac593ffa0f38a0057c6e8855f34ff37801e7f3d","src/protocols/bus_specific_driver_override.rs":"a695a18e6729020919f99880fb14c2a26f565471f15d6300cb67475d33ec6087","src/protocols/debug_support.rs":"a03fc44a2ad151e58eff3bd72f910b63bfadd5802ac2fc157fc94c2030bd9fa9","src/protocols/debugport.rs":"bc491b03444ec35593a9cde519d0ab5fc863a046073dd813f1eb58c075da45dc","src/protocols/decompress.rs":"a4c67d958f0b9a392840a53eef69369acc6f545f90369685b3cae3c32e5cbc51","src/protocols/device_path.rs":"1a8c897f8e392fd7fe60d829b7852d22dd847f5f64bf1382e58e7776d9a176a2","src/protocols/device_path_from_text.rs":"e972d7699fb031df9da2c4d5623ae289d4cc282116b634218039b97d1e7628fe","src/protocols/device_path_to_text.rs":"9929d0797ab4892051bd15ac831dc2566f7106ec9edec0fd289b3c780a2b0976","src/protocols/device_path_utilities.rs":"7d7e9ba66e9f8add3c77718bd605e6e434598f2834236e7805aba57b531eba30","src/protocols/disk_io.rs":"01a68686dae958b4fbd9fd5bbc556cf41b61e7da281a0b4ea56e62e61822d843","src/protocols/disk_io2.rs":"33aeb807ab9b78534787f44e0a3a8c52afe057e23b48ecb6703195cd0b135a8e","src/protocols/driver_binding.rs":"f48363c5afd21aac39cbd2644be0e861acc4b182bc14c68a4f84e94a5f001f8d","src/protocols/driver_diagnostics2.rs":"62b0ffe6760ae0e90e51652c667ec623cef3bbd44c17fb89881a24167b1b446c","src/protocols/driver_family_override.rs":"004aceae8540766d87eb2cef9c8c37ab5aa3755920af6d960cb9171a41acb4a1","src/protocols/file.rs":"25f09f54cf3ebd69366bde5160218247c6712ae19dac17a36ec4e2881b214f93","src/protocols/graphics_output.rs":"9e9b7ac05b92ac0a9680506edb3ed36ebbac2dc9c800268b2c54cbf539e9dd9e","src/protocols/hii_database.rs":"faa82d420085dee851f8a332db00c888e7ed1685d43e2c286790a34b2ecfdf7a","src/protocols/hii_font.rs":"092f45c74335c524ee379508e2b6925ed9033c7c10928e64b2489ca3778a4723","src/protocols/hii_font_ex.rs":"2400b2092c14da2aa09c62266356321d4b96838dc41c5918a2f3e0e1e3920318","src/protocols/hii_package_list.rs":"6bb29d076697bb3b672913b98f55995f9efcba830b4ab6bfed29bd8402aa70ab","src/protocols/hii_string.rs":"1327ba9e108900ddafec98c02534c81104c3c47cad32a7d17c7b7a7ee723c86f","src/protocols/ip4.rs":"252d244ec9b098fcee6a185ffa98cbdee34164c7872fe30b07d5e8dfdf7a8fe3","src/protocols/ip6.rs":"72a51653aa4535f7b136a7bacaa1718e0a68be847d4a716c6e76b7f75a94e6ea","src/protocols/load_file.rs":"8aa4a43db3da6c71398eb9408b2be9b893eb2147e5317a0f977e998db2910cba","src/protocols/load_file2.rs":"5be9c5d501b4f10a44ba91448fce5d4a20c01eab9a8f7fb3c63eaa62cbb1c2f4","src/protocols/loaded_image.rs":"508cf1514071e611f3c70776463a19f53685b3569c52a2350f307cbeac9cbbcc","src/protocols/loaded_image_device_path.rs":"66e8fe3477785dadf96401da10e203ba729e54d7eddedaffc4d17acc9a0daf7f","src/protocols/managed_network.rs":"592111dee58137a21f3b49211baf0d9c73a14ca9de95fa31d7e87435ded3808d","src/protocols/memory_attribute.rs":"c512ae777b95248127fc39b63eb99d746aaf8a5dc2cfda167d15ea5aa627653c","src/protocols/mp_services.rs":"509e7d483df52ab1ba1f9241bb7b286ffe205c4328557b3115cd290b4c9f8278","src/protocols/pci_io.rs":"43dac18dfa284d83836ff1ccfb8e40d48ef94533b72cdb99ce30a768f70be67c","src/protocols/platform_driver_override.rs":"f0ada483bccd90f31a6f0ce8831ea00885b66ca558e34e347ec775cbc171534c","src/protocols/rng.rs":"279ce976d190889064d0159b10586bab9a2e5670368b460be4497ead523d6dda","src/protocols/service_binding.rs":"256920333ea26fabd3b62ba8a8b25068349acab6da01a807dfba3307a45d79a0","src/protocols/shell.rs":"3259b0bf2574da6b2343da0b9ce46980d296a8e6d8c2fe36df3a07d0629ad653","src/protocols/shell_dynamic_command.rs":"9c2f977fe4e0757b633de49627a9645d81c821a39e752cb329f6285d376fe52d","src/protocols/shell_parameters.rs":"fb88c63822a14dc94e96f7912fa5409f68b669105fb81459b969ec066002e352","src/protocols/simple_file_system.rs":"e3d870bdaf3ae0f0cded7817a4eda6d5dea2e0b77855cd21c7ac05046c77b564","src/protocols/simple_network.rs":"8b8c49872488cc0e759efe74c04fc65f5d7637dca7d9a0a1cedb5dafb2597acb","src/protocols/simple_text_input.rs":"d9a7e8cbf508daedaa26dc8301e52be957edfcc12be9e962b8504584aee669a8","src/protocols/simple_text_input_ex.rs":"84ccd2ff2c354c0447a9c46406ad76de018495d5f85979cf35d78b7070a8f63c","src/protocols/simple_text_output.rs":"05857880b5c0a6fb6ce83eedcc7fa25107f368950ab18a3d0ef1ed669d3456df","src/protocols/tcp4.rs":"2131cf06fd856017ea45135fb59264a43fabc3db1b7ddc90cd19c169ccdacae9","src/protocols/tcp6.rs":"98c24c43632acc60f2dc558c13a03657f93edca735435c50b9f2cc5625f7d3bb","src/protocols/timestamp.rs":"118cc4a54da4f53a4bb2fbf66cde8c433208266a22b59b4e370393f5a40c20a9","src/protocols/udp4.rs":"c4d344a40c674524383be51621a485d15ccfffaa94a1050c7a4642199fa91bf6","src/protocols/udp6.rs":"ea4ee2154e045a4e5992688e6cafed2d5a5de41f2796f5c070e739818b361550","src/system.rs":"2428de82301085a5c99b1b54411530adf2c895aa311a58c94c1b8e9eb247b18f","src/vendor.rs":"6cc53fe0e98220fe627d82c9b27f1468095358bb26634a8c19274495888138af","src/vendor/intel/console_control.rs":"03a7fdf97ab1836353266786811ac47171d1c425dcead707f5ed0e2e834873fb"},"package":"69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"} \ No newline at end of file diff --git a/tools/vendor/r-efi/.cargo_vcs_info.json b/tools/vendor/r-efi/.cargo_vcs_info.json new file mode 100644 index 0000000000..b54a881859 --- /dev/null +++ b/tools/vendor/r-efi/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "97b55bed1c2c91dcbf787674849f05337ff80b33" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/tools/vendor/r-efi/.github/workflows/publish.yml b/tools/vendor/r-efi/.github/workflows/publish.yml new file mode 100644 index 0000000000..294285cdf8 --- /dev/null +++ b/tools/vendor/r-efi/.github/workflows/publish.yml @@ -0,0 +1,39 @@ +# +# Publish Releases +# +# This workflow can be manually triggered and will then publish the +# specified release to the configured release channels. +# + +name: "Publish Releases" + +on: + workflow_dispatch: + inputs: + tag: + description: "Git-Tag to Publish" + default: "" + required: true + verify: + description: "Verify package before publishing" + default: "yes" + required: false + +defaults: + run: + shell: "bash" + +jobs: + publish: + name: "Publish Crate" + uses: readaheadeu/rae-actions/.github/workflows/lib-publish-rust.yml@v1 + + permissions: + contents: write + + secrets: + ciotoken: ${{ secrets.DEPLOY_CRATESIO_TOKEN }} + + with: + tag: ${{ github.event.inputs.tag }} + verify: ${{ github.event.inputs.verify == 'yes' || false }} diff --git a/tools/vendor/r-efi/.github/workflows/rust-tests.yml b/tools/vendor/r-efi/.github/workflows/rust-tests.yml new file mode 100644 index 0000000000..8044b87e74 --- /dev/null +++ b/tools/vendor/r-efi/.github/workflows/rust-tests.yml @@ -0,0 +1,125 @@ +# +# Rust Test Suite +# +# This workflow builds the project via Cargo, configures a suitable test +# environment, and then runs the test-suite defined in Cargo. +# + +name: "Rust Test Suite" + +on: + pull_request: + push: + branches-ignore: ["pr/**"] + tags: ["**"] + workflow_dispatch: + +defaults: + run: + shell: "bash" + +jobs: + # + # CI with Default Configuration + # + # This simply runs `cargo build && cargo test` on all sources. We want to + # explicitly ensure that this project stays compatible to the stable channel + # and the standard build setup. + # + ci: + name: "Default - rust-${{ matrix.rust }}@${{ matrix.os }}" + + strategy: + fail-fast: false + matrix: + os: ["ubuntu-latest"] + rust: ["nightly", "stable"] + + runs-on: ${{ matrix.os }} + + steps: + - name: "Fetch Sources" + uses: actions/checkout@v3 + - name: "Install Rust Components" + run: rustup default "${{ matrix.rust }}" + - name: "Build Project" + run: cargo build --verbose --all-targets + - name: "Run Tests" + run: cargo test --verbose + + # + # Cross-Compilation to UEFI Target + # + # This cross-compiles all sources (including the examples) for native UEFI + # targets. This test ensures that we can actually compile for our main target + # platforms. + # + ci-cross: + name: "Cross-Compilation - ${{ matrix.target }}/rust-${{ matrix.rust }}@${{ matrix.os }}" + + strategy: + fail-fast: false + matrix: + os: ["ubuntu-latest"] + rust: ["nightly", "stable"] + target: + - "aarch64-unknown-uefi" + - "i686-unknown-uefi" + - "x86_64-unknown-uefi" + + runs-on: ${{ matrix.os }} + + steps: + - name: "Fetch Sources" + uses: actions/checkout@v3 + - name: "Install Rust Components" + run: | + rustup default "${{ matrix.rust }}" + rustup target add --toolchain "${{ matrix.rust }}" "${{ matrix.target }}" + - name: "Build Project" + run: | + cargo build \ + --examples \ + --features native \ + --lib \ + --target "${{ matrix.target }}" \ + --verbose + + # + # Bootstrap to UEFI Target + # + # This uses the `-Zbuild-std` feature to fully bootstrap a native UEFI target + # via cross-compilation. This currently requires a nightly compiler. + # + ci-bootstrap: + name: "Bootstrap - ${{ matrix.target }}/rust-${{ matrix.rust }}@${{ matrix.os }}" + + strategy: + fail-fast: false + matrix: + os: ["ubuntu-latest"] + rust: ["nightly"] + target: + - "aarch64-unknown-uefi" + - "i686-unknown-uefi" + - "x86_64-unknown-uefi" + + runs-on: ${{ matrix.os }} + + steps: + - name: "Fetch Sources" + uses: actions/checkout@v3 + - name: "Install Rust Components" + run: | + rustup default "${{ matrix.rust }}" + rustup component add --toolchain "${{ matrix.rust }}" rust-src + - name: "Build Project" + run: | + cargo build \ + -Zbuild-std=core,compiler_builtins,alloc \ + -Zbuild-std-features=compiler-builtins-mem \ + --examples \ + --features native \ + --lib \ + --target "${{ matrix.target }}" \ + --verbose diff --git a/tools/vendor/r-efi/AUTHORS b/tools/vendor/r-efi/AUTHORS new file mode 100644 index 0000000000..7397b2bd6a --- /dev/null +++ b/tools/vendor/r-efi/AUTHORS @@ -0,0 +1,74 @@ +LICENSE: + This project is triple-licensed under the MIT License, the Apache + License, Version 2.0, and the GNU Lesser General Public License, + Version 2.1+. + +AUTHORS-MIT: + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +AUTHORS-ASL: + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +AUTHORS-LGPL: + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; If not, see . + +COPYRIGHT: (ordered alphabetically) + Copyright (C) 2017-2023 Red Hat, Inc. + Copyright (C) 2019-2023 Microsoft Corporation + Copyright (C) 2022-2023 David Rheinsberg + +AUTHORS: (ordered alphabetically) + Alex James + Ayush Singh + Boris-Chengbiao Zhou + Bret Barkelew + Christopher Zurcher + David Rheinsberg + Dmitry Mostovenko + Hiroki Tokunaga + Joe Richey + John Schock + Michael Kubacki + Oliver Smith-Denny + Richard Wiedenhöft + Rob Bradford , + Tom Gundersen + Trevor Gross diff --git a/tools/vendor/r-efi/Cargo.lock b/tools/vendor/r-efi/Cargo.lock new file mode 100644 index 0000000000..3194f70ec4 --- /dev/null +++ b/tools/vendor/r-efi/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "r-efi" +version = "5.3.0" +dependencies = [ + "rustc-std-workspace-core", +] + +[[package]] +name = "rustc-std-workspace-core" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9c45b374136f52f2d6311062c7146bff20fec063c3f5d46a410bd937746955" diff --git a/tools/vendor/r-efi/Cargo.toml b/tools/vendor/r-efi/Cargo.toml new file mode 100644 index 0000000000..632d34af66 --- /dev/null +++ b/tools/vendor/r-efi/Cargo.toml @@ -0,0 +1,70 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +rust-version = "1.68" +name = "r-efi" +version = "5.3.0" +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "UEFI Reference Specification Protocol Constants and Definitions" +homepage = "https://github.com/r-efi/r-efi/wiki" +readme = "README.md" +keywords = [ + "boot", + "efi", + "firmware", + "specification", + "uefi", +] +categories = [ + "embedded", + "hardware-support", + "no-std", + "os", +] +license = "MIT OR Apache-2.0 OR LGPL-2.1-or-later" +repository = "https://github.com/r-efi/r-efi" + +[features] +efiapi = [] +examples = ["native"] +native = [] +rustc-dep-of-std = ["core"] + +[lib] +name = "r_efi" +path = "src/lib.rs" + +[[example]] +name = "freestanding" +path = "examples/freestanding.rs" +required-features = ["native"] + +[[example]] +name = "gop-query" +path = "examples/gop-query.rs" +required-features = ["native"] + +[[example]] +name = "hello-world" +path = "examples/hello-world.rs" +required-features = ["native"] + +[dependencies.core] +version = "1.0.0" +optional = true +package = "rustc-std-workspace-core" diff --git a/tools/vendor/r-efi/Cargo.toml.orig b/tools/vendor/r-efi/Cargo.toml.orig new file mode 100644 index 0000000000..c09e2aa302 --- /dev/null +++ b/tools/vendor/r-efi/Cargo.toml.orig @@ -0,0 +1,51 @@ +[package] +name = "r-efi" +version = "5.3.0" + +categories = [ + "embedded", + "hardware-support", + "no-std", + "os", +] +description = "UEFI Reference Specification Protocol Constants and Definitions" +edition = "2018" +homepage = "https://github.com/r-efi/r-efi/wiki" +keywords = [ + "boot", + "efi", + "firmware", + "specification", + "uefi", +] +license = "MIT OR Apache-2.0 OR LGPL-2.1-or-later" +readme = "README.md" +repository = "https://github.com/r-efi/r-efi" +rust-version = "1.68" + +[dependencies] +# Required setup to build as part of rustc. +core = { version = '1.0.0', optional = true, package = 'rustc-std-workspace-core' } + +[features] +# No-op for backwards compatibility. +efiapi = [] +# Maps to `native` for backwards compatibility. +examples = ['native'] +# We feature-gate all native code, since it will not link correctly, unless you +# use a UEFI target configuration. To make `cargo test` work, we exclude all +# these from normal runs. +native = [] +rustc-dep-of-std = ['core'] + +[[example]] +name = "freestanding" +required-features = ["native"] + +[[example]] +name = "gop-query" +required-features = ["native"] + +[[example]] +name = "hello-world" +required-features = ["native"] diff --git a/tools/vendor/r-efi/Makefile b/tools/vendor/r-efi/Makefile new file mode 100644 index 0000000000..d1d4b16d27 --- /dev/null +++ b/tools/vendor/r-efi/Makefile @@ -0,0 +1,85 @@ +# +# Maintenance Makefile +# + +# Enforce bash with fatal errors. +SHELL := /bin/bash -eo pipefail + +# Keep intermediates around on failures for better caching. +.SECONDARY: + +# Default build and source directories. +BUILDDIR ?= ./build +SRCDIR ?= . + +# +# Target: help +# + +.PHONY: help +help: + @# 80-width marker: + @# 01234567012345670123456701234567012345670123456701234567012345670123456701234567 + @echo "make [TARGETS...]" + @echo + @echo "The following targets are provided by this maintenance makefile:" + @echo + @echo " help: Print this usage information" + @echo + @echo " publish-github: Publish a release to GitHub" + +# +# Target: BUILDDIR +# + +$(BUILDDIR)/: + mkdir -p "$@" + +$(BUILDDIR)/%/: + mkdir -p "$@" + +# +# Target: FORCE +# +# Used as alternative to `.PHONY` if the target is not fixed. +# + +.PHONY: FORCE +FORCE: + +# +# Target: publish-* +# + +PUBLISH_REPO ?= r-efi/r-efi +PUBLISH_VERSION ?= + +define PUBLISH_RELNOTES_PY +with open('NEWS.md', 'r') as f: + notes = f.read().split("\n## CHANGES WITH ")[1:] + notes = dict(map(lambda v: (v[:v.find(":")], v), notes)) + notes = notes["$(PUBLISH_VERSION)"].strip() +print(" # r-efi - UEFI Reference Specification Protocol Constants and Definitions\n") +print(" ## CHANGES WITH", notes) +endef + +export PUBLISH_RELNOTES_PY +export PUBLISH_REPO +export PUBLISH_VERSION + +.PHONY: publish-github +publish-github: + test ! -z "$${PUBLISH_REPO}" + test ! -z "$${PUBLISH_VERSION}" + python \ + - \ + <<<"$${PUBLISH_RELNOTES_PY}" \ + | gh \ + release \ + --repo "$${PUBLISH_REPO}" \ + create \ + --verify-tag \ + --title \ + "r-efi-$${PUBLISH_VERSION}" \ + --notes-file - \ + "v$${PUBLISH_VERSION}" diff --git a/tools/vendor/r-efi/NEWS.md b/tools/vendor/r-efi/NEWS.md new file mode 100644 index 0000000000..d3d15e7a2a --- /dev/null +++ b/tools/vendor/r-efi/NEWS.md @@ -0,0 +1,301 @@ +# r-efi - UEFI Reference Specification Protocol Constants and Definitions + +## CHANGES WITH 5.3.0: + + * Remove the optional dependency on `compiler-builtins`, which was + needed to build r-efi as part of rustc. This is no longer necessary. + + Contributions from: David Rheinsberg, Trevor Gross + + - Dußlingen, 2025-06-17 + +## CHANGES WITH 5.2.0: + + * Add the memory attribute protocol. + + Contributions from: David Rheinsberg, Oliver Smith-Denny + + - Dußlingen, 2024-12-22 + +## CHANGES WITH 5.1.0: + + * Mark `Guid::as_bytes` and `Guid::from_bytes` as `const fn`, aligning + them with the other methods on `Guid`. This helps creating constant + GUIDs via macros or other external helpers. + + Contributions from: Christopher Zurcher, David Rheinsberg + + - Dußlingen, 2024-09-01 + +## CHANGES WITH 5.0.0: + + * Change the type of the `unload` function-pointer of the Loaded Image + Protocol to `Option`, given that it can be `NULL` to indicate + that the image cannot be unloaded. + This is a major API break, but any users very likely need to adjust + anyway to avoid NULL-derefs. + + Contributions from: David Rheinsberg, John Schock + + - Dußlingen, 2024-07-30 + +## CHANGES WITH 4.5.0: + + * Implement or derive more standard traits for UEFI base types. In + particular, implement `[Partial]Eq`, `Hash`, `[Partial]Ord` for + `Boolean`, `Status`, `Guid`, and network address types. + + * Fix the signature of `BootUninstallMultipleProtocolInterfaces` to + match the UEFI specification. Note that it uses var-args and is thus + not fully usable from stable Rust. + + Contributions from: Ayush Singh, David Rheinsberg, John Schock + + - Dußlingen, 2024-05-23 + +## CHANGES WITH 4.4.0: + + * Add definitions for `UNACCEPTED_MEMORY_TYPE`, media device subtypes + for device paths, before-EBS and after-RTB event groups, missing + memory attributes. + + * Add memory masks for common memory attribute classes. The symbol + names are takend from EDK2, yet their purpose is defined in the + specification. + + * New protocols: platform_driver_override, bus_specific_driver_override, + driver_family_override, load_file, load_file2, pci-io + + Contributions from: David Rheinsberg, Dmitry Mostovenko, John Schock, + Michael Kubacki + + - Dußlingen, 2024-03-27 + +## CHANGES WITH 4.3.0: + + * Change alignment of `Guid` to 4 (was 8 before). This deviates from + the specification, but aligns with EDK2. This should fix alignment + mismatches when combining r-efi with EDK2, or other UEFI + implementations. + + * `Guid` gained a new constructor `from_bytes()` to allow creating + GUID-abstractions from foreign types based on the standardized + memory representation. + + * Add all configuration-table GUIDs mentioned in the spec. These are + often rooted in external specifications, but are strongly related + to UEFI. + + * Add configuration-table definitions for RT_PROPERTIES and + CONFORMANCE_PROFILES. + + * New protocols: hii_package_list, absolute_pointer + + Contributions from: David Rheinsberg, John Schock, Michael Kubacki, + Nicholas Bishop + + - Dußlingen, 2023-10-18 + +## CHANGES WITH 4.2.0: + + * Bump required compiler version to: rust-1.68 + + * New Protocols: debugport, debug-support, driver-diagnostics2, + mp-services, shell, shell-dynamic-command, + shell-parameters, udp-4, udp-6 + + * Use const-generics instead of ZSTs to represent dynamic trailing + members in C structs. + + * The `examples` feature has been renamed to `native` (a backwards + compatible feature is left in place). + + * Add support for riscv64. + + * Use the official rust `efiapi` calling convention. This was + stabilized with rust-1.68. + + Contributions from: Ayush Singh, David Rheinsberg, Rob Bradford + + - Dußlingen, 2023-03-20 + +## CHANGES WITH 4.1.0: + + * New Protocols: device-path-{from,to}-text, ip4, ip6, managed-network, + rng, service-binding, tcp4, tcp6, timestamp + + * `ImageEntryPoint` is now correctly annotated as `eficall`. + + * `Time` now derives `Default`. + + * Fix nullable function pointers to use `Option`. + + * Function prototypes now have an explicit type definition and can be + used independent of their protocol definition. + + * The new `rust-dep-of-std` feature option allows pulling in r-efi + into the rust standard library. It prepares the crate workspace to + be suitable for the standard library. It has no use outside of this. + + * Adopt the MIT license as 3rd licensing option to allow for + integration into the rust compiler and ecosystem. + + Contributions from: Ayush Singh, David Rheinsberg, Joe Richey + + - Tübingen, 2022-08-23 + +## CHANGES WITH 4.0.0: + + * Convert all enums to constants with type-aliases. This is an API + break, but it is needed for spec-compliance. With the old enums, one + couldn't encode all the possible values defined by the spec. + Especially, the vendor-reserved ranges were unable to be encoded in + a safe manner. Also see commit 401a91901e860 for a detailed + discussion. + API users likely need to convert their CamelCase enum usage to the + new UPPER_CASE constants. + + * Convert all incomplete types to empty arrays. This affects all + structures that use trailing unbound arrays. These are actually ABI + incompatible with UEFI, since rust represents raw-pointers to such + types as fat-pointers. Such arrays have now been converted to empty + arrays, which should still allow accessing the memory location and + retaining structure properties, but avoids fat-pointers. + This is an API break, so you might have to adjust your accessors of + those trailing structure members. + + * Implement `Clone` and `Copy` for most basic structures. Since these + are used as plain carriers, no higher clone/copy logic is needed. It + should be clear from the project-description, that only basic UEFI + compatibility is provided. + + * Add the console-control vendor protocol. This protocol allows + controlling console properties. It is not part of the UEFI + specification, but rather defined by the TianoCore project. + + * Add a new example showing how to use the GOP functions to query the + active graphics device. + + Contributions from: David Rheinsberg, GGRei, Hiroki Tokunaga, + Richard Wiedenhöft + + - Tübingen, 2021-06-23 + +## CHANGES WITH 3.2.0: + + * Add new protocols: DiskIo, DiskIo2, BlockIo, DriverBinding + + * Extend the Device-Path payload structure and add the HardDriveMedia + payload. + + * Add HII definitions: A new top-level module `hii` with all the basic + HII constants, as well as a handful of HII protocols (hii_database, + hii_font, hii_string) + + * Document new `-Zbuild-std` based cross-compilation, serving as + official rust alternative to cargo-xbuild. + + Contributions from: Alex James, Bret Barkelew, David Rheinsberg, + Michael Kubacki + + - Tübingen, 2020-10-23 + +## CHANGES WITH 3.1.0: + + * Add the basic networking types to `r_efi::base`. This includes MAC + and IP address types. + + * Add the EFI_SIMPLE_NETWORK_PROTOCOL definitions and all required + constants to make basic networking available. + + * Add a new uefi-cross example, which is copied from upstream rustc + sources, so we can test local modifications to it. + + Contributions from: Alex James, David Rheinsberg + + - Tübingen, 2020-09-10 + +## CHANGES WITH 3.0.0: + + * Fix a missing parameter in `BootServices::locate_device_path()`. The + prototype incorrectly had 2 arguments, while the official version + takes 3. The final `handle` argument was missing. + This is an API break in `r-efi`. It should have a limited impact, + since the function was mostly useless without a handle. + Thanks to Michael Kubacki for catching this! + + * Adjust the `device_path` parameter in a bunch of `BootServices` + calls. This used to take a `*mut c_void` parameter, since the device + path protocol was not implemented. + Since we have to bump the major version anyway, we use this to also + fix these argument-types to the correct device-path protocol type, + which has been implemented some time ago. + + Contributions from: David Rheinsberg, Michael Kubacki + + - Tübingen, 2020-04-24 + +## CHANGES WITH 2.2.0: + + * Provide `as_usize()` accessor for `efi::Status` types. This allows + accessing the raw underlying value of a status object. + + * The project moved to its new home at: github.com/r-efi/r-efi + + Contributions from: David Rheinsberg, Joe Richey + + - Tübingen, 2020-04-16 + +## CHANGES WITH 2.1.0: + + * Add the graphics-output-protocol. + + * Expose reserved fields in open structures, otherwise they cannot be + instantiated from outside the crate itself. + + Contributions from: David Herrmann, Richard Wiedenhöft, Rob Bradford + + - Tübingen, 2019-03-20 + +## CHANGES WITH 2.0.0: + + * Add a set of UEFI protocols, including simple-text-input, + file-protocol, simple-file-system, device-path, and more. + + * Fix signature of `BootServices::allocate_pages`. + + Contributions from: David Rheinsberg, Richard Wiedenhöft, Tom Gundersen + + - Tübingen, 2019-03-01 + +## CHANGES WITH 1.0.0: + + * Enhance the basic UEFI type integration with the rust ecosystem. Add + `Debug`, `Eq`, `Ord`, ... derivations, provide converters to/from the + core library, and document the internal workings. + + * Fix `Boolean` to use `newtype(u8)` to make it ABI compatible to UEFI. + This now accepts any byte value that UEFI accetps without any + conversion required. + + Contributions from: Boris-Chengbiao Zhou, David Rheinsberg, Tom + Gundersen + + - Tübingen, 2019-02-14 + +## CHANGES WITH 0.1.1: + + * Feature gate examples to make `cargo test` work on non-UEFI systems + like CI. + + Contributions from: David Herrmann + + - Tübingen, 2018-12-10 + +## CHANGES WITH 0.1.0: + + * Initial release of r-efi. + + Contributions from: David Herrmann + + - Tübingen, 2018-12-10 diff --git a/tools/vendor/r-efi/README.md b/tools/vendor/r-efi/README.md new file mode 100644 index 0000000000..b5b8f87576 --- /dev/null +++ b/tools/vendor/r-efi/README.md @@ -0,0 +1,99 @@ +r-efi +===== + +UEFI Reference Specification Protocol Constants and Definitions + +The r-efi project provides the protocol constants and definitions of the +UEFI Reference Specification as native rust code. The scope of this project is +limited to those protocol definitions. The protocols are not actually +implemented. As such, this project serves as base for any UEFI application that +needs to interact with UEFI, or implement (parts of) the UEFI specification. + +### Project + + * **Website**: + * **Bug Tracker**: + +### Requirements + +The requirements for this project are: + + * `rustc >= 1.68.0` + +### Build + +To build this project, run: + +```sh +cargo build +``` + +Available configuration options are: + + * **native**: This feature-selector enables compilation of modules and + examples that require native UEFI targets. Those will not + compile on foreign targets and thus are guarded by this flag. + +##### Build via: official toolchains + +Starting with rust-version 1.68, rustup distributes pre-compiled toolchains for +many UEFI targets. You can enumerate and install them via `rustup`. This +example shows how to enumerate all available targets for your stable toolchain +and then install the UEFI target for the `x86_64` architecture: + +```sh +rustup target list --toolchain=stable +rustup target add --toolchain=stable x86_64-unknown-uefi +``` + +This project can then be compiled directly for the selected target: + +```sh +cargo +stable build \ + --examples \ + --features native \ + --lib \ + --target x86_64-unknown-uefi +``` + +##### Build via: cargo/rustc nightly with -Zbuild-std + +If no pre-compiled toolchains are available for your selected target, you can +compile the project and the required parts of the standard library via the +experimental `-Zbuild-std` feature of rustc. This requires a nightly compiler: + +```sh +cargo +nightly build \ + -Zbuild-std=core,compiler_builtins,alloc \ + -Zbuild-std-features=compiler-builtins-mem \ + --examples \ + --features native \ + --lib \ + --target x86_64-unknown-uefi +``` + +##### Build via: foreign target + +The project can be built for non-UEFI targets via the standard rust toolchains. +This allows non-UEFI targets to interact with UEFI systems or otherwise host +UEFI operations. Furthermore, this allows running the foreign test-suite of +this project as long as the target supports the full standard library: + +```sh +cargo +stable build --all-targets +cargo +stable test --all-targets +``` + +Note that the `native` feature must not be enabled for foreign targets as it +will not compile on non-UEFI systems. + +### Repository: + + - **web**: + - **https**: `https://github.com/r-efi/r-efi.git` + - **ssh**: `git@github.com:r-efi/r-efi.git` + +### License: + + - **MIT** OR **Apache-2.0** OR **LGPL-2.1-or-later** + - See AUTHORS file for details. diff --git a/tools/vendor/r-efi/examples/freestanding.rs b/tools/vendor/r-efi/examples/freestanding.rs new file mode 100644 index 0000000000..1c4e3aaf42 --- /dev/null +++ b/tools/vendor/r-efi/examples/freestanding.rs @@ -0,0 +1,34 @@ +// Example: Freestanding +// +// This example is a plain UEFI application without any external requirements +// but `core`. It immediately returns control to the caller upon execution, +// yielding the exit code 0. +// +// The `main` function serves as entry-point. Depending on your +// target-configuration, it must be exported with a pre-configured name so the +// linker will correctly mark it as entry-point. The target configurations +// shipped with upstream rust-lang use `efi_main` as symbol name. +// +// Additionally, a panic handler is provided. This is executed by rust on +// panic. For simplicity, we simply end up in an infinite loop. For real +// applications, this method should probably call into +// `SystemTable->boot_services->exit()` to exit the UEFI application. Note, +// however, that UEFI applications are likely to run in the same address space +// as the entire firmware. Hence, halting the machine might be a viable +// alternative. All that is out-of-scope for this example, though. +// +// Note that as of rust-1.31.0, all features used here are stabilized. No +// unstable features are required, nor do we rely on nightly compilers. + +#![no_main] +#![no_std] + +#[panic_handler] +fn panic_handler(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[export_name = "efi_main"] +pub extern "C" fn main(_h: *mut core::ffi::c_void, _st: *mut core::ffi::c_void) -> usize { + 0 +} diff --git a/tools/vendor/r-efi/examples/gop-query.rs b/tools/vendor/r-efi/examples/gop-query.rs new file mode 100644 index 0000000000..b4ab4c370b --- /dev/null +++ b/tools/vendor/r-efi/examples/gop-query.rs @@ -0,0 +1,188 @@ +// Example: Graphics Query +// +// This is a slightly more complex UEFI application than `hello-world`. It +// locates the graphics-output-protocol, queries its current mode and prints +// the current resolution to the UEFI console. +// +// This example should make everyone aware that UEFI programing in Rust really +// asks for helper layers. While the C/FFI/Spec API can be used directly, it +// is quite cumbersome. Especially the error handling is overly difficult. +// +// Nevertheless, this example shows how to find UEFI protocol and invoke +// their member functions. +// +// Like all the other r-efi examples, it is a standalone example. That is, no +// UTF-16 helpers are pulled in, nor any allocators or panic frameworks. For +// real world scenarios, you really should choose such helpers. + +#![no_main] +#![no_std] + +use r_efi::efi; + +#[panic_handler] +fn panic_handler(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +fn fail(_r: efi::Status) -> ! { + panic!(); +} + +// A simple `itoa()`-ish function that takes a u32 and turns it into a UTF-16 +// string. It always prints exactly 10 characters, so leading zeroes are used +// for small numbers. +fn utoa(mut u: u32, a: &mut [u16]) { + for i in 0..10 { + a[9 - i] = 0x0030u16 + ((u % 10) as u16); + u = u / 10; + } +} + +// A simple helper that takes two integers and prints them to the UEFI console +// with a short prefix. It uses a UTF-16 buffer and fills in the numbers before +// printing the entire buffer. +fn print_xy(st: *mut efi::SystemTable, x: u32, y: u32) { + let mut s = [ + 0x0058u16, 0x0059u16, 0x003au16, // "XY:" + 0x0020u16, // " " + 0x0020u16, 0x0020u16, 0x0020u16, 0x0020u16, // " " + 0x0020u16, 0x0020u16, 0x0020u16, 0x0020u16, // " " + 0x0020u16, 0x0020u16, // " " + 0x0078u16, // "x" + 0x0020u16, 0x0020u16, 0x0020u16, 0x0020u16, // " " + 0x0020u16, 0x0020u16, 0x0020u16, 0x0020u16, // " " + 0x0020u16, 0x0020u16, // " " + 0x000au16, // "\n" + 0x0000u16, // NUL + ]; + + utoa(x, &mut s[4..14]); + utoa(y, &mut s[15..25]); + + unsafe { + let r = ((*(*st).con_out).output_string)((*st).con_out, s.as_ptr() as *mut efi::Char16); + if r.is_error() { + fail(r); + } + } +} + +// This function locates singleton UEFI protocols. Those protocols do not +// require to register listener handles, but are globally available to all +// UEFI applications. It takes a GUID of the protocol to locate and returns +// the protocol pointer on success. +fn locate_singleton( + st: *mut efi::SystemTable, + guid: *const efi::Guid, +) -> Result<*mut core::ffi::c_void, efi::Status> { + let mut interface: *mut core::ffi::c_void = core::ptr::null_mut(); + let mut handles: *mut efi::Handle = core::ptr::null_mut(); + let mut n_handles: usize = 0; + let mut r: efi::Status; + + // Use `locate_handle_buffer()` to find all handles that support the + // specified protocol. + unsafe { + if (*st).hdr.revision < efi::SYSTEM_TABLE_REVISION_1_10 { + // We use `LocateHandleBuffer`, which was introduced in 1.10. + return Err(efi::Status::UNSUPPORTED); + } + + let r = ((*(*st).boot_services).locate_handle_buffer)( + efi::BY_PROTOCOL, + guid as *mut _, + core::ptr::null_mut(), + &mut n_handles, + &mut handles, + ); + match r { + efi::Status::SUCCESS => {} + efi::Status::NOT_FOUND => return Err(r), + efi::Status::OUT_OF_RESOURCES => return Err(r), + _ => panic!(), + }; + } + + // Now that we have all handles with the specified protocol, query it for + // the protocol interface. We loop here, even though every item should + // succeed. Lets be on the safe side. + // Secondly, we use `handle_protocol()` here, but really should be using + // `open_protocol()`. But for singleton protocols, this does not matter, + // so lets use the simple path for now. + unsafe { + r = efi::Status::NOT_FOUND; + for i in 0..n_handles { + r = ((*(*st).boot_services).handle_protocol)( + *handles.offset(core::convert::TryFrom::::try_from(i).unwrap()), + guid as *mut _, + &mut interface, + ); + match r { + efi::Status::SUCCESS => break, + efi::Status::UNSUPPORTED => continue, + _ => panic!(), + }; + } + } + + // Free the allocated buffer memory of `handles`. This was allocated on the + // pool by `locate_handle_buffer()`. + unsafe { + let r = ((*(*st).boot_services).free_pool)(handles as *mut core::ffi::c_void); + assert!(!r.is_error()); + } + + // In case we found nothing, return `NOT_FOUND`, otherwise return the + // interface identifier. + match r { + efi::Status::SUCCESS => Ok(interface), + _ => Err(efi::Status::NOT_FOUND), + } +} + +// A simple helper that queries the current mode of the GraphicsOutputProtocol +// and returns the x and y dimensions on success. +fn query_gop( + gop: *mut efi::protocols::graphics_output::Protocol, +) -> Result<(u32, u32), efi::Status> { + let mut info: *mut efi::protocols::graphics_output::ModeInformation = core::ptr::null_mut(); + let mut z_info: usize = 0; + + unsafe { + // We could just look at `gop->mode->info`, but lets query the mode + // instead to show how to query other modes than the active one. + let r = ((*gop).query_mode)(gop, (*(*gop).mode).mode, &mut z_info, &mut info); + match r { + efi::Status::SUCCESS => {} + efi::Status::DEVICE_ERROR => return Err(r), + _ => panic!(), + }; + if z_info < core::mem::size_of_val(&*info) { + return Err(efi::Status::UNSUPPORTED); + } + + Ok(((*info).horizontal_resolution, (*info).vertical_resolution)) + } +} + +// This is the UEFI application entrypoint. We use it to locate the GOP +// pointer, query the current mode, and then print it to the system console. +#[export_name = "efi_main"] +pub extern "C" fn main(_h: efi::Handle, st: *mut efi::SystemTable) -> efi::Status { + let r = locate_singleton(st, &efi::protocols::graphics_output::PROTOCOL_GUID); + let gop = match r { + Ok(v) => v, + Err(r) => fail(r), + }; + + let r = query_gop(gop as _); + let v = match r { + Ok(v) => v, + Err(r) => fail(r), + }; + + print_xy(st, v.0, v.1); + + efi::Status::SUCCESS +} diff --git a/tools/vendor/r-efi/examples/hello-world.rs b/tools/vendor/r-efi/examples/hello-world.rs new file mode 100644 index 0000000000..25b5243ac8 --- /dev/null +++ b/tools/vendor/r-efi/examples/hello-world.rs @@ -0,0 +1,55 @@ +// Example: Hello World! +// +// This is an example UEFI application that prints "Hello World!", then waits +// for key input before it exits. It serves as base example how to write UEFI +// applications without any helper modules other than the UEFI protocol +// definitions. +// +// This example builds upon the `freestanding.rs` example, using the same setup +// and rust integration. See there for details on the panic-handler and entry +// point configuration. +// +// Note that UEFI uses UTF-16 strings. Since rust literals are UTF-8, we have +// to use an open-coded, zero-terminated, UTF-16 array as argument to +// `output_string()`. Similarly to the panic handler, real applications should +// rather use UTF-16 modules. + +#![no_main] +#![no_std] + +use r_efi::efi; + +#[panic_handler] +fn panic_handler(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[export_name = "efi_main"] +pub extern "C" fn main(_h: efi::Handle, st: *mut efi::SystemTable) -> efi::Status { + let s = [ + 0x0048u16, 0x0065u16, 0x006cu16, 0x006cu16, 0x006fu16, // "Hello" + 0x0020u16, // " " + 0x0057u16, 0x006fu16, 0x0072u16, 0x006cu16, 0x0064u16, // "World" + 0x0021u16, // "!" + 0x000au16, // "\n" + 0x0000u16, // NUL + ]; + + // Print "Hello World!". + let r = + unsafe { ((*(*st).con_out).output_string)((*st).con_out, s.as_ptr() as *mut efi::Char16) }; + if r.is_error() { + return r; + } + + // Wait for key input, by waiting on the `wait_for_key` event hook. + let r = unsafe { + let mut x: usize = 0; + ((*(*st).boot_services).wait_for_event)(1, &mut (*(*st).con_in).wait_for_key, &mut x) + }; + if r.is_error() { + return r; + } + + efi::Status::SUCCESS +} diff --git a/tools/vendor/r-efi/src/base.rs b/tools/vendor/r-efi/src/base.rs new file mode 100644 index 0000000000..077a60e7f1 --- /dev/null +++ b/tools/vendor/r-efi/src/base.rs @@ -0,0 +1,993 @@ +//! UEFI Base Environment +//! +//! This module defines the base environment for UEFI development. It provides types and macros as +//! declared in the UEFI specification, as well as de-facto standard additions provided by the +//! reference implementation by Intel. +//! +//! # Target Configuration +//! +//! Wherever possible, native rust types are used to represent their UEFI counter-parts. However, +//! this means the ABI depends on the implementation of said rust types. Hence, native rust types +//! are only used where rust supports a stable ABI of said types, and their ABI matches the ABI +//! defined by the UEFI specification. +//! +//! Nevertheless, even if the ABI of a specific type is marked stable, this does not imply that it +//! is the same across architectures. For instance, rust's `u64` type has the same binary +//! representation as the `UINT64` type in UEFI. But this does not imply that it has the same +//! binary representation on `x86_64` and on `ppc64be`. As a result of this, the compilation of +//! this module is tied to the target-configuration you passed to the rust compiler. Wherever +//! possible and reasonable, any architecture differences are abstracted, though. This means that +//! in most cases you can use this module even though your target-configuration might not match +//! the native UEFI target-configuration. +//! +//! The recommend way to compile your code, is to use the native target-configuration for UEFI. +//! These configurations are not necessarily included in the upstream rust compiler. Hence, you +//! might have to craft one yourself. For all systems that we can test on, we make sure to push +//! the target configuration into upstream rust-lang. +//! +//! However, there are situations where you want to access UEFI data from a non-native host. For +//! instance, a UEFI boot loader might store data in boot variables, formatted according to types +//! declared in the UEFI specification. An OS booted thereafter might want to access these +//! variables, but it might be compiled with a different target-configuration than the UEFI +//! environment that it was booted from. A similar situation occurs when you call UEFI runtime +//! functions from your OS. In all those cases, you should very likely be able to use this module +//! to interact with UEFI as well. This is, because most bits of the target-configuration of UEFI +//! and your OS very likely match. In fact, to figure out whether this is safe, you need to make +//! sure that the rust ABI would match in both target-configurations. If it is, all other details +//! are handled within this module just fine. +//! +//! In case of doubt, contact us! +//! +//! # Core Primitives +//! +//! Several of the UEFI primitives are represented by native Rust. These have no type aliases or +//! other definitions here, but you are recommended to use native rust directly. These include: +//! +//! * `NULL`, `void *`: Void pointers have a native rust implementation in +//! [`c_void`](core::ffi::c_void). `NULL` is represented through +//! [`null`](core::ptr::null) and [`is_null()`](core::ptr) for +//! all pointer types. +//! * `uint8_t`..`uint64_t`, +//! `int8_t`..`int64_t`: Fixed-size integers are represented by their native rust equivalents +//! (`u8`..`u64`, `i8`..`i64`). +//! +//! * `UINTN`, `INTN`: Native-sized (or instruction-width sized) integers are represented by +//! their native rust equivalents (`usize`, `isize`). +//! +//! # UEFI Details +//! +//! The UEFI Specification describes its target environments in detail. Each supported +//! architecture has a separate section with details on calling conventions, CPU setup, and more. +//! You are highly recommended to conduct the UEFI Specification for details on the programming +//! environment. Following a summary of key parts relevant to rust developers: +//! +//! * Similar to rust, integers are either fixed-size, or native size. This maps nicely to the +//! native rust types. The common `long`, `int`, `short` types known from ISO-C are not used. +//! Whenever you refer to memory (either pointing to it, or remember the size of a memory +//! block), the native size integers should be your tool of choice. +//! +//! * Even though the CPU might run in any endianness, all stored data is little-endian. That +//! means, if you encounter integers split into byte-arrays (e.g., +//! `CEfiDevicePathProtocol.length`), you must assume it is little-endian encoded. But if you +//! encounter native integers, you must assume they are encoded in native endianness. +//! For now the UEFI specification only defines little-endian architectures, hence this did not +//! pop up as actual issue. Future extensions might change this, though. +//! +//! * The Microsoft calling-convention is used. That is, all external calls to UEFI functions +//! follow a calling convention that is very similar to that used on Microsoft Windows. All +//! such ABI functions must be marked with the right calling-convention. The UEFI Specification +//! defines some additional common rules for all its APIs, though. You will most likely not see +//! any of these mentioned in the individual API documentions. So here is a short reminder: +//! +//! * Pointers must reference physical-memory locations (no I/O mappings, no +//! virtual addresses, etc.). Once ExitBootServices() was called, and the +//! virtual address mapping was set, you must provide virtual-memory +//! locations instead. +//! * Pointers must be correctly aligned. +//! * NULL is disallowed, unless explicitly mentioned otherwise. +//! * Data referenced by pointers is undefined on error-return from a +//! function. +//! * You must not pass data larger than native-size (sizeof(CEfiUSize)) on +//! the stack. You must pass them by reference. +//! +//! * Stack size is at least 128KiB and 16-byte aligned. All stack space might be marked +//! non-executable! Once ExitBootServices() was called, you must guarantee at least 4KiB of +//! stack space, 16-byte aligned for all runtime services you call. +//! Details might differ depending on architectures. But the numbers here should serve as +//! ball-park figures. + +// Target Architecture +// +// The UEFI Specification explicitly lists all supported target architectures. While external +// implementors are free to port UEFI to other targets, we need information on the target +// architecture to successfully compile for it. This includes calling-conventions, register +// layouts, endianness, and more. Most of these details are hidden in the rust-target-declaration. +// However, some details are still left to the actual rust code. +// +// This initial check just makes sure the compilation is halted with a suitable error message if +// the target architecture is not supported. +// +// We try to minimize conditional compilations as much as possible. A simple search for +// `target_arch` should reveal all uses throughout the code-base. If you add your target to this +// error-check, you must adjust all other uses as well. +// +// Similarly, UEFI only defines configurations for little-endian architectures so far. Several +// bits of the specification are thus unclear how they would be applied on big-endian systems. We +// therefore mark it as unsupported. If you override this, you are on your own. +#[cfg(not(any( + target_arch = "arm", + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "x86", + target_arch = "x86_64" +)))] +compile_error!("The target architecture is not supported."); +#[cfg(not(target_endian = "little"))] +compile_error!("The target endianness is not supported."); + +// eficall_abi!() +// +// This macro is the architecture-dependent implementation of eficall!(). See the documentation of +// the eficall!() macro for a description. Nowadays, this simply maps to `extern "efiapi"`, since +// this has been stabilized with rust-1.68. + +#[macro_export] +#[doc(hidden)] +macro_rules! eficall_abi { + (($($prefix:tt)*),($($suffix:tt)*)) => { $($prefix)* extern "efiapi" $($suffix)* }; +} + +/// Annotate function with UEFI calling convention +/// +/// Since rust-1.68 you can use `extern "efiapi"` as calling-convention to achieve the same +/// behavior as this macro. This macro is kept for backwards-compatibility only, but will nowadays +/// map to `extern "efiapi"`. +/// +/// This macro takes a function-declaration as argument and produces the same function-declaration +/// but annotated with the correct calling convention. Since the default `extern "C"` annotation +/// depends on your compiler defaults, we cannot use it. Instead, this macro selects the default +/// for your target platform. +/// +/// Ideally, the macro would expand to `extern ""` so you would be able to write: +/// +/// ```ignore +/// // THIS DOES NOT WORK! +/// pub fn eficall!{} foobar() { +/// // ... +/// } +/// ``` +/// +/// However, macros are evaluated too late for this to work. Instead, the entire construct must be +/// wrapped in a macro, which then expands to the same construct but with `extern ""` +/// inserted at the correct place: +/// +/// ``` +/// use r_efi::{eficall, eficall_abi}; +/// +/// eficall!{pub fn foobar() { +/// // ... +/// }} +/// +/// type FooBar = eficall!{fn(u8) -> (u8)}; +/// ``` +/// +/// The `eficall!{}` macro takes either a function-type or function-definition as argument. It +/// inserts `extern ""` after the function qualifiers, but before the `fn` keyword. +/// +/// # Internals +/// +/// The `eficall!{}` macro tries to parse the function header so it can insert `extern ""` at +/// the right place. If, for whatever reason, this does not work with a particular syntax, you can +/// use the internal `eficall_abi!{}` macro. This macro takes two token-streams as input and +/// evaluates to the concatenation of both token-streams, but separated by the selected ABI. +/// +/// For instance, the following 3 type definitions are equivalent, assuming the selected ABI +/// is "C": +/// +/// ``` +/// use r_efi::{eficall, eficall_abi}; +/// +/// type FooBar1 = unsafe extern "C" fn(u8) -> (u8); +/// type FooBar2 = eficall!{unsafe fn(u8) -> (u8)}; +/// type FooBar3 = eficall_abi!{(unsafe), (fn(u8) -> (u8))}; +/// ``` +/// +/// # Calling Conventions +/// +/// The UEFI specification defines the calling convention for each platform individually. It +/// usually refers to other standards for details, but adds some restrictions on top. As of this +/// writing, it mentions: +/// +/// * aarch32 / arm: The `aapcs` calling-convention is used. It is native to aarch32 and described +/// in a document called +/// "Procedure Call Standard for the ARM Architecture". It is openly distributed +/// by ARM and widely known under the keyword `aapcs`. +/// * aarch64: The `aapcs64` calling-convention is used. It is native to aarch64 and described in +/// a document called +/// "Procedure Call Standard for the ARM 64-bit Architecture (AArch64)". It is openly +/// distributed by ARM and widely known under the keyword `aapcs64`. +/// * ia-64: The "P64 C Calling Convention" as described in the +/// "Itanium Software Conventions and Runtime Architecture Guide". It is also +/// standardized in the "Intel Itanium SAL Specification". +/// * RISC-V: The "Standard RISC-V C Calling Convention" is used. The UEFI specification +/// describes it in detail, but also refers to the official RISC-V resources for +/// detailed information. +/// * x86 / ia-32: The `cdecl` C calling convention is used. Originated in the C Language and +/// originally tightly coupled to C specifics. Unclear whether a formal +/// specification exists (does anyone know?). Most compilers support it under the +/// `cdecl` keyword, and in nearly all situations it is the default on x86. +/// * x86_64 / amd64 / x64: The `win64` calling-convention is used. It is similar to the `sysv64` +/// convention that is used on most non-windows x86_64 systems, but not +/// exactly the same. Microsoft provides open documentation on it. See +/// MSDN "x64 Software Conventions -> Calling Conventions". +/// The UEFI Specification does not directly refer to `win64`, but +/// contains a full specification of the calling convention itself. +/// +/// Note that in most cases the UEFI Specification adds several more restrictions on top of the +/// common calling-conventions. These restrictions usually do not affect how the compiler will lay +/// out the function calls. Instead, it usually only restricts the set of APIs that are allowed in +/// UEFI. Therefore, most compilers already support the calling conventions used on UEFI. +/// +/// # Variadics +/// +/// For some reason, the rust compiler allows variadics only in combination with the `"C"` calling +/// convention, even if the selected calling-convention matches what `"C"` would select on the +/// target platform. Hence, you will very likely be unable to use variadics with this macro. +/// Luckily, all of the UEFI functions that use variadics are wrappers around more low-level +/// accessors, so they are not necessarily required. +#[macro_export] +macro_rules! eficall { + // Muncher + // + // The `@munch()` rules are internal and should not be invoked directly. We walk through the + // input, moving one token after the other from the suffix into the prefix until we find the + // position where to insert `extern ""`. This muncher never drops any tokens, hence we + // can safely match invalid statements just fine, as the compiler will later print proper + // diagnostics when parsing the macro output. + // Once done, we invoke the `eficall_abi!{}` macro, which simply inserts the correct ABI. + (@munch(($($prefix:tt)*),(pub $($suffix:tt)*))) => { eficall!{@munch(($($prefix)* pub),($($suffix)*))} }; + (@munch(($($prefix:tt)*),(unsafe $($suffix:tt)*))) => { eficall!{@munch(($($prefix)* unsafe),($($suffix)*))} }; + (@munch(($($prefix:tt)*),($($suffix:tt)*))) => { eficall_abi!{($($prefix)*),($($suffix)*)} }; + + // Entry Point + // + // This captures the entire argument and invokes its own TT-muncher, but splits the input into + // prefix and suffix, so the TT-muncher can walk through it. Note that initially everything is + // in the suffix and the prefix is empty. + ($($arg:tt)*) => { eficall!{@munch((),($($arg)*))} }; +} + +/// Boolean Type +/// +/// This boolean type works very similar to the rust primitive type of [`bool`]. However, the rust +/// primitive type has no stable ABI, hence we provide this type to represent booleans on the FFI +/// interface. +/// +/// UEFI defines booleans to be 1-byte integers, which can only have the values of `0` or `1`. +/// However, in practice anything non-zero is considered `true` by nearly all UEFI systems. Hence, +/// this type implements a boolean over `u8` and maps `0` to `false`, everything else to `true`. +/// +/// The binary representation of this type is ABI. That is, you are allowed to transmute from and +/// to `u8`. Furthermore, this type never modifies its binary representation. If it was +/// initialized as, or transmuted from, a specific integer value, this value will be retained. +/// However, on the rust side you will never see the integer value. It instead behaves truly as a +/// boolean. If you need access to the integer value, you have to transmute it back to `u8`. +#[repr(C)] +#[derive(Clone, Copy, Debug)] +// Manual impls for: Default, Eq, Hash, Ord, PartialEq, PartialOrd +pub struct Boolean(u8); + +/// Single-byte Character Type +/// +/// The `Char8` type represents single-byte characters. UEFI defines them to be ASCII compatible, +/// using the ISO-Latin-1 character set. +pub type Char8 = u8; + +/// Dual-byte Character Type +/// +/// The `Char16` type represents dual-byte characters. UEFI defines them to be UCS-2 encoded. +pub type Char16 = u16; + +/// Status Codes +/// +/// UEFI uses the `Status` type to represent all kinds of status codes. This includes return codes +/// from functions, but also complex state of different devices and drivers. It is a simple +/// `usize`, but wrapped in a rust-type to allow us to implement helpers on this type. Depending +/// on the context, different state is stored in it. Note that it is always binary compatible to a +/// usize! +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +#[derive(Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Status(usize); + +/// Object Handles +/// +/// Handles represent access to an opaque object. Handles are untyped by default, but get a +/// meaning when you combine them with an interface. Internally, they are simple void pointers. It +/// is the UEFI driver model that applies meaning to them. +pub type Handle = *mut core::ffi::c_void; + +/// Event Objects +/// +/// Event objects represent hooks into the main-loop of a UEFI environment. They allow to register +/// callbacks, to be invoked when a specific event happens. In most cases you use events to +/// register timer-based callbacks, as well as chaining events together. Internally, they are +/// simple void pointers. It is the UEFI task management that applies meaning to them. +pub type Event = *mut core::ffi::c_void; + +/// Logical Block Addresses +/// +/// The LBA type is used to denote logical block addresses of block devices. It is a simple 64-bit +/// integer, that is used to denote addresses when working with block devices. +pub type Lba = u64; + +/// Thread Priority Levels +/// +/// The process model of UEFI systems is highly simplified. Priority levels are used to order +/// execution of pending tasks. The TPL type denotes a priority level of a specific task. The +/// higher the number, the higher the priority. It is a simple integer type, but its range is +/// usually highly restricted. The UEFI task management provides constants and accessors for TPLs. +pub type Tpl = usize; + +/// Physical Memory Address +/// +/// A simple 64bit integer containing a physical memory address. +pub type PhysicalAddress = u64; + +/// Virtual Memory Address +/// +/// A simple 64bit integer containing a virtual memory address. +pub type VirtualAddress = u64; + +/// Application Entry Point +/// +/// This type defines the entry-point of UEFI applications. It is ABI and cannot be changed. +/// Whenever you load UEFI images, the entry-point is called with this signature. +/// +/// In most cases the UEFI image (or application) is unloaded when control returns from the entry +/// point. In case of UEFI drivers, they can request to stay loaded until an explicit unload. +/// +/// The system table is provided as mutable pointer. This is, because there is no guarantee that +/// timer interrupts do not modify the table. Furthermore, exiting boot services causes several +/// modifications on that table. And lastly, the system table lives longer than the function +/// invocation, if invoked as an UEFI driver. +/// In most cases it is perfectly fine to cast the pointer to a real rust reference. However, this +/// should be an explicit decision by the caller. +pub type ImageEntryPoint = eficall! {fn(Handle, *mut crate::system::SystemTable) -> Status}; + +/// Globally Unique Identifiers +/// +/// The `Guid` type represents globally unique identifiers as defined by RFC-4122 (i.e., only the +/// `10x` variant is used), with the caveat that LE is used instead of BE. +/// +/// Note that only the binary representation of Guids is stable. You are highly recommended to +/// interpret Guids as 128bit integers. +/// +/// The UEFI specification requires the type to be 64-bit aligned, yet EDK2 uses a mere 32-bit +/// alignment. Hence, for compatibility, a 32-bit alignment is used. +/// +/// UEFI uses the Microsoft-style Guid format. Hence, a lot of documentation and code refers to +/// these Guids. If you thusly cannot treat Guids as 128-bit integers, this Guid type allows you +/// to access the individual fields of the Microsoft-style Guid. A reminder of the Guid encoding: +/// +/// ```text +/// 0 1 2 3 +/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// | time_low | +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// | time_mid | time_hi_and_version | +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// |clk_seq_hi_res | clk_seq_low | node (0-1) | +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// | node (2-5) | +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// ``` +/// +/// The individual fields are encoded as little-endian. Accessors are provided for the Guid +/// structure allowing access to these fields in native endian byte order. +#[repr(C, align(4))] +#[derive(Clone, Copy, Debug)] +#[derive(Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Guid { + time_low: [u8; 4], + time_mid: [u8; 2], + time_hi_and_version: [u8; 2], + clk_seq_hi_res: u8, + clk_seq_low: u8, + node: [u8; 6], +} + +/// Network MAC Address +/// +/// This type encapsulates a single networking media access control address +/// (MAC). It is a simple 32 bytes buffer with no special alignment. Note that +/// no comparison function are defined by default, since trailing bytes of the +/// address might be random. +/// +/// The interpretation of the content differs depending on the protocol it is +/// used with. See each documentation for details. In most cases this contains +/// an Ethernet address. +#[repr(C)] +#[derive(Clone, Copy, Debug)] +#[derive(Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct MacAddress { + pub addr: [u8; 32], +} + +/// IPv4 Address +/// +/// Binary representation of an IPv4 address. It is encoded in network byte +/// order (i.e., big endian). Note that no special alignment restrictions are +/// defined by the standard specification. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +#[derive(Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Ipv4Address { + pub addr: [u8; 4], +} + +/// IPv6 Address +/// +/// Binary representation of an IPv6 address, encoded in network byte order +/// (i.e., big endian). Similar to the IPv4 address, no special alignment +/// restrictions are defined by the standard specification. +#[repr(C)] +#[derive(Clone, Copy, Debug)] +#[derive(Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Ipv6Address { + pub addr: [u8; 16], +} + +/// IP Address +/// +/// A union type over the different IP addresses available. Alignment is always +/// fixed to 4-bytes. Note that trailing bytes might be random, so no +/// comparison functions are derived. +#[repr(C, align(4))] +#[derive(Clone, Copy)] +pub union IpAddress { + pub addr: [u32; 4], + pub v4: Ipv4Address, + pub v6: Ipv6Address, +} + +impl Boolean { + /// Literal False + /// + /// This constant represents the `false` value of the `Boolean` type. + pub const FALSE: Boolean = Boolean(0u8); + + /// Literal True + /// + /// This constant represents the `true` value of the `Boolean` type. + pub const TRUE: Boolean = Boolean(1u8); +} + +impl From for Boolean { + fn from(v: u8) -> Self { + Boolean(v) + } +} + +impl From for Boolean { + fn from(v: bool) -> Self { + match v { + false => Boolean::FALSE, + true => Boolean::TRUE, + } + } +} + +impl Default for Boolean { + fn default() -> Self { + Self::FALSE + } +} + +impl From for u8 { + fn from(v: Boolean) -> Self { + match v.0 { + 0 => 0, + _ => 1, + } + } +} + +impl From for bool { + fn from(v: Boolean) -> Self { + match v.0 { + 0 => false, + _ => true, + } + } +} + +impl Eq for Boolean {} + +impl core::hash::Hash for Boolean { + fn hash(&self, state: &mut H) { + bool::from(*self).hash(state) + } +} + +impl Ord for Boolean { + fn cmp(&self, other: &Boolean) -> core::cmp::Ordering { + bool::from(*self).cmp(&(*other).into()) + } +} + +impl PartialEq for Boolean { + fn eq(&self, other: &Boolean) -> bool { + bool::from(*self).eq(&(*other).into()) + } +} + +impl PartialEq for Boolean { + fn eq(&self, other: &bool) -> bool { + bool::from(*self).eq(other) + } +} + +impl PartialOrd for Boolean { + fn partial_cmp(&self, other: &Boolean) -> Option { + bool::from(*self).partial_cmp(&(*other).into()) + } +} + +impl PartialOrd for Boolean { + fn partial_cmp(&self, other: &bool) -> Option { + bool::from(*self).partial_cmp(other) + } +} + +impl Status { + const WIDTH: usize = 8usize * core::mem::size_of::(); + const MASK: usize = 0xc0 << (Status::WIDTH - 8); + const ERROR_MASK: usize = 0x80 << (Status::WIDTH - 8); + const WARNING_MASK: usize = 0x00 << (Status::WIDTH - 8); + + /// Success Code + /// + /// This code represents a successfull function invocation. Its value is guaranteed to be 0. + /// However, note that warnings are considered success as well, so this is not the only code + /// that can be returned by UEFI functions on success. However, in nearly all situations + /// warnings are not allowed, so the effective result will be SUCCESS. + pub const SUCCESS: Status = Status::from_usize(0); + + // List of predefined error codes + pub const LOAD_ERROR: Status = Status::from_usize(1 | Status::ERROR_MASK); + pub const INVALID_PARAMETER: Status = Status::from_usize(2 | Status::ERROR_MASK); + pub const UNSUPPORTED: Status = Status::from_usize(3 | Status::ERROR_MASK); + pub const BAD_BUFFER_SIZE: Status = Status::from_usize(4 | Status::ERROR_MASK); + pub const BUFFER_TOO_SMALL: Status = Status::from_usize(5 | Status::ERROR_MASK); + pub const NOT_READY: Status = Status::from_usize(6 | Status::ERROR_MASK); + pub const DEVICE_ERROR: Status = Status::from_usize(7 | Status::ERROR_MASK); + pub const WRITE_PROTECTED: Status = Status::from_usize(8 | Status::ERROR_MASK); + pub const OUT_OF_RESOURCES: Status = Status::from_usize(9 | Status::ERROR_MASK); + pub const VOLUME_CORRUPTED: Status = Status::from_usize(10 | Status::ERROR_MASK); + pub const VOLUME_FULL: Status = Status::from_usize(11 | Status::ERROR_MASK); + pub const NO_MEDIA: Status = Status::from_usize(12 | Status::ERROR_MASK); + pub const MEDIA_CHANGED: Status = Status::from_usize(13 | Status::ERROR_MASK); + pub const NOT_FOUND: Status = Status::from_usize(14 | Status::ERROR_MASK); + pub const ACCESS_DENIED: Status = Status::from_usize(15 | Status::ERROR_MASK); + pub const NO_RESPONSE: Status = Status::from_usize(16 | Status::ERROR_MASK); + pub const NO_MAPPING: Status = Status::from_usize(17 | Status::ERROR_MASK); + pub const TIMEOUT: Status = Status::from_usize(18 | Status::ERROR_MASK); + pub const NOT_STARTED: Status = Status::from_usize(19 | Status::ERROR_MASK); + pub const ALREADY_STARTED: Status = Status::from_usize(20 | Status::ERROR_MASK); + pub const ABORTED: Status = Status::from_usize(21 | Status::ERROR_MASK); + pub const ICMP_ERROR: Status = Status::from_usize(22 | Status::ERROR_MASK); + pub const TFTP_ERROR: Status = Status::from_usize(23 | Status::ERROR_MASK); + pub const PROTOCOL_ERROR: Status = Status::from_usize(24 | Status::ERROR_MASK); + pub const INCOMPATIBLE_VERSION: Status = Status::from_usize(25 | Status::ERROR_MASK); + pub const SECURITY_VIOLATION: Status = Status::from_usize(26 | Status::ERROR_MASK); + pub const CRC_ERROR: Status = Status::from_usize(27 | Status::ERROR_MASK); + pub const END_OF_MEDIA: Status = Status::from_usize(28 | Status::ERROR_MASK); + pub const END_OF_FILE: Status = Status::from_usize(31 | Status::ERROR_MASK); + pub const INVALID_LANGUAGE: Status = Status::from_usize(32 | Status::ERROR_MASK); + pub const COMPROMISED_DATA: Status = Status::from_usize(33 | Status::ERROR_MASK); + pub const IP_ADDRESS_CONFLICT: Status = Status::from_usize(34 | Status::ERROR_MASK); + pub const HTTP_ERROR: Status = Status::from_usize(35 | Status::ERROR_MASK); + + // List of error codes from protocols + // UDP4 + pub const NETWORK_UNREACHABLE: Status = Status::from_usize(100 | Status::ERROR_MASK); + pub const HOST_UNREACHABLE: Status = Status::from_usize(101 | Status::ERROR_MASK); + pub const PROTOCOL_UNREACHABLE: Status = Status::from_usize(102 | Status::ERROR_MASK); + pub const PORT_UNREACHABLE: Status = Status::from_usize(103 | Status::ERROR_MASK); + // TCP4 + pub const CONNECTION_FIN: Status = Status::from_usize(104 | Status::ERROR_MASK); + pub const CONNECTION_RESET: Status = Status::from_usize(105 | Status::ERROR_MASK); + pub const CONNECTION_REFUSED: Status = Status::from_usize(106 | Status::ERROR_MASK); + + // List of predefined warning codes + pub const WARN_UNKNOWN_GLYPH: Status = Status::from_usize(1 | Status::WARNING_MASK); + pub const WARN_DELETE_FAILURE: Status = Status::from_usize(2 | Status::WARNING_MASK); + pub const WARN_WRITE_FAILURE: Status = Status::from_usize(3 | Status::WARNING_MASK); + pub const WARN_BUFFER_TOO_SMALL: Status = Status::from_usize(4 | Status::WARNING_MASK); + pub const WARN_STALE_DATA: Status = Status::from_usize(5 | Status::WARNING_MASK); + pub const WARN_FILE_SYSTEM: Status = Status::from_usize(6 | Status::WARNING_MASK); + pub const WARN_RESET_REQUIRED: Status = Status::from_usize(7 | Status::WARNING_MASK); + + /// Create Status Code from Integer + /// + /// This takes the literal value of a status code and turns it into a `Status` object. Note + /// that we want it as `const fn` so we cannot use `core::convert::From`. + pub const fn from_usize(v: usize) -> Status { + Status(v) + } + + /// Return Underlying Integer Representation + /// + /// This takes the `Status` object and returns the underlying integer representation as + /// defined by the UEFI specification. + pub const fn as_usize(&self) -> usize { + self.0 + } + + fn value(&self) -> usize { + self.0 + } + + fn mask(&self) -> usize { + self.value() & Status::MASK + } + + /// Check whether this is an error + /// + /// This returns true if the given status code is considered an error. Errors mean the + /// operation did not succeed, nor produce any valuable output. Output parameters must be + /// considered invalid if an error was returned. That is, its content is not well defined. + pub fn is_error(&self) -> bool { + self.mask() == Status::ERROR_MASK + } + + /// Check whether this is a warning + /// + /// This returns true if the given status code is considered a warning. Warnings are to be + /// treated as success, but might indicate data loss or other device errors. However, if an + /// operation returns with a warning code, it must be considered successfull, and the output + /// parameters are valid. + pub fn is_warning(&self) -> bool { + self.value() != 0 && self.mask() == Status::WARNING_MASK + } +} + +impl From for Result { + fn from(status: Status) -> Self { + if status.is_error() { + Err(status) + } else { + Ok(status) + } + } +} + +impl Guid { + const fn u32_to_bytes_le(num: u32) -> [u8; 4] { + [ + num as u8, + (num >> 8) as u8, + (num >> 16) as u8, + (num >> 24) as u8, + ] + } + + const fn u32_from_bytes_le(bytes: &[u8; 4]) -> u32 { + (bytes[0] as u32) + | ((bytes[1] as u32) << 8) + | ((bytes[2] as u32) << 16) + | ((bytes[3] as u32) << 24) + } + + const fn u16_to_bytes_le(num: u16) -> [u8; 2] { + [num as u8, (num >> 8) as u8] + } + + const fn u16_from_bytes_le(bytes: &[u8; 2]) -> u16 { + (bytes[0] as u16) | ((bytes[1] as u16) << 8) + } + + /// Initialize a Guid from its individual fields + /// + /// This function initializes a Guid object given the individual fields as specified in the + /// UEFI specification. That is, if you simply copy the literals from the specification into + /// your code, this function will correctly initialize the Guid object. + /// + /// In other words, this takes the individual fields in native endian and converts them to the + /// correct endianness for a UEFI Guid. + /// + /// Due to the fact that UEFI Guids use variant 2 of the UUID specification in a little-endian + /// (or even mixed-endian) format, the following transformation is likely applied from text + /// representation to binary representation: + /// + /// 00112233-4455-6677-8899-aabbccddeeff + /// => + /// 33 22 11 00 55 44 77 66 88 99 aa bb cc dd ee ff + /// + /// (Note that UEFI protocols often use `88-99` instead of `8899`) + /// The first 3 parts use little-endian notation, the last 2 use big-endian. + pub const fn from_fields( + time_low: u32, + time_mid: u16, + time_hi_and_version: u16, + clk_seq_hi_res: u8, + clk_seq_low: u8, + node: &[u8; 6], + ) -> Guid { + Guid { + time_low: Self::u32_to_bytes_le(time_low), + time_mid: Self::u16_to_bytes_le(time_mid), + time_hi_and_version: Self::u16_to_bytes_le(time_hi_and_version), + clk_seq_hi_res: clk_seq_hi_res, + clk_seq_low: clk_seq_low, + node: *node, + } + } + + /// Access a Guid as individual fields + /// + /// This decomposes a Guid back into the individual fields as given in the specification. The + /// individual fields are returned in native-endianness. + pub const fn as_fields(&self) -> (u32, u16, u16, u8, u8, &[u8; 6]) { + ( + Self::u32_from_bytes_le(&self.time_low), + Self::u16_from_bytes_le(&self.time_mid), + Self::u16_from_bytes_le(&self.time_hi_and_version), + self.clk_seq_hi_res, + self.clk_seq_low, + &self.node, + ) + } + + /// Initialize a Guid from its byte representation + /// + /// Create a new Guid object from its byte representation. This + /// reinterprets the bytes as a Guid and copies them into a new Guid + /// instance. Note that you can safely transmute instead. + /// + /// See `as_bytes()` for the inverse operation. + pub const fn from_bytes(bytes: &[u8; 16]) -> Self { + unsafe { core::mem::transmute::<[u8; 16], Guid>(*bytes) } + } + + /// Access a Guid as raw byte array + /// + /// This provides access to a Guid through a byte array. It is a simple re-interpretation of + /// the Guid value as a 128-bit byte array. No conversion is performed. This is a simple cast. + pub const fn as_bytes(&self) -> &[u8; 16] { + unsafe { core::mem::transmute::<&Guid, &[u8; 16]>(self) } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::mem::{align_of, size_of}; + + // Helper to compute a hash of an object. + fn hash(v: &T) -> u64 { + let mut h = std::hash::DefaultHasher::new(); + v.hash(&mut h); + core::hash::Hasher::finish(&h) + } + + // Verify Type Size and Alignemnt + // + // Since UEFI defines explicitly the ABI of their types, we can verify that our implementation + // is correct by checking the size and alignment of the ABI types matches what the spec + // mandates. + #[test] + fn type_size_and_alignment() { + // + // Booleans + // + + assert_eq!(size_of::(), 1); + assert_eq!(align_of::(), 1); + + // + // Char8 / Char16 + // + + assert_eq!(size_of::(), 1); + assert_eq!(align_of::(), 1); + assert_eq!(size_of::(), 2); + assert_eq!(align_of::(), 2); + + assert_eq!(size_of::(), size_of::()); + assert_eq!(align_of::(), align_of::()); + assert_eq!(size_of::(), size_of::()); + assert_eq!(align_of::(), align_of::()); + + // + // Status + // + + assert_eq!(size_of::(), size_of::()); + assert_eq!(align_of::(), align_of::()); + + // + // Handles / Events + // + + assert_eq!(size_of::(), size_of::()); + assert_eq!(align_of::(), align_of::()); + assert_eq!(size_of::(), size_of::()); + assert_eq!(align_of::(), align_of::()); + + assert_eq!(size_of::(), size_of::<*mut ()>()); + assert_eq!(align_of::(), align_of::<*mut ()>()); + assert_eq!(size_of::(), size_of::<*mut ()>()); + assert_eq!(align_of::(), align_of::<*mut ()>()); + + // + // Lba / Tpl + // + + assert_eq!(size_of::(), size_of::()); + assert_eq!(align_of::(), align_of::()); + assert_eq!(size_of::(), size_of::()); + assert_eq!(align_of::(), align_of::()); + + // + // PhysicalAddress / VirtualAddress + // + + assert_eq!(size_of::(), size_of::()); + assert_eq!(align_of::(), align_of::()); + assert_eq!(size_of::(), size_of::()); + assert_eq!(align_of::(), align_of::()); + + // + // ImageEntryPoint + // + + assert_eq!(size_of::(), size_of::()); + assert_eq!(align_of::(), align_of::()); + + // + // Guid + // + + assert_eq!(size_of::(), 16); + assert_eq!(align_of::(), 4); + + // + // Networking Types + // + + assert_eq!(size_of::(), 32); + assert_eq!(align_of::(), 1); + assert_eq!(size_of::(), 4); + assert_eq!(align_of::(), 1); + assert_eq!(size_of::(), 16); + assert_eq!(align_of::(), 1); + assert_eq!(size_of::(), 16); + assert_eq!(align_of::(), 4); + } + + #[test] + fn eficall() { + // + // Make sure the eficall!{} macro can deal with all kinds of function callbacks. + // + + let _: eficall! {fn()}; + let _: eficall! {unsafe fn()}; + let _: eficall! {fn(i32)}; + let _: eficall! {fn(i32) -> i32}; + let _: eficall! {fn(i32, i32) -> (i32, i32)}; + + eficall! {fn _unused00() {}} + eficall! {unsafe fn _unused01() {}} + eficall! {pub unsafe fn _unused02() {}} + } + + // Verify Boolean ABI + // + // Even though booleans are strictly 1-bit, and thus 0 or 1, in practice all UEFI systems + // treat it more like C does, and a boolean formatted as `u8` now allows any value other than + // 0 to represent `true`. Make sure we support the same. + #[test] + fn booleans() { + // Verify PartialEq works. + assert_ne!(Boolean::FALSE, Boolean::TRUE); + + // Verify Boolean<->bool conversion and comparison works. + assert_eq!(Boolean::FALSE, false); + assert_eq!(Boolean::TRUE, true); + + // Iterate all possible values for `u8` and verify 0 behaves as `false`, and everything + // else behaves as `true`. We verify both, the natural constructor through `From`, as well + // as a transmute. + for i in 0u8..=255u8 { + let v1: Boolean = i.into(); + let v2: Boolean = unsafe { std::mem::transmute::(i) }; + + assert_eq!(v1, v2); + assert_eq!(v1, v1); + assert_eq!(v2, v2); + + match i { + 0 => { + assert_eq!(v1, Boolean::FALSE); + assert_eq!(v1, false); + assert_eq!(v2, Boolean::FALSE); + assert_eq!(v2, false); + + assert_ne!(v1, Boolean::TRUE); + assert_ne!(v1, true); + assert_ne!(v2, Boolean::TRUE); + assert_ne!(v2, true); + + assert!(v1 < Boolean::TRUE); + assert!(v1 < true); + assert!(v1 >= Boolean::FALSE); + assert!(v1 >= false); + assert!(v1 <= Boolean::FALSE); + assert!(v1 <= false); + assert_eq!(v1.cmp(&true.into()), core::cmp::Ordering::Less); + assert_eq!(v1.cmp(&false.into()), core::cmp::Ordering::Equal); + + assert_eq!(hash(&v1), hash(&false)); + } + _ => { + assert_eq!(v1, Boolean::TRUE); + assert_eq!(v1, true); + assert_eq!(v2, Boolean::TRUE); + assert_eq!(v2, true); + + assert_ne!(v1, Boolean::FALSE); + assert_ne!(v1, false); + assert_ne!(v2, Boolean::FALSE); + assert_ne!(v2, false); + + assert!(v1 <= Boolean::TRUE); + assert!(v1 <= true); + assert!(v1 >= Boolean::TRUE); + assert!(v1 >= true); + assert!(v1 > Boolean::FALSE); + assert!(v1 > false); + assert_eq!(v1.cmp(&true.into()), core::cmp::Ordering::Equal); + assert_eq!(v1.cmp(&false.into()), core::cmp::Ordering::Greater); + + assert_eq!(hash(&v1), hash(&true)); + } + } + } + } + + // Verify Guid Manipulations + // + // Test that creation of Guids from fields and bytes yields the expected + // values, and conversions work as expected. + #[test] + fn guid() { + let fields = ( + 0x550e8400, + 0xe29b, + 0x41d4, + 0xa7, + 0x16, + &[0x44, 0x66, 0x55, 0x44, 0x00, 0x00], + ); + #[rustfmt::skip] + let bytes = [ + 0x00, 0x84, 0x0e, 0x55, + 0x9b, 0xe2, + 0xd4, 0x41, + 0xa7, + 0x16, + 0x44, 0x66, 0x55, 0x44, 0x00, 0x00, + ]; + let (f0, f1, f2, f3, f4, f5) = fields; + let g_fields = Guid::from_fields(f0, f1, f2, f3, f4, f5); + let g_bytes = Guid::from_bytes(&bytes); + + assert_eq!(g_fields, g_bytes); + assert_eq!(g_fields.as_bytes(), &bytes); + assert_eq!(g_bytes.as_fields(), fields); + } +} diff --git a/tools/vendor/r-efi/src/hii.rs b/tools/vendor/r-efi/src/hii.rs new file mode 100644 index 0000000000..340176b62f --- /dev/null +++ b/tools/vendor/r-efi/src/hii.rs @@ -0,0 +1,1300 @@ +//! Human Interface Infrastructure (HII) +//! +//! This module contains bindings and definitions copied from Section 33.3 of +//! the UEFI spec, as well as the core HII related definitions. + +// +// Core HII Definitions +// + +// This is the exception to the rule. It's defined in 34.8 (HII_DATABASE +// protocol), not 33.3, but it's used throughout the HII protocols, so it makes +// sense to be defined at the base. +pub type Handle = *mut core::ffi::c_void; + +// +// 33.3.1 Package Lists and Package Headers +// + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct PackageHeader { + pub length: [u8; 3], + pub r#type: u8, + pub data: [u8; N], +} + +pub const PACKAGE_TYPE_ALL: u8 = 0x00; +pub const PACKAGE_TYPE_GUID: u8 = 0x01; +pub const PACKAGE_FORMS: u8 = 0x02; +pub const PACKAGE_STRINGS: u8 = 0x04; +pub const PACKAGE_FONTS: u8 = 0x05; +pub const PACKAGE_IMAGES: u8 = 0x06; +pub const PACKAGE_SIMPLE_FONTS: u8 = 0x07; +pub const PACKAGE_DEVICE_PATH: u8 = 0x08; +pub const PACKAGE_KEYBOARD_LAYOUT: u8 = 0x09; +pub const PACKAGE_ANIMATIONS: u8 = 0x0A; +pub const PACKAGE_END: u8 = 0xDF; +pub const PACKAGE_TYPE_SYSTEM_BEGIN: u8 = 0xE0; +pub const PACKAGE_TYPE_SYSTEM_END: u8 = 0xFF; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct PackageListHeader { + pub package_list_guid: crate::base::Guid, + pub package_length: u32, +} + +// +// 33.3.3 Font Package +// + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct FontPackageHdr { + pub header: PackageHeader, + pub hdr_size: u32, + pub glyph_block_offset: u32, + pub cell: GlyphInfo, + pub font_style: FontStyle, + pub font_family: [crate::base::Char16; N], +} + +pub type FontStyle = u32; + +pub const FONT_STYLE_NORMAL: FontStyle = 0x00000000; +pub const FONT_STYLE_BOLD: FontStyle = 0x00000001; +pub const FONT_STYLE_ITALIC: FontStyle = 0x00000002; +pub const FONT_STYLE_EMBOSS: FontStyle = 0x00010000; +pub const FONT_STYLE_OUTLINE: FontStyle = 0x00020000; +pub const FONT_STYLE_SHADOW: FontStyle = 0x00040000; +pub const FONT_STYLE_UNDERLINE: FontStyle = 0x00080000; +pub const FONT_STYLE_DBL_UNDER: FontStyle = 0x00100000; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct GlyphBlock { + pub block_type: u8, + pub block_body: [u8; N], +} + +pub const GIBT_END: u8 = 0x00; +pub const GIBT_GLYPH: u8 = 0x10; +pub const GIBT_GLYPHS: u8 = 0x11; +pub const GIBT_GLYPH_DEFAULT: u8 = 0x12; +pub const GIBT_GLYPHS_DEFAULT: u8 = 0x13; +pub const GIBT_GLYPH_VARIABILITY: u8 = 0x14; +pub const GIBT_DUPLICATE: u8 = 0x20; +pub const GIBT_SKIP2: u8 = 0x21; +pub const GIBT_SKIP1: u8 = 0x22; +pub const GIBT_DEFAULTS: u8 = 0x23; +pub const GIBT_EXT1: u8 = 0x30; +pub const GIBT_EXT2: u8 = 0x31; +pub const GIBT_EXT4: u8 = 0x32; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct GlyphInfo { + pub width: u16, + pub height: u16, + pub offset_x: i16, + pub offset_y: i16, + pub advance_x: i16, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct GibtDefaultsBlock { + pub header: GlyphBlock, + pub cell: GlyphInfo, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct GibtDuplicateBlock { + pub header: GlyphBlock, + pub char_value: crate::base::Char16, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct GlyphGibtEndBlock { + pub header: GlyphBlock, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct GibtExt1Block { + pub header: GlyphBlock, + pub block_type_2: u8, + pub length: u8, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct GibtExt2Block { + pub header: GlyphBlock, + pub block_type_2: u8, + pub length: u16, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct GibtExt4Block { + pub header: GlyphBlock, + pub block_type_2: u8, + pub length: u32, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct GibtGlyphBlock { + pub header: GlyphBlock, + pub cell: GlyphInfo, + pub bitmap_data: [u8; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct GibtGlyphsBlock { + pub header: GlyphBlock, + pub cell: GlyphInfo, + pub count: u16, + pub bitmap_data: [u8; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct GibtGlyphDefaultBlock { + pub header: GlyphBlock, + pub bitmap_data: [u8; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct GibtGlypshDefaultBlock { + pub header: GlyphBlock, + pub count: u16, + pub bitmap_data: [u8; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct GibtSkip2Block { + pub header: GlyphBlock, + pub skip_count: u16, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct GibtSkip1Block { + pub header: GlyphBlock, + pub skip_count: u8, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct GibtVariabilityBlock { + pub header: GlyphBlock, + pub cell: GlyphInfo, + pub glyph_pack_in_bits: u8, + pub bitmap_data: [u8; N], +} + +// +// 33.3.8 Forms Package +// + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct FormPackageHdr { + pub header: PackageHeader, + pub op_code_header: IfrOpHeader, + // Op-Codes Follow... +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrOpHeader { + pub op_code: u8, + pub length_and_scope: u8, // Length:7, Scope:1 +} + +pub type QuestionId = u16; +pub type ImageId = u16; +pub type StringId = u16; +pub type FormId = u16; +pub type VarstoreId = u16; +pub type AnimationId = u16; + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IfrQuestionHeader { + pub header: IfrStatementHeader, + pub question_id: QuestionId, + pub var_store_id: VarstoreId, + pub var_store_info: IfrQuestionHeaderVarstoreInfo, + pub flags: u8, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union IfrQuestionHeaderVarstoreInfo { + pub var_name: StringId, + pub var_offset: u16, +} + +pub const IFR_FLAG_READ_ONLY: u8 = 0x01; +pub const IFR_FLAG_CALLBACK: u8 = 0x04; +pub const IFR_FLAG_RESET_REQUIRED: u8 = 0x10; +pub const IFR_FLAG_REST_STYLE: u8 = 0x20; +pub const IFR_FLAG_RECONNECT_REQUIRED: u8 = 0x40; +pub const IFR_FLAG_OPTIONS_ONLY: u8 = 0x80; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrStatementHeader { + pub prompt: StringId, + pub help: StringId, +} + +pub const IFR_FORM_OP: u8 = 0x01; +pub const IFR_SUBTITLE_OP: u8 = 0x02; +pub const IFR_TEXT_OP: u8 = 0x03; +pub const IFR_IMAGE_OP: u8 = 0x04; +pub const IFR_ONE_OF_OP: u8 = 0x05; +pub const IFR_CHECKBOX_OP: u8 = 0x06; +pub const IFR_NUMERIC_OP: u8 = 0x07; +pub const IFR_PASSWORD_OP: u8 = 0x08; +pub const IFR_ONE_OF_OPTION_OP: u8 = 0x09; +pub const IFR_SUPPRESS_IF_OP: u8 = 0x0A; +pub const IFR_LOCKED_OP: u8 = 0x0B; +pub const IFR_ACTION_OP: u8 = 0x0C; +pub const IFR_RESET_BUTTON_OP: u8 = 0x0D; +pub const IFR_FORM_SET_OP: u8 = 0x0E; +pub const IFR_REF_OP: u8 = 0x0F; +pub const IFR_NO_SUBMIT_IF_OP: u8 = 0x10; +pub const IFR_INCONSISTENT_IF_OP: u8 = 0x11; +pub const IFR_EQ_ID_VAL_OP: u8 = 0x12; +pub const IFR_EQ_ID_ID_OP: u8 = 0x13; +pub const IFR_EQ_ID_VAL_LIST_OP: u8 = 0x14; +pub const IFR_AND_OP: u8 = 0x15; +pub const IFR_OR_OP: u8 = 0x16; +pub const IFR_NOT_OP: u8 = 0x17; +pub const IFR_RULE_OP: u8 = 0x18; +pub const IFR_GRAY_OUT_IF_OP: u8 = 0x19; +pub const IFR_DATE_OP: u8 = 0x1A; +pub const IFR_TIME_OP: u8 = 0x1B; +pub const IFR_STRING_OP: u8 = 0x1C; +pub const IFR_REFRESH_OP: u8 = 0x1D; +pub const IFR_DISABLE_IF_OP: u8 = 0x1E; +pub const IFR_ANIMATION_OP: u8 = 0x1F; +pub const IFR_TO_LOWER_OP: u8 = 0x20; +pub const IFR_TO_UPPER_OP: u8 = 0x21; +pub const IFR_MAP_OP: u8 = 0x22; +pub const IFR_ORDERED_LIST_OP: u8 = 0x23; +pub const IFR_VARSTORE_OP: u8 = 0x24; +pub const IFR_VARSTORE_NAME_VALUE_OP: u8 = 0x25; +pub const IFR_VARSTORE_EFI_OP: u8 = 0x26; +pub const IFR_VARSTORE_DEVICE_OP: u8 = 0x27; +pub const IFR_VERSION_OP: u8 = 0x28; +pub const IFR_END_OP: u8 = 0x29; +pub const IFR_MATCH_OP: u8 = 0x2A; +pub const IFR_GET_OP: u8 = 0x2B; +pub const IFR_SET_OP: u8 = 0x2C; +pub const IFR_READ_OP: u8 = 0x2D; +pub const IFR_WRITE_OP: u8 = 0x2E; +pub const IFR_EQUAL_OP: u8 = 0x2F; +pub const IFR_NOT_EQUAL_OP: u8 = 0x30; +pub const IFR_GREATER_THAN_OP: u8 = 0x31; +pub const IFR_GREATER_EQUAL_OP: u8 = 0x32; +pub const IFR_LESS_THAN_OP: u8 = 0x33; +pub const IFR_LESS_EQUAL_OP: u8 = 0x34; +pub const IFR_BITWISE_AND_OP: u8 = 0x35; +pub const IFR_BITWISE_OR_OP: u8 = 0x36; +pub const IFR_BITWISE_NOT_OP: u8 = 0x37; +pub const IFR_SHIFT_LEFT_OP: u8 = 0x38; +pub const IFR_SHIFT_RIGHT_OP: u8 = 0x39; +pub const IFR_ADD_OP: u8 = 0x3A; +pub const IFR_SUBTRACT_OP: u8 = 0x3B; +pub const IFR_MULTIPLY_OP: u8 = 0x3C; +pub const IFR_DIVIDE_OP: u8 = 0x3D; +pub const IFR_MODULO_OP: u8 = 0x3E; +pub const IFR_RULE_REF_OP: u8 = 0x3F; +pub const IFR_QUESTION_REF1_OP: u8 = 0x40; +pub const IFR_QUESTION_REF2_OP: u8 = 0x41; +pub const IFR_UINT8_OP: u8 = 0x42; +pub const IFR_UINT16_OP: u8 = 0x43; +pub const IFR_UINT32_OP: u8 = 0x44; +pub const IFR_UINT64_OP: u8 = 0x45; +pub const IFR_TRUE_OP: u8 = 0x46; +pub const IFR_FALSE_OP: u8 = 0x47; +pub const IFR_TO_UINT_OP: u8 = 0x48; +pub const IFR_TO_STRING_OP: u8 = 0x49; +pub const IFR_TO_BOOLEAN_OP: u8 = 0x4A; +pub const IFR_MID_OP: u8 = 0x4B; +pub const IFR_FIND_OP: u8 = 0x4C; +pub const IFR_TOKEN_OP: u8 = 0x4D; +pub const IFR_STRING_REF1_OP: u8 = 0x4E; +pub const IFR_STRING_REF2_OP: u8 = 0x4F; +pub const IFR_CONDITIONAL_OP: u8 = 0x50; +pub const IFR_QUESTION_REF3_OP: u8 = 0x51; +pub const IFR_ZERO_OP: u8 = 0x52; +pub const IFR_ONE_OP: u8 = 0x53; +pub const IFR_ONES_OP: u8 = 0x54; +pub const IFR_UNDEFINED_OP: u8 = 0x55; +pub const IFR_LENGTH_OP: u8 = 0x56; +pub const IFR_DUP_OP: u8 = 0x57; +pub const IFR_THIS_OP: u8 = 0x58; +pub const IFR_SPAN_OP: u8 = 0x59; +pub const IFR_VALUE_OP: u8 = 0x5A; +pub const IFR_DEFAULT_OP: u8 = 0x5B; +pub const IFR_DEFAULTSTORE_OP: u8 = 0x5C; +pub const IFR_FORM_MAP_OP: u8 = 0x5D; +pub const IFR_CATENATE_OP: u8 = 0x5E; +pub const IFR_GUID_OP: u8 = 0x5F; +pub const IFR_SECURITY_OP: u8 = 0x60; +pub const IFR_MODAL_TAG_OP: u8 = 0x61; +pub const IFR_REFRESH_ID_OP: u8 = 0x62; +pub const IFR_WARNING_IF_OP: u8 = 0x63; +pub const IFR_MATCH2_OP: u8 = 0x64; + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IfrAction { + pub header: IfrOpHeader, + pub question: IfrQuestionHeader, + pub question_config: StringId, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IfrAction1 { + pub header: IfrOpHeader, + pub question: IfrQuestionHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrAnimation { + pub header: IfrOpHeader, + pub id: AnimationId, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrAdd { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrAnd { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrBitwiseAnd { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrBitwiseNot { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrBitwiseOr { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrCatenate { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IfrCheckbox { + pub header: IfrOpHeader, + pub question: IfrQuestionHeader, + pub flags: u8, +} + +pub const IFR_CHECKBOX_DEFAULT: u8 = 0x01; +pub const IFR_CHECKBOX_DEFAULT_MFG: u8 = 0x02; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrConditional { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IfrDate { + pub header: IfrOpHeader, + pub question: IfrQuestionHeader, + pub flags: u8, +} + +pub const QF_DATE_YEAR_SUPPRESS: u8 = 0x01; +pub const QF_DATE_MONTH_SUPPRESS: u8 = 0x02; +pub const QF_DATE_DAY_SUPPRESS: u8 = 0x04; +pub const QF_DATE_STORAGE: u8 = 0x30; + +pub const QF_DATE_STORAGE_NORMAL: u8 = 0x00; +pub const QF_DATE_STORAGE_TIME: u8 = 0x10; +pub const QF_DATE_STORAGE_WAKEUP: u8 = 0x20; + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IfrDefault { + pub header: IfrOpHeader, + pub default_id: u16, + pub r#type: u8, + pub value: IfrTypeValue, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrDefault2 { + pub header: IfrOpHeader, + pub default_id: u16, + pub r#type: u8, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrDefaultstore { + pub header: IfrOpHeader, + pub default_name: StringId, + pub default_id: u16, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrDisableIf { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrDivide { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrDup { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrEnd { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrEqual { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrEqIdId { + pub header: IfrOpHeader, + pub question_id_1: QuestionId, + pub question_id_2: QuestionId, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrEqIdValList { + pub header: IfrOpHeader, + pub question_id: QuestionId, + pub list_length: u16, + pub value_list: [u16; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrEqIdVal { + pub header: IfrOpHeader, + pub question_id: QuestionId, + pub value: u16, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrFalse { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrFind { + pub header: IfrOpHeader, + pub format: u8, +} + +pub const IFR_FF_CASE_SENSITIVE: u8 = 0x00; +pub const IFR_FF_CASE_INSENSITIVE: u8 = 0x01; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrForm { + pub header: IfrOpHeader, + pub form_id: FormId, + pub form_title: StringId, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrFormMapMethod { + pub method_title: StringId, + pub method_identifier: crate::base::Guid, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrFormMap { + pub header: IfrOpHeader, + pub form_id: FormId, + pub methods: [IfrFormMapMethod; N], +} + +pub const STANDARD_FORM_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x3bd2f4ec, + 0xe524, + 0x46e4, + 0xa9, + 0xd8, + &[0x51, 0x01, 0x17, 0x42, 0x55, 0x62], +); + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrFormSet { + pub header: IfrOpHeader, + pub guid: crate::base::Guid, + pub form_set_title: StringId, + pub help: StringId, + pub flags: u8, + pub class_guid: [crate::base::Guid; N], +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IfrGet { + pub header: IfrOpHeader, + pub var_store_id: VarstoreId, + pub var_store_info: IfrGetVarStoreInfo, + pub var_store_type: u8, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union IfrGetVarStoreInfo { + pub var_name: StringId, + pub var_offset: u16, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrGrayOutIf { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrGreaterEqual { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrGreaterThan { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrGuid { + pub header: IfrOpHeader, + pub guid: crate::base::Guid, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrImage { + pub id: ImageId, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrInconsistentIf { + pub header: IfrOpHeader, + pub error: StringId, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrLength { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrLessEqual { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrLessThan { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrLocked { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrMap { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrMatch { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrMid { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrModalTag { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrModulo { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrMultiply { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrNot { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrNotEqual { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrNoSubmitIf { + pub header: IfrOpHeader, + pub error: StringId, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrNumericDataU8 { + pub min_value: u8, + pub max_value: u8, + pub step: u8, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrNumericDataU16 { + pub min_value: u16, + pub max_value: u16, + pub step: u16, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrNumericDataU32 { + pub min_value: u32, + pub max_value: u32, + pub step: u32, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrNumericDataU64 { + pub min_value: u64, + pub max_value: u64, + pub step: u64, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union IfrNumericData { + pub r#u8: IfrNumericDataU8, + pub r#u16: IfrNumericDataU16, + pub r#u32: IfrNumericDataU32, + pub r#u64: IfrNumericDataU64, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IfrNumeric { + pub header: IfrOpHeader, + pub question: IfrQuestionHeader, + pub flags: u8, + pub data: IfrNumericData, +} + +pub const IFR_NUMERIC_SIZE: u8 = 0x03; +pub const IFR_NUMERIC_SIZE_1: u8 = 0x00; +pub const IFR_NUMERIC_SIZE_2: u8 = 0x01; +pub const IFR_NUMERIC_SIZE_4: u8 = 0x02; +pub const IFR_NUMERIC_SIZE_8: u8 = 0x03; + +pub const IFR_DISPLAY: u8 = 0x30; +pub const IFR_DISPLAY_INT_DEC: u8 = 0x00; +pub const IFR_DISPLAY_UINT_DEC: u8 = 0x10; +pub const IFR_DISPLAY_UINT_HEX: u8 = 0x20; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrOne { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrOnes { + pub header: IfrOpHeader, +} + +type IfrOneOfData = IfrNumericData; + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IfrOneOf { + pub header: IfrOpHeader, + pub question: IfrQuestionHeader, + pub flags: u8, + pub data: IfrOneOfData, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IfrOneOfOption { + pub header: IfrOpHeader, + pub option: StringId, + pub flags: u8, + pub r#type: u8, + pub value: IfrTypeValue, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union IfrTypeValue { + pub r#u8: u8, + pub r#u16: u16, + pub r#u32: u32, + pub r#u64: u64, + pub b: crate::base::Boolean, + pub time: Time, + pub date: Date, + pub string: StringId, + pub r#ref: Ref, + pub buffer: [u8; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Time { + pub hour: u8, + pub minute: u8, + pub second: u8, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Date { + pub year: u16, + pub month: u8, + pub day: u8, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Ref { + pub question_id: QuestionId, + pub form_id: FormId, + pub form_set_guid: crate::base::Guid, + pub device_path: StringId, +} + +pub const IFR_TYPE_NUM_SIZE_8: u8 = 0x00; +pub const IFR_TYPE_NUM_SIZE_16: u8 = 0x01; +pub const IFR_TYPE_NUM_SIZE_32: u8 = 0x02; +pub const IFR_TYPE_NUM_SIZE_64: u8 = 0x03; +pub const IFR_TYPE_BOOLEAN: u8 = 0x04; +pub const IFR_TYPE_TIME: u8 = 0x05; +pub const IFR_TYPE_DATE: u8 = 0x06; +pub const IFR_TYPE_STRING: u8 = 0x07; +pub const IFR_TYPE_OTHER: u8 = 0x08; +pub const IFR_TYPE_UNDEFINED: u8 = 0x09; +pub const IFR_TYPE_ACTION: u8 = 0x0A; +pub const IFR_TYPE_BUFFER: u8 = 0x0B; +pub const IFR_TYPE_REF: u8 = 0x0C; + +pub const IFR_OPTION_DEFAULT: u8 = 0x10; +pub const IFR_OPTION_DEFAULT_MFG: u8 = 0x20; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrOr { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IfrOrderedList { + pub header: IfrOpHeader, + pub question: IfrQuestionHeader, + pub max_containers: u8, + pub flags: u8, +} + +pub const IFR_UNIQUE_SET: u8 = 0x01; +pub const IFR_NO_EMPTY_SET: u8 = 0x02; + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IfrPassword { + pub header: IfrOpHeader, + pub question: IfrQuestionHeader, + pub min_size: u16, + pub max_size: u16, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrQuestionRef1 { + pub header: IfrOpHeader, + pub question_id: QuestionId, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrQuestionRef2 { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrQuestionRef3 { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrQuestionRef32 { + pub header: IfrOpHeader, + pub device_path: StringId, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrQuestionRef33 { + pub header: IfrOpHeader, + pub device_path: StringId, + pub guid: crate::base::Guid, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrRead { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IfrRef { + pub header: IfrOpHeader, + pub question: IfrQuestionHeader, + pub form_id: FormId, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IfrRef2 { + pub header: IfrOpHeader, + pub question: IfrQuestionHeader, + pub form_id: FormId, + pub question_id: QuestionId, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IfrRef3 { + pub header: IfrOpHeader, + pub question: IfrQuestionHeader, + pub form_id: FormId, + pub question_id: QuestionId, + pub form_set_id: crate::base::Guid, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IfrRef4 { + pub header: IfrOpHeader, + pub question: IfrQuestionHeader, + pub form_id: FormId, + pub question_id: QuestionId, + pub form_set_id: crate::base::Guid, + pub device_path: StringId, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IfrRef5 { + pub header: IfrOpHeader, + pub question: IfrQuestionHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrRefresh { + pub header: IfrOpHeader, + pub refresh_interval: u8, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrRefreshId { + pub header: IfrOpHeader, + pub refresh_event_group_id: crate::base::Guid, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrResetButton { + pub header: IfrOpHeader, + pub statement: IfrStatementHeader, + pub deafult_id: DefaultId, +} + +pub type DefaultId = u16; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrRule { + pub header: IfrOpHeader, + pub rule_id: u8, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrRuleRef { + pub header: IfrOpHeader, + pub rule_id: u8, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrSecurity { + pub header: IfrOpHeader, + pub permissions: crate::base::Guid, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union IfrSetVarStoreInfo { + pub var_name: StringId, + pub var_offset: u16, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IfrSet { + pub header: IfrOpHeader, + pub var_store_id: VarstoreId, + pub var_store_info: IfrSetVarStoreInfo, + pub var_store_type: u8, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrShiftLeft { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrShiftRight { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrSpan { + pub header: IfrOpHeader, + pub flags: u8, +} + +pub const IFR_FLAGS_FIRST_MATCHING: u8 = 0x00; +pub const IFR_FLAGS_FIRST_NON_MATCHING: u8 = 0x01; + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IfrString { + pub header: IfrOpHeader, + pub question: IfrQuestionHeader, + pub min_size: u8, + pub max_size: u8, + pub flags: u8, +} + +pub const IFR_STRING_MULTI_LINE: u8 = 0x01; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrStringRef1 { + pub header: IfrOpHeader, + pub string_id: StringId, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrStringRef2 { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrSubtitle { + pub header: IfrOpHeader, + pub statement: IfrStatementHeader, + pub flags: u8, +} + +pub const IFR_FLAGS_HORIZONTAL: u8 = 0x01; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrSubtract { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrSuppressIf { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrText { + pub header: IfrOpHeader, + pub statement: IfrStatementHeader, + pub text_two: StringId, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrThis { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IfrTime { + pub header: IfrOpHeader, + pub question: IfrQuestionHeader, + pub flags: u8, +} + +pub const QF_TIME_HOUR_SUPPRESS: u8 = 0x01; +pub const QF_TIME_MINUTE_SUPPRESS: u8 = 0x02; +pub const QF_TIME_SECOND_SUPPRESS: u8 = 0x04; +pub const QF_TIME_STORAGE: u8 = 0x30; + +pub const QF_TIME_STORAGE_NORMAL: u8 = 0x00; +pub const QF_TIME_STORAGE_TIME: u8 = 0x10; +pub const QF_TIME_STORAGE_WAKEUP: u8 = 0x20; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrToken { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrToBoolean { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrToLower { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrToString { + pub header: IfrOpHeader, + pub format: u8, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrToUint { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrToUpper { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrTrue { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrUint8 { + pub header: IfrOpHeader, + pub value: u8, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrUint16 { + pub header: IfrOpHeader, + pub value: u16, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrUint32 { + pub header: IfrOpHeader, + pub value: u32, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrUint64 { + pub header: IfrOpHeader, + pub value: u64, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrUndefined { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrValue { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrVarstore { + pub header: IfrOpHeader, + pub guid: crate::base::Guid, + pub var_store_id: VarstoreId, + pub size: u16, + pub name: [u8; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrVarstoreNameValue { + pub header: IfrOpHeader, + pub var_store_id: VarstoreId, + pub guid: crate::base::Guid, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrVarstoreEfi { + pub header: IfrOpHeader, + pub var_store_id: VarstoreId, + pub guid: crate::base::Guid, + pub attributes: u32, + pub size: u16, + pub name: [u8; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrVarstoreDevice { + pub header: IfrOpHeader, + pub device_path: StringId, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrVersion { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrWrite { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrZero { + pub header: IfrOpHeader, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrWarningIf { + pub header: IfrOpHeader, + pub warning: StringId, + pub time_out: u8, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IfrMatch2 { + pub header: IfrOpHeader, + pub syntax_type: crate::base::Guid, +} diff --git a/tools/vendor/r-efi/src/lib.rs b/tools/vendor/r-efi/src/lib.rs new file mode 100644 index 0000000000..3e0426c98c --- /dev/null +++ b/tools/vendor/r-efi/src/lib.rs @@ -0,0 +1,182 @@ +//! UEFI Reference Specification Protocol Constants and Definitions +//! +//! This project provides protocol constants and definitions as defined in the UEFI Reference +//! Specification. The aim is to provide all these constants as C-ABI compatible imports to rust. +//! Safe rust abstractions over the UEFI API are out of scope of this project. That is, the +//! purpose is really just to extract all the bits and pieces from the specification and provide +//! them as rust types and constants. +//! +//! While we strongly recommend using safe abstractions to interact with UEFI systems, this +//! project serves both as base to write those abstractions, but also as last resort if you have +//! to deal with quirks and peculiarities of UEFI systems directly. Therefore, several examples +//! are included, which show how to interact with UEFI systems from rust. These serve both as +//! documentation for anyone interested in how the system works, but also as base for anyone +//! implementing safe abstractions on top. +//! +//! # Target Configuration +//! +//! Rust code can be compiled natively for UEFI systems. However, you are quite unlikely to have a +//! rust compiler running in an UEFI environment. Therefore, you will most likely want to cross +//! compile your rust code for UEFI systems. To do this, you need a target-configuration for UEFI +//! systems. As of rust-1.61, upstream rust includes the following UEFI targets: +//! +//! * `aarch64-unknown-uefi`: A native UEFI target for aarch64 systems (64bit ARM). +//! * `i686-unknown-uefi`: A native UEFI target for i686 systems (32bit Intel x86). +//! * `x86_64-unknown-uefi`: A native UEFI target for x86-64 systems (64bit Intel x86). +//! +//! If none of these targets match your architecture, you have to create the target specification +//! yourself. Feel free to contact the `r-efi` project for help. +//! +//! # Transpose Guidelines +//! +//! The UEFI specification provides C language symbols and definitions of all +//! its protocols and features. Those are integral parts of the specification +//! and UEFI programming is often tightly coupled with the C language. For +//! better compatibility to existing UEFI documentation, all the rust symbols +//! are transposed from C following strict rules, aiming for close similarity +//! to specification. This section gives a rationale on some of the less +//! obvious choices and tries to describe as many of those rules as possible. +//! +//! * `no enums`: Rust enums do not allow random discriminant values. However, +//! many UEFI enumerations use reserved ranges for vendor defined values. +//! These cannot be represented with rust enums in an efficient manner. +//! Hence, any enumerations are turned into rust constants with an +//! accompanying type alias. +//! +//! A detailed discussion can be found in: +//! +//! ```gitlog +//! commit 401a91901e860a5c0cd0f92b75dda0a72cf65322 +//! Author: David Rheinsberg +//! Date: Wed Apr 21 12:07:07 2021 +0200 +//! +//! r-efi: convert enums to constants +//! ``` +//! +//! * `no incomplete types`: Several structures use incomplete structure types +//! by using an unbound array as last member. While rust can easily +//! represent those within its type-system, such structures become DSTs, +//! hence even raw pointers to them become fat-pointers, and would thus +//! violate the UEFI ABI. +//! +//! Instead, we use const-generics to allow compile-time adjustment of the +//! variable-sized structures, with a default value of 0. This allows +//! computing different sizes of the structures without any runtime overhead. +//! +//! * `nullable callbacks as Option`: Rust has no raw function pointers, but +//! just normal Rust function pointers. Those, however, have no valid null +//! value. The Rust ABI guarantees that `Option` is an C-ABI +//! compatible replacement for nullable function pointers, with `None` being +//! mapped to `NULL`. Hence, whenever UEFI APIs require nullable function +//! pointers, we use `Option`. +//! +//! * `prefer *mut over *const`: Whenever we transpose pointers from the +//! specification into Rust, we prefer `*mut` in almost all cases. `*const` +//! should only be used if the underlying value is known not to be accessible +//! via any other mutable pointer type. Since this is rarely the case in +//! UEFI, we avoid it. +//! +//! The reasoning is that Rust allows coercing immutable types into `*const` +//! pointers, without any explicit casting required. However, immutable Rust +//! references require that no other mutable reference exists simultaneously. +//! This is not a guarantee of `const`-pointers in C / UEFI, hence this +//! coercion is usually ill-advised or even wrong. +//! +//! Lastly, note that `*mut` and `*const` and be `as`-casted in both +//! directions without violating any Rust guarantees. Any UB concerns always +//! stem from the safety guarantees of the surrounding code, not of the +//! raw-pointer handling. +//! +//! # Specification Details +//! +//! This section lists errata of, and general comments on, the UEFI +//! specification relevant to the development of `r-efi`: +//! +//! * The `Unload` function-pointer of the LoadedImageProtocol can be `NULL`, +//! despite the protocol documentation lacking any mention of this. Other +//! parts of the specification refer to images lacking an unload function, +//! but there is no explicit documentation how this manifests in the +//! protocol structure. EDK2 assumes `NULL` indicates a lack of unload +//! function, and an errata has been submitted to the UEFI forum. +//! +//! * The specification mandates an 8-byte alignment for the `GUID` structure +//! However, all widespread implementations (including EDK2) use a 4-byte +//! alignment. An errata has been reported to EDK2 (still pending). +//! +//! # Examples +//! +//! To write free-standing UEFI applications, you need to disable the entry-point provided by rust +//! and instead provide your own. Most target-configurations look for a function called `efi_main` +//! during linking and set it as entry point. If you use the target-configurations provided with +//! upstream rust, they will pick the function called `efi_main` as entry-point. +//! +//! The following example shows a minimal UEFI application, which simply returns success upon +//! invocation. Note that you must provide your own panic-handler when running without `libstd`. +//! In our case, we use a trivial implementation that simply loops forever. +//! +//! ```ignore +//! #![no_main] +//! #![no_std] +//! +//! use r_efi::efi; +//! +//! #[panic_handler] +//! fn panic_handler(_info: &core::panic::PanicInfo) -> ! { +//! loop {} +//! } +//! +//! #[export_name = "efi_main"] +//! pub extern fn main(_h: efi::Handle, _st: *mut efi::SystemTable) -> efi::Status { +//! efi::Status::SUCCESS +//! } +//! ``` + +// Mark this crate as `no_std`. We have no std::* dependencies (and we better don't have them), +// so no reason to require it. This does not mean that you cannot use std::* with UEFI. You have +// to port it to UEFI first, though. +// +// In case of unit-test compilation, we pull in `std` and drop the `no_std` marker. This allows +// basic unit-tests on the compilation host. For integration tests, we have separate compilation +// units, so they will be unaffected by this. +#![cfg_attr(not(test), no_std)] + +// Import the different core modules. We separate them into different modules to make it easier to +// work on them and describe what each part implements. This is different to the reference +// implementation, which uses a flat namespace due to its origins in the C language. For +// compatibility, we provide this flat namespace as well. See the `efi` submodule. +#[macro_use] +pub mod base; +#[macro_use] +pub mod hii; +#[macro_use] +pub mod system; + +// Import the protocols. Each protocol is separated into its own module, readily imported by the +// meta `protocols` module. Note that this puts all symbols into their respective protocol +// namespace, thus clearly separating them (unlike the UEFI Specification, which more often than +// not violates its own namespacing). +pub mod protocols; + +// Import vendor protocols. They are just like protocols in `protocols`, but +// separated for better namespacing. +pub mod vendor; + +/// Flat EFI Namespace +/// +/// The EFI namespace re-exports all symbols in a single, flat namespace. This allows mirroring +/// the entire EFI namespace as given in the specification and makes it easier to refer to them +/// with the same names as the reference C implementation. +/// +/// Note that the individual protocols are put into submodules. The specification does this in +/// most parts as well (by prefixing all symbols). This is not true in all cases, as the +/// specification suffers from lack of namespaces in the reference C language. However, we decided +/// to namespace the remaining bits as well, for better consistency throughout the API. This +/// should be self-explanatory in nearly all cases. +pub mod efi { + pub use crate::base::*; + pub use crate::system::*; + + pub use crate::hii; + pub use crate::protocols; + pub use crate::vendor; +} diff --git a/tools/vendor/r-efi/src/protocols.rs b/tools/vendor/r-efi/src/protocols.rs new file mode 100644 index 0000000000..59d73d5cc3 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols.rs @@ -0,0 +1,54 @@ +//! UEFI Protocols +//! +//! The UEFI Specification splits most of its non-core parts into separate protocols. They can +//! refer to each other, but their documentation and implementation is split apart. We provide +//! each protocol as a separate module, so it is clearly defined where a symbol belongs to. + +pub mod absolute_pointer; +pub mod block_io; +pub mod bus_specific_driver_override; +pub mod debug_support; +pub mod debugport; +pub mod decompress; +pub mod device_path; +pub mod device_path_from_text; +pub mod device_path_to_text; +pub mod device_path_utilities; +pub mod disk_io; +pub mod disk_io2; +pub mod driver_binding; +pub mod driver_diagnostics2; +pub mod driver_family_override; +pub mod file; +pub mod graphics_output; +pub mod hii_database; +pub mod hii_font; +pub mod hii_font_ex; +pub mod hii_package_list; +pub mod hii_string; +pub mod ip4; +pub mod ip6; +pub mod load_file; +pub mod load_file2; +pub mod loaded_image; +pub mod loaded_image_device_path; +pub mod managed_network; +pub mod memory_attribute; +pub mod mp_services; +pub mod pci_io; +pub mod platform_driver_override; +pub mod rng; +pub mod service_binding; +pub mod shell; +pub mod shell_dynamic_command; +pub mod shell_parameters; +pub mod simple_file_system; +pub mod simple_network; +pub mod simple_text_input; +pub mod simple_text_input_ex; +pub mod simple_text_output; +pub mod tcp4; +pub mod tcp6; +pub mod timestamp; +pub mod udp4; +pub mod udp6; diff --git a/tools/vendor/r-efi/src/protocols/absolute_pointer.rs b/tools/vendor/r-efi/src/protocols/absolute_pointer.rs new file mode 100644 index 0000000000..e9aaeca6fc --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/absolute_pointer.rs @@ -0,0 +1,69 @@ +//! Absolute Pointer Protocol +//! +//! Provides a simple method for accessing absolute pointer devices. This +//! includes devices such as touch screens and digitizers. The Absolute Pointer +//! Protocol allows information about a pointer device to be retrieved. The +//! protocol is attached to the device handle of an absolute pointer device, +//! and can be used for input from the user in the preboot environment. +//! +//! Supported devices may return 1, 2, or 3 axis of information. The Z axis may +//! optionally be used to return pressure data measurements derived from user +//! pen force. +//! +//! All supported devices must support a touch-active status. Supported devices +//! may optionally support a second input button, for example a pen +//! side-button. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x8d59d32b, + 0xc655, + 0x4ae9, + 0x9b, + 0x15, + &[0xf2, 0x59, 0x04, 0x99, 0x2a, 0x43], +); + +pub const SUPPORTS_ALT_ACTIVE: u32 = 0x00000001; +pub const SUPPORTS_PRESSURE_AS_Z: u32 = 0x00000002; + +#[derive(Clone, Copy, Debug, Default)] +#[repr(C)] +pub struct Mode { + pub absolute_min_x: u64, + pub absolute_min_y: u64, + pub absolute_min_z: u64, + pub absolute_max_x: u64, + pub absolute_max_y: u64, + pub absolute_max_z: u64, + pub attributes: u32, +} + +pub const TOUCH_ACTIVE: u32 = 0x00000001; +pub const ALT_ACTIVE: u32 = 0x00000002; + +#[derive(Clone, Copy, Debug, Default)] +#[repr(C)] +pub struct State { + pub current_x: u64, + pub current_y: u64, + pub current_z: u64, + pub active_buttons: u32, +} + +pub type Reset = eficall! {fn( + this: *mut Protocol, + extended_verification: bool, +) -> crate::base::Status}; + +pub type GetState = eficall! {fn( + this: *mut Protocol, + state: *mut State, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub reset: Reset, + pub get_state: GetState, + pub wait_for_input: crate::efi::Event, + pub mode: *mut Mode, +} diff --git a/tools/vendor/r-efi/src/protocols/block_io.rs b/tools/vendor/r-efi/src/protocols/block_io.rs new file mode 100644 index 0000000000..26a2c0a10e --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/block_io.rs @@ -0,0 +1,70 @@ +//! Block I/O Protocol +//! +//! Used to abstract mass storage devices to allow code running in the EFI boot services environment +//! to access the storage devices without specific knowledge of the type of device or controller that +//! manages the device. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x964e5b21, + 0x6459, + 0x11d2, + 0x8e, + 0x39, + &[0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b], +); + +pub const REVISION: u64 = 0x0000000000010000u64; +pub const REVISION2: u64 = 0x0000000000020001u64; +pub const REVISION3: u64 = 0x000000000002001fu64; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Media { + pub media_id: u32, + pub removable_media: bool, + pub media_present: bool, + pub logical_partition: bool, + pub read_only: bool, + pub write_caching: bool, + pub block_size: u32, + pub io_align: u32, + pub last_block: crate::base::Lba, + pub lowest_aligned_lba: crate::base::Lba, + pub logical_blocks_per_physical_block: u32, + pub optimal_transfer_length_granularity: u32, +} + +pub type ProtocolReset = eficall! {fn( + *mut Protocol, + crate::base::Boolean, +) -> crate::base::Status}; + +pub type ProtocolReadBlocks = eficall! {fn( + *mut Protocol, + u32, + crate::base::Lba, + usize, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type ProtocolWriteBlocks = eficall! {fn( + *mut Protocol, + u32, + crate::base::Lba, + usize, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type ProtocolFlushBlocks = eficall! {fn( + *mut Protocol, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub revision: u64, + pub media: *const Media, + pub reset: ProtocolReset, + pub read_blocks: ProtocolReadBlocks, + pub write_blocks: ProtocolWriteBlocks, + pub flush_blocks: ProtocolFlushBlocks, +} diff --git a/tools/vendor/r-efi/src/protocols/bus_specific_driver_override.rs b/tools/vendor/r-efi/src/protocols/bus_specific_driver_override.rs new file mode 100644 index 0000000000..f9e2cda276 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/bus_specific_driver_override.rs @@ -0,0 +1,32 @@ +//! Bus Specific Driver Override Protocol +//! +//! This protocol matches one or more drivers to a controller. This protocol is +//! produced by a bus driver, and it is installed on the child handles of buses +//! that require a bus specific algorithm for matching drivers to controllers. +//! This protocol is used by the `EFI_BOOT_SERVICES.ConnectController()` boot +//! service to select the best driver for a controller. All of the drivers +//! returned by this protocol have a higher precedence than drivers found in +//! the general EFI Driver Binding search algorithm, but a lower precedence +//! than those drivers returned by the EFI Platform Driver Override Protocol. +//! If more than one driver image handle is returned by this protocol, then the +//! drivers image handles are returned in order from highest precedence to +//! lowest precedence. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x3bc1b285, + 0x8a15, + 0x4a82, + 0xaa, + 0xbf, + &[0x4d, 0x7d, 0x13, 0xfb, 0x32, 0x65], +); + +pub type ProtocolGetDriver = eficall! {fn( + *mut Protocol, + *mut crate::base::Handle, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub get_driver: ProtocolGetDriver, +} diff --git a/tools/vendor/r-efi/src/protocols/debug_support.rs b/tools/vendor/r-efi/src/protocols/debug_support.rs new file mode 100644 index 0000000000..c8272e9100 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/debug_support.rs @@ -0,0 +1,835 @@ +//! Debug Support Protocol +//! +//! It provides the services to allow the debug agent to register callback functions that are +//! called either periodically or when specific processor exceptions occur. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x2755590c, + 0x6f3c, + 0x42fa, + 0x9e, + 0xa4, + &[0xa3, 0xba, 0x54, 0x3c, 0xda, 0x25], +); + +pub type InstructionSetArchitecture = u32; + +pub const ISA_IA32: InstructionSetArchitecture = 0x014c; +pub const ISA_X64: InstructionSetArchitecture = 0x8664; +pub const ISA_IPF: InstructionSetArchitecture = 0x0200; +pub const ISA_EBC: InstructionSetArchitecture = 0x0ebc; +pub const ISA_ARM: InstructionSetArchitecture = 0x1c2; +pub const ISA_AARCH64: InstructionSetArchitecture = 0xaa64; +pub const ISA_RISCV32: InstructionSetArchitecture = 0x5032; +pub const ISA_RISCV64: InstructionSetArchitecture = 0x5064; +pub const ISA_RISCV128: InstructionSetArchitecture = 0x5128; + +#[repr(C)] +#[derive(Clone, Copy)] +pub union SystemContext { + pub system_context_ebc: *mut SystemContextEbc, + pub system_context_ia32: *mut SystemContextIa32, + pub system_context_x64: *mut SystemContextX64, + pub system_context_ipf: *mut SystemContextIpf, + pub system_context_arm: *mut SystemContextArm, + pub system_context_aarch64: *mut SystemContextAArch64, + pub system_context_riscv32: *mut SystemContextRiscV32, + pub system_context_riscv64: *mut SystemContextRiscV64, + pub system_context_riscv128: *mut SystemContextRiscV128, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct SystemContextEbc { + pub r0: u64, + pub r1: u64, + pub r2: u64, + pub r3: u64, + pub r4: u64, + pub r5: u64, + pub r6: u64, + pub r7: u64, + pub flags: u64, + pub control_flags: u64, + pub ip: u64, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct SystemContextRiscV32 { + // Integer registers + pub zero: u32, + pub ra: u32, + pub sp: u32, + pub gp: u32, + pub tp: u32, + pub t0: u32, + pub t1: u32, + pub t2: u32, + pub s0fp: u32, + pub s1: u32, + pub a0: u32, + pub a1: u32, + pub a2: u32, + pub a3: u32, + pub a4: u32, + pub a5: u32, + pub a6: u32, + pub a7: u32, + pub s2: u32, + pub s3: u32, + pub s4: u32, + pub s5: u32, + pub s6: u32, + pub s7: u32, + pub s8: u32, + pub s9: u32, + pub s10: u32, + pub s11: u32, + pub t3: u32, + pub t4: u32, + pub t5: u32, + pub t6: u32, + // Floating registers for F, D and Q Standard Extensions + pub ft0: u128, + pub ft1: u128, + pub ft2: u128, + pub ft3: u128, + pub ft4: u128, + pub ft5: u128, + pub ft6: u128, + pub ft7: u128, + pub fs0: u128, + pub fs1: u128, + pub fa0: u128, + pub fa1: u128, + pub fa2: u128, + pub fa3: u128, + pub fa4: u128, + pub fa5: u128, + pub fa6: u128, + pub fa7: u128, + pub fs2: u128, + pub fs3: u128, + pub fs4: u128, + pub fs5: u128, + pub fs6: u128, + pub fs7: u128, + pub fs8: u128, + pub fs9: u128, + pub fs10: u128, + pub fs11: u128, + pub ft8: u128, + pub ft9: u128, + pub ft10: u128, + pub ft11: u128, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct SystemContextRiscV64 { + // Integer registers + pub zero: u64, + pub ra: u64, + pub sp: u64, + pub gp: u64, + pub tp: u64, + pub t0: u64, + pub t1: u64, + pub t2: u64, + pub s0fp: u64, + pub s1: u64, + pub a0: u64, + pub a1: u64, + pub a2: u64, + pub a3: u64, + pub a4: u64, + pub a5: u64, + pub a6: u64, + pub a7: u64, + pub s2: u64, + pub s3: u64, + pub s4: u64, + pub s5: u64, + pub s6: u64, + pub s7: u64, + pub s8: u64, + pub s9: u64, + pub s10: u64, + pub s11: u64, + pub t3: u64, + pub t4: u64, + pub t5: u64, + pub t6: u64, + // Floating registers for F, D and Q Standard Extensions + pub ft0: u128, + pub ft1: u128, + pub ft2: u128, + pub ft3: u128, + pub ft4: u128, + pub ft5: u128, + pub ft6: u128, + pub ft7: u128, + pub fs0: u128, + pub fs1: u128, + pub fa0: u128, + pub fa1: u128, + pub fa2: u128, + pub fa3: u128, + pub fa4: u128, + pub fa5: u128, + pub fa6: u128, + pub fa7: u128, + pub fs2: u128, + pub fs3: u128, + pub fs4: u128, + pub fs5: u128, + pub fs6: u128, + pub fs7: u128, + pub fs8: u128, + pub fs9: u128, + pub fs10: u128, + pub fs11: u128, + pub ft8: u128, + pub ft9: u128, + pub ft10: u128, + pub ft11: u128, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct SystemContextRiscV128 { + // Integer registers + pub zero: u128, + pub ra: u128, + pub sp: u128, + pub gp: u128, + pub tp: u128, + pub t0: u128, + pub t1: u128, + pub t2: u128, + pub s0fp: u128, + pub s1: u128, + pub a0: u128, + pub a1: u128, + pub a2: u128, + pub a3: u128, + pub a4: u128, + pub a5: u128, + pub a6: u128, + pub a7: u128, + pub s2: u128, + pub s3: u128, + pub s4: u128, + pub s5: u128, + pub s6: u128, + pub s7: u128, + pub s8: u128, + pub s9: u128, + pub s10: u128, + pub s11: u128, + pub t3: u128, + pub t4: u128, + pub t5: u128, + pub t6: u128, + // Floating registers for F, D and Q Standard Extensions + pub ft0: u128, + pub ft1: u128, + pub ft2: u128, + pub ft3: u128, + pub ft4: u128, + pub ft5: u128, + pub ft6: u128, + pub ft7: u128, + pub fs0: u128, + pub fs1: u128, + pub fa0: u128, + pub fa1: u128, + pub fa2: u128, + pub fa3: u128, + pub fa4: u128, + pub fa5: u128, + pub fa6: u128, + pub fa7: u128, + pub fs2: u128, + pub fs3: u128, + pub fs4: u128, + pub fs5: u128, + pub fs6: u128, + pub fs7: u128, + pub fs8: u128, + pub fs9: u128, + pub fs10: u128, + pub fs11: u128, + pub ft8: u128, + pub ft9: u128, + pub ft10: u128, + pub ft11: u128, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct SystemContextIa32 { + // ExceptionData is additional data pushed on the stack by some types of IA-32 exceptions + pub exception_data: u32, + pub fx_save_state: FxSaveStateIA32, + pub dr0: u32, + pub dr1: u32, + pub dr2: u32, + pub dr3: u32, + pub dr6: u32, + pub dr7: u32, + pub cr0: u32, + // Reserved + pub cr1: u32, + pub cr2: u32, + pub cr3: u32, + pub cr4: u32, + pub eflags: u32, + pub ldtr: u32, + pub tr: u32, + pub gdtr: [u32; 2], + pub idtr: [u32; 2], + pub eip: u32, + pub gs: u32, + pub fs: u32, + pub es: u32, + pub ds: u32, + pub cs: u32, + pub ss: u32, + pub edi: u32, + pub esi: u32, + pub ebp: u32, + pub esp: u32, + pub ebx: u32, + pub edx: u32, + pub ecx: u32, + pub eax: u32, +} + +// FXSAVE_STATE - FP / MMX / XMM registers +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct FxSaveStateIA32 { + pub fcw: u16, + pub fsw: u16, + pub ftw: u16, + pub opcode: u16, + pub eip: u32, + pub cs: u16, + pub reserved_1: u16, + pub data_offset: u32, + pub ds: u16, + pub reserved_2: [u8; 10], + pub st0mm0: [u8; 10], + pub reserved_3: [u8; 6], + pub st1mm1: [u8; 10], + pub reserved_4: [u8; 6], + pub st2mm2: [u8; 10], + pub reserved_5: [u8; 6], + pub st3mm3: [u8; 10], + pub reserved_6: [u8; 6], + pub st4mm4: [u8; 10], + pub reserved_7: [u8; 6], + pub st5mm5: [u8; 10], + pub reserved_8: [u8; 6], + pub st6mm6: [u8; 10], + pub reserved_9: [u8; 6], + pub st7mm7: [u8; 10], + pub reserved_10: [u8; 6], + pub xmm0: [u8; 16], + pub xmm1: [u8; 16], + pub xmm2: [u8; 16], + pub xmm3: [u8; 16], + pub xmm4: [u8; 16], + pub xmm5: [u8; 16], + pub xmm6: [u8; 16], + pub xmm7: [u8; 16], + pub reserved_11: [u8; 14 * 16], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct SystemContextX64 { + // ExceptionData is additional data pushed on the stack by some types of x64 64-bit mode exceptions + pub exception_data: u64, + pub fx_save_state: FxSaveStateX64, + pub dr0: u64, + pub dr1: u64, + pub dr2: u64, + pub dr3: u64, + pub dr6: u64, + pub dr7: u64, + pub cr0: u64, + // Reserved + pub cr1: u64, + pub cr2: u64, + pub cr3: u64, + pub cr4: u64, + pub cr8: u64, + pub rflags: u64, + pub ldtr: u64, + pub tr: u64, + pub gdtr: [u64; 2], + pub idtr: [u64; 2], + pub rip: u64, + pub gs: u64, + pub fs: u64, + pub es: u64, + pub ds: u64, + pub cs: u64, + pub ss: u64, + pub rdi: u64, + pub rsi: u64, + pub rbp: u64, + pub rsp: u64, + pub rbx: u64, + pub rdx: u64, + pub rcx: u64, + pub rax: u64, + pub r8: u64, + pub r9: u64, + pub r10: u64, + pub r11: u64, + pub r12: u64, + pub r13: u64, + pub r14: u64, + pub r15: u64, +} + +// FXSAVE_STATE – FP / MMX / XMM registers +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct FxSaveStateX64 { + pub fcw: u16, + pub fsw: u16, + pub ftw: u16, + pub opcode: u16, + pub rip: u64, + pub data_offset: u64, + pub reserved_1: [u8; 8], + pub st0mm0: [u8; 10], + pub reserved_2: [u8; 6], + pub st1mm1: [u8; 10], + pub reserved_3: [u8; 6], + pub st2mm2: [u8; 10], + pub reserved_4: [u8; 6], + pub st3mm3: [u8; 10], + pub reserved_5: [u8; 6], + pub st4mm4: [u8; 10], + pub reserved_6: [u8; 6], + pub st5mm5: [u8; 10], + pub reserved_7: [u8; 6], + pub st6mm6: [u8; 10], + pub reserved_8: [u8; 6], + pub st7mm7: [u8; 10], + pub reserved_9: [u8; 6], + pub xmm0: [u8; 16], + pub xmm1: [u8; 16], + pub xmm2: [u8; 16], + pub xmm3: [u8; 16], + pub xmm4: [u8; 16], + pub xmm5: [u8; 16], + pub xmm6: [u8; 16], + pub xmm7: [u8; 16], + pub reserved_11: [u8; 14 * 16], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct SystemContextIpf { + pub reserved: u64, + pub r1: u64, + pub r2: u64, + pub r3: u64, + pub r4: u64, + pub r5: u64, + pub r6: u64, + pub r7: u64, + pub r8: u64, + pub r9: u64, + pub r10: u64, + pub r11: u64, + pub r12: u64, + pub r13: u64, + pub r14: u64, + pub r15: u64, + pub r16: u64, + pub r17: u64, + pub r18: u64, + pub r19: u64, + pub r20: u64, + pub r21: u64, + pub r22: u64, + pub r23: u64, + pub r24: u64, + pub r25: u64, + pub r26: u64, + pub r27: u64, + pub r28: u64, + pub r29: u64, + pub r30: u64, + pub r31: u64, + pub f2: [u64; 2], + pub f3: [u64; 2], + pub f4: [u64; 2], + pub f5: [u64; 2], + pub f6: [u64; 2], + pub f7: [u64; 2], + pub f8: [u64; 2], + pub f9: [u64; 2], + pub f10: [u64; 2], + pub f11: [u64; 2], + pub f12: [u64; 2], + pub f13: [u64; 2], + pub f14: [u64; 2], + pub f15: [u64; 2], + pub f16: [u64; 2], + pub f17: [u64; 2], + pub f18: [u64; 2], + pub f19: [u64; 2], + pub f20: [u64; 2], + pub f21: [u64; 2], + pub f22: [u64; 2], + pub f23: [u64; 2], + pub f24: [u64; 2], + pub f25: [u64; 2], + pub f26: [u64; 2], + pub f27: [u64; 2], + pub f28: [u64; 2], + pub f29: [u64; 2], + pub f30: [u64; 2], + pub f31: [u64; 2], + pub pr: u64, + pub b0: u64, + pub b1: u64, + pub b2: u64, + pub b3: u64, + pub b4: u64, + pub b5: u64, + pub b6: u64, + pub b7: u64, + // application registers + pub ar_rsc: u64, + pub ar_bsp: u64, + pub ar_bspstore: u64, + pub ar_rnat: u64, + pub ar_fcr: u64, + pub ar_eflag: u64, + pub ar_csd: u64, + pub ar_ssd: u64, + pub ar_cflg: u64, + pub ar_fsr: u64, + pub ar_fir: u64, + pub ar_fdr: u64, + pub ar_ccv: u64, + pub ar_unat: u64, + pub ar_fpsr: u64, + pub ar_pfs: u64, + pub ar_lc: u64, + pub ar_ec: u64, + // control registers + pub cr_dcr: u64, + pub cr_itm: u64, + pub cr_iva: u64, + pub cr_pta: u64, + pub cr_ipsr: u64, + pub cr_isr: u64, + pub cr_iip: u64, + pub cr_ifa: u64, + pub cr_itir: u64, + pub cr_iipa: u64, + pub cr_ifs: u64, + pub cr_iim: u64, + pub cr_iha: u64, + // debug registers + pub dbr0: u64, + pub dbr1: u64, + pub dbr2: u64, + pub dbr3: u64, + pub dbr4: u64, + pub dbr5: u64, + pub dbr6: u64, + pub dbr7: u64, + pub ibr0: u64, + pub ibr1: u64, + pub ibr2: u64, + pub ibr3: u64, + pub ibr4: u64, + pub ibr5: u64, + pub ibr6: u64, + pub ibr7: u64, + // virtual Registers + pub int_nat: u64, // nat bits for r1-r31 +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct SystemContextArm { + pub r0: u32, + pub r1: u32, + pub r2: u32, + pub r3: u32, + pub r4: u32, + pub r5: u32, + pub r6: u32, + pub r7: u32, + pub r8: u32, + pub r9: u32, + pub r10: u32, + pub r11: u32, + pub r12: u32, + pub sp: u32, + pub lr: u32, + pub pc: u32, + pub cpsr: u32, + pub dfsr: u32, + pub dfar: u32, + pub ifsr: u32, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct SystemContextAArch64 { + // General Purpose Registers + pub x0: u64, + pub x1: u64, + pub x2: u64, + pub x3: u64, + pub x4: u64, + pub x5: u64, + pub x6: u64, + pub x7: u64, + pub x8: u64, + pub x9: u64, + pub x10: u64, + pub x11: u64, + pub x12: u64, + pub x13: u64, + pub x14: u64, + pub x15: u64, + pub x16: u64, + pub x17: u64, + pub x18: u64, + pub x19: u64, + pub x20: u64, + pub x21: u64, + pub x22: u64, + pub x23: u64, + pub x24: u64, + pub x25: u64, + pub x26: u64, + pub x27: u64, + pub x28: u64, + pub fp: u64, // x29 - Frame Pointer + pub lr: u64, // x30 - Link Register + pub sp: u64, // x31 - Stack Pointer + // FP/SIMD Registers + pub v0: [u64; 2], + pub v1: [u64; 2], + pub v2: [u64; 2], + pub v3: [u64; 2], + pub v4: [u64; 2], + pub v5: [u64; 2], + pub v6: [u64; 2], + pub v7: [u64; 2], + pub v8: [u64; 2], + pub v9: [u64; 2], + pub v10: [u64; 2], + pub v11: [u64; 2], + pub v12: [u64; 2], + pub v13: [u64; 2], + pub v14: [u64; 2], + pub v15: [u64; 2], + pub v16: [u64; 2], + pub v17: [u64; 2], + pub v18: [u64; 2], + pub v19: [u64; 2], + pub v20: [u64; 2], + pub v21: [u64; 2], + pub v22: [u64; 2], + pub v23: [u64; 2], + pub v24: [u64; 2], + pub v25: [u64; 2], + pub v26: [u64; 2], + pub v27: [u64; 2], + pub v28: [u64; 2], + pub v29: [u64; 2], + pub v30: [u64; 2], + pub v31: [u64; 2], + pub elr: u64, // Exception Link Register + pub spsr: u64, // Saved Processor Status Register + pub fpsr: u64, // Floating Point Status Register + pub esr: u64, // Exception Syndrome Register + pub far: u64, // Fault Address Register +} + +pub type ExceptionType = isize; + +// EBC Exception types +pub const EXCEPT_EBC_UNDEFINED: ExceptionType = 0; +pub const EXCEPT_EBC_DIVIDE_ERROR: ExceptionType = 1; +pub const EXCEPT_EBC_DEBUG: ExceptionType = 2; +pub const EXCEPT_EBC_BREAKPOINT: ExceptionType = 3; +pub const EXCEPT_EBC_OVERFLOW: ExceptionType = 4; +pub const EXCEPT_EBC_INVALID_OPCODE: ExceptionType = 5; +pub const EXCEPT_EBC_STACK_FAULT: ExceptionType = 6; +pub const EXCEPT_EBC_ALIGNMENT_CHECK: ExceptionType = 7; +pub const EXCEPT_EBC_INSTRUCTION_ENCODING: ExceptionType = 8; +pub const EXCEPT_EBC_BAD_BREAK: ExceptionType = 9; +pub const EXCEPT_EBC_SINGLE_STEP: ExceptionType = 10; + +// IA-32 Exception types +pub const EXCEPT_IA32_DIVIDE_ERROR: ExceptionType = 0; +pub const EXCEPT_IA32_DEBUG: ExceptionType = 1; +pub const EXCEPT_IA32_NMI: ExceptionType = 2; +pub const EXCEPT_IA32_BREAKPOINT: ExceptionType = 3; +pub const EXCEPT_IA32_OVERFLOW: ExceptionType = 4; +pub const EXCEPT_IA32_BOUND: ExceptionType = 5; +pub const EXCEPT_IA32_INVALID_OPCODE: ExceptionType = 6; +pub const EXCEPT_IA32_DOUBLE_FAULT: ExceptionType = 8; +pub const EXCEPT_IA32_INVALID_TSS: ExceptionType = 10; +pub const EXCEPT_IA32_SEG_NOT_PRESENT: ExceptionType = 11; +pub const EXCEPT_IA32_STACK_FAULT: ExceptionType = 12; +pub const EXCEPT_IA32_GP_FAULT: ExceptionType = 13; +pub const EXCEPT_IA32_PAGE_FAULT: ExceptionType = 14; +pub const EXCEPT_IA32_FP_ERROR: ExceptionType = 16; +pub const EXCEPT_IA32_ALIGNMENT_CHECK: ExceptionType = 17; +pub const EXCEPT_IA32_MACHINE_CHECK: ExceptionType = 18; +pub const EXCEPT_IA32_SIMD: ExceptionType = 19; + +// X64 Exception types +pub const EXCEPT_X64_DIVIDE_ERROR: ExceptionType = 0; +pub const EXCEPT_X64_DEBUG: ExceptionType = 1; +pub const EXCEPT_X64_NMI: ExceptionType = 2; +pub const EXCEPT_X64_BREAKPOINT: ExceptionType = 3; +pub const EXCEPT_X64_OVERFLOW: ExceptionType = 4; +pub const EXCEPT_X64_BOUND: ExceptionType = 5; +pub const EXCEPT_X64_INVALID_OPCODE: ExceptionType = 6; +pub const EXCEPT_X64_DOUBLE_FAULT: ExceptionType = 8; +pub const EXCEPT_X64_INVALID_TSS: ExceptionType = 10; +pub const EXCEPT_X64_SEG_NOT_PRESENT: ExceptionType = 11; +pub const EXCEPT_X64_STACK_FAULT: ExceptionType = 12; +pub const EXCEPT_X64_GP_FAULT: ExceptionType = 13; +pub const EXCEPT_X64_PAGE_FAULT: ExceptionType = 14; +pub const EXCEPT_X64_FP_ERROR: ExceptionType = 16; +pub const EXCEPT_X64_ALIGNMENT_CHECK: ExceptionType = 17; +pub const EXCEPT_X64_MACHINE_CHECK: ExceptionType = 18; +pub const EXCEPT_X64_SIMD: ExceptionType = 19; + +// Itanium Processor Family Exception types +pub const EXCEPT_IPF_VHTP_TRANSLATION: ExceptionType = 0; +pub const EXCEPT_IPF_INSTRUCTION_TLB: ExceptionType = 1; +pub const EXCEPT_IPF_DATA_TLB: ExceptionType = 2; +pub const EXCEPT_IPF_ALT_INSTRUCTION_TLB: ExceptionType = 3; +pub const EXCEPT_IPF_ALT_DATA_TLB: ExceptionType = 4; +pub const EXCEPT_IPF_DATA_NESTED_TLB: ExceptionType = 5; +pub const EXCEPT_IPF_INSTRUCTION_KEY_MISSED: ExceptionType = 6; +pub const EXCEPT_IPF_DATA_KEY_MISSED: ExceptionType = 7; +pub const EXCEPT_IPF_DIRTY_BIT: ExceptionType = 8; +pub const EXCEPT_IPF_INSTRUCTION_ACCESS_BIT: ExceptionType = 9; +pub const EXCEPT_IPF_DATA_ACCESS_BIT: ExceptionType = 10; +pub const EXCEPT_IPF_BREAKPOINT: ExceptionType = 11; +pub const EXCEPT_IPF_EXTERNAL_INTERRUPT: ExceptionType = 12; +// 13 - 19 reserved +pub const EXCEPT_IPF_PAGE_NOT_PRESENT: ExceptionType = 20; +pub const EXCEPT_IPF_KEY_PERMISSION: ExceptionType = 21; +pub const EXCEPT_IPF_INSTRUCTION_ACCESS_RIGHTS: ExceptionType = 22; +pub const EXCEPT_IPF_DATA_ACCESS_RIGHTS: ExceptionType = 23; +pub const EXCEPT_IPF_GENERAL_EXCEPTION: ExceptionType = 24; +pub const EXCEPT_IPF_DISABLED_FP_REGISTER: ExceptionType = 25; +pub const EXCEPT_IPF_NAT_CONSUMPTION: ExceptionType = 26; +pub const EXCEPT_IPF_SPECULATION: ExceptionType = 27; +// 28 reserved +pub const EXCEPT_IPF_DEBUG: ExceptionType = 29; +pub const EXCEPT_IPF_UNALIGNED_REFERENCE: ExceptionType = 30; +pub const EXCEPT_IPF_UNSUPPORTED_DATA_REFERENCE: ExceptionType = 31; +pub const EXCEPT_IPF_FP_FAULT: ExceptionType = 32; +pub const EXCEPT_IPF_FP_TRAP: ExceptionType = 33; +pub const EXCEPT_IPF_LOWER_PRIVILEGE_TRANSFER_TRAP: ExceptionType = 34; +pub const EXCEPT_IPF_TAKEN_BRANCH: ExceptionType = 35; +pub const EXCEPT_IPF_SINGLE_STEP: ExceptionType = 36; +// 37 - 44 reserved +pub const EXCEPT_IPF_IA32_EXCEPTION: ExceptionType = 45; +pub const EXCEPT_IPF_IA32_INTERCEPT: ExceptionType = 46; +pub const EXCEPT_IPF_IA32_INTERRUPT: ExceptionType = 47; + +// ARM processor exception types +pub const EXCEPT_ARM_RESET: ExceptionType = 0; +pub const EXCEPT_ARM_UNDEFINED_INSTRUCTION: ExceptionType = 1; +pub const EXCEPT_ARM_SOFTWARE_INTERRUPT: ExceptionType = 2; +pub const EXCEPT_ARM_PREFETCH_ABORT: ExceptionType = 3; +pub const EXCEPT_ARM_DATA_ABORT: ExceptionType = 4; +pub const EXCEPT_ARM_RESERVED: ExceptionType = 5; +pub const EXCEPT_ARM_IRQ: ExceptionType = 6; +pub const EXCEPT_ARM_FIQ: ExceptionType = 7; +pub const MAX_ARM_EXCEPTION: ExceptionType = EXCEPT_ARM_FIQ; + +// AARCH64 processor exception types. +pub const EXCEPT_AARCH64_SYNCHRONOUS_EXCEPTIONS: ExceptionType = 0; +pub const EXCEPT_AARCH64_IRQ: ExceptionType = 1; +pub const EXCEPT_AARCH64_FIQ: ExceptionType = 2; +pub const EXCEPT_AARCH64_SERROR: ExceptionType = 3; +pub const MAX_AARCH64_EXCEPTION: ExceptionType = EXCEPT_AARCH64_SERROR; + +// RISC-V processor exception types. +pub const EXCEPT_RISCV_INST_MISALIGNED: ExceptionType = 0; +pub const EXCEPT_RISCV_INST_ACCESS_FAULT: ExceptionType = 1; +pub const EXCEPT_RISCV_ILLEGAL_INST: ExceptionType = 2; +pub const EXCEPT_RISCV_BREAKPOINT: ExceptionType = 3; +pub const EXCEPT_RISCV_LOAD_ADDRESS_MISALIGNED: ExceptionType = 4; +pub const EXCEPT_RISCV_LOAD_ACCESS_FAULT: ExceptionType = 5; +pub const EXCEPT_RISCV_STORE_AMO_ADDRESS_MISALIGNED: ExceptionType = 6; +pub const EXCEPT_RISCV_STORE_AMO_ACCESS_FAULT: ExceptionType = 7; +pub const EXCEPT_RISCV_ENV_CALL_FROM_UMODE: ExceptionType = 8; +pub const EXCEPT_RISCV_ENV_CALL_FROM_SMODE: ExceptionType = 9; +pub const EXCEPT_RISCV_ENV_CALL_FROM_MMODE: ExceptionType = 11; +pub const EXCEPT_RISCV_INST_PAGE_FAULT: ExceptionType = 12; +pub const EXCEPT_RISCV_LOAD_PAGE_FAULT: ExceptionType = 13; +pub const EXCEPT_RISCV_STORE_AMO_PAGE_FAULT: ExceptionType = 15; + +// RISC-V processor interrupt types. +pub const EXCEPT_RISCV_SUPERVISOR_SOFTWARE_INT: ExceptionType = 1; +pub const EXCEPT_RISCV_MACHINE_SOFTWARE_INT: ExceptionType = 3; +pub const EXCEPT_RISCV_SUPERVISOR_TIMER_INT: ExceptionType = 5; +pub const EXCEPT_RISCV_MACHINE_TIMER_INT: ExceptionType = 7; +pub const EXCEPT_RISCV_SUPERVISOR_EXTERNAL_INT: ExceptionType = 9; +pub const EXCEPT_RISCV_MACHINE_EXTERNAL_INT: ExceptionType = 11; + +pub type GetMaximumProcessorIndex = eficall! {fn( + *mut Protocol, + *mut usize, +) -> crate::base::Status}; + +pub type PeriodicCallback = eficall! {fn(SystemContext)}; + +pub type RegisterPeriodicCallback = eficall! {fn( + *mut Protocol, + usize, + Option, +) -> crate::base::Status}; + +pub type ExceptionCallback = eficall! {fn(ExceptionType, SystemContext)}; + +pub type RegisterExceptionCallback = eficall! {fn( + *mut Protocol, + usize, + Option, + ExceptionType, +) -> crate::base::Status}; + +pub type InvalidateInstructionCache = eficall! {fn( + *mut Protocol, + usize, + *mut core::ffi::c_void, + u64, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub isa: InstructionSetArchitecture, + pub get_maximum_processor_index: GetMaximumProcessorIndex, + pub register_periodic_callback: RegisterPeriodicCallback, + pub register_exception_callback: RegisterExceptionCallback, + pub invalidate_instruction_cache: InvalidateInstructionCache, +} diff --git a/tools/vendor/r-efi/src/protocols/debugport.rs b/tools/vendor/r-efi/src/protocols/debugport.rs new file mode 100644 index 0000000000..a335fd9715 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/debugport.rs @@ -0,0 +1,42 @@ +//! Debug Port Protocol +//! +//! It provides the communication link between the debug agent and the remote host. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xeba4e8d2, + 0x3858, + 0x41ec, + 0xa2, + 0x81, + &[0x26, 0x47, 0xba, 0x96, 0x60, 0xd0], +); + +pub type Reset = eficall! {fn( + *mut Protocol, +) -> *mut crate::base::Status}; + +pub type Write = eficall! {fn( + *mut Protocol, + u32, + *mut usize, + *mut core::ffi::c_void +) -> *mut crate::base::Status}; + +pub type Read = eficall! {fn( + *mut Protocol, + u32, + *mut usize, + *mut core::ffi::c_void +) -> *mut crate::base::Status}; + +pub type Poll = eficall! {fn( + *mut Protocol, +) -> *mut crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub reset: Reset, + pub write: Write, + pub read: Read, + pub poll: Poll, +} diff --git a/tools/vendor/r-efi/src/protocols/decompress.rs b/tools/vendor/r-efi/src/protocols/decompress.rs new file mode 100644 index 0000000000..640369ad04 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/decompress.rs @@ -0,0 +1,37 @@ +//! Decompress Protocol +//! +//! The decompress protocol provides a decompression service that allows a compressed source +//! buffer in memory to be decompressed into a destination buffer in memory. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xd8117cfe, + 0x94a6, + 0x11d4, + 0x9a, + 0x3a, + &[0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d], +); + +pub type ProtocolGetInfo = eficall! {fn( + *mut Protocol, + *mut core::ffi::c_void, + u32, + *mut u32, + *mut u32, +) -> crate::base::Status}; + +pub type ProtocolDecompress = eficall! {fn( + *mut Protocol, + *mut core::ffi::c_void, + u32, + *mut core::ffi::c_void, + u32, + *mut core::ffi::c_void, + u32, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub get_info: ProtocolGetInfo, + pub decompress: ProtocolDecompress, +} diff --git a/tools/vendor/r-efi/src/protocols/device_path.rs b/tools/vendor/r-efi/src/protocols/device_path.rs new file mode 100644 index 0000000000..93455f5085 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/device_path.rs @@ -0,0 +1,82 @@ +//! Device Path Protocol +//! +//! The device path protocol defines how to obtain generic path/location information +//! concerning the phisycal or logical device. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x09576e91, + 0x6d3f, + 0x11d2, + 0x8e, + 0x39, + &[0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b], +); + +pub const TYPE_HARDWARE: u8 = 0x01; +pub const TYPE_ACPI: u8 = 0x02; +pub const TYPE_MESSAGING: u8 = 0x03; +pub const TYPE_MEDIA: u8 = 0x04; +pub const TYPE_BIOS: u8 = 0x05; +pub const TYPE_END: u8 = 0x7f; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Protocol { + pub r#type: u8, + pub sub_type: u8, + pub length: [u8; 2], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct End { + pub header: Protocol, +} + +impl End { + pub const SUBTYPE_INSTANCE: u8 = 0x01; + pub const SUBTYPE_ENTIRE: u8 = 0xff; +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Hardware { + pub header: Protocol, +} + +impl Hardware { + pub const SUBTYPE_PCI: u8 = 0x01; + pub const SUBTYPE_PCCARD: u8 = 0x02; + pub const SUBTYPE_MMAP: u8 = 0x03; + pub const SUBTYPE_VENDOR: u8 = 0x04; + pub const SUBTYPE_CONTROLLER: u8 = 0x05; + pub const SUBTYPE_BMC: u8 = 0x06; +} + +#[repr(C, packed)] +#[derive(Clone, Copy, Debug)] +pub struct HardDriveMedia { + pub header: Protocol, + pub partition_number: u32, + pub partition_start: u64, + pub partition_size: u64, + pub partition_signature: [u8; 16], + pub partition_format: u8, + pub signature_type: u8, +} + +pub struct Media { + pub header: Protocol, +} + +impl Media { + pub const SUBTYPE_HARDDRIVE: u8 = 0x01; + pub const SUBTYPE_CDROM: u8 = 0x02; + pub const SUBTYPE_VENDOR: u8 = 0x03; + pub const SUBTYPE_FILE_PATH: u8 = 0x04; + pub const SUBTYPE_MEDIA_PROTOCOL: u8 = 0x05; + pub const SUBTYPE_PIWG_FIRMWARE_FILE: u8 = 0x06; + pub const SUBTYPE_PIWG_FIRMWARE_VOLUME: u8 = 0x07; + pub const SUBTYPE_RELATIVE_OFFSET_RANGE: u8 = 0x08; + pub const SUBTYPE_RAM_DISK: u8 = 0x09; +} diff --git a/tools/vendor/r-efi/src/protocols/device_path_from_text.rs b/tools/vendor/r-efi/src/protocols/device_path_from_text.rs new file mode 100644 index 0000000000..b9191edd93 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/device_path_from_text.rs @@ -0,0 +1,26 @@ +//! Device Path From Text Protocol +//! +//! Convert text to device paths and device nodes. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x5c99a21, + 0xc70f, + 0x4ad2, + 0x8a, + 0x5f, + &[0x35, 0xdf, 0x33, 0x43, 0xf5, 0x1e], +); + +pub type DevicePathFromTextNode = eficall! {fn( + *const crate::base::Char16, +) -> *mut crate::protocols::device_path::Protocol}; + +pub type DevicePathFromTextPath = eficall! {fn( + *const crate::base::Char16, +) -> *mut crate::protocols::device_path::Protocol}; + +#[repr(C)] +pub struct Protocol { + pub convert_text_to_device_node: DevicePathFromTextNode, + pub convert_text_to_device_path: DevicePathFromTextPath, +} diff --git a/tools/vendor/r-efi/src/protocols/device_path_to_text.rs b/tools/vendor/r-efi/src/protocols/device_path_to_text.rs new file mode 100644 index 0000000000..c0b98337e7 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/device_path_to_text.rs @@ -0,0 +1,30 @@ +//! Device Path to Text Protocol +//! +//! Convert device nodes and paths to text. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x8b843e20, + 0x8132, + 0x4852, + 0x90, + 0xcc, + &[0x55, 0x1a, 0x4e, 0x4a, 0x7f, 0x1c], +); + +pub type DevicePathToTextNode = eficall! {fn( + *mut crate::protocols::device_path::Protocol, + crate::base::Boolean, + crate::base::Boolean, +) -> *mut crate::base::Char16}; + +pub type DevicePathToTextPath = eficall! {fn( + *mut crate::protocols::device_path::Protocol, + crate::base::Boolean, + crate::base::Boolean, +) -> *mut crate::base::Char16}; + +#[repr(C)] +pub struct Protocol { + pub convert_device_node_to_text: DevicePathToTextNode, + pub convert_device_path_to_text: DevicePathToTextPath, +} diff --git a/tools/vendor/r-efi/src/protocols/device_path_utilities.rs b/tools/vendor/r-efi/src/protocols/device_path_utilities.rs new file mode 100644 index 0000000000..d34aea819c --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/device_path_utilities.rs @@ -0,0 +1,63 @@ +//! Device Path Utilities Protocol +//! +//! The device-path utilities protocol provides common utilities for creating and manipulating +//! device paths and device nodes. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x379be4e, + 0xd706, + 0x437d, + 0xb0, + 0x37, + &[0xed, 0xb8, 0x2f, 0xb7, 0x72, 0xa4], +); + +pub type ProtocolGetDevicePathSize = eficall! {fn( + *const crate::protocols::device_path::Protocol, +) -> usize}; + +pub type ProtocolDuplicateDevicePath = eficall! {fn( + *const crate::protocols::device_path::Protocol, +) -> *mut crate::protocols::device_path::Protocol}; + +pub type ProtocolAppendDevicePath = eficall! {fn( + *const crate::protocols::device_path::Protocol, + *const crate::protocols::device_path::Protocol, +) -> *mut crate::protocols::device_path::Protocol}; + +pub type ProtocolAppendDeviceNode = eficall! {fn( + *const crate::protocols::device_path::Protocol, + *const crate::protocols::device_path::Protocol, +) -> *mut crate::protocols::device_path::Protocol}; + +pub type ProtocolAppendDevicePathInstance = eficall! {fn( + *const crate::protocols::device_path::Protocol, + *const crate::protocols::device_path::Protocol, +) -> *mut crate::protocols::device_path::Protocol}; + +pub type ProtocolGetNextDevicePathInstance = eficall! {fn( + *mut *mut crate::protocols::device_path::Protocol, + *mut usize, +) -> *mut crate::protocols::device_path::Protocol}; + +pub type ProtocolIsDevicePathMultiInstance = eficall! {fn( + *const crate::protocols::device_path::Protocol, +) -> crate::base::Boolean}; + +pub type ProtocolCreateDeviceNode = eficall! {fn( + u8, + u8, + u16, +) -> *mut crate::protocols::device_path::Protocol}; + +#[repr(C)] +pub struct Protocol { + pub get_device_path_size: ProtocolGetDevicePathSize, + pub duplicate_device_path: ProtocolDuplicateDevicePath, + pub append_device_path: ProtocolAppendDevicePath, + pub append_device_node: ProtocolAppendDeviceNode, + pub append_device_path_instance: ProtocolAppendDevicePathInstance, + pub get_next_device_path_instance: ProtocolGetNextDevicePathInstance, + pub is_device_path_multi_instance: ProtocolIsDevicePathMultiInstance, + pub create_device_node: ProtocolCreateDeviceNode, +} diff --git a/tools/vendor/r-efi/src/protocols/disk_io.rs b/tools/vendor/r-efi/src/protocols/disk_io.rs new file mode 100644 index 0000000000..dd3093f130 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/disk_io.rs @@ -0,0 +1,40 @@ +//! Disk I/O Protocol +//! +//! Abstracts block accesses of the Block I/O protocol to a more general offset-length protocol. +//! Firmware is responsible for adding this protocol to any Block I/O interface that appears +//! in the system that does not already have a Disk I/O protocol. File systems and other disk +//! access code utilize the Disk I/O protocol. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xce345171, + 0xba0b, + 0x11d2, + 0x8e, + 0x4f, + &[0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b], +); + +pub const REVISION: u64 = 0x0000000000010000u64; + +pub type ProtocolReadDisk = eficall! {fn( + *mut Protocol, + u32, + u64, + usize, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type ProtocolWriteDisk = eficall! {fn( + *mut Protocol, + u32, + u64, + usize, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub revision: u64, + pub read_disk: ProtocolReadDisk, + pub write_disk: ProtocolWriteDisk, +} diff --git a/tools/vendor/r-efi/src/protocols/disk_io2.rs b/tools/vendor/r-efi/src/protocols/disk_io2.rs new file mode 100644 index 0000000000..777ed4b449 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/disk_io2.rs @@ -0,0 +1,58 @@ +//! Disk I/O 2 Protocol +//! +//! Extends the Disk I/O protocol interface to enable non-blocking / +//! asynchronous byte-oriented disk operation. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x151c8eae, + 0x7f2c, + 0x472c, + 0x9e, + 0x54, + &[0x98, 0x28, 0x19, 0x4f, 0x6a, 0x88], +); + +pub const REVISION: u64 = 0x0000000000020000u64; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Token { + event: crate::base::Event, + transaction_status: crate::base::Status, +} + +pub type ProtocolCancel = eficall! {fn( + *mut Protocol, +) -> crate::base::Status}; + +pub type ProtocolReadDiskEx = eficall! {fn( + *mut Protocol, + u32, + u64, + *mut Token, + usize, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type ProtocolWriteDiskEx = eficall! {fn( + *mut Protocol, + u32, + u64, + *mut Token, + usize, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type ProtocolFlushDiskEx = eficall! {fn( + *mut Protocol, + *mut Token, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub revision: u64, + pub cancel: ProtocolCancel, + pub read_disk_ex: ProtocolReadDiskEx, + pub write_disk_ex: ProtocolWriteDiskEx, + pub flush_disk_ex: ProtocolFlushDiskEx, +} diff --git a/tools/vendor/r-efi/src/protocols/driver_binding.rs b/tools/vendor/r-efi/src/protocols/driver_binding.rs new file mode 100644 index 0000000000..7ba2ee3283 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/driver_binding.rs @@ -0,0 +1,42 @@ +//! Driver Binding Protocol +//! +//! Provides the services required to determine if a driver supports a given controller. If +//! a controller is supported, then it also provides routines to start and stop the controller. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x18a031ab, + 0xb443, + 0x4d1a, + 0xa5, + 0xc0, + &[0x0c, 0x09, 0x26, 0x1e, 0x9f, 0x71], +); + +pub type ProtocolSupported = eficall! {fn( + *mut Protocol, + crate::base::Handle, + *mut crate::protocols::device_path::Protocol, +) -> crate::base::Status}; + +pub type ProtocolStart = eficall! {fn( + *mut Protocol, + crate::base::Handle, + *mut crate::protocols::device_path::Protocol, +) -> crate::base::Status}; + +pub type ProtocolStop = eficall! {fn( + *mut Protocol, + crate::base::Handle, + usize, + *mut crate::base::Handle, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub supported: ProtocolSupported, + pub start: ProtocolStart, + pub stop: ProtocolStop, + pub version: u32, + pub image_handle: crate::base::Handle, + pub driver_binding_handle: crate::base::Handle, +} diff --git a/tools/vendor/r-efi/src/protocols/driver_diagnostics2.rs b/tools/vendor/r-efi/src/protocols/driver_diagnostics2.rs new file mode 100644 index 0000000000..dff5bd446f --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/driver_diagnostics2.rs @@ -0,0 +1,38 @@ +//! Driver Diagnostics Protocol +//! +//! Defined in UEFI Specification, Section 11.4 +//! Used to perform diagnostics on a controller that a UEFI driver is managing. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x4d330321, + 0x025f, + 0x4aac, + 0x90, + 0xd8, + &[0x5e, 0xd9, 0x00, 0x17, 0x3b, 0x63], +); + +pub type Type = u32; + +pub const TYPE_STANDARD: Type = 0; +pub const TYPE_EXTENDED: Type = 1; +pub const TYPE_MANUFACTURING: Type = 2; +pub const TYPE_CANCEL: Type = 3; +pub const TYPE_MAXIMUM: Type = 4; + +pub type RunDiagnostics = eficall! {fn( + *mut Protocol, + crate::base::Handle, + crate::base::Handle, + Type, + *mut crate::base::Char8, + *mut *mut crate::base::Guid, + *mut usize, + *mut *mut crate::base::Char16, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub run_diagnostics: RunDiagnostics, + pub supported_languages: *mut crate::base::Char8, +} diff --git a/tools/vendor/r-efi/src/protocols/driver_family_override.rs b/tools/vendor/r-efi/src/protocols/driver_family_override.rs new file mode 100644 index 0000000000..d1924764a8 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/driver_family_override.rs @@ -0,0 +1,23 @@ +//! Driver Family Override Protocol +//! +//! When installed, the Driver Family Override Protocol informs the UEFI Boot +//! Service `ConnectController()` that this driver is higher priority than the +//! list of drivers returned by the Bus Specific Driver Override Protocol. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xb1ee129e, + 0xda36, + 0x4181, + 0x91, + 0xf8, + &[0x04, 0xa4, 0x92, 0x37, 0x66, 0xa7], +); + +pub type ProtocolGetVersion = eficall! {fn( + *mut Protocol, +) -> u32}; + +#[repr(C)] +pub struct Protocol { + pub get_version: ProtocolGetVersion, +} diff --git a/tools/vendor/r-efi/src/protocols/file.rs b/tools/vendor/r-efi/src/protocols/file.rs new file mode 100644 index 0000000000..4a66ce2949 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/file.rs @@ -0,0 +1,183 @@ +//! File Protocol +//! +//! Provides an interface to interact with both files and directories. This protocol is typically +//! obtained via an EFI_SIMPLE_FILE_SYSTEM protocol or via another EFI_FILE_PROTOCOL. + +pub const REVISION: u64 = 0x0000_0000_0001_0000u64; +pub const REVISION2: u64 = 0x0000_0000_0002_0000u64; +pub const LATEST_REVISION: u64 = REVISION2; + +pub const MODE_READ: u64 = 0x0000000000000001u64; +pub const MODE_WRITE: u64 = 0x0000000000000002u64; +pub const MODE_CREATE: u64 = 0x8000000000000000u64; + +pub const READ_ONLY: u64 = 0x0000000000000001u64; +pub const HIDDEN: u64 = 0x0000000000000002u64; +pub const SYSTEM: u64 = 0x0000000000000004u64; +pub const RESERVED: u64 = 0x0000000000000008u64; +pub const DIRECTORY: u64 = 0x0000000000000010u64; +pub const ARCHIVE: u64 = 0x0000000000000020u64; +pub const VALID_ATTR: u64 = 0x0000000000000037u64; + +pub const INFO_ID: crate::base::Guid = crate::base::Guid::from_fields( + 0x09576e92, + 0x6d3f, + 0x11d2, + 0x8e, + 0x39, + &[0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b], +); +pub const SYSTEM_INFO_ID: crate::base::Guid = crate::base::Guid::from_fields( + 0x09576e93, + 0x6d3f, + 0x11d2, + 0x8e, + 0x39, + &[0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b], +); +pub const SYSTEM_VOLUME_LABEL_ID: crate::base::Guid = crate::base::Guid::from_fields( + 0xdb47d7d3, + 0xfe81, + 0x11d3, + 0x9a, + 0x35, + &[0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d], +); + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IoToken { + pub event: crate::base::Event, + pub status: crate::base::Status, + pub buffer_size: usize, + pub buffer: *mut core::ffi::c_void, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Info { + pub size: u64, + pub file_size: u64, + pub physical_size: u64, + pub create_time: crate::system::Time, + pub last_access_time: crate::system::Time, + pub modification_time: crate::system::Time, + pub attribute: u64, + pub file_name: [crate::base::Char16; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct SystemInfo { + pub size: u64, + pub read_only: crate::base::Boolean, + pub volume_size: u64, + pub free_space: u64, + pub block_size: u32, + pub volume_label: [crate::base::Char16; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct SystemVolumeLabel { + pub volume_label: [crate::base::Char16; N], +} + +pub type ProtocolOpen = eficall! {fn( + *mut Protocol, + *mut *mut Protocol, + *mut crate::base::Char16, + u64, + u64, +) -> crate::base::Status}; + +pub type ProtocolClose = eficall! {fn( + *mut Protocol, +) -> crate::base::Status}; + +pub type ProtocolDelete = eficall! {fn( + *mut Protocol, +) -> crate::base::Status}; + +pub type ProtocolRead = eficall! {fn( + *mut Protocol, + *mut usize, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type ProtocolWrite = eficall! {fn( + *mut Protocol, + *mut usize, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type ProtocolGetPosition = eficall! {fn( + *mut Protocol, + *mut u64, +) -> crate::base::Status}; + +pub type ProtocolSetPosition = eficall! {fn( + *mut Protocol, + u64, +) -> crate::base::Status}; + +pub type ProtocolGetInfo = eficall! {fn( + *mut Protocol, + *mut crate::base::Guid, + *mut usize, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type ProtocolSetInfo = eficall! {fn( + *mut Protocol, + *mut crate::base::Guid, + usize, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type ProtocolFlush = eficall! {fn( + *mut Protocol, +) -> crate::base::Status}; + +pub type ProtocolOpenEx = eficall! {fn( + *mut Protocol, + *mut *mut Protocol, + *mut crate::base::Char16, + u64, + u64, + *mut IoToken, +) -> crate::base::Status}; + +pub type ProtocolReadEx = eficall! {fn( + *mut Protocol, + *mut IoToken, +) -> crate::base::Status}; + +pub type ProtocolWriteEx = eficall! {fn( + *mut Protocol, + *mut IoToken, +) -> crate::base::Status}; + +pub type ProtocolFlushEx = eficall! {fn( + *mut Protocol, + *mut IoToken, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub revision: u64, + pub open: ProtocolOpen, + pub close: ProtocolClose, + pub delete: ProtocolDelete, + pub read: ProtocolRead, + pub write: ProtocolWrite, + pub get_position: ProtocolGetPosition, + pub set_position: ProtocolSetPosition, + pub get_info: ProtocolGetInfo, + pub set_info: ProtocolSetInfo, + pub flush: ProtocolFlush, + pub open_ex: ProtocolOpenEx, + pub read_ex: ProtocolReadEx, + pub write_ex: ProtocolWriteEx, + pub flush_ex: ProtocolFlushEx, +} diff --git a/tools/vendor/r-efi/src/protocols/graphics_output.rs b/tools/vendor/r-efi/src/protocols/graphics_output.rs new file mode 100644 index 0000000000..a6a4438b8e --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/graphics_output.rs @@ -0,0 +1,103 @@ +//! Graphics Output Protocol +//! +//! Provides means to configure graphics hardware and get access to +//! framebuffers. Replaces the old UGA interface from EFI with a +//! VGA-independent API. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x9042a9de, + 0x23dc, + 0x4a38, + 0x96, + 0xfb, + &[0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a], +); + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct PixelBitmask { + pub red_mask: u32, + pub green_mask: u32, + pub blue_mask: u32, + pub reserved_mask: u32, +} + +pub type GraphicsPixelFormat = u32; + +pub const PIXEL_RED_GREEN_BLUE_RESERVED_8_BIT_PER_COLOR: GraphicsPixelFormat = 0x00000000; +pub const PIXEL_BLUE_GREEN_RED_RESERVED_8_BIT_PER_COLOR: GraphicsPixelFormat = 0x00000001; +pub const PIXEL_BIT_MASK: GraphicsPixelFormat = 0x00000002; +pub const PIXEL_BLT_ONLY: GraphicsPixelFormat = 0x00000003; +pub const PIXEL_FORMAT_MAX: GraphicsPixelFormat = 0x00000004; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ModeInformation { + pub version: u32, + pub horizontal_resolution: u32, + pub vertical_resolution: u32, + pub pixel_format: GraphicsPixelFormat, + pub pixel_information: PixelBitmask, + pub pixels_per_scan_line: u32, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Mode { + pub max_mode: u32, + pub mode: u32, + pub info: *mut ModeInformation, + pub size_of_info: usize, + pub frame_buffer_base: crate::base::PhysicalAddress, + pub frame_buffer_size: usize, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct BltPixel { + pub blue: u8, + pub green: u8, + pub red: u8, + pub reserved: u8, +} + +pub type BltOperation = u32; + +pub const BLT_VIDEO_FILL: BltOperation = 0x00000000; +pub const BLT_VIDEO_TO_BLT_BUFFER: BltOperation = 0x00000001; +pub const BLT_BUFFER_TO_VIDEO: BltOperation = 0x00000002; +pub const BLT_VIDEO_TO_VIDEO: BltOperation = 0x00000003; +pub const BLT_OPERATION_MAX: BltOperation = 0x00000004; + +pub type ProtocolQueryMode = eficall! {fn( + *mut Protocol, + u32, + *mut usize, + *mut *mut ModeInformation, +) -> crate::base::Status}; + +pub type ProtocolSetMode = eficall! {fn( + *mut Protocol, + u32, +) -> crate::base::Status}; + +pub type ProtocolBlt = eficall! {fn( + *mut Protocol, + *mut BltPixel, + BltOperation, + usize, + usize, + usize, + usize, + usize, + usize, + usize, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub query_mode: ProtocolQueryMode, + pub set_mode: ProtocolSetMode, + pub blt: ProtocolBlt, + pub mode: *mut Mode, +} diff --git a/tools/vendor/r-efi/src/protocols/hii_database.rs b/tools/vendor/r-efi/src/protocols/hii_database.rs new file mode 100644 index 0000000000..579c43cf25 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/hii_database.rs @@ -0,0 +1,299 @@ +//! Human Interface Infrastructure (HII) Protocol +//! +//! Database manager for HII-related data structures. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xef9fc172, + 0xa1b2, + 0x4693, + 0xb3, + 0x27, + &[0x6d, 0x32, 0xfc, 0x41, 0x60, 0x42], +); + +pub const SET_KEYBOARD_LAYOUT_EVENT_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x14982a4f, + 0xb0ed, + 0x45b8, + 0xa8, + 0x11, + &[0x5a, 0x7a, 0x9b, 0xc2, 0x32, 0xdf], +); + +pub type ProtocolNewPackageList = eficall! {fn( + *const Protocol, + *const crate::hii::PackageListHeader, + crate::base::Handle, + *mut crate::hii::Handle, +) -> crate::base::Status}; + +pub type ProtocolRemovePackageList = eficall! {fn( + *const Protocol, + crate::hii::Handle, +) -> crate::base::Status}; + +pub type ProtocolUpdatePackageList = eficall! {fn( + *const Protocol, + crate::hii::Handle, + *const crate::hii::PackageListHeader, +) -> crate::base::Status}; + +pub type ProtocolListPackageLists = eficall! {fn( + *const Protocol, + u8, + *const crate::base::Guid, + *mut usize, + *mut crate::hii::Handle, +) -> crate::base::Status}; + +pub type ProtocolExportPackageLists = eficall! {fn( + *const Protocol, + crate::hii::Handle, + *mut usize, + *mut crate::hii::PackageListHeader, +) -> crate::base::Status}; + +pub type ProtocolRegisterPackageNotify = eficall! {fn( + *const Protocol, + u8, + *const crate::base::Guid, + Notify, + NotifyType, + *mut crate::base::Handle, +) -> crate::base::Status}; + +pub type ProtocolUnregisterPackageNotify = eficall! {fn( + *const Protocol, + crate::base::Handle, +) -> crate::base::Status}; + +pub type ProtocolFindKeyboardLayouts = eficall! {fn( + *const Protocol, + *mut u16, + *mut crate::base::Guid, +) -> crate::base::Status}; + +pub type ProtocolGetKeyboardLayout = eficall! {fn( + *const Protocol, + *const crate::base::Guid, + *mut u16, + *mut KeyboardLayout, +) -> crate::base::Status}; + +pub type ProtocolSetKeyboardLayout = eficall! {fn( + *const Protocol, + *mut crate::base::Guid, +) -> crate::base::Status}; + +pub type ProtocolGetPackageListHandle = eficall! {fn( + *const Protocol, + crate::hii::Handle, + *mut crate::base::Handle, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub new_package_list: ProtocolNewPackageList, + pub remove_package_list: ProtocolRemovePackageList, + pub update_package_list: ProtocolUpdatePackageList, + pub list_package_lists: ProtocolListPackageLists, + pub export_package_lists: ProtocolExportPackageLists, + pub register_package_notify: ProtocolRegisterPackageNotify, + pub unregister_package_notify: ProtocolUnregisterPackageNotify, + pub find_keyboard_layouts: ProtocolFindKeyboardLayouts, + pub get_keyboard_layout: ProtocolGetKeyboardLayout, + pub set_keyboard_layout: ProtocolSetKeyboardLayout, + pub get_package_list_handle: ProtocolGetPackageListHandle, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct KeyboardLayout { + pub layout_length: u16, + pub guid: crate::base::Guid, + pub layout_descriptor_string_offset: u32, + pub descriptor_count: u8, + pub descriptors: [KeyDescriptor; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct KeyDescriptor { + pub key: Key, + pub unicode: crate::base::Char16, + pub shifted_unicode: crate::base::Char16, + pub alt_gr_unicode: crate::base::Char16, + pub shifted_alt_gr_unicode: crate::base::Char16, + pub modifier: u16, + pub affected_attribute: u16, +} + +pub const AFFECTED_BY_STANDARD_SHIFT: u16 = 0x0001; +pub const AFFECTED_BY_CAPS_LOCK: u16 = 0x0002; +pub const AFFECTED_BY_NUM_LOCK: u16 = 0x0004; + +pub type Key = u32; + +pub const EFI_KEY_LCTRL: Key = 0x00000000; +pub const EFI_KEY_A0: Key = 0x00000001; +pub const EFI_KEY_LALT: Key = 0x00000002; +pub const EFI_KEY_SPACE_BAR: Key = 0x00000003; +pub const EFI_KEY_A2: Key = 0x00000004; +pub const EFI_KEY_A3: Key = 0x00000005; +pub const EFI_KEY_A4: Key = 0x00000006; +pub const EFI_KEY_RCTRL: Key = 0x00000007; +pub const EFI_KEY_LEFT_ARROW: Key = 0x00000008; +pub const EFI_KEY_DOWN_ARROW: Key = 0x00000009; +pub const EFI_KEY_RIGHT_ARROW: Key = 0x0000000a; +pub const EFI_KEY_ZERO: Key = 0x0000000b; +pub const EFI_KEY_PERIOD: Key = 0x0000000c; +pub const EFI_KEY_ENTER: Key = 0x0000000d; +pub const EFI_KEY_LSHIFT: Key = 0x0000000e; +pub const EFI_KEY_B0: Key = 0x0000000f; +pub const EFI_KEY_B1: Key = 0x00000010; +pub const EFI_KEY_B2: Key = 0x00000011; +pub const EFI_KEY_B3: Key = 0x00000012; +pub const EFI_KEY_B4: Key = 0x00000013; +pub const EFI_KEY_B5: Key = 0x00000014; +pub const EFI_KEY_B6: Key = 0x00000015; +pub const EFI_KEY_B7: Key = 0x00000016; +pub const EFI_KEY_B8: Key = 0x00000017; +pub const EFI_KEY_B9: Key = 0x00000018; +pub const EFI_KEY_B10: Key = 0x00000019; +pub const EFI_KEY_RSHIFT: Key = 0x0000001a; +pub const EFI_KEY_UP_ARROW: Key = 0x0000001b; +pub const EFI_KEY_ONE: Key = 0x0000001c; +pub const EFI_KEY_TWO: Key = 0x0000001d; +pub const EFI_KEY_THREE: Key = 0x0000001e; +pub const EFI_KEY_CAPS_LOCK: Key = 0x0000001f; +pub const EFI_KEY_C1: Key = 0x00000020; +pub const EFI_KEY_C2: Key = 0x00000021; +pub const EFI_KEY_C3: Key = 0x00000022; +pub const EFI_KEY_C4: Key = 0x00000023; +pub const EFI_KEY_C5: Key = 0x00000024; +pub const EFI_KEY_C6: Key = 0x00000025; +pub const EFI_KEY_C7: Key = 0x00000026; +pub const EFI_KEY_C8: Key = 0x00000027; +pub const EFI_KEY_C9: Key = 0x00000028; +pub const EFI_KEY_C10: Key = 0x00000029; +pub const EFI_KEY_C11: Key = 0x0000002a; +pub const EFI_KEY_C12: Key = 0x0000002b; +pub const EFI_KEY_FOUR: Key = 0x0000002c; +pub const EFI_KEY_FIVE: Key = 0x0000002d; +pub const EFI_KEY_SIX: Key = 0x0000002e; +pub const EFI_KEY_PLUS: Key = 0x0000002f; +pub const EFI_KEY_TAB: Key = 0x00000030; +pub const EFI_KEY_D1: Key = 0x00000031; +pub const EFI_KEY_D2: Key = 0x00000032; +pub const EFI_KEY_D3: Key = 0x00000033; +pub const EFI_KEY_D4: Key = 0x00000034; +pub const EFI_KEY_D5: Key = 0x00000035; +pub const EFI_KEY_D6: Key = 0x00000036; +pub const EFI_KEY_D7: Key = 0x00000037; +pub const EFI_KEY_D8: Key = 0x00000038; +pub const EFI_KEY_D9: Key = 0x00000039; +pub const EFI_KEY_D10: Key = 0x0000003a; +pub const EFI_KEY_D11: Key = 0x0000003b; +pub const EFI_KEY_D12: Key = 0x0000003c; +pub const EFI_KEY_D13: Key = 0x0000003d; +pub const EFI_KEY_DEL: Key = 0x0000003e; +pub const EFI_KEY_END: Key = 0x0000003f; +pub const EFI_KEY_PGDN: Key = 0x00000040; +pub const EFI_KEY_SEVEN: Key = 0x00000041; +pub const EFI_KEY_EIGHT: Key = 0x00000042; +pub const EFI_KEY_NINE: Key = 0x00000043; +pub const EFI_KEY_E0: Key = 0x00000044; +pub const EFI_KEY_E1: Key = 0x00000045; +pub const EFI_KEY_E2: Key = 0x00000046; +pub const EFI_KEY_E3: Key = 0x00000047; +pub const EFI_KEY_E4: Key = 0x00000048; +pub const EFI_KEY_E5: Key = 0x00000049; +pub const EFI_KEY_E6: Key = 0x0000004a; +pub const EFI_KEY_E7: Key = 0x0000004b; +pub const EFI_KEY_E8: Key = 0x0000004c; +pub const EFI_KEY_E9: Key = 0x0000004d; +pub const EFI_KEY_E10: Key = 0x0000004e; +pub const EFI_KEY_E11: Key = 0x0000004f; +pub const EFI_KEY_E12: Key = 0x00000050; +pub const EFI_KEY_BACK_SPACE: Key = 0x00000051; +pub const EFI_KEY_INS: Key = 0x00000052; +pub const EFI_KEY_HOME: Key = 0x00000053; +pub const EFI_KEY_PGUP: Key = 0x00000054; +pub const EFI_KEY_NLCK: Key = 0x00000055; +pub const EFI_KEY_SLASH: Key = 0x00000056; +pub const EFI_KEY_ASTERISK: Key = 0x00000057; +pub const EFI_KEY_MINUS: Key = 0x00000058; +pub const EFI_KEY_ESC: Key = 0x00000059; +pub const EFI_KEY_F1: Key = 0x0000005a; +pub const EFI_KEY_F2: Key = 0x0000005b; +pub const EFI_KEY_F3: Key = 0x0000005c; +pub const EFI_KEY_F4: Key = 0x0000005d; +pub const EFI_KEY_F5: Key = 0x0000005e; +pub const EFI_KEY_F6: Key = 0x0000005f; +pub const EFI_KEY_F7: Key = 0x00000060; +pub const EFI_KEY_F8: Key = 0x00000061; +pub const EFI_KEY_F9: Key = 0x00000062; +pub const EFI_KEY_F10: Key = 0x00000063; +pub const EFI_KEY_F11: Key = 0x00000064; +pub const EFI_KEY_F12: Key = 0x00000065; +pub const EFI_KEY_PRINT: Key = 0x00000066; +pub const EFI_KEY_SLCK: Key = 0x00000067; +pub const EFI_KEY_PAUSE: Key = 0x00000068; + +pub const NULL_MODIFIER: u16 = 0x0000; +pub const LEFT_CONTROL_MODIFIER: u16 = 0x0001; +pub const RIGHT_CONTROL_MODIFIER: u16 = 0x0002; +pub const LEFT_ALT_MODIFIER: u16 = 0x0003; +pub const RIGHT_ALT_MODIFIER: u16 = 0x0004; +pub const ALT_GR_MODIFIER: u16 = 0x0005; +pub const INSERT_MODIFIER: u16 = 0x0006; +pub const DELETE_MODIFIER: u16 = 0x0007; +pub const PAGE_DOWN_MODIFIER: u16 = 0x0008; +pub const PAGE_UP_MODIFIER: u16 = 0x0009; +pub const HOME_MODIFIER: u16 = 0x000A; +pub const END_MODIFIER: u16 = 0x000B; +pub const LEFT_SHIFT_MODIFIER: u16 = 0x000C; +pub const RIGHT_SHIFT_MODIFIER: u16 = 0x000D; +pub const CAPS_LOCK_MODIFIER: u16 = 0x000E; +pub const NUM_LOCK_MODIFIER: u16 = 0x000F; +pub const LEFT_ARROW_MODIFIER: u16 = 0x0010; +pub const RIGHT_ARROW_MODIFIER: u16 = 0x0011; +pub const DOWN_ARROW_MODIFIER: u16 = 0x0012; +pub const UP_ARROW_MODIFIER: u16 = 0x0013; +pub const NS_KEY_MODIFIER: u16 = 0x0014; +pub const NS_KEY_DEPENDENCY_MODIFIER: u16 = 0x0015; +pub const FUNCTION_KEY_ONE_MODIFIER: u16 = 0x0016; +pub const FUNCTION_KEY_TWO_MODIFIER: u16 = 0x0017; +pub const FUNCTION_KEY_THREE_MODIFIER: u16 = 0x0018; +pub const FUNCTION_KEY_FOUR_MODIFIER: u16 = 0x0019; +pub const FUNCTION_KEY_FIVE_MODIFIER: u16 = 0x001A; +pub const FUNCTION_KEY_SIX_MODIFIER: u16 = 0x001B; +pub const FUNCTION_KEY_SEVEN_MODIFIER: u16 = 0x001C; +pub const FUNCTION_KEY_EIGHT_MODIFIER: u16 = 0x001D; +pub const FUNCTION_KEY_NINE_MODIFIER: u16 = 0x001E; +pub const FUNCTION_KEY_TEN_MODIFIER: u16 = 0x001F; +pub const FUNCTION_KEY_ELEVEN_MODIFIER: u16 = 0x0020; +pub const FUNCTION_KEY_TWELVE_MODIFIER: u16 = 0x0021; +pub const PRINT_MODIFIER: u16 = 0x0022; +pub const SYS_REQUEST_MODIFIER: u16 = 0x0023; +pub const SCROLL_LOCK_MODIFIER: u16 = 0x0024; +pub const PAUSE_MODIFIER: u16 = 0x0025; +pub const BREAK_MODIFIER: u16 = 0x0026; +pub const LEFT_LOGO_MODIFIER: u16 = 0x0027; +pub const RIGHT_LOGO_MODIFIER: u16 = 0x0028; +pub const MENU_MODIFIER: u16 = 0x0029; + +pub type Notify = eficall! {fn( + u8, + *const crate::base::Guid, + *const crate::hii::PackageHeader, + crate::hii::Handle, + NotifyType, +) -> crate::base::Status}; + +pub type NotifyType = usize; + +pub const NOTIFY_NEW_PACK: NotifyType = 0x00000001; +pub const NOTIFY_REMOVE_PACK: NotifyType = 0x00000002; +pub const NOTIFY_EXPORT_PACK: NotifyType = 0x00000004; +pub const NOTIFY_ADD_PACK: NotifyType = 0x00000008; diff --git a/tools/vendor/r-efi/src/protocols/hii_font.rs b/tools/vendor/r-efi/src/protocols/hii_font.rs new file mode 100644 index 0000000000..4ca7bf7b37 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/hii_font.rs @@ -0,0 +1,87 @@ +//! HII Font Protocol + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xe9ca4775, + 0x8657, + 0x47fc, + 0x97, + 0xe7, + &[0x7e, 0xd6, 0x5a, 0x08, 0x43, 0x24], +); + +pub type ProtocolStringToImage = eficall! {fn( + *const Protocol, + OutFlags, + String, + *const super::hii_font_ex::DisplayInfo, + *mut *mut super::hii_font_ex::ImageOutput, + usize, + usize, + *mut *mut RowInfo, + *mut usize, + *mut usize, +) -> crate::base::Status}; + +pub type ProtocolStringIdToImage = eficall! {fn( + *const Protocol, + OutFlags, + crate::hii::Handle, + crate::hii::StringId, + *const crate::base::Char8, + *const super::hii_font_ex::DisplayInfo, + *mut *mut super::hii_font_ex::ImageOutput, + usize, + usize, + *mut *mut RowInfo, + *mut usize, + *mut usize, +) -> crate::base::Status}; + +pub type ProtocolGetGlyph = eficall! {fn( + *const Protocol, + crate::base::Char16, + *const super::hii_font_ex::DisplayInfo, + *mut *mut super::hii_font_ex::ImageOutput, + *mut usize, +) -> crate::base::Status}; + +pub type ProtocolGetFontInfo = eficall! {fn( + *const Protocol, + *mut Handle, + *const super::hii_font_ex::DisplayInfo, + *mut *mut super::hii_font_ex::DisplayInfo, + String, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub string_to_image: ProtocolStringToImage, + pub string_id_to_image: ProtocolStringIdToImage, + pub get_glyph: ProtocolGetGlyph, + pub get_font_info: ProtocolGetFontInfo, +} + +pub type OutFlags = u32; + +pub const OUT_FLAG_CLIP: OutFlags = 0x00000001; +pub const OUT_FLAG_WRAP: OutFlags = 0x00000002; +pub const OUT_FLAG_CLIP_CLEAN_Y: OutFlags = 0x00000004; +pub const OUT_FLAG_CLIP_CLEAN_X: OutFlags = 0x00000008; +pub const OUT_FLAG_TRANSPARENT: OutFlags = 0x00000010; +pub const IGNORE_IF_NO_GLYPH: OutFlags = 0x00000020; +pub const IGNORE_LINE_BREAK: OutFlags = 0x00000040; +pub const DIRECT_TO_SCREEN: OutFlags = 0x00000080; + +pub type String = *mut crate::base::Char16; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct RowInfo { + pub start_index: usize, + pub end_index: usize, + pub line_height: usize, + pub line_width: usize, + pub baseline_offset: usize, +} + +pub type Handle = *mut core::ffi::c_void; diff --git a/tools/vendor/r-efi/src/protocols/hii_font_ex.rs b/tools/vendor/r-efi/src/protocols/hii_font_ex.rs new file mode 100644 index 0000000000..92466986f2 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/hii_font_ex.rs @@ -0,0 +1,107 @@ +//! HII Font Ex Protocol + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x849e6875, + 0xdb35, + 0x4df8, + 0xb4, + 0x1e, + &[0xc8, 0xf3, 0x37, 0x18, 0x07, 0x3f], +); + +pub type ProtocolStringToImageEx = eficall! {fn( + *const Protocol, + super::hii_font::OutFlags, + super::hii_font::String, + *const DisplayInfo, + *mut *mut ImageOutput, + usize, + usize, + *mut *mut super::hii_font::RowInfo, + *mut usize, + *mut usize, +) -> crate::base::Status}; + +pub type ProtocolStringIdToImageEx = eficall! {fn( + *const Protocol, + super::hii_font::OutFlags, + crate::hii::Handle, + crate::hii::StringId, + *const crate::base::Char8, + *const DisplayInfo, + *mut *mut ImageOutput, + usize, + usize, + *mut *mut super::hii_font::RowInfo, + *mut usize, + *mut usize, +) -> crate::base::Status}; + +pub type ProtocolGetGlyphEx = eficall! {fn( + *const Protocol, + crate::base::Char16, + *const DisplayInfo, + *mut *mut ImageOutput, + usize, +) -> crate::base::Status}; + +pub type ProtocolGetFontInfoEx = eficall! {fn( + *const Protocol, + *mut super::hii_font::Handle, + *const DisplayInfo, + *mut *mut DisplayInfo, + super::hii_font::String, +) -> crate::base::Status}; + +pub type ProtocolGetGlyphInfo = eficall! {fn( + *const Protocol, + crate::base::Char16, + *const DisplayInfo, + *mut crate::hii::GlyphInfo, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub string_to_image_ex: ProtocolStringToImageEx, + pub string_id_to_image_ex: ProtocolStringIdToImageEx, + pub get_glyph_ex: ProtocolGetGlyphEx, + pub get_font_info_ex: ProtocolGetFontInfoEx, + pub get_glyph_info: ProtocolGetGlyphInfo, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct DisplayInfo { + pub foreground_color: super::graphics_output::BltPixel, + pub background_color: super::graphics_output::BltPixel, + pub font_info_mask: InfoMask, + pub font_info: super::hii_string::Info, +} + +pub type InfoMask = u32; + +pub const INFO_SYS_FONT: InfoMask = 0x00000001; +pub const INFO_SYS_SIZE: InfoMask = 0x00000002; +pub const INFO_SYS_STYLE: InfoMask = 0x00000004; +pub const INFO_SYS_FORE_COLOR: InfoMask = 0x00000010; +pub const INFO_SYS_BACK_COLOR: InfoMask = 0x00000020; +pub const INFO_RESIZE: InfoMask = 0x00001000; +pub const INFO_RESTYLE: InfoMask = 0x00002000; +pub const INFO_ANY_FONT: InfoMask = 0x00010000; +pub const INFO_ANY_SIZE: InfoMask = 0x00020000; +pub const INFO_ANY_STYLE: InfoMask = 0x00040000; + +#[repr(C)] +#[derive(Clone, Copy)] +pub union ImageOutputImage { + pub bitmap: *mut super::graphics_output::BltPixel, + pub screen: *mut super::graphics_output::Protocol, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct ImageOutput { + pub width: u16, + pub height: u16, + pub image: ImageOutputImage, +} diff --git a/tools/vendor/r-efi/src/protocols/hii_package_list.rs b/tools/vendor/r-efi/src/protocols/hii_package_list.rs new file mode 100644 index 0000000000..3a9c6fdbd7 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/hii_package_list.rs @@ -0,0 +1,14 @@ +//! Human Interface Infrastructure (HII) Package List Protocol +//! +//! Installed onto an image handle during load if the image contains a custom PE/COFF +//! resource with type 'HII'. The protocol's interface pointer points to the HII package +//! list which is contained in the resource's data. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x6a1ee763, + 0xd47a, + 0x43b4, + 0xaa, + 0xbe, + &[0xef, 0x1d, 0xe2, 0xab, 0x56, 0xfc], +); diff --git a/tools/vendor/r-efi/src/protocols/hii_string.rs b/tools/vendor/r-efi/src/protocols/hii_string.rs new file mode 100644 index 0000000000..c7a1d6e33d --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/hii_string.rs @@ -0,0 +1,71 @@ +//! HII String Protocol + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xfd96974, + 0x23aa, + 0x4cdc, + 0xb9, + 0xcb, + &[0x98, 0xd1, 0x77, 0x50, 0x32, 0x2a], +); + +pub type ProtocolNewString = eficall! {fn( + *const Protocol, + crate::hii::Handle, + *mut crate::hii::StringId, + *const crate::base::Char8, + *const crate::base::Char16, + super::hii_font::String, + *const Info, +) -> crate::base::Status}; + +pub type ProtocolGetString = eficall! {fn( + *const Protocol, + *const crate::base::Char8, + crate::hii::Handle, + crate::hii::StringId, + super::hii_font::String, + *mut usize, + *mut *mut Info, +) -> crate::base::Status}; + +pub type ProtocolSetString = eficall! {fn( + *const Protocol, + crate::hii::Handle, + crate::hii::StringId, + *const crate::base::Char8, + super::hii_font::String, + *const Info, +) -> crate::base::Status}; + +pub type ProtocolGetLanguages = eficall! {fn( + *const Protocol, + crate::hii::Handle, + *mut crate::base::Char8, + *mut usize, +) -> crate::base::Status}; + +pub type ProtocolGetSecondaryLanguages = eficall! {fn( + *const Protocol, + crate::hii::Handle, + *const crate::base::Char8, + *mut crate::base::Char8, + *mut usize, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub new_string: ProtocolNewString, + pub get_string: ProtocolGetString, + pub set_string: ProtocolSetString, + pub get_languages: ProtocolGetLanguages, + pub get_secondary_languages: ProtocolGetSecondaryLanguages, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Info { + pub font_style: crate::hii::FontStyle, + pub font_size: u16, + pub font_name: [crate::base::Char16; N], +} diff --git a/tools/vendor/r-efi/src/protocols/ip4.rs b/tools/vendor/r-efi/src/protocols/ip4.rs new file mode 100644 index 0000000000..1a57b5c991 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/ip4.rs @@ -0,0 +1,202 @@ +//! IPv4 Protocol +//! +//! It implements a simple packet-oriented interface that can be used by +//! drivers, daemons, and applications to transmit and receive network packets. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x41d94cd2, + 0x35b6, + 0x455a, + 0x82, + 0x58, + &[0xd4, 0xe5, 0x13, 0x34, 0xaa, 0xdd], +); + +pub const SERVICE_BINDING_PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xc51711e7, + 0xb4bf, + 0x404a, + 0xbf, + 0xb8, + &[0x0a, 0x04, 0x8e, 0xf1, 0xff, 0xe4], +); + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ConfigData { + pub default_protocol: u8, + pub accept_any_protocol: crate::base::Boolean, + pub accept_icmp_errors: crate::base::Boolean, + pub accept_broadcast: crate::base::Boolean, + pub accept_promiscuous: crate::base::Boolean, + pub use_default_address: crate::base::Boolean, + pub station_address: crate::base::Ipv4Address, + pub subnet_mask: crate::base::Ipv4Address, + pub type_of_service: u8, + pub time_to_live: u8, + pub do_not_fragment: crate::base::Boolean, + pub raw_data: crate::base::Boolean, + pub receive_timeout: u32, + pub transmit_timeout: u32, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct RouteTable { + pub subnet_address: crate::base::Ipv4Address, + pub subnet_mask: crate::base::Ipv4Address, + pub gateway_address: crate::base::Ipv4Address, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IcmpType { + pub r#type: u8, + pub code: u8, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ModeData { + pub is_started: crate::base::Boolean, + pub max_packet_size: u32, + pub config_data: ConfigData, + pub is_configured: crate::base::Boolean, + pub group_count: u32, + pub group_table: *mut crate::base::Ipv4Address, + pub route_count: u32, + pub route_table: *mut RouteTable, + pub icmp_type_count: u32, + pub icmp_type_list: *mut IcmpType, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct CompletionToken { + pub event: crate::base::Event, + pub status: crate::base::Status, + pub packet: CompletionTokenPacket, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union CompletionTokenPacket { + pub rx_data: *mut ReceiveData, + pub tx_data: *mut TransmitData, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ReceiveData { + pub time_stamp: crate::system::Time, + pub recycle_signal: crate::base::Event, + pub header_length: u32, + pub header: *mut Header, + pub options_length: u32, + pub options: *mut core::ffi::c_void, + pub data_length: u32, + pub fragment_count: u32, + pub fragment_table: [FragmentData; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct TransmitData { + pub destination_address: crate::base::Ipv4Address, + pub override_data: *mut OverrideData, + pub options_length: u32, + pub options_buffer: *mut core::ffi::c_void, + pub total_data_length: u32, + pub fragment_count: u32, + pub fragment_table: [FragmentData; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Header { + pub header_length_and_version: u8, + pub type_of_service: u8, + pub total_length: u16, + pub identification: u16, + pub fragmentation: u16, + pub time_to_live: u8, + pub protocol: u8, + pub checksum: u16, + pub source_address: crate::base::Ipv4Address, + pub destination_address: crate::base::Ipv4Address, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct FragmentData { + pub fragment_length: u32, + pub fragment_buffer: *mut core::ffi::c_void, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct OverrideData { + pub source_address: crate::base::Ipv4Address, + pub gateway_address: crate::base::Ipv4Address, + pub protocol: u8, + pub type_of_service: u8, + pub time_to_live: u8, + pub do_not_fragment: crate::base::Boolean, +} + +pub type ProtocolGetModeData = eficall! {fn( + *mut Protocol, + *mut ModeData, + *mut crate::protocols::managed_network::ConfigData, + *mut crate::protocols::simple_network::Mode, +) -> crate::base::Status}; + +pub type ProtocolConfigure = eficall! {fn( + *mut Protocol, + *mut ConfigData, +) -> crate::base::Status}; + +pub type ProtocolGroups = eficall! {fn( + *mut Protocol, + crate::base::Boolean, + *mut crate::base::Ipv4Address, +) -> crate::base::Status}; + +pub type ProtocolRoutes = eficall! {fn( + *mut Protocol, + crate::base::Boolean, + *mut crate::base::Ipv4Address, + *mut crate::base::Ipv4Address, + *mut crate::base::Ipv4Address, +) -> crate::base::Status}; + +pub type ProtocolTransmit = eficall! {fn( + *mut Protocol, + *mut CompletionToken, +) -> crate::base::Status}; + +pub type ProtocolReceive = eficall! {fn( + *mut Protocol, + *mut CompletionToken, +) -> crate::base::Status}; + +pub type ProtocolCancel = eficall! {fn( + *mut Protocol, + *mut CompletionToken, +) -> crate::base::Status}; + +pub type ProtocolPoll = eficall! {fn( + *mut Protocol, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub get_mode_data: ProtocolGetModeData, + pub configure: ProtocolConfigure, + pub groups: ProtocolGroups, + pub routes: ProtocolRoutes, + pub transmit: ProtocolTransmit, + pub receive: ProtocolReceive, + pub cancel: ProtocolCancel, + pub poll: ProtocolPoll, +} diff --git a/tools/vendor/r-efi/src/protocols/ip6.rs b/tools/vendor/r-efi/src/protocols/ip6.rs new file mode 100644 index 0000000000..ad03e8f1ff --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/ip6.rs @@ -0,0 +1,264 @@ +//! IPv6 Protocol +//! +//! It implements a simple packet-oriented interface that can be used by +//! drivers, daemons, and applications to transmit and receive network packets. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x2c8759d5, + 0x5c2d, + 0x66ef, + 0x92, + 0x5f, + &[0xb6, 0x6c, 0x10, 0x19, 0x57, 0xe2], +); + +pub const SERVICE_BINDING_PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xec835dd3, + 0xfe0f, + 0x617b, + 0xa6, + 0x21, + &[0xb3, 0x50, 0xc3, 0xe1, 0x33, 0x88], +); + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ModeData { + pub is_started: crate::base::Boolean, + pub max_packet_size: u32, + pub config_data: ConfigData, + pub is_configured: crate::base::Boolean, + pub address_count: u32, + pub address_list: *mut AddressInfo, + pub group_count: u32, + pub group_table: *mut crate::base::Ipv6Address, + pub route_count: u32, + pub route_table: *mut RouteTable, + pub neighbor_count: u32, + pub neighbor_cache: *mut NeighborCache, + pub prefix_count: u32, + pub prefix_table: *mut AddressInfo, + pub icmp_type_count: u32, + pub icmp_type_list: *mut IcmpType, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ConfigData { + pub default_protocol: u8, + pub accept_any_protocol: crate::base::Boolean, + pub accept_icmp_errors: crate::base::Boolean, + pub accept_promiscuous: crate::base::Boolean, + pub destination_address: crate::base::Ipv6Address, + pub station_address: crate::base::Ipv6Address, + pub traffic_class: u8, + pub hop_limit: u8, + pub flow_lable: u32, + pub receive_timeout: u32, + pub transmit_timeout: u32, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct AddressInfo { + pub address: crate::base::Ipv6Address, + pub prefix_length: u8, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct RouteTable { + pub gateway: crate::base::Ipv6Address, + pub destination: crate::base::Ipv6Address, + pub prefix_length: u8, +} + +pub type NeighborState = u32; + +pub const NEIGHBOR_IN_COMPLETE: NeighborState = 0x00000000; +pub const NEIGHBOR_REACHABLE: NeighborState = 0x00000001; +pub const NEIGHBOR_STATE: NeighborState = 0x00000002; +pub const NEIGHBOR_DELAY: NeighborState = 0x00000003; +pub const NEIGHBOR_PROBE: NeighborState = 0x00000004; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct NeighborCache { + pub neighbor: crate::base::Ipv6Address, + pub link_address: crate::base::MacAddress, + pub state: NeighborState, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct IcmpType { + pub r#type: u8, + pub code: u8, +} + +pub const ICMP_V6_DEST_UNREACHABLE: u8 = 0x01; +pub const ICMP_V6_PACKET_TOO_BIG: u8 = 0x02; +pub const ICMP_V6_TIME_EXCEEDED: u8 = 0x03; +pub const ICMP_V6_PARAMETER_PROBLEM: u8 = 0x04; + +pub const ICMP_V6_ECHO_REQUEST: u8 = 0x80; +pub const ICMP_V6_ECHO_REPLY: u8 = 0x81; +pub const ICMP_V6_LISTENER_QUERY: u8 = 0x82; +pub const ICMP_V6_LISTENER_REPORT: u8 = 0x83; +pub const ICMP_V6_LISTENER_DONE: u8 = 0x84; +pub const ICMP_V6_ROUTER_SOLICIT: u8 = 0x85; +pub const ICMP_V6_ROUTER_ADVERTISE: u8 = 0x86; +pub const ICMP_V6_NEIGHBOR_SOLICIT: u8 = 0x87; +pub const ICMP_V6_NEIGHBOR_ADVERTISE: u8 = 0x88; +pub const ICMP_V6_REDIRECT: u8 = 0x89; +pub const ICMP_V6_LISTENER_REPORT_2: u8 = 0x8f; + +pub const ICMP_V6_NO_ROUTE_TO_DEST: u8 = 0x00; +pub const ICMP_V6_COMM_PROHIBITED: u8 = 0x01; +pub const ICMP_V6_BEYOND_SCOPE: u8 = 0x02; +pub const ICMP_V6_ADDR_UNREACHABLE: u8 = 0x03; +pub const ICMP_V6_PORT_UNREACHABLE: u8 = 0x04; +pub const ICMP_V6_SOURCE_ADDR_FAILED: u8 = 0x05; +pub const ICMP_V6_ROUTE_REJECTED: u8 = 0x06; + +pub const ICMP_V6_TIMEOUT_HOP_LIMIT: u8 = 0x00; +pub const ICMP_V6_TIMEOUT_REASSEMBLE: u8 = 0x01; + +pub const ICMP_V6_ERRONEOUS_HEADER: u8 = 0x00; +pub const ICMP_V6_UNRECOGNIZE_NEXT_HDR: u8 = 0x01; +pub const ICMP_V6_UNRECOGNIZE_OPTION: u8 = 0x02; + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct CompletionToken { + pub event: crate::base::Event, + pub status: crate::base::Status, + pub packet: CompletionTokenPacket, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union CompletionTokenPacket { + pub rx_data: *mut ReceiveData, + pub tx_data: *mut TransmitData, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ReceiveData { + pub time_stamp: crate::system::Time, + pub recycle_signal: crate::base::Event, + pub header_length: u32, + pub header: *mut Header, + pub data_length: u32, + pub fragment_count: u32, + pub fragment_table: [FragmentData; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Header { + pub traffic_class_h_version: u8, + pub flow_label_h_traffic_class_l: u8, + pub flow_label_l: u16, + pub payload_length: u16, + pub next_header: u8, + pub hop_limit: u8, + pub source_address: crate::base::Ipv6Address, + pub destination_address: crate::base::Ipv6Address, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct FragmentData { + pub fragment_length: u32, + pub fragment_buffer: *mut core::ffi::c_void, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct TransmitData { + pub destination_address: *mut crate::base::Ipv6Address, + pub override_data: *mut OverrideData, + pub ext_hdrs_length: u32, + pub ext_hdrs: *mut core::ffi::c_void, + pub next_header: u8, + pub data_length: u32, + pub fragment_count: u32, + pub fragment_table: [FragmentData; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct OverrideData { + pub protocol: u8, + pub hop_limit: u8, + pub flow_label: u32, +} + +pub type ProtocolGetModeData = eficall! {fn( + *mut Protocol, + *mut ModeData, + *mut crate::protocols::managed_network::ConfigData, + *mut crate::protocols::simple_network::Mode, +) -> crate::base::Status}; + +pub type ProtocolConfigure = eficall! {fn( + *mut Protocol, + *mut ConfigData, +) -> crate::base::Status}; + +pub type ProtocolGroups = eficall! {fn( + *mut Protocol, + crate::base::Boolean, + *mut crate::base::Ipv6Address, +) -> crate::base::Status}; + +pub type ProtocolRoutes = eficall! {fn( + *mut Protocol, + crate::base::Boolean, + *mut crate::base::Ipv6Address, + u8, + *mut crate::base::Ipv6Address, +) -> crate::base::Status}; + +pub type ProtocolNeighbors = eficall! {fn( + *mut Protocol, + crate::base::Boolean, + *mut crate::base::Ipv6Address, + *mut crate::base::MacAddress, + u32, + crate::base::Boolean, +) -> crate::base::Status}; + +pub type ProtocolTransmit = eficall! {fn( + *mut Protocol, + *mut CompletionToken, +) -> crate::base::Status}; + +pub type ProtocolReceive = eficall! {fn( + *mut Protocol, + *mut CompletionToken, +) -> crate::base::Status}; + +pub type ProtocolCancel = eficall! {fn( + *mut Protocol, + *mut CompletionToken, +) -> crate::base::Status}; + +pub type ProtocolPoll = eficall! {fn( + *mut Protocol, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub get_mode_data: ProtocolGetModeData, + pub configure: ProtocolConfigure, + pub groups: ProtocolGroups, + pub routes: ProtocolRoutes, + pub neighbors: ProtocolNeighbors, + pub transmit: ProtocolTransmit, + pub receive: ProtocolReceive, + pub cancel: ProtocolCancel, + pub poll: ProtocolPoll, +} diff --git a/tools/vendor/r-efi/src/protocols/load_file.rs b/tools/vendor/r-efi/src/protocols/load_file.rs new file mode 100644 index 0000000000..fc70f1fa1e --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/load_file.rs @@ -0,0 +1,26 @@ +//! Load File Protocol +//! +//! The Load File protocol is used to obtain files, that are primarily boot +//! options, from arbitrary devices. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x56ec3091, + 0x954c, + 0x11d2, + 0x8e, + 0x3f, + &[0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b], +); + +pub type ProtocolLoadFile = eficall! {fn( + *mut Protocol, + *mut crate::protocols::device_path::Protocol, + crate::base::Boolean, + *mut usize, + *mut core::ffi::c_void +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub load_file: ProtocolLoadFile, +} diff --git a/tools/vendor/r-efi/src/protocols/load_file2.rs b/tools/vendor/r-efi/src/protocols/load_file2.rs new file mode 100644 index 0000000000..0b7c31f47c --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/load_file2.rs @@ -0,0 +1,15 @@ +//! Load File 2 Protocol +//! +//! The Load File 2 protocol is used to obtain files from arbitrary devices +//! that are not boot options. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x4006c0c1, + 0xfcb3, + 0x403e, + 0x99, + 0x6d, + &[0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d], +); + +pub type Protocol = crate::protocols::load_file::Protocol; diff --git a/tools/vendor/r-efi/src/protocols/loaded_image.rs b/tools/vendor/r-efi/src/protocols/loaded_image.rs new file mode 100644 index 0000000000..1057071603 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/loaded_image.rs @@ -0,0 +1,39 @@ +//! Loaded Image Protocol +//! +//! The loaded image protocol defines how to obtain information about a loaded image from an +//! image handle. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x5b1b31a1, + 0x9562, + 0x11d2, + 0x8e, + 0x3f, + &[0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b], +); + +pub const REVISION: u32 = 0x00001000u32; + +pub type ProtocolUnload = eficall! {fn( + crate::base::Handle, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub revision: u32, + pub parent_handle: crate::base::Handle, + pub system_table: *mut crate::system::SystemTable, + + pub device_handle: crate::base::Handle, + pub file_path: *mut crate::protocols::device_path::Protocol, + pub reserved: *mut core::ffi::c_void, + + pub load_options_size: u32, + pub load_options: *mut core::ffi::c_void, + + pub image_base: *mut core::ffi::c_void, + pub image_size: u64, + pub image_code_type: crate::system::MemoryType, + pub image_data_type: crate::system::MemoryType, + pub unload: Option, +} diff --git a/tools/vendor/r-efi/src/protocols/loaded_image_device_path.rs b/tools/vendor/r-efi/src/protocols/loaded_image_device_path.rs new file mode 100644 index 0000000000..55067317f8 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/loaded_image_device_path.rs @@ -0,0 +1,13 @@ +//! Loaded Image Device Path Protocol +//! +//! The loaded image device path protocol provides the device path of a loaded image, using the +//! protocol structures of the device-path and loaded-image protocols. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xbc62157e, + 0x3e33, + 0x4fec, + 0x99, + 0x20, + &[0x2d, 0x3b, 0x36, 0xd7, 0x50, 0xdf], +); diff --git a/tools/vendor/r-efi/src/protocols/managed_network.rs b/tools/vendor/r-efi/src/protocols/managed_network.rs new file mode 100644 index 0000000000..12d516b4bc --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/managed_network.rs @@ -0,0 +1,147 @@ +//! Managed Network Protocol +//! +//! It provides raw (unformatted) asynchronous network packet I/O services. +//! These services make it possible for multiple-event-driven drivers and +//! applications to access and use the system network interfaces at the same +//! time. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x7ab33a91, + 0xace5, + 0x4326, + 0xb5, + 0x72, + &[0xe7, 0xee, 0x33, 0xd3, 0x9f, 0x16], +); + +pub const SERVICE_BINDING_PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xf36ff770, + 0xa7e1, + 0x42cf, + 0x9e, + 0xd2, + &[0x56, 0xf0, 0xf2, 0x71, 0xf4, 0x4c], +); + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ConfigData { + pub received_queue_timeout_value: u32, + pub transmit_queue_timeout_value: u32, + pub protocol_type_filter: u16, + pub enable_unicast_receive: crate::base::Boolean, + pub enable_multicast_receive: crate::base::Boolean, + pub enable_broadcast_receive: crate::base::Boolean, + pub enable_promiscuous_receive: crate::base::Boolean, + pub flush_queues_on_reset: crate::base::Boolean, + pub enable_receive_timestamps: crate::base::Boolean, + pub disable_background_polling: crate::base::Boolean, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct CompletionToken { + pub event: crate::base::Event, + pub status: crate::base::Status, + pub packet: CompletionTokenPacket, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union CompletionTokenPacket { + pub rx_data: *mut ReceiveData, + pub tx_data: *mut TransmitData, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ReceiveData { + pub timestamp: crate::system::Time, + pub recycle_event: crate::base::Event, + pub packet_length: u32, + pub header_length: u32, + pub address_length: u32, + pub data_length: u32, + pub broadcast_flag: crate::base::Boolean, + pub multicast_flag: crate::base::Boolean, + pub promiscuous_flag: crate::base::Boolean, + pub protocol_type: u16, + pub destination_address: *mut core::ffi::c_void, + pub source_address: *mut core::ffi::c_void, + pub media_header: *mut core::ffi::c_void, + pub packet_data: *mut core::ffi::c_void, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct TransmitData { + pub destination_address: *mut crate::base::MacAddress, + pub source_address: *mut crate::base::MacAddress, + pub protocol_type: u16, + pub data_length: u32, + pub header_length: u16, + pub fragment_count: u16, + pub fragment_table: [FragmentData; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct FragmentData { + pub fragment_length: u32, + pub fragment_buffer: *mut core::ffi::c_void, +} + +pub type ProtocolGetModeData = eficall! {fn( + *mut Protocol, + *mut ConfigData, + *mut crate::protocols::simple_network::Mode, +) -> crate::base::Status}; + +pub type ProtocolConfigure = eficall! {fn( + *mut Protocol, + *mut ConfigData, +) -> crate::base::Status}; + +pub type ProtocolMcastIpToMac = eficall! {fn( + *mut Protocol, + crate::base::Boolean, + *mut crate::base::IpAddress, + *mut crate::base::MacAddress, +) -> crate::base::Status}; + +pub type ProtocolGroups = eficall! {fn( + *mut Protocol, + crate::base::Boolean, + *mut crate::base::MacAddress, +) -> crate::base::Status}; + +pub type ProtocolTransmit = eficall! {fn( + *mut Protocol, + *mut CompletionToken, +) -> crate::base::Status}; + +pub type ProtocolReceive = eficall! {fn( + *mut Protocol, + *mut CompletionToken, +) -> crate::base::Status}; + +pub type ProtocolCancel = eficall! {fn( + *mut Protocol, + *mut CompletionToken, +) -> crate::base::Status}; + +pub type ProtocolPoll = eficall! {fn( + *mut Protocol, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub get_mode_data: ProtocolGetModeData, + pub configure: ProtocolConfigure, + pub mcast_ip_to_mac: ProtocolMcastIpToMac, + pub groups: ProtocolGroups, + pub transmit: ProtocolTransmit, + pub receive: ProtocolReceive, + pub cancel: ProtocolCancel, + pub poll: ProtocolPoll, +} diff --git a/tools/vendor/r-efi/src/protocols/memory_attribute.rs b/tools/vendor/r-efi/src/protocols/memory_attribute.rs new file mode 100644 index 0000000000..7166fea635 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/memory_attribute.rs @@ -0,0 +1,40 @@ +//! Memory Attribute Protocol +//! +//! Provides an interface to abstract setting or getting of memory attributes in the UEFI environment. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xf4560cf6, + 0x40ec, + 0x4b4a, + 0xa1, + 0x92, + &[0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89], +); + +pub type GetMemoryAttributes = eficall! {fn( + *mut Protocol, + crate::base::PhysicalAddress, + u64, + *mut u64, +) -> crate::base::Status}; + +pub type SetMemoryAttributes = eficall! {fn( + *mut Protocol, + crate::base::PhysicalAddress, + u64, + u64, +) -> crate::base::Status}; + +pub type ClearMemoryAttributes = eficall! {fn( + *mut Protocol, + crate::base::PhysicalAddress, + u64, + u64, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub get_memory_attributes: GetMemoryAttributes, + pub set_memory_attributes: SetMemoryAttributes, + pub clear_memory_attributes: ClearMemoryAttributes, +} diff --git a/tools/vendor/r-efi/src/protocols/mp_services.rs b/tools/vendor/r-efi/src/protocols/mp_services.rs new file mode 100644 index 0000000000..6163b4c51e --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/mp_services.rs @@ -0,0 +1,121 @@ +//! Multi-Processor Services Protocol +//! +//! This Protocol is defined in the UEFI Platform Integration Specification, +//! Section 13.4. +//! +//! This provides a generalized way of performing the following tasks: +//! - Retrieving information of multi-processor environments. +//! - Dispatching user-provided function to APs. +//! - Maintain MP-related processor status. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x3fdda605, + 0xa76e, + 0x4f46, + 0xad, + 0x29, + &[0x12, 0xf4, 0x53, 0x1b, 0x3d, 0x08], +); + +pub const PROCESSOR_AS_BSP_BIT: u32 = 0x00000001; +pub const PROCESSOR_ENABLED_BIT: u32 = 0x00000002; +pub const PROCESSOR_HEALTH_STATUS_BIT: u32 = 0x00000004; + +pub const END_OF_CPU_LIST: usize = usize::MAX; + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct CpuPhysicalLocation { + pub package: u32, + pub core: u32, + pub thread: u32, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct CpuPhysicalLocation2 { + pub package: u32, + pub module: u32, + pub tile: u32, + pub die: u32, + pub core: u32, + pub thread: u32, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union ExtendedProcessorInformation { + pub location2: CpuPhysicalLocation2, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct ProcessorInformation { + pub processor_id: u64, + pub status_flag: u32, + pub location: CpuPhysicalLocation, + pub extended_information: ExtendedProcessorInformation, +} + +pub type ApProcedure = eficall! {fn(*mut core::ffi::c_void)}; + +pub type GetNumberOfProcessors = eficall! {fn( + *mut Protocol, + *mut usize, + *mut usize, +) -> crate::base::Status}; + +pub type GetProcessorInfo = eficall! {fn( + *mut Protocol, + usize, + *mut ProcessorInformation, +) -> crate::base::Status}; + +pub type StartupAllAps = eficall! {fn( + *mut Protocol, + ApProcedure, + crate::base::Boolean, + crate::base::Event, + usize, + *mut core::ffi::c_void, + *mut *mut usize, +) -> crate::base::Status}; + +pub type StartupThisAp = eficall! {fn( + *mut Protocol, + ApProcedure, + usize, + crate::base::Event, + usize, + *mut core::ffi::c_void, + *mut crate::base::Boolean, +) -> crate::base::Status}; + +pub type SwitchBsp = eficall! {fn( + *mut Protocol, + usize, + crate::base::Boolean, +) -> crate::base::Status}; + +pub type EnableDisableAp = eficall! {fn( + *mut Protocol, + usize, + crate::base::Boolean, + *mut u32, +) -> crate::base::Status}; + +pub type WhoAmI = eficall! {fn( + *mut Protocol, + *mut usize, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub get_number_of_processors: GetNumberOfProcessors, + pub get_processor_info: GetProcessorInfo, + pub startup_all_aps: StartupAllAps, + pub startup_this_ap: StartupThisAp, + pub switch_bsp: SwitchBsp, + pub enable_disable_ap: EnableDisableAp, + pub who_am_i: WhoAmI, +} diff --git a/tools/vendor/r-efi/src/protocols/pci_io.rs b/tools/vendor/r-efi/src/protocols/pci_io.rs new file mode 100644 index 0000000000..68787d1a12 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/pci_io.rs @@ -0,0 +1,203 @@ +//! PCI I/O Protocol +//! +//! Used by code, typically drivers, running in the EFI boot services +//! environment to access memory and I/O on a PCI controller. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x4cf5b200, + 0x68b8, + 0x4ca5, + 0x9e, + 0xec, + &[0xb2, 0x3e, 0x3f, 0x50, 0x02, 0x9a], +); + +pub type Width = u32; + +pub const WIDTH_UINT8: Width = 0x00000000; +pub const WIDTH_UINT16: Width = 0x00000001; +pub const WIDTH_UINT32: Width = 0x00000002; +pub const WIDTH_UINT64: Width = 0x00000003; +pub const WIDTH_FIFO_UINT8: Width = 0x00000004; +pub const WIDTH_FIFO_UINT16: Width = 0x00000005; +pub const WIDTH_FIFO_UINT32: Width = 0x00000006; +pub const WIDTH_FIFO_UINT64: Width = 0x00000007; +pub const WIDTH_FILL_UINT8: Width = 0x00000008; +pub const WIDTH_FILL_UINT16: Width = 0x00000009; +pub const WIDTH_FILL_UINT32: Width = 0x0000000a; +pub const WIDTH_FILL_UINT64: Width = 0x0000000b; +pub const WIDTH_MAXIMUM: Width = 0x0000000c; + +pub type Operation = u32; + +pub const OPERATION_BUS_MASTER_READ: Operation = 0x00000000; +pub const OPERATION_BUS_MASTER_WRITE: Operation = 0x00000001; +pub const OPERATION_BUS_MASTER_COMMON_BUFFER: Operation = 0x00000002; +pub const OPERATION_MAXIMUM: Operation = 0x00000003; + +pub type Attribute = u64; + +pub const ATTRIBUTE_ISA_MOTHERBOARD_IO: Attribute = 0x00000001; +pub const ATTRIBUTE_ISA_IO: Attribute = 0x00000002; +pub const ATTRIBUTE_VGA_PALETTE_IO: Attribute = 0x00000004; +pub const ATTRIBUTE_VGA_MEMORY: Attribute = 0x00000008; +pub const ATTRIBUTE_VGA_IO: Attribute = 0x00000010; +pub const ATTRIBUTE_IDE_PRIMARY_IO: Attribute = 0x00000020; +pub const ATTRIBUTE_IDE_SECONDARY_IO: Attribute = 0x00000040; +pub const ATTRIBUTE_MEMORY_WRITE_COMBINE: Attribute = 0x00000080; +pub const ATTRIBUTE_IO: Attribute = 0x00000100; +pub const ATTRIBUTE_MEMORY: Attribute = 0x00000200; +pub const ATTRIBUTE_BUS_MASTER: Attribute = 0x00000400; +pub const ATTRIBUTE_MEMORY_CACHED: Attribute = 0x00000800; +pub const ATTRIBUTE_MEMORY_DISABLE: Attribute = 0x00001000; +pub const ATTRIBUTE_EMBEDDED_DEVICE: Attribute = 0x00002000; +pub const ATTRIBUTE_EMBEDDED_ROM: Attribute = 0x00004000; +pub const ATTRIBUTE_DUAL_ADDRESS_CYCLE: Attribute = 0x00008000; +pub const ATTRIBUTE_ISA_IO_16: Attribute = 0x00010000; +pub const ATTRIBUTE_VGA_PALETTE_IO_16: Attribute = 0x00020000; +pub const ATTRIBUTE_VGA_IO_16: Attribute = 0x00040000; + +pub type AttributeOperation = u32; + +pub const ATTRIBUTE_OPERATION_GET: AttributeOperation = 0x00000000; +pub const ATTRIBUTE_OPERATION_SET: AttributeOperation = 0x00000001; +pub const ATTRIBUTE_OPERATION_ENABLE: AttributeOperation = 0x00000002; +pub const ATTRIBUTE_OPERATION_DISABLE: AttributeOperation = 0x00000003; +pub const ATTRIBUTE_OPERATION_SUPPORTED: AttributeOperation = 0x00000004; +pub const ATTRIBUTE_OPERATION_MAXIMUM: AttributeOperation = 0x00000005; + +pub const PASS_THROUGH_BAR: u8 = 0xff; + +pub type ProtocolPollIoMem = eficall! {fn( + *mut Protocol, + Width, + u8, + u64, + u64, + u64, + u64, + *mut u64, +) -> crate::base::Status}; + +pub type ProtocolIoMem = eficall! {fn( + *mut Protocol, + Width, + u8, + u64, + usize, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type ProtocolConfig = eficall! {fn( + *mut Protocol, + Width, + u32, + usize, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type ProtocolCopyMem = eficall! {fn( + *mut Protocol, + Width, + u8, + u64, + u8, + u64, + usize, +) -> crate::base::Status}; + +pub type ProtocolMap = eficall! {fn( + *mut Protocol, + Operation, + *mut core::ffi::c_void, + *mut usize, + *mut crate::base::PhysicalAddress, + *mut *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type ProtocolUnmap = eficall! {fn( + *mut Protocol, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type ProtocolAllocateBuffer = eficall! {fn( + *mut Protocol, + crate::system::AllocateType, + crate::system::MemoryType, + usize, + *mut *mut core::ffi::c_void, + Attribute, +) -> crate::base::Status}; + +pub type ProtocolFreeBuffer = eficall! {fn( + *mut Protocol, + usize, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type ProtocolFlush = eficall! {fn( + *mut Protocol, +) -> crate::base::Status}; + +pub type ProtocolGetLocation = eficall! {fn( + *mut Protocol, + *mut usize, + *mut usize, + *mut usize, + *mut usize, +) -> crate::base::Status}; + +pub type ProtocolAttributes = eficall! {fn( + *mut Protocol, + AttributeOperation, + Attribute, + *mut Attribute, +) -> crate::base::Status}; + +pub type ProtocolGetBarAttributes = eficall! {fn( + *mut Protocol, + u8, + *mut Attribute, + *mut *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type ProtocolSetBarAttributes = eficall! {fn( + *mut Protocol, + Attribute, + u8, + *mut u64, + *mut u64, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Access { + pub read: ProtocolIoMem, + pub write: ProtocolIoMem, +} + +#[repr(C)] +pub struct ConfigAccess { + pub read: ProtocolConfig, + pub write: ProtocolConfig, +} + +#[repr(C)] +pub struct Protocol { + pub poll_mem: ProtocolPollIoMem, + pub poll_io: ProtocolPollIoMem, + pub mem: Access, + pub io: Access, + pub pci: ConfigAccess, + pub copy_mem: ProtocolCopyMem, + pub map: ProtocolMap, + pub unmap: ProtocolUnmap, + pub allocate_buffer: ProtocolAllocateBuffer, + pub free_buffer: ProtocolFreeBuffer, + pub flush: ProtocolFlush, + pub get_location: ProtocolGetLocation, + pub attributes: ProtocolAttributes, + pub get_bar_attributes: ProtocolGetBarAttributes, + pub set_bar_attributes: ProtocolSetBarAttributes, + pub rom_size: u64, + pub rom_image: *mut core::ffi::c_void, +} diff --git a/tools/vendor/r-efi/src/protocols/platform_driver_override.rs b/tools/vendor/r-efi/src/protocols/platform_driver_override.rs new file mode 100644 index 0000000000..30ceb8d336 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/platform_driver_override.rs @@ -0,0 +1,46 @@ +//! Platform Driver Override Protocol +//! +//! This protocol matches one or more drivers to a controller. A platform driver +//! produces this protocol, and it is installed on a separate handle. This +//! protocol is used by the `EFI_BOOT_SERVICES.ConnectController()` boot service +//! to select the best driver for a controller. All of the drivers returned by +//! this protocol have a higher precedence than drivers found from an EFI Bus +//! Specific Driver Override Protocol or drivers found from the general UEFI +//! driver binding search algorithm. If more than one driver is returned by this +//! protocol, then the drivers are returned in order from highest precedence to +//! lowest precedence. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x6b30c738, + 0xa391, + 0x11d4, + 0x9a, + 0x3b, + &[0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d], +); + +pub type ProtocolGetDriver = eficall! {fn( + *mut Protocol, + crate::base::Handle, + *mut crate::base::Handle, +) -> crate::base::Status}; + +pub type ProtocolGetDriverPath = eficall! {fn( + *mut Protocol, + crate::base::Handle, + *mut *mut crate::protocols::device_path::Protocol +) -> crate::base::Status}; + +pub type ProtocolDriverLoaded = eficall! {fn( + *mut Protocol, + crate::base::Handle, + *mut crate::protocols::device_path::Protocol, + crate::base::Handle, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub get_driver: ProtocolGetDriver, + pub get_driver_path: ProtocolGetDriverPath, + pub driver_loaded: ProtocolDriverLoaded, +} diff --git a/tools/vendor/r-efi/src/protocols/rng.rs b/tools/vendor/r-efi/src/protocols/rng.rs new file mode 100644 index 0000000000..92eb013757 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/rng.rs @@ -0,0 +1,83 @@ +//! Random Number Generator Protocol +//! +//! This protocol is used to provide random numbers for use in applications, or +//! entropy for seeding other random number generators. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x3152bca5, + 0xeade, + 0x433d, + 0x86, + 0x2e, + &[0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44], +); + +pub type Algorithm = crate::base::Guid; + +pub const ALGORITHM_SP800_90_HASH_256_GUID: Algorithm = crate::base::Guid::from_fields( + 0xa7af67cb, + 0x603b, + 0x4d42, + 0xba, + 0x21, + &[0x70, 0xbf, 0xb6, 0x29, 0x3f, 0x96], +); +pub const ALGORITHM_SP800_90_HMAC_256_GUID: Algorithm = crate::base::Guid::from_fields( + 0xc5149b43, + 0xae85, + 0x4f53, + 0x99, + 0x82, + &[0xb9, 0x43, 0x35, 0xd3, 0xa9, 0xe7], +); +pub const ALGORITHM_SP800_90_CTR_256_GUID: Algorithm = crate::base::Guid::from_fields( + 0x44f0de6e, + 0x4d8c, + 0x4045, + 0xa8, + 0xc7, + &[0x4d, 0xd1, 0x68, 0x85, 0x6b, 0x9e], +); +pub const ALGORITHM_X9_31_3DES_GUID: Algorithm = crate::base::Guid::from_fields( + 0x63c4785a, + 0xca34, + 0x4012, + 0xa3, + 0xc8, + &[0x0b, 0x6a, 0x32, 0x4f, 0x55, 0x46], +); +pub const ALGORITHM_X9_31_AES_GUID: Algorithm = crate::base::Guid::from_fields( + 0xacd03321, + 0x777e, + 0x4d3d, + 0xb1, + 0xc8, + &[0x20, 0xcf, 0xd8, 0x88, 0x20, 0xc9], +); +pub const ALGORITHM_RAW: Algorithm = crate::base::Guid::from_fields( + 0xe43176d7, + 0xb6e8, + 0x4827, + 0xb7, + 0x84, + &[0x7f, 0xfd, 0xc4, 0xb6, 0x85, 0x61], +); + +pub type ProtocolGetInfo = eficall! {fn( + *mut Protocol, + *mut usize, + *mut Algorithm, +) -> crate::base::Status}; + +pub type ProtocolGetRng = eficall! {fn( + *mut Protocol, + *mut Algorithm, + usize, + *mut u8, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub get_info: ProtocolGetInfo, + pub get_rng: ProtocolGetRng, +} diff --git a/tools/vendor/r-efi/src/protocols/service_binding.rs b/tools/vendor/r-efi/src/protocols/service_binding.rs new file mode 100644 index 0000000000..67c69f91f8 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/service_binding.rs @@ -0,0 +1,20 @@ +//! Service Binding Protocol +//! +//! Provides services that are required to create and destroy child handles +//! that support a given set of protocols. + +pub type ProtocolCreateChild = eficall! {fn( + *mut Protocol, + *mut crate::base::Handle, +) -> crate::base::Status}; + +pub type ProtocolDestroyChild = eficall! {fn( + *mut Protocol, + crate::base::Handle, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub create_child: ProtocolCreateChild, + pub destroy_child: ProtocolDestroyChild, +} diff --git a/tools/vendor/r-efi/src/protocols/shell.rs b/tools/vendor/r-efi/src/protocols/shell.rs new file mode 100644 index 0000000000..01b6b2c68b --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/shell.rs @@ -0,0 +1,295 @@ +//! Shell Protocol +//! +//! Provides shell services to UEFI applications. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x6302d008, + 0x7f9b, + 0x4f30, + 0x87, + 0xac, + &[0x60, 0xc9, 0xfe, 0xf5, 0xda, 0x4e], +); + +pub const MAJOR_VERSION: u32 = 0x00000002; +pub const MINOR_VERSION: u32 = 0x00000002; + +pub type FileHandle = *mut core::ffi::c_void; + +pub type DeviceNameFlags = u32; +pub const DEVICE_NAME_USE_COMPONENT_NAME: DeviceNameFlags = 0x00000001; +pub const DEVICE_NAME_USE_DEVICE_PATH: DeviceNameFlags = 0x00000002; + +#[repr(C)] +pub struct ListEntry { + pub flink: *mut ListEntry, + pub blink: *mut ListEntry, +} + +#[repr(C)] +pub struct FileInfo { + pub link: ListEntry, + pub status: crate::base::Status, + pub full_name: *mut crate::base::Char16, + pub file_name: *mut crate::base::Char16, + pub handle: FileHandle, + pub info: *mut crate::protocols::file::Info, +} + +pub type Execute = eficall! {fn( + *mut crate::base::Handle, + *mut crate::base::Char16, + *mut *mut crate::base::Char16, + *mut crate::base::Status, +) -> crate::base::Status}; + +pub type GetEnv = eficall! {fn( + *mut crate::base::Char16, +) -> *mut crate::base::Char16}; + +pub type SetEnv = eficall! {fn( + *mut crate::base::Char16, + *mut crate::base::Char16, + crate::base::Boolean, +) -> crate::base::Status}; + +pub type GetAlias = eficall! {fn( + *mut crate::base::Char16, + *mut crate::base::Boolean, +) -> *mut crate::base::Char16}; + +pub type SetAlias = eficall! {fn( + *mut crate::base::Char16, + *mut crate::base::Char16, + crate::base::Boolean, + crate::base::Boolean, +) -> crate::base::Status}; + +pub type GetHelpText = eficall! {fn( + *mut crate::base::Char16, + *mut crate::base::Char16, + *mut *mut crate::base::Char16, +) -> crate::base::Status}; + +pub type GetDevicePathFromMap = eficall! {fn( + *mut crate::base::Char16, +) -> *mut crate::protocols::device_path::Protocol}; + +pub type GetMapFromDevicePath = eficall! {fn( + *mut *mut crate::protocols::device_path::Protocol, +) -> *mut crate::base::Char16}; + +pub type GetDevicePathFromFilePath = eficall! {fn( + *mut crate::base::Char16, +) -> *mut crate::protocols::device_path::Protocol}; + +pub type GetFilePathFromDevicePath = eficall! {fn( + *mut crate::protocols::device_path::Protocol, +) -> *mut crate::base::Char16}; + +pub type SetMap = eficall! {fn( + *mut crate::protocols::device_path::Protocol, + *mut crate::base::Char16, +) -> crate::base::Status}; + +pub type GetCurDir = eficall! {fn( + *mut crate::base::Char16, +) -> *mut crate::base::Char16}; + +pub type SetCurDir = eficall! {fn( + *mut crate::base::Char16, + *mut crate::base::Char16, +) -> crate::base::Status}; + +pub type OpenFileList = eficall! {fn( + *mut crate::base::Char16, + u64, + *mut *mut FileInfo, +) -> crate::base::Status}; + +pub type FreeFileList = eficall! {fn( + *mut *mut FileInfo, +) -> crate::base::Status}; + +pub type RemoveDupInFileList = eficall! {fn( + *mut *mut FileInfo, +) -> crate::base::Status}; + +pub type BatchIsActive = eficall! {fn() -> crate::base::Boolean}; + +pub type IsRootShell = eficall! {fn() -> crate::base::Boolean}; + +pub type EnablePageBreak = eficall! {fn()}; + +pub type DisablePageBreak = eficall! {fn()}; + +pub type GetPageBreak = eficall! {fn() -> crate::base::Boolean}; + +pub type GetDeviceName = eficall! {fn( + crate::base::Handle, + DeviceNameFlags, + *mut crate::base::Char8, + *mut *mut crate::base::Char16, +) -> crate::base::Status}; + +pub type GetFileInfo = eficall! {fn( + FileHandle, +) -> *mut crate::protocols::file::Info}; + +pub type SetFileInfo = eficall! {fn( + FileHandle, + *mut crate::protocols::file::Info +) -> crate::base::Status}; + +pub type OpenFileByName = eficall! {fn( + *mut crate::base::Char16, + *mut FileHandle, + u64, +) -> crate::base::Status}; + +pub type CloseFile = eficall! {fn( + FileHandle, +) -> crate::base::Status}; + +pub type CreateFile = eficall! {fn( + *mut crate::base::Char16, + u64, + *mut FileHandle, +) -> crate::base::Status}; + +pub type ReadFile = eficall! {fn( + FileHandle, + *mut usize, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type WriteFile = eficall! {fn( + FileHandle, + *mut usize, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type DeleteFile = eficall! {fn( + FileHandle, +) -> crate::base::Status}; + +pub type DeleteFileByName = eficall! {fn( + *mut crate::base::Char16, +) -> crate::base::Status}; + +pub type GetFilePosition = eficall! {fn( + FileHandle, + *mut u64, +) -> crate::base::Status}; + +pub type SetFilePosition = eficall! {fn( + FileHandle, + u64, +) -> crate::base::Status}; + +pub type FlushFile = eficall! {fn( + FileHandle, +) -> crate::base::Status}; + +pub type FindFiles = eficall! {fn( + *mut crate::base::Char16, + *mut *mut FileInfo, +) -> crate::base::Status}; + +pub type FindFilesInDir = eficall! {fn( + FileHandle, + *mut *mut FileInfo, +) -> crate::base::Status}; + +pub type GetFileSize = eficall! {fn( + FileHandle, + *mut u64, +) -> crate::base::Status}; + +pub type OpenRoot = eficall! {fn( + *mut crate::protocols::device_path::Protocol, + *mut FileHandle, +) -> crate::base::Status}; + +pub type OpenRootByHandle = eficall! {fn( + crate::base::Handle, + *mut FileHandle, +) -> crate::base::Status}; + +pub type RegisterGuidName = eficall! {fn( + *mut crate::base::Guid, + *mut crate::base::Char16, +) -> crate::base::Status}; + +pub type GetGuidName = eficall! {fn( + *mut crate::base::Guid, + *mut *mut crate::base::Char16, +) -> crate::base::Status}; + +pub type GetGuidFromName = eficall! {fn( + *mut crate::base::Char16, + *mut crate::base::Guid, +) -> crate::base::Status}; + +pub type GetEnvEx = eficall! {fn( + *mut crate::base::Char16, + *mut u32, +) -> *mut crate::base::Char16}; + +#[repr(C)] +pub struct Protocol { + pub execute: Execute, + pub get_env: GetEnv, + pub set_env: SetEnv, + pub get_alias: GetAlias, + pub set_alias: SetAlias, + pub get_help_text: GetHelpText, + pub get_device_path_from_map: GetDevicePathFromMap, + pub get_map_from_device_path: GetMapFromDevicePath, + pub get_device_path_from_file_path: GetDevicePathFromFilePath, + pub get_file_path_from_device_path: GetFilePathFromDevicePath, + pub set_map: SetMap, + + pub get_cur_dir: GetCurDir, + pub set_cur_dir: SetCurDir, + pub open_file_list: OpenFileList, + pub free_file_list: FreeFileList, + pub remove_dup_in_file_list: RemoveDupInFileList, + + pub batch_is_active: BatchIsActive, + pub is_root_shell: IsRootShell, + pub enable_page_break: EnablePageBreak, + pub disable_page_break: DisablePageBreak, + pub get_page_break: GetPageBreak, + pub get_device_name: GetDeviceName, + + pub get_file_info: GetFileInfo, + pub set_file_info: SetFileInfo, + pub open_file_by_name: OpenFileByName, + pub close_file: CloseFile, + pub create_file: CreateFile, + pub read_file: ReadFile, + pub write_file: WriteFile, + pub delete_file: DeleteFile, + pub delete_file_by_name: DeleteFileByName, + pub get_file_position: GetFilePosition, + pub set_file_position: SetFilePosition, + pub flush_file: FlushFile, + pub find_files: FindFiles, + pub find_files_in_dir: FindFilesInDir, + pub get_file_size: GetFileSize, + + pub open_root: OpenRoot, + pub open_root_by_handle: OpenRootByHandle, + + pub execution_break: crate::base::Event, + + pub major_version: u32, + pub minor_version: u32, + pub register_guid_name: RegisterGuidName, + pub get_guid_name: GetGuidName, + pub get_guid_from_name: GetGuidFromName, + + // Shell 2.1 + pub get_env_ex: GetEnvEx, +} diff --git a/tools/vendor/r-efi/src/protocols/shell_dynamic_command.rs b/tools/vendor/r-efi/src/protocols/shell_dynamic_command.rs new file mode 100644 index 0000000000..a1d775915c --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/shell_dynamic_command.rs @@ -0,0 +1,33 @@ +//! Shell Dynamic Command Protocol +//! +//! Defined in UEFI Shell Specification, Section 2.4 + +use super::{shell, shell_parameters}; + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x3c7200e9, + 0x005f, + 0x4ea4, + 0x87, + 0xde, + &[0xa3, 0xdf, 0xac, 0x8a, 0x27, 0xc3], +); + +pub type CommandHandler = eficall! {fn( + *mut Protocol, + *mut crate::system::SystemTable, + *mut shell_parameters::Protocol, + *mut shell::Protocol, +) -> crate::base::Status}; + +pub type CommandGetHelp = eficall! {fn( + *mut Protocol, + *mut crate::base::Char8, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub command_name: *mut crate::base::Char16, + pub handler: CommandHandler, + pub get_help: CommandGetHelp, +} diff --git a/tools/vendor/r-efi/src/protocols/shell_parameters.rs b/tools/vendor/r-efi/src/protocols/shell_parameters.rs new file mode 100644 index 0000000000..e779d0add5 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/shell_parameters.rs @@ -0,0 +1,23 @@ +//! Shell Parameters Protocol +//! +//! Defined in the UEFI Shell Specification, Section 2.3. + +use super::shell; + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x752f3136, + 0x4e16, + 0x4fdc, + 0xa2, + 0x2a, + &[0xe5, 0xf4, 0x68, 0x12, 0xf4, 0xca], +); + +#[repr(C)] +pub struct Protocol { + pub argv: *mut *mut crate::base::Char16, + pub argc: usize, + pub std_in: shell::FileHandle, + pub std_out: shell::FileHandle, + pub std_err: shell::FileHandle, +} diff --git a/tools/vendor/r-efi/src/protocols/simple_file_system.rs b/tools/vendor/r-efi/src/protocols/simple_file_system.rs new file mode 100644 index 0000000000..0f0947b9b1 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/simple_file_system.rs @@ -0,0 +1,26 @@ +//! Simple File System Protocol +//! +//! Provides the `open_volume` function returning a file protocol representing the root directory +//! of a filesystem. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x964e5b22, + 0x6459, + 0x11d2, + 0x8e, + 0x39, + &[0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b], +); + +pub const REVISION: u64 = 0x0000000000010000u64; + +pub type ProtocolOpenVolume = eficall! {fn( + *mut Protocol, + *mut *mut crate::protocols::file::Protocol, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub revision: u64, + pub open_volume: ProtocolOpenVolume, +} diff --git a/tools/vendor/r-efi/src/protocols/simple_network.rs b/tools/vendor/r-efi/src/protocols/simple_network.rs new file mode 100644 index 0000000000..883ff31b48 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/simple_network.rs @@ -0,0 +1,196 @@ +//! Simple Network Protocol +//! +//! The simple network protcol provides services to initialize a network interface, transmit +//! packets, receive packets, and close a network interface. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xa19832b9, + 0xac25, + 0x11d3, + 0x9a, + 0x2d, + &[0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d], +); + +pub const REVISION: u64 = 0x0000000000010000u64; + +pub const MAX_MCAST_FILTER_CNT: usize = 16; + +pub const RECEIVE_UNICAST: u32 = 0x00000001u32; +pub const RECEIVE_MULTICAST: u32 = 0x00000002u32; +pub const RECEIVE_BROADCAST: u32 = 0x00000004u32; +pub const RECEIVE_PROMISCUOUS: u32 = 0x00000008u32; +pub const RECEIVE_PROMISCUOUS_MULTICAST: u32 = 0x00000010u32; + +pub const RECEIVE_INTERRUPT: u32 = 0x00000001u32; +pub const TRANSMIT_INTERRUPT: u32 = 0x00000002u32; +pub const COMMAND_INTERRUPT: u32 = 0x00000004u32; +pub const SOFTWARE_INTERRUPT: u32 = 0x000000008u32; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Mode { + pub state: u32, + pub hw_address_size: u32, + pub media_header_size: u32, + pub max_packet_size: u32, + pub nvram_size: u32, + pub nvram_access_size: u32, + pub receive_filter_mask: u32, + pub receive_filter_setting: u32, + pub max_mcast_filter_count: u32, + pub mcast_filter_count: u32, + pub mcast_filter: [crate::base::MacAddress; MAX_MCAST_FILTER_CNT], + pub current_address: crate::base::MacAddress, + pub broadcast_address: crate::base::MacAddress, + pub permanent_address: crate::base::MacAddress, + pub if_type: u8, + pub mac_address_changeable: crate::base::Boolean, + pub multiple_tx_supported: crate::base::Boolean, + pub media_present_supported: crate::base::Boolean, + pub media_present: crate::base::Boolean, +} + +pub type State = u32; + +pub const STOPPED: State = 0x00000000; +pub const STARTED: State = 0x00000001; +pub const INITIALIZED: State = 0x00000002; +pub const MAX_STATE: State = 0x00000003; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Statistics { + pub rx_total_frames: u64, + pub rx_good_frames: u64, + pub rx_undersize_frames: u64, + pub rx_oversize_frames: u64, + pub rx_dropped_frames: u64, + pub rx_unicast_frames: u64, + pub rx_broadcast_frames: u64, + pub rx_multicast_frames: u64, + pub rx_crc_error_frames: u64, + pub rx_total_bytes: u64, + pub tx_total_frames: u64, + pub tx_good_frames: u64, + pub tx_undersize_frames: u64, + pub tx_oversize_frames: u64, + pub tx_dropped_frames: u64, + pub tx_unicast_frames: u64, + pub tx_broadcast_frames: u64, + pub tx_multicast_frames: u64, + pub tx_crc_error_frames: u64, + pub tx_total_bytes: u64, + pub collisions: u64, + pub unsupported_protocol: u64, + pub rx_duplicated_frames: u64, + pub rx_decrypt_error_frames: u64, + pub tx_error_frames: u64, + pub tx_retry_frames: u64, +} + +pub type ProtocolStart = eficall! {fn( + *mut Protocol, +) -> crate::base::Status}; + +pub type ProtocolStop = eficall! {fn( + *mut Protocol, +) -> crate::base::Status}; + +pub type ProtocolInitialize = eficall! {fn( + *mut Protocol, + usize, + usize, +) -> crate::base::Status}; + +pub type ProtocolReset = eficall! {fn( + *mut Protocol, + crate::base::Boolean, +) -> crate::base::Status}; + +pub type ProtocolShutdown = eficall! {fn( + *mut Protocol, +) -> crate::base::Status}; + +pub type ProtocolReceiveFilters = eficall! {fn( + *mut Protocol, + u32, + u32, + crate::base::Boolean, + usize, + *mut crate::base::MacAddress, +) -> crate::base::Status}; + +pub type ProtocolStationAddress = eficall! {fn( + *mut Protocol, + crate::base::Boolean, + *mut crate::base::MacAddress, +) -> crate::base::Status}; + +pub type ProtocolStatistics = eficall! {fn( + *mut Protocol, + crate::base::Boolean, + *mut usize, + *mut Statistics, +) -> crate::base::Status}; + +pub type ProtocolMcastIpToMac = eficall! {fn( + *mut Protocol, + crate::base::Boolean, + *mut crate::base::IpAddress, + *mut crate::base::MacAddress, +) -> crate::base::Status}; + +pub type ProtocolNvData = eficall! {fn( + *mut Protocol, + crate::base::Boolean, + usize, + usize, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type ProtocolGetStatus = eficall! {fn( + *mut Protocol, + *mut u32, + *mut *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type ProtocolTransmit = eficall! {fn( + *mut Protocol, + usize, + usize, + *mut core::ffi::c_void, + *mut crate::base::MacAddress, + *mut crate::base::MacAddress, + *mut u16, +) -> crate::base::Status}; + +pub type ProtocolReceive = eficall! {fn( + *mut Protocol, + *mut usize, + *mut usize, + *mut core::ffi::c_void, + *mut crate::base::MacAddress, + *mut crate::base::MacAddress, + *mut u16, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub revision: u64, + pub start: ProtocolStart, + pub stop: ProtocolStop, + pub initialize: ProtocolInitialize, + pub reset: ProtocolReset, + pub shutdown: ProtocolShutdown, + pub receive_filters: ProtocolReceiveFilters, + pub station_address: ProtocolStationAddress, + pub statistics: ProtocolStatistics, + pub mcast_ip_to_mac: ProtocolMcastIpToMac, + pub nv_data: ProtocolNvData, + pub get_status: ProtocolGetStatus, + pub transmit: ProtocolTransmit, + pub receive: ProtocolReceive, + pub wait_for_packet: crate::base::Event, + pub mode: *mut Mode, +} diff --git a/tools/vendor/r-efi/src/protocols/simple_text_input.rs b/tools/vendor/r-efi/src/protocols/simple_text_input.rs new file mode 100644 index 0000000000..30f4104efc --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/simple_text_input.rs @@ -0,0 +1,38 @@ +//! Simple Text Input Protocol +//! +//! The simple-text-input protocol defines how to read basic key-strokes. It is limited to +//! non-modifiers and lacks any detailed reporting. It is mostly useful for debugging and admin +//! interaction. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x387477c1, + 0x69c7, + 0x11d2, + 0x8e, + 0x39, + &[0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b], +); + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +pub struct InputKey { + pub scan_code: u16, + pub unicode_char: crate::base::Char16, +} + +pub type ProtocolReset = eficall! {fn( + *mut Protocol, + crate::base::Boolean, +) -> crate::base::Status}; + +pub type ProtocolReadKeyStroke = eficall! {fn( + *mut Protocol, + *mut InputKey, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub reset: ProtocolReset, + pub read_key_stroke: ProtocolReadKeyStroke, + pub wait_for_key: crate::base::Event, +} diff --git a/tools/vendor/r-efi/src/protocols/simple_text_input_ex.rs b/tools/vendor/r-efi/src/protocols/simple_text_input_ex.rs new file mode 100644 index 0000000000..f0baf8a97c --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/simple_text_input_ex.rs @@ -0,0 +1,85 @@ +//! Extended Simple Text Input Protocol +//! +//! The simple-text-input-ex protocol extends the simple-text-input protocol by allowing more +//! details reporting about modifiers, etc. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xdd9e7534, + 0x7762, + 0x4698, + 0x8c, + 0x14, + &[0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa], +); + +pub const SHIFT_STATE_VALID: u32 = 0x80000000u32; +pub const RIGHT_SHIFT_PRESSED: u32 = 0x00000001u32; +pub const LEFT_SHIFT_PRESSED: u32 = 0x00000002u32; +pub const RIGHT_CONTROL_PRESSED: u32 = 0x00000004u32; +pub const LEFT_CONTROL_PRESSED: u32 = 0x00000008u32; +pub const RIGHT_ALT_PRESSED: u32 = 0x00000010u32; +pub const LEFT_ALT_PRESSED: u32 = 0x00000020u32; +pub const RIGHT_LOGO_PRESSED: u32 = 0x00000040u32; +pub const LEFT_LOGO_PRESSED: u32 = 0x00000080u32; +pub const MENU_KEY_PRESSED: u32 = 0x00000100u32; +pub const SYS_REQ_PRESSED: u32 = 0x00000200u32; + +pub const TOGGLE_STATE_VALID: u8 = 0x80u8; +pub const KEY_STATE_EXPOSED: u8 = 0x40u8; +pub const SCROLL_LOCK_ACTIVE: u8 = 0x01u8; +pub const NUM_LOCK_ACTIVE: u8 = 0x02u8; +pub const CAPS_LOCK_ACTIVE: u8 = 0x04u8; + +pub type KeyToggleState = u8; +pub type KeyNotifyFunction = eficall! {fn(*mut KeyData) -> crate::base::Status}; + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +pub struct KeyState { + pub key_shift_state: u32, + pub key_toggle_state: KeyToggleState, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +pub struct KeyData { + pub key: crate::protocols::simple_text_input::InputKey, + pub key_state: KeyState, +} + +pub type ProtocolReset = eficall! {fn( + *mut Protocol, + crate::base::Boolean, +) -> crate::base::Status}; + +pub type ProtocolReadKeyStrokeEx = eficall! {fn( + *mut Protocol, + *mut KeyData, +) -> crate::base::Status}; + +pub type ProtocolSetState = eficall! {fn( + *mut Protocol, + *mut KeyToggleState, +) -> crate::base::Status}; + +pub type ProtocolRegisterKeyNotify = eficall! {fn( + *mut Protocol, + *mut KeyData, + KeyNotifyFunction, + *mut *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type ProtocolUnregisterKeyNotify = eficall! {fn( + *mut Protocol, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub reset: ProtocolReset, + pub read_key_stroke_ex: ProtocolReadKeyStrokeEx, + pub wait_for_key_ex: crate::base::Event, + pub set_state: ProtocolSetState, + pub register_key_notify: ProtocolRegisterKeyNotify, + pub unregister_key_notify: ProtocolUnregisterKeyNotify, +} diff --git a/tools/vendor/r-efi/src/protocols/simple_text_output.rs b/tools/vendor/r-efi/src/protocols/simple_text_output.rs new file mode 100644 index 0000000000..d100736db2 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/simple_text_output.rs @@ -0,0 +1,86 @@ +//! Simple Text Output Protocol +//! +//! The simple-text-output protocol provides a simple way to print text on screen. It is modeled +//! around the old VGA-consoles, but does not carry all the old cruft. It expects a rectangular +//! text array and allows you to move the cursor around to write Unicode symbols to screen. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x387477c2, + 0x69c7, + 0x11d2, + 0x8e, + 0x39, + &[0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b], +); + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Mode { + pub max_mode: i32, + pub mode: i32, + pub attribute: i32, + pub cursor_column: i32, + pub cursor_row: i32, + pub cursor_visible: crate::base::Boolean, +} + +pub type ProtocolReset = eficall! {fn( + *mut Protocol, + crate::base::Boolean, +) -> crate::base::Status}; + +pub type ProtocolOutputString = eficall! {fn( + *mut Protocol, + *mut crate::base::Char16, +) -> crate::base::Status}; + +pub type ProtocolTestString = eficall! {fn( + *mut Protocol, + *mut crate::base::Char16, +) -> crate::base::Status}; + +pub type ProtocolQueryMode = eficall! {fn( + *mut Protocol, + usize, + *mut usize, + *mut usize, +) -> crate::base::Status}; + +pub type ProtocolSetMode = eficall! {fn( + *mut Protocol, + usize, +) -> crate::base::Status}; + +pub type ProtocolSetAttribute = eficall! {fn( + *mut Protocol, + usize, +) -> crate::base::Status}; + +pub type ProtocolClearScreen = eficall! {fn( + *mut Protocol, +) -> crate::base::Status}; + +pub type ProtocolSetCursorPosition = eficall! {fn( + *mut Protocol, + usize, + usize, +) -> crate::base::Status}; + +pub type ProtocolEnableCursor = eficall! {fn( + *mut Protocol, + crate::base::Boolean, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub reset: ProtocolReset, + pub output_string: ProtocolOutputString, + pub test_string: ProtocolTestString, + pub query_mode: ProtocolQueryMode, + pub set_mode: ProtocolSetMode, + pub set_attribute: ProtocolSetAttribute, + pub clear_screen: ProtocolClearScreen, + pub set_cursor_position: ProtocolSetCursorPosition, + pub enable_cursor: ProtocolEnableCursor, + pub mode: *mut Mode, +} diff --git a/tools/vendor/r-efi/src/protocols/tcp4.rs b/tools/vendor/r-efi/src/protocols/tcp4.rs new file mode 100644 index 0000000000..051b1d8eff --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/tcp4.rs @@ -0,0 +1,224 @@ +//! Transmission Control Protocol version 4 +//! +//! It provides services to send and receive data streams. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x65530bc7, + 0xa359, + 0x410f, + 0xb0, + 0x10, + &[0x5a, 0xad, 0xc7, 0xec, 0x2b, 0x62], +); + +pub const SERVICE_BINDING_PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x00720665, + 0x67eb, + 0x4a99, + 0xba, + 0xf7, + &[0xd3, 0xc3, 0x3a, 0x1c, 0x7c, 0xc9], +); + +pub type ConnectionState = u32; + +pub const STATE_CLOSED: ConnectionState = 0x00000000; +pub const STATE_LISTEN: ConnectionState = 0x00000001; +pub const STATE_SYN_SENT: ConnectionState = 0x00000002; +pub const STATE_SYN_RECEIVED: ConnectionState = 0x00000003; +pub const STATE_ESTABLISHED: ConnectionState = 0x00000004; +pub const STATE_FIN_WAIT1: ConnectionState = 0x00000005; +pub const STATE_FIN_WAIT2: ConnectionState = 0x00000006; +pub const STATE_CLOSING: ConnectionState = 0x00000007; +pub const STATE_TIME_WAIT: ConnectionState = 0x00000008; +pub const STATE_CLOSE_WAIT: ConnectionState = 0x00000009; +pub const STATE_LAST_ACK: ConnectionState = 0x0000000a; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ConfigData { + pub type_of_service: u8, + pub time_to_live: u8, + pub access_point: AccessPoint, + pub control_option: *mut r#Option, +} + +impl Default for ConfigData { + fn default() -> Self { + Self { + type_of_service: Default::default(), + time_to_live: Default::default(), + access_point: Default::default(), + control_option: core::ptr::null_mut(), + } + } +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +pub struct AccessPoint { + pub use_default_address: crate::base::Boolean, + pub station_address: crate::base::Ipv4Address, + pub subnet_mask: crate::base::Ipv4Address, + pub station_port: u16, + pub remote_address: crate::base::Ipv4Address, + pub remote_port: u16, + pub active_flag: crate::base::Boolean, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct r#Option { + pub receive_buffer_size: u32, + pub send_buffer_size: u32, + pub max_syn_back_log: u32, + pub connection_timeout: u32, + pub data_retries: u32, + pub fin_timeout: u32, + pub time_wait_timeout: u32, + pub keep_alive_probes: u32, + pub keep_alive_time: u32, + pub keep_alive_interval: u32, + pub enable_nagle: crate::base::Boolean, + pub enable_time_stamp: crate::base::Boolean, + pub enable_window_scaling: crate::base::Boolean, + pub enable_selective_ack: crate::base::Boolean, + pub enable_path_mtu_discovery: crate::base::Boolean, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct CompletionToken { + pub event: crate::base::Event, + pub status: crate::base::Status, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ConnectionToken { + pub completion_token: CompletionToken, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ListenToken { + pub completion_token: CompletionToken, + pub new_child_handle: crate::base::Handle, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IoToken { + pub completion_token: CompletionToken, + pub packet: IoTokenPacket, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union IoTokenPacket { + pub rx_data: *mut ReceiveData, + pub tx_data: *mut TransmitData, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ReceiveData { + pub urgent_flag: crate::base::Boolean, + pub data_length: u32, + pub fragment_count: u32, + pub fragment_table: [FragmentData; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct FragmentData { + pub fragment_length: u32, + pub fragment_buffer: *mut core::ffi::c_void, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct TransmitData { + pub push: crate::base::Boolean, + pub urgent: crate::base::Boolean, + pub data_length: u32, + pub fragment_count: u32, + pub fragment_table: [FragmentData; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct CloseToken { + pub completion_token: CompletionToken, + pub abort_on_close: crate::base::Boolean, +} + +pub type ProtocolGetModeData = eficall! {fn( + *mut Protocol, + *mut ConnectionState, + *mut ConfigData, + *mut crate::protocols::ip4::ModeData, + *mut crate::protocols::managed_network::ConfigData, + *mut crate::protocols::simple_network::Mode, +) -> crate::base::Status}; + +pub type ProtocolConfigure = eficall! {fn( + *mut Protocol, + *mut ConfigData, +) -> crate::base::Status}; + +pub type ProtocolRoutes = eficall! {fn( + *mut Protocol, + crate::base::Boolean, + *mut crate::base::Ipv4Address, + *mut crate::base::Ipv4Address, + *mut crate::base::Ipv4Address, +) -> crate::base::Status}; + +pub type ProtocolConnect = eficall! {fn( + *mut Protocol, + *mut ConnectionToken, +) -> crate::base::Status}; + +pub type ProtocolAccept = eficall! {fn( + *mut Protocol, + *mut ListenToken, +) -> crate::base::Status}; + +pub type ProtocolTransmit = eficall! {fn( + *mut Protocol, + *mut IoToken, +) -> crate::base::Status}; + +pub type ProtocolReceive = eficall! {fn( + *mut Protocol, + *mut IoToken, +) -> crate::base::Status}; + +pub type ProtocolClose = eficall! {fn( + *mut Protocol, + *mut CloseToken, +) -> crate::base::Status}; + +pub type ProtocolCancel = eficall! {fn( + *mut Protocol, + *mut CompletionToken, +) -> crate::base::Status}; + +pub type ProtocolPoll = eficall! {fn( + *mut Protocol, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub get_mode_data: ProtocolGetModeData, + pub configure: ProtocolConfigure, + pub routes: ProtocolRoutes, + pub connect: ProtocolConnect, + pub accept: ProtocolAccept, + pub transmit: ProtocolTransmit, + pub receive: ProtocolReceive, + pub close: ProtocolClose, + pub cancel: ProtocolCancel, + pub poll: ProtocolPoll, +} diff --git a/tools/vendor/r-efi/src/protocols/tcp6.rs b/tools/vendor/r-efi/src/protocols/tcp6.rs new file mode 100644 index 0000000000..428b5c3eb6 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/tcp6.rs @@ -0,0 +1,202 @@ +//! Transmission Control Protocol version 6 +//! +//! It provides services to send and receive data streams. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x46e44855, + 0xbd60, + 0x4ab7, + 0xab, + 0x0d, + &[0xa6, 0x79, 0xb9, 0x44, 0x7d, 0x77], +); + +pub const SERVICE_BINDING_PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xec20eb79, + 0x6c1a, + 0x4664, + 0x9a, + 0x0d, + &[0xd2, 0xe4, 0xcc, 0x16, 0xd6, 0x64], +); + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct AccessPoint { + pub station_address: crate::base::Ipv6Address, + pub station_port: u16, + pub remote_address: crate::base::Ipv6Address, + pub remote_port: u16, + pub active_flag: crate::base::Boolean, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct r#Option { + pub receive_buffer_size: u32, + pub send_buffer_size: u32, + pub max_syn_back_log: u32, + pub connection_timeout: u32, + pub data_retries: u32, + pub fin_timeout: u32, + pub time_wait_timeout: u32, + pub keep_alive_probes: u32, + pub keep_alive_time: u32, + pub keep_alive_interval: u32, + pub enable_nagle: crate::base::Boolean, + pub enable_time_stamp: crate::base::Boolean, + pub enable_window_scaling: crate::base::Boolean, + pub enable_selective_ack: crate::base::Boolean, + pub enable_path_mtu_discovery: crate::base::Boolean, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ConfigData { + pub traffic_class: u8, + pub hop_limit: u8, + pub access_point: AccessPoint, + pub control_option: *mut r#Option, +} + +pub type ConnectionState = u32; + +pub const STATE_CLOSED: ConnectionState = 0x00000000; +pub const STATE_LISTEN: ConnectionState = 0x00000001; +pub const STATE_SYN_SENT: ConnectionState = 0x00000002; +pub const STATE_SYN_RECEIVED: ConnectionState = 0x00000003; +pub const STATE_ESTABLISHED: ConnectionState = 0x00000004; +pub const STATE_FIN_WAIT1: ConnectionState = 0x00000005; +pub const STATE_FIN_WAIT2: ConnectionState = 0x00000006; +pub const STATE_CLOSING: ConnectionState = 0x00000007; +pub const STATE_TIME_WAIT: ConnectionState = 0x00000008; +pub const STATE_CLOSE_WAIT: ConnectionState = 0x00000009; +pub const STATE_LAST_ACK: ConnectionState = 0x0000000a; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct CompletionToken { + pub event: crate::base::Event, + pub status: crate::base::Status, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ConnectionToken { + pub completion_token: CompletionToken, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ListenToken { + pub completion_token: CompletionToken, + pub new_child_handle: crate::base::Handle, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct IoToken { + pub completion_token: CompletionToken, + pub packet: IoTokenPacket, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union IoTokenPacket { + pub rx_data: *mut ReceiveData, + pub tx_data: *mut TransmitData, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ReceiveData { + pub urgent_flag: crate::base::Boolean, + pub data_length: u32, + pub fragment_count: u32, + pub fragment_table: [FragmentData; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct FragmentData { + pub fragment_length: u32, + pub fragment_buffer: *mut core::ffi::c_void, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct TransmitData { + pub push: crate::base::Boolean, + pub urgent: crate::base::Boolean, + pub data_length: u32, + pub fragment_count: u32, + pub fragment_table: [FragmentData; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct CloseToken { + pub completion_token: CompletionToken, + pub abort_on_close: crate::base::Boolean, +} + +pub type ProtocolGetModeData = eficall! {fn( + *mut Protocol, + *mut ConnectionState, + *mut ConfigData, + *mut crate::protocols::ip6::ModeData, + *mut crate::protocols::managed_network::ConfigData, + *mut crate::protocols::simple_network::Mode, +) -> crate::base::Status}; + +pub type ProtocolConfigure = eficall! {fn( + *mut Protocol, + *mut ConfigData, +) -> crate::base::Status}; + +pub type ProtocolConnect = eficall! {fn( + *mut Protocol, + *mut ConnectionToken, +) -> crate::base::Status}; + +pub type ProtocolAccept = eficall! {fn( + *mut Protocol, + *mut ListenToken, +) -> crate::base::Status}; + +pub type ProtocolTransmit = eficall! {fn( + *mut Protocol, + *mut IoToken, +) -> crate::base::Status}; + +pub type ProtocolReceive = eficall! {fn( + *mut Protocol, + *mut IoToken, +) -> crate::base::Status}; + +pub type ProtocolClose = eficall! {fn( + *mut Protocol, + *mut CloseToken, +) -> crate::base::Status}; + +pub type ProtocolCancel = eficall! {fn( + *mut Protocol, + *mut CompletionToken, +) -> crate::base::Status}; + +pub type ProtocolPoll = eficall! {fn( + *mut Protocol, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub get_mode_data: ProtocolGetModeData, + pub configure: ProtocolConfigure, + pub connect: ProtocolConnect, + pub accept: ProtocolAccept, + pub transmit: ProtocolTransmit, + pub receive: ProtocolReceive, + pub close: ProtocolClose, + pub cancel: ProtocolCancel, + pub poll: ProtocolPoll, +} diff --git a/tools/vendor/r-efi/src/protocols/timestamp.rs b/tools/vendor/r-efi/src/protocols/timestamp.rs new file mode 100644 index 0000000000..3e8f6b5756 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/timestamp.rs @@ -0,0 +1,32 @@ +//! EFI Timestamp Protocol +//! +//! The Timestamp protocol provides a platform independent interface for +//! retrieving a high resolution timestamp counter. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xafbfde41, + 0x2e6e, + 0x4262, + 0xba, + 0x65, + &[0x62, 0xb9, 0x23, 0x6e, 0x54, 0x95], +); + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct Properties { + pub frequency: u64, + pub end_value: u64, +} + +pub type ProtocolGetTimestamp = eficall! {fn() -> u64}; + +pub type ProtocolGetProperties = eficall! {fn( + *mut Properties, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub get_timestamp: ProtocolGetTimestamp, + pub get_properties: ProtocolGetProperties, +} diff --git a/tools/vendor/r-efi/src/protocols/udp4.rs b/tools/vendor/r-efi/src/protocols/udp4.rs new file mode 100644 index 0000000000..f374235cec --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/udp4.rs @@ -0,0 +1,151 @@ +//! User Datagram Protocol V4 +//! +//! It provides simple packet-oriented services to transmit and receive UDP packets. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x3ad9df29, + 0x4501, + 0x478d, + 0xb1, + 0xf8, + &[0x7f, 0x7f, 0xe7, 0x0e, 0x50, 0xf3], +); + +pub const SERVICE_BINDING_PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x83f01464, + 0x99bd, + 0x45e5, + 0xb3, + 0x83, + &[0xaf, 0x63, 0x05, 0xd8, 0xe9, 0xe6], +); + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ConfigData { + pub accept_broadcast: crate::base::Boolean, + pub accept_promiscuous: crate::base::Boolean, + pub accept_any_port: crate::base::Boolean, + pub allow_duplicate_port: crate::base::Boolean, + pub type_of_service: u8, + pub time_to_live: u8, + pub do_not_fragment: crate::base::Boolean, + pub receive_timeout: u32, + pub transmit_timeout: u32, + pub use_default_address: crate::base::Boolean, + pub station_address: crate::base::Ipv4Address, + pub subnet_mask: crate::base::Ipv4Address, + pub station_port: u16, + pub remote_address: crate::base::Ipv4Address, + pub remote_port: u16, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct SessionData { + pub source_address: crate::base::Ipv4Address, + pub source_port: u16, + pub destination_address: crate::base::Ipv4Address, + pub destination_port: u16, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct FragmentData { + pub fragment_length: u32, + pub fragment_buffer: *mut core::ffi::c_void, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct ReceiveData { + pub time_stamp: crate::system::Time, + pub recycle_signal: crate::base::Event, + pub udp_session: SessionData, + pub data_length: u32, + pub fragment_count: u32, + pub fragment_table: [FragmentData; N], +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct TransmitData { + pub udp_session_data: *mut SessionData, + pub gateway_address: *mut crate::base::Ipv4Address, + pub data_length: u32, + pub fragment_count: u32, + pub fragment_table: [FragmentData; N], +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union CompletionTokenPacket { + pub rx_data: *mut ReceiveData, + pub tx_data: *mut TransmitData, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct CompletionToken { + pub event: crate::base::Event, + pub status: crate::base::Status, + pub packet: CompletionTokenPacket, +} + +pub type ProtocolGetModeData = eficall! {fn( + *mut Protocol, + *mut ConfigData, + *mut crate::protocols::ip4::ModeData, + *mut crate::protocols::managed_network::ConfigData, + *mut crate::protocols::simple_network::Mode, +) -> crate::base::Status}; + +pub type ProtocolConfigure = eficall! {fn( + *mut Protocol, + *mut ConfigData, +) -> crate::base::Status}; + +pub type ProtocolGroups = eficall! {fn( + *mut Protocol, + crate::base::Boolean, + *mut crate::base::Ipv4Address, +) -> crate::base::Status}; + +pub type ProtocolRoutes = eficall! {fn( + *mut Protocol, + crate::base::Boolean, + *mut crate::base::Ipv4Address, + *mut crate::base::Ipv4Address, + *mut crate::base::Ipv4Address, +) -> crate::base::Status}; + +pub type ProtocolTransmit = eficall! {fn( + *mut Protocol, + *mut CompletionToken, +) -> crate::base::Status}; + +pub type ProtocolReceive = eficall! {fn( + *mut Protocol, + *mut CompletionToken, +) -> crate::base::Status}; + +pub type ProtocolCancel = eficall! {fn( + *mut Protocol, + *mut CompletionToken, +) -> crate::base::Status}; + +pub type ProtocolPoll = eficall! {fn( + *mut Protocol, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub get_mode_data: ProtocolGetModeData, + pub configure: ProtocolConfigure, + pub groups: ProtocolGroups, + pub routes: ProtocolRoutes, + pub transmit: ProtocolTransmit, + pub receive: ProtocolReceive, + pub cancel: ProtocolCancel, + pub poll: ProtocolPoll, +} diff --git a/tools/vendor/r-efi/src/protocols/udp6.rs b/tools/vendor/r-efi/src/protocols/udp6.rs new file mode 100644 index 0000000000..796863be45 --- /dev/null +++ b/tools/vendor/r-efi/src/protocols/udp6.rs @@ -0,0 +1,137 @@ +//! User Datagram Protocol V6 +//! +//! It provides simple packet-oriented services to transmit and receive UDP packets. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x4f948815, + 0xb4b9, + 0x43cb, + 0x8a, + 0x33, + &[0x90, 0xe0, 0x60, 0xb3, 0x49, 0x55], +); + +pub const SERVICE_BINDING_PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x66ed4721, + 0x3c98, + 0x4d3e, + 0x81, + 0xe3, + &[0xd0, 0x3d, 0xd3, 0x9a, 0x72, 0x54], +); + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ConfigData { + pub accept_promiscuous: crate::base::Boolean, + pub accept_any_port: crate::base::Boolean, + pub allow_duplicate_port: crate::base::Boolean, + pub traffic_class: u8, + pub hop_limit: u8, + pub receive_timeout: u32, + pub transmit_timeout: u32, + pub station_address: crate::base::Ipv6Address, + pub station_port: u16, + pub remote_address: crate::base::Ipv6Address, + pub remote_port: u16, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct SessionData { + pub source_address: crate::base::Ipv6Address, + pub source_port: u16, + pub destination_address: crate::base::Ipv6Address, + pub destination_port: u16, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct FragmentData { + pub fragment_length: u32, + pub fragment_buffer: *mut core::ffi::c_void, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct ReceiveData { + pub time_stamp: crate::system::Time, + pub recycle_signal: crate::base::Event, + pub udp_session: SessionData, + pub data_length: u32, + pub fragment_count: u32, + pub fragment_table: [FragmentData; N], +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct TransmitData { + pub udp_session_data: *mut SessionData, + pub data_length: u32, + pub fragment_count: u32, + pub fragment_table: [FragmentData; N], +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union CompletionTokenPacket { + pub rx_data: *mut ReceiveData, + pub tx_data: *mut TransmitData, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct CompletionToken { + pub event: crate::base::Event, + pub status: crate::base::Status, + pub packet: CompletionTokenPacket, +} + +pub type ProtocolGetModeData = eficall! {fn( + *mut Protocol, + *mut ConfigData, + *mut crate::protocols::ip6::ModeData, + *mut crate::protocols::managed_network::ConfigData, + *mut crate::protocols::simple_network::Mode, +) -> crate::base::Status}; + +pub type ProtocolConfigure = eficall! {fn( + *mut Protocol, + *mut ConfigData, +) -> crate::base::Status}; + +pub type ProtocolGroups = eficall! {fn( + *mut Protocol, + crate::base::Boolean, + *mut crate::base::Ipv6Address, +) -> crate::base::Status}; + +pub type ProtocolTransmit = eficall! {fn( + *mut Protocol, + *mut CompletionToken, +) -> crate::base::Status}; + +pub type ProtocolReceive = eficall! {fn( + *mut Protocol, + *mut CompletionToken, +) -> crate::base::Status}; + +pub type ProtocolCancel = eficall! {fn( + *mut Protocol, + *mut CompletionToken, +) -> crate::base::Status}; + +pub type ProtocolPoll = eficall! {fn( + *mut Protocol, +) -> crate::base::Status}; + +#[repr(C)] +pub struct Protocol { + pub get_mode_data: ProtocolGetModeData, + pub configure: ProtocolConfigure, + pub groups: ProtocolGroups, + pub transmit: ProtocolTransmit, + pub receive: ProtocolReceive, + pub cancel: ProtocolCancel, + pub poll: ProtocolPoll, +} diff --git a/tools/vendor/r-efi/src/system.rs b/tools/vendor/r-efi/src/system.rs new file mode 100644 index 0000000000..28c4c43581 --- /dev/null +++ b/tools/vendor/r-efi/src/system.rs @@ -0,0 +1,1130 @@ +//! UEFI System Integration +//! +//! This header defines the structures and types of the surrounding system of an UEFI application. +//! It contains the definitions of the system table, the runtime and boot services, as well as +//! common types. +//! +//! We do not document the behavior of each of these types and functions. They follow the UEFI +//! specification, which does a well-enough job of documenting each. This file just provides you +//! the rust definitions of each symbol and some limited hints on some pecularities. + +// +// Time Management +// +// UEFI time management is modeled around the EFI_TIME structure, which represents any arbitrary +// timestamp. The runtime and boot services provide helper functions to query and set the system +// time. +// + +pub const TIME_ADJUST_DAYLIGHT: u8 = 0x01u8; +pub const TIME_IN_DAYLIGHT: u8 = 0x02u8; + +pub const UNSPECIFIED_TIMEZONE: i16 = 0x07ffi16; + +// Cannot derive `Eq` etc. due to uninitialized `pad2` field. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +pub struct Time { + pub year: u16, + pub month: u8, + pub day: u8, + pub hour: u8, + pub minute: u8, + pub second: u8, + pub pad1: u8, + pub nanosecond: u32, + pub timezone: i16, + pub daylight: u8, + pub pad2: u8, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct TimeCapabilities { + pub resolution: u32, + pub accuracy: u32, + pub sets_to_zero: crate::base::Boolean, +} + +// +// UEFI Variables +// +// UEFI systems provide a way to store global variables. These can be persistent or volatile. The +// variable store must be provided by the platform, but persistent storage might not be available. +// + +pub const VARIABLE_NON_VOLATILE: u32 = 0x00000001u32; +pub const VARIABLE_BOOTSERVICE_ACCESS: u32 = 0x00000002u32; +pub const VARIABLE_RUNTIME_ACCESS: u32 = 0x00000004u32; +pub const VARIABLE_HARDWARE_ERROR_RECORD: u32 = 0x00000008u32; +pub const VARIABLE_AUTHENTICATED_WRITE_ACCESS: u32 = 0x00000010u32; +pub const VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS: u32 = 0x00000020u32; +pub const VARIABLE_APPEND_WRITE: u32 = 0x00000040u32; +pub const VARIABLE_ENHANCED_AUTHENTICATED_ACCESS: u32 = 0x00000080u32; + +pub const VARIABLE_AUTHENTICATION_3_CERT_ID_SHA256: u32 = 0x1u32; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct VariableAuthentication3CertId { + pub r#type: u8, + pub id_size: u32, + pub id: [u8; N], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct VariableAuthentication { + pub monotonic_count: u64, + pub auth_info: [u8; N], // WIN_CERTIFICATE_UEFI_ID from PE/COFF +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct VariableAuthentication2 { + pub timestamp: Time, + pub auth_info: [u8; N], // WIN_CERTIFICATE_UEFI_ID from PE/COFF +} + +pub const VARIABLE_AUTHENTICATION_3_TIMESTAMP_TYPE: u32 = 0x1u32; +pub const VARIABLE_AUTHENTICATION_3_NONCE_TYPE: u32 = 0x2u32; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct VariableAuthentication3 { + pub version: u8, + pub r#type: u8, + pub metadata_size: u32, + pub flags: u32, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct VariableAuthentication3Nonce { + pub nonce_size: u32, + pub nonce: [u8; N], +} + +pub const HARDWARE_ERROR_VARIABLE_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x414E6BDD, + 0xE47B, + 0x47cc, + 0xB2, + 0x44, + &[0xBB, 0x61, 0x02, 0x0C, 0xF5, 0x16], +); + +// +// Virtual Mappings +// +// UEFI runs in an 1-to-1 mapping from virtual to physical addresses. But once you exit boot +// services, you can apply any address mapping you want, as long as you inform UEFI about it (or, +// alternatively, stop using the UEFI runtime services). +// + +pub const OPTIONAL_POINTER: u32 = 0x00000001u32; + +// +// System Reset +// +// UEFI provides access to firmware functions to reset the system. This includes a wide variety of +// different possible resets. +// + +pub type ResetType = u32; + +pub const RESET_COLD: ResetType = 0x00000000; +pub const RESET_WARM: ResetType = 0x00000001; +pub const RESET_SHUTDOWN: ResetType = 0x00000002; +pub const RESET_PLATFORM_SPECIFIC: ResetType = 0x00000003; + +// +// Update Capsules +// +// The process of firmware updates is generalized in UEFI. There are small blobs called capsules +// that you can push into the firmware to be run either immediately or on next reboot. +// + +#[repr(C)] +#[derive(Clone, Copy)] +pub union CapsuleBlockDescriptorUnion { + pub data_block: crate::base::PhysicalAddress, + pub continuation_pointer: crate::base::PhysicalAddress, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct CapsuleBlockDescriptor { + pub length: u64, + pub data: CapsuleBlockDescriptorUnion, +} + +pub const CAPSULE_FLAGS_PERSIST_ACROSS_RESET: u32 = 0x00010000u32; +pub const CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE: u32 = 0x00020000u32; +pub const CAPSULE_FLAGS_INITIATE_RESET: u32 = 0x00040000u32; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct CapsuleHeader { + pub capsule_guid: crate::base::Guid, + pub header_size: u32, + pub flags: u32, + pub capsule_image_size: u32, +} + +pub const OS_INDICATIONS_BOOT_TO_FW_UI: u64 = 0x0000000000000001u64; +pub const OS_INDICATIONS_TIMESTAMP_REVOCATION: u64 = 0x0000000000000002u64; +pub const OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED: u64 = 0x0000000000000004u64; +pub const OS_INDICATIONS_FMP_CAPSULE_SUPPORTED: u64 = 0x0000000000000008u64; +pub const OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED: u64 = 0x0000000000000010u64; +pub const OS_INDICATIONS_START_OS_RECOVERY: u64 = 0x0000000000000020u64; +pub const OS_INDICATIONS_START_PLATFORM_RECOVERY: u64 = 0x0000000000000040u64; + +pub const CAPSULE_REPORT_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x39b68c46, + 0xf7fb, + 0x441b, + 0xb6, + 0xec, + &[0x16, 0xb0, 0xf6, 0x98, 0x21, 0xf3], +); + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct CapsuleResultVariableHeader { + pub variable_total_size: u32, + pub reserved: u32, + pub capsule_guid: crate::base::Guid, + pub capsule_processed: Time, + pub capsule_status: crate::base::Status, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct CapsuleResultVariableFMP { + pub version: u16, + pub payload_index: u8, + pub update_image_index: u8, + pub update_image_type_id: crate::base::Guid, + pub capsule_file_name_and_target: [crate::base::Char16; N], +} + +// +// Tasks +// +// UEFI uses a simplified task model, and only ever runs on a single CPU. Usually, there is only +// one single task running on the system, which is the current execution. No interrupts are +// supported, other than timer interrupts. That is, all device management must be reliant on +// polling. +// +// You can, however, register callbacks to be run by the UEFI core. That is, either when execution +// is returned to the UEFI core, or when a timer interrupt fires, the scheduler will run the +// highest priority task next, interrupting the current task. You can use simple +// task-priority-levels (TPL) to adjust the priority of your callbacks and current task. +// + +pub const EVT_TIMER: u32 = 0x80000000u32; +pub const EVT_RUNTIME: u32 = 0x40000000u32; +pub const EVT_NOTIFY_WAIT: u32 = 0x00000100u32; +pub const EVT_NOTIFY_SIGNAL: u32 = 0x00000200u32; +pub const EVT_SIGNAL_EXIT_BOOT_SERVICES: u32 = 0x00000201u32; +pub const EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE: u32 = 0x60000202u32; + +pub type EventNotify = eficall! {fn(crate::base::Event, *mut core::ffi::c_void)}; + +pub const EVENT_GROUP_EXIT_BOOT_SERVICES: crate::base::Guid = crate::base::Guid::from_fields( + 0x27abf055, + 0xb1b8, + 0x4c26, + 0x80, + 0x48, + &[0x74, 0x8f, 0x37, 0xba, 0xa2, 0xdf], +); +pub const EVENT_GROUP_BEFORE_EXIT_BOOT_SERVICES: crate::base::Guid = crate::base::Guid::from_fields( + 0x8be0e274, + 0x3970, + 0x4b44, + 0x80, + 0xc5, + &[0x1a, 0xb9, 0x50, 0x2f, 0x3b, 0xfc], +); +pub const EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE: crate::base::Guid = crate::base::Guid::from_fields( + 0x13fa7698, + 0xc831, + 0x49c7, + 0x87, + 0xea, + &[0x8f, 0x43, 0xfc, 0xc2, 0x51, 0x96], +); +pub const EVENT_GROUP_MEMORY_MAP_CHANGE: crate::base::Guid = crate::base::Guid::from_fields( + 0x78bee926, + 0x692f, + 0x48fd, + 0x9e, + 0xdb, + &[0x1, 0x42, 0x2e, 0xf0, 0xd7, 0xab], +); +pub const EVENT_GROUP_READY_TO_BOOT: crate::base::Guid = crate::base::Guid::from_fields( + 0x7ce88fb3, + 0x4bd7, + 0x4679, + 0x87, + 0xa8, + &[0xa8, 0xd8, 0xde, 0xe5, 0x0d, 0x2b], +); +pub const EVENT_GROUP_AFTER_READY_TO_BOOT: crate::base::Guid = crate::base::Guid::from_fields( + 0x3a2a00ad, + 0x98b9, + 0x4cdf, + 0xa4, + 0x78, + &[0x70, 0x27, 0x77, 0xf1, 0xc1, 0x0b], +); +pub const EVENT_GROUP_RESET_SYSTEM: crate::base::Guid = crate::base::Guid::from_fields( + 0x62da6a56, + 0x13fb, + 0x485a, + 0xa8, + 0xda, + &[0xa3, 0xdd, 0x79, 0x12, 0xcb, 0x6b], +); + +pub type TimerDelay = u32; + +pub const TIMER_CANCEL: TimerDelay = 0x00000000; +pub const TIMER_PERIODIC: TimerDelay = 0x00000001; +pub const TIMER_RELATIVE: TimerDelay = 0x00000002; + +pub const TPL_APPLICATION: crate::base::Tpl = 4; +pub const TPL_CALLBACK: crate::base::Tpl = 8; +pub const TPL_NOTIFY: crate::base::Tpl = 16; +pub const TPL_HIGH_LEVEL: crate::base::Tpl = 31; + +// +// Memory management +// +// The UEFI boot services provide you pool-allocation helpers to reserve memory. The region for +// each allocation can be selected by the caller, allowing to reserve memory that even survives +// beyond boot services. However, dynamic allocations can only performed via boot services, so no +// dynamic modifications can be done once you exit boot services. +// + +pub type AllocateType = u32; + +pub const ALLOCATE_ANY_PAGES: AllocateType = 0x00000000; +pub const ALLOCATE_MAX_ADDRESS: AllocateType = 0x00000001; +pub const ALLOCATE_ADDRESS: AllocateType = 0x00000002; + +pub type MemoryType = u32; + +pub const RESERVED_MEMORY_TYPE: MemoryType = 0x00000000; +pub const LOADER_CODE: MemoryType = 0x00000001; +pub const LOADER_DATA: MemoryType = 0x00000002; +pub const BOOT_SERVICES_CODE: MemoryType = 0x00000003; +pub const BOOT_SERVICES_DATA: MemoryType = 0x00000004; +pub const RUNTIME_SERVICES_CODE: MemoryType = 0x00000005; +pub const RUNTIME_SERVICES_DATA: MemoryType = 0x00000006; +pub const CONVENTIONAL_MEMORY: MemoryType = 0x00000007; +pub const UNUSABLE_MEMORY: MemoryType = 0x00000008; +pub const ACPI_RECLAIM_MEMORY: MemoryType = 0x00000009; +pub const ACPI_MEMORY_NVS: MemoryType = 0x0000000a; +pub const MEMORY_MAPPED_IO: MemoryType = 0x0000000b; +pub const MEMORY_MAPPED_IO_PORT_SPACE: MemoryType = 0x0000000c; +pub const PAL_CODE: MemoryType = 0x0000000d; +pub const PERSISTENT_MEMORY: MemoryType = 0x0000000e; +pub const UNACCEPTED_MEMORY_TYPE: MemoryType = 0x0000000f; + +pub const MEMORY_UC: u64 = 0x0000000000000001u64; +pub const MEMORY_WC: u64 = 0x0000000000000002u64; +pub const MEMORY_WT: u64 = 0x0000000000000004u64; +pub const MEMORY_WB: u64 = 0x0000000000000008u64; +pub const MEMORY_UCE: u64 = 0x0000000000000010u64; +pub const MEMORY_WP: u64 = 0x0000000000001000u64; +pub const MEMORY_RP: u64 = 0x0000000000002000u64; +pub const MEMORY_XP: u64 = 0x0000000000004000u64; +pub const MEMORY_NV: u64 = 0x0000000000008000u64; +pub const MEMORY_MORE_RELIABLE: u64 = 0x0000000000010000u64; +pub const MEMORY_RO: u64 = 0x0000000000020000u64; +pub const MEMORY_SP: u64 = 0x0000000000040000u64; +pub const MEMORY_CPU_CRYPTO: u64 = 0x0000000000080000u64; +pub const MEMORY_RUNTIME: u64 = 0x8000000000000000u64; +pub const MEMORY_ISA_VALID: u64 = 0x4000000000000000u64; +pub const MEMORY_ISA_MASK: u64 = 0x0FFFF00000000000u64; + +/// Mask of memory attributes that specify cacheability attributes. No symbol +/// is defined by the spec, but the attributes are annotated in the spec. Note +/// that `MEMORY_WP`, despite its name, is treated as cacheability attribute. +/// Use `MEMORY_RO` as replacement access attribute (see the spec for details). +pub const CACHE_ATTRIBUTE_MASK: u64 = + MEMORY_UC | MEMORY_WC | MEMORY_WT | MEMORY_WB | MEMORY_UCE | MEMORY_WP; + +/// Mask of memory attributes that specify access protection attributes. No +/// symbol is defined by the spec, but the attributes are annotated in the +/// spec. Note that `MEMORY_WP` is treated as cacheability attribute, and its +/// access protection functionality is replaced by `MEMORY_RO`. +pub const MEMORY_ACCESS_MASK: u64 = MEMORY_RP | MEMORY_XP | MEMORY_RO; + +/// Mask of memory attributes that specify properties of a memory region that +/// can be managed via the CPU architecture protocol. +pub const MEMORY_ATTRIBUTE_MASK: u64 = MEMORY_ACCESS_MASK | MEMORY_SP | MEMORY_CPU_CRYPTO; + +pub const MEMORY_DESCRIPTOR_VERSION: u32 = 0x00000001u32; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct MemoryDescriptor { + pub r#type: u32, + pub physical_start: crate::base::PhysicalAddress, + pub virtual_start: crate::base::VirtualAddress, + pub number_of_pages: u64, + pub attribute: u64, +} + +// +// Protocol Management +// +// The UEFI driver model provides ways to have bus-drivers, device-drivers, and applications as +// separate, independent entities. They use protocols to communicate, and handles to refer to +// common state. Drivers and devices can be registered dynamically at runtime, and can support +// hotplugging. +// + +pub type InterfaceType = u32; + +pub const NATIVE_INTERFACE: InterfaceType = 0x00000000; + +pub type LocateSearchType = u32; + +pub const ALL_HANDLES: LocateSearchType = 0x00000000; +pub const BY_REGISTER_NOTIFY: LocateSearchType = 0x00000001; +pub const BY_PROTOCOL: LocateSearchType = 0x00000002; + +pub const OPEN_PROTOCOL_BY_HANDLE_PROTOCOL: u32 = 0x00000001u32; +pub const OPEN_PROTOCOL_GET_PROTOCOL: u32 = 0x00000002u32; +pub const OPEN_PROTOCOL_TEST_PROTOCOL: u32 = 0x00000004u32; +pub const OPEN_PROTOCOL_BY_CHILD_CONTROLLER: u32 = 0x00000008u32; +pub const OPEN_PROTOCOL_BY_DRIVER: u32 = 0x00000010u32; +pub const OPEN_PROTOCOL_EXCLUSIVE: u32 = 0x00000020u32; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct OpenProtocolInformationEntry { + pub agent_handle: crate::base::Handle, + pub controller_handle: crate::base::Handle, + pub attributes: u32, + pub open_count: u32, +} + +// +// Configuration Tables +// +// The system table contains an array of auxiliary tables, indexed by their GUID, called +// configuration tables. Each table uses the generic ConfigurationTable structure as header. +// + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ConfigurationTable { + pub vendor_guid: crate::base::Guid, + pub vendor_table: *mut core::ffi::c_void, +} + +pub const RT_PROPERTIES_TABLE_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xeb66918a, + 0x7eef, + 0x402a, + 0x84, + 0x2e, + &[0x93, 0x1d, 0x21, 0xc3, 0x8a, 0xe9], +); + +pub const RT_PROPERTIES_TABLE_VERSION: u16 = 0x0001; + +pub const RT_SUPPORTED_GET_TIME: u32 = 0x0000001; +pub const RT_SUPPORTED_SET_TIME: u32 = 0x0000002; +pub const RT_SUPPORTED_GET_WAKEUP_TIME: u32 = 0x0000004; +pub const RT_SUPPORTED_SET_WAKEUP_TIME: u32 = 0x0000008; +pub const RT_SUPPORTED_GET_VARIABLE: u32 = 0x00000010; +pub const RT_SUPPORTED_GET_NEXT_VARIABLE_NAME: u32 = 0x00000020; +pub const RT_SUPPORTED_SET_VARIABLE: u32 = 0x00000040; +pub const RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP: u32 = 0x00000080; +pub const RT_SUPPORTED_CONVERT_POINTER: u32 = 0x00000100; +pub const RT_SUPPORTED_GET_NEXT_HIGH_MONOTONIC_COUNT: u32 = 0x00000200; +pub const RT_SUPPORTED_RESET_SYSTEM: u32 = 0x00000400; +pub const RT_SUPPORTED_UPDATE_CAPSULE: u32 = 0x00000800; +pub const RT_SUPPORTED_QUERY_CAPSULE_CAPABILITIES: u32 = 0x00001000; +pub const RT_SUPPORTED_QUERY_VARIABLE_INFO: u32 = 0x00002000; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct RtPropertiesTable { + pub version: u16, + pub length: u16, + pub runtime_services_supported: u32, +} + +pub const PROPERTIES_TABLE_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x880aaca3, + 0x4adc, + 0x4a04, + 0x90, + 0x79, + &[0xb7, 0x47, 0x34, 0x8, 0x25, 0xe5], +); + +pub const PROPERTIES_TABLE_VERSION: u32 = 0x00010000u32; + +pub const PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA: u64 = 0x1u64; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct PropertiesTable { + pub version: u32, + pub length: u32, + pub memory_protection_attribute: u64, +} + +pub const MEMORY_ATTRIBUTES_TABLE_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xdcfa911d, + 0x26eb, + 0x469f, + 0xa2, + 0x20, + &[0x38, 0xb7, 0xdc, 0x46, 0x12, 0x20], +); + +pub const MEMORY_ATTRIBUTES_TABLE_VERSION: u32 = 0x00000001u32; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct MemoryAttributesTable { + pub version: u32, + pub number_of_entries: u32, + pub descriptor_size: u32, + pub reserved: u32, + pub entry: [MemoryDescriptor; N], +} + +pub const CONFORMANCE_PROFILES_TABLE_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x36122546, + 0xf7e7, + 0x4c8f, + 0xbd, + 0x9b, + &[0xeb, 0x85, 0x25, 0xb5, 0x0c, 0x0b], +); + +pub const CONFORMANCE_PROFILES_TABLE_VERSION: u16 = 0x0001; + +pub const CONFORMANCE_PROFILES_UEFI_SPEC_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x523c91af, + 0xa195, + 0x4382, + 0x81, + 0x8d, + &[0x29, 0x5f, 0xe4, 0x00, 0x64, 0x65], +); + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct ConformanceProfilesTable { + pub version: u16, + pub number_of_profiles: u16, + pub conformance_profiles: [crate::base::Guid; N], +} + +// +// External Configuration Tables +// +// This lists the Guids of configuration tables of other industry standards, as listed in +// the UEFI specification. See each standard for details on the data included in each table. +// + +pub const ACPI_10_TABLE_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xeb9d2d30, + 0x2d88, + 0x11d3, + 0x9a, + 0x16, + &[0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d], +); + +pub const ACPI_20_TABLE_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x8868e871, + 0xe4f1, + 0x11d3, + 0xbc, + 0x22, + &[0x00, 0x80, 0xc7, 0x3c, 0x88, 0x81], +); + +pub const DTB_TABLE_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xb1b621d5, + 0xf19c, + 0x41a5, + 0x83, + 0x0b, + &[0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0], +); + +pub const JSON_CAPSULE_DATA_TABLE_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x35e7a725, + 0x8dd2, + 0x4cac, + 0x80, + 0x11, + &[0x33, 0xcd, 0xa8, 0x10, 0x90, 0x56], +); + +pub const JSON_CAPSULE_RESULT_TABLE_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xdbc461c3, + 0xb3de, + 0x422a, + 0xb9, + 0xb4, + &[0x98, 0x86, 0xfd, 0x49, 0xa1, 0xe5], +); + +pub const JSON_CONFIG_DATA_TABLE_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0x87367f87, + 0x1119, + 0x41ce, + 0xaa, + 0xec, + &[0x8b, 0xe0, 0x11, 0x1f, 0x55, 0x8a], +); + +pub const MPS_TABLE_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xeb9d2d2f, + 0x2d88, + 0x11d3, + 0x9a, + 0x16, + &[0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d], +); + +pub const SAL_SYSTEM_TABLE_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xeb9d2d32, + 0x2d88, + 0x11d3, + 0x9a, + 0x16, + &[0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d], +); + +pub const SMBIOS_TABLE_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xeb9d2d31, + 0x2d88, + 0x11d3, + 0x9a, + 0x16, + &[0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d], +); + +pub const SMBIOS3_TABLE_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xf2fd1544, + 0x9794, + 0x4a2c, + 0x99, + 0x2e, + &[0xe5, 0xbb, 0xcf, 0x20, 0xe3, 0x94], +); + +// +// Global Tables +// +// UEFI uses no global state, so all access to UEFI internal state is done through vtables you get +// passed to your entry-point. The global entry is the system-table, which encorporates several +// sub-tables, including the runtime and boot service tables, and configuration tables (including +// vendor extensions). +// + +pub const SPECIFICATION_REVISION: u32 = SYSTEM_TABLE_REVISION; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct TableHeader { + pub signature: u64, + pub revision: u32, + pub header_size: u32, + pub crc32: u32, + pub reserved: u32, +} + +pub const RUNTIME_SERVICES_SIGNATURE: u64 = 0x56524553544e5552u64; // "RUNTSERV" +pub const RUNTIME_SERVICES_REVISION: u32 = SPECIFICATION_REVISION; + +pub type RuntimeGetTime = eficall! {fn( + *mut Time, + *mut TimeCapabilities, +) -> crate::base::Status}; + +pub type RuntimeSetTime = eficall! {fn( + *mut Time, +) -> crate::base::Status}; + +pub type RuntimeGetWakeupTime = eficall! {fn( + *mut crate::base::Boolean, + *mut crate::base::Boolean, + *mut Time, +) -> crate::base::Status}; + +pub type RuntimeSetWakeupTime = eficall! {fn( + crate::base::Boolean, + *mut Time, +) -> crate::base::Status}; + +pub type RuntimeSetVirtualAddressMap = eficall! {fn( + usize, + usize, + u32, + *mut MemoryDescriptor, +) -> crate::base::Status}; + +pub type RuntimeConvertPointer = eficall! {fn( + usize, + *mut *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type RuntimeGetVariable = eficall! {fn( + *mut crate::base::Char16, + *mut crate::base::Guid, + *mut u32, + *mut usize, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type RuntimeGetNextVariableName = eficall! {fn( + *mut usize, + *mut crate::base::Char16, + *mut crate::base::Guid, +) -> crate::base::Status}; + +pub type RuntimeSetVariable = eficall! {fn( + *mut crate::base::Char16, + *mut crate::base::Guid, + u32, + usize, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type RuntimeGetNextHighMonoCount = eficall! {fn( + *mut u32, +) -> crate::base::Status}; + +pub type RuntimeResetSystem = eficall! {fn( + ResetType, + crate::base::Status, + usize, + *mut core::ffi::c_void, +)}; + +pub type RuntimeUpdateCapsule = eficall! {fn( + *mut *mut CapsuleHeader, + usize, + crate::base::PhysicalAddress, +) -> crate::base::Status}; + +pub type RuntimeQueryCapsuleCapabilities = eficall! {fn( + *mut *mut CapsuleHeader, + usize, + *mut u64, + *mut ResetType, +) -> crate::base::Status}; + +pub type RuntimeQueryVariableInfo = eficall! {fn( + u32, + *mut u64, + *mut u64, + *mut u64, +) -> crate::base::Status}; + +#[repr(C)] +pub struct RuntimeServices { + pub hdr: TableHeader, + + pub get_time: RuntimeGetTime, + pub set_time: RuntimeSetTime, + pub get_wakeup_time: RuntimeGetWakeupTime, + pub set_wakeup_time: RuntimeSetWakeupTime, + + pub set_virtual_address_map: RuntimeSetVirtualAddressMap, + pub convert_pointer: RuntimeConvertPointer, + + pub get_variable: RuntimeGetVariable, + pub get_next_variable_name: RuntimeGetNextVariableName, + pub set_variable: RuntimeSetVariable, + + pub get_next_high_mono_count: RuntimeGetNextHighMonoCount, + pub reset_system: RuntimeResetSystem, + + pub update_capsule: RuntimeUpdateCapsule, + pub query_capsule_capabilities: RuntimeQueryCapsuleCapabilities, + pub query_variable_info: RuntimeQueryVariableInfo, +} + +pub const BOOT_SERVICES_SIGNATURE: u64 = 0x56524553544f4f42u64; // "BOOTSERV" +pub const BOOT_SERVICES_REVISION: u32 = SPECIFICATION_REVISION; + +pub type BootRaiseTpl = eficall! {fn( + crate::base::Tpl, +) -> crate::base::Tpl}; + +pub type BootRestoreTpl = eficall! {fn( + crate::base::Tpl, +)}; + +pub type BootAllocatePages = eficall! {fn( + AllocateType, + MemoryType, + usize, + *mut crate::base::PhysicalAddress, +) -> crate::base::Status}; + +pub type BootFreePages = eficall! {fn( + crate::base::PhysicalAddress, + usize, +) -> crate::base::Status}; + +pub type BootGetMemoryMap = eficall! {fn( + *mut usize, + *mut MemoryDescriptor, + *mut usize, + *mut usize, + *mut u32, +) -> crate::base::Status}; + +pub type BootAllocatePool = eficall! {fn( + MemoryType, + usize, + *mut *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type BootFreePool = eficall! {fn( + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type BootCreateEvent = eficall! {fn( + u32, + crate::base::Tpl, + Option, + *mut core::ffi::c_void, + *mut crate::base::Event, +) -> crate::base::Status}; + +pub type BootSetTimer = eficall! {fn( + crate::base::Event, + TimerDelay, + u64, +) -> crate::base::Status}; + +pub type BootWaitForEvent = eficall! {fn( + usize, + *mut crate::base::Event, + *mut usize, +) -> crate::base::Status}; + +pub type BootSignalEvent = eficall! {fn( + crate::base::Event, +) -> crate::base::Status}; + +pub type BootCloseEvent = eficall! {fn( + crate::base::Event, +) -> crate::base::Status}; + +pub type BootCheckEvent = eficall! {fn( + crate::base::Event, +) -> crate::base::Status}; + +pub type BootInstallProtocolInterface = eficall! {fn( + *mut crate::base::Handle, + *mut crate::base::Guid, + InterfaceType, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type BootReinstallProtocolInterface = eficall! {fn( + crate::base::Handle, + *mut crate::base::Guid, + *mut core::ffi::c_void, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type BootUninstallProtocolInterface = eficall! {fn( + crate::base::Handle, + *mut crate::base::Guid, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type BootHandleProtocol = eficall! {fn( + crate::base::Handle, + *mut crate::base::Guid, + *mut *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type BootRegisterProtocolNotify = eficall! {fn( + *mut crate::base::Guid, + crate::base::Event, + *mut *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type BootLocateHandle = eficall! {fn( + LocateSearchType, + *mut crate::base::Guid, + *mut core::ffi::c_void, + *mut usize, + *mut crate::base::Handle, +) -> crate::base::Status}; + +pub type BootLocateDevicePath = eficall! {fn( + *mut crate::base::Guid, + *mut *mut crate::protocols::device_path::Protocol, + *mut crate::base::Handle, +) -> crate::base::Status}; + +pub type BootInstallConfigurationTable = eficall! {fn( + *mut crate::base::Guid, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type BootLoadImage = eficall! {fn( + crate::base::Boolean, + crate::base::Handle, + *mut crate::protocols::device_path::Protocol, + *mut core::ffi::c_void, + usize, + *mut crate::base::Handle, +) -> crate::base::Status}; + +pub type BootStartImage = eficall! {fn( + crate::base::Handle, + *mut usize, + *mut *mut crate::base::Char16, +) -> crate::base::Status}; + +pub type BootExit = eficall! {fn( + crate::base::Handle, + crate::base::Status, + usize, + *mut crate::base::Char16, +) -> crate::base::Status}; + +pub type BootUnloadImage = eficall! {fn( + crate::base::Handle, +) -> crate::base::Status}; + +pub type BootExitBootServices = eficall! {fn( + crate::base::Handle, + usize, +) -> crate::base::Status}; + +pub type BootGetNextMonotonicCount = eficall! {fn( + *mut u64, +) -> crate::base::Status}; + +pub type BootStall = eficall! {fn( + usize, +) -> crate::base::Status}; + +pub type BootSetWatchdogTimer = eficall! {fn( + usize, + u64, + usize, + *mut crate::base::Char16, +) -> crate::base::Status}; + +pub type BootConnectController = eficall! {fn( + crate::base::Handle, + *mut crate::base::Handle, + *mut crate::protocols::device_path::Protocol, + crate::base::Boolean, +) -> crate::base::Status}; + +pub type BootDisconnectController = eficall! {fn( + crate::base::Handle, + crate::base::Handle, + crate::base::Handle, +) -> crate::base::Status}; + +pub type BootOpenProtocol = eficall! {fn( + crate::base::Handle, + *mut crate::base::Guid, + *mut *mut core::ffi::c_void, + crate::base::Handle, + crate::base::Handle, + u32, +) -> crate::base::Status}; + +pub type BootCloseProtocol = eficall! {fn( + crate::base::Handle, + *mut crate::base::Guid, + crate::base::Handle, + crate::base::Handle, +) -> crate::base::Status}; + +pub type BootOpenProtocolInformation = eficall! {fn( + crate::base::Handle, + *mut crate::base::Guid, + *mut *mut OpenProtocolInformationEntry, + *mut usize, +) -> crate::base::Status}; + +pub type BootProtocolsPerHandle = eficall! {fn( + crate::base::Handle, + *mut *mut *mut crate::base::Guid, + *mut usize, +) -> crate::base::Status}; + +pub type BootLocateHandleBuffer = eficall! {fn( + LocateSearchType, + *mut crate::base::Guid, + *mut core::ffi::c_void, + *mut usize, + *mut *mut crate::base::Handle, +) -> crate::base::Status}; + +pub type BootLocateProtocol = eficall! {fn( + *mut crate::base::Guid, + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type BootInstallMultipleProtocolInterfaces = eficall! {fn( + *mut crate::base::Handle, + // XXX: Actual definition is variadic. See eficall!{} for details. + *mut core::ffi::c_void, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type BootUninstallMultipleProtocolInterfaces = eficall! {fn( + crate::base::Handle, + // XXX: Actual definition is variadic. See eficall!{} for details. + *mut core::ffi::c_void, + *mut core::ffi::c_void, +) -> crate::base::Status}; + +pub type BootCalculateCrc32 = eficall! {fn( + *mut core::ffi::c_void, + usize, + *mut u32, +) -> crate::base::Status}; + +pub type BootCopyMem = eficall! {fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + usize, +)}; + +pub type BootSetMem = eficall! {fn( + *mut core::ffi::c_void, + usize, + u8, +)}; + +pub type BootCreateEventEx = eficall! {fn( + u32, + crate::base::Tpl, + Option, + *const core::ffi::c_void, + *const crate::base::Guid, + *mut crate::base::Event, +) -> crate::base::Status}; + +#[repr(C)] +pub struct BootServices { + pub hdr: TableHeader, + + pub raise_tpl: BootRaiseTpl, + pub restore_tpl: BootRestoreTpl, + + pub allocate_pages: BootAllocatePages, + pub free_pages: BootFreePages, + pub get_memory_map: BootGetMemoryMap, + pub allocate_pool: BootAllocatePool, + pub free_pool: BootFreePool, + + pub create_event: BootCreateEvent, + pub set_timer: BootSetTimer, + pub wait_for_event: BootWaitForEvent, + pub signal_event: BootSignalEvent, + pub close_event: BootCloseEvent, + pub check_event: BootCheckEvent, + + pub install_protocol_interface: BootInstallProtocolInterface, + pub reinstall_protocol_interface: BootReinstallProtocolInterface, + pub uninstall_protocol_interface: BootUninstallProtocolInterface, + pub handle_protocol: BootHandleProtocol, + pub reserved: *mut core::ffi::c_void, + pub register_protocol_notify: BootRegisterProtocolNotify, + pub locate_handle: BootLocateHandle, + pub locate_device_path: BootLocateDevicePath, + + pub install_configuration_table: BootInstallConfigurationTable, + + pub load_image: BootLoadImage, + pub start_image: BootStartImage, + pub exit: BootExit, + pub unload_image: BootUnloadImage, + pub exit_boot_services: BootExitBootServices, + + pub get_next_monotonic_count: BootGetNextMonotonicCount, + pub stall: BootStall, + pub set_watchdog_timer: BootSetWatchdogTimer, + + // 1.1+ + pub connect_controller: BootConnectController, + pub disconnect_controller: BootDisconnectController, + + pub open_protocol: BootOpenProtocol, + pub close_protocol: BootCloseProtocol, + pub open_protocol_information: BootOpenProtocolInformation, + + pub protocols_per_handle: BootProtocolsPerHandle, + pub locate_handle_buffer: BootLocateHandleBuffer, + pub locate_protocol: BootLocateProtocol, + pub install_multiple_protocol_interfaces: BootInstallMultipleProtocolInterfaces, + pub uninstall_multiple_protocol_interfaces: BootUninstallMultipleProtocolInterfaces, + + pub calculate_crc32: BootCalculateCrc32, + + pub copy_mem: BootCopyMem, + pub set_mem: BootSetMem, + + // 2.0+ + pub create_event_ex: BootCreateEventEx, +} + +pub const SYSTEM_TABLE_REVISION_2_70: u32 = (2 << 16) | (70); +pub const SYSTEM_TABLE_REVISION_2_60: u32 = (2 << 16) | (60); +pub const SYSTEM_TABLE_REVISION_2_50: u32 = (2 << 16) | (50); +pub const SYSTEM_TABLE_REVISION_2_40: u32 = (2 << 16) | (40); +pub const SYSTEM_TABLE_REVISION_2_31: u32 = (2 << 16) | (31); +pub const SYSTEM_TABLE_REVISION_2_30: u32 = (2 << 16) | (30); +pub const SYSTEM_TABLE_REVISION_2_20: u32 = (2 << 16) | (20); +pub const SYSTEM_TABLE_REVISION_2_10: u32 = (2 << 16) | (10); +pub const SYSTEM_TABLE_REVISION_2_00: u32 = (2 << 16) | (0); +pub const SYSTEM_TABLE_REVISION_1_10: u32 = (1 << 16) | (10); +pub const SYSTEM_TABLE_REVISION_1_02: u32 = (1 << 16) | (2); + +pub const SYSTEM_TABLE_SIGNATURE: u64 = 0x5453595320494249u64; // "IBI SYST" +pub const SYSTEM_TABLE_REVISION: u32 = SYSTEM_TABLE_REVISION_2_70; + +#[repr(C)] +pub struct SystemTable { + pub hdr: TableHeader, + pub firmware_vendor: *mut crate::base::Char16, + pub firmware_revision: u32, + + pub console_in_handle: crate::base::Handle, + pub con_in: *mut crate::protocols::simple_text_input::Protocol, + pub console_out_handle: crate::base::Handle, + pub con_out: *mut crate::protocols::simple_text_output::Protocol, + pub standard_error_handle: crate::base::Handle, + pub std_err: *mut crate::protocols::simple_text_output::Protocol, + + pub runtime_services: *mut RuntimeServices, + pub boot_services: *mut BootServices, + + pub number_of_table_entries: usize, + pub configuration_table: *mut ConfigurationTable, +} diff --git a/tools/vendor/r-efi/src/vendor.rs b/tools/vendor/r-efi/src/vendor.rs new file mode 100644 index 0000000000..d931b8fc31 --- /dev/null +++ b/tools/vendor/r-efi/src/vendor.rs @@ -0,0 +1,10 @@ +//! UEFI Vendor Protocols +//! +//! Many vendor protocols are not part of the official specification. But we +//! still allow importing them here, so we have a central place to collect +//! them. Note that we separate them by vendor-name, which is not the best +//! name-space but should be acceptible. + +pub mod intel { + pub mod console_control; +} diff --git a/tools/vendor/r-efi/src/vendor/intel/console_control.rs b/tools/vendor/r-efi/src/vendor/intel/console_control.rs new file mode 100644 index 0000000000..a6fbf15889 --- /dev/null +++ b/tools/vendor/r-efi/src/vendor/intel/console_control.rs @@ -0,0 +1,37 @@ +//! Console Control Protocol +//! +//! The console-control protocols allows modifying the behavior of the default +//! console device. It is supported by TianoCore and widely adopted. + +pub const PROTOCOL_GUID: crate::base::Guid = crate::base::Guid::from_fields( + 0xf42f7782, + 0x012e, + 0x4c12, + 0x99, + 0x56, + &[0x49, 0xf9, 0x43, 0x04, 0xf7, 0x21], +); + +pub type ScreenMode = u32; + +pub const SCREEN_TEXT: ScreenMode = 0x00000000; +pub const SCREEN_GRAPHICS: ScreenMode = 0x00000001; +pub const SCREEN_MAX_VALUE: ScreenMode = 0x00000002; + +#[repr(C)] +pub struct Protocol { + pub get_mode: eficall! {fn( + *mut Protocol, + *mut ScreenMode, + *mut crate::base::Boolean, + *mut crate::base::Boolean, + ) -> crate::base::Status}, + pub set_mode: eficall! {fn( + *mut Protocol, + ScreenMode, + ) -> crate::base::Status}, + pub lock_std_in: eficall! {fn( + *mut Protocol, + *mut crate::base::Char16, + ) -> crate::base::Status}, +} diff --git a/tools/vendor/tempfile/.cargo-checksum.json b/tools/vendor/tempfile/.cargo-checksum.json new file mode 100644 index 0000000000..f33ff0dd56 --- /dev/null +++ b/tools/vendor/tempfile/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"b0bb5e92738a6397666004a4d0cd8119cc51321685d0ace1341196485952b09b","CHANGELOG.md":"cccc792c6e7fd31850d33c0197de6e9fbb0b404c33fe52c882fd3c59a47ee9d0","Cargo.lock":"fe172750644273a4f39797a27bd6392e020e72b5125671e0f75f2f4901eb06c0","Cargo.toml":"bdaf497273583ced8c41bd276603ea8b875ce0bd68f50200c2680c22cc6e72f8","Cargo.toml.orig":"a37b2561fa745de5522be5f5788c95c8a03c72aab53e6e52d607a08112176166","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"8b427f5bc501764575e52ba4f9d95673cf8f6d80a86d0d06599852e1a9a20a36","README.md":"972f1c35ec653943e067fd2c3d09e78f593b2e9e1eafd5b9668bf3653513de3e","src/dir/imp/any.rs":"744ee1f31d934f8acf54e1412fee93def3321b153b33c28ace71b12c65cf1e4b","src/dir/imp/mod.rs":"4df49b0c0e25a436ceaf566cfc0ca3da6793a91e6e29774d5628a482848f630c","src/dir/imp/unix.rs":"fd1f2162b5fd741f6395c8ef0b34c2430fe9ffb1414bc381c17d5193828e3806","src/dir/mod.rs":"d144325a6528a9a6e7c78a42e0eec42ee401c3b834ef5851376323725756f219","src/env.rs":"97e63846efec09c77d3f784424a717a804004cafc095f53db8574658bf4a8a81","src/error.rs":"cc7d8eace0fff11cb342158d2885d5637bfb14b24ef30755e808554772039c5f","src/file/imp/mod.rs":"09a9e287ffd0c6bfa6750608bd52b2c747885412312c5ccddf37e31b9a8ebb8d","src/file/imp/other.rs":"9efa22cc67555bef747e1870e8deb74c3d57b4c0fed751b34f81eeff910b5a88","src/file/imp/unix.rs":"91fa7e2db9827026e7e432f8717134814718a2c480e114afe3790f2e2789a0ea","src/file/imp/windows.rs":"52c5b6498f22b0834d713ade6ab791526bd8c930e29aa78507026f7c11add22b","src/file/mod.rs":"42deb79b269885bb228a173ca170c66d88999b3a985f9002f0925155ac798ce9","src/lib.rs":"6709d0666546a94aacd5a3bdbb1c5529515ef5450e56f216c7531b71c38bcfb3","src/spooled.rs":"f53a0dbff8a596ca3728f28744ee4b89a31ea5f903d6501c8396fd328b663208","src/util.rs":"dbcb3cb6dd114e83afdab8e0d2a7c4e521383025313f8397570a71ee5d5cfa78","tests/env.rs":"50f6ffe70080adec84ae883fe5a756aa98b07b88c77f22ca20a8478d355ae74c","tests/namedtempfile.rs":"568223d7fa4f7a3dcfcb862f91ac387716061afc8c2e7268fcd6a5cceb26ab1f","tests/spooled.rs":"a0d08fc5546fa7f93662919ca4cec99f2f5d7fc030a90cfe1c97efbf67ecd84f","tests/tempdir.rs":"b52cbe9937525075c83091da520734ef0c4eac35cd13c9c121000e2dcef87cc5","tests/tempfile.rs":"f3c3db1e577c3100d49c09d1f49567c04cb5992743441b4172cd15847444d5f1"},"package":"655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"} \ No newline at end of file diff --git a/tools/vendor/tempfile/.cargo_vcs_info.json b/tools/vendor/tempfile/.cargo_vcs_info.json new file mode 100644 index 0000000000..a308f682d1 --- /dev/null +++ b/tools/vendor/tempfile/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "1712764bb4be5f7787d9b24e6d99961178a52b47" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/tools/vendor/tempfile/CHANGELOG.md b/tools/vendor/tempfile/CHANGELOG.md new file mode 100644 index 0000000000..48b85c22b3 --- /dev/null +++ b/tools/vendor/tempfile/CHANGELOG.md @@ -0,0 +1,376 @@ +# Changelog + +## 3.24.0 + +- Actually support WASIp2 without the nightly feature. This library is now feature complete on WASIp2 without any additional feature flags. +- Exclude CI scripts from the published crate. + +## 3.23.0 + +- Remove need for the "nightly" feature to compile with "wasip2". + +## 3.22.0 + +- Updated `windows-sys` requirement to allow version 0.61.x +- Remove `unstable-windows-keep-open-tempfile` feature. + +## 3.21.0 + +- Updated `windows-sys` requirement to allow version 0.60.x + +## 3.20.0 + +This release mostly unifies the behavior/capabilities around "keeping" temporary files: + +- Rename `Builder::keep(bool)` (via deprecation) to `Builder::disable_cleanup(bool)` to make it clear that behaves differently from `NamedTempFile::keep()`. The former disables automatic cleanup while the latter _consumes_ the `NamedTempFile` object entirely and unsets the "temporary file" attribute (on Windows). +- Rename `TempDir::into_path` (via deprecation) to `TempDir::keep` to mirror `NamedTempFile::keep`. +- Add `TempDir::disable_cleanup`, `NamedTempFile::disable_cleanup`, and `TempPath::disable_cleanup` making it possible to disable automatic cleanup in-place _after_ creating a temporary file/directory (equivalent to calling `Builder::disable_cleanup` before creating the file/directory). + +Additionally, it adds a few spooled temporary file features: +git lo +- Add `SpooledTempFile::into_file` for turning a `SpooledTempFile` into a regular unnamed temporary file, writing it to the backing storage ("rolling" it) if it was still stored in-memory. +- Add `spooled_tempfile_in` and `SpooledTempFile::new_in` methods for creating spooled temporary files in a specific directory. This makes it possible to choose the backing device for your spooled temporary file which is rather important on Linux where the default temporary directory is likely backed by memory (defeating the entire point of having a spooled temporary file). + +Finally, this release improves documentation, especially the top-level documentation explaining which temporary file type to use. + +**BREAKING** for those with `deny(warnings)`: + +- `Builder::keep` deprecated in favor of `Builder::disable_cleanup`. +- `TempDir::into_path` is deprecated in favor of `TempDir::keep`. + +## 3.19.1 + +- Don't unlink temporary files immediately on Windows (fixes #339). Unfortunately, this seemed to corrupt the file object (possibly a Windows kernel bug) in rare cases and isn't strictly speaking necessary. + +## 3.19.0 + +- Remove direct dependency on `cfg-if`. It's still in the tree, but we didn't really need to use it in this crate. +- Add an unstable feature (`unstable-windows-keep-open-tempfile`) to test a potential fix to #339. + +## 3.18.0 + +- Update `rustix` to 1.0.0. +- Make `NamedTempFile::persist_noclobber` atomic on Apple operating systems. It's now atomic on MacOS, Windows, and Linux (depending on the OS version and filesystem used). + +## 3.17.1 + +- Fix build with `windows-sys` 0.52. Unfortunately, we have no CI for older `windows-sys` versions at the moment... + +## 3.17.0 + +- Make sure to use absolute paths in when creating unnamed temporary files (avoids a small race in the "immediate unlink" logic) and in `Builder::make_in` (when creating temporary files of arbitrary types). +- Prevent a theoretical crash that could (maybe) happen when a temporary file is created from a drop function run in a TLS destructor. Nobody has actually reported a case of this happening in practice and I have been unable to create this scenario in a test. +- When reseeding with `getrandom`, use platform (e.g., CPU) specific randomness sources where possible. +- Clarify some documentation. +- Unlink unnamed temporary files on windows _immediately_ when possible instead of waiting for the handle to be closed. We open files with "Unix" semantics, so this is generally possible. + +## 3.16.0 + +- Update `getrandom` to `0.3.0` (thanks to @paolobarbolini). +- Allow `windows-sys` versions `0.59.x` in addition to `0.59.0` (thanks @ErichDonGubler). +- Improved security documentation (thanks to @n0toose for collaborating with me on this). + +## 3.15.0 + +Re-seed the per-thread RNG from system randomness when we repeatedly fail to create temporary files (#314). This resolves a potential DoS vector (#178) while avoiding `getrandom` in the common case where it's necessary. The feature is optional but enabled by default via the `getrandom` feature. + +For libc-free builds, you'll either need to disable this feature or opt-in to a different [`getrandom` backend](https://github.com/rust-random/getrandom?tab=readme-ov-file#opt-in-backends). + +## 3.14.0 + +- Make the wasip2 target work (requires tempfile's "nightly" feature to be enabled). [#305](https://github.com/Stebalien/tempfile/pull/305). +- Allow older windows-sys versions [#304](https://github.com/Stebalien/tempfile/pull/304). + +## 3.13.0 + +- Add `with_suffix` constructors for easily creating new temporary files with a specific suffix (e.g., a specific file extension). Thanks to @Borgerr. +- Update dependencies (fastrand & rustix). + +## 3.12.0 + +- Add a `keep(keep: bool)` function to builder that suppresses delete-on-drop behavior (thanks to @RalfJung). +- Update `windows-sys` from 0.52 to 0.59. + +## 3.11.0 + +- Add the ability to override the default temporary directory. This API shouldn't be used in general, but there are some cases where it's unavoidable. + +## 3.10.1 + +- Handle potential integer overflows in 32-bit systems when seeking/truncating "spooled" temporary files past 4GiB (2³²). +- Handle a theoretical 32-bit overflow when generating a temporary file name larger than 4GiB. Now it'll panic (on allocation failure) rather than silently succeeding due to wraparound. + +Thanks to @stoeckmann for finding and fixing both of these issues. + +## 3.10.0 + +- Drop `redox_syscall` dependency, we now use `rustix` for Redox. +- Add `Builder::permissions` for setting the permissions on temporary files and directories (thanks to @Byron). +- Update rustix to 0.38.31. +- Update fastrand to 2.0.1. + +## 3.9.0 + +- Updates windows-sys to 0.52 +- Updates minimum rustix version to 0.38.25 + +## 3.8.1 + +- Update rustix to fix a potential panic on `persist_noclobber` on android. +- Update redox_syscall to 0.4 (on redox). +- Fix some docs typos. + +## 3.8.0 + +- Added `with_prefix` and `with_prefix_in` to `TempDir` and `NamedTempFile` to make it easier to create temporary files/directories with nice prefixes. +- Misc cleanups. + +## 3.7.1 + +- Tempfile builds on haiku again. +- Under the hood, we've switched from the unlinkat/linkat syscalls to the regular unlink/link syscalls where possible. + +## 3.7.0 + +BREAKING: This release updates the MSRV to 1.63. This isn't an API-breaking change (so no major +release) but it's still a breaking change for some users. + +- Update fastrand from 1.6 to 2.0 +- Update rustix to 0.38 +- Updates the MSRV to 1.63. +- Provide AsFd/AsRawFd on wasi. + +## 3.6.0 + +- Update windows-sys to 0.48. +- Update rustix min version to 0.37.11 +- Forward some `NamedTempFile` and `SpooledTempFile` methods to the underlying `File` object for + better performance (especially vectorized writes, etc.). +- Implement `AsFd` and `AsHandle`. +- Misc documentation fixes and code cleanups. + +## 3.5.0 + +- Update rustix from 0.36 to 0.37.1. This makes wasi work on rust stable +- Update `windows-sys`, `redox_syscall` +- BREAKING: Remove the implementation of `Write for &NamedTempFile where &F: Write`. Unfortunately, this can cause compile issues in unrelated code (https://github.com/Stebalien/tempfile/issues/224). + +## 3.4.0 + +SECURITY: Prior `tempfile` releases depended on `remove_dir_all` version 0.5.0 which was vulnerable to a [TOCTOU race](https://github.com/XAMPPRocky/remove_dir_all/security/advisories/GHSA-mc8h-8q98-g5hr). This same race is present in rust versions prior to 1.58.1. + +Features: + +- Generalized temporary files: `NamedTempFile` can now abstract over different kinds of files (e.g., + unix domain sockets, pipes, etc.): + - Add `Builder::make` and `Builder::make_in` for generalized temp file + creation. + - Add `NamedTempFile::from_parts` to complement `NamedTempFile::into_parts`. + - Add generic parameter to `NamedTempFile` to support wrapping non-File types. + +Bug Fixes/Improvements: + +- Don't try to create a temporary file multiple times if the file path has been fully specified by + the user (no random characters). +- `NamedTempFile::persist_noclobber` is now always atomic on linux when `renameat_with` is + supported. Previously, it would first link the new path, then unlink the previous path. +- Fix compiler warnings on windows. + +Trivia: + +- Switch from `libc` to `rustix` on wasi/unix. This now makes direct syscalls instead of calling + through libc. +- Remove `remove_dir_all` dependency. The rust standard library has optimized their internal version + significantly. + - Switch to official windows-sys windows bindings. + +Breaking: + + - The minimum rust version is now `1.48.0`. + - Mark most functions as `must_use`. + - Uses direct syscalls on linux by default, instead of libc. + - The new type parameter in `NamedTempFile` may lead to type inference issues in some cases. + +## 3.3.0 + +Features: + +- Replace rand with fastrand for a significantly smaller dependency tree. Cryptographic randomness + isn't necessary for temporary file names, and isn't all that helpful either. +- Add limited WASI support. +- Add a function to extract the inner data from a `SpooledTempFile`. + +Bug Fixes: + +- Make it possible to persist unnamed temporary files on linux by removing the `O_EXCL` flag. +- Fix redox minimum crate version. + +## 3.2.0 + +Features: + +- Bump rand dependency to `0.8`. +- Bump cfg-if dependency to `1.0` + +Other than that, this release mostly includes small cleanups and simplifications. + +Breaking: The minimum rust version is now `1.40.0`. + +## 3.1.0 + +Features: + +- Bump rand dependency to `0.7`. + +Breaking: The minimum rust version is now `1.32.0`. + +## 3.0.9 + +Documentation: + +- Add an example for reopening a named temporary file. +- Flesh out the security documentation. + +Features: + +- Introduce an `append` option to the builder. +- Errors: + - No longer implement the soft-deprecated `description`. + - Implement `source` instead of `cause`. + +Breaking: The minimum rust version is now 1.30. + +## 3.0.8 + +This is a bugfix release. + +Fixes: + +- Export `PathPersistError`. +- Fix a bug where flushing a `SpooledTempFile` to disk could fail to write part + of the file in some rare, yet-to-reproduced cases. + +## 3.0.7 + +Breaking: + +- `Builder::prefix` and `Builder::suffix` now accept a generic `&AsRef`. + This could affect type inference. +- Temporary files (except unnamed temporary files on Windows and Linux >= 3.11) + now use absolute path names. This will break programs that create temporary + files relative to their current working directory when they don't have the + search permission (x) on some ancestor directory. This is only likely to + affect programs with strange chroot-less filesystem sandboxes. If you believe + you're affected by this issue, please comment on #40. + +Features: + +- Accept anything implementing `&AsRef` in the builder: &OsStr, &OsString, &Path, etc. + +Fixes: + +- Fix LFS support. +- Use absolute paths for named temporary files to guard against changes in the + current directory. +- Use absolute paths when creating unnamed temporary files on platforms that + can't create unlinked or auto-deleted temporary files. This fixes a very + unlikely race where the current directory could change while the temporary + file is being created. + +Misc: + +- Use modern stdlib features to avoid custom unsafe code. This reduces the + number of unsafe blocks from 12 to 4. + +## 3.0.6 + +- Don't hide temporary files on windows, fixing #66 and #69. + +## 3.0.5 + +Features: + +- Added a spooled temporary file implementation. This temporary file variant + starts out as an in-memory temporary file but "rolls-over" onto disk when it + grows over a specified size (#68). +- Errors are now annotated with paths to make debugging easier (#73). + +Misc: + +- The rand version has been bumped to 0.6 (#74). + +Bugs: + +- Tempfile compiles again on Redox (#75). + +## 3.0.4 + +- Now compiles on unsupported platforms. + +## 3.0.3 + +- update rand to 0.5 + +## 3.0.2 + +- Actually *delete* temporary files on non-Linux unix systems (thanks to +@oliverhenshaw for the fix and a test case). + +## 3.0.1 + +- Restore NamedTempFile::new_in + +## 3.0.0 + +- Adds temporary directory support (@KodrAus) +- Allow closing named temporary files without deleting them (@jasonwhite) + +## 2.2.0 + +- Redox Support + +## 2.1.6 + +- Remove build script and bump minimum rustc version to 1.9.0 + +## 2.1.5 + +- Don't build platform-specific dependencies on all platforms. +- Cleanup some documentation. + +## 2.1.4 + +- Fix crates.io tags. No interesting changes. + +## 2.1.3 + +Export `PersistError`. + +## 2.1.2 + +Add `Read`/`Write`/`Seek` impls on `&NamedTempFile`. This mirrors the +implementations on `&File`. One can currently just deref to a `&File` but these +implementations are more discoverable. + +## 2.1.1 + +Add LFS Support. + +## 2.1.0 + +- Implement `AsRef` for `NamedTempFile` allowing named temporary files to + be borrowed as `File`s. +- Add a method to convert a `NamedTempFile` to an unnamed temporary `File`. + +## 2.0.1 + +- Arm bugfix + +## 2.0.0 + +This release replaces `TempFile` with a `tempfile()` function that returns +`std::fs::File` objects. These are significantly more useful because most rust +libraries expect normal `File` objects. + +To continue supporting shared temporary files, this new version adds a +`reopen()` method to `NamedTempFile`. diff --git a/tools/vendor/tempfile/Cargo.lock b/tools/vendor/tempfile/Cargo.lock new file mode 100644 index 0000000000..b07375edd8 --- /dev/null +++ b/tools/vendor/tempfile/Cargo.lock @@ -0,0 +1,128 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" + +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.178" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "tempfile" +version = "3.24.0" +dependencies = [ + "doc-comment", + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "wasi" +version = "0.14.4+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a5f4a424faf49c3c2c344f166f0662341d470ea185e939657aaff130f0ec4a" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + +[[package]] +name = "windows-sys" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +dependencies = [ + "windows-link", +] + +[[package]] +name = "wit-bindgen" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c573471f125075647d03df72e026074b7203790d41351cd6edc96f46bcccd36" diff --git a/tools/vendor/tempfile/Cargo.toml b/tools/vendor/tempfile/Cargo.toml new file mode 100644 index 0000000000..0db1820fcd --- /dev/null +++ b/tools/vendor/tempfile/Cargo.toml @@ -0,0 +1,105 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.63" +name = "tempfile" +version = "3.24.0" +authors = [ + "Steven Allen ", + "The Rust Project Developers", + "Ashley Mannix ", + "Jason White ", +] +build = false +include = [ + "CHANGELOG.md", + "Cargo.toml", + "LICENSE-*", + "README.md", + "src/**/*.rs", + "tests/**/*.rs", +] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "A library for managing temporary files and directories." +homepage = "https://stebalien.com/projects/tempfile-rs/" +documentation = "https://docs.rs/tempfile" +readme = "README.md" +keywords = [ + "tempfile", + "tmpfile", + "filesystem", +] +license = "MIT OR Apache-2.0" +repository = "https://github.com/Stebalien/tempfile" + +[package.metadata.docs.rs] +rustdoc-args = ["--generate-link-to-definition"] + +[features] +default = ["getrandom"] +nightly = [] + +[lib] +name = "tempfile" +path = "src/lib.rs" + +[[test]] +name = "env" +path = "tests/env.rs" + +[[test]] +name = "namedtempfile" +path = "tests/namedtempfile.rs" + +[[test]] +name = "spooled" +path = "tests/spooled.rs" + +[[test]] +name = "tempdir" +path = "tests/tempdir.rs" + +[[test]] +name = "tempfile" +path = "tests/tempfile.rs" + +[dependencies.fastrand] +version = "2.1.1" + +[dependencies.once_cell] +version = "1.19.0" +features = ["std"] +default-features = false + +[dev-dependencies.doc-comment] +version = "0.3" + +[target.'cfg(any(unix, target_os = "wasi"))'.dependencies.rustix] +version = "1.1.3" +features = ["fs"] + +[target.'cfg(any(unix, windows, target_os = "wasi"))'.dependencies.getrandom] +version = "0.3.0" +optional = true +default-features = false + +[target."cfg(windows)".dependencies.windows-sys] +version = ">=0.52, <0.62" +features = [ + "Win32_Storage_FileSystem", + "Win32_Foundation", +] diff --git a/tools/vendor/tempfile/Cargo.toml.orig b/tools/vendor/tempfile/Cargo.toml.orig new file mode 100644 index 0000000000..ef8fc96063 --- /dev/null +++ b/tools/vendor/tempfile/Cargo.toml.orig @@ -0,0 +1,47 @@ +[package] +name = "tempfile" +version = "3.24.0" +authors = [ + "Steven Allen ", + "The Rust Project Developers", + "Ashley Mannix ", + "Jason White ", +] +documentation = "https://docs.rs/tempfile" +edition = "2021" +rust-version = "1.63" +homepage = "https://stebalien.com/projects/tempfile-rs/" +keywords = ["tempfile", "tmpfile", "filesystem"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/Stebalien/tempfile" +description = "A library for managing temporary files and directories." + +include = ["CHANGELOG.md", "Cargo.toml", "LICENSE-*", "README.md", "src/**/*.rs", "tests/**/*.rs"] + +[dependencies] +fastrand = "2.1.1" +# Not available in stdlib until 1.70, but we support 1.63 to support Debian stable. +once_cell = { version = "1.19.0", default-features = false, features = ["std"] } + +[target.'cfg(any(unix, windows, target_os = "wasi"))'.dependencies] +getrandom = { version = "0.3.0", default-features = false, optional = true } + +[target.'cfg(any(unix, target_os = "wasi"))'.dependencies] +rustix = { version = "1.1.3", features = ["fs"] } + +[target.'cfg(windows)'.dependencies.windows-sys] +version = ">=0.52, <0.62" +features = [ + "Win32_Storage_FileSystem", + "Win32_Foundation", +] + +[dev-dependencies] +doc-comment = "0.3" + +[features] +default = ["getrandom"] +nightly = [] # DEPRECATED + +[package.metadata.docs.rs] +rustdoc-args = ["--generate-link-to-definition"] diff --git a/tools/vendor/tempfile/LICENSE-APACHE b/tools/vendor/tempfile/LICENSE-APACHE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/tools/vendor/tempfile/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/tools/vendor/tempfile/LICENSE-MIT b/tools/vendor/tempfile/LICENSE-MIT new file mode 100644 index 0000000000..0c3270fdd1 --- /dev/null +++ b/tools/vendor/tempfile/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2015 Steven Allen + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/tools/vendor/tempfile/README.md b/tools/vendor/tempfile/README.md new file mode 100644 index 0000000000..4d886b135c --- /dev/null +++ b/tools/vendor/tempfile/README.md @@ -0,0 +1,46 @@ +tempfile +======== + +[![Crate](https://img.shields.io/crates/v/tempfile.svg)](https://crates.io/crates/tempfile) +[![Build Status](https://github.com/Stebalien/tempfile/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/Stebalien/tempfile/actions/workflows/ci.yml?query=branch%3Amaster) + +A secure, cross-platform, temporary file library for Rust. In addition to creating +temporary files, this library also allows users to securely open multiple +independent references to the same temporary file (useful for consumer/producer +patterns and surprisingly difficult to implement securely). + +[Documentation](https://docs.rs/tempfile/) + +Usage +----- + +Minimum required Rust version: 1.63.0 + +Add this to your `Cargo.toml`: + +```toml +[dependencies] +tempfile = "3" +``` + +Example +------- + +```rust +use std::fs::File; +use std::io::{Write, Read, Seek, SeekFrom}; + +fn main() { + // Write + let mut tmpfile: File = tempfile::tempfile().unwrap(); + write!(tmpfile, "Hello World!").unwrap(); + + // Seek to start + tmpfile.seek(SeekFrom::Start(0)).unwrap(); + + // Read + let mut buf = String::new(); + tmpfile.read_to_string(&mut buf).unwrap(); + assert_eq!("Hello World!", buf); +} +``` diff --git a/tools/vendor/tempfile/src/dir/imp/any.rs b/tools/vendor/tempfile/src/dir/imp/any.rs new file mode 100644 index 0000000000..e938d87c2a --- /dev/null +++ b/tools/vendor/tempfile/src/dir/imp/any.rs @@ -0,0 +1,24 @@ +use crate::error::IoResultExt; +use crate::TempDir; +use std::path::PathBuf; +use std::{fs, io}; + +fn not_supported(msg: &str) -> io::Result { + Err(io::Error::new(io::ErrorKind::Other, msg)) +} + +pub fn create( + path: PathBuf, + permissions: Option<&std::fs::Permissions>, + disable_cleanup: bool, +) -> io::Result { + if permissions.map_or(false, |p| p.readonly()) { + return not_supported("changing permissions is not supported on this platform"); + } + fs::create_dir(&path) + .with_err_path(|| &path) + .map(|_| TempDir { + path: path.into_boxed_path(), + disable_cleanup, + }) +} diff --git a/tools/vendor/tempfile/src/dir/imp/mod.rs b/tools/vendor/tempfile/src/dir/imp/mod.rs new file mode 100644 index 0000000000..26d0a22738 --- /dev/null +++ b/tools/vendor/tempfile/src/dir/imp/mod.rs @@ -0,0 +1,9 @@ +#[cfg(unix)] +mod unix; +#[cfg(unix)] +pub use unix::*; + +#[cfg(not(unix))] +mod any; +#[cfg(not(unix))] +pub use any::*; diff --git a/tools/vendor/tempfile/src/dir/imp/unix.rs b/tools/vendor/tempfile/src/dir/imp/unix.rs new file mode 100644 index 0000000000..54f305e984 --- /dev/null +++ b/tools/vendor/tempfile/src/dir/imp/unix.rs @@ -0,0 +1,26 @@ +use crate::error::IoResultExt; +use crate::TempDir; +use std::io; +use std::path::PathBuf; + +pub fn create( + path: PathBuf, + permissions: Option<&std::fs::Permissions>, + disable_cleanup: bool, +) -> io::Result { + let mut dir_options = std::fs::DirBuilder::new(); + #[cfg(not(target_os = "wasi"))] + { + use std::os::unix::fs::{DirBuilderExt, PermissionsExt}; + if let Some(p) = permissions { + dir_options.mode(p.mode()); + } + } + dir_options + .create(&path) + .with_err_path(|| &path) + .map(|_| TempDir { + path: path.into_boxed_path(), + disable_cleanup, + }) +} diff --git a/tools/vendor/tempfile/src/dir/mod.rs b/tools/vendor/tempfile/src/dir/mod.rs new file mode 100644 index 0000000000..93a8d7ccb8 --- /dev/null +++ b/tools/vendor/tempfile/src/dir/mod.rs @@ -0,0 +1,514 @@ +// Copyright 2015 The Rust Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::ffi::OsStr; +use std::fs::remove_dir_all; +use std::mem; +use std::path::{self, Path, PathBuf}; +use std::{fmt, io}; + +use crate::error::IoResultExt; +use crate::Builder; + +#[cfg(doc)] +use crate::env; + +/// Create a new temporary directory. Also see [`tempdir_in`]. +/// +/// The `tempdir` function creates a directory in the file system and returns a +/// [`TempDir`]. The directory will be automatically deleted when the `TempDir`'s +/// destructor is run. +/// +/// # Resource Leaking +/// +/// See [the resource leaking][resource-leaking] docs on `TempDir`. +/// +/// # Security +/// +/// Temporary directories are created with the default permissions unless otherwise +/// specified via [`Builder::permissions`]. Depending on your platform, this may make +/// them world-readable. +/// +/// # Errors +/// +/// If the directory can not be created, `Err` is returned. +/// +/// # Examples +/// +/// ``` +/// use tempfile::tempdir; +/// use std::fs::File; +/// use std::io::Write; +/// +/// // Create a directory inside of `env::temp_dir()` +/// let tmp_dir = tempdir()?; +/// +/// let file_path = tmp_dir.path().join("my-temporary-note.txt"); +/// let mut tmp_file = File::create(file_path)?; +/// writeln!(tmp_file, "Brian was here. Briefly.")?; +/// +/// // `tmp_dir` goes out of scope, the directory as well as +/// // `tmp_file` will be deleted here. +/// drop(tmp_file); +/// tmp_dir.close()?; +/// # Ok::<(), std::io::Error>(()) +/// ``` +/// +/// [`TempDir`]: struct.TempDir.html +/// [resource-leaking]: struct.TempDir.html#resource-leaking +pub fn tempdir() -> io::Result { + TempDir::new() +} + +/// Create a new temporary directory in a specific directory. Also see [`tempdir`]. +/// +/// The `tempdir_in` function creates a directory in the specified directory +/// and returns a [`TempDir`]. +/// The directory will be automatically deleted when the `TempDir`s +/// destructor is run. +/// +/// # Resource Leaking +/// +/// See [the resource leaking][resource-leaking] docs on `TempDir`. +/// +/// # Errors +/// +/// If the directory can not be created, `Err` is returned. +/// +/// # Examples +/// +/// ``` +/// use tempfile::tempdir_in; +/// use std::fs::File; +/// use std::io::Write; +/// +/// // Create a directory inside of the current directory. +/// let tmp_dir = tempdir_in(".")?; +/// +/// let file_path = tmp_dir.path().join("my-temporary-note.txt"); +/// let mut tmp_file = File::create(file_path)?; +/// writeln!(tmp_file, "Brian was here. Briefly.")?; +/// +/// // `tmp_dir` goes out of scope, the directory as well as +/// // `tmp_file` will be deleted here. +/// drop(tmp_file); +/// tmp_dir.close()?; +/// # Ok::<(), std::io::Error>(()) +/// ``` +/// +/// [`TempDir`]: struct.TempDir.html +/// [resource-leaking]: struct.TempDir.html#resource-leaking +pub fn tempdir_in>(dir: P) -> io::Result { + TempDir::new_in(dir) +} + +/// A directory in the filesystem that is automatically deleted when +/// it goes out of scope. +/// +/// The [`TempDir`] type creates a directory on the file system that +/// is deleted once it goes out of scope. At construction, the +/// `TempDir` creates a new directory with a randomly generated name. +/// +/// The default constructor, [`TempDir::new()`], creates directories in +/// the location returned by [`env::temp_dir()`], but `TempDir` +/// can be configured to manage a temporary directory in any location +/// by constructing with a [`Builder`]. +/// +/// After creating a `TempDir`, work with the file system by doing +/// standard [`std::fs`] file system operations on its [`Path`], +/// which can be retrieved with [`TempDir::path()`]. Once the `TempDir` +/// value is dropped, the directory at the path will be deleted, along +/// with any files and directories it contains. It is your responsibility +/// to ensure that no further file system operations are attempted +/// inside the temporary directory once it has been deleted. +/// +/// # Resource Leaking +/// +/// Various platform-specific conditions may cause `TempDir` to fail +/// to delete the underlying directory. It's important to ensure that +/// handles (like [`File`] and [`ReadDir`]) to files inside the +/// directory are dropped before the `TempDir` goes out of scope. The +/// `TempDir` destructor will silently ignore any errors in deleting +/// the directory; to instead handle errors call [`TempDir::close()`]. +/// +/// Note that if the program exits before the `TempDir` destructor is +/// run, such as via [`std::process::exit()`], by segfaulting, or by +/// receiving a signal like `SIGINT`, then the temporary directory +/// will not be deleted. +/// +/// # Examples +/// +/// Create a temporary directory with a generated name: +/// +/// ``` +/// use std::fs::File; +/// use std::io::Write; +/// use tempfile::TempDir; +/// +/// // Create a directory inside of `env::temp_dir()` +/// let tmp_dir = TempDir::new()?; +/// # Ok::<(), std::io::Error>(()) +/// ``` +/// +/// Create a temporary directory with a prefix in its name: +/// +/// ``` +/// use std::fs::File; +/// use std::io::Write; +/// use tempfile::Builder; +/// +/// // Create a directory inside of `env::temp_dir()`, +/// // whose name will begin with 'example'. +/// let tmp_dir = Builder::new().prefix("example").tempdir()?; +/// # Ok::<(), std::io::Error>(()) +/// ``` +/// +/// [`File`]: http://doc.rust-lang.org/std/fs/struct.File.html +/// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html +/// [`ReadDir`]: http://doc.rust-lang.org/std/fs/struct.ReadDir.html +/// [`Builder`]: struct.Builder.html +/// [`TempDir::close()`]: struct.TempDir.html#method.close +/// [`TempDir::new()`]: struct.TempDir.html#method.new +/// [`TempDir::path()`]: struct.TempDir.html#method.path +/// [`TempDir`]: struct.TempDir.html +/// [`std::fs`]: http://doc.rust-lang.org/std/fs/index.html +/// [`std::process::exit()`]: http://doc.rust-lang.org/std/process/fn.exit.html +pub struct TempDir { + path: Box, + disable_cleanup: bool, +} + +impl TempDir { + /// Attempts to make a temporary directory inside of `env::temp_dir()`. + /// + /// See [`Builder`] for more configuration. + /// + /// The directory and everything inside it will be automatically deleted + /// once the returned `TempDir` is destroyed. + /// + /// # Errors + /// + /// If the directory can not be created, `Err` is returned. + /// + /// # Examples + /// + /// ``` + /// use std::fs::File; + /// use std::io::Write; + /// use tempfile::TempDir; + /// + /// // Create a directory inside of `env::temp_dir()` + /// let tmp_dir = TempDir::new()?; + /// + /// let file_path = tmp_dir.path().join("my-temporary-note.txt"); + /// let mut tmp_file = File::create(file_path)?; + /// writeln!(tmp_file, "Brian was here. Briefly.")?; + /// + /// // `tmp_dir` goes out of scope, the directory as well as + /// // `tmp_file` will be deleted here. + /// # Ok::<(), std::io::Error>(()) + /// ``` + /// + /// [`Builder`]: struct.Builder.html + pub fn new() -> io::Result { + Builder::new().tempdir() + } + + /// Attempts to make a temporary directory inside of `dir`. + /// The directory and everything inside it will be automatically + /// deleted once the returned `TempDir` is destroyed. + /// + /// # Errors + /// + /// If the directory can not be created, `Err` is returned. + /// + /// # Examples + /// + /// ``` + /// use std::fs::{self, File}; + /// use std::io::Write; + /// use tempfile::TempDir; + /// + /// // Create a directory inside of the current directory + /// let tmp_dir = TempDir::new_in(".")?; + /// let file_path = tmp_dir.path().join("my-temporary-note.txt"); + /// let mut tmp_file = File::create(file_path)?; + /// writeln!(tmp_file, "Brian was here. Briefly.")?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + pub fn new_in>(dir: P) -> io::Result { + Builder::new().tempdir_in(dir) + } + + /// Attempts to make a temporary directory with the specified prefix inside of + /// `env::temp_dir()`. The directory and everything inside it will be automatically + /// deleted once the returned `TempDir` is destroyed. + /// + /// # Errors + /// + /// If the directory can not be created, `Err` is returned. + /// + /// # Examples + /// + /// ``` + /// use std::fs::{self, File}; + /// use std::io::Write; + /// use tempfile::TempDir; + /// + /// // Create a directory inside of the current directory + /// let tmp_dir = TempDir::with_prefix("foo-")?; + /// let tmp_name = tmp_dir.path().file_name().unwrap().to_str().unwrap(); + /// assert!(tmp_name.starts_with("foo-")); + /// # Ok::<(), std::io::Error>(()) + /// ``` + pub fn with_prefix>(prefix: S) -> io::Result { + Builder::new().prefix(&prefix).tempdir() + } + + /// Attempts to make a temporary directory with the specified suffix inside of + /// `env::temp_dir()`. The directory and everything inside it will be automatically + /// deleted once the returned `TempDir` is destroyed. + /// + /// # Errors + /// + /// If the directory can not be created, `Err` is returned. + /// + /// # Examples + /// + /// ``` + /// use std::fs::{self, File}; + /// use std::io::Write; + /// use tempfile::TempDir; + /// + /// // Create a directory inside of the current directory + /// let tmp_dir = TempDir::with_suffix("-foo")?; + /// let tmp_name = tmp_dir.path().file_name().unwrap().to_str().unwrap(); + /// assert!(tmp_name.ends_with("-foo")); + /// # Ok::<(), std::io::Error>(()) + /// ``` + pub fn with_suffix>(suffix: S) -> io::Result { + Builder::new().suffix(&suffix).tempdir() + } + /// Attempts to make a temporary directory with the specified prefix inside + /// the specified directory. The directory and everything inside it will be + /// automatically deleted once the returned `TempDir` is destroyed. + /// + /// # Errors + /// + /// If the directory can not be created, `Err` is returned. + /// + /// # Examples + /// + /// ``` + /// use std::fs::{self, File}; + /// use std::io::Write; + /// use tempfile::TempDir; + /// + /// // Create a directory inside of the current directory + /// let tmp_dir = TempDir::with_suffix_in("-foo", ".")?; + /// let tmp_name = tmp_dir.path().file_name().unwrap().to_str().unwrap(); + /// assert!(tmp_name.ends_with("-foo")); + /// # Ok::<(), std::io::Error>(()) + /// ``` + pub fn with_suffix_in, P: AsRef>( + suffix: S, + dir: P, + ) -> io::Result { + Builder::new().suffix(&suffix).tempdir_in(dir) + } + + /// Attempts to make a temporary directory with the specified prefix inside + /// the specified directory. The directory and everything inside it will be + /// automatically deleted once the returned `TempDir` is destroyed. + /// + /// # Errors + /// + /// If the directory can not be created, `Err` is returned. + /// + /// # Examples + /// + /// ``` + /// use std::fs::{self, File}; + /// use std::io::Write; + /// use tempfile::TempDir; + /// + /// // Create a directory inside of the current directory + /// let tmp_dir = TempDir::with_prefix_in("foo-", ".")?; + /// let tmp_name = tmp_dir.path().file_name().unwrap().to_str().unwrap(); + /// assert!(tmp_name.starts_with("foo-")); + /// # Ok::<(), std::io::Error>(()) + /// ``` + pub fn with_prefix_in, P: AsRef>( + prefix: S, + dir: P, + ) -> io::Result { + Builder::new().prefix(&prefix).tempdir_in(dir) + } + + /// Accesses the [`Path`] to the temporary directory. + /// + /// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html + /// + /// # Examples + /// + /// ``` + /// use tempfile::TempDir; + /// + /// let tmp_path; + /// + /// { + /// let tmp_dir = TempDir::new()?; + /// tmp_path = tmp_dir.path().to_owned(); + /// + /// // Check that the temp directory actually exists. + /// assert!(tmp_path.exists()); + /// + /// // End of `tmp_dir` scope, directory will be deleted + /// } + /// + /// // Temp directory should be deleted by now + /// assert_eq!(tmp_path.exists(), false); + /// # Ok::<(), std::io::Error>(()) + /// ``` + #[must_use] + pub fn path(&self) -> &path::Path { + self.path.as_ref() + } + + /// Deprecated alias for [`TempDir::keep`]. + #[must_use] + #[deprecated = "use TempDir::keep()"] + pub fn into_path(self) -> PathBuf { + self.keep() + } + + /// Persist the temporary directory to disk, returning the [`PathBuf`] where it is located. + /// + /// This consumes the [`TempDir`] without deleting directory on the filesystem, meaning that + /// the directory will no longer be automatically deleted. + /// + /// If you want to disable automatic cleanup of the temporary directory in-place, keeping the + /// `TempDir` as-is, use [`TempDir::disable_cleanup`] instead. + /// + /// [`TempDir`]: struct.TempDir.html + /// [`PathBuf`]: http://doc.rust-lang.org/std/path/struct.PathBuf.html + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use tempfile::TempDir; + /// + /// let tmp_dir = TempDir::new()?; + /// + /// // Persist the temporary directory to disk, + /// // getting the path where it is. + /// let tmp_path = tmp_dir.keep(); + /// + /// // Delete the temporary directory ourselves. + /// fs::remove_dir_all(tmp_path)?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + #[must_use] + pub fn keep(mut self) -> PathBuf { + self.disable_cleanup(true); + mem::replace(&mut self.path, PathBuf::new().into_boxed_path()).into() + } + + /// Disable cleanup of the temporary directory. If `disable_cleanup` is `true`, the temporary + /// directory will not be deleted when this `TempDir` is dropped. This method is equivalent to + /// calling [`Builder::disable_cleanup`] when creating the `TempDir`. + /// + /// **NOTE:** this method is primarily useful for testing/debugging. If you want to simply turn + /// a temporary directory into a non-temporary directory, prefer [`TempDir::keep`]. + pub fn disable_cleanup(&mut self, disable_cleanup: bool) { + self.disable_cleanup = disable_cleanup + } + + /// Closes and removes the temporary directory, returning a `Result`. + /// + /// Although `TempDir` removes the directory on drop, in the destructor + /// any errors are ignored. To detect errors cleaning up the temporary + /// directory, call `close` instead. + /// + /// # Errors + /// + /// This function may return a variety of [`std::io::Error`]s that result from deleting + /// the files and directories contained with the temporary directory, + /// as well as from deleting the temporary directory itself. These errors + /// may be platform specific. + /// + /// [`std::io::Error`]: http://doc.rust-lang.org/std/io/struct.Error.html + /// + /// # Examples + /// + /// ``` + /// use std::fs::File; + /// use std::io::Write; + /// use tempfile::TempDir; + /// + /// // Create a directory inside of `env::temp_dir()`. + /// let tmp_dir = TempDir::new()?; + /// let file_path = tmp_dir.path().join("my-temporary-note.txt"); + /// let mut tmp_file = File::create(file_path)?; + /// writeln!(tmp_file, "Brian was here. Briefly.")?; + /// + /// // By closing the `TempDir` explicitly we can check that it has + /// // been deleted successfully. If we don't close it explicitly, + /// // the directory will still be deleted when `tmp_dir` goes out + /// // of scope, but we won't know whether deleting the directory + /// // succeeded. + /// drop(tmp_file); + /// tmp_dir.close()?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + pub fn close(mut self) -> io::Result<()> { + let result = remove_dir_all(self.path()).with_err_path(|| self.path()); + + // Set self.path to empty Box to release the memory, since an empty + // Box does not allocate any heap memory. + self.path = PathBuf::new().into_boxed_path(); + + // Prevent the Drop impl from being called. + mem::forget(self); + + result + } +} + +impl AsRef for TempDir { + fn as_ref(&self) -> &Path { + self.path() + } +} + +impl fmt::Debug for TempDir { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TempDir") + .field("path", &self.path()) + .finish() + } +} + +impl Drop for TempDir { + fn drop(&mut self) { + if !self.disable_cleanup { + let _ = remove_dir_all(self.path()); + } + } +} + +pub(crate) fn create( + path: PathBuf, + permissions: Option<&std::fs::Permissions>, + disable_cleanup: bool, +) -> io::Result { + imp::create(path, permissions, disable_cleanup) +} + +mod imp; diff --git a/tools/vendor/tempfile/src/env.rs b/tools/vendor/tempfile/src/env.rs new file mode 100644 index 0000000000..4fdfdf84d2 --- /dev/null +++ b/tools/vendor/tempfile/src/env.rs @@ -0,0 +1,51 @@ +use std::env; +use std::path::{Path, PathBuf}; + +#[cfg(doc)] +use crate::{tempdir_in, tempfile_in, Builder}; + +// Once rust 1.70 is wide-spread (Debian stable), we can use OnceLock from stdlib. +use once_cell::sync::OnceCell as OnceLock; + +static DEFAULT_TEMPDIR: OnceLock = OnceLock::new(); + +/// Override the default temporary directory (defaults to [`std::env::temp_dir`]). This function +/// changes the _global_ default temporary directory for the entire program and should not be called +/// except in exceptional cases where it's not configured correctly by the platform. Applications +/// should first check if the path returned by [`env::temp_dir`] is acceptable. +/// +/// If you're writing a library and want to control where your temporary files are placed, you +/// should instead use the `_in` variants of the various temporary file/directory constructors +/// ([`tempdir_in`], [`tempfile_in`], the so-named functions on [`Builder`], etc.). +/// +/// Only the first call to this function will succeed. All further calls will fail with `Err(path)` +/// where `path` is previously set default temporary directory override. +/// +/// **NOTE:** This function does not check if the specified directory exists and/or is writable. +pub fn override_temp_dir(path: &Path) -> Result<(), PathBuf> { + let mut we_set = false; + let val = DEFAULT_TEMPDIR.get_or_init(|| { + we_set = true; + path.to_path_buf() + }); + if we_set { + Ok(()) + } else { + Err(val.to_owned()) + } +} + +/// Returns the default temporary directory, used for both temporary directories and files if no +/// directory is explicitly specified. +/// +/// This function simply delegates to [`std::env::temp_dir`] unless the default temporary directory +/// has been override by a call to [`override_temp_dir`]. +/// +/// **NOTE:** This function does not check if the returned directory exists and/or is writable. +pub fn temp_dir() -> PathBuf { + DEFAULT_TEMPDIR + .get() + .map(|p| p.to_owned()) + // Don't cache this in case the user uses std::env::set to change the temporary directory. + .unwrap_or_else(env::temp_dir) +} diff --git a/tools/vendor/tempfile/src/error.rs b/tools/vendor/tempfile/src/error.rs new file mode 100644 index 0000000000..ed6b6cc8d1 --- /dev/null +++ b/tools/vendor/tempfile/src/error.rs @@ -0,0 +1,45 @@ +use std::path::PathBuf; +use std::{error, fmt, io}; + +#[derive(Debug)] +struct PathError { + path: PathBuf, + err: io::Error, +} + +impl fmt::Display for PathError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} at path {:?}", self.err, self.path) + } +} + +impl error::Error for PathError { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + self.err.source() + } +} + +pub(crate) trait IoResultExt { + fn with_err_path(self, path: F) -> Self + where + F: FnOnce() -> P, + P: Into; +} + +impl IoResultExt for Result { + fn with_err_path(self, path: F) -> Self + where + F: FnOnce() -> P, + P: Into, + { + self.map_err(|e| { + io::Error::new( + e.kind(), + PathError { + path: path().into(), + err: e, + }, + ) + }) + } +} diff --git a/tools/vendor/tempfile/src/file/imp/mod.rs b/tools/vendor/tempfile/src/file/imp/mod.rs new file mode 100644 index 0000000000..8a00f201ec --- /dev/null +++ b/tools/vendor/tempfile/src/file/imp/mod.rs @@ -0,0 +1,9 @@ +#[cfg_attr(any(unix, target_os = "redox", target_os = "wasi"), path = "unix.rs")] +#[cfg_attr(windows, path = "windows.rs")] +#[cfg_attr( + not(any(unix, target_os = "redox", target_os = "wasi", windows)), + path = "other.rs" +)] +mod platform; + +pub use self::platform::*; diff --git a/tools/vendor/tempfile/src/file/imp/other.rs b/tools/vendor/tempfile/src/file/imp/other.rs new file mode 100644 index 0000000000..bba36712a2 --- /dev/null +++ b/tools/vendor/tempfile/src/file/imp/other.rs @@ -0,0 +1,34 @@ +use std::fs::{File, OpenOptions}; +use std::io; +use std::path::Path; + +fn not_supported() -> io::Result { + Err(io::Error::new( + io::ErrorKind::Other, + "operation not supported on this platform", + )) +} + +pub fn create_named( + _path: &Path, + _open_options: &mut OpenOptions, + _permissions: Option<&std::fs::Permissions>, +) -> io::Result { + not_supported() +} + +pub fn create(_dir: &Path) -> io::Result { + not_supported() +} + +pub fn reopen(_file: &File, _path: &Path) -> io::Result { + not_supported() +} + +pub fn persist(_old_path: &Path, _new_path: &Path, _overwrite: bool) -> io::Result<()> { + not_supported() +} + +pub fn keep(_path: &Path) -> io::Result<()> { + not_supported() +} diff --git a/tools/vendor/tempfile/src/file/imp/unix.rs b/tools/vendor/tempfile/src/file/imp/unix.rs new file mode 100644 index 0000000000..403250a944 --- /dev/null +++ b/tools/vendor/tempfile/src/file/imp/unix.rs @@ -0,0 +1,147 @@ +use std::ffi::OsStr; +use std::fs::{self, File, OpenOptions}; +use std::io; + +use crate::util; +use std::path::Path; + +#[cfg(not(target_os = "redox"))] +use { + rustix::fs::{rename, unlink}, + std::fs::hard_link, +}; + +pub fn create_named( + path: &Path, + open_options: &mut OpenOptions, + #[cfg_attr(target_os = "wasi", allow(unused))] permissions: Option<&std::fs::Permissions>, +) -> io::Result { + open_options.read(true).write(true).create_new(true); + + #[cfg(not(target_os = "wasi"))] + { + use std::os::unix::fs::{OpenOptionsExt, PermissionsExt}; + open_options.mode(permissions.map(|p| p.mode()).unwrap_or(0o600)); + } + + open_options.open(path) +} + +fn create_unlinked(path: &Path) -> io::Result { + let tmp; + // shadow this to decrease the lifetime. It can't live longer than `tmp`. + let mut path = path; + if !path.is_absolute() { + let cur_dir = std::env::current_dir()?; + tmp = cur_dir.join(path); + path = &tmp; + } + + let f = create_named(path, &mut OpenOptions::new(), None)?; + // don't care whether the path has already been unlinked, + // but perhaps there are some IO error conditions we should send up? + let _ = fs::remove_file(path); + Ok(f) +} + +#[cfg(target_os = "linux")] +pub fn create(dir: &Path) -> io::Result { + use rustix::{fs::OFlags, io::Errno}; + use std::os::unix::fs::OpenOptionsExt; + OpenOptions::new() + .read(true) + .write(true) + .custom_flags(OFlags::TMPFILE.bits() as i32) // do not mix with `create_new(true)` + .open(dir) + .or_else(|e| { + match Errno::from_io_error(&e) { + // These are the three "not supported" error codes for O_TMPFILE. + Some(Errno::OPNOTSUPP) | Some(Errno::ISDIR) | Some(Errno::NOENT) => { + create_unix(dir) + } + _ => Err(e), + } + }) +} + +#[cfg(not(target_os = "linux"))] +pub fn create(dir: &Path) -> io::Result { + create_unix(dir) +} + +fn create_unix(dir: &Path) -> io::Result { + util::create_helper( + dir, + OsStr::new(".tmp"), + OsStr::new(""), + crate::NUM_RAND_CHARS, + |path| create_unlinked(&path), + ) +} + +pub fn reopen(file: &File, path: &Path) -> io::Result { + let new_file = OpenOptions::new().read(true).write(true).open(path)?; + let old_meta = rustix::fs::fstat(file)?; + let new_meta = rustix::fs::fstat(&new_file)?; + if old_meta.st_dev != new_meta.st_dev || old_meta.st_ino != new_meta.st_ino { + return Err(io::Error::new( + io::ErrorKind::NotFound, + "original tempfile has been replaced", + )); + } + Ok(new_file) +} + +#[cfg(not(target_os = "redox"))] +pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> { + if overwrite { + rename(old_path, new_path)?; + } else { + // On Linux and apple operating systems, use `renameat_with` to avoid overwriting an + // existing name, if the kernel and the filesystem support it. + #[cfg(any( + target_os = "android", + target_os = "linux", + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "visionos", + target_os = "watchos", + ))] + { + use rustix::fs::{renameat_with, RenameFlags, CWD}; + use rustix::io::Errno; + use std::sync::atomic::{AtomicBool, Ordering::Relaxed}; + + static NOSYS: AtomicBool = AtomicBool::new(false); + if !NOSYS.load(Relaxed) { + match renameat_with(CWD, old_path, CWD, new_path, RenameFlags::NOREPLACE) { + Ok(()) => return Ok(()), + Err(Errno::NOSYS) => NOSYS.store(true, Relaxed), + Err(Errno::INVAL) => {} + Err(e) => return Err(e.into()), + } + } + } + + // Otherwise use `hard_link` to create the new filesystem name, which + // will fail if the name already exists, and then `unlink` to remove + // the old name. + hard_link(old_path, new_path)?; + + // Ignore unlink errors. Can we do better? + let _ = unlink(old_path); + } + Ok(()) +} + +#[cfg(target_os = "redox")] +pub fn persist(_old_path: &Path, _new_path: &Path, _overwrite: bool) -> io::Result<()> { + // XXX implement when possible + use rustix::io::Errno; + Err(Errno::NOSYS.into()) +} + +pub fn keep(_: &Path) -> io::Result<()> { + Ok(()) +} diff --git a/tools/vendor/tempfile/src/file/imp/windows.rs b/tools/vendor/tempfile/src/file/imp/windows.rs new file mode 100644 index 0000000000..3ef149d6fb --- /dev/null +++ b/tools/vendor/tempfile/src/file/imp/windows.rs @@ -0,0 +1,119 @@ +use std::ffi::OsStr; +use std::fs::{File, OpenOptions}; +use std::os::windows::ffi::OsStrExt; +use std::os::windows::fs::OpenOptionsExt; +use std::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle}; +use std::path::Path; +use std::{io, iter}; + +use windows_sys::Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE}; +use windows_sys::Win32::Storage::FileSystem::{ + MoveFileExW, ReOpenFile, SetFileAttributesW, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_TEMPORARY, + FILE_FLAG_DELETE_ON_CLOSE, FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_SHARE_DELETE, + FILE_SHARE_READ, FILE_SHARE_WRITE, MOVEFILE_REPLACE_EXISTING, +}; + +use crate::util; + +fn to_utf16(s: &Path) -> Vec { + s.as_os_str().encode_wide().chain(iter::once(0)).collect() +} + +fn not_supported(msg: &str) -> io::Result { + Err(io::Error::new(io::ErrorKind::Other, msg)) +} + +pub fn create_named( + path: &Path, + open_options: &mut OpenOptions, + permissions: Option<&std::fs::Permissions>, +) -> io::Result { + if permissions.map_or(false, |p| p.readonly()) { + return not_supported("changing permissions is not supported on this platform"); + } + open_options + .create_new(true) + .read(true) + .write(true) + .custom_flags(FILE_ATTRIBUTE_TEMPORARY) + .open(path) +} + +pub fn create(dir: &Path) -> io::Result { + util::create_helper( + dir, + OsStr::new(".tmp"), + OsStr::new(""), + crate::NUM_RAND_CHARS, + |path| { + let f = OpenOptions::new() + .create_new(true) + .read(true) + .write(true) + .share_mode(0) + .custom_flags(FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE) + .open(path)?; + // NOTE: in theory, we could delete the file immediately (we open the file in "unix + // semantics" mode) but this seemed to corrupt something in Windows at scale (see #339). + // So we just rely on `FILE_FLAG_DELETE_ON_CLOSE`. + Ok(f) + }, + ) +} + +pub fn reopen(file: &File, _path: &Path) -> io::Result { + let handle = file.as_raw_handle(); + unsafe { + let handle = ReOpenFile( + handle as HANDLE, + FILE_GENERIC_READ | FILE_GENERIC_WRITE, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + ); + if handle == INVALID_HANDLE_VALUE { + Err(io::Error::last_os_error()) + } else { + Ok(FromRawHandle::from_raw_handle(handle as RawHandle)) + } + } +} + +pub fn keep(path: &Path) -> io::Result<()> { + unsafe { + let path_w = to_utf16(path); + if SetFileAttributesW(path_w.as_ptr(), FILE_ATTRIBUTE_NORMAL) == 0 { + Err(io::Error::last_os_error()) + } else { + Ok(()) + } + } +} + +pub fn persist(old_path: &Path, new_path: &Path, overwrite: bool) -> io::Result<()> { + unsafe { + let old_path_w = to_utf16(old_path); + let new_path_w = to_utf16(new_path); + + // Don't succeed if this fails. We don't want to claim to have successfully persisted a file + // still marked as temporary because this file won't have the same consistency guarantees. + if SetFileAttributesW(old_path_w.as_ptr(), FILE_ATTRIBUTE_NORMAL) == 0 { + return Err(io::Error::last_os_error()); + } + + let mut flags = 0; + + if overwrite { + flags |= MOVEFILE_REPLACE_EXISTING; + } + + if MoveFileExW(old_path_w.as_ptr(), new_path_w.as_ptr(), flags) == 0 { + let e = io::Error::last_os_error(); + // If this fails, the temporary file is now un-hidden and no longer marked temporary + // (slightly less efficient) but it will still work. + let _ = SetFileAttributesW(old_path_w.as_ptr(), FILE_ATTRIBUTE_TEMPORARY); + Err(e) + } else { + Ok(()) + } + } +} diff --git a/tools/vendor/tempfile/src/file/mod.rs b/tools/vendor/tempfile/src/file/mod.rs new file mode 100644 index 0000000000..b3d51c3d59 --- /dev/null +++ b/tools/vendor/tempfile/src/file/mod.rs @@ -0,0 +1,1078 @@ +use std::error; +use std::ffi::OsStr; +use std::fmt; +use std::fs::{self, File, OpenOptions}; +use std::io::{self, Read, Seek, SeekFrom, Write}; +use std::mem; +use std::ops::Deref; +#[cfg(target_os = "wasi")] +use std::os::fd::{AsFd, AsRawFd, BorrowedFd, RawFd}; +#[cfg(unix)] // we don't use std::os::fd because that's not available on rust 1.63. +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; +#[cfg(windows)] +use std::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, RawHandle}; +use std::path::{Path, PathBuf}; + +use crate::env; +use crate::error::IoResultExt; +use crate::Builder; + +mod imp; + +/// Create a new temporary file. Also see [`tempfile_in`]. +/// +/// The file will be created in the location returned by [`env::temp_dir()`]. +/// +/// # Security +/// +/// This variant is secure/reliable in the presence of a pathological temporary file cleaner. +/// +/// # Resource Leaking +/// +/// The temporary file will be automatically removed by the OS when the last handle to it is closed. +/// This doesn't rely on Rust destructors being run, so will (almost) never fail to clean up the temporary file. +/// +/// # Errors +/// +/// If the file can not be created, `Err` is returned. +/// +/// # Examples +/// +/// ``` +/// use tempfile::tempfile; +/// use std::io::Write; +/// +/// // Create a file inside of `env::temp_dir()`. +/// let mut file = tempfile()?; +/// +/// writeln!(file, "Brian was here. Briefly.")?; +/// # Ok::<(), std::io::Error>(()) +/// ``` +pub fn tempfile() -> io::Result { + tempfile_in(env::temp_dir()) +} + +/// Create a new temporary file in the specified directory. Also see [`tempfile`]. +/// +/// # Security +/// +/// This variant is secure/reliable in the presence of a pathological temporary file cleaner. +/// If the temporary file isn't created in [`env::temp_dir()`] then temporary file cleaners aren't an issue. +/// +/// # Resource Leaking +/// +/// The temporary file will be automatically removed by the OS when the last handle to it is closed. +/// This doesn't rely on Rust destructors being run, so will (almost) never fail to clean up the temporary file. +/// +/// # Errors +/// +/// If the file can not be created, `Err` is returned. +/// +/// # Examples +/// +/// ``` +/// use tempfile::tempfile_in; +/// use std::io::Write; +/// +/// // Create a file inside of the current working directory +/// let mut file = tempfile_in("./")?; +/// +/// writeln!(file, "Brian was here. Briefly.")?; +/// # Ok::<(), std::io::Error>(()) +/// ``` +pub fn tempfile_in>(dir: P) -> io::Result { + imp::create(dir.as_ref()) +} + +/// Error returned when persisting a temporary file path fails. +#[derive(Debug)] +pub struct PathPersistError { + /// The underlying IO error. + pub error: io::Error, + /// The temporary file path that couldn't be persisted. + pub path: TempPath, +} + +impl From for io::Error { + #[inline] + fn from(error: PathPersistError) -> io::Error { + error.error + } +} + +impl From for TempPath { + #[inline] + fn from(error: PathPersistError) -> TempPath { + error.path + } +} + +impl fmt::Display for PathPersistError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "failed to persist temporary file path: {}", self.error) + } +} + +impl error::Error for PathPersistError { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + Some(&self.error) + } +} + +/// A path to a named temporary file without an open file handle. +/// +/// This is useful when the temporary file needs to be used by a child process, +/// for example. +/// +/// When dropped, the temporary file is deleted unless `disable_cleanup(true)` was called on the +/// builder that constructed this temporary file and/or was called on either this `TempPath` or the +/// `NamedTempFile` from which this `TempPath` was constructed. +pub struct TempPath { + path: Box, + disable_cleanup: bool, +} + +impl TempPath { + /// Close and remove the temporary file. + /// + /// Use this if you want to detect errors in deleting the file. + /// + /// # Errors + /// + /// If the file cannot be deleted, `Err` is returned. + /// + /// # Examples + /// + /// ```no_run + /// use tempfile::NamedTempFile; + /// + /// let file = NamedTempFile::new()?; + /// + /// // Close the file, but keep the path to it around. + /// let path = file.into_temp_path(); + /// + /// // By closing the `TempPath` explicitly, we can check that it has + /// // been deleted successfully. If we don't close it explicitly, the + /// // file will still be deleted when `file` goes out of scope, but we + /// // won't know whether deleting the file succeeded. + /// path.close()?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + pub fn close(mut self) -> io::Result<()> { + let result = fs::remove_file(&self.path).with_err_path(|| &*self.path); + self.path = PathBuf::new().into_boxed_path(); + mem::forget(self); + result + } + + /// Persist the temporary file at the target path. + /// + /// If a file exists at the target path, persist will atomically replace it. + /// If this method fails, it will return `self` in the resulting + /// [`PathPersistError`]. + /// + /// Note: Temporary files cannot be persisted across filesystems. Also + /// neither the file contents nor the containing directory are + /// synchronized, so the update may not yet have reached the disk when + /// `persist` returns. + /// + /// # Security + /// + /// Only use this method if you're positive that a temporary file cleaner + /// won't have deleted your file. Otherwise, you might end up persisting an + /// attacker controlled file. + /// + /// # Errors + /// + /// If the file cannot be moved to the new location, `Err` is returned. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::Write; + /// use tempfile::NamedTempFile; + /// + /// let mut file = NamedTempFile::new()?; + /// writeln!(file, "Brian was here. Briefly.")?; + /// + /// let path = file.into_temp_path(); + /// path.persist("./saved_file.txt")?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + /// + /// [`PathPersistError`]: struct.PathPersistError.html + pub fn persist>(mut self, new_path: P) -> Result<(), PathPersistError> { + match imp::persist(&self.path, new_path.as_ref(), true) { + Ok(_) => { + // Don't drop `self`. We don't want to try deleting the old + // temporary file path. (It'll fail, but the failure is never + // seen.) + self.path = PathBuf::new().into_boxed_path(); + mem::forget(self); + Ok(()) + } + Err(e) => Err(PathPersistError { + error: e, + path: self, + }), + } + } + + /// Persist the temporary file at the target path if and only if no file exists there. + /// + /// If a file exists at the target path, fail. If this method fails, it will + /// return `self` in the resulting [`PathPersistError`]. + /// + /// Note: Temporary files cannot be persisted across filesystems. Also Note: + /// This method is not atomic. It can leave the original link to the + /// temporary file behind. + /// + /// # Security + /// + /// Only use this method if you're positive that a temporary file cleaner + /// won't have deleted your file. Otherwise, you might end up persisting an + /// attacker controlled file. + /// + /// # Errors + /// + /// If the file cannot be moved to the new location or a file already exists + /// there, `Err` is returned. + /// + /// # Examples + /// + /// ```no_run + /// use tempfile::NamedTempFile; + /// use std::io::Write; + /// + /// let mut file = NamedTempFile::new()?; + /// writeln!(file, "Brian was here. Briefly.")?; + /// + /// let path = file.into_temp_path(); + /// path.persist_noclobber("./saved_file.txt")?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + /// + /// [`PathPersistError`]: struct.PathPersistError.html + pub fn persist_noclobber>( + mut self, + new_path: P, + ) -> Result<(), PathPersistError> { + match imp::persist(&self.path, new_path.as_ref(), false) { + Ok(_) => { + // Don't drop `self`. We don't want to try deleting the old + // temporary file path. (It'll fail, but the failure is never + // seen.) + self.path = PathBuf::new().into_boxed_path(); + mem::forget(self); + Ok(()) + } + Err(e) => Err(PathPersistError { + error: e, + path: self, + }), + } + } + + /// Keep the temporary file from being deleted. This function will turn the + /// temporary file into a non-temporary file without moving it. + /// + /// # Errors + /// + /// On some platforms (e.g., Windows), we need to mark the file as + /// non-temporary. This operation could fail. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::Write; + /// use tempfile::NamedTempFile; + /// + /// let mut file = NamedTempFile::new()?; + /// writeln!(file, "Brian was here. Briefly.")?; + /// + /// let path = file.into_temp_path(); + /// let path = path.keep()?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + /// + /// [`PathPersistError`]: struct.PathPersistError.html + pub fn keep(mut self) -> Result { + match imp::keep(&self.path) { + Ok(_) => { + self.disable_cleanup(true); + Ok(mem::replace( + &mut self.path, + // Replace with an empty boxed path buf, this doesn't allocate. + PathBuf::new().into_boxed_path(), + ) + .into_path_buf()) + } + Err(e) => Err(PathPersistError { + error: e, + path: self, + }), + } + } + + /// Disable cleanup of the temporary file. If `disable_cleanup` is `true`, the temporary file + /// will not be deleted when this `TempPath` is dropped. This method is equivalent to calling + /// [`Builder::disable_cleanup`] when creating the original `NamedTempFile`, which see for + /// relevant warnings. + /// + /// **NOTE:** this method is primarily useful for testing/debugging. If you want to simply turn + /// a temporary file-path into a non-temporary file-path, prefer [`TempPath::keep`]. + pub fn disable_cleanup(&mut self, disable_cleanup: bool) { + self.disable_cleanup = disable_cleanup + } + + /// Create a new TempPath from an existing path. This can be done even if no + /// file exists at the given path. + /// + /// This is mostly useful for interacting with libraries and external + /// components that provide files to be consumed or expect a path with no + /// existing file to be given. + pub fn from_path(path: impl Into) -> Self { + Self { + path: path.into().into_boxed_path(), + disable_cleanup: false, + } + } + + pub(crate) fn new(path: PathBuf, disable_cleanup: bool) -> Self { + Self { + path: path.into_boxed_path(), + disable_cleanup, + } + } +} + +impl fmt::Debug for TempPath { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.path.fmt(f) + } +} + +impl Drop for TempPath { + fn drop(&mut self) { + if !self.disable_cleanup { + let _ = fs::remove_file(&self.path); + } + } +} + +impl Deref for TempPath { + type Target = Path; + + fn deref(&self) -> &Path { + &self.path + } +} + +impl AsRef for TempPath { + fn as_ref(&self) -> &Path { + &self.path + } +} + +impl AsRef for TempPath { + fn as_ref(&self) -> &OsStr { + self.path.as_os_str() + } +} + +/// A named temporary file. +/// +/// The default constructor, [`NamedTempFile::new()`], creates files in +/// the location returned by [`env::temp_dir()`], but `NamedTempFile` +/// can be configured to manage a temporary file in any location +/// by constructing with [`NamedTempFile::new_in()`]. +/// +/// # Security +/// +/// Most operating systems employ temporary file cleaners to delete old +/// temporary files. Unfortunately these temporary file cleaners don't always +/// reliably _detect_ whether the temporary file is still being used. +/// +/// Specifically, the following sequence of events can happen: +/// +/// 1. A user creates a temporary file with `NamedTempFile::new()`. +/// 2. Time passes. +/// 3. The temporary file cleaner deletes (unlinks) the temporary file from the +/// filesystem. +/// 4. Some other program creates a new file to replace this deleted temporary +/// file. +/// 5. The user tries to re-open the temporary file (in the same program or in a +/// different program) by path. Unfortunately, they'll end up opening the +/// file created by the other program, not the original file. +/// +/// ## Operating System Specific Concerns +/// +/// The behavior of temporary files and temporary file cleaners differ by +/// operating system. +/// +/// ### Windows +/// +/// On Windows, temporary files are, by default, created in per-user temporary +/// file directories so only an application running as the same user would be +/// able to interfere (which they could do anyways). However, an application +/// running as the same user can still _accidentally_ re-create deleted +/// temporary files if the number of random bytes in the temporary file name is +/// too small. +/// +/// ### MacOS +/// +/// Like on Windows, temporary files are created in per-user temporary file +/// directories by default so calling `NamedTempFile::new()` should be +/// relatively safe. +/// +/// ### Linux +/// +/// Unfortunately, most _Linux_ distributions don't create per-user temporary +/// file directories. Worse, systemd's tmpfiles daemon (a common temporary file +/// cleaner) will happily remove open temporary files if they haven't been +/// modified within the last 10 days. +/// +/// # Resource Leaking +/// +/// If the program exits before the `NamedTempFile` destructor is +/// run, the temporary file will not be deleted. This can happen +/// if the process exits using [`std::process::exit()`], a segfault occurs, +/// receiving an interrupt signal like `SIGINT` that is not handled, or by using +/// a statically declared `NamedTempFile` instance (like with [`lazy_static`]). +/// +/// Use the [`tempfile()`] function unless you need a named file path. +/// +/// [`tempfile()`]: fn.tempfile.html +/// [`NamedTempFile::new()`]: #method.new +/// [`NamedTempFile::new_in()`]: #method.new_in +/// [`std::process::exit()`]: http://doc.rust-lang.org/std/process/fn.exit.html +/// [`lazy_static`]: https://github.com/rust-lang-nursery/lazy-static.rs/issues/62 +pub struct NamedTempFile { + path: TempPath, + file: F, +} + +impl fmt::Debug for NamedTempFile { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "NamedTempFile({:?})", self.path) + } +} + +impl AsRef for NamedTempFile { + #[inline] + fn as_ref(&self) -> &Path { + self.path() + } +} + +/// Error returned when persisting a temporary file fails. +pub struct PersistError { + /// The underlying IO error. + pub error: io::Error, + /// The temporary file that couldn't be persisted. + pub file: NamedTempFile, +} + +impl fmt::Debug for PersistError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "PersistError({:?})", self.error) + } +} + +impl From> for io::Error { + #[inline] + fn from(error: PersistError) -> io::Error { + error.error + } +} + +impl From> for NamedTempFile { + #[inline] + fn from(error: PersistError) -> NamedTempFile { + error.file + } +} + +impl fmt::Display for PersistError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "failed to persist temporary file: {}", self.error) + } +} + +impl error::Error for PersistError { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + Some(&self.error) + } +} + +impl NamedTempFile { + /// Create a new named temporary file. + /// + /// See [`Builder`] for more configuration. + /// + /// # Security + /// + /// This will create a temporary file in the default temporary file + /// directory (platform dependent). This has security implications on many + /// platforms so please read the security section of this type's + /// documentation. + /// + /// Reasons to use this method: + /// + /// 1. The file has a short lifetime and your temporary file cleaner is + /// sane (doesn't delete recently accessed files). + /// + /// 2. You trust every user on your system (i.e. you are the only user). + /// + /// 3. You have disabled your system's temporary file cleaner or verified + /// that your system doesn't have a temporary file cleaner. + /// + /// Reasons not to use this method: + /// + /// 1. You'll fix it later. No you won't. + /// + /// 2. You don't care about the security of the temporary file. If none of + /// the "reasons to use this method" apply, referring to a temporary + /// file by name may allow an attacker to create/overwrite your + /// non-temporary files. There are exceptions but if you don't already + /// know them, don't use this method. + /// + /// # Errors + /// + /// If the file can not be created, `Err` is returned. + /// + /// # Examples + /// + /// Create a named temporary file and write some data to it: + /// + /// ```no_run + /// use std::io::Write; + /// use tempfile::NamedTempFile; + /// + /// let mut file = NamedTempFile::new()?; + /// + /// writeln!(file, "Brian was here. Briefly.")?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + /// + /// [`Builder`]: struct.Builder.html + pub fn new() -> io::Result { + Builder::new().tempfile() + } + + /// Create a new named temporary file in the specified directory. + /// + /// This is equivalent to: + /// + /// ```ignore + /// Builder::new().tempfile_in(dir) + /// ``` + /// + /// See [`NamedTempFile::new()`] for details. + /// + /// [`NamedTempFile::new()`]: #method.new + pub fn new_in>(dir: P) -> io::Result { + Builder::new().tempfile_in(dir) + } + + /// Create a new named temporary file with the specified filename suffix. + /// + /// See [`NamedTempFile::new()`] for details. + /// + /// [`NamedTempFile::new()`]: #method.new + pub fn with_suffix>(suffix: S) -> io::Result { + Builder::new().suffix(&suffix).tempfile() + } + /// Create a new named temporary file with the specified filename suffix, + /// in the specified directory. + /// + /// This is equivalent to: + /// + /// ```ignore + /// Builder::new().suffix(&suffix).tempfile_in(directory) + /// ``` + /// + /// See [`NamedTempFile::new()`] for details. + /// + /// [`NamedTempFile::new()`]: #method.new + pub fn with_suffix_in, P: AsRef>( + suffix: S, + dir: P, + ) -> io::Result { + Builder::new().suffix(&suffix).tempfile_in(dir) + } + + /// Create a new named temporary file with the specified filename prefix. + /// + /// See [`NamedTempFile::new()`] for details. + /// + /// [`NamedTempFile::new()`]: #method.new + pub fn with_prefix>(prefix: S) -> io::Result { + Builder::new().prefix(&prefix).tempfile() + } + /// Create a new named temporary file with the specified filename prefix, + /// in the specified directory. + /// + /// This is equivalent to: + /// + /// ```ignore + /// Builder::new().prefix(&prefix).tempfile_in(directory) + /// ``` + /// + /// See [`NamedTempFile::new()`] for details. + /// + /// [`NamedTempFile::new()`]: #method.new + pub fn with_prefix_in, P: AsRef>( + prefix: S, + dir: P, + ) -> io::Result { + Builder::new().prefix(&prefix).tempfile_in(dir) + } +} + +impl NamedTempFile { + /// Get the temporary file's path. + /// + /// # Security + /// + /// Referring to a temporary file's path may not be secure in all cases. + /// Please read the security section on the top level documentation of this + /// type for details. + /// + /// # Examples + /// + /// ```no_run + /// use tempfile::NamedTempFile; + /// + /// let file = NamedTempFile::new()?; + /// + /// println!("{:?}", file.path()); + /// # Ok::<(), std::io::Error>(()) + /// ``` + #[inline] + pub fn path(&self) -> &Path { + &self.path + } + + /// Close and remove the temporary file. + /// + /// Use this if you want to detect errors in deleting the file. + /// + /// # Errors + /// + /// If the file cannot be deleted, `Err` is returned. + /// + /// # Examples + /// + /// ```no_run + /// use tempfile::NamedTempFile; + /// + /// let file = NamedTempFile::new()?; + /// + /// // By closing the `NamedTempFile` explicitly, we can check that it has + /// // been deleted successfully. If we don't close it explicitly, + /// // the file will still be deleted when `file` goes out + /// // of scope, but we won't know whether deleting the file + /// // succeeded. + /// file.close()?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + pub fn close(self) -> io::Result<()> { + let NamedTempFile { path, .. } = self; + path.close() + } + + /// Persist the temporary file at the target path. + /// + /// If a file exists at the target path, persist will atomically replace it. + /// If this method fails, it will return `self` in the resulting + /// [`PersistError`]. + /// + /// **Note:** Temporary files cannot be persisted across filesystems. Also + /// neither the file contents nor the containing directory are + /// synchronized, so the update may not yet have reached the disk when + /// `persist` returns. + /// + /// # Security + /// + /// This method persists the temporary file using its path and may not be + /// secure in all cases. Please read the security section on the top + /// level documentation of this type for details. + /// + /// # Errors + /// + /// If the file cannot be moved to the new location, `Err` is returned. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::Write; + /// use tempfile::NamedTempFile; + /// + /// let file = NamedTempFile::new()?; + /// + /// let mut persisted_file = file.persist("./saved_file.txt")?; + /// writeln!(persisted_file, "Brian was here. Briefly.")?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + /// + /// [`PersistError`]: struct.PersistError.html + pub fn persist>(self, new_path: P) -> Result> { + let NamedTempFile { path, file } = self; + match path.persist(new_path) { + Ok(_) => Ok(file), + Err(err) => { + let PathPersistError { error, path } = err; + Err(PersistError { + file: NamedTempFile { path, file }, + error, + }) + } + } + } + + /// Persist the temporary file at the target path if and only if no file exists there. + /// + /// If a file exists at the target path, fail. If this method fails, it will + /// return `self` in the resulting PersistError. + /// + /// **Note:** Temporary files cannot be persisted across filesystems. + /// + /// **Atomicity:** This method is not guaranteed to be atomic on all platforms, although it will + /// generally be atomic on Windows and modern Linux filesystems. While it will never overwrite a + /// file at the target path, it may leave the original link to the temporary file behind leaving + /// you with two [hard links][hardlink] in your filesystem pointing at the same underlying file. + /// This can happen if either (a) we lack permission to "unlink" the original filename; (b) this + /// program crashes while persisting the temporary file; or (c) the filesystem is removed, + /// unmounted, etc. while we're performing this operation. + /// + /// # Security + /// + /// This method persists the temporary file using its path and may not be + /// secure in all cases. Please read the security section on the top + /// level documentation of this type for details. + /// + /// # Errors + /// + /// If the file cannot be moved to the new location or a file already exists there, + /// `Err` is returned. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::Write; + /// use tempfile::NamedTempFile; + /// + /// let file = NamedTempFile::new()?; + /// + /// let mut persisted_file = file.persist_noclobber("./saved_file.txt")?; + /// writeln!(persisted_file, "Brian was here. Briefly.")?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + /// + /// [hardlink]: https://en.wikipedia.org/wiki/Hard_link + pub fn persist_noclobber>(self, new_path: P) -> Result> { + let NamedTempFile { path, file } = self; + match path.persist_noclobber(new_path) { + Ok(_) => Ok(file), + Err(err) => { + let PathPersistError { error, path } = err; + Err(PersistError { + file: NamedTempFile { path, file }, + error, + }) + } + } + } + + /// Keep the temporary file from being deleted. This function will turn the + /// temporary file into a non-temporary file without moving it. + /// + /// # Errors + /// + /// On some platforms (e.g., Windows), we need to mark the file as + /// non-temporary. This operation could fail. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::Write; + /// use tempfile::NamedTempFile; + /// + /// let mut file = NamedTempFile::new()?; + /// writeln!(file, "Brian was here. Briefly.")?; + /// + /// let (file, path) = file.keep()?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + /// + /// [`PathPersistError`]: struct.PathPersistError.html + pub fn keep(self) -> Result<(F, PathBuf), PersistError> { + let (file, path) = (self.file, self.path); + match path.keep() { + Ok(path) => Ok((file, path)), + Err(PathPersistError { error, path }) => Err(PersistError { + file: NamedTempFile { path, file }, + error, + }), + } + } + + /// Disable cleanup of the temporary file. If `disable_cleanup` is `true`, the temporary file + /// will not be deleted when this `TempPath` is dropped. This method is equivalent to calling + /// [`Builder::disable_cleanup`] when creating the original `NamedTempFile`, which see for + /// relevant warnings. + /// + /// **NOTE:** this method is primarily useful for testing/debugging. If you want to simply turn + /// a temporary file into a non-temporary file, prefer [`NamedTempFile::keep`]. + pub fn disable_cleanup(&mut self, disable_cleanup: bool) { + self.path.disable_cleanup(disable_cleanup) + } + + /// Get a reference to the underlying file. + pub fn as_file(&self) -> &F { + &self.file + } + + /// Get a mutable reference to the underlying file. + pub fn as_file_mut(&mut self) -> &mut F { + &mut self.file + } + + /// Turn this named temporary file into an "unnamed" temporary file as if you + /// had constructed it with [`tempfile()`]. + /// + /// The underlying file will be removed from the filesystem but the returned [`File`] + /// can still be read/written. + pub fn into_file(self) -> F { + self.file + } + + /// Closes the file, leaving only the temporary file path. + /// + /// This is useful when another process must be able to open the temporary + /// file. + pub fn into_temp_path(self) -> TempPath { + self.path + } + + /// Converts the named temporary file into its constituent parts. + /// + /// Note: When the path is dropped, the underlying file will be removed from the filesystem but + /// the returned [`File`] can still be read/written. + pub fn into_parts(self) -> (F, TempPath) { + (self.file, self.path) + } + + /// Creates a `NamedTempFile` from its constituent parts. + /// + /// This can be used with [`NamedTempFile::into_parts`] to reconstruct the + /// `NamedTempFile`. + pub fn from_parts(file: F, path: TempPath) -> Self { + Self { file, path } + } +} + +impl NamedTempFile { + /// Securely reopen the temporary file. + /// + /// This function is useful when you need multiple independent handles to + /// the same file. It's perfectly fine to drop the original `NamedTempFile` + /// while holding on to `File`s returned by this function; the `File`s will + /// remain usable. However, they may not be nameable. + /// + /// # Errors + /// + /// If the file cannot be reopened, `Err` is returned. + /// + /// # Security + /// + /// Unlike `File::open(my_temp_file.path())`, `NamedTempFile::reopen()` + /// guarantees that the re-opened file is the _same_ file, even in the + /// presence of pathological temporary file cleaners. + /// + /// # Examples + /// + /// ```no_run + /// use tempfile::NamedTempFile; + /// + /// let file = NamedTempFile::new()?; + /// + /// let another_handle = file.reopen()?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + pub fn reopen(&self) -> io::Result { + imp::reopen(self.as_file(), NamedTempFile::path(self)) + .with_err_path(|| NamedTempFile::path(self)) + } +} + +impl Read for NamedTempFile { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.as_file_mut().read(buf).with_err_path(|| self.path()) + } + + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + self.as_file_mut() + .read_vectored(bufs) + .with_err_path(|| self.path()) + } + + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.as_file_mut() + .read_to_end(buf) + .with_err_path(|| self.path()) + } + + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + self.as_file_mut() + .read_to_string(buf) + .with_err_path(|| self.path()) + } + + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + self.as_file_mut() + .read_exact(buf) + .with_err_path(|| self.path()) + } +} + +impl Read for &NamedTempFile { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.as_file().read(buf).with_err_path(|| self.path()) + } + + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + self.as_file() + .read_vectored(bufs) + .with_err_path(|| self.path()) + } + + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.as_file() + .read_to_end(buf) + .with_err_path(|| self.path()) + } + + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + self.as_file() + .read_to_string(buf) + .with_err_path(|| self.path()) + } + + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + self.as_file().read_exact(buf).with_err_path(|| self.path()) + } +} + +impl Write for NamedTempFile { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.as_file_mut().write(buf).with_err_path(|| self.path()) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + self.as_file_mut().flush().with_err_path(|| self.path()) + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.as_file_mut() + .write_vectored(bufs) + .with_err_path(|| self.path()) + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.as_file_mut() + .write_all(buf) + .with_err_path(|| self.path()) + } + + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + self.as_file_mut() + .write_fmt(fmt) + .with_err_path(|| self.path()) + } +} + +impl Write for &NamedTempFile { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.as_file().write(buf).with_err_path(|| self.path()) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + self.as_file().flush().with_err_path(|| self.path()) + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.as_file() + .write_vectored(bufs) + .with_err_path(|| self.path()) + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.as_file().write_all(buf).with_err_path(|| self.path()) + } + + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + self.as_file().write_fmt(fmt).with_err_path(|| self.path()) + } +} + +impl Seek for NamedTempFile { + fn seek(&mut self, pos: SeekFrom) -> io::Result { + self.as_file_mut().seek(pos).with_err_path(|| self.path()) + } +} + +impl Seek for &NamedTempFile { + fn seek(&mut self, pos: SeekFrom) -> io::Result { + self.as_file().seek(pos).with_err_path(|| self.path()) + } +} + +#[cfg(any(unix, target_os = "wasi"))] +impl AsFd for NamedTempFile { + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_file().as_fd() + } +} + +#[cfg(any(unix, target_os = "wasi"))] +impl AsRawFd for NamedTempFile { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.as_file().as_raw_fd() + } +} + +#[cfg(windows)] +impl AsHandle for NamedTempFile { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + self.as_file().as_handle() + } +} + +#[cfg(windows)] +impl AsRawHandle for NamedTempFile { + #[inline] + fn as_raw_handle(&self) -> RawHandle { + self.as_file().as_raw_handle() + } +} + +pub(crate) fn create_named( + path: PathBuf, + open_options: &mut OpenOptions, + permissions: Option<&std::fs::Permissions>, + keep: bool, +) -> io::Result { + imp::create_named(&path, open_options, permissions) + .with_err_path(|| path.clone()) + .map(|file| NamedTempFile { + path: TempPath { + path: path.into_boxed_path(), + disable_cleanup: keep, + }, + file, + }) +} diff --git a/tools/vendor/tempfile/src/lib.rs b/tools/vendor/tempfile/src/lib.rs new file mode 100644 index 0000000000..9a24b90410 --- /dev/null +++ b/tools/vendor/tempfile/src/lib.rs @@ -0,0 +1,757 @@ +//! This is a library for creating temporary files and directories that are automatically deleted +//! when no longer referenced (i.e., on drop). +//! +//! - Use [`tempfile()`] when you need a real [`std::fs::File`] but don't need to refer to it +//! by-path. +//! - Use [`NamedTempFile::new()`] when you need a _named_ temporary file that can be refered to its +//! path. +//! - Use [`tempdir()`] when you need a temporary directory that will be recursively deleted on drop. +//! - Use [`spooled_tempfile()`] when you need an in-memory buffer that will ultimately be backed by +//! a temporary file if it gets too large. +//! +//! # Design +//! +//! This crate provides several approaches to creating temporary files and directories. +//! [`tempfile()`] relies on the OS to remove the temporary file once the last handle is closed. +//! [`TempDir`] and [`NamedTempFile`] both rely on Rust destructors for cleanup. +//! +//! ## Resource Leaking +//! +//! `tempfile` will (almost) never fail to cleanup temporary resources. However `TempDir` and +//! `NamedTempFile` will fail if their destructors don't run. This is because `tempfile` relies on +//! the OS to cleanup the underlying file, while `TempDir` and `NamedTempFile` rely on rust +//! destructors to do so. Destructors may fail to run if the process exits through an unhandled +//! signal interrupt (like `SIGINT`), or if the instance is declared statically (like with +//! [`lazy_static`]), among other possible reasons. +//! +//! ## Unexpected File Deletion +//! +//! Most operating systems periodically clean up temporary files that haven't been accessed recently +//! (often on the order of multiple days). This issue does not affect unnamed temporary files but +//! can invalidate the paths associated with named temporary files on Unix-like systems because the +//! temporary file can be unlinked from the filesystem while still open and in-use. See the +//! [temporary file cleaner](#temporary-file-cleaners) section for more security implications. +//! +//! ## Security +//! +//! This section discusses security issues relevant to Unix-like operating systems that use shared +//! temporary directories by default. Importantly, it's not relevant for Windows or macOS as both +//! operating systems use private per-user temporary directories by default. +//! +//! Applications can mitigate the issues described below by using [`env::override_temp_dir`] to +//! change the default temporary directory but should do so if and only if default the temporary +//! directory ([`env::temp_dir`]) is unsuitable (is world readable, world writable, managed by a +//! temporary file cleaner, etc.). +//! +//! ### Temporary File Cleaners +//! +//! In the presence of pathological temporary file cleaner, relying on file paths is unsafe because +//! a temporary file cleaner could delete the temporary file which an attacker could then replace. +//! +//! This isn't an issue for [`tempfile`] as it doesn't rely on file paths. However, [`NamedTempFile`] +//! and temporary directories _do_ rely on file paths for _some_ operations. See the security +//! documentation on the [`NamedTempFile`] and the [`TempDir`] types for more information. +//! +//! Mitigation: +//! +//! - This is rarely an issue for short-lived files as temporary file cleaners usually only remove +//! temporary files that haven't been modified or accessed within many (10-30) days. +//! - Very long lived temporary files should be placed in directories not managed by temporary file +//! cleaners. +//! +//! ### Access Permissions +//! +//! Temporary _files_ created with this library are private by default on all operating systems. +//! However, temporary _directories_ are created with the default permissions and will therefore be +//! world-readable by default unless the user has changed their umask and/or default temporary +//! directory. +//! +//! ### Denial of Service +//! +//! If the file-name randomness ([`Builder::rand_bytes`]) is too small and/or this crate is built +//! without the `getrandom` feature, it may be possible for an attacker to predict the random file +//! names chosen by this library, preventing temporary file creation by creating temporary files +//! with these predicted file names. By default, this library mitigates this denial of service +//! attack by: +//! +//! 1. Defaulting to 6 random characters per temporary file forcing an attacker to create billions +//! of files before random collisions are expected (at which point you probably have larger +//! problems). +//! 2. Re-seeding the random filename generator from system randomness after 3 failed attempts to +//! create temporary a file (when the `getrandom` feature is enabled as it is by default on all +//! major platforms). +//! +//! ## Early drop pitfall +//! +//! Because `TempDir` and `NamedTempFile` rely on their destructors for cleanup, this can lead +//! to an unexpected early removal of the directory/file, usually when working with APIs which are +//! generic over `AsRef`. Consider the following example: +//! +//! ```no_run +//! use tempfile::tempdir; +//! use std::process::Command; +//! +//! // Create a directory inside of `env::temp_dir()`. +//! let temp_dir = tempdir()?; +//! +//! // Spawn the `touch` command inside the temporary directory and collect the exit status +//! // Note that `temp_dir` is **not** moved into `current_dir`, but passed as a reference +//! let exit_status = Command::new("touch").arg("tmp").current_dir(&temp_dir).status()?; +//! assert!(exit_status.success()); +//! +//! # Ok::<(), std::io::Error>(()) +//! ``` +//! +//! This works because a reference to `temp_dir` is passed to `current_dir`, resulting in the +//! destructor of `temp_dir` being run after the `Command` has finished execution. Moving the +//! `TempDir` into the `current_dir` call would result in the `TempDir` being converted into +//! an internal representation, with the original value being dropped and the directory thus +//! being deleted, before the command can be executed. +//! +//! The `touch` command would fail with an `No such file or directory` error. +//! +//! ## Examples +//! +//! Create a temporary file and write some data into it: +//! +//! ``` +//! use tempfile::tempfile; +//! use std::io::Write; +//! +//! // Create a file inside of `env::temp_dir()`. +//! let mut file = tempfile()?; +//! +//! writeln!(file, "Brian was here. Briefly.")?; +//! # Ok::<(), std::io::Error>(()) +//! ``` +//! +//! Create a named temporary file and open an independent file handle: +//! +//! ``` +//! use tempfile::NamedTempFile; +//! use std::io::{Write, Read}; +//! +//! let text = "Brian was here. Briefly."; +//! +//! // Create a file inside of `env::temp_dir()`. +//! let mut file1 = NamedTempFile::new()?; +//! +//! // Re-open it. +//! let mut file2 = file1.reopen()?; +//! +//! // Write some test data to the first handle. +//! file1.write_all(text.as_bytes())?; +//! +//! // Read the test data using the second handle. +//! let mut buf = String::new(); +//! file2.read_to_string(&mut buf)?; +//! assert_eq!(buf, text); +//! # Ok::<(), std::io::Error>(()) +//! ``` +//! +//! Create a temporary directory and add a file to it: +//! +//! ``` +//! use tempfile::tempdir; +//! use std::fs::File; +//! use std::io::Write; +//! +//! // Create a directory inside of `env::temp_dir()`. +//! let dir = tempdir()?; +//! +//! let file_path = dir.path().join("my-temporary-note.txt"); +//! let mut file = File::create(file_path)?; +//! writeln!(file, "Brian was here. Briefly.")?; +//! +//! // By closing the `TempDir` explicitly, we can check that it has +//! // been deleted successfully. If we don't close it explicitly, +//! // the directory will still be deleted when `dir` goes out +//! // of scope, but we won't know whether deleting the directory +//! // succeeded. +//! drop(file); +//! dir.close()?; +//! # Ok::<(), std::io::Error>(()) +//! ``` +//! +//! [`tempfile()`]: fn.tempfile.html +//! [`tempdir()`]: fn.tempdir.html +//! [`TempDir`]: struct.TempDir.html +//! [`NamedTempFile`]: struct.NamedTempFile.html +//! [`lazy_static`]: https://github.com/rust-lang-nursery/lazy-static.rs/issues/62 + +#![doc( + html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://www.rust-lang.org/favicon.ico", + html_root_url = "https://docs.rs/tempfile/latest" +)] +#![cfg_attr(test, deny(warnings))] +#![deny(rust_2018_idioms)] +#![allow(clippy::redundant_field_names)] + +#[cfg(doctest)] +doc_comment::doctest!("../README.md"); + +const NUM_RETRIES: u32 = 65536; +const NUM_RAND_CHARS: usize = 6; + +use std::ffi::OsStr; +use std::fs::{OpenOptions, Permissions}; +use std::io; +use std::path::Path; + +mod dir; +mod error; +mod file; +mod spooled; +mod util; + +pub mod env; + +pub use crate::dir::{tempdir, tempdir_in, TempDir}; +pub use crate::file::{ + tempfile, tempfile_in, NamedTempFile, PathPersistError, PersistError, TempPath, +}; +pub use crate::spooled::{spooled_tempfile, spooled_tempfile_in, SpooledData, SpooledTempFile}; + +/// Create a new temporary file or directory with custom options. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct Builder<'a, 'b> { + random_len: usize, + prefix: &'a OsStr, + suffix: &'b OsStr, + append: bool, + permissions: Option, + disable_cleanup: bool, +} + +impl Default for Builder<'_, '_> { + fn default() -> Self { + Builder { + random_len: crate::NUM_RAND_CHARS, + prefix: OsStr::new(".tmp"), + suffix: OsStr::new(""), + append: false, + permissions: None, + disable_cleanup: false, + } + } +} + +impl<'a, 'b> Builder<'a, 'b> { + /// Create a new `Builder`. + /// + /// # Examples + /// + /// Create a named temporary file and write some data into it: + /// + /// ``` + /// use std::ffi::OsStr; + /// use tempfile::Builder; + /// + /// let named_tempfile = Builder::new() + /// .prefix("my-temporary-note") + /// .suffix(".txt") + /// .rand_bytes(5) + /// .tempfile()?; + /// + /// let name = named_tempfile + /// .path() + /// .file_name().and_then(OsStr::to_str); + /// + /// if let Some(name) = name { + /// assert!(name.starts_with("my-temporary-note")); + /// assert!(name.ends_with(".txt")); + /// assert_eq!(name.len(), "my-temporary-note.txt".len() + 5); + /// } + /// # Ok::<(), std::io::Error>(()) + /// ``` + /// + /// Create a temporary directory and add a file to it: + /// + /// ``` + /// use std::io::Write; + /// use std::fs::File; + /// use std::ffi::OsStr; + /// use tempfile::Builder; + /// + /// let dir = Builder::new() + /// .prefix("my-temporary-dir") + /// .rand_bytes(5) + /// .tempdir()?; + /// + /// let file_path = dir.path().join("my-temporary-note.txt"); + /// let mut file = File::create(file_path)?; + /// writeln!(file, "Brian was here. Briefly.")?; + /// + /// // By closing the `TempDir` explicitly, we can check that it has + /// // been deleted successfully. If we don't close it explicitly, + /// // the directory will still be deleted when `dir` goes out + /// // of scope, but we won't know whether deleting the directory + /// // succeeded. + /// drop(file); + /// dir.close()?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + /// + /// Create a temporary directory with a chosen prefix under a chosen folder: + /// + /// ```no_run + /// use tempfile::Builder; + /// + /// let dir = Builder::new() + /// .prefix("my-temporary-dir") + /// .tempdir_in("folder-with-tempdirs")?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /// Set a custom filename prefix. + /// + /// Path separators are legal but not advisable. + /// Default: `.tmp`. + /// + /// # Examples + /// + /// ``` + /// use tempfile::Builder; + /// + /// let named_tempfile = Builder::new() + /// .prefix("my-temporary-note") + /// .tempfile()?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + pub fn prefix + ?Sized>(&mut self, prefix: &'a S) -> &mut Self { + self.prefix = prefix.as_ref(); + self + } + + /// Set a custom filename suffix. + /// + /// Path separators are legal but not advisable. + /// Default: empty. + /// + /// # Examples + /// + /// ``` + /// use tempfile::Builder; + /// + /// let named_tempfile = Builder::new() + /// .suffix(".txt") + /// .tempfile()?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + pub fn suffix + ?Sized>(&mut self, suffix: &'b S) -> &mut Self { + self.suffix = suffix.as_ref(); + self + } + + /// Set the number of random bytes. + /// + /// Default: `6`. + /// + /// # Examples + /// + /// ``` + /// use tempfile::Builder; + /// + /// let named_tempfile = Builder::new() + /// .rand_bytes(5) + /// .tempfile()?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + pub fn rand_bytes(&mut self, rand: usize) -> &mut Self { + self.random_len = rand; + self + } + + /// Configure the file to be opened in append-only mode. + /// + /// Default: `false`. + /// + /// # Examples + /// + /// ``` + /// use tempfile::Builder; + /// + /// let named_tempfile = Builder::new() + /// .append(true) + /// .tempfile()?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + pub fn append(&mut self, append: bool) -> &mut Self { + self.append = append; + self + } + + /// Set the permissions for the new temporary file/directory. + /// + /// # Platform Notes + /// + /// ## Windows + /// + /// This setting is only fully-supported on unix-like platforms. On Windows, if this method is + /// called with a [`Permissions`] object where `permissions.readonly` returns true, creating + /// temporary files and directories will fail with an error. + /// + /// ## Unix + /// + /// On unix-like systems, the actual permission bits set on the tempfile or tempdir will be + /// affected by the `umask` applied by the underlying syscall. The actual permission bits are + /// calculated via `permissions & !umask`. In other words, depending on your umask, the + /// permissions of the created file may be more restrictive (but never more permissive) than the + /// ones you specified. + /// + /// Permissions default to `0o600` for tempfiles and `0o777` for tempdirs. Note, this doesn't + /// include effects of the current `umask`. For example, combined with the standard umask + /// `0o022`, the defaults yield `0o600` for tempfiles and `0o755` for tempdirs. + /// + /// ## WASI + /// + /// While custom permissions are allowed on WASI, they will be ignored as the platform has no + /// concept of permissions or file modes (or multiple users for that matter). + /// + /// # Examples + /// + /// Create a named temporary file that is world-readable. + /// + /// ``` + /// # #[cfg(unix)] + /// # { + /// use tempfile::Builder; + /// use std::os::unix::fs::PermissionsExt; + /// + /// let all_read_write = std::fs::Permissions::from_mode(0o666); + /// let tempfile = Builder::new().permissions(all_read_write).tempfile()?; + /// + /// // Check that this worked and that the file is world-readable. + /// // + /// // NOTE: the file likely won't actually be created with 0o666 permissions because it's + /// // restricted by the user's umask. + /// // + /// // NOTE: This test will fail if the user's umask is, e.g., 0o066. + /// let actual_permissions = tempfile.path().metadata()?.permissions(); + /// assert_eq!(actual_permissions.mode() & 0o044, 0o044); + /// # } + /// # Ok::<(), std::io::Error>(()) + /// ``` + /// + /// Create a named temporary directory that is restricted to the owner. + /// + /// ``` + /// # #[cfg(unix)] + /// # { + /// use tempfile::Builder; + /// use std::os::unix::fs::PermissionsExt; + /// + /// let owner_rwx = std::fs::Permissions::from_mode(0o700); + /// let tempdir = Builder::new().permissions(owner_rwx).tempdir()?; + /// let actual_permissions = tempdir.path().metadata()?.permissions(); + /// assert_eq!( + /// actual_permissions.mode() & !0o170000, + /// 0o700, + /// "we get the narrow permissions we asked for" + /// ); + /// # } + /// # Ok::<(), std::io::Error>(()) + /// ``` + pub fn permissions(&mut self, permissions: Permissions) -> &mut Self { + self.permissions = Some(permissions); + self + } + + /// Disable cleanup of the file/folder to even when the [`NamedTempFile`]/[`TempDir`] goes out + /// of scope. Prefer [`NamedTempFile::keep`] and [`TempDir::keep`] where possible; + /// `disable_cleanup` is provided for testing & debugging. + /// + /// By default, the file/folder is automatically cleaned up in the destructor of + /// [`NamedTempFile`]/[`TempDir`]. When `disable_cleanup` is set to `true`, this behavior is + /// suppressed. If you wish to disable cleanup after creating a temporary file/directory, call + /// [`NamedTempFile::disable_cleanup`] or [`TempDir::disable_cleanup`]. + /// + /// # Warnings + /// + /// On some platforms (for now, only Windows), temporary files are marked with a special + /// "temporary file" (`FILE_ATTRIBUTE_TEMPORARY`) attribute. Disabling cleanup _will not_ unset + /// this attribute while calling [`NamedTempFile::keep`] will. + /// + /// # Examples + /// + /// ``` + /// use tempfile::Builder; + /// + /// let named_tempfile = Builder::new() + /// .disable_cleanup(true) + /// .tempfile()?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + pub fn disable_cleanup(&mut self, disable_cleanup: bool) -> &mut Self { + self.disable_cleanup = disable_cleanup; + self + } + + /// Deprecated alias for [`Builder::disable_cleanup`]. + #[deprecated = "Use Builder::disable_cleanup"] + pub fn keep(&mut self, keep: bool) -> &mut Self { + self.disable_cleanup(keep) + } + + /// Create the named temporary file. + /// + /// # Security + /// + /// See [the security][security] docs on `NamedTempFile`. + /// + /// # Resource leaking + /// + /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`. + /// + /// # Errors + /// + /// If the file cannot be created, `Err` is returned. + /// + /// # Examples + /// + /// ``` + /// use tempfile::Builder; + /// + /// let tempfile = Builder::new().tempfile()?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + /// + /// [security]: struct.NamedTempFile.html#security + /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking + pub fn tempfile(&self) -> io::Result { + self.tempfile_in(env::temp_dir()) + } + + /// Create the named temporary file in the specified directory. + /// + /// # Security + /// + /// See [the security][security] docs on `NamedTempFile`. + /// + /// # Resource leaking + /// + /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`. + /// + /// # Errors + /// + /// If the file cannot be created, `Err` is returned. + /// + /// # Examples + /// + /// ``` + /// use tempfile::Builder; + /// + /// let tempfile = Builder::new().tempfile_in("./")?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + /// + /// [security]: struct.NamedTempFile.html#security + /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking + pub fn tempfile_in>(&self, dir: P) -> io::Result { + util::create_helper( + dir.as_ref(), + self.prefix, + self.suffix, + self.random_len, + |path| { + file::create_named( + path, + OpenOptions::new().append(self.append), + self.permissions.as_ref(), + self.disable_cleanup, + ) + }, + ) + } + + /// Attempts to make a temporary directory inside of [`env::temp_dir()`] whose + /// name will have the prefix, `prefix`. The directory and + /// everything inside it will be automatically deleted once the + /// returned `TempDir` is destroyed. + /// + /// # Resource leaking + /// + /// See [the resource leaking][resource-leaking] docs on `TempDir`. + /// + /// # Errors + /// + /// If the directory can not be created, `Err` is returned. + /// + /// # Examples + /// + /// ``` + /// use tempfile::Builder; + /// + /// let tmp_dir = Builder::new().tempdir()?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + /// + /// [resource-leaking]: struct.TempDir.html#resource-leaking + pub fn tempdir(&self) -> io::Result { + self.tempdir_in(env::temp_dir()) + } + + /// Attempts to make a temporary directory inside of `dir`. + /// The directory and everything inside it will be automatically + /// deleted once the returned `TempDir` is destroyed. + /// + /// # Resource leaking + /// + /// See [the resource leaking][resource-leaking] docs on `TempDir`. + /// + /// # Errors + /// + /// If the directory can not be created, `Err` is returned. + /// + /// # Examples + /// + /// ``` + /// use tempfile::Builder; + /// + /// let tmp_dir = Builder::new().tempdir_in("./")?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + /// + /// [resource-leaking]: struct.TempDir.html#resource-leaking + pub fn tempdir_in>(&self, dir: P) -> io::Result { + util::create_helper( + dir.as_ref(), + self.prefix, + self.suffix, + self.random_len, + |path| dir::create(path, self.permissions.as_ref(), self.disable_cleanup), + ) + } + + /// Attempts to create a temporary file (or file-like object) using the + /// provided closure. The closure is passed a temporary file path and + /// returns an [`std::io::Result`]. The path provided to the closure will be + /// inside of [`env::temp_dir()`]. Use [`Builder::make_in`] to provide + /// a custom temporary directory. If the closure returns one of the + /// following errors, then another randomized file path is tried: + /// - [`std::io::ErrorKind::AlreadyExists`] + /// - [`std::io::ErrorKind::AddrInUse`] + /// + /// This can be helpful for taking full control over the file creation, but + /// leaving the temporary file path construction up to the library. This + /// also enables creating a temporary UNIX domain socket, since it is not + /// possible to bind to a socket that already exists. + /// + /// Note that [`Builder::append`] is ignored when using [`Builder::make`]. + /// + /// # Security + /// + /// This has the same [security implications][security] as + /// [`NamedTempFile`], but with additional caveats. Specifically, it is up + /// to the closure to ensure that the file does not exist and that such a + /// check is *atomic*. Otherwise, a [time-of-check to time-of-use + /// bug][TOCTOU] could be introduced. + /// + /// For example, the following is **not** secure: + /// + /// ``` + /// use std::fs::File; + /// use tempfile::Builder; + /// + /// // This is NOT secure! + /// let tempfile = Builder::new().make(|path| { + /// if path.is_file() { + /// return Err(std::io::ErrorKind::AlreadyExists.into()); + /// } + /// + /// // Between the check above and the usage below, an attacker could + /// // have replaced `path` with another file, which would get truncated + /// // by `File::create`. + /// + /// File::create(path) + /// })?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + /// + /// Note that simply using [`std::fs::File::create`] alone is not correct + /// because it does not fail if the file already exists: + /// + /// ``` + /// use tempfile::Builder; + /// use std::fs::File; + /// + /// // This could overwrite an existing file! + /// let tempfile = Builder::new().make(|path| File::create(path))?; + /// # Ok::<(), std::io::Error>(()) + /// ``` + /// For creating regular temporary files, use [`Builder::tempfile`] instead + /// to avoid these problems. This function is meant to enable more exotic + /// use-cases. + /// + /// # Resource leaking + /// + /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`. + /// + /// # Errors + /// + /// If the closure returns any error besides + /// [`std::io::ErrorKind::AlreadyExists`] or + /// [`std::io::ErrorKind::AddrInUse`], then `Err` is returned. + /// + /// # Examples + /// ``` + /// # #[cfg(unix)] + /// # { + /// use std::os::unix::net::UnixListener; + /// use tempfile::Builder; + /// + /// let tempsock = Builder::new().make(|path| UnixListener::bind(path))?; + /// # } + /// # Ok::<(), std::io::Error>(()) + /// ``` + /// + /// [TOCTOU]: https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use + /// [security]: struct.NamedTempFile.html#security + /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking + pub fn make(&self, f: F) -> io::Result> + where + F: FnMut(&Path) -> io::Result, + { + self.make_in(env::temp_dir(), f) + } + + /// This is the same as [`Builder::make`], except `dir` is used as the base + /// directory for the temporary file path. + /// + /// See [`Builder::make`] for more details and security implications. + /// + /// # Examples + /// ``` + /// # #[cfg(unix)] + /// # { + /// use tempfile::Builder; + /// use std::os::unix::net::UnixListener; + /// + /// let tempsock = Builder::new().make_in("./", |path| UnixListener::bind(path))?; + /// # } + /// # Ok::<(), std::io::Error>(()) + /// ``` + pub fn make_in(&self, dir: P, mut f: F) -> io::Result> + where + F: FnMut(&Path) -> io::Result, + P: AsRef, + { + util::create_helper( + dir.as_ref(), + self.prefix, + self.suffix, + self.random_len, + move |path| { + Ok(NamedTempFile::from_parts( + f(&path)?, + TempPath::new(path, self.disable_cleanup), + )) + }, + ) + } +} diff --git a/tools/vendor/tempfile/src/spooled.rs b/tools/vendor/tempfile/src/spooled.rs new file mode 100644 index 0000000000..57ba9bead4 --- /dev/null +++ b/tools/vendor/tempfile/src/spooled.rs @@ -0,0 +1,249 @@ +use crate::file::tempfile; +use crate::tempfile_in; +use std::fs::File; +use std::io::{self, Cursor, Read, Seek, SeekFrom, Write}; +use std::path::{Path, PathBuf}; + +/// A wrapper for the two states of a [`SpooledTempFile`]. Either: +/// +/// 1. An in-memory [`Cursor`] representing the state of the file. +/// 2. A temporary [`File`]. +#[derive(Debug)] +pub enum SpooledData { + InMemory(Cursor>), + OnDisk(File), +} + +/// An object that behaves like a regular temporary file, but keeps data in +/// memory until it reaches a configured size, at which point the data is +/// written to a temporary file on disk, and further operations use the file +/// on disk. +#[derive(Debug)] +pub struct SpooledTempFile { + max_size: usize, + dir: Option, + inner: SpooledData, +} + +/// Create a new [`SpooledTempFile`]. Also see [`spooled_tempfile_in`]. +/// +/// # Security +/// +/// This variant is secure/reliable in the presence of a pathological temporary +/// file cleaner. +/// +/// # Backing Storage +/// +/// By default, the underlying temporary file will be created in your operating system's temporary +/// file directory which is _often_ an in-memory filesystem. You may want to consider using +/// [`spooled_tempfile_in`] instead, passing a storage-backed filesystem (e.g., `/var/tmp` on +/// Linux). +/// +/// # Resource Leaking +/// +/// The temporary file will be automatically removed by the OS when the last +/// handle to it is closed. This doesn't rely on Rust destructors being run, so +/// will (almost) never fail to clean up the temporary file. +/// +/// # Examples +/// +/// ``` +/// use tempfile::spooled_tempfile; +/// use std::io::Write; +/// +/// let mut file = spooled_tempfile(15); +/// +/// writeln!(file, "short line")?; +/// assert!(!file.is_rolled()); +/// +/// // as a result of this write call, the size of the data will exceed +/// // `max_size` (15), so it will be written to a temporary file on disk, +/// // and the in-memory buffer will be dropped +/// writeln!(file, "marvin gardens")?; +/// assert!(file.is_rolled()); +/// # Ok::<(), std::io::Error>(()) +/// ``` +#[inline] +pub fn spooled_tempfile(max_size: usize) -> SpooledTempFile { + SpooledTempFile::new(max_size) +} + +/// Construct a new [`SpooledTempFile`], backed by a file in the specified directory. Use this when, +/// e.g., you need the temporary file to be backed by a specific filesystem (e.g., when your default +/// temporary directory is in-memory). Also see [`spooled_tempfile`]. +/// +/// **NOTE:** The specified path isn't checked until the temporary file is "rolled over" into a real +/// temporary file. If the specified directory isn't writable, writes to the temporary file will +/// fail once the `max_size` is reached. +#[inline] +pub fn spooled_tempfile_in>(max_size: usize, dir: P) -> SpooledTempFile { + SpooledTempFile::new_in(max_size, dir) +} + +/// Write a cursor into a temporary file, returning the temporary file. +fn cursor_to_tempfile(cursor: &Cursor>, p: &Option) -> io::Result { + let mut file = match p { + Some(p) => tempfile_in(p)?, + None => tempfile()?, + }; + file.write_all(cursor.get_ref())?; + file.seek(SeekFrom::Start(cursor.position()))?; + Ok(file) +} + +impl SpooledTempFile { + /// Construct a new [`SpooledTempFile`]. + #[must_use] + pub fn new(max_size: usize) -> SpooledTempFile { + SpooledTempFile { + max_size, + dir: None, + inner: SpooledData::InMemory(Cursor::new(Vec::new())), + } + } + + /// Construct a new [`SpooledTempFile`], backed by a file in the specified directory. + #[must_use] + pub fn new_in>(max_size: usize, dir: P) -> SpooledTempFile { + SpooledTempFile { + max_size, + dir: Some(dir.as_ref().to_owned()), + inner: SpooledData::InMemory(Cursor::new(Vec::new())), + } + } + + /// Returns true if the file has been rolled over to disk. + #[must_use] + pub fn is_rolled(&self) -> bool { + match self.inner { + SpooledData::InMemory(_) => false, + SpooledData::OnDisk(_) => true, + } + } + + /// Rolls over to a file on disk, regardless of current size. Does nothing + /// if already rolled over. + pub fn roll(&mut self) -> io::Result<()> { + if let SpooledData::InMemory(cursor) = &mut self.inner { + self.inner = SpooledData::OnDisk(cursor_to_tempfile(cursor, &self.dir)?); + } + Ok(()) + } + + /// Truncate the file to the specified size. + pub fn set_len(&mut self, size: u64) -> Result<(), io::Error> { + if size > self.max_size as u64 { + self.roll()?; // does nothing if already rolled over + } + match &mut self.inner { + SpooledData::InMemory(cursor) => { + cursor.get_mut().resize(size as usize, 0); + Ok(()) + } + SpooledData::OnDisk(file) => file.set_len(size), + } + } + + /// Consumes and returns the inner `SpooledData` type. + #[must_use] + pub fn into_inner(self) -> SpooledData { + self.inner + } + + /// Convert into a regular unnamed temporary file, writing it to disk if necessary. + pub fn into_file(self) -> io::Result { + match self.inner { + SpooledData::InMemory(cursor) => cursor_to_tempfile(&cursor, &self.dir), + SpooledData::OnDisk(file) => Ok(file), + } + } +} + +impl Read for SpooledTempFile { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match &mut self.inner { + SpooledData::InMemory(cursor) => cursor.read(buf), + SpooledData::OnDisk(file) => file.read(buf), + } + } + + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + match &mut self.inner { + SpooledData::InMemory(cursor) => cursor.read_vectored(bufs), + SpooledData::OnDisk(file) => file.read_vectored(bufs), + } + } + + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + match &mut self.inner { + SpooledData::InMemory(cursor) => cursor.read_to_end(buf), + SpooledData::OnDisk(file) => file.read_to_end(buf), + } + } + + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + match &mut self.inner { + SpooledData::InMemory(cursor) => cursor.read_to_string(buf), + SpooledData::OnDisk(file) => file.read_to_string(buf), + } + } + + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + match &mut self.inner { + SpooledData::InMemory(cursor) => cursor.read_exact(buf), + SpooledData::OnDisk(file) => file.read_exact(buf), + } + } +} + +impl Write for SpooledTempFile { + fn write(&mut self, buf: &[u8]) -> io::Result { + // roll over to file if necessary + if matches! { + &self.inner, SpooledData::InMemory(cursor) + if cursor.position().saturating_add(buf.len() as u64) > self.max_size as u64 + } { + self.roll()?; + } + + // write the bytes + match &mut self.inner { + SpooledData::InMemory(cursor) => cursor.write(buf), + SpooledData::OnDisk(file) => file.write(buf), + } + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + if matches! { + &self.inner, SpooledData::InMemory(cursor) + // Borrowed from the rust standard library. + if bufs + .iter() + .fold(cursor.position(), |a, b| a.saturating_add(b.len() as u64)) + > self.max_size as u64 + } { + self.roll()?; + } + match &mut self.inner { + SpooledData::InMemory(cursor) => cursor.write_vectored(bufs), + SpooledData::OnDisk(file) => file.write_vectored(bufs), + } + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + match &mut self.inner { + SpooledData::InMemory(cursor) => cursor.flush(), + SpooledData::OnDisk(file) => file.flush(), + } + } +} + +impl Seek for SpooledTempFile { + fn seek(&mut self, pos: SeekFrom) -> io::Result { + match &mut self.inner { + SpooledData::InMemory(cursor) => cursor.seek(pos), + SpooledData::OnDisk(file) => file.seek(pos), + } + } +} diff --git a/tools/vendor/tempfile/src/util.rs b/tools/vendor/tempfile/src/util.rs new file mode 100644 index 0000000000..2ac9bea473 --- /dev/null +++ b/tools/vendor/tempfile/src/util.rs @@ -0,0 +1,82 @@ +use std::ffi::{OsStr, OsString}; +use std::path::{Path, PathBuf}; +use std::{io, iter::repeat_with}; + +use crate::error::IoResultExt; + +fn tmpname(rng: &mut fastrand::Rng, prefix: &OsStr, suffix: &OsStr, rand_len: usize) -> OsString { + let capacity = prefix + .len() + .saturating_add(suffix.len()) + .saturating_add(rand_len); + let mut buf = OsString::with_capacity(capacity); + buf.push(prefix); + let mut char_buf = [0u8; 4]; + for c in repeat_with(|| rng.alphanumeric()).take(rand_len) { + buf.push(c.encode_utf8(&mut char_buf)); + } + buf.push(suffix); + buf +} + +pub fn create_helper( + base: &Path, + prefix: &OsStr, + suffix: &OsStr, + random_len: usize, + mut f: impl FnMut(PathBuf) -> io::Result, +) -> io::Result { + // Make the path absolute. Otherwise, changing the current directory can invalidate a stored + // path (causing issues when cleaning up temporary files. + let mut base = base; // re-borrow to shrink lifetime + let base_path_storage; // slot to store the absolute path, if necessary. + if !base.is_absolute() { + let cur_dir = std::env::current_dir()?; + base_path_storage = cur_dir.join(base); + base = &base_path_storage; + } + + let num_retries = if random_len != 0 { + crate::NUM_RETRIES + } else { + 1 + }; + + // We fork the fastrand rng. + let mut rng = fastrand::Rng::new(); + for i in 0..num_retries { + // If we fail to create the file the first three times, re-seed from system randomness in + // case an attacker is predicting our randomness (fastrand is predictable). If re-seeding + // doesn't help, either: + // + // 1. We have lots of temporary files, possibly created by an attacker but not necessarily. + // Re-seeding the randomness won't help here. + // 2. We're failing to create random files for some other reason. This shouldn't be the case + // given that we're checking error kinds, but it could happen. + #[cfg(all( + feature = "getrandom", + any(windows, unix, target_os = "redox", target_os = "wasi") + ))] + if i == 3 { + if let Ok(seed) = getrandom::u64() { + rng.seed(seed); + } + } + let _ = i; // avoid unused variable warning for the above. + + let path = base.join(tmpname(&mut rng, prefix, suffix, random_len)); + return match f(path) { + Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists && num_retries > 1 => continue, + // AddrInUse can happen if we're creating a UNIX domain socket and + // the path already exists. + Err(ref e) if e.kind() == io::ErrorKind::AddrInUse && num_retries > 1 => continue, + res => res, + }; + } + + Err(io::Error::new( + io::ErrorKind::AlreadyExists, + "too many temporary files exist", + )) + .with_err_path(|| base) +} diff --git a/tools/vendor/tempfile/tests/env.rs b/tools/vendor/tempfile/tests/env.rs new file mode 100644 index 0000000000..47317a8f20 --- /dev/null +++ b/tools/vendor/tempfile/tests/env.rs @@ -0,0 +1,16 @@ +#![deny(rust_2018_idioms)] + +use std::path::Path; + +#[test] +fn test_override_temp_dir() { + #[cfg(not(target_os = "wasi"))] + assert_eq!(tempfile::env::temp_dir(), std::env::temp_dir()); + + let new_tmp = Path::new("/tmp/override"); + tempfile::env::override_temp_dir(new_tmp).unwrap(); + assert_eq!(tempfile::env::temp_dir(), new_tmp); + + let new_tmp2 = Path::new("/tmp/override2"); + tempfile::env::override_temp_dir(new_tmp2).expect_err("override should only be possible once"); +} diff --git a/tools/vendor/tempfile/tests/namedtempfile.rs b/tools/vendor/tempfile/tests/namedtempfile.rs new file mode 100644 index 0000000000..c453b521a6 --- /dev/null +++ b/tools/vendor/tempfile/tests/namedtempfile.rs @@ -0,0 +1,621 @@ +#![deny(rust_2018_idioms)] + +use std::ffi::{OsStr, OsString}; +use std::fs::File; +use std::io::{self, Read, Seek, SeekFrom, Write}; +use std::path::{Path, PathBuf}; +use tempfile::{env, tempdir, Builder, NamedTempFile, TempPath}; + +fn exists>(path: P) -> bool { + std::fs::metadata(path.as_ref()).is_ok() +} + +/// For the wasi platforms, `std::env::temp_dir` will panic. For those targets, configure the /tmp +/// directory instead as the base directory for temp files. +fn configure_wasi_temp_dir() { + if cfg!(target_os = "wasi") { + let _ = tempfile::env::override_temp_dir(Path::new("/tmp")); + } +} + +#[test] +fn test_prefix() { + configure_wasi_temp_dir(); + + let tmpfile = NamedTempFile::with_prefix("prefix").unwrap(); + let name = tmpfile.path().file_name().unwrap().to_str().unwrap(); + assert!(name.starts_with("prefix")); +} + +#[test] +fn test_suffix() { + configure_wasi_temp_dir(); + + let tmpfile = NamedTempFile::with_suffix("suffix").unwrap(); + let name = tmpfile.path().file_name().unwrap().to_str().unwrap(); + assert!(name.ends_with("suffix")); +} + +#[test] +fn test_basic() { + configure_wasi_temp_dir(); + + let mut tmpfile = NamedTempFile::new().unwrap(); + write!(tmpfile, "abcde").unwrap(); + tmpfile.seek(SeekFrom::Start(0)).unwrap(); + let mut buf = String::new(); + tmpfile.read_to_string(&mut buf).unwrap(); + assert_eq!("abcde", buf); +} + +#[test] +fn test_deleted() { + configure_wasi_temp_dir(); + + let tmpfile = NamedTempFile::new().unwrap(); + let path = tmpfile.path().to_path_buf(); + assert!(exists(&path)); + drop(tmpfile); + assert!(!exists(&path)); +} + +#[test] +fn test_persist() { + configure_wasi_temp_dir(); + + let mut tmpfile = NamedTempFile::new().unwrap(); + let old_path = tmpfile.path().to_path_buf(); + let persist_path = env::temp_dir().join("persisted_temporary_file"); + write!(tmpfile, "abcde").unwrap(); + { + assert!(exists(&old_path)); + let mut f = tmpfile.persist(&persist_path).unwrap(); + assert!(!exists(&old_path)); + + // Check original file + f.seek(SeekFrom::Start(0)).unwrap(); + let mut buf = String::new(); + f.read_to_string(&mut buf).unwrap(); + assert_eq!("abcde", buf); + } + + { + // Try opening it at the new path. + let mut f = File::open(&persist_path).unwrap(); + f.seek(SeekFrom::Start(0)).unwrap(); + let mut buf = String::new(); + f.read_to_string(&mut buf).unwrap(); + assert_eq!("abcde", buf); + } + std::fs::remove_file(&persist_path).unwrap(); +} + +#[test] +fn test_persist_noclobber() { + configure_wasi_temp_dir(); + + let mut tmpfile = NamedTempFile::new().unwrap(); + let old_path = tmpfile.path().to_path_buf(); + let persist_target = NamedTempFile::new().unwrap(); + let persist_path = persist_target.path().to_path_buf(); + write!(tmpfile, "abcde").unwrap(); + assert!(exists(&old_path)); + { + tmpfile = tmpfile.persist_noclobber(&persist_path).unwrap_err().into(); + assert!(exists(&old_path)); + std::fs::remove_file(&persist_path).unwrap(); + drop(persist_target); + } + tmpfile.persist_noclobber(&persist_path).unwrap(); + // Try opening it at the new path. + let mut f = File::open(&persist_path).unwrap(); + f.seek(SeekFrom::Start(0)).unwrap(); + let mut buf = String::new(); + f.read_to_string(&mut buf).unwrap(); + assert_eq!("abcde", buf); + std::fs::remove_file(&persist_path).unwrap(); +} + +#[test] +fn test_customnamed() { + configure_wasi_temp_dir(); + + let tmpfile = Builder::new() + .prefix("tmp") + .suffix(&".rs") + .rand_bytes(12) + .tempfile() + .unwrap(); + let name = tmpfile.path().file_name().unwrap().to_str().unwrap(); + assert!(name.starts_with("tmp")); + assert!(name.ends_with(".rs")); + assert_eq!(name.len(), 18); +} + +#[test] +fn test_append() { + configure_wasi_temp_dir(); + + let mut tmpfile = Builder::new().append(true).tempfile().unwrap(); + tmpfile.write_all(b"a").unwrap(); + tmpfile.seek(SeekFrom::Start(0)).unwrap(); + tmpfile.write_all(b"b").unwrap(); + + tmpfile.seek(SeekFrom::Start(0)).unwrap(); + let mut buf = vec![0u8; 1]; + tmpfile.read_exact(&mut buf).unwrap(); + assert_eq!(buf, b"a"); +} + +#[test] +fn test_reopen() { + configure_wasi_temp_dir(); + + let source = NamedTempFile::new().unwrap(); + let mut first = source.reopen().unwrap(); + let mut second = source.reopen().unwrap(); + drop(source); + + write!(first, "abcde").expect("write failed"); + let mut buf = String::new(); + second.read_to_string(&mut buf).unwrap(); + assert_eq!("abcde", buf); +} + +#[test] +fn test_into_file() { + configure_wasi_temp_dir(); + + let mut file = NamedTempFile::new().unwrap(); + let path = file.path().to_owned(); + write!(file, "abcde").expect("write failed"); + + assert!(path.exists()); + let mut file = file.into_file(); + assert!(!path.exists()); + + file.seek(SeekFrom::Start(0)).unwrap(); + let mut buf = String::new(); + file.read_to_string(&mut buf).unwrap(); + assert_eq!("abcde", buf); +} + +#[test] +fn test_immut() { + configure_wasi_temp_dir(); + + let tmpfile = NamedTempFile::new().unwrap(); + (&tmpfile).write_all(b"abcde").unwrap(); + (&tmpfile).seek(SeekFrom::Start(0)).unwrap(); + let mut buf = String::new(); + (&tmpfile).read_to_string(&mut buf).unwrap(); + assert_eq!("abcde", buf); +} + +#[test] +fn test_temppath() { + configure_wasi_temp_dir(); + + let mut tmpfile = NamedTempFile::new().unwrap(); + write!(tmpfile, "abcde").unwrap(); + + let path = tmpfile.into_temp_path(); + assert!(path.is_file()); +} + +#[test] +fn test_temppath_persist() { + configure_wasi_temp_dir(); + + let mut tmpfile = NamedTempFile::new().unwrap(); + write!(tmpfile, "abcde").unwrap(); + + let tmppath = tmpfile.into_temp_path(); + + let old_path = tmppath.to_path_buf(); + let persist_path = env::temp_dir().join("persisted_temppath_file"); + + { + assert!(exists(&old_path)); + tmppath.persist(&persist_path).unwrap(); + assert!(!exists(&old_path)); + } + + { + // Try opening it at the new path. + let mut f = File::open(&persist_path).unwrap(); + f.seek(SeekFrom::Start(0)).unwrap(); + let mut buf = String::new(); + f.read_to_string(&mut buf).unwrap(); + assert_eq!("abcde", buf); + } + + std::fs::remove_file(&persist_path).unwrap(); +} + +#[test] +fn test_temppath_persist_noclobber() { + configure_wasi_temp_dir(); + + let mut tmpfile = NamedTempFile::new().unwrap(); + write!(tmpfile, "abcde").unwrap(); + + let mut tmppath = tmpfile.into_temp_path(); + + let old_path = tmppath.to_path_buf(); + let persist_target = NamedTempFile::new().unwrap(); + let persist_path = persist_target.path().to_path_buf(); + + assert!(exists(&old_path)); + + { + tmppath = tmppath.persist_noclobber(&persist_path).unwrap_err().into(); + assert!(exists(&old_path)); + std::fs::remove_file(&persist_path).unwrap(); + drop(persist_target); + } + + tmppath.persist_noclobber(&persist_path).unwrap(); + + // Try opening it at the new path. + let mut f = File::open(&persist_path).unwrap(); + f.seek(SeekFrom::Start(0)).unwrap(); + let mut buf = String::new(); + f.read_to_string(&mut buf).unwrap(); + assert_eq!("abcde", buf); + std::fs::remove_file(&persist_path).unwrap(); +} + +#[test] +fn temp_path_from_existing() { + configure_wasi_temp_dir(); + + let tmp_dir = tempdir().unwrap(); + let tmp_file_path_1 = tmp_dir.path().join("testfile1"); + let tmp_file_path_2 = tmp_dir.path().join("testfile2"); + + File::create(&tmp_file_path_1).unwrap(); + assert!(tmp_file_path_1.exists(), "Test file 1 hasn't been created"); + + File::create(&tmp_file_path_2).unwrap(); + assert!(tmp_file_path_2.exists(), "Test file 2 hasn't been created"); + + let tmp_path = TempPath::from_path(&tmp_file_path_1); + assert!( + tmp_file_path_1.exists(), + "Test file has been deleted before dropping TempPath" + ); + + drop(tmp_path); + assert!( + !tmp_file_path_1.exists(), + "Test file exists after dropping TempPath" + ); + assert!( + tmp_file_path_2.exists(), + "Test file 2 has been deleted before dropping TempDir" + ); +} + +#[test] +#[allow(unreachable_code)] +fn temp_path_from_argument_types() { + // This just has to compile + return; + + TempPath::from_path(""); + TempPath::from_path(String::new()); + TempPath::from_path(OsStr::new("")); + TempPath::from_path(OsString::new()); + TempPath::from_path(Path::new("")); + TempPath::from_path(PathBuf::new()); + TempPath::from_path(PathBuf::new().into_boxed_path()); +} + +#[test] +fn test_write_after_close() { + configure_wasi_temp_dir(); + + let path = NamedTempFile::new().unwrap().into_temp_path(); + File::create(path).unwrap().write_all(b"test").unwrap(); +} + +#[test] +fn test_change_dir() { + configure_wasi_temp_dir(); + + let dir_a = tempdir().unwrap(); + let dir_b = tempdir().unwrap(); + + std::env::set_current_dir(&dir_a).expect("failed to change to directory ~"); + let tmpfile = NamedTempFile::new_in(".").unwrap(); + let path = std::env::current_dir().unwrap().join(tmpfile.path()); + std::env::set_current_dir(&dir_b).expect("failed to change to directory B"); + drop(tmpfile); + assert!(!exists(path)); + + drop(dir_a); + drop(dir_b); +} + +#[test] +fn test_change_dir_make() { + configure_wasi_temp_dir(); + + let dir_a = tempdir().unwrap(); + let dir_b = tempdir().unwrap(); + + std::env::set_current_dir(&dir_a).expect("failed to change to directory A"); + let tmpfile = Builder::new().make_in(".", |p| File::create(p)).unwrap(); + let path = std::env::current_dir().unwrap().join(tmpfile.path()); + std::env::set_current_dir(&dir_b).expect("failed to change to directory B"); + drop(tmpfile); + assert!(!exists(path)); + + drop(dir_a); + drop(dir_b); +} + +#[test] +fn test_into_parts() { + configure_wasi_temp_dir(); + + let mut file = NamedTempFile::new().unwrap(); + write!(file, "abcd").expect("write failed"); + + let (mut file, temp_path) = file.into_parts(); + + let path = temp_path.to_path_buf(); + + assert!(path.exists()); + drop(temp_path); + assert!(!path.exists()); + + write!(file, "efgh").expect("write failed"); + + file.seek(SeekFrom::Start(0)).unwrap(); + let mut buf = String::new(); + file.read_to_string(&mut buf).unwrap(); + assert_eq!("abcdefgh", buf); +} + +#[test] +fn test_from_parts() { + configure_wasi_temp_dir(); + + let mut file = NamedTempFile::new().unwrap(); + write!(file, "abcd").expect("write failed"); + + let (file, temp_path) = file.into_parts(); + + let file = NamedTempFile::from_parts(file, temp_path); + + assert!(file.path().exists()); +} + +#[test] +fn test_keep() { + configure_wasi_temp_dir(); + + let mut tmpfile = NamedTempFile::new().unwrap(); + write!(tmpfile, "abcde").unwrap(); + let (mut f, temp_path) = tmpfile.into_parts(); + let path; + { + assert!(exists(&temp_path)); + path = temp_path.keep().unwrap(); + assert!(exists(&path)); + + // Check original file + f.seek(SeekFrom::Start(0)).unwrap(); + let mut buf = String::new(); + f.read_to_string(&mut buf).unwrap(); + assert_eq!("abcde", buf); + } + + { + // Try opening it again. + let mut f = File::open(&path).unwrap(); + f.seek(SeekFrom::Start(0)).unwrap(); + let mut buf = String::new(); + f.read_to_string(&mut buf).unwrap(); + assert_eq!("abcde", buf); + } + std::fs::remove_file(&path).unwrap(); +} + +#[test] +fn test_disable_cleanup() { + configure_wasi_temp_dir(); + + // Case 0: never mark as "disable cleanup" + // Case 1: enable "disable cleanup" in the builder, don't touch it after. + // Case 2: enable "disable cleanup" in the builder, turn it off after. + // Case 3: don't enable disable cleanup in the builder, turn it on after. + + for case in 0..4 { + let in_builder = case & 1 > 0; + let toggle = case & 2 > 0; + let mut tmpfile = Builder::new() + .disable_cleanup(in_builder) + .tempfile() + .unwrap(); + write!(tmpfile, "abcde").unwrap(); + if toggle { + tmpfile.disable_cleanup(!in_builder); + } + + let path = tmpfile.path().to_owned(); + drop(tmpfile); + + if in_builder ^ toggle { + // Try opening it again. + let mut f = File::open(&path).unwrap(); + let mut buf = String::new(); + f.read_to_string(&mut buf).unwrap(); + assert_eq!("abcde", buf); + std::fs::remove_file(&path).unwrap(); + } else { + assert!(!path.exists(), "tempfile wasn't deleted"); + } + } +} + +#[test] +fn test_make() { + configure_wasi_temp_dir(); + + let tmpfile = Builder::new().make(|path| File::create(path)).unwrap(); + + assert!(tmpfile.path().is_file()); +} + +#[test] +fn test_make_in() { + configure_wasi_temp_dir(); + + let tmp_dir = tempdir().unwrap(); + + let tmpfile = Builder::new() + .make_in(tmp_dir.path(), |path| File::create(path)) + .unwrap(); + + assert!(tmpfile.path().is_file()); + assert_eq!(tmpfile.path().parent(), Some(tmp_dir.path())); +} + +#[test] +fn test_make_fnmut() { + configure_wasi_temp_dir(); + + let mut count = 0; + + // Show that an FnMut can be used. + let tmpfile = Builder::new() + .make(|path| { + count += 1; + File::create(path) + }) + .unwrap(); + + assert!(tmpfile.path().is_file()); +} + +#[cfg(unix)] +#[test] +fn test_make_uds() { + use std::os::unix::net::UnixListener; + + let temp_sock = Builder::new() + .prefix("tmp") + .suffix(".sock") + .rand_bytes(12) + .make(|path| UnixListener::bind(path)) + .unwrap(); + + assert!(temp_sock.path().exists()); +} + +#[cfg(unix)] +#[test] +fn test_make_uds_conflict() { + use std::io::ErrorKind; + use std::os::unix::net::UnixListener; + + let sockets = std::iter::repeat_with(|| { + Builder::new() + .prefix("tmp") + .suffix(".sock") + .rand_bytes(1) + .make(|path| UnixListener::bind(path)) + }) + .take_while(|r| match r { + Ok(_) => true, + Err(e) if matches!(e.kind(), ErrorKind::AddrInUse | ErrorKind::AlreadyExists) => false, + Err(e) => panic!("unexpected error {e}"), + }) + .collect::, _>>() + .unwrap(); + + // Number of sockets we can create. Depends on whether or not the filesystem is case sensitive. + + #[cfg(target_os = "macos")] + const NUM_FILES: usize = 36; + #[cfg(not(target_os = "macos"))] + const NUM_FILES: usize = 62; + + assert_eq!(sockets.len(), NUM_FILES); + + for socket in sockets { + assert!(socket.path().exists()); + } +} + +/// Make sure we re-seed with system randomness if we run into a conflict. +#[test] +fn test_reseed() { + configure_wasi_temp_dir(); + + // Deterministic seed. + fastrand::seed(42); + + // I need to create 5 conflicts but I can't just make 5 temporary files because we fork the RNG + // each time we create a file. + let mut attempts = 0; + let mut files: Vec<_> = Vec::new(); + let _ = Builder::new().make(|path| -> io::Result { + if attempts == 5 { + return Err(io::Error::new(io::ErrorKind::Other, "stop!")); + } + attempts += 1; + let f = File::options() + .write(true) + .create_new(true) + .open(path) + .unwrap(); + + files.push(NamedTempFile::from_parts(f, TempPath::from_path(path))); + Err(io::Error::new(io::ErrorKind::AlreadyExists, "fake!")) + }); + + assert_eq!(5, attempts); + attempts = 0; + + // Re-seed to cause a conflict. + fastrand::seed(42); + + let _f = Builder::new() + .make(|path| { + attempts += 1; + File::options().write(true).create_new(true).open(path) + }) + .unwrap(); + + // We expect exactly three conflict before we re-seed with system randomness. + assert_eq!(4, attempts); +} + +// Issue #224. +#[test] +fn test_overly_generic_bounds() { + pub struct Foo(T); + + impl Foo + where + T: Sync + Send + 'static, + for<'a> &'a T: Write + Read, + { + pub fn new(foo: T) -> Self { + Self(foo) + } + } + + // Don't really need to run this. Only care if it compiles. + if let Ok(file) = File::open("i_do_not_exist") { + let mut f; + let _x = { + f = Foo::new(file); + &mut f + }; + } +} diff --git a/tools/vendor/tempfile/tests/spooled.rs b/tools/vendor/tempfile/tests/spooled.rs new file mode 100644 index 0000000000..21cfeb7ebd --- /dev/null +++ b/tools/vendor/tempfile/tests/spooled.rs @@ -0,0 +1,377 @@ +#![deny(rust_2018_idioms)] + +use std::io::{Read, Seek, SeekFrom, Write}; + +use tempfile::{env, spooled_tempfile, spooled_tempfile_in, SpooledTempFile}; + +/// For the wasi platforms, `std::env::temp_dir` will panic. For those targets, configure the /tmp +/// directory instead as the base directory for temp files. +fn configure_wasi_temp_dir() { + if cfg!(target_os = "wasi") { + let _ = tempfile::env::override_temp_dir(std::path::Path::new("/tmp")); + } +} + +#[test] +fn test_automatic_rollover() { + configure_wasi_temp_dir(); + + let mut t = spooled_tempfile(10); + let mut buf = Vec::new(); + + assert!(!t.is_rolled()); + assert_eq!(t.stream_position().unwrap(), 0); + assert_eq!(t.read_to_end(&mut buf).unwrap(), 0); + assert_eq!(buf.as_slice(), b""); + buf.clear(); + + assert_eq!(t.write(b"abcde").unwrap(), 5); + + assert!(!t.is_rolled()); + assert_eq!(t.seek(SeekFrom::Start(0)).unwrap(), 0); + assert_eq!(t.read_to_end(&mut buf).unwrap(), 5); + assert_eq!(buf.as_slice(), b"abcde"); + + assert_eq!(t.write(b"fghijklmno").unwrap(), 10); + + assert_eq!(t.stream_position().unwrap(), 15); + assert!(t.is_rolled()); +} + +#[test] +fn test_custom_dir() { + configure_wasi_temp_dir(); + + { + let mut t = spooled_tempfile_in(10, env::temp_dir()); + t.roll() + .expect("failed to roll temp file in a specified directory"); + } + + { + let mut t = spooled_tempfile_in(10, "/does-not-exist/"); + t.roll() + .expect_err("should fail to roll the temporary file into a nonexistent tempdir"); + } +} + +#[test] +fn test_explicit_rollover() { + configure_wasi_temp_dir(); + + let mut t = SpooledTempFile::new(100); + assert_eq!(t.write(b"abcdefghijklmnopqrstuvwxyz").unwrap(), 26); + assert_eq!(t.stream_position().unwrap(), 26); + assert!(!t.is_rolled()); + + // roll over explicitly + assert!(t.roll().is_ok()); + assert!(t.is_rolled()); + assert_eq!(t.stream_position().unwrap(), 26); + + let mut buf = Vec::new(); + assert_eq!(t.read_to_end(&mut buf).unwrap(), 0); + assert_eq!(buf.as_slice(), b""); + buf.clear(); + + assert_eq!(t.seek(SeekFrom::Start(0)).unwrap(), 0); + assert_eq!(t.read_to_end(&mut buf).unwrap(), 26); + assert_eq!(buf.as_slice(), b"abcdefghijklmnopqrstuvwxyz"); + assert_eq!(t.stream_position().unwrap(), 26); +} + +// called by test_seek_{buffer, file} +// assumes t is empty and offset is 0 to start +fn test_seek(t: &mut SpooledTempFile) { + configure_wasi_temp_dir(); + + assert_eq!(t.write(b"abcdefghijklmnopqrstuvwxyz").unwrap(), 26); + + assert_eq!(t.stream_position().unwrap(), 26); // tell() + assert_eq!(t.seek(SeekFrom::Current(-1)).unwrap(), 25); + assert_eq!(t.seek(SeekFrom::Current(1)).unwrap(), 26); + assert_eq!(t.seek(SeekFrom::Current(1)).unwrap(), 27); + assert_eq!(t.seek(SeekFrom::Current(-27)).unwrap(), 0); + assert!(t.seek(SeekFrom::Current(-1)).is_err()); + assert!(t.seek(SeekFrom::Current(-1245)).is_err()); + + assert_eq!(t.seek(SeekFrom::Start(0)).unwrap(), 0); + assert_eq!(t.seek(SeekFrom::Start(1)).unwrap(), 1); + assert_eq!(t.seek(SeekFrom::Start(26)).unwrap(), 26); + assert_eq!(t.seek(SeekFrom::Start(27)).unwrap(), 27); + // // these are build errors + // assert!(t.seek(SeekFrom::Start(-1)).is_err()); + // assert!(t.seek(SeekFrom::Start(-1000)).is_err()); + + assert_eq!(t.seek(SeekFrom::End(0)).unwrap(), 26); + assert_eq!(t.seek(SeekFrom::End(-1)).unwrap(), 25); + assert_eq!(t.seek(SeekFrom::End(-26)).unwrap(), 0); + assert!(t.seek(SeekFrom::End(-27)).is_err()); + assert!(t.seek(SeekFrom::End(-99)).is_err()); + assert_eq!(t.seek(SeekFrom::End(1)).unwrap(), 27); + assert_eq!(t.seek(SeekFrom::End(1)).unwrap(), 27); +} + +#[test] +fn test_seek_buffer() { + configure_wasi_temp_dir(); + + let mut t = spooled_tempfile(100); + test_seek(&mut t); +} + +#[test] +fn test_seek_file() { + configure_wasi_temp_dir(); + + let mut t = SpooledTempFile::new(10); + test_seek(&mut t); +} + +fn test_seek_read(t: &mut SpooledTempFile) { + configure_wasi_temp_dir(); + + assert_eq!(t.write(b"abcdefghijklmnopqrstuvwxyz").unwrap(), 26); + + let mut buf = Vec::new(); + + // we're at the end + assert_eq!(t.read_to_end(&mut buf).unwrap(), 0); + assert_eq!(buf.as_slice(), b""); + buf.clear(); + + // seek to start, read whole thing + assert_eq!(t.seek(SeekFrom::Start(0)).unwrap(), 0); + assert_eq!(t.read_to_end(&mut buf).unwrap(), 26); + assert_eq!(buf.as_slice(), b"abcdefghijklmnopqrstuvwxyz"); + buf.clear(); + + // now we're at the end again + assert_eq!(t.stream_position().unwrap(), 26); // tell() + assert_eq!(t.read_to_end(&mut buf).unwrap(), 0); + assert_eq!(buf.as_slice(), b""); + buf.clear(); + + // seek to somewhere in the middle, read a bit + assert_eq!(t.seek(SeekFrom::Start(5)).unwrap(), 5); + let mut buf = [0; 5]; + assert!(t.read_exact(&mut buf).is_ok()); + assert_eq!(buf, *b"fghij"); + + // read again from current spot + assert_eq!(t.stream_position().unwrap(), 10); // tell() + assert!(t.read_exact(&mut buf).is_ok()); + assert_eq!(buf, *b"klmno"); + + let mut buf = [0; 15]; + // partial read + assert_eq!(t.read(&mut buf).unwrap(), 11); + assert_eq!(buf[0..11], *b"pqrstuvwxyz"); + + // try to read off the end: UnexpectedEof + assert!(t.read_exact(&mut buf).is_err()); +} + +#[test] +fn test_seek_read_buffer() { + configure_wasi_temp_dir(); + + let mut t = spooled_tempfile(100); + test_seek_read(&mut t); +} + +#[test] +fn test_seek_read_file() { + configure_wasi_temp_dir(); + + let mut t = SpooledTempFile::new(10); + test_seek_read(&mut t); +} + +fn test_overwrite_middle(t: &mut SpooledTempFile) { + configure_wasi_temp_dir(); + + assert_eq!(t.write(b"abcdefghijklmnopqrstuvwxyz").unwrap(), 26); + + assert_eq!(t.seek(SeekFrom::Start(10)).unwrap(), 10); + assert_eq!(t.write(b"0123456789").unwrap(), 10); + assert_eq!(t.seek(SeekFrom::Start(0)).unwrap(), 0); + + let mut buf = Vec::new(); + assert_eq!(t.read_to_end(&mut buf).unwrap(), 26); + assert_eq!(buf.as_slice(), b"abcdefghij0123456789uvwxyz"); +} + +#[test] +fn test_overwrite_middle_of_buffer() { + let mut t = spooled_tempfile(100); + test_overwrite_middle(&mut t); +} + +#[test] +fn test_overwrite_middle_of_file() { + let mut t = SpooledTempFile::new(10); + test_overwrite_middle(&mut t); +} + +#[test] +fn test_overwrite_and_extend_buffer() { + let mut t = spooled_tempfile(100); + assert_eq!(t.write(b"abcdefghijklmnopqrstuvwxyz").unwrap(), 26); + assert_eq!(t.seek(SeekFrom::End(-5)).unwrap(), 21); + assert_eq!(t.write(b"0123456789").unwrap(), 10); + assert_eq!(t.seek(SeekFrom::Start(0)).unwrap(), 0); + let mut buf = Vec::new(); + assert_eq!(t.read_to_end(&mut buf).unwrap(), 31); + assert_eq!(buf.as_slice(), b"abcdefghijklmnopqrstu0123456789"); + assert!(!t.is_rolled()); +} + +#[test] +fn test_overwrite_and_extend_rollover() { + configure_wasi_temp_dir(); + + let mut t = SpooledTempFile::new(20); + assert_eq!(t.write(b"abcdefghijklmno").unwrap(), 15); + assert!(!t.is_rolled()); + assert_eq!(t.seek(SeekFrom::End(-5)).unwrap(), 10); + assert_eq!(t.stream_position().unwrap(), 10); // tell() + assert!(!t.is_rolled()); + assert_eq!(t.write(b"0123456789)!@#$%^&*(").unwrap(), 20); + assert!(t.is_rolled()); + assert_eq!(t.stream_position().unwrap(), 30); // tell() + let mut buf = Vec::new(); + assert_eq!(t.seek(SeekFrom::Start(0)).unwrap(), 0); + assert_eq!(t.read_to_end(&mut buf).unwrap(), 30); + assert_eq!(buf.as_slice(), b"abcdefghij0123456789)!@#$%^&*("); +} + +fn test_sparse(t: &mut SpooledTempFile) { + assert_eq!(t.write(b"abcde").unwrap(), 5); + assert_eq!(t.seek(SeekFrom::Current(5)).unwrap(), 10); + assert_eq!(t.write(b"klmno").unwrap(), 5); + assert_eq!(t.seek(SeekFrom::Start(0)).unwrap(), 0); + let mut buf = Vec::new(); + assert_eq!(t.read_to_end(&mut buf).unwrap(), 15); + assert_eq!(buf.as_slice(), b"abcde\0\0\0\0\0klmno"); +} + +#[test] +fn test_sparse_buffer() { + let mut t = spooled_tempfile(100); + test_sparse(&mut t); +} + +#[test] +fn test_sparse_file() { + configure_wasi_temp_dir(); + + let mut t = SpooledTempFile::new(1); + test_sparse(&mut t); +} + +#[test] +fn test_sparse_write_rollover() { + configure_wasi_temp_dir(); + + let mut t = spooled_tempfile(10); + assert_eq!(t.write(b"abcde").unwrap(), 5); + assert!(!t.is_rolled()); + assert_eq!(t.seek(SeekFrom::Current(5)).unwrap(), 10); + assert!(!t.is_rolled()); + assert_eq!(t.write(b"klmno").unwrap(), 5); + assert!(t.is_rolled()); + assert_eq!(t.seek(SeekFrom::Start(0)).unwrap(), 0); + let mut buf = Vec::new(); + assert_eq!(t.read_to_end(&mut buf).unwrap(), 15); + assert_eq!(buf.as_slice(), b"abcde\0\0\0\0\0klmno"); +} + +fn test_set_len(t: &mut SpooledTempFile) { + let mut buf: Vec = Vec::new(); + + assert_eq!(t.write(b"abcdefghijklmnopqrstuvwxyz").unwrap(), 26); + + // truncate to 10 bytes + assert!(t.set_len(10).is_ok()); + + // position should not have moved + assert_eq!(t.stream_position().unwrap(), 26); // tell() + + assert_eq!(t.read_to_end(&mut buf).unwrap(), 0); + assert_eq!(buf.as_slice(), b""); + assert_eq!(t.stream_position().unwrap(), 26); // tell() + buf.clear(); + + // read whole thing + assert_eq!(t.seek(SeekFrom::Start(0)).unwrap(), 0); + assert_eq!(t.read_to_end(&mut buf).unwrap(), 10); + assert_eq!(buf.as_slice(), b"abcdefghij"); + buf.clear(); + + // set_len to expand beyond the end + assert!(t.set_len(40).is_ok()); + assert_eq!(t.stream_position().unwrap(), 10); // tell() + assert_eq!(t.seek(SeekFrom::Start(0)).unwrap(), 0); + assert_eq!(t.read_to_end(&mut buf).unwrap(), 40); + assert_eq!( + buf.as_slice(), + &b"abcdefghij\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"[..] + ); +} + +#[test] +fn test_set_len_buffer() { + let mut t = spooled_tempfile(100); + test_set_len(&mut t); +} + +#[test] +fn test_set_len_file() { + let mut t = spooled_tempfile(100); + test_set_len(&mut t); +} + +#[test] +fn test_set_len_rollover() { + configure_wasi_temp_dir(); + + let mut buf: Vec = Vec::new(); + + let mut t = spooled_tempfile(10); + assert_eq!(t.write(b"abcde").unwrap(), 5); + assert!(!t.is_rolled()); + assert_eq!(t.stream_position().unwrap(), 5); // tell() + + assert_eq!(t.seek(SeekFrom::Start(0)).unwrap(), 0); + assert_eq!(t.read_to_end(&mut buf).unwrap(), 5); + assert_eq!(buf.as_slice(), b"abcde"); + assert_eq!(t.stream_position().unwrap(), 5); // tell() + buf.clear(); + + assert!(t.set_len(20).is_ok()); + assert!(t.is_rolled()); + assert_eq!(t.stream_position().unwrap(), 5); // tell() + assert_eq!(t.seek(SeekFrom::Start(0)).unwrap(), 0); + assert_eq!(t.read_to_end(&mut buf).unwrap(), 20); + assert_eq!(buf.as_slice(), b"abcde\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); +} + +#[test] +fn test_write_overflow() { + configure_wasi_temp_dir(); + + let mut t = spooled_tempfile(10); + t.seek(SeekFrom::Start(u64::MAX)).unwrap(); + assert!(t.write(b"abcde").is_err()); +} + +#[cfg(target_pointer_width = "32")] +#[test] +fn test_set_len_truncation() { + configure_wasi_temp_dir(); + + let mut t = spooled_tempfile(100); + assert!(t.set_len(usize::MAX as u64 + 5).is_ok()); + assert!(t.is_rolled()); +} diff --git a/tools/vendor/tempfile/tests/tempdir.rs b/tools/vendor/tempfile/tests/tempdir.rs new file mode 100644 index 0000000000..cccd543d92 --- /dev/null +++ b/tools/vendor/tempfile/tests/tempdir.rs @@ -0,0 +1,196 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![deny(rust_2018_idioms)] + +use std::fs; +use std::path::Path; + +use tempfile::{Builder, TempDir}; + +/// For the wasi platforms, `std::env::temp_dir` will panic. For those targets, configure the /tmp +/// directory instead as the base directory for temp files. +fn configure_wasi_temp_dir() { + if cfg!(target_os = "wasi") { + let _ = tempfile::env::override_temp_dir(std::path::Path::new("/tmp")); + } +} + +#[test] +fn test_tempdir() { + configure_wasi_temp_dir(); + + let path = { + let p = Builder::new().prefix("foobar").tempdir().unwrap(); + let p = p.path(); + assert!(p.to_str().unwrap().contains("foobar")); + p.to_path_buf() + }; + assert!(!path.exists()); +} + +#[test] +fn test_prefix() { + configure_wasi_temp_dir(); + + let tmpfile = TempDir::with_prefix("prefix").unwrap(); + let name = tmpfile.path().file_name().unwrap().to_str().unwrap(); + assert!(name.starts_with("prefix")); +} + +#[test] +fn test_suffix() { + configure_wasi_temp_dir(); + + let tmpfile = TempDir::with_suffix("suffix").unwrap(); + let name = tmpfile.path().file_name().unwrap().to_str().unwrap(); + assert!(name.ends_with("suffix")); +} + +#[test] +fn test_customnamed() { + configure_wasi_temp_dir(); + + let tmpfile = Builder::new() + .prefix("prefix") + .suffix("suffix") + .rand_bytes(12) + .tempdir() + .unwrap(); + let name = tmpfile.path().file_name().unwrap().to_str().unwrap(); + assert!(name.starts_with("prefix")); + assert!(name.ends_with("suffix")); + assert_eq!(name.len(), 24); +} + +#[test] +#[cfg_attr(target_os = "wasi", ignore = "thread::spawn is not supported")] +fn test_rm_tempdir_threading() { + configure_wasi_temp_dir(); + + use std::sync::mpsc::channel; + use std::thread; + + let (tx, rx) = channel(); + let f = move || { + let tmp = TempDir::new().unwrap(); + tx.send(tmp.path().to_path_buf()).unwrap(); + panic!("panic to unwind past `tmp`"); + }; + let _ = thread::spawn(f).join(); + let path = rx.recv().unwrap(); + assert!(!path.exists()); + + let tmp = TempDir::new().unwrap(); + let path = tmp.path().to_path_buf(); + let f = move || { + let _tmp = tmp; + panic!("panic to unwind past `tmp`"); + }; + let _ = thread::spawn(f).join(); + assert!(!path.exists()); + + let path; + { + let f = move || TempDir::new().unwrap(); + + let tmp = thread::spawn(f).join().unwrap(); + path = tmp.path().to_path_buf(); + assert!(path.exists()); + } + assert!(!path.exists()); +} + +#[test] +fn test_tempdir_keep() { + configure_wasi_temp_dir(); + + let path = { + let tmp = TempDir::new().unwrap(); + tmp.keep() + }; + assert!(path.exists()); + fs::remove_dir_all(&path).unwrap(); + assert!(!path.exists()); +} + +#[test] +fn test_tmpdir_close() { + configure_wasi_temp_dir(); + + let tmp = TempDir::new().unwrap(); + let path = tmp.path().to_path_buf(); + assert!(path.exists()); + tmp.close().unwrap(); + assert!(!path.exists()); +} + +#[test] +#[cfg_attr(target_os = "wasi", ignore = "unwinding is not supported")] +fn dont_double_panic() { + configure_wasi_temp_dir(); + + use std::thread; + let r: Result<(), _> = thread::spawn(move || { + let tmpdir = TempDir::new().unwrap(); + // Remove the temporary directory so that TempDir sees + // an error on drop + fs::remove_dir(tmpdir.path()).unwrap(); + // Panic. If TempDir panics *again* due to the rmdir + // error then the process will abort. + panic!(); + }) + .join(); + assert!(r.is_err()); +} + +#[test] +fn pass_as_asref_path() { + configure_wasi_temp_dir(); + + let tempdir = TempDir::new().unwrap(); + takes_asref_path(&tempdir); + + fn takes_asref_path>(path: T) { + let path = path.as_ref(); + assert!(path.exists()); + } +} + +#[test] +fn test_disable_cleanup() { + configure_wasi_temp_dir(); + + // Case 0: never mark as "disable cleanup" + // Case 1: enable "disable cleanup" in the builder, don't touch it after. + // Case 2: enable "disable cleanup" in the builder, turn it off after. + // Case 3: don't enable disable cleanup in the builder, turn it on after. + for case in 0..4 { + let in_builder = case & 1 > 0; + let toggle = case & 2 > 0; + let mut tmpdir = Builder::new() + .disable_cleanup(in_builder) + .tempdir() + .unwrap(); + if toggle { + tmpdir.disable_cleanup(!in_builder); + } + + let path = tmpdir.path().to_owned(); + drop(tmpdir); + + if in_builder ^ toggle { + assert!(path.exists()); + fs::remove_dir(path).unwrap(); + } else { + assert!(!path.exists(), "tempdir wasn't deleted"); + } + } +} diff --git a/tools/vendor/tempfile/tests/tempfile.rs b/tools/vendor/tempfile/tests/tempfile.rs new file mode 100644 index 0000000000..8d245c5fed --- /dev/null +++ b/tools/vendor/tempfile/tests/tempfile.rs @@ -0,0 +1,78 @@ +#![deny(rust_2018_idioms)] + +use std::fs; +use std::io::{Read, Seek, SeekFrom, Write}; +#[cfg(target_os = "linux")] +use std::{ + sync::mpsc::{sync_channel, TryRecvError}, + thread, +}; + +#[test] +fn test_basic() { + // For the wasi platforms, `std::env::temp_dir` will panic. For those targets, configure the /tmp + // directory instead as the base directory for temp files. + #[cfg(target_os = "wasi")] + let _ = tempfile::env::override_temp_dir(std::path::Path::new("/tmp")); + + let mut tmpfile = tempfile::tempfile().unwrap(); + write!(tmpfile, "abcde").unwrap(); + tmpfile.seek(SeekFrom::Start(0)).unwrap(); + let mut buf = String::new(); + tmpfile.read_to_string(&mut buf).unwrap(); + assert_eq!("abcde", buf); +} + +#[test] +fn test_cleanup() { + // For the wasi platforms, `std::env::temp_dir` will panic. For those targets, configure the /tmp + // directory instead as the base directory for temp files. + #[cfg(target_os = "wasi")] + let _ = tempfile::env::override_temp_dir(std::path::Path::new("/tmp")); + + let tmpdir = tempfile::tempdir().unwrap(); + { + let mut tmpfile = tempfile::tempfile_in(&tmpdir).unwrap(); + write!(tmpfile, "abcde").unwrap(); + } + let num_files = fs::read_dir(&tmpdir).unwrap().count(); + assert!(num_files == 0); +} + +// Only run this test on Linux. MacOS doesn't like us creating so many files, apparently. +#[cfg(target_os = "linux")] +#[test] +fn test_pathological_cleaner() { + let tmpdir = tempfile::tempdir().unwrap(); + let (tx, rx) = sync_channel(0); + let cleaner_thread = thread::spawn(move || { + let tmp_path = rx.recv().unwrap(); + while rx.try_recv() == Err(TryRecvError::Empty) { + let files = fs::read_dir(&tmp_path).unwrap(); + for f in files { + // skip errors + if f.is_err() { + continue; + } + let f = f.unwrap(); + let _ = fs::remove_file(f.path()); + } + } + }); + + // block until cleaner_thread makes progress + tx.send(tmpdir.path().to_owned()).unwrap(); + // need 40-400 iterations to encounter race with cleaner on original system + for _ in 0..10000 { + let mut tmpfile = tempfile::tempfile_in(&tmpdir).unwrap(); + write!(tmpfile, "abcde").unwrap(); + tmpfile.seek(SeekFrom::Start(0)).unwrap(); + let mut buf = String::new(); + tmpfile.read_to_string(&mut buf).unwrap(); + assert_eq!("abcde", buf); + } + + // close the channel to make cleaner_thread exit + drop(tx); + cleaner_thread.join().expect("The cleaner thread failed"); +} diff --git a/tools/vendor/termtree/.cargo-checksum.json b/tools/vendor/termtree/.cargo-checksum.json new file mode 100644 index 0000000000..540c36bee7 --- /dev/null +++ b/tools/vendor/termtree/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"5e2ebee6ed7a679681ca497b1fe2eca3c9e2e764d3b89fdf65ffe591accffb41","Cargo.lock":"e3f51afad3ca92c1740e46f0c43e0efc3ae6e27fb4d1d61a203cfb6acb424610","Cargo.toml":"af900495afd85e58f154b6ed37ae6abf3e3771e84dbedfdd0cd9af335e38efb5","Cargo.toml.orig":"10c18aea175d4b585bfab654fad130a724d6fcc011f07dadbbb0dfb0fecd1329","LICENSE-MIT":"6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6","README.md":"7eac81f10fbebb1df5a57eded07f91baa9092fc36f64aeaa55b0bb69865ea36c","examples/pretty-test.rs":"7379b44f151d6a7067e7187254ce8e5147e4ffb9a40d70e329dee0e3c7348004","examples/tree.rs":"4872086d9f32d88343d5f24a04cdffeb16b99b944fd504f06ac8173c557f01cf","src/lib.rs":"617496cd4934c601ff2c319d00c6368d06d65f34e54759e1330ab0f6579d500b","src/tests.rs":"ba005aa7a409a5daf0a788e67cd96cd8f7e963c59bd83ccaeeb39fbe1bc58597"},"package":"8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"} \ No newline at end of file diff --git a/tools/vendor/termtree/.cargo_vcs_info.json b/tools/vendor/termtree/.cargo_vcs_info.json new file mode 100644 index 0000000000..bdb6550ef5 --- /dev/null +++ b/tools/vendor/termtree/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "976ddd79b0af191bdd1325adfa0c4baa9c773f63" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/tools/vendor/termtree/Cargo.lock b/tools/vendor/termtree/Cargo.lock new file mode 100644 index 0000000000..a9a02afff0 --- /dev/null +++ b/tools/vendor/termtree/Cargo.lock @@ -0,0 +1,184 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "similar" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" + +[[package]] +name = "snapbox" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40e14d10e4c2b4331ac24c33baa5a03e1fbca81c045b285b53b2a612d28569fb" +dependencies = [ + "anstream", + "anstyle", + "normalize-line-endings", + "similar", + "snapbox-macros", +] + +[[package]] +name = "snapbox-macros" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f4c14672714436c09254801c934b203196a51182a5107fb76591c7cc56424d" +dependencies = [ + "anstream", +] + +[[package]] +name = "termtree" +version = "0.5.1" +dependencies = [ + "snapbox", +] + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/tools/vendor/termtree/Cargo.toml b/tools/vendor/termtree/Cargo.toml new file mode 100644 index 0000000000..f453d606d0 --- /dev/null +++ b/tools/vendor/termtree/Cargo.toml @@ -0,0 +1,175 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.74" +name = "termtree" +version = "0.5.1" +build = false +include = [ + "build.rs", + "src/**/*", + "Cargo.toml", + "Cargo.lock", + "LICENSE*", + "README.md", + "benches/**/*", + "examples/**/*", +] +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Visualize tree-like data on the command-line" +homepage = "https://github.com/rust-cli/termtree" +documentation = "https://docs.rs/termtree" +readme = "README.md" +keywords = [ + "cli", + "tree", + "dag", +] +categories = [ + "command-line-interface", + "visualization", +] +license = "MIT" +repository = "https://github.com/rust-cli/termtree" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = [ + "--cfg", + "docsrs", +] + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +min = 1 +replace = "{{version}}" +search = "Unreleased" + +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "CHANGELOG.md" +replace = "...{{tag_name}}" +search = '\.\.\.HEAD' + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +min = 1 +replace = "{{date}}" +search = "ReleaseDate" + +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "CHANGELOG.md" +replace = """ + +## [Unreleased] - ReleaseDate +""" +search = "" + +[[package.metadata.release.pre-release-replacements]] +exactly = 1 +file = "CHANGELOG.md" +replace = """ + +[Unreleased]: https://github.com/rust-cli/termtree/compare/{{tag_name}}...HEAD""" +search = "" + +[lib] +name = "termtree" +path = "src/lib.rs" + +[[example]] +name = "pretty-test" +path = "examples/pretty-test.rs" + +[[example]] +name = "tree" +path = "examples/tree.rs" + +[dependencies] + +[dev-dependencies.snapbox] +version = "0.6.10" + +[lints.clippy] +bool_assert_comparison = "allow" +branches_sharing_code = "allow" +checked_conversions = "warn" +collapsible_else_if = "allow" +create_dir = "warn" +dbg_macro = "warn" +debug_assert_with_mut_call = "warn" +doc_markdown = "warn" +empty_enum = "warn" +enum_glob_use = "warn" +expl_impl_clone_on_copy = "warn" +explicit_deref_methods = "warn" +explicit_into_iter_loop = "warn" +fallible_impl_from = "warn" +filter_map_next = "warn" +flat_map_option = "warn" +float_cmp_const = "warn" +fn_params_excessive_bools = "warn" +from_iter_instead_of_collect = "warn" +if_same_then_else = "allow" +implicit_clone = "warn" +imprecise_flops = "warn" +inconsistent_struct_constructor = "warn" +inefficient_to_string = "warn" +infinite_loop = "warn" +invalid_upcast_comparisons = "warn" +items_after_statements = "warn" +large_digit_groups = "warn" +large_stack_arrays = "warn" +large_types_passed_by_value = "warn" +let_and_return = "allow" +linkedlist = "warn" +lossy_float_literal = "warn" +macro_use_imports = "warn" +match_wildcard_for_single_variants = "warn" +mem_forget = "warn" +mutex_integer = "warn" +needless_continue = "warn" +needless_for_each = "warn" +negative_feature_names = "warn" +path_buf_push_overwrite = "warn" +ptr_as_ptr = "warn" +rc_mutex = "warn" +redundant_feature_names = "warn" +ref_option_ref = "warn" +rest_pat_in_fully_bound_structs = "warn" +same_functions_in_if_condition = "warn" +self_named_module_files = "warn" +semicolon_if_nothing_returned = "warn" +single_match_else = "warn" +str_to_string = "warn" +string_add = "warn" +string_add_assign = "warn" +string_lit_as_bytes = "warn" +string_to_string = "warn" +todo = "warn" +trait_duplication_in_bounds = "warn" +verbose_file_reads = "warn" +wildcard_imports = "warn" +zero_sized_map_values = "warn" + +[lints.rust] +rust_2018_idioms = "warn" +unreachable_pub = "warn" +unsafe_op_in_unsafe_fn = "warn" +unused_lifetimes = "warn" +unused_macro_rules = "warn" +unused_qualifications = "warn" diff --git a/tools/vendor/termtree/Cargo.toml.orig b/tools/vendor/termtree/Cargo.toml.orig new file mode 100644 index 0000000000..7a54b6f8b5 --- /dev/null +++ b/tools/vendor/termtree/Cargo.toml.orig @@ -0,0 +1,122 @@ +[workspace] +resolver = "2" + +[workspace.package] +license = "MIT" +edition = "2021" +rust-version = "1.74" # MSRV +include = [ + "build.rs", + "src/**/*", + "Cargo.toml", + "Cargo.lock", + "LICENSE*", + "README.md", + "benches/**/*", + "examples/**/*" +] + +[workspace.lints.rust] +rust_2018_idioms = "warn" +unreachable_pub = "warn" +unsafe_op_in_unsafe_fn = "warn" +unused_lifetimes = "warn" +unused_macro_rules = "warn" +unused_qualifications = "warn" + +[workspace.lints.clippy] +bool_assert_comparison = "allow" +branches_sharing_code = "allow" +checked_conversions = "warn" +collapsible_else_if = "allow" +create_dir = "warn" +dbg_macro = "warn" +debug_assert_with_mut_call = "warn" +doc_markdown = "warn" +empty_enum = "warn" +enum_glob_use = "warn" +expl_impl_clone_on_copy = "warn" +explicit_deref_methods = "warn" +explicit_into_iter_loop = "warn" +fallible_impl_from = "warn" +filter_map_next = "warn" +flat_map_option = "warn" +float_cmp_const = "warn" +fn_params_excessive_bools = "warn" +from_iter_instead_of_collect = "warn" +if_same_then_else = "allow" +implicit_clone = "warn" +imprecise_flops = "warn" +inconsistent_struct_constructor = "warn" +inefficient_to_string = "warn" +infinite_loop = "warn" +invalid_upcast_comparisons = "warn" +items_after_statements = "warn" +large_digit_groups = "warn" +large_stack_arrays = "warn" +large_types_passed_by_value = "warn" +let_and_return = "allow" # sometimes good to name what you are returning +linkedlist = "warn" +lossy_float_literal = "warn" +macro_use_imports = "warn" +match_wildcard_for_single_variants = "warn" +mem_forget = "warn" +mutex_integer = "warn" +needless_continue = "warn" +needless_for_each = "warn" +negative_feature_names = "warn" +path_buf_push_overwrite = "warn" +ptr_as_ptr = "warn" +rc_mutex = "warn" +redundant_feature_names = "warn" +ref_option_ref = "warn" +rest_pat_in_fully_bound_structs = "warn" +same_functions_in_if_condition = "warn" +self_named_module_files = "warn" +semicolon_if_nothing_returned = "warn" +single_match_else = "warn" +str_to_string = "warn" +string_add = "warn" +string_add_assign = "warn" +string_lit_as_bytes = "warn" +string_to_string = "warn" +todo = "warn" +trait_duplication_in_bounds = "warn" +verbose_file_reads = "warn" +wildcard_imports = "warn" +zero_sized_map_values = "warn" + +[package] +name = "termtree" +version = "0.5.1" +description = "Visualize tree-like data on the command-line" +documentation = "https://docs.rs/termtree" +homepage = "https://github.com/rust-cli/termtree" +repository = "https://github.com/rust-cli/termtree" +categories = ["command-line-interface", "visualization"] +keywords = ["cli", "tree", "dag"] +license.workspace = true +edition.workspace = true +rust-version.workspace = true +include.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[package.metadata.release] +pre-release-replacements = [ + {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, + {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, + {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, + {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, + {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/termtree/compare/{{tag_name}}...HEAD", exactly=1}, +] + +[dependencies] + +[dev-dependencies] +snapbox = "0.6.10" + +[lints] +workspace = true diff --git a/tools/vendor/termtree/LICENSE-MIT b/tools/vendor/termtree/LICENSE-MIT new file mode 100644 index 0000000000..a2d01088b6 --- /dev/null +++ b/tools/vendor/termtree/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) Individual contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/tools/vendor/termtree/README.md b/tools/vendor/termtree/README.md new file mode 100644 index 0000000000..71d4594ee4 --- /dev/null +++ b/tools/vendor/termtree/README.md @@ -0,0 +1,51 @@ +# termtree [![Main](https://github.com/rust-cli/termtree/actions/workflows/main.yml/badge.svg)](https://github.com/rust-cli/termtree/actions/workflows/main.yml) + +> Visualize tree-like data on the command-line + +[API documentation](https://docs.rs/termtree) + +## Example + +An example program is provided under the "examples" directory to mimic the `tree(1)` +linux program + +```bash +$ cargo run --example tree target + Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs + Running `target/debug/examples/tree target` +target +└── debug + ├── .cargo-lock + ├── .fingerprint + | └── termtree-21a5bdbd42e0b6da + | ├── dep-example-tree + | ├── dep-lib-termtree + | ├── example-tree + | ├── example-tree.json + | ├── lib-termtree + | └── lib-termtree.json + ├── build + ├── deps + | └── libtermtree.rlib + ├── examples + | ├── tree + | └── tree.dSYM + | └── Contents + | ├── Info.plist + | └── Resources + | └── DWARF + | └── tree + ├── libtermtree.rlib + └── native +``` + +## Related Crates + +- [`treeline`](https://crates.io/crates/treeline): termtree was forked from this. +- [`tree_decorator`](https://crates.io/crates/tree_decorator) +- [`xtree`](https://crates.io/crates/xtree) +- [`ptree`](https://crates.io/crates/ptree) + +## License + +Licensed under MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) diff --git a/tools/vendor/termtree/examples/pretty-test.rs b/tools/vendor/termtree/examples/pretty-test.rs new file mode 100644 index 0000000000..1635b03d36 --- /dev/null +++ b/tools/vendor/termtree/examples/pretty-test.rs @@ -0,0 +1,100 @@ +//! Make the output of `cargo test` prettier in tree style. +//! To see the complete demo coupling with rust-script, you can refer to +//! + +use std::collections::{btree_map::Entry, BTreeMap}; +use termtree::{GlyphPalette, Tree}; + +fn main() { + let text = " +test a::b::c ... FAILED +test a::d ... ok +test works ... ok +"; + assert_eq!( + pretty_test(text.trim().lines()).unwrap().to_string(), + "\ +test +├── a +│ ├── b +│ │ └─ ❌ c +│ └─ ✅ d +└─ ✅ works\n" + ); +} + +#[derive(Debug)] +enum Node<'s> { + Path(BTreeMap<&'s str, Node<'s>>), + Status(&'s str), +} + +fn pretty_test<'s>(lines: impl Iterator) -> Option> { + let mut path = BTreeMap::new(); + for line in lines { + let mut iter = line.splitn(3, ' '); + let mut split = iter.nth(1)?.split("::"); + let next = split.next(); + let status = iter.next()?; + make_node(split, status, &mut path, next); + } + let mut tree = Tree::new("test"); + for (root, child) in path { + make_tree(root, &child, &mut tree); + } + Some(tree) +} + +// Add paths to Node +fn make_node<'s>( + mut split: impl Iterator, + status: &'s str, + path: &mut BTreeMap<&'s str, Node<'s>>, + key: Option<&'s str>, +) { + let Some(key) = key else { return }; + let next = split.next(); + match path.entry(key) { + Entry::Vacant(empty) => { + if next.is_some() { + let mut btree = BTreeMap::new(); + make_node(split, status, &mut btree, next); + empty.insert(Node::Path(btree)); + } else { + empty.insert(Node::Status(status)); + } + } + Entry::Occupied(mut btree) => { + if let Node::Path(btree) = btree.get_mut() { + make_node(split, status, btree, next); + } + } + } +} + +// Add Node to Tree +fn make_tree<'s>(root: &'s str, node: &Node<'s>, parent: &mut Tree<&'s str>) { + match node { + Node::Path(btree) => { + let mut t = Tree::new(root); + for (path, child) in btree { + make_tree(path, child, &mut t); + } + parent.push(t); + } + Node::Status(s) => { + parent.push(Tree::new(root).with_glyphs(set_status(s))); + } + } +} + +// Display with a status icon +fn set_status(status: &str) -> GlyphPalette { + let mut glyph = GlyphPalette::new(); + glyph.item_indent = if status.ends_with("ok") { + "─ ✅ " + } else { + "─ ❌ " + }; + glyph +} diff --git a/tools/vendor/termtree/examples/tree.rs b/tools/vendor/termtree/examples/tree.rs new file mode 100644 index 0000000000..0a9bca385c --- /dev/null +++ b/tools/vendor/termtree/examples/tree.rs @@ -0,0 +1,32 @@ +use termtree::Tree; + +use std::path::Path; +use std::{env, fs, io}; + +fn label>(p: P) -> String { + p.as_ref().file_name().unwrap().to_str().unwrap().to_owned() +} + +fn tree>(p: P) -> io::Result> { + let result = fs::read_dir(&p)?.filter_map(|e| e.ok()).fold( + Tree::new(label(p.as_ref().canonicalize()?)), + |mut root, entry| { + let dir = entry.metadata().unwrap(); + if dir.is_dir() { + root.push(tree(entry.path()).unwrap()); + } else { + root.push(Tree::new(label(entry.path()))); + } + root + }, + ); + Ok(result) +} + +fn main() { + let dir = env::args().nth(1).unwrap_or_else(|| String::from(".")); + match tree(dir) { + Ok(tree) => println!("{}", tree), + Err(err) => println!("error: {}", err), + } +} diff --git a/tools/vendor/termtree/src/lib.rs b/tools/vendor/termtree/src/lib.rs new file mode 100644 index 0000000000..c232582559 --- /dev/null +++ b/tools/vendor/termtree/src/lib.rs @@ -0,0 +1,230 @@ +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![warn(clippy::print_stderr)] +#![warn(clippy::print_stdout)] + +#[cfg(test)] +mod tests; + +use std::collections::VecDeque; +use std::fmt::{self, Display}; +use std::rc::Rc; + +/// a simple recursive type which is able to render its +/// components in a tree-like format +#[derive(Debug, Clone)] +pub struct Tree { + pub root: D, + pub leaves: Vec>, + multiline: bool, + glyphs: Option, +} + +impl Tree { + pub fn new(root: D) -> Self { + Tree { + root, + leaves: Vec::new(), + multiline: false, + glyphs: None, + } + } + + pub fn with_leaves(mut self, leaves: impl IntoIterator>>) -> Self { + self.leaves = leaves.into_iter().map(Into::into).collect(); + self + } + + /// Ensure all lines for `root` are indented + pub fn with_multiline(mut self, yes: bool) -> Self { + self.multiline = yes; + self + } + + /// Customize the rendering of this node + pub fn with_glyphs(mut self, glyphs: GlyphPalette) -> Self { + self.glyphs = Some(glyphs); + self + } +} + +impl Tree { + /// Ensure all lines for `root` are indented + pub fn set_multiline(&mut self, yes: bool) -> &mut Self { + self.multiline = yes; + self + } + + /// Customize the rendering of this node + pub fn set_glyphs(&mut self, glyphs: GlyphPalette) -> &mut Self { + self.glyphs = Some(glyphs); + self + } +} + +impl Tree { + pub fn push(&mut self, leaf: impl Into>) -> &mut Self { + self.leaves.push(leaf.into()); + self + } +} + +impl From for Tree { + fn from(inner: D) -> Self { + Self::new(inner) + } +} + +impl Extend for Tree { + fn extend>(&mut self, iter: T) { + self.leaves.extend(iter.into_iter().map(Into::into)); + } +} + +impl Extend> for Tree { + fn extend>>(&mut self, iter: T) { + self.leaves.extend(iter); + } +} + +impl Display for Tree { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.root.fmt(f)?; // Pass along `f.alternate()` + writeln!(f)?; + let mut queue = DisplauQueue::new(); + let no_space = Rc::new(Vec::new()); + let default_glyphs = GlyphPalette::new(); + let glyphs = self.glyphs.as_ref().unwrap_or(&default_glyphs); + enqueue_leaves(&mut queue, self, glyphs, no_space); + while let Some((last, leaf, glyphs, spaces)) = queue.pop_front() { + let mut prefix = ( + if last { + glyphs.last_item + } else { + glyphs.middle_item + }, + glyphs.item_indent, + ); + + if leaf.multiline { + let rest_prefix = ( + if last { + glyphs.last_skip + } else { + glyphs.middle_skip + }, + glyphs.skip_indent, + ); + debug_assert_eq!(prefix.0.chars().count(), rest_prefix.0.chars().count()); + debug_assert_eq!(prefix.1.chars().count(), rest_prefix.1.chars().count()); + + let root = if f.alternate() { + format!("{:#}", leaf.root) + } else { + format!("{:}", leaf.root) + }; + for line in root.lines() { + // print single line + for s in spaces.as_slice() { + s.skip.fmt(f)?; + s.indent.fmt(f)?; + } + prefix.0.fmt(f)?; + prefix.1.fmt(f)?; + line.fmt(f)?; + writeln!(f)?; + prefix = rest_prefix; + } + } else { + // print single line + for s in spaces.as_slice() { + s.skip.fmt(f)?; + s.indent.fmt(f)?; + } + prefix.0.fmt(f)?; + prefix.1.fmt(f)?; + leaf.root.fmt(f)?; // Pass along `f.alternate()` + writeln!(f)?; + } + + // recurse + if !leaf.leaves.is_empty() { + let s: &Vec = &spaces; + let mut child_spaces = s.clone(); + child_spaces.push(if last { + glyphs.last_space() + } else { + glyphs.middle_space() + }); + let child_spaces = Rc::new(child_spaces); + enqueue_leaves(&mut queue, leaf, glyphs, child_spaces); + } + } + Ok(()) + } +} + +type DisplauQueue<'t, D> = VecDeque<(bool, &'t Tree, &'t GlyphPalette, Rc>)>; + +fn enqueue_leaves<'t, D: Display>( + queue: &mut DisplauQueue<'t, D>, + parent: &'t Tree, + parent_glyphs: &'t GlyphPalette, + spaces: Rc>, +) { + for (i, leaf) in parent.leaves.iter().rev().enumerate() { + let last = i == 0; + let glyphs = leaf.glyphs.as_ref().unwrap_or(parent_glyphs); + queue.push_front((last, leaf, glyphs, spaces.clone())); + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +struct SpacePalette { + skip: &'static str, + indent: &'static str, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct GlyphPalette { + pub middle_item: &'static str, + pub last_item: &'static str, + pub item_indent: &'static str, + + pub middle_skip: &'static str, + pub last_skip: &'static str, + pub skip_indent: &'static str, +} + +impl GlyphPalette { + pub const fn new() -> Self { + Self { + middle_item: "├", + last_item: "└", + item_indent: "── ", + + middle_skip: "│", + last_skip: " ", + skip_indent: " ", + } + } + + fn middle_space(&self) -> SpacePalette { + SpacePalette { + skip: self.middle_skip, + indent: self.skip_indent, + } + } + + fn last_space(&self) -> SpacePalette { + SpacePalette { + skip: self.last_skip, + indent: self.skip_indent, + } + } +} + +impl Default for GlyphPalette { + fn default() -> Self { + Self::new() + } +} diff --git a/tools/vendor/termtree/src/tests.rs b/tools/vendor/termtree/src/tests.rs new file mode 100644 index 0000000000..ff3e7f0c44 --- /dev/null +++ b/tools/vendor/termtree/src/tests.rs @@ -0,0 +1,123 @@ +use snapbox::assert_data_eq; +use snapbox::str; + +use super::*; + +#[test] +fn render_tree_root() { + let tree = Tree::new("foo"); + assert_data_eq!( + format!("{}", tree), + str![[r#" +foo + +"#]] + ); +} + +#[test] +fn render_tree_with_leaves() { + let tree = Tree::new("foo").with_leaves([Tree::new("bar").with_leaves(["baz"])]); + assert_data_eq!( + format!("{}", tree), + str![[r#" +foo +└── bar + └── baz + +"#]] + ); +} + +#[test] +fn render_tree_with_multiple_leaves() { + let tree = Tree::new("foo").with_leaves(["bar", "baz"]); + assert_data_eq!( + format!("{}", tree), + str![[r#" +foo +├── bar +└── baz + +"#]] + ); +} + +#[test] +fn render_tree_with_multiline_leaf() { + let tree = Tree::new("foo").with_leaves([ + Tree::new("hello\nworld").with_multiline(true), + Tree::new("goodbye\nworld").with_multiline(true), + ]); + assert_data_eq!( + format!("{}", tree), + str![[r#" +foo +├── hello +│ world +└── goodbye + world + +"#]] + ); +} + +#[test] +fn render_custom_glyphs() { + let root = GlyphPalette { + middle_item: "[mid ]", + last_item: "[last ]", + item_indent: "[indent ]", + + middle_skip: "[mskip]", + last_skip: "[lskip]", + skip_indent: "[iskip ]", + }; + let middle = GlyphPalette { + middle_item: "(mid )", + last_item: "(last )", + item_indent: "(indent )", + + middle_skip: "(mskip)", + last_skip: "(lskip)", + skip_indent: "(iskip )", + }; + + let tree = Tree::new("node 1").with_glyphs(root).with_leaves([ + Tree::new("node 1.1"), + Tree::new("node 1.2"), + Tree::new("node 1.3").with_leaves([ + Tree::new("node 1.3.1").with_glyphs(middle), + Tree::new("node 1.3.2").with_glyphs(middle), + Tree::new("node 1.3.3") + .with_glyphs(middle) + .with_leaves(["node 1.3.3.1", "node 1.3.3.2"]), + ]), + Tree::new("node 1.4").with_leaves([ + Tree::new("node 1.4.1"), + Tree::new("node 1.4.2"), + Tree::new("node 1.4.3").with_leaves(["node 1.4.3.1", "node 1.4.3.2"]), + ]), + ]); + assert_data_eq!( + format!("{}", tree), + str![[r#" +node 1 +[mid ][indent ]node 1.1 +[mid ][indent ]node 1.2 +[mid ][indent ]node 1.3 +[mskip][iskip ](mid )(indent )node 1.3.1 +[mskip][iskip ](mid )(indent )node 1.3.2 +[mskip][iskip ](last )(indent )node 1.3.3 +[mskip][iskip ](lskip)(iskip )(mid )(indent )node 1.3.3.1 +[mskip][iskip ](lskip)(iskip )(last )(indent )node 1.3.3.2 +[last ][indent ]node 1.4 +[lskip][iskip ][mid ][indent ]node 1.4.1 +[lskip][iskip ][mid ][indent ]node 1.4.2 +[lskip][iskip ][last ][indent ]node 1.4.3 +[lskip][iskip ][lskip][iskip ][mid ][indent ]node 1.4.3.1 +[lskip][iskip ][lskip][iskip ][last ][indent ]node 1.4.3.2 + +"#]] + ); +} diff --git a/tools/vendor/wait-timeout/.cargo-checksum.json b/tools/vendor/wait-timeout/.cargo-checksum.json new file mode 100644 index 0000000000..018da3d497 --- /dev/null +++ b/tools/vendor/wait-timeout/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"1a79bdc9d66fdf3746b320a4347416dc51d173420dd3778394bfbbdfd14f2428",".github/dependabot.yml":"d04c9b0253b2bbae886b59a11399ea260397b460cd9f5712d692d1c85f8ec090",".github/workflows/main.yml":"787f0faf050778869f3ce35aa86cdd4f5676e8d0aa26c7f989c6366b34132ac3","Cargo.lock":"1d7ce34706c4472da4885967170a161a56ba502717f09f6d4227f87c5a4eb827","Cargo.toml":"b7125bf2e61f202c7f3074892ab58b5bbc66893026fb0dea17dadae1ad84f0d5","Cargo.toml.orig":"fe69bfa87528d1eb6c2989b511b5ae2fa1a313883937105bdab5117f1e45eac2","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"98929e53f432d525819f988542fa66595a6db202e7e6001190aecc2a66786155","src/bin/exit.rs":"6361e1d6b0aa6b3c6d01dad168d41ee29f6cb616711ac48a1fef99f87fd989ef","src/bin/reader.rs":"a14469a7e8a436ab9bccb5048b8458ae143efbe16028b81bc304a265eddce8d7","src/bin/sleep.rs":"da1762ba3729657315f2062001a001dd530e14f074917d42a1065019444b0ddd","src/lib.rs":"c8eea673f3de91526912c957dc3765984b24acf662bfc0bba27aaf4a91132728","src/unix.rs":"707608e3079cd0287357c06f0f1b414816c96f8a4fcbeb521f726c86429bc4cd","src/windows.rs":"8e403f116d1879a7322255e20d3be0383af49cc7c2248e3e5e693d11ac84cb5c","tests/smoke.rs":"5d6c0e2951232bc855ea690eec65e3acd19496611c1145b1b7d60d03111449f5"},"package":"09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"} \ No newline at end of file diff --git a/tools/vendor/wait-timeout/.cargo_vcs_info.json b/tools/vendor/wait-timeout/.cargo_vcs_info.json new file mode 100644 index 0000000000..a59d97e57d --- /dev/null +++ b/tools/vendor/wait-timeout/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "bda62e3123f7d30362984673b6d8c3330aa4a7d6" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/tools/vendor/wait-timeout/.github/dependabot.yml b/tools/vendor/wait-timeout/.github/dependabot.yml new file mode 100644 index 0000000000..7377d37597 --- /dev/null +++ b/tools/vendor/wait-timeout/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: +- package-ecosystem: cargo + directory: "/" + schedule: + interval: daily + time: "08:00" + open-pull-requests-limit: 10 diff --git a/tools/vendor/wait-timeout/.github/workflows/main.yml b/tools/vendor/wait-timeout/.github/workflows/main.yml new file mode 100644 index 0000000000..4d15f3b3b5 --- /dev/null +++ b/tools/vendor/wait-timeout/.github/workflows/main.yml @@ -0,0 +1,33 @@ +name: CI +on: [push, pull_request] + +jobs: + test: + name: Test + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: ubuntu-latest + rust: stable + - os: ubuntu-latest + rust: beta + - os: ubuntu-latest + rust: nightly + - os: windows-latest + rust: stable + steps: + - uses: actions/checkout@v2 + - name: Install Rust + run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} + shell: bash + - run: cargo test + + rustfmt: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: Install Rust + run: rustup update stable && rustup default stable && rustup component add rustfmt + - run: cargo fmt -- --check diff --git a/tools/vendor/wait-timeout/Cargo.lock b/tools/vendor/wait-timeout/Cargo.lock new file mode 100644 index 0000000000..1a909af8c2 --- /dev/null +++ b/tools/vendor/wait-timeout/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "wait-timeout" +version = "0.2.1" +dependencies = [ + "libc", +] diff --git a/tools/vendor/wait-timeout/Cargo.toml b/tools/vendor/wait-timeout/Cargo.toml new file mode 100644 index 0000000000..a50e87cdd7 --- /dev/null +++ b/tools/vendor/wait-timeout/Cargo.toml @@ -0,0 +1,60 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +name = "wait-timeout" +version = "0.2.1" +authors = ["Alex Crichton "] +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = """ +A crate to wait on a child process with a timeout specified across Unix and +Windows platforms. +""" +homepage = "https://github.com/alexcrichton/wait-timeout" +documentation = "https://docs.rs/wait-timeout" +readme = "README.md" +categories = ["os"] +license = "MIT/Apache-2.0" +repository = "https://github.com/alexcrichton/wait-timeout" + +[lib] +name = "wait_timeout" +path = "src/lib.rs" + +[[bin]] +name = "exit" +path = "src/bin/exit.rs" + +[[bin]] +name = "reader" +path = "src/bin/reader.rs" + +[[bin]] +name = "sleep" +path = "src/bin/sleep.rs" + +[[test]] +name = "smoke" +path = "tests/smoke.rs" + +[target."cfg(unix)".dependencies.libc] +version = "0.2.56" + +[badges.appveyor] +repository = "alexcrichton/wait-timeout" + +[badges.travis-ci] +repository = "alexcrichton/wait-timeout" diff --git a/tools/vendor/wait-timeout/Cargo.toml.orig b/tools/vendor/wait-timeout/Cargo.toml.orig new file mode 100644 index 0000000000..483cac1a2b --- /dev/null +++ b/tools/vendor/wait-timeout/Cargo.toml.orig @@ -0,0 +1,21 @@ +[package] +name = "wait-timeout" +version = "0.2.1" +authors = ["Alex Crichton "] +license = "MIT/Apache-2.0" +readme = "README.md" +repository = "https://github.com/alexcrichton/wait-timeout" +homepage = "https://github.com/alexcrichton/wait-timeout" +documentation = "https://docs.rs/wait-timeout" +description = """ +A crate to wait on a child process with a timeout specified across Unix and +Windows platforms. +""" +categories = ["os"] + +[badges] +travis-ci = { repository = "alexcrichton/wait-timeout" } +appveyor = { repository = "alexcrichton/wait-timeout" } + +[target.'cfg(unix)'.dependencies] +libc = "0.2.56" diff --git a/tools/vendor/wait-timeout/LICENSE-APACHE b/tools/vendor/wait-timeout/LICENSE-APACHE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/tools/vendor/wait-timeout/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/tools/vendor/wait-timeout/LICENSE-MIT b/tools/vendor/wait-timeout/LICENSE-MIT new file mode 100644 index 0000000000..39e0ed6602 --- /dev/null +++ b/tools/vendor/wait-timeout/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 Alex Crichton + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/tools/vendor/wait-timeout/README.md b/tools/vendor/wait-timeout/README.md new file mode 100644 index 0000000000..0682a1f4fb --- /dev/null +++ b/tools/vendor/wait-timeout/README.md @@ -0,0 +1,13 @@ +# wait-timeout + +[![Build Status](https://github.com/alexcrichton/wait-timeout/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/alexcrichton/wait-timeout/actions/workflows/main.yml) + +[Documentation](https://docs.rs/wait-timeout) + +Rust crate for waiting on a `Child` process with a timeout specified. + +```toml +# Cargo.toml +[dependencies] +wait-timeout = "0.1.5" +``` diff --git a/tools/vendor/wait-timeout/src/bin/exit.rs b/tools/vendor/wait-timeout/src/bin/exit.rs new file mode 100644 index 0000000000..f33c49f34e --- /dev/null +++ b/tools/vendor/wait-timeout/src/bin/exit.rs @@ -0,0 +1,4 @@ +fn main() { + let code = std::env::args().nth(1).unwrap().parse().unwrap(); + std::process::exit(code); +} diff --git a/tools/vendor/wait-timeout/src/bin/reader.rs b/tools/vendor/wait-timeout/src/bin/reader.rs new file mode 100644 index 0000000000..65fae8bca4 --- /dev/null +++ b/tools/vendor/wait-timeout/src/bin/reader.rs @@ -0,0 +1,7 @@ +use std::io::{stdin, Read}; + +#[allow(unused_must_use)] +fn main() { + let mut buffer: [u8; 32] = Default::default(); + stdin().read(&mut buffer); +} diff --git a/tools/vendor/wait-timeout/src/bin/sleep.rs b/tools/vendor/wait-timeout/src/bin/sleep.rs new file mode 100644 index 0000000000..48dabdc022 --- /dev/null +++ b/tools/vendor/wait-timeout/src/bin/sleep.rs @@ -0,0 +1,4 @@ +fn main() { + let amt = std::env::args().nth(1).unwrap().parse().unwrap(); + std::thread::sleep(std::time::Duration::from_millis(amt)); +} diff --git a/tools/vendor/wait-timeout/src/lib.rs b/tools/vendor/wait-timeout/src/lib.rs new file mode 100644 index 0000000000..fb9c9411da --- /dev/null +++ b/tools/vendor/wait-timeout/src/lib.rs @@ -0,0 +1,70 @@ +//! A crate to wait on a child process with a particular timeout. +//! +//! This crate is an implementation for Unix and Windows of the ability to wait +//! on a child process with a timeout specified. On Windows the implementation +//! is fairly trivial as it's just a call to `WaitForSingleObject` with a +//! timeout argument, but on Unix the implementation is much more involved. The +//! current implementation registers a `SIGCHLD` handler and initializes some +//! global state. This handler also works within multi-threaded environments. +//! If your application is otherwise handling `SIGCHLD` then bugs may arise. +//! +//! # Example +//! +//! ```no_run +//! use std::process::Command; +//! use wait_timeout::ChildExt; +//! use std::time::Duration; +//! +//! let mut child = Command::new("foo").spawn().unwrap(); +//! +//! let one_sec = Duration::from_secs(1); +//! let status_code = match child.wait_timeout(one_sec).unwrap() { +//! Some(status) => status.code(), +//! None => { +//! // child hasn't exited yet +//! child.kill().unwrap(); +//! child.wait().unwrap().code() +//! } +//! }; +//! ``` + +#![deny(missing_docs, warnings)] +#![doc(html_root_url = "https://docs.rs/wait-timeout/0.1")] + +#[cfg(unix)] +extern crate libc; + +use std::io; +use std::process::{Child, ExitStatus}; +use std::time::Duration; + +#[cfg(unix)] +#[path = "unix.rs"] +mod imp; +#[cfg(windows)] +#[path = "windows.rs"] +mod imp; + +/// Extension methods for the standard `std::process::Child` type. +pub trait ChildExt { + /// Deprecated, use `wait_timeout` instead. + #[doc(hidden)] + fn wait_timeout_ms(&mut self, ms: u32) -> io::Result> { + self.wait_timeout(Duration::from_millis(ms as u64)) + } + + /// Wait for this child to exit, timing out after the duration `dur` has + /// elapsed. + /// + /// If `Ok(None)` is returned then the timeout period elapsed without the + /// child exiting, and if `Ok(Some(..))` is returned then the child exited + /// with the specified exit code. + fn wait_timeout(&mut self, dur: Duration) -> io::Result>; +} + +impl ChildExt for Child { + fn wait_timeout(&mut self, dur: Duration) -> io::Result> { + drop(self.stdin.take()); + imp::wait_timeout(self, dur) + } +} diff --git a/tools/vendor/wait-timeout/src/unix.rs b/tools/vendor/wait-timeout/src/unix.rs new file mode 100644 index 0000000000..ba3613937d --- /dev/null +++ b/tools/vendor/wait-timeout/src/unix.rs @@ -0,0 +1,271 @@ +//! Unix implementation of waiting for children with timeouts +//! +//! On unix, wait() and its friends have no timeout parameters, so there is +//! no way to time out a thread in wait(). From some googling and some +//! thinking, it appears that there are a few ways to handle timeouts in +//! wait(), but the only real reasonable one for a multi-threaded program is +//! to listen for SIGCHLD. +//! +//! With this in mind, the waiting mechanism with a timeout only uses +//! waitpid() with WNOHANG, but otherwise all the necessary blocking is done by +//! waiting for a SIGCHLD to arrive (and that blocking has a timeout). Note, +//! however, that waitpid() is still used to actually reap the child. +//! +//! Signal handling is super tricky in general, and this is no exception. Due +//! to the async nature of SIGCHLD, we use the self-pipe trick to transmit +//! data out of the signal handler to the rest of the application. + +#![allow(bad_style)] + +use std::cmp; +use std::collections::HashMap; +use std::io::{self, Read, Write}; +use std::mem; +use std::os::unix::net::UnixStream; +use std::os::unix::prelude::*; +use std::process::{Child, ExitStatus}; +use std::sync::{Mutex, Once}; +use std::time::{Duration, Instant}; + +use libc::{self, c_int}; + +static INIT: Once = Once::new(); +static mut STATE: *mut State = 0 as *mut _; + +struct State { + prev: libc::sigaction, + write: UnixStream, + read: UnixStream, + map: Mutex, +} + +type StateMap = HashMap<*mut Child, (UnixStream, Option)>; + +pub fn wait_timeout(child: &mut Child, dur: Duration) -> io::Result> { + INIT.call_once(State::init); + unsafe { (*STATE).wait_timeout(child, dur) } +} + +impl State { + #[allow(unused_assignments)] + fn init() { + unsafe { + // Create our "self pipe" and then set both ends to nonblocking + // mode. + let (read, write) = UnixStream::pair().unwrap(); + read.set_nonblocking(true).unwrap(); + write.set_nonblocking(true).unwrap(); + + let state = Box::new(State { + prev: mem::zeroed(), + write: write, + read: read, + map: Mutex::new(HashMap::new()), + }); + + // Register our sigchld handler + let mut new: libc::sigaction = mem::zeroed(); + new.sa_sigaction = sigchld_handler as usize; + new.sa_flags = libc::SA_NOCLDSTOP | libc::SA_RESTART | libc::SA_SIGINFO; + + STATE = Box::into_raw(state); + + assert_eq!(libc::sigaction(libc::SIGCHLD, &new, &mut (*STATE).prev), 0); + } + } + + fn wait_timeout(&self, child: &mut Child, dur: Duration) -> io::Result> { + // First up, prep our notification pipe which will tell us when our + // child has been reaped (other threads may signal this pipe). + let (read, write) = UnixStream::pair()?; + read.set_nonblocking(true)?; + write.set_nonblocking(true)?; + + // Next, take a lock on the map of children currently waiting. Right + // after this, **before** we add ourselves to the map, we check to see + // if our child has actually already exited via a `try_wait`. If the + // child has exited then we return immediately as we'll never otherwise + // receive a SIGCHLD notification. + // + // If the wait reports the child is still running, however, we add + // ourselves to the map and then block in `select` waiting for something + // to happen. + let mut map = self.map.lock().unwrap(); + if let Some(status) = child.try_wait()? { + return Ok(Some(status)); + } + assert!(map.insert(child, (write, None)).is_none()); + drop(map); + + // Make sure that no matter what when we exit our pointer is removed + // from the map. + struct Remove<'a> { + state: &'a State, + child: &'a mut Child, + } + impl<'a> Drop for Remove<'a> { + fn drop(&mut self) { + let mut map = self.state.map.lock().unwrap(); + drop(map.remove(&(self.child as *mut Child))); + } + } + let remove = Remove { state: self, child }; + + // Alright, we're guaranteed that we'll eventually get a SIGCHLD due + // to our `try_wait` failing, and we're also guaranteed that we'll + // get notified about this because we're in the map. Next up wait + // for an event. + // + // Note that this happens in a loop for two reasons; we could + // receive EINTR or we could pick up a SIGCHLD for other threads but not + // actually be ready oureslves. + let start = Instant::now(); + let mut fds = [ + libc::pollfd { + fd: self.read.as_raw_fd(), + events: libc::POLLIN, + + revents: 0, + }, + libc::pollfd { + fd: read.as_raw_fd(), + events: libc::POLLIN, + revents: 0, + }, + ]; + loop { + let elapsed = start.elapsed(); + if elapsed >= dur { + break; + } + let timeout = dur - elapsed; + let timeout = timeout + .as_secs() + .checked_mul(1_000) + .and_then(|amt| amt.checked_add(timeout.subsec_nanos() as u64 / 1_000_000)) + .unwrap_or(u64::max_value()); + let timeout = cmp::min(::max_value() as u64, timeout) as c_int; + let r = unsafe { libc::poll(fds.as_mut_ptr(), 2, timeout) }; + let timeout = match r { + 0 => true, + n if n > 0 => false, + n => { + let err = io::Error::last_os_error(); + if err.kind() == io::ErrorKind::Interrupted { + continue; + } else { + panic!("error in select = {}: {}", n, err) + } + } + }; + + // Now that something has happened, we need to process what actually + // happened. There's are three reasons we could have woken up: + // + // 1. The file descriptor in our SIGCHLD handler was written to. + // This means that a SIGCHLD was received and we need to poll the + // entire list of waiting processes to figure out which ones + // actually exited. + // 2. Our file descriptor was written to. This means that another + // thread reaped our child and listed the exit status in the + // local map. + // 3. We timed out. This means we need to remove ourselves from the + // map and simply carry on. + // + // In the case that a SIGCHLD signal was received, we do that + // processing and keep going. If our fd was written to or a timeout + // was received then we break out of the loop and return from this + // call. + let mut map = self.map.lock().unwrap(); + if drain(&self.read) { + self.process_sigchlds(&mut map); + } + + if drain(&read) || timeout { + break; + } + } + + let mut map = self.map.lock().unwrap(); + let (_write, ret) = map.remove(&(remove.child as *mut Child)).unwrap(); + drop(map); + Ok(ret) + } + + fn process_sigchlds(&self, map: &mut StateMap) { + for (&k, &mut (ref write, ref mut status)) in map { + // Already reaped, nothing to do here + if status.is_some() { + continue; + } + + *status = unsafe { (*k).try_wait().unwrap() }; + if status.is_some() { + notify(write); + } + } + } +} + +fn drain(mut file: &UnixStream) -> bool { + let mut ret = false; + let mut buf = [0u8; 16]; + loop { + match file.read(&mut buf) { + Ok(0) => return true, // EOF == something happened + Ok(..) => ret = true, // data read, but keep draining + Err(e) => { + if e.kind() == io::ErrorKind::WouldBlock { + return ret; + } else { + panic!("bad read: {}", e) + } + } + } + } +} + +fn notify(mut file: &UnixStream) { + match file.write(&[1]) { + Ok(..) => {} + Err(e) => { + if e.kind() != io::ErrorKind::WouldBlock { + panic!("bad error on write fd: {}", e) + } + } + } +} + +// Signal handler for SIGCHLD signals, must be async-signal-safe! +// +// This function will write to the writing half of the "self pipe" to wake +// up the helper thread if it's waiting. Note that this write must be +// nonblocking because if it blocks and the reader is the thread we +// interrupted, then we'll deadlock. +// +// When writing, if the write returns EWOULDBLOCK then we choose to ignore +// it. At that point we're guaranteed that there's something in the pipe +// which will wake up the other end at some point, so we just allow this +// signal to be coalesced with the pending signals on the pipe. +#[allow(unused_assignments)] +extern "C" fn sigchld_handler(signum: c_int, info: *mut libc::siginfo_t, ptr: *mut libc::c_void) { + type FnSigaction = extern "C" fn(c_int, *mut libc::siginfo_t, *mut libc::c_void); + type FnHandler = extern "C" fn(c_int); + + unsafe { + let state = &*STATE; + notify(&state.write); + + let fnptr = state.prev.sa_sigaction; + if fnptr == 0 { + return; + } + if state.prev.sa_flags & libc::SA_SIGINFO == 0 { + let action = mem::transmute::(fnptr); + action(signum) + } else { + let action = mem::transmute::(fnptr); + action(signum, info, ptr) + } + } +} diff --git a/tools/vendor/wait-timeout/src/windows.rs b/tools/vendor/wait-timeout/src/windows.rs new file mode 100644 index 0000000000..6e607e19ac --- /dev/null +++ b/tools/vendor/wait-timeout/src/windows.rs @@ -0,0 +1,31 @@ +use std::io; +use std::os::windows::prelude::*; +use std::process::{Child, ExitStatus}; +use std::time::Duration; + +type DWORD = u32; +type HANDLE = *mut u8; + +const WAIT_OBJECT_0: DWORD = 0x00000000; +const WAIT_TIMEOUT: DWORD = 258; + +extern "system" { + fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; +} + +pub fn wait_timeout(child: &mut Child, dur: Duration) -> io::Result> { + let ms = dur.as_millis(); + let ms = if ms > (DWORD::max_value() as u128) { + DWORD::max_value() + } else { + ms as DWORD + }; + unsafe { + match WaitForSingleObject(child.as_raw_handle() as *mut _, ms) { + WAIT_OBJECT_0 => {} + WAIT_TIMEOUT => return Ok(None), + _ => return Err(io::Error::last_os_error()), + } + } + child.try_wait() +} diff --git a/tools/vendor/wait-timeout/tests/smoke.rs b/tools/vendor/wait-timeout/tests/smoke.rs new file mode 100644 index 0000000000..a87dc614b1 --- /dev/null +++ b/tools/vendor/wait-timeout/tests/smoke.rs @@ -0,0 +1,105 @@ +extern crate wait_timeout; + +use std::env; +use std::process::{Child, Command, Stdio}; +use std::time::{Duration, Instant}; + +use wait_timeout::ChildExt; + +macro_rules! t { + ($e:expr) => { + match $e { + Ok(e) => e, + Err(e) => panic!("{} failed with {}", stringify!($e), e), + } + }; +} + +fn sleeper(ms: u32) -> Child { + let mut me = env::current_exe().unwrap(); + me.pop(); + if me.ends_with("deps") { + me.pop(); + } + me.push("sleep"); + t!(Command::new(me).arg(ms.to_string()).spawn()) +} + +fn exit(code: u32) -> Child { + let mut me = env::current_exe().unwrap(); + me.pop(); + if me.ends_with("deps") { + me.pop(); + } + me.push("exit"); + t!(Command::new(me).arg(code.to_string()).spawn()) +} + +fn reader() -> Child { + let mut me = env::current_exe().unwrap(); + me.pop(); + if me.ends_with("deps") { + me.pop(); + } + me.push("reader"); + t!(Command::new(me).stdin(Stdio::piped()).spawn()) +} + +#[test] +fn smoke_insta_timeout() { + let mut child = sleeper(1_000); + assert_eq!(t!(child.wait_timeout_ms(0)), None); + + t!(child.kill()); + let status = t!(child.wait()); + assert!(!status.success()); +} + +#[test] +fn smoke_success() { + let start = Instant::now(); + let mut child = sleeper(0); + let status = t!(child.wait_timeout_ms(1_000)).expect("should have succeeded"); + assert!(status.success()); + + assert!(start.elapsed() < Duration::from_millis(500)); +} + +#[test] +fn smoke_timeout() { + let mut child = sleeper(1_000_000); + let start = Instant::now(); + assert_eq!(t!(child.wait_timeout_ms(100)), None); + assert!(start.elapsed() > Duration::from_millis(80)); + + t!(child.kill()); + let status = t!(child.wait()); + assert!(!status.success()); +} + +#[test] +fn smoke_reader() { + let mut child = reader(); + let dur = Duration::from_millis(100); + let status = t!(child.wait_timeout(dur)).unwrap(); + assert!(status.success()); +} + +#[test] +fn exit_codes() { + let mut child = exit(0); + let status = t!(child.wait_timeout_ms(1_000)).unwrap(); + assert_eq!(status.code(), Some(0)); + + let mut child = exit(1); + let status = t!(child.wait_timeout_ms(1_000)).unwrap(); + assert_eq!(status.code(), Some(1)); + + // check STILL_ACTIVE on windows, on unix this ends up just getting + // truncated so don't bother with it. + if cfg!(windows) { + let mut child = exit(259); + let status = t!(child.wait_timeout_ms(1_000)).unwrap(); + assert_eq!(status.code(), Some(259)); + } +} diff --git a/tools/vendor/wasip2/.cargo-checksum.json b/tools/vendor/wasip2/.cargo-checksum.json new file mode 100644 index 0000000000..5f76b460a7 --- /dev/null +++ b/tools/vendor/wasip2/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"6db47a044aaea8461ba63346a96ac4ee93eb065b05d902e91eae7fd7b07549e8","Cargo.lock":"a0bae1d749228589ae9a9a1a22ee6e19abf47c8025ae0e5584773211999811a7","Cargo.toml":"df5f20155009f7a858d67af7755ba85c925b0174559abc8723ec3c97ca6629df","Cargo.toml.orig":"22b4d1984cd3f2df71911afab64e1d20cc4af088a8c7ea88829cf20a4e82418c","README.md":"b768b011583d6c3f7d47c57de28f7dd9ade631b286753435ae1ae9acab6ce557","examples/cli-command-no_std.rs":"56f3f5d08affa4f2211cbdcdf4ec865981502aaa5cdccbffb0b2084736330430","examples/cli-command.rs":"1e84ceae7a33d9c9e9340651b086f155d138eeb9d375c6a1678e3d282f3b668a","examples/hello-world-no_std.rs":"a0b3581ea43e8a1588de8619dee70ddb48a2360e84ef7c9c21b9d6cfbe350dda","examples/hello-world.rs":"634b4999d89675ffad329eea3eb006fad935806895507e0c3e7d770681a9089d","examples/http-proxy-no_std.rs":"9969f8c9747739be697329c563e32c352961655c600eedafb2fac11589216443","examples/http-proxy.rs":"a31e4c31203860ab96da3ca176ca99db7b88ea513ca7a20cc8955a57794367bc","src/command.rs":"35e114d1e65923d46d2fd22eb6b64cb0c37964094450b0e4789413ef1aad240e","src/ext/mod.rs":"4bc79c71442ac6c55dc4f8a0632b6d6805c0c9c74b22b56791a483ae447dac0c","src/ext/std.rs":"c35fd8bdafd24a6adef4f5a27e8e4bb421b0e50cebfe350868ded43980b1a06e","src/imports.rs":"40aa666ccca0b8e7469d2a9193588b00425a8efd4252f7b9a8e6b5144401c5f9","src/lib.rs":"2db558cb84747a1178155997ff55c4cf87972a9fd0fdb7f0ed1c04f1c97d9f47","src/proxy.rs":"6261baf8ac38d8e5dac067156c78448a81eede6963e9370dbf59940fac71ada2","wit/deps/cli.wit":"91aebf406712e618d037fa153b270476541d48d890b4ad6b111ec97b40d73194","wit/deps/clocks.wit":"654a3b5e8b065202334de477828444dca099e576660b6a069c6aaf64685bf68f","wit/deps/filesystem.wit":"4065ba3e5d45ac9cdcfb4bbc31774424d24c453abccf1c9421414ff45f8459b9","wit/deps/http.wit":"873455584340b0e0aa801b842a72d86a67206825bf2d8fc6bca870da6e6ff8f6","wit/deps/io.wit":"80957ac766c6b7eb743ce237a8f92700e1a0b007a1856794ef1972b61ddfce50","wit/deps/random.wit":"4b3229e0ec18cc3fe71f824f855d9cf04809ec8a8d383f088671ca654f7fb885","wit/deps/sockets.wit":"d19af1d835606a576aace12d24a3699aa6f150e22084bb8d880e285c6257786c","wit/wasi-crate.wit":"561b9308e635f442fcfbe3c4e783d0b4f2110f4c7bdadc8775170e2e119f7453"},"package":"9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"} \ No newline at end of file diff --git a/tools/vendor/wasip2/.cargo_vcs_info.json b/tools/vendor/wasip2/.cargo_vcs_info.json new file mode 100644 index 0000000000..647e6d9cdf --- /dev/null +++ b/tools/vendor/wasip2/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "06ce201370fcde0d1b0d47cac8ecb1b0b312c9f9" + }, + "path_in_vcs": "crates/wasip2" +} \ No newline at end of file diff --git a/tools/vendor/wasip2/Cargo.lock b/tools/vendor/wasip2/Cargo.lock new file mode 100644 index 0000000000..9080ddb2aa --- /dev/null +++ b/tools/vendor/wasip2/Cargo.lock @@ -0,0 +1,41 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "rustc-std-workspace-alloc" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d441c3b2ebf55cebf796bfdc265d67fa09db17b7bb6bd4be75c509e1e8fec3" + +[[package]] +name = "rustc-std-workspace-core" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9c45b374136f52f2d6311062c7146bff20fec063c3f5d46a410bd937746955" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +dependencies = [ + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", + "wit-bindgen", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "bitflags", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] diff --git a/tools/vendor/wasip2/Cargo.toml b/tools/vendor/wasip2/Cargo.toml new file mode 100644 index 0000000000..378be68239 --- /dev/null +++ b/tools/vendor/wasip2/Cargo.toml @@ -0,0 +1,97 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.87.0" +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "WASIp2 API bindings for Rust" +documentation = "https://docs.rs/wasip2" +readme = "README.md" +keywords = [ + "webassembly", + "wasm", +] +categories = [ + "no-std", + "wasm", +] +license = "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT" +repository = "https://github.com/bytecodealliance/wasi-rs" + +[features] +bitflags = ["wit-bindgen/bitflags"] +default = [ + "std", + "bitflags", +] +rustc-dep-of-std = [ + "core", + "alloc", + "wit-bindgen/rustc-dep-of-std", +] +std = [] + +[lib] +name = "wasip2" +path = "src/lib.rs" + +[[example]] +name = "cli-command" +crate-type = ["cdylib"] +path = "examples/cli-command.rs" +required-features = ["std"] + +[[example]] +name = "cli-command-no_std" +crate-type = ["cdylib"] +path = "examples/cli-command-no_std.rs" + +[[example]] +name = "hello-world" +path = "examples/hello-world.rs" +required-features = ["std"] + +[[example]] +name = "hello-world-no_std" +path = "examples/hello-world-no_std.rs" + +[[example]] +name = "http-proxy" +crate-type = ["cdylib"] +path = "examples/http-proxy.rs" +required-features = ["std"] + +[[example]] +name = "http-proxy-no_std" +crate-type = ["cdylib"] +path = "examples/http-proxy-no_std.rs" + +[dependencies.alloc] +version = "1.0" +optional = true +package = "rustc-std-workspace-alloc" + +[dependencies.core] +version = "1.0" +optional = true +package = "rustc-std-workspace-core" + +[dependencies.wit-bindgen] +version = "0.51.0" +default-features = false diff --git a/tools/vendor/wasip2/Cargo.toml.orig b/tools/vendor/wasip2/Cargo.toml.orig new file mode 100644 index 0000000000..79c09cc116 --- /dev/null +++ b/tools/vendor/wasip2/Cargo.toml.orig @@ -0,0 +1,48 @@ +[package] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +description = "WASIp2 API bindings for Rust" +categories = ["no-std", "wasm"] +keywords = ["webassembly", "wasm"] +documentation = "https://docs.rs/wasip2" +readme = "README.md" +license.workspace = true +edition.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +wit-bindgen = { workspace = true } + +# When built as part of libstd +core = { optional = true, workspace = true } +alloc = { optional = true, workspace = true } + +[features] +default = ["std", "bitflags"] +std = [] +bitflags = ["wit-bindgen/bitflags"] +# Unstable feature to support being a libstd dependency +rustc-dep-of-std = ["core", "alloc", "wit-bindgen/rustc-dep-of-std"] + +[[example]] +name = "cli-command-no_std" +crate-type = ["cdylib"] + +[[example]] +name = "cli-command" +crate-type = ["cdylib"] +required-features = ["std"] + +[[example]] +name = "hello-world" +required-features = ["std"] + +[[example]] +name = "http-proxy-no_std" +crate-type = ["cdylib"] + +[[example]] +name = "http-proxy" +crate-type = ["cdylib"] +required-features = ["std"] diff --git a/tools/vendor/wasip2/README.md b/tools/vendor/wasip2/README.md new file mode 100644 index 0000000000..16f8eb7eda --- /dev/null +++ b/tools/vendor/wasip2/README.md @@ -0,0 +1,117 @@ +

+ +This crate contains bindings for [WASIp2](https://github.com/WebAssembly/WASI) +APIs for the worlds: + +* [`wasi:cli/command`] +* [`wasi:http/proxy`] + +This crate is procedurally generated from [WIT] files using [`wit-bindgen`]. + +[`wasi:cli/command`]: https://github.com/WebAssembly/wasi-cli +[`wasi:http/proxy`]: https://github.com/WebAssembly/wasi-http +[WIT]: https://component-model.bytecodealliance.org/design/wit.html +[`wit-bindgen`]: https://github.com/bytecodealliance/wit-bindgen +[components]: https://component-model.bytecodealliance.org/ +[`wasm-tools`]: https://github.com/bytecodealliance/wasm-tools + +# Usage + +Depending on this crate can be done by adding it to your dependencies: + +```sh +$ cargo add wasip2 +``` + +Next you can use the APIs in the root of the module like so: + +```rust +fn main() { + let stdout = wasip2::cli::stdout::get_stdout(); + stdout.blocking_write_and_flush(b"Hello, world!\n").unwrap(); +} +``` + +This crate can currently be used in three main ways. + +- One is to use it and compile for the [`wasm32-wasip2` target] in Rust 1.82 and later. + this is the simplest approach, as all the tools needed are included in the + Rust tooling, however it doesn't yet support some of the features of the + other approaches. + +- Another is to use it and compile using [`cargo component`]. This is essentially + the same as the next option, except that `cargo component` handles most of the + steps for you. `cargo component` also has a number of additional features for + working with dependencies and custom WIT interfaces. + +- And the third is to compile for the `wasm32-wasip1` target, and then adapt + the resulting modules into component using `wasm-tools component new`; see + the next section here for details. + +[`wasm32-wasip2` target]: https://blog.rust-lang.org/2024/11/26/wasip2-tier-2.html +[`cargo component`]: https://github.com/bytecodealliance/cargo-component + +## Building with wasm32-wasip1 and `cargo component new`. + +The `wasm32-wasip2` target works with a simple `cargo build --target wasm32-wasip2` +and doesn't need a lot of documentation here, and `cargo component` has its own +documentation, so here we have some documentation for the `wasm32-wasip1` way. + +``` +$ cargo build --target wasm32-wasip1 +``` + +Next you'll want an "adapter" to convert the Rust standard library's usage of +`wasi_snapshot_preview1` to the component model. An example adapter can be found +from [Wasmtime's release page](https://github.com/bytecodealliance/wasmtime/releases/download/v17.0.0/wasi_snapshot_preview1.command.wasm). + +``` +$ curl -LO https://github.com/bytecodealliance/wasmtime/releases/download/v17.0.0/wasi_snapshot_preview1.command.wasm +``` + +Next to create a component you'll use the [`wasm-tools`] CLI to create a +component: + +``` +$ cargo install wasm-tools +$ wasm-tools component new target/wasm32-wasip1/debug/foo.wasm \ + --adapt ./wasi_snapshot_preview1.command.wasm \ + -o component.wasm +``` + +And finally the component can be run by a runtime that has Component Model +support, such as [Wasmtime]: + +``` +$ wasmtime run component.wasm +Hello, world! +``` + +[Wasmtime]: https://github.com/bytecodealliance/wasmtime + +# Development + +The bulk of the `wasip2` crate is generated by the [`wit-bindgen`] tool. The +`src/bindings.rs` file can be regenerated with: + +``` +$ ./ci/regenerate.sh +``` + +WASI definitions are located in the `wit` directory of this repository. +Currently they're copied from upstream repositories but are hoped to be better +managed in the future. diff --git a/tools/vendor/wasip2/examples/cli-command-no_std.rs b/tools/vendor/wasip2/examples/cli-command-no_std.rs new file mode 100644 index 0000000000..667d2fe1c4 --- /dev/null +++ b/tools/vendor/wasip2/examples/cli-command-no_std.rs @@ -0,0 +1,11 @@ +wasip2::cli::command::export!(Example); + +struct Example; + +impl wasip2::exports::cli::run::Guest for Example { + fn run() -> Result<(), ()> { + let stdout = wasip2::cli::stdout::get_stdout(); + stdout.blocking_write_and_flush(b"Hello, WASI!").unwrap(); + Ok(()) + } +} diff --git a/tools/vendor/wasip2/examples/cli-command.rs b/tools/vendor/wasip2/examples/cli-command.rs new file mode 100644 index 0000000000..3f5d5d1890 --- /dev/null +++ b/tools/vendor/wasip2/examples/cli-command.rs @@ -0,0 +1,14 @@ +use std::io::Write as _; + +wasip2::cli::command::export!(Example); + +struct Example; + +impl wasip2::exports::cli::run::Guest for Example { + fn run() -> Result<(), ()> { + let mut stdout = wasip2::cli::stdout::get_stdout(); + stdout.write_all(b"Hello, WASI!").unwrap(); + stdout.flush().unwrap(); + Ok(()) + } +} diff --git a/tools/vendor/wasip2/examples/hello-world-no_std.rs b/tools/vendor/wasip2/examples/hello-world-no_std.rs new file mode 100644 index 0000000000..ea6b416ad7 --- /dev/null +++ b/tools/vendor/wasip2/examples/hello-world-no_std.rs @@ -0,0 +1,4 @@ +fn main() { + let stdout = wasip2::cli::stdout::get_stdout(); + stdout.blocking_write_and_flush(b"Hello, world!\n").unwrap(); +} diff --git a/tools/vendor/wasip2/examples/hello-world.rs b/tools/vendor/wasip2/examples/hello-world.rs new file mode 100644 index 0000000000..5030f12cf0 --- /dev/null +++ b/tools/vendor/wasip2/examples/hello-world.rs @@ -0,0 +1,7 @@ +use std::io::Write as _; + +fn main() { + let mut stdout = wasip2::cli::stdout::get_stdout(); + stdout.write_all(b"Hello, world!\n").unwrap(); + stdout.flush().unwrap(); +} diff --git a/tools/vendor/wasip2/examples/http-proxy-no_std.rs b/tools/vendor/wasip2/examples/http-proxy-no_std.rs new file mode 100644 index 0000000000..9f05f9612d --- /dev/null +++ b/tools/vendor/wasip2/examples/http-proxy-no_std.rs @@ -0,0 +1,22 @@ +use wasip2::http::types::{ + Fields, IncomingRequest, OutgoingBody, OutgoingResponse, ResponseOutparam, +}; + +wasip2::http::proxy::export!(Example); + +struct Example; + +impl wasip2::exports::http::incoming_handler::Guest for Example { + fn handle(_request: IncomingRequest, response_out: ResponseOutparam) { + let resp = OutgoingResponse::new(Fields::new()); + let body = resp.body().unwrap(); + + ResponseOutparam::set(response_out, Ok(resp)); + + let out = body.write().unwrap(); + out.blocking_write_and_flush(b"Hello, WASI!").unwrap(); + drop(out); + + OutgoingBody::finish(body, None).unwrap(); + } +} diff --git a/tools/vendor/wasip2/examples/http-proxy.rs b/tools/vendor/wasip2/examples/http-proxy.rs new file mode 100644 index 0000000000..86a90b3b80 --- /dev/null +++ b/tools/vendor/wasip2/examples/http-proxy.rs @@ -0,0 +1,25 @@ +use std::io::Write as _; + +use wasip2::http::types::{ + Fields, IncomingRequest, OutgoingBody, OutgoingResponse, ResponseOutparam, +}; + +wasip2::http::proxy::export!(Example); + +struct Example; + +impl wasip2::exports::http::incoming_handler::Guest for Example { + fn handle(_request: IncomingRequest, response_out: ResponseOutparam) { + let resp = OutgoingResponse::new(Fields::new()); + let body = resp.body().unwrap(); + + ResponseOutparam::set(response_out, Ok(resp)); + + let mut out = body.write().unwrap(); + out.write_all(b"Hello, WASI!").unwrap(); + out.flush().unwrap(); + drop(out); + + OutgoingBody::finish(body, None).unwrap(); + } +} diff --git a/tools/vendor/wasip2/src/command.rs b/tools/vendor/wasip2/src/command.rs new file mode 100644 index 0000000000..d591db7b4d --- /dev/null +++ b/tools/vendor/wasip2/src/command.rs @@ -0,0 +1,627 @@ +// Generated by `wit-bindgen` 0.51.0. DO NOT EDIT! +// Options used: +// * std_feature +// * with "wasi:cli/environment@0.2.9" = "crate::cli::environment" +// * with "wasi:cli/exit@0.2.9" = "crate::cli::exit" +// * with "wasi:cli/stdin@0.2.9" = "crate::cli::stdin" +// * with "wasi:cli/stdout@0.2.9" = "crate::cli::stdout" +// * with "wasi:cli/stderr@0.2.9" = "crate::cli::stderr" +// * with "wasi:cli/terminal-input@0.2.9" = "crate::cli::terminal_input" +// * with "wasi:cli/terminal-output@0.2.9" = "crate::cli::terminal_output" +// * with "wasi:cli/terminal-stdin@0.2.9" = "crate::cli::terminal_stdin" +// * with "wasi:cli/terminal-stdout@0.2.9" = "crate::cli::terminal_stdout" +// * with "wasi:cli/terminal-stderr@0.2.9" = "crate::cli::terminal_stderr" +// * with "wasi:clocks/monotonic-clock@0.2.9" = "crate::clocks::monotonic_clock" +// * with "wasi:clocks/wall-clock@0.2.9" = "crate::clocks::wall_clock" +// * with "wasi:filesystem/types@0.2.9" = "crate::filesystem::types" +// * with "wasi:filesystem/preopens@0.2.9" = "crate::filesystem::preopens" +// * with "wasi:io/error@0.2.9" = "crate::io::error" +// * with "wasi:io/poll@0.2.9" = "crate::io::poll" +// * with "wasi:io/streams@0.2.9" = "crate::io::streams" +// * with "wasi:random/random@0.2.9" = "crate::random::random" +// * with "wasi:random/insecure@0.2.9" = "crate::random::insecure" +// * with "wasi:random/insecure-seed@0.2.9" = "crate::random::insecure_seed" +// * with "wasi:sockets/network@0.2.9" = "crate::sockets::network" +// * with "wasi:sockets/instance-network@0.2.9" = "crate::sockets::instance_network" +// * with "wasi:sockets/tcp@0.2.9" = "crate::sockets::tcp" +// * with "wasi:sockets/tcp-create-socket@0.2.9" = "crate::sockets::tcp_create_socket" +// * with "wasi:sockets/udp@0.2.9" = "crate::sockets::udp" +// * with "wasi:sockets/udp-create-socket@0.2.9" = "crate::sockets::udp_create_socket" +// * with "wasi:sockets/ip-name-lookup@0.2.9" = "crate::sockets::ip_name_lookup" +// * type_section_suffix: "rust-wasip2-1.0.2+wasi-0.2.9-from-crates-io-command-world" +// * default-bindings-module: "$crate" +// * export-macro-name: _export_command +// * pub-export-macro +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::cli::environment as __with_name0; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::cli::exit as __with_name1; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::io::error as __with_name2; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::io::poll as __with_name3; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::io::streams as __with_name4; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::cli::stdin as __with_name5; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::cli::stdout as __with_name6; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::cli::stderr as __with_name7; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::cli::terminal_input as __with_name8; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::cli::terminal_output as __with_name9; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::cli::terminal_stdin as __with_name10; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::cli::terminal_stdout as __with_name11; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::cli::terminal_stderr as __with_name12; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::clocks::monotonic_clock as __with_name13; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::clocks::wall_clock as __with_name14; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::filesystem::types as __with_name15; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::filesystem::preopens as __with_name16; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::sockets::network as __with_name17; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::sockets::instance_network as __with_name18; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::sockets::udp as __with_name19; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::sockets::udp_create_socket as __with_name20; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::sockets::tcp as __with_name21; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::sockets::tcp_create_socket as __with_name22; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::sockets::ip_name_lookup as __with_name23; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::random::random as __with_name24; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::random::insecure as __with_name25; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::random::insecure_seed as __with_name26; +#[rustfmt::skip] +#[allow(dead_code, clippy::all)] +pub mod exports { + pub mod wasi { + pub mod cli { + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod run { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::super::__link_custom_section_describing_imports; + use super::super::super::super::_rt; + #[doc(hidden)] + #[allow(non_snake_case, unused_unsafe)] + pub unsafe fn _export_run_cabi() -> i32 { + unsafe { + #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); + let result0 = { T::run() }; + let result1 = match result0 { + Ok(_) => 0i32, + Err(_) => 1i32, + }; + result1 + } + } + pub trait Guest { + /// Run the program. + #[allow(async_fn_in_trait)] + fn run() -> Result<(), ()>; + } + #[doc(hidden)] + #[macro_export] + macro_rules! __export_wasi_cli_run_0_2_9_cabi { + ($ty:ident with_types_in $($path_to_types:tt)*) => { + const _ : () = { #[unsafe (export_name = + "wasi:cli/run@0.2.9#run")] unsafe extern "C" fn export_run() -> + i32 { unsafe { $($path_to_types)*:: _export_run_cabi::<$ty > () } + } }; + }; + } + #[doc(hidden)] + pub use __export_wasi_cli_run_0_2_9_cabi; + } + } + } +} +#[rustfmt::skip] +mod _rt { + #![allow(dead_code, unused_imports, clippy::all)] + #[cfg(target_arch = "wasm32")] + pub fn run_ctors_once() { + wit_bindgen::rt::run_ctors_once(); + } +} +/// Generates `#[unsafe(no_mangle)]` functions to export the specified type as +/// the root implementation of all generated traits. +/// +/// For more information see the documentation of `wit_bindgen::generate!`. +/// +/// ```rust +/// # macro_rules! _export_command{ ($($t:tt)*) => (); } +/// # trait Guest {} +/// struct MyType; +/// +/// impl Guest for MyType { +/// // ... +/// } +/// +/// _export_command!(MyType); +/// ``` +#[allow(unused_macros)] +#[doc(hidden)] +#[macro_export] +macro_rules! __export_command_impl { + ($ty:ident) => { + $crate::_export_command!($ty with_types_in $crate); + }; + ($ty:ident with_types_in $($path_to_types_root:tt)*) => { + $($path_to_types_root)*:: + exports::wasi::cli::run::__export_wasi_cli_run_0_2_9_cabi!($ty with_types_in + $($path_to_types_root)*:: exports::wasi::cli::run); const _ : () = { + #[rustfmt::skip] #[cfg(target_arch = "wasm32")] #[unsafe (link_section = + "component-type:wit-bindgen:0.51.0:wasi:cli@0.2.9:command:imports and exportsrust-wasip2-1.0.2+wasi-0.2.9-from-crates-io-command-world")] + #[doc(hidden)] #[allow(clippy::octal_escapes)] pub static + __WIT_BINDGEN_COMPONENT_TYPE : [u8; 10773] = * + b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x97S\x01A\x02\x01AI\x01\ +B\x0a\x01o\x02ss\x01p\0\x01@\0\0\x01\x04\0\x0fget-environment\x01\x02\x01ps\x01@\ +\0\0\x03\x04\0\x0dget-arguments\x01\x04\x01ks\x01@\0\0\x05\x04\0\x0binitial-cwd\x01\ +\x06\x03\0\x1awasi:cli/environment@0.2.9\x05\0\x01B\x03\x01j\0\0\x01@\x01\x06sta\ +tus\0\x01\0\x04\0\x04exit\x01\x01\x03\0\x13wasi:cli/exit@0.2.9\x05\x01\x01B\x04\x04\ +\0\x05error\x03\x01\x01h\0\x01@\x01\x04self\x01\0s\x04\0\x1d[method]error.to-deb\ +ug-string\x01\x02\x03\0\x13wasi:io/error@0.2.9\x05\x02\x01B\x0a\x04\0\x08pollabl\ +e\x03\x01\x01h\0\x01@\x01\x04self\x01\0\x7f\x04\0\x16[method]pollable.ready\x01\x02\ +\x01@\x01\x04self\x01\x01\0\x04\0\x16[method]pollable.block\x01\x03\x01p\x01\x01\ +py\x01@\x01\x02in\x04\0\x05\x04\0\x04poll\x01\x06\x03\0\x12wasi:io/poll@0.2.9\x05\ +\x03\x02\x03\0\x02\x05error\x02\x03\0\x03\x08pollable\x01B(\x02\x03\x02\x01\x04\x04\ +\0\x05error\x03\0\0\x02\x03\x02\x01\x05\x04\0\x08pollable\x03\0\x02\x01i\x01\x01\ +q\x02\x15last-operation-failed\x01\x04\0\x06closed\0\0\x04\0\x0cstream-error\x03\ +\0\x05\x04\0\x0cinput-stream\x03\x01\x04\0\x0doutput-stream\x03\x01\x01h\x07\x01\ +p}\x01j\x01\x0a\x01\x06\x01@\x02\x04self\x09\x03lenw\0\x0b\x04\0\x19[method]inpu\ +t-stream.read\x01\x0c\x04\0\"[method]input-stream.blocking-read\x01\x0c\x01j\x01\ +w\x01\x06\x01@\x02\x04self\x09\x03lenw\0\x0d\x04\0\x19[method]input-stream.skip\x01\ +\x0e\x04\0\"[method]input-stream.blocking-skip\x01\x0e\x01i\x03\x01@\x01\x04self\ +\x09\0\x0f\x04\0\x1e[method]input-stream.subscribe\x01\x10\x01h\x08\x01@\x01\x04\ +self\x11\0\x0d\x04\0![method]output-stream.check-write\x01\x12\x01j\0\x01\x06\x01\ +@\x02\x04self\x11\x08contents\x0a\0\x13\x04\0\x1b[method]output-stream.write\x01\ +\x14\x04\0.[method]output-stream.blocking-write-and-flush\x01\x14\x01@\x01\x04se\ +lf\x11\0\x13\x04\0\x1b[method]output-stream.flush\x01\x15\x04\0$[method]output-s\ +tream.blocking-flush\x01\x15\x01@\x01\x04self\x11\0\x0f\x04\0\x1f[method]output-\ +stream.subscribe\x01\x16\x01@\x02\x04self\x11\x03lenw\0\x13\x04\0\"[method]outpu\ +t-stream.write-zeroes\x01\x17\x04\05[method]output-stream.blocking-write-zeroes-\ +and-flush\x01\x17\x01@\x03\x04self\x11\x03src\x09\x03lenw\0\x0d\x04\0\x1c[method\ +]output-stream.splice\x01\x18\x04\0%[method]output-stream.blocking-splice\x01\x18\ +\x03\0\x15wasi:io/streams@0.2.9\x05\x06\x02\x03\0\x04\x0cinput-stream\x01B\x05\x02\ +\x03\x02\x01\x07\x04\0\x0cinput-stream\x03\0\0\x01i\x01\x01@\0\0\x02\x04\0\x09ge\ +t-stdin\x01\x03\x03\0\x14wasi:cli/stdin@0.2.9\x05\x08\x02\x03\0\x04\x0doutput-st\ +ream\x01B\x05\x02\x03\x02\x01\x09\x04\0\x0doutput-stream\x03\0\0\x01i\x01\x01@\0\ +\0\x02\x04\0\x0aget-stdout\x01\x03\x03\0\x15wasi:cli/stdout@0.2.9\x05\x0a\x01B\x05\ +\x02\x03\x02\x01\x09\x04\0\x0doutput-stream\x03\0\0\x01i\x01\x01@\0\0\x02\x04\0\x0a\ +get-stderr\x01\x03\x03\0\x15wasi:cli/stderr@0.2.9\x05\x0b\x01B\x01\x04\0\x0eterm\ +inal-input\x03\x01\x03\0\x1dwasi:cli/terminal-input@0.2.9\x05\x0c\x01B\x01\x04\0\ +\x0fterminal-output\x03\x01\x03\0\x1ewasi:cli/terminal-output@0.2.9\x05\x0d\x02\x03\ +\0\x08\x0eterminal-input\x01B\x06\x02\x03\x02\x01\x0e\x04\0\x0eterminal-input\x03\ +\0\0\x01i\x01\x01k\x02\x01@\0\0\x03\x04\0\x12get-terminal-stdin\x01\x04\x03\0\x1d\ +wasi:cli/terminal-stdin@0.2.9\x05\x0f\x02\x03\0\x09\x0fterminal-output\x01B\x06\x02\ +\x03\x02\x01\x10\x04\0\x0fterminal-output\x03\0\0\x01i\x01\x01k\x02\x01@\0\0\x03\ +\x04\0\x13get-terminal-stdout\x01\x04\x03\0\x1ewasi:cli/terminal-stdout@0.2.9\x05\ +\x11\x01B\x06\x02\x03\x02\x01\x10\x04\0\x0fterminal-output\x03\0\0\x01i\x01\x01k\ +\x02\x01@\0\0\x03\x04\0\x13get-terminal-stderr\x01\x04\x03\0\x1ewasi:cli/termina\ +l-stderr@0.2.9\x05\x12\x01B\x0f\x02\x03\x02\x01\x05\x04\0\x08pollable\x03\0\0\x01\ +w\x04\0\x07instant\x03\0\x02\x01w\x04\0\x08duration\x03\0\x04\x01@\0\0\x03\x04\0\ +\x03now\x01\x06\x01@\0\0\x05\x04\0\x0aresolution\x01\x07\x01i\x01\x01@\x01\x04wh\ +en\x03\0\x08\x04\0\x11subscribe-instant\x01\x09\x01@\x01\x04when\x05\0\x08\x04\0\ +\x12subscribe-duration\x01\x0a\x03\0!wasi:clocks/monotonic-clock@0.2.9\x05\x13\x01\ +B\x05\x01r\x02\x07secondsw\x0bnanosecondsy\x04\0\x08datetime\x03\0\0\x01@\0\0\x01\ +\x04\0\x03now\x01\x02\x04\0\x0aresolution\x01\x02\x03\0\x1cwasi:clocks/wall-cloc\ +k@0.2.9\x05\x14\x02\x03\0\x04\x05error\x02\x03\0\x0e\x08datetime\x01Br\x02\x03\x02\ +\x01\x07\x04\0\x0cinput-stream\x03\0\0\x02\x03\x02\x01\x09\x04\0\x0doutput-strea\ +m\x03\0\x02\x02\x03\x02\x01\x15\x04\0\x05error\x03\0\x04\x02\x03\x02\x01\x16\x04\ +\0\x08datetime\x03\0\x06\x01w\x04\0\x08filesize\x03\0\x08\x01m\x08\x07unknown\x0c\ +block-device\x10character-device\x09directory\x04fifo\x0dsymbolic-link\x0cregula\ +r-file\x06socket\x04\0\x0fdescriptor-type\x03\0\x0a\x01n\x06\x04read\x05write\x13\ +file-integrity-sync\x13data-integrity-sync\x14requested-write-sync\x10mutate-dir\ +ectory\x04\0\x10descriptor-flags\x03\0\x0c\x01n\x01\x0esymlink-follow\x04\0\x0ap\ +ath-flags\x03\0\x0e\x01n\x04\x06create\x09directory\x09exclusive\x08truncate\x04\ +\0\x0aopen-flags\x03\0\x10\x01w\x04\0\x0alink-count\x03\0\x12\x01k\x07\x01r\x06\x04\ +type\x0b\x0alink-count\x13\x04size\x09\x15data-access-timestamp\x14\x1bdata-modi\ +fication-timestamp\x14\x17status-change-timestamp\x14\x04\0\x0fdescriptor-stat\x03\ +\0\x15\x01q\x03\x09no-change\0\0\x03now\0\0\x09timestamp\x01\x07\0\x04\0\x0dnew-\ +timestamp\x03\0\x17\x01r\x02\x04type\x0b\x04names\x04\0\x0fdirectory-entry\x03\0\ +\x19\x01m%\x06access\x0bwould-block\x07already\x0ebad-descriptor\x04busy\x08dead\ +lock\x05quota\x05exist\x0efile-too-large\x15illegal-byte-sequence\x0bin-progress\ +\x0binterrupted\x07invalid\x02io\x0cis-directory\x04loop\x0etoo-many-links\x0cme\ +ssage-size\x0dname-too-long\x09no-device\x08no-entry\x07no-lock\x13insufficient-\ +memory\x12insufficient-space\x0dnot-directory\x09not-empty\x0fnot-recoverable\x0b\ +unsupported\x06no-tty\x0eno-such-device\x08overflow\x0dnot-permitted\x04pipe\x09\ +read-only\x0cinvalid-seek\x0etext-file-busy\x0ccross-device\x04\0\x0aerror-code\x03\ +\0\x1b\x01m\x06\x06normal\x0asequential\x06random\x09will-need\x09dont-need\x08n\ +o-reuse\x04\0\x06advice\x03\0\x1d\x01r\x02\x05lowerw\x05upperw\x04\0\x13metadata\ +-hash-value\x03\0\x1f\x04\0\x0adescriptor\x03\x01\x04\0\x16directory-entry-strea\ +m\x03\x01\x01h!\x01i\x01\x01j\x01$\x01\x1c\x01@\x02\x04self#\x06offset\x09\0%\x04\ +\0\"[method]descriptor.read-via-stream\x01&\x01i\x03\x01j\x01'\x01\x1c\x01@\x02\x04\ +self#\x06offset\x09\0(\x04\0#[method]descriptor.write-via-stream\x01)\x01@\x01\x04\ +self#\0(\x04\0$[method]descriptor.append-via-stream\x01*\x01j\0\x01\x1c\x01@\x04\ +\x04self#\x06offset\x09\x06length\x09\x06advice\x1e\0+\x04\0\x19[method]descript\ +or.advise\x01,\x01@\x01\x04self#\0+\x04\0\x1c[method]descriptor.sync-data\x01-\x01\ +j\x01\x0d\x01\x1c\x01@\x01\x04self#\0.\x04\0\x1c[method]descriptor.get-flags\x01\ +/\x01j\x01\x0b\x01\x1c\x01@\x01\x04self#\00\x04\0\x1b[method]descriptor.get-type\ +\x011\x01@\x02\x04self#\x04size\x09\0+\x04\0\x1b[method]descriptor.set-size\x012\ +\x01@\x03\x04self#\x15data-access-timestamp\x18\x1bdata-modification-timestamp\x18\ +\0+\x04\0\x1c[method]descriptor.set-times\x013\x01p}\x01o\x024\x7f\x01j\x015\x01\ +\x1c\x01@\x03\x04self#\x06length\x09\x06offset\x09\06\x04\0\x17[method]descripto\ +r.read\x017\x01j\x01\x09\x01\x1c\x01@\x03\x04self#\x06buffer4\x06offset\x09\08\x04\ +\0\x18[method]descriptor.write\x019\x01i\"\x01j\x01:\x01\x1c\x01@\x01\x04self#\0\ +;\x04\0![method]descriptor.read-directory\x01<\x04\0\x17[method]descriptor.sync\x01\ +-\x01@\x02\x04self#\x04paths\0+\x04\0&[method]descriptor.create-directory-at\x01\ +=\x01j\x01\x16\x01\x1c\x01@\x01\x04self#\0>\x04\0\x17[method]descriptor.stat\x01\ +?\x01@\x03\x04self#\x0apath-flags\x0f\x04paths\0>\x04\0\x1a[method]descriptor.st\ +at-at\x01@\x01@\x05\x04self#\x0apath-flags\x0f\x04paths\x15data-access-timestamp\ +\x18\x1bdata-modification-timestamp\x18\0+\x04\0\x1f[method]descriptor.set-times\ +-at\x01A\x01@\x05\x04self#\x0eold-path-flags\x0f\x08old-paths\x0enew-descriptor#\ +\x08new-paths\0+\x04\0\x1a[method]descriptor.link-at\x01B\x01i!\x01j\x01\xc3\0\x01\ +\x1c\x01@\x05\x04self#\x0apath-flags\x0f\x04paths\x0aopen-flags\x11\x05flags\x0d\ +\0\xc4\0\x04\0\x1a[method]descriptor.open-at\x01E\x01j\x01s\x01\x1c\x01@\x02\x04\ +self#\x04paths\0\xc6\0\x04\0\x1e[method]descriptor.readlink-at\x01G\x04\0&[metho\ +d]descriptor.remove-directory-at\x01=\x01@\x04\x04self#\x08old-paths\x0enew-desc\ +riptor#\x08new-paths\0+\x04\0\x1c[method]descriptor.rename-at\x01H\x01@\x03\x04s\ +elf#\x08old-paths\x08new-paths\0+\x04\0\x1d[method]descriptor.symlink-at\x01I\x04\ +\0![method]descriptor.unlink-file-at\x01=\x01@\x02\x04self#\x05other#\0\x7f\x04\0\ +![method]descriptor.is-same-object\x01J\x01j\x01\x20\x01\x1c\x01@\x01\x04self#\0\ +\xcb\0\x04\0\x20[method]descriptor.metadata-hash\x01L\x01@\x03\x04self#\x0apath-\ +flags\x0f\x04paths\0\xcb\0\x04\0#[method]descriptor.metadata-hash-at\x01M\x01h\"\ +\x01k\x1a\x01j\x01\xcf\0\x01\x1c\x01@\x01\x04self\xce\0\0\xd0\0\x04\03[method]di\ +rectory-entry-stream.read-directory-entry\x01Q\x01h\x05\x01k\x1c\x01@\x01\x03err\ +\xd2\0\0\xd3\0\x04\0\x15filesystem-error-code\x01T\x03\0\x1bwasi:filesystem/type\ +s@0.2.9\x05\x17\x02\x03\0\x0f\x0adescriptor\x01B\x07\x02\x03\x02\x01\x18\x04\0\x0a\ +descriptor\x03\0\0\x01i\x01\x01o\x02\x02s\x01p\x03\x01@\0\0\x04\x04\0\x0fget-dir\ +ectories\x01\x05\x03\0\x1ewasi:filesystem/preopens@0.2.9\x05\x19\x01B\x11\x04\0\x07\ +network\x03\x01\x01m\x15\x07unknown\x0daccess-denied\x0dnot-supported\x10invalid\ +-argument\x0dout-of-memory\x07timeout\x14concurrency-conflict\x0fnot-in-progress\ +\x0bwould-block\x0dinvalid-state\x10new-socket-limit\x14address-not-bindable\x0e\ +address-in-use\x12remote-unreachable\x12connection-refused\x10connection-reset\x12\ +connection-aborted\x12datagram-too-large\x11name-unresolvable\x1atemporary-resol\ +ver-failure\x1apermanent-resolver-failure\x04\0\x0aerror-code\x03\0\x01\x01m\x02\ +\x04ipv4\x04ipv6\x04\0\x11ip-address-family\x03\0\x03\x01o\x04}}}}\x04\0\x0cipv4\ +-address\x03\0\x05\x01o\x08{{{{{{{{\x04\0\x0cipv6-address\x03\0\x07\x01q\x02\x04\ +ipv4\x01\x06\0\x04ipv6\x01\x08\0\x04\0\x0aip-address\x03\0\x09\x01r\x02\x04port{\ +\x07address\x06\x04\0\x13ipv4-socket-address\x03\0\x0b\x01r\x04\x04port{\x09flow\ +-infoy\x07address\x08\x08scope-idy\x04\0\x13ipv6-socket-address\x03\0\x0d\x01q\x02\ +\x04ipv4\x01\x0c\0\x04ipv6\x01\x0e\0\x04\0\x11ip-socket-address\x03\0\x0f\x03\0\x1a\ +wasi:sockets/network@0.2.9\x05\x1a\x02\x03\0\x11\x07network\x01B\x05\x02\x03\x02\ +\x01\x1b\x04\0\x07network\x03\0\0\x01i\x01\x01@\0\0\x02\x04\0\x10instance-networ\ +k\x01\x03\x03\0#wasi:sockets/instance-network@0.2.9\x05\x1c\x02\x03\0\x11\x0aerr\ +or-code\x02\x03\0\x11\x11ip-socket-address\x02\x03\0\x11\x11ip-address-family\x01\ +BD\x02\x03\x02\x01\x05\x04\0\x08pollable\x03\0\0\x02\x03\x02\x01\x1b\x04\0\x07ne\ +twork\x03\0\x02\x02\x03\x02\x01\x1d\x04\0\x0aerror-code\x03\0\x04\x02\x03\x02\x01\ +\x1e\x04\0\x11ip-socket-address\x03\0\x06\x02\x03\x02\x01\x1f\x04\0\x11ip-addres\ +s-family\x03\0\x08\x01p}\x01r\x02\x04data\x0a\x0eremote-address\x07\x04\0\x11inc\ +oming-datagram\x03\0\x0b\x01k\x07\x01r\x02\x04data\x0a\x0eremote-address\x0d\x04\ +\0\x11outgoing-datagram\x03\0\x0e\x04\0\x0audp-socket\x03\x01\x04\0\x18incoming-\ +datagram-stream\x03\x01\x04\0\x18outgoing-datagram-stream\x03\x01\x01h\x10\x01h\x03\ +\x01j\0\x01\x05\x01@\x03\x04self\x13\x07network\x14\x0dlocal-address\x07\0\x15\x04\ +\0\x1d[method]udp-socket.start-bind\x01\x16\x01@\x01\x04self\x13\0\x15\x04\0\x1e\ +[method]udp-socket.finish-bind\x01\x17\x01i\x11\x01i\x12\x01o\x02\x18\x19\x01j\x01\ +\x1a\x01\x05\x01@\x02\x04self\x13\x0eremote-address\x0d\0\x1b\x04\0\x19[method]u\ +dp-socket.stream\x01\x1c\x01j\x01\x07\x01\x05\x01@\x01\x04self\x13\0\x1d\x04\0\x20\ +[method]udp-socket.local-address\x01\x1e\x04\0![method]udp-socket.remote-address\ +\x01\x1e\x01@\x01\x04self\x13\0\x09\x04\0![method]udp-socket.address-family\x01\x1f\ +\x01j\x01}\x01\x05\x01@\x01\x04self\x13\0\x20\x04\0$[method]udp-socket.unicast-h\ +op-limit\x01!\x01@\x02\x04self\x13\x05value}\0\x15\x04\0([method]udp-socket.set-\ +unicast-hop-limit\x01\"\x01j\x01w\x01\x05\x01@\x01\x04self\x13\0#\x04\0&[method]\ +udp-socket.receive-buffer-size\x01$\x01@\x02\x04self\x13\x05valuew\0\x15\x04\0*[\ +method]udp-socket.set-receive-buffer-size\x01%\x04\0#[method]udp-socket.send-buf\ +fer-size\x01$\x04\0'[method]udp-socket.set-send-buffer-size\x01%\x01i\x01\x01@\x01\ +\x04self\x13\0&\x04\0\x1c[method]udp-socket.subscribe\x01'\x01h\x11\x01p\x0c\x01\ +j\x01)\x01\x05\x01@\x02\x04self(\x0bmax-resultsw\0*\x04\0([method]incoming-datag\ +ram-stream.receive\x01+\x01@\x01\x04self(\0&\x04\0*[method]incoming-datagram-str\ +eam.subscribe\x01,\x01h\x12\x01@\x01\x04self-\0#\x04\0+[method]outgoing-datagram\ +-stream.check-send\x01.\x01p\x0f\x01@\x02\x04self-\x09datagrams/\0#\x04\0%[metho\ +d]outgoing-datagram-stream.send\x010\x01@\x01\x04self-\0&\x04\0*[method]outgoing\ +-datagram-stream.subscribe\x011\x03\0\x16wasi:sockets/udp@0.2.9\x05\x20\x02\x03\0\ +\x13\x0audp-socket\x01B\x0c\x02\x03\x02\x01\x1b\x04\0\x07network\x03\0\0\x02\x03\ +\x02\x01\x1d\x04\0\x0aerror-code\x03\0\x02\x02\x03\x02\x01\x1f\x04\0\x11ip-addre\ +ss-family\x03\0\x04\x02\x03\x02\x01!\x04\0\x0audp-socket\x03\0\x06\x01i\x07\x01j\ +\x01\x08\x01\x03\x01@\x01\x0eaddress-family\x05\0\x09\x04\0\x11create-udp-socket\ +\x01\x0a\x03\0$wasi:sockets/udp-create-socket@0.2.9\x05\"\x02\x03\0\x0d\x08durat\ +ion\x01BT\x02\x03\x02\x01\x07\x04\0\x0cinput-stream\x03\0\0\x02\x03\x02\x01\x09\x04\ +\0\x0doutput-stream\x03\0\x02\x02\x03\x02\x01\x05\x04\0\x08pollable\x03\0\x04\x02\ +\x03\x02\x01#\x04\0\x08duration\x03\0\x06\x02\x03\x02\x01\x1b\x04\0\x07network\x03\ +\0\x08\x02\x03\x02\x01\x1d\x04\0\x0aerror-code\x03\0\x0a\x02\x03\x02\x01\x1e\x04\ +\0\x11ip-socket-address\x03\0\x0c\x02\x03\x02\x01\x1f\x04\0\x11ip-address-family\ +\x03\0\x0e\x01m\x03\x07receive\x04send\x04both\x04\0\x0dshutdown-type\x03\0\x10\x04\ +\0\x0atcp-socket\x03\x01\x01h\x12\x01h\x09\x01j\0\x01\x0b\x01@\x03\x04self\x13\x07\ +network\x14\x0dlocal-address\x0d\0\x15\x04\0\x1d[method]tcp-socket.start-bind\x01\ +\x16\x01@\x01\x04self\x13\0\x15\x04\0\x1e[method]tcp-socket.finish-bind\x01\x17\x01\ +@\x03\x04self\x13\x07network\x14\x0eremote-address\x0d\0\x15\x04\0\x20[method]tc\ +p-socket.start-connect\x01\x18\x01i\x01\x01i\x03\x01o\x02\x19\x1a\x01j\x01\x1b\x01\ +\x0b\x01@\x01\x04self\x13\0\x1c\x04\0![method]tcp-socket.finish-connect\x01\x1d\x04\ +\0\x1f[method]tcp-socket.start-listen\x01\x17\x04\0\x20[method]tcp-socket.finish\ +-listen\x01\x17\x01i\x12\x01o\x03\x1e\x19\x1a\x01j\x01\x1f\x01\x0b\x01@\x01\x04s\ +elf\x13\0\x20\x04\0\x19[method]tcp-socket.accept\x01!\x01j\x01\x0d\x01\x0b\x01@\x01\ +\x04self\x13\0\"\x04\0\x20[method]tcp-socket.local-address\x01#\x04\0![method]tc\ +p-socket.remote-address\x01#\x01@\x01\x04self\x13\0\x7f\x04\0\x1f[method]tcp-soc\ +ket.is-listening\x01$\x01@\x01\x04self\x13\0\x0f\x04\0![method]tcp-socket.addres\ +s-family\x01%\x01@\x02\x04self\x13\x05valuew\0\x15\x04\0*[method]tcp-socket.set-\ +listen-backlog-size\x01&\x01j\x01\x7f\x01\x0b\x01@\x01\x04self\x13\0'\x04\0%[met\ +hod]tcp-socket.keep-alive-enabled\x01(\x01@\x02\x04self\x13\x05value\x7f\0\x15\x04\ +\0)[method]tcp-socket.set-keep-alive-enabled\x01)\x01j\x01\x07\x01\x0b\x01@\x01\x04\ +self\x13\0*\x04\0'[method]tcp-socket.keep-alive-idle-time\x01+\x01@\x02\x04self\x13\ +\x05value\x07\0\x15\x04\0+[method]tcp-socket.set-keep-alive-idle-time\x01,\x04\0\ +&[method]tcp-socket.keep-alive-interval\x01+\x04\0*[method]tcp-socket.set-keep-a\ +live-interval\x01,\x01j\x01y\x01\x0b\x01@\x01\x04self\x13\0-\x04\0#[method]tcp-s\ +ocket.keep-alive-count\x01.\x01@\x02\x04self\x13\x05valuey\0\x15\x04\0'[method]t\ +cp-socket.set-keep-alive-count\x01/\x01j\x01}\x01\x0b\x01@\x01\x04self\x13\00\x04\ +\0\x1c[method]tcp-socket.hop-limit\x011\x01@\x02\x04self\x13\x05value}\0\x15\x04\ +\0\x20[method]tcp-socket.set-hop-limit\x012\x01j\x01w\x01\x0b\x01@\x01\x04self\x13\ +\03\x04\0&[method]tcp-socket.receive-buffer-size\x014\x04\0*[method]tcp-socket.s\ +et-receive-buffer-size\x01&\x04\0#[method]tcp-socket.send-buffer-size\x014\x04\0\ +'[method]tcp-socket.set-send-buffer-size\x01&\x01i\x05\x01@\x01\x04self\x13\05\x04\ +\0\x1c[method]tcp-socket.subscribe\x016\x01@\x02\x04self\x13\x0dshutdown-type\x11\ +\0\x15\x04\0\x1b[method]tcp-socket.shutdown\x017\x03\0\x16wasi:sockets/tcp@0.2.9\ +\x05$\x02\x03\0\x15\x0atcp-socket\x01B\x0c\x02\x03\x02\x01\x1b\x04\0\x07network\x03\ +\0\0\x02\x03\x02\x01\x1d\x04\0\x0aerror-code\x03\0\x02\x02\x03\x02\x01\x1f\x04\0\ +\x11ip-address-family\x03\0\x04\x02\x03\x02\x01%\x04\0\x0atcp-socket\x03\0\x06\x01\ +i\x07\x01j\x01\x08\x01\x03\x01@\x01\x0eaddress-family\x05\0\x09\x04\0\x11create-\ +tcp-socket\x01\x0a\x03\0$wasi:sockets/tcp-create-socket@0.2.9\x05&\x02\x03\0\x11\ +\x0aip-address\x01B\x16\x02\x03\x02\x01\x05\x04\0\x08pollable\x03\0\0\x02\x03\x02\ +\x01\x1b\x04\0\x07network\x03\0\x02\x02\x03\x02\x01\x1d\x04\0\x0aerror-code\x03\0\ +\x04\x02\x03\x02\x01'\x04\0\x0aip-address\x03\0\x06\x04\0\x16resolve-address-str\ +eam\x03\x01\x01h\x08\x01k\x07\x01j\x01\x0a\x01\x05\x01@\x01\x04self\x09\0\x0b\x04\ +\03[method]resolve-address-stream.resolve-next-address\x01\x0c\x01i\x01\x01@\x01\ +\x04self\x09\0\x0d\x04\0([method]resolve-address-stream.subscribe\x01\x0e\x01h\x03\ +\x01i\x08\x01j\x01\x10\x01\x05\x01@\x02\x07network\x0f\x04names\0\x11\x04\0\x11r\ +esolve-addresses\x01\x12\x03\0!wasi:sockets/ip-name-lookup@0.2.9\x05(\x01B\x05\x01\ +p}\x01@\x01\x03lenw\0\0\x04\0\x10get-random-bytes\x01\x01\x01@\0\0w\x04\0\x0eget\ +-random-u64\x01\x02\x03\0\x18wasi:random/random@0.2.9\x05)\x01B\x05\x01p}\x01@\x01\ +\x03lenw\0\0\x04\0\x19get-insecure-random-bytes\x01\x01\x01@\0\0w\x04\0\x17get-i\ +nsecure-random-u64\x01\x02\x03\0\x1awasi:random/insecure@0.2.9\x05*\x01B\x03\x01\ +o\x02ww\x01@\0\0\0\x04\0\x0dinsecure-seed\x01\x01\x03\0\x1fwasi:random/insecure-\ +seed@0.2.9\x05+\x01B\x03\x01j\0\0\x01@\0\0\0\x04\0\x03run\x01\x01\x04\0\x12wasi:\ +cli/run@0.2.9\x05,\x04\0\x16wasi:cli/command@0.2.9\x04\0\x0b\x0d\x01\0\x07comman\ +d\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x070.244.0\x10\ +wit-bindgen-rust\x060.51.0"; + }; + }; +} +#[doc(inline)] +pub use __export_command_impl as _export_command; +#[rustfmt::skip] +#[cfg(target_arch = "wasm32")] + +#[cfg_attr(feature = "rustc-dep-of-std", unsafe(link_section = "component-type:wit-bindgen:0.51.0:wasi:cli@0.2.9:command-with-all-of-its-exports-removed:encoded worldrust-wasip2-1.0.2+wasi-0.2.9-from-crates-io-command-world-in-libstd"))] +#[cfg_attr(not(feature = "rustc-dep-of-std"), unsafe(link_section = "component-type:wit-bindgen:0.51.0:wasi:cli@0.2.9:command-with-all-of-its-exports-removed:encoded worldrust-wasip2-1.0.2+wasi-0.2.9-from-crates-io-command-world"))] + +#[doc(hidden)] +#[allow(clippy::octal_escapes)] +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 10794] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x8cS\x01A\x02\x01AG\x01\ +B\x0a\x01o\x02ss\x01p\0\x01@\0\0\x01\x04\0\x0fget-environment\x01\x02\x01ps\x01@\ +\0\0\x03\x04\0\x0dget-arguments\x01\x04\x01ks\x01@\0\0\x05\x04\0\x0binitial-cwd\x01\ +\x06\x03\0\x1awasi:cli/environment@0.2.9\x05\0\x01B\x03\x01j\0\0\x01@\x01\x06sta\ +tus\0\x01\0\x04\0\x04exit\x01\x01\x03\0\x13wasi:cli/exit@0.2.9\x05\x01\x01B\x04\x04\ +\0\x05error\x03\x01\x01h\0\x01@\x01\x04self\x01\0s\x04\0\x1d[method]error.to-deb\ +ug-string\x01\x02\x03\0\x13wasi:io/error@0.2.9\x05\x02\x01B\x0a\x04\0\x08pollabl\ +e\x03\x01\x01h\0\x01@\x01\x04self\x01\0\x7f\x04\0\x16[method]pollable.ready\x01\x02\ +\x01@\x01\x04self\x01\x01\0\x04\0\x16[method]pollable.block\x01\x03\x01p\x01\x01\ +py\x01@\x01\x02in\x04\0\x05\x04\0\x04poll\x01\x06\x03\0\x12wasi:io/poll@0.2.9\x05\ +\x03\x02\x03\0\x02\x05error\x02\x03\0\x03\x08pollable\x01B(\x02\x03\x02\x01\x04\x04\ +\0\x05error\x03\0\0\x02\x03\x02\x01\x05\x04\0\x08pollable\x03\0\x02\x01i\x01\x01\ +q\x02\x15last-operation-failed\x01\x04\0\x06closed\0\0\x04\0\x0cstream-error\x03\ +\0\x05\x04\0\x0cinput-stream\x03\x01\x04\0\x0doutput-stream\x03\x01\x01h\x07\x01\ +p}\x01j\x01\x0a\x01\x06\x01@\x02\x04self\x09\x03lenw\0\x0b\x04\0\x19[method]inpu\ +t-stream.read\x01\x0c\x04\0\"[method]input-stream.blocking-read\x01\x0c\x01j\x01\ +w\x01\x06\x01@\x02\x04self\x09\x03lenw\0\x0d\x04\0\x19[method]input-stream.skip\x01\ +\x0e\x04\0\"[method]input-stream.blocking-skip\x01\x0e\x01i\x03\x01@\x01\x04self\ +\x09\0\x0f\x04\0\x1e[method]input-stream.subscribe\x01\x10\x01h\x08\x01@\x01\x04\ +self\x11\0\x0d\x04\0![method]output-stream.check-write\x01\x12\x01j\0\x01\x06\x01\ +@\x02\x04self\x11\x08contents\x0a\0\x13\x04\0\x1b[method]output-stream.write\x01\ +\x14\x04\0.[method]output-stream.blocking-write-and-flush\x01\x14\x01@\x01\x04se\ +lf\x11\0\x13\x04\0\x1b[method]output-stream.flush\x01\x15\x04\0$[method]output-s\ +tream.blocking-flush\x01\x15\x01@\x01\x04self\x11\0\x0f\x04\0\x1f[method]output-\ +stream.subscribe\x01\x16\x01@\x02\x04self\x11\x03lenw\0\x13\x04\0\"[method]outpu\ +t-stream.write-zeroes\x01\x17\x04\05[method]output-stream.blocking-write-zeroes-\ +and-flush\x01\x17\x01@\x03\x04self\x11\x03src\x09\x03lenw\0\x0d\x04\0\x1c[method\ +]output-stream.splice\x01\x18\x04\0%[method]output-stream.blocking-splice\x01\x18\ +\x03\0\x15wasi:io/streams@0.2.9\x05\x06\x02\x03\0\x04\x0cinput-stream\x01B\x05\x02\ +\x03\x02\x01\x07\x04\0\x0cinput-stream\x03\0\0\x01i\x01\x01@\0\0\x02\x04\0\x09ge\ +t-stdin\x01\x03\x03\0\x14wasi:cli/stdin@0.2.9\x05\x08\x02\x03\0\x04\x0doutput-st\ +ream\x01B\x05\x02\x03\x02\x01\x09\x04\0\x0doutput-stream\x03\0\0\x01i\x01\x01@\0\ +\0\x02\x04\0\x0aget-stdout\x01\x03\x03\0\x15wasi:cli/stdout@0.2.9\x05\x0a\x01B\x05\ +\x02\x03\x02\x01\x09\x04\0\x0doutput-stream\x03\0\0\x01i\x01\x01@\0\0\x02\x04\0\x0a\ +get-stderr\x01\x03\x03\0\x15wasi:cli/stderr@0.2.9\x05\x0b\x01B\x01\x04\0\x0eterm\ +inal-input\x03\x01\x03\0\x1dwasi:cli/terminal-input@0.2.9\x05\x0c\x01B\x01\x04\0\ +\x0fterminal-output\x03\x01\x03\0\x1ewasi:cli/terminal-output@0.2.9\x05\x0d\x02\x03\ +\0\x08\x0eterminal-input\x01B\x06\x02\x03\x02\x01\x0e\x04\0\x0eterminal-input\x03\ +\0\0\x01i\x01\x01k\x02\x01@\0\0\x03\x04\0\x12get-terminal-stdin\x01\x04\x03\0\x1d\ +wasi:cli/terminal-stdin@0.2.9\x05\x0f\x02\x03\0\x09\x0fterminal-output\x01B\x06\x02\ +\x03\x02\x01\x10\x04\0\x0fterminal-output\x03\0\0\x01i\x01\x01k\x02\x01@\0\0\x03\ +\x04\0\x13get-terminal-stdout\x01\x04\x03\0\x1ewasi:cli/terminal-stdout@0.2.9\x05\ +\x11\x01B\x06\x02\x03\x02\x01\x10\x04\0\x0fterminal-output\x03\0\0\x01i\x01\x01k\ +\x02\x01@\0\0\x03\x04\0\x13get-terminal-stderr\x01\x04\x03\0\x1ewasi:cli/termina\ +l-stderr@0.2.9\x05\x12\x01B\x0f\x02\x03\x02\x01\x05\x04\0\x08pollable\x03\0\0\x01\ +w\x04\0\x07instant\x03\0\x02\x01w\x04\0\x08duration\x03\0\x04\x01@\0\0\x03\x04\0\ +\x03now\x01\x06\x01@\0\0\x05\x04\0\x0aresolution\x01\x07\x01i\x01\x01@\x01\x04wh\ +en\x03\0\x08\x04\0\x11subscribe-instant\x01\x09\x01@\x01\x04when\x05\0\x08\x04\0\ +\x12subscribe-duration\x01\x0a\x03\0!wasi:clocks/monotonic-clock@0.2.9\x05\x13\x01\ +B\x05\x01r\x02\x07secondsw\x0bnanosecondsy\x04\0\x08datetime\x03\0\0\x01@\0\0\x01\ +\x04\0\x03now\x01\x02\x04\0\x0aresolution\x01\x02\x03\0\x1cwasi:clocks/wall-cloc\ +k@0.2.9\x05\x14\x02\x03\0\x04\x05error\x02\x03\0\x0e\x08datetime\x01Br\x02\x03\x02\ +\x01\x07\x04\0\x0cinput-stream\x03\0\0\x02\x03\x02\x01\x09\x04\0\x0doutput-strea\ +m\x03\0\x02\x02\x03\x02\x01\x15\x04\0\x05error\x03\0\x04\x02\x03\x02\x01\x16\x04\ +\0\x08datetime\x03\0\x06\x01w\x04\0\x08filesize\x03\0\x08\x01m\x08\x07unknown\x0c\ +block-device\x10character-device\x09directory\x04fifo\x0dsymbolic-link\x0cregula\ +r-file\x06socket\x04\0\x0fdescriptor-type\x03\0\x0a\x01n\x06\x04read\x05write\x13\ +file-integrity-sync\x13data-integrity-sync\x14requested-write-sync\x10mutate-dir\ +ectory\x04\0\x10descriptor-flags\x03\0\x0c\x01n\x01\x0esymlink-follow\x04\0\x0ap\ +ath-flags\x03\0\x0e\x01n\x04\x06create\x09directory\x09exclusive\x08truncate\x04\ +\0\x0aopen-flags\x03\0\x10\x01w\x04\0\x0alink-count\x03\0\x12\x01k\x07\x01r\x06\x04\ +type\x0b\x0alink-count\x13\x04size\x09\x15data-access-timestamp\x14\x1bdata-modi\ +fication-timestamp\x14\x17status-change-timestamp\x14\x04\0\x0fdescriptor-stat\x03\ +\0\x15\x01q\x03\x09no-change\0\0\x03now\0\0\x09timestamp\x01\x07\0\x04\0\x0dnew-\ +timestamp\x03\0\x17\x01r\x02\x04type\x0b\x04names\x04\0\x0fdirectory-entry\x03\0\ +\x19\x01m%\x06access\x0bwould-block\x07already\x0ebad-descriptor\x04busy\x08dead\ +lock\x05quota\x05exist\x0efile-too-large\x15illegal-byte-sequence\x0bin-progress\ +\x0binterrupted\x07invalid\x02io\x0cis-directory\x04loop\x0etoo-many-links\x0cme\ +ssage-size\x0dname-too-long\x09no-device\x08no-entry\x07no-lock\x13insufficient-\ +memory\x12insufficient-space\x0dnot-directory\x09not-empty\x0fnot-recoverable\x0b\ +unsupported\x06no-tty\x0eno-such-device\x08overflow\x0dnot-permitted\x04pipe\x09\ +read-only\x0cinvalid-seek\x0etext-file-busy\x0ccross-device\x04\0\x0aerror-code\x03\ +\0\x1b\x01m\x06\x06normal\x0asequential\x06random\x09will-need\x09dont-need\x08n\ +o-reuse\x04\0\x06advice\x03\0\x1d\x01r\x02\x05lowerw\x05upperw\x04\0\x13metadata\ +-hash-value\x03\0\x1f\x04\0\x0adescriptor\x03\x01\x04\0\x16directory-entry-strea\ +m\x03\x01\x01h!\x01i\x01\x01j\x01$\x01\x1c\x01@\x02\x04self#\x06offset\x09\0%\x04\ +\0\"[method]descriptor.read-via-stream\x01&\x01i\x03\x01j\x01'\x01\x1c\x01@\x02\x04\ +self#\x06offset\x09\0(\x04\0#[method]descriptor.write-via-stream\x01)\x01@\x01\x04\ +self#\0(\x04\0$[method]descriptor.append-via-stream\x01*\x01j\0\x01\x1c\x01@\x04\ +\x04self#\x06offset\x09\x06length\x09\x06advice\x1e\0+\x04\0\x19[method]descript\ +or.advise\x01,\x01@\x01\x04self#\0+\x04\0\x1c[method]descriptor.sync-data\x01-\x01\ +j\x01\x0d\x01\x1c\x01@\x01\x04self#\0.\x04\0\x1c[method]descriptor.get-flags\x01\ +/\x01j\x01\x0b\x01\x1c\x01@\x01\x04self#\00\x04\0\x1b[method]descriptor.get-type\ +\x011\x01@\x02\x04self#\x04size\x09\0+\x04\0\x1b[method]descriptor.set-size\x012\ +\x01@\x03\x04self#\x15data-access-timestamp\x18\x1bdata-modification-timestamp\x18\ +\0+\x04\0\x1c[method]descriptor.set-times\x013\x01p}\x01o\x024\x7f\x01j\x015\x01\ +\x1c\x01@\x03\x04self#\x06length\x09\x06offset\x09\06\x04\0\x17[method]descripto\ +r.read\x017\x01j\x01\x09\x01\x1c\x01@\x03\x04self#\x06buffer4\x06offset\x09\08\x04\ +\0\x18[method]descriptor.write\x019\x01i\"\x01j\x01:\x01\x1c\x01@\x01\x04self#\0\ +;\x04\0![method]descriptor.read-directory\x01<\x04\0\x17[method]descriptor.sync\x01\ +-\x01@\x02\x04self#\x04paths\0+\x04\0&[method]descriptor.create-directory-at\x01\ +=\x01j\x01\x16\x01\x1c\x01@\x01\x04self#\0>\x04\0\x17[method]descriptor.stat\x01\ +?\x01@\x03\x04self#\x0apath-flags\x0f\x04paths\0>\x04\0\x1a[method]descriptor.st\ +at-at\x01@\x01@\x05\x04self#\x0apath-flags\x0f\x04paths\x15data-access-timestamp\ +\x18\x1bdata-modification-timestamp\x18\0+\x04\0\x1f[method]descriptor.set-times\ +-at\x01A\x01@\x05\x04self#\x0eold-path-flags\x0f\x08old-paths\x0enew-descriptor#\ +\x08new-paths\0+\x04\0\x1a[method]descriptor.link-at\x01B\x01i!\x01j\x01\xc3\0\x01\ +\x1c\x01@\x05\x04self#\x0apath-flags\x0f\x04paths\x0aopen-flags\x11\x05flags\x0d\ +\0\xc4\0\x04\0\x1a[method]descriptor.open-at\x01E\x01j\x01s\x01\x1c\x01@\x02\x04\ +self#\x04paths\0\xc6\0\x04\0\x1e[method]descriptor.readlink-at\x01G\x04\0&[metho\ +d]descriptor.remove-directory-at\x01=\x01@\x04\x04self#\x08old-paths\x0enew-desc\ +riptor#\x08new-paths\0+\x04\0\x1c[method]descriptor.rename-at\x01H\x01@\x03\x04s\ +elf#\x08old-paths\x08new-paths\0+\x04\0\x1d[method]descriptor.symlink-at\x01I\x04\ +\0![method]descriptor.unlink-file-at\x01=\x01@\x02\x04self#\x05other#\0\x7f\x04\0\ +![method]descriptor.is-same-object\x01J\x01j\x01\x20\x01\x1c\x01@\x01\x04self#\0\ +\xcb\0\x04\0\x20[method]descriptor.metadata-hash\x01L\x01@\x03\x04self#\x0apath-\ +flags\x0f\x04paths\0\xcb\0\x04\0#[method]descriptor.metadata-hash-at\x01M\x01h\"\ +\x01k\x1a\x01j\x01\xcf\0\x01\x1c\x01@\x01\x04self\xce\0\0\xd0\0\x04\03[method]di\ +rectory-entry-stream.read-directory-entry\x01Q\x01h\x05\x01k\x1c\x01@\x01\x03err\ +\xd2\0\0\xd3\0\x04\0\x15filesystem-error-code\x01T\x03\0\x1bwasi:filesystem/type\ +s@0.2.9\x05\x17\x02\x03\0\x0f\x0adescriptor\x01B\x07\x02\x03\x02\x01\x18\x04\0\x0a\ +descriptor\x03\0\0\x01i\x01\x01o\x02\x02s\x01p\x03\x01@\0\0\x04\x04\0\x0fget-dir\ +ectories\x01\x05\x03\0\x1ewasi:filesystem/preopens@0.2.9\x05\x19\x01B\x11\x04\0\x07\ +network\x03\x01\x01m\x15\x07unknown\x0daccess-denied\x0dnot-supported\x10invalid\ +-argument\x0dout-of-memory\x07timeout\x14concurrency-conflict\x0fnot-in-progress\ +\x0bwould-block\x0dinvalid-state\x10new-socket-limit\x14address-not-bindable\x0e\ +address-in-use\x12remote-unreachable\x12connection-refused\x10connection-reset\x12\ +connection-aborted\x12datagram-too-large\x11name-unresolvable\x1atemporary-resol\ +ver-failure\x1apermanent-resolver-failure\x04\0\x0aerror-code\x03\0\x01\x01m\x02\ +\x04ipv4\x04ipv6\x04\0\x11ip-address-family\x03\0\x03\x01o\x04}}}}\x04\0\x0cipv4\ +-address\x03\0\x05\x01o\x08{{{{{{{{\x04\0\x0cipv6-address\x03\0\x07\x01q\x02\x04\ +ipv4\x01\x06\0\x04ipv6\x01\x08\0\x04\0\x0aip-address\x03\0\x09\x01r\x02\x04port{\ +\x07address\x06\x04\0\x13ipv4-socket-address\x03\0\x0b\x01r\x04\x04port{\x09flow\ +-infoy\x07address\x08\x08scope-idy\x04\0\x13ipv6-socket-address\x03\0\x0d\x01q\x02\ +\x04ipv4\x01\x0c\0\x04ipv6\x01\x0e\0\x04\0\x11ip-socket-address\x03\0\x0f\x03\0\x1a\ +wasi:sockets/network@0.2.9\x05\x1a\x02\x03\0\x11\x07network\x01B\x05\x02\x03\x02\ +\x01\x1b\x04\0\x07network\x03\0\0\x01i\x01\x01@\0\0\x02\x04\0\x10instance-networ\ +k\x01\x03\x03\0#wasi:sockets/instance-network@0.2.9\x05\x1c\x02\x03\0\x11\x0aerr\ +or-code\x02\x03\0\x11\x11ip-socket-address\x02\x03\0\x11\x11ip-address-family\x01\ +BD\x02\x03\x02\x01\x05\x04\0\x08pollable\x03\0\0\x02\x03\x02\x01\x1b\x04\0\x07ne\ +twork\x03\0\x02\x02\x03\x02\x01\x1d\x04\0\x0aerror-code\x03\0\x04\x02\x03\x02\x01\ +\x1e\x04\0\x11ip-socket-address\x03\0\x06\x02\x03\x02\x01\x1f\x04\0\x11ip-addres\ +s-family\x03\0\x08\x01p}\x01r\x02\x04data\x0a\x0eremote-address\x07\x04\0\x11inc\ +oming-datagram\x03\0\x0b\x01k\x07\x01r\x02\x04data\x0a\x0eremote-address\x0d\x04\ +\0\x11outgoing-datagram\x03\0\x0e\x04\0\x0audp-socket\x03\x01\x04\0\x18incoming-\ +datagram-stream\x03\x01\x04\0\x18outgoing-datagram-stream\x03\x01\x01h\x10\x01h\x03\ +\x01j\0\x01\x05\x01@\x03\x04self\x13\x07network\x14\x0dlocal-address\x07\0\x15\x04\ +\0\x1d[method]udp-socket.start-bind\x01\x16\x01@\x01\x04self\x13\0\x15\x04\0\x1e\ +[method]udp-socket.finish-bind\x01\x17\x01i\x11\x01i\x12\x01o\x02\x18\x19\x01j\x01\ +\x1a\x01\x05\x01@\x02\x04self\x13\x0eremote-address\x0d\0\x1b\x04\0\x19[method]u\ +dp-socket.stream\x01\x1c\x01j\x01\x07\x01\x05\x01@\x01\x04self\x13\0\x1d\x04\0\x20\ +[method]udp-socket.local-address\x01\x1e\x04\0![method]udp-socket.remote-address\ +\x01\x1e\x01@\x01\x04self\x13\0\x09\x04\0![method]udp-socket.address-family\x01\x1f\ +\x01j\x01}\x01\x05\x01@\x01\x04self\x13\0\x20\x04\0$[method]udp-socket.unicast-h\ +op-limit\x01!\x01@\x02\x04self\x13\x05value}\0\x15\x04\0([method]udp-socket.set-\ +unicast-hop-limit\x01\"\x01j\x01w\x01\x05\x01@\x01\x04self\x13\0#\x04\0&[method]\ +udp-socket.receive-buffer-size\x01$\x01@\x02\x04self\x13\x05valuew\0\x15\x04\0*[\ +method]udp-socket.set-receive-buffer-size\x01%\x04\0#[method]udp-socket.send-buf\ +fer-size\x01$\x04\0'[method]udp-socket.set-send-buffer-size\x01%\x01i\x01\x01@\x01\ +\x04self\x13\0&\x04\0\x1c[method]udp-socket.subscribe\x01'\x01h\x11\x01p\x0c\x01\ +j\x01)\x01\x05\x01@\x02\x04self(\x0bmax-resultsw\0*\x04\0([method]incoming-datag\ +ram-stream.receive\x01+\x01@\x01\x04self(\0&\x04\0*[method]incoming-datagram-str\ +eam.subscribe\x01,\x01h\x12\x01@\x01\x04self-\0#\x04\0+[method]outgoing-datagram\ +-stream.check-send\x01.\x01p\x0f\x01@\x02\x04self-\x09datagrams/\0#\x04\0%[metho\ +d]outgoing-datagram-stream.send\x010\x01@\x01\x04self-\0&\x04\0*[method]outgoing\ +-datagram-stream.subscribe\x011\x03\0\x16wasi:sockets/udp@0.2.9\x05\x20\x02\x03\0\ +\x13\x0audp-socket\x01B\x0c\x02\x03\x02\x01\x1b\x04\0\x07network\x03\0\0\x02\x03\ +\x02\x01\x1d\x04\0\x0aerror-code\x03\0\x02\x02\x03\x02\x01\x1f\x04\0\x11ip-addre\ +ss-family\x03\0\x04\x02\x03\x02\x01!\x04\0\x0audp-socket\x03\0\x06\x01i\x07\x01j\ +\x01\x08\x01\x03\x01@\x01\x0eaddress-family\x05\0\x09\x04\0\x11create-udp-socket\ +\x01\x0a\x03\0$wasi:sockets/udp-create-socket@0.2.9\x05\"\x02\x03\0\x0d\x08durat\ +ion\x01BT\x02\x03\x02\x01\x07\x04\0\x0cinput-stream\x03\0\0\x02\x03\x02\x01\x09\x04\ +\0\x0doutput-stream\x03\0\x02\x02\x03\x02\x01\x05\x04\0\x08pollable\x03\0\x04\x02\ +\x03\x02\x01#\x04\0\x08duration\x03\0\x06\x02\x03\x02\x01\x1b\x04\0\x07network\x03\ +\0\x08\x02\x03\x02\x01\x1d\x04\0\x0aerror-code\x03\0\x0a\x02\x03\x02\x01\x1e\x04\ +\0\x11ip-socket-address\x03\0\x0c\x02\x03\x02\x01\x1f\x04\0\x11ip-address-family\ +\x03\0\x0e\x01m\x03\x07receive\x04send\x04both\x04\0\x0dshutdown-type\x03\0\x10\x04\ +\0\x0atcp-socket\x03\x01\x01h\x12\x01h\x09\x01j\0\x01\x0b\x01@\x03\x04self\x13\x07\ +network\x14\x0dlocal-address\x0d\0\x15\x04\0\x1d[method]tcp-socket.start-bind\x01\ +\x16\x01@\x01\x04self\x13\0\x15\x04\0\x1e[method]tcp-socket.finish-bind\x01\x17\x01\ +@\x03\x04self\x13\x07network\x14\x0eremote-address\x0d\0\x15\x04\0\x20[method]tc\ +p-socket.start-connect\x01\x18\x01i\x01\x01i\x03\x01o\x02\x19\x1a\x01j\x01\x1b\x01\ +\x0b\x01@\x01\x04self\x13\0\x1c\x04\0![method]tcp-socket.finish-connect\x01\x1d\x04\ +\0\x1f[method]tcp-socket.start-listen\x01\x17\x04\0\x20[method]tcp-socket.finish\ +-listen\x01\x17\x01i\x12\x01o\x03\x1e\x19\x1a\x01j\x01\x1f\x01\x0b\x01@\x01\x04s\ +elf\x13\0\x20\x04\0\x19[method]tcp-socket.accept\x01!\x01j\x01\x0d\x01\x0b\x01@\x01\ +\x04self\x13\0\"\x04\0\x20[method]tcp-socket.local-address\x01#\x04\0![method]tc\ +p-socket.remote-address\x01#\x01@\x01\x04self\x13\0\x7f\x04\0\x1f[method]tcp-soc\ +ket.is-listening\x01$\x01@\x01\x04self\x13\0\x0f\x04\0![method]tcp-socket.addres\ +s-family\x01%\x01@\x02\x04self\x13\x05valuew\0\x15\x04\0*[method]tcp-socket.set-\ +listen-backlog-size\x01&\x01j\x01\x7f\x01\x0b\x01@\x01\x04self\x13\0'\x04\0%[met\ +hod]tcp-socket.keep-alive-enabled\x01(\x01@\x02\x04self\x13\x05value\x7f\0\x15\x04\ +\0)[method]tcp-socket.set-keep-alive-enabled\x01)\x01j\x01\x07\x01\x0b\x01@\x01\x04\ +self\x13\0*\x04\0'[method]tcp-socket.keep-alive-idle-time\x01+\x01@\x02\x04self\x13\ +\x05value\x07\0\x15\x04\0+[method]tcp-socket.set-keep-alive-idle-time\x01,\x04\0\ +&[method]tcp-socket.keep-alive-interval\x01+\x04\0*[method]tcp-socket.set-keep-a\ +live-interval\x01,\x01j\x01y\x01\x0b\x01@\x01\x04self\x13\0-\x04\0#[method]tcp-s\ +ocket.keep-alive-count\x01.\x01@\x02\x04self\x13\x05valuey\0\x15\x04\0'[method]t\ +cp-socket.set-keep-alive-count\x01/\x01j\x01}\x01\x0b\x01@\x01\x04self\x13\00\x04\ +\0\x1c[method]tcp-socket.hop-limit\x011\x01@\x02\x04self\x13\x05value}\0\x15\x04\ +\0\x20[method]tcp-socket.set-hop-limit\x012\x01j\x01w\x01\x0b\x01@\x01\x04self\x13\ +\03\x04\0&[method]tcp-socket.receive-buffer-size\x014\x04\0*[method]tcp-socket.s\ +et-receive-buffer-size\x01&\x04\0#[method]tcp-socket.send-buffer-size\x014\x04\0\ +'[method]tcp-socket.set-send-buffer-size\x01&\x01i\x05\x01@\x01\x04self\x13\05\x04\ +\0\x1c[method]tcp-socket.subscribe\x016\x01@\x02\x04self\x13\x0dshutdown-type\x11\ +\0\x15\x04\0\x1b[method]tcp-socket.shutdown\x017\x03\0\x16wasi:sockets/tcp@0.2.9\ +\x05$\x02\x03\0\x15\x0atcp-socket\x01B\x0c\x02\x03\x02\x01\x1b\x04\0\x07network\x03\ +\0\0\x02\x03\x02\x01\x1d\x04\0\x0aerror-code\x03\0\x02\x02\x03\x02\x01\x1f\x04\0\ +\x11ip-address-family\x03\0\x04\x02\x03\x02\x01%\x04\0\x0atcp-socket\x03\0\x06\x01\ +i\x07\x01j\x01\x08\x01\x03\x01@\x01\x0eaddress-family\x05\0\x09\x04\0\x11create-\ +tcp-socket\x01\x0a\x03\0$wasi:sockets/tcp-create-socket@0.2.9\x05&\x02\x03\0\x11\ +\x0aip-address\x01B\x16\x02\x03\x02\x01\x05\x04\0\x08pollable\x03\0\0\x02\x03\x02\ +\x01\x1b\x04\0\x07network\x03\0\x02\x02\x03\x02\x01\x1d\x04\0\x0aerror-code\x03\0\ +\x04\x02\x03\x02\x01'\x04\0\x0aip-address\x03\0\x06\x04\0\x16resolve-address-str\ +eam\x03\x01\x01h\x08\x01k\x07\x01j\x01\x0a\x01\x05\x01@\x01\x04self\x09\0\x0b\x04\ +\03[method]resolve-address-stream.resolve-next-address\x01\x0c\x01i\x01\x01@\x01\ +\x04self\x09\0\x0d\x04\0([method]resolve-address-stream.subscribe\x01\x0e\x01h\x03\ +\x01i\x08\x01j\x01\x10\x01\x05\x01@\x02\x07network\x0f\x04names\0\x11\x04\0\x11r\ +esolve-addresses\x01\x12\x03\0!wasi:sockets/ip-name-lookup@0.2.9\x05(\x01B\x05\x01\ +p}\x01@\x01\x03lenw\0\0\x04\0\x10get-random-bytes\x01\x01\x01@\0\0w\x04\0\x0eget\ +-random-u64\x01\x02\x03\0\x18wasi:random/random@0.2.9\x05)\x01B\x05\x01p}\x01@\x01\ +\x03lenw\0\0\x04\0\x19get-insecure-random-bytes\x01\x01\x01@\0\0w\x04\0\x17get-i\ +nsecure-random-u64\x01\x02\x03\0\x1awasi:random/insecure@0.2.9\x05*\x01B\x03\x01\ +o\x02ww\x01@\0\0\0\x04\0\x0dinsecure-seed\x01\x01\x03\0\x1fwasi:random/insecure-\ +seed@0.2.9\x05+\x04\06wasi:cli/command-with-all-of-its-exports-removed@0.2.9\x04\ +\0\x0b-\x01\0'command-with-all-of-its-exports-removed\x03\0\0\0G\x09producers\x01\ +\x0cprocessed-by\x02\x0dwit-component\x070.244.0\x10wit-bindgen-rust\x060.51.0"; +#[inline(never)] +#[doc(hidden)] +pub fn __link_custom_section_describing_imports() { + wit_bindgen::rt::maybe_link_cabi_realloc(); +} diff --git a/tools/vendor/wasip2/src/ext/mod.rs b/tools/vendor/wasip2/src/ext/mod.rs new file mode 100644 index 0000000000..b581f22d1a --- /dev/null +++ b/tools/vendor/wasip2/src/ext/mod.rs @@ -0,0 +1,8 @@ +#[cfg(feature = "std")] +mod std; + +impl core::fmt::Display for crate::io::error::Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.write_str(&self.to_debug_string()) + } +} diff --git a/tools/vendor/wasip2/src/ext/std.rs b/tools/vendor/wasip2/src/ext/std.rs new file mode 100644 index 0000000000..bd0178d03f --- /dev/null +++ b/tools/vendor/wasip2/src/ext/std.rs @@ -0,0 +1,69 @@ +use std::error::Error; +use std::io; +use std::num::NonZeroU64; + +use crate::io::streams::StreamError; + +impl Error for crate::io::error::Error {} + +impl io::Read for crate::io::streams::InputStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let n = buf + .len() + .try_into() + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + match self.blocking_read(n) { + Ok(chunk) => { + let n = chunk.len(); + if n > buf.len() { + return Err(io::Error::new( + io::ErrorKind::Other, + "more bytes read than requested", + )); + } + buf[..n].copy_from_slice(&chunk); + Ok(n) + } + Err(StreamError::Closed) => Ok(0), + Err(StreamError::LastOperationFailed(e)) => { + Err(io::Error::new(io::ErrorKind::Other, e.to_debug_string())) + } + } + } +} + +impl io::Write for crate::io::streams::OutputStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + let n = loop { + match self.check_write().map(NonZeroU64::new) { + Ok(Some(n)) => { + break n; + } + Ok(None) => { + self.subscribe().block(); + } + Err(StreamError::Closed) => return Ok(0), + Err(StreamError::LastOperationFailed(e)) => { + return Err(io::Error::new(io::ErrorKind::Other, e.to_debug_string())) + } + }; + }; + let n = n + .get() + .try_into() + .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + let n = buf.len().min(n); + crate::io::streams::OutputStream::write(self, &buf[..n]).map_err(|e| match e { + StreamError::Closed => io::ErrorKind::UnexpectedEof.into(), + StreamError::LastOperationFailed(e) => { + io::Error::new(io::ErrorKind::Other, e.to_debug_string()) + } + })?; + Ok(n) + } + + fn flush(&mut self) -> io::Result<()> { + self.blocking_flush() + .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) + } +} diff --git a/tools/vendor/wasip2/src/imports.rs b/tools/vendor/wasip2/src/imports.rs new file mode 100644 index 0000000000..bf732b7c8c --- /dev/null +++ b/tools/vendor/wasip2/src/imports.rs @@ -0,0 +1,10225 @@ +// Generated by `wit-bindgen` 0.51.0. DO NOT EDIT! +// Options used: +// * std_feature +// * type_section_suffix: "rust-wasip2-1.0.2+wasi-0.2.9-from-crates-io" +#[rustfmt::skip] +#[allow(dead_code, clippy::all)] +pub mod wasi { + pub mod cli { + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod environment { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + #[allow(unused_unsafe, clippy::all)] + /// Get the POSIX-style environment variables. + /// + /// Each environment variable is provided as a pair of string variable names + /// and string value. + /// + /// Morally, these are a value import, but until value imports are available + /// in the component model, this import function should return the same + /// values each time it is called. + #[allow(async_fn_in_trait)] + pub fn get_environment() -> _rt::Vec<(_rt::String, _rt::String)> { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 2 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:cli/environment@0.2.9")] + unsafe extern "C" { + #[link_name = "get-environment"] + fn wit_import1(_: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: *mut u8) { + unreachable!() + } + wit_import1(ptr0); + let l2 = *ptr0.add(0).cast::<*mut u8>(); + let l3 = *ptr0 + .add(::core::mem::size_of::<*const u8>()) + .cast::(); + let base10 = l2; + let len10 = l3; + let mut result10 = _rt::Vec::with_capacity(len10); + for i in 0..len10 { + let base = base10 + .add(i * (4 * ::core::mem::size_of::<*const u8>())); + let e10 = { + let l4 = *base.add(0).cast::<*mut u8>(); + let l5 = *base + .add(::core::mem::size_of::<*const u8>()) + .cast::(); + let len6 = l5; + let bytes6 = _rt::Vec::from_raw_parts(l4.cast(), len6, len6); + let l7 = *base + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l8 = *base + .add(3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len9 = l8; + let bytes9 = _rt::Vec::from_raw_parts(l7.cast(), len9, len9); + (_rt::string_lift(bytes6), _rt::string_lift(bytes9)) + }; + result10.push(e10); + } + _rt::cabi_dealloc( + base10, + len10 * (4 * ::core::mem::size_of::<*const u8>()), + ::core::mem::size_of::<*const u8>(), + ); + let result11 = result10; + result11 + } + } + #[allow(unused_unsafe, clippy::all)] + /// Get the POSIX-style arguments to the program. + #[allow(async_fn_in_trait)] + pub fn get_arguments() -> _rt::Vec<_rt::String> { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 2 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:cli/environment@0.2.9")] + unsafe extern "C" { + #[link_name = "get-arguments"] + fn wit_import1(_: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: *mut u8) { + unreachable!() + } + wit_import1(ptr0); + let l2 = *ptr0.add(0).cast::<*mut u8>(); + let l3 = *ptr0 + .add(::core::mem::size_of::<*const u8>()) + .cast::(); + let base7 = l2; + let len7 = l3; + let mut result7 = _rt::Vec::with_capacity(len7); + for i in 0..len7 { + let base = base7 + .add(i * (2 * ::core::mem::size_of::<*const u8>())); + let e7 = { + let l4 = *base.add(0).cast::<*mut u8>(); + let l5 = *base + .add(::core::mem::size_of::<*const u8>()) + .cast::(); + let len6 = l5; + let bytes6 = _rt::Vec::from_raw_parts(l4.cast(), len6, len6); + _rt::string_lift(bytes6) + }; + result7.push(e7); + } + _rt::cabi_dealloc( + base7, + len7 * (2 * ::core::mem::size_of::<*const u8>()), + ::core::mem::size_of::<*const u8>(), + ); + let result8 = result7; + result8 + } + } + #[allow(unused_unsafe, clippy::all)] + /// Return a path that programs should use as their initial current working + /// directory, interpreting `.` as shorthand for this. + #[allow(async_fn_in_trait)] + pub fn initial_cwd() -> Option<_rt::String> { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 3 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 3 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:cli/environment@0.2.9")] + unsafe extern "C" { + #[link_name = "initial-cwd"] + fn wit_import1(_: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: *mut u8) { + unreachable!() + } + wit_import1(ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result6 = match l2 { + 0 => None, + 1 => { + let e = { + let l3 = *ptr0 + .add(::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l4 = *ptr0 + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len5 = l4; + let bytes5 = _rt::Vec::from_raw_parts( + l3.cast(), + len5, + len5, + ); + _rt::string_lift(bytes5) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result6 + } + } + } + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod exit { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + #[allow(unused_unsafe, clippy::all)] + /// Exit the current instance and any linked instances. + #[allow(async_fn_in_trait)] + pub fn exit(status: Result<(), ()>) -> () { + unsafe { + let result0 = match status { + Ok(_) => 0i32, + Err(_) => 1i32, + }; + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:cli/exit@0.2.9")] + unsafe extern "C" { + #[link_name = "exit"] + fn wit_import1(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32) { + unreachable!() + } + wit_import1(result0); + } + } + } + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod stdin { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + pub type InputStream = super::super::super::wasi::io::streams::InputStream; + #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] + pub fn get_stdin() -> InputStream { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:cli/stdin@0.2.9")] + unsafe extern "C" { + #[link_name = "get-stdin"] + fn wit_import0() -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0() -> i32 { + unreachable!() + } + let ret = wit_import0(); + super::super::super::wasi::io::streams::InputStream::from_handle( + ret as u32, + ) + } + } + } + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod stdout { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + pub type OutputStream = super::super::super::wasi::io::streams::OutputStream; + #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] + pub fn get_stdout() -> OutputStream { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:cli/stdout@0.2.9")] + unsafe extern "C" { + #[link_name = "get-stdout"] + fn wit_import0() -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0() -> i32 { + unreachable!() + } + let ret = wit_import0(); + super::super::super::wasi::io::streams::OutputStream::from_handle( + ret as u32, + ) + } + } + } + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod stderr { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + pub type OutputStream = super::super::super::wasi::io::streams::OutputStream; + #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] + pub fn get_stderr() -> OutputStream { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:cli/stderr@0.2.9")] + unsafe extern "C" { + #[link_name = "get-stderr"] + fn wit_import0() -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0() -> i32 { + unreachable!() + } + let ret = wit_import0(); + super::super::super::wasi::io::streams::OutputStream::from_handle( + ret as u32, + ) + } + } + } + /// Terminal input. + /// + /// In the future, this may include functions for disabling echoing, + /// disabling input buffering so that keyboard events are sent through + /// immediately, querying supported features, and so on. + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod terminal_input { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + /// The input side of a terminal. + #[derive(Debug)] + #[repr(transparent)] + pub struct TerminalInput { + handle: _rt::Resource, + } + impl TerminalInput { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for TerminalInput { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:cli/terminal-input@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]terminal-input"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + } + /// Terminal output. + /// + /// In the future, this may include functions for querying the terminal + /// size, being notified of terminal size changes, querying supported + /// features, and so on. + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod terminal_output { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + /// The output side of a terminal. + #[derive(Debug)] + #[repr(transparent)] + pub struct TerminalOutput { + handle: _rt::Resource, + } + impl TerminalOutput { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for TerminalOutput { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:cli/terminal-output@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]terminal-output"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + } + /// An interface providing an optional `terminal-input` for stdin as a + /// link-time authority. + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod terminal_stdin { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + pub type TerminalInput = super::super::super::wasi::cli::terminal_input::TerminalInput; + #[allow(unused_unsafe, clippy::all)] + /// If stdin is connected to a terminal, return a `terminal-input` handle + /// allowing further interaction with it. + #[allow(async_fn_in_trait)] + pub fn get_terminal_stdin() -> Option { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 8]); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:cli/terminal-stdin@0.2.9")] + unsafe extern "C" { + #[link_name = "get-terminal-stdin"] + fn wit_import1(_: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: *mut u8) { + unreachable!() + } + wit_import1(ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => None, + 1 => { + let e = { + let l3 = *ptr0.add(4).cast::(); + super::super::super::wasi::cli::terminal_input::TerminalInput::from_handle( + l3 as u32, + ) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + /// An interface providing an optional `terminal-output` for stdout as a + /// link-time authority. + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod terminal_stdout { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + pub type TerminalOutput = super::super::super::wasi::cli::terminal_output::TerminalOutput; + #[allow(unused_unsafe, clippy::all)] + /// If stdout is connected to a terminal, return a `terminal-output` handle + /// allowing further interaction with it. + #[allow(async_fn_in_trait)] + pub fn get_terminal_stdout() -> Option { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 8]); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:cli/terminal-stdout@0.2.9")] + unsafe extern "C" { + #[link_name = "get-terminal-stdout"] + fn wit_import1(_: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: *mut u8) { + unreachable!() + } + wit_import1(ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => None, + 1 => { + let e = { + let l3 = *ptr0.add(4).cast::(); + super::super::super::wasi::cli::terminal_output::TerminalOutput::from_handle( + l3 as u32, + ) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + /// An interface providing an optional `terminal-output` for stderr as a + /// link-time authority. + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod terminal_stderr { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + pub type TerminalOutput = super::super::super::wasi::cli::terminal_output::TerminalOutput; + #[allow(unused_unsafe, clippy::all)] + /// If stderr is connected to a terminal, return a `terminal-output` handle + /// allowing further interaction with it. + #[allow(async_fn_in_trait)] + pub fn get_terminal_stderr() -> Option { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 8]); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:cli/terminal-stderr@0.2.9")] + unsafe extern "C" { + #[link_name = "get-terminal-stderr"] + fn wit_import1(_: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: *mut u8) { + unreachable!() + } + wit_import1(ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => None, + 1 => { + let e = { + let l3 = *ptr0.add(4).cast::(); + super::super::super::wasi::cli::terminal_output::TerminalOutput::from_handle( + l3 as u32, + ) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + } + pub mod clocks { + /// WASI Monotonic Clock is a clock API intended to let users measure elapsed + /// time. + /// + /// It is intended to be portable at least between Unix-family platforms and + /// Windows. + /// + /// A monotonic clock is a clock which has an unspecified initial value, and + /// successive reads of the clock will produce non-decreasing values. + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod monotonic_clock { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + pub type Pollable = super::super::super::wasi::io::poll::Pollable; + /// An instant in time, in nanoseconds. An instant is relative to an + /// unspecified initial value, and can only be compared to instances from + /// the same monotonic-clock. + pub type Instant = u64; + /// A duration of time, in nanoseconds. + pub type Duration = u64; + #[allow(unused_unsafe, clippy::all)] + /// Read the current value of the clock. + /// + /// The clock is monotonic, therefore calling this function repeatedly will + /// produce a sequence of non-decreasing values. + /// + /// For completeness, this function traps if it's not possible to represent + /// the value of the clock in an `instant`. Consequently, implementations + /// should ensure that the starting time is low enough to avoid the + /// possibility of overflow in practice. + #[allow(async_fn_in_trait)] + pub fn now() -> Instant { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:clocks/monotonic-clock@0.2.9")] + unsafe extern "C" { + #[link_name = "now"] + fn wit_import0() -> i64; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0() -> i64 { + unreachable!() + } + let ret = wit_import0(); + ret as u64 + } + } + #[allow(unused_unsafe, clippy::all)] + /// Query the resolution of the clock. Returns the duration of time + /// corresponding to a clock tick. + #[allow(async_fn_in_trait)] + pub fn resolution() -> Duration { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:clocks/monotonic-clock@0.2.9")] + unsafe extern "C" { + #[link_name = "resolution"] + fn wit_import0() -> i64; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0() -> i64 { + unreachable!() + } + let ret = wit_import0(); + ret as u64 + } + } + #[allow(unused_unsafe, clippy::all)] + /// Create a `pollable` which will resolve once the specified instant + /// has occurred. + #[allow(async_fn_in_trait)] + pub fn subscribe_instant(when: Instant) -> Pollable { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:clocks/monotonic-clock@0.2.9")] + unsafe extern "C" { + #[link_name = "subscribe-instant"] + fn wit_import0(_: i64) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i64) -> i32 { + unreachable!() + } + let ret = wit_import0(_rt::as_i64(when)); + super::super::super::wasi::io::poll::Pollable::from_handle( + ret as u32, + ) + } + } + #[allow(unused_unsafe, clippy::all)] + /// Create a `pollable` that will resolve after the specified duration has + /// elapsed from the time this function is invoked. + #[allow(async_fn_in_trait)] + pub fn subscribe_duration(when: Duration) -> Pollable { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:clocks/monotonic-clock@0.2.9")] + unsafe extern "C" { + #[link_name = "subscribe-duration"] + fn wit_import0(_: i64) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i64) -> i32 { + unreachable!() + } + let ret = wit_import0(_rt::as_i64(when)); + super::super::super::wasi::io::poll::Pollable::from_handle( + ret as u32, + ) + } + } + } + /// WASI Wall Clock is a clock API intended to let users query the current + /// time. The name "wall" makes an analogy to a "clock on the wall", which + /// is not necessarily monotonic as it may be reset. + /// + /// It is intended to be portable at least between Unix-family platforms and + /// Windows. + /// + /// A wall clock is a clock which measures the date and time according to + /// some external reference. + /// + /// External references may be reset, so this clock is not necessarily + /// monotonic, making it unsuitable for measuring elapsed time. + /// + /// It is intended for reporting the current date and time for humans. + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod wall_clock { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + /// A time and date in seconds plus nanoseconds. + #[repr(C)] + #[derive(Clone, Copy)] + pub struct Datetime { + pub seconds: u64, + pub nanoseconds: u32, + } + impl ::core::fmt::Debug for Datetime { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + f.debug_struct("Datetime") + .field("seconds", &self.seconds) + .field("nanoseconds", &self.nanoseconds) + .finish() + } + } + #[allow(unused_unsafe, clippy::all)] + /// Read the current value of the clock. + /// + /// This clock is not monotonic, therefore calling this function repeatedly + /// will not necessarily produce a sequence of non-decreasing values. + /// + /// The returned timestamps represent the number of seconds since + /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], + /// also known as [Unix Time]. + /// + /// The nanoseconds field of the output is always less than 1000000000. + /// + /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 + /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time + #[allow(async_fn_in_trait)] + pub fn now() -> Datetime { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 16]); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:clocks/wall-clock@0.2.9")] + unsafe extern "C" { + #[link_name = "now"] + fn wit_import1(_: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: *mut u8) { + unreachable!() + } + wit_import1(ptr0); + let l2 = *ptr0.add(0).cast::(); + let l3 = *ptr0.add(8).cast::(); + let result4 = Datetime { + seconds: l2 as u64, + nanoseconds: l3 as u32, + }; + result4 + } + } + #[allow(unused_unsafe, clippy::all)] + /// Query the resolution of the clock. + /// + /// The nanoseconds field of the output is always less than 1000000000. + #[allow(async_fn_in_trait)] + pub fn resolution() -> Datetime { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 16]); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:clocks/wall-clock@0.2.9")] + unsafe extern "C" { + #[link_name = "resolution"] + fn wit_import1(_: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: *mut u8) { + unreachable!() + } + wit_import1(ptr0); + let l2 = *ptr0.add(0).cast::(); + let l3 = *ptr0.add(8).cast::(); + let result4 = Datetime { + seconds: l2 as u64, + nanoseconds: l3 as u32, + }; + result4 + } + } + } + } + pub mod filesystem { + /// WASI filesystem is a filesystem API primarily intended to let users run WASI + /// programs that access their files on their existing filesystems, without + /// significant overhead. + /// + /// It is intended to be roughly portable between Unix-family platforms and + /// Windows, though it does not hide many of the major differences. + /// + /// Paths are passed as interface-type `string`s, meaning they must consist of + /// a sequence of Unicode Scalar Values (USVs). Some filesystems may contain + /// paths which are not accessible by this API. + /// + /// The directory separator in WASI is always the forward-slash (`/`). + /// + /// All paths in WASI are relative paths, and are interpreted relative to a + /// `descriptor` referring to a base directory. If a `path` argument to any WASI + /// function starts with `/`, or if any step of resolving a `path`, including + /// `..` and symbolic link steps, reaches a directory outside of the base + /// directory, or reaches a symlink to an absolute or rooted path in the + /// underlying filesystem, the function fails with `error-code::not-permitted`. + /// + /// For more information about WASI path resolution and sandboxing, see + /// [WASI filesystem path resolution]. + /// + /// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod types { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + pub type InputStream = super::super::super::wasi::io::streams::InputStream; + pub type OutputStream = super::super::super::wasi::io::streams::OutputStream; + pub type Error = super::super::super::wasi::io::streams::Error; + pub type Datetime = super::super::super::wasi::clocks::wall_clock::Datetime; + /// File size or length of a region within a file. + pub type Filesize = u64; + /// The type of a filesystem object referenced by a descriptor. + /// + /// Note: This was called `filetype` in earlier versions of WASI. + #[repr(u8)] + #[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] + pub enum DescriptorType { + /// The type of the descriptor or file is unknown or is different from + /// any of the other types specified. + Unknown, + /// The descriptor refers to a block device inode. + BlockDevice, + /// The descriptor refers to a character device inode. + CharacterDevice, + /// The descriptor refers to a directory inode. + Directory, + /// The descriptor refers to a named pipe. + Fifo, + /// The file refers to a symbolic link inode. + SymbolicLink, + /// The descriptor refers to a regular file inode. + RegularFile, + /// The descriptor refers to a socket. + Socket, + } + impl ::core::fmt::Debug for DescriptorType { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + match self { + DescriptorType::Unknown => { + f.debug_tuple("DescriptorType::Unknown").finish() + } + DescriptorType::BlockDevice => { + f.debug_tuple("DescriptorType::BlockDevice").finish() + } + DescriptorType::CharacterDevice => { + f.debug_tuple("DescriptorType::CharacterDevice").finish() + } + DescriptorType::Directory => { + f.debug_tuple("DescriptorType::Directory").finish() + } + DescriptorType::Fifo => { + f.debug_tuple("DescriptorType::Fifo").finish() + } + DescriptorType::SymbolicLink => { + f.debug_tuple("DescriptorType::SymbolicLink").finish() + } + DescriptorType::RegularFile => { + f.debug_tuple("DescriptorType::RegularFile").finish() + } + DescriptorType::Socket => { + f.debug_tuple("DescriptorType::Socket").finish() + } + } + } + } + impl DescriptorType { + #[doc(hidden)] + pub unsafe fn _lift(val: u8) -> DescriptorType { + if !cfg!(debug_assertions) { + return unsafe { ::core::mem::transmute(val) }; + } + match val { + 0 => DescriptorType::Unknown, + 1 => DescriptorType::BlockDevice, + 2 => DescriptorType::CharacterDevice, + 3 => DescriptorType::Directory, + 4 => DescriptorType::Fifo, + 5 => DescriptorType::SymbolicLink, + 6 => DescriptorType::RegularFile, + 7 => DescriptorType::Socket, + _ => panic!("invalid enum discriminant"), + } + } + } + wit_bindgen::rt::bitflags::bitflags! { + #[doc = " Descriptor flags."] #[doc = ""] #[doc = + " Note: This was called `fdflags` in earlier versions of WASI."] + #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] pub + struct DescriptorFlags : u8 { #[doc = " Read mode: Data can be read."] + const READ = 1 << 0; #[doc = " Write mode: Data can be written to."] + const WRITE = 1 << 1; #[doc = + " Request that writes be performed according to synchronized I/O file"] + #[doc = + " integrity completion. The data stored in the file and the file's"] + #[doc = + " metadata are synchronized. This is similar to `O_SYNC` in POSIX."] + #[doc = ""] #[doc = + " The precise semantics of this operation have not yet been defined for"] + #[doc = + " WASI. At this time, it should be interpreted as a request, and not a"] + #[doc = " requirement."] const FILE_INTEGRITY_SYNC = 1 << 2; #[doc = + " Request that writes be performed according to synchronized I/O data"] + #[doc = " integrity completion. Only the data stored in the file is"] + #[doc = " synchronized. This is similar to `O_DSYNC` in POSIX."] #[doc = + ""] #[doc = + " The precise semantics of this operation have not yet been defined for"] + #[doc = + " WASI. At this time, it should be interpreted as a request, and not a"] + #[doc = " requirement."] const DATA_INTEGRITY_SYNC = 1 << 3; #[doc = + " Requests that reads be performed at the same level of integrity"] #[doc + = " requested for writes. This is similar to `O_RSYNC` in POSIX."] #[doc + = ""] #[doc = + " The precise semantics of this operation have not yet been defined for"] + #[doc = + " WASI. At this time, it should be interpreted as a request, and not a"] + #[doc = " requirement."] const REQUESTED_WRITE_SYNC = 1 << 4; #[doc = + " Mutating directories mode: Directory contents may be mutated."] #[doc = + ""] #[doc = + " When this flag is unset on a descriptor, operations using the"] #[doc = + " descriptor which would create, rename, delete, modify the data or"] + #[doc = + " metadata of filesystem objects, or obtain another handle which"] #[doc + = + " would permit any of those, shall fail with `error-code::read-only` if"] + #[doc = " they would otherwise succeed."] #[doc = ""] #[doc = + " This may only be set on directories."] const MUTATE_DIRECTORY = 1 << 5; + } + } + wit_bindgen::rt::bitflags::bitflags! { + #[doc = " Flags determining the method of how paths are resolved."] + #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] pub + struct PathFlags : u8 { #[doc = + " As long as the resolved path corresponds to a symbolic link, it is"] + #[doc = " expanded."] const SYMLINK_FOLLOW = 1 << 0; } + } + wit_bindgen::rt::bitflags::bitflags! { + #[doc = " Open flags used by `open-at`."] #[derive(PartialEq, Eq, + PartialOrd, Ord, Hash, Debug, Clone, Copy)] pub struct OpenFlags : u8 { + #[doc = + " Create file if it does not exist, similar to `O_CREAT` in POSIX."] + const CREATE = 1 << 0; #[doc = + " Fail if not a directory, similar to `O_DIRECTORY` in POSIX."] const + DIRECTORY = 1 << 1; #[doc = + " Fail if file already exists, similar to `O_EXCL` in POSIX."] const + EXCLUSIVE = 1 << 2; #[doc = + " Truncate file to size 0, similar to `O_TRUNC` in POSIX."] const + TRUNCATE = 1 << 3; } + } + /// Number of hard links to an inode. + pub type LinkCount = u64; + /// File attributes. + /// + /// Note: This was called `filestat` in earlier versions of WASI. + #[repr(C)] + #[derive(Clone, Copy)] + pub struct DescriptorStat { + /// File type. + pub type_: DescriptorType, + /// Number of hard links to the file. + pub link_count: LinkCount, + /// For regular files, the file size in bytes. For symbolic links, the + /// length in bytes of the pathname contained in the symbolic link. + pub size: Filesize, + /// Last data access timestamp. + /// + /// If the `option` is none, the platform doesn't maintain an access + /// timestamp for this file. + pub data_access_timestamp: Option, + /// Last data modification timestamp. + /// + /// If the `option` is none, the platform doesn't maintain a + /// modification timestamp for this file. + pub data_modification_timestamp: Option, + /// Last file status-change timestamp. + /// + /// If the `option` is none, the platform doesn't maintain a + /// status-change timestamp for this file. + pub status_change_timestamp: Option, + } + impl ::core::fmt::Debug for DescriptorStat { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + f.debug_struct("DescriptorStat") + .field("type", &self.type_) + .field("link-count", &self.link_count) + .field("size", &self.size) + .field("data-access-timestamp", &self.data_access_timestamp) + .field( + "data-modification-timestamp", + &self.data_modification_timestamp, + ) + .field("status-change-timestamp", &self.status_change_timestamp) + .finish() + } + } + /// When setting a timestamp, this gives the value to set it to. + #[derive(Clone, Copy)] + pub enum NewTimestamp { + /// Leave the timestamp set to its previous value. + NoChange, + /// Set the timestamp to the current time of the system clock associated + /// with the filesystem. + Now, + /// Set the timestamp to the given value. + Timestamp(Datetime), + } + impl ::core::fmt::Debug for NewTimestamp { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + match self { + NewTimestamp::NoChange => { + f.debug_tuple("NewTimestamp::NoChange").finish() + } + NewTimestamp::Now => f.debug_tuple("NewTimestamp::Now").finish(), + NewTimestamp::Timestamp(e) => { + f.debug_tuple("NewTimestamp::Timestamp").field(e).finish() + } + } + } + } + /// A directory entry. + #[derive(Clone)] + pub struct DirectoryEntry { + /// The type of the file referred to by this directory entry. + pub type_: DescriptorType, + /// The name of the object. + pub name: _rt::String, + } + impl ::core::fmt::Debug for DirectoryEntry { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + f.debug_struct("DirectoryEntry") + .field("type", &self.type_) + .field("name", &self.name) + .finish() + } + } + /// Error codes returned by functions, similar to `errno` in POSIX. + /// Not all of these error codes are returned by the functions provided by this + /// API; some are used in higher-level library layers, and others are provided + /// merely for alignment with POSIX. + #[repr(u8)] + #[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] + pub enum ErrorCode { + /// Permission denied, similar to `EACCES` in POSIX. + Access, + /// Resource unavailable, or operation would block, similar to `EAGAIN` and `EWOULDBLOCK` in POSIX. + WouldBlock, + /// Connection already in progress, similar to `EALREADY` in POSIX. + Already, + /// Bad descriptor, similar to `EBADF` in POSIX. + BadDescriptor, + /// Device or resource busy, similar to `EBUSY` in POSIX. + Busy, + /// Resource deadlock would occur, similar to `EDEADLK` in POSIX. + Deadlock, + /// Storage quota exceeded, similar to `EDQUOT` in POSIX. + Quota, + /// File exists, similar to `EEXIST` in POSIX. + Exist, + /// File too large, similar to `EFBIG` in POSIX. + FileTooLarge, + /// Illegal byte sequence, similar to `EILSEQ` in POSIX. + IllegalByteSequence, + /// Operation in progress, similar to `EINPROGRESS` in POSIX. + InProgress, + /// Interrupted function, similar to `EINTR` in POSIX. + Interrupted, + /// Invalid argument, similar to `EINVAL` in POSIX. + Invalid, + /// I/O error, similar to `EIO` in POSIX. + Io, + /// Is a directory, similar to `EISDIR` in POSIX. + IsDirectory, + /// Too many levels of symbolic links, similar to `ELOOP` in POSIX. + Loop, + /// Too many links, similar to `EMLINK` in POSIX. + TooManyLinks, + /// Message too large, similar to `EMSGSIZE` in POSIX. + MessageSize, + /// Filename too long, similar to `ENAMETOOLONG` in POSIX. + NameTooLong, + /// No such device, similar to `ENODEV` in POSIX. + NoDevice, + /// No such file or directory, similar to `ENOENT` in POSIX. + NoEntry, + /// No locks available, similar to `ENOLCK` in POSIX. + NoLock, + /// Not enough space, similar to `ENOMEM` in POSIX. + InsufficientMemory, + /// No space left on device, similar to `ENOSPC` in POSIX. + InsufficientSpace, + /// Not a directory or a symbolic link to a directory, similar to `ENOTDIR` in POSIX. + NotDirectory, + /// Directory not empty, similar to `ENOTEMPTY` in POSIX. + NotEmpty, + /// State not recoverable, similar to `ENOTRECOVERABLE` in POSIX. + NotRecoverable, + /// Not supported, similar to `ENOTSUP` and `ENOSYS` in POSIX. + Unsupported, + /// Inappropriate I/O control operation, similar to `ENOTTY` in POSIX. + NoTty, + /// No such device or address, similar to `ENXIO` in POSIX. + NoSuchDevice, + /// Value too large to be stored in data type, similar to `EOVERFLOW` in POSIX. + Overflow, + /// Operation not permitted, similar to `EPERM` in POSIX. + NotPermitted, + /// Broken pipe, similar to `EPIPE` in POSIX. + Pipe, + /// Read-only file system, similar to `EROFS` in POSIX. + ReadOnly, + /// Invalid seek, similar to `ESPIPE` in POSIX. + InvalidSeek, + /// Text file busy, similar to `ETXTBSY` in POSIX. + TextFileBusy, + /// Cross-device link, similar to `EXDEV` in POSIX. + CrossDevice, + } + impl ErrorCode { + pub fn name(&self) -> &'static str { + match self { + ErrorCode::Access => "access", + ErrorCode::WouldBlock => "would-block", + ErrorCode::Already => "already", + ErrorCode::BadDescriptor => "bad-descriptor", + ErrorCode::Busy => "busy", + ErrorCode::Deadlock => "deadlock", + ErrorCode::Quota => "quota", + ErrorCode::Exist => "exist", + ErrorCode::FileTooLarge => "file-too-large", + ErrorCode::IllegalByteSequence => "illegal-byte-sequence", + ErrorCode::InProgress => "in-progress", + ErrorCode::Interrupted => "interrupted", + ErrorCode::Invalid => "invalid", + ErrorCode::Io => "io", + ErrorCode::IsDirectory => "is-directory", + ErrorCode::Loop => "loop", + ErrorCode::TooManyLinks => "too-many-links", + ErrorCode::MessageSize => "message-size", + ErrorCode::NameTooLong => "name-too-long", + ErrorCode::NoDevice => "no-device", + ErrorCode::NoEntry => "no-entry", + ErrorCode::NoLock => "no-lock", + ErrorCode::InsufficientMemory => "insufficient-memory", + ErrorCode::InsufficientSpace => "insufficient-space", + ErrorCode::NotDirectory => "not-directory", + ErrorCode::NotEmpty => "not-empty", + ErrorCode::NotRecoverable => "not-recoverable", + ErrorCode::Unsupported => "unsupported", + ErrorCode::NoTty => "no-tty", + ErrorCode::NoSuchDevice => "no-such-device", + ErrorCode::Overflow => "overflow", + ErrorCode::NotPermitted => "not-permitted", + ErrorCode::Pipe => "pipe", + ErrorCode::ReadOnly => "read-only", + ErrorCode::InvalidSeek => "invalid-seek", + ErrorCode::TextFileBusy => "text-file-busy", + ErrorCode::CrossDevice => "cross-device", + } + } + pub fn message(&self) -> &'static str { + match self { + ErrorCode::Access => { + "Permission denied, similar to `EACCES` in POSIX." + } + ErrorCode::WouldBlock => { + "Resource unavailable, or operation would block, similar to `EAGAIN` and `EWOULDBLOCK` in POSIX." + } + ErrorCode::Already => { + "Connection already in progress, similar to `EALREADY` in POSIX." + } + ErrorCode::BadDescriptor => { + "Bad descriptor, similar to `EBADF` in POSIX." + } + ErrorCode::Busy => { + "Device or resource busy, similar to `EBUSY` in POSIX." + } + ErrorCode::Deadlock => { + "Resource deadlock would occur, similar to `EDEADLK` in POSIX." + } + ErrorCode::Quota => { + "Storage quota exceeded, similar to `EDQUOT` in POSIX." + } + ErrorCode::Exist => "File exists, similar to `EEXIST` in POSIX.", + ErrorCode::FileTooLarge => { + "File too large, similar to `EFBIG` in POSIX." + } + ErrorCode::IllegalByteSequence => { + "Illegal byte sequence, similar to `EILSEQ` in POSIX." + } + ErrorCode::InProgress => { + "Operation in progress, similar to `EINPROGRESS` in POSIX." + } + ErrorCode::Interrupted => { + "Interrupted function, similar to `EINTR` in POSIX." + } + ErrorCode::Invalid => { + "Invalid argument, similar to `EINVAL` in POSIX." + } + ErrorCode::Io => "I/O error, similar to `EIO` in POSIX.", + ErrorCode::IsDirectory => { + "Is a directory, similar to `EISDIR` in POSIX." + } + ErrorCode::Loop => { + "Too many levels of symbolic links, similar to `ELOOP` in POSIX." + } + ErrorCode::TooManyLinks => { + "Too many links, similar to `EMLINK` in POSIX." + } + ErrorCode::MessageSize => { + "Message too large, similar to `EMSGSIZE` in POSIX." + } + ErrorCode::NameTooLong => { + "Filename too long, similar to `ENAMETOOLONG` in POSIX." + } + ErrorCode::NoDevice => { + "No such device, similar to `ENODEV` in POSIX." + } + ErrorCode::NoEntry => { + "No such file or directory, similar to `ENOENT` in POSIX." + } + ErrorCode::NoLock => { + "No locks available, similar to `ENOLCK` in POSIX." + } + ErrorCode::InsufficientMemory => { + "Not enough space, similar to `ENOMEM` in POSIX." + } + ErrorCode::InsufficientSpace => { + "No space left on device, similar to `ENOSPC` in POSIX." + } + ErrorCode::NotDirectory => { + "Not a directory or a symbolic link to a directory, similar to `ENOTDIR` in POSIX." + } + ErrorCode::NotEmpty => { + "Directory not empty, similar to `ENOTEMPTY` in POSIX." + } + ErrorCode::NotRecoverable => { + "State not recoverable, similar to `ENOTRECOVERABLE` in POSIX." + } + ErrorCode::Unsupported => { + "Not supported, similar to `ENOTSUP` and `ENOSYS` in POSIX." + } + ErrorCode::NoTty => { + "Inappropriate I/O control operation, similar to `ENOTTY` in POSIX." + } + ErrorCode::NoSuchDevice => { + "No such device or address, similar to `ENXIO` in POSIX." + } + ErrorCode::Overflow => { + "Value too large to be stored in data type, similar to `EOVERFLOW` in POSIX." + } + ErrorCode::NotPermitted => { + "Operation not permitted, similar to `EPERM` in POSIX." + } + ErrorCode::Pipe => "Broken pipe, similar to `EPIPE` in POSIX.", + ErrorCode::ReadOnly => { + "Read-only file system, similar to `EROFS` in POSIX." + } + ErrorCode::InvalidSeek => { + "Invalid seek, similar to `ESPIPE` in POSIX." + } + ErrorCode::TextFileBusy => { + "Text file busy, similar to `ETXTBSY` in POSIX." + } + ErrorCode::CrossDevice => { + "Cross-device link, similar to `EXDEV` in POSIX." + } + } + } + } + impl ::core::fmt::Debug for ErrorCode { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + f.debug_struct("ErrorCode") + .field("code", &(*self as i32)) + .field("name", &self.name()) + .field("message", &self.message()) + .finish() + } + } + impl ::core::fmt::Display for ErrorCode { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + write!(f, "{} (error {})", self.name(), * self as i32) + } + } + #[cfg(feature = "std")] + impl ::core::error::Error for ErrorCode {} + impl ErrorCode { + #[doc(hidden)] + pub unsafe fn _lift(val: u8) -> ErrorCode { + if !cfg!(debug_assertions) { + return unsafe { ::core::mem::transmute(val) }; + } + match val { + 0 => ErrorCode::Access, + 1 => ErrorCode::WouldBlock, + 2 => ErrorCode::Already, + 3 => ErrorCode::BadDescriptor, + 4 => ErrorCode::Busy, + 5 => ErrorCode::Deadlock, + 6 => ErrorCode::Quota, + 7 => ErrorCode::Exist, + 8 => ErrorCode::FileTooLarge, + 9 => ErrorCode::IllegalByteSequence, + 10 => ErrorCode::InProgress, + 11 => ErrorCode::Interrupted, + 12 => ErrorCode::Invalid, + 13 => ErrorCode::Io, + 14 => ErrorCode::IsDirectory, + 15 => ErrorCode::Loop, + 16 => ErrorCode::TooManyLinks, + 17 => ErrorCode::MessageSize, + 18 => ErrorCode::NameTooLong, + 19 => ErrorCode::NoDevice, + 20 => ErrorCode::NoEntry, + 21 => ErrorCode::NoLock, + 22 => ErrorCode::InsufficientMemory, + 23 => ErrorCode::InsufficientSpace, + 24 => ErrorCode::NotDirectory, + 25 => ErrorCode::NotEmpty, + 26 => ErrorCode::NotRecoverable, + 27 => ErrorCode::Unsupported, + 28 => ErrorCode::NoTty, + 29 => ErrorCode::NoSuchDevice, + 30 => ErrorCode::Overflow, + 31 => ErrorCode::NotPermitted, + 32 => ErrorCode::Pipe, + 33 => ErrorCode::ReadOnly, + 34 => ErrorCode::InvalidSeek, + 35 => ErrorCode::TextFileBusy, + 36 => ErrorCode::CrossDevice, + _ => panic!("invalid enum discriminant"), + } + } + } + /// File or memory access pattern advisory information. + #[repr(u8)] + #[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] + pub enum Advice { + /// The application has no advice to give on its behavior with respect + /// to the specified data. + Normal, + /// The application expects to access the specified data sequentially + /// from lower offsets to higher offsets. + Sequential, + /// The application expects to access the specified data in a random + /// order. + Random, + /// The application expects to access the specified data in the near + /// future. + WillNeed, + /// The application expects that it will not access the specified data + /// in the near future. + DontNeed, + /// The application expects to access the specified data once and then + /// not reuse it thereafter. + NoReuse, + } + impl ::core::fmt::Debug for Advice { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + match self { + Advice::Normal => f.debug_tuple("Advice::Normal").finish(), + Advice::Sequential => { + f.debug_tuple("Advice::Sequential").finish() + } + Advice::Random => f.debug_tuple("Advice::Random").finish(), + Advice::WillNeed => f.debug_tuple("Advice::WillNeed").finish(), + Advice::DontNeed => f.debug_tuple("Advice::DontNeed").finish(), + Advice::NoReuse => f.debug_tuple("Advice::NoReuse").finish(), + } + } + } + impl Advice { + #[doc(hidden)] + pub unsafe fn _lift(val: u8) -> Advice { + if !cfg!(debug_assertions) { + return unsafe { ::core::mem::transmute(val) }; + } + match val { + 0 => Advice::Normal, + 1 => Advice::Sequential, + 2 => Advice::Random, + 3 => Advice::WillNeed, + 4 => Advice::DontNeed, + 5 => Advice::NoReuse, + _ => panic!("invalid enum discriminant"), + } + } + } + /// A 128-bit hash value, split into parts because wasm doesn't have a + /// 128-bit integer type. + #[repr(C)] + #[derive(Clone, Copy)] + pub struct MetadataHashValue { + /// 64 bits of a 128-bit hash value. + pub lower: u64, + /// Another 64 bits of a 128-bit hash value. + pub upper: u64, + } + impl ::core::fmt::Debug for MetadataHashValue { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + f.debug_struct("MetadataHashValue") + .field("lower", &self.lower) + .field("upper", &self.upper) + .finish() + } + } + /// A descriptor is a reference to a filesystem object, which may be a file, + /// directory, named pipe, special file, or other object on which filesystem + /// calls may be made. + #[derive(Debug)] + #[repr(transparent)] + pub struct Descriptor { + handle: _rt::Resource, + } + impl Descriptor { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for Descriptor { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]descriptor"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + /// A stream of directory entries. + #[derive(Debug)] + #[repr(transparent)] + pub struct DirectoryEntryStream { + handle: _rt::Resource, + } + impl DirectoryEntryStream { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for DirectoryEntryStream { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]directory-entry-stream"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Return a stream for reading from a file, if available. + /// + /// May fail with an error-code describing why the file cannot be read. + /// + /// Multiple read, write, and append streams may be active on the same open + /// file and they do not interfere with each other. + /// + /// Note: This allows using `read-stream`, which is similar to `read` in POSIX. + #[allow(async_fn_in_trait)] + pub fn read_via_stream( + &self, + offset: Filesize, + ) -> Result { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 8], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.read-via-stream"] + fn wit_import1(_: i32, _: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i64, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, _rt::as_i64(offset), ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result5 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(4).cast::(); + super::super::super::wasi::io::streams::InputStream::from_handle( + l3 as u32, + ) + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(4).cast::()); + ErrorCode::_lift(l4 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Return a stream for writing to a file, if available. + /// + /// May fail with an error-code describing why the file cannot be written. + /// + /// Note: This allows using `write-stream`, which is similar to `write` in + /// POSIX. + #[allow(async_fn_in_trait)] + pub fn write_via_stream( + &self, + offset: Filesize, + ) -> Result { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 8], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.write-via-stream"] + fn wit_import1(_: i32, _: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i64, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, _rt::as_i64(offset), ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result5 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(4).cast::(); + super::super::super::wasi::io::streams::OutputStream::from_handle( + l3 as u32, + ) + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(4).cast::()); + ErrorCode::_lift(l4 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Return a stream for appending to a file, if available. + /// + /// May fail with an error-code describing why the file cannot be appended. + /// + /// Note: This allows using `write-stream`, which is similar to `write` with + /// `O_APPEND` in POSIX. + #[allow(async_fn_in_trait)] + pub fn append_via_stream(&self) -> Result { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 8], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.append-via-stream"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result5 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(4).cast::(); + super::super::super::wasi::io::streams::OutputStream::from_handle( + l3 as u32, + ) + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(4).cast::()); + ErrorCode::_lift(l4 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Provide file advisory information on a descriptor. + /// + /// This is similar to `posix_fadvise` in POSIX. + #[allow(async_fn_in_trait)] + pub fn advise( + &self, + offset: Filesize, + length: Filesize, + advice: Advice, + ) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.advise"] + fn wit_import1(_: i32, _: i64, _: i64, _: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1( + _: i32, + _: i64, + _: i64, + _: i32, + _: *mut u8, + ) { + unreachable!() + } + wit_import1( + (self).handle() as i32, + _rt::as_i64(offset), + _rt::as_i64(length), + advice.clone() as i32, + ptr0, + ); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + ErrorCode::_lift(l3 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Synchronize the data of a file to disk. + /// + /// This function succeeds with no effect if the file descriptor is not + /// opened for writing. + /// + /// Note: This is similar to `fdatasync` in POSIX. + #[allow(async_fn_in_trait)] + pub fn sync_data(&self) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.sync-data"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + ErrorCode::_lift(l3 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Get flags associated with a descriptor. + /// + /// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX. + /// + /// Note: This returns the value that was the `fs_flags` value returned + /// from `fdstat_get` in earlier versions of WASI. + #[allow(async_fn_in_trait)] + pub fn get_flags(&self) -> Result { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.get-flags"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result5 = match l2 { + 0 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + DescriptorFlags::empty() + | DescriptorFlags::from_bits_retain(((l3 as u8) << 0) as _) + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(1).cast::()); + ErrorCode::_lift(l4 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Get the dynamic type of a descriptor. + /// + /// Note: This returns the same value as the `type` field of the `fd-stat` + /// returned by `stat`, `stat-at` and similar. + /// + /// Note: This returns similar flags to the `st_mode & S_IFMT` value provided + /// by `fstat` in POSIX. + /// + /// Note: This returns the value that was the `fs_filetype` value returned + /// from `fdstat_get` in earlier versions of WASI. + #[allow(async_fn_in_trait)] + pub fn get_type(&self) -> Result { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.get-type"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result5 = match l2 { + 0 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + DescriptorType::_lift(l3 as u8) + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(1).cast::()); + ErrorCode::_lift(l4 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Adjust the size of an open file. If this increases the file's size, the + /// extra bytes are filled with zeros. + /// + /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. + #[allow(async_fn_in_trait)] + pub fn set_size(&self, size: Filesize) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.set-size"] + fn wit_import1(_: i32, _: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i64, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, _rt::as_i64(size), ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + ErrorCode::_lift(l3 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Adjust the timestamps of an open file or directory. + /// + /// Note: This is similar to `futimens` in POSIX. + /// + /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI. + #[allow(async_fn_in_trait)] + pub fn set_times( + &self, + data_access_timestamp: NewTimestamp, + data_modification_timestamp: NewTimestamp, + ) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let (result1_0, result1_1, result1_2) = match data_access_timestamp { + NewTimestamp::NoChange => (0i32, 0i64, 0i32), + NewTimestamp::Now => (1i32, 0i64, 0i32), + NewTimestamp::Timestamp(e) => { + let super::super::super::wasi::clocks::wall_clock::Datetime { + seconds: seconds0, + nanoseconds: nanoseconds0, + } = e; + (2i32, _rt::as_i64(seconds0), _rt::as_i32(nanoseconds0)) + } + }; + let (result3_0, result3_1, result3_2) = match data_modification_timestamp { + NewTimestamp::NoChange => (0i32, 0i64, 0i32), + NewTimestamp::Now => (1i32, 0i64, 0i32), + NewTimestamp::Timestamp(e) => { + let super::super::super::wasi::clocks::wall_clock::Datetime { + seconds: seconds2, + nanoseconds: nanoseconds2, + } = e; + (2i32, _rt::as_i64(seconds2), _rt::as_i32(nanoseconds2)) + } + }; + let ptr4 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.set-times"] + fn wit_import5( + _: i32, + _: i32, + _: i64, + _: i32, + _: i32, + _: i64, + _: i32, + _: *mut u8, + ); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import5( + _: i32, + _: i32, + _: i64, + _: i32, + _: i32, + _: i64, + _: i32, + _: *mut u8, + ) { + unreachable!() + } + wit_import5( + (self).handle() as i32, + result1_0, + result1_1, + result1_2, + result3_0, + result3_1, + result3_2, + ptr4, + ); + let l6 = i32::from(*ptr4.add(0).cast::()); + let result8 = match l6 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l7 = i32::from(*ptr4.add(1).cast::()); + ErrorCode::_lift(l7 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result8 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Read from a descriptor, without using and updating the descriptor's offset. + /// + /// This function returns a list of bytes containing the data that was + /// read, along with a bool which, when true, indicates that the end of the + /// file was reached. The returned list will contain up to `length` bytes; it + /// may return fewer than requested, if the end of the file is reached or + /// if the I/O operation is interrupted. + /// + /// In the future, this may change to return a `stream`. + /// + /// Note: This is similar to `pread` in POSIX. + #[allow(async_fn_in_trait)] + pub fn read( + &self, + length: Filesize, + offset: Filesize, + ) -> Result<(_rt::Vec, bool), ErrorCode> { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 4 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 4 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.read"] + fn wit_import1(_: i32, _: i64, _: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1( + _: i32, + _: i64, + _: i64, + _: *mut u8, + ) { + unreachable!() + } + wit_import1( + (self).handle() as i32, + _rt::as_i64(length), + _rt::as_i64(offset), + ptr0, + ); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result8 = match l2 { + 0 => { + let e = { + let l3 = *ptr0 + .add(::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l4 = *ptr0 + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len5 = l4; + let l6 = i32::from( + *ptr0 + .add(3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + ( + <_ as From< + _rt::Vec<_>, + >>::from(_rt::Vec::from_raw_parts(l3.cast(), len5, len5)), + _rt::bool_lift(l6 as u8), + ) + }; + Ok(e) + } + 1 => { + let e = { + let l7 = i32::from( + *ptr0.add(::core::mem::size_of::<*const u8>()).cast::(), + ); + ErrorCode::_lift(l7 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result8 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Write to a descriptor, without using and updating the descriptor's offset. + /// + /// It is valid to write past the end of a file; the file is extended to the + /// extent of the write, with bytes between the previous end and the start of + /// the write set to zero. + /// + /// In the future, this may change to take a `stream`. + /// + /// Note: This is similar to `pwrite` in POSIX. + #[allow(async_fn_in_trait)] + pub fn write( + &self, + buffer: &[u8], + offset: Filesize, + ) -> Result { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 16], + ); + let vec0 = buffer; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let ptr1 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.write"] + fn wit_import2( + _: i32, + _: *mut u8, + _: usize, + _: i64, + _: *mut u8, + ); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import2( + _: i32, + _: *mut u8, + _: usize, + _: i64, + _: *mut u8, + ) { + unreachable!() + } + wit_import2( + (self).handle() as i32, + ptr0.cast_mut(), + len0, + _rt::as_i64(offset), + ptr1, + ); + let l3 = i32::from(*ptr1.add(0).cast::()); + let result6 = match l3 { + 0 => { + let e = { + let l4 = *ptr1.add(8).cast::(); + l4 as u64 + }; + Ok(e) + } + 1 => { + let e = { + let l5 = i32::from(*ptr1.add(8).cast::()); + ErrorCode::_lift(l5 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result6 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Read directory entries from a directory. + /// + /// On filesystems where directories contain entries referring to themselves + /// and their parents, often named `.` and `..` respectively, these entries + /// are omitted. + /// + /// This always returns a new stream which starts at the beginning of the + /// directory. Multiple streams may be active on the same directory, and they + /// do not interfere with each other. + #[allow(async_fn_in_trait)] + pub fn read_directory(&self) -> Result { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 8], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.read-directory"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result5 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(4).cast::(); + DirectoryEntryStream::from_handle(l3 as u32) + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(4).cast::()); + ErrorCode::_lift(l4 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Synchronize the data and metadata of a file to disk. + /// + /// This function succeeds with no effect if the file descriptor is not + /// opened for writing. + /// + /// Note: This is similar to `fsync` in POSIX. + #[allow(async_fn_in_trait)] + pub fn sync(&self) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.sync"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + ErrorCode::_lift(l3 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Create a directory. + /// + /// Note: This is similar to `mkdirat` in POSIX. + #[allow(async_fn_in_trait)] + pub fn create_directory_at(&self, path: &str) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let vec0 = path; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let ptr1 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.create-directory-at"] + fn wit_import2(_: i32, _: *mut u8, _: usize, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import2( + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import2((self).handle() as i32, ptr0.cast_mut(), len0, ptr1); + let l3 = i32::from(*ptr1.add(0).cast::()); + let result5 = match l3 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr1.add(1).cast::()); + ErrorCode::_lift(l4 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Return the attributes of an open file or directory. + /// + /// Note: This is similar to `fstat` in POSIX, except that it does not return + /// device and inode information. For testing whether two descriptors refer to + /// the same underlying filesystem object, use `is-same-object`. To obtain + /// additional data that can be used do determine whether a file has been + /// modified, use `metadata-hash`. + /// + /// Note: This was called `fd_filestat_get` in earlier versions of WASI. + #[allow(async_fn_in_trait)] + pub fn stat(&self) -> Result { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 104]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 104], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.stat"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result16 = match l2 { + 0 => { + let e = { + let l3 = i32::from(*ptr0.add(8).cast::()); + let l4 = *ptr0.add(16).cast::(); + let l5 = *ptr0.add(24).cast::(); + let l6 = i32::from(*ptr0.add(32).cast::()); + let l9 = i32::from(*ptr0.add(56).cast::()); + let l12 = i32::from(*ptr0.add(80).cast::()); + DescriptorStat { + type_: DescriptorType::_lift(l3 as u8), + link_count: l4 as u64, + size: l5 as u64, + data_access_timestamp: match l6 { + 0 => None, + 1 => { + let e = { + let l7 = *ptr0.add(40).cast::(); + let l8 = *ptr0.add(48).cast::(); + super::super::super::wasi::clocks::wall_clock::Datetime { + seconds: l7 as u64, + nanoseconds: l8 as u32, + } + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + data_modification_timestamp: match l9 { + 0 => None, + 1 => { + let e = { + let l10 = *ptr0.add(64).cast::(); + let l11 = *ptr0.add(72).cast::(); + super::super::super::wasi::clocks::wall_clock::Datetime { + seconds: l10 as u64, + nanoseconds: l11 as u32, + } + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + status_change_timestamp: match l12 { + 0 => None, + 1 => { + let e = { + let l13 = *ptr0.add(88).cast::(); + let l14 = *ptr0.add(96).cast::(); + super::super::super::wasi::clocks::wall_clock::Datetime { + seconds: l13 as u64, + nanoseconds: l14 as u32, + } + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + Ok(e) + } + 1 => { + let e = { + let l15 = i32::from(*ptr0.add(8).cast::()); + ErrorCode::_lift(l15 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result16 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Return the attributes of a file or directory. + /// + /// Note: This is similar to `fstatat` in POSIX, except that it does not + /// return device and inode information. See the `stat` description for a + /// discussion of alternatives. + /// + /// Note: This was called `path_filestat_get` in earlier versions of WASI. + #[allow(async_fn_in_trait)] + pub fn stat_at( + &self, + path_flags: PathFlags, + path: &str, + ) -> Result { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 104]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 104], + ); + let flags0 = path_flags; + let vec1 = path; + let ptr1 = vec1.as_ptr().cast::(); + let len1 = vec1.len(); + let ptr2 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.stat-at"] + fn wit_import3( + _: i32, + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import3( + _: i32, + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import3( + (self).handle() as i32, + (flags0.bits() >> 0) as i32, + ptr1.cast_mut(), + len1, + ptr2, + ); + let l4 = i32::from(*ptr2.add(0).cast::()); + let result18 = match l4 { + 0 => { + let e = { + let l5 = i32::from(*ptr2.add(8).cast::()); + let l6 = *ptr2.add(16).cast::(); + let l7 = *ptr2.add(24).cast::(); + let l8 = i32::from(*ptr2.add(32).cast::()); + let l11 = i32::from(*ptr2.add(56).cast::()); + let l14 = i32::from(*ptr2.add(80).cast::()); + DescriptorStat { + type_: DescriptorType::_lift(l5 as u8), + link_count: l6 as u64, + size: l7 as u64, + data_access_timestamp: match l8 { + 0 => None, + 1 => { + let e = { + let l9 = *ptr2.add(40).cast::(); + let l10 = *ptr2.add(48).cast::(); + super::super::super::wasi::clocks::wall_clock::Datetime { + seconds: l9 as u64, + nanoseconds: l10 as u32, + } + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + data_modification_timestamp: match l11 { + 0 => None, + 1 => { + let e = { + let l12 = *ptr2.add(64).cast::(); + let l13 = *ptr2.add(72).cast::(); + super::super::super::wasi::clocks::wall_clock::Datetime { + seconds: l12 as u64, + nanoseconds: l13 as u32, + } + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + status_change_timestamp: match l14 { + 0 => None, + 1 => { + let e = { + let l15 = *ptr2.add(88).cast::(); + let l16 = *ptr2.add(96).cast::(); + super::super::super::wasi::clocks::wall_clock::Datetime { + seconds: l15 as u64, + nanoseconds: l16 as u32, + } + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + Ok(e) + } + 1 => { + let e = { + let l17 = i32::from(*ptr2.add(8).cast::()); + ErrorCode::_lift(l17 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result18 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Adjust the timestamps of a file or directory. + /// + /// Note: This is similar to `utimensat` in POSIX. + /// + /// Note: This was called `path_filestat_set_times` in earlier versions of + /// WASI. + #[allow(async_fn_in_trait)] + pub fn set_times_at( + &self, + path_flags: PathFlags, + path: &str, + data_access_timestamp: NewTimestamp, + data_modification_timestamp: NewTimestamp, + ) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let flags0 = path_flags; + let vec1 = path; + let ptr1 = vec1.as_ptr().cast::(); + let len1 = vec1.len(); + let (result3_0, result3_1, result3_2) = match data_access_timestamp { + NewTimestamp::NoChange => (0i32, 0i64, 0i32), + NewTimestamp::Now => (1i32, 0i64, 0i32), + NewTimestamp::Timestamp(e) => { + let super::super::super::wasi::clocks::wall_clock::Datetime { + seconds: seconds2, + nanoseconds: nanoseconds2, + } = e; + (2i32, _rt::as_i64(seconds2), _rt::as_i32(nanoseconds2)) + } + }; + let (result5_0, result5_1, result5_2) = match data_modification_timestamp { + NewTimestamp::NoChange => (0i32, 0i64, 0i32), + NewTimestamp::Now => (1i32, 0i64, 0i32), + NewTimestamp::Timestamp(e) => { + let super::super::super::wasi::clocks::wall_clock::Datetime { + seconds: seconds4, + nanoseconds: nanoseconds4, + } = e; + (2i32, _rt::as_i64(seconds4), _rt::as_i32(nanoseconds4)) + } + }; + let ptr6 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.set-times-at"] + fn wit_import7( + _: i32, + _: i32, + _: *mut u8, + _: usize, + _: i32, + _: i64, + _: i32, + _: i32, + _: i64, + _: i32, + _: *mut u8, + ); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import7( + _: i32, + _: i32, + _: *mut u8, + _: usize, + _: i32, + _: i64, + _: i32, + _: i32, + _: i64, + _: i32, + _: *mut u8, + ) { + unreachable!() + } + wit_import7( + (self).handle() as i32, + (flags0.bits() >> 0) as i32, + ptr1.cast_mut(), + len1, + result3_0, + result3_1, + result3_2, + result5_0, + result5_1, + result5_2, + ptr6, + ); + let l8 = i32::from(*ptr6.add(0).cast::()); + let result10 = match l8 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l9 = i32::from(*ptr6.add(1).cast::()); + ErrorCode::_lift(l9 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result10 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Create a hard link. + /// + /// Fails with `error-code::no-entry` if the old path does not exist, + /// with `error-code::exist` if the new path already exists, and + /// `error-code::not-permitted` if the old path is not a file. + /// + /// Note: This is similar to `linkat` in POSIX. + #[allow(async_fn_in_trait)] + pub fn link_at( + &self, + old_path_flags: PathFlags, + old_path: &str, + new_descriptor: &Descriptor, + new_path: &str, + ) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let flags0 = old_path_flags; + let vec1 = old_path; + let ptr1 = vec1.as_ptr().cast::(); + let len1 = vec1.len(); + let vec2 = new_path; + let ptr2 = vec2.as_ptr().cast::(); + let len2 = vec2.len(); + let ptr3 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.link-at"] + fn wit_import4( + _: i32, + _: i32, + _: *mut u8, + _: usize, + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import4( + _: i32, + _: i32, + _: *mut u8, + _: usize, + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import4( + (self).handle() as i32, + (flags0.bits() >> 0) as i32, + ptr1.cast_mut(), + len1, + (new_descriptor).handle() as i32, + ptr2.cast_mut(), + len2, + ptr3, + ); + let l5 = i32::from(*ptr3.add(0).cast::()); + let result7 = match l5 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l6 = i32::from(*ptr3.add(1).cast::()); + ErrorCode::_lift(l6 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result7 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Open a file or directory. + /// + /// If `flags` contains `descriptor-flags::mutate-directory`, and the base + /// descriptor doesn't have `descriptor-flags::mutate-directory` set, + /// `open-at` fails with `error-code::read-only`. + /// + /// If `flags` contains `write` or `mutate-directory`, or `open-flags` + /// contains `truncate` or `create`, and the base descriptor doesn't have + /// `descriptor-flags::mutate-directory` set, `open-at` fails with + /// `error-code::read-only`. + /// + /// Note: This is similar to `openat` in POSIX. + #[allow(async_fn_in_trait)] + pub fn open_at( + &self, + path_flags: PathFlags, + path: &str, + open_flags: OpenFlags, + flags: DescriptorFlags, + ) -> Result { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 8], + ); + let flags0 = path_flags; + let vec1 = path; + let ptr1 = vec1.as_ptr().cast::(); + let len1 = vec1.len(); + let flags2 = open_flags; + let flags3 = flags; + let ptr4 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.open-at"] + fn wit_import5( + _: i32, + _: i32, + _: *mut u8, + _: usize, + _: i32, + _: i32, + _: *mut u8, + ); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import5( + _: i32, + _: i32, + _: *mut u8, + _: usize, + _: i32, + _: i32, + _: *mut u8, + ) { + unreachable!() + } + wit_import5( + (self).handle() as i32, + (flags0.bits() >> 0) as i32, + ptr1.cast_mut(), + len1, + (flags2.bits() >> 0) as i32, + (flags3.bits() >> 0) as i32, + ptr4, + ); + let l6 = i32::from(*ptr4.add(0).cast::()); + let result9 = match l6 { + 0 => { + let e = { + let l7 = *ptr4.add(4).cast::(); + Descriptor::from_handle(l7 as u32) + }; + Ok(e) + } + 1 => { + let e = { + let l8 = i32::from(*ptr4.add(4).cast::()); + ErrorCode::_lift(l8 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result9 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Read the contents of a symbolic link. + /// + /// If the contents contain an absolute or rooted path in the underlying + /// filesystem, this function fails with `error-code::not-permitted`. + /// + /// Note: This is similar to `readlinkat` in POSIX. + #[allow(async_fn_in_trait)] + pub fn readlink_at(&self, path: &str) -> Result<_rt::String, ErrorCode> { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 3 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 3 + * ::core::mem::size_of::<*const u8>()], + ); + let vec0 = path; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let ptr1 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.readlink-at"] + fn wit_import2(_: i32, _: *mut u8, _: usize, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import2( + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import2((self).handle() as i32, ptr0.cast_mut(), len0, ptr1); + let l3 = i32::from(*ptr1.add(0).cast::()); + let result8 = match l3 { + 0 => { + let e = { + let l4 = *ptr1 + .add(::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l5 = *ptr1 + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len6 = l5; + let bytes6 = _rt::Vec::from_raw_parts( + l4.cast(), + len6, + len6, + ); + _rt::string_lift(bytes6) + }; + Ok(e) + } + 1 => { + let e = { + let l7 = i32::from( + *ptr1.add(::core::mem::size_of::<*const u8>()).cast::(), + ); + ErrorCode::_lift(l7 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result8 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Remove a directory. + /// + /// Return `error-code::not-empty` if the directory is not empty. + /// + /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + #[allow(async_fn_in_trait)] + pub fn remove_directory_at(&self, path: &str) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let vec0 = path; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let ptr1 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.remove-directory-at"] + fn wit_import2(_: i32, _: *mut u8, _: usize, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import2( + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import2((self).handle() as i32, ptr0.cast_mut(), len0, ptr1); + let l3 = i32::from(*ptr1.add(0).cast::()); + let result5 = match l3 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr1.add(1).cast::()); + ErrorCode::_lift(l4 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Rename a filesystem object. + /// + /// Note: This is similar to `renameat` in POSIX. + #[allow(async_fn_in_trait)] + pub fn rename_at( + &self, + old_path: &str, + new_descriptor: &Descriptor, + new_path: &str, + ) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let vec0 = old_path; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let vec1 = new_path; + let ptr1 = vec1.as_ptr().cast::(); + let len1 = vec1.len(); + let ptr2 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.rename-at"] + fn wit_import3( + _: i32, + _: *mut u8, + _: usize, + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import3( + _: i32, + _: *mut u8, + _: usize, + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import3( + (self).handle() as i32, + ptr0.cast_mut(), + len0, + (new_descriptor).handle() as i32, + ptr1.cast_mut(), + len1, + ptr2, + ); + let l4 = i32::from(*ptr2.add(0).cast::()); + let result6 = match l4 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l5 = i32::from(*ptr2.add(1).cast::()); + ErrorCode::_lift(l5 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result6 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Create a symbolic link (also known as a "symlink"). + /// + /// If `old-path` starts with `/`, the function fails with + /// `error-code::not-permitted`. + /// + /// Note: This is similar to `symlinkat` in POSIX. + #[allow(async_fn_in_trait)] + pub fn symlink_at( + &self, + old_path: &str, + new_path: &str, + ) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let vec0 = old_path; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let vec1 = new_path; + let ptr1 = vec1.as_ptr().cast::(); + let len1 = vec1.len(); + let ptr2 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.symlink-at"] + fn wit_import3( + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + _: usize, + _: *mut u8, + ); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import3( + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import3( + (self).handle() as i32, + ptr0.cast_mut(), + len0, + ptr1.cast_mut(), + len1, + ptr2, + ); + let l4 = i32::from(*ptr2.add(0).cast::()); + let result6 = match l4 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l5 = i32::from(*ptr2.add(1).cast::()); + ErrorCode::_lift(l5 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result6 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Unlink a filesystem object that is not a directory. + /// + /// Return `error-code::is-directory` if the path refers to a directory. + /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. + #[allow(async_fn_in_trait)] + pub fn unlink_file_at(&self, path: &str) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let vec0 = path; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let ptr1 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.unlink-file-at"] + fn wit_import2(_: i32, _: *mut u8, _: usize, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import2( + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import2((self).handle() as i32, ptr0.cast_mut(), len0, ptr1); + let l3 = i32::from(*ptr1.add(0).cast::()); + let result5 = match l3 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr1.add(1).cast::()); + ErrorCode::_lift(l4 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Test whether two descriptors refer to the same filesystem object. + /// + /// In POSIX, this corresponds to testing whether the two descriptors have the + /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers. + /// wasi-filesystem does not expose device and inode numbers, so this function + /// may be used instead. + #[allow(async_fn_in_trait)] + pub fn is_same_object(&self, other: &Descriptor) -> bool { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.is-same-object"] + fn wit_import0(_: i32, _: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32, _: i32) -> i32 { + unreachable!() + } + let ret = wit_import0( + (self).handle() as i32, + (other).handle() as i32, + ); + _rt::bool_lift(ret as u8) + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Return a hash of the metadata associated with a filesystem object referred + /// to by a descriptor. + /// + /// This returns a hash of the last-modification timestamp and file size, and + /// may also include the inode number, device number, birth timestamp, and + /// other metadata fields that may change when the file is modified or + /// replaced. It may also include a secret value chosen by the + /// implementation and not otherwise exposed. + /// + /// Implementations are encouraged to provide the following properties: + /// + /// - If the file is not modified or replaced, the computed hash value should + /// usually not change. + /// - If the object is modified or replaced, the computed hash value should + /// usually change. + /// - The inputs to the hash should not be easily computable from the + /// computed hash. + /// + /// However, none of these is required. + #[allow(async_fn_in_trait)] + pub fn metadata_hash(&self) -> Result { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 24]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 24], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.metadata-hash"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result6 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(8).cast::(); + let l4 = *ptr0.add(16).cast::(); + MetadataHashValue { + lower: l3 as u64, + upper: l4 as u64, + } + }; + Ok(e) + } + 1 => { + let e = { + let l5 = i32::from(*ptr0.add(8).cast::()); + ErrorCode::_lift(l5 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result6 + } + } + } + impl Descriptor { + #[allow(unused_unsafe, clippy::all)] + /// Return a hash of the metadata associated with a filesystem object referred + /// to by a directory descriptor and a relative path. + /// + /// This performs the same hash computation as `metadata-hash`. + #[allow(async_fn_in_trait)] + pub fn metadata_hash_at( + &self, + path_flags: PathFlags, + path: &str, + ) -> Result { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 24]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 24], + ); + let flags0 = path_flags; + let vec1 = path; + let ptr1 = vec1.as_ptr().cast::(); + let len1 = vec1.len(); + let ptr2 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]descriptor.metadata-hash-at"] + fn wit_import3( + _: i32, + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import3( + _: i32, + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import3( + (self).handle() as i32, + (flags0.bits() >> 0) as i32, + ptr1.cast_mut(), + len1, + ptr2, + ); + let l4 = i32::from(*ptr2.add(0).cast::()); + let result8 = match l4 { + 0 => { + let e = { + let l5 = *ptr2.add(8).cast::(); + let l6 = *ptr2.add(16).cast::(); + MetadataHashValue { + lower: l5 as u64, + upper: l6 as u64, + } + }; + Ok(e) + } + 1 => { + let e = { + let l7 = i32::from(*ptr2.add(8).cast::()); + ErrorCode::_lift(l7 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result8 + } + } + } + impl DirectoryEntryStream { + #[allow(unused_unsafe, clippy::all)] + /// Read a single directory entry from a `directory-entry-stream`. + #[allow(async_fn_in_trait)] + pub fn read_directory_entry( + &self, + ) -> Result, ErrorCode> { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 5 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 5 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]directory-entry-stream.read-directory-entry"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result9 = match l2 { + 0 => { + let e = { + let l3 = i32::from( + *ptr0.add(::core::mem::size_of::<*const u8>()).cast::(), + ); + match l3 { + 0 => None, + 1 => { + let e = { + let l4 = i32::from( + *ptr0 + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + let l5 = *ptr0 + .add(3 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l6 = *ptr0 + .add(4 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len7 = l6; + let bytes7 = _rt::Vec::from_raw_parts( + l5.cast(), + len7, + len7, + ); + DirectoryEntry { + type_: DescriptorType::_lift(l4 as u8), + name: _rt::string_lift(bytes7), + } + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + Ok(e) + } + 1 => { + let e = { + let l8 = i32::from( + *ptr0.add(::core::mem::size_of::<*const u8>()).cast::(), + ); + ErrorCode::_lift(l8 as u8) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result9 + } + } + } + #[allow(unused_unsafe, clippy::all)] + /// Attempts to extract a filesystem-related `error-code` from the stream + /// `error` provided. + /// + /// Stream operations which return `stream-error::last-operation-failed` + /// have a payload with more information about the operation that failed. + /// This payload can be passed through to this function to see if there's + /// filesystem-related information about the error to return. + /// + /// Note that this function is fallible because not all stream-related + /// errors are filesystem-related errors. + #[allow(async_fn_in_trait)] + pub fn filesystem_error_code(err: &Error) -> Option { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 2]); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/types@0.2.9")] + unsafe extern "C" { + #[link_name = "filesystem-error-code"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((err).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => None, + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + ErrorCode::_lift(l3 as u8) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod preopens { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + pub type Descriptor = super::super::super::wasi::filesystem::types::Descriptor; + #[allow(unused_unsafe, clippy::all)] + /// Return the set of preopened directories, and their paths. + #[allow(async_fn_in_trait)] + pub fn get_directories() -> _rt::Vec<(Descriptor, _rt::String)> { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 2 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:filesystem/preopens@0.2.9")] + unsafe extern "C" { + #[link_name = "get-directories"] + fn wit_import1(_: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: *mut u8) { + unreachable!() + } + wit_import1(ptr0); + let l2 = *ptr0.add(0).cast::<*mut u8>(); + let l3 = *ptr0 + .add(::core::mem::size_of::<*const u8>()) + .cast::(); + let base8 = l2; + let len8 = l3; + let mut result8 = _rt::Vec::with_capacity(len8); + for i in 0..len8 { + let base = base8 + .add(i * (3 * ::core::mem::size_of::<*const u8>())); + let e8 = { + let l4 = *base.add(0).cast::(); + let l5 = *base + .add(::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l6 = *base + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len7 = l6; + let bytes7 = _rt::Vec::from_raw_parts(l5.cast(), len7, len7); + ( + super::super::super::wasi::filesystem::types::Descriptor::from_handle( + l4 as u32, + ), + _rt::string_lift(bytes7), + ) + }; + result8.push(e8); + } + _rt::cabi_dealloc( + base8, + len8 * (3 * ::core::mem::size_of::<*const u8>()), + ::core::mem::size_of::<*const u8>(), + ); + let result9 = result8; + result9 + } + } + } + } + pub mod io { + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod error { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + /// A resource which represents some error information. + /// + /// The only method provided by this resource is `to-debug-string`, + /// which provides some human-readable information about the error. + /// + /// In the `wasi:io` package, this resource is returned through the + /// `wasi:io/streams.stream-error` type. + /// + /// To provide more specific error information, other interfaces may + /// offer functions to "downcast" this error into more specific types. For example, + /// errors returned from streams derived from filesystem types can be described using + /// the filesystem's own error-code type. This is done using the function + /// `wasi:filesystem/types.filesystem-error-code`, which takes a `borrow` + /// parameter and returns an `option`. + /// + /// The set of functions which can "downcast" an `error` into a more + /// concrete type is open. + #[derive(Debug)] + #[repr(transparent)] + pub struct Error { + handle: _rt::Resource, + } + impl Error { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for Error { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/error@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]error"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + impl Error { + #[allow(unused_unsafe, clippy::all)] + /// Returns a string that is suitable to assist humans in debugging + /// this error. + /// + /// WARNING: The returned string should not be consumed mechanically! + /// It may change across platforms, hosts, or other implementation + /// details. Parsing this string is a major platform-compatibility + /// hazard. + #[allow(async_fn_in_trait)] + pub fn to_debug_string(&self) -> _rt::String { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 2 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/error@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]error.to-debug-string"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = *ptr0.add(0).cast::<*mut u8>(); + let l3 = *ptr0 + .add(::core::mem::size_of::<*const u8>()) + .cast::(); + let len4 = l3; + let bytes4 = _rt::Vec::from_raw_parts(l2.cast(), len4, len4); + let result5 = _rt::string_lift(bytes4); + result5 + } + } + } + } + /// A poll API intended to let users wait for I/O events on multiple handles + /// at once. + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod poll { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + /// `pollable` represents a single I/O event which may be ready, or not. + #[derive(Debug)] + #[repr(transparent)] + pub struct Pollable { + handle: _rt::Resource, + } + impl Pollable { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for Pollable { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/poll@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]pollable"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + impl Pollable { + #[allow(unused_unsafe, clippy::all)] + /// Return the readiness of a pollable. This function never blocks. + /// + /// Returns `true` when the pollable is ready, and `false` otherwise. + #[allow(async_fn_in_trait)] + pub fn ready(&self) -> bool { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/poll@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]pollable.ready"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((self).handle() as i32); + _rt::bool_lift(ret as u8) + } + } + } + impl Pollable { + #[allow(unused_unsafe, clippy::all)] + /// `block` returns immediately if the pollable is ready, and otherwise + /// blocks until ready. + /// + /// This function is equivalent to calling `poll.poll` on a list + /// containing only this pollable. + #[allow(async_fn_in_trait)] + pub fn block(&self) -> () { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/poll@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]pollable.block"] + fn wit_import0(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) { + unreachable!() + } + wit_import0((self).handle() as i32); + } + } + } + #[allow(unused_unsafe, clippy::all)] + /// Poll for completion on a set of pollables. + /// + /// This function takes a list of pollables, which identify I/O sources of + /// interest, and waits until one or more of the events is ready for I/O. + /// + /// The result `list` contains one or more indices of handles in the + /// argument list that is ready for I/O. + /// + /// This function traps if either: + /// - the list is empty, or: + /// - the list contains more elements than can be indexed with a `u32` value. + /// + /// A timeout can be implemented by adding a pollable from the + /// wasi-clocks API to the list. + /// + /// This function does not return a `result`; polling in itself does not + /// do any I/O so it doesn't fail. If any of the I/O sources identified by + /// the pollables has an error, it is indicated by marking the source as + /// being ready for I/O. + #[allow(async_fn_in_trait)] + pub fn poll(in_: &[&Pollable]) -> _rt::Vec { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 2 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2 + * ::core::mem::size_of::<*const u8>()], + ); + let vec0 = in_; + let len0 = vec0.len(); + let layout0 = _rt::alloc::Layout::from_size_align(vec0.len() * 4, 4) + .unwrap(); + let (result0, _cleanup0) = wit_bindgen::rt::Cleanup::new(layout0); + for (i, e) in vec0.into_iter().enumerate() { + let base = result0.add(i * 4); + { + *base.add(0).cast::() = (e).handle() as i32; + } + } + let ptr1 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/poll@0.2.9")] + unsafe extern "C" { + #[link_name = "poll"] + fn wit_import2(_: *mut u8, _: usize, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import2(_: *mut u8, _: usize, _: *mut u8) { + unreachable!() + } + wit_import2(result0, len0, ptr1); + let l3 = *ptr1.add(0).cast::<*mut u8>(); + let l4 = *ptr1 + .add(::core::mem::size_of::<*const u8>()) + .cast::(); + let len5 = l4; + let result6 = <_ as From< + _rt::Vec<_>, + >>::from(_rt::Vec::from_raw_parts(l3.cast(), len5, len5)); + result6 + } + } + } + /// WASI I/O is an I/O abstraction API which is currently focused on providing + /// stream types. + /// + /// In the future, the component model is expected to add built-in stream types; + /// when it does, they are expected to subsume this API. + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod streams { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + pub type Error = super::super::super::wasi::io::error::Error; + pub type Pollable = super::super::super::wasi::io::poll::Pollable; + /// An error for input-stream and output-stream operations. + pub enum StreamError { + /// The last operation (a write or flush) failed before completion. + /// + /// More information is available in the `error` payload. + /// + /// After this, the stream will be closed. All future operations return + /// `stream-error::closed`. + LastOperationFailed(Error), + /// The stream is closed: no more input will be accepted by the + /// stream. A closed output-stream will return this error on all + /// future operations. + Closed, + } + impl ::core::fmt::Debug for StreamError { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + match self { + StreamError::LastOperationFailed(e) => { + f.debug_tuple("StreamError::LastOperationFailed") + .field(e) + .finish() + } + StreamError::Closed => { + f.debug_tuple("StreamError::Closed").finish() + } + } + } + } + impl ::core::fmt::Display for StreamError { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + write!(f, "{:?}", self) + } + } + #[cfg(feature = "std")] + impl ::core::error::Error for StreamError {} + /// An input bytestream. + /// + /// `input-stream`s are *non-blocking* to the extent practical on underlying + /// platforms. I/O operations always return promptly; if fewer bytes are + /// promptly available than requested, they return the number of bytes promptly + /// available, which could even be zero. To wait for data to be available, + /// use the `subscribe` function to obtain a `pollable` which can be polled + /// for using `wasi:io/poll`. + #[derive(Debug)] + #[repr(transparent)] + pub struct InputStream { + handle: _rt::Resource, + } + impl InputStream { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for InputStream { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/streams@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]input-stream"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + /// An output bytestream. + /// + /// `output-stream`s are *non-blocking* to the extent practical on + /// underlying platforms. Except where specified otherwise, I/O operations also + /// always return promptly, after the number of bytes that can be written + /// promptly, which could even be zero. To wait for the stream to be ready to + /// accept data, the `subscribe` function to obtain a `pollable` which can be + /// polled for using `wasi:io/poll`. + /// + /// Dropping an `output-stream` while there's still an active write in + /// progress may result in the data being lost. Before dropping the stream, + /// be sure to fully flush your writes. + #[derive(Debug)] + #[repr(transparent)] + pub struct OutputStream { + handle: _rt::Resource, + } + impl OutputStream { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for OutputStream { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/streams@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]output-stream"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + impl InputStream { + #[allow(unused_unsafe, clippy::all)] + /// Perform a non-blocking read from the stream. + /// + /// When the source of a `read` is binary data, the bytes from the source + /// are returned verbatim. When the source of a `read` is known to the + /// implementation to be text, bytes containing the UTF-8 encoding of the + /// text are returned. + /// + /// This function returns a list of bytes containing the read data, + /// when successful. The returned list will contain up to `len` bytes; + /// it may return fewer than requested, but not more. The list is + /// empty when no bytes are available for reading at this time. The + /// pollable given by `subscribe` will be ready when more bytes are + /// available. + /// + /// This function fails with a `stream-error` when the operation + /// encounters an error, giving `last-operation-failed`, or when the + /// stream is closed, giving `closed`. + /// + /// When the caller gives a `len` of 0, it represents a request to + /// read 0 bytes. If the stream is still open, this call should + /// succeed and return an empty list, or otherwise fail with `closed`. + /// + /// The `len` parameter is a `u64`, which could represent a list of u8 which + /// is not possible to allocate in wasm32, or not desirable to allocate as + /// as a return value by the callee. The callee may return a list of bytes + /// less than `len` in size while more bytes are available for reading. + #[allow(async_fn_in_trait)] + pub fn read(&self, len: u64) -> Result<_rt::Vec, StreamError> { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 3 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 3 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/streams@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]input-stream.read"] + fn wit_import1(_: i32, _: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i64, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, _rt::as_i64(&len), ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result9 = match l2 { + 0 => { + let e = { + let l3 = *ptr0 + .add(::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l4 = *ptr0 + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len5 = l4; + <_ as From< + _rt::Vec<_>, + >>::from(_rt::Vec::from_raw_parts(l3.cast(), len5, len5)) + }; + Ok(e) + } + 1 => { + let e = { + let l6 = i32::from( + *ptr0.add(::core::mem::size_of::<*const u8>()).cast::(), + ); + let v8 = match l6 { + 0 => { + let e8 = { + let l7 = *ptr0 + .add(4 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::(); + super::super::super::wasi::io::error::Error::from_handle( + l7 as u32, + ) + }; + StreamError::LastOperationFailed(e8) + } + n => { + debug_assert_eq!(n, 1, "invalid enum discriminant"); + StreamError::Closed + } + }; + v8 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result9 + } + } + } + impl InputStream { + #[allow(unused_unsafe, clippy::all)] + /// Read bytes from a stream, after blocking until at least one byte can + /// be read. Except for blocking, behavior is identical to `read`. + #[allow(async_fn_in_trait)] + pub fn blocking_read( + &self, + len: u64, + ) -> Result<_rt::Vec, StreamError> { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 3 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 3 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/streams@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]input-stream.blocking-read"] + fn wit_import1(_: i32, _: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i64, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, _rt::as_i64(&len), ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result9 = match l2 { + 0 => { + let e = { + let l3 = *ptr0 + .add(::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l4 = *ptr0 + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len5 = l4; + <_ as From< + _rt::Vec<_>, + >>::from(_rt::Vec::from_raw_parts(l3.cast(), len5, len5)) + }; + Ok(e) + } + 1 => { + let e = { + let l6 = i32::from( + *ptr0.add(::core::mem::size_of::<*const u8>()).cast::(), + ); + let v8 = match l6 { + 0 => { + let e8 = { + let l7 = *ptr0 + .add(4 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::(); + super::super::super::wasi::io::error::Error::from_handle( + l7 as u32, + ) + }; + StreamError::LastOperationFailed(e8) + } + n => { + debug_assert_eq!(n, 1, "invalid enum discriminant"); + StreamError::Closed + } + }; + v8 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result9 + } + } + } + impl InputStream { + #[allow(unused_unsafe, clippy::all)] + /// Skip bytes from a stream. Returns number of bytes skipped. + /// + /// Behaves identical to `read`, except instead of returning a list + /// of bytes, returns the number of bytes consumed from the stream. + #[allow(async_fn_in_trait)] + pub fn skip(&self, len: u64) -> Result { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 16], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/streams@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]input-stream.skip"] + fn wit_import1(_: i32, _: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i64, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, _rt::as_i64(&len), ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result7 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(8).cast::(); + l3 as u64 + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(8).cast::()); + let v6 = match l4 { + 0 => { + let e6 = { + let l5 = *ptr0.add(12).cast::(); + super::super::super::wasi::io::error::Error::from_handle( + l5 as u32, + ) + }; + StreamError::LastOperationFailed(e6) + } + n => { + debug_assert_eq!(n, 1, "invalid enum discriminant"); + StreamError::Closed + } + }; + v6 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result7 + } + } + } + impl InputStream { + #[allow(unused_unsafe, clippy::all)] + /// Skip bytes from a stream, after blocking until at least one byte + /// can be skipped. Except for blocking behavior, identical to `skip`. + #[allow(async_fn_in_trait)] + pub fn blocking_skip(&self, len: u64) -> Result { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 16], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/streams@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]input-stream.blocking-skip"] + fn wit_import1(_: i32, _: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i64, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, _rt::as_i64(&len), ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result7 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(8).cast::(); + l3 as u64 + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(8).cast::()); + let v6 = match l4 { + 0 => { + let e6 = { + let l5 = *ptr0.add(12).cast::(); + super::super::super::wasi::io::error::Error::from_handle( + l5 as u32, + ) + }; + StreamError::LastOperationFailed(e6) + } + n => { + debug_assert_eq!(n, 1, "invalid enum discriminant"); + StreamError::Closed + } + }; + v6 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result7 + } + } + } + impl InputStream { + #[allow(unused_unsafe, clippy::all)] + /// Create a `pollable` which will resolve once either the specified stream + /// has bytes available to read or the other end of the stream has been + /// closed. + /// The created `pollable` is a child resource of the `input-stream`. + /// Implementations may trap if the `input-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + #[allow(async_fn_in_trait)] + pub fn subscribe(&self) -> Pollable { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/streams@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]input-stream.subscribe"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((self).handle() as i32); + super::super::super::wasi::io::poll::Pollable::from_handle( + ret as u32, + ) + } + } + } + impl OutputStream { + #[allow(unused_unsafe, clippy::all)] + /// Check readiness for writing. This function never blocks. + /// + /// Returns the number of bytes permitted for the next call to `write`, + /// or an error. Calling `write` with more bytes than this function has + /// permitted will trap. + /// + /// When this function returns 0 bytes, the `subscribe` pollable will + /// become ready when this function will report at least 1 byte, or an + /// error. + #[allow(async_fn_in_trait)] + pub fn check_write(&self) -> Result { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 16], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/streams@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]output-stream.check-write"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result7 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(8).cast::(); + l3 as u64 + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(8).cast::()); + let v6 = match l4 { + 0 => { + let e6 = { + let l5 = *ptr0.add(12).cast::(); + super::super::super::wasi::io::error::Error::from_handle( + l5 as u32, + ) + }; + StreamError::LastOperationFailed(e6) + } + n => { + debug_assert_eq!(n, 1, "invalid enum discriminant"); + StreamError::Closed + } + }; + v6 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result7 + } + } + } + impl OutputStream { + #[allow(unused_unsafe, clippy::all)] + /// Perform a write. This function never blocks. + /// + /// When the destination of a `write` is binary data, the bytes from + /// `contents` are written verbatim. When the destination of a `write` is + /// known to the implementation to be text, the bytes of `contents` are + /// transcoded from UTF-8 into the encoding of the destination and then + /// written. + /// + /// Precondition: check-write gave permit of Ok(n) and contents has a + /// length of less than or equal to n. Otherwise, this function will trap. + /// + /// returns Err(closed) without writing if the stream has closed since + /// the last call to check-write provided a permit. + #[allow(async_fn_in_trait)] + pub fn write(&self, contents: &[u8]) -> Result<(), StreamError> { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 12]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 12], + ); + let vec0 = contents; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let ptr1 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/streams@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]output-stream.write"] + fn wit_import2(_: i32, _: *mut u8, _: usize, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import2( + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import2((self).handle() as i32, ptr0.cast_mut(), len0, ptr1); + let l3 = i32::from(*ptr1.add(0).cast::()); + let result7 = match l3 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr1.add(4).cast::()); + let v6 = match l4 { + 0 => { + let e6 = { + let l5 = *ptr1.add(8).cast::(); + super::super::super::wasi::io::error::Error::from_handle( + l5 as u32, + ) + }; + StreamError::LastOperationFailed(e6) + } + n => { + debug_assert_eq!(n, 1, "invalid enum discriminant"); + StreamError::Closed + } + }; + v6 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result7 + } + } + } + impl OutputStream { + #[allow(unused_unsafe, clippy::all)] + /// Perform a write of up to 4096 bytes, and then flush the stream. Block + /// until all of these operations are complete, or an error occurs. + /// + /// Returns success when all of the contents written are successfully + /// flushed to output. If an error occurs at any point before all + /// contents are successfully flushed, that error is returned as soon as + /// possible. If writing and flushing the complete contents causes the + /// stream to become closed, this call should return success, and + /// subsequent calls to check-write or other interfaces should return + /// stream-error::closed. + #[allow(async_fn_in_trait)] + pub fn blocking_write_and_flush( + &self, + contents: &[u8], + ) -> Result<(), StreamError> { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 12]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 12], + ); + let vec0 = contents; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let ptr1 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/streams@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]output-stream.blocking-write-and-flush"] + fn wit_import2(_: i32, _: *mut u8, _: usize, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import2( + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import2((self).handle() as i32, ptr0.cast_mut(), len0, ptr1); + let l3 = i32::from(*ptr1.add(0).cast::()); + let result7 = match l3 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr1.add(4).cast::()); + let v6 = match l4 { + 0 => { + let e6 = { + let l5 = *ptr1.add(8).cast::(); + super::super::super::wasi::io::error::Error::from_handle( + l5 as u32, + ) + }; + StreamError::LastOperationFailed(e6) + } + n => { + debug_assert_eq!(n, 1, "invalid enum discriminant"); + StreamError::Closed + } + }; + v6 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result7 + } + } + } + impl OutputStream { + #[allow(unused_unsafe, clippy::all)] + /// Request to flush buffered output. This function never blocks. + /// + /// This tells the output-stream that the caller intends any buffered + /// output to be flushed. the output which is expected to be flushed + /// is all that has been passed to `write` prior to this call. + /// + /// Upon calling this function, the `output-stream` will not accept any + /// writes (`check-write` will return `ok(0)`) until the flush has + /// completed. The `subscribe` pollable will become ready when the + /// flush has completed and the stream can accept more writes. + #[allow(async_fn_in_trait)] + pub fn flush(&self) -> Result<(), StreamError> { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 12]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 12], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/streams@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]output-stream.flush"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result6 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(4).cast::()); + let v5 = match l3 { + 0 => { + let e5 = { + let l4 = *ptr0.add(8).cast::(); + super::super::super::wasi::io::error::Error::from_handle( + l4 as u32, + ) + }; + StreamError::LastOperationFailed(e5) + } + n => { + debug_assert_eq!(n, 1, "invalid enum discriminant"); + StreamError::Closed + } + }; + v5 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result6 + } + } + } + impl OutputStream { + #[allow(unused_unsafe, clippy::all)] + /// Request to flush buffered output, and block until flush completes + /// and stream is ready for writing again. + #[allow(async_fn_in_trait)] + pub fn blocking_flush(&self) -> Result<(), StreamError> { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 12]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 12], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/streams@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]output-stream.blocking-flush"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result6 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(4).cast::()); + let v5 = match l3 { + 0 => { + let e5 = { + let l4 = *ptr0.add(8).cast::(); + super::super::super::wasi::io::error::Error::from_handle( + l4 as u32, + ) + }; + StreamError::LastOperationFailed(e5) + } + n => { + debug_assert_eq!(n, 1, "invalid enum discriminant"); + StreamError::Closed + } + }; + v5 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result6 + } + } + } + impl OutputStream { + #[allow(unused_unsafe, clippy::all)] + /// Create a `pollable` which will resolve once the output-stream + /// is ready for more writing, or an error has occurred. When this + /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an + /// error. + /// + /// If the stream is closed, this pollable is always ready immediately. + /// + /// The created `pollable` is a child resource of the `output-stream`. + /// Implementations may trap if the `output-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + #[allow(async_fn_in_trait)] + pub fn subscribe(&self) -> Pollable { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/streams@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]output-stream.subscribe"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((self).handle() as i32); + super::super::super::wasi::io::poll::Pollable::from_handle( + ret as u32, + ) + } + } + } + impl OutputStream { + #[allow(unused_unsafe, clippy::all)] + /// Write zeroes to a stream. + /// + /// This should be used precisely like `write` with the exact same + /// preconditions (must use check-write first), but instead of + /// passing a list of bytes, you simply pass the number of zero-bytes + /// that should be written. + #[allow(async_fn_in_trait)] + pub fn write_zeroes(&self, len: u64) -> Result<(), StreamError> { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 12]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 12], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/streams@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]output-stream.write-zeroes"] + fn wit_import1(_: i32, _: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i64, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, _rt::as_i64(&len), ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result6 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(4).cast::()); + let v5 = match l3 { + 0 => { + let e5 = { + let l4 = *ptr0.add(8).cast::(); + super::super::super::wasi::io::error::Error::from_handle( + l4 as u32, + ) + }; + StreamError::LastOperationFailed(e5) + } + n => { + debug_assert_eq!(n, 1, "invalid enum discriminant"); + StreamError::Closed + } + }; + v5 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result6 + } + } + } + impl OutputStream { + #[allow(unused_unsafe, clippy::all)] + /// Perform a write of up to 4096 zeroes, and then flush the stream. + /// Block until all of these operations are complete, or an error + /// occurs. + /// + /// Functionality is equivelant to `blocking-write-and-flush` with + /// contents given as a list of len containing only zeroes. + #[allow(async_fn_in_trait)] + pub fn blocking_write_zeroes_and_flush( + &self, + len: u64, + ) -> Result<(), StreamError> { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 12]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 12], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/streams@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]output-stream.blocking-write-zeroes-and-flush"] + fn wit_import1(_: i32, _: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i64, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, _rt::as_i64(&len), ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result6 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(4).cast::()); + let v5 = match l3 { + 0 => { + let e5 = { + let l4 = *ptr0.add(8).cast::(); + super::super::super::wasi::io::error::Error::from_handle( + l4 as u32, + ) + }; + StreamError::LastOperationFailed(e5) + } + n => { + debug_assert_eq!(n, 1, "invalid enum discriminant"); + StreamError::Closed + } + }; + v5 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result6 + } + } + } + impl OutputStream { + #[allow(unused_unsafe, clippy::all)] + /// Read from one stream and write to another. + /// + /// The behavior of splice is equivalent to: + /// 1. calling `check-write` on the `output-stream` + /// 2. calling `read` on the `input-stream` with the smaller of the + /// `check-write` permitted length and the `len` provided to `splice` + /// 3. calling `write` on the `output-stream` with that read data. + /// + /// Any error reported by the call to `check-write`, `read`, or + /// `write` ends the splice and reports that error. + /// + /// This function returns the number of bytes transferred; it may be less + /// than `len`. + #[allow(async_fn_in_trait)] + pub fn splice( + &self, + src: &InputStream, + len: u64, + ) -> Result { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 16], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/streams@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]output-stream.splice"] + fn wit_import1(_: i32, _: i32, _: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1( + _: i32, + _: i32, + _: i64, + _: *mut u8, + ) { + unreachable!() + } + wit_import1( + (self).handle() as i32, + (src).handle() as i32, + _rt::as_i64(&len), + ptr0, + ); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result7 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(8).cast::(); + l3 as u64 + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(8).cast::()); + let v6 = match l4 { + 0 => { + let e6 = { + let l5 = *ptr0.add(12).cast::(); + super::super::super::wasi::io::error::Error::from_handle( + l5 as u32, + ) + }; + StreamError::LastOperationFailed(e6) + } + n => { + debug_assert_eq!(n, 1, "invalid enum discriminant"); + StreamError::Closed + } + }; + v6 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result7 + } + } + } + impl OutputStream { + #[allow(unused_unsafe, clippy::all)] + /// Read from one stream and write to another, with blocking. + /// + /// This is similar to `splice`, except that it blocks until the + /// `output-stream` is ready for writing, and the `input-stream` + /// is ready for reading, before performing the `splice`. + #[allow(async_fn_in_trait)] + pub fn blocking_splice( + &self, + src: &InputStream, + len: u64, + ) -> Result { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 16], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:io/streams@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]output-stream.blocking-splice"] + fn wit_import1(_: i32, _: i32, _: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1( + _: i32, + _: i32, + _: i64, + _: *mut u8, + ) { + unreachable!() + } + wit_import1( + (self).handle() as i32, + (src).handle() as i32, + _rt::as_i64(&len), + ptr0, + ); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result7 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(8).cast::(); + l3 as u64 + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(8).cast::()); + let v6 = match l4 { + 0 => { + let e6 = { + let l5 = *ptr0.add(12).cast::(); + super::super::super::wasi::io::error::Error::from_handle( + l5 as u32, + ) + }; + StreamError::LastOperationFailed(e6) + } + n => { + debug_assert_eq!(n, 1, "invalid enum discriminant"); + StreamError::Closed + } + }; + v6 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result7 + } + } + } + } + } + pub mod random { + /// WASI Random is a random data API. + /// + /// It is intended to be portable at least between Unix-family platforms and + /// Windows. + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod random { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + #[allow(unused_unsafe, clippy::all)] + /// Return `len` cryptographically-secure random or pseudo-random bytes. + /// + /// This function must produce data at least as cryptographically secure and + /// fast as an adequately seeded cryptographically-secure pseudo-random + /// number generator (CSPRNG). It must not block, from the perspective of + /// the calling program, under any circumstances, including on the first + /// request and on requests for numbers of bytes. The returned data must + /// always be unpredictable. + /// + /// This function must always return fresh data. Deterministic environments + /// must omit this function, rather than implementing it with deterministic + /// data. + #[allow(async_fn_in_trait)] + pub fn get_random_bytes(len: u64) -> _rt::Vec { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 2 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:random/random@0.2.9")] + unsafe extern "C" { + #[link_name = "get-random-bytes"] + fn wit_import1(_: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i64, _: *mut u8) { + unreachable!() + } + wit_import1(_rt::as_i64(&len), ptr0); + let l2 = *ptr0.add(0).cast::<*mut u8>(); + let l3 = *ptr0 + .add(::core::mem::size_of::<*const u8>()) + .cast::(); + let len4 = l3; + let result5 = <_ as From< + _rt::Vec<_>, + >>::from(_rt::Vec::from_raw_parts(l2.cast(), len4, len4)); + result5 + } + } + #[allow(unused_unsafe, clippy::all)] + /// Return a cryptographically-secure random or pseudo-random `u64` value. + /// + /// This function returns the same type of data as `get-random-bytes`, + /// represented as a `u64`. + #[allow(async_fn_in_trait)] + pub fn get_random_u64() -> u64 { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:random/random@0.2.9")] + unsafe extern "C" { + #[link_name = "get-random-u64"] + fn wit_import0() -> i64; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0() -> i64 { + unreachable!() + } + let ret = wit_import0(); + ret as u64 + } + } + } + /// The insecure interface for insecure pseudo-random numbers. + /// + /// It is intended to be portable at least between Unix-family platforms and + /// Windows. + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod insecure { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + #[allow(unused_unsafe, clippy::all)] + /// Return `len` insecure pseudo-random bytes. + /// + /// This function is not cryptographically secure. Do not use it for + /// anything related to security. + /// + /// There are no requirements on the values of the returned bytes, however + /// implementations are encouraged to return evenly distributed values with + /// a long period. + #[allow(async_fn_in_trait)] + pub fn get_insecure_random_bytes(len: u64) -> _rt::Vec { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 2 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:random/insecure@0.2.9")] + unsafe extern "C" { + #[link_name = "get-insecure-random-bytes"] + fn wit_import1(_: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i64, _: *mut u8) { + unreachable!() + } + wit_import1(_rt::as_i64(&len), ptr0); + let l2 = *ptr0.add(0).cast::<*mut u8>(); + let l3 = *ptr0 + .add(::core::mem::size_of::<*const u8>()) + .cast::(); + let len4 = l3; + let result5 = <_ as From< + _rt::Vec<_>, + >>::from(_rt::Vec::from_raw_parts(l2.cast(), len4, len4)); + result5 + } + } + #[allow(unused_unsafe, clippy::all)] + /// Return an insecure pseudo-random `u64` value. + /// + /// This function returns the same type of pseudo-random data as + /// `get-insecure-random-bytes`, represented as a `u64`. + #[allow(async_fn_in_trait)] + pub fn get_insecure_random_u64() -> u64 { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:random/insecure@0.2.9")] + unsafe extern "C" { + #[link_name = "get-insecure-random-u64"] + fn wit_import0() -> i64; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0() -> i64 { + unreachable!() + } + let ret = wit_import0(); + ret as u64 + } + } + } + /// The insecure-seed interface for seeding hash-map DoS resistance. + /// + /// It is intended to be portable at least between Unix-family platforms and + /// Windows. + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod insecure_seed { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + #[allow(unused_unsafe, clippy::all)] + /// Return a 128-bit value that may contain a pseudo-random value. + /// + /// The returned value is not required to be computed from a CSPRNG, and may + /// even be entirely deterministic. Host implementations are encouraged to + /// provide pseudo-random values to any program exposed to + /// attacker-controlled content, to enable DoS protection built into many + /// languages' hash-map implementations. + /// + /// This function is intended to only be called once, by a source language + /// to initialize Denial Of Service (DoS) protection in its hash-map + /// implementation. + /// + /// # Expected future evolution + /// + /// This will likely be changed to a value import, to prevent it from being + /// called multiple times and potentially used for purposes other than DoS + /// protection. + #[allow(async_fn_in_trait)] + pub fn insecure_seed() -> (u64, u64) { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 16]); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:random/insecure-seed@0.2.9")] + unsafe extern "C" { + #[link_name = "insecure-seed"] + fn wit_import1(_: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: *mut u8) { + unreachable!() + } + wit_import1(ptr0); + let l2 = *ptr0.add(0).cast::(); + let l3 = *ptr0.add(8).cast::(); + let result4 = (l2 as u64, l3 as u64); + result4 + } + } + } + } + pub mod sockets { + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod network { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + /// An opaque resource that represents access to (a subset of) the network. + /// This enables context-based security for networking. + /// There is no need for this to map 1:1 to a physical network interface. + #[derive(Debug)] + #[repr(transparent)] + pub struct Network { + handle: _rt::Resource, + } + impl Network { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for Network { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/network@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]network"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + /// Error codes. + /// + /// In theory, every API can return any error code. + /// In practice, API's typically only return the errors documented per API + /// combined with a couple of errors that are always possible: + /// - `unknown` + /// - `access-denied` + /// - `not-supported` + /// - `out-of-memory` + /// - `concurrency-conflict` + /// + /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. + #[repr(u8)] + #[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] + pub enum ErrorCode { + /// Unknown error + Unknown, + /// Access denied. + /// + /// POSIX equivalent: EACCES, EPERM + AccessDenied, + /// The operation is not supported. + /// + /// POSIX equivalent: EOPNOTSUPP + NotSupported, + /// One of the arguments is invalid. + /// + /// POSIX equivalent: EINVAL + InvalidArgument, + /// Not enough memory to complete the operation. + /// + /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY + OutOfMemory, + /// The operation timed out before it could finish completely. + Timeout, + /// This operation is incompatible with another asynchronous operation that is already in progress. + /// + /// POSIX equivalent: EALREADY + ConcurrencyConflict, + /// Trying to finish an asynchronous operation that: + /// - has not been started yet, or: + /// - was already finished by a previous `finish-*` call. + /// + /// Note: this is scheduled to be removed when `future`s are natively supported. + NotInProgress, + /// The operation has been aborted because it could not be completed immediately. + /// + /// Note: this is scheduled to be removed when `future`s are natively supported. + WouldBlock, + /// The operation is not valid in the socket's current state. + InvalidState, + /// A new socket resource could not be created because of a system limit. + NewSocketLimit, + /// A bind operation failed because the provided address is not an address that the `network` can bind to. + AddressNotBindable, + /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available. + AddressInUse, + /// The remote address is not reachable + RemoteUnreachable, + /// The TCP connection was forcefully rejected + ConnectionRefused, + /// The TCP connection was reset. + ConnectionReset, + /// A TCP connection was aborted. + ConnectionAborted, + /// The size of a datagram sent to a UDP socket exceeded the maximum + /// supported size. + DatagramTooLarge, + /// Name does not exist or has no suitable associated IP addresses. + NameUnresolvable, + /// A temporary failure in name resolution occurred. + TemporaryResolverFailure, + /// A permanent failure in name resolution occurred. + PermanentResolverFailure, + } + impl ErrorCode { + pub fn name(&self) -> &'static str { + match self { + ErrorCode::Unknown => "unknown", + ErrorCode::AccessDenied => "access-denied", + ErrorCode::NotSupported => "not-supported", + ErrorCode::InvalidArgument => "invalid-argument", + ErrorCode::OutOfMemory => "out-of-memory", + ErrorCode::Timeout => "timeout", + ErrorCode::ConcurrencyConflict => "concurrency-conflict", + ErrorCode::NotInProgress => "not-in-progress", + ErrorCode::WouldBlock => "would-block", + ErrorCode::InvalidState => "invalid-state", + ErrorCode::NewSocketLimit => "new-socket-limit", + ErrorCode::AddressNotBindable => "address-not-bindable", + ErrorCode::AddressInUse => "address-in-use", + ErrorCode::RemoteUnreachable => "remote-unreachable", + ErrorCode::ConnectionRefused => "connection-refused", + ErrorCode::ConnectionReset => "connection-reset", + ErrorCode::ConnectionAborted => "connection-aborted", + ErrorCode::DatagramTooLarge => "datagram-too-large", + ErrorCode::NameUnresolvable => "name-unresolvable", + ErrorCode::TemporaryResolverFailure => { + "temporary-resolver-failure" + } + ErrorCode::PermanentResolverFailure => { + "permanent-resolver-failure" + } + } + } + pub fn message(&self) -> &'static str { + match self { + ErrorCode::Unknown => "Unknown error", + ErrorCode::AccessDenied => { + "Access denied. + + POSIX equivalent: EACCES, EPERM" + } + ErrorCode::NotSupported => { + "The operation is not supported. + + POSIX equivalent: EOPNOTSUPP" + } + ErrorCode::InvalidArgument => { + "One of the arguments is invalid. + + POSIX equivalent: EINVAL" + } + ErrorCode::OutOfMemory => { + "Not enough memory to complete the operation. + + POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY" + } + ErrorCode::Timeout => { + "The operation timed out before it could finish completely." + } + ErrorCode::ConcurrencyConflict => { + "This operation is incompatible with another asynchronous operation that is already in progress. + + POSIX equivalent: EALREADY" + } + ErrorCode::NotInProgress => { + "Trying to finish an asynchronous operation that: + - has not been started yet, or: + - was already finished by a previous `finish-*` call. + + Note: this is scheduled to be removed when `future`s are natively supported." + } + ErrorCode::WouldBlock => { + "The operation has been aborted because it could not be completed immediately. + + Note: this is scheduled to be removed when `future`s are natively supported." + } + ErrorCode::InvalidState => { + "The operation is not valid in the socket's current state." + } + ErrorCode::NewSocketLimit => { + "A new socket resource could not be created because of a system limit." + } + ErrorCode::AddressNotBindable => { + "A bind operation failed because the provided address is not an address that the `network` can bind to." + } + ErrorCode::AddressInUse => { + "A bind operation failed because the provided address is already in use or because there are no ephemeral ports available." + } + ErrorCode::RemoteUnreachable => { + "The remote address is not reachable" + } + ErrorCode::ConnectionRefused => { + "The TCP connection was forcefully rejected" + } + ErrorCode::ConnectionReset => "The TCP connection was reset.", + ErrorCode::ConnectionAborted => "A TCP connection was aborted.", + ErrorCode::DatagramTooLarge => { + "The size of a datagram sent to a UDP socket exceeded the maximum + supported size." + } + ErrorCode::NameUnresolvable => { + "Name does not exist or has no suitable associated IP addresses." + } + ErrorCode::TemporaryResolverFailure => { + "A temporary failure in name resolution occurred." + } + ErrorCode::PermanentResolverFailure => { + "A permanent failure in name resolution occurred." + } + } + } + } + impl ::core::fmt::Debug for ErrorCode { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + f.debug_struct("ErrorCode") + .field("code", &(*self as i32)) + .field("name", &self.name()) + .field("message", &self.message()) + .finish() + } + } + impl ::core::fmt::Display for ErrorCode { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + write!(f, "{} (error {})", self.name(), * self as i32) + } + } + #[cfg(feature = "std")] + impl ::core::error::Error for ErrorCode {} + impl ErrorCode { + #[doc(hidden)] + pub unsafe fn _lift(val: u8) -> ErrorCode { + if !cfg!(debug_assertions) { + return unsafe { ::core::mem::transmute(val) }; + } + match val { + 0 => ErrorCode::Unknown, + 1 => ErrorCode::AccessDenied, + 2 => ErrorCode::NotSupported, + 3 => ErrorCode::InvalidArgument, + 4 => ErrorCode::OutOfMemory, + 5 => ErrorCode::Timeout, + 6 => ErrorCode::ConcurrencyConflict, + 7 => ErrorCode::NotInProgress, + 8 => ErrorCode::WouldBlock, + 9 => ErrorCode::InvalidState, + 10 => ErrorCode::NewSocketLimit, + 11 => ErrorCode::AddressNotBindable, + 12 => ErrorCode::AddressInUse, + 13 => ErrorCode::RemoteUnreachable, + 14 => ErrorCode::ConnectionRefused, + 15 => ErrorCode::ConnectionReset, + 16 => ErrorCode::ConnectionAborted, + 17 => ErrorCode::DatagramTooLarge, + 18 => ErrorCode::NameUnresolvable, + 19 => ErrorCode::TemporaryResolverFailure, + 20 => ErrorCode::PermanentResolverFailure, + _ => panic!("invalid enum discriminant"), + } + } + } + #[repr(u8)] + #[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] + pub enum IpAddressFamily { + /// Similar to `AF_INET` in POSIX. + Ipv4, + /// Similar to `AF_INET6` in POSIX. + Ipv6, + } + impl ::core::fmt::Debug for IpAddressFamily { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + match self { + IpAddressFamily::Ipv4 => { + f.debug_tuple("IpAddressFamily::Ipv4").finish() + } + IpAddressFamily::Ipv6 => { + f.debug_tuple("IpAddressFamily::Ipv6").finish() + } + } + } + } + impl IpAddressFamily { + #[doc(hidden)] + pub unsafe fn _lift(val: u8) -> IpAddressFamily { + if !cfg!(debug_assertions) { + return unsafe { ::core::mem::transmute(val) }; + } + match val { + 0 => IpAddressFamily::Ipv4, + 1 => IpAddressFamily::Ipv6, + _ => panic!("invalid enum discriminant"), + } + } + } + pub type Ipv4Address = (u8, u8, u8, u8); + pub type Ipv6Address = (u16, u16, u16, u16, u16, u16, u16, u16); + #[derive(Clone, Copy)] + pub enum IpAddress { + Ipv4(Ipv4Address), + Ipv6(Ipv6Address), + } + impl ::core::fmt::Debug for IpAddress { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + match self { + IpAddress::Ipv4(e) => { + f.debug_tuple("IpAddress::Ipv4").field(e).finish() + } + IpAddress::Ipv6(e) => { + f.debug_tuple("IpAddress::Ipv6").field(e).finish() + } + } + } + } + #[repr(C)] + #[derive(Clone, Copy)] + pub struct Ipv4SocketAddress { + /// sin_port + pub port: u16, + /// sin_addr + pub address: Ipv4Address, + } + impl ::core::fmt::Debug for Ipv4SocketAddress { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + f.debug_struct("Ipv4SocketAddress") + .field("port", &self.port) + .field("address", &self.address) + .finish() + } + } + #[repr(C)] + #[derive(Clone, Copy)] + pub struct Ipv6SocketAddress { + /// sin6_port + pub port: u16, + /// sin6_flowinfo + pub flow_info: u32, + /// sin6_addr + pub address: Ipv6Address, + /// sin6_scope_id + pub scope_id: u32, + } + impl ::core::fmt::Debug for Ipv6SocketAddress { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + f.debug_struct("Ipv6SocketAddress") + .field("port", &self.port) + .field("flow-info", &self.flow_info) + .field("address", &self.address) + .field("scope-id", &self.scope_id) + .finish() + } + } + #[derive(Clone, Copy)] + pub enum IpSocketAddress { + Ipv4(Ipv4SocketAddress), + Ipv6(Ipv6SocketAddress), + } + impl ::core::fmt::Debug for IpSocketAddress { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + match self { + IpSocketAddress::Ipv4(e) => { + f.debug_tuple("IpSocketAddress::Ipv4").field(e).finish() + } + IpSocketAddress::Ipv6(e) => { + f.debug_tuple("IpSocketAddress::Ipv6").field(e).finish() + } + } + } + } + } + /// This interface provides a value-export of the default network handle.. + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod instance_network { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + pub type Network = super::super::super::wasi::sockets::network::Network; + #[allow(unused_unsafe, clippy::all)] + /// Get a handle to the default network. + #[allow(async_fn_in_trait)] + pub fn instance_network() -> Network { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/instance-network@0.2.9")] + unsafe extern "C" { + #[link_name = "instance-network"] + fn wit_import0() -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0() -> i32 { + unreachable!() + } + let ret = wit_import0(); + super::super::super::wasi::sockets::network::Network::from_handle( + ret as u32, + ) + } + } + } + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod udp { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + pub type Pollable = super::super::super::wasi::io::poll::Pollable; + pub type Network = super::super::super::wasi::sockets::network::Network; + pub type ErrorCode = super::super::super::wasi::sockets::network::ErrorCode; + pub type IpSocketAddress = super::super::super::wasi::sockets::network::IpSocketAddress; + pub type IpAddressFamily = super::super::super::wasi::sockets::network::IpAddressFamily; + /// A received datagram. + #[derive(Clone)] + pub struct IncomingDatagram { + /// The payload. + /// + /// Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes. + pub data: _rt::Vec, + /// The source address. + /// + /// This field is guaranteed to match the remote address the stream was initialized with, if any. + /// + /// Equivalent to the `src_addr` out parameter of `recvfrom`. + pub remote_address: IpSocketAddress, + } + impl ::core::fmt::Debug for IncomingDatagram { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + f.debug_struct("IncomingDatagram") + .field("data", &self.data) + .field("remote-address", &self.remote_address) + .finish() + } + } + /// A datagram to be sent out. + #[derive(Clone)] + pub struct OutgoingDatagram { + /// The payload. + pub data: _rt::Vec, + /// The destination address. + /// + /// The requirements on this field depend on how the stream was initialized: + /// - with a remote address: this field must be None or match the stream's remote address exactly. + /// - without a remote address: this field is required. + /// + /// If this value is None, the send operation is equivalent to `send` in POSIX. Otherwise it is equivalent to `sendto`. + pub remote_address: Option, + } + impl ::core::fmt::Debug for OutgoingDatagram { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + f.debug_struct("OutgoingDatagram") + .field("data", &self.data) + .field("remote-address", &self.remote_address) + .finish() + } + } + /// A UDP socket handle. + #[derive(Debug)] + #[repr(transparent)] + pub struct UdpSocket { + handle: _rt::Resource, + } + impl UdpSocket { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for UdpSocket { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]udp-socket"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + #[derive(Debug)] + #[repr(transparent)] + pub struct IncomingDatagramStream { + handle: _rt::Resource, + } + impl IncomingDatagramStream { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for IncomingDatagramStream { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]incoming-datagram-stream"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + #[derive(Debug)] + #[repr(transparent)] + pub struct OutgoingDatagramStream { + handle: _rt::Resource, + } + impl OutgoingDatagramStream { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for OutgoingDatagramStream { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]outgoing-datagram-stream"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + impl UdpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the port is zero, the socket will be bound to a random free port. + /// + /// # Typical errors + /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) + /// - `invalid-state`: The socket is already bound. (EINVAL) + /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// Unlike in POSIX, in WASI the bind operation is async. This enables + /// interactive WASI hosts to inject permission prompts. Runtimes that + /// don't want to make use of this ability can simply call the native + /// `bind` as part of either `start-bind` or `finish-bind`. + /// + /// # References + /// - + /// - + /// - + /// - + #[allow(async_fn_in_trait)] + pub fn start_bind( + &self, + network: &Network, + local_address: IpSocketAddress, + ) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + use super::super::super::wasi::sockets::network::IpSocketAddress as V4; + let ( + result5_0, + result5_1, + result5_2, + result5_3, + result5_4, + result5_5, + result5_6, + result5_7, + result5_8, + result5_9, + result5_10, + result5_11, + ) = match local_address { + V4::Ipv4(e) => { + let super::super::super::wasi::sockets::network::Ipv4SocketAddress { + port: port0, + address: address0, + } = e; + let (t1_0, t1_1, t1_2, t1_3) = address0; + ( + 0i32, + _rt::as_i32(port0), + _rt::as_i32(t1_0), + _rt::as_i32(t1_1), + _rt::as_i32(t1_2), + _rt::as_i32(t1_3), + 0i32, + 0i32, + 0i32, + 0i32, + 0i32, + 0i32, + ) + } + V4::Ipv6(e) => { + let super::super::super::wasi::sockets::network::Ipv6SocketAddress { + port: port2, + flow_info: flow_info2, + address: address2, + scope_id: scope_id2, + } = e; + let (t3_0, t3_1, t3_2, t3_3, t3_4, t3_5, t3_6, t3_7) = address2; + ( + 1i32, + _rt::as_i32(port2), + _rt::as_i32(flow_info2), + _rt::as_i32(t3_0), + _rt::as_i32(t3_1), + _rt::as_i32(t3_2), + _rt::as_i32(t3_3), + _rt::as_i32(t3_4), + _rt::as_i32(t3_5), + _rt::as_i32(t3_6), + _rt::as_i32(t3_7), + _rt::as_i32(scope_id2), + ) + } + }; + let ptr6 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]udp-socket.start-bind"] + fn wit_import7( + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: *mut u8, + ); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import7( + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: *mut u8, + ) { + unreachable!() + } + wit_import7( + (self).handle() as i32, + (network).handle() as i32, + result5_0, + result5_1, + result5_2, + result5_3, + result5_4, + result5_5, + result5_6, + result5_7, + result5_8, + result5_9, + result5_10, + result5_11, + ptr6, + ); + let l8 = i32::from(*ptr6.add(0).cast::()); + let result10 = match l8 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l9 = i32::from(*ptr6.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l9 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result10 + } + } + } + impl UdpSocket { + #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] + pub fn finish_bind(&self) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]udp-socket.finish-bind"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l3 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl UdpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Set up inbound & outbound communication channels, optionally to a specific peer. + /// + /// This function only changes the local socket configuration and does not generate any network traffic. + /// On success, the `remote-address` of the socket is updated. The `local-address` may be updated as well, + /// based on the best network path to `remote-address`. + /// + /// When a `remote-address` is provided, the returned streams are limited to communicating with that specific peer: + /// - `send` can only be used to send to this destination. + /// - `receive` will only return datagrams sent from the provided `remote-address`. + /// + /// This method may be called multiple times on the same socket to change its association, but + /// only the most recently returned pair of streams will be operational. Implementations may trap if + /// the streams returned by a previous invocation haven't been dropped yet before calling `stream` again. + /// + /// The POSIX equivalent in pseudo-code is: + /// ```text + /// if (was previously connected) { + /// connect(s, AF_UNSPEC) + /// } + /// if (remote_address is Some) { + /// connect(s, remote_address) + /// } + /// ``` + /// + /// Unlike in POSIX, the socket must already be explicitly bound. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-state`: The socket is not bound. + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// + /// # References + /// - + /// - + /// - + /// - + #[allow(async_fn_in_trait)] + pub fn stream( + &self, + remote_address: Option, + ) -> Result< + (IncomingDatagramStream, OutgoingDatagramStream), + ErrorCode, + > { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 12]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 12], + ); + let ( + result6_0, + result6_1, + result6_2, + result6_3, + result6_4, + result6_5, + result6_6, + result6_7, + result6_8, + result6_9, + result6_10, + result6_11, + result6_12, + ) = match remote_address { + Some(e) => { + use super::super::super::wasi::sockets::network::IpSocketAddress as V4; + let ( + result5_0, + result5_1, + result5_2, + result5_3, + result5_4, + result5_5, + result5_6, + result5_7, + result5_8, + result5_9, + result5_10, + result5_11, + ) = match e { + V4::Ipv4(e) => { + let super::super::super::wasi::sockets::network::Ipv4SocketAddress { + port: port0, + address: address0, + } = e; + let (t1_0, t1_1, t1_2, t1_3) = address0; + ( + 0i32, + _rt::as_i32(port0), + _rt::as_i32(t1_0), + _rt::as_i32(t1_1), + _rt::as_i32(t1_2), + _rt::as_i32(t1_3), + 0i32, + 0i32, + 0i32, + 0i32, + 0i32, + 0i32, + ) + } + V4::Ipv6(e) => { + let super::super::super::wasi::sockets::network::Ipv6SocketAddress { + port: port2, + flow_info: flow_info2, + address: address2, + scope_id: scope_id2, + } = e; + let (t3_0, t3_1, t3_2, t3_3, t3_4, t3_5, t3_6, t3_7) = address2; + ( + 1i32, + _rt::as_i32(port2), + _rt::as_i32(flow_info2), + _rt::as_i32(t3_0), + _rt::as_i32(t3_1), + _rt::as_i32(t3_2), + _rt::as_i32(t3_3), + _rt::as_i32(t3_4), + _rt::as_i32(t3_5), + _rt::as_i32(t3_6), + _rt::as_i32(t3_7), + _rt::as_i32(scope_id2), + ) + } + }; + ( + 1i32, + result5_0, + result5_1, + result5_2, + result5_3, + result5_4, + result5_5, + result5_6, + result5_7, + result5_8, + result5_9, + result5_10, + result5_11, + ) + } + None => { + ( + 0i32, + 0i32, + 0i32, + 0i32, + 0i32, + 0i32, + 0i32, + 0i32, + 0i32, + 0i32, + 0i32, + 0i32, + 0i32, + ) + } + }; + let ptr7 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]udp-socket.stream"] + fn wit_import8( + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: *mut u8, + ); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import8( + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: *mut u8, + ) { + unreachable!() + } + wit_import8( + (self).handle() as i32, + result6_0, + result6_1, + result6_2, + result6_3, + result6_4, + result6_5, + result6_6, + result6_7, + result6_8, + result6_9, + result6_10, + result6_11, + result6_12, + ptr7, + ); + let l9 = i32::from(*ptr7.add(0).cast::()); + let result13 = match l9 { + 0 => { + let e = { + let l10 = *ptr7.add(4).cast::(); + let l11 = *ptr7.add(8).cast::(); + ( + IncomingDatagramStream::from_handle(l10 as u32), + OutgoingDatagramStream::from_handle(l11 as u32), + ) + }; + Ok(e) + } + 1 => { + let e = { + let l12 = i32::from(*ptr7.add(4).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l12 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result13 + } + } + } + impl UdpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Get the current bound address. + /// + /// POSIX mentions: + /// > If the socket has not been bound to a local name, the value + /// > stored in the object pointed to by `address` is unspecified. + /// + /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + #[allow(async_fn_in_trait)] + pub fn local_address(&self) -> Result { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 36]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 36], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]udp-socket.local-address"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result22 = match l2 { + 0 => { + let e = { + let l3 = i32::from(*ptr0.add(4).cast::()); + use super::super::super::wasi::sockets::network::IpSocketAddress as V20; + let v20 = match l3 { + 0 => { + let e20 = { + let l4 = i32::from(*ptr0.add(8).cast::()); + let l5 = i32::from(*ptr0.add(10).cast::()); + let l6 = i32::from(*ptr0.add(11).cast::()); + let l7 = i32::from(*ptr0.add(12).cast::()); + let l8 = i32::from(*ptr0.add(13).cast::()); + super::super::super::wasi::sockets::network::Ipv4SocketAddress { + port: l4 as u16, + address: (l5 as u8, l6 as u8, l7 as u8, l8 as u8), + } + }; + V20::Ipv4(e20) + } + n => { + debug_assert_eq!(n, 1, "invalid enum discriminant"); + let e20 = { + let l9 = i32::from(*ptr0.add(8).cast::()); + let l10 = *ptr0.add(12).cast::(); + let l11 = i32::from(*ptr0.add(16).cast::()); + let l12 = i32::from(*ptr0.add(18).cast::()); + let l13 = i32::from(*ptr0.add(20).cast::()); + let l14 = i32::from(*ptr0.add(22).cast::()); + let l15 = i32::from(*ptr0.add(24).cast::()); + let l16 = i32::from(*ptr0.add(26).cast::()); + let l17 = i32::from(*ptr0.add(28).cast::()); + let l18 = i32::from(*ptr0.add(30).cast::()); + let l19 = *ptr0.add(32).cast::(); + super::super::super::wasi::sockets::network::Ipv6SocketAddress { + port: l9 as u16, + flow_info: l10 as u32, + address: ( + l11 as u16, + l12 as u16, + l13 as u16, + l14 as u16, + l15 as u16, + l16 as u16, + l17 as u16, + l18 as u16, + ), + scope_id: l19 as u32, + } + }; + V20::Ipv6(e20) + } + }; + v20 + }; + Ok(e) + } + 1 => { + let e = { + let l21 = i32::from(*ptr0.add(4).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l21 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result22 + } + } + } + impl UdpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Get the address the socket is currently streaming to. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not streaming to a specific remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + #[allow(async_fn_in_trait)] + pub fn remote_address(&self) -> Result { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 36]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 36], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]udp-socket.remote-address"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result22 = match l2 { + 0 => { + let e = { + let l3 = i32::from(*ptr0.add(4).cast::()); + use super::super::super::wasi::sockets::network::IpSocketAddress as V20; + let v20 = match l3 { + 0 => { + let e20 = { + let l4 = i32::from(*ptr0.add(8).cast::()); + let l5 = i32::from(*ptr0.add(10).cast::()); + let l6 = i32::from(*ptr0.add(11).cast::()); + let l7 = i32::from(*ptr0.add(12).cast::()); + let l8 = i32::from(*ptr0.add(13).cast::()); + super::super::super::wasi::sockets::network::Ipv4SocketAddress { + port: l4 as u16, + address: (l5 as u8, l6 as u8, l7 as u8, l8 as u8), + } + }; + V20::Ipv4(e20) + } + n => { + debug_assert_eq!(n, 1, "invalid enum discriminant"); + let e20 = { + let l9 = i32::from(*ptr0.add(8).cast::()); + let l10 = *ptr0.add(12).cast::(); + let l11 = i32::from(*ptr0.add(16).cast::()); + let l12 = i32::from(*ptr0.add(18).cast::()); + let l13 = i32::from(*ptr0.add(20).cast::()); + let l14 = i32::from(*ptr0.add(22).cast::()); + let l15 = i32::from(*ptr0.add(24).cast::()); + let l16 = i32::from(*ptr0.add(26).cast::()); + let l17 = i32::from(*ptr0.add(28).cast::()); + let l18 = i32::from(*ptr0.add(30).cast::()); + let l19 = *ptr0.add(32).cast::(); + super::super::super::wasi::sockets::network::Ipv6SocketAddress { + port: l9 as u16, + flow_info: l10 as u32, + address: ( + l11 as u16, + l12 as u16, + l13 as u16, + l14 as u16, + l15 as u16, + l16 as u16, + l17 as u16, + l18 as u16, + ), + scope_id: l19 as u32, + } + }; + V20::Ipv6(e20) + } + }; + v20 + }; + Ok(e) + } + 1 => { + let e = { + let l21 = i32::from(*ptr0.add(4).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l21 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result22 + } + } + } + impl UdpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + #[allow(async_fn_in_trait)] + pub fn address_family(&self) -> IpAddressFamily { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]udp-socket.address-family"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((self).handle() as i32); + super::super::super::wasi::sockets::network::IpAddressFamily::_lift( + ret as u8, + ) + } + } + } + impl UdpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The TTL value must be 1 or higher. + #[allow(async_fn_in_trait)] + pub fn unicast_hop_limit(&self) -> Result { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]udp-socket.unicast-hop-limit"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result5 = match l2 { + 0 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + l3 as u8 + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l4 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + impl UdpSocket { + #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] + pub fn set_unicast_hop_limit(&self, value: u8) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]udp-socket.set-unicast-hop-limit"] + fn wit_import1(_: i32, _: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, _rt::as_i32(&value), ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l3 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl UdpSocket { + #[allow(unused_unsafe, clippy::all)] + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + #[allow(async_fn_in_trait)] + pub fn receive_buffer_size(&self) -> Result { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 16], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]udp-socket.receive-buffer-size"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result5 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(8).cast::(); + l3 as u64 + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(8).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l4 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + impl UdpSocket { + #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] + pub fn set_receive_buffer_size( + &self, + value: u64, + ) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]udp-socket.set-receive-buffer-size"] + fn wit_import1(_: i32, _: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i64, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, _rt::as_i64(&value), ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l3 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl UdpSocket { + #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] + pub fn send_buffer_size(&self) -> Result { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 16], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]udp-socket.send-buffer-size"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result5 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(8).cast::(); + l3 as u64 + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(8).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l4 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + impl UdpSocket { + #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] + pub fn set_send_buffer_size(&self, value: u64) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]udp-socket.set-send-buffer-size"] + fn wit_import1(_: i32, _: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i64, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, _rt::as_i64(&value), ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l3 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl UdpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Create a `pollable` which will resolve once the socket is ready for I/O. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + #[allow(async_fn_in_trait)] + pub fn subscribe(&self) -> Pollable { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]udp-socket.subscribe"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((self).handle() as i32); + super::super::super::wasi::io::poll::Pollable::from_handle( + ret as u32, + ) + } + } + } + impl IncomingDatagramStream { + #[allow(unused_unsafe, clippy::all)] + /// Receive messages on the socket. + /// + /// This function attempts to receive up to `max-results` datagrams on the socket without blocking. + /// The returned list may contain fewer elements than requested, but never more. + /// + /// This function returns successfully with an empty list when either: + /// - `max-results` is 0, or: + /// - `max-results` is greater than 0, but no results are immediately available. + /// This function never returns `error(would-block)`. + /// + /// # Typical errors + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + #[allow(async_fn_in_trait)] + pub fn receive( + &self, + max_results: u64, + ) -> Result<_rt::Vec, ErrorCode> { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 3 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 3 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]incoming-datagram-stream.receive"] + fn wit_import1(_: i32, _: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i64, _: *mut u8) { + unreachable!() + } + wit_import1( + (self).handle() as i32, + _rt::as_i64(&max_results), + ptr0, + ); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result28 = match l2 { + 0 => { + let e = { + let l3 = *ptr0 + .add(::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l4 = *ptr0 + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let base26 = l3; + let len26 = l4; + let mut result26 = _rt::Vec::with_capacity(len26); + for i in 0..len26 { + let base = base26 + .add(i * (32 + 2 * ::core::mem::size_of::<*const u8>())); + let e26 = { + let l5 = *base.add(0).cast::<*mut u8>(); + let l6 = *base + .add(::core::mem::size_of::<*const u8>()) + .cast::(); + let len7 = l6; + let l8 = i32::from( + *base + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + use super::super::super::wasi::sockets::network::IpSocketAddress as V25; + let v25 = match l8 { + 0 => { + let e25 = { + let l9 = i32::from( + *base + .add(4 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + let l10 = i32::from( + *base + .add(6 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + let l11 = i32::from( + *base + .add(7 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + let l12 = i32::from( + *base + .add(8 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + let l13 = i32::from( + *base + .add(9 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + super::super::super::wasi::sockets::network::Ipv4SocketAddress { + port: l9 as u16, + address: (l10 as u8, l11 as u8, l12 as u8, l13 as u8), + } + }; + V25::Ipv4(e25) + } + n => { + debug_assert_eq!(n, 1, "invalid enum discriminant"); + let e25 = { + let l14 = i32::from( + *base + .add(4 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + let l15 = *base + .add(8 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let l16 = i32::from( + *base + .add(12 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + let l17 = i32::from( + *base + .add(14 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + let l18 = i32::from( + *base + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + let l19 = i32::from( + *base + .add(18 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + let l20 = i32::from( + *base + .add(20 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + let l21 = i32::from( + *base + .add(22 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + let l22 = i32::from( + *base + .add(24 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + let l23 = i32::from( + *base + .add(26 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + let l24 = *base + .add(28 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + super::super::super::wasi::sockets::network::Ipv6SocketAddress { + port: l14 as u16, + flow_info: l15 as u32, + address: ( + l16 as u16, + l17 as u16, + l18 as u16, + l19 as u16, + l20 as u16, + l21 as u16, + l22 as u16, + l23 as u16, + ), + scope_id: l24 as u32, + } + }; + V25::Ipv6(e25) + } + }; + IncomingDatagram { + data: <_ as From< + _rt::Vec<_>, + >>::from(_rt::Vec::from_raw_parts(l5.cast(), len7, len7)), + remote_address: v25, + } + }; + result26.push(e26); + } + _rt::cabi_dealloc( + base26, + len26 * (32 + 2 * ::core::mem::size_of::<*const u8>()), + ::core::mem::size_of::<*const u8>(), + ); + result26 + }; + Ok(e) + } + 1 => { + let e = { + let l27 = i32::from( + *ptr0.add(::core::mem::size_of::<*const u8>()).cast::(), + ); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l27 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result28 + } + } + } + impl IncomingDatagramStream { + #[allow(unused_unsafe, clippy::all)] + /// Create a `pollable` which will resolve once the stream is ready to receive again. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + #[allow(async_fn_in_trait)] + pub fn subscribe(&self) -> Pollable { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]incoming-datagram-stream.subscribe"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((self).handle() as i32); + super::super::super::wasi::io::poll::Pollable::from_handle( + ret as u32, + ) + } + } + } + impl OutgoingDatagramStream { + #[allow(unused_unsafe, clippy::all)] + /// Check readiness for sending. This function never blocks. + /// + /// Returns the number of datagrams permitted for the next call to `send`, + /// or an error. Calling `send` with more datagrams than this function has + /// permitted will trap. + /// + /// When this function returns ok(0), the `subscribe` pollable will + /// become ready when this function will report at least ok(1), or an + /// error. + /// + /// Never returns `would-block`. + #[allow(async_fn_in_trait)] + pub fn check_send(&self) -> Result { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 16], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]outgoing-datagram-stream.check-send"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result5 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(8).cast::(); + l3 as u64 + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(8).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l4 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + impl OutgoingDatagramStream { + #[allow(unused_unsafe, clippy::all)] + /// Send messages on the socket. + /// + /// This function attempts to send all provided `datagrams` on the socket without blocking and + /// returns how many messages were actually sent (or queued for sending). This function never + /// returns `error(would-block)`. If none of the datagrams were able to be sent, `ok(0)` is returned. + /// + /// This function semantically behaves the same as iterating the `datagrams` list and sequentially + /// sending each individual datagram until either the end of the list has been reached or the first error occurred. + /// If at least one datagram has been sent successfully, this function never returns an error. + /// + /// If the input list is empty, the function returns `ok(0)`. + /// + /// Each call to `send` must be permitted by a preceding `check-send`. Implementations must trap if + /// either `check-send` was not called or `datagrams` contains more items than `check-send` permitted. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The socket is in "connected" mode and `remote-address` is `some` value that does not match the address passed to `stream`. (EISCONN) + /// - `invalid-argument`: The socket is not "connected" and no value for `remote-address` was provided. (EDESTADDRREQ) + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + #[allow(async_fn_in_trait)] + pub fn send( + &self, + datagrams: &[OutgoingDatagram], + ) -> Result { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 16], + ); + let vec7 = datagrams; + let len7 = vec7.len(); + let layout7 = _rt::alloc::Layout::from_size_align( + vec7.len() * (32 + 3 * ::core::mem::size_of::<*const u8>()), + ::core::mem::size_of::<*const u8>(), + ) + .unwrap(); + let (result7, _cleanup7) = wit_bindgen::rt::Cleanup::new( + layout7, + ); + for (i, e) in vec7.into_iter().enumerate() { + let base = result7 + .add(i * (32 + 3 * ::core::mem::size_of::<*const u8>())); + { + let OutgoingDatagram { + data: data0, + remote_address: remote_address0, + } = e; + let vec1 = data0; + let ptr1 = vec1.as_ptr().cast::(); + let len1 = vec1.len(); + *base + .add(::core::mem::size_of::<*const u8>()) + .cast::() = len1; + *base.add(0).cast::<*mut u8>() = ptr1.cast_mut(); + match remote_address0 { + Some(e) => { + *base + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::() = (1i32) as u8; + use super::super::super::wasi::sockets::network::IpSocketAddress as V6; + match e { + V6::Ipv4(e) => { + *base + .add(4 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::() = (0i32) as u8; + let super::super::super::wasi::sockets::network::Ipv4SocketAddress { + port: port2, + address: address2, + } = e; + *base + .add(8 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::() = (_rt::as_i32(port2)) as u16; + let (t3_0, t3_1, t3_2, t3_3) = address2; + *base + .add(10 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::() = (_rt::as_i32(t3_0)) as u8; + *base + .add(11 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::() = (_rt::as_i32(t3_1)) as u8; + *base + .add(12 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::() = (_rt::as_i32(t3_2)) as u8; + *base + .add(13 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::() = (_rt::as_i32(t3_3)) as u8; + } + V6::Ipv6(e) => { + *base + .add(4 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::() = (1i32) as u8; + let super::super::super::wasi::sockets::network::Ipv6SocketAddress { + port: port4, + flow_info: flow_info4, + address: address4, + scope_id: scope_id4, + } = e; + *base + .add(8 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::() = (_rt::as_i32(port4)) as u16; + *base + .add(12 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::() = _rt::as_i32(flow_info4); + let (t5_0, t5_1, t5_2, t5_3, t5_4, t5_5, t5_6, t5_7) = address4; + *base + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::() = (_rt::as_i32(t5_0)) as u16; + *base + .add(18 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::() = (_rt::as_i32(t5_1)) as u16; + *base + .add(20 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::() = (_rt::as_i32(t5_2)) as u16; + *base + .add(22 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::() = (_rt::as_i32(t5_3)) as u16; + *base + .add(24 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::() = (_rt::as_i32(t5_4)) as u16; + *base + .add(26 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::() = (_rt::as_i32(t5_5)) as u16; + *base + .add(28 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::() = (_rt::as_i32(t5_6)) as u16; + *base + .add(30 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::() = (_rt::as_i32(t5_7)) as u16; + *base + .add(32 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::() = _rt::as_i32(scope_id4); + } + } + } + None => { + *base + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::() = (0i32) as u8; + } + }; + } + } + let ptr8 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]outgoing-datagram-stream.send"] + fn wit_import9(_: i32, _: *mut u8, _: usize, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import9( + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import9((self).handle() as i32, result7, len7, ptr8); + let l10 = i32::from(*ptr8.add(0).cast::()); + let result13 = match l10 { + 0 => { + let e = { + let l11 = *ptr8.add(8).cast::(); + l11 as u64 + }; + Ok(e) + } + 1 => { + let e = { + let l12 = i32::from(*ptr8.add(8).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l12 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result13 + } + } + } + impl OutgoingDatagramStream { + #[allow(unused_unsafe, clippy::all)] + /// Create a `pollable` which will resolve once the stream is ready to send again. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + #[allow(async_fn_in_trait)] + pub fn subscribe(&self) -> Pollable { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]outgoing-datagram-stream.subscribe"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((self).handle() as i32); + super::super::super::wasi::io::poll::Pollable::from_handle( + ret as u32, + ) + } + } + } + } + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod udp_create_socket { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + pub type ErrorCode = super::super::super::wasi::sockets::network::ErrorCode; + pub type IpAddressFamily = super::super::super::wasi::sockets::network::IpAddressFamily; + pub type UdpSocket = super::super::super::wasi::sockets::udp::UdpSocket; + #[allow(unused_unsafe, clippy::all)] + /// Create a new UDP socket. + /// + /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. + /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// + /// This function does not require a network capability handle. This is considered to be safe because + /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind` is called, + /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. + /// + /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + /// + /// # Typical errors + /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References: + /// - + /// - + /// - + /// - + #[allow(async_fn_in_trait)] + pub fn create_udp_socket( + address_family: IpAddressFamily, + ) -> Result { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 8]); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/udp-create-socket@0.2.9")] + unsafe extern "C" { + #[link_name = "create-udp-socket"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1(address_family.clone() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result5 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(4).cast::(); + super::super::super::wasi::sockets::udp::UdpSocket::from_handle( + l3 as u32, + ) + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(4).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l4 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod tcp { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + pub type InputStream = super::super::super::wasi::io::streams::InputStream; + pub type OutputStream = super::super::super::wasi::io::streams::OutputStream; + pub type Pollable = super::super::super::wasi::io::poll::Pollable; + pub type Duration = super::super::super::wasi::clocks::monotonic_clock::Duration; + pub type Network = super::super::super::wasi::sockets::network::Network; + pub type ErrorCode = super::super::super::wasi::sockets::network::ErrorCode; + pub type IpSocketAddress = super::super::super::wasi::sockets::network::IpSocketAddress; + pub type IpAddressFamily = super::super::super::wasi::sockets::network::IpAddressFamily; + #[repr(u8)] + #[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] + pub enum ShutdownType { + /// Similar to `SHUT_RD` in POSIX. + Receive, + /// Similar to `SHUT_WR` in POSIX. + Send, + /// Similar to `SHUT_RDWR` in POSIX. + Both, + } + impl ::core::fmt::Debug for ShutdownType { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + match self { + ShutdownType::Receive => { + f.debug_tuple("ShutdownType::Receive").finish() + } + ShutdownType::Send => { + f.debug_tuple("ShutdownType::Send").finish() + } + ShutdownType::Both => { + f.debug_tuple("ShutdownType::Both").finish() + } + } + } + } + impl ShutdownType { + #[doc(hidden)] + pub unsafe fn _lift(val: u8) -> ShutdownType { + if !cfg!(debug_assertions) { + return unsafe { ::core::mem::transmute(val) }; + } + match val { + 0 => ShutdownType::Receive, + 1 => ShutdownType::Send, + 2 => ShutdownType::Both, + _ => panic!("invalid enum discriminant"), + } + } + } + /// A TCP socket resource. + /// + /// The socket can be in one of the following states: + /// - `unbound` + /// - `bind-in-progress` + /// - `bound` (See note below) + /// - `listen-in-progress` + /// - `listening` + /// - `connect-in-progress` + /// - `connected` + /// - `closed` + /// See + /// for more information. + /// + /// Note: Except where explicitly mentioned, whenever this documentation uses + /// the term "bound" without backticks it actually means: in the `bound` state *or higher*. + /// (i.e. `bound`, `listen-in-progress`, `listening`, `connect-in-progress` or `connected`) + /// + /// In addition to the general error codes documented on the + /// `network::error-code` type, TCP socket methods may always return + /// `error(invalid-state)` when in the `closed` state. + #[derive(Debug)] + #[repr(transparent)] + pub struct TcpSocket { + handle: _rt::Resource, + } + impl TcpSocket { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for TcpSocket { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]tcp-socket"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the TCP/UDP port is zero, the socket will be bound to a random free port. + /// + /// Bind can be attempted multiple times on the same socket, even with + /// different arguments on each iteration. But never concurrently and + /// only as long as the previous bind failed. Once a bind succeeds, the + /// binding can't be changed anymore. + /// + /// # Typical errors + /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) + /// - `invalid-argument`: `local-address` is not a unicast address. (EINVAL) + /// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address. (EINVAL) + /// - `invalid-state`: The socket is already bound. (EINVAL) + /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// When binding to a non-zero port, this bind operation shouldn't be affected by the TIME_WAIT + /// state of a recently closed socket on the same local address. In practice this means that the SO_REUSEADDR + /// socket option should be set implicitly on all platforms, except on Windows where this is the default behavior + /// and SO_REUSEADDR performs something different entirely. + /// + /// Unlike in POSIX, in WASI the bind operation is async. This enables + /// interactive WASI hosts to inject permission prompts. Runtimes that + /// don't want to make use of this ability can simply call the native + /// `bind` as part of either `start-bind` or `finish-bind`. + /// + /// # References + /// - + /// - + /// - + /// - + #[allow(async_fn_in_trait)] + pub fn start_bind( + &self, + network: &Network, + local_address: IpSocketAddress, + ) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + use super::super::super::wasi::sockets::network::IpSocketAddress as V4; + let ( + result5_0, + result5_1, + result5_2, + result5_3, + result5_4, + result5_5, + result5_6, + result5_7, + result5_8, + result5_9, + result5_10, + result5_11, + ) = match local_address { + V4::Ipv4(e) => { + let super::super::super::wasi::sockets::network::Ipv4SocketAddress { + port: port0, + address: address0, + } = e; + let (t1_0, t1_1, t1_2, t1_3) = address0; + ( + 0i32, + _rt::as_i32(port0), + _rt::as_i32(t1_0), + _rt::as_i32(t1_1), + _rt::as_i32(t1_2), + _rt::as_i32(t1_3), + 0i32, + 0i32, + 0i32, + 0i32, + 0i32, + 0i32, + ) + } + V4::Ipv6(e) => { + let super::super::super::wasi::sockets::network::Ipv6SocketAddress { + port: port2, + flow_info: flow_info2, + address: address2, + scope_id: scope_id2, + } = e; + let (t3_0, t3_1, t3_2, t3_3, t3_4, t3_5, t3_6, t3_7) = address2; + ( + 1i32, + _rt::as_i32(port2), + _rt::as_i32(flow_info2), + _rt::as_i32(t3_0), + _rt::as_i32(t3_1), + _rt::as_i32(t3_2), + _rt::as_i32(t3_3), + _rt::as_i32(t3_4), + _rt::as_i32(t3_5), + _rt::as_i32(t3_6), + _rt::as_i32(t3_7), + _rt::as_i32(scope_id2), + ) + } + }; + let ptr6 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.start-bind"] + fn wit_import7( + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: *mut u8, + ); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import7( + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: *mut u8, + ) { + unreachable!() + } + wit_import7( + (self).handle() as i32, + (network).handle() as i32, + result5_0, + result5_1, + result5_2, + result5_3, + result5_4, + result5_5, + result5_6, + result5_7, + result5_8, + result5_9, + result5_10, + result5_11, + ptr6, + ); + let l8 = i32::from(*ptr6.add(0).cast::()); + let result10 = match l8 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l9 = i32::from(*ptr6.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l9 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result10 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] + pub fn finish_bind(&self) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.finish-bind"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l3 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Connect to a remote endpoint. + /// + /// On success: + /// - the socket is transitioned into the `connected` state. + /// - a pair of streams is returned that can be used to read & write to the connection + /// + /// After a failed connection attempt, the socket will be in the `closed` + /// state and the only valid action left is to `drop` the socket. A single + /// socket can not be used to connect more than once. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS) + /// - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address. (EINVAL, EADDRNOTAVAIL on Illumos) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) + /// - `invalid-argument`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + /// - `invalid-state`: The socket is already in the `connected` state. (EISCONN) + /// - `invalid-state`: The socket is already in the `listening` state. (EOPNOTSUPP, EINVAL on Windows) + /// - `timeout`: Connection timed out. (ETIMEDOUT) + /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) + /// - `connection-reset`: The connection was reset. (ECONNRESET) + /// - `connection-aborted`: The connection was aborted. (ECONNABORTED) + /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `not-in-progress`: A connect operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// The POSIX equivalent of `start-connect` is the regular `connect` syscall. + /// Because all WASI sockets are non-blocking this is expected to return + /// EINPROGRESS, which should be translated to `ok()` in WASI. + /// + /// The POSIX equivalent of `finish-connect` is a `poll` for event `POLLOUT` + /// with a timeout of 0 on the socket descriptor. Followed by a check for + /// the `SO_ERROR` socket option, in case the poll signaled readiness. + /// + /// # References + /// - + /// - + /// - + /// - + #[allow(async_fn_in_trait)] + pub fn start_connect( + &self, + network: &Network, + remote_address: IpSocketAddress, + ) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + use super::super::super::wasi::sockets::network::IpSocketAddress as V4; + let ( + result5_0, + result5_1, + result5_2, + result5_3, + result5_4, + result5_5, + result5_6, + result5_7, + result5_8, + result5_9, + result5_10, + result5_11, + ) = match remote_address { + V4::Ipv4(e) => { + let super::super::super::wasi::sockets::network::Ipv4SocketAddress { + port: port0, + address: address0, + } = e; + let (t1_0, t1_1, t1_2, t1_3) = address0; + ( + 0i32, + _rt::as_i32(port0), + _rt::as_i32(t1_0), + _rt::as_i32(t1_1), + _rt::as_i32(t1_2), + _rt::as_i32(t1_3), + 0i32, + 0i32, + 0i32, + 0i32, + 0i32, + 0i32, + ) + } + V4::Ipv6(e) => { + let super::super::super::wasi::sockets::network::Ipv6SocketAddress { + port: port2, + flow_info: flow_info2, + address: address2, + scope_id: scope_id2, + } = e; + let (t3_0, t3_1, t3_2, t3_3, t3_4, t3_5, t3_6, t3_7) = address2; + ( + 1i32, + _rt::as_i32(port2), + _rt::as_i32(flow_info2), + _rt::as_i32(t3_0), + _rt::as_i32(t3_1), + _rt::as_i32(t3_2), + _rt::as_i32(t3_3), + _rt::as_i32(t3_4), + _rt::as_i32(t3_5), + _rt::as_i32(t3_6), + _rt::as_i32(t3_7), + _rt::as_i32(scope_id2), + ) + } + }; + let ptr6 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.start-connect"] + fn wit_import7( + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: *mut u8, + ); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import7( + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: i32, + _: *mut u8, + ) { + unreachable!() + } + wit_import7( + (self).handle() as i32, + (network).handle() as i32, + result5_0, + result5_1, + result5_2, + result5_3, + result5_4, + result5_5, + result5_6, + result5_7, + result5_8, + result5_9, + result5_10, + result5_11, + ptr6, + ); + let l8 = i32::from(*ptr6.add(0).cast::()); + let result10 = match l8 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l9 = i32::from(*ptr6.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l9 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result10 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] + pub fn finish_connect( + &self, + ) -> Result<(InputStream, OutputStream), ErrorCode> { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 12]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 12], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.finish-connect"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result6 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(4).cast::(); + let l4 = *ptr0.add(8).cast::(); + ( + super::super::super::wasi::io::streams::InputStream::from_handle( + l3 as u32, + ), + super::super::super::wasi::io::streams::OutputStream::from_handle( + l4 as u32, + ), + ) + }; + Ok(e) + } + 1 => { + let e = { + let l5 = i32::from(*ptr0.add(4).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l5 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result6 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Start listening for new connections. + /// + /// Transitions the socket into the `listening` state. + /// + /// Unlike POSIX, the socket must already be explicitly bound. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. (EDESTADDRREQ) + /// - `invalid-state`: The socket is already in the `connected` state. (EISCONN, EINVAL on BSD) + /// - `invalid-state`: The socket is already in the `listening` state. + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) + /// - `not-in-progress`: A listen operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// Unlike in POSIX, in WASI the listen operation is async. This enables + /// interactive WASI hosts to inject permission prompts. Runtimes that + /// don't want to make use of this ability can simply call the native + /// `listen` as part of either `start-listen` or `finish-listen`. + /// + /// # References + /// - + /// - + /// - + /// - + #[allow(async_fn_in_trait)] + pub fn start_listen(&self) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.start-listen"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l3 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] + pub fn finish_listen(&self) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.finish-listen"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l3 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Accept a new client socket. + /// + /// The returned socket is bound and in the `connected` state. The following properties are inherited from the listener socket: + /// - `address-family` + /// - `keep-alive-enabled` + /// - `keep-alive-idle-time` + /// - `keep-alive-interval` + /// - `keep-alive-count` + /// - `hop-limit` + /// - `receive-buffer-size` + /// - `send-buffer-size` + /// + /// On success, this function returns the newly accepted client socket along with + /// a pair of streams that can be used to read & write to the connection. + /// + /// # Typical errors + /// - `invalid-state`: Socket is not in the `listening` state. (EINVAL) + /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) + /// - `connection-aborted`: An incoming connection was pending, but was terminated by the client before this listener could accept it. (ECONNABORTED) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References + /// - + /// - + /// - + /// - + #[allow(async_fn_in_trait)] + pub fn accept( + &self, + ) -> Result<(TcpSocket, InputStream, OutputStream), ErrorCode> { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 16], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.accept"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result7 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(4).cast::(); + let l4 = *ptr0.add(8).cast::(); + let l5 = *ptr0.add(12).cast::(); + ( + TcpSocket::from_handle(l3 as u32), + super::super::super::wasi::io::streams::InputStream::from_handle( + l4 as u32, + ), + super::super::super::wasi::io::streams::OutputStream::from_handle( + l5 as u32, + ), + ) + }; + Ok(e) + } + 1 => { + let e = { + let l6 = i32::from(*ptr0.add(4).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l6 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result7 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Get the bound local address. + /// + /// POSIX mentions: + /// > If the socket has not been bound to a local name, the value + /// > stored in the object pointed to by `address` is unspecified. + /// + /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + #[allow(async_fn_in_trait)] + pub fn local_address(&self) -> Result { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 36]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 36], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.local-address"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result22 = match l2 { + 0 => { + let e = { + let l3 = i32::from(*ptr0.add(4).cast::()); + use super::super::super::wasi::sockets::network::IpSocketAddress as V20; + let v20 = match l3 { + 0 => { + let e20 = { + let l4 = i32::from(*ptr0.add(8).cast::()); + let l5 = i32::from(*ptr0.add(10).cast::()); + let l6 = i32::from(*ptr0.add(11).cast::()); + let l7 = i32::from(*ptr0.add(12).cast::()); + let l8 = i32::from(*ptr0.add(13).cast::()); + super::super::super::wasi::sockets::network::Ipv4SocketAddress { + port: l4 as u16, + address: (l5 as u8, l6 as u8, l7 as u8, l8 as u8), + } + }; + V20::Ipv4(e20) + } + n => { + debug_assert_eq!(n, 1, "invalid enum discriminant"); + let e20 = { + let l9 = i32::from(*ptr0.add(8).cast::()); + let l10 = *ptr0.add(12).cast::(); + let l11 = i32::from(*ptr0.add(16).cast::()); + let l12 = i32::from(*ptr0.add(18).cast::()); + let l13 = i32::from(*ptr0.add(20).cast::()); + let l14 = i32::from(*ptr0.add(22).cast::()); + let l15 = i32::from(*ptr0.add(24).cast::()); + let l16 = i32::from(*ptr0.add(26).cast::()); + let l17 = i32::from(*ptr0.add(28).cast::()); + let l18 = i32::from(*ptr0.add(30).cast::()); + let l19 = *ptr0.add(32).cast::(); + super::super::super::wasi::sockets::network::Ipv6SocketAddress { + port: l9 as u16, + flow_info: l10 as u32, + address: ( + l11 as u16, + l12 as u16, + l13 as u16, + l14 as u16, + l15 as u16, + l16 as u16, + l17 as u16, + l18 as u16, + ), + scope_id: l19 as u32, + } + }; + V20::Ipv6(e20) + } + }; + v20 + }; + Ok(e) + } + 1 => { + let e = { + let l21 = i32::from(*ptr0.add(4).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l21 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result22 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Get the remote address. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + #[allow(async_fn_in_trait)] + pub fn remote_address(&self) -> Result { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 36]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 36], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.remote-address"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result22 = match l2 { + 0 => { + let e = { + let l3 = i32::from(*ptr0.add(4).cast::()); + use super::super::super::wasi::sockets::network::IpSocketAddress as V20; + let v20 = match l3 { + 0 => { + let e20 = { + let l4 = i32::from(*ptr0.add(8).cast::()); + let l5 = i32::from(*ptr0.add(10).cast::()); + let l6 = i32::from(*ptr0.add(11).cast::()); + let l7 = i32::from(*ptr0.add(12).cast::()); + let l8 = i32::from(*ptr0.add(13).cast::()); + super::super::super::wasi::sockets::network::Ipv4SocketAddress { + port: l4 as u16, + address: (l5 as u8, l6 as u8, l7 as u8, l8 as u8), + } + }; + V20::Ipv4(e20) + } + n => { + debug_assert_eq!(n, 1, "invalid enum discriminant"); + let e20 = { + let l9 = i32::from(*ptr0.add(8).cast::()); + let l10 = *ptr0.add(12).cast::(); + let l11 = i32::from(*ptr0.add(16).cast::()); + let l12 = i32::from(*ptr0.add(18).cast::()); + let l13 = i32::from(*ptr0.add(20).cast::()); + let l14 = i32::from(*ptr0.add(22).cast::()); + let l15 = i32::from(*ptr0.add(24).cast::()); + let l16 = i32::from(*ptr0.add(26).cast::()); + let l17 = i32::from(*ptr0.add(28).cast::()); + let l18 = i32::from(*ptr0.add(30).cast::()); + let l19 = *ptr0.add(32).cast::(); + super::super::super::wasi::sockets::network::Ipv6SocketAddress { + port: l9 as u16, + flow_info: l10 as u32, + address: ( + l11 as u16, + l12 as u16, + l13 as u16, + l14 as u16, + l15 as u16, + l16 as u16, + l17 as u16, + l18 as u16, + ), + scope_id: l19 as u32, + } + }; + V20::Ipv6(e20) + } + }; + v20 + }; + Ok(e) + } + 1 => { + let e = { + let l21 = i32::from(*ptr0.add(4).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l21 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result22 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Whether the socket is in the `listening` state. + /// + /// Equivalent to the SO_ACCEPTCONN socket option. + #[allow(async_fn_in_trait)] + pub fn is_listening(&self) -> bool { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.is-listening"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((self).handle() as i32); + _rt::bool_lift(ret as u8) + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + #[allow(async_fn_in_trait)] + pub fn address_family(&self) -> IpAddressFamily { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.address-family"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((self).handle() as i32); + super::super::super::wasi::sockets::network::IpAddressFamily::_lift( + ret as u8, + ) + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Hints the desired listen queue size. Implementations are free to ignore this. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// + /// # Typical errors + /// - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen. + /// - `invalid-argument`: (set) The provided value was 0. + /// - `invalid-state`: (set) The socket is in the `connect-in-progress` or `connected` state. + #[allow(async_fn_in_trait)] + pub fn set_listen_backlog_size( + &self, + value: u64, + ) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.set-listen-backlog-size"] + fn wit_import1(_: i32, _: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i64, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, _rt::as_i64(&value), ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l3 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Enables or disables keepalive. + /// + /// The keepalive behavior can be adjusted using: + /// - `keep-alive-idle-time` + /// - `keep-alive-interval` + /// - `keep-alive-count` + /// These properties can be configured while `keep-alive-enabled` is false, but only come into effect when `keep-alive-enabled` is true. + /// + /// Equivalent to the SO_KEEPALIVE socket option. + #[allow(async_fn_in_trait)] + pub fn keep_alive_enabled(&self) -> Result { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.keep-alive-enabled"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result5 = match l2 { + 0 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + _rt::bool_lift(l3 as u8) + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l4 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] + pub fn set_keep_alive_enabled( + &self, + value: bool, + ) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.set-keep-alive-enabled"] + fn wit_import1(_: i32, _: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i32, _: *mut u8) { + unreachable!() + } + wit_import1( + (self).handle() as i32, + match &value { + true => 1, + false => 0, + }, + ptr0, + ); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l3 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Amount of time the connection has to be idle before TCP starts sending keepalive packets. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPIDLE socket option. (TCP_KEEPALIVE on MacOS) + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + #[allow(async_fn_in_trait)] + pub fn keep_alive_idle_time(&self) -> Result { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 16], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.keep-alive-idle-time"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result5 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(8).cast::(); + l3 as u64 + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(8).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l4 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] + pub fn set_keep_alive_idle_time( + &self, + value: Duration, + ) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.set-keep-alive-idle-time"] + fn wit_import1(_: i32, _: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i64, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, _rt::as_i64(value), ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l3 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + /// The time between keepalive packets. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPINTVL socket option. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + #[allow(async_fn_in_trait)] + pub fn keep_alive_interval(&self) -> Result { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 16], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.keep-alive-interval"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result5 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(8).cast::(); + l3 as u64 + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(8).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l4 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] + pub fn set_keep_alive_interval( + &self, + value: Duration, + ) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.set-keep-alive-interval"] + fn wit_import1(_: i32, _: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i64, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, _rt::as_i64(value), ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l3 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + /// The maximum amount of keepalive packets TCP should send before aborting the connection. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPCNT socket option. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + #[allow(async_fn_in_trait)] + pub fn keep_alive_count(&self) -> Result { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 8], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.keep-alive-count"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result5 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(4).cast::(); + l3 as u32 + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(4).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l4 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] + pub fn set_keep_alive_count(&self, value: u32) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.set-keep-alive-count"] + fn wit_import1(_: i32, _: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, _rt::as_i32(&value), ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l3 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The TTL value must be 1 or higher. + #[allow(async_fn_in_trait)] + pub fn hop_limit(&self) -> Result { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.hop-limit"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result5 = match l2 { + 0 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + l3 as u8 + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l4 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] + pub fn set_hop_limit(&self, value: u8) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.set-hop-limit"] + fn wit_import1(_: i32, _: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, _rt::as_i32(&value), ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l3 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + #[allow(async_fn_in_trait)] + pub fn receive_buffer_size(&self) -> Result { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 16], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.receive-buffer-size"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result5 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(8).cast::(); + l3 as u64 + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(8).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l4 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] + pub fn set_receive_buffer_size( + &self, + value: u64, + ) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.set-receive-buffer-size"] + fn wit_import1(_: i32, _: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i64, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, _rt::as_i64(&value), ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l3 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] + pub fn send_buffer_size(&self) -> Result { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 16], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.send-buffer-size"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result5 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(8).cast::(); + l3 as u64 + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(8).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l4 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] + pub fn set_send_buffer_size(&self, value: u64) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.set-send-buffer-size"] + fn wit_import1(_: i32, _: i64, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i64, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, _rt::as_i64(&value), ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l3 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Create a `pollable` which can be used to poll for, or block on, + /// completion of any of the asynchronous operations of this socket. + /// + /// When `finish-bind`, `finish-listen`, `finish-connect` or `accept` + /// return `error(would-block)`, this pollable can be used to wait for + /// their success or failure, after which the method can be retried. + /// + /// The pollable is not limited to the async operation that happens to be + /// in progress at the time of calling `subscribe` (if any). Theoretically, + /// `subscribe` only has to be called once per socket and can then be + /// (re)used for the remainder of the socket's lifetime. + /// + /// See + /// for more information. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + #[allow(async_fn_in_trait)] + pub fn subscribe(&self) -> Pollable { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.subscribe"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((self).handle() as i32); + super::super::super::wasi::io::poll::Pollable::from_handle( + ret as u32, + ) + } + } + } + impl TcpSocket { + #[allow(unused_unsafe, clippy::all)] + /// Initiate a graceful shutdown. + /// + /// - `receive`: The socket is not expecting to receive any data from + /// the peer. The `input-stream` associated with this socket will be + /// closed. Any data still in the receive queue at time of calling + /// this method will be discarded. + /// - `send`: The socket has no more data to send to the peer. The `output-stream` + /// associated with this socket will be closed and a FIN packet will be sent. + /// - `both`: Same effect as `receive` & `send` combined. + /// + /// This function is idempotent; shutting down a direction more than once + /// has no effect and returns `ok`. + /// + /// The shutdown function does not close (drop) the socket. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + #[allow(async_fn_in_trait)] + pub fn shutdown( + &self, + shutdown_type: ShutdownType, + ) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]tcp-socket.shutdown"] + fn wit_import1(_: i32, _: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i32, _: *mut u8) { + unreachable!() + } + wit_import1( + (self).handle() as i32, + shutdown_type.clone() as i32, + ptr0, + ); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(1).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l3 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + } + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod tcp_create_socket { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + pub type ErrorCode = super::super::super::wasi::sockets::network::ErrorCode; + pub type IpAddressFamily = super::super::super::wasi::sockets::network::IpAddressFamily; + pub type TcpSocket = super::super::super::wasi::sockets::tcp::TcpSocket; + #[allow(unused_unsafe, clippy::all)] + /// Create a new TCP socket. + /// + /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. + /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// + /// This function does not require a network capability handle. This is considered to be safe because + /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` + /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. + /// + /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + /// + /// # Typical errors + /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References + /// - + /// - + /// - + /// - + #[allow(async_fn_in_trait)] + pub fn create_tcp_socket( + address_family: IpAddressFamily, + ) -> Result { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 8]); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/tcp-create-socket@0.2.9")] + unsafe extern "C" { + #[link_name = "create-tcp-socket"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1(address_family.clone() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result5 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(4).cast::(); + super::super::super::wasi::sockets::tcp::TcpSocket::from_handle( + l3 as u32, + ) + }; + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(4).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l4 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result5 + } + } + } + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod ip_name_lookup { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + pub type Pollable = super::super::super::wasi::io::poll::Pollable; + pub type Network = super::super::super::wasi::sockets::network::Network; + pub type ErrorCode = super::super::super::wasi::sockets::network::ErrorCode; + pub type IpAddress = super::super::super::wasi::sockets::network::IpAddress; + #[derive(Debug)] + #[repr(transparent)] + pub struct ResolveAddressStream { + handle: _rt::Resource, + } + impl ResolveAddressStream { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for ResolveAddressStream { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/ip-name-lookup@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]resolve-address-stream"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + impl ResolveAddressStream { + #[allow(unused_unsafe, clippy::all)] + /// Returns the next address from the resolver. + /// + /// This function should be called multiple times. On each call, it will + /// return the next address in connection order preference. If all + /// addresses have been exhausted, this function returns `none`. + /// + /// This function never returns IPv4-mapped IPv6 addresses. + /// + /// # Typical errors + /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) + /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) + /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) + /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) + #[allow(async_fn_in_trait)] + pub fn resolve_next_address( + &self, + ) -> Result, ErrorCode> { + unsafe { + #[repr(align(2))] + struct RetArea([::core::mem::MaybeUninit; 22]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 22], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/ip-name-lookup@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]resolve-address-stream.resolve-next-address"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result19 = match l2 { + 0 => { + let e = { + let l3 = i32::from(*ptr0.add(2).cast::()); + match l3 { + 0 => None, + 1 => { + let e = { + let l4 = i32::from(*ptr0.add(4).cast::()); + use super::super::super::wasi::sockets::network::IpAddress as V17; + let v17 = match l4 { + 0 => { + let e17 = { + let l5 = i32::from(*ptr0.add(6).cast::()); + let l6 = i32::from(*ptr0.add(7).cast::()); + let l7 = i32::from(*ptr0.add(8).cast::()); + let l8 = i32::from(*ptr0.add(9).cast::()); + (l5 as u8, l6 as u8, l7 as u8, l8 as u8) + }; + V17::Ipv4(e17) + } + n => { + debug_assert_eq!(n, 1, "invalid enum discriminant"); + let e17 = { + let l9 = i32::from(*ptr0.add(6).cast::()); + let l10 = i32::from(*ptr0.add(8).cast::()); + let l11 = i32::from(*ptr0.add(10).cast::()); + let l12 = i32::from(*ptr0.add(12).cast::()); + let l13 = i32::from(*ptr0.add(14).cast::()); + let l14 = i32::from(*ptr0.add(16).cast::()); + let l15 = i32::from(*ptr0.add(18).cast::()); + let l16 = i32::from(*ptr0.add(20).cast::()); + ( + l9 as u16, + l10 as u16, + l11 as u16, + l12 as u16, + l13 as u16, + l14 as u16, + l15 as u16, + l16 as u16, + ) + }; + V17::Ipv6(e17) + } + }; + v17 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + Ok(e) + } + 1 => { + let e = { + let l18 = i32::from(*ptr0.add(2).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l18 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result19 + } + } + } + impl ResolveAddressStream { + #[allow(unused_unsafe, clippy::all)] + /// Create a `pollable` which will resolve once the stream is ready for I/O. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + #[allow(async_fn_in_trait)] + pub fn subscribe(&self) -> Pollable { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/ip-name-lookup@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]resolve-address-stream.subscribe"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((self).handle() as i32); + super::super::super::wasi::io::poll::Pollable::from_handle( + ret as u32, + ) + } + } + } + #[allow(unused_unsafe, clippy::all)] + /// Resolve an internet host name to a list of IP addresses. + /// + /// Unicode domain names are automatically converted to ASCII using IDNA encoding. + /// If the input is an IP address string, the address is parsed and returned + /// as-is without making any external requests. + /// + /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. + /// + /// This function never blocks. It either immediately fails or immediately + /// returns successfully with a `resolve-address-stream` that can be used + /// to (asynchronously) fetch the results. + /// + /// # Typical errors + /// - `invalid-argument`: `name` is a syntactically invalid domain name or IP address. + /// + /// # References: + /// - + /// - + /// - + /// - + #[allow(async_fn_in_trait)] + pub fn resolve_addresses( + network: &Network, + name: &str, + ) -> Result { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 8]); + let vec0 = name; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let ptr1 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:sockets/ip-name-lookup@0.2.9")] + unsafe extern "C" { + #[link_name = "resolve-addresses"] + fn wit_import2(_: i32, _: *mut u8, _: usize, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import2( + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import2((network).handle() as i32, ptr0.cast_mut(), len0, ptr1); + let l3 = i32::from(*ptr1.add(0).cast::()); + let result6 = match l3 { + 0 => { + let e = { + let l4 = *ptr1.add(4).cast::(); + ResolveAddressStream::from_handle(l4 as u32) + }; + Ok(e) + } + 1 => { + let e = { + let l5 = i32::from(*ptr1.add(4).cast::()); + super::super::super::wasi::sockets::network::ErrorCode::_lift( + l5 as u8, + ) + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result6 + } + } + } + } +} +#[rustfmt::skip] +mod _rt { + #![allow(dead_code, unused_imports, clippy::all)] + pub use alloc_crate::vec::Vec; + pub use alloc_crate::string::String; + pub unsafe fn string_lift(bytes: Vec) -> String { + if cfg!(debug_assertions) { + String::from_utf8(bytes).unwrap() + } else { + unsafe { String::from_utf8_unchecked(bytes) } + } + } + pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { + if size == 0 { + return; + } + unsafe { + let layout = alloc::Layout::from_size_align_unchecked(size, align); + alloc::dealloc(ptr, layout); + } + } + pub unsafe fn invalid_enum_discriminant() -> T { + if cfg!(debug_assertions) { + panic!("invalid enum discriminant") + } else { + unsafe { core::hint::unreachable_unchecked() } + } + } + use core::fmt; + use core::marker; + use core::sync::atomic::{AtomicU32, Ordering::Relaxed}; + /// A type which represents a component model resource, either imported or + /// exported into this component. + /// + /// This is a low-level wrapper which handles the lifetime of the resource + /// (namely this has a destructor). The `T` provided defines the component model + /// intrinsics that this wrapper uses. + /// + /// One of the chief purposes of this type is to provide `Deref` implementations + /// to access the underlying data when it is owned. + /// + /// This type is primarily used in generated code for exported and imported + /// resources. + #[repr(transparent)] + pub struct Resource { + handle: AtomicU32, + _marker: marker::PhantomData, + } + /// A trait which all wasm resources implement, namely providing the ability to + /// drop a resource. + /// + /// This generally is implemented by generated code, not user-facing code. + #[allow(clippy::missing_safety_doc)] + pub unsafe trait WasmResource { + /// Invokes the `[resource-drop]...` intrinsic. + unsafe fn drop(handle: u32); + } + impl Resource { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + debug_assert!(handle != 0 && handle != u32::MAX); + Self { + handle: AtomicU32::new(handle), + _marker: marker::PhantomData, + } + } + /// Takes ownership of the handle owned by `resource`. + /// + /// Note that this ideally would be `into_handle` taking `Resource` by + /// ownership. The code generator does not enable that in all situations, + /// unfortunately, so this is provided instead. + /// + /// Also note that `take_handle` is in theory only ever called on values + /// owned by a generated function. For example a generated function might + /// take `Resource` as an argument but then call `take_handle` on a + /// reference to that argument. In that sense the dynamic nature of + /// `take_handle` should only be exposed internally to generated code, not + /// to user code. + #[doc(hidden)] + pub fn take_handle(resource: &Resource) -> u32 { + resource.handle.swap(u32::MAX, Relaxed) + } + #[doc(hidden)] + pub fn handle(resource: &Resource) -> u32 { + resource.handle.load(Relaxed) + } + } + impl fmt::Debug for Resource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Resource").field("handle", &self.handle).finish() + } + } + impl Drop for Resource { + fn drop(&mut self) { + unsafe { + match self.handle.load(Relaxed) { + u32::MAX => {} + other => T::drop(other), + } + } + } + } + pub unsafe fn bool_lift(val: u8) -> bool { + if cfg!(debug_assertions) { + match val { + 0 => false, + 1 => true, + _ => panic!("invalid bool discriminant"), + } + } else { + val != 0 + } + } + pub use alloc_crate::alloc; + pub fn as_i64(t: T) -> i64 { + t.as_i64() + } + pub trait AsI64 { + fn as_i64(self) -> i64; + } + impl<'a, T: Copy + AsI64> AsI64 for &'a T { + fn as_i64(self) -> i64 { + (*self).as_i64() + } + } + impl AsI64 for i64 { + #[inline] + fn as_i64(self) -> i64 { + self as i64 + } + } + impl AsI64 for u64 { + #[inline] + fn as_i64(self) -> i64 { + self as i64 + } + } + pub fn as_i32(t: T) -> i32 { + t.as_i32() + } + pub trait AsI32 { + fn as_i32(self) -> i32; + } + impl<'a, T: Copy + AsI32> AsI32 for &'a T { + fn as_i32(self) -> i32 { + (*self).as_i32() + } + } + impl AsI32 for i32 { + #[inline] + fn as_i32(self) -> i32 { + self as i32 + } + } + impl AsI32 for u32 { + #[inline] + fn as_i32(self) -> i32 { + self as i32 + } + } + impl AsI32 for i16 { + #[inline] + fn as_i32(self) -> i32 { + self as i32 + } + } + impl AsI32 for u16 { + #[inline] + fn as_i32(self) -> i32 { + self as i32 + } + } + impl AsI32 for i8 { + #[inline] + fn as_i32(self) -> i32 { + self as i32 + } + } + impl AsI32 for u8 { + #[inline] + fn as_i32(self) -> i32 { + self as i32 + } + } + impl AsI32 for char { + #[inline] + fn as_i32(self) -> i32 { + self as i32 + } + } + impl AsI32 for usize { + #[inline] + fn as_i32(self) -> i32 { + self as i32 + } + } + extern crate alloc as alloc_crate; +} +#[rustfmt::skip] +#[cfg(target_arch = "wasm32")] + +#[cfg_attr(feature = "rustc-dep-of-std", unsafe(link_section = "component-type:wit-bindgen:0.51.0:wasi:cli@0.2.9:imports:encoded worldrust-wasip2-1.0.2+wasi-0.2.9-from-crates-io-in-libstd"))] +#[cfg_attr(not(feature = "rustc-dep-of-std"), unsafe(link_section = "component-type:wit-bindgen:0.51.0:wasi:cli@0.2.9:imports:encoded worldrust-wasip2-1.0.2+wasi-0.2.9-from-crates-io"))] + +#[doc(hidden)] +#[allow(clippy::octal_escapes)] +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 10730] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xecR\x01A\x02\x01AG\x01\ +B\x0a\x01o\x02ss\x01p\0\x01@\0\0\x01\x04\0\x0fget-environment\x01\x02\x01ps\x01@\ +\0\0\x03\x04\0\x0dget-arguments\x01\x04\x01ks\x01@\0\0\x05\x04\0\x0binitial-cwd\x01\ +\x06\x03\0\x1awasi:cli/environment@0.2.9\x05\0\x01B\x03\x01j\0\0\x01@\x01\x06sta\ +tus\0\x01\0\x04\0\x04exit\x01\x01\x03\0\x13wasi:cli/exit@0.2.9\x05\x01\x01B\x04\x04\ +\0\x05error\x03\x01\x01h\0\x01@\x01\x04self\x01\0s\x04\0\x1d[method]error.to-deb\ +ug-string\x01\x02\x03\0\x13wasi:io/error@0.2.9\x05\x02\x01B\x0a\x04\0\x08pollabl\ +e\x03\x01\x01h\0\x01@\x01\x04self\x01\0\x7f\x04\0\x16[method]pollable.ready\x01\x02\ +\x01@\x01\x04self\x01\x01\0\x04\0\x16[method]pollable.block\x01\x03\x01p\x01\x01\ +py\x01@\x01\x02in\x04\0\x05\x04\0\x04poll\x01\x06\x03\0\x12wasi:io/poll@0.2.9\x05\ +\x03\x02\x03\0\x02\x05error\x02\x03\0\x03\x08pollable\x01B(\x02\x03\x02\x01\x04\x04\ +\0\x05error\x03\0\0\x02\x03\x02\x01\x05\x04\0\x08pollable\x03\0\x02\x01i\x01\x01\ +q\x02\x15last-operation-failed\x01\x04\0\x06closed\0\0\x04\0\x0cstream-error\x03\ +\0\x05\x04\0\x0cinput-stream\x03\x01\x04\0\x0doutput-stream\x03\x01\x01h\x07\x01\ +p}\x01j\x01\x0a\x01\x06\x01@\x02\x04self\x09\x03lenw\0\x0b\x04\0\x19[method]inpu\ +t-stream.read\x01\x0c\x04\0\"[method]input-stream.blocking-read\x01\x0c\x01j\x01\ +w\x01\x06\x01@\x02\x04self\x09\x03lenw\0\x0d\x04\0\x19[method]input-stream.skip\x01\ +\x0e\x04\0\"[method]input-stream.blocking-skip\x01\x0e\x01i\x03\x01@\x01\x04self\ +\x09\0\x0f\x04\0\x1e[method]input-stream.subscribe\x01\x10\x01h\x08\x01@\x01\x04\ +self\x11\0\x0d\x04\0![method]output-stream.check-write\x01\x12\x01j\0\x01\x06\x01\ +@\x02\x04self\x11\x08contents\x0a\0\x13\x04\0\x1b[method]output-stream.write\x01\ +\x14\x04\0.[method]output-stream.blocking-write-and-flush\x01\x14\x01@\x01\x04se\ +lf\x11\0\x13\x04\0\x1b[method]output-stream.flush\x01\x15\x04\0$[method]output-s\ +tream.blocking-flush\x01\x15\x01@\x01\x04self\x11\0\x0f\x04\0\x1f[method]output-\ +stream.subscribe\x01\x16\x01@\x02\x04self\x11\x03lenw\0\x13\x04\0\"[method]outpu\ +t-stream.write-zeroes\x01\x17\x04\05[method]output-stream.blocking-write-zeroes-\ +and-flush\x01\x17\x01@\x03\x04self\x11\x03src\x09\x03lenw\0\x0d\x04\0\x1c[method\ +]output-stream.splice\x01\x18\x04\0%[method]output-stream.blocking-splice\x01\x18\ +\x03\0\x15wasi:io/streams@0.2.9\x05\x06\x02\x03\0\x04\x0cinput-stream\x01B\x05\x02\ +\x03\x02\x01\x07\x04\0\x0cinput-stream\x03\0\0\x01i\x01\x01@\0\0\x02\x04\0\x09ge\ +t-stdin\x01\x03\x03\0\x14wasi:cli/stdin@0.2.9\x05\x08\x02\x03\0\x04\x0doutput-st\ +ream\x01B\x05\x02\x03\x02\x01\x09\x04\0\x0doutput-stream\x03\0\0\x01i\x01\x01@\0\ +\0\x02\x04\0\x0aget-stdout\x01\x03\x03\0\x15wasi:cli/stdout@0.2.9\x05\x0a\x01B\x05\ +\x02\x03\x02\x01\x09\x04\0\x0doutput-stream\x03\0\0\x01i\x01\x01@\0\0\x02\x04\0\x0a\ +get-stderr\x01\x03\x03\0\x15wasi:cli/stderr@0.2.9\x05\x0b\x01B\x01\x04\0\x0eterm\ +inal-input\x03\x01\x03\0\x1dwasi:cli/terminal-input@0.2.9\x05\x0c\x01B\x01\x04\0\ +\x0fterminal-output\x03\x01\x03\0\x1ewasi:cli/terminal-output@0.2.9\x05\x0d\x02\x03\ +\0\x08\x0eterminal-input\x01B\x06\x02\x03\x02\x01\x0e\x04\0\x0eterminal-input\x03\ +\0\0\x01i\x01\x01k\x02\x01@\0\0\x03\x04\0\x12get-terminal-stdin\x01\x04\x03\0\x1d\ +wasi:cli/terminal-stdin@0.2.9\x05\x0f\x02\x03\0\x09\x0fterminal-output\x01B\x06\x02\ +\x03\x02\x01\x10\x04\0\x0fterminal-output\x03\0\0\x01i\x01\x01k\x02\x01@\0\0\x03\ +\x04\0\x13get-terminal-stdout\x01\x04\x03\0\x1ewasi:cli/terminal-stdout@0.2.9\x05\ +\x11\x01B\x06\x02\x03\x02\x01\x10\x04\0\x0fterminal-output\x03\0\0\x01i\x01\x01k\ +\x02\x01@\0\0\x03\x04\0\x13get-terminal-stderr\x01\x04\x03\0\x1ewasi:cli/termina\ +l-stderr@0.2.9\x05\x12\x01B\x0f\x02\x03\x02\x01\x05\x04\0\x08pollable\x03\0\0\x01\ +w\x04\0\x07instant\x03\0\x02\x01w\x04\0\x08duration\x03\0\x04\x01@\0\0\x03\x04\0\ +\x03now\x01\x06\x01@\0\0\x05\x04\0\x0aresolution\x01\x07\x01i\x01\x01@\x01\x04wh\ +en\x03\0\x08\x04\0\x11subscribe-instant\x01\x09\x01@\x01\x04when\x05\0\x08\x04\0\ +\x12subscribe-duration\x01\x0a\x03\0!wasi:clocks/monotonic-clock@0.2.9\x05\x13\x01\ +B\x05\x01r\x02\x07secondsw\x0bnanosecondsy\x04\0\x08datetime\x03\0\0\x01@\0\0\x01\ +\x04\0\x03now\x01\x02\x04\0\x0aresolution\x01\x02\x03\0\x1cwasi:clocks/wall-cloc\ +k@0.2.9\x05\x14\x02\x03\0\x04\x05error\x02\x03\0\x0e\x08datetime\x01Br\x02\x03\x02\ +\x01\x07\x04\0\x0cinput-stream\x03\0\0\x02\x03\x02\x01\x09\x04\0\x0doutput-strea\ +m\x03\0\x02\x02\x03\x02\x01\x15\x04\0\x05error\x03\0\x04\x02\x03\x02\x01\x16\x04\ +\0\x08datetime\x03\0\x06\x01w\x04\0\x08filesize\x03\0\x08\x01m\x08\x07unknown\x0c\ +block-device\x10character-device\x09directory\x04fifo\x0dsymbolic-link\x0cregula\ +r-file\x06socket\x04\0\x0fdescriptor-type\x03\0\x0a\x01n\x06\x04read\x05write\x13\ +file-integrity-sync\x13data-integrity-sync\x14requested-write-sync\x10mutate-dir\ +ectory\x04\0\x10descriptor-flags\x03\0\x0c\x01n\x01\x0esymlink-follow\x04\0\x0ap\ +ath-flags\x03\0\x0e\x01n\x04\x06create\x09directory\x09exclusive\x08truncate\x04\ +\0\x0aopen-flags\x03\0\x10\x01w\x04\0\x0alink-count\x03\0\x12\x01k\x07\x01r\x06\x04\ +type\x0b\x0alink-count\x13\x04size\x09\x15data-access-timestamp\x14\x1bdata-modi\ +fication-timestamp\x14\x17status-change-timestamp\x14\x04\0\x0fdescriptor-stat\x03\ +\0\x15\x01q\x03\x09no-change\0\0\x03now\0\0\x09timestamp\x01\x07\0\x04\0\x0dnew-\ +timestamp\x03\0\x17\x01r\x02\x04type\x0b\x04names\x04\0\x0fdirectory-entry\x03\0\ +\x19\x01m%\x06access\x0bwould-block\x07already\x0ebad-descriptor\x04busy\x08dead\ +lock\x05quota\x05exist\x0efile-too-large\x15illegal-byte-sequence\x0bin-progress\ +\x0binterrupted\x07invalid\x02io\x0cis-directory\x04loop\x0etoo-many-links\x0cme\ +ssage-size\x0dname-too-long\x09no-device\x08no-entry\x07no-lock\x13insufficient-\ +memory\x12insufficient-space\x0dnot-directory\x09not-empty\x0fnot-recoverable\x0b\ +unsupported\x06no-tty\x0eno-such-device\x08overflow\x0dnot-permitted\x04pipe\x09\ +read-only\x0cinvalid-seek\x0etext-file-busy\x0ccross-device\x04\0\x0aerror-code\x03\ +\0\x1b\x01m\x06\x06normal\x0asequential\x06random\x09will-need\x09dont-need\x08n\ +o-reuse\x04\0\x06advice\x03\0\x1d\x01r\x02\x05lowerw\x05upperw\x04\0\x13metadata\ +-hash-value\x03\0\x1f\x04\0\x0adescriptor\x03\x01\x04\0\x16directory-entry-strea\ +m\x03\x01\x01h!\x01i\x01\x01j\x01$\x01\x1c\x01@\x02\x04self#\x06offset\x09\0%\x04\ +\0\"[method]descriptor.read-via-stream\x01&\x01i\x03\x01j\x01'\x01\x1c\x01@\x02\x04\ +self#\x06offset\x09\0(\x04\0#[method]descriptor.write-via-stream\x01)\x01@\x01\x04\ +self#\0(\x04\0$[method]descriptor.append-via-stream\x01*\x01j\0\x01\x1c\x01@\x04\ +\x04self#\x06offset\x09\x06length\x09\x06advice\x1e\0+\x04\0\x19[method]descript\ +or.advise\x01,\x01@\x01\x04self#\0+\x04\0\x1c[method]descriptor.sync-data\x01-\x01\ +j\x01\x0d\x01\x1c\x01@\x01\x04self#\0.\x04\0\x1c[method]descriptor.get-flags\x01\ +/\x01j\x01\x0b\x01\x1c\x01@\x01\x04self#\00\x04\0\x1b[method]descriptor.get-type\ +\x011\x01@\x02\x04self#\x04size\x09\0+\x04\0\x1b[method]descriptor.set-size\x012\ +\x01@\x03\x04self#\x15data-access-timestamp\x18\x1bdata-modification-timestamp\x18\ +\0+\x04\0\x1c[method]descriptor.set-times\x013\x01p}\x01o\x024\x7f\x01j\x015\x01\ +\x1c\x01@\x03\x04self#\x06length\x09\x06offset\x09\06\x04\0\x17[method]descripto\ +r.read\x017\x01j\x01\x09\x01\x1c\x01@\x03\x04self#\x06buffer4\x06offset\x09\08\x04\ +\0\x18[method]descriptor.write\x019\x01i\"\x01j\x01:\x01\x1c\x01@\x01\x04self#\0\ +;\x04\0![method]descriptor.read-directory\x01<\x04\0\x17[method]descriptor.sync\x01\ +-\x01@\x02\x04self#\x04paths\0+\x04\0&[method]descriptor.create-directory-at\x01\ +=\x01j\x01\x16\x01\x1c\x01@\x01\x04self#\0>\x04\0\x17[method]descriptor.stat\x01\ +?\x01@\x03\x04self#\x0apath-flags\x0f\x04paths\0>\x04\0\x1a[method]descriptor.st\ +at-at\x01@\x01@\x05\x04self#\x0apath-flags\x0f\x04paths\x15data-access-timestamp\ +\x18\x1bdata-modification-timestamp\x18\0+\x04\0\x1f[method]descriptor.set-times\ +-at\x01A\x01@\x05\x04self#\x0eold-path-flags\x0f\x08old-paths\x0enew-descriptor#\ +\x08new-paths\0+\x04\0\x1a[method]descriptor.link-at\x01B\x01i!\x01j\x01\xc3\0\x01\ +\x1c\x01@\x05\x04self#\x0apath-flags\x0f\x04paths\x0aopen-flags\x11\x05flags\x0d\ +\0\xc4\0\x04\0\x1a[method]descriptor.open-at\x01E\x01j\x01s\x01\x1c\x01@\x02\x04\ +self#\x04paths\0\xc6\0\x04\0\x1e[method]descriptor.readlink-at\x01G\x04\0&[metho\ +d]descriptor.remove-directory-at\x01=\x01@\x04\x04self#\x08old-paths\x0enew-desc\ +riptor#\x08new-paths\0+\x04\0\x1c[method]descriptor.rename-at\x01H\x01@\x03\x04s\ +elf#\x08old-paths\x08new-paths\0+\x04\0\x1d[method]descriptor.symlink-at\x01I\x04\ +\0![method]descriptor.unlink-file-at\x01=\x01@\x02\x04self#\x05other#\0\x7f\x04\0\ +![method]descriptor.is-same-object\x01J\x01j\x01\x20\x01\x1c\x01@\x01\x04self#\0\ +\xcb\0\x04\0\x20[method]descriptor.metadata-hash\x01L\x01@\x03\x04self#\x0apath-\ +flags\x0f\x04paths\0\xcb\0\x04\0#[method]descriptor.metadata-hash-at\x01M\x01h\"\ +\x01k\x1a\x01j\x01\xcf\0\x01\x1c\x01@\x01\x04self\xce\0\0\xd0\0\x04\03[method]di\ +rectory-entry-stream.read-directory-entry\x01Q\x01h\x05\x01k\x1c\x01@\x01\x03err\ +\xd2\0\0\xd3\0\x04\0\x15filesystem-error-code\x01T\x03\0\x1bwasi:filesystem/type\ +s@0.2.9\x05\x17\x02\x03\0\x0f\x0adescriptor\x01B\x07\x02\x03\x02\x01\x18\x04\0\x0a\ +descriptor\x03\0\0\x01i\x01\x01o\x02\x02s\x01p\x03\x01@\0\0\x04\x04\0\x0fget-dir\ +ectories\x01\x05\x03\0\x1ewasi:filesystem/preopens@0.2.9\x05\x19\x01B\x11\x04\0\x07\ +network\x03\x01\x01m\x15\x07unknown\x0daccess-denied\x0dnot-supported\x10invalid\ +-argument\x0dout-of-memory\x07timeout\x14concurrency-conflict\x0fnot-in-progress\ +\x0bwould-block\x0dinvalid-state\x10new-socket-limit\x14address-not-bindable\x0e\ +address-in-use\x12remote-unreachable\x12connection-refused\x10connection-reset\x12\ +connection-aborted\x12datagram-too-large\x11name-unresolvable\x1atemporary-resol\ +ver-failure\x1apermanent-resolver-failure\x04\0\x0aerror-code\x03\0\x01\x01m\x02\ +\x04ipv4\x04ipv6\x04\0\x11ip-address-family\x03\0\x03\x01o\x04}}}}\x04\0\x0cipv4\ +-address\x03\0\x05\x01o\x08{{{{{{{{\x04\0\x0cipv6-address\x03\0\x07\x01q\x02\x04\ +ipv4\x01\x06\0\x04ipv6\x01\x08\0\x04\0\x0aip-address\x03\0\x09\x01r\x02\x04port{\ +\x07address\x06\x04\0\x13ipv4-socket-address\x03\0\x0b\x01r\x04\x04port{\x09flow\ +-infoy\x07address\x08\x08scope-idy\x04\0\x13ipv6-socket-address\x03\0\x0d\x01q\x02\ +\x04ipv4\x01\x0c\0\x04ipv6\x01\x0e\0\x04\0\x11ip-socket-address\x03\0\x0f\x03\0\x1a\ +wasi:sockets/network@0.2.9\x05\x1a\x02\x03\0\x11\x07network\x01B\x05\x02\x03\x02\ +\x01\x1b\x04\0\x07network\x03\0\0\x01i\x01\x01@\0\0\x02\x04\0\x10instance-networ\ +k\x01\x03\x03\0#wasi:sockets/instance-network@0.2.9\x05\x1c\x02\x03\0\x11\x0aerr\ +or-code\x02\x03\0\x11\x11ip-socket-address\x02\x03\0\x11\x11ip-address-family\x01\ +BD\x02\x03\x02\x01\x05\x04\0\x08pollable\x03\0\0\x02\x03\x02\x01\x1b\x04\0\x07ne\ +twork\x03\0\x02\x02\x03\x02\x01\x1d\x04\0\x0aerror-code\x03\0\x04\x02\x03\x02\x01\ +\x1e\x04\0\x11ip-socket-address\x03\0\x06\x02\x03\x02\x01\x1f\x04\0\x11ip-addres\ +s-family\x03\0\x08\x01p}\x01r\x02\x04data\x0a\x0eremote-address\x07\x04\0\x11inc\ +oming-datagram\x03\0\x0b\x01k\x07\x01r\x02\x04data\x0a\x0eremote-address\x0d\x04\ +\0\x11outgoing-datagram\x03\0\x0e\x04\0\x0audp-socket\x03\x01\x04\0\x18incoming-\ +datagram-stream\x03\x01\x04\0\x18outgoing-datagram-stream\x03\x01\x01h\x10\x01h\x03\ +\x01j\0\x01\x05\x01@\x03\x04self\x13\x07network\x14\x0dlocal-address\x07\0\x15\x04\ +\0\x1d[method]udp-socket.start-bind\x01\x16\x01@\x01\x04self\x13\0\x15\x04\0\x1e\ +[method]udp-socket.finish-bind\x01\x17\x01i\x11\x01i\x12\x01o\x02\x18\x19\x01j\x01\ +\x1a\x01\x05\x01@\x02\x04self\x13\x0eremote-address\x0d\0\x1b\x04\0\x19[method]u\ +dp-socket.stream\x01\x1c\x01j\x01\x07\x01\x05\x01@\x01\x04self\x13\0\x1d\x04\0\x20\ +[method]udp-socket.local-address\x01\x1e\x04\0![method]udp-socket.remote-address\ +\x01\x1e\x01@\x01\x04self\x13\0\x09\x04\0![method]udp-socket.address-family\x01\x1f\ +\x01j\x01}\x01\x05\x01@\x01\x04self\x13\0\x20\x04\0$[method]udp-socket.unicast-h\ +op-limit\x01!\x01@\x02\x04self\x13\x05value}\0\x15\x04\0([method]udp-socket.set-\ +unicast-hop-limit\x01\"\x01j\x01w\x01\x05\x01@\x01\x04self\x13\0#\x04\0&[method]\ +udp-socket.receive-buffer-size\x01$\x01@\x02\x04self\x13\x05valuew\0\x15\x04\0*[\ +method]udp-socket.set-receive-buffer-size\x01%\x04\0#[method]udp-socket.send-buf\ +fer-size\x01$\x04\0'[method]udp-socket.set-send-buffer-size\x01%\x01i\x01\x01@\x01\ +\x04self\x13\0&\x04\0\x1c[method]udp-socket.subscribe\x01'\x01h\x11\x01p\x0c\x01\ +j\x01)\x01\x05\x01@\x02\x04self(\x0bmax-resultsw\0*\x04\0([method]incoming-datag\ +ram-stream.receive\x01+\x01@\x01\x04self(\0&\x04\0*[method]incoming-datagram-str\ +eam.subscribe\x01,\x01h\x12\x01@\x01\x04self-\0#\x04\0+[method]outgoing-datagram\ +-stream.check-send\x01.\x01p\x0f\x01@\x02\x04self-\x09datagrams/\0#\x04\0%[metho\ +d]outgoing-datagram-stream.send\x010\x01@\x01\x04self-\0&\x04\0*[method]outgoing\ +-datagram-stream.subscribe\x011\x03\0\x16wasi:sockets/udp@0.2.9\x05\x20\x02\x03\0\ +\x13\x0audp-socket\x01B\x0c\x02\x03\x02\x01\x1b\x04\0\x07network\x03\0\0\x02\x03\ +\x02\x01\x1d\x04\0\x0aerror-code\x03\0\x02\x02\x03\x02\x01\x1f\x04\0\x11ip-addre\ +ss-family\x03\0\x04\x02\x03\x02\x01!\x04\0\x0audp-socket\x03\0\x06\x01i\x07\x01j\ +\x01\x08\x01\x03\x01@\x01\x0eaddress-family\x05\0\x09\x04\0\x11create-udp-socket\ +\x01\x0a\x03\0$wasi:sockets/udp-create-socket@0.2.9\x05\"\x02\x03\0\x0d\x08durat\ +ion\x01BT\x02\x03\x02\x01\x07\x04\0\x0cinput-stream\x03\0\0\x02\x03\x02\x01\x09\x04\ +\0\x0doutput-stream\x03\0\x02\x02\x03\x02\x01\x05\x04\0\x08pollable\x03\0\x04\x02\ +\x03\x02\x01#\x04\0\x08duration\x03\0\x06\x02\x03\x02\x01\x1b\x04\0\x07network\x03\ +\0\x08\x02\x03\x02\x01\x1d\x04\0\x0aerror-code\x03\0\x0a\x02\x03\x02\x01\x1e\x04\ +\0\x11ip-socket-address\x03\0\x0c\x02\x03\x02\x01\x1f\x04\0\x11ip-address-family\ +\x03\0\x0e\x01m\x03\x07receive\x04send\x04both\x04\0\x0dshutdown-type\x03\0\x10\x04\ +\0\x0atcp-socket\x03\x01\x01h\x12\x01h\x09\x01j\0\x01\x0b\x01@\x03\x04self\x13\x07\ +network\x14\x0dlocal-address\x0d\0\x15\x04\0\x1d[method]tcp-socket.start-bind\x01\ +\x16\x01@\x01\x04self\x13\0\x15\x04\0\x1e[method]tcp-socket.finish-bind\x01\x17\x01\ +@\x03\x04self\x13\x07network\x14\x0eremote-address\x0d\0\x15\x04\0\x20[method]tc\ +p-socket.start-connect\x01\x18\x01i\x01\x01i\x03\x01o\x02\x19\x1a\x01j\x01\x1b\x01\ +\x0b\x01@\x01\x04self\x13\0\x1c\x04\0![method]tcp-socket.finish-connect\x01\x1d\x04\ +\0\x1f[method]tcp-socket.start-listen\x01\x17\x04\0\x20[method]tcp-socket.finish\ +-listen\x01\x17\x01i\x12\x01o\x03\x1e\x19\x1a\x01j\x01\x1f\x01\x0b\x01@\x01\x04s\ +elf\x13\0\x20\x04\0\x19[method]tcp-socket.accept\x01!\x01j\x01\x0d\x01\x0b\x01@\x01\ +\x04self\x13\0\"\x04\0\x20[method]tcp-socket.local-address\x01#\x04\0![method]tc\ +p-socket.remote-address\x01#\x01@\x01\x04self\x13\0\x7f\x04\0\x1f[method]tcp-soc\ +ket.is-listening\x01$\x01@\x01\x04self\x13\0\x0f\x04\0![method]tcp-socket.addres\ +s-family\x01%\x01@\x02\x04self\x13\x05valuew\0\x15\x04\0*[method]tcp-socket.set-\ +listen-backlog-size\x01&\x01j\x01\x7f\x01\x0b\x01@\x01\x04self\x13\0'\x04\0%[met\ +hod]tcp-socket.keep-alive-enabled\x01(\x01@\x02\x04self\x13\x05value\x7f\0\x15\x04\ +\0)[method]tcp-socket.set-keep-alive-enabled\x01)\x01j\x01\x07\x01\x0b\x01@\x01\x04\ +self\x13\0*\x04\0'[method]tcp-socket.keep-alive-idle-time\x01+\x01@\x02\x04self\x13\ +\x05value\x07\0\x15\x04\0+[method]tcp-socket.set-keep-alive-idle-time\x01,\x04\0\ +&[method]tcp-socket.keep-alive-interval\x01+\x04\0*[method]tcp-socket.set-keep-a\ +live-interval\x01,\x01j\x01y\x01\x0b\x01@\x01\x04self\x13\0-\x04\0#[method]tcp-s\ +ocket.keep-alive-count\x01.\x01@\x02\x04self\x13\x05valuey\0\x15\x04\0'[method]t\ +cp-socket.set-keep-alive-count\x01/\x01j\x01}\x01\x0b\x01@\x01\x04self\x13\00\x04\ +\0\x1c[method]tcp-socket.hop-limit\x011\x01@\x02\x04self\x13\x05value}\0\x15\x04\ +\0\x20[method]tcp-socket.set-hop-limit\x012\x01j\x01w\x01\x0b\x01@\x01\x04self\x13\ +\03\x04\0&[method]tcp-socket.receive-buffer-size\x014\x04\0*[method]tcp-socket.s\ +et-receive-buffer-size\x01&\x04\0#[method]tcp-socket.send-buffer-size\x014\x04\0\ +'[method]tcp-socket.set-send-buffer-size\x01&\x01i\x05\x01@\x01\x04self\x13\05\x04\ +\0\x1c[method]tcp-socket.subscribe\x016\x01@\x02\x04self\x13\x0dshutdown-type\x11\ +\0\x15\x04\0\x1b[method]tcp-socket.shutdown\x017\x03\0\x16wasi:sockets/tcp@0.2.9\ +\x05$\x02\x03\0\x15\x0atcp-socket\x01B\x0c\x02\x03\x02\x01\x1b\x04\0\x07network\x03\ +\0\0\x02\x03\x02\x01\x1d\x04\0\x0aerror-code\x03\0\x02\x02\x03\x02\x01\x1f\x04\0\ +\x11ip-address-family\x03\0\x04\x02\x03\x02\x01%\x04\0\x0atcp-socket\x03\0\x06\x01\ +i\x07\x01j\x01\x08\x01\x03\x01@\x01\x0eaddress-family\x05\0\x09\x04\0\x11create-\ +tcp-socket\x01\x0a\x03\0$wasi:sockets/tcp-create-socket@0.2.9\x05&\x02\x03\0\x11\ +\x0aip-address\x01B\x16\x02\x03\x02\x01\x05\x04\0\x08pollable\x03\0\0\x02\x03\x02\ +\x01\x1b\x04\0\x07network\x03\0\x02\x02\x03\x02\x01\x1d\x04\0\x0aerror-code\x03\0\ +\x04\x02\x03\x02\x01'\x04\0\x0aip-address\x03\0\x06\x04\0\x16resolve-address-str\ +eam\x03\x01\x01h\x08\x01k\x07\x01j\x01\x0a\x01\x05\x01@\x01\x04self\x09\0\x0b\x04\ +\03[method]resolve-address-stream.resolve-next-address\x01\x0c\x01i\x01\x01@\x01\ +\x04self\x09\0\x0d\x04\0([method]resolve-address-stream.subscribe\x01\x0e\x01h\x03\ +\x01i\x08\x01j\x01\x10\x01\x05\x01@\x02\x07network\x0f\x04names\0\x11\x04\0\x11r\ +esolve-addresses\x01\x12\x03\0!wasi:sockets/ip-name-lookup@0.2.9\x05(\x01B\x05\x01\ +p}\x01@\x01\x03lenw\0\0\x04\0\x10get-random-bytes\x01\x01\x01@\0\0w\x04\0\x0eget\ +-random-u64\x01\x02\x03\0\x18wasi:random/random@0.2.9\x05)\x01B\x05\x01p}\x01@\x01\ +\x03lenw\0\0\x04\0\x19get-insecure-random-bytes\x01\x01\x01@\0\0w\x04\0\x17get-i\ +nsecure-random-u64\x01\x02\x03\0\x1awasi:random/insecure@0.2.9\x05*\x01B\x03\x01\ +o\x02ww\x01@\0\0\0\x04\0\x0dinsecure-seed\x01\x01\x03\0\x1fwasi:random/insecure-\ +seed@0.2.9\x05+\x04\0\x16wasi:cli/imports@0.2.9\x04\0\x0b\x0d\x01\0\x07imports\x03\ +\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x070.244.0\x10wit-\ +bindgen-rust\x060.51.0"; +#[inline(never)] +#[doc(hidden)] +pub fn __link_custom_section_describing_imports() { + wit_bindgen::rt::maybe_link_cabi_realloc(); +} diff --git a/tools/vendor/wasip2/src/lib.rs b/tools/vendor/wasip2/src/lib.rs new file mode 100644 index 0000000000..7bdf5a70d4 --- /dev/null +++ b/tools/vendor/wasip2/src/lib.rs @@ -0,0 +1,296 @@ +//! Raw API bindings to the [WebAssembly System Interface (WASI)][WASI] +//! +//! [WASI]: https://github.com/WebAssembly/WASI +//! +//! This crate provides Rust API bindings to the imports of [WASI] [worlds] such +//! as: +//! +//! * [`wasi:cli/command`] +//! * [`wasi:http/proxy`] +//! +//! This crate is procedurally generated with the [`wit-bindgen`] bindings +//! generator. Note that generated code is published to crates.io to slim this +//! crate down in terms of build dependencies and resources. +//! +//! # What is WASI? +//! +//! [WASI] is a set of APIs defined for the WebAssembly [Component Model] to +//! help components interact with the outside world. Core WebAssembly has no +//! intrinsic ability to access the host, for example `println!` don't work, but +//! [WASI] defines how to do so with the [`wasi:cli/stdio`] package. +//! +//! [WASI] is defined by an IDL called [WIT] using files that have the extension +//! `*.wit`. [WASI] and [WIT] are themselves then both defined in terms of the +//! [Component Model] in terms of types available and base semantics for APIs. +//! +//! [WASI] defines a number of standard "worlds" which are a description of a +//! what a WebAssembly component can import from an embedding and must export to +//! an embedding. An example world is [`wasi:cli/command`] which is a world for +//! running CLI applications. This world provides basic system utilities such as +//! clocks, a filesystem, CLI arguments, etc. The one required export is a main +//! function. +//! +//! The purpose of this crate is to provide pregenerated bindings to access +//! [WASI]-defined imports available to components. +//! +//! # What is a Component? +//! +//! An important aspect of [WASI] is that it is defined in terms of the +//! [Component Model]. The [Component Model] is a proposal for WebAssembly which +//! is a new format for wasm binaries, a component. A component contains "core" +//! WebAssembly modules (which are [standard WebAssembly modules]) but also has +//! the ability to do more: +//! +//! * A component can contain multiple core WebAssembly modules. +//! * Types used with component imports and exports are more comprehensive than +//! core WebAssembly. Core WebAssembly provides integers and floats, for +//! example, and components build on this and add strings, records (aka a Rust +//! `struct`), variants (aka a Rust `enum`), and resources (think a file +//! descriptor on Unix). +//! * A component provides procedural instructions of how to instantiate its +//! internal core WebAssembly modules with the imports it has. +//! +//! The [Component Model] is a not considered an official WebAssembly standard +//! at this time. It has been in development for 5 years (as of January 2024), +//! however, and the WASI 0.2.0 milestone (more on versioning in a moment) in +//! January 2024 represents a concrete target for ecosystems to use. Runtimes +//! such as [Wasmtime] support the [Component Model] for out-of-browser usage +//! and [jco] is an example of how components can be run in a browser. +//! +//! A full description of the component model is out of scope for this crate's +//! documentation but it suffices to say that [WASI], and this crate, are +//! intended to target components. Components use core WebAssembly modules as an +//! important technical detail, but the final output of this crate is intended +//! to be a component. +//! +//! # What are generated bindings? +//! +//! Above it was seen that [WASI] is defined with [WIT]. These programmatic +//! descriptions of [WASI] APIs are not suitable for use directly in Rust, +//! however these descriptions define how Rust can use them. Each [WIT] function +//! has a defined meaning in core WebAssembly via the [Canonical ABI]. This is a +//! lower level than most users want to operate at, however, so the generated +//! bindings in this crate serve as the bridge. +//! +//! More specifically the generated functions in this crate take the [Canonical +//! ABI] format of [WIT] functions and provide idiomatic Rust functions to call. +//! For example the [`wasi:cli/environment`] definition includes: +//! +//! ```wit +//! interface environment { +//! // ... +//! get-environment: func() -> list>; +//! // ... +//! } +//! ``` +//! +//! This corresponds to +//! [`wasi::cli::environment::get_environment`](crate::cli::environment::get_environment). +//! +//! Bindings are pre-generated in this crate with the [`wit-bindgen`] tool. You +//! can also generate your own bindings with [`wit-bindgen`] and [WASI] [WIT] +//! files too, but that's not covered by this crate. +//! +//! # WASI Today and `wasi_snapshot_preview1` +//! +//! This crate is based on the 0.2.0 version of [WASI] APIs. This version of +//! [WASI] was declared "phase 3" (suitable for general use and testing) in +//! January of 2024. Prior to this 0.2.0 "preview2" release of [WASI] there was +//! `wasi_snapshot_preview1`. This previous "preview1" release of [WASI] was +//! circa 2019 and was the initial vision for [WASI] as a standard. Development +//! of [WASI] migrated to the [Component Model] in the meantime. +//! +//! This means that the old `wasi_snapshot_preview1` interfaces are no longer +//! provided by this crate because [WASI] is no longer defined by those +//! interfaces. This includes the historical `*.witx` format which has now been +//! sueprseded. Note that the 0.11.x release series of this crate contains +//! bindings to the historical `wasi_snapshot_preview1` APIs if you're +//! interested in using them. +//! +//! # Crate Organization +//! +//! This crate is currently entirely generated by [`wit-bindgen`] which has the +//! following structure: +//! +//! * Each [WIT] package with bindings corresponds to a top-level module. For +//! example [`wasi:random`] can be found in the [`random`] module. +//! * Each [WIT] interface then corresponds to a submodule of its package's +//! module. For example [`wasi:random/insecure`] can be found in the +//! [`random::insecure`] module. +//! * Each [WIT] function has a Rust function with an idiomatic signature. +//! module. For example [`random::insecure::get_insecure_random_u64`]. +//! +//! Note that [WIT] documentation is rendered as rustdoc documentation in these +//! APIs as well. +//! +//! # Using this Crate +//! +//! This crate is intended to be easiest to use with a future +//! `wasm32-wasip2` target added to the Rust compiler. In the meantime +//! it's recommended to use the `wasm32-wasip1` target instead: +//! +//! ```sh +//! $ cargo build --target wasm32-wasip1 +//! ``` +//! +//! Note that the output of the `wasm32-wasip1` target is a core wasm module, not +//! a component, so to turn it into a component you can use the [`wasm-tools`] +//! CLI in combination with an "adapter module" for the `wasi_snapshot_preview1` +//! APIs that the Rust standard library uses (example adapters can be found on +//! [Wasmtime's release page][adapters] as +//! [`wasi_snapshot_preview1.command.wasm`] for example) +//! +//! ```sh +//! $ wasm-tools component new ./target/wasm32-wasip1/debug/my-app.wasm \ +//! --adapt ./wasi_snapshot_preview1.command.wasm \ +//! -o my-component.wasm +//! ``` +//! +//! ## Export Macros +//! +//! In addition to providing bindings for imports this crate also provides +//! macros to export the `wasi:cli/run` and `wasi:http/proxy` worlds, see their +//! respective documentation for more information: +//! +//! - [`wasi::cli::command::export!`](crate::cli::command::export) +//! - [`wasi::http::proxy::export!`](crate::http::proxy::export) +//! +//! [worlds]: https://component-model.bytecodealliance.org/design/worlds.html +//! [`wasi:cli/command`]: https://github.com/WebAssembly/wasi-cli/ +//! [`wasi:http/proxy`]: https://github.com/WebAssembly/wasi-http +//! [`wasi:cli/stdio`]: https://github.com/WebAssembly/wasi-cli/blob/main/wit/stdio.wit +//! [`wit-bindgen`]: https://github.com/bytecodealliance/wit-bindgen/ +//! [Component Model]: https://component-model.bytecodealliance.org/ +//! [WIT]: https://component-model.bytecodealliance.org/design/wit.html +//! [standard WebAssembly modules]: https://webassembly.github.io/spec/ +//! [Wasmtime]: https://github.com/bytecodealliance/wasmtime +//! [jco]: https://github.com/bytecodealliance/jco +//! [Canonical ABI]: https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md +//! [`wasi:cli/environment`]: https://github.com/WebAssembly/wasi-cli/blob/main/wit/environment.wit +//! [`wasi:random`]: https://github.com/WebAssembly/wasi-random +//! [`wasi:random/insecure`]: https://github.com/WebAssembly/wasi-random/blob/main/wit/insecure.wit +//! [`wasm-tools`]: https://github.com/bytecodealliance/wasm-tools +//! [adapters]: https://github.com/bytecodealliance/wasmtime/releases +//! [`wasi_snapshot_preview1.command.wasm`]: https://github.com/bytecodealliance/wasmtime/releases/download/v17.0.0/wasi_snapshot_preview1.command.wasm + +#![no_std] + +#[cfg(feature = "std")] +extern crate std; + +pub mod ext; + +// These modules are all auto-generated by `./ci/regenerate.sh` +#[allow(unused_imports)] +mod command; +mod imports; +#[allow(unused_imports)] +mod proxy; + +// generated bindings start with the package namespace, which in this case is +// `wasi`, but the crate is already called wasi, so lift everything up one level +// to the root of this crate. +pub use imports::wasi::*; + +// Expand the `cli` and `http` modules with `export!` macros for the +// command/proxy worlds, but also retain all the contents defined in the +// `bindings` module as well. +pub mod cli { + pub use super::imports::wasi::cli::*; + + pub mod command { + /// Generate an exported instance of the `wasi:cli/command` world. + /// + /// This macro generate the `#[no_mangle]` functions necessary to + /// export this interface. It takes an argument which is a type that + /// must implement the + /// [`exports::cli::run::Guest`](crate::exports::cli::run::Guest) + /// trait. + /// + /// ``` + /// struct MyCliRunner; + /// + /// impl wasip2::exports::cli::run::Guest for MyCliRunner { + /// fn run() -> Result<(), ()> { + /// // ... + /// # panic!(); + /// } + /// } + /// + /// wasip2::cli::command::export!(MyCliRunner); + /// ``` + /// + /// ## Compatibility with `wasm32-wasip1` targets + /// + /// This macro is not compatible with `wasm32-wasip1` `bin` targets + /// which instead use a `fn main()` with the + /// `wasi_snapshot_preview1.command.wasm` adapter. This macro _can_ be + /// used with the `reactor` or `proxy` adapters. + /// + /// + #[doc(inline)] + pub use crate::command::_export_command as export; + } +} + +pub mod http { + pub use super::proxy::wasi::http::*; + + pub mod proxy { + /// Generate an exported instance of the `wasi:http/proxy` world. + /// + /// This macro will generate `#[no_mangle]` functions as necessary to + /// export an implementation of the + /// [`exports::http::incoming_handler::Guest`](crate::exports::http::incoming_handler::Guest) + /// trait. This macro takes + /// an argument which is a type that implements this trait: + /// + /// ``` + /// use wasip2::http::types::{IncomingRequest, ResponseOutparam}; + /// + /// struct MyIncomingHandler; + /// + /// impl wasip2::exports::http::incoming_handler::Guest for MyIncomingHandler { + /// fn handle(request: IncomingRequest, response_out: ResponseOutparam) { + /// // ... + /// # panic!(); + /// } + /// } + /// + /// wasip2::http::proxy::export!(MyIncomingHandler); + /// ``` + /// + /// + #[doc(inline)] + pub use crate::proxy::_export_proxy as export; + } +} + +pub mod exports { + // This is required by the `export!` macros of this crate which assume that + // the types it's referring to show up as `exports::wasi::...`. + // + // This isn't part of the public interface, though, so hide this. + #[doc(hidden)] + pub mod wasi { + pub use crate::command::exports::wasi::*; + pub use crate::proxy::exports::wasi::*; + } + + // These are the restructured public interface of this crate. + pub use crate::command::exports::wasi::cli; + pub use crate::proxy::exports::wasi::http; +} + +// These macros are used by recursive invocations of the macro, but they're +// `#[doc(hidden)]` as it's not part of the public interface. +#[doc(hidden)] +pub use crate::command::_export_command; +#[doc(hidden)] +pub use crate::proxy::_export_proxy; diff --git a/tools/vendor/wasip2/src/proxy.rs b/tools/vendor/wasip2/src/proxy.rs new file mode 100644 index 0000000000..33e65388f0 --- /dev/null +++ b/tools/vendor/wasip2/src/proxy.rs @@ -0,0 +1,7451 @@ +// Generated by `wit-bindgen` 0.51.0. DO NOT EDIT! +// Options used: +// * std_feature +// * with "wasi:cli/stdin@0.2.9" = "crate::cli::stdin" +// * with "wasi:cli/stdout@0.2.9" = "crate::cli::stdout" +// * with "wasi:cli/stderr@0.2.9" = "crate::cli::stderr" +// * with "wasi:clocks/monotonic-clock@0.2.9" = "crate::clocks::monotonic_clock" +// * with "wasi:clocks/wall-clock@0.2.9" = "crate::clocks::wall_clock" +// * with "wasi:io/error@0.2.9" = "crate::io::error" +// * with "wasi:io/poll@0.2.9" = "crate::io::poll" +// * with "wasi:io/streams@0.2.9" = "crate::io::streams" +// * with "wasi:random/random@0.2.9" = "crate::random::random" +// * type_section_suffix: "rust-wasip2-1.0.2+wasi-0.2.9-from-crates-io-proxy-world" +// * default-bindings-module: "$crate" +// * export-macro-name: _export_proxy +// * pub-export-macro +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::io::poll as __with_name0; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::clocks::monotonic_clock as __with_name1; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::clocks::wall_clock as __with_name2; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::random::random as __with_name3; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::io::error as __with_name4; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::io::streams as __with_name5; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::cli::stdout as __with_name6; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::cli::stderr as __with_name7; +#[allow(unfulfilled_lint_expectations, unused_imports)] +use crate::cli::stdin as __with_name8; +#[rustfmt::skip] +#[allow(dead_code, clippy::all)] +pub mod wasi { + pub mod http { + /// This interface defines all of the types and methods for implementing + /// HTTP Requests and Responses, both incoming and outgoing, as well as + /// their headers, trailers, and bodies. + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod types { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + pub type Duration = super::super::super::__with_name1::Duration; + pub type InputStream = super::super::super::__with_name5::InputStream; + pub type OutputStream = super::super::super::__with_name5::OutputStream; + pub type IoError = super::super::super::__with_name4::Error; + pub type Pollable = super::super::super::__with_name0::Pollable; + /// This type corresponds to HTTP standard Methods. + #[derive(Clone)] + pub enum Method { + Get, + Head, + Post, + Put, + Delete, + Connect, + Options, + Trace, + Patch, + Other(_rt::String), + } + impl ::core::fmt::Debug for Method { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + match self { + Method::Get => f.debug_tuple("Method::Get").finish(), + Method::Head => f.debug_tuple("Method::Head").finish(), + Method::Post => f.debug_tuple("Method::Post").finish(), + Method::Put => f.debug_tuple("Method::Put").finish(), + Method::Delete => f.debug_tuple("Method::Delete").finish(), + Method::Connect => f.debug_tuple("Method::Connect").finish(), + Method::Options => f.debug_tuple("Method::Options").finish(), + Method::Trace => f.debug_tuple("Method::Trace").finish(), + Method::Patch => f.debug_tuple("Method::Patch").finish(), + Method::Other(e) => { + f.debug_tuple("Method::Other").field(e).finish() + } + } + } + } + /// This type corresponds to HTTP standard Related Schemes. + #[derive(Clone)] + pub enum Scheme { + Http, + Https, + Other(_rt::String), + } + impl ::core::fmt::Debug for Scheme { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + match self { + Scheme::Http => f.debug_tuple("Scheme::Http").finish(), + Scheme::Https => f.debug_tuple("Scheme::Https").finish(), + Scheme::Other(e) => { + f.debug_tuple("Scheme::Other").field(e).finish() + } + } + } + } + /// Defines the case payload type for `DNS-error` above: + #[derive(Clone)] + pub struct DnsErrorPayload { + pub rcode: Option<_rt::String>, + pub info_code: Option, + } + impl ::core::fmt::Debug for DnsErrorPayload { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + f.debug_struct("DnsErrorPayload") + .field("rcode", &self.rcode) + .field("info-code", &self.info_code) + .finish() + } + } + /// Defines the case payload type for `TLS-alert-received` above: + #[derive(Clone)] + pub struct TlsAlertReceivedPayload { + pub alert_id: Option, + pub alert_message: Option<_rt::String>, + } + impl ::core::fmt::Debug for TlsAlertReceivedPayload { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + f.debug_struct("TlsAlertReceivedPayload") + .field("alert-id", &self.alert_id) + .field("alert-message", &self.alert_message) + .finish() + } + } + /// Defines the case payload type for `HTTP-response-{header,trailer}-size` above: + #[derive(Clone)] + pub struct FieldSizePayload { + pub field_name: Option<_rt::String>, + pub field_size: Option, + } + impl ::core::fmt::Debug for FieldSizePayload { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + f.debug_struct("FieldSizePayload") + .field("field-name", &self.field_name) + .field("field-size", &self.field_size) + .finish() + } + } + /// These cases are inspired by the IANA HTTP Proxy Error Types: + /// + #[derive(Clone)] + pub enum ErrorCode { + DnsTimeout, + DnsError(DnsErrorPayload), + DestinationNotFound, + DestinationUnavailable, + DestinationIpProhibited, + DestinationIpUnroutable, + ConnectionRefused, + ConnectionTerminated, + ConnectionTimeout, + ConnectionReadTimeout, + ConnectionWriteTimeout, + ConnectionLimitReached, + TlsProtocolError, + TlsCertificateError, + TlsAlertReceived(TlsAlertReceivedPayload), + HttpRequestDenied, + HttpRequestLengthRequired, + HttpRequestBodySize(Option), + HttpRequestMethodInvalid, + HttpRequestUriInvalid, + HttpRequestUriTooLong, + HttpRequestHeaderSectionSize(Option), + HttpRequestHeaderSize(Option), + HttpRequestTrailerSectionSize(Option), + HttpRequestTrailerSize(FieldSizePayload), + HttpResponseIncomplete, + HttpResponseHeaderSectionSize(Option), + HttpResponseHeaderSize(FieldSizePayload), + HttpResponseBodySize(Option), + HttpResponseTrailerSectionSize(Option), + HttpResponseTrailerSize(FieldSizePayload), + HttpResponseTransferCoding(Option<_rt::String>), + HttpResponseContentCoding(Option<_rt::String>), + HttpResponseTimeout, + HttpUpgradeFailed, + HttpProtocolError, + LoopDetected, + ConfigurationError, + /// This is a catch-all error for anything that doesn't fit cleanly into a + /// more specific case. It also includes an optional string for an + /// unstructured description of the error. Users should not depend on the + /// string for diagnosing errors, as it's not required to be consistent + /// between implementations. + InternalError(Option<_rt::String>), + } + impl ::core::fmt::Debug for ErrorCode { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + match self { + ErrorCode::DnsTimeout => { + f.debug_tuple("ErrorCode::DnsTimeout").finish() + } + ErrorCode::DnsError(e) => { + f.debug_tuple("ErrorCode::DnsError").field(e).finish() + } + ErrorCode::DestinationNotFound => { + f.debug_tuple("ErrorCode::DestinationNotFound").finish() + } + ErrorCode::DestinationUnavailable => { + f.debug_tuple("ErrorCode::DestinationUnavailable").finish() + } + ErrorCode::DestinationIpProhibited => { + f.debug_tuple("ErrorCode::DestinationIpProhibited").finish() + } + ErrorCode::DestinationIpUnroutable => { + f.debug_tuple("ErrorCode::DestinationIpUnroutable").finish() + } + ErrorCode::ConnectionRefused => { + f.debug_tuple("ErrorCode::ConnectionRefused").finish() + } + ErrorCode::ConnectionTerminated => { + f.debug_tuple("ErrorCode::ConnectionTerminated").finish() + } + ErrorCode::ConnectionTimeout => { + f.debug_tuple("ErrorCode::ConnectionTimeout").finish() + } + ErrorCode::ConnectionReadTimeout => { + f.debug_tuple("ErrorCode::ConnectionReadTimeout").finish() + } + ErrorCode::ConnectionWriteTimeout => { + f.debug_tuple("ErrorCode::ConnectionWriteTimeout").finish() + } + ErrorCode::ConnectionLimitReached => { + f.debug_tuple("ErrorCode::ConnectionLimitReached").finish() + } + ErrorCode::TlsProtocolError => { + f.debug_tuple("ErrorCode::TlsProtocolError").finish() + } + ErrorCode::TlsCertificateError => { + f.debug_tuple("ErrorCode::TlsCertificateError").finish() + } + ErrorCode::TlsAlertReceived(e) => { + f.debug_tuple("ErrorCode::TlsAlertReceived") + .field(e) + .finish() + } + ErrorCode::HttpRequestDenied => { + f.debug_tuple("ErrorCode::HttpRequestDenied").finish() + } + ErrorCode::HttpRequestLengthRequired => { + f.debug_tuple("ErrorCode::HttpRequestLengthRequired") + .finish() + } + ErrorCode::HttpRequestBodySize(e) => { + f.debug_tuple("ErrorCode::HttpRequestBodySize") + .field(e) + .finish() + } + ErrorCode::HttpRequestMethodInvalid => { + f.debug_tuple("ErrorCode::HttpRequestMethodInvalid").finish() + } + ErrorCode::HttpRequestUriInvalid => { + f.debug_tuple("ErrorCode::HttpRequestUriInvalid").finish() + } + ErrorCode::HttpRequestUriTooLong => { + f.debug_tuple("ErrorCode::HttpRequestUriTooLong").finish() + } + ErrorCode::HttpRequestHeaderSectionSize(e) => { + f.debug_tuple("ErrorCode::HttpRequestHeaderSectionSize") + .field(e) + .finish() + } + ErrorCode::HttpRequestHeaderSize(e) => { + f.debug_tuple("ErrorCode::HttpRequestHeaderSize") + .field(e) + .finish() + } + ErrorCode::HttpRequestTrailerSectionSize(e) => { + f.debug_tuple("ErrorCode::HttpRequestTrailerSectionSize") + .field(e) + .finish() + } + ErrorCode::HttpRequestTrailerSize(e) => { + f.debug_tuple("ErrorCode::HttpRequestTrailerSize") + .field(e) + .finish() + } + ErrorCode::HttpResponseIncomplete => { + f.debug_tuple("ErrorCode::HttpResponseIncomplete").finish() + } + ErrorCode::HttpResponseHeaderSectionSize(e) => { + f.debug_tuple("ErrorCode::HttpResponseHeaderSectionSize") + .field(e) + .finish() + } + ErrorCode::HttpResponseHeaderSize(e) => { + f.debug_tuple("ErrorCode::HttpResponseHeaderSize") + .field(e) + .finish() + } + ErrorCode::HttpResponseBodySize(e) => { + f.debug_tuple("ErrorCode::HttpResponseBodySize") + .field(e) + .finish() + } + ErrorCode::HttpResponseTrailerSectionSize(e) => { + f.debug_tuple("ErrorCode::HttpResponseTrailerSectionSize") + .field(e) + .finish() + } + ErrorCode::HttpResponseTrailerSize(e) => { + f.debug_tuple("ErrorCode::HttpResponseTrailerSize") + .field(e) + .finish() + } + ErrorCode::HttpResponseTransferCoding(e) => { + f.debug_tuple("ErrorCode::HttpResponseTransferCoding") + .field(e) + .finish() + } + ErrorCode::HttpResponseContentCoding(e) => { + f.debug_tuple("ErrorCode::HttpResponseContentCoding") + .field(e) + .finish() + } + ErrorCode::HttpResponseTimeout => { + f.debug_tuple("ErrorCode::HttpResponseTimeout").finish() + } + ErrorCode::HttpUpgradeFailed => { + f.debug_tuple("ErrorCode::HttpUpgradeFailed").finish() + } + ErrorCode::HttpProtocolError => { + f.debug_tuple("ErrorCode::HttpProtocolError").finish() + } + ErrorCode::LoopDetected => { + f.debug_tuple("ErrorCode::LoopDetected").finish() + } + ErrorCode::ConfigurationError => { + f.debug_tuple("ErrorCode::ConfigurationError").finish() + } + ErrorCode::InternalError(e) => { + f.debug_tuple("ErrorCode::InternalError").field(e).finish() + } + } + } + } + impl ::core::fmt::Display for ErrorCode { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + write!(f, "{:?}", self) + } + } + #[cfg(feature = "std")] + impl ::core::error::Error for ErrorCode {} + /// This type enumerates the different kinds of errors that may occur when + /// setting or appending to a `fields` resource. + #[derive(Clone, Copy)] + pub enum HeaderError { + /// This error indicates that a `field-name` or `field-value` was + /// syntactically invalid when used with an operation that sets headers in a + /// `fields`. + InvalidSyntax, + /// This error indicates that a forbidden `field-name` was used when trying + /// to set a header in a `fields`. + Forbidden, + /// This error indicates that the operation on the `fields` was not + /// permitted because the fields are immutable. + Immutable, + } + impl ::core::fmt::Debug for HeaderError { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + match self { + HeaderError::InvalidSyntax => { + f.debug_tuple("HeaderError::InvalidSyntax").finish() + } + HeaderError::Forbidden => { + f.debug_tuple("HeaderError::Forbidden").finish() + } + HeaderError::Immutable => { + f.debug_tuple("HeaderError::Immutable").finish() + } + } + } + } + impl ::core::fmt::Display for HeaderError { + fn fmt( + &self, + f: &mut ::core::fmt::Formatter<'_>, + ) -> ::core::fmt::Result { + write!(f, "{:?}", self) + } + } + #[cfg(feature = "std")] + impl ::core::error::Error for HeaderError {} + /// Field keys are always strings. + /// + /// Field keys should always be treated as case insensitive by the `fields` + /// resource for the purposes of equality checking. + /// + /// # Deprecation + /// + /// This type has been deprecated in favor of the `field-name` type. + pub type FieldKey = _rt::String; + /// Field names are always strings. + /// + /// Field names should always be treated as case insensitive by the `fields` + /// resource for the purposes of equality checking. + pub type FieldName = FieldKey; + /// Field values should always be ASCII strings. However, in + /// reality, HTTP implementations often have to interpret malformed values, + /// so they are provided as a list of bytes. + pub type FieldValue = _rt::Vec; + /// This following block defines the `fields` resource which corresponds to + /// HTTP standard Fields. Fields are a common representation used for both + /// Headers and Trailers. + /// + /// A `fields` may be mutable or immutable. A `fields` created using the + /// constructor, `from-list`, or `clone` will be mutable, but a `fields` + /// resource given by other means (including, but not limited to, + /// `incoming-request.headers`, `outgoing-request.headers`) might be + /// immutable. In an immutable fields, the `set`, `append`, and `delete` + /// operations will fail with `header-error.immutable`. + #[derive(Debug)] + #[repr(transparent)] + pub struct Fields { + handle: _rt::Resource, + } + impl Fields { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for Fields { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]fields"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + /// Headers is an alias for Fields. + pub type Headers = Fields; + /// Trailers is an alias for Fields. + pub type Trailers = Fields; + /// Represents an incoming HTTP Request. + #[derive(Debug)] + #[repr(transparent)] + pub struct IncomingRequest { + handle: _rt::Resource, + } + impl IncomingRequest { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for IncomingRequest { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]incoming-request"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + /// Represents an outgoing HTTP Request. + #[derive(Debug)] + #[repr(transparent)] + pub struct OutgoingRequest { + handle: _rt::Resource, + } + impl OutgoingRequest { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for OutgoingRequest { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]outgoing-request"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + /// Parameters for making an HTTP Request. Each of these parameters is + /// currently an optional timeout applicable to the transport layer of the + /// HTTP protocol. + /// + /// These timeouts are separate from any the user may use to bound a + /// blocking call to `wasi:io/poll.poll`. + #[derive(Debug)] + #[repr(transparent)] + pub struct RequestOptions { + handle: _rt::Resource, + } + impl RequestOptions { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for RequestOptions { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]request-options"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + /// Represents the ability to send an HTTP Response. + /// + /// This resource is used by the `wasi:http/incoming-handler` interface to + /// allow a Response to be sent corresponding to the Request provided as the + /// other argument to `incoming-handler.handle`. + #[derive(Debug)] + #[repr(transparent)] + pub struct ResponseOutparam { + handle: _rt::Resource, + } + impl ResponseOutparam { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for ResponseOutparam { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]response-outparam"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + /// This type corresponds to the HTTP standard Status Code. + pub type StatusCode = u16; + /// Represents an incoming HTTP Response. + #[derive(Debug)] + #[repr(transparent)] + pub struct IncomingResponse { + handle: _rt::Resource, + } + impl IncomingResponse { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for IncomingResponse { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]incoming-response"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + /// Represents an incoming HTTP Request or Response's Body. + /// + /// A body has both its contents - a stream of bytes - and a (possibly + /// empty) set of trailers, indicating that the full contents of the + /// body have been received. This resource represents the contents as + /// an `input-stream` and the delivery of trailers as a `future-trailers`, + /// and ensures that the user of this interface may only be consuming either + /// the body contents or waiting on trailers at any given time. + #[derive(Debug)] + #[repr(transparent)] + pub struct IncomingBody { + handle: _rt::Resource, + } + impl IncomingBody { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for IncomingBody { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]incoming-body"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + /// Represents a future which may eventually return trailers, or an error. + /// + /// In the case that the incoming HTTP Request or Response did not have any + /// trailers, this future will resolve to the empty set of trailers once the + /// complete Request or Response body has been received. + #[derive(Debug)] + #[repr(transparent)] + pub struct FutureTrailers { + handle: _rt::Resource, + } + impl FutureTrailers { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for FutureTrailers { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]future-trailers"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + /// Represents an outgoing HTTP Response. + #[derive(Debug)] + #[repr(transparent)] + pub struct OutgoingResponse { + handle: _rt::Resource, + } + impl OutgoingResponse { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for OutgoingResponse { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]outgoing-response"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + /// Represents an outgoing HTTP Request or Response's Body. + /// + /// A body has both its contents - a stream of bytes - and a (possibly + /// empty) set of trailers, inducating the full contents of the body + /// have been sent. This resource represents the contents as an + /// `output-stream` child resource, and the completion of the body (with + /// optional trailers) with a static function that consumes the + /// `outgoing-body` resource, and ensures that the user of this interface + /// may not write to the body contents after the body has been finished. + /// + /// If the user code drops this resource, as opposed to calling the static + /// method `finish`, the implementation should treat the body as incomplete, + /// and that an error has occurred. The implementation should propagate this + /// error to the HTTP protocol by whatever means it has available, + /// including: corrupting the body on the wire, aborting the associated + /// Request, or sending a late status code for the Response. + #[derive(Debug)] + #[repr(transparent)] + pub struct OutgoingBody { + handle: _rt::Resource, + } + impl OutgoingBody { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for OutgoingBody { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]outgoing-body"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + /// Represents a future which may eventually return an incoming HTTP + /// Response, or an error. + /// + /// This resource is returned by the `wasi:http/outgoing-handler` interface to + /// provide the HTTP Response corresponding to the sent Request. + #[derive(Debug)] + #[repr(transparent)] + pub struct FutureIncomingResponse { + handle: _rt::Resource, + } + impl FutureIncomingResponse { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: unsafe { _rt::Resource::from_handle(handle) }, + } + } + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + _rt::Resource::take_handle(&self.handle) + } + #[doc(hidden)] + pub fn handle(&self) -> u32 { + _rt::Resource::handle(&self.handle) + } + } + unsafe impl _rt::WasmResource for FutureIncomingResponse { + #[inline] + unsafe fn drop(_handle: u32) { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[resource-drop]future-incoming-response"] + fn drop(_: i32); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn drop(_: i32) { + unreachable!() + } + unsafe { + drop(_handle as i32); + } + } + } + impl Fields { + #[allow(unused_unsafe, clippy::all)] + /// Construct an empty HTTP Fields. + /// + /// The resulting `fields` is mutable. + #[allow(async_fn_in_trait)] + pub fn new() -> Self { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[constructor]fields"] + fn wit_import0() -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0() -> i32 { + unreachable!() + } + let ret = wit_import0(); + Fields::from_handle(ret as u32) + } + } + } + impl Fields { + #[allow(unused_unsafe, clippy::all)] + /// Construct an HTTP Fields. + /// + /// The resulting `fields` is mutable. + /// + /// The list represents each name-value pair in the Fields. Names + /// which have multiple values are represented by multiple entries in this + /// list with the same name. + /// + /// The tuple is a pair of the field name, represented as a string, and + /// Value, represented as a list of bytes. + /// + /// An error result will be returned if any `field-name` or `field-value` is + /// syntactically invalid, or if a field is forbidden. + #[allow(async_fn_in_trait)] + pub fn from_list( + entries: &[(FieldName, FieldValue)], + ) -> Result { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 8], + ); + let vec3 = entries; + let len3 = vec3.len(); + let layout3 = _rt::alloc::Layout::from_size_align( + vec3.len() * (4 * ::core::mem::size_of::<*const u8>()), + ::core::mem::size_of::<*const u8>(), + ) + .unwrap(); + let (result3, _cleanup3) = wit_bindgen::rt::Cleanup::new( + layout3, + ); + for (i, e) in vec3.into_iter().enumerate() { + let base = result3 + .add(i * (4 * ::core::mem::size_of::<*const u8>())); + { + let (t0_0, t0_1) = e; + let vec1 = t0_0; + let ptr1 = vec1.as_ptr().cast::(); + let len1 = vec1.len(); + *base + .add(::core::mem::size_of::<*const u8>()) + .cast::() = len1; + *base.add(0).cast::<*mut u8>() = ptr1.cast_mut(); + let vec2 = t0_1; + let ptr2 = vec2.as_ptr().cast::(); + let len2 = vec2.len(); + *base + .add(3 * ::core::mem::size_of::<*const u8>()) + .cast::() = len2; + *base + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>() = ptr2.cast_mut(); + } + } + let ptr4 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[static]fields.from-list"] + fn wit_import5(_: *mut u8, _: usize, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import5( + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import5(result3, len3, ptr4); + let l6 = i32::from(*ptr4.add(0).cast::()); + let result10 = match l6 { + 0 => { + let e = { + let l7 = *ptr4.add(4).cast::(); + Fields::from_handle(l7 as u32) + }; + Ok(e) + } + 1 => { + let e = { + let l8 = i32::from(*ptr4.add(4).cast::()); + let v9 = match l8 { + 0 => HeaderError::InvalidSyntax, + 1 => HeaderError::Forbidden, + n => { + debug_assert_eq!(n, 2, "invalid enum discriminant"); + HeaderError::Immutable + } + }; + v9 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result10 + } + } + } + impl Fields { + #[allow(unused_unsafe, clippy::all)] + /// Get all of the values corresponding to a name. If the name is not present + /// in this `fields` or is syntactically invalid, an empty list is returned. + /// However, if the name is present but empty, this is represented by a list + /// with one or more empty field-values present. + #[allow(async_fn_in_trait)] + pub fn get(&self, name: &str) -> _rt::Vec { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 2 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2 + * ::core::mem::size_of::<*const u8>()], + ); + let vec0 = name; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let ptr1 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]fields.get"] + fn wit_import2(_: i32, _: *mut u8, _: usize, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import2( + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import2((self).handle() as i32, ptr0.cast_mut(), len0, ptr1); + let l3 = *ptr1.add(0).cast::<*mut u8>(); + let l4 = *ptr1 + .add(::core::mem::size_of::<*const u8>()) + .cast::(); + let base8 = l3; + let len8 = l4; + let mut result8 = _rt::Vec::with_capacity(len8); + for i in 0..len8 { + let base = base8 + .add(i * (2 * ::core::mem::size_of::<*const u8>())); + let e8 = { + let l5 = *base.add(0).cast::<*mut u8>(); + let l6 = *base + .add(::core::mem::size_of::<*const u8>()) + .cast::(); + let len7 = l6; + <_ as From< + _rt::Vec<_>, + >>::from(_rt::Vec::from_raw_parts(l5.cast(), len7, len7)) + }; + result8.push(e8); + } + _rt::cabi_dealloc( + base8, + len8 * (2 * ::core::mem::size_of::<*const u8>()), + ::core::mem::size_of::<*const u8>(), + ); + let result9 = result8; + result9 + } + } + } + impl Fields { + #[allow(unused_unsafe, clippy::all)] + /// Returns `true` when the name is present in this `fields`. If the name is + /// syntactically invalid, `false` is returned. + #[allow(async_fn_in_trait)] + pub fn has(&self, name: &str) -> bool { + unsafe { + let vec0 = name; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]fields.has"] + fn wit_import1(_: i32, _: *mut u8, _: usize) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1( + _: i32, + _: *mut u8, + _: usize, + ) -> i32 { + unreachable!() + } + let ret = wit_import1( + (self).handle() as i32, + ptr0.cast_mut(), + len0, + ); + _rt::bool_lift(ret as u8) + } + } + } + impl Fields { + #[allow(unused_unsafe, clippy::all)] + /// Set all of the values for a name. Clears any existing values for that + /// name, if they have been set. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + /// + /// Fails with `header-error.invalid-syntax` if the `field-name` or any of + /// the `field-value`s are syntactically invalid. + #[allow(async_fn_in_trait)] + pub fn set( + &self, + name: &str, + value: &[FieldValue], + ) -> Result<(), HeaderError> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let vec0 = name; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let vec2 = value; + let len2 = vec2.len(); + let layout2 = _rt::alloc::Layout::from_size_align( + vec2.len() * (2 * ::core::mem::size_of::<*const u8>()), + ::core::mem::size_of::<*const u8>(), + ) + .unwrap(); + let (result2, _cleanup2) = wit_bindgen::rt::Cleanup::new( + layout2, + ); + for (i, e) in vec2.into_iter().enumerate() { + let base = result2 + .add(i * (2 * ::core::mem::size_of::<*const u8>())); + { + let vec1 = e; + let ptr1 = vec1.as_ptr().cast::(); + let len1 = vec1.len(); + *base + .add(::core::mem::size_of::<*const u8>()) + .cast::() = len1; + *base.add(0).cast::<*mut u8>() = ptr1.cast_mut(); + } + } + let ptr3 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]fields.set"] + fn wit_import4( + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + _: usize, + _: *mut u8, + ); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import4( + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import4( + (self).handle() as i32, + ptr0.cast_mut(), + len0, + result2, + len2, + ptr3, + ); + let l5 = i32::from(*ptr3.add(0).cast::()); + let result8 = match l5 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l6 = i32::from(*ptr3.add(1).cast::()); + let v7 = match l6 { + 0 => HeaderError::InvalidSyntax, + 1 => HeaderError::Forbidden, + n => { + debug_assert_eq!(n, 2, "invalid enum discriminant"); + HeaderError::Immutable + } + }; + v7 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result8 + } + } + } + impl Fields { + #[allow(unused_unsafe, clippy::all)] + /// Delete all values for a name. Does nothing if no values for the name + /// exist. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + /// + /// Fails with `header-error.invalid-syntax` if the `field-name` is + /// syntactically invalid. + #[allow(async_fn_in_trait)] + pub fn delete(&self, name: &str) -> Result<(), HeaderError> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let vec0 = name; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let ptr1 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]fields.delete"] + fn wit_import2(_: i32, _: *mut u8, _: usize, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import2( + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import2((self).handle() as i32, ptr0.cast_mut(), len0, ptr1); + let l3 = i32::from(*ptr1.add(0).cast::()); + let result6 = match l3 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr1.add(1).cast::()); + let v5 = match l4 { + 0 => HeaderError::InvalidSyntax, + 1 => HeaderError::Forbidden, + n => { + debug_assert_eq!(n, 2, "invalid enum discriminant"); + HeaderError::Immutable + } + }; + v5 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result6 + } + } + } + impl Fields { + #[allow(unused_unsafe, clippy::all)] + /// Append a value for a name. Does not change or delete any existing + /// values for that name. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + /// + /// Fails with `header-error.invalid-syntax` if the `field-name` or + /// `field-value` are syntactically invalid. + #[allow(async_fn_in_trait)] + pub fn append( + &self, + name: &str, + value: &[u8], + ) -> Result<(), HeaderError> { + unsafe { + #[repr(align(1))] + struct RetArea([::core::mem::MaybeUninit; 2]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2], + ); + let vec0 = name; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let vec1 = value; + let ptr1 = vec1.as_ptr().cast::(); + let len1 = vec1.len(); + let ptr2 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]fields.append"] + fn wit_import3( + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + _: usize, + _: *mut u8, + ); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import3( + _: i32, + _: *mut u8, + _: usize, + _: *mut u8, + _: usize, + _: *mut u8, + ) { + unreachable!() + } + wit_import3( + (self).handle() as i32, + ptr0.cast_mut(), + len0, + ptr1.cast_mut(), + len1, + ptr2, + ); + let l4 = i32::from(*ptr2.add(0).cast::()); + let result7 = match l4 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l5 = i32::from(*ptr2.add(1).cast::()); + let v6 = match l5 { + 0 => HeaderError::InvalidSyntax, + 1 => HeaderError::Forbidden, + n => { + debug_assert_eq!(n, 2, "invalid enum discriminant"); + HeaderError::Immutable + } + }; + v6 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result7 + } + } + } + impl Fields { + #[allow(unused_unsafe, clippy::all)] + /// Retrieve the full set of names and values in the Fields. Like the + /// constructor, the list represents each name-value pair. + /// + /// The outer list represents each name-value pair in the Fields. Names + /// which have multiple values are represented by multiple entries in this + /// list with the same name. + /// + /// The names and values are always returned in the original casing and in + /// the order in which they will be serialized for transport. + #[allow(async_fn_in_trait)] + pub fn entries(&self) -> _rt::Vec<(FieldName, FieldValue)> { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 2 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 2 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]fields.entries"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = *ptr0.add(0).cast::<*mut u8>(); + let l3 = *ptr0 + .add(::core::mem::size_of::<*const u8>()) + .cast::(); + let base10 = l2; + let len10 = l3; + let mut result10 = _rt::Vec::with_capacity(len10); + for i in 0..len10 { + let base = base10 + .add(i * (4 * ::core::mem::size_of::<*const u8>())); + let e10 = { + let l4 = *base.add(0).cast::<*mut u8>(); + let l5 = *base + .add(::core::mem::size_of::<*const u8>()) + .cast::(); + let len6 = l5; + let bytes6 = _rt::Vec::from_raw_parts( + l4.cast(), + len6, + len6, + ); + let l7 = *base + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l8 = *base + .add(3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len9 = l8; + ( + _rt::string_lift(bytes6), + <_ as From< + _rt::Vec<_>, + >>::from(_rt::Vec::from_raw_parts(l7.cast(), len9, len9)), + ) + }; + result10.push(e10); + } + _rt::cabi_dealloc( + base10, + len10 * (4 * ::core::mem::size_of::<*const u8>()), + ::core::mem::size_of::<*const u8>(), + ); + let result11 = result10; + result11 + } + } + } + impl Fields { + #[allow(unused_unsafe, clippy::all)] + /// Make a deep copy of the Fields. Equivalent in behavior to calling the + /// `fields` constructor on the return value of `entries`. The resulting + /// `fields` is mutable. + #[allow(async_fn_in_trait)] + pub fn clone(&self) -> Fields { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]fields.clone"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((self).handle() as i32); + Fields::from_handle(ret as u32) + } + } + } + impl IncomingRequest { + #[allow(unused_unsafe, clippy::all)] + /// Returns the method of the incoming request. + #[allow(async_fn_in_trait)] + pub fn method(&self) -> Method { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 3 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 3 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]incoming-request.method"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let v6 = match l2 { + 0 => Method::Get, + 1 => Method::Head, + 2 => Method::Post, + 3 => Method::Put, + 4 => Method::Delete, + 5 => Method::Connect, + 6 => Method::Options, + 7 => Method::Trace, + 8 => Method::Patch, + n => { + debug_assert_eq!(n, 9, "invalid enum discriminant"); + let e6 = { + let l3 = *ptr0 + .add(::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l4 = *ptr0 + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len5 = l4; + let bytes5 = _rt::Vec::from_raw_parts( + l3.cast(), + len5, + len5, + ); + _rt::string_lift(bytes5) + }; + Method::Other(e6) + } + }; + let result7 = v6; + result7 + } + } + } + impl IncomingRequest { + #[allow(unused_unsafe, clippy::all)] + /// Returns the path with query parameters from the request, as a string. + #[allow(async_fn_in_trait)] + pub fn path_with_query(&self) -> Option<_rt::String> { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 3 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 3 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]incoming-request.path-with-query"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result6 = match l2 { + 0 => None, + 1 => { + let e = { + let l3 = *ptr0 + .add(::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l4 = *ptr0 + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len5 = l4; + let bytes5 = _rt::Vec::from_raw_parts( + l3.cast(), + len5, + len5, + ); + _rt::string_lift(bytes5) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result6 + } + } + } + impl IncomingRequest { + #[allow(unused_unsafe, clippy::all)] + /// Returns the protocol scheme from the request. + #[allow(async_fn_in_trait)] + pub fn scheme(&self) -> Option { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 4 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 4 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]incoming-request.scheme"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result8 = match l2 { + 0 => None, + 1 => { + let e = { + let l3 = i32::from( + *ptr0.add(::core::mem::size_of::<*const u8>()).cast::(), + ); + let v7 = match l3 { + 0 => Scheme::Http, + 1 => Scheme::Https, + n => { + debug_assert_eq!(n, 2, "invalid enum discriminant"); + let e7 = { + let l4 = *ptr0 + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l5 = *ptr0 + .add(3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len6 = l5; + let bytes6 = _rt::Vec::from_raw_parts( + l4.cast(), + len6, + len6, + ); + _rt::string_lift(bytes6) + }; + Scheme::Other(e7) + } + }; + v7 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result8 + } + } + } + impl IncomingRequest { + #[allow(unused_unsafe, clippy::all)] + /// Returns the authority of the Request's target URI, if present. + #[allow(async_fn_in_trait)] + pub fn authority(&self) -> Option<_rt::String> { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 3 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 3 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]incoming-request.authority"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result6 = match l2 { + 0 => None, + 1 => { + let e = { + let l3 = *ptr0 + .add(::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l4 = *ptr0 + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len5 = l4; + let bytes5 = _rt::Vec::from_raw_parts( + l3.cast(), + len5, + len5, + ); + _rt::string_lift(bytes5) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result6 + } + } + } + impl IncomingRequest { + #[allow(unused_unsafe, clippy::all)] + /// Get the `headers` associated with the request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// The `headers` returned are a child resource: it must be dropped before + /// the parent `incoming-request` is dropped. Dropping this + /// `incoming-request` before all children are dropped will trap. + #[allow(async_fn_in_trait)] + pub fn headers(&self) -> Headers { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]incoming-request.headers"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((self).handle() as i32); + Fields::from_handle(ret as u32) + } + } + } + impl IncomingRequest { + #[allow(unused_unsafe, clippy::all)] + /// Gives the `incoming-body` associated with this request. Will only + /// return success at most once, and subsequent calls will return error. + #[allow(async_fn_in_trait)] + pub fn consume(&self) -> Result { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 8], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]incoming-request.consume"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(4).cast::(); + IncomingBody::from_handle(l3 as u32) + }; + Ok(e) + } + 1 => { + let e = (); + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl OutgoingRequest { + #[allow(unused_unsafe, clippy::all)] + /// Construct a new `outgoing-request` with a default `method` of `GET`, and + /// `none` values for `path-with-query`, `scheme`, and `authority`. + /// + /// * `headers` is the HTTP Headers for the Request. + /// + /// It is possible to construct, or manipulate with the accessor functions + /// below, an `outgoing-request` with an invalid combination of `scheme` + /// and `authority`, or `headers` which are not permitted to be sent. + /// It is the obligation of the `outgoing-handler.handle` implementation + /// to reject invalid constructions of `outgoing-request`. + #[allow(async_fn_in_trait)] + pub fn new(headers: Headers) -> Self { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[constructor]outgoing-request"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((&headers).take_handle() as i32); + OutgoingRequest::from_handle(ret as u32) + } + } + } + impl OutgoingRequest { + #[allow(unused_unsafe, clippy::all)] + /// Returns the resource corresponding to the outgoing Body for this + /// Request. + /// + /// Returns success on the first call: the `outgoing-body` resource for + /// this `outgoing-request` can be retrieved at most once. Subsequent + /// calls will return error. + #[allow(async_fn_in_trait)] + pub fn body(&self) -> Result { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 8], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]outgoing-request.body"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(4).cast::(); + OutgoingBody::from_handle(l3 as u32) + }; + Ok(e) + } + 1 => { + let e = (); + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl OutgoingRequest { + #[allow(unused_unsafe, clippy::all)] + /// Get the Method for the Request. + #[allow(async_fn_in_trait)] + pub fn method(&self) -> Method { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 3 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 3 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]outgoing-request.method"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let v6 = match l2 { + 0 => Method::Get, + 1 => Method::Head, + 2 => Method::Post, + 3 => Method::Put, + 4 => Method::Delete, + 5 => Method::Connect, + 6 => Method::Options, + 7 => Method::Trace, + 8 => Method::Patch, + n => { + debug_assert_eq!(n, 9, "invalid enum discriminant"); + let e6 = { + let l3 = *ptr0 + .add(::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l4 = *ptr0 + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len5 = l4; + let bytes5 = _rt::Vec::from_raw_parts( + l3.cast(), + len5, + len5, + ); + _rt::string_lift(bytes5) + }; + Method::Other(e6) + } + }; + let result7 = v6; + result7 + } + } + } + impl OutgoingRequest { + #[allow(unused_unsafe, clippy::all)] + /// Set the Method for the Request. Fails if the string present in a + /// `method.other` argument is not a syntactically valid method. + #[allow(async_fn_in_trait)] + pub fn set_method(&self, method: &Method) -> Result<(), ()> { + unsafe { + let (result1_0, result1_1, result1_2) = match method { + Method::Get => (0i32, ::core::ptr::null_mut(), 0usize), + Method::Head => (1i32, ::core::ptr::null_mut(), 0usize), + Method::Post => (2i32, ::core::ptr::null_mut(), 0usize), + Method::Put => (3i32, ::core::ptr::null_mut(), 0usize), + Method::Delete => (4i32, ::core::ptr::null_mut(), 0usize), + Method::Connect => (5i32, ::core::ptr::null_mut(), 0usize), + Method::Options => (6i32, ::core::ptr::null_mut(), 0usize), + Method::Trace => (7i32, ::core::ptr::null_mut(), 0usize), + Method::Patch => (8i32, ::core::ptr::null_mut(), 0usize), + Method::Other(e) => { + let vec0 = e; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + (9i32, ptr0.cast_mut(), len0) + } + }; + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]outgoing-request.set-method"] + fn wit_import2(_: i32, _: i32, _: *mut u8, _: usize) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import2( + _: i32, + _: i32, + _: *mut u8, + _: usize, + ) -> i32 { + unreachable!() + } + let ret = wit_import2( + (self).handle() as i32, + result1_0, + result1_1, + result1_2, + ); + match ret { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = (); + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + } + } + } + } + impl OutgoingRequest { + #[allow(unused_unsafe, clippy::all)] + /// Get the combination of the HTTP Path and Query for the Request. + /// When `none`, this represents an empty Path and empty Query. + #[allow(async_fn_in_trait)] + pub fn path_with_query(&self) -> Option<_rt::String> { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 3 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 3 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]outgoing-request.path-with-query"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result6 = match l2 { + 0 => None, + 1 => { + let e = { + let l3 = *ptr0 + .add(::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l4 = *ptr0 + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len5 = l4; + let bytes5 = _rt::Vec::from_raw_parts( + l3.cast(), + len5, + len5, + ); + _rt::string_lift(bytes5) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result6 + } + } + } + impl OutgoingRequest { + #[allow(unused_unsafe, clippy::all)] + /// Set the combination of the HTTP Path and Query for the Request. + /// When `none`, this represents an empty Path and empty Query. Fails is the + /// string given is not a syntactically valid path and query uri component. + #[allow(async_fn_in_trait)] + pub fn set_path_with_query( + &self, + path_with_query: Option<&str>, + ) -> Result<(), ()> { + unsafe { + let (result1_0, result1_1, result1_2) = match path_with_query { + Some(e) => { + let vec0 = e; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + (1i32, ptr0.cast_mut(), len0) + } + None => (0i32, ::core::ptr::null_mut(), 0usize), + }; + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]outgoing-request.set-path-with-query"] + fn wit_import2(_: i32, _: i32, _: *mut u8, _: usize) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import2( + _: i32, + _: i32, + _: *mut u8, + _: usize, + ) -> i32 { + unreachable!() + } + let ret = wit_import2( + (self).handle() as i32, + result1_0, + result1_1, + result1_2, + ); + match ret { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = (); + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + } + } + } + } + impl OutgoingRequest { + #[allow(unused_unsafe, clippy::all)] + /// Get the HTTP Related Scheme for the Request. When `none`, the + /// implementation may choose an appropriate default scheme. + #[allow(async_fn_in_trait)] + pub fn scheme(&self) -> Option { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 4 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 4 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]outgoing-request.scheme"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result8 = match l2 { + 0 => None, + 1 => { + let e = { + let l3 = i32::from( + *ptr0.add(::core::mem::size_of::<*const u8>()).cast::(), + ); + let v7 = match l3 { + 0 => Scheme::Http, + 1 => Scheme::Https, + n => { + debug_assert_eq!(n, 2, "invalid enum discriminant"); + let e7 = { + let l4 = *ptr0 + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l5 = *ptr0 + .add(3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len6 = l5; + let bytes6 = _rt::Vec::from_raw_parts( + l4.cast(), + len6, + len6, + ); + _rt::string_lift(bytes6) + }; + Scheme::Other(e7) + } + }; + v7 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result8 + } + } + } + impl OutgoingRequest { + #[allow(unused_unsafe, clippy::all)] + /// Set the HTTP Related Scheme for the Request. When `none`, the + /// implementation may choose an appropriate default scheme. Fails if the + /// string given is not a syntactically valid uri scheme. + #[allow(async_fn_in_trait)] + pub fn set_scheme(&self, scheme: Option<&Scheme>) -> Result<(), ()> { + unsafe { + let (result2_0, result2_1, result2_2, result2_3) = match scheme { + Some(e) => { + let (result1_0, result1_1, result1_2) = match e { + Scheme::Http => (0i32, ::core::ptr::null_mut(), 0usize), + Scheme::Https => (1i32, ::core::ptr::null_mut(), 0usize), + Scheme::Other(e) => { + let vec0 = e; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + (2i32, ptr0.cast_mut(), len0) + } + }; + (1i32, result1_0, result1_1, result1_2) + } + None => (0i32, 0i32, ::core::ptr::null_mut(), 0usize), + }; + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]outgoing-request.set-scheme"] + fn wit_import3( + _: i32, + _: i32, + _: i32, + _: *mut u8, + _: usize, + ) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import3( + _: i32, + _: i32, + _: i32, + _: *mut u8, + _: usize, + ) -> i32 { + unreachable!() + } + let ret = wit_import3( + (self).handle() as i32, + result2_0, + result2_1, + result2_2, + result2_3, + ); + match ret { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = (); + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + } + } + } + } + impl OutgoingRequest { + #[allow(unused_unsafe, clippy::all)] + /// Get the authority of the Request's target URI. A value of `none` may be used + /// with Related Schemes which do not require an authority. The HTTP and + /// HTTPS schemes always require an authority. + #[allow(async_fn_in_trait)] + pub fn authority(&self) -> Option<_rt::String> { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 3 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 3 + * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]outgoing-request.authority"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result6 = match l2 { + 0 => None, + 1 => { + let e = { + let l3 = *ptr0 + .add(::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l4 = *ptr0 + .add(2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len5 = l4; + let bytes5 = _rt::Vec::from_raw_parts( + l3.cast(), + len5, + len5, + ); + _rt::string_lift(bytes5) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result6 + } + } + } + impl OutgoingRequest { + #[allow(unused_unsafe, clippy::all)] + /// Set the authority of the Request's target URI. A value of `none` may be used + /// with Related Schemes which do not require an authority. The HTTP and + /// HTTPS schemes always require an authority. Fails if the string given is + /// not a syntactically valid URI authority. + #[allow(async_fn_in_trait)] + pub fn set_authority(&self, authority: Option<&str>) -> Result<(), ()> { + unsafe { + let (result1_0, result1_1, result1_2) = match authority { + Some(e) => { + let vec0 = e; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + (1i32, ptr0.cast_mut(), len0) + } + None => (0i32, ::core::ptr::null_mut(), 0usize), + }; + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]outgoing-request.set-authority"] + fn wit_import2(_: i32, _: i32, _: *mut u8, _: usize) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import2( + _: i32, + _: i32, + _: *mut u8, + _: usize, + ) -> i32 { + unreachable!() + } + let ret = wit_import2( + (self).handle() as i32, + result1_0, + result1_1, + result1_2, + ); + match ret { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = (); + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + } + } + } + } + impl OutgoingRequest { + #[allow(unused_unsafe, clippy::all)] + /// Get the headers associated with the Request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// This headers resource is a child: it must be dropped before the parent + /// `outgoing-request` is dropped, or its ownership is transferred to + /// another component by e.g. `outgoing-handler.handle`. + #[allow(async_fn_in_trait)] + pub fn headers(&self) -> Headers { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]outgoing-request.headers"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((self).handle() as i32); + Fields::from_handle(ret as u32) + } + } + } + impl RequestOptions { + #[allow(unused_unsafe, clippy::all)] + /// Construct a default `request-options` value. + #[allow(async_fn_in_trait)] + pub fn new() -> Self { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[constructor]request-options"] + fn wit_import0() -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0() -> i32 { + unreachable!() + } + let ret = wit_import0(); + RequestOptions::from_handle(ret as u32) + } + } + } + impl RequestOptions { + #[allow(unused_unsafe, clippy::all)] + /// The timeout for the initial connect to the HTTP Server. + #[allow(async_fn_in_trait)] + pub fn connect_timeout(&self) -> Option { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 16], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]request-options.connect-timeout"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => None, + 1 => { + let e = { + let l3 = *ptr0.add(8).cast::(); + l3 as u64 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl RequestOptions { + #[allow(unused_unsafe, clippy::all)] + /// Set the timeout for the initial connect to the HTTP Server. An error + /// return value indicates that this timeout is not supported. + #[allow(async_fn_in_trait)] + pub fn set_connect_timeout( + &self, + duration: Option, + ) -> Result<(), ()> { + unsafe { + let (result0_0, result0_1) = match duration { + Some(e) => (1i32, _rt::as_i64(e)), + None => (0i32, 0i64), + }; + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]request-options.set-connect-timeout"] + fn wit_import1(_: i32, _: i32, _: i64) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i32, _: i64) -> i32 { + unreachable!() + } + let ret = wit_import1( + (self).handle() as i32, + result0_0, + result0_1, + ); + match ret { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = (); + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + } + } + } + } + impl RequestOptions { + #[allow(unused_unsafe, clippy::all)] + /// The timeout for receiving the first byte of the Response body. + #[allow(async_fn_in_trait)] + pub fn first_byte_timeout(&self) -> Option { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 16], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]request-options.first-byte-timeout"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => None, + 1 => { + let e = { + let l3 = *ptr0.add(8).cast::(); + l3 as u64 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl RequestOptions { + #[allow(unused_unsafe, clippy::all)] + /// Set the timeout for receiving the first byte of the Response body. An + /// error return value indicates that this timeout is not supported. + #[allow(async_fn_in_trait)] + pub fn set_first_byte_timeout( + &self, + duration: Option, + ) -> Result<(), ()> { + unsafe { + let (result0_0, result0_1) = match duration { + Some(e) => (1i32, _rt::as_i64(e)), + None => (0i32, 0i64), + }; + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]request-options.set-first-byte-timeout"] + fn wit_import1(_: i32, _: i32, _: i64) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i32, _: i64) -> i32 { + unreachable!() + } + let ret = wit_import1( + (self).handle() as i32, + result0_0, + result0_1, + ); + match ret { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = (); + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + } + } + } + } + impl RequestOptions { + #[allow(unused_unsafe, clippy::all)] + /// The timeout for receiving subsequent chunks of bytes in the Response + /// body stream. + #[allow(async_fn_in_trait)] + pub fn between_bytes_timeout(&self) -> Option { + unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit; 16]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 16], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]request-options.between-bytes-timeout"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => None, + 1 => { + let e = { + let l3 = *ptr0.add(8).cast::(); + l3 as u64 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl RequestOptions { + #[allow(unused_unsafe, clippy::all)] + /// Set the timeout for receiving subsequent chunks of bytes in the Response + /// body stream. An error return value indicates that this timeout is not + /// supported. + #[allow(async_fn_in_trait)] + pub fn set_between_bytes_timeout( + &self, + duration: Option, + ) -> Result<(), ()> { + unsafe { + let (result0_0, result0_1) = match duration { + Some(e) => (1i32, _rt::as_i64(e)), + None => (0i32, 0i64), + }; + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]request-options.set-between-bytes-timeout"] + fn wit_import1(_: i32, _: i32, _: i64) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: i32, _: i64) -> i32 { + unreachable!() + } + let ret = wit_import1( + (self).handle() as i32, + result0_0, + result0_1, + ); + match ret { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = (); + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + } + } + } + } + impl ResponseOutparam { + #[allow(unused_unsafe, clippy::all)] + /// Set the value of the `response-outparam` to either send a response, + /// or indicate an error. + /// + /// This method consumes the `response-outparam` to ensure that it is + /// called at most once. If it is never called, the implementation + /// will respond with an error. + /// + /// The user may provide an `error` to `response` to allow the + /// implementation determine how to respond with an HTTP error response. + #[allow(async_fn_in_trait)] + pub fn set( + param: ResponseOutparam, + response: Result, + ) -> () { + unsafe { + let ( + result38_0, + result38_1, + result38_2, + result38_3, + result38_4, + result38_5, + result38_6, + result38_7, + ) = match &response { + Ok(e) => { + ( + 0i32, + (e).take_handle() as i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + Err(e) => { + let ( + result37_0, + result37_1, + result37_2, + result37_3, + result37_4, + result37_5, + result37_6, + ) = match e { + ErrorCode::DnsTimeout => { + ( + 0i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::DnsError(e) => { + let DnsErrorPayload { + rcode: rcode0, + info_code: info_code0, + } = e; + let (result2_0, result2_1, result2_2) = match rcode0 { + Some(e) => { + let vec1 = e; + let ptr1 = vec1.as_ptr().cast::(); + let len1 = vec1.len(); + (1i32, ptr1.cast_mut(), len1) + } + None => (0i32, ::core::ptr::null_mut(), 0usize), + }; + let (result3_0, result3_1) = match info_code0 { + Some(e) => (1i32, _rt::as_i32(e)), + None => (0i32, 0i32), + }; + ( + 1i32, + result2_0, + { + let mut t = ::core::mem::MaybeUninit::::uninit(); + t.as_mut_ptr().cast::<*mut u8>().write(result2_1); + t + }, + result2_2 as *mut u8, + result3_0 as *mut u8, + result3_1 as usize, + 0i32, + ) + } + ErrorCode::DestinationNotFound => { + ( + 2i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::DestinationUnavailable => { + ( + 3i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::DestinationIpProhibited => { + ( + 4i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::DestinationIpUnroutable => { + ( + 5i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::ConnectionRefused => { + ( + 6i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::ConnectionTerminated => { + ( + 7i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::ConnectionTimeout => { + ( + 8i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::ConnectionReadTimeout => { + ( + 9i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::ConnectionWriteTimeout => { + ( + 10i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::ConnectionLimitReached => { + ( + 11i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::TlsProtocolError => { + ( + 12i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::TlsCertificateError => { + ( + 13i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::TlsAlertReceived(e) => { + let TlsAlertReceivedPayload { + alert_id: alert_id4, + alert_message: alert_message4, + } = e; + let (result5_0, result5_1) = match alert_id4 { + Some(e) => (1i32, _rt::as_i32(e)), + None => (0i32, 0i32), + }; + let (result7_0, result7_1, result7_2) = match alert_message4 { + Some(e) => { + let vec6 = e; + let ptr6 = vec6.as_ptr().cast::(); + let len6 = vec6.len(); + (1i32, ptr6.cast_mut(), len6) + } + None => (0i32, ::core::ptr::null_mut(), 0usize), + }; + ( + 14i32, + result5_0, + ::core::mem::MaybeUninit::new(i64::from(result5_1) as u64), + result7_0 as *mut u8, + result7_1, + result7_2, + 0i32, + ) + } + ErrorCode::HttpRequestDenied => { + ( + 15i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::HttpRequestLengthRequired => { + ( + 16i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::HttpRequestBodySize(e) => { + let (result8_0, result8_1) = match e { + Some(e) => (1i32, _rt::as_i64(e)), + None => (0i32, 0i64), + }; + ( + 17i32, + result8_0, + ::core::mem::MaybeUninit::new(result8_1 as u64), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::HttpRequestMethodInvalid => { + ( + 18i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::HttpRequestUriInvalid => { + ( + 19i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::HttpRequestUriTooLong => { + ( + 20i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::HttpRequestHeaderSectionSize(e) => { + let (result9_0, result9_1) = match e { + Some(e) => (1i32, _rt::as_i32(e)), + None => (0i32, 0i32), + }; + ( + 21i32, + result9_0, + ::core::mem::MaybeUninit::new(i64::from(result9_1) as u64), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::HttpRequestHeaderSize(e) => { + let ( + result14_0, + result14_1, + result14_2, + result14_3, + result14_4, + result14_5, + ) = match e { + Some(e) => { + let FieldSizePayload { + field_name: field_name10, + field_size: field_size10, + } = e; + let (result12_0, result12_1, result12_2) = match field_name10 { + Some(e) => { + let vec11 = e; + let ptr11 = vec11.as_ptr().cast::(); + let len11 = vec11.len(); + (1i32, ptr11.cast_mut(), len11) + } + None => (0i32, ::core::ptr::null_mut(), 0usize), + }; + let (result13_0, result13_1) = match field_size10 { + Some(e) => (1i32, _rt::as_i32(e)), + None => (0i32, 0i32), + }; + ( + 1i32, + result12_0, + result12_1, + result12_2, + result13_0, + result13_1, + ) + } + None => { + (0i32, 0i32, ::core::ptr::null_mut(), 0usize, 0i32, 0i32) + } + }; + ( + 22i32, + result14_0, + ::core::mem::MaybeUninit::new(i64::from(result14_1) as u64), + result14_2, + result14_3 as *mut u8, + result14_4 as usize, + result14_5, + ) + } + ErrorCode::HttpRequestTrailerSectionSize(e) => { + let (result15_0, result15_1) = match e { + Some(e) => (1i32, _rt::as_i32(e)), + None => (0i32, 0i32), + }; + ( + 23i32, + result15_0, + ::core::mem::MaybeUninit::new(i64::from(result15_1) as u64), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::HttpRequestTrailerSize(e) => { + let FieldSizePayload { + field_name: field_name16, + field_size: field_size16, + } = e; + let (result18_0, result18_1, result18_2) = match field_name16 { + Some(e) => { + let vec17 = e; + let ptr17 = vec17.as_ptr().cast::(); + let len17 = vec17.len(); + (1i32, ptr17.cast_mut(), len17) + } + None => (0i32, ::core::ptr::null_mut(), 0usize), + }; + let (result19_0, result19_1) = match field_size16 { + Some(e) => (1i32, _rt::as_i32(e)), + None => (0i32, 0i32), + }; + ( + 24i32, + result18_0, + { + let mut t = ::core::mem::MaybeUninit::::uninit(); + t.as_mut_ptr().cast::<*mut u8>().write(result18_1); + t + }, + result18_2 as *mut u8, + result19_0 as *mut u8, + result19_1 as usize, + 0i32, + ) + } + ErrorCode::HttpResponseIncomplete => { + ( + 25i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::HttpResponseHeaderSectionSize(e) => { + let (result20_0, result20_1) = match e { + Some(e) => (1i32, _rt::as_i32(e)), + None => (0i32, 0i32), + }; + ( + 26i32, + result20_0, + ::core::mem::MaybeUninit::new(i64::from(result20_1) as u64), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::HttpResponseHeaderSize(e) => { + let FieldSizePayload { + field_name: field_name21, + field_size: field_size21, + } = e; + let (result23_0, result23_1, result23_2) = match field_name21 { + Some(e) => { + let vec22 = e; + let ptr22 = vec22.as_ptr().cast::(); + let len22 = vec22.len(); + (1i32, ptr22.cast_mut(), len22) + } + None => (0i32, ::core::ptr::null_mut(), 0usize), + }; + let (result24_0, result24_1) = match field_size21 { + Some(e) => (1i32, _rt::as_i32(e)), + None => (0i32, 0i32), + }; + ( + 27i32, + result23_0, + { + let mut t = ::core::mem::MaybeUninit::::uninit(); + t.as_mut_ptr().cast::<*mut u8>().write(result23_1); + t + }, + result23_2 as *mut u8, + result24_0 as *mut u8, + result24_1 as usize, + 0i32, + ) + } + ErrorCode::HttpResponseBodySize(e) => { + let (result25_0, result25_1) = match e { + Some(e) => (1i32, _rt::as_i64(e)), + None => (0i32, 0i64), + }; + ( + 28i32, + result25_0, + ::core::mem::MaybeUninit::new(result25_1 as u64), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::HttpResponseTrailerSectionSize(e) => { + let (result26_0, result26_1) = match e { + Some(e) => (1i32, _rt::as_i32(e)), + None => (0i32, 0i32), + }; + ( + 29i32, + result26_0, + ::core::mem::MaybeUninit::new(i64::from(result26_1) as u64), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::HttpResponseTrailerSize(e) => { + let FieldSizePayload { + field_name: field_name27, + field_size: field_size27, + } = e; + let (result29_0, result29_1, result29_2) = match field_name27 { + Some(e) => { + let vec28 = e; + let ptr28 = vec28.as_ptr().cast::(); + let len28 = vec28.len(); + (1i32, ptr28.cast_mut(), len28) + } + None => (0i32, ::core::ptr::null_mut(), 0usize), + }; + let (result30_0, result30_1) = match field_size27 { + Some(e) => (1i32, _rt::as_i32(e)), + None => (0i32, 0i32), + }; + ( + 30i32, + result29_0, + { + let mut t = ::core::mem::MaybeUninit::::uninit(); + t.as_mut_ptr().cast::<*mut u8>().write(result29_1); + t + }, + result29_2 as *mut u8, + result30_0 as *mut u8, + result30_1 as usize, + 0i32, + ) + } + ErrorCode::HttpResponseTransferCoding(e) => { + let (result32_0, result32_1, result32_2) = match e { + Some(e) => { + let vec31 = e; + let ptr31 = vec31.as_ptr().cast::(); + let len31 = vec31.len(); + (1i32, ptr31.cast_mut(), len31) + } + None => (0i32, ::core::ptr::null_mut(), 0usize), + }; + ( + 31i32, + result32_0, + { + let mut t = ::core::mem::MaybeUninit::::uninit(); + t.as_mut_ptr().cast::<*mut u8>().write(result32_1); + t + }, + result32_2 as *mut u8, + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::HttpResponseContentCoding(e) => { + let (result34_0, result34_1, result34_2) = match e { + Some(e) => { + let vec33 = e; + let ptr33 = vec33.as_ptr().cast::(); + let len33 = vec33.len(); + (1i32, ptr33.cast_mut(), len33) + } + None => (0i32, ::core::ptr::null_mut(), 0usize), + }; + ( + 32i32, + result34_0, + { + let mut t = ::core::mem::MaybeUninit::::uninit(); + t.as_mut_ptr().cast::<*mut u8>().write(result34_1); + t + }, + result34_2 as *mut u8, + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::HttpResponseTimeout => { + ( + 33i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::HttpUpgradeFailed => { + ( + 34i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::HttpProtocolError => { + ( + 35i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::LoopDetected => { + ( + 36i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::ConfigurationError => { + ( + 37i32, + 0i32, + ::core::mem::MaybeUninit::::zeroed(), + ::core::ptr::null_mut(), + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + ErrorCode::InternalError(e) => { + let (result36_0, result36_1, result36_2) = match e { + Some(e) => { + let vec35 = e; + let ptr35 = vec35.as_ptr().cast::(); + let len35 = vec35.len(); + (1i32, ptr35.cast_mut(), len35) + } + None => (0i32, ::core::ptr::null_mut(), 0usize), + }; + ( + 38i32, + result36_0, + { + let mut t = ::core::mem::MaybeUninit::::uninit(); + t.as_mut_ptr().cast::<*mut u8>().write(result36_1); + t + }, + result36_2 as *mut u8, + ::core::ptr::null_mut(), + 0usize, + 0i32, + ) + } + }; + ( + 1i32, + result37_0, + result37_1, + result37_2, + result37_3, + result37_4, + result37_5, + result37_6, + ) + } + }; + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[static]response-outparam.set"] + fn wit_import39( + _: i32, + _: i32, + _: i32, + _: i32, + _: ::core::mem::MaybeUninit, + _: *mut u8, + _: *mut u8, + _: usize, + _: i32, + ); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import39( + _: i32, + _: i32, + _: i32, + _: i32, + _: ::core::mem::MaybeUninit, + _: *mut u8, + _: *mut u8, + _: usize, + _: i32, + ) { + unreachable!() + } + wit_import39( + (¶m).take_handle() as i32, + result38_0, + result38_1, + result38_2, + result38_3, + result38_4, + result38_5, + result38_6, + result38_7, + ); + } + } + } + impl IncomingResponse { + #[allow(unused_unsafe, clippy::all)] + /// Returns the status code from the incoming response. + #[allow(async_fn_in_trait)] + pub fn status(&self) -> StatusCode { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]incoming-response.status"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((self).handle() as i32); + ret as u16 + } + } + } + impl IncomingResponse { + #[allow(unused_unsafe, clippy::all)] + /// Returns the headers from the incoming response. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// This headers resource is a child: it must be dropped before the parent + /// `incoming-response` is dropped. + #[allow(async_fn_in_trait)] + pub fn headers(&self) -> Headers { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]incoming-response.headers"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((self).handle() as i32); + Fields::from_handle(ret as u32) + } + } + } + impl IncomingResponse { + #[allow(unused_unsafe, clippy::all)] + /// Returns the incoming body. May be called at most once. Returns error + /// if called additional times. + #[allow(async_fn_in_trait)] + pub fn consume(&self) -> Result { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 8], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]incoming-response.consume"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(4).cast::(); + IncomingBody::from_handle(l3 as u32) + }; + Ok(e) + } + 1 => { + let e = (); + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl IncomingBody { + #[allow(unused_unsafe, clippy::all)] + /// Returns the contents of the body, as a stream of bytes. + /// + /// Returns success on first call: the stream representing the contents + /// can be retrieved at most once. Subsequent calls will return error. + /// + /// The returned `input-stream` resource is a child: it must be dropped + /// before the parent `incoming-body` is dropped, or consumed by + /// `incoming-body.finish`. + /// + /// This invariant ensures that the implementation can determine whether + /// the user is consuming the contents of the body, waiting on the + /// `future-trailers` to be ready, or neither. This allows for network + /// backpressure is to be applied when the user is consuming the body, + /// and for that backpressure to not inhibit delivery of the trailers if + /// the user does not read the entire body. + #[allow(async_fn_in_trait)] + pub fn stream(&self) -> Result { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 8], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]incoming-body.stream"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(4).cast::(); + super::super::super::__with_name5::InputStream::from_handle( + l3 as u32, + ) + }; + Ok(e) + } + 1 => { + let e = (); + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl IncomingBody { + #[allow(unused_unsafe, clippy::all)] + /// Takes ownership of `incoming-body`, and returns a `future-trailers`. + /// This function will trap if the `input-stream` child is still alive. + #[allow(async_fn_in_trait)] + pub fn finish(this: IncomingBody) -> FutureTrailers { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[static]incoming-body.finish"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((&this).take_handle() as i32); + FutureTrailers::from_handle(ret as u32) + } + } + } + impl FutureTrailers { + #[allow(unused_unsafe, clippy::all)] + /// Returns a pollable which becomes ready when either the trailers have + /// been received, or an error has occurred. When this pollable is ready, + /// the `get` method will return `some`. + #[allow(async_fn_in_trait)] + pub fn subscribe(&self) -> Pollable { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]future-trailers.subscribe"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((self).handle() as i32); + super::super::super::__with_name0::Pollable::from_handle( + ret as u32, + ) + } + } + } + impl FutureTrailers { + #[allow(unused_unsafe, clippy::all)] + /// Returns the contents of the trailers, or an error which occurred, + /// once the future is ready. + /// + /// The outer `option` represents future readiness. Users can wait on this + /// `option` to become `some` using the `subscribe` method. + /// + /// The outer `result` is used to retrieve the trailers or error at most + /// once. It will be success on the first call in which the outer option + /// is `some`, and error on subsequent calls. + /// + /// The inner `result` represents that either the HTTP Request or Response + /// body, as well as any trailers, were received successfully, or that an + /// error occurred receiving them. The optional `trailers` indicates whether + /// or not trailers were present in the body. + /// + /// When some `trailers` are returned by this method, the `trailers` + /// resource is immutable, and a child. Use of the `set`, `append`, or + /// `delete` methods will return an error, and the resource must be + /// dropped before the parent `future-trailers` is dropped. + #[allow(async_fn_in_trait)] + pub fn get( + &self, + ) -> Option, ErrorCode>, ()>> { + unsafe { + #[repr(align(8))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 40 + 4 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 40 + + 4 * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]future-trailers.get"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result70 = match l2 { + 0 => None, + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(8).cast::()); + match l3 { + 0 => { + let e = { + let l4 = i32::from(*ptr0.add(16).cast::()); + match l4 { + 0 => { + let e = { + let l5 = i32::from(*ptr0.add(24).cast::()); + match l5 { + 0 => None, + 1 => { + let e = { + let l6 = *ptr0.add(28).cast::(); + Fields::from_handle(l6 as u32) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + Ok(e) + } + 1 => { + let e = { + let l7 = i32::from(*ptr0.add(24).cast::()); + let v69 = match l7 { + 0 => ErrorCode::DnsTimeout, + 1 => { + let e69 = { + let l8 = i32::from(*ptr0.add(32).cast::()); + let l12 = i32::from( + *ptr0 + .add(32 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + DnsErrorPayload { + rcode: match l8 { + 0 => None, + 1 => { + let e = { + let l9 = *ptr0 + .add(32 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l10 = *ptr0 + .add(32 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len11 = l10; + let bytes11 = _rt::Vec::from_raw_parts( + l9.cast(), + len11, + len11, + ); + _rt::string_lift(bytes11) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + info_code: match l12 { + 0 => None, + 1 => { + let e = { + let l13 = i32::from( + *ptr0 + .add(34 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + l13 as u16 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + ErrorCode::DnsError(e69) + } + 2 => ErrorCode::DestinationNotFound, + 3 => ErrorCode::DestinationUnavailable, + 4 => ErrorCode::DestinationIpProhibited, + 5 => ErrorCode::DestinationIpUnroutable, + 6 => ErrorCode::ConnectionRefused, + 7 => ErrorCode::ConnectionTerminated, + 8 => ErrorCode::ConnectionTimeout, + 9 => ErrorCode::ConnectionReadTimeout, + 10 => ErrorCode::ConnectionWriteTimeout, + 11 => ErrorCode::ConnectionLimitReached, + 12 => ErrorCode::TlsProtocolError, + 13 => ErrorCode::TlsCertificateError, + 14 => { + let e69 = { + let l14 = i32::from(*ptr0.add(32).cast::()); + let l16 = i32::from( + *ptr0 + .add(32 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + TlsAlertReceivedPayload { + alert_id: match l14 { + 0 => None, + 1 => { + let e = { + let l15 = i32::from(*ptr0.add(33).cast::()); + l15 as u8 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + alert_message: match l16 { + 0 => None, + 1 => { + let e = { + let l17 = *ptr0 + .add(32 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l18 = *ptr0 + .add(32 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len19 = l18; + let bytes19 = _rt::Vec::from_raw_parts( + l17.cast(), + len19, + len19, + ); + _rt::string_lift(bytes19) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + ErrorCode::TlsAlertReceived(e69) + } + 15 => ErrorCode::HttpRequestDenied, + 16 => ErrorCode::HttpRequestLengthRequired, + 17 => { + let e69 = { + let l20 = i32::from(*ptr0.add(32).cast::()); + match l20 { + 0 => None, + 1 => { + let e = { + let l21 = *ptr0.add(40).cast::(); + l21 as u64 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpRequestBodySize(e69) + } + 18 => ErrorCode::HttpRequestMethodInvalid, + 19 => ErrorCode::HttpRequestUriInvalid, + 20 => ErrorCode::HttpRequestUriTooLong, + 21 => { + let e69 = { + let l22 = i32::from(*ptr0.add(32).cast::()); + match l22 { + 0 => None, + 1 => { + let e = { + let l23 = *ptr0.add(36).cast::(); + l23 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpRequestHeaderSectionSize(e69) + } + 22 => { + let e69 = { + let l24 = i32::from(*ptr0.add(32).cast::()); + match l24 { + 0 => None, + 1 => { + let e = { + let l25 = i32::from( + *ptr0 + .add(32 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + let l29 = i32::from( + *ptr0 + .add(32 + 4 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + FieldSizePayload { + field_name: match l25 { + 0 => None, + 1 => { + let e = { + let l26 = *ptr0 + .add(32 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l27 = *ptr0 + .add(32 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len28 = l27; + let bytes28 = _rt::Vec::from_raw_parts( + l26.cast(), + len28, + len28, + ); + _rt::string_lift(bytes28) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + field_size: match l29 { + 0 => None, + 1 => { + let e = { + let l30 = *ptr0 + .add(36 + 4 * ::core::mem::size_of::<*const u8>()) + .cast::(); + l30 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpRequestHeaderSize(e69) + } + 23 => { + let e69 = { + let l31 = i32::from(*ptr0.add(32).cast::()); + match l31 { + 0 => None, + 1 => { + let e = { + let l32 = *ptr0.add(36).cast::(); + l32 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpRequestTrailerSectionSize(e69) + } + 24 => { + let e69 = { + let l33 = i32::from(*ptr0.add(32).cast::()); + let l37 = i32::from( + *ptr0 + .add(32 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + FieldSizePayload { + field_name: match l33 { + 0 => None, + 1 => { + let e = { + let l34 = *ptr0 + .add(32 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l35 = *ptr0 + .add(32 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len36 = l35; + let bytes36 = _rt::Vec::from_raw_parts( + l34.cast(), + len36, + len36, + ); + _rt::string_lift(bytes36) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + field_size: match l37 { + 0 => None, + 1 => { + let e = { + let l38 = *ptr0 + .add(36 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + l38 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + ErrorCode::HttpRequestTrailerSize(e69) + } + 25 => ErrorCode::HttpResponseIncomplete, + 26 => { + let e69 = { + let l39 = i32::from(*ptr0.add(32).cast::()); + match l39 { + 0 => None, + 1 => { + let e = { + let l40 = *ptr0.add(36).cast::(); + l40 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpResponseHeaderSectionSize(e69) + } + 27 => { + let e69 = { + let l41 = i32::from(*ptr0.add(32).cast::()); + let l45 = i32::from( + *ptr0 + .add(32 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + FieldSizePayload { + field_name: match l41 { + 0 => None, + 1 => { + let e = { + let l42 = *ptr0 + .add(32 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l43 = *ptr0 + .add(32 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len44 = l43; + let bytes44 = _rt::Vec::from_raw_parts( + l42.cast(), + len44, + len44, + ); + _rt::string_lift(bytes44) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + field_size: match l45 { + 0 => None, + 1 => { + let e = { + let l46 = *ptr0 + .add(36 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + l46 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + ErrorCode::HttpResponseHeaderSize(e69) + } + 28 => { + let e69 = { + let l47 = i32::from(*ptr0.add(32).cast::()); + match l47 { + 0 => None, + 1 => { + let e = { + let l48 = *ptr0.add(40).cast::(); + l48 as u64 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpResponseBodySize(e69) + } + 29 => { + let e69 = { + let l49 = i32::from(*ptr0.add(32).cast::()); + match l49 { + 0 => None, + 1 => { + let e = { + let l50 = *ptr0.add(36).cast::(); + l50 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpResponseTrailerSectionSize(e69) + } + 30 => { + let e69 = { + let l51 = i32::from(*ptr0.add(32).cast::()); + let l55 = i32::from( + *ptr0 + .add(32 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + FieldSizePayload { + field_name: match l51 { + 0 => None, + 1 => { + let e = { + let l52 = *ptr0 + .add(32 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l53 = *ptr0 + .add(32 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len54 = l53; + let bytes54 = _rt::Vec::from_raw_parts( + l52.cast(), + len54, + len54, + ); + _rt::string_lift(bytes54) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + field_size: match l55 { + 0 => None, + 1 => { + let e = { + let l56 = *ptr0 + .add(36 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + l56 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + ErrorCode::HttpResponseTrailerSize(e69) + } + 31 => { + let e69 = { + let l57 = i32::from(*ptr0.add(32).cast::()); + match l57 { + 0 => None, + 1 => { + let e = { + let l58 = *ptr0 + .add(32 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l59 = *ptr0 + .add(32 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len60 = l59; + let bytes60 = _rt::Vec::from_raw_parts( + l58.cast(), + len60, + len60, + ); + _rt::string_lift(bytes60) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpResponseTransferCoding(e69) + } + 32 => { + let e69 = { + let l61 = i32::from(*ptr0.add(32).cast::()); + match l61 { + 0 => None, + 1 => { + let e = { + let l62 = *ptr0 + .add(32 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l63 = *ptr0 + .add(32 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len64 = l63; + let bytes64 = _rt::Vec::from_raw_parts( + l62.cast(), + len64, + len64, + ); + _rt::string_lift(bytes64) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpResponseContentCoding(e69) + } + 33 => ErrorCode::HttpResponseTimeout, + 34 => ErrorCode::HttpUpgradeFailed, + 35 => ErrorCode::HttpProtocolError, + 36 => ErrorCode::LoopDetected, + 37 => ErrorCode::ConfigurationError, + n => { + debug_assert_eq!(n, 38, "invalid enum discriminant"); + let e69 = { + let l65 = i32::from(*ptr0.add(32).cast::()); + match l65 { + 0 => None, + 1 => { + let e = { + let l66 = *ptr0 + .add(32 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l67 = *ptr0 + .add(32 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len68 = l67; + let bytes68 = _rt::Vec::from_raw_parts( + l66.cast(), + len68, + len68, + ); + _rt::string_lift(bytes68) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::InternalError(e69) + } + }; + v69 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + Ok(e) + } + 1 => { + let e = (); + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result70 + } + } + } + impl OutgoingResponse { + #[allow(unused_unsafe, clippy::all)] + /// Construct an `outgoing-response`, with a default `status-code` of `200`. + /// If a different `status-code` is needed, it must be set via the + /// `set-status-code` method. + /// + /// * `headers` is the HTTP Headers for the Response. + #[allow(async_fn_in_trait)] + pub fn new(headers: Headers) -> Self { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[constructor]outgoing-response"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((&headers).take_handle() as i32); + OutgoingResponse::from_handle(ret as u32) + } + } + } + impl OutgoingResponse { + #[allow(unused_unsafe, clippy::all)] + /// Get the HTTP Status Code for the Response. + #[allow(async_fn_in_trait)] + pub fn status_code(&self) -> StatusCode { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]outgoing-response.status-code"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((self).handle() as i32); + ret as u16 + } + } + } + impl OutgoingResponse { + #[allow(unused_unsafe, clippy::all)] + /// Set the HTTP Status Code for the Response. Fails if the status-code + /// given is not a valid http status code. + #[allow(async_fn_in_trait)] + pub fn set_status_code( + &self, + status_code: StatusCode, + ) -> Result<(), ()> { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]outgoing-response.set-status-code"] + fn wit_import0(_: i32, _: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32, _: i32) -> i32 { + unreachable!() + } + let ret = wit_import0( + (self).handle() as i32, + _rt::as_i32(status_code), + ); + match ret { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = (); + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + } + } + } + } + impl OutgoingResponse { + #[allow(unused_unsafe, clippy::all)] + /// Get the headers associated with the Request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// This headers resource is a child: it must be dropped before the parent + /// `outgoing-request` is dropped, or its ownership is transferred to + /// another component by e.g. `outgoing-handler.handle`. + #[allow(async_fn_in_trait)] + pub fn headers(&self) -> Headers { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]outgoing-response.headers"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((self).handle() as i32); + Fields::from_handle(ret as u32) + } + } + } + impl OutgoingResponse { + #[allow(unused_unsafe, clippy::all)] + /// Returns the resource corresponding to the outgoing Body for this Response. + /// + /// Returns success on the first call: the `outgoing-body` resource for + /// this `outgoing-response` can be retrieved at most once. Subsequent + /// calls will return error. + #[allow(async_fn_in_trait)] + pub fn body(&self) -> Result { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 8], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]outgoing-response.body"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(4).cast::(); + OutgoingBody::from_handle(l3 as u32) + }; + Ok(e) + } + 1 => { + let e = (); + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl OutgoingBody { + #[allow(unused_unsafe, clippy::all)] + /// Returns a stream for writing the body contents. + /// + /// The returned `output-stream` is a child resource: it must be dropped + /// before the parent `outgoing-body` resource is dropped (or finished), + /// otherwise the `outgoing-body` drop or `finish` will trap. + /// + /// Returns success on the first call: the `output-stream` resource for + /// this `outgoing-body` may be retrieved at most once. Subsequent calls + /// will return error. + #[allow(async_fn_in_trait)] + pub fn write(&self) -> Result { + unsafe { + #[repr(align(4))] + struct RetArea([::core::mem::MaybeUninit; 8]); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 8], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]outgoing-body.write"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result4 = match l2 { + 0 => { + let e = { + let l3 = *ptr0.add(4).cast::(); + super::super::super::__with_name5::OutputStream::from_handle( + l3 as u32, + ) + }; + Ok(e) + } + 1 => { + let e = (); + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result4 + } + } + } + impl OutgoingBody { + #[allow(unused_unsafe, clippy::all)] + /// Finalize an outgoing body, optionally providing trailers. This must be + /// called to signal that the response is complete. If the `outgoing-body` + /// is dropped without calling `outgoing-body.finalize`, the implementation + /// should treat the body as corrupted. + /// + /// Fails if the body's `outgoing-request` or `outgoing-response` was + /// constructed with a Content-Length header, and the contents written + /// to the body (via `write`) does not match the value given in the + /// Content-Length. + #[allow(async_fn_in_trait)] + pub fn finish( + this: OutgoingBody, + trailers: Option, + ) -> Result<(), ErrorCode> { + unsafe { + #[repr(align(8))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 24 + 4 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 24 + + 4 * ::core::mem::size_of::<*const u8>()], + ); + let (result0_0, result0_1) = match &trailers { + Some(e) => (1i32, (e).take_handle() as i32), + None => (0i32, 0i32), + }; + let ptr1 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[static]outgoing-body.finish"] + fn wit_import2(_: i32, _: i32, _: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import2( + _: i32, + _: i32, + _: i32, + _: *mut u8, + ) { + unreachable!() + } + wit_import2( + (&this).take_handle() as i32, + result0_0, + result0_1, + ptr1, + ); + let l3 = i32::from(*ptr1.add(0).cast::()); + let result67 = match l3 { + 0 => { + let e = (); + Ok(e) + } + 1 => { + let e = { + let l4 = i32::from(*ptr1.add(8).cast::()); + let v66 = match l4 { + 0 => ErrorCode::DnsTimeout, + 1 => { + let e66 = { + let l5 = i32::from(*ptr1.add(16).cast::()); + let l9 = i32::from( + *ptr1 + .add(16 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + DnsErrorPayload { + rcode: match l5 { + 0 => None, + 1 => { + let e = { + let l6 = *ptr1 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l7 = *ptr1 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len8 = l7; + let bytes8 = _rt::Vec::from_raw_parts( + l6.cast(), + len8, + len8, + ); + _rt::string_lift(bytes8) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + info_code: match l9 { + 0 => None, + 1 => { + let e = { + let l10 = i32::from( + *ptr1 + .add(18 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + l10 as u16 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + ErrorCode::DnsError(e66) + } + 2 => ErrorCode::DestinationNotFound, + 3 => ErrorCode::DestinationUnavailable, + 4 => ErrorCode::DestinationIpProhibited, + 5 => ErrorCode::DestinationIpUnroutable, + 6 => ErrorCode::ConnectionRefused, + 7 => ErrorCode::ConnectionTerminated, + 8 => ErrorCode::ConnectionTimeout, + 9 => ErrorCode::ConnectionReadTimeout, + 10 => ErrorCode::ConnectionWriteTimeout, + 11 => ErrorCode::ConnectionLimitReached, + 12 => ErrorCode::TlsProtocolError, + 13 => ErrorCode::TlsCertificateError, + 14 => { + let e66 = { + let l11 = i32::from(*ptr1.add(16).cast::()); + let l13 = i32::from( + *ptr1 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + TlsAlertReceivedPayload { + alert_id: match l11 { + 0 => None, + 1 => { + let e = { + let l12 = i32::from(*ptr1.add(17).cast::()); + l12 as u8 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + alert_message: match l13 { + 0 => None, + 1 => { + let e = { + let l14 = *ptr1 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l15 = *ptr1 + .add(16 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len16 = l15; + let bytes16 = _rt::Vec::from_raw_parts( + l14.cast(), + len16, + len16, + ); + _rt::string_lift(bytes16) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + ErrorCode::TlsAlertReceived(e66) + } + 15 => ErrorCode::HttpRequestDenied, + 16 => ErrorCode::HttpRequestLengthRequired, + 17 => { + let e66 = { + let l17 = i32::from(*ptr1.add(16).cast::()); + match l17 { + 0 => None, + 1 => { + let e = { + let l18 = *ptr1.add(24).cast::(); + l18 as u64 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpRequestBodySize(e66) + } + 18 => ErrorCode::HttpRequestMethodInvalid, + 19 => ErrorCode::HttpRequestUriInvalid, + 20 => ErrorCode::HttpRequestUriTooLong, + 21 => { + let e66 = { + let l19 = i32::from(*ptr1.add(16).cast::()); + match l19 { + 0 => None, + 1 => { + let e = { + let l20 = *ptr1.add(20).cast::(); + l20 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpRequestHeaderSectionSize(e66) + } + 22 => { + let e66 = { + let l21 = i32::from(*ptr1.add(16).cast::()); + match l21 { + 0 => None, + 1 => { + let e = { + let l22 = i32::from( + *ptr1 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + let l26 = i32::from( + *ptr1 + .add(16 + 4 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + FieldSizePayload { + field_name: match l22 { + 0 => None, + 1 => { + let e = { + let l23 = *ptr1 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l24 = *ptr1 + .add(16 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len25 = l24; + let bytes25 = _rt::Vec::from_raw_parts( + l23.cast(), + len25, + len25, + ); + _rt::string_lift(bytes25) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + field_size: match l26 { + 0 => None, + 1 => { + let e = { + let l27 = *ptr1 + .add(20 + 4 * ::core::mem::size_of::<*const u8>()) + .cast::(); + l27 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpRequestHeaderSize(e66) + } + 23 => { + let e66 = { + let l28 = i32::from(*ptr1.add(16).cast::()); + match l28 { + 0 => None, + 1 => { + let e = { + let l29 = *ptr1.add(20).cast::(); + l29 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpRequestTrailerSectionSize(e66) + } + 24 => { + let e66 = { + let l30 = i32::from(*ptr1.add(16).cast::()); + let l34 = i32::from( + *ptr1 + .add(16 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + FieldSizePayload { + field_name: match l30 { + 0 => None, + 1 => { + let e = { + let l31 = *ptr1 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l32 = *ptr1 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len33 = l32; + let bytes33 = _rt::Vec::from_raw_parts( + l31.cast(), + len33, + len33, + ); + _rt::string_lift(bytes33) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + field_size: match l34 { + 0 => None, + 1 => { + let e = { + let l35 = *ptr1 + .add(20 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + l35 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + ErrorCode::HttpRequestTrailerSize(e66) + } + 25 => ErrorCode::HttpResponseIncomplete, + 26 => { + let e66 = { + let l36 = i32::from(*ptr1.add(16).cast::()); + match l36 { + 0 => None, + 1 => { + let e = { + let l37 = *ptr1.add(20).cast::(); + l37 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpResponseHeaderSectionSize(e66) + } + 27 => { + let e66 = { + let l38 = i32::from(*ptr1.add(16).cast::()); + let l42 = i32::from( + *ptr1 + .add(16 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + FieldSizePayload { + field_name: match l38 { + 0 => None, + 1 => { + let e = { + let l39 = *ptr1 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l40 = *ptr1 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len41 = l40; + let bytes41 = _rt::Vec::from_raw_parts( + l39.cast(), + len41, + len41, + ); + _rt::string_lift(bytes41) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + field_size: match l42 { + 0 => None, + 1 => { + let e = { + let l43 = *ptr1 + .add(20 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + l43 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + ErrorCode::HttpResponseHeaderSize(e66) + } + 28 => { + let e66 = { + let l44 = i32::from(*ptr1.add(16).cast::()); + match l44 { + 0 => None, + 1 => { + let e = { + let l45 = *ptr1.add(24).cast::(); + l45 as u64 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpResponseBodySize(e66) + } + 29 => { + let e66 = { + let l46 = i32::from(*ptr1.add(16).cast::()); + match l46 { + 0 => None, + 1 => { + let e = { + let l47 = *ptr1.add(20).cast::(); + l47 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpResponseTrailerSectionSize(e66) + } + 30 => { + let e66 = { + let l48 = i32::from(*ptr1.add(16).cast::()); + let l52 = i32::from( + *ptr1 + .add(16 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + FieldSizePayload { + field_name: match l48 { + 0 => None, + 1 => { + let e = { + let l49 = *ptr1 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l50 = *ptr1 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len51 = l50; + let bytes51 = _rt::Vec::from_raw_parts( + l49.cast(), + len51, + len51, + ); + _rt::string_lift(bytes51) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + field_size: match l52 { + 0 => None, + 1 => { + let e = { + let l53 = *ptr1 + .add(20 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + l53 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + ErrorCode::HttpResponseTrailerSize(e66) + } + 31 => { + let e66 = { + let l54 = i32::from(*ptr1.add(16).cast::()); + match l54 { + 0 => None, + 1 => { + let e = { + let l55 = *ptr1 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l56 = *ptr1 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len57 = l56; + let bytes57 = _rt::Vec::from_raw_parts( + l55.cast(), + len57, + len57, + ); + _rt::string_lift(bytes57) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpResponseTransferCoding(e66) + } + 32 => { + let e66 = { + let l58 = i32::from(*ptr1.add(16).cast::()); + match l58 { + 0 => None, + 1 => { + let e = { + let l59 = *ptr1 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l60 = *ptr1 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len61 = l60; + let bytes61 = _rt::Vec::from_raw_parts( + l59.cast(), + len61, + len61, + ); + _rt::string_lift(bytes61) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpResponseContentCoding(e66) + } + 33 => ErrorCode::HttpResponseTimeout, + 34 => ErrorCode::HttpUpgradeFailed, + 35 => ErrorCode::HttpProtocolError, + 36 => ErrorCode::LoopDetected, + 37 => ErrorCode::ConfigurationError, + n => { + debug_assert_eq!(n, 38, "invalid enum discriminant"); + let e66 = { + let l62 = i32::from(*ptr1.add(16).cast::()); + match l62 { + 0 => None, + 1 => { + let e = { + let l63 = *ptr1 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l64 = *ptr1 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len65 = l64; + let bytes65 = _rt::Vec::from_raw_parts( + l63.cast(), + len65, + len65, + ); + _rt::string_lift(bytes65) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::InternalError(e66) + } + }; + v66 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result67 + } + } + } + impl FutureIncomingResponse { + #[allow(unused_unsafe, clippy::all)] + /// Returns a pollable which becomes ready when either the Response has + /// been received, or an error has occurred. When this pollable is ready, + /// the `get` method will return `some`. + #[allow(async_fn_in_trait)] + pub fn subscribe(&self) -> Pollable { + unsafe { + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]future-incoming-response.subscribe"] + fn wit_import0(_: i32) -> i32; + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import0(_: i32) -> i32 { + unreachable!() + } + let ret = wit_import0((self).handle() as i32); + super::super::super::__with_name0::Pollable::from_handle( + ret as u32, + ) + } + } + } + impl FutureIncomingResponse { + #[allow(unused_unsafe, clippy::all)] + /// Returns the incoming HTTP Response, or an error, once one is ready. + /// + /// The outer `option` represents future readiness. Users can wait on this + /// `option` to become `some` using the `subscribe` method. + /// + /// The outer `result` is used to retrieve the response or error at most + /// once. It will be success on the first call in which the outer option + /// is `some`, and error on subsequent calls. + /// + /// The inner `result` represents that either the incoming HTTP Response + /// status and headers have received successfully, or that an error + /// occurred. Errors may also occur while consuming the response body, + /// but those will be reported by the `incoming-body` and its + /// `output-stream` child. + #[allow(async_fn_in_trait)] + pub fn get( + &self, + ) -> Option, ()>> { + unsafe { + #[repr(align(8))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 40 + 4 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 40 + + 4 * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "[method]future-incoming-response.get"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((self).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result69 = match l2 { + 0 => None, + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(8).cast::()); + match l3 { + 0 => { + let e = { + let l4 = i32::from(*ptr0.add(16).cast::()); + match l4 { + 0 => { + let e = { + let l5 = *ptr0.add(24).cast::(); + IncomingResponse::from_handle(l5 as u32) + }; + Ok(e) + } + 1 => { + let e = { + let l6 = i32::from(*ptr0.add(24).cast::()); + let v68 = match l6 { + 0 => ErrorCode::DnsTimeout, + 1 => { + let e68 = { + let l7 = i32::from(*ptr0.add(32).cast::()); + let l11 = i32::from( + *ptr0 + .add(32 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + DnsErrorPayload { + rcode: match l7 { + 0 => None, + 1 => { + let e = { + let l8 = *ptr0 + .add(32 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l9 = *ptr0 + .add(32 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len10 = l9; + let bytes10 = _rt::Vec::from_raw_parts( + l8.cast(), + len10, + len10, + ); + _rt::string_lift(bytes10) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + info_code: match l11 { + 0 => None, + 1 => { + let e = { + let l12 = i32::from( + *ptr0 + .add(34 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + l12 as u16 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + ErrorCode::DnsError(e68) + } + 2 => ErrorCode::DestinationNotFound, + 3 => ErrorCode::DestinationUnavailable, + 4 => ErrorCode::DestinationIpProhibited, + 5 => ErrorCode::DestinationIpUnroutable, + 6 => ErrorCode::ConnectionRefused, + 7 => ErrorCode::ConnectionTerminated, + 8 => ErrorCode::ConnectionTimeout, + 9 => ErrorCode::ConnectionReadTimeout, + 10 => ErrorCode::ConnectionWriteTimeout, + 11 => ErrorCode::ConnectionLimitReached, + 12 => ErrorCode::TlsProtocolError, + 13 => ErrorCode::TlsCertificateError, + 14 => { + let e68 = { + let l13 = i32::from(*ptr0.add(32).cast::()); + let l15 = i32::from( + *ptr0 + .add(32 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + TlsAlertReceivedPayload { + alert_id: match l13 { + 0 => None, + 1 => { + let e = { + let l14 = i32::from(*ptr0.add(33).cast::()); + l14 as u8 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + alert_message: match l15 { + 0 => None, + 1 => { + let e = { + let l16 = *ptr0 + .add(32 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l17 = *ptr0 + .add(32 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len18 = l17; + let bytes18 = _rt::Vec::from_raw_parts( + l16.cast(), + len18, + len18, + ); + _rt::string_lift(bytes18) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + ErrorCode::TlsAlertReceived(e68) + } + 15 => ErrorCode::HttpRequestDenied, + 16 => ErrorCode::HttpRequestLengthRequired, + 17 => { + let e68 = { + let l19 = i32::from(*ptr0.add(32).cast::()); + match l19 { + 0 => None, + 1 => { + let e = { + let l20 = *ptr0.add(40).cast::(); + l20 as u64 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpRequestBodySize(e68) + } + 18 => ErrorCode::HttpRequestMethodInvalid, + 19 => ErrorCode::HttpRequestUriInvalid, + 20 => ErrorCode::HttpRequestUriTooLong, + 21 => { + let e68 = { + let l21 = i32::from(*ptr0.add(32).cast::()); + match l21 { + 0 => None, + 1 => { + let e = { + let l22 = *ptr0.add(36).cast::(); + l22 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpRequestHeaderSectionSize(e68) + } + 22 => { + let e68 = { + let l23 = i32::from(*ptr0.add(32).cast::()); + match l23 { + 0 => None, + 1 => { + let e = { + let l24 = i32::from( + *ptr0 + .add(32 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + let l28 = i32::from( + *ptr0 + .add(32 + 4 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + FieldSizePayload { + field_name: match l24 { + 0 => None, + 1 => { + let e = { + let l25 = *ptr0 + .add(32 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l26 = *ptr0 + .add(32 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len27 = l26; + let bytes27 = _rt::Vec::from_raw_parts( + l25.cast(), + len27, + len27, + ); + _rt::string_lift(bytes27) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + field_size: match l28 { + 0 => None, + 1 => { + let e = { + let l29 = *ptr0 + .add(36 + 4 * ::core::mem::size_of::<*const u8>()) + .cast::(); + l29 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpRequestHeaderSize(e68) + } + 23 => { + let e68 = { + let l30 = i32::from(*ptr0.add(32).cast::()); + match l30 { + 0 => None, + 1 => { + let e = { + let l31 = *ptr0.add(36).cast::(); + l31 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpRequestTrailerSectionSize(e68) + } + 24 => { + let e68 = { + let l32 = i32::from(*ptr0.add(32).cast::()); + let l36 = i32::from( + *ptr0 + .add(32 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + FieldSizePayload { + field_name: match l32 { + 0 => None, + 1 => { + let e = { + let l33 = *ptr0 + .add(32 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l34 = *ptr0 + .add(32 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len35 = l34; + let bytes35 = _rt::Vec::from_raw_parts( + l33.cast(), + len35, + len35, + ); + _rt::string_lift(bytes35) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + field_size: match l36 { + 0 => None, + 1 => { + let e = { + let l37 = *ptr0 + .add(36 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + l37 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + ErrorCode::HttpRequestTrailerSize(e68) + } + 25 => ErrorCode::HttpResponseIncomplete, + 26 => { + let e68 = { + let l38 = i32::from(*ptr0.add(32).cast::()); + match l38 { + 0 => None, + 1 => { + let e = { + let l39 = *ptr0.add(36).cast::(); + l39 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpResponseHeaderSectionSize(e68) + } + 27 => { + let e68 = { + let l40 = i32::from(*ptr0.add(32).cast::()); + let l44 = i32::from( + *ptr0 + .add(32 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + FieldSizePayload { + field_name: match l40 { + 0 => None, + 1 => { + let e = { + let l41 = *ptr0 + .add(32 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l42 = *ptr0 + .add(32 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len43 = l42; + let bytes43 = _rt::Vec::from_raw_parts( + l41.cast(), + len43, + len43, + ); + _rt::string_lift(bytes43) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + field_size: match l44 { + 0 => None, + 1 => { + let e = { + let l45 = *ptr0 + .add(36 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + l45 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + ErrorCode::HttpResponseHeaderSize(e68) + } + 28 => { + let e68 = { + let l46 = i32::from(*ptr0.add(32).cast::()); + match l46 { + 0 => None, + 1 => { + let e = { + let l47 = *ptr0.add(40).cast::(); + l47 as u64 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpResponseBodySize(e68) + } + 29 => { + let e68 = { + let l48 = i32::from(*ptr0.add(32).cast::()); + match l48 { + 0 => None, + 1 => { + let e = { + let l49 = *ptr0.add(36).cast::(); + l49 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpResponseTrailerSectionSize(e68) + } + 30 => { + let e68 = { + let l50 = i32::from(*ptr0.add(32).cast::()); + let l54 = i32::from( + *ptr0 + .add(32 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + FieldSizePayload { + field_name: match l50 { + 0 => None, + 1 => { + let e = { + let l51 = *ptr0 + .add(32 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l52 = *ptr0 + .add(32 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len53 = l52; + let bytes53 = _rt::Vec::from_raw_parts( + l51.cast(), + len53, + len53, + ); + _rt::string_lift(bytes53) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + field_size: match l54 { + 0 => None, + 1 => { + let e = { + let l55 = *ptr0 + .add(36 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + l55 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + ErrorCode::HttpResponseTrailerSize(e68) + } + 31 => { + let e68 = { + let l56 = i32::from(*ptr0.add(32).cast::()); + match l56 { + 0 => None, + 1 => { + let e = { + let l57 = *ptr0 + .add(32 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l58 = *ptr0 + .add(32 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len59 = l58; + let bytes59 = _rt::Vec::from_raw_parts( + l57.cast(), + len59, + len59, + ); + _rt::string_lift(bytes59) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpResponseTransferCoding(e68) + } + 32 => { + let e68 = { + let l60 = i32::from(*ptr0.add(32).cast::()); + match l60 { + 0 => None, + 1 => { + let e = { + let l61 = *ptr0 + .add(32 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l62 = *ptr0 + .add(32 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len63 = l62; + let bytes63 = _rt::Vec::from_raw_parts( + l61.cast(), + len63, + len63, + ); + _rt::string_lift(bytes63) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpResponseContentCoding(e68) + } + 33 => ErrorCode::HttpResponseTimeout, + 34 => ErrorCode::HttpUpgradeFailed, + 35 => ErrorCode::HttpProtocolError, + 36 => ErrorCode::LoopDetected, + 37 => ErrorCode::ConfigurationError, + n => { + debug_assert_eq!(n, 38, "invalid enum discriminant"); + let e68 = { + let l64 = i32::from(*ptr0.add(32).cast::()); + match l64 { + 0 => None, + 1 => { + let e = { + let l65 = *ptr0 + .add(32 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l66 = *ptr0 + .add(32 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len67 = l66; + let bytes67 = _rt::Vec::from_raw_parts( + l65.cast(), + len67, + len67, + ); + _rt::string_lift(bytes67) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::InternalError(e68) + } + }; + v68 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + Ok(e) + } + 1 => { + let e = (); + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result69 + } + } + } + #[allow(unused_unsafe, clippy::all)] + /// Attempts to extract a http-related `error` from the wasi:io `error` + /// provided. + /// + /// Stream operations which return + /// `wasi:io/stream.stream-error.last-operation-failed` have a payload of + /// type `wasi:io/error.error` with more information about the operation + /// that failed. This payload can be passed through to this function to see + /// if there's http-related information about the error to return. + /// + /// Note that this function is fallible because not all io-errors are + /// http-related errors. + #[allow(async_fn_in_trait)] + pub fn http_error_code(err: &IoError) -> Option { + unsafe { + #[repr(align(8))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 24 + 4 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 24 + + 4 * ::core::mem::size_of::<*const u8>()], + ); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/types@0.2.9")] + unsafe extern "C" { + #[link_name = "http-error-code"] + fn wit_import1(_: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import1(_: i32, _: *mut u8) { + unreachable!() + } + wit_import1((err).handle() as i32, ptr0); + let l2 = i32::from(*ptr0.add(0).cast::()); + let result66 = match l2 { + 0 => None, + 1 => { + let e = { + let l3 = i32::from(*ptr0.add(8).cast::()); + let v65 = match l3 { + 0 => ErrorCode::DnsTimeout, + 1 => { + let e65 = { + let l4 = i32::from(*ptr0.add(16).cast::()); + let l8 = i32::from( + *ptr0 + .add(16 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + DnsErrorPayload { + rcode: match l4 { + 0 => None, + 1 => { + let e = { + let l5 = *ptr0 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l6 = *ptr0 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len7 = l6; + let bytes7 = _rt::Vec::from_raw_parts( + l5.cast(), + len7, + len7, + ); + _rt::string_lift(bytes7) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + info_code: match l8 { + 0 => None, + 1 => { + let e = { + let l9 = i32::from( + *ptr0 + .add(18 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + l9 as u16 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + ErrorCode::DnsError(e65) + } + 2 => ErrorCode::DestinationNotFound, + 3 => ErrorCode::DestinationUnavailable, + 4 => ErrorCode::DestinationIpProhibited, + 5 => ErrorCode::DestinationIpUnroutable, + 6 => ErrorCode::ConnectionRefused, + 7 => ErrorCode::ConnectionTerminated, + 8 => ErrorCode::ConnectionTimeout, + 9 => ErrorCode::ConnectionReadTimeout, + 10 => ErrorCode::ConnectionWriteTimeout, + 11 => ErrorCode::ConnectionLimitReached, + 12 => ErrorCode::TlsProtocolError, + 13 => ErrorCode::TlsCertificateError, + 14 => { + let e65 = { + let l10 = i32::from(*ptr0.add(16).cast::()); + let l12 = i32::from( + *ptr0 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + TlsAlertReceivedPayload { + alert_id: match l10 { + 0 => None, + 1 => { + let e = { + let l11 = i32::from(*ptr0.add(17).cast::()); + l11 as u8 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + alert_message: match l12 { + 0 => None, + 1 => { + let e = { + let l13 = *ptr0 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l14 = *ptr0 + .add(16 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len15 = l14; + let bytes15 = _rt::Vec::from_raw_parts( + l13.cast(), + len15, + len15, + ); + _rt::string_lift(bytes15) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + ErrorCode::TlsAlertReceived(e65) + } + 15 => ErrorCode::HttpRequestDenied, + 16 => ErrorCode::HttpRequestLengthRequired, + 17 => { + let e65 = { + let l16 = i32::from(*ptr0.add(16).cast::()); + match l16 { + 0 => None, + 1 => { + let e = { + let l17 = *ptr0.add(24).cast::(); + l17 as u64 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpRequestBodySize(e65) + } + 18 => ErrorCode::HttpRequestMethodInvalid, + 19 => ErrorCode::HttpRequestUriInvalid, + 20 => ErrorCode::HttpRequestUriTooLong, + 21 => { + let e65 = { + let l18 = i32::from(*ptr0.add(16).cast::()); + match l18 { + 0 => None, + 1 => { + let e = { + let l19 = *ptr0.add(20).cast::(); + l19 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpRequestHeaderSectionSize(e65) + } + 22 => { + let e65 = { + let l20 = i32::from(*ptr0.add(16).cast::()); + match l20 { + 0 => None, + 1 => { + let e = { + let l21 = i32::from( + *ptr0 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + let l25 = i32::from( + *ptr0 + .add(16 + 4 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + FieldSizePayload { + field_name: match l21 { + 0 => None, + 1 => { + let e = { + let l22 = *ptr0 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l23 = *ptr0 + .add(16 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len24 = l23; + let bytes24 = _rt::Vec::from_raw_parts( + l22.cast(), + len24, + len24, + ); + _rt::string_lift(bytes24) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + field_size: match l25 { + 0 => None, + 1 => { + let e = { + let l26 = *ptr0 + .add(20 + 4 * ::core::mem::size_of::<*const u8>()) + .cast::(); + l26 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpRequestHeaderSize(e65) + } + 23 => { + let e65 = { + let l27 = i32::from(*ptr0.add(16).cast::()); + match l27 { + 0 => None, + 1 => { + let e = { + let l28 = *ptr0.add(20).cast::(); + l28 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpRequestTrailerSectionSize(e65) + } + 24 => { + let e65 = { + let l29 = i32::from(*ptr0.add(16).cast::()); + let l33 = i32::from( + *ptr0 + .add(16 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + FieldSizePayload { + field_name: match l29 { + 0 => None, + 1 => { + let e = { + let l30 = *ptr0 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l31 = *ptr0 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len32 = l31; + let bytes32 = _rt::Vec::from_raw_parts( + l30.cast(), + len32, + len32, + ); + _rt::string_lift(bytes32) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + field_size: match l33 { + 0 => None, + 1 => { + let e = { + let l34 = *ptr0 + .add(20 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + l34 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + ErrorCode::HttpRequestTrailerSize(e65) + } + 25 => ErrorCode::HttpResponseIncomplete, + 26 => { + let e65 = { + let l35 = i32::from(*ptr0.add(16).cast::()); + match l35 { + 0 => None, + 1 => { + let e = { + let l36 = *ptr0.add(20).cast::(); + l36 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpResponseHeaderSectionSize(e65) + } + 27 => { + let e65 = { + let l37 = i32::from(*ptr0.add(16).cast::()); + let l41 = i32::from( + *ptr0 + .add(16 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + FieldSizePayload { + field_name: match l37 { + 0 => None, + 1 => { + let e = { + let l38 = *ptr0 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l39 = *ptr0 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len40 = l39; + let bytes40 = _rt::Vec::from_raw_parts( + l38.cast(), + len40, + len40, + ); + _rt::string_lift(bytes40) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + field_size: match l41 { + 0 => None, + 1 => { + let e = { + let l42 = *ptr0 + .add(20 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + l42 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + ErrorCode::HttpResponseHeaderSize(e65) + } + 28 => { + let e65 = { + let l43 = i32::from(*ptr0.add(16).cast::()); + match l43 { + 0 => None, + 1 => { + let e = { + let l44 = *ptr0.add(24).cast::(); + l44 as u64 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpResponseBodySize(e65) + } + 29 => { + let e65 = { + let l45 = i32::from(*ptr0.add(16).cast::()); + match l45 { + 0 => None, + 1 => { + let e = { + let l46 = *ptr0.add(20).cast::(); + l46 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpResponseTrailerSectionSize(e65) + } + 30 => { + let e65 = { + let l47 = i32::from(*ptr0.add(16).cast::()); + let l51 = i32::from( + *ptr0 + .add(16 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + FieldSizePayload { + field_name: match l47 { + 0 => None, + 1 => { + let e = { + let l48 = *ptr0 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l49 = *ptr0 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len50 = l49; + let bytes50 = _rt::Vec::from_raw_parts( + l48.cast(), + len50, + len50, + ); + _rt::string_lift(bytes50) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + field_size: match l51 { + 0 => None, + 1 => { + let e = { + let l52 = *ptr0 + .add(20 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + l52 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + ErrorCode::HttpResponseTrailerSize(e65) + } + 31 => { + let e65 = { + let l53 = i32::from(*ptr0.add(16).cast::()); + match l53 { + 0 => None, + 1 => { + let e = { + let l54 = *ptr0 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l55 = *ptr0 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len56 = l55; + let bytes56 = _rt::Vec::from_raw_parts( + l54.cast(), + len56, + len56, + ); + _rt::string_lift(bytes56) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpResponseTransferCoding(e65) + } + 32 => { + let e65 = { + let l57 = i32::from(*ptr0.add(16).cast::()); + match l57 { + 0 => None, + 1 => { + let e = { + let l58 = *ptr0 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l59 = *ptr0 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len60 = l59; + let bytes60 = _rt::Vec::from_raw_parts( + l58.cast(), + len60, + len60, + ); + _rt::string_lift(bytes60) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::HttpResponseContentCoding(e65) + } + 33 => ErrorCode::HttpResponseTimeout, + 34 => ErrorCode::HttpUpgradeFailed, + 35 => ErrorCode::HttpProtocolError, + 36 => ErrorCode::LoopDetected, + 37 => ErrorCode::ConfigurationError, + n => { + debug_assert_eq!(n, 38, "invalid enum discriminant"); + let e65 = { + let l61 = i32::from(*ptr0.add(16).cast::()); + match l61 { + 0 => None, + 1 => { + let e = { + let l62 = *ptr0 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l63 = *ptr0 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len64 = l63; + let bytes64 = _rt::Vec::from_raw_parts( + l62.cast(), + len64, + len64, + ); + _rt::string_lift(bytes64) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + ErrorCode::InternalError(e65) + } + }; + v65 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result66 + } + } + } + /// This interface defines a handler of outgoing HTTP Requests. It should be + /// imported by components which wish to make HTTP Requests. + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod outgoing_handler { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use super::super::super::_rt; + pub type OutgoingRequest = super::super::super::wasi::http::types::OutgoingRequest; + pub type RequestOptions = super::super::super::wasi::http::types::RequestOptions; + pub type FutureIncomingResponse = super::super::super::wasi::http::types::FutureIncomingResponse; + pub type ErrorCode = super::super::super::wasi::http::types::ErrorCode; + #[allow(unused_unsafe, clippy::all)] + /// This function is invoked with an outgoing HTTP Request, and it returns + /// a resource `future-incoming-response` which represents an HTTP Response + /// which may arrive in the future. + /// + /// The `options` argument accepts optional parameters for the HTTP + /// protocol's transport layer. + /// + /// This function may return an error if the `outgoing-request` is invalid + /// or not allowed to be made. Otherwise, protocol errors are reported + /// through the `future-incoming-response`. + #[allow(async_fn_in_trait)] + pub fn handle( + request: OutgoingRequest, + options: Option, + ) -> Result { + unsafe { + #[repr(align(8))] + struct RetArea( + [::core::mem::MaybeUninit< + u8, + >; 24 + 4 * ::core::mem::size_of::<*const u8>()], + ); + let mut ret_area = RetArea( + [::core::mem::MaybeUninit::uninit(); 24 + + 4 * ::core::mem::size_of::<*const u8>()], + ); + let (result0_0, result0_1) = match &options { + Some(e) => (1i32, (e).take_handle() as i32), + None => (0i32, 0i32), + }; + let ptr1 = ret_area.0.as_mut_ptr().cast::(); + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "wasi:http/outgoing-handler@0.2.9")] + unsafe extern "C" { + #[link_name = "handle"] + fn wit_import2(_: i32, _: i32, _: i32, _: *mut u8); + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn wit_import2( + _: i32, + _: i32, + _: i32, + _: *mut u8, + ) { + unreachable!() + } + wit_import2( + (&request).take_handle() as i32, + result0_0, + result0_1, + ptr1, + ); + let l3 = i32::from(*ptr1.add(0).cast::()); + let result68 = match l3 { + 0 => { + let e = { + let l4 = *ptr1.add(8).cast::(); + super::super::super::wasi::http::types::FutureIncomingResponse::from_handle( + l4 as u32, + ) + }; + Ok(e) + } + 1 => { + let e = { + let l5 = i32::from(*ptr1.add(8).cast::()); + use super::super::super::wasi::http::types::ErrorCode as V67; + let v67 = match l5 { + 0 => V67::DnsTimeout, + 1 => { + let e67 = { + let l6 = i32::from(*ptr1.add(16).cast::()); + let l10 = i32::from( + *ptr1 + .add(16 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + super::super::super::wasi::http::types::DnsErrorPayload { + rcode: match l6 { + 0 => None, + 1 => { + let e = { + let l7 = *ptr1 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l8 = *ptr1 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len9 = l8; + let bytes9 = _rt::Vec::from_raw_parts( + l7.cast(), + len9, + len9, + ); + _rt::string_lift(bytes9) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + info_code: match l10 { + 0 => None, + 1 => { + let e = { + let l11 = i32::from( + *ptr1 + .add(18 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + l11 as u16 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + V67::DnsError(e67) + } + 2 => V67::DestinationNotFound, + 3 => V67::DestinationUnavailable, + 4 => V67::DestinationIpProhibited, + 5 => V67::DestinationIpUnroutable, + 6 => V67::ConnectionRefused, + 7 => V67::ConnectionTerminated, + 8 => V67::ConnectionTimeout, + 9 => V67::ConnectionReadTimeout, + 10 => V67::ConnectionWriteTimeout, + 11 => V67::ConnectionLimitReached, + 12 => V67::TlsProtocolError, + 13 => V67::TlsCertificateError, + 14 => { + let e67 = { + let l12 = i32::from(*ptr1.add(16).cast::()); + let l14 = i32::from( + *ptr1 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + super::super::super::wasi::http::types::TlsAlertReceivedPayload { + alert_id: match l12 { + 0 => None, + 1 => { + let e = { + let l13 = i32::from(*ptr1.add(17).cast::()); + l13 as u8 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + alert_message: match l14 { + 0 => None, + 1 => { + let e = { + let l15 = *ptr1 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l16 = *ptr1 + .add(16 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len17 = l16; + let bytes17 = _rt::Vec::from_raw_parts( + l15.cast(), + len17, + len17, + ); + _rt::string_lift(bytes17) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + V67::TlsAlertReceived(e67) + } + 15 => V67::HttpRequestDenied, + 16 => V67::HttpRequestLengthRequired, + 17 => { + let e67 = { + let l18 = i32::from(*ptr1.add(16).cast::()); + match l18 { + 0 => None, + 1 => { + let e = { + let l19 = *ptr1.add(24).cast::(); + l19 as u64 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + V67::HttpRequestBodySize(e67) + } + 18 => V67::HttpRequestMethodInvalid, + 19 => V67::HttpRequestUriInvalid, + 20 => V67::HttpRequestUriTooLong, + 21 => { + let e67 = { + let l20 = i32::from(*ptr1.add(16).cast::()); + match l20 { + 0 => None, + 1 => { + let e = { + let l21 = *ptr1.add(20).cast::(); + l21 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + V67::HttpRequestHeaderSectionSize(e67) + } + 22 => { + let e67 = { + let l22 = i32::from(*ptr1.add(16).cast::()); + match l22 { + 0 => None, + 1 => { + let e = { + let l23 = i32::from( + *ptr1 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + let l27 = i32::from( + *ptr1 + .add(16 + 4 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + super::super::super::wasi::http::types::FieldSizePayload { + field_name: match l23 { + 0 => None, + 1 => { + let e = { + let l24 = *ptr1 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l25 = *ptr1 + .add(16 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len26 = l25; + let bytes26 = _rt::Vec::from_raw_parts( + l24.cast(), + len26, + len26, + ); + _rt::string_lift(bytes26) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + field_size: match l27 { + 0 => None, + 1 => { + let e = { + let l28 = *ptr1 + .add(20 + 4 * ::core::mem::size_of::<*const u8>()) + .cast::(); + l28 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + V67::HttpRequestHeaderSize(e67) + } + 23 => { + let e67 = { + let l29 = i32::from(*ptr1.add(16).cast::()); + match l29 { + 0 => None, + 1 => { + let e = { + let l30 = *ptr1.add(20).cast::(); + l30 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + V67::HttpRequestTrailerSectionSize(e67) + } + 24 => { + let e67 = { + let l31 = i32::from(*ptr1.add(16).cast::()); + let l35 = i32::from( + *ptr1 + .add(16 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + super::super::super::wasi::http::types::FieldSizePayload { + field_name: match l31 { + 0 => None, + 1 => { + let e = { + let l32 = *ptr1 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l33 = *ptr1 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len34 = l33; + let bytes34 = _rt::Vec::from_raw_parts( + l32.cast(), + len34, + len34, + ); + _rt::string_lift(bytes34) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + field_size: match l35 { + 0 => None, + 1 => { + let e = { + let l36 = *ptr1 + .add(20 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + l36 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + V67::HttpRequestTrailerSize(e67) + } + 25 => V67::HttpResponseIncomplete, + 26 => { + let e67 = { + let l37 = i32::from(*ptr1.add(16).cast::()); + match l37 { + 0 => None, + 1 => { + let e = { + let l38 = *ptr1.add(20).cast::(); + l38 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + V67::HttpResponseHeaderSectionSize(e67) + } + 27 => { + let e67 = { + let l39 = i32::from(*ptr1.add(16).cast::()); + let l43 = i32::from( + *ptr1 + .add(16 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + super::super::super::wasi::http::types::FieldSizePayload { + field_name: match l39 { + 0 => None, + 1 => { + let e = { + let l40 = *ptr1 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l41 = *ptr1 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len42 = l41; + let bytes42 = _rt::Vec::from_raw_parts( + l40.cast(), + len42, + len42, + ); + _rt::string_lift(bytes42) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + field_size: match l43 { + 0 => None, + 1 => { + let e = { + let l44 = *ptr1 + .add(20 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + l44 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + V67::HttpResponseHeaderSize(e67) + } + 28 => { + let e67 = { + let l45 = i32::from(*ptr1.add(16).cast::()); + match l45 { + 0 => None, + 1 => { + let e = { + let l46 = *ptr1.add(24).cast::(); + l46 as u64 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + V67::HttpResponseBodySize(e67) + } + 29 => { + let e67 = { + let l47 = i32::from(*ptr1.add(16).cast::()); + match l47 { + 0 => None, + 1 => { + let e = { + let l48 = *ptr1.add(20).cast::(); + l48 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + V67::HttpResponseTrailerSectionSize(e67) + } + 30 => { + let e67 = { + let l49 = i32::from(*ptr1.add(16).cast::()); + let l53 = i32::from( + *ptr1 + .add(16 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(), + ); + super::super::super::wasi::http::types::FieldSizePayload { + field_name: match l49 { + 0 => None, + 1 => { + let e = { + let l50 = *ptr1 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l51 = *ptr1 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len52 = l51; + let bytes52 = _rt::Vec::from_raw_parts( + l50.cast(), + len52, + len52, + ); + _rt::string_lift(bytes52) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + field_size: match l53 { + 0 => None, + 1 => { + let e = { + let l54 = *ptr1 + .add(20 + 3 * ::core::mem::size_of::<*const u8>()) + .cast::(); + l54 as u32 + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, + } + }; + V67::HttpResponseTrailerSize(e67) + } + 31 => { + let e67 = { + let l55 = i32::from(*ptr1.add(16).cast::()); + match l55 { + 0 => None, + 1 => { + let e = { + let l56 = *ptr1 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l57 = *ptr1 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len58 = l57; + let bytes58 = _rt::Vec::from_raw_parts( + l56.cast(), + len58, + len58, + ); + _rt::string_lift(bytes58) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + V67::HttpResponseTransferCoding(e67) + } + 32 => { + let e67 = { + let l59 = i32::from(*ptr1.add(16).cast::()); + match l59 { + 0 => None, + 1 => { + let e = { + let l60 = *ptr1 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l61 = *ptr1 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len62 = l61; + let bytes62 = _rt::Vec::from_raw_parts( + l60.cast(), + len62, + len62, + ); + _rt::string_lift(bytes62) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + V67::HttpResponseContentCoding(e67) + } + 33 => V67::HttpResponseTimeout, + 34 => V67::HttpUpgradeFailed, + 35 => V67::HttpProtocolError, + 36 => V67::LoopDetected, + 37 => V67::ConfigurationError, + n => { + debug_assert_eq!(n, 38, "invalid enum discriminant"); + let e67 = { + let l63 = i32::from(*ptr1.add(16).cast::()); + match l63 { + 0 => None, + 1 => { + let e = { + let l64 = *ptr1 + .add(16 + 1 * ::core::mem::size_of::<*const u8>()) + .cast::<*mut u8>(); + let l65 = *ptr1 + .add(16 + 2 * ::core::mem::size_of::<*const u8>()) + .cast::(); + let len66 = l65; + let bytes66 = _rt::Vec::from_raw_parts( + l64.cast(), + len66, + len66, + ); + _rt::string_lift(bytes66) + }; + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + } + }; + V67::InternalError(e67) + } + }; + v67 + }; + Err(e) + } + _ => _rt::invalid_enum_discriminant(), + }; + result68 + } + } + } + } +} +#[rustfmt::skip] +#[allow(dead_code, clippy::all)] +pub mod exports { + pub mod wasi { + pub mod http { + /// This interface defines a handler of incoming HTTP Requests. It should + /// be exported by components which can respond to HTTP Requests. + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod incoming_handler { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = super::super::super::super::__link_custom_section_describing_imports; + use super::super::super::super::_rt; + pub type IncomingRequest = super::super::super::super::wasi::http::types::IncomingRequest; + pub type ResponseOutparam = super::super::super::super::wasi::http::types::ResponseOutparam; + #[doc(hidden)] + #[allow(non_snake_case, unused_unsafe)] + pub unsafe fn _export_handle_cabi(arg0: i32, arg1: i32) { + unsafe { + #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); + { + T::handle( + super::super::super::super::wasi::http::types::IncomingRequest::from_handle( + arg0 as u32, + ), + super::super::super::super::wasi::http::types::ResponseOutparam::from_handle( + arg1 as u32, + ), + ) + }; + } + } + pub trait Guest { + /// This function is invoked with an incoming HTTP Request, and a resource + /// `response-outparam` which provides the capability to reply with an HTTP + /// Response. The response is sent by calling the `response-outparam.set` + /// method, which allows execution to continue after the response has been + /// sent. This enables both streaming to the response body, and performing other + /// work. + /// + /// The implementor of this function must write a response to the + /// `response-outparam` before returning, or else the caller will respond + /// with an error on its behalf. + #[allow(async_fn_in_trait)] + fn handle( + request: IncomingRequest, + response_out: ResponseOutparam, + ) -> (); + } + #[doc(hidden)] + #[macro_export] + macro_rules! __export_wasi_http_incoming_handler_0_2_9_cabi { + ($ty:ident with_types_in $($path_to_types:tt)*) => { + const _ : () = { #[unsafe (export_name = + "wasi:http/incoming-handler@0.2.9#handle")] unsafe extern "C" fn + export_handle(arg0 : i32, arg1 : i32,) { unsafe { + $($path_to_types)*:: _export_handle_cabi::<$ty > (arg0, arg1) } } + }; + }; + } + #[doc(hidden)] + pub use __export_wasi_http_incoming_handler_0_2_9_cabi; + } + } + } +} +#[rustfmt::skip] +mod _rt { + #![allow(dead_code, unused_imports, clippy::all)] + pub use alloc_crate::string::String; + pub use alloc_crate::vec::Vec; + use core::fmt; + use core::marker; + use core::sync::atomic::{AtomicU32, Ordering::Relaxed}; + /// A type which represents a component model resource, either imported or + /// exported into this component. + /// + /// This is a low-level wrapper which handles the lifetime of the resource + /// (namely this has a destructor). The `T` provided defines the component model + /// intrinsics that this wrapper uses. + /// + /// One of the chief purposes of this type is to provide `Deref` implementations + /// to access the underlying data when it is owned. + /// + /// This type is primarily used in generated code for exported and imported + /// resources. + #[repr(transparent)] + pub struct Resource { + handle: AtomicU32, + _marker: marker::PhantomData, + } + /// A trait which all wasm resources implement, namely providing the ability to + /// drop a resource. + /// + /// This generally is implemented by generated code, not user-facing code. + #[allow(clippy::missing_safety_doc)] + pub unsafe trait WasmResource { + /// Invokes the `[resource-drop]...` intrinsic. + unsafe fn drop(handle: u32); + } + impl Resource { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + debug_assert!(handle != 0 && handle != u32::MAX); + Self { + handle: AtomicU32::new(handle), + _marker: marker::PhantomData, + } + } + /// Takes ownership of the handle owned by `resource`. + /// + /// Note that this ideally would be `into_handle` taking `Resource` by + /// ownership. The code generator does not enable that in all situations, + /// unfortunately, so this is provided instead. + /// + /// Also note that `take_handle` is in theory only ever called on values + /// owned by a generated function. For example a generated function might + /// take `Resource` as an argument but then call `take_handle` on a + /// reference to that argument. In that sense the dynamic nature of + /// `take_handle` should only be exposed internally to generated code, not + /// to user code. + #[doc(hidden)] + pub fn take_handle(resource: &Resource) -> u32 { + resource.handle.swap(u32::MAX, Relaxed) + } + #[doc(hidden)] + pub fn handle(resource: &Resource) -> u32 { + resource.handle.load(Relaxed) + } + } + impl fmt::Debug for Resource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Resource").field("handle", &self.handle).finish() + } + } + impl Drop for Resource { + fn drop(&mut self) { + unsafe { + match self.handle.load(Relaxed) { + u32::MAX => {} + other => T::drop(other), + } + } + } + } + pub use alloc_crate::alloc; + pub unsafe fn invalid_enum_discriminant() -> T { + if cfg!(debug_assertions) { + panic!("invalid enum discriminant") + } else { + unsafe { core::hint::unreachable_unchecked() } + } + } + pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { + if size == 0 { + return; + } + unsafe { + let layout = alloc::Layout::from_size_align_unchecked(size, align); + alloc::dealloc(ptr, layout); + } + } + pub unsafe fn bool_lift(val: u8) -> bool { + if cfg!(debug_assertions) { + match val { + 0 => false, + 1 => true, + _ => panic!("invalid bool discriminant"), + } + } else { + val != 0 + } + } + pub unsafe fn string_lift(bytes: Vec) -> String { + if cfg!(debug_assertions) { + String::from_utf8(bytes).unwrap() + } else { + unsafe { String::from_utf8_unchecked(bytes) } + } + } + pub fn as_i64(t: T) -> i64 { + t.as_i64() + } + pub trait AsI64 { + fn as_i64(self) -> i64; + } + impl<'a, T: Copy + AsI64> AsI64 for &'a T { + fn as_i64(self) -> i64 { + (*self).as_i64() + } + } + impl AsI64 for i64 { + #[inline] + fn as_i64(self) -> i64 { + self as i64 + } + } + impl AsI64 for u64 { + #[inline] + fn as_i64(self) -> i64 { + self as i64 + } + } + pub fn as_i32(t: T) -> i32 { + t.as_i32() + } + pub trait AsI32 { + fn as_i32(self) -> i32; + } + impl<'a, T: Copy + AsI32> AsI32 for &'a T { + fn as_i32(self) -> i32 { + (*self).as_i32() + } + } + impl AsI32 for i32 { + #[inline] + fn as_i32(self) -> i32 { + self as i32 + } + } + impl AsI32 for u32 { + #[inline] + fn as_i32(self) -> i32 { + self as i32 + } + } + impl AsI32 for i16 { + #[inline] + fn as_i32(self) -> i32 { + self as i32 + } + } + impl AsI32 for u16 { + #[inline] + fn as_i32(self) -> i32 { + self as i32 + } + } + impl AsI32 for i8 { + #[inline] + fn as_i32(self) -> i32 { + self as i32 + } + } + impl AsI32 for u8 { + #[inline] + fn as_i32(self) -> i32 { + self as i32 + } + } + impl AsI32 for char { + #[inline] + fn as_i32(self) -> i32 { + self as i32 + } + } + impl AsI32 for usize { + #[inline] + fn as_i32(self) -> i32 { + self as i32 + } + } + #[cfg(target_arch = "wasm32")] + pub fn run_ctors_once() { + wit_bindgen::rt::run_ctors_once(); + } + extern crate alloc as alloc_crate; +} +/// Generates `#[unsafe(no_mangle)]` functions to export the specified type as +/// the root implementation of all generated traits. +/// +/// For more information see the documentation of `wit_bindgen::generate!`. +/// +/// ```rust +/// # macro_rules! _export_proxy{ ($($t:tt)*) => (); } +/// # trait Guest {} +/// struct MyType; +/// +/// impl Guest for MyType { +/// // ... +/// } +/// +/// _export_proxy!(MyType); +/// ``` +#[allow(unused_macros)] +#[doc(hidden)] +#[macro_export] +macro_rules! __export_proxy_impl { + ($ty:ident) => { + $crate::_export_proxy!($ty with_types_in $crate); + }; + ($ty:ident with_types_in $($path_to_types_root:tt)*) => { + $($path_to_types_root)*:: + exports::wasi::http::incoming_handler::__export_wasi_http_incoming_handler_0_2_9_cabi!($ty + with_types_in $($path_to_types_root)*:: exports::wasi::http::incoming_handler); + const _ : () = { #[rustfmt::skip] #[cfg(target_arch = "wasm32")] #[unsafe + (link_section = + "component-type:wit-bindgen:0.51.0:wasi:http@0.2.9:proxy:imports and exportsrust-wasip2-1.0.2+wasi-0.2.9-from-crates-io-proxy-world")] + #[doc(hidden)] #[allow(clippy::octal_escapes)] pub static + __WIT_BINDGEN_COMPONENT_TYPE : [u8; 7040] = * + b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x846\x01A\x02\x01A#\x01\ +B\x0a\x04\0\x08pollable\x03\x01\x01h\0\x01@\x01\x04self\x01\0\x7f\x04\0\x16[meth\ +od]pollable.ready\x01\x02\x01@\x01\x04self\x01\x01\0\x04\0\x16[method]pollable.b\ +lock\x01\x03\x01p\x01\x01py\x01@\x01\x02in\x04\0\x05\x04\0\x04poll\x01\x06\x03\0\ +\x12wasi:io/poll@0.2.9\x05\0\x02\x03\0\0\x08pollable\x01B\x0f\x02\x03\x02\x01\x01\ +\x04\0\x08pollable\x03\0\0\x01w\x04\0\x07instant\x03\0\x02\x01w\x04\0\x08duratio\ +n\x03\0\x04\x01@\0\0\x03\x04\0\x03now\x01\x06\x01@\0\0\x05\x04\0\x0aresolution\x01\ +\x07\x01i\x01\x01@\x01\x04when\x03\0\x08\x04\0\x11subscribe-instant\x01\x09\x01@\ +\x01\x04when\x05\0\x08\x04\0\x12subscribe-duration\x01\x0a\x03\0!wasi:clocks/mon\ +otonic-clock@0.2.9\x05\x02\x01B\x05\x01r\x02\x07secondsw\x0bnanosecondsy\x04\0\x08\ +datetime\x03\0\0\x01@\0\0\x01\x04\0\x03now\x01\x02\x04\0\x0aresolution\x01\x02\x03\ +\0\x1cwasi:clocks/wall-clock@0.2.9\x05\x03\x01B\x05\x01p}\x01@\x01\x03lenw\0\0\x04\ +\0\x10get-random-bytes\x01\x01\x01@\0\0w\x04\0\x0eget-random-u64\x01\x02\x03\0\x18\ +wasi:random/random@0.2.9\x05\x04\x01B\x04\x04\0\x05error\x03\x01\x01h\0\x01@\x01\ +\x04self\x01\0s\x04\0\x1d[method]error.to-debug-string\x01\x02\x03\0\x13wasi:io/\ +error@0.2.9\x05\x05\x02\x03\0\x04\x05error\x01B(\x02\x03\x02\x01\x06\x04\0\x05er\ +ror\x03\0\0\x02\x03\x02\x01\x01\x04\0\x08pollable\x03\0\x02\x01i\x01\x01q\x02\x15\ +last-operation-failed\x01\x04\0\x06closed\0\0\x04\0\x0cstream-error\x03\0\x05\x04\ +\0\x0cinput-stream\x03\x01\x04\0\x0doutput-stream\x03\x01\x01h\x07\x01p}\x01j\x01\ +\x0a\x01\x06\x01@\x02\x04self\x09\x03lenw\0\x0b\x04\0\x19[method]input-stream.re\ +ad\x01\x0c\x04\0\"[method]input-stream.blocking-read\x01\x0c\x01j\x01w\x01\x06\x01\ +@\x02\x04self\x09\x03lenw\0\x0d\x04\0\x19[method]input-stream.skip\x01\x0e\x04\0\ +\"[method]input-stream.blocking-skip\x01\x0e\x01i\x03\x01@\x01\x04self\x09\0\x0f\ +\x04\0\x1e[method]input-stream.subscribe\x01\x10\x01h\x08\x01@\x01\x04self\x11\0\ +\x0d\x04\0![method]output-stream.check-write\x01\x12\x01j\0\x01\x06\x01@\x02\x04\ +self\x11\x08contents\x0a\0\x13\x04\0\x1b[method]output-stream.write\x01\x14\x04\0\ +.[method]output-stream.blocking-write-and-flush\x01\x14\x01@\x01\x04self\x11\0\x13\ +\x04\0\x1b[method]output-stream.flush\x01\x15\x04\0$[method]output-stream.blocki\ +ng-flush\x01\x15\x01@\x01\x04self\x11\0\x0f\x04\0\x1f[method]output-stream.subsc\ +ribe\x01\x16\x01@\x02\x04self\x11\x03lenw\0\x13\x04\0\"[method]output-stream.wri\ +te-zeroes\x01\x17\x04\05[method]output-stream.blocking-write-zeroes-and-flush\x01\ +\x17\x01@\x03\x04self\x11\x03src\x09\x03lenw\0\x0d\x04\0\x1c[method]output-strea\ +m.splice\x01\x18\x04\0%[method]output-stream.blocking-splice\x01\x18\x03\0\x15wa\ +si:io/streams@0.2.9\x05\x07\x02\x03\0\x05\x0doutput-stream\x01B\x05\x02\x03\x02\x01\ +\x08\x04\0\x0doutput-stream\x03\0\0\x01i\x01\x01@\0\0\x02\x04\0\x0aget-stdout\x01\ +\x03\x03\0\x15wasi:cli/stdout@0.2.9\x05\x09\x01B\x05\x02\x03\x02\x01\x08\x04\0\x0d\ +output-stream\x03\0\0\x01i\x01\x01@\0\0\x02\x04\0\x0aget-stderr\x01\x03\x03\0\x15\ +wasi:cli/stderr@0.2.9\x05\x0a\x02\x03\0\x05\x0cinput-stream\x01B\x05\x02\x03\x02\ +\x01\x0b\x04\0\x0cinput-stream\x03\0\0\x01i\x01\x01@\0\0\x02\x04\0\x09get-stdin\x01\ +\x03\x03\0\x14wasi:cli/stdin@0.2.9\x05\x0c\x02\x03\0\x01\x08duration\x01B\xc1\x01\ +\x02\x03\x02\x01\x0d\x04\0\x08duration\x03\0\0\x02\x03\x02\x01\x0b\x04\0\x0cinpu\ +t-stream\x03\0\x02\x02\x03\x02\x01\x08\x04\0\x0doutput-stream\x03\0\x04\x02\x03\x02\ +\x01\x06\x04\0\x08io-error\x03\0\x06\x02\x03\x02\x01\x01\x04\0\x08pollable\x03\0\ +\x08\x01q\x0a\x03get\0\0\x04head\0\0\x04post\0\0\x03put\0\0\x06delete\0\0\x07con\ +nect\0\0\x07options\0\0\x05trace\0\0\x05patch\0\0\x05other\x01s\0\x04\0\x06metho\ +d\x03\0\x0a\x01q\x03\x04HTTP\0\0\x05HTTPS\0\0\x05other\x01s\0\x04\0\x06scheme\x03\ +\0\x0c\x01ks\x01k{\x01r\x02\x05rcode\x0e\x09info-code\x0f\x04\0\x11DNS-error-pay\ +load\x03\0\x10\x01k}\x01r\x02\x08alert-id\x12\x0dalert-message\x0e\x04\0\x1aTLS-\ +alert-received-payload\x03\0\x13\x01ky\x01r\x02\x0afield-name\x0e\x0afield-size\x15\ +\x04\0\x12field-size-payload\x03\0\x16\x01kw\x01k\x17\x01q'\x0bDNS-timeout\0\0\x09\ +DNS-error\x01\x11\0\x15destination-not-found\0\0\x17destination-unavailable\0\0\x19\ +destination-IP-prohibited\0\0\x19destination-IP-unroutable\0\0\x12connection-ref\ +used\0\0\x15connection-terminated\0\0\x12connection-timeout\0\0\x17connection-re\ +ad-timeout\0\0\x18connection-write-timeout\0\0\x18connection-limit-reached\0\0\x12\ +TLS-protocol-error\0\0\x15TLS-certificate-error\0\0\x12TLS-alert-received\x01\x14\ +\0\x13HTTP-request-denied\0\0\x1cHTTP-request-length-required\0\0\x16HTTP-reques\ +t-body-size\x01\x18\0\x1bHTTP-request-method-invalid\0\0\x18HTTP-request-URI-inv\ +alid\0\0\x19HTTP-request-URI-too-long\0\0\x20HTTP-request-header-section-size\x01\ +\x15\0\x18HTTP-request-header-size\x01\x19\0!HTTP-request-trailer-section-size\x01\ +\x15\0\x19HTTP-request-trailer-size\x01\x17\0\x18HTTP-response-incomplete\0\0!HT\ +TP-response-header-section-size\x01\x15\0\x19HTTP-response-header-size\x01\x17\0\ +\x17HTTP-response-body-size\x01\x18\0\"HTTP-response-trailer-section-size\x01\x15\ +\0\x1aHTTP-response-trailer-size\x01\x17\0\x1dHTTP-response-transfer-coding\x01\x0e\ +\0\x1cHTTP-response-content-coding\x01\x0e\0\x15HTTP-response-timeout\0\0\x13HTT\ +P-upgrade-failed\0\0\x13HTTP-protocol-error\0\0\x0dloop-detected\0\0\x13configur\ +ation-error\0\0\x0einternal-error\x01\x0e\0\x04\0\x0aerror-code\x03\0\x1a\x01q\x03\ +\x0einvalid-syntax\0\0\x09forbidden\0\0\x09immutable\0\0\x04\0\x0cheader-error\x03\ +\0\x1c\x01s\x04\0\x09field-key\x03\0\x1e\x04\0\x0afield-name\x03\0\x1f\x01p}\x04\ +\0\x0bfield-value\x03\0!\x04\0\x06fields\x03\x01\x04\0\x07headers\x03\0#\x04\0\x08\ +trailers\x03\0#\x04\0\x10incoming-request\x03\x01\x04\0\x10outgoing-request\x03\x01\ +\x04\0\x0frequest-options\x03\x01\x04\0\x11response-outparam\x03\x01\x01{\x04\0\x0b\ +status-code\x03\0*\x04\0\x11incoming-response\x03\x01\x04\0\x0dincoming-body\x03\ +\x01\x04\0\x0ffuture-trailers\x03\x01\x04\0\x11outgoing-response\x03\x01\x04\0\x0d\ +outgoing-body\x03\x01\x04\0\x18future-incoming-response\x03\x01\x01i#\x01@\0\02\x04\ +\0\x13[constructor]fields\x013\x01o\x02\x20\"\x01p4\x01j\x012\x01\x1d\x01@\x01\x07\ +entries5\06\x04\0\x18[static]fields.from-list\x017\x01h#\x01p\"\x01@\x02\x04self\ +8\x04name\x20\09\x04\0\x12[method]fields.get\x01:\x01@\x02\x04self8\x04name\x20\0\ +\x7f\x04\0\x12[method]fields.has\x01;\x01j\0\x01\x1d\x01@\x03\x04self8\x04name\x20\ +\x05value9\0<\x04\0\x12[method]fields.set\x01=\x01@\x02\x04self8\x04name\x20\0<\x04\ +\0\x15[method]fields.delete\x01>\x01@\x03\x04self8\x04name\x20\x05value\"\0<\x04\ +\0\x15[method]fields.append\x01?\x01@\x01\x04self8\05\x04\0\x16[method]fields.en\ +tries\x01@\x01@\x01\x04self8\02\x04\0\x14[method]fields.clone\x01A\x01h&\x01@\x01\ +\x04self\xc2\0\0\x0b\x04\0\x1f[method]incoming-request.method\x01C\x01@\x01\x04s\ +elf\xc2\0\0\x0e\x04\0([method]incoming-request.path-with-query\x01D\x01k\x0d\x01\ +@\x01\x04self\xc2\0\0\xc5\0\x04\0\x1f[method]incoming-request.scheme\x01F\x04\0\"\ +[method]incoming-request.authority\x01D\x01i$\x01@\x01\x04self\xc2\0\0\xc7\0\x04\ +\0\x20[method]incoming-request.headers\x01H\x01i-\x01j\x01\xc9\0\0\x01@\x01\x04s\ +elf\xc2\0\0\xca\0\x04\0\x20[method]incoming-request.consume\x01K\x01i'\x01@\x01\x07\ +headers\xc7\0\0\xcc\0\x04\0\x1d[constructor]outgoing-request\x01M\x01h'\x01i0\x01\ +j\x01\xcf\0\0\x01@\x01\x04self\xce\0\0\xd0\0\x04\0\x1d[method]outgoing-request.b\ +ody\x01Q\x01@\x01\x04self\xce\0\0\x0b\x04\0\x1f[method]outgoing-request.method\x01\ +R\x01j\0\0\x01@\x02\x04self\xce\0\x06method\x0b\0\xd3\0\x04\0#[method]outgoing-r\ +equest.set-method\x01T\x01@\x01\x04self\xce\0\0\x0e\x04\0([method]outgoing-reque\ +st.path-with-query\x01U\x01@\x02\x04self\xce\0\x0fpath-with-query\x0e\0\xd3\0\x04\ +\0,[method]outgoing-request.set-path-with-query\x01V\x01@\x01\x04self\xce\0\0\xc5\ +\0\x04\0\x1f[method]outgoing-request.scheme\x01W\x01@\x02\x04self\xce\0\x06schem\ +e\xc5\0\0\xd3\0\x04\0#[method]outgoing-request.set-scheme\x01X\x04\0\"[method]ou\ +tgoing-request.authority\x01U\x01@\x02\x04self\xce\0\x09authority\x0e\0\xd3\0\x04\ +\0&[method]outgoing-request.set-authority\x01Y\x01@\x01\x04self\xce\0\0\xc7\0\x04\ +\0\x20[method]outgoing-request.headers\x01Z\x01i(\x01@\0\0\xdb\0\x04\0\x1c[const\ +ructor]request-options\x01\\\x01h(\x01k\x01\x01@\x01\x04self\xdd\0\0\xde\0\x04\0\ +'[method]request-options.connect-timeout\x01_\x01@\x02\x04self\xdd\0\x08duration\ +\xde\0\0\xd3\0\x04\0+[method]request-options.set-connect-timeout\x01`\x04\0*[met\ +hod]request-options.first-byte-timeout\x01_\x04\0.[method]request-options.set-fi\ +rst-byte-timeout\x01`\x04\0-[method]request-options.between-bytes-timeout\x01_\x04\ +\01[method]request-options.set-between-bytes-timeout\x01`\x01i)\x01i/\x01j\x01\xe2\ +\0\x01\x1b\x01@\x02\x05param\xe1\0\x08response\xe3\0\x01\0\x04\0\x1d[static]resp\ +onse-outparam.set\x01d\x01h,\x01@\x01\x04self\xe5\0\0+\x04\0\x20[method]incoming\ +-response.status\x01f\x01@\x01\x04self\xe5\0\0\xc7\0\x04\0![method]incoming-resp\ +onse.headers\x01g\x01@\x01\x04self\xe5\0\0\xca\0\x04\0![method]incoming-response\ +.consume\x01h\x01h-\x01i\x03\x01j\x01\xea\0\0\x01@\x01\x04self\xe9\0\0\xeb\0\x04\ +\0\x1c[method]incoming-body.stream\x01l\x01i.\x01@\x01\x04this\xc9\0\0\xed\0\x04\ +\0\x1c[static]incoming-body.finish\x01n\x01h.\x01i\x09\x01@\x01\x04self\xef\0\0\xf0\ +\0\x04\0![method]future-trailers.subscribe\x01q\x01i%\x01k\xf2\0\x01j\x01\xf3\0\x01\ +\x1b\x01j\x01\xf4\0\0\x01k\xf5\0\x01@\x01\x04self\xef\0\0\xf6\0\x04\0\x1b[method\ +]future-trailers.get\x01w\x01@\x01\x07headers\xc7\0\0\xe2\0\x04\0\x1e[constructo\ +r]outgoing-response\x01x\x01h/\x01@\x01\x04self\xf9\0\0+\x04\0%[method]outgoing-\ +response.status-code\x01z\x01@\x02\x04self\xf9\0\x0bstatus-code+\0\xd3\0\x04\0)[\ +method]outgoing-response.set-status-code\x01{\x01@\x01\x04self\xf9\0\0\xc7\0\x04\ +\0![method]outgoing-response.headers\x01|\x01@\x01\x04self\xf9\0\0\xd0\0\x04\0\x1e\ +[method]outgoing-response.body\x01}\x01h0\x01i\x05\x01j\x01\xff\0\0\x01@\x01\x04\ +self\xfe\0\0\x80\x01\x04\0\x1b[method]outgoing-body.write\x01\x81\x01\x01j\0\x01\ +\x1b\x01@\x02\x04this\xcf\0\x08trailers\xf3\0\0\x82\x01\x04\0\x1c[static]outgoin\ +g-body.finish\x01\x83\x01\x01h1\x01@\x01\x04self\x84\x01\0\xf0\0\x04\0*[method]f\ +uture-incoming-response.subscribe\x01\x85\x01\x01i,\x01j\x01\x86\x01\x01\x1b\x01\ +j\x01\x87\x01\0\x01k\x88\x01\x01@\x01\x04self\x84\x01\0\x89\x01\x04\0$[method]fu\ +ture-incoming-response.get\x01\x8a\x01\x01h\x07\x01k\x1b\x01@\x01\x03err\x8b\x01\ +\0\x8c\x01\x04\0\x0fhttp-error-code\x01\x8d\x01\x03\0\x15wasi:http/types@0.2.9\x05\ +\x0e\x02\x03\0\x09\x10outgoing-request\x02\x03\0\x09\x0frequest-options\x02\x03\0\ +\x09\x18future-incoming-response\x02\x03\0\x09\x0aerror-code\x01B\x0f\x02\x03\x02\ +\x01\x0f\x04\0\x10outgoing-request\x03\0\0\x02\x03\x02\x01\x10\x04\0\x0frequest-\ +options\x03\0\x02\x02\x03\x02\x01\x11\x04\0\x18future-incoming-response\x03\0\x04\ +\x02\x03\x02\x01\x12\x04\0\x0aerror-code\x03\0\x06\x01i\x01\x01i\x03\x01k\x09\x01\ +i\x05\x01j\x01\x0b\x01\x07\x01@\x02\x07request\x08\x07options\x0a\0\x0c\x04\0\x06\ +handle\x01\x0d\x03\0\x20wasi:http/outgoing-handler@0.2.9\x05\x13\x02\x03\0\x09\x10\ +incoming-request\x02\x03\0\x09\x11response-outparam\x01B\x08\x02\x03\x02\x01\x14\ +\x04\0\x10incoming-request\x03\0\0\x02\x03\x02\x01\x15\x04\0\x11response-outpara\ +m\x03\0\x02\x01i\x01\x01i\x03\x01@\x02\x07request\x04\x0cresponse-out\x05\x01\0\x04\ +\0\x06handle\x01\x06\x04\0\x20wasi:http/incoming-handler@0.2.9\x05\x16\x04\0\x15\ +wasi:http/proxy@0.2.9\x04\0\x0b\x0b\x01\0\x05proxy\x03\0\0\0G\x09producers\x01\x0c\ +processed-by\x02\x0dwit-component\x070.244.0\x10wit-bindgen-rust\x060.51.0"; + }; + }; +} +#[doc(inline)] +pub use __export_proxy_impl as _export_proxy; +#[rustfmt::skip] +#[cfg(target_arch = "wasm32")] + +#[cfg_attr(feature = "rustc-dep-of-std", unsafe(link_section = "component-type:wit-bindgen:0.51.0:wasi:http@0.2.9:proxy-with-all-of-its-exports-removed:encoded worldrust-wasip2-1.0.2+wasi-0.2.9-from-crates-io-proxy-world-in-libstd"))] +#[cfg_attr(not(feature = "rustc-dep-of-std"), unsafe(link_section = "component-type:wit-bindgen:0.51.0:wasi:http@0.2.9:proxy-with-all-of-its-exports-removed:encoded worldrust-wasip2-1.0.2+wasi-0.2.9-from-crates-io-proxy-world"))] + +#[doc(hidden)] +#[allow(clippy::octal_escapes)] +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 6921] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xed4\x01A\x02\x01A\x1f\ +\x01B\x0a\x04\0\x08pollable\x03\x01\x01h\0\x01@\x01\x04self\x01\0\x7f\x04\0\x16[\ +method]pollable.ready\x01\x02\x01@\x01\x04self\x01\x01\0\x04\0\x16[method]pollab\ +le.block\x01\x03\x01p\x01\x01py\x01@\x01\x02in\x04\0\x05\x04\0\x04poll\x01\x06\x03\ +\0\x12wasi:io/poll@0.2.9\x05\0\x02\x03\0\0\x08pollable\x01B\x0f\x02\x03\x02\x01\x01\ +\x04\0\x08pollable\x03\0\0\x01w\x04\0\x07instant\x03\0\x02\x01w\x04\0\x08duratio\ +n\x03\0\x04\x01@\0\0\x03\x04\0\x03now\x01\x06\x01@\0\0\x05\x04\0\x0aresolution\x01\ +\x07\x01i\x01\x01@\x01\x04when\x03\0\x08\x04\0\x11subscribe-instant\x01\x09\x01@\ +\x01\x04when\x05\0\x08\x04\0\x12subscribe-duration\x01\x0a\x03\0!wasi:clocks/mon\ +otonic-clock@0.2.9\x05\x02\x01B\x05\x01r\x02\x07secondsw\x0bnanosecondsy\x04\0\x08\ +datetime\x03\0\0\x01@\0\0\x01\x04\0\x03now\x01\x02\x04\0\x0aresolution\x01\x02\x03\ +\0\x1cwasi:clocks/wall-clock@0.2.9\x05\x03\x01B\x05\x01p}\x01@\x01\x03lenw\0\0\x04\ +\0\x10get-random-bytes\x01\x01\x01@\0\0w\x04\0\x0eget-random-u64\x01\x02\x03\0\x18\ +wasi:random/random@0.2.9\x05\x04\x01B\x04\x04\0\x05error\x03\x01\x01h\0\x01@\x01\ +\x04self\x01\0s\x04\0\x1d[method]error.to-debug-string\x01\x02\x03\0\x13wasi:io/\ +error@0.2.9\x05\x05\x02\x03\0\x04\x05error\x01B(\x02\x03\x02\x01\x06\x04\0\x05er\ +ror\x03\0\0\x02\x03\x02\x01\x01\x04\0\x08pollable\x03\0\x02\x01i\x01\x01q\x02\x15\ +last-operation-failed\x01\x04\0\x06closed\0\0\x04\0\x0cstream-error\x03\0\x05\x04\ +\0\x0cinput-stream\x03\x01\x04\0\x0doutput-stream\x03\x01\x01h\x07\x01p}\x01j\x01\ +\x0a\x01\x06\x01@\x02\x04self\x09\x03lenw\0\x0b\x04\0\x19[method]input-stream.re\ +ad\x01\x0c\x04\0\"[method]input-stream.blocking-read\x01\x0c\x01j\x01w\x01\x06\x01\ +@\x02\x04self\x09\x03lenw\0\x0d\x04\0\x19[method]input-stream.skip\x01\x0e\x04\0\ +\"[method]input-stream.blocking-skip\x01\x0e\x01i\x03\x01@\x01\x04self\x09\0\x0f\ +\x04\0\x1e[method]input-stream.subscribe\x01\x10\x01h\x08\x01@\x01\x04self\x11\0\ +\x0d\x04\0![method]output-stream.check-write\x01\x12\x01j\0\x01\x06\x01@\x02\x04\ +self\x11\x08contents\x0a\0\x13\x04\0\x1b[method]output-stream.write\x01\x14\x04\0\ +.[method]output-stream.blocking-write-and-flush\x01\x14\x01@\x01\x04self\x11\0\x13\ +\x04\0\x1b[method]output-stream.flush\x01\x15\x04\0$[method]output-stream.blocki\ +ng-flush\x01\x15\x01@\x01\x04self\x11\0\x0f\x04\0\x1f[method]output-stream.subsc\ +ribe\x01\x16\x01@\x02\x04self\x11\x03lenw\0\x13\x04\0\"[method]output-stream.wri\ +te-zeroes\x01\x17\x04\05[method]output-stream.blocking-write-zeroes-and-flush\x01\ +\x17\x01@\x03\x04self\x11\x03src\x09\x03lenw\0\x0d\x04\0\x1c[method]output-strea\ +m.splice\x01\x18\x04\0%[method]output-stream.blocking-splice\x01\x18\x03\0\x15wa\ +si:io/streams@0.2.9\x05\x07\x02\x03\0\x05\x0doutput-stream\x01B\x05\x02\x03\x02\x01\ +\x08\x04\0\x0doutput-stream\x03\0\0\x01i\x01\x01@\0\0\x02\x04\0\x0aget-stdout\x01\ +\x03\x03\0\x15wasi:cli/stdout@0.2.9\x05\x09\x01B\x05\x02\x03\x02\x01\x08\x04\0\x0d\ +output-stream\x03\0\0\x01i\x01\x01@\0\0\x02\x04\0\x0aget-stderr\x01\x03\x03\0\x15\ +wasi:cli/stderr@0.2.9\x05\x0a\x02\x03\0\x05\x0cinput-stream\x01B\x05\x02\x03\x02\ +\x01\x0b\x04\0\x0cinput-stream\x03\0\0\x01i\x01\x01@\0\0\x02\x04\0\x09get-stdin\x01\ +\x03\x03\0\x14wasi:cli/stdin@0.2.9\x05\x0c\x02\x03\0\x01\x08duration\x01B\xc1\x01\ +\x02\x03\x02\x01\x0d\x04\0\x08duration\x03\0\0\x02\x03\x02\x01\x0b\x04\0\x0cinpu\ +t-stream\x03\0\x02\x02\x03\x02\x01\x08\x04\0\x0doutput-stream\x03\0\x04\x02\x03\x02\ +\x01\x06\x04\0\x08io-error\x03\0\x06\x02\x03\x02\x01\x01\x04\0\x08pollable\x03\0\ +\x08\x01q\x0a\x03get\0\0\x04head\0\0\x04post\0\0\x03put\0\0\x06delete\0\0\x07con\ +nect\0\0\x07options\0\0\x05trace\0\0\x05patch\0\0\x05other\x01s\0\x04\0\x06metho\ +d\x03\0\x0a\x01q\x03\x04HTTP\0\0\x05HTTPS\0\0\x05other\x01s\0\x04\0\x06scheme\x03\ +\0\x0c\x01ks\x01k{\x01r\x02\x05rcode\x0e\x09info-code\x0f\x04\0\x11DNS-error-pay\ +load\x03\0\x10\x01k}\x01r\x02\x08alert-id\x12\x0dalert-message\x0e\x04\0\x1aTLS-\ +alert-received-payload\x03\0\x13\x01ky\x01r\x02\x0afield-name\x0e\x0afield-size\x15\ +\x04\0\x12field-size-payload\x03\0\x16\x01kw\x01k\x17\x01q'\x0bDNS-timeout\0\0\x09\ +DNS-error\x01\x11\0\x15destination-not-found\0\0\x17destination-unavailable\0\0\x19\ +destination-IP-prohibited\0\0\x19destination-IP-unroutable\0\0\x12connection-ref\ +used\0\0\x15connection-terminated\0\0\x12connection-timeout\0\0\x17connection-re\ +ad-timeout\0\0\x18connection-write-timeout\0\0\x18connection-limit-reached\0\0\x12\ +TLS-protocol-error\0\0\x15TLS-certificate-error\0\0\x12TLS-alert-received\x01\x14\ +\0\x13HTTP-request-denied\0\0\x1cHTTP-request-length-required\0\0\x16HTTP-reques\ +t-body-size\x01\x18\0\x1bHTTP-request-method-invalid\0\0\x18HTTP-request-URI-inv\ +alid\0\0\x19HTTP-request-URI-too-long\0\0\x20HTTP-request-header-section-size\x01\ +\x15\0\x18HTTP-request-header-size\x01\x19\0!HTTP-request-trailer-section-size\x01\ +\x15\0\x19HTTP-request-trailer-size\x01\x17\0\x18HTTP-response-incomplete\0\0!HT\ +TP-response-header-section-size\x01\x15\0\x19HTTP-response-header-size\x01\x17\0\ +\x17HTTP-response-body-size\x01\x18\0\"HTTP-response-trailer-section-size\x01\x15\ +\0\x1aHTTP-response-trailer-size\x01\x17\0\x1dHTTP-response-transfer-coding\x01\x0e\ +\0\x1cHTTP-response-content-coding\x01\x0e\0\x15HTTP-response-timeout\0\0\x13HTT\ +P-upgrade-failed\0\0\x13HTTP-protocol-error\0\0\x0dloop-detected\0\0\x13configur\ +ation-error\0\0\x0einternal-error\x01\x0e\0\x04\0\x0aerror-code\x03\0\x1a\x01q\x03\ +\x0einvalid-syntax\0\0\x09forbidden\0\0\x09immutable\0\0\x04\0\x0cheader-error\x03\ +\0\x1c\x01s\x04\0\x09field-key\x03\0\x1e\x04\0\x0afield-name\x03\0\x1f\x01p}\x04\ +\0\x0bfield-value\x03\0!\x04\0\x06fields\x03\x01\x04\0\x07headers\x03\0#\x04\0\x08\ +trailers\x03\0#\x04\0\x10incoming-request\x03\x01\x04\0\x10outgoing-request\x03\x01\ +\x04\0\x0frequest-options\x03\x01\x04\0\x11response-outparam\x03\x01\x01{\x04\0\x0b\ +status-code\x03\0*\x04\0\x11incoming-response\x03\x01\x04\0\x0dincoming-body\x03\ +\x01\x04\0\x0ffuture-trailers\x03\x01\x04\0\x11outgoing-response\x03\x01\x04\0\x0d\ +outgoing-body\x03\x01\x04\0\x18future-incoming-response\x03\x01\x01i#\x01@\0\02\x04\ +\0\x13[constructor]fields\x013\x01o\x02\x20\"\x01p4\x01j\x012\x01\x1d\x01@\x01\x07\ +entries5\06\x04\0\x18[static]fields.from-list\x017\x01h#\x01p\"\x01@\x02\x04self\ +8\x04name\x20\09\x04\0\x12[method]fields.get\x01:\x01@\x02\x04self8\x04name\x20\0\ +\x7f\x04\0\x12[method]fields.has\x01;\x01j\0\x01\x1d\x01@\x03\x04self8\x04name\x20\ +\x05value9\0<\x04\0\x12[method]fields.set\x01=\x01@\x02\x04self8\x04name\x20\0<\x04\ +\0\x15[method]fields.delete\x01>\x01@\x03\x04self8\x04name\x20\x05value\"\0<\x04\ +\0\x15[method]fields.append\x01?\x01@\x01\x04self8\05\x04\0\x16[method]fields.en\ +tries\x01@\x01@\x01\x04self8\02\x04\0\x14[method]fields.clone\x01A\x01h&\x01@\x01\ +\x04self\xc2\0\0\x0b\x04\0\x1f[method]incoming-request.method\x01C\x01@\x01\x04s\ +elf\xc2\0\0\x0e\x04\0([method]incoming-request.path-with-query\x01D\x01k\x0d\x01\ +@\x01\x04self\xc2\0\0\xc5\0\x04\0\x1f[method]incoming-request.scheme\x01F\x04\0\"\ +[method]incoming-request.authority\x01D\x01i$\x01@\x01\x04self\xc2\0\0\xc7\0\x04\ +\0\x20[method]incoming-request.headers\x01H\x01i-\x01j\x01\xc9\0\0\x01@\x01\x04s\ +elf\xc2\0\0\xca\0\x04\0\x20[method]incoming-request.consume\x01K\x01i'\x01@\x01\x07\ +headers\xc7\0\0\xcc\0\x04\0\x1d[constructor]outgoing-request\x01M\x01h'\x01i0\x01\ +j\x01\xcf\0\0\x01@\x01\x04self\xce\0\0\xd0\0\x04\0\x1d[method]outgoing-request.b\ +ody\x01Q\x01@\x01\x04self\xce\0\0\x0b\x04\0\x1f[method]outgoing-request.method\x01\ +R\x01j\0\0\x01@\x02\x04self\xce\0\x06method\x0b\0\xd3\0\x04\0#[method]outgoing-r\ +equest.set-method\x01T\x01@\x01\x04self\xce\0\0\x0e\x04\0([method]outgoing-reque\ +st.path-with-query\x01U\x01@\x02\x04self\xce\0\x0fpath-with-query\x0e\0\xd3\0\x04\ +\0,[method]outgoing-request.set-path-with-query\x01V\x01@\x01\x04self\xce\0\0\xc5\ +\0\x04\0\x1f[method]outgoing-request.scheme\x01W\x01@\x02\x04self\xce\0\x06schem\ +e\xc5\0\0\xd3\0\x04\0#[method]outgoing-request.set-scheme\x01X\x04\0\"[method]ou\ +tgoing-request.authority\x01U\x01@\x02\x04self\xce\0\x09authority\x0e\0\xd3\0\x04\ +\0&[method]outgoing-request.set-authority\x01Y\x01@\x01\x04self\xce\0\0\xc7\0\x04\ +\0\x20[method]outgoing-request.headers\x01Z\x01i(\x01@\0\0\xdb\0\x04\0\x1c[const\ +ructor]request-options\x01\\\x01h(\x01k\x01\x01@\x01\x04self\xdd\0\0\xde\0\x04\0\ +'[method]request-options.connect-timeout\x01_\x01@\x02\x04self\xdd\0\x08duration\ +\xde\0\0\xd3\0\x04\0+[method]request-options.set-connect-timeout\x01`\x04\0*[met\ +hod]request-options.first-byte-timeout\x01_\x04\0.[method]request-options.set-fi\ +rst-byte-timeout\x01`\x04\0-[method]request-options.between-bytes-timeout\x01_\x04\ +\01[method]request-options.set-between-bytes-timeout\x01`\x01i)\x01i/\x01j\x01\xe2\ +\0\x01\x1b\x01@\x02\x05param\xe1\0\x08response\xe3\0\x01\0\x04\0\x1d[static]resp\ +onse-outparam.set\x01d\x01h,\x01@\x01\x04self\xe5\0\0+\x04\0\x20[method]incoming\ +-response.status\x01f\x01@\x01\x04self\xe5\0\0\xc7\0\x04\0![method]incoming-resp\ +onse.headers\x01g\x01@\x01\x04self\xe5\0\0\xca\0\x04\0![method]incoming-response\ +.consume\x01h\x01h-\x01i\x03\x01j\x01\xea\0\0\x01@\x01\x04self\xe9\0\0\xeb\0\x04\ +\0\x1c[method]incoming-body.stream\x01l\x01i.\x01@\x01\x04this\xc9\0\0\xed\0\x04\ +\0\x1c[static]incoming-body.finish\x01n\x01h.\x01i\x09\x01@\x01\x04self\xef\0\0\xf0\ +\0\x04\0![method]future-trailers.subscribe\x01q\x01i%\x01k\xf2\0\x01j\x01\xf3\0\x01\ +\x1b\x01j\x01\xf4\0\0\x01k\xf5\0\x01@\x01\x04self\xef\0\0\xf6\0\x04\0\x1b[method\ +]future-trailers.get\x01w\x01@\x01\x07headers\xc7\0\0\xe2\0\x04\0\x1e[constructo\ +r]outgoing-response\x01x\x01h/\x01@\x01\x04self\xf9\0\0+\x04\0%[method]outgoing-\ +response.status-code\x01z\x01@\x02\x04self\xf9\0\x0bstatus-code+\0\xd3\0\x04\0)[\ +method]outgoing-response.set-status-code\x01{\x01@\x01\x04self\xf9\0\0\xc7\0\x04\ +\0![method]outgoing-response.headers\x01|\x01@\x01\x04self\xf9\0\0\xd0\0\x04\0\x1e\ +[method]outgoing-response.body\x01}\x01h0\x01i\x05\x01j\x01\xff\0\0\x01@\x01\x04\ +self\xfe\0\0\x80\x01\x04\0\x1b[method]outgoing-body.write\x01\x81\x01\x01j\0\x01\ +\x1b\x01@\x02\x04this\xcf\0\x08trailers\xf3\0\0\x82\x01\x04\0\x1c[static]outgoin\ +g-body.finish\x01\x83\x01\x01h1\x01@\x01\x04self\x84\x01\0\xf0\0\x04\0*[method]f\ +uture-incoming-response.subscribe\x01\x85\x01\x01i,\x01j\x01\x86\x01\x01\x1b\x01\ +j\x01\x87\x01\0\x01k\x88\x01\x01@\x01\x04self\x84\x01\0\x89\x01\x04\0$[method]fu\ +ture-incoming-response.get\x01\x8a\x01\x01h\x07\x01k\x1b\x01@\x01\x03err\x8b\x01\ +\0\x8c\x01\x04\0\x0fhttp-error-code\x01\x8d\x01\x03\0\x15wasi:http/types@0.2.9\x05\ +\x0e\x02\x03\0\x09\x10outgoing-request\x02\x03\0\x09\x0frequest-options\x02\x03\0\ +\x09\x18future-incoming-response\x02\x03\0\x09\x0aerror-code\x01B\x0f\x02\x03\x02\ +\x01\x0f\x04\0\x10outgoing-request\x03\0\0\x02\x03\x02\x01\x10\x04\0\x0frequest-\ +options\x03\0\x02\x02\x03\x02\x01\x11\x04\0\x18future-incoming-response\x03\0\x04\ +\x02\x03\x02\x01\x12\x04\0\x0aerror-code\x03\0\x06\x01i\x01\x01i\x03\x01k\x09\x01\ +i\x05\x01j\x01\x0b\x01\x07\x01@\x02\x07request\x08\x07options\x0a\0\x0c\x04\0\x06\ +handle\x01\x0d\x03\0\x20wasi:http/outgoing-handler@0.2.9\x05\x13\x04\05wasi:http\ +/proxy-with-all-of-its-exports-removed@0.2.9\x04\0\x0b+\x01\0%proxy-with-all-of-\ +its-exports-removed\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-compo\ +nent\x070.244.0\x10wit-bindgen-rust\x060.51.0"; +#[inline(never)] +#[doc(hidden)] +pub fn __link_custom_section_describing_imports() { + wit_bindgen::rt::maybe_link_cabi_realloc(); +} diff --git a/tools/vendor/wasip2/wit/deps/cli.wit b/tools/vendor/wasip2/wit/deps/cli.wit new file mode 100644 index 0000000000..edd1f9ae1f --- /dev/null +++ b/tools/vendor/wasip2/wit/deps/cli.wit @@ -0,0 +1,233 @@ +package wasi:cli@0.2.9; + +@since(version = 0.2.0) +interface environment { + /// Get the POSIX-style environment variables. + /// + /// Each environment variable is provided as a pair of string variable names + /// and string value. + /// + /// Morally, these are a value import, but until value imports are available + /// in the component model, this import function should return the same + /// values each time it is called. + @since(version = 0.2.0) + get-environment: func() -> list>; + + /// Get the POSIX-style arguments to the program. + @since(version = 0.2.0) + get-arguments: func() -> list; + + /// Return a path that programs should use as their initial current working + /// directory, interpreting `.` as shorthand for this. + @since(version = 0.2.0) + initial-cwd: func() -> option; +} + +@since(version = 0.2.0) +interface exit { + /// Exit the current instance and any linked instances. + @since(version = 0.2.0) + exit: func(status: result); + + /// Exit the current instance and any linked instances, reporting the + /// specified status code to the host. + /// + /// The meaning of the code depends on the context, with 0 usually meaning + /// "success", and other values indicating various types of failure. + /// + /// This function does not return; the effect is analogous to a trap, but + /// without the connotation that something bad has happened. + @unstable(feature = cli-exit-with-code) + exit-with-code: func(status-code: u8); +} + +@since(version = 0.2.0) +interface run { + /// Run the program. + @since(version = 0.2.0) + run: func() -> result; +} + +@since(version = 0.2.0) +interface stdin { + @since(version = 0.2.0) + use wasi:io/streams@0.2.9.{input-stream}; + + @since(version = 0.2.0) + get-stdin: func() -> input-stream; +} + +@since(version = 0.2.0) +interface stdout { + @since(version = 0.2.0) + use wasi:io/streams@0.2.9.{output-stream}; + + @since(version = 0.2.0) + get-stdout: func() -> output-stream; +} + +@since(version = 0.2.0) +interface stderr { + @since(version = 0.2.0) + use wasi:io/streams@0.2.9.{output-stream}; + + @since(version = 0.2.0) + get-stderr: func() -> output-stream; +} + +/// Terminal input. +/// +/// In the future, this may include functions for disabling echoing, +/// disabling input buffering so that keyboard events are sent through +/// immediately, querying supported features, and so on. +@since(version = 0.2.0) +interface terminal-input { + /// The input side of a terminal. + @since(version = 0.2.0) + resource terminal-input; +} + +/// Terminal output. +/// +/// In the future, this may include functions for querying the terminal +/// size, being notified of terminal size changes, querying supported +/// features, and so on. +@since(version = 0.2.0) +interface terminal-output { + /// The output side of a terminal. + @since(version = 0.2.0) + resource terminal-output; +} + +/// An interface providing an optional `terminal-input` for stdin as a +/// link-time authority. +@since(version = 0.2.0) +interface terminal-stdin { + @since(version = 0.2.0) + use terminal-input.{terminal-input}; + + /// If stdin is connected to a terminal, return a `terminal-input` handle + /// allowing further interaction with it. + @since(version = 0.2.0) + get-terminal-stdin: func() -> option; +} + +/// An interface providing an optional `terminal-output` for stdout as a +/// link-time authority. +@since(version = 0.2.0) +interface terminal-stdout { + @since(version = 0.2.0) + use terminal-output.{terminal-output}; + + /// If stdout is connected to a terminal, return a `terminal-output` handle + /// allowing further interaction with it. + @since(version = 0.2.0) + get-terminal-stdout: func() -> option; +} + +/// An interface providing an optional `terminal-output` for stderr as a +/// link-time authority. +@since(version = 0.2.0) +interface terminal-stderr { + @since(version = 0.2.0) + use terminal-output.{terminal-output}; + + /// If stderr is connected to a terminal, return a `terminal-output` handle + /// allowing further interaction with it. + @since(version = 0.2.0) + get-terminal-stderr: func() -> option; +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import environment; + @since(version = 0.2.0) + import exit; + @since(version = 0.2.0) + import wasi:io/error@0.2.9; + @since(version = 0.2.0) + import wasi:io/poll@0.2.9; + @since(version = 0.2.0) + import wasi:io/streams@0.2.9; + @since(version = 0.2.0) + import stdin; + @since(version = 0.2.0) + import stdout; + @since(version = 0.2.0) + import stderr; + @since(version = 0.2.0) + import terminal-input; + @since(version = 0.2.0) + import terminal-output; + @since(version = 0.2.0) + import terminal-stdin; + @since(version = 0.2.0) + import terminal-stdout; + @since(version = 0.2.0) + import terminal-stderr; + import wasi:clocks/monotonic-clock@0.2.9; + import wasi:clocks/wall-clock@0.2.9; + @unstable(feature = clocks-timezone) + import wasi:clocks/timezone@0.2.9; + import wasi:filesystem/types@0.2.9; + import wasi:filesystem/preopens@0.2.9; + import wasi:sockets/network@0.2.9; + import wasi:sockets/instance-network@0.2.9; + import wasi:sockets/udp@0.2.9; + import wasi:sockets/udp-create-socket@0.2.9; + import wasi:sockets/tcp@0.2.9; + import wasi:sockets/tcp-create-socket@0.2.9; + import wasi:sockets/ip-name-lookup@0.2.9; + import wasi:random/random@0.2.9; + import wasi:random/insecure@0.2.9; + import wasi:random/insecure-seed@0.2.9; +} +@since(version = 0.2.0) +world command { + @since(version = 0.2.0) + import environment; + @since(version = 0.2.0) + import exit; + @since(version = 0.2.0) + import wasi:io/error@0.2.9; + @since(version = 0.2.0) + import wasi:io/poll@0.2.9; + @since(version = 0.2.0) + import wasi:io/streams@0.2.9; + @since(version = 0.2.0) + import stdin; + @since(version = 0.2.0) + import stdout; + @since(version = 0.2.0) + import stderr; + @since(version = 0.2.0) + import terminal-input; + @since(version = 0.2.0) + import terminal-output; + @since(version = 0.2.0) + import terminal-stdin; + @since(version = 0.2.0) + import terminal-stdout; + @since(version = 0.2.0) + import terminal-stderr; + import wasi:clocks/monotonic-clock@0.2.9; + import wasi:clocks/wall-clock@0.2.9; + @unstable(feature = clocks-timezone) + import wasi:clocks/timezone@0.2.9; + import wasi:filesystem/types@0.2.9; + import wasi:filesystem/preopens@0.2.9; + import wasi:sockets/network@0.2.9; + import wasi:sockets/instance-network@0.2.9; + import wasi:sockets/udp@0.2.9; + import wasi:sockets/udp-create-socket@0.2.9; + import wasi:sockets/tcp@0.2.9; + import wasi:sockets/tcp-create-socket@0.2.9; + import wasi:sockets/ip-name-lookup@0.2.9; + import wasi:random/random@0.2.9; + import wasi:random/insecure@0.2.9; + import wasi:random/insecure-seed@0.2.9; + + @since(version = 0.2.0) + export run; +} diff --git a/tools/vendor/wasip2/wit/deps/clocks.wit b/tools/vendor/wasip2/wit/deps/clocks.wit new file mode 100644 index 0000000000..9fc2ee9b6d --- /dev/null +++ b/tools/vendor/wasip2/wit/deps/clocks.wit @@ -0,0 +1,162 @@ +package wasi:clocks@0.2.9; + +/// WASI Monotonic Clock is a clock API intended to let users measure elapsed +/// time. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +/// +/// A monotonic clock is a clock which has an unspecified initial value, and +/// successive reads of the clock will produce non-decreasing values. +@since(version = 0.2.0) +interface monotonic-clock { + @since(version = 0.2.0) + use wasi:io/poll@0.2.9.{pollable}; + + /// An instant in time, in nanoseconds. An instant is relative to an + /// unspecified initial value, and can only be compared to instances from + /// the same monotonic-clock. + @since(version = 0.2.0) + type instant = u64; + + /// A duration of time, in nanoseconds. + @since(version = 0.2.0) + type duration = u64; + + /// Read the current value of the clock. + /// + /// The clock is monotonic, therefore calling this function repeatedly will + /// produce a sequence of non-decreasing values. + /// + /// For completeness, this function traps if it's not possible to represent + /// the value of the clock in an `instant`. Consequently, implementations + /// should ensure that the starting time is low enough to avoid the + /// possibility of overflow in practice. + @since(version = 0.2.0) + now: func() -> instant; + + /// Query the resolution of the clock. Returns the duration of time + /// corresponding to a clock tick. + @since(version = 0.2.0) + resolution: func() -> duration; + + /// Create a `pollable` which will resolve once the specified instant + /// has occurred. + @since(version = 0.2.0) + subscribe-instant: func(when: instant) -> pollable; + + /// Create a `pollable` that will resolve after the specified duration has + /// elapsed from the time this function is invoked. + @since(version = 0.2.0) + subscribe-duration: func(when: duration) -> pollable; +} + +/// WASI Wall Clock is a clock API intended to let users query the current +/// time. The name "wall" makes an analogy to a "clock on the wall", which +/// is not necessarily monotonic as it may be reset. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +/// +/// A wall clock is a clock which measures the date and time according to +/// some external reference. +/// +/// External references may be reset, so this clock is not necessarily +/// monotonic, making it unsuitable for measuring elapsed time. +/// +/// It is intended for reporting the current date and time for humans. +@since(version = 0.2.0) +interface wall-clock { + /// A time and date in seconds plus nanoseconds. + @since(version = 0.2.0) + record datetime { + seconds: u64, + nanoseconds: u32, + } + + /// Read the current value of the clock. + /// + /// This clock is not monotonic, therefore calling this function repeatedly + /// will not necessarily produce a sequence of non-decreasing values. + /// + /// The returned timestamps represent the number of seconds since + /// 1970-01-01T00:00:00Z, also known as [POSIX's Seconds Since the Epoch], + /// also known as [Unix Time]. + /// + /// The nanoseconds field of the output is always less than 1000000000. + /// + /// [POSIX's Seconds Since the Epoch]: https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_16 + /// [Unix Time]: https://en.wikipedia.org/wiki/Unix_time + @since(version = 0.2.0) + now: func() -> datetime; + + /// Query the resolution of the clock. + /// + /// The nanoseconds field of the output is always less than 1000000000. + @since(version = 0.2.0) + resolution: func() -> datetime; +} + +@unstable(feature = clocks-timezone) +interface timezone { + @unstable(feature = clocks-timezone) + use wall-clock.{datetime}; + + /// Information useful for displaying the timezone of a specific `datetime`. + /// + /// This information may vary within a single `timezone` to reflect daylight + /// saving time adjustments. + @unstable(feature = clocks-timezone) + record timezone-display { + /// The number of seconds difference between UTC time and the local + /// time of the timezone. + /// + /// The returned value will always be less than 86400 which is the + /// number of seconds in a day (24*60*60). + /// + /// In implementations that do not expose an actual time zone, this + /// should return 0. + utc-offset: s32, + /// The abbreviated name of the timezone to display to a user. The name + /// `UTC` indicates Coordinated Universal Time. Otherwise, this should + /// reference local standards for the name of the time zone. + /// + /// In implementations that do not expose an actual time zone, this + /// should be the string `UTC`. + /// + /// In time zones that do not have an applicable name, a formatted + /// representation of the UTC offset may be returned, such as `-04:00`. + name: string, + /// Whether daylight saving time is active. + /// + /// In implementations that do not expose an actual time zone, this + /// should return false. + in-daylight-saving-time: bool, + } + + /// Return information needed to display the given `datetime`. This includes + /// the UTC offset, the time zone name, and a flag indicating whether + /// daylight saving time is active. + /// + /// If the timezone cannot be determined for the given `datetime`, return a + /// `timezone-display` for `UTC` with a `utc-offset` of 0 and no daylight + /// saving time. + @unstable(feature = clocks-timezone) + display: func(when: datetime) -> timezone-display; + + /// The same as `display`, but only return the UTC offset. + @unstable(feature = clocks-timezone) + utc-offset: func(when: datetime) -> s32; +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import wasi:io/poll@0.2.9; + @since(version = 0.2.0) + import monotonic-clock; + @since(version = 0.2.0) + import wall-clock; + @unstable(feature = clocks-timezone) + import timezone; +} diff --git a/tools/vendor/wasip2/wit/deps/filesystem.wit b/tools/vendor/wasip2/wit/deps/filesystem.wit new file mode 100644 index 0000000000..3d488a2096 --- /dev/null +++ b/tools/vendor/wasip2/wit/deps/filesystem.wit @@ -0,0 +1,587 @@ +package wasi:filesystem@0.2.9; + +/// WASI filesystem is a filesystem API primarily intended to let users run WASI +/// programs that access their files on their existing filesystems, without +/// significant overhead. +/// +/// It is intended to be roughly portable between Unix-family platforms and +/// Windows, though it does not hide many of the major differences. +/// +/// Paths are passed as interface-type `string`s, meaning they must consist of +/// a sequence of Unicode Scalar Values (USVs). Some filesystems may contain +/// paths which are not accessible by this API. +/// +/// The directory separator in WASI is always the forward-slash (`/`). +/// +/// All paths in WASI are relative paths, and are interpreted relative to a +/// `descriptor` referring to a base directory. If a `path` argument to any WASI +/// function starts with `/`, or if any step of resolving a `path`, including +/// `..` and symbolic link steps, reaches a directory outside of the base +/// directory, or reaches a symlink to an absolute or rooted path in the +/// underlying filesystem, the function fails with `error-code::not-permitted`. +/// +/// For more information about WASI path resolution and sandboxing, see +/// [WASI filesystem path resolution]. +/// +/// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md +@since(version = 0.2.0) +interface types { + @since(version = 0.2.0) + use wasi:io/streams@0.2.9.{input-stream, output-stream, error}; + @since(version = 0.2.0) + use wasi:clocks/wall-clock@0.2.9.{datetime}; + + /// File size or length of a region within a file. + @since(version = 0.2.0) + type filesize = u64; + + /// The type of a filesystem object referenced by a descriptor. + /// + /// Note: This was called `filetype` in earlier versions of WASI. + @since(version = 0.2.0) + enum descriptor-type { + /// The type of the descriptor or file is unknown or is different from + /// any of the other types specified. + unknown, + /// The descriptor refers to a block device inode. + block-device, + /// The descriptor refers to a character device inode. + character-device, + /// The descriptor refers to a directory inode. + directory, + /// The descriptor refers to a named pipe. + fifo, + /// The file refers to a symbolic link inode. + symbolic-link, + /// The descriptor refers to a regular file inode. + regular-file, + /// The descriptor refers to a socket. + socket, + } + + /// Descriptor flags. + /// + /// Note: This was called `fdflags` in earlier versions of WASI. + @since(version = 0.2.0) + flags descriptor-flags { + /// Read mode: Data can be read. + read, + /// Write mode: Data can be written to. + write, + /// Request that writes be performed according to synchronized I/O file + /// integrity completion. The data stored in the file and the file's + /// metadata are synchronized. This is similar to `O_SYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + file-integrity-sync, + /// Request that writes be performed according to synchronized I/O data + /// integrity completion. Only the data stored in the file is + /// synchronized. This is similar to `O_DSYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + data-integrity-sync, + /// Requests that reads be performed at the same level of integrity + /// requested for writes. This is similar to `O_RSYNC` in POSIX. + /// + /// The precise semantics of this operation have not yet been defined for + /// WASI. At this time, it should be interpreted as a request, and not a + /// requirement. + requested-write-sync, + /// Mutating directories mode: Directory contents may be mutated. + /// + /// When this flag is unset on a descriptor, operations using the + /// descriptor which would create, rename, delete, modify the data or + /// metadata of filesystem objects, or obtain another handle which + /// would permit any of those, shall fail with `error-code::read-only` if + /// they would otherwise succeed. + /// + /// This may only be set on directories. + mutate-directory, + } + + /// Flags determining the method of how paths are resolved. + @since(version = 0.2.0) + flags path-flags { + /// As long as the resolved path corresponds to a symbolic link, it is + /// expanded. + symlink-follow, + } + + /// Open flags used by `open-at`. + @since(version = 0.2.0) + flags open-flags { + /// Create file if it does not exist, similar to `O_CREAT` in POSIX. + create, + /// Fail if not a directory, similar to `O_DIRECTORY` in POSIX. + directory, + /// Fail if file already exists, similar to `O_EXCL` in POSIX. + exclusive, + /// Truncate file to size 0, similar to `O_TRUNC` in POSIX. + truncate, + } + + /// Number of hard links to an inode. + @since(version = 0.2.0) + type link-count = u64; + + /// File attributes. + /// + /// Note: This was called `filestat` in earlier versions of WASI. + @since(version = 0.2.0) + record descriptor-stat { + /// File type. + %type: descriptor-type, + /// Number of hard links to the file. + link-count: link-count, + /// For regular files, the file size in bytes. For symbolic links, the + /// length in bytes of the pathname contained in the symbolic link. + size: filesize, + /// Last data access timestamp. + /// + /// If the `option` is none, the platform doesn't maintain an access + /// timestamp for this file. + data-access-timestamp: option, + /// Last data modification timestamp. + /// + /// If the `option` is none, the platform doesn't maintain a + /// modification timestamp for this file. + data-modification-timestamp: option, + /// Last file status-change timestamp. + /// + /// If the `option` is none, the platform doesn't maintain a + /// status-change timestamp for this file. + status-change-timestamp: option, + } + + /// When setting a timestamp, this gives the value to set it to. + @since(version = 0.2.0) + variant new-timestamp { + /// Leave the timestamp set to its previous value. + no-change, + /// Set the timestamp to the current time of the system clock associated + /// with the filesystem. + now, + /// Set the timestamp to the given value. + timestamp(datetime), + } + + /// A directory entry. + record directory-entry { + /// The type of the file referred to by this directory entry. + %type: descriptor-type, + /// The name of the object. + name: string, + } + + /// Error codes returned by functions, similar to `errno` in POSIX. + /// Not all of these error codes are returned by the functions provided by this + /// API; some are used in higher-level library layers, and others are provided + /// merely for alignment with POSIX. + enum error-code { + /// Permission denied, similar to `EACCES` in POSIX. + access, + /// Resource unavailable, or operation would block, similar to `EAGAIN` and `EWOULDBLOCK` in POSIX. + would-block, + /// Connection already in progress, similar to `EALREADY` in POSIX. + already, + /// Bad descriptor, similar to `EBADF` in POSIX. + bad-descriptor, + /// Device or resource busy, similar to `EBUSY` in POSIX. + busy, + /// Resource deadlock would occur, similar to `EDEADLK` in POSIX. + deadlock, + /// Storage quota exceeded, similar to `EDQUOT` in POSIX. + quota, + /// File exists, similar to `EEXIST` in POSIX. + exist, + /// File too large, similar to `EFBIG` in POSIX. + file-too-large, + /// Illegal byte sequence, similar to `EILSEQ` in POSIX. + illegal-byte-sequence, + /// Operation in progress, similar to `EINPROGRESS` in POSIX. + in-progress, + /// Interrupted function, similar to `EINTR` in POSIX. + interrupted, + /// Invalid argument, similar to `EINVAL` in POSIX. + invalid, + /// I/O error, similar to `EIO` in POSIX. + io, + /// Is a directory, similar to `EISDIR` in POSIX. + is-directory, + /// Too many levels of symbolic links, similar to `ELOOP` in POSIX. + loop, + /// Too many links, similar to `EMLINK` in POSIX. + too-many-links, + /// Message too large, similar to `EMSGSIZE` in POSIX. + message-size, + /// Filename too long, similar to `ENAMETOOLONG` in POSIX. + name-too-long, + /// No such device, similar to `ENODEV` in POSIX. + no-device, + /// No such file or directory, similar to `ENOENT` in POSIX. + no-entry, + /// No locks available, similar to `ENOLCK` in POSIX. + no-lock, + /// Not enough space, similar to `ENOMEM` in POSIX. + insufficient-memory, + /// No space left on device, similar to `ENOSPC` in POSIX. + insufficient-space, + /// Not a directory or a symbolic link to a directory, similar to `ENOTDIR` in POSIX. + not-directory, + /// Directory not empty, similar to `ENOTEMPTY` in POSIX. + not-empty, + /// State not recoverable, similar to `ENOTRECOVERABLE` in POSIX. + not-recoverable, + /// Not supported, similar to `ENOTSUP` and `ENOSYS` in POSIX. + unsupported, + /// Inappropriate I/O control operation, similar to `ENOTTY` in POSIX. + no-tty, + /// No such device or address, similar to `ENXIO` in POSIX. + no-such-device, + /// Value too large to be stored in data type, similar to `EOVERFLOW` in POSIX. + overflow, + /// Operation not permitted, similar to `EPERM` in POSIX. + not-permitted, + /// Broken pipe, similar to `EPIPE` in POSIX. + pipe, + /// Read-only file system, similar to `EROFS` in POSIX. + read-only, + /// Invalid seek, similar to `ESPIPE` in POSIX. + invalid-seek, + /// Text file busy, similar to `ETXTBSY` in POSIX. + text-file-busy, + /// Cross-device link, similar to `EXDEV` in POSIX. + cross-device, + } + + /// File or memory access pattern advisory information. + @since(version = 0.2.0) + enum advice { + /// The application has no advice to give on its behavior with respect + /// to the specified data. + normal, + /// The application expects to access the specified data sequentially + /// from lower offsets to higher offsets. + sequential, + /// The application expects to access the specified data in a random + /// order. + random, + /// The application expects to access the specified data in the near + /// future. + will-need, + /// The application expects that it will not access the specified data + /// in the near future. + dont-need, + /// The application expects to access the specified data once and then + /// not reuse it thereafter. + no-reuse, + } + + /// A 128-bit hash value, split into parts because wasm doesn't have a + /// 128-bit integer type. + @since(version = 0.2.0) + record metadata-hash-value { + /// 64 bits of a 128-bit hash value. + lower: u64, + /// Another 64 bits of a 128-bit hash value. + upper: u64, + } + + /// A descriptor is a reference to a filesystem object, which may be a file, + /// directory, named pipe, special file, or other object on which filesystem + /// calls may be made. + @since(version = 0.2.0) + resource descriptor { + /// Return a stream for reading from a file, if available. + /// + /// May fail with an error-code describing why the file cannot be read. + /// + /// Multiple read, write, and append streams may be active on the same open + /// file and they do not interfere with each other. + /// + /// Note: This allows using `read-stream`, which is similar to `read` in POSIX. + @since(version = 0.2.0) + read-via-stream: func(offset: filesize) -> result; + /// Return a stream for writing to a file, if available. + /// + /// May fail with an error-code describing why the file cannot be written. + /// + /// Note: This allows using `write-stream`, which is similar to `write` in + /// POSIX. + @since(version = 0.2.0) + write-via-stream: func(offset: filesize) -> result; + /// Return a stream for appending to a file, if available. + /// + /// May fail with an error-code describing why the file cannot be appended. + /// + /// Note: This allows using `write-stream`, which is similar to `write` with + /// `O_APPEND` in POSIX. + @since(version = 0.2.0) + append-via-stream: func() -> result; + /// Provide file advisory information on a descriptor. + /// + /// This is similar to `posix_fadvise` in POSIX. + @since(version = 0.2.0) + advise: func(offset: filesize, length: filesize, advice: advice) -> result<_, error-code>; + /// Synchronize the data of a file to disk. + /// + /// This function succeeds with no effect if the file descriptor is not + /// opened for writing. + /// + /// Note: This is similar to `fdatasync` in POSIX. + @since(version = 0.2.0) + sync-data: func() -> result<_, error-code>; + /// Get flags associated with a descriptor. + /// + /// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX. + /// + /// Note: This returns the value that was the `fs_flags` value returned + /// from `fdstat_get` in earlier versions of WASI. + @since(version = 0.2.0) + get-flags: func() -> result; + /// Get the dynamic type of a descriptor. + /// + /// Note: This returns the same value as the `type` field of the `fd-stat` + /// returned by `stat`, `stat-at` and similar. + /// + /// Note: This returns similar flags to the `st_mode & S_IFMT` value provided + /// by `fstat` in POSIX. + /// + /// Note: This returns the value that was the `fs_filetype` value returned + /// from `fdstat_get` in earlier versions of WASI. + @since(version = 0.2.0) + get-type: func() -> result; + /// Adjust the size of an open file. If this increases the file's size, the + /// extra bytes are filled with zeros. + /// + /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. + @since(version = 0.2.0) + set-size: func(size: filesize) -> result<_, error-code>; + /// Adjust the timestamps of an open file or directory. + /// + /// Note: This is similar to `futimens` in POSIX. + /// + /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI. + @since(version = 0.2.0) + set-times: func(data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; + /// Read from a descriptor, without using and updating the descriptor's offset. + /// + /// This function returns a list of bytes containing the data that was + /// read, along with a bool which, when true, indicates that the end of the + /// file was reached. The returned list will contain up to `length` bytes; it + /// may return fewer than requested, if the end of the file is reached or + /// if the I/O operation is interrupted. + /// + /// In the future, this may change to return a `stream`. + /// + /// Note: This is similar to `pread` in POSIX. + @since(version = 0.2.0) + read: func(length: filesize, offset: filesize) -> result, bool>, error-code>; + /// Write to a descriptor, without using and updating the descriptor's offset. + /// + /// It is valid to write past the end of a file; the file is extended to the + /// extent of the write, with bytes between the previous end and the start of + /// the write set to zero. + /// + /// In the future, this may change to take a `stream`. + /// + /// Note: This is similar to `pwrite` in POSIX. + @since(version = 0.2.0) + write: func(buffer: list, offset: filesize) -> result; + /// Read directory entries from a directory. + /// + /// On filesystems where directories contain entries referring to themselves + /// and their parents, often named `.` and `..` respectively, these entries + /// are omitted. + /// + /// This always returns a new stream which starts at the beginning of the + /// directory. Multiple streams may be active on the same directory, and they + /// do not interfere with each other. + @since(version = 0.2.0) + read-directory: func() -> result; + /// Synchronize the data and metadata of a file to disk. + /// + /// This function succeeds with no effect if the file descriptor is not + /// opened for writing. + /// + /// Note: This is similar to `fsync` in POSIX. + @since(version = 0.2.0) + sync: func() -> result<_, error-code>; + /// Create a directory. + /// + /// Note: This is similar to `mkdirat` in POSIX. + @since(version = 0.2.0) + create-directory-at: func(path: string) -> result<_, error-code>; + /// Return the attributes of an open file or directory. + /// + /// Note: This is similar to `fstat` in POSIX, except that it does not return + /// device and inode information. For testing whether two descriptors refer to + /// the same underlying filesystem object, use `is-same-object`. To obtain + /// additional data that can be used do determine whether a file has been + /// modified, use `metadata-hash`. + /// + /// Note: This was called `fd_filestat_get` in earlier versions of WASI. + @since(version = 0.2.0) + stat: func() -> result; + /// Return the attributes of a file or directory. + /// + /// Note: This is similar to `fstatat` in POSIX, except that it does not + /// return device and inode information. See the `stat` description for a + /// discussion of alternatives. + /// + /// Note: This was called `path_filestat_get` in earlier versions of WASI. + @since(version = 0.2.0) + stat-at: func(path-flags: path-flags, path: string) -> result; + /// Adjust the timestamps of a file or directory. + /// + /// Note: This is similar to `utimensat` in POSIX. + /// + /// Note: This was called `path_filestat_set_times` in earlier versions of + /// WASI. + @since(version = 0.2.0) + set-times-at: func(path-flags: path-flags, path: string, data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; + /// Create a hard link. + /// + /// Fails with `error-code::no-entry` if the old path does not exist, + /// with `error-code::exist` if the new path already exists, and + /// `error-code::not-permitted` if the old path is not a file. + /// + /// Note: This is similar to `linkat` in POSIX. + @since(version = 0.2.0) + link-at: func(old-path-flags: path-flags, old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; + /// Open a file or directory. + /// + /// If `flags` contains `descriptor-flags::mutate-directory`, and the base + /// descriptor doesn't have `descriptor-flags::mutate-directory` set, + /// `open-at` fails with `error-code::read-only`. + /// + /// If `flags` contains `write` or `mutate-directory`, or `open-flags` + /// contains `truncate` or `create`, and the base descriptor doesn't have + /// `descriptor-flags::mutate-directory` set, `open-at` fails with + /// `error-code::read-only`. + /// + /// Note: This is similar to `openat` in POSIX. + @since(version = 0.2.0) + open-at: func(path-flags: path-flags, path: string, open-flags: open-flags, %flags: descriptor-flags) -> result; + /// Read the contents of a symbolic link. + /// + /// If the contents contain an absolute or rooted path in the underlying + /// filesystem, this function fails with `error-code::not-permitted`. + /// + /// Note: This is similar to `readlinkat` in POSIX. + @since(version = 0.2.0) + readlink-at: func(path: string) -> result; + /// Remove a directory. + /// + /// Return `error-code::not-empty` if the directory is not empty. + /// + /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. + @since(version = 0.2.0) + remove-directory-at: func(path: string) -> result<_, error-code>; + /// Rename a filesystem object. + /// + /// Note: This is similar to `renameat` in POSIX. + @since(version = 0.2.0) + rename-at: func(old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; + /// Create a symbolic link (also known as a "symlink"). + /// + /// If `old-path` starts with `/`, the function fails with + /// `error-code::not-permitted`. + /// + /// Note: This is similar to `symlinkat` in POSIX. + @since(version = 0.2.0) + symlink-at: func(old-path: string, new-path: string) -> result<_, error-code>; + /// Unlink a filesystem object that is not a directory. + /// + /// Return `error-code::is-directory` if the path refers to a directory. + /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. + @since(version = 0.2.0) + unlink-file-at: func(path: string) -> result<_, error-code>; + /// Test whether two descriptors refer to the same filesystem object. + /// + /// In POSIX, this corresponds to testing whether the two descriptors have the + /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers. + /// wasi-filesystem does not expose device and inode numbers, so this function + /// may be used instead. + @since(version = 0.2.0) + is-same-object: func(other: borrow) -> bool; + /// Return a hash of the metadata associated with a filesystem object referred + /// to by a descriptor. + /// + /// This returns a hash of the last-modification timestamp and file size, and + /// may also include the inode number, device number, birth timestamp, and + /// other metadata fields that may change when the file is modified or + /// replaced. It may also include a secret value chosen by the + /// implementation and not otherwise exposed. + /// + /// Implementations are encouraged to provide the following properties: + /// + /// - If the file is not modified or replaced, the computed hash value should + /// usually not change. + /// - If the object is modified or replaced, the computed hash value should + /// usually change. + /// - The inputs to the hash should not be easily computable from the + /// computed hash. + /// + /// However, none of these is required. + @since(version = 0.2.0) + metadata-hash: func() -> result; + /// Return a hash of the metadata associated with a filesystem object referred + /// to by a directory descriptor and a relative path. + /// + /// This performs the same hash computation as `metadata-hash`. + @since(version = 0.2.0) + metadata-hash-at: func(path-flags: path-flags, path: string) -> result; + } + + /// A stream of directory entries. + @since(version = 0.2.0) + resource directory-entry-stream { + /// Read a single directory entry from a `directory-entry-stream`. + @since(version = 0.2.0) + read-directory-entry: func() -> result, error-code>; + } + + /// Attempts to extract a filesystem-related `error-code` from the stream + /// `error` provided. + /// + /// Stream operations which return `stream-error::last-operation-failed` + /// have a payload with more information about the operation that failed. + /// This payload can be passed through to this function to see if there's + /// filesystem-related information about the error to return. + /// + /// Note that this function is fallible because not all stream-related + /// errors are filesystem-related errors. + @since(version = 0.2.0) + filesystem-error-code: func(err: borrow) -> option; +} + +@since(version = 0.2.0) +interface preopens { + @since(version = 0.2.0) + use types.{descriptor}; + + /// Return the set of preopened directories, and their paths. + @since(version = 0.2.0) + get-directories: func() -> list>; +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import wasi:io/error@0.2.9; + @since(version = 0.2.0) + import wasi:io/poll@0.2.9; + @since(version = 0.2.0) + import wasi:io/streams@0.2.9; + @since(version = 0.2.0) + import wasi:clocks/wall-clock@0.2.9; + @since(version = 0.2.0) + import types; + @since(version = 0.2.0) + import preopens; +} diff --git a/tools/vendor/wasip2/wit/deps/http.wit b/tools/vendor/wasip2/wit/deps/http.wit new file mode 100644 index 0000000000..580b28105d --- /dev/null +++ b/tools/vendor/wasip2/wit/deps/http.wit @@ -0,0 +1,733 @@ +package wasi:http@0.2.9; + +/// This interface defines all of the types and methods for implementing +/// HTTP Requests and Responses, both incoming and outgoing, as well as +/// their headers, trailers, and bodies. +@since(version = 0.2.0) +interface types { + @since(version = 0.2.0) + use wasi:clocks/monotonic-clock@0.2.9.{duration}; + @since(version = 0.2.0) + use wasi:io/streams@0.2.9.{input-stream, output-stream}; + @since(version = 0.2.0) + use wasi:io/error@0.2.9.{error as io-error}; + @since(version = 0.2.0) + use wasi:io/poll@0.2.9.{pollable}; + + /// This type corresponds to HTTP standard Methods. + @since(version = 0.2.0) + variant method { + get, + head, + post, + put, + delete, + connect, + options, + trace, + patch, + other(string), + } + + /// This type corresponds to HTTP standard Related Schemes. + @since(version = 0.2.0) + variant scheme { + HTTP, + HTTPS, + other(string), + } + + /// Defines the case payload type for `DNS-error` above: + @since(version = 0.2.0) + record DNS-error-payload { + rcode: option, + info-code: option, + } + + /// Defines the case payload type for `TLS-alert-received` above: + @since(version = 0.2.0) + record TLS-alert-received-payload { + alert-id: option, + alert-message: option, + } + + /// Defines the case payload type for `HTTP-response-{header,trailer}-size` above: + @since(version = 0.2.0) + record field-size-payload { + field-name: option, + field-size: option, + } + + /// These cases are inspired by the IANA HTTP Proxy Error Types: + /// + @since(version = 0.2.0) + variant error-code { + DNS-timeout, + DNS-error(DNS-error-payload), + destination-not-found, + destination-unavailable, + destination-IP-prohibited, + destination-IP-unroutable, + connection-refused, + connection-terminated, + connection-timeout, + connection-read-timeout, + connection-write-timeout, + connection-limit-reached, + TLS-protocol-error, + TLS-certificate-error, + TLS-alert-received(TLS-alert-received-payload), + HTTP-request-denied, + HTTP-request-length-required, + HTTP-request-body-size(option), + HTTP-request-method-invalid, + HTTP-request-URI-invalid, + HTTP-request-URI-too-long, + HTTP-request-header-section-size(option), + HTTP-request-header-size(option), + HTTP-request-trailer-section-size(option), + HTTP-request-trailer-size(field-size-payload), + HTTP-response-incomplete, + HTTP-response-header-section-size(option), + HTTP-response-header-size(field-size-payload), + HTTP-response-body-size(option), + HTTP-response-trailer-section-size(option), + HTTP-response-trailer-size(field-size-payload), + HTTP-response-transfer-coding(option), + HTTP-response-content-coding(option), + HTTP-response-timeout, + HTTP-upgrade-failed, + HTTP-protocol-error, + loop-detected, + configuration-error, + /// This is a catch-all error for anything that doesn't fit cleanly into a + /// more specific case. It also includes an optional string for an + /// unstructured description of the error. Users should not depend on the + /// string for diagnosing errors, as it's not required to be consistent + /// between implementations. + internal-error(option), + } + + /// This type enumerates the different kinds of errors that may occur when + /// setting or appending to a `fields` resource. + @since(version = 0.2.0) + variant header-error { + /// This error indicates that a `field-name` or `field-value` was + /// syntactically invalid when used with an operation that sets headers in a + /// `fields`. + invalid-syntax, + /// This error indicates that a forbidden `field-name` was used when trying + /// to set a header in a `fields`. + forbidden, + /// This error indicates that the operation on the `fields` was not + /// permitted because the fields are immutable. + immutable, + } + + /// Field keys are always strings. + /// + /// Field keys should always be treated as case insensitive by the `fields` + /// resource for the purposes of equality checking. + /// + /// # Deprecation + /// + /// This type has been deprecated in favor of the `field-name` type. + @since(version = 0.2.0) + @deprecated(version = 0.2.2) + type field-key = string; + + /// Field names are always strings. + /// + /// Field names should always be treated as case insensitive by the `fields` + /// resource for the purposes of equality checking. + @since(version = 0.2.1) + type field-name = field-key; + + /// Field values should always be ASCII strings. However, in + /// reality, HTTP implementations often have to interpret malformed values, + /// so they are provided as a list of bytes. + @since(version = 0.2.0) + type field-value = list; + + /// This following block defines the `fields` resource which corresponds to + /// HTTP standard Fields. Fields are a common representation used for both + /// Headers and Trailers. + /// + /// A `fields` may be mutable or immutable. A `fields` created using the + /// constructor, `from-list`, or `clone` will be mutable, but a `fields` + /// resource given by other means (including, but not limited to, + /// `incoming-request.headers`, `outgoing-request.headers`) might be + /// immutable. In an immutable fields, the `set`, `append`, and `delete` + /// operations will fail with `header-error.immutable`. + @since(version = 0.2.0) + resource fields { + /// Construct an empty HTTP Fields. + /// + /// The resulting `fields` is mutable. + @since(version = 0.2.0) + constructor(); + /// Construct an HTTP Fields. + /// + /// The resulting `fields` is mutable. + /// + /// The list represents each name-value pair in the Fields. Names + /// which have multiple values are represented by multiple entries in this + /// list with the same name. + /// + /// The tuple is a pair of the field name, represented as a string, and + /// Value, represented as a list of bytes. + /// + /// An error result will be returned if any `field-name` or `field-value` is + /// syntactically invalid, or if a field is forbidden. + @since(version = 0.2.0) + from-list: static func(entries: list>) -> result; + /// Get all of the values corresponding to a name. If the name is not present + /// in this `fields` or is syntactically invalid, an empty list is returned. + /// However, if the name is present but empty, this is represented by a list + /// with one or more empty field-values present. + @since(version = 0.2.0) + get: func(name: field-name) -> list; + /// Returns `true` when the name is present in this `fields`. If the name is + /// syntactically invalid, `false` is returned. + @since(version = 0.2.0) + has: func(name: field-name) -> bool; + /// Set all of the values for a name. Clears any existing values for that + /// name, if they have been set. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + /// + /// Fails with `header-error.invalid-syntax` if the `field-name` or any of + /// the `field-value`s are syntactically invalid. + @since(version = 0.2.0) + set: func(name: field-name, value: list) -> result<_, header-error>; + /// Delete all values for a name. Does nothing if no values for the name + /// exist. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + /// + /// Fails with `header-error.invalid-syntax` if the `field-name` is + /// syntactically invalid. + @since(version = 0.2.0) + delete: func(name: field-name) -> result<_, header-error>; + /// Append a value for a name. Does not change or delete any existing + /// values for that name. + /// + /// Fails with `header-error.immutable` if the `fields` are immutable. + /// + /// Fails with `header-error.invalid-syntax` if the `field-name` or + /// `field-value` are syntactically invalid. + @since(version = 0.2.0) + append: func(name: field-name, value: field-value) -> result<_, header-error>; + /// Retrieve the full set of names and values in the Fields. Like the + /// constructor, the list represents each name-value pair. + /// + /// The outer list represents each name-value pair in the Fields. Names + /// which have multiple values are represented by multiple entries in this + /// list with the same name. + /// + /// The names and values are always returned in the original casing and in + /// the order in which they will be serialized for transport. + @since(version = 0.2.0) + entries: func() -> list>; + /// Make a deep copy of the Fields. Equivalent in behavior to calling the + /// `fields` constructor on the return value of `entries`. The resulting + /// `fields` is mutable. + @since(version = 0.2.0) + clone: func() -> fields; + } + + /// Headers is an alias for Fields. + @since(version = 0.2.0) + type headers = fields; + + /// Trailers is an alias for Fields. + @since(version = 0.2.0) + type trailers = fields; + + /// Represents an incoming HTTP Request. + @since(version = 0.2.0) + resource incoming-request { + /// Returns the method of the incoming request. + @since(version = 0.2.0) + method: func() -> method; + /// Returns the path with query parameters from the request, as a string. + @since(version = 0.2.0) + path-with-query: func() -> option; + /// Returns the protocol scheme from the request. + @since(version = 0.2.0) + scheme: func() -> option; + /// Returns the authority of the Request's target URI, if present. + @since(version = 0.2.0) + authority: func() -> option; + /// Get the `headers` associated with the request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// The `headers` returned are a child resource: it must be dropped before + /// the parent `incoming-request` is dropped. Dropping this + /// `incoming-request` before all children are dropped will trap. + @since(version = 0.2.0) + headers: func() -> headers; + /// Gives the `incoming-body` associated with this request. Will only + /// return success at most once, and subsequent calls will return error. + @since(version = 0.2.0) + consume: func() -> result; + } + + /// Represents an outgoing HTTP Request. + @since(version = 0.2.0) + resource outgoing-request { + /// Construct a new `outgoing-request` with a default `method` of `GET`, and + /// `none` values for `path-with-query`, `scheme`, and `authority`. + /// + /// * `headers` is the HTTP Headers for the Request. + /// + /// It is possible to construct, or manipulate with the accessor functions + /// below, an `outgoing-request` with an invalid combination of `scheme` + /// and `authority`, or `headers` which are not permitted to be sent. + /// It is the obligation of the `outgoing-handler.handle` implementation + /// to reject invalid constructions of `outgoing-request`. + @since(version = 0.2.0) + constructor(headers: headers); + /// Returns the resource corresponding to the outgoing Body for this + /// Request. + /// + /// Returns success on the first call: the `outgoing-body` resource for + /// this `outgoing-request` can be retrieved at most once. Subsequent + /// calls will return error. + @since(version = 0.2.0) + body: func() -> result; + /// Get the Method for the Request. + @since(version = 0.2.0) + method: func() -> method; + /// Set the Method for the Request. Fails if the string present in a + /// `method.other` argument is not a syntactically valid method. + @since(version = 0.2.0) + set-method: func(method: method) -> result; + /// Get the combination of the HTTP Path and Query for the Request. + /// When `none`, this represents an empty Path and empty Query. + @since(version = 0.2.0) + path-with-query: func() -> option; + /// Set the combination of the HTTP Path and Query for the Request. + /// When `none`, this represents an empty Path and empty Query. Fails is the + /// string given is not a syntactically valid path and query uri component. + @since(version = 0.2.0) + set-path-with-query: func(path-with-query: option) -> result; + /// Get the HTTP Related Scheme for the Request. When `none`, the + /// implementation may choose an appropriate default scheme. + @since(version = 0.2.0) + scheme: func() -> option; + /// Set the HTTP Related Scheme for the Request. When `none`, the + /// implementation may choose an appropriate default scheme. Fails if the + /// string given is not a syntactically valid uri scheme. + @since(version = 0.2.0) + set-scheme: func(scheme: option) -> result; + /// Get the authority of the Request's target URI. A value of `none` may be used + /// with Related Schemes which do not require an authority. The HTTP and + /// HTTPS schemes always require an authority. + @since(version = 0.2.0) + authority: func() -> option; + /// Set the authority of the Request's target URI. A value of `none` may be used + /// with Related Schemes which do not require an authority. The HTTP and + /// HTTPS schemes always require an authority. Fails if the string given is + /// not a syntactically valid URI authority. + @since(version = 0.2.0) + set-authority: func(authority: option) -> result; + /// Get the headers associated with the Request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// This headers resource is a child: it must be dropped before the parent + /// `outgoing-request` is dropped, or its ownership is transferred to + /// another component by e.g. `outgoing-handler.handle`. + @since(version = 0.2.0) + headers: func() -> headers; + } + + /// Parameters for making an HTTP Request. Each of these parameters is + /// currently an optional timeout applicable to the transport layer of the + /// HTTP protocol. + /// + /// These timeouts are separate from any the user may use to bound a + /// blocking call to `wasi:io/poll.poll`. + @since(version = 0.2.0) + resource request-options { + /// Construct a default `request-options` value. + @since(version = 0.2.0) + constructor(); + /// The timeout for the initial connect to the HTTP Server. + @since(version = 0.2.0) + connect-timeout: func() -> option; + /// Set the timeout for the initial connect to the HTTP Server. An error + /// return value indicates that this timeout is not supported. + @since(version = 0.2.0) + set-connect-timeout: func(duration: option) -> result; + /// The timeout for receiving the first byte of the Response body. + @since(version = 0.2.0) + first-byte-timeout: func() -> option; + /// Set the timeout for receiving the first byte of the Response body. An + /// error return value indicates that this timeout is not supported. + @since(version = 0.2.0) + set-first-byte-timeout: func(duration: option) -> result; + /// The timeout for receiving subsequent chunks of bytes in the Response + /// body stream. + @since(version = 0.2.0) + between-bytes-timeout: func() -> option; + /// Set the timeout for receiving subsequent chunks of bytes in the Response + /// body stream. An error return value indicates that this timeout is not + /// supported. + @since(version = 0.2.0) + set-between-bytes-timeout: func(duration: option) -> result; + } + + /// Represents the ability to send an HTTP Response. + /// + /// This resource is used by the `wasi:http/incoming-handler` interface to + /// allow a Response to be sent corresponding to the Request provided as the + /// other argument to `incoming-handler.handle`. + @since(version = 0.2.0) + resource response-outparam { + /// Send an HTTP 1xx response. + /// + /// Unlike `response-outparam.set`, this does not consume the + /// `response-outparam`, allowing the guest to send an arbitrary number of + /// informational responses before sending the final response using + /// `response-outparam.set`. + /// + /// This will return an `HTTP-protocol-error` if `status` is not in the + /// range [100-199], or an `internal-error` if the implementation does not + /// support informational responses. + @unstable(feature = informational-outbound-responses) + send-informational: func(status: u16, headers: headers) -> result<_, error-code>; + /// Set the value of the `response-outparam` to either send a response, + /// or indicate an error. + /// + /// This method consumes the `response-outparam` to ensure that it is + /// called at most once. If it is never called, the implementation + /// will respond with an error. + /// + /// The user may provide an `error` to `response` to allow the + /// implementation determine how to respond with an HTTP error response. + @since(version = 0.2.0) + set: static func(param: response-outparam, response: result); + } + + /// This type corresponds to the HTTP standard Status Code. + @since(version = 0.2.0) + type status-code = u16; + + /// Represents an incoming HTTP Response. + @since(version = 0.2.0) + resource incoming-response { + /// Returns the status code from the incoming response. + @since(version = 0.2.0) + status: func() -> status-code; + /// Returns the headers from the incoming response. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// This headers resource is a child: it must be dropped before the parent + /// `incoming-response` is dropped. + @since(version = 0.2.0) + headers: func() -> headers; + /// Returns the incoming body. May be called at most once. Returns error + /// if called additional times. + @since(version = 0.2.0) + consume: func() -> result; + } + + /// Represents an incoming HTTP Request or Response's Body. + /// + /// A body has both its contents - a stream of bytes - and a (possibly + /// empty) set of trailers, indicating that the full contents of the + /// body have been received. This resource represents the contents as + /// an `input-stream` and the delivery of trailers as a `future-trailers`, + /// and ensures that the user of this interface may only be consuming either + /// the body contents or waiting on trailers at any given time. + @since(version = 0.2.0) + resource incoming-body { + /// Returns the contents of the body, as a stream of bytes. + /// + /// Returns success on first call: the stream representing the contents + /// can be retrieved at most once. Subsequent calls will return error. + /// + /// The returned `input-stream` resource is a child: it must be dropped + /// before the parent `incoming-body` is dropped, or consumed by + /// `incoming-body.finish`. + /// + /// This invariant ensures that the implementation can determine whether + /// the user is consuming the contents of the body, waiting on the + /// `future-trailers` to be ready, or neither. This allows for network + /// backpressure is to be applied when the user is consuming the body, + /// and for that backpressure to not inhibit delivery of the trailers if + /// the user does not read the entire body. + @since(version = 0.2.0) + %stream: func() -> result; + /// Takes ownership of `incoming-body`, and returns a `future-trailers`. + /// This function will trap if the `input-stream` child is still alive. + @since(version = 0.2.0) + finish: static func(this: incoming-body) -> future-trailers; + } + + /// Represents a future which may eventually return trailers, or an error. + /// + /// In the case that the incoming HTTP Request or Response did not have any + /// trailers, this future will resolve to the empty set of trailers once the + /// complete Request or Response body has been received. + @since(version = 0.2.0) + resource future-trailers { + /// Returns a pollable which becomes ready when either the trailers have + /// been received, or an error has occurred. When this pollable is ready, + /// the `get` method will return `some`. + @since(version = 0.2.0) + subscribe: func() -> pollable; + /// Returns the contents of the trailers, or an error which occurred, + /// once the future is ready. + /// + /// The outer `option` represents future readiness. Users can wait on this + /// `option` to become `some` using the `subscribe` method. + /// + /// The outer `result` is used to retrieve the trailers or error at most + /// once. It will be success on the first call in which the outer option + /// is `some`, and error on subsequent calls. + /// + /// The inner `result` represents that either the HTTP Request or Response + /// body, as well as any trailers, were received successfully, or that an + /// error occurred receiving them. The optional `trailers` indicates whether + /// or not trailers were present in the body. + /// + /// When some `trailers` are returned by this method, the `trailers` + /// resource is immutable, and a child. Use of the `set`, `append`, or + /// `delete` methods will return an error, and the resource must be + /// dropped before the parent `future-trailers` is dropped. + @since(version = 0.2.0) + get: func() -> option, error-code>>>; + } + + /// Represents an outgoing HTTP Response. + @since(version = 0.2.0) + resource outgoing-response { + /// Construct an `outgoing-response`, with a default `status-code` of `200`. + /// If a different `status-code` is needed, it must be set via the + /// `set-status-code` method. + /// + /// * `headers` is the HTTP Headers for the Response. + @since(version = 0.2.0) + constructor(headers: headers); + /// Get the HTTP Status Code for the Response. + @since(version = 0.2.0) + status-code: func() -> status-code; + /// Set the HTTP Status Code for the Response. Fails if the status-code + /// given is not a valid http status code. + @since(version = 0.2.0) + set-status-code: func(status-code: status-code) -> result; + /// Get the headers associated with the Request. + /// + /// The returned `headers` resource is immutable: `set`, `append`, and + /// `delete` operations will fail with `header-error.immutable`. + /// + /// This headers resource is a child: it must be dropped before the parent + /// `outgoing-request` is dropped, or its ownership is transferred to + /// another component by e.g. `outgoing-handler.handle`. + @since(version = 0.2.0) + headers: func() -> headers; + /// Returns the resource corresponding to the outgoing Body for this Response. + /// + /// Returns success on the first call: the `outgoing-body` resource for + /// this `outgoing-response` can be retrieved at most once. Subsequent + /// calls will return error. + @since(version = 0.2.0) + body: func() -> result; + } + + /// Represents an outgoing HTTP Request or Response's Body. + /// + /// A body has both its contents - a stream of bytes - and a (possibly + /// empty) set of trailers, inducating the full contents of the body + /// have been sent. This resource represents the contents as an + /// `output-stream` child resource, and the completion of the body (with + /// optional trailers) with a static function that consumes the + /// `outgoing-body` resource, and ensures that the user of this interface + /// may not write to the body contents after the body has been finished. + /// + /// If the user code drops this resource, as opposed to calling the static + /// method `finish`, the implementation should treat the body as incomplete, + /// and that an error has occurred. The implementation should propagate this + /// error to the HTTP protocol by whatever means it has available, + /// including: corrupting the body on the wire, aborting the associated + /// Request, or sending a late status code for the Response. + @since(version = 0.2.0) + resource outgoing-body { + /// Returns a stream for writing the body contents. + /// + /// The returned `output-stream` is a child resource: it must be dropped + /// before the parent `outgoing-body` resource is dropped (or finished), + /// otherwise the `outgoing-body` drop or `finish` will trap. + /// + /// Returns success on the first call: the `output-stream` resource for + /// this `outgoing-body` may be retrieved at most once. Subsequent calls + /// will return error. + @since(version = 0.2.0) + write: func() -> result; + /// Finalize an outgoing body, optionally providing trailers. This must be + /// called to signal that the response is complete. If the `outgoing-body` + /// is dropped without calling `outgoing-body.finalize`, the implementation + /// should treat the body as corrupted. + /// + /// Fails if the body's `outgoing-request` or `outgoing-response` was + /// constructed with a Content-Length header, and the contents written + /// to the body (via `write`) does not match the value given in the + /// Content-Length. + @since(version = 0.2.0) + finish: static func(this: outgoing-body, trailers: option) -> result<_, error-code>; + } + + /// Represents a future which may eventually return an incoming HTTP + /// Response, or an error. + /// + /// This resource is returned by the `wasi:http/outgoing-handler` interface to + /// provide the HTTP Response corresponding to the sent Request. + @since(version = 0.2.0) + resource future-incoming-response { + /// Returns a pollable which becomes ready when either the Response has + /// been received, or an error has occurred. When this pollable is ready, + /// the `get` method will return `some`. + @since(version = 0.2.0) + subscribe: func() -> pollable; + /// Returns the incoming HTTP Response, or an error, once one is ready. + /// + /// The outer `option` represents future readiness. Users can wait on this + /// `option` to become `some` using the `subscribe` method. + /// + /// The outer `result` is used to retrieve the response or error at most + /// once. It will be success on the first call in which the outer option + /// is `some`, and error on subsequent calls. + /// + /// The inner `result` represents that either the incoming HTTP Response + /// status and headers have received successfully, or that an error + /// occurred. Errors may also occur while consuming the response body, + /// but those will be reported by the `incoming-body` and its + /// `output-stream` child. + @since(version = 0.2.0) + get: func() -> option>>; + } + + /// Attempts to extract a http-related `error` from the wasi:io `error` + /// provided. + /// + /// Stream operations which return + /// `wasi:io/stream.stream-error.last-operation-failed` have a payload of + /// type `wasi:io/error.error` with more information about the operation + /// that failed. This payload can be passed through to this function to see + /// if there's http-related information about the error to return. + /// + /// Note that this function is fallible because not all io-errors are + /// http-related errors. + @since(version = 0.2.0) + http-error-code: func(err: borrow) -> option; +} + +/// This interface defines a handler of incoming HTTP Requests. It should +/// be exported by components which can respond to HTTP Requests. +@since(version = 0.2.0) +interface incoming-handler { + @since(version = 0.2.0) + use types.{incoming-request, response-outparam}; + + /// This function is invoked with an incoming HTTP Request, and a resource + /// `response-outparam` which provides the capability to reply with an HTTP + /// Response. The response is sent by calling the `response-outparam.set` + /// method, which allows execution to continue after the response has been + /// sent. This enables both streaming to the response body, and performing other + /// work. + /// + /// The implementor of this function must write a response to the + /// `response-outparam` before returning, or else the caller will respond + /// with an error on its behalf. + @since(version = 0.2.0) + handle: func(request: incoming-request, response-out: response-outparam); +} + +/// This interface defines a handler of outgoing HTTP Requests. It should be +/// imported by components which wish to make HTTP Requests. +@since(version = 0.2.0) +interface outgoing-handler { + @since(version = 0.2.0) + use types.{outgoing-request, request-options, future-incoming-response, error-code}; + + /// This function is invoked with an outgoing HTTP Request, and it returns + /// a resource `future-incoming-response` which represents an HTTP Response + /// which may arrive in the future. + /// + /// The `options` argument accepts optional parameters for the HTTP + /// protocol's transport layer. + /// + /// This function may return an error if the `outgoing-request` is invalid + /// or not allowed to be made. Otherwise, protocol errors are reported + /// through the `future-incoming-response`. + @since(version = 0.2.0) + handle: func(request: outgoing-request, options: option) -> result; +} + +/// The `wasi:http/imports` world imports all the APIs for HTTP proxies. +/// It is intended to be `include`d in other worlds. +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import wasi:io/poll@0.2.9; + @since(version = 0.2.0) + import wasi:clocks/monotonic-clock@0.2.9; + @since(version = 0.2.0) + import wasi:clocks/wall-clock@0.2.9; + @since(version = 0.2.0) + import wasi:random/random@0.2.9; + @since(version = 0.2.0) + import wasi:io/error@0.2.9; + @since(version = 0.2.0) + import wasi:io/streams@0.2.9; + @since(version = 0.2.0) + import wasi:cli/stdout@0.2.9; + @since(version = 0.2.0) + import wasi:cli/stderr@0.2.9; + @since(version = 0.2.0) + import wasi:cli/stdin@0.2.9; + @since(version = 0.2.0) + import types; + @since(version = 0.2.0) + import outgoing-handler; +} +/// The `wasi:http/proxy` world captures a widely-implementable intersection of +/// hosts that includes HTTP forward and reverse proxies. Components targeting +/// this world may concurrently stream in and out any number of incoming and +/// outgoing HTTP requests. +@since(version = 0.2.0) +world proxy { + @since(version = 0.2.0) + import wasi:io/poll@0.2.9; + @since(version = 0.2.0) + import wasi:clocks/monotonic-clock@0.2.9; + @since(version = 0.2.0) + import wasi:clocks/wall-clock@0.2.9; + @since(version = 0.2.0) + import wasi:random/random@0.2.9; + @since(version = 0.2.0) + import wasi:io/error@0.2.9; + @since(version = 0.2.0) + import wasi:io/streams@0.2.9; + @since(version = 0.2.0) + import wasi:cli/stdout@0.2.9; + @since(version = 0.2.0) + import wasi:cli/stderr@0.2.9; + @since(version = 0.2.0) + import wasi:cli/stdin@0.2.9; + @since(version = 0.2.0) + import types; + @since(version = 0.2.0) + import outgoing-handler; + + @since(version = 0.2.0) + export incoming-handler; +} diff --git a/tools/vendor/wasip2/wit/deps/io.wit b/tools/vendor/wasip2/wit/deps/io.wit new file mode 100644 index 0000000000..93bdfbe2e1 --- /dev/null +++ b/tools/vendor/wasip2/wit/deps/io.wit @@ -0,0 +1,299 @@ +package wasi:io@0.2.9; + +@since(version = 0.2.0) +interface error { + /// A resource which represents some error information. + /// + /// The only method provided by this resource is `to-debug-string`, + /// which provides some human-readable information about the error. + /// + /// In the `wasi:io` package, this resource is returned through the + /// `wasi:io/streams.stream-error` type. + /// + /// To provide more specific error information, other interfaces may + /// offer functions to "downcast" this error into more specific types. For example, + /// errors returned from streams derived from filesystem types can be described using + /// the filesystem's own error-code type. This is done using the function + /// `wasi:filesystem/types.filesystem-error-code`, which takes a `borrow` + /// parameter and returns an `option`. + /// + /// The set of functions which can "downcast" an `error` into a more + /// concrete type is open. + @since(version = 0.2.0) + resource error { + /// Returns a string that is suitable to assist humans in debugging + /// this error. + /// + /// WARNING: The returned string should not be consumed mechanically! + /// It may change across platforms, hosts, or other implementation + /// details. Parsing this string is a major platform-compatibility + /// hazard. + @since(version = 0.2.0) + to-debug-string: func() -> string; + } +} + +/// A poll API intended to let users wait for I/O events on multiple handles +/// at once. +@since(version = 0.2.0) +interface poll { + /// `pollable` represents a single I/O event which may be ready, or not. + @since(version = 0.2.0) + resource pollable { + /// Return the readiness of a pollable. This function never blocks. + /// + /// Returns `true` when the pollable is ready, and `false` otherwise. + @since(version = 0.2.0) + ready: func() -> bool; + /// `block` returns immediately if the pollable is ready, and otherwise + /// blocks until ready. + /// + /// This function is equivalent to calling `poll.poll` on a list + /// containing only this pollable. + @since(version = 0.2.0) + block: func(); + } + + /// Poll for completion on a set of pollables. + /// + /// This function takes a list of pollables, which identify I/O sources of + /// interest, and waits until one or more of the events is ready for I/O. + /// + /// The result `list` contains one or more indices of handles in the + /// argument list that is ready for I/O. + /// + /// This function traps if either: + /// - the list is empty, or: + /// - the list contains more elements than can be indexed with a `u32` value. + /// + /// A timeout can be implemented by adding a pollable from the + /// wasi-clocks API to the list. + /// + /// This function does not return a `result`; polling in itself does not + /// do any I/O so it doesn't fail. If any of the I/O sources identified by + /// the pollables has an error, it is indicated by marking the source as + /// being ready for I/O. + @since(version = 0.2.0) + poll: func(in: list>) -> list; +} + +/// WASI I/O is an I/O abstraction API which is currently focused on providing +/// stream types. +/// +/// In the future, the component model is expected to add built-in stream types; +/// when it does, they are expected to subsume this API. +@since(version = 0.2.0) +interface streams { + @since(version = 0.2.0) + use error.{error}; + @since(version = 0.2.0) + use poll.{pollable}; + + /// An error for input-stream and output-stream operations. + @since(version = 0.2.0) + variant stream-error { + /// The last operation (a write or flush) failed before completion. + /// + /// More information is available in the `error` payload. + /// + /// After this, the stream will be closed. All future operations return + /// `stream-error::closed`. + last-operation-failed(error), + /// The stream is closed: no more input will be accepted by the + /// stream. A closed output-stream will return this error on all + /// future operations. + closed, + } + + /// An input bytestream. + /// + /// `input-stream`s are *non-blocking* to the extent practical on underlying + /// platforms. I/O operations always return promptly; if fewer bytes are + /// promptly available than requested, they return the number of bytes promptly + /// available, which could even be zero. To wait for data to be available, + /// use the `subscribe` function to obtain a `pollable` which can be polled + /// for using `wasi:io/poll`. + @since(version = 0.2.0) + resource input-stream { + /// Perform a non-blocking read from the stream. + /// + /// When the source of a `read` is binary data, the bytes from the source + /// are returned verbatim. When the source of a `read` is known to the + /// implementation to be text, bytes containing the UTF-8 encoding of the + /// text are returned. + /// + /// This function returns a list of bytes containing the read data, + /// when successful. The returned list will contain up to `len` bytes; + /// it may return fewer than requested, but not more. The list is + /// empty when no bytes are available for reading at this time. The + /// pollable given by `subscribe` will be ready when more bytes are + /// available. + /// + /// This function fails with a `stream-error` when the operation + /// encounters an error, giving `last-operation-failed`, or when the + /// stream is closed, giving `closed`. + /// + /// When the caller gives a `len` of 0, it represents a request to + /// read 0 bytes. If the stream is still open, this call should + /// succeed and return an empty list, or otherwise fail with `closed`. + /// + /// The `len` parameter is a `u64`, which could represent a list of u8 which + /// is not possible to allocate in wasm32, or not desirable to allocate as + /// as a return value by the callee. The callee may return a list of bytes + /// less than `len` in size while more bytes are available for reading. + @since(version = 0.2.0) + read: func(len: u64) -> result, stream-error>; + /// Read bytes from a stream, after blocking until at least one byte can + /// be read. Except for blocking, behavior is identical to `read`. + @since(version = 0.2.0) + blocking-read: func(len: u64) -> result, stream-error>; + /// Skip bytes from a stream. Returns number of bytes skipped. + /// + /// Behaves identical to `read`, except instead of returning a list + /// of bytes, returns the number of bytes consumed from the stream. + @since(version = 0.2.0) + skip: func(len: u64) -> result; + /// Skip bytes from a stream, after blocking until at least one byte + /// can be skipped. Except for blocking behavior, identical to `skip`. + @since(version = 0.2.0) + blocking-skip: func(len: u64) -> result; + /// Create a `pollable` which will resolve once either the specified stream + /// has bytes available to read or the other end of the stream has been + /// closed. + /// The created `pollable` is a child resource of the `input-stream`. + /// Implementations may trap if the `input-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } + + /// An output bytestream. + /// + /// `output-stream`s are *non-blocking* to the extent practical on + /// underlying platforms. Except where specified otherwise, I/O operations also + /// always return promptly, after the number of bytes that can be written + /// promptly, which could even be zero. To wait for the stream to be ready to + /// accept data, the `subscribe` function to obtain a `pollable` which can be + /// polled for using `wasi:io/poll`. + /// + /// Dropping an `output-stream` while there's still an active write in + /// progress may result in the data being lost. Before dropping the stream, + /// be sure to fully flush your writes. + @since(version = 0.2.0) + resource output-stream { + /// Check readiness for writing. This function never blocks. + /// + /// Returns the number of bytes permitted for the next call to `write`, + /// or an error. Calling `write` with more bytes than this function has + /// permitted will trap. + /// + /// When this function returns 0 bytes, the `subscribe` pollable will + /// become ready when this function will report at least 1 byte, or an + /// error. + @since(version = 0.2.0) + check-write: func() -> result; + /// Perform a write. This function never blocks. + /// + /// When the destination of a `write` is binary data, the bytes from + /// `contents` are written verbatim. When the destination of a `write` is + /// known to the implementation to be text, the bytes of `contents` are + /// transcoded from UTF-8 into the encoding of the destination and then + /// written. + /// + /// Precondition: check-write gave permit of Ok(n) and contents has a + /// length of less than or equal to n. Otherwise, this function will trap. + /// + /// returns Err(closed) without writing if the stream has closed since + /// the last call to check-write provided a permit. + @since(version = 0.2.0) + write: func(contents: list) -> result<_, stream-error>; + /// Perform a write of up to 4096 bytes, and then flush the stream. Block + /// until all of these operations are complete, or an error occurs. + /// + /// Returns success when all of the contents written are successfully + /// flushed to output. If an error occurs at any point before all + /// contents are successfully flushed, that error is returned as soon as + /// possible. If writing and flushing the complete contents causes the + /// stream to become closed, this call should return success, and + /// subsequent calls to check-write or other interfaces should return + /// stream-error::closed. + @since(version = 0.2.0) + blocking-write-and-flush: func(contents: list) -> result<_, stream-error>; + /// Request to flush buffered output. This function never blocks. + /// + /// This tells the output-stream that the caller intends any buffered + /// output to be flushed. the output which is expected to be flushed + /// is all that has been passed to `write` prior to this call. + /// + /// Upon calling this function, the `output-stream` will not accept any + /// writes (`check-write` will return `ok(0)`) until the flush has + /// completed. The `subscribe` pollable will become ready when the + /// flush has completed and the stream can accept more writes. + @since(version = 0.2.0) + flush: func() -> result<_, stream-error>; + /// Request to flush buffered output, and block until flush completes + /// and stream is ready for writing again. + @since(version = 0.2.0) + blocking-flush: func() -> result<_, stream-error>; + /// Create a `pollable` which will resolve once the output-stream + /// is ready for more writing, or an error has occurred. When this + /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an + /// error. + /// + /// If the stream is closed, this pollable is always ready immediately. + /// + /// The created `pollable` is a child resource of the `output-stream`. + /// Implementations may trap if the `output-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + @since(version = 0.2.0) + subscribe: func() -> pollable; + /// Write zeroes to a stream. + /// + /// This should be used precisely like `write` with the exact same + /// preconditions (must use check-write first), but instead of + /// passing a list of bytes, you simply pass the number of zero-bytes + /// that should be written. + @since(version = 0.2.0) + write-zeroes: func(len: u64) -> result<_, stream-error>; + /// Perform a write of up to 4096 zeroes, and then flush the stream. + /// Block until all of these operations are complete, or an error + /// occurs. + /// + /// Functionality is equivelant to `blocking-write-and-flush` with + /// contents given as a list of len containing only zeroes. + @since(version = 0.2.0) + blocking-write-zeroes-and-flush: func(len: u64) -> result<_, stream-error>; + /// Read from one stream and write to another. + /// + /// The behavior of splice is equivalent to: + /// 1. calling `check-write` on the `output-stream` + /// 2. calling `read` on the `input-stream` with the smaller of the + /// `check-write` permitted length and the `len` provided to `splice` + /// 3. calling `write` on the `output-stream` with that read data. + /// + /// Any error reported by the call to `check-write`, `read`, or + /// `write` ends the splice and reports that error. + /// + /// This function returns the number of bytes transferred; it may be less + /// than `len`. + @since(version = 0.2.0) + splice: func(src: borrow, len: u64) -> result; + /// Read from one stream and write to another, with blocking. + /// + /// This is similar to `splice`, except that it blocks until the + /// `output-stream` is ready for writing, and the `input-stream` + /// is ready for reading, before performing the `splice`. + @since(version = 0.2.0) + blocking-splice: func(src: borrow, len: u64) -> result; + } +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import error; + @since(version = 0.2.0) + import poll; + @since(version = 0.2.0) + import streams; +} diff --git a/tools/vendor/wasip2/wit/deps/random.wit b/tools/vendor/wasip2/wit/deps/random.wit new file mode 100644 index 0000000000..c114cc1110 --- /dev/null +++ b/tools/vendor/wasip2/wit/deps/random.wit @@ -0,0 +1,92 @@ +package wasi:random@0.2.9; + +/// The insecure-seed interface for seeding hash-map DoS resistance. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +@since(version = 0.2.0) +interface insecure-seed { + /// Return a 128-bit value that may contain a pseudo-random value. + /// + /// The returned value is not required to be computed from a CSPRNG, and may + /// even be entirely deterministic. Host implementations are encouraged to + /// provide pseudo-random values to any program exposed to + /// attacker-controlled content, to enable DoS protection built into many + /// languages' hash-map implementations. + /// + /// This function is intended to only be called once, by a source language + /// to initialize Denial Of Service (DoS) protection in its hash-map + /// implementation. + /// + /// # Expected future evolution + /// + /// This will likely be changed to a value import, to prevent it from being + /// called multiple times and potentially used for purposes other than DoS + /// protection. + @since(version = 0.2.0) + insecure-seed: func() -> tuple; +} + +/// The insecure interface for insecure pseudo-random numbers. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +@since(version = 0.2.0) +interface insecure { + /// Return `len` insecure pseudo-random bytes. + /// + /// This function is not cryptographically secure. Do not use it for + /// anything related to security. + /// + /// There are no requirements on the values of the returned bytes, however + /// implementations are encouraged to return evenly distributed values with + /// a long period. + @since(version = 0.2.0) + get-insecure-random-bytes: func(len: u64) -> list; + + /// Return an insecure pseudo-random `u64` value. + /// + /// This function returns the same type of pseudo-random data as + /// `get-insecure-random-bytes`, represented as a `u64`. + @since(version = 0.2.0) + get-insecure-random-u64: func() -> u64; +} + +/// WASI Random is a random data API. +/// +/// It is intended to be portable at least between Unix-family platforms and +/// Windows. +@since(version = 0.2.0) +interface random { + /// Return `len` cryptographically-secure random or pseudo-random bytes. + /// + /// This function must produce data at least as cryptographically secure and + /// fast as an adequately seeded cryptographically-secure pseudo-random + /// number generator (CSPRNG). It must not block, from the perspective of + /// the calling program, under any circumstances, including on the first + /// request and on requests for numbers of bytes. The returned data must + /// always be unpredictable. + /// + /// This function must always return fresh data. Deterministic environments + /// must omit this function, rather than implementing it with deterministic + /// data. + @since(version = 0.2.0) + get-random-bytes: func(len: u64) -> list; + + /// Return a cryptographically-secure random or pseudo-random `u64` value. + /// + /// This function returns the same type of data as `get-random-bytes`, + /// represented as a `u64`. + @since(version = 0.2.0) + get-random-u64: func() -> u64; +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import random; + @since(version = 0.2.0) + import insecure; + @since(version = 0.2.0) + import insecure-seed; +} diff --git a/tools/vendor/wasip2/wit/deps/sockets.wit b/tools/vendor/wasip2/wit/deps/sockets.wit new file mode 100644 index 0000000000..6c8ec26110 --- /dev/null +++ b/tools/vendor/wasip2/wit/deps/sockets.wit @@ -0,0 +1,949 @@ +package wasi:sockets@0.2.9; + +@since(version = 0.2.0) +interface network { + @unstable(feature = network-error-code) + use wasi:io/error@0.2.9.{error}; + + /// An opaque resource that represents access to (a subset of) the network. + /// This enables context-based security for networking. + /// There is no need for this to map 1:1 to a physical network interface. + @since(version = 0.2.0) + resource network; + + /// Error codes. + /// + /// In theory, every API can return any error code. + /// In practice, API's typically only return the errors documented per API + /// combined with a couple of errors that are always possible: + /// - `unknown` + /// - `access-denied` + /// - `not-supported` + /// - `out-of-memory` + /// - `concurrency-conflict` + /// + /// See each individual API for what the POSIX equivalents are. They sometimes differ per API. + @since(version = 0.2.0) + enum error-code { + /// Unknown error + unknown, + /// Access denied. + /// + /// POSIX equivalent: EACCES, EPERM + access-denied, + /// The operation is not supported. + /// + /// POSIX equivalent: EOPNOTSUPP + not-supported, + /// One of the arguments is invalid. + /// + /// POSIX equivalent: EINVAL + invalid-argument, + /// Not enough memory to complete the operation. + /// + /// POSIX equivalent: ENOMEM, ENOBUFS, EAI_MEMORY + out-of-memory, + /// The operation timed out before it could finish completely. + timeout, + /// This operation is incompatible with another asynchronous operation that is already in progress. + /// + /// POSIX equivalent: EALREADY + concurrency-conflict, + /// Trying to finish an asynchronous operation that: + /// - has not been started yet, or: + /// - was already finished by a previous `finish-*` call. + /// + /// Note: this is scheduled to be removed when `future`s are natively supported. + not-in-progress, + /// The operation has been aborted because it could not be completed immediately. + /// + /// Note: this is scheduled to be removed when `future`s are natively supported. + would-block, + /// The operation is not valid in the socket's current state. + invalid-state, + /// A new socket resource could not be created because of a system limit. + new-socket-limit, + /// A bind operation failed because the provided address is not an address that the `network` can bind to. + address-not-bindable, + /// A bind operation failed because the provided address is already in use or because there are no ephemeral ports available. + address-in-use, + /// The remote address is not reachable + remote-unreachable, + /// The TCP connection was forcefully rejected + connection-refused, + /// The TCP connection was reset. + connection-reset, + /// A TCP connection was aborted. + connection-aborted, + /// The size of a datagram sent to a UDP socket exceeded the maximum + /// supported size. + datagram-too-large, + /// Name does not exist or has no suitable associated IP addresses. + name-unresolvable, + /// A temporary failure in name resolution occurred. + temporary-resolver-failure, + /// A permanent failure in name resolution occurred. + permanent-resolver-failure, + } + + @since(version = 0.2.0) + enum ip-address-family { + /// Similar to `AF_INET` in POSIX. + ipv4, + /// Similar to `AF_INET6` in POSIX. + ipv6, + } + + @since(version = 0.2.0) + type ipv4-address = tuple; + + @since(version = 0.2.0) + type ipv6-address = tuple; + + @since(version = 0.2.0) + variant ip-address { + ipv4(ipv4-address), + ipv6(ipv6-address), + } + + @since(version = 0.2.0) + record ipv4-socket-address { + /// sin_port + port: u16, + /// sin_addr + address: ipv4-address, + } + + @since(version = 0.2.0) + record ipv6-socket-address { + /// sin6_port + port: u16, + /// sin6_flowinfo + flow-info: u32, + /// sin6_addr + address: ipv6-address, + /// sin6_scope_id + scope-id: u32, + } + + @since(version = 0.2.0) + variant ip-socket-address { + ipv4(ipv4-socket-address), + ipv6(ipv6-socket-address), + } + + /// Attempts to extract a network-related `error-code` from the stream + /// `error` provided. + /// + /// Stream operations which return `stream-error::last-operation-failed` + /// have a payload with more information about the operation that failed. + /// This payload can be passed through to this function to see if there's + /// network-related information about the error to return. + /// + /// Note that this function is fallible because not all stream-related + /// errors are network-related errors. + @unstable(feature = network-error-code) + network-error-code: func(err: borrow) -> option; +} + +/// This interface provides a value-export of the default network handle.. +@since(version = 0.2.0) +interface instance-network { + @since(version = 0.2.0) + use network.{network}; + + /// Get a handle to the default network. + @since(version = 0.2.0) + instance-network: func() -> network; +} + +@since(version = 0.2.0) +interface ip-name-lookup { + @since(version = 0.2.0) + use wasi:io/poll@0.2.9.{pollable}; + @since(version = 0.2.0) + use network.{network, error-code, ip-address}; + + @since(version = 0.2.0) + resource resolve-address-stream { + /// Returns the next address from the resolver. + /// + /// This function should be called multiple times. On each call, it will + /// return the next address in connection order preference. If all + /// addresses have been exhausted, this function returns `none`. + /// + /// This function never returns IPv4-mapped IPv6 addresses. + /// + /// # Typical errors + /// - `name-unresolvable`: Name does not exist or has no suitable associated IP addresses. (EAI_NONAME, EAI_NODATA, EAI_ADDRFAMILY) + /// - `temporary-resolver-failure`: A temporary failure in name resolution occurred. (EAI_AGAIN) + /// - `permanent-resolver-failure`: A permanent failure in name resolution occurred. (EAI_FAIL) + /// - `would-block`: A result is not available yet. (EWOULDBLOCK, EAGAIN) + @since(version = 0.2.0) + resolve-next-address: func() -> result, error-code>; + /// Create a `pollable` which will resolve once the stream is ready for I/O. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } + + /// Resolve an internet host name to a list of IP addresses. + /// + /// Unicode domain names are automatically converted to ASCII using IDNA encoding. + /// If the input is an IP address string, the address is parsed and returned + /// as-is without making any external requests. + /// + /// See the wasi-socket proposal README.md for a comparison with getaddrinfo. + /// + /// This function never blocks. It either immediately fails or immediately + /// returns successfully with a `resolve-address-stream` that can be used + /// to (asynchronously) fetch the results. + /// + /// # Typical errors + /// - `invalid-argument`: `name` is a syntactically invalid domain name or IP address. + /// + /// # References: + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + resolve-addresses: func(network: borrow, name: string) -> result; +} + +@since(version = 0.2.0) +interface tcp { + @since(version = 0.2.0) + use wasi:io/streams@0.2.9.{input-stream, output-stream}; + @since(version = 0.2.0) + use wasi:io/poll@0.2.9.{pollable}; + @since(version = 0.2.0) + use wasi:clocks/monotonic-clock@0.2.9.{duration}; + @since(version = 0.2.0) + use network.{network, error-code, ip-socket-address, ip-address-family}; + + @since(version = 0.2.0) + enum shutdown-type { + /// Similar to `SHUT_RD` in POSIX. + receive, + /// Similar to `SHUT_WR` in POSIX. + send, + /// Similar to `SHUT_RDWR` in POSIX. + both, + } + + /// A TCP socket resource. + /// + /// The socket can be in one of the following states: + /// - `unbound` + /// - `bind-in-progress` + /// - `bound` (See note below) + /// - `listen-in-progress` + /// - `listening` + /// - `connect-in-progress` + /// - `connected` + /// - `closed` + /// See + /// for more information. + /// + /// Note: Except where explicitly mentioned, whenever this documentation uses + /// the term "bound" without backticks it actually means: in the `bound` state *or higher*. + /// (i.e. `bound`, `listen-in-progress`, `listening`, `connect-in-progress` or `connected`) + /// + /// In addition to the general error codes documented on the + /// `network::error-code` type, TCP socket methods may always return + /// `error(invalid-state)` when in the `closed` state. + @since(version = 0.2.0) + resource tcp-socket { + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the TCP/UDP port is zero, the socket will be bound to a random free port. + /// + /// Bind can be attempted multiple times on the same socket, even with + /// different arguments on each iteration. But never concurrently and + /// only as long as the previous bind failed. Once a bind succeeds, the + /// binding can't be changed anymore. + /// + /// # Typical errors + /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) + /// - `invalid-argument`: `local-address` is not a unicast address. (EINVAL) + /// - `invalid-argument`: `local-address` is an IPv4-mapped IPv6 address. (EINVAL) + /// - `invalid-state`: The socket is already bound. (EINVAL) + /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// When binding to a non-zero port, this bind operation shouldn't be affected by the TIME_WAIT + /// state of a recently closed socket on the same local address. In practice this means that the SO_REUSEADDR + /// socket option should be set implicitly on all platforms, except on Windows where this is the default behavior + /// and SO_REUSEADDR performs something different entirely. + /// + /// Unlike in POSIX, in WASI the bind operation is async. This enables + /// interactive WASI hosts to inject permission prompts. Runtimes that + /// don't want to make use of this ability can simply call the native + /// `bind` as part of either `start-bind` or `finish-bind`. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + @since(version = 0.2.0) + finish-bind: func() -> result<_, error-code>; + /// Connect to a remote endpoint. + /// + /// On success: + /// - the socket is transitioned into the `connected` state. + /// - a pair of streams is returned that can be used to read & write to the connection + /// + /// After a failed connection attempt, the socket will be in the `closed` + /// state and the only valid action left is to `drop` the socket. A single + /// socket can not be used to connect more than once. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: `remote-address` is not a unicast address. (EINVAL, ENETUNREACH on Linux, EAFNOSUPPORT on MacOS) + /// - `invalid-argument`: `remote-address` is an IPv4-mapped IPv6 address. (EINVAL, EADDRNOTAVAIL on Illumos) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EADDRNOTAVAIL on Windows) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EADDRNOTAVAIL on Windows) + /// - `invalid-argument`: The socket is already attached to a different network. The `network` passed to `connect` must be identical to the one passed to `bind`. + /// - `invalid-state`: The socket is already in the `connected` state. (EISCONN) + /// - `invalid-state`: The socket is already in the `listening` state. (EOPNOTSUPP, EINVAL on Windows) + /// - `timeout`: Connection timed out. (ETIMEDOUT) + /// - `connection-refused`: The connection was forcefully rejected. (ECONNREFUSED) + /// - `connection-reset`: The connection was reset. (ECONNRESET) + /// - `connection-aborted`: The connection was aborted. (ECONNABORTED) + /// - `remote-unreachable`: The remote address is not reachable. (EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `not-in-progress`: A connect operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// The POSIX equivalent of `start-connect` is the regular `connect` syscall. + /// Because all WASI sockets are non-blocking this is expected to return + /// EINPROGRESS, which should be translated to `ok()` in WASI. + /// + /// The POSIX equivalent of `finish-connect` is a `poll` for event `POLLOUT` + /// with a timeout of 0 on the socket descriptor. Followed by a check for + /// the `SO_ERROR` socket option, in case the poll signaled readiness. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code>; + @since(version = 0.2.0) + finish-connect: func() -> result, error-code>; + /// Start listening for new connections. + /// + /// Transitions the socket into the `listening` state. + /// + /// Unlike POSIX, the socket must already be explicitly bound. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. (EDESTADDRREQ) + /// - `invalid-state`: The socket is already in the `connected` state. (EISCONN, EINVAL on BSD) + /// - `invalid-state`: The socket is already in the `listening` state. + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE) + /// - `not-in-progress`: A listen operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// Unlike in POSIX, in WASI the listen operation is async. This enables + /// interactive WASI hosts to inject permission prompts. Runtimes that + /// don't want to make use of this ability can simply call the native + /// `listen` as part of either `start-listen` or `finish-listen`. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + start-listen: func() -> result<_, error-code>; + @since(version = 0.2.0) + finish-listen: func() -> result<_, error-code>; + /// Accept a new client socket. + /// + /// The returned socket is bound and in the `connected` state. The following properties are inherited from the listener socket: + /// - `address-family` + /// - `keep-alive-enabled` + /// - `keep-alive-idle-time` + /// - `keep-alive-interval` + /// - `keep-alive-count` + /// - `hop-limit` + /// - `receive-buffer-size` + /// - `send-buffer-size` + /// + /// On success, this function returns the newly accepted client socket along with + /// a pair of streams that can be used to read & write to the connection. + /// + /// # Typical errors + /// - `invalid-state`: Socket is not in the `listening` state. (EINVAL) + /// - `would-block`: No pending connections at the moment. (EWOULDBLOCK, EAGAIN) + /// - `connection-aborted`: An incoming connection was pending, but was terminated by the client before this listener could accept it. (ECONNABORTED) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + accept: func() -> result, error-code>; + /// Get the bound local address. + /// + /// POSIX mentions: + /// > If the socket has not been bound to a local name, the value + /// > stored in the object pointed to by `address` is unspecified. + /// + /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + local-address: func() -> result; + /// Get the remote address. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + remote-address: func() -> result; + /// Whether the socket is in the `listening` state. + /// + /// Equivalent to the SO_ACCEPTCONN socket option. + @since(version = 0.2.0) + is-listening: func() -> bool; + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + @since(version = 0.2.0) + address-family: func() -> ip-address-family; + /// Hints the desired listen queue size. Implementations are free to ignore this. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// + /// # Typical errors + /// - `not-supported`: (set) The platform does not support changing the backlog size after the initial listen. + /// - `invalid-argument`: (set) The provided value was 0. + /// - `invalid-state`: (set) The socket is in the `connect-in-progress` or `connected` state. + @since(version = 0.2.0) + set-listen-backlog-size: func(value: u64) -> result<_, error-code>; + /// Enables or disables keepalive. + /// + /// The keepalive behavior can be adjusted using: + /// - `keep-alive-idle-time` + /// - `keep-alive-interval` + /// - `keep-alive-count` + /// These properties can be configured while `keep-alive-enabled` is false, but only come into effect when `keep-alive-enabled` is true. + /// + /// Equivalent to the SO_KEEPALIVE socket option. + @since(version = 0.2.0) + keep-alive-enabled: func() -> result; + @since(version = 0.2.0) + set-keep-alive-enabled: func(value: bool) -> result<_, error-code>; + /// Amount of time the connection has to be idle before TCP starts sending keepalive packets. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPIDLE socket option. (TCP_KEEPALIVE on MacOS) + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.2.0) + keep-alive-idle-time: func() -> result; + @since(version = 0.2.0) + set-keep-alive-idle-time: func(value: duration) -> result<_, error-code>; + /// The time between keepalive packets. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPINTVL socket option. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.2.0) + keep-alive-interval: func() -> result; + @since(version = 0.2.0) + set-keep-alive-interval: func(value: duration) -> result<_, error-code>; + /// The maximum amount of keepalive packets TCP should send before aborting the connection. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the TCP_KEEPCNT socket option. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.2.0) + keep-alive-count: func() -> result; + @since(version = 0.2.0) + set-keep-alive-count: func(value: u32) -> result<_, error-code>; + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The TTL value must be 1 or higher. + @since(version = 0.2.0) + hop-limit: func() -> result; + @since(version = 0.2.0) + set-hop-limit: func(value: u8) -> result<_, error-code>; + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.2.0) + receive-buffer-size: func() -> result; + @since(version = 0.2.0) + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + @since(version = 0.2.0) + send-buffer-size: func() -> result; + @since(version = 0.2.0) + set-send-buffer-size: func(value: u64) -> result<_, error-code>; + /// Create a `pollable` which can be used to poll for, or block on, + /// completion of any of the asynchronous operations of this socket. + /// + /// When `finish-bind`, `finish-listen`, `finish-connect` or `accept` + /// return `error(would-block)`, this pollable can be used to wait for + /// their success or failure, after which the method can be retried. + /// + /// The pollable is not limited to the async operation that happens to be + /// in progress at the time of calling `subscribe` (if any). Theoretically, + /// `subscribe` only has to be called once per socket and can then be + /// (re)used for the remainder of the socket's lifetime. + /// + /// See + /// for more information. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + @since(version = 0.2.0) + subscribe: func() -> pollable; + /// Initiate a graceful shutdown. + /// + /// - `receive`: The socket is not expecting to receive any data from + /// the peer. The `input-stream` associated with this socket will be + /// closed. Any data still in the receive queue at time of calling + /// this method will be discarded. + /// - `send`: The socket has no more data to send to the peer. The `output-stream` + /// associated with this socket will be closed and a FIN packet will be sent. + /// - `both`: Same effect as `receive` & `send` combined. + /// + /// This function is idempotent; shutting down a direction more than once + /// has no effect and returns `ok`. + /// + /// The shutdown function does not close (drop) the socket. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not in the `connected` state. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + shutdown: func(shutdown-type: shutdown-type) -> result<_, error-code>; + } +} + +@since(version = 0.2.0) +interface tcp-create-socket { + @since(version = 0.2.0) + use network.{network, error-code, ip-address-family}; + @since(version = 0.2.0) + use tcp.{tcp-socket}; + + /// Create a new TCP socket. + /// + /// Similar to `socket(AF_INET or AF_INET6, SOCK_STREAM, IPPROTO_TCP)` in POSIX. + /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// + /// This function does not require a network capability handle. This is considered to be safe because + /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind`/`connect` + /// is called, the socket is effectively an in-memory configuration object, unable to communicate with the outside world. + /// + /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + /// + /// # Typical errors + /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + create-tcp-socket: func(address-family: ip-address-family) -> result; +} + +@since(version = 0.2.0) +interface udp { + @since(version = 0.2.0) + use wasi:io/poll@0.2.9.{pollable}; + @since(version = 0.2.0) + use network.{network, error-code, ip-socket-address, ip-address-family}; + + /// A received datagram. + @since(version = 0.2.0) + record incoming-datagram { + /// The payload. + /// + /// Theoretical max size: ~64 KiB. In practice, typically less than 1500 bytes. + data: list, + /// The source address. + /// + /// This field is guaranteed to match the remote address the stream was initialized with, if any. + /// + /// Equivalent to the `src_addr` out parameter of `recvfrom`. + remote-address: ip-socket-address, + } + + /// A datagram to be sent out. + @since(version = 0.2.0) + record outgoing-datagram { + /// The payload. + data: list, + /// The destination address. + /// + /// The requirements on this field depend on how the stream was initialized: + /// - with a remote address: this field must be None or match the stream's remote address exactly. + /// - without a remote address: this field is required. + /// + /// If this value is None, the send operation is equivalent to `send` in POSIX. Otherwise it is equivalent to `sendto`. + remote-address: option, + } + + /// A UDP socket handle. + @since(version = 0.2.0) + resource udp-socket { + /// Bind the socket to a specific network on the provided IP address and port. + /// + /// If the IP address is zero (`0.0.0.0` in IPv4, `::` in IPv6), it is left to the implementation to decide which + /// network interface(s) to bind to. + /// If the port is zero, the socket will be bound to a random free port. + /// + /// # Typical errors + /// - `invalid-argument`: The `local-address` has the wrong address family. (EAFNOSUPPORT, EFAULT on Windows) + /// - `invalid-state`: The socket is already bound. (EINVAL) + /// - `address-in-use`: No ephemeral ports available. (EADDRINUSE, ENOBUFS on Windows) + /// - `address-in-use`: Address is already in use. (EADDRINUSE) + /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) + /// - `not-in-progress`: A `bind` operation is not in progress. + /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// Unlike in POSIX, in WASI the bind operation is async. This enables + /// interactive WASI hosts to inject permission prompts. Runtimes that + /// don't want to make use of this ability can simply call the native + /// `bind` as part of either `start-bind` or `finish-bind`. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + start-bind: func(network: borrow, local-address: ip-socket-address) -> result<_, error-code>; + @since(version = 0.2.0) + finish-bind: func() -> result<_, error-code>; + /// Set up inbound & outbound communication channels, optionally to a specific peer. + /// + /// This function only changes the local socket configuration and does not generate any network traffic. + /// On success, the `remote-address` of the socket is updated. The `local-address` may be updated as well, + /// based on the best network path to `remote-address`. + /// + /// When a `remote-address` is provided, the returned streams are limited to communicating with that specific peer: + /// - `send` can only be used to send to this destination. + /// - `receive` will only return datagrams sent from the provided `remote-address`. + /// + /// This method may be called multiple times on the same socket to change its association, but + /// only the most recently returned pair of streams will be operational. Implementations may trap if + /// the streams returned by a previous invocation haven't been dropped yet before calling `stream` again. + /// + /// The POSIX equivalent in pseudo-code is: + /// ```text + /// if (was previously connected) { + /// connect(s, AF_UNSPEC) + /// } + /// if (remote_address is Some) { + /// connect(s, remote_address) + /// } + /// ``` + /// + /// Unlike in POSIX, the socket must already be explicitly bound. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-state`: The socket is not bound. + /// - `address-in-use`: Tried to perform an implicit bind, but there were no ephemeral ports available. (EADDRINUSE, EADDRNOTAVAIL on Linux, EAGAIN on BSD) + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + %stream: func(remote-address: option) -> result, error-code>; + /// Get the current bound address. + /// + /// POSIX mentions: + /// > If the socket has not been bound to a local name, the value + /// > stored in the object pointed to by `address` is unspecified. + /// + /// WASI is stricter and requires `local-address` to return `invalid-state` when the socket hasn't been bound yet. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not bound to any local address. + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + local-address: func() -> result; + /// Get the address the socket is currently streaming to. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not streaming to a specific remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + remote-address: func() -> result; + /// Whether this is a IPv4 or IPv6 socket. + /// + /// Equivalent to the SO_DOMAIN socket option. + @since(version = 0.2.0) + address-family: func() -> ip-address-family; + /// Equivalent to the IP_TTL & IPV6_UNICAST_HOPS socket options. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The TTL value must be 1 or higher. + @since(version = 0.2.0) + unicast-hop-limit: func() -> result; + @since(version = 0.2.0) + set-unicast-hop-limit: func(value: u8) -> result<_, error-code>; + /// The kernel buffer space reserved for sends/receives on this socket. + /// + /// If the provided value is 0, an `invalid-argument` error is returned. + /// Any other value will never cause an error, but it might be silently clamped and/or rounded. + /// I.e. after setting a value, reading the same setting back may return a different value. + /// + /// Equivalent to the SO_RCVBUF and SO_SNDBUF socket options. + /// + /// # Typical errors + /// - `invalid-argument`: (set) The provided value was 0. + @since(version = 0.2.0) + receive-buffer-size: func() -> result; + @since(version = 0.2.0) + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + @since(version = 0.2.0) + send-buffer-size: func() -> result; + @since(version = 0.2.0) + set-send-buffer-size: func(value: u64) -> result<_, error-code>; + /// Create a `pollable` which will resolve once the socket is ready for I/O. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } + + @since(version = 0.2.0) + resource incoming-datagram-stream { + /// Receive messages on the socket. + /// + /// This function attempts to receive up to `max-results` datagrams on the socket without blocking. + /// The returned list may contain fewer elements than requested, but never more. + /// + /// This function returns successfully with an empty list when either: + /// - `max-results` is 0, or: + /// - `max-results` is greater than 0, but no results are immediately available. + /// This function never returns `error(would-block)`. + /// + /// # Typical errors + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + receive: func(max-results: u64) -> result, error-code>; + /// Create a `pollable` which will resolve once the stream is ready to receive again. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } + + @since(version = 0.2.0) + resource outgoing-datagram-stream { + /// Check readiness for sending. This function never blocks. + /// + /// Returns the number of datagrams permitted for the next call to `send`, + /// or an error. Calling `send` with more datagrams than this function has + /// permitted will trap. + /// + /// When this function returns ok(0), the `subscribe` pollable will + /// become ready when this function will report at least ok(1), or an + /// error. + /// + /// Never returns `would-block`. + check-send: func() -> result; + /// Send messages on the socket. + /// + /// This function attempts to send all provided `datagrams` on the socket without blocking and + /// returns how many messages were actually sent (or queued for sending). This function never + /// returns `error(would-block)`. If none of the datagrams were able to be sent, `ok(0)` is returned. + /// + /// This function semantically behaves the same as iterating the `datagrams` list and sequentially + /// sending each individual datagram until either the end of the list has been reached or the first error occurred. + /// If at least one datagram has been sent successfully, this function never returns an error. + /// + /// If the input list is empty, the function returns `ok(0)`. + /// + /// Each call to `send` must be permitted by a preceding `check-send`. Implementations must trap if + /// either `check-send` was not called or `datagrams` contains more items than `check-send` permitted. + /// + /// # Typical errors + /// - `invalid-argument`: The `remote-address` has the wrong address family. (EAFNOSUPPORT) + /// - `invalid-argument`: The IP address in `remote-address` is set to INADDR_ANY (`0.0.0.0` / `::`). (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The port in `remote-address` is set to 0. (EDESTADDRREQ, EADDRNOTAVAIL) + /// - `invalid-argument`: The socket is in "connected" mode and `remote-address` is `some` value that does not match the address passed to `stream`. (EISCONN) + /// - `invalid-argument`: The socket is not "connected" and no value for `remote-address` was provided. (EDESTADDRREQ) + /// - `remote-unreachable`: The remote address is not reachable. (ECONNRESET, ENETRESET on Windows, EHOSTUNREACH, EHOSTDOWN, ENETUNREACH, ENETDOWN, ENONET) + /// - `connection-refused`: The connection was refused. (ECONNREFUSED) + /// - `datagram-too-large`: The datagram is too large. (EMSGSIZE) + /// + /// # References + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + send: func(datagrams: list) -> result; + /// Create a `pollable` which will resolve once the stream is ready to send again. + /// + /// Note: this function is here for WASI 0.2 only. + /// It's planned to be removed when `future` is natively supported in Preview3. + @since(version = 0.2.0) + subscribe: func() -> pollable; + } +} + +@since(version = 0.2.0) +interface udp-create-socket { + @since(version = 0.2.0) + use network.{network, error-code, ip-address-family}; + @since(version = 0.2.0) + use udp.{udp-socket}; + + /// Create a new UDP socket. + /// + /// Similar to `socket(AF_INET or AF_INET6, SOCK_DGRAM, IPPROTO_UDP)` in POSIX. + /// On IPv6 sockets, IPV6_V6ONLY is enabled by default and can't be configured otherwise. + /// + /// This function does not require a network capability handle. This is considered to be safe because + /// at time of creation, the socket is not bound to any `network` yet. Up to the moment `bind` is called, + /// the socket is effectively an in-memory configuration object, unable to communicate with the outside world. + /// + /// All sockets are non-blocking. Use the wasi-poll interface to block on asynchronous operations. + /// + /// # Typical errors + /// - `not-supported`: The specified `address-family` is not supported. (EAFNOSUPPORT) + /// - `new-socket-limit`: The new socket resource could not be created because of a system limit. (EMFILE, ENFILE) + /// + /// # References: + /// - + /// - + /// - + /// - + @since(version = 0.2.0) + create-udp-socket: func(address-family: ip-address-family) -> result; +} + +@since(version = 0.2.0) +world imports { + @since(version = 0.2.0) + import wasi:io/error@0.2.9; + @since(version = 0.2.0) + import network; + @since(version = 0.2.0) + import instance-network; + @since(version = 0.2.0) + import wasi:io/poll@0.2.9; + @since(version = 0.2.0) + import udp; + @since(version = 0.2.0) + import udp-create-socket; + @since(version = 0.2.0) + import wasi:io/streams@0.2.9; + @since(version = 0.2.0) + import wasi:clocks/monotonic-clock@0.2.9; + @since(version = 0.2.0) + import tcp; + @since(version = 0.2.0) + import tcp-create-socket; + @since(version = 0.2.0) + import ip-name-lookup; +} diff --git a/tools/vendor/wasip2/wit/wasi-crate.wit b/tools/vendor/wasip2/wit/wasi-crate.wit new file mode 100644 index 0000000000..7ff44fe8eb --- /dev/null +++ b/tools/vendor/wasip2/wit/wasi-crate.wit @@ -0,0 +1 @@ +package rust:wasi; diff --git a/tools/vendor/wit-bindgen/.cargo-checksum.json b/tools/vendor/wit-bindgen/.cargo-checksum.json new file mode 100644 index 0000000000..5c6ad664ea --- /dev/null +++ b/tools/vendor/wit-bindgen/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"656a1645bc40f24d324b407612dadb5bad5980028cdf6c2c864fbc31d741a631","Cargo.lock":"4d46d08d4e6e1f470fce63ac5801006d4a6540e3196855a54ffe329c50df00bd","Cargo.toml":"d1e8b402a3d89bdba12e665298231871b96b3d22f374265145ad51d9e442b461","Cargo.toml.orig":"8fc9ed97d4bc560626ce05666d4f38f608acb2d5babfddb12bbea475b71a9771","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-Apache-2.0_WITH_LLVM-exception":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"4473d64adb6b173923f3acfd525b81aab1912dc0bc625a3643c132d7a8be3079","build.rs":"99b4276049457d3ea1d304277d66b0ff1abba0b9b1f5d60a5e25bdd3fd897a91","src/examples.rs":"c7329d7518ddcd42436d62fc4ffcb552d9920c95f3483b6426f0921268866665","src/examples/_0_world_imports.rs":"99d00cb52b5b12bdbafa1e5834e2d4f5e7f7b462a06a07e40becd18b01cd8ded","src/examples/_1_interface_imports.rs":"5cc5031ec71e1715efcd955a512ead215949ce0a8b2890d995fda7df2c3095b2","src/examples/_2_imported_resources.rs":"b29ca6f519cba51706e3ef201705a0daf8d4347d47a54b4780603fc7322d9d60","src/examples/_3_world_exports.rs":"1e4ddab02890bacc2793fa2f1dd7137e39bbb6f56e855c9a9232336915caf37d","src/examples/_4_exported_resources.rs":"68b5d95a370a89cceff82a54915d15d9c848da01d3bff65077508766f18afe93","src/lib.rs":"669a30455de2df9ef9b15e820bc5442288c8e5bb9f75a191a62331f103562366","src/rt/async_support.rs":"7b40f0812300d10ad3fb818dff41ac69c75dc1620bdd6a6eee25d29ff474ccb6","src/rt/async_support/abi_buffer.rs":"046a4055a01c1fd7aa0576ffcc86d265af33ae823c3e4624a15c12a6138ae65c","src/rt/async_support/cabi.rs":"2cadb038c049c3e9b6d2abeb2799f6285fed86d934c7a4adc2bf63c8406cc27f","src/rt/async_support/error_context.rs":"e24eec41b6c50062961e44c0f950303697a37090646510c557b3300402ba78bb","src/rt/async_support/future_support.rs":"d1d3dd34cca9bce2560d03076f40846aa0e08b69a405435bbd0c98887377258e","src/rt/async_support/inter_task_wakeup.rs":"dc21c22ae1042669005f95124d4c5e5b727f3551e3b8f76a563b637861a6c6bc","src/rt/async_support/inter_task_wakeup_disabled.rs":"9c2acc728e8606b4bb10f871d56bcefad3a2c9c8ec280ab4ebfbd9533aef624b","src/rt/async_support/spawn.rs":"54a779263daca4c89f5006fee125bb9d2f45d2d1730359883df9f823db2114ef","src/rt/async_support/spawn_disabled.rs":"90c3aa119661229f8ac3f53271e60cad9c26ca3a9e9b68664ef8273f66431069","src/rt/async_support/stream_support.rs":"236489f062956befb7c9a71668cc58d2ff76575d2ef17bca496fbd39bb03f599","src/rt/async_support/subtask.rs":"a1815f4106cc52258274aab8ecc233f31676bce7199dd36d0dbcdb588ee1b2d9","src/rt/async_support/unit_stream.rs":"a1349d76cb4bea582950208ea60f337fe240fce158df0cb2b2720381660f4a43","src/rt/async_support/waitable.rs":"813a89192d5e8c6e6f60dd19b02ab615964a9f668f906f46d2ce39b4f6474335","src/rt/async_support/waitable_set.rs":"ac0518cd7dbd41b3530fa7fd55079c6586292dde02a99869d6ed9f4893a1e1ab","src/rt/libwit_bindgen_cabi.a":"85b1573335b014c7ef2ad77001ffba5838d88176e6a3ed94c728b2e8cef4794e","src/rt/mod.rs":"2eba798e051b9d8bc537db377967c7fa4e81fde5788579efddd223e593cf9076","src/rt/wit_bindgen_cabi_realloc.c":"e5d0f04ca8b8a157d6c4e9689a8116eba304232c37e0638124d997f1673e3629","src/rt/wit_bindgen_cabi_realloc.o":"6cfaf4aa9ea6010f1772da34339efc44b4a76a72ac5def2ee73a71030edc098c","src/rt/wit_bindgen_cabi_realloc.rs":"91405ab4c143f23760c9e71e0a845065d9be3afbdb147a94cad841ca84153a95","src/rt/wit_bindgen_cabi_wasip3.c":"ddb8eedcfdd53c07382d005f7d75bd7d7513c6c647814cab01a5128c2ac6a94e","src/rt/wit_bindgen_cabi_wasip3.o":"76a7b6bf1529c2657f0e7620a443db23294647b009916df1b3b55646879ef61a","wasi-cli@0.2.0.wasm":"60b274319c9dce183269a0e3ad922b8ee4ead915f34d171de9da23a390863119"},"package":"d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"} \ No newline at end of file diff --git a/tools/vendor/wit-bindgen/.cargo_vcs_info.json b/tools/vendor/wit-bindgen/.cargo_vcs_info.json new file mode 100644 index 0000000000..9cb29f85b9 --- /dev/null +++ b/tools/vendor/wit-bindgen/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "0c39eee9f23d0530ed81bdf68bdd91fcc09ae08f" + }, + "path_in_vcs": "crates/guest-rust" +} \ No newline at end of file diff --git a/tools/vendor/wit-bindgen/Cargo.lock b/tools/vendor/wit-bindgen/Cargo.lock new file mode 100644 index 0000000000..52af04e4e6 --- /dev/null +++ b/tools/vendor/wit-bindgen/Cargo.lock @@ -0,0 +1,438 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-std-workspace-alloc" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d441c3b2ebf55cebf796bfdc265d67fa09db17b7bb6bd4be75c509e1e8fec3" + +[[package]] +name = "rustc-std-workspace-core" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9c45b374136f52f2d6311062c7146bff20fec063c3f5d46a410bd937746955" + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "syn" +version = "2.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +dependencies = [ + "bitflags", + "futures", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "zmij" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc5a66a20078bf1251bde995aa2fdcc4b800c70b5d92dd2c62abc5c60f679f8" diff --git a/tools/vendor/wit-bindgen/Cargo.toml b/tools/vendor/wit-bindgen/Cargo.toml new file mode 100644 index 0000000000..11bb1ec41e --- /dev/null +++ b/tools/vendor/wit-bindgen/Cargo.toml @@ -0,0 +1,102 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2024" +rust-version = "1.87.0" +name = "wit-bindgen" +version = "0.51.0" +authors = ["Alex Crichton "] +build = "build.rs" +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = """ +Rust bindings generator and runtime support for WIT and the component model. +Used when compiling Rust programs to the component model. +""" +homepage = "https://github.com/bytecodealliance/wit-bindgen" +readme = "README.md" +license = "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT" +repository = "https://github.com/bytecodealliance/wit-bindgen" +resolver = "2" + +[package.metadata.docs.rs] +all-features = true + +[features] +async = [ + "std", + "wit-bindgen-rust-macro?/async", +] +async-spawn = [ + "async", + "dep:futures", +] +bitflags = ["dep:bitflags"] +default = [ + "macros", + "realloc", + "async", + "std", + "bitflags", +] +inter-task-wakeup = ["async"] +macros = ["dep:wit-bindgen-rust-macro"] +realloc = [] +rustc-dep-of-std = [ + "dep:core", + "dep:alloc", +] +std = [] + +[lib] +name = "wit_bindgen" +path = "src/lib.rs" + +[dependencies.alloc] +version = "1.0" +optional = true +package = "rustc-std-workspace-alloc" + +[dependencies.bitflags] +version = "2.3.3" +optional = true + +[dependencies.core] +version = "1.0" +optional = true +package = "rustc-std-workspace-core" + +[dependencies.futures] +version = "0.3.30" +optional = true + +[dependencies.wit-bindgen-rust-macro] +version = "0.51.0" +optional = true + +[lints.clippy] +allow_attributes_without_reason = "warn" +clone_on_copy = "warn" +manual_strip = "warn" +map_clone = "warn" +uninlined_format_args = "warn" +unnecessary_cast = "warn" +unnecessary_fallible_conversions = "warn" +unnecessary_mut_passed = "warn" +unnecessary_to_owned = "warn" + +[lints.clippy.all] +level = "allow" +priority = -1 diff --git a/tools/vendor/wit-bindgen/Cargo.toml.orig b/tools/vendor/wit-bindgen/Cargo.toml.orig new file mode 100644 index 0000000000..73e7981297 --- /dev/null +++ b/tools/vendor/wit-bindgen/Cargo.toml.orig @@ -0,0 +1,45 @@ +[package] +name = "wit-bindgen" +authors = ["Alex Crichton "] +version = { workspace = true } +edition = { workspace = true } +repository = { workspace = true } +license = { workspace = true } +rust-version = { workspace = true } +homepage = 'https://github.com/bytecodealliance/wit-bindgen' +description = """ +Rust bindings generator and runtime support for WIT and the component model. +Used when compiling Rust programs to the component model. +""" + +[lints] +workspace = true + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +wit-bindgen-rust-macro = { path = "./macro", optional = true, version = "0.51.0" } +bitflags = { workspace = true, optional = true } +futures = { version = "0.3.30", optional = true } + +# When built as part of libstd +core = { version = "1.0", optional = true, package = "rustc-std-workspace-core" } +alloc = { version = "1.0", optional = true, package = "rustc-std-workspace-alloc" } + +[features] +default = ["macros", "realloc", "async", "std", "bitflags"] +macros = ["dep:wit-bindgen-rust-macro"] +realloc = [] +std = [] +async = ["std", "wit-bindgen-rust-macro?/async"] +bitflags = ["dep:bitflags"] +async-spawn = ['async', 'dep:futures'] + +# Unstable feature to support being a libstd dependency +rustc-dep-of-std = ["dep:core", "dep:alloc"] + +# Requires a newer `wasm-component-ld` and `wasm-tools`, so currently disabled +# by default. Once the versions of tooling have propagated this will be enabled +# by default. +inter-task-wakeup = ["async"] diff --git a/tools/vendor/wit-bindgen/LICENSE-APACHE b/tools/vendor/wit-bindgen/LICENSE-APACHE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/tools/vendor/wit-bindgen/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/tools/vendor/wit-bindgen/LICENSE-Apache-2.0_WITH_LLVM-exception b/tools/vendor/wit-bindgen/LICENSE-Apache-2.0_WITH_LLVM-exception new file mode 100644 index 0000000000..f9d81955f4 --- /dev/null +++ b/tools/vendor/wit-bindgen/LICENSE-Apache-2.0_WITH_LLVM-exception @@ -0,0 +1,220 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + diff --git a/tools/vendor/wit-bindgen/LICENSE-MIT b/tools/vendor/wit-bindgen/LICENSE-MIT new file mode 100644 index 0000000000..31aa79387f --- /dev/null +++ b/tools/vendor/wit-bindgen/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/tools/vendor/wit-bindgen/README.md b/tools/vendor/wit-bindgen/README.md new file mode 100644 index 0000000000..7fdfbe309a --- /dev/null +++ b/tools/vendor/wit-bindgen/README.md @@ -0,0 +1,45 @@ +
+

wit-bindgen

+ +

+ Guest Rust language bindings generator for + WIT + and the + Component Model + +

+ +A Bytecode Alliance project + +

+ supported rustc stable + Documentation Status +

+
+ +# About + +This crate provides a macro, [`generate!`], to automatically generate Rust +bindings for a [WIT] [world]. For more information about this crate see the +[online documentation] which includes some examples and longer form reference +documentation as well. + +This crate is developed as a portion of the [`wit-bindgen` repository] which +also contains a CLI and generators for other languages. + +[`generate!`]: https://docs.rs/wit-bindgen/latest/wit_bindgen/macro.generate.html +[WIT]: https://component-model.bytecodealliance.org/design/wit.html +[world]: https://component-model.bytecodealliance.org/design/worlds.html +[online documentation]: https://docs.rs/wit-bindgen +[`wit-bindgen` repository]: https://github.com/bytecodealliance/wit-bindgen + +# License + +This project is licensed under the Apache 2.0 license with the LLVM exception. +See [LICENSE](LICENSE) for more details. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this project by you, as defined in the Apache-2.0 license, +shall be licensed as above, without any additional terms or conditions. diff --git a/tools/vendor/wit-bindgen/build.rs b/tools/vendor/wit-bindgen/build.rs new file mode 100644 index 0000000000..e54be4a686 --- /dev/null +++ b/tools/vendor/wit-bindgen/build.rs @@ -0,0 +1,32 @@ +use std::env; +use std::path::PathBuf; + +fn main() { + let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap_or(String::new()); + let target_family = env::var("CARGO_CFG_TARGET_FAMILY").unwrap_or(String::new()); + let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + + if target_family != "wasm" { + return; + } + + if target_arch != "wasm32" { + panic!("only wasm32 supports cabi-realloc right now"); + } + + let mut src = env::current_dir().unwrap(); + src.push("src"); + src.push("rt"); + src.push("libwit_bindgen_cabi.a"); + + let dst_name = format!( + "wit_bindgen_cabi{}", + env!("CARGO_PKG_VERSION").replace(".", "_") + ); + let dst = out_dir.join(format!("lib{dst_name}.a")); + + std::fs::copy(&src, &dst).unwrap(); + + println!("cargo:rustc-link-lib=static={dst_name}"); + println!("cargo:rustc-link-search=native={}", out_dir.display()); +} diff --git a/tools/vendor/wit-bindgen/src/examples.rs b/tools/vendor/wit-bindgen/src/examples.rs new file mode 100644 index 0000000000..a63996889f --- /dev/null +++ b/tools/vendor/wit-bindgen/src/examples.rs @@ -0,0 +1,55 @@ +//! Examples of output of the [`generate!`] macro. +//! +//! This module is only included in docs.rs documentation and is not present in +//! the actual crate when compiling from crates.io. The purpose of this module +//! is to showcase what the output of the [`generate!`] macro looks like. +//! +//! [`generate!`]: crate::generate + +/// An example of generated bindings for top-level imported functions and +/// interfaces into a world. +/// +/// The code used to generate this module is: +/// +/// ```rust +#[doc = include_str!("./examples/_0_world_imports.rs")] +/// ``` +pub mod _0_world_imports; + +/// An example of importing interfaces into a world. +/// +/// The code used to generate this module is: +/// +/// ```rust +#[doc = include_str!("./examples/_1_interface_imports.rs")] +/// ``` +pub mod _1_interface_imports; + +/// An example of importing resources into a world. +/// +/// The code used to generate this module is: +/// +/// ```rust +#[doc = include_str!("./examples/_2_imported_resources.rs")] +/// ``` +pub mod _2_imported_resources; + +/// An example of exporting items from a world and the traits that they +/// generate. +/// +/// The code used to generate this module is: +/// +/// ```rust +#[doc = include_str!("./examples/_3_world_exports.rs")] +/// ``` +pub mod _3_world_exports; + +/// An example of exporting resources from a world and the traits that they +/// generate. +/// +/// The code used to generate this module is: +/// +/// ```rust +#[doc = include_str!("./examples/_4_exported_resources.rs")] +/// ``` +pub mod _4_exported_resources; diff --git a/tools/vendor/wit-bindgen/src/examples/_0_world_imports.rs b/tools/vendor/wit-bindgen/src/examples/_0_world_imports.rs new file mode 100644 index 0000000000..6fe04fe5bf --- /dev/null +++ b/tools/vendor/wit-bindgen/src/examples/_0_world_imports.rs @@ -0,0 +1,17 @@ +crate::generate!({ + inline: r#" + package example:world-imports; + + world with-imports { + /// Fetch a greeting to present. + import greet: func() -> string; + + /// Log a message to the host. + import log: func(msg: string); + + import my-custom-host: interface { + tick: func(); + } + } + "#, +}); diff --git a/tools/vendor/wit-bindgen/src/examples/_1_interface_imports.rs b/tools/vendor/wit-bindgen/src/examples/_1_interface_imports.rs new file mode 100644 index 0000000000..400121fed1 --- /dev/null +++ b/tools/vendor/wit-bindgen/src/examples/_1_interface_imports.rs @@ -0,0 +1,32 @@ +crate::generate!({ + inline: r#" + package example:interface-imports; + + interface logging { + enum level { + debug, + info, + warn, + error, + } + + log: func(level: level, msg: string); + } + + world with-imports { + // Local interfaces can be imported. + import logging; + + // Dependencies can also be referenced, and they're loaded from the + // `path` directive specified below. + import wasi:cli/environment@0.2.0; + } + "#, + + path: "wasi-cli@0.2.0.wasm", + + // specify that this interface dependency should be generated as well. + with: { + "wasi:cli/environment@0.2.0": generate, + } +}); diff --git a/tools/vendor/wit-bindgen/src/examples/_2_imported_resources.rs b/tools/vendor/wit-bindgen/src/examples/_2_imported_resources.rs new file mode 100644 index 0000000000..be2e2d306a --- /dev/null +++ b/tools/vendor/wit-bindgen/src/examples/_2_imported_resources.rs @@ -0,0 +1,22 @@ +crate::generate!({ + inline: r#" + package example:imported-resources; + + world import-some-resources { + enum level { + debug, + info, + warn, + error, + } + resource logger { + constructor(max-level: level); + + get-max-level: func() -> level; + set-max-level: func(level: level); + + log: func(level: level, msg: string); + } + } + "#, +}); diff --git a/tools/vendor/wit-bindgen/src/examples/_3_world_exports.rs b/tools/vendor/wit-bindgen/src/examples/_3_world_exports.rs new file mode 100644 index 0000000000..2ccedbf7b5 --- /dev/null +++ b/tools/vendor/wit-bindgen/src/examples/_3_world_exports.rs @@ -0,0 +1,47 @@ +crate::generate!({ + inline: r#" + package example:world-exports; + + world with-exports { + import log: func(msg: string); + + export run: func(); + + /// An example of exporting an interface inline naming it directly. + export environment: interface { + get: func(var: string) -> string; + set: func(var: string, val: string); + } + + /// An example of exporting an interface defined in this file. + export units; + + /// An example of exporting an interface defined in a dependency. + export wasi:random/insecure@0.2.0; + } + + interface units { + use wasi:clocks/monotonic-clock@0.2.0.{duration}; + + /// Renders the number of bytes as a human readable string. + bytes-to-string: func(bytes: u64) -> string; + + /// Renders the provided duration as a human readable string. + duration-to-string: func(dur: duration) -> string; + } + "#, + + // provided here to get the export macro rendered in documentation, not + // required for external use. + pub_export_macro: true, + + // provided to specify the path to `wasi:*` dependencies referenced above. + path: "wasi-cli@0.2.0.wasm", + + // specify that these interface dependencies should be generated as well. + with: { + "wasi:random/insecure@0.2.0": generate, + "wasi:clocks/monotonic-clock@0.2.0": generate, + "wasi:io/poll@0.2.0": generate + } +}); diff --git a/tools/vendor/wit-bindgen/src/examples/_4_exported_resources.rs b/tools/vendor/wit-bindgen/src/examples/_4_exported_resources.rs new file mode 100644 index 0000000000..5d1281be56 --- /dev/null +++ b/tools/vendor/wit-bindgen/src/examples/_4_exported_resources.rs @@ -0,0 +1,26 @@ +crate::generate!({ + inline: r#" + package example:exported-resources; + + world import-some-resources { + export logging; + } + + interface logging { + enum level { + debug, + info, + warn, + error, + } + resource logger { + constructor(max-level: level); + + get-max-level: func() -> level; + set-max-level: func(level: level); + + log: func(level: level, msg: string); + } + } + "#, +}); diff --git a/tools/vendor/wit-bindgen/src/lib.rs b/tools/vendor/wit-bindgen/src/lib.rs new file mode 100644 index 0000000000..a2a35d5b9f --- /dev/null +++ b/tools/vendor/wit-bindgen/src/lib.rs @@ -0,0 +1,887 @@ +//! Bindings generation support for Rust with the Component Model. +//! +//! This crate is a bindings generator for [WIT] and the [Component Model]. +//! Users are likely interested in the [`generate!`] macro which actually +//! generates bindings. Otherwise this crate provides any runtime support +//! necessary for the macro-generated code. +//! +//! [WIT]: https://component-model.bytecodealliance.org/design/wit.html +//! [Component Model]: https://component-model.bytecodealliance.org/ + +#![no_std] +#![cfg_attr(docsrs, feature(doc_cfg))] + +#[cfg(not(feature = "rustc-dep-of-std"))] +extern crate alloc; +#[cfg(feature = "std")] +extern crate std; + +/// Generate bindings for an input WIT document. +/// +/// This macro is the bread-and-butter of the `wit-bindgen` crate. The macro +/// here will parse [WIT] as input and generate Rust bindings to work with the +/// `world` that's specified in the [WIT]. For a primer on WIT see [this +/// documentation][WIT] and for a primer on worlds see [here][worlds]. +/// +/// [WIT]: https://component-model.bytecodealliance.org/design/wit.html +/// [worlds]: https://component-model.bytecodealliance.org/design/worlds.html +/// +/// This macro takes as input a [WIT package] as well as a [`world`][worlds] +/// within that package. It will then generate a Rust function for all `import`s +/// into the world. If there are any `export`s then a Rust `trait` will be +/// generated for you to implement. The macro additionally takes a number of +/// configuration parameters documented below as well. +/// +/// Basic invocation of the macro can look like: +/// +/// ``` +/// use wit_bindgen::generate; +/// # macro_rules! generate { ($($t:tt)*) => () } +/// +/// generate!(); +/// ``` +/// +/// This will parse a WIT package in the `wit` folder adjacent to your project's +/// `Cargo.toml` file. Within this WIT package there must be precisely one +/// `world` and that world will be the one that has bindings generated for it. +/// All other options remain at their default values (more on this below). +/// +/// If your WIT package has more than one `world`, or if you want to select a +/// world from the dependencies, you can specify a world explicitly: +/// +/// ``` +/// use wit_bindgen::generate; +/// # macro_rules! generate { ($($t:tt)*) => () } +/// +/// generate!("my-world"); +/// generate!("wasi:cli/imports"); +/// ``` +/// +/// This form of the macro takes a single string as an argument which is a +/// "world specifier" to select which world is being generated. As a single +/// string, such as `"my-world"`, this selects the world named `my-world` in the +/// package being parsed in the `wit` folder. The longer form specification +/// `"wasi:cli/imports"` indicates that the `wasi:cli` package, located in the +/// `wit/deps` folder, will have a world named `imports` and those bindings will +/// be generated. +/// +/// If your WIT package is located in a different directory than one called +/// `wit` then it can be specified with the `in` keyword: +/// +/// ``` +/// use wit_bindgen::generate; +/// # macro_rules! generate { ($($t:tt)*) => () } +/// +/// generate!(in "./my/other/path/to/wit"); +/// generate!("a-world" in "../path/to/wit"); +/// ``` +/// +/// The full-form of the macro, however, takes a braced structure which is a +/// "bag of options": +/// +/// ``` +/// use wit_bindgen::generate; +/// # macro_rules! generate { ($($t:tt)*) => () } +/// +/// generate!({ +/// world: "my-world", +/// path: "../path/to/wit", +/// // ... +/// }); +/// ``` +/// +/// For documentation on each option, see below. +/// +/// ## Exploring generated bindings +/// +/// Once bindings have been generated they can be explored via a number of means +/// to see what was generated: +/// +/// * Using `cargo doc` should render all of the generated bindings in addition +/// to the original comments in the WIT format itself. +/// * If your IDE supports `rust-analyzer` code completion should be available +/// to explore and see types. +/// * The `wit-bindgen` CLI tool, packaged as `wit-bindgen-cli` on crates.io, +/// can be executed the same as the `generate!` macro and the output can be +/// read. +/// * If you're seeing an error, `WIT_BINDGEN_DEBUG=1` can help debug what's +/// happening (more on this below) by emitting macro output to a file. +/// * This documentation can be consulted for various constructs as well. +/// +/// Currently browsing generated code may have road bumps on the way. If you run +/// into issues or have idea of how to improve the situation please [file an +/// issue]. +/// +/// [file an issue]: https://github.com/bytecodealliance/wit-bindgen/issues/new +/// +/// ## Namespacing +/// +/// In WIT, worlds can import and export `interface`s, functions, and types. Each +/// `interface` can either be "anonymous" and only named within the context of a +/// `world` or it can have a "package ID" associated with it. Names in Rust take +/// into account all the names associated with a WIT `interface`. For example +/// the package ID `foo:bar/baz` would create a `mod foo` which contains a `mod +/// bar` which contains a `mod baz`. +/// +/// WIT imports and exports are additionally separated into their own +/// namespaces. Imports are generated at the level of the `generate!` macro +/// where exports are generated under an `exports` namespace. +/// +/// ## Imports +/// +/// Imports into a `world` can be types, resources, functions, and interfaces. +/// Each of these is bound as a Rust type, function, or module. The intent is +/// that the WIT interfaces map to what is roughly idiomatic Rust for the given +/// interface. +/// +/// ### Imports: Top-level functions and types +/// +/// Imports at the top-level of a world are generated directly where the +/// `generate!` macro is invoked. +/// +/// ``` +/// use wit_bindgen::generate; +/// +/// generate!({ +/// inline: r" +/// package a:b; +/// +/// world the-world { +/// record fahrenheit { +/// degrees: f32, +/// } +/// +/// import what-temperature-is-it: func() -> fahrenheit; +/// +/// record celsius { +/// degrees: f32, +/// } +/// +/// import convert-to-celsius: func(a: fahrenheit) -> celsius; +/// } +/// ", +/// }); +/// +/// fn test() { +/// let current_temp = what_temperature_is_it(); +/// println!("current temp in fahrenheit is {}", current_temp.degrees); +/// let in_celsius: Celsius = convert_to_celsius(current_temp); +/// println!("current temp in celsius is {}", in_celsius.degrees); +/// } +/// ``` +/// +/// ### Imports: Interfaces +/// +/// Interfaces are placed into submodules where the `generate!` macro is +/// invoked and are namespaced based on their identifiers. +/// +/// ``` +/// use wit_bindgen::generate; +/// +/// generate!({ +/// inline: r" +/// package my:test; +/// +/// interface logging { +/// enum level { +/// debug, +/// info, +/// error, +/// } +/// log: func(level: level, msg: string); +/// } +/// +/// world the-world { +/// import logging; +/// import global-logger: interface { +/// use logging.{level}; +/// +/// set-current-level: func(level: level); +/// get-current-level: func() -> level; +/// } +/// } +/// ", +/// }); +/// +/// // `my` and `test` are from `package my:test;` and `logging` is for the +/// // interfac name. +/// use my::test::logging::Level; +/// +/// fn test() { +/// let current_level = global_logger::get_current_level(); +/// println!("current logging level is {current_level:?}"); +/// global_logger::set_current_level(Level::Error); +/// +/// my::test::logging::log(Level::Info, "Hello there!"); +/// } +/// # +/// # fn main() {} +/// ``` +/// +/// ### Imports: Resources +/// +/// Imported resources generate a type named after the name of the resource. +/// This type is then used both for borrows as `&T` as well as via ownership as +/// `T`. Resource methods are bound as methods on the type `T`. +/// +/// ``` +/// use wit_bindgen::generate; +/// +/// generate!({ +/// inline: r#" +/// package my:test; +/// +/// interface logger { +/// enum level { +/// debug, +/// info, +/// error, +/// } +/// +/// resource logger { +/// constructor(destination: string); +/// log: func(level: level, msg: string); +/// } +/// } +/// +/// // Note that while this world does not textually import the above +/// // `logger` interface it is a transitive dependency via the `use` +/// // statement so the "elaborated world" imports the logger. +/// world the-world { +/// use logger.{logger}; +/// +/// import get-global-logger: func() -> logger; +/// } +/// "#, +/// }); +/// +/// use my::test::logger::Level; +/// +/// fn test() { +/// let logger = get_global_logger(); +/// logger.log(Level::Debug, "This is a global message"); +/// +/// let logger2 = Logger::new("/tmp/other.log"); +/// logger2.log(Level::Info, "This is not a global message"); +/// } +/// # +/// # fn main() {} +/// ``` +/// +/// Note in the above example the lack of import of `Logger`. The `use` +/// statement imported the `Logger` type, an alias of it, from the `logger` +/// interface into `the-world`. This generated a Rust `type` alias so `Logger` +/// was available at the top-level. +/// +/// ## Exports: Basic Usage +/// +/// A WIT world can not only `import` functionality but can additionally +/// `export` functionality as well. An `export` represents a contract that the +/// Rust program must implement to be able to work correctly. The `generate!` +/// macro's goal is to take care of all the low-level and ABI details for you, +/// so the end result is that `generate!`, for exports, will generate Rust +/// `trait`s that you must implement. +/// +/// A minimal example of this is: +/// +/// ``` +/// use wit_bindgen::generate; +/// +/// generate!({ +/// inline: r#" +/// package my:test; +/// +/// world my-world { +/// export hello: func(); +/// } +/// "#, +/// }); +/// +/// struct MyComponent; +/// +/// impl Guest for MyComponent { +/// fn hello() {} +/// } +/// +/// export!(MyComponent); +/// # +/// # fn main() {} +/// ``` +/// +/// Here the `Guest` trait was generated by the `generate!` macro and represents +/// the functions at the top-level of `my-world`, in this case the function +/// `hello`. A custom type, here called `MyComponent`, is created and the trait +/// is implemented for that type. +/// +/// Additionally a macro is generated by `generate!` (macros generating macros) +/// called `export!`. The `export!` macro is given a component that implements +/// the export `trait`s and then it will itself generate all necessary +/// `#[unsafe(no_mangle)]` functions to implement the ABI required. +/// +/// ## Exports: Multiple Interfaces +/// +/// Each `interface` in WIT will generate a `trait` that must be implemented in +/// addition to the top-level `trait` for the world. All traits are named +/// `Guest` here and are namespaced appropriately in modules: +/// +/// ``` +/// use wit_bindgen::generate; +/// +/// generate!({ +/// inline: r#" +/// package my:test; +/// +/// interface a { +/// func-in-a: func(); +/// second-func-in-a: func(); +/// } +/// +/// world my-world { +/// export a; +/// export b: interface { +/// func-in-b: func(); +/// } +/// export c: func(); +/// } +/// "#, +/// }); +/// +/// struct MyComponent; +/// +/// impl Guest for MyComponent { +/// fn c() {} +/// } +/// +/// impl exports::my::test::a::Guest for MyComponent { +/// fn func_in_a() {} +/// fn second_func_in_a() {} +/// } +/// +/// impl exports::b::Guest for MyComponent { +/// fn func_in_b() {} +/// } +/// +/// export!(MyComponent); +/// # +/// # fn main() {} +/// ``` +/// +/// Here note that there were three `Guest` traits generated for each of the +/// three groups: two interfaces and one `world`. Also note that traits (and +/// types) for exports are namespaced in an `exports` module. +/// +/// Note that when the top-level `world` does not have any exported functions, +/// or if an interface does not have any functions, then no `trait` is +/// generated: +/// +/// ``` +/// use wit_bindgen::generate; +/// +/// generate!({ +/// inline: r#" +/// package my:test; +/// +/// interface a { +/// type my-type = u32; +/// } +/// +/// world my-world { +/// export b: interface { +/// use a.{my-type}; +/// +/// foo: func() -> my-type; +/// } +/// } +/// "#, +/// }); +/// +/// struct MyComponent; +/// +/// impl exports::b::Guest for MyComponent { +/// fn foo() -> u32 { +/// 42 +/// } +/// } +/// +/// export!(MyComponent); +/// # +/// # fn main() {} +/// ``` +/// +/// ## Exports: Resources +/// +/// Exporting a resource is significantly different than importing a resource. +/// A component defining a resource can create new resources of that type at any +/// time, for example. Additionally resources can be "dereferenced" into their +/// underlying values within the component. +/// +/// Owned resources have a custom type generated and borrowed resources are +/// generated with a type of the same name suffixed with `Borrow<'_>`, such as +/// `MyResource` and `MyResourceBorrow<'_>`. +/// +/// Like `interface`s the methods and functions used with a `resource` are +/// packaged up into a `trait`. +/// +/// Specifying a custom resource type is done with an associated type on the +/// corresponding trait for the resource's containing interface/world: +/// +/// ``` +/// use wit_bindgen::generate; +/// use std::cell::{RefCell, Cell}; +/// +/// generate!({ +/// inline: r#" +/// package my:test; +/// +/// interface logging { +/// enum level { +/// debug, +/// info, +/// error, +/// } +/// +/// resource logger { +/// constructor(level: level); +/// log: func(level: level, msg: string); +/// level: func() -> level; +/// set-level: func(level: level); +/// } +/// } +/// +/// world my-world { +/// export logging; +/// } +/// "#, +/// }); +/// +/// use exports::my::test::logging::{Guest, GuestLogger, Level}; +/// +/// struct MyComponent; +/// +/// // Note that the `logging` interface has no methods of its own but a trait +/// // is required to be implemented here to specify the type of `Logger`. +/// impl Guest for MyComponent { +/// type Logger = MyLogger; +/// } +/// +/// struct MyLogger { +/// level: Cell, +/// contents: RefCell, +/// } +/// +/// impl GuestLogger for MyLogger { +/// fn new(level: Level) -> MyLogger { +/// MyLogger { +/// level: Cell::new(level), +/// contents: RefCell::new(String::new()), +/// } +/// } +/// +/// fn log(&self, level: Level, msg: String) { +/// if level as u32 <= self.level.get() as u32 { +/// self.contents.borrow_mut().push_str(&msg); +/// self.contents.borrow_mut().push_str("\n"); +/// } +/// } +/// +/// fn level(&self) -> Level { +/// self.level.get() +/// } +/// +/// fn set_level(&self, level: Level) { +/// self.level.set(level); +/// } +/// } +/// +/// export!(MyComponent); +/// # +/// # fn main() {} +/// ``` +/// +/// It's important to note that resources in Rust do not get `&mut self` as +/// methods, but instead are required to be defined with `&self`. This requires +/// the use of interior mutability such as `Cell` and `RefCell` above from the +/// `std::cell` module. +/// +/// ## Exports: The `export!` macro +/// +/// Components are created by having exported WebAssembly functions with +/// specific names, and these functions are not created when `generate!` is +/// invoked. Instead these functions are created afterwards once you've defined +/// your own type an implemented the various `trait`s for it. The +/// `#[unsafe(no_mangle)]` functions that will become the component are created +/// with the generated `export!` macro. +/// +/// Each call to `generate!` will itself generate a macro called `export!`. +/// The macro's first argument is the name of a type that implements the traits +/// generated: +/// +/// ``` +/// use wit_bindgen::generate; +/// +/// generate!({ +/// inline: r#" +/// package my:test; +/// +/// world my-world { +/// # export hello: func(); +/// // ... +/// } +/// "#, +/// }); +/// +/// struct MyComponent; +/// +/// impl Guest for MyComponent { +/// # fn hello() {} +/// // ... +/// } +/// +/// export!(MyComponent); +/// # +/// # fn main() {} +/// ``` +/// +/// This argument is a Rust type which implements the `Guest` traits generated +/// by `generate!`. Note that all `Guest` traits must be implemented for the +/// type provided or an error will be generated. +/// +/// This macro additionally accepts a second argument. The macro itself needs to +/// be able to find the module where the `generate!` macro itself was originally +/// invoked. Currently that can't be done automatically so a path to where +/// `generate!` was provided can also be passed to the macro. By default, the +/// argument is set to `self`: +/// +/// ``` +/// use wit_bindgen::generate; +/// +/// generate!({ +/// // ... +/// # inline: r#" +/// # package my:test; +/// # +/// # world my-world { +/// # export hello: func(); +/// # // ... +/// # } +/// # "#, +/// }); +/// # +/// # struct MyComponent; +/// # +/// # impl Guest for MyComponent { +/// # fn hello() {} +/// # // ... +/// # } +/// # +/// export!(MyComponent with_types_in self); +/// # +/// # fn main() {} +/// ``` +/// +/// This indicates that the current module, referred to with `self`, is the one +/// which had the `generate!` macro expanded. +/// +/// If, however, the `generate!` macro was run in a different module then that +/// must be configured: +/// +/// ``` +/// mod bindings { +/// wit_bindgen::generate!({ +/// // ... +/// # inline: r#" +/// # package my:test; +/// # +/// # world my-world { +/// # export hello: func(); +/// # // ... +/// # } +/// # "#, +/// }); +/// } +/// # +/// # struct MyComponent; +/// # +/// # impl bindings::Guest for MyComponent { +/// # fn hello() {} +/// # // ... +/// # } +/// # +/// bindings::export!(MyComponent with_types_in bindings); +/// # +/// # fn main() {} +/// ``` +/// +/// ## Debugging output to `generate!` +/// +/// While `wit-bindgen` is tested to the best of our ability there are +/// inevitably bugs and issues that arise. These can range from bad error +/// messages to misconfigured invocations to bugs in the macro itself. To assist +/// with debugging these situations the macro recognizes an environment +/// variable: +/// +/// ```shell +/// export WIT_BINDGEN_DEBUG=1 +/// ``` +/// +/// When set the macro will emit the result of expansion to a file and then +/// `include!` that file. Any error messages generated by `rustc` should then +/// point to the generated file and allow you to open it up, read it, and +/// inspect it. This can often provide better context to the error than rustc +/// provides by default with macros. +/// +/// It is not recommended to set this environment variable by default as it will +/// cause excessive rebuilds of Cargo projects. It's recommended to only use it +/// as necessary to debug issues. +/// +/// ## Options to `generate!` +/// +/// The full list of options that can be passed to the `generate!` macro are as +/// follows. Note that there are no required options, they all have default +/// values. +/// +/// +/// ``` +/// use wit_bindgen::generate; +/// # macro_rules! generate { ($($t:tt)*) => () } +/// +/// generate!({ +/// // The name of the world that bindings are being generated for. If this +/// // is not specified then it's required that the package selected +/// // below has a single `world` in it. +/// world: "my-world", +/// +/// // Path to parse WIT and its dependencies from. Defaults to the `wit` +/// // folder adjacent to your `Cargo.toml`. +/// // +/// // This parameter also supports the form of a list, such as: +/// // ["../path/to/wit1", "../path/to/wit2"] +/// // Usually used in testing, our test suite may want to generate code +/// // from wit files located in multiple paths within a single mod, and we +/// // don't want to copy these files again. Currently these locations must +/// // be ordered, as later paths can't contain dependencies on earlier +/// // paths. This restriction may be lifted in the future. +/// path: "../path/to/wit", +/// +/// // Enables passing "inline WIT". If specified this is the default +/// // package that a world is selected from. Any dependencies that this +/// // inline WIT refers to must be defined in the `path` option above. +/// // +/// // By default this is not specified. +/// inline: " +/// world my-world { +/// import wasi:cli/imports; +/// +/// export my-run: func() +/// } +/// ", +/// +/// // Additional traits to derive for all defined types. Note that not all +/// // types may be able to implement these traits, such as resources. +/// // +/// // By default this set is empty. +/// additional_derives: [PartialEq, Eq, Hash, Clone], +/// +/// // When generating bindings for interfaces that are not defined in the +/// // same package as `world`, this option can be used to either generate +/// // those bindings or point to already generated bindings. +/// // For example, if your world refers to WASI types then the `wasi` crate +/// // already has generated bindings for all WASI types and structures. In this +/// // situation the key `with` here can be used to use those types +/// // elsewhere rather than regenerating types. +/// // If for example your world refers to some type and you want to use +/// // your own custom implementation of that type then you can specify +/// // that here as well. There is a requirement on the remapped (custom) +/// // type to have the same internal structure and identical to what would +/// // wit-bindgen generate (including alignment, etc.), since +/// // lifting/lowering uses its fields directly. +/// // +/// // If, however, your world refers to interfaces for which you don't have +/// // already generated bindings then you can use the special `generate` value +/// // to have those bindings generated. +/// // +/// // The `with` key here works for interfaces and individual types. +/// // +/// // When an interface or type is specified here no bindings will be +/// // generated at all. It's assumed bindings are fully generated +/// // somewhere else. This is an indicator that any further references to types +/// // defined in these interfaces should use the upstream paths specified +/// // here instead. +/// // +/// // Any unused keys in this map are considered an error. +/// with: { +/// "wasi:io/poll": wasi::io::poll, +/// "some:package/my-interface": generate, +/// "some:package/my-interface/my-type": my_crate::types::MyType, +/// }, +/// +/// // Indicates that all interfaces not present in `with` should be assumed +/// // to be marked with `generate`. +/// generate_all, +/// +/// // An optional list of function names to skip generating bindings for. +/// // This is only applicable to imports and the name specified is the name +/// // of the function. +/// skip: ["foo", "bar", "baz"], +/// +/// // Configuration of how Rust types are generated. +/// // +/// // This option will change how WIT types are mapped to Rust types. There +/// // are a number of ways this can be done depending on the context. For +/// // example a Rust `&str` is suitable to pass to an imported function but +/// // an exported function receives a `String`. These both represent the +/// // WIT type `string`, however. +/// // +/// // Type generation becomes extra-significant when aggregates come into +/// // play (such as a WIT `record` or `variant`), especially when the +/// // aggregate is used both in an imported function and exported one. +/// // +/// // There are three modes of ownership, documented here, but only one +/// // can be specified. +/// // +/// // The default mode is "Owning" meaning that all Rust types will by +/// // default contain their owned containers. For example a `record` with +/// // a `string` will map to a Rust `struct` containing a `String`. This +/// // maximizes the chance that types can be shared between imports and +/// // exports but can come at a cost where calling an import may require +/// // more allocations than necessary. +/// ownership: Owning, +/// +/// // The second mode of ownership is "Borrowing". This mode then +/// // additionally has a boolean flag indicating whether duplicate types +/// // should be generated if necessary. +/// // +/// // This mode will prefer using borrowed values in Rust to represent WIT +/// // values where possible. For example if the argument to an imported +/// // function is a record-with-a-string then in Rust that will generate a +/// // `struct` with a lifetime parameter storing `&'a str`. +/// // +/// // The `duplicate_if_necessary` flag will cause duplicate types to be +/// // generated when a WIT type is used both in an import and export. In +/// // this situation one will be called `FooParam` and one will be called +/// // `FooResult` (where `foo` is the WIT name). +/// // +/// // It's generally recommended to not turn this on unless performance +/// // requires it. Even if so, please feel free to open an issue on the +/// // `wit-bindgen` repository to help improve the default "Owning" use +/// // case above if possible. +/// ownership: Borrowing { duplicate_if_necessary: false }, +/// +/// // Specifies an alternative name for the `export!` macro generated for +/// // any exports this world has. +/// // +/// // Defaults to "export" +/// export_macro_name: "export", +/// +/// // Indicates whether the `export!` macro is `pub` or just `pub(crate)`. +/// // +/// // This defaults to `false`. +/// pub_export_macro: false, +/// +/// // The generated `export!` macro, if any, will by default look for +/// // generated types adjacent to where the `export!` macro is invoked +/// // through the `self` module. This option can be used to change the +/// // defaults to look somewhere else instead. +/// default_bindings_module: "path::to::bindings", +/// +/// // This will suffix the custom section containing component type +/// // information with the specified string. This is not required by +/// // default but if the same world is generated in two different locations +/// // in the crate then one bindings generation location will need this +/// // suffix to avoid having the custom sections corrupt each other. +/// type_section_suffix: "suffix", +/// +/// // Configures the path to the `wit-bindgen` crate itself. By default +/// // this is `wit_bindgen` assuming that your crate depends on the +/// // `wit-bindgen` crate itself. +/// runtime_path: "path::to::wit_bindgen", +/// +/// // Configure where the `bitflags` crate is located. By default this +/// // is `wit_bindgen::bitflags` which already reexports `bitflags` for +/// // you. +/// bitflags_path: "path::to::bitflags", +/// +/// // Indicates that instead of `&str` and `String` the `&[u8]` and +/// // `Vec` types should be used. Only intended for cases where +/// // compiled size is of the utmost concern as this can avoid pulling in +/// // UTF-8 validation. +/// raw_strings, +/// +/// // Emits `#[cfg(feature = "std")]` around `impl Error for ... {}` blocks +/// // for generated types. This is a niche option that is only here to +/// // support the standard library itself depending on this crate one day. +/// std_feature, +/// +/// // Disable a workaround to force wasm constructors to be run only once +/// // when exported functions are called. +/// disable_run_ctors_once_workaround: false, +/// +/// // Whether to generate unused `record`, `enum`, `variant` types. +/// // By default, they will not be generated unless they are used as input +/// // or return value of a function. +/// generate_unused_types: false, +/// +/// // A list of "features" which correspond to WIT features to activate +/// // when parsing WIT files. This enables `@unstable` annotations showing +/// // up and having bindings generated for them. +/// // +/// // By default this is an empty list. +/// features: ["foo", "bar", "baz"], +/// +/// // Disables generation of a `#[used]` static to try harder to get the +/// // custom section describing WIT types linked into the binary when +/// // used in library-like situations. This is `false` by default with +/// // `#[used]` statics being emitted. +/// disable_custom_section_link_helpers: false, +/// +/// // Write generated code to a .rs file, which allows the compiler to +/// // emit more useful diagnostics for errors in the generated code. This +/// // is primarily useful for `wit-bindgen` developers. +/// // +/// // This does the same thing as setting `WIT_BINDGEN_DEBUG=1`, except +/// // that it can be used on a more fine-grained basis (i.e. it only affects +/// // the specific `generate!` call where it is used. +/// debug: true, +/// +/// // Generate async import and/or export bindings. +/// // +/// // The resulting bindings will use the component model +/// // [async ABI](https://github.com/WebAssembly/component-model/blob/main/design/mvp/Async.md). +/// // +/// // If this option is not provided then the WIT's source annotation will +/// // be used instead. +/// async: true, // all bindings are async +/// async: false, // all bindings are sync +/// // With an array per-function configuration can be specified. A leading +/// // '-' will disable async for that particular function. +/// async: [ +/// "wasi:http/types@0.3.0-draft#[static]body.finish", +/// "import:wasi:http/handler@0.3.0-draft#handle", +/// "-export:wasi:http/handler@0.3.0-draft#handle", +/// "all", +/// ], +/// }); +/// ``` +/// +/// [WIT package]: https://component-model.bytecodealliance.org/design/packages.html +#[cfg(feature = "macros")] +pub use wit_bindgen_rust_macro::generate; + +#[cfg(docsrs)] +pub mod examples; + +#[doc(hidden)] +pub mod rt; + +#[cfg(feature = "inter-task-wakeup")] +pub use rt::async_support::UnitStreamOps; +#[cfg(feature = "async-spawn")] +pub use rt::async_support::spawn; +#[cfg(feature = "async")] +pub use rt::async_support::{ + AbiBuffer, FutureOps, FutureRead, FutureReader, FutureWrite, FutureWriteCancel, + FutureWriteError, FutureWriter, RawFutureRead, RawFutureReader, RawFutureWrite, + RawFutureWriter, RawStreamRead, RawStreamReader, RawStreamWrite, RawStreamWriter, StreamOps, + StreamRead, StreamReader, StreamResult, StreamWrite, StreamWriter, backpressure_dec, + backpressure_inc, block_on, yield_async, yield_blocking, +}; diff --git a/tools/vendor/wit-bindgen/src/rt/async_support.rs b/tools/vendor/wit-bindgen/src/rt/async_support.rs new file mode 100644 index 0000000000..080919a8c1 --- /dev/null +++ b/tools/vendor/wit-bindgen/src/rt/async_support.rs @@ -0,0 +1,708 @@ +#![deny(missing_docs)] + +extern crate std; +use core::sync::atomic::{AtomicU32, Ordering}; +use std::boxed::Box; +use std::collections::BTreeMap; +use std::ffi::c_void; +use std::future::Future; +use std::mem; +use std::pin::Pin; +use std::ptr; +use std::sync::Arc; +use std::task::{Context, Poll, Wake, Waker}; + +macro_rules! rtdebug { + ($($f:tt)*) => { + // Change this flag to enable debugging, right now we're not using a + // crate like `log` or such to reduce runtime deps. Intended to be used + // during development for now. + if false { + std::eprintln!($($f)*); + } + } +} + +/// Helper macro to deduplicate foreign definitions of wasm functions. +/// +/// This automatically imports when on wasm targets and then defines a dummy +/// panicking shim for native targets to support native compilation but fail at +/// runtime. +macro_rules! extern_wasm { + ( + $(#[$extern_attr:meta])* + unsafe extern "C" { + $( + $(#[$func_attr:meta])* + $vis:vis fn $func_name:ident ( $($args:tt)* ) $(-> $ret:ty)?; + )* + } + ) => { + $( + #[cfg(not(target_family = "wasm"))] + #[allow(unused, reason = "dummy shim for non-wasm compilation, never invoked")] + $vis unsafe fn $func_name($($args)*) $(-> $ret)? { + unreachable!(); + } + )* + + #[cfg(target_family = "wasm")] + $(#[$extern_attr])* + unsafe extern "C" { + $( + $(#[$func_attr])* + $vis fn $func_name($($args)*) $(-> $ret)?; + )* + } + }; +} + +mod abi_buffer; +mod cabi; +mod error_context; +mod future_support; +#[cfg(feature = "inter-task-wakeup")] +mod inter_task_wakeup; +mod stream_support; +mod subtask; +#[cfg(feature = "inter-task-wakeup")] +mod unit_stream; +mod waitable; +mod waitable_set; + +#[cfg(not(feature = "inter-task-wakeup"))] +use inter_task_wakeup_disabled as inter_task_wakeup; +#[cfg(not(feature = "inter-task-wakeup"))] +mod inter_task_wakeup_disabled; + +use self::waitable_set::WaitableSet; +pub use abi_buffer::*; +pub use error_context::*; +pub use future_support::*; +pub use stream_support::*; +#[doc(hidden)] +pub use subtask::Subtask; +#[cfg(feature = "inter-task-wakeup")] +pub use unit_stream::*; + +type BoxFuture<'a> = Pin + 'a>>; + +#[cfg(feature = "async-spawn")] +mod spawn; +#[cfg(feature = "async-spawn")] +pub use spawn::spawn; +#[cfg(not(feature = "async-spawn"))] +mod spawn_disabled; +#[cfg(not(feature = "async-spawn"))] +use spawn_disabled as spawn; + +/// Represents a task created by either a call to an async-lifted export or a +/// future run using `block_on` or `start_task`. +struct FutureState<'a> { + /// Remaining work to do (if any) before this task can be considered "done". + /// + /// Note that we won't tell the host the task is done until this is drained + /// and `waitables` is empty. + tasks: spawn::Tasks<'a>, + + /// The waitable set containing waitables created by this task, if any. + waitable_set: Option, + + /// State of all waitables in `waitable_set`, and the ptr/callback they're + /// associated with. + // + // Note that this is a `BTreeMap` rather than a `HashMap` only because, as + // of this writing, initializing the default hasher for `HashMap` requires + // calling `wasi_snapshot_preview1:random_get`, which requires initializing + // the `wasi_snapshot_preview1` adapter when targeting `wasm32-wasip2` and + // later, and that's expensive enough that we'd prefer to avoid it for apps + // which otherwise make no use of the adapter. + waitables: BTreeMap, + + /// Raw structure used to pass to `cabi::wasip3_task_set` + wasip3_task: cabi::wasip3_task, + + /// Rust-level state for the waker, notably a bool as to whether this has + /// been woken. + waker: Arc, + + /// Clone of `waker` field, but represented as `std::task::Waker`. + waker_clone: Waker, + + /// State related to supporting inter-task wakeup scenarios. + inter_task_wakeup: inter_task_wakeup::State, +} + +impl FutureState<'_> { + fn new(future: BoxFuture<'_>) -> FutureState<'_> { + let waker = Arc::new(FutureWaker::default()); + FutureState { + waker_clone: waker.clone().into(), + waker, + tasks: spawn::Tasks::new(future), + waitable_set: None, + waitables: BTreeMap::new(), + wasip3_task: cabi::wasip3_task { + // This pointer is filled in before calling `wasip3_task_set`. + ptr: ptr::null_mut(), + version: cabi::WASIP3_TASK_V1, + waitable_register, + waitable_unregister, + }, + inter_task_wakeup: Default::default(), + } + } + + fn get_or_create_waitable_set(&mut self) -> &WaitableSet { + self.waitable_set.get_or_insert_with(WaitableSet::new) + } + + fn add_waitable(&mut self, waitable: u32) { + self.get_or_create_waitable_set().join(waitable) + } + + fn remove_waitable(&mut self, waitable: u32) { + WaitableSet::remove_waitable_from_all_sets(waitable) + } + + fn remaining_work(&self) -> bool { + !self.waitables.is_empty() + } + + /// Handles the `event{0,1,2}` event codes and returns a corresponding + /// return code along with a flag whether this future is "done" or not. + fn callback(&mut self, event0: u32, event1: u32, event2: u32) -> CallbackCode { + match event0 { + EVENT_NONE => rtdebug!("EVENT_NONE"), + EVENT_SUBTASK => rtdebug!("EVENT_SUBTASK({event1:#x}, {event2:#x})"), + EVENT_STREAM_READ => rtdebug!("EVENT_STREAM_READ({event1:#x}, {event2:#x})"), + EVENT_STREAM_WRITE => rtdebug!("EVENT_STREAM_WRITE({event1:#x}, {event2:#x})"), + EVENT_FUTURE_READ => rtdebug!("EVENT_FUTURE_READ({event1:#x}, {event2:#x})"), + EVENT_FUTURE_WRITE => rtdebug!("EVENT_FUTURE_WRITE({event1:#x}, {event2:#x})"), + EVENT_CANCEL => { + rtdebug!("EVENT_CANCEL"); + + // Cancellation is mapped to destruction in Rust, so return a + // code/bool indicating we're done. The caller will then + // appropriately deallocate this `FutureState` which will + // transitively run all destructors. + return CallbackCode::Exit; + } + _ => unreachable!(), + } + + self.with_p3_task_set(|me| { + // Transition our sleep state to ensure that the inter-task stream + // isn't used since there's no need to use that here. + me.waker + .sleep_state + .store(SLEEP_STATE_WOKEN, Ordering::Relaxed); + + // With all of our context now configured, deliver the event + // notification this callback corresponds to. + // + // Note that this should happen under the reset of + // `waker.sleep_state` above to ensure that if a waker is woken it + // won't actually signal our inter-task stream since we're already + // in the process of handling the future. + if event0 != EVENT_NONE { + me.deliver_waitable_event(event1, event2) + } + + // If there's still an in-progress read (e.g. `event{1,2}`) wasn't + // ourselves getting woken up, then cancel the read since we're + // processing the future here anyway. + me.cancel_inter_task_stream_read(); + + loop { + let mut context = Context::from_waker(&me.waker_clone); + + // On each turn of this loop reset the state to "polling" + // which clears out any pending wakeup if one was sent. This + // in theory helps minimize wakeups from previous iterations + // happening in this iteration. + me.waker + .sleep_state + .store(SLEEP_STATE_POLLING, Ordering::Relaxed); + + // Poll our future, seeing if it was able to make progress. + let poll = me.tasks.poll_next(&mut context); + + match poll { + // A future completed, yay! Keep going to see if more have + // completed. + Poll::Ready(Some(())) => (), + + // The task list is empty, but there might be remaining work + // in terms of waitables through the cabi interface. In this + // situation wait for all waitables to be resolved before + // signaling that our own task is done. + Poll::Ready(None) => { + assert!(me.tasks.is_empty()); + if me.remaining_work() { + let waitable = me.waitable_set.as_ref().unwrap().as_raw(); + break CallbackCode::Wait(waitable); + } else { + break CallbackCode::Exit; + } + } + + // Some future within `self.tasks` is not ready yet. If our + // `waker` was signaled then that means this is a yield + // operation, otherwise it means we're blocking on + // something. + Poll::Pending => { + assert!(!me.tasks.is_empty()); + if me.waker.sleep_state.load(Ordering::Relaxed) == SLEEP_STATE_WOKEN { + if me.remaining_work() { + let (event0, event1, event2) = + me.waitable_set.as_ref().unwrap().poll(); + if event0 != EVENT_NONE { + me.deliver_waitable_event(event1, event2); + continue; + } + } + break CallbackCode::Yield; + } + + // Transition our state to "sleeping" so wakeup + // notifications know that they need to signal the + // inter-task stream. + me.waker + .sleep_state + .store(SLEEP_STATE_SLEEPING, Ordering::Relaxed); + me.read_inter_task_stream(); + let waitable = me.waitable_set.as_ref().unwrap().as_raw(); + break CallbackCode::Wait(waitable); + } + } + } + }) + } + + /// Deliver the `code` event to the `waitable` store within our map. This + /// waitable should be present because it's part of the waitable set which + /// is kept in-sync with our map. + fn deliver_waitable_event(&mut self, waitable: u32, code: u32) { + self.remove_waitable(waitable); + + if self + .inter_task_wakeup + .consume_waitable_event(waitable, code) + { + return; + } + + let (ptr, callback) = self.waitables.remove(&waitable).unwrap(); + unsafe { + callback(ptr, code); + } + } + + fn with_p3_task_set(&mut self, f: impl FnOnce(&mut Self) -> R) -> R { + // Finish our `wasip3_task` by initializing its self-referential pointer, + // and then register it for the duration of this function with + // `wasip3_task_set`. The previous value of `wasip3_task_set` will get + // restored when this function returns. + struct ResetTask(*mut cabi::wasip3_task); + impl Drop for ResetTask { + fn drop(&mut self) { + unsafe { + cabi::wasip3_task_set(self.0); + } + } + } + let self_raw = self as *mut FutureState<'_>; + self.wasip3_task.ptr = self_raw.cast(); + let prev = unsafe { cabi::wasip3_task_set(&mut self.wasip3_task) }; + let _reset = ResetTask(prev); + + f(self) + } +} + +impl Drop for FutureState<'_> { + fn drop(&mut self) { + // If there's an active read of the inter-task stream, go ahead and + // cancel it, since we're about to drop the stream anyway. + self.cancel_inter_task_stream_read(); + + // If this state has active tasks then they need to be dropped which may + // execute arbitrary code. This arbitrary code might require the p3 APIs + // for managing waitables, notably around removing them. In this + // situation we ensure that the p3 task is set while futures are being + // destroyed. + if !self.tasks.is_empty() { + self.with_p3_task_set(|me| { + me.tasks = Default::default(); + }) + } + } +} + +unsafe extern "C" fn waitable_register( + ptr: *mut c_void, + waitable: u32, + callback: unsafe extern "C" fn(*mut c_void, u32), + callback_ptr: *mut c_void, +) -> *mut c_void { + let ptr = ptr.cast::>(); + assert!(!ptr.is_null()); + unsafe { + (*ptr).add_waitable(waitable); + match (*ptr).waitables.insert(waitable, (callback_ptr, callback)) { + Some((prev, _)) => prev, + None => ptr::null_mut(), + } + } +} + +unsafe extern "C" fn waitable_unregister(ptr: *mut c_void, waitable: u32) -> *mut c_void { + let ptr = ptr.cast::>(); + assert!(!ptr.is_null()); + unsafe { + (*ptr).remove_waitable(waitable); + match (*ptr).waitables.remove(&waitable) { + Some((prev, _)) => prev, + None => ptr::null_mut(), + } + } +} + +/// Status for "this task is actively being polled" +const SLEEP_STATE_POLLING: u32 = 0; +/// Status for "this task has a wakeup scheduled, no more action need be taken". +const SLEEP_STATE_WOKEN: u32 = 1; +/// Status for "this task is not being polled and has not been woken" +/// +/// Wakeups on this status signal the inter-task stream. +const SLEEP_STATE_SLEEPING: u32 = 2; + +#[derive(Default)] +struct FutureWaker { + /// One of `SLEEP_STATE_*` indicating the current status. + sleep_state: AtomicU32, + inter_task_stream: inter_task_wakeup::WakerState, +} + +impl Wake for FutureWaker { + fn wake(self: Arc) { + Self::wake_by_ref(&self) + } + + fn wake_by_ref(self: &Arc) { + match self.sleep_state.swap(SLEEP_STATE_WOKEN, Ordering::Relaxed) { + // If this future was currently being polled, or if someone else + // already woke it up, then there's nothing to do. + SLEEP_STATE_POLLING | SLEEP_STATE_WOKEN => {} + + // If this future is sleeping, however, then this is a cross-task + // wakeup meaning that we need to write to its wakeup stream. + other => { + assert_eq!(other, SLEEP_STATE_SLEEPING); + self.inter_task_stream.wake(); + } + } + } +} + +const EVENT_NONE: u32 = 0; +const EVENT_SUBTASK: u32 = 1; +const EVENT_STREAM_READ: u32 = 2; +const EVENT_STREAM_WRITE: u32 = 3; +const EVENT_FUTURE_READ: u32 = 4; +const EVENT_FUTURE_WRITE: u32 = 5; +const EVENT_CANCEL: u32 = 6; + +#[derive(PartialEq, Debug)] +enum CallbackCode { + Exit, + Yield, + Wait(u32), +} + +impl CallbackCode { + fn encode(self) -> u32 { + match self { + CallbackCode::Exit => 0, + CallbackCode::Yield => 1, + CallbackCode::Wait(waitable) => 2 | (waitable << 4), + } + } +} + +const STATUS_STARTING: u32 = 0; +const STATUS_STARTED: u32 = 1; +const STATUS_RETURNED: u32 = 2; +const STATUS_STARTED_CANCELLED: u32 = 3; +const STATUS_RETURNED_CANCELLED: u32 = 4; + +const BLOCKED: u32 = 0xffff_ffff; +const COMPLETED: u32 = 0x0; +const DROPPED: u32 = 0x1; +const CANCELLED: u32 = 0x2; + +/// Return code of stream/future operations. +#[derive(PartialEq, Debug, Copy, Clone)] +enum ReturnCode { + /// The operation is blocked and has not completed. + Blocked, + /// The operation completed with the specified number of items. + Completed(u32), + /// The other end is dropped, but before that the specified number of items + /// were transferred. + Dropped(u32), + /// The operation was cancelled, but before that the specified number of + /// items were transferred. + Cancelled(u32), +} + +impl ReturnCode { + fn decode(val: u32) -> ReturnCode { + if val == BLOCKED { + return ReturnCode::Blocked; + } + let amt = val >> 4; + match val & 0xf { + COMPLETED => ReturnCode::Completed(amt), + DROPPED => ReturnCode::Dropped(amt), + CANCELLED => ReturnCode::Cancelled(amt), + _ => panic!("unknown return code {val:#x}"), + } + } +} + +/// Starts execution of the `task` provided, an asynchronous computation. +/// +/// This is used for async-lifted exports at their definition site. The +/// representation of the export is `task` and this function is called from the +/// entrypoint. The code returned here is the same as the callback associated +/// with this export, and the callback will be used if this task doesn't exit +/// immediately with its result. +#[doc(hidden)] +pub fn start_task(task: impl Future + 'static) -> i32 { + // Allocate a new `FutureState` which will track all state necessary for + // our exported task. + let state = Box::into_raw(Box::new(FutureState::new(Box::pin(task)))); + + // Store our `FutureState` into our context-local-storage slot and then + // pretend we got EVENT_NONE to kick off everything. + // + // SAFETY: we should own `context.set` as we're the root level exported + // task, and then `callback` is only invoked when context-local storage is + // valid. + unsafe { + assert!(context_get().is_null()); + context_set(state.cast()); + callback(EVENT_NONE, 0, 0) as i32 + } +} + +/// Handle a progress notification from the host regarding either a call to an +/// async-lowered import or a stream/future read/write operation. +/// +/// # Unsafety +/// +/// This function assumes that `context_get()` returns a `FutureState`. +#[doc(hidden)] +pub unsafe fn callback(event0: u32, event1: u32, event2: u32) -> u32 { + // Acquire our context-local state, assert it's not-null, and then reset + // the state to null while we're running to help prevent any unintended + // usage. + let state = context_get().cast::>(); + assert!(!state.is_null()); + unsafe { + context_set(ptr::null_mut()); + } + + // Use `state` to run the `callback` function in the context of our event + // codes we received. If the callback decides to exit then we're done with + // our future so deallocate it. Otherwise put our future back in + // context-local storage and forward the code. + unsafe { + let rc = (*state).callback(event0, event1, event2); + if rc == CallbackCode::Exit { + drop(Box::from_raw(state)); + } else { + context_set(state.cast()); + } + rtdebug!(" => (cb) {rc:?}"); + rc.encode() + } +} + +/// Run the specified future to completion, returning the result. +/// +/// This uses `waitable-set.wait` to poll for progress on any in-progress calls +/// to async-lowered imports as necessary. +// TODO: refactor so `'static` bounds aren't necessary +pub fn block_on(future: impl Future) -> T { + let mut result = None; + let mut state = FutureState::new(Box::pin(async { + result = Some(future.await); + })); + let mut event = (EVENT_NONE, 0, 0); + loop { + match state.callback(event.0, event.1, event.2) { + CallbackCode::Exit => { + drop(state); + break result.unwrap(); + } + CallbackCode::Yield => event = state.waitable_set.as_ref().unwrap().poll(), + CallbackCode::Wait(_) => event = state.waitable_set.as_ref().unwrap().wait(), + } + } +} + +/// Call the `yield` canonical built-in function. +/// +/// This yields control to the host temporarily, allowing other tasks to make +/// progress. It's a good idea to call this inside a busy loop which does not +/// otherwise ever yield control the host. +/// +/// Note that this function is a blocking function, not an `async` function. +/// That means that this is not an async yield which allows other tasks in this +/// component to progress, but instead this will block the current function +/// until the host gets back around to returning from this yield. Asynchronous +/// functions should probably use [`yield_async`] instead. +/// +/// # Return Value +/// +/// This function returns a `bool` which indicates whether execution should +/// continue after this yield point. A return value of `true` means that the +/// task was not cancelled and execution should continue. A return value of +/// `false`, however, means that the task was cancelled while it was suspended +/// at this yield point. The caller should return back and exit from the task +/// ASAP in this situation. +pub fn yield_blocking() -> bool { + extern_wasm! { + #[link(wasm_import_module = "$root")] + unsafe extern "C" { + #[link_name = "[thread-yield]"] + fn yield_() -> bool; + } + } + + // Note that the return value from the raw intrinsic is inverted, the + // canonical ABI returns "did this task get cancelled" while this function + // works as "should work continue going". + unsafe { !yield_() } +} + +/// The asynchronous counterpart to [`yield_blocking`]. +/// +/// This function does not block the current task but instead gives the +/// Rust-level executor a chance to yield control back to the host temporarily. +/// This means that other Rust-level tasks may also be able to progress during +/// this yield operation. +/// +/// # Return Value +/// +/// Unlike [`yield_blocking`] this function does not return anything. If this +/// component task is cancelled while paused at this yield point then the future +/// will be dropped and a Rust-level destructor will take over and clean up the +/// task. It's not necessary to do anything with the return value of this +/// function other than ensuring that you `.await` the function call. +pub async fn yield_async() { + #[derive(Default)] + struct Yield { + yielded: bool, + } + + impl Future for Yield { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<()> { + if self.yielded { + Poll::Ready(()) + } else { + self.yielded = true; + context.waker().wake_by_ref(); + Poll::Pending + } + } + } + + Yield::default().await; +} + +/// Call the `backpressure.inc` canonical built-in function. +pub fn backpressure_inc() { + extern_wasm! { + #[link(wasm_import_module = "$root")] + unsafe extern "C" { + #[link_name = "[backpressure-inc]"] + fn backpressure_inc(); + } + } + + unsafe { backpressure_inc() } +} + +/// Call the `backpressure.dec` canonical built-in function. +pub fn backpressure_dec() { + extern_wasm! { + #[link(wasm_import_module = "$root")] + unsafe extern "C" { + #[link_name = "[backpressure-dec]"] + fn backpressure_dec(); + } + } + + unsafe { backpressure_dec() } +} + +fn context_get() -> *mut u8 { + extern_wasm! { + #[link(wasm_import_module = "$root")] + unsafe extern "C" { + #[link_name = "[context-get-0]"] + fn get() -> *mut u8; + } + } + + unsafe { get() } +} + +unsafe fn context_set(value: *mut u8) { + extern_wasm! { + #[link(wasm_import_module = "$root")] + unsafe extern "C" { + #[link_name = "[context-set-0]"] + fn set(value: *mut u8); + } + } + + unsafe { set(value) } +} + +#[doc(hidden)] +pub struct TaskCancelOnDrop { + _priv: (), +} + +impl TaskCancelOnDrop { + #[doc(hidden)] + pub fn new() -> TaskCancelOnDrop { + TaskCancelOnDrop { _priv: () } + } + + #[doc(hidden)] + pub fn forget(self) { + mem::forget(self); + } +} + +impl Drop for TaskCancelOnDrop { + fn drop(&mut self) { + extern_wasm! { + #[link(wasm_import_module = "[export]$root")] + unsafe extern "C" { + #[link_name = "[task-cancel]"] + fn cancel(); + } + } + + unsafe { cancel() } + } +} diff --git a/tools/vendor/wit-bindgen/src/rt/async_support/abi_buffer.rs b/tools/vendor/wit-bindgen/src/rt/async_support/abi_buffer.rs new file mode 100644 index 0000000000..5179ddbcc0 --- /dev/null +++ b/tools/vendor/wit-bindgen/src/rt/async_support/abi_buffer.rs @@ -0,0 +1,420 @@ +use crate::rt::Cleanup; +use crate::rt::async_support::StreamOps; +use std::alloc::Layout; +use std::mem::{self, MaybeUninit}; +use std::ptr; +use std::vec::Vec; + +/// A helper structure used with a stream to handle the canonical ABI +/// representation of lists and track partial writes. +/// +/// This structure is returned whenever a write to a stream completes. This +/// keeps track of the original buffer used to perform a write (`Vec`) and +/// additionally tracks any partial writes. Writes can then be resumed with +/// this buffer again or the partial write can be converted back to `Vec` to +/// get access to the remaining values. +/// +/// This value is created through the [`StreamWrite`](super::StreamWrite) +/// future's return value. +pub struct AbiBuffer { + rust_storage: Vec>, + ops: O, + alloc: Option, + cursor: usize, +} + +impl AbiBuffer { + pub(crate) fn new(mut vec: Vec, mut ops: O) -> AbiBuffer { + // SAFETY: We're converting `Vec` to `Vec>`, which + // should be safe. + let rust_storage = unsafe { + let ptr = vec.as_mut_ptr(); + let len = vec.len(); + let cap = vec.capacity(); + mem::forget(vec); + Vec::>::from_raw_parts(ptr.cast(), len, cap) + }; + + // If `lower` is provided then the canonical ABI format is different + // from the native format, so all items are converted at this time. + // + // Note that this is probably pretty inefficient for "big" use cases + // but it's hoped that "big" use cases are using `u8` and therefore + // skip this entirely. + let alloc = if ops.native_abi_matches_canonical_abi() { + None + } else { + let elem_layout = ops.elem_layout(); + let layout = Layout::from_size_align( + elem_layout.size() * rust_storage.len(), + elem_layout.align(), + ) + .unwrap(); + let (mut ptr, cleanup) = Cleanup::new(layout); + // SAFETY: All items in `rust_storage` are already initialized so + // it should be safe to read them and move ownership into the + // canonical ABI format. + unsafe { + for item in rust_storage.iter() { + let item = item.assume_init_read(); + ops.lower(item, ptr); + ptr = ptr.add(elem_layout.size()); + } + } + cleanup + }; + AbiBuffer { + rust_storage, + alloc, + ops, + cursor: 0, + } + } + + /// Returns the canonical ABI pointer/length to pass off to a write + /// operation. + pub(crate) fn abi_ptr_and_len(&self) -> (*const u8, usize) { + // If there's no `lower` operation then it means that `T`'s layout is + // the same in the canonical ABI so it can be used as-is. In this + // situation the list would have been un-tampered with above. + if self.ops.native_abi_matches_canonical_abi() { + // SAFETY: this should be in-bounds, so it should be safe. + let ptr = unsafe { self.rust_storage.as_ptr().add(self.cursor).cast() }; + let len = self.rust_storage.len() - self.cursor; + return (ptr, len.try_into().unwrap()); + } + + // Othereise when `lower` is present that means that `self.alloc` has + // the ABI pointer we should pass along. + let ptr = self + .alloc + .as_ref() + .map(|c| c.ptr.as_ptr()) + .unwrap_or(ptr::null_mut()); + ( + // SAFETY: this should be in-bounds, so it should be safe. + unsafe { ptr.add(self.cursor * self.ops.elem_layout().size()) }, + self.rust_storage.len() - self.cursor, + ) + } + + /// Converts this `AbiBuffer` back into a `Vec` + /// + /// This commit consumes this buffer and yields back unwritten values as a + /// `Vec`. The remaining items in `Vec` have not yet been written and + /// all written items have been removed from the front of the list. + /// + /// Note that the backing storage of the returned `Vec` has not changed + /// from whe this buffer was created. + /// + /// Also note that this can be an expensive operation if a partial write + /// occurred as this will involve shifting items from the end of the vector + /// to the start of the vector. + pub fn into_vec(mut self) -> Vec { + self.take_vec() + } + + /// Returns the number of items remaining in this buffer. + pub fn remaining(&self) -> usize { + self.rust_storage.len() - self.cursor + } + + /// Advances this buffer by `amt` items. + /// + /// This signals that `amt` items are no longer going to be yielded from + /// `abi_ptr_and_len`. Additionally this will perform any deallocation + /// necessary for the starting `amt` items in this list. + pub(crate) fn advance(&mut self, amt: usize) { + assert!(amt + self.cursor <= self.rust_storage.len()); + if !self.ops.contains_lists() { + self.cursor += amt; + return; + } + let (mut ptr, len) = self.abi_ptr_and_len(); + assert!(amt <= len); + for _ in 0..amt { + // SAFETY: we're managing the pointer passed to `dealloc_lists` and + // it was initialized with a `lower`, and then the pointer + // arithmetic should all be in-bounds. + unsafe { + self.ops.dealloc_lists(ptr.cast_mut()); + ptr = ptr.add(self.ops.elem_layout().size()); + } + } + self.cursor += amt; + } + + fn take_vec(&mut self) -> Vec { + // First, if necessary, convert remaining values within `self.alloc` + // back into `self.rust_storage`. This is necessary when a lift + // operation is available meaning that the representation of `T` is + // different in the canonical ABI. + // + // Note that when `lift` is provided then when this original + // `AbiBuffer` was created it moved ownership of all values from the + // original vector into the `alloc` value. This is the reverse + // operation, moving all the values back into the vector. + if !self.ops.native_abi_matches_canonical_abi() { + let (mut ptr, mut len) = self.abi_ptr_and_len(); + // SAFETY: this should be safe as `lift` is operating on values that + // were initialized with a previous `lower`, and the pointer + // arithmetic here should all be in-bounds. + unsafe { + for dst in self.rust_storage[self.cursor..].iter_mut() { + dst.write(self.ops.lift(ptr.cast_mut())); + ptr = ptr.add(self.ops.elem_layout().size()); + len -= 1; + } + assert_eq!(len, 0); + } + } + + // Next extract the rust storage and zero out this struct's fields. + // This is also the location where a "shift" happens to remove items + // from the beginning of the returned vector as those have already been + // transferred somewhere else. + let mut storage = mem::take(&mut self.rust_storage); + storage.drain(..self.cursor); + self.cursor = 0; + self.alloc = None; + + // SAFETY: we're casting `Vec>` here to `Vec`. The + // elements were either always initialized (`lift` is `None`) or we just + // re-initialized them above from `self.alloc`. + unsafe { + let ptr = storage.as_mut_ptr(); + let len = storage.len(); + let cap = storage.capacity(); + mem::forget(storage); + Vec::::from_raw_parts(ptr.cast(), len, cap) + } + } +} + +impl Drop for AbiBuffer +where + O: StreamOps, +{ + fn drop(&mut self) { + let _ = self.take_vec(); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::rt::async_support::StreamVtable; + use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; + use std::vec; + + extern "C" fn cancel(_: u32) -> u32 { + todo!() + } + extern "C" fn drop(_: u32) { + todo!() + } + extern "C" fn new() -> u64 { + todo!() + } + extern "C" fn start_read(_: u32, _: *mut u8, _: usize) -> u32 { + todo!() + } + extern "C" fn start_write(_: u32, _: *const u8, _: usize) -> u32 { + todo!() + } + + static BLANK: StreamVtable = StreamVtable { + cancel_read: cancel, + cancel_write: cancel, + drop_readable: drop, + drop_writable: drop, + dealloc_lists: None, + lift: None, + lower: None, + layout: unsafe { Layout::from_size_align_unchecked(1, 1) }, + new, + start_read, + start_write, + }; + + #[test] + fn blank_advance_to_end() { + let mut buffer = AbiBuffer::new(vec![1, 2, 3, 4], &BLANK); + assert_eq!(buffer.remaining(), 4); + buffer.advance(1); + assert_eq!(buffer.remaining(), 3); + buffer.advance(2); + assert_eq!(buffer.remaining(), 1); + buffer.advance(1); + assert_eq!(buffer.remaining(), 0); + assert_eq!(buffer.into_vec(), []); + } + + #[test] + fn blank_advance_partial() { + let buffer = AbiBuffer::new(vec![1, 2, 3, 4], &BLANK); + assert_eq!(buffer.into_vec(), [1, 2, 3, 4]); + let mut buffer = AbiBuffer::new(vec![1, 2, 3, 4], &BLANK); + buffer.advance(1); + assert_eq!(buffer.into_vec(), [2, 3, 4]); + let mut buffer = AbiBuffer::new(vec![1, 2, 3, 4], &BLANK); + buffer.advance(1); + buffer.advance(2); + assert_eq!(buffer.into_vec(), [4]); + } + + #[test] + fn blank_ptr_eq() { + let mut buf = vec![1, 2, 3, 4]; + let ptr = buf.as_mut_ptr(); + let mut buffer = AbiBuffer::new(buf, &BLANK); + let (a, b) = buffer.abi_ptr_and_len(); + assert_eq!(a, ptr); + assert_eq!(b, 4); + unsafe { + assert_eq!(std::slice::from_raw_parts(a, b), [1, 2, 3, 4]); + } + + buffer.advance(1); + let (a, b) = buffer.abi_ptr_and_len(); + assert_eq!(a, ptr.wrapping_add(1)); + assert_eq!(b, 3); + unsafe { + assert_eq!(std::slice::from_raw_parts(a, b), [2, 3, 4]); + } + + buffer.advance(2); + let (a, b) = buffer.abi_ptr_and_len(); + assert_eq!(a, ptr.wrapping_add(3)); + assert_eq!(b, 1); + unsafe { + assert_eq!(std::slice::from_raw_parts(a, b), [4]); + } + + let ret = buffer.into_vec(); + assert_eq!(ret, [4]); + assert_eq!(ret.as_ptr(), ptr); + } + + #[derive(PartialEq, Eq, Debug)] + struct B(u8); + + static OP: StreamVtable = StreamVtable { + cancel_read: cancel, + cancel_write: cancel, + drop_readable: drop, + drop_writable: drop, + dealloc_lists: Some(|_ptr| {}), + lift: Some(|ptr| unsafe { B(*ptr - 1) }), + lower: Some(|b, ptr| unsafe { + *ptr = b.0 + 1; + }), + layout: unsafe { Layout::from_size_align_unchecked(1, 1) }, + new, + start_read, + start_write, + }; + + #[test] + fn op_advance_to_end() { + let mut buffer = AbiBuffer::new(vec![B(1), B(2), B(3), B(4)], &OP); + assert_eq!(buffer.remaining(), 4); + buffer.advance(1); + assert_eq!(buffer.remaining(), 3); + buffer.advance(2); + assert_eq!(buffer.remaining(), 1); + buffer.advance(1); + assert_eq!(buffer.remaining(), 0); + assert_eq!(buffer.into_vec(), []); + } + + #[test] + fn op_advance_partial() { + let buffer = AbiBuffer::new(vec![B(1), B(2), B(3), B(4)], &OP); + assert_eq!(buffer.into_vec(), [B(1), B(2), B(3), B(4)]); + let mut buffer = AbiBuffer::new(vec![B(1), B(2), B(3), B(4)], &OP); + buffer.advance(1); + assert_eq!(buffer.into_vec(), [B(2), B(3), B(4)]); + let mut buffer = AbiBuffer::new(vec![B(1), B(2), B(3), B(4)], &OP); + buffer.advance(1); + buffer.advance(2); + assert_eq!(buffer.into_vec(), [B(4)]); + } + + #[test] + fn op_ptrs() { + let mut buf = vec![B(1), B(2), B(3), B(4)]; + let ptr = buf.as_mut_ptr().cast::(); + let mut buffer = AbiBuffer::new(buf, &OP); + let (a, b) = buffer.abi_ptr_and_len(); + let base = a; + assert_ne!(a, ptr); + assert_eq!(b, 4); + unsafe { + assert_eq!(std::slice::from_raw_parts(a, b), [2, 3, 4, 5]); + } + + buffer.advance(1); + let (a, b) = buffer.abi_ptr_and_len(); + assert_ne!(a, ptr.wrapping_add(1)); + assert_eq!(a, base.wrapping_add(1)); + assert_eq!(b, 3); + unsafe { + assert_eq!(std::slice::from_raw_parts(a, b), [3, 4, 5]); + } + + buffer.advance(2); + let (a, b) = buffer.abi_ptr_and_len(); + assert_ne!(a, ptr.wrapping_add(3)); + assert_eq!(a, base.wrapping_add(3)); + assert_eq!(b, 1); + unsafe { + assert_eq!(std::slice::from_raw_parts(a, b), [5]); + } + + let ret = buffer.into_vec(); + assert_eq!(ret, [B(4)]); + assert_eq!(ret.as_ptr(), ptr.cast()); + } + + #[test] + fn dealloc_lists() { + static DEALLOCS: AtomicUsize = AtomicUsize::new(0); + static OP: StreamVtable = StreamVtable { + cancel_read: cancel, + cancel_write: cancel, + drop_readable: drop, + drop_writable: drop, + dealloc_lists: Some(|ptr| { + let prev = DEALLOCS.fetch_add(1, Relaxed); + assert_eq!(unsafe { usize::from(*ptr) }, prev + 1); + }), + lift: Some(|ptr| unsafe { B(*ptr) }), + lower: Some(|b, ptr| unsafe { + *ptr = b.0; + }), + layout: unsafe { Layout::from_size_align_unchecked(1, 1) }, + new, + start_read, + start_write, + }; + + assert_eq!(DEALLOCS.load(Relaxed), 0); + let buf = vec![B(1), B(2), B(3), B(4)]; + let mut buffer = AbiBuffer::new(buf, &OP); + assert_eq!(DEALLOCS.load(Relaxed), 0); + buffer.abi_ptr_and_len(); + assert_eq!(DEALLOCS.load(Relaxed), 0); + + buffer.advance(1); + assert_eq!(DEALLOCS.load(Relaxed), 1); + buffer.abi_ptr_and_len(); + assert_eq!(DEALLOCS.load(Relaxed), 1); + buffer.advance(2); + assert_eq!(DEALLOCS.load(Relaxed), 3); + buffer.abi_ptr_and_len(); + assert_eq!(DEALLOCS.load(Relaxed), 3); + buffer.into_vec(); + assert_eq!(DEALLOCS.load(Relaxed), 3); + } +} diff --git a/tools/vendor/wit-bindgen/src/rt/async_support/cabi.rs b/tools/vendor/wit-bindgen/src/rt/async_support/cabi.rs new file mode 100644 index 0000000000..d696d1e55a --- /dev/null +++ b/tools/vendor/wit-bindgen/src/rt/async_support/cabi.rs @@ -0,0 +1,107 @@ +//! Definition of the "C ABI" of how imported functions interact with exported +//! tasks. +//! +//! Ok this crate is written in Rust, why in the world does this exist? This +//! comment is intended to explain this rationale but the tl;dr; is we want +//! this to work: +//! +//! * Within a single component ... +//! * One rust crate uses `wit-bindgen 0.A.0` to generate an exported function. +//! * One rust crate uses `wit-bindgen 0.B.0` to bind an imported function. +//! * The two crates are connected in the application with +//! `std::future::Future`. +//! +//! Without this module this situation won't work because 0.A.0 has no +//! knowledge of 0.B.0 meaning that 0.B.0 has no means of inserting a `waitable` +//! into the `waitable-set` managed by 0.A.0's export. +//! +//! To solve this problem the long-term intention is that something will live +//! in `wasi-libc` itself, but in the meantime it's living "somewhere" within +//! `wit-bindgen 0.*.0`. Specifically all `wit-bindgen` versions will +//! reference, via C linkage, a single function which is used to manipulate a +//! single pointer in linear memory. This pointer is a `wasip3_task` structure +//! which has all the various fields to use it. +//! +//! The `wasip3_task_set` symbol is itself defined in C inside of the +//! `src/wit_bindgen_cabi.c` file at this time, specifically because it's +//! annotated with `__weak__` meaning that any definition of it suffices. This +//! isn't possible to define in stable Rust (specifically `__weak__`). +//! +//! Once `wasip3_task_set` is defined everything then operates via indirection, +//! aka based off the returned pointer. The intention is that exported functions +//! will set this (it's sort of like an executor) and then imported functions +//! will all use this as the source of registering waitables. In the end that +//! means that it's possible to share types with `std::future::Future` that +//! are backed at the ABI level with this "channel". +//! +//! In the future it's hoped that this can move into `wasi-libc` itself, or if +//! `wasi-libc` provides something else that would be prioritized over this. +//! For now this is basically an affordance that we're going to be frequently +//! releaseing new major versions of `wit-bindgen` and we don't want to force +//! applications to all be using the exact same version of the bindings +//! generator and async bindings. +//! +//! Additionally for now this file is serving as documentation of this +//! interface. + +use core::ffi::c_void; + +extern_wasm! { + unsafe extern "C" { + /// Sets the global task pointer to `ptr` provided. Returns the previous + /// value. + /// + /// This function acts as a dual getter and a setter. To get the + /// current task pointer a dummy `ptr` can be provided (e.g. NULL) and then + /// it's passed back when you're done working with it. When setting the + /// current task pointer it's recommended to call this and then call it + /// again with the previous value when the tasks's work is done. + /// + /// For executors they need to ensure that the `ptr` passed in lives for + /// the entire lifetime of the component model task. + pub fn wasip3_task_set(ptr: *mut wasip3_task) -> *mut wasip3_task; + } +} + +/// The first version of `wasip3_task` which implies the existence of the +/// fields `ptr`, `waitable_register`, and `waitable_unregister`. +pub const WASIP3_TASK_V1: u32 = 1; + +/// Indirect "vtable" used to connect imported functions and exported tasks. +/// Executors (e.g. exported functions) define and manage this while imports +/// use it. +#[repr(C)] +pub struct wasip3_task { + /// Currently `WASIP3_TASK_V1`. Indicates what fields are present next + /// depending on the version here. + pub version: u32, + + /// Private pointer owned by the `wasip3_task` itself, passed to callbacks + /// below as the first argument. + pub ptr: *mut c_void, + + /// Register a new `waitable` for this exported task. + /// + /// This exported task will add `waitable` to its `waitable-set`. When it + /// becomes ready then `callback` will be invoked with the ready code as + /// well as the `callback_ptr` provided. + /// + /// If `waitable` was previously registered with this task then the + /// previous `callback_ptr` is returned. Otherwise `NULL` is returned. + /// + /// It's the caller's responsibility to ensure that `callback_ptr` is valid + /// until `callback` is invoked, `waitable_unregister` is invoked, or + /// `waitable_register` is called again to overwrite the value. + pub waitable_register: unsafe extern "C" fn( + ptr: *mut c_void, + waitable: u32, + callback: unsafe extern "C" fn(callback_ptr: *mut c_void, code: u32), + callback_ptr: *mut c_void, + ) -> *mut c_void, + + /// Removes the `waitable` from this task's `waitable-set`. + /// + /// Returns the `callback_ptr` passed to `waitable_register` if present, or + /// `NULL` if it's not present. + pub waitable_unregister: unsafe extern "C" fn(ptr: *mut c_void, waitable: u32) -> *mut c_void, +} diff --git a/tools/vendor/wit-bindgen/src/rt/async_support/error_context.rs b/tools/vendor/wit-bindgen/src/rt/async_support/error_context.rs new file mode 100644 index 0000000000..a9fc4faf3b --- /dev/null +++ b/tools/vendor/wit-bindgen/src/rt/async_support/error_context.rs @@ -0,0 +1,83 @@ +//! Raw bindings to `error-context` in the canonical ABI. + +use std::fmt::{self, Debug, Display}; +use std::ptr; +use std::string::String; + +/// Represents the Component Model `error-context` type. +#[derive(PartialEq, Eq)] +pub struct ErrorContext { + handle: u32, +} + +impl ErrorContext { + /// Call the `error-context.new` canonical built-in function. + pub fn new(debug_message: &str) -> ErrorContext { + unsafe { + let handle = new(debug_message.as_ptr(), debug_message.len()); + ErrorContext::from_handle(handle) + } + } + + #[doc(hidden)] + pub fn from_handle(handle: u32) -> Self { + Self { handle } + } + + #[doc(hidden)] + pub fn handle(&self) -> u32 { + self.handle + } + + /// Extract the debug message from a given [`ErrorContext`] + pub fn debug_message(&self) -> String { + unsafe { + let mut ret = RetPtr { + ptr: ptr::null_mut(), + len: 0, + }; + debug_message(self.handle, &mut ret); + String::from_raw_parts(ret.ptr, ret.len, ret.len) + } + } +} + +impl Debug for ErrorContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ErrorContext") + .field("debug_message", &self.debug_message()) + .finish() + } +} + +impl Display for ErrorContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&self.debug_message(), f) + } +} + +impl std::error::Error for ErrorContext {} + +impl Drop for ErrorContext { + fn drop(&mut self) { + unsafe { drop(self.handle) } + } +} + +#[repr(C)] +struct RetPtr { + ptr: *mut u8, + len: usize, +} + +extern_wasm! { + #[link(wasm_import_module = "$root")] + unsafe extern "C" { + #[link_name = "[error-context-new-utf8]"] + fn new(_: *const u8, _: usize) -> u32; + #[link_name = "[error-context-drop]"] + fn drop(_: u32); + #[link_name = "[error-context-debug-message-utf8]"] + fn debug_message(_: u32, _: &mut RetPtr); + } +} diff --git a/tools/vendor/wit-bindgen/src/rt/async_support/future_support.rs b/tools/vendor/wit-bindgen/src/rt/async_support/future_support.rs new file mode 100644 index 0000000000..54eeddb5a1 --- /dev/null +++ b/tools/vendor/wit-bindgen/src/rt/async_support/future_support.rs @@ -0,0 +1,994 @@ +//! Runtime support for `future` in the component model. +//! +//! There are a number of tricky concerns to all balance when implementing +//! bindings to `future`, specifically with how it interacts with Rust. This +//! will attempt to go over some of the high-level details of the implementation +//! here. +//! +//! ## Leak safety +//! +//! It's safe to leak any value at any time currently in Rust. In other words +//! Rust doesn't have linear types (yet). Typically this isn't really a problem +//! but the component model intrinsics we're working with here operate by being +//! given a pointer and then at some point in the future the pointer may be +//! read. This means that it's our responsibility to keep this pointer alive and +//! valid for the entire duration of an asynchronous operation. +//! +//! Chiefly this means that borrowed values are a no-no in this module. For +//! example if you were to send a `&[u8]` as an implementation of +//! `future>` that would not be sound. For example: +//! +//! * The future send operation is started, recording an address of `&[u8]`. +//! * The future is then leaked. +//! * According to rustc, later in code the original `&[u8]` is then no longer +//! borrowed. +//! * The original source of `&[u8]` could then be deallocated. +//! * Then the component model actually reads the pointer that it was given. +//! +//! This constraint effectively means that all types flowing in-and-out of +//! futures, streams, and async APIs are all "owned values", notably no +//! lifetimes. This requires, for example, that `future>` operates on +//! `Vec`. +//! +//! This is in stark contrast to bindings generated for `list` otherwise, +//! however, where for example a synchronous import with a `list` argument +//! would be bound with a `&[u8]` argument. Until Rust has some form of linear +//! types, however, it's not possible to loosen this restriction soundly because +//! it's generally not safe to leak an active I/O operation. This restriction is +//! similar to why it's so difficult to bind `io_uring` in safe Rust, which +//! operates similarly to the component model where pointers are submitted and +//! read in the future after the original call for submission returns. +//! +//! ## Lowering Owned Values +//! +//! According to the above everything with futures/streams operates on owned +//! values already, but this also affects precisely how lifting and lowering is +//! performed. In general any active asynchronous operation could be cancelled +//! at any time, meaning we have to deal with situations such as: +//! +//! * A `write` hasn't even started yet. +//! * A `write` was started and then cancelled. +//! * A `write` was started and then the other end dropped the channel. +//! * A `write` was started and then the other end received the value. +//! +//! In all of these situations regardless of the structure of `T` we can't leak +//! memory. The `future.write` intrinsic, however, takes no ownership of the +//! memory involved which means that we're still responsible for cleaning up +//! lists. It does take ownership, however, of `own` handles and other +//! resources. +//! +//! The way that this is solved for futures/streams is to lean further into +//! processing owned values. Namely lowering a `T` takes `T`-by-value, not `&T`. +//! This means that lowering operates similarly to return values of exported +//! functions, not parameters to imported functions. By lowering an owned value +//! of `T` this preserves a nice property where the lowered value has exclusive +//! ownership of all of its pointers/resources/etc. Lowering `&T` may require a +//! "cleanup list" for example which we avoid here entirely. +//! +//! This then makes the second and third cases above, getting a value back after +//! lowering, much easier. Namely re-acquisition of a value is simple `lift` +//! operation as if we received a value on the channel. +//! +//! ## Inefficiencies +//! +//! The above requirements generally mean that this is not a hyper-efficient +//! implementation. All writes and reads, for example, start out with allocation +//! memory on the heap to be owned by the asynchronous operation. Writing a +//! `list` to a future passes ownership of `Vec` but in theory doesn't +//! not actually require relinquishing ownership of the vector. Furthermore +//! there's no way to re-acquire a `T` after it has been sent, but all of `T` is +//! still valid except for `own` resources. +//! +//! That's all to say that this implementation can probably still be improved +//! upon, but doing so is thought to be pretty nontrivial at this time. It +//! should be noted though that there are other high-level inefficiencies with +//! WIT unrelated to this module. For example `list` is not always +//! represented the same in Rust as it is in the canonical ABI. That means that +//! sending `list` into a future might require copying the entire list and +//! changing its layout. Currently this is par-for-the-course with bindings. +//! +//! ## Linear (exactly once) Writes +//! +//! The component model requires that a writable end of a future must be written +//! to before closing, otherwise the drop operation traps. Ideally usage of +//! this API shouldn't result in traps so this is modeled in the Rust-level API +//! to prevent this trap from occurring. Rust does not support linear types +//! (types that must be used exactly once), instead it only has affine types +//! (types which must be used at most once), meaning that this requires some +//! runtime support. +//! +//! Specifically the `FutureWriter` structure stores two auxiliary Rust-specific +//! pieces of information: +//! +//! * A `should_write_default_value` boolean - if `true` on destruction then a +//! value has not yet been written and something must be written. +//! * A `default: fn() -> T` constructor to lazily create the default value to +//! be sent in this situation. +//! +//! This `default` field is provided by the user when the future is initially +//! created. Additionally during `Drop` a new Rust-level task will be spawned to +//! perform the write in the background. That'll keep the component-level task +//! alive until that write completes but otherwise shouldn't hinder anything +//! else. + +use crate::rt::Cleanup; +use crate::rt::async_support::ReturnCode; +use crate::rt::async_support::waitable::{WaitableOp, WaitableOperation}; +use std::alloc::Layout; +use std::fmt; +use std::future::{Future, IntoFuture}; +use std::marker; +use std::mem::{self, ManuallyDrop}; +use std::pin::Pin; +use std::ptr; +use std::sync::atomic::{AtomicU32, Ordering::Relaxed}; +use std::sync::{Arc, Mutex}; +use std::task::{Context, Poll, Wake, Waker}; + +/// Helper trait which encapsulates the various operations which can happen +/// with a future. +pub trait FutureOps { + /// The Rust type that's sent or received on this future. + type Payload; + + /// The `future.new` intrinsic. + fn new(&mut self) -> u64; + /// The canonical ABI layout of the type that this future is + /// sending/receiving. + fn elem_layout(&mut self) -> Layout; + /// Converts a Rust type to its canonical ABI representation. + unsafe fn lower(&mut self, payload: Self::Payload, dst: *mut u8); + /// Used to deallocate any Rust-owned lists in the canonical ABI + /// representation for when a value is successfully sent but needs to be + /// cleaned up. + unsafe fn dealloc_lists(&mut self, dst: *mut u8); + /// Converts from the canonical ABI representation to a Rust value. + unsafe fn lift(&mut self, dst: *mut u8) -> Self::Payload; + /// The `future.write` intrinsic + unsafe fn start_write(&mut self, future: u32, val: *const u8) -> u32; + /// The `future.read` intrinsic + unsafe fn start_read(&mut self, future: u32, val: *mut u8) -> u32; + /// The `future.cancel-read` intrinsic + unsafe fn cancel_read(&mut self, future: u32) -> u32; + /// The `future.cancel-write` intrinsic + unsafe fn cancel_write(&mut self, future: u32) -> u32; + /// The `future.drop-readable` intrinsic + unsafe fn drop_readable(&mut self, future: u32); + /// The `future.drop-writable` intrinsic + unsafe fn drop_writable(&mut self, future: u32); +} + +/// Function table used for [`FutureWriter`] and [`FutureReader`] +/// +/// Instances of this table are generated by `wit_bindgen::generate!`. This is +/// not a trait to enable different `FutureVtable<()>` instances to exist, for +/// example, through different calls to `wit_bindgen::generate!`. +/// +/// It's not intended that any user implements this vtable, instead it's +/// intended to only be auto-generated. +#[doc(hidden)] +pub struct FutureVtable { + /// The Canonical ABI layout of `T` in-memory. + pub layout: Layout, + + /// A callback to consume a value of `T` and lower it to the canonical ABI + /// pointed to by `dst`. + /// + /// The `dst` pointer should have `self.layout`. This is used to convert + /// in-memory representations in Rust to their canonical representations in + /// the component model. + pub lower: unsafe fn(value: T, dst: *mut u8), + + /// A callback to deallocate any lists within the canonical ABI value `dst` + /// provided. + /// + /// This is used when a value is successfully sent to another component. In + /// such a situation it may be possible that the canonical lowering of `T` + /// has lists that are still owned by this component and must be + /// deallocated. This is akin to a `post-return` callback for returns of + /// exported functions. + pub dealloc_lists: unsafe fn(dst: *mut u8), + + /// A callback to lift a value of `T` from the canonical ABI representation + /// provided. + pub lift: unsafe fn(dst: *mut u8) -> T, + + /// The raw `future.write` intrinsic. + pub start_write: unsafe extern "C" fn(future: u32, val: *const u8) -> u32, + /// The raw `future.read` intrinsic. + pub start_read: unsafe extern "C" fn(future: u32, val: *mut u8) -> u32, + /// The raw `future.cancel-write` intrinsic. + pub cancel_write: unsafe extern "C" fn(future: u32) -> u32, + /// The raw `future.cancel-read` intrinsic. + pub cancel_read: unsafe extern "C" fn(future: u32) -> u32, + /// The raw `future.drop-writable` intrinsic. + pub drop_writable: unsafe extern "C" fn(future: u32), + /// The raw `future.drop-readable` intrinsic. + pub drop_readable: unsafe extern "C" fn(future: u32), + /// The raw `future.new` intrinsic. + pub new: unsafe extern "C" fn() -> u64, +} + +impl FutureOps for &FutureVtable { + type Payload = T; + + fn new(&mut self) -> u64 { + unsafe { (self.new)() } + } + fn elem_layout(&mut self) -> Layout { + self.layout + } + unsafe fn lower(&mut self, payload: Self::Payload, dst: *mut u8) { + unsafe { (self.lower)(payload, dst) } + } + unsafe fn dealloc_lists(&mut self, dst: *mut u8) { + unsafe { (self.dealloc_lists)(dst) } + } + unsafe fn lift(&mut self, dst: *mut u8) -> Self::Payload { + unsafe { (self.lift)(dst) } + } + unsafe fn start_write(&mut self, future: u32, val: *const u8) -> u32 { + unsafe { (self.start_write)(future, val) } + } + unsafe fn start_read(&mut self, future: u32, val: *mut u8) -> u32 { + unsafe { (self.start_read)(future, val) } + } + unsafe fn cancel_read(&mut self, future: u32) -> u32 { + unsafe { (self.cancel_read)(future) } + } + unsafe fn cancel_write(&mut self, future: u32) -> u32 { + unsafe { (self.cancel_write)(future) } + } + unsafe fn drop_readable(&mut self, future: u32) { + unsafe { (self.drop_readable)(future) } + } + unsafe fn drop_writable(&mut self, future: u32) { + unsafe { (self.drop_writable)(future) } + } +} + +/// Helper function to create a new read/write pair for a component model +/// future. +/// +/// # Unsafety +/// +/// This function is unsafe as it requires the functions within `vtable` to +/// correctly uphold the contracts of the component model. +pub unsafe fn future_new( + default: fn() -> T, + vtable: &'static FutureVtable, +) -> (FutureWriter, FutureReader) { + let (tx, rx) = unsafe { raw_future_new(vtable) }; + (unsafe { FutureWriter::new(tx, default) }, rx) +} + +/// Helper function to create a new read/write pair for a component model +/// future. +/// +/// # Unsafety +/// +/// This function is unsafe as it requires the functions within `vtable` to +/// correctly uphold the contracts of the component model. +pub unsafe fn raw_future_new(mut ops: O) -> (RawFutureWriter, RawFutureReader) +where + O: FutureOps + Clone, +{ + unsafe { + let handles = ops.new(); + let reader = handles as u32; + let writer = (handles >> 32) as u32; + rtdebug!("future.new() = [{writer}, {reader}]"); + ( + RawFutureWriter::new(writer, ops.clone()), + RawFutureReader::new(reader, ops), + ) + } +} + +/// Represents the writable end of a Component Model `future`. +/// +/// A [`FutureWriter`] can be used to send a single value of `T` to the other +/// end of a `future`. In a sense this is similar to a oneshot channel in Rust. +pub struct FutureWriter { + raw: ManuallyDrop>>, + + /// Whether or not a value should be written during `drop`. + /// + /// This is set to `false` when a value is successfully written or when a + /// value is written but the future is witnessed as being dropped. + /// + /// Note that this is set to `true` on construction to ensure that only + /// location which actually witness a completed write set it to `false`. + should_write_default_value: bool, + + /// Constructor for the default value to write during `drop`, should one + /// need to be written. + default: fn() -> T, +} + +impl FutureWriter { + /// Helper function to wrap a handle/vtable into a `FutureWriter`. + /// + /// # Unsafety + /// + /// This function is unsafe as it requires the functions within `vtable` to + /// correctly uphold the contracts of the component model. + unsafe fn new(raw: RawFutureWriter<&'static FutureVtable>, default: fn() -> T) -> Self { + Self { + raw: ManuallyDrop::new(raw), + default, + should_write_default_value: true, + } + } + + /// Write the specified `value` to this `future`. + /// + /// This method is equivalent to an `async fn` which sends the `value` into + /// this future. The asynchronous operation acts as a rendezvous where the + /// operation does not complete until the other side has successfully + /// received the value. + /// + /// # Return Value + /// + /// The returned [`FutureWrite`] is a future that can be `.await`'d. The + /// return value of this future is: + /// + /// * `Ok(())` - the `value` was sent and received. The `self` value was + /// consumed along the way and will no longer be accessible. + /// * `Err(FutureWriteError { value })` - an attempt was made to send + /// `value` but the other half of this [`FutureWriter`] was dropped before + /// the value was received. This consumes `self` because the channel is + /// now dropped, but `value` is returned in case the caller wants to reuse + /// it. + /// + /// # Cancellation + /// + /// The returned future can be cancelled normally via `drop` which means + /// that the `value` provided here, along with this `FutureWriter` itself, + /// will be lost. There is also [`FutureWrite::cancel`] which can be used to + /// possibly re-acquire `value` and `self` if the operation was cancelled. + /// In such a situation the operation can be retried at a future date. + pub fn write(mut self, value: T) -> FutureWrite { + let raw = unsafe { ManuallyDrop::take(&mut self.raw).write(value) }; + let default = self.default; + mem::forget(self); + FutureWrite { raw, default } + } +} + +impl fmt::Debug for FutureWriter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FutureWriter") + .field("handle", &self.raw.handle) + .finish() + } +} + +impl Drop for FutureWriter { + fn drop(&mut self) { + // If a value has not yet been written into this writer than that must + // be done so now. Take the `raw` writer and perform the write via a + // waker that drives the future. + // + // If `should_write_default_value` is `false` then a write has already + // happened and we can go ahead and just synchronously drop this writer + // as we would any other handle. + if self.should_write_default_value { + let raw = unsafe { ManuallyDrop::take(&mut self.raw) }; + let value = (self.default)(); + raw.write_and_forget(value); + } else { + unsafe { ManuallyDrop::drop(&mut self.raw) } + } + } +} + +/// Represents a write operation which may be cancelled prior to completion. +/// +/// This is returned by [`FutureWriter::write`]. +pub struct FutureWrite { + raw: RawFutureWrite<&'static FutureVtable>, + default: fn() -> T, +} + +/// Result of [`FutureWrite::cancel`]. +#[derive(Debug)] +pub enum FutureWriteCancel { + /// The cancel request raced with the receipt of the sent value, and the + /// value was actually sent. Neither the value nor the writer are made + /// available here as both are gone. + AlreadySent, + + /// The other end was dropped before cancellation happened. + /// + /// In this case the original value is returned back to the caller but the + /// writer itself is not longer accessible as it's no longer usable. + Dropped(T), + + /// The pending write was successfully cancelled and the value being written + /// is returned along with the writer to resume again in the future if + /// necessary. + Cancelled(T, FutureWriter), +} + +impl Future for FutureWrite { + type Output = Result<(), FutureWriteError>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.pin_project().poll(cx) + } +} + +impl FutureWrite { + fn pin_project(self: Pin<&mut Self>) -> Pin<&mut RawFutureWrite<&'static FutureVtable>> { + // SAFETY: we've chosen that when `Self` is pinned that it translates to + // always pinning the inner field, so that's codified here. + unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().raw) } + } + + /// Cancel this write if it hasn't already completed. + /// + /// This method can be used to cancel a write-in-progress and re-acquire + /// the writer and the value being sent. Note that the write operation may + /// succeed racily or the other end may also drop racily, and these + /// outcomes are reflected in the returned value here. + /// + /// # Panics + /// + /// Panics if the operation has already been completed via `Future::poll`, + /// or if this method is called twice. + pub fn cancel(self: Pin<&mut Self>) -> FutureWriteCancel { + let default = self.default; + match self.pin_project().cancel() { + RawFutureWriteCancel::AlreadySent => FutureWriteCancel::AlreadySent, + RawFutureWriteCancel::Dropped(val) => FutureWriteCancel::Dropped(val), + RawFutureWriteCancel::Cancelled(val, raw) => FutureWriteCancel::Cancelled( + val, + FutureWriter { + raw: ManuallyDrop::new(raw), + default, + should_write_default_value: true, + }, + ), + } + } +} + +impl Drop for FutureWrite { + fn drop(&mut self) { + if self.raw.op.is_done() { + return; + } + + // Although the underlying `WaitableOperation` will already + // auto-cancel-on-drop we need to specially handle that here because if + // the cancellation goes through then it means that no value will have + // been written to this future which will cause a trap. By using + // `Self::cancel` it's ensured that if cancellation succeeds a + // `FutureWriter` is created. In `Drop for FutureWriter` that'll handle + // the last-ditch write-default logic. + // + // SAFETY: we're in the destructor here so the value `self` is about + // to go away and we can guarantee we're not moving out of it. + let pin = unsafe { Pin::new_unchecked(self) }; + pin.cancel(); + } +} + +/// Raw version of [`FutureWriter`]. +pub struct RawFutureWriter { + handle: u32, + ops: O, +} + +impl RawFutureWriter { + unsafe fn new(handle: u32, ops: O) -> Self { + Self { handle, ops } + } + + /// Same as [`FutureWriter::write`], but the raw version. + pub fn write(self, value: O::Payload) -> RawFutureWrite { + RawFutureWrite { + op: WaitableOperation::new(FutureWriteOp(marker::PhantomData), (self, value)), + } + } + + /// Writes `value` in the background. + /// + /// This does not block and is not cancellable. + pub fn write_and_forget(self, value: O::Payload) + where + O: 'static, + { + return Arc::new(DeferredWrite { + write: Mutex::new(self.write(value)), + }) + .wake(); + + /// Helper structure which behaves both as a future of sorts and an + /// executor of sorts. + /// + /// This type is constructed in `Drop for FutureWriter` to send out a + /// default value when no other has been written. This manages the + /// `FutureWrite` operation happening internally through a `Wake` + /// implementation. That means that this is a sort of cyclical future + /// which, when woken, will complete the write operation. + /// + /// The purpose of this is to be a "lightweight" way of "spawn"-ing a + /// future write to happen in the background. Crucially, however, this + /// doesn't require the `async-spawn` feature and instead works with the + /// `wasip3_task` C ABI structures (which spawn doesn't support). + struct DeferredWrite { + write: Mutex>, + } + + // SAFETY: Needed to satisfy `Waker::from` but otherwise should be ok + // because wasm doesn't have threads anyway right now. + unsafe impl Send for DeferredWrite {} + unsafe impl Sync for DeferredWrite {} + + impl Wake for DeferredWrite { + fn wake(self: Arc) { + // When a `wake` signal comes in that should happen in two + // locations: + // + // 1. When `DeferredWrite` is initially constructed. + // 2. When an event comes in indicating that the internal write + // has completed. + // + // The implementation here is the same in both cases. A clone of + // `self` is converted to a `Waker`, and this `Waker` notably + // owns the internal future itself. The internal write operation + // is then pushed forward (e.g. it's issued in (1) or checked up + // on in (2)). + // + // If `Pending` is returned then `waker` should have been stored + // away within the `wasip3_task` C ABI structure. Otherwise it + // should not have been stored away and `self` should be the + // sole reference which means everything will get cleaned up + // when this function returns. + let poll = { + let waker = Waker::from(self.clone()); + let mut cx = Context::from_waker(&waker); + let mut write = self.write.lock().unwrap(); + unsafe { Pin::new_unchecked(&mut *write).poll(&mut cx) } + }; + if poll.is_ready() { + assert_eq!(Arc::strong_count(&self), 1); + } else { + assert!(Arc::strong_count(&self) > 1); + } + assert_eq!(Arc::weak_count(&self), 0); + } + } + } +} + +impl fmt::Debug for RawFutureWriter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawFutureWriter") + .field("handle", &self.handle) + .finish() + } +} + +impl Drop for RawFutureWriter { + fn drop(&mut self) { + unsafe { + rtdebug!("future.drop-writable({})", self.handle); + self.ops.drop_writable(self.handle); + } + } +} + +/// Represents a write operation which may be cancelled prior to completion. +/// +/// This is returned by [`FutureWriter::write`]. +pub struct RawFutureWrite { + op: WaitableOperation>, +} + +struct FutureWriteOp(marker::PhantomData); + +enum WriteComplete { + Written, + Dropped(T), + Cancelled(T), +} + +unsafe impl WaitableOp for FutureWriteOp { + type Start = (RawFutureWriter, O::Payload); + type InProgress = (RawFutureWriter, Option); + type Result = (WriteComplete, RawFutureWriter); + type Cancel = RawFutureWriteCancel; + + fn start(&mut self, (mut writer, value): Self::Start) -> (u32, Self::InProgress) { + // TODO: it should be safe to store the lower-destination in + // `WaitableOperation` using `Pin` memory and such, but that would + // require some type-level trickery to get a correctly-sized value + // plumbed all the way to here. For now just dynamically allocate it and + // leave the optimization of leaving out this dynamic allocation to the + // future. + // + // In lieu of that a dedicated location on the heap is created for the + // lowering, and then `value`, as an owned value, is lowered into this + // pointer to initialize it. + let (ptr, cleanup) = Cleanup::new(writer.ops.elem_layout()); + // SAFETY: `ptr` is allocated with `ops.layout` and should be + // safe to use here. + let code = unsafe { + writer.ops.lower(value, ptr); + writer.ops.start_write(writer.handle, ptr) + }; + rtdebug!("future.write({}, {ptr:?}) = {code:#x}", writer.handle); + (code, (writer, cleanup)) + } + + fn start_cancelled(&mut self, (writer, value): Self::Start) -> Self::Cancel { + RawFutureWriteCancel::Cancelled(value, writer) + } + + fn in_progress_update( + &mut self, + (mut writer, cleanup): Self::InProgress, + code: u32, + ) -> Result { + let ptr = cleanup + .as_ref() + .map(|c| c.ptr.as_ptr()) + .unwrap_or(ptr::null_mut()); + match code { + super::BLOCKED => Err((writer, cleanup)), + + // The other end has dropped its end. + // + // The value was not received by the other end so `ptr` still has + // all of its resources intact. Use `lift` to construct a new + // instance of `T` which takes ownership of pointers and resources + // and such. The allocation of `ptr` is then cleaned up naturally + // when `cleanup` goes out of scope. + super::DROPPED | super::CANCELLED => { + // SAFETY: we're the ones managing `ptr` so we know it's safe to + // pass here. + let value = unsafe { writer.ops.lift(ptr) }; + let status = if code == super::DROPPED { + WriteComplete::Dropped(value) + } else { + WriteComplete::Cancelled(value) + }; + Ok((status, writer)) + } + + // This write has completed. + // + // Here we need to clean up our allocations. The `ptr` exclusively + // owns all of the value being sent and we notably need to cleanup + // the transitive list allocations present in this pointer. Use + // `dealloc_lists` for that (effectively a post-return lookalike). + // + // Afterwards the `cleanup` itself is naturally dropped and cleaned + // up. + super::COMPLETED => { + // SAFETY: we're the ones managing `ptr` so we know it's safe to + // pass here. + unsafe { + writer.ops.dealloc_lists(ptr); + } + Ok((WriteComplete::Written, writer)) + } + + other => unreachable!("unexpected code {other:?}"), + } + } + + fn in_progress_waitable(&mut self, (writer, _): &Self::InProgress) -> u32 { + writer.handle + } + + fn in_progress_cancel(&mut self, (writer, _): &mut Self::InProgress) -> u32 { + // SAFETY: we're managing `writer` and all the various operational bits, + // so this relies on `WaitableOperation` being safe. + let code = unsafe { writer.ops.cancel_write(writer.handle) }; + rtdebug!("future.cancel-write({}) = {code:#x}", writer.handle); + code + } + + fn result_into_cancel(&mut self, (result, writer): Self::Result) -> Self::Cancel { + match result { + // The value was actually sent, meaning we can't yield back the + // future nor the value. + WriteComplete::Written => RawFutureWriteCancel::AlreadySent, + + // The value was not sent because the other end either hung up or we + // successfully cancelled. In both cases return back the value here + // with the writer. + WriteComplete::Dropped(val) => RawFutureWriteCancel::Dropped(val), + WriteComplete::Cancelled(val) => RawFutureWriteCancel::Cancelled(val, writer), + } + } +} + +impl Future for RawFutureWrite { + type Output = Result<(), FutureWriteError>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.pin_project() + .poll_complete(cx) + .map(|(result, _writer)| match result { + WriteComplete::Written => Ok(()), + WriteComplete::Dropped(value) | WriteComplete::Cancelled(value) => { + Err(FutureWriteError { value }) + } + }) + } +} + +impl RawFutureWrite { + fn pin_project(self: Pin<&mut Self>) -> Pin<&mut WaitableOperation>> { + // SAFETY: we've chosen that when `Self` is pinned that it translates to + // always pinning the inner field, so that's codified here. + unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().op) } + } + + /// Same as [`FutureWrite::cancel`], but returns a [`RawFutureWriteCancel`] + /// instead. + pub fn cancel(self: Pin<&mut Self>) -> RawFutureWriteCancel { + self.pin_project().cancel() + } +} + +/// Error type in the result of [`FutureWrite`], or the error type that is a result of +/// a failure to write a future. +pub struct FutureWriteError { + /// The value that could not be sent. + pub value: T, +} + +impl fmt::Debug for FutureWriteError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FutureWriteError").finish_non_exhaustive() + } +} + +impl fmt::Display for FutureWriteError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "read end dropped".fmt(f) + } +} + +impl std::error::Error for FutureWriteError {} + +/// Result of [`FutureWrite::cancel`]. +#[derive(Debug)] +pub enum RawFutureWriteCancel { + /// The cancel request raced with the receipt of the sent value, and the + /// value was actually sent. Neither the value nor the writer are made + /// available here as both are gone. + AlreadySent, + + /// The other end was dropped before cancellation happened. + /// + /// In this case the original value is returned back to the caller but the + /// writer itself is not longer accessible as it's no longer usable. + Dropped(O::Payload), + + /// The pending write was successfully cancelled and the value being written + /// is returned along with the writer to resume again in the future if + /// necessary. + Cancelled(O::Payload, RawFutureWriter), +} + +/// Represents the readable end of a Component Model `future`. +pub type FutureReader = RawFutureReader<&'static FutureVtable>; + +/// Represents the readable end of a Component Model `future`. +pub struct RawFutureReader { + handle: AtomicU32, + ops: O, +} + +impl fmt::Debug for RawFutureReader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RawFutureReader") + .field("handle", &self.handle) + .finish() + } +} + +impl RawFutureReader { + /// Raw constructor for a future reader. + /// + /// Takes ownership of the `handle` provided. + /// + /// # Safety + /// + /// The `ops` specified must be both valid and well-typed for `handle`. + pub unsafe fn new(handle: u32, ops: O) -> Self { + Self { + handle: AtomicU32::new(handle), + ops, + } + } + + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + let ret = self.opt_handle().unwrap(); + self.handle.store(u32::MAX, Relaxed); + ret + } + + fn handle(&self) -> u32 { + self.opt_handle().unwrap() + } + + fn opt_handle(&self) -> Option { + match self.handle.load(Relaxed) { + u32::MAX => None, + other => Some(other), + } + } +} + +impl IntoFuture for RawFutureReader { + type Output = O::Payload; + type IntoFuture = RawFutureRead; + + /// Convert this object into a `Future` which will resolve when a value is + /// written to the writable end of this `future`. + fn into_future(self) -> Self::IntoFuture { + RawFutureRead { + op: WaitableOperation::new(FutureReadOp(marker::PhantomData), self), + } + } +} + +impl Drop for RawFutureReader { + fn drop(&mut self) { + let Some(handle) = self.opt_handle() else { + return; + }; + unsafe { + rtdebug!("future.drop-readable({handle})"); + self.ops.drop_readable(handle); + } + } +} + +/// Represents a read operation which may be cancelled prior to completion. +/// +/// This represents a read operation on a [`FutureReader`] and is created via +/// `IntoFuture`. +pub type FutureRead = RawFutureRead<&'static FutureVtable>; + +/// Represents a read operation which may be cancelled prior to completion. +/// +/// This represents a read operation on a [`FutureReader`] and is created via +/// `IntoFuture`. +pub struct RawFutureRead { + op: WaitableOperation>, +} + +struct FutureReadOp(marker::PhantomData); + +enum ReadComplete { + Value(T), + Cancelled, +} + +unsafe impl WaitableOp for FutureReadOp { + type Start = RawFutureReader; + type InProgress = (RawFutureReader, Option); + type Result = (ReadComplete, RawFutureReader); + type Cancel = Result>; + + fn start(&mut self, mut reader: Self::Start) -> (u32, Self::InProgress) { + let (ptr, cleanup) = Cleanup::new(reader.ops.elem_layout()); + // SAFETY: `ptr` is allocated with `vtable.layout` and should be + // safe to use here. Its lifetime for the async operation is hinged on + // `WaitableOperation` being safe. + let code = unsafe { reader.ops.start_read(reader.handle(), ptr) }; + rtdebug!("future.read({}, {ptr:?}) = {code:#x}", reader.handle()); + (code, (reader, cleanup)) + } + + fn start_cancelled(&mut self, state: Self::Start) -> Self::Cancel { + Err(state) + } + + fn in_progress_update( + &mut self, + (mut reader, cleanup): Self::InProgress, + code: u32, + ) -> Result { + match ReturnCode::decode(code) { + ReturnCode::Blocked => Err((reader, cleanup)), + + // Let `cleanup` fall out of scope to clean up its allocation here, + // and otherwise tahe reader is plumbed through to possibly restart + // the read in the future. + ReturnCode::Cancelled(0) => Ok((ReadComplete::Cancelled, reader)), + + // The read has completed, so lift the value from the stored memory and + // `cleanup` naturally falls out of scope after transferring ownership of + // everything to the returned `value`. + ReturnCode::Completed(0) => { + let ptr = cleanup + .as_ref() + .map(|c| c.ptr.as_ptr()) + .unwrap_or(ptr::null_mut()); + + // SAFETY: we're the ones managing `ptr` so we know it's safe to + // pass here. + let value = unsafe { reader.ops.lift(ptr) }; + Ok((ReadComplete::Value(value), reader)) + } + + other => panic!("unexpected code {other:?}"), + } + } + + fn in_progress_waitable(&mut self, (reader, _): &Self::InProgress) -> u32 { + reader.handle() + } + + fn in_progress_cancel(&mut self, (reader, _): &mut Self::InProgress) -> u32 { + // SAFETY: we're managing `reader` and all the various operational bits, + // so this relies on `WaitableOperation` being safe. + let code = unsafe { reader.ops.cancel_read(reader.handle()) }; + rtdebug!("future.cancel-read({}) = {code:#x}", reader.handle()); + code + } + + fn result_into_cancel(&mut self, (value, reader): Self::Result) -> Self::Cancel { + match value { + // The value was actually read, so thread that through here. + ReadComplete::Value(value) => Ok(value), + + // The read was successfully cancelled, so thread through the + // `reader` to possibly restart later on. + ReadComplete::Cancelled => Err(reader), + } + } +} + +impl Future for RawFutureRead { + type Output = O::Payload; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.pin_project() + .poll_complete(cx) + .map(|(result, _reader)| match result { + ReadComplete::Value(val) => val, + // This is only possible if, after calling `FutureRead::cancel`, + // the future is polled again. The `cancel` method is documented + // as "don't do that" so this is left to panic. + ReadComplete::Cancelled => panic!("cannot poll after cancelling"), + }) + } +} + +impl RawFutureRead { + fn pin_project(self: Pin<&mut Self>) -> Pin<&mut WaitableOperation>> { + // SAFETY: we've chosen that when `Self` is pinned that it translates to + // always pinning the inner field, so that's codified here. + unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().op) } + } + + /// Cancel this read if it hasn't already completed. + /// + /// Return values include: + /// + /// * `Ok(value)` - future completed before this cancellation request + /// was received. + /// * `Err(reader)` - read operation was cancelled and it can be retried in + /// the future if desired. + /// + /// # Panics + /// + /// Panics if the operation has already been completed via `Future::poll`, + /// or if this method is called twice. Additionally if this method completes + /// then calling `poll` again on `self` will panic. + pub fn cancel(self: Pin<&mut Self>) -> Result> { + self.pin_project().cancel() + } +} diff --git a/tools/vendor/wit-bindgen/src/rt/async_support/inter_task_wakeup.rs b/tools/vendor/wit-bindgen/src/rt/async_support/inter_task_wakeup.rs new file mode 100644 index 0000000000..89dd39756a --- /dev/null +++ b/tools/vendor/wit-bindgen/src/rt/async_support/inter_task_wakeup.rs @@ -0,0 +1,98 @@ +use super::FutureState; +use crate::rt::async_support::{BLOCKED, COMPLETED}; +use crate::{RawStreamReader, RawStreamWriter, StreamOps, UnitStreamOps}; +use std::ptr; +use std::sync::Mutex; + +#[derive(Default)] +pub struct State { + /// A lazily-initialized stream used to signal inter-task notifications. + /// + /// This stream is used when one component-model task is used to wake up + /// another component-model task. This can happen when the async event being + /// waited on is defined purely in Rust, for example, and doesn't rely on + /// any component-model primitives. + stream: Option>, + /// Boolean if there's an active read of `inter_task_stream`. Used to handle + /// cancellation/deduplication of the read. + stream_reading: bool, +} + +impl FutureState<'_> { + pub(super) fn read_inter_task_stream(&mut self) { + // Lazily allocate the inter-task stream now that we're actually going + // to sleep. We don't know where the wakeup notification will come from + // so it's required to allocate one here. + if self.inter_task_wakeup.stream.is_none() { + assert!(!self.inter_task_wakeup.stream_reading); + let (writer, reader) = UnitStreamOps::new(); + self.inter_task_wakeup.stream = Some(reader); + let mut waker_stream = self.waker.inter_task_stream.lock.lock().unwrap(); + assert!(waker_stream.is_none()); + *waker_stream = Some(writer); + } + + // If there's not already a pending read then schedule a new read here. + // + // Note that this should always return `BLOCKED` since as the only task + // running it's not possible for a read to be anywhere else in the + // system. Additionally we keep the read end alive, so this shouldn't + // ever returned dropped/closed either. + if !self.inter_task_wakeup.stream_reading { + let stream = self.inter_task_wakeup.stream.as_mut().unwrap(); + let handle = stream.handle(); + let rc = unsafe { UnitStreamOps.start_read(handle, ptr::null_mut(), 1) }; + assert_eq!(rc, BLOCKED); + self.inter_task_wakeup.stream_reading = true; + self.add_waitable(handle); + } + } + + /// Cancels the active read of the inter-task stream, if any. + /// + /// Has no effect if there is no active read. + pub(super) fn cancel_inter_task_stream_read(&mut self) { + if !self.inter_task_wakeup.stream_reading { + return; + } + self.inter_task_wakeup.stream_reading = false; + let handle = self.inter_task_wakeup.stream.as_mut().unwrap().handle(); + // Note that the return code here is discarded. No matter what the read + // is cancelled, and whether we actually read something or whether we + // cancelled doesn't matter. + unsafe { + UnitStreamOps.cancel_read(handle); + } + self.remove_waitable(handle); + } +} + +impl State { + pub fn consume_waitable_event(&mut self, waitable: u32, _code: u32) -> bool { + if let Some(reader) = self.stream.as_mut() { + if reader.handle() == waitable { + self.stream_reading = false; + return true; + } + } + false + } +} + +#[derive(Default)] +pub struct WakerState { + lock: Mutex>>, +} + +impl WakerState { + pub fn wake(&self) { + // Here the wakeup stream should already have been filled in by the + // original future itself. The stream should also have an active read + // while the future is sleeping. This means that this write should + // succeed immediately. + let mut inter_task_stream = self.lock.lock().unwrap(); + let stream = inter_task_stream.as_mut().unwrap(); + let rc = unsafe { UnitStreamOps.start_write(stream.handle(), ptr::null_mut(), 1) }; + assert_eq!(rc, COMPLETED | (1 << 4)); + } +} diff --git a/tools/vendor/wit-bindgen/src/rt/async_support/inter_task_wakeup_disabled.rs b/tools/vendor/wit-bindgen/src/rt/async_support/inter_task_wakeup_disabled.rs new file mode 100644 index 0000000000..3f7462cfb0 --- /dev/null +++ b/tools/vendor/wit-bindgen/src/rt/async_support/inter_task_wakeup_disabled.rs @@ -0,0 +1,45 @@ +use super::FutureState; + +#[derive(Default)] +pub struct State; + +impl FutureState<'_> { + pub(super) fn read_inter_task_stream(&mut self) { + assert!( + self.remaining_work(), + " + + Rust task cannot sleep waiting only on Rust-originating events unless the + `wit-bindgen` crate is compiled with the `inter-task-wakeup` feature + enabled. + +" + ); + } + + pub(super) fn cancel_inter_task_stream_read(&mut self) { + // nothing to do + } +} + +impl State { + pub fn consume_waitable_event(&mut self, _waitable: u32, _code: u32) -> bool { + false + } +} + +#[derive(Default)] +pub struct WakerState; + +impl WakerState { + pub fn wake(&self) { + panic!( + " + + Cannot support cross-component-model-task wakeup unlses the `wit-bindgen` + crate is compiled with the `inter-task-wakeup` feature enabled. + +" + ); + } +} diff --git a/tools/vendor/wit-bindgen/src/rt/async_support/spawn.rs b/tools/vendor/wit-bindgen/src/rt/async_support/spawn.rs new file mode 100644 index 0000000000..da0778d31d --- /dev/null +++ b/tools/vendor/wit-bindgen/src/rt/async_support/spawn.rs @@ -0,0 +1,82 @@ +// TODO: Switch to interior mutability (e.g. use Mutexes or thread-local +// RefCells) and remove this, since even in single-threaded mode `static mut` +// references can be a hazard due to recursive access. +#![allow(static_mut_refs)] + +use crate::rt::async_support::BoxFuture; +use futures::stream::{FuturesUnordered, StreamExt}; +use std::boxed::Box; +use std::future::Future; +use std::task::{Context, Poll}; +use std::vec::Vec; + +/// Any newly-deferred work queued by calls to the `spawn` function while +/// polling the current task. +static mut SPAWNED: Vec = Vec::new(); + +#[derive(Default)] +pub struct Tasks<'a> { + tasks: FuturesUnordered>, +} + +impl<'a> Tasks<'a> { + pub fn new(root: BoxFuture<'a>) -> Tasks<'a> { + Tasks { + tasks: [root].into_iter().collect(), + } + } + + pub fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll> { + unsafe { + let ret = self.tasks.poll_next_unpin(cx); + if !SPAWNED.is_empty() { + self.tasks.extend(SPAWNED.drain(..)); + } + ret + } + } + + pub fn is_empty(&self) -> bool { + self.tasks.is_empty() + } +} + +/// Spawn the provided `future` to get executed concurrently with the +/// currently-running async computation. +/// +/// This API is somewhat similar to `tokio::task::spawn` for example but has a +/// number of limitations to be aware of. If possible it's recommended to avoid +/// this, but it can be convenient if these limitations do not apply to you: +/// +/// * Spawned tasks do not work when the version of the `wit-bindgen` crate +/// managing the export bindings is different from the version of this crate. +/// To work correctly the `spawn` function and export executor must be at +/// exactly the same version. Given the major-version-breaking nature of +/// `wit-bindgen` this is not always easy to rely on. This is tracked in +/// [#1305]. +/// +/// * Spawned tasks do not outlive the scope of the async computation they are +/// spawned within. For example with an async export function spawned tasks +/// will be polled within the context of that component-model async task. For +/// computations executing within a [`block_on`] call, however, the spawned +/// tasks will be executed within that scope. This notably means that for +/// [`block_on`] spawned tasks will prevent the [`block_on`] function from +/// returning, even if a value is available to return. +/// +/// * There is no handle returned to the spawned task meaning that it cannot be +/// cancelled or monitored. +/// +/// * The task spawned here is executed *concurrently*, not in *parallel*. This +/// means that while one future is being polled no other future can be polled +/// at the same time. This is similar to a single-thread executor in Tokio. +/// +/// With these restrictions in mind this can be used to express +/// execution-after-returning in the component model. For example once an +/// exported async function has produced a value this can be used to continue to +/// execute some more code before the component model async task exits. +/// +/// [`block_on`]: crate::block_on +/// [#1305]: https://github.com/bytecodealliance/wit-bindgen/issues/1305 +pub fn spawn(future: impl Future + 'static) { + unsafe { SPAWNED.push(Box::pin(future)) } +} diff --git a/tools/vendor/wit-bindgen/src/rt/async_support/spawn_disabled.rs b/tools/vendor/wit-bindgen/src/rt/async_support/spawn_disabled.rs new file mode 100644 index 0000000000..fa23dfc329 --- /dev/null +++ b/tools/vendor/wit-bindgen/src/rt/async_support/spawn_disabled.rs @@ -0,0 +1,30 @@ +use crate::rt::async_support::BoxFuture; +use std::task::{Context, Poll}; + +#[derive(Default)] +pub struct Tasks<'a> { + future: Option>, +} + +impl<'a> Tasks<'a> { + pub fn new(root: BoxFuture<'a>) -> Tasks<'a> { + Tasks { future: Some(root) } + } + + pub fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll> { + if let Some(future) = self.future.as_mut() { + if future.as_mut().poll(cx).is_ready() { + self.future = None; + } + } + if self.is_empty() { + Poll::Ready(None) + } else { + Poll::Pending + } + } + + pub fn is_empty(&self) -> bool { + self.future.is_none() + } +} diff --git a/tools/vendor/wit-bindgen/src/rt/async_support/stream_support.rs b/tools/vendor/wit-bindgen/src/rt/async_support/stream_support.rs new file mode 100644 index 0000000000..6e4771827a --- /dev/null +++ b/tools/vendor/wit-bindgen/src/rt/async_support/stream_support.rs @@ -0,0 +1,744 @@ +//! For a high-level overview of how this module is implemented see the +//! module documentation in `future_support.rs`. + +use crate::rt::async_support::waitable::{WaitableOp, WaitableOperation}; +use crate::rt::async_support::{AbiBuffer, DROPPED, ReturnCode}; +use { + crate::rt::Cleanup, + std::{ + alloc::Layout, + fmt, + future::Future, + pin::Pin, + ptr, + sync::atomic::{AtomicU32, Ordering::Relaxed}, + task::{Context, Poll}, + vec::Vec, + }, +}; + +/// Operations that a stream requires throughout the implementation. +/// +/// This is generated by `wit_bindgen::generate!` primarily. +#[doc(hidden)] +pub unsafe trait StreamOps: Clone { + /// The Rust type that's sent or received on this stream. + type Payload: 'static; + + /// The `stream.new` intrinsic. + fn new(&mut self) -> u64; + + /// The canonical ABI layout of the type that this stream is + /// sending/receiving. + fn elem_layout(&self) -> Layout; + + /// Returns whether `lift` or `lower` is required to create `Self::Payload`. + /// + /// If this returns `false` then `Self::Payload` is natively in its + /// canonical ABI representation. + fn native_abi_matches_canonical_abi(&self) -> bool; + + /// Returns whether `O::Payload` has lists that need to be deallocated with + /// `dealloc_lists`. + fn contains_lists(&self) -> bool; + + /// Converts a Rust type to its canonical ABI representation. + unsafe fn lower(&mut self, payload: Self::Payload, dst: *mut u8); + /// Used to deallocate any Rust-owned lists in the canonical ABI + /// representation for when a value is successfully sent but needs to be + /// cleaned up. + unsafe fn dealloc_lists(&mut self, dst: *mut u8); + /// Converts from the canonical ABI representation to a Rust value. + unsafe fn lift(&mut self, dst: *mut u8) -> Self::Payload; + /// The `stream.write` intrinsic + unsafe fn start_write(&mut self, stream: u32, val: *const u8, amt: usize) -> u32; + /// The `stream.read` intrinsic + unsafe fn start_read(&mut self, stream: u32, val: *mut u8, amt: usize) -> u32; + /// The `stream.cancel-read` intrinsic + unsafe fn cancel_read(&mut self, stream: u32) -> u32; + /// The `stream.cancel-write` intrinsic + unsafe fn cancel_write(&mut self, stream: u32) -> u32; + /// The `stream.drop-readable` intrinsic + unsafe fn drop_readable(&mut self, stream: u32); + /// The `stream.drop-writable` intrinsic + unsafe fn drop_writable(&mut self, stream: u32); +} +/// Operations that a stream requires throughout the implementation. +/// +/// This is generated by `wit_bindgen::generate!` primarily. +#[doc(hidden)] +pub struct StreamVtable { + /// The in-memory canonical ABI layout of a single value of `T`. + pub layout: Layout, + + /// An optional callback where if provided will lower an owned `T` value + /// into the `dst` pointer. + /// + /// If this is called the ownership of all of `T`'s lists and resources are + /// passed to `dst`, possibly by reallocating if `T`'s layout differs from + /// the canonical ABI layout. + /// + /// If this is `None` then it means that `T` has the same layout in-memory + /// in Rust as it does in the canonical ABI. In such a situation the + /// lower/lift operation can be dropped. + pub lower: Option, + + /// Callback used to deallocate any owned lists in `dst` after a value has + /// been successfully sent along a stream. + /// + /// `None` means that `T` has no lists internally. + pub dealloc_lists: Option, + + /// Dual of `lower`, and like `lower` if this is missing then it means that + /// `T` has the same in-memory representation in Rust and the canonical ABI. + pub lift: Option T>, + + /// The raw `stream.write` intrinsic. + pub start_write: unsafe extern "C" fn(stream: u32, val: *const u8, amt: usize) -> u32, + /// The raw `stream.read` intrinsic. + pub start_read: unsafe extern "C" fn(stream: u32, val: *mut u8, amt: usize) -> u32, + /// The raw `stream.cancel-write` intrinsic. + pub cancel_write: unsafe extern "C" fn(stream: u32) -> u32, + /// The raw `stream.cancel-read` intrinsic. + pub cancel_read: unsafe extern "C" fn(stream: u32) -> u32, + /// The raw `stream.drop-writable` intrinsic. + pub drop_writable: unsafe extern "C" fn(stream: u32), + /// The raw `stream.drop-readable` intrinsic. + pub drop_readable: unsafe extern "C" fn(stream: u32), + /// The raw `stream.new` intrinsic. + pub new: unsafe extern "C" fn() -> u64, +} + +unsafe impl StreamOps for &StreamVtable { + type Payload = T; + + fn new(&mut self) -> u64 { + unsafe { (self.new)() } + } + fn elem_layout(&self) -> Layout { + self.layout + } + fn native_abi_matches_canonical_abi(&self) -> bool { + self.lift.is_none() + } + fn contains_lists(&self) -> bool { + self.dealloc_lists.is_some() + } + unsafe fn lower(&mut self, payload: Self::Payload, dst: *mut u8) { + if let Some(f) = self.lower { + unsafe { f(payload, dst) } + } + } + unsafe fn dealloc_lists(&mut self, dst: *mut u8) { + if let Some(f) = self.dealloc_lists { + unsafe { f(dst) } + } + } + unsafe fn lift(&mut self, dst: *mut u8) -> Self::Payload { + unsafe { (self.lift.unwrap())(dst) } + } + unsafe fn start_write(&mut self, stream: u32, val: *const u8, amt: usize) -> u32 { + unsafe { (self.start_write)(stream, val, amt) } + } + unsafe fn start_read(&mut self, stream: u32, val: *mut u8, amt: usize) -> u32 { + unsafe { (self.start_read)(stream, val, amt) } + } + unsafe fn cancel_read(&mut self, stream: u32) -> u32 { + unsafe { (self.cancel_read)(stream) } + } + unsafe fn cancel_write(&mut self, stream: u32) -> u32 { + unsafe { (self.cancel_write)(stream) } + } + unsafe fn drop_readable(&mut self, stream: u32) { + unsafe { (self.drop_readable)(stream) } + } + unsafe fn drop_writable(&mut self, stream: u32) { + unsafe { (self.drop_writable)(stream) } + } +} + +/// Helper function to create a new read/write pair for a component model +/// stream. +pub unsafe fn stream_new( + vtable: &'static StreamVtable, +) -> (StreamWriter, StreamReader) { + unsafe { raw_stream_new(vtable) } +} + +/// Helper function to create a new read/write pair for a component model +/// stream. +pub unsafe fn raw_stream_new(mut ops: O) -> (RawStreamWriter, RawStreamReader) +where + O: StreamOps + Clone, +{ + unsafe { + let handles = ops.new(); + let reader = handles as u32; + let writer = (handles >> 32) as u32; + rtdebug!("stream.new() = [{writer}, {reader}]"); + ( + RawStreamWriter::new(writer, ops.clone()), + RawStreamReader::new(reader, ops), + ) + } +} + +/// Represents the writable end of a Component Model `stream`. +pub type StreamWriter = RawStreamWriter<&'static StreamVtable>; + +/// Represents the writable end of a Component Model `stream`. +pub struct RawStreamWriter { + handle: u32, + ops: O, + done: bool, +} + +impl RawStreamWriter +where + O: StreamOps, +{ + #[doc(hidden)] + pub unsafe fn new(handle: u32, ops: O) -> Self { + Self { + handle, + ops, + done: false, + } + } + + /// Returns the index of the component-model handle that this stream is + /// using. + pub fn handle(&self) -> u32 { + self.handle + } + + /// Initiate a write of the `values` provided into this stream. + /// + /// This method is akin to an `async fn` except that the returned + /// [`StreamWrite`] future can also be cancelled via [`StreamWrite::cancel`] + /// to re-acquire undelivered values. + /// + /// This method will perform at most a single write of the `values` + /// provided. The returned future will resolve once the write has completed. + /// + /// # Return Values + /// + /// The returned [`StreamWrite`] future returns a tuple of `(result, buf)`. + /// The `result` can be `StreamResult::Complete(n)` meaning that `n` values + /// were sent from `values` into this writer. A result of + /// `StreamResult::Dropped` means that no values were sent and the other side + /// has hung-up and sending values will no longer be possible. + /// + /// The `buf` returned is an [`AbiBuffer`] which retains ownership of the + /// original `values` provided here. That can be used to re-acquire `values` + /// through the [`AbiBuffer::into_vec`] method. The `buf` maintains an + /// internal cursor of how many values have been written and if the write + /// should be resumed to write the entire buffer then the + /// [`StreamWriter::write_buf`] method can be used to resume writing at the + /// next value in the buffer. + /// + /// # Cancellation + /// + /// The returned [`StreamWrite`] future can be cancelled like any other Rust + /// future via `drop`, but this means that `values` will be lost within the + /// future. The [`StreamWrite::cancel`] method can be used to re-acquire the + /// in-progress write that is being done with `values`. This is effectively + /// a way of forcing the future to immediately resolve. + /// + /// Note that if this future is cancelled via `drop` it does not mean that + /// no values were sent. It may be possible that values were still sent + /// despite being cancelled. Cancelling a write and determining what + /// happened must be done with [`StreamWrite::cancel`]. + pub fn write(&mut self, values: Vec) -> RawStreamWrite<'_, O> { + self.write_buf(AbiBuffer::new(values, self.ops.clone())) + } + + /// Same as [`StreamWriter::write`], except this takes [`AbiBuffer`] + /// instead of `Vec`. + pub fn write_buf(&mut self, values: AbiBuffer) -> RawStreamWrite<'_, O> { + RawStreamWrite { + op: WaitableOperation::new(StreamWriteOp { writer: self }, values), + } + } + + /// Writes all of the `values` provided into this stream. + /// + /// This is a higher-level method than [`StreamWriter::write`] and does not + /// expose cancellation for example. This will successively attempt to write + /// all of `values` provided into this stream. Upon completion the same + /// vector will be returned and any remaining elements in the vector were + /// not sent because the stream was dropped. + pub async fn write_all(&mut self, values: Vec) -> Vec { + // Perform an initial write which converts `values` into `AbiBuffer`. + let (mut status, mut buf) = self.write(values).await; + + // While the previous write completed and there's still remaining items + // in the buffer, perform another write. + while let StreamResult::Complete(_) = status { + if buf.remaining() == 0 { + break; + } + (status, buf) = self.write_buf(buf).await; + + // FIXME(WebAssembly/component-model#490) + if status == StreamResult::Cancelled { + status = StreamResult::Complete(0); + } + } + + // Return back any values that weren't written by shifting them to the + // front of the returned vector. + assert!(buf.remaining() == 0 || matches!(status, StreamResult::Dropped)); + buf.into_vec() + } + + /// Writes the singular `value` provided + /// + /// This is a higher-level method than [`StreamWriter::write`] and does not + /// expose cancellation for example. This will attempt to send `value` on + /// this stream. + /// + /// If the other end hangs up then the value is returned back as + /// `Some(value)`, otherwise `None` is returned indicating the value was + /// sent. + pub async fn write_one(&mut self, value: O::Payload) -> Option { + // TODO: can probably be a bit more efficient about this and avoid + // moving `value` onto the heap in some situations, but that's left as + // an optimization for later. + self.write_all(std::vec![value]).await.pop() + } +} + +impl fmt::Debug for RawStreamWriter +where + O: StreamOps, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamWriter") + .field("handle", &self.handle) + .finish() + } +} + +impl Drop for RawStreamWriter +where + O: StreamOps, +{ + fn drop(&mut self) { + rtdebug!("stream.drop-writable({})", self.handle); + unsafe { + self.ops.drop_writable(self.handle); + } + } +} + +/// Represents a write operation which may be cancelled prior to completion. +pub type StreamWrite<'a, T> = RawStreamWrite<'a, &'static StreamVtable>; + +/// Represents a write operation which may be cancelled prior to completion. +pub struct RawStreamWrite<'a, O: StreamOps> { + op: WaitableOperation>, +} + +struct StreamWriteOp<'a, O: StreamOps> { + writer: &'a mut RawStreamWriter, +} + +/// Result of a [`StreamWriter::write`] or [`StreamReader::read`] operation, +/// yielded by the [`StreamWrite`] or [`StreamRead`] futures. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum StreamResult { + /// The provided number of values were successfully transferred. + /// + /// For writes this is how many items were written, and for reads this is + /// how many items were read. + Complete(usize), + /// No values were written, the other end has dropped its handle. + Dropped, + /// No values were written, the operation was cancelled. + Cancelled, +} + +unsafe impl<'a, O> WaitableOp for StreamWriteOp<'a, O> +where + O: StreamOps, +{ + type Start = AbiBuffer; + type InProgress = AbiBuffer; + type Result = (StreamResult, AbiBuffer); + type Cancel = (StreamResult, AbiBuffer); + + fn start(&mut self, buf: Self::Start) -> (u32, Self::InProgress) { + if self.writer.done { + return (DROPPED, buf); + } + + let (ptr, len) = buf.abi_ptr_and_len(); + // SAFETY: sure hope this is safe, everything in this module and + // `AbiBuffer` is trying to make this safe. + let code = unsafe { self.writer.ops.start_write(self.writer.handle, ptr, len) }; + rtdebug!( + "stream.write({}, {ptr:?}, {len}) = {code:#x}", + self.writer.handle + ); + (code, buf) + } + + fn start_cancelled(&mut self, buf: Self::Start) -> Self::Cancel { + (StreamResult::Cancelled, buf) + } + + fn in_progress_update( + &mut self, + mut buf: Self::InProgress, + code: u32, + ) -> Result { + match ReturnCode::decode(code) { + ReturnCode::Blocked => Err(buf), + ReturnCode::Dropped(0) => Ok((StreamResult::Dropped, buf)), + ReturnCode::Cancelled(0) => Ok((StreamResult::Cancelled, buf)), + code @ (ReturnCode::Completed(amt) + | ReturnCode::Dropped(amt) + | ReturnCode::Cancelled(amt)) => { + let amt = amt.try_into().unwrap(); + buf.advance(amt); + if let ReturnCode::Dropped(_) = code { + self.writer.done = true; + } + Ok((StreamResult::Complete(amt), buf)) + } + } + } + + fn in_progress_waitable(&mut self, _: &Self::InProgress) -> u32 { + self.writer.handle + } + + fn in_progress_cancel(&mut self, _: &mut Self::InProgress) -> u32 { + // SAFETY: we're managing `writer` and all the various operational bits, + // so this relies on `WaitableOperation` being safe. + let code = unsafe { self.writer.ops.cancel_write(self.writer.handle) }; + rtdebug!("stream.cancel-write({}) = {code:#x}", self.writer.handle); + code + } + + fn result_into_cancel(&mut self, result: Self::Result) -> Self::Cancel { + result + } +} + +impl Future for RawStreamWrite<'_, O> { + type Output = (StreamResult, AbiBuffer); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.pin_project().poll_complete(cx) + } +} + +impl<'a, O: StreamOps> RawStreamWrite<'a, O> { + fn pin_project(self: Pin<&mut Self>) -> Pin<&mut WaitableOperation>> { + // SAFETY: we've chosen that when `Self` is pinned that it translates to + // always pinning the inner field, so that's codified here. + unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().op) } + } + + /// Cancel this write if it hasn't already completed. + /// + /// This method can be used to cancel a write-in-progress and re-acquire + /// values being sent. Note that the result here may still indicate that + /// some values were written if the race to cancel the write was lost. + /// + /// # Panics + /// + /// Panics if the operation has already been completed via `Future::poll`, + /// or if this method is called twice. + pub fn cancel(self: Pin<&mut Self>) -> (StreamResult, AbiBuffer) { + self.pin_project().cancel() + } +} + +/// Represents the readable end of a Component Model `stream`. +pub type StreamReader = RawStreamReader<&'static StreamVtable>; + +/// Represents the readable end of a Component Model `stream`. +pub struct RawStreamReader { + handle: AtomicU32, + ops: O, + done: bool, +} + +impl fmt::Debug for RawStreamReader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamReader") + .field("handle", &self.handle) + .finish() + } +} + +impl RawStreamReader { + #[doc(hidden)] + pub fn new(handle: u32, ops: O) -> Self { + Self { + handle: AtomicU32::new(handle), + ops, + done: false, + } + } + + #[doc(hidden)] + pub fn take_handle(&self) -> u32 { + let ret = self.opt_handle().unwrap(); + self.handle.store(u32::MAX, Relaxed); + ret + } + + /// Returns the index of the component-model handle that this stream is + /// using. + pub fn handle(&self) -> u32 { + self.opt_handle().unwrap() + } + + fn opt_handle(&self) -> Option { + match self.handle.load(Relaxed) { + u32::MAX => None, + other => Some(other), + } + } + + /// Starts a new read operation on this stream into `buf`. + /// + /// This method will read values into the spare capacity of the `buf` + /// provided. If `buf` has no spare capacity then this will be equivalent + /// to a zero-length read. + /// + /// Upon completion the `buf` will be yielded back to the caller via the + /// completion of the [`StreamRead`] future. + /// + /// # Cancellation + /// + /// Cancelling the returned future can be done with `drop` like all Rust + /// futures, but it does not mean that no values were read. To accurately + /// determine if values were read the [`StreamRead::cancel`] method must be + /// used. + pub fn read(&mut self, buf: Vec) -> RawStreamRead<'_, O> { + RawStreamRead { + op: WaitableOperation::new(StreamReadOp { reader: self }, buf), + } + } + + /// Reads a single item from this stream. + /// + /// This is a higher-level method than [`StreamReader::read`] in that it + /// reads only a single item and does not expose control over cancellation. + pub async fn next(&mut self) -> Option { + // TODO: should amortize this allocation and avoid doing it every time. + // Or somehow perhaps make this more optimal. + let (_result, mut buf) = self.read(Vec::with_capacity(1)).await; + buf.pop() + } + + /// Reads all items from this stream and returns the list. + /// + /// This method will read all remaining items from this stream into a list + /// and await the stream to be dropped. + pub async fn collect(mut self) -> Vec { + let mut ret = Vec::new(); + loop { + // If there's no more spare capacity then reserve room for one item + // which should trigger `Vec`'s built-in resizing logic, which will + // free up likely more capacity than just one slot. + if ret.len() == ret.capacity() { + ret.reserve(1); + } + let (status, buf) = self.read(ret).await; + ret = buf; + match status { + StreamResult::Complete(_) => {} + StreamResult::Dropped => break, + StreamResult::Cancelled => unreachable!(), + } + } + ret + } +} + +impl Drop for RawStreamReader { + fn drop(&mut self) { + let Some(handle) = self.opt_handle() else { + return; + }; + unsafe { + rtdebug!("stream.drop-readable({})", handle); + self.ops.drop_readable(handle); + } + } +} + +/// Represents a read operation which may be cancelled prior to completion. +pub type StreamRead<'a, T> = RawStreamRead<'a, &'static StreamVtable>; + +/// Represents a read operation which may be cancelled prior to completion. +pub struct RawStreamRead<'a, O: StreamOps> { + op: WaitableOperation>, +} + +struct StreamReadOp<'a, O: StreamOps> { + reader: &'a mut RawStreamReader, +} + +unsafe impl<'a, O: StreamOps> WaitableOp for StreamReadOp<'a, O> { + type Start = Vec; + type InProgress = (Vec, Option); + type Result = (StreamResult, Vec); + type Cancel = (StreamResult, Vec); + + fn start(&mut self, mut buf: Self::Start) -> (u32, Self::InProgress) { + if self.reader.done { + return (DROPPED, (buf, None)); + } + + let cap = buf.spare_capacity_mut(); + let ptr; + let cleanup; + // If `T` requires a lifting operation, then allocate a slab of memory + // which will store the canonical ABI read. Otherwise we can use the + // raw capacity in `buf` itself. + if self.reader.ops.native_abi_matches_canonical_abi() { + ptr = cap.as_mut_ptr().cast(); + cleanup = None; + } else { + let elem_layout = self.reader.ops.elem_layout(); + let layout = + Layout::from_size_align(elem_layout.size() * cap.len(), elem_layout.align()) + .unwrap(); + (ptr, cleanup) = Cleanup::new(layout); + } + // SAFETY: `ptr` is either in `buf` or in `cleanup`, both of which will + // persist with this async operation itself. + let code = unsafe { + self.reader + .ops + .start_read(self.reader.handle(), ptr, cap.len()) + }; + rtdebug!( + "stream.read({}, {ptr:?}, {}) = {code:#x}", + self.reader.handle(), + cap.len() + ); + (code, (buf, cleanup)) + } + + fn start_cancelled(&mut self, buf: Self::Start) -> Self::Cancel { + (StreamResult::Cancelled, buf) + } + + fn in_progress_update( + &mut self, + (mut buf, cleanup): Self::InProgress, + code: u32, + ) -> Result { + match ReturnCode::decode(code) { + ReturnCode::Blocked => Err((buf, cleanup)), + + // Note that the `cleanup`, if any, is discarded here. + ReturnCode::Dropped(0) => Ok((StreamResult::Dropped, buf)), + + // When an in-progress read is successfully cancelled then the + // allocation that was being read into, if any, is just discarded. + // + // TODO: should maybe thread this around like `AbiBuffer` to cache + // the read allocation? + ReturnCode::Cancelled(0) => Ok((StreamResult::Cancelled, buf)), + + code @ (ReturnCode::Completed(amt) + | ReturnCode::Dropped(amt) + | ReturnCode::Cancelled(amt)) => { + let amt = usize::try_from(amt).unwrap(); + let cur_len = buf.len(); + assert!(amt <= buf.capacity() - cur_len); + + if self.reader.ops.native_abi_matches_canonical_abi() { + // If no `lift` was necessary, then the results of this operation + // were read directly into `buf`, so just update its length now that + // values have been initialized. + unsafe { + buf.set_len(cur_len + amt); + } + } else { + // With a `lift` operation this now requires reading `amt` items + // from `cleanup` and pushing them into `buf`. + let mut ptr = cleanup + .as_ref() + .map(|c| c.ptr.as_ptr()) + .unwrap_or(ptr::null_mut()); + for _ in 0..amt { + unsafe { + buf.push(self.reader.ops.lift(ptr)); + ptr = ptr.add(self.reader.ops.elem_layout().size()); + } + } + } + + // Intentionally dispose of `cleanup` here as, if it was used, all + // allocations have been read from it and appended to `buf`. + drop(cleanup); + if let ReturnCode::Dropped(_) = code { + self.reader.done = true; + } + Ok((StreamResult::Complete(amt), buf)) + } + } + } + + fn in_progress_waitable(&mut self, _: &Self::InProgress) -> u32 { + self.reader.handle() + } + + fn in_progress_cancel(&mut self, _: &mut Self::InProgress) -> u32 { + // SAFETY: we're managing `reader` and all the various operational bits, + // so this relies on `WaitableOperation` being safe. + let code = unsafe { self.reader.ops.cancel_read(self.reader.handle()) }; + rtdebug!("stream.cancel-read({}) = {code:#x}", self.reader.handle()); + code + } + + fn result_into_cancel(&mut self, result: Self::Result) -> Self::Cancel { + result + } +} + +impl Future for RawStreamRead<'_, O> { + type Output = (StreamResult, Vec); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.pin_project().poll_complete(cx) + } +} + +impl<'a, O> RawStreamRead<'a, O> +where + O: StreamOps, +{ + fn pin_project(self: Pin<&mut Self>) -> Pin<&mut WaitableOperation>> { + // SAFETY: we've chosen that when `Self` is pinned that it translates to + // always pinning the inner field, so that's codified here. + unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().op) } + } + + /// Cancel this read if it hasn't already completed. + /// + /// This method will initiate a cancellation operation for this active + /// read. This may race with the actual read itself and so this may actually + /// complete with some results. + /// + /// The final result of cancellation is returned, along with the original + /// buffer. + /// + /// # Panics + /// + /// Panics if the operation has already been completed via `Future::poll`, + /// or if this method is called twice. + pub fn cancel(self: Pin<&mut Self>) -> (StreamResult, Vec) { + self.pin_project().cancel() + } +} diff --git a/tools/vendor/wit-bindgen/src/rt/async_support/subtask.rs b/tools/vendor/wit-bindgen/src/rt/async_support/subtask.rs new file mode 100644 index 0000000000..9236d533fb --- /dev/null +++ b/tools/vendor/wit-bindgen/src/rt/async_support/subtask.rs @@ -0,0 +1,284 @@ +//! Bindings used to manage subtasks, or invocations of imported functions. +//! +//! See `future_support` for some more discussion but the basic idea is the same +//! where we require that everything is passed by ownership to primarily deal +//! with the possibility of leaking futures. By always requiring ownership we +//! can guarantee that even when a future is leaked all its parameters passed to +//! the canonical ABI are additionally leaked with it which should be memory +//! safe. + +use crate::rt::Cleanup; +use crate::rt::async_support::waitable::{WaitableOp, WaitableOperation}; +use crate::rt::async_support::{ + STATUS_RETURNED, STATUS_RETURNED_CANCELLED, STATUS_STARTED, STATUS_STARTED_CANCELLED, + STATUS_STARTING, +}; +use std::alloc::Layout; +use std::future::Future; +use std::marker; +use std::num::NonZeroU32; +use std::ptr; + +/// Raw operations used to invoke an imported asynchronous function. +/// +/// This trait is implemented by generated bindings and is used to implement +/// asynchronous imports. +/// +/// # Unsafety +/// +/// All operations/constants must be self-consistent for how this module expects +/// them all to be used. +pub unsafe trait Subtask { + /// The parameters to this task. + type Params; + /// The representation of lowered parameters for this task. + /// + /// This is used to account for how lowered imports may have up to 4 flat + /// arguments or may also be indirect as well in memory. Either way this + /// represents the actual ABI values passed to the import. + type ParamsLower: Copy; + /// The results of this task. + type Results; + + /// The in-memory layout of both parameters and results allocated with + /// parameters coming first. + fn abi_layout(&mut self) -> Layout; + + /// The offset, in bytes, from the start of `ABI_LAYOUT` to where the + /// results will be stored. + fn results_offset(&mut self) -> usize; + + /// The raw function import using `[async-lower]` and the canonical ABI. + unsafe fn call_import(&mut self, params: Self::ParamsLower, results: *mut u8) -> u32; + + /// Bindings-generated version of lowering `params`. + /// + /// This may use the heap-allocated `dst`, which is an uninitialized + /// allocation of `Self::ABI_LAYOUT`. This returns any ABI parameters + /// necessary to actually invoke the imported function. + /// + /// Note that `ParamsLower` may return `dst` if there are more ABI + /// parameters than are allowed flat params (as specified by the canonical + /// ABI). + unsafe fn params_lower(&mut self, params: Self::Params, dst: *mut u8) -> Self::ParamsLower; + + /// Bindings-generated version of deallocating any lists stored within + /// `lower`. + unsafe fn params_dealloc_lists(&mut self, lower: Self::ParamsLower); + + /// Bindings-generated version of deallocating not only owned lists within + /// `lower` but also deallocating any owned resources. + unsafe fn params_dealloc_lists_and_own(&mut self, lower: Self::ParamsLower); + + /// Bindings-generated version of lifting the results stored at `src`. + unsafe fn results_lift(&mut self, src: *mut u8) -> Self::Results; + + /// Helper function to actually perform this asynchronous call with + /// `params`. + fn call(&mut self, params: Self::Params) -> impl Future + where + Self: Sized, + { + async { + match WaitableOperation::>::new(SubtaskOps(self), Start { params }) + .await + { + Ok(results) => results, + Err(_) => unreachable!( + "cancellation is not exposed API-wise, \ + should not be possible" + ), + } + } + } +} + +struct SubtaskOps<'a, T>(&'a mut T); + +struct Start { + params: T::Params, +} + +unsafe impl WaitableOp for SubtaskOps<'_, T> { + type Start = Start; + type InProgress = InProgress; + type Result = Result; + type Cancel = Result; + + fn start(&mut self, state: Self::Start) -> (u32, Self::InProgress) { + unsafe { + let (ptr_params, cleanup) = Cleanup::new(self.0.abi_layout()); + let ptr_results = ptr_params.add(self.0.results_offset()); + let params_lower = self.0.params_lower(state.params, ptr_params); + let packed = self.0.call_import(params_lower, ptr_results); + let code = packed & 0xf; + let subtask = NonZeroU32::new(packed >> 4).map(|handle| SubtaskHandle { handle }); + rtdebug!("({ptr_params:?}, {ptr_results:?}) = ({code:#x}, {subtask:#x?})"); + + ( + code, + InProgress { + params_lower, + params_and_results: cleanup, + subtask, + started: false, + _marker: marker::PhantomData, + }, + ) + } + } + + fn start_cancelled(&mut self, _state: Self::Start) -> Self::Cancel { + Err(()) + } + + fn in_progress_update( + &mut self, + mut state: Self::InProgress, + code: u32, + ) -> Result { + match code { + // Nothing new to do in this state, we're still waiting for the task + // to start. + STATUS_STARTING => { + assert!(!state.started); + Err(state) + } + + // Still not done yet, but we can record that this is started and + // otherwise deallocate lists in the parameters. + STATUS_STARTED => { + state.flag_started(self.0); + Err(state) + } + + STATUS_RETURNED => { + // Conditionally flag as started if we haven't otherwise + // explicitly transitioned through `STATUS_STARTED`. + if !state.started { + state.flag_started(self.0); + } + + // Now that our results have been written we can read them. + // + // Note that by dropping `state` here we'll both deallocate the + // params/results storage area as well as the subtask handle + // itself. + let ptr = state.ptr_results(self.0); + unsafe { Ok(Ok(self.0.results_lift(ptr))) } + } + + // This subtask was dropped which forced cancellation. Said + // cancellation stopped the subtask before it reached the "started" + // state, meaning that we still own all of the parameters in their + // lowered form. + // + // In this situation we lift the parameters, even after we + // previously lowered them, back into `T::Params`. That notably + // re-acquires ownership and is suitable for disposing of all of + // the parameters via normal Rust-based destructors. + STATUS_STARTED_CANCELLED => { + assert!(!state.started); + unsafe { + self.0.params_dealloc_lists_and_own(state.params_lower); + } + Ok(Err(())) + } + + // This subtask was dropped which forced cancellation. Said + // cancellation stopped the subtask before it reached the "returned" + // state, meaning that it started, received the arguments, but then + // did not complete. + // + // In this situation we may have already received `STATUS_STARTED`, + // but we also might not have. This means we conditionally need + // to flag this task as started which will deallocate all lists + // owned by the parameters. + // + // After that though we do not have ownership of the parameters any + // more (e.g. own resources are all gone) so there's nothing to + // return. Here we yield a result and dispose of the in-progress + // state. + STATUS_RETURNED_CANCELLED => { + if !state.started { + state.flag_started(self.0); + } + Ok(Err(())) + } + + other => panic!("unknown code {other:#x}"), + } + } + + fn in_progress_waitable(&mut self, state: &Self::InProgress) -> u32 { + // This shouldn't get called in the one case this isn't present: when + // `STATUS_RETURNED` is returned and no waitable is created. That's the + // `unwrap()` condition here. + state.subtask.as_ref().unwrap().handle.get() + } + + fn in_progress_cancel(&mut self, state: &mut Self::InProgress) -> u32 { + unsafe { cancel(self.in_progress_waitable(state)) } + } + + fn result_into_cancel(&mut self, result: Self::Result) -> Self::Cancel { + result + } +} + +#[derive(Debug)] +struct SubtaskHandle { + handle: NonZeroU32, +} + +impl Drop for SubtaskHandle { + fn drop(&mut self) { + unsafe { + drop(self.handle.get()); + } + } +} + +struct InProgress { + params_and_results: Option, + params_lower: T::ParamsLower, + started: bool, + subtask: Option, + _marker: marker::PhantomData, +} + +impl InProgress { + fn flag_started(&mut self, op: &mut T) { + assert!(!self.started); + self.started = true; + + // SAFETY: the initial entrypoint of `call` requires that the vtable is + // setup correctly and we're obeying the invariants of the vtable, + // deallocating lists in an allocation that we exclusively own. + unsafe { + op.params_dealloc_lists(self.params_lower); + } + } + + fn ptr_results(&mut self, op: &mut T) -> *mut u8 { + // SAFETY: the `T` trait has unsafely promised us that the offset is + // in-bounds of the allocation layout. + unsafe { + self.params_and_results + .as_ref() + .map(|c| c.ptr.as_ptr()) + .unwrap_or(ptr::null_mut()) + .add(op.results_offset()) + } + } +} + +extern_wasm! { + #[link(wasm_import_module = "$root")] + unsafe extern "C" { + #[link_name = "[subtask-cancel]"] + fn cancel(handle: u32) -> u32; + #[link_name = "[subtask-drop]"] + fn drop(handle: u32); + } +} diff --git a/tools/vendor/wit-bindgen/src/rt/async_support/unit_stream.rs b/tools/vendor/wit-bindgen/src/rt/async_support/unit_stream.rs new file mode 100644 index 0000000000..985462874c --- /dev/null +++ b/tools/vendor/wit-bindgen/src/rt/async_support/unit_stream.rs @@ -0,0 +1,81 @@ +use crate::rt::async_support::raw_stream_new; +use crate::{RawStreamReader, RawStreamWriter, StreamOps}; +use std::alloc::Layout; + +/// Operations for `stream<()>`. +/// +/// Can be combined with [`RawStreamWriter`] and [`RawStreamReader`] as created +/// through [`UnitStreamOps::new`] +#[derive(Copy, Clone)] +pub struct UnitStreamOps; + +extern_wasm! { + #[link(wasm_import_module = "$root")] + unsafe extern "C" { + #[link_name = "[stream-new-unit]"] + fn unit_new() -> u64; + #[link_name = "[async-lower][stream-write-unit]"] + fn unit_write(stream: u32, val: *const u8, amt: usize) -> u32; + #[link_name = "[async-lower][stream-read-unit]"] + fn unit_read(stream: u32, val: *mut u8, amt: usize) -> u32; + #[link_name = "[stream-cancel-read-unit]"] + fn unit_cancel_read(stream: u32) -> u32; + #[link_name = "[stream-cancel-write-unit]"] + fn unit_cancel_write(stream: u32) -> u32; + #[link_name = "[stream-drop-readable-unit]"] + fn unit_drop_readable(stream: u32) ; + #[link_name = "[stream-drop-writable-unit]"] + fn unit_drop_writable(stream: u32) ; + } +} + +impl UnitStreamOps { + /// Creates a new unit stream read/write pair. + pub fn new() -> (RawStreamWriter, RawStreamReader) { + unsafe { raw_stream_new(UnitStreamOps) } + } +} + +unsafe impl StreamOps for UnitStreamOps { + type Payload = (); + + fn new(&mut self) -> u64 { + unsafe { unit_new() } + } + fn elem_layout(&self) -> Layout { + Layout::new::<()>() + } + fn native_abi_matches_canonical_abi(&self) -> bool { + true + } + fn contains_lists(&self) -> bool { + false + } + unsafe fn lower(&mut self, (): (), _dst: *mut u8) { + unreachable!() + } + unsafe fn dealloc_lists(&mut self, _dst: *mut u8) { + unreachable!() + } + unsafe fn lift(&mut self, _dst: *mut u8) -> Self::Payload { + unreachable!() + } + unsafe fn start_write(&mut self, stream: u32, val: *const u8, amt: usize) -> u32 { + unsafe { unit_write(stream, val, amt) } + } + unsafe fn start_read(&mut self, stream: u32, val: *mut u8, amt: usize) -> u32 { + unsafe { unit_read(stream, val, amt) } + } + unsafe fn cancel_read(&mut self, stream: u32) -> u32 { + unsafe { unit_cancel_read(stream) } + } + unsafe fn cancel_write(&mut self, stream: u32) -> u32 { + unsafe { unit_cancel_write(stream) } + } + unsafe fn drop_readable(&mut self, stream: u32) { + unsafe { unit_drop_readable(stream) } + } + unsafe fn drop_writable(&mut self, stream: u32) { + unsafe { unit_drop_writable(stream) } + } +} diff --git a/tools/vendor/wit-bindgen/src/rt/async_support/waitable.rs b/tools/vendor/wit-bindgen/src/rt/async_support/waitable.rs new file mode 100644 index 0000000000..516d5ef425 --- /dev/null +++ b/tools/vendor/wit-bindgen/src/rt/async_support/waitable.rs @@ -0,0 +1,482 @@ +//! Generic support for "any waitable" and performing asynchronous operations on +//! that waitable. + +use super::cabi; +use std::ffi::c_void; +use std::future::Future; +use std::marker; +use std::mem; +use std::pin::Pin; +use std::ptr; +use std::task::{Context, Poll, Waker}; + +/// Generic future-based operation on any "waitable" in the component model. +/// +/// This is used right now to power futures and streams for both read/write +/// halves. This structure is driven by `S`, an implementation of +/// [`WaitableOp`], which codifies the various state transitions and what to do +/// on each state transition. +pub struct WaitableOperation { + op: S, + state: WaitableOperationState, + /// Storage for the final result of this asynchronous operation, if it's + /// completed asynchronously. + completion_status: CompletionStatus, +} + +/// Structure used to store the `u32` return code from the canonical ABI about +/// an asynchronous operation. +/// +/// When an asynchronous operation is started and it does not immediately +/// complete then this structure is used to asynchronously fill in the return +/// code. A `Pin<&mut CompletionStatus>` is used to register a pointer with +/// `FutureState` to get filled in. +/// +/// Note that this means that this type is participating in unsafe lifetime +/// management and has properties it needs to uphold as a result. Specifically +/// the `PhantomPinned` field here means that `Pin` actually has meaning for +/// this structure, notably that once `Pin<&mut CompletionStatus>` is created +/// then it's guaranteed the destructor will be run before the backing memory +/// is deallocated. That's used in `WaitableOperation` above to share an +/// internal pointer of this data structure with `FuturesState` safely. The +/// destructor of `WaitableOperation` will deregister from `FutureState` meaning +/// that if `FuturesState` has a pointer here then it should be valid . +struct CompletionStatus { + /// Where the async operation's code is filled in, and `None` until that + /// happens. + code: Option, + + waker: Option, + + /// This is necessary to ensure that `Pin<&mut CompletionStatus>` carries + /// the "pin guarantee", basically to mean that it's not safe to construct + /// `Pin<&mut CompletionStatus>` and it must somehow require `unsafe` code. + _pinned: marker::PhantomPinned, +} + +/// Helper trait to be used with `WaitableOperation` to assist with machinery +/// necessary to track in-flight reads/writes on futures. +/// +/// # Unsafety +/// +/// This trait is `unsafe` as it has various guarantees that must be upheld by +/// implementors such as: +/// +/// * `S::in_progress_waitable` must always return the same value for the state +/// given. +pub unsafe trait WaitableOp { + /// Initial state of this operation, used to kick off the actual component + /// model operation and transition to `InProgress`. + type Start; + + /// Intermediate state of this operation when the component model is + /// involved but it hasn't resolved just yet. + type InProgress; + + /// Result type of this operation. + type Result; + + /// Result of when this operation is cancelled. + type Cancel; + + /// Starts the async operation. + /// + /// This method will actually call `{future,stream}.{read,write}` with + /// `state` provided. The return code of the intrinsic is returned here + /// along with the `InProgress` state. + fn start(&mut self, state: Self::Start) -> (u32, Self::InProgress); + + /// Optionally complete the async operation. + /// + /// This method will transition from the `InProgress` state, with some + /// status code that was received, to either a completed result or a new + /// `InProgress` state. This is invoked when: + /// + /// * a new status code has been received by an async export's `callback` + /// * cancellation returned a code to be processed here + fn in_progress_update( + &mut self, + state: Self::InProgress, + code: u32, + ) -> Result; + + /// Conversion from the "start" state to the "cancel" result, needed when an + /// operation is cancelled before it's started. + fn start_cancelled(&mut self, state: Self::Start) -> Self::Cancel; + + /// Acquires the component-model `waitable` index that the `InProgress` + /// state is waiting on. + fn in_progress_waitable(&mut self, state: &Self::InProgress) -> u32; + + /// Initiates a request for cancellation of this operation. Returns the + /// status code returned by the `{future,stream}.cancel-{read,write}` + /// intrinsic. + /// + /// Note that this must synchronously complete the operation somehow. This + /// cannot return a status code indicating that an operation is pending, + /// instead the operation must be complete with the returned code. That may + /// mean that this intrinsic can block while figuring things out in the + /// component model ABI, for example. + fn in_progress_cancel(&mut self, state: &mut Self::InProgress) -> u32; + + /// Converts a "completion result" into a "cancel result". This is necessary + /// when an in-progress operation is cancelled so the in-progress result is + /// first acquired and then transitioned to a cancel request. + fn result_into_cancel(&mut self, result: Self::Result) -> Self::Cancel; +} + +enum WaitableOperationState { + Start(S::Start), + InProgress(S::InProgress), + Done, +} + +impl WaitableOperation +where + S: WaitableOp, +{ + /// Creates a new operation in the initial state. + pub fn new(op: S, state: S::Start) -> WaitableOperation { + WaitableOperation { + op, + state: WaitableOperationState::Start(state), + completion_status: CompletionStatus { + code: None, + waker: None, + _pinned: marker::PhantomPinned, + }, + } + } + + fn pin_project( + self: Pin<&mut Self>, + ) -> ( + &mut S, + &mut WaitableOperationState, + Pin<&mut CompletionStatus>, + ) { + // SAFETY: this is the one method used to project from `Pin<&mut Self>` + // to the fields, and the contract we're deciding on is that + // `state` is never pinned but the `CompletionStatus` is. That's used + // to share a raw pointer with the completion callback with + // respect to `Option` internally. + unsafe { + let me = self.get_unchecked_mut(); + ( + &mut me.op, + &mut me.state, + Pin::new_unchecked(&mut me.completion_status), + ) + } + } + + /// Registers a completion of `waitable` within the current task's future to: + /// + /// * Fill in `completion_status` with the result of a completion event. + /// * Call `cx.waker().wake()`. + pub fn register_waker(self: Pin<&mut Self>, waitable: u32, cx: &mut Context) { + let (_, _, mut completion_status) = self.pin_project(); + debug_assert!(completion_status.as_mut().code_mut().is_none()); + *completion_status.as_mut().waker_mut() = Some(cx.waker().clone()); + + // SAFETY: There's quite a lot going on here. First is the usage of + // `task` below, and for that see `unregister_waker` below for why this + // pattern should be safe. + // + // Otherwise we're handing off a pointer to `completion_status` to the + // `task` itself. That should be safe as we're guaranteed, via + // `Pin<&mut Self>`, that before `&mut Self` is deallocated the + // destructor will be run which will perform de-registration via + // cancellation. + unsafe { + let task = cabi::wasip3_task_set(ptr::null_mut()); + assert!(!task.is_null()); + assert!((*task).version >= cabi::WASIP3_TASK_V1); + let ptr: *mut CompletionStatus = completion_status.get_unchecked_mut(); + let prev = ((*task).waitable_register)((*task).ptr, waitable, cabi_wake, ptr.cast()); + // We might be inserting a waker for the first time or overwriting + // the previous waker. Only assert the expected value here if the + // previous value was non-null. + if !prev.is_null() { + assert_eq!(ptr, prev.cast()); + } + cabi::wasip3_task_set(task); + } + + unsafe extern "C" fn cabi_wake(ptr: *mut c_void, code: u32) { + let ptr: &mut CompletionStatus = unsafe { &mut *ptr.cast::() }; + ptr.code = Some(code); + ptr.waker.take().unwrap().wake() + } + } + + /// Deregisters the corresponding `register_waker` within the current task + /// for the `waitable` passed here. + /// + /// This relinquishes control of the original `completion_status` pointer + /// passed to `register_waker` after this call has completed. + pub fn unregister_waker(self: Pin<&mut Self>, waitable: u32) { + // SAFETY: the contract of `wasip3_task_set` is that the returned + // pointer is valid for the lifetime of our entire task, so it's valid + // for this stack frame. Additionally we assert it's non-null to + // double-check it's initialized and additionally check the version for + // the fields that we access. + // + // Otherwise the `waitable_unregister` callback should be safe because: + // + // * We're fulfilling the contract where the first argument must be + // `(*task).ptr` + // * We own the `waitable` that we're passing in, so we're fulfilling + // the contract that arbitrary waitables for other units of work + // aren't being manipulated. + unsafe { + let task = cabi::wasip3_task_set(ptr::null_mut()); + assert!(!task.is_null()); + assert!((*task).version >= cabi::WASIP3_TASK_V1); + let prev = ((*task).waitable_unregister)((*task).ptr, waitable); + + // Note that `_prev` here is not guaranteed to be either `NULL` or + // not. A racy completion notification may have come in and + // removed our waitable from the map even though we're in the + // `InProgress` state, meaning it may not be present. + // + // The main thing is that after this method is called the + // internal `completion_status` is guaranteed to no longer be in + // `task`. + // + // Note, though, that if present this must be our `CompletionStatus` + // pointer. + if !prev.is_null() { + let ptr: *mut CompletionStatus = self.pin_project().2.get_unchecked_mut(); + assert_eq!(ptr, prev.cast()); + } + + cabi::wasip3_task_set(task); + } + } + + /// Polls this operation to see if it has completed yet. + /// + /// This is intended to be used within `Future::poll`. + pub fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + use WaitableOperationState::*; + + let (op, state, completion_status) = self.as_mut().pin_project(); + + // First up, determine the completion status, if any, that's available. + let optional_code = match state { + // If this operation hasn't actually started yet then now's the + // time to start it. + Start(_) => { + let Start(s) = mem::replace(state, Done) else { + unreachable!() + }; + let (code, s) = op.start(s); + *state = InProgress(s); + Some(code) + } + + // This operation was previously queued so we're just waiting on + // the completion to come in. Read the completion status and + // interpret it down below. + // + // Note that it's the responsibility of the completion callback at + // the ABI level that we install to fill in this pointer, e.g. it's + // part of the `register_waker` contract. + InProgress(_) => completion_status.code_mut().take(), + + // This write has already completed, it's a Rust-level API violation + // to call this function again. + Done => panic!("cannot re-poll after operation completes"), + }; + + self.poll_complete_with_code(Some(cx), optional_code) + } + + /// After acquiring the current return of this operation in `optional_code`, + /// figures out what to do with it. + /// + /// The `cx` argument is optional to do nothing in the case that + /// `optional_code` is not present. + fn poll_complete_with_code( + mut self: Pin<&mut Self>, + cx: Option<&mut Context>, + optional_code: Option, + ) -> Poll { + use WaitableOperationState::*; + + let (op, state, _completion_status) = self.as_mut().pin_project(); + + // If a status code is provided, then extract the in-progress state and + // see what it thinks about this code. If we're done, yay! If not then + // record the new in-progress state and fall through to registering a + // waker. + // + // If no status code is available then that means we were polled before + // the status came back, so just re-register the waker. + if let Some(code) = optional_code { + let InProgress(in_progress) = mem::replace(state, Done) else { + unreachable!() + }; + match op.in_progress_update(in_progress, code) { + Ok(result) => return Poll::Ready(result), + Err(in_progress) => *state = InProgress(in_progress), + } + } + + let in_progress = match state { + InProgress(s) => s, + _ => unreachable!(), + }; + + // The operation is still in progress. + // + // Register the `cx.waker()` to get notified when `writer.handle` + // receives its completion. + if let Some(cx) = cx { + let handle = op.in_progress_waitable(in_progress); + self.register_waker(handle, cx); + } + Poll::Pending + } + + /// Cancels the in-flight operation, if it's still in-flight, and sees what + /// happened. + /// + /// Defers to `S` how to communicate the current status through the + /// cancellation type. + /// + /// # Panics + /// + /// Panics if the operation has already been completed via `poll_complete` + /// above. + /// Panics if this method is called twice. + pub fn cancel(mut self: Pin<&mut Self>) -> S::Cancel { + use WaitableOperationState::*; + + let (op, state, mut completion_status) = self.as_mut().pin_project(); + let in_progress = match state { + // This operation was never actually started, so there's no need to + // cancel anything, just pull out the value and return it. + Start(_) => { + let Start(s) = mem::replace(state, Done) else { + unreachable!() + }; + return op.start_cancelled(s); + } + + // This operation is actively in progress, fall through to below. + InProgress(s) => s, + + // This operation was already completed after a `poll_complete` + // above advanced to the `Done` state, or this was cancelled twice. + // In such situations this is a programmer error to call this + // method, so panic. + Done => panic!("cannot cancel operation after completing it"), + }; + + // Our operation is in-progress, let's take a look at the pending + // completion code, if any. + match completion_status.as_mut().code_mut().take() { + // A completion code, or status update, is available. This can + // happen for example if an export received a status update for + // this operation but then during the subsequent poll we decided + // that the future should be dropped instead, aka a race between + // two events. In this situation though to fully process the + // cancellation we need to see what's up, so check to see if the + // operation is done with this code. + // + // Note that in this branch it's known that this operation's waker + // is not registered with the exported task because the exported + // task already delivered us the completion code, which + // automatically deregisters it at this time. + Some(code) => { + match self.as_mut().poll_complete_with_code(None, Some(code)) { + // The operation completed without us needing to cancel it, + // so just convert that to the `Cancel` type. In this + // situation no cancellation is necessary, the async + // operation is now inert, and we can immediately return. + Poll::Ready(result) => { + return self.as_mut().pin_project().0.result_into_cancel(result); + } + + // The operation, despite receiving an update via a code, + // has not yet completed. In this case we do indeed need to + // perform cancellation, so fall through to below. + Poll::Pending => {} + } + } + + // A completion code is not yet available. In this situation we + // deregister our waker from the exported task's waitable set and + // callback handling since we'll be no longer waiting for events. + // Cancellation below happens synchronously. + // + // After we've unregistered fall through to below. + None => { + let waitable = op.in_progress_waitable(in_progress); + self.as_mut().unregister_waker(waitable); + } + } + + // This operation is guaranteed actively in progress at this point. + // That means we really do in fact need to cancel it. Here the + // appropriate cancellation intrinsic for the component model is + // invoked which returns the final completion status for this + // operation. + // + // The completion code is forwarded to `poll_complete_with_code` which + // determines what happened as a result. Note that at this time + // cancellation is required to be a synchronous operation in Rust, even + // if it's async in the component model, since that's the only way for + // this to be sound. Rust doesn't currently have linear types or async + // destructors for example to ensure otherwise that if this were to + // proceed asynchronously that we could rely on it being invoked. + let (op, InProgress(in_progress), _) = self.as_mut().pin_project() else { + unreachable!() + }; + let code = op.in_progress_cancel(in_progress); + match self.as_mut().poll_complete_with_code(None, Some(code)) { + Poll::Ready(result) => self.as_mut().pin_project().0.result_into_cancel(result), + Poll::Pending => unreachable!(), + } + } + + /// Returns whether or not this operation has completed. + pub fn is_done(&self) -> bool { + matches!(self.state, WaitableOperationState::Done) + } +} + +impl Future for WaitableOperation { + type Output = S::Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.poll_complete(cx) + } +} + +impl Drop for WaitableOperation { + fn drop(&mut self) { + // If this operation has already completed then skip cancellation, + // otherwise it's our job to cancel anything in-flight. + if self.is_done() { + return; + } + + // SAFETY: we're in the destructor here so the value `self` is about + // to go away and we can guarantee we're not moving out of it. + let pin = unsafe { Pin::new_unchecked(self) }; + pin.cancel(); + } +} + +impl CompletionStatus { + fn code_mut(self: Pin<&mut Self>) -> &mut Option { + unsafe { &mut self.get_unchecked_mut().code } + } + + fn waker_mut(self: Pin<&mut Self>) -> &mut Option { + unsafe { &mut self.get_unchecked_mut().waker } + } +} diff --git a/tools/vendor/wit-bindgen/src/rt/async_support/waitable_set.rs b/tools/vendor/wit-bindgen/src/rt/async_support/waitable_set.rs new file mode 100644 index 0000000000..f0960946f7 --- /dev/null +++ b/tools/vendor/wit-bindgen/src/rt/async_support/waitable_set.rs @@ -0,0 +1,82 @@ +//! Low-level FFI-like bindings around `waitable-set` in the canonical ABI. + +use std::num::NonZeroU32; + +pub struct WaitableSet(NonZeroU32); + +impl WaitableSet { + pub fn new() -> WaitableSet { + let ret = WaitableSet(NonZeroU32::new(unsafe { new() }).unwrap()); + rtdebug!("waitable-set.new() = {}", ret.0.get()); + ret + } + + pub fn join(&self, waitable: u32) { + rtdebug!("waitable-set.join({waitable}, {})", self.0.get()); + unsafe { join(waitable, self.0.get()) } + } + + pub fn remove_waitable_from_all_sets(waitable: u32) { + rtdebug!("waitable.join({waitable}, 0)"); + unsafe { join(waitable, 0) } + } + + pub fn wait(&self) -> (u32, u32, u32) { + unsafe { + let mut payload = [0; 2]; + rtdebug!("waitable-set.wait({}) = ...", self.0.get()); + let event0 = wait(self.0.get(), &mut payload); + rtdebug!( + "waitable-set.wait({}) = ({event0}, {:#x}, {:#x})", + self.0.get(), + payload[0], + payload[1], + ); + (event0, payload[0], payload[1]) + } + } + + pub fn poll(&self) -> (u32, u32, u32) { + unsafe { + let mut payload = [0; 2]; + rtdebug!("waitable-set.poll({}) = ...", self.0.get()); + let event0 = poll(self.0.get(), &mut payload); + rtdebug!( + "waitable-set.poll({}) = ({event0}, {:#x}, {:#x})", + self.0.get(), + payload[0], + payload[1], + ); + (event0, payload[0], payload[1]) + } + } + + pub fn as_raw(&self) -> u32 { + self.0.get() + } +} + +impl Drop for WaitableSet { + fn drop(&mut self) { + unsafe { + rtdebug!("waitable-set.drop({})", self.0.get()); + drop(self.0.get()); + } + } +} + +extern_wasm! { + #[link(wasm_import_module = "$root")] + unsafe extern "C" { + #[link_name = "[waitable-set-new]"] + fn new() -> u32; + #[link_name = "[waitable-set-drop]"] + fn drop(set: u32); + #[link_name = "[waitable-join]"] + fn join(waitable: u32, set: u32); + #[link_name = "[waitable-set-wait]"] + fn wait(_: u32, _: *mut [u32; 2]) -> u32; + #[link_name = "[waitable-set-poll]"] + fn poll(_: u32, _: *mut [u32; 2]) -> u32; + } +} diff --git a/tools/vendor/wit-bindgen/src/rt/libwit_bindgen_cabi.a b/tools/vendor/wit-bindgen/src/rt/libwit_bindgen_cabi.a new file mode 100644 index 0000000000..4779dd7b23 Binary files /dev/null and b/tools/vendor/wit-bindgen/src/rt/libwit_bindgen_cabi.a differ diff --git a/tools/vendor/wit-bindgen/src/rt/mod.rs b/tools/vendor/wit-bindgen/src/rt/mod.rs new file mode 100644 index 0000000000..a85a37f12f --- /dev/null +++ b/tools/vendor/wit-bindgen/src/rt/mod.rs @@ -0,0 +1,232 @@ +use core::alloc::Layout; +use core::ptr::{self, NonNull}; + +// Re-export `bitflags` so that we can reference it from macros. +#[cfg(feature = "bitflags")] +pub use bitflags; + +#[cfg(not(feature = "bitflags"))] +pub mod bitflags { + #[macro_export] + macro_rules! bitflags { + ( + $(#[$attr:meta])* + $vis:vis struct $name:ident : $repr:ty { + $( + $(#[$flag_attr:meta])* + const $flag:ident = $val:expr; + )* + } + ) => { + $(#[$attr])* + $vis struct $name { + bits: $repr, + } + + impl $name { + $( + $(#[$flag_attr])* + $vis const $flag: Self = Self { bits: $val }; + )* + $vis fn empty() -> Self { + Self { bits: 0 } + } + + $vis fn from_bits_retain(bits: $repr) -> Self { + Self { bits } + } + + $vis fn bits(&self) -> $repr { + self.bits + } + } + + impl core::ops::BitOr<$name> for $name { + type Output = Self; + fn bitor(self, rhs: $name) -> $name { + Self { bits: self.bits | rhs.bits } + } + } + + impl core::ops::BitAnd<$name> for $name { + type Output = Self; + fn bitand(self, rhs: $name) -> $name { + Self { bits: self.bits & rhs.bits } + } + } + + impl core::ops::BitXor<$name> for $name { + type Output = Self; + fn bitxor(self, rhs: $name) -> $name { + Self { bits: self.bits ^ rhs.bits } + } + } + }; + } + + pub use crate::bitflags; +} + +/// For more information about this see `./ci/rebuild-libwit-bindgen-cabi.sh`. +#[cfg(not(target_env = "p2"))] +mod wit_bindgen_cabi_realloc; + +/// This function is called from generated bindings and will be deleted by +/// the linker. The purpose of this function is to force a reference to the +/// symbol `cabi_realloc` to make its way through to the final linker +/// command line. That way `wasm-ld` will pick it up, see it needs to be +/// exported, and then export it. +/// +/// For more information about this see `./ci/rebuild-libwit-bindgen-cabi.sh`. +pub fn maybe_link_cabi_realloc() { + #[cfg(all(target_family = "wasm", not(target_env = "p2")))] + { + unsafe extern "C" { + fn cabi_realloc( + old_ptr: *mut u8, + old_len: usize, + align: usize, + new_len: usize, + ) -> *mut u8; + } + // Force the `cabi_realloc` symbol to be referenced from here. This + // is done with a `#[used]` Rust `static` to ensure that this + // reference makes it all the way to the linker before it's + // considered for garbage collection. When the linker sees it it'll + // remove this `static` here (due to it not actually being needed) + // but the linker will have at that point seen the `cabi_realloc` + // symbol and it should get exported. + #[used] + static _NAME_DOES_NOT_MATTER: unsafe extern "C" fn( + *mut u8, + usize, + usize, + usize, + ) -> *mut u8 = cabi_realloc; + } +} + +/// NB: this function is called by a generated function in the +/// `cabi_realloc` module above. It's otherwise never explicitly called. +/// +/// For more information about this see `./ci/rebuild-libwit-bindgen-cabi.sh`. +#[cfg(not(target_env = "p2"))] +pub unsafe fn cabi_realloc( + old_ptr: *mut u8, + old_len: usize, + align: usize, + new_len: usize, +) -> *mut u8 { + use alloc::alloc::{Layout, alloc as allocate, handle_alloc_error, realloc}; + + let layout; + let ptr = unsafe { + if old_len == 0 { + if new_len == 0 { + return align as *mut u8; + } + layout = Layout::from_size_align_unchecked(new_len, align); + allocate(layout) + } else { + debug_assert_ne!(new_len, 0, "non-zero old_len requires non-zero new_len!"); + layout = Layout::from_size_align_unchecked(old_len, align); + realloc(old_ptr, layout, new_len) + } + }; + if ptr.is_null() { + // Print a nice message in debug mode, but in release mode don't + // pull in so many dependencies related to printing so just emit an + // `unreachable` instruction. + if cfg!(debug_assertions) { + handle_alloc_error(layout); + } else { + #[cfg(target_arch = "wasm32")] + core::arch::wasm32::unreachable(); + #[cfg(not(target_arch = "wasm32"))] + unreachable!(); + } + } + return ptr; +} + +/// Provide a hook for generated export functions to run static constructors at +/// most once. +/// +/// wit-bindgen-rust generates a call to this function at the start of all +/// component export functions. Importantly, it is not called as part of +/// `cabi_realloc`, which is a *core* export func, but should not execute ctors. +#[cfg(target_arch = "wasm32")] +pub fn run_ctors_once() { + static mut RUN: bool = false; + unsafe { + if !RUN { + // This function is synthesized by `wasm-ld` to run all static + // constructors. wasm-ld will either provide an implementation + // of this symbol, or synthesize a wrapper around each + // exported function to (unconditionally) run ctors. By using + // this function, the linked module is opting into "manually" + // running ctors. + unsafe extern "C" { + fn __wasm_call_ctors(); + } + __wasm_call_ctors(); + RUN = true; + } + } +} + +/// Support for using the Component Model Async ABI +#[cfg(feature = "async")] +pub mod async_support; + +/// Cleanup helper used to deallocate blocks of canonical ABI data from +/// lowerings. +pub struct Cleanup { + ptr: NonNull, + layout: Layout, +} + +// Usage of the returned pointer is always unsafe and must abide by these +// conventions, but this structure itself has no inherent reason to not be +// send/sync. +unsafe impl Send for Cleanup {} +unsafe impl Sync for Cleanup {} + +impl Cleanup { + /// Allocates a chunk of memory with `layout` and returns an object to clean + /// it up. + /// + /// Always returns a pointer which is null if `layout` has size zero. The + /// optional cleanup returned will be present if `layout` has a non-zero + /// size. When dropped `Cleanup` will deallocate the pointer returned. + pub fn new(layout: Layout) -> (*mut u8, Option) { + use alloc::alloc; + + if layout.size() == 0 { + return (ptr::null_mut(), None); + } + let ptr = unsafe { alloc::alloc(layout) }; + let ptr = match NonNull::new(ptr) { + Some(ptr) => ptr, + None => alloc::handle_alloc_error(layout), + }; + (ptr.as_ptr(), Some(Cleanup { ptr, layout })) + } + + /// Discards this cleanup to leak its memory or intentionally transfer + /// ownership to some other location. + pub fn forget(self) { + core::mem::forget(self); + } +} + +impl Drop for Cleanup { + fn drop(&mut self) { + unsafe { + for i in 0..self.layout.size() { + *self.ptr.add(i).as_ptr() = 0xff; + } + alloc::alloc::dealloc(self.ptr.as_ptr(), self.layout); + } + } +} diff --git a/tools/vendor/wit-bindgen/src/rt/wit_bindgen_cabi_realloc.c b/tools/vendor/wit-bindgen/src/rt/wit_bindgen_cabi_realloc.c new file mode 100644 index 0000000000..b838d63fdf --- /dev/null +++ b/tools/vendor/wit-bindgen/src/rt/wit_bindgen_cabi_realloc.c @@ -0,0 +1,10 @@ +// This file is generated by ./ci/rebuild-libwit-bindgen-cabi.sh + +#include + +extern void *cabi_realloc_wit_bindgen_0_51_0(void *ptr, size_t old_size, size_t align, size_t new_size); + +__attribute__((__weak__, __export_name__("cabi_realloc"))) +void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { + return cabi_realloc_wit_bindgen_0_51_0(ptr, old_size, align, new_size); +} diff --git a/tools/vendor/wit-bindgen/src/rt/wit_bindgen_cabi_realloc.o b/tools/vendor/wit-bindgen/src/rt/wit_bindgen_cabi_realloc.o new file mode 100644 index 0000000000..79c8e9ba7d Binary files /dev/null and b/tools/vendor/wit-bindgen/src/rt/wit_bindgen_cabi_realloc.o differ diff --git a/tools/vendor/wit-bindgen/src/rt/wit_bindgen_cabi_realloc.rs b/tools/vendor/wit-bindgen/src/rt/wit_bindgen_cabi_realloc.rs new file mode 100644 index 0000000000..b336670d9b --- /dev/null +++ b/tools/vendor/wit-bindgen/src/rt/wit_bindgen_cabi_realloc.rs @@ -0,0 +1,11 @@ +// This file is generated by ./ci/rebuild-libwit-bindgen-cabi.sh + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn cabi_realloc_wit_bindgen_0_51_0( + old_ptr: *mut u8, + old_len: usize, + align: usize, + new_len: usize, +) -> *mut u8 { + unsafe { crate::rt::cabi_realloc(old_ptr, old_len, align, new_len) } +} diff --git a/tools/vendor/wit-bindgen/src/rt/wit_bindgen_cabi_wasip3.c b/tools/vendor/wit-bindgen/src/rt/wit_bindgen_cabi_wasip3.c new file mode 100644 index 0000000000..2e4a506176 --- /dev/null +++ b/tools/vendor/wit-bindgen/src/rt/wit_bindgen_cabi_wasip3.c @@ -0,0 +1,12 @@ +// This file is generated by ./ci/rebuild-libwit-bindgen-cabi.sh + +#include + +static void *WASIP3_TASK = NULL; + +__attribute__((__weak__)) +void *wasip3_task_set(void *ptr) { + void *ret = WASIP3_TASK; + WASIP3_TASK = ptr; + return ret; +} diff --git a/tools/vendor/wit-bindgen/src/rt/wit_bindgen_cabi_wasip3.o b/tools/vendor/wit-bindgen/src/rt/wit_bindgen_cabi_wasip3.o new file mode 100644 index 0000000000..5e7eea4c81 Binary files /dev/null and b/tools/vendor/wit-bindgen/src/rt/wit_bindgen_cabi_wasip3.o differ diff --git a/tools/vendor/wit-bindgen/wasi-cli@0.2.0.wasm b/tools/vendor/wit-bindgen/wasi-cli@0.2.0.wasm new file mode 100644 index 0000000000..1f25a7c4fb Binary files /dev/null and b/tools/vendor/wit-bindgen/wasi-cli@0.2.0.wasm differ
+

wasip2

+ +A
Bytecode Alliance project + +

+ WASIp2 API Bindings for Rust +

+ +

+ Crates.io version + Download + docs.rs docs +

+