diff --git a/.travis.yml b/.travis.yml index 6d272d12..fe6a40c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,10 +13,11 @@ before_script: - rustup target add thumbv7em-none-eabihf # Any target that does not have a standard library will do script: - cargo fmt --all -- --check - - (rustup component add clippy && cargo clippy --all --all-features -- -D clippy::all) || true + - (rustup component add clippy && cargo clippy --all -- -D clippy::all) || true - cargo build - cargo test - - cargo build --no-default-features --target thumbv7em-none-eabihf # Test we can build a platform that does not have std. + - cargo build --no-default-features --features alloc --target thumbv7em-none-eabihf # Test we can build a platform that does not have std. - cargo test --no-default-features --lib --tests # Run no_std tests + - [[ $TRAVIS_RUST_VERSION != "1.31.0" ]] && cargo build --no-default-features --features alloc - cargo build --features unsealed_read_write # The crate should still build when the unsealed_read_write feature is enabled. - cargo build --no-default-features --features unsealed_read_write # The crate should still build when the unsealed_read_write feature is enabled and std disabled. diff --git a/Cargo.toml b/Cargo.toml index 5ffca951..18088d53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,5 +26,8 @@ serde_derive = { version = "1.0.14", default-features = false } [features] default = ["std"] +# Uses `alloc` library and adds support for vector functions with +# `no_std`. +alloc = ["serde/alloc"] std = ["serde/std" ] unsealed_read_write = [] diff --git a/src/de.rs b/src/de.rs index e5ef4dff..69cb4564 100644 --- a/src/de.rs +++ b/src/de.rs @@ -15,11 +15,12 @@ use crate::error::{Error, ErrorCode, Result}; use crate::read::EitherLifetime; #[cfg(feature = "unsealed_read_write")] pub use crate::read::EitherLifetime; -use crate::read::Offset; #[cfg(feature = "std")] -pub use crate::read::{IoRead, SliceRead}; +pub use crate::read::IoRead; +use crate::read::Offset; +#[cfg(any(feature = "std", feature = "alloc"))] +pub use crate::read::SliceRead; pub use crate::read::{MutSliceRead, Read, SliceReadFixed}; - /// Decodes a value from CBOR data in a slice. /// /// # Examples @@ -41,7 +42,7 @@ pub use crate::read::{MutSliceRead, Read, SliceReadFixed}; /// let value: &str = de::from_slice(&v[..]).unwrap(); /// assert_eq!(value, "foobar"); /// ``` -#[cfg(feature = "std")] +#[cfg(any(feature = "std", feature = "alloc"))] pub fn from_slice<'a, T>(slice: &'a [u8]) -> Result where T: de::Deserialize<'a>, @@ -144,7 +145,7 @@ where } } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", feature = "alloc"))] impl<'a> Deserializer> { /// Constructs a `Deserializer` which reads from a slice. /// diff --git a/src/lib.rs b/src/lib.rs index dfca186b..749cdeb9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -142,13 +142,19 @@ //! serde_cbor = { version = "0.10", default-features = false } //! ``` //! +//! Without the `std` feature the functions [from_reader], [from_slice], [to_vec], and [to_writer] +//! are not exported. To export [from_slice] and [to_vec] enable the `alloc` feature. The `alloc` +//! feature uses the [`alloc` library][alloc-lib] and requires at least version 1.36.0 of Rust. +//! +//! [alloc-lib]: https://doc.rust-lang.org/alloc/ +//! //! *Note*: to use derive macros in serde you will need to declare `serde` //! dependency like so: //! ``` toml //! serde = { version = "1.0", default-features = false, features = ["derive"] } //! ``` //! -//! Serialize an object. +//! Serialize an object with `no_std` and without `alloc`. //! ``` rust //! # #[macro_use] extern crate serde_derive; //! # fn main() -> Result<(), serde_cbor::Error> { @@ -258,6 +264,9 @@ #[cfg(all(not(feature = "std"), test))] extern crate std; +#[cfg(feature = "alloc")] +extern crate alloc; + pub mod de; pub mod error; mod read; @@ -270,18 +279,31 @@ pub mod value; // Re-export the [items recommended by serde](https://serde.rs/conventions.html). #[doc(inline)] pub use crate::de::{Deserializer, StreamDeserializer}; + #[doc(inline)] pub use crate::error::{Error, Result}; + #[doc(inline)] pub use crate::ser::Serializer; + // Convenience functions for serialization and deserialization. // These functions are only available in `std` mode. #[cfg(feature = "std")] #[doc(inline)] -pub use crate::de::{from_reader, from_slice}; +pub use crate::de::from_reader; + +#[cfg(any(feature = "std", feature = "alloc"))] +#[doc(inline)] +pub use crate::de::from_slice; + +#[cfg(any(feature = "std", feature = "alloc"))] +#[doc(inline)] +pub use crate::ser::to_vec; + #[cfg(feature = "std")] #[doc(inline)] -pub use crate::ser::{to_vec, to_writer}; +pub use crate::ser::to_writer; + // Re-export the value type like serde_json #[cfg(feature = "std")] #[doc(inline)] diff --git a/src/read.rs b/src/read.rs index 89bbd7f0..512574ef 100644 --- a/src/read.rs +++ b/src/read.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "alloc")] +use alloc::{vec, vec::Vec}; #[cfg(feature = "std")] use core::cmp; use core::mem; @@ -284,7 +286,7 @@ where } /// A CBOR input source that reads from a slice of bytes. -#[cfg(feature = "std")] +#[cfg(any(feature = "std", feature = "alloc"))] #[derive(Debug)] pub struct SliceRead<'a> { slice: &'a [u8], @@ -292,7 +294,7 @@ pub struct SliceRead<'a> { index: usize, } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", feature = "alloc"))] impl<'a> SliceRead<'a> { /// Creates a CBOR input source to read from a slice of bytes. pub fn new(slice: &'a [u8]) -> SliceRead<'a> { @@ -314,7 +316,7 @@ impl<'a> SliceRead<'a> { } } -#[cfg(feature = "std")] +#[cfg(any(feature = "std", feature = "alloc"))] impl<'a> Offset for SliceRead<'a> { #[inline] fn byte_offset(&self) -> usize { @@ -322,10 +324,13 @@ impl<'a> Offset for SliceRead<'a> { } } -#[cfg(all(feature = "std", not(feature = "unsealed_read_write")))] +#[cfg(all( + any(feature = "std", feature = "alloc"), + not(feature = "unsealed_read_write") +))] impl<'a> private::Sealed for SliceRead<'a> {} -#[cfg(feature = "std")] +#[cfg(any(feature = "std", feature = "alloc"))] impl<'a> Read<'a> for SliceRead<'a> { #[inline] fn next(&mut self) -> Result> { diff --git a/src/ser.rs b/src/ser.rs index d827728c..13d6f07b 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -1,5 +1,8 @@ //! Serialize a Rust data structure to CBOR data. +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + #[cfg(feature = "std")] pub use crate::write::IoWrite; pub use crate::write::{SliceWrite, Write}; @@ -12,13 +15,13 @@ use serde::ser::{self, Serialize}; use std::io; /// Serializes a value to a vector. -#[cfg(feature = "std")] +#[cfg(any(feature = "std", feature = "alloc"))] pub fn to_vec(value: &T) -> Result> where T: ser::Serialize, { let mut vec = Vec::new(); - to_writer(&mut vec, value)?; + value.serialize(&mut Serializer::new(&mut vec))?; Ok(vec) } diff --git a/src/write.rs b/src/write.rs index 53934017..94c326ef 100644 --- a/src/write.rs +++ b/src/write.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "alloc")] +use alloc::vec::Vec; #[cfg(not(feature = "std"))] use core::fmt; #[cfg(feature = "std")] @@ -90,17 +92,20 @@ impl Write for IoWrite { #[cfg(all(feature = "std", not(feature = "unsealed_read_write")))] impl private::Sealed for IoWrite where W: io::Write {} -// TODO this should be possible with just alloc -#[cfg(feature = "std")] +#[cfg(any(feature = "std", feature = "alloc"))] impl Write for Vec { - type Error = io::Error; + type Error = error::Error; fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { - io::Write::write_all(self, buf) + self.extend_from_slice(buf); + Ok(()) } } -#[cfg(all(feature = "std", not(feature = "unsealed_read_write")))] +#[cfg(all( + any(feature = "std", feature = "alloc"), + not(feature = "unsealed_read_write") +))] impl private::Sealed for Vec {} #[cfg(not(feature = "std"))]