From ae806af644029218edebfc8d5febc8e2946cca9d Mon Sep 17 00:00:00 2001 From: Corey Richardson Date: Sun, 1 May 2016 23:30:35 +1000 Subject: [PATCH] Enable use in no_std environments These changes are fairly invasive to imports and uses of non-libcore types, but allow for some or none of the freestanding crates (core, rustc_unicode, alloc, collections) to be supported by serde. --- .travis.yml | 4 + README.md | 14 ++ serde/Cargo.toml | 7 +- serde/src/bytes.rs | 292 ++++++++++++++++++--------------- serde/src/de/from_primitive.rs | 10 +- serde/src/de/impls.rs | 93 +++++++++-- serde/src/de/mod.rs | 17 +- serde/src/de/value.rs | 76 ++++++++- serde/src/error.rs | 44 +++++ serde/src/lib.rs | 33 +++- serde/src/ser/impls.rs | 68 +++++++- serde/src/ser/mod.rs | 20 ++- serde/src/utils.rs | 72 ++++++++ 13 files changed, 573 insertions(+), 177 deletions(-) create mode 100644 serde/src/error.rs create mode 100644 serde/src/utils.rs diff --git a/.travis.yml b/.travis.yml index c26851f0..0b7cf31c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,10 @@ script: - (cd serde && travis-cargo build) - (cd serde && travis-cargo test) - (cd serde && travis-cargo --only nightly test -- --features nightly-testing) +- (cd serde && travis-cargo --skip 1.5.0 build -- --no-default-features) +- (cd serde && travis-cargo --only nightly build -- --no-default-features) +- (cd serde && travis-cargo --only nightly build -- --no-default-features --features alloc) +- (cd serde && travis-cargo --only nightly build -- --no-default-features --features collections) - (cd serde_tests && travis-cargo test) - (cd serde_tests && travis-cargo --only nightly test -- --features nightly-testing) - (cd serde_macros && travis-cargo --only nightly test -- --features nightly-testing) diff --git a/README.md b/README.md index 570289a0..7af59589 100644 --- a/README.md +++ b/README.md @@ -718,6 +718,20 @@ Field Annotations: | `#[serde(serialize_with="$path")]` | Call a function `fn(&T, &mut S) -> Result<(), S::Error> where S: Serializer` to serialize this value of type `T` | | `#[serde(deserialize_with="$path")]` | Call a function `fn(&mut D) -> Result where D: Deserializer` to deserialize this value of type `T` | +Using in `no_std` crates +======================== + +The core `serde` package defines a number of features to enable usage in a +variety of freestanding environments. Enable any or none of the following +features, and use `default-features = false` in your `Cargo.toml`: + +- `alloc` (implies `nightly`) +- `collections` (implies `alloc` and `nightly`) +- `std` (default) + +If you only use `default-features = false`, you will receive a stock `no_std` +serde with no support for any of the collection types. + Upgrading from Serde 0.6 ======================== diff --git a/serde/Cargo.toml b/serde/Cargo.toml index cbdfbd5e..528db293 100644 --- a/serde/Cargo.toml +++ b/serde/Cargo.toml @@ -10,8 +10,13 @@ readme = "../README.md" keywords = ["serde", "serialization"] [features] +default = ["std"] + +std = [] nightly = [] -nightly-testing = ["clippy", "nightly"] +alloc = ["nightly"] +collections = ["alloc"] +nightly-testing = ["clippy", "nightly", "std"] [dependencies] clippy = { version = "^0.*", optional = true } diff --git a/serde/src/bytes.rs b/serde/src/bytes.rs index 21f6af0e..f83ab8dd 100644 --- a/serde/src/bytes.rs +++ b/serde/src/bytes.rs @@ -1,11 +1,15 @@ //! Helper module to enable serializing bytes more efficiently -use std::ops; -use std::fmt; -use std::ascii; +use core::{ops, fmt, char, iter, slice}; +use core::fmt::Write; use ser; -use de; + +#[cfg(any(feature = "std", feature = "collections"))] +pub use self::bytebuf::{ByteBuf, ByteBufVisitor}; + +#[cfg(feature = "collections")] +use collections::Vec; /////////////////////////////////////////////////////////////////////////////// @@ -17,7 +21,11 @@ pub struct Bytes<'a> { impl<'a> fmt::Debug for Bytes<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "b\"{}\"", escape_bytestring(self.bytes)) + try!(f.write_str("b\"")); + for c in escape_bytestring(self.bytes) { + try!(f.write_char(c)); + } + f.write_char('"') } } @@ -29,6 +37,7 @@ impl<'a> From<&'a [u8]> for Bytes<'a> { } } +#[cfg(any(feature = "std", feature = "collections"))] impl<'a> From<&'a Vec> for Bytes<'a> { fn from(bytes: &'a Vec) -> Self { Bytes { @@ -60,157 +69,172 @@ impl<'a> ser::Serialize for Bytes<'a> { /////////////////////////////////////////////////////////////////////////////// -/// `ByteBuf` wraps a `Vec` and serializes as a byte array. -#[derive(Clone, Default, Eq, Hash, PartialEq, PartialOrd, Ord)] -pub struct ByteBuf { - bytes: Vec, -} +#[cfg(any(feature = "std", feature = "collections"))] +mod bytebuf { + use core::ops; + use core::fmt; + use core::fmt::Write; -impl ByteBuf { - /// Construct a new, empty `ByteBuf`. - pub fn new() -> Self { - ByteBuf { - bytes: Vec::new(), + use ser; + use de; + + #[cfg(feature = "collections")] + use collections::Vec; + + /// `ByteBuf` wraps a `Vec` and serializes as a byte array. + #[derive(Clone, Default, Eq, Hash, PartialEq, PartialOrd, Ord)] + pub struct ByteBuf { + bytes: Vec, + } + + impl ByteBuf { + /// Construct a new, empty `ByteBuf`. + pub fn new() -> Self { + ByteBuf { + bytes: Vec::new(), + } + } + + /// Construct a new, empty `ByteBuf` with the specified capacity. + pub fn with_capacity(cap: usize) -> Self { + ByteBuf { + bytes: Vec::with_capacity(cap) + } } } - /// Construct a new, empty `ByteBuf` with the specified capacity. - pub fn with_capacity(cap: usize) -> Self { - ByteBuf { - bytes: Vec::with_capacity(cap) + impl fmt::Debug for ByteBuf { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(f.write_str("b\"")); + for c in super::escape_bytestring(self.bytes.as_ref()) { + try!(f.write_char(c)); + } + f.write_char('"') } } -} -impl fmt::Debug for ByteBuf { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "b\"{}\"", escape_bytestring(self.bytes.as_ref())) - } -} - -impl Into> for ByteBuf { - fn into(self) -> Vec { - self.bytes - } -} - -impl From> for ByteBuf { - fn from(bytes: Vec) -> Self { - ByteBuf { - bytes: bytes, + impl Into> for ByteBuf { + fn into(self) -> Vec { + self.bytes } } -} -impl AsRef> for ByteBuf { - fn as_ref(&self) -> &Vec { - &self.bytes - } -} - -impl AsRef<[u8]> for ByteBuf { - fn as_ref(&self) -> &[u8] { - &self.bytes - } -} - -impl AsMut> for ByteBuf { - fn as_mut(&mut self) -> &mut Vec { - &mut self.bytes - } -} - -impl AsMut<[u8]> for ByteBuf { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.bytes - } -} - -impl ops::Deref for ByteBuf { - type Target = [u8]; - - fn deref(&self) -> &[u8] { &self.bytes[..] } -} - -impl ops::DerefMut for ByteBuf { - fn deref_mut(&mut self) -> &mut [u8] { &mut self.bytes[..] } -} - -impl ser::Serialize for ByteBuf { - fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> - where S: ser::Serializer - { - serializer.serialize_bytes(&self) - } -} - -/// This type implements the `serde::de::Visitor` trait for a `ByteBuf`. -pub struct ByteBufVisitor; - -impl de::Visitor for ByteBufVisitor { - type Value = ByteBuf; - - #[inline] - fn visit_unit(&mut self) -> Result - where E: de::Error, - { - Ok(ByteBuf { - bytes: Vec::new(), - }) + impl From> for ByteBuf { + fn from(bytes: Vec) -> Self { + ByteBuf { + bytes: bytes, + } + } } - #[inline] - fn visit_seq(&mut self, mut visitor: V) -> Result - where V: de::SeqVisitor, - { - let (len, _) = visitor.size_hint(); - let mut values = Vec::with_capacity(len); + impl AsRef> for ByteBuf { + fn as_ref(&self) -> &Vec { + &self.bytes + } + } - while let Some(value) = try!(visitor.visit()) { - values.push(value); + impl AsRef<[u8]> for ByteBuf { + fn as_ref(&self) -> &[u8] { + &self.bytes + } + } + + impl AsMut> for ByteBuf { + fn as_mut(&mut self) -> &mut Vec { + &mut self.bytes + } + } + + impl AsMut<[u8]> for ByteBuf { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.bytes + } + } + + impl ops::Deref for ByteBuf { + type Target = [u8]; + + fn deref(&self) -> &[u8] { &self.bytes[..] } + } + + impl ops::DerefMut for ByteBuf { + fn deref_mut(&mut self) -> &mut [u8] { &mut self.bytes[..] } + } + + impl ser::Serialize for ByteBuf { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + where S: ser::Serializer + { + serializer.serialize_bytes(&self) + } + } + + /// This type implements the `serde::de::Visitor` trait for a `ByteBuf`. + pub struct ByteBufVisitor; + + impl de::Visitor for ByteBufVisitor { + type Value = ByteBuf; + + #[inline] + fn visit_unit(&mut self) -> Result + where E: de::Error, + { + Ok(ByteBuf { + bytes: Vec::new(), + }) } - try!(visitor.end()); + #[inline] + fn visit_seq(&mut self, mut visitor: V) -> Result + where V: de::SeqVisitor, + { + let (len, _) = visitor.size_hint(); + let mut values = Vec::with_capacity(len); - Ok(ByteBuf { - bytes: values, - }) + while let Some(value) = try!(visitor.visit()) { + values.push(value); + } + + try!(visitor.end()); + + Ok(ByteBuf { + bytes: values, + }) + } + + #[inline] + fn visit_bytes(&mut self, v: &[u8]) -> Result + where E: de::Error, + { + self.visit_byte_buf(v.to_vec()) + } + + #[inline] + fn visit_byte_buf(&mut self, v: Vec) -> Result + where E: de::Error, + { + Ok(ByteBuf { + bytes: v, + }) + } } - #[inline] - fn visit_bytes(&mut self, v: &[u8]) -> Result - where E: de::Error, - { - self.visit_byte_buf(v.to_vec()) - } - - #[inline] - fn visit_byte_buf(&mut self, v: Vec) -> Result - where E: de::Error, - { - Ok(ByteBuf { - bytes: v, - }) - } -} - -impl de::Deserialize for ByteBuf { - #[inline] - fn deserialize(deserializer: &mut D) -> Result - where D: de::Deserializer - { - deserializer.deserialize_bytes(ByteBufVisitor) + impl de::Deserialize for ByteBuf { + #[inline] + fn deserialize(deserializer: &mut D) -> Result + where D: de::Deserializer + { + deserializer.deserialize_bytes(ByteBufVisitor) + } } } /////////////////////////////////////////////////////////////////////////////// -fn escape_bytestring(bytes: &[u8]) -> String { - let mut result = String::new(); - for &b in bytes { - for esc in ascii::escape_default(b) { - result.push(esc as char); - } +#[inline] +fn escape_bytestring<'a>(bytes: &'a [u8]) -> iter::FlatMap, char::EscapeDefault, fn(&u8) -> char::EscapeDefault> { + fn f(b: &u8) -> char::EscapeDefault { + char::from_u32(*b as u32).unwrap().escape_default() } - result + bytes.iter().flat_map(f as fn(&u8) -> char::EscapeDefault) } diff --git a/serde/src/de/from_primitive.rs b/serde/src/de/from_primitive.rs index fea4173d..14e51860 100644 --- a/serde/src/de/from_primitive.rs +++ b/serde/src/de/from_primitive.rs @@ -13,10 +13,10 @@ // Rust 1.5 is unhappy that this private module is undocumented. #![allow(missing_docs)] -use std::{usize, u8, u16, u32, u64}; -use std::{isize, i8, i16, i32, i64}; -use std::{f32, f64}; -use std::mem::size_of; +use core::{usize, u8, u16, u32, u64}; +use core::{isize, i8, i16, i32, i64}; +use core::{f32, f64}; +use core::mem::size_of; /// Numbers which have upper and lower bounds pub trait Bounded { @@ -274,7 +274,7 @@ macro_rules! impl_to_primitive_float_to_float { Some($slf as $DstT) } else { let n = $slf as f64; - let max_value: $SrcT = ::std::$SrcT::MAX; + let max_value: $SrcT = ::core::$SrcT::MAX; if -max_value as f64 <= n && n <= max_value as f64 { Some($slf as $DstT) } else { diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index cfff6704..6cfa84cb 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -1,30 +1,63 @@ //! This module contains `Deserialize` and `Visitor` implementations. +#[cfg(feature = "std")] use std::borrow::Cow; -use std::collections::{ +#[cfg(all(feature = "nightly", feature = "collections", not(feature = "std")))] +use collections::borrow::Cow; + +#[cfg(all(feature = "collections", not(feature = "std")))] +use collections::{ BinaryHeap, BTreeMap, BTreeSet, LinkedList, + VecDeque, + Vec, + String, +}; + +#[cfg(feature = "std")] +use std::collections::{ HashMap, HashSet, + BinaryHeap, + BTreeMap, + BTreeSet, + LinkedList, VecDeque, }; -#[cfg(feature = "nightly")] + +#[cfg(all(feature = "nightly", feature = "collections"))] use collections::enum_set::{CLike, EnumSet}; -use std::hash::Hash; -use std::marker::PhantomData; +#[cfg(all(feature = "nightly", feature = "collections"))] +use collections::borrow::ToOwned; + +use core::hash::Hash; +use core::marker::PhantomData; +#[cfg(feature = "std")] use std::net; +#[cfg(feature = "std")] use std::path; +use core::str; + +#[cfg(feature = "std")] use std::rc::Rc; -use std::str; +#[cfg(all(feature = "nightly", feature = "alloc", not(feature = "std")))] +use alloc::rc::Rc; + +#[cfg(feature = "std")] use std::sync::Arc; +#[cfg(all(feature = "nightly", feature = "alloc", not(feature = "std")))] +use alloc::arc::Arc; + +#[cfg(all(feature = "nightly", feature = "alloc", not(feature = "std")))] +use alloc::boxed::Box; #[cfg(feature = "nightly")] use core::nonzero::{NonZero, Zeroable}; #[cfg(feature = "nightly")] -use std::num::Zero; +use core::num::Zero; use de::{ Deserialize, @@ -85,7 +118,7 @@ impl Visitor for BoolVisitor { fn visit_str(&mut self, s: &str) -> Result where E: Error, { - match s.trim() { + match s.trim_matches(|c| ::utils::Pattern_White_Space(c)) { "true" => Ok(true), "false" => Ok(false), _ => Err(Error::invalid_type(Type::Bool)), @@ -151,10 +184,10 @@ impl Visitor for PrimitiveVisitor impl_deserialize_num_method!(f64, visit_f64, from_f64, Type::F64); #[inline] - fn visit_str(&mut self, v: &str) -> Result + fn visit_str(&mut self, s: &str) -> Result where E: Error, { - str::FromStr::from_str(v.trim()).or_else(|_| { + str::FromStr::from_str(s.trim_matches(::utils::Pattern_White_Space)).or_else(|_| { Err(Error::invalid_type(Type::Str)) }) } @@ -228,8 +261,10 @@ impl Deserialize for char { /////////////////////////////////////////////////////////////////////////////// +#[cfg(any(feature = "std", feature = "collections"))] struct StringVisitor; +#[cfg(any(feature = "std", feature = "collections"))] impl Visitor for StringVisitor { type Value = String; @@ -264,6 +299,7 @@ impl Visitor for StringVisitor { } } +#[cfg(any(feature = "std", feature = "collections"))] impl Deserialize for String { fn deserialize(deserializer: &mut D) -> Result where D: Deserializer, @@ -406,6 +442,7 @@ macro_rules! seq_impl { } } +#[cfg(any(feature = "std", feature = "collections"))] seq_impl!( BinaryHeap, , @@ -415,6 +452,7 @@ seq_impl!( BinaryHeap::with_capacity(visitor.size_hint().0), BinaryHeap::push); +#[cfg(any(feature = "std", feature = "collections"))] seq_impl!( BTreeSet, , @@ -424,7 +462,7 @@ seq_impl!( BTreeSet::new(), BTreeSet::insert); -#[cfg(feature = "nightly")] +#[cfg(all(feature = "nightly", feature = "collections"))] seq_impl!( EnumSet, , @@ -434,6 +472,7 @@ seq_impl!( EnumSet::new(), EnumSet::insert); +#[cfg(any(feature = "std", feature = "collections"))] seq_impl!( LinkedList, , @@ -443,6 +482,7 @@ seq_impl!( LinkedList::new(), LinkedList::push_back); +#[cfg(feature = "std")] seq_impl!( HashSet, , @@ -452,6 +492,7 @@ seq_impl!( HashSet::with_capacity(visitor.size_hint().0), HashSet::insert); +#[cfg(any(feature = "std", feature = "collections"))] seq_impl!( Vec, , @@ -461,6 +502,7 @@ seq_impl!( Vec::with_capacity(visitor.size_hint().0), Vec::push); +#[cfg(any(feature = "std", feature = "collections"))] seq_impl!( VecDeque, , @@ -747,6 +789,7 @@ macro_rules! map_impl { } } +#[cfg(any(feature = "std", feature = "collections"))] map_impl!( BTreeMap, , @@ -756,6 +799,7 @@ map_impl!( BTreeMap::new(), BTreeMap::insert); +#[cfg(feature = "std")] map_impl!( HashMap, , @@ -767,7 +811,7 @@ map_impl!( /////////////////////////////////////////////////////////////////////////////// -#[cfg(feature = "nightly")] +#[cfg(all(feature = "nightly", feature = "std"))] impl Deserialize for net::IpAddr { fn deserialize(deserializer: &mut D) -> Result where D: Deserializer, @@ -780,6 +824,7 @@ impl Deserialize for net::IpAddr { } } +#[cfg(feature = "std")] impl Deserialize for net::Ipv4Addr { fn deserialize(deserializer: &mut D) -> Result where D: Deserializer, @@ -792,6 +837,7 @@ impl Deserialize for net::Ipv4Addr { } } +#[cfg(feature = "std")] impl Deserialize for net::Ipv6Addr { fn deserialize(deserializer: &mut D) -> Result where D: Deserializer, @@ -806,6 +852,7 @@ impl Deserialize for net::Ipv6Addr { /////////////////////////////////////////////////////////////////////////////// +#[cfg(feature = "std")] impl Deserialize for net::SocketAddr { fn deserialize(deserializer: &mut D) -> Result where D: Deserializer, @@ -818,6 +865,7 @@ impl Deserialize for net::SocketAddr { } } +#[cfg(feature = "std")] impl Deserialize for net::SocketAddrV4 { fn deserialize(deserializer: &mut D) -> Result where D: Deserializer, @@ -830,6 +878,7 @@ impl Deserialize for net::SocketAddrV4 { } } +#[cfg(feature = "std")] impl Deserialize for net::SocketAddrV6 { fn deserialize(deserializer: &mut D) -> Result where D: Deserializer, @@ -844,8 +893,10 @@ impl Deserialize for net::SocketAddrV6 { /////////////////////////////////////////////////////////////////////////////// +#[cfg(feature = "std")] struct PathBufVisitor; +#[cfg(feature = "std")] impl Visitor for PathBufVisitor { type Value = path::PathBuf; @@ -862,6 +913,7 @@ impl Visitor for PathBufVisitor { } } +#[cfg(feature = "std")] impl Deserialize for path::PathBuf { fn deserialize(deserializer: &mut D) -> Result where D: Deserializer, @@ -872,6 +924,7 @@ impl Deserialize for path::PathBuf { /////////////////////////////////////////////////////////////////////////////// +#[cfg(any(feature = "std", feature = "alloc"))] impl Deserialize for Box { fn deserialize(deserializer: &mut D) -> Result, D::Error> where D: Deserializer, @@ -881,6 +934,7 @@ impl Deserialize for Box { } } +#[cfg(any(feature = "std", feature = "collections"))] impl Deserialize for Box<[T]> { fn deserialize(deserializer: &mut D) -> Result, D::Error> where D: Deserializer, @@ -890,6 +944,7 @@ impl Deserialize for Box<[T]> { } } +#[cfg(any(feature = "std", feature = "alloc"))] impl Deserialize for Arc { fn deserialize(deserializer: &mut D) -> Result, D::Error> where D: Deserializer, @@ -899,6 +954,7 @@ impl Deserialize for Arc { } } +#[cfg(any(feature = "std", feature = "alloc"))] impl Deserialize for Rc { fn deserialize(deserializer: &mut D) -> Result, D::Error> where D: Deserializer, @@ -908,6 +964,7 @@ impl Deserialize for Rc { } } +#[cfg(any(feature = "std", feature = "collections"))] impl<'a, T: ?Sized> Deserialize for Cow<'a, T> where T: ToOwned, T::Owned: Deserialize, { #[inline] fn deserialize(deserializer: &mut D) -> Result, D::Error> @@ -954,7 +1011,10 @@ impl Deserialize for Result where T: Deserialize, E: Deserialize { impl ::de::Visitor for FieldVisitor { type Value = Field; + #[cfg(any(feature = "std", feature = "collections"))] fn visit_usize(&mut self, value: usize) -> Result where E: Error { + #[cfg(feature = "collections")] + use collections::string::ToString; match value { 0 => Ok(Field::Ok), 1 => Ok(Field::Err), @@ -962,6 +1022,17 @@ impl Deserialize for Result where T: Deserialize, E: Deserialize { } } + #[cfg(all(not(feature = "std"), not(feature = "collections")))] + fn visit_usize(&mut self, value: usize) -> Result where E: Error { + #[cfg(feature = "collections")] + use collections::string::ToString; + match value { + 0 => Ok(Field::Ok), + 1 => Ok(Field::Err), + _ => Err(Error::unknown_field("some number")), + } + } + fn visit_str(&mut self, value: &str) -> Result where E: Error { match value { "Ok" => Ok(Field::Ok), diff --git a/serde/src/de/mod.rs b/serde/src/de/mod.rs index a251e715..3a90438f 100644 --- a/serde/src/de/mod.rs +++ b/serde/src/de/mod.rs @@ -1,6 +1,12 @@ //! Generic deserialization framework. +#[cfg(feature = "std")] use std::error; +#[cfg(not(feature = "std"))] +use error; + +#[cfg(all(not(feature = "std"), feature = "collections"))] +use collections::{String, Vec}; pub mod impls; pub mod value; @@ -12,8 +18,13 @@ mod from_primitive; /// `Deserializer` error. pub trait Error: Sized + error::Error { /// Raised when there is general error when deserializing a type. + #[cfg(any(feature = "std", feature = "collections"))] fn custom>(msg: T) -> Self; + /// Raised when there is general error when deserializing a type. + #[cfg(all(not(feature = "std"), not(feature = "collections")))] + fn custom>(msg: T) -> Self; + /// Raised when a `Deserialize` type unexpectedly hit the end of the stream. fn end_of_stream() -> Self; @@ -558,9 +569,7 @@ pub trait Visitor { fn visit_char(&mut self, v: char) -> Result where E: Error, { - // FIXME: this allocation is required in order to be compatible with stable rust, which - // doesn't support encoding a `char` into a stack buffer. - self.visit_string(v.to_string()) + self.visit_str(::core::str::from_utf8(::utils::encode_utf8(v).as_slice()).unwrap()) } /// `visit_str` deserializes a `&str` into a `Value`. @@ -574,6 +583,7 @@ pub trait Visitor { /// a copy if it is deserializing a string from a `String` type. By default it passes a `&str` /// to the `visit_str` method. #[inline] + #[cfg(any(feature = "std", feature = "collections"))] fn visit_string(&mut self, v: String) -> Result where E: Error, { @@ -638,6 +648,7 @@ pub trait Visitor { } /// `visit_byte_buf` deserializes a `Vec` into a `Value`. + #[cfg(any(feature = "std", feature = "collections"))] fn visit_byte_buf(&mut self, v: Vec) -> Result where E: Error, { diff --git a/serde/src/de/value.rs b/serde/src/de/value.rs index 791cb900..b2349a17 100644 --- a/serde/src/de/value.rs +++ b/serde/src/de/value.rs @@ -1,5 +1,6 @@ //! This module supports deserializing from primitives with the `ValueDeserializer` trait. +#[cfg(feature = "std")] use std::collections::{ BTreeMap, BTreeSet, @@ -10,11 +11,31 @@ use std::collections::{ hash_map, hash_set, }; -use std::hash::Hash; -use std::error; -use std::fmt; +#[cfg(feature = "std")] use std::vec; -use std::marker::PhantomData; + +#[cfg(all(feature = "collections", not(feature = "std")))] +use collections::{ + BTreeMap, + BTreeSet, + Vec, + String, + btree_map, + btree_set, + vec, +}; + +#[cfg(all(feature = "nightly", feature = "collections"))] +use collections::borrow::ToOwned; + +use core::hash::Hash; +#[cfg(feature = "std")] +use std::error; +#[cfg(not(feature = "std"))] +use error; + +use core::fmt; +use core::marker::PhantomData; use de; use bytes; @@ -25,7 +46,11 @@ use bytes; #[derive(Clone, Debug, PartialEq)] pub enum Error { /// The value had some custom error. + #[cfg(any(feature = "std", feature = "collections"))] Custom(String), + /// The value had some custom error. + #[cfg(all(not(feature = "std"), not(feature = "collections")))] + Custom(&'static str), /// The value had an incorrect type. InvalidType(de::Type), @@ -34,29 +59,60 @@ pub enum Error { InvalidLength(usize), /// The value is invalid and cannot be deserialized. + #[cfg(any(feature = "std", feature = "collections"))] InvalidValue(String), + /// The value is invalid and cannot be deserialized. + #[cfg(all(not(feature = "std"), not(feature = "collections")))] + InvalidValue(&'static str), /// EOF while deserializing a value. EndOfStream, /// Unknown variant in enum. + #[cfg(any(feature = "std", feature = "collections"))] UnknownVariant(String), + /// Unknown variant in enum. + #[cfg(all(not(feature = "std"), not(feature = "collections")))] + UnknownVariant(&'static str), /// Unknown field in struct. + #[cfg(any(feature = "std", feature = "collections"))] UnknownField(String), + /// Unknown field in struct. + #[cfg(all(not(feature = "std"), not(feature = "collections")))] + UnknownField(&'static str), /// Struct is missing a field. MissingField(&'static str), } impl de::Error for Error { + #[cfg(any(feature = "std", feature = "collections"))] fn custom>(msg: T) -> Self { Error::Custom(msg.into()) } + + #[cfg(all(not(feature = "std"), not(feature = "collections")))] + fn custom>(msg: T) -> Self { Error::Custom(msg.into()) } + fn end_of_stream() -> Self { Error::EndOfStream } fn invalid_type(ty: de::Type) -> Self { Error::InvalidType(ty) } + + #[cfg(any(feature = "std", feature = "collections"))] fn invalid_value(msg: &str) -> Self { Error::InvalidValue(msg.to_owned()) } + + #[cfg(all(not(feature = "std"), not(feature = "collections")))] + fn invalid_value(msg: &str) -> Self { Error::InvalidValue("invalid value") } + fn invalid_length(len: usize) -> Self { Error::InvalidLength(len) } + + #[cfg(any(feature = "std", feature = "collections"))] fn unknown_variant(variant: &str) -> Self { Error::UnknownVariant(String::from(variant)) } + #[cfg(any(feature = "std", feature = "collections"))] fn unknown_field(field: &str) -> Self { Error::UnknownField(String::from(field)) } + + #[cfg(all(not(feature = "std"), not(feature = "collections")))] + fn unknown_variant(variant: &str) -> Self { Error::UnknownVariant("unknown variant") } + #[cfg(all(not(feature = "std"), not(feature = "collections")))] + fn unknown_field(field: &str) -> Self { Error::UnknownField("unknown field") } fn missing_field(field: &'static str) -> Self { Error::MissingField(field) } } @@ -238,8 +294,10 @@ impl<'a, E> de::VariantVisitor for StrDeserializer<'a, E> /////////////////////////////////////////////////////////////////////////////// /// A helper deserializer that deserializes a `String`. +#[cfg(any(feature = "std", feature = "collections"))] pub struct StringDeserializer(Option, PhantomData); +#[cfg(any(feature = "std", feature = "collections"))] impl ValueDeserializer for String where E: de::Error, { @@ -250,6 +308,7 @@ impl ValueDeserializer for String } } +#[cfg(any(feature = "std", feature = "collections"))] impl de::Deserializer for StringDeserializer where E: de::Error, { @@ -274,6 +333,7 @@ impl de::Deserializer for StringDeserializer } } +#[cfg(any(feature = "std", feature = "collections"))] impl<'a, E> de::VariantVisitor for StringDeserializer where E: de::Error, { @@ -361,6 +421,7 @@ impl de::SeqVisitor for SeqDeserializer /////////////////////////////////////////////////////////////////////////////// +#[cfg(any(feature = "std", feature = "collections"))] impl ValueDeserializer for Vec where T: ValueDeserializer, E: de::Error, @@ -373,6 +434,7 @@ impl ValueDeserializer for Vec } } +#[cfg(any(feature = "std", feature = "collections"))] impl ValueDeserializer for BTreeSet where T: ValueDeserializer + Eq + Ord, E: de::Error, @@ -385,6 +447,7 @@ impl ValueDeserializer for BTreeSet } } +#[cfg(feature = "std")] impl ValueDeserializer for HashSet where T: ValueDeserializer + Eq + Hash, E: de::Error, @@ -527,6 +590,7 @@ impl de::MapVisitor for MapDeserializer /////////////////////////////////////////////////////////////////////////////// +#[cfg(any(feature = "std", feature = "collections"))] impl ValueDeserializer for BTreeMap where K: ValueDeserializer + Eq + Ord, V: ValueDeserializer, @@ -540,6 +604,7 @@ impl ValueDeserializer for BTreeMap } } +#[cfg(feature = "std")] impl ValueDeserializer for HashMap where K: ValueDeserializer + Eq + Hash, V: ValueDeserializer, @@ -618,6 +683,7 @@ impl<'a, E> de::Deserializer for BytesDeserializer<'a, E> /////////////////////////////////////////////////////////////////////////////// +#[cfg(any(feature = "std", feature = "collections"))] impl ValueDeserializer for bytes::ByteBuf where E: de::Error, { @@ -629,8 +695,10 @@ impl ValueDeserializer for bytes::ByteBuf } /// A helper deserializer that deserializes a `Vec`. +#[cfg(any(feature = "std", feature = "collections"))] pub struct ByteBufDeserializer(Option>, PhantomData); +#[cfg(any(feature = "std", feature = "collections"))] impl de::Deserializer for ByteBufDeserializer where E: de::Error, { diff --git a/serde/src/error.rs b/serde/src/error.rs new file mode 100644 index 00000000..9852e1f2 --- /dev/null +++ b/serde/src/error.rs @@ -0,0 +1,44 @@ +//! A stand-in for `std::error` +use core::any::TypeId; +use core::fmt::{Debug, Display}; + + +/// A stand-in for `std::error::Error`, which requires no allocation. +#[cfg(feature = "nightly")] +pub trait Error: Debug + Display + ::core::marker::Reflect { + /// A short description of the error. + /// + /// The description should not contain newlines or sentence-ending + /// punctuation, to facilitate embedding in larger user-facing + /// strings. + fn description(&self) -> &str; + + /// The lower-level cause of this error, if any. + fn cause(&self) -> Option<&Error> { None } + + /// Get the `TypeId` of `self` + #[doc(hidden)] + fn type_id(&self) -> TypeId where Self: 'static { + TypeId::of::() + } +} + +/// A stand-in for `std::error::Error`, which requires no allocation. +#[cfg(not(feature = "nightly"))] +pub trait Error: Debug + Display { + /// A short description of the error. + /// + /// The description should not contain newlines or sentence-ending + /// punctuation, to facilitate embedding in larger user-facing + /// strings. + fn description(&self) -> &str; + + /// The lower-level cause of this error, if any. + fn cause(&self) -> Option<&Error> { None } + + /// Stubbed! Returns type_id of `()` + #[doc(hidden)] + fn type_id(&self) -> TypeId where Self: 'static { + TypeId::of::<()>() + } +} diff --git a/serde/src/lib.rs b/serde/src/lib.rs index b00fdc17..e98741c6 100644 --- a/serde/src/lib.rs +++ b/serde/src/lib.rs @@ -10,23 +10,46 @@ //! [github repository](https://github.com/serde-rs/serde) #![doc(html_root_url="https://serde-rs.github.io/serde/serde")] -#![cfg_attr(feature = "nightly", feature(collections, enumset, nonzero, plugin, step_trait, - zero_one))] +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(feature = "nightly", feature(reflect_marker, unicode, nonzero, plugin, step_trait, zero_one))] +#![cfg_attr(feature = "alloc", feature(alloc))] +#![cfg_attr(feature = "collections", feature(collections, enumset))] #![cfg_attr(feature = "nightly-testing", plugin(clippy))] #![cfg_attr(feature = "nightly-testing", allow(linkedlist))] +#![cfg_attr(any(not(feature = "std"), feature = "nightly"), allow(unused_variables, unused_imports, unused_features, dead_code))] + #![deny(missing_docs)] -#[cfg(feature = "nightly")] +#[cfg(all(feature = "nightly", feature = "collections"))] extern crate collections; -#[cfg(feature = "nightly")] -extern crate core; +#[cfg(all(feature = "nightly", feature = "alloc"))] +extern crate alloc; + +#[cfg(feature = "std")] +mod core { + pub use std::{ops, hash, fmt, cmp, marker, mem, i8, i16, i32, i64, u8, u16, u32, u64, isize, + usize, f32, f64, char, str, num, slice, iter}; + #[cfg(feature = "nightly")] + extern crate core; + #[cfg(feature = "nightly")] + pub use self::core::nonzero; +} pub use ser::{Serialize, Serializer}; pub use de::{Deserialize, Deserializer, Error}; +#[cfg(not(feature = "std"))] +macro_rules! format { + ($s:expr, $($rest:tt)*) => ($s) +} + pub mod bytes; pub mod de; +#[cfg(feature = "std")] pub mod iter; pub mod ser; +#[cfg(not(feature = "std"))] +pub mod error; +mod utils; diff --git a/serde/src/ser/impls.rs b/serde/src/ser/impls.rs index fec942d7..b51226a9 100644 --- a/serde/src/ser/impls.rs +++ b/serde/src/ser/impls.rs @@ -1,6 +1,11 @@ //! Implementations for all of Rust's builtin types. +#[cfg(feature = "std")] use std::borrow::Cow; +#[cfg(all(feature = "collections", not(feature = "std")))] +use collections::borrow::Cow; + +#[cfg(feature = "std")] use std::collections::{ BinaryHeap, BTreeMap, @@ -10,20 +15,47 @@ use std::collections::{ HashSet, VecDeque, }; -#[cfg(feature = "nightly")] +#[cfg(all(feature = "collections", not(feature = "std")))] +use collections::{ + BinaryHeap, + BTreeMap, + BTreeSet, + LinkedList, + VecDeque, + String, + Vec, +}; + +#[cfg(all(feature = "nightly", feature = "collections"))] use collections::enum_set::{CLike, EnumSet}; -use std::hash::Hash; +#[cfg(all(feature = "nightly", feature = "collections"))] +use collections::borrow::ToOwned; + +use core::hash::Hash; #[cfg(feature = "nightly")] -use std::iter; +use core::iter; +#[cfg(feature = "std")] use std::net; #[cfg(feature = "nightly")] -use std::num; +use core::num; #[cfg(feature = "nightly")] -use std::ops; +use core::ops; +#[cfg(feature = "std")] use std::path; +#[cfg(feature = "std")] use std::rc::Rc; +#[cfg(all(feature = "alloc", not(feature = "std")))] +use alloc::rc::Rc; + +#[cfg(feature = "std")] use std::sync::Arc; -use std::marker::PhantomData; +#[cfg(all(feature = "alloc", not(feature = "std")))] +use alloc::arc::Arc; + +#[cfg(all(feature = "alloc", not(feature = "std")))] +use alloc::boxed::Box; + +use core::marker::PhantomData; #[cfg(feature = "nightly")] use core::nonzero::{NonZero, Zeroable}; @@ -77,6 +109,7 @@ impl Serialize for str { } } +#[cfg(any(feature = "std", feature = "collections"))] impl Serialize for String { #[inline] fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> @@ -260,6 +293,7 @@ array_impls!(32); /////////////////////////////////////////////////////////////////////////////// +#[cfg(any(feature = "std", feature = "collections"))] impl Serialize for BinaryHeap where T: Serialize + Ord { @@ -271,6 +305,7 @@ impl Serialize for BinaryHeap } } +#[cfg(any(feature = "std", feature = "collections"))] impl Serialize for BTreeSet where T: Serialize + Ord, { @@ -282,7 +317,7 @@ impl Serialize for BTreeSet } } -#[cfg(feature = "nightly")] +#[cfg(all(feature = "nightly", feature = "collections"))] impl Serialize for EnumSet where T: Serialize + CLike { @@ -294,6 +329,7 @@ impl Serialize for EnumSet } } +#[cfg(feature = "std")] impl Serialize for HashSet where T: Serialize + Eq + Hash, { @@ -305,6 +341,7 @@ impl Serialize for HashSet } } +#[cfg(any(feature = "std", feature = "collections"))] impl Serialize for LinkedList where T: Serialize, { @@ -330,6 +367,7 @@ impl Serialize for ops::Range } } +#[cfg(any(feature = "std", feature = "collections"))] impl Serialize for Vec where T: Serialize { #[inline] fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> @@ -339,6 +377,7 @@ impl Serialize for Vec where T: Serialize { } } +#[cfg(any(feature = "std", feature = "collections"))] impl Serialize for VecDeque where T: Serialize { #[inline] fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> @@ -598,6 +637,7 @@ impl MapVisitor for MapIteratorVisitor /////////////////////////////////////////////////////////////////////////////// +#[cfg(any(feature = "std", feature = "collections"))] impl Serialize for BTreeMap where K: Serialize + Ord, V: Serialize, @@ -610,6 +650,7 @@ impl Serialize for BTreeMap } } +#[cfg(feature = "std")] impl Serialize for HashMap where K: Serialize + Eq + Hash, V: Serialize, @@ -642,6 +683,7 @@ impl<'a, T: ?Sized> Serialize for &'a mut T where T: Serialize { } } +#[cfg(any(feature = "std", feature = "alloc"))] impl Serialize for Box where T: Serialize { #[inline] fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> @@ -651,6 +693,7 @@ impl Serialize for Box where T: Serialize { } } +#[cfg(any(feature = "std", feature = "alloc"))] impl Serialize for Rc where T: Serialize, { #[inline] fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> @@ -660,6 +703,7 @@ impl Serialize for Rc where T: Serialize, { } } +#[cfg(any(feature = "std", feature = "alloc"))] impl Serialize for Arc where T: Serialize, { #[inline] fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> @@ -669,6 +713,7 @@ impl Serialize for Arc where T: Serialize, { } } +#[cfg(any(feature = "std", feature = "collections"))] impl<'a, T: ?Sized> Serialize for Cow<'a, T> where T: Serialize + ToOwned, { #[inline] fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> @@ -695,7 +740,7 @@ impl Serialize for Result where T: Serialize, E: Serialize { /////////////////////////////////////////////////////////////////////////////// -#[cfg(feature = "nightly")] +#[cfg(all(feature = "std", feature = "nightly"))] impl Serialize for net::IpAddr { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer, @@ -707,6 +752,7 @@ impl Serialize for net::IpAddr { } } +#[cfg(feature = "std")] impl Serialize for net::Ipv4Addr { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer, @@ -715,6 +761,7 @@ impl Serialize for net::Ipv4Addr { } } +#[cfg(feature = "std")] impl Serialize for net::Ipv6Addr { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer, @@ -725,6 +772,7 @@ impl Serialize for net::Ipv6Addr { /////////////////////////////////////////////////////////////////////////////// +#[cfg(feature = "std")] impl Serialize for net::SocketAddr { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer, @@ -736,6 +784,7 @@ impl Serialize for net::SocketAddr { } } +#[cfg(feature = "std")] impl Serialize for net::SocketAddrV4 { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer, @@ -744,6 +793,7 @@ impl Serialize for net::SocketAddrV4 { } } +#[cfg(feature = "std")] impl Serialize for net::SocketAddrV6 { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer, @@ -754,6 +804,7 @@ impl Serialize for net::SocketAddrV6 { /////////////////////////////////////////////////////////////////////////////// +#[cfg(feature = "std")] impl Serialize for path::Path { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer, @@ -765,6 +816,7 @@ impl Serialize for path::Path { } } +#[cfg(feature = "std")] impl Serialize for path::PathBuf { fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer, diff --git a/serde/src/ser/mod.rs b/serde/src/ser/mod.rs index 7129ebfb..22f28498 100644 --- a/serde/src/ser/mod.rs +++ b/serde/src/ser/mod.rs @@ -1,6 +1,12 @@ //! Generic serialization framework. +#[cfg(feature = "std")] use std::error; +#[cfg(not(feature = "std"))] +use error; + +#[cfg(all(feature = "collections", not(feature = "std")))] +use collections::String; pub mod impls; @@ -10,8 +16,13 @@ pub mod impls; /// `Serializer` error. pub trait Error: Sized + error::Error { /// Raised when there is general error when deserializing a type. + #[cfg(any(feature = "std", feature = "collections"))] fn custom>(msg: T) -> Self; + /// Raised when there is general error when deserializing a type. + #[cfg(all(not(feature = "std"), not(feature = "collections")))] + fn custom>(msg: T) -> Self; + /// Raised when a `Serialize` was passed an incorrect value. fn invalid_value(msg: &str) -> Self { Error::custom(format!("invalid value: {}", msg)) @@ -111,13 +122,10 @@ pub trait Serializer { /// Serializes a `f64` value. fn serialize_f64(&mut self, v: f64) -> Result<(), Self::Error>; - /// Serializes a character. By default it serializes it as a `&str` containing a - /// single character. - #[inline] + /// Serializes a character. By default it serializes as bytes containing the UTF-8 encoding + /// of the character. fn serialize_char(&mut self, v: char) -> Result<(), Self::Error> { - // FIXME: this allocation is required in order to be compatible with stable rust, which - // doesn't support encoding a `char` into a stack buffer. - self.serialize_str(&v.to_string()) + self.serialize_bytes(::utils::encode_utf8(v).as_slice()) } /// Serializes a `&str`. diff --git a/serde/src/utils.rs b/serde/src/utils.rs new file mode 100644 index 00000000..44931cb4 --- /dev/null +++ b/serde/src/utils.rs @@ -0,0 +1,72 @@ +//! Private utility functions + +const TAG_CONT: u8 = 0b1000_0000; +const TAG_TWO_B: u8 = 0b1100_0000; +const TAG_THREE_B: u8 = 0b1110_0000; +const TAG_FOUR_B: u8 = 0b1111_0000; +const MAX_ONE_B: u32 = 0x80; +const MAX_TWO_B: u32 = 0x800; +const MAX_THREE_B: u32 = 0x10000; + +#[inline] +pub fn encode_utf8(c: char) -> EncodeUtf8 { + let code = c as u32; + let mut buf = [0; 4]; + let pos = if code < MAX_ONE_B { + buf[3] = code as u8; + 3 + } else if code < MAX_TWO_B { + buf[2] = (code >> 6 & 0x1F) as u8 | TAG_TWO_B; + buf[3] = (code & 0x3F) as u8 | TAG_CONT; + 2 + } else if code < MAX_THREE_B { + buf[1] = (code >> 12 & 0x0F) as u8 | TAG_THREE_B; + buf[2] = (code >> 6 & 0x3F) as u8 | TAG_CONT; + buf[3] = (code & 0x3F) as u8 | TAG_CONT; + 1 + } else { + buf[0] = (code >> 18 & 0x07) as u8 | TAG_FOUR_B; + buf[1] = (code >> 12 & 0x3F) as u8 | TAG_CONT; + buf[2] = (code >> 6 & 0x3F) as u8 | TAG_CONT; + buf[3] = (code & 0x3F) as u8 | TAG_CONT; + 0 + }; + EncodeUtf8 { buf: buf, pos: pos } +} + +pub struct EncodeUtf8 { + buf: [u8; 4], + pos: usize, +} + +impl EncodeUtf8 { + /// Returns the remaining bytes of this iterator as a slice. + pub fn as_slice(&self) -> &[u8] { + &self.buf[self.pos..] + } +} + +#[allow(non_upper_case_globals)] +const Pattern_White_Space_table: &'static [(char, char)] = &[ + ('\u{9}', '\u{d}'), ('\u{20}', '\u{20}'), ('\u{85}', '\u{85}'), ('\u{200e}', '\u{200f}'), + ('\u{2028}', '\u{2029}') +]; + +fn bsearch_range_table(c: char, r: &'static [(char, char)]) -> bool { + use core::cmp::Ordering::{Equal, Less, Greater}; + r.binary_search_by(|&(lo, hi)| { + if c < lo { + Greater + } else if hi < c { + Less + } else { + Equal + } + }) + .is_ok() +} + +#[allow(non_snake_case)] +pub fn Pattern_White_Space(c: char) -> bool { + bsearch_range_table(c, Pattern_White_Space_table) +}