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