Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] feat: introduce ArbitraryInRange #192

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
feat: introduce ArbitraryInRange
  • Loading branch information
sivizius committed Sep 21, 2024
commit d97bd18450579c3e194a339f388f0fc75160e57c
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -11,6 +11,8 @@ pub enum Error {
NotEnoughData,
/// The input bytes were not of the right format
IncorrectFormat,
/// Cannot generate value in given range
InvalidRange,
}

impl fmt::Display for Error {
@@ -28,6 +30,7 @@ impl fmt::Display for Error {
f,
"The raw data is not of the correct format to construct this type"
),
Error::InvalidRange => write!(f, "Cannot generate a value in given range"),
}
}
}
40 changes: 3 additions & 37 deletions src/foreign/alloc/boxed.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,10 @@
use {
crate::{size_hint, Arbitrary, Result, Unstructured},
crate::{Arbitrary, Result, Unstructured},
std::boxed::Box,
};

impl<'a, A> Arbitrary<'a> for Box<A>
where
A: Arbitrary<'a>,
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
Arbitrary::arbitrary(u).map(Self::new)
}

#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
Self::try_size_hint(depth).unwrap_or_default()
}

#[inline]
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
size_hint::try_recursion_guard(depth, <A as Arbitrary>::try_size_hint)
}
}

impl<'a, A> Arbitrary<'a> for Box<[A]>
where
A: Arbitrary<'a>,
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
u.arbitrary_iter()?.collect()
}

fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
u.arbitrary_take_rest_iter()?.collect()
}

#[inline]
fn size_hint(_depth: usize) -> (usize, Option<usize>) {
(0, None)
}
}
implement_wrapped_new! { Box! }
implement_from_iter! { Box<[A]> }

impl<'a> Arbitrary<'a> for Box<str> {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
23 changes: 2 additions & 21 deletions src/foreign/alloc/collections/binary_heap.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,3 @@
use {
crate::{Arbitrary, Result, Unstructured},
std::collections::binary_heap::BinaryHeap,
};
use std::collections::binary_heap::BinaryHeap;

impl<'a, A> Arbitrary<'a> for BinaryHeap<A>
where
A: Arbitrary<'a> + Ord,
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
u.arbitrary_iter()?.collect()
}

fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
u.arbitrary_take_rest_iter()?.collect()
}

#[inline]
fn size_hint(_depth: usize) -> (usize, Option<usize>) {
(0, None)
}
}
implement_from_iter! { BinaryHeap<A>: Ord }
23 changes: 2 additions & 21 deletions src/foreign/alloc/collections/btree_set.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,3 @@
use {
crate::{Arbitrary, Result, Unstructured},
std::collections::btree_set::BTreeSet,
};
use std::collections::btree_set::BTreeSet;

impl<'a, A> Arbitrary<'a> for BTreeSet<A>
where
A: Arbitrary<'a> + Ord,
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
u.arbitrary_iter()?.collect()
}

fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
u.arbitrary_take_rest_iter()?.collect()
}

#[inline]
fn size_hint(_depth: usize) -> (usize, Option<usize>) {
(0, None)
}
}
implement_from_iter! { BTreeSet<A>: Ord }
23 changes: 2 additions & 21 deletions src/foreign/alloc/collections/linked_list.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,3 @@
use {
crate::{Arbitrary, Result, Unstructured},
std::collections::linked_list::LinkedList,
};
use std::collections::linked_list::LinkedList;

impl<'a, A> Arbitrary<'a> for LinkedList<A>
where
A: Arbitrary<'a>,
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
u.arbitrary_iter()?.collect()
}

fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
u.arbitrary_take_rest_iter()?.collect()
}

#[inline]
fn size_hint(_depth: usize) -> (usize, Option<usize>) {
(0, None)
}
}
implement_from_iter! { LinkedList<A> }
23 changes: 2 additions & 21 deletions src/foreign/alloc/collections/vec_deque.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,3 @@
use {
crate::{Arbitrary, Result, Unstructured},
std::collections::vec_deque::VecDeque,
};
use std::collections::vec_deque::VecDeque;

impl<'a, A> Arbitrary<'a> for VecDeque<A>
where
A: Arbitrary<'a>,
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
u.arbitrary_iter()?.collect()
}

fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
u.arbitrary_take_rest_iter()?.collect()
}

#[inline]
fn size_hint(_depth: usize) -> (usize, Option<usize>) {
(0, None)
}
}
implement_from_iter! { VecDeque<A> }
40 changes: 3 additions & 37 deletions src/foreign/alloc/rc.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,10 @@
use {
crate::{size_hint, Arbitrary, Result, Unstructured},
crate::{Arbitrary, Result, Unstructured},
std::rc::Rc,
};

impl<'a, A> Arbitrary<'a> for Rc<A>
where
A: Arbitrary<'a>,
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
Arbitrary::arbitrary(u).map(Self::new)
}

#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
Self::try_size_hint(depth).unwrap_or_default()
}

#[inline]
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
size_hint::try_recursion_guard(depth, <A as Arbitrary>::try_size_hint)
}
}

impl<'a, A> Arbitrary<'a> for Rc<[A]>
where
A: Arbitrary<'a>,
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
u.arbitrary_iter()?.collect()
}

fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
u.arbitrary_take_rest_iter()?.collect()
}

#[inline]
fn size_hint(_depth: usize) -> (usize, Option<usize>) {
(0, None)
}
}
implement_wrapped_new! { Rc! }
implement_from_iter! { Rc<[A]> }

