diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1dbac58..55c1573 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -61,3 +61,13 @@ jobs: run: cargo install cargo-fuzz - name: "Fuzz for 3 minutes" run: cargo fuzz run int_in_range -- -max_total_time=$((3 * 60)) + no_std_build: + name: no_std Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: rustup update + - run: cargo build --verbose --no-default-features + - run: cargo test --verbose --no-default-features + - run: cargo build --verbose --no-default-features --features=alloc + - run: cargo test --verbose --no-default-features --features=alloc diff --git a/Cargo.toml b/Cargo.toml index 20aa826..02209ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,12 +2,12 @@ name = "arbitrary" version = "1.4.2" # Make sure this matches the derive crate version (not including the patch version) authors = [ - "The Rust-Fuzz Project Developers", - "Nick Fitzgerald ", - "Manish Goregaokar ", - "Simonas Kazlauskas ", - "Brian L. Troutwine ", - "Corey Farwell ", + "The Rust-Fuzz Project Developers", + "Nick Fitzgerald ", + "Manish Goregaokar ", + "Simonas Kazlauskas ", + "Brian L. Troutwine ", + "Corey Farwell ", ] categories = ["development-tools::testing"] edition = "2021" @@ -22,10 +22,15 @@ include = ["Cargo.toml", "CHANGELOG.md", "LICENSE-MIT", "LICENSE-APACHE", "READM [dependencies] derive_arbitrary = { version = "~1.4.0", path = "./derive", optional = true } +rustversion = "1" [features] +default = ["std"] + +alloc = [] # Turn this feature on to enable support for `#[derive(Arbitrary)]`. derive = ["derive_arbitrary"] +std = ["alloc"] [[example]] name = "derive_enum" diff --git a/README.md b/README.md index 5d75f31..5f28c9a 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,12 @@ compile with older versions but that may change in any new patch release. We reserve the right to increment the MSRV on minor releases, however we will strive to only do it deliberately and for good reasons. +### Notes on no_std compatability: + +- When compiling for versions older than **1.81.0** the `std` feature must be enabled. +- When compiling for versions older than **1.64.0** the `impl` for `CString` is not available + with the `no_std` feature. And + ## License Licensed under dual MIT or Apache-2.0 at your choice. diff --git a/fuzz/fuzz_targets/int_in_range.rs b/fuzz/fuzz_targets/int_in_range.rs index 14c07f0..6bedddf 100755 --- a/fuzz/fuzz_targets/int_in_range.rs +++ b/fuzz/fuzz_targets/int_in_range.rs @@ -1,8 +1,8 @@ #![no_main] use arbitrary::{unstructured::Int, Arbitrary, Result, Unstructured}; +use core::{fmt::Display, ops::RangeInclusive}; use libfuzzer_sys::fuzz_target; -use std::{fmt::Display, ops::RangeInclusive}; fuzz_target!(|data: &[u8]| { fuzz(data).expect("`int_in_range` should never return an error"); diff --git a/src/error.rs b/src/error.rs index 8cdf39b..1dc2d5a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,10 @@ -use std::{error, fmt}; +use core::fmt; + +#[rustversion::before(1.81)] +use std::error; + +#[rustversion::since(1.81)] +use core::error; /// An enumeration of buffer creation errors #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -41,14 +47,14 @@ 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 = std::result::Result; +pub type Result = core::result::Result; #[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 + // 0.1% as `core::result::Result`; instead, let `arbitrary::Result` have an // overridable error type. #[test] fn can_use_custom_error_types_with_result() -> super::Result<(), String> { diff --git a/src/foreign/alloc/borrow.rs b/src/foreign/alloc/borrow.rs index b5b1e22..617afcb 100644 --- a/src/foreign/alloc/borrow.rs +++ b/src/foreign/alloc/borrow.rs @@ -1,6 +1,6 @@ use { crate::{size_hint, Arbitrary, Result, Unstructured}, - std::borrow::{Cow, ToOwned}, + alloc::borrow::{Cow, ToOwned}, }; impl<'a, A> Arbitrary<'a> for Cow<'a, A> diff --git a/src/foreign/alloc/boxed.rs b/src/foreign/alloc/boxed.rs index c3014a3..f397557 100644 --- a/src/foreign/alloc/boxed.rs +++ b/src/foreign/alloc/boxed.rs @@ -1,6 +1,6 @@ use { crate::{size_hint, Arbitrary, Result, Unstructured}, - std::boxed::Box, + alloc::{boxed::Box, string::String}, }; impl<'a, A> Arbitrary<'a> for Box diff --git a/src/foreign/alloc/collections/binary_heap.rs b/src/foreign/alloc/collections/binary_heap.rs index 25a0384..02ef3fc 100644 --- a/src/foreign/alloc/collections/binary_heap.rs +++ b/src/foreign/alloc/collections/binary_heap.rs @@ -1,6 +1,6 @@ use { crate::{Arbitrary, Result, Unstructured}, - std::collections::binary_heap::BinaryHeap, + alloc::collections::binary_heap::BinaryHeap, }; impl<'a, A> Arbitrary<'a> for BinaryHeap diff --git a/src/foreign/alloc/collections/btree_map.rs b/src/foreign/alloc/collections/btree_map.rs index 21b93a4..65790c1 100644 --- a/src/foreign/alloc/collections/btree_map.rs +++ b/src/foreign/alloc/collections/btree_map.rs @@ -1,6 +1,6 @@ use { crate::{Arbitrary, Result, Unstructured}, - std::collections::btree_map::BTreeMap, + alloc::collections::btree_map::BTreeMap, }; impl<'a, K, V> Arbitrary<'a> for BTreeMap diff --git a/src/foreign/alloc/collections/btree_set.rs b/src/foreign/alloc/collections/btree_set.rs index 8c6e92f..b862a0f 100644 --- a/src/foreign/alloc/collections/btree_set.rs +++ b/src/foreign/alloc/collections/btree_set.rs @@ -1,6 +1,6 @@ use { crate::{Arbitrary, Result, Unstructured}, - std::collections::btree_set::BTreeSet, + alloc::collections::btree_set::BTreeSet, }; impl<'a, A> Arbitrary<'a> for BTreeSet diff --git a/src/foreign/alloc/collections/linked_list.rs b/src/foreign/alloc/collections/linked_list.rs index 6bf2e98..7bb6425 100644 --- a/src/foreign/alloc/collections/linked_list.rs +++ b/src/foreign/alloc/collections/linked_list.rs @@ -1,6 +1,6 @@ use { crate::{Arbitrary, Result, Unstructured}, - std::collections::linked_list::LinkedList, + alloc::collections::linked_list::LinkedList, }; impl<'a, A> Arbitrary<'a> for LinkedList diff --git a/src/foreign/alloc/collections/vec_deque.rs b/src/foreign/alloc/collections/vec_deque.rs index 40e0413..44a37b6 100644 --- a/src/foreign/alloc/collections/vec_deque.rs +++ b/src/foreign/alloc/collections/vec_deque.rs @@ -1,6 +1,6 @@ use { crate::{Arbitrary, Result, Unstructured}, - std::collections::vec_deque::VecDeque, + alloc::collections::vec_deque::VecDeque, }; impl<'a, A> Arbitrary<'a> for VecDeque diff --git a/src/foreign/alloc/ffi/c_str.rs b/src/foreign/alloc/ffi/c_str.rs index a1b2383..ec213ec 100644 --- a/src/foreign/alloc/ffi/c_str.rs +++ b/src/foreign/alloc/ffi/c_str.rs @@ -1,6 +1,10 @@ +#[rustversion::since(1.64)] +use alloc::ffi::CString; +#[rustversion::before(1.64)] +use std::ffi::CString; use { crate::{Arbitrary, Result, Unstructured}, - std::ffi::CString, + alloc::vec::Vec, }; impl<'a> Arbitrary<'a> for CString { diff --git a/src/foreign/alloc/rc.rs b/src/foreign/alloc/rc.rs index 6d58167..2243be4 100644 --- a/src/foreign/alloc/rc.rs +++ b/src/foreign/alloc/rc.rs @@ -1,6 +1,6 @@ use { crate::{size_hint, Arbitrary, Result, Unstructured}, - std::rc::Rc, + alloc::rc::Rc, }; impl<'a, A> Arbitrary<'a> for Rc diff --git a/src/foreign/alloc/string.rs b/src/foreign/alloc/string.rs index a579784..47ea9eb 100644 --- a/src/foreign/alloc/string.rs +++ b/src/foreign/alloc/string.rs @@ -1,6 +1,6 @@ use { crate::{Arbitrary, Result, Unstructured}, - std::string::String, + alloc::string::String, }; impl<'a> Arbitrary<'a> for String { diff --git a/src/foreign/alloc/sync.rs b/src/foreign/alloc/sync.rs index c8ca1db..64d2dcd 100644 --- a/src/foreign/alloc/sync.rs +++ b/src/foreign/alloc/sync.rs @@ -1,6 +1,6 @@ use { crate::{size_hint, Arbitrary, Result, Unstructured}, - std::sync::Arc, + alloc::sync::Arc, }; impl<'a, A> Arbitrary<'a> for Arc diff --git a/src/foreign/alloc/vec.rs b/src/foreign/alloc/vec.rs index 63313ba..2985712 100644 --- a/src/foreign/alloc/vec.rs +++ b/src/foreign/alloc/vec.rs @@ -1,6 +1,6 @@ use { crate::{Arbitrary, Result, Unstructured}, - std::vec::Vec, + alloc::vec::Vec, }; impl<'a, A> Arbitrary<'a> for Vec diff --git a/src/foreign/mod.rs b/src/foreign/mod.rs index b1c42be..3868936 100644 --- a/src/foreign/mod.rs +++ b/src/foreign/mod.rs @@ -2,6 +2,8 @@ //! //! [`Arbitrary`]: crate::Arbitrary +#[cfg(feature = "alloc")] mod alloc; mod core; +#[cfg(feature = "std")] mod std; diff --git a/src/foreign/std/collections/hash_map.rs b/src/foreign/std/collections/hash_map.rs index d2e77af..85afe3a 100644 --- a/src/foreign/std/collections/hash_map.rs +++ b/src/foreign/std/collections/hash_map.rs @@ -1,9 +1,7 @@ use { crate::{Arbitrary, Result, Unstructured}, - std::{ - collections::hash_map::HashMap, - hash::{BuildHasher, Hash}, - }, + core::hash::{BuildHasher, Hash}, + std::collections::hash_map::HashMap, }; impl<'a, K, V, S> Arbitrary<'a> for HashMap diff --git a/src/foreign/std/collections/hash_set.rs b/src/foreign/std/collections/hash_set.rs index 5cb63d2..07c4c8a 100644 --- a/src/foreign/std/collections/hash_set.rs +++ b/src/foreign/std/collections/hash_set.rs @@ -1,9 +1,7 @@ use { crate::{Arbitrary, Result, Unstructured}, - std::{ - collections::hash_set::HashSet, - hash::{BuildHasher, Hash}, - }, + core::hash::{BuildHasher, Hash}, + std::collections::hash_set::HashSet, }; impl<'a, A, S> Arbitrary<'a> for HashSet diff --git a/src/lib.rs b/src/lib.rs index 8b28c69..4192335 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,7 @@ //! [`Arbitrary`] trait's documentation for details on //! automatically deriving, implementing, and/or using the trait. +#![cfg_attr(not(any(feature = "std", test)), no_std)] #![deny(bad_style)] #![deny(missing_docs)] #![deny(future_incompatible)] @@ -22,6 +23,9 @@ #![deny(rust_2018_idioms)] #![deny(unused)] +#[cfg(feature = "alloc")] +extern crate alloc; + mod error; mod foreign; pub mod size_hint; @@ -49,8 +53,12 @@ impl core::fmt::Display for MaxRecursionReached { } } +#[rustversion::before(1.81)] impl std::error::Error for MaxRecursionReached {} +#[rustversion::since(1.81)] +impl core::error::Error for MaxRecursionReached {} + /// Generate arbitrary structured values from raw, unstructured data. /// /// The `Arbitrary` trait allows you to generate valid structured values, like @@ -122,9 +130,9 @@ impl std::error::Error for MaxRecursionReached {} /// /// ``` /// # #[cfg(feature = "derive")] mod foo { -/// # pub struct MyCollection { _t: std::marker::PhantomData } +/// # pub struct MyCollection { _t: core::marker::PhantomData } /// # impl MyCollection { -/// # pub fn new() -> Self { MyCollection { _t: std::marker::PhantomData } } +/// # pub fn new() -> Self { MyCollection { _t: core::marker::PhantomData } } /// # pub fn insert(&mut self, element: T) {} /// # } /// use arbitrary::{Arbitrary, Result, Unstructured}; @@ -554,7 +562,7 @@ pub struct CompileFailTests; // Support for `#[derive(Arbitrary)]`. #[doc(hidden)] -#[cfg(feature = "derive")] +#[cfg(all(feature = "derive", feature = "std"))] pub mod details { use super::*; diff --git a/src/size_hint.rs b/src/size_hint.rs index 95707ee..9f73346 100644 --- a/src/size_hint.rs +++ b/src/size_hint.rs @@ -67,10 +67,10 @@ pub fn and_all(hints: &[(usize, Option)]) -> (usize, Option) { /// `lhs` and `rhs` size hints. #[inline] pub fn or(lhs: (usize, Option), rhs: (usize, Option)) -> (usize, Option) { - let lower = std::cmp::min(lhs.0, rhs.0); + let lower = core::cmp::min(lhs.0, rhs.0); let upper = lhs .1 - .and_then(|lhs| rhs.1.map(|rhs| std::cmp::max(lhs, rhs))); + .and_then(|lhs| rhs.1.map(|rhs| core::cmp::max(lhs, rhs))); (lower, upper) } diff --git a/src/tests.rs b/src/tests.rs index 7746715..69c5c0e 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,6 +1,9 @@ +#[cfg(feature = "alloc")] +use alloc::{rc::Rc, string::String, sync::Arc, vec::Vec}; use { super::{Arbitrary, Result, Unstructured}, - std::{collections::HashSet, fmt::Debug, hash::Hash, rc::Rc, sync::Arc}, + core::{fmt::Debug, hash::Hash}, + std::collections::HashSet, }; /// Assert that the given expected values are all generated. @@ -154,6 +157,7 @@ fn arbitrary_take_rest_for_bytes() { } #[test] +#[cfg(feature = "alloc")] fn arbitrary_for_vec_u8() { assert_generates::>([ vec![], @@ -175,6 +179,7 @@ fn arbitrary_for_vec_u8() { } #[test] +#[cfg(feature = "alloc")] fn arbitrary_for_vec_vec_u8() { assert_generates::>>([ vec![], @@ -193,6 +198,7 @@ fn arbitrary_for_vec_vec_u8() { } #[test] +#[cfg(feature = "alloc")] fn arbitrary_for_vec_vec_vec_u8() { assert_generates::>>>([ vec![], @@ -217,11 +223,13 @@ fn arbitrary_for_vec_vec_vec_u8() { } #[test] +#[cfg(feature = "alloc")] fn arbitrary_for_string() { assert_generates::(["".into(), "a".into(), "aa".into(), "aaa".into()]); } #[test] +#[cfg(feature = "alloc")] 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, 12, @@ -257,6 +265,7 @@ fn arbitrary_collection() { } #[test] +#[cfg(feature = "alloc")] fn arbitrary_take_rest() { // Basic examples let x = [1, 2, 3, 4]; @@ -307,6 +316,7 @@ fn arbitrary_take_rest() { } #[test] +#[cfg(feature = "alloc")] fn size_hint_for_tuples() { assert_eq!( (7, Some(7)), diff --git a/src/unstructured.rs b/src/unstructured.rs index 2b5e043..edccef2 100644 --- a/src/unstructured.rs +++ b/src/unstructured.rs @@ -9,9 +9,9 @@ //! Wrappers around raw, unstructured bytes. use crate::{Arbitrary, Error, Result}; -use std::marker::PhantomData; -use std::ops::ControlFlow; -use std::{mem, ops}; +use core::marker::PhantomData; +use core::ops::ControlFlow; +use core::{mem, ops}; /// A source of unstructured data. /// @@ -186,9 +186,9 @@ impl<'a> Unstructured<'a> { /// /// ``` /// use arbitrary::{Arbitrary, Result, Unstructured}; - /// # pub struct MyCollection { _t: std::marker::PhantomData } + /// # pub struct MyCollection { _t: core::marker::PhantomData } /// # impl MyCollection { - /// # pub fn with_capacity(capacity: usize) -> Self { MyCollection { _t: std::marker::PhantomData } } + /// # pub fn with_capacity(capacity: usize) -> Self { MyCollection { _t: core::marker::PhantomData } } /// # pub fn insert(&mut self, element: T) {} /// # } /// @@ -218,7 +218,7 @@ impl<'a> Unstructured<'a> { let byte_size = self.arbitrary_byte_size()?; let (lower, upper) = ::size_hint(0); let elem_size = upper.unwrap_or(lower * 2); - let elem_size = std::cmp::max(1, elem_size); + let elem_size = core::cmp::max(1, elem_size); Ok(byte_size / elem_size) } @@ -575,7 +575,7 @@ impl<'a> Unstructured<'a> { /// 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()); + let n = core::cmp::min(buffer.len(), self.data.len()); buffer[..n].copy_from_slice(&self.data[..n]); for byte in buffer[n..].iter_mut() { *byte = 0; @@ -691,8 +691,8 @@ impl<'a> Unstructured<'a> { /// 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)`. + /// `Ok(core::ops::ControlFlow::Break)`. To continue the loop, return + /// `Ok(core::ops::ControlFlow::Continue)`. /// /// # Panics /// @@ -705,7 +705,7 @@ impl<'a> Unstructured<'a> { /// /// ``` /// use arbitrary::{Result, Unstructured}; - /// use std::ops::ControlFlow; + /// use core::ops::ControlFlow; /// /// enum Type { /// /// A boolean type. @@ -819,7 +819,7 @@ impl<'a, ElementType: Arbitrary<'a>> Iterator for ArbitraryTakeRestIter<'a, Elem /// Don't implement this trait yourself. pub trait Int: Copy - + std::fmt::Debug + + core::fmt::Debug + PartialOrd + Ord + ops::Sub