aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cargo_vcs_info.json7
-rw-r--r--Android.bp62
-rw-r--r--CHANGELOG.md54
-rw-r--r--Cargo.toml28
-rw-r--r--Cargo.toml.orig4
-rw-r--r--METADATA8
-rw-r--r--README.md2
-rw-r--r--TEST_MAPPING19
-rw-r--r--cargo2android.json12
-rw-r--r--patches/Android.bp.patch20
-rw-r--r--src/lib.rs192
-rw-r--r--src/unstructured.rs165
-rwxr-xr-x[-rw-r--r--]tests/derive.rs4
13 files changed, 468 insertions, 109 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index cfe44cd..508adbd 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,6 @@
{
"git": {
- "sha1": "83b852c9bba47359ad33fc5b1f81f29425bc97e8"
- }
-}
+ "sha1": "d0d238d880276fd617c38f7e4712bf40db58aad6"
+ },
+ "path_in_vcs": ""
+} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
index b90737f..83c7cf4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,4 +1,4 @@
-// This file is generated by cargo2android.py --run --dependencies --device --features derive --patch patches/Android.bp.patch.
+// This file is generated by cargo2android.py --config cargo2android.json.
// Do not modify this file as changes will be overridden on upgrade.
package {
@@ -37,21 +37,67 @@ 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",
+ srcs: ["src/lib.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ features: [
+ "derive",
+ "derive_arbitrary",
+ ],
+ proc_macros: ["libderive_arbitrary"],
+}
+
+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",
+ srcs: ["tests/derive.rs"],
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ test_options: {
+ unit_test: true,
+ },
+ edition: "2018",
+ features: [
+ "derive",
+ "derive_arbitrary",
+ ],
+ rustlibs: [
+ "libarbitrary",
+ ],
+ proc_macros: ["libderive_arbitrary"],
+}
+
rust_library_rlib {
name: "libarbitrary",
+ // has rustc warnings
host_supported: true,
crate_name: "arbitrary",
+ cargo_env_compat: true,
+ cargo_pkg_version: "1.1.0",
srcs: ["src/lib.rs"],
edition: "2018",
features: [
"derive",
+ "derive_arbitrary",
],
proc_macros: ["libderive_arbitrary"],
+ apex_available: [
+ "com.android.uwb",
+ ],
+ min_sdk_version: "Tiramisu",
}
-
-// dependent_library ["feature_list"]
-// derive_arbitrary-1.0.0
-// proc-macro2-1.0.26 "default,proc-macro"
-// quote-1.0.9 "default,proc-macro"
-// syn-1.0.68 "clone-impls,default,derive,parsing,printing,proc-macro,quote"
-// unicode-xid-0.2.1 "default"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6542d53..2a7c179 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,60 @@ Released YYYY-MM-DD.
--------------------------------------------------------------------------------
+## 1.1.0
+
+Released 2022-02-09.
+
+### Added
+
+* Added the `Unstructured::ratio` method to generate a boolean that is `true` at
+ the given rate.
+
+* Added the `Unstructured::arbitrary_loop` method to call a function an
+ arbitrary number of times.
+
+--------------------------------------------------------------------------------
+
+## 1.0.3
+
+Released 2021-11-20.
+
+### Fixed
+
+* Fixed documentation for `Unstructured::fill_bytes`. We forgot to update this
+ way back in [#53](https://github.com/rust-fuzz/arbitrary/pull/53) when the
+ behavior changed.
+
+--------------------------------------------------------------------------------
+
+## 1.0.2
+
+Released 2021-08-25.
+
+### Added
+
+* `Arbitrary` impls for `HashMap`s and `HashSet`s with custom `Hasher`s
+ [#87](https://github.com/rust-fuzz/arbitrary/pull/87)
+
+--------------------------------------------------------------------------------
+
+## 1.0.1
+
+Released 2021-05-20.
+
+### Added
+
+* `Arbitrary` impls for `NonZeroX` types [#79](https://github.com/rust-fuzz/arbitrary/pull/79)
+* `Arbitrary` impls for all arrays using const generics [#55](https://github.com/rust-fuzz/arbitrary/pull/55)
+* `Arbitrary` impls for `Ipv4Addr` and `Ipv6Addr` [#84](https://github.com/rust-fuzz/arbitrary/pull/84)
+
+### Fixed
+
+* Use fewer bytes for `Unstructured::int_in_range()` [#80](https://github.com/rust-fuzz/arbitrary/pull/80)
+* Use correct range for `char` generation [#83](https://github.com/rust-fuzz/arbitrary/pull/83)
+
+--------------------------------------------------------------------------------
+
## 1.0.0
Released 2020-02-24.
diff --git a/Cargo.toml b/Cargo.toml
index b57a351..a42a90b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,24 +3,33 @@
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
-# to registry (e.g., crates.io) dependencies
+# to registry (e.g., crates.io) dependencies.
#
-# If you believe there's an error in this file please file an
-# issue against the rust-lang/cargo repository. If you're
-# editing this file be aware that the upstream Cargo.toml
-# will likely look very different (and much more reasonable)
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
name = "arbitrary"
-version = "1.0.0"
-authors = ["The Rust-Fuzz Project Developers", "Nick Fitzgerald <fitzgen@gmail.com>", "Manish Goregaokar <manishsmail@gmail.com>", "Simonas Kazlauskas <arbitrary@kazlauskas.me>", "Brian L. Troutwine <brian@troutwine.us>", "Corey Farwell <coreyf@rwell.org>"]
+version = "1.1.0"
+authors = [
+ "The Rust-Fuzz Project Developers",
+ "Nick Fitzgerald <fitzgen@gmail.com>",
+ "Manish Goregaokar <manishsmail@gmail.com>",
+ "Simonas Kazlauskas <arbitrary@kazlauskas.me>",
+ "Brian L. Troutwine <brian@troutwine.us>",
+ "Corey Farwell <coreyf@rwell.org>",
+]
description = "The trait for generating structured data from unstructured data"
documentation = "https://docs.rs/arbitrary/"
readme = "README.md"
-keywords = ["arbitrary", "testing"]
+keywords = [
+ "arbitrary",
+ "testing",
+]
categories = ["development-tools::testing"]
-license = "MIT/Apache-2.0"
+license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-fuzz/arbitrary/"
[[example]]
@@ -31,6 +40,7 @@ required-features = ["derive"]
name = "derive"
path = "./tests/derive.rs"
required-features = ["derive"]
+
[dependencies.derive_arbitrary]
version = "1.0.0"
optional = true
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index f177ed4..df4e2a1 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "arbitrary"
-version = "1.0.0" # Make sure this matches the derive crate version
+version = "1.1.0" # Make sure this matches the derive crate version
authors = [
"The Rust-Fuzz Project Developers",
"Nick Fitzgerald <fitzgen@gmail.com>",
@@ -14,7 +14,7 @@ edition = "2018"
keywords = ["arbitrary", "testing"]
readme = "README.md"
description = "The trait for generating structured data from unstructured data"
-license = "MIT/Apache-2.0"
+license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-fuzz/arbitrary/"
documentation = "https://docs.rs/arbitrary/"
diff --git a/METADATA b/METADATA
index 1029866..00cf505 100644
--- a/METADATA
+++ b/METADATA
@@ -7,13 +7,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/arbitrary/arbitrary-1.0.0.crate"
+ value: "https://static.crates.io/crates/arbitrary/arbitrary-1.1.0.crate"
}
- version: "1.0.0"
+ version: "1.1.0"
license_type: NOTICE
last_upgrade_date {
- year: 2021
- month: 4
+ year: 2022
+ month: 3
day: 1
}
}
diff --git a/README.md b/README.md
index fce547a..38bd949 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@
## About
-The `Arbitrary` crate lets you construct arbitrary instance of a type.
+The `Arbitrary` crate lets you construct arbitrary instances of a type.
This crate is primarily intended to be combined with a fuzzer like [libFuzzer
and `cargo-fuzz`](https://github.com/rust-fuzz/cargo-fuzz) or
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..707daa8
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,19 @@
+// Generated by update_crate_tests.py for tests that depend on this crate.
+{
+ "presubmit": [
+ {
+ "name": "arbitrary_test_src_lib"
+ },
+ {
+ "name": "arbitrary_test_tests_derive"
+ }
+ ],
+ "presubmit-rust": [
+ {
+ "name": "arbitrary_test_src_lib"
+ },
+ {
+ "name": "arbitrary_test_tests_derive"
+ }
+ ]
+}
diff --git a/cargo2android.json b/cargo2android.json
new file mode 100644
index 0000000..47d3147
--- /dev/null
+++ b/cargo2android.json
@@ -0,0 +1,12 @@
+{
+ "apex_available": [
+ "com.android.uwb"
+ ],
+ "dependencies": true,
+ "device": true,
+ "features": "derive",
+ "force-rlib": true,
+ "min_sdk_version": "Tiramisu",
+ "run": true,
+ "tests": true
+} \ No newline at end of file
diff --git a/patches/Android.bp.patch b/patches/Android.bp.patch
deleted file mode 100644
index f6b7c1e..0000000
--- a/patches/Android.bp.patch
+++ /dev/null
@@ -1,20 +0,0 @@
-diff --git a/Android.bp b/Android.bp
-index fa383fd..7c098db 100644
---- a/Android.bp
-+++ b/Android.bp
-@@ -1,6 +1,6 @@
- // This file is generated by cargo2android.py --run --dependencies --device --features derive --patch patches/Android.bp.patch.
-
--rust_library {
-+rust_library_rlib {
- name: "libarbitrary",
- host_supported: true,
- crate_name: "arbitrary",
-@@ -8,7 +8,6 @@ rust_library {
- edition: "2018",
- features: [
- "derive",
-- "derive_arbitrary",
- ],
- proc_macros: ["libderive_arbitrary"],
- }
diff --git a/src/lib.rs b/src/lib.rs
index 48898a8..7b791ab 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -37,12 +37,16 @@ pub mod size_hint;
use core::cell::{Cell, RefCell, UnsafeCell};
use core::iter;
use core::mem;
+use core::num::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize};
+use core::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize};
use core::ops::{Range, RangeBounds, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};
use core::str;
use core::time::Duration;
use std::borrow::{Cow, ToOwned};
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
use std::ffi::{CString, OsString};
+use std::hash::BuildHasher;
+use std::net::{Ipv4Addr, Ipv6Addr};
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize};
@@ -95,7 +99,7 @@ use std::sync::{Arc, Mutex};
/// Implementing `Arbitrary` mostly involves nested calls to other `Arbitrary`
/// arbitrary implementations for each of your `struct` or `enum`'s members. But
/// sometimes you need some amount of raw data, or you need to generate a
-/// variably-sized collection type, or you something of that sort. The
+/// variably-sized collection type, or something of that sort. The
/// [`Unstructured`][crate::Unstructured] type helps you with these tasks.
///
/// ```
@@ -204,7 +208,7 @@ pub trait Arbitrary<'a>: Sized {
/// not a recursive type, or your implementation is not transitively calling
/// any other `size_hint` methods, you can ignore the `depth` parameter.
/// Note that if you are implementing `Arbitrary` for a generic type, you
- /// cannot guarantee the lack of type recrusion!
+ /// cannot guarantee the lack of type recursion!
///
/// Otherwise, you need to use
/// [`arbitrary::size_hint::recursion_guard(depth)`][crate::size_hint::recursion_guard]
@@ -345,12 +349,13 @@ impl_arbitrary_for_floats! {
impl<'a> Arbitrary<'a> for char {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
use std::char;
- const CHAR_END: u32 = 0x0011_000;
+ // 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) {
- return Ok(c);
+ Ok(c)
} else {
// We found a surrogate, wrap and try again
c -= SURROGATES_START;
@@ -571,61 +576,91 @@ macro_rules! arbitrary_tuple {
}
arbitrary_tuple!(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z);
-macro_rules! arbitrary_array {
- {$n:expr, ($t:ident, $a:ident) $(($ts:ident, $as:ident))*} => {
- arbitrary_array!{($n - 1), $(($ts, $as))*}
-
- impl<'a, T: Arbitrary<'a>> Arbitrary<'a> for [T; $n] {
- fn arbitrary(u: &mut Unstructured<'a>) -> Result<[T; $n]> {
- Ok([
- Arbitrary::arbitrary(u)?,
- $(<$ts as Arbitrary>::arbitrary(u)?),*
- ])
- }
-
- #[allow(unused_mut)]
- fn arbitrary_take_rest(mut u: Unstructured<'a>) -> Result<[T; $n]> {
- $(let $as = $ts::arbitrary(&mut u)?;)*
- let last = Arbitrary::arbitrary_take_rest(u)?;
+// Helper to safely create arrays since the standard library doesn't
+// provide one yet. Shouldn't be necessary in the future.
+struct ArrayGuard<T, const N: usize> {
+ dst: *mut T,
+ initialized: usize,
+}
- Ok([
- $($as,)* last
- ])
- }
+impl<T, const N: usize> Drop for ArrayGuard<T, N> {
+ fn drop(&mut self) {
+ debug_assert!(self.initialized <= N);
+ let initialized_part = core::ptr::slice_from_raw_parts_mut(self.dst, self.initialized);
+ unsafe {
+ core::ptr::drop_in_place(initialized_part);
+ }
+ }
+}
- #[inline]
- fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::and_all(&[
- <$t as Arbitrary>::size_hint(depth),
- $( <$ts as Arbitrary>::size_hint(depth) ),*
- ])
- }
+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>,
+{
+ 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,
};
- ($n: expr,) => {};
+ 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);
+ Ok(array.assume_init())
+ }
}
-impl<'a, T: Arbitrary<'a>> Arbitrary<'a> for [T; 0] {
- fn arbitrary(_: &mut Unstructured<'a>) -> Result<[T; 0]> {
- Ok([])
+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))
}
- fn arbitrary_take_rest(_: Unstructured<'a>) -> Result<[T; 0]> {
- Ok([])
+ #[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)?;
+ }
+ Ok(array)
}
#[inline]
- fn size_hint(_: usize) -> (usize, Option<usize>) {
- crate::size_hint::and_all(&[])
+ fn size_hint(d: usize) -> (usize, Option<usize>) {
+ crate::size_hint::and_all(&create_array::<_, (usize, Option<usize>), N>(|_| {
+ <T as Arbitrary>::size_hint(d)
+ }))
}
}
-arbitrary_array! { 32, (T, a) (T, b) (T, c) (T, d) (T, e) (T, f) (T, g) (T, h)
-(T, i) (T, j) (T, k) (T, l) (T, m) (T, n) (T, o) (T, p)
-(T, q) (T, r) (T, s) (T, u) (T, v) (T, w) (T, x) (T, y)
-(T, z) (T, aa) (T, ab) (T, ac) (T, ad) (T, ae) (T, af)
-(T, ag) }
-
impl<'a> Arbitrary<'a> for &'a [u8] {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
let len = u.arbitrary_len::<u8>()?;
@@ -702,8 +737,8 @@ impl<'a, A: Arbitrary<'a> + Ord> Arbitrary<'a> for BinaryHeap<A> {
}
}
-impl<'a, K: Arbitrary<'a> + Eq + ::std::hash::Hash, V: Arbitrary<'a>> Arbitrary<'a>
- for HashMap<K, V>
+impl<'a, K: Arbitrary<'a> + Eq + ::std::hash::Hash, V: Arbitrary<'a>, S: BuildHasher + Default>
+ Arbitrary<'a> for HashMap<K, V, S>
{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
u.arbitrary_iter()?.collect()
@@ -719,7 +754,9 @@ impl<'a, K: Arbitrary<'a> + Eq + ::std::hash::Hash, V: Arbitrary<'a>> Arbitrary<
}
}
-impl<'a, A: Arbitrary<'a> + Eq + ::std::hash::Hash> Arbitrary<'a> for HashSet<A> {
+impl<'a, A: Arbitrary<'a> + Eq + ::std::hash::Hash, S: BuildHasher + Default> Arbitrary<'a>
+ for HashSet<A, S>
+{
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
u.arbitrary_iter()?.collect()
}
@@ -872,7 +909,7 @@ impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Box<A> {
#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::recursion_guard(depth, |depth| <A as Arbitrary>::size_hint(depth))
+ crate::size_hint::recursion_guard(depth, <A as Arbitrary>::size_hint)
}
}
@@ -918,7 +955,7 @@ impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Arc<A> {
#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::recursion_guard(depth, |depth| <A as Arbitrary>::size_hint(depth))
+ crate::size_hint::recursion_guard(depth, <A as Arbitrary>::size_hint)
}
}
@@ -929,7 +966,7 @@ impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for Rc<A> {
#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
- crate::size_hint::recursion_guard(depth, |depth| <A as Arbitrary>::size_hint(depth))
+ crate::size_hint::recursion_guard(depth, <A as Arbitrary>::size_hint)
}
}
@@ -1010,6 +1047,59 @@ impl<'a, A: Arbitrary<'a>> Arbitrary<'a> for ::std::num::Wrapping<A> {
}
}
+macro_rules! implement_nonzero_int {
+ ($nonzero:ty, $int:ty) => {
+ 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),
+ }
+ }
+
+ #[inline]
+ fn size_hint(depth: usize) -> (usize, Option<usize>) {
+ <$int as Arbitrary<'a>>::size_hint(depth)
+ }
+ }
+ };
+}
+
+implement_nonzero_int! { NonZeroI8, i8 }
+implement_nonzero_int! { NonZeroI16, i16 }
+implement_nonzero_int! { NonZeroI32, i32 }
+implement_nonzero_int! { NonZeroI64, i64 }
+implement_nonzero_int! { NonZeroI128, i128 }
+implement_nonzero_int! { NonZeroIsize, isize }
+implement_nonzero_int! { NonZeroU8, u8 }
+implement_nonzero_int! { NonZeroU16, u16 }
+implement_nonzero_int! { NonZeroU32, u32 }
+implement_nonzero_int! { NonZeroU64, u64 }
+implement_nonzero_int! { NonZeroU128, u128 }
+implement_nonzero_int! { NonZeroUsize, usize }
+
+impl<'a> Arbitrary<'a> for Ipv4Addr {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Ok(Ipv4Addr::from(u32::arbitrary(u)?))
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (4, Some(4))
+ }
+}
+
+impl<'a> Arbitrary<'a> for Ipv6Addr {
+ fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
+ Ok(Ipv6Addr::from(u128::arbitrary(u)?))
+ }
+
+ #[inline]
+ fn size_hint(_depth: usize) -> (usize, Option<usize>) {
+ (16, Some(16))
+ }
+}
+
#[cfg(test)]
mod test {
use super::*;
diff --git a/src/unstructured.rs b/src/unstructured.rs
index 9e2f497..ff3e1f3 100644
--- a/src/unstructured.rs
+++ b/src/unstructured.rs
@@ -10,6 +10,7 @@
use crate::{Arbitrary, Error, Result};
use std::marker::PhantomData;
+use std::ops::ControlFlow;
use std::{mem, ops};
/// A source of unstructured data.
@@ -221,7 +222,7 @@ impl<'a> Unstructured<'a> {
}
fn arbitrary_byte_size(&mut self) -> Result<usize> {
- if self.data.len() == 0 {
+ if self.data.is_empty() {
Ok(0)
} else if self.data.len() == 1 {
self.data = &[];
@@ -322,7 +323,7 @@ impl<'a> Unstructured<'a> {
let mut offset: usize = 0;
while offset < mem::size_of::<T>()
- && (range >> T::Widest::from_usize(offset)) > T::Widest::ZERO
+ && (range >> T::Widest::from_usize(offset * 8)) > T::Widest::ZERO
{
let byte = bytes.next().ok_or(Error::NotEnoughData)?;
result = (result << 8) | T::Widest::from_u8(byte);
@@ -382,6 +383,41 @@ impl<'a> Unstructured<'a> {
Ok(&choices[idx])
}
+ /// Generate a boolean according to the given ratio.
+ ///
+ /// # Panics
+ ///
+ /// Panics when the numerator and denominator do not meet these constraints:
+ ///
+ /// * `0 < numerator <= denominator`
+ ///
+ /// # Example
+ ///
+ /// Generate a boolean that is `true` five sevenths of the time:
+ ///
+ /// ```
+ /// # fn foo() -> arbitrary::Result<()> {
+ /// use arbitrary::Unstructured;
+ ///
+ /// # let my_data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
+ /// let mut u = Unstructured::new(&my_data);
+ ///
+ /// if u.ratio(5, 7)? {
+ /// // Take this branch 5/7 of the time.
+ /// }
+ /// # Ok(())
+ /// # }
+ /// ```
+ pub fn ratio<T>(&mut self, numerator: T, denominator: T) -> Result<bool>
+ where
+ T: Int,
+ {
+ assert!(T::ZERO < numerator);
+ assert!(numerator <= denominator);
+ let x = self.int_in_range(T::ONE..=denominator)?;
+ Ok(x <= numerator)
+ }
+
/// Fill a `buffer` with bytes from the underlying raw data.
///
/// This should only be called within an `Arbitrary` implementation. This is
@@ -389,8 +425,8 @@ impl<'a> Unstructured<'a> {
/// `Arbitrary` implementations like `<Vec<u8>>::arbitrary` and
/// `String::arbitrary` over using this method directly.
///
- /// If this `Unstructured` does not have enough data to fill the whole
- /// `buffer`, an error is returned.
+ /// If this `Unstructured` does not have enough underlying data to fill the
+ /// whole `buffer`, it pads the buffer out with zeros.
///
/// # Example
///
@@ -400,16 +436,21 @@ impl<'a> Unstructured<'a> {
/// let mut u = Unstructured::new(&[1, 2, 3, 4]);
///
/// let mut buf = [0; 2];
+ ///
+ /// assert!(u.fill_buffer(&mut buf).is_ok());
+ /// assert_eq!(buf, [1, 2]);
+ ///
/// assert!(u.fill_buffer(&mut buf).is_ok());
+ /// assert_eq!(buf, [3, 4]);
+ ///
/// assert!(u.fill_buffer(&mut buf).is_ok());
+ /// assert_eq!(buf, [0, 0]);
/// ```
pub fn fill_buffer(&mut self, buffer: &mut [u8]) -> Result<()> {
let n = std::cmp::min(buffer.len(), self.data.len());
- for i in 0..n {
- buffer[i] = self.data[i];
- }
- for i in self.data.len()..buffer.len() {
- buffer[i] = 0;
+ buffer[..n].copy_from_slice(&self.data[..n]);
+ for byte in buffer[n..].iter_mut() {
+ *byte = 0;
}
self.data = &self.data[n..];
Ok(())
@@ -518,6 +559,100 @@ impl<'a> Unstructured<'a> {
_marker: PhantomData,
})
}
+
+ /// Call the given function an arbitrary number of times.
+ ///
+ /// The function is given this `Unstructured` so that it can continue to
+ /// generate arbitrary data and structures.
+ ///
+ /// You may optionaly specify minimum and maximum bounds on the number of
+ /// times the function is called.
+ ///
+ /// You may break out of the loop early by returning
+ /// `Ok(std::ops::ControlFlow::Break)`. To continue the loop, return
+ /// `Ok(std::ops::ControlFlow::Continue)`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `min > max`.
+ ///
+ /// # Example
+ ///
+ /// Call a closure that generates an arbitrary type inside a context an
+ /// arbitrary number of times:
+ ///
+ /// ```
+ /// use arbitrary::{Result, Unstructured};
+ /// use std::ops::ControlFlow;
+ ///
+ /// enum Type {
+ /// /// A boolean type.
+ /// Bool,
+ ///
+ /// /// An integer type.
+ /// Int,
+ ///
+ /// /// A list of the `i`th type in this type's context.
+ /// List(usize),
+ /// }
+ ///
+ /// fn arbitrary_types_context(u: &mut Unstructured) -> Result<Vec<Type>> {
+ /// let mut context = vec![];
+ ///
+ /// u.arbitrary_loop(Some(10), Some(20), |u| {
+ /// let num_choices = if context.is_empty() {
+ /// 2
+ /// } else {
+ /// 3
+ /// };
+ /// let ty = match u.int_in_range::<u8>(1..=num_choices)? {
+ /// 1 => Type::Bool,
+ /// 2 => Type::Int,
+ /// 3 => Type::List(u.int_in_range(0..=context.len() - 1)?),
+ /// _ => unreachable!(),
+ /// };
+ /// context.push(ty);
+ /// Ok(ControlFlow::Continue(()))
+ /// })?;
+ ///
+ /// // The number of loop iterations are constrained by the min/max
+ /// // bounds that we provided.
+ /// assert!(context.len() >= 10);
+ /// assert!(context.len() <= 20);
+ ///
+ /// Ok(context)
+ /// }
+ /// ```
+ pub fn arbitrary_loop(
+ &mut self,
+ min: Option<u32>,
+ max: Option<u32>,
+ mut f: impl FnMut(&mut Self) -> Result<ControlFlow<(), ()>>,
+ ) -> 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;
+ }
+ match f(self)? {
+ ControlFlow::Continue(_) => continue,
+ ControlFlow::Break(_) => break,
+ }
+ }
+
+ Ok(())
+ }
}
/// Utility iterator produced by [`Unstructured::arbitrary_iter`]
@@ -701,4 +836,16 @@ mod tests {
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(&[1]);
+ u.int_in_range::<u8>(0..=u8::MAX).unwrap();
+
+ let mut u = Unstructured::new(&[1]);
+ u.int_in_range::<u32>(0..=u8::MAX as u32).unwrap();
+
+ let mut u = Unstructured::new(&[1]);
+ u.int_in_range::<u32>(0..=u8::MAX as u32 + 1).unwrap_err();
+ }
}
diff --git a/tests/derive.rs b/tests/derive.rs
index 9dfbbd5..adf1188 100644..100755
--- a/tests/derive.rs
+++ b/tests/derive.rs
@@ -164,7 +164,7 @@ fn one_lifetime() {
assert_eq!("abc", lifetime.alpha);
let (lower, upper) = <OneLifetime as Arbitrary>::size_hint(0);
- assert_eq!(lower, 8);
+ assert_eq!(lower, std::mem::size_of::<usize>());
assert_eq!(upper, None);
}
@@ -183,6 +183,6 @@ fn two_lifetimes() {
assert_eq!("def", lifetime.beta);
let (lower, upper) = <TwoLifetimes as Arbitrary>::size_hint(0);
- assert_eq!(lower, 16);
+ assert_eq!(lower, std::mem::size_of::<usize>() * 2);
assert_eq!(upper, None);
}