impl<'a> Arbitrary<'a> for Rc<str> {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
16 changes: 14 additions & 2 deletions src/foreign/alloc/string.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
use {
crate::{Arbitrary, Result, Unstructured},
std::string::String,
crate::{Arbitrary, ArbitraryInRange, Result, Unstructured},
std::{ops::RangeBounds, string::String},
};

impl<'a> ArbitraryInRange<'a> for String {
type Bound = char;

fn arbitrary_in_range<R>(u: &mut Unstructured<'a>, range: &R) -> Result<Self>
where
R: RangeBounds<Self::Bound>,
{
u.arbitrary_in_range_iter::<Self::Bound, _>(range)?
.collect()
}
}

impl<'a> Arbitrary<'a> for String {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
<&str as Arbitrary>::arbitrary(u).map(Into::into)
40 changes: 3 additions & 37 deletions src/foreign/alloc/sync.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,10 @@
use {
crate::{size_hint, Arbitrary, Result, Unstructured},
crate::{Arbitrary, Result, Unstructured},
std::sync::Arc,
};

impl<'a, A> Arbitrary<'a> for Arc<A>
where
A: Arbitrary<'a>,
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
Arbitrary::arbitrary(u).map(Self::new)
}

#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
Self::try_size_hint(depth).unwrap_or_default()
}

#[inline]
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
size_hint::try_recursion_guard(depth, <A as Arbitrary>::try_size_hint)
}
}

impl<'a, A> Arbitrary<'a> for Arc<[A]>
where
A: Arbitrary<'a>,
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
u.arbitrary_iter()?.collect()
}

fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
u.arbitrary_take_rest_iter()?.collect()
}

#[inline]
fn size_hint(_depth: usize) -> (usize, Option<usize>) {
(0, None)
}
}
implement_wrapped_new! { Arc! }
implement_from_iter! { Arc<[A]> }

impl<'a> Arbitrary<'a> for Arc<str> {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
23 changes: 2 additions & 21 deletions src/foreign/alloc/vec.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,3 @@
use {
crate::{Arbitrary, Result, Unstructured},
std::vec::Vec,
};
use std::vec::Vec;

impl<'a, A> Arbitrary<'a> for Vec<A>
where
A: Arbitrary<'a>,
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
u.arbitrary_iter()?.collect()
}

fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
u.arbitrary_take_rest_iter()?.collect()
}

#[inline]
fn size_hint(_depth: usize) -> (usize, Option<usize>) {
(0, None)
}
}
implement_from_iter! { Vec<A> }
35 changes: 31 additions & 4 deletions src/foreign/core/array.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use {
crate::{size_hint, Arbitrary, Result, Unstructured},
crate::{size_hint, Arbitrary, ArbitraryInRange, Result, Unstructured},
core::{
array,
mem::{self, MaybeUninit},
@@ -45,20 +45,47 @@ where
}
}

impl<'a, T, const N: usize> ArbitraryInRange<'a> for [T; N]
where
T: ArbitraryInRange<'a>,
{
type Bound = T::Bound;

#[inline]
fn arbitrary_in_range<R>(u: &mut Unstructured<'a>, range: &R) -> Result<Self>
where
R: std::ops::RangeBounds<Self::Bound>,
{
try_create_array(|_| T::arbitrary_in_range(u, range))
}

#[inline]
fn arbitrary_in_range_take_rest<R>(mut u: Unstructured<'a>, range: &R) -> Result<Self>
where
R: std::ops::RangeBounds<Self::Bound>,
{
let mut array = Self::arbitrary_in_range(&mut u, range)?;
if let Some(last) = array.last_mut() {
*last = T::arbitrary_in_range_take_rest(u, range)?;
}
Ok(array)
}
}

impl<'a, T, const N: usize> Arbitrary<'a> for [T; N]
where
T: Arbitrary<'a>,
{
#[inline]
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
try_create_array(|_| <T as Arbitrary<'a>>::arbitrary(u))
try_create_array(|_| T::arbitrary(u))
}

#[inline]
fn arbitrary_take_rest(mut u: Unstructured<'a>) -> Result<Self> {
let mut array = Self::arbitrary(&mut u)?;
if let Some(last) = array.last_mut() {
*last = Arbitrary::arbitrary_take_rest(u)?;
*last = T::arbitrary_take_rest(u)?;
}
Ok(array)
}
@@ -70,7 +97,7 @@ where

#[inline]
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
let hint = <T as Arbitrary>::try_size_hint(depth)?;
let hint = T::try_size_hint(depth)?;
Ok(size_hint::and_all(&array::from_fn::<_, N, _>(|_| hint)))
}
}
64 changes: 4 additions & 60 deletions src/foreign/core/cell.rs
Original file line number Diff line number Diff line change
@@ -1,61 +1,5 @@
use {
crate::{Arbitrary, MaxRecursionReached, Result, Unstructured},
core::cell::{Cell, RefCell, UnsafeCell},
};
use core::cell::{Cell, RefCell, UnsafeCell};

impl<'a, A> Arbitrary<'a> for Cell<A>
where
A: Arbitrary<'a>,
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
Arbitrary::arbitrary(u).map(Self::new)
}

#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
Self::try_size_hint(depth).unwrap_or_default()
}

#[inline]
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
<A as Arbitrary<'a>>::try_size_hint(depth)
}
}

impl<'a, A> Arbitrary<'a> for RefCell<A>
where
A: Arbitrary<'a>,
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
Arbitrary::arbitrary(u).map(Self::new)
}

#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
Self::try_size_hint(depth).unwrap_or_default()
}

#[inline]
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
<A as Arbitrary<'a>>::try_size_hint(depth)
}
}

impl<'a, A> Arbitrary<'a> for UnsafeCell<A>
where
A: Arbitrary<'a>,
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
Arbitrary::arbitrary(u).map(Self::new)
}

#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
Self::try_size_hint(depth).unwrap_or_default()
}

#[inline]
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
<A as Arbitrary<'a>>::try_size_hint(depth)
}
}
implement_wrapped_new! { Cell }
implement_wrapped_new! { RefCell }
implement_wrapped_new! { UnsafeCell }
82 changes: 68 additions & 14 deletions src/foreign/core/char.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,74 @@
use crate::{Arbitrary, Result, Unstructured};
use {
crate::{Arbitrary, ArbitraryInRange, Error, Result, Unstructured},
core::ops::{Bound, RangeBounds},
};

