use crate::DefaultMutator;
use fuzzcheck_traits::Mutator;
fn binary_search_arbitrary(low: u8, high: u8, step: u64) -> u8 {
let next = low.wrapping_add(high.wrapping_sub(low) / 2);
if low.wrapping_add(1) == high {
if step % 2 == 0 {
high
} else {
low
}
} else if step == 0 {
next
} else if step % 2 == 1 {
binary_search_arbitrary(next.wrapping_add(1), high, step / 2)
} else {
binary_search_arbitrary(low, next.wrapping_sub(1), (step - 1) / 2)
}
}
macro_rules! impl_unsigned_mutator {
($name:ty,$name_mutator:ident,$rand:path,$size:expr) => {
pub struct $name_mutator {
shuffled_integers: [u8; 256],
rng: fastrand::Rng,
}
impl Default for $name_mutator {
fn default() -> Self {
let mut shuffled_integers = [0; 256];
for (i, x) in shuffled_integers.iter_mut().enumerate() {
*x = binary_search_arbitrary(0, u8::MAX, i as u64);
}
$name_mutator {
shuffled_integers,
rng: fastrand::Rng::default(),
}
}
}
impl $name_mutator {
fn uniform_permutation(&self, step: u64) -> $name {
let size = $size as u64;
const GRANULARITY: u64 =
((std::mem::size_of::<usize>() * 8) - (256u64.leading_zeros() as usize) - 1) as u64;
const STEP_MASK: u64 = ((u8::MAX as usize) >> (8 - GRANULARITY)) as u64;
let step_i = (step & STEP_MASK) as usize;
let mut prev = unsafe { *self.shuffled_integers.get_unchecked(step_i) as $name };
let mut result = (prev << (size - GRANULARITY)) as $name;
for i in 1..(size / GRANULARITY) {
let step_i = (((step >> (i * GRANULARITY)) ^ prev as u64) & STEP_MASK) as usize;
prev = unsafe { *self.shuffled_integers.get_unchecked(step_i) as $name };
result |= prev << (size - (i + 1) * GRANULARITY);
}
result
}
}
impl Mutator<$name> for $name_mutator {
type Cache = ();
type MutationStep = u64; type ArbitraryStep = u64;
type UnmutateToken = $name;
fn cache_from_value(&self, _value: &$name) -> Self::Cache {}
fn initial_step_from_value(&self, _value: &$name) -> Self::MutationStep {
0
}
fn max_complexity(&self) -> f64 {
std::mem::size_of::<$name>() as f64 * 8.0
}
fn min_complexity(&self) -> f64 {
std::mem::size_of::<$name>() as f64 * 8.0
}
fn complexity(&self, _value: &$name, _cache: &Self::Cache) -> f64 {
std::mem::size_of::<$name>() as f64 * 8.0
}
fn ordered_arbitrary(&self, step: &mut Self::ArbitraryStep, max_cplx: f64) -> Option<($name, Self::Cache)> {
if max_cplx < self.min_complexity() {
return None;
}
if *step > <$name>::MAX as u64 {
None
} else {
let value = self.uniform_permutation(*step);
*step += 1;
Some((value, ()))
}
}
fn random_arbitrary(&self, _max_cplx: f64) -> ($name, Self::Cache) {
let value = self.uniform_permutation(self.rng.u64(..));
(value, ())
}
fn ordered_mutate(
&self,
value: &mut $name,
_cache: &mut Self::Cache,
step: &mut Self::MutationStep,
max_cplx: f64,
) -> Option<Self::UnmutateToken> {
if max_cplx < self.min_complexity() {
return None;
}
if *step > 10u64.saturating_add(<$name>::MAX as u64) {
return None;
}
let token = *value;
*value = {
let mut tmp_step = *step;
if tmp_step < 8 {
let nudge = (tmp_step + 2) as $name;
if nudge % 2 == 0 {
value.wrapping_add(nudge / 2)
} else {
value.wrapping_sub(nudge / 2)
}
} else {
tmp_step -= 7;
self.uniform_permutation(tmp_step)
}
};
*step = step.wrapping_add(1);
Some(token)
}
fn random_mutate(
&self,
value: &mut $name,
_cache: &mut Self::Cache,
_max_cplx: f64,
) -> Self::UnmutateToken {
std::mem::replace(value, $rand(..))
}
fn unmutate(&self, value: &mut $name, _cache: &mut Self::Cache, t: Self::UnmutateToken) {
*value = t;
}
}
impl DefaultMutator for $name {
type Mutator = $name_mutator;
fn default_mutator() -> Self::Mutator {
<$name_mutator>::default()
}
}
};
}
impl_unsigned_mutator!(u8, U8Mutator, fastrand::u8, 8);
impl_unsigned_mutator!(u16, U16Mutator, fastrand::u16, 16);
impl_unsigned_mutator!(u32, U32Mutator, fastrand::u32, 32);
impl_unsigned_mutator!(u64, U64Mutator, fastrand::u64, 64);
macro_rules! impl_signed_mutator {
($name:ty,$name_unsigned:ty,$name_mutator:ident,$rand:path,$size:expr) => {
pub struct $name_mutator {
shuffled_integers: [u8; 256],
rng: fastrand::Rng,
}
impl Default for $name_mutator {
fn default() -> Self {
let mut shuffled_integers = [0; 256];
for (i, x) in shuffled_integers.iter_mut().enumerate() {
*x = binary_search_arbitrary(0, u8::MAX, i as u64);
}
$name_mutator {
shuffled_integers,
rng: fastrand::Rng::default(),
}
}
}
impl $name_mutator {
fn uniform_permutation(&self, step: u64) -> $name_unsigned {
let size = $size as u64;
const GRANULARITY: u64 =
((std::mem::size_of::<usize>() * 8) - (256_u64.leading_zeros() as usize) - 1) as u64;
const STEP_MASK: u64 = ((u8::MAX as usize) >> (8 - GRANULARITY)) as u64;
let step_i = (step & STEP_MASK) as usize;
let mut prev = unsafe { *self.shuffled_integers.get_unchecked(step_i) as $name_unsigned };
let mut result = (prev << (size - GRANULARITY)) as $name_unsigned;
for i in 1..(size / GRANULARITY) {
let step_i = (((step >> (i * GRANULARITY)) ^ prev as u64) & STEP_MASK) as usize;
prev = unsafe { *self.shuffled_integers.get_unchecked(step_i) as $name_unsigned };
result |= prev << (size - (i + 1) * GRANULARITY);
}
result as $name_unsigned
}
}
impl Mutator<$name> for $name_mutator {
type Cache = ();
type MutationStep = u64; type ArbitraryStep = u64;
type UnmutateToken = $name;
fn cache_from_value(&self, _value: &$name) -> Self::Cache {}
fn initial_step_from_value(&self, _value: &$name) -> Self::MutationStep {
0
}
fn max_complexity(&self) -> f64 {
std::mem::size_of::<$name>() as f64 * 8.0
}
fn min_complexity(&self) -> f64 {
std::mem::size_of::<$name>() as f64 * 8.0
}
fn complexity(&self, _value: &$name, _cache: &Self::Cache) -> f64 {
std::mem::size_of::<$name>() as f64 * 8.0
}
fn ordered_arbitrary(
&self,
step: &mut Self::ArbitraryStep,
_max_cplx: f64,
) -> Option<($name, Self::Cache)> {
if *step > <$name_unsigned>::MAX as u64 {
None
} else {
let value = self.uniform_permutation(*step) as $name;
*step += 1;
Some((value, ()))
}
}
fn random_arbitrary(&self, _max_cplx: f64) -> ($name, Self::Cache) {
let value = self.uniform_permutation(self.rng.u64(..)) as $name;
(value, ())
}
fn ordered_mutate(
&self,
value: &mut $name,
_cache: &mut Self::Cache,
step: &mut Self::MutationStep,
_max_cplx: f64,
) -> Option<Self::UnmutateToken> {
let token = *value;
*value = {
let mut tmp_step = *step;
if tmp_step < 8 {
let nudge = (tmp_step + 2) as $name;
if nudge % 2 == 0 {
value.wrapping_add(nudge / 2)
} else {
value.wrapping_sub(nudge / 2)
}
} else {
tmp_step -= 7;
self.uniform_permutation(tmp_step) as $name
}
};
*step = step.wrapping_add(1);
Some(token)
}
fn random_mutate(
&self,
value: &mut $name,
_cache: &mut Self::Cache,
_max_cplx: f64,
) -> Self::UnmutateToken {
std::mem::replace(value, $rand(..))
}
fn unmutate(&self, value: &mut $name, _cache: &mut Self::Cache, t: Self::UnmutateToken) {
*value = t
}
}
};
}
impl_signed_mutator!(i8, u8, I8Mutator, fastrand::i8, 8);
impl_signed_mutator!(i16, u16, I16Mutator, fastrand::i16, 16);
impl_signed_mutator!(i32, u32, I32Mutator, fastrand::i32, 32);
impl_signed_mutator!(i64, u64, I64Mutator, fastrand::i64, 64);