Skip to content

Commit

Permalink
feat(bolero-generator): implement `bytes::Driver<T> where T: AsRef<[u…
Browse files Browse the repository at this point in the history
…8]>` (#260)
  • Loading branch information
camshaft authored Nov 26, 2024
1 parent 04cb435 commit 665f1c7
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 33 deletions.
9 changes: 2 additions & 7 deletions lib/bolero-generator/src/alloc/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,6 @@ fn string_type_test() {

#[test]
fn string_with_test() {
for _ in 0..100 {
let results = generator_test!(gen::<String>().with().len(32usize));
assert!(results.into_iter().any(|s| s.chars().count() == 32));
return;
}

panic!("failed to generate a valid string");
let results = generator_test!(gen::<String>().with().len(32usize));
assert!(results.into_iter().any(|s| s.chars().count() == 32));
}
2 changes: 1 addition & 1 deletion lib/bolero-generator/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use rand_core::RngCore;
#[macro_use]
mod macros;

mod bytes;
pub mod bytes;
#[cfg(feature = "alloc")]
pub mod cache;
#[cfg(feature = "alloc")]
Expand Down
114 changes: 100 additions & 14 deletions lib/bolero-generator/src/driver/bytes.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,74 @@
use super::*;

#[derive(Debug)]
pub struct ByteSliceDriver<'a> {
input: &'a [u8],
pub struct Driver<I> {
input: I,
depth: usize,
max_depth: usize,
len: usize,
cursor: usize,
}

impl<'a> ByteSliceDriver<'a> {
pub fn new(input: &'a [u8], options: &Options) -> Self {
impl<I> Driver<I>
where
I: AsRef<[u8]>,
{
pub fn new(input: I, options: &Options) -> Self {
let max_depth = options.max_depth_or_default();
let len = options.max_len_or_default().min(input.len());
let input = &input[..len];
let len = options.max_len_or_default().min(input.as_ref().len());

Self {
input,
depth: 0,
max_depth,
len,
cursor: 0,
}
}

pub fn reset(&mut self, input: I, options: &Options) -> I {
let max_depth = options.max_depth_or_default();
let len = options.max_len_or_default().min(input.as_ref().len());

let prev = core::mem::replace(&mut self.input, input);
self.depth = 0;
self.max_depth = max_depth;
self.cursor = 0;
self.len = len;

prev
}

#[inline]
pub fn as_slice(&self) -> &[u8] {
&self.input.as_ref()[self.cursor..self.len]
}

#[inline]
pub fn into_inner(self) -> I {
self.input
}
}

impl<'a> FillBytes for ByteSliceDriver<'a> {
impl<I> FillBytes for Driver<I>
where
I: AsRef<[u8]>,
{
#[inline]
fn peek_bytes(&mut self, offset: usize, bytes: &mut [u8]) -> Option<()> {
match self.input.len().checked_sub(offset) {
let slice = self.as_slice();
match slice.len().checked_sub(offset) {
None | Some(0) => {
// no bytes left so fill in zeros
bytes.fill(0);
}
Some(remaining_len) if remaining_len >= bytes.len() => {
let input = &self.input[offset..];
let input = &slice[offset..];
let input = &input[..bytes.len()];
bytes.copy_from_slice(input);
}
Some(remaining_len) => {
let input = &self.input[offset..];
let input = &slice[offset..];
// we don't have enough bytes to fill the whole output
let (head, tail) = bytes.split_at_mut(remaining_len);
head.copy_from_slice(input);
Expand All @@ -53,12 +81,16 @@ impl<'a> FillBytes for ByteSliceDriver<'a> {

#[inline]
fn consume_bytes(&mut self, consumed: usize) {
let consumed = consumed.min(self.input.len());
self.input = &self.input[consumed..];
let remaining = self.len - self.cursor;
let consumed = consumed.min(remaining);
self.cursor += consumed;
}
}

impl<'a> Driver for ByteSliceDriver<'a> {
impl<I> super::Driver for Driver<I>
where
I: AsRef<[u8]>,
{
gen_from_bytes!();

#[inline]
Expand All @@ -67,7 +99,7 @@ impl<'a> Driver for ByteSliceDriver<'a> {
Hint: FnOnce() -> (usize, Option<usize>),
Gen: FnMut(&[u8]) -> Option<(usize, T)>,
{
let slice = self.input;
let slice = self.as_slice();
let (len, value) = gen(slice)?;
self.consume_bytes(len);
Some(value)
Expand All @@ -88,3 +120,57 @@ impl<'a> Driver for ByteSliceDriver<'a> {
self.max_depth
}
}

#[derive(Debug)]
pub struct ByteSliceDriver<'a>(Driver<&'a [u8]>);

impl<'a> ByteSliceDriver<'a> {
pub fn new(input: &'a [u8], options: &Options) -> Self {
Self(Driver::new(input, options))
}

#[inline]
pub fn as_slice(&self) -> &[u8] {
self.0.as_slice()
}
}

impl<'a> FillBytes for ByteSliceDriver<'a> {
#[inline]
fn peek_bytes(&mut self, offset: usize, bytes: &mut [u8]) -> Option<()> {
self.0.peek_bytes(offset, bytes)
}

#[inline]
fn consume_bytes(&mut self, consumed: usize) {
self.0.consume_bytes(consumed)
}
}

impl<'a> super::Driver for ByteSliceDriver<'a> {
gen_from_bytes!();

#[inline]
fn gen_from_bytes<Hint, Gen, T>(&mut self, hint: Hint, gen: Gen) -> Option<T>
where
Hint: FnOnce() -> (usize, Option<usize>),
Gen: FnMut(&[u8]) -> Option<(usize, T)>,
{
self.0.gen_from_bytes(hint, gen)
}

#[inline]
fn depth(&self) -> usize {
self.0.depth
}

#[inline]
fn set_depth(&mut self, depth: usize) {
self.0.depth = depth;
}

#[inline]
fn max_depth(&self) -> usize {
self.0.max_depth
}
}
23 changes: 13 additions & 10 deletions lib/bolero-generator/src/driver/exhaustive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl Driver {
}

pub fn replay(&mut self) {
self.state.position = 0;
self.state.cursor = 0;
}
}

Expand All @@ -61,7 +61,7 @@ pub struct Frame {
struct State {
started: bool,
stack: Vec<Frame>,
position: usize,
cursor: usize,
estimate: f64,
}

Expand All @@ -74,7 +74,7 @@ impl State {
self.stack.clear();
self.stack
.extend(state.iter().map(|&v| Frame { value: v, bound: v }));
self.position = 0;
self.cursor = 0;
self.started = !state.is_empty();
self.estimate = 1.0;
}
Expand All @@ -95,7 +95,7 @@ impl State {
if self.stack[i].value < self.stack[i].bound {
self.stack[i].value += 1;
self.stack.truncate(i + 1);
self.position = 0;
self.cursor = 0;
return ControlFlow::Continue(());
}
}
Expand All @@ -111,17 +111,20 @@ impl State {
return 0;
}

if self.position == self.stack.len() {
while self.cursor >= self.stack.len() {
if self.cursor == self.stack.len() {
self.estimate += bound as f64;
}

self.stack.push(Default::default());
self.estimate += bound as f64;
}

let frame = &mut self.stack[self.position];
let frame = &mut self.stack[self.cursor];

self.position += 1;
self.cursor += 1;

frame.bound = bound;
frame.value
frame.bound = frame.bound.max(bound);
frame.value.min(bound)
}

#[inline]
Expand Down
2 changes: 1 addition & 1 deletion lib/bolero-generator/src/driver/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ macro_rules! gen_from_bytes {

#[inline(always)]
fn gen_variant(&mut self, variants: usize, base_case: usize) -> Option<usize> {
if self.depth == self.max_depth {
if self.depth() == self.max_depth() {
return Some(base_case);
}

Expand Down

0 comments on commit 665f1c7

Please sign in to comment.