/// The lower limit of the surrogates block:
const SURROGATES_LOWER: u32 = 0xd800;

/// The lower limit of the surrogates block:
const SURROGATES_UPPER: u32 = 0xdfff;

fn map_char_bound(character: Bound<&char>) -> Bound<u32> {
match character {
Bound::Included(character) => Bound::Included(u32::from(*character)),
Bound::Excluded(character) => Bound::Excluded(u32::from(*character)),
Bound::Unbounded => Bound::Unbounded,
}
}

impl<'a> ArbitraryInRange<'a> for char {
type Bound = Self;

fn arbitrary_in_range<R>(u: &mut Unstructured<'a>, range: &R) -> Result<Self>
where
R: RangeBounds<Self::Bound>,
{
let minimum = map_char_bound(range.start_bound());
let maximum = map_char_bound(range.end_bound());
let range = (minimum, maximum);

// In the range of valid unicode characters (['\0', char::MAX]),
// there is a block called surrogates ([SURROGATES_LOWER, SURROGATES_UPPER]).
let character = match (
range.contains(&SURROGATES_LOWER),
range.contains(&SURROGATES_UPPER),
) {
// If SURROGATES_LOWER is not in range but SURROGATES_UPPER is, fix the lower bound:
(false, true) => {
u32::arbitrary_in_range(u, &(Bound::Excluded(SURROGATES_UPPER), maximum))
}

// If SURROGATES_LOWER is in range but SURROGATES_UPPER not, fix the upper bound:
(true, false) => {
u32::arbitrary_in_range(u, &(minimum, Bound::Excluded(SURROGATES_LOWER)))
}

// If the entire surrogate-block is inside range, choose one of:
// * minimum .. SURROGATES_LOWER
// * SURROGATES_UPPER .. maximum
(true, true) => bool::arbitrary(u).and_then(|coin_flip| {
let range = match coin_flip {
true => (Bound::Excluded(SURROGATES_UPPER), maximum),
false => (minimum, Bound::Excluded(SURROGATES_LOWER)),
};
u32::arbitrary_in_range(u, &range)
}),

// If neither the lower nor upper bound of the surrogate block is within the range,
// the range is either entirely below, above or within the surrogate block.
// In this case, either all values of the given range are valid characters or none.
_ => u32::arbitrary_in_range(u, &(minimum, maximum)),
};

// InvalidRange will be returned if range is entirely within the surrogate block.
char::from_u32(character?).ok_or(Error::InvalidRange)
}
}

impl<'a> Arbitrary<'a> for char {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
// The highest unicode code point is 0x11_FFFF
const CHAR_END: u32 = 0x11_0000;
// The size of the surrogate blocks
const SURROGATES_START: u32 = 0xD800;
let mut c = <u32 as Arbitrary<'a>>::arbitrary(u)? % CHAR_END;
if let Some(c) = char::from_u32(c) {
Ok(c)
} else {
// We found a surrogate, wrap and try again
c -= SURROGATES_START;
Ok(char::from_u32(c)
.expect("Generated character should be valid! This is a bug in arbitrary-rs"))
}
char::arbitrary_in_range(u, &..)
}

#[inline]
170 changes: 110 additions & 60 deletions src/foreign/core/num.rs
Original file line number Diff line number Diff line change
@@ -1,71 +1,78 @@
use {
crate::{Arbitrary, Error, MaxRecursionReached, Result, Unstructured},
crate::{Arbitrary, ArbitraryInRange, Error, MaxRecursionReached, Result, Unstructured},
core::{
mem,
num::{
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping,
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, Saturating, Wrapping,
},
ops::{Bound, RangeBounds},
},
};

#[inline]
fn map_bound<T, U, F>(bound: Bound<T>, f: F) -> Bound<U>
where
F: FnOnce(T) -> U,
{
match bound {
Bound::Included(bound) => Bound::Included(f(bound)),
Bound::Excluded(bound) => Bound::Excluded(f(bound)),
Bound::Unbounded => Bound::Unbounded,
}
}

macro_rules! impl_arbitrary_for_integers {
( $( $ty:ty; )* ) => {
( $( $ty:ty: $actual:ty; )* ) => {
$(
impl<'a> ArbitraryInRange<'a> for $ty {
type Bound = Self;

fn arbitrary_in_range<R>(u: &mut Unstructured<'a>, range: &R) -> Result<Self>
where
R: RangeBounds<Self::Bound>
{
let minimum = map_bound(range.start_bound(), |x| *x as Self);
let maximum = map_bound(range.start_bound(), |x| *x as Self);
u.int_in_range((minimum, maximum))
}
}

impl<'a> Arbitrary<'a> for $ty {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
let mut buf = [0; mem::size_of::<$ty>()];
u.fill_buffer(&mut buf)?;
Ok(Self::from_le_bytes(buf))
Self::arbitrary_in_range(u, &..)
}

#[inline]
fn size_hint(_depth: usize) -> (usize, Option<usize>) {
let n = mem::size_of::<$ty>();
(n, Some(n))
// The implementation of `arbitrary` uses `int_in_range`,
// which is actually able to generate integers even from 0 bytes,
// thus the actual minimum is `0`.
// However, to meaningful generate integers, more bytes are required.
(mem::size_of::<$actual>(), Some(mem::size_of::<$actual>()))
}

}
)*
}
}

impl_arbitrary_for_integers! {
u8;
u16;
u32;
u64;
u128;
i8;
i16;
i32;
i64;
i128;
}

// Note: We forward Arbitrary for i/usize to i/u64 in order to simplify corpus
// compatibility between 32-bit and 64-bit builds. This introduces dead space in
// 32-bit builds but keeps the input layout independent of the build platform.
impl<'a> Arbitrary<'a> for usize {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
u.arbitrary::<u64>().map(|x| x as usize)
}

#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
<u64 as Arbitrary>::size_hint(depth)
}
}

