Fix mutator slowdown (#1138)

* perf stat

* fix except swap

* swap

* fix

* reveral-based byte swap

* Revert "reveral-based byte swap"

This reverts commit 2bc9609ece47fd4e8f6d96862f8ad3fb77f11aec.

* no introspection

* clp fmt

* change rand_range to have at least 1 length

* don't use modulo

---------

Co-authored-by: Addison Crump <addison.crump@cispa.de>
Co-authored-by: Dominik Maier <domenukk@gmail.com>
This commit is contained in:
Dongjia "toka" Zhang 2023-03-16 23:58:03 +09:00 committed by GitHub
parent 3c331e5a9b
commit 08fe6ab791

View File

@ -63,10 +63,13 @@ pub fn buffer_set<T: Clone>(data: &mut [T], from: usize, len: usize, val: T) {
/// This problem corresponds to: <https://oeis.org/A059036>
#[inline]
pub fn rand_range<S: HasRand>(state: &mut S, upper: usize, max_len: usize) -> Range<usize> {
let len = state.rand_mut().next() as usize % max_len + 1;
let offset2 = state.rand_mut().next() as usize % (upper + len);
let len = 1 + state.rand_mut().below(max_len as u64) as usize;
// sample from [1..upper + len]
let mut offset2 = 1 + state.rand_mut().below((upper + len - 1) as u64) as usize;
let offset1 = offset2.saturating_sub(len);
let offset2 = offset2.clamp(0, upper);
if offset2 > upper {
offset2 = upper;
}
offset1..offset2
}
@ -490,9 +493,6 @@ where
}
let range = rand_range(state, size, size);
if range.is_empty() {
return Ok(MutationResult::Skipped);
}
input.bytes_mut().drain(range);
@ -535,17 +535,15 @@ where
return Ok(MutationResult::Skipped);
}
let mut range = rand_range(state, size, min(max_size - size, 16));
if range.is_empty() {
return Ok(MutationResult::Skipped);
}
let new_size = range.len() + size;
let range = rand_range(state, size, min(16, max_size - size));
let mut target = size;
core::mem::swap(&mut target, &mut range.end);
input.bytes_mut().resize(new_size, 0);
input.bytes_mut().copy_within(range, target);
input.bytes_mut().resize(size + range.len(), 0);
buffer_self_copy(
input.bytes_mut(),
range.start,
range.start + range.len(),
size - range.start,
);
Ok(MutationResult::Mutated)
}
@ -586,14 +584,22 @@ where
return Ok(MutationResult::Skipped);
}
let amount = 1 + state.rand_mut().below(min(max_size - size, 16) as u64) as usize;
let mut amount = 1 + state.rand_mut().below(16) as usize;
let offset = state.rand_mut().below(size as u64 + 1) as usize;
if size + amount > max_size {
if max_size > size {
amount = max_size - size;
} else {
return Ok(MutationResult::Skipped);
}
}
let val = input.bytes()[state.rand_mut().below(size as u64) as usize];
input
.bytes_mut()
.splice(offset..offset, core::iter::repeat(val).take(amount));
input.bytes_mut().resize(size + amount, 0);
buffer_self_copy(input.bytes_mut(), offset, offset + amount, size - offset);
buffer_set(input.bytes_mut(), offset, amount, val);
Ok(MutationResult::Mutated)
}
@ -634,14 +640,22 @@ where
return Ok(MutationResult::Skipped);
}
let amount = 1 + state.rand_mut().below(min(max_size - size, 16) as u64) as usize;
let mut amount = 1 + state.rand_mut().below(16) as usize;
let offset = state.rand_mut().below(size as u64 + 1) as usize;
if size + amount > max_size {
if max_size > size {
amount = max_size - size;
} else {
return Ok(MutationResult::Skipped);
}
}
let val = state.rand_mut().next() as u8;
input
.bytes_mut()
.splice(offset..offset, core::iter::repeat(val).take(amount));
input.bytes_mut().resize(size + amount, 0);
buffer_self_copy(input.bytes_mut(), offset, offset + amount, size - offset);
buffer_set(input.bytes_mut(), offset, amount, val);
Ok(MutationResult::Mutated)
}
@ -681,15 +695,10 @@ where
return Ok(MutationResult::Skipped);
}
let range = rand_range(state, size, min(size, 16));
if range.is_empty() {
return Ok(MutationResult::Skipped);
}
let val = *state.rand_mut().choose(input.bytes());
let quantity = range.len();
input
.bytes_mut()
.splice(range, core::iter::repeat(val).take(quantity));
buffer_set(input.bytes_mut(), range.start, quantity, val);
Ok(MutationResult::Mutated)
}
@ -729,15 +738,10 @@ where
return Ok(MutationResult::Skipped);
}
let range = rand_range(state, size, min(size, 16));
if range.is_empty() {
return Ok(MutationResult::Skipped);
}
let val = state.rand_mut().next() as u8;
let quantity = range.len();
input
.bytes_mut()
.splice(range, core::iter::repeat(val).take(quantity));
buffer_set(input.bytes_mut(), range.start, quantity, val);
Ok(MutationResult::Mutated)
}
@ -780,7 +784,7 @@ where
let target = state.rand_mut().below(size as u64) as usize;
let range = rand_range(state, size, size - target);
input.bytes_mut().copy_within(range, target);
buffer_self_copy(input.bytes_mut(), range.start, target, range.len());
Ok(MutationResult::Mutated)
}
@ -818,22 +822,32 @@ where
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let size = input.bytes().len();
if size <= 1 || size == state.max_size() {
if size <= 1 || size >= state.max_size() {
return Ok(MutationResult::Skipped);
}
let target = state.rand_mut().below(size as u64) as usize;
// make sure that the sampled range is both in bounds and of an acceptable size
let max_insert_len = min(size - target, state.max_size() - size);
let range = rand_range(state, size, max_insert_len);
let range = rand_range(state, size, min(16, max_insert_len));
self.tmp_buf.clear();
self.tmp_buf.extend(input.bytes()[range].iter().copied());
input
.bytes_mut()
.splice(target..target, self.tmp_buf.drain(..));
input.bytes_mut().resize(size + range.len(), 0);
self.tmp_buf.resize(range.len(), 0);
buffer_copy(
&mut self.tmp_buf,
input.bytes(),
range.start,
0,
range.len(),
);
buffer_self_copy(
input.bytes_mut(),
target,
target + range.len(),
size - target,
);
buffer_copy(input.bytes_mut(), &self.tmp_buf, 0, target, range.len());
Ok(MutationResult::Mutated)
}
}
@ -858,6 +872,7 @@ pub struct BytesSwapMutator {
tmp_buf: Vec<u8>,
}
#[allow(clippy::too_many_lines)]
impl<I, S> Mutator<I, S> for BytesSwapMutator
where
S: HasRand,
@ -874,31 +889,166 @@ where
return Ok(MutationResult::Skipped);
}
self.tmp_buf.clear();
let first = rand_range(state, size, size);
if state.rand_mut().next() & 1 == 0 && first.start != 0 {
// The second range comes before first.
let second = rand_range(state, first.start, first.start);
self.tmp_buf.extend(input.bytes_mut().drain(first.clone()));
self.tmp_buf
.extend(input.bytes()[second.clone()].iter().copied());
input
.bytes_mut()
.splice(first.start..first.start, self.tmp_buf.drain(first.len()..));
input.bytes_mut().splice(second, self.tmp_buf.drain(..));
self.tmp_buf.resize(first.len(), 0);
// If range first is larger
if first.len() >= second.len() {
let diff_in_size = first.len() - second.len();
// copy first range to tmp
buffer_copy(
&mut self.tmp_buf,
input.bytes(),
first.start,
0,
first.len(),
);
// adjust second.end..first.start, move them by diff_in_size to the right
buffer_self_copy(
input.bytes_mut(),
second.end,
second.end + diff_in_size,
first.start - second.end,
);
// copy second to where first was
buffer_self_copy(
input.bytes_mut(),
second.start,
first.start + diff_in_size,
second.len(),
);
// copy first back
buffer_copy(
input.bytes_mut(),
&self.tmp_buf,
0,
second.start,
first.len(),
);
} else {
let diff_in_size = second.len() - first.len();
// copy first range to tmp
buffer_copy(
&mut self.tmp_buf,
input.bytes(),
first.start,
0,
first.len(),
);
// adjust second.end..first.start, move them by diff_in_size to the left
buffer_self_copy(
input.bytes_mut(),
second.end,
second.end - diff_in_size,
first.start - second.end,
);
// copy second to where first was
buffer_self_copy(
input.bytes_mut(),
second.start,
first.start - diff_in_size,
second.len(),
);
// copy first back
buffer_copy(
input.bytes_mut(),
&self.tmp_buf,
0,
second.start,
first.len(),
);
}
Ok(MutationResult::Mutated)
} else if first.end != size {
// The first range comes before the second range
let mut second = rand_range(state, size - first.end, size - first.end);
second.start += first.end;
second.end += first.end;
self.tmp_buf.extend(input.bytes_mut().drain(second.clone()));
self.tmp_buf
.extend(input.bytes()[first.clone()].iter().copied());
input.bytes_mut().splice(
second.start..second.start,
self.tmp_buf.drain(second.len()..),
);
input.bytes_mut().splice(first, self.tmp_buf.drain(..));
self.tmp_buf.resize(second.len(), 0);
if second.len() >= first.len() {
let diff_in_size = second.len() - first.len();
// copy second range to tmp
buffer_copy(
&mut self.tmp_buf,
input.bytes(),
second.start,
0,
second.len(),
);
// adjust first.end..second.start, move them by diff_in_size to the right
buffer_self_copy(
input.bytes_mut(),
first.end,
first.end + diff_in_size,
second.start - first.end,
);
// copy first to where second was
buffer_self_copy(
input.bytes_mut(),
first.start,
second.start + diff_in_size,
first.len(),
);
// copy second back
buffer_copy(
input.bytes_mut(),
&self.tmp_buf,
0,
first.start,
second.len(),
);
} else {
let diff_in_size = first.len() - second.len();
// copy second range to tmp
buffer_copy(
&mut self.tmp_buf,
input.bytes(),
second.start,
0,
second.len(),
);
// adjust first.end..second.start, move them by diff_in_size to the left
buffer_self_copy(
input.bytes_mut(),
first.end,
first.end - diff_in_size,
second.start - first.end,
);
// copy first to where second was
buffer_self_copy(
input.bytes_mut(),
first.start,
second.start - diff_in_size,
first.len(),
);
// copy second back
buffer_copy(
input.bytes_mut(),
&self.tmp_buf,
0,
first.start,
second.len(),
);
}
Ok(MutationResult::Mutated)
} else {
Ok(MutationResult::Skipped)
@ -967,10 +1117,20 @@ where
let mut other_testcase = state.corpus().get(idx)?.borrow_mut();
let other = other_testcase.load_input()?;
input
.bytes_mut()
.splice(target..target, other.bytes()[range].iter().copied());
input.bytes_mut().resize(size + range.len(), 0);
buffer_self_copy(
input.bytes_mut(),
target,
target + range.len(),
size - target,
);
buffer_copy(
input.bytes_mut(),
other.bytes(),
range.start,
target,
range.len(),
);
Ok(MutationResult::Mutated)
}
}
@ -1034,11 +1194,13 @@ where
let mut other_testcase = state.corpus().get(idx)?.borrow_mut();
let other = other_testcase.load_input()?;
input.bytes_mut().splice(
target..(target + range.len()),
other.bytes()[range].iter().copied(),
buffer_copy(
input.bytes_mut(),
other.bytes(),
range.start,
target,
range.len(),
);
Ok(MutationResult::Mutated)
}
}