diff --git a/.travis.yml b/.travis.yml index 707dee3f4..1571f801f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,6 +19,9 @@ matrix: - rust: stable - rust: beta - rust: 1.31.0 + - rust: 1.36.0 + script: + - cargo build --no-default-features --features alloc - rust: nightly name: Clippy diff --git a/Cargo.toml b/Cargo.toml index a987aa0a5..e942f55d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,9 +16,9 @@ travis-ci = { repository = "serde-rs/json" } appveyor = { repository = "serde-rs/json" } [dependencies] -serde = "1.0.60" +serde = { version = "1.0.60", default-features = false } indexmap = { version = "1.2", optional = true } -itoa = "0.4.3" +itoa = { version = "0.4.3", default-features = false } ryu = "1.0" [dev-dependencies] @@ -42,7 +42,15 @@ features = ["raw_value"] ### FEATURES ################################################################# [features] -default = [] +default = ["std"] + +std = ["serde/std", "itoa/std"] + +# Provide integration for heap-allocated collections without depending on the +# rest of the Rust standard library. +# NOTE: Disabling both `std` *and* `alloc` features is not supported yet. +# Available on Rust 1.36+. +alloc = ["serde/alloc"] # Use a different representation for the map type of serde_json::Value. # This allows data to be read into a Value and written back to a JSON string diff --git a/src/de.rs b/src/de.rs index 5c9c20b20..c71b796eb 100644 --- a/src/de.rs +++ b/src/de.rs @@ -1,10 +1,9 @@ //! Deserialize JSON data to a Rust data structure. -use std::io; -use std::marker::PhantomData; -use std::result; -use std::str::FromStr; -use std::{i32, u64}; +use lib::str::FromStr; +use lib::*; + +use io; use serde::de::{self, Expected, Unexpected}; diff --git a/src/error.rs b/src/error.rs index 5e3e8f064..b0876f71a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,10 +1,9 @@ //! When serializing or deserializing JSON goes wrong. -use std::error; -use std::fmt::{self, Debug, Display}; -use std::io; -use std::result; -use std::str::FromStr; +use lib::str::FromStr; +use lib::*; + +use io; use serde::de; use serde::ser; @@ -333,8 +332,8 @@ impl Display for ErrorCode { } } -impl error::Error for Error { - fn source(&self) -> Option<&(error::Error + 'static)> { +impl serde::de::StdError for Error { + fn source(&self) -> Option<&(serde::de::StdError + 'static)> { match self.err.code { ErrorCode::Io(ref err) => Some(err), _ => None, diff --git a/src/features_check/error.rs b/src/features_check/error.rs new file mode 100644 index 000000000..967904bd4 --- /dev/null +++ b/src/features_check/error.rs @@ -0,0 +1 @@ +"serde_json doesn't work without `alloc` or default `std` feature yet" \ No newline at end of file diff --git a/src/features_check/mod.rs b/src/features_check/mod.rs new file mode 100644 index 000000000..5b27a1244 --- /dev/null +++ b/src/features_check/mod.rs @@ -0,0 +1,13 @@ +//! Shows a user-friendly compiler error on incompatible selected features. + +#[allow(unused_macros)] +macro_rules! hide_from_rustfmt { + ($mod:item) => { + $mod + }; +} + +#[cfg(all(not(feature = "std"), not(feature = "alloc")))] +hide_from_rustfmt! { + mod error; +} diff --git a/src/io/core.rs b/src/io/core.rs new file mode 100644 index 000000000..d899eba1c --- /dev/null +++ b/src/io/core.rs @@ -0,0 +1,158 @@ +//! Reimplements core logic and types from `std::io` in an `alloc`-friendly +//! fashion. +#![cfg(not(feature = "std"))] + +use lib::*; + +pub enum ErrorKind { + InvalidData, + WriteZero, + Other, + UnexpectedEof, +} + +impl ErrorKind { + #[inline] + fn as_str(&self) -> &'static str { + match self { + ErrorKind::InvalidData => "invalid data", + ErrorKind::WriteZero => "write zero", + ErrorKind::Other => "other os error", + ErrorKind::UnexpectedEof => "unexpected end of file", + } + } +} + +pub struct Error { + repr: Repr, +} + +enum Repr { + Simple(ErrorKind), + Custom(ErrorKind, Box), +} + +impl Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.repr { + Repr::Custom(_, msg) => write!(fmt, "{}", msg), + Repr::Simple(kind) => write!(fmt, "{}", kind.as_str()), + } + } +} + +impl Debug for Error { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(self, fmt) + } +} + +impl serde::de::StdError for Error {} + +impl From for Error { + #[inline] + fn from(kind: ErrorKind) -> Error { + Error { + repr: Repr::Simple(kind), + } + } +} + +impl Error { + #[inline] + pub fn new(kind: ErrorKind, error: E) -> Error + where + E: Into>, + { + Error { + repr: Repr::Custom(kind, error.into()), + } + } +} + +pub type Result = result::Result; + +pub trait Write { + fn write(&mut self, buf: &[u8]) -> Result; + + fn write_all(&mut self, mut buf: &[u8]) -> Result<()> { + while !buf.is_empty() { + match self.write(buf) { + Ok(0) => { + return Err(Error::new( + ErrorKind::WriteZero, + "failed to write whole buffer", + )) + } + Ok(n) => buf = &buf[n..], + Err(e) => return Err(e), + } + } + Ok(()) + } + + fn flush(&mut self) -> Result<()>; +} + +impl Write for &mut W { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + (*self).write(buf) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<()> { + (*self).write_all(buf) + } + + #[inline] + fn flush(&mut self) -> Result<()> { + (*self).flush() + } +} + +impl Write for Vec { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + self.extend_from_slice(buf); + Ok(buf.len()) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<()> { + self.extend_from_slice(buf); + Ok(()) + } + + #[inline] + fn flush(&mut self) -> Result<()> { + Ok(()) + } +} + +pub trait Read { + fn read(&mut self, buf: &mut [u8]) -> Result; + fn bytes(self) -> Bytes + where + Self: Sized, + { + Bytes { inner: self } + } +} + +pub struct Bytes { + inner: R, +} + +impl Iterator for Bytes { + type Item = Result; + + fn next(&mut self) -> Option> { + let mut byte = 0; + match self.inner.read(slice::from_mut(&mut byte)) { + Ok(0) => None, + Ok(..) => Some(Ok(byte)), + Err(e) => Some(Err(e)), + } + } +} diff --git a/src/io/mod.rs b/src/io/mod.rs new file mode 100644 index 000000000..c64822359 --- /dev/null +++ b/src/io/mod.rs @@ -0,0 +1,19 @@ +//! A tiny, `no_std`-friendly facade around `std::io`. +//! Reexports types from `std` when available; otherwise reimplements and +//! provides some of the core logic. +//! +//! The main reason that `std::io` hasn't found itself reexported as part of +//! the `core` crate is the `std::io::{Read, Write}` traits' reliance on +//! `std::io::Error`, which may contain internally a heap-allocated `Box` +//! and/or now relying on OS-specific `std::backtrace::Backtrace`. + +pub use self::imp::{Bytes, Error, ErrorKind, Read, Result, Write}; + +mod core; + +mod imp { + #[cfg(not(feature = "std"))] + pub use super::core::*; + #[cfg(feature = "std")] + pub use std::io::*; +} diff --git a/src/iter.rs b/src/iter.rs index 1a9a954d1..53cbcbc05 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -1,4 +1,4 @@ -use std::io; +use io; pub struct LineColIterator { iter: I, diff --git a/src/lib.rs b/src/lib.rs index 17e3689e5..4d6512d42 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -331,6 +331,7 @@ must_use_candidate, ))] #![deny(missing_docs)] +#![cfg_attr(not(feature = "std"), no_std)] #[macro_use] extern crate serde; @@ -339,6 +340,67 @@ extern crate indexmap; extern crate itoa; extern crate ryu; +//////////////////////////////////////////////////////////////////////////////// + +#[cfg(not(feature = "std"))] +extern crate alloc; + +/// A facade around all the types we need from the `std`, `core`, and `alloc` +/// crates. This avoids elaborate import wrangling having to happen in every +/// module. +mod lib { + mod core { + #[cfg(not(feature = "std"))] + pub use core::*; + #[cfg(feature = "std")] + pub use std::*; + } + + pub use self::core::{char, str}; + pub use self::core::{cmp, mem, num, slice}; + + pub use self::core::{borrow, iter, ops}; + + pub use self::core::cell::{Cell, RefCell}; + pub use self::core::clone::{self, Clone}; + pub use self::core::convert::{self, From, Into}; + pub use self::core::default::{self, Default}; + pub use self::core::fmt::{self, Debug, Display}; + pub use self::core::hash::{self, Hash}; + pub use self::core::marker::{self, PhantomData}; + pub use self::core::result::{self, Result}; + + #[cfg(not(feature = "std"))] + pub use alloc::borrow::{Cow, ToOwned}; + #[cfg(feature = "std")] + pub use std::borrow::{Cow, ToOwned}; + + #[cfg(not(feature = "std"))] + pub use alloc::string::{String, ToString}; + #[cfg(feature = "std")] + pub use std::string::{String, ToString}; + + #[cfg(not(feature = "std"))] + pub use alloc::vec::{self, Vec}; + #[cfg(feature = "std")] + pub use std::vec::{self, Vec}; + + #[cfg(not(feature = "std"))] + pub use alloc::boxed::Box; + #[cfg(feature = "std")] + pub use std::boxed::Box; + + #[cfg(not(feature = "std"))] + pub use alloc::collections::{btree_map, BTreeMap}; + #[cfg(feature = "std")] + pub use std::collections::{btree_map, BTreeMap}; + + #[cfg(feature = "std")] + pub use std::error; +} + +//////////////////////////////////////////////////////////////////////////////// + #[doc(inline)] pub use self::de::{from_reader, from_slice, from_str, Deserializer, StreamDeserializer}; #[doc(inline)] @@ -355,8 +417,8 @@ pub use self::value::{from_value, to_value, Map, Number, Value}; macro_rules! try { ($e:expr) => { match $e { - ::std::result::Result::Ok(val) => val, - ::std::result::Result::Err(err) => return ::std::result::Result::Err(err), + ::lib::Result::Ok(val) => val, + ::lib::Result::Err(err) => return ::lib::Result::Err(err), } }; } @@ -370,6 +432,9 @@ pub mod map; pub mod ser; pub mod value; +mod features_check; + +mod io; mod iter; mod number; mod read; diff --git a/src/map.rs b/src/map.rs index 66fc991cc..0d717616c 100644 --- a/src/map.rs +++ b/src/map.rs @@ -6,16 +6,13 @@ //! [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html //! [`IndexMap`]: https://docs.rs/indexmap/*/indexmap/map/struct.IndexMap.html -use serde::{de, ser}; -use std::borrow::Borrow; -use std::fmt::{self, Debug}; -use std::hash::Hash; -use std::iter::FromIterator; -use std::ops; +use lib::borrow::Borrow; +use lib::iter::FromIterator; +use lib::*; + use value::Value; -#[cfg(not(feature = "preserve_order"))] -use std::collections::{btree_map, BTreeMap}; +use serde::{de, ser}; #[cfg(feature = "preserve_order")] use indexmap::{self, IndexMap}; @@ -152,7 +149,7 @@ impl Map { #[cfg(feature = "preserve_order")] use indexmap::map::Entry as EntryImpl; #[cfg(not(feature = "preserve_order"))] - use std::collections::btree_map::Entry as EntryImpl; + use lib::btree_map::Entry as EntryImpl; match self.map.entry(key.into()) { EntryImpl::Vacant(vacant) => Entry::Vacant(VacantEntry { vacant: vacant }), diff --git a/src/number.rs b/src/number.rs index 84672e9e0..fc75f4230 100644 --- a/src/number.rs +++ b/src/number.rs @@ -1,7 +1,8 @@ +use lib::*; + use error::Error; use serde::de::{self, Unexpected, Visitor}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::fmt::{self, Debug, Display}; #[cfg(feature = "arbitrary_precision")] use itoa; diff --git a/src/raw.rs b/src/raw.rs index d445960f1..9e635cde9 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -1,5 +1,4 @@ -use std::fmt::{self, Debug, Display}; -use std::mem; +use lib::*; use serde::de::value::BorrowedStrDeserializer; use serde::de::{ diff --git a/src/read.rs b/src/read.rs index 71b050413..6e795c9c8 100644 --- a/src/read.rs +++ b/src/read.rs @@ -1,5 +1,7 @@ -use std::ops::Deref; -use std::{char, cmp, io, str}; +use lib::ops::Deref; +use lib::*; + +use io; #[cfg(feature = "raw_value")] use serde::de::Visitor; diff --git a/src/ser.rs b/src/ser.rs index cfae3815f..ffc112c79 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -1,9 +1,9 @@ //! Serialize a Rust data structure into JSON data. -use std::fmt; -use std::io; -use std::num::FpCategory; -use std::str; +use lib::num::FpCategory; +use lib::*; + +use io; use super::error::{Error, ErrorCode, Result}; use serde::ser::{self, Impossible, Serialize}; @@ -460,7 +460,7 @@ where where T: fmt::Display, { - use std::fmt::Write; + use self::fmt::Write; struct Adapter<'ser, W: 'ser, F: 'ser> { writer: &'ser mut W, @@ -1638,7 +1638,9 @@ pub trait Formatter { where W: io::Write, { - itoa::write(writer, value).map(drop) + let mut buffer = itoa::Buffer::new(); + let s = buffer.format(value); + writer.write_all(s.as_bytes()) } /// Writes an integer value like `-123` to the specified writer. @@ -1647,7 +1649,9 @@ pub trait Formatter { where W: io::Write, { - itoa::write(writer, value).map(drop) + let mut buffer = itoa::Buffer::new(); + let s = buffer.format(value); + writer.write_all(s.as_bytes()) } /// Writes an integer value like `-123` to the specified writer. @@ -1656,7 +1660,9 @@ pub trait Formatter { where W: io::Write, { - itoa::write(writer, value).map(drop) + let mut buffer = itoa::Buffer::new(); + let s = buffer.format(value); + writer.write_all(s.as_bytes()) } /// Writes an integer value like `-123` to the specified writer. @@ -1665,7 +1671,9 @@ pub trait Formatter { where W: io::Write, { - itoa::write(writer, value).map(drop) + let mut buffer = itoa::Buffer::new(); + let s = buffer.format(value); + writer.write_all(s.as_bytes()) } /// Writes an integer value like `123` to the specified writer. @@ -1674,7 +1682,9 @@ pub trait Formatter { where W: io::Write, { - itoa::write(writer, value).map(drop) + let mut buffer = itoa::Buffer::new(); + let s = buffer.format(value); + writer.write_all(s.as_bytes()) } /// Writes an integer value like `123` to the specified writer. @@ -1683,7 +1693,9 @@ pub trait Formatter { where W: io::Write, { - itoa::write(writer, value).map(drop) + let mut buffer = itoa::Buffer::new(); + let s = buffer.format(value); + writer.write_all(s.as_bytes()) } /// Writes an integer value like `123` to the specified writer. @@ -1692,7 +1704,9 @@ pub trait Formatter { where W: io::Write, { - itoa::write(writer, value).map(drop) + let mut buffer = itoa::Buffer::new(); + let s = buffer.format(value); + writer.write_all(s.as_bytes()) } /// Writes an integer value like `123` to the specified writer. @@ -1701,7 +1715,9 @@ pub trait Formatter { where W: io::Write, { - itoa::write(writer, value).map(drop) + let mut buffer = itoa::Buffer::new(); + let s = buffer.format(value); + writer.write_all(s.as_bytes()) } /// Writes a floating point value like `-31.26e+12` to the specified writer. diff --git a/src/value/de.rs b/src/value/de.rs index c1f0810dd..c0de6b887 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -1,8 +1,5 @@ -use std::borrow::Cow; -use std::fmt; -use std::slice; -use std::str; -use std::vec; +use lib::str::FromStr; +use lib::*; use serde; use serde::de::{ @@ -134,7 +131,7 @@ impl<'de> Deserialize<'de> for Value { } } -impl str::FromStr for Value { +impl FromStr for Value { type Err = Error; fn from_str(s: &str) -> Result { super::super::de::from_str(s) diff --git a/src/value/from.rs b/src/value/from.rs index 2b743d2af..fdc3b6660 100644 --- a/src/value/from.rs +++ b/src/value/from.rs @@ -1,4 +1,5 @@ -use std::borrow::Cow; +use lib::iter::FromIterator; +use lib::*; use super::Value; use map::Map; @@ -182,7 +183,7 @@ impl<'a, T: Clone + Into> From<&'a [T]> for Value { } } -impl> ::std::iter::FromIterator for Value { +impl> FromIterator for Value { /// Convert an iteratable type to a `Value` /// /// # Examples diff --git a/src/value/index.rs b/src/value/index.rs index 47990a547..00f71f8e7 100644 --- a/src/value/index.rs +++ b/src/value/index.rs @@ -1,5 +1,4 @@ -use std::fmt; -use std::ops; +use lib::*; use super::Value; use map::Map; @@ -135,7 +134,7 @@ mod private { pub trait Sealed {} impl Sealed for usize {} impl Sealed for str {} - impl Sealed for String {} + impl Sealed for super::String {} impl<'a, T: ?Sized> Sealed for &'a T where T: Sealed {} } diff --git a/src/value/mod.rs b/src/value/mod.rs index fee9939fc..f0db11f42 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -90,10 +90,9 @@ //! [from_slice]: https://docs.serde.rs/serde_json/de/fn.from_slice.html //! [from_reader]: https://docs.serde.rs/serde_json/de/fn.from_reader.html -use std::fmt::{self, Debug}; -use std::io; -use std::mem; -use std::str; +use lib::*; + +use io; use serde::de::DeserializeOwned; use serde::ser::Serialize; diff --git a/src/value/partial_eq.rs b/src/value/partial_eq.rs index cfcf95706..7fc64c3bd 100644 --- a/src/value/partial_eq.rs +++ b/src/value/partial_eq.rs @@ -1,3 +1,5 @@ +use lib::*; + use super::Value; fn eq_i64(value: &Value, other: i64) -> bool { diff --git a/src/value/ser.rs b/src/value/ser.rs index b0e09beb5..eaba74bcb 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -1,3 +1,5 @@ +use lib::*; + use serde::ser::Impossible; use serde::{self, Serialize};