impl<'a> Arbitrary<'a> for isize {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
u.arbitrary::<i64>().map(|x| x as isize)
}

#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
<i64 as Arbitrary>::size_hint(depth)
}
impl_arbitrary_for_integers! {
u8: u8;
u16: u16;
u32: u32;
u64: u64;
u128: u128;
usize: u64;
i8: i8;
i16: i16;
i32: i32;
i64: i64;
i128: i128;
isize: i64;
}

macro_rules! impl_arbitrary_for_floats {
@@ -92,12 +99,23 @@ impl_arbitrary_for_floats! {

macro_rules! implement_nonzero_int {
($nonzero:ty, $int:ty) => {
impl<'a> ArbitraryInRange<'a> for $nonzero {
type Bound = $int;

fn arbitrary_in_range<R>(u: &mut Unstructured<'a>, range: &R) -> Result<Self>
where
R: RangeBounds<Self::Bound>,
{
Self::new(<$int as ArbitraryInRange<'a>>::arbitrary_in_range(
u, range,
)?)
.ok_or(Error::InvalidRange)
}
}

impl<'a> Arbitrary<'a> for $nonzero {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
match Self::new(<$int as Arbitrary<'a>>::arbitrary(u)?) {
Some(n) => Ok(n),
None => Err(Error::IncorrectFormat),
}
Self::new(<$int as Arbitrary<'a>>::arbitrary(u)?).ok_or(Error::IncorrectFormat)
}

#[inline]
@@ -121,21 +139,53 @@ implement_nonzero_int! { NonZeroU64, u64 }
implement_nonzero_int! { NonZeroU128, u128 }
implement_nonzero_int! { NonZeroUsize, usize }

impl<'a, A> Arbitrary<'a> for Wrapping<A>
where
A: Arbitrary<'a>,
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
Arbitrary::arbitrary(u).map(Wrapping)
}
macro_rules! implement_wrapped {
($outer:ident) => {
impl<'a, A> ArbitraryInRange<'a> for $outer<A>
where
A: ArbitraryInRange<'a>,
{
type Bound = A::Bound;

fn arbitrary_in_range<R>(u: &mut Unstructured<'a>, range: &R) -> Result<Self>
where
R: RangeBounds<Self::Bound>,
{
A::arbitrary_in_range(u, range).map($outer)
}

#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
Self::try_size_hint(depth).unwrap_or_default()
}
fn arbitrary_in_range_take_rest<R>(u: Unstructured<'a>, range: &R) -> Result<Self>
where
R: RangeBounds<Self::Bound>,
{
A::arbitrary_in_range_take_rest(u, range).map($outer)
}
}

#[inline]
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
<A as Arbitrary<'a>>::try_size_hint(depth)
}
impl<'a, A> Arbitrary<'a> for $outer<A>
where
A: Arbitrary<'a>,
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
A::arbitrary(u).map($outer)
}

fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
A::arbitrary_take_rest(u).map($outer)
}

#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
Self::try_size_hint(depth).unwrap_or_default()
}

#[inline]
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
<A as Arbitrary<'a>>::try_size_hint(depth)
}
}
};
}

implement_wrapped! { Saturating }
implement_wrapped! { Wrapping }
2 changes: 1 addition & 1 deletion src/foreign/core/ops.rs
Original file line number Diff line number Diff line change
@@ -104,7 +104,7 @@ where
A: Arbitrary<'a>,
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
match u.int_in_range::<u8>(0..=2)? {
match u.int_in_range(0..=2u8)? {
0 => Ok(Bound::Included(A::arbitrary(u)?)),
1 => Ok(Bound::Excluded(A::arbitrary(u)?)),
2 => Ok(Bound::Unbounded),
25 changes: 23 additions & 2 deletions src/foreign/core/option.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
use crate::{size_hint, Arbitrary, MaxRecursionReached, Result, Unstructured};
use {
crate::{size_hint, Arbitrary, ArbitraryInRange, MaxRecursionReached, Result, Unstructured},
core::ops::RangeBounds,
};

impl<'a, A> ArbitraryInRange<'a> for Option<A>
where
A: ArbitraryInRange<'a>,
{
type Bound = A::Bound;

fn arbitrary_in_range<R>(u: &mut Unstructured<'a>, range: &R) -> Result<Self>
where
R: RangeBounds<Self::Bound>,
{
Ok(if <bool as Arbitrary<'a>>::arbitrary(u)? {
Some(A::arbitrary_in_range(u, range)?)
} else {
None
})
}
}

impl<'a, A> Arbitrary<'a> for Option<A>
where
A: Arbitrary<'a>,
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
Ok(if <bool as Arbitrary<'a>>::arbitrary(u)? {
Some(Arbitrary::arbitrary(u)?)
Some(A::arbitrary(u)?)
} else {
None
})
38 changes: 15 additions & 23 deletions src/foreign/core/sync/atomic.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use {
crate::{Arbitrary, Result, Unstructured},
core::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize},
crate::{Arbitrary, ArbitraryInRange, Result, Unstructured},
core::sync::atomic::{
AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicU16, AtomicU32,
AtomicU64, AtomicU8, AtomicUsize,
},
};

impl<'a> Arbitrary<'a> for AtomicBool {
@@ -14,24 +17,13 @@ impl<'a> Arbitrary<'a> for AtomicBool {
}
}

impl<'a> Arbitrary<'a> for AtomicIsize {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
Arbitrary::arbitrary(u).map(Self::new)
}

#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
<isize as Arbitrary<'a>>::size_hint(depth)
}
}

impl<'a> Arbitrary<'a> for AtomicUsize {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
Arbitrary::arbitrary(u).map(Self::new)
}

