#![allow(dead_code)]
pub struct ByteCursor<'a> {
input: &'a [u8],
pos: usize,
}
impl<'a> ByteCursor<'a> {
pub fn new(input: &'a [u8]) -> Self {
Self { input, pos: 0 }
}
pub fn next(&mut self) -> u8 {
let byte = if self.input.is_empty() {
(self.pos as u8).wrapping_mul(29).wrapping_add(7)
} else {
self.input[self.pos % self.input.len()]
.wrapping_add(((self.pos / self.input.len()) as u8).wrapping_mul(19))
};
self.pos += 1;
byte
}
pub fn bool(&mut self) -> bool {
self.next() & 1 == 1
}
pub fn usize(&mut self) -> usize {
let mut value = 0usize;
for shift in (0..usize::BITS as usize).step_by(8) {
value |= (self.next() as usize) << shift;
}
value
}
pub fn len(&mut self, max: usize, boundaries: &[usize]) -> usize {
if self.next().is_multiple_of(4) {
boundaries[(self.next() as usize) % boundaries.len()].min(max)
} else {
self.usize() % (max + 1)
}
}
pub fn bytes(&mut self, len: usize) -> Vec<u8> {
(0..len).map(|_| self.byte()).collect()
}
pub fn byte(&mut self) -> u8 {
let byte = self.next();
match byte % 16 {
0 => b'a',
1 => b' ',
2 => b'/',
3 => b'.',
4 => b',',
5 => b'_',
6 => b'-',
7 => b':',
8..=10 => b'a' + (byte % 26),
11..=13 => b'A' + (byte % 26),
_ => b'0' + (byte % 10),
}
}
pub fn string(&mut self, len: usize) -> String {
(0..len).map(|_| self.char()).collect()
}
pub fn char(&mut self) -> char {
let byte = self.next();
if byte & 0x0f == 0 {
return match (byte >> 4) & 3 {
0 => 'é',
1 => 'ن',
2 => '다',
_ => '😀',
};
}
match byte % 18 {
0 => 'a',
1 => ' ',
2 => '/',
3 => '.',
4 => ',',
5 => '_',
6 => '-',
7 => ':',
8..=11 => (b'a' + (byte % 26)) as char,
12..=15 => (b'A' + (byte % 26)) as char,
_ => (b'0' + (byte % 10)) as char,
}
}
}
pub fn test_bound(max: usize, miri_max: usize) -> usize {
if cfg!(miri) { max.min(miri_max) } else { max }
}
pub fn test_iterations(default: usize) -> usize {
if cfg!(miri) { default.min(4) } else { default }
}
pub fn run_generated_inputs<F>(iterations: usize, max_len: usize, check_input: F)
where
F: Fn(&[u8]),
{
use proptest::prelude::*;
use proptest::test_runner::{Config as ProptestConfig, TestCaseError, TestRunner};
use std::panic::{AssertUnwindSafe, catch_unwind};
let strategy = prop::collection::vec(any::<u8>(), 0..=max_len);
let mut runner = TestRunner::new(ProptestConfig {
cases: test_iterations(iterations) as u32,
..ProptestConfig::default()
});
runner
.run(&strategy, |input| {
catch_unwind(AssertUnwindSafe(|| check_input(&input)))
.map_err(|payload| TestCaseError::fail(panic_payload_to_string(payload)))?;
Ok(())
})
.unwrap();
}
fn panic_payload_to_string(payload: Box<dyn std::any::Any + Send>) -> String {
if let Some(message) = payload.downcast_ref::<String>() {
message.clone()
} else if let Some(message) = payload.downcast_ref::<&'static str>() {
(*message).to_owned()
} else {
"property assertion panicked".to_owned()
}
}