From b1ad6c43eb04fe5028bdfdb3a345d0e1b4291c0c Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Wed, 7 Oct 2020 03:44:43 +0000 Subject: [PATCH] Bug 1669055: Vendor Cranelift e22e2c3722f2fbccd3c8d3230119fa04c332c69c. r=jseward This patch pulls in an updated Cranelift with a new validation strategy, introduced by bytecodealliance/wasmtime#2059. This new design validates the Wasm module as it parses the function bodies. A subsequent patch will adapt Baldrdash to work with this. Differential Revision: https://phabricator.services.mozilla.com/D92503 --- .cargo/config.in | 2 +- Cargo.lock | 34 +- Cargo.toml | 4 +- .../cranelift-codegen/.cargo-checksum.json | 2 +- third_party/rust/cranelift-codegen/Cargo.toml | 2 +- .../src/isa/x64/inst/emit.rs | 37 +- .../cranelift-codegen/src/isa/x64/inst/mod.rs | 156 +- .../cranelift-codegen/src/isa/x64/lower.rs | 206 +- .../rust/cranelift-wasm/.cargo-checksum.json | 2 +- third_party/rust/cranelift-wasm/Cargo.toml | 3 +- .../cranelift-wasm/src/code_translator.rs | 818 +++---- .../rust/cranelift-wasm/src/environ/dummy.rs | 29 +- .../rust/cranelift-wasm/src/environ/spec.rs | 16 +- .../cranelift-wasm/src/func_translator.rs | 177 +- third_party/rust/cranelift-wasm/src/lib.rs | 5 + .../cranelift-wasm/src/module_translator.rs | 71 +- .../cranelift-wasm/src/sections_translator.rs | 30 +- .../cranelift-wasm/src/translation_utils.rs | 65 +- .../cranelift-wasm/tests/wasm_testsuite.rs | 12 +- .../rust/itertools-0.8.0/.cargo-checksum.json | 1 + third_party/rust/itertools-0.8.0/Cargo.toml | 47 + .../rust/itertools-0.8.0/LICENSE-APACHE | 201 ++ third_party/rust/itertools-0.8.0/LICENSE-MIT | 25 + third_party/rust/itertools-0.8.0/README.rst | 526 ++++ .../rust/itertools-0.8.0/benches/bench1.rs | 733 ++++++ .../rust/itertools-0.8.0/benches/extra/mod.rs | 4 + .../benches/extra/zipslices.rs | 189 ++ .../itertools-0.8.0/benches/tree_fold1.rs | 126 + .../benches/tuple_combinations.rs | 97 + .../rust/itertools-0.8.0/benches/tuples.rs | 190 ++ .../rust/itertools-0.8.0/examples/iris.data | 150 ++ .../rust/itertools-0.8.0/examples/iris.rs | 141 ++ .../rust/itertools-0.8.0/src/adaptors/mod.rs | 1278 ++++++++++ .../src/adaptors/multi_product.rs | 220 ++ .../rust/itertools-0.8.0/src/combinations.rs | 165 ++ .../rust/itertools-0.8.0/src/concat_impl.rs | 22 + .../itertools-0.8.0/src/cons_tuples_impl.rs | 68 + third_party/rust/itertools-0.8.0/src/diff.rs | 61 + .../itertools-0.8.0/src/either_or_both.rs | 58 + .../rust/itertools-0.8.0/src/format.rs | 113 + third_party/rust/itertools-0.8.0/src/free.rs | 236 ++ .../rust/itertools-0.8.0/src/group_map.rs | 22 + .../rust/itertools-0.8.0/src/groupbylazy.rs | 571 +++++ .../rust/itertools-0.8.0/src/impl_macros.rs | 14 + .../rust/itertools-0.8.0/src/intersperse.rs | 60 + .../rust/itertools-0.8.0/src/kmerge_impl.rs | 256 ++ third_party/rust/itertools-0.8.0/src/lib.rs | 2176 +++++++++++++++++ .../rust/itertools-0.8.0/src/merge_join.rs | 87 + .../rust/itertools-0.8.0/src/minmax.rs | 114 + .../itertools-0.8.0/src/multipeek_impl.rs | 104 + .../rust/itertools-0.8.0/src/pad_tail.rs | 83 + .../itertools-0.8.0/src/peeking_take_while.rs | 149 ++ .../src/process_results_impl.rs | 81 + .../itertools-0.8.0/src/put_back_n_impl.rs | 63 + .../rust/itertools-0.8.0/src/rciter_impl.rs | 98 + .../rust/itertools-0.8.0/src/repeatn.rs | 54 + .../rust/itertools-0.8.0/src/size_hint.rs | 104 + .../rust/itertools-0.8.0/src/sources.rs | 190 ++ third_party/rust/itertools-0.8.0/src/tee.rs | 78 + .../rust/itertools-0.8.0/src/tuple_impl.rs | 266 ++ .../rust/itertools-0.8.0/src/unique_impl.rs | 134 + .../rust/itertools-0.8.0/src/with_position.rs | 90 + .../rust/itertools-0.8.0/src/zip_eq_impl.rs | 60 + .../rust/itertools-0.8.0/src/zip_longest.rs | 78 + .../rust/itertools-0.8.0/src/ziptuple.rs | 111 + .../rust/itertools-0.8.0/tests/merge_join.rs | 110 + .../tests/peeking_take_while.rs | 53 + .../rust/itertools-0.8.0/tests/quick.rs | 1017 ++++++++ .../rust/itertools-0.8.0/tests/test_core.rs | 246 ++ .../rust/itertools-0.8.0/tests/test_std.rs | 735 ++++++ .../rust/itertools-0.8.0/tests/tuples.rs | 88 + third_party/rust/itertools-0.8.0/tests/zip.rs | 65 + .../rust/itertools/.cargo-checksum.json | 2 +- third_party/rust/itertools/CHANGELOG.md | 319 +++ third_party/rust/itertools/Cargo.lock | 584 +++++ third_party/rust/itertools/Cargo.toml | 36 +- third_party/rust/itertools/README.rst | 475 +--- third_party/rust/itertools/benches/bench1.rs | 952 +++++--- .../benches/combinations_with_replacement.rs | 40 + .../rust/itertools/benches/extra/mod.rs | 2 - .../rust/itertools/benches/extra/zipslices.rs | 1 - .../itertools/benches/fold_specialization.rs | 73 + .../rust/itertools/benches/tree_fold1.rs | 94 +- .../itertools/benches/tuple_combinations.rs | 136 +- third_party/rust/itertools/benches/tuples.rs | 123 +- third_party/rust/itertools/examples/iris.rs | 6 +- .../rust/itertools/src/adaptors/mod.rs | 308 ++- .../itertools/src/adaptors/multi_product.rs | 6 +- .../rust/itertools/src/combinations.rs | 124 +- .../src/combinations_with_replacement.rs | 107 + third_party/rust/itertools/src/concat_impl.rs | 2 +- .../rust/itertools/src/cons_tuples_impl.rs | 6 +- third_party/rust/itertools/src/diff.rs | 4 +- .../rust/itertools/src/either_or_both.rs | 140 +- .../rust/itertools/src/exactly_one_err.rs | 58 + third_party/rust/itertools/src/format.rs | 17 +- third_party/rust/itertools/src/free.rs | 16 +- third_party/rust/itertools/src/groupbylazy.rs | 6 +- third_party/rust/itertools/src/impl_macros.rs | 10 + third_party/rust/itertools/src/intersperse.rs | 21 +- third_party/rust/itertools/src/kmerge_impl.rs | 106 +- third_party/rust/itertools/src/lazy_buffer.rs | 59 + third_party/rust/itertools/src/lib.rs | 729 +++++- third_party/rust/itertools/src/merge_join.rs | 86 +- .../rust/itertools/src/multipeek_impl.rs | 6 +- third_party/rust/itertools/src/pad_tail.rs | 6 +- .../rust/itertools/src/peeking_take_while.rs | 9 +- .../rust/itertools/src/permutations.rs | 279 +++ .../itertools/src/process_results_impl.rs | 2 +- .../rust/itertools/src/put_back_n_impl.rs | 2 +- third_party/rust/itertools/src/rciter_impl.rs | 4 +- third_party/rust/itertools/src/repeatn.rs | 6 +- third_party/rust/itertools/src/sources.rs | 5 +- third_party/rust/itertools/src/tee.rs | 2 +- third_party/rust/itertools/src/tuple_impl.rs | 45 +- third_party/rust/itertools/src/unique_impl.rs | 6 +- .../rust/itertools/src/with_position.rs | 7 + third_party/rust/itertools/src/zip_longest.rs | 2 +- .../itertools/tests/adaptors_no_collect.rs | 47 + .../itertools/tests/fold_specialization.rs | 13 + .../rust/itertools/tests/merge_join.rs | 2 - .../itertools/tests/peeking_take_while.rs | 3 - third_party/rust/itertools/tests/quick.rs | 176 +- .../rust/itertools/tests/specializations.rs | 149 ++ third_party/rust/itertools/tests/test_core.rs | 40 +- third_party/rust/itertools/tests/test_std.rs | 90 +- third_party/rust/itertools/tests/tuples.rs | 2 - third_party/rust/itertools/tests/zip.rs | 2 - .../rust/wasmparser/.cargo-checksum.json | 2 +- third_party/rust/wasmparser/Cargo.lock | 113 +- third_party/rust/wasmparser/Cargo.toml | 2 +- .../rust/wasmparser/benches/benchmark.rs | 21 +- .../rust/wasmparser/src/binary_reader.rs | 370 +-- third_party/rust/wasmparser/src/lib.rs | 42 +- third_party/rust/wasmparser/src/limits.rs | 5 +- .../rust/wasmparser/src/module_resources.rs | 396 +-- .../wasmparser/src/operators_validator.rs | 1908 +++++++-------- third_party/rust/wasmparser/src/primitives.rs | 104 +- .../wasmparser/src/readers/code_section.rs | 6 +- third_party/rust/wasmparser/src/validator.rs | 499 ++-- .../rust/wasmparser/src/validator/func.rs | 161 +- 141 files changed, 18493 insertions(+), 4368 deletions(-) create mode 100644 third_party/rust/itertools-0.8.0/.cargo-checksum.json create mode 100644 third_party/rust/itertools-0.8.0/Cargo.toml create mode 100644 third_party/rust/itertools-0.8.0/LICENSE-APACHE create mode 100644 third_party/rust/itertools-0.8.0/LICENSE-MIT create mode 100644 third_party/rust/itertools-0.8.0/README.rst create mode 100644 third_party/rust/itertools-0.8.0/benches/bench1.rs create mode 100644 third_party/rust/itertools-0.8.0/benches/extra/mod.rs create mode 100644 third_party/rust/itertools-0.8.0/benches/extra/zipslices.rs create mode 100644 third_party/rust/itertools-0.8.0/benches/tree_fold1.rs create mode 100644 third_party/rust/itertools-0.8.0/benches/tuple_combinations.rs create mode 100644 third_party/rust/itertools-0.8.0/benches/tuples.rs create mode 100644 third_party/rust/itertools-0.8.0/examples/iris.data create mode 100644 third_party/rust/itertools-0.8.0/examples/iris.rs create mode 100644 third_party/rust/itertools-0.8.0/src/adaptors/mod.rs create mode 100644 third_party/rust/itertools-0.8.0/src/adaptors/multi_product.rs create mode 100644 third_party/rust/itertools-0.8.0/src/combinations.rs create mode 100644 third_party/rust/itertools-0.8.0/src/concat_impl.rs create mode 100644 third_party/rust/itertools-0.8.0/src/cons_tuples_impl.rs create mode 100644 third_party/rust/itertools-0.8.0/src/diff.rs create mode 100644 third_party/rust/itertools-0.8.0/src/either_or_both.rs create mode 100644 third_party/rust/itertools-0.8.0/src/format.rs create mode 100644 third_party/rust/itertools-0.8.0/src/free.rs create mode 100644 third_party/rust/itertools-0.8.0/src/group_map.rs create mode 100644 third_party/rust/itertools-0.8.0/src/groupbylazy.rs create mode 100644 third_party/rust/itertools-0.8.0/src/impl_macros.rs create mode 100644 third_party/rust/itertools-0.8.0/src/intersperse.rs create mode 100644 third_party/rust/itertools-0.8.0/src/kmerge_impl.rs create mode 100644 third_party/rust/itertools-0.8.0/src/lib.rs create mode 100644 third_party/rust/itertools-0.8.0/src/merge_join.rs create mode 100644 third_party/rust/itertools-0.8.0/src/minmax.rs create mode 100644 third_party/rust/itertools-0.8.0/src/multipeek_impl.rs create mode 100644 third_party/rust/itertools-0.8.0/src/pad_tail.rs create mode 100644 third_party/rust/itertools-0.8.0/src/peeking_take_while.rs create mode 100644 third_party/rust/itertools-0.8.0/src/process_results_impl.rs create mode 100644 third_party/rust/itertools-0.8.0/src/put_back_n_impl.rs create mode 100644 third_party/rust/itertools-0.8.0/src/rciter_impl.rs create mode 100644 third_party/rust/itertools-0.8.0/src/repeatn.rs create mode 100644 third_party/rust/itertools-0.8.0/src/size_hint.rs create mode 100644 third_party/rust/itertools-0.8.0/src/sources.rs create mode 100644 third_party/rust/itertools-0.8.0/src/tee.rs create mode 100644 third_party/rust/itertools-0.8.0/src/tuple_impl.rs create mode 100644 third_party/rust/itertools-0.8.0/src/unique_impl.rs create mode 100644 third_party/rust/itertools-0.8.0/src/with_position.rs create mode 100644 third_party/rust/itertools-0.8.0/src/zip_eq_impl.rs create mode 100644 third_party/rust/itertools-0.8.0/src/zip_longest.rs create mode 100644 third_party/rust/itertools-0.8.0/src/ziptuple.rs create mode 100644 third_party/rust/itertools-0.8.0/tests/merge_join.rs create mode 100644 third_party/rust/itertools-0.8.0/tests/peeking_take_while.rs create mode 100644 third_party/rust/itertools-0.8.0/tests/quick.rs create mode 100644 third_party/rust/itertools-0.8.0/tests/test_core.rs create mode 100644 third_party/rust/itertools-0.8.0/tests/test_std.rs create mode 100644 third_party/rust/itertools-0.8.0/tests/tuples.rs create mode 100644 third_party/rust/itertools-0.8.0/tests/zip.rs create mode 100644 third_party/rust/itertools/CHANGELOG.md create mode 100644 third_party/rust/itertools/Cargo.lock create mode 100644 third_party/rust/itertools/benches/combinations_with_replacement.rs create mode 100644 third_party/rust/itertools/benches/fold_specialization.rs create mode 100644 third_party/rust/itertools/src/combinations_with_replacement.rs create mode 100644 third_party/rust/itertools/src/exactly_one_err.rs create mode 100644 third_party/rust/itertools/src/lazy_buffer.rs create mode 100644 third_party/rust/itertools/src/permutations.rs create mode 100644 third_party/rust/itertools/tests/adaptors_no_collect.rs create mode 100644 third_party/rust/itertools/tests/fold_specialization.rs create mode 100644 third_party/rust/itertools/tests/specializations.rs diff --git a/.cargo/config.in b/.cargo/config.in index 04182b477320d..3046897f5df01 100644 --- a/.cargo/config.in +++ b/.cargo/config.in @@ -60,7 +60,7 @@ rev = "3224e2dee65c0726c448484d4c3c43956b9330ec" [source."https://github.com/bytecodealliance/wasmtime"] git = "https://github.com/bytecodealliance/wasmtime" replace-with = "vendored-sources" -rev = "57fed697920cb888c6cb7e406d13518f7edd12ea" +rev = "e22e2c3722f2fbccd3c8d3230119fa04c332c69c" [source."https://github.com/badboy/failure"] git = "https://github.com/badboy/failure" diff --git a/Cargo.lock b/Cargo.lock index fac712fa46cf4..7236f6e9ea494 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -762,7 +762,7 @@ dependencies = [ [[package]] name = "cranelift-bforest" version = "0.67.0" -source = "git+https://github.com/bytecodealliance/wasmtime?rev=57fed697920cb888c6cb7e406d13518f7edd12ea#57fed697920cb888c6cb7e406d13518f7edd12ea" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=e22e2c3722f2fbccd3c8d3230119fa04c332c69c#e22e2c3722f2fbccd3c8d3230119fa04c332c69c" dependencies = [ "cranelift-entity 0.67.0", ] @@ -770,7 +770,7 @@ dependencies = [ [[package]] name = "cranelift-codegen" version = "0.67.0" -source = "git+https://github.com/bytecodealliance/wasmtime?rev=57fed697920cb888c6cb7e406d13518f7edd12ea#57fed697920cb888c6cb7e406d13518f7edd12ea" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=e22e2c3722f2fbccd3c8d3230119fa04c332c69c#e22e2c3722f2fbccd3c8d3230119fa04c332c69c" dependencies = [ "byteorder", "cranelift-bforest", @@ -787,7 +787,7 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" version = "0.67.0" -source = "git+https://github.com/bytecodealliance/wasmtime?rev=57fed697920cb888c6cb7e406d13518f7edd12ea#57fed697920cb888c6cb7e406d13518f7edd12ea" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=e22e2c3722f2fbccd3c8d3230119fa04c332c69c#e22e2c3722f2fbccd3c8d3230119fa04c332c69c" dependencies = [ "cranelift-codegen-shared", "cranelift-entity 0.67.0", @@ -796,7 +796,7 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" version = "0.67.0" -source = "git+https://github.com/bytecodealliance/wasmtime?rev=57fed697920cb888c6cb7e406d13518f7edd12ea#57fed697920cb888c6cb7e406d13518f7edd12ea" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=e22e2c3722f2fbccd3c8d3230119fa04c332c69c#e22e2c3722f2fbccd3c8d3230119fa04c332c69c" [[package]] name = "cranelift-entity" @@ -806,12 +806,12 @@ source = "git+https://github.com/PLSysSec/lucet_sandbox_compiler?rev=477d8fc53a6 [[package]] name = "cranelift-entity" version = "0.67.0" -source = "git+https://github.com/bytecodealliance/wasmtime?rev=57fed697920cb888c6cb7e406d13518f7edd12ea#57fed697920cb888c6cb7e406d13518f7edd12ea" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=e22e2c3722f2fbccd3c8d3230119fa04c332c69c#e22e2c3722f2fbccd3c8d3230119fa04c332c69c" [[package]] name = "cranelift-frontend" version = "0.67.0" -source = "git+https://github.com/bytecodealliance/wasmtime?rev=57fed697920cb888c6cb7e406d13518f7edd12ea#57fed697920cb888c6cb7e406d13518f7edd12ea" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=e22e2c3722f2fbccd3c8d3230119fa04c332c69c#e22e2c3722f2fbccd3c8d3230119fa04c332c69c" dependencies = [ "cranelift-codegen", "log", @@ -822,14 +822,15 @@ dependencies = [ [[package]] name = "cranelift-wasm" version = "0.67.0" -source = "git+https://github.com/bytecodealliance/wasmtime?rev=57fed697920cb888c6cb7e406d13518f7edd12ea#57fed697920cb888c6cb7e406d13518f7edd12ea" +source = "git+https://github.com/bytecodealliance/wasmtime?rev=e22e2c3722f2fbccd3c8d3230119fa04c332c69c#e22e2c3722f2fbccd3c8d3230119fa04c332c69c" dependencies = [ "cranelift-codegen", "cranelift-entity 0.67.0", "cranelift-frontend", + "itertools 0.9.0", "log", "thiserror", - "wasmparser 0.59.0", + "wasmparser 0.63.0", ] [[package]] @@ -2422,6 +2423,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.4" @@ -3884,7 +3894,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "537aa19b95acde10a12fec4301466386f757403de4cd4e5b4fa78fb5ecb18f72" dependencies = [ "anyhow", - "itertools", + "itertools 0.8.0", "proc-macro2", "quote", "syn", @@ -4738,7 +4748,7 @@ dependencies = [ "hashbrown", "hashglobe", "indexmap", - "itertools", + "itertools 0.8.0", "itoa", "lazy_static", "log", @@ -5551,9 +5561,9 @@ checksum = "073da89bf1c84db000dd68ce660c1b4a08e3a2d28fd1e3394ab9e7abdde4a0f8" [[package]] name = "wasmparser" -version = "0.59.0" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a950e6a618f62147fd514ff445b2a0b53120d382751960797f85f058c7eda9b9" +checksum = "57da5d7300428d75d8b3cdfb736e41ee6af8926d69c1de2f201a1a22f234b7b5" [[package]] name = "wast" diff --git a/Cargo.toml b/Cargo.toml index d0e451aef746e..d1b826e2b388a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,8 +75,8 @@ failure_derive = { git = "https://github.com/badboy/failure", rev = "64af847bc5f [patch.crates-io.cranelift-codegen] git = "https://github.com/bytecodealliance/wasmtime" -rev = "57fed697920cb888c6cb7e406d13518f7edd12ea" +rev = "e22e2c3722f2fbccd3c8d3230119fa04c332c69c" [patch.crates-io.cranelift-wasm] git = "https://github.com/bytecodealliance/wasmtime" -rev = "57fed697920cb888c6cb7e406d13518f7edd12ea" +rev = "e22e2c3722f2fbccd3c8d3230119fa04c332c69c" diff --git a/third_party/rust/cranelift-codegen/.cargo-checksum.json b/third_party/rust/cranelift-codegen/.cargo-checksum.json index 81d1266ba9577..8780beda780a4 100644 --- a/third_party/rust/cranelift-codegen/.cargo-checksum.json +++ b/third_party/rust/cranelift-codegen/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"0e907958c974e4ebf351d9d19c38b08699e646e29803dab0f383b2d803b614e2","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"e5127227a7db4a8aa92fa6613ed71801025790e696bb41b0323fb7f3c6f7495a","build.rs":"afe67e345d21f9a137ecad473bb595a8b4983d5d435ca0505c91021954c215a7","src/abi.rs":"8922d75a807798945c64ee135f7f22c58e97d8412c6d5b6bf6c4e75c6823d308","src/binemit/memorysink.rs":"6eb4af661c11461a85612729553eec8581984dd7ce5a3cb905dc3667a49ab954","src/binemit/mod.rs":"4fbd7566c56a02a9faa18a02a7f3ba97297a42bba62c8c60a291a160760abfad","src/binemit/relaxation.rs":"0547f1a33ea176b281d6f09d1bd7a253dcba895785008e61c114861f1a86307f","src/binemit/shrink.rs":"552d64dff3b044bca77f39891d0e39ee619f6ec0669bf9917132c97becea79b0","src/binemit/stack_map.rs":"cae6ef55b748a3700909d26f408c3d244a7dd76c0918b9b04c17127c18dbf8bf","src/bitset.rs":"ff667c19a63a6a9bcf06f99a46b6db839323f5d3d5cc5e7b00f1ab77d331fc77","src/cfg_printer.rs":"a4cd85ecb2c403b29dc3fc6c60ee7f97b6ed602e8ba88ec87accb988d009271e","src/constant_hash.rs":"8f53afb38891fbdab8553c7a89e61f0ce30947a22fb24ae84523c9a1ab025c3f","src/context.rs":"e3e1644565c1a6b6dd21daae36f9b22e9f69851fbb3c86e33b1756bb9a2e020e","src/cursor.rs":"eaa0e4ea65bec30aa79e2fb62e89c713b1adec6eeddd5592237d7fcce47c5fa7","src/dbg.rs":"1898d94cff0975815eb348651702e95c8f2f63886501d3b7043ee75668480472","src/dce.rs":"b98545dbf5c8d0c4a33b2ec6cd905d6371fe843481ad608ff59b4a009fc9be19","src/divconst_magic_numbers.rs":"e7f8f46f3a25ed7155890e9d76c10f5dde9dc5bbcebb623c8166fa415abdd959","src/dominator_tree.rs":"b3a5c7837150390a78ade90807965dfcb8768a7e3ae9ee02f2e4a1ad8f3bafa9","src/flowgraph.rs":"71490e2f7a1ea905758a258b2bebe77f125302f446f9839dd60003fdafaef5fe","src/fx.rs":"8a5d07487906d8316a179e826fcd817a92a4860686256a6fd9d78ba47c63f330","src/inst_predicates.rs":"ba71bba10250fada4f04bcd6690583b077061bab988c4d6609c41e8af7de5e98","src/ir/atomic_rmw_op.rs":"44311956feebef2ec01a0156e690a0d09f66b616e401d54a5fc5c9769c448be5","src/ir/builder.rs":"3425957b118a0c40847ef7f473543baef5a24f0c7d8af198955180c8f297ee5a","src/ir/constant.rs":"9c42014b7182a89bc2387e13d3956bbbd98025f32a3abeb5d253d2ba56ad279c","src/ir/dfg.rs":"e803858c6cef60cec09a82d686a73715d0eb142ac96742023cd6112dcf87073a","src/ir/entities.rs":"f0b49b9c351b94703d6c878b564e53a30010c142c47effaf603ec4ade784686b","src/ir/extfunc.rs":"d901ee549f2cf980d807481c3dd6c2b89db241044d465492146bf44db05e9f86","src/ir/extname.rs":"977b2e380094e99cfdd080112bda709ca61a6d942e1e4c2015d39eaf3ea80f4c","src/ir/function.rs":"3034e03ac2e6bcf300b91c9df03e144550ff6c91ef76b105c48ccbc7a752401b","src/ir/globalvalue.rs":"2f3a54cc2ade91439536b02c46ce790c3634a386a4cc4d27d7da7ad929b8bb90","src/ir/heap.rs":"a59d3e5901412b53c0b53a8cdf10765ff5921de9c410ae9acea226c89827df3c","src/ir/immediates.rs":"2f5366e9cd9f7731fdf4d9038bdd3c9976ea6dce38626ede289b37580407da16","src/ir/instructions.rs":"a25709234ce737a776060224cc10f61e0fc9b9ce51a020af7d1c5acb127658e4","src/ir/jumptable.rs":"184fa640957e8b0e451dc594e4b348f9aff5fb5b54ab3d856ab19df25f4f3322","src/ir/layout.rs":"2956643a822e67a0c1196f8d3c81af11d0c0122b2d97427ce3ed0c57bb633cbf","src/ir/libcall.rs":"7c9255bdef9a16937d817a3bfd542be4c2734eea3c8b8a870ff411eac2efaad8","src/ir/memflags.rs":"dbcf3798ab66dc764b73fb7f139a621c54cc6bcc683f1f70a33ed7e8c3486bfd","src/ir/mod.rs":"14cf95dae76a6192c755ed74837c3a36d92827ccdbe2e73f805abea29fc723d8","src/ir/progpoint.rs":"a985a7fccde1e985ca24bf2344f16faa4cda7cffb30cb56bf27fabbb5727bc62","src/ir/sourceloc.rs":"67c66adace734645ddf311ecb22e975f20756e07c91e10f698a830cfc2cd08aa","src/ir/stackslot.rs":"433b61e05263f40c022f73b0ab1141e3e45b348a0309ca02546e6450b2957792","src/ir/table.rs":"dcc3b663a989b2b084402b08dc9a0e928dbd052e194a46a1886cc6f0cf1a5f2c","src/ir/trapcode.rs":"7948d0c387f75695c5d1e03c2ea6808954f0e658ba3cbd636fbbbbbd9a623561","src/ir/types.rs":"dfd9470aa4325adc252eb7ebb2951a5b98d22d0dc31be482419f5cbbb74bf86d","src/ir/valueloc.rs":"628f292ee6b8375e07f360edaf3609b522fd4d7bea131fb8ad3cab237927e7ab","src/isa/aarch64/abi.rs":"88684c74cae823f43f696f44d92325b7ef7b1dd7dbaf98e41ad95ec5b2daeffa","src/isa/aarch64/inst/args.rs":"531232293e355312fb9aff23aa0be1b1168f5456345d3f1958ca7eccd58ac3d8","src/isa/aarch64/inst/emit.rs":"2d736168fad643aea7801c6c7e2593ac7519ec999cd3dcfc76d5f1a4f1b1915b","src/isa/aarch64/inst/emit_tests.rs":"43eb00924a9523d8d2a1be36adfab8b2e7c5c47b2445e819251bb785a40662fd","src/isa/aarch64/inst/imms.rs":"c99980c31bacf5454f9e299e9e351fdddd71b0ec5106c282b8504ee7462777f5","src/isa/aarch64/inst/mod.rs":"4b60b097644401f7095285be6a1f353d3696ce3dc767d5b59d042678adde58a5","src/isa/aarch64/inst/regs.rs":"2e3f52d48b5963a85a275823ee1fd8bc5debf8ee310c4e8cddc77d04f4da45ea","src/isa/aarch64/lower.rs":"c484e991eb583fc6ca21f394bac3374c3d0241911b71c017ab51edae79f3c66f","src/isa/aarch64/lower_inst.rs":"397d5668f5a1eeae41b47f828654366252a4058ace15b2d334b515bf293c89e1","src/isa/aarch64/mod.rs":"8343d3d9f5273ba26146ac3278429b6b0855f1dcbac0de2af62b921a1f46f137","src/isa/arm32/abi.rs":"9be6d9485af2f3e33424ef0edb8fbf641a86787c9ba5c49b09c2d6b6d375c935","src/isa/arm32/inst/args.rs":"3c00b0876c4d57130c2c1ccb56940886f49b09776ab37acbfc1817661e6e9651","src/isa/arm32/inst/emit.rs":"962a16039b9a1540a2e591015f289f59821594576639b20ff0d691cf78630c67","src/isa/arm32/inst/emit_tests.rs":"17dd44aece41b54979de00c6f1e0647af6cdd4254abb8e629797d5611a1a9dea","src/isa/arm32/inst/mod.rs":"e371c2946e1fbaaccd82cbe6958ce6dbbe2e9f3145a5f074cc9ba986ffe6fcee","src/isa/arm32/inst/regs.rs":"0a67309b3f006e3ceec6408e2cf89ed19d9bd303b0ae12eb3a1ed3756b853719","src/isa/arm32/lower.rs":"55fd734fa5e7cb02a9e2e5e6d5935bf7c66c74f10bcfb455094702c38a10e539","src/isa/arm32/lower_inst.rs":"7bfc74171dfb937013f848d8a274ef27c19993f87bc4b0d15195daca58c40a67","src/isa/arm32/mod.rs":"bf6e39e5b324d733eb2ead675c811740db54a3a275437c152d59ab05106f2d75","src/isa/call_conv.rs":"6b9991b4038529be9362fd5feeef679f4fb93140301b353534b78ab8653a68de","src/isa/constraints.rs":"296f473a95146a743ecb73d8d5908675be02e37607efd287f55322549dc25763","src/isa/enc_tables.rs":"382e714f9500afc292c563cb66d4c963d6787e58f197b1db242db7a099c22b9a","src/isa/encoding.rs":"22e21fdb8e15859c69e4f836bb61e1a82cd6838d093ce5fe641a90f16fb65c9e","src/isa/mod.rs":"47c3d25892691d196dc4dab437fc40c039e1a86a9caed7ddf61aeb2b27453dc8","src/isa/registers.rs":"61840d736b1943c3e54ac324db6f7de4f76170800f047dde267dcc9aa2d53e6a","src/isa/riscv/abi.rs":"aa60b701efcef417ee1262a95398343578dc1a30decc8e11044b74d41654ec51","src/isa/riscv/binemit.rs":"264d223da311d4482ebf2f55438b665c67b163058251bc78173c76ba983a31ef","src/isa/riscv/enc_tables.rs":"8491f2082b24c7dedeb7c36cfd913bf9aeaa0a4c8fc754166e9285f4ae002f40","src/isa/riscv/mod.rs":"03ee02848dbc3325a5ef38e66c05be571c380fbe4ca520b4f87c7572db228beb","src/isa/riscv/registers.rs":"6275ec3ef19195d16556c1856cb23095f25a80c31d6b429eaa749d549515a6d3","src/isa/riscv/settings.rs":"e3c063add822ca366e845f5a19d25b56635e828b157a37d29aca5355650e6a66","src/isa/stack.rs":"c391216fb8ee6566e3b14aa4bd83ba85aef2bd23422a9dca2c8c6f47016372e2","src/isa/test_utils.rs":"b579887191371a008e8b58462ea3cd333ddb109382c93e5f5c577a6d0ba7ffbf","src/isa/unwind.rs":"dab61943d7ca89e099604df2a36dba3b8a661fcf7c10dc13e1257ff8f4890bb3","src/isa/unwind/systemv.rs":"f93c36a366470a54ce2af229eea5ef7c299d28b34093dd656f3c5ad87ea94386","src/isa/unwind/winx64.rs":"2b2cbf0803c7967ecb0159ec2c649b45070f9d79a4f2e425ba51e6017b8167a3","src/isa/x64/abi.rs":"b908428e634af7c7402399a43daadb685ccc508c9ae8b94386fc73d947bff919","src/isa/x64/inst/args.rs":"27d9f3edcaf813d35666cba9abbcd3b8948ddeb79aadcaa31cc53b594952b64a","src/isa/x64/inst/emit.rs":"9ba2d42261306d3bf74c27e84ef728547536187869f8083dd5f8d67455a01d8b","src/isa/x64/inst/emit_tests.rs":"669a04ade8044d78cac48a1de741f5eef6530014283ffd9a2747c878653d12e8","src/isa/x64/inst/mod.rs":"695081ccf09b836b883e71a12dd1aa9d0bcef92559631c4a71b86b5cb18350ec","src/isa/x64/inst/regs.rs":"98fb494b533a6a0e24074cf61f55859d5406459cf239976a3d4fe14af337c456","src/isa/x64/lower.rs":"d37867de4e80c543c86508f977b1f6e01ab4c422acfb373a2d3ef82202f1b1c7","src/isa/x64/mod.rs":"111fbe24a15ef71f3151f1c0c697f152ceeb2933e3c356e83d7a65f8ad3d16aa","src/isa/x64/settings.rs":"8e318036aab3e2c94edfcbf87137325e5423c1c04e14a3fac9e3cc73cebfa6e1","src/isa/x86/abi.rs":"451397a196eff3d1d4ee55b18b72165d97459509d8c3df14f7d4b54cd7199efd","src/isa/x86/binemit.rs":"fb5051471cd9c860455a0c1d74aec7d919d4c7229ada10222631c3694f9a091f","src/isa/x86/enc_tables.rs":"f8e9453eaa7b69b72a373741150db5c951415e7a3ad5c8f5319edadd1aa5cad0","src/isa/x86/mod.rs":"d22ab9590f1c93ac1b60ef8b264c3dc596a0a3d979914b711378b167fece15be","src/isa/x86/registers.rs":"1abbc1aa24c6fc4c7e610b6e840eab29046b821de08cf57fc05e2c2f665479c0","src/isa/x86/settings.rs":"d3e403db3507830f79bcc976c17340b57052cf1b50877fcf1a79549f2a054458","src/isa/x86/unwind.rs":"2fb77f98437c425b3c6438a9e964501568579ca47f74c1a7eba47ab59c7a5046","src/isa/x86/unwind/systemv.rs":"3b6a2534c2c560433646150549194d2af0b290a389760197fd4789e557b07bb5","src/isa/x86/unwind/winx64.rs":"a932f79615536c0cd313cde0f9bdf982815ba9700cc977526657085b24e59b06","src/iterators.rs":"d399a80bc65b97f7d650978e19086dcc1d280ac96d60628eac3d17eb363b0d71","src/legalizer/boundary.rs":"084e1a0c53b0dfed21e6b5e263a012604bffbf4c8e1cac290d0c8ef2eb8d030c","src/legalizer/call.rs":"be6074c64c1a00e5e81159dd94c8401fef62205b22c15e07e0c56cf922554d00","src/legalizer/globalvalue.rs":"a5d09ee41a04ba991d7f5d2d8d8c30a209748d38501a005e0ea568df2663cbb5","src/legalizer/heap.rs":"a6026d44c9ce31e0a21413c50581985dad1584700fde9dbab0b2cefafa5c9d14","src/legalizer/libcall.rs":"4f187c04acb41696bbb80abf0efc4a24b939205619fc6cc40aa8cff86ae1d84b","src/legalizer/mod.rs":"27f6c0d4d5938e096bbcd1777d6c07da50d0b7e9741559662f71c842bc6a850e","src/legalizer/split.rs":"697f08886dbf35fcc69eccc7b597986a58cc73ca7d3cf5d581fffc658a0dad33","src/legalizer/table.rs":"c36d03525312e3191aba8ee00c26a87c1ea200f9a9a0370f0cc84eeacff71786","src/lib.rs":"d9344914821f20e2553bbd9cbcdb6031c491e7a8252cded5fa55ecfab8f58f3a","src/licm.rs":"75e94228c37a7d32cc9b0d51644439f9b1837768df67fd3de08ee43b8cdf8123","src/loop_analysis.rs":"4f23c08df9bc95195d68e9721a3a040c6192276ad33894c1225647034f01b23d","src/machinst/abi.rs":"2470c97947237118ef8d243391b4cd0a9ffefbd0e45b08c667f1e56e884f4563","src/machinst/abi_impl.rs":"b2312225e20f683fb0058779ec6db59b3c52da3cdab58741ee25231f4058b684","src/machinst/adapter.rs":"caf5fcde25b0f41b49d108e3da72f6d9e5dfa97e24d5171698cf9eba1d283399","src/machinst/blockorder.rs":"04387238c1855051a44f8faffb76983514251a031af7d1837224551b8f574b60","src/machinst/buffer.rs":"900f7d41374f1fbe47abe03a4896241ed779524b0ead4af8c8d50e1014de8447","src/machinst/compile.rs":"27c36a0beb0e06d8f4f5c4b013de213d7071037c4f8a374b9615aa07bb42a036","src/machinst/helpers.rs":"05f16d72ef2cc06e7d4319feaee3f60c3f5e789c24b8e92d8af3b17310c33f02","src/machinst/inst_common.rs":"4d1cf90032ec7494d3a06d0bccd6ebd713e09377d7f1a8cfba0aa15452c95e42","src/machinst/lower.rs":"1e34eb52ecbd4ccd24406fbac6cc2d4bc0ed6b73a98295999aa27195fc177c49","src/machinst/mod.rs":"d719f1384ccd8934055d407b971b4bfdbb9729066b8ff35ea632d1c54d83b71d","src/machinst/pretty_print.rs":"6076d9ae0ec48ada9e674ad46c25f48e502adb6b8049c6e1edbcb3b68bd37f87","src/machinst/vcode.rs":"9127475e2883b027266371a06e58fb8d0846a0e33e94942526155b5e68dcaefa","src/nan_canonicalization.rs":"dd853d0b1c1b5274513e9fb24b8beb8db877aca8fdfea5ceccbd1a80309c8842","src/partition_slice.rs":"861e778f887ea5d0841d730963565690fd8c298edddf08848d0d215ae5125eba","src/peepmatic.rs":"78b5fc63ea64356fb4ffc76a3df4663999e36ad1cbfed853609a401072900caf","src/postopt.rs":"ab74e2811909805d467d470da5e66879328c8f47db263422efedf3f1c449d8b2","src/predicates.rs":"d4fa993d8b3036ac9e19d1c1d8880ab5b33030fff0a38d65e2a24b9f9d3956c9","src/preopt.peepmatic":"8b6a6c0f4bf6dcf06df1cc30c467ac39e285ccd85f13f4bc76496f5fd6268a51","src/preopt.serialized":"a7a0968f221417caf91edf9ab9d1a993b8e1aaef009e9f26f477a64b5351de75","src/print_errors.rs":"075b74520f47070b839b43714f55a46a7cc2697b9b0f24a7162d481b7e46b7d2","src/redundant_reload_remover.rs":"2c72cc013f33e1257425592ef4ee2b9437ab3dc84d9759589c15fd217bde83a2","src/regalloc/affinity.rs":"ec5d688c24043a8aa72efcfbfddc14497cd9bab288c9d339e5c0150cdade2b1d","src/regalloc/branch_splitting.rs":"32e34197f84e30cff758f4db611a9c70dd587dd8d094729c34aa00303538c0d0","src/regalloc/coalescing.rs":"154842e7f380f2626c698283dbc5e0d5e7f0cc5d22b938e90312d17b71a8bb72","src/regalloc/coloring.rs":"ded1d8e531c38412fb19fe746fed65a6b6598819a29cd76c9b4bd5c4d0d6011a","src/regalloc/context.rs":"7f041d9202d3d1f040f4658acfd0f2b37b5934854b2d5cfa83864d5d1a424076","src/regalloc/diversion.rs":"2e474940b0c38610ca231faba7c7c3cfadf401a2f24247b6f3730ac862fce21f","src/regalloc/live_value_tracker.rs":"845dc3f43cc6b795fea51bf293e7c6ab4961d59ab6ca2670fcab7a2a9bd996be","src/regalloc/liveness.rs":"0b027b8e4444a715af1b93d594a293d2fd430ad06940da05b06a4750535e9652","src/regalloc/liverange.rs":"2e98802e90868051b53ddc8555db0ea98aabc77df7789df2a92627650a43227e","src/regalloc/mod.rs":"a1a460c0bf747c7762655f1ea6f12749b839eb2a72a85144c91cf92fa2bc9476","src/regalloc/pressure.rs":"4565f701d87dff65694b37743fe72d6282dab133f467c448b2a73ef6f37ee9d3","src/regalloc/register_set.rs":"c740d10a061c4b8527ce319842b519d743e93e64db53851360f9ca2c099fd652","src/regalloc/reload.rs":"2132bd4cf45ce60b7799277d36bda35c05064ee1c60798388b8f55a0668fca47","src/regalloc/safepoint.rs":"e398457875d4079bb78ce7b34e2a2f16f3c70680e252fbafbfac17c1d42e17c7","src/regalloc/solver.rs":"e2da4779aba96f77af97c385196b2c498ab25e4b47fd6249a9e3ce813517ce91","src/regalloc/spilling.rs":"3b75be8be6568a091dd8a1fd174b099859c6e9969c03bd765b5fb50f52fcccb5","src/regalloc/virtregs.rs":"a01b5d3cb1753e344c6663dd73de00dd452d442990f89477610b22c86c9afdfd","src/remove_constant_phis.rs":"0e6c2cdce9229bf9a9275d4895d772b90370ed6b7dfb690e63c245c2a9f6c94d","src/result.rs":"7164a1b35f26aeb9a6eda79f773f64ecb97b80b50f5b01ea5d34b64361160cbd","src/scoped_hash_map.rs":"c8d0071ce7e19438e9995b5fca1ea0fca81234811943b06b344331f9742c3718","src/settings.rs":"58a7fe17dfcb6f9e1831fec5044967c8a9bf58ed2c57dcbdc8533764443c3beb","src/simple_gvn.rs":"1de1d0c0e028833350eda7186243f255c9db97fe04f0e6fa688b8a710caa78be","src/simple_preopt.rs":"cac21be7637415f54be27af6135c1cc777352146b47bf25ac8e0b30cf5ab4d44","src/souper_harvest.rs":"dd89016122959baf74575d6d5e9d5e331df7c8c94a76e46d5bfb872d63ab61ca","src/stack_layout.rs":"41d35401faa171d9823e9c6e26c1337f9e16c6b8ba613f0cd98c3c0032930496","src/timing.rs":"bbff7ca6f6ab8ce2f5d1ee0ce5785d19c0b03b6bf7bf65f8c9a2de7883f88506","src/topo_order.rs":"c092ee7a44e5f14962501eafd4478dfb855ce66af15d9c94a9b244ea30d6e991","src/unreachable_code.rs":"baea08a55b1e7eb2379fa2c4bb5ed4f5a536a35eafcb377f8ab79dc41d14d3d4","src/value_label.rs":"e464557e5bab579773929fcfca843e553af201174da1a73460d199446abc7fc7","src/verifier/cssa.rs":"2590b0ecbc134bbedac50915ed9c9e054c89f81e455c7bc0f37d4ddf57a38d05","src/verifier/flags.rs":"233a4c6fb42e32d92bcbef4ec094a26aa79bdd25cb478847236b6ce5d88d3d54","src/verifier/liveness.rs":"b6ab6dfb1390cea8091b71a6f2fd629ee356987b6a0714e8773d7b0eb7fa889f","src/verifier/locations.rs":"2b4e62e1bb79551725414b5a77425c00e9ad56ad766d6293db1eb261b64f51f9","src/verifier/mod.rs":"90c0b62c907414fe04b3ea35385742409a6ce3f30165355ec919a9c8552fca19","src/write.rs":"421cf2479f2875468476b593b06a236333e4113690ddcc57720d0d86377050cb"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"e7e6963b0f0ca752e72830bc895a2b5556c3c44b4144ac9670047eec448a931f","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"e5127227a7db4a8aa92fa6613ed71801025790e696bb41b0323fb7f3c6f7495a","build.rs":"afe67e345d21f9a137ecad473bb595a8b4983d5d435ca0505c91021954c215a7","src/abi.rs":"8922d75a807798945c64ee135f7f22c58e97d8412c6d5b6bf6c4e75c6823d308","src/binemit/memorysink.rs":"6eb4af661c11461a85612729553eec8581984dd7ce5a3cb905dc3667a49ab954","src/binemit/mod.rs":"4fbd7566c56a02a9faa18a02a7f3ba97297a42bba62c8c60a291a160760abfad","src/binemit/relaxation.rs":"0547f1a33ea176b281d6f09d1bd7a253dcba895785008e61c114861f1a86307f","src/binemit/shrink.rs":"552d64dff3b044bca77f39891d0e39ee619f6ec0669bf9917132c97becea79b0","src/binemit/stack_map.rs":"cae6ef55b748a3700909d26f408c3d244a7dd76c0918b9b04c17127c18dbf8bf","src/bitset.rs":"ff667c19a63a6a9bcf06f99a46b6db839323f5d3d5cc5e7b00f1ab77d331fc77","src/cfg_printer.rs":"a4cd85ecb2c403b29dc3fc6c60ee7f97b6ed602e8ba88ec87accb988d009271e","src/constant_hash.rs":"8f53afb38891fbdab8553c7a89e61f0ce30947a22fb24ae84523c9a1ab025c3f","src/context.rs":"e3e1644565c1a6b6dd21daae36f9b22e9f69851fbb3c86e33b1756bb9a2e020e","src/cursor.rs":"eaa0e4ea65bec30aa79e2fb62e89c713b1adec6eeddd5592237d7fcce47c5fa7","src/dbg.rs":"1898d94cff0975815eb348651702e95c8f2f63886501d3b7043ee75668480472","src/dce.rs":"b98545dbf5c8d0c4a33b2ec6cd905d6371fe843481ad608ff59b4a009fc9be19","src/divconst_magic_numbers.rs":"e7f8f46f3a25ed7155890e9d76c10f5dde9dc5bbcebb623c8166fa415abdd959","src/dominator_tree.rs":"b3a5c7837150390a78ade90807965dfcb8768a7e3ae9ee02f2e4a1ad8f3bafa9","src/flowgraph.rs":"71490e2f7a1ea905758a258b2bebe77f125302f446f9839dd60003fdafaef5fe","src/fx.rs":"8a5d07487906d8316a179e826fcd817a92a4860686256a6fd9d78ba47c63f330","src/inst_predicates.rs":"ba71bba10250fada4f04bcd6690583b077061bab988c4d6609c41e8af7de5e98","src/ir/atomic_rmw_op.rs":"44311956feebef2ec01a0156e690a0d09f66b616e401d54a5fc5c9769c448be5","src/ir/builder.rs":"3425957b118a0c40847ef7f473543baef5a24f0c7d8af198955180c8f297ee5a","src/ir/constant.rs":"9c42014b7182a89bc2387e13d3956bbbd98025f32a3abeb5d253d2ba56ad279c","src/ir/dfg.rs":"e803858c6cef60cec09a82d686a73715d0eb142ac96742023cd6112dcf87073a","src/ir/entities.rs":"f0b49b9c351b94703d6c878b564e53a30010c142c47effaf603ec4ade784686b","src/ir/extfunc.rs":"d901ee549f2cf980d807481c3dd6c2b89db241044d465492146bf44db05e9f86","src/ir/extname.rs":"977b2e380094e99cfdd080112bda709ca61a6d942e1e4c2015d39eaf3ea80f4c","src/ir/function.rs":"3034e03ac2e6bcf300b91c9df03e144550ff6c91ef76b105c48ccbc7a752401b","src/ir/globalvalue.rs":"2f3a54cc2ade91439536b02c46ce790c3634a386a4cc4d27d7da7ad929b8bb90","src/ir/heap.rs":"a59d3e5901412b53c0b53a8cdf10765ff5921de9c410ae9acea226c89827df3c","src/ir/immediates.rs":"2f5366e9cd9f7731fdf4d9038bdd3c9976ea6dce38626ede289b37580407da16","src/ir/instructions.rs":"a25709234ce737a776060224cc10f61e0fc9b9ce51a020af7d1c5acb127658e4","src/ir/jumptable.rs":"184fa640957e8b0e451dc594e4b348f9aff5fb5b54ab3d856ab19df25f4f3322","src/ir/layout.rs":"2956643a822e67a0c1196f8d3c81af11d0c0122b2d97427ce3ed0c57bb633cbf","src/ir/libcall.rs":"7c9255bdef9a16937d817a3bfd542be4c2734eea3c8b8a870ff411eac2efaad8","src/ir/memflags.rs":"dbcf3798ab66dc764b73fb7f139a621c54cc6bcc683f1f70a33ed7e8c3486bfd","src/ir/mod.rs":"14cf95dae76a6192c755ed74837c3a36d92827ccdbe2e73f805abea29fc723d8","src/ir/progpoint.rs":"a985a7fccde1e985ca24bf2344f16faa4cda7cffb30cb56bf27fabbb5727bc62","src/ir/sourceloc.rs":"67c66adace734645ddf311ecb22e975f20756e07c91e10f698a830cfc2cd08aa","src/ir/stackslot.rs":"433b61e05263f40c022f73b0ab1141e3e45b348a0309ca02546e6450b2957792","src/ir/table.rs":"dcc3b663a989b2b084402b08dc9a0e928dbd052e194a46a1886cc6f0cf1a5f2c","src/ir/trapcode.rs":"7948d0c387f75695c5d1e03c2ea6808954f0e658ba3cbd636fbbbbbd9a623561","src/ir/types.rs":"dfd9470aa4325adc252eb7ebb2951a5b98d22d0dc31be482419f5cbbb74bf86d","src/ir/valueloc.rs":"628f292ee6b8375e07f360edaf3609b522fd4d7bea131fb8ad3cab237927e7ab","src/isa/aarch64/abi.rs":"88684c74cae823f43f696f44d92325b7ef7b1dd7dbaf98e41ad95ec5b2daeffa","src/isa/aarch64/inst/args.rs":"531232293e355312fb9aff23aa0be1b1168f5456345d3f1958ca7eccd58ac3d8","src/isa/aarch64/inst/emit.rs":"2d736168fad643aea7801c6c7e2593ac7519ec999cd3dcfc76d5f1a4f1b1915b","src/isa/aarch64/inst/emit_tests.rs":"43eb00924a9523d8d2a1be36adfab8b2e7c5c47b2445e819251bb785a40662fd","src/isa/aarch64/inst/imms.rs":"c99980c31bacf5454f9e299e9e351fdddd71b0ec5106c282b8504ee7462777f5","src/isa/aarch64/inst/mod.rs":"4b60b097644401f7095285be6a1f353d3696ce3dc767d5b59d042678adde58a5","src/isa/aarch64/inst/regs.rs":"2e3f52d48b5963a85a275823ee1fd8bc5debf8ee310c4e8cddc77d04f4da45ea","src/isa/aarch64/lower.rs":"c484e991eb583fc6ca21f394bac3374c3d0241911b71c017ab51edae79f3c66f","src/isa/aarch64/lower_inst.rs":"397d5668f5a1eeae41b47f828654366252a4058ace15b2d334b515bf293c89e1","src/isa/aarch64/mod.rs":"8343d3d9f5273ba26146ac3278429b6b0855f1dcbac0de2af62b921a1f46f137","src/isa/arm32/abi.rs":"9be6d9485af2f3e33424ef0edb8fbf641a86787c9ba5c49b09c2d6b6d375c935","src/isa/arm32/inst/args.rs":"3c00b0876c4d57130c2c1ccb56940886f49b09776ab37acbfc1817661e6e9651","src/isa/arm32/inst/emit.rs":"962a16039b9a1540a2e591015f289f59821594576639b20ff0d691cf78630c67","src/isa/arm32/inst/emit_tests.rs":"17dd44aece41b54979de00c6f1e0647af6cdd4254abb8e629797d5611a1a9dea","src/isa/arm32/inst/mod.rs":"e371c2946e1fbaaccd82cbe6958ce6dbbe2e9f3145a5f074cc9ba986ffe6fcee","src/isa/arm32/inst/regs.rs":"0a67309b3f006e3ceec6408e2cf89ed19d9bd303b0ae12eb3a1ed3756b853719","src/isa/arm32/lower.rs":"55fd734fa5e7cb02a9e2e5e6d5935bf7c66c74f10bcfb455094702c38a10e539","src/isa/arm32/lower_inst.rs":"7bfc74171dfb937013f848d8a274ef27c19993f87bc4b0d15195daca58c40a67","src/isa/arm32/mod.rs":"bf6e39e5b324d733eb2ead675c811740db54a3a275437c152d59ab05106f2d75","src/isa/call_conv.rs":"6b9991b4038529be9362fd5feeef679f4fb93140301b353534b78ab8653a68de","src/isa/constraints.rs":"296f473a95146a743ecb73d8d5908675be02e37607efd287f55322549dc25763","src/isa/enc_tables.rs":"382e714f9500afc292c563cb66d4c963d6787e58f197b1db242db7a099c22b9a","src/isa/encoding.rs":"22e21fdb8e15859c69e4f836bb61e1a82cd6838d093ce5fe641a90f16fb65c9e","src/isa/mod.rs":"47c3d25892691d196dc4dab437fc40c039e1a86a9caed7ddf61aeb2b27453dc8","src/isa/registers.rs":"61840d736b1943c3e54ac324db6f7de4f76170800f047dde267dcc9aa2d53e6a","src/isa/riscv/abi.rs":"aa60b701efcef417ee1262a95398343578dc1a30decc8e11044b74d41654ec51","src/isa/riscv/binemit.rs":"264d223da311d4482ebf2f55438b665c67b163058251bc78173c76ba983a31ef","src/isa/riscv/enc_tables.rs":"8491f2082b24c7dedeb7c36cfd913bf9aeaa0a4c8fc754166e9285f4ae002f40","src/isa/riscv/mod.rs":"03ee02848dbc3325a5ef38e66c05be571c380fbe4ca520b4f87c7572db228beb","src/isa/riscv/registers.rs":"6275ec3ef19195d16556c1856cb23095f25a80c31d6b429eaa749d549515a6d3","src/isa/riscv/settings.rs":"e3c063add822ca366e845f5a19d25b56635e828b157a37d29aca5355650e6a66","src/isa/stack.rs":"c391216fb8ee6566e3b14aa4bd83ba85aef2bd23422a9dca2c8c6f47016372e2","src/isa/test_utils.rs":"b579887191371a008e8b58462ea3cd333ddb109382c93e5f5c577a6d0ba7ffbf","src/isa/unwind.rs":"dab61943d7ca89e099604df2a36dba3b8a661fcf7c10dc13e1257ff8f4890bb3","src/isa/unwind/systemv.rs":"f93c36a366470a54ce2af229eea5ef7c299d28b34093dd656f3c5ad87ea94386","src/isa/unwind/winx64.rs":"2b2cbf0803c7967ecb0159ec2c649b45070f9d79a4f2e425ba51e6017b8167a3","src/isa/x64/abi.rs":"b908428e634af7c7402399a43daadb685ccc508c9ae8b94386fc73d947bff919","src/isa/x64/inst/args.rs":"27d9f3edcaf813d35666cba9abbcd3b8948ddeb79aadcaa31cc53b594952b64a","src/isa/x64/inst/emit.rs":"051e545a0e1c593747afe65510fa9be0036f7664051497617ce86df358496d11","src/isa/x64/inst/emit_tests.rs":"669a04ade8044d78cac48a1de741f5eef6530014283ffd9a2747c878653d12e8","src/isa/x64/inst/mod.rs":"c1c5e5307f9030588832ff63c53e478812b4d6a7dc15cec425304a15d21e243f","src/isa/x64/inst/regs.rs":"98fb494b533a6a0e24074cf61f55859d5406459cf239976a3d4fe14af337c456","src/isa/x64/lower.rs":"fbcd4986fe1d3b0e1937fb8a9407f6ae6b9d3a75d8d90925f1e5eb4524f47e67","src/isa/x64/mod.rs":"111fbe24a15ef71f3151f1c0c697f152ceeb2933e3c356e83d7a65f8ad3d16aa","src/isa/x64/settings.rs":"8e318036aab3e2c94edfcbf87137325e5423c1c04e14a3fac9e3cc73cebfa6e1","src/isa/x86/abi.rs":"451397a196eff3d1d4ee55b18b72165d97459509d8c3df14f7d4b54cd7199efd","src/isa/x86/binemit.rs":"fb5051471cd9c860455a0c1d74aec7d919d4c7229ada10222631c3694f9a091f","src/isa/x86/enc_tables.rs":"f8e9453eaa7b69b72a373741150db5c951415e7a3ad5c8f5319edadd1aa5cad0","src/isa/x86/mod.rs":"d22ab9590f1c93ac1b60ef8b264c3dc596a0a3d979914b711378b167fece15be","src/isa/x86/registers.rs":"1abbc1aa24c6fc4c7e610b6e840eab29046b821de08cf57fc05e2c2f665479c0","src/isa/x86/settings.rs":"d3e403db3507830f79bcc976c17340b57052cf1b50877fcf1a79549f2a054458","src/isa/x86/unwind.rs":"2fb77f98437c425b3c6438a9e964501568579ca47f74c1a7eba47ab59c7a5046","src/isa/x86/unwind/systemv.rs":"3b6a2534c2c560433646150549194d2af0b290a389760197fd4789e557b07bb5","src/isa/x86/unwind/winx64.rs":"a932f79615536c0cd313cde0f9bdf982815ba9700cc977526657085b24e59b06","src/iterators.rs":"d399a80bc65b97f7d650978e19086dcc1d280ac96d60628eac3d17eb363b0d71","src/legalizer/boundary.rs":"084e1a0c53b0dfed21e6b5e263a012604bffbf4c8e1cac290d0c8ef2eb8d030c","src/legalizer/call.rs":"be6074c64c1a00e5e81159dd94c8401fef62205b22c15e07e0c56cf922554d00","src/legalizer/globalvalue.rs":"a5d09ee41a04ba991d7f5d2d8d8c30a209748d38501a005e0ea568df2663cbb5","src/legalizer/heap.rs":"a6026d44c9ce31e0a21413c50581985dad1584700fde9dbab0b2cefafa5c9d14","src/legalizer/libcall.rs":"4f187c04acb41696bbb80abf0efc4a24b939205619fc6cc40aa8cff86ae1d84b","src/legalizer/mod.rs":"27f6c0d4d5938e096bbcd1777d6c07da50d0b7e9741559662f71c842bc6a850e","src/legalizer/split.rs":"697f08886dbf35fcc69eccc7b597986a58cc73ca7d3cf5d581fffc658a0dad33","src/legalizer/table.rs":"c36d03525312e3191aba8ee00c26a87c1ea200f9a9a0370f0cc84eeacff71786","src/lib.rs":"d9344914821f20e2553bbd9cbcdb6031c491e7a8252cded5fa55ecfab8f58f3a","src/licm.rs":"75e94228c37a7d32cc9b0d51644439f9b1837768df67fd3de08ee43b8cdf8123","src/loop_analysis.rs":"4f23c08df9bc95195d68e9721a3a040c6192276ad33894c1225647034f01b23d","src/machinst/abi.rs":"2470c97947237118ef8d243391b4cd0a9ffefbd0e45b08c667f1e56e884f4563","src/machinst/abi_impl.rs":"b2312225e20f683fb0058779ec6db59b3c52da3cdab58741ee25231f4058b684","src/machinst/adapter.rs":"caf5fcde25b0f41b49d108e3da72f6d9e5dfa97e24d5171698cf9eba1d283399","src/machinst/blockorder.rs":"04387238c1855051a44f8faffb76983514251a031af7d1837224551b8f574b60","src/machinst/buffer.rs":"900f7d41374f1fbe47abe03a4896241ed779524b0ead4af8c8d50e1014de8447","src/machinst/compile.rs":"27c36a0beb0e06d8f4f5c4b013de213d7071037c4f8a374b9615aa07bb42a036","src/machinst/helpers.rs":"05f16d72ef2cc06e7d4319feaee3f60c3f5e789c24b8e92d8af3b17310c33f02","src/machinst/inst_common.rs":"4d1cf90032ec7494d3a06d0bccd6ebd713e09377d7f1a8cfba0aa15452c95e42","src/machinst/lower.rs":"1e34eb52ecbd4ccd24406fbac6cc2d4bc0ed6b73a98295999aa27195fc177c49","src/machinst/mod.rs":"d719f1384ccd8934055d407b971b4bfdbb9729066b8ff35ea632d1c54d83b71d","src/machinst/pretty_print.rs":"6076d9ae0ec48ada9e674ad46c25f48e502adb6b8049c6e1edbcb3b68bd37f87","src/machinst/vcode.rs":"9127475e2883b027266371a06e58fb8d0846a0e33e94942526155b5e68dcaefa","src/nan_canonicalization.rs":"dd853d0b1c1b5274513e9fb24b8beb8db877aca8fdfea5ceccbd1a80309c8842","src/partition_slice.rs":"861e778f887ea5d0841d730963565690fd8c298edddf08848d0d215ae5125eba","src/peepmatic.rs":"78b5fc63ea64356fb4ffc76a3df4663999e36ad1cbfed853609a401072900caf","src/postopt.rs":"ab74e2811909805d467d470da5e66879328c8f47db263422efedf3f1c449d8b2","src/predicates.rs":"d4fa993d8b3036ac9e19d1c1d8880ab5b33030fff0a38d65e2a24b9f9d3956c9","src/preopt.peepmatic":"8b6a6c0f4bf6dcf06df1cc30c467ac39e285ccd85f13f4bc76496f5fd6268a51","src/preopt.serialized":"a7a0968f221417caf91edf9ab9d1a993b8e1aaef009e9f26f477a64b5351de75","src/print_errors.rs":"075b74520f47070b839b43714f55a46a7cc2697b9b0f24a7162d481b7e46b7d2","src/redundant_reload_remover.rs":"2c72cc013f33e1257425592ef4ee2b9437ab3dc84d9759589c15fd217bde83a2","src/regalloc/affinity.rs":"ec5d688c24043a8aa72efcfbfddc14497cd9bab288c9d339e5c0150cdade2b1d","src/regalloc/branch_splitting.rs":"32e34197f84e30cff758f4db611a9c70dd587dd8d094729c34aa00303538c0d0","src/regalloc/coalescing.rs":"154842e7f380f2626c698283dbc5e0d5e7f0cc5d22b938e90312d17b71a8bb72","src/regalloc/coloring.rs":"ded1d8e531c38412fb19fe746fed65a6b6598819a29cd76c9b4bd5c4d0d6011a","src/regalloc/context.rs":"7f041d9202d3d1f040f4658acfd0f2b37b5934854b2d5cfa83864d5d1a424076","src/regalloc/diversion.rs":"2e474940b0c38610ca231faba7c7c3cfadf401a2f24247b6f3730ac862fce21f","src/regalloc/live_value_tracker.rs":"845dc3f43cc6b795fea51bf293e7c6ab4961d59ab6ca2670fcab7a2a9bd996be","src/regalloc/liveness.rs":"0b027b8e4444a715af1b93d594a293d2fd430ad06940da05b06a4750535e9652","src/regalloc/liverange.rs":"2e98802e90868051b53ddc8555db0ea98aabc77df7789df2a92627650a43227e","src/regalloc/mod.rs":"a1a460c0bf747c7762655f1ea6f12749b839eb2a72a85144c91cf92fa2bc9476","src/regalloc/pressure.rs":"4565f701d87dff65694b37743fe72d6282dab133f467c448b2a73ef6f37ee9d3","src/regalloc/register_set.rs":"c740d10a061c4b8527ce319842b519d743e93e64db53851360f9ca2c099fd652","src/regalloc/reload.rs":"2132bd4cf45ce60b7799277d36bda35c05064ee1c60798388b8f55a0668fca47","src/regalloc/safepoint.rs":"e398457875d4079bb78ce7b34e2a2f16f3c70680e252fbafbfac17c1d42e17c7","src/regalloc/solver.rs":"e2da4779aba96f77af97c385196b2c498ab25e4b47fd6249a9e3ce813517ce91","src/regalloc/spilling.rs":"3b75be8be6568a091dd8a1fd174b099859c6e9969c03bd765b5fb50f52fcccb5","src/regalloc/virtregs.rs":"a01b5d3cb1753e344c6663dd73de00dd452d442990f89477610b22c86c9afdfd","src/remove_constant_phis.rs":"0e6c2cdce9229bf9a9275d4895d772b90370ed6b7dfb690e63c245c2a9f6c94d","src/result.rs":"7164a1b35f26aeb9a6eda79f773f64ecb97b80b50f5b01ea5d34b64361160cbd","src/scoped_hash_map.rs":"c8d0071ce7e19438e9995b5fca1ea0fca81234811943b06b344331f9742c3718","src/settings.rs":"58a7fe17dfcb6f9e1831fec5044967c8a9bf58ed2c57dcbdc8533764443c3beb","src/simple_gvn.rs":"1de1d0c0e028833350eda7186243f255c9db97fe04f0e6fa688b8a710caa78be","src/simple_preopt.rs":"cac21be7637415f54be27af6135c1cc777352146b47bf25ac8e0b30cf5ab4d44","src/souper_harvest.rs":"dd89016122959baf74575d6d5e9d5e331df7c8c94a76e46d5bfb872d63ab61ca","src/stack_layout.rs":"41d35401faa171d9823e9c6e26c1337f9e16c6b8ba613f0cd98c3c0032930496","src/timing.rs":"bbff7ca6f6ab8ce2f5d1ee0ce5785d19c0b03b6bf7bf65f8c9a2de7883f88506","src/topo_order.rs":"c092ee7a44e5f14962501eafd4478dfb855ce66af15d9c94a9b244ea30d6e991","src/unreachable_code.rs":"baea08a55b1e7eb2379fa2c4bb5ed4f5a536a35eafcb377f8ab79dc41d14d3d4","src/value_label.rs":"e464557e5bab579773929fcfca843e553af201174da1a73460d199446abc7fc7","src/verifier/cssa.rs":"2590b0ecbc134bbedac50915ed9c9e054c89f81e455c7bc0f37d4ddf57a38d05","src/verifier/flags.rs":"233a4c6fb42e32d92bcbef4ec094a26aa79bdd25cb478847236b6ce5d88d3d54","src/verifier/liveness.rs":"b6ab6dfb1390cea8091b71a6f2fd629ee356987b6a0714e8773d7b0eb7fa889f","src/verifier/locations.rs":"2b4e62e1bb79551725414b5a77425c00e9ad56ad766d6293db1eb261b64f51f9","src/verifier/mod.rs":"90c0b62c907414fe04b3ea35385742409a6ce3f30165355ec919a9c8552fca19","src/write.rs":"421cf2479f2875468476b593b06a236333e4113690ddcc57720d0d86377050cb"},"package":null} \ No newline at end of file diff --git a/third_party/rust/cranelift-codegen/Cargo.toml b/third_party/rust/cranelift-codegen/Cargo.toml index 27493b3443ed6..3ac7f2717150f 100644 --- a/third_party/rust/cranelift-codegen/Cargo.toml +++ b/third_party/rust/cranelift-codegen/Cargo.toml @@ -30,7 +30,7 @@ peepmatic-traits = { path = "../peepmatic/crates/traits", optional = true, versi peepmatic-runtime = { path = "../peepmatic/crates/runtime", optional = true, version = "0.67.0" } regalloc = "0.0.30" souper-ir = { version = "1", optional = true } -wast = { version = "22.0.0", optional = true } +wast = { version = "25.0.0", optional = true } # It is a goal of the cranelift-codegen crate to have minimal external dependencies. # Please don't add any unless they are essential to the task of creating binary # machine code. Integration tests that need external dependencies can be diff --git a/third_party/rust/cranelift-codegen/src/isa/x64/inst/emit.rs b/third_party/rust/cranelift-codegen/src/isa/x64/inst/emit.rs index 363299c97547a..edddb7e6b314b 100644 --- a/third_party/rust/cranelift-codegen/src/isa/x64/inst/emit.rs +++ b/third_party/rust/cranelift-codegen/src/isa/x64/inst/emit.rs @@ -476,7 +476,7 @@ pub(crate) fn emit( state: &mut EmitState, ) { match inst { - Inst::Alu_RMI_R { + Inst::AluRmiR { is_64, op, src, @@ -517,8 +517,8 @@ pub(crate) fn emit( } RegMemImm::Imm { simm32 } => { - let useImm8 = low8_will_sign_extend_to_32(*simm32); - let opcode = if useImm8 { 0x6B } else { 0x69 }; + let use_imm8 = low8_will_sign_extend_to_32(*simm32); + let opcode = if use_imm8 { 0x6B } else { 0x69 }; // Yes, really, reg_g twice. emit_std_reg_reg( sink, @@ -529,7 +529,7 @@ pub(crate) fn emit( reg_g.to_reg(), rex, ); - emit_simm(sink, if useImm8 { 1 } else { 4 }, *simm32); + emit_simm(sink, if use_imm8 { 1 } else { 4 }, *simm32); } } } else { @@ -891,7 +891,7 @@ pub(crate) fn emit( } } - Inst::Mov_R_R { is_64, src, dst } => { + Inst::MovRR { is_64, src, dst } => { let rex = if *is_64 { RexFlags::set_w() } else { @@ -900,7 +900,7 @@ pub(crate) fn emit( emit_std_reg_reg(sink, LegacyPrefixes::None, 0x89, 1, *src, dst.to_reg(), rex); } - Inst::MovZX_RM_R { + Inst::MovzxRmR { ext_mode, src, dst, @@ -981,7 +981,7 @@ pub(crate) fn emit( } } - Inst::Mov64_M_R { src, dst, srcloc } => { + Inst::Mov64MR { src, dst, srcloc } => { let src = &src.finalize(state); if let Some(srcloc) = *srcloc { @@ -1010,7 +1010,7 @@ pub(crate) fn emit( RexFlags::set_w(), ), - Inst::MovSX_RM_R { + Inst::MovsxRmR { ext_mode, src, dst, @@ -1083,7 +1083,7 @@ pub(crate) fn emit( } } - Inst::Mov_R_M { + Inst::MovRM { size, src, dst, @@ -1155,7 +1155,7 @@ pub(crate) fn emit( } } - Inst::Shift_R { + Inst::ShiftR { size, kind, num_bits, @@ -1255,7 +1255,7 @@ pub(crate) fn emit( }; } - Inst::Cmp_RMI_R { + Inst::CmpRmiR { size, src: src_e, dst: reg_g, @@ -1424,13 +1424,12 @@ pub(crate) fn emit( } Inst::Pop64 { dst } => { - let encDst = int_reg_enc(dst.to_reg()); - if encDst >= 8 { - // 0x41 == REX.{W=0, B=1}. It seems that REX.W is irrelevant - // here. + let enc_dst = int_reg_enc(dst.to_reg()); + if enc_dst >= 8 { + // 0x41 == REX.{W=0, B=1}. It seems that REX.W is irrelevant here. sink.put1(0x41); } - sink.put1(0x58 + (encDst & 7)); + sink.put1(0x58 + (enc_dst & 7)); } Inst::CallKnown { @@ -1741,7 +1740,7 @@ pub(crate) fn emit( }; } - Inst::XMM_RM_R { + Inst::XmmRmR { op, src: src_e, dst: reg_g, @@ -2008,7 +2007,7 @@ pub(crate) fn emit( // emitted. } - Inst::Xmm_Mov_R_M { + Inst::XmmMovRM { op, src, dst, @@ -2090,7 +2089,7 @@ pub(crate) fn emit( } } - Inst::XMM_Cmp_RM_R { op, src, dst } => { + Inst::XmmCmpRmR { op, src, dst } => { let rex = RexFlags::clear_w(); let (prefix, opcode, len) = match op { SseOpcode::Ptest => (LegacyPrefixes::_66, 0x0F3817, 3), diff --git a/third_party/rust/cranelift-codegen/src/isa/x64/inst/mod.rs b/third_party/rust/cranelift-codegen/src/isa/x64/inst/mod.rs index f15521d35c6a1..48180223989c8 100644 --- a/third_party/rust/cranelift-codegen/src/isa/x64/inst/mod.rs +++ b/third_party/rust/cranelift-codegen/src/isa/x64/inst/mod.rs @@ -1,7 +1,5 @@ //! This module defines x86_64-specific machine instruction types. #![allow(dead_code)] -#![allow(non_snake_case)] -#![allow(non_camel_case_types)] use crate::binemit::{CodeOffset, StackMap}; use crate::ir::{types, ExternalName, Opcode, SourceLoc, TrapCode, Type}; @@ -34,13 +32,13 @@ use regs::{create_reg_universe_systemv, show_ireg_sized}; /// Instructions. Destinations are on the RIGHT (a la AT&T syntax). #[derive(Clone)] pub enum Inst { - /// nops of various sizes, including zero + /// Nops of various sizes, including zero. Nop { len: u8 }, // ===================================== // Integer instructions. /// Integer arithmetic/bit-twiddling: (add sub and or xor mul adc? sbb?) (32 64) (reg addr imm) reg - Alu_RMI_R { + AluRmiR { is_64: bool, op: AluRmiROpcode, src: RegMemImm, @@ -114,7 +112,7 @@ pub enum Inst { }, /// GPR to GPR move: mov (64 32) reg reg. - Mov_R_R { + MovRR { is_64: bool, src: Reg, dst: Writable, @@ -123,7 +121,7 @@ pub enum Inst { /// Zero-extended loads, except for 64 bits: movz (bl bq wl wq lq) addr reg. /// Note that the lq variant doesn't really exist since the default zero-extend rule makes it /// unnecessary. For that case we emit the equivalent "movl AM, reg32". - MovZX_RM_R { + MovzxRmR { ext_mode: ExtMode, src: RegMem, dst: Writable, @@ -132,7 +130,7 @@ pub enum Inst { }, /// A plain 64-bit integer load, since MovZX_RM_R can't represent that. - Mov64_M_R { + Mov64MR { src: SyntheticAmode, dst: Writable, /// Source location, if the memory access can be out-of-bounds. @@ -146,7 +144,7 @@ pub enum Inst { }, /// Sign-extended loads and moves: movs (bl bq wl wq lq) addr reg. - MovSX_RM_R { + MovsxRmR { ext_mode: ExtMode, src: RegMem, dst: Writable, @@ -155,7 +153,7 @@ pub enum Inst { }, /// Integer stores: mov (b w l q) reg addr. - Mov_R_M { + MovRM { size: u8, // 1, 2, 4 or 8. src: Reg, dst: SyntheticAmode, @@ -164,7 +162,7 @@ pub enum Inst { }, /// Arithmetic shifts: (shl shr sar) (b w l q) imm reg. - Shift_R { + ShiftR { size: u8, // 1, 2, 4 or 8 kind: ShiftKind, /// shift count: Some(0 .. #bits-in-type - 1), or None to mean "%cl". @@ -180,7 +178,7 @@ pub enum Inst { }, /// Integer comparisons/tests: cmp (b w l q) (reg addr imm) reg. - Cmp_RMI_R { + CmpRmiR { size: u8, // 1, 2, 4 or 8 src: RegMemImm, dst: Reg, @@ -210,7 +208,7 @@ pub enum Inst { // ===================================== // Floating-point operations. /// XMM (scalar or vector) binary op: (add sub and or xor mul adc? sbb?) (32 64) (reg addr) reg - XMM_RM_R { + XmmRmR { op: SseOpcode, src: RegMem, dst: Writable, @@ -231,7 +229,7 @@ pub enum Inst { }, /// XMM (scalar or vector) unary op (from xmm to reg/mem): stores, movd, movq - Xmm_Mov_R_M { + XmmMovRM { op: SseOpcode, src: Reg, dst: SyntheticAmode, @@ -327,7 +325,7 @@ pub enum Inst { }, /// Float comparisons/tests: cmp (b w l q) (reg addr imm) reg. - XMM_Cmp_RM_R { + XmmCmpRmR { op: SseOpcode, src: RegMem, dst: Reg, @@ -519,7 +517,7 @@ impl Inst { ) -> Self { src.assert_regclass_is(RegClass::I64); debug_assert!(dst.to_reg().get_class() == RegClass::I64); - Self::Alu_RMI_R { + Self::AluRmiR { is_64, op, src, @@ -609,7 +607,7 @@ impl Inst { pub(crate) fn mov_r_r(is_64: bool, src: Reg, dst: Writable) -> Inst { debug_assert!(src.get_class() == RegClass::I64); debug_assert!(dst.to_reg().get_class() == RegClass::I64); - Inst::Mov_R_R { is_64, src, dst } + Inst::MovRR { is_64, src, dst } } // TODO Can be replaced by `Inst::move` (high-level) and `Inst::unary_rm_r` (low-level) @@ -651,7 +649,7 @@ impl Inst { pub(crate) fn xmm_rm_r(op: SseOpcode, src: RegMem, dst: Writable) -> Self { src.assert_regclass_is(RegClass::V128); debug_assert!(dst.to_reg().get_class() == RegClass::V128); - Inst::XMM_RM_R { op, src, dst } + Inst::XmmRmR { op, src, dst } } pub(crate) fn xmm_uninit_value(dst: Writable) -> Self { @@ -666,7 +664,7 @@ impl Inst { srcloc: Option, ) -> Inst { debug_assert!(src.get_class() == RegClass::V128); - Inst::Xmm_Mov_R_M { + Inst::XmmMovRM { op, src, dst: dst.into(), @@ -709,7 +707,7 @@ impl Inst { pub(crate) fn xmm_cmp_rm_r(op: SseOpcode, src: RegMem, dst: Reg) -> Inst { src.assert_regclass_is(RegClass::V128); debug_assert!(dst.get_class() == RegClass::V128); - Inst::XMM_Cmp_RM_R { op, src, dst } + Inst::XmmCmpRmR { op, src, dst } } pub(crate) fn cvt_u64_to_float_seq( @@ -824,7 +822,7 @@ impl Inst { ) -> Inst { src.assert_regclass_is(RegClass::I64); debug_assert!(dst.to_reg().get_class() == RegClass::I64); - Inst::MovZX_RM_R { + Inst::MovzxRmR { ext_mode, src, dst, @@ -846,7 +844,7 @@ impl Inst { ) -> Inst { src.assert_regclass_is(RegClass::I64); debug_assert!(dst.to_reg().get_class() == RegClass::I64); - Inst::MovSX_RM_R { + Inst::MovsxRmR { ext_mode, src, dst, @@ -860,7 +858,7 @@ impl Inst { srcloc: Option, ) -> Inst { debug_assert!(dst.to_reg().get_class() == RegClass::I64); - Inst::Mov64_M_R { + Inst::Mov64MR { src: src.into(), dst, srcloc, @@ -884,7 +882,7 @@ impl Inst { ) -> Inst { debug_assert!(size == 8 || size == 4 || size == 2 || size == 1); debug_assert!(src.get_class() == RegClass::I64); - Inst::Mov_R_M { + Inst::MovRM { size, src, dst: dst.into(), @@ -913,7 +911,7 @@ impl Inst { true }); debug_assert!(dst.to_reg().get_class() == RegClass::I64); - Inst::Shift_R { + Inst::ShiftR { size, kind, num_bits, @@ -931,7 +929,7 @@ impl Inst { src.assert_regclass_is(RegClass::I64); debug_assert!(size == 8 || size == 4 || size == 2 || size == 1); debug_assert!(dst.get_class() == RegClass::I64); - Inst::Cmp_RMI_R { size, src, dst } + Inst::CmpRmiR { size, src, dst } } pub(crate) fn trap(srcloc: SourceLoc, trap_code: TrapCode) -> Inst { @@ -1135,12 +1133,12 @@ impl Inst { /// same as the first register (already handled). fn produces_const(&self) -> bool { match self { - Self::Alu_RMI_R { op, src, dst, .. } => { + Self::AluRmiR { op, src, dst, .. } => { src.to_reg() == Some(dst.to_reg()) && (*op == AluRmiROpcode::Xor || *op == AluRmiROpcode::Sub) } - Self::XMM_RM_R { op, src, dst, .. } => { + Self::XmmRmR { op, src, dst, .. } => { src.to_reg() == Some(dst.to_reg()) && (*op == SseOpcode::Xorps || *op == SseOpcode::Xorpd @@ -1183,11 +1181,11 @@ impl ShowWithRRU for Inst { ljustify(s1 + &s2) } - fn suffixLQ(is_64: bool) -> String { + fn suffix_lq(is_64: bool) -> String { (if is_64 { "q" } else { "l" }).to_string() } - fn sizeLQ(is_64: bool) -> u8 { + fn size_lq(is_64: bool) -> u8 { if is_64 { 8 } else { @@ -1195,7 +1193,7 @@ impl ShowWithRRU for Inst { } } - fn suffixBWLQ(size: u8) -> String { + fn suffix_bwlq(size: u8) -> String { match size { 1 => "b".to_string(), 2 => "w".to_string(), @@ -1208,34 +1206,34 @@ impl ShowWithRRU for Inst { match self { Inst::Nop { len } => format!("{} len={}", ljustify("nop".to_string()), len), - Inst::Alu_RMI_R { + Inst::AluRmiR { is_64, op, src, dst, } => format!( "{} {}, {}", - ljustify2(op.to_string(), suffixLQ(*is_64)), - src.show_rru_sized(mb_rru, sizeLQ(*is_64)), - show_ireg_sized(dst.to_reg(), mb_rru, sizeLQ(*is_64)), + ljustify2(op.to_string(), suffix_lq(*is_64)), + src.show_rru_sized(mb_rru, size_lq(*is_64)), + show_ireg_sized(dst.to_reg(), mb_rru, size_lq(*is_64)), ), Inst::UnaryRmR { src, dst, op, size } => format!( "{} {}, {}", - ljustify2(op.to_string(), suffixBWLQ(*size)), + ljustify2(op.to_string(), suffix_bwlq(*size)), src.show_rru_sized(mb_rru, *size), show_ireg_sized(dst.to_reg(), mb_rru, *size), ), Inst::Not { size, src } => format!( "{} {}", - ljustify2("not".to_string(), suffixBWLQ(*size)), + ljustify2("not".to_string(), suffix_bwlq(*size)), show_ireg_sized(src.to_reg(), mb_rru, *size) ), Inst::Neg { size, src } => format!( "{} {}", - ljustify2("neg".to_string(), suffixBWLQ(*size)), + ljustify2("neg".to_string(), suffix_bwlq(*size)), show_ireg_sized(src.to_reg(), mb_rru, *size) ), @@ -1298,14 +1296,14 @@ impl ShowWithRRU for Inst { show_ireg_sized(dst.to_reg(), mb_rru, 8), ), - Inst::Xmm_Mov_R_M { op, src, dst, .. } => format!( + Inst::XmmMovRM { op, src, dst, .. } => format!( "{} {}, {}", ljustify(op.to_string()), show_ireg_sized(*src, mb_rru, 8), dst.show_rru(mb_rru), ), - Inst::XMM_RM_R { op, src, dst } => format!( + Inst::XmmRmR { op, src, dst } => format!( "{} {}, {}", ljustify(op.to_string()), src.show_rru_sized(mb_rru, 8), @@ -1383,7 +1381,7 @@ impl ShowWithRRU for Inst { dst.show_rru(mb_rru) ), - Inst::XMM_Cmp_RM_R { op, src, dst } => format!( + Inst::XmmCmpRmR { op, src, dst } => format!( "{} {}, {}", ljustify(op.to_string()), src.show_rru_sized(mb_rru, 8), @@ -1474,14 +1472,14 @@ impl ShowWithRRU for Inst { } } - Inst::Mov_R_R { is_64, src, dst } => format!( + Inst::MovRR { is_64, src, dst } => format!( "{} {}, {}", - ljustify2("mov".to_string(), suffixLQ(*is_64)), - show_ireg_sized(*src, mb_rru, sizeLQ(*is_64)), - show_ireg_sized(dst.to_reg(), mb_rru, sizeLQ(*is_64)) + ljustify2("mov".to_string(), suffix_lq(*is_64)), + show_ireg_sized(*src, mb_rru, size_lq(*is_64)), + show_ireg_sized(dst.to_reg(), mb_rru, size_lq(*is_64)) ), - Inst::MovZX_RM_R { + Inst::MovzxRmR { ext_mode, src, dst, .. } => { if *ext_mode == ExtMode::LQ { @@ -1501,7 +1499,7 @@ impl ShowWithRRU for Inst { } } - Inst::Mov64_M_R { src, dst, .. } => format!( + Inst::Mov64MR { src, dst, .. } => format!( "{} {}, {}", ljustify("movq".to_string()), src.show_rru(mb_rru), @@ -1515,7 +1513,7 @@ impl ShowWithRRU for Inst { dst.show_rru(mb_rru) ), - Inst::MovSX_RM_R { + Inst::MovsxRmR { ext_mode, src, dst, .. } => format!( "{} {}, {}", @@ -1524,14 +1522,14 @@ impl ShowWithRRU for Inst { show_ireg_sized(dst.to_reg(), mb_rru, ext_mode.dst_size()) ), - Inst::Mov_R_M { size, src, dst, .. } => format!( + Inst::MovRM { size, src, dst, .. } => format!( "{} {}, {}", - ljustify2("mov".to_string(), suffixBWLQ(*size)), + ljustify2("mov".to_string(), suffix_bwlq(*size)), show_ireg_sized(*src, mb_rru, *size), dst.show_rru(mb_rru) ), - Inst::Shift_R { + Inst::ShiftR { size, kind, num_bits, @@ -1539,13 +1537,13 @@ impl ShowWithRRU for Inst { } => match num_bits { None => format!( "{} %cl, {}", - ljustify2(kind.to_string(), suffixBWLQ(*size)), + ljustify2(kind.to_string(), suffix_bwlq(*size)), show_ireg_sized(dst.to_reg(), mb_rru, *size) ), Some(num_bits) => format!( "{} ${}, {}", - ljustify2(kind.to_string(), suffixBWLQ(*size)), + ljustify2(kind.to_string(), suffix_bwlq(*size)), num_bits, show_ireg_sized(dst.to_reg(), mb_rru, *size) ), @@ -1558,9 +1556,9 @@ impl ShowWithRRU for Inst { dst.to_reg().show_rru(mb_rru) ), - Inst::Cmp_RMI_R { size, src, dst } => format!( + Inst::CmpRmiR { size, src, dst } => format!( "{} {}, {}", - ljustify2("cmp".to_string(), suffixBWLQ(*size)), + ljustify2("cmp".to_string(), suffix_bwlq(*size)), src.show_rru_sized(mb_rru, *size), show_ireg_sized(*dst, mb_rru, *size) ), @@ -1573,7 +1571,7 @@ impl ShowWithRRU for Inst { Inst::Cmove { size, cc, src, dst } => format!( "{} {}, {}", - ljustify(format!("cmov{}{}", cc.to_string(), suffixBWLQ(*size))), + ljustify(format!("cmov{}{}", cc.to_string(), suffix_bwlq(*size))), src.show_rru_sized(mb_rru, *size), show_ireg_sized(dst.to_reg(), mb_rru, *size) ), @@ -1662,7 +1660,7 @@ impl ShowWithRRU for Inst { Inst::LockCmpxchg { ty, src, dst, .. } => { let size = ty.bytes() as u8; format!("lock cmpxchg{} {}, {}", - suffixBWLQ(size), show_ireg_sized(*src, mb_rru, size), dst.show_rru(mb_rru)) + suffix_bwlq(size), show_ireg_sized(*src, mb_rru, size), dst.show_rru(mb_rru)) } Inst::AtomicRmwSeq { ty, op, .. } => { @@ -1702,7 +1700,7 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) { // regalloc.rs will "fix" this for us by removing the the modified set from the use and def // sets. match inst { - Inst::Alu_RMI_R { src, dst, .. } => { + Inst::AluRmiR { src, dst, .. } => { if inst.produces_const() { // No need to account for src, since src == dst. collector.add_def(*dst); @@ -1754,7 +1752,7 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) { src.get_regs_as_uses(collector); collector.add_def(*dst); } - Inst::XMM_RM_R { src, dst, .. } => { + Inst::XmmRmR { src, dst, .. } => { if inst.produces_const() { // No need to account for src, since src == dst. collector.add_def(*dst); @@ -1789,18 +1787,18 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) { src.get_regs_as_uses(collector); collector.add_mod(*dst); } - Inst::Xmm_Mov_R_M { src, dst, .. } => { + Inst::XmmMovRM { src, dst, .. } => { collector.add_use(*src); dst.get_regs_as_uses(collector); } - Inst::XMM_Cmp_RM_R { src, dst, .. } => { + Inst::XmmCmpRmR { src, dst, .. } => { src.get_regs_as_uses(collector); collector.add_use(*dst); } Inst::Imm { dst, .. } => { collector.add_def(*dst); } - Inst::Mov_R_R { src, dst, .. } | Inst::XmmToGpr { src, dst, .. } => { + Inst::MovRR { src, dst, .. } | Inst::XmmToGpr { src, dst, .. } => { collector.add_use(*src); collector.add_def(*dst); } @@ -1839,29 +1837,29 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) { collector.add_def(*tmp_gpr); collector.add_def(*tmp_xmm); } - Inst::MovZX_RM_R { src, dst, .. } => { + Inst::MovzxRmR { src, dst, .. } => { src.get_regs_as_uses(collector); collector.add_def(*dst); } - Inst::Mov64_M_R { src, dst, .. } | Inst::LoadEffectiveAddress { addr: src, dst } => { + Inst::Mov64MR { src, dst, .. } | Inst::LoadEffectiveAddress { addr: src, dst } => { src.get_regs_as_uses(collector); collector.add_def(*dst) } - Inst::MovSX_RM_R { src, dst, .. } => { + Inst::MovsxRmR { src, dst, .. } => { src.get_regs_as_uses(collector); collector.add_def(*dst); } - Inst::Mov_R_M { src, dst, .. } => { + Inst::MovRM { src, dst, .. } => { collector.add_use(*src); dst.get_regs_as_uses(collector); } - Inst::Shift_R { num_bits, dst, .. } => { + Inst::ShiftR { num_bits, dst, .. } => { if num_bits.is_none() { collector.add_use(regs::rcx()); } collector.add_mod(*dst); } - Inst::Cmp_RMI_R { src, dst, .. } => { + Inst::CmpRmiR { src, dst, .. } => { src.get_regs_as_uses(collector); collector.add_use(*dst); // yes, really `add_use` } @@ -2036,7 +2034,7 @@ fn x64_map_regs(inst: &mut Inst, mapper: &RUM) { match inst { // ** Nop - Inst::Alu_RMI_R { + Inst::AluRmiR { ref mut src, ref mut dst, .. @@ -2093,7 +2091,7 @@ fn x64_map_regs(inst: &mut Inst, mapper: &RUM) { map_mod(mapper, dst); } } - Inst::XMM_RM_R { + Inst::XmmRmR { ref mut src, ref mut dst, .. @@ -2128,7 +2126,7 @@ fn x64_map_regs(inst: &mut Inst, mapper: &RUM) { map_use(mapper, lhs); map_mod(mapper, rhs_dst); } - Inst::Xmm_Mov_R_M { + Inst::XmmMovRM { ref mut src, ref mut dst, .. @@ -2136,7 +2134,7 @@ fn x64_map_regs(inst: &mut Inst, mapper: &RUM) { map_use(mapper, src); dst.map_uses(mapper); } - Inst::XMM_Cmp_RM_R { + Inst::XmmCmpRmR { ref mut src, ref mut dst, .. @@ -2145,7 +2143,7 @@ fn x64_map_regs(inst: &mut Inst, mapper: &RUM) { map_use(mapper, dst); } Inst::Imm { ref mut dst, .. } => map_def(mapper, dst), - Inst::Mov_R_R { + Inst::MovRR { ref mut src, ref mut dst, .. @@ -2197,7 +2195,7 @@ fn x64_map_regs(inst: &mut Inst, mapper: &RUM) { map_def(mapper, tmp_gpr); map_def(mapper, tmp_xmm); } - Inst::MovZX_RM_R { + Inst::MovzxRmR { ref mut src, ref mut dst, .. @@ -2205,11 +2203,11 @@ fn x64_map_regs(inst: &mut Inst, mapper: &RUM) { src.map_uses(mapper); map_def(mapper, dst); } - Inst::Mov64_M_R { src, dst, .. } | Inst::LoadEffectiveAddress { addr: src, dst } => { + Inst::Mov64MR { src, dst, .. } | Inst::LoadEffectiveAddress { addr: src, dst } => { src.map_uses(mapper); map_def(mapper, dst); } - Inst::MovSX_RM_R { + Inst::MovsxRmR { ref mut src, ref mut dst, .. @@ -2217,7 +2215,7 @@ fn x64_map_regs(inst: &mut Inst, mapper: &RUM) { src.map_uses(mapper); map_def(mapper, dst); } - Inst::Mov_R_M { + Inst::MovRM { ref mut src, ref mut dst, .. @@ -2225,10 +2223,10 @@ fn x64_map_regs(inst: &mut Inst, mapper: &RUM) { map_use(mapper, src); dst.map_uses(mapper); } - Inst::Shift_R { ref mut dst, .. } => { + Inst::ShiftR { ref mut dst, .. } => { map_mod(mapper, dst); } - Inst::Cmp_RMI_R { + Inst::CmpRmiR { ref mut src, ref mut dst, .. @@ -2343,7 +2341,7 @@ impl MachInst for Inst { // out the upper 32 bits of the destination. For example, we could // conceivably use `movl %reg, %reg` to zero out the top 32 bits of // %reg. - Self::Mov_R_R { + Self::MovRR { is_64, src, dst, .. } if *is_64 => Some((*dst, *src)), // Note as well that MOVS[S|D] when used in the `XmmUnaryRmR` context are pure moves of diff --git a/third_party/rust/cranelift-codegen/src/isa/x64/lower.rs b/third_party/rust/cranelift-codegen/src/isa/x64/lower.rs index 4fa2000299395..6eac09618a981 100644 --- a/third_party/rust/cranelift-codegen/src/isa/x64/lower.rs +++ b/third_party/rust/cranelift-codegen/src/isa/x64/lower.rs @@ -1,7 +1,5 @@ //! Lowering rules for X64. -#![allow(non_snake_case)] - use crate::ir::{ condcodes::FloatCC, condcodes::IntCC, types, AbiParam, ArgumentPurpose, ExternalName, Inst as IRInst, InstructionData, LibCall, Opcode, Signature, Type, @@ -1763,12 +1761,204 @@ fn lower_insn_to_regs>( let is_min = op == Opcode::Fmin; let output_ty = ty.unwrap(); ctx.emit(Inst::gen_move(dst, rhs, output_ty)); - let op_size = match output_ty { - types::F32 => OperandSize::Size32, - types::F64 => OperandSize::Size64, - _ => panic!("unexpected type {:?} for fmin/fmax", output_ty), - }; - ctx.emit(Inst::xmm_min_max_seq(op_size, is_min, lhs, dst)); + if !output_ty.is_vector() { + let op_size = match output_ty { + types::F32 => OperandSize::Size32, + types::F64 => OperandSize::Size64, + _ => panic!("unexpected type {:?} for fmin/fmax", output_ty), + }; + ctx.emit(Inst::xmm_min_max_seq(op_size, is_min, lhs, dst)); + } else { + // X64's implementation of floating point min and floating point max does not + // propagate NaNs and +0's in a way that is friendly to the SIMD spec. For the + // scalar approach we use jumps to handle cases where NaN and +0 propagation is + // not consistent with what is needed. However for packed floating point min and + // floating point max we implement a different approach to avoid the sequence + // of jumps that would be required on a per lane basis. Because we do not need to + // lower labels and jumps but do need ctx for creating temporaries we implement + // the lowering here in lower.rs instead of emit.rs as is done in the case for scalars. + // The outline of approach is as follows: + // + // First we preform the Min/Max in both directions. This is because in the + // case of an operand's lane containing a NaN or in the case of the lanes of the + // two operands containing 0 but with mismatched signs, x64 will return the second + // operand regardless of its contents. So in order to make sure we capture NaNs and + // normalize NaNs and 0 values we capture the operation in both directions and merge the + // results. Then we normalize the results through operations that create a mask for the + // lanes containing NaNs, we use that mask to adjust NaNs to quite NaNs and normalize + // 0s. + // + // The following sequence is generated for min: + // + // movap{s,d} %lhs, %tmp + // minp{s,d} %dst, %tmp + // minp,{s,d} %lhs, %dst + // orp{s,d} %dst, %tmp + // cmpp{s,d} %tmp, %dst, $3 + // orps{s,d} %dst, %tmp + // psrl{s,d} {$10, $13}, %dst + // andnp{s,d} %tmp, %dst + // + // and for max the sequence is: + // + // movap{s,d} %lhs, %tmp + // minp{s,d} %dst, %tmp + // minp,{s,d} %lhs, %dst + // xorp{s,d} %tmp, %dst + // orp{s,d} %dst, %tmp + // subp{s,d} %dst, %tmp + // cmpp{s,d} %tmp, %dst, $3 + // psrl{s,d} {$10, $13}, %dst + // andnp{s,d} %tmp, %dst + + if is_min { + let (mov_op, min_op, or_op, cmp_op, shift_op, shift_by, andn_op) = + match output_ty { + types::F32X4 => ( + SseOpcode::Movaps, + SseOpcode::Minps, + SseOpcode::Orps, + SseOpcode::Cmpps, + SseOpcode::Psrld, + 10, + SseOpcode::Andnps, + ), + types::F64X2 => ( + SseOpcode::Movapd, + SseOpcode::Minpd, + SseOpcode::Orpd, + SseOpcode::Cmppd, + SseOpcode::Psrlq, + 13, + SseOpcode::Andnpd, + ), + _ => unimplemented!("unsupported op type {:?}", output_ty), + }; + + // Copy lhs into tmp + let tmp_xmm1 = ctx.alloc_tmp(RegClass::V128, output_ty); + ctx.emit(Inst::xmm_mov(mov_op, RegMem::reg(lhs), tmp_xmm1, None)); + + // Perform min in reverse direction + ctx.emit(Inst::xmm_rm_r(min_op, RegMem::from(dst), tmp_xmm1)); + + // Perform min in original direction + ctx.emit(Inst::xmm_rm_r(min_op, RegMem::reg(lhs), dst)); + + // X64 handles propagation of -0's and Nans differently between left and right + // operands. After doing the min in both directions, this OR will + // guarrentee capture of -0's and Nan in our tmp register + ctx.emit(Inst::xmm_rm_r(or_op, RegMem::from(dst), tmp_xmm1)); + + // Compare unordered to create mask for lanes containing NaNs and then use + // that mask to saturate the NaN containing lanes in the tmp register with 1s. + // TODO: Would a check for NaN and then a jump be better here in the + // common case than continuing on to normalize NaNs that might not exist? + let cond = FcmpImm::from(FloatCC::Unordered); + ctx.emit(Inst::xmm_rm_r_imm( + cmp_op, + RegMem::reg(tmp_xmm1.to_reg()), + dst, + cond.encode(), + false, + )); + ctx.emit(Inst::xmm_rm_r(or_op, RegMem::reg(dst.to_reg()), tmp_xmm1)); + + // The dst register holds a mask for lanes containing NaNs. + // We take that mask and shift in preparation for creating a different mask + // to normalize NaNs (create a quite NaN) by zeroing out the appropriate + // number of least signficant bits. We shift right each lane by 10 bits + // (1 sign + 8 exp. + 1 MSB sig.) for F32X4 and by 13 bits (1 sign + + // 11 exp. + 1 MSB sig.) for F64X2. + ctx.emit(Inst::xmm_rmi_reg(shift_op, RegMemImm::imm(shift_by), dst)); + + // Finally we do a nand with the tmp register to produce the final results + // in the dst. + ctx.emit(Inst::xmm_rm_r(andn_op, RegMem::reg(tmp_xmm1.to_reg()), dst)); + } else { + let ( + mov_op, + max_op, + xor_op, + or_op, + sub_op, + cmp_op, + shift_op, + shift_by, + andn_op, + ) = match output_ty { + types::F32X4 => ( + SseOpcode::Movaps, + SseOpcode::Maxps, + SseOpcode::Xorps, + SseOpcode::Orps, + SseOpcode::Subps, + SseOpcode::Cmpps, + SseOpcode::Psrld, + 10, + SseOpcode::Andnps, + ), + types::F64X2 => ( + SseOpcode::Movapd, + SseOpcode::Maxpd, + SseOpcode::Xorpd, + SseOpcode::Orpd, + SseOpcode::Subpd, + SseOpcode::Cmppd, + SseOpcode::Psrlq, + 13, + SseOpcode::Andnpd, + ), + _ => unimplemented!("unsupported op type {:?}", output_ty), + }; + + // Copy lhs into tmp. + let tmp_xmm1 = ctx.alloc_tmp(RegClass::V128, types::F32); + ctx.emit(Inst::xmm_mov(mov_op, RegMem::reg(lhs), tmp_xmm1, None)); + + // Perform max in reverse direction. + ctx.emit(Inst::xmm_rm_r(max_op, RegMem::reg(dst.to_reg()), tmp_xmm1)); + + // Perform max in original direction. + ctx.emit(Inst::xmm_rm_r(max_op, RegMem::reg(lhs), dst)); + + // Get the difference between the two results and store in tmp. + // Max uses a different approach than min to account for potential + // discrepancies with plus/minus 0. + ctx.emit(Inst::xmm_rm_r(xor_op, RegMem::reg(tmp_xmm1.to_reg()), dst)); + + // X64 handles propagation of -0's and Nans differently between left and right + // operands. After doing the max in both directions, this OR will + // guarentee capture of 0's and Nan in our tmp register. + ctx.emit(Inst::xmm_rm_r(or_op, RegMem::reg(dst.to_reg()), tmp_xmm1)); + + // Capture NaNs and sign discrepancies. + ctx.emit(Inst::xmm_rm_r(sub_op, RegMem::reg(dst.to_reg()), tmp_xmm1)); + + // Compare unordered to create mask for lanes containing NaNs and then use + // that mask to saturate the NaN containing lanes in the tmp register with 1s. + let cond = FcmpImm::from(FloatCC::Unordered); + ctx.emit(Inst::xmm_rm_r_imm( + cmp_op, + RegMem::reg(tmp_xmm1.to_reg()), + dst, + cond.encode(), + false, + )); + + // The dst register holds a mask for lanes containing NaNs. + // We take that mask and shift in preparation for creating a different mask + // to normalize NaNs (create a quite NaN) by zeroing out the appropriate + // number of least signficant bits. We shift right each lane by 10 bits + // (1 sign + 8 exp. + 1 MSB sig.) for F32X4 and by 13 bits (1 sign + + // 11 exp. + 1 MSB sig.) for F64X2. + ctx.emit(Inst::xmm_rmi_reg(shift_op, RegMemImm::imm(shift_by), dst)); + + // Finally we do a nand with the tmp register to produce the final results + // in the dst. + ctx.emit(Inst::xmm_rm_r(andn_op, RegMem::reg(tmp_xmm1.to_reg()), dst)); + } + } } Opcode::Sqrt => { diff --git a/third_party/rust/cranelift-wasm/.cargo-checksum.json b/third_party/rust/cranelift-wasm/.cargo-checksum.json index e1815c8b48116..a7d5ba091c65c 100644 --- a/third_party/rust/cranelift-wasm/.cargo-checksum.json +++ b/third_party/rust/cranelift-wasm/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"7a4570632d7829d4596daeae9c0d17cb5a7120e34e4234022b2cd3b9700e8c93","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"c82c252fbeeaa101a0eef042b9a925eb1fa3d2b51d19481b9c22e593e6a8d772","src/code_translator.rs":"d85885a51f19f25a1c60c94f6f35815273547a18b04d73584d7a8d283c099651","src/environ/dummy.rs":"0c05a77ab37a305c799f1b0e99c9debe1b8f59a3e3aa764e2fe39a923716b2ee","src/environ/mod.rs":"692f35d75f125f9c071f7166252f427e4bac29401356f73307c6c36e23c667fb","src/environ/spec.rs":"0f97fff3cc545772a1959f7d0439713fd7dc8d8adf43a2636f8174126dc1393c","src/func_translator.rs":"6af2fe6d71ab05aac84aaa7149bfb7be18f1ab056895bf9208a5fe0d502118c9","src/lib.rs":"7bdbcf638fa30fb05e8320439881f7536824f7f60a7db4f0c1b51ab369edf895","src/module_translator.rs":"1374fa56ca18a782083fa0f25f2ad675044a92bbf1a0a1cc44fcaf695807e044","src/sections_translator.rs":"11d65fd2e595e41f976e5c7d0df823f70449f79a9d2facbed61263616f8cfec1","src/state/func_state.rs":"581a5648b11fa07aef3cff0752597864c7cd44a4d44e27c50fc7349955b3fda3","src/state/mod.rs":"20014cb93615467b4d20321b52f67f66040417efcaa739a4804093bb559eed19","src/state/module_state.rs":"7ca3cb06b4481bc3ae74697fbcd437aea1d851eaa3cfe18cc013a4af43728957","src/translation_utils.rs":"69f20c47ea22f0badd21a6187b5f9764252a00d19643a7e3e691797a9fe34f1b","tests/wasm_testsuite.rs":"da8dedfd11918946e9cf6af68fd4826f020ef90a4e22742b1a30e61a3fb4aedd"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"53367fadfe62cdc349a55a3460992677690300d6e568e38b8d73e2b5c7376c7d","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"c82c252fbeeaa101a0eef042b9a925eb1fa3d2b51d19481b9c22e593e6a8d772","src/code_translator.rs":"b25c4a4b002f2fab0364fd890539de76bc163e6f8a817d1a53bec271970ea903","src/environ/dummy.rs":"f780d523b7024d4fed923992bbe95ca4816e3a8770a83ea01de94d4cd1625b6e","src/environ/mod.rs":"692f35d75f125f9c071f7166252f427e4bac29401356f73307c6c36e23c667fb","src/environ/spec.rs":"ed806f95bd13e26ae14684287f547e77bdcc31c10a6a23ee45764f573ff4793f","src/func_translator.rs":"ebc7e7f872d03fc05e9c013e0eb575b0ae2828322b7194c60b8e764f2816d12e","src/lib.rs":"83fbcf3561b7255d346b6b33114f4a051f3a7133f11f1a238c0fe12ddbdd1ba7","src/module_translator.rs":"dc63d45d1917034f9d06008899143e32041e6d3475629d3c6eee3bb0195a0b3e","src/sections_translator.rs":"00012d18f1467f71959c4b68dcb2c059523011cac50f7ac869d3206ce9021475","src/state/func_state.rs":"581a5648b11fa07aef3cff0752597864c7cd44a4d44e27c50fc7349955b3fda3","src/state/mod.rs":"20014cb93615467b4d20321b52f67f66040417efcaa739a4804093bb559eed19","src/state/module_state.rs":"7ca3cb06b4481bc3ae74697fbcd437aea1d851eaa3cfe18cc013a4af43728957","src/translation_utils.rs":"1b9d3d63fc259c9c657747da750d0434f0acbc53381ca32860b04d017754eb55","tests/wasm_testsuite.rs":"5e9f8441acdafe1552b4ae79c8c27603dad2b047791b035d28354d9f29b4d4e7"},"package":null} \ No newline at end of file diff --git a/third_party/rust/cranelift-wasm/Cargo.toml b/third_party/rust/cranelift-wasm/Cargo.toml index 08288f32efe8f..bae037044e6a7 100644 --- a/third_party/rust/cranelift-wasm/Cargo.toml +++ b/third_party/rust/cranelift-wasm/Cargo.toml @@ -12,11 +12,12 @@ keywords = ["webassembly", "wasm"] edition = "2018" [dependencies] -wasmparser = { version = "0.59.0", default-features = false } +wasmparser = { version = "0.63.0", default-features = false } cranelift-codegen = { path = "../codegen", version = "0.67.0", default-features = false } cranelift-entity = { path = "../entity", version = "0.67.0" } cranelift-frontend = { path = "../frontend", version = "0.67.0", default-features = false } hashbrown = { version = "0.7", optional = true } +itertools = "0.9.0" log = { version = "0.4.6", default-features = false } serde = { version = "1.0.94", features = ["derive"], optional = true } thiserror = "1.0.4" diff --git a/third_party/rust/cranelift-wasm/src/code_translator.rs b/third_party/rust/cranelift-wasm/src/code_translator.rs index 18f7e72901703..f1ef849177585 100644 --- a/third_party/rust/cranelift-wasm/src/code_translator.rs +++ b/third_party/rust/cranelift-wasm/src/code_translator.rs @@ -24,7 +24,7 @@ //! argument. use super::{hash_map, HashMap}; use crate::environ::{FuncEnvironment, GlobalVariable, ReturnMode, WasmResult}; -use crate::state::{ControlStackFrame, ElseData, FuncTranslationState, ModuleTranslationState}; +use crate::state::{ControlStackFrame, ElseData, FuncTranslationState}; use crate::translation_utils::{ block_with_params, blocktype_params_results, f32_translation, f64_translation, }; @@ -43,9 +43,9 @@ use cranelift_frontend::{FunctionBuilder, Variable}; use std::cmp; use std::convert::TryFrom; use std::vec::Vec; -use wasmparser::{MemoryImmediate, Operator}; +use wasmparser::{FuncValidator, MemoryImmediate, Operator, WasmModuleResources}; -// Clippy warns about "flags: _" but its important to document that the flags field is ignored +// Clippy warns about "align: _" but its important to document that the flags field is ignored #[cfg_attr( feature = "cargo-clippy", allow(clippy::unneeded_field_pattern, clippy::cognitive_complexity) @@ -53,14 +53,14 @@ use wasmparser::{MemoryImmediate, Operator}; /// Translates wasm operators into Cranelift IR instructions. Returns `true` if it inserted /// a return. pub fn translate_operator( - module_translation_state: &ModuleTranslationState, + validator: &mut FuncValidator, op: &Operator, builder: &mut FunctionBuilder, state: &mut FuncTranslationState, environ: &mut FE, ) -> WasmResult<()> { if !state.reachable { - translate_unreachable_operator(module_translation_state, &op, builder, state, environ)?; + translate_unreachable_operator(validator, &op, builder, state, environ)?; return Ok(()); } @@ -180,14 +180,14 @@ pub fn translate_operator( * possible `Block`'s arguments values. ***********************************************************************************/ Operator::Block { ty } => { - let (params, results) = blocktype_params_results(module_translation_state, *ty)?; - let next = block_with_params(builder, results, environ)?; + let (params, results) = blocktype_params_results(validator, *ty)?; + let next = block_with_params(builder, results.clone(), environ)?; state.push_block(next, params.len(), results.len()); } Operator::Loop { ty } => { - let (params, results) = blocktype_params_results(module_translation_state, *ty)?; - let loop_body = block_with_params(builder, params, environ)?; - let next = block_with_params(builder, results, environ)?; + let (params, results) = blocktype_params_results(validator, *ty)?; + let loop_body = block_with_params(builder, params.clone(), environ)?; + let next = block_with_params(builder, results.clone(), environ)?; builder.ins().jump(loop_body, state.peekn(params.len())); state.push_loop(loop_body, next, params.len(), results.len()); @@ -204,15 +204,15 @@ pub fn translate_operator( Operator::If { ty } => { let val = state.pop1(); - let (params, results) = blocktype_params_results(module_translation_state, *ty)?; - let (destination, else_data) = if params == results { + let (params, results) = blocktype_params_results(validator, *ty)?; + let (destination, else_data) = if params.clone().eq(results.clone()) { // It is possible there is no `else` block, so we will only // allocate a block for it if/when we find the `else`. For now, // we if the condition isn't true, then we jump directly to the // destination block following the whole `if...end`. If we do end // up discovering an `else`, then we will allocate a block for it // and go back and patch the jump. - let destination = block_with_params(builder, results, environ)?; + let destination = block_with_params(builder, results.clone(), environ)?; let branch_inst = builder .ins() .brz(val, destination, state.peekn(params.len())); @@ -220,8 +220,8 @@ pub fn translate_operator( } else { // The `if` type signature is not valid without an `else` block, // so we eagerly allocate the `else` block here. - let destination = block_with_params(builder, results, environ)?; - let else_block = block_with_params(builder, params, environ)?; + let destination = block_with_params(builder, results.clone(), environ)?; + let else_block = block_with_params(builder, params.clone(), environ)?; builder .ins() .brz(val, else_block, state.peekn(params.len())); @@ -268,9 +268,10 @@ pub fn translate_operator( let else_block = match *else_data { ElseData::NoElse { branch_inst } => { let (params, _results) = - blocktype_params_results(module_translation_state, blocktype)?; + blocktype_params_results(validator, blocktype)?; debug_assert_eq!(params.len(), num_return_values); - let else_block = block_with_params(builder, params, environ)?; + let else_block = + block_with_params(builder, params.clone(), environ)?; builder.ins().jump(destination, state.peekn(params.len())); state.popn(params.len()); @@ -387,9 +388,10 @@ pub fn translate_operator( } Operator::BrIf { relative_depth } => translate_br_if(*relative_depth, builder, state), Operator::BrTable { table } => { - let (depths, default) = table.read_table()?; + let mut depths = table.targets().collect::, _>>()?; + let default = depths.pop().unwrap().0; let mut min_depth = default; - for depth in &*depths { + for (depth, _) in depths.iter() { if *depth < min_depth { min_depth = *depth; } @@ -407,7 +409,7 @@ pub fn translate_operator( let mut data = JumpTableData::with_capacity(depths.len()); if jump_args_count == 0 { // No jump arguments - for depth in &*depths { + for (depth, _) in depths.iter() { let block = { let i = state.control_stack.len() - 1 - (*depth as usize); let frame = &mut state.control_stack[i]; @@ -430,7 +432,7 @@ pub fn translate_operator( let return_count = jump_args_count; let mut dest_block_sequence = vec![]; let mut dest_block_map = HashMap::new(); - for depth in &*depths { + for (depth, _) in depths.iter() { let branch_block = match dest_block_map.entry(*depth as usize) { hash_map::Entry::Occupied(entry) => *entry.get(), hash_map::Entry::Vacant(entry) => { @@ -570,137 +572,95 @@ pub fn translate_operator( * Memory management is handled by environment. It is usually translated into calls to * special functions. ************************************************************************************/ - Operator::MemoryGrow { reserved } => { + Operator::MemoryGrow { mem, mem_byte: _ } => { // The WebAssembly MVP only supports one linear memory, but we expect the reserved // argument to be a memory index. - let heap_index = MemoryIndex::from_u32(*reserved); - let heap = state.get_heap(builder.func, *reserved, environ)?; + let heap_index = MemoryIndex::from_u32(*mem); + let heap = state.get_heap(builder.func, *mem, environ)?; let val = state.pop1(); state.push1(environ.translate_memory_grow(builder.cursor(), heap_index, heap, val)?) } - Operator::MemorySize { reserved } => { - let heap_index = MemoryIndex::from_u32(*reserved); - let heap = state.get_heap(builder.func, *reserved, environ)?; + Operator::MemorySize { mem, mem_byte: _ } => { + let heap_index = MemoryIndex::from_u32(*mem); + let heap = state.get_heap(builder.func, *mem, environ)?; state.push1(environ.translate_memory_size(builder.cursor(), heap_index, heap)?); } /******************************* Load instructions *********************************** * Wasm specifies an integer alignment flag but we drop it in Cranelift. * The memory base address is provided by the environment. ************************************************************************************/ - Operator::I32Load8U { - memarg: MemoryImmediate { flags: _, offset }, - } => { - translate_load(*offset, ir::Opcode::Uload8, I32, builder, state, environ)?; + Operator::I32Load8U { memarg } => { + translate_load(memarg, ir::Opcode::Uload8, I32, builder, state, environ)?; } - Operator::I32Load16U { - memarg: MemoryImmediate { flags: _, offset }, - } => { - translate_load(*offset, ir::Opcode::Uload16, I32, builder, state, environ)?; + Operator::I32Load16U { memarg } => { + translate_load(memarg, ir::Opcode::Uload16, I32, builder, state, environ)?; } - Operator::I32Load8S { - memarg: MemoryImmediate { flags: _, offset }, - } => { - translate_load(*offset, ir::Opcode::Sload8, I32, builder, state, environ)?; + Operator::I32Load8S { memarg } => { + translate_load(memarg, ir::Opcode::Sload8, I32, builder, state, environ)?; } - Operator::I32Load16S { - memarg: MemoryImmediate { flags: _, offset }, - } => { - translate_load(*offset, ir::Opcode::Sload16, I32, builder, state, environ)?; + Operator::I32Load16S { memarg } => { + translate_load(memarg, ir::Opcode::Sload16, I32, builder, state, environ)?; } - Operator::I64Load8U { - memarg: MemoryImmediate { flags: _, offset }, - } => { - translate_load(*offset, ir::Opcode::Uload8, I64, builder, state, environ)?; + Operator::I64Load8U { memarg } => { + translate_load(memarg, ir::Opcode::Uload8, I64, builder, state, environ)?; } - Operator::I64Load16U { - memarg: MemoryImmediate { flags: _, offset }, - } => { - translate_load(*offset, ir::Opcode::Uload16, I64, builder, state, environ)?; + Operator::I64Load16U { memarg } => { + translate_load(memarg, ir::Opcode::Uload16, I64, builder, state, environ)?; } - Operator::I64Load8S { - memarg: MemoryImmediate { flags: _, offset }, - } => { - translate_load(*offset, ir::Opcode::Sload8, I64, builder, state, environ)?; + Operator::I64Load8S { memarg } => { + translate_load(memarg, ir::Opcode::Sload8, I64, builder, state, environ)?; } - Operator::I64Load16S { - memarg: MemoryImmediate { flags: _, offset }, - } => { - translate_load(*offset, ir::Opcode::Sload16, I64, builder, state, environ)?; + Operator::I64Load16S { memarg } => { + translate_load(memarg, ir::Opcode::Sload16, I64, builder, state, environ)?; } - Operator::I64Load32S { - memarg: MemoryImmediate { flags: _, offset }, - } => { - translate_load(*offset, ir::Opcode::Sload32, I64, builder, state, environ)?; + Operator::I64Load32S { memarg } => { + translate_load(memarg, ir::Opcode::Sload32, I64, builder, state, environ)?; } - Operator::I64Load32U { - memarg: MemoryImmediate { flags: _, offset }, - } => { - translate_load(*offset, ir::Opcode::Uload32, I64, builder, state, environ)?; + Operator::I64Load32U { memarg } => { + translate_load(memarg, ir::Opcode::Uload32, I64, builder, state, environ)?; } - Operator::I32Load { - memarg: MemoryImmediate { flags: _, offset }, - } => { - translate_load(*offset, ir::Opcode::Load, I32, builder, state, environ)?; + Operator::I32Load { memarg } => { + translate_load(memarg, ir::Opcode::Load, I32, builder, state, environ)?; } - Operator::F32Load { - memarg: MemoryImmediate { flags: _, offset }, - } => { - translate_load(*offset, ir::Opcode::Load, F32, builder, state, environ)?; + Operator::F32Load { memarg } => { + translate_load(memarg, ir::Opcode::Load, F32, builder, state, environ)?; } - Operator::I64Load { - memarg: MemoryImmediate { flags: _, offset }, - } => { - translate_load(*offset, ir::Opcode::Load, I64, builder, state, environ)?; + Operator::I64Load { memarg } => { + translate_load(memarg, ir::Opcode::Load, I64, builder, state, environ)?; } - Operator::F64Load { - memarg: MemoryImmediate { flags: _, offset }, - } => { - translate_load(*offset, ir::Opcode::Load, F64, builder, state, environ)?; + Operator::F64Load { memarg } => { + translate_load(memarg, ir::Opcode::Load, F64, builder, state, environ)?; } - Operator::V128Load { - memarg: MemoryImmediate { flags: _, offset }, - } => { - translate_load(*offset, ir::Opcode::Load, I8X16, builder, state, environ)?; + Operator::V128Load { memarg } => { + translate_load(memarg, ir::Opcode::Load, I8X16, builder, state, environ)?; } - Operator::I16x8Load8x8S { - memarg: MemoryImmediate { flags: _, offset }, - } => { - let (flags, base, offset) = prepare_load(*offset, 8, builder, state, environ)?; + Operator::V128Load8x8S { memarg } => { + let (flags, base, offset) = prepare_load(memarg, 8, builder, state, environ)?; let loaded = builder.ins().sload8x8(flags, base, offset); state.push1(loaded); } - Operator::I16x8Load8x8U { - memarg: MemoryImmediate { flags: _, offset }, - } => { - let (flags, base, offset) = prepare_load(*offset, 8, builder, state, environ)?; + Operator::V128Load8x8U { memarg } => { + let (flags, base, offset) = prepare_load(memarg, 8, builder, state, environ)?; let loaded = builder.ins().uload8x8(flags, base, offset); state.push1(loaded); } - Operator::I32x4Load16x4S { - memarg: MemoryImmediate { flags: _, offset }, - } => { - let (flags, base, offset) = prepare_load(*offset, 8, builder, state, environ)?; + Operator::V128Load16x4S { memarg } => { + let (flags, base, offset) = prepare_load(memarg, 8, builder, state, environ)?; let loaded = builder.ins().sload16x4(flags, base, offset); state.push1(loaded); } - Operator::I32x4Load16x4U { - memarg: MemoryImmediate { flags: _, offset }, - } => { - let (flags, base, offset) = prepare_load(*offset, 8, builder, state, environ)?; + Operator::V128Load16x4U { memarg } => { + let (flags, base, offset) = prepare_load(memarg, 8, builder, state, environ)?; let loaded = builder.ins().uload16x4(flags, base, offset); state.push1(loaded); } - Operator::I64x2Load32x2S { - memarg: MemoryImmediate { flags: _, offset }, - } => { - let (flags, base, offset) = prepare_load(*offset, 8, builder, state, environ)?; + Operator::V128Load32x2S { memarg } => { + let (flags, base, offset) = prepare_load(memarg, 8, builder, state, environ)?; let loaded = builder.ins().sload32x2(flags, base, offset); state.push1(loaded); } - Operator::I64x2Load32x2U { - memarg: MemoryImmediate { flags: _, offset }, - } => { - let (flags, base, offset) = prepare_load(*offset, 8, builder, state, environ)?; + Operator::V128Load32x2U { memarg } => { + let (flags, base, offset) = prepare_load(memarg, 8, builder, state, environ)?; let loaded = builder.ins().uload32x2(flags, base, offset); state.push1(loaded); } @@ -708,45 +668,23 @@ pub fn translate_operator( * Wasm specifies an integer alignment flag but we drop it in Cranelift. * The memory base address is provided by the environment. ************************************************************************************/ - Operator::I32Store { - memarg: MemoryImmediate { flags: _, offset }, + Operator::I32Store { memarg } + | Operator::I64Store { memarg } + | Operator::F32Store { memarg } + | Operator::F64Store { memarg } => { + translate_store(memarg, ir::Opcode::Store, builder, state, environ)?; } - | Operator::I64Store { - memarg: MemoryImmediate { flags: _, offset }, + Operator::I32Store8 { memarg } | Operator::I64Store8 { memarg } => { + translate_store(memarg, ir::Opcode::Istore8, builder, state, environ)?; } - | Operator::F32Store { - memarg: MemoryImmediate { flags: _, offset }, + Operator::I32Store16 { memarg } | Operator::I64Store16 { memarg } => { + translate_store(memarg, ir::Opcode::Istore16, builder, state, environ)?; } - | Operator::F64Store { - memarg: MemoryImmediate { flags: _, offset }, - } => { - translate_store(*offset, ir::Opcode::Store, builder, state, environ)?; + Operator::I64Store32 { memarg } => { + translate_store(memarg, ir::Opcode::Istore32, builder, state, environ)?; } - Operator::I32Store8 { - memarg: MemoryImmediate { flags: _, offset }, - } - | Operator::I64Store8 { - memarg: MemoryImmediate { flags: _, offset }, - } => { - translate_store(*offset, ir::Opcode::Istore8, builder, state, environ)?; - } - Operator::I32Store16 { - memarg: MemoryImmediate { flags: _, offset }, - } - | Operator::I64Store16 { - memarg: MemoryImmediate { flags: _, offset }, - } => { - translate_store(*offset, ir::Opcode::Istore16, builder, state, environ)?; - } - Operator::I64Store32 { - memarg: MemoryImmediate { flags: _, offset }, - } => { - translate_store(*offset, ir::Opcode::Istore32, builder, state, environ)?; - } - Operator::V128Store { - memarg: MemoryImmediate { flags: _, offset }, - } => { - translate_store(*offset, ir::Opcode::Store, builder, state, environ)?; + Operator::V128Store { memarg } => { + translate_store(memarg, ir::Opcode::Store, builder, state, environ)?; } /****************************** Nullary Operators ************************************/ Operator::I32Const { value } => state.push1(builder.ins().iconst(I32, i64::from(*value))), @@ -1054,13 +992,13 @@ pub fn translate_operator( let index = FuncIndex::from_u32(*function_index); state.push1(environ.translate_ref_func(builder.cursor(), index)?); } - Operator::I32AtomicWait { .. } | Operator::I64AtomicWait { .. } => { + Operator::MemoryAtomicWait32 { .. } | Operator::MemoryAtomicWait64 { .. } => { // The WebAssembly MVP only supports one linear memory and // wasmparser will ensure that the memory indices specified are // zero. let implied_ty = match op { - Operator::I64AtomicWait { .. } => I64, - Operator::I32AtomicWait { .. } => I32, + Operator::MemoryAtomicWait64 { .. } => I64, + Operator::MemoryAtomicWait32 { .. } => I32, _ => unreachable!(), }; let heap_index = MemoryIndex::from_u32(0); @@ -1081,7 +1019,7 @@ pub fn translate_operator( )?; state.push1(res); } - Operator::AtomicNotify { .. } => { + Operator::MemoryAtomicNotify { .. } => { // The WebAssembly MVP only supports one linear memory and // wasmparser will ensure that the memory indices specified are // zero. @@ -1093,275 +1031,230 @@ pub fn translate_operator( environ.translate_atomic_notify(builder.cursor(), heap_index, heap, addr, count)?; state.push1(res); } - Operator::I32AtomicLoad { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_load(I32, I32, *offset, builder, state, environ)?, - Operator::I64AtomicLoad { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_load(I64, I64, *offset, builder, state, environ)?, - Operator::I32AtomicLoad8U { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_load(I32, I8, *offset, builder, state, environ)?, - Operator::I32AtomicLoad16U { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_load(I32, I16, *offset, builder, state, environ)?, - Operator::I64AtomicLoad8U { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_load(I64, I8, *offset, builder, state, environ)?, - Operator::I64AtomicLoad16U { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_load(I64, I16, *offset, builder, state, environ)?, - Operator::I64AtomicLoad32U { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_load(I64, I32, *offset, builder, state, environ)?, - - Operator::I32AtomicStore { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_store(I32, *offset, builder, state, environ)?, - Operator::I64AtomicStore { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_store(I64, *offset, builder, state, environ)?, - Operator::I32AtomicStore8 { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_store(I8, *offset, builder, state, environ)?, - Operator::I32AtomicStore16 { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_store(I16, *offset, builder, state, environ)?, - Operator::I64AtomicStore8 { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_store(I8, *offset, builder, state, environ)?, - Operator::I64AtomicStore16 { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_store(I16, *offset, builder, state, environ)?, - Operator::I64AtomicStore32 { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_store(I32, *offset, builder, state, environ)?, - - Operator::I32AtomicRmwAdd { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I32, I32, AtomicRmwOp::Add, *offset, builder, state, environ)?, - Operator::I64AtomicRmwAdd { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I64, AtomicRmwOp::Add, *offset, builder, state, environ)?, - Operator::I32AtomicRmw8AddU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I32, I8, AtomicRmwOp::Add, *offset, builder, state, environ)?, - Operator::I32AtomicRmw16AddU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I32, I16, AtomicRmwOp::Add, *offset, builder, state, environ)?, - Operator::I64AtomicRmw8AddU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I8, AtomicRmwOp::Add, *offset, builder, state, environ)?, - Operator::I64AtomicRmw16AddU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I16, AtomicRmwOp::Add, *offset, builder, state, environ)?, - Operator::I64AtomicRmw32AddU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I32, AtomicRmwOp::Add, *offset, builder, state, environ)?, - - Operator::I32AtomicRmwSub { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I32, I32, AtomicRmwOp::Sub, *offset, builder, state, environ)?, - Operator::I64AtomicRmwSub { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I64, AtomicRmwOp::Sub, *offset, builder, state, environ)?, - Operator::I32AtomicRmw8SubU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I32, I8, AtomicRmwOp::Sub, *offset, builder, state, environ)?, - Operator::I32AtomicRmw16SubU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I32, I16, AtomicRmwOp::Sub, *offset, builder, state, environ)?, - Operator::I64AtomicRmw8SubU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I8, AtomicRmwOp::Sub, *offset, builder, state, environ)?, - Operator::I64AtomicRmw16SubU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I16, AtomicRmwOp::Sub, *offset, builder, state, environ)?, - Operator::I64AtomicRmw32SubU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I32, AtomicRmwOp::Sub, *offset, builder, state, environ)?, - - Operator::I32AtomicRmwAnd { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I32, I32, AtomicRmwOp::And, *offset, builder, state, environ)?, - Operator::I64AtomicRmwAnd { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I64, AtomicRmwOp::And, *offset, builder, state, environ)?, - Operator::I32AtomicRmw8AndU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I32, I8, AtomicRmwOp::And, *offset, builder, state, environ)?, - Operator::I32AtomicRmw16AndU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I32, I16, AtomicRmwOp::And, *offset, builder, state, environ)?, - Operator::I64AtomicRmw8AndU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I8, AtomicRmwOp::And, *offset, builder, state, environ)?, - Operator::I64AtomicRmw16AndU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I16, AtomicRmwOp::And, *offset, builder, state, environ)?, - Operator::I64AtomicRmw32AndU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I32, AtomicRmwOp::And, *offset, builder, state, environ)?, - - Operator::I32AtomicRmwOr { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I32, I32, AtomicRmwOp::Or, *offset, builder, state, environ)?, - Operator::I64AtomicRmwOr { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I64, AtomicRmwOp::Or, *offset, builder, state, environ)?, - Operator::I32AtomicRmw8OrU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I32, I8, AtomicRmwOp::Or, *offset, builder, state, environ)?, - Operator::I32AtomicRmw16OrU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I32, I16, AtomicRmwOp::Or, *offset, builder, state, environ)?, - Operator::I64AtomicRmw8OrU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I8, AtomicRmwOp::Or, *offset, builder, state, environ)?, - Operator::I64AtomicRmw16OrU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I16, AtomicRmwOp::Or, *offset, builder, state, environ)?, - Operator::I64AtomicRmw32OrU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I32, AtomicRmwOp::Or, *offset, builder, state, environ)?, - - Operator::I32AtomicRmwXor { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I32, I32, AtomicRmwOp::Xor, *offset, builder, state, environ)?, - Operator::I64AtomicRmwXor { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I64, AtomicRmwOp::Xor, *offset, builder, state, environ)?, - Operator::I32AtomicRmw8XorU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I32, I8, AtomicRmwOp::Xor, *offset, builder, state, environ)?, - Operator::I32AtomicRmw16XorU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I32, I16, AtomicRmwOp::Xor, *offset, builder, state, environ)?, - Operator::I64AtomicRmw8XorU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I8, AtomicRmwOp::Xor, *offset, builder, state, environ)?, - Operator::I64AtomicRmw16XorU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I16, AtomicRmwOp::Xor, *offset, builder, state, environ)?, - Operator::I64AtomicRmw32XorU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I32, AtomicRmwOp::Xor, *offset, builder, state, environ)?, - - Operator::I32AtomicRmwXchg { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw( - I32, - I32, - AtomicRmwOp::Xchg, - *offset, - builder, - state, - environ, - )?, - Operator::I64AtomicRmwXchg { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw( - I64, - I64, - AtomicRmwOp::Xchg, - *offset, - builder, - state, - environ, - )?, - Operator::I32AtomicRmw8XchgU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I32, I8, AtomicRmwOp::Xchg, *offset, builder, state, environ)?, - Operator::I32AtomicRmw16XchgU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw( - I32, - I16, - AtomicRmwOp::Xchg, - *offset, - builder, - state, - environ, - )?, - Operator::I64AtomicRmw8XchgU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw(I64, I8, AtomicRmwOp::Xchg, *offset, builder, state, environ)?, - Operator::I64AtomicRmw16XchgU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw( - I64, - I16, - AtomicRmwOp::Xchg, - *offset, - builder, - state, - environ, - )?, - Operator::I64AtomicRmw32XchgU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_rmw( - I64, - I32, - AtomicRmwOp::Xchg, - *offset, - builder, - state, - environ, - )?, - - Operator::I32AtomicRmwCmpxchg { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_cas(I32, I32, *offset, builder, state, environ)?, - Operator::I64AtomicRmwCmpxchg { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_cas(I64, I64, *offset, builder, state, environ)?, - Operator::I32AtomicRmw8CmpxchgU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_cas(I32, I8, *offset, builder, state, environ)?, - Operator::I32AtomicRmw16CmpxchgU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_cas(I32, I16, *offset, builder, state, environ)?, - Operator::I64AtomicRmw8CmpxchgU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_cas(I64, I8, *offset, builder, state, environ)?, - Operator::I64AtomicRmw16CmpxchgU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_cas(I64, I16, *offset, builder, state, environ)?, - Operator::I64AtomicRmw32CmpxchgU { - memarg: MemoryImmediate { flags: _, offset }, - } => translate_atomic_cas(I64, I32, *offset, builder, state, environ)?, + Operator::I32AtomicLoad { memarg } => { + translate_atomic_load(I32, I32, memarg, builder, state, environ)? + } + Operator::I64AtomicLoad { memarg } => { + translate_atomic_load(I64, I64, memarg, builder, state, environ)? + } + Operator::I32AtomicLoad8U { memarg } => { + translate_atomic_load(I32, I8, memarg, builder, state, environ)? + } + Operator::I32AtomicLoad16U { memarg } => { + translate_atomic_load(I32, I16, memarg, builder, state, environ)? + } + Operator::I64AtomicLoad8U { memarg } => { + translate_atomic_load(I64, I8, memarg, builder, state, environ)? + } + Operator::I64AtomicLoad16U { memarg } => { + translate_atomic_load(I64, I16, memarg, builder, state, environ)? + } + Operator::I64AtomicLoad32U { memarg } => { + translate_atomic_load(I64, I32, memarg, builder, state, environ)? + } + + Operator::I32AtomicStore { memarg } => { + translate_atomic_store(I32, memarg, builder, state, environ)? + } + Operator::I64AtomicStore { memarg } => { + translate_atomic_store(I64, memarg, builder, state, environ)? + } + Operator::I32AtomicStore8 { memarg } => { + translate_atomic_store(I8, memarg, builder, state, environ)? + } + Operator::I32AtomicStore16 { memarg } => { + translate_atomic_store(I16, memarg, builder, state, environ)? + } + Operator::I64AtomicStore8 { memarg } => { + translate_atomic_store(I8, memarg, builder, state, environ)? + } + Operator::I64AtomicStore16 { memarg } => { + translate_atomic_store(I16, memarg, builder, state, environ)? + } + Operator::I64AtomicStore32 { memarg } => { + translate_atomic_store(I32, memarg, builder, state, environ)? + } + + Operator::I32AtomicRmwAdd { memarg } => { + translate_atomic_rmw(I32, I32, AtomicRmwOp::Add, memarg, builder, state, environ)? + } + Operator::I64AtomicRmwAdd { memarg } => { + translate_atomic_rmw(I64, I64, AtomicRmwOp::Add, memarg, builder, state, environ)? + } + Operator::I32AtomicRmw8AddU { memarg } => { + translate_atomic_rmw(I32, I8, AtomicRmwOp::Add, memarg, builder, state, environ)? + } + Operator::I32AtomicRmw16AddU { memarg } => { + translate_atomic_rmw(I32, I16, AtomicRmwOp::Add, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw8AddU { memarg } => { + translate_atomic_rmw(I64, I8, AtomicRmwOp::Add, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw16AddU { memarg } => { + translate_atomic_rmw(I64, I16, AtomicRmwOp::Add, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw32AddU { memarg } => { + translate_atomic_rmw(I64, I32, AtomicRmwOp::Add, memarg, builder, state, environ)? + } + + Operator::I32AtomicRmwSub { memarg } => { + translate_atomic_rmw(I32, I32, AtomicRmwOp::Sub, memarg, builder, state, environ)? + } + Operator::I64AtomicRmwSub { memarg } => { + translate_atomic_rmw(I64, I64, AtomicRmwOp::Sub, memarg, builder, state, environ)? + } + Operator::I32AtomicRmw8SubU { memarg } => { + translate_atomic_rmw(I32, I8, AtomicRmwOp::Sub, memarg, builder, state, environ)? + } + Operator::I32AtomicRmw16SubU { memarg } => { + translate_atomic_rmw(I32, I16, AtomicRmwOp::Sub, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw8SubU { memarg } => { + translate_atomic_rmw(I64, I8, AtomicRmwOp::Sub, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw16SubU { memarg } => { + translate_atomic_rmw(I64, I16, AtomicRmwOp::Sub, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw32SubU { memarg } => { + translate_atomic_rmw(I64, I32, AtomicRmwOp::Sub, memarg, builder, state, environ)? + } + + Operator::I32AtomicRmwAnd { memarg } => { + translate_atomic_rmw(I32, I32, AtomicRmwOp::And, memarg, builder, state, environ)? + } + Operator::I64AtomicRmwAnd { memarg } => { + translate_atomic_rmw(I64, I64, AtomicRmwOp::And, memarg, builder, state, environ)? + } + Operator::I32AtomicRmw8AndU { memarg } => { + translate_atomic_rmw(I32, I8, AtomicRmwOp::And, memarg, builder, state, environ)? + } + Operator::I32AtomicRmw16AndU { memarg } => { + translate_atomic_rmw(I32, I16, AtomicRmwOp::And, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw8AndU { memarg } => { + translate_atomic_rmw(I64, I8, AtomicRmwOp::And, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw16AndU { memarg } => { + translate_atomic_rmw(I64, I16, AtomicRmwOp::And, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw32AndU { memarg } => { + translate_atomic_rmw(I64, I32, AtomicRmwOp::And, memarg, builder, state, environ)? + } + + Operator::I32AtomicRmwOr { memarg } => { + translate_atomic_rmw(I32, I32, AtomicRmwOp::Or, memarg, builder, state, environ)? + } + Operator::I64AtomicRmwOr { memarg } => { + translate_atomic_rmw(I64, I64, AtomicRmwOp::Or, memarg, builder, state, environ)? + } + Operator::I32AtomicRmw8OrU { memarg } => { + translate_atomic_rmw(I32, I8, AtomicRmwOp::Or, memarg, builder, state, environ)? + } + Operator::I32AtomicRmw16OrU { memarg } => { + translate_atomic_rmw(I32, I16, AtomicRmwOp::Or, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw8OrU { memarg } => { + translate_atomic_rmw(I64, I8, AtomicRmwOp::Or, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw16OrU { memarg } => { + translate_atomic_rmw(I64, I16, AtomicRmwOp::Or, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw32OrU { memarg } => { + translate_atomic_rmw(I64, I32, AtomicRmwOp::Or, memarg, builder, state, environ)? + } + + Operator::I32AtomicRmwXor { memarg } => { + translate_atomic_rmw(I32, I32, AtomicRmwOp::Xor, memarg, builder, state, environ)? + } + Operator::I64AtomicRmwXor { memarg } => { + translate_atomic_rmw(I64, I64, AtomicRmwOp::Xor, memarg, builder, state, environ)? + } + Operator::I32AtomicRmw8XorU { memarg } => { + translate_atomic_rmw(I32, I8, AtomicRmwOp::Xor, memarg, builder, state, environ)? + } + Operator::I32AtomicRmw16XorU { memarg } => { + translate_atomic_rmw(I32, I16, AtomicRmwOp::Xor, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw8XorU { memarg } => { + translate_atomic_rmw(I64, I8, AtomicRmwOp::Xor, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw16XorU { memarg } => { + translate_atomic_rmw(I64, I16, AtomicRmwOp::Xor, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw32XorU { memarg } => { + translate_atomic_rmw(I64, I32, AtomicRmwOp::Xor, memarg, builder, state, environ)? + } + + Operator::I32AtomicRmwXchg { memarg } => { + translate_atomic_rmw(I32, I32, AtomicRmwOp::Xchg, memarg, builder, state, environ)? + } + Operator::I64AtomicRmwXchg { memarg } => { + translate_atomic_rmw(I64, I64, AtomicRmwOp::Xchg, memarg, builder, state, environ)? + } + Operator::I32AtomicRmw8XchgU { memarg } => { + translate_atomic_rmw(I32, I8, AtomicRmwOp::Xchg, memarg, builder, state, environ)? + } + Operator::I32AtomicRmw16XchgU { memarg } => { + translate_atomic_rmw(I32, I16, AtomicRmwOp::Xchg, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw8XchgU { memarg } => { + translate_atomic_rmw(I64, I8, AtomicRmwOp::Xchg, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw16XchgU { memarg } => { + translate_atomic_rmw(I64, I16, AtomicRmwOp::Xchg, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw32XchgU { memarg } => { + translate_atomic_rmw(I64, I32, AtomicRmwOp::Xchg, memarg, builder, state, environ)? + } + + Operator::I32AtomicRmwCmpxchg { memarg } => { + translate_atomic_cas(I32, I32, memarg, builder, state, environ)? + } + Operator::I64AtomicRmwCmpxchg { memarg } => { + translate_atomic_cas(I64, I64, memarg, builder, state, environ)? + } + Operator::I32AtomicRmw8CmpxchgU { memarg } => { + translate_atomic_cas(I32, I8, memarg, builder, state, environ)? + } + Operator::I32AtomicRmw16CmpxchgU { memarg } => { + translate_atomic_cas(I32, I16, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw8CmpxchgU { memarg } => { + translate_atomic_cas(I64, I8, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw16CmpxchgU { memarg } => { + translate_atomic_cas(I64, I16, memarg, builder, state, environ)? + } + Operator::I64AtomicRmw32CmpxchgU { memarg } => { + translate_atomic_cas(I64, I32, memarg, builder, state, environ)? + } Operator::AtomicFence { .. } => { builder.ins().fence(); } - Operator::MemoryCopy => { + Operator::MemoryCopy { src, dst } => { // The WebAssembly MVP only supports one linear memory and // wasmparser will ensure that the memory indices specified are // zero. - let heap_index = MemoryIndex::from_u32(0); - let heap = state.get_heap(builder.func, 0, environ)?; + assert_eq!(src, dst, "unimplemented between-memories copy"); + let heap_index = MemoryIndex::from_u32(*src); + let heap = state.get_heap(builder.func, *src, environ)?; let len = state.pop1(); let src = state.pop1(); let dest = state.pop1(); environ.translate_memory_copy(builder.cursor(), heap_index, heap, dest, src, len)?; } - Operator::MemoryFill => { - // The WebAssembly MVP only supports one linear memory and - // wasmparser will ensure that the memory index specified is - // zero. - let heap_index = MemoryIndex::from_u32(0); - let heap = state.get_heap(builder.func, 0, environ)?; + Operator::MemoryFill { mem } => { + let heap_index = MemoryIndex::from_u32(*mem); + let heap = state.get_heap(builder.func, *mem, environ)?; let len = state.pop1(); let val = state.pop1(); let dest = state.pop1(); environ.translate_memory_fill(builder.cursor(), heap_index, heap, dest, val, len)?; } - Operator::MemoryInit { segment } => { - // The WebAssembly MVP only supports one linear memory and - // wasmparser will ensure that the memory index specified is - // zero. - let heap_index = MemoryIndex::from_u32(0); - let heap = state.get_heap(builder.func, 0, environ)?; + Operator::MemoryInit { segment, mem } => { + let heap_index = MemoryIndex::from_u32(*mem); + let heap = state.get_heap(builder.func, *mem, environ)?; let len = state.pop1(); let src = state.pop1(); let dest = state.pop1(); @@ -1479,23 +1372,15 @@ pub fn translate_operator( let splatted = builder.ins().splat(type_of(op), state.pop1()); state.push1(splatted) } - Operator::V8x16LoadSplat { - memarg: MemoryImmediate { flags: _, offset }, - } - | Operator::V16x8LoadSplat { - memarg: MemoryImmediate { flags: _, offset }, - } - | Operator::V32x4LoadSplat { - memarg: MemoryImmediate { flags: _, offset }, - } - | Operator::V64x2LoadSplat { - memarg: MemoryImmediate { flags: _, offset }, - } => { + Operator::V128Load8Splat { memarg } + | Operator::V128Load16Splat { memarg } + | Operator::V128Load32Splat { memarg } + | Operator::V128Load64Splat { memarg } => { // TODO: For spec compliance, this is initially implemented as a combination of `load + // splat` but could be implemented eventually as a single instruction (`load_splat`). // See https://github.com/bytecodealliance/wasmtime/issues/1175. translate_load( - *offset, + memarg, ir::Opcode::Load, type_of(op).lane_type(), builder, @@ -1540,7 +1425,7 @@ pub fn translate_operator( let vector = optionally_bitcast_vector(vector, type_of(op), builder); state.push1(builder.ins().insertlane(vector, replacement, *lane)) } - Operator::V8x16Shuffle { lanes, .. } => { + Operator::I8x16Shuffle { lanes, .. } => { let (a, b) = pop2_with_bitcast(state, I8X16, builder); let lanes = ConstantData::from(lanes.as_ref()); let mask = builder.func.dfg.immediates.push(lanes); @@ -1551,7 +1436,7 @@ pub fn translate_operator( // to WASM using the less specific v128 type for certain operations and more specific // types (e.g. i8x16) for others. } - Operator::V8x16Swizzle => { + Operator::I8x16Swizzle => { let (a, b) = pop2_with_bitcast(state, I8X16, builder); state.push1(builder.ins().swizzle(I8X16, a, b)) } @@ -1559,11 +1444,11 @@ pub fn translate_operator( let (a, b) = pop2_with_bitcast(state, type_of(op), builder); state.push1(builder.ins().iadd(a, b)) } - Operator::I8x16AddSaturateS | Operator::I16x8AddSaturateS => { + Operator::I8x16AddSatS | Operator::I16x8AddSatS => { let (a, b) = pop2_with_bitcast(state, type_of(op), builder); state.push1(builder.ins().sadd_sat(a, b)) } - Operator::I8x16AddSaturateU | Operator::I16x8AddSaturateU => { + Operator::I8x16AddSatU | Operator::I16x8AddSatU => { let (a, b) = pop2_with_bitcast(state, type_of(op), builder); state.push1(builder.ins().uadd_sat(a, b)) } @@ -1571,11 +1456,11 @@ pub fn translate_operator( let (a, b) = pop2_with_bitcast(state, type_of(op), builder); state.push1(builder.ins().isub(a, b)) } - Operator::I8x16SubSaturateS | Operator::I16x8SubSaturateS => { + Operator::I8x16SubSatS | Operator::I16x8SubSatS => { let (a, b) = pop2_with_bitcast(state, type_of(op), builder); state.push1(builder.ins().ssub_sat(a, b)) } - Operator::I8x16SubSaturateU | Operator::I16x8SubSaturateU => { + Operator::I8x16SubSatU | Operator::I16x8SubSatU => { let (a, b) = pop2_with_bitcast(state, type_of(op), builder); state.push1(builder.ins().usub_sat(a, b)) } @@ -1828,7 +1713,22 @@ pub fn translate_operator( let a = pop1_with_bitcast(state, I16X8, builder); state.push1(builder.ins().uwiden_high(a)) } - Operator::I8x16Bitmask | Operator::I16x8Bitmask | Operator::I32x4Bitmask => { + + Operator::F32x4Ceil + | Operator::F32x4Floor + | Operator::F32x4Trunc + | Operator::F32x4Nearest + | Operator::F32x4PMin + | Operator::F32x4PMax + | Operator::F64x2Ceil + | Operator::F64x2Floor + | Operator::F64x2Trunc + | Operator::F64x2PMin + | Operator::F64x2PMax + | Operator::F64x2Nearest + | Operator::I8x16Bitmask + | Operator::I16x8Bitmask + | Operator::I32x4Bitmask => { return Err(wasm_unsupported!("proposed SIMD operator {:?}", op)); } @@ -1845,7 +1745,7 @@ pub fn translate_operator( /// are dropped but special ones like `End` or `Else` signal the potential end of the unreachable /// portion so the translation state must be updated accordingly. fn translate_unreachable_operator( - module_translation_state: &ModuleTranslationState, + validator: &FuncValidator, op: &Operator, builder: &mut FunctionBuilder, state: &mut FuncTranslationState, @@ -1889,7 +1789,7 @@ fn translate_unreachable_operator( let else_block = match *else_data { ElseData::NoElse { branch_inst } => { let (params, _results) = - blocktype_params_results(module_translation_state, blocktype)?; + blocktype_params_results(validator, blocktype)?; let else_block = block_with_params(builder, params, environ)?; let frame = state.control_stack.last().unwrap(); frame.truncate_value_stack_to_else_params(&mut state.stack); @@ -2056,7 +1956,7 @@ fn get_heap_addr( /// Prepare for a load; factors out common functionality between load and load_extend operations. fn prepare_load( - offset: u32, + memarg: &MemoryImmediate, loaded_bytes: u32, builder: &mut FunctionBuilder, state: &mut FuncTranslationState, @@ -2064,12 +1964,11 @@ fn prepare_load( ) -> WasmResult<(MemFlags, Value, Offset32)> { let addr32 = state.pop1(); - // We don't yet support multiple linear memories. - let heap = state.get_heap(builder.func, 0, environ)?; + let heap = state.get_heap(builder.func, memarg.memory, environ)?; let (base, offset) = get_heap_addr( heap, addr32, - offset, + memarg.offset, loaded_bytes, environ.pointer_type(), builder, @@ -2085,7 +1984,7 @@ fn prepare_load( /// Translate a load instruction. fn translate_load( - offset: u32, + memarg: &MemoryImmediate, opcode: ir::Opcode, result_ty: Type, builder: &mut FunctionBuilder, @@ -2093,7 +1992,7 @@ fn translate_load( environ: &mut FE, ) -> WasmResult<()> { let (flags, base, offset) = prepare_load( - offset, + memarg, mem_op_size(opcode, result_ty), builder, state, @@ -2106,7 +2005,7 @@ fn translate_load( /// Translate a store instruction. fn translate_store( - offset: u32, + memarg: &MemoryImmediate, opcode: ir::Opcode, builder: &mut FunctionBuilder, state: &mut FuncTranslationState, @@ -2115,12 +2014,11 @@ fn translate_store( let (addr32, val) = state.pop2(); let val_ty = builder.func.dfg.value_type(val); - // We don't yet support multiple linear memories. - let heap = state.get_heap(builder.func, 0, environ)?; + let heap = state.get_heap(builder.func, memarg.memory, environ)?; let (base, offset) = get_heap_addr( heap, addr32, - offset, + memarg.offset, mem_op_size(opcode, val_ty), environ.pointer_type(), builder, @@ -2153,7 +2051,7 @@ fn translate_icmp(cc: IntCC, builder: &mut FunctionBuilder, state: &mut FuncTran // and then compute the final effective address. fn finalise_atomic_mem_addr( linear_mem_addr: Value, - offset: u32, + memarg: &MemoryImmediate, access_ty: Type, builder: &mut FunctionBuilder, state: &mut FuncTranslationState, @@ -2161,7 +2059,9 @@ fn finalise_atomic_mem_addr( ) -> WasmResult { // Check the alignment of `linear_mem_addr`. let access_ty_bytes = access_ty.bytes(); - let final_lma = builder.ins().iadd_imm(linear_mem_addr, i64::from(offset)); + let final_lma = builder + .ins() + .iadd_imm(linear_mem_addr, i64::from(memarg.offset)); if access_ty_bytes != 1 { assert!(access_ty_bytes == 2 || access_ty_bytes == 4 || access_ty_bytes == 8); let final_lma_misalignment = builder @@ -2175,8 +2075,8 @@ fn finalise_atomic_mem_addr( .trapif(IntCC::NotEqual, f, ir::TrapCode::HeapMisaligned); } - // Compute the final effective address. Note, we don't yet support multiple linear memories. - let heap = state.get_heap(builder.func, 0, environ)?; + // Compute the final effective address. + let heap = state.get_heap(builder.func, memarg.memory, environ)?; let (base, offset) = get_heap_addr( heap, final_lma, @@ -2194,7 +2094,7 @@ fn translate_atomic_rmw( widened_ty: Type, access_ty: Type, op: AtomicRmwOp, - offset: u32, + memarg: &MemoryImmediate, builder: &mut FunctionBuilder, state: &mut FuncTranslationState, environ: &mut FE, @@ -2225,7 +2125,7 @@ fn translate_atomic_rmw( } let final_effective_address = - finalise_atomic_mem_addr(linear_mem_addr, offset, access_ty, builder, state, environ)?; + finalise_atomic_mem_addr(linear_mem_addr, memarg, access_ty, builder, state, environ)?; // See the comments in `prepare_load` about the flags. let flags = MemFlags::new(); @@ -2242,7 +2142,7 @@ fn translate_atomic_rmw( fn translate_atomic_cas( widened_ty: Type, access_ty: Type, - offset: u32, + memarg: &MemoryImmediate, builder: &mut FunctionBuilder, state: &mut FuncTranslationState, environ: &mut FE, @@ -2278,7 +2178,7 @@ fn translate_atomic_cas( } let final_effective_address = - finalise_atomic_mem_addr(linear_mem_addr, offset, access_ty, builder, state, environ)?; + finalise_atomic_mem_addr(linear_mem_addr, memarg, access_ty, builder, state, environ)?; // See the comments in `prepare_load` about the flags. let flags = MemFlags::new(); @@ -2295,7 +2195,7 @@ fn translate_atomic_cas( fn translate_atomic_load( widened_ty: Type, access_ty: Type, - offset: u32, + memarg: &MemoryImmediate, builder: &mut FunctionBuilder, state: &mut FuncTranslationState, environ: &mut FE, @@ -2320,7 +2220,7 @@ fn translate_atomic_load( assert!(w_ty_ok && widened_ty.bytes() >= access_ty.bytes()); let final_effective_address = - finalise_atomic_mem_addr(linear_mem_addr, offset, access_ty, builder, state, environ)?; + finalise_atomic_mem_addr(linear_mem_addr, memarg, access_ty, builder, state, environ)?; // See the comments in `prepare_load` about the flags. let flags = MemFlags::new(); @@ -2336,7 +2236,7 @@ fn translate_atomic_load( fn translate_atomic_store( access_ty: Type, - offset: u32, + memarg: &MemoryImmediate, builder: &mut FunctionBuilder, state: &mut FuncTranslationState, environ: &mut FE, @@ -2366,7 +2266,7 @@ fn translate_atomic_store( } let final_effective_address = - finalise_atomic_mem_addr(linear_mem_addr, offset, access_ty, builder, state, environ)?; + finalise_atomic_mem_addr(linear_mem_addr, memarg, access_ty, builder, state, environ)?; // See the comments in `prepare_load` about the flags. let flags = MemFlags::new(); @@ -2460,9 +2360,9 @@ fn type_of(operator: &Operator) -> Type { | Operator::V128Xor | Operator::V128Bitselect => I8X16, // default type representing V128 - Operator::V8x16Shuffle { .. } + Operator::I8x16Shuffle { .. } | Operator::I8x16Splat - | Operator::V8x16LoadSplat { .. } + | Operator::V128Load8Splat { .. } | Operator::I8x16ExtractLaneS { .. } | Operator::I8x16ExtractLaneU { .. } | Operator::I8x16ReplaceLane { .. } @@ -2484,11 +2384,11 @@ fn type_of(operator: &Operator) -> Type { | Operator::I8x16ShrS | Operator::I8x16ShrU | Operator::I8x16Add - | Operator::I8x16AddSaturateS - | Operator::I8x16AddSaturateU + | Operator::I8x16AddSatS + | Operator::I8x16AddSatU | Operator::I8x16Sub - | Operator::I8x16SubSaturateS - | Operator::I8x16SubSaturateU + | Operator::I8x16SubSatS + | Operator::I8x16SubSatU | Operator::I8x16MinS | Operator::I8x16MinU | Operator::I8x16MaxS @@ -2497,7 +2397,7 @@ fn type_of(operator: &Operator) -> Type { | Operator::I8x16Bitmask => I8X16, Operator::I16x8Splat - | Operator::V16x8LoadSplat { .. } + | Operator::V128Load16Splat { .. } | Operator::I16x8ExtractLaneS { .. } | Operator::I16x8ExtractLaneU { .. } | Operator::I16x8ReplaceLane { .. } @@ -2519,11 +2419,11 @@ fn type_of(operator: &Operator) -> Type { | Operator::I16x8ShrS | Operator::I16x8ShrU | Operator::I16x8Add - | Operator::I16x8AddSaturateS - | Operator::I16x8AddSaturateU + | Operator::I16x8AddSatS + | Operator::I16x8AddSatU | Operator::I16x8Sub - | Operator::I16x8SubSaturateS - | Operator::I16x8SubSaturateU + | Operator::I16x8SubSatS + | Operator::I16x8SubSatU | Operator::I16x8MinS | Operator::I16x8MinU | Operator::I16x8MaxS @@ -2533,7 +2433,7 @@ fn type_of(operator: &Operator) -> Type { | Operator::I16x8Bitmask => I16X8, Operator::I32x4Splat - | Operator::V32x4LoadSplat { .. } + | Operator::V128Load32Splat { .. } | Operator::I32x4ExtractLane { .. } | Operator::I32x4ReplaceLane { .. } | Operator::I32x4Eq @@ -2565,7 +2465,7 @@ fn type_of(operator: &Operator) -> Type { | Operator::I32x4Bitmask => I32X4, Operator::I64x2Splat - | Operator::V64x2LoadSplat { .. } + | Operator::V128Load64Splat { .. } | Operator::I64x2ExtractLane { .. } | Operator::I64x2ReplaceLane { .. } | Operator::I64x2Neg diff --git a/third_party/rust/cranelift-wasm/src/environ/dummy.rs b/third_party/rust/cranelift-wasm/src/environ/dummy.rs index 8bf4e0b1f9a13..ae87ec5727f3b 100644 --- a/third_party/rust/cranelift-wasm/src/environ/dummy.rs +++ b/third_party/rust/cranelift-wasm/src/environ/dummy.rs @@ -10,7 +10,6 @@ use crate::environ::{ WasmFuncType, WasmResult, }; use crate::func_translator::FuncTranslator; -use crate::state::ModuleTranslationState; use crate::translation_utils::{ DataIndex, DefinedFuncIndex, ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table, TableIndex, @@ -26,6 +25,7 @@ use cranelift_frontend::FunctionBuilder; use std::boxed::Box; use std::string::String; use std::vec::Vec; +use wasmparser::{FuncValidator, FunctionBody, ValidatorResources, WasmFeatures}; /// Compute a `ir::ExternalName` for a given wasm function index. fn get_func_name(func_index: FuncIndex) -> ir::ExternalName { @@ -738,10 +738,11 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment { fn define_function_body( &mut self, - module_translation_state: &ModuleTranslationState, - body_bytes: &'data [u8], - body_offset: usize, + mut validator: FuncValidator, + body: FunctionBody<'data>, ) -> WasmResult<()> { + self.func_bytecode_sizes + .push(body.get_binary_reader().bytes_remaining()); let func = { let mut func_environ = DummyFuncEnvironment::new(&self.info, self.return_mode); let func_index = @@ -752,16 +753,10 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment { if self.debug_info { func.collect_debug_info(); } - self.trans.translate( - module_translation_state, - body_bytes, - body_offset, - &mut func, - &mut func_environ, - )?; + self.trans + .translate_body(&mut validator, body, &mut func, &mut func_environ)?; func }; - self.func_bytecode_sizes.push(body_bytes.len()); self.info.function_bodies.push(func); Ok(()) } @@ -773,4 +768,14 @@ impl<'data> ModuleEnvironment<'data> for DummyEnvironment { fn declare_func_name(&mut self, func_index: FuncIndex, name: &'data str) { self.function_names[func_index] = String::from(name); } + + fn wasm_features(&self) -> WasmFeatures { + WasmFeatures { + multi_value: true, + simd: true, + reference_types: true, + bulk_memory: true, + ..WasmFeatures::default() + } + } } diff --git a/third_party/rust/cranelift-wasm/src/environ/spec.rs b/third_party/rust/cranelift-wasm/src/environ/spec.rs index 045d6e2f29f3f..47fbb9d2c31d4 100644 --- a/third_party/rust/cranelift-wasm/src/environ/spec.rs +++ b/third_party/rust/cranelift-wasm/src/environ/spec.rs @@ -6,7 +6,7 @@ //! //! [Wasmtime]: https://github.com/bytecodealliance/wasmtime -use crate::state::{FuncTranslationState, ModuleTranslationState}; +use crate::state::FuncTranslationState; use crate::translation_utils::{ DataIndex, ElemIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table, TableIndex, @@ -23,8 +23,8 @@ use serde::{Deserialize, Serialize}; use std::boxed::Box; use std::string::ToString; use thiserror::Error; -use wasmparser::BinaryReaderError; -use wasmparser::Operator; +use wasmparser::ValidatorResources; +use wasmparser::{BinaryReaderError, FuncValidator, FunctionBody, Operator, WasmFeatures}; /// WebAssembly value type -- equivalent of `wasmparser`'s Type. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -798,9 +798,8 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment { /// Provides the contents of a function body. fn define_function_body( &mut self, - module_translation_state: &ModuleTranslationState, - body_bytes: &'data [u8], - body_offset: usize, + validator: FuncValidator, + body: FunctionBody<'data>, ) -> WasmResult<()>; /// Provides the number of data initializers up front. By default this does nothing, but @@ -841,4 +840,9 @@ pub trait ModuleEnvironment<'data>: TargetEnvironment { fn custom_section(&mut self, _name: &'data str, _data: &'data [u8]) -> WasmResult<()> { Ok(()) } + + /// Returns the list of enabled wasm features this translation will be using. + fn wasm_features(&self) -> WasmFeatures { + WasmFeatures::default() + } } diff --git a/third_party/rust/cranelift-wasm/src/func_translator.rs b/third_party/rust/cranelift-wasm/src/func_translator.rs index f46b0f7fa0c24..158271ab9646c 100644 --- a/third_party/rust/cranelift-wasm/src/func_translator.rs +++ b/third_party/rust/cranelift-wasm/src/func_translator.rs @@ -6,7 +6,7 @@ use crate::code_translator::{bitcast_arguments, translate_operator, wasm_param_types}; use crate::environ::{FuncEnvironment, ReturnMode, WasmResult}; -use crate::state::{FuncTranslationState, ModuleTranslationState}; +use crate::state::FuncTranslationState; use crate::translation_utils::get_vmctx_value_label; use crate::wasm_unsupported; use core::convert::TryInto; @@ -14,7 +14,7 @@ use cranelift_codegen::entity::EntityRef; use cranelift_codegen::ir::{self, Block, InstBuilder, ValueLabel}; use cranelift_codegen::timing; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; -use wasmparser::{self, BinaryReader}; +use wasmparser::{self, BinaryReader, FuncValidator, FunctionBody, WasmModuleResources}; /// WebAssembly to Cranelift IR function translator. /// @@ -55,29 +55,30 @@ impl FuncTranslator { /// pub fn translate( &mut self, - module_translation_state: &ModuleTranslationState, + validator: &mut FuncValidator, code: &[u8], code_offset: usize, func: &mut ir::Function, environ: &mut FE, ) -> WasmResult<()> { - self.translate_from_reader( - module_translation_state, - BinaryReader::new_with_offset(code, code_offset), + self.translate_body( + validator, + FunctionBody::new(code_offset, code), func, environ, ) } - /// Translate a binary WebAssembly function from a `BinaryReader`. - pub fn translate_from_reader( + /// Translate a binary WebAssembly function from a `FunctionBody`. + pub fn translate_body( &mut self, - module_translation_state: &ModuleTranslationState, - mut reader: BinaryReader, + validator: &mut FuncValidator, + body: FunctionBody<'_>, func: &mut ir::Function, environ: &mut FE, ) -> WasmResult<()> { let _tt = timing::wasm_translate_function(); + let mut reader = body.get_binary_reader(); log::debug!( "translate({} bytes, {}{})", reader.bytes_remaining(), @@ -107,14 +108,8 @@ impl FuncTranslator { builder.append_block_params_for_function_returns(exit_block); self.state.initialize(&builder.func.signature, exit_block); - parse_local_decls(&mut reader, &mut builder, num_params, environ)?; - parse_function_body( - module_translation_state, - reader, - &mut builder, - &mut self.state, - environ, - )?; + parse_local_decls(&mut reader, &mut builder, num_params, environ, validator)?; + parse_function_body(validator, reader, &mut builder, &mut self.state, environ)?; builder.finalize(); Ok(()) @@ -161,14 +156,17 @@ fn parse_local_decls( builder: &mut FunctionBuilder, num_params: usize, environ: &mut FE, + validator: &mut FuncValidator, ) -> WasmResult<()> { let mut next_local = num_params; - let local_count = reader.read_local_count()?; + let local_count = reader.read_var_u32()?; - let mut locals_total = 0; for _ in 0..local_count { builder.set_srcloc(cur_srcloc(reader)); - let (count, ty) = reader.read_local_decl(&mut locals_total)?; + let pos = reader.original_position(); + let count = reader.read_var_u32()?; + let ty = reader.read_type()?; + validator.define_locals(pos, count, ty)?; declare_locals(builder, count, ty, &mut next_local, environ)?; } @@ -218,7 +216,7 @@ fn declare_locals( /// This assumes that the local variable declarations have already been parsed and function /// arguments and locals are declared in the builder. fn parse_function_body( - module_translation_state: &ModuleTranslationState, + validator: &mut FuncValidator, mut reader: BinaryReader, builder: &mut FunctionBuilder, state: &mut FuncTranslationState, @@ -227,14 +225,17 @@ fn parse_function_body( // The control stack is initialized with a single block representing the whole function. debug_assert_eq!(state.control_stack.len(), 1, "State not initialized"); - // Keep going until the final `End` operator which pops the outermost block. - while !state.control_stack.is_empty() { + while !reader.eof() { + let pos = reader.original_position(); builder.set_srcloc(cur_srcloc(&reader)); let op = reader.read_operator()?; + validator.op(pos, &op)?; environ.before_translate_operator(&op, builder, state)?; - translate_operator(module_translation_state, &op, builder, state, environ)?; + translate_operator(validator, &op, builder, state, environ)?; environ.after_translate_operator(&op, builder, state)?; } + let pos = reader.original_position(); + validator.finish(pos)?; // The final `End` operator left us in the exit block where we need to manually add a return // instruction. @@ -261,8 +262,6 @@ fn parse_function_body( // or the end of the function is unreachable. state.stack.clear(); - debug_assert!(reader.eof()); - Ok(()) } @@ -277,26 +276,27 @@ fn cur_srcloc(reader: &BinaryReader) -> ir::SourceLoc { mod tests { use super::{FuncTranslator, ReturnMode}; use crate::environ::DummyEnvironment; - use crate::ModuleTranslationState; use cranelift_codegen::ir::types::I32; use cranelift_codegen::{ir, isa, settings, Context}; use log::debug; use target_lexicon::PointerWidth; + use wasmparser::{ + FuncValidator, FunctionBody, Parser, ValidPayload, Validator, ValidatorResources, + }; #[test] fn small1() { // Implicit return. - // - // (func $small1 (param i32) (result i32) - // (i32.add (get_local 0) (i32.const 1)) - // ) - const BODY: [u8; 7] = [ - 0x00, // local decl count - 0x20, 0x00, // get_local 0 - 0x41, 0x01, // i32.const 1 - 0x6a, // i32.add - 0x0b, // end - ]; + let wasm = wat::parse_str( + " + (module + (func $small2 (param i32) (result i32) + (i32.add (get_local 0) (i32.const 1)) + ) + ) + ", + ) + .unwrap(); let mut trans = FuncTranslator::new(); let flags = settings::Flags::new(settings::builder()); @@ -309,21 +309,15 @@ mod tests { false, ); - let module_translation_state = ModuleTranslationState::new(); let mut ctx = Context::new(); ctx.func.name = ir::ExternalName::testcase("small1"); ctx.func.signature.params.push(ir::AbiParam::new(I32)); ctx.func.signature.returns.push(ir::AbiParam::new(I32)); + let (body, mut validator) = extract_func(&wasm); trans - .translate( - &module_translation_state, - &BODY, - 0, - &mut ctx.func, - &mut runtime.func_env(), - ) + .translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env()) .unwrap(); debug!("{}", ctx.func.display(None)); ctx.verify(&flags).unwrap(); @@ -332,18 +326,16 @@ mod tests { #[test] fn small2() { // Same as above, but with an explicit return instruction. - // - // (func $small2 (param i32) (result i32) - // (return (i32.add (get_local 0) (i32.const 1))) - // ) - const BODY: [u8; 8] = [ - 0x00, // local decl count - 0x20, 0x00, // get_local 0 - 0x41, 0x01, // i32.const 1 - 0x6a, // i32.add - 0x0f, // return - 0x0b, // end - ]; + let wasm = wat::parse_str( + " + (module + (func $small2 (param i32) (result i32) + (return (i32.add (get_local 0) (i32.const 1))) + ) + ) + ", + ) + .unwrap(); let mut trans = FuncTranslator::new(); let flags = settings::Flags::new(settings::builder()); @@ -356,21 +348,15 @@ mod tests { false, ); - let module_translation_state = ModuleTranslationState::new(); let mut ctx = Context::new(); ctx.func.name = ir::ExternalName::testcase("small2"); ctx.func.signature.params.push(ir::AbiParam::new(I32)); ctx.func.signature.returns.push(ir::AbiParam::new(I32)); + let (body, mut validator) = extract_func(&wasm); trans - .translate( - &module_translation_state, - &BODY, - 0, - &mut ctx.func, - &mut runtime.func_env(), - ) + .translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env()) .unwrap(); debug!("{}", ctx.func.display(None)); ctx.verify(&flags).unwrap(); @@ -379,27 +365,21 @@ mod tests { #[test] fn infloop() { // An infinite loop, no return instructions. - // - // (func $infloop (result i32) - // (local i32) - // (loop (result i32) - // (i32.add (get_local 0) (i32.const 1)) - // (set_local 0) - // (br 0) - // ) - // ) - const BODY: [u8; 16] = [ - 0x01, // 1 local decl. - 0x01, 0x7f, // 1 i32 local. - 0x03, 0x7f, // loop i32 - 0x20, 0x00, // get_local 0 - 0x41, 0x01, // i32.const 0 - 0x6a, // i32.add - 0x21, 0x00, // set_local 0 - 0x0c, 0x00, // br 0 - 0x0b, // end - 0x0b, // end - ]; + let wasm = wat::parse_str( + " + (module + (func $infloop (result i32) + (local i32) + (loop (result i32) + (i32.add (get_local 0) (i32.const 1)) + (set_local 0) + (br 0) + ) + ) + ) + ", + ) + .unwrap(); let mut trans = FuncTranslator::new(); let flags = settings::Flags::new(settings::builder()); @@ -412,22 +392,27 @@ mod tests { false, ); - let module_translation_state = ModuleTranslationState::new(); let mut ctx = Context::new(); ctx.func.name = ir::ExternalName::testcase("infloop"); ctx.func.signature.returns.push(ir::AbiParam::new(I32)); + let (body, mut validator) = extract_func(&wasm); trans - .translate( - &module_translation_state, - &BODY, - 0, - &mut ctx.func, - &mut runtime.func_env(), - ) + .translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env()) .unwrap(); debug!("{}", ctx.func.display(None)); ctx.verify(&flags).unwrap(); } + + fn extract_func(wat: &[u8]) -> (FunctionBody<'_>, FuncValidator) { + let mut validator = Validator::new(); + for payload in Parser::new(0).parse_all(wat) { + match validator.payload(&payload.unwrap()).unwrap() { + ValidPayload::Func(validator, body) => return (body, validator), + _ => {} + } + } + panic!("failed to find function"); + } } diff --git a/third_party/rust/cranelift-wasm/src/lib.rs b/third_party/rust/cranelift-wasm/src/lib.rs index 661bfe8dd4df3..cbc4a51861c6b 100644 --- a/third_party/rust/cranelift-wasm/src/lib.rs +++ b/third_party/rust/cranelift-wasm/src/lib.rs @@ -72,5 +72,10 @@ pub use crate::translation_utils::{ }; pub use cranelift_frontend::FunctionBuilder; +// Convenience reexport of the wasmparser crate that we're linking against, +// since a number of types in `wasmparser` show up in the public API of +// `cranelift-wasm`. +pub use wasmparser; + /// Version number of this crate. pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/third_party/rust/cranelift-wasm/src/module_translator.rs b/third_party/rust/cranelift-wasm/src/module_translator.rs index e3cd0b44bf895..89e6923fcf86c 100644 --- a/third_party/rust/cranelift-wasm/src/module_translator.rs +++ b/third_party/rust/cranelift-wasm/src/module_translator.rs @@ -8,7 +8,7 @@ use crate::sections_translator::{ }; use crate::state::ModuleTranslationState; use cranelift_codegen::timing; -use wasmparser::{NameSectionReader, Parser, Payload}; +use wasmparser::{NameSectionReader, Parser, Payload, Validator}; /// Translate a sequence of bytes forming a valid Wasm binary into a list of valid Cranelift IR /// [`Function`](cranelift_codegen::ir::Function). @@ -18,75 +18,105 @@ pub fn translate_module<'data>( ) -> WasmResult { let _tt = timing::wasm_translate_module(); let mut module_translation_state = ModuleTranslationState::new(); + let mut validator = Validator::new(); + validator.wasm_features(environ.wasm_features()); for payload in Parser::new(0).parse_all(data) { match payload? { - Payload::Version { .. } | Payload::End => {} + Payload::Version { num, range } => { + validator.version(num, &range)?; + } + Payload::End => { + validator.end()?; + } Payload::TypeSection(types) => { + validator.type_section(&types)?; parse_type_section(types, &mut module_translation_state, environ)?; } Payload::ImportSection(imports) => { + validator.import_section(&imports)?; parse_import_section(imports, environ)?; } Payload::FunctionSection(functions) => { + validator.function_section(&functions)?; parse_function_section(functions, environ)?; } Payload::TableSection(tables) => { + validator.table_section(&tables)?; parse_table_section(tables, environ)?; } Payload::MemorySection(memories) => { + validator.memory_section(&memories)?; parse_memory_section(memories, environ)?; } Payload::GlobalSection(globals) => { + validator.global_section(&globals)?; parse_global_section(globals, environ)?; } Payload::ExportSection(exports) => { + validator.export_section(&exports)?; parse_export_section(exports, environ)?; } - Payload::StartSection { func, .. } => { + Payload::StartSection { func, range } => { + validator.start_section(func, &range)?; parse_start_section(func, environ)?; } Payload::ElementSection(elements) => { + validator.element_section(&elements)?; parse_element_section(elements, environ)?; } Payload::CodeSectionStart { count, range, .. } => { + validator.code_section_start(count, &range)?; environ.reserve_function_bodies(count, range.start as u64); } - Payload::CodeSectionEntry(code) => { - let mut code = code.get_binary_reader(); - let size = code.bytes_remaining(); - let offset = code.original_position(); - environ.define_function_body( - &module_translation_state, - code.read_bytes(size)?, - offset, - )?; + Payload::CodeSectionEntry(body) => { + let func_validator = validator.code_section_entry()?; + environ.define_function_body(func_validator, body)?; } Payload::DataSection(data) => { + validator.data_section(&data)?; parse_data_section(data, environ)?; } - Payload::DataCountSection { count, .. } => { + Payload::DataCountSection { count, range } => { + validator.data_count_section(count, &range)?; environ.reserve_passive_data(count)?; } - Payload::ModuleSection(_) - | Payload::InstanceSection(_) - | Payload::AliasSection(_) - | Payload::ModuleCodeSectionStart { .. } - | Payload::ModuleCodeSectionEntry { .. } => { + Payload::ModuleSection(s) => { + validator.module_section(&s)?; + unimplemented!("module linking not implemented yet") + } + Payload::InstanceSection(s) => { + validator.instance_section(&s)?; + unimplemented!("module linking not implemented yet") + } + Payload::AliasSection(s) => { + validator.alias_section(&s)?; + unimplemented!("module linking not implemented yet") + } + Payload::ModuleCodeSectionStart { + count, + range, + size: _, + } => { + validator.module_code_section_start(count, &range)?; + unimplemented!("module linking not implemented yet") + } + + Payload::ModuleCodeSectionEntry { .. } => { unimplemented!("module linking not implemented yet") } @@ -105,7 +135,10 @@ pub fn translate_module<'data>( Payload::CustomSection { name, data, .. } => environ.custom_section(name, data)?, - Payload::UnknownSection { .. } => unreachable!(), + Payload::UnknownSection { id, range, .. } => { + validator.unknown_section(id, &range)?; + unreachable!(); + } } } diff --git a/third_party/rust/cranelift-wasm/src/sections_translator.rs b/third_party/rust/cranelift-wasm/src/sections_translator.rs index 3267822526797..da52b2846802a 100644 --- a/third_party/rust/cranelift-wasm/src/sections_translator.rs +++ b/third_party/rust/cranelift-wasm/src/sections_translator.rs @@ -26,7 +26,7 @@ use wasmparser::{ self, Data, DataKind, DataSectionReader, Element, ElementItem, ElementItems, ElementKind, ElementSectionReader, Export, ExportSectionReader, ExternalKind, FunctionSectionReader, GlobalSectionReader, GlobalType, ImportSectionEntryType, ImportSectionReader, - MemorySectionReader, MemoryType, NameSectionReader, Naming, Operator, TableSectionReader, Type, + MemorySectionReader, MemoryType, NameSectionReader, Naming, Operator, TableSectionReader, TypeDef, TypeSectionReader, }; @@ -88,7 +88,7 @@ pub fn parse_import_section<'data>( ImportSectionEntryType::Module(_sig) | ImportSectionEntryType::Instance(_sig) => { unimplemented!("module linking not implemented yet") } - ImportSectionEntryType::Memory(MemoryType { + ImportSectionEntryType::Memory(MemoryType::M32 { limits: ref memlimits, shared, }) => { @@ -102,6 +102,9 @@ pub fn parse_import_section<'data>( field_name, )?; } + ImportSectionEntryType::Memory(MemoryType::M64 { .. }) => { + unimplemented!(); + } ImportSectionEntryType::Global(ref ty) => { environ.declare_global_import( Global { @@ -189,11 +192,16 @@ pub fn parse_memory_section( for entry in memories { let memory = entry?; - environ.declare_memory(Memory { - minimum: memory.limits.initial, - maximum: memory.limits.maximum, - shared: memory.shared, - })?; + match memory { + MemoryType::M32 { limits, shared } => { + environ.declare_memory(Memory { + minimum: limits.initial, + maximum: limits.maximum, + shared: shared, + })?; + } + MemoryType::M64 { .. } => unimplemented!(), + } } Ok(()) @@ -313,13 +321,7 @@ pub fn parse_element_section<'data>( environ.reserve_table_elements(elements.get_count())?; for (index, entry) in elements.into_iter().enumerate() { - let Element { kind, items, ty } = entry?; - if ty != Type::FuncRef { - return Err(wasm_unsupported!( - "unsupported table element type: {:?}", - ty - )); - } + let Element { kind, items, ty: _ } = entry?; let segments = read_elems(&items)?; match kind { ElementKind::Active { diff --git a/third_party/rust/cranelift-wasm/src/translation_utils.rs b/third_party/rust/cranelift-wasm/src/translation_utils.rs index 2975584f7346d..d8bb6c8593cde 100644 --- a/third_party/rust/cranelift-wasm/src/translation_utils.rs +++ b/third_party/rust/cranelift-wasm/src/translation_utils.rs @@ -1,6 +1,5 @@ //! Helper functions and structures for the translation. use crate::environ::{TargetEnvironment, WasmResult, WasmType}; -use crate::state::ModuleTranslationState; use crate::wasm_unsupported; use core::convert::TryInto; use core::u32; @@ -10,7 +9,7 @@ use cranelift_codegen::ir::immediates::V128Imm; use cranelift_frontend::FunctionBuilder; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; -use wasmparser; +use wasmparser::{FuncValidator, WasmFuncType, WasmModuleResources}; /// Index type of a function (imported or defined) inside the WebAssembly module. #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] @@ -194,38 +193,56 @@ pub fn tabletype_to_type( } /// Get the parameter and result types for the given Wasm blocktype. -pub fn blocktype_params_results( - module_translation_state: &ModuleTranslationState, +pub fn blocktype_params_results<'a, T>( + validator: &'a FuncValidator, ty_or_ft: wasmparser::TypeOrFuncType, -) -> WasmResult<(&[wasmparser::Type], &[wasmparser::Type])> { - Ok(match ty_or_ft { - wasmparser::TypeOrFuncType::Type(ty) => match ty { - wasmparser::Type::I32 => (&[], &[wasmparser::Type::I32]), - wasmparser::Type::I64 => (&[], &[wasmparser::Type::I64]), - wasmparser::Type::F32 => (&[], &[wasmparser::Type::F32]), - wasmparser::Type::F64 => (&[], &[wasmparser::Type::F64]), - wasmparser::Type::V128 => (&[], &[wasmparser::Type::V128]), - wasmparser::Type::ExternRef => (&[], &[wasmparser::Type::ExternRef]), - wasmparser::Type::FuncRef => (&[], &[wasmparser::Type::FuncRef]), - wasmparser::Type::EmptyBlockType => (&[], &[]), - ty => return Err(wasm_unsupported!("blocktype_params_results: type {:?}", ty)), - }, +) -> WasmResult<( + impl ExactSizeIterator + Clone + 'a, + impl ExactSizeIterator + Clone + 'a, +)> +where + T: WasmModuleResources, +{ + return Ok(match ty_or_ft { + wasmparser::TypeOrFuncType::Type(ty) => { + let (params, results): (&'static [wasmparser::Type], &'static [wasmparser::Type]) = + match ty { + wasmparser::Type::I32 => (&[], &[wasmparser::Type::I32]), + wasmparser::Type::I64 => (&[], &[wasmparser::Type::I64]), + wasmparser::Type::F32 => (&[], &[wasmparser::Type::F32]), + wasmparser::Type::F64 => (&[], &[wasmparser::Type::F64]), + wasmparser::Type::V128 => (&[], &[wasmparser::Type::V128]), + wasmparser::Type::ExternRef => (&[], &[wasmparser::Type::ExternRef]), + wasmparser::Type::FuncRef => (&[], &[wasmparser::Type::FuncRef]), + wasmparser::Type::EmptyBlockType => (&[], &[]), + ty => return Err(wasm_unsupported!("blocktype_params_results: type {:?}", ty)), + }; + ( + itertools::Either::Left(params.iter().copied()), + itertools::Either::Left(results.iter().copied()), + ) + } wasmparser::TypeOrFuncType::FuncType(ty_index) => { - let sig_idx = SignatureIndex::from_u32(ty_index); - let (ref params, ref returns) = module_translation_state.wasm_types[sig_idx]; - (&*params, &*returns) + let ty = validator + .resources() + .func_type_at(ty_index) + .expect("should be valid"); + ( + itertools::Either::Right(ty.inputs()), + itertools::Either::Right(ty.outputs()), + ) } - }) + }); } /// Create a `Block` with the given Wasm parameters. pub fn block_with_params( builder: &mut FunctionBuilder, - params: &[wasmparser::Type], + params: impl IntoIterator, environ: &PE, ) -> WasmResult { let block = builder.create_block(); - for ty in params.iter() { + for ty in params { match ty { wasmparser::Type::I32 => { builder.append_block_param(block, ir::types::I32); @@ -240,7 +257,7 @@ pub fn block_with_params( builder.append_block_param(block, ir::types::F64); } wasmparser::Type::ExternRef | wasmparser::Type::FuncRef => { - builder.append_block_param(block, environ.reference_type((*ty).try_into()?)); + builder.append_block_param(block, environ.reference_type(ty.try_into()?)); } wasmparser::Type::V128 => { builder.append_block_param(block, ir::types::I8X16); diff --git a/third_party/rust/cranelift-wasm/tests/wasm_testsuite.rs b/third_party/rust/cranelift-wasm/tests/wasm_testsuite.rs index 65fd3cfdeb826..73b5508b2f455 100644 --- a/third_party/rust/cranelift-wasm/tests/wasm_testsuite.rs +++ b/third_party/rust/cranelift-wasm/tests/wasm_testsuite.rs @@ -4,9 +4,6 @@ use cranelift_codegen::settings::{self, Flags}; use cranelift_codegen::verifier; use cranelift_wasm::{translate_module, DummyEnvironment, FuncIndex, ReturnMode}; use std::fs; -use std::fs::File; -use std::io; -use std::io::prelude::*; use std::path::Path; use std::str::FromStr; use target_lexicon::triple; @@ -69,20 +66,13 @@ fn use_name_section() { ); } -fn read_file(path: &Path) -> io::Result> { - let mut buf: Vec = Vec::new(); - let mut file = File::open(path)?; - file.read_to_end(&mut buf)?; - Ok(buf) -} - fn read_module(path: &Path) -> Vec { match path.extension() { None => { panic!("the file extension is not wasm or wat"); } Some(ext) => match ext.to_str() { - Some("wasm") => read_file(path).expect("error reading wasm file"), + Some("wasm") => std::fs::read(path).expect("error reading wasm file"), Some("wat") => wat::parse_file(path) .map_err(|e| e.to_string()) .expect("failed to parse wat"), diff --git a/third_party/rust/itertools-0.8.0/.cargo-checksum.json b/third_party/rust/itertools-0.8.0/.cargo-checksum.json new file mode 100644 index 0000000000000..d7eca710e87f9 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"a4dc734c8e5ac0e5b57ded98adeb9340621d5c586d196d423781d7ceab06538b","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545","README.rst":"9db27658be49c4d5501c6bb6fbed1dcc1e796c7d934757cf3e94eab14d241d1d","benches/bench1.rs":"9794e5377019e4cf8caee3e7bb30034cf8ac291a485e64407df321f3f33affa0","benches/extra/mod.rs":"4c5b03e74fc5b02383500c9da9fd6550262706ee569d70d085700f6d0b5749ba","benches/extra/zipslices.rs":"108dd488de366b2d83fb6bcc603ecbf9a017e165ac19d03440074fa244af3fb2","benches/tree_fold1.rs":"84cddbabb1a681f3196430a8e27b060103366143a3ee4c42c3b0628fc00a7543","benches/tuple_combinations.rs":"6a4b89eb2e45fa0c99e5e5942b3196b76998213c66c44b3765b869be42016a82","benches/tuples.rs":"412a952f08bb03695952d5cfd57949dcf28be8b99e3c6653994bdb8af9654653","examples/iris.data":"596ffd580471ca4d4880f8e439c7281f3b50d8249a5960353cb200b1490f63a0","examples/iris.rs":"3996ca0a62762aec2b102f0f4244fe90d4b4354286d68d80cdc40e35f4352ba3","src/adaptors/mod.rs":"11ef622421a5980a722a8cd2a732e9379470c1817d8b9c4bcde654c3e417b794","src/adaptors/multi_product.rs":"bb239555be38cde1f419bacfd09728f6ccaf51b6b4811c266b5677705175e685","src/combinations.rs":"a9a3fc78eb5c9f3933ff60275a635d0c81f4864a73515dc052aeb2add4ad4909","src/concat_impl.rs":"276339b00588f54c25f8ffbe0ae3c0031f7e52fb53c6578554a0bde1681b58a5","src/cons_tuples_impl.rs":"87c620d2ffdd3475218f5f493dbef601491be9f6cdfe57c44929449b32e6709f","src/diff.rs":"921e2b867d7b32ffedc72a5eb780811322d14d1e0883a608b9028a2afcad0df2","src/either_or_both.rs":"8ea6db74fa21535de45cb48ee7134495e8822a0fa181fdb646e081251ac9459f","src/format.rs":"412fbe02f12311c6fbcec1044f57ad6991783f5a3f323b9c391accfe4915106f","src/free.rs":"473d19906720eb2a1309c0505497658b1426f3ea0c845b40f41f3154194fff18","src/group_map.rs":"872d6e243e649ad30c94973c034596cc3377b10018e361bca07e11c612006de6","src/groupbylazy.rs":"a067a12671be9ae05a9152518103f39f7286fde09f758de8af75a1064a3b5567","src/impl_macros.rs":"eb0bb3f70ec1bcaffa6110ae4134c777951ed1e5f48d8c811dbf0a597dc48faa","src/intersperse.rs":"9c18f239654ebfcce1d68a0256d2df6d79a2b8c4fb5df87d67e2ebe04a07e1a9","src/kmerge_impl.rs":"51e71d3e76670a8efb16597a9224d2c9b40cee6bc6270d06b66aadafea6dc26b","src/lib.rs":"edac21e8bff7bc7695480022a33ded58916eb1c5ed43f5aa68a7c965b452af0b","src/merge_join.rs":"98e6fcc761a558ad21789efe041c3f90e62f6c75e05840670df45ad4f9b07e1f","src/minmax.rs":"4668a7f824fbc133599f43ffb6f7283e5bd603e07df2d8176abc6f25d6af9db0","src/multipeek_impl.rs":"ebe9544d94d0bf7200f7625241a3b5a291b7b564091a08cad40ff08b51f1b1bf","src/pad_tail.rs":"078615a2892f8c6db665074cf6f1be1bef4cf5ee418bc174edcfd4dc703e163f","src/peeking_take_while.rs":"6aea3bb40fb480e9f3695ce2a7a3a2e2346d437ca846d20e6bb3c09beb0934f4","src/process_results_impl.rs":"5f454cf62ceb82cab7c08c0c190de3ae083e219a8acc7a1a22f17eec9cfcd65c","src/put_back_n_impl.rs":"d35858184c525372b22d14d42cdf63726cf0fd50f5bd42ec7a82d55a8e180e9f","src/rciter_impl.rs":"8f51abc7e1ae3320cc5d56fadd66f880a7a06773be656bd8c4712357f01ae1d9","src/repeatn.rs":"4bd1782364b16105fbef3f3de7bf62780710e5c996db44a00e4b5f16c2625d86","src/size_hint.rs":"c1d35b422a696cf3d63e7c90d8f9fdf01a304cf8156e914287c4ef48fea62dd3","src/sources.rs":"cb6ebe06b05f7da0ac1073ba486c45afaeebf8b558c0f5499f0eacd2cd1785da","src/tee.rs":"59cf9ef0b41882307ea1e3503a2ff351f401f4c43d95acf423a990b0bf0e29ae","src/tuple_impl.rs":"0c7f907e85d2ef0661583b36c7b8a7341b8feadafe28d10539a211dff5c028ea","src/unique_impl.rs":"63db2d720ff5e3d9c0d6c2b245ffff25d4040e4fcbcb2a6524b0f912826f86af","src/with_position.rs":"d922f045f6fa090a431be928f3221c6dc37ac6f9bb54461b3b84f99a7e91244a","src/zip_eq_impl.rs":"f857c69120255db16ad6ddec628c79cb573b1d5179fcebab1906bf5b762c02e3","src/zip_longest.rs":"375325ef069970e6fb83c6097c2824877bb0f06e4f1e664e4fe681804abe003c","src/ziptuple.rs":"d7ae7d3c33185ad74ab2bba750ac337b5c236750cc8341dd9883faf6465712a1","tests/merge_join.rs":"546eaffae40010f15a7bcf95bc53f5e9b67424c5b93df6ffb0aaa1e48e8b90c0","tests/peeking_take_while.rs":"a2ae6474e09620a47bb8a6e3c62929261e72c52881370adb2d22e89aa9e9aec8","tests/quick.rs":"290d52047c66b5c493ff425cc527a476c55a150bc859709a8a8559f3f0a74274","tests/test_core.rs":"1a1c4c2dbb657e9ce5cd2bb0d6475cf965878e2a259b730ada9238c71c2a8d4f","tests/test_std.rs":"90d2794cbee64b3a5a1cb53193387e2d83d2b9bdf588edb506a7bf3c5191d2ec","tests/tuples.rs":"5323d15a7abf6545b2655167d3206b6cf6a947e9409a244ea6a8cf4ad8ceac64","tests/zip.rs":"fe213d70c4fa114cb4d1930a6b971f4af617a239041ddb87e6b5a9bbe62261b8"},"package":"5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"} \ No newline at end of file diff --git a/third_party/rust/itertools-0.8.0/Cargo.toml b/third_party/rust/itertools-0.8.0/Cargo.toml new file mode 100644 index 0000000000000..9141434fe461c --- /dev/null +++ b/third_party/rust/itertools-0.8.0/Cargo.toml @@ -0,0 +1,47 @@ +# 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 = "itertools" +version = "0.8.0" +authors = ["bluss"] +exclude = ["/bors.toml"] +description = "Extra iterator adaptors, iterator methods, free functions, and macros." +documentation = "https://docs.rs/itertools/" +keywords = ["iterator", "data-structure", "zip", "product", "group-by"] +categories = ["algorithms", "rust-patterns"] +license = "MIT/Apache-2.0" +repository = "https://github.com/bluss/rust-itertools" +[package.metadata.release] +no-dev-version = true +[profile.bench] +debug = true + +[lib] +test = false +bench = false +[dependencies.either] +version = "1.0" +default-features = false +[dev-dependencies.permutohedron] +version = "0.2" + +[dev-dependencies.quickcheck] +version = "0.7" +default-features = false + +[dev-dependencies.rand] +version = "0.6" + +[features] +default = ["use_std"] +use_std = [] diff --git a/third_party/rust/itertools-0.8.0/LICENSE-APACHE b/third_party/rust/itertools-0.8.0/LICENSE-APACHE new file mode 100644 index 0000000000000..16fe87b06e802 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/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/third_party/rust/itertools-0.8.0/LICENSE-MIT b/third_party/rust/itertools-0.8.0/LICENSE-MIT new file mode 100644 index 0000000000000..9203baa055d41 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2015 + +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/third_party/rust/itertools-0.8.0/README.rst b/third_party/rust/itertools-0.8.0/README.rst new file mode 100644 index 0000000000000..b0ddda3c816e6 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/README.rst @@ -0,0 +1,526 @@ + +Itertools +========= + +Extra iterator adaptors, functions and macros. + +Please read the `API documentation here`__ + +__ https://docs.rs/itertools/ + +|build_status|_ |crates|_ + +.. |build_status| image:: https://travis-ci.org/bluss/rust-itertools.svg?branch=master +.. _build_status: https://travis-ci.org/bluss/rust-itertools + +.. |crates| image:: http://meritbadge.herokuapp.com/itertools +.. _crates: https://crates.io/crates/itertools + +How to use with cargo: + +.. code:: toml + + [dependencies] + itertools = "0.8" + +How to use in your crate: + +.. code:: rust + + #[macro_use] extern crate itertools; + + use itertools::Itertools; + +How to contribute +----------------- + +- Fix a bug or implement a new thing +- Include tests for your new feature, preferably a quickcheck test +- Make a Pull Request + +For new features, please first consider filing a PR to `rust-lang/rust `_, +adding your new feature to the `Iterator` trait of the standard library, if you believe it is reasonable. +If it isn't accepted there, proposing it for inclusion in ``itertools`` is a good idea. +The reason for doing is this is so that we avoid future breakage as with ``.flatten()``. +However, if your feature involves heap allocation, such as storing elements in a ``Vec``, +then it can't be accepted into ``libcore``, and you should propose it for ``itertools`` directly instead. + +Recent Changes +-------------- + +- 0.8.0 + + - Added new adaptor ``.map_into()`` for conversions using ``Into`` by @vorner + - Improved ``Itertools`` docs by @JohnHeitmann + - The return type of ``.sorted/_by/_by_key()`` is now an iterator, not a Vec. + - The return type of the ``izip!(x, y)`` macro with exactly two arguments + is now the usual ``Iterator::zip``. + - Remove ``.flatten()`` in favour of std's ``.flatten()`` + - Deprecate ``.foreach()`` in favour of std's ``.for_each()`` + - Deprecate ``.step()`` in favour of std's ``.step_by()`` + - Deprecate ``repeat_call`` in favour of std's ``repeat_with`` + - Deprecate ``.fold_while()`` in favour of std's ``.try_fold()`` + - Require Rust 1.24 as minimal version. + +- 0.7.11 + + - Add convenience methods to ``EitherOrBoth``, making it more similar to ``Option`` + and ``Either`` by @jethrogb + +- 0.7.10 + + - No changes. + +- 0.7.9 + + - New inclusion policy: See the readme about suggesting features for std before + accepting them in itertools. + - The ``FoldWhile`` type now implements ``Eq`` and ``PartialEq`` by @jturner314 + +- 0.7.8 + + - Add new iterator method ``.tree_fold1()`` which is like ``.fold1()`` + except items are combined in a tree structure (see its docs). + By @scottmcm + - Add more ``Debug`` impls by @phimuemue: KMerge, KMergeBy, MergeJoinBy, + ConsTuples, Intersperse, ProcessResults, RcIter, Tee, TupleWindows, Tee, + ZipLongest, ZipEq, Zip. + +- 0.7.7 + + - Add new iterator method ``.into_group_map() -> HashMap>`` + which turns an iterator of ``(K, V)`` elements into such a hash table, + where values are grouped by key. By @tobz1000 + - Add new free function ``flatten`` for the ``.flatten()`` adaptor. + **NOTE:** recent Rust nightlies have ``Iterator::flatten`` and thus a clash + with our flatten adaptor. One workaround is to use the itertools ``flatten`` + free function. + +- 0.7.6 + + - Add new adaptor ``.multi_cartesian_product()`` which is an n-ary product + iterator by @tobz1000 + - Add new method ``.sorted_by_key()`` by @Xion + - Provide simpler and faster ``.count()`` for ``.unique()`` and ``.unique_by()`` + +- 0.7.5 + + - ``.multipeek()`` now implements ``PeekingNext``, by @nicopap. + +- 0.7.4 + + - Add new adaptor ``.update()`` by @lucasem; this adaptor is used + to modify an element before passing it on in an iterator chain. + +- 0.7.3 + + - Add new method ``.collect_tuple()`` by @matklad; it makes a tuple out of + the iterator's elements if the number of them matches **exactly**. + - Implement ``fold`` and ``collect`` for ``.map_results()`` which means + it reuses the code of the standard ``.map()`` for these methods. + +- 0.7.2 + + - Add new adaptor ``.merge_join_by`` by @srijs; a heterogeneous merge join + for two ordered sequences. + +- 0.7.1 + + - Iterator adaptors and iterators in itertools now use the same ``must_use`` + reminder that the standard library adaptors do, by @matematikaedit and @bluss + *“iterator adaptors are lazy and do nothing unless consumed”*. + +- 0.7.0 + + - Faster ``izip!()`` by @krdln + + - ``izip!()`` is now a wrapper for repeated regular ``.zip()`` and + a single ``.map()``. This means it optimizes as well as the standard + library ``.zip()`` it uses. + **Note:** ``multizip`` and ``izip!()`` are now different! The former + has a named type but the latter optimizes better. + + - Faster ``.unique()`` + + - ``no_std`` support, which is opt-in! + + - Many lovable features are still there without std, like ``izip!()`` + or ``.format()`` or ``.merge()``, but not those that use collections. + + - Trait bounds were required up front instead of just on the type: + ``group_by``'s ``PartialEq`` by @Phlosioneer and ``repeat_call``'s + ``FnMut``. + - Removed deprecated constructor ``Zip::new`` — use ``izip!()`` or ``multizip()`` + +- 0.6.5 + + - Fix bug in ``.cartesian_product()``'s fold (which only was visible for + unfused iterators). + +- 0.6.4 + + - Add specific ``fold`` implementations for ``.cartesian_product()`` and + ``cons_tuples()``, which improves their performance in fold, foreach, and + iterator consumers derived from them. + +- 0.6.3 + + - Add iterator adaptor ``.positions(predicate)`` by @tmccombs + +- 0.6.2 + + - Add function ``process_results`` which can “lift” a function of the regular + values of an iterator so that it can process the ``Ok`` values from an + iterator of ``Results`` instead, by @shepmaster + - Add iterator method ``.concat()`` which combines all iterator elements + into a single collection using the ``Extend`` trait, by @srijs + +- 0.6.1 + + - Better size hint testing and subsequent size hint bugfixes by @rkarp. + Fixes bugs in product, interleave_shortest size hints. + - New iterator method ``.all_equal()`` by @phimuemue + +- 0.6.0 + + - Deprecated names were removed in favour of their replacements + - ``.flatten()`` does not implement double ended iteration anymore + - ``.fold_while()`` uses ``&mut self`` and returns ``FoldWhile``, for + composability (#168) + - ``.foreach()`` and ``.fold1()`` use ``self``, like ``.fold()`` does. + - ``.combinations(0)`` now produces a single empty vector. (#174) + +- 0.5.10 + + - Add itertools method ``.kmerge_by()`` (and corresponding free function) + - Relaxed trait requirement of ``.kmerge()`` and ``.minmax()`` to PartialOrd. + +- 0.5.9 + + - Add multipeek method ``.reset_peek()`` + - Add categories + +- 0.5.8 + + - Add iterator adaptor ``.peeking_take_while()`` and its trait ``PeekingNext``. + +- 0.5.7 + + - Add iterator adaptor ``.with_position()`` + - Fix multipeek's performance for long peeks by using ``VecDeque``. + +- 0.5.6 + + - Add ``.map_results()`` + +- 0.5.5 + + - Many more adaptors now implement ``Debug`` + - Add free function constructor ``repeat_n``. ``RepeatN::new`` is now + deprecated. + +- 0.5.4 + + - Add infinite generator function ``iterate``, that takes a seed and a + closure. + +- 0.5.3 + + - Special-cased ``.fold()`` for flatten and put back. ``.foreach()`` + now uses fold on the iterator, to pick up any iterator specific loop + implementation. + - ``.combinations(n)`` asserts up front that ``n != 0``, instead of + running into an error on the second iterator element. + +- 0.5.2 + + - Add ``.tuples::()`` that iterates by two, three or four elements at + a time (where ``T`` is a tuple type). + - Add ``.tuple_windows::()`` that iterates using a window of the + two, three or four most recent elements. + - Add ``.next_tuple::()`` method, that picks the next two, three or four + elements in one go. + - ``.interleave()`` now has an accurate size hint. + +- 0.5.1 + + - Workaround module/function name clash that made racer crash on completing + itertools. Only internal changes needed. + +- 0.5.0 + + - `Release announcement `_ + - Renamed: + + - combinations is now tuple_combinations + - combinations_n to combinations + - group_by_lazy, chunks_lazy to group_by, chunks + - Unfold::new to unfold() + - RepeatCall::new to repeat_call() + - Zip::new to multizip + - PutBack::new, PutBackN::new to put_back, put_back_n + - PutBack::with_value is now a builder setter, not a constructor + - MultiPeek::new, .multipeek() to multipeek() + - format to format_with and format_default to format + - .into_rc() to rciter + - ``Partition`` enum is now ``Either`` + + - Module reorganization: + + - All iterator structs are under ``itertools::structs`` but also + reexported to the top level, for backwards compatibility + - All free functions are reexported at the root, ``itertools::free`` will + be removed in the next version + + - Removed: + + - ZipSlices, use .zip() instead + - .enumerate_from(), ZipTrusted, due to being unstable + - .mend_slices(), moved to crate odds + - Stride, StrideMut, moved to crate odds + - linspace(), moved to crate itertools-num + - .sort_by(), use .sorted_by() + - .is_empty_hint(), use .size_hint() + - .dropn(), use .dropping() + - .map_fn(), use .map() + - .slice(), use .take() / .skip() + - helper traits in misc + - ``new`` constructors on iterator structs, use Itertools trait or free + functions instead + - ``itertools::size_hint`` is now private + + - Behaviour changes: + + - format and format_with helpers now panic if you try to format them more + than once. + - ``repeat_call`` is not double ended anymore + + - New features: + + - tuple flattening iterator is constructible with ``cons_tuples`` + - itertools reexports ``Either`` from the ``either`` crate. ``Either`` + is an iterator when ``L, R`` are. + - ``MinMaxResult`` now implements Copy and Clone + - tuple_combinations supports 1-4 tuples of combinations (previously just 2) + +- 0.4.19 + + - Add ``.minmax_by()`` + - Add ``itertools::free::cloned`` + - Add ``itertools::free::rciter`` + - Improve ``.step(n)`` slightly to take advantage of specialized Fuse better. + +- 0.4.18 + + - Only changes related to the "unstable" crate feature. This feature is more + or less deprecated. + + - Use deprecated warnings when unstable is enabled. .enumerate_from() will + be removed imminently since it's using a deprecated libstd trait. + +- 0.4.17 + + - Fix bug in .kmerge() that caused it to often produce the wrong order (#134) + +- 0.4.16 + + - Improve precision of the interleave_shortest adaptor's size hint (it is + now computed exactly when possible). + +- 0.4.15 + + - Fixup on top of the workaround in 0.4.14. A function in itertools::free was + removed by mistake and now it is added back again. + +- 0.4.14 + + - Workaround an upstream regression in a rust nightly build that broke + compilation of of itertools::free::{interleave, merge} + +- 0.4.13 + + - Add .minmax() and .minmax_by_key(), iterator methods for finding both minimum + and maximum in one scan. + - Add .format_default(), a simpler version of .format() (lazy formatting + for iterators). + +- 0.4.12 + + - Add .zip_eq(), an adaptor like .zip() except it ensures iterators + of inequal length don't pass silently (instead it panics). + - Add .fold_while(), an iterator method that is a fold that + can short-circuit. + - Add .partition_map(), an iterator method that can separate elements + into two collections. + +- 0.4.11 + + - Add .get() for Stride{,Mut} and .get_mut() for StrideMut + +- 0.4.10 + + - Improve performance of .kmerge() + +- 0.4.9 + + - Add k-ary merge adaptor .kmerge() + - Fix a bug in .islice() with ranges a..b where a > b. + +- 0.4.8 + + - Implement Clone, Debug for Linspace + +- 0.4.7 + + - Add function diff_with() that compares two iterators + - Add .combinations_n(), an n-ary combinations iterator + - Add methods PutBack::with_value and PutBack::into_parts. + +- 0.4.6 + + - Add method .sorted() + - Add module ``itertools::free`` with free function variants of common + iterator adaptors and methods. + For example ``enumerate(iterable)``, ``rev(iterable)``, and so on. + +- 0.4.5 + + - Add .flatten() + +- 0.4.4 + + - Allow composing ZipSlices with itself + +- 0.4.3 + + - Write iproduct!() as a single expression; this allows temporary values + in its arguments. + +- 0.4.2 + + - Add .fold_options() + - Require Rust 1.1 or later + +- 0.4.1 + + - Update .dropping() to take advantage of .nth() + +- 0.4.0 + + - .merge(), .unique() and .dedup() now perform better due to not using + function pointers + - Add free functions enumerate() and rev() + - Breaking changes: + + - Return types of .merge() and .merge_by() renamed and changed + - Method Merge::new removed + - .merge_by() now takes a closure that returns bool. + - Return type of .dedup() changed + - Return type of .mend_slices() changed + - Return type of .unique() changed + - Removed function times(), struct Times: use a range instead + - Removed deprecated macro icompr!() + - Removed deprecated FnMap and method .fn_map(): use .map_fn() + - .interleave_shortest() is no longer guaranteed to act like fused + +- 0.3.25 + + - Rename .sort_by() to .sorted_by(). Old name is deprecated. + - Fix well-formedness warnings from RFC 1214, no user visible impact + +- 0.3.24 + + - Improve performance of .merge()'s ordering function slightly + +- 0.3.23 + + - Added .chunks(), similar to (and based on) .group_by_lazy(). + - Tweak linspace to match numpy.linspace and make it double ended. + +- 0.3.22 + + - Added ZipSlices, a fast zip for slices + +- 0.3.21 + + - Remove `Debug` impl for `Format`, it will have different use later + +- 0.3.20 + + - Optimize .group_by_lazy() + +- 0.3.19 + + - Added .group_by_lazy(), a possibly nonallocating group by + - Added .format(), a nonallocating formatting helper for iterators + - Remove uses of RandomAccessIterator since it has been deprecated in rust. + +- 0.3.17 + + - Added (adopted) Unfold from rust + +- 0.3.16 + + - Added adaptors .unique(), .unique_by() + +- 0.3.15 + + - Added method .sort_by() + +- 0.3.14 + + - Added adaptor .while_some() + +- 0.3.13 + + - Added adaptor .interleave_shortest() + - Added adaptor .pad_using() + +- 0.3.11 + + - Added assert_equal function + +- 0.3.10 + + - Bugfix .combinations() size_hint. + +- 0.3.8 + + - Added source RepeatCall + +- 0.3.7 + + - Added adaptor PutBackN + - Added adaptor .combinations() + +- 0.3.6 + + - Added itertools::partition, partition a sequence in place based on a predicate. + - Deprecate icompr!() with no replacement. + +- 0.3.5 + + - .map_fn() replaces deprecated .fn_map(). + +- 0.3.4 + + - .take_while_ref() *by-ref adaptor* + - .coalesce() *adaptor* + - .mend_slices() *adaptor* + +- 0.3.3 + + - .dropping_back() *method* + - .fold1() *method* + - .is_empty_hint() *method* + +License +------- + +Dual-licensed to be compatible 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. This file may not be copied, modified, or distributed +except according to those terms. diff --git a/third_party/rust/itertools-0.8.0/benches/bench1.rs b/third_party/rust/itertools-0.8.0/benches/bench1.rs new file mode 100644 index 0000000000000..b9d3b4ff4ba45 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/benches/bench1.rs @@ -0,0 +1,733 @@ +#![feature(test)] + +extern crate test; +#[macro_use] extern crate itertools; + +use test::{black_box}; +use itertools::Itertools; + +use itertools::free::cloned; + +use std::iter::repeat; +use std::cmp; +use std::ops::Add; + +mod extra; + +use extra::ZipSlices; + +#[bench] +fn slice_iter(b: &mut test::Bencher) +{ + let xs: Vec<_> = repeat(1i32).take(20).collect(); + b.iter(|| for elt in xs.iter() { + test::black_box(elt); + }) +} + +#[bench] +fn slice_iter_rev(b: &mut test::Bencher) +{ + let xs: Vec<_> = repeat(1i32).take(20).collect(); + b.iter(|| for elt in xs.iter().rev() { + test::black_box(elt); + }) +} + +#[bench] +fn zip_default_zip(b: &mut test::Bencher) +{ + let xs = vec![0; 1024]; + let ys = vec![0; 768]; + let xs = black_box(xs); + let ys = black_box(ys); + + b.iter(|| { + for (&x, &y) in xs.iter().zip(&ys) { + test::black_box(x); + test::black_box(y); + } + }) +} + +#[bench] +fn zipdot_i32_default_zip(b: &mut test::Bencher) +{ + let xs = vec![2; 1024]; + let ys = vec![2; 768]; + let xs = black_box(xs); + let ys = black_box(ys); + + b.iter(|| { + let mut s = 0; + for (&x, &y) in xs.iter().zip(&ys) { + s += x * y; + } + s + }) +} + +#[bench] +fn zipdot_f32_default_zip(b: &mut test::Bencher) +{ + let xs = vec![2f32; 1024]; + let ys = vec![2f32; 768]; + let xs = black_box(xs); + let ys = black_box(ys); + + b.iter(|| { + let mut s = 0.; + for (&x, &y) in xs.iter().zip(&ys) { + s += x * y; + } + s + }) +} + +#[bench] +fn zip_default_zip3(b: &mut test::Bencher) +{ + let xs = vec![0; 1024]; + let ys = vec![0; 768]; + let zs = vec![0; 766]; + let xs = black_box(xs); + let ys = black_box(ys); + let zs = black_box(zs); + + b.iter(|| { + for ((&x, &y), &z) in xs.iter().zip(&ys).zip(&zs) { + test::black_box(x); + test::black_box(y); + test::black_box(z); + } + }) +} + +#[bench] +fn zip_slices_ziptuple(b: &mut test::Bencher) +{ + let xs = vec![0; 1024]; + let ys = vec![0; 768]; + + b.iter(|| { + let xs = black_box(&xs); + let ys = black_box(&ys); + for (&x, &y) in itertools::multizip((xs, ys)) { + test::black_box(x); + test::black_box(y); + } + }) +} + +#[bench] +fn zipslices(b: &mut test::Bencher) +{ + let xs = vec![0; 1024]; + let ys = vec![0; 768]; + let xs = black_box(xs); + let ys = black_box(ys); + + b.iter(|| { + for (&x, &y) in ZipSlices::new(&xs, &ys) { + test::black_box(x); + test::black_box(y); + } + }) +} + +#[bench] +fn zipslices_mut(b: &mut test::Bencher) +{ + let xs = vec![0; 1024]; + let ys = vec![0; 768]; + let xs = black_box(xs); + let mut ys = black_box(ys); + + b.iter(|| { + for (&x, &mut y) in ZipSlices::from_slices(&xs[..], &mut ys[..]) { + test::black_box(x); + test::black_box(y); + } + }) +} + +#[bench] +fn zipdot_i32_zipslices(b: &mut test::Bencher) +{ + let xs = vec![2; 1024]; + let ys = vec![2; 768]; + let xs = black_box(xs); + let ys = black_box(ys); + + b.iter(|| { + let mut s = 0i32; + for (&x, &y) in ZipSlices::new(&xs, &ys) { + s += x * y; + } + s + }) +} + +#[bench] +fn zipdot_f32_zipslices(b: &mut test::Bencher) +{ + let xs = vec![2f32; 1024]; + let ys = vec![2f32; 768]; + let xs = black_box(xs); + let ys = black_box(ys); + + b.iter(|| { + let mut s = 0.; + for (&x, &y) in ZipSlices::new(&xs, &ys) { + s += x * y; + } + s + }) +} + + +#[bench] +fn zip_checked_counted_loop(b: &mut test::Bencher) +{ + let xs = vec![0; 1024]; + let ys = vec![0; 768]; + let xs = black_box(xs); + let ys = black_box(ys); + + b.iter(|| { + // Must slice to equal lengths, and then bounds checks are eliminated! + let len = cmp::min(xs.len(), ys.len()); + let xs = &xs[..len]; + let ys = &ys[..len]; + + for i in 0..len { + let x = xs[i]; + let y = ys[i]; + test::black_box(x); + test::black_box(y); + } + }) +} + +#[bench] +fn zipdot_i32_checked_counted_loop(b: &mut test::Bencher) +{ + let xs = vec![2; 1024]; + let ys = vec![2; 768]; + let xs = black_box(xs); + let ys = black_box(ys); + + b.iter(|| { + // Must slice to equal lengths, and then bounds checks are eliminated! + let len = cmp::min(xs.len(), ys.len()); + let xs = &xs[..len]; + let ys = &ys[..len]; + + let mut s = 0i32; + + for i in 0..len { + s += xs[i] * ys[i]; + } + s + }) +} + +#[bench] +fn zipdot_f32_checked_counted_loop(b: &mut test::Bencher) +{ + let xs = vec![2f32; 1024]; + let ys = vec![2f32; 768]; + let xs = black_box(xs); + let ys = black_box(ys); + + b.iter(|| { + // Must slice to equal lengths, and then bounds checks are eliminated! + let len = cmp::min(xs.len(), ys.len()); + let xs = &xs[..len]; + let ys = &ys[..len]; + + let mut s = 0.; + + for i in 0..len { + s += xs[i] * ys[i]; + } + s + }) +} + +#[bench] +fn zipdot_f32_checked_counted_unrolled_loop(b: &mut test::Bencher) +{ + let xs = vec![2f32; 1024]; + let ys = vec![2f32; 768]; + let xs = black_box(xs); + let ys = black_box(ys); + + b.iter(|| { + // Must slice to equal lengths, and then bounds checks are eliminated! + let len = cmp::min(xs.len(), ys.len()); + let mut xs = &xs[..len]; + let mut ys = &ys[..len]; + + let mut s = 0.; + let (mut p0, mut p1, mut p2, mut p3, mut p4, mut p5, mut p6, mut p7) = + (0., 0., 0., 0., 0., 0., 0., 0.); + + // how to unroll and have bounds checks eliminated (by cristicbz) + // split sum into eight parts to enable vectorization (by bluss) + while xs.len() >= 8 { + p0 += xs[0] * ys[0]; + p1 += xs[1] * ys[1]; + p2 += xs[2] * ys[2]; + p3 += xs[3] * ys[3]; + p4 += xs[4] * ys[4]; + p5 += xs[5] * ys[5]; + p6 += xs[6] * ys[6]; + p7 += xs[7] * ys[7]; + + xs = &xs[8..]; + ys = &ys[8..]; + } + s += p0 + p4; + s += p1 + p5; + s += p2 + p6; + s += p3 + p7; + + for i in 0..xs.len() { + s += xs[i] * ys[i]; + } + s + }) +} + +#[bench] +fn zip_unchecked_counted_loop(b: &mut test::Bencher) +{ + let xs = vec![0; 1024]; + let ys = vec![0; 768]; + let xs = black_box(xs); + let ys = black_box(ys); + + b.iter(|| { + let len = cmp::min(xs.len(), ys.len()); + for i in 0..len { + unsafe { + let x = *xs.get_unchecked(i); + let y = *ys.get_unchecked(i); + test::black_box(x); + test::black_box(y); + } + } + }) +} + +#[bench] +fn zipdot_i32_unchecked_counted_loop(b: &mut test::Bencher) +{ + let xs = vec![2; 1024]; + let ys = vec![2; 768]; + let xs = black_box(xs); + let ys = black_box(ys); + + b.iter(|| { + let len = cmp::min(xs.len(), ys.len()); + let mut s = 0i32; + for i in 0..len { + unsafe { + let x = *xs.get_unchecked(i); + let y = *ys.get_unchecked(i); + s += x * y; + } + } + s + }) +} + +#[bench] +fn zipdot_f32_unchecked_counted_loop(b: &mut test::Bencher) +{ + let xs = vec![2.; 1024]; + let ys = vec![2.; 768]; + let xs = black_box(xs); + let ys = black_box(ys); + + b.iter(|| { + let len = cmp::min(xs.len(), ys.len()); + let mut s = 0f32; + for i in 0..len { + unsafe { + let x = *xs.get_unchecked(i); + let y = *ys.get_unchecked(i); + s += x * y; + } + } + s + }) +} + +#[bench] +fn zip_unchecked_counted_loop3(b: &mut test::Bencher) +{ + let xs = vec![0; 1024]; + let ys = vec![0; 768]; + let zs = vec![0; 766]; + let xs = black_box(xs); + let ys = black_box(ys); + let zs = black_box(zs); + + b.iter(|| { + let len = cmp::min(xs.len(), cmp::min(ys.len(), zs.len())); + for i in 0..len { + unsafe { + let x = *xs.get_unchecked(i); + let y = *ys.get_unchecked(i); + let z = *zs.get_unchecked(i); + test::black_box(x); + test::black_box(y); + test::black_box(z); + } + } + }) +} + +#[bench] +fn group_by_lazy_1(b: &mut test::Bencher) { + let mut data = vec![0; 1024]; + for (index, elt) in data.iter_mut().enumerate() { + *elt = index / 10; + } + + let data = test::black_box(data); + + b.iter(|| { + for (_key, group) in &data.iter().group_by(|elt| **elt) { + for elt in group { + test::black_box(elt); + } + } + }) +} + +#[bench] +fn group_by_lazy_2(b: &mut test::Bencher) { + let mut data = vec![0; 1024]; + for (index, elt) in data.iter_mut().enumerate() { + *elt = index / 2; + } + + let data = test::black_box(data); + + b.iter(|| { + for (_key, group) in &data.iter().group_by(|elt| **elt) { + for elt in group { + test::black_box(elt); + } + } + }) +} + +#[bench] +fn slice_chunks(b: &mut test::Bencher) { + let data = vec![0; 1024]; + + let data = test::black_box(data); + let sz = test::black_box(10); + + b.iter(|| { + for group in data.chunks(sz) { + for elt in group { + test::black_box(elt); + } + } + }) +} + +#[bench] +fn chunks_lazy_1(b: &mut test::Bencher) { + let data = vec![0; 1024]; + + let data = test::black_box(data); + let sz = test::black_box(10); + + b.iter(|| { + for group in &data.iter().chunks(sz) { + for elt in group { + test::black_box(elt); + } + } + }) +} + +#[bench] +fn equal(b: &mut test::Bencher) { + let data = vec![7; 1024]; + let l = data.len(); + let alpha = test::black_box(&data[1..]); + let beta = test::black_box(&data[..l - 1]); + b.iter(|| { + itertools::equal(alpha, beta) + }) +} + +#[bench] +fn merge_default(b: &mut test::Bencher) { + let mut data1 = vec![0; 1024]; + let mut data2 = vec![0; 800]; + let mut x = 0; + for (_, elt) in data1.iter_mut().enumerate() { + *elt = x; + x += 1; + } + + let mut y = 0; + for (i, elt) in data2.iter_mut().enumerate() { + *elt += y; + if i % 3 == 0 { + y += 3; + } else { + y += 0; + } + } + let data1 = test::black_box(data1); + let data2 = test::black_box(data2); + b.iter(|| { + data1.iter().merge(&data2).count() + }) +} + +#[bench] +fn merge_by_cmp(b: &mut test::Bencher) { + let mut data1 = vec![0; 1024]; + let mut data2 = vec![0; 800]; + let mut x = 0; + for (_, elt) in data1.iter_mut().enumerate() { + *elt = x; + x += 1; + } + + let mut y = 0; + for (i, elt) in data2.iter_mut().enumerate() { + *elt += y; + if i % 3 == 0 { + y += 3; + } else { + y += 0; + } + } + let data1 = test::black_box(data1); + let data2 = test::black_box(data2); + b.iter(|| { + data1.iter().merge_by(&data2, PartialOrd::le).count() + }) +} + +#[bench] +fn merge_by_lt(b: &mut test::Bencher) { + let mut data1 = vec![0; 1024]; + let mut data2 = vec![0; 800]; + let mut x = 0; + for (_, elt) in data1.iter_mut().enumerate() { + *elt = x; + x += 1; + } + + let mut y = 0; + for (i, elt) in data2.iter_mut().enumerate() { + *elt += y; + if i % 3 == 0 { + y += 3; + } else { + y += 0; + } + } + let data1 = test::black_box(data1); + let data2 = test::black_box(data2); + b.iter(|| { + data1.iter().merge_by(&data2, |a, b| a <= b).count() + }) +} + +#[bench] +fn kmerge_default(b: &mut test::Bencher) { + let mut data1 = vec![0; 1024]; + let mut data2 = vec![0; 800]; + let mut x = 0; + for (_, elt) in data1.iter_mut().enumerate() { + *elt = x; + x += 1; + } + + let mut y = 0; + for (i, elt) in data2.iter_mut().enumerate() { + *elt += y; + if i % 3 == 0 { + y += 3; + } else { + y += 0; + } + } + let data1 = test::black_box(data1); + let data2 = test::black_box(data2); + let its = &[data1.iter(), data2.iter()]; + b.iter(|| { + its.iter().cloned().kmerge().count() + }) +} + +#[bench] +fn kmerge_tenway(b: &mut test::Bencher) { + let mut data = vec![0; 10240]; + + let mut state = 1729u16; + fn rng(state: &mut u16) -> u16 { + let new = state.wrapping_mul(31421) + 6927; + *state = new; + new + } + + for elt in &mut data { + *elt = rng(&mut state); + } + + let mut chunks = Vec::new(); + let mut rest = &mut data[..]; + while rest.len() > 0 { + let chunk_len = 1 + rng(&mut state) % 512; + let chunk_len = cmp::min(rest.len(), chunk_len as usize); + let (fst, tail) = {rest}.split_at_mut(chunk_len); + fst.sort(); + chunks.push(fst.iter().cloned()); + rest = tail; + } + + // println!("Chunk lengths: {}", chunks.iter().format_with(", ", |elt, f| f(&elt.len()))); + + b.iter(|| { + chunks.iter().cloned().kmerge().count() + }) +} + + +fn fast_integer_sum(iter: I) -> I::Item + where I: IntoIterator, + I::Item: Default + Add +{ + iter.into_iter().fold(<_>::default(), |x, y| x + y) +} + + +#[bench] +fn step_vec_2(b: &mut test::Bencher) { + let v = vec![0; 1024]; + b.iter(|| { + fast_integer_sum(cloned(v.iter().step(2))) + }); +} + +#[bench] +fn step_vec_10(b: &mut test::Bencher) { + let v = vec![0; 1024]; + b.iter(|| { + fast_integer_sum(cloned(v.iter().step(10))) + }); +} + +#[bench] +fn step_range_2(b: &mut test::Bencher) { + let v = black_box(0..1024); + b.iter(|| { + fast_integer_sum(v.clone().step(2)) + }); +} + +#[bench] +fn step_range_10(b: &mut test::Bencher) { + let v = black_box(0..1024); + b.iter(|| { + fast_integer_sum(v.clone().step(10)) + }); +} + +#[bench] +fn cartesian_product_iterator(b: &mut test::Bencher) +{ + let xs = vec![0; 16]; + + b.iter(|| { + let mut sum = 0; + for (&x, &y, &z) in iproduct!(&xs, &xs, &xs) { + sum += x; + sum += y; + sum += z; + } + sum + }) +} + +#[bench] +fn cartesian_product_fold(b: &mut test::Bencher) +{ + let xs = vec![0; 16]; + + b.iter(|| { + let mut sum = 0; + iproduct!(&xs, &xs, &xs).fold((), |(), (&x, &y, &z)| { + sum += x; + sum += y; + sum += z; + }); + sum + }) +} + +#[bench] +fn multi_cartesian_product_iterator(b: &mut test::Bencher) +{ + let xs = [vec![0; 16], vec![0; 16], vec![0; 16]]; + + b.iter(|| { + let mut sum = 0; + for x in xs.into_iter().multi_cartesian_product() { + sum += x[0]; + sum += x[1]; + sum += x[2]; + } + sum + }) +} + +#[bench] +fn multi_cartesian_product_fold(b: &mut test::Bencher) +{ + let xs = [vec![0; 16], vec![0; 16], vec![0; 16]]; + + b.iter(|| { + let mut sum = 0; + xs.into_iter().multi_cartesian_product().fold((), |(), x| { + sum += x[0]; + sum += x[1]; + sum += x[2]; + }); + sum + }) +} + +#[bench] +fn cartesian_product_nested_for(b: &mut test::Bencher) +{ + let xs = vec![0; 16]; + + b.iter(|| { + let mut sum = 0; + for &x in &xs { + for &y in &xs { + for &z in &xs { + sum += x; + sum += y; + sum += z; + } + } + } + sum + }) +} diff --git a/third_party/rust/itertools-0.8.0/benches/extra/mod.rs b/third_party/rust/itertools-0.8.0/benches/extra/mod.rs new file mode 100644 index 0000000000000..5ddb5772f423f --- /dev/null +++ b/third_party/rust/itertools-0.8.0/benches/extra/mod.rs @@ -0,0 +1,4 @@ + + +pub use self::zipslices::ZipSlices; +mod zipslices; diff --git a/third_party/rust/itertools-0.8.0/benches/extra/zipslices.rs b/third_party/rust/itertools-0.8.0/benches/extra/zipslices.rs new file mode 100644 index 0000000000000..493a539fd63eb --- /dev/null +++ b/third_party/rust/itertools-0.8.0/benches/extra/zipslices.rs @@ -0,0 +1,189 @@ +use std::cmp; + +// Note: There are different ways to implement ZipSlices. +// This version performed the best in benchmarks. +// +// I also implemented a version with three pointes (tptr, tend, uptr), +// that mimiced slice::Iter and only checked bounds by using tptr == tend, +// but that was inferior to this solution. + +/// An iterator which iterates two slices simultaneously. +/// +/// `ZipSlices` acts like a double-ended `.zip()` iterator. +/// +/// It was intended to be more efficient than `.zip()`, and it was, then +/// rustc changed how it optimizes so it can not promise improved performance +/// at this time. +/// +/// Note that elements past the end of the shortest of the two slices are ignored. +/// +/// Iterator element type for `ZipSlices` is `(T::Item, U::Item)`. For example, +/// for a `ZipSlices<&'a [A], &'b mut [B]>`, the element type is `(&'a A, &'b mut B)`. +#[derive(Clone)] +pub struct ZipSlices { + t: T, + u: U, + len: usize, + index: usize, +} + +impl<'a, 'b, A, B> ZipSlices<&'a [A], &'b [B]> { + /// Create a new `ZipSlices` from slices `a` and `b`. + /// + /// Act like a double-ended `.zip()` iterator, but more efficiently. + /// + /// Note that elements past the end of the shortest of the two slices are ignored. + #[inline(always)] + pub fn new(a: &'a [A], b: &'b [B]) -> Self { + let minl = cmp::min(a.len(), b.len()); + ZipSlices { + t: a, + u: b, + len: minl, + index: 0, + } + } +} + +impl ZipSlices + where T: Slice, + U: Slice +{ + /// Create a new `ZipSlices` from slices `a` and `b`. + /// + /// Act like a double-ended `.zip()` iterator, but more efficiently. + /// + /// Note that elements past the end of the shortest of the two slices are ignored. + #[inline(always)] + pub fn from_slices(a: T, b: U) -> Self { + let minl = cmp::min(a.len(), b.len()); + ZipSlices { + t: a, + u: b, + len: minl, + index: 0, + } + } +} + +impl Iterator for ZipSlices + where T: Slice, + U: Slice +{ + type Item = (T::Item, U::Item); + + #[inline(always)] + fn next(&mut self) -> Option { + unsafe { + if self.index >= self.len { + None + } else { + let i = self.index; + self.index += 1; + Some(( + self.t.get_unchecked(i), + self.u.get_unchecked(i))) + } + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = self.len - self.index; + (len, Some(len)) + } +} + +impl DoubleEndedIterator for ZipSlices + where T: Slice, + U: Slice +{ + #[inline(always)] + fn next_back(&mut self) -> Option { + unsafe { + if self.index >= self.len { + None + } else { + self.len -= 1; + let i = self.len; + Some(( + self.t.get_unchecked(i), + self.u.get_unchecked(i))) + } + } + } +} + +impl ExactSizeIterator for ZipSlices + where T: Slice, + U: Slice +{} + +unsafe impl Slice for ZipSlices + where T: Slice, + U: Slice +{ + type Item = (T::Item, U::Item); + + fn len(&self) -> usize { + self.len - self.index + } + + unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item { + (self.t.get_unchecked(i), + self.u.get_unchecked(i)) + } +} + +/// A helper trait to let `ZipSlices` accept both `&[T]` and `&mut [T]`. +/// +/// Unsafe trait because: +/// +/// - Implementors must guarantee that `get_unchecked` is valid for all indices `0..len()`. +pub unsafe trait Slice { + /// The type of a reference to the slice's elements + type Item; + #[doc(hidden)] + fn len(&self) -> usize; + #[doc(hidden)] + unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item; +} + +unsafe impl<'a, T> Slice for &'a [T] { + type Item = &'a T; + #[inline(always)] + fn len(&self) -> usize { (**self).len() } + #[inline(always)] + unsafe fn get_unchecked(&mut self, i: usize) -> &'a T { + debug_assert!(i < self.len()); + (**self).get_unchecked(i) + } +} + +unsafe impl<'a, T> Slice for &'a mut [T] { + type Item = &'a mut T; + #[inline(always)] + fn len(&self) -> usize { (**self).len() } + #[inline(always)] + unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut T { + debug_assert!(i < self.len()); + // override the lifetime constraints of &mut &'a mut [T] + (*(*self as *mut [T])).get_unchecked_mut(i) + } +} + +#[test] +fn zipslices() { + + let xs = [1, 2, 3, 4, 5, 6]; + let ys = [1, 2, 3, 7]; + ::itertools::assert_equal(ZipSlices::new(&xs, &ys), xs.iter().zip(&ys)); + + let xs = [1, 2, 3, 4, 5, 6]; + let mut ys = [0; 6]; + for (x, y) in ZipSlices::from_slices(&xs[..], &mut ys[..]) { + *y = *x; + } + ::itertools::assert_equal(&xs, &ys); +} + diff --git a/third_party/rust/itertools-0.8.0/benches/tree_fold1.rs b/third_party/rust/itertools-0.8.0/benches/tree_fold1.rs new file mode 100644 index 0000000000000..b71589f3fea33 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/benches/tree_fold1.rs @@ -0,0 +1,126 @@ +#![feature(test)] + +extern crate test; +extern crate itertools; + +use itertools::Itertools; +use itertools::cloned; +use test::Bencher; + +trait IterEx : Iterator { + // Another efficient implementation against which to compare, + // but needs `std` so is less desirable. + fn tree_fold1_vec(self, mut f: F) -> Option + where F: FnMut(Self::Item, Self::Item) -> Self::Item, + Self: Sized, + { + let hint = self.size_hint().0; + let cap = std::mem::size_of::() * 8 - hint.leading_zeros() as usize; + let mut stack = Vec::with_capacity(cap); + self.enumerate().foreach(|(mut i, mut x)| { + while (i & 1) != 0 { + x = f(stack.pop().unwrap(), x); + i >>= 1; + } + stack.push(x); + }); + stack.into_iter().fold1(f) + } +} +impl IterEx for T {} + +macro_rules! def_benchs { + ($N:expr, + $FUN:ident, + $BENCH_NAME:ident, + ) => ( + mod $BENCH_NAME { + use super::*; + + #[bench] + fn sum(b: &mut Bencher) { + let v: Vec = (0.. $N).collect(); + b.iter(|| { + cloned(&v).$FUN(|x, y| x + y) + }); + } + + #[bench] + fn complex_iter(b: &mut Bencher) { + let u = (3..).take($N / 2); + let v = (5..).take($N / 2); + let it = u.chain(v); + + b.iter(|| { + it.clone().map(|x| x as f32).$FUN(f32::atan2) + }); + } + + #[bench] + fn string_format(b: &mut Bencher) { + // This goes quadratic with linear `fold1`, so use a smaller + // size to not waste too much time in travis. The allocations + // in here are so expensive anyway that it'll still take + // way longer per iteration than the other two benchmarks. + let v: Vec = (0.. ($N/4)).collect(); + b.iter(|| { + cloned(&v).map(|x| x.to_string()).$FUN(|x, y| format!("{} + {}", x, y)) + }); + } + } + ) +} + +def_benchs!{ + 10_000, + fold1, + fold1_10k, +} + +def_benchs!{ + 10_000, + tree_fold1, + tree_fold1_stack_10k, +} + +def_benchs!{ + 10_000, + tree_fold1_vec, + tree_fold1_vec_10k, +} + +def_benchs!{ + 100, + fold1, + fold1_100, +} + +def_benchs!{ + 100, + tree_fold1, + tree_fold1_stack_100, +} + +def_benchs!{ + 100, + tree_fold1_vec, + tree_fold1_vec_100, +} + +def_benchs!{ + 8, + fold1, + fold1_08, +} + +def_benchs!{ + 8, + tree_fold1, + tree_fold1_stack_08, +} + +def_benchs!{ + 8, + tree_fold1_vec, + tree_fold1_vec_08, +} diff --git a/third_party/rust/itertools-0.8.0/benches/tuple_combinations.rs b/third_party/rust/itertools-0.8.0/benches/tuple_combinations.rs new file mode 100644 index 0000000000000..4a14b1d0bd16f --- /dev/null +++ b/third_party/rust/itertools-0.8.0/benches/tuple_combinations.rs @@ -0,0 +1,97 @@ +#![feature(test)] + +extern crate test; +extern crate itertools; + +use test::{black_box, Bencher}; +use itertools::Itertools; + +// approximate 100_000 iterations for each combination +const N1: usize = 100_000; +const N2: usize = 448; +const N3: usize = 86; +const N4: usize = 41; + +#[bench] +fn comb_for1(b: &mut Bencher) { + b.iter(|| { + for i in 0..N1 { + black_box(i); + } + }); +} + +#[bench] +fn comb_for2(b: &mut Bencher) { + b.iter(|| { + for i in 0..N2 { + for j in (i + 1)..N2 { + black_box(i + j); + } + } + }); +} + +#[bench] +fn comb_for3(b: &mut Bencher) { + b.iter(|| { + for i in 0..N3 { + for j in (i + 1)..N3 { + for k in (j + 1)..N3 { + black_box(i + j + k); + } + } + } + }); +} + +#[bench] +fn comb_for4(b: &mut Bencher) { + b.iter(|| { + for i in 0..N4 { + for j in (i + 1)..N4 { + for k in (j + 1)..N4 { + for l in (k + 1)..N4 { + black_box(i + j + k + l); + } + } + } + } + }); +} + +#[bench] +fn comb_c1(b: &mut Bencher) { + b.iter(|| { + for (i,) in (0..N1).tuple_combinations() { + black_box(i); + } + }); +} + +#[bench] +fn comb_c2(b: &mut Bencher) { + b.iter(|| { + for (i, j) in (0..N2).tuple_combinations() { + black_box(i + j); + } + }); +} + +#[bench] +fn comb_c3(b: &mut Bencher) { + b.iter(|| { + for (i, j, k) in (0..N3).tuple_combinations() { + black_box(i + j + k); + } + }); +} + +#[bench] +fn comb_c4(b: &mut Bencher) { + b.iter(|| { + for (i, j, k, l) in (0..N4).tuple_combinations() { + black_box(i + j + k + l); + } + }); +} diff --git a/third_party/rust/itertools-0.8.0/benches/tuples.rs b/third_party/rust/itertools-0.8.0/benches/tuples.rs new file mode 100644 index 0000000000000..b0f2990fdfd31 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/benches/tuples.rs @@ -0,0 +1,190 @@ +#![feature(test)] + +extern crate test; +extern crate itertools; + +use test::Bencher; +use itertools::Itertools; + +fn s1(a: u32) -> u32 { + a +} + +fn s2(a: u32, b: u32) -> u32 { + a + b +} + +fn s3(a: u32, b: u32, c: u32) -> u32 { + a + b + c +} + +fn s4(a: u32, b: u32, c: u32, d: u32) -> u32 { + a + b + c + d +} + +fn sum_s1(s: &[u32]) -> u32 { + s1(s[0]) +} + +fn sum_s2(s: &[u32]) -> u32 { + s2(s[0], s[1]) +} + +fn sum_s3(s: &[u32]) -> u32 { + s3(s[0], s[1], s[2]) +} + +fn sum_s4(s: &[u32]) -> u32 { + s4(s[0], s[1], s[2], s[3]) +} + +fn sum_t1(s: &(&u32, )) -> u32 { + s1(*s.0) +} + +fn sum_t2(s: &(&u32, &u32)) -> u32 { + s2(*s.0, *s.1) +} + +fn sum_t3(s: &(&u32, &u32, &u32)) -> u32 { + s3(*s.0, *s.1, *s.2) +} + +fn sum_t4(s: &(&u32, &u32, &u32, &u32)) -> u32 { + s4(*s.0, *s.1, *s.2, *s.3) +} + +macro_rules! def_benchs { + ($N:expr; + $TUPLE_FUN:ident, + $TUPLES:ident, + $TUPLE_WINDOWS:ident; + $SLICE_FUN:ident, + $CHUNKS:ident, + $WINDOWS:ident; + $FOR_CHUNKS:ident, + $FOR_WINDOWS:ident + ) => ( + #[bench] + fn $FOR_CHUNKS(b: &mut Bencher) { + let v: Vec = (0.. $N * 1_000).collect(); + let mut s = 0; + b.iter(|| { + let mut j = 0; + for _ in 0..1_000 { + s += $SLICE_FUN(&v[j..(j + $N)]); + j += $N; + } + s + }); + } + + #[bench] + fn $FOR_WINDOWS(b: &mut Bencher) { + let v: Vec = (0..1_000).collect(); + let mut s = 0; + b.iter(|| { + for i in 0..(1_000 - $N) { + s += $SLICE_FUN(&v[i..(i + $N)]); + } + s + }); + } + + #[bench] + fn $TUPLES(b: &mut Bencher) { + let v: Vec = (0.. $N * 1_000).collect(); + let mut s = 0; + b.iter(|| { + for x in v.iter().tuples() { + s += $TUPLE_FUN(&x); + } + s + }); + } + + #[bench] + fn $CHUNKS(b: &mut Bencher) { + let v: Vec = (0.. $N * 1_000).collect(); + let mut s = 0; + b.iter(|| { + for x in v.chunks($N) { + s += $SLICE_FUN(x); + } + s + }); + } + + #[bench] + fn $TUPLE_WINDOWS(b: &mut Bencher) { + let v: Vec = (0..1_000).collect(); + let mut s = 0; + b.iter(|| { + for x in v.iter().tuple_windows() { + s += $TUPLE_FUN(&x); + } + s + }); + } + + #[bench] + fn $WINDOWS(b: &mut Bencher) { + let v: Vec = (0..1_000).collect(); + let mut s = 0; + b.iter(|| { + for x in v.windows($N) { + s += $SLICE_FUN(x); + } + s + }); + } + ) +} + +def_benchs!{ + 1; + sum_t1, + tuple_chunks_1, + tuple_windows_1; + sum_s1, + slice_chunks_1, + slice_windows_1; + for_chunks_1, + for_windows_1 +} + +def_benchs!{ + 2; + sum_t2, + tuple_chunks_2, + tuple_windows_2; + sum_s2, + slice_chunks_2, + slice_windows_2; + for_chunks_2, + for_windows_2 +} + +def_benchs!{ + 3; + sum_t3, + tuple_chunks_3, + tuple_windows_3; + sum_s3, + slice_chunks_3, + slice_windows_3; + for_chunks_3, + for_windows_3 +} + +def_benchs!{ + 4; + sum_t4, + tuple_chunks_4, + tuple_windows_4; + sum_s4, + slice_chunks_4, + slice_windows_4; + for_chunks_4, + for_windows_4 +} diff --git a/third_party/rust/itertools-0.8.0/examples/iris.data b/third_party/rust/itertools-0.8.0/examples/iris.data new file mode 100644 index 0000000000000..a3490e0e07dc9 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/examples/iris.data @@ -0,0 +1,150 @@ +5.1,3.5,1.4,0.2,Iris-setosa +4.9,3.0,1.4,0.2,Iris-setosa +4.7,3.2,1.3,0.2,Iris-setosa +4.6,3.1,1.5,0.2,Iris-setosa +5.0,3.6,1.4,0.2,Iris-setosa +5.4,3.9,1.7,0.4,Iris-setosa +4.6,3.4,1.4,0.3,Iris-setosa +5.0,3.4,1.5,0.2,Iris-setosa +4.4,2.9,1.4,0.2,Iris-setosa +4.9,3.1,1.5,0.1,Iris-setosa +5.4,3.7,1.5,0.2,Iris-setosa +4.8,3.4,1.6,0.2,Iris-setosa +4.8,3.0,1.4,0.1,Iris-setosa +4.3,3.0,1.1,0.1,Iris-setosa +5.8,4.0,1.2,0.2,Iris-setosa +5.7,4.4,1.5,0.4,Iris-setosa +5.4,3.9,1.3,0.4,Iris-setosa +5.1,3.5,1.4,0.3,Iris-setosa +5.7,3.8,1.7,0.3,Iris-setosa +5.1,3.8,1.5,0.3,Iris-setosa +5.4,3.4,1.7,0.2,Iris-setosa +5.1,3.7,1.5,0.4,Iris-setosa +4.6,3.6,1.0,0.2,Iris-setosa +5.1,3.3,1.7,0.5,Iris-setosa +4.8,3.4,1.9,0.2,Iris-setosa +5.0,3.0,1.6,0.2,Iris-setosa +5.0,3.4,1.6,0.4,Iris-setosa +5.2,3.5,1.5,0.2,Iris-setosa +5.2,3.4,1.4,0.2,Iris-setosa +4.7,3.2,1.6,0.2,Iris-setosa +4.8,3.1,1.6,0.2,Iris-setosa +5.4,3.4,1.5,0.4,Iris-setosa +5.2,4.1,1.5,0.1,Iris-setosa +5.5,4.2,1.4,0.2,Iris-setosa +4.9,3.1,1.5,0.1,Iris-setosa +5.0,3.2,1.2,0.2,Iris-setosa +5.5,3.5,1.3,0.2,Iris-setosa +4.9,3.1,1.5,0.1,Iris-setosa +4.4,3.0,1.3,0.2,Iris-setosa +5.1,3.4,1.5,0.2,Iris-setosa +5.0,3.5,1.3,0.3,Iris-setosa +4.5,2.3,1.3,0.3,Iris-setosa +4.4,3.2,1.3,0.2,Iris-setosa +5.0,3.5,1.6,0.6,Iris-setosa +5.1,3.8,1.9,0.4,Iris-setosa +4.8,3.0,1.4,0.3,Iris-setosa +5.1,3.8,1.6,0.2,Iris-setosa +4.6,3.2,1.4,0.2,Iris-setosa +5.3,3.7,1.5,0.2,Iris-setosa +5.0,3.3,1.4,0.2,Iris-setosa +7.0,3.2,4.7,1.4,Iris-versicolor +6.4,3.2,4.5,1.5,Iris-versicolor +6.9,3.1,4.9,1.5,Iris-versicolor +5.5,2.3,4.0,1.3,Iris-versicolor +6.5,2.8,4.6,1.5,Iris-versicolor +5.7,2.8,4.5,1.3,Iris-versicolor +6.3,3.3,4.7,1.6,Iris-versicolor +4.9,2.4,3.3,1.0,Iris-versicolor +6.6,2.9,4.6,1.3,Iris-versicolor +5.2,2.7,3.9,1.4,Iris-versicolor +5.0,2.0,3.5,1.0,Iris-versicolor +5.9,3.0,4.2,1.5,Iris-versicolor +6.0,2.2,4.0,1.0,Iris-versicolor +6.1,2.9,4.7,1.4,Iris-versicolor +5.6,2.9,3.6,1.3,Iris-versicolor +6.7,3.1,4.4,1.4,Iris-versicolor +5.6,3.0,4.5,1.5,Iris-versicolor +5.8,2.7,4.1,1.0,Iris-versicolor +6.2,2.2,4.5,1.5,Iris-versicolor +5.6,2.5,3.9,1.1,Iris-versicolor +5.9,3.2,4.8,1.8,Iris-versicolor +6.1,2.8,4.0,1.3,Iris-versicolor +6.3,2.5,4.9,1.5,Iris-versicolor +6.1,2.8,4.7,1.2,Iris-versicolor +6.4,2.9,4.3,1.3,Iris-versicolor +6.6,3.0,4.4,1.4,Iris-versicolor +6.8,2.8,4.8,1.4,Iris-versicolor +6.7,3.0,5.0,1.7,Iris-versicolor +6.0,2.9,4.5,1.5,Iris-versicolor +5.7,2.6,3.5,1.0,Iris-versicolor +5.5,2.4,3.8,1.1,Iris-versicolor +5.5,2.4,3.7,1.0,Iris-versicolor +5.8,2.7,3.9,1.2,Iris-versicolor +6.0,2.7,5.1,1.6,Iris-versicolor +5.4,3.0,4.5,1.5,Iris-versicolor +6.0,3.4,4.5,1.6,Iris-versicolor +6.7,3.1,4.7,1.5,Iris-versicolor +6.3,2.3,4.4,1.3,Iris-versicolor +5.6,3.0,4.1,1.3,Iris-versicolor +5.5,2.5,4.0,1.3,Iris-versicolor +5.5,2.6,4.4,1.2,Iris-versicolor +6.1,3.0,4.6,1.4,Iris-versicolor +5.8,2.6,4.0,1.2,Iris-versicolor +5.0,2.3,3.3,1.0,Iris-versicolor +5.6,2.7,4.2,1.3,Iris-versicolor +5.7,3.0,4.2,1.2,Iris-versicolor +5.7,2.9,4.2,1.3,Iris-versicolor +6.2,2.9,4.3,1.3,Iris-versicolor +5.1,2.5,3.0,1.1,Iris-versicolor +5.7,2.8,4.1,1.3,Iris-versicolor +6.3,3.3,6.0,2.5,Iris-virginica +5.8,2.7,5.1,1.9,Iris-virginica +7.1,3.0,5.9,2.1,Iris-virginica +6.3,2.9,5.6,1.8,Iris-virginica +6.5,3.0,5.8,2.2,Iris-virginica +7.6,3.0,6.6,2.1,Iris-virginica +4.9,2.5,4.5,1.7,Iris-virginica +7.3,2.9,6.3,1.8,Iris-virginica +6.7,2.5,5.8,1.8,Iris-virginica +7.2,3.6,6.1,2.5,Iris-virginica +6.5,3.2,5.1,2.0,Iris-virginica +6.4,2.7,5.3,1.9,Iris-virginica +6.8,3.0,5.5,2.1,Iris-virginica +5.7,2.5,5.0,2.0,Iris-virginica +5.8,2.8,5.1,2.4,Iris-virginica +6.4,3.2,5.3,2.3,Iris-virginica +6.5,3.0,5.5,1.8,Iris-virginica +7.7,3.8,6.7,2.2,Iris-virginica +7.7,2.6,6.9,2.3,Iris-virginica +6.0,2.2,5.0,1.5,Iris-virginica +6.9,3.2,5.7,2.3,Iris-virginica +5.6,2.8,4.9,2.0,Iris-virginica +7.7,2.8,6.7,2.0,Iris-virginica +6.3,2.7,4.9,1.8,Iris-virginica +6.7,3.3,5.7,2.1,Iris-virginica +7.2,3.2,6.0,1.8,Iris-virginica +6.2,2.8,4.8,1.8,Iris-virginica +6.1,3.0,4.9,1.8,Iris-virginica +6.4,2.8,5.6,2.1,Iris-virginica +7.2,3.0,5.8,1.6,Iris-virginica +7.4,2.8,6.1,1.9,Iris-virginica +7.9,3.8,6.4,2.0,Iris-virginica +6.4,2.8,5.6,2.2,Iris-virginica +6.3,2.8,5.1,1.5,Iris-virginica +6.1,2.6,5.6,1.4,Iris-virginica +7.7,3.0,6.1,2.3,Iris-virginica +6.3,3.4,5.6,2.4,Iris-virginica +6.4,3.1,5.5,1.8,Iris-virginica +6.0,3.0,4.8,1.8,Iris-virginica +6.9,3.1,5.4,2.1,Iris-virginica +6.7,3.1,5.6,2.4,Iris-virginica +6.9,3.1,5.1,2.3,Iris-virginica +5.8,2.7,5.1,1.9,Iris-virginica +6.8,3.2,5.9,2.3,Iris-virginica +6.7,3.3,5.7,2.5,Iris-virginica +6.7,3.0,5.2,2.3,Iris-virginica +6.3,2.5,5.0,1.9,Iris-virginica +6.5,3.0,5.2,2.0,Iris-virginica +6.2,3.4,5.4,2.3,Iris-virginica +5.9,3.0,5.1,1.8,Iris-virginica diff --git a/third_party/rust/itertools-0.8.0/examples/iris.rs b/third_party/rust/itertools-0.8.0/examples/iris.rs new file mode 100644 index 0000000000000..c09afbea0d324 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/examples/iris.rs @@ -0,0 +1,141 @@ +/// +/// This example parses, sorts and groups the iris dataset +/// and does some simple manipulations. +/// +/// Iterators and itertools functionality are used throughout. +/// +/// + +extern crate itertools; + +use itertools::Itertools; +use std::collections::HashMap; +use std::iter::repeat; +use std::num::ParseFloatError; +use std::str::FromStr; + +static DATA: &'static str = include_str!("iris.data"); + +#[derive(Clone, Debug)] +struct Iris { + name: String, + data: [f32; 4], +} + +#[derive(Clone, Debug)] +enum ParseError { + Numeric(ParseFloatError), + Other(&'static str), +} + +impl From for ParseError { + fn from(err: ParseFloatError) -> Self { + ParseError::Numeric(err) + } +} + +/// Parse an Iris from a comma-separated line +impl FromStr for Iris { + type Err = ParseError; + + fn from_str(s: &str) -> Result { + let mut iris = Iris { name: "".into(), data: [0.; 4] }; + let mut parts = s.split(",").map(str::trim); + + // using Iterator::by_ref() + for (index, part) in parts.by_ref().take(4).enumerate() { + iris.data[index] = try!(part.parse::()); + } + if let Some(name) = parts.next() { + iris.name = name.into(); + } else { + return Err(ParseError::Other("Missing name")) + } + Ok(iris) + } +} + +fn main() { + // using Itertools::fold_results to create the result of parsing + let irises = DATA.lines() + .map(str::parse) + .fold_results(Vec::new(), |mut v, iris: Iris| { + v.push(iris); + v + }); + let mut irises = match irises { + Err(e) => { + println!("Error parsing: {:?}", e); + std::process::exit(1); + } + Ok(data) => data, + }; + + // Sort them and group them + irises.sort_by(|a, b| Ord::cmp(&a.name, &b.name)); + + // using Iterator::cycle() + let mut plot_symbols = "+ox".chars().cycle(); + let mut symbolmap = HashMap::new(); + + // using Itertools::group_by + for (species, species_group) in &irises.iter().group_by(|iris| &iris.name) { + // assign a plot symbol + symbolmap.entry(species).or_insert_with(|| { + plot_symbols.next().unwrap() + }); + println!("{} (symbol={})", species, symbolmap[species]); + + for iris in species_group { + // using Itertools::format for lazy formatting + println!("{:>3.1}", iris.data.iter().format(", ")); + } + + } + + // Look at all combinations of the four columns + // + // See https://en.wikipedia.org/wiki/Iris_flower_data_set + // + let n = 30; // plot size + let mut plot = vec![' '; n * n]; + + // using Itertools::tuple_combinations + for (a, b) in (0..4).tuple_combinations() { + println!("Column {} vs {}:", a, b); + + // Clear plot + // + // using std::iter::repeat; + // using Itertools::set_from + plot.iter_mut().set_from(repeat(' ')); + + // using Itertools::minmax + let min_max = |data: &[Iris], col| { + data.iter() + .map(|iris| iris.data[col]) + .minmax() + .into_option() + .expect("Can't find min/max of empty iterator") + }; + let (min_x, max_x) = min_max(&irises, a); + let (min_y, max_y) = min_max(&irises, b); + + // Plot the data points + let round_to_grid = |x, min, max| ((x - min) / (max - min) * ((n - 1) as f32)) as usize; + let flip = |ix| n - 1 - ix; // reverse axis direction + + for iris in &irises { + let ix = round_to_grid(iris.data[a], min_x, max_x); + let iy = flip(round_to_grid(iris.data[b], min_y, max_y)); + plot[n * iy + ix] = symbolmap[&iris.name]; + } + + // render plot + // + // using Itertools::join + for line in plot.chunks(n) { + println!("{}", line.iter().join(" ")) + } + } +} diff --git a/third_party/rust/itertools-0.8.0/src/adaptors/mod.rs b/third_party/rust/itertools-0.8.0/src/adaptors/mod.rs new file mode 100644 index 0000000000000..59aebfaf068cc --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/adaptors/mod.rs @@ -0,0 +1,1278 @@ +//! 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. This file may not be copied, modified, or distributed +//! except according to those terms. + +mod multi_product; +#[cfg(feature = "use_std")] +pub use self::multi_product::*; + +use std::fmt; +use std::mem::replace; +use std::iter::{Fuse, Peekable, FromIterator}; +use std::marker::PhantomData; +use size_hint; + +macro_rules! clone_fields { + ($name:ident, $base:expr, $($field:ident),+) => ( + $name { + $( + $field : $base . $field .clone() + ),* + } + ); +} + +/// An iterator adaptor that alternates elements from two iterators until both +/// run out. +/// +/// This iterator is *fused*. +/// +/// See [`.interleave()`](../trait.Itertools.html#method.interleave) for more information. +#[derive(Clone, Debug)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Interleave { + a: Fuse, + b: Fuse, + flag: bool, +} + +/// Create an iterator that interleaves elements in `i` and `j`. +/// +/// `IntoIterator` enabled version of `i.interleave(j)`. +/// +/// ``` +/// use itertools::interleave; +/// +/// for elt in interleave(&[1, 2, 3], &[2, 3, 4]) { +/// /* loop body */ +/// } +/// ``` +pub fn interleave(i: I, j: J) -> Interleave<::IntoIter, ::IntoIter> + where I: IntoIterator, + J: IntoIterator +{ + Interleave { + a: i.into_iter().fuse(), + b: j.into_iter().fuse(), + flag: false, + } +} + +impl Iterator for Interleave + where I: Iterator, + J: Iterator +{ + type Item = I::Item; + #[inline] + fn next(&mut self) -> Option { + self.flag = !self.flag; + if self.flag { + match self.a.next() { + None => self.b.next(), + r => r, + } + } else { + match self.b.next() { + None => self.a.next(), + r => r, + } + } + } + + fn size_hint(&self) -> (usize, Option) { + size_hint::add(self.a.size_hint(), self.b.size_hint()) + } +} + +/// An iterator adaptor that alternates elements from the two iterators until +/// one of them runs out. +/// +/// This iterator is *fused*. +/// +/// See [`.interleave_shortest()`](../trait.Itertools.html#method.interleave_shortest) +/// for more information. +#[derive(Clone, Debug)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct InterleaveShortest + where I: Iterator, + J: Iterator +{ + it0: I, + it1: J, + phase: bool, // false ==> it0, true ==> it1 +} + +/// Create a new `InterleaveShortest` iterator. +pub fn interleave_shortest(a: I, b: J) -> InterleaveShortest + where I: Iterator, + J: Iterator +{ + InterleaveShortest { + it0: a, + it1: b, + phase: false, + } +} + +impl Iterator for InterleaveShortest + where I: Iterator, + J: Iterator +{ + type Item = I::Item; + + #[inline] + fn next(&mut self) -> Option { + match self.phase { + false => match self.it0.next() { + None => None, + e => { + self.phase = true; + e + } + }, + true => match self.it1.next() { + None => None, + e => { + self.phase = false; + e + } + }, + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (curr_hint, next_hint) = { + let it0_hint = self.it0.size_hint(); + let it1_hint = self.it1.size_hint(); + if self.phase { + (it1_hint, it0_hint) + } else { + (it0_hint, it1_hint) + } + }; + let (curr_lower, curr_upper) = curr_hint; + let (next_lower, next_upper) = next_hint; + let (combined_lower, combined_upper) = + size_hint::mul_scalar(size_hint::min(curr_hint, next_hint), 2); + let lower = + if curr_lower > next_lower { + combined_lower + 1 + } else { + combined_lower + }; + let upper = { + let extra_elem = match (curr_upper, next_upper) { + (_, None) => false, + (None, Some(_)) => true, + (Some(curr_max), Some(next_max)) => curr_max > next_max, + }; + if extra_elem { + combined_upper.and_then(|x| x.checked_add(1)) + } else { + combined_upper + } + }; + (lower, upper) + } +} + +#[derive(Clone, Debug)] +/// An iterator adaptor that allows putting back a single +/// item to the front of the iterator. +/// +/// Iterator element type is `I::Item`. +pub struct PutBack + where I: Iterator +{ + top: Option, + iter: I, +} + +/// Create an iterator where you can put back a single item +pub fn put_back(iterable: I) -> PutBack + where I: IntoIterator +{ + PutBack { + top: None, + iter: iterable.into_iter(), + } +} + +impl PutBack + where I: Iterator +{ + /// put back value `value` (builder method) + pub fn with_value(mut self, value: I::Item) -> Self { + self.put_back(value); + self + } + + /// Split the `PutBack` into its parts. + #[inline] + pub fn into_parts(self) -> (Option, I) { + let PutBack{top, iter} = self; + (top, iter) + } + + /// Put back a single value to the front of the iterator. + /// + /// If a value is already in the put back slot, it is overwritten. + #[inline] + pub fn put_back(&mut self, x: I::Item) { + self.top = Some(x) + } +} + +impl Iterator for PutBack + where I: Iterator +{ + type Item = I::Item; + #[inline] + fn next(&mut self) -> Option { + match self.top { + None => self.iter.next(), + ref mut some => some.take(), + } + } + #[inline] + fn size_hint(&self) -> (usize, Option) { + // Not ExactSizeIterator because size may be larger than usize + size_hint::add_scalar(self.iter.size_hint(), self.top.is_some() as usize) + } + + fn all(&mut self, mut f: G) -> bool + where G: FnMut(Self::Item) -> bool + { + if let Some(elt) = self.top.take() { + if !f(elt) { + return false; + } + } + self.iter.all(f) + } + + fn fold(mut self, init: Acc, mut f: G) -> Acc + where G: FnMut(Acc, Self::Item) -> Acc, + { + let mut accum = init; + if let Some(elt) = self.top.take() { + accum = f(accum, elt); + } + self.iter.fold(accum, f) + } +} + +#[derive(Debug, Clone)] +/// An iterator adaptor that iterates over the cartesian product of +/// the element sets of two iterators `I` and `J`. +/// +/// Iterator element type is `(I::Item, J::Item)`. +/// +/// See [`.cartesian_product()`](../trait.Itertools.html#method.cartesian_product) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Product + where I: Iterator +{ + a: I, + a_cur: Option, + b: J, + b_orig: J, +} + +/// Create a new cartesian product iterator +/// +/// Iterator element type is `(I::Item, J::Item)`. +pub fn cartesian_product(mut i: I, j: J) -> Product + where I: Iterator, + J: Clone + Iterator, + I::Item: Clone +{ + Product { + a_cur: i.next(), + a: i, + b: j.clone(), + b_orig: j, + } +} + + +impl Iterator for Product + where I: Iterator, + J: Clone + Iterator, + I::Item: Clone +{ + type Item = (I::Item, J::Item); + fn next(&mut self) -> Option<(I::Item, J::Item)> { + let elt_b = match self.b.next() { + None => { + self.b = self.b_orig.clone(); + match self.b.next() { + None => return None, + Some(x) => { + self.a_cur = self.a.next(); + x + } + } + } + Some(x) => x + }; + match self.a_cur { + None => None, + Some(ref a) => { + Some((a.clone(), elt_b)) + } + } + } + + fn size_hint(&self) -> (usize, Option) { + let has_cur = self.a_cur.is_some() as usize; + // Not ExactSizeIterator because size may be larger than usize + let (b_min, b_max) = self.b.size_hint(); + + // Compute a * b_orig + b for both lower and upper bound + size_hint::add( + size_hint::mul(self.a.size_hint(), self.b_orig.size_hint()), + (b_min * has_cur, b_max.map(move |x| x * has_cur))) + } + + fn fold(mut self, mut accum: Acc, mut f: G) -> Acc + where G: FnMut(Acc, Self::Item) -> Acc, + { + // use a split loop to handle the loose a_cur as well as avoiding to + // clone b_orig at the end. + if let Some(mut a) = self.a_cur.take() { + let mut b = self.b; + loop { + accum = b.fold(accum, |acc, elt| f(acc, (a.clone(), elt))); + + // we can only continue iterating a if we had a first element; + if let Some(next_a) = self.a.next() { + b = self.b_orig.clone(); + a = next_a; + } else { + break; + } + } + } + accum + } +} + +/// A “meta iterator adaptor”. Its closure receives a reference to the iterator +/// and may pick off as many elements as it likes, to produce the next iterator element. +/// +/// Iterator element type is *X*, if the return type of `F` is *Option\*. +/// +/// See [`.batching()`](../trait.Itertools.html#method.batching) for more information. +#[derive(Clone)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Batching { + f: F, + iter: I, +} + +impl fmt::Debug for Batching where I: fmt::Debug { + debug_fmt_fields!(Batching, iter); +} + +/// Create a new Batching iterator. +pub fn batching(iter: I, f: F) -> Batching { + Batching { f: f, iter: iter } +} + +impl Iterator for Batching + where I: Iterator, + F: FnMut(&mut I) -> Option +{ + type Item = B; + #[inline] + fn next(&mut self) -> Option { + (self.f)(&mut self.iter) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + // No information about closue behavior + (0, None) + } +} + +/// An iterator adaptor that steps a number elements in the base iterator +/// for each iteration. +/// +/// The iterator steps by yielding the next element from the base iterator, +/// then skipping forward *n-1* elements. +/// +/// See [`.step()`](../trait.Itertools.html#method.step) for more information. +#[deprecated(note="Use std .step_by() instead", since="0.8")] +#[allow(deprecated)] +#[derive(Clone, Debug)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Step { + iter: Fuse, + skip: usize, +} + +/// Create a `Step` iterator. +/// +/// **Panics** if the step is 0. +#[allow(deprecated)] +pub fn step(iter: I, step: usize) -> Step + where I: Iterator +{ + assert!(step != 0); + Step { + iter: iter.fuse(), + skip: step - 1, + } +} + +#[allow(deprecated)] +impl Iterator for Step + where I: Iterator +{ + type Item = I::Item; + #[inline] + fn next(&mut self) -> Option { + let elt = self.iter.next(); + if self.skip > 0 { + self.iter.nth(self.skip - 1); + } + elt + } + + fn size_hint(&self) -> (usize, Option) { + let (low, high) = self.iter.size_hint(); + let div = |x: usize| { + if x == 0 { + 0 + } else { + 1 + (x - 1) / (self.skip + 1) + } + }; + (div(low), high.map(div)) + } +} + +// known size +#[allow(deprecated)] +impl ExactSizeIterator for Step + where I: ExactSizeIterator +{} + + +struct MergeCore + where I: Iterator, + J: Iterator +{ + a: Peekable, + b: Peekable, + fused: Option, +} + + +impl Clone for MergeCore + where I: Iterator, + J: Iterator, + Peekable: Clone, + Peekable: Clone +{ + fn clone(&self) -> Self { + clone_fields!(MergeCore, self, a, b, fused) + } +} + +impl MergeCore + where I: Iterator, + J: Iterator +{ + fn next_with(&mut self, mut less_than: F) -> Option + where F: FnMut(&I::Item, &I::Item) -> bool + { + let less_than = match self.fused { + Some(lt) => lt, + None => match (self.a.peek(), self.b.peek()) { + (Some(a), Some(b)) => less_than(a, b), + (Some(_), None) => { + self.fused = Some(true); + true + } + (None, Some(_)) => { + self.fused = Some(false); + false + } + (None, None) => return None, + } + }; + + if less_than { + self.a.next() + } else { + self.b.next() + } + } + + fn size_hint(&self) -> (usize, Option) { + // Not ExactSizeIterator because size may be larger than usize + size_hint::add(self.a.size_hint(), self.b.size_hint()) + } +} + +/// An iterator adaptor that merges the two base iterators in ascending order. +/// If both base iterators are sorted (ascending), the result is sorted. +/// +/// Iterator element type is `I::Item`. +/// +/// See [`.merge()`](../trait.Itertools.html#method.merge_by) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Merge + where I: Iterator, + J: Iterator +{ + merge: MergeCore, +} + +impl Clone for Merge + where I: Iterator, + J: Iterator, + Peekable: Clone, + Peekable: Clone +{ + fn clone(&self) -> Self { + clone_fields!(Merge, self, merge) + } +} + +impl fmt::Debug for Merge + where I: Iterator + fmt::Debug, J: Iterator + fmt::Debug, + I::Item: fmt::Debug, +{ + debug_fmt_fields!(Merge, merge.a, merge.b); +} + +/// Create an iterator that merges elements in `i` and `j`. +/// +/// `IntoIterator` enabled version of `i.merge(j)`. +/// +/// ``` +/// use itertools::merge; +/// +/// for elt in merge(&[1, 2, 3], &[2, 3, 4]) { +/// /* loop body */ +/// } +/// ``` +pub fn merge(i: I, j: J) -> Merge<::IntoIter, ::IntoIter> + where I: IntoIterator, + J: IntoIterator, + I::Item: PartialOrd +{ + Merge { + merge: MergeCore { + a: i.into_iter().peekable(), + b: j.into_iter().peekable(), + fused: None, + }, + } +} + +impl Iterator for Merge + where I: Iterator, + J: Iterator, + I::Item: PartialOrd +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + self.merge.next_with(|a, b| a <= b) + } + + fn size_hint(&self) -> (usize, Option) { + self.merge.size_hint() + } +} + +/// An iterator adaptor that merges the two base iterators in ascending order. +/// If both base iterators are sorted (ascending), the result is sorted. +/// +/// Iterator element type is `I::Item`. +/// +/// See [`.merge_by()`](../trait.Itertools.html#method.merge_by) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct MergeBy + where I: Iterator, + J: Iterator +{ + merge: MergeCore, + cmp: F, +} + +impl fmt::Debug for MergeBy + where I: Iterator + fmt::Debug, J: Iterator + fmt::Debug, + I::Item: fmt::Debug, +{ + debug_fmt_fields!(MergeBy, merge.a, merge.b); +} + +/// Create a `MergeBy` iterator. +pub fn merge_by_new(a: I, b: J, cmp: F) -> MergeBy + where I: Iterator, + J: Iterator +{ + MergeBy { + merge: MergeCore { + a: a.peekable(), + b: b.peekable(), + fused: None, + }, + cmp: cmp, + } +} + +impl Clone for MergeBy + where I: Iterator, + J: Iterator, + Peekable: Clone, + Peekable: Clone, + F: Clone +{ + fn clone(&self) -> Self { + clone_fields!(MergeBy, self, merge, cmp) + } +} + +impl Iterator for MergeBy + where I: Iterator, + J: Iterator, + F: FnMut(&I::Item, &I::Item) -> bool +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + self.merge.next_with(&mut self.cmp) + } + + fn size_hint(&self) -> (usize, Option) { + self.merge.size_hint() + } +} + +#[derive(Clone, Debug)] +pub struct CoalesceCore + where I: Iterator +{ + iter: I, + last: Option, +} + +impl CoalesceCore + where I: Iterator +{ + fn next_with(&mut self, mut f: F) -> Option + where F: FnMut(I::Item, I::Item) -> Result + { + // this fuses the iterator + let mut last = match self.last.take() { + None => return None, + Some(x) => x, + }; + for next in &mut self.iter { + match f(last, next) { + Ok(joined) => last = joined, + Err((last_, next_)) => { + self.last = Some(next_); + return Some(last_); + } + } + } + + Some(last) + } + + fn size_hint(&self) -> (usize, Option) { + let (low, hi) = size_hint::add_scalar(self.iter.size_hint(), + self.last.is_some() as usize); + ((low > 0) as usize, hi) + } +} + +/// An iterator adaptor that may join together adjacent elements. +/// +/// See [`.coalesce()`](../trait.Itertools.html#method.coalesce) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Coalesce + where I: Iterator +{ + iter: CoalesceCore, + f: F, +} + +impl Clone for Coalesce + where I: Iterator, + I::Item: Clone +{ + fn clone(&self) -> Self { + clone_fields!(Coalesce, self, iter, f) + } +} + +impl fmt::Debug for Coalesce + where I: Iterator + fmt::Debug, + I::Item: fmt::Debug, +{ + debug_fmt_fields!(Coalesce, iter); +} + +/// Create a new `Coalesce`. +pub fn coalesce(mut iter: I, f: F) -> Coalesce + where I: Iterator +{ + Coalesce { + iter: CoalesceCore { + last: iter.next(), + iter: iter, + }, + f: f, + } +} + +impl Iterator for Coalesce + where I: Iterator, + F: FnMut(I::Item, I::Item) -> Result +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + self.iter.next_with(&mut self.f) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +/// An iterator adaptor that removes repeated duplicates. +/// +/// See [`.dedup()`](../trait.Itertools.html#method.dedup) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Dedup + where I: Iterator +{ + iter: CoalesceCore, +} + +impl Clone for Dedup + where I: Iterator, + I::Item: Clone +{ + fn clone(&self) -> Self { + clone_fields!(Dedup, self, iter) + } +} + +/// Create a new `Dedup`. +pub fn dedup(mut iter: I) -> Dedup + where I: Iterator +{ + Dedup { + iter: CoalesceCore { + last: iter.next(), + iter: iter, + }, + } +} + +impl fmt::Debug for Dedup + where I: Iterator + fmt::Debug, + I::Item: fmt::Debug, +{ + debug_fmt_fields!(Dedup, iter); +} + +impl Iterator for Dedup + where I: Iterator, + I::Item: PartialEq +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + self.iter.next_with(|x, y| { + if x == y { Ok(x) } else { Err((x, y)) } + }) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + fn fold(self, mut accum: Acc, mut f: G) -> Acc + where G: FnMut(Acc, Self::Item) -> Acc, + { + if let Some(mut last) = self.iter.last { + accum = self.iter.iter.fold(accum, |acc, elt| { + if elt == last { + acc + } else { + f(acc, replace(&mut last, elt)) + } + }); + f(accum, last) + } else { + accum + } + } +} + +/// An iterator adaptor that borrows from a `Clone`-able iterator +/// to only pick off elements while the predicate returns `true`. +/// +/// See [`.take_while_ref()`](../trait.Itertools.html#method.take_while_ref) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct TakeWhileRef<'a, I: 'a, F> { + iter: &'a mut I, + f: F, +} + +impl<'a, I, F> fmt::Debug for TakeWhileRef<'a, I, F> + where I: Iterator + fmt::Debug, +{ + debug_fmt_fields!(TakeWhileRef, iter); +} + +/// Create a new `TakeWhileRef` from a reference to clonable iterator. +pub fn take_while_ref(iter: &mut I, f: F) -> TakeWhileRef + where I: Iterator + Clone +{ + TakeWhileRef { iter: iter, f: f } +} + +impl<'a, I, F> Iterator for TakeWhileRef<'a, I, F> + where I: Iterator + Clone, + F: FnMut(&I::Item) -> bool +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + let old = self.iter.clone(); + match self.iter.next() { + None => None, + Some(elt) => { + if (self.f)(&elt) { + Some(elt) + } else { + *self.iter = old; + None + } + } + } + } + + fn size_hint(&self) -> (usize, Option) { + let (_, hi) = self.iter.size_hint(); + (0, hi) + } +} + +/// An iterator adaptor that filters `Option` iterator elements +/// and produces `A`. Stops on the first `None` encountered. +/// +/// See [`.while_some()`](../trait.Itertools.html#method.while_some) for more information. +#[derive(Clone, Debug)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct WhileSome { + iter: I, +} + +/// Create a new `WhileSome`. +pub fn while_some(iter: I) -> WhileSome { + WhileSome { iter: iter } +} + +impl Iterator for WhileSome + where I: Iterator> +{ + type Item = A; + + fn next(&mut self) -> Option { + match self.iter.next() { + None | Some(None) => None, + Some(elt) => elt, + } + } + + fn size_hint(&self) -> (usize, Option) { + let sh = self.iter.size_hint(); + (0, sh.1) + } +} + +/// An iterator to iterate through all combinations in a `Clone`-able iterator that produces tuples +/// of a specific size. +/// +/// See [`.tuple_combinations()`](../trait.Itertools.html#method.tuple_combinations) for more +/// information. +#[derive(Debug)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct TupleCombinations + where I: Iterator, + T: HasCombination +{ + iter: T::Combination, + _mi: PhantomData, + _mt: PhantomData +} + +pub trait HasCombination: Sized { + type Combination: From + Iterator; +} + +/// Create a new `TupleCombinations` from a clonable iterator. +pub fn tuple_combinations(iter: I) -> TupleCombinations + where I: Iterator + Clone, + I::Item: Clone, + T: HasCombination, +{ + TupleCombinations { + iter: T::Combination::from(iter), + _mi: PhantomData, + _mt: PhantomData, + } +} + +impl Iterator for TupleCombinations + where I: Iterator, + T: HasCombination, +{ + type Item = T; + + fn next(&mut self) -> Option { + self.iter.next() + } +} + +#[derive(Debug)] +pub struct Tuple1Combination { + iter: I, +} + +impl From for Tuple1Combination { + fn from(iter: I) -> Self { + Tuple1Combination { iter: iter } + } +} + +impl Iterator for Tuple1Combination { + type Item = (I::Item,); + + fn next(&mut self) -> Option { + self.iter.next().map(|x| (x,)) + } +} + +impl HasCombination for (I::Item,) { + type Combination = Tuple1Combination; +} + +macro_rules! impl_tuple_combination { + ($C:ident $P:ident ; $A:ident, $($I:ident),* ; $($X:ident)*) => ( + #[derive(Debug)] + pub struct $C { + item: Option, + iter: I, + c: $P, + } + + impl From for $C { + fn from(mut iter: I) -> Self { + $C { + item: iter.next(), + iter: iter.clone(), + c: $P::from(iter), + } + } + } + + impl From for $C> { + fn from(iter: I) -> Self { + let mut iter = iter.fuse(); + $C { + item: iter.next(), + iter: iter.clone(), + c: $P::from(iter), + } + } + } + + impl Iterator for $C + where I: Iterator + Clone, + I::Item: Clone + { + type Item = ($($I),*); + + fn next(&mut self) -> Option { + if let Some(($($X),*,)) = self.c.next() { + let z = self.item.clone().unwrap(); + Some((z, $($X),*)) + } else { + self.item = self.iter.next(); + self.item.clone().and_then(|z| { + self.c = $P::from(self.iter.clone()); + self.c.next().map(|($($X),*,)| (z, $($X),*)) + }) + } + } + } + + impl HasCombination for ($($I),*) + where I: Iterator + Clone, + I::Item: Clone + { + type Combination = $C>; + } + ) +} + +impl_tuple_combination!(Tuple2Combination Tuple1Combination ; A, A, A ; a); +impl_tuple_combination!(Tuple3Combination Tuple2Combination ; A, A, A, A ; a b); +impl_tuple_combination!(Tuple4Combination Tuple3Combination ; A, A, A, A, A; a b c); + +/// An iterator adapter to apply `Into` conversion to each element. +/// +/// See [`.map_into()`](../trait.Itertools.html#method.map_into) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct MapInto { + iter: I, + _res: PhantomData R>, +} + +/// Create a new [`MapInto`](struct.MapInto.html) iterator. +pub fn map_into(iter: I) -> MapInto { + MapInto { + iter: iter, + _res: PhantomData, + } +} + +impl Iterator for MapInto + where I: Iterator, + I::Item: Into, +{ + type Item = R; + + fn next(&mut self) -> Option { + self.iter + .next() + .map(|i| i.into()) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + fn fold(self, init: Acc, mut fold_f: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.fold(init, move |acc, v| fold_f(acc, v.into())) + } +} + +impl DoubleEndedIterator for MapInto + where I: DoubleEndedIterator, + I::Item: Into, +{ + fn next_back(&mut self) -> Option { + self.iter + .next_back() + .map(|i| i.into()) + } +} + +impl ExactSizeIterator for MapInto +where + I: ExactSizeIterator, + I::Item: Into, +{} + +/// An iterator adapter to apply a transformation within a nested `Result`. +/// +/// See [`.map_results()`](../trait.Itertools.html#method.map_results) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct MapResults { + iter: I, + f: F +} + +/// Create a new `MapResults` iterator. +pub fn map_results(iter: I, f: F) -> MapResults + where I: Iterator>, + F: FnMut(T) -> U, +{ + MapResults { + iter: iter, + f: f, + } +} + +impl Iterator for MapResults + where I: Iterator>, + F: FnMut(T) -> U, +{ + type Item = Result; + + fn next(&mut self) -> Option { + self.iter.next().map(|v| v.map(&mut self.f)) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + fn fold(self, init: Acc, mut fold_f: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.fold(init, move |acc, v| fold_f(acc, v.map(&mut f))) + } + + fn collect(self) -> C + where C: FromIterator + { + let mut f = self.f; + self.iter.map(move |v| v.map(&mut f)).collect() + } +} + +/// An iterator adapter to get the positions of each element that matches a predicate. +/// +/// See [`.positions()`](../trait.Itertools.html#method.positions) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Positions { + iter: I, + f: F, + count: usize, +} + +/// Create a new `Positions` iterator. +pub fn positions(iter: I, f: F) -> Positions + where I: Iterator, + F: FnMut(I::Item) -> bool, +{ + Positions { + iter: iter, + f: f, + count: 0 + } +} + +impl Iterator for Positions + where I: Iterator, + F: FnMut(I::Item) -> bool, +{ + type Item = usize; + + fn next(&mut self) -> Option { + while let Some(v) = self.iter.next() { + let i = self.count; + self.count = i + 1; + if (self.f)(v) { + return Some(i); + } + } + None + } + + fn size_hint(&self) -> (usize, Option) { + (0, self.iter.size_hint().1) + } +} + +impl DoubleEndedIterator for Positions + where I: DoubleEndedIterator + ExactSizeIterator, + F: FnMut(I::Item) -> bool, +{ + fn next_back(&mut self) -> Option { + while let Some(v) = self.iter.next_back() { + if (self.f)(v) { + return Some(self.count + self.iter.len()) + } + } + None + } +} + +/// An iterator adapter to apply a mutating function to each element before yielding it. +/// +/// See [`.update()`](../trait.Itertools.html#method.update) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Update { + iter: I, + f: F, +} + +/// Create a new `Update` iterator. +pub fn update(iter: I, f: F) -> Update +where + I: Iterator, + F: FnMut(&mut I::Item), +{ + Update { iter: iter, f: f } +} + +impl Iterator for Update +where + I: Iterator, + F: FnMut(&mut I::Item), +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + if let Some(mut v) = self.iter.next() { + (self.f)(&mut v); + Some(v) + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + fn fold(self, init: Acc, mut g: G) -> Acc + where G: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.fold(init, move |acc, mut v| { f(&mut v); g(acc, v) }) + } + + // if possible, re-use inner iterator specializations in collect + fn collect(self) -> C + where C: FromIterator + { + let mut f = self.f; + self.iter.map(move |mut v| { f(&mut v); v }).collect() + } +} + +impl ExactSizeIterator for Update +where + I: ExactSizeIterator, + F: FnMut(&mut I::Item), +{} + +impl DoubleEndedIterator for Update +where + I: DoubleEndedIterator, + F: FnMut(&mut I::Item), +{ + fn next_back(&mut self) -> Option { + if let Some(mut v) = self.iter.next_back() { + (self.f)(&mut v); + Some(v) + } else { + None + } + } +} diff --git a/third_party/rust/itertools-0.8.0/src/adaptors/multi_product.rs b/third_party/rust/itertools-0.8.0/src/adaptors/multi_product.rs new file mode 100644 index 0000000000000..a6796386ed101 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/adaptors/multi_product.rs @@ -0,0 +1,220 @@ +#![cfg(feature = "use_std")] + +use size_hint; +use Itertools; + +#[derive(Clone)] +/// An iterator adaptor that iterates over the cartesian product of +/// multiple iterators of type `I`. +/// +/// An iterator element type is `Vec`. +/// +/// See [`.multi_cartesian_product()`](../trait.Itertools.html#method.multi_cartesian_product) +/// for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct MultiProduct(Vec>) + where I: Iterator + Clone, + I::Item: Clone; + +/// Create a new cartesian product iterator over an arbitrary number +/// of iterators of the same type. +/// +/// Iterator element is of type `Vec`. +pub fn multi_cartesian_product(iters: H) -> MultiProduct<::IntoIter> + where H: Iterator, + H::Item: IntoIterator, + ::IntoIter: Clone, + ::Item: Clone +{ + MultiProduct(iters.map(|i| MultiProductIter::new(i.into_iter())).collect()) +} + +#[derive(Clone, Debug)] +/// Holds the state of a single iterator within a MultiProduct. +struct MultiProductIter + where I: Iterator + Clone, + I::Item: Clone +{ + cur: Option, + iter: I, + iter_orig: I, +} + +/// Holds the current state during an iteration of a MultiProduct. +#[derive(Debug)] +enum MultiProductIterState { + StartOfIter, + MidIter { on_first_iter: bool }, +} + +impl MultiProduct + where I: Iterator + Clone, + I::Item: Clone +{ + /// Iterates the rightmost iterator, then recursively iterates iterators + /// to the left if necessary. + /// + /// Returns true if the iteration succeeded, else false. + fn iterate_last( + multi_iters: &mut [MultiProductIter], + mut state: MultiProductIterState + ) -> bool { + use self::MultiProductIterState::*; + + if let Some((last, rest)) = multi_iters.split_last_mut() { + let on_first_iter = match state { + StartOfIter => { + let on_first_iter = !last.in_progress(); + state = MidIter { on_first_iter: on_first_iter }; + on_first_iter + }, + MidIter { on_first_iter } => on_first_iter + }; + + if !on_first_iter { + last.iterate(); + } + + if last.in_progress() { + true + } else if MultiProduct::iterate_last(rest, state) { + last.reset(); + last.iterate(); + // If iterator is None twice consecutively, then iterator is + // empty; whole product is empty. + last.in_progress() + } else { + false + } + } else { + // Reached end of iterator list. On initialisation, return true. + // At end of iteration (final iterator finishes), finish. + match state { + StartOfIter => false, + MidIter { on_first_iter } => on_first_iter + } + } + } + + /// Returns the unwrapped value of the next iteration. + fn curr_iterator(&self) -> Vec { + self.0.iter().map(|multi_iter| { + multi_iter.cur.clone().unwrap() + }).collect() + } + + /// Returns true if iteration has started and has not yet finished; false + /// otherwise. + fn in_progress(&self) -> bool { + if let Some(last) = self.0.last() { + last.in_progress() + } else { + false + } + } +} + +impl MultiProductIter + where I: Iterator + Clone, + I::Item: Clone +{ + fn new(iter: I) -> Self { + MultiProductIter { + cur: None, + iter: iter.clone(), + iter_orig: iter + } + } + + /// Iterate the managed iterator. + fn iterate(&mut self) { + self.cur = self.iter.next(); + } + + /// Reset the managed iterator. + fn reset(&mut self) { + self.iter = self.iter_orig.clone(); + } + + /// Returns true if the current iterator has been started and has not yet + /// finished; false otherwise. + fn in_progress(&self) -> bool { + self.cur.is_some() + } +} + +impl Iterator for MultiProduct + where I: Iterator + Clone, + I::Item: Clone +{ + type Item = Vec; + + fn next(&mut self) -> Option { + if MultiProduct::iterate_last( + &mut self.0, + MultiProductIterState::StartOfIter + ) { + Some(self.curr_iterator()) + } else { + None + } + } + + fn count(self) -> usize { + if self.0.len() == 0 { + return 0; + } + + if !self.in_progress() { + return self.0.into_iter().fold(1, |acc, multi_iter| { + acc * multi_iter.iter.count() + }); + } + + self.0.into_iter().fold( + 0, + |acc, MultiProductIter { iter, iter_orig, cur: _ }| { + let total_count = iter_orig.count(); + let cur_count = iter.count(); + acc * total_count + cur_count + } + ) + } + + fn size_hint(&self) -> (usize, Option) { + // Not ExactSizeIterator because size may be larger than usize + if self.0.len() == 0 { + return (0, Some(0)); + } + + if !self.in_progress() { + return self.0.iter().fold((1, Some(1)), |acc, multi_iter| { + size_hint::mul(acc, multi_iter.iter.size_hint()) + }); + } + + self.0.iter().fold( + (0, Some(0)), + |acc, &MultiProductIter { ref iter, ref iter_orig, cur: _ }| { + let cur_size = iter.size_hint(); + let total_size = iter_orig.size_hint(); + size_hint::add(size_hint::mul(acc, total_size), cur_size) + } + ) + } + + fn last(self) -> Option { + let iter_count = self.0.len(); + + let lasts: Self::Item = self.0.into_iter() + .map(|multi_iter| multi_iter.iter.last()) + .while_some() + .collect(); + + if lasts.len() == iter_count { + Some(lasts) + } else { + None + } + } +} diff --git a/third_party/rust/itertools-0.8.0/src/combinations.rs b/third_party/rust/itertools-0.8.0/src/combinations.rs new file mode 100644 index 0000000000000..a7744151c921d --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/combinations.rs @@ -0,0 +1,165 @@ + +use std::ops::Index; +use std::fmt; + +/// An iterator to iterate through all the `n`-length combinations in an iterator. +/// +/// See [`.combinations()`](../trait.Itertools.html#method.combinations) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Combinations { + n: usize, + indices: Vec, + pool: LazyBuffer, + first: bool, +} + +impl fmt::Debug for Combinations + where I: Iterator + fmt::Debug, + I::Item: fmt::Debug, +{ + debug_fmt_fields!(Combinations, n, indices, pool, first); +} + +/// Create a new `Combinations` from a clonable iterator. +pub fn combinations(iter: I, n: usize) -> Combinations + where I: Iterator +{ + let mut indices: Vec = Vec::with_capacity(n); + for i in 0..n { + indices.push(i); + } + let mut pool: LazyBuffer = LazyBuffer::new(iter); + + for _ in 0..n { + if !pool.get_next() { + break; + } + } + + Combinations { + n: n, + indices: indices, + pool: pool, + first: true, + } +} + +impl Iterator for Combinations + where I: Iterator, + I::Item: Clone +{ + type Item = Vec; + fn next(&mut self) -> Option { + let mut pool_len = self.pool.len(); + if self.pool.is_done() { + if pool_len == 0 || self.n > pool_len { + return None; + } + } + + if self.first { + self.first = false; + } else if self.n == 0 { + return None; + } else { + // Scan from the end, looking for an index to increment + let mut i: usize = self.n - 1; + + // Check if we need to consume more from the iterator + if self.indices[i] == pool_len - 1 && !self.pool.is_done() { + if self.pool.get_next() { + pool_len += 1; + } + } + + while self.indices[i] == i + pool_len - self.n { + if i > 0 { + i -= 1; + } else { + // Reached the last combination + return None; + } + } + + // Increment index, and reset the ones to its right + self.indices[i] += 1; + let mut j = i + 1; + while j < self.n { + self.indices[j] = self.indices[j - 1] + 1; + j += 1; + } + } + + // Create result vector based on the indices + let mut result = Vec::with_capacity(self.n); + for i in self.indices.iter() { + result.push(self.pool[*i].clone()); + } + Some(result) + } +} + +#[derive(Debug)] +struct LazyBuffer { + it: I, + done: bool, + buffer: Vec, +} + +impl LazyBuffer + where I: Iterator +{ + pub fn new(it: I) -> LazyBuffer { + let mut it = it; + let mut buffer = Vec::new(); + let done; + if let Some(first) = it.next() { + buffer.push(first); + done = false; + } else { + done = true; + } + LazyBuffer { + it: it, + done: done, + buffer: buffer, + } + } + + pub fn len(&self) -> usize { + self.buffer.len() + } + + pub fn is_done(&self) -> bool { + self.done + } + + pub fn get_next(&mut self) -> bool { + if self.done { + return false; + } + let next_item = self.it.next(); + match next_item { + Some(x) => { + self.buffer.push(x); + true + } + None => { + self.done = true; + false + } + } + } +} + +impl Index for LazyBuffer + where I: Iterator, + I::Item: Sized +{ + type Output = I::Item; + + fn index<'b>(&'b self, _index: usize) -> &'b I::Item { + self.buffer.index(_index) + } +} + diff --git a/third_party/rust/itertools-0.8.0/src/concat_impl.rs b/third_party/rust/itertools-0.8.0/src/concat_impl.rs new file mode 100644 index 0000000000000..05b370e1c66da --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/concat_impl.rs @@ -0,0 +1,22 @@ +use Itertools; + +/// Combine all an iterator's elements into one element by using `Extend`. +/// +/// `IntoIterator`-enabled version of `.concat()` +/// +/// This combinator will extend the first item with each of the rest of the +/// items of the iterator. If the iterator is empty, the default value of +/// `I::Item` is returned. +/// +/// ```rust +/// use itertools::concat; +/// +/// let input = vec![vec![1], vec![2, 3], vec![4, 5, 6]]; +/// assert_eq!(concat(input), vec![1, 2, 3, 4, 5, 6]); +/// ``` +pub fn concat(iterable: I) -> I::Item + where I: IntoIterator, + I::Item: Extend<<::Item as IntoIterator>::Item> + IntoIterator + Default +{ + iterable.into_iter().fold1(|mut a, b| { a.extend(b); a }).unwrap_or_else(|| <_>::default()) +} diff --git a/third_party/rust/itertools-0.8.0/src/cons_tuples_impl.rs b/third_party/rust/itertools-0.8.0/src/cons_tuples_impl.rs new file mode 100644 index 0000000000000..9b27e7580fe19 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/cons_tuples_impl.rs @@ -0,0 +1,68 @@ + +macro_rules! impl_cons_iter( + ($_A:ident, $_B:ident, ) => (); // stop + + ($A:ident, $($B:ident,)*) => ( + impl_cons_iter!($($B,)*); + #[allow(non_snake_case)] + impl Iterator for ConsTuples + where Iter: Iterator, + { + type Item = ($($B,)* X, ); + fn next(&mut self) -> Option { + self.iter.next().map(|(($($B,)*), x)| ($($B,)* x, )) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + fn fold(self, accum: Acc, mut f: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.fold(accum, move |acc, (($($B,)*), x)| f(acc, ($($B,)* x, ))) + } + } + + #[allow(non_snake_case)] + impl DoubleEndedIterator for ConsTuples + where Iter: DoubleEndedIterator, + { + fn next_back(&mut self) -> Option { + self.iter.next().map(|(($($B,)*), x)| ($($B,)* x, )) + } + } + + ); +); + +impl_cons_iter!(A, B, C, D, E, F, G, H,); + +/// An iterator that maps an iterator of tuples like +/// `((A, B), C)` to an iterator of `(A, B, C)`. +/// +/// Used by the `iproduct!()` macro. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +#[derive(Debug)] +pub struct ConsTuples + where I: Iterator, +{ + iter: I, +} + +impl Clone for ConsTuples + where I: Clone + Iterator, +{ + fn clone(&self) -> Self { + ConsTuples { + iter: self.iter.clone(), + } + } +} + +/// Create an iterator that maps for example iterators of +/// `((A, B), C)` to `(A, B, C)`. +pub fn cons_tuples(iterable: I) -> ConsTuples + where I: Iterator +{ + ConsTuples { iter: iterable.into_iter() } +} diff --git a/third_party/rust/itertools-0.8.0/src/diff.rs b/third_party/rust/itertools-0.8.0/src/diff.rs new file mode 100644 index 0000000000000..2951bc422ef03 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/diff.rs @@ -0,0 +1,61 @@ +//! "Diff"ing iterators for caching elements to sequential collections without requiring the new +//! elements' iterator to be `Clone`. +//! +//! - [**Diff**](./enum.Diff.html) (produced by the [**diff_with**](./fn.diff_with.html) function) +//! describes the difference between two non-`Clone` iterators `I` and `J` after breaking ASAP from +//! a lock-step comparison. + +use free::put_back; +use structs::PutBack; + +/// A type returned by the [`diff_with`](./fn.diff_with.html) function. +/// +/// `Diff` represents the way in which the elements yielded by the iterator `I` differ to some +/// iterator `J`. +pub enum Diff + where I: Iterator, + J: Iterator +{ + /// The index of the first non-matching element along with both iterator's remaining elements + /// starting with the first mis-match. + FirstMismatch(usize, PutBack, PutBack), + /// The total number of elements that were in `J` along with the remaining elements of `I`. + Shorter(usize, PutBack), + /// The total number of elements that were in `I` along with the remaining elements of `J`. + Longer(usize, PutBack), +} + +/// Compares every element yielded by both `i` and `j` with the given function in lock-step and +/// returns a `Diff` which describes how `j` differs from `i`. +/// +/// If the number of elements yielded by `j` is less than the number of elements yielded by `i`, +/// the number of `j` elements yielded will be returned along with `i`'s remaining elements as +/// `Diff::Shorter`. +/// +/// If the two elements of a step differ, the index of those elements along with the remaining +/// elements of both `i` and `j` are returned as `Diff::FirstMismatch`. +/// +/// If `i` becomes exhausted before `j` becomes exhausted, the number of elements in `i` along with +/// the remaining `j` elements will be returned as `Diff::Longer`. +pub fn diff_with(i: I, j: J, is_equal: F) + -> Option> + where I: IntoIterator, + J: IntoIterator, + F: Fn(&I::Item, &J::Item) -> bool +{ + let mut i = i.into_iter(); + let mut j = j.into_iter(); + let mut idx = 0; + while let Some(i_elem) = i.next() { + match j.next() { + None => return Some(Diff::Shorter(idx, put_back(i).with_value(i_elem))), + Some(j_elem) => if !is_equal(&i_elem, &j_elem) { + let remaining_i = put_back(i).with_value(i_elem); + let remaining_j = put_back(j).with_value(j_elem); + return Some(Diff::FirstMismatch(idx, remaining_i, remaining_j)); + }, + } + idx += 1; + } + j.next().map(|j_elem| Diff::Longer(idx, put_back(j).with_value(j_elem))) +} diff --git a/third_party/rust/itertools-0.8.0/src/either_or_both.rs b/third_party/rust/itertools-0.8.0/src/either_or_both.rs new file mode 100644 index 0000000000000..55ae06c6b4341 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/either_or_both.rs @@ -0,0 +1,58 @@ +use EitherOrBoth::*; + +/// Value that either holds a single A or B, or both. +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum EitherOrBoth { + /// Both values are present. + Both(A, B), + /// Only the left value of type `A` is present. + Left(A), + /// Only the right value of type `B` is present. + Right(B), +} + +impl EitherOrBoth { + /// If `Left`, or `Both`, return true, otherwise, return false. + pub fn has_left(&self) -> bool { + self.as_ref().left().is_some() + } + + /// If `Right`, or `Both`, return true, otherwise, return false. + pub fn has_right(&self) -> bool { + self.as_ref().right().is_some() + } + + /// If `Left`, or `Both`, return `Some` with the left value, otherwise, return `None`. + pub fn left(self) -> Option { + match self { + Left(left) | Both(left, _) => Some(left), + _ => None + } + } + + /// If `Right`, or `Both`, return `Some` with the right value, otherwise, return `None`. + pub fn right(self) -> Option { + match self { + Right(right) | Both(_, right) => Some(right), + _ => None + } + } + + /// Converts from `&EitherOrBoth` to `EitherOrBoth<&A, &B>`. + pub fn as_ref(&self) -> EitherOrBoth<&A, &B> { + match *self { + Left(ref left) => Left(left), + Right(ref right) => Right(right), + Both(ref left, ref right) => Both(left, right), + } + } + + /// Converts from `&mut EitherOrBoth` to `EitherOrBoth<&mut A, &mut B>`. + pub fn as_mut(&mut self) -> EitherOrBoth<&mut A, &mut B> { + match *self { + Left(ref mut left) => Left(left), + Right(ref mut right) => Right(right), + Both(ref mut left, ref mut right) => Both(left, right), + } + } +} diff --git a/third_party/rust/itertools-0.8.0/src/format.rs b/third_party/rust/itertools-0.8.0/src/format.rs new file mode 100644 index 0000000000000..c42806b01c593 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/format.rs @@ -0,0 +1,113 @@ +use std::fmt; +use std::cell::RefCell; + +/// Format all iterator elements lazily, separated by `sep`. +/// +/// The format value can only be formatted once, after that the iterator is +/// exhausted. +/// +/// See [`.format_with()`](../trait.Itertools.html#method.format_with) for more information. +pub struct FormatWith<'a, I, F> { + sep: &'a str, + /// FormatWith uses interior mutability because Display::fmt takes &self. + inner: RefCell>, +} + +/// Format all iterator elements lazily, separated by `sep`. +/// +/// The format value can only be formatted once, after that the iterator is +/// exhausted. +/// +/// See [`.format()`](../trait.Itertools.html#method.format) +/// for more information. +#[derive(Clone)] +pub struct Format<'a, I> { + sep: &'a str, + /// Format uses interior mutability because Display::fmt takes &self. + inner: RefCell>, +} + +pub fn new_format<'a, I, F>(iter: I, separator: &'a str, f: F) -> FormatWith<'a, I, F> + where I: Iterator, + F: FnMut(I::Item, &mut FnMut(&fmt::Display) -> fmt::Result) -> fmt::Result +{ + FormatWith { + sep: separator, + inner: RefCell::new(Some((iter, f))), + } +} + +pub fn new_format_default<'a, I>(iter: I, separator: &'a str) -> Format<'a, I> + where I: Iterator, +{ + Format { + sep: separator, + inner: RefCell::new(Some(iter)), + } +} + +impl<'a, I, F> fmt::Display for FormatWith<'a, I, F> + where I: Iterator, + F: FnMut(I::Item, &mut FnMut(&fmt::Display) -> fmt::Result) -> fmt::Result +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let (mut iter, mut format) = match self.inner.borrow_mut().take() { + Some(t) => t, + None => panic!("FormatWith: was already formatted once"), + }; + + if let Some(fst) = iter.next() { + try!(format(fst, &mut |disp: &fmt::Display| disp.fmt(f))); + for elt in iter { + if self.sep.len() > 0 { + + try!(f.write_str(self.sep)); + } + try!(format(elt, &mut |disp: &fmt::Display| disp.fmt(f))); + } + } + Ok(()) + } +} + +impl<'a, I> Format<'a, I> + where I: Iterator, +{ + fn format(&self, f: &mut fmt::Formatter, mut cb: F) -> fmt::Result + where F: FnMut(&I::Item, &mut fmt::Formatter) -> fmt::Result, + { + let mut iter = match self.inner.borrow_mut().take() { + Some(t) => t, + None => panic!("Format: was already formatted once"), + }; + + if let Some(fst) = iter.next() { + try!(cb(&fst, f)); + for elt in iter { + if self.sep.len() > 0 { + try!(f.write_str(self.sep)); + } + try!(cb(&elt, f)); + } + } + Ok(()) + } +} + +macro_rules! impl_format { + ($($fmt_trait:ident)*) => { + $( + impl<'a, I> fmt::$fmt_trait for Format<'a, I> + where I: Iterator, + I::Item: fmt::$fmt_trait, + { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.format(f, fmt::$fmt_trait::fmt) + } + } + )* + } +} + +impl_format!{Display Debug + UpperExp LowerExp UpperHex LowerHex Octal Binary Pointer} diff --git a/third_party/rust/itertools-0.8.0/src/free.rs b/third_party/rust/itertools-0.8.0/src/free.rs new file mode 100644 index 0000000000000..2bc72a9aa1075 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/free.rs @@ -0,0 +1,236 @@ +//! Free functions that create iterator adaptors or call iterator methods. +//! +//! The benefit of free functions is that they accept any `IntoIterator` as +//! argument, so the resulting code may be easier to read. + +#[cfg(feature = "use_std")] +use std::fmt::Display; +use std::iter::{self, Zip}; +#[cfg(feature = "use_std")] +type VecIntoIter = ::std::vec::IntoIter; + +#[cfg(feature = "use_std")] +use Itertools; + +pub use adaptors::{ + interleave, + merge, + put_back, +}; +#[cfg(feature = "use_std")] +pub use put_back_n_impl::put_back_n; +#[cfg(feature = "use_std")] +pub use multipeek_impl::multipeek; +#[cfg(feature = "use_std")] +pub use kmerge_impl::kmerge; +pub use zip_eq_impl::zip_eq; +pub use merge_join::merge_join_by; +#[cfg(feature = "use_std")] +pub use rciter_impl::rciter; + +/// Iterate `iterable` with a running index. +/// +/// `IntoIterator` enabled version of `.enumerate()`. +/// +/// ``` +/// use itertools::enumerate; +/// +/// for (i, elt) in enumerate(&[1, 2, 3]) { +/// /* loop body */ +/// } +/// ``` +pub fn enumerate(iterable: I) -> iter::Enumerate + where I: IntoIterator +{ + iterable.into_iter().enumerate() +} + +/// Iterate `iterable` in reverse. +/// +/// `IntoIterator` enabled version of `.rev()`. +/// +/// ``` +/// use itertools::rev; +/// +/// for elt in rev(&[1, 2, 3]) { +/// /* loop body */ +/// } +/// ``` +pub fn rev(iterable: I) -> iter::Rev + where I: IntoIterator, + I::IntoIter: DoubleEndedIterator +{ + iterable.into_iter().rev() +} + +/// Iterate `i` and `j` in lock step. +/// +/// `IntoIterator` enabled version of `i.zip(j)`. +/// +/// ``` +/// use itertools::zip; +/// +/// let data = [1, 2, 3, 4, 5]; +/// for (a, b) in zip(&data, &data[1..]) { +/// /* loop body */ +/// } +/// ``` +pub fn zip(i: I, j: J) -> Zip + where I: IntoIterator, + J: IntoIterator +{ + i.into_iter().zip(j) +} + +/// Create an iterator that first iterates `i` and then `j`. +/// +/// `IntoIterator` enabled version of `i.chain(j)`. +/// +/// ``` +/// use itertools::chain; +/// +/// for elt in chain(&[1, 2, 3], &[4]) { +/// /* loop body */ +/// } +/// ``` +pub fn chain(i: I, j: J) -> iter::Chain<::IntoIter, ::IntoIter> + where I: IntoIterator, + J: IntoIterator +{ + i.into_iter().chain(j) +} + +/// Create an iterator that clones each element from &T to T +/// +/// `IntoIterator` enabled version of `i.cloned()`. +/// +/// ``` +/// use itertools::cloned; +/// +/// assert_eq!(cloned(b"abc").next(), Some(b'a')); +/// ``` +pub fn cloned<'a, I, T: 'a>(iterable: I) -> iter::Cloned + where I: IntoIterator, + T: Clone, +{ + iterable.into_iter().cloned() +} + +/// Perform a fold operation over the iterable. +/// +/// `IntoIterator` enabled version of `i.fold(init, f)` +/// +/// ``` +/// use itertools::fold; +/// +/// assert_eq!(fold(&[1., 2., 3.], 0., |a, &b| f32::max(a, b)), 3.); +/// ``` +pub fn fold(iterable: I, init: B, f: F) -> B + where I: IntoIterator, + F: FnMut(B, I::Item) -> B +{ + iterable.into_iter().fold(init, f) +} + +/// Test whether the predicate holds for all elements in the iterable. +/// +/// `IntoIterator` enabled version of `i.all(f)` +/// +/// ``` +/// use itertools::all; +/// +/// assert!(all(&[1, 2, 3], |elt| *elt > 0)); +/// ``` +pub fn all(iterable: I, f: F) -> bool + where I: IntoIterator, + F: FnMut(I::Item) -> bool +{ + iterable.into_iter().all(f) +} + +/// Test whether the predicate holds for any elements in the iterable. +/// +/// `IntoIterator` enabled version of `i.any(f)` +/// +/// ``` +/// use itertools::any; +/// +/// assert!(any(&[0, -1, 2], |elt| *elt > 0)); +/// ``` +pub fn any(iterable: I, f: F) -> bool + where I: IntoIterator, + F: FnMut(I::Item) -> bool +{ + iterable.into_iter().any(f) +} + +/// Return the maximum value of the iterable. +/// +/// `IntoIterator` enabled version of `i.max()`. +/// +/// ``` +/// use itertools::max; +/// +/// assert_eq!(max(0..10), Some(9)); +/// ``` +pub fn max(iterable: I) -> Option + where I: IntoIterator, + I::Item: Ord +{ + iterable.into_iter().max() +} + +/// Return the minimum value of the iterable. +/// +/// `IntoIterator` enabled version of `i.min()`. +/// +/// ``` +/// use itertools::min; +/// +/// assert_eq!(min(0..10), Some(0)); +/// ``` +pub fn min(iterable: I) -> Option + where I: IntoIterator, + I::Item: Ord +{ + iterable.into_iter().min() +} + + +/// Combine all iterator elements into one String, seperated by `sep`. +/// +/// `IntoIterator` enabled version of `iterable.join(sep)`. +/// +/// ``` +/// use itertools::join; +/// +/// assert_eq!(join(&[1, 2, 3], ", "), "1, 2, 3"); +/// ``` +#[cfg(feature = "use_std")] +pub fn join(iterable: I, sep: &str) -> String + where I: IntoIterator, + I::Item: Display +{ + iterable.into_iter().join(sep) +} + +/// Sort all iterator elements into a new iterator in ascending order. +/// +/// `IntoIterator` enabled version of [`iterable.sorted()`][1]. +/// +/// [1]: trait.Itertools.html#method.sorted +/// +/// ``` +/// use itertools::sorted; +/// use itertools::assert_equal; +/// +/// assert_equal(sorted("rust".chars()), "rstu".chars()); +/// ``` +#[cfg(feature = "use_std")] +pub fn sorted(iterable: I) -> VecIntoIter + where I: IntoIterator, + I::Item: Ord +{ + iterable.into_iter().sorted() +} + diff --git a/third_party/rust/itertools-0.8.0/src/group_map.rs b/third_party/rust/itertools-0.8.0/src/group_map.rs new file mode 100644 index 0000000000000..be9f8420d89b4 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/group_map.rs @@ -0,0 +1,22 @@ +#![cfg(feature = "use_std")] + +use std::collections::HashMap; +use std::hash::Hash; +use std::iter::Iterator; + +/// Return a `HashMap` of keys mapped to a list of their corresponding values. +/// +/// See [`.into_group_map()`](../trait.Itertools.html#method.into_group_map) +/// for more information. +pub fn into_group_map(iter: I) -> HashMap> + where I: Iterator, + K: Hash + Eq, +{ + let mut lookup = HashMap::new(); + + for (key, val) in iter { + lookup.entry(key).or_insert(Vec::new()).push(val); + } + + lookup +} \ No newline at end of file diff --git a/third_party/rust/itertools-0.8.0/src/groupbylazy.rs b/third_party/rust/itertools-0.8.0/src/groupbylazy.rs new file mode 100644 index 0000000000000..ff253e845df1d --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/groupbylazy.rs @@ -0,0 +1,571 @@ +use std::cell::{Cell, RefCell}; +use std::vec; + +/// A trait to unify FnMut for GroupBy with the chunk key in IntoChunks +trait KeyFunction { + type Key; + fn call_mut(&mut self, arg: A) -> Self::Key; +} + +impl<'a, A, K, F: ?Sized> KeyFunction for F + where F: FnMut(A) -> K +{ + type Key = K; + #[inline] + fn call_mut(&mut self, arg: A) -> Self::Key { + (*self)(arg) + } +} + + +/// ChunkIndex acts like the grouping key function for IntoChunks +#[derive(Debug)] +struct ChunkIndex { + size: usize, + index: usize, + key: usize, +} + +impl ChunkIndex { + #[inline(always)] + fn new(size: usize) -> Self { + ChunkIndex { + size: size, + index: 0, + key: 0, + } + } +} + +impl<'a, A> KeyFunction for ChunkIndex { + type Key = usize; + #[inline(always)] + fn call_mut(&mut self, _arg: A) -> Self::Key { + if self.index == self.size { + self.key += 1; + self.index = 0; + } + self.index += 1; + self.key + } +} + + +struct GroupInner + where I: Iterator +{ + key: F, + iter: I, + current_key: Option, + current_elt: Option, + /// flag set if iterator is exhausted + done: bool, + /// Index of group we are currently buffering or visiting + top_group: usize, + /// Least index for which we still have elements buffered + oldest_buffered_group: usize, + /// Group index for `buffer[0]` -- the slots + /// bottom_group..oldest_buffered_group are unused and will be erased when + /// that range is large enough. + bottom_group: usize, + /// Buffered groups, from `bottom_group` (index 0) to `top_group`. + buffer: Vec>, + /// index of last group iter that was dropped, usize::MAX == none + dropped_group: usize, +} + +impl GroupInner + where I: Iterator, + F: for<'a> KeyFunction<&'a I::Item, Key=K>, + K: PartialEq, +{ + /// `client`: Index of group that requests next element + #[inline(always)] + fn step(&mut self, client: usize) -> Option { + /* + println!("client={}, bottom_group={}, oldest_buffered_group={}, top_group={}, buffers=[{}]", + client, self.bottom_group, self.oldest_buffered_group, + self.top_group, + self.buffer.iter().map(|elt| elt.len()).format(", ")); + */ + if client < self.oldest_buffered_group { + None + } else if client < self.top_group || + (client == self.top_group && + self.buffer.len() > self.top_group - self.bottom_group) + { + self.lookup_buffer(client) + } else if self.done { + None + } else if self.top_group == client { + self.step_current() + } else { + self.step_buffering(client) + } + } + + #[inline(never)] + fn lookup_buffer(&mut self, client: usize) -> Option { + // if `bufidx` doesn't exist in self.buffer, it might be empty + let bufidx = client - self.bottom_group; + if client < self.oldest_buffered_group { + return None; + } + let elt = self.buffer.get_mut(bufidx).and_then(|queue| queue.next()); + if elt.is_none() && client == self.oldest_buffered_group { + // FIXME: VecDeque is unfortunately not zero allocation when empty, + // so we do this job manually. + // `bottom_group..oldest_buffered_group` is unused, and if it's large enough, erase it. + self.oldest_buffered_group += 1; + // skip forward further empty queues too + while self.buffer.get(self.oldest_buffered_group - self.bottom_group) + .map_or(false, |buf| buf.len() == 0) + { + self.oldest_buffered_group += 1; + } + + let nclear = self.oldest_buffered_group - self.bottom_group; + if nclear > 0 && nclear >= self.buffer.len() / 2 { + let mut i = 0; + self.buffer.retain(|buf| { + i += 1; + debug_assert!(buf.len() == 0 || i > nclear); + i > nclear + }); + self.bottom_group = self.oldest_buffered_group; + } + } + elt + } + + /// Take the next element from the iterator, and set the done + /// flag if exhausted. Must not be called after done. + #[inline(always)] + fn next_element(&mut self) -> Option { + debug_assert!(!self.done); + match self.iter.next() { + None => { self.done = true; None } + otherwise => otherwise, + } + } + + + #[inline(never)] + fn step_buffering(&mut self, client: usize) -> Option { + // requested a later group -- walk through the current group up to + // the requested group index, and buffer the elements (unless + // the group is marked as dropped). + // Because the `Groups` iterator is always the first to request + // each group index, client is the next index efter top_group. + debug_assert!(self.top_group + 1 == client); + let mut group = Vec::new(); + + if let Some(elt) = self.current_elt.take() { + if self.top_group != self.dropped_group { + group.push(elt); + } + } + let mut first_elt = None; // first element of the next group + + while let Some(elt) = self.next_element() { + let key = self.key.call_mut(&elt); + match self.current_key.take() { + None => {} + Some(old_key) => if old_key != key { + self.current_key = Some(key); + first_elt = Some(elt); + break; + }, + } + self.current_key = Some(key); + if self.top_group != self.dropped_group { + group.push(elt); + } + } + + if self.top_group != self.dropped_group { + self.push_next_group(group); + } + if first_elt.is_some() { + self.top_group += 1; + debug_assert!(self.top_group == client); + } + first_elt + } + + fn push_next_group(&mut self, group: Vec) { + // When we add a new buffered group, fill up slots between oldest_buffered_group and top_group + while self.top_group - self.bottom_group > self.buffer.len() { + if self.buffer.is_empty() { + self.bottom_group += 1; + self.oldest_buffered_group += 1; + } else { + self.buffer.push(Vec::new().into_iter()); + } + } + self.buffer.push(group.into_iter()); + debug_assert!(self.top_group + 1 - self.bottom_group == self.buffer.len()); + } + + /// This is the immediate case, where we use no buffering + #[inline] + fn step_current(&mut self) -> Option { + debug_assert!(!self.done); + if let elt @ Some(..) = self.current_elt.take() { + return elt; + } + match self.next_element() { + None => None, + Some(elt) => { + let key = self.key.call_mut(&elt); + match self.current_key.take() { + None => {} + Some(old_key) => if old_key != key { + self.current_key = Some(key); + self.current_elt = Some(elt); + self.top_group += 1; + return None; + }, + } + self.current_key = Some(key); + Some(elt) + } + } + } + + /// Request the just started groups' key. + /// + /// `client`: Index of group + /// + /// **Panics** if no group key is available. + fn group_key(&mut self, client: usize) -> K { + // This can only be called after we have just returned the first + // element of a group. + // Perform this by simply buffering one more element, grabbing the + // next key. + debug_assert!(!self.done); + debug_assert!(client == self.top_group); + debug_assert!(self.current_key.is_some()); + debug_assert!(self.current_elt.is_none()); + let old_key = self.current_key.take().unwrap(); + if let Some(elt) = self.next_element() { + let key = self.key.call_mut(&elt); + if old_key != key { + self.top_group += 1; + } + self.current_key = Some(key); + self.current_elt = Some(elt); + } + old_key + } +} + +impl GroupInner + where I: Iterator, +{ + /// Called when a group is dropped + fn drop_group(&mut self, client: usize) { + // It's only useful to track the maximal index + if self.dropped_group == !0 || client > self.dropped_group { + self.dropped_group = client; + } + } +} + +/// `GroupBy` is the storage for the lazy grouping operation. +/// +/// If the groups are consumed in their original order, or if each +/// group is dropped without keeping it around, then `GroupBy` uses +/// no allocations. It needs allocations only if several group iterators +/// are alive at the same time. +/// +/// This type implements `IntoIterator` (it is **not** an iterator +/// itself), because the group iterators need to borrow from this +/// value. It should be stored in a local variable or temporary and +/// iterated. +/// +/// See [`.group_by()`](../trait.Itertools.html#method.group_by) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct GroupBy + where I: Iterator, +{ + inner: RefCell>, + // the group iterator's current index. Keep this in the main value + // so that simultaneous iterators all use the same state. + index: Cell, +} + +/// Create a new +pub fn new(iter: J, f: F) -> GroupBy + where J: IntoIterator, + F: FnMut(&J::Item) -> K, +{ + GroupBy { + inner: RefCell::new(GroupInner { + key: f, + iter: iter.into_iter(), + current_key: None, + current_elt: None, + done: false, + top_group: 0, + oldest_buffered_group: 0, + bottom_group: 0, + buffer: Vec::new(), + dropped_group: !0, + }), + index: Cell::new(0), + } +} + +impl GroupBy + where I: Iterator, +{ + /// `client`: Index of group that requests next element + fn step(&self, client: usize) -> Option + where F: FnMut(&I::Item) -> K, + K: PartialEq, + { + self.inner.borrow_mut().step(client) + } + + /// `client`: Index of group + fn drop_group(&self, client: usize) { + self.inner.borrow_mut().drop_group(client) + } +} + +impl<'a, K, I, F> IntoIterator for &'a GroupBy + where I: Iterator, + I::Item: 'a, + F: FnMut(&I::Item) -> K, + K: PartialEq +{ + type Item = (K, Group<'a, K, I, F>); + type IntoIter = Groups<'a, K, I, F>; + + fn into_iter(self) -> Self::IntoIter { + Groups { parent: self } + } +} + + +/// An iterator that yields the Group iterators. +/// +/// Iterator element type is `(K, Group)`: +/// the group's key `K` and the group's iterator. +/// +/// See [`.group_by()`](../trait.Itertools.html#method.group_by) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Groups<'a, K: 'a, I: 'a, F: 'a> + where I: Iterator, + I::Item: 'a +{ + parent: &'a GroupBy, +} + +impl<'a, K, I, F> Iterator for Groups<'a, K, I, F> + where I: Iterator, + I::Item: 'a, + F: FnMut(&I::Item) -> K, + K: PartialEq +{ + type Item = (K, Group<'a, K, I, F>); + + #[inline] + fn next(&mut self) -> Option { + let index = self.parent.index.get(); + self.parent.index.set(index + 1); + let inner = &mut *self.parent.inner.borrow_mut(); + inner.step(index).map(|elt| { + let key = inner.group_key(index); + (key, Group { + parent: self.parent, + index: index, + first: Some(elt), + }) + }) + } +} + +/// An iterator for the elements in a single group. +/// +/// Iterator element type is `I::Item`. +pub struct Group<'a, K: 'a, I: 'a, F: 'a> + where I: Iterator, + I::Item: 'a, +{ + parent: &'a GroupBy, + index: usize, + first: Option, +} + +impl<'a, K, I, F> Drop for Group<'a, K, I, F> + where I: Iterator, + I::Item: 'a, +{ + fn drop(&mut self) { + self.parent.drop_group(self.index); + } +} + +impl<'a, K, I, F> Iterator for Group<'a, K, I, F> + where I: Iterator, + I::Item: 'a, + F: FnMut(&I::Item) -> K, + K: PartialEq, +{ + type Item = I::Item; + #[inline] + fn next(&mut self) -> Option { + if let elt @ Some(..) = self.first.take() { + return elt; + } + self.parent.step(self.index) + } +} + +///// IntoChunks ///// + +/// Create a new +pub fn new_chunks(iter: J, size: usize) -> IntoChunks + where J: IntoIterator, +{ + IntoChunks { + inner: RefCell::new(GroupInner { + key: ChunkIndex::new(size), + iter: iter.into_iter(), + current_key: None, + current_elt: None, + done: false, + top_group: 0, + oldest_buffered_group: 0, + bottom_group: 0, + buffer: Vec::new(), + dropped_group: !0, + }), + index: Cell::new(0), + } +} + + +/// `ChunkLazy` is the storage for a lazy chunking operation. +/// +/// `IntoChunks` behaves just like `GroupBy`: it is iterable, and +/// it only buffers if several chunk iterators are alive at the same time. +/// +/// This type implements `IntoIterator` (it is **not** an iterator +/// itself), because the chunk iterators need to borrow from this +/// value. It should be stored in a local variable or temporary and +/// iterated. +/// +/// Iterator element type is `Chunk`, each chunk's iterator. +/// +/// See [`.chunks()`](../trait.Itertools.html#method.chunks) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct IntoChunks + where I: Iterator, +{ + inner: RefCell>, + // the chunk iterator's current index. Keep this in the main value + // so that simultaneous iterators all use the same state. + index: Cell, +} + + +impl IntoChunks + where I: Iterator, +{ + /// `client`: Index of chunk that requests next element + fn step(&self, client: usize) -> Option { + self.inner.borrow_mut().step(client) + } + + /// `client`: Index of chunk + fn drop_group(&self, client: usize) { + self.inner.borrow_mut().drop_group(client) + } +} + +impl<'a, I> IntoIterator for &'a IntoChunks + where I: Iterator, + I::Item: 'a, +{ + type Item = Chunk<'a, I>; + type IntoIter = Chunks<'a, I>; + + fn into_iter(self) -> Self::IntoIter { + Chunks { + parent: self, + } + } +} + + +/// An iterator that yields the Chunk iterators. +/// +/// Iterator element type is `Chunk`. +/// +/// See [`.chunks()`](../trait.Itertools.html#method.chunks) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Chunks<'a, I: 'a> + where I: Iterator, + I::Item: 'a, +{ + parent: &'a IntoChunks, +} + +impl<'a, I> Iterator for Chunks<'a, I> + where I: Iterator, + I::Item: 'a, +{ + type Item = Chunk<'a, I>; + + #[inline] + fn next(&mut self) -> Option { + let index = self.parent.index.get(); + self.parent.index.set(index + 1); + let inner = &mut *self.parent.inner.borrow_mut(); + inner.step(index).map(|elt| { + Chunk { + parent: self.parent, + index: index, + first: Some(elt), + } + }) + } +} + +/// An iterator for the elements in a single chunk. +/// +/// Iterator element type is `I::Item`. +pub struct Chunk<'a, I: 'a> + where I: Iterator, + I::Item: 'a, +{ + parent: &'a IntoChunks, + index: usize, + first: Option, +} + +impl<'a, I> Drop for Chunk<'a, I> + where I: Iterator, + I::Item: 'a, +{ + fn drop(&mut self) { + self.parent.drop_group(self.index); + } +} + +impl<'a, I> Iterator for Chunk<'a, I> + where I: Iterator, + I::Item: 'a, +{ + type Item = I::Item; + #[inline] + fn next(&mut self) -> Option { + if let elt @ Some(..) = self.first.take() { + return elt; + } + self.parent.step(self.index) + } +} diff --git a/third_party/rust/itertools-0.8.0/src/impl_macros.rs b/third_party/rust/itertools-0.8.0/src/impl_macros.rs new file mode 100644 index 0000000000000..b41760aee8dee --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/impl_macros.rs @@ -0,0 +1,14 @@ +//! +//! Implementation's internal macros + +macro_rules! debug_fmt_fields { + ($tyname:ident, $($($field:ident).+),*) => { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + f.debug_struct(stringify!($tyname)) + $( + .field(stringify!($($field).+), &self.$($field).+) + )* + .finish() + } + } +} diff --git a/third_party/rust/itertools-0.8.0/src/intersperse.rs b/third_party/rust/itertools-0.8.0/src/intersperse.rs new file mode 100644 index 0000000000000..b1dc732117b19 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/intersperse.rs @@ -0,0 +1,60 @@ +use std::iter::Fuse; +use super::size_hint; + +#[derive(Clone)] +/// An iterator adaptor to insert a particular value +/// between each element of the adapted iterator. +/// +/// Iterator element type is `I::Item` +/// +/// This iterator is *fused*. +/// +/// See [`.intersperse()`](../trait.Itertools.html#method.intersperse) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +#[derive(Debug)] +pub struct Intersperse + where I: Iterator +{ + element: I::Item, + iter: Fuse, + peek: Option, +} + +/// Create a new Intersperse iterator +pub fn intersperse(iter: I, elt: I::Item) -> Intersperse + where I: Iterator +{ + let mut iter = iter.fuse(); + Intersperse { + peek: iter.next(), + iter: iter, + element: elt, + } +} + +impl Iterator for Intersperse + where I: Iterator, + I::Item: Clone +{ + type Item = I::Item; + #[inline] + fn next(&mut self) -> Option { + if self.peek.is_some() { + self.peek.take() + } else { + self.peek = self.iter.next(); + if self.peek.is_some() { + Some(self.element.clone()) + } else { + None + } + } + } + + fn size_hint(&self) -> (usize, Option) { + // 2 * SH + { 1 or 0 } + let has_peek = self.peek.is_some() as usize; + let sh = self.iter.size_hint(); + size_hint::add_scalar(size_hint::add(sh, sh), has_peek) + } +} diff --git a/third_party/rust/itertools-0.8.0/src/kmerge_impl.rs b/third_party/rust/itertools-0.8.0/src/kmerge_impl.rs new file mode 100644 index 0000000000000..0720899e371ca --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/kmerge_impl.rs @@ -0,0 +1,256 @@ + +use size_hint; +use Itertools; + +use std::mem::replace; +use std::fmt; + +macro_rules! clone_fields { + ($name:ident, $base:expr, $($field:ident),+) => ( + $name { + $( + $field : $base . $field .clone() + ),* + } + ); +} + +/// Head element and Tail iterator pair +/// +/// `PartialEq`, `Eq`, `PartialOrd` and `Ord` are implemented by comparing sequences based on +/// first items (which are guaranteed to exist). +/// +/// The meanings of `PartialOrd` and `Ord` are reversed so as to turn the heap used in +/// `KMerge` into a min-heap. +#[derive(Debug)] +struct HeadTail + where I: Iterator +{ + head: I::Item, + tail: I, +} + +impl HeadTail + where I: Iterator +{ + /// Constructs a `HeadTail` from an `Iterator`. Returns `None` if the `Iterator` is empty. + fn new(mut it: I) -> Option> { + let head = it.next(); + head.map(|h| { + HeadTail { + head: h, + tail: it, + } + }) + } + + /// Get the next element and update `head`, returning the old head in `Some`. + /// + /// Returns `None` when the tail is exhausted (only `head` then remains). + fn next(&mut self) -> Option { + if let Some(next) = self.tail.next() { + Some(replace(&mut self.head, next)) + } else { + None + } + } + + /// Hints at the size of the sequence, same as the `Iterator` method. + fn size_hint(&self) -> (usize, Option) { + size_hint::add_scalar(self.tail.size_hint(), 1) + } +} + +impl Clone for HeadTail + where I: Iterator + Clone, + I::Item: Clone +{ + fn clone(&self) -> Self { + clone_fields!(HeadTail, self, head, tail) + } +} + +/// Make `data` a heap (min-heap w.r.t the sorting). +fn heapify(data: &mut [T], mut less_than: S) + where S: FnMut(&T, &T) -> bool +{ + for i in (0..data.len() / 2).rev() { + sift_down(data, i, &mut less_than); + } +} + +/// Sift down element at `index` (`heap` is a min-heap wrt the ordering) +fn sift_down(heap: &mut [T], index: usize, mut less_than: S) + where S: FnMut(&T, &T) -> bool +{ + debug_assert!(index <= heap.len()); + let mut pos = index; + let mut child = 2 * pos + 1; + // the `pos` conditional is to avoid a bounds check + while pos < heap.len() && child < heap.len() { + let right = child + 1; + + // pick the smaller of the two children + if right < heap.len() && less_than(&heap[right], &heap[child]) { + child = right; + } + + // sift down is done if we are already in order + if !less_than(&heap[child], &heap[pos]) { + return; + } + heap.swap(pos, child); + pos = child; + child = 2 * pos + 1; + } +} + +/// An iterator adaptor that merges an abitrary number of base iterators in ascending order. +/// If all base iterators are sorted (ascending), the result is sorted. +/// +/// Iterator element type is `I::Item`. +/// +/// See [`.kmerge()`](../trait.Itertools.html#method.kmerge) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct KMerge + where I: Iterator +{ + heap: Vec>, +} + +impl fmt::Debug for KMerge + where I: Iterator + fmt::Debug, + I::Item: fmt::Debug, +{ + debug_fmt_fields!(KMerge, heap); +} + +/// Create an iterator that merges elements of the contained iterators using +/// the ordering function. +/// +/// Equivalent to `iterable.into_iter().kmerge()`. +/// +/// ``` +/// use itertools::kmerge; +/// +/// for elt in kmerge(vec![vec![0, 2, 4], vec![1, 3, 5], vec![6, 7]]) { +/// /* loop body */ +/// } +/// ``` +pub fn kmerge(iterable: I) -> KMerge<::IntoIter> + where I: IntoIterator, + I::Item: IntoIterator, + <::Item as IntoIterator>::Item: PartialOrd +{ + let iter = iterable.into_iter(); + let (lower, _) = iter.size_hint(); + let mut heap = Vec::with_capacity(lower); + heap.extend(iter.filter_map(|it| HeadTail::new(it.into_iter()))); + heapify(&mut heap, |a, b| a.head < b.head); + KMerge { heap: heap } +} + +impl Clone for KMerge + where I: Iterator + Clone, + I::Item: Clone +{ + fn clone(&self) -> KMerge { + clone_fields!(KMerge, self, heap) + } +} + +impl Iterator for KMerge + where I: Iterator, + I::Item: PartialOrd +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + if self.heap.is_empty() { + return None; + } + let result = if let Some(next) = self.heap[0].next() { + next + } else { + self.heap.swap_remove(0).head + }; + sift_down(&mut self.heap, 0, |a, b| a.head < b.head); + Some(result) + } + + fn size_hint(&self) -> (usize, Option) { + self.heap.iter() + .map(|i| i.size_hint()) + .fold1(size_hint::add) + .unwrap_or((0, Some(0))) + } +} + +/// An iterator adaptor that merges an abitrary number of base iterators +/// according to an ordering function. +/// +/// Iterator element type is `I::Item`. +/// +/// See [`.kmerge_by()`](../trait.Itertools.html#method.kmerge_by) for more +/// information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct KMergeBy + where I: Iterator, +{ + heap: Vec>, + less_than: F, +} + +impl fmt::Debug for KMergeBy + where I: Iterator + fmt::Debug, + I::Item: fmt::Debug, +{ + debug_fmt_fields!(KMergeBy, heap); +} + +/// Create an iterator that merges elements of the contained iterators. +/// +/// Equivalent to `iterable.into_iter().kmerge_by(less_than)`. +pub fn kmerge_by(iterable: I, mut less_than: F) + -> KMergeBy<::IntoIter, F> + where I: IntoIterator, + I::Item: IntoIterator, + F: FnMut(&<::Item as IntoIterator>::Item, + &<::Item as IntoIterator>::Item) -> bool +{ + let iter = iterable.into_iter(); + let (lower, _) = iter.size_hint(); + let mut heap: Vec<_> = Vec::with_capacity(lower); + heap.extend(iter.filter_map(|it| HeadTail::new(it.into_iter()))); + heapify(&mut heap, |a, b| less_than(&a.head, &b.head)); + KMergeBy { heap: heap, less_than: less_than } +} + + +impl Iterator for KMergeBy + where I: Iterator, + F: FnMut(&I::Item, &I::Item) -> bool +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + if self.heap.is_empty() { + return None; + } + let result = if let Some(next) = self.heap[0].next() { + next + } else { + self.heap.swap_remove(0).head + }; + let less_than = &mut self.less_than; + sift_down(&mut self.heap, 0, |a, b| less_than(&a.head, &b.head)); + Some(result) + } + + fn size_hint(&self) -> (usize, Option) { + self.heap.iter() + .map(|i| i.size_hint()) + .fold1(size_hint::add) + .unwrap_or((0, Some(0))) + } +} diff --git a/third_party/rust/itertools-0.8.0/src/lib.rs b/third_party/rust/itertools-0.8.0/src/lib.rs new file mode 100644 index 0000000000000..ab98bcdf9b3a0 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/lib.rs @@ -0,0 +1,2176 @@ +#![warn(missing_docs)] +#![crate_name="itertools"] +#![cfg_attr(not(feature = "use_std"), no_std)] + +//! Extra iterator adaptors, functions and macros. +//! +//! To extend [`Iterator`] with methods in this crate, import +//! the [`Itertools` trait](./trait.Itertools.html): +//! +//! ``` +//! use itertools::Itertools; +//! ``` +//! +//! Now, new methods like [`interleave`](./trait.Itertools.html#method.interleave) +//! are available on all iterators: +//! +//! ``` +//! use itertools::Itertools; +//! +//! let it = (1..3).interleave(vec![-1, -2]); +//! itertools::assert_equal(it, vec![1, -1, 2, -2]); +//! ``` +//! +//! Most iterator methods are also provided as functions (with the benefit +//! that they convert parameters using [`IntoIterator`]): +//! +//! ``` +//! use itertools::interleave; +//! +//! for elt in interleave(&[1, 2, 3], &[2, 3, 4]) { +//! /* loop body */ +//! } +//! ``` +//! +//! ## Crate Features +//! +//! - `use_std` +//! - Enabled by default. +//! - Disable to compile itertools using `#![no_std]`. This disables +//! any items that depend on collections (like `group_by`, `unique`, +//! `kmerge`, `join` and many more). +//! +//! ## Rust Version +//! +//! This version of itertools requires Rust 1.24 or later. +//! +//! [`Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html +#![doc(html_root_url="https://docs.rs/itertools/0.8/")] + +extern crate either; + +#[cfg(not(feature = "use_std"))] +extern crate core as std; + +pub use either::Either; + +#[cfg(feature = "use_std")] +use std::collections::HashMap; +use std::iter::{IntoIterator}; +use std::cmp::Ordering; +use std::fmt; +#[cfg(feature = "use_std")] +use std::hash::Hash; +#[cfg(feature = "use_std")] +use std::fmt::Write; +#[cfg(feature = "use_std")] +type VecIntoIter = ::std::vec::IntoIter; +#[cfg(feature = "use_std")] +use std::iter::FromIterator; + +#[macro_use] +mod impl_macros; + +// for compatibility with no std and macros +#[doc(hidden)] +pub use std::iter as __std_iter; + +/// The concrete iterator types. +pub mod structs { + pub use adaptors::{ + Dedup, + Interleave, + InterleaveShortest, + Product, + PutBack, + Batching, + MapInto, + MapResults, + Merge, + MergeBy, + TakeWhileRef, + WhileSome, + Coalesce, + TupleCombinations, + Positions, + Update, + }; + #[allow(deprecated)] + pub use adaptors::Step; + #[cfg(feature = "use_std")] + pub use adaptors::MultiProduct; + #[cfg(feature = "use_std")] + pub use combinations::Combinations; + pub use cons_tuples_impl::ConsTuples; + pub use format::{Format, FormatWith}; + #[cfg(feature = "use_std")] + pub use groupbylazy::{IntoChunks, Chunk, Chunks, GroupBy, Group, Groups}; + pub use intersperse::Intersperse; + #[cfg(feature = "use_std")] + pub use kmerge_impl::{KMerge, KMergeBy}; + pub use merge_join::MergeJoinBy; + #[cfg(feature = "use_std")] + pub use multipeek_impl::MultiPeek; + pub use pad_tail::PadUsing; + pub use peeking_take_while::PeekingTakeWhile; + pub use process_results_impl::ProcessResults; + #[cfg(feature = "use_std")] + pub use put_back_n_impl::PutBackN; + #[cfg(feature = "use_std")] + pub use rciter_impl::RcIter; + pub use repeatn::RepeatN; + #[allow(deprecated)] + pub use sources::{RepeatCall, Unfold, Iterate}; + #[cfg(feature = "use_std")] + pub use tee::Tee; + pub use tuple_impl::{TupleBuffer, TupleWindows, Tuples}; + #[cfg(feature = "use_std")] + pub use unique_impl::{Unique, UniqueBy}; + pub use with_position::WithPosition; + pub use zip_eq_impl::ZipEq; + pub use zip_longest::ZipLongest; + pub use ziptuple::Zip; +} +#[allow(deprecated)] +pub use structs::*; +pub use concat_impl::concat; +pub use cons_tuples_impl::cons_tuples; +pub use diff::diff_with; +pub use diff::Diff; +#[cfg(feature = "use_std")] +pub use kmerge_impl::{kmerge_by}; +pub use minmax::MinMaxResult; +pub use peeking_take_while::PeekingNext; +pub use process_results_impl::process_results; +pub use repeatn::repeat_n; +#[allow(deprecated)] +pub use sources::{repeat_call, unfold, iterate}; +pub use with_position::Position; +pub use ziptuple::multizip; +mod adaptors; +mod either_or_both; +pub use either_or_both::EitherOrBoth; +#[doc(hidden)] +pub mod free; +#[doc(inline)] +pub use free::*; +mod concat_impl; +mod cons_tuples_impl; +#[cfg(feature = "use_std")] +mod combinations; +mod diff; +mod format; +#[cfg(feature = "use_std")] +mod group_map; +#[cfg(feature = "use_std")] +mod groupbylazy; +mod intersperse; +#[cfg(feature = "use_std")] +mod kmerge_impl; +mod merge_join; +mod minmax; +#[cfg(feature = "use_std")] +mod multipeek_impl; +mod pad_tail; +mod peeking_take_while; +mod process_results_impl; +#[cfg(feature = "use_std")] +mod put_back_n_impl; +#[cfg(feature = "use_std")] +mod rciter_impl; +mod repeatn; +mod size_hint; +mod sources; +#[cfg(feature = "use_std")] +mod tee; +mod tuple_impl; +#[cfg(feature = "use_std")] +mod unique_impl; +mod with_position; +mod zip_eq_impl; +mod zip_longest; +mod ziptuple; + +#[macro_export] +/// Create an iterator over the “cartesian product” of iterators. +/// +/// Iterator element type is like `(A, B, ..., E)` if formed +/// from iterators `(I, J, ..., M)` with element types `I::Item = A`, `J::Item = B`, etc. +/// +/// ``` +/// #[macro_use] extern crate itertools; +/// # fn main() { +/// // Iterate over the coordinates of a 4 x 4 x 4 grid +/// // from (0, 0, 0), (0, 0, 1), .., (0, 1, 0), (0, 1, 1), .. etc until (3, 3, 3) +/// for (i, j, k) in iproduct!(0..4, 0..4, 0..4) { +/// // .. +/// } +/// # } +/// ``` +/// +/// **Note:** To enable the macros in this crate, use the `#[macro_use]` +/// attribute when importing the crate: +/// +/// ``` +/// #[macro_use] extern crate itertools; +/// # fn main() { } +/// ``` +macro_rules! iproduct { + (@flatten $I:expr,) => ( + $I + ); + (@flatten $I:expr, $J:expr, $($K:expr,)*) => ( + iproduct!(@flatten $crate::cons_tuples(iproduct!($I, $J)), $($K,)*) + ); + ($I:expr) => ( + $crate::__std_iter::IntoIterator::into_iter($I) + ); + ($I:expr, $J:expr) => ( + $crate::Itertools::cartesian_product(iproduct!($I), iproduct!($J)) + ); + ($I:expr, $J:expr, $($K:expr),+) => ( + iproduct!(@flatten iproduct!($I, $J), $($K,)+) + ); +} + +#[macro_export] +/// Create an iterator running multiple iterators in lockstep. +/// +/// The `izip!` iterator yields elements until any subiterator +/// returns `None`. +/// +/// This is a version of the standard ``.zip()`` that's supporting more than +/// two iterators. The iterator element type is a tuple with one element +/// from each of the input iterators. Just like ``.zip()``, the iteration stops +/// when the shortest of the inputs reaches its end. +/// +/// **Note:** The result of this macro is in the general case an iterator +/// composed of repeated `.zip()` and a `.map()`; it has an anonymous type. +/// The special cases of one and two arguments produce the equivalent of +/// `$a.into_iter()` and `$a.into_iter().zip($b)` respectively. +/// +/// Prefer this macro `izip!()` over [`multizip`] for the performance benefits +/// of using the standard library `.zip()`. +/// +/// [`multizip`]: fn.multizip.html +/// +/// ``` +/// #[macro_use] extern crate itertools; +/// # fn main() { +/// +/// // iterate over three sequences side-by-side +/// let mut results = [0, 0, 0, 0]; +/// let inputs = [3, 7, 9, 6]; +/// +/// for (r, index, input) in izip!(&mut results, 0..10, &inputs) { +/// *r = index * 10 + input; +/// } +/// +/// assert_eq!(results, [0 + 3, 10 + 7, 29, 36]); +/// # } +/// ``` +/// +/// **Note:** To enable the macros in this crate, use the `#[macro_use]` +/// attribute when importing the crate: +/// +/// ``` +/// #[macro_use] extern crate itertools; +/// # fn main() { } +/// ``` +macro_rules! izip { + // @closure creates a tuple-flattening closure for .map() call. usage: + // @closure partial_pattern => partial_tuple , rest , of , iterators + // eg. izip!( @closure ((a, b), c) => (a, b, c) , dd , ee ) + ( @closure $p:pat => $tup:expr ) => { + |$p| $tup + }; + + // The "b" identifier is a different identifier on each recursion level thanks to hygiene. + ( @closure $p:pat => ( $($tup:tt)* ) , $_iter:expr $( , $tail:expr )* ) => { + izip!(@closure ($p, b) => ( $($tup)*, b ) $( , $tail )*) + }; + + // unary + ($first:expr $(,)*) => { + $crate::__std_iter::IntoIterator::into_iter($first) + }; + + // binary + ($first:expr, $second:expr $(,)*) => { + izip!($first) + .zip($second) + }; + + // n-ary where n > 2 + ( $first:expr $( , $rest:expr )* $(,)* ) => { + izip!($first) + $( + .zip($rest) + )* + .map( + izip!(@closure a => (a) $( , $rest )*) + ) + }; +} + +/// An [`Iterator`] blanket implementation that provides extra adaptors and +/// methods. +/// +/// This trait defines a number of methods. They are divided into two groups: +/// +/// * *Adaptors* take an iterator and parameter as input, and return +/// a new iterator value. These are listed first in the trait. An example +/// of an adaptor is [`.interleave()`](#method.interleave) +/// +/// * *Regular methods* are those that don't return iterators and instead +/// return a regular value of some other kind. +/// [`.next_tuple()`](#method.next_tuple) is an example and the first regular +/// method in the list. +/// +/// [`Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html +pub trait Itertools : Iterator { + // adaptors + + /// Alternate elements from two iterators until both have run out. + /// + /// Iterator element type is `Self::Item`. + /// + /// This iterator is *fused*. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let it = (1..7).interleave(vec![-1, -2]); + /// itertools::assert_equal(it, vec![1, -1, 2, -2, 3, 4, 5, 6]); + /// ``` + fn interleave(self, other: J) -> Interleave + where J: IntoIterator, + Self: Sized + { + interleave(self, other) + } + + /// Alternate elements from two iterators until at least one of them has run + /// out. + /// + /// Iterator element type is `Self::Item`. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let it = (1..7).interleave_shortest(vec![-1, -2]); + /// itertools::assert_equal(it, vec![1, -1, 2, -2, 3]); + /// ``` + fn interleave_shortest(self, other: J) -> InterleaveShortest + where J: IntoIterator, + Self: Sized + { + adaptors::interleave_shortest(self, other.into_iter()) + } + + /// An iterator adaptor to insert a particular value + /// between each element of the adapted iterator. + /// + /// Iterator element type is `Self::Item`. + /// + /// This iterator is *fused*. + /// + /// ``` + /// use itertools::Itertools; + /// + /// itertools::assert_equal((0..3).intersperse(8), vec![0, 8, 1, 8, 2]); + /// ``` + fn intersperse(self, element: Self::Item) -> Intersperse + where Self: Sized, + Self::Item: Clone + { + intersperse::intersperse(self, element) + } + + /// Create an iterator which iterates over both this and the specified + /// iterator simultaneously, yielding pairs of two optional elements. + /// + /// This iterator is *fused*. + /// + /// As long as neither input iterator is exhausted yet, it yields two values + /// via `EitherOrBoth::Both`. + /// + /// When the parameter iterator is exhausted, it only yields a value from the + /// `self` iterator via `EitherOrBoth::Left`. + /// + /// When the `self` iterator is exhausted, it only yields a value from the + /// parameter iterator via `EitherOrBoth::Right`. + /// + /// When both iterators return `None`, all further invocations of `.next()` + /// will return `None`. + /// + /// Iterator element type is + /// [`EitherOrBoth`](enum.EitherOrBoth.html). + /// + /// ```rust + /// use itertools::EitherOrBoth::{Both, Right}; + /// use itertools::Itertools; + /// let it = (0..1).zip_longest(1..3); + /// itertools::assert_equal(it, vec![Both(0, 1), Right(2)]); + /// ``` + #[inline] + fn zip_longest(self, other: J) -> ZipLongest + where J: IntoIterator, + Self: Sized + { + zip_longest::zip_longest(self, other.into_iter()) + } + + /// Create an iterator which iterates over both this and the specified + /// iterator simultaneously, yielding pairs of elements. + /// + /// **Panics** if the iterators reach an end and they are not of equal + /// lengths. + #[inline] + fn zip_eq(self, other: J) -> ZipEq + where J: IntoIterator, + Self: Sized + { + zip_eq(self, other) + } + + /// A “meta iterator adaptor”. Its closure receives a reference to the + /// iterator and may pick off as many elements as it likes, to produce the + /// next iterator element. + /// + /// Iterator element type is `B`. + /// + /// ``` + /// use itertools::Itertools; + /// + /// // An adaptor that gathers elements in pairs + /// let pit = (0..4).batching(|it| { + /// match it.next() { + /// None => None, + /// Some(x) => match it.next() { + /// None => None, + /// Some(y) => Some((x, y)), + /// } + /// } + /// }); + /// + /// itertools::assert_equal(pit, vec![(0, 1), (2, 3)]); + /// ``` + /// + fn batching(self, f: F) -> Batching + where F: FnMut(&mut Self) -> Option, + Self: Sized + { + adaptors::batching(self, f) + } + + /// Return an *iterable* that can group iterator elements. + /// Consecutive elements that map to the same key (“runs”), are assigned + /// to the same group. + /// + /// `GroupBy` is the storage for the lazy grouping operation. + /// + /// If the groups are consumed in order, or if each group's iterator is + /// dropped without keeping it around, then `GroupBy` uses no + /// allocations. It needs allocations only if several group iterators + /// are alive at the same time. + /// + /// This type implements `IntoIterator` (it is **not** an iterator + /// itself), because the group iterators need to borrow from this + /// value. It should be stored in a local variable or temporary and + /// iterated. + /// + /// Iterator element type is `(K, Group)`: the group's key and the + /// group iterator. + /// + /// ``` + /// use itertools::Itertools; + /// + /// // group data into runs of larger than zero or not. + /// let data = vec![1, 3, -2, -2, 1, 0, 1, 2]; + /// // groups: |---->|------>|--------->| + /// + /// // Note: The `&` is significant here, `GroupBy` is iterable + /// // only by reference. You can also call `.into_iter()` explicitly. + /// for (key, group) in &data.into_iter().group_by(|elt| *elt >= 0) { + /// // Check that the sum of each group is +/- 4. + /// assert_eq!(4, group.sum::().abs()); + /// } + /// ``` + #[cfg(feature = "use_std")] + fn group_by(self, key: F) -> GroupBy + where Self: Sized, + F: FnMut(&Self::Item) -> K, + K: PartialEq, + { + groupbylazy::new(self, key) + } + + /// Return an *iterable* that can chunk the iterator. + /// + /// Yield subiterators (chunks) that each yield a fixed number elements, + /// determined by `size`. The last chunk will be shorter if there aren't + /// enough elements. + /// + /// `IntoChunks` is based on `GroupBy`: it is iterable (implements + /// `IntoIterator`, **not** `Iterator`), and it only buffers if several + /// chunk iterators are alive at the same time. + /// + /// Iterator element type is `Chunk`, each chunk's iterator. + /// + /// **Panics** if `size` is 0. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let data = vec![1, 1, 2, -2, 6, 0, 3, 1]; + /// //chunk size=3 |------->|-------->|--->| + /// + /// // Note: The `&` is significant here, `IntoChunks` is iterable + /// // only by reference. You can also call `.into_iter()` explicitly. + /// for chunk in &data.into_iter().chunks(3) { + /// // Check that the sum of each chunk is 4. + /// assert_eq!(4, chunk.sum()); + /// } + /// ``` + #[cfg(feature = "use_std")] + fn chunks(self, size: usize) -> IntoChunks + where Self: Sized, + { + assert!(size != 0); + groupbylazy::new_chunks(self, size) + } + + /// Return an iterator over all contiguous windows producing tuples of + /// a specific size (up to 4). + /// + /// `tuple_windows` clones the iterator elements so that they can be + /// part of successive windows, this makes it most suited for iterators + /// of references and other values that are cheap to copy. + /// + /// ``` + /// use itertools::Itertools; + /// let mut v = Vec::new(); + /// for (a, b) in (1..5).tuple_windows() { + /// v.push((a, b)); + /// } + /// assert_eq!(v, vec![(1, 2), (2, 3), (3, 4)]); + /// + /// let mut it = (1..5).tuple_windows(); + /// assert_eq!(Some((1, 2, 3)), it.next()); + /// assert_eq!(Some((2, 3, 4)), it.next()); + /// assert_eq!(None, it.next()); + /// + /// // this requires a type hint + /// let it = (1..5).tuple_windows::<(_, _, _)>(); + /// itertools::assert_equal(it, vec![(1, 2, 3), (2, 3, 4)]); + /// + /// // you can also specify the complete type + /// use itertools::TupleWindows; + /// use std::ops::Range; + /// + /// let it: TupleWindows, (u32, u32, u32)> = (1..5).tuple_windows(); + /// itertools::assert_equal(it, vec![(1, 2, 3), (2, 3, 4)]); + /// ``` + fn tuple_windows(self) -> TupleWindows + where Self: Sized + Iterator, + T: tuple_impl::TupleCollect, + T::Item: Clone + { + tuple_impl::tuple_windows(self) + } + + /// Return an iterator that groups the items in tuples of a specific size + /// (up to 4). + /// + /// See also the method [`.next_tuple()`](#method.next_tuple). + /// + /// ``` + /// use itertools::Itertools; + /// let mut v = Vec::new(); + /// for (a, b) in (1..5).tuples() { + /// v.push((a, b)); + /// } + /// assert_eq!(v, vec![(1, 2), (3, 4)]); + /// + /// let mut it = (1..7).tuples(); + /// assert_eq!(Some((1, 2, 3)), it.next()); + /// assert_eq!(Some((4, 5, 6)), it.next()); + /// assert_eq!(None, it.next()); + /// + /// // this requires a type hint + /// let it = (1..7).tuples::<(_, _, _)>(); + /// itertools::assert_equal(it, vec![(1, 2, 3), (4, 5, 6)]); + /// + /// // you can also specify the complete type + /// use itertools::Tuples; + /// use std::ops::Range; + /// + /// let it: Tuples, (u32, u32, u32)> = (1..7).tuples(); + /// itertools::assert_equal(it, vec![(1, 2, 3), (4, 5, 6)]); + /// ``` + /// + /// See also [`Tuples::into_buffer`](structs/struct.Tuples.html#method.into_buffer). + fn tuples(self) -> Tuples + where Self: Sized + Iterator, + T: tuple_impl::TupleCollect + { + tuple_impl::tuples(self) + } + + /// Split into an iterator pair that both yield all elements from + /// the original iterator. + /// + /// **Note:** If the iterator is clonable, prefer using that instead + /// of using this method. It is likely to be more efficient. + /// + /// Iterator element type is `Self::Item`. + /// + /// ``` + /// use itertools::Itertools; + /// let xs = vec![0, 1, 2, 3]; + /// + /// let (mut t1, t2) = xs.into_iter().tee(); + /// itertools::assert_equal(t1.next(), Some(0)); + /// itertools::assert_equal(t2, 0..4); + /// itertools::assert_equal(t1, 1..4); + /// ``` + #[cfg(feature = "use_std")] + fn tee(self) -> (Tee, Tee) + where Self: Sized, + Self::Item: Clone + { + tee::new(self) + } + + /// Return an iterator adaptor that steps `n` elements in the base iterator + /// for each iteration. + /// + /// The iterator steps by yielding the next element from the base iterator, + /// then skipping forward `n - 1` elements. + /// + /// Iterator element type is `Self::Item`. + /// + /// **Panics** if the step is 0. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let it = (0..8).step(3); + /// itertools::assert_equal(it, vec![0, 3, 6]); + /// ``` + #[deprecated(note="Use std .step_by() instead", since="0.8")] + #[allow(deprecated)] + fn step(self, n: usize) -> Step + where Self: Sized + { + adaptors::step(self, n) + } + + /// Convert each item of the iterator using the `Into` trait. + /// + /// ```rust + /// use itertools::Itertools; + /// + /// (1i32..42i32).map_into::().collect_vec(); + /// ``` + fn map_into(self) -> MapInto + where Self: Sized, + Self::Item: Into, + { + adaptors::map_into(self) + } + + /// Return an iterator adaptor that applies the provided closure + /// to every `Result::Ok` value. `Result::Err` values are + /// unchanged. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let input = vec![Ok(41), Err(false), Ok(11)]; + /// let it = input.into_iter().map_results(|i| i + 1); + /// itertools::assert_equal(it, vec![Ok(42), Err(false), Ok(12)]); + /// ``` + fn map_results(self, f: F) -> MapResults + where Self: Iterator> + Sized, + F: FnMut(T) -> U, + { + adaptors::map_results(self, f) + } + + /// Return an iterator adaptor that merges the two base iterators in + /// ascending order. If both base iterators are sorted (ascending), the + /// result is sorted. + /// + /// Iterator element type is `Self::Item`. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let a = (0..11).step(3); + /// let b = (0..11).step(5); + /// let it = a.merge(b); + /// itertools::assert_equal(it, vec![0, 0, 3, 5, 6, 9, 10]); + /// ``` + fn merge(self, other: J) -> Merge + where Self: Sized, + Self::Item: PartialOrd, + J: IntoIterator + { + merge(self, other) + } + + /// Return an iterator adaptor that merges the two base iterators in order. + /// This is much like `.merge()` but allows for a custom ordering. + /// + /// This can be especially useful for sequences of tuples. + /// + /// Iterator element type is `Self::Item`. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let a = (0..).zip("bc".chars()); + /// let b = (0..).zip("ad".chars()); + /// let it = a.merge_by(b, |x, y| x.1 <= y.1); + /// itertools::assert_equal(it, vec![(0, 'a'), (0, 'b'), (1, 'c'), (1, 'd')]); + /// ``` + + fn merge_by(self, other: J, is_first: F) -> MergeBy + where Self: Sized, + J: IntoIterator, + F: FnMut(&Self::Item, &Self::Item) -> bool + { + adaptors::merge_by_new(self, other.into_iter(), is_first) + } + + /// Create an iterator that merges items from both this and the specified + /// iterator in ascending order. + /// + /// It chooses whether to pair elements based on the `Ordering` returned by the + /// specified compare function. At any point, inspecting the tip of the + /// iterators `I` and `J` as items `i` of type `I::Item` and `j` of type + /// `J::Item` respectively, the resulting iterator will: + /// + /// - Emit `EitherOrBoth::Left(i)` when `i < j`, + /// and remove `i` from its source iterator + /// - Emit `EitherOrBoth::Right(j)` when `i > j`, + /// and remove `j` from its source iterator + /// - Emit `EitherOrBoth::Both(i, j)` when `i == j`, + /// and remove both `i` and `j` from their respective source iterators + /// + /// ``` + /// use itertools::Itertools; + /// use itertools::EitherOrBoth::{Left, Right, Both}; + /// + /// let ki = (0..10).step(3); + /// let ku = (0..10).step(5); + /// let ki_ku = ki.merge_join_by(ku, |i, j| i.cmp(j)).map(|either| { + /// match either { + /// Left(_) => "Ki", + /// Right(_) => "Ku", + /// Both(_, _) => "KiKu" + /// } + /// }); + /// + /// itertools::assert_equal(ki_ku, vec!["KiKu", "Ki", "Ku", "Ki", "Ki"]); + /// ``` + #[inline] + fn merge_join_by(self, other: J, cmp_fn: F) -> MergeJoinBy + where J: IntoIterator, + F: FnMut(&Self::Item, &J::Item) -> std::cmp::Ordering, + Self: Sized + { + merge_join_by(self, other, cmp_fn) + } + + + /// Return an iterator adaptor that flattens an iterator of iterators by + /// merging them in ascending order. + /// + /// If all base iterators are sorted (ascending), the result is sorted. + /// + /// Iterator element type is `Self::Item`. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let a = (0..6).step(3); + /// let b = (1..6).step(3); + /// let c = (2..6).step(3); + /// let it = vec![a, b, c].into_iter().kmerge(); + /// itertools::assert_equal(it, vec![0, 1, 2, 3, 4, 5]); + /// ``` + #[cfg(feature = "use_std")] + fn kmerge(self) -> KMerge<::IntoIter> + where Self: Sized, + Self::Item: IntoIterator, + ::Item: PartialOrd, + { + kmerge(self) + } + + /// Return an iterator adaptor that flattens an iterator of iterators by + /// merging them according to the given closure. + /// + /// The closure `first` is called with two elements *a*, *b* and should + /// return `true` if *a* is ordered before *b*. + /// + /// If all base iterators are sorted according to `first`, the result is + /// sorted. + /// + /// Iterator element type is `Self::Item`. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let a = vec![-1f64, 2., 3., -5., 6., -7.]; + /// let b = vec![0., 2., -4.]; + /// let mut it = vec![a, b].into_iter().kmerge_by(|a, b| a.abs() < b.abs()); + /// assert_eq!(it.next(), Some(0.)); + /// assert_eq!(it.last(), Some(-7.)); + /// ``` + #[cfg(feature = "use_std")] + fn kmerge_by(self, first: F) + -> KMergeBy<::IntoIter, F> + where Self: Sized, + Self::Item: IntoIterator, + F: FnMut(&::Item, + &::Item) -> bool + { + kmerge_by(self, first) + } + + /// Return an iterator adaptor that iterates over the cartesian product of + /// the element sets of two iterators `self` and `J`. + /// + /// Iterator element type is `(Self::Item, J::Item)`. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let it = (0..2).cartesian_product("αβ".chars()); + /// itertools::assert_equal(it, vec![(0, 'α'), (0, 'β'), (1, 'α'), (1, 'β')]); + /// ``` + fn cartesian_product(self, other: J) -> Product + where Self: Sized, + Self::Item: Clone, + J: IntoIterator, + J::IntoIter: Clone + { + adaptors::cartesian_product(self, other.into_iter()) + } + + /// Return an iterator adaptor that iterates over the cartesian product of + /// all subiterators returned by meta-iterator `self`. + /// + /// All provided iterators must yield the same `Item` type. To generate + /// the product of iterators yielding multiple types, use the + /// [`iproduct`](macro.iproduct.html) macro instead. + /// + /// + /// The iterator element type is `Vec`, where `T` is the iterator element + /// of the subiterators. + /// + /// ``` + /// use itertools::Itertools; + /// let mut multi_prod = (0..3).map(|i| (i * 2)..(i * 2 + 2)) + /// .multi_cartesian_product(); + /// assert_eq!(multi_prod.next(), Some(vec![0, 2, 4])); + /// assert_eq!(multi_prod.next(), Some(vec![0, 2, 5])); + /// assert_eq!(multi_prod.next(), Some(vec![0, 3, 4])); + /// assert_eq!(multi_prod.next(), Some(vec![0, 3, 5])); + /// assert_eq!(multi_prod.next(), Some(vec![1, 2, 4])); + /// assert_eq!(multi_prod.next(), Some(vec![1, 2, 5])); + /// assert_eq!(multi_prod.next(), Some(vec![1, 3, 4])); + /// assert_eq!(multi_prod.next(), Some(vec![1, 3, 5])); + /// assert_eq!(multi_prod.next(), None); + /// ``` + #[cfg(feature = "use_std")] + fn multi_cartesian_product(self) -> MultiProduct<::IntoIter> + where Self: Iterator + Sized, + Self::Item: IntoIterator, + ::IntoIter: Clone, + ::Item: Clone + { + adaptors::multi_cartesian_product(self) + } + + /// Return an iterator adaptor that uses the passed-in closure to + /// optionally merge together consecutive elements. + /// + /// The closure `f` is passed two elements, `previous` and `current` and may + /// return either (1) `Ok(combined)` to merge the two values or + /// (2) `Err((previous', current'))` to indicate they can't be merged. + /// In (2), the value `previous'` is emitted by the iterator. + /// Either (1) `combined` or (2) `current'` becomes the previous value + /// when coalesce continues with the next pair of elements to merge. The + /// value that remains at the end is also emitted by the iterator. + /// + /// Iterator element type is `Self::Item`. + /// + /// This iterator is *fused*. + /// + /// ``` + /// use itertools::Itertools; + /// + /// // sum same-sign runs together + /// let data = vec![-1., -2., -3., 3., 1., 0., -1.]; + /// itertools::assert_equal(data.into_iter().coalesce(|x, y| + /// if (x >= 0.) == (y >= 0.) { + /// Ok(x + y) + /// } else { + /// Err((x, y)) + /// }), + /// vec![-6., 4., -1.]); + /// ``` + fn coalesce(self, f: F) -> Coalesce + where Self: Sized, + F: FnMut(Self::Item, Self::Item) + -> Result + { + adaptors::coalesce(self, f) + } + + /// Remove duplicates from sections of consecutive identical elements. + /// If the iterator is sorted, all elements will be unique. + /// + /// Iterator element type is `Self::Item`. + /// + /// This iterator is *fused*. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let data = vec![1., 1., 2., 3., 3., 2., 2.]; + /// itertools::assert_equal(data.into_iter().dedup(), + /// vec![1., 2., 3., 2.]); + /// ``` + fn dedup(self) -> Dedup + where Self: Sized, + Self::Item: PartialEq, + { + adaptors::dedup(self) + } + + /// Return an iterator adaptor that filters out elements that have + /// already been produced once during the iteration. Duplicates + /// are detected using hash and equality. + /// + /// Clones of visited elements are stored in a hash set in the + /// iterator. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let data = vec![10, 20, 30, 20, 40, 10, 50]; + /// itertools::assert_equal(data.into_iter().unique(), + /// vec![10, 20, 30, 40, 50]); + /// ``` + #[cfg(feature = "use_std")] + fn unique(self) -> Unique + where Self: Sized, + Self::Item: Clone + Eq + Hash + { + unique_impl::unique(self) + } + + /// Return an iterator adaptor that filters out elements that have + /// already been produced once during the iteration. + /// + /// Duplicates are detected by comparing the key they map to + /// with the keying function `f` by hash and equality. + /// The keys are stored in a hash set in the iterator. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let data = vec!["a", "bb", "aa", "c", "ccc"]; + /// itertools::assert_equal(data.into_iter().unique_by(|s| s.len()), + /// vec!["a", "bb", "ccc"]); + /// ``` + #[cfg(feature = "use_std")] + fn unique_by(self, f: F) -> UniqueBy + where Self: Sized, + V: Eq + Hash, + F: FnMut(&Self::Item) -> V + { + unique_impl::unique_by(self, f) + } + + /// Return an iterator adaptor that borrows from this iterator and + /// takes items while the closure `accept` returns `true`. + /// + /// This adaptor can only be used on iterators that implement `PeekingNext` + /// like `.peekable()`, `put_back` and a few other collection iterators. + /// + /// The last and rejected element (first `false`) is still available when + /// `peeking_take_while` is done. + /// + /// + /// See also [`.take_while_ref()`](#method.take_while_ref) + /// which is a similar adaptor. + fn peeking_take_while(&mut self, accept: F) -> PeekingTakeWhile + where Self: Sized + PeekingNext, + F: FnMut(&Self::Item) -> bool, + { + peeking_take_while::peeking_take_while(self, accept) + } + + /// Return an iterator adaptor that borrows from a `Clone`-able iterator + /// to only pick off elements while the predicate `accept` returns `true`. + /// + /// It uses the `Clone` trait to restore the original iterator so that the + /// last and rejected element (first `false`) is still available when + /// `take_while_ref` is done. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let mut hexadecimals = "0123456789abcdef".chars(); + /// + /// let decimals = hexadecimals.take_while_ref(|c| c.is_numeric()) + /// .collect::(); + /// assert_eq!(decimals, "0123456789"); + /// assert_eq!(hexadecimals.next(), Some('a')); + /// + /// ``` + fn take_while_ref(&mut self, accept: F) -> TakeWhileRef + where Self: Clone, + F: FnMut(&Self::Item) -> bool + { + adaptors::take_while_ref(self, accept) + } + + /// Return an iterator adaptor that filters `Option` iterator elements + /// and produces `A`. Stops on the first `None` encountered. + /// + /// Iterator element type is `A`, the unwrapped element. + /// + /// ``` + /// use itertools::Itertools; + /// + /// // List all hexadecimal digits + /// itertools::assert_equal( + /// (0..).map(|i| std::char::from_digit(i, 16)).while_some(), + /// "0123456789abcdef".chars()); + /// + /// ``` + fn while_some(self) -> WhileSome + where Self: Sized + Iterator> + { + adaptors::while_some(self) + } + + /// Return an iterator adaptor that iterates over the combinations of the + /// elements from an iterator. + /// + /// Iterator element can be any homogeneous tuple of type `Self::Item` with + /// size up to 4. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let mut v = Vec::new(); + /// for (a, b) in (1..5).tuple_combinations() { + /// v.push((a, b)); + /// } + /// assert_eq!(v, vec![(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]); + /// + /// let mut it = (1..5).tuple_combinations(); + /// assert_eq!(Some((1, 2, 3)), it.next()); + /// assert_eq!(Some((1, 2, 4)), it.next()); + /// assert_eq!(Some((1, 3, 4)), it.next()); + /// assert_eq!(Some((2, 3, 4)), it.next()); + /// assert_eq!(None, it.next()); + /// + /// // this requires a type hint + /// let it = (1..5).tuple_combinations::<(_, _, _)>(); + /// itertools::assert_equal(it, vec![(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]); + /// + /// // you can also specify the complete type + /// use itertools::TupleCombinations; + /// use std::ops::Range; + /// + /// let it: TupleCombinations, (u32, u32, u32)> = (1..5).tuple_combinations(); + /// itertools::assert_equal(it, vec![(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]); + /// ``` + fn tuple_combinations(self) -> TupleCombinations + where Self: Sized + Clone, + Self::Item: Clone, + T: adaptors::HasCombination, + { + adaptors::tuple_combinations(self) + } + + /// Return an iterator adaptor that iterates over the `n`-length combinations of + /// the elements from an iterator. + /// + /// Iterator element type is `Vec`. The iterator produces a new Vec per iteration, + /// and clones the iterator elements. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let it = (1..5).combinations(3); + /// itertools::assert_equal(it, vec![ + /// vec![1, 2, 3], + /// vec![1, 2, 4], + /// vec![1, 3, 4], + /// vec![2, 3, 4], + /// ]); + /// ``` + #[cfg(feature = "use_std")] + fn combinations(self, n: usize) -> Combinations + where Self: Sized, + Self::Item: Clone + { + combinations::combinations(self, n) + } + + /// Return an iterator adaptor that pads the sequence to a minimum length of + /// `min` by filling missing elements using a closure `f`. + /// + /// Iterator element type is `Self::Item`. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let it = (0..5).pad_using(10, |i| 2*i); + /// itertools::assert_equal(it, vec![0, 1, 2, 3, 4, 10, 12, 14, 16, 18]); + /// + /// let it = (0..10).pad_using(5, |i| 2*i); + /// itertools::assert_equal(it, vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); + /// + /// let it = (0..5).pad_using(10, |i| 2*i).rev(); + /// itertools::assert_equal(it, vec![18, 16, 14, 12, 10, 4, 3, 2, 1, 0]); + /// ``` + fn pad_using(self, min: usize, f: F) -> PadUsing + where Self: Sized, + F: FnMut(usize) -> Self::Item + { + pad_tail::pad_using(self, min, f) + } + + /// Return an iterator adaptor that wraps each element in a `Position` to + /// ease special-case handling of the first or last elements. + /// + /// Iterator element type is + /// [`Position`](enum.Position.html) + /// + /// ``` + /// use itertools::{Itertools, Position}; + /// + /// let it = (0..4).with_position(); + /// itertools::assert_equal(it, + /// vec![Position::First(0), + /// Position::Middle(1), + /// Position::Middle(2), + /// Position::Last(3)]); + /// + /// let it = (0..1).with_position(); + /// itertools::assert_equal(it, vec![Position::Only(0)]); + /// ``` + fn with_position(self) -> WithPosition + where Self: Sized, + { + with_position::with_position(self) + } + + /// Return an iterator adaptor that yields the indices of all elements + /// satisfying a predicate, counted from the start of the iterator. + /// + /// Equivalent to `iter.enumerate().filter(|(_, v)| predicate(v)).map(|(i, _)| i)`. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let data = vec![1, 2, 3, 3, 4, 6, 7, 9]; + /// itertools::assert_equal(data.iter().positions(|v| v % 2 == 0), vec![1, 4, 5]); + /// + /// itertools::assert_equal(data.iter().positions(|v| v % 2 == 1).rev(), vec![7, 6, 3, 2, 0]); + /// ``` + fn positions

(self, predicate: P) -> Positions + where Self: Sized, + P: FnMut(Self::Item) -> bool, + { + adaptors::positions(self, predicate) + } + + /// Return an iterator adaptor that applies a mutating function + /// to each element before yielding it. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let input = vec![vec![1], vec![3, 2, 1]]; + /// let it = input.into_iter().update(|mut v| v.push(0)); + /// itertools::assert_equal(it, vec![vec![1, 0], vec![3, 2, 1, 0]]); + /// ``` + fn update(self, updater: F) -> Update + where Self: Sized, + F: FnMut(&mut Self::Item), + { + adaptors::update(self, updater) + } + + // non-adaptor methods + /// Advances the iterator and returns the next items grouped in a tuple of + /// a specific size (up to 4). + /// + /// If there are enough elements to be grouped in a tuple, then the tuple is + /// returned inside `Some`, otherwise `None` is returned. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let mut iter = 1..5; + /// + /// assert_eq!(Some((1, 2)), iter.next_tuple()); + /// ``` + fn next_tuple(&mut self) -> Option + where Self: Sized + Iterator, + T: tuple_impl::TupleCollect + { + T::collect_from_iter_no_buf(self) + } + + /// Collects all items from the iterator into a tuple of a specific size + /// (up to 4). + /// + /// If the number of elements inside the iterator is **exactly** equal to + /// the tuple size, then the tuple is returned inside `Some`, otherwise + /// `None` is returned. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let iter = 1..3; + /// + /// if let Some((x, y)) = iter.collect_tuple() { + /// assert_eq!((x, y), (1, 2)) + /// } else { + /// panic!("Expected two elements") + /// } + /// ``` + fn collect_tuple(mut self) -> Option + where Self: Sized + Iterator, + T: tuple_impl::TupleCollect + { + match self.next_tuple() { + elt @ Some(_) => match self.next() { + Some(_) => None, + None => elt, + }, + _ => None + } + } + + + /// Find the position and value of the first element satisfying a predicate. + /// + /// The iterator is not advanced past the first element found. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let text = "Hα"; + /// assert_eq!(text.chars().find_position(|ch| ch.is_lowercase()), Some((1, 'α'))); + /// ``` + fn find_position

(&mut self, mut pred: P) -> Option<(usize, Self::Item)> + where P: FnMut(&Self::Item) -> bool + { + let mut index = 0usize; + for elt in self { + if pred(&elt) { + return Some((index, elt)); + } + index += 1; + } + None + } + + /// Check whether all elements compare equal. + /// + /// Empty iterators are considered to have equal elements: + /// + /// ``` + /// use itertools::Itertools; + /// + /// let data = vec![1, 1, 1, 2, 2, 3, 3, 3, 4, 5, 5]; + /// assert!(!data.iter().all_equal()); + /// assert!(data[0..3].iter().all_equal()); + /// assert!(data[3..5].iter().all_equal()); + /// assert!(data[5..8].iter().all_equal()); + /// + /// let data : Option = None; + /// assert!(data.into_iter().all_equal()); + /// ``` + fn all_equal(&mut self) -> bool + where Self::Item: PartialEq, + { + self.dedup().nth(1).is_none() + } + + /// Consume the first `n` elements from the iterator eagerly, + /// and return the same iterator again. + /// + /// It works similarly to *.skip(* `n` *)* except it is eager and + /// preserves the iterator type. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let mut iter = "αβγ".chars().dropping(2); + /// itertools::assert_equal(iter, "γ".chars()); + /// ``` + /// + /// *Fusing notes: if the iterator is exhausted by dropping, + /// the result of calling `.next()` again depends on the iterator implementation.* + fn dropping(mut self, n: usize) -> Self + where Self: Sized + { + if n > 0 { + self.nth(n - 1); + } + self + } + + /// Consume the last `n` elements from the iterator eagerly, + /// and return the same iterator again. + /// + /// This is only possible on double ended iterators. `n` may be + /// larger than the number of elements. + /// + /// Note: This method is eager, dropping the back elements immediately and + /// preserves the iterator type. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let init = vec![0, 3, 6, 9].into_iter().dropping_back(1); + /// itertools::assert_equal(init, vec![0, 3, 6]); + /// ``` + fn dropping_back(mut self, n: usize) -> Self + where Self: Sized, + Self: DoubleEndedIterator + { + if n > 0 { + (&mut self).rev().nth(n - 1); + } + self + } + + /// Run the closure `f` eagerly on each element of the iterator. + /// + /// Consumes the iterator until its end. + /// + /// ``` + /// use std::sync::mpsc::channel; + /// use itertools::Itertools; + /// + /// let (tx, rx) = channel(); + /// + /// // use .foreach() to apply a function to each value -- sending it + /// (0..5).map(|x| x * 2 + 1).foreach(|x| { tx.send(x).unwrap(); } ); + /// + /// drop(tx); + /// + /// itertools::assert_equal(rx.iter(), vec![1, 3, 5, 7, 9]); + /// ``` + #[deprecated(note="Use .for_each() instead", since="0.8")] + fn foreach(self, f: F) + where F: FnMut(Self::Item), + Self: Sized, + { + self.for_each(f) + } + + /// Combine all an iterator's elements into one element by using `Extend`. + /// + /// This combinator will extend the first item with each of the rest of the + /// items of the iterator. If the iterator is empty, the default value of + /// `I::Item` is returned. + /// + /// ```rust + /// use itertools::Itertools; + /// + /// let input = vec![vec![1], vec![2, 3], vec![4, 5, 6]]; + /// assert_eq!(input.into_iter().concat(), + /// vec![1, 2, 3, 4, 5, 6]); + /// ``` + fn concat(self) -> Self::Item + where Self: Sized, + Self::Item: Extend<<::Item as IntoIterator>::Item> + IntoIterator + Default + { + concat(self) + } + + /// `.collect_vec()` is simply a type specialization of `.collect()`, + /// for convenience. + #[cfg(feature = "use_std")] + fn collect_vec(self) -> Vec + where Self: Sized + { + self.collect() + } + + /// Assign to each reference in `self` from the `from` iterator, + /// stopping at the shortest of the two iterators. + /// + /// The `from` iterator is queried for its next element before the `self` + /// iterator, and if either is exhausted the method is done. + /// + /// Return the number of elements written. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let mut xs = [0; 4]; + /// xs.iter_mut().set_from(1..); + /// assert_eq!(xs, [1, 2, 3, 4]); + /// ``` + #[inline] + fn set_from<'a, A: 'a, J>(&mut self, from: J) -> usize + where Self: Iterator, + J: IntoIterator + { + let mut count = 0; + for elt in from { + match self.next() { + None => break, + Some(ptr) => *ptr = elt, + } + count += 1; + } + count + } + + /// Combine all iterator elements into one String, seperated by `sep`. + /// + /// Use the `Display` implementation of each element. + /// + /// ``` + /// use itertools::Itertools; + /// + /// assert_eq!(["a", "b", "c"].iter().join(", "), "a, b, c"); + /// assert_eq!([1, 2, 3].iter().join(", "), "1, 2, 3"); + /// ``` + #[cfg(feature = "use_std")] + fn join(&mut self, sep: &str) -> String + where Self::Item: std::fmt::Display + { + match self.next() { + None => String::new(), + Some(first_elt) => { + // estimate lower bound of capacity needed + let (lower, _) = self.size_hint(); + let mut result = String::with_capacity(sep.len() * lower); + write!(&mut result, "{}", first_elt).unwrap(); + for elt in self { + result.push_str(sep); + write!(&mut result, "{}", elt).unwrap(); + } + result + } + } + } + + /// Format all iterator elements, separated by `sep`. + /// + /// All elements are formatted (any formatting trait) + /// with `sep` inserted between each element. + /// + /// **Panics** if the formatter helper is formatted more than once. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let data = [1.1, 2.71828, -3.]; + /// assert_eq!( + /// format!("{:.2}", data.iter().format(", ")), + /// "1.10, 2.72, -3.00"); + /// ``` + fn format(self, sep: &str) -> Format + where Self: Sized, + { + format::new_format_default(self, sep) + } + + /// Format all iterator elements, separated by `sep`. + /// + /// This is a customizable version of `.format()`. + /// + /// The supplied closure `format` is called once per iterator element, + /// with two arguments: the element and a callback that takes a + /// `&Display` value, i.e. any reference to type that implements `Display`. + /// + /// Using `&format_args!(...)` is the most versatile way to apply custom + /// element formatting. The callback can be called multiple times if needed. + /// + /// **Panics** if the formatter helper is formatted more than once. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let data = [1.1, 2.71828, -3.]; + /// let data_formatter = data.iter().format_with(", ", |elt, f| f(&format_args!("{:.2}", elt))); + /// assert_eq!(format!("{}", data_formatter), + /// "1.10, 2.72, -3.00"); + /// + /// // .format_with() is recursively composable + /// let matrix = [[1., 2., 3.], + /// [4., 5., 6.]]; + /// let matrix_formatter = matrix.iter().format_with("\n", |row, f| { + /// f(&row.iter().format_with(", ", |elt, g| g(&elt))) + /// }); + /// assert_eq!(format!("{}", matrix_formatter), + /// "1, 2, 3\n4, 5, 6"); + /// + /// + /// ``` + fn format_with(self, sep: &str, format: F) -> FormatWith + where Self: Sized, + F: FnMut(Self::Item, &mut FnMut(&fmt::Display) -> fmt::Result) -> fmt::Result, + { + format::new_format(self, sep, format) + } + + /// Fold `Result` values from an iterator. + /// + /// Only `Ok` values are folded. If no error is encountered, the folded + /// value is returned inside `Ok`. Otherwise, the operation terminates + /// and returns the first `Err` value it encounters. No iterator elements are + /// consumed after the first error. + /// + /// The first accumulator value is the `start` parameter. + /// Each iteration passes the accumulator value and the next value inside `Ok` + /// to the fold function `f` and its return value becomes the new accumulator value. + /// + /// For example the sequence *Ok(1), Ok(2), Ok(3)* will result in a + /// computation like this: + /// + /// ```ignore + /// let mut accum = start; + /// accum = f(accum, 1); + /// accum = f(accum, 2); + /// accum = f(accum, 3); + /// ``` + /// + /// With a `start` value of 0 and an addition as folding function, + /// this effetively results in *((0 + 1) + 2) + 3* + /// + /// ``` + /// use std::ops::Add; + /// use itertools::Itertools; + /// + /// let values = [1, 2, -2, -1, 2, 1]; + /// assert_eq!( + /// values.iter() + /// .map(Ok::<_, ()>) + /// .fold_results(0, Add::add), + /// Ok(3) + /// ); + /// assert!( + /// values.iter() + /// .map(|&x| if x >= 0 { Ok(x) } else { Err("Negative number") }) + /// .fold_results(0, Add::add) + /// .is_err() + /// ); + /// ``` + fn fold_results(&mut self, mut start: B, mut f: F) -> Result + where Self: Iterator>, + F: FnMut(B, A) -> B + { + for elt in self { + match elt { + Ok(v) => start = f(start, v), + Err(u) => return Err(u), + } + } + Ok(start) + } + + /// Fold `Option` values from an iterator. + /// + /// Only `Some` values are folded. If no `None` is encountered, the folded + /// value is returned inside `Some`. Otherwise, the operation terminates + /// and returns `None`. No iterator elements are consumed after the `None`. + /// + /// This is the `Option` equivalent to `fold_results`. + /// + /// ``` + /// use std::ops::Add; + /// use itertools::Itertools; + /// + /// let mut values = vec![Some(1), Some(2), Some(-2)].into_iter(); + /// assert_eq!(values.fold_options(5, Add::add), Some(5 + 1 + 2 - 2)); + /// + /// let mut more_values = vec![Some(2), None, Some(0)].into_iter(); + /// assert!(more_values.fold_options(0, Add::add).is_none()); + /// assert_eq!(more_values.next().unwrap(), Some(0)); + /// ``` + fn fold_options(&mut self, mut start: B, mut f: F) -> Option + where Self: Iterator>, + F: FnMut(B, A) -> B + { + for elt in self { + match elt { + Some(v) => start = f(start, v), + None => return None, + } + } + Some(start) + } + + /// Accumulator of the elements in the iterator. + /// + /// Like `.fold()`, without a base case. If the iterator is + /// empty, return `None`. With just one element, return it. + /// Otherwise elements are accumulated in sequence using the closure `f`. + /// + /// ``` + /// use itertools::Itertools; + /// + /// assert_eq!((0..10).fold1(|x, y| x + y).unwrap_or(0), 45); + /// assert_eq!((0..0).fold1(|x, y| x * y), None); + /// ``` + fn fold1(mut self, f: F) -> Option + where F: FnMut(Self::Item, Self::Item) -> Self::Item, + Self: Sized, + { + self.next().map(move |x| self.fold(x, f)) + } + + /// Accumulate the elements in the iterator in a tree-like manner. + /// + /// You can think of it as, while there's more than one item, repeatedly + /// combining adjacent items. It does so in bottom-up-merge-sort order, + /// however, so that it needs only logarithmic stack space. + /// + /// This produces a call tree like the following (where the calls under + /// an item are done after reading that item): + /// + /// ```text + /// 1 2 3 4 5 6 7 + /// │ │ │ │ │ │ │ + /// └─f └─f └─f │ + /// │ │ │ │ + /// └───f └─f + /// │ │ + /// └─────f + /// ``` + /// + /// Which, for non-associative functions, will typically produce a different + /// result than the linear call tree used by `fold1`: + /// + /// ```text + /// 1 2 3 4 5 6 7 + /// │ │ │ │ │ │ │ + /// └─f─f─f─f─f─f + /// ``` + /// + /// If `f` is associative, prefer the normal `fold1` instead. + /// + /// ``` + /// use itertools::Itertools; + /// + /// // The same tree as above + /// let num_strings = (1..8).map(|x| x.to_string()); + /// assert_eq!(num_strings.tree_fold1(|x, y| format!("f({}, {})", x, y)), + /// Some(String::from("f(f(f(1, 2), f(3, 4)), f(f(5, 6), 7))"))); + /// + /// // Like fold1, an empty iterator produces None + /// assert_eq!((0..0).tree_fold1(|x, y| x * y), None); + /// + /// // tree_fold1 matches fold1 for associative operations... + /// assert_eq!((0..10).tree_fold1(|x, y| x + y), + /// (0..10).fold1(|x, y| x + y)); + /// // ...but not for non-associative ones + /// assert!((0..10).tree_fold1(|x, y| x - y) + /// != (0..10).fold1(|x, y| x - y)); + /// ``` + // FIXME: If minver changes to >= 1.13, use `assert_ne!` in the doctest. + fn tree_fold1(mut self, mut f: F) -> Option + where F: FnMut(Self::Item, Self::Item) -> Self::Item, + Self: Sized, + { + type State = Result>; + + fn inner0(it: &mut II, f: &mut FF) -> State + where + II: Iterator, + FF: FnMut(T, T) -> T + { + // This function could be replaced with `it.next().ok_or(None)`, + // but half the useful tree_fold1 work is combining adjacent items, + // so put that in a form that LLVM is more likely to optimize well. + + let a = + if let Some(v) = it.next() { v } + else { return Err(None) }; + let b = + if let Some(v) = it.next() { v } + else { return Err(Some(a)) }; + Ok(f(a, b)) + } + + fn inner(stop: usize, it: &mut II, f: &mut FF) -> State + where + II: Iterator, + FF: FnMut(T, T) -> T + { + let mut x = try!(inner0(it, f)); + for height in 0..stop { + // Try to get another tree the same size with which to combine it, + // creating a new tree that's twice as big for next time around. + let next = + if height == 0 { + inner0(it, f) + } else { + inner(height, it, f) + }; + match next { + Ok(y) => x = f(x, y), + + // If we ran out of items, combine whatever we did manage + // to get. It's better combined with the current value + // than something in a parent frame, because the tree in + // the parent is always as least as big as this one. + Err(None) => return Err(Some(x)), + Err(Some(y)) => return Err(Some(f(x, y))), + } + } + Ok(x) + } + + match inner(usize::max_value(), &mut self, &mut f) { + Err(x) => x, + _ => unreachable!(), + } + } + + /// An iterator method that applies a function, producing a single, final value. + /// + /// `fold_while()` is basically equivalent to `fold()` but with additional support for + /// early exit via short-circuiting. + /// + /// ``` + /// use itertools::Itertools; + /// use itertools::FoldWhile::{Continue, Done}; + /// + /// let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + /// + /// let mut result = 0; + /// + /// // for loop: + /// for i in &numbers { + /// if *i > 5 { + /// break; + /// } + /// result = result + i; + /// } + /// + /// // fold: + /// let result2 = numbers.iter().fold(0, |acc, x| { + /// if *x > 5 { acc } else { acc + x } + /// }); + /// + /// // fold_while: + /// let result3 = numbers.iter().fold_while(0, |acc, x| { + /// if *x > 5 { Done(acc) } else { Continue(acc + x) } + /// }).into_inner(); + /// + /// // they're the same + /// assert_eq!(result, result2); + /// assert_eq!(result2, result3); + /// ``` + /// + /// The big difference between the computations of `result2` and `result3` is that while + /// `fold()` called the provided closure for every item of the callee iterator, + /// `fold_while()` actually stopped iterating as soon as it encountered `Fold::Done(_)`. + #[deprecated(note="Use .try_fold() instead", since="0.8")] + fn fold_while(&mut self, init: B, mut f: F) -> FoldWhile + where Self: Sized, + F: FnMut(B, Self::Item) -> FoldWhile + { + let mut acc = init; + while let Some(item) = self.next() { + match f(acc, item) { + FoldWhile::Continue(res) => acc = res, + res @ FoldWhile::Done(_) => return res, + } + } + FoldWhile::Continue(acc) + } + + /// Sort all iterator elements into a new iterator in ascending order. + /// + /// **Note:** This consumes the entire iterator, uses the + /// `slice::sort()` method and returns the result as a new + /// iterator that owns its elements. + /// + /// The sorted iterator, if directly collected to a `Vec`, is converted + /// without any extra copying or allocation cost. + /// + /// ``` + /// use itertools::Itertools; + /// + /// // sort the letters of the text in ascending order + /// let text = "bdacfe"; + /// itertools::assert_equal(text.chars().sorted(), + /// "abcdef".chars()); + /// ``` + #[cfg(feature = "use_std")] + fn sorted(self) -> VecIntoIter + where Self: Sized, + Self::Item: Ord + { + // Use .sort() directly since it is not quite identical with + // .sort_by(Ord::cmp) + let mut v = Vec::from_iter(self); + v.sort(); + v.into_iter() + } + + /// Sort all iterator elements into a new iterator in ascending order. + /// + /// **Note:** This consumes the entire iterator, uses the + /// `slice::sort_by()` method and returns the result as a new + /// iterator that owns its elements. + /// + /// The sorted iterator, if directly collected to a `Vec`, is converted + /// without any extra copying or allocation cost. + /// + /// ``` + /// use itertools::Itertools; + /// + /// // sort people in descending order by age + /// let people = vec![("Jane", 20), ("John", 18), ("Jill", 30), ("Jack", 27)]; + /// + /// let oldest_people_first = people + /// .into_iter() + /// .sorted_by(|a, b| Ord::cmp(&b.1, &a.1)) + /// .map(|(person, _age)| person); + /// + /// itertools::assert_equal(oldest_people_first, + /// vec!["Jill", "Jack", "Jane", "John"]); + /// ``` + #[cfg(feature = "use_std")] + fn sorted_by(self, cmp: F) -> VecIntoIter + where Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, + { + let mut v = Vec::from_iter(self); + v.sort_by(cmp); + v.into_iter() + } + + /// Sort all iterator elements into a new iterator in ascending order. + /// + /// **Note:** This consumes the entire iterator, uses the + /// `slice::sort_by_key()` method and returns the result as a new + /// iterator that owns its elements. + /// + /// The sorted iterator, if directly collected to a `Vec`, is converted + /// without any extra copying or allocation cost. + /// + /// ``` + /// use itertools::Itertools; + /// + /// // sort people in descending order by age + /// let people = vec![("Jane", 20), ("John", 18), ("Jill", 30), ("Jack", 27)]; + /// + /// let oldest_people_first = people + /// .into_iter() + /// .sorted_by_key(|x| -x.1) + /// .map(|(person, _age)| person); + /// + /// itertools::assert_equal(oldest_people_first, + /// vec!["Jill", "Jack", "Jane", "John"]); + /// ``` + #[cfg(feature = "use_std")] + fn sorted_by_key(self, f: F) -> VecIntoIter + where Self: Sized, + K: Ord, + F: FnMut(&Self::Item) -> K, + { + let mut v = Vec::from_iter(self); + v.sort_by_key(f); + v.into_iter() + } + + /// Collect all iterator elements into one of two + /// partitions. Unlike `Iterator::partition`, each partition may + /// have a distinct type. + /// + /// ``` + /// use itertools::{Itertools, Either}; + /// + /// let successes_and_failures = vec![Ok(1), Err(false), Err(true), Ok(2)]; + /// + /// let (successes, failures): (Vec<_>, Vec<_>) = successes_and_failures + /// .into_iter() + /// .partition_map(|r| { + /// match r { + /// Ok(v) => Either::Left(v), + /// Err(v) => Either::Right(v), + /// } + /// }); + /// + /// assert_eq!(successes, [1, 2]); + /// assert_eq!(failures, [false, true]); + /// ``` + fn partition_map(self, predicate: F) -> (A, B) + where Self: Sized, + F: Fn(Self::Item) -> Either, + A: Default + Extend, + B: Default + Extend, + { + let mut left = A::default(); + let mut right = B::default(); + + for val in self { + match predicate(val) { + Either::Left(v) => left.extend(Some(v)), + Either::Right(v) => right.extend(Some(v)), + } + } + + (left, right) + } + + /// Return a `HashMap` of keys mapped to `Vec`s of values. Keys and values + /// are taken from `(Key, Value)` tuple pairs yielded by the input iterator. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let data = vec![(0, 10), (2, 12), (3, 13), (0, 20), (3, 33), (2, 42)]; + /// let lookup = data.into_iter().into_group_map(); + /// + /// assert_eq!(lookup[&0], vec![10, 20]); + /// assert_eq!(lookup.get(&1), None); + /// assert_eq!(lookup[&2], vec![12, 42]); + /// assert_eq!(lookup[&3], vec![13, 33]); + /// ``` + #[cfg(feature = "use_std")] + fn into_group_map(self) -> HashMap> + where Self: Iterator + Sized, + K: Hash + Eq, + { + group_map::into_group_map(self) + } + + /// Return the minimum and maximum elements in the iterator. + /// + /// The return type `MinMaxResult` is an enum of three variants: + /// + /// - `NoElements` if the iterator is empty. + /// - `OneElement(x)` if the iterator has exactly one element. + /// - `MinMax(x, y)` is returned otherwise, where `x <= y`. Two + /// values are equal if and only if there is more than one + /// element in the iterator and all elements are equal. + /// + /// On an iterator of length `n`, `minmax` does `1.5 * n` comparisons, + /// and so is faster than calling `min` and `max` separately which does + /// `2 * n` comparisons. + /// + /// # Examples + /// + /// ``` + /// use itertools::Itertools; + /// use itertools::MinMaxResult::{NoElements, OneElement, MinMax}; + /// + /// let a: [i32; 0] = []; + /// assert_eq!(a.iter().minmax(), NoElements); + /// + /// let a = [1]; + /// assert_eq!(a.iter().minmax(), OneElement(&1)); + /// + /// let a = [1, 2, 3, 4, 5]; + /// assert_eq!(a.iter().minmax(), MinMax(&1, &5)); + /// + /// let a = [1, 1, 1, 1]; + /// assert_eq!(a.iter().minmax(), MinMax(&1, &1)); + /// ``` + /// + /// The elements can be floats but no particular result is guaranteed + /// if an element is NaN. + fn minmax(self) -> MinMaxResult + where Self: Sized, Self::Item: PartialOrd + { + minmax::minmax_impl(self, |_| (), |x, y, _, _| x < y) + } + + /// Return the minimum and maximum element of an iterator, as determined by + /// the specified function. + /// + /// The return value is a variant of `MinMaxResult` like for `minmax()`. + /// + /// For the minimum, the first minimal element is returned. For the maximum, + /// the last maximal element wins. This matches the behavior of the standard + /// `Iterator::min()` and `Iterator::max()` methods. + /// + /// The keys can be floats but no particular result is guaranteed + /// if a key is NaN. + fn minmax_by_key(self, key: F) -> MinMaxResult + where Self: Sized, K: PartialOrd, F: FnMut(&Self::Item) -> K + { + minmax::minmax_impl(self, key, |_, _, xk, yk| xk < yk) + } + + /// Return the minimum and maximum element of an iterator, as determined by + /// the specified comparison function. + /// + /// The return value is a variant of `MinMaxResult` like for `minmax()`. + /// + /// For the minimum, the first minimal element is returned. For the maximum, + /// the last maximal element wins. This matches the behavior of the standard + /// `Iterator::min()` and `Iterator::max()` methods. + fn minmax_by(self, mut compare: F) -> MinMaxResult + where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering + { + minmax::minmax_impl( + self, + |_| (), + |x, y, _, _| Ordering::Less == compare(x, y) + ) + } +} + +impl Itertools for T where T: Iterator { } + +/// Return `true` if both iterables produce equal sequences +/// (elements pairwise equal and sequences of the same length), +/// `false` otherwise. +/// +/// This is an `IntoIterator` enabled function that is similar to the standard +/// library method `Iterator::eq`. +/// +/// ``` +/// assert!(itertools::equal(vec![1, 2, 3], 1..4)); +/// assert!(!itertools::equal(&[0, 0], &[0, 0, 0])); +/// ``` +pub fn equal(a: I, b: J) -> bool + where I: IntoIterator, + J: IntoIterator, + I::Item: PartialEq +{ + let mut ia = a.into_iter(); + let mut ib = b.into_iter(); + loop { + match ia.next() { + Some(x) => match ib.next() { + Some(y) => if x != y { return false; }, + None => return false, + }, + None => return ib.next().is_none() + } + } +} + +/// Assert that two iterables produce equal sequences, with the same +/// semantics as *equal(a, b)*. +/// +/// **Panics** on assertion failure with a message that shows the +/// two iteration elements. +/// +/// ```ignore +/// assert_equal("exceed".split('c'), "excess".split('c')); +/// // ^PANIC: panicked at 'Failed assertion Some("eed") == Some("ess") for iteration 1', +/// ``` +pub fn assert_equal(a: I, b: J) + where I: IntoIterator, + J: IntoIterator, + I::Item: fmt::Debug + PartialEq, + J::Item: fmt::Debug, +{ + let mut ia = a.into_iter(); + let mut ib = b.into_iter(); + let mut i = 0; + loop { + match (ia.next(), ib.next()) { + (None, None) => return, + (a, b) => { + let equal = match (&a, &b) { + (&Some(ref a), &Some(ref b)) => a == b, + _ => false, + }; + assert!(equal, "Failed assertion {a:?} == {b:?} for iteration {i}", + i=i, a=a, b=b); + i += 1; + } + } + } +} + +/// Partition a sequence using predicate `pred` so that elements +/// that map to `true` are placed before elements which map to `false`. +/// +/// The order within the partitions is arbitrary. +/// +/// Return the index of the split point. +/// +/// ``` +/// use itertools::partition; +/// +/// # // use repeated numbers to not promise any ordering +/// let mut data = [7, 1, 1, 7, 1, 1, 7]; +/// let split_index = partition(&mut data, |elt| *elt >= 3); +/// +/// assert_eq!(data, [7, 7, 7, 1, 1, 1, 1]); +/// assert_eq!(split_index, 3); +/// ``` +pub fn partition<'a, A: 'a, I, F>(iter: I, mut pred: F) -> usize + where I: IntoIterator, + I::IntoIter: DoubleEndedIterator, + F: FnMut(&A) -> bool +{ + let mut split_index = 0; + let mut iter = iter.into_iter(); + 'main: while let Some(front) = iter.next() { + if !pred(front) { + loop { + match iter.next_back() { + Some(back) => if pred(back) { + std::mem::swap(front, back); + break; + }, + None => break 'main, + } + } + } + split_index += 1; + } + split_index +} + +/// An enum used for controlling the execution of `.fold_while()`. +/// +/// See [`.fold_while()`](trait.Itertools.html#method.fold_while) for more information. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum FoldWhile { + /// Continue folding with this value + Continue(T), + /// Fold is complete and will return this value + Done(T), +} + +impl FoldWhile { + /// Return the value in the continue or done. + pub fn into_inner(self) -> T { + match self { + FoldWhile::Continue(x) | FoldWhile::Done(x) => x, + } + } + + /// Return true if `self` is `Done`, false if it is `Continue`. + pub fn is_done(&self) -> bool { + match *self { + FoldWhile::Continue(_) => false, + FoldWhile::Done(_) => true, + } + } +} diff --git a/third_party/rust/itertools-0.8.0/src/merge_join.rs b/third_party/rust/itertools-0.8.0/src/merge_join.rs new file mode 100644 index 0000000000000..5f9a0f40132f5 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/merge_join.rs @@ -0,0 +1,87 @@ +use std::cmp::Ordering; +use std::iter::Fuse; +use std::fmt; + +use super::adaptors::{PutBack, put_back}; +use either_or_both::EitherOrBoth; + +/// Return an iterator adaptor that merge-joins items from the two base iterators in ascending order. +/// +/// See [`.merge_join_by()`](trait.Itertools.html#method.merge_join_by) for more information. +pub fn merge_join_by(left: I, right: J, cmp_fn: F) + -> MergeJoinBy + where I: IntoIterator, + J: IntoIterator, + F: FnMut(&I::Item, &J::Item) -> Ordering +{ + MergeJoinBy { + left: put_back(left.into_iter().fuse()), + right: put_back(right.into_iter().fuse()), + cmp_fn: cmp_fn + } +} + +/// An iterator adaptor that merge-joins items from the two base iterators in ascending order. +/// +/// See [`.merge_join_by()`](../trait.Itertools.html#method.merge_join_by) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct MergeJoinBy { + left: PutBack>, + right: PutBack>, + cmp_fn: F +} + +impl fmt::Debug for MergeJoinBy + where I: Iterator + fmt::Debug, + I::Item: fmt::Debug, + J: Iterator + fmt::Debug, + J::Item: fmt::Debug, +{ + debug_fmt_fields!(MergeJoinBy, left, right); +} + +impl Iterator for MergeJoinBy + where I: Iterator, + J: Iterator, + F: FnMut(&I::Item, &J::Item) -> Ordering +{ + type Item = EitherOrBoth; + + fn next(&mut self) -> Option { + match (self.left.next(), self.right.next()) { + (None, None) => None, + (Some(left), None) => + Some(EitherOrBoth::Left(left)), + (None, Some(right)) => + Some(EitherOrBoth::Right(right)), + (Some(left), Some(right)) => { + match (self.cmp_fn)(&left, &right) { + Ordering::Equal => + Some(EitherOrBoth::Both(left, right)), + Ordering::Less => { + self.right.put_back(right); + Some(EitherOrBoth::Left(left)) + }, + Ordering::Greater => { + self.left.put_back(left); + Some(EitherOrBoth::Right(right)) + } + } + } + } + } + + fn size_hint(&self) -> (usize, Option) { + let (a_lower, a_upper) = self.left.size_hint(); + let (b_lower, b_upper) = self.right.size_hint(); + + let lower = ::std::cmp::max(a_lower, b_lower); + + let upper = match (a_upper, b_upper) { + (Some(x), Some(y)) => Some(x + y), + _ => None, + }; + + (lower, upper) + } +} diff --git a/third_party/rust/itertools-0.8.0/src/minmax.rs b/third_party/rust/itertools-0.8.0/src/minmax.rs new file mode 100644 index 0000000000000..38180ef6d0935 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/minmax.rs @@ -0,0 +1,114 @@ + +/// `MinMaxResult` is an enum returned by `minmax`. See `Itertools::minmax()` for +/// more detail. +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum MinMaxResult { + /// Empty iterator + NoElements, + + /// Iterator with one element, so the minimum and maximum are the same + OneElement(T), + + /// More than one element in the iterator, the first element is not larger + /// than the second + MinMax(T, T) +} + +impl MinMaxResult { + /// `into_option` creates an `Option` of type `(T, T)`. The returned `Option` + /// has variant `None` if and only if the `MinMaxResult` has variant + /// `NoElements`. Otherwise `Some((x, y))` is returned where `x <= y`. + /// If the `MinMaxResult` has variant `OneElement(x)`, performing this + /// operation will make one clone of `x`. + /// + /// # Examples + /// + /// ``` + /// use itertools::MinMaxResult::{self, NoElements, OneElement, MinMax}; + /// + /// let r: MinMaxResult = NoElements; + /// assert_eq!(r.into_option(), None); + /// + /// let r = OneElement(1); + /// assert_eq!(r.into_option(), Some((1, 1))); + /// + /// let r = MinMax(1, 2); + /// assert_eq!(r.into_option(), Some((1, 2))); + /// ``` + pub fn into_option(self) -> Option<(T,T)> { + match self { + MinMaxResult::NoElements => None, + MinMaxResult::OneElement(x) => Some((x.clone(), x)), + MinMaxResult::MinMax(x, y) => Some((x, y)) + } + } +} + +/// Implementation guts for `minmax` and `minmax_by_key`. +pub fn minmax_impl(mut it: I, mut key_for: F, + mut lt: L) -> MinMaxResult + where I: Iterator, + F: FnMut(&I::Item) -> K, + L: FnMut(&I::Item, &I::Item, &K, &K) -> bool, +{ + let (mut min, mut max, mut min_key, mut max_key) = match it.next() { + None => return MinMaxResult::NoElements, + Some(x) => { + match it.next() { + None => return MinMaxResult::OneElement(x), + Some(y) => { + let xk = key_for(&x); + let yk = key_for(&y); + if !lt(&y, &x, &yk, &xk) {(x, y, xk, yk)} else {(y, x, yk, xk)} + } + } + } + }; + + loop { + // `first` and `second` are the two next elements we want to look + // at. We first compare `first` and `second` (#1). The smaller one + // is then compared to current minimum (#2). The larger one is + // compared to current maximum (#3). This way we do 3 comparisons + // for 2 elements. + let first = match it.next() { + None => break, + Some(x) => x + }; + let second = match it.next() { + None => { + let first_key = key_for(&first); + if lt(&first, &min, &first_key, &min_key) { + min = first; + } else if !lt(&first, &max, &first_key, &max_key) { + max = first; + } + break; + } + Some(x) => x + }; + let first_key = key_for(&first); + let second_key = key_for(&second); + if !lt(&second, &first, &second_key, &first_key) { + if lt(&first, &min, &first_key, &min_key) { + min = first; + min_key = first_key; + } + if !lt(&second, &max, &second_key, &max_key) { + max = second; + max_key = second_key; + } + } else { + if lt(&second, &min, &second_key, &min_key) { + min = second; + min_key = second_key; + } + if !lt(&first, &max, &first_key, &max_key) { + max = first; + max_key = first_key; + } + } + } + + MinMaxResult::MinMax(min, max) +} diff --git a/third_party/rust/itertools-0.8.0/src/multipeek_impl.rs b/third_party/rust/itertools-0.8.0/src/multipeek_impl.rs new file mode 100644 index 0000000000000..a6a2fb33ebc46 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/multipeek_impl.rs @@ -0,0 +1,104 @@ + + +use std::iter::Fuse; +use std::collections::VecDeque; +use size_hint; +use PeekingNext; + +/// See [`multipeek()`](../fn.multipeek.html) for more information. +#[derive(Clone, Debug)] +pub struct MultiPeek + where I: Iterator +{ + iter: Fuse, + buf: VecDeque, + index: usize, +} + +/// An iterator adaptor that allows the user to peek at multiple `.next()` +/// values without advancing the base iterator. +pub fn multipeek(iterable: I) -> MultiPeek + where I: IntoIterator +{ + MultiPeek { + iter: iterable.into_iter().fuse(), + buf: VecDeque::new(), + index: 0, + } +} + +impl MultiPeek + where I: Iterator +{ + /// Reset the peeking “cursor” + pub fn reset_peek(&mut self) { + self.index = 0; + } +} + +impl MultiPeek { + /// Works exactly like `.next()` with the only difference that it doesn't + /// advance itself. `.peek()` can be called multiple times, to peek + /// further ahead. + pub fn peek(&mut self) -> Option<&I::Item> { + let ret = if self.index < self.buf.len() { + Some(&self.buf[self.index]) + } else { + match self.iter.next() { + Some(x) => { + self.buf.push_back(x); + Some(&self.buf[self.index]) + } + None => return None, + } + }; + + self.index += 1; + ret + } +} + +impl PeekingNext for MultiPeek + where I: Iterator, +{ + fn peeking_next(&mut self, accept: F) -> Option + where F: FnOnce(&Self::Item) -> bool + { + if self.buf.is_empty() { + if let Some(r) = self.peek() { + if !accept(r) { return None } + } + } else { + if let Some(r) = self.buf.get(0) { + if !accept(r) { return None } + } + } + self.next() + } +} + +impl Iterator for MultiPeek + where I: Iterator +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + self.index = 0; + if self.buf.is_empty() { + self.iter.next() + } else { + self.buf.pop_front() + } + } + + fn size_hint(&self) -> (usize, Option) { + size_hint::add_scalar(self.iter.size_hint(), self.buf.len()) + } +} + +// Same size +impl ExactSizeIterator for MultiPeek + where I: ExactSizeIterator +{} + + diff --git a/third_party/rust/itertools-0.8.0/src/pad_tail.rs b/third_party/rust/itertools-0.8.0/src/pad_tail.rs new file mode 100644 index 0000000000000..c9cfe6af30b86 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/pad_tail.rs @@ -0,0 +1,83 @@ +use std::iter::Fuse; +use size_hint; + +/// An iterator adaptor that pads a sequence to a minimum length by filling +/// missing elements using a closure. +/// +/// Iterator element type is `I::Item`. +/// +/// See [`.pad_using()`](../trait.Itertools.html#method.pad_using) for more information. +#[derive(Clone)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct PadUsing { + iter: Fuse, + min: usize, + pos: usize, + filler: F, +} + +/// Create a new **PadUsing** iterator. +pub fn pad_using(iter: I, min: usize, filler: F) -> PadUsing + where I: Iterator, + F: FnMut(usize) -> I::Item +{ + PadUsing { + iter: iter.fuse(), + min: min, + pos: 0, + filler: filler, + } +} + +impl Iterator for PadUsing + where I: Iterator, + F: FnMut(usize) -> I::Item +{ + type Item = I::Item; + + #[inline] + fn next(&mut self) -> Option { + match self.iter.next() { + None => { + if self.pos < self.min { + let e = Some((self.filler)(self.pos)); + self.pos += 1; + e + } else { + None + } + }, + e => { + self.pos += 1; + e + } + } + } + + fn size_hint(&self) -> (usize, Option) { + let tail = self.min.saturating_sub(self.pos); + size_hint::max(self.iter.size_hint(), (tail, Some(tail))) + } +} + +impl DoubleEndedIterator for PadUsing + where I: DoubleEndedIterator + ExactSizeIterator, + F: FnMut(usize) -> I::Item +{ + fn next_back(&mut self) -> Option { + if self.min == 0 { + self.iter.next_back() + } else if self.iter.len() >= self.min { + self.min -= 1; + self.iter.next_back() + } else { + self.min -= 1; + Some((self.filler)(self.min)) + } + } +} + +impl ExactSizeIterator for PadUsing + where I: ExactSizeIterator, + F: FnMut(usize) -> I::Item +{} diff --git a/third_party/rust/itertools-0.8.0/src/peeking_take_while.rs b/third_party/rust/itertools-0.8.0/src/peeking_take_while.rs new file mode 100644 index 0000000000000..0b2291dfdd9c1 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/peeking_take_while.rs @@ -0,0 +1,149 @@ + +use std::iter::Peekable; +use PutBack; +#[cfg(feature = "use_std")] +use PutBackN; + +/// An iterator that allows peeking at an element before deciding to accept it. +/// +/// See [`.peeking_take_while()`](trait.Itertools.html#method.peeking_take_while) +/// for more information. +/// +/// This is implemented by peeking adaptors like peekable and put back, +/// but also by a few iterators that can be peeked natively, like the slice’s +/// by reference iterator (`std::slice::Iter`). +pub trait PeekingNext : Iterator { + /// Pass a reference to the next iterator element to the closure `accept`; + /// if `accept` returns true, return it as the next element, + /// else None. + fn peeking_next(&mut self, accept: F) -> Option + where F: FnOnce(&Self::Item) -> bool; +} + +impl PeekingNext for Peekable + where I: Iterator, +{ + fn peeking_next(&mut self, accept: F) -> Option + where F: FnOnce(&Self::Item) -> bool + { + if let Some(r) = self.peek() { + if !accept(r) { + return None; + } + } + self.next() + } +} + +impl PeekingNext for PutBack + where I: Iterator, +{ + fn peeking_next(&mut self, accept: F) -> Option + where F: FnOnce(&Self::Item) -> bool + { + if let Some(r) = self.next() { + if !accept(&r) { + self.put_back(r); + return None; + } + Some(r) + } else { + None + } + } +} + +#[cfg(feature = "use_std")] +impl PeekingNext for PutBackN + where I: Iterator, +{ + fn peeking_next(&mut self, accept: F) -> Option + where F: FnOnce(&Self::Item) -> bool + { + if let Some(r) = self.next() { + if !accept(&r) { + self.put_back(r); + return None; + } + Some(r) + } else { + None + } + } +} + +/// An iterator adaptor that takes items while a closure returns `true`. +/// +/// See [`.peeking_take_while()`](../trait.Itertools.html#method.peeking_take_while) +/// for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct PeekingTakeWhile<'a, I: 'a, F> + where I: Iterator, +{ + iter: &'a mut I, + f: F, +} + +/// Create a PeekingTakeWhile +pub fn peeking_take_while(iter: &mut I, f: F) -> PeekingTakeWhile + where I: Iterator, +{ + PeekingTakeWhile { + iter: iter, + f: f, + } +} + +impl<'a, I, F> Iterator for PeekingTakeWhile<'a, I, F> + where I: PeekingNext, + F: FnMut(&I::Item) -> bool, + +{ + type Item = I::Item; + fn next(&mut self) -> Option { + self.iter.peeking_next(&mut self.f) + } + + fn size_hint(&self) -> (usize, Option) { + let (_, hi) = self.iter.size_hint(); + (0, hi) + } +} + +// Some iterators are so lightweight we can simply clone them to save their +// state and use that for peeking. +macro_rules! peeking_next_by_clone { + ([$($typarm:tt)*] $type_:ty) => { + impl<$($typarm)*> PeekingNext for $type_ { + fn peeking_next(&mut self, accept: F) -> Option + where F: FnOnce(&Self::Item) -> bool + { + let saved_state = self.clone(); + if let Some(r) = self.next() { + if !accept(&r) { + *self = saved_state; + } else { + return Some(r) + } + } + None + } + } + } +} + +peeking_next_by_clone! { ['a, T] ::std::slice::Iter<'a, T> } +peeking_next_by_clone! { ['a] ::std::str::Chars<'a> } +peeking_next_by_clone! { ['a] ::std::str::CharIndices<'a> } +peeking_next_by_clone! { ['a] ::std::str::Bytes<'a> } +peeking_next_by_clone! { ['a, T] ::std::option::Iter<'a, T> } +peeking_next_by_clone! { ['a, T] ::std::result::Iter<'a, T> } +peeking_next_by_clone! { [T] ::std::iter::Empty } +#[cfg(feature = "use_std")] +peeking_next_by_clone! { ['a, T] ::std::collections::linked_list::Iter<'a, T> } +#[cfg(feature = "use_std")] +peeking_next_by_clone! { ['a, T] ::std::collections::vec_deque::Iter<'a, T> } + +// cloning a Rev has no extra overhead; peekable and put backs are never DEI. +peeking_next_by_clone! { [I: Clone + PeekingNext + DoubleEndedIterator] + ::std::iter::Rev } diff --git a/third_party/rust/itertools-0.8.0/src/process_results_impl.rs b/third_party/rust/itertools-0.8.0/src/process_results_impl.rs new file mode 100644 index 0000000000000..f78515d77f817 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/process_results_impl.rs @@ -0,0 +1,81 @@ + +/// An iterator that produces only the `T` values as long as the +/// inner iterator produces `Ok(T)`. +/// +/// Used by [`process_results`](../fn.process_results.html), see its docs +/// for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +#[derive(Debug)] +pub struct ProcessResults<'a, I, E: 'a> { + error: &'a mut Result<(), E>, + iter: I, +} + +impl<'a, I, T, E> Iterator for ProcessResults<'a, I, E> + where I: Iterator> +{ + type Item = T; + + fn next(&mut self) -> Option { + match self.iter.next() { + Some(Ok(x)) => Some(x), + Some(Err(e)) => { + *self.error = Err(e); + None + } + None => None, + } + } + + fn size_hint(&self) -> (usize, Option) { + let (_, hi) = self.iter.size_hint(); + (0, hi) + } +} + +/// “Lift” a function of the values of an iterator so that it can process +/// an iterator of `Result` values instead. +/// +/// `iterable` is an iterator or iterable with `Result` elements, where +/// `T` is the value type and `E` the error type. +/// +/// `processor` is a closure that receives an adapted version of the iterable +/// as the only argument — the adapted iterator produces elements of type `T`, +/// as long as the original iterator produces `Ok` values. +/// +/// If the original iterable produces an error at any point, the adapted +/// iterator ends and the `process_results` function will return the +/// error iself. +/// +/// Otherwise, the return value from the closure is returned wrapped +/// inside `Ok`. +/// +/// # Example +/// +/// ``` +/// use itertools::process_results; +/// +/// type R = Result; +/// +/// let first_values: Vec = vec![Ok(1), Ok(0), Ok(3)]; +/// let second_values: Vec = vec![Ok(2), Ok(1), Err("overflow")]; +/// +/// // “Lift” the iterator .max() method to work on the values in Results using process_results +/// +/// let first_max = process_results(first_values, |iter| iter.max().unwrap_or(0)); +/// let second_max = process_results(second_values, |iter| iter.max().unwrap_or(0)); +/// +/// assert_eq!(first_max, Ok(3)); +/// assert!(second_max.is_err()); +/// ``` +pub fn process_results(iterable: I, processor: F) -> Result + where I: IntoIterator>, + F: FnOnce(ProcessResults) -> R +{ + let iter = iterable.into_iter(); + let mut error = Ok(()); + + let result = processor(ProcessResults { error: &mut error, iter: iter }); + + error.map(|_| result) +} diff --git a/third_party/rust/itertools-0.8.0/src/put_back_n_impl.rs b/third_party/rust/itertools-0.8.0/src/put_back_n_impl.rs new file mode 100644 index 0000000000000..cc083207144ed --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/put_back_n_impl.rs @@ -0,0 +1,63 @@ +use size_hint; + +/// An iterator adaptor that allows putting multiple +/// items in front of the iterator. +/// +/// Iterator element type is `I::Item`. +#[derive(Debug, Clone)] +pub struct PutBackN { + top: Vec, + iter: I, +} + +/// Create an iterator where you can put back multiple values to the front +/// of the iteration. +/// +/// Iterator element type is `I::Item`. +pub fn put_back_n(iterable: I) -> PutBackN + where I: IntoIterator +{ + PutBackN { + top: Vec::new(), + iter: iterable.into_iter(), + } +} + +impl PutBackN { + /// Puts x in front of the iterator. + /// The values are yielded in order of the most recently put back + /// values first. + /// + /// ```rust + /// use itertools::put_back_n; + /// + /// let mut it = put_back_n(1..5); + /// it.next(); + /// it.put_back(1); + /// it.put_back(0); + /// + /// assert!(itertools::equal(it, 0..5)); + /// ``` + #[inline] + pub fn put_back(&mut self, x: I::Item) { + self.top.push(x); + } +} + +impl Iterator for PutBackN { + type Item = I::Item; + #[inline] + fn next(&mut self) -> Option { + if self.top.is_empty() { + self.iter.next() + } else { + self.top.pop() + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + size_hint::add_scalar(self.iter.size_hint(), self.top.len()) + } +} + diff --git a/third_party/rust/itertools-0.8.0/src/rciter_impl.rs b/third_party/rust/itertools-0.8.0/src/rciter_impl.rs new file mode 100644 index 0000000000000..1c3b03b5bcd6d --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/rciter_impl.rs @@ -0,0 +1,98 @@ + +use std::iter::IntoIterator; +use std::rc::Rc; +use std::cell::RefCell; + +/// A wrapper for `Rc>`, that implements the `Iterator` trait. +#[derive(Debug)] +pub struct RcIter { + /// The boxed iterator. + pub rciter: Rc>, +} + +/// Return an iterator inside a `Rc>` wrapper. +/// +/// The returned `RcIter` can be cloned, and each clone will refer back to the +/// same original iterator. +/// +/// `RcIter` allows doing interesting things like using `.zip()` on an iterator with +/// itself, at the cost of runtime borrow checking which may have a performance +/// penalty. +/// +/// Iterator element type is `Self::Item`. +/// +/// ``` +/// use itertools::rciter; +/// use itertools::zip; +/// +/// // In this example a range iterator is created and we iterate it using +/// // three separate handles (two of them given to zip). +/// // We also use the IntoIterator implementation for `&RcIter`. +/// +/// let mut iter = rciter(0..9); +/// let mut z = zip(&iter, &iter); +/// +/// assert_eq!(z.next(), Some((0, 1))); +/// assert_eq!(z.next(), Some((2, 3))); +/// assert_eq!(z.next(), Some((4, 5))); +/// assert_eq!(iter.next(), Some(6)); +/// assert_eq!(z.next(), Some((7, 8))); +/// assert_eq!(z.next(), None); +/// ``` +/// +/// **Panics** in iterator methods if a borrow error is encountered in the +/// iterator methods. It can only happen if the `RcIter` is reentered in +/// `.next()`, i.e. if it somehow participates in an “iterator knot” +/// where it is an adaptor of itself. +pub fn rciter(iterable: I) -> RcIter + where I: IntoIterator +{ + RcIter { rciter: Rc::new(RefCell::new(iterable.into_iter())) } +} + +impl Clone for RcIter { + #[inline] + fn clone(&self) -> RcIter { + RcIter { rciter: self.rciter.clone() } + } +} + +impl Iterator for RcIter + where I: Iterator +{ + type Item = A; + #[inline] + fn next(&mut self) -> Option { + self.rciter.borrow_mut().next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + // To work sanely with other API that assume they own an iterator, + // so it can't change in other places, we can't guarantee as much + // in our size_hint. Other clones may drain values under our feet. + let (_, hi) = self.rciter.borrow().size_hint(); + (0, hi) + } +} + +impl DoubleEndedIterator for RcIter + where I: DoubleEndedIterator +{ + #[inline] + fn next_back(&mut self) -> Option { + self.rciter.borrow_mut().next_back() + } +} + +/// Return an iterator from `&RcIter` (by simply cloning it). +impl<'a, I> IntoIterator for &'a RcIter + where I: Iterator +{ + type Item = I::Item; + type IntoIter = RcIter; + + fn into_iter(self) -> RcIter { + self.clone() + } +} diff --git a/third_party/rust/itertools-0.8.0/src/repeatn.rs b/third_party/rust/itertools-0.8.0/src/repeatn.rs new file mode 100644 index 0000000000000..1c7c310014cde --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/repeatn.rs @@ -0,0 +1,54 @@ + +/// An iterator that produces *n* repetitions of an element. +/// +/// See [`repeat_n()`](../fn.repeat_n.html) for more information. +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[derive(Debug)] +pub struct RepeatN { + elt: Option, + n: usize, +} + +/// Create an iterator that produces `n` repetitions of `element`. +pub fn repeat_n(element: A, n: usize) -> RepeatN + where A: Clone, +{ + if n == 0 { + RepeatN { elt: None, n: n, } + } else { + RepeatN { elt: Some(element), n: n, } + } +} + +impl Iterator for RepeatN + where A: Clone +{ + type Item = A; + + fn next(&mut self) -> Option { + if self.n > 1 { + self.n -= 1; + self.elt.as_ref().cloned() + } else { + self.n = 0; + self.elt.take() + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.n, Some(self.n)) + } +} + +impl DoubleEndedIterator for RepeatN + where A: Clone +{ + #[inline] + fn next_back(&mut self) -> Option { + self.next() + } +} + +impl ExactSizeIterator for RepeatN + where A: Clone +{} diff --git a/third_party/rust/itertools-0.8.0/src/size_hint.rs b/third_party/rust/itertools-0.8.0/src/size_hint.rs new file mode 100644 index 0000000000000..be54443f29aa4 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/size_hint.rs @@ -0,0 +1,104 @@ +//! Arithmetic on **Iterator** *.size_hint()* values. +//! + +use std::usize; +use std::cmp; + +/// **SizeHint** is the return type of **Iterator::size_hint()**. +pub type SizeHint = (usize, Option); + +/// Add **SizeHint** correctly. +#[inline] +pub fn add(a: SizeHint, b: SizeHint) -> SizeHint { + let min = a.0.checked_add(b.0).unwrap_or(usize::MAX); + let max = match (a.1, b.1) { + (Some(x), Some(y)) => x.checked_add(y), + _ => None, + }; + + (min, max) +} + +/// Add **x** correctly to a **SizeHint**. +#[inline] +pub fn add_scalar(sh: SizeHint, x: usize) -> SizeHint { + let (mut low, mut hi) = sh; + low = low.saturating_add(x); + hi = hi.and_then(|elt| elt.checked_add(x)); + (low, hi) +} + +/// Sbb **x** correctly to a **SizeHint**. +#[inline] +#[allow(dead_code)] +pub fn sub_scalar(sh: SizeHint, x: usize) -> SizeHint { + let (mut low, mut hi) = sh; + low = low.saturating_sub(x); + hi = hi.map(|elt| elt.saturating_sub(x)); + (low, hi) +} + + +/// Multiply **SizeHint** correctly +/// +/// ```ignore +/// use std::usize; +/// use itertools::size_hint; +/// +/// assert_eq!(size_hint::mul((3, Some(4)), (3, Some(4))), +/// (9, Some(16))); +/// +/// assert_eq!(size_hint::mul((3, Some(4)), (usize::MAX, None)), +/// (usize::MAX, None)); +/// +/// assert_eq!(size_hint::mul((3, None), (0, Some(0))), +/// (0, Some(0))); +/// ``` +#[inline] +pub fn mul(a: SizeHint, b: SizeHint) -> SizeHint { + let low = a.0.checked_mul(b.0).unwrap_or(usize::MAX); + let hi = match (a.1, b.1) { + (Some(x), Some(y)) => x.checked_mul(y), + (Some(0), None) | (None, Some(0)) => Some(0), + _ => None, + }; + (low, hi) +} + +/// Multiply **x** correctly with a **SizeHint**. +#[inline] +pub fn mul_scalar(sh: SizeHint, x: usize) -> SizeHint { + let (mut low, mut hi) = sh; + low = low.saturating_mul(x); + hi = hi.and_then(|elt| elt.checked_mul(x)); + (low, hi) +} + +/// Return the maximum +#[inline] +pub fn max(a: SizeHint, b: SizeHint) -> SizeHint { + let (a_lower, a_upper) = a; + let (b_lower, b_upper) = b; + + let lower = cmp::max(a_lower, b_lower); + + let upper = match (a_upper, b_upper) { + (Some(x), Some(y)) => Some(cmp::max(x, y)), + _ => None, + }; + + (lower, upper) +} + +/// Return the minimum +#[inline] +pub fn min(a: SizeHint, b: SizeHint) -> SizeHint { + let (a_lower, a_upper) = a; + let (b_lower, b_upper) = b; + let lower = cmp::min(a_lower, b_lower); + let upper = match (a_upper, b_upper) { + (Some(u1), Some(u2)) => Some(cmp::min(u1, u2)), + _ => a_upper.or(b_upper), + }; + (lower, upper) +} diff --git a/third_party/rust/itertools-0.8.0/src/sources.rs b/third_party/rust/itertools-0.8.0/src/sources.rs new file mode 100644 index 0000000000000..a579f3d9c2253 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/sources.rs @@ -0,0 +1,190 @@ +//! Iterators that are sources (produce elements from parameters, +//! not from another iterator). +#![allow(deprecated)] + +use std::fmt; +use std::mem; + +/// See [`repeat_call`](../fn.repeat_call.html) for more information. +#[deprecated(note="Use std repeat_with() instead", since="0.8")] +pub struct RepeatCall { + f: F, +} + +impl fmt::Debug for RepeatCall +{ + debug_fmt_fields!(RepeatCall, ); +} + +/// An iterator source that produces elements indefinitely by calling +/// a given closure. +/// +/// Iterator element type is the return type of the closure. +/// +/// ``` +/// use itertools::repeat_call; +/// use itertools::Itertools; +/// use std::collections::BinaryHeap; +/// +/// let mut heap = BinaryHeap::from(vec![2, 5, 3, 7, 8]); +/// +/// // extract each element in sorted order +/// for element in repeat_call(|| heap.pop()).while_some() { +/// print!("{}", element); +/// } +/// +/// itertools::assert_equal( +/// repeat_call(|| 1).take(5), +/// vec![1, 1, 1, 1, 1] +/// ); +/// ``` +#[deprecated(note="Use std repeat_with() instead", since="0.8")] +pub fn repeat_call(function: F) -> RepeatCall + where F: FnMut() -> A +{ + RepeatCall { f: function } +} + +impl Iterator for RepeatCall + where F: FnMut() -> A +{ + type Item = A; + + #[inline] + fn next(&mut self) -> Option { + Some((self.f)()) + } + + fn size_hint(&self) -> (usize, Option) { + (usize::max_value(), None) + } +} + +/// Creates a new unfold source with the specified closure as the "iterator +/// function" and an initial state to eventually pass to the closure +/// +/// `unfold` is a general iterator builder: it has a mutable state value, +/// and a closure with access to the state that produces the next value. +/// +/// This more or less equivalent to a regular struct with an `Iterator` +/// implementation, and is useful for one-off iterators. +/// +/// ``` +/// // an iterator that yields sequential Fibonacci numbers, +/// // and stops at the maximum representable value. +/// +/// use itertools::unfold; +/// +/// let (mut x1, mut x2) = (1u32, 1u32); +/// let mut fibonacci = unfold((), move |_| { +/// // Attempt to get the next Fibonacci number +/// let next = x1.saturating_add(x2); +/// +/// // Shift left: ret <- x1 <- x2 <- next +/// let ret = x1; +/// x1 = x2; +/// x2 = next; +/// +/// // If addition has saturated at the maximum, we are finished +/// if ret == x1 && ret > 1 { +/// return None; +/// } +/// +/// Some(ret) +/// }); +/// +/// itertools::assert_equal(fibonacci.by_ref().take(8), +/// vec![1, 1, 2, 3, 5, 8, 13, 21]); +/// assert_eq!(fibonacci.last(), Some(2_971_215_073)) +/// ``` +pub fn unfold(initial_state: St, f: F) -> Unfold + where F: FnMut(&mut St) -> Option +{ + Unfold { + f: f, + state: initial_state, + } +} + +impl fmt::Debug for Unfold + where St: fmt::Debug, +{ + debug_fmt_fields!(Unfold, state); +} + +/// See [`unfold`](../fn.unfold.html) for more information. +#[derive(Clone)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +pub struct Unfold { + f: F, + /// Internal state that will be passed to the closure on the next iteration + pub state: St, +} + +impl Iterator for Unfold + where F: FnMut(&mut St) -> Option +{ + type Item = A; + + #[inline] + fn next(&mut self) -> Option { + (self.f)(&mut self.state) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + // no possible known bounds at this point + (0, None) + } +} + +/// An iterator that infinitely applies function to value and yields results. +/// +/// This `struct` is created by the [`iterate()`] function. See its documentation for more. +/// +/// [`iterate()`]: ../fn.iterate.html +#[derive(Clone)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +pub struct Iterate { + state: St, + f: F, +} + +impl fmt::Debug for Iterate + where St: fmt::Debug, +{ + debug_fmt_fields!(Iterate, state); +} + +impl Iterator for Iterate + where F: FnMut(&St) -> St +{ + type Item = St; + + #[inline] + fn next(&mut self) -> Option { + let next_state = (self.f)(&self.state); + Some(mem::replace(&mut self.state, next_state)) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (usize::max_value(), None) + } +} + +/// Creates a new iterator that infinitely applies function to value and yields results. +/// +/// ``` +/// use itertools::iterate; +/// +/// itertools::assert_equal(iterate(1, |&i| i * 3).take(5), vec![1, 3, 9, 27, 81]); +/// ``` +pub fn iterate(initial_value: St, f: F) -> Iterate + where F: FnMut(&St) -> St +{ + Iterate { + state: initial_value, + f: f, + } +} diff --git a/third_party/rust/itertools-0.8.0/src/tee.rs b/third_party/rust/itertools-0.8.0/src/tee.rs new file mode 100644 index 0000000000000..77d261759d1ef --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/tee.rs @@ -0,0 +1,78 @@ +use super::size_hint; + +use std::cell::RefCell; +use std::collections::VecDeque; +use std::rc::Rc; + +/// Common buffer object for the two tee halves +#[derive(Debug)] +struct TeeBuffer { + backlog: VecDeque, + iter: I, + /// The owner field indicates which id should read from the backlog + owner: bool, +} + +/// One half of an iterator pair where both return the same elements. +/// +/// See [`.tee()`](../trait.Itertools.html#method.tee) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +#[derive(Debug)] +pub struct Tee + where I: Iterator +{ + rcbuffer: Rc>>, + id: bool, +} + +pub fn new(iter: I) -> (Tee, Tee) + where I: Iterator +{ + let buffer = TeeBuffer{backlog: VecDeque::new(), iter: iter, owner: false}; + let t1 = Tee{rcbuffer: Rc::new(RefCell::new(buffer)), id: true}; + let t2 = Tee{rcbuffer: t1.rcbuffer.clone(), id: false}; + (t1, t2) +} + +impl Iterator for Tee + where I: Iterator, + I::Item: Clone +{ + type Item = I::Item; + fn next(&mut self) -> Option { + // .borrow_mut may fail here -- but only if the user has tied some kind of weird + // knot where the iterator refers back to itself. + let mut buffer = self.rcbuffer.borrow_mut(); + if buffer.owner == self.id { + match buffer.backlog.pop_front() { + None => {} + some_elt => return some_elt, + } + } + match buffer.iter.next() { + None => None, + Some(elt) => { + buffer.backlog.push_back(elt.clone()); + buffer.owner = !self.id; + Some(elt) + } + } + } + + fn size_hint(&self) -> (usize, Option) { + let buffer = self.rcbuffer.borrow(); + let sh = buffer.iter.size_hint(); + + if buffer.owner == self.id { + let log_len = buffer.backlog.len(); + size_hint::add_scalar(sh, log_len) + } else { + sh + } + } +} + +impl ExactSizeIterator for Tee + where I: ExactSizeIterator, + I::Item: Clone +{} diff --git a/third_party/rust/itertools-0.8.0/src/tuple_impl.rs b/third_party/rust/itertools-0.8.0/src/tuple_impl.rs new file mode 100644 index 0000000000000..0daa7800c1472 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/tuple_impl.rs @@ -0,0 +1,266 @@ +//! Some iterator that produces tuples + +use std::iter::Fuse; + +/// An iterator over a incomplete tuple. +/// +/// See [`.tuples()`](../trait.Itertools.html#method.tuples) and +/// [`Tuples::into_buffer()`](struct.Tuples.html#method.into_buffer). +#[derive(Debug)] +pub struct TupleBuffer + where T: TupleCollect +{ + cur: usize, + buf: T::Buffer, +} + +impl TupleBuffer + where T: TupleCollect +{ + fn new(buf: T::Buffer) -> Self { + TupleBuffer { + cur: 0, + buf: buf, + } + } +} + +impl Iterator for TupleBuffer + where T: TupleCollect +{ + type Item = T::Item; + + fn next(&mut self) -> Option { + let s = self.buf.as_mut(); + if let Some(ref mut item) = s.get_mut(self.cur) { + self.cur += 1; + item.take() + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + let buffer = &self.buf.as_ref()[self.cur..]; + let len = if buffer.len() == 0 { + 0 + } else { + buffer.iter() + .position(|x| x.is_none()) + .unwrap_or(buffer.len()) + }; + (len, Some(len)) + } +} + +impl ExactSizeIterator for TupleBuffer + where T: TupleCollect +{ +} + +/// An iterator that groups the items in tuples of a specific size. +/// +/// See [`.tuples()`](../trait.Itertools.html#method.tuples) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Tuples + where I: Iterator, + T: TupleCollect +{ + iter: Fuse, + buf: T::Buffer, +} + +/// Create a new tuples iterator. +pub fn tuples(iter: I) -> Tuples + where I: Iterator, + T: TupleCollect +{ + Tuples { + iter: iter.fuse(), + buf: Default::default(), + } +} + +impl Iterator for Tuples + where I: Iterator, + T: TupleCollect +{ + type Item = T; + + fn next(&mut self) -> Option { + T::collect_from_iter(&mut self.iter, &mut self.buf) + } +} + +impl Tuples + where I: Iterator, + T: TupleCollect +{ + /// Return a buffer with the produced items that was not enough to be grouped in a tuple. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let mut iter = (0..5).tuples(); + /// assert_eq!(Some((0, 1, 2)), iter.next()); + /// assert_eq!(None, iter.next()); + /// itertools::assert_equal(vec![3, 4], iter.into_buffer()); + /// ``` + pub fn into_buffer(self) -> TupleBuffer { + TupleBuffer::new(self.buf) + } +} + + +/// An iterator over all contiguous windows that produces tuples of a specific size. +/// +/// See [`.tuple_windows()`](../trait.Itertools.html#method.tuple_windows) for more +/// information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +#[derive(Debug)] +pub struct TupleWindows + where I: Iterator, + T: TupleCollect +{ + iter: I, + last: Option, +} + +/// Create a new tuple windows iterator. +pub fn tuple_windows(mut iter: I) -> TupleWindows + where I: Iterator, + T: TupleCollect, + T::Item: Clone +{ + use std::iter::once; + + let mut last = None; + if T::num_items() != 1 { + // put in a duplicate item in front of the tuple; this simplifies + // .next() function. + if let Some(item) = iter.next() { + let iter = once(item.clone()).chain(once(item)).chain(&mut iter); + last = T::collect_from_iter_no_buf(iter); + } + } + + TupleWindows { + last: last, + iter: iter, + } +} + +impl Iterator for TupleWindows + where I: Iterator, + T: TupleCollect + Clone, + T::Item: Clone +{ + type Item = T; + + fn next(&mut self) -> Option { + if T::num_items() == 1 { + return T::collect_from_iter_no_buf(&mut self.iter) + } + if let Some(ref mut last) = self.last { + if let Some(new) = self.iter.next() { + last.left_shift_push(new); + return Some(last.clone()); + } + } + None + } +} + +pub trait TupleCollect: Sized { + type Item; + type Buffer: Default + AsRef<[Option]> + AsMut<[Option]>; + + fn collect_from_iter(iter: I, buf: &mut Self::Buffer) -> Option + where I: IntoIterator; + + fn collect_from_iter_no_buf(iter: I) -> Option + where I: IntoIterator; + + fn num_items() -> usize; + + fn left_shift_push(&mut self, item: Self::Item); +} + +macro_rules! impl_tuple_collect { + () => (); + ($N:expr; $A:ident ; $($X:ident),* ; $($Y:ident),* ; $($Y_rev:ident),*) => ( + impl<$A> TupleCollect for ($($X),*,) { + type Item = $A; + type Buffer = [Option<$A>; $N - 1]; + + #[allow(unused_assignments, unused_mut)] + fn collect_from_iter(iter: I, buf: &mut Self::Buffer) -> Option + where I: IntoIterator + { + let mut iter = iter.into_iter(); + $( + let mut $Y = None; + )* + + loop { + $( + $Y = iter.next(); + if $Y.is_none() { + break + } + )* + return Some(($($Y.unwrap()),*,)) + } + + let mut i = 0; + let mut s = buf.as_mut(); + $( + if i < s.len() { + s[i] = $Y; + i += 1; + } + )* + return None; + } + + #[allow(unused_assignments)] + fn collect_from_iter_no_buf(iter: I) -> Option + where I: IntoIterator + { + let mut iter = iter.into_iter(); + loop { + $( + let $Y = if let Some($Y) = iter.next() { + $Y + } else { + break; + }; + )* + return Some(($($Y),*,)) + } + + return None; + } + + fn num_items() -> usize { + $N + } + + fn left_shift_push(&mut self, item: $A) { + use std::mem::replace; + + let &mut ($(ref mut $Y),*,) = self; + let tmp = item; + $( + let tmp = replace($Y_rev, tmp); + )* + drop(tmp); + } + } + ) +} + +impl_tuple_collect!(1; A; A; a; a); +impl_tuple_collect!(2; A; A, A; a, b; b, a); +impl_tuple_collect!(3; A; A, A, A; a, b, c; c, b, a); +impl_tuple_collect!(4; A; A, A, A, A; a, b, c, d; d, c, b, a); diff --git a/third_party/rust/itertools-0.8.0/src/unique_impl.rs b/third_party/rust/itertools-0.8.0/src/unique_impl.rs new file mode 100644 index 0000000000000..d9e7fd3dc8ff6 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/unique_impl.rs @@ -0,0 +1,134 @@ + +use std::collections::HashMap; +use std::collections::hash_map::{Entry}; +use std::hash::Hash; +use std::fmt; + +/// An iterator adapter to filter out duplicate elements. +/// +/// See [`.unique_by()`](../trait.Itertools.html#method.unique) for more information. +#[derive(Clone)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct UniqueBy { + iter: I, + // Use a hashmap for the entry API + used: HashMap, + f: F, +} + +impl fmt::Debug for UniqueBy + where I: Iterator + fmt::Debug, + V: fmt::Debug + Hash + Eq, +{ + debug_fmt_fields!(UniqueBy, iter, used); +} + +/// Create a new `UniqueBy` iterator. +pub fn unique_by(iter: I, f: F) -> UniqueBy + where V: Eq + Hash, + F: FnMut(&I::Item) -> V, + I: Iterator, +{ + UniqueBy { + iter: iter, + used: HashMap::new(), + f: f, + } +} + +// count the number of new unique keys in iterable (`used` is the set already seen) +fn count_new_keys(mut used: HashMap, iterable: I) -> usize + where I: IntoIterator, + K: Hash + Eq, +{ + let iter = iterable.into_iter(); + let current_used = used.len(); + used.extend(iter.map(|key| (key, ()))); + used.len() - current_used +} + +impl Iterator for UniqueBy + where I: Iterator, + V: Eq + Hash, + F: FnMut(&I::Item) -> V +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + while let Some(v) = self.iter.next() { + let key = (self.f)(&v); + if self.used.insert(key, ()).is_none() { + return Some(v); + } + } + None + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (low, hi) = self.iter.size_hint(); + ((low > 0 && self.used.is_empty()) as usize, hi) + } + + fn count(self) -> usize { + let mut key_f = self.f; + count_new_keys(self.used, self.iter.map(move |elt| key_f(&elt))) + } +} + +impl Iterator for Unique + where I: Iterator, + I::Item: Eq + Hash + Clone +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + while let Some(v) = self.iter.iter.next() { + if let Entry::Vacant(entry) = self.iter.used.entry(v) { + let elt = entry.key().clone(); + entry.insert(()); + return Some(elt); + } + } + None + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (low, hi) = self.iter.iter.size_hint(); + ((low > 0 && self.iter.used.is_empty()) as usize, hi) + } + + fn count(self) -> usize { + count_new_keys(self.iter.used, self.iter.iter) + } +} + +/// An iterator adapter to filter out duplicate elements. +/// +/// See [`.unique()`](../trait.Itertools.html#method.unique) for more information. +#[derive(Clone)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Unique { + iter: UniqueBy, +} + +impl fmt::Debug for Unique + where I: Iterator + fmt::Debug, + I::Item: Hash + Eq + fmt::Debug, +{ + debug_fmt_fields!(Unique, iter); +} + +pub fn unique(iter: I) -> Unique + where I: Iterator, + I::Item: Eq + Hash, +{ + Unique { + iter: UniqueBy { + iter: iter, + used: HashMap::new(), + f: (), + } + } +} diff --git a/third_party/rust/itertools-0.8.0/src/with_position.rs b/third_party/rust/itertools-0.8.0/src/with_position.rs new file mode 100644 index 0000000000000..2a7c2b8ad6a20 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/with_position.rs @@ -0,0 +1,90 @@ +use std::iter::{Fuse,Peekable}; + +/// An iterator adaptor that wraps each element in an [`Position`](../enum.Position.html). +/// +/// Iterator element type is `Position`. +/// +/// See [`.with_position()`](../trait.Itertools.html#method.with_position) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct WithPosition + where I: Iterator, +{ + handled_first: bool, + peekable: Peekable>, +} + +/// Create a new `WithPosition` iterator. +pub fn with_position(iter: I) -> WithPosition + where I: Iterator, +{ + WithPosition { + handled_first: false, + peekable: iter.fuse().peekable(), + } +} + +/// A value yielded by `WithPosition`. +/// Indicates the position of this element in the iterator results. +/// +/// See [`.with_position()`](trait.Itertools.html#method.with_position) for more information. +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Position { + /// This is the first element. + First(T), + /// This is neither the first nor the last element. + Middle(T), + /// This is the last element. + Last(T), + /// This is the only element. + Only(T), +} + +impl Position { + /// Return the inner value. + pub fn into_inner(self) -> T { + match self { + Position::First(x) | + Position::Middle(x) | + Position::Last(x) | + Position::Only(x) => x, + } + } +} + +impl Iterator for WithPosition { + type Item = Position; + + fn next(&mut self) -> Option { + match self.peekable.next() { + Some(item) => { + if !self.handled_first { + // Haven't seen the first item yet, and there is one to give. + self.handled_first = true; + // Peek to see if this is also the last item, + // in which case tag it as `Only`. + match self.peekable.peek() { + Some(_) => Some(Position::First(item)), + None => Some(Position::Only(item)), + } + } else { + // Have seen the first item, and there's something left. + // Peek to see if this is the last item. + match self.peekable.peek() { + Some(_) => Some(Position::Middle(item)), + None => Some(Position::Last(item)), + } + } + } + // Iterator is finished. + None => None, + } + } + + fn size_hint(&self) -> (usize, Option) { + self.peekable.size_hint() + } +} + +impl ExactSizeIterator for WithPosition + where I: ExactSizeIterator, +{ } diff --git a/third_party/rust/itertools-0.8.0/src/zip_eq_impl.rs b/third_party/rust/itertools-0.8.0/src/zip_eq_impl.rs new file mode 100644 index 0000000000000..857465da419eb --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/zip_eq_impl.rs @@ -0,0 +1,60 @@ +use super::size_hint; + +/// An iterator which iterates two other iterators simultaneously +/// +/// See [`.zip_eq()`](../trait.Itertools.html#method.zip_eq) for more information. +#[derive(Clone, Debug)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct ZipEq { + a: I, + b: J, +} + +/// Iterate `i` and `j` in lock step. +/// +/// **Panics** if the iterators are not of the same length. +/// +/// `IntoIterator` enabled version of `i.zip_eq(j)`. +/// +/// ``` +/// use itertools::zip_eq; +/// +/// let data = [1, 2, 3, 4, 5]; +/// for (a, b) in zip_eq(&data[..data.len() - 1], &data[1..]) { +/// /* loop body */ +/// } +/// ``` +pub fn zip_eq(i: I, j: J) -> ZipEq + where I: IntoIterator, + J: IntoIterator +{ + ZipEq { + a: i.into_iter(), + b: j.into_iter(), + } +} + +impl Iterator for ZipEq + where I: Iterator, + J: Iterator +{ + type Item = (I::Item, J::Item); + + fn next(&mut self) -> Option { + match (self.a.next(), self.b.next()) { + (None, None) => None, + (Some(a), Some(b)) => Some((a, b)), + (None, Some(_)) | (Some(_), None) => + panic!("itertools: .zip_eq() reached end of one iterator before the other") + } + } + + fn size_hint(&self) -> (usize, Option) { + size_hint::min(self.a.size_hint(), self.b.size_hint()) + } +} + +impl ExactSizeIterator for ZipEq + where I: ExactSizeIterator, + J: ExactSizeIterator +{} diff --git a/third_party/rust/itertools-0.8.0/src/zip_longest.rs b/third_party/rust/itertools-0.8.0/src/zip_longest.rs new file mode 100644 index 0000000000000..68a381acee3f6 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/zip_longest.rs @@ -0,0 +1,78 @@ +use std::cmp::Ordering::{Equal, Greater, Less}; +use super::size_hint; +use std::iter::Fuse; + +use either_or_both::EitherOrBoth; + +// ZipLongest originally written by SimonSapin, +// and dedicated to itertools https://github.com/rust-lang/rust/pull/19283 + +/// An iterator which iterates two other iterators simultaneously +/// +/// This iterator is *fused*. +/// +/// See [`.zip_longest()`](../trait.Itertools.html#method.zip_longest) for more information. +#[derive(Clone, Debug)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct ZipLongest { + a: Fuse, + b: Fuse, +} + +/// Create a new `ZipLongest` iterator. +pub fn zip_longest(a: T, b: U) -> ZipLongest + where T: Iterator, + U: Iterator +{ + ZipLongest { + a: a.fuse(), + b: b.fuse(), + } +} + +impl Iterator for ZipLongest + where T: Iterator, + U: Iterator +{ + type Item = EitherOrBoth; + + #[inline] + fn next(&mut self) -> Option { + match (self.a.next(), self.b.next()) { + (None, None) => None, + (Some(a), None) => Some(EitherOrBoth::Left(a)), + (None, Some(b)) => Some(EitherOrBoth::Right(b)), + (Some(a), Some(b)) => Some(EitherOrBoth::Both(a, b)), + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + size_hint::max(self.a.size_hint(), self.b.size_hint()) + } +} + +impl DoubleEndedIterator for ZipLongest + where T: DoubleEndedIterator + ExactSizeIterator, + U: DoubleEndedIterator + ExactSizeIterator +{ + #[inline] + fn next_back(&mut self) -> Option { + match self.a.len().cmp(&self.b.len()) { + Equal => match (self.a.next_back(), self.b.next_back()) { + (None, None) => None, + (Some(a), Some(b)) => Some(EitherOrBoth::Both(a, b)), + // These can only happen if .len() is inconsistent with .next_back() + (Some(a), None) => Some(EitherOrBoth::Left(a)), + (None, Some(b)) => Some(EitherOrBoth::Right(b)), + }, + Greater => self.a.next_back().map(EitherOrBoth::Left), + Less => self.b.next_back().map(EitherOrBoth::Right), + } + } +} + +impl ExactSizeIterator for ZipLongest + where T: ExactSizeIterator, + U: ExactSizeIterator +{} diff --git a/third_party/rust/itertools-0.8.0/src/ziptuple.rs b/third_party/rust/itertools-0.8.0/src/ziptuple.rs new file mode 100644 index 0000000000000..2dc3ea5e0be56 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/src/ziptuple.rs @@ -0,0 +1,111 @@ +use super::size_hint; + +/// See [`multizip`](../fn.multizip.html) for more information. +#[derive(Clone, Debug)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Zip { + t: T, +} + +/// An iterator that generalizes *.zip()* and allows running multiple iterators in lockstep. +/// +/// The iterator `Zip<(I, J, ..., M)>` is formed from a tuple of iterators (or values that +/// implement `IntoIterator`) and yields elements +/// until any of the subiterators yields `None`. +/// +/// The iterator element type is a tuple like like `(A, B, ..., E)` where `A` to `E` are the +/// element types of the subiterator. +/// +/// **Note:** The result of this macro is a value of a named type (`Zip<(I, J, +/// ..)>` of each component iterator `I, J, ...`) if each component iterator is +/// nameable. +/// +/// Prefer [`izip!()`] over `multizip` for the performance benefits of using the +/// standard library `.zip()`. Prefer `multizip` if a nameable type is needed. +/// +/// [`izip!()`]: macro.izip.html +/// +/// ``` +/// use itertools::multizip; +/// +/// // iterate over three sequences side-by-side +/// let mut results = [0, 0, 0, 0]; +/// let inputs = [3, 7, 9, 6]; +/// +/// for (r, index, input) in multizip((&mut results, 0..10, &inputs)) { +/// *r = index * 10 + input; +/// } +/// +/// assert_eq!(results, [0 + 3, 10 + 7, 29, 36]); +/// ``` +pub fn multizip(t: U) -> Zip + where Zip: From, + Zip: Iterator, +{ + Zip::from(t) +} + +macro_rules! impl_zip_iter { + ($($B:ident),*) => ( + #[allow(non_snake_case)] + impl<$($B: IntoIterator),*> From<($($B,)*)> for Zip<($($B::IntoIter,)*)> { + fn from(t: ($($B,)*)) -> Self { + let ($($B,)*) = t; + Zip { t: ($($B.into_iter(),)*) } + } + } + + #[allow(non_snake_case)] + #[allow(unused_assignments)] + impl<$($B),*> Iterator for Zip<($($B,)*)> + where + $( + $B: Iterator, + )* + { + type Item = ($($B::Item,)*); + + fn next(&mut self) -> Option + { + let ($(ref mut $B,)*) = self.t; + + // NOTE: Just like iter::Zip, we check the iterators + // for None in order. We may finish unevenly (some + // iterators gave n + 1 elements, some only n). + $( + let $B = match $B.next() { + None => return None, + Some(elt) => elt + }; + )* + Some(($($B,)*)) + } + + fn size_hint(&self) -> (usize, Option) + { + let sh = (::std::usize::MAX, None); + let ($(ref $B,)*) = self.t; + $( + let sh = size_hint::min($B.size_hint(), sh); + )* + sh + } + } + + #[allow(non_snake_case)] + impl<$($B),*> ExactSizeIterator for Zip<($($B,)*)> where + $( + $B: ExactSizeIterator, + )* + { } + ); +} + +impl_zip_iter!(A); +impl_zip_iter!(A, B); +impl_zip_iter!(A, B, C); +impl_zip_iter!(A, B, C, D); +impl_zip_iter!(A, B, C, D, E); +impl_zip_iter!(A, B, C, D, E, F); +impl_zip_iter!(A, B, C, D, E, F, G); +impl_zip_iter!(A, B, C, D, E, F, G, H); diff --git a/third_party/rust/itertools-0.8.0/tests/merge_join.rs b/third_party/rust/itertools-0.8.0/tests/merge_join.rs new file mode 100644 index 0000000000000..41829202b8d29 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/tests/merge_join.rs @@ -0,0 +1,110 @@ +extern crate itertools; + +use itertools::EitherOrBoth; +use itertools::free::merge_join_by; + +#[test] +fn empty() { + let left: Vec = vec![]; + let right: Vec = vec![]; + let expected_result: Vec> = vec![]; + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)) + .collect::>(); + assert_eq!(expected_result, actual_result); +} + +#[test] +fn left_only() { + let left: Vec = vec![1,2,3]; + let right: Vec = vec![]; + let expected_result: Vec> = vec![ + EitherOrBoth::Left(1), + EitherOrBoth::Left(2), + EitherOrBoth::Left(3) + ]; + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)) + .collect::>(); + assert_eq!(expected_result, actual_result); +} + +#[test] +fn right_only() { + let left: Vec = vec![]; + let right: Vec = vec![1,2,3]; + let expected_result: Vec> = vec![ + EitherOrBoth::Right(1), + EitherOrBoth::Right(2), + EitherOrBoth::Right(3) + ]; + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)) + .collect::>(); + assert_eq!(expected_result, actual_result); +} + +#[test] +fn first_left_then_right() { + let left: Vec = vec![1,2,3]; + let right: Vec = vec![4,5,6]; + let expected_result: Vec> = vec![ + EitherOrBoth::Left(1), + EitherOrBoth::Left(2), + EitherOrBoth::Left(3), + EitherOrBoth::Right(4), + EitherOrBoth::Right(5), + EitherOrBoth::Right(6) + ]; + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)) + .collect::>(); + assert_eq!(expected_result, actual_result); +} + +#[test] +fn first_right_then_left() { + let left: Vec = vec![4,5,6]; + let right: Vec = vec![1,2,3]; + let expected_result: Vec> = vec![ + EitherOrBoth::Right(1), + EitherOrBoth::Right(2), + EitherOrBoth::Right(3), + EitherOrBoth::Left(4), + EitherOrBoth::Left(5), + EitherOrBoth::Left(6) + ]; + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)) + .collect::>(); + assert_eq!(expected_result, actual_result); +} + +#[test] +fn interspersed_left_and_right() { + let left: Vec = vec![1,3,5]; + let right: Vec = vec![2,4,6]; + let expected_result: Vec> = vec![ + EitherOrBoth::Left(1), + EitherOrBoth::Right(2), + EitherOrBoth::Left(3), + EitherOrBoth::Right(4), + EitherOrBoth::Left(5), + EitherOrBoth::Right(6) + ]; + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)) + .collect::>(); + assert_eq!(expected_result, actual_result); +} + +#[test] +fn overlapping_left_and_right() { + let left: Vec = vec![1,3,4,6]; + let right: Vec = vec![2,3,4,5]; + let expected_result: Vec> = vec![ + EitherOrBoth::Left(1), + EitherOrBoth::Right(2), + EitherOrBoth::Both(3, 3), + EitherOrBoth::Both(4, 4), + EitherOrBoth::Right(5), + EitherOrBoth::Left(6) + ]; + let actual_result = merge_join_by(left, right, |l, r| l.cmp(r)) + .collect::>(); + assert_eq!(expected_result, actual_result); +} diff --git a/third_party/rust/itertools-0.8.0/tests/peeking_take_while.rs b/third_party/rust/itertools-0.8.0/tests/peeking_take_while.rs new file mode 100644 index 0000000000000..45c76c2d5c9b4 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/tests/peeking_take_while.rs @@ -0,0 +1,53 @@ + +extern crate itertools; + +use itertools::Itertools; +use itertools::{put_back, put_back_n}; + +#[test] +fn peeking_take_while_peekable() { + let mut r = (0..10).peekable(); + r.peeking_take_while(|x| *x <= 3).count(); + assert_eq!(r.next(), Some(4)); +} + +#[test] +fn peeking_take_while_put_back() { + let mut r = put_back(0..10); + r.peeking_take_while(|x| *x <= 3).count(); + assert_eq!(r.next(), Some(4)); + r.peeking_take_while(|_| true).count(); + assert_eq!(r.next(), None); +} + +#[test] +fn peeking_take_while_put_back_n() { + let mut r = put_back_n(6..10); + for elt in (0..6).rev() { + r.put_back(elt); + } + r.peeking_take_while(|x| *x <= 3).count(); + assert_eq!(r.next(), Some(4)); + r.peeking_take_while(|_| true).count(); + assert_eq!(r.next(), None); +} + +#[test] +fn peeking_take_while_slice_iter() { + let v = [1, 2, 3, 4, 5, 6]; + let mut r = v.iter(); + r.peeking_take_while(|x| **x <= 3).count(); + assert_eq!(r.next(), Some(&4)); + r.peeking_take_while(|_| true).count(); + assert_eq!(r.next(), None); +} + +#[test] +fn peeking_take_while_slice_iter_rev() { + let v = [1, 2, 3, 4, 5, 6]; + let mut r = v.iter().rev(); + r.peeking_take_while(|x| **x >= 3).count(); + assert_eq!(r.next(), Some(&2)); + r.peeking_take_while(|_| true).count(); + assert_eq!(r.next(), None); +} diff --git a/third_party/rust/itertools-0.8.0/tests/quick.rs b/third_party/rust/itertools-0.8.0/tests/quick.rs new file mode 100644 index 0000000000000..e0c61b47deb6b --- /dev/null +++ b/third_party/rust/itertools-0.8.0/tests/quick.rs @@ -0,0 +1,1017 @@ +//! The purpose of these tests is to cover corner cases of iterators +//! and adaptors. +//! +//! In particular we test the tedious size_hint and exact size correctness. + +#[macro_use] extern crate itertools; + +extern crate quickcheck; +extern crate rand; + +use std::default::Default; + +use quickcheck as qc; +use std::ops::Range; +use std::cmp::Ordering; +use itertools::Itertools; +use itertools::{ + multizip, + EitherOrBoth, +}; +use itertools::free::{ + cloned, + enumerate, + multipeek, + put_back, + put_back_n, + rciter, + zip, + zip_eq, +}; + +use rand::Rng; +use rand::seq::SliceRandom; +use quickcheck::TestResult; + +/// Trait for size hint modifier types +trait HintKind: Copy + Send + qc::Arbitrary { + fn loosen_bounds(&self, org_hint: (usize, Option)) -> (usize, Option); +} + +/// Exact size hint variant that leaves hints unchanged +#[derive(Clone, Copy, Debug)] +struct Exact {} + +impl HintKind for Exact { + fn loosen_bounds(&self, org_hint: (usize, Option)) -> (usize, Option) { + org_hint + } +} + +impl qc::Arbitrary for Exact { + fn arbitrary(_: &mut G) -> Self { + Exact {} + } +} + +/// Inexact size hint variant to simulate imprecise (but valid) size hints +/// +/// Will always decrease the lower bound and increase the upper bound +/// of the size hint by set amounts. +#[derive(Clone, Copy, Debug)] +struct Inexact { + underestimate: usize, + overestimate: usize, +} + +impl HintKind for Inexact { + fn loosen_bounds(&self, org_hint: (usize, Option)) -> (usize, Option) { + let (org_lower, org_upper) = org_hint; + (org_lower.saturating_sub(self.underestimate), + org_upper.and_then(move |x| x.checked_add(self.overestimate))) + } +} + +impl qc::Arbitrary for Inexact { + fn arbitrary(g: &mut G) -> Self { + let ue_value = usize::arbitrary(g); + let oe_value = usize::arbitrary(g); + // Compensate for quickcheck using extreme values too rarely + let ue_choices = &[0, ue_value, usize::max_value()]; + let oe_choices = &[0, oe_value, usize::max_value()]; + Inexact { + underestimate: *ue_choices.choose(g).unwrap(), + overestimate: *oe_choices.choose(g).unwrap(), + } + } + + fn shrink(&self) -> Box> { + let underestimate_value = self.underestimate; + let overestimate_value = self.overestimate; + Box::new( + underestimate_value.shrink().flat_map(move |ue_value| + overestimate_value.shrink().map(move |oe_value| + Inexact { + underestimate: ue_value, + overestimate: oe_value, + } + ) + ) + ) + } +} + +/// Our base iterator that we can impl Arbitrary for +/// +/// By default we'll return inexact bounds estimates for size_hint +/// to make tests harder to pass. +/// +/// NOTE: Iter is tricky and is not fused, to help catch bugs. +/// At the end it will return None once, then return Some(0), +/// then return None again. +#[derive(Clone, Debug)] +struct Iter { + iterator: Range, + // fuse/done flag + fuse_flag: i32, + hint_kind: SK, +} + +impl Iter where HK: HintKind +{ + fn new(it: Range, hint_kind: HK) -> Self { + Iter { + iterator: it, + fuse_flag: 0, + hint_kind: hint_kind + } + } +} + +impl Iterator for Iter + where Range: Iterator, + as Iterator>::Item: Default, + HK: HintKind, +{ + type Item = as Iterator>::Item; + + fn next(&mut self) -> Option + { + let elt = self.iterator.next(); + if elt.is_none() { + self.fuse_flag += 1; + // check fuse flag + if self.fuse_flag == 2 { + return Some(Default::default()) + } + } + elt + } + + fn size_hint(&self) -> (usize, Option) + { + let org_hint = self.iterator.size_hint(); + self.hint_kind.loosen_bounds(org_hint) + } +} + +impl DoubleEndedIterator for Iter + where Range: DoubleEndedIterator, + as Iterator>::Item: Default, + HK: HintKind +{ + fn next_back(&mut self) -> Option { self.iterator.next_back() } +} + +impl ExactSizeIterator for Iter where Range: ExactSizeIterator, + as Iterator>::Item: Default, +{ } + +impl qc::Arbitrary for Iter + where T: qc::Arbitrary, + HK: HintKind, +{ + fn arbitrary(g: &mut G) -> Self + { + Iter::new(T::arbitrary(g)..T::arbitrary(g), HK::arbitrary(g)) + } + + fn shrink(&self) -> Box>> + { + let r = self.iterator.clone(); + let hint_kind = self.hint_kind; + Box::new( + r.start.shrink().flat_map(move |a| + r.end.shrink().map(move |b| + Iter::new(a.clone()..b, hint_kind) + ) + ) + ) + } +} + +/// A meta-iterator which yields `Iter`s whose start/endpoints are +/// increased or decreased linearly on each iteration. +#[derive(Clone, Debug)] +struct ShiftRange { + range_start: i32, + range_end: i32, + start_step: i32, + end_step: i32, + iter_count: u32, + hint_kind: HK, +} + +impl Iterator for ShiftRange where HK: HintKind { + type Item = Iter; + + fn next(&mut self) -> Option { + if self.iter_count == 0 { + return None; + } + + let iter = Iter::new(self.range_start..self.range_end, self.hint_kind); + + self.range_start += self.start_step; + self.range_end += self.end_step; + self.iter_count -= 1; + + Some(iter) + } +} + +impl ExactSizeIterator for ShiftRange { } + +impl qc::Arbitrary for ShiftRange + where HK: HintKind +{ + fn arbitrary(g: &mut G) -> Self { + const MAX_STARTING_RANGE_DIFF: i32 = 32; + const MAX_STEP_MODULO: i32 = 8; + const MAX_ITER_COUNT: u32 = 3; + + let range_start = qc::Arbitrary::arbitrary(g); + let range_end = range_start + g.gen_range(0, MAX_STARTING_RANGE_DIFF + 1); + let start_step = g.gen_range(-MAX_STEP_MODULO, MAX_STEP_MODULO + 1); + let end_step = g.gen_range(-MAX_STEP_MODULO, MAX_STEP_MODULO + 1); + let iter_count = g.gen_range(0, MAX_ITER_COUNT + 1); + let hint_kind = qc::Arbitrary::arbitrary(g); + + ShiftRange { + range_start: range_start, + range_end: range_end, + start_step: start_step, + end_step: end_step, + iter_count: iter_count, + hint_kind: hint_kind + } + } +} + +fn correct_size_hint(mut it: I) -> bool { + // record size hint at each iteration + let initial_hint = it.size_hint(); + let mut hints = Vec::with_capacity(initial_hint.0 + 1); + hints.push(initial_hint); + while let Some(_) = it.next() { + hints.push(it.size_hint()) + } + + let mut true_count = hints.len(); // start off +1 too much + + // check all the size hints + for &(low, hi) in &hints { + true_count -= 1; + if low > true_count || + (hi.is_some() && hi.unwrap() < true_count) + { + println!("True size: {:?}, size hint: {:?}", true_count, (low, hi)); + //println!("All hints: {:?}", hints); + return false + } + } + true +} + +fn exact_size(mut it: I) -> bool { + // check every iteration + let (mut low, mut hi) = it.size_hint(); + if Some(low) != hi { return false; } + while let Some(_) = it.next() { + let (xlow, xhi) = it.size_hint(); + if low != xlow + 1 { return false; } + low = xlow; + hi = xhi; + if Some(low) != hi { return false; } + } + let (low, hi) = it.size_hint(); + low == 0 && hi == Some(0) +} + +// Exact size for this case, without ExactSizeIterator +fn exact_size_for_this(mut it: I) -> bool { + // check every iteration + let (mut low, mut hi) = it.size_hint(); + if Some(low) != hi { return false; } + while let Some(_) = it.next() { + let (xlow, xhi) = it.size_hint(); + if low != xlow + 1 { return false; } + low = xlow; + hi = xhi; + if Some(low) != hi { return false; } + } + let (low, hi) = it.size_hint(); + low == 0 && hi == Some(0) +} + +/* + * NOTE: Range is broken! + * (all signed ranges are) +#[quickcheck] +fn size_range_i8(a: Iter) -> bool { + exact_size(a) +} + +#[quickcheck] +fn size_range_i16(a: Iter) -> bool { + exact_size(a) +} + +#[quickcheck] +fn size_range_u8(a: Iter) -> bool { + exact_size(a) +} + */ + +macro_rules! quickcheck { + // accept several property function definitions + // The property functions can use pattern matching and `mut` as usual + // in the function arguments, but the functions can not be generic. + {$($(#$attr:tt)* fn $fn_name:ident($($arg:tt)*) -> $ret:ty { $($code:tt)* })*} => ( + $( + #[test] + $(#$attr)* + fn $fn_name() { + fn prop($($arg)*) -> $ret { + $($code)* + } + ::quickcheck::quickcheck(quickcheck!(@fn prop [] $($arg)*)); + } + )* + ); + // parse argument list (with patterns allowed) into prop as fn(_, _) -> _ + (@fn $f:ident [$($t:tt)*]) => { + $f as fn($($t),*) -> _ + }; + (@fn $f:ident [$($p:tt)*] : $($tail:tt)*) => { + quickcheck!(@fn $f [$($p)* _] $($tail)*) + }; + (@fn $f:ident [$($p:tt)*] $t:tt $($tail:tt)*) => { + quickcheck!(@fn $f [$($p)*] $($tail)*) + }; +} + +quickcheck! { + + fn size_product(a: Iter, b: Iter) -> bool { + correct_size_hint(a.cartesian_product(b)) + } + fn size_product3(a: Iter, b: Iter, c: Iter) -> bool { + correct_size_hint(iproduct!(a, b, c)) + } + + fn correct_cartesian_product3(a: Iter, b: Iter, c: Iter, + take_manual: usize) -> () + { + // test correctness of iproduct through regular iteration (take) + // and through fold. + let ac = a.clone(); + let br = &b.clone(); + let cr = &c.clone(); + let answer: Vec<_> = ac.flat_map(move |ea| br.clone().flat_map(move |eb| cr.clone().map(move |ec| (ea, eb, ec)))).collect(); + let mut product_iter = iproduct!(a, b, c); + let mut actual = Vec::new(); + + actual.extend((&mut product_iter).take(take_manual)); + if actual.len() == take_manual { + product_iter.fold((), |(), elt| actual.push(elt)); + } + assert_eq!(answer, actual); + } + + fn size_multi_product(a: ShiftRange) -> bool { + correct_size_hint(a.multi_cartesian_product()) + } + fn correct_multi_product3(a: ShiftRange, take_manual: usize) -> () { + // Fix no. of iterators at 3 + let a = ShiftRange { iter_count: 3, ..a }; + + // test correctness of MultiProduct through regular iteration (take) + // and through fold. + let mut iters = a.clone(); + let i0 = iters.next().unwrap(); + let i1r = &iters.next().unwrap(); + let i2r = &iters.next().unwrap(); + let answer: Vec<_> = i0.flat_map(move |ei0| i1r.clone().flat_map(move |ei1| i2r.clone().map(move |ei2| vec![ei0, ei1, ei2]))).collect(); + let mut multi_product = a.clone().multi_cartesian_product(); + let mut actual = Vec::new(); + + actual.extend((&mut multi_product).take(take_manual)); + if actual.len() == take_manual { + multi_product.fold((), |(), elt| actual.push(elt)); + } + assert_eq!(answer, actual); + + assert_eq!(answer.into_iter().last(), a.clone().multi_cartesian_product().last()); + } + + #[allow(deprecated)] + fn size_step(a: Iter, s: usize) -> bool { + let mut s = s; + if s == 0 { + s += 1; // never zero + } + let filt = a.clone().dedup(); + correct_size_hint(filt.step(s)) && + exact_size(a.step(s)) + } + + #[allow(deprecated)] + fn equal_step(a: Iter, s: usize) -> bool { + let mut s = s; + if s == 0 { + s += 1; // never zero + } + let mut i = 0; + itertools::equal(a.clone().step(s), a.filter(|_| { + let keep = i % s == 0; + i += 1; + keep + })) + } + + #[allow(deprecated)] + fn equal_step_vec(a: Vec, s: usize) -> bool { + let mut s = s; + if s == 0 { + s += 1; // never zero + } + let mut i = 0; + itertools::equal(a.iter().step(s), a.iter().filter(|_| { + let keep = i % s == 0; + i += 1; + keep + })) + } + + fn size_multipeek(a: Iter, s: u8) -> bool { + let mut it = multipeek(a); + // peek a few times + for _ in 0..s { + it.peek(); + } + exact_size(it) + } + + fn equal_merge(a: Vec, b: Vec) -> bool { + let mut sa = a.clone(); + let mut sb = b.clone(); + sa.sort(); + sb.sort(); + let mut merged = sa.clone(); + merged.extend(sb.iter().cloned()); + merged.sort(); + itertools::equal(&merged, sa.iter().merge(&sb)) + } + fn size_merge(a: Iter, b: Iter) -> bool { + correct_size_hint(a.merge(b)) + } + fn size_zip(a: Iter, b: Iter, c: Iter) -> bool { + let filt = a.clone().dedup(); + correct_size_hint(multizip((filt, b.clone(), c.clone()))) && + exact_size(multizip((a, b, c))) + } + fn size_zip_rc(a: Iter, b: Iter) -> bool { + let rc = rciter(a.clone()); + correct_size_hint(multizip((&rc, &rc, b))) + } + + fn size_zip_macro(a: Iter, b: Iter, c: Iter) -> bool { + let filt = a.clone().dedup(); + correct_size_hint(izip!(filt, b.clone(), c.clone())) && + exact_size(izip!(a, b, c)) + } + fn equal_kmerge(a: Vec, b: Vec, c: Vec) -> bool { + use itertools::free::kmerge; + let mut sa = a.clone(); + let mut sb = b.clone(); + let mut sc = c.clone(); + sa.sort(); + sb.sort(); + sc.sort(); + let mut merged = sa.clone(); + merged.extend(sb.iter().cloned()); + merged.extend(sc.iter().cloned()); + merged.sort(); + itertools::equal(merged.into_iter(), kmerge(vec![sa, sb, sc])) + } + + // Any number of input iterators + fn equal_kmerge_2(mut inputs: Vec>) -> bool { + use itertools::free::kmerge; + // sort the inputs + for input in &mut inputs { + input.sort(); + } + let mut merged = inputs.concat(); + merged.sort(); + itertools::equal(merged.into_iter(), kmerge(inputs)) + } + + // Any number of input iterators + fn equal_kmerge_by_ge(mut inputs: Vec>) -> bool { + // sort the inputs + for input in &mut inputs { + input.sort(); + input.reverse(); + } + let mut merged = inputs.concat(); + merged.sort(); + merged.reverse(); + itertools::equal(merged.into_iter(), + inputs.into_iter().kmerge_by(|x, y| x >= y)) + } + + // Any number of input iterators + fn equal_kmerge_by_lt(mut inputs: Vec>) -> bool { + // sort the inputs + for input in &mut inputs { + input.sort(); + } + let mut merged = inputs.concat(); + merged.sort(); + itertools::equal(merged.into_iter(), + inputs.into_iter().kmerge_by(|x, y| x < y)) + } + + // Any number of input iterators + fn equal_kmerge_by_le(mut inputs: Vec>) -> bool { + // sort the inputs + for input in &mut inputs { + input.sort(); + } + let mut merged = inputs.concat(); + merged.sort(); + itertools::equal(merged.into_iter(), + inputs.into_iter().kmerge_by(|x, y| x <= y)) + } + fn size_kmerge(a: Iter, b: Iter, c: Iter) -> bool { + use itertools::free::kmerge; + correct_size_hint(kmerge(vec![a, b, c])) + } + fn equal_zip_eq(a: Vec, b: Vec) -> bool { + let len = std::cmp::min(a.len(), b.len()); + let a = &a[..len]; + let b = &b[..len]; + itertools::equal(zip_eq(a, b), zip(a, b)) + } + fn size_zip_longest(a: Iter, b: Iter) -> bool { + let filt = a.clone().dedup(); + let filt2 = b.clone().dedup(); + correct_size_hint(filt.zip_longest(b.clone())) && + correct_size_hint(a.clone().zip_longest(filt2)) && + exact_size(a.zip_longest(b)) + } + fn size_2_zip_longest(a: Iter, b: Iter) -> bool { + let it = a.clone().zip_longest(b.clone()); + let jt = a.clone().zip_longest(b.clone()); + itertools::equal(a.clone(), + it.filter_map(|elt| match elt { + EitherOrBoth::Both(x, _) => Some(x), + EitherOrBoth::Left(x) => Some(x), + _ => None, + } + )) + && + itertools::equal(b.clone(), + jt.filter_map(|elt| match elt { + EitherOrBoth::Both(_, y) => Some(y), + EitherOrBoth::Right(y) => Some(y), + _ => None, + } + )) + } + fn size_interleave(a: Iter, b: Iter) -> bool { + correct_size_hint(a.interleave(b)) + } + fn exact_interleave(a: Iter, b: Iter) -> bool { + exact_size_for_this(a.interleave(b)) + } + fn size_interleave_shortest(a: Iter, b: Iter) -> bool { + correct_size_hint(a.interleave_shortest(b)) + } + fn exact_interleave_shortest(a: Vec<()>, b: Vec<()>) -> bool { + exact_size_for_this(a.iter().interleave_shortest(&b)) + } + fn size_intersperse(a: Iter, x: i16) -> bool { + correct_size_hint(a.intersperse(x)) + } + fn equal_intersperse(a: Vec, x: i32) -> bool { + let mut inter = false; + let mut i = 0; + for elt in a.iter().cloned().intersperse(x) { + if inter { + if elt != x { return false } + } else { + if elt != a[i] { return false } + i += 1; + } + inter = !inter; + } + true + } + + fn equal_combinations_2(a: Vec) -> bool { + let mut v = Vec::new(); + for (i, x) in enumerate(&a) { + for y in &a[i + 1..] { + v.push((x, y)); + } + } + itertools::equal(a.iter().tuple_combinations::<(_, _)>(), v) + } + + fn collect_tuple_matches_size(a: Iter) -> bool { + let size = a.clone().count(); + a.collect_tuple::<(_, _, _)>().is_some() == (size == 3) + } +} + +quickcheck! { + fn equal_dedup(a: Vec) -> bool { + let mut b = a.clone(); + b.dedup(); + itertools::equal(&b, a.iter().dedup()) + } +} + +quickcheck! { + fn size_dedup(a: Vec) -> bool { + correct_size_hint(a.iter().dedup()) + } +} + +quickcheck! { + fn exact_repeatn((n, x): (usize, i32)) -> bool { + let it = itertools::repeat_n(x, n); + exact_size(it) + } +} + +quickcheck! { + fn size_put_back(a: Vec, x: Option) -> bool { + let mut it = put_back(a.into_iter()); + match x { + Some(t) => it.put_back(t), + None => {} + } + correct_size_hint(it) + } +} + +quickcheck! { + fn size_put_backn(a: Vec, b: Vec) -> bool { + let mut it = put_back_n(a.into_iter()); + for elt in b { + it.put_back(elt) + } + correct_size_hint(it) + } +} + +quickcheck! { + fn size_tee(a: Vec) -> bool { + let (mut t1, mut t2) = a.iter().tee(); + t1.next(); + t1.next(); + t2.next(); + exact_size(t1) && exact_size(t2) + } +} + +quickcheck! { + fn size_tee_2(a: Vec) -> bool { + let (mut t1, mut t2) = a.iter().dedup().tee(); + t1.next(); + t1.next(); + t2.next(); + correct_size_hint(t1) && correct_size_hint(t2) + } +} + +quickcheck! { + fn size_take_while_ref(a: Vec, stop: u8) -> bool { + correct_size_hint(a.iter().take_while_ref(|x| **x != stop)) + } +} + +quickcheck! { + fn equal_partition(a: Vec) -> bool { + let mut a = a; + let mut ap = a.clone(); + let split_index = itertools::partition(&mut ap, |x| *x >= 0); + let parted = (0..split_index).all(|i| ap[i] >= 0) && + (split_index..a.len()).all(|i| ap[i] < 0); + + a.sort(); + ap.sort(); + parted && (a == ap) + } +} + +quickcheck! { + fn size_combinations(it: Iter) -> bool { + correct_size_hint(it.tuple_combinations::<(_, _)>()) + } +} + +quickcheck! { + fn equal_combinations(it: Iter) -> bool { + let values = it.clone().collect_vec(); + let mut cmb = it.tuple_combinations(); + for i in 0..values.len() { + for j in i+1..values.len() { + let pair = (values[i], values[j]); + if pair != cmb.next().unwrap() { + return false; + } + } + } + cmb.next() == None + } +} + +quickcheck! { + fn size_pad_tail(it: Iter, pad: u8) -> bool { + correct_size_hint(it.clone().pad_using(pad as usize, |_| 0)) && + correct_size_hint(it.dropping(1).rev().pad_using(pad as usize, |_| 0)) + } +} + +quickcheck! { + fn size_pad_tail2(it: Iter, pad: u8) -> bool { + exact_size(it.pad_using(pad as usize, |_| 0)) + } +} + +quickcheck! { + fn size_unique(it: Iter) -> bool { + correct_size_hint(it.unique()) + } + + fn count_unique(it: Vec, take_first: u8) -> () { + let answer = { + let mut v = it.clone(); + v.sort(); v.dedup(); + v.len() + }; + let mut iter = cloned(&it).unique(); + let first_count = (&mut iter).take(take_first as usize).count(); + let rest_count = iter.count(); + assert_eq!(answer, first_count + rest_count); + } +} + +quickcheck! { + fn fuzz_group_by_lazy_1(it: Iter) -> bool { + let jt = it.clone(); + let groups = it.group_by(|k| *k); + let res = itertools::equal(jt, groups.into_iter().flat_map(|(_, x)| x)); + res + } +} + +quickcheck! { + fn fuzz_group_by_lazy_2(data: Vec) -> bool { + let groups = data.iter().group_by(|k| *k / 10); + let res = itertools::equal(data.iter(), groups.into_iter().flat_map(|(_, x)| x)); + res + } +} + +quickcheck! { + fn fuzz_group_by_lazy_3(data: Vec) -> bool { + let grouper = data.iter().group_by(|k| *k / 10); + let groups = grouper.into_iter().collect_vec(); + let res = itertools::equal(data.iter(), groups.into_iter().flat_map(|(_, x)| x)); + res + } +} + +quickcheck! { + fn fuzz_group_by_lazy_duo(data: Vec, order: Vec<(bool, bool)>) -> bool { + let grouper = data.iter().group_by(|k| *k / 3); + let mut groups1 = grouper.into_iter(); + let mut groups2 = grouper.into_iter(); + let mut elts = Vec::<&u8>::new(); + let mut old_groups = Vec::new(); + + let tup1 = |(_, b)| b; + for &(ord, consume_now) in &order { + let iter = &mut [&mut groups1, &mut groups2][ord as usize]; + match iter.next() { + Some((_, gr)) => if consume_now { + for og in old_groups.drain(..) { + elts.extend(og); + } + elts.extend(gr); + } else { + old_groups.push(gr); + }, + None => break, + } + } + for og in old_groups.drain(..) { + elts.extend(og); + } + for gr in groups1.map(&tup1) { elts.extend(gr); } + for gr in groups2.map(&tup1) { elts.extend(gr); } + itertools::assert_equal(&data, elts); + true + } +} + +quickcheck! { + fn equal_chunks_lazy(a: Vec, size: u8) -> bool { + let mut size = size; + if size == 0 { + size += 1; + } + let chunks = a.iter().chunks(size as usize); + let it = a.chunks(size as usize); + for (a, b) in chunks.into_iter().zip(it) { + if !itertools::equal(a, b) { + return false; + } + } + true + } +} + +quickcheck! { + fn equal_tuple_windows_1(a: Vec) -> bool { + let x = a.windows(1).map(|s| (&s[0], )); + let y = a.iter().tuple_windows::<(_,)>(); + itertools::equal(x, y) + } + + fn equal_tuple_windows_2(a: Vec) -> bool { + let x = a.windows(2).map(|s| (&s[0], &s[1])); + let y = a.iter().tuple_windows::<(_, _)>(); + itertools::equal(x, y) + } + + fn equal_tuple_windows_3(a: Vec) -> bool { + let x = a.windows(3).map(|s| (&s[0], &s[1], &s[2])); + let y = a.iter().tuple_windows::<(_, _, _)>(); + itertools::equal(x, y) + } + + fn equal_tuple_windows_4(a: Vec) -> bool { + let x = a.windows(4).map(|s| (&s[0], &s[1], &s[2], &s[3])); + let y = a.iter().tuple_windows::<(_, _, _, _)>(); + itertools::equal(x, y) + } + + fn equal_tuples_1(a: Vec) -> bool { + let x = a.chunks(1).map(|s| (&s[0], )); + let y = a.iter().tuples::<(_,)>(); + itertools::equal(x, y) + } + + fn equal_tuples_2(a: Vec) -> bool { + let x = a.chunks(2).filter(|s| s.len() == 2).map(|s| (&s[0], &s[1])); + let y = a.iter().tuples::<(_, _)>(); + itertools::equal(x, y) + } + + fn equal_tuples_3(a: Vec) -> bool { + let x = a.chunks(3).filter(|s| s.len() == 3).map(|s| (&s[0], &s[1], &s[2])); + let y = a.iter().tuples::<(_, _, _)>(); + itertools::equal(x, y) + } + + fn equal_tuples_4(a: Vec) -> bool { + let x = a.chunks(4).filter(|s| s.len() == 4).map(|s| (&s[0], &s[1], &s[2], &s[3])); + let y = a.iter().tuples::<(_, _, _, _)>(); + itertools::equal(x, y) + } + + fn exact_tuple_buffer(a: Vec) -> bool { + let mut iter = a.iter().tuples::<(_, _, _, _)>(); + (&mut iter).last(); + let buffer = iter.into_buffer(); + assert_eq!(buffer.len(), a.len() % 4); + exact_size(buffer) + } +} + +// with_position +quickcheck! { + fn with_position_exact_size_1(a: Vec) -> bool { + exact_size_for_this(a.iter().with_position()) + } + fn with_position_exact_size_2(a: Iter) -> bool { + exact_size_for_this(a.with_position()) + } +} + +quickcheck! { + fn correct_group_map_modulo_key(a: Vec, modulo: u8) -> () { + let modulo = if modulo == 0 { 1 } else { modulo }; // Avoid `% 0` + let count = a.len(); + let lookup = a.into_iter().map(|i| (i % modulo, i)).into_group_map(); + + assert_eq!(lookup.values().flat_map(|vals| vals.iter()).count(), count); + + for (&key, vals) in lookup.iter() { + assert!(vals.iter().all(|&val| val % modulo == key)); + } + } +} + +/// A peculiar type: Equality compares both tuple items, but ordering only the +/// first item. This is so we can check the stability property easily. +#[derive(Clone, Debug, PartialEq, Eq)] +struct Val(u32, u32); + +impl PartialOrd for Val { + fn partial_cmp(&self, other: &Val) -> Option { + self.0.partial_cmp(&other.0) + } +} + +impl Ord for Val { + fn cmp(&self, other: &Val) -> Ordering { + self.0.cmp(&other.0) + } +} + +impl qc::Arbitrary for Val { + fn arbitrary(g: &mut G) -> Self { + let (x, y) = <(u32, u32)>::arbitrary(g); + Val(x, y) + } + fn shrink(&self) -> Box> { + Box::new((self.0, self.1).shrink().map(|(x, y)| Val(x, y))) + } +} + +quickcheck! { + fn minmax(a: Vec) -> bool { + use itertools::MinMaxResult; + + + let minmax = a.iter().minmax(); + let expected = match a.len() { + 0 => MinMaxResult::NoElements, + 1 => MinMaxResult::OneElement(&a[0]), + _ => MinMaxResult::MinMax(a.iter().min().unwrap(), + a.iter().max().unwrap()), + }; + minmax == expected + } +} + +quickcheck! { + fn minmax_f64(a: Vec) -> TestResult { + use itertools::MinMaxResult; + + if a.iter().any(|x| x.is_nan()) { + return TestResult::discard(); + } + + let min = cloned(&a).fold1(f64::min); + let max = cloned(&a).fold1(f64::max); + + let minmax = cloned(&a).minmax(); + let expected = match a.len() { + 0 => MinMaxResult::NoElements, + 1 => MinMaxResult::OneElement(min.unwrap()), + _ => MinMaxResult::MinMax(min.unwrap(), max.unwrap()), + }; + TestResult::from_bool(minmax == expected) + } +} + +quickcheck! { + #[allow(deprecated)] + fn tree_fold1_f64(mut a: Vec) -> TestResult { + fn collapse_adjacent(x: Vec, mut f: F) -> Vec + where F: FnMut(f64, f64) -> f64 + { + let mut out = Vec::new(); + for i in (0..x.len()).step(2) { + if i == x.len()-1 { + out.push(x[i]) + } else { + out.push(f(x[i], x[i+1])); + } + } + out + } + + if a.iter().any(|x| x.is_nan()) { + return TestResult::discard(); + } + + let actual = a.iter().cloned().tree_fold1(f64::atan2); + + while a.len() > 1 { + a = collapse_adjacent(a, f64::atan2); + } + let expected = a.pop(); + + TestResult::from_bool(actual == expected) + } +} diff --git a/third_party/rust/itertools-0.8.0/tests/test_core.rs b/third_party/rust/itertools-0.8.0/tests/test_core.rs new file mode 100644 index 0000000000000..cf97abd3603a0 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/tests/test_core.rs @@ -0,0 +1,246 @@ +//! 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. This file may not be copied, modified, or distributed +//! except according to those terms. +#![no_std] + +#[macro_use] extern crate itertools as it; + +use core::iter; + +use it::Itertools; +use it::interleave; +use it::multizip; +use it::free::put_back; + +#[test] +fn product2() { + let s = "αβ"; + + let mut prod = iproduct!(s.chars(), 0..2); + assert!(prod.next() == Some(('α', 0))); + assert!(prod.next() == Some(('α', 1))); + assert!(prod.next() == Some(('β', 0))); + assert!(prod.next() == Some(('β', 1))); + assert!(prod.next() == None); +} + +#[test] +fn product_temporary() { + for (_x, _y, _z) in iproduct!( + [0, 1, 2].iter().cloned(), + [0, 1, 2].iter().cloned(), + [0, 1, 2].iter().cloned()) + { + // ok + } +} + + +#[test] +fn izip_macro() { + let mut zip = izip!(2..3); + assert!(zip.next() == Some(2)); + assert!(zip.next().is_none()); + + let mut zip = izip!(0..3, 0..2, 0..2i8); + for i in 0..2 { + assert!((i as usize, i, i as i8) == zip.next().unwrap()); + } + assert!(zip.next().is_none()); + + let xs: [isize; 0] = []; + let mut zip = izip!(0..3, 0..2, 0..2i8, &xs); + assert!(zip.next().is_none()); +} + +#[test] +fn izip2() { + let _zip1: iter::Zip<_, _> = izip!(1.., 2..); + let _zip2: iter::Zip<_, _> = izip!(1.., 2.., ); +} + +#[test] +fn izip3() { + let mut zip: iter::Map, _> = izip!(0..3, 0..2, 0..2i8); + for i in 0..2 { + assert!((i as usize, i, i as i8) == zip.next().unwrap()); + } + assert!(zip.next().is_none()); +} + +#[test] +fn multizip3() { + let mut zip = multizip((0..3, 0..2, 0..2i8)); + for i in 0..2 { + assert!((i as usize, i, i as i8) == zip.next().unwrap()); + } + assert!(zip.next().is_none()); + + let xs: [isize; 0] = []; + let mut zip = multizip((0..3, 0..2, 0..2i8, xs.iter())); + assert!(zip.next().is_none()); + + for (_, _, _, _, _) in multizip((0..3, 0..2, xs.iter(), &xs, xs.to_vec())) { + /* test compiles */ + } +} + +#[test] +fn write_to() { + let xs = [7, 9, 8]; + let mut ys = [0; 5]; + let cnt = ys.iter_mut().set_from(xs.iter().map(|x| *x)); + assert!(cnt == xs.len()); + assert!(ys == [7, 9, 8, 0, 0]); + + let cnt = ys.iter_mut().set_from(0..10); + assert!(cnt == ys.len()); + assert!(ys == [0, 1, 2, 3, 4]); +} + +#[test] +fn test_interleave() { + let xs: [u8; 0] = []; + let ys = [7u8, 9, 8, 10]; + let zs = [2u8, 77]; + let it = interleave(xs.iter(), ys.iter()); + it::assert_equal(it, ys.iter()); + + let rs = [7u8, 2, 9, 77, 8, 10]; + let it = interleave(ys.iter(), zs.iter()); + it::assert_equal(it, rs.iter()); +} + +#[allow(deprecated)] +#[test] +fn foreach() { + let xs = [1i32, 2, 3]; + let mut sum = 0; + xs.iter().foreach(|elt| sum += *elt); + assert!(sum == 6); +} + +#[test] +fn dropping() { + let xs = [1, 2, 3]; + let mut it = xs.iter().dropping(2); + assert_eq!(it.next(), Some(&3)); + assert!(it.next().is_none()); + let mut it = xs.iter().dropping(5); + assert!(it.next().is_none()); +} + +#[test] +fn batching() { + let xs = [0, 1, 2, 1, 3]; + let ys = [(0, 1), (2, 1)]; + + // An iterator that gathers elements up in pairs + let pit = xs.iter().cloned().batching(|it| { + match it.next() { + None => None, + Some(x) => match it.next() { + None => None, + Some(y) => Some((x, y)), + } + } + }); + it::assert_equal(pit, ys.iter().cloned()); +} + +#[test] +fn test_put_back() { + let xs = [0, 1, 1, 1, 2, 1, 3, 3]; + let mut pb = put_back(xs.iter().cloned()); + pb.next(); + pb.put_back(1); + pb.put_back(0); + it::assert_equal(pb, xs.iter().cloned()); +} + +#[allow(deprecated)] +#[test] +fn step() { + it::assert_equal((0..10).step(1), 0..10); + it::assert_equal((0..10).step(2), (0..10).filter(|x: &i32| *x % 2 == 0)); + it::assert_equal((0..10).step(10), 0..1); +} + +#[allow(deprecated)] +#[test] +fn merge() { + it::assert_equal((0..10).step(2).merge((1..10).step(2)), 0..10); +} + + +#[test] +fn repeatn() { + let s = "α"; + let mut it = it::repeat_n(s, 3); + assert_eq!(it.len(), 3); + assert_eq!(it.next(), Some(s)); + assert_eq!(it.next(), Some(s)); + assert_eq!(it.next(), Some(s)); + assert_eq!(it.next(), None); + assert_eq!(it.next(), None); +} + +#[test] +fn count_clones() { + // Check that RepeatN only clones N - 1 times. + + use core::cell::Cell; + #[derive(PartialEq, Debug)] + struct Foo { + n: Cell + } + + impl Clone for Foo + { + fn clone(&self) -> Self + { + let n = self.n.get(); + self.n.set(n + 1); + Foo { n: Cell::new(n + 1) } + } + } + + + for n in 0..10 { + let f = Foo{n: Cell::new(0)}; + let it = it::repeat_n(f, n); + // drain it + let last = it.last(); + if n == 0 { + assert_eq!(last, None); + } else { + assert_eq!(last, Some(Foo{n: Cell::new(n - 1)})); + } + } +} + +#[test] +fn part() { + let mut data = [7, 1, 1, 9, 1, 1, 3]; + let i = it::partition(&mut data, |elt| *elt >= 3); + assert_eq!(i, 3); + assert_eq!(data, [7, 3, 9, 1, 1, 1, 1]); + + let i = it::partition(&mut data, |elt| *elt == 1); + assert_eq!(i, 4); + assert_eq!(data, [1, 1, 1, 1, 9, 3, 7]); + + let mut data = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + let i = it::partition(&mut data, |elt| *elt % 3 == 0); + assert_eq!(i, 3); + assert_eq!(data, [9, 6, 3, 4, 5, 2, 7, 8, 1]); +} + +#[test] +fn tree_fold1() { + for i in 0..100 { + assert_eq!((0..i).tree_fold1(|x, y| x + y), (0..i).fold1(|x, y| x + y)); + } +} diff --git a/third_party/rust/itertools-0.8.0/tests/test_std.rs b/third_party/rust/itertools-0.8.0/tests/test_std.rs new file mode 100644 index 0000000000000..b587f447060ce --- /dev/null +++ b/third_party/rust/itertools-0.8.0/tests/test_std.rs @@ -0,0 +1,735 @@ + +#[macro_use] extern crate itertools as it; +extern crate permutohedron; + +use it::Itertools; +use it::multizip; +use it::multipeek; +use it::free::rciter; +use it::free::put_back_n; +use it::FoldWhile; +use it::cloned; + +#[test] +fn product3() { + let prod = iproduct!(0..3, 0..2, 0..2); + assert_eq!(prod.size_hint(), (12, Some(12))); + let v = prod.collect_vec(); + for i in 0..3 { + for j in 0..2 { + for k in 0..2 { + assert!((i, j, k) == v[(i * 2 * 2 + j * 2 + k) as usize]); + } + } + } + for (_, _, _, _) in iproduct!(0..3, 0..2, 0..2, 0..3) { + /* test compiles */ + } +} + +#[test] +fn interleave_shortest() { + let v0: Vec = vec![0, 2, 4]; + let v1: Vec = vec![1, 3, 5, 7]; + let it = v0.into_iter().interleave_shortest(v1.into_iter()); + assert_eq!(it.size_hint(), (6, Some(6))); + assert_eq!(it.collect_vec(), vec![0, 1, 2, 3, 4, 5]); + + let v0: Vec = vec![0, 2, 4, 6, 8]; + let v1: Vec = vec![1, 3, 5]; + let it = v0.into_iter().interleave_shortest(v1.into_iter()); + assert_eq!(it.size_hint(), (7, Some(7))); + assert_eq!(it.collect_vec(), vec![0, 1, 2, 3, 4, 5, 6]); + + let i0 = ::std::iter::repeat(0); + let v1: Vec<_> = vec![1, 3, 5]; + let it = i0.interleave_shortest(v1.into_iter()); + assert_eq!(it.size_hint(), (7, Some(7))); + + let v0: Vec<_> = vec![0, 2, 4]; + let i1 = ::std::iter::repeat(1); + let it = v0.into_iter().interleave_shortest(i1); + assert_eq!(it.size_hint(), (6, Some(6))); +} + + +#[test] +fn unique_by() { + let xs = ["aaa", "bbbbb", "aa", "ccc", "bbbb", "aaaaa", "cccc"]; + let ys = ["aaa", "bbbbb", "ccc"]; + it::assert_equal(ys.iter(), xs.iter().unique_by(|x| x[..2].to_string())); +} + +#[test] +fn unique() { + let xs = [0, 1, 2, 3, 2, 1, 3]; + let ys = [0, 1, 2, 3]; + it::assert_equal(ys.iter(), xs.iter().unique()); + let xs = [0, 1]; + let ys = [0, 1]; + it::assert_equal(ys.iter(), xs.iter().unique()); +} + +#[test] +fn intersperse() { + let xs = ["a", "", "b", "c"]; + let v: Vec<&str> = xs.iter().map(|x| x.clone()).intersperse(", ").collect(); + let text: String = v.concat(); + assert_eq!(text, "a, , b, c".to_string()); + + let ys = [0, 1, 2, 3]; + let mut it = ys[..0].iter().map(|x| *x).intersperse(1); + assert!(it.next() == None); +} + +#[test] +fn dedup() { + let xs = [0, 1, 1, 1, 2, 1, 3, 3]; + let ys = [0, 1, 2, 1, 3]; + it::assert_equal(ys.iter(), xs.iter().dedup()); + let xs = [0, 0, 0, 0, 0]; + let ys = [0]; + it::assert_equal(ys.iter(), xs.iter().dedup()); + + let xs = [0, 1, 1, 1, 2, 1, 3, 3]; + let ys = [0, 1, 2, 1, 3]; + let mut xs_d = Vec::new(); + xs.iter().dedup().fold((), |(), &elt| xs_d.push(elt)); + assert_eq!(&xs_d, &ys); +} + +#[test] +fn all_equal() { + assert!(!"AABBCCC".chars().all_equal()); + assert!("AAAAAAA".chars().all_equal()); + for (_key, mut sub) in &"AABBCCC".chars().group_by(|&x| x) { + assert!(sub.all_equal()); + } +} + +#[test] +fn test_put_back_n() { + let xs = [0, 1, 1, 1, 2, 1, 3, 3]; + let mut pb = put_back_n(xs.iter().cloned()); + pb.next(); + pb.next(); + pb.put_back(1); + pb.put_back(0); + it::assert_equal(pb, xs.iter().cloned()); +} + +#[test] +fn tee() { + let xs = [0, 1, 2, 3]; + let (mut t1, mut t2) = xs.iter().cloned().tee(); + assert_eq!(t1.next(), Some(0)); + assert_eq!(t2.next(), Some(0)); + assert_eq!(t1.next(), Some(1)); + assert_eq!(t1.next(), Some(2)); + assert_eq!(t1.next(), Some(3)); + assert_eq!(t1.next(), None); + assert_eq!(t2.next(), Some(1)); + assert_eq!(t2.next(), Some(2)); + assert_eq!(t1.next(), None); + assert_eq!(t2.next(), Some(3)); + assert_eq!(t2.next(), None); + assert_eq!(t1.next(), None); + assert_eq!(t2.next(), None); + + let (t1, t2) = xs.iter().cloned().tee(); + it::assert_equal(t1, xs.iter().cloned()); + it::assert_equal(t2, xs.iter().cloned()); + + let (t1, t2) = xs.iter().cloned().tee(); + it::assert_equal(t1.zip(t2), xs.iter().cloned().zip(xs.iter().cloned())); +} + + +#[test] +fn test_rciter() { + let xs = [0, 1, 1, 1, 2, 1, 3, 5, 6]; + + let mut r1 = rciter(xs.iter().cloned()); + let mut r2 = r1.clone(); + assert_eq!(r1.next(), Some(0)); + assert_eq!(r2.next(), Some(1)); + let mut z = r1.zip(r2); + assert_eq!(z.next(), Some((1, 1))); + assert_eq!(z.next(), Some((2, 1))); + assert_eq!(z.next(), Some((3, 5))); + assert_eq!(z.next(), None); + + // test intoiterator + let r1 = rciter(0..5); + let mut z = izip!(&r1, r1); + assert_eq!(z.next(), Some((0, 1))); +} + +#[allow(deprecated)] +#[test] +fn trait_pointers() { + struct ByRef<'r, I: ?Sized>(&'r mut I) where I: 'r; + + impl<'r, X, I: ?Sized> Iterator for ByRef<'r, I> where + I: 'r + Iterator + { + type Item = X; + fn next(&mut self) -> Option + { + self.0.next() + } + } + + let mut it = Box::new(0..10) as Box>; + assert_eq!(it.next(), Some(0)); + + { + /* make sure foreach works on non-Sized */ + let jt: &mut Iterator = &mut *it; + assert_eq!(jt.next(), Some(1)); + + { + let mut r = ByRef(jt); + assert_eq!(r.next(), Some(2)); + } + + assert_eq!(jt.find_position(|x| *x == 4), Some((1, 4))); + jt.foreach(|_| ()); + } +} + +#[test] +fn merge_by() { + let odd : Vec<(u32, &str)> = vec![(1, "hello"), (3, "world"), (5, "!")]; + let even = vec![(2, "foo"), (4, "bar"), (6, "baz")]; + let expected = vec![(1, "hello"), (2, "foo"), (3, "world"), (4, "bar"), (5, "!"), (6, "baz")]; + let results = odd.iter().merge_by(even.iter(), |a, b| a.0 <= b.0); + it::assert_equal(results, expected.iter()); +} + +#[test] +fn merge_by_btree() { + use std::collections::BTreeMap; + let mut bt1 = BTreeMap::new(); + bt1.insert("hello", 1); + bt1.insert("world", 3); + let mut bt2 = BTreeMap::new(); + bt2.insert("foo", 2); + bt2.insert("bar", 4); + let results = bt1.into_iter().merge_by(bt2.into_iter(), |a, b| a.0 <= b.0 ); + let expected = vec![("bar", 4), ("foo", 2), ("hello", 1), ("world", 3)]; + it::assert_equal(results, expected.into_iter()); +} + +#[allow(deprecated)] +#[test] +fn kmerge() { + let its = (0..4).map(|s| (s..10).step(4)); + + it::assert_equal(its.kmerge(), 0..10); +} + +#[allow(deprecated)] +#[test] +fn kmerge_2() { + let its = vec![3, 2, 1, 0].into_iter().map(|s| (s..10).step(4)); + + it::assert_equal(its.kmerge(), 0..10); +} + +#[test] +fn kmerge_empty() { + let its = (0..4).map(|_| 0..0); + assert_eq!(its.kmerge().next(), None); +} + +#[test] +fn kmerge_size_hint() { + let its = (0..5).map(|_| (0..10)); + assert_eq!(its.kmerge().size_hint(), (50, Some(50))); +} + +#[test] +fn kmerge_empty_size_hint() { + let its = (0..5).map(|_| (0..0)); + assert_eq!(its.kmerge().size_hint(), (0, Some(0))); +} + +#[test] +fn join() { + let many = [1, 2, 3]; + let one = [1]; + let none: Vec = vec![]; + + assert_eq!(many.iter().join(", "), "1, 2, 3"); + assert_eq!( one.iter().join(", "), "1"); + assert_eq!(none.iter().join(", "), ""); +} + +#[test] +fn sorted_by() { + let sc = [3, 4, 1, 2].iter().cloned().sorted_by(|&a, &b| { + a.cmp(&b) + }); + it::assert_equal(sc, vec![1, 2, 3, 4]); + + let v = (0..5).sorted_by(|&a, &b| a.cmp(&b).reverse()); + it::assert_equal(v, vec![4, 3, 2, 1, 0]); +} + +#[test] +fn sorted_by_key() { + let sc = [3, 4, 1, 2].iter().cloned().sorted_by_key(|&x| x); + it::assert_equal(sc, vec![1, 2, 3, 4]); + + let v = (0..5).sorted_by_key(|&x| -x); + it::assert_equal(v, vec![4, 3, 2, 1, 0]); +} + +#[test] +fn test_multipeek() { + let nums = vec![1u8,2,3,4,5]; + + let mp = multipeek(nums.iter().map(|&x| x)); + assert_eq!(nums, mp.collect::>()); + + let mut mp = multipeek(nums.iter().map(|&x| x)); + assert_eq!(mp.peek(), Some(&1)); + assert_eq!(mp.next(), Some(1)); + assert_eq!(mp.peek(), Some(&2)); + assert_eq!(mp.peek(), Some(&3)); + assert_eq!(mp.next(), Some(2)); + assert_eq!(mp.peek(), Some(&3)); + assert_eq!(mp.peek(), Some(&4)); + assert_eq!(mp.peek(), Some(&5)); + assert_eq!(mp.peek(), None); + assert_eq!(mp.next(), Some(3)); + assert_eq!(mp.next(), Some(4)); + assert_eq!(mp.peek(), Some(&5)); + assert_eq!(mp.peek(), None); + assert_eq!(mp.next(), Some(5)); + assert_eq!(mp.next(), None); + assert_eq!(mp.peek(), None); + +} + +#[test] +fn test_multipeek_reset() { + let data = [1, 2, 3, 4]; + + let mut mp = multipeek(cloned(&data)); + assert_eq!(mp.peek(), Some(&1)); + assert_eq!(mp.next(), Some(1)); + assert_eq!(mp.peek(), Some(&2)); + assert_eq!(mp.peek(), Some(&3)); + mp.reset_peek(); + assert_eq!(mp.peek(), Some(&2)); + assert_eq!(mp.next(), Some(2)); +} + +#[test] +fn test_multipeek_peeking_next() { + use it::PeekingNext; + let nums = vec![1u8,2,3,4,5,6,7]; + + let mut mp = multipeek(nums.iter().map(|&x| x)); + assert_eq!(mp.peeking_next(|&x| x != 0), Some(1)); + assert_eq!(mp.next(), Some(2)); + assert_eq!(mp.peek(), Some(&3)); + assert_eq!(mp.peek(), Some(&4)); + assert_eq!(mp.peeking_next(|&x| x == 3), Some(3)); + assert_eq!(mp.peek(), Some(&4)); + assert_eq!(mp.peeking_next(|&x| x != 4), None); + assert_eq!(mp.peeking_next(|&x| x == 4), Some(4)); + assert_eq!(mp.peek(), Some(&5)); + assert_eq!(mp.peek(), Some(&6)); + assert_eq!(mp.peeking_next(|&x| x != 5), None); + assert_eq!(mp.peek(), Some(&7)); + assert_eq!(mp.peeking_next(|&x| x == 5), Some(5)); + assert_eq!(mp.peeking_next(|&x| x == 6), Some(6)); + assert_eq!(mp.peek(), Some(&7)); + assert_eq!(mp.peek(), None); + assert_eq!(mp.next(), Some(7)); + assert_eq!(mp.peek(), None); +} + +#[test] +fn pad_using() { + it::assert_equal((0..0).pad_using(1, |_| 1), 1..2); + + let v: Vec = vec![0, 1, 2]; + let r = v.into_iter().pad_using(5, |n| n); + it::assert_equal(r, vec![0, 1, 2, 3, 4]); + + let v: Vec = vec![0, 1, 2]; + let r = v.into_iter().pad_using(1, |_| panic!()); + it::assert_equal(r, vec![0, 1, 2]); +} + +#[test] +fn group_by() { + for (ch1, sub) in &"AABBCCC".chars().group_by(|&x| x) { + for ch2 in sub { + assert_eq!(ch1, ch2); + } + } + + for (ch1, sub) in &"AAABBBCCCCDDDD".chars().group_by(|&x| x) { + for ch2 in sub { + assert_eq!(ch1, ch2); + if ch1 == 'C' { + break; + } + } + } + + let toupper = |ch: &char| ch.to_uppercase().nth(0).unwrap(); + + // try all possible orderings + for indices in permutohedron::Heap::new(&mut [0, 1, 2, 3]) { + let groups = "AaaBbbccCcDDDD".chars().group_by(&toupper); + let mut subs = groups.into_iter().collect_vec(); + + for &idx in &indices[..] { + let (key, text) = match idx { + 0 => ('A', "Aaa".chars()), + 1 => ('B', "Bbb".chars()), + 2 => ('C', "ccCc".chars()), + 3 => ('D', "DDDD".chars()), + _ => unreachable!(), + }; + assert_eq!(key, subs[idx].0); + it::assert_equal(&mut subs[idx].1, text); + } + } + + let groups = "AAABBBCCCCDDDD".chars().group_by(|&x| x); + let mut subs = groups.into_iter().map(|(_, g)| g).collect_vec(); + + let sd = subs.pop().unwrap(); + let sc = subs.pop().unwrap(); + let sb = subs.pop().unwrap(); + let sa = subs.pop().unwrap(); + for (a, b, c, d) in multizip((sa, sb, sc, sd)) { + assert_eq!(a, 'A'); + assert_eq!(b, 'B'); + assert_eq!(c, 'C'); + assert_eq!(d, 'D'); + } + + // check that the key closure is called exactly n times + { + let mut ntimes = 0; + let text = "AABCCC"; + for (_, sub) in &text.chars().group_by(|&x| { ntimes += 1; x}) { + for _ in sub { + } + } + assert_eq!(ntimes, text.len()); + } + + { + let mut ntimes = 0; + let text = "AABCCC"; + for _ in &text.chars().group_by(|&x| { ntimes += 1; x}) { + } + assert_eq!(ntimes, text.len()); + } + + { + let text = "ABCCCDEEFGHIJJKK"; + let gr = text.chars().group_by(|&x| x); + it::assert_equal(gr.into_iter().flat_map(|(_, sub)| sub), text.chars()); + } +} + +#[test] +fn group_by_lazy_2() { + let data = vec![0, 1]; + let groups = data.iter().group_by(|k| *k); + let gs = groups.into_iter().collect_vec(); + it::assert_equal(data.iter(), gs.into_iter().flat_map(|(_k, g)| g)); + + let data = vec![0, 1, 1, 0, 0]; + let groups = data.iter().group_by(|k| *k); + let mut gs = groups.into_iter().collect_vec(); + gs[1..].reverse(); + it::assert_equal(&[0, 0, 0, 1, 1], gs.into_iter().flat_map(|(_, g)| g)); + + let grouper = data.iter().group_by(|k| *k); + let mut groups = Vec::new(); + for (k, group) in &grouper { + if *k == 1 { + groups.push(group); + } + } + it::assert_equal(&mut groups[0], &[1, 1]); + + let data = vec![0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3]; + let grouper = data.iter().group_by(|k| *k); + let mut groups = Vec::new(); + for (i, (_, group)) in grouper.into_iter().enumerate() { + if i < 2 { + groups.push(group); + } else if i < 4 { + for _ in group { + } + } else { + groups.push(group); + } + } + it::assert_equal(&mut groups[0], &[0, 0, 0]); + it::assert_equal(&mut groups[1], &[1, 1]); + it::assert_equal(&mut groups[2], &[3, 3]); + + // use groups as chunks + let data = vec![0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3]; + let mut i = 0; + let grouper = data.iter().group_by(move |_| { let k = i / 3; i += 1; k }); + for (i, group) in &grouper { + match i { + 0 => it::assert_equal(group, &[0, 0, 0]), + 1 => it::assert_equal(group, &[1, 1, 0]), + 2 => it::assert_equal(group, &[0, 2, 2]), + 3 => it::assert_equal(group, &[3, 3]), + _ => unreachable!(), + } + } +} + +#[test] +fn group_by_lazy_3() { + // test consuming each group on the lap after it was produced + let data = vec![0, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2]; + let grouper = data.iter().group_by(|elt| *elt); + let mut last = None; + for (key, group) in &grouper { + if let Some(gr) = last.take() { + for elt in gr { + assert!(elt != key && i32::abs(elt - key) == 1); + } + } + last = Some(group); + } +} + +#[test] +fn chunks() { + let data = vec![0, 0, 0, 1, 1, 0, 0, 2, 2, 3, 3]; + let grouper = data.iter().chunks(3); + for (i, chunk) in grouper.into_iter().enumerate() { + match i { + 0 => it::assert_equal(chunk, &[0, 0, 0]), + 1 => it::assert_equal(chunk, &[1, 1, 0]), + 2 => it::assert_equal(chunk, &[0, 2, 2]), + 3 => it::assert_equal(chunk, &[3, 3]), + _ => unreachable!(), + } + } +} + +#[test] +fn concat_empty() { + let data: Vec> = Vec::new(); + assert_eq!(data.into_iter().concat(), Vec::new()) +} + +#[test] +fn concat_non_empty() { + let data = vec![vec![1,2,3], vec![4,5,6], vec![7,8,9]]; + assert_eq!(data.into_iter().concat(), vec![1,2,3,4,5,6,7,8,9]) +} + +#[test] +fn combinations() { + assert!((1..3).combinations(5).next().is_none()); + + let it = (1..3).combinations(2); + it::assert_equal(it, vec![ + vec![1, 2], + ]); + + let it = (1..5).combinations(2); + it::assert_equal(it, vec![ + vec![1, 2], + vec![1, 3], + vec![1, 4], + vec![2, 3], + vec![2, 4], + vec![3, 4], + ]); + + it::assert_equal((0..0).tuple_combinations::<(_, _)>(), >::new()); + it::assert_equal((0..1).tuple_combinations::<(_, _)>(), >::new()); + it::assert_equal((0..2).tuple_combinations::<(_, _)>(), vec![(0, 1)]); + + it::assert_equal((0..0).combinations(2), >>::new()); + it::assert_equal((0..1).combinations(1), vec![vec![0]]); + it::assert_equal((0..2).combinations(1), vec![vec![0], vec![1]]); + it::assert_equal((0..2).combinations(2), vec![vec![0, 1]]); +} + +#[test] +fn combinations_of_too_short() { + for i in 1..10 { + assert!((0..0).combinations(i).next().is_none()); + assert!((0..i - 1).combinations(i).next().is_none()); + } +} + + +#[test] +fn combinations_zero() { + it::assert_equal((1..3).combinations(0), vec![vec![]]); +} + +#[test] +fn diff_mismatch() { + let a = vec![1, 2, 3, 4]; + let b = vec![1.0, 5.0, 3.0, 4.0]; + let b_map = b.into_iter().map(|f| f as i32); + let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b); + + assert!(match diff { + Some(it::Diff::FirstMismatch(1, _, from_diff)) => + from_diff.collect::>() == vec![5, 3, 4], + _ => false, + }); +} + +#[test] +fn diff_longer() { + let a = vec![1, 2, 3, 4]; + let b = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]; + let b_map = b.into_iter().map(|f| f as i32); + let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b); + + assert!(match diff { + Some(it::Diff::Longer(_, remaining)) => + remaining.collect::>() == vec![5, 6], + _ => false, + }); +} + +#[test] +fn diff_shorter() { + let a = vec![1, 2, 3, 4]; + let b = vec![1.0, 2.0]; + let b_map = b.into_iter().map(|f| f as i32); + let diff = it::diff_with(a.iter(), b_map, |a, b| *a == b); + + assert!(match diff { + Some(it::Diff::Shorter(len, _)) => len == 2, + _ => false, + }); +} + +#[test] +fn minmax() { + use std::cmp::Ordering; + use it::MinMaxResult; + + // A peculiar type: Equality compares both tuple items, but ordering only the + // first item. This is so we can check the stability property easily. + #[derive(Clone, Debug, PartialEq, Eq)] + struct Val(u32, u32); + + impl PartialOrd for Val { + fn partial_cmp(&self, other: &Val) -> Option { + self.0.partial_cmp(&other.0) + } + } + + impl Ord for Val { + fn cmp(&self, other: &Val) -> Ordering { + self.0.cmp(&other.0) + } + } + + assert_eq!(None::>.iter().minmax(), MinMaxResult::NoElements); + + assert_eq!(Some(1u32).iter().minmax(), MinMaxResult::OneElement(&1)); + + let data = vec![Val(0, 1), Val(2, 0), Val(0, 2), Val(1, 0), Val(2, 1)]; + + let minmax = data.iter().minmax(); + assert_eq!(minmax, MinMaxResult::MinMax(&Val(0, 1), &Val(2, 1))); + + let (min, max) = data.iter().minmax_by_key(|v| v.1).into_option().unwrap(); + assert_eq!(min, &Val(2, 0)); + assert_eq!(max, &Val(0, 2)); + + let (min, max) = data.iter().minmax_by(|x, y| x.1.cmp(&y.1)).into_option().unwrap(); + assert_eq!(min, &Val(2, 0)); + assert_eq!(max, &Val(0, 2)); +} + +#[test] +fn format() { + let data = [0, 1, 2, 3]; + let ans1 = "0, 1, 2, 3"; + let ans2 = "0--1--2--3"; + + let t1 = format!("{}", data.iter().format(", ")); + assert_eq!(t1, ans1); + let t2 = format!("{:?}", data.iter().format("--")); + assert_eq!(t2, ans2); + + let dataf = [1.1, 2.71828, -22.]; + let t3 = format!("{:.2e}", dataf.iter().format(", ")); + assert_eq!(t3, "1.10e0, 2.72e0, -2.20e1"); +} + +#[test] +fn while_some() { + let ns = (1..10).map(|x| if x % 5 != 0 { Some(x) } else { None }) + .while_some(); + it::assert_equal(ns, vec![1, 2, 3, 4]); +} + +#[allow(deprecated)] +#[test] +fn fold_while() { + let mut iterations = 0; + let vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let sum = vec.into_iter().fold_while(0, |acc, item| { + iterations += 1; + let new_sum = acc.clone() + item; + if new_sum <= 20 { + FoldWhile::Continue(new_sum) + } else { + FoldWhile::Done(acc) + } + }).into_inner(); + assert_eq!(iterations, 6); + assert_eq!(sum, 15); +} + +#[test] +fn tree_fold1() { + let x = [ + "", + "0", + "0 1 x", + "0 1 x 2 x", + "0 1 x 2 3 x x", + "0 1 x 2 3 x x 4 x", + "0 1 x 2 3 x x 4 5 x x", + "0 1 x 2 3 x x 4 5 x 6 x x", + "0 1 x 2 3 x x 4 5 x 6 7 x x x", + "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 x", + "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x x", + "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 x x", + "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x x", + "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 x x", + "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 13 x x x", + "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 13 x 14 x x x", + "0 1 x 2 3 x x 4 5 x 6 7 x x x 8 9 x 10 11 x x 12 13 x 14 15 x x x x", + ]; + for (i, &s) in x.iter().enumerate() { + let expected = if s == "" { None } else { Some(s.to_string()) }; + let num_strings = (0..i).map(|x| x.to_string()); + let actual = num_strings.tree_fold1(|a, b| format!("{} {} x", a, b)); + assert_eq!(actual, expected); + } +} diff --git a/third_party/rust/itertools-0.8.0/tests/tuples.rs b/third_party/rust/itertools-0.8.0/tests/tuples.rs new file mode 100644 index 0000000000000..07dba57fe63d6 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/tests/tuples.rs @@ -0,0 +1,88 @@ +extern crate itertools; + +use itertools::Itertools; + +#[test] +fn tuples() { + let v = [1, 2, 3, 4, 5]; + let mut iter = v.iter().cloned().tuples(); + assert_eq!(Some((1,)), iter.next()); + assert_eq!(Some((2,)), iter.next()); + assert_eq!(Some((3,)), iter.next()); + assert_eq!(Some((4,)), iter.next()); + assert_eq!(Some((5,)), iter.next()); + assert_eq!(None, iter.next()); + assert_eq!(None, iter.into_buffer().next()); + + let mut iter = v.iter().cloned().tuples(); + assert_eq!(Some((1, 2)), iter.next()); + assert_eq!(Some((3, 4)), iter.next()); + assert_eq!(None, iter.next()); + itertools::assert_equal(vec![5], iter.into_buffer()); + + let mut iter = v.iter().cloned().tuples(); + assert_eq!(Some((1, 2, 3)), iter.next()); + assert_eq!(None, iter.next()); + itertools::assert_equal(vec![4, 5], iter.into_buffer()); + + let mut iter = v.iter().cloned().tuples(); + assert_eq!(Some((1, 2, 3, 4)), iter.next()); + assert_eq!(None, iter.next()); + itertools::assert_equal(vec![5], iter.into_buffer()); +} + +#[test] +fn tuple_windows() { + let v = [1, 2, 3, 4, 5]; + + let mut iter = v.iter().cloned().tuple_windows(); + assert_eq!(Some((1,)), iter.next()); + assert_eq!(Some((2,)), iter.next()); + assert_eq!(Some((3,)), iter.next()); + + let mut iter = v.iter().cloned().tuple_windows(); + assert_eq!(Some((1, 2)), iter.next()); + assert_eq!(Some((2, 3)), iter.next()); + assert_eq!(Some((3, 4)), iter.next()); + assert_eq!(Some((4, 5)), iter.next()); + assert_eq!(None, iter.next()); + + let mut iter = v.iter().cloned().tuple_windows(); + assert_eq!(Some((1, 2, 3)), iter.next()); + assert_eq!(Some((2, 3, 4)), iter.next()); + assert_eq!(Some((3, 4, 5)), iter.next()); + assert_eq!(None, iter.next()); + + let mut iter = v.iter().cloned().tuple_windows(); + assert_eq!(Some((1, 2, 3, 4)), iter.next()); + assert_eq!(Some((2, 3, 4, 5)), iter.next()); + assert_eq!(None, iter.next()); + + let v = [1, 2, 3]; + let mut iter = v.iter().cloned().tuple_windows::<(_, _, _, _)>(); + assert_eq!(None, iter.next()); +} + +#[test] +fn next_tuple() { + let v = [1, 2, 3, 4, 5]; + let mut iter = v.iter(); + assert_eq!(iter.next_tuple().map(|(&x, &y)| (x, y)), Some((1, 2))); + assert_eq!(iter.next_tuple().map(|(&x, &y)| (x, y)), Some((3, 4))); + assert_eq!(iter.next_tuple::<(_, _)>(), None); +} + +#[test] +fn collect_tuple() { + let v = [1, 2]; + let iter = v.iter().cloned(); + assert_eq!(iter.collect_tuple(), Some((1, 2))); + + let v = [1]; + let iter = v.iter().cloned(); + assert_eq!(iter.collect_tuple::<(_, _)>(), None); + + let v = [1, 2, 3]; + let iter = v.iter().cloned(); + assert_eq!(iter.collect_tuple::<(_, _)>(), None); +} diff --git a/third_party/rust/itertools-0.8.0/tests/zip.rs b/third_party/rust/itertools-0.8.0/tests/zip.rs new file mode 100644 index 0000000000000..c5c51899b8939 --- /dev/null +++ b/third_party/rust/itertools-0.8.0/tests/zip.rs @@ -0,0 +1,65 @@ +extern crate itertools; + +use itertools::Itertools; +use itertools::EitherOrBoth::{Both, Left, Right}; +use itertools::free::zip_eq; + +#[test] +fn zip_longest_fused() { + let a = [Some(1), None, Some(3), Some(4)]; + let b = [1, 2, 3]; + + let unfused = a.iter().batching(|it| *it.next().unwrap()) + .zip_longest(b.iter().cloned()); + itertools::assert_equal(unfused, + vec![Both(1, 1), Right(2), Right(3)]); +} + +#[test] +fn test_zip_longest_size_hint() { + let c = (1..10).cycle(); + let v: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let v2 = &[10, 11, 12]; + + assert_eq!(c.zip_longest(v.iter()).size_hint(), (std::usize::MAX, None)); + + assert_eq!(v.iter().zip_longest(v2.iter()).size_hint(), (10, Some(10))); +} + +#[test] +fn test_double_ended_zip_longest() { + let xs = [1, 2, 3, 4, 5, 6]; + let ys = [1, 2, 3, 7]; + let a = xs.iter().map(|&x| x); + let b = ys.iter().map(|&x| x); + let mut it = a.zip_longest(b); + assert_eq!(it.next(), Some(Both(1, 1))); + assert_eq!(it.next(), Some(Both(2, 2))); + assert_eq!(it.next_back(), Some(Left(6))); + assert_eq!(it.next_back(), Some(Left(5))); + assert_eq!(it.next_back(), Some(Both(4, 7))); + assert_eq!(it.next(), Some(Both(3, 3))); + assert_eq!(it.next(), None); +} + + +#[should_panic] +#[test] +fn zip_eq_panic1() +{ + let a = [1, 2]; + let b = [1, 2, 3]; + + zip_eq(&a, &b).count(); +} + +#[should_panic] +#[test] +fn zip_eq_panic2() +{ + let a: [i32; 0] = []; + let b = [1, 2, 3]; + + zip_eq(&a, &b).count(); +} + diff --git a/third_party/rust/itertools/.cargo-checksum.json b/third_party/rust/itertools/.cargo-checksum.json index d7eca710e87f9..1c535f98756f9 100644 --- a/third_party/rust/itertools/.cargo-checksum.json +++ b/third_party/rust/itertools/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"a4dc734c8e5ac0e5b57ded98adeb9340621d5c586d196d423781d7ceab06538b","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545","README.rst":"9db27658be49c4d5501c6bb6fbed1dcc1e796c7d934757cf3e94eab14d241d1d","benches/bench1.rs":"9794e5377019e4cf8caee3e7bb30034cf8ac291a485e64407df321f3f33affa0","benches/extra/mod.rs":"4c5b03e74fc5b02383500c9da9fd6550262706ee569d70d085700f6d0b5749ba","benches/extra/zipslices.rs":"108dd488de366b2d83fb6bcc603ecbf9a017e165ac19d03440074fa244af3fb2","benches/tree_fold1.rs":"84cddbabb1a681f3196430a8e27b060103366143a3ee4c42c3b0628fc00a7543","benches/tuple_combinations.rs":"6a4b89eb2e45fa0c99e5e5942b3196b76998213c66c44b3765b869be42016a82","benches/tuples.rs":"412a952f08bb03695952d5cfd57949dcf28be8b99e3c6653994bdb8af9654653","examples/iris.data":"596ffd580471ca4d4880f8e439c7281f3b50d8249a5960353cb200b1490f63a0","examples/iris.rs":"3996ca0a62762aec2b102f0f4244fe90d4b4354286d68d80cdc40e35f4352ba3","src/adaptors/mod.rs":"11ef622421a5980a722a8cd2a732e9379470c1817d8b9c4bcde654c3e417b794","src/adaptors/multi_product.rs":"bb239555be38cde1f419bacfd09728f6ccaf51b6b4811c266b5677705175e685","src/combinations.rs":"a9a3fc78eb5c9f3933ff60275a635d0c81f4864a73515dc052aeb2add4ad4909","src/concat_impl.rs":"276339b00588f54c25f8ffbe0ae3c0031f7e52fb53c6578554a0bde1681b58a5","src/cons_tuples_impl.rs":"87c620d2ffdd3475218f5f493dbef601491be9f6cdfe57c44929449b32e6709f","src/diff.rs":"921e2b867d7b32ffedc72a5eb780811322d14d1e0883a608b9028a2afcad0df2","src/either_or_both.rs":"8ea6db74fa21535de45cb48ee7134495e8822a0fa181fdb646e081251ac9459f","src/format.rs":"412fbe02f12311c6fbcec1044f57ad6991783f5a3f323b9c391accfe4915106f","src/free.rs":"473d19906720eb2a1309c0505497658b1426f3ea0c845b40f41f3154194fff18","src/group_map.rs":"872d6e243e649ad30c94973c034596cc3377b10018e361bca07e11c612006de6","src/groupbylazy.rs":"a067a12671be9ae05a9152518103f39f7286fde09f758de8af75a1064a3b5567","src/impl_macros.rs":"eb0bb3f70ec1bcaffa6110ae4134c777951ed1e5f48d8c811dbf0a597dc48faa","src/intersperse.rs":"9c18f239654ebfcce1d68a0256d2df6d79a2b8c4fb5df87d67e2ebe04a07e1a9","src/kmerge_impl.rs":"51e71d3e76670a8efb16597a9224d2c9b40cee6bc6270d06b66aadafea6dc26b","src/lib.rs":"edac21e8bff7bc7695480022a33ded58916eb1c5ed43f5aa68a7c965b452af0b","src/merge_join.rs":"98e6fcc761a558ad21789efe041c3f90e62f6c75e05840670df45ad4f9b07e1f","src/minmax.rs":"4668a7f824fbc133599f43ffb6f7283e5bd603e07df2d8176abc6f25d6af9db0","src/multipeek_impl.rs":"ebe9544d94d0bf7200f7625241a3b5a291b7b564091a08cad40ff08b51f1b1bf","src/pad_tail.rs":"078615a2892f8c6db665074cf6f1be1bef4cf5ee418bc174edcfd4dc703e163f","src/peeking_take_while.rs":"6aea3bb40fb480e9f3695ce2a7a3a2e2346d437ca846d20e6bb3c09beb0934f4","src/process_results_impl.rs":"5f454cf62ceb82cab7c08c0c190de3ae083e219a8acc7a1a22f17eec9cfcd65c","src/put_back_n_impl.rs":"d35858184c525372b22d14d42cdf63726cf0fd50f5bd42ec7a82d55a8e180e9f","src/rciter_impl.rs":"8f51abc7e1ae3320cc5d56fadd66f880a7a06773be656bd8c4712357f01ae1d9","src/repeatn.rs":"4bd1782364b16105fbef3f3de7bf62780710e5c996db44a00e4b5f16c2625d86","src/size_hint.rs":"c1d35b422a696cf3d63e7c90d8f9fdf01a304cf8156e914287c4ef48fea62dd3","src/sources.rs":"cb6ebe06b05f7da0ac1073ba486c45afaeebf8b558c0f5499f0eacd2cd1785da","src/tee.rs":"59cf9ef0b41882307ea1e3503a2ff351f401f4c43d95acf423a990b0bf0e29ae","src/tuple_impl.rs":"0c7f907e85d2ef0661583b36c7b8a7341b8feadafe28d10539a211dff5c028ea","src/unique_impl.rs":"63db2d720ff5e3d9c0d6c2b245ffff25d4040e4fcbcb2a6524b0f912826f86af","src/with_position.rs":"d922f045f6fa090a431be928f3221c6dc37ac6f9bb54461b3b84f99a7e91244a","src/zip_eq_impl.rs":"f857c69120255db16ad6ddec628c79cb573b1d5179fcebab1906bf5b762c02e3","src/zip_longest.rs":"375325ef069970e6fb83c6097c2824877bb0f06e4f1e664e4fe681804abe003c","src/ziptuple.rs":"d7ae7d3c33185ad74ab2bba750ac337b5c236750cc8341dd9883faf6465712a1","tests/merge_join.rs":"546eaffae40010f15a7bcf95bc53f5e9b67424c5b93df6ffb0aaa1e48e8b90c0","tests/peeking_take_while.rs":"a2ae6474e09620a47bb8a6e3c62929261e72c52881370adb2d22e89aa9e9aec8","tests/quick.rs":"290d52047c66b5c493ff425cc527a476c55a150bc859709a8a8559f3f0a74274","tests/test_core.rs":"1a1c4c2dbb657e9ce5cd2bb0d6475cf965878e2a259b730ada9238c71c2a8d4f","tests/test_std.rs":"90d2794cbee64b3a5a1cb53193387e2d83d2b9bdf588edb506a7bf3c5191d2ec","tests/tuples.rs":"5323d15a7abf6545b2655167d3206b6cf6a947e9409a244ea6a8cf4ad8ceac64","tests/zip.rs":"fe213d70c4fa114cb4d1930a6b971f4af617a239041ddb87e6b5a9bbe62261b8"},"package":"5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"} \ No newline at end of file +{"files":{"CHANGELOG.md":"31d5b840f465cdc6697fddf4b2227969f68d6948c374c4ceadc8eff2b8857f0c","Cargo.lock":"eebdb4d7cd2d0744fb7c469cd43d80f39ea90c5319f6aa23cab94fd2e6c7271b","Cargo.toml":"48b6a4509d3716af7f114d30abe6a29d8430c23d00d51a0cd526db2c7046c1ca","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545","README.rst":"e6bb15fcba6c23020d1a0a13c84d290188a897cf4a9ae5ab37176032371ba08f","benches/bench1.rs":"bb06f39db0544b1380cd4929139ccf521a9eecab7ca3f910b9499f965ec0a047","benches/combinations_with_replacement.rs":"11f29160652a2d90ce7ca4b1c339c4457888ab6867e2456ce1c62e3adf9be737","benches/extra/mod.rs":"6ca290d72302a1945078621610b5788060b0de29639decebbdc557a80044aa97","benches/extra/zipslices.rs":"3db9764c21536c541cdf3f93ba2e34f6ab63dd12a9149aa5bd8a4524778176f1","benches/fold_specialization.rs":"9f5b137afc033e1e00609bb353ab79764a3515c0d129a3ba251368e00db61cd5","benches/tree_fold1.rs":"539232e74f9aaea295a42069ac5af707811e90dc1c71c6e0a9064ffc731999de","benches/tuple_combinations.rs":"20317c18e9ae42d50b341880c71081ab1aaa5b281aa74ce14707b291ba87d7b0","benches/tuples.rs":"5a620783ae203e9ff9623d10d2c7fe9911d8b6c811cbad7613afa30e390c759d","examples/iris.data":"596ffd580471ca4d4880f8e439c7281f3b50d8249a5960353cb200b1490f63a0","examples/iris.rs":"15483be8a439e1c6c32d96b84dd30b6b2fadb584cae09a057a0cacda8180416a","src/adaptors/mod.rs":"fdbd9452de37639e1b48f4dfad84f35a3f162a05b0570f3e7dc7774d05716761","src/adaptors/multi_product.rs":"0acbb29eba50a057fb83408ae83eb275b1c4f4ad41fa82be815ca37274ef7c2a","src/combinations.rs":"f40f6d332ada63b001bf17cded0ee99d2e29eef6a3211586409720e95c58e468","src/combinations_with_replacement.rs":"3fe1d2f40d4d047f3f05def2b9eb3acf46f5fe1cf66349551969b4963df56696","src/concat_impl.rs":"450d5640ba7e968ff8f2855b9cf60fca82f06f62590dc2348b9ef8bfa4224231","src/cons_tuples_impl.rs":"4c2167fd1f840c50b08a8e8fbcc9d4c318469c4065eaa92835fdf1c7c60b416c","src/diff.rs":"bb90542a685a76b58ee367e88c66c436fa3306c69a25ce9217f7886919c37868","src/either_or_both.rs":"a3fe2e651660e7d8f05367f5c07e91ce60bf42ea32a89c7b3e1b8f0ee01651bf","src/exactly_one_err.rs":"423c089bd1954c0ab721195df633e04d25e39c612fbbcec6bca5df5c3a0c9411","src/format.rs":"8f74682be045c48d59671af2077687b8ada63b05230b0d0c48a08266b4ab75c1","src/free.rs":"5e7a8b363aaa639fe2895668a45d577a06072ce4785c6639385152bf4c423db2","src/group_map.rs":"872d6e243e649ad30c94973c034596cc3377b10018e361bca07e11c612006de6","src/groupbylazy.rs":"404f44d3920e36f92274fd53229cecdb95da94ed3886d329f085b9a9828f0ae5","src/impl_macros.rs":"18d55fe8cf59e0088cc09115601688aa54ade3148069bb6ebf88305eafbaeac7","src/intersperse.rs":"78000b821190b6d5e6f0fac75cf13f20ea35d9261952562f1a27c6b50a390d5f","src/kmerge_impl.rs":"fa5c5ddc916273cb30f88cc8deb17c9ed44346eb658029cecc39fc779caa630d","src/lazy_buffer.rs":"7e479db2b2df8e64a63108ade2d7861116afc13e26a504660b54bd5ead97895b","src/lib.rs":"100fb67af1fb70a168bb37186c550cb10afa810582ffb6a1b1bca10139141d2b","src/merge_join.rs":"928c4ade1adf921b41f8eea50e113e113ed9036d336a97957b4cbb3787748a39","src/minmax.rs":"4668a7f824fbc133599f43ffb6f7283e5bd603e07df2d8176abc6f25d6af9db0","src/multipeek_impl.rs":"fc8a177fd96e1f0dd6a1a13f3c0625cbe0e6ca914af25e46cec86ea5434ef7f3","src/pad_tail.rs":"8de020e05dd959ae19cc33f8c57e8d50378964c61136dd0ab95182f9a979a21d","src/peeking_take_while.rs":"a15ed841d217145e5ab2d13612bff7b7de5fb1274528af891574b84177133188","src/permutations.rs":"7322443db3ca5c52edcfd9564ba45c544ba3ae6dcbabd44f0c5d2f91c1745c77","src/process_results_impl.rs":"cd85c14725cd82f6b6b0b9fa97c25d383295094ac4154b929706f97af495fa51","src/put_back_n_impl.rs":"fa21109d180973821f41d3f7e4d29b377b3d4910d4d5ca1cab3d7a0085c33fd7","src/rciter_impl.rs":"fb0a13ea76531f9e549d758a78b4a7a19a1d9a9ceeb73946f5f3832895e545bd","src/repeatn.rs":"7fc91a65cd05bb2e8776edcacbe6407eb8a329a59918e72f0c83592db429a881","src/size_hint.rs":"c1d35b422a696cf3d63e7c90d8f9fdf01a304cf8156e914287c4ef48fea62dd3","src/sources.rs":"7667bf1d40f0fe47400d01438dc55b6e33bb339e93c2adade82900eea061cd26","src/tee.rs":"c50561be52f24ef1b76c23032acbe476823a83c47be4084498cee72cdd9cc690","src/tuple_impl.rs":"00b8f2d765a82b5427041831576259b7a8d7d67b653cf33bb64b871e29fa96e1","src/unique_impl.rs":"58f61e031f7f7aadb2fd7ea7e660f43ca2f5ec00dec79c3231a6206d35ce7815","src/with_position.rs":"dc3e619f637e533d15919d26b641f49f3e62e7b8eed0a8a4aa8f058f9c4581be","src/zip_eq_impl.rs":"f857c69120255db16ad6ddec628c79cb573b1d5179fcebab1906bf5b762c02e3","src/zip_longest.rs":"c785582f5b56df4c8503cbe9e527d0bef264f97df75c013964fa634b5ada8103","src/ziptuple.rs":"d7ae7d3c33185ad74ab2bba750ac337b5c236750cc8341dd9883faf6465712a1","tests/adaptors_no_collect.rs":"dc69691bed895e6fd3922942ccc7d424a5247b0f79dea74cf8e2f8a8af0bd491","tests/fold_specialization.rs":"fc86706ccbbc5c1dd43e0eaff046352e7f3cbcc2ee569ab1fc910db643cba37b","tests/merge_join.rs":"b08c4ee6529d234c68d411a413b8781455d18a1eab17872d1828bb75a4fcf79b","tests/peeking_take_while.rs":"4b1c394e44a9ef9bc0de707ae080b45803db722f79834c20f15b826d7c3f1f2e","tests/quick.rs":"ed0dbcd28692d4672f88d5d3517e7fdf257a8accae350d859f594529b837ece3","tests/specializations.rs":"e073e3617419b6518783b5ccd4e3fc0a9a471156dcc5fa77da481b4d3bb9a3e5","tests/test_core.rs":"c5c6546539649e319ca8a44e033f2c762468628d5ce4170790d0ba01c2e0e2e6","tests/test_std.rs":"944901d44b0e1ac02bfd9a84d6b11657abf3aa3423a5317512c6b28b22c3b262","tests/tuples.rs":"014e4da776174bfe923270e2a359cd9c95b372fce4b952b8138909d6e2c52762","tests/zip.rs":"09149f5d9bd2f586c346ef442d07f9240c7efe0b8437fb5c1fc1014be516c275"},"package":"284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"} \ No newline at end of file diff --git a/third_party/rust/itertools/CHANGELOG.md b/third_party/rust/itertools/CHANGELOG.md new file mode 100644 index 0000000000000..67633ab75214e --- /dev/null +++ b/third_party/rust/itertools/CHANGELOG.md @@ -0,0 +1,319 @@ +# Changelog + +## 0.9.0 + - Fix potential overflow in `MergeJoinBy::size_hint` (#385) + - Add `derive(Clone)` where possible (#382) + - Add `try_collect` method (#394) + - Add `HomogeneousTuple` trait (#389) + - Fix `combinations(0)` and `combinations_with_replacement(0)` (#383) + - Don't require `ParitalEq` to the `Item` of `DedupBy` (#397) + - Implement missing specializations on the `PutBack` adaptor and on the `MergeJoinBy` iterator (#372) + - Add `position_*` methods (#412) + - Derive `Hash` for `EitherOrBoth` (#417) + - Increase minimum supported Rust version to 1.32.0 + +## 0.8.2 + - Use `slice::iter` instead of `into_iter` to avoid future breakage (#378, by @LukasKalbertodt) +## 0.8.1 + - Added a [`.exactly_one()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.exactly_one) iterator method that, on success, extracts the single value of an iterator ; by @Xaeroxe + - Added combinatory iterator adaptors: + - [`.permutations(k)`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.permutations): + + `[0, 1, 2].iter().permutations(2)` yields + + ```rust + [ + vec![0, 1], + vec![0, 2], + vec![1, 0], + vec![1, 2], + vec![2, 0], + vec![2, 1], + ] + ``` + + ; by @tobz1000 + + - [`.combinations_with_replacement(k)`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.combinations_with_replacement): + + `[0, 1, 2].iter().combinations_with_replacement(2)` yields + + ```rust + [ + vec![0, 0], + vec![0, 1], + vec![0, 2], + vec![1, 1], + vec![1, 2], + vec![2, 2], + ] + ``` + + ; by @tommilligan + + - For reference, these methods join the already existing [`.combinations(k)`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.combinations): + + `[0, 1, 2].iter().combinations(2)` yields + + ```rust + [ + vec![0, 1], + vec![0, 2], + vec![1, 2], + ] + ``` + + - Improved the performance of [`.fold()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.fold)-based internal iteration for the [`.intersperse()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.intersperse) iterator ; by @jswrenn + - Added [`.dedup_by()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.dedup_by), [`.merge_by()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.merge_by) and [`.kmerge_by()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.kmerge_by) adaptors that work like [`.dedup()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.dedup), [`.merge()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.merge) and [`.kmerge()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.kmerge), but taking an additional custom comparison closure parameter. ; by @phimuemue + - Improved the performance of [`.all_equal()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.all_equal) ; by @fyrchik + - Loosened the bounds on [`.partition_map()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.partition_map) to take just a `FnMut` closure rather than a `Fn` closure, and made its implementation use internal iteration for better performance ; by @danielhenrymantilla + - Added convenience methods to [`EitherOrBoth`](https://docs.rs/itertools/0.8.1/itertools/enum.EitherOrBoth.html) elements yielded from the [`.zip_longest()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.zip_longest) iterator adaptor ; by @Avi-D-coder + - Added [`.sum1()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.sum1) and [`.product1()`](https://docs.rs/itertools/0.8.1/itertools/trait.Itertools.html#method.product1) iterator methods that respectively try to return the sum and the product of the elements of an iterator **when it is not empty**, otherwise they return `None` ; by @Emerentius +## 0.8.0 + - Added new adaptor `.map_into()` for conversions using `Into` by @vorner + - Improved `Itertools` docs by @JohnHeitmann + - The return type of `.sorted_by_by_key()` is now an iterator, not a Vec. + - The return type of the `izip!(x, y)` macro with exactly two arguments is now the usual `Iterator::zip`. + - Remove `.flatten()` in favour of std's `.flatten()` + - Deprecate `.foreach()` in favour of std's `.for_each()` + - Deprecate `.step()` in favour of std's `.step_by()` + - Deprecate `repeat_call` in favour of std's `repeat_with` + - Deprecate `.fold_while()` in favour of std's `.try_fold()` + - Require Rust 1.24 as minimal version. +## 0.7.11 + - Add convenience methods to `EitherOrBoth`, making it more similar to `Option` and `Either` by @jethrogb +## 0.7.10 + - No changes. +## 0.7.9 + - New inclusion policy: See the readme about suggesting features for std before accepting them in itertools. + - The `FoldWhile` type now implements `Eq` and `PartialEq` by @jturner314 +## 0.7.8 + - Add new iterator method `.tree_fold1()` which is like `.fold1()` except items are combined in a tree structure (see its docs). By @scottmcm + - Add more `Debug` impls by @phimuemue: KMerge, KMergeBy, MergeJoinBy, ConsTuples, Intersperse, ProcessResults, RcIter, Tee, TupleWindows, Tee, ZipLongest, ZipEq, Zip. +## 0.7.7 + - Add new iterator method `.into_group_map() -> HashMap>` which turns an iterator of `(K, V)` elements into such a hash table, where values are grouped by key. By @tobz1000 + - Add new free function `flatten` for the `.flatten()` adaptor. **NOTE:** recent Rust nightlies have `Iterator::flatten` and thus a clash with our flatten adaptor. One workaround is to use the itertools `flatten` free function. +## 0.7.6 + - Add new adaptor `.multi_cartesian_product()` which is an n-ary product iterator by @tobz1000 + - Add new method `.sorted_by_key()` by @Xion + - Provide simpler and faster `.count()` for `.unique()` and `.unique_by()` +## 0.7.5 + - `.multipeek()` now implements `PeekingNext`, by @nicopap. +## 0.7.4 + - Add new adaptor `.update()` by @lucasem; this adaptor is used to modify an element before passing it on in an iterator chain. +## 0.7.3 + - Add new method `.collect_tuple()` by @matklad; it makes a tuple out of the iterator's elements if the number of them matches **exactly**. + - Implement `fold` and `collect` for `.map_results()` which means it reuses the code of the standard `.map()` for these methods. +## 0.7.2 + - Add new adaptor `.merge_join_by` by @srijs; a heterogeneous merge join for two ordered sequences. +## 0.7.1 + - Iterator adaptors and iterators in itertools now use the same `must_use` reminder that the standard library adaptors do, by @matematikaedit and @bluss *“iterator adaptors are lazy and do nothing unless consumed”*. +## 0.7.0 + - Faster `izip!()` by @krdln + - `izip!()` is now a wrapper for repeated regular `.zip()` and a single `.map()`. This means it optimizes as well as the standard library `.zip()` it uses. **Note:** `multizip` and `izip!()` are now different! The former has a named type but the latter optimizes better. + - Faster `.unique()` + - `no_std` support, which is opt-in! + - Many lovable features are still there without std, like `izip!()` or `.format()` or `.merge()`, but not those that use collections. + - Trait bounds were required up front instead of just on the type: `group_by`'s `PartialEq` by @Phlosioneer and `repeat_call`'s `FnMut`. + - Removed deprecated constructor `Zip::new` — use `izip!()` or `multizip()` +## 0.6.5 + - Fix bug in `.cartesian_product()`'s fold (which only was visible for unfused iterators). +## 0.6.4 + - Add specific `fold` implementations for `.cartesian_product()` and `cons_tuples()`, which improves their performance in fold, foreach, and iterator consumers derived from them. +## 0.6.3 + - Add iterator adaptor `.positions(predicate)` by @tmccombs +## 0.6.2 + - Add function `process_results` which can “lift” a function of the regular values of an iterator so that it can process the `Ok` values from an iterator of `Results` instead, by @shepmaster + - Add iterator method `.concat()` which combines all iterator elements into a single collection using the `Extend` trait, by @srijs +## 0.6.1 + - Better size hint testing and subsequent size hint bugfixes by @rkarp. Fixes bugs in product, `interleave_shortest` size hints. + - New iterator method `.all_equal()` by @phimuemue +## 0.6.0 + - Deprecated names were removed in favour of their replacements + - `.flatten()` does not implement double ended iteration anymore + - `.fold_while()` uses `&mut self` and returns `FoldWhile`, for composability #168 + - `.foreach()` and `.fold1()` use `self`, like `.fold()` does. + - `.combinations(0)` now produces a single empty vector. #174 +## 0.5.10 + - Add itertools method `.kmerge_by()` (and corresponding free function) + - Relaxed trait requirement of `.kmerge()` and `.minmax()` to PartialOrd. +## 0.5.9 + - Add multipeek method `.reset_peek()` + - Add categories +## 0.5.8 + - Add iterator adaptor `.peeking_take_while()` and its trait `PeekingNext`. +## 0.5.7 + - Add iterator adaptor `.with_position()` + - Fix multipeek's performance for long peeks by using `VecDeque`. +## 0.5.6 + - Add `.map_results()` +## 0.5.5 + - Many more adaptors now implement `Debug` + - Add free function constructor `repeat_n`. `RepeatN::new` is now deprecated. +## 0.5.4 + - Add infinite generator function `iterate`, that takes a seed and a closure. +## 0.5.3 + - Special-cased `.fold()` for flatten and put back. `.foreach()` now uses fold on the iterator, to pick up any iterator specific loop implementation. + - `.combinations(n)` asserts up front that `n != 0`, instead of running into an error on the second iterator element. +## 0.5.2 + - Add `.tuples::()` that iterates by two, three or four elements at a time (where `T` is a tuple type). + - Add `.tuple_windows::()` that iterates using a window of the two, three or four most recent elements. + - Add `.next_tuple::()` method, that picks the next two, three or four elements in one go. + - `.interleave()` now has an accurate size hint. +## 0.5.1 + - Workaround module/function name clash that made racer crash on completing itertools. Only internal changes needed. +## 0.5.0 + - [Release announcement](http://bluss.github.io/rust/2016/09/26/itertools-0.5.0/) + - Renamed: + - `combinations` is now `tuple_combinations` + - `combinations_n` to `combinations` + - `group_by_lazy`, `chunks_lazy` to `group_by`, `chunks` + - `Unfold::new` to `unfold()` + - `RepeatCall::new` to `repeat_call()` + - `Zip::new` to `multizip` + - `PutBack::new`, `PutBackN::new` to `put_back`, `put_back_n` + - `PutBack::with_value` is now a builder setter, not a constructor + - `MultiPeek::new`, `.multipeek()` to `multipeek()` + - `format` to `format_with` and `format_default` to `format` + - `.into_rc()` to `rciter` + - `Partition` enum is now `Either` + - Module reorganization: + - All iterator structs are under `itertools::structs` but also reexported to the top level, for backwards compatibility + - All free functions are reexported at the root, `itertools::free` will be removed in the next version + - Removed: + - `ZipSlices`, use `.zip()` instead + - `.enumerate_from()`, `ZipTrusted`, due to being unstable + - `.mend_slices()`, moved to crate `odds` + - Stride, StrideMut, moved to crate `odds` + - `linspace()`, moved to crate `itertools-num` + - `.sort_by()`, use `.sorted_by()` + - `.is_empty_hint()`, use `.size_hint()` + - `.dropn()`, use `.dropping()` + - `.map_fn()`, use `.map()` + - `.slice()`, use `.take()` / `.skip()` + - helper traits in `misc` + - `new` constructors on iterator structs, use `Itertools` trait or free functions instead + - `itertools::size_hint` is now private + - Behaviour changes: + - `format` and `format_with` helpers now panic if you try to format them more than once. + - `repeat_call` is not double ended anymore + - New features: + - tuple flattening iterator is constructible with `cons_tuples` + - itertools reexports `Either` from the `either` crate. `Either` is an iterator when `L, R` are. + - `MinMaxResult` now implements `Copy` and `Clone` + - `tuple_combinations` supports 1-4 tuples of combinations (previously just 2) +## 0.4.19 + - Add `.minmax_by()` + - Add `itertools::free::cloned` + - Add `itertools::free::rciter` + - Improve `.step(n)` slightly to take advantage of specialized Fuse better. +## 0.4.18 + - Only changes related to the "unstable" crate feature. This feature is more or less deprecated. + - Use deprecated warnings when unstable is enabled. `.enumerate_from()` will be removed imminently since it's using a deprecated libstd trait. +## 0.4.17 + - Fix bug in `.kmerge()` that caused it to often produce the wrong order #134 +## 0.4.16 + - Improve precision of the `interleave_shortest` adaptor's size hint (it is now computed exactly when possible). +## 0.4.15 + - Fixup on top of the workaround in 0.4.14. A function in `itertools::free` was removed by mistake and now it is added back again. +## 0.4.14 + - Workaround an upstream regression in a rust nightly build that broke compilation of of `itertools::free::{interleave, merge}` +## 0.4.13 + - Add `.minmax()` and `.minmax_by_key()`, iterator methods for finding both minimum and maximum in one scan. + - Add `.format_default()`, a simpler version of `.format()` (lazy formatting for iterators). +## 0.4.12 + - Add `.zip_eq()`, an adaptor like `.zip()` except it ensures iterators of inequal length don't pass silently (instead it panics). + - Add `.fold_while()`, an iterator method that is a fold that can short-circuit. + - Add `.partition_map()`, an iterator method that can separate elements into two collections. +## 0.4.11 + - Add `.get()` for `Stride{,Mut}` and `.get_mut()` for `StrideMut` +## 0.4.10 + - Improve performance of `.kmerge()` +## 0.4.9 + - Add k-ary merge adaptor `.kmerge()` + - Fix a bug in `.islice()` with ranges `a..b` where a `> b`. +## 0.4.8 + - Implement `Clone`, `Debug` for `Linspace` +## 0.4.7 + - Add function `diff_with()` that compares two iterators + - Add `.combinations_n()`, an n-ary combinations iterator + - Add methods `PutBack::with_value` and `PutBack::into_parts`. +## 0.4.6 + - Add method `.sorted()` + - Add module `itertools::free` with free function variants of common iterator adaptors and methods. For example `enumerate(iterable)`, `rev(iterable)`, and so on. +## 0.4.5 + - Add `.flatten()` +## 0.4.4 + - Allow composing `ZipSlices` with itself +## 0.4.3 + - Write `iproduct!()` as a single expression; this allows temporary values in its arguments. +## 0.4.2 + - Add `.fold_options()` + - Require Rust 1.1 or later +## 0.4.1 + - Update `.dropping()` to take advantage of `.nth()` +## 0.4.0 + - `.merge()`, `.unique()` and `.dedup()` now perform better due to not using function pointers + - Add free functions `enumerate()` and `rev()` + - Breaking changes: + - Return types of `.merge()` and `.merge_by()` renamed and changed + - Method `Merge::new` removed + - `.merge_by()` now takes a closure that returns bool. + - Return type of `.dedup()` changed + - Return type of `.mend_slices()` changed + - Return type of `.unique()` changed + - Removed function `times()`, struct `Times`: use a range instead + - Removed deprecated macro `icompr!()` + - Removed deprecated `FnMap` and method `.fn_map()`: use `.map_fn()` + - `.interleave_shortest()` is no longer guaranteed to act like fused +## 0.3.25 + - Rename `.sort_by()` to `.sorted_by()`. Old name is deprecated. + - Fix well-formedness warnings from RFC 1214, no user visible impact +## 0.3.24 + - Improve performance of `.merge()`'s ordering function slightly +## 0.3.23 + - Added `.chunks()`, similar to (and based on) `.group_by_lazy()`. + - Tweak linspace to match numpy.linspace and make it double ended. +## 0.3.22 + - Added `ZipSlices`, a fast zip for slices +## 0.3.21 + - Remove `Debug` impl for `Format`, it will have different use later +## 0.3.20 + - Optimize `.group_by_lazy()` +## 0.3.19 + - Added `.group_by_lazy()`, a possibly nonallocating group by + - Added `.format()`, a nonallocating formatting helper for iterators + - Remove uses of `RandomAccessIterator` since it has been deprecated in rust. +## 0.3.17 + - Added (adopted) `Unfold` from rust +## 0.3.16 + - Added adaptors `.unique()`, `.unique_by()` +## 0.3.15 + - Added method `.sort_by()` +## 0.3.14 + - Added adaptor `.while_some()` +## 0.3.13 + - Added adaptor `.interleave_shortest()` + - Added adaptor `.pad_using()` +## 0.3.11 + - Added `assert_equal` function +## 0.3.10 + - Bugfix `.combinations()` `size_hint`. +## 0.3.8 + - Added source `RepeatCall` +## 0.3.7 + - Added adaptor `PutBackN` + - Added adaptor `.combinations()` +## 0.3.6 + - Added `itertools::partition`, partition a sequence in place based on a predicate. + - Deprecate `icompr!()` with no replacement. +## 0.3.5 + - `.map_fn()` replaces deprecated `.fn_map()`. +## 0.3.4 + - `.take_while_ref()` *by-ref adaptor* + - `.coalesce()` *adaptor* + - `.mend_slices()` *adaptor* +## 0.3.3 + - `.dropping_back()` *method* + - `.fold1()` *method* + - `.is_empty_hint()` *method* diff --git a/third_party/rust/itertools/Cargo.lock b/third_party/rust/itertools/Cargo.lock new file mode 100644 index 0000000000000..917f2124a6019 --- /dev/null +++ b/third_party/rust/itertools/Cargo.lock @@ -0,0 +1,584 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bstr" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "c2-chacha" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cast" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "clap" +version = "2.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "criterion" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "criterion-plot 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "csv 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_xoshiro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", + "tinytemplate 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "criterion-plot" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-deque" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-queue" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "csv" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bstr 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "csv-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "either" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hermit-abi" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itertools" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itertools" +version = "0.9.0" +dependencies = [ + "criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "permutohedron 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itoa" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.66" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memoffset" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num_cpus" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "permutohedron" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quickcheck" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_os" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_xoshiro" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rayon" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rayon-core" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-automata" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ryu" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scopeguard" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_derive" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tinytemplate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "walkdir" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +"checksum bstr 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "502ae1441a0a5adb8fbd38a5955a6416b9493e92b465de5e4a9bde6a539c2c48" +"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" +"checksum cast 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +"checksum criterion 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "938703e165481c8d612ea3479ac8342e5615185db37765162e762ec3523e2fc6" +"checksum criterion-plot 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a01e15e0ea58e8234f96146b1f91fa9d0e4dd7a38da93ff7a75d42c0b9d3a545" +"checksum crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca" +"checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" +"checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" +"checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" +"checksum csv 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "00affe7f6ab566df61b4be3ce8cf16bc2576bca0963ceb0955e45d514bf9a279" +"checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c" +"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +"checksum hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772" +"checksum itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" +"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +"checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" +"checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" +"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" +"checksum permutohedron 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b687ff7b5da449d39e418ad391e5e08da53ec334903ddbb921db208908fc372c" +"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +"checksum proc-macro2 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" +"checksum quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" +"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +"checksum rand_os 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a788ae3edb696cfcba1c19bfd388cc4b8c21f8a408432b199c072825084da58a" +"checksum rand_xoshiro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e18c91676f670f6f0312764c759405f13afb98d5d73819840cf72a518487bff" +"checksum rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" +"checksum rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" +"checksum regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" +"checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" +"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" +"checksum serde_json 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)" = "15913895b61e0be854afd32fd4163fcd2a3df34142cf2cb961b310ce694cbf90" +"checksum syn 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" +"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +"checksum tinytemplate 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "57a3c6667d3e65eb1bc3aed6fd14011c6cbc3a0665218ab7f5daf040b9ec371a" +"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/third_party/rust/itertools/Cargo.toml b/third_party/rust/itertools/Cargo.toml index 9141434fe461c..d8a9b8cd3b909 100644 --- a/third_party/rust/itertools/Cargo.toml +++ b/third_party/rust/itertools/Cargo.toml @@ -3,7 +3,7 @@ # 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 +# 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 @@ -11,8 +11,9 @@ # will likely look very different (and much more reasonable) [package] +edition = "2018" name = "itertools" -version = "0.8.0" +version = "0.9.0" authors = ["bluss"] exclude = ["/bors.toml"] description = "Extra iterator adaptors, iterator methods, free functions, and macros." @@ -29,18 +30,45 @@ debug = true [lib] test = false bench = false + +[[bench]] +name = "tuple_combinations" +harness = false + +[[bench]] +name = "tuples" +harness = false + +[[bench]] +name = "fold_specialization" +harness = false + +[[bench]] +name = "combinations_with_replacement" +harness = false + +[[bench]] +name = "tree_fold1" +harness = false + +[[bench]] +name = "bench1" +harness = false [dependencies.either] version = "1.0" default-features = false +[dev-dependencies.criterion] +version = "=0.3.0" + [dev-dependencies.permutohedron] version = "0.2" [dev-dependencies.quickcheck] -version = "0.7" +version = "0.9" default-features = false [dev-dependencies.rand] -version = "0.6" +version = "0.7" [features] default = ["use_std"] diff --git a/third_party/rust/itertools/README.rst b/third_party/rust/itertools/README.rst index b0ddda3c816e6..24c99d3e7421a 100644 --- a/third_party/rust/itertools/README.rst +++ b/third_party/rust/itertools/README.rst @@ -10,8 +10,8 @@ __ https://docs.rs/itertools/ |build_status|_ |crates|_ -.. |build_status| image:: https://travis-ci.org/bluss/rust-itertools.svg?branch=master -.. _build_status: https://travis-ci.org/bluss/rust-itertools +.. |build_status| image:: https://travis-ci.org/rust-itertools/itertools.svg?branch=master +.. _build_status: https://travis-ci.org/rust-itertools/itertools .. |crates| image:: http://meritbadge.herokuapp.com/itertools .. _crates: https://crates.io/crates/itertools @@ -27,8 +27,6 @@ How to use in your crate: .. code:: rust - #[macro_use] extern crate itertools; - use itertools::Itertools; How to contribute @@ -45,475 +43,6 @@ The reason for doing is this is so that we avoid future breakage as with ``.flat However, if your feature involves heap allocation, such as storing elements in a ``Vec``, then it can't be accepted into ``libcore``, and you should propose it for ``itertools`` directly instead. -Recent Changes --------------- - -- 0.8.0 - - - Added new adaptor ``.map_into()`` for conversions using ``Into`` by @vorner - - Improved ``Itertools`` docs by @JohnHeitmann - - The return type of ``.sorted/_by/_by_key()`` is now an iterator, not a Vec. - - The return type of the ``izip!(x, y)`` macro with exactly two arguments - is now the usual ``Iterator::zip``. - - Remove ``.flatten()`` in favour of std's ``.flatten()`` - - Deprecate ``.foreach()`` in favour of std's ``.for_each()`` - - Deprecate ``.step()`` in favour of std's ``.step_by()`` - - Deprecate ``repeat_call`` in favour of std's ``repeat_with`` - - Deprecate ``.fold_while()`` in favour of std's ``.try_fold()`` - - Require Rust 1.24 as minimal version. - -- 0.7.11 - - - Add convenience methods to ``EitherOrBoth``, making it more similar to ``Option`` - and ``Either`` by @jethrogb - -- 0.7.10 - - - No changes. - -- 0.7.9 - - - New inclusion policy: See the readme about suggesting features for std before - accepting them in itertools. - - The ``FoldWhile`` type now implements ``Eq`` and ``PartialEq`` by @jturner314 - -- 0.7.8 - - - Add new iterator method ``.tree_fold1()`` which is like ``.fold1()`` - except items are combined in a tree structure (see its docs). - By @scottmcm - - Add more ``Debug`` impls by @phimuemue: KMerge, KMergeBy, MergeJoinBy, - ConsTuples, Intersperse, ProcessResults, RcIter, Tee, TupleWindows, Tee, - ZipLongest, ZipEq, Zip. - -- 0.7.7 - - - Add new iterator method ``.into_group_map() -> HashMap>`` - which turns an iterator of ``(K, V)`` elements into such a hash table, - where values are grouped by key. By @tobz1000 - - Add new free function ``flatten`` for the ``.flatten()`` adaptor. - **NOTE:** recent Rust nightlies have ``Iterator::flatten`` and thus a clash - with our flatten adaptor. One workaround is to use the itertools ``flatten`` - free function. - -- 0.7.6 - - - Add new adaptor ``.multi_cartesian_product()`` which is an n-ary product - iterator by @tobz1000 - - Add new method ``.sorted_by_key()`` by @Xion - - Provide simpler and faster ``.count()`` for ``.unique()`` and ``.unique_by()`` - -- 0.7.5 - - - ``.multipeek()`` now implements ``PeekingNext``, by @nicopap. - -- 0.7.4 - - - Add new adaptor ``.update()`` by @lucasem; this adaptor is used - to modify an element before passing it on in an iterator chain. - -- 0.7.3 - - - Add new method ``.collect_tuple()`` by @matklad; it makes a tuple out of - the iterator's elements if the number of them matches **exactly**. - - Implement ``fold`` and ``collect`` for ``.map_results()`` which means - it reuses the code of the standard ``.map()`` for these methods. - -- 0.7.2 - - - Add new adaptor ``.merge_join_by`` by @srijs; a heterogeneous merge join - for two ordered sequences. - -- 0.7.1 - - - Iterator adaptors and iterators in itertools now use the same ``must_use`` - reminder that the standard library adaptors do, by @matematikaedit and @bluss - *“iterator adaptors are lazy and do nothing unless consumed”*. - -- 0.7.0 - - - Faster ``izip!()`` by @krdln - - - ``izip!()`` is now a wrapper for repeated regular ``.zip()`` and - a single ``.map()``. This means it optimizes as well as the standard - library ``.zip()`` it uses. - **Note:** ``multizip`` and ``izip!()`` are now different! The former - has a named type but the latter optimizes better. - - - Faster ``.unique()`` - - - ``no_std`` support, which is opt-in! - - - Many lovable features are still there without std, like ``izip!()`` - or ``.format()`` or ``.merge()``, but not those that use collections. - - - Trait bounds were required up front instead of just on the type: - ``group_by``'s ``PartialEq`` by @Phlosioneer and ``repeat_call``'s - ``FnMut``. - - Removed deprecated constructor ``Zip::new`` — use ``izip!()`` or ``multizip()`` - -- 0.6.5 - - - Fix bug in ``.cartesian_product()``'s fold (which only was visible for - unfused iterators). - -- 0.6.4 - - - Add specific ``fold`` implementations for ``.cartesian_product()`` and - ``cons_tuples()``, which improves their performance in fold, foreach, and - iterator consumers derived from them. - -- 0.6.3 - - - Add iterator adaptor ``.positions(predicate)`` by @tmccombs - -- 0.6.2 - - - Add function ``process_results`` which can “lift” a function of the regular - values of an iterator so that it can process the ``Ok`` values from an - iterator of ``Results`` instead, by @shepmaster - - Add iterator method ``.concat()`` which combines all iterator elements - into a single collection using the ``Extend`` trait, by @srijs - -- 0.6.1 - - - Better size hint testing and subsequent size hint bugfixes by @rkarp. - Fixes bugs in product, interleave_shortest size hints. - - New iterator method ``.all_equal()`` by @phimuemue - -- 0.6.0 - - - Deprecated names were removed in favour of their replacements - - ``.flatten()`` does not implement double ended iteration anymore - - ``.fold_while()`` uses ``&mut self`` and returns ``FoldWhile``, for - composability (#168) - - ``.foreach()`` and ``.fold1()`` use ``self``, like ``.fold()`` does. - - ``.combinations(0)`` now produces a single empty vector. (#174) - -- 0.5.10 - - - Add itertools method ``.kmerge_by()`` (and corresponding free function) - - Relaxed trait requirement of ``.kmerge()`` and ``.minmax()`` to PartialOrd. - -- 0.5.9 - - - Add multipeek method ``.reset_peek()`` - - Add categories - -- 0.5.8 - - - Add iterator adaptor ``.peeking_take_while()`` and its trait ``PeekingNext``. - -- 0.5.7 - - - Add iterator adaptor ``.with_position()`` - - Fix multipeek's performance for long peeks by using ``VecDeque``. - -- 0.5.6 - - - Add ``.map_results()`` - -- 0.5.5 - - - Many more adaptors now implement ``Debug`` - - Add free function constructor ``repeat_n``. ``RepeatN::new`` is now - deprecated. - -- 0.5.4 - - - Add infinite generator function ``iterate``, that takes a seed and a - closure. - -- 0.5.3 - - - Special-cased ``.fold()`` for flatten and put back. ``.foreach()`` - now uses fold on the iterator, to pick up any iterator specific loop - implementation. - - ``.combinations(n)`` asserts up front that ``n != 0``, instead of - running into an error on the second iterator element. - -- 0.5.2 - - - Add ``.tuples::()`` that iterates by two, three or four elements at - a time (where ``T`` is a tuple type). - - Add ``.tuple_windows::()`` that iterates using a window of the - two, three or four most recent elements. - - Add ``.next_tuple::()`` method, that picks the next two, three or four - elements in one go. - - ``.interleave()`` now has an accurate size hint. - -- 0.5.1 - - - Workaround module/function name clash that made racer crash on completing - itertools. Only internal changes needed. - -- 0.5.0 - - - `Release announcement `_ - - Renamed: - - - combinations is now tuple_combinations - - combinations_n to combinations - - group_by_lazy, chunks_lazy to group_by, chunks - - Unfold::new to unfold() - - RepeatCall::new to repeat_call() - - Zip::new to multizip - - PutBack::new, PutBackN::new to put_back, put_back_n - - PutBack::with_value is now a builder setter, not a constructor - - MultiPeek::new, .multipeek() to multipeek() - - format to format_with and format_default to format - - .into_rc() to rciter - - ``Partition`` enum is now ``Either`` - - - Module reorganization: - - - All iterator structs are under ``itertools::structs`` but also - reexported to the top level, for backwards compatibility - - All free functions are reexported at the root, ``itertools::free`` will - be removed in the next version - - - Removed: - - - ZipSlices, use .zip() instead - - .enumerate_from(), ZipTrusted, due to being unstable - - .mend_slices(), moved to crate odds - - Stride, StrideMut, moved to crate odds - - linspace(), moved to crate itertools-num - - .sort_by(), use .sorted_by() - - .is_empty_hint(), use .size_hint() - - .dropn(), use .dropping() - - .map_fn(), use .map() - - .slice(), use .take() / .skip() - - helper traits in misc - - ``new`` constructors on iterator structs, use Itertools trait or free - functions instead - - ``itertools::size_hint`` is now private - - - Behaviour changes: - - - format and format_with helpers now panic if you try to format them more - than once. - - ``repeat_call`` is not double ended anymore - - - New features: - - - tuple flattening iterator is constructible with ``cons_tuples`` - - itertools reexports ``Either`` from the ``either`` crate. ``Either`` - is an iterator when ``L, R`` are. - - ``MinMaxResult`` now implements Copy and Clone - - tuple_combinations supports 1-4 tuples of combinations (previously just 2) - -- 0.4.19 - - - Add ``.minmax_by()`` - - Add ``itertools::free::cloned`` - - Add ``itertools::free::rciter`` - - Improve ``.step(n)`` slightly to take advantage of specialized Fuse better. - -- 0.4.18 - - - Only changes related to the "unstable" crate feature. This feature is more - or less deprecated. - - - Use deprecated warnings when unstable is enabled. .enumerate_from() will - be removed imminently since it's using a deprecated libstd trait. - -- 0.4.17 - - - Fix bug in .kmerge() that caused it to often produce the wrong order (#134) - -- 0.4.16 - - - Improve precision of the interleave_shortest adaptor's size hint (it is - now computed exactly when possible). - -- 0.4.15 - - - Fixup on top of the workaround in 0.4.14. A function in itertools::free was - removed by mistake and now it is added back again. - -- 0.4.14 - - - Workaround an upstream regression in a rust nightly build that broke - compilation of of itertools::free::{interleave, merge} - -- 0.4.13 - - - Add .minmax() and .minmax_by_key(), iterator methods for finding both minimum - and maximum in one scan. - - Add .format_default(), a simpler version of .format() (lazy formatting - for iterators). - -- 0.4.12 - - - Add .zip_eq(), an adaptor like .zip() except it ensures iterators - of inequal length don't pass silently (instead it panics). - - Add .fold_while(), an iterator method that is a fold that - can short-circuit. - - Add .partition_map(), an iterator method that can separate elements - into two collections. - -- 0.4.11 - - - Add .get() for Stride{,Mut} and .get_mut() for StrideMut - -- 0.4.10 - - - Improve performance of .kmerge() - -- 0.4.9 - - - Add k-ary merge adaptor .kmerge() - - Fix a bug in .islice() with ranges a..b where a > b. - -- 0.4.8 - - - Implement Clone, Debug for Linspace - -- 0.4.7 - - - Add function diff_with() that compares two iterators - - Add .combinations_n(), an n-ary combinations iterator - - Add methods PutBack::with_value and PutBack::into_parts. - -- 0.4.6 - - - Add method .sorted() - - Add module ``itertools::free`` with free function variants of common - iterator adaptors and methods. - For example ``enumerate(iterable)``, ``rev(iterable)``, and so on. - -- 0.4.5 - - - Add .flatten() - -- 0.4.4 - - - Allow composing ZipSlices with itself - -- 0.4.3 - - - Write iproduct!() as a single expression; this allows temporary values - in its arguments. - -- 0.4.2 - - - Add .fold_options() - - Require Rust 1.1 or later - -- 0.4.1 - - - Update .dropping() to take advantage of .nth() - -- 0.4.0 - - - .merge(), .unique() and .dedup() now perform better due to not using - function pointers - - Add free functions enumerate() and rev() - - Breaking changes: - - - Return types of .merge() and .merge_by() renamed and changed - - Method Merge::new removed - - .merge_by() now takes a closure that returns bool. - - Return type of .dedup() changed - - Return type of .mend_slices() changed - - Return type of .unique() changed - - Removed function times(), struct Times: use a range instead - - Removed deprecated macro icompr!() - - Removed deprecated FnMap and method .fn_map(): use .map_fn() - - .interleave_shortest() is no longer guaranteed to act like fused - -- 0.3.25 - - - Rename .sort_by() to .sorted_by(). Old name is deprecated. - - Fix well-formedness warnings from RFC 1214, no user visible impact - -- 0.3.24 - - - Improve performance of .merge()'s ordering function slightly - -- 0.3.23 - - - Added .chunks(), similar to (and based on) .group_by_lazy(). - - Tweak linspace to match numpy.linspace and make it double ended. - -- 0.3.22 - - - Added ZipSlices, a fast zip for slices - -- 0.3.21 - - - Remove `Debug` impl for `Format`, it will have different use later - -- 0.3.20 - - - Optimize .group_by_lazy() - -- 0.3.19 - - - Added .group_by_lazy(), a possibly nonallocating group by - - Added .format(), a nonallocating formatting helper for iterators - - Remove uses of RandomAccessIterator since it has been deprecated in rust. - -- 0.3.17 - - - Added (adopted) Unfold from rust - -- 0.3.16 - - - Added adaptors .unique(), .unique_by() - -- 0.3.15 - - - Added method .sort_by() - -- 0.3.14 - - - Added adaptor .while_some() - -- 0.3.13 - - - Added adaptor .interleave_shortest() - - Added adaptor .pad_using() - -- 0.3.11 - - - Added assert_equal function - -- 0.3.10 - - - Bugfix .combinations() size_hint. - -- 0.3.8 - - - Added source RepeatCall - -- 0.3.7 - - - Added adaptor PutBackN - - Added adaptor .combinations() - -- 0.3.6 - - - Added itertools::partition, partition a sequence in place based on a predicate. - - Deprecate icompr!() with no replacement. - -- 0.3.5 - - - .map_fn() replaces deprecated .fn_map(). - -- 0.3.4 - - - .take_while_ref() *by-ref adaptor* - - .coalesce() *adaptor* - - .mend_slices() *adaptor* - -- 0.3.3 - - - .dropping_back() *method* - - .fold1() *method* - - .is_empty_hint() *method* - License ------- diff --git a/third_party/rust/itertools/benches/bench1.rs b/third_party/rust/itertools/benches/bench1.rs index b9d3b4ff4ba45..71278d17b6b12 100644 --- a/third_party/rust/itertools/benches/bench1.rs +++ b/third_party/rust/itertools/benches/bench1.rs @@ -1,92 +1,87 @@ -#![feature(test)] - -extern crate test; -#[macro_use] extern crate itertools; - -use test::{black_box}; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; use itertools::Itertools; - use itertools::free::cloned; +use itertools::iproduct; use std::iter::repeat; use std::cmp; -use std::ops::Add; +use std::ops::{Add, Range}; mod extra; -use extra::ZipSlices; +use crate::extra::ZipSlices; -#[bench] -fn slice_iter(b: &mut test::Bencher) -{ +fn slice_iter(c: &mut Criterion) { let xs: Vec<_> = repeat(1i32).take(20).collect(); - b.iter(|| for elt in xs.iter() { - test::black_box(elt); - }) + + c.bench_function("slice iter", move |b| { + b.iter(|| for elt in xs.iter() { + black_box(elt); + }) + }); } -#[bench] -fn slice_iter_rev(b: &mut test::Bencher) -{ +fn slice_iter_rev(c: &mut Criterion) { let xs: Vec<_> = repeat(1i32).take(20).collect(); - b.iter(|| for elt in xs.iter().rev() { - test::black_box(elt); - }) + + c.bench_function("slice iter rev", move |b| { + b.iter(|| for elt in xs.iter().rev() { + black_box(elt); + }) + }); } -#[bench] -fn zip_default_zip(b: &mut test::Bencher) -{ +fn zip_default_zip(c: &mut Criterion) { let xs = vec![0; 1024]; let ys = vec![0; 768]; let xs = black_box(xs); let ys = black_box(ys); - b.iter(|| { - for (&x, &y) in xs.iter().zip(&ys) { - test::black_box(x); - test::black_box(y); - } - }) + c.bench_function("zip default zip", move |b| { + b.iter(|| { + for (&x, &y) in xs.iter().zip(&ys) { + black_box(x); + black_box(y); + } + }) + }); } -#[bench] -fn zipdot_i32_default_zip(b: &mut test::Bencher) -{ +fn zipdot_i32_default_zip(c: &mut Criterion) { let xs = vec![2; 1024]; let ys = vec![2; 768]; let xs = black_box(xs); let ys = black_box(ys); - b.iter(|| { - let mut s = 0; - for (&x, &y) in xs.iter().zip(&ys) { - s += x * y; - } - s - }) + c.bench_function("zipdot i32 default zip", move |b| { + b.iter(|| { + let mut s = 0; + for (&x, &y) in xs.iter().zip(&ys) { + s += x * y; + } + s + }) + }); } -#[bench] -fn zipdot_f32_default_zip(b: &mut test::Bencher) -{ +fn zipdot_f32_default_zip(c: &mut Criterion) { let xs = vec![2f32; 1024]; let ys = vec![2f32; 768]; let xs = black_box(xs); let ys = black_box(ys); - b.iter(|| { - let mut s = 0.; - for (&x, &y) in xs.iter().zip(&ys) { - s += x * y; - } - s - }) + c.bench_function("zipdot f32 default zip", move |b| { + b.iter(|| { + let mut s = 0.; + for (&x, &y) in xs.iter().zip(&ys) { + s += x * y; + } + s + }) + }); } -#[bench] -fn zip_default_zip3(b: &mut test::Bencher) -{ +fn zip_default_zip3(c: &mut Criterion) { let xs = vec![0; 1024]; let ys = vec![0; 768]; let zs = vec![0; 766]; @@ -94,280 +89,279 @@ fn zip_default_zip3(b: &mut test::Bencher) let ys = black_box(ys); let zs = black_box(zs); - b.iter(|| { - for ((&x, &y), &z) in xs.iter().zip(&ys).zip(&zs) { - test::black_box(x); - test::black_box(y); - test::black_box(z); - } - }) + c.bench_function("zip default zip3", move |b| { + b.iter(|| { + for ((&x, &y), &z) in xs.iter().zip(&ys).zip(&zs) { + black_box(x); + black_box(y); + black_box(z); + } + }) + }); } -#[bench] -fn zip_slices_ziptuple(b: &mut test::Bencher) -{ +fn zip_slices_ziptuple(c: &mut Criterion) { let xs = vec![0; 1024]; let ys = vec![0; 768]; - b.iter(|| { - let xs = black_box(&xs); - let ys = black_box(&ys); - for (&x, &y) in itertools::multizip((xs, ys)) { - test::black_box(x); - test::black_box(y); - } - }) + c.bench_function("zip slices ziptuple", move |b| { + b.iter(|| { + let xs = black_box(&xs); + let ys = black_box(&ys); + for (&x, &y) in itertools::multizip((xs, ys)) { + black_box(x); + black_box(y); + } + }) + }); } -#[bench] -fn zipslices(b: &mut test::Bencher) -{ +fn zipslices(c: &mut Criterion) { let xs = vec![0; 1024]; let ys = vec![0; 768]; let xs = black_box(xs); let ys = black_box(ys); - b.iter(|| { - for (&x, &y) in ZipSlices::new(&xs, &ys) { - test::black_box(x); - test::black_box(y); - } - }) + c.bench_function("zipslices", move |b| { + b.iter(|| { + for (&x, &y) in ZipSlices::new(&xs, &ys) { + black_box(x); + black_box(y); + } + }) + }); } -#[bench] -fn zipslices_mut(b: &mut test::Bencher) -{ +fn zipslices_mut(c: &mut Criterion) { let xs = vec![0; 1024]; let ys = vec![0; 768]; let xs = black_box(xs); let mut ys = black_box(ys); - b.iter(|| { - for (&x, &mut y) in ZipSlices::from_slices(&xs[..], &mut ys[..]) { - test::black_box(x); - test::black_box(y); - } - }) + c.bench_function("zipslices mut", move |b| { + b.iter(|| { + for (&x, &mut y) in ZipSlices::from_slices(&xs[..], &mut ys[..]) { + black_box(x); + black_box(y); + } + }) + }); } -#[bench] -fn zipdot_i32_zipslices(b: &mut test::Bencher) -{ +fn zipdot_i32_zipslices(c: &mut Criterion) { let xs = vec![2; 1024]; let ys = vec![2; 768]; let xs = black_box(xs); let ys = black_box(ys); - b.iter(|| { - let mut s = 0i32; - for (&x, &y) in ZipSlices::new(&xs, &ys) { - s += x * y; - } - s - }) + c.bench_function("zipdot i32 zipslices", move |b| { + b.iter(|| { + let mut s = 0i32; + for (&x, &y) in ZipSlices::new(&xs, &ys) { + s += x * y; + } + s + }) + }); } -#[bench] -fn zipdot_f32_zipslices(b: &mut test::Bencher) -{ +fn zipdot_f32_zipslices(c: &mut Criterion) { let xs = vec![2f32; 1024]; let ys = vec![2f32; 768]; let xs = black_box(xs); let ys = black_box(ys); - b.iter(|| { - let mut s = 0.; - for (&x, &y) in ZipSlices::new(&xs, &ys) { - s += x * y; - } - s - }) + c.bench_function("zipdot f32 zipslices", move |b| { + b.iter(|| { + let mut s = 0.; + for (&x, &y) in ZipSlices::new(&xs, &ys) { + s += x * y; + } + s + }) + }); } - -#[bench] -fn zip_checked_counted_loop(b: &mut test::Bencher) -{ +fn zip_checked_counted_loop(c: &mut Criterion) { let xs = vec![0; 1024]; let ys = vec![0; 768]; let xs = black_box(xs); let ys = black_box(ys); - b.iter(|| { - // Must slice to equal lengths, and then bounds checks are eliminated! - let len = cmp::min(xs.len(), ys.len()); - let xs = &xs[..len]; - let ys = &ys[..len]; - - for i in 0..len { - let x = xs[i]; - let y = ys[i]; - test::black_box(x); - test::black_box(y); - } - }) + c.bench_function("zip checked counted loop", move |b| { + b.iter(|| { + // Must slice to equal lengths, and then bounds checks are eliminated! + let len = cmp::min(xs.len(), ys.len()); + let xs = &xs[..len]; + let ys = &ys[..len]; + + for i in 0..len { + let x = xs[i]; + let y = ys[i]; + black_box(x); + black_box(y); + } + }) + }); } -#[bench] -fn zipdot_i32_checked_counted_loop(b: &mut test::Bencher) -{ +fn zipdot_i32_checked_counted_loop(c: &mut Criterion) { let xs = vec![2; 1024]; let ys = vec![2; 768]; let xs = black_box(xs); let ys = black_box(ys); - b.iter(|| { - // Must slice to equal lengths, and then bounds checks are eliminated! - let len = cmp::min(xs.len(), ys.len()); - let xs = &xs[..len]; - let ys = &ys[..len]; + c.bench_function("zipdot i32 checked counted loop", move |b| { + b.iter(|| { + // Must slice to equal lengths, and then bounds checks are eliminated! + let len = cmp::min(xs.len(), ys.len()); + let xs = &xs[..len]; + let ys = &ys[..len]; - let mut s = 0i32; + let mut s = 0i32; - for i in 0..len { - s += xs[i] * ys[i]; - } - s - }) + for i in 0..len { + s += xs[i] * ys[i]; + } + s + }) + }); } -#[bench] -fn zipdot_f32_checked_counted_loop(b: &mut test::Bencher) -{ +fn zipdot_f32_checked_counted_loop(c: &mut Criterion) { let xs = vec![2f32; 1024]; let ys = vec![2f32; 768]; let xs = black_box(xs); let ys = black_box(ys); - b.iter(|| { - // Must slice to equal lengths, and then bounds checks are eliminated! - let len = cmp::min(xs.len(), ys.len()); - let xs = &xs[..len]; - let ys = &ys[..len]; + c.bench_function("zipdot f32 checked counted loop", move |b| { + b.iter(|| { + // Must slice to equal lengths, and then bounds checks are eliminated! + let len = cmp::min(xs.len(), ys.len()); + let xs = &xs[..len]; + let ys = &ys[..len]; - let mut s = 0.; + let mut s = 0.; - for i in 0..len { - s += xs[i] * ys[i]; - } - s - }) + for i in 0..len { + s += xs[i] * ys[i]; + } + s + }) + }); } -#[bench] -fn zipdot_f32_checked_counted_unrolled_loop(b: &mut test::Bencher) -{ +fn zipdot_f32_checked_counted_unrolled_loop(c: &mut Criterion) { let xs = vec![2f32; 1024]; let ys = vec![2f32; 768]; let xs = black_box(xs); let ys = black_box(ys); - b.iter(|| { - // Must slice to equal lengths, and then bounds checks are eliminated! - let len = cmp::min(xs.len(), ys.len()); - let mut xs = &xs[..len]; - let mut ys = &ys[..len]; - - let mut s = 0.; - let (mut p0, mut p1, mut p2, mut p3, mut p4, mut p5, mut p6, mut p7) = - (0., 0., 0., 0., 0., 0., 0., 0.); - - // how to unroll and have bounds checks eliminated (by cristicbz) - // split sum into eight parts to enable vectorization (by bluss) - while xs.len() >= 8 { - p0 += xs[0] * ys[0]; - p1 += xs[1] * ys[1]; - p2 += xs[2] * ys[2]; - p3 += xs[3] * ys[3]; - p4 += xs[4] * ys[4]; - p5 += xs[5] * ys[5]; - p6 += xs[6] * ys[6]; - p7 += xs[7] * ys[7]; - - xs = &xs[8..]; - ys = &ys[8..]; - } - s += p0 + p4; - s += p1 + p5; - s += p2 + p6; - s += p3 + p7; + c.bench_function("zipdot f32 checked counted unrolled loop", move |b| { + b.iter(|| { + // Must slice to equal lengths, and then bounds checks are eliminated! + let len = cmp::min(xs.len(), ys.len()); + let mut xs = &xs[..len]; + let mut ys = &ys[..len]; + + let mut s = 0.; + let (mut p0, mut p1, mut p2, mut p3, mut p4, mut p5, mut p6, mut p7) = + (0., 0., 0., 0., 0., 0., 0., 0.); + + // how to unroll and have bounds checks eliminated (by cristicbz) + // split sum into eight parts to enable vectorization (by bluss) + while xs.len() >= 8 { + p0 += xs[0] * ys[0]; + p1 += xs[1] * ys[1]; + p2 += xs[2] * ys[2]; + p3 += xs[3] * ys[3]; + p4 += xs[4] * ys[4]; + p5 += xs[5] * ys[5]; + p6 += xs[6] * ys[6]; + p7 += xs[7] * ys[7]; + + xs = &xs[8..]; + ys = &ys[8..]; + } + s += p0 + p4; + s += p1 + p5; + s += p2 + p6; + s += p3 + p7; - for i in 0..xs.len() { - s += xs[i] * ys[i]; - } - s - }) + for i in 0..xs.len() { + s += xs[i] * ys[i]; + } + s + }) + }); } -#[bench] -fn zip_unchecked_counted_loop(b: &mut test::Bencher) -{ +fn zip_unchecked_counted_loop(c: &mut Criterion) { let xs = vec![0; 1024]; let ys = vec![0; 768]; let xs = black_box(xs); let ys = black_box(ys); - b.iter(|| { - let len = cmp::min(xs.len(), ys.len()); - for i in 0..len { - unsafe { - let x = *xs.get_unchecked(i); - let y = *ys.get_unchecked(i); - test::black_box(x); - test::black_box(y); + c.bench_function("zip unchecked counted loop", move |b| { + b.iter(|| { + let len = cmp::min(xs.len(), ys.len()); + for i in 0..len { + unsafe { + let x = *xs.get_unchecked(i); + let y = *ys.get_unchecked(i); + black_box(x); + black_box(y); + } } - } - }) + }) + }); } -#[bench] -fn zipdot_i32_unchecked_counted_loop(b: &mut test::Bencher) -{ +fn zipdot_i32_unchecked_counted_loop(c: &mut Criterion) { let xs = vec![2; 1024]; let ys = vec![2; 768]; let xs = black_box(xs); let ys = black_box(ys); - b.iter(|| { - let len = cmp::min(xs.len(), ys.len()); - let mut s = 0i32; - for i in 0..len { - unsafe { - let x = *xs.get_unchecked(i); - let y = *ys.get_unchecked(i); - s += x * y; + c.bench_function("zipdot i32 unchecked counted loop", move |b| { + b.iter(|| { + let len = cmp::min(xs.len(), ys.len()); + let mut s = 0i32; + for i in 0..len { + unsafe { + let x = *xs.get_unchecked(i); + let y = *ys.get_unchecked(i); + s += x * y; + } } - } - s - }) + s + }) + }); } -#[bench] -fn zipdot_f32_unchecked_counted_loop(b: &mut test::Bencher) -{ +fn zipdot_f32_unchecked_counted_loop(c: &mut Criterion) { let xs = vec![2.; 1024]; let ys = vec![2.; 768]; let xs = black_box(xs); let ys = black_box(ys); - b.iter(|| { - let len = cmp::min(xs.len(), ys.len()); - let mut s = 0f32; - for i in 0..len { - unsafe { - let x = *xs.get_unchecked(i); - let y = *ys.get_unchecked(i); - s += x * y; + c.bench_function("zipdot f32 unchecked counted loop", move |b| { + b.iter(|| { + let len = cmp::min(xs.len(), ys.len()); + let mut s = 0f32; + for i in 0..len { + unsafe { + let x = *xs.get_unchecked(i); + let y = *ys.get_unchecked(i); + s += x * y; + } } - } - s - }) + s + }) + }); } -#[bench] -fn zip_unchecked_counted_loop3(b: &mut test::Bencher) -{ +fn zip_unchecked_counted_loop3(c: &mut Criterion) { let xs = vec![0; 1024]; let ys = vec![0; 768]; let zs = vec![0; 766]; @@ -375,102 +369,109 @@ fn zip_unchecked_counted_loop3(b: &mut test::Bencher) let ys = black_box(ys); let zs = black_box(zs); - b.iter(|| { - let len = cmp::min(xs.len(), cmp::min(ys.len(), zs.len())); - for i in 0..len { - unsafe { - let x = *xs.get_unchecked(i); - let y = *ys.get_unchecked(i); - let z = *zs.get_unchecked(i); - test::black_box(x); - test::black_box(y); - test::black_box(z); + c.bench_function("zip unchecked counted loop3", move |b| { + b.iter(|| { + let len = cmp::min(xs.len(), cmp::min(ys.len(), zs.len())); + for i in 0..len { + unsafe { + let x = *xs.get_unchecked(i); + let y = *ys.get_unchecked(i); + let z = *zs.get_unchecked(i); + black_box(x); + black_box(y); + black_box(z); + } } - } - }) + }) + }); } -#[bench] -fn group_by_lazy_1(b: &mut test::Bencher) { +fn group_by_lazy_1(c: &mut Criterion) { let mut data = vec![0; 1024]; for (index, elt) in data.iter_mut().enumerate() { *elt = index / 10; } - let data = test::black_box(data); + let data = black_box(data); - b.iter(|| { - for (_key, group) in &data.iter().group_by(|elt| **elt) { - for elt in group { - test::black_box(elt); + c.bench_function("group by lazy 1", move |b| { + b.iter(|| { + for (_key, group) in &data.iter().group_by(|elt| **elt) { + for elt in group { + black_box(elt); + } } - } - }) + }) + }); } -#[bench] -fn group_by_lazy_2(b: &mut test::Bencher) { +fn group_by_lazy_2(c: &mut Criterion) { let mut data = vec![0; 1024]; for (index, elt) in data.iter_mut().enumerate() { *elt = index / 2; } - let data = test::black_box(data); + let data = black_box(data); - b.iter(|| { - for (_key, group) in &data.iter().group_by(|elt| **elt) { - for elt in group { - test::black_box(elt); + c.bench_function("group by lazy 2", move |b| { + b.iter(|| { + for (_key, group) in &data.iter().group_by(|elt| **elt) { + for elt in group { + black_box(elt); + } } - } - }) + }) + }); } -#[bench] -fn slice_chunks(b: &mut test::Bencher) { +fn slice_chunks(c: &mut Criterion) { let data = vec![0; 1024]; - let data = test::black_box(data); - let sz = test::black_box(10); + let data = black_box(data); + let sz = black_box(10); - b.iter(|| { - for group in data.chunks(sz) { - for elt in group { - test::black_box(elt); + c.bench_function("slice chunks", move |b| { + b.iter(|| { + for group in data.chunks(sz) { + for elt in group { + black_box(elt); + } } - } - }) + }) + }); } -#[bench] -fn chunks_lazy_1(b: &mut test::Bencher) { +fn chunks_lazy_1(c: &mut Criterion) { let data = vec![0; 1024]; - let data = test::black_box(data); - let sz = test::black_box(10); + let data = black_box(data); + let sz = black_box(10); - b.iter(|| { - for group in &data.iter().chunks(sz) { - for elt in group { - test::black_box(elt); + c.bench_function("chunks lazy 1", move |b| { + b.iter(|| { + for group in &data.iter().chunks(sz) { + for elt in group { + black_box(elt); + } } - } - }) + }) + }); } -#[bench] -fn equal(b: &mut test::Bencher) { +fn equal(c: &mut Criterion) { let data = vec![7; 1024]; let l = data.len(); - let alpha = test::black_box(&data[1..]); - let beta = test::black_box(&data[..l - 1]); - b.iter(|| { - itertools::equal(alpha, beta) - }) + let alpha = black_box(&data[1..]); + let beta = black_box(&data[..l - 1]); + + c.bench_function("equal", move |b| { + b.iter(|| { + itertools::equal(alpha, beta) + }) + }); } -#[bench] -fn merge_default(b: &mut test::Bencher) { +fn merge_default(c: &mut Criterion) { let mut data1 = vec![0; 1024]; let mut data2 = vec![0; 800]; let mut x = 0; @@ -488,15 +489,17 @@ fn merge_default(b: &mut test::Bencher) { y += 0; } } - let data1 = test::black_box(data1); - let data2 = test::black_box(data2); - b.iter(|| { - data1.iter().merge(&data2).count() - }) + let data1 = black_box(data1); + let data2 = black_box(data2); + + c.bench_function("merge default", move |b| { + b.iter(|| { + data1.iter().merge(&data2).count() + }) + }); } -#[bench] -fn merge_by_cmp(b: &mut test::Bencher) { +fn merge_by_cmp(c: &mut Criterion) { let mut data1 = vec![0; 1024]; let mut data2 = vec![0; 800]; let mut x = 0; @@ -514,15 +517,17 @@ fn merge_by_cmp(b: &mut test::Bencher) { y += 0; } } - let data1 = test::black_box(data1); - let data2 = test::black_box(data2); - b.iter(|| { - data1.iter().merge_by(&data2, PartialOrd::le).count() - }) + let data1 = black_box(data1); + let data2 = black_box(data2); + + c.bench_function("merge by cmp", move |b| { + b.iter(|| { + data1.iter().merge_by(&data2, PartialOrd::le).count() + }) + }); } -#[bench] -fn merge_by_lt(b: &mut test::Bencher) { +fn merge_by_lt(c: &mut Criterion) { let mut data1 = vec![0; 1024]; let mut data2 = vec![0; 800]; let mut x = 0; @@ -540,15 +545,17 @@ fn merge_by_lt(b: &mut test::Bencher) { y += 0; } } - let data1 = test::black_box(data1); - let data2 = test::black_box(data2); - b.iter(|| { - data1.iter().merge_by(&data2, |a, b| a <= b).count() - }) + let data1 = black_box(data1); + let data2 = black_box(data2); + + c.bench_function("merge by lt", move |b| { + b.iter(|| { + data1.iter().merge_by(&data2, |a, b| a <= b).count() + }) + }); } -#[bench] -fn kmerge_default(b: &mut test::Bencher) { +fn kmerge_default(c: &mut Criterion) { let mut data1 = vec![0; 1024]; let mut data2 = vec![0; 800]; let mut x = 0; @@ -566,16 +573,18 @@ fn kmerge_default(b: &mut test::Bencher) { y += 0; } } - let data1 = test::black_box(data1); - let data2 = test::black_box(data2); + let data1 = black_box(data1); + let data2 = black_box(data2); let its = &[data1.iter(), data2.iter()]; - b.iter(|| { - its.iter().cloned().kmerge().count() - }) + + c.bench_function("kmerge default", move |b| { + b.iter(|| { + its.iter().cloned().kmerge().count() + }) + }); } -#[bench] -fn kmerge_tenway(b: &mut test::Bencher) { +fn kmerge_tenway(c: &mut Criterion) { let mut data = vec![0; 10240]; let mut state = 1729u16; @@ -602,12 +611,13 @@ fn kmerge_tenway(b: &mut test::Bencher) { // println!("Chunk lengths: {}", chunks.iter().format_with(", ", |elt, f| f(&elt.len()))); - b.iter(|| { - chunks.iter().cloned().kmerge().count() - }) + c.bench_function("kmerge tenway", move |b| { + b.iter(|| { + chunks.iter().cloned().kmerge().count() + }) + }); } - fn fast_integer_sum(iter: I) -> I::Item where I: IntoIterator, I::Item: Default + Add @@ -615,119 +625,253 @@ fn fast_integer_sum(iter: I) -> I::Item iter.into_iter().fold(<_>::default(), |x, y| x + y) } - -#[bench] -fn step_vec_2(b: &mut test::Bencher) { +fn step_vec_2(c: &mut Criterion) { let v = vec![0; 1024]; - b.iter(|| { - fast_integer_sum(cloned(v.iter().step(2))) + + c.bench_function("step vec 2", move |b| { + b.iter(|| { + fast_integer_sum(cloned(v.iter().step_by(2))) + }) }); } -#[bench] -fn step_vec_10(b: &mut test::Bencher) { +fn step_vec_10(c: &mut Criterion) { let v = vec![0; 1024]; - b.iter(|| { - fast_integer_sum(cloned(v.iter().step(10))) + + c.bench_function("step vec 10", move |b| { + b.iter(|| { + fast_integer_sum(cloned(v.iter().step_by(10))) + }) }); } -#[bench] -fn step_range_2(b: &mut test::Bencher) { +fn step_range_2(c: &mut Criterion) { let v = black_box(0..1024); - b.iter(|| { - fast_integer_sum(v.clone().step(2)) + + c.bench_function("step range 2", move |b| { + b.iter(|| { + fast_integer_sum(v.clone().step_by(2)) + }) }); } -#[bench] -fn step_range_10(b: &mut test::Bencher) { +fn step_range_10(c: &mut Criterion) { let v = black_box(0..1024); - b.iter(|| { - fast_integer_sum(v.clone().step(10)) + + c.bench_function("step range 10", move |b| { + b.iter(|| { + fast_integer_sum(v.clone().step_by(10)) + }) }); } -#[bench] -fn cartesian_product_iterator(b: &mut test::Bencher) -{ +fn cartesian_product_iterator(c: &mut Criterion) { let xs = vec![0; 16]; - b.iter(|| { - let mut sum = 0; - for (&x, &y, &z) in iproduct!(&xs, &xs, &xs) { - sum += x; - sum += y; - sum += z; - } - sum - }) + c.bench_function("cartesian product iterator", move |b| { + b.iter(|| { + let mut sum = 0; + for (&x, &y, &z) in iproduct!(&xs, &xs, &xs) { + sum += x; + sum += y; + sum += z; + } + sum + }) + }); } -#[bench] -fn cartesian_product_fold(b: &mut test::Bencher) -{ +fn cartesian_product_fold(c: &mut Criterion) { let xs = vec![0; 16]; - b.iter(|| { - let mut sum = 0; - iproduct!(&xs, &xs, &xs).fold((), |(), (&x, &y, &z)| { - sum += x; - sum += y; - sum += z; - }); - sum - }) + c.bench_function("cartesian product fold", move |b| { + b.iter(|| { + let mut sum = 0; + iproduct!(&xs, &xs, &xs).fold((), |(), (&x, &y, &z)| { + sum += x; + sum += y; + sum += z; + }); + sum + }) + }); } -#[bench] -fn multi_cartesian_product_iterator(b: &mut test::Bencher) -{ +fn multi_cartesian_product_iterator(c: &mut Criterion) { let xs = [vec![0; 16], vec![0; 16], vec![0; 16]]; - b.iter(|| { - let mut sum = 0; - for x in xs.into_iter().multi_cartesian_product() { - sum += x[0]; - sum += x[1]; - sum += x[2]; - } - sum - }) + c.bench_function("multi cartesian product iterator", move |b| { + b.iter(|| { + let mut sum = 0; + for x in xs.iter().multi_cartesian_product() { + sum += x[0]; + sum += x[1]; + sum += x[2]; + } + sum + }) + }); } -#[bench] -fn multi_cartesian_product_fold(b: &mut test::Bencher) -{ +fn multi_cartesian_product_fold(c: &mut Criterion) { let xs = [vec![0; 16], vec![0; 16], vec![0; 16]]; - b.iter(|| { - let mut sum = 0; - xs.into_iter().multi_cartesian_product().fold((), |(), x| { - sum += x[0]; - sum += x[1]; - sum += x[2]; - }); - sum - }) + c.bench_function("multi cartesian product fold", move |b| { + b.iter(|| { + let mut sum = 0; + xs.iter().multi_cartesian_product().fold((), |(), x| { + sum += x[0]; + sum += x[1]; + sum += x[2]; + }); + sum + }) + }); } -#[bench] -fn cartesian_product_nested_for(b: &mut test::Bencher) -{ +fn cartesian_product_nested_for(c: &mut Criterion) { let xs = vec![0; 16]; - b.iter(|| { - let mut sum = 0; - for &x in &xs { - for &y in &xs { - for &z in &xs { - sum += x; - sum += y; - sum += z; + c.bench_function("cartesian product nested for", move |b| { + b.iter(|| { + let mut sum = 0; + for &x in &xs { + for &y in &xs { + for &z in &xs { + sum += x; + sum += y; + sum += z; + } + } + } + sum + }) + }); +} + +fn all_equal(c: &mut Criterion) { + let mut xs = vec![0; 5_000_000]; + xs.extend(vec![1; 5_000_000]); + + c.bench_function("all equal", move |b| { + b.iter(|| xs.iter().all_equal()) + }); +} + +fn all_equal_for(c: &mut Criterion) { + let mut xs = vec![0; 5_000_000]; + xs.extend(vec![1; 5_000_000]); + + c.bench_function("all equal for", move |b| { + b.iter(|| { + for &x in &xs { + if x != xs[0] { + return false; } } + true + }) + }); +} + +fn all_equal_default(c: &mut Criterion) { + let mut xs = vec![0; 5_000_000]; + xs.extend(vec![1; 5_000_000]); + + c.bench_function("all equal default", move |b| { + b.iter(|| xs.iter().dedup().nth(1).is_none()) + }); +} + +const PERM_COUNT: usize = 6; + +fn permutations_iter(c: &mut Criterion) { + struct NewIterator(Range); + + impl Iterator for NewIterator { + type Item = usize; + + fn next(&mut self) -> Option { + self.0.next() } - sum - }) + } + + c.bench_function("permutations iter", move |b| { + b.iter(|| { + for _ in NewIterator(0..PERM_COUNT).permutations(PERM_COUNT) { + + } + }) + }); } + +fn permutations_range(c: &mut Criterion) { + c.bench_function("permutations range", move |b| { + b.iter(|| { + for _ in (0..PERM_COUNT).permutations(PERM_COUNT) { + + } + }) + }); +} + +fn permutations_slice(c: &mut Criterion) { + let v = (0..PERM_COUNT).collect_vec(); + + c.bench_function("permutations slice", move |b| { + b.iter(|| { + for _ in v.as_slice().iter().permutations(PERM_COUNT) { + + } + }) + }); +} + +criterion_group!( + benches, + slice_iter, + slice_iter_rev, + zip_default_zip, + zipdot_i32_default_zip, + zipdot_f32_default_zip, + zip_default_zip3, + zip_slices_ziptuple, + zipslices, + zipslices_mut, + zipdot_i32_zipslices, + zipdot_f32_zipslices, + zip_checked_counted_loop, + zipdot_i32_checked_counted_loop, + zipdot_f32_checked_counted_loop, + zipdot_f32_checked_counted_unrolled_loop, + zip_unchecked_counted_loop, + zipdot_i32_unchecked_counted_loop, + zipdot_f32_unchecked_counted_loop, + zip_unchecked_counted_loop3, + group_by_lazy_1, + group_by_lazy_2, + slice_chunks, + chunks_lazy_1, + equal, + merge_default, + merge_by_cmp, + merge_by_lt, + kmerge_default, + kmerge_tenway, + step_vec_2, + step_vec_10, + step_range_2, + step_range_10, + cartesian_product_iterator, + cartesian_product_fold, + multi_cartesian_product_iterator, + multi_cartesian_product_fold, + cartesian_product_nested_for, + all_equal, + all_equal_for, + all_equal_default, + permutations_iter, + permutations_range, + permutations_slice, +); +criterion_main!(benches); diff --git a/third_party/rust/itertools/benches/combinations_with_replacement.rs b/third_party/rust/itertools/benches/combinations_with_replacement.rs new file mode 100644 index 0000000000000..8e4fa3dc3b1bc --- /dev/null +++ b/third_party/rust/itertools/benches/combinations_with_replacement.rs @@ -0,0 +1,40 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use itertools::Itertools; + +fn comb_replacement_n10_k5(c: &mut Criterion) { + c.bench_function("comb replacement n10k5", move |b| { + b.iter(|| { + for i in (0..10).combinations_with_replacement(5) { + black_box(i); + } + }) + }); +} + +fn comb_replacement_n5_k10(c: &mut Criterion) { + c.bench_function("comb replacement n5 k10", move |b| { + b.iter(|| { + for i in (0..5).combinations_with_replacement(10) { + black_box(i); + } + }) + }); +} + +fn comb_replacement_n10_k10(c: &mut Criterion) { + c.bench_function("comb replacement n10 k10", move |b| { + b.iter(|| { + for i in (0..10).combinations_with_replacement(10) { + black_box(i); + } + }) + }); +} + +criterion_group!( + benches, + comb_replacement_n10_k5, + comb_replacement_n5_k10, + comb_replacement_n10_k10, +); +criterion_main!(benches); diff --git a/third_party/rust/itertools/benches/extra/mod.rs b/third_party/rust/itertools/benches/extra/mod.rs index 5ddb5772f423f..52fe5cc3fe055 100644 --- a/third_party/rust/itertools/benches/extra/mod.rs +++ b/third_party/rust/itertools/benches/extra/mod.rs @@ -1,4 +1,2 @@ - - pub use self::zipslices::ZipSlices; mod zipslices; diff --git a/third_party/rust/itertools/benches/extra/zipslices.rs b/third_party/rust/itertools/benches/extra/zipslices.rs index 493a539fd63eb..8bf3967f59480 100644 --- a/third_party/rust/itertools/benches/extra/zipslices.rs +++ b/third_party/rust/itertools/benches/extra/zipslices.rs @@ -186,4 +186,3 @@ fn zipslices() { } ::itertools::assert_equal(&xs, &ys); } - diff --git a/third_party/rust/itertools/benches/fold_specialization.rs b/third_party/rust/itertools/benches/fold_specialization.rs new file mode 100644 index 0000000000000..53319a55c8791 --- /dev/null +++ b/third_party/rust/itertools/benches/fold_specialization.rs @@ -0,0 +1,73 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use itertools::Itertools; + +struct Unspecialized(I); + +impl Iterator for Unspecialized +where I: Iterator +{ + type Item = I::Item; + + #[inline(always)] + fn next(&mut self) -> Option { + self.0.next() + } + + #[inline(always)] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +mod specialization { + use super::*; + + pub mod intersperse { + use super::*; + + pub fn external(c: &mut Criterion) + { + let arr = [1; 1024]; + + c.bench_function("external", move |b| { + b.iter(|| { + let mut sum = 0; + for &x in arr.iter().intersperse(&0) { + sum += x; + } + sum + }) + }); + } + + pub fn internal_specialized(c: &mut Criterion) + { + let arr = [1; 1024]; + + c.bench_function("internal specialized", move |b| { + b.iter(|| { + arr.iter().intersperse(&0).fold(0, |acc, x| acc + x) + }) + }); + } + + pub fn internal_unspecialized(c: &mut Criterion) + { + let arr = [1; 1024]; + + c.bench_function("internal unspecialized", move |b| { + b.iter(|| { + Unspecialized(arr.iter().intersperse(&0)).fold(0, |acc, x| acc + x) + }) + }); + } + } +} + +criterion_group!( + benches, + specialization::intersperse::external, + specialization::intersperse::internal_specialized, + specialization::intersperse::internal_unspecialized, +); +criterion_main!(benches); diff --git a/third_party/rust/itertools/benches/tree_fold1.rs b/third_party/rust/itertools/benches/tree_fold1.rs index b71589f3fea33..f12995db8e380 100644 --- a/third_party/rust/itertools/benches/tree_fold1.rs +++ b/third_party/rust/itertools/benches/tree_fold1.rs @@ -1,11 +1,5 @@ -#![feature(test)] - -extern crate test; -extern crate itertools; - -use itertools::Itertools; -use itertools::cloned; -use test::Bencher; +use criterion::{criterion_group, criterion_main, Criterion}; +use itertools::{Itertools, cloned}; trait IterEx : Iterator { // Another efficient implementation against which to compare, @@ -17,7 +11,7 @@ trait IterEx : Iterator { let hint = self.size_hint().0; let cap = std::mem::size_of::() * 8 - hint.leading_zeros() as usize; let mut stack = Vec::with_capacity(cap); - self.enumerate().foreach(|(mut i, mut x)| { + self.enumerate().for_each(|(mut i, mut x)| { while (i & 1) != 0 { x = f(stack.pop().unwrap(), x); i >>= 1; @@ -35,39 +29,51 @@ macro_rules! def_benchs { $BENCH_NAME:ident, ) => ( mod $BENCH_NAME { - use super::*; - - #[bench] - fn sum(b: &mut Bencher) { - let v: Vec = (0.. $N).collect(); - b.iter(|| { - cloned(&v).$FUN(|x, y| x + y) - }); - } + use super::*; - #[bench] - fn complex_iter(b: &mut Bencher) { - let u = (3..).take($N / 2); - let v = (5..).take($N / 2); - let it = u.chain(v); + pub fn sum(c: &mut Criterion) { + let v: Vec = (0.. $N).collect(); - b.iter(|| { - it.clone().map(|x| x as f32).$FUN(f32::atan2) - }); - } + c.bench_function(&(stringify!($BENCH_NAME).replace('_', " ") + " sum"), move |b| { + b.iter(|| { + cloned(&v).$FUN(|x, y| x + y) + }) + }); + } - #[bench] - fn string_format(b: &mut Bencher) { - // This goes quadratic with linear `fold1`, so use a smaller - // size to not waste too much time in travis. The allocations - // in here are so expensive anyway that it'll still take - // way longer per iteration than the other two benchmarks. - let v: Vec = (0.. ($N/4)).collect(); - b.iter(|| { - cloned(&v).map(|x| x.to_string()).$FUN(|x, y| format!("{} + {}", x, y)) - }); - } + pub fn complex_iter(c: &mut Criterion) { + let u = (3..).take($N / 2); + let v = (5..).take($N / 2); + let it = u.chain(v); + + c.bench_function(&(stringify!($BENCH_NAME).replace('_', " ") + " complex iter"), move |b| { + b.iter(|| { + it.clone().map(|x| x as f32).$FUN(f32::atan2) + }) + }); + } + + pub fn string_format(c: &mut Criterion) { + // This goes quadratic with linear `fold1`, so use a smaller + // size to not waste too much time in travis. The allocations + // in here are so expensive anyway that it'll still take + // way longer per iteration than the other two benchmarks. + let v: Vec = (0.. ($N/4)).collect(); + + c.bench_function(&(stringify!($BENCH_NAME).replace('_', " ") + " string format"), move |b| { + b.iter(|| { + cloned(&v).map(|x| x.to_string()).$FUN(|x, y| format!("{} + {}", x, y)) + }) + }); + } } + + criterion_group!( + $BENCH_NAME, + $BENCH_NAME::sum, + $BENCH_NAME::complex_iter, + $BENCH_NAME::string_format, + ); ) } @@ -124,3 +130,15 @@ def_benchs!{ tree_fold1_vec, tree_fold1_vec_08, } + +criterion_main!( + fold1_10k, + tree_fold1_stack_10k, + tree_fold1_vec_10k, + fold1_100, + tree_fold1_stack_100, + tree_fold1_vec_100, + fold1_08, + tree_fold1_stack_08, + tree_fold1_vec_08, +); diff --git a/third_party/rust/itertools/benches/tuple_combinations.rs b/third_party/rust/itertools/benches/tuple_combinations.rs index 4a14b1d0bd16f..84411efd80df6 100644 --- a/third_party/rust/itertools/benches/tuple_combinations.rs +++ b/third_party/rust/itertools/benches/tuple_combinations.rs @@ -1,9 +1,4 @@ -#![feature(test)] - -extern crate test; -extern crate itertools; - -use test::{black_box, Bencher}; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; use itertools::Itertools; // approximate 100_000 iterations for each combination @@ -12,86 +7,107 @@ const N2: usize = 448; const N3: usize = 86; const N4: usize = 41; -#[bench] -fn comb_for1(b: &mut Bencher) { - b.iter(|| { - for i in 0..N1 { - black_box(i); - } +fn comb_for1(c: &mut Criterion) { + c.bench_function("comb for1", move |b| { + b.iter(|| { + for i in 0..N1 { + black_box(i); + } + }) }); } -#[bench] -fn comb_for2(b: &mut Bencher) { - b.iter(|| { - for i in 0..N2 { - for j in (i + 1)..N2 { - black_box(i + j); +fn comb_for2(c: &mut Criterion) { + c.bench_function("comb for2", move |b| { + b.iter(|| { + for i in 0..N2 { + for j in (i + 1)..N2 { + black_box(i + j); + } } - } + }) }); } -#[bench] -fn comb_for3(b: &mut Bencher) { - b.iter(|| { - for i in 0..N3 { - for j in (i + 1)..N3 { - for k in (j + 1)..N3 { - black_box(i + j + k); +fn comb_for3(c: &mut Criterion) { + c.bench_function("comb for3", move |b| { + b.iter(|| { + for i in 0..N3 { + for j in (i + 1)..N3 { + for k in (j + 1)..N3 { + black_box(i + j + k); + } } } - } + }) }); } -#[bench] -fn comb_for4(b: &mut Bencher) { - b.iter(|| { - for i in 0..N4 { - for j in (i + 1)..N4 { - for k in (j + 1)..N4 { - for l in (k + 1)..N4 { - black_box(i + j + k + l); +fn comb_for4(c: &mut Criterion) { + c.bench_function("comb for4", move |b| { + b.iter(|| { + for i in 0..N4 { + for j in (i + 1)..N4 { + for k in (j + 1)..N4 { + for l in (k + 1)..N4 { + black_box(i + j + k + l); + } } } } - } + }) }); } -#[bench] -fn comb_c1(b: &mut Bencher) { - b.iter(|| { - for (i,) in (0..N1).tuple_combinations() { - black_box(i); - } +fn comb_c1(c: &mut Criterion) { + c.bench_function("comb c1", move |b| { + b.iter(|| { + for (i,) in (0..N1).tuple_combinations() { + black_box(i); + } + }) }); } -#[bench] -fn comb_c2(b: &mut Bencher) { - b.iter(|| { - for (i, j) in (0..N2).tuple_combinations() { - black_box(i + j); - } +fn comb_c2(c: &mut Criterion) { + c.bench_function("comb c2", move |b| { + b.iter(|| { + for (i, j) in (0..N2).tuple_combinations() { + black_box(i + j); + } + }) }); } -#[bench] -fn comb_c3(b: &mut Bencher) { - b.iter(|| { - for (i, j, k) in (0..N3).tuple_combinations() { - black_box(i + j + k); - } +fn comb_c3(c: &mut Criterion) { + c.bench_function("comb c3", move |b| { + b.iter(|| { + for (i, j, k) in (0..N3).tuple_combinations() { + black_box(i + j + k); + } + }) }); } -#[bench] -fn comb_c4(b: &mut Bencher) { - b.iter(|| { - for (i, j, k, l) in (0..N4).tuple_combinations() { - black_box(i + j + k + l); - } +fn comb_c4(c: &mut Criterion) { + c.bench_function("comb c4", move |b| { + b.iter(|| { + for (i, j, k, l) in (0..N4).tuple_combinations() { + black_box(i + j + k + l); + } + }) }); } + +criterion_group!( + benches, + comb_for1, + comb_for2, + comb_for3, + comb_for4, + comb_c1, + comb_c2, + comb_c3, + comb_c4, +); +criterion_main!(benches); diff --git a/third_party/rust/itertools/benches/tuples.rs b/third_party/rust/itertools/benches/tuples.rs index b0f2990fdfd31..ea50aaaee1ce7 100644 --- a/third_party/rust/itertools/benches/tuples.rs +++ b/third_party/rust/itertools/benches/tuples.rs @@ -1,9 +1,4 @@ -#![feature(test)] - -extern crate test; -extern crate itertools; - -use test::Bencher; +use criterion::{criterion_group, criterion_main, Criterion}; use itertools::Itertools; fn s1(a: u32) -> u32 { @@ -56,6 +51,7 @@ fn sum_t4(s: &(&u32, &u32, &u32, &u32)) -> u32 { macro_rules! def_benchs { ($N:expr; + $BENCH_GROUP:ident, $TUPLE_FUN:ident, $TUPLES:ident, $TUPLE_WINDOWS:ident; @@ -65,84 +61,101 @@ macro_rules! def_benchs { $FOR_CHUNKS:ident, $FOR_WINDOWS:ident ) => ( - #[bench] - fn $FOR_CHUNKS(b: &mut Bencher) { + fn $FOR_CHUNKS(c: &mut Criterion) { let v: Vec = (0.. $N * 1_000).collect(); let mut s = 0; - b.iter(|| { - let mut j = 0; - for _ in 0..1_000 { - s += $SLICE_FUN(&v[j..(j + $N)]); - j += $N; - } - s + c.bench_function(&stringify!($FOR_CHUNKS).replace('_', " "), move |b| { + b.iter(|| { + let mut j = 0; + for _ in 0..1_000 { + s += $SLICE_FUN(&v[j..(j + $N)]); + j += $N; + } + s + }) }); } - #[bench] - fn $FOR_WINDOWS(b: &mut Bencher) { + fn $FOR_WINDOWS(c: &mut Criterion) { let v: Vec = (0..1_000).collect(); let mut s = 0; - b.iter(|| { - for i in 0..(1_000 - $N) { - s += $SLICE_FUN(&v[i..(i + $N)]); - } - s + c.bench_function(&stringify!($FOR_WINDOWS).replace('_', " "), move |b| { + b.iter(|| { + for i in 0..(1_000 - $N) { + s += $SLICE_FUN(&v[i..(i + $N)]); + } + s + }) }); } - #[bench] - fn $TUPLES(b: &mut Bencher) { + fn $TUPLES(c: &mut Criterion) { let v: Vec = (0.. $N * 1_000).collect(); let mut s = 0; - b.iter(|| { - for x in v.iter().tuples() { - s += $TUPLE_FUN(&x); - } - s + c.bench_function(&stringify!($TUPLES).replace('_', " "), move |b| { + b.iter(|| { + for x in v.iter().tuples() { + s += $TUPLE_FUN(&x); + } + s + }) }); } - #[bench] - fn $CHUNKS(b: &mut Bencher) { + fn $CHUNKS(c: &mut Criterion) { let v: Vec = (0.. $N * 1_000).collect(); let mut s = 0; - b.iter(|| { - for x in v.chunks($N) { - s += $SLICE_FUN(x); - } - s + c.bench_function(&stringify!($CHUNKS).replace('_', " "), move |b| { + b.iter(|| { + for x in v.chunks($N) { + s += $SLICE_FUN(x); + } + s + }) }); } - #[bench] - fn $TUPLE_WINDOWS(b: &mut Bencher) { + fn $TUPLE_WINDOWS(c: &mut Criterion) { let v: Vec = (0..1_000).collect(); let mut s = 0; - b.iter(|| { - for x in v.iter().tuple_windows() { - s += $TUPLE_FUN(&x); - } - s + c.bench_function(&stringify!($TUPLE_WINDOWS).replace('_', " "), move |b| { + b.iter(|| { + for x in v.iter().tuple_windows() { + s += $TUPLE_FUN(&x); + } + s + }) }); } - #[bench] - fn $WINDOWS(b: &mut Bencher) { + fn $WINDOWS(c: &mut Criterion) { let v: Vec = (0..1_000).collect(); let mut s = 0; - b.iter(|| { - for x in v.windows($N) { - s += $SLICE_FUN(x); - } - s + c.bench_function(&stringify!($WINDOWS).replace('_', " "), move |b| { + b.iter(|| { + for x in v.windows($N) { + s += $SLICE_FUN(x); + } + s + }) }); } + + criterion_group!( + $BENCH_GROUP, + $FOR_CHUNKS, + $FOR_WINDOWS, + $TUPLES, + $CHUNKS, + $TUPLE_WINDOWS, + $WINDOWS, + ); ) } def_benchs!{ 1; + benches_1, sum_t1, tuple_chunks_1, tuple_windows_1; @@ -155,6 +168,7 @@ def_benchs!{ def_benchs!{ 2; + benches_2, sum_t2, tuple_chunks_2, tuple_windows_2; @@ -167,6 +181,7 @@ def_benchs!{ def_benchs!{ 3; + benches_3, sum_t3, tuple_chunks_3, tuple_windows_3; @@ -179,6 +194,7 @@ def_benchs!{ def_benchs!{ 4; + benches_4, sum_t4, tuple_chunks_4, tuple_windows_4; @@ -188,3 +204,10 @@ def_benchs!{ for_chunks_4, for_windows_4 } + +criterion_main!( + benches_1, + benches_2, + benches_3, + benches_4, +); diff --git a/third_party/rust/itertools/examples/iris.rs b/third_party/rust/itertools/examples/iris.rs index c09afbea0d324..25ab373f70937 100644 --- a/third_party/rust/itertools/examples/iris.rs +++ b/third_party/rust/itertools/examples/iris.rs @@ -3,10 +3,6 @@ /// and does some simple manipulations. /// /// Iterators and itertools functionality are used throughout. -/// -/// - -extern crate itertools; use itertools::Itertools; use std::collections::HashMap; @@ -44,7 +40,7 @@ impl FromStr for Iris { // using Iterator::by_ref() for (index, part) in parts.by_ref().take(4).enumerate() { - iris.data[index] = try!(part.parse::()); + iris.data[index] = part.parse::()?; } if let Some(name) = parts.next() { iris.name = name.into(); diff --git a/third_party/rust/itertools/src/adaptors/mod.rs b/third_party/rust/itertools/src/adaptors/mod.rs index 59aebfaf068cc..7d61f117cea72 100644 --- a/third_party/rust/itertools/src/adaptors/mod.rs +++ b/third_party/rust/itertools/src/adaptors/mod.rs @@ -12,17 +12,7 @@ use std::fmt; use std::mem::replace; use std::iter::{Fuse, Peekable, FromIterator}; use std::marker::PhantomData; -use size_hint; - -macro_rules! clone_fields { - ($name:ident, $base:expr, $($field:ident),+) => ( - $name { - $( - $field : $base . $field .clone() - ),* - } - ); -} +use crate::size_hint; /// An iterator adaptor that alternates elements from two iterators until both /// run out. @@ -243,6 +233,28 @@ impl Iterator for PutBack size_hint::add_scalar(self.iter.size_hint(), self.top.is_some() as usize) } + fn count(self) -> usize { + self.iter.count() + (self.top.is_some() as usize) + } + + fn last(self) -> Option { + self.iter.last().or(self.top) + } + + fn nth(&mut self, n: usize) -> Option { + match self.top { + None => self.iter.nth(n), + ref mut some => { + if n == 0 { + some.take() + } else { + *some = None; + self.iter.nth(n - 1) + } + } + } + } + fn all(&mut self, mut f: G) -> bool where G: FnMut(Self::Item) -> bool { @@ -380,7 +392,7 @@ impl fmt::Debug for Batching where I: fmt::Debug { /// Create a new Batching iterator. pub fn batching(iter: I, f: F) -> Batching { - Batching { f: f, iter: iter } + Batching { f, iter } } impl Iterator for Batching @@ -463,61 +475,16 @@ impl ExactSizeIterator for Step where I: ExactSizeIterator {} - -struct MergeCore - where I: Iterator, - J: Iterator -{ - a: Peekable, - b: Peekable, - fused: Option, +pub trait MergePredicate { + fn merge_pred(&mut self, a: &T, b: &T) -> bool; } +#[derive(Clone)] +pub struct MergeLte; -impl Clone for MergeCore - where I: Iterator, - J: Iterator, - Peekable: Clone, - Peekable: Clone -{ - fn clone(&self) -> Self { - clone_fields!(MergeCore, self, a, b, fused) - } -} - -impl MergeCore - where I: Iterator, - J: Iterator -{ - fn next_with(&mut self, mut less_than: F) -> Option - where F: FnMut(&I::Item, &I::Item) -> bool - { - let less_than = match self.fused { - Some(lt) => lt, - None => match (self.a.peek(), self.b.peek()) { - (Some(a), Some(b)) => less_than(a, b), - (Some(_), None) => { - self.fused = Some(true); - true - } - (None, Some(_)) => { - self.fused = Some(false); - false - } - (None, None) => return None, - } - }; - - if less_than { - self.a.next() - } else { - self.b.next() - } - } - - fn size_hint(&self) -> (usize, Option) { - // Not ExactSizeIterator because size may be larger than usize - size_hint::add(self.a.size_hint(), self.b.size_hint()) +impl MergePredicate for MergeLte { + fn merge_pred(&mut self, a: &T, b: &T) -> bool { + a <= b } } @@ -528,30 +495,7 @@ impl MergeCore /// /// See [`.merge()`](../trait.Itertools.html#method.merge_by) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct Merge - where I: Iterator, - J: Iterator -{ - merge: MergeCore, -} - -impl Clone for Merge - where I: Iterator, - J: Iterator, - Peekable: Clone, - Peekable: Clone -{ - fn clone(&self) -> Self { - clone_fields!(Merge, self, merge) - } -} - -impl fmt::Debug for Merge - where I: Iterator + fmt::Debug, J: Iterator + fmt::Debug, - I::Item: fmt::Debug, -{ - debug_fmt_fields!(Merge, merge.a, merge.b); -} +pub type Merge = MergeBy; /// Create an iterator that merges elements in `i` and `j`. /// @@ -569,29 +513,7 @@ pub fn merge(i: I, j: J) -> Merge<::IntoIter, , I::Item: PartialOrd { - Merge { - merge: MergeCore { - a: i.into_iter().peekable(), - b: j.into_iter().peekable(), - fused: None, - }, - } -} - -impl Iterator for Merge - where I: Iterator, - J: Iterator, - I::Item: PartialOrd -{ - type Item = I::Item; - - fn next(&mut self) -> Option { - self.merge.next_with(|a, b| a <= b) - } - - fn size_hint(&self) -> (usize, Option) { - self.merge.size_hint() - } + merge_by_new(i, j, MergeLte) } /// An iterator adaptor that merges the two base iterators in ascending order. @@ -605,7 +527,9 @@ pub struct MergeBy where I: Iterator, J: Iterator { - merge: MergeCore, + a: Peekable, + b: Peekable, + fused: Option, cmp: F, } @@ -613,21 +537,26 @@ impl fmt::Debug for MergeBy where I: Iterator + fmt::Debug, J: Iterator + fmt::Debug, I::Item: fmt::Debug, { - debug_fmt_fields!(MergeBy, merge.a, merge.b); + debug_fmt_fields!(MergeBy, a, b); +} + +implbool> MergePredicate for F { + fn merge_pred(&mut self, a: &T, b: &T) -> bool { + self(a, b) + } } /// Create a `MergeBy` iterator. -pub fn merge_by_new(a: I, b: J, cmp: F) -> MergeBy - where I: Iterator, - J: Iterator +pub fn merge_by_new(a: I, b: J, cmp: F) -> MergeBy + where I: IntoIterator, + J: IntoIterator, + F: MergePredicate, { MergeBy { - merge: MergeCore { - a: a.peekable(), - b: b.peekable(), - fused: None, - }, - cmp: cmp, + a: a.into_iter().peekable(), + b: b.into_iter().peekable(), + fused: None, + cmp, } } @@ -638,24 +567,42 @@ impl Clone for MergeBy Peekable: Clone, F: Clone { - fn clone(&self) -> Self { - clone_fields!(MergeBy, self, merge, cmp) - } + clone_fields!(a, b, fused, cmp); } impl Iterator for MergeBy where I: Iterator, J: Iterator, - F: FnMut(&I::Item, &I::Item) -> bool + F: MergePredicate { type Item = I::Item; fn next(&mut self) -> Option { - self.merge.next_with(&mut self.cmp) + let less_than = match self.fused { + Some(lt) => lt, + None => match (self.a.peek(), self.b.peek()) { + (Some(a), Some(b)) => self.cmp.merge_pred(a, b), + (Some(_), None) => { + self.fused = Some(true); + true + } + (None, Some(_)) => { + self.fused = Some(false); + false + } + (None, None) => return None, + } + }; + if less_than { + self.a.next() + } else { + self.b.next() + } } fn size_hint(&self) -> (usize, Option) { - self.merge.size_hint() + // Not ExactSizeIterator because size may be larger than usize + size_hint::add(self.a.size_hint(), self.b.size_hint()) } } @@ -713,9 +660,7 @@ impl Clone for Coalesce where I: Iterator, I::Item: Clone { - fn clone(&self) -> Self { - clone_fields!(Coalesce, self, iter, f) - } + clone_fields!(iter, f); } impl fmt::Debug for Coalesce @@ -732,9 +677,9 @@ pub fn coalesce(mut iter: I, f: F) -> Coalesce Coalesce { iter: CoalesceCore { last: iter.next(), - iter: iter, + iter, }, - f: f, + f, } } @@ -753,53 +698,85 @@ impl Iterator for Coalesce } } -/// An iterator adaptor that removes repeated duplicates. +/// An iterator adaptor that removes repeated duplicates, determining equality using a comparison function. /// -/// See [`.dedup()`](../trait.Itertools.html#method.dedup) for more information. +/// See [`.dedup_by()`](../trait.Itertools.html#method.dedup_by) or [`.dedup()`](../trait.Itertools.html#method.dedup) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct Dedup +pub struct DedupBy where I: Iterator { iter: CoalesceCore, + dedup_pred: Pred, } -impl Clone for Dedup +pub trait DedupPredicate { // TODO replace by Fn(&T, &T)->bool once Rust supports it + fn dedup_pair(&mut self, a: &T, b: &T) -> bool; +} + +#[derive(Clone)] +pub struct DedupEq; + +impl DedupPredicate for DedupEq { + fn dedup_pair(&mut self, a: &T, b: &T) -> bool { + a==b + } +} + +implbool> DedupPredicate for F { + fn dedup_pair(&mut self, a: &T, b: &T) -> bool { + self(a, b) + } +} + +/// An iterator adaptor that removes repeated duplicates. +/// +/// See [`.dedup()`](../trait.Itertools.html#method.dedup) for more information. +pub type Dedup=DedupBy; + +impl Clone for DedupBy where I: Iterator, - I::Item: Clone + I::Item: Clone, { - fn clone(&self) -> Self { - clone_fields!(Dedup, self, iter) - } + clone_fields!(iter, dedup_pred); } -/// Create a new `Dedup`. -pub fn dedup(mut iter: I) -> Dedup - where I: Iterator +/// Create a new `DedupBy`. +pub fn dedup_by(mut iter: I, dedup_pred: Pred) -> DedupBy + where I: Iterator, { - Dedup { + DedupBy { iter: CoalesceCore { last: iter.next(), - iter: iter, + iter, }, + dedup_pred, } } -impl fmt::Debug for Dedup +/// Create a new `Dedup`. +pub fn dedup(iter: I) -> Dedup + where I: Iterator +{ + dedup_by(iter, DedupEq) +} + +impl fmt::Debug for DedupBy where I: Iterator + fmt::Debug, I::Item: fmt::Debug, { debug_fmt_fields!(Dedup, iter); } -impl Iterator for Dedup +impl Iterator for DedupBy where I: Iterator, - I::Item: PartialEq + Pred: DedupPredicate, { type Item = I::Item; fn next(&mut self) -> Option { + let ref mut dedup_pred = self.dedup_pred; self.iter.next_with(|x, y| { - if x == y { Ok(x) } else { Err((x, y)) } + if dedup_pred.dedup_pair(&x, &y) { Ok(x) } else { Err((x, y)) } }) } @@ -811,8 +788,9 @@ impl Iterator for Dedup where G: FnMut(Acc, Self::Item) -> Acc, { if let Some(mut last) = self.iter.last { + let mut dedup_pred = self.dedup_pred; accum = self.iter.iter.fold(accum, |acc, elt| { - if elt == last { + if dedup_pred.dedup_pair(&elt, &last) { acc } else { f(acc, replace(&mut last, elt)) @@ -845,7 +823,7 @@ impl<'a, I, F> fmt::Debug for TakeWhileRef<'a, I, F> pub fn take_while_ref(iter: &mut I, f: F) -> TakeWhileRef where I: Iterator + Clone { - TakeWhileRef { iter: iter, f: f } + TakeWhileRef { iter, f } } impl<'a, I, F> Iterator for TakeWhileRef<'a, I, F> @@ -887,7 +865,7 @@ pub struct WhileSome { /// Create a new `WhileSome`. pub fn while_some(iter: I) -> WhileSome { - WhileSome { iter: iter } + WhileSome { iter } } impl Iterator for WhileSome @@ -913,7 +891,7 @@ impl Iterator for WhileSome /// /// See [`.tuple_combinations()`](../trait.Itertools.html#method.tuple_combinations) for more /// information. -#[derive(Debug)] +#[derive(Clone, Debug)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct TupleCombinations where I: Iterator, @@ -952,14 +930,14 @@ impl Iterator for TupleCombinations } } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Tuple1Combination { iter: I, } impl From for Tuple1Combination { fn from(iter: I) -> Self { - Tuple1Combination { iter: iter } + Tuple1Combination { iter } } } @@ -977,7 +955,7 @@ impl HasCombination for (I::Item,) { macro_rules! impl_tuple_combination { ($C:ident $P:ident ; $A:ident, $($I:ident),* ; $($X:ident)*) => ( - #[derive(Debug)] + #[derive(Clone, Debug)] pub struct $C { item: Option, iter: I, @@ -1041,6 +1019,7 @@ impl_tuple_combination!(Tuple4Combination Tuple3Combination ; A, A, A, A, A; a b /// An iterator adapter to apply `Into` conversion to each element. /// /// See [`.map_into()`](../trait.Itertools.html#method.map_into) for more information. +#[derive(Clone)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct MapInto { iter: I, @@ -1050,7 +1029,7 @@ pub struct MapInto { /// Create a new [`MapInto`](struct.MapInto.html) iterator. pub fn map_into(iter: I) -> MapInto { MapInto { - iter: iter, + iter, _res: PhantomData, } } @@ -1098,6 +1077,7 @@ where /// An iterator adapter to apply a transformation within a nested `Result`. /// /// See [`.map_results()`](../trait.Itertools.html#method.map_results) for more information. +#[derive(Clone)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct MapResults { iter: I, @@ -1110,8 +1090,8 @@ pub fn map_results(iter: I, f: F) -> MapResults F: FnMut(T) -> U, { MapResults { - iter: iter, - f: f, + iter, + f, } } @@ -1147,6 +1127,7 @@ impl Iterator for MapResults /// An iterator adapter to get the positions of each element that matches a predicate. /// /// See [`.positions()`](../trait.Itertools.html#method.positions) for more information. +#[derive(Clone)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Positions { iter: I, @@ -1160,8 +1141,8 @@ pub fn positions(iter: I, f: F) -> Positions F: FnMut(I::Item) -> bool, { Positions { - iter: iter, - f: f, + iter, + f, count: 0 } } @@ -1205,6 +1186,7 @@ impl DoubleEndedIterator for Positions /// An iterator adapter to apply a mutating function to each element before yielding it. /// /// See [`.update()`](../trait.Itertools.html#method.update) for more information. +#[derive(Clone)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Update { iter: I, @@ -1217,7 +1199,7 @@ where I: Iterator, F: FnMut(&mut I::Item), { - Update { iter: iter, f: f } + Update { iter, f } } impl Iterator for Update diff --git a/third_party/rust/itertools/src/adaptors/multi_product.rs b/third_party/rust/itertools/src/adaptors/multi_product.rs index a6796386ed101..4a31713ab8366 100644 --- a/third_party/rust/itertools/src/adaptors/multi_product.rs +++ b/third_party/rust/itertools/src/adaptors/multi_product.rs @@ -1,7 +1,7 @@ #![cfg(feature = "use_std")] -use size_hint; -use Itertools; +use crate::size_hint; +use crate::Itertools; #[derive(Clone)] /// An iterator adaptor that iterates over the cartesian product of @@ -65,7 +65,7 @@ impl MultiProduct let on_first_iter = match state { StartOfIter => { let on_first_iter = !last.in_progress(); - state = MidIter { on_first_iter: on_first_iter }; + state = MidIter { on_first_iter }; on_first_iter }, MidIter { on_first_iter } => on_first_iter diff --git a/third_party/rust/itertools/src/combinations.rs b/third_party/rust/itertools/src/combinations.rs index a7744151c921d..8759518086ff8 100644 --- a/third_party/rust/itertools/src/combinations.rs +++ b/third_party/rust/itertools/src/combinations.rs @@ -1,45 +1,46 @@ - -use std::ops::Index; use std::fmt; -/// An iterator to iterate through all the `n`-length combinations in an iterator. +use super::lazy_buffer::LazyBuffer; + +/// An iterator to iterate through all the `k`-length combinations in an iterator. /// /// See [`.combinations()`](../trait.Itertools.html#method.combinations) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Combinations { - n: usize, indices: Vec, pool: LazyBuffer, first: bool, } +impl Clone for Combinations + where I: Clone + Iterator, + I::Item: Clone, +{ + clone_fields!(indices, pool, first); +} + impl fmt::Debug for Combinations where I: Iterator + fmt::Debug, I::Item: fmt::Debug, { - debug_fmt_fields!(Combinations, n, indices, pool, first); + debug_fmt_fields!(Combinations, indices, pool, first); } /// Create a new `Combinations` from a clonable iterator. -pub fn combinations(iter: I, n: usize) -> Combinations +pub fn combinations(iter: I, k: usize) -> Combinations where I: Iterator { - let mut indices: Vec = Vec::with_capacity(n); - for i in 0..n { - indices.push(i); - } let mut pool: LazyBuffer = LazyBuffer::new(iter); - for _ in 0..n { + for _ in 0..k { if !pool.get_next() { break; } } Combinations { - n: n, - indices: indices, - pool: pool, + indices: (0..k).collect(), + pool, first: true, } } @@ -50,29 +51,23 @@ impl Iterator for Combinations { type Item = Vec; fn next(&mut self) -> Option { - let mut pool_len = self.pool.len(); - if self.pool.is_done() { - if pool_len == 0 || self.n > pool_len { + if self.first { + if self.pool.is_done() { return None; } - } - - if self.first { self.first = false; - } else if self.n == 0 { + } else if self.indices.len() == 0 { return None; } else { // Scan from the end, looking for an index to increment - let mut i: usize = self.n - 1; + let mut i: usize = self.indices.len() - 1; // Check if we need to consume more from the iterator - if self.indices[i] == pool_len - 1 && !self.pool.is_done() { - if self.pool.get_next() { - pool_len += 1; - } + if self.indices[i] == self.pool.len() - 1 { + self.pool.get_next(); // may change pool size } - while self.indices[i] == i + pool_len - self.n { + while self.indices[i] == i + self.pool.len() - self.indices.len() { if i > 0 { i -= 1; } else { @@ -83,83 +78,12 @@ impl Iterator for Combinations // Increment index, and reset the ones to its right self.indices[i] += 1; - let mut j = i + 1; - while j < self.n { + for j in i+1..self.indices.len() { self.indices[j] = self.indices[j - 1] + 1; - j += 1; } } // Create result vector based on the indices - let mut result = Vec::with_capacity(self.n); - for i in self.indices.iter() { - result.push(self.pool[*i].clone()); - } - Some(result) + Some(self.indices.iter().map(|i| self.pool[*i].clone()).collect()) } } - -#[derive(Debug)] -struct LazyBuffer { - it: I, - done: bool, - buffer: Vec, -} - -impl LazyBuffer - where I: Iterator -{ - pub fn new(it: I) -> LazyBuffer { - let mut it = it; - let mut buffer = Vec::new(); - let done; - if let Some(first) = it.next() { - buffer.push(first); - done = false; - } else { - done = true; - } - LazyBuffer { - it: it, - done: done, - buffer: buffer, - } - } - - pub fn len(&self) -> usize { - self.buffer.len() - } - - pub fn is_done(&self) -> bool { - self.done - } - - pub fn get_next(&mut self) -> bool { - if self.done { - return false; - } - let next_item = self.it.next(); - match next_item { - Some(x) => { - self.buffer.push(x); - true - } - None => { - self.done = true; - false - } - } - } -} - -impl Index for LazyBuffer - where I: Iterator, - I::Item: Sized -{ - type Output = I::Item; - - fn index<'b>(&'b self, _index: usize) -> &'b I::Item { - self.buffer.index(_index) - } -} - diff --git a/third_party/rust/itertools/src/combinations_with_replacement.rs b/third_party/rust/itertools/src/combinations_with_replacement.rs new file mode 100644 index 0000000000000..d5115452117b7 --- /dev/null +++ b/third_party/rust/itertools/src/combinations_with_replacement.rs @@ -0,0 +1,107 @@ +use std::fmt; + +use super::lazy_buffer::LazyBuffer; + +/// An iterator to iterate through all the `n`-length combinations in an iterator, with replacement. +/// +/// See [`.combinations_with_replacement()`](../trait.Itertools.html#method.combinations_with_replacement) for more information. +#[derive(Clone)] +pub struct CombinationsWithReplacement +where + I: Iterator, + I::Item: Clone, +{ + k: usize, + indices: Vec, + // The current known max index value. This increases as pool grows. + max_index: usize, + pool: LazyBuffer, + first: bool, +} + +impl fmt::Debug for CombinationsWithReplacement +where + I: Iterator + fmt::Debug, + I::Item: fmt::Debug + Clone, +{ + debug_fmt_fields!(Combinations, k, indices, max_index, pool, first); +} + +impl CombinationsWithReplacement +where + I: Iterator, + I::Item: Clone, +{ + /// Map the current mask over the pool to get an output combination + fn current(&self) -> Vec { + self.indices.iter().map(|i| self.pool[*i].clone()).collect() + } +} + +/// Create a new `CombinationsWithReplacement` from a clonable iterator. +pub fn combinations_with_replacement(iter: I, k: usize) -> CombinationsWithReplacement +where + I: Iterator, + I::Item: Clone, +{ + let indices: Vec = vec![0; k]; + let pool: LazyBuffer = LazyBuffer::new(iter); + + CombinationsWithReplacement { + k, + indices, + max_index: 0, + pool, + first: true, + } +} + +impl Iterator for CombinationsWithReplacement +where + I: Iterator, + I::Item: Clone, +{ + type Item = Vec; + fn next(&mut self) -> Option { + // If this is the first iteration, return early + if self.first { + // In empty edge cases, stop iterating immediately + return if self.k != 0 && !self.pool.get_next() { + None + // Otherwise, yield the initial state + } else { + self.first = false; + Some(self.current()) + }; + } + + // Check if we need to consume more from the iterator + // This will run while we increment our first index digit + if self.pool.get_next() { + self.max_index = self.pool.len() - 1; + } + + // Work out where we need to update our indices + let mut increment: Option<(usize, usize)> = None; + for (i, indices_int) in self.indices.iter().enumerate().rev() { + if indices_int < &self.max_index { + increment = Some((i, indices_int + 1)); + break; + } + } + + match increment { + // If we can update the indices further + Some((increment_from, increment_value)) => { + // We need to update the rightmost non-max value + // and all those to the right + for indices_index in increment_from..self.indices.len() { + self.indices[indices_index] = increment_value + } + Some(self.current()) + } + // Otherwise, we're done + None => None, + } + } +} diff --git a/third_party/rust/itertools/src/concat_impl.rs b/third_party/rust/itertools/src/concat_impl.rs index 05b370e1c66da..6048d18f69751 100644 --- a/third_party/rust/itertools/src/concat_impl.rs +++ b/third_party/rust/itertools/src/concat_impl.rs @@ -1,4 +1,4 @@ -use Itertools; +use crate::Itertools; /// Combine all an iterator's elements into one element by using `Extend`. /// diff --git a/third_party/rust/itertools/src/cons_tuples_impl.rs b/third_party/rust/itertools/src/cons_tuples_impl.rs index 9b27e7580fe19..3cdfe0d183442 100644 --- a/third_party/rust/itertools/src/cons_tuples_impl.rs +++ b/third_party/rust/itertools/src/cons_tuples_impl.rs @@ -52,11 +52,7 @@ pub struct ConsTuples impl Clone for ConsTuples where I: Clone + Iterator, { - fn clone(&self) -> Self { - ConsTuples { - iter: self.iter.clone(), - } - } + clone_fields!(iter); } /// Create an iterator that maps for example iterators of diff --git a/third_party/rust/itertools/src/diff.rs b/third_party/rust/itertools/src/diff.rs index 2951bc422ef03..c196d8d2f2fd4 100644 --- a/third_party/rust/itertools/src/diff.rs +++ b/third_party/rust/itertools/src/diff.rs @@ -5,8 +5,8 @@ //! describes the difference between two non-`Clone` iterators `I` and `J` after breaking ASAP from //! a lock-step comparison. -use free::put_back; -use structs::PutBack; +use crate::free::put_back; +use crate::structs::PutBack; /// A type returned by the [`diff_with`](./fn.diff_with.html) function. /// diff --git a/third_party/rust/itertools/src/either_or_both.rs b/third_party/rust/itertools/src/either_or_both.rs index 55ae06c6b4341..a03a4f16ef11a 100644 --- a/third_party/rust/itertools/src/either_or_both.rs +++ b/third_party/rust/itertools/src/either_or_both.rs @@ -1,7 +1,9 @@ -use EitherOrBoth::*; +use crate::EitherOrBoth::*; + +use either::Either; /// Value that either holds a single A or B, or both. -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Eq, Hash, Debug)] pub enum EitherOrBoth { /// Both values are present. Both(A, B), @@ -22,11 +24,35 @@ impl EitherOrBoth { self.as_ref().right().is_some() } + /// If Left, return true otherwise, return false. + /// Exclusive version of [`has_left`]. + pub fn is_left(&self) -> bool { + match *self { + Left(_) => true, + _ => false, + } + } + + /// If Right, return true otherwise, return false. + /// Exclusive version of [`has_right`]. + pub fn is_right(&self) -> bool { + match *self { + Right(_) => true, + _ => false, + } + } + + /// If Right, return true otherwise, return false. + /// Equivalent to `self.as_ref().both().is_some()`. + pub fn is_both(&self) -> bool { + self.as_ref().both().is_some() + } + /// If `Left`, or `Both`, return `Some` with the left value, otherwise, return `None`. pub fn left(self) -> Option { match self { Left(left) | Both(left, _) => Some(left), - _ => None + _ => None, } } @@ -34,7 +60,15 @@ impl EitherOrBoth { pub fn right(self) -> Option { match self { Right(right) | Both(_, right) => Some(right), - _ => None + _ => None, + } + } + + /// If Both, return `Some` tuple containing left and right. + pub fn both(self) -> Option<(A, B)> { + match self { + Both(a, b) => Some((a, b)), + _ => None, } } @@ -55,4 +89,102 @@ impl EitherOrBoth { Both(ref mut left, ref mut right) => Both(left, right), } } + + /// Convert `EitherOrBoth` to `EitherOrBoth`. + pub fn flip(self) -> EitherOrBoth { + match self { + Left(a) => Right(a), + Right(b) => Left(b), + Both(a, b) => Both(b, a), + } + } + + /// Apply the function `f` on the value `a` in `Left(a)` or `Both(a, b)` variants. If it is + /// present rewrapping the result in `self`'s original variant. + pub fn map_left(self, f: F) -> EitherOrBoth + where + F: FnOnce(A) -> M, + { + match self { + Both(a, b) => Both(f(a), b), + Left(a) => Left(f(a)), + Right(b) => Right(b), + } + } + + /// Apply the function `f` on the value `b` in `Right(b)` or `Both(a, b)` variants. + /// If it is present rewrapping the result in `self`'s original variant. + pub fn map_right(self, f: F) -> EitherOrBoth + where + F: FnOnce(B) -> M, + { + match self { + Left(a) => Left(a), + Right(b) => Right(f(b)), + Both(a, b) => Both(a, f(b)), + } + } + + /// Apply the functions `f` and `g` on the value `a` and `b` respectively; + /// found in `Left(a)`, `Right(b)`, or `Both(a, b)` variants. + /// The Result is rewrapped `self`'s original variant. + pub fn map_any(self, f: F, g: G) -> EitherOrBoth + where + F: FnOnce(A) -> L, + G: FnOnce(B) -> R, + { + match self { + Left(a) => Left(f(a)), + Right(b) => Right(g(b)), + Both(a, b) => Both(f(a), g(b)), + } + } + + /// Apply the function `f` on the value `b` in `Right(b)` or `Both(a, _)` variants if it is + /// present. + pub fn left_and_then(self, f: F) -> EitherOrBoth + where + F: FnOnce(A) -> EitherOrBoth, + { + match self { + Left(a) | Both(a, _) => f(a), + Right(b) => Right(b), + } + } + + /// Apply the function `f` on the value `a` + /// in `Left(a)` or `Both(a, _)` variants if it is present. + pub fn right_and_then(self, f: F) -> EitherOrBoth + where + F: FnOnce(B) -> EitherOrBoth, + { + match self { + Left(a) => Left(a), + Right(b) | Both(_, b) => f(b), + } + } +} + +impl EitherOrBoth { + /// Return either value of left, right, or the product of `f` applied where `Both` are present. + pub fn reduce(self, f: F) -> T + where + F: FnOnce(T, T) -> T, + { + match self { + Left(a) => a, + Right(b) => b, + Both(a, b) => f(a, b), + } + } +} + +impl Into>> for EitherOrBoth { + fn into(self) -> Option> { + match self { + EitherOrBoth::Left(l) => Some(Either::Left(l)), + EitherOrBoth::Right(r) => Some(Either::Right(r)), + _ => None, + } + } } diff --git a/third_party/rust/itertools/src/exactly_one_err.rs b/third_party/rust/itertools/src/exactly_one_err.rs new file mode 100644 index 0000000000000..e4925ffb704f9 --- /dev/null +++ b/third_party/rust/itertools/src/exactly_one_err.rs @@ -0,0 +1,58 @@ +use std::iter::ExactSizeIterator; + +use crate::size_hint; + +/// Iterator returned for the error case of `IterTools::exactly_one()` +/// This iterator yields exactly the same elements as the input iterator. +/// +/// During the execution of exactly_one the iterator must be mutated. This wrapper +/// effectively "restores" the state of the input iterator when it's handed back. +/// +/// This is very similar to PutBackN except this iterator only supports 0-2 elements and does not +/// use a `Vec`. +#[derive(Debug, Clone)] +pub struct ExactlyOneError +where + I: Iterator, +{ + first_two: (Option, Option), + inner: I, +} + +impl ExactlyOneError +where + I: Iterator, +{ + /// Creates a new `ExactlyOneErr` iterator. + pub(crate) fn new(first_two: (Option, Option), inner: I) -> Self { + Self { first_two, inner } + } +} + +impl Iterator for ExactlyOneError +where + I: Iterator, +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + self.first_two + .0 + .take() + .or_else(|| self.first_two.1.take()) + .or_else(|| self.inner.next()) + } + + fn size_hint(&self) -> (usize, Option) { + let mut additional_len = 0; + if self.first_two.0.is_some() { + additional_len += 1; + } + if self.first_two.1.is_some() { + additional_len += 1; + } + size_hint::add_scalar(self.inner.size_hint(), additional_len) + } +} + +impl ExactSizeIterator for ExactlyOneError where I: ExactSizeIterator {} diff --git a/third_party/rust/itertools/src/format.rs b/third_party/rust/itertools/src/format.rs index c42806b01c593..f72ed3917d8cd 100644 --- a/third_party/rust/itertools/src/format.rs +++ b/third_party/rust/itertools/src/format.rs @@ -7,6 +7,7 @@ use std::cell::RefCell; /// exhausted. /// /// See [`.format_with()`](../trait.Itertools.html#method.format_with) for more information. +#[derive(Clone)] pub struct FormatWith<'a, I, F> { sep: &'a str, /// FormatWith uses interior mutability because Display::fmt takes &self. @@ -29,7 +30,7 @@ pub struct Format<'a, I> { pub fn new_format<'a, I, F>(iter: I, separator: &'a str, f: F) -> FormatWith<'a, I, F> where I: Iterator, - F: FnMut(I::Item, &mut FnMut(&fmt::Display) -> fmt::Result) -> fmt::Result + F: FnMut(I::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result { FormatWith { sep: separator, @@ -48,7 +49,7 @@ pub fn new_format_default<'a, I>(iter: I, separator: &'a str) -> Format<'a, I> impl<'a, I, F> fmt::Display for FormatWith<'a, I, F> where I: Iterator, - F: FnMut(I::Item, &mut FnMut(&fmt::Display) -> fmt::Result) -> fmt::Result + F: FnMut(I::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let (mut iter, mut format) = match self.inner.borrow_mut().take() { @@ -57,13 +58,13 @@ impl<'a, I, F> fmt::Display for FormatWith<'a, I, F> }; if let Some(fst) = iter.next() { - try!(format(fst, &mut |disp: &fmt::Display| disp.fmt(f))); + format(fst, &mut |disp: &dyn fmt::Display| disp.fmt(f))?; for elt in iter { if self.sep.len() > 0 { - try!(f.write_str(self.sep)); + f.write_str(self.sep)?; } - try!(format(elt, &mut |disp: &fmt::Display| disp.fmt(f))); + format(elt, &mut |disp: &dyn fmt::Display| disp.fmt(f))?; } } Ok(()) @@ -82,12 +83,12 @@ impl<'a, I> Format<'a, I> }; if let Some(fst) = iter.next() { - try!(cb(&fst, f)); + cb(&fst, f)?; for elt in iter { if self.sep.len() > 0 { - try!(f.write_str(self.sep)); + f.write_str(self.sep)?; } - try!(cb(&elt, f)); + cb(&elt, f)?; } } Ok(()) diff --git a/third_party/rust/itertools/src/free.rs b/third_party/rust/itertools/src/free.rs index 2bc72a9aa1075..a6bc1bd1b8707 100644 --- a/third_party/rust/itertools/src/free.rs +++ b/third_party/rust/itertools/src/free.rs @@ -10,23 +10,23 @@ use std::iter::{self, Zip}; type VecIntoIter = ::std::vec::IntoIter; #[cfg(feature = "use_std")] -use Itertools; +use crate::Itertools; -pub use adaptors::{ +pub use crate::adaptors::{ interleave, merge, put_back, }; #[cfg(feature = "use_std")] -pub use put_back_n_impl::put_back_n; +pub use crate::put_back_n_impl::put_back_n; #[cfg(feature = "use_std")] -pub use multipeek_impl::multipeek; +pub use crate::multipeek_impl::multipeek; #[cfg(feature = "use_std")] -pub use kmerge_impl::kmerge; -pub use zip_eq_impl::zip_eq; -pub use merge_join::merge_join_by; +pub use crate::kmerge_impl::kmerge; +pub use crate::zip_eq_impl::zip_eq; +pub use crate::merge_join::merge_join_by; #[cfg(feature = "use_std")] -pub use rciter_impl::rciter; +pub use crate::rciter_impl::rciter; /// Iterate `iterable` with a running index. /// diff --git a/third_party/rust/itertools/src/groupbylazy.rs b/third_party/rust/itertools/src/groupbylazy.rs index ff253e845df1d..bf6e3c7a158e0 100644 --- a/third_party/rust/itertools/src/groupbylazy.rs +++ b/third_party/rust/itertools/src/groupbylazy.rs @@ -30,7 +30,7 @@ impl ChunkIndex { #[inline(always)] fn new(size: usize) -> Self { ChunkIndex { - size: size, + size, index: 0, key: 0, } @@ -380,7 +380,7 @@ impl<'a, K, I, F> Iterator for Groups<'a, K, I, F> let key = inner.group_key(index); (key, Group { parent: self.parent, - index: index, + index, first: Some(elt), }) }) @@ -528,7 +528,7 @@ impl<'a, I> Iterator for Chunks<'a, I> inner.step(index).map(|elt| { Chunk { parent: self.parent, - index: index, + index, first: Some(elt), } }) diff --git a/third_party/rust/itertools/src/impl_macros.rs b/third_party/rust/itertools/src/impl_macros.rs index b41760aee8dee..04ab8e177fc17 100644 --- a/third_party/rust/itertools/src/impl_macros.rs +++ b/third_party/rust/itertools/src/impl_macros.rs @@ -12,3 +12,13 @@ macro_rules! debug_fmt_fields { } } } + +macro_rules! clone_fields { + ($($field:ident),*) => { + fn clone(&self) -> Self { + Self { + $($field: self.$field.clone(),)* + } + } + } +} diff --git a/third_party/rust/itertools/src/intersperse.rs b/third_party/rust/itertools/src/intersperse.rs index b1dc732117b19..0579299278496 100644 --- a/third_party/rust/itertools/src/intersperse.rs +++ b/third_party/rust/itertools/src/intersperse.rs @@ -27,7 +27,7 @@ pub fn intersperse(iter: I, elt: I::Item) -> Intersperse let mut iter = iter.fuse(); Intersperse { peek: iter.next(), - iter: iter, + iter, element: elt, } } @@ -57,4 +57,23 @@ impl Iterator for Intersperse let sh = self.iter.size_hint(); size_hint::add_scalar(size_hint::add(sh, sh), has_peek) } + + fn fold(mut self, init: B, mut f: F) -> B where + Self: Sized, F: FnMut(B, Self::Item) -> B, + { + let mut accum = init; + + if let Some(x) = self.peek.take() { + accum = f(accum, x); + } + + let element = &self.element; + + self.iter.fold(accum, + |accum, x| { + let accum = f(accum, element.clone()); + let accum = f(accum, x); + accum + }) + } } diff --git a/third_party/rust/itertools/src/kmerge_impl.rs b/third_party/rust/itertools/src/kmerge_impl.rs index 0720899e371ca..8f68aeb253c6e 100644 --- a/third_party/rust/itertools/src/kmerge_impl.rs +++ b/third_party/rust/itertools/src/kmerge_impl.rs @@ -1,20 +1,9 @@ - -use size_hint; -use Itertools; +use crate::size_hint; +use crate::Itertools; use std::mem::replace; use std::fmt; -macro_rules! clone_fields { - ($name:ident, $base:expr, $($field:ident),+) => ( - $name { - $( - $field : $base . $field .clone() - ),* - } - ); -} - /// Head element and Tail iterator pair /// /// `PartialEq`, `Eq`, `PartialOrd` and `Ord` are implemented by comparing sequences based on @@ -65,9 +54,7 @@ impl Clone for HeadTail where I: Iterator + Clone, I::Item: Clone { - fn clone(&self) -> Self { - clone_fields!(HeadTail, self, head, tail) - } + clone_fields!(head, tail); } /// Make `data` a heap (min-heap w.r.t the sorting). @@ -112,17 +99,25 @@ fn sift_down(heap: &mut [T], index: usize, mut less_than: S) /// /// See [`.kmerge()`](../trait.Itertools.html#method.kmerge) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct KMerge - where I: Iterator -{ - heap: Vec>, +pub type KMerge = KMergeBy; + +pub trait KMergePredicate { + fn kmerge_pred(&mut self, a: &T, b: &T) -> bool; } -impl fmt::Debug for KMerge - where I: Iterator + fmt::Debug, - I::Item: fmt::Debug, -{ - debug_fmt_fields!(KMerge, heap); +#[derive(Clone)] +pub struct KMergeByLt; + +impl KMergePredicate for KMergeByLt { + fn kmerge_pred(&mut self, a: &T, b: &T) -> bool { + a < b + } +} + +implbool> KMergePredicate for F { + fn kmerge_pred(&mut self, a: &T, b: &T) -> bool { + self(a, b) + } } /// Create an iterator that merges elements of the contained iterators using @@ -142,48 +137,7 @@ pub fn kmerge(iterable: I) -> KMerge<::IntoIter> I::Item: IntoIterator, <::Item as IntoIterator>::Item: PartialOrd { - let iter = iterable.into_iter(); - let (lower, _) = iter.size_hint(); - let mut heap = Vec::with_capacity(lower); - heap.extend(iter.filter_map(|it| HeadTail::new(it.into_iter()))); - heapify(&mut heap, |a, b| a.head < b.head); - KMerge { heap: heap } -} - -impl Clone for KMerge - where I: Iterator + Clone, - I::Item: Clone -{ - fn clone(&self) -> KMerge { - clone_fields!(KMerge, self, heap) - } -} - -impl Iterator for KMerge - where I: Iterator, - I::Item: PartialOrd -{ - type Item = I::Item; - - fn next(&mut self) -> Option { - if self.heap.is_empty() { - return None; - } - let result = if let Some(next) = self.heap[0].next() { - next - } else { - self.heap.swap_remove(0).head - }; - sift_down(&mut self.heap, 0, |a, b| a.head < b.head); - Some(result) - } - - fn size_hint(&self) -> (usize, Option) { - self.heap.iter() - .map(|i| i.size_hint()) - .fold1(size_hint::add) - .unwrap_or((0, Some(0))) - } + kmerge_by(iterable, KMergeByLt) } /// An iterator adaptor that merges an abitrary number of base iterators @@ -215,21 +169,27 @@ pub fn kmerge_by(iterable: I, mut less_than: F) -> KMergeBy<::IntoIter, F> where I: IntoIterator, I::Item: IntoIterator, - F: FnMut(&<::Item as IntoIterator>::Item, - &<::Item as IntoIterator>::Item) -> bool + F: KMergePredicate<<::Item as IntoIterator>::Item>, { let iter = iterable.into_iter(); let (lower, _) = iter.size_hint(); let mut heap: Vec<_> = Vec::with_capacity(lower); heap.extend(iter.filter_map(|it| HeadTail::new(it.into_iter()))); - heapify(&mut heap, |a, b| less_than(&a.head, &b.head)); - KMergeBy { heap: heap, less_than: less_than } + heapify(&mut heap, |a, b| less_than.kmerge_pred(&a.head, &b.head)); + KMergeBy { heap, less_than } } +impl Clone for KMergeBy + where I: Iterator + Clone, + I::Item: Clone, + F: Clone, +{ + clone_fields!(heap, less_than); +} impl Iterator for KMergeBy where I: Iterator, - F: FnMut(&I::Item, &I::Item) -> bool + F: KMergePredicate { type Item = I::Item; @@ -243,7 +203,7 @@ impl Iterator for KMergeBy self.heap.swap_remove(0).head }; let less_than = &mut self.less_than; - sift_down(&mut self.heap, 0, |a, b| less_than(&a.head, &b.head)); + sift_down(&mut self.heap, 0, |a, b| less_than.kmerge_pred(&a.head, &b.head)); Some(result) } diff --git a/third_party/rust/itertools/src/lazy_buffer.rs b/third_party/rust/itertools/src/lazy_buffer.rs new file mode 100644 index 0000000000000..01123d473bf80 --- /dev/null +++ b/third_party/rust/itertools/src/lazy_buffer.rs @@ -0,0 +1,59 @@ +use std::ops::Index; + +#[derive(Debug, Clone)] +pub struct LazyBuffer { + pub it: I, + done: bool, + buffer: Vec, +} + +impl LazyBuffer +where + I: Iterator, +{ + pub fn new(it: I) -> LazyBuffer { + LazyBuffer { + it, + done: false, + buffer: Vec::new(), + } + } + + pub fn len(&self) -> usize { + self.buffer.len() + } + + pub fn is_done(&self) -> bool { + self.done + } + + pub fn get_next(&mut self) -> bool { + if self.done { + return false; + } + let next_item = self.it.next(); + match next_item { + Some(x) => { + self.buffer.push(x); + true + } + None => { + self.done = true; + false + } + } + } +} + +impl Index for LazyBuffer +where + I: Iterator, + I::Item: Sized, + Vec: Index +{ + type Output = as Index>::Output; + + fn index(&self, _index: J) -> &Self::Output { + self.buffer.index(_index) + } +} diff --git a/third_party/rust/itertools/src/lib.rs b/third_party/rust/itertools/src/lib.rs index ab98bcdf9b3a0..b8daefda601f9 100644 --- a/third_party/rust/itertools/src/lib.rs +++ b/third_party/rust/itertools/src/lib.rs @@ -42,13 +42,11 @@ //! //! ## Rust Version //! -//! This version of itertools requires Rust 1.24 or later. +//! This version of itertools requires Rust 1.32 or later. //! //! [`Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html #![doc(html_root_url="https://docs.rs/itertools/0.8/")] -extern crate either; - #[cfg(not(feature = "use_std"))] extern crate core as std; @@ -56,7 +54,7 @@ pub use either::Either; #[cfg(feature = "use_std")] use std::collections::HashMap; -use std::iter::{IntoIterator}; +use std::iter::{IntoIterator, once}; use std::cmp::Ordering; use std::fmt; #[cfg(feature = "use_std")] @@ -77,8 +75,9 @@ pub use std::iter as __std_iter; /// The concrete iterator types. pub mod structs { - pub use adaptors::{ + pub use crate::adaptors::{ Dedup, + DedupBy, Interleave, InterleaveShortest, Product, @@ -96,68 +95,82 @@ pub mod structs { Update, }; #[allow(deprecated)] - pub use adaptors::Step; + pub use crate::adaptors::Step; + #[cfg(feature = "use_std")] + pub use crate::adaptors::MultiProduct; + #[cfg(feature = "use_std")] + pub use crate::combinations::Combinations; #[cfg(feature = "use_std")] - pub use adaptors::MultiProduct; + pub use crate::combinations_with_replacement::CombinationsWithReplacement; + pub use crate::cons_tuples_impl::ConsTuples; + pub use crate::exactly_one_err::ExactlyOneError; + pub use crate::format::{Format, FormatWith}; #[cfg(feature = "use_std")] - pub use combinations::Combinations; - pub use cons_tuples_impl::ConsTuples; - pub use format::{Format, FormatWith}; + pub use crate::groupbylazy::{IntoChunks, Chunk, Chunks, GroupBy, Group, Groups}; + pub use crate::intersperse::Intersperse; #[cfg(feature = "use_std")] - pub use groupbylazy::{IntoChunks, Chunk, Chunks, GroupBy, Group, Groups}; - pub use intersperse::Intersperse; + pub use crate::kmerge_impl::{KMerge, KMergeBy}; + pub use crate::merge_join::MergeJoinBy; #[cfg(feature = "use_std")] - pub use kmerge_impl::{KMerge, KMergeBy}; - pub use merge_join::MergeJoinBy; + pub use crate::multipeek_impl::MultiPeek; + pub use crate::pad_tail::PadUsing; + pub use crate::peeking_take_while::PeekingTakeWhile; #[cfg(feature = "use_std")] - pub use multipeek_impl::MultiPeek; - pub use pad_tail::PadUsing; - pub use peeking_take_while::PeekingTakeWhile; - pub use process_results_impl::ProcessResults; + pub use crate::permutations::Permutations; + pub use crate::process_results_impl::ProcessResults; #[cfg(feature = "use_std")] - pub use put_back_n_impl::PutBackN; + pub use crate::put_back_n_impl::PutBackN; #[cfg(feature = "use_std")] - pub use rciter_impl::RcIter; - pub use repeatn::RepeatN; + pub use crate::rciter_impl::RcIter; + pub use crate::repeatn::RepeatN; #[allow(deprecated)] - pub use sources::{RepeatCall, Unfold, Iterate}; + pub use crate::sources::{RepeatCall, Unfold, Iterate}; #[cfg(feature = "use_std")] - pub use tee::Tee; - pub use tuple_impl::{TupleBuffer, TupleWindows, Tuples}; + pub use crate::tee::Tee; + pub use crate::tuple_impl::{TupleBuffer, TupleWindows, Tuples}; #[cfg(feature = "use_std")] - pub use unique_impl::{Unique, UniqueBy}; - pub use with_position::WithPosition; - pub use zip_eq_impl::ZipEq; - pub use zip_longest::ZipLongest; - pub use ziptuple::Zip; + pub use crate::unique_impl::{Unique, UniqueBy}; + pub use crate::with_position::WithPosition; + pub use crate::zip_eq_impl::ZipEq; + pub use crate::zip_longest::ZipLongest; + pub use crate::ziptuple::Zip; +} + +/// Traits helpful for using certain `Itertools` methods in generic contexts. +pub mod traits { + pub use crate::tuple_impl::HomogeneousTuple; } + #[allow(deprecated)] -pub use structs::*; -pub use concat_impl::concat; -pub use cons_tuples_impl::cons_tuples; -pub use diff::diff_with; -pub use diff::Diff; +pub use crate::structs::*; +pub use crate::concat_impl::concat; +pub use crate::cons_tuples_impl::cons_tuples; +pub use crate::diff::diff_with; +pub use crate::diff::Diff; #[cfg(feature = "use_std")] -pub use kmerge_impl::{kmerge_by}; -pub use minmax::MinMaxResult; -pub use peeking_take_while::PeekingNext; -pub use process_results_impl::process_results; -pub use repeatn::repeat_n; +pub use crate::kmerge_impl::{kmerge_by}; +pub use crate::minmax::MinMaxResult; +pub use crate::peeking_take_while::PeekingNext; +pub use crate::process_results_impl::process_results; +pub use crate::repeatn::repeat_n; #[allow(deprecated)] -pub use sources::{repeat_call, unfold, iterate}; -pub use with_position::Position; -pub use ziptuple::multizip; +pub use crate::sources::{repeat_call, unfold, iterate}; +pub use crate::with_position::Position; +pub use crate::ziptuple::multizip; mod adaptors; mod either_or_both; -pub use either_or_both::EitherOrBoth; +pub use crate::either_or_both::EitherOrBoth; #[doc(hidden)] pub mod free; #[doc(inline)] -pub use free::*; +pub use crate::free::*; mod concat_impl; mod cons_tuples_impl; #[cfg(feature = "use_std")] mod combinations; +#[cfg(feature = "use_std")] +mod combinations_with_replacement; +mod exactly_one_err; mod diff; mod format; #[cfg(feature = "use_std")] @@ -167,12 +180,16 @@ mod groupbylazy; mod intersperse; #[cfg(feature = "use_std")] mod kmerge_impl; +#[cfg(feature = "use_std")] +mod lazy_buffer; mod merge_join; mod minmax; #[cfg(feature = "use_std")] mod multipeek_impl; mod pad_tail; mod peeking_take_while; +#[cfg(feature = "use_std")] +mod permutations; mod process_results_impl; #[cfg(feature = "use_std")] mod put_back_n_impl; @@ -198,7 +215,8 @@ mod ziptuple; /// from iterators `(I, J, ..., M)` with element types `I::Item = A`, `J::Item = B`, etc. /// /// ``` -/// #[macro_use] extern crate itertools; +/// # use itertools::iproduct; +/// # /// # fn main() { /// // Iterate over the coordinates of a 4 x 4 x 4 grid /// // from (0, 0, 0), (0, 0, 1), .., (0, 1, 0), (0, 1, 1), .. etc until (3, 3, 3) @@ -207,14 +225,6 @@ mod ziptuple; /// } /// # } /// ``` -/// -/// **Note:** To enable the macros in this crate, use the `#[macro_use]` -/// attribute when importing the crate: -/// -/// ``` -/// #[macro_use] extern crate itertools; -/// # fn main() { } -/// ``` macro_rules! iproduct { (@flatten $I:expr,) => ( $I @@ -255,7 +265,8 @@ macro_rules! iproduct { /// [`multizip`]: fn.multizip.html /// /// ``` -/// #[macro_use] extern crate itertools; +/// # use itertools::izip; +/// # /// # fn main() { /// /// // iterate over three sequences side-by-side @@ -269,14 +280,6 @@ macro_rules! iproduct { /// assert_eq!(results, [0 + 3, 10 + 7, 29, 36]); /// # } /// ``` -/// -/// **Note:** To enable the macros in this crate, use the `#[macro_use]` -/// attribute when importing the crate: -/// -/// ``` -/// #[macro_use] extern crate itertools; -/// # fn main() { } -/// ``` macro_rules! izip { // @closure creates a tuple-flattening closure for .map() call. usage: // @closure partial_pattern => partial_tuple , rest , of , iterators @@ -492,10 +495,11 @@ pub trait Itertools : Iterator { /// /// // Note: The `&` is significant here, `GroupBy` is iterable /// // only by reference. You can also call `.into_iter()` explicitly. + /// let mut data_grouped = Vec::new(); /// for (key, group) in &data.into_iter().group_by(|elt| *elt >= 0) { - /// // Check that the sum of each group is +/- 4. - /// assert_eq!(4, group.sum::().abs()); + /// data_grouped.push((key, group.collect())); /// } + /// assert_eq!(data_grouped, vec![(true, vec![1, 3]), (false, vec![-2, -2]), (true, vec![1, 0, 1, 2])]); /// ``` #[cfg(feature = "use_std")] fn group_by(self, key: F) -> GroupBy @@ -574,7 +578,7 @@ pub trait Itertools : Iterator { /// ``` fn tuple_windows(self) -> TupleWindows where Self: Sized + Iterator, - T: tuple_impl::TupleCollect, + T: traits::HomogeneousTuple, T::Item: Clone { tuple_impl::tuple_windows(self) @@ -613,7 +617,7 @@ pub trait Itertools : Iterator { /// See also [`Tuples::into_buffer`](structs/struct.Tuples.html#method.into_buffer). fn tuples(self) -> Tuples where Self: Sized + Iterator, - T: tuple_impl::TupleCollect + T: traits::HomogeneousTuple { tuple_impl::tuples(self) } @@ -954,6 +958,28 @@ pub trait Itertools : Iterator { adaptors::dedup(self) } + /// Remove duplicates from sections of consecutive identical elements, + /// determining equality using a comparison function. + /// If the iterator is sorted, all elements will be unique. + /// + /// Iterator element type is `Self::Item`. + /// + /// This iterator is *fused*. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let data = vec![(0, 1.), (1, 1.), (0, 2.), (0, 3.), (1, 3.), (1, 2.), (2, 2.)]; + /// itertools::assert_equal(data.into_iter().dedup_by(|x, y| x.1==y.1), + /// vec![(0, 1.), (0, 2.), (0, 3.), (1, 2.)]); + /// ``` + fn dedup_by(self, cmp: Cmp) -> DedupBy + where Self: Sized, + Cmp: FnMut(&Self::Item, &Self::Item)->bool, + { + adaptors::dedup_by(self, cmp) + } + /// Return an iterator adaptor that filters out elements that have /// already been produced once during the iteration. Duplicates /// are detected using hash and equality. @@ -1104,7 +1130,7 @@ pub trait Itertools : Iterator { adaptors::tuple_combinations(self) } - /// Return an iterator adaptor that iterates over the `n`-length combinations of + /// Return an iterator adaptor that iterates over the `k`-length combinations of /// the elements from an iterator. /// /// Iterator element type is `Vec`. The iterator produces a new Vec per iteration, @@ -1119,14 +1145,99 @@ pub trait Itertools : Iterator { /// vec![1, 2, 4], /// vec![1, 3, 4], /// vec![2, 3, 4], - /// ]); + /// ]); + /// ``` + /// + /// Note: Combinations does not take into account the equality of the iterated values. + /// ``` + /// use itertools::Itertools; + /// + /// let it = vec![1, 2, 2].into_iter().combinations(2); + /// itertools::assert_equal(it, vec![ + /// vec![1, 2], // Note: these are the same + /// vec![1, 2], // Note: these are the same + /// vec![2, 2], + /// ]); /// ``` #[cfg(feature = "use_std")] - fn combinations(self, n: usize) -> Combinations + fn combinations(self, k: usize) -> Combinations where Self: Sized, Self::Item: Clone { - combinations::combinations(self, n) + combinations::combinations(self, k) + } + + /// Return an iterator that iterates over the `k`-length combinations of + /// the elements from an iterator, with replacement. + /// + /// Iterator element type is `Vec`. The iterator produces a new Vec per iteration, + /// and clones the iterator elements. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let it = (1..4).combinations_with_replacement(2); + /// itertools::assert_equal(it, vec![ + /// vec![1, 1], + /// vec![1, 2], + /// vec![1, 3], + /// vec![2, 2], + /// vec![2, 3], + /// vec![3, 3], + /// ]); + /// ``` + #[cfg(feature = "use_std")] + fn combinations_with_replacement(self, k: usize) -> CombinationsWithReplacement + where + Self: Sized, + Self::Item: Clone, + { + combinations_with_replacement::combinations_with_replacement(self, k) + } + + /// Return an iterator adaptor that iterates over all k-permutations of the + /// elements from an iterator. + /// + /// Iterator element type is `Vec` with length `k`. The iterator + /// produces a new Vec per iteration, and clones the iterator elements. + /// + /// If `k` is greater than the length of the input iterator, the resultant + /// iterator adaptor will be empty. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let perms = (5..8).permutations(2); + /// itertools::assert_equal(perms, vec![ + /// vec![5, 6], + /// vec![5, 7], + /// vec![6, 5], + /// vec![6, 7], + /// vec![7, 5], + /// vec![7, 6], + /// ]); + /// ``` + /// + /// Note: Permutations does not take into account the equality of the iterated values. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let it = vec![2, 2].into_iter().permutations(2); + /// itertools::assert_equal(it, vec![ + /// vec![2, 2], // Note: these are the same + /// vec![2, 2], // Note: these are the same + /// ]); + /// ``` + /// + /// Note: The source iterator is collected lazily, and will not be + /// re-iterated if the permutations adaptor is completed and re-iterated. + #[cfg(feature = "use_std")] + fn permutations(self, k: usize) -> Permutations + where Self: Sized, + Self::Item: Clone + { + permutations::permutations(self, k) } /// Return an iterator adaptor that pads the sequence to a minimum length of @@ -1231,7 +1342,7 @@ pub trait Itertools : Iterator { /// ``` fn next_tuple(&mut self) -> Option where Self: Sized + Iterator, - T: tuple_impl::TupleCollect + T: traits::HomogeneousTuple { T::collect_from_iter_no_buf(self) } @@ -1256,7 +1367,7 @@ pub trait Itertools : Iterator { /// ``` fn collect_tuple(mut self) -> Option where Self: Sized + Iterator, - T: tuple_impl::TupleCollect + T: traits::HomogeneousTuple { match self.next_tuple() { elt @ Some(_) => match self.next() { @@ -1308,9 +1419,13 @@ pub trait Itertools : Iterator { /// assert!(data.into_iter().all_equal()); /// ``` fn all_equal(&mut self) -> bool - where Self::Item: PartialEq, + where Self: Sized, + Self::Item: PartialEq, { - self.dedup().nth(1).is_none() + match self.next() { + None => true, + Some(a) => self.all(|x| a == x), + } } /// Consume the first `n` elements from the iterator eagerly, @@ -1416,6 +1531,35 @@ pub trait Itertools : Iterator { self.collect() } + /// `.try_collect()` is more convenient way of writing + /// `.collect::>()` + /// + /// # Example + /// + /// ``` + /// use std::{fs, io}; + /// use itertools::Itertools; + /// + /// fn process_dir_entries(entries: &[fs::DirEntry]) { + /// // ... + /// } + /// + /// fn do_stuff() -> std::io::Result<()> { + /// let entries: Vec<_> = fs::read_dir(".")?.try_collect()?; + /// process_dir_entries(&entries); + /// + /// Ok(()) + /// } + /// ``` + #[cfg(feature = "use_std")] + fn try_collect(self) -> Result + where + Self: Sized + Iterator>, + Result: FromIterator>, + { + self.collect() + } + /// Assign to each reference in `self` from the `from` iterator, /// stopping at the shortest of the two iterators. /// @@ -1447,7 +1591,7 @@ pub trait Itertools : Iterator { count } - /// Combine all iterator elements into one String, seperated by `sep`. + /// Combine all iterator elements into one String, separated by `sep`. /// /// Use the `Display` implementation of each element. /// @@ -1532,7 +1676,7 @@ pub trait Itertools : Iterator { /// ``` fn format_with(self, sep: &str, format: F) -> FormatWith where Self: Sized, - F: FnMut(Self::Item, &mut FnMut(&fmt::Display) -> fmt::Result) -> fmt::Result, + F: FnMut(Self::Item, &mut dyn FnMut(&dyn fmt::Display) -> fmt::Result) -> fmt::Result, { format::new_format(self, sep, format) } @@ -1559,7 +1703,7 @@ pub trait Itertools : Iterator { /// ``` /// /// With a `start` value of 0 and an addition as folding function, - /// this effetively results in *((0 + 1) + 2) + 3* + /// this effectively results in *((0 + 1) + 2) + 3* /// /// ``` /// use std::ops::Add; @@ -1688,10 +1832,9 @@ pub trait Itertools : Iterator { /// assert_eq!((0..10).tree_fold1(|x, y| x + y), /// (0..10).fold1(|x, y| x + y)); /// // ...but not for non-associative ones - /// assert!((0..10).tree_fold1(|x, y| x - y) - /// != (0..10).fold1(|x, y| x - y)); + /// assert_ne!((0..10).tree_fold1(|x, y| x - y), + /// (0..10).fold1(|x, y| x - y)); /// ``` - // FIXME: If minver changes to >= 1.13, use `assert_ne!` in the doctest. fn tree_fold1(mut self, mut f: F) -> Option where F: FnMut(Self::Item, Self::Item) -> Self::Item, Self: Sized, @@ -1721,7 +1864,7 @@ pub trait Itertools : Iterator { II: Iterator, FF: FnMut(T, T) -> T { - let mut x = try!(inner0(it, f)); + let mut x = inner0(it, f)?; for height in 0..stop { // Try to get another tree the same size with which to combine it, // creating a new tree that's twice as big for next time around. @@ -1805,6 +1948,64 @@ pub trait Itertools : Iterator { FoldWhile::Continue(acc) } + /// Iterate over the entire iterator and add all the elements. + /// + /// An empty iterator returns `None`, otherwise `Some(sum)`. + /// + /// # Panics + /// + /// When calling `sum1()` and a primitive integer type is being returned, this + /// method will panic if the computation overflows and debug assertions are + /// enabled. + /// + /// # Examples + /// + /// ``` + /// use itertools::Itertools; + /// + /// let empty_sum = (1..1).sum1::(); + /// assert_eq!(empty_sum, None); + /// + /// let nonempty_sum = (1..11).sum1::(); + /// assert_eq!(nonempty_sum, Some(55)); + /// ``` + fn sum1(mut self) -> Option + where Self: Sized, + S: std::iter::Sum, + { + self.next() + .map(|first| once(first).chain(self).sum()) + } + + /// Iterate over the entire iterator and multiply all the elements. + /// + /// An empty iterator returns `None`, otherwise `Some(product)`. + /// + /// # Panics + /// + /// When calling `product1()` and a primitive integer type is being returned, + /// method will panic if the computation overflows and debug assertions are + /// enabled. + /// + /// # Examples + /// ``` + /// use itertools::Itertools; + /// + /// let empty_product = (1..1).product1::(); + /// assert_eq!(empty_product, None); + /// + /// let nonempty_product = (1..11).product1::(); + /// assert_eq!(nonempty_product, Some(3628800)); + /// ``` + fn product1

(mut self) -> Option

+ where Self: Sized, + P: std::iter::Product, + { + self.next() + .map(|first| once(first).chain(self).product()) + } + + /// Sort all iterator elements into a new iterator in ascending order. /// /// **Note:** This consumes the entire iterator, uses the @@ -1922,34 +2123,32 @@ pub trait Itertools : Iterator { /// assert_eq!(successes, [1, 2]); /// assert_eq!(failures, [false, true]); /// ``` - fn partition_map(self, predicate: F) -> (A, B) + fn partition_map(self, mut predicate: F) -> (A, B) where Self: Sized, - F: Fn(Self::Item) -> Either, + F: FnMut(Self::Item) -> Either, A: Default + Extend, B: Default + Extend, { let mut left = A::default(); let mut right = B::default(); - for val in self { - match predicate(val) { - Either::Left(v) => left.extend(Some(v)), - Either::Right(v) => right.extend(Some(v)), - } - } + self.for_each(|val| match predicate(val) { + Either::Left(v) => left.extend(Some(v)), + Either::Right(v) => right.extend(Some(v)), + }); (left, right) } /// Return a `HashMap` of keys mapped to `Vec`s of values. Keys and values /// are taken from `(Key, Value)` tuple pairs yielded by the input iterator. - /// + /// /// ``` /// use itertools::Itertools; - /// + /// /// let data = vec![(0, 10), (2, 12), (3, 13), (0, 20), (3, 33), (2, 42)]; /// let lookup = data.into_iter().into_group_map(); - /// + /// /// assert_eq!(lookup[&0], vec![10, 20]); /// assert_eq!(lookup.get(&1), None); /// assert_eq!(lookup[&2], vec![12, 42]); @@ -2038,6 +2237,352 @@ pub trait Itertools : Iterator { |x, y, _, _| Ordering::Less == compare(x, y) ) } + + /// Return the position of the maximum element in the iterator. + /// + /// If several elements are equally maximum, the position of the + /// last of them is returned. + /// + /// # Examples + /// + /// ``` + /// use itertools::Itertools; + /// + /// let a: [i32; 0] = []; + /// assert_eq!(a.iter().position_max(), None); + /// + /// let a = [-3, 0, 1, 5, -10]; + /// assert_eq!(a.iter().position_max(), Some(3)); + /// + /// let a = [1, 1, -1, -1]; + /// assert_eq!(a.iter().position_max(), Some(1)); + /// ``` + fn position_max(self) -> Option + where Self: Sized, Self::Item: Ord + { + self.enumerate() + .max_by(|x, y| Ord::cmp(&x.1, &y.1)) + .map(|x| x.0) + } + + /// Return the position of the maximum element in the iterator, as + /// determined by the specified function. + /// + /// If several elements are equally maximum, the position of the + /// last of them is returned. + /// + /// # Examples + /// + /// ``` + /// use itertools::Itertools; + /// + /// let a: [i32; 0] = []; + /// assert_eq!(a.iter().position_max_by_key(|x| x.abs()), None); + /// + /// let a = [-3_i32, 0, 1, 5, -10]; + /// assert_eq!(a.iter().position_max_by_key(|x| x.abs()), Some(4)); + /// + /// let a = [1_i32, 1, -1, -1]; + /// assert_eq!(a.iter().position_max_by_key(|x| x.abs()), Some(3)); + /// ``` + fn position_max_by_key(self, mut key: F) -> Option + where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K + { + self.enumerate() + .max_by(|x, y| Ord::cmp(&key(&x.1), &key(&y.1))) + .map(|x| x.0) + } + + /// Return the position of the maximum element in the iterator, as + /// determined by the specified comparison function. + /// + /// If several elements are equally maximum, the position of the + /// last of them is returned. + /// + /// # Examples + /// + /// ``` + /// use itertools::Itertools; + /// + /// let a: [i32; 0] = []; + /// assert_eq!(a.iter().position_max_by(|x, y| x.cmp(y)), None); + /// + /// let a = [-3_i32, 0, 1, 5, -10]; + /// assert_eq!(a.iter().position_max_by(|x, y| x.cmp(y)), Some(3)); + /// + /// let a = [1_i32, 1, -1, -1]; + /// assert_eq!(a.iter().position_max_by(|x, y| x.cmp(y)), Some(1)); + /// ``` + fn position_max_by(self, mut compare: F) -> Option + where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering + { + self.enumerate() + .max_by(|x, y| compare(&x.1, &y.1)) + .map(|x| x.0) + } + + /// Return the position of the minimum element in the iterator. + /// + /// If several elements are equally minimum, the position of the + /// first of them is returned. + /// + /// # Examples + /// + /// ``` + /// use itertools::Itertools; + /// + /// let a: [i32; 0] = []; + /// assert_eq!(a.iter().position_min(), None); + /// + /// let a = [-3, 0, 1, 5, -10]; + /// assert_eq!(a.iter().position_min(), Some(4)); + /// + /// let a = [1, 1, -1, -1]; + /// assert_eq!(a.iter().position_min(), Some(2)); + /// ``` + fn position_min(self) -> Option + where Self: Sized, Self::Item: Ord + { + self.enumerate() + .min_by(|x, y| Ord::cmp(&x.1, &y.1)) + .map(|x| x.0) + } + + /// Return the position of the minimum element in the iterator, as + /// determined by the specified function. + /// + /// If several elements are equally minimum, the position of the + /// first of them is returned. + /// + /// # Examples + /// + /// ``` + /// use itertools::Itertools; + /// + /// let a: [i32; 0] = []; + /// assert_eq!(a.iter().position_min_by_key(|x| x.abs()), None); + /// + /// let a = [-3_i32, 0, 1, 5, -10]; + /// assert_eq!(a.iter().position_min_by_key(|x| x.abs()), Some(1)); + /// + /// let a = [1_i32, 1, -1, -1]; + /// assert_eq!(a.iter().position_min_by_key(|x| x.abs()), Some(0)); + /// ``` + fn position_min_by_key(self, mut key: F) -> Option + where Self: Sized, K: Ord, F: FnMut(&Self::Item) -> K + { + self.enumerate() + .min_by(|x, y| Ord::cmp(&key(&x.1), &key(&y.1))) + .map(|x| x.0) + } + + /// Return the position of the minimum element in the iterator, as + /// determined by the specified comparison function. + /// + /// If several elements are equally minimum, the position of the + /// first of them is returned. + /// + /// # Examples + /// + /// ``` + /// use itertools::Itertools; + /// + /// let a: [i32; 0] = []; + /// assert_eq!(a.iter().position_min_by(|x, y| x.cmp(y)), None); + /// + /// let a = [-3_i32, 0, 1, 5, -10]; + /// assert_eq!(a.iter().position_min_by(|x, y| x.cmp(y)), Some(4)); + /// + /// let a = [1_i32, 1, -1, -1]; + /// assert_eq!(a.iter().position_min_by(|x, y| x.cmp(y)), Some(2)); + /// ``` + fn position_min_by(self, mut compare: F) -> Option + where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering + { + self.enumerate() + .min_by(|x, y| compare(&x.1, &y.1)) + .map(|x| x.0) + } + + /// Return the positions of the minimum and maximum elements in + /// the iterator. + /// + /// The return type [`MinMaxResult`] is an enum of three variants: + /// + /// - `NoElements` if the iterator is empty. + /// - `OneElement(xpos)` if the iterator has exactly one element. + /// - `MinMax(xpos, ypos)` is returned otherwise, where the + /// element at `xpos` ≤ the element at `ypos`. While the + /// referenced elements themselves may be equal, `xpos` cannot + /// be equal to `ypos`. + /// + /// On an iterator of length `n`, `position_minmax` does `1.5 * n` + /// comparisons, and so is faster than calling `positon_min` and + /// `position_max` separately which does `2 * n` comparisons. + /// + /// For the minimum, if several elements are equally minimum, the + /// position of the first of them is returned. For the maximum, if + /// several elements are equally maximum, the position of the last + /// of them is returned. + /// + /// The elements can be floats but no particular result is + /// guaranteed if an element is NaN. + /// + /// # Examples + /// + /// ``` + /// use itertools::Itertools; + /// use itertools::MinMaxResult::{NoElements, OneElement, MinMax}; + /// + /// let a: [i32; 0] = []; + /// assert_eq!(a.iter().position_minmax(), NoElements); + /// + /// let a = [10]; + /// assert_eq!(a.iter().position_minmax(), OneElement(0)); + /// + /// let a = [-3, 0, 1, 5, -10]; + /// assert_eq!(a.iter().position_minmax(), MinMax(4, 3)); + /// + /// let a = [1, 1, -1, -1]; + /// assert_eq!(a.iter().position_minmax(), MinMax(2, 1)); + /// ``` + /// + /// [`MinMaxResult`]: enum.MinMaxResult.html + fn position_minmax(self) -> MinMaxResult + where Self: Sized, Self::Item: PartialOrd + { + use crate::MinMaxResult::{NoElements, OneElement, MinMax}; + match minmax::minmax_impl(self.enumerate(), |_| (), |x, y, _, _| x.1 < y.1) { + NoElements => NoElements, + OneElement(x) => OneElement(x.0), + MinMax(x, y) => MinMax(x.0, y.0), + } + } + + /// Return the postions of the minimum and maximum elements of an + /// iterator, as determined by the specified function. + /// + /// The return value is a variant of [`MinMaxResult`] like for + /// [`position_minmax`]. + /// + /// For the minimum, if several elements are equally minimum, the + /// position of the first of them is returned. For the maximum, if + /// several elements are equally maximum, the position of the last + /// of them is returned. + /// + /// The keys can be floats but no particular result is guaranteed + /// if a key is NaN. + /// + /// # Examples + /// + /// ``` + /// use itertools::Itertools; + /// use itertools::MinMaxResult::{NoElements, OneElement, MinMax}; + /// + /// let a: [i32; 0] = []; + /// assert_eq!(a.iter().position_minmax_by_key(|x| x.abs()), NoElements); + /// + /// let a = [10_i32]; + /// assert_eq!(a.iter().position_minmax_by_key(|x| x.abs()), OneElement(0)); + /// + /// let a = [-3_i32, 0, 1, 5, -10]; + /// assert_eq!(a.iter().position_minmax_by_key(|x| x.abs()), MinMax(1, 4)); + /// + /// let a = [1_i32, 1, -1, -1]; + /// assert_eq!(a.iter().position_minmax_by_key(|x| x.abs()), MinMax(0, 3)); + /// ``` + /// + /// [`MinMaxResult`]: enum.MinMaxResult.html + /// [`position_minmax`]: #method.position_minmax + fn position_minmax_by_key(self, mut key: F) -> MinMaxResult + where Self: Sized, K: PartialOrd, F: FnMut(&Self::Item) -> K + { + use crate::MinMaxResult::{NoElements, OneElement, MinMax}; + match self.enumerate().minmax_by_key(|e| key(&e.1)) { + NoElements => NoElements, + OneElement(x) => OneElement(x.0), + MinMax(x, y) => MinMax(x.0, y.0), + } + } + + /// Return the postions of the minimum and maximum elements of an + /// iterator, as determined by the specified comparison function. + /// + /// The return value is a variant of [`MinMaxResult`] like for + /// [`position_minmax`]. + /// + /// For the minimum, if several elements are equally minimum, the + /// position of the first of them is returned. For the maximum, if + /// several elements are equally maximum, the position of the last + /// of them is returned. + /// + /// # Examples + /// + /// ``` + /// use itertools::Itertools; + /// use itertools::MinMaxResult::{NoElements, OneElement, MinMax}; + /// + /// let a: [i32; 0] = []; + /// assert_eq!(a.iter().position_minmax_by(|x, y| x.cmp(y)), NoElements); + /// + /// let a = [10_i32]; + /// assert_eq!(a.iter().position_minmax_by(|x, y| x.cmp(y)), OneElement(0)); + /// + /// let a = [-3_i32, 0, 1, 5, -10]; + /// assert_eq!(a.iter().position_minmax_by(|x, y| x.cmp(y)), MinMax(4, 3)); + /// + /// let a = [1_i32, 1, -1, -1]; + /// assert_eq!(a.iter().position_minmax_by(|x, y| x.cmp(y)), MinMax(2, 1)); + /// ``` + /// + /// [`MinMaxResult`]: enum.MinMaxResult.html + /// [`position_minmax`]: #method.position_minmax + fn position_minmax_by(self, mut compare: F) -> MinMaxResult + where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering + { + use crate::MinMaxResult::{NoElements, OneElement, MinMax}; + match self.enumerate().minmax_by(|x, y| compare(&x.1, &y.1)) { + NoElements => NoElements, + OneElement(x) => OneElement(x.0), + MinMax(x, y) => MinMax(x.0, y.0), + } + } + + /// If the iterator yields exactly one element, that element will be returned, otherwise + /// an error will be returned containing an iterator that has the same output as the input + /// iterator. + /// + /// This provides an additional layer of validation over just calling `Iterator::next()`. + /// If your assumption that there should only be one element yielded is false this provides + /// the opportunity to detect and handle that, preventing errors at a distance. + /// + /// # Examples + /// ``` + /// use itertools::Itertools; + /// + /// assert_eq!((0..10).filter(|&x| x == 2).exactly_one().unwrap(), 2); + /// assert!((0..10).filter(|&x| x > 1 && x < 4).exactly_one().unwrap_err().eq(2..4)); + /// assert!((0..10).filter(|&x| x > 1 && x < 5).exactly_one().unwrap_err().eq(2..5)); + /// assert!((0..10).filter(|&_| false).exactly_one().unwrap_err().eq(0..0)); + /// ``` + fn exactly_one(mut self) -> Result> + where + Self: Sized, + { + match self.next() { + Some(first) => { + match self.next() { + Some(second) => { + Err(ExactlyOneError::new((Some(first), Some(second)), self)) + } + None => { + Ok(first) + } + } + } + None => Err(ExactlyOneError::new((None, None), self)), + } + } } impl Itertools for T where T: Iterator { } diff --git a/third_party/rust/itertools/src/merge_join.rs b/third_party/rust/itertools/src/merge_join.rs index 5f9a0f40132f5..0f87ae42e1eab 100644 --- a/third_party/rust/itertools/src/merge_join.rs +++ b/third_party/rust/itertools/src/merge_join.rs @@ -3,7 +3,7 @@ use std::iter::Fuse; use std::fmt; use super::adaptors::{PutBack, put_back}; -use either_or_both::EitherOrBoth; +use crate::either_or_both::EitherOrBoth; /// Return an iterator adaptor that merge-joins items from the two base iterators in ascending order. /// @@ -17,7 +17,7 @@ pub fn merge_join_by(left: I, right: J, cmp_fn: F) MergeJoinBy { left: put_back(left.into_iter().fuse()), right: put_back(right.into_iter().fuse()), - cmp_fn: cmp_fn + cmp_fn, } } @@ -31,6 +31,16 @@ pub struct MergeJoinBy { cmp_fn: F } +impl Clone for MergeJoinBy + where I: Iterator, + J: Iterator, + PutBack>: Clone, + PutBack>: Clone, + F: Clone, +{ + clone_fields!(left, right, cmp_fn); +} + impl fmt::Debug for MergeJoinBy where I: Iterator + fmt::Debug, I::Item: fmt::Debug, @@ -78,10 +88,80 @@ impl Iterator for MergeJoinBy let lower = ::std::cmp::max(a_lower, b_lower); let upper = match (a_upper, b_upper) { - (Some(x), Some(y)) => Some(x + y), + (Some(x), Some(y)) => x.checked_add(y), _ => None, }; (lower, upper) } + + fn count(mut self) -> usize { + let mut count = 0; + loop { + match (self.left.next(), self.right.next()) { + (None, None) => break count, + (Some(_left), None) => break count + 1 + self.left.into_parts().1.count(), + (None, Some(_right)) => break count + 1 + self.right.into_parts().1.count(), + (Some(left), Some(right)) => { + count += 1; + match (self.cmp_fn)(&left, &right) { + Ordering::Equal => {} + Ordering::Less => self.right.put_back(right), + Ordering::Greater => self.left.put_back(left), + } + } + } + } + } + + fn last(mut self) -> Option { + let mut previous_element = None; + loop { + match (self.left.next(), self.right.next()) { + (None, None) => break previous_element, + (Some(left), None) => { + break Some(EitherOrBoth::Left( + self.left.into_parts().1.last().unwrap_or(left), + )) + } + (None, Some(right)) => { + break Some(EitherOrBoth::Right( + self.right.into_parts().1.last().unwrap_or(right), + )) + } + (Some(left), Some(right)) => { + previous_element = match (self.cmp_fn)(&left, &right) { + Ordering::Equal => Some(EitherOrBoth::Both(left, right)), + Ordering::Less => { + self.right.put_back(right); + Some(EitherOrBoth::Left(left)) + } + Ordering::Greater => { + self.left.put_back(left); + Some(EitherOrBoth::Right(right)) + } + } + } + } + } + } + + fn nth(&mut self, mut n: usize) -> Option { + loop { + if n == 0 { + break self.next(); + } + n -= 1; + match (self.left.next(), self.right.next()) { + (None, None) => break None, + (Some(_left), None) => break self.left.nth(n).map(EitherOrBoth::Left), + (None, Some(_right)) => break self.right.nth(n).map(EitherOrBoth::Right), + (Some(left), Some(right)) => match (self.cmp_fn)(&left, &right) { + Ordering::Equal => {} + Ordering::Less => self.right.put_back(right), + Ordering::Greater => self.left.put_back(left), + }, + } + } + } } diff --git a/third_party/rust/itertools/src/multipeek_impl.rs b/third_party/rust/itertools/src/multipeek_impl.rs index a6a2fb33ebc46..0b64e1d7ad64a 100644 --- a/third_party/rust/itertools/src/multipeek_impl.rs +++ b/third_party/rust/itertools/src/multipeek_impl.rs @@ -1,9 +1,7 @@ - - use std::iter::Fuse; use std::collections::VecDeque; -use size_hint; -use PeekingNext; +use crate::size_hint; +use crate::PeekingNext; /// See [`multipeek()`](../fn.multipeek.html) for more information. #[derive(Clone, Debug)] diff --git a/third_party/rust/itertools/src/pad_tail.rs b/third_party/rust/itertools/src/pad_tail.rs index c9cfe6af30b86..8d9d5ffa53774 100644 --- a/third_party/rust/itertools/src/pad_tail.rs +++ b/third_party/rust/itertools/src/pad_tail.rs @@ -1,5 +1,5 @@ use std::iter::Fuse; -use size_hint; +use crate::size_hint; /// An iterator adaptor that pads a sequence to a minimum length by filling /// missing elements using a closure. @@ -23,9 +23,9 @@ pub fn pad_using(iter: I, min: usize, filler: F) -> PadUsing { PadUsing { iter: iter.fuse(), - min: min, + min, pos: 0, - filler: filler, + filler, } } diff --git a/third_party/rust/itertools/src/peeking_take_while.rs b/third_party/rust/itertools/src/peeking_take_while.rs index 0b2291dfdd9c1..b404904d1a403 100644 --- a/third_party/rust/itertools/src/peeking_take_while.rs +++ b/third_party/rust/itertools/src/peeking_take_while.rs @@ -1,8 +1,7 @@ - use std::iter::Peekable; -use PutBack; +use crate::PutBack; #[cfg(feature = "use_std")] -use PutBackN; +use crate::PutBackN; /// An iterator that allows peeking at an element before deciding to accept it. /// @@ -89,8 +88,8 @@ pub fn peeking_take_while(iter: &mut I, f: F) -> PeekingTakeWhile where I: Iterator, { PeekingTakeWhile { - iter: iter, - f: f, + iter, + f, } } diff --git a/third_party/rust/itertools/src/permutations.rs b/third_party/rust/itertools/src/permutations.rs new file mode 100644 index 0000000000000..fb4dd5085afdc --- /dev/null +++ b/third_party/rust/itertools/src/permutations.rs @@ -0,0 +1,279 @@ +use std::fmt; +use std::iter::once; + +use super::lazy_buffer::LazyBuffer; + +/// An iterator adaptor that iterates through all the `k`-permutations of the +/// elements from an iterator. +/// +/// See [`.permutations()`](../trait.Itertools.html#method.permutations) for +/// more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Permutations { + vals: LazyBuffer, + state: PermutationState, +} + +impl Clone for Permutations + where I: Clone + Iterator, + I::Item: Clone, +{ + clone_fields!(vals, state); +} + +#[derive(Clone, Debug)] +enum PermutationState { + StartUnknownLen { + k: usize, + }, + OngoingUnknownLen { + k: usize, + min_n: usize, + }, + Complete(CompleteState), + Empty, +} + +#[derive(Clone, Debug)] +enum CompleteState { + Start { + n: usize, + k: usize, + }, + Ongoing { + indices: Vec, + cycles: Vec, + } +} + +enum CompleteStateRemaining { + Known(usize), + Overflow, +} + +impl fmt::Debug for Permutations + where I: Iterator + fmt::Debug, + I::Item: fmt::Debug, +{ + debug_fmt_fields!(Permutations, vals, state); +} + +pub fn permutations(iter: I, k: usize) -> Permutations { + let mut vals = LazyBuffer::new(iter); + + if k == 0 { + // Special case, yields single empty vec; `n` is irrelevant + let state = PermutationState::Complete(CompleteState::Start { n: 0, k: 0 }); + + return Permutations { + vals, + state + }; + } + + let mut enough_vals = true; + + while vals.len() < k { + if !vals.get_next() { + enough_vals = false; + break; + } + } + + let state = if enough_vals { + PermutationState::StartUnknownLen { k } + } else { + PermutationState::Empty + }; + + Permutations { + vals, + state + } +} + +impl Iterator for Permutations +where + I: Iterator, + I::Item: Clone +{ + type Item = Vec; + + fn next(&mut self) -> Option { + self.advance(); + + let &mut Permutations { ref vals, ref state } = self; + + match state { + &PermutationState::StartUnknownLen { .. } => panic!("unexpected iterator state"), + &PermutationState::OngoingUnknownLen { k, min_n } => { + let latest_idx = min_n - 1; + let indices = (0..(k - 1)).chain(once(latest_idx)); + + Some(indices.map(|i| vals[i].clone()).collect()) + } + &PermutationState::Complete(CompleteState::Start { .. }) => None, + &PermutationState::Complete(CompleteState::Ongoing { ref indices, ref cycles }) => { + let k = cycles.len(); + + Some(indices[0..k].iter().map(|&i| vals[i].clone()).collect()) + }, + &PermutationState::Empty => None + } + } + + fn count(self) -> usize { + let Permutations { vals, state } = self; + + fn from_complete(complete_state: CompleteState) -> usize { + match complete_state.remaining() { + CompleteStateRemaining::Known(count) => count, + CompleteStateRemaining::Overflow => { + panic!("Iterator count greater than usize::MAX"); + } + } + } + + match state { + PermutationState::StartUnknownLen { k } => { + let n = vals.len() + vals.it.count(); + let complete_state = CompleteState::Start { n, k }; + + from_complete(complete_state) + } + PermutationState::OngoingUnknownLen { k, min_n } => { + let prev_iteration_count = min_n - k + 1; + let n = vals.len() + vals.it.count(); + let complete_state = CompleteState::Start { n, k }; + + from_complete(complete_state) - prev_iteration_count + }, + PermutationState::Complete(state) => from_complete(state), + PermutationState::Empty => 0 + } + } + + fn size_hint(&self) -> (usize, Option) { + match self.state { + PermutationState::StartUnknownLen { .. } | + PermutationState::OngoingUnknownLen { .. } => (0, None), // TODO can we improve this lower bound? + PermutationState::Complete(ref state) => match state.remaining() { + CompleteStateRemaining::Known(count) => (count, Some(count)), + CompleteStateRemaining::Overflow => (::std::usize::MAX, None) + } + PermutationState::Empty => (0, Some(0)) + } + } +} + +impl Permutations +where + I: Iterator, + I::Item: Clone +{ + fn advance(&mut self) { + let &mut Permutations { ref mut vals, ref mut state } = self; + + *state = match state { + &mut PermutationState::StartUnknownLen { k } => { + PermutationState::OngoingUnknownLen { k, min_n: k } + } + &mut PermutationState::OngoingUnknownLen { k, min_n } => { + if vals.get_next() { + PermutationState::OngoingUnknownLen { k, min_n: min_n + 1 } + } else { + let n = min_n; + let prev_iteration_count = n - k + 1; + let mut complete_state = CompleteState::Start { n, k }; + + // Advance the complete-state iterator to the correct point + for _ in 0..(prev_iteration_count + 1) { + complete_state.advance(); + } + + PermutationState::Complete(complete_state) + } + } + &mut PermutationState::Complete(ref mut state) => { + state.advance(); + + return; + } + &mut PermutationState::Empty => { return; } + }; + } +} + +impl CompleteState { + fn advance(&mut self) { + *self = match self { + &mut CompleteState::Start { n, k } => { + let indices = (0..n).collect(); + let cycles = ((n - k)..n).rev().collect(); + + CompleteState::Ongoing { + cycles, + indices + } + }, + &mut CompleteState::Ongoing { ref mut indices, ref mut cycles } => { + let n = indices.len(); + let k = cycles.len(); + + for i in (0..k).rev() { + if cycles[i] == 0 { + cycles[i] = n - i - 1; + + let to_push = indices.remove(i); + indices.push(to_push); + } else { + let swap_index = n - cycles[i]; + indices.swap(i, swap_index); + + cycles[i] -= 1; + return; + } + } + + CompleteState::Start { n, k } + } + } + } + + fn remaining(&self) -> CompleteStateRemaining { + use self::CompleteStateRemaining::{Known, Overflow}; + + match self { + &CompleteState::Start { n, k } => { + if n < k { + return Known(0); + } + + let count: Option = (n - k + 1..n + 1).fold(Some(1), |acc, i| { + acc.and_then(|acc| acc.checked_mul(i)) + }); + + match count { + Some(count) => Known(count), + None => Overflow + } + } + &CompleteState::Ongoing { ref indices, ref cycles } => { + let mut count: usize = 0; + + for (i, &c) in cycles.iter().enumerate() { + let radix = indices.len() - i; + let next_count = count.checked_mul(radix) + .and_then(|count| count.checked_add(c)); + + count = match next_count { + Some(count) => count, + None => { return Overflow; } + }; + } + + Known(count) + } + } + } +} diff --git a/third_party/rust/itertools/src/process_results_impl.rs b/third_party/rust/itertools/src/process_results_impl.rs index f78515d77f817..c819f50290689 100644 --- a/third_party/rust/itertools/src/process_results_impl.rs +++ b/third_party/rust/itertools/src/process_results_impl.rs @@ -75,7 +75,7 @@ pub fn process_results(iterable: I, processor: F) -> Result let iter = iterable.into_iter(); let mut error = Ok(()); - let result = processor(ProcessResults { error: &mut error, iter: iter }); + let result = processor(ProcessResults { error: &mut error, iter }); error.map(|_| result) } diff --git a/third_party/rust/itertools/src/put_back_n_impl.rs b/third_party/rust/itertools/src/put_back_n_impl.rs index cc083207144ed..dcb28946e92b5 100644 --- a/third_party/rust/itertools/src/put_back_n_impl.rs +++ b/third_party/rust/itertools/src/put_back_n_impl.rs @@ -1,4 +1,4 @@ -use size_hint; +use crate::size_hint; /// An iterator adaptor that allows putting multiple /// items in front of the iterator. diff --git a/third_party/rust/itertools/src/rciter_impl.rs b/third_party/rust/itertools/src/rciter_impl.rs index 1c3b03b5bcd6d..6ee90ad310c12 100644 --- a/third_party/rust/itertools/src/rciter_impl.rs +++ b/third_party/rust/itertools/src/rciter_impl.rs @@ -52,9 +52,7 @@ pub fn rciter(iterable: I) -> RcIter impl Clone for RcIter { #[inline] - fn clone(&self) -> RcIter { - RcIter { rciter: self.rciter.clone() } - } + clone_fields!(rciter); } impl Iterator for RcIter diff --git a/third_party/rust/itertools/src/repeatn.rs b/third_party/rust/itertools/src/repeatn.rs index 1c7c310014cde..8bc485083ff02 100644 --- a/third_party/rust/itertools/src/repeatn.rs +++ b/third_party/rust/itertools/src/repeatn.rs @@ -3,7 +3,7 @@ /// /// See [`repeat_n()`](../fn.repeat_n.html) for more information. #[must_use = "iterators are lazy and do nothing unless consumed"] -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct RepeatN { elt: Option, n: usize, @@ -14,9 +14,9 @@ pub fn repeat_n(element: A, n: usize) -> RepeatN where A: Clone, { if n == 0 { - RepeatN { elt: None, n: n, } + RepeatN { elt: None, n, } } else { - RepeatN { elt: Some(element), n: n, } + RepeatN { elt: Some(element), n, } } } diff --git a/third_party/rust/itertools/src/sources.rs b/third_party/rust/itertools/src/sources.rs index a579f3d9c2253..17d9ff92c4ee3 100644 --- a/third_party/rust/itertools/src/sources.rs +++ b/third_party/rust/itertools/src/sources.rs @@ -6,6 +6,7 @@ use std::fmt; use std::mem; /// See [`repeat_call`](../fn.repeat_call.html) for more information. +#[derive(Clone)] #[deprecated(note="Use std repeat_with() instead", since="0.8")] pub struct RepeatCall { f: F, @@ -101,7 +102,7 @@ pub fn unfold(initial_state: St, f: F) -> Unfold where F: FnMut(&mut St) -> Option { Unfold { - f: f, + f, state: initial_state, } } @@ -185,6 +186,6 @@ pub fn iterate(initial_value: St, f: F) -> Iterate { Iterate { state: initial_value, - f: f, + f, } } diff --git a/third_party/rust/itertools/src/tee.rs b/third_party/rust/itertools/src/tee.rs index 77d261759d1ef..aa4be41be2f58 100644 --- a/third_party/rust/itertools/src/tee.rs +++ b/third_party/rust/itertools/src/tee.rs @@ -28,7 +28,7 @@ pub struct Tee pub fn new(iter: I) -> (Tee, Tee) where I: Iterator { - let buffer = TeeBuffer{backlog: VecDeque::new(), iter: iter, owner: false}; + let buffer = TeeBuffer{backlog: VecDeque::new(), iter, owner: false}; let t1 = Tee{rcbuffer: Rc::new(RefCell::new(buffer)), id: true}; let t2 = Tee{rcbuffer: t1.rcbuffer.clone(), id: false}; (t1, t2) diff --git a/third_party/rust/itertools/src/tuple_impl.rs b/third_party/rust/itertools/src/tuple_impl.rs index 0daa7800c1472..f205f01b3c60e 100644 --- a/third_party/rust/itertools/src/tuple_impl.rs +++ b/third_party/rust/itertools/src/tuple_impl.rs @@ -2,31 +2,43 @@ use std::iter::Fuse; +// `HomogeneousTuple` is a public facade for `TupleCollect`, allowing +// tuple-related methods to be used by clients in generic contexts, while +// hiding the implementation details of `TupleCollect`. +// See https://github.com/rust-itertools/itertools/issues/387 + +/// Implemented for homogeneous tuples of size up to 4. +pub trait HomogeneousTuple + : TupleCollect +{} + +impl HomogeneousTuple for T {} + /// An iterator over a incomplete tuple. /// /// See [`.tuples()`](../trait.Itertools.html#method.tuples) and /// [`Tuples::into_buffer()`](struct.Tuples.html#method.into_buffer). -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct TupleBuffer - where T: TupleCollect + where T: HomogeneousTuple { cur: usize, buf: T::Buffer, } impl TupleBuffer - where T: TupleCollect + where T: HomogeneousTuple { fn new(buf: T::Buffer) -> Self { TupleBuffer { cur: 0, - buf: buf, + buf, } } } impl Iterator for TupleBuffer - where T: TupleCollect + where T: HomogeneousTuple { type Item = T::Item; @@ -54,17 +66,18 @@ impl Iterator for TupleBuffer } impl ExactSizeIterator for TupleBuffer - where T: TupleCollect + where T: HomogeneousTuple { } /// An iterator that groups the items in tuples of a specific size. /// /// See [`.tuples()`](../trait.Itertools.html#method.tuples) for more information. +#[derive(Clone)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Tuples where I: Iterator, - T: TupleCollect + T: HomogeneousTuple { iter: Fuse, buf: T::Buffer, @@ -73,7 +86,7 @@ pub struct Tuples /// Create a new tuples iterator. pub fn tuples(iter: I) -> Tuples where I: Iterator, - T: TupleCollect + T: HomogeneousTuple { Tuples { iter: iter.fuse(), @@ -83,7 +96,7 @@ pub fn tuples(iter: I) -> Tuples impl Iterator for Tuples where I: Iterator, - T: TupleCollect + T: HomogeneousTuple { type Item = T; @@ -94,7 +107,7 @@ impl Iterator for Tuples impl Tuples where I: Iterator, - T: TupleCollect + T: HomogeneousTuple { /// Return a buffer with the produced items that was not enough to be grouped in a tuple. /// @@ -117,10 +130,10 @@ impl Tuples /// See [`.tuple_windows()`](../trait.Itertools.html#method.tuple_windows) for more /// information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct TupleWindows where I: Iterator, - T: TupleCollect + T: HomogeneousTuple { iter: I, last: Option, @@ -129,7 +142,7 @@ pub struct TupleWindows /// Create a new tuple windows iterator. pub fn tuple_windows(mut iter: I) -> TupleWindows where I: Iterator, - T: TupleCollect, + T: HomogeneousTuple, T::Item: Clone { use std::iter::once; @@ -145,14 +158,14 @@ pub fn tuple_windows(mut iter: I) -> TupleWindows } TupleWindows { - last: last, - iter: iter, + last, + iter, } } impl Iterator for TupleWindows where I: Iterator, - T: TupleCollect + Clone, + T: HomogeneousTuple + Clone, T::Item: Clone { type Item = T; diff --git a/third_party/rust/itertools/src/unique_impl.rs b/third_party/rust/itertools/src/unique_impl.rs index d9e7fd3dc8ff6..cf8675c1885dc 100644 --- a/third_party/rust/itertools/src/unique_impl.rs +++ b/third_party/rust/itertools/src/unique_impl.rs @@ -30,9 +30,9 @@ pub fn unique_by(iter: I, f: F) -> UniqueBy I: Iterator, { UniqueBy { - iter: iter, + iter, used: HashMap::new(), - f: f, + f, } } @@ -126,7 +126,7 @@ pub fn unique(iter: I) -> Unique { Unique { iter: UniqueBy { - iter: iter, + iter, used: HashMap::new(), f: (), } diff --git a/third_party/rust/itertools/src/with_position.rs b/third_party/rust/itertools/src/with_position.rs index 2a7c2b8ad6a20..1440fb6f519d0 100644 --- a/third_party/rust/itertools/src/with_position.rs +++ b/third_party/rust/itertools/src/with_position.rs @@ -13,6 +13,13 @@ pub struct WithPosition peekable: Peekable>, } +impl Clone for WithPosition + where I: Clone + Iterator, + I::Item: Clone, +{ + clone_fields!(handled_first, peekable); +} + /// Create a new `WithPosition` iterator. pub fn with_position(iter: I) -> WithPosition where I: Iterator, diff --git a/third_party/rust/itertools/src/zip_longest.rs b/third_party/rust/itertools/src/zip_longest.rs index 68a381acee3f6..1395c8428cd48 100644 --- a/third_party/rust/itertools/src/zip_longest.rs +++ b/third_party/rust/itertools/src/zip_longest.rs @@ -2,7 +2,7 @@ use std::cmp::Ordering::{Equal, Greater, Less}; use super::size_hint; use std::iter::Fuse; -use either_or_both::EitherOrBoth; +use crate::either_or_both::EitherOrBoth; // ZipLongest originally written by SimonSapin, // and dedicated to itertools https://github.com/rust-lang/rust/pull/19283 diff --git a/third_party/rust/itertools/tests/adaptors_no_collect.rs b/third_party/rust/itertools/tests/adaptors_no_collect.rs new file mode 100644 index 0000000000000..a47f906f9f282 --- /dev/null +++ b/third_party/rust/itertools/tests/adaptors_no_collect.rs @@ -0,0 +1,47 @@ +use itertools::Itertools; + +struct PanickingCounter { + curr: usize, + max: usize, +} + +impl Iterator for PanickingCounter { + type Item = (); + + fn next(&mut self) -> Option { + self.curr += 1; + + if self.curr == self.max { + panic!( + "Input iterator reached maximum of {} suggesting collection by adaptor", + self.max + ); + } + + Some(()) + } +} + +fn no_collect_test(to_adaptor: T) + where A: Iterator, T: Fn(PanickingCounter) -> A +{ + let counter = PanickingCounter { curr: 0, max: 10_000 }; + let adaptor = to_adaptor(counter); + + for _ in adaptor.take(5) {} +} + +#[test] +fn permutations_no_collect() { + no_collect_test(|iter| iter.permutations(5)) +} + +#[test] +fn combinations_no_collect() { + no_collect_test(|iter| iter.combinations(5)) +} + +#[test] +fn combinations_with_replacement_no_collect() { + no_collect_test(|iter| iter.combinations_with_replacement(5)) +} \ No newline at end of file diff --git a/third_party/rust/itertools/tests/fold_specialization.rs b/third_party/rust/itertools/tests/fold_specialization.rs new file mode 100644 index 0000000000000..a984b40b8a89e --- /dev/null +++ b/third_party/rust/itertools/tests/fold_specialization.rs @@ -0,0 +1,13 @@ +use itertools::Itertools; + +#[test] +fn specialization_intersperse() { + let mut iter = (1..2).intersperse(0); + iter.clone().for_each(|x| assert_eq!(Some(x), iter.next())); + + let mut iter = (1..3).intersperse(0); + iter.clone().for_each(|x| assert_eq!(Some(x), iter.next())); + + let mut iter = (1..4).intersperse(0); + iter.clone().for_each(|x| assert_eq!(Some(x), iter.next())); +} diff --git a/third_party/rust/itertools/tests/merge_join.rs b/third_party/rust/itertools/tests/merge_join.rs index 41829202b8d29..3280b7d4ecb08 100644 --- a/third_party/rust/itertools/tests/merge_join.rs +++ b/third_party/rust/itertools/tests/merge_join.rs @@ -1,5 +1,3 @@ -extern crate itertools; - use itertools::EitherOrBoth; use itertools::free::merge_join_by; diff --git a/third_party/rust/itertools/tests/peeking_take_while.rs b/third_party/rust/itertools/tests/peeking_take_while.rs index 45c76c2d5c9b4..a1147027e82b4 100644 --- a/third_party/rust/itertools/tests/peeking_take_while.rs +++ b/third_party/rust/itertools/tests/peeking_take_while.rs @@ -1,6 +1,3 @@ - -extern crate itertools; - use itertools::Itertools; use itertools::{put_back, put_back_n}; diff --git a/third_party/rust/itertools/tests/quick.rs b/third_party/rust/itertools/tests/quick.rs index e0c61b47deb6b..683ba7ae434aa 100644 --- a/third_party/rust/itertools/tests/quick.rs +++ b/third_party/rust/itertools/tests/quick.rs @@ -3,20 +3,17 @@ //! //! In particular we test the tedious size_hint and exact size correctness. -#[macro_use] extern crate itertools; - -extern crate quickcheck; -extern crate rand; - -use std::default::Default; - use quickcheck as qc; +use std::default::Default; use std::ops::Range; -use std::cmp::Ordering; +use std::cmp::{max, min, Ordering}; +use std::collections::HashSet; use itertools::Itertools; use itertools::{ multizip, EitherOrBoth, + iproduct, + izip, }; use itertools::free::{ cloned, @@ -85,7 +82,7 @@ impl qc::Arbitrary for Inexact { } } - fn shrink(&self) -> Box> { + fn shrink(&self) -> Box> { let underestimate_value = self.underestimate; let overestimate_value = self.overestimate; Box::new( @@ -123,7 +120,7 @@ impl Iter where HK: HintKind Iter { iterator: it, fuse_flag: 0, - hint_kind: hint_kind + hint_kind, } } } @@ -176,7 +173,7 @@ impl qc::Arbitrary for Iter Iter::new(T::arbitrary(g)..T::arbitrary(g), HK::arbitrary(g)) } - fn shrink(&self) -> Box>> + fn shrink(&self) -> Box>> { let r = self.iterator.clone(); let hint_kind = self.hint_kind; @@ -238,16 +235,53 @@ impl qc::Arbitrary for ShiftRange let hint_kind = qc::Arbitrary::arbitrary(g); ShiftRange { - range_start: range_start, - range_end: range_end, - start_step: start_step, - end_step: end_step, - iter_count: iter_count, - hint_kind: hint_kind + range_start, + range_end, + start_step, + end_step, + iter_count, + hint_kind, } } } +fn correct_count(get_it: F) -> bool +where + I: Iterator, + F: Fn() -> I +{ + let mut counts = vec![get_it().count()]; + + 'outer: loop { + let mut it = get_it(); + + for _ in 0..(counts.len() - 1) { + if let None = it.next() { + panic!("Iterator shouldn't be finished, may not be deterministic"); + } + } + + if let None = it.next() { + break 'outer; + } + + counts.push(it.count()); + } + + let total_actual_count = counts.len() - 1; + + for (i, returned_count) in counts.into_iter().enumerate() { + let actual_count = total_actual_count - i; + if actual_count != returned_count { + println!("Total iterations: {} True count: {} returned count: {}", i, actual_count, returned_count); + + return false; + } + } + + true +} + fn correct_size_hint(mut it: I) -> bool { // record size hint at each iteration let initial_hint = it.size_hint(); @@ -625,6 +659,88 @@ quickcheck! { let size = a.clone().count(); a.collect_tuple::<(_, _, _)>().is_some() == (size == 3) } + + fn correct_permutations(vals: HashSet, k: usize) -> () { + // Test permutations only on iterators of distinct integers, to prevent + // false positives. + + const MAX_N: usize = 5; + + let n = min(vals.len(), MAX_N); + let vals: HashSet = vals.into_iter().take(n).collect(); + + let perms = vals.iter().permutations(k); + + let mut actual = HashSet::new(); + + for perm in perms { + assert_eq!(perm.len(), k); + + let all_items_valid = perm.iter().all(|p| vals.contains(p)); + assert!(all_items_valid, "perm contains value not from input: {:?}", perm); + + // Check that all perm items are distinct + let distinct_len = { + let perm_set: HashSet<_> = perm.iter().collect(); + perm_set.len() + }; + assert_eq!(perm.len(), distinct_len); + + // Check that the perm is new + assert!(actual.insert(perm.clone()), "perm already encountered: {:?}", perm); + } + } + + fn permutations_lexic_order(a: usize, b: usize) -> () { + let a = a % 6; + let b = b % 6; + + let n = max(a, b); + let k = min (a, b); + + let expected_first: Vec = (0..k).collect(); + let expected_last: Vec = ((n - k)..n).rev().collect(); + + let mut perms = (0..n).permutations(k); + + let mut curr_perm = match perms.next() { + Some(p) => p, + None => { return; } + }; + + assert_eq!(expected_first, curr_perm); + + while let Some(next_perm) = perms.next() { + assert!( + next_perm > curr_perm, + "next perm isn't greater-than current; next_perm={:?} curr_perm={:?} n={}", + next_perm, curr_perm, n + ); + + curr_perm = next_perm; + } + + assert_eq!(expected_last, curr_perm); + + } + + fn permutations_count(n: usize, k: usize) -> bool { + let n = n % 6; + + correct_count(|| (0..n).permutations(k)) + } + + fn permutations_size(a: Iter, k: usize) -> bool { + correct_size_hint(a.take(5).permutations(k)) + } + + fn permutations_k0_yields_once(n: usize) -> () { + let k = 0; + let expected: Vec> = vec![vec![]]; + let actual = (0..n).permutations(k).collect_vec(); + + assert_eq!(expected, actual); + } } quickcheck! { @@ -635,12 +751,26 @@ quickcheck! { } } +quickcheck! { + fn equal_dedup_by(a: Vec<(i32, i32)>) -> bool { + let mut b = a.clone(); + b.dedup_by(|x, y| x.0==y.0); + itertools::equal(&b, a.iter().dedup_by(|x, y| x.0==y.0)) + } +} + quickcheck! { fn size_dedup(a: Vec) -> bool { correct_size_hint(a.iter().dedup()) } } +quickcheck! { + fn size_dedup_by(a: Vec<(i32, i32)>) -> bool { + correct_size_hint(a.iter().dedup_by(|x, y| x.0==y.0)) + } +} + quickcheck! { fn exact_repeatn((n, x): (usize, i32)) -> bool { let it = itertools::repeat_n(x, n); @@ -942,7 +1072,7 @@ impl qc::Arbitrary for Val { let (x, y) = <(u32, u32)>::arbitrary(g); Val(x, y) } - fn shrink(&self) -> Box> { + fn shrink(&self) -> Box> { Box::new((self.0, self.1).shrink().map(|(x, y)| Val(x, y))) } } @@ -1015,3 +1145,13 @@ quickcheck! { TestResult::from_bool(actual == expected) } } + +quickcheck! { + fn exactly_one_i32(a: Vec) -> TestResult { + let ret = a.iter().cloned().exactly_one(); + match a.len() { + 1 => TestResult::from_bool(ret.unwrap() == a[0]), + _ => TestResult::from_bool(ret.unwrap_err().eq(a.iter().cloned())), + } + } +} diff --git a/third_party/rust/itertools/tests/specializations.rs b/third_party/rust/itertools/tests/specializations.rs new file mode 100644 index 0000000000000..ef51bbedf0615 --- /dev/null +++ b/third_party/rust/itertools/tests/specializations.rs @@ -0,0 +1,149 @@ +use itertools::{EitherOrBoth, Itertools}; +use std::fmt::Debug; +use std::ops::BitXor; +use quickcheck::quickcheck; + +struct Unspecialized(I); +impl Iterator for Unspecialized +where + I: Iterator, +{ + type Item = I::Item; + + #[inline(always)] + fn next(&mut self) -> Option { + self.0.next() + } + + #[inline(always)] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} + +fn check_specialized<'a, V, IterItem, Iter, F>(iterator: &Iter, mapper: F) +where + V: Eq + Debug, + IterItem: 'a, + Iter: Iterator + Clone + 'a, + F: Fn(Box + 'a>) -> V, +{ + assert_eq!( + mapper(Box::new(Unspecialized(iterator.clone()))), + mapper(Box::new(iterator.clone())) + ) +} + +fn check_specialized_count_last_nth_sizeh<'a, IterItem, Iter>( + it: &Iter, + known_expected_size: Option, +) where + IterItem: 'a + Eq + Debug, + Iter: Iterator + Clone + 'a, +{ + let size = it.clone().count(); + if let Some(expected_size) = known_expected_size { + assert_eq!(size, expected_size); + } + check_specialized(it, |i| i.count()); + check_specialized(it, |i| i.last()); + for n in 0..size + 2 { + check_specialized(it, |mut i| i.nth(n)); + } + let mut it_sh = it.clone(); + for n in 0..size + 2 { + let len = it_sh.clone().count(); + let (min, max) = it_sh.size_hint(); + assert_eq!((size - n.min(size)), len); + assert!(min <= len); + if let Some(max) = max { + assert!(len <= max); + } + it_sh.next(); + } +} + +fn check_specialized_fold_xor<'a, IterItem, Iter>(it: &Iter) +where + IterItem: 'a + + BitXor + + Eq + + Debug + + BitXor<::Output, Output = ::Output> + + Clone, + ::Output: + BitXor::Output> + Eq + Debug + Clone, + Iter: Iterator + Clone + 'a, +{ + check_specialized(it, |mut i| { + let first = i.next().map(|f| f.clone() ^ (f.clone() ^ f)); + i.fold(first, |acc, v: IterItem| acc.map(move |a| v ^ a)) + }); +} + +fn put_back_test(test_vec: Vec, known_expected_size: Option) { + { + // Lexical lifetimes support + let pb = itertools::put_back(test_vec.iter()); + check_specialized_count_last_nth_sizeh(&pb, known_expected_size); + check_specialized_fold_xor(&pb); + } + + let mut pb = itertools::put_back(test_vec.into_iter()); + pb.put_back(1); + check_specialized_count_last_nth_sizeh(&pb, known_expected_size.map(|x| x + 1)); + check_specialized_fold_xor(&pb) +} + +#[test] +fn put_back() { + put_back_test(vec![7, 4, 1], Some(3)); +} + +quickcheck! { + fn put_back_qc(test_vec: Vec) -> () { + put_back_test(test_vec, None) + } +} + +fn merge_join_by_test(i1: Vec, i2: Vec, known_expected_size: Option) { + let i1 = i1.into_iter(); + let i2 = i2.into_iter(); + let mjb = i1.clone().merge_join_by(i2.clone(), std::cmp::Ord::cmp); + check_specialized_count_last_nth_sizeh(&mjb, known_expected_size); + // Rust 1.24 compatibility: + fn eob_left_z(eob: EitherOrBoth) -> usize { + eob.left().unwrap_or(0) + } + fn eob_right_z(eob: EitherOrBoth) -> usize { + eob.left().unwrap_or(0) + } + fn eob_both_z(eob: EitherOrBoth) -> usize { + let (a, b) = eob.both().unwrap_or((0, 0)); + assert_eq!(a, b); + a + } + check_specialized_fold_xor(&mjb.clone().map(eob_left_z)); + check_specialized_fold_xor(&mjb.clone().map(eob_right_z)); + check_specialized_fold_xor(&mjb.clone().map(eob_both_z)); + + // And the other way around + let mjb = i2.merge_join_by(i1, std::cmp::Ord::cmp); + check_specialized_count_last_nth_sizeh(&mjb, known_expected_size); + check_specialized_fold_xor(&mjb.clone().map(eob_left_z)); + check_specialized_fold_xor(&mjb.clone().map(eob_right_z)); + check_specialized_fold_xor(&mjb.clone().map(eob_both_z)); +} + +#[test] +fn merge_join_by() { + let i1 = vec![1, 3, 5, 7, 8, 9]; + let i2 = vec![0, 3, 4, 5]; + merge_join_by_test(i1, i2, Some(8)); +} + +quickcheck! { + fn merge_join_by_qc(i1: Vec, i2: Vec) -> () { + merge_join_by_test(i1, i2, None) + } +} diff --git a/third_party/rust/itertools/tests/test_core.rs b/third_party/rust/itertools/tests/test_core.rs index cf97abd3603a0..5861653da8edf 100644 --- a/third_party/rust/itertools/tests/test_core.rs +++ b/third_party/rust/itertools/tests/test_core.rs @@ -5,14 +5,14 @@ //! except according to those terms. #![no_std] -#[macro_use] extern crate itertools as it; - use core::iter; - -use it::Itertools; -use it::interleave; -use it::multizip; -use it::free::put_back; +use itertools as it; +use crate::it::Itertools; +use crate::it::interleave; +use crate::it::multizip; +use crate::it::free::put_back; +use crate::it::iproduct; +use crate::it::izip; #[test] fn product2() { @@ -244,3 +244,29 @@ fn tree_fold1() { assert_eq!((0..i).tree_fold1(|x, y| x + y), (0..i).fold1(|x, y| x + y)); } } + +#[test] +fn exactly_one() { + assert_eq!((0..10).filter(|&x| x == 2).exactly_one().unwrap(), 2); + assert!((0..10).filter(|&x| x > 1 && x < 4).exactly_one().unwrap_err().eq(2..4)); + assert!((0..10).filter(|&x| x > 1 && x < 5).exactly_one().unwrap_err().eq(2..5)); + assert!((0..10).filter(|&_| false).exactly_one().unwrap_err().eq(0..0)); +} + +#[test] +fn sum1() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(v[..0].iter().cloned().sum1::(), None); + assert_eq!(v[1..2].iter().cloned().sum1::(), Some(1)); + assert_eq!(v[1..3].iter().cloned().sum1::(), Some(3)); + assert_eq!(v.iter().cloned().sum1::(), Some(55)); +} + +#[test] +fn product1() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + assert_eq!(v[..0].iter().cloned().product1::(), None); + assert_eq!(v[..1].iter().cloned().product1::(), Some(0)); + assert_eq!(v[1..3].iter().cloned().product1::(), Some(2)); + assert_eq!(v[1..5].iter().cloned().product1::(), Some(24)); +} diff --git a/third_party/rust/itertools/tests/test_std.rs b/third_party/rust/itertools/tests/test_std.rs index b587f447060ce..ba077848c51f2 100644 --- a/third_party/rust/itertools/tests/test_std.rs +++ b/third_party/rust/itertools/tests/test_std.rs @@ -1,14 +1,14 @@ - -#[macro_use] extern crate itertools as it; -extern crate permutohedron; - -use it::Itertools; -use it::multizip; -use it::multipeek; -use it::free::rciter; -use it::free::put_back_n; -use it::FoldWhile; -use it::cloned; +use permutohedron; +use itertools as it; +use crate::it::Itertools; +use crate::it::multizip; +use crate::it::multipeek; +use crate::it::free::rciter; +use crate::it::free::put_back_n; +use crate::it::FoldWhile; +use crate::it::cloned; +use crate::it::iproduct; +use crate::it::izip; #[test] fn product3() { @@ -98,8 +98,26 @@ fn dedup() { assert_eq!(&xs_d, &ys); } +#[test] +fn dedup_by() { + let xs = [(0, 0), (0, 1), (1, 1), (2, 1), (0, 2), (3, 1), (0, 3), (1, 3)]; + let ys = [(0, 0), (0, 1), (0, 2), (3, 1), (0, 3)]; + it::assert_equal(ys.iter(), xs.iter().dedup_by(|x, y| x.1==y.1)); + let xs = [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]; + let ys = [(0, 1)]; + it::assert_equal(ys.iter(), xs.iter().dedup_by(|x, y| x.0==y.0)); + + let xs = [(0, 0), (0, 1), (1, 1), (2, 1), (0, 2), (3, 1), (0, 3), (1, 3)]; + let ys = [(0, 0), (0, 1), (0, 2), (3, 1), (0, 3)]; + let mut xs_d = Vec::new(); + xs.iter().dedup_by(|x, y| x.1==y.1).fold((), |(), &elt| xs_d.push(elt)); + assert_eq!(&xs_d, &ys); +} + #[test] fn all_equal() { + assert!("".chars().all_equal()); + assert!("A".chars().all_equal()); assert!(!"AABBCCC".chars().all_equal()); assert!("AAAAAAA".chars().all_equal()); for (_key, mut sub) in &"AABBCCC".chars().group_by(|&x| x) { @@ -168,7 +186,7 @@ fn test_rciter() { #[allow(deprecated)] #[test] fn trait_pointers() { - struct ByRef<'r, I: ?Sized>(&'r mut I) where I: 'r; + struct ByRef<'r, I: ?Sized>(&'r mut I) ; impl<'r, X, I: ?Sized> Iterator for ByRef<'r, I> where I: 'r + Iterator @@ -180,12 +198,12 @@ fn trait_pointers() { } } - let mut it = Box::new(0..10) as Box>; + let mut it = Box::new(0..10) as Box>; assert_eq!(it.next(), Some(0)); { /* make sure foreach works on non-Sized */ - let jt: &mut Iterator = &mut *it; + let jt: &mut dyn Iterator = &mut *it; assert_eq!(jt.next(), Some(1)); { @@ -329,7 +347,7 @@ fn test_multipeek_reset() { #[test] fn test_multipeek_peeking_next() { - use it::PeekingNext; + use crate::it::PeekingNext; let nums = vec![1u8,2,3,4,5,6,7]; let mut mp = multipeek(nums.iter().map(|&x| x)); @@ -581,6 +599,46 @@ fn combinations_of_too_short() { #[test] fn combinations_zero() { it::assert_equal((1..3).combinations(0), vec![vec![]]); + it::assert_equal((0..0).combinations(0), vec![vec![]]); +} + +#[test] +fn permutations_zero() { + it::assert_equal((1..3).permutations(0), vec![vec![]]); + it::assert_equal((0..0).permutations(0), vec![vec![]]); +} + +#[test] +fn combinations_with_replacement() { + // Pool smaller than n + it::assert_equal((0..1).combinations_with_replacement(2), vec![vec![0, 0]]); + // Pool larger than n + it::assert_equal( + (0..3).combinations_with_replacement(2), + vec![ + vec![0, 0], + vec![0, 1], + vec![0, 2], + vec![1, 1], + vec![1, 2], + vec![2, 2], + ], + ); + // Zero size + it::assert_equal( + (0..3).combinations_with_replacement(0), + vec![vec![]], + ); + // Zero size on empty pool + it::assert_equal( + (0..0).combinations_with_replacement(0), + vec![vec![]], + ); + // Empty pool + it::assert_equal( + (0..0).combinations_with_replacement(2), + >>::new(), + ); } #[test] @@ -627,7 +685,7 @@ fn diff_shorter() { #[test] fn minmax() { use std::cmp::Ordering; - use it::MinMaxResult; + use crate::it::MinMaxResult; // A peculiar type: Equality compares both tuple items, but ordering only the // first item. This is so we can check the stability property easily. diff --git a/third_party/rust/itertools/tests/tuples.rs b/third_party/rust/itertools/tests/tuples.rs index 07dba57fe63d6..9fc8b3cc78a56 100644 --- a/third_party/rust/itertools/tests/tuples.rs +++ b/third_party/rust/itertools/tests/tuples.rs @@ -1,5 +1,3 @@ -extern crate itertools; - use itertools::Itertools; #[test] diff --git a/third_party/rust/itertools/tests/zip.rs b/third_party/rust/itertools/tests/zip.rs index c5c51899b8939..b1af52c9c4121 100644 --- a/third_party/rust/itertools/tests/zip.rs +++ b/third_party/rust/itertools/tests/zip.rs @@ -1,5 +1,3 @@ -extern crate itertools; - use itertools::Itertools; use itertools::EitherOrBoth::{Both, Left, Right}; use itertools::free::zip_eq; diff --git a/third_party/rust/wasmparser/.cargo-checksum.json b/third_party/rust/wasmparser/.cargo-checksum.json index 5e32b4707ca4a..0aae113cf2f0c 100644 --- a/third_party/rust/wasmparser/.cargo-checksum.json +++ b/third_party/rust/wasmparser/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.lock":"097d8d38fce861128185d43320f25aacc4f306c7a273427f0cc53460d2985c64","Cargo.toml":"38db996a0283398e7353969aa161779c7bd8160c8ced694497b12977b5983c95","README.md":"2e252886759b5ee5137ec39efc0765850be2cb4242c68e6b44452b75d3191db3","benches/benchmark.rs":"fd8556367534c5aa04960dc8c053dd50e531d2fbe6234b811d5e3ae95649e463","compare-with-main.sh":"2ddfab71ba571055292a29a36c1ede05f64ba094c78495b945d1486bf32ab6d7","examples/simple.rs":"606072a46c5c80df29da3ecd98a989feb1289243550033cd3c3e1df6045ce8ce","src/binary_reader.rs":"b99ceb7182a2581e8834a9a6a7f351bdf0e0f9fbbbcdc4af3bfb9c506c3fc219","src/lib.rs":"414ed00613d315875c0e84750fa0086a1a67e776a9f40637f547bf99f1cc7f0f","src/limits.rs":"76226dcbb57180a399d1e5dfaed0b2516728ab09d91852e3f9aa3adebf06b3b7","src/module_resources.rs":"3a2137adb9018a5d5ebcaf274f969e650e184f77e5db62cd9b655cc6e97fdee1","src/operators_validator.rs":"5fb8f7611f5bf3115016ae81cc4e1e1e0ac4605725df0c9131f0c5d5d1a9514f","src/parser.rs":"c05a92a04020e990635728101e3f7d6653ccdeb54f2ce3615fc6c058b344cd8e","src/primitives.rs":"f93340797ff49370c13a489dc34832771156bb38541b19cb56e371e9a2e099b2","src/readers/alias_section.rs":"ef6556c3e300549958010aba9f1a0f6852c80ceddc763b4c11463d97234488b3","src/readers/code_section.rs":"ab19a5ed2a72e85e8365a8f6915ebbc10ca07b72a097322c53c36fdfb13bd57c","src/readers/data_section.rs":"d919a22ebc44c53ca434df6c1d16efc8e126e7b55ed99691d5ae73c10bfadfff","src/readers/element_section.rs":"f168a3cb02439aeaa81621525e2747d3bc4743fac2237dcdf8658b67e010ca08","src/readers/export_section.rs":"3fe296f1789e789009a79115052194a1352d947f2a8830945d6b7d9983bb8579","src/readers/function_section.rs":"5467d7a375c22a4cc225819212e616f275ef01516f185b346eae2ffbb5c53cb3","src/readers/global_section.rs":"359450911ac662503f90288798baec2415df6d3b80990a7b75794683df7894b8","src/readers/import_section.rs":"80906451f78c64d31a76772d97c96d18d208eeabaaaf82372b0567a8991795c1","src/readers/init_expr.rs":"7020c80013dad4518a5f969c3ab4d624b46d778f03e632871cf343964f63441c","src/readers/instance_section.rs":"0f6cc9ed6bb6520493090eff5bbd6a9030ba1432206799b7dfed0b9285bafd73","src/readers/linking_section.rs":"db3091a48827a5b035e2f79f40f7ed9a7ea10acd4db6ab2bbd01e17a65a4265e","src/readers/memory_section.rs":"67d8457d3167b39fc9ae2c04f3c3e28bc10be97bbdaccd681675fb8d3eba2bd3","src/readers/mod.rs":"0fbaa4e1d7e3e68d8857fd8b3ce5c3fba886a1b460cd37398afcbf4802280b4e","src/readers/module_code_section.rs":"806eea527c84570ca6c5b8ca556f95727edae7da29e4f384b067113231b8e5f5","src/readers/module_section.rs":"6e28be8f1f4d2f1a9470ec356c46a76c2f21916143a1f0e245b96d2272d0301e","src/readers/name_section.rs":"60d4aa007cfdc16eedc1b4cb0bee560f6eebd82aaa81e9be9c844e515b16e445","src/readers/operators.rs":"3800f0321a776ddc5e8fb030828e4f2a65ebafa4b7f0808774384559ddfe49ea","src/readers/producers_section.rs":"77f93449e4bdcd61e4c79e47a685742f49cd5dac837ba002bce14120f14c9470","src/readers/reloc_section.rs":"e48e6acaa5145d6fbe1d74eb406ee59c43235faa47fbf0b07288504e60573a5e","src/readers/section_reader.rs":"e99763ce9c48994fd1e92f011a449936c4206a5c91d50fa580d003b6cc824ec5","src/readers/table_section.rs":"5d94185f68c4c7526a8836a2ebdb5b20fe754af58b68d2d0eb8fea62b7e6fe71","src/readers/type_section.rs":"87a54d238bf900aac0d0508e5c644d71b1e591df99367587feb68146a25a5a61","src/validator.rs":"8d401bdac4f8ecdd477a9fe5b979d82b49c4b988194a9a4f1253bafd15e36b11","src/validator/func.rs":"69508e0b0cfde783ca3635070496573b65a4b3ce9c39fe5afb0b6af19e346b2b"},"package":"a950e6a618f62147fd514ff445b2a0b53120d382751960797f85f058c7eda9b9"} \ No newline at end of file +{"files":{"Cargo.lock":"b484e6e75470ba8772cdfdcbbd66e647cfc963654ef7ae56bd3149f554f96dfe","Cargo.toml":"824a3a989355c393ec8aa217408ae4ec64ca083d320ac0ee46c04ca73a8b5e78","README.md":"2e252886759b5ee5137ec39efc0765850be2cb4242c68e6b44452b75d3191db3","benches/benchmark.rs":"2ed1bd83b9df9d91b11b0ab34053a77a228e9d3961a4a161477dd74882e95378","compare-with-main.sh":"2ddfab71ba571055292a29a36c1ede05f64ba094c78495b945d1486bf32ab6d7","examples/simple.rs":"606072a46c5c80df29da3ecd98a989feb1289243550033cd3c3e1df6045ce8ce","src/binary_reader.rs":"3108b99f780aa05a598fd61d3b8b71623b92c97801f6da3d05cb00855cfb5416","src/lib.rs":"372776c7339cc879ba2a1109e4692d09441dd6832955ed48aed1806b135b72a4","src/limits.rs":"dff24bbeea651c6ad06f7c63916c17e024f892e5262e939ed88274021e967d0b","src/module_resources.rs":"8d4bc965c11d3d2adfe2162cc81ceb075ac2a73da8a77ce25f154af3449a79c9","src/operators_validator.rs":"f181e6ccec7144637214d43f4a5ab34f0e2f3c56f09296492efb8c6f951be852","src/parser.rs":"c05a92a04020e990635728101e3f7d6653ccdeb54f2ce3615fc6c058b344cd8e","src/primitives.rs":"72bd331f63ed40758af000064faf7f175988caa2eede064bee77e8cfbac511c3","src/readers/alias_section.rs":"ef6556c3e300549958010aba9f1a0f6852c80ceddc763b4c11463d97234488b3","src/readers/code_section.rs":"d7d8be8f8bf158dac6ba7ee1d6583ea386e440f728a1f6ce1aa2e91352f884f4","src/readers/data_section.rs":"d919a22ebc44c53ca434df6c1d16efc8e126e7b55ed99691d5ae73c10bfadfff","src/readers/element_section.rs":"f168a3cb02439aeaa81621525e2747d3bc4743fac2237dcdf8658b67e010ca08","src/readers/export_section.rs":"3fe296f1789e789009a79115052194a1352d947f2a8830945d6b7d9983bb8579","src/readers/function_section.rs":"5467d7a375c22a4cc225819212e616f275ef01516f185b346eae2ffbb5c53cb3","src/readers/global_section.rs":"359450911ac662503f90288798baec2415df6d3b80990a7b75794683df7894b8","src/readers/import_section.rs":"80906451f78c64d31a76772d97c96d18d208eeabaaaf82372b0567a8991795c1","src/readers/init_expr.rs":"7020c80013dad4518a5f969c3ab4d624b46d778f03e632871cf343964f63441c","src/readers/instance_section.rs":"0f6cc9ed6bb6520493090eff5bbd6a9030ba1432206799b7dfed0b9285bafd73","src/readers/linking_section.rs":"db3091a48827a5b035e2f79f40f7ed9a7ea10acd4db6ab2bbd01e17a65a4265e","src/readers/memory_section.rs":"67d8457d3167b39fc9ae2c04f3c3e28bc10be97bbdaccd681675fb8d3eba2bd3","src/readers/mod.rs":"0fbaa4e1d7e3e68d8857fd8b3ce5c3fba886a1b460cd37398afcbf4802280b4e","src/readers/module_code_section.rs":"806eea527c84570ca6c5b8ca556f95727edae7da29e4f384b067113231b8e5f5","src/readers/module_section.rs":"6e28be8f1f4d2f1a9470ec356c46a76c2f21916143a1f0e245b96d2272d0301e","src/readers/name_section.rs":"60d4aa007cfdc16eedc1b4cb0bee560f6eebd82aaa81e9be9c844e515b16e445","src/readers/operators.rs":"3800f0321a776ddc5e8fb030828e4f2a65ebafa4b7f0808774384559ddfe49ea","src/readers/producers_section.rs":"77f93449e4bdcd61e4c79e47a685742f49cd5dac837ba002bce14120f14c9470","src/readers/reloc_section.rs":"e48e6acaa5145d6fbe1d74eb406ee59c43235faa47fbf0b07288504e60573a5e","src/readers/section_reader.rs":"e99763ce9c48994fd1e92f011a449936c4206a5c91d50fa580d003b6cc824ec5","src/readers/table_section.rs":"5d94185f68c4c7526a8836a2ebdb5b20fe754af58b68d2d0eb8fea62b7e6fe71","src/readers/type_section.rs":"87a54d238bf900aac0d0508e5c644d71b1e591df99367587feb68146a25a5a61","src/validator.rs":"003f1bc3f7fe0db0cf49007d45e4d1d3404c393ee96830625b138f9ed2a8c40b","src/validator/func.rs":"cda4491f7865df340f2c1fe066c7e9162febe0eef06e7d2b8fd9a50e469b3eae"},"package":"57da5d7300428d75d8b3cdfb736e41ee6af8926d69c1de2f201a1a22f234b7b5"} \ No newline at end of file diff --git a/third_party/rust/wasmparser/Cargo.lock b/third_party/rust/wasmparser/Cargo.lock index f6ebd52f91023..0d71dd6167ff0 100644 --- a/third_party/rust/wasmparser/Cargo.lock +++ b/third_party/rust/wasmparser/Cargo.lock @@ -2,9 +2,9 @@ # It is not intended for manual editing. [[package]] name = "anyhow" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f" +checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" [[package]] name = "atty" @@ -19,9 +19,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "bitflags" @@ -70,9 +70,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "clap" -version = "2.33.1" +version = "2.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ "bitflags", "textwrap", @@ -115,6 +115,16 @@ dependencies = [ "itertools", ] +[[package]] +name = "crossbeam-channel" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" +dependencies = [ + "crossbeam-utils", + "maybe-uninit", +] + [[package]] name = "crossbeam-deque" version = "0.7.3" @@ -141,17 +151,6 @@ dependencies = [ "scopeguard", ] -[[package]] -name = "crossbeam-queue" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" -dependencies = [ - "cfg-if", - "crossbeam-utils", - "maybe-uninit", -] - [[package]] name = "crossbeam-utils" version = "0.7.2" @@ -187,9 +186,9 @@ dependencies = [ [[package]] name = "either" -version = "1.5.3" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "getopts" @@ -208,9 +207,9 @@ checksum = "d36fab90f82edc3c747f9d438e06cf0a491055896f2a279638bb5beed6c40177" [[package]] name = "hermit-abi" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" +checksum = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151" dependencies = [ "libc", ] @@ -232,9 +231,9 @@ checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" [[package]] name = "js-sys" -version = "0.3.41" +version = "0.3.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4b9172132a62451e56142bff9afc91c8e4a4500aa5b847da36815b63bfda916" +checksum = "ca059e81d9486668f12d455a4ea6daa600bd408134cd17e3d3fb5a32d1f016f8" dependencies = [ "wasm-bindgen", ] @@ -247,15 +246,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.72" +version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9f8082297d534141b30c8d39e9b1773713ab50fdbe4ff30f750d063b3bfd701" +checksum = "aa7087f49d294270db4e1928fc110c976cd4b9e5a16348e0a1df09afa99e6c98" [[package]] name = "log" -version = "0.4.8" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" dependencies = [ "cfg-if", ] @@ -274,9 +273,9 @@ checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" [[package]] name = "memoffset" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f" +checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" dependencies = [ "autocfg", ] @@ -320,9 +319,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.18" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ "unicode-xid", ] @@ -338,9 +337,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.3.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f02856753d04e03e26929f820d0a0a337ebe71f849801eea335d464b349080" +checksum = "dcf6960dc9a5b4ee8d3e4c5787b4a112a8818e0290a42ff664ad60692fdf2032" dependencies = [ "autocfg", "crossbeam-deque", @@ -350,12 +349,12 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.7.1" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e92e15d89083484e11353891f1af602cc661426deb9564c298b270c726973280" +checksum = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf" dependencies = [ + "crossbeam-channel", "crossbeam-deque", - "crossbeam-queue", "crossbeam-utils", "lazy_static", "num_cpus", @@ -432,9 +431,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.114" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" +checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5" [[package]] name = "serde_cbor" @@ -448,9 +447,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.114" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" +checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8" dependencies = [ "proc-macro2", "quote", @@ -459,9 +458,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.56" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3" +checksum = "a230ea9107ca2220eea9d46de97eddcb04cd00e92d13dda78e478dd33fa82bd4" dependencies = [ "itoa", "ryu", @@ -470,9 +469,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.34" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cae2873c940d92e697597c5eee105fb570cd5689c695806f672883653349b" +checksum = "9c51d92969d209b54a98397e1b91c8ae82d8c87a7bb87df0b29aa2ad81454228" dependencies = [ "proc-macro2", "quote", @@ -523,9 +522,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.64" +version = "0.2.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a634620115e4a229108b71bde263bb4220c483b3f07f5ba514ee8d15064c4c2" +checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -533,9 +532,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.64" +version = "0.2.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e53963b583d18a5aa3aaae4b4c1cb535218246131ba22a71f05b518098571df" +checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68" dependencies = [ "bumpalo", "lazy_static", @@ -548,9 +547,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.64" +version = "0.2.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fcfd5ef6eec85623b4c6e844293d4516470d8f19cd72d0d12246017eb9060b8" +checksum = "6b13312a745c08c469f0b292dd2fcd6411dba5f7160f593da6ef69b64e407038" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -558,9 +557,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.64" +version = "0.2.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9adff9ee0e94b926ca81b57f57f86d5545cdcb1d259e21ec9bdd95b901754c75" +checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe" dependencies = [ "proc-macro2", "quote", @@ -571,13 +570,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.64" +version = "0.2.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7b90ea6c632dd06fd765d44542e234d5e63d9bb917ecd64d79778a13bd79ae" +checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307" [[package]] name = "wasmparser" -version = "0.59.0" +version = "0.63.0" dependencies = [ "anyhow", "criterion", @@ -587,9 +586,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.41" +version = "0.3.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "863539788676619aac1a23e2df3655e96b32b0e05eb72ca34ba045ad573c625d" +checksum = "4bf6ef87ad7ae8008e15a355ce696bed26012b7caa21605188cfd8214ab51e2d" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/third_party/rust/wasmparser/Cargo.toml b/third_party/rust/wasmparser/Cargo.toml index 70bbc9875e886..c2f7d055345d5 100644 --- a/third_party/rust/wasmparser/Cargo.toml +++ b/third_party/rust/wasmparser/Cargo.toml @@ -13,7 +13,7 @@ [package] edition = "2018" name = "wasmparser" -version = "0.59.0" +version = "0.63.0" authors = ["Yury Delendik "] description = "A simple event-driven library for parsing WebAssembly binary files.\n" homepage = "https://github.com/bytecodealliance/wasm-tools/tree/main/crates/wasmparser" diff --git a/third_party/rust/wasmparser/benches/benchmark.rs b/third_party/rust/wasmparser/benches/benchmark.rs index 1d45294e63d5d..25a7257cb0a4a 100644 --- a/third_party/rust/wasmparser/benches/benchmark.rs +++ b/third_party/rust/wasmparser/benches/benchmark.rs @@ -6,7 +6,7 @@ use criterion::Criterion; use std::fs; use std::path::Path; use std::path::PathBuf; -use wasmparser::{DataKind, ElementKind, Parser, Payload, Validator}; +use wasmparser::{DataKind, ElementKind, Parser, Payload, Validator, WasmFeatures}; /// A benchmark input. pub struct BenchmarkInput { @@ -206,13 +206,18 @@ fn it_works_benchmark(c: &mut Criterion) { fn validate_benchmark(c: &mut Criterion) { fn validator() -> Validator { let mut ret = Validator::new(); - ret.wasm_reference_types(true) - .wasm_multi_value(true) - .wasm_simd(true) - .wasm_module_linking(true) - .wasm_bulk_memory(true) - .wasm_threads(true) - .wasm_tail_call(true); + ret.wasm_features(WasmFeatures { + reference_types: true, + multi_value: true, + simd: true, + module_linking: true, + bulk_memory: true, + threads: true, + tail_call: true, + multi_memory: true, + memory64: true, + deterministic_only: false, + }); return ret; } let mut inputs = collect_benchmark_inputs(); diff --git a/third_party/rust/wasmparser/src/binary_reader.rs b/third_party/rust/wasmparser/src/binary_reader.rs index e95ac4372fa78..e7d4011df7d54 100644 --- a/third_party/rust/wasmparser/src/binary_reader.rs +++ b/third_party/rust/wasmparser/src/binary_reader.rs @@ -13,17 +13,17 @@ * limitations under the License. */ -use std::boxed::Box; use std::convert::TryInto; +use std::fmt; use std::str; -use std::vec::Vec; use crate::limits::*; use crate::primitives::{ BinaryReaderError, BrTable, CustomSectionKind, ExternalKind, FuncType, GlobalType, Ieee32, Ieee64, LinkingType, MemoryImmediate, MemoryType, NameType, Operator, RelocType, - ResizableLimits, Result, SIMDLaneIndex, SectionCode, TableType, Type, TypeOrFuncType, V128, + ResizableLimits, ResizableLimits64, Result, SIMDLaneIndex, SectionCode, TableType, Type, + TypeOrFuncType, V128, }; use crate::{ExportType, Import, ImportSectionEntryType, InstanceType, ModuleType}; @@ -74,7 +74,7 @@ impl Range { } /// A binary reader of the WebAssembly structures and types. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Hash)] pub struct BinaryReader<'a> { pub(crate) buffer: &'a [u8], pub(crate) position: usize, @@ -195,37 +195,6 @@ impl<'a> BinaryReader<'a> { } } - /// Read a `count` indicating the number of times to call `read_local_decl`. - pub fn read_local_count(&mut self) -> Result { - let local_count = self.read_var_u32()? as usize; - if local_count > MAX_WASM_FUNCTION_LOCALS { - return Err(BinaryReaderError::new( - "local_count is out of bounds", - self.original_position() - 1, - )); - } - Ok(local_count) - } - - /// Read a `(count, value_type)` declaration of local variables of the same type. - pub fn read_local_decl(&mut self, locals_total: &mut usize) -> Result<(u32, Type)> { - let count = self.read_var_u32()?; - let value_type = self.read_type()?; - *locals_total = locals_total.checked_add(count as usize).ok_or_else(|| { - BinaryReaderError::new( - "locals_total is out of bounds", - self.original_position() - 1, - ) - })?; - if *locals_total > MAX_WASM_FUNCTION_LOCALS { - return Err(BinaryReaderError::new( - "locals_total is out of bounds", - self.original_position() - 1, - )); - } - Ok((count, value_type)) - } - pub(crate) fn read_external_kind(&mut self) -> Result { let code = self.read_u8()?; match code { @@ -353,6 +322,16 @@ impl<'a> BinaryReader<'a> { Ok(ResizableLimits { initial, maximum }) } + fn read_resizable_limits64(&mut self, max_present: bool) -> Result { + let initial = self.read_var_u64()?; + let maximum = if max_present { + Some(self.read_var_u64()?) + } else { + None + }; + Ok(ResizableLimits64 { initial, maximum }) + } + pub(crate) fn read_table_type(&mut self) -> Result { let element_type = self.read_type()?; let flags = self.read_var_u32()?; @@ -370,16 +349,28 @@ impl<'a> BinaryReader<'a> { } pub(crate) fn read_memory_type(&mut self) -> Result { - let flags = self.read_var_u32()?; - if (flags & !0x3) != 0 { + let pos = self.original_position(); + let flags = self.read_u8()?; + if (flags & !0x7) != 0 { return Err(BinaryReaderError::new( "invalid table resizable limits flags", - self.original_position() - 1, + pos, )); } - let limits = self.read_resizable_limits((flags & 0x1) != 0)?; - let shared = (flags & 0x2) != 0; - Ok(MemoryType { limits, shared }) + if flags & 0x4 == 0 { + let limits = self.read_resizable_limits((flags & 0x1) != 0)?; + let shared = (flags & 0x2) != 0; + Ok(MemoryType::M32 { limits, shared }) + } else { + let limits = self.read_resizable_limits64((flags & 0x1) != 0)?; + if (flags & 0x2) != 0 { + return Err(BinaryReaderError::new( + "64-bit memories cannot be shared", + pos, + )); + } + Ok(MemoryType::M64 { limits }) + } } pub(crate) fn read_global_type(&mut self) -> Result { @@ -389,10 +380,31 @@ impl<'a> BinaryReader<'a> { }) } + fn read_first_byte_and_var_u32(&mut self) -> Result<(u8, u32)> { + let pos = self.position; + let val = self.read_var_u32()?; + Ok((self.buffer[pos], val)) + } + fn read_memarg(&mut self) -> Result { + let flags_pos = self.original_position(); + let mut flags = self.read_var_u32()?; + let offset = self.read_var_u32()?; + let memory = if flags & (1 << 6) != 0 { + flags ^= 1 << 6; + self.read_var_u32()? + } else { + 0 + }; + let align = if flags >= (1 << 6) { + return Err(BinaryReaderError::new("alignment too large", flags_pos)); + } else { + flags as u8 + }; Ok(MemoryImmediate { - flags: self.read_var_u32()?, - offset: self.read_var_u32()?, + align, + offset, + memory, }) } @@ -448,8 +460,9 @@ impl<'a> BinaryReader<'a> { self.skip_var_32()?; } self.skip_var_32()?; + let end = self.position; Ok(BrTable { - buffer: &self.buffer[start..self.position], + reader: BinaryReader::new_with_offset(&self.buffer[start..end], start), cnt: targets_len as usize, }) } @@ -511,7 +524,9 @@ impl<'a> BinaryReader<'a> { /// Advances the `BinaryReader` a single byte, and returns the data as /// a `u32`. + /// /// # Errors + /// /// If `BinaryReader` has no bytes remaining. pub fn read_u8(&mut self) -> Result { self.ensure_has_byte()?; @@ -522,7 +537,9 @@ impl<'a> BinaryReader<'a> { /// Advances the `BinaryReader` up to two bytes to parse a variable /// length integer as a `u8`. + /// /// # Errors + /// /// If `BinaryReader` has less than one or two bytes remaining, or the /// integer is larger than eight bits. pub fn read_var_u8(&mut self) -> Result { @@ -544,7 +561,9 @@ impl<'a> BinaryReader<'a> { /// Advances the `BinaryReader` up to four bytes to parse a variable /// length integer as a `u32`. + /// /// # Errors + /// /// If `BinaryReader` has less than one or up to four bytes remaining, or /// the integer is larger than 32 bits. pub fn read_var_u32(&mut self) -> Result { @@ -574,6 +593,40 @@ impl<'a> BinaryReader<'a> { Ok(result) } + /// Advances the `BinaryReader` up to four bytes to parse a variable + /// length integer as a `u64`. + /// + /// # Errors + /// + /// If `BinaryReader` has less than one or up to eight bytes remaining, or + /// the integer is larger than 64 bits. + pub fn read_var_u64(&mut self) -> Result { + // Optimization for single byte u64. + let byte = u64::from(self.read_u8()?); + if (byte & 0x80) == 0 { + return Ok(byte); + } + + let mut result = byte & 0x7F; + let mut shift = 7; + loop { + let byte = u64::from(self.read_u8()?); + result |= (byte & 0x7F) << shift; + if shift >= 57 && (byte >> (64 - shift)) != 0 { + // The continuation bit or unused bits are set. + return Err(BinaryReaderError::new( + "Invalid var_u64", + self.original_position() - 1, + )); + } + shift += 7; + if (byte & 0x80) == 0 { + break; + } + } + Ok(result) + } + /// Advances the `BinaryReader` up to four bytes over a variable length 32 /// bit integer, discarding the result. /// # Errors @@ -775,27 +828,28 @@ impl<'a> BinaryReader<'a> { }) } - fn read_memarg_of_align(&mut self, max_align: u32) -> Result { + fn read_memarg_of_align(&mut self, max_align: u8) -> Result { + let align_pos = self.original_position(); let imm = self.read_memarg()?; - if imm.flags > max_align { + if imm.align > max_align { return Err(BinaryReaderError::new( "alignment must not be larger than natural", - self.original_position() - 1, + align_pos, )); } Ok(imm) } fn read_0xfe_operator(&mut self) -> Result> { - let code = self.read_u8()? as u8; + let code = self.read_var_u32()?; Ok(match code { - 0x00 => Operator::AtomicNotify { + 0x00 => Operator::MemoryAtomicNotify { memarg: self.read_memarg_of_align(2)?, }, - 0x01 => Operator::I32AtomicWait { + 0x01 => Operator::MemoryAtomicWait32 { memarg: self.read_memarg_of_align(2)?, }, - 0x02 => Operator::I64AtomicWait { + 0x02 => Operator::MemoryAtomicWait64 { memarg: self.read_memarg_of_align(3)?, }, 0x03 => Operator::AtomicFence { @@ -993,7 +1047,7 @@ impl<'a> BinaryReader<'a> { _ => { return Err(BinaryReaderError::new( - "Unknown 0xFE opcode", + format!("Unknown 0xfe subopcode: 0x{:x}", code), self.original_position() - 1, )); } @@ -1162,12 +1216,14 @@ impl<'a> BinaryReader<'a> { 0x3e => Operator::I64Store32 { memarg: self.read_memarg()?, }, - 0x3f => Operator::MemorySize { - reserved: self.read_var_u1()?, - }, - 0x40 => Operator::MemoryGrow { - reserved: self.read_var_u1()?, - }, + 0x3f => { + let (mem_byte, mem) = self.read_first_byte_and_var_u32()?; + Operator::MemorySize { mem_byte, mem } + } + 0x40 => { + let (mem_byte, mem) = self.read_first_byte_and_var_u32()?; + Operator::MemoryGrow { mem_byte, mem } + } 0x41 => Operator::I32Const { value: self.read_var_i32()?, }, @@ -1324,7 +1380,7 @@ impl<'a> BinaryReader<'a> { _ => { return Err(BinaryReaderError::new( - "Unknown opcode", + format!("Unknown opcode: 0x{:x}", code), self.original_position() - 1, )); } @@ -1332,7 +1388,7 @@ impl<'a> BinaryReader<'a> { } fn read_0xfc_operator(&mut self) -> Result> { - let code = self.read_u8()? as u8; + let code = self.read_var_u32()?; Ok(match code { 0x00 => Operator::I32TruncSatF32S, 0x01 => Operator::I32TruncSatF32U, @@ -1345,45 +1401,21 @@ impl<'a> BinaryReader<'a> { 0x08 => { let segment = self.read_var_u32()?; - let mem = self.read_u8()?; - if mem != 0 { - return Err(BinaryReaderError::new( - "reserved byte must be zero", - self.original_position() - 1, - )); - } - Operator::MemoryInit { segment } + let mem = self.read_var_u32()?; + Operator::MemoryInit { segment, mem } } 0x09 => { let segment = self.read_var_u32()?; Operator::DataDrop { segment } } 0x0a => { - let dst = self.read_u8()?; - if dst != 0 { - return Err(BinaryReaderError::new( - "reserved byte must be zero", - self.original_position() - 1, - )); - } - let src = self.read_u8()?; - if src != 0 { - return Err(BinaryReaderError::new( - "reserved byte must be zero", - self.original_position() - 1, - )); - } - Operator::MemoryCopy + let dst = self.read_var_u32()?; + let src = self.read_var_u32()?; + Operator::MemoryCopy { src, dst } } 0x0b => { - let mem = self.read_u8()?; - if mem != 0 { - return Err(BinaryReaderError::new( - "reserved byte must be zero", - self.original_position() - 1, - )); - } - Operator::MemoryFill + let mem = self.read_var_u32()?; + Operator::MemoryFill { mem } } 0x0c => { let segment = self.read_var_u32()?; @@ -1419,7 +1451,7 @@ impl<'a> BinaryReader<'a> { _ => { return Err(BinaryReaderError::new( - "Unknown 0xfc opcode", + format!("Unknown 0xfc subopcode: 0x{:x}", code), self.original_position() - 1, )); } @@ -1449,34 +1481,34 @@ impl<'a> BinaryReader<'a> { 0x00 => Operator::V128Load { memarg: self.read_memarg()?, }, - 0x01 => Operator::I16x8Load8x8S { + 0x01 => Operator::V128Load8x8S { memarg: self.read_memarg_of_align(3)?, }, - 0x02 => Operator::I16x8Load8x8U { + 0x02 => Operator::V128Load8x8U { memarg: self.read_memarg_of_align(3)?, }, - 0x03 => Operator::I32x4Load16x4S { + 0x03 => Operator::V128Load16x4S { memarg: self.read_memarg_of_align(3)?, }, - 0x04 => Operator::I32x4Load16x4U { + 0x04 => Operator::V128Load16x4U { memarg: self.read_memarg_of_align(3)?, }, - 0x05 => Operator::I64x2Load32x2S { + 0x05 => Operator::V128Load32x2S { memarg: self.read_memarg_of_align(3)?, }, - 0x06 => Operator::I64x2Load32x2U { + 0x06 => Operator::V128Load32x2U { memarg: self.read_memarg_of_align(3)?, }, - 0x07 => Operator::V8x16LoadSplat { + 0x07 => Operator::V128Load8Splat { memarg: self.read_memarg_of_align(0)?, }, - 0x08 => Operator::V16x8LoadSplat { + 0x08 => Operator::V128Load16Splat { memarg: self.read_memarg_of_align(1)?, }, - 0x09 => Operator::V32x4LoadSplat { + 0x09 => Operator::V128Load32Splat { memarg: self.read_memarg_of_align(2)?, }, - 0x0a => Operator::V64x2LoadSplat { + 0x0a => Operator::V128Load64Splat { memarg: self.read_memarg_of_align(3)?, }, 0x0b => Operator::V128Store { @@ -1490,9 +1522,9 @@ impl<'a> BinaryReader<'a> { for lane in &mut lanes { *lane = self.read_lane_index(32)? } - Operator::V8x16Shuffle { lanes } + Operator::I8x16Shuffle { lanes } } - 0x0e => Operator::V8x16Swizzle, + 0x0e => Operator::I8x16Swizzle, 0x0f => Operator::I8x16Splat, 0x10 => Operator::I16x8Splat, 0x11 => Operator::I32x4Splat, @@ -1600,11 +1632,11 @@ impl<'a> BinaryReader<'a> { 0x6c => Operator::I8x16ShrS, 0x6d => Operator::I8x16ShrU, 0x6e => Operator::I8x16Add, - 0x6f => Operator::I8x16AddSaturateS, - 0x70 => Operator::I8x16AddSaturateU, + 0x6f => Operator::I8x16AddSatS, + 0x70 => Operator::I8x16AddSatU, 0x71 => Operator::I8x16Sub, - 0x72 => Operator::I8x16SubSaturateS, - 0x73 => Operator::I8x16SubSaturateU, + 0x72 => Operator::I8x16SubSatS, + 0x73 => Operator::I8x16SubSatU, 0x76 => Operator::I8x16MinS, 0x77 => Operator::I8x16MinU, 0x78 => Operator::I8x16MaxS, @@ -1625,11 +1657,11 @@ impl<'a> BinaryReader<'a> { 0x8c => Operator::I16x8ShrS, 0x8d => Operator::I16x8ShrU, 0x8e => Operator::I16x8Add, - 0x8f => Operator::I16x8AddSaturateS, - 0x90 => Operator::I16x8AddSaturateU, + 0x8f => Operator::I16x8AddSatS, + 0x90 => Operator::I16x8AddSatU, 0x91 => Operator::I16x8Sub, - 0x92 => Operator::I16x8SubSaturateS, - 0x93 => Operator::I16x8SubSaturateU, + 0x92 => Operator::I16x8SubSatS, + 0x93 => Operator::I16x8SubSatU, 0x95 => Operator::I16x8Mul, 0x96 => Operator::I16x8MinS, 0x97 => Operator::I16x8MinU, @@ -1662,6 +1694,14 @@ impl<'a> BinaryReader<'a> { 0xce => Operator::I64x2Add, 0xd1 => Operator::I64x2Sub, 0xd5 => Operator::I64x2Mul, + 0xd8 => Operator::F32x4Ceil, + 0xd9 => Operator::F32x4Floor, + 0xda => Operator::F32x4Trunc, + 0xdb => Operator::F32x4Nearest, + 0xdc => Operator::F64x2Ceil, + 0xdd => Operator::F64x2Floor, + 0xde => Operator::F64x2Trunc, + 0xdf => Operator::F64x2Nearest, 0xe0 => Operator::F32x4Abs, 0xe1 => Operator::F32x4Neg, 0xe3 => Operator::F32x4Sqrt, @@ -1671,6 +1711,8 @@ impl<'a> BinaryReader<'a> { 0xe7 => Operator::F32x4Div, 0xe8 => Operator::F32x4Min, 0xe9 => Operator::F32x4Max, + 0xea => Operator::F32x4PMin, + 0xeb => Operator::F32x4PMax, 0xec => Operator::F64x2Abs, 0xed => Operator::F64x2Neg, 0xef => Operator::F64x2Sqrt, @@ -1680,13 +1722,15 @@ impl<'a> BinaryReader<'a> { 0xf3 => Operator::F64x2Div, 0xf4 => Operator::F64x2Min, 0xf5 => Operator::F64x2Max, + 0xf6 => Operator::F64x2PMin, + 0xf7 => Operator::F64x2PMax, 0xf8 => Operator::I32x4TruncSatF32x4S, 0xf9 => Operator::I32x4TruncSatF32x4U, 0xfa => Operator::F32x4ConvertI32x4S, 0xfb => Operator::F32x4ConvertI32x4U, _ => { return Err(BinaryReaderError::new( - "Unknown 0xfd opcode", + format!("Unknown 0xfd subopcode: 0x{:x}", code), self.original_position() - 1, )); } @@ -1777,73 +1821,57 @@ impl<'a> BrTable<'a> { self.len() == 0 } - /// Reads br_table entries. + /// Returns the list of targets that this `br_table` instruction will be + /// jumping to. + /// + /// This method will return an iterator which parses each target of this + /// `br_table` as well as the default target. The returned iterator will + /// yield `self.len() + 1` elements. + /// + /// Each iterator item is a tuple of `(u32, bool)`, where the first item is + /// the relative depth of the jump and the second item is `true` if the item + /// is the default label. You're guaranteed that `true` will only show up + /// for the final element of the iterator. + /// + /// #Examples /// - /// # Examples /// ```rust - /// let buf = vec![0x0e, 0x02, 0x01, 0x02, 0x00]; + /// let buf = [0x0e, 0x02, 0x01, 0x02, 0x00]; /// let mut reader = wasmparser::BinaryReader::new(&buf); /// let op = reader.read_operator().unwrap(); - /// if let wasmparser::Operator::BrTable { ref table } = op { - /// let br_table_depths = table.read_table().unwrap(); - /// assert!(br_table_depths.0 == vec![1,2].into_boxed_slice() && - /// br_table_depths.1 == 0); - /// } else { - /// unreachable!(); + /// if let wasmparser::Operator::BrTable { table } = op { + /// let targets = table.targets().collect::, _>>().unwrap(); + /// assert_eq!(targets, [(1, false), (2, false), (0, true)]); /// } /// ``` - pub fn read_table(&self) -> Result<(Box<[u32]>, u32)> { - let mut reader = BinaryReader::new(self.buffer); - let mut table = Vec::new(); - while !reader.eof() { - table.push(reader.read_var_u32()?); - } - let default_target = table.pop().ok_or_else(|| { - BinaryReaderError::new( - "br_table missing default target", - reader.original_position(), - ) - })?; - Ok((table.into_boxed_slice(), default_target)) - } -} - -/// Iterator for `BrTable`. -/// -/// #Examples -/// ```rust -/// let buf = vec![0x0e, 0x02, 0x01, 0x02, 0x00]; -/// let mut reader = wasmparser::BinaryReader::new(&buf); -/// let op = reader.read_operator().unwrap(); -/// if let wasmparser::Operator::BrTable { ref table } = op { -/// for depth in table { -/// println!("BrTable depth: {}", depth); -/// } -/// } -/// ``` -#[derive(Clone, Debug)] -pub struct BrTableIterator<'a> { - reader: BinaryReader<'a>, -} - -impl<'a> IntoIterator for &'a BrTable<'a> { - type Item = u32; - type IntoIter = BrTableIterator<'a>; - - fn into_iter(self) -> Self::IntoIter { - BrTableIterator { - reader: BinaryReader::new(self.buffer), - } + pub fn targets<'b>(&'b self) -> impl Iterator> + 'b { + let mut reader = self.reader.clone(); + (0..self.cnt + 1).map(move |i| { + let label = reader.read_var_u32()?; + let ret = (label, i == self.cnt); + if ret.1 && !reader.eof() { + return Err(BinaryReaderError::new( + "trailing data in br_table", + reader.original_position(), + )); + } + Ok(ret) + }) } } -impl<'a> Iterator for BrTableIterator<'a> { - type Item = u32; - - fn next(&mut self) -> Option { - if self.reader.eof() { - return None; +impl fmt::Debug for BrTable<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut f = f.debug_struct("BrTable"); + f.field("count", &self.cnt); + match self.targets().collect::>>() { + Ok(targets) => { + f.field("targets", &targets); + } + Err(_) => { + f.field("reader", &self.reader); + } } - self.reader.read_var_u32().ok() + f.finish() } } diff --git a/third_party/rust/wasmparser/src/lib.rs b/third_party/rust/wasmparser/src/lib.rs index 5e9ad6aa45f4e..33ab655ec340f 100644 --- a/third_party/rust/wasmparser/src/lib.rs +++ b/third_party/rust/wasmparser/src/lib.rs @@ -26,47 +26,9 @@ pub use crate::binary_reader::BinaryReader; pub use crate::binary_reader::Range; -pub use crate::primitives::BinaryReaderError; -pub use crate::primitives::BrTable; -pub use crate::primitives::CustomSectionKind; -pub use crate::primitives::ExportType; -pub use crate::primitives::ExternalKind; -pub use crate::primitives::FuncType; -pub use crate::primitives::GlobalType; -pub use crate::primitives::Ieee32; -pub use crate::primitives::Ieee64; -pub use crate::primitives::ImportSectionEntryType; -pub use crate::primitives::InstanceType; -pub use crate::primitives::LinkingType; -pub use crate::primitives::MemoryImmediate; -pub use crate::primitives::MemoryType; -pub use crate::primitives::ModuleType; -pub use crate::primitives::NameType; -pub use crate::primitives::Naming; -pub use crate::primitives::Operator; -pub use crate::primitives::RelocType; -pub use crate::primitives::ResizableLimits; -pub use crate::primitives::Result; -pub use crate::primitives::SectionCode; -pub use crate::primitives::TableType; -pub use crate::primitives::Type; -pub use crate::primitives::TypeDef; -pub use crate::primitives::TypeOrFuncType; -pub use crate::primitives::V128; - -pub use crate::module_resources::WasmFuncType; -pub use crate::module_resources::WasmGlobalType; -pub use crate::module_resources::WasmMemoryType; -pub use crate::module_resources::WasmModuleResources; -pub use crate::module_resources::WasmTableType; -pub use crate::module_resources::WasmType; -pub use crate::module_resources::WasmTypeDef; - -pub(crate) use crate::module_resources::{wasm_func_type_inputs, wasm_func_type_outputs}; - -pub use crate::operators_validator::OperatorValidatorConfig; - +pub use crate::module_resources::*; pub use crate::parser::*; +pub use crate::primitives::*; pub use crate::readers::*; pub use crate::validator::*; diff --git a/third_party/rust/wasmparser/src/limits.rs b/third_party/rust/wasmparser/src/limits.rs index 7562242ac3887..285f2a8cb3b5e 100644 --- a/third_party/rust/wasmparser/src/limits.rs +++ b/third_party/rust/wasmparser/src/limits.rs @@ -20,8 +20,9 @@ pub const MAX_WASM_FUNCTIONS: usize = 1_000_000; pub const MAX_WASM_IMPORTS: usize = 100_000; pub const MAX_WASM_EXPORTS: usize = 100_000; pub const MAX_WASM_GLOBALS: usize = 1_000_000; -pub const _MAX_WASM_DATA_SEGMENTS: usize = 100_000; +pub const MAX_WASM_DATA_SEGMENTS: usize = 100_000; pub const MAX_WASM_MEMORY_PAGES: usize = 65536; +pub const MAX_WASM_MEMORY64_PAGES: u64 = 1 << 48; pub const MAX_WASM_STRING_SIZE: usize = 100_000; pub const _MAX_WASM_MODULE_SIZE: usize = 1024 * 1024 * 1024; //= 1 GiB pub const MAX_WASM_FUNCTION_SIZE: usize = 128 * 1024; @@ -31,6 +32,6 @@ pub const MAX_WASM_FUNCTION_RETURNS: usize = 1000; pub const _MAX_WASM_TABLE_SIZE: usize = 10_000_000; pub const MAX_WASM_TABLE_ENTRIES: usize = 10_000_000; pub const MAX_WASM_TABLES: usize = 100; -pub const MAX_WASM_MEMORIES: usize = 1; +pub const MAX_WASM_MEMORIES: usize = 100; pub const MAX_WASM_MODULES: usize = 1_000; pub const MAX_WASM_INSTANCES: usize = 1_000; diff --git a/third_party/rust/wasmparser/src/module_resources.rs b/third_party/rust/wasmparser/src/module_resources.rs index 4ed1c4d58635e..bdf137ce123d4 100644 --- a/third_party/rust/wasmparser/src/module_resources.rs +++ b/third_party/rust/wasmparser/src/module_resources.rs @@ -13,32 +13,11 @@ * limitations under the License. */ -use crate::{FuncType, TypeDef}; - -/// Types that qualify as Wasm types for validation purposes. -/// -/// Must be comparable with `wasmparser` given Wasm types and -/// must be comparable to themselves. -pub trait WasmType: PartialEq + PartialEq + Eq { - /// Converts the custom type into a `wasmparser` known type. - /// - /// # Note - /// - /// This interface is required as bridge until transitioning is complete. - fn to_parser_type(&self) -> crate::Type; -} - -pub trait WasmTypeDef { - type FuncType: WasmFuncType; - - fn as_func(&self) -> Option<&Self::FuncType>; -} +use crate::{FuncType, GlobalType, MemoryType, TableType, Type}; +use std::ops::Range; /// Types that qualify as Wasm function types for validation purposes. pub trait WasmFuncType { - /// A type that is comparable with Wasm types. - type Type: WasmType; - /// Returns the number of input types. fn len_inputs(&self) -> usize; /// Returns the number of output types. @@ -49,230 +28,162 @@ pub trait WasmFuncType { /// /// The returned type may be wrapped by the user crate and thus /// the actually returned type only has to be comparable to a Wasm type. - fn input_at(&self, at: u32) -> Option<&Self::Type>; + fn input_at(&self, at: u32) -> Option; /// Returns the type at given index if any. /// /// # Note /// /// The returned type may be wrapped by the user crate and thus /// the actually returned type only has to be comparable to a Wasm type. - fn output_at(&self, at: u32) -> Option<&Self::Type>; -} + fn output_at(&self, at: u32) -> Option; + + /// Returns the list of inputs as an iterator. + fn inputs(&self) -> WasmFuncTypeInputs<'_, Self> + where + Self: Sized, + { + WasmFuncTypeInputs { + func_type: self, + range: 0..self.len_inputs() as u32, + } + } -/// Iterator over the inputs of a Wasm function type. -pub(crate) struct WasmFuncTypeInputs<'a, F, T> -where - F: WasmFuncType, - T: WasmType + 'a, -{ - /// The iterated-over function type. - func_type: &'a F, - /// The current starting index. - start: u32, - /// The current ending index. - end: u32, + /// Returns the list of outputs as an iterator. + fn outputs(&self) -> WasmFuncTypeOutputs<'_, Self> + where + Self: Sized, + { + WasmFuncTypeOutputs { + func_type: self, + range: 0..self.len_outputs() as u32, + } + } } -impl<'a, F, T> WasmFuncTypeInputs<'a, F, T> +impl WasmFuncType for &'_ T where - F: WasmFuncType, - T: WasmType + 'a, + T: ?Sized + WasmFuncType, { - fn new(func_type: &'a F) -> Self { - Self { - func_type, - start: 0, - end: func_type.len_inputs() as u32, - } + fn len_inputs(&self) -> usize { + T::len_inputs(self) + } + fn len_outputs(&self) -> usize { + T::len_outputs(self) } + fn input_at(&self, at: u32) -> Option { + T::input_at(self, at) + } + fn output_at(&self, at: u32) -> Option { + T::output_at(self, at) + } +} + +/// Iterator over the inputs of a Wasm function type. +pub struct WasmFuncTypeInputs<'a, T> { + /// The iterated-over function type. + func_type: &'a T, + /// The range we're iterating over. + range: Range, } -impl<'a, F, T> Iterator for WasmFuncTypeInputs<'a, F, T> +impl Iterator for WasmFuncTypeInputs<'_, T> where - F: WasmFuncType, - T: WasmType + 'a, + T: WasmFuncType, { - type Item = &'a T; + type Item = crate::Type; fn next(&mut self) -> Option { - if self.start == self.end { - return None; - } - let ty = self - .func_type - .input_at(self.start) - // Expected since `self.start != self.end`. - .expect("we expect to receive an input type at this point"); - self.start += 1; - Some(ty) + self.range + .next() + .map(|i| self.func_type.input_at(i).unwrap()) } fn size_hint(&self) -> (usize, Option) { - (self.len(), Some(self.len())) + self.range.size_hint() } } -impl<'a, F, T> DoubleEndedIterator for WasmFuncTypeInputs<'a, F, T> +impl DoubleEndedIterator for WasmFuncTypeInputs<'_, T> where - F: WasmFuncType, - T: WasmType + 'a, + T: WasmFuncType, { fn next_back(&mut self) -> Option { - if self.start == self.end { - return None; - } - let ty = self - .func_type - .input_at(self.end) - // Expected since `self.start != self.end`. - .expect("we expect to receive an input type at this point"); - self.end -= 1; - Some(ty) + self.range + .next_back() + .map(|i| self.func_type.input_at(i).unwrap()) } } -impl<'a, F, T> ExactSizeIterator for WasmFuncTypeInputs<'a, F, T> +impl ExactSizeIterator for WasmFuncTypeInputs<'_, T> where - F: WasmFuncType, - T: WasmType + 'a, + T: WasmFuncType, { fn len(&self) -> usize { - (self.end as usize) - (self.start as usize) + self.range.len() } } -/// Iterator over the outputs of a Wasm function type. -pub(crate) struct WasmFuncTypeOutputs<'a, F, T> -where - F: WasmFuncType, - T: WasmType + 'a, -{ - /// The iterated-over function type. - func_type: &'a F, - /// The current starting index. - start: u32, - /// The current ending index. - end: u32, -} - -impl<'a, F, T> WasmFuncTypeOutputs<'a, F, T> -where - F: WasmFuncType, - T: WasmType + 'a, -{ - fn new(func_type: &'a F) -> Self { - Self { - func_type, - start: 0, - end: func_type.len_outputs() as u32, +impl<'a, T> Clone for WasmFuncTypeInputs<'a, T> { + fn clone(&self) -> WasmFuncTypeInputs<'a, T> { + WasmFuncTypeInputs { + func_type: self.func_type, + range: self.range.clone(), } } } -impl<'a, F, T> Iterator for WasmFuncTypeOutputs<'a, F, T> +/// Iterator over the outputs of a Wasm function type. +pub struct WasmFuncTypeOutputs<'a, T> { + /// The iterated-over function type. + func_type: &'a T, + /// The range we're iterating over. + range: Range, +} + +impl Iterator for WasmFuncTypeOutputs<'_, T> where - F: WasmFuncType, - T: WasmType + 'a, + T: WasmFuncType, { - type Item = &'a T; + type Item = crate::Type; fn next(&mut self) -> Option { - if self.start == self.end { - return None; - } - let ty = self - .func_type - .output_at(self.start) - // Expected since `self.start != self.end`. - .expect("we expect to receive an input type at this point"); - self.start += 1; - Some(ty) + self.range + .next() + .map(|i| self.func_type.output_at(i).unwrap()) } fn size_hint(&self) -> (usize, Option) { - (self.len(), Some(self.len())) + self.range.size_hint() } } -impl<'a, F, T> DoubleEndedIterator for WasmFuncTypeOutputs<'a, F, T> +impl DoubleEndedIterator for WasmFuncTypeOutputs<'_, T> where - F: WasmFuncType, - T: WasmType + 'a, + T: WasmFuncType, { fn next_back(&mut self) -> Option { - if self.start == self.end { - return None; - } - let ty = self - .func_type - .output_at(self.end) - // Expected since `self.start != self.end`. - .expect("we expect to receive an input type at this point"); - self.end -= 1; - Some(ty) + self.range + .next_back() + .map(|i| self.func_type.output_at(i).unwrap()) } } -impl<'a, F, T> ExactSizeIterator for WasmFuncTypeOutputs<'a, F, T> +impl ExactSizeIterator for WasmFuncTypeOutputs<'_, T> where - F: WasmFuncType, - T: WasmType + 'a, + T: WasmFuncType, { fn len(&self) -> usize { - (self.end as usize) - (self.start as usize) + self.range.len() } } -/// Returns an iterator over the input types of a Wasm function type. -pub(crate) fn wasm_func_type_inputs<'a, F, T>(func_type: &'a F) -> WasmFuncTypeInputs<'a, F, T> -where - F: WasmFuncType, - T: WasmType + 'a, -{ - WasmFuncTypeInputs::new(func_type) -} - -/// Returns an iterator over the output types of a Wasm function type. -pub(crate) fn wasm_func_type_outputs<'a, F, T>(func_type: &'a F) -> WasmFuncTypeOutputs<'a, F, T> -where - F: WasmFuncType, - T: WasmType + 'a, -{ - WasmFuncTypeOutputs::new(func_type) -} - -/// Types that qualify as Wasm table types for validation purposes. -pub trait WasmTableType { - /// A type that is comparable with Wasm types. - type Type: WasmType; - - /// Returns the element type of the table. - fn element_type(&self) -> &Self::Type; - /// Returns the initial limit of the table. - fn initial_limit(&self) -> u32; - /// Returns the maximum limit of the table if any. - fn maximum_limit(&self) -> Option; -} - -/// Types that qualify as Wasm memory types for validation purposes. -pub trait WasmMemoryType { - /// Returns `true` if the linear memory is shared. - fn is_shared(&self) -> bool; - /// Returns the initial limit of the linear memory. - fn initial_limit(&self) -> u32; - /// Returns the maximum limit of the linear memory if any. - fn maximum_limit(&self) -> Option; -} - -/// Types that qualify as Wasm global types for validation purposes. -pub trait WasmGlobalType { - /// A type that is comparable with Wasm types. - type Type: WasmType; - - /// Returns `true` if the global variable is mutable. - fn is_mutable(&self) -> bool; - /// Returns the content type of the global variable. - fn content_type(&self) -> &Self::Type; +impl<'a, T> Clone for WasmFuncTypeOutputs<'a, T> { + fn clone(&self) -> WasmFuncTypeOutputs<'a, T> { + WasmFuncTypeOutputs { + func_type: self.func_type, + range: self.range.clone(), + } + } } /// Types that qualify as Wasm valiation database. @@ -285,26 +196,20 @@ pub trait WasmGlobalType { /// the need of an additional parsing or validation step or copying data around. pub trait WasmModuleResources { /// The function type used for validation. - type TypeDef: WasmTypeDef; - /// The table type used for validation. - type TableType: WasmTableType; - /// The memory type used for validation. - type MemoryType: WasmMemoryType; - /// The global type used for validation. - type GlobalType: WasmGlobalType; + type FuncType: WasmFuncType; - /// Returns the type at given index. - fn type_at(&self, at: u32) -> Option<&Self::TypeDef>; /// Returns the table at given index if any. - fn table_at(&self, at: u32) -> Option<&Self::TableType>; + fn table_at(&self, at: u32) -> Option; /// Returns the linear memory at given index. - fn memory_at(&self, at: u32) -> Option<&Self::MemoryType>; + fn memory_at(&self, at: u32) -> Option; /// Returns the global variable at given index. - fn global_at(&self, at: u32) -> Option<&Self::GlobalType>; - /// Returns the function signature ID at given index. - fn func_type_at(&self, at: u32) -> Option<&::FuncType>; + fn global_at(&self, at: u32) -> Option; + /// Returns the `FuncType` associated with the given type index. + fn func_type_at(&self, type_idx: u32) -> Option<&Self::FuncType>; + /// Returns the `FuncType` associated with the given function index. + fn type_of_function(&self, func_idx: u32) -> Option<&Self::FuncType>; /// Returns the element type at the given index. - fn element_type_at(&self, at: u32) -> Option; + fn element_type_at(&self, at: u32) -> Option; /// Returns the number of elements. fn element_count(&self) -> u32; @@ -319,27 +224,24 @@ impl WasmModuleResources for &'_ T where T: ?Sized + WasmModuleResources, { - type TypeDef = T::TypeDef; - type TableType = T::TableType; - type MemoryType = T::MemoryType; - type GlobalType = T::GlobalType; + type FuncType = T::FuncType; - fn type_at(&self, at: u32) -> Option<&Self::TypeDef> { - T::type_at(self, at) - } - fn table_at(&self, at: u32) -> Option<&Self::TableType> { + fn table_at(&self, at: u32) -> Option { T::table_at(self, at) } - fn memory_at(&self, at: u32) -> Option<&Self::MemoryType> { + fn memory_at(&self, at: u32) -> Option { T::memory_at(self, at) } - fn global_at(&self, at: u32) -> Option<&Self::GlobalType> { + fn global_at(&self, at: u32) -> Option { T::global_at(self, at) } - fn func_type_at(&self, at: u32) -> Option<&::FuncType> { + fn func_type_at(&self, at: u32) -> Option<&Self::FuncType> { T::func_type_at(self, at) } - fn element_type_at(&self, at: u32) -> Option { + fn type_of_function(&self, func_idx: u32) -> Option<&Self::FuncType> { + T::type_of_function(self, func_idx) + } + fn element_type_at(&self, at: u32) -> Option { T::element_type_at(self, at) } @@ -354,26 +256,7 @@ where } } -impl WasmType for crate::Type { - fn to_parser_type(&self) -> crate::Type { - *self - } -} - -impl<'a> WasmTypeDef for TypeDef<'a> { - type FuncType = FuncType; - - fn as_func(&self) -> Option<&Self::FuncType> { - match self { - TypeDef::Func(f) => Some(f), - _ => None, - } - } -} - -impl WasmFuncType for crate::FuncType { - type Type = crate::Type; - +impl WasmFuncType for FuncType { fn len_inputs(&self) -> usize { self.params.len() } @@ -382,52 +265,11 @@ impl WasmFuncType for crate::FuncType { self.returns.len() } - fn input_at(&self, at: u32) -> Option<&Self::Type> { - self.params.get(at as usize) - } - - fn output_at(&self, at: u32) -> Option<&Self::Type> { - self.returns.get(at as usize) - } -} - -impl WasmGlobalType for crate::GlobalType { - type Type = crate::Type; - - fn is_mutable(&self) -> bool { - self.mutable - } - - fn content_type(&self) -> &Self::Type { - &self.content_type + fn input_at(&self, at: u32) -> Option { + self.params.get(at as usize).copied() } -} -impl WasmTableType for crate::TableType { - type Type = crate::Type; - - fn element_type(&self) -> &Self::Type { - &self.element_type - } - - fn initial_limit(&self) -> u32 { - self.limits.initial - } - - fn maximum_limit(&self) -> Option { - self.limits.maximum - } -} - -impl WasmMemoryType for crate::MemoryType { - fn is_shared(&self) -> bool { - self.shared - } - - fn initial_limit(&self) -> u32 { - self.limits.initial - } - fn maximum_limit(&self) -> Option { - self.limits.maximum + fn output_at(&self, at: u32) -> Option { + self.returns.get(at as usize).copied() } } diff --git a/third_party/rust/wasmparser/src/operators_validator.rs b/third_party/rust/wasmparser/src/operators_validator.rs index 150b5aee1d9a9..8cad006095f1b 100644 --- a/third_party/rust/wasmparser/src/operators_validator.rs +++ b/third_party/rust/wasmparser/src/operators_validator.rs @@ -13,268 +13,18 @@ * limitations under the License. */ -use std::cmp::min; +// The basic validation algorithm here is copied from the "Validation +// Algorithm" section of the WebAssembly specification - +// https://webassembly.github.io/spec/core/appendix/algorithm.html. +// +// That algorithm is followed pretty closely here, namely `push_operand`, +// `pop_operand`, `push_ctrl`, and `pop_ctrl`. If anything here is a bit +// confusing it's recomended to read over that section to see how it maps to +// the various methods here. +use crate::limits::MAX_WASM_FUNCTION_LOCALS; use crate::primitives::{MemoryImmediate, Operator, SIMDLaneIndex, Type, TypeOrFuncType}; -use crate::{ - wasm_func_type_inputs, wasm_func_type_outputs, BinaryReaderError, WasmFuncType, WasmGlobalType, - WasmModuleResources, WasmTableType, WasmType, WasmTypeDef, -}; - -#[derive(Debug)] -struct BlockState { - start_types: Vec, - return_types: Vec, - // Position in `FuncState::stack_types` array where block values - // start. - stack_starts_at: usize, - // True for loop. - jump_to_top: bool, - is_else_allowed: bool, - is_dead_code: bool, - // Amount of the required polymorphic values at the stack_starts_at - // position in `FuncState::stack_types` array. These values are - // fictitious and are not actually present in the stack_types. - polymorphic_values: Option, -} - -impl BlockState { - fn is_stack_polymorphic(&self) -> bool { - self.polymorphic_values.is_some() - } -} - -#[derive(Debug)] -struct FuncState { - local_types: Vec, - blocks: Vec, - stack_types: Vec, - end_function: bool, -} - -impl FuncState { - fn block_at(&self, depth: usize) -> &BlockState { - assert!(depth < self.blocks.len()); - &self.blocks[self.blocks.len() - 1 - depth] - } - fn last_block(&self) -> &BlockState { - self.blocks.last().unwrap() - } - fn stack_type_at(&self, index: usize) -> Option { - let stack_starts_at = self.last_block().stack_starts_at; - if self.last_block().is_stack_polymorphic() - && stack_starts_at + index >= self.stack_types.len() - { - return None; - } - assert!(stack_starts_at + index < self.stack_types.len()); - Some(self.stack_types[self.stack_types.len() - 1 - index]) - } - fn assert_stack_type_at(&self, index: usize, expected: Type) -> bool { - match self.stack_type_at(index) { - Some(ty) => ty == expected, - None => true, - } - } - fn assert_block_stack_len(&self, depth: usize, minimal_len: usize) -> bool { - assert!(depth < self.blocks.len()); - let blocks_end = self.blocks.len(); - let block_offset = blocks_end - 1 - depth; - for i in block_offset..blocks_end { - if self.blocks[i].is_stack_polymorphic() { - return true; - } - } - let block_starts_at = self.blocks[block_offset].stack_starts_at; - self.stack_types.len() >= block_starts_at + minimal_len - } - fn assert_last_block_stack_len_exact(&self, len: usize) -> bool { - let block_starts_at = self.last_block().stack_starts_at; - if self.last_block().is_stack_polymorphic() { - let polymorphic_values = self.last_block().polymorphic_values.unwrap(); - self.stack_types.len() + polymorphic_values <= block_starts_at + len - } else { - self.stack_types.len() == block_starts_at + len - } - } - fn remove_frame_stack_types(&mut self, remove_count: usize) -> OperatorValidatorResult<()> { - if remove_count == 0 { - return Ok(()); - } - let last_block = self.blocks.last_mut().unwrap(); - if last_block.is_stack_polymorphic() { - let len = self.stack_types.len(); - let remove_non_polymorphic = len - .checked_sub(last_block.stack_starts_at) - .ok_or_else(|| OperatorValidatorError::new("invalid block signature"))? - .min(remove_count); - self.stack_types.truncate(len - remove_non_polymorphic); - let polymorphic_values = last_block.polymorphic_values.unwrap(); - let remove_polymorphic = min(remove_count - remove_non_polymorphic, polymorphic_values); - last_block.polymorphic_values = Some(polymorphic_values - remove_polymorphic); - } else { - assert!(self.stack_types.len() >= last_block.stack_starts_at + remove_count); - let keep = self.stack_types.len() - remove_count; - self.stack_types.truncate(keep); - } - Ok(()) - } - fn push_block( - &mut self, - ty: TypeOrFuncType, - block_type: BlockType, - resources: impl WasmModuleResources, - ) -> OperatorValidatorResult<()> { - let (start_types, return_types) = match ty { - TypeOrFuncType::Type(Type::EmptyBlockType) => (vec![], vec![]), - TypeOrFuncType::Type(ty) => (vec![], vec![ty]), - TypeOrFuncType::FuncType(idx) => { - let ty = func_type_at(&resources, idx)?; - ( - wasm_func_type_inputs(ty) - .map(WasmType::to_parser_type) - .collect::>(), - wasm_func_type_outputs(ty) - .map(WasmType::to_parser_type) - .collect::>(), - ) - } - }; - if block_type == BlockType::If { - // Collect conditional value from the stack_types. - let last_block = self.blocks.last().unwrap(); - if !last_block.is_stack_polymorphic() - || self.stack_types.len() > last_block.stack_starts_at - { - self.stack_types.pop(); - } - assert!(self.stack_types.len() >= last_block.stack_starts_at); - } - for (i, ty) in start_types.iter().rev().enumerate() { - if !self.assert_stack_type_at(i, *ty) { - return Err(OperatorValidatorError::new("stack operand type mismatch")); - } - } - let (stack_starts_at, polymorphic_values) = { - // When stack for last block is polymorphic, ensure that - // the polymorphic_values matches, and next block is informed about that. - let last_block = self.blocks.last_mut().unwrap(); - if !last_block.is_stack_polymorphic() - || last_block.stack_starts_at + start_types.len() <= self.stack_types.len() - { - (self.stack_types.len() - start_types.len(), None) - } else { - let unknown_stack_types_len = - last_block.stack_starts_at + start_types.len() - self.stack_types.len(); - (last_block.stack_starts_at, Some(unknown_stack_types_len)) - } - }; - self.blocks.push(BlockState { - start_types, - return_types, - stack_starts_at, - jump_to_top: block_type == BlockType::Loop, - is_else_allowed: block_type == BlockType::If, - is_dead_code: false, - polymorphic_values, - }); - Ok(()) - } - fn pop_block(&mut self) { - assert!(self.blocks.len() > 1); - let last_block = self.blocks.pop().unwrap(); - if last_block.is_stack_polymorphic() { - assert!( - self.stack_types.len() - <= last_block.return_types.len() + last_block.stack_starts_at - ); - } else { - assert!( - self.stack_types.len() - == last_block.return_types.len() + last_block.stack_starts_at - ); - } - let keep = last_block.stack_starts_at; - self.stack_types.truncate(keep); - self.stack_types.extend_from_slice(&last_block.return_types); - } - fn reset_block(&mut self) { - assert!(self.last_block().is_else_allowed); - let last_block = self.blocks.last_mut().unwrap(); - let keep = last_block.stack_starts_at; - self.stack_types.truncate(keep); - self.stack_types - .extend(last_block.start_types.iter().cloned()); - last_block.is_else_allowed = false; - last_block.polymorphic_values = None; - } - fn change_frame(&mut self, remove_count: usize) -> OperatorValidatorResult<()> { - self.remove_frame_stack_types(remove_count) - } - fn change_frame_with_type( - &mut self, - remove_count: usize, - ty: Type, - ) -> OperatorValidatorResult<()> { - self.remove_frame_stack_types(remove_count)?; - self.stack_types.push(ty); - Ok(()) - } - fn change_frame_with_types( - &mut self, - remove_count: usize, - new_items: I, - ) -> OperatorValidatorResult<()> - where - I: Iterator, - { - self.remove_frame_stack_types(remove_count)?; - self.stack_types.extend(new_items); - Ok(()) - } - fn change_frame_to_exact_types_from(&mut self, depth: usize) -> OperatorValidatorResult<()> { - let types = self.block_at(depth).return_types.clone(); - let last_block = self.blocks.last_mut().unwrap(); - let keep = last_block.stack_starts_at; - self.stack_types.truncate(keep); - self.stack_types.extend_from_slice(&types); - last_block.polymorphic_values = None; - Ok(()) - } - fn change_frame_after_select(&mut self, ty: Option) -> OperatorValidatorResult<()> { - self.remove_frame_stack_types(3)?; - if ty.is_none() { - let last_block = self.blocks.last_mut().unwrap(); - assert!(last_block.is_stack_polymorphic()); - last_block.polymorphic_values = Some(last_block.polymorphic_values.unwrap() + 1); - return Ok(()); - } - self.stack_types.push(ty.unwrap()); - Ok(()) - } - fn start_dead_code(&mut self) { - let last_block = self.blocks.last_mut().unwrap(); - let keep = last_block.stack_starts_at; - self.stack_types.truncate(keep); - last_block.is_dead_code = true; - last_block.polymorphic_values = Some(0); - } - fn end_function(&mut self) { - self.end_function = true; - } -} - -#[derive(Copy, Clone, PartialEq, Eq)] -enum BlockType { - Block, - Loop, - If, -} - -pub enum FunctionEnd { - No, - Yes, -} +use crate::{BinaryReaderError, Result, WasmFeatures, WasmFuncType, WasmModuleResources}; /// A wrapper around a `BinaryReaderError` where the inner error's offset is a /// temporary placeholder value. This can be converted into a proper @@ -315,377 +65,288 @@ impl OperatorValidatorError { type OperatorValidatorResult = std::result::Result; -#[derive(Copy, Clone, Debug)] -pub struct OperatorValidatorConfig { - pub enable_threads: bool, - pub enable_reference_types: bool, - pub enable_simd: bool, - pub enable_bulk_memory: bool, - pub enable_multi_value: bool, - pub enable_tail_call: bool, - pub enable_module_linking: bool, - - #[cfg(feature = "deterministic")] - pub deterministic_only: bool, -} +pub(crate) struct OperatorValidator { + // The total number of locals that this function contains + num_locals: u32, + // This is a "compressed" list of locals for this function. The list of + // locals are represented as a list of tuples. The second element is the + // type of the local, and the first element is monotonically increasing as + // you visit elements of this list. The first element is the maximum index + // of the local, after the previous index, of the type specified. + // + // This allows us to do a binary search on the list for a local's index for + // `local.{get,set,tee}`. We do a binary search for the index desired, and + // it either lies in a "hole" where the maximum index is specified later, + // or it's at the end of the list meaning it's out of bounds. + locals: Vec<(u32, Type)>, -pub(crate) const DEFAULT_OPERATOR_VALIDATOR_CONFIG: OperatorValidatorConfig = - OperatorValidatorConfig { - enable_threads: false, - enable_reference_types: false, - enable_simd: false, - enable_bulk_memory: false, - enable_multi_value: true, - enable_tail_call: false, - enable_module_linking: false, + // The `operands` is the current type stack, and the `control` list is the + // list of blocks that we're currently in. + operands: Vec>, + control: Vec, - #[cfg(feature = "deterministic")] - deterministic_only: true, - }; + // This is a list of flags for wasm features which are used to gate various + // instructions. + features: WasmFeatures, +} -pub(crate) fn check_value_type( - ty: Type, - operator_config: &OperatorValidatorConfig, -) -> OperatorValidatorResult<()> { - match ty { - Type::I32 | Type::I64 | Type::F32 | Type::F64 => Ok(()), - Type::FuncRef | Type::ExternRef => { - if !operator_config.enable_reference_types { - return Err(OperatorValidatorError::new( - "reference types support is not enabled", - )); - } - Ok(()) - } - Type::V128 => { - if !operator_config.enable_simd { - return Err(OperatorValidatorError::new("SIMD support is not enabled")); - } - Ok(()) - } - _ => Err(OperatorValidatorError::new("invalid value type")), - } +// This structure corresponds to `ctrl_frame` as specified at in the validation +// appendix of the wasm spec +struct Frame { + // Indicator for what kind of instruction pushed this frame. + kind: FrameKind, + // The type signature of this frame, represented as a singular return type + // or a type index pointing into the module's types. + block_type: TypeOrFuncType, + // The index, below which, this frame cannot modify the operand stack. + height: usize, + // Whether this frame is unreachable so far. + unreachable: bool, } -#[derive(Debug)] -pub(crate) struct OperatorValidator { - func_state: FuncState, - config: OperatorValidatorConfig, +#[derive(PartialEq, Copy, Clone)] +enum FrameKind { + Block, + If, + Else, + Loop, } impl OperatorValidator { - pub fn new( - func_type: &F, - locals: &[(u32, Type)], - config: OperatorValidatorConfig, - ) -> OperatorValidatorResult - where - F: WasmFuncType, - T: WasmType, - { - let local_types = { - let mut local_types = wasm_func_type_inputs(func_type) - .map(WasmType::to_parser_type) - .collect::>(); - for local in locals { - check_value_type(local.1, &config)?; - for _ in 0..local.0 { - local_types.push(local.1); - } - } - local_types - }; - let mut blocks = Vec::new(); - let last_returns = wasm_func_type_outputs(func_type) - .map(WasmType::to_parser_type) + pub fn new( + ty: u32, + offset: usize, + features: &WasmFeatures, + resources: &impl WasmModuleResources, + ) -> Result { + let locals = func_type_at(resources, ty) + .map_err(|e| e.set_offset(offset))? + .inputs() + .enumerate() + .map(|(i, ty)| (i as u32, ty)) .collect::>(); - blocks.push(BlockState { - start_types: vec![], - return_types: last_returns, - stack_starts_at: 0, - jump_to_top: false, - is_else_allowed: false, - is_dead_code: false, - polymorphic_values: None, - }); - Ok(OperatorValidator { - func_state: FuncState { - local_types, - blocks, - stack_types: Vec::new(), - end_function: false, - }, - config, + num_locals: locals.len() as u32, + locals, + operands: Vec::new(), + control: vec![Frame { + kind: FrameKind::Block, + block_type: TypeOrFuncType::FuncType(ty), + height: 0, + unreachable: false, + }], + features: *features, }) } - fn check_frame_size(&self, require_count: usize) -> OperatorValidatorResult<()> { - if !self.func_state.assert_block_stack_len(0, require_count) { - Err(OperatorValidatorError::new( - "type mismatch: not enough operands", - )) - } else { - Ok(()) + pub fn define_locals(&mut self, offset: usize, count: u32, ty: Type) -> Result<()> { + self.features + .check_value_type(ty) + .map_err(|e| BinaryReaderError::new(e, offset))?; + if count == 0 { + return Ok(()); } - } - - fn check_operands_1(&self, operand: Type) -> OperatorValidatorResult<()> { - self.check_frame_size(1)?; - if !self.func_state.assert_stack_type_at(0, operand) { - return Err(OperatorValidatorError::new("stack operand type mismatch")); + match self.num_locals.checked_add(count) { + Some(n) => self.num_locals = n, + None => return Err(BinaryReaderError::new("locals overflow", offset)), } - Ok(()) - } - - fn check_operands_2(&self, operand1: Type, operand2: Type) -> OperatorValidatorResult<()> { - self.check_frame_size(2)?; - if !self.func_state.assert_stack_type_at(1, operand1) { - return Err(OperatorValidatorError::new("stack operand type mismatch")); - } - if !self.func_state.assert_stack_type_at(0, operand2) { - return Err(OperatorValidatorError::new("stack operand type mismatch")); + if self.num_locals > (MAX_WASM_FUNCTION_LOCALS as u32) { + return Err(BinaryReaderError::new("locals exceed maximum", offset)); } + self.locals.push((self.num_locals - 1, ty)); Ok(()) } - fn check_operands_3( - &self, - operand1: Type, - operand2: Type, - operand3: Type, - ) -> OperatorValidatorResult<()> { - self.check_frame_size(3)?; - if !self.func_state.assert_stack_type_at(2, operand1) { - return Err(OperatorValidatorError::new("stack operand type mismatch")); + /// Fetches the type for the local at `idx`, returning an error if it's out + /// of bounds. + fn local(&self, idx: u32) -> OperatorValidatorResult { + match self.locals.binary_search_by_key(&idx, |(idx, _)| *idx) { + // If this index would be inserted at the end of the list, then the + // index is out of bounds and we return an error. + Err(i) if i == self.locals.len() => { + bail_op_err!("unknown local {}: local index out of bounds", idx) + } + // If `Ok` is returned we found the index exactly, or if `Err` is + // returned the position is the one which is the least index + // greater thatn `idx`, which is still the type of `idx` according + // to our "compressed" representation. In both cases we access the + // list at index `i`. + Ok(i) | Err(i) => Ok(self.locals[i].1), } - if !self.func_state.assert_stack_type_at(1, operand2) { - return Err(OperatorValidatorError::new("stack operand type mismatch")); - } - if !self.func_state.assert_stack_type_at(0, operand3) { - return Err(OperatorValidatorError::new("stack operand type mismatch")); - } - Ok(()) } - fn check_operands(&self, expected_types: I) -> OperatorValidatorResult<()> - where - I: ExactSizeIterator, - { - let len = expected_types.len(); - self.check_frame_size(len)?; - for (i, expected_type) in expected_types.enumerate() { - if !self - .func_state - .assert_stack_type_at(len - 1 - i, expected_type) - { - return Err(OperatorValidatorError::new("stack operand type mismatch")); - } - } + /// Pushes a type onto the operand stack. + /// + /// This is used by instructions to represent a value that is pushed to the + /// operand stack. This can fail, but only if `Type` is feature gated. + /// Otherwise the push operation always succeeds. + fn push_operand(&mut self, ty: Type) -> OperatorValidatorResult<()> { + self.features + .check_value_type(ty) + .map_err(OperatorValidatorError::new)?; + self.operands.push(Some(ty)); Ok(()) } - fn check_block_return_types( - &self, - block: &BlockState, - reserve_items: usize, - ) -> OperatorValidatorResult<()> { - if !self.config.enable_multi_value && block.return_types.len() > 1 { - return Err(OperatorValidatorError::new( - "blocks, loops, and ifs may only return at most one \ - value when multi-value is not enabled", - )); - } - let len = block.return_types.len(); - for i in 0..len { - if !self - .func_state - .assert_stack_type_at(len - 1 - i + reserve_items, block.return_types[i]) - { - return Err(OperatorValidatorError::new( - "type mismatch: stack item type does not match block item type", - )); + /// Attempts to pop a type from the operand stack. + /// + /// This function is used to remove types from the operand stack. The + /// `expected` argument can be used to indicate that a type is required, or + /// simply that something is needed to be popped. + /// + /// If `expected` is `Some(T)` then this will be guaranteed to return + /// `Some(T)`, and it will only return success if the current block is + /// unreachable or if `T` was found at the top of the operand stack. + /// + /// If `expected` is `None` then it indicates that something must be on the + /// operand stack, but it doesn't matter what's on the operand stack. This + /// is useful for polymorphic instructions like `select`. + /// + /// If `Some(T)` is returned then `T` was popped from the operand stack and + /// matches `expected`. If `None` is returned then it means that `None` was + /// expected and a type was successfully popped, but its exact type is + /// indeterminate because the current block is unreachable. + fn pop_operand(&mut self, expected: Option) -> OperatorValidatorResult> { + let control = self.control.last().unwrap(); + let actual = if self.operands.len() == control.height { + if control.unreachable { + None + } else { + bail_op_err!( + "type mismatch: expected {:?} but nothing on stack", + expected + ) } + } else { + self.operands.pop().unwrap() + }; + if actual.is_none() { + Ok(expected) + } else if expected.is_none() { + Ok(actual) + } else if actual != expected { + bail_op_err!("type mismatch: expected {:?}, found {:?}", expected, actual) + } else { + Ok(actual) } - Ok(()) - } - - fn check_block_return(&self) -> OperatorValidatorResult<()> { - let len = self.func_state.last_block().return_types.len(); - if !self.func_state.assert_last_block_stack_len_exact(len) { - return Err(OperatorValidatorError::new( - "type mismatch: stack size does not match block type", - )); - } - self.check_block_return_types(self.func_state.last_block(), 0) } - fn check_call( - &mut self, - function_index: u32, - resources: impl WasmModuleResources, - ) -> OperatorValidatorResult<()> { - let ty = match resources.func_type_at(function_index) { - Some(i) => i, - None => { - bail_op_err!( - "unknown function {}: function index out of bounds", - function_index - ); - } - }; - self.check_operands(wasm_func_type_inputs(ty).map(WasmType::to_parser_type))?; - self.func_state.change_frame_with_types( - ty.len_inputs(), - wasm_func_type_outputs(ty).map(WasmType::to_parser_type), - )?; - Ok(()) + /// Flags the current control frame as unreachable, additionally truncating + /// the currently active operand stack. + fn unreachable(&mut self) { + let control = self.control.last_mut().unwrap(); + self.operands.truncate(control.height); + control.unreachable = true; } - fn check_call_indirect( + /// Pushes a new frame onto the control stack. + /// + /// This operation is used when entering a new block such as an if, loop, + /// or block itself. The `kind` of block is specified which indicates how + /// breaks interact with this block's type. Additionally the type signature + /// of the block is specified by `ty`. + fn push_ctrl( &mut self, - index: u32, - table_index: u32, - resources: impl WasmModuleResources, + kind: FrameKind, + ty: TypeOrFuncType, + resources: &impl WasmModuleResources, ) -> OperatorValidatorResult<()> { - if resources.table_at(table_index).is_none() { - return Err(OperatorValidatorError::new( - "unknown table: table index out of bounds", - )); + // Push a new frame which has a snapshot of the height of the current + // operand stack. + self.control.push(Frame { + kind, + block_type: ty, + height: self.operands.len(), + unreachable: false, + }); + // All of the parameters are now also available in this control frame, + // so we push them here in order. + for ty in params(ty, resources)? { + self.push_operand(ty)?; } - let ty = func_type_at(&resources, index)?; - let types = { - let mut types = Vec::with_capacity(ty.len_inputs() + 1); - types.extend(wasm_func_type_inputs(ty).map(WasmType::to_parser_type)); - types.push(Type::I32); - types - }; - self.check_operands(types.into_iter())?; - self.func_state.change_frame_with_types( - ty.len_inputs() + 1, - wasm_func_type_outputs(ty).map(WasmType::to_parser_type), - )?; Ok(()) } - fn check_return(&mut self) -> OperatorValidatorResult<()> { - let depth = (self.func_state.blocks.len() - 1) as u32; - self.check_jump_from_block(depth, 0)?; - self.func_state.start_dead_code(); - Ok(()) - } + /// Pops a frame from the control stack. + /// + /// This function is used when exiting a block and leaves a block scope. + /// Internally this will validate that blocks have the correct result type. + fn pop_ctrl(&mut self, resources: &impl WasmModuleResources) -> OperatorValidatorResult { + // Read the expected type and expected height of the operand stack the + // end of the frame. + let frame = self.control.last().unwrap(); + let ty = frame.block_type; + let height = frame.height; - fn check_jump_from_block( - &self, - relative_depth: u32, - reserve_items: usize, - ) -> OperatorValidatorResult<()> { - if relative_depth as usize >= self.func_state.blocks.len() { - return Err(OperatorValidatorError::new( - "unknown label: invalid block depth", - )); - } - let block = self.func_state.block_at(relative_depth as usize); - if block.jump_to_top { - let len = block.start_types.len(); - if !self - .func_state - .assert_block_stack_len(0, reserve_items + len) - { - return Err(OperatorValidatorError::new( - "type mismatch: stack size does not match target loop type", - )); - } - for i in 0..len { - if !self - .func_state - .assert_stack_type_at(len - 1 - i + reserve_items, block.start_types[i]) - { - return Err(OperatorValidatorError::new( - "type mismatch: stack item type does not match block param type", - )); - } - } - return Ok(()); + // Pop all the result types, in reverse order, from the operand stack. + // These types will, possibly, be transferred to the next frame. + for ty in results(ty, resources)?.rev() { + self.pop_operand(Some(ty))?; } - let len = block.return_types.len(); - if !self - .func_state - .assert_block_stack_len(0, len + reserve_items) - { - return Err(OperatorValidatorError::new( - "type mismatch: stack size does not match target block type", - )); + // Make sure that the operand stack has returned to is original + // height... + if self.operands.len() != height { + bail_op_err!("type mismatch: values remaining on stack at end of block"); } - self.check_block_return_types(block, reserve_items) + + // And then we can remove it! + Ok(self.control.pop().unwrap()) } - fn match_block_return(&self, depth1: u32, depth2: u32) -> OperatorValidatorResult<()> { - if depth1 as usize >= self.func_state.blocks.len() { - return Err(OperatorValidatorError::new( - "unknown label: invalid block depth", - )); - } - if depth2 as usize >= self.func_state.blocks.len() { - return Err(OperatorValidatorError::new( - "unknown label: invalid block depth", - )); + /// Validates a relative jump to the `depth` specified. + /// + /// Returns the type signature of the block that we're jumping to as well + /// as the kind of block if the jump is valid. Otherwise returns an error. + fn jump(&self, depth: u32) -> OperatorValidatorResult<(TypeOrFuncType, FrameKind)> { + match (self.control.len() - 1).checked_sub(depth as usize) { + Some(i) => { + let frame = &self.control[i]; + Ok((frame.block_type, frame.kind)) + } + None => bail_op_err!("unknown label: branch depth too large"), } - let block1 = self.func_state.block_at(depth1 as usize); - let block2 = self.func_state.block_at(depth2 as usize); - let return_types1 = &block1.return_types; - let return_types2 = &block2.return_types; - if block1.jump_to_top || block2.jump_to_top { - if block1.jump_to_top { - if !block2.jump_to_top && !return_types2.is_empty() { - return Err(OperatorValidatorError::new( - "type mismatch: block types do not match", - )); - } - } else if !return_types1.is_empty() { - return Err(OperatorValidatorError::new( - "type mismatch: block types do not match", - )); - } - } else if *return_types1 != *return_types2 { - return Err(OperatorValidatorError::new( - "type mismatch: block types do not match", - )); - } - Ok(()) } + /// Validates that `memory_index` is valid in this module, and returns the + /// type of address used to index the memory specified. fn check_memory_index( &self, memory_index: u32, resources: impl WasmModuleResources, - ) -> OperatorValidatorResult<()> { - if resources.memory_at(memory_index).is_none() { - bail_op_err!("unknown memory {}", memory_index); + ) -> OperatorValidatorResult { + if memory_index > 0 && !self.features.multi_memory { + return Err(OperatorValidatorError::new( + "multi-memory support is not enabled", + )); + } + match resources.memory_at(memory_index) { + Some(mem) => Ok(mem.index_type()), + None => bail_op_err!("unknown memory {}", memory_index), } - Ok(()) } + /// Validates a `memarg for alignment and such (also the memory it + /// references), and returns the type of index used to address the memory. fn check_memarg( &self, memarg: MemoryImmediate, - max_align: u32, + max_align: u8, resources: impl WasmModuleResources, - ) -> OperatorValidatorResult<()> { - self.check_memory_index(0, resources)?; - let align = memarg.flags; + ) -> OperatorValidatorResult { + let index_ty = self.check_memory_index(memarg.memory, resources)?; + let align = memarg.align; if align > max_align { return Err(OperatorValidatorError::new( "alignment must not be larger than natural", )); } - Ok(()) + Ok(index_ty) } #[cfg(feature = "deterministic")] fn check_non_deterministic_enabled(&self) -> OperatorValidatorResult<()> { - if !self.config.deterministic_only { + if !self.features.deterministic_only { return Err(OperatorValidatorError::new( "deterministic_only support is not enabled", )); @@ -700,7 +361,7 @@ impl OperatorValidator { } fn check_threads_enabled(&self) -> OperatorValidatorResult<()> { - if !self.config.enable_threads { + if !self.features.threads { return Err(OperatorValidatorError::new( "threads support is not enabled", )); @@ -709,7 +370,7 @@ impl OperatorValidator { } fn check_reference_types_enabled(&self) -> OperatorValidatorResult<()> { - if !self.config.enable_reference_types { + if !self.features.reference_types { return Err(OperatorValidatorError::new( "reference types support is not enabled", )); @@ -718,14 +379,14 @@ impl OperatorValidator { } fn check_simd_enabled(&self) -> OperatorValidatorResult<()> { - if !self.config.enable_simd { + if !self.features.simd { return Err(OperatorValidatorError::new("SIMD support is not enabled")); } Ok(()) } fn check_bulk_memory_enabled(&self) -> OperatorValidatorResult<()> { - if !self.config.enable_bulk_memory { + if !self.features.bulk_memory { return Err(OperatorValidatorError::new( "bulk memory support is not enabled", )); @@ -737,9 +398,8 @@ impl OperatorValidator { &self, _: MemoryImmediate, resources: impl WasmModuleResources, - ) -> OperatorValidatorResult<()> { - self.check_memory_index(0, resources)?; - Ok(()) + ) -> OperatorValidatorResult { + self.check_memory_index(0, resources) } fn check_simd_lane_index(&self, index: SIMDLaneIndex, max: u8) -> OperatorValidatorResult<()> { @@ -749,6 +409,7 @@ impl OperatorValidator { Ok(()) } + /// Validates a block type, primarily with various in-flight proposals. fn check_block_type( &self, ty: TypeOrFuncType, @@ -766,7 +427,7 @@ impl OperatorValidator { TypeOrFuncType::Type(Type::V128) => self.check_simd_enabled(), TypeOrFuncType::FuncType(idx) => { let ty = func_type_at(&resources, idx)?; - if !self.config.enable_multi_value { + if !self.features.multi_value { if ty.len_outputs() > 1 { return Err(OperatorValidatorError::new( "blocks, loops, and ifs may only return at most one \ @@ -786,209 +447,231 @@ impl OperatorValidator { } } - fn check_block_params( - &self, - ty: TypeOrFuncType, - resources: impl WasmModuleResources, - skip: usize, + /// Validates a `call` instruction, ensuring that the function index is + /// in-bounds and the right types are on the stack to call the function. + fn check_call( + &mut self, + function_index: u32, + resources: &impl WasmModuleResources, ) -> OperatorValidatorResult<()> { - if let TypeOrFuncType::FuncType(idx) = ty { - let func_ty = func_type_at(&resources, idx)?; - let len = func_ty.len_inputs(); - self.check_frame_size(len + skip)?; - for (i, ty) in wasm_func_type_inputs(func_ty).enumerate() { - if !self - .func_state - .assert_stack_type_at(len - 1 - i + skip, ty.to_parser_type()) - { - return Err(OperatorValidatorError::new( - "stack operand type mismatch for block", - )); - } + let ty = match resources.type_of_function(function_index) { + Some(i) => i, + None => { + bail_op_err!( + "unknown function {}: function index out of bounds", + function_index + ); } + }; + for ty in ty.inputs().rev() { + self.pop_operand(Some(ty))?; + } + for ty in ty.outputs() { + self.push_operand(ty)?; } Ok(()) } - fn check_select(&self, expected_ty: Option) -> OperatorValidatorResult> { - self.check_frame_size(3)?; - let func_state = &self.func_state; - let last_block = func_state.last_block(); - - let ty = if last_block.is_stack_polymorphic() { - match func_state.stack_types.len() - last_block.stack_starts_at { - 0 => return Ok(None), - 1 => { - self.check_operands_1(Type::I32)?; - return Ok(None); - } - 2 => { - self.check_operands_1(Type::I32)?; - func_state.stack_types[func_state.stack_types.len() - 2] - } - _ => { - let ty = expected_ty - .unwrap_or(func_state.stack_types[func_state.stack_types.len() - 3]); - self.check_operands_2(ty, Type::I32)?; - ty - } - } - } else { - let ty = - expected_ty.unwrap_or(func_state.stack_types[func_state.stack_types.len() - 3]); - self.check_operands_2(ty, Type::I32)?; - ty - }; + /// Validates a call to an indirect function, very similar to `check_call`. + fn check_call_indirect( + &mut self, + index: u32, + table_index: u32, + resources: &impl WasmModuleResources, + ) -> OperatorValidatorResult<()> { + if resources.table_at(table_index).is_none() { + return Err(OperatorValidatorError::new( + "unknown table: table index out of bounds", + )); + } + let ty = func_type_at(&resources, index)?; + self.pop_operand(Some(Type::I32))?; + for ty in ty.inputs().rev() { + self.pop_operand(Some(ty))?; + } + for ty in ty.outputs() { + self.push_operand(ty)?; + } + Ok(()) + } - Ok(Some(ty)) + /// Validates a `return` instruction, popping types from the operand + /// stack that the function needs. + fn check_return( + &mut self, + resources: &impl WasmModuleResources, + ) -> OperatorValidatorResult<()> { + for ty in results(self.control[0].block_type, resources)?.rev() { + self.pop_operand(Some(ty))?; + } + self.unreachable(); + Ok(()) } - pub(crate) fn process_operator( + pub fn process_operator( &mut self, operator: &Operator, resources: &impl WasmModuleResources, - ) -> OperatorValidatorResult { - if self.func_state.end_function { - return Err(OperatorValidatorError::new("unexpected operator")); + ) -> OperatorValidatorResult<()> { + if self.control.len() == 0 { + bail_op_err!("operators remaining after end of function"); } match *operator { - Operator::Unreachable => self.func_state.start_dead_code(), - Operator::Nop => (), + Operator::Nop => {} + + // Note that the handling of these control flow operators are the + // same as specified in the "Validation Algorithm" appendix of the + // online wasm specification (referenced at the top of this module). + Operator::Unreachable => self.unreachable(), Operator::Block { ty } => { self.check_block_type(ty, resources)?; - self.check_block_params(ty, resources, 0)?; - self.func_state - .push_block(ty, BlockType::Block, resources)?; + for ty in params(ty, resources)?.rev() { + self.pop_operand(Some(ty))?; + } + self.push_ctrl(FrameKind::Block, ty, resources)?; } Operator::Loop { ty } => { self.check_block_type(ty, resources)?; - self.check_block_params(ty, resources, 0)?; - self.func_state.push_block(ty, BlockType::Loop, resources)?; + for ty in params(ty, resources)?.rev() { + self.pop_operand(Some(ty))?; + } + self.push_ctrl(FrameKind::Loop, ty, resources)?; } Operator::If { ty } => { self.check_block_type(ty, resources)?; - self.check_operands_1(Type::I32)?; - self.check_block_params(ty, resources, 1)?; - self.func_state.push_block(ty, BlockType::If, resources)?; + self.pop_operand(Some(Type::I32))?; + for ty in params(ty, resources)?.rev() { + self.pop_operand(Some(ty))?; + } + self.push_ctrl(FrameKind::If, ty, resources)?; } Operator::Else => { - if !self.func_state.last_block().is_else_allowed { - return Err(OperatorValidatorError::new( - "unexpected else: if block is not started", - )); + let frame = self.pop_ctrl(resources)?; + if frame.kind != FrameKind::If { + bail_op_err!("else found outside of an `if` block"); } - self.check_block_return()?; - self.func_state.reset_block() + self.push_ctrl(FrameKind::Else, frame.block_type, resources)?; } Operator::End => { - self.check_block_return()?; - if self.func_state.blocks.len() == 1 { - self.func_state.end_function(); - return Ok(FunctionEnd::Yes); - } + let mut frame = self.pop_ctrl(resources)?; - let last_block = &self.func_state.last_block(); - if last_block.is_else_allowed && last_block.start_types != last_block.return_types { - return Err(OperatorValidatorError::new("type mismatch: else is expected: if block has a type that can't be implemented with a no-op")); + // Note that this `if` isn't included in the appendix right + // now, but it's used to allow for `if` statements that are + // missing an `else` block which have the same parameter/return + // types on the block (since that's valid). + if frame.kind == FrameKind::If { + self.push_ctrl(FrameKind::Else, frame.block_type, resources)?; + frame = self.pop_ctrl(resources)?; + } + for ty in results(frame.block_type, resources)? { + self.push_operand(ty)?; } - self.func_state.pop_block() } Operator::Br { relative_depth } => { - self.check_jump_from_block(relative_depth, 0)?; - self.func_state.start_dead_code() + let (ty, kind) = self.jump(relative_depth)?; + for ty in label_types(ty, resources, kind)?.rev() { + self.pop_operand(Some(ty))?; + } + self.unreachable(); } Operator::BrIf { relative_depth } => { - self.check_operands_1(Type::I32)?; - self.check_jump_from_block(relative_depth, 1)?; - if self.func_state.last_block().is_stack_polymorphic() { - self.func_state - .change_frame_to_exact_types_from(relative_depth as usize)?; - } else { - self.func_state.change_frame(1)?; + self.pop_operand(Some(Type::I32))?; + let (ty, kind) = self.jump(relative_depth)?; + for ty in label_types(ty, resources, kind)?.rev() { + self.pop_operand(Some(ty))?; + } + for ty in label_types(ty, resources, kind)? { + self.push_operand(ty)?; } } Operator::BrTable { ref table } => { - self.check_operands_1(Type::I32)?; - let mut depth0: Option = None; - for relative_depth in table { - if depth0.is_none() { - self.check_jump_from_block(relative_depth, 1)?; - depth0 = Some(relative_depth); - continue; + self.pop_operand(Some(Type::I32))?; + let mut label = None; + for element in table.targets() { + let (relative_depth, _is_default) = element.map_err(|mut e| { + e.inner.offset = usize::max_value(); + OperatorValidatorError(e) + })?; + let block = self.jump(relative_depth)?; + match label { + None => label = Some(block), + Some(prev) => { + let a = label_types(block.0, resources, block.1)?; + let b = label_types(prev.0, resources, prev.1)?; + if a.ne(b) { + bail_op_err!( + "type mismatch: br_table target labels have different types" + ); + } + } } - self.match_block_return(relative_depth, depth0.unwrap())?; } - self.func_state.start_dead_code() + let (ty, kind) = label.unwrap(); + for ty in label_types(ty, resources, kind)?.rev() { + self.pop_operand(Some(ty))?; + } + self.unreachable(); } - Operator::Return => self.check_return()?, + Operator::Return => self.check_return(resources)?, Operator::Call { function_index } => self.check_call(function_index, resources)?, Operator::ReturnCall { function_index } => { - if !self.config.enable_tail_call { + if !self.features.tail_call { return Err(OperatorValidatorError::new( "tail calls support is not enabled", )); } self.check_call(function_index, resources)?; - self.check_return()?; + self.check_return(resources)?; } Operator::CallIndirect { index, table_index } => { self.check_call_indirect(index, table_index, resources)? } Operator::ReturnCallIndirect { index, table_index } => { - if !self.config.enable_tail_call { + if !self.features.tail_call { return Err(OperatorValidatorError::new( "tail calls support is not enabled", )); } self.check_call_indirect(index, table_index, resources)?; - self.check_return()?; + self.check_return(resources)?; } Operator::Drop => { - self.check_frame_size(1)?; - self.func_state.change_frame(1)?; + self.pop_operand(None)?; } Operator::Select => { - let ty = self.check_select(None)?; - match ty { - Some(Type::I32) | Some(Type::I64) | Some(Type::F32) | Some(Type::F64) => {} - Some(_) => { - bail_op_err!("type mismatch: only integer types allowed with bare `select`") - } - None => {} + self.pop_operand(Some(Type::I32))?; + let ty = self.pop_operand(None)?; + match self.pop_operand(ty)? { + ty @ Some(Type::I32) + | ty @ Some(Type::I64) + | ty @ Some(Type::F32) + | ty @ Some(Type::F64) => self.operands.push(ty), + None => self.operands.push(None), + Some(_) => bail_op_err!("type mismatch: select only takes integral types"), } - self.func_state.change_frame_after_select(ty)?; } Operator::TypedSelect { ty } => { - self.check_select(Some(ty))?; - self.func_state.change_frame_after_select(Some(ty))?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + self.pop_operand(Some(ty))?; + self.push_operand(ty)?; } Operator::LocalGet { local_index } => { - if local_index as usize >= self.func_state.local_types.len() { - bail_op_err!("unknown local {}: local index out of bounds", local_index); - } - let local_type = self.func_state.local_types[local_index as usize]; - self.func_state.change_frame_with_type(0, local_type)?; + let ty = self.local(local_index)?; + self.push_operand(ty)?; } Operator::LocalSet { local_index } => { - if local_index as usize >= self.func_state.local_types.len() { - bail_op_err!("unknown local {}: local index out of bounds", local_index); - } - let local_type = self.func_state.local_types[local_index as usize]; - self.check_operands_1(local_type)?; - self.func_state.change_frame(1)?; + let ty = self.local(local_index)?; + self.pop_operand(Some(ty))?; } Operator::LocalTee { local_index } => { - if local_index as usize >= self.func_state.local_types.len() { - bail_op_err!("unknown local {}: local index out of bounds", local_index); - } - let local_type = self.func_state.local_types[local_index as usize]; - self.check_operands_1(local_type)?; - self.func_state.change_frame_with_type(1, local_type)?; + let ty = self.local(local_index)?; + self.pop_operand(Some(ty))?; + self.push_operand(ty)?; } Operator::GlobalGet { global_index } => { if let Some(ty) = resources.global_at(global_index) { - self.func_state - .change_frame_with_type(0, ty.content_type().to_parser_type())?; + self.push_operand(ty.content_type)?; } else { return Err(OperatorValidatorError::new( "unknown global: global index out of bounds", @@ -997,13 +680,12 @@ impl OperatorValidator { } Operator::GlobalSet { global_index } => { if let Some(ty) = resources.global_at(global_index) { - if !ty.is_mutable() { + if !ty.mutable { return Err(OperatorValidatorError::new( "global is immutable: cannot modify it with `global.set`", )); } - self.check_operands_1(ty.content_type().to_parser_type())?; - self.func_state.change_frame(1)?; + self.pop_operand(Some(ty.content_type))?; } else { return Err(OperatorValidatorError::new( "unknown global: global index out of bounds", @@ -1011,150 +693,127 @@ impl OperatorValidator { }; } Operator::I32Load { memarg } => { - self.check_memarg(memarg, 2, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::I32)?; + let ty = self.check_memarg(memarg, 2, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; } Operator::I64Load { memarg } => { - self.check_memarg(memarg, 3, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::I64)?; + let ty = self.check_memarg(memarg, 3, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I64)?; } Operator::F32Load { memarg } => { self.check_non_deterministic_enabled()?; - self.check_memarg(memarg, 2, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::F32)?; + let ty = self.check_memarg(memarg, 2, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::F32)?; } Operator::F64Load { memarg } => { self.check_non_deterministic_enabled()?; - self.check_memarg(memarg, 3, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::F64)?; - } - Operator::I32Load8S { memarg } => { - self.check_memarg(memarg, 0, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::I32)?; - } - Operator::I32Load8U { memarg } => { - self.check_memarg(memarg, 0, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::I32)?; - } - Operator::I32Load16S { memarg } => { - self.check_memarg(memarg, 1, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::I32)?; - } - Operator::I32Load16U { memarg } => { - self.check_memarg(memarg, 1, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::I32)?; - } - Operator::I64Load8S { memarg } => { - self.check_memarg(memarg, 0, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::I64)?; - } - Operator::I64Load8U { memarg } => { - self.check_memarg(memarg, 0, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::I64)?; - } - Operator::I64Load16S { memarg } => { - self.check_memarg(memarg, 1, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::I64)?; - } - Operator::I64Load16U { memarg } => { - self.check_memarg(memarg, 1, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::I64)?; - } - Operator::I64Load32S { memarg } => { - self.check_memarg(memarg, 2, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::I64)?; - } - Operator::I64Load32U { memarg } => { - self.check_memarg(memarg, 2, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::I64)?; + let ty = self.check_memarg(memarg, 3, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::F64)?; + } + Operator::I32Load8S { memarg } | Operator::I32Load8U { memarg } => { + let ty = self.check_memarg(memarg, 0, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; + } + Operator::I32Load16S { memarg } | Operator::I32Load16U { memarg } => { + let ty = self.check_memarg(memarg, 1, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; + } + Operator::I64Load8S { memarg } | Operator::I64Load8U { memarg } => { + let ty = self.check_memarg(memarg, 0, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I64)?; + } + Operator::I64Load16S { memarg } | Operator::I64Load16U { memarg } => { + let ty = self.check_memarg(memarg, 1, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I64)?; + } + Operator::I64Load32S { memarg } | Operator::I64Load32U { memarg } => { + let ty = self.check_memarg(memarg, 2, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I64)?; } Operator::I32Store { memarg } => { - self.check_memarg(memarg, 2, resources)?; - self.check_operands_2(Type::I32, Type::I32)?; - self.func_state.change_frame(2)?; + let ty = self.check_memarg(memarg, 2, resources)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; } Operator::I64Store { memarg } => { - self.check_memarg(memarg, 3, resources)?; - self.check_operands_2(Type::I32, Type::I64)?; - self.func_state.change_frame(2)?; + let ty = self.check_memarg(memarg, 3, resources)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(ty))?; } Operator::F32Store { memarg } => { self.check_non_deterministic_enabled()?; - self.check_memarg(memarg, 2, resources)?; - self.check_operands_2(Type::I32, Type::F32)?; - self.func_state.change_frame(2)?; + let ty = self.check_memarg(memarg, 2, resources)?; + self.pop_operand(Some(Type::F32))?; + self.pop_operand(Some(ty))?; } Operator::F64Store { memarg } => { self.check_non_deterministic_enabled()?; - self.check_memarg(memarg, 3, resources)?; - self.check_operands_2(Type::I32, Type::F64)?; - self.func_state.change_frame(2)?; + let ty = self.check_memarg(memarg, 3, resources)?; + self.pop_operand(Some(Type::F64))?; + self.pop_operand(Some(ty))?; } Operator::I32Store8 { memarg } => { - self.check_memarg(memarg, 0, resources)?; - self.check_operands_2(Type::I32, Type::I32)?; - self.func_state.change_frame(2)?; + let ty = self.check_memarg(memarg, 0, resources)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; } Operator::I32Store16 { memarg } => { - self.check_memarg(memarg, 1, resources)?; - self.check_operands_2(Type::I32, Type::I32)?; - self.func_state.change_frame(2)?; + let ty = self.check_memarg(memarg, 1, resources)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; } Operator::I64Store8 { memarg } => { - self.check_memarg(memarg, 0, resources)?; - self.check_operands_2(Type::I32, Type::I64)?; - self.func_state.change_frame(2)?; + let ty = self.check_memarg(memarg, 0, resources)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(ty))?; } Operator::I64Store16 { memarg } => { - self.check_memarg(memarg, 1, resources)?; - self.check_operands_2(Type::I32, Type::I64)?; - self.func_state.change_frame(2)?; + let ty = self.check_memarg(memarg, 1, resources)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(ty))?; } Operator::I64Store32 { memarg } => { - self.check_memarg(memarg, 2, resources)?; - self.check_operands_2(Type::I32, Type::I64)?; - self.func_state.change_frame(2)?; + let ty = self.check_memarg(memarg, 2, resources)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(ty))?; } - Operator::MemorySize { - reserved: memory_index, - } => { - self.check_memory_index(memory_index, resources)?; - self.func_state.change_frame_with_type(0, Type::I32)?; + Operator::MemorySize { mem, mem_byte } => { + if mem_byte != 0 && !self.features.multi_memory { + return Err(OperatorValidatorError::new("multi-memory not enabled")); + } + let index_ty = self.check_memory_index(mem, resources)?; + self.push_operand(index_ty)?; } - Operator::MemoryGrow { - reserved: memory_index, - } => { - self.check_memory_index(memory_index, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::I32)?; + Operator::MemoryGrow { mem, mem_byte } => { + if mem_byte != 0 && !self.features.multi_memory { + return Err(OperatorValidatorError::new("multi-memory not enabled")); + } + let index_ty = self.check_memory_index(mem, resources)?; + self.pop_operand(Some(index_ty))?; + self.push_operand(index_ty)?; } - Operator::I32Const { .. } => self.func_state.change_frame_with_type(0, Type::I32)?, - Operator::I64Const { .. } => self.func_state.change_frame_with_type(0, Type::I64)?, + Operator::I32Const { .. } => self.push_operand(Type::I32)?, + Operator::I64Const { .. } => self.push_operand(Type::I64)?, Operator::F32Const { .. } => { self.check_non_deterministic_enabled()?; - self.func_state.change_frame_with_type(0, Type::F32)?; + self.push_operand(Type::F32)?; } Operator::F64Const { .. } => { self.check_non_deterministic_enabled()?; - self.func_state.change_frame_with_type(0, Type::F64)?; + self.push_operand(Type::F64)?; } Operator::I32Eqz => { - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::I32)?; + self.pop_operand(Some(Type::I32))?; + self.push_operand(Type::I32)?; } Operator::I32Eq | Operator::I32Ne @@ -1166,12 +825,13 @@ impl OperatorValidator { | Operator::I32LeU | Operator::I32GeS | Operator::I32GeU => { - self.check_operands_2(Type::I32, Type::I32)?; - self.func_state.change_frame_with_type(2, Type::I32)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::I32))?; + self.push_operand(Type::I32)?; } Operator::I64Eqz => { - self.check_operands_1(Type::I64)?; - self.func_state.change_frame_with_type(1, Type::I32)?; + self.pop_operand(Some(Type::I64))?; + self.push_operand(Type::I32)?; } Operator::I64Eq | Operator::I64Ne @@ -1183,8 +843,9 @@ impl OperatorValidator { | Operator::I64LeU | Operator::I64GeS | Operator::I64GeU => { - self.check_operands_2(Type::I64, Type::I64)?; - self.func_state.change_frame_with_type(2, Type::I32)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(Type::I64))?; + self.push_operand(Type::I32)?; } Operator::F32Eq | Operator::F32Ne @@ -1193,8 +854,9 @@ impl OperatorValidator { | Operator::F32Le | Operator::F32Ge => { self.check_non_deterministic_enabled()?; - self.check_operands_2(Type::F32, Type::F32)?; - self.func_state.change_frame_with_type(2, Type::I32)?; + self.pop_operand(Some(Type::F32))?; + self.pop_operand(Some(Type::F32))?; + self.push_operand(Type::I32)?; } Operator::F64Eq | Operator::F64Ne @@ -1203,12 +865,13 @@ impl OperatorValidator { | Operator::F64Le | Operator::F64Ge => { self.check_non_deterministic_enabled()?; - self.check_operands_2(Type::F64, Type::F64)?; - self.func_state.change_frame_with_type(2, Type::I32)?; + self.pop_operand(Some(Type::F64))?; + self.pop_operand(Some(Type::F64))?; + self.push_operand(Type::I32)?; } Operator::I32Clz | Operator::I32Ctz | Operator::I32Popcnt => { - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::I32)?; + self.pop_operand(Some(Type::I32))?; + self.push_operand(Type::I32)?; } Operator::I32Add | Operator::I32Sub @@ -1225,12 +888,13 @@ impl OperatorValidator { | Operator::I32ShrU | Operator::I32Rotl | Operator::I32Rotr => { - self.check_operands_2(Type::I32, Type::I32)?; - self.func_state.change_frame_with_type(2, Type::I32)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::I32))?; + self.push_operand(Type::I32)?; } Operator::I64Clz | Operator::I64Ctz | Operator::I64Popcnt => { - self.check_operands_1(Type::I64)?; - self.func_state.change_frame_with_type(1, Type::I64)?; + self.pop_operand(Some(Type::I64))?; + self.push_operand(Type::I64)?; } Operator::I64Add | Operator::I64Sub @@ -1247,8 +911,9 @@ impl OperatorValidator { | Operator::I64ShrU | Operator::I64Rotl | Operator::I64Rotr => { - self.check_operands_2(Type::I64, Type::I64)?; - self.func_state.change_frame_with_type(2, Type::I64)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(Type::I64))?; + self.push_operand(Type::I64)?; } Operator::F32Abs | Operator::F32Neg @@ -1258,8 +923,8 @@ impl OperatorValidator { | Operator::F32Nearest | Operator::F32Sqrt => { self.check_non_deterministic_enabled()?; - self.check_operands_1(Type::F32)?; - self.func_state.change_frame_with_type(1, Type::F32)?; + self.pop_operand(Some(Type::F32))?; + self.push_operand(Type::F32)?; } Operator::F32Add | Operator::F32Sub @@ -1269,8 +934,9 @@ impl OperatorValidator { | Operator::F32Max | Operator::F32Copysign => { self.check_non_deterministic_enabled()?; - self.check_operands_2(Type::F32, Type::F32)?; - self.func_state.change_frame_with_type(2, Type::F32)?; + self.pop_operand(Some(Type::F32))?; + self.pop_operand(Some(Type::F32))?; + self.push_operand(Type::F32)?; } Operator::F64Abs | Operator::F64Neg @@ -1280,8 +946,8 @@ impl OperatorValidator { | Operator::F64Nearest | Operator::F64Sqrt => { self.check_non_deterministic_enabled()?; - self.check_operands_1(Type::F64)?; - self.func_state.change_frame_with_type(1, Type::F64)?; + self.pop_operand(Some(Type::F64))?; + self.push_operand(Type::F64)?; } Operator::F64Add | Operator::F64Sub @@ -1291,140 +957,141 @@ impl OperatorValidator { | Operator::F64Max | Operator::F64Copysign => { self.check_non_deterministic_enabled()?; - self.check_operands_2(Type::F64, Type::F64)?; - self.func_state.change_frame_with_type(2, Type::F64)?; + self.pop_operand(Some(Type::F64))?; + self.pop_operand(Some(Type::F64))?; + self.push_operand(Type::F64)?; } Operator::I32WrapI64 => { - self.check_operands_1(Type::I64)?; - self.func_state.change_frame_with_type(1, Type::I32)?; + self.pop_operand(Some(Type::I64))?; + self.push_operand(Type::I32)?; } Operator::I32TruncF32S | Operator::I32TruncF32U => { - self.check_operands_1(Type::F32)?; - self.func_state.change_frame_with_type(1, Type::I32)?; + self.pop_operand(Some(Type::F32))?; + self.push_operand(Type::I32)?; } Operator::I32TruncF64S | Operator::I32TruncF64U => { - self.check_operands_1(Type::F64)?; - self.func_state.change_frame_with_type(1, Type::I32)?; + self.pop_operand(Some(Type::F64))?; + self.push_operand(Type::I32)?; } Operator::I64ExtendI32S | Operator::I64ExtendI32U => { - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::I64)?; + self.pop_operand(Some(Type::I32))?; + self.push_operand(Type::I64)?; } Operator::I64TruncF32S | Operator::I64TruncF32U => { - self.check_operands_1(Type::F32)?; - self.func_state.change_frame_with_type(1, Type::I64)?; + self.pop_operand(Some(Type::F32))?; + self.push_operand(Type::I64)?; } Operator::I64TruncF64S | Operator::I64TruncF64U => { - self.check_operands_1(Type::F64)?; - self.func_state.change_frame_with_type(1, Type::I64)?; + self.pop_operand(Some(Type::F64))?; + self.push_operand(Type::I64)?; } Operator::F32ConvertI32S | Operator::F32ConvertI32U => { self.check_non_deterministic_enabled()?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::F32)?; + self.pop_operand(Some(Type::I32))?; + self.push_operand(Type::F32)?; } Operator::F32ConvertI64S | Operator::F32ConvertI64U => { self.check_non_deterministic_enabled()?; - self.check_operands_1(Type::I64)?; - self.func_state.change_frame_with_type(1, Type::F32)?; + self.pop_operand(Some(Type::I64))?; + self.push_operand(Type::F32)?; } Operator::F32DemoteF64 => { self.check_non_deterministic_enabled()?; - self.check_operands_1(Type::F64)?; - self.func_state.change_frame_with_type(1, Type::F32)?; + self.pop_operand(Some(Type::F64))?; + self.push_operand(Type::F32)?; } Operator::F64ConvertI32S | Operator::F64ConvertI32U => { self.check_non_deterministic_enabled()?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::F64)?; + self.pop_operand(Some(Type::I32))?; + self.push_operand(Type::F64)?; } Operator::F64ConvertI64S | Operator::F64ConvertI64U => { self.check_non_deterministic_enabled()?; - self.check_operands_1(Type::I64)?; - self.func_state.change_frame_with_type(1, Type::F64)?; + self.pop_operand(Some(Type::I64))?; + self.push_operand(Type::F64)?; } Operator::F64PromoteF32 => { self.check_non_deterministic_enabled()?; - self.check_operands_1(Type::F32)?; - self.func_state.change_frame_with_type(1, Type::F64)?; + self.pop_operand(Some(Type::F32))?; + self.push_operand(Type::F64)?; } Operator::I32ReinterpretF32 => { - self.check_operands_1(Type::F32)?; - self.func_state.change_frame_with_type(1, Type::I32)?; + self.pop_operand(Some(Type::F32))?; + self.push_operand(Type::I32)?; } Operator::I64ReinterpretF64 => { - self.check_operands_1(Type::F64)?; - self.func_state.change_frame_with_type(1, Type::I64)?; + self.pop_operand(Some(Type::F64))?; + self.push_operand(Type::I64)?; } Operator::F32ReinterpretI32 => { self.check_non_deterministic_enabled()?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::F32)?; + self.pop_operand(Some(Type::I32))?; + self.push_operand(Type::F32)?; } Operator::F64ReinterpretI64 => { self.check_non_deterministic_enabled()?; - self.check_operands_1(Type::I64)?; - self.func_state.change_frame_with_type(1, Type::F64)?; + self.pop_operand(Some(Type::I64))?; + self.push_operand(Type::F64)?; } Operator::I32TruncSatF32S | Operator::I32TruncSatF32U => { - self.check_operands_1(Type::F32)?; - self.func_state.change_frame_with_type(1, Type::I32)?; + self.pop_operand(Some(Type::F32))?; + self.push_operand(Type::I32)?; } Operator::I32TruncSatF64S | Operator::I32TruncSatF64U => { - self.check_operands_1(Type::F64)?; - self.func_state.change_frame_with_type(1, Type::I32)?; + self.pop_operand(Some(Type::F64))?; + self.push_operand(Type::I32)?; } Operator::I64TruncSatF32S | Operator::I64TruncSatF32U => { - self.check_operands_1(Type::F32)?; - self.func_state.change_frame_with_type(1, Type::I64)?; + self.pop_operand(Some(Type::F32))?; + self.push_operand(Type::I64)?; } Operator::I64TruncSatF64S | Operator::I64TruncSatF64U => { - self.check_operands_1(Type::F64)?; - self.func_state.change_frame_with_type(1, Type::I64)?; + self.pop_operand(Some(Type::F64))?; + self.push_operand(Type::I64)?; } Operator::I32Extend16S | Operator::I32Extend8S => { - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::I32)?; + self.pop_operand(Some(Type::I32))?; + self.push_operand(Type::I32)?; } Operator::I64Extend32S | Operator::I64Extend16S | Operator::I64Extend8S => { - self.check_operands_1(Type::I64)?; - self.func_state.change_frame_with_type(1, Type::I64)?; + self.pop_operand(Some(Type::I64))?; + self.push_operand(Type::I64)?; } Operator::I32AtomicLoad { memarg } | Operator::I32AtomicLoad16U { memarg } | Operator::I32AtomicLoad8U { memarg } => { self.check_threads_enabled()?; - self.check_shared_memarg_wo_align(memarg, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::I32)?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; } Operator::I64AtomicLoad { memarg } | Operator::I64AtomicLoad32U { memarg } | Operator::I64AtomicLoad16U { memarg } | Operator::I64AtomicLoad8U { memarg } => { self.check_threads_enabled()?; - self.check_shared_memarg_wo_align(memarg, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::I64)?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I64)?; } Operator::I32AtomicStore { memarg } | Operator::I32AtomicStore16 { memarg } | Operator::I32AtomicStore8 { memarg } => { self.check_threads_enabled()?; - self.check_shared_memarg_wo_align(memarg, resources)?; - self.check_operands_2(Type::I32, Type::I32)?; - self.func_state.change_frame(2)?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; } Operator::I64AtomicStore { memarg } | Operator::I64AtomicStore32 { memarg } | Operator::I64AtomicStore16 { memarg } | Operator::I64AtomicStore8 { memarg } => { self.check_threads_enabled()?; - self.check_shared_memarg_wo_align(memarg, resources)?; - self.check_operands_2(Type::I32, Type::I64)?; - self.func_state.change_frame(2)?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(ty))?; } Operator::I32AtomicRmwAdd { memarg } | Operator::I32AtomicRmwSub { memarg } @@ -1442,9 +1109,10 @@ impl OperatorValidator { | Operator::I32AtomicRmw8OrU { memarg } | Operator::I32AtomicRmw8XorU { memarg } => { self.check_threads_enabled()?; - self.check_shared_memarg_wo_align(memarg, resources)?; - self.check_operands_2(Type::I32, Type::I32)?; - self.func_state.change_frame_with_type(2, Type::I32)?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; } Operator::I64AtomicRmwAdd { memarg } | Operator::I64AtomicRmwSub { memarg } @@ -1467,61 +1135,73 @@ impl OperatorValidator { | Operator::I64AtomicRmw8OrU { memarg } | Operator::I64AtomicRmw8XorU { memarg } => { self.check_threads_enabled()?; - self.check_shared_memarg_wo_align(memarg, resources)?; - self.check_operands_2(Type::I32, Type::I64)?; - self.func_state.change_frame_with_type(2, Type::I64)?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I64)?; } Operator::I32AtomicRmwXchg { memarg } | Operator::I32AtomicRmw16XchgU { memarg } | Operator::I32AtomicRmw8XchgU { memarg } => { self.check_threads_enabled()?; - self.check_shared_memarg_wo_align(memarg, resources)?; - self.check_operands_2(Type::I32, Type::I32)?; - self.func_state.change_frame_with_type(2, Type::I32)?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; } Operator::I32AtomicRmwCmpxchg { memarg } | Operator::I32AtomicRmw16CmpxchgU { memarg } | Operator::I32AtomicRmw8CmpxchgU { memarg } => { self.check_threads_enabled()?; - self.check_shared_memarg_wo_align(memarg, resources)?; - self.check_operands_3(Type::I32, Type::I32, Type::I32)?; - self.func_state.change_frame_with_type(3, Type::I32)?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; } Operator::I64AtomicRmwXchg { memarg } | Operator::I64AtomicRmw32XchgU { memarg } | Operator::I64AtomicRmw16XchgU { memarg } | Operator::I64AtomicRmw8XchgU { memarg } => { self.check_threads_enabled()?; - self.check_shared_memarg_wo_align(memarg, resources)?; - self.check_operands_2(Type::I32, Type::I64)?; - self.func_state.change_frame_with_type(2, Type::I64)?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I64)?; } Operator::I64AtomicRmwCmpxchg { memarg } | Operator::I64AtomicRmw32CmpxchgU { memarg } | Operator::I64AtomicRmw16CmpxchgU { memarg } | Operator::I64AtomicRmw8CmpxchgU { memarg } => { self.check_threads_enabled()?; - self.check_shared_memarg_wo_align(memarg, resources)?; - self.check_operands_3(Type::I32, Type::I64, Type::I64)?; - self.func_state.change_frame_with_type(3, Type::I64)?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I64)?; } - Operator::AtomicNotify { memarg } => { + Operator::MemoryAtomicNotify { memarg } => { self.check_threads_enabled()?; - self.check_shared_memarg_wo_align(memarg, resources)?; - self.check_operands_2(Type::I32, Type::I32)?; - self.func_state.change_frame_with_type(2, Type::I32)?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; } - Operator::I32AtomicWait { memarg } => { + Operator::MemoryAtomicWait32 { memarg } => { self.check_threads_enabled()?; - self.check_shared_memarg_wo_align(memarg, resources)?; - self.check_operands_3(Type::I32, Type::I32, Type::I64)?; - self.func_state.change_frame_with_type(3, Type::I32)?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; } - Operator::I64AtomicWait { memarg } => { + Operator::MemoryAtomicWait64 { memarg } => { self.check_threads_enabled()?; - self.check_shared_memarg_wo_align(memarg, resources)?; - self.check_operands_3(Type::I32, Type::I64, Type::I64)?; - self.func_state.change_frame_with_type(3, Type::I32)?; + let ty = self.check_shared_memarg_wo_align(memarg, resources)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; } Operator::AtomicFence { ref flags } => { self.check_threads_enabled()?; @@ -1541,12 +1221,11 @@ impl OperatorValidator { )) } } - self.func_state.change_frame_with_type(0, ty)?; + self.push_operand(ty)?; } Operator::RefIsNull => { self.check_reference_types_enabled()?; - self.check_frame_size(1)?; - match self.func_state.stack_type_at(0) { + match self.pop_operand(None)? { None | Some(Type::FuncRef) | Some(Type::ExternRef) => {} _ => { return Err(OperatorValidatorError::new( @@ -1554,11 +1233,11 @@ impl OperatorValidator { )) } } - self.func_state.change_frame_with_type(1, Type::I32)?; + self.push_operand(Type::I32)?; } Operator::RefFunc { function_index } => { self.check_reference_types_enabled()?; - if resources.func_type_at(function_index).is_none() { + if resources.type_of_function(function_index).is_none() { return Err(OperatorValidatorError::new( "unknown function: function index out of bounds", )); @@ -1566,121 +1245,127 @@ impl OperatorValidator { if !resources.is_function_referenced(function_index) { return Err(OperatorValidatorError::new("undeclared function reference")); } - self.func_state.change_frame_with_type(0, Type::FuncRef)?; + self.push_operand(Type::FuncRef)?; } Operator::V128Load { memarg } => { self.check_simd_enabled()?; - self.check_memarg(memarg, 4, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::V128)?; + let ty = self.check_memarg(memarg, 4, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::V128)?; } Operator::V128Store { memarg } => { self.check_simd_enabled()?; - self.check_memarg(memarg, 4, resources)?; - self.check_operands_2(Type::I32, Type::V128)?; - self.func_state.change_frame(2)?; + let ty = self.check_memarg(memarg, 4, resources)?; + self.pop_operand(Some(Type::V128))?; + self.pop_operand(Some(ty))?; } Operator::V128Const { .. } => { self.check_simd_enabled()?; - self.func_state.change_frame_with_type(0, Type::V128)?; + self.push_operand(Type::V128)?; } Operator::I8x16Splat | Operator::I16x8Splat | Operator::I32x4Splat => { self.check_simd_enabled()?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::V128)?; + self.pop_operand(Some(Type::I32))?; + self.push_operand(Type::V128)?; } Operator::I64x2Splat => { self.check_simd_enabled()?; - self.check_operands_1(Type::I64)?; - self.func_state.change_frame_with_type(1, Type::V128)?; + self.pop_operand(Some(Type::I64))?; + self.push_operand(Type::V128)?; } Operator::F32x4Splat => { self.check_non_deterministic_enabled()?; self.check_simd_enabled()?; - self.check_operands_1(Type::F32)?; - self.func_state.change_frame_with_type(1, Type::V128)?; + self.pop_operand(Some(Type::F32))?; + self.push_operand(Type::V128)?; } Operator::F64x2Splat => { self.check_non_deterministic_enabled()?; self.check_simd_enabled()?; - self.check_operands_1(Type::F64)?; - self.func_state.change_frame_with_type(1, Type::V128)?; + self.pop_operand(Some(Type::F64))?; + self.push_operand(Type::V128)?; } Operator::I8x16ExtractLaneS { lane } | Operator::I8x16ExtractLaneU { lane } => { self.check_simd_enabled()?; self.check_simd_lane_index(lane, 16)?; - self.check_operands_1(Type::V128)?; - self.func_state.change_frame_with_type(1, Type::I32)?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::I32)?; } Operator::I16x8ExtractLaneS { lane } | Operator::I16x8ExtractLaneU { lane } => { self.check_simd_enabled()?; self.check_simd_lane_index(lane, 8)?; - self.check_operands_1(Type::V128)?; - self.func_state.change_frame_with_type(1, Type::I32)?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::I32)?; } Operator::I32x4ExtractLane { lane } => { self.check_simd_enabled()?; self.check_simd_lane_index(lane, 4)?; - self.check_operands_1(Type::V128)?; - self.func_state.change_frame_with_type(1, Type::I32)?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::I32)?; } Operator::I8x16ReplaceLane { lane } => { self.check_simd_enabled()?; self.check_simd_lane_index(lane, 16)?; - self.check_operands_2(Type::V128, Type::I32)?; - self.func_state.change_frame_with_type(2, Type::V128)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; } Operator::I16x8ReplaceLane { lane } => { self.check_simd_enabled()?; self.check_simd_lane_index(lane, 8)?; - self.check_operands_2(Type::V128, Type::I32)?; - self.func_state.change_frame_with_type(2, Type::V128)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; } Operator::I32x4ReplaceLane { lane } => { self.check_simd_enabled()?; self.check_simd_lane_index(lane, 4)?; - self.check_operands_2(Type::V128, Type::I32)?; - self.func_state.change_frame_with_type(2, Type::V128)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; } Operator::I64x2ExtractLane { lane } => { self.check_simd_enabled()?; self.check_simd_lane_index(lane, 2)?; - self.check_operands_1(Type::V128)?; - self.func_state.change_frame_with_type(1, Type::I64)?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::I64)?; } Operator::I64x2ReplaceLane { lane } => { self.check_simd_enabled()?; self.check_simd_lane_index(lane, 2)?; - self.check_operands_2(Type::V128, Type::I64)?; - self.func_state.change_frame_with_type(2, Type::V128)?; + self.pop_operand(Some(Type::I64))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; } Operator::F32x4ExtractLane { lane } => { self.check_non_deterministic_enabled()?; self.check_simd_enabled()?; self.check_simd_lane_index(lane, 4)?; - self.check_operands_1(Type::V128)?; - self.func_state.change_frame_with_type(1, Type::F32)?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::F32)?; } Operator::F32x4ReplaceLane { lane } => { self.check_non_deterministic_enabled()?; self.check_simd_enabled()?; self.check_simd_lane_index(lane, 4)?; - self.check_operands_2(Type::V128, Type::F32)?; - self.func_state.change_frame_with_type(2, Type::V128)?; + self.pop_operand(Some(Type::F32))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; } Operator::F64x2ExtractLane { lane } => { self.check_non_deterministic_enabled()?; self.check_simd_enabled()?; self.check_simd_lane_index(lane, 2)?; - self.check_operands_1(Type::V128)?; - self.func_state.change_frame_with_type(1, Type::F64)?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::F64)?; } Operator::F64x2ReplaceLane { lane } => { self.check_non_deterministic_enabled()?; self.check_simd_enabled()?; self.check_simd_lane_index(lane, 2)?; - self.check_operands_2(Type::V128, Type::F64)?; - self.func_state.change_frame_with_type(2, Type::V128)?; + self.pop_operand(Some(Type::F64))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; } Operator::F32x4Eq | Operator::F32x4Ne @@ -1700,16 +1385,21 @@ impl OperatorValidator { | Operator::F32x4Div | Operator::F32x4Min | Operator::F32x4Max + | Operator::F32x4PMin + | Operator::F32x4PMax | Operator::F64x2Add | Operator::F64x2Sub | Operator::F64x2Mul | Operator::F64x2Div | Operator::F64x2Min - | Operator::F64x2Max => { + | Operator::F64x2Max + | Operator::F64x2PMin + | Operator::F64x2PMax => { self.check_non_deterministic_enabled()?; self.check_simd_enabled()?; - self.check_operands_2(Type::V128, Type::V128)?; - self.func_state.change_frame_with_type(2, Type::V128)?; + self.pop_operand(Some(Type::V128))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; } Operator::I8x16Eq | Operator::I8x16Ne @@ -1746,21 +1436,21 @@ impl OperatorValidator { | Operator::V128Or | Operator::V128Xor | Operator::I8x16Add - | Operator::I8x16AddSaturateS - | Operator::I8x16AddSaturateU + | Operator::I8x16AddSatS + | Operator::I8x16AddSatU | Operator::I8x16Sub - | Operator::I8x16SubSaturateS - | Operator::I8x16SubSaturateU + | Operator::I8x16SubSatS + | Operator::I8x16SubSatU | Operator::I8x16MinS | Operator::I8x16MinU | Operator::I8x16MaxS | Operator::I8x16MaxU | Operator::I16x8Add - | Operator::I16x8AddSaturateS - | Operator::I16x8AddSaturateU + | Operator::I16x8AddSatS + | Operator::I16x8AddSatU | Operator::I16x8Sub - | Operator::I16x8SubSaturateS - | Operator::I16x8SubSaturateU + | Operator::I16x8SubSatS + | Operator::I16x8SubSatU | Operator::I16x8Mul | Operator::I16x8MinS | Operator::I16x8MinU @@ -1783,10 +1473,19 @@ impl OperatorValidator { | Operator::I16x8NarrowI32x4S | Operator::I16x8NarrowI32x4U => { self.check_simd_enabled()?; - self.check_operands_2(Type::V128, Type::V128)?; - self.func_state.change_frame_with_type(2, Type::V128)?; - } - Operator::F32x4Abs + self.pop_operand(Some(Type::V128))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; + } + Operator::F32x4Ceil + | Operator::F32x4Floor + | Operator::F32x4Trunc + | Operator::F32x4Nearest + | Operator::F64x2Ceil + | Operator::F64x2Floor + | Operator::F64x2Trunc + | Operator::F64x2Nearest + | Operator::F32x4Abs | Operator::F32x4Neg | Operator::F32x4Sqrt | Operator::F64x2Abs @@ -1796,8 +1495,8 @@ impl OperatorValidator { | Operator::F32x4ConvertI32x4U => { self.check_non_deterministic_enabled()?; self.check_simd_enabled()?; - self.check_operands_1(Type::V128)?; - self.func_state.change_frame_with_type(1, Type::V128)?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; } Operator::V128Not | Operator::I8x16Abs @@ -1818,13 +1517,15 @@ impl OperatorValidator { | Operator::I32x4WidenLowI16x8U | Operator::I32x4WidenHighI16x8U => { self.check_simd_enabled()?; - self.check_operands_1(Type::V128)?; - self.func_state.change_frame_with_type(1, Type::V128)?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; } Operator::V128Bitselect => { self.check_simd_enabled()?; - self.check_operands_3(Type::V128, Type::V128, Type::V128)?; - self.func_state.change_frame_with_type(3, Type::V128)?; + self.pop_operand(Some(Type::V128))?; + self.pop_operand(Some(Type::V128))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; } Operator::I8x16AnyTrue | Operator::I8x16AllTrue @@ -1836,8 +1537,8 @@ impl OperatorValidator { | Operator::I32x4AllTrue | Operator::I32x4Bitmask => { self.check_simd_enabled()?; - self.check_operands_1(Type::V128)?; - self.func_state.change_frame_with_type(1, Type::I32)?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::I32)?; } Operator::I8x16Shl | Operator::I8x16ShrS @@ -1852,61 +1553,65 @@ impl OperatorValidator { | Operator::I64x2ShrS | Operator::I64x2ShrU => { self.check_simd_enabled()?; - self.check_operands_2(Type::V128, Type::I32)?; - self.func_state.change_frame_with_type(2, Type::V128)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; } - Operator::V8x16Swizzle => { + Operator::I8x16Swizzle => { self.check_simd_enabled()?; - self.check_operands_2(Type::V128, Type::V128)?; - self.func_state.change_frame_with_type(2, Type::V128)?; + self.pop_operand(Some(Type::V128))?; + self.pop_operand(Some(Type::V128))?; + self.push_operand(Type::V128)?; } - Operator::V8x16Shuffle { ref lanes } => { + Operator::I8x16Shuffle { ref lanes } => { self.check_simd_enabled()?; - self.check_operands_2(Type::V128, Type::V128)?; + self.pop_operand(Some(Type::V128))?; + self.pop_operand(Some(Type::V128))?; for i in lanes { self.check_simd_lane_index(*i, 32)?; } - self.func_state.change_frame_with_type(2, Type::V128)?; + self.push_operand(Type::V128)?; } - Operator::V8x16LoadSplat { memarg } => { + Operator::V128Load8Splat { memarg } => { self.check_simd_enabled()?; - self.check_memarg(memarg, 0, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::V128)?; + let ty = self.check_memarg(memarg, 0, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::V128)?; } - Operator::V16x8LoadSplat { memarg } => { + Operator::V128Load16Splat { memarg } => { self.check_simd_enabled()?; - self.check_memarg(memarg, 1, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::V128)?; + let ty = self.check_memarg(memarg, 1, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::V128)?; } - Operator::V32x4LoadSplat { memarg } => { + Operator::V128Load32Splat { memarg } => { self.check_simd_enabled()?; - self.check_memarg(memarg, 2, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::V128)?; - } - Operator::V64x2LoadSplat { memarg } - | Operator::I16x8Load8x8S { memarg } - | Operator::I16x8Load8x8U { memarg } - | Operator::I32x4Load16x4S { memarg } - | Operator::I32x4Load16x4U { memarg } - | Operator::I64x2Load32x2S { memarg } - | Operator::I64x2Load32x2U { memarg } => { + let ty = self.check_memarg(memarg, 2, resources)?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::V128)?; + } + Operator::V128Load64Splat { memarg } + | Operator::V128Load8x8S { memarg } + | Operator::V128Load8x8U { memarg } + | Operator::V128Load16x4S { memarg } + | Operator::V128Load16x4U { memarg } + | Operator::V128Load32x2S { memarg } + | Operator::V128Load32x2U { memarg } => { self.check_simd_enabled()?; - self.check_memarg(memarg, 3, resources)?; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, Type::V128)?; + let idx = self.check_memarg(memarg, 3, resources)?; + self.pop_operand(Some(idx))?; + self.push_operand(Type::V128)?; } - Operator::MemoryInit { segment } => { + Operator::MemoryInit { mem, segment } => { self.check_bulk_memory_enabled()?; - self.check_memory_index(0, resources)?; + let ty = self.check_memory_index(mem, resources)?; if segment >= resources.data_count() { bail_op_err!("unknown data segment {}", segment); } - self.check_operands_3(Type::I32, Type::I32, Type::I32)?; - self.func_state.change_frame(3)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; } Operator::DataDrop { segment } => { self.check_bulk_memory_enabled()?; @@ -1914,11 +1619,23 @@ impl OperatorValidator { bail_op_err!("unknown data segment {}", segment); } } - Operator::MemoryCopy | Operator::MemoryFill => { + Operator::MemoryCopy { src, dst } => { self.check_bulk_memory_enabled()?; - self.check_memory_index(0, resources)?; - self.check_operands_3(Type::I32, Type::I32, Type::I32)?; - self.func_state.change_frame(3)?; + let dst_ty = self.check_memory_index(dst, resources)?; + let src_ty = self.check_memory_index(src, resources)?; + self.pop_operand(Some(match src_ty { + Type::I32 => Type::I32, + _ => dst_ty, + }))?; + self.pop_operand(Some(src_ty))?; + self.pop_operand(Some(dst_ty))?; + } + Operator::MemoryFill { mem } => { + self.check_bulk_memory_enabled()?; + let ty = self.check_memory_index(mem, resources)?; + self.pop_operand(Some(ty))?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; } Operator::TableInit { segment, table } => { self.check_bulk_memory_enabled()?; @@ -1936,11 +1653,12 @@ impl OperatorValidator { segment ), }; - if segment_ty != table.element_type().to_parser_type() { + if segment_ty != table.element_type { return Err(OperatorValidatorError::new("type mismatch")); } - self.check_operands_3(Type::I32, Type::I32, Type::I32)?; - self.func_state.change_frame(3)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::I32))?; } Operator::ElemDrop { segment } => { self.check_bulk_memory_enabled()?; @@ -1964,72 +1682,138 @@ impl OperatorValidator { (Some(a), Some(b)) => (a, b), _ => return Err(OperatorValidatorError::new("table index out of bounds")), }; - if src.element_type().to_parser_type() != dst.element_type().to_parser_type() { + if src.element_type != dst.element_type { return Err(OperatorValidatorError::new("type mismatch")); } - self.check_operands_3(Type::I32, Type::I32, Type::I32)?; - self.func_state.change_frame(3)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(Type::I32))?; } Operator::TableGet { table } => { self.check_reference_types_enabled()?; let ty = match resources.table_at(table) { - Some(ty) => ty.element_type().to_parser_type(), + Some(ty) => ty.element_type, None => return Err(OperatorValidatorError::new("table index out of bounds")), }; - self.check_operands_1(Type::I32)?; - self.func_state.change_frame_with_type(1, ty)?; + self.pop_operand(Some(Type::I32))?; + self.push_operand(ty)?; } Operator::TableSet { table } => { self.check_reference_types_enabled()?; let ty = match resources.table_at(table) { - Some(ty) => ty.element_type().to_parser_type(), + Some(ty) => ty.element_type, None => return Err(OperatorValidatorError::new("table index out of bounds")), }; - self.check_operands_2(Type::I32, ty)?; - self.func_state.change_frame(2)?; + self.pop_operand(Some(ty))?; + self.pop_operand(Some(Type::I32))?; } Operator::TableGrow { table } => { self.check_reference_types_enabled()?; let ty = match resources.table_at(table) { - Some(ty) => ty.element_type().to_parser_type(), + Some(ty) => ty.element_type, None => return Err(OperatorValidatorError::new("table index out of bounds")), }; - self.check_operands_2(ty, Type::I32)?; - self.func_state.change_frame_with_type(2, Type::I32)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + self.push_operand(Type::I32)?; } Operator::TableSize { table } => { self.check_reference_types_enabled()?; if resources.table_at(table).is_none() { return Err(OperatorValidatorError::new("table index out of bounds")); } - self.func_state.change_frame_with_type(0, Type::I32)?; + self.push_operand(Type::I32)?; } Operator::TableFill { table } => { self.check_bulk_memory_enabled()?; let ty = match resources.table_at(table) { - Some(ty) => ty.element_type().to_parser_type(), + Some(ty) => ty.element_type, None => return Err(OperatorValidatorError::new("table index out of bounds")), }; - self.check_operands_3(Type::I32, ty, Type::I32)?; - self.func_state.change_frame(3)?; + self.pop_operand(Some(Type::I32))?; + self.pop_operand(Some(ty))?; + self.pop_operand(Some(Type::I32))?; } } - Ok(FunctionEnd::No) + Ok(()) + } + + pub fn finish(&mut self) -> OperatorValidatorResult<()> { + if self.control.len() != 0 { + bail_op_err!("control frames remain at end of function"); + } + Ok(()) } } fn func_type_at( resources: &T, at: u32, -) -> OperatorValidatorResult<&::FuncType> { - let ty = match resources.type_at(at) { - Some(ty) => ty, - None => { - return Err(OperatorValidatorError::new( - "unknown type: type index out of bounds", - )) +) -> OperatorValidatorResult<&T::FuncType> { + resources + .func_type_at(at) + .ok_or_else(|| OperatorValidatorError::new("unknown type: type index out of bounds")) +} + +enum Either { + A(A), + B(B), +} + +impl Iterator for Either +where + A: Iterator, + B: Iterator, +{ + type Item = A::Item; + fn next(&mut self) -> Option { + match self { + Either::A(a) => a.next(), + Either::B(b) => b.next(), } - }; - ty.as_func() - .ok_or_else(|| OperatorValidatorError::new("type index not a function type")) + } +} +impl DoubleEndedIterator for Either +where + A: DoubleEndedIterator, + B: DoubleEndedIterator, +{ + fn next_back(&mut self) -> Option { + match self { + Either::A(a) => a.next_back(), + Either::B(b) => b.next_back(), + } + } +} + +fn params<'a>( + ty: TypeOrFuncType, + resources: &'a impl WasmModuleResources, +) -> OperatorValidatorResult + 'a> { + Ok(match ty { + TypeOrFuncType::FuncType(t) => Either::A(func_type_at(resources, t)?.inputs()), + TypeOrFuncType::Type(_) => Either::B(None.into_iter()), + }) +} + +fn results<'a>( + ty: TypeOrFuncType, + resources: &'a impl WasmModuleResources, +) -> OperatorValidatorResult + 'a> { + Ok(match ty { + TypeOrFuncType::FuncType(t) => Either::A(func_type_at(resources, t)?.outputs()), + TypeOrFuncType::Type(Type::EmptyBlockType) => Either::B(None.into_iter()), + TypeOrFuncType::Type(t) => Either::B(Some(t).into_iter()), + }) +} + +fn label_types<'a>( + ty: TypeOrFuncType, + resources: &'a impl WasmModuleResources, + kind: FrameKind, +) -> OperatorValidatorResult + 'a> { + Ok(match kind { + FrameKind::Loop => Either::A(params(ty, resources)?), + _ => Either::B(results(ty, resources)?), + }) } diff --git a/third_party/rust/wasmparser/src/primitives.rs b/third_party/rust/wasmparser/src/primitives.rs index 416d8a92b5e62..1eaee9a1f97a3 100644 --- a/third_party/rust/wasmparser/src/primitives.rs +++ b/third_party/rust/wasmparser/src/primitives.rs @@ -32,7 +32,7 @@ pub(crate) struct BinaryReaderErrorInner { pub(crate) needed_hint: Option, } -pub type Result = result::Result; +pub type Result = result::Result; impl Error for BinaryReaderError {} @@ -195,6 +195,12 @@ pub struct ResizableLimits { pub maximum: Option, } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct ResizableLimits64 { + pub initial: u64, + pub maximum: Option, +} + #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct TableType { pub element_type: Type, @@ -202,9 +208,23 @@ pub struct TableType { } #[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct MemoryType { - pub limits: ResizableLimits, - pub shared: bool, +pub enum MemoryType { + M32 { + limits: ResizableLimits, + shared: bool, + }, + M64 { + limits: ResizableLimits64, + }, +} + +impl MemoryType { + pub fn index_type(&self) -> Type { + match self { + MemoryType::M32 { .. } => Type::I32, + MemoryType::M64 { .. } => Type::I64, + } + } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -225,8 +245,10 @@ pub enum ImportSectionEntryType { #[derive(Debug, Copy, Clone)] pub struct MemoryImmediate { - pub flags: u32, + /// Alignment, stored as `n` where the actual alignment is `2^n` + pub align: u8, pub offset: u32, + pub memory: u32, } #[derive(Debug, Copy, Clone)] @@ -260,9 +282,9 @@ pub enum RelocType { } /// A br_table entries representation. -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct BrTable<'a> { - pub(crate) buffer: &'a [u8], + pub(crate) reader: crate::BinaryReader<'a>, pub(crate) cnt: usize, } @@ -354,8 +376,8 @@ pub enum Operator<'a> { I64Store8 { memarg: MemoryImmediate }, I64Store16 { memarg: MemoryImmediate }, I64Store32 { memarg: MemoryImmediate }, - MemorySize { reserved: u32 }, - MemoryGrow { reserved: u32 }, + MemorySize { mem: u32, mem_byte: u8 }, + MemoryGrow { mem: u32, mem_byte: u8 }, I32Const { value: i32 }, I64Const { value: i64 }, F32Const { value: Ieee32 }, @@ -505,10 +527,10 @@ pub enum Operator<'a> { // 0xFC operators // bulk memory https://github.com/WebAssembly/bulk-memory-operations/blob/master/proposals/bulk-memory-operations/Overview.md - MemoryInit { segment: u32 }, + MemoryInit { segment: u32, mem: u32 }, DataDrop { segment: u32 }, - MemoryCopy, - MemoryFill, + MemoryCopy { src: u32, dst: u32 }, + MemoryFill { mem: u32 }, TableInit { segment: u32, table: u32 }, ElemDrop { segment: u32 }, TableCopy { dst_table: u32, src_table: u32 }, @@ -520,9 +542,9 @@ pub enum Operator<'a> { // 0xFE operators // https://github.com/WebAssembly/threads/blob/master/proposals/threads/Overview.md - AtomicNotify { memarg: MemoryImmediate }, - I32AtomicWait { memarg: MemoryImmediate }, - I64AtomicWait { memarg: MemoryImmediate }, + MemoryAtomicNotify { memarg: MemoryImmediate }, + MemoryAtomicWait32 { memarg: MemoryImmediate }, + MemoryAtomicWait64 { memarg: MemoryImmediate }, AtomicFence { flags: u8 }, I32AtomicLoad { memarg: MemoryImmediate }, I64AtomicLoad { memarg: MemoryImmediate }, @@ -670,11 +692,11 @@ pub enum Operator<'a> { I8x16ShrS, I8x16ShrU, I8x16Add, - I8x16AddSaturateS, - I8x16AddSaturateU, + I8x16AddSatS, + I8x16AddSatU, I8x16Sub, - I8x16SubSaturateS, - I8x16SubSaturateU, + I8x16SubSatS, + I8x16SubSatU, I8x16MinS, I8x16MinU, I8x16MaxS, @@ -688,11 +710,11 @@ pub enum Operator<'a> { I16x8ShrS, I16x8ShrU, I16x8Add, - I16x8AddSaturateS, - I16x8AddSaturateU, + I16x8AddSatS, + I16x8AddSatU, I16x8Sub, - I16x8SubSaturateS, - I16x8SubSaturateU, + I16x8SubSatS, + I16x8SubSatU, I16x8Mul, I16x8MinS, I16x8MinU, @@ -720,6 +742,14 @@ pub enum Operator<'a> { I64x2Add, I64x2Sub, I64x2Mul, + F32x4Ceil, + F32x4Floor, + F32x4Trunc, + F32x4Nearest, + F64x2Ceil, + F64x2Floor, + F64x2Trunc, + F64x2Nearest, F32x4Abs, F32x4Neg, F32x4Sqrt, @@ -729,6 +759,8 @@ pub enum Operator<'a> { F32x4Div, F32x4Min, F32x4Max, + F32x4PMin, + F32x4PMax, F64x2Abs, F64x2Neg, F64x2Sqrt, @@ -738,16 +770,18 @@ pub enum Operator<'a> { F64x2Div, F64x2Min, F64x2Max, + F64x2PMin, + F64x2PMax, I32x4TruncSatF32x4S, I32x4TruncSatF32x4U, F32x4ConvertI32x4S, F32x4ConvertI32x4U, - V8x16Swizzle, - V8x16Shuffle { lanes: [SIMDLaneIndex; 16] }, - V8x16LoadSplat { memarg: MemoryImmediate }, - V16x8LoadSplat { memarg: MemoryImmediate }, - V32x4LoadSplat { memarg: MemoryImmediate }, - V64x2LoadSplat { memarg: MemoryImmediate }, + I8x16Swizzle, + I8x16Shuffle { lanes: [SIMDLaneIndex; 16] }, + V128Load8Splat { memarg: MemoryImmediate }, + V128Load16Splat { memarg: MemoryImmediate }, + V128Load32Splat { memarg: MemoryImmediate }, + V128Load64Splat { memarg: MemoryImmediate }, I8x16NarrowI16x8S, I8x16NarrowI16x8U, I16x8NarrowI32x4S, @@ -760,12 +794,12 @@ pub enum Operator<'a> { I32x4WidenHighI16x8S, I32x4WidenLowI16x8U, I32x4WidenHighI16x8U, - I16x8Load8x8S { memarg: MemoryImmediate }, - I16x8Load8x8U { memarg: MemoryImmediate }, - I32x4Load16x4S { memarg: MemoryImmediate }, - I32x4Load16x4U { memarg: MemoryImmediate }, - I64x2Load32x2S { memarg: MemoryImmediate }, - I64x2Load32x2U { memarg: MemoryImmediate }, + V128Load8x8S { memarg: MemoryImmediate }, + V128Load8x8U { memarg: MemoryImmediate }, + V128Load16x4S { memarg: MemoryImmediate }, + V128Load16x4U { memarg: MemoryImmediate }, + V128Load32x2S { memarg: MemoryImmediate }, + V128Load32x2U { memarg: MemoryImmediate }, I8x16RoundingAverageU, I16x8RoundingAverageU, } diff --git a/third_party/rust/wasmparser/src/readers/code_section.rs b/third_party/rust/wasmparser/src/readers/code_section.rs index 5b0e63757861b..f17f66ac57759 100644 --- a/third_party/rust/wasmparser/src/readers/code_section.rs +++ b/third_party/rust/wasmparser/src/readers/code_section.rs @@ -18,7 +18,7 @@ use super::{ SectionReader, SectionWithLimitedItems, Type, }; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct FunctionBody<'a> { offset: usize, data: &'a [u8], @@ -170,7 +170,7 @@ impl<'a> CodeSectionReader<'a> { /// for _ in 0..code_reader.get_count() { /// let body = code_reader.read().expect("function body"); /// let mut binary_reader = body.get_binary_reader(); - /// assert!(binary_reader.read_local_count().expect("local count") == 0); + /// assert!(binary_reader.read_var_u32().expect("local count") == 0); /// let op = binary_reader.read_operator().expect("first operator"); /// println!("First operator: {:?}", op); /// } @@ -227,7 +227,7 @@ impl<'a> IntoIterator for CodeSectionReader<'a> { /// let mut code_reader = CodeSectionReader::new(data, 0).unwrap(); /// for body in code_reader { /// let mut binary_reader = body.expect("b").get_binary_reader(); - /// assert!(binary_reader.read_local_count().expect("local count") == 0); + /// assert!(binary_reader.read_var_u32().expect("local count") == 0); /// let op = binary_reader.read_operator().expect("first operator"); /// println!("First operator: {:?}", op); /// } diff --git a/third_party/rust/wasmparser/src/validator.rs b/third_party/rust/wasmparser/src/validator.rs index 4a4820e19589f..13f6101529820 100644 --- a/third_party/rust/wasmparser/src/validator.rs +++ b/third_party/rust/wasmparser/src/validator.rs @@ -14,84 +14,17 @@ */ use crate::limits::*; -use crate::operators_validator::{ - FunctionEnd, OperatorValidator, OperatorValidatorConfig, DEFAULT_OPERATOR_VALIDATOR_CONFIG, -}; +use crate::ResizableLimits64; use crate::WasmModuleResources; use crate::{Alias, AliasedInstance, ExternalKind, Import, ImportSectionEntryType}; use crate::{BinaryReaderError, GlobalType, MemoryType, Range, Result, TableType, Type}; use crate::{DataKind, ElementItem, ElementKind, InitExpr, Instance, Operator}; -use crate::{Export, ExportType, FunctionBody, OperatorsReader, Parser, Payload}; +use crate::{Export, ExportType, FunctionBody, Parser, Payload}; use crate::{FuncType, ResizableLimits, SectionReader, SectionWithLimitedItems}; use std::collections::{HashMap, HashSet}; use std::mem; use std::sync::Arc; -/// Test whether the given buffer contains a valid WebAssembly function. -/// The resources parameter contains all needed data to validate the operators. -pub fn validate_function_body( - bytes: &[u8], - offset: usize, - func_index: u32, - resources: impl WasmModuleResources, - operator_config: Option, -) -> Result<()> { - let operator_config = operator_config.unwrap_or(DEFAULT_OPERATOR_VALIDATOR_CONFIG); - let function_body = FunctionBody::new(offset, bytes); - let mut locals_reader = function_body.get_locals_reader()?; - let local_count = locals_reader.get_count() as usize; - if local_count > MAX_WASM_FUNCTION_LOCALS { - return Err(BinaryReaderError::new( - "locals exceed maximum", - locals_reader.original_position(), - )); - } - let mut locals: Vec<(u32, Type)> = Vec::with_capacity(local_count); - let mut locals_total: usize = 0; - for _ in 0..local_count { - let (count, ty) = locals_reader.read()?; - locals_total = locals_total.checked_add(count as usize).ok_or_else(|| { - BinaryReaderError::new("locals overflow", locals_reader.original_position()) - })?; - if locals_total > MAX_WASM_FUNCTION_LOCALS { - return Err(BinaryReaderError::new( - "locals exceed maximum", - locals_reader.original_position(), - )); - } - locals.push((count, ty)); - } - let operators_reader = function_body.get_operators_reader()?; - let func_type = resources - .func_type_at(func_index) - // Note: This was an out-of-bounds access before the change to return `Option` - // so I assumed it is considered a bug to access a non-existing function - // id here and went with panicking instead of returning a proper error. - .expect("the function index of the validated function itself is out of bounds"); - let mut operator_validator = OperatorValidator::new(func_type, &locals, operator_config) - .map_err(|e| e.set_offset(offset))?; - let mut eof_found = false; - let mut last_op = 0; - for item in operators_reader.into_iter_with_offsets() { - let (ref op, offset) = item?; - match operator_validator - .process_operator(op, &resources) - .map_err(|e| e.set_offset(offset))? - { - FunctionEnd::Yes => { - eof_found = true; - } - FunctionEnd::No => { - last_op = offset; - } - } - } - if !eof_found { - return Err(BinaryReaderError::new("end of function not found", last_op)); - } - Ok(()) -} - /// Test whether the given buffer contains a valid WebAssembly module, /// analogous to [`WebAssembly.validate`][js] in the JS API. /// @@ -207,16 +140,29 @@ struct ModuleState { parent: Option>, } -#[derive(Clone)] -struct WasmFeatures { - reference_types: bool, - module_linking: bool, - simd: bool, - multi_value: bool, - threads: bool, - tail_call: bool, - bulk_memory: bool, - deterministic_only: bool, +/// Flags for features that are enabled for validation. +#[derive(Hash, Debug, Copy, Clone)] +pub struct WasmFeatures { + /// The WebAssembly reference types proposal + pub reference_types: bool, + /// The WebAssembly module linking proposal + pub module_linking: bool, + /// The WebAssembly SIMD proposal + pub simd: bool, + /// The WebAssembly multi-value proposal (enabled by default) + pub multi_value: bool, + /// The WebAssembly threads proposal + pub threads: bool, + /// The WebAssembly tail-call proposal + pub tail_call: bool, + /// The WebAssembly bulk memory operations proposal + pub bulk_memory: bool, + /// Whether or not only deterministic instructions are allowed + pub deterministic_only: bool, + /// The WebAssembly multi memory proposal + pub multi_memory: bool, + /// The WebAssembly memory64 proposal + pub memory64: bool, } impl Default for WasmFeatures { @@ -229,6 +175,8 @@ impl Default for WasmFeatures { threads: false, tail_call: false, bulk_memory: false, + multi_memory: false, + memory64: false, deterministic_only: cfg!(feature = "deterministic"), // on-by-default features @@ -316,10 +264,7 @@ pub enum ValidPayload<'a> { /// validator that was in use should be popped off the stack to resume. Pop, /// A function was found to be validate. - /// - /// The function validator will need to be run over the operators in - /// [`OperatorsReader`] to finish validation. - Func(FuncValidator, OperatorsReader<'a>), + Func(FuncValidator, FunctionBody<'a>), } impl Validator { @@ -332,74 +277,9 @@ impl Validator { Validator::default() } - /// Configures whether the reference types proposal for WebAssembly is - /// enabled. - /// - /// The default for this option is `false`. - pub fn wasm_reference_types(&mut self, enabled: bool) -> &mut Validator { - self.features.reference_types = enabled; - self - } - - /// Configures whether the module linking proposal for WebAssembly is - /// enabled. - /// - /// The default for this option is `false`. - pub fn wasm_module_linking(&mut self, enabled: bool) -> &mut Validator { - self.features.module_linking = enabled; - self - } - - /// Configures whether the SIMD proposal for WebAssembly is - /// enabled. - /// - /// The default for this option is `false`. - pub fn wasm_simd(&mut self, enabled: bool) -> &mut Validator { - self.features.simd = enabled; - self - } - - /// Configures whether the threads proposal for WebAssembly is - /// enabled. - /// - /// The default for this option is `false`. - pub fn wasm_threads(&mut self, enabled: bool) -> &mut Validator { - self.features.threads = enabled; - self - } - - /// Configures whether the tail call proposal for WebAssembly is - /// enabled. - /// - /// The default for this option is `false`. - pub fn wasm_tail_call(&mut self, enabled: bool) -> &mut Validator { - self.features.tail_call = enabled; - self - } - - /// Configures whether the multi-value proposal for WebAssembly is - /// enabled. - /// - /// The default for this option is `true`. - pub fn wasm_multi_value(&mut self, enabled: bool) -> &mut Validator { - self.features.multi_value = enabled; - self - } - - /// Configures whether the bulk-memory proposal for WebAssembly is - /// enabled. - /// - /// The default for this option is `true`. - pub fn wasm_bulk_memory(&mut self, enabled: bool) -> &mut Validator { - self.features.bulk_memory = enabled; - self - } - - /// Configures whether only deterministic instructions are allowed to validate. - /// - /// The default for this option is `true`. - pub fn deterministic_only(&mut self, enabled: bool) -> &mut Validator { - self.features.deterministic_only = enabled; + /// Configures the enabled WebAssembly features for this `Validator`. + pub fn wasm_features(&mut self, features: WasmFeatures) -> &mut Validator { + self.features = features; self } @@ -426,12 +306,8 @@ impl Validator { } } - for (mut validator, ops) in functions_to_validate { - for item in ops.into_iter_with_offsets() { - let (op, offset) = item?; - validator.op(offset, &op)?; - } - validator.finish()?; + for (mut validator, body) in functions_to_validate { + validator.validate(&body)?; } Ok(()) } @@ -472,8 +348,8 @@ impl Validator { size: _, } => self.code_section_start(*count, range)?, CodeSectionEntry(body) => { - let (func_validator, ops) = self.code_section_entry(body)?; - return Ok(ValidPayload::Func(func_validator, ops)); + let func_validator = self.code_section_entry()?; + return Ok(ValidPayload::Func(func_validator, body.clone())); } ModuleCodeSectionStart { count, @@ -526,7 +402,7 @@ impl Validator { } // ... otherwise if this is a repeated section then only the "module // linking header" is allows to have repeats - if order == self.order && self.order == Order::ModuleLinkingHeader { + if prev == self.order && self.order == Order::ModuleLinkingHeader { return Ok(()); } self.create_error("section out of order") @@ -735,23 +611,9 @@ impl Validator { } fn value_type(&self, ty: Type) -> Result<()> { - match ty { - Type::I32 | Type::I64 | Type::F32 | Type::F64 => Ok(()), - Type::FuncRef | Type::ExternRef => { - if self.features.reference_types { - Ok(()) - } else { - self.create_error("reference types support is not enabled") - } - } - Type::V128 => { - if self.features.simd { - Ok(()) - } else { - self.create_error("SIMD support is not enabled") - } - } - _ => self.create_error("invalid value type"), + match self.features.check_value_type(ty) { + Ok(()) => Ok(()), + Err(e) => self.create_error(e), } } @@ -785,25 +647,49 @@ impl Validator { } _ => return self.create_error("element is not reference type"), } - self.limits(&ty.limits) + self.limits(&ty.limits)?; + if ty.limits.initial > MAX_WASM_TABLE_ENTRIES as u32 { + return self.create_error("minimum table size is out of bounds"); + } + Ok(()) } fn memory_type(&self, ty: &MemoryType) -> Result<()> { - self.limits(&ty.limits)?; - let initial = ty.limits.initial; - if initial as usize > MAX_WASM_MEMORY_PAGES { - return self.create_error("memory size must be at most 65536 pages (4GiB)"); - } - let maximum = ty.limits.maximum; - if maximum.is_some() && maximum.unwrap() as usize > MAX_WASM_MEMORY_PAGES { - return self.create_error("memory size must be at most 65536 pages (4GiB)"); - } - if ty.shared { - if !self.features.threads { - return self.create_error("threads must be enabled for shared memories"); + match ty { + MemoryType::M32 { limits, shared } => { + self.limits(limits)?; + let initial = limits.initial; + if initial as usize > MAX_WASM_MEMORY_PAGES { + return self.create_error("memory size must be at most 65536 pages (4GiB)"); + } + if let Some(maximum) = limits.maximum { + if maximum as usize > MAX_WASM_MEMORY_PAGES { + return self.create_error("memory size must be at most 65536 pages (4GiB)"); + } + } + if *shared { + if !self.features.threads { + return self.create_error("threads must be enabled for shared memories"); + } + if limits.maximum.is_none() { + return self.create_error("shared memory must have maximum size"); + } + } } - if ty.limits.maximum.is_none() { - return self.create_error("shared memory must have maximum size"); + MemoryType::M64 { limits } => { + if !self.features.memory64 { + return self.create_error("memory64 must be enabled for 64-bit memories"); + } + self.limits64(&limits)?; + let initial = limits.initial; + if initial > MAX_WASM_MEMORY64_PAGES { + return self.create_error("memory initial size too large"); + } + if let Some(maximum) = limits.maximum { + if maximum > MAX_WASM_MEMORY64_PAGES { + return self.create_error("memory initial size too large"); + } + } } } Ok(()) @@ -822,6 +708,15 @@ impl Validator { Ok(()) } + fn limits64(&self, limits: &ResizableLimits64) -> Result<()> { + if let Some(max) = limits.maximum { + if limits.initial > max { + return self.create_error("size minimum must not be greater than maximum"); + } + } + Ok(()) + } + /// Validates [`Payload::ImportSection`](crate::Payload) pub fn import_section(&mut self, section: &crate::ImportSectionReader<'_>) -> Result<()> { let order = self.header_order(Order::Import); @@ -829,6 +724,9 @@ impl Validator { } fn import(&mut self, entry: Import<'_>) -> Result<()> { + if !self.features.module_linking && entry.field.is_none() { + return self.create_error("module linking proposal is not enabled"); + } self.import_entry_type(&entry.ty)?; let (len, max, desc) = match entry.ty { ImportSectionEntryType::Function(type_index) => { @@ -846,7 +744,7 @@ impl Validator { ImportSectionEntryType::Memory(ty) => { let state = self.state.assert_mut(); state.memories.push(ty); - (state.memories.len(), MAX_WASM_MEMORIES, "memories") + (state.memories.len(), self.max_memories(), "memories") } ImportSectionEntryType::Global(ty) => { let def = self.state.def(ty); @@ -1085,16 +983,20 @@ impl Validator { expected: Def<&ImportSectionEntryType>, actual: Def<&ImportSectionEntryType>, ) -> Result<()> { - let limits_match = |expected: &ResizableLimits, actual: &ResizableLimits| { - actual.initial >= expected.initial - && match expected.maximum { - Some(expected_max) => match actual.maximum { - Some(actual_max) => actual_max <= expected_max, - None => false, - }, - None => true, - } - }; + macro_rules! limits_match { + ($expected:expr, $actual:expr) => {{ + let expected = $expected; + let actual = $actual; + actual.initial >= expected.initial + && match expected.maximum { + Some(expected_max) => match actual.maximum { + Some(actual_max) => actual_max <= expected_max, + None => false, + }, + None => true, + } + }}; + } match (expected.item, actual.item) { ( ImportSectionEntryType::Function(expected_idx), @@ -1109,17 +1011,34 @@ impl Validator { } (ImportSectionEntryType::Table(expected), ImportSectionEntryType::Table(actual)) => { if expected.element_type == actual.element_type - && limits_match(&expected.limits, &actual.limits) + && limits_match!(&expected.limits, &actual.limits) { return Ok(()); } self.create_error("table provided for instantiation has wrong type") } (ImportSectionEntryType::Memory(expected), ImportSectionEntryType::Memory(actual)) => { - if limits_match(&expected.limits, &actual.limits) - && expected.shared == actual.shared - { - return Ok(()); + match (expected, actual) { + ( + MemoryType::M32 { + limits: a, + shared: ash, + }, + MemoryType::M32 { + limits: b, + shared: bsh, + }, + ) => { + if limits_match!(a, b) && ash == bsh { + return Ok(()); + } + } + (MemoryType::M64 { limits: a }, MemoryType::M64 { limits: b }) => { + if limits_match!(a, b) { + return Ok(()); + } + } + _ => {} } self.create_error("memory provided for instantiation has wrong type") } @@ -1232,11 +1151,19 @@ impl Validator { }) } + fn max_memories(&self) -> usize { + if self.features.multi_memory { + MAX_WASM_MEMORIES + } else { + 1 + } + } + pub fn memory_section(&mut self, section: &crate::MemorySectionReader<'_>) -> Result<()> { self.check_max( self.state.memories.len(), section.get_count(), - MAX_WASM_MEMORIES, + self.max_memories(), "memories", )?; self.section(Order::Memory, section, |me, ty| { @@ -1256,14 +1183,14 @@ impl Validator { )?; self.section(Order::Global, section, |me, g| { me.global_type(&g.ty)?; - me.init_expr(&g.init_expr, g.ty.content_type)?; + me.init_expr(&g.init_expr, g.ty.content_type, false)?; let def = me.state.def(g.ty); me.state.assert_mut().globals.push(def); Ok(()) }) } - fn init_expr(&mut self, expr: &InitExpr<'_>, expected_ty: Type) -> Result<()> { + fn init_expr(&mut self, expr: &InitExpr<'_>, expected_ty: Type, allow32: bool) -> Result<()> { let mut ops = expr.get_operators_reader().into_iter_with_offsets(); let (op, offset) = match ops.next() { Some(Err(e)) => return Err(e), @@ -1298,7 +1225,9 @@ impl Validator { } }; if ty != expected_ty { - return self.create_error("type mismatch: invalid init_expr type"); + if !allow32 || ty != Type::I32 { + return self.create_error("type mismatch: invalid init_expr type"); + } } // Make sure the next instruction is an `end` @@ -1515,7 +1444,7 @@ impl Validator { if e.ty != table.item.element_type { return me.create_error("element_type != table type"); } - me.init_expr(&init_expr, Type::I32)?; + me.init_expr(&init_expr, Type::I32, false)?; } ElementKind::Passive | ElementKind::Declared => { if !me.features.bulk_memory { @@ -1532,10 +1461,16 @@ impl Validator { match items.read()? { ElementItem::Null(ty) => { if ty != e.ty { - return me.create_error("null type doesn't match element type"); + return me.create_error( + "type mismatch: null type doesn't match element type", + ); } } ElementItem::Func(f) => { + if e.ty != Type::FuncRef { + return me + .create_error("type mismatch: segment does not have funcref type"); + } me.get_func_type_index(me.state.def(f))?; me.state.assert_mut().function_references.insert(f); } @@ -1552,6 +1487,9 @@ impl Validator { self.offset = range.start; self.update_order(Order::DataCount)?; self.state.assert_mut().data_count = Some(count); + if count > MAX_WASM_DATA_SEGMENTS as u32 { + return self.create_error("data count section specifies too many data segments"); + } Ok(()) } @@ -1620,68 +1558,25 @@ impl Validator { /// This function will prepare a [`FuncValidator`] which can be used to /// validate the function. The function body provided will be parsed only /// enough to create the function validation context. After this the - /// [`OperatorsReader`] returned can be used to read the opcodes of the - /// function as well as feed information into the validator. + /// [`OperatorsReader`](crate::readers::OperatorsReader) returned can be used to read the + /// opcodes of the function as well as feed information into the validator. /// /// Note that the returned [`FuncValidator`] is "connected" to this /// [`Validator`] in that it uses the internal context of this validator for /// validating the function. The [`FuncValidator`] can be sent to /// another thread, for example, to offload actual processing of functions /// elsewhere. - pub fn code_section_entry<'a>( - &mut self, - body: &FunctionBody<'a>, - ) -> Result<(FuncValidator, OperatorsReader<'a>)> { + pub fn code_section_entry(&mut self) -> Result> { let ty_index = self.state.func_type_indices[self.code_section_index]; self.code_section_index += 1; - self.offset = body.get_binary_reader().original_position(); - - let mut locals = body.get_locals_reader()?; - - let mut list = Vec::new(); - let mut total = 0u32; - let max = MAX_WASM_FUNCTION_LOCALS as u32; - if locals.get_count() > max { - return self.create_error("locals exceed maximum"); - } - for _ in 0..locals.get_count() { - self.offset = locals.original_position(); - let (cnt, ty) = locals.read()?; - total = match total.checked_add(cnt) { - Some(total) => total, - None => return self.create_error("locals overflow"), - }; - if total > max { - return self.create_error("locals exceed maximum"); - } - list.push((cnt, ty)); - } - let config = OperatorValidatorConfig { - enable_module_linking: self.features.module_linking, - enable_reference_types: self.features.reference_types, - enable_multi_value: self.features.multi_value, - enable_simd: self.features.simd, - enable_bulk_memory: self.features.bulk_memory, - enable_threads: self.features.threads, - enable_tail_call: self.features.tail_call, - #[cfg(feature = "deterministic")] - deterministic_only: self.features.deterministic_only, - }; - let ty = self.func_type_at(ty_index)?; - Ok(( - FuncValidator { - validator: OperatorValidator::new(ty.item, &list, config).map_err(|e| e.0)?, - state: self.state.arc().clone(), - offset: body.get_binary_reader().original_position(), - eof_found: false, - }, - body.get_operators_reader()?, - )) + let resources = ValidatorResources(self.state.arc().clone()); + Ok(FuncValidator::new(ty_index.item, 0, resources, &self.features).unwrap()) } /// Validates [`Payload::DataSection`](crate::Payload). pub fn data_section(&mut self, section: &crate::DataSectionReader<'_>) -> Result<()> { self.data_found = section.get_count(); + self.check_max(0, section.get_count(), MAX_WASM_DATA_SEGMENTS, "segments")?; self.section(Order::Data, section, |me, d| { match d.kind { DataKind::Passive => {} @@ -1689,8 +1584,9 @@ impl Validator { memory_index, init_expr, } => { - me.get_memory(me.state.def(memory_index))?; - me.init_expr(&init_expr, Type::I32)?; + let ty = me.get_memory(me.state.def(memory_index))?.index_type(); + let allow32 = ty == Type::I64; + me.init_expr(&init_expr, ty, allow32)?; } } Ok(()) @@ -1735,6 +1631,29 @@ impl Validator { } } +impl WasmFeatures { + pub(crate) fn check_value_type(&self, ty: Type) -> Result<(), &'static str> { + match ty { + Type::I32 | Type::I64 | Type::F32 | Type::F64 => Ok(()), + Type::FuncRef | Type::ExternRef => { + if self.reference_types { + Ok(()) + } else { + Err("reference types support is not enabled") + } + } + Type::V128 => { + if self.simd { + Ok(()) + } else { + Err("SIMD support is not enabled") + } + } + _ => Err("invalid value type"), + } + } +} + impl ModuleState { fn def(&self, item: T) -> Def { Def { @@ -1812,6 +1731,56 @@ impl Def { } } +/// The implementation of [`WasmModuleResources`] used by [`Validator`]. +pub struct ValidatorResources(Arc); + +impl WasmModuleResources for ValidatorResources { + type FuncType = crate::FuncType; + + fn table_at(&self, at: u32) -> Option { + self.0.get_table(self.0.def(at)).map(|t| t.item) + } + + fn memory_at(&self, at: u32) -> Option { + self.0.get_memory(self.0.def(at)).copied() + } + + fn global_at(&self, at: u32) -> Option { + self.0.get_global(self.0.def(at)).map(|t| t.item) + } + + fn func_type_at(&self, at: u32) -> Option<&Self::FuncType> { + match self.0.get_type(self.0.def(at))?.item { + TypeDef::Func(f) => Some(f), + _ => None, + } + } + + fn type_of_function(&self, at: u32) -> Option<&Self::FuncType> { + let ty = self.0.get_func_type_index(self.0.def(at))?; + match self.0.get_type(ty)?.item { + TypeDef::Func(f) => Some(f), + _ => None, + } + } + + fn element_type_at(&self, at: u32) -> Option { + self.0.element_types.get(at as usize).cloned() + } + + fn element_count(&self) -> u32 { + self.0.element_types.len() as u32 + } + + fn data_count(&self) -> u32 { + self.0.data_count.unwrap_or(0) + } + + fn is_function_referenced(&self, idx: u32) -> bool { + self.0.function_references.contains(&idx) + } +} + mod arc { use std::ops::Deref; use std::sync::Arc; diff --git a/third_party/rust/wasmparser/src/validator/func.rs b/third_party/rust/wasmparser/src/validator/func.rs index dd93977b4b511..fa113f3e03e25 100644 --- a/third_party/rust/wasmparser/src/validator/func.rs +++ b/third_party/rust/wasmparser/src/validator/func.rs @@ -1,8 +1,6 @@ -use super::ModuleState; -use crate::operators_validator::{FunctionEnd, OperatorValidator}; -use crate::{BinaryReaderError, Result, Type}; -use crate::{Operator, WasmModuleResources, WasmTypeDef}; -use std::sync::Arc; +use crate::operators_validator::OperatorValidator; +use crate::{BinaryReader, Result, Type}; +use crate::{FunctionBody, Operator, WasmFeatures, WasmModuleResources}; /// Validation context for a WebAssembly function. /// @@ -11,29 +9,81 @@ use std::sync::Arc; /// and is created per-function in a WebAssembly module. This structure is /// suitable for sending to other threads while the original /// [`Validator`](crate::Validator) continues processing other functions. -pub struct FuncValidator { - pub(super) validator: OperatorValidator, - pub(super) state: Arc, - pub(super) offset: usize, - pub(super) eof_found: bool, +pub struct FuncValidator { + validator: OperatorValidator, + resources: T, } -impl FuncValidator { +impl FuncValidator { + /// Creates a new `FuncValidator`. + /// + /// The returned `FuncValidator` can be used to validate a function with + /// the type `ty` specified. The `resources` indicate what the containing + /// module has for the function to use, and the `features` configure what + /// WebAssembly proposals are enabled for this function. + /// + /// The returned validator can be used to then parse a [`FunctionBody`], for + /// example, to read locals and validate operators. + pub fn new( + ty: u32, + offset: usize, + resources: T, + features: &WasmFeatures, + ) -> Result> { + Ok(FuncValidator { + validator: OperatorValidator::new(ty, offset, features, &resources)?, + resources, + }) + } + + /// Convenience function to validate an entire function's body. + /// + /// You may not end up using this in final implementations because you'll + /// often want to interleave validation with parsing. + pub fn validate(&mut self, body: &FunctionBody<'_>) -> Result<()> { + let mut reader = body.get_binary_reader(); + self.read_locals(&mut reader)?; + while !reader.eof() { + let pos = reader.original_position(); + let op = reader.read_operator()?; + self.op(pos, &op)?; + } + self.finish(reader.original_position()) + } + + /// Reads the local defintions from the given `BinaryReader`, often sourced + /// from a `FunctionBody`. + /// + /// This function will automatically advance the `BinaryReader` forward, + /// leaving reading operators up to the caller afterwards. + pub fn read_locals(&mut self, reader: &mut BinaryReader<'_>) -> Result<()> { + for _ in 0..reader.read_var_u32()? { + let offset = reader.original_position(); + let cnt = reader.read_var_u32()?; + let ty = reader.read_type()?; + self.define_locals(offset, cnt, ty)?; + } + Ok(()) + } + + /// Defines locals into this validator. + /// + /// This should be used if the application is already reading local + /// definitions and there's no need to re-parse the function again. + pub fn define_locals(&mut self, offset: usize, count: u32, ty: Type) -> Result<()> { + self.validator.define_locals(offset, count, ty) + } + /// Validates the next operator in a function. /// /// This functions is expected to be called once-per-operator in a /// WebAssembly function. Each operator's offset in the original binary and - /// the operator itself are passed to this function. + /// the operator itself are passed to this function to provide more useful + /// error messages. pub fn op(&mut self, offset: usize, operator: &Operator<'_>) -> Result<()> { - self.offset = offset; - let end = self - .validator - .process_operator(operator, &*self.state) + self.validator + .process_operator(operator, &self.resources) .map_err(|e| e.set_offset(offset))?; - match end { - FunctionEnd::Yes => self.eof_found = true, - FunctionEnd::No => {} - } Ok(()) } @@ -42,71 +92,16 @@ impl FuncValidator { /// This will validate that the function was properly terminated with the /// `end` opcode. If this function is not called then the function will not /// be properly validated. - pub fn finish(&mut self) -> Result<()> { - if !self.eof_found { - return Err(BinaryReaderError::new( - "end of function not found", - self.offset, - )); - } + /// + /// The `offset` provided to this function will be used as a position for an + /// error if validation fails. + pub fn finish(&mut self, offset: usize) -> Result<()> { + self.validator.finish().map_err(|e| e.set_offset(offset))?; Ok(()) } -} -impl WasmModuleResources for ModuleState { - type TypeDef = super::TypeDef; - type TableType = crate::TableType; - type MemoryType = crate::MemoryType; - type GlobalType = crate::GlobalType; - - fn type_at(&self, at: u32) -> Option<&Self::TypeDef> { - self.get_type(self.def(at)).map(|t| t.item) - } - - fn table_at(&self, at: u32) -> Option<&Self::TableType> { - self.get_table(self.def(at)).map(|t| &t.item) - } - - fn memory_at(&self, at: u32) -> Option<&Self::MemoryType> { - self.get_memory(self.def(at)) - } - - fn global_at(&self, at: u32) -> Option<&Self::GlobalType> { - self.get_global(self.def(at)).map(|t| &t.item) - } - - fn func_type_at(&self, at: u32) -> Option<&super::FuncType> { - let ty = self.get_func_type_index(self.def(at))?; - match self.get_type(ty)?.item { - super::TypeDef::Func(f) => Some(f), - _ => None, - } - } - - fn element_type_at(&self, at: u32) -> Option { - self.element_types.get(at as usize).cloned() - } - - fn element_count(&self) -> u32 { - self.element_types.len() as u32 - } - - fn data_count(&self) -> u32 { - self.data_count.unwrap_or(0) - } - - fn is_function_referenced(&self, idx: u32) -> bool { - self.function_references.contains(&idx) - } -} - -impl WasmTypeDef for super::TypeDef { - type FuncType = crate::FuncType; - - fn as_func(&self) -> Option<&Self::FuncType> { - match self { - super::TypeDef::Func(f) => Some(f), - _ => None, - } + /// Returns the underlying module resources that this validator is using. + pub fn resources(&self) -> &T { + &self.resources } }