#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
<usize as Arbitrary<'a>>::size_hint(depth)
}
}
implement_new! { AtomicI8: i8 }
implement_new! { AtomicI16: i16 }
implement_new! { AtomicI32: i32 }
implement_new! { AtomicI64: i64 }
implement_new! { AtomicIsize: isize }
implement_new! { AtomicU8: u8 }
implement_new! { AtomicU16: u16 }
implement_new! { AtomicU32: u32 }
implement_new! { AtomicU64: u64 }
implement_new! { AtomicUsize: usize }
67 changes: 64 additions & 3 deletions src/foreign/core/time.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,74 @@
use {
crate::{size_hint, Arbitrary, Result, Unstructured},
core::time::Duration,
crate::{size_hint, Arbitrary, ArbitraryInRange, Result, Unstructured},
core::{
ops::{Bound, RangeBounds},
time::Duration,
},
};

const MAX_NANOS: u32 = 999_999_999;

fn split_duration(duration: &Duration) -> (u64, u32) {
(
duration.as_secs(),
// We can assume, that this is always in the range 0–MAX_NANOS
duration.subsec_nanos(),
)
}

fn split_bound_duration(duration: Bound<&Duration>) -> (Bound<u64>, Bound<u32>) {
match duration {
Bound::Included(duration) => {
let (secs, nanos) = split_duration(duration);
(Bound::Included(secs), Bound::Included(nanos))
}
Bound::Excluded(duration) => {
let (secs, nanos) = split_duration(duration);
(Bound::Excluded(secs), Bound::Excluded(nanos))
}
Bound::Unbounded => (Bound::Unbounded, Bound::Unbounded),
}
}

impl<'a> ArbitraryInRange<'a> for Duration {
type Bound = Self;

fn arbitrary_in_range<R>(u: &mut Unstructured<'a>, range: &R) -> Result<Self>
where
R: RangeBounds<Self::Bound>,
{
let (start_secs, start_nanos) = split_bound_duration(range.start_bound());
let (end_secs, end_nanos) = split_bound_duration(range.end_bound());
let secs = u64::arbitrary_in_range(u, &(start_secs, end_secs))?;

// If secs is not the minimum, nanos can start at 0.
let start_nanos = match start_secs {
Bound::Included(start_secs) => (secs == start_secs).then_some(start_nanos),
Bound::Excluded(start_secs) => (secs == start_secs + 1).then_some(start_nanos),
// If start_secs is unbounded, start_nanos is too
Bound::Unbounded => None,
};
let start_nanos = start_nanos.unwrap_or(Bound::Unbounded);

// If secs is not the maximum, nanos can end at 999_999_999.
let end_nanos = match end_secs {
Bound::Included(end_secs) => (end_secs == secs).then_some(end_nanos),
Bound::Excluded(end_secs) => (end_secs == secs + 1).then_some(end_nanos),
// If end_secs is unbounded, end_nanos is too
Bound::Unbounded => None,
};
let end_nanos = end_nanos.unwrap_or(Bound::Included(MAX_NANOS));

let nanos = u32::arbitrary_in_range(u, &(start_nanos, end_nanos))?;

Ok(Self::new(secs, nanos))
}
}
impl<'a> Arbitrary<'a> for Duration {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
Ok(Self::new(
<u64 as Arbitrary>::arbitrary(u)?,
u.int_in_range(0..=999_999_999)?,
u.int_in_range(0..=MAX_NANOS)?,
))
}

140 changes: 140 additions & 0 deletions src/foreign/mod.rs
Original file line number Diff line number Diff line change
@@ -2,6 +2,146 @@
//!
//! [`Arbitrary`]: crate::Arbitrary
macro_rules! implement_from_iter {
($outer:ident <$inner:ty> $(: $($bound:ident),+)?) => {
impl<'a, A> crate::ArbitraryInRange<'a> for $outer<$inner>
where
A: crate::ArbitraryInRange<'a> $($(+ $bound)+)?,
{
type Bound = A::Bound;

fn arbitrary_in_range<R>(
u: &mut crate::Unstructured<'a>,
range: &R,
) -> crate::Result<Self>
where
R: core::ops::RangeBounds<Self::Bound>,
{
u.arbitrary_in_range_iter(range)?.collect()
}
}

impl<'a, A> crate::Arbitrary<'a> for $outer<$inner>
where
A: crate::Arbitrary<'a> $($(+ $bound)+)?,
{
fn arbitrary(u: &mut crate::Unstructured<'a>) -> crate::Result<Self> {
u.arbitrary_iter()?.collect()
}

fn arbitrary_take_rest(u: crate::Unstructured<'a>) -> crate::Result<Self> {
u.arbitrary_take_rest_iter()?.collect()
}

#[inline]
fn size_hint(_depth: usize) -> (usize, Option<usize>) {
(0, None)
}
}
};
}

macro_rules! implement_new {
($ty:ty: $bound:ty) => {
impl<'a> ArbitraryInRange<'a> for $ty {
type Bound = $bound;

fn arbitrary_in_range<R>(u: &mut Unstructured<'a>, range: &R) -> Result<Self>
where
R: core::ops::RangeBounds<Self::Bound>,
{
<$bound as ArbitraryInRange<'a>>::arbitrary_in_range(u, range).map(Self::new)
}
}

impl<'a> Arbitrary<'a> for $ty {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
<$bound as Arbitrary<'a>>::arbitrary(u).map(Self::new)
}

#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
Self::try_size_hint(depth).unwrap_or_default()
}

#[inline]
fn try_size_hint(
depth: usize,
) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
<$bound as Arbitrary<'a>>::try_size_hint(depth)
}
}
};
}

macro_rules! implement_wrapped_new {
($outer:ident) => {
implement_wrapped_new! {
$outer @
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
A::try_size_hint(depth)
}
}
};
($outer:ident!) => {
implement_wrapped_new! {
$outer @
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), crate::MaxRecursionReached> {
crate::size_hint::try_recursion_guard(depth, <A as Arbitrary>::try_size_hint)
}
}
};
($outer:ident @ $try_size_hint:item) => {
impl<'a, A> crate::ArbitraryInRange<'a> for $outer<A>
where
A: crate::ArbitraryInRange<'a>,
{
type Bound = A::Bound;

fn arbitrary_in_range<R>(
u: &mut crate::Unstructured<'a>,
range: &R,
) -> crate::Result<Self>
where
R: core::ops::RangeBounds<Self::Bound>,
{
A::arbitrary_in_range(u, range).map($outer::new)
}

fn arbitrary_in_range_take_rest<R>(
u: crate::Unstructured<'a>,
range: &R,
) -> crate::Result<Self>
where
R: core::ops::RangeBounds<Self::Bound>,
{
A::arbitrary_in_range_take_rest(u, range).map($outer::new)
}
}

impl<'a, A> crate::Arbitrary<'a> for $outer<A>
where
A: crate::Arbitrary<'a>,
{
fn arbitrary(u: &mut crate::Unstructured<'a>) -> crate::Result<Self> {
A::arbitrary(u).map($outer::new)
}

fn arbitrary_take_rest(u: crate::Unstructured<'a>) -> crate::Result<Self> {
A::arbitrary_take_rest(u).map($outer::new)
}

#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
Self::try_size_hint(depth).unwrap_or_default()
}

#[inline]
$try_size_hint
}
};
}

mod alloc;
mod core;
mod std;
18 changes: 17 additions & 1 deletion src/foreign/std/collections/hash_set.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
use {
crate::{Arbitrary, Result, Unstructured},
crate::{Arbitrary, ArbitraryInRange, Result, Unstructured},
std::{
collections::hash_set::HashSet,
hash::{BuildHasher, Hash},
ops::RangeBounds,
},
};

impl<'a, A, S> ArbitraryInRange<'a> for HashSet<A, S>
where
A: ArbitraryInRange<'a> + Eq + Hash,
S: BuildHasher + Default,
{
type Bound = A::Bound;

fn arbitrary_in_range<R>(u: &mut Unstructured<'a>, range: &R) -> Result<Self>
where
R: RangeBounds<Self::Bound>,
{
u.arbitrary_in_range_iter(range)?.collect()
}
}

impl<'a, A, S> Arbitrary<'a> for HashSet<A, S>
where
A: Arbitrary<'a> + Eq + Hash,
24 changes: 2 additions & 22 deletions src/foreign/std/sync.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,3 @@
use {
crate::{Arbitrary, MaxRecursionReached, Result, Unstructured},
std::sync::Mutex,
};
use std::sync::Mutex;

impl<'a, A> Arbitrary<'a> for Mutex<A>
where
A: Arbitrary<'a>,
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
Arbitrary::arbitrary(u).map(Self::new)
}

#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
Self::try_size_hint(depth).unwrap_or_default()
}

#[inline]
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
A::try_size_hint(depth)
}
}
implement_wrapped_new! { Mutex }
41 changes: 39 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -30,13 +30,16 @@ pub mod unstructured;
#[cfg(test)]
mod tests;

pub use error::*;
pub use {
self::error::{Error, Result},
core::ops::RangeBounds,
};

#[cfg(feature = "derive_arbitrary")]
pub use derive_arbitrary::*;

#[doc(inline)]
pub use unstructured::Unstructured;
pub use self::unstructured::Unstructured;

/// Error indicating that the maximum recursion depth has been reached while calculating [`Arbitrary::size_hint`]()
#[derive(Debug, Clone)]
@@ -418,6 +421,40 @@ pub trait Arbitrary<'a>: Sized {
}
}

/// Generate arbitrary structured values in a given range from raw, unstructured data.
///
/// This trait is similar to [`Arbitrary`], but for ordered types,
/// even though it is not required for `Self` to implement [`PartialOrd`] or even [`Ord`].
pub trait ArbitraryInRange<'a>: Sized {
/// The type of the range bounds.
///
/// Usually, this is equal to `Self`, but some wrapping types like [`Option<T>`],
/// which only make sense as range bounds if `T` can be used as range bounds,
/// it might be more useful to use the wrapped/inner type `T` instead.
type Bound;

