/src/wasm-tools/crates/wasm-smith/src/lib.rs
Line | Count | Source (jump to first uncovered line) |
1 | | //! A WebAssembly test case generator. |
2 | | //! |
3 | | //! ## Usage |
4 | | //! |
5 | | //! First, use [`cargo fuzz`](https://github.com/rust-fuzz/cargo-fuzz) to define |
6 | | //! a new fuzz target: |
7 | | //! |
8 | | //! ```shell |
9 | | //! $ cargo fuzz add my_wasm_smith_fuzz_target |
10 | | //! ``` |
11 | | //! |
12 | | //! Next, add `wasm-smith` to your dependencies: |
13 | | //! |
14 | | //! ```shell |
15 | | //! $ cargo add wasm-smith |
16 | | //! ``` |
17 | | //! |
18 | | //! Then, define your fuzz target so that it takes arbitrary |
19 | | //! `wasm_smith::Module`s as an argument, convert the module into serialized |
20 | | //! Wasm bytes via the `to_bytes` method, and then feed it into your system: |
21 | | //! |
22 | | //! ```no_run |
23 | | //! // fuzz/fuzz_targets/my_wasm_smith_fuzz_target.rs |
24 | | //! |
25 | | //! #![no_main] |
26 | | //! |
27 | | //! use libfuzzer_sys::fuzz_target; |
28 | | //! use wasm_smith::Module; |
29 | | //! |
30 | | //! fuzz_target!(|module: Module| { |
31 | | //! let wasm_bytes = module.to_bytes(); |
32 | | //! |
33 | | //! // Your code here... |
34 | | //! }); |
35 | | //! ``` |
36 | | //! |
37 | | //! Finally, start fuzzing: |
38 | | //! |
39 | | //! ```shell |
40 | | //! $ cargo fuzz run my_wasm_smith_fuzz_target |
41 | | //! ``` |
42 | | //! |
43 | | //! > **Note:** For a real world example, also check out [the `validate` fuzz |
44 | | //! > target](https://github.com/fitzgen/wasm-smith/blob/main/fuzz/fuzz_targets/validate.rs) |
45 | | //! > defined in this repository. Using the `wasmparser` crate, it checks that |
46 | | //! > every module generated by `wasm-smith` validates successfully. |
47 | | //! |
48 | | //! ## Design |
49 | | //! |
50 | | //! The design and implementation strategy of wasm-smith is outlined in |
51 | | //! [this article](https://fitzgeraldnick.com/2020/08/24/writing-a-test-case-generator.html). |
52 | | |
53 | | #![deny(missing_docs, missing_debug_implementations)] |
54 | | // Needed for the `instructions!` macro in `src/code_builder.rs`. |
55 | | #![recursion_limit = "512"] |
56 | | |
57 | | mod component; |
58 | | mod config; |
59 | | mod core; |
60 | | |
61 | | pub use crate::core::{ |
62 | | ConfiguredModule, InstructionKind, InstructionKinds, MaybeInvalidModule, Module, |
63 | | }; |
64 | | use arbitrary::{Result, Unstructured}; |
65 | | pub use component::{Component, ConfiguredComponent}; |
66 | | pub use config::{Config, DefaultConfig, SwarmConfig}; |
67 | | use std::{collections::HashSet, fmt::Write, str}; |
68 | | use wasmparser::types::{KebabStr, KebabString}; |
69 | | |
70 | | /// Do something an arbitrary number of times. |
71 | | /// |
72 | | /// The callback can return `false` to exit the loop early. |
73 | 2.01M | pub(crate) fn arbitrary_loop<'a>( |
74 | 2.01M | u: &mut Unstructured<'a>, |
75 | 2.01M | min: usize, |
76 | 2.01M | max: usize, |
77 | 2.01M | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, |
78 | 2.01M | ) -> Result<()> { |
79 | 2.01M | assert!(max >= min); |
80 | 2.01M | for _ in 0..min { |
81 | 0 | if !f(u)? { |
82 | 0 | return Err(arbitrary::Error::IncorrectFormat); |
83 | 0 | } |
84 | | } |
85 | 2.01M | for _ in 0..(max - min) { |
86 | 30.4M | let keep_going = u.arbitrary().unwrap_or(false); |
87 | 30.4M | if !keep_going { |
88 | 1.52M | break; |
89 | 28.9M | } |
90 | 28.9M | |
91 | 28.9M | if !f(u)? { |
92 | 5.63k | break; |
93 | 28.9M | } |
94 | | } |
95 | | |
96 | 2.01M | Ok(()) |
97 | 2.01M | } wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_tags::{closure#0}>Line | Count | Source | 73 | 4.85k | pub(crate) fn arbitrary_loop<'a>( | 74 | 4.85k | u: &mut Unstructured<'a>, | 75 | 4.85k | min: usize, | 76 | 4.85k | max: usize, | 77 | 4.85k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 78 | 4.85k | ) -> Result<()> { | 79 | 4.85k | assert!(max >= min); | 80 | 4.85k | for _ in 0..min { | 81 | 0 | if !f(u)? { | 82 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 83 | 0 | } | 84 | | } | 85 | 4.85k | for _ in 0..(max - min) { | 86 | 65.1k | let keep_going = u.arbitrary().unwrap_or(false); | 87 | 65.1k | if !keep_going { | 88 | 4.38k | break; | 89 | 60.8k | } | 90 | 60.8k | | 91 | 60.8k | if !f(u)? { | 92 | 220 | break; | 93 | 60.5k | } | 94 | | } | 95 | | | 96 | 4.85k | Ok(()) | 97 | 4.85k | } |
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_variant_type::{closure#0}>Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_component_type::{closure#0}::{closure#0}>wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_types::{closure#0}>Line | Count | Source | 73 | 38.2k | pub(crate) fn arbitrary_loop<'a>( | 74 | 38.2k | u: &mut Unstructured<'a>, | 75 | 38.2k | min: usize, | 76 | 38.2k | max: usize, | 77 | 38.2k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 78 | 38.2k | ) -> Result<()> { | 79 | 38.2k | assert!(max >= min); | 80 | 38.2k | for _ in 0..min { | 81 | 0 | if !f(u)? { | 82 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 83 | 0 | } | 84 | | } | 85 | 38.2k | for _ in 0..(max - min) { | 86 | 353k | let keep_going = u.arbitrary().unwrap_or(false); | 87 | 353k | if !keep_going { | 88 | 36.6k | break; | 89 | 317k | } | 90 | 317k | | 91 | 317k | if !f(u)? { | 92 | 0 | break; | 93 | 317k | } | 94 | | } | 95 | | | 96 | 38.2k | Ok(()) | 97 | 38.2k | } |
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_tables::{closure#0}>Line | Count | Source | 73 | 38.2k | pub(crate) fn arbitrary_loop<'a>( | 74 | 38.2k | u: &mut Unstructured<'a>, | 75 | 38.2k | min: usize, | 76 | 38.2k | max: usize, | 77 | 38.2k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 78 | 38.2k | ) -> Result<()> { | 79 | 38.2k | assert!(max >= min); | 80 | 38.2k | for _ in 0..min { | 81 | 0 | if !f(u)? { | 82 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 83 | 0 | } | 84 | | } | 85 | 38.2k | for _ in 0..(max - min) { | 86 | 118k | let keep_going = u.arbitrary().unwrap_or(false); | 87 | 118k | if !keep_going { | 88 | 27.0k | break; | 89 | 91.1k | } | 90 | 91.1k | | 91 | 91.1k | if !f(u)? { | 92 | 2.42k | break; | 93 | 88.7k | } | 94 | | } | 95 | | | 96 | 38.2k | Ok(()) | 97 | 38.2k | } |
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_imports::{closure#0}>Line | Count | Source | 73 | 38.2k | pub(crate) fn arbitrary_loop<'a>( | 74 | 38.2k | u: &mut Unstructured<'a>, | 75 | 38.2k | min: usize, | 76 | 38.2k | max: usize, | 77 | 38.2k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 78 | 38.2k | ) -> Result<()> { | 79 | 38.2k | assert!(max >= min); | 80 | 38.2k | for _ in 0..min { | 81 | 0 | if !f(u)? { | 82 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 83 | 0 | } | 84 | | } | 85 | 38.2k | for _ in 0..(max - min) { | 86 | 303k | let keep_going = u.arbitrary().unwrap_or(false); | 87 | 303k | if !keep_going { | 88 | 33.6k | break; | 89 | 269k | } | 90 | 269k | | 91 | 269k | if !f(u)? { | 92 | 774 | break; | 93 | 268k | } | 94 | | } | 95 | | | 96 | 38.2k | Ok(()) | 97 | 38.2k | } |
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_flags_type::{closure#0}>Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_import_section::{closure#0}>wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_globals::{closure#0}>Line | Count | Source | 73 | 38.2k | pub(crate) fn arbitrary_loop<'a>( | 74 | 38.2k | u: &mut Unstructured<'a>, | 75 | 38.2k | min: usize, | 76 | 38.2k | max: usize, | 77 | 38.2k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 78 | 38.2k | ) -> Result<()> { | 79 | 38.2k | assert!(max >= min); | 80 | 38.2k | for _ in 0..min { | 81 | 0 | if !f(u)? { | 82 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 83 | 0 | } | 84 | | } | 85 | 38.2k | for _ in 0..(max - min) { | 86 | 348k | let keep_going = u.arbitrary().unwrap_or(false); | 87 | 348k | if !keep_going { | 88 | 35.4k | break; | 89 | 313k | } | 90 | 313k | | 91 | 313k | if !f(u)? { | 92 | 345 | break; | 93 | 313k | } | 94 | | } | 95 | | | 96 | 38.2k | Ok(()) | 97 | 38.2k | } |
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_elems::{closure#7}::{closure#2}>Line | Count | Source | 73 | 94.3k | pub(crate) fn arbitrary_loop<'a>( | 74 | 94.3k | u: &mut Unstructured<'a>, | 75 | 94.3k | min: usize, | 76 | 94.3k | max: usize, | 77 | 94.3k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 78 | 94.3k | ) -> Result<()> { | 79 | 94.3k | assert!(max >= min); | 80 | 94.3k | for _ in 0..min { | 81 | 0 | if !f(u)? { | 82 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 83 | 0 | } | 84 | | } | 85 | 94.3k | for _ in 0..(max - min) { | 86 | 9.69M | let keep_going = u.arbitrary().unwrap_or(false); | 87 | 9.69M | if !keep_going { | 88 | 43.6k | break; | 89 | 9.64M | } | 90 | 9.64M | | 91 | 9.64M | if !f(u)? { | 92 | 0 | break; | 93 | 9.64M | } | 94 | | } | 95 | | | 96 | 94.3k | Ok(()) | 97 | 94.3k | } |
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_elems::{closure#7}>Line | Count | Source | 73 | 25.2k | pub(crate) fn arbitrary_loop<'a>( | 74 | 25.2k | u: &mut Unstructured<'a>, | 75 | 25.2k | min: usize, | 76 | 25.2k | max: usize, | 77 | 25.2k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 78 | 25.2k | ) -> Result<()> { | 79 | 25.2k | assert!(max >= min); | 80 | 25.2k | for _ in 0..min { | 81 | 0 | if !f(u)? { | 82 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 83 | 0 | } | 84 | | } | 85 | 25.2k | for _ in 0..(max - min) { | 86 | 191k | let keep_going = u.arbitrary().unwrap_or(false); | 87 | 191k | if !keep_going { | 88 | 22.6k | break; | 89 | 168k | } | 90 | 168k | | 91 | 168k | if !f(u)? { | 92 | 0 | break; | 93 | 168k | } | 94 | | } | 95 | | | 96 | 25.2k | Ok(()) | 97 | 25.2k | } |
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_memories::{closure#0}>Line | Count | Source | 73 | 38.2k | pub(crate) fn arbitrary_loop<'a>( | 74 | 38.2k | u: &mut Unstructured<'a>, | 75 | 38.2k | min: usize, | 76 | 38.2k | max: usize, | 77 | 38.2k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 78 | 38.2k | ) -> Result<()> { | 79 | 38.2k | assert!(max >= min); | 80 | 38.2k | for _ in 0..min { | 81 | 0 | if !f(u)? { | 82 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 83 | 0 | } | 84 | | } | 85 | 38.2k | for _ in 0..(max - min) { | 86 | 131k | let keep_going = u.arbitrary().unwrap_or(false); | 87 | 131k | if !keep_going { | 88 | 28.5k | break; | 89 | 102k | } | 90 | 102k | | 91 | 102k | if !f(u)? { | 92 | 564 | break; | 93 | 102k | } | 94 | | } | 95 | | | 96 | 38.2k | Ok(()) | 97 | 38.2k | } |
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_record_type::{closure#0}>wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_locals::{closure#0}>Line | Count | Source | 73 | 902k | pub(crate) fn arbitrary_loop<'a>( | 74 | 902k | u: &mut Unstructured<'a>, | 75 | 902k | min: usize, | 76 | 902k | max: usize, | 77 | 902k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 78 | 902k | ) -> Result<()> { | 79 | 902k | assert!(max >= min); | 80 | 902k | for _ in 0..min { | 81 | 0 | if !f(u)? { | 82 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 83 | 0 | } | 84 | | } | 85 | 902k | for _ in 0..(max - min) { | 86 | 6.95M | let keep_going = u.arbitrary().unwrap_or(false); | 87 | 6.95M | if !keep_going { | 88 | 859k | break; | 89 | 6.09M | } | 90 | 6.09M | | 91 | 6.09M | if !f(u)? { | 92 | 0 | break; | 93 | 6.09M | } | 94 | | } | 95 | | | 96 | 902k | Ok(()) | 97 | 902k | } |
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_enum_type::{closure#0}>Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_tuple_type::{closure#0}>Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_module_type::{closure#0}>Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_canonical_section::{closure#0}>Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_core_type_section::{closure#0}>wasm_smith::arbitrary_loop::<wasm_smith::core::arbitrary_func_type::{closure#1}>Line | Count | Source | 73 | 317k | pub(crate) fn arbitrary_loop<'a>( | 74 | 317k | u: &mut Unstructured<'a>, | 75 | 317k | min: usize, | 76 | 317k | max: usize, | 77 | 317k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 78 | 317k | ) -> Result<()> { | 79 | 317k | assert!(max >= min); | 80 | 317k | for _ in 0..min { | 81 | 0 | if !f(u)? { | 82 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 83 | 0 | } | 84 | | } | 85 | 317k | for _ in 0..(max - min) { | 86 | 3.17M | let keep_going = u.arbitrary().unwrap_or(false); | 87 | 3.17M | if !keep_going { | 88 | 132k | break; | 89 | 3.04M | } | 90 | 3.04M | | 91 | 3.04M | if !f(u)? { | 92 | 0 | break; | 93 | 3.04M | } | 94 | | } | 95 | | | 96 | 317k | Ok(()) | 97 | 317k | } |
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_data::{closure#4}>Line | Count | Source | 73 | 30.6k | pub(crate) fn arbitrary_loop<'a>( | 74 | 30.6k | u: &mut Unstructured<'a>, | 75 | 30.6k | min: usize, | 76 | 30.6k | max: usize, | 77 | 30.6k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 78 | 30.6k | ) -> Result<()> { | 79 | 30.6k | assert!(max >= min); | 80 | 30.6k | for _ in 0..min { | 81 | 0 | if !f(u)? { | 82 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 83 | 0 | } | 84 | | } | 85 | 30.6k | for _ in 0..(max - min) { | 86 | 131k | let keep_going = u.arbitrary().unwrap_or(false); | 87 | 131k | if !keep_going { | 88 | 28.8k | break; | 89 | 102k | } | 90 | 102k | | 91 | 102k | if !f(u)? { | 92 | 0 | break; | 93 | 102k | } | 94 | | } | 95 | | | 96 | 30.6k | Ok(()) | 97 | 30.6k | } |
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_type_section::{closure#0}>Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_instance_type::{closure#0}::{closure#0}>wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_funcs::{closure#0}>Line | Count | Source | 73 | 34.5k | pub(crate) fn arbitrary_loop<'a>( | 74 | 34.5k | u: &mut Unstructured<'a>, | 75 | 34.5k | min: usize, | 76 | 34.5k | max: usize, | 77 | 34.5k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 78 | 34.5k | ) -> Result<()> { | 79 | 34.5k | assert!(max >= min); | 80 | 34.5k | for _ in 0..min { | 81 | 0 | if !f(u)? { | 82 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 83 | 0 | } | 84 | | } | 85 | 34.5k | for _ in 0..(max - min) { | 86 | 935k | let keep_going = u.arbitrary().unwrap_or(false); | 87 | 935k | if !keep_going { | 88 | 32.7k | break; | 89 | 902k | } | 90 | 902k | | 91 | 902k | if !f(u)? { | 92 | 568 | break; | 93 | 902k | } | 94 | | } | 95 | | | 96 | 34.5k | Ok(()) | 97 | 34.5k | } |
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_exports::{closure#4}>Line | Count | Source | 73 | 38.2k | pub(crate) fn arbitrary_loop<'a>( | 74 | 38.2k | u: &mut Unstructured<'a>, | 75 | 38.2k | min: usize, | 76 | 38.2k | max: usize, | 77 | 38.2k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 78 | 38.2k | ) -> Result<()> { | 79 | 38.2k | assert!(max >= min); | 80 | 38.2k | for _ in 0..min { | 81 | 0 | if !f(u)? { | 82 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 83 | 0 | } | 84 | | } | 85 | 38.2k | for _ in 0..(max - min) { | 86 | 215k | let keep_going = u.arbitrary().unwrap_or(false); | 87 | 215k | if !keep_going { | 88 | 34.0k | break; | 89 | 181k | } | 90 | 181k | | 91 | 181k | if !f(u)? { | 92 | 743 | break; | 93 | 180k | } | 94 | | } | 95 | | | 96 | 38.2k | Ok(()) | 97 | 38.2k | } |
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_func_type::{closure#0}>Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_union_type::{closure#0}>wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_elems::{closure#7}::{closure#3}>Line | Count | Source | 73 | 59.3k | pub(crate) fn arbitrary_loop<'a>( | 74 | 59.3k | u: &mut Unstructured<'a>, | 75 | 59.3k | min: usize, | 76 | 59.3k | max: usize, | 77 | 59.3k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 78 | 59.3k | ) -> Result<()> { | 79 | 59.3k | assert!(max >= min); | 80 | 59.3k | for _ in 0..min { | 81 | 0 | if !f(u)? { | 82 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 83 | 0 | } | 84 | | } | 85 | 59.3k | for _ in 0..(max - min) { | 86 | 4.33M | let keep_going = u.arbitrary().unwrap_or(false); | 87 | 4.33M | if !keep_going { | 88 | 31.4k | break; | 89 | 4.30M | } | 90 | 4.30M | | 91 | 4.30M | if !f(u)? { | 92 | 0 | break; | 93 | 4.30M | } | 94 | | } | 95 | | | 96 | 59.3k | Ok(()) | 97 | 59.3k | } |
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_func_type::{closure#1}>wasm_smith::arbitrary_loop::<wasm_smith::core::arbitrary_func_type::{closure#0}>Line | Count | Source | 73 | 317k | pub(crate) fn arbitrary_loop<'a>( | 74 | 317k | u: &mut Unstructured<'a>, | 75 | 317k | min: usize, | 76 | 317k | max: usize, | 77 | 317k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 78 | 317k | ) -> Result<()> { | 79 | 317k | assert!(max >= min); | 80 | 317k | for _ in 0..min { | 81 | 0 | if !f(u)? { | 82 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 83 | 0 | } | 84 | | } | 85 | 317k | for _ in 0..(max - min) { | 86 | 3.50M | let keep_going = u.arbitrary().unwrap_or(false); | 87 | 3.50M | if !keep_going { | 88 | 174k | break; | 89 | 3.32M | } | 90 | 3.32M | | 91 | 3.32M | if !f(u)? { | 92 | 0 | break; | 93 | 3.32M | } | 94 | | } | 95 | | | 96 | 317k | Ok(()) | 97 | 317k | } |
|
98 | | |
99 | | // Mirror what happens in `Arbitrary for String`, but do so with a clamped size. |
100 | 718k | pub(crate) fn limited_str<'a>(max_size: usize, u: &mut Unstructured<'a>) -> Result<&'a str> { |
101 | 718k | let size = u.arbitrary_len::<u8>()?; |
102 | 718k | let size = std::cmp::min(size, max_size); |
103 | 718k | match str::from_utf8(u.peek_bytes(size).unwrap()) { |
104 | 91.3k | Ok(s) => { |
105 | 91.3k | u.bytes(size).unwrap(); |
106 | 91.3k | Ok(s) |
107 | | } |
108 | 626k | Err(e) => { |
109 | 626k | let i = e.valid_up_to(); |
110 | 626k | let valid = u.bytes(i).unwrap(); |
111 | 626k | let s = unsafe { |
112 | 0 | debug_assert!(str::from_utf8(valid).is_ok()); |
113 | 626k | str::from_utf8_unchecked(valid) |
114 | 626k | }; |
115 | 626k | Ok(s) |
116 | | } |
117 | | } |
118 | 718k | } |
119 | | |
120 | 718k | pub(crate) fn limited_string(max_size: usize, u: &mut Unstructured) -> Result<String> { |
121 | 718k | Ok(limited_str(max_size, u)?.into()) |
122 | 718k | } |
123 | | |
124 | 180k | pub(crate) fn unique_string( |
125 | 180k | max_size: usize, |
126 | 180k | names: &mut HashSet<String>, |
127 | 180k | u: &mut Unstructured, |
128 | 180k | ) -> Result<String> { |
129 | 180k | let mut name = limited_string(max_size, u)?; |
130 | 330k | while names.contains(&name) { |
131 | 149k | write!(&mut name, "{}", names.len()).unwrap(); |
132 | 149k | } |
133 | 180k | names.insert(name.clone()); |
134 | 180k | Ok(name) |
135 | 180k | } |
136 | | |
137 | 0 | pub(crate) fn unique_kebab_string( |
138 | 0 | max_size: usize, |
139 | 0 | names: &mut HashSet<KebabString>, |
140 | 0 | u: &mut Unstructured, |
141 | 0 | ) -> Result<KebabString> { |
142 | 0 | let size = std::cmp::min(u.arbitrary_len::<u8>()?, max_size); |
143 | 0 | let mut name = String::with_capacity(size); |
144 | 0 | let mut require_alpha = true; |
145 | 0 | for _ in 0..size { |
146 | 0 | name.push(match u.int_in_range::<u8>(0..=36)? { |
147 | 0 | x if (0..26).contains(&x) => { |
148 | 0 | require_alpha = false; |
149 | 0 | (b'a' + x) as char |
150 | | } |
151 | 0 | x if (26..36).contains(&x) => { |
152 | 0 | if require_alpha { |
153 | 0 | require_alpha = false; |
154 | 0 | (b'a' + (x - 26)) as char |
155 | | } else { |
156 | 0 | (b'0' + (x - 26)) as char |
157 | | } |
158 | | } |
159 | 0 | x if x == 36 => { |
160 | 0 | if require_alpha { |
161 | 0 | require_alpha = false; |
162 | 0 | 'a' |
163 | | } else { |
164 | 0 | require_alpha = true; |
165 | 0 | '-' |
166 | | } |
167 | | } |
168 | 0 | _ => unreachable!(), |
169 | | }); |
170 | | } |
171 | | |
172 | 0 | if name.is_empty() || name.ends_with('-') { |
173 | 0 | name.push('a'); |
174 | 0 | } |
175 | | |
176 | 0 | while names.contains(KebabStr::new(&name).unwrap()) { |
177 | 0 | write!(&mut name, "{}", names.len()).unwrap(); |
178 | 0 | } |
179 | | |
180 | 0 | let name = KebabString::new(name).unwrap(); |
181 | 0 | names.insert(name.clone()); |
182 | 0 |
|
183 | 0 | Ok(name) |
184 | 0 | } |
185 | | |
186 | 0 | pub(crate) fn unique_url( |
187 | 0 | max_size: usize, |
188 | 0 | names: &mut HashSet<KebabString>, |
189 | 0 | u: &mut Unstructured, |
190 | 0 | ) -> Result<String> { |
191 | 0 | let path = unique_kebab_string(max_size, names, u)?; |
192 | 0 | Ok(format!("https://example.com/{path}")) |
193 | 0 | } |