aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 05:11:57 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 05:11:57 +0000
commitf3b2847c1cd3da1a103b4dfbe80f9e01d61c4495 (patch)
treedc2692d0085b040a1674a3092cf72cedba7404ed
parentc8c757de63969fd5f509ed685b5d012b6590a0f3 (diff)
parent9d1d38c4a8c5cbb5b69d6d52edfb19ef7d85c651 (diff)
downloadarbitrary-android14-mainline-sdkext-release.tar.gz
Snap for 10453563 from 9d1d38c4a8c5cbb5b69d6d52edfb19ef7d85c651 to mainline-sdkext-releaseaml_sdk_341510000aml_sdk_341410000aml_sdk_341110080aml_sdk_341110000aml_sdk_341010000aml_sdk_340912010android14-mainline-sdkext-release
Change-Id: Ic6a852f76746119fdb0cf445c648f9288e6fced8
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--.github/workflows/rust.yml26
-rw-r--r--Android.bp11
-rw-r--r--CHANGELOG.md125
-rw-r--r--Cargo.toml7
-rw-r--r--Cargo.toml.orig8
-rw-r--r--METADATA12
-rw-r--r--README.md33
-rw-r--r--examples/derive_enum.rs4
-rw-r--r--src/error.rs15
-rw-r--r--src/lib.rs292
-rw-r--r--src/unstructured.rs324
-rw-r--r--[-rwxr-xr-x]tests/derive.rs94
-rw-r--r--tests/path.rs3
14 files changed, 786 insertions, 170 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 508adbd..4b94df4 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
{
"git": {
- "sha1": "d0d238d880276fd617c38f7e4712bf40db58aad6"
+ "sha1": "98044ba8b0f7d730b500fa3b4f2e96a5edcd926d"
},
"path_in_vcs": ""
} \ No newline at end of file
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 14c2d65..72f3d12 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -7,7 +7,7 @@ jobs:
name: Vanilla Build
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v1
+ - uses: actions/checkout@v3
- run: rustup update
- name: Build
run: cargo build --verbose --all
@@ -17,7 +17,7 @@ jobs:
name: All Features Enabled Build
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v1
+ - uses: actions/checkout@v3
- run: rustup update
- name: Build
run: cargo build --verbose --all-features --all
@@ -25,11 +25,31 @@ jobs:
run: cargo test --verbose --all-features --all
- name: Build Examples
run: cargo build --examples --all-features --all
+ clippy:
+ name: Clippy
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - run: rustup update
+ - run: rustup component add clippy
+ - run: cargo clippy --all-features --workspace -- -Dclippy::all
rustfmt:
name: Check rustfmt
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v1
+ - uses: actions/checkout@v3
- run: rustup update
- run: rustup component add rustfmt --toolchain stable
- run: cargo +stable fmt --all -- --check
+ fuzz:
+ name: Run `int_in_range` fuzz target
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - run: rustup update
+ - name: "Install nightly"
+ run: rustup toolchain install nightly && rustup default nightly
+ - name: "Install `cargo-fuzz`"
+ run: cargo install cargo-fuzz
+ - name: "Fuzz for 3 minutes"
+ run: cargo fuzz run int_in_range -- -max_total_time=$((3 * 60))
diff --git a/Android.bp b/Android.bp
index 83c7cf4..e82a8cd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -39,11 +39,10 @@ license {
rust_test {
name: "arbitrary_test_src_lib",
- // has rustc warnings
host_supported: true,
crate_name: "arbitrary",
cargo_env_compat: true,
- cargo_pkg_version: "1.1.0",
+ cargo_pkg_version: "1.2.3",
srcs: ["src/lib.rs"],
test_suites: ["general-tests"],
auto_gen_config: true,
@@ -60,11 +59,10 @@ rust_test {
rust_test {
name: "arbitrary_test_tests_derive",
- // has rustc warnings
host_supported: true,
crate_name: "arbitrary",
cargo_env_compat: true,
- cargo_pkg_version: "1.1.0",
+ cargo_pkg_version: "1.2.3",
srcs: ["tests/derive.rs"],
test_suites: ["general-tests"],
auto_gen_config: true,
@@ -84,11 +82,10 @@ rust_test {
rust_library_rlib {
name: "libarbitrary",
- // has rustc warnings
host_supported: true,
crate_name: "arbitrary",
cargo_env_compat: true,
- cargo_pkg_version: "1.1.0",
+ cargo_pkg_version: "1.2.3",
srcs: ["src/lib.rs"],
edition: "2018",
features: [
@@ -99,5 +96,7 @@ rust_library_rlib {
apex_available: [
"com.android.uwb",
],
+ product_available: true,
+ vendor_available: true,
min_sdk_version: "Tiramisu",
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2a7c179..2d4bb72 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,7 +20,7 @@ Released YYYY-MM-DD.
### Fixed
-* TODO (or remove section if none)
+* (Included in `arbitrary_derive` 1.2.1) Fixed bug in Derive macro around `no_std` path uses [#131](https://github.com/rust-fuzz/arbitrary/pull/131)
### Security
@@ -28,6 +28,129 @@ Released YYYY-MM-DD.
--------------------------------------------------------------------------------
+## 1.2.3
+
+Released 2023-01-20.
+
+### Fixed
+
+* The `derive(Arbitrary)` will now annotate the generated `impl`s with a `#[automatically_derived]`
+ attribute to indicate to e.g. clippy that lints should not fire for the code within the derived
+ implementation.
+
+## 1.2.2
+
+Released 2023-01-03.
+
+### Fixed
+
+* Ensured that `arbitrary` and `derive_arbitrary` versions are synced up so that
+ they don't, e.g., emit generated code that depends on newer versions of
+ `arbitrary` than the one currently in
+ use. [#134](https://github.com/rust-fuzz/arbitrary/issues/134)
+
+## 1.2.1
+
+### Fixed
+
+* Fixed an issue where `std::thread_local!` macro invocations in derive code
+ were not fully prefixed, causing confusing build errors in certain situations.
+
+## 1.2.0
+
+Released 2022-10-20.
+
+### Added
+
+* Support custom arbitrary implementation for fields on
+ derive. [#129](https://github.com/rust-fuzz/arbitrary/pull/129)
+
+--------------------------------------------------------------------------------
+
+## 1.1.6
+
+Released 2022-09-08.
+
+### Fixed
+
+* Fixed a potential panic due to an off-by-one error in the `Arbitrary`
+ implementation for `std::ops::Bound<T>`.
+
+--------------------------------------------------------------------------------
+
+## 1.1.5
+
+Released 2022-09-20.
+
+### Added
+
+* Implemented `Arbitrary` for `std::ops::Bound<T>`.
+
+### Fixed
+
+* Fixed a bug where `Unstructured::int_in_range` could return out-of-range
+ integers when generating arbitrary signed integers.
+
+--------------------------------------------------------------------------------
+
+## 1.1.4
+
+Released 2022-08-29.
+
+### Added
+
+* Implemented `Arbitrary` for `Rc<str>` and `Arc<str>`
+
+### Changed
+
+* Allow overriding the error type in `arbitrary::Result`
+* The `Unstructured::arbitrary_loop` method will consume fewer bytes of input
+ now.
+
+### Fixed
+
+* Fixed a bug where `Unstructured::int_in_range` could return out-of-range
+ integers.
+
+--------------------------------------------------------------------------------
+
+## 1.1.3
+
+Released 2022-06-23.
+
+### Fixed
+
+* Fixed some potential (but highly unlikely) name-clashes inside
+ `derive(Arbitrary)`'s generated
+ code. [#111](https://github.com/rust-fuzz/arbitrary/pull/111)
+* Fixed an edge case where `derive(Arbitrary)` for recursive types that detected
+ an overflow would not reset the overflow
+ detection. [#111](https://github.com/rust-fuzz/arbitrary/pull/111)
+
+--------------------------------------------------------------------------------
+
+## 1.1.2
+
+Released 2022-06-16.
+
+### Fixed
+
+* Fixed a warning inside `derive(Arbitrary)`-generated
+ code. [#110](https://github.com/rust-fuzz/arbitrary/pull/110)
+
+--------------------------------------------------------------------------------
+
+## 1.1.1
+
+Released 2022-06-14.
+
+### Fixed
+
+* Fixed a stack overflow when using `derive(Arbitrary)` with recursive types and
+ empty inputs. [#109](https://github.com/rust-fuzz/arbitrary/pull/109)
+
+--------------------------------------------------------------------------------
+
## 1.1.0
Released 2022-02-09.
diff --git a/Cargo.toml b/Cargo.toml
index a42a90b..9b11611 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,8 +11,9 @@
[package]
edition = "2018"
+rust-version = "1.63.0"
name = "arbitrary"
-version = "1.1.0"
+version = "1.2.3"
authors = [
"The Rust-Fuzz Project Developers",
"Nick Fitzgerald <fitzgen@gmail.com>",
@@ -42,10 +43,8 @@ path = "./tests/derive.rs"
required-features = ["derive"]
[dependencies.derive_arbitrary]
-version = "1.0.0"
+version = "1.2.3"
optional = true
-[dev-dependencies]
-
[features]
derive = ["derive_arbitrary"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index df4e2a1..ed72b4c 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "arbitrary"
-version = "1.1.0" # Make sure this matches the derive crate version
+version = "1.2.3" # Make sure this matches the derive crate version (not including the patch version)
authors = [
"The Rust-Fuzz Project Developers",
"Nick Fitzgerald <fitzgen@gmail.com>",
@@ -17,11 +17,10 @@ description = "The trait for generating structured data from unstructured data"
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-fuzz/arbitrary/"
documentation = "https://docs.rs/arbitrary/"
+rust-version = "1.63.0"
[dependencies]
-derive_arbitrary = { version = "1.0.0", path = "./derive", optional = true }
-
-[dev-dependencies]
+derive_arbitrary = { version = "1.2.3", path = "./derive", optional = true }
[features]
# Turn this feature on to enable support for `#[derive(Arbitrary)]`.
@@ -37,3 +36,4 @@ path = "./tests/derive.rs"
required-features = ["derive"]
[workspace]
+members = ["./fuzz"]
diff --git a/METADATA b/METADATA
index 00cf505..8621082 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,7 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update rust/crates/arbitrary
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+
name: "arbitrary"
description: "The trait for generating structured data from unstructured data"
third_party {
@@ -7,13 +11,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/arbitrary/arbitrary-1.1.0.crate"
+ value: "https://static.crates.io/crates/arbitrary/arbitrary-1.2.3.crate"
}
- version: "1.1.0"
+ version: "1.2.3"
license_type: NOTICE
last_upgrade_date {
- year: 2022
- month: 3
+ year: 2023
+ month: 2
day: 1
}
}
diff --git a/README.md b/README.md
index 38bd949..3f5619b 100644
--- a/README.md
+++ b/README.md
@@ -61,6 +61,39 @@ pub struct Rgb {
}
```
+#### Customizing single fields
+
+This can be particular handy if your structure uses a type that does not implement `Arbitrary` or you want to have more customization for particular fields.
+
+```rust
+#[derive(Arbitrary)]
+pub struct Rgba {
+ // set `r` to Default::default()
+ #[arbitrary(default)]
+ pub r: u8,
+
+ // set `g` to 255
+ #[arbitrary(value = 255)]
+ pub g: u8,
+
+ // Generate `b` with a custom function of type
+ //
+ // fn(&mut Unstructured) -> arbitrary::Result<T>
+ //
+ // where `T` is the field's type.
+ #[arbitrary(with = arbitrary_b)]
+ pub b: u8,
+
+ // Generate `a` with a custom closure (shortuct to avoid a custom funciton)
+ #[arbitrary(with = |u: &mut Unstructured| u.int_in_range(0..=64))]
+ pub a: u8,
+}
+
+fn arbitrary_b(u: &mut Unstructured) -> arbitrary::Result<u8> {
+ u.int_in_range(64..=128)
+}
+```
+
### Implementing `Arbitrary` By Hand
Alternatively, you can write an `Arbitrary` implementation by hand:
diff --git a/examples/derive_enum.rs b/examples/derive_enum.rs
index fbcc106..c4fc9c9 100644
--- a/examples/derive_enum.rs
+++ b/examples/derive_enum.rs
@@ -2,6 +2,10 @@
//!
//! Note that this requires enabling the "derive" cargo feature.
+// Various enums/fields that we are deriving `Arbitrary` for aren't actually
+// used except to show off the derive.
+#![allow(dead_code)]
+
use arbitrary::{Arbitrary, Unstructured};
#[derive(Arbitrary, Debug)]
diff --git a/src/error.rs b/src/error.rs
index f590c12..1d3df2a 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -37,4 +37,17 @@ impl error::Error for Error {}
/// A `Result` with the error type fixed as `arbitrary::Error`.
///
/// Either an `Ok(T)` or `Err(arbitrary::Error)`.
-pub type Result<T> = std::result::Result<T, Error>;
+pub type Result<T, E = Error> = std::result::Result<T, E>;
+
+#[cfg(test)]
+mod tests {
+ // Often people will import our custom `Result` type because 99.9% of
+ // results in a file will be `arbitrary::Result` but then have that one last
+ // 0.1% that want to have a custom error type. Don't make them prefix that
+ // 0.1% as `std::result::Result`; instead, let `arbitrary::Result` have an
+ // overridable error type.
+ #[test]
+ fn can_use_custom_error_types_with_result() -> super::Result<(), String> {
+ Ok(())
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 7b791ab..a3fa48b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -34,6 +34,7 @@ pub use unstructured::Unstructured;
pub mod size_hint;
+use core::array;
use core::cell::{Cell, RefCell, UnsafeCell};
use core::iter;
use core::mem;
@@ -47,6 +48,7 @@ use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedL
use std::ffi::{CString, OsString};
use std::hash::BuildHasher;
use std::net::{Ipv4Addr, Ipv6Addr};
+use std::ops::Bound;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize};
@@ -138,14 +140,17 @@ pub trait Arbitrary<'a>: Sized {
/// perhaps given to you by a fuzzer like AFL or libFuzzer. You wrap this
/// raw data in an `Unstructured`, and then you can call `<MyType as
/// Arbitrary>::arbitrary` to construct an arbitrary instance of `MyType`
- /// from that unstuctured data.
+ /// from that unstructured data.
///
- /// Implementation may return an error if there is not enough data to
- /// construct a full instance of `Self`. This is generally OK: it is better
- /// to exit early and get the fuzzer to provide more input data, than it is
- /// to generate default values in place of the missing data, which would
- /// bias the distribution of generated values, and ultimately make fuzzing
- /// less efficient.
+ /// Implementations may return an error if there is not enough data to
+ /// construct a full instance of `Self`, or they may fill out the rest of
+ /// `Self` with dummy values. Using dummy values when the underlying data is
+ /// exhausted can help avoid accidentally "defeating" some of the fuzzer's
+ /// mutations to the underlying byte stream that might otherwise lead to
+ /// interesting runtime behavior or new code coverage if only we had just a
+ /// few more bytes. However, it also requires that implementations for
+ /// recursive types (e.g. `struct Foo(Option<Box<Foo>>)`) avoid infinite
+ /// recursion when the underlying data is exhausted.
///
/// ```
/// # #[cfg(feature = "derive")] fn foo() {
@@ -174,11 +179,13 @@ pub trait Arbitrary<'a>: Sized {
/// See also the documentation for [`Unstructured`][crate::Unstructured].
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self>;
- /// Generate an arbitrary value of `Self` from the entirety of the given unstructured data.
+ /// Generate an arbitrary value of `Self` from the entirety of the given
+ /// unstructured data.
///
- /// This is similar to Arbitrary::arbitrary, however it assumes that it is the
- /// last consumer of the given data, and is thus able to consume it all if it needs.
- /// See also the documentation for [`Unstructured`][crate::Unstructured].
+ /// This is similar to Arbitrary::arbitrary, however it assumes that it is
+ /// the last consumer of the given data, and is thus able to consume it all
+ /// if it needs. See also the documentation for
+ /// [`Unstructured`][crate::Unstructured].
fn arbitrary_take_rest(mut u: Unstructured<'a>) -> Result<Self> {
Self::arbitrary(&mut u)
}
@@ -202,6 +209,17 @@ pub trait Arbitrary<'a>: Sized {
/// default with a better implementation. The
/// [`size_hint`][crate::size_hint] module will help with this task.
///
+ /// ## Invariant
+ ///
+ /// It must be possible to construct every possible output using only inputs
+ /// of lengths bounded by these parameters. This applies to both
+ /// [`Arbitrary::arbitrary`] and [`Arbitrary::arbitrary_take_rest`].
+ ///
+ /// This is trivially true for `(0, None)`. To restrict this further, it
+ /// must be proven that all inputs that are now excluded produced redundant
+ /// outputs which are still possible to produce using the reduced input
+ /// space.
+ ///
/// ## The `depth` Parameter
///
/// If you 100% know that the type you are implementing `Arbitrary` for is
@@ -593,27 +611,6 @@ impl<T, const N: usize> Drop for ArrayGuard<T, N> {
}
}
-fn create_array<F, T, const N: usize>(mut cb: F) -> [T; N]
-where
- F: FnMut(usize) -> T,
-{
- let mut array: mem::MaybeUninit<[T; N]> = mem::MaybeUninit::uninit();
- let array_ptr = array.as_mut_ptr();
- let dst = array_ptr as _;
- let mut guard: ArrayGuard<T, N> = ArrayGuard {
- dst,
- initialized: 0,
- };
- unsafe {
- for (idx, value_ptr) in (&mut *array.as_mut_ptr()).iter_mut().enumerate() {
- core::ptr::write(value_ptr, cb(idx));
- guard.initialized += 1;
- }
- mem::forget(guard);
- array.assume_init()
- }
-}
-
fn try_create_array<F, T, const N: usize>(mut cb: F) -> Result<[T; N]>
where
F: FnMut(usize) -> Result<T>,
@@ -626,7 +623,7 @@ where
initialized: 0,
};
unsafe {
- for (idx, value_ptr) in (&mut *array.as_mut_ptr()).iter_mut().enumerate() {
+ for (idx, value_ptr) in (*array.as_mut_ptr()).iter_mut().enumerate() {
core::ptr::write(value_ptr, cb(idx)?);
guard.initialized += 1;
}
@@ -655,7 +652,7 @@ where
#[inline]
fn size_hint(d: usize) -> (usize, Option<usize>) {
- crate::size_hint::and_all(&create_array::<_, (usize, Option<usize>), N>(|_| {
+ crate::size_hint::and_all(&array::from_fn::<_, N, _>(|_| {
<T as Arbitrary>::size_hint(d)
}))
}
@@ -672,8 +669,8 @@ impl<'a> Arbitrary<'a> for &'a [u8] {
}
#[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- <usize as Arbitrary>::size_hint(depth)
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
}
}
@@ -687,8 +684,8 @@ impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Vec<A> {
}
#[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::and(<usize as Arbitrary>::size_hint(depth), (0, None))
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
}
}
@@ -702,8 +699,8 @@ impl<'a, K: Arbitrary<'a> + Ord, V: Arbitrary<'a>> Arbitrary<'a> for BTreeMap<K,
}
#[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::and(<usize as Arbitrary>::size_hint(depth), (0, None))
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
}
}
@@ -717,8 +714,27 @@ impl<'a, A: Arbitrary<'a> + Ord> Arbitrary<'a> for BTreeSet<A> {
}
#[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
+ }
+}
+
+impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Bound<A> {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ match u.int_in_range::<u8>(0..=2)? {
+ 0 => Ok(Bound::Included(A::arbitrary(u)?)),
+ 1 => Ok(Bound::Excluded(A::arbitrary(u)?)),
+ 2 => Ok(Bound::Unbounded),
+ _ => unreachable!(),
+ }
+ }
+
+ #[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::and(<usize as Arbitrary>::size_hint(depth), (0, None))
+ size_hint::or(
+ size_hint::and((1, Some(1)), A::size_hint(depth)),
+ (1, Some(1)),
+ )
}
}
@@ -732,8 +748,8 @@ impl<'a, A: Arbitrary<'a> + Ord> Arbitrary<'a> for BinaryHeap<A> {
}
#[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::and(<usize as Arbitrary>::size_hint(depth), (0, None))
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
}
}
@@ -749,8 +765,8 @@ impl<'a, K: Arbitrary<'a> + Eq + ::std::hash::Hash, V: Arbitrary<'a>, S: BuildHa
}
#[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::and(<usize as Arbitrary>::size_hint(depth), (0, None))
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
}
}
@@ -766,8 +782,8 @@ impl<'a, A: Arbitrary<'a> + Eq + ::std::hash::Hash, S: BuildHasher + Default> Ar
}
#[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::and(<usize as Arbitrary>::size_hint(depth), (0, None))
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
}
}
@@ -781,8 +797,8 @@ impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for LinkedList<A> {
}
#[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::and(<usize as Arbitrary>::size_hint(depth), (0, None))
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
}
}
@@ -796,8 +812,8 @@ impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for VecDeque<A> {
}
#[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::and(<usize as Arbitrary>::size_hint(depth), (0, None))
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
}
}
@@ -821,7 +837,7 @@ where
impl<'a> Arbitrary<'a> for &'a str {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
let size = u.arbitrary_len::<u8>()?;
- match str::from_utf8(&u.peek_bytes(size).unwrap()) {
+ match str::from_utf8(u.peek_bytes(size).unwrap()) {
Ok(s) => {
u.bytes(size).unwrap();
Ok(s)
@@ -840,14 +856,12 @@ impl<'a> Arbitrary<'a> for &'a str {
fn arbitrary_take_rest(u: Unstructured<'a>) -> Result<Self> {
let bytes = u.take_rest();
- str::from_utf8(bytes)
- .map_err(|_| Error::IncorrectFormat)
- .map(Into::into)
+ str::from_utf8(bytes).map_err(|_| Error::IncorrectFormat)
}
#[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::and(<usize as Arbitrary>::size_hint(depth), (0, None))
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (0, None)
}
}
@@ -959,6 +973,17 @@ impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Arc<A> {
}
}
+impl<'a> Arbitrary<'a> for Arc<str> {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ <&str as Arbitrary>::arbitrary(u).map(Into::into)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ <&str as Arbitrary>::size_hint(depth)
+ }
+}
+
impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Rc<A> {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
Arbitrary::arbitrary(u).map(Self::new)
@@ -970,6 +995,17 @@ impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Rc<A> {
}
}
+impl<'a> Arbitrary<'a> for Rc<str> {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ <&str as Arbitrary>::arbitrary(u).map(Into::into)
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ <&str as Arbitrary>::size_hint(depth)
+ }
+}
+
impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Cell<A> {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
Arbitrary::arbitrary(u).map(Self::new)
@@ -1104,6 +1140,56 @@ impl<'a> Arbitrary<'a> for Ipv6Addr {
mod test {
use super::*;
+ /// Generates an arbitrary `T`, and checks that the result is consistent with the
+ /// `size_hint()` reported by `T`.
+ fn checked_arbitrary<'a, T: Arbitrary<'a>>(u: &mut Unstructured<'a>) -> Result<T> {
+ let (min, max) = T::size_hint(0);
+
+ let len_before = u.len();
+ let result = T::arbitrary(u);
+
+ let consumed = len_before - u.len();
+
+ if let Some(max) = max {
+ assert!(
+ consumed <= max,
+ "incorrect maximum size: indicated {}, actually consumed {}",
+ max,
+ consumed
+ );
+ }
+
+ if result.is_ok() {
+ assert!(
+ consumed >= min,
+ "incorrect minimum size: indicated {}, actually consumed {}",
+ min,
+ consumed
+ );
+ }
+
+ result
+ }
+
+ /// Like `checked_arbitrary()`, but calls `arbitrary_take_rest()` instead of `arbitrary()`.
+ fn checked_arbitrary_take_rest<'a, T: Arbitrary<'a>>(u: Unstructured<'a>) -> Result<T> {
+ let (min, _) = T::size_hint(0);
+
+ let len_before = u.len();
+ let result = T::arbitrary_take_rest(u);
+
+ if result.is_ok() {
+ assert!(
+ len_before >= min,
+ "incorrect minimum size: indicated {}, worked with {}",
+ min,
+ len_before
+ );
+ }
+
+ result
+ }
+
#[test]
fn finite_buffer_fill_buffer() {
let x = [1, 2, 3, 4];
@@ -1122,7 +1208,7 @@ mod test {
let x = [1, 2, 3, 4];
let mut buf = Unstructured::new(&x);
let expected = 1 | (2 << 8) | (3 << 16) | (4 << 24);
- let actual = i32::arbitrary(&mut buf).unwrap();
+ let actual = checked_arbitrary::<i32>(&mut buf).unwrap();
assert_eq!(expected, actual);
}
@@ -1131,7 +1217,7 @@ mod test {
let x = [1, 2, 3, 4, 4];
let mut buf = Unstructured::new(&x);
let expected = &[1, 2, 3, 4];
- let actual = <&[u8] as Arbitrary>::arbitrary(&mut buf).unwrap();
+ let actual = checked_arbitrary::<&[u8]>(&mut buf).unwrap();
assert_eq!(expected, actual);
}
@@ -1140,26 +1226,30 @@ mod test {
let x = [1, 2, 3, 4];
let buf = Unstructured::new(&x);
let expected = &[1, 2, 3, 4];
- let actual = <&[u8] as Arbitrary>::arbitrary_take_rest(buf).unwrap();
+ let actual = checked_arbitrary_take_rest::<&[u8]>(buf).unwrap();
assert_eq!(expected, actual);
}
#[test]
fn arbitrary_collection() {
let x = [
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 12,
];
assert_eq!(
- Vec::<u8>::arbitrary(&mut Unstructured::new(&x)).unwrap(),
+ checked_arbitrary::<&[u8]>(&mut Unstructured::new(&x)).unwrap(),
+ &[1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3]
+ );
+ assert_eq!(
+ checked_arbitrary::<Vec<u8>>(&mut Unstructured::new(&x)).unwrap(),
&[2, 4, 6, 8, 1]
);
assert_eq!(
- Vec::<u32>::arbitrary(&mut Unstructured::new(&x)).unwrap(),
+ checked_arbitrary::<Vec<u32>>(&mut Unstructured::new(&x)).unwrap(),
&[84148994]
);
assert_eq!(
- String::arbitrary(&mut Unstructured::new(&x)).unwrap(),
- "\x01\x02\x03\x04\x05\x06\x07\x08"
+ checked_arbitrary::<String>(&mut Unstructured::new(&x)).unwrap(),
+ "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x01\x02\x03"
);
}
@@ -1167,17 +1257,30 @@ mod test {
fn arbitrary_take_rest() {
let x = [1, 2, 3, 4];
assert_eq!(
- Vec::<u8>::arbitrary_take_rest(Unstructured::new(&x)).unwrap(),
+ checked_arbitrary_take_rest::<&[u8]>(Unstructured::new(&x)).unwrap(),
+ &[1, 2, 3, 4]
+ );
+ assert_eq!(
+ checked_arbitrary_take_rest::<Vec<u8>>(Unstructured::new(&x)).unwrap(),
&[1, 2, 3, 4]
);
assert_eq!(
- Vec::<u32>::arbitrary_take_rest(Unstructured::new(&x)).unwrap(),
+ checked_arbitrary_take_rest::<Vec<u32>>(Unstructured::new(&x)).unwrap(),
&[0x4030201]
);
assert_eq!(
- String::arbitrary_take_rest(Unstructured::new(&x)).unwrap(),
+ checked_arbitrary_take_rest::<String>(Unstructured::new(&x)).unwrap(),
"\x01\x02\x03\x04"
);
+
+ assert_eq!(
+ checked_arbitrary_take_rest::<&[u8]>(Unstructured::new(&[])).unwrap(),
+ &[]
+ );
+ assert_eq!(
+ checked_arbitrary_take_rest::<Vec<u8>>(Unstructured::new(&[])).unwrap(),
+ &[]
+ );
}
#[test]
@@ -1186,9 +1289,54 @@ mod test {
(7, Some(7)),
<(bool, u16, i32) as Arbitrary<'_>>::size_hint(0)
);
- assert_eq!(
- (1 + mem::size_of::<usize>(), None),
- <(u8, Vec<u8>) as Arbitrary>::size_hint(0)
- );
+ assert_eq!((1, None), <(u8, Vec<u8>) as Arbitrary>::size_hint(0));
}
}
+
+/// Multiple conflicting arbitrary attributes are used on the same field:
+/// ```compile_fail
+/// #[derive(::arbitrary::Arbitrary)]
+/// struct Point {
+/// #[arbitrary(value = 2)]
+/// #[arbitrary(value = 2)]
+/// x: i32,
+/// }
+/// ```
+///
+/// An unknown attribute:
+/// ```compile_fail
+/// #[derive(::arbitrary::Arbitrary)]
+/// struct Point {
+/// #[arbitrary(unknown_attr)]
+/// x: i32,
+/// }
+/// ```
+///
+/// An unknown attribute with a value:
+/// ```compile_fail
+/// #[derive(::arbitrary::Arbitrary)]
+/// struct Point {
+/// #[arbitrary(unknown_attr = 13)]
+/// x: i32,
+/// }
+/// ```
+///
+/// `value` without RHS:
+/// ```compile_fail
+/// #[derive(::arbitrary::Arbitrary)]
+/// struct Point {
+/// #[arbitrary(value)]
+/// x: i32,
+/// }
+/// ```
+///
+/// `with` without RHS:
+/// ```compile_fail
+/// #[derive(::arbitrary::Arbitrary)]
+/// struct Point {
+/// #[arbitrary(with)]
+/// x: i32,
+/// }
+/// ```
+#[cfg(all(doctest, feature = "derive"))]
+pub struct CompileFailTests;
diff --git a/src/unstructured.rs b/src/unstructured.rs
index ff3e1f3..0bfdff2 100644
--- a/src/unstructured.rs
+++ b/src/unstructured.rs
@@ -216,7 +216,7 @@ impl<'a> Unstructured<'a> {
{
let byte_size = self.arbitrary_byte_size()?;
let (lower, upper) = <ElementType as Arbitrary>::size_hint(0);
- let elem_size = upper.unwrap_or_else(|| lower * 2);
+ let elem_size = upper.unwrap_or(lower * 2);
let elem_size = std::cmp::max(1, elem_size);
Ok(byte_size / elem_size)
}
@@ -236,19 +236,20 @@ impl<'a> Unstructured<'a> {
// We only consume as many bytes as necessary to cover the entire
// range of the byte string.
- let len = if self.data.len() <= std::u8::MAX as usize + 1 {
+ // Note: We cast to u64 so we don't overflow when checking std::u32::MAX + 4 on 32-bit archs
+ let len = if self.data.len() as u64 <= std::u8::MAX as u64 + 1 {
let bytes = 1;
let max_size = self.data.len() - bytes;
let (rest, for_size) = self.data.split_at(max_size);
self.data = rest;
Self::int_in_range_impl(0..=max_size as u8, for_size.iter().copied())?.0 as usize
- } else if self.data.len() <= std::u16::MAX as usize + 1 {
+ } else if self.data.len() as u64 <= std::u16::MAX as u64 + 2 {
let bytes = 2;
let max_size = self.data.len() - bytes;
let (rest, for_size) = self.data.split_at(max_size);
self.data = rest;
Self::int_in_range_impl(0..=max_size as u16, for_size.iter().copied())?.0 as usize
- } else if self.data.len() <= std::u32::MAX as usize + 1 {
+ } else if self.data.len() as u64 <= std::u32::MAX as u64 + 4 {
let bytes = 4;
let max_size = self.data.len() - bytes;
let (rest, for_size) = self.data.split_at(max_size);
@@ -273,7 +274,7 @@ impl<'a> Unstructured<'a> {
///
/// # Panics
///
- /// Panics if `range.start >= range.end`. That is, the given range must be
+ /// Panics if `range.start > range.end`. That is, the given range must be
/// non-empty.
///
/// # Example
@@ -305,8 +306,8 @@ impl<'a> Unstructured<'a> {
where
T: Int,
{
- let start = range.start();
- let end = range.end();
+ let start = *range.start();
+ let end = *range.end();
assert!(
start <= end,
"`arbitrary::Unstructured::int_in_range` requires a non-empty range"
@@ -315,30 +316,59 @@ impl<'a> Unstructured<'a> {
// When there is only one possible choice, don't waste any entropy from
// the underlying data.
if start == end {
- return Ok((*start, 0));
+ return Ok((start, 0));
}
- let range: T::Widest = end.as_widest() - start.as_widest();
- let mut result = T::Widest::ZERO;
- let mut offset: usize = 0;
+ // From here on out we work with the unsigned representation. All of the
+ // operations performed below work out just as well whether or not `T`
+ // is a signed or unsigned integer.
+ let start = start.to_unsigned();
+ let end = end.to_unsigned();
+
+ let delta = end.wrapping_sub(start);
+ debug_assert_ne!(delta, T::Unsigned::ZERO);
- while offset < mem::size_of::<T>()
- && (range >> T::Widest::from_usize(offset * 8)) > T::Widest::ZERO
+ // Compute an arbitrary integer offset from the start of the range. We
+ // do this by consuming `size_of(T)` bytes from the input to create an
+ // arbitrary integer and then clamping that int into our range bounds
+ // with a modulo operation.
+ let mut arbitrary_int = T::Unsigned::ZERO;
+ let mut bytes_consumed: usize = 0;
+
+ while (bytes_consumed < mem::size_of::<T>())
+ && (delta >> T::Unsigned::from_usize(bytes_consumed * 8)) > T::Unsigned::ZERO
{
- let byte = bytes.next().ok_or(Error::NotEnoughData)?;
- result = (result << 8) | T::Widest::from_u8(byte);
- offset += 1;
- }
+ let byte = match bytes.next() {
+ None => break,
+ Some(b) => b,
+ };
+ bytes_consumed += 1;
- // Avoid division by zero.
- if let Some(range) = range.checked_add(T::Widest::ONE) {
- result = result % range;
+ // Combine this byte into our arbitrary integer, but avoid
+ // overflowing the shift for `u8` and `i8`.
+ arbitrary_int = if mem::size_of::<T>() == 1 {
+ T::Unsigned::from_u8(byte)
+ } else {
+ (arbitrary_int << 8) | T::Unsigned::from_u8(byte)
+ };
}
- Ok((
- T::from_widest(start.as_widest().wrapping_add(result)),
- offset,
- ))
+ let offset = if delta == T::Unsigned::MAX {
+ arbitrary_int
+ } else {
+ arbitrary_int % (delta.checked_add(T::Unsigned::ONE).unwrap())
+ };
+
+ // Finally, we add `start` to our offset from `start` to get the result
+ // actual value within the range.
+ let result = start.wrapping_add(offset);
+
+ // And convert back to our maybe-signed representation.
+ let result = T::from_unsigned(result);
+ debug_assert!(*range.start() <= result);
+ debug_assert!(result <= *range.end());
+
+ Ok((result, bytes_consumed))
}
/// Choose one of the given choices.
@@ -376,11 +406,53 @@ impl<'a> Unstructured<'a> {
/// assert!(result.is_err());
/// ```
pub fn choose<'b, T>(&mut self, choices: &'b [T]) -> Result<&'b T> {
- if choices.is_empty() {
+ let idx = self.choose_index(choices.len())?;
+ Ok(&choices[idx])
+ }
+
+ /// Choose a value in `0..len`.
+ ///
+ /// Returns an error if the `len` is zero.
+ ///
+ /// # Examples
+ ///
+ /// Using Fisher–Yates shuffle shuffle to gerate an arbitrary permutation.
+ ///
+ /// [Fisher–Yates shuffle]: https://en.wikipedia.org/wiki/Fisher–Yates_shuffle
+ ///
+ /// ```
+ /// use arbitrary::Unstructured;
+ ///
+ /// let mut u = Unstructured::new(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]);
+ /// let mut permutation = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
+ /// let mut to_permute = &mut permutation[..];
+ /// while to_permute.len() > 1 {
+ /// let idx = u.choose_index(to_permute.len()).unwrap();
+ /// to_permute.swap(0, idx);
+ /// to_permute = &mut to_permute[1..];
+ /// }
+ ///
+ /// println!("permutation: {:?}", permutation);
+ /// ```
+ ///
+ /// An error is returned if the length is zero:
+ ///
+ /// ```
+ /// use arbitrary::Unstructured;
+ ///
+ /// let mut u = Unstructured::new(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]);
+ /// let array: [i32; 0] = [];
+ ///
+ /// let result = u.choose_index(array.len());
+ ///
+ /// assert!(result.is_err());
+ /// ```
+ pub fn choose_index(&mut self, len: usize) -> Result<usize> {
+ if len == 0 {
return Err(Error::EmptyChoose);
}
- let idx = self.int_in_range(0..=choices.len() - 1)?;
- Ok(&choices[idx])
+ let idx = self.int_in_range(0..=len - 1)?;
+ Ok(idx)
}
/// Generate a boolean according to the given ratio.
@@ -524,7 +596,7 @@ impl<'a> Unstructured<'a> {
/// assert_eq!(remaining, [1, 2, 3]);
/// ```
pub fn take_rest(mut self) -> &'a [u8] {
- mem::replace(&mut self.data, &[])
+ mem::take(&mut self.data)
}
/// Provide an iterator over elements for constructing a collection
@@ -631,20 +703,8 @@ impl<'a> Unstructured<'a> {
) -> Result<()> {
let min = min.unwrap_or(0);
let max = max.unwrap_or(u32::MAX);
- assert!(min <= max);
- for _ in 0..min {
- match f(self)? {
- ControlFlow::Continue(_) => continue,
- ControlFlow::Break(_) => return Ok(()),
- }
- }
-
- for _ in 0..(max - min) {
- let keep_going = self.arbitrary().unwrap_or(false);
- if !keep_going {
- break;
- }
+ for _ in 0..self.int_in_range(min..=max)? {
match f(self)? {
ControlFlow::Continue(_) => continue,
ControlFlow::Break(_) => break,
@@ -718,6 +778,7 @@ impl<'a, ElementType: Arbitrary<'a>> Iterator for ArbitraryTakeRestIter<'a, Elem
/// Don't implement this trait yourself.
pub trait Int:
Copy
+ + std::fmt::Debug
+ PartialOrd
+ Ord
+ ops::Sub<Self, Output = Self>
@@ -727,7 +788,7 @@ pub trait Int:
+ ops::BitOr<Self, Output = Self>
{
#[doc(hidden)]
- type Widest: Int;
+ type Unsigned: Int;
#[doc(hidden)]
const ZERO: Self;
@@ -736,10 +797,7 @@ pub trait Int:
const ONE: Self;
#[doc(hidden)]
- fn as_widest(self) -> Self::Widest;
-
- #[doc(hidden)]
- fn from_widest(w: Self::Widest) -> Self;
+ const MAX: Self;
#[doc(hidden)]
fn from_u8(b: u8) -> Self;
@@ -752,26 +810,28 @@ pub trait Int:
#[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 : $widest:ty ; )* ) => {
+ ( $( $ty:ty : $unsigned_ty: ty ; )* ) => {
$(
impl Int for $ty {
- type Widest = $widest;
+ type Unsigned = $unsigned_ty;
const ZERO: Self = 0;
const ONE: Self = 1;
- fn as_widest(self) -> Self::Widest {
- self as $widest
- }
-
- fn from_widest(w: Self::Widest) -> Self {
- let x = <$ty>::max_value().as_widest();
- (w % x) as Self
- }
+ const MAX: Self = Self::MAX;
fn from_u8(b: u8) -> Self {
b as Self
@@ -788,24 +848,36 @@ macro_rules! impl_int {
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: u128;
- u16: u128;
- u32: u128;
- u64: u128;
+ u8: u8;
+ u16: u16;
+ u32: u32;
+ u64: u64;
u128: u128;
- usize: u128;
- i8: i128;
- i16: i128;
- i32: i128;
- i64: i128;
- i128: i128;
- isize: i128;
+ usize: usize;
+ i8: u8;
+ i16: u16;
+ i32: u32;
+ i64: u64;
+ i128: u128;
+ isize: usize;
}
#[cfg(test)]
@@ -839,13 +911,121 @@ mod tests {
#[test]
fn int_in_range_uses_minimal_amount_of_bytes() {
- let mut u = Unstructured::new(&[1]);
- u.int_in_range::<u8>(0..=u8::MAX).unwrap();
+ let mut u = Unstructured::new(&[1, 2]);
+ assert_eq!(1, u.int_in_range::<u8>(0..=u8::MAX).unwrap());
+ assert_eq!(u.len(), 1);
- let mut u = Unstructured::new(&[1]);
- u.int_in_range::<u32>(0..=u8::MAX as u32).unwrap();
+ let mut u = Unstructured::new(&[1, 2]);
+ assert_eq!(1, u.int_in_range::<u32>(0..=u8::MAX as u32).unwrap());
+ assert_eq!(u.len(), 1);
let mut u = Unstructured::new(&[1]);
- u.int_in_range::<u32>(0..=u8::MAX as u32 + 1).unwrap_err();
+ assert_eq!(1, 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);
+ }
}
}
diff --git a/tests/derive.rs b/tests/derive.rs
index adf1188..f29d227 100755..100644
--- a/tests/derive.rs
+++ b/tests/derive.rs
@@ -1,4 +1,7 @@
#![cfg(feature = "derive")]
+// Various structs/fields that we are deriving `Arbitrary` for aren't actually
+// used except to exercise the derive.
+#![allow(dead_code)]
use arbitrary::*;
@@ -164,7 +167,7 @@ fn one_lifetime() {
assert_eq!("abc", lifetime.alpha);
let (lower, upper) = <OneLifetime as Arbitrary>::size_hint(0);
- assert_eq!(lower, std::mem::size_of::<usize>());
+ assert_eq!(lower, 0);
assert_eq!(upper, None);
}
@@ -183,6 +186,93 @@ fn two_lifetimes() {
assert_eq!("def", lifetime.beta);
let (lower, upper) = <TwoLifetimes as Arbitrary>::size_hint(0);
- assert_eq!(lower, std::mem::size_of::<usize>() * 2);
+ assert_eq!(lower, 0);
assert_eq!(upper, None);
}
+
+#[test]
+fn recursive_and_empty_input() {
+ // None of the following derives should result in a stack overflow. See
+ // https://github.com/rust-fuzz/arbitrary/issues/107 for details.
+
+ #[derive(Debug, Arbitrary)]
+ enum Nat {
+ Succ(Box<Nat>),
+ Zero,
+ }
+
+ let _ = Nat::arbitrary(&mut Unstructured::new(&[]));
+
+ #[derive(Debug, Arbitrary)]
+ enum Nat2 {
+ Zero,
+ Succ(Box<Nat2>),
+ }
+
+ let _ = Nat2::arbitrary(&mut Unstructured::new(&[]));
+
+ #[derive(Debug, Arbitrary)]
+ struct Nat3 {
+ f: Option<Box<Nat3>>,
+ }
+
+ let _ = Nat3::arbitrary(&mut Unstructured::new(&[]));
+
+ #[derive(Debug, Arbitrary)]
+ struct Nat4(Option<Box<Nat4>>);
+
+ let _ = Nat4::arbitrary(&mut Unstructured::new(&[]));
+
+ #[derive(Debug, Arbitrary)]
+ enum Nat5 {
+ Zero,
+ Succ { f: Box<Nat5> },
+ }
+
+ let _ = Nat5::arbitrary(&mut Unstructured::new(&[]));
+}
+
+#[test]
+fn test_field_attributes() {
+ // A type that DOES NOT implement Arbitrary
+ #[derive(Debug)]
+ struct Weight(u8);
+
+ #[derive(Debug, Arbitrary)]
+ struct Parcel {
+ #[arbitrary(with = arbitrary_weight)]
+ weight: Weight,
+
+ #[arbitrary(default)]
+ width: u8,
+
+ #[arbitrary(value = 2 + 2)]
+ length: u8,
+
+ height: u8,
+
+ #[arbitrary(with = |u: &mut Unstructured| u.int_in_range(0..=100))]
+ price: u8,
+ }
+
+ fn arbitrary_weight(u: &mut Unstructured) -> arbitrary::Result<Weight> {
+ u.int_in_range(45..=56).map(Weight)
+ }
+
+ let parcel: Parcel = arbitrary_from(&[6, 199, 17]);
+
+ // 45 + 6 = 51
+ assert_eq!(parcel.weight.0, 51);
+
+ // u8::default()
+ assert_eq!(parcel.width, 0);
+
+ // 2 + 2 = 4
+ assert_eq!(parcel.length, 4);
+
+ // 199 is the 2nd byte used by arbitrary
+ assert_eq!(parcel.height, 199);
+
+ // 17 is the 3rd byte used by arbitrary
+ assert_eq!(parcel.price, 17);
+}
diff --git a/tests/path.rs b/tests/path.rs
index 15dbbe3..c42ec0a 100644
--- a/tests/path.rs
+++ b/tests/path.rs
@@ -1,4 +1,7 @@
#![cfg(feature = "derive")]
+// Various structs/fields that we are deriving `Arbitrary` for aren't actually
+// used except to show off the derive.
+#![allow(dead_code)]
// Regression test for ensuring the derives work without Arbitrary being imported