/// Generate an arbitrary value of `Self` in given `range`.
///
/// See also the documentation of [`Arbitrary::arbitrary`].
fn arbitrary_in_range<R>(u: &mut Unstructured<'a>, range: &R) -> Result<Self>
where
R: RangeBounds<Self::Bound>;

/// Generate an arbitrary value of `Self` in given `range`
/// from the entirety of the given unstructured data.
///
/// Unlike [`ArbitraryInRange::arbitrary`], which borrows the source of `unstructured` data,
/// this method will take ownership and thus can only be called by the last consumer.
///
/// See also the documentation for [`Unstructured`].
fn arbitrary_in_range_take_rest<R>(mut u: Unstructured<'a>, range: &R) -> Result<Self>
where
R: RangeBounds<Self::Bound>,
{
Self::arbitrary_in_range(&mut u, range)
}
}

/// Multiple conflicting arbitrary attributes are used on the same field:
/// ```compile_fail
/// #[derive(::arbitrary::Arbitrary)]
144 changes: 144 additions & 0 deletions src/unstructured/integer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
//! FIXME: Missing Documentation.
use core::{
fmt::Debug,
mem,
ops::{BitOr, Rem, Shl, Shr, Sub},
};

/// A trait that is implemented for all of the primitive integers:
///
/// * `u8`
/// * `u16`
/// * `u32`
/// * `u64`
/// * `u128`
/// * `usize`
/// * `i8`
/// * `i16`
/// * `i32`
/// * `i64`
/// * `i128`
/// * `isize`
///
/// Don't implement this trait yourself.
pub trait Int:
Copy
+ Debug
+ PartialOrd
+ Ord
+ Sub<Self, Output = Self>
+ Rem<Self, Output = Self>
+ Shr<Self, Output = Self>
+ Shl<usize, Output = Self>
+ BitOr<Self, Output = Self>
{
#[doc(hidden)]
type Unsigned: Int;

#[doc(hidden)]
type Array: Default + AsMut<[u8]>;

#[doc(hidden)]
const ZERO: Self;

#[doc(hidden)]
const ONE: Self;

#[doc(hidden)]
const MIN: Self;

#[doc(hidden)]
const MAX: Self;

#[doc(hidden)]
fn from_u8(b: u8) -> Self;

#[doc(hidden)]
fn from_bytes(bytes: Self::Array) -> Self;

#[doc(hidden)]
fn from_usize(u: usize) -> Self;

#[doc(hidden)]
fn checked_add(self, rhs: Self) -> Option<Self>;

#[doc(hidden)]
fn wrapping_add(self, rhs: Self) -> Self;

#[doc(hidden)]
fn wrapping_sub(self, rhs: Self) -> Self;

#[doc(hidden)]
fn to_unsigned(self) -> Self::Unsigned;

#[doc(hidden)]
fn from_unsigned(unsigned: Self::Unsigned) -> Self;
}

macro_rules! impl_int {
( $( $ty:ty : $unsigned_ty: ty ; )* ) => {
$(
impl Int for $ty {
type Unsigned = $unsigned_ty;

type Array = [u8; mem::size_of::<$ty>()];

const ZERO: Self = 0;

const ONE: Self = 1;

const MIN: Self = Self::MIN;

const MAX: Self = Self::MAX;

fn from_u8(b: u8) -> Self {
b as Self
}

fn from_bytes(bytes: Self::Array) -> Self {
Self::from_le_bytes(bytes)
}

fn from_usize(u: usize) -> Self {
u as Self
}

fn checked_add(self, rhs: Self) -> Option<Self> {
<$ty>::checked_add(self, rhs)
}

fn wrapping_add(self, rhs: Self) -> Self {
<$ty>::wrapping_add(self, rhs)
}

fn wrapping_sub(self, rhs: Self) -> Self {
<$ty>::wrapping_sub(self, rhs)
}

fn to_unsigned(self) -> Self::Unsigned {
self as $unsigned_ty
}

fn from_unsigned(unsigned: $unsigned_ty) -> Self {
unsigned as Self
}
}
)*
}
}

impl_int! {
u8: u8;
u16: u16;
u32: u32;
u64: u64;
u128: u128;
usize: usize;
i8: u8;
i16: u16;
i32: u32;
i64: u64;
i128: u128;
isize: usize;
}
106 changes: 106 additions & 0 deletions src/unstructured/iterators.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//! FIXME: Missing Documentation.
use {
super::Unstructured,
crate::{Arbitrary, ArbitraryInRange, Result},
core::{marker::PhantomData, ops::RangeBounds},
};

/// Utility iterator produced by [`Unstructured::arbitrary_iter`]
pub struct ArbitraryIter<'a, 'b, ElementType> {
u: &'b mut Unstructured<'a>,
_marker: PhantomData<ElementType>,
}

impl<'a, 'b, ElementType> ArbitraryIter<'a, 'b, ElementType> {
/// Construct a new arbitrary iterator consuming all the remaining bytes.
pub fn new(u: &'b mut Unstructured<'a>) -> Self {
Self {
u,
_marker: PhantomData,
}
}
}

impl<'a, 'b, ElementType: Arbitrary<'a>> Iterator for ArbitraryIter<'a, 'b, ElementType> {
type Item = Result<ElementType>;
fn next(&mut self) -> Option<Result<ElementType>> {
let keep_going = self.u.arbitrary().unwrap_or(false);
if keep_going {
Some(Arbitrary::arbitrary(self.u))
} else {
None
}
}
}

/// Utility iterator produced by [`Unstructured::arbitrary_in_range_iter`]
pub struct ArbitraryInRangeIter<'a, 'u, 'r, ElementType, Range>
where
ElementType: ArbitraryInRange<'a>,
Range: RangeBounds<ElementType::Bound>,
{
u: &'u mut Unstructured<'a>,
range: &'r Range,
_marker: PhantomData<ElementType>,
}

impl<'a, 'u, 'r, ElementType, Range> ArbitraryInRangeIter<'a, 'u, 'r, ElementType, Range>
where
ElementType: ArbitraryInRange<'a>,
Range: RangeBounds<ElementType::Bound>,
{
/// Construct a new arbitrary iterator consuming all the remaining bytes.
pub fn new(u: &'u mut Unstructured<'a>, range: &'r Range) -> Self {
Self {
u,
range,
_marker: PhantomData,
}
}
}

impl<'a, 'u, 'r, ElementType, Range> Iterator
for ArbitraryInRangeIter<'a, 'u, 'r, ElementType, Range>
where
ElementType: ArbitraryInRange<'a>,
Range: RangeBounds<ElementType::Bound>,
{
type Item = Result<ElementType>;
fn next(&mut self) -> Option<Result<ElementType>> {
let keep_going = self.u.arbitrary().unwrap_or(false);
if keep_going {
Some(ArbitraryInRange::arbitrary_in_range(self.u, self.range))
} else {
None
}
}
}

/// Utility iterator produced by [`Unstructured::arbitrary_take_rest_iter`]
pub struct ArbitraryTakeRestIter<'a, ElementType> {
u: Unstructured<'a>,
_marker: PhantomData<ElementType>,
}

impl<'a, ElementType> ArbitraryTakeRestIter<'a, ElementType> {
/// Construct a new arbitrary iterator consuming all the remaining bytes.
pub fn new(u: Unstructured<'a>) -> Self {
Self {
u,
_marker: PhantomData,
}
}
}

