/src/wasm-tools/crates/wasm-smith/src/lib.rs
Line | Count | Source |
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 | | //! # #[cfg(not(target_family = "wasm"))] mod x { |
28 | | //! use libfuzzer_sys::fuzz_target; |
29 | | //! use wasm_smith::Module; |
30 | | //! |
31 | | //! fuzz_target!(|module: Module| { |
32 | | //! let wasm_bytes = module.to_bytes(); |
33 | | //! |
34 | | //! // Your code here... |
35 | | //! }); |
36 | | //! # } |
37 | | //! ``` |
38 | | //! |
39 | | //! Finally, start fuzzing: |
40 | | //! |
41 | | //! ```shell |
42 | | //! $ cargo fuzz run my_wasm_smith_fuzz_target |
43 | | //! ``` |
44 | | //! |
45 | | //! > **Note:** For a real world example, also check out [the `validate` fuzz |
46 | | //! > target](https://github.com/bytecodealliance/wasm-tools/blob/main/fuzz/src/validate.rs) |
47 | | //! > defined in this repository. Using the `wasmparser` crate, it checks that |
48 | | //! > every module generated by `wasm-smith` validates successfully. |
49 | | //! |
50 | | //! ## Design |
51 | | //! |
52 | | //! The design and implementation strategy of wasm-smith is outlined in |
53 | | //! [this article](https://fitzgeraldnick.com/2020/08/24/writing-a-test-case-generator.html). |
54 | | |
55 | | #![cfg_attr(docsrs, feature(doc_cfg))] |
56 | | #![deny(missing_docs, missing_debug_implementations)] |
57 | | // Needed for the `instructions!` macro in `src/code_builder.rs`. |
58 | | #![recursion_limit = "512"] |
59 | | |
60 | | #[cfg(feature = "component-model")] |
61 | | mod component; |
62 | | mod config; |
63 | | mod core; |
64 | | |
65 | | pub use crate::core::{InstructionKind, InstructionKinds, Module}; |
66 | | use arbitrary::{Result, Unstructured}; |
67 | | #[cfg(feature = "component-model")] |
68 | | pub use component::Component; |
69 | | pub use config::{Config, MemoryOffsetChoices}; |
70 | | use std::{collections::HashSet, fmt::Write, str}; |
71 | | use wasm_encoder::MemoryType; |
72 | | |
73 | | #[doc(hidden)] |
74 | | pub use config::InternalOptionalConfig; |
75 | | |
76 | 944k | pub(crate) fn page_size(mem: &MemoryType) -> u32 { |
77 | | const DEFAULT_WASM_PAGE_SIZE_LOG2: u32 = 16; |
78 | 944k | 1 << mem.page_size_log2.unwrap_or(DEFAULT_WASM_PAGE_SIZE_LOG2) |
79 | 944k | } |
80 | | |
81 | | /// Do something an arbitrary number of times. |
82 | | /// |
83 | | /// The callback can return `false` to exit the loop early. |
84 | 833k | pub(crate) fn arbitrary_loop<'a>( |
85 | 833k | u: &mut Unstructured<'a>, |
86 | 833k | min: usize, |
87 | 833k | max: usize, |
88 | 833k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, |
89 | 833k | ) -> Result<()> { |
90 | 833k | assert!(max >= min); |
91 | 833k | for _ in 0..min { |
92 | 0 | if !f(u)? { |
93 | 0 | return Err(arbitrary::Error::IncorrectFormat); |
94 | 0 | } |
95 | | } |
96 | 833k | for _ in 0..(max - min) { |
97 | 6.57M | let keep_going = u.arbitrary().unwrap_or(false); |
98 | 6.57M | if !keep_going { |
99 | 652k | break; |
100 | 5.92M | } |
101 | | |
102 | 5.92M | if !f(u)? { |
103 | 2.50k | break; |
104 | 5.92M | } |
105 | | } |
106 | | |
107 | 833k | Ok(()) |
108 | 833k | } wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_elems::{closure#5}::{closure#2}> Line | Count | Source | 84 | 25.5k | pub(crate) fn arbitrary_loop<'a>( | 85 | 25.5k | u: &mut Unstructured<'a>, | 86 | 25.5k | min: usize, | 87 | 25.5k | max: usize, | 88 | 25.5k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 89 | 25.5k | ) -> Result<()> { | 90 | 25.5k | assert!(max >= min); | 91 | 25.5k | for _ in 0..min { | 92 | 0 | if !f(u)? { | 93 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 94 | 0 | } | 95 | | } | 96 | 25.5k | for _ in 0..(max - min) { | 97 | 401k | let keep_going = u.arbitrary().unwrap_or(false); | 98 | 401k | if !keep_going { | 99 | 12.9k | break; | 100 | 388k | } | 101 | | | 102 | 388k | if !f(u)? { | 103 | 0 | break; | 104 | 388k | } | 105 | | } | 106 | | | 107 | 25.5k | Ok(()) | 108 | 25.5k | } |
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_elems::{closure#5}::{closure#3}> Line | Count | Source | 84 | 21.6k | pub(crate) fn arbitrary_loop<'a>( | 85 | 21.6k | u: &mut Unstructured<'a>, | 86 | 21.6k | min: usize, | 87 | 21.6k | max: usize, | 88 | 21.6k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 89 | 21.6k | ) -> Result<()> { | 90 | 21.6k | assert!(max >= min); | 91 | 21.6k | for _ in 0..min { | 92 | 0 | if !f(u)? { | 93 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 94 | 0 | } | 95 | | } | 96 | 21.6k | for _ in 0..(max - min) { | 97 | 643k | let keep_going = u.arbitrary().unwrap_or(false); | 98 | 643k | if !keep_going { | 99 | 11.3k | break; | 100 | 632k | } | 101 | | | 102 | 632k | if !f(u)? { | 103 | 0 | break; | 104 | 632k | } | 105 | | } | 106 | | | 107 | 21.6k | Ok(()) | 108 | 21.6k | } |
Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_instance_type::{closure#0}::{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_data::{closure#4}> Line | Count | Source | 84 | 16.0k | pub(crate) fn arbitrary_loop<'a>( | 85 | 16.0k | u: &mut Unstructured<'a>, | 86 | 16.0k | min: usize, | 87 | 16.0k | max: usize, | 88 | 16.0k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 89 | 16.0k | ) -> Result<()> { | 90 | 16.0k | assert!(max >= min); | 91 | 16.0k | for _ in 0..min { | 92 | 0 | if !f(u)? { | 93 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 94 | 0 | } | 95 | | } | 96 | 16.0k | for _ in 0..(max - min) { | 97 | 48.3k | let keep_going = u.arbitrary().unwrap_or(false); | 98 | 48.3k | if !keep_going { | 99 | 15.2k | break; | 100 | 33.0k | } | 101 | | | 102 | 33.0k | if !f(u)? { | 103 | 0 | break; | 104 | 33.0k | } | 105 | | } | 106 | | | 107 | 16.0k | Ok(()) | 108 | 16.0k | } |
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_tags::{closure#0}> Line | Count | Source | 84 | 4.14k | pub(crate) fn arbitrary_loop<'a>( | 85 | 4.14k | u: &mut Unstructured<'a>, | 86 | 4.14k | min: usize, | 87 | 4.14k | max: usize, | 88 | 4.14k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 89 | 4.14k | ) -> Result<()> { | 90 | 4.14k | assert!(max >= min); | 91 | 4.14k | for _ in 0..min { | 92 | 0 | if !f(u)? { | 93 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 94 | 0 | } | 95 | | } | 96 | 4.14k | for _ in 0..(max - min) { | 97 | 64.9k | let keep_going = u.arbitrary().unwrap_or(false); | 98 | 64.9k | if !keep_going { | 99 | 3.71k | break; | 100 | 61.2k | } | 101 | | | 102 | 61.2k | if !f(u)? { | 103 | 109 | break; | 104 | 61.0k | } | 105 | | } | 106 | | | 107 | 4.14k | Ok(()) | 108 | 4.14k | } |
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_elems::{closure#5}> Line | Count | Source | 84 | 14.7k | pub(crate) fn arbitrary_loop<'a>( | 85 | 14.7k | u: &mut Unstructured<'a>, | 86 | 14.7k | min: usize, | 87 | 14.7k | max: usize, | 88 | 14.7k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 89 | 14.7k | ) -> Result<()> { | 90 | 14.7k | assert!(max >= min); | 91 | 14.7k | for _ in 0..min { | 92 | 0 | if !f(u)? { | 93 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 94 | 0 | } | 95 | | } | 96 | 14.7k | for _ in 0..(max - min) { | 97 | 65.5k | let keep_going = u.arbitrary().unwrap_or(false); | 98 | 65.5k | if !keep_going { | 99 | 13.7k | break; | 100 | 51.7k | } | 101 | | | 102 | 51.7k | if !f(u)? { | 103 | 0 | break; | 104 | 51.7k | } | 105 | | } | 106 | | | 107 | 14.7k | Ok(()) | 108 | 14.7k | } |
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_funcs::{closure#1}> Line | Count | Source | 84 | 17.6k | pub(crate) fn arbitrary_loop<'a>( | 85 | 17.6k | u: &mut Unstructured<'a>, | 86 | 17.6k | min: usize, | 87 | 17.6k | max: usize, | 88 | 17.6k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 89 | 17.6k | ) -> Result<()> { | 90 | 17.6k | assert!(max >= min); | 91 | 17.6k | for _ in 0..min { | 92 | 0 | if !f(u)? { | 93 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 94 | 0 | } | 95 | | } | 96 | 17.6k | for _ in 0..(max - min) { | 97 | 362k | let keep_going = u.arbitrary().unwrap_or(false); | 98 | 362k | if !keep_going { | 99 | 17.0k | break; | 100 | 345k | } | 101 | | | 102 | 345k | if !f(u)? { | 103 | 190 | break; | 104 | 344k | } | 105 | | } | 106 | | | 107 | 17.6k | Ok(()) | 108 | 17.6k | } |
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_locals::{closure#0}> Line | Count | Source | 84 | 344k | pub(crate) fn arbitrary_loop<'a>( | 85 | 344k | u: &mut Unstructured<'a>, | 86 | 344k | min: usize, | 87 | 344k | max: usize, | 88 | 344k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 89 | 344k | ) -> Result<()> { | 90 | 344k | assert!(max >= min); | 91 | 344k | for _ in 0..min { | 92 | 0 | if !f(u)? { | 93 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 94 | 0 | } | 95 | | } | 96 | 344k | for _ in 0..(max - min) { | 97 | 2.34M | let keep_going = u.arbitrary().unwrap_or(false); | 98 | 2.34M | if !keep_going { | 99 | 330k | break; | 100 | 2.01M | } | 101 | | | 102 | 2.01M | if !f(u)? { | 103 | 0 | break; | 104 | 2.01M | } | 105 | | } | 106 | | | 107 | 344k | Ok(()) | 108 | 344k | } |
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_tables::{closure#0}> Line | Count | Source | 84 | 20.8k | pub(crate) fn arbitrary_loop<'a>( | 85 | 20.8k | u: &mut Unstructured<'a>, | 86 | 20.8k | min: usize, | 87 | 20.8k | max: usize, | 88 | 20.8k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 89 | 20.8k | ) -> Result<()> { | 90 | 20.8k | assert!(max >= min); | 91 | 20.8k | for _ in 0..min { | 92 | 0 | if !f(u)? { | 93 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 94 | 0 | } | 95 | | } | 96 | 20.8k | for _ in 0..(max - min) { | 97 | 46.0k | let keep_going = u.arbitrary().unwrap_or(false); | 98 | 46.0k | if !keep_going { | 99 | 13.4k | break; | 100 | 32.5k | } | 101 | | | 102 | 32.5k | if !f(u)? { | 103 | 1.11k | break; | 104 | 31.4k | } | 105 | | } | 106 | | | 107 | 20.8k | Ok(()) | 108 | 20.8k | } |
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_exports::{closure#4}> Line | Count | Source | 84 | 20.8k | pub(crate) fn arbitrary_loop<'a>( | 85 | 20.8k | u: &mut Unstructured<'a>, | 86 | 20.8k | min: usize, | 87 | 20.8k | max: usize, | 88 | 20.8k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 89 | 20.8k | ) -> Result<()> { | 90 | 20.8k | assert!(max >= min); | 91 | 20.8k | for _ in 0..min { | 92 | 0 | if !f(u)? { | 93 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 94 | 0 | } | 95 | | } | 96 | 20.8k | for _ in 0..(max - min) { | 97 | 91.4k | let keep_going = u.arbitrary().unwrap_or(false); | 98 | 91.4k | if !keep_going { | 99 | 19.3k | break; | 100 | 72.1k | } | 101 | | | 102 | 72.1k | if !f(u)? { | 103 | 297 | break; | 104 | 71.8k | } | 105 | | } | 106 | | | 107 | 20.8k | Ok(()) | 108 | 20.8k | } |
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_globals::{closure#0}> Line | Count | Source | 84 | 20.8k | pub(crate) fn arbitrary_loop<'a>( | 85 | 20.8k | u: &mut Unstructured<'a>, | 86 | 20.8k | min: usize, | 87 | 20.8k | max: usize, | 88 | 20.8k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 89 | 20.8k | ) -> Result<()> { | 90 | 20.8k | assert!(max >= min); | 91 | 20.8k | for _ in 0..min { | 92 | 0 | if !f(u)? { | 93 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 94 | 0 | } | 95 | | } | 96 | 20.8k | for _ in 0..(max - min) { | 97 | 142k | let keep_going = u.arbitrary().unwrap_or(false); | 98 | 142k | if !keep_going { | 99 | 19.6k | break; | 100 | 122k | } | 101 | | | 102 | 122k | if !f(u)? { | 103 | 92 | break; | 104 | 122k | } | 105 | | } | 106 | | | 107 | 20.8k | Ok(()) | 108 | 20.8k | } |
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_imports::{closure#0}> Line | Count | Source | 84 | 20.8k | pub(crate) fn arbitrary_loop<'a>( | 85 | 20.8k | u: &mut Unstructured<'a>, | 86 | 20.8k | min: usize, | 87 | 20.8k | max: usize, | 88 | 20.8k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 89 | 20.8k | ) -> Result<()> { | 90 | 20.8k | assert!(max >= min); | 91 | 20.8k | for _ in 0..min { | 92 | 0 | if !f(u)? { | 93 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 94 | 0 | } | 95 | | } | 96 | 20.8k | for _ in 0..(max - min) { | 97 | 177k | let keep_going = u.arbitrary().unwrap_or(false); | 98 | 177k | if !keep_going { | 99 | 18.4k | break; | 100 | 158k | } | 101 | | | 102 | 158k | if !f(u)? { | 103 | 311 | break; | 104 | 158k | } | 105 | | } | 106 | | | 107 | 20.8k | Ok(()) | 108 | 20.8k | } |
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_memories::{closure#0}> Line | Count | Source | 84 | 20.8k | pub(crate) fn arbitrary_loop<'a>( | 85 | 20.8k | u: &mut Unstructured<'a>, | 86 | 20.8k | min: usize, | 87 | 20.8k | max: usize, | 88 | 20.8k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 89 | 20.8k | ) -> Result<()> { | 90 | 20.8k | assert!(max >= min); | 91 | 20.8k | for _ in 0..min { | 92 | 0 | if !f(u)? { | 93 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 94 | 0 | } | 95 | | } | 96 | 20.8k | for _ in 0..(max - min) { | 97 | 66.3k | let keep_going = u.arbitrary().unwrap_or(false); | 98 | 66.3k | if !keep_going { | 99 | 18.1k | break; | 100 | 48.2k | } | 101 | | | 102 | 48.2k | if !f(u)? { | 103 | 399 | break; | 104 | 47.8k | } | 105 | | } | 106 | | | 107 | 20.8k | Ok(()) | 108 | 20.8k | } |
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_func_type::{closure#0}> Line | Count | Source | 84 | 142k | pub(crate) fn arbitrary_loop<'a>( | 85 | 142k | u: &mut Unstructured<'a>, | 86 | 142k | min: usize, | 87 | 142k | max: usize, | 88 | 142k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 89 | 142k | ) -> Result<()> { | 90 | 142k | assert!(max >= min); | 91 | 142k | for _ in 0..min { | 92 | 0 | if !f(u)? { | 93 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 94 | 0 | } | 95 | | } | 96 | 142k | for _ in 0..(max - min) { | 97 | 1.33M | let keep_going = u.arbitrary().unwrap_or(false); | 98 | 1.33M | if !keep_going { | 99 | 91.2k | break; | 100 | 1.24M | } | 101 | | | 102 | 1.24M | if !f(u)? { | 103 | 0 | break; | 104 | 1.24M | } | 105 | | } | 106 | | | 107 | 142k | Ok(()) | 108 | 142k | } |
wasm_smith::arbitrary_loop::<<wasm_smith::core::Module>::arbitrary_func_type::{closure#1}> Line | Count | Source | 84 | 142k | pub(crate) fn arbitrary_loop<'a>( | 85 | 142k | u: &mut Unstructured<'a>, | 86 | 142k | min: usize, | 87 | 142k | max: usize, | 88 | 142k | mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>, | 89 | 142k | ) -> Result<()> { | 90 | 142k | assert!(max >= min); | 91 | 142k | for _ in 0..min { | 92 | 0 | if !f(u)? { | 93 | 0 | return Err(arbitrary::Error::IncorrectFormat); | 94 | 0 | } | 95 | | } | 96 | 142k | for _ in 0..(max - min) { | 97 | 788k | let keep_going = u.arbitrary().unwrap_or(false); | 98 | 788k | if !keep_going { | 99 | 67.1k | break; | 100 | 721k | } | 101 | | | 102 | 721k | if !f(u)? { | 103 | 0 | break; | 104 | 721k | } | 105 | | } | 106 | | | 107 | 142k | Ok(()) | 108 | 142k | } |
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_func_type::{closure#0}> Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_func_type::{closure#1}> 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_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_record_type::{closure#0}> 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_variant_type::{closure#0}> Unexecuted instantiation: wasm_smith::arbitrary_loop::<<wasm_smith::component::ComponentBuilder>::arbitrary_import_section::{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}> Unexecuted instantiation: wasm_smith::arbitrary_loop::<wasm_smith::component::arbitrary_func_type::{closure#0}> Unexecuted instantiation: wasm_smith::arbitrary_loop::<wasm_smith::component::arbitrary_func_type::{closure#1}> |
109 | | |
110 | | // Mirror what happens in `Arbitrary for String`, but do so with a clamped size. |
111 | 397k | pub(crate) fn limited_str<'a>(max_size: usize, u: &mut Unstructured<'a>) -> Result<&'a str> { |
112 | 397k | let size = u.arbitrary_len::<u8>()?; |
113 | 397k | let size = std::cmp::min(size, max_size); |
114 | 397k | match str::from_utf8(u.peek_bytes(size).unwrap()) { |
115 | 24.0k | Ok(s) => { |
116 | 24.0k | u.bytes(size).unwrap(); |
117 | 24.0k | Ok(s) |
118 | | } |
119 | 373k | Err(e) => { |
120 | 373k | let i = e.valid_up_to(); |
121 | 373k | let valid = u.bytes(i).unwrap(); |
122 | 373k | let s = str::from_utf8(valid).unwrap(); |
123 | 373k | Ok(s) |
124 | | } |
125 | | } |
126 | 397k | } |
127 | | |
128 | 397k | pub(crate) fn limited_string(max_size: usize, u: &mut Unstructured) -> Result<String> { |
129 | 397k | Ok(limited_str(max_size, u)?.into()) |
130 | 397k | } |
131 | | |
132 | 80.6k | pub(crate) fn unique_string( |
133 | 80.6k | max_size: usize, |
134 | 80.6k | names: &mut HashSet<String>, |
135 | 80.6k | u: &mut Unstructured, |
136 | 80.6k | ) -> Result<String> { |
137 | 80.6k | let mut name = limited_string(max_size, u)?; |
138 | 140k | while names.contains(&name) { |
139 | 59.6k | write!(&mut name, "{}", names.len()).unwrap(); |
140 | 59.6k | } |
141 | 80.6k | names.insert(name.clone()); |
142 | 80.6k | Ok(name) |
143 | 80.6k | } |
144 | | |
145 | | #[cfg(feature = "component-model")] |
146 | 0 | pub(crate) fn unique_kebab_string( |
147 | 0 | max_size: usize, |
148 | 0 | names: &mut HashSet<String>, |
149 | 0 | u: &mut Unstructured, |
150 | 0 | ) -> Result<String> { |
151 | 0 | let size = std::cmp::min(u.arbitrary_len::<u8>()?, max_size); |
152 | 0 | let mut name = String::with_capacity(size); |
153 | 0 | let mut require_alpha = true; |
154 | 0 | for _ in 0..size { |
155 | 0 | name.push(match u.int_in_range::<u8>(0..=36)? { |
156 | 0 | x if (0..26).contains(&x) => { |
157 | 0 | require_alpha = false; |
158 | 0 | (b'a' + x) as char |
159 | | } |
160 | 0 | x if (26..36).contains(&x) => { |
161 | 0 | if require_alpha { |
162 | 0 | require_alpha = false; |
163 | 0 | (b'a' + (x - 26)) as char |
164 | | } else { |
165 | 0 | (b'0' + (x - 26)) as char |
166 | | } |
167 | | } |
168 | 0 | x if x == 36 => { |
169 | 0 | if require_alpha { |
170 | 0 | require_alpha = false; |
171 | 0 | 'a' |
172 | | } else { |
173 | 0 | require_alpha = true; |
174 | 0 | '-' |
175 | | } |
176 | | } |
177 | 0 | _ => unreachable!(), |
178 | | }); |
179 | | } |
180 | | |
181 | 0 | if name.is_empty() || name.ends_with('-') { |
182 | 0 | name.push('a'); |
183 | 0 | } |
184 | | |
185 | 0 | while names.contains(&name) { |
186 | 0 | write!(&mut name, "{}", names.len()).unwrap(); |
187 | 0 | } |
188 | | |
189 | 0 | names.insert(name.clone()); |
190 | | |
191 | 0 | Ok(name) |
192 | 0 | } |
193 | | |
194 | | #[cfg(feature = "component-model")] |
195 | 0 | pub(crate) fn unique_url( |
196 | 0 | max_size: usize, |
197 | 0 | names: &mut HashSet<String>, |
198 | 0 | u: &mut Unstructured, |
199 | 0 | ) -> Result<String> { |
200 | 0 | let path = unique_kebab_string(max_size, names, u)?; |
201 | 0 | Ok(format!("https://example.com/{path}")) |
202 | 0 | } |