1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
54#![deny(missing_docs, missing_debug_implementations)]
55#![recursion_limit = "512"]
57
58#[cfg(feature = "component-model")]
59mod component;
60mod config;
61mod core;
62
63pub use crate::core::{InstructionKind, InstructionKinds, Module};
64use arbitrary::{Result, Unstructured};
65#[cfg(feature = "component-model")]
66pub use component::Component;
67pub use config::{Config, MemoryOffsetChoices};
68use std::{collections::HashSet, fmt::Write, str};
69use wasm_encoder::MemoryType;
70
71#[cfg(feature = "_internal_cli")]
72pub use config::InternalOptionalConfig;
73
74pub(crate) fn page_size(mem: &MemoryType) -> u32 {
75 const DEFAULT_WASM_PAGE_SIZE_LOG2: u32 = 16;
76 1 << mem.page_size_log2.unwrap_or(DEFAULT_WASM_PAGE_SIZE_LOG2)
77}
78
79pub(crate) fn arbitrary_loop<'a>(
83 u: &mut Unstructured<'a>,
84 min: usize,
85 max: usize,
86 mut f: impl FnMut(&mut Unstructured<'a>) -> Result<bool>,
87) -> Result<()> {
88 assert!(max >= min);
89 for _ in 0..min {
90 if !f(u)? {
91 return Err(arbitrary::Error::IncorrectFormat);
92 }
93 }
94 for _ in 0..(max - min) {
95 let keep_going = u.arbitrary().unwrap_or(false);
96 if !keep_going {
97 break;
98 }
99
100 if !f(u)? {
101 break;
102 }
103 }
104
105 Ok(())
106}
107
108pub(crate) fn limited_str<'a>(max_size: usize, u: &mut Unstructured<'a>) -> Result<&'a str> {
110 let size = u.arbitrary_len::<u8>()?;
111 let size = std::cmp::min(size, max_size);
112 match str::from_utf8(u.peek_bytes(size).unwrap()) {
113 Ok(s) => {
114 u.bytes(size).unwrap();
115 Ok(s)
116 }
117 Err(e) => {
118 let i = e.valid_up_to();
119 let valid = u.bytes(i).unwrap();
120 let s = str::from_utf8(valid).unwrap();
121 Ok(s)
122 }
123 }
124}
125
126pub(crate) fn limited_string(max_size: usize, u: &mut Unstructured) -> Result<String> {
127 Ok(limited_str(max_size, u)?.into())
128}
129
130pub(crate) fn unique_string(
131 max_size: usize,
132 names: &mut HashSet<String>,
133 u: &mut Unstructured,
134) -> Result<String> {
135 let mut name = limited_string(max_size, u)?;
136 while names.contains(&name) {
137 write!(&mut name, "{}", names.len()).unwrap();
138 }
139 names.insert(name.clone());
140 Ok(name)
141}
142
143#[cfg(feature = "component-model")]
144pub(crate) fn unique_kebab_string(
145 max_size: usize,
146 names: &mut HashSet<String>,
147 u: &mut Unstructured,
148) -> Result<String> {
149 let size = std::cmp::min(u.arbitrary_len::<u8>()?, max_size);
150 let mut name = String::with_capacity(size);
151 let mut require_alpha = true;
152 for _ in 0..size {
153 name.push(match u.int_in_range::<u8>(0..=36)? {
154 x if (0..26).contains(&x) => {
155 require_alpha = false;
156 (b'a' + x) as char
157 }
158 x if (26..36).contains(&x) => {
159 if require_alpha {
160 require_alpha = false;
161 (b'a' + (x - 26)) as char
162 } else {
163 (b'0' + (x - 26)) as char
164 }
165 }
166 x if x == 36 => {
167 if require_alpha {
168 require_alpha = false;
169 'a'
170 } else {
171 require_alpha = true;
172 '-'
173 }
174 }
175 _ => unreachable!(),
176 });
177 }
178
179 if name.is_empty() || name.ends_with('-') {
180 name.push('a');
181 }
182
183 while names.contains(&name) {
184 write!(&mut name, "{}", names.len()).unwrap();
185 }
186
187 names.insert(name.clone());
188
189 Ok(name)
190}
191
192#[cfg(feature = "component-model")]
193pub(crate) fn unique_url(
194 max_size: usize,
195 names: &mut HashSet<String>,
196 u: &mut Unstructured,
197) -> Result<String> {
198 let path = unique_kebab_string(max_size, names, u)?;
199 Ok(format!("https://example.com/{path}"))
200}