impl<'a, ElementType: Arbitrary<'a>> Iterator for ArbitraryTakeRestIter<'a, ElementType> {
type Item = Result<ElementType>;
fn next(&mut self) -> Option<Result<ElementType>> {
let keep_going = self.u.arbitrary().unwrap_or(false);
if keep_going {
Some(Arbitrary::arbitrary(&mut self.u))
} else {
None
}
}
}
459 changes: 101 additions & 358 deletions src/unstructured.rs → src/unstructured/mod.rs

Large diffs are not rendered by default.

152 changes: 152 additions & 0 deletions src/unstructured/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
use super::*;

#[test]
fn test_byte_size() {
let mut u = Unstructured::new(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 6]);
// Should take one byte off the end
assert_eq!(u.arbitrary_byte_size().unwrap(), 6);
assert_eq!(u.len(), 9);
let mut v = vec![0; 260];
v.push(1);
v.push(4);
let mut u = Unstructured::new(&v);
// Should read two bytes off the end
assert_eq!(u.arbitrary_byte_size().unwrap(), 0x104);
assert_eq!(u.len(), 260);
}

#[test]
fn int_in_range_of_one() {
let mut u = Unstructured::new(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 6]);
let x = u.int_in_range(0..=0).unwrap();
assert_eq!(x, 0);
let choice = *u.choose(&[42]).unwrap();
assert_eq!(choice, 42)
}

#[test]
fn int_in_range_uses_minimal_amount_of_bytes() {
let mut u = Unstructured::new(&[0x23, 0x42]);
assert_eq!(0x23, u.int_in_range::<u8, _>(..).unwrap());
assert_eq!(u.len(), 1);

let mut u = Unstructured::new(&[0x42, 0x23]);
assert_eq!(0x42, u.int_in_range::<u32, _>(..u8::MAX as u32).unwrap());
assert_eq!(u.len(), 1);

let mut u = Unstructured::new(&[0x37, 0x13]);
assert_eq!(0x1337, u.int_in_range::<u32, _>(..).unwrap());
assert!(u.is_empty());

let mut u = Unstructured::new(&[42]);
assert_eq!(
42,
u.int_in_range::<u32, _>(0..=u8::MAX as u32 + 1).unwrap()
);
assert!(u.is_empty());
}

#[test]
fn int_in_range_in_bounds() {
for input in u8::MIN..=u8::MAX {
let input = [input];

let mut u = Unstructured::new(&input);
let x = u.int_in_range(1..=u8::MAX).unwrap();
assert_ne!(x, 0);

let mut u = Unstructured::new(&input);
let x = u.int_in_range(0..=u8::MAX - 1).unwrap();
assert_ne!(x, u8::MAX);
}
}

#[test]
fn int_in_range_covers_unsigned_range() {
// Test that we generate all values within the range given to
// `int_in_range`.

let mut full = [false; u8::MAX as usize + 1];
let mut no_zero = [false; u8::MAX as usize];
let mut no_max = [false; u8::MAX as usize];
let mut narrow = [false; 10];

for input in u8::MIN..=u8::MAX {
let input = [input];

let mut u = Unstructured::new(&input);
let x = u.int_in_range(0..=u8::MAX).unwrap();
full[x as usize] = true;

let mut u = Unstructured::new(&input);
let x = u.int_in_range(1..=u8::MAX).unwrap();
no_zero[x as usize - 1] = true;

let mut u = Unstructured::new(&input);
let x = u.int_in_range(0..=u8::MAX - 1).unwrap();
no_max[x as usize] = true;

let mut u = Unstructured::new(&input);
let x = u.int_in_range(100..=109).unwrap();
narrow[x as usize - 100] = true;
}

for (i, covered) in full.iter().enumerate() {
assert!(covered, "full[{}] should have been generated", i);
}
for (i, covered) in no_zero.iter().enumerate() {
assert!(covered, "no_zero[{}] should have been generated", i);
}
for (i, covered) in no_max.iter().enumerate() {
assert!(covered, "no_max[{}] should have been generated", i);
}
for (i, covered) in narrow.iter().enumerate() {
assert!(covered, "narrow[{}] should have been generated", i);
}
}

#[test]
fn int_in_range_covers_signed_range() {
// Test that we generate all values within the range given to
// `int_in_range`.

let mut full = [false; u8::MAX as usize + 1];
let mut no_min = [false; u8::MAX as usize];
let mut no_max = [false; u8::MAX as usize];
let mut narrow = [false; 21];

let abs_i8_min: isize = 128;

for input in 0..=u8::MAX {
let input = [input];

let mut u = Unstructured::new(&input);
let x = u.int_in_range(i8::MIN..=i8::MAX).unwrap();
full[(x as isize + abs_i8_min) as usize] = true;

let mut u = Unstructured::new(&input);
let x = u.int_in_range(i8::MIN + 1..=i8::MAX).unwrap();
no_min[(x as isize + abs_i8_min - 1) as usize] = true;

let mut u = Unstructured::new(&input);
let x = u.int_in_range(i8::MIN..=i8::MAX - 1).unwrap();
no_max[(x as isize + abs_i8_min) as usize] = true;

let mut u = Unstructured::new(&input);
let x = u.int_in_range(-10..=10).unwrap();
narrow[(x as isize + 10) as usize] = true;
}

for (i, covered) in full.iter().enumerate() {
assert!(covered, "full[{}] should have been generated", i);
}
for (i, covered) in no_min.iter().enumerate() {
assert!(covered, "no_min[{}] should have been generated", i);
}
for (i, covered) in no_max.iter().enumerate() {
assert!(covered, "no_max[{}] should have been generated", i);
}
for (i, covered) in narrow.iter().enumerate() {
assert!(covered, "narrow[{}] should have been generated", i);
}
}