From 4600a9af7ff58248271667d860879a6a18b6191b Mon Sep 17 00:00:00 2001 From: freax13 Date: Tue, 26 Nov 2019 20:11:07 +0100 Subject: [PATCH 01/11] add alloc/no_std support --- Cargo.toml | 8 ++- src/de.rs | 14 +++-- src/error.rs | 18 ++++-- src/io.rs | 79 +++++++++++++++++++++++++++ src/iter.rs | 2 +- src/lib.rs | 10 +++- src/map.rs | 20 ++++--- src/number.rs | 2 +- src/read.rs | 7 ++- src/ser.rs | 118 ++++++++++++++++++++++++++++++++++++++-- src/value/de.rs | 16 +++++- src/value/from.rs | 9 ++- src/value/index.rs | 11 +++- src/value/mod.rs | 17 ++++-- src/value/partial_eq.rs | 2 + src/value/ser.rs | 7 +++ 16 files changed, 301 insertions(+), 39 deletions(-) create mode 100644 src/io.rs diff --git a/Cargo.toml b/Cargo.toml index a987aa0a5..35ca7b86f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ 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" ryu = "1.0" @@ -42,7 +42,11 @@ features = ["raw_value"] ### FEATURES ################################################################# [features] -default = [] +default = ["std"] + +std = ["serde/std"] + +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..f8f0228ce 100644 --- a/src/de.rs +++ b/src/de.rs @@ -1,10 +1,14 @@ //! 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 io; +use core::marker::PhantomData; +use core::result; +use core::str::FromStr; +use core::{i32, u64}; +#[cfg(feature = "alloc")] +use alloc::string::String; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; use serde::de::{self, Expected, Unexpected}; diff --git a/src/error.rs b/src/error.rs index 5e3e8f064..7f4967a62 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,10 +1,15 @@ //! When serializing or deserializing JSON goes wrong. +#[cfg(feature = "std")] use std::error; -use std::fmt::{self, Debug, Display}; -use std::io; -use std::result; -use std::str::FromStr; +use core::fmt::{self, Debug, Display}; +use io; +use core::result; +use core::str::FromStr; +#[cfg(feature = "alloc")] +use alloc::boxed::Box; +#[cfg(feature = "alloc")] +use alloc::string::{String, ToString}; use serde::de; use serde::ser; @@ -131,6 +136,7 @@ pub enum Category { } #[cfg_attr(feature = "cargo-clippy", allow(fallible_impl_from))] +#[cfg(feature = "std")] impl From for io::Error { /// Convert a `serde_json::Error` into an `io::Error`. /// @@ -333,6 +339,7 @@ impl Display for ErrorCode { } } +#[cfg(feature = "std")] impl error::Error for Error { fn source(&self) -> Option<&(error::Error + 'static)> { match self.err.code { @@ -342,6 +349,9 @@ impl error::Error for Error { } } +#[cfg(not(feature = "std"))] +impl serde::de::StdError for Error {} + impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { Display::fmt(&*self.err, f) diff --git a/src/io.rs b/src/io.rs new file mode 100644 index 000000000..d95d137ac --- /dev/null +++ b/src/io.rs @@ -0,0 +1,79 @@ +#[cfg(not(feature = "std"))] +use core::slice; + +#[cfg(feature = "std")] +pub use std::io::{Result, Write, Read, Error, Bytes, ErrorKind}; + +#[cfg(not(feature = "std"))] +pub type Error = &'static str; + +#[cfg(not(feature = "std"))] +pub type Result = core::result::Result; + +#[cfg(not(feature = "std"))] +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("failed to write whole buffer"), + Ok(n) => buf = &buf[n..], + Err(e) => return Err(e), + } + } + Ok(()) + } + + fn flush(&mut self) -> Result<()>; +} + +#[cfg(not(feature = "std"))] +impl Write for &mut W { + fn write(&mut self, buf: &[u8]) -> Result { + (*self).write(buf) + } + + fn flush(&mut self) -> Result<()> { + (*self).flush() + } +} + +#[cfg(all(not(feature = "std"), feature = "alloc"))] +impl Write for &mut serde::export::Vec { + fn write(&mut self, buf: &[u8]) -> Result { + self.extend(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> Result<()> {Ok(())} +} + +#[cfg(not(feature = "std"))] +pub trait Read { + fn read(&mut self, buf: &mut [u8]) -> Result; + fn bytes(self) -> Bytes where Self: Sized { + Bytes { + inner: self, + } + } +} + +#[cfg(not(feature = "std"))] +pub struct Bytes { + inner: R, +} + +#[cfg(not(feature = "std"))] +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)), + } + } +} \ No newline at end of file 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..ebfad4f47 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; @@ -338,6 +339,10 @@ extern crate serde; extern crate indexmap; extern crate itoa; extern crate ryu; +#[cfg(feature = "std")] +extern crate core; +#[cfg(feature = "alloc")] +extern crate alloc; #[doc(inline)] pub use self::de::{from_reader, from_slice, from_str, Deserializer, StreamDeserializer}; @@ -355,8 +360,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), + ::core::result::Result::Ok(val) => val, + ::core::result::Result::Err(err) => return ::core::result::Result::Err(err), } }; } @@ -370,6 +375,7 @@ pub mod map; pub mod ser; pub mod value; +mod io; mod iter; mod number; mod read; diff --git a/src/map.rs b/src/map.rs index 66fc991cc..1d82c4adc 100644 --- a/src/map.rs +++ b/src/map.rs @@ -7,15 +7,19 @@ //! [`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 core::borrow::Borrow; +use core::fmt::{self, Debug}; +use core::hash::Hash; +use core::iter::FromIterator; +use core::ops; use value::Value; +#[cfg(feature = "alloc")] +use alloc::string::String; -#[cfg(not(feature = "preserve_order"))] +#[cfg(all(not(feature = "preserve_order"), feature = "std"))] use std::collections::{btree_map, BTreeMap}; +#[cfg(all(not(feature = "preserve_order"), feature = "alloc"))] +use alloc::collections::{btree_map, BTreeMap}; #[cfg(feature = "preserve_order")] use indexmap::{self, IndexMap}; @@ -151,8 +155,10 @@ impl Map { { #[cfg(feature = "preserve_order")] use indexmap::map::Entry as EntryImpl; - #[cfg(not(feature = "preserve_order"))] + #[cfg(all(not(feature = "preserve_order"), feature = "std"))] use std::collections::btree_map::Entry as EntryImpl; + #[cfg(all(not(feature = "preserve_order"), feature = "alloc"))] + use alloc::collections::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..63916e3ff 100644 --- a/src/number.rs +++ b/src/number.rs @@ -1,7 +1,7 @@ use error::Error; use serde::de::{self, Unexpected, Visitor}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::fmt::{self, Debug, Display}; +use core::fmt::{self, Debug, Display}; #[cfg(feature = "arbitrary_precision")] use itoa; diff --git a/src/read.rs b/src/read.rs index 71b050413..a5d68b984 100644 --- a/src/read.rs +++ b/src/read.rs @@ -1,5 +1,8 @@ -use std::ops::Deref; -use std::{char, cmp, io, str}; +use core::ops::Deref; +use io; +use core::{char, cmp, str}; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; #[cfg(feature = "raw_value")] use serde::de::Visitor; diff --git a/src/ser.rs b/src/ser.rs index cfae3815f..170663c34 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -1,9 +1,13 @@ //! Serialize a Rust data structure into JSON data. -use std::fmt; -use std::io; -use std::num::FpCategory; -use std::str; +use core::fmt; +use io; +use core::num::FpCategory; +use core::str; +#[cfg(feature = "alloc")] +use alloc::string::{String, ToString}; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; use super::error::{Error, ErrorCode, Result}; use serde::ser::{self, Impossible, Serialize}; @@ -460,7 +464,7 @@ where where T: fmt::Display, { - use std::fmt::Write; + use core::fmt::Write; struct Adapter<'ser, W: 'ser, F: 'ser> { writer: &'ser mut W, @@ -1634,6 +1638,7 @@ pub trait Formatter { /// Writes an integer value like `-123` to the specified writer. #[inline] + #[cfg(feature = "std")] fn write_i8(&mut self, writer: &mut W, value: i8) -> io::Result<()> where W: io::Write, @@ -1643,6 +1648,19 @@ pub trait Formatter { /// Writes an integer value like `-123` to the specified writer. #[inline] + #[cfg(not(feature = "std"))] + fn write_i8(&mut self, writer: &mut W, value: i8) -> io::Result<()> + where + W: io::Write, + { + 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. + #[inline] + #[cfg(feature = "std")] fn write_i16(&mut self, writer: &mut W, value: i16) -> io::Result<()> where W: io::Write, @@ -1652,6 +1670,19 @@ pub trait Formatter { /// Writes an integer value like `-123` to the specified writer. #[inline] + #[cfg(not(feature = "std"))] + fn write_i16(&mut self, writer: &mut W, value: i16) -> io::Result<()> + where + W: io::Write, + { + 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. + #[inline] + #[cfg(feature = "std")] fn write_i32(&mut self, writer: &mut W, value: i32) -> io::Result<()> where W: io::Write, @@ -1661,6 +1692,19 @@ pub trait Formatter { /// Writes an integer value like `-123` to the specified writer. #[inline] + #[cfg(not(feature = "std"))] + fn write_i32(&mut self, writer: &mut W, value: i32) -> io::Result<()> + where + W: io::Write, + { + 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. + #[inline] + #[cfg(feature = "std")] fn write_i64(&mut self, writer: &mut W, value: i64) -> io::Result<()> where W: io::Write, @@ -1668,8 +1712,21 @@ pub trait Formatter { itoa::write(writer, value).map(drop) } + /// Writes an integer value like `-123` to the specified writer. + #[inline] + #[cfg(not(feature = "std"))] + fn write_i64(&mut self, writer: &mut W, value: i64) -> io::Result<()> + where + W: io::Write, + { + 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. #[inline] + #[cfg(feature = "std")] fn write_u8(&mut self, writer: &mut W, value: u8) -> io::Result<()> where W: io::Write, @@ -1679,6 +1736,19 @@ pub trait Formatter { /// Writes an integer value like `123` to the specified writer. #[inline] + #[cfg(not(feature = "std"))] + fn write_u8(&mut self, writer: &mut W, value: u8) -> io::Result<()> + where + W: io::Write, + { + 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. + #[inline] + #[cfg(feature = "std")] fn write_u16(&mut self, writer: &mut W, value: u16) -> io::Result<()> where W: io::Write, @@ -1688,6 +1758,19 @@ pub trait Formatter { /// Writes an integer value like `123` to the specified writer. #[inline] + #[cfg(not(feature = "std"))] + fn write_u16(&mut self, writer: &mut W, value: u16) -> io::Result<()> + where + W: io::Write, + { + 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. + #[inline] + #[cfg(feature = "std")] fn write_u32(&mut self, writer: &mut W, value: u32) -> io::Result<()> where W: io::Write, @@ -1697,6 +1780,19 @@ pub trait Formatter { /// Writes an integer value like `123` to the specified writer. #[inline] + #[cfg(not(feature = "std"))] + fn write_u32(&mut self, writer: &mut W, value: u32) -> io::Result<()> + where + W: io::Write, + { + 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. + #[inline] + #[cfg(feature = "std")] fn write_u64(&mut self, writer: &mut W, value: u64) -> io::Result<()> where W: io::Write, @@ -1704,6 +1800,18 @@ pub trait Formatter { itoa::write(writer, value).map(drop) } + /// Writes an integer value like `123` to the specified writer. + #[inline] + #[cfg(not(feature = "std"))] + fn write_u64(&mut self, writer: &mut W, value: u64) -> io::Result<()> + where + W: io::Write, + { + 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. #[inline] fn write_f32(&mut self, writer: &mut W, value: f32) -> io::Result<()> diff --git a/src/value/de.rs b/src/value/de.rs index c1f0810dd..d53735975 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -1,8 +1,18 @@ +#[cfg(feature = "std")] use std::borrow::Cow; -use std::fmt; -use std::slice; -use std::str; +#[cfg(feature = "alloc")] +use alloc::borrow::{Cow, ToOwned}; +use core::fmt; +use core::slice; +use core::str; +#[cfg(feature = "std")] use std::vec; +#[cfg(feature = "alloc")] +use alloc::vec; +#[cfg(feature = "alloc")] +use alloc::string::String; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; use serde; use serde::de::{ diff --git a/src/value/from.rs b/src/value/from.rs index 2b743d2af..cf00046f3 100644 --- a/src/value/from.rs +++ b/src/value/from.rs @@ -1,4 +1,11 @@ +#[cfg(feature = "std")] use std::borrow::Cow; +#[cfg(feature = "alloc")] +use alloc::borrow::Cow; +#[cfg(feature = "alloc")] +use alloc::string::{String, ToString}; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; use super::Value; use map::Map; @@ -182,7 +189,7 @@ impl<'a, T: Clone + Into> From<&'a [T]> for Value { } } -impl> ::std::iter::FromIterator for Value { +impl> ::core::iter::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..95a1d89d8 100644 --- a/src/value/index.rs +++ b/src/value/index.rs @@ -1,5 +1,9 @@ -use std::fmt; -use std::ops; +use core::fmt; +use core::ops; +#[cfg(feature = "alloc")] +use alloc::string::String; +#[cfg(feature = "alloc")] +use alloc::borrow::ToOwned; use super::Value; use map::Map; @@ -132,6 +136,9 @@ where // Prevent users from implementing the Index trait. mod private { + #[cfg(feature = "alloc")] + use alloc::string::String; + pub trait Sealed {} impl Sealed for usize {} impl Sealed for str {} diff --git a/src/value/mod.rs b/src/value/mod.rs index fee9939fc..6c9e25bdc 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -90,10 +90,14 @@ //! [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 core::fmt::{self, Debug}; +use io; +use core::mem; +use core::str; +#[cfg(feature = "alloc")] +use alloc::string::String; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; use serde::de::DeserializeOwned; use serde::ser::Serialize; @@ -194,11 +198,16 @@ struct WriterFormatter<'a, 'b: 'a> { impl<'a, 'b> io::Write for WriterFormatter<'a, 'b> { fn write(&mut self, buf: &[u8]) -> io::Result { + #[cfg(feature = "std")] fn io_error(_: E) -> io::Error { // Error value does not matter because fmt::Display impl below just // maps it to fmt::Error io::Error::new(io::ErrorKind::Other, "fmt error") } + #[cfg(not(feature = "std"))] + fn io_error(_: E) -> &'static str { + "fmt error" + } let s = try!(str::from_utf8(buf).map_err(io_error)); try!(self.inner.write_str(s).map_err(io_error)); Ok(buf.len()) diff --git a/src/value/partial_eq.rs b/src/value/partial_eq.rs index cfcf95706..af9e01683 100644 --- a/src/value/partial_eq.rs +++ b/src/value/partial_eq.rs @@ -1,4 +1,6 @@ use super::Value; +#[cfg(feature = "alloc")] +use alloc::string::String; fn eq_i64(value: &Value, other: i64) -> bool { value.as_i64().map_or(false, |i| i == other) diff --git a/src/value/ser.rs b/src/value/ser.rs index b0e09beb5..61c78dc8e 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -6,6 +6,13 @@ use map::Map; use number::Number; use value::{to_value, Value}; +#[cfg(feature = "alloc")] +use alloc::string::{String, ToString}; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; +#[cfg(feature = "alloc")] +use alloc::borrow::ToOwned; + impl Serialize for Value { #[inline] fn serialize(&self, serializer: S) -> Result From 65336702c3901206a188eb05adb994cb428db7c1 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Thu, 28 Nov 2019 19:50:47 +0100 Subject: [PATCH 02/11] Fix std feature dependency for itoa --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 35ca7b86f..cea6b1cfa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ appveyor = { repository = "serde-rs/json" } [dependencies] 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] @@ -44,7 +44,7 @@ features = ["raw_value"] [features] default = ["std"] -std = ["serde/std"] +std = ["serde/std", "itoa/std"] alloc = ["serde/alloc"] From 9ecf91504ae3fe33bbb16d71339c4993baf15999 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Mon, 20 Jan 2020 16:44:59 +0100 Subject: [PATCH 03/11] Add facade around std/alloc/core like serde does --- src/de.rs | 11 ++----- src/error.rs | 12 ++------ src/lib.rs | 65 ++++++++++++++++++++++++++++++++++++++--- src/map.rs | 23 +++++---------- src/number.rs | 3 +- src/raw.rs | 3 +- src/read.rs | 7 ++--- src/ser.rs | 12 +++----- src/value/de.rs | 19 ++---------- src/value/from.rs | 12 ++------ src/value/index.rs | 12 ++------ src/value/mod.rs | 9 ++---- src/value/partial_eq.rs | 4 +-- src/value/ser.rs | 9 ++---- 14 files changed, 98 insertions(+), 103 deletions(-) diff --git a/src/de.rs b/src/de.rs index f8f0228ce..c71b796eb 100644 --- a/src/de.rs +++ b/src/de.rs @@ -1,14 +1,9 @@ //! Deserialize JSON data to a Rust data structure. +use lib::str::FromStr; +use lib::*; + use io; -use core::marker::PhantomData; -use core::result; -use core::str::FromStr; -use core::{i32, u64}; -#[cfg(feature = "alloc")] -use alloc::string::String; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; use serde::de::{self, Expected, Unexpected}; diff --git a/src/error.rs b/src/error.rs index 7f4967a62..50c39df36 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,15 +1,9 @@ //! When serializing or deserializing JSON goes wrong. -#[cfg(feature = "std")] -use std::error; -use core::fmt::{self, Debug, Display}; +use lib::str::FromStr; +use lib::*; + use io; -use core::result; -use core::str::FromStr; -#[cfg(feature = "alloc")] -use alloc::boxed::Box; -#[cfg(feature = "alloc")] -use alloc::string::{String, ToString}; use serde::de; use serde::ser; diff --git a/src/lib.rs b/src/lib.rs index ebfad4f47..1ed269e1e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -339,11 +339,68 @@ extern crate serde; extern crate indexmap; extern crate itoa; extern crate ryu; -#[cfg(feature = "std")] -extern crate core; + +//////////////////////////////////////////////////////////////////////////////// + #[cfg(feature = "alloc")] 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(all(feature = "alloc", not(feature = "std")))] + pub use alloc::borrow::{Cow, ToOwned}; + #[cfg(feature = "std")] + pub use std::borrow::{Cow, ToOwned}; + + #[cfg(all(feature = "alloc", not(feature = "std")))] + pub use alloc::string::{String, ToString}; + #[cfg(feature = "std")] + pub use std::string::{String, ToString}; + + #[cfg(all(feature = "alloc", not(feature = "std")))] + pub use alloc::vec::{self, Vec}; + #[cfg(feature = "std")] + pub use std::vec::{self, Vec}; + + #[cfg(all(feature = "alloc", not(feature = "std")))] + pub use alloc::boxed::Box; + #[cfg(feature = "std")] + pub use std::boxed::Box; + + #[cfg(all(feature = "alloc", 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)] @@ -360,8 +417,8 @@ pub use self::value::{from_value, to_value, Map, Number, Value}; macro_rules! try { ($e:expr) => { match $e { - ::core::result::Result::Ok(val) => val, - ::core::result::Result::Err(err) => return ::core::result::Result::Err(err), + ::lib::Result::Ok(val) => val, + ::lib::Result::Err(err) => return ::lib::Result::Err(err), } }; } diff --git a/src/map.rs b/src/map.rs index 1d82c4adc..0d717616c 100644 --- a/src/map.rs +++ b/src/map.rs @@ -6,20 +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 core::borrow::Borrow; -use core::fmt::{self, Debug}; -use core::hash::Hash; -use core::iter::FromIterator; -use core::ops; +use lib::borrow::Borrow; +use lib::iter::FromIterator; +use lib::*; + use value::Value; -#[cfg(feature = "alloc")] -use alloc::string::String; -#[cfg(all(not(feature = "preserve_order"), feature = "std"))] -use std::collections::{btree_map, BTreeMap}; -#[cfg(all(not(feature = "preserve_order"), feature = "alloc"))] -use alloc::collections::{btree_map, BTreeMap}; +use serde::{de, ser}; #[cfg(feature = "preserve_order")] use indexmap::{self, IndexMap}; @@ -155,10 +148,8 @@ impl Map { { #[cfg(feature = "preserve_order")] use indexmap::map::Entry as EntryImpl; - #[cfg(all(not(feature = "preserve_order"), feature = "std"))] - use std::collections::btree_map::Entry as EntryImpl; - #[cfg(all(not(feature = "preserve_order"), feature = "alloc"))] - use alloc::collections::btree_map::Entry as EntryImpl; + #[cfg(not(feature = "preserve_order"))] + 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 63916e3ff..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 core::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 a5d68b984..6e795c9c8 100644 --- a/src/read.rs +++ b/src/read.rs @@ -1,8 +1,7 @@ -use core::ops::Deref; +use lib::ops::Deref; +use lib::*; + use io; -use core::{char, cmp, str}; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; #[cfg(feature = "raw_value")] use serde::de::Visitor; diff --git a/src/ser.rs b/src/ser.rs index 170663c34..325b8bfb7 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -1,13 +1,9 @@ //! Serialize a Rust data structure into JSON data. -use core::fmt; +use lib::num::FpCategory; +use lib::*; + use io; -use core::num::FpCategory; -use core::str; -#[cfg(feature = "alloc")] -use alloc::string::{String, ToString}; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; use super::error::{Error, ErrorCode, Result}; use serde::ser::{self, Impossible, Serialize}; @@ -464,7 +460,7 @@ where where T: fmt::Display, { - use core::fmt::Write; + use self::fmt::Write; struct Adapter<'ser, W: 'ser, F: 'ser> { writer: &'ser mut W, diff --git a/src/value/de.rs b/src/value/de.rs index d53735975..c0de6b887 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -1,18 +1,5 @@ -#[cfg(feature = "std")] -use std::borrow::Cow; -#[cfg(feature = "alloc")] -use alloc::borrow::{Cow, ToOwned}; -use core::fmt; -use core::slice; -use core::str; -#[cfg(feature = "std")] -use std::vec; -#[cfg(feature = "alloc")] -use alloc::vec; -#[cfg(feature = "alloc")] -use alloc::string::String; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; +use lib::str::FromStr; +use lib::*; use serde; use serde::de::{ @@ -144,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 cf00046f3..fdc3b6660 100644 --- a/src/value/from.rs +++ b/src/value/from.rs @@ -1,11 +1,5 @@ -#[cfg(feature = "std")] -use std::borrow::Cow; -#[cfg(feature = "alloc")] -use alloc::borrow::Cow; -#[cfg(feature = "alloc")] -use alloc::string::{String, ToString}; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; +use lib::iter::FromIterator; +use lib::*; use super::Value; use map::Map; @@ -189,7 +183,7 @@ impl<'a, T: Clone + Into> From<&'a [T]> for Value { } } -impl> ::core::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 95a1d89d8..00f71f8e7 100644 --- a/src/value/index.rs +++ b/src/value/index.rs @@ -1,9 +1,4 @@ -use core::fmt; -use core::ops; -#[cfg(feature = "alloc")] -use alloc::string::String; -#[cfg(feature = "alloc")] -use alloc::borrow::ToOwned; +use lib::*; use super::Value; use map::Map; @@ -136,13 +131,10 @@ where // Prevent users from implementing the Index trait. mod private { - #[cfg(feature = "alloc")] - use alloc::string::String; - 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 6c9e25bdc..f6fd21b28 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -90,14 +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 core::fmt::{self, Debug}; +use lib::*; + use io; -use core::mem; -use core::str; -#[cfg(feature = "alloc")] -use alloc::string::String; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; use serde::de::DeserializeOwned; use serde::ser::Serialize; diff --git a/src/value/partial_eq.rs b/src/value/partial_eq.rs index af9e01683..8ddd3c009 100644 --- a/src/value/partial_eq.rs +++ b/src/value/partial_eq.rs @@ -1,6 +1,6 @@ +use lib::String; + use super::Value; -#[cfg(feature = "alloc")] -use alloc::string::String; fn eq_i64(value: &Value, other: i64) -> bool { value.as_i64().map_or(false, |i| i == other) diff --git a/src/value/ser.rs b/src/value/ser.rs index 61c78dc8e..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}; @@ -6,13 +8,6 @@ use map::Map; use number::Number; use value::{to_value, Value}; -#[cfg(feature = "alloc")] -use alloc::string::{String, ToString}; -#[cfg(feature = "alloc")] -use alloc::vec::Vec; -#[cfg(feature = "alloc")] -use alloc::borrow::ToOwned; - impl Serialize for Value { #[inline] fn serialize(&self, serializer: S) -> Result From 15bfaf6a07768ccac3a1fbcf661a66a9f6b26d19 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Mon, 20 Jan 2020 19:34:47 +0100 Subject: [PATCH 04/11] Reorganize std::io facade --- src/io.rs | 62 +++++++++++++++++++++++++++++++++-------- src/value/mod.rs | 4 ++- src/value/partial_eq.rs | 2 +- 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/src/io.rs b/src/io.rs index d95d137ac..0ef20297a 100644 --- a/src/io.rs +++ b/src/io.rs @@ -1,15 +1,33 @@ +//! 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`. +//! +//! Because of this, we simply redefine those traits as if the error type is +//! simply a `&'static str` and reimplement those traits for `core` primitives +//! or `alloc` types, e.g. `Vec`. #[cfg(not(feature = "std"))] -use core::slice; +use lib::*; #[cfg(feature = "std")] -pub use std::io::{Result, Write, Read, Error, Bytes, ErrorKind}; +pub use std::io::ErrorKind; +#[cfg(feature = "std")] +pub use std::io::Error; #[cfg(not(feature = "std"))] pub type Error = &'static str; +#[cfg(feature = "std")] +pub use std::io::Result; #[cfg(not(feature = "std"))] pub type Result = core::result::Result; +#[cfg(feature = "std")] +pub use std::io::Write; #[cfg(not(feature = "std"))] pub trait Write { fn write(&mut self, buf: &[u8]) -> Result; @@ -30,35 +48,57 @@ pub trait Write { #[cfg(not(feature = "std"))] 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() } } -#[cfg(all(not(feature = "std"), feature = "alloc"))] -impl Write for &mut serde::export::Vec { +#[cfg(not(feature = "std"))] +impl Write for Vec { + #[inline] fn write(&mut self, buf: &[u8]) -> Result { - self.extend(buf); + self.extend_from_slice(buf); Ok(buf.len()) } - fn flush(&mut self) -> Result<()> {Ok(())} + #[inline] + fn write_all(&mut self, buf: &[u8]) -> Result<()> { + self.extend_from_slice(buf); + Ok(()) + } + + #[inline] + fn flush(&mut self) -> Result<()> { + Ok(()) + } } +#[cfg(feature = "std")] +pub use std::io::Read; #[cfg(not(feature = "std"))] pub trait Read { fn read(&mut self, buf: &mut [u8]) -> Result; - fn bytes(self) -> Bytes where Self: Sized { - Bytes { - inner: self, - } + fn bytes(self) -> Bytes + where + Self: Sized, + { + Bytes { inner: self } } } +#[cfg(feature = "std")] +pub use std::io::Bytes; #[cfg(not(feature = "std"))] pub struct Bytes { inner: R, @@ -76,4 +116,4 @@ impl Iterator for Bytes { Err(e) => Some(Err(e)), } } -} \ No newline at end of file +} diff --git a/src/value/mod.rs b/src/value/mod.rs index f6fd21b28..fdda55f7e 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -199,10 +199,12 @@ impl<'a, 'b> io::Write for WriterFormatter<'a, 'b> { // maps it to fmt::Error io::Error::new(io::ErrorKind::Other, "fmt error") } + #[cfg(not(feature = "std"))] - fn io_error(_: E) -> &'static str { + fn io_error(_: E) -> io::Error { "fmt error" } + let s = try!(str::from_utf8(buf).map_err(io_error)); try!(self.inner.write_str(s).map_err(io_error)); Ok(buf.len()) diff --git a/src/value/partial_eq.rs b/src/value/partial_eq.rs index 8ddd3c009..7fc64c3bd 100644 --- a/src/value/partial_eq.rs +++ b/src/value/partial_eq.rs @@ -1,4 +1,4 @@ -use lib::String; +use lib::*; use super::Value; From b47d2d1aec9db161b2cfadf5c3b43215bbbc9646 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Mon, 20 Jan 2020 20:35:58 +0100 Subject: [PATCH 05/11] Mention availability of alloc feature --- Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index cea6b1cfa..e942f55d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,10 @@ 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. From d7f7a51c720a8b2c9056e9a249dced1779097cae Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Mon, 20 Jan 2020 22:01:14 +0100 Subject: [PATCH 06/11] travis: Test no_std/alloc on 1.36.0 --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) 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 From 176ff712f83038d334107574cc81d0ad4cb4386f Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Mon, 20 Jan 2020 22:41:58 +0100 Subject: [PATCH 07/11] Inline the itoa::write calls Inlining this simple, already `core`-compatible function is better than noisily repeating the same definition that does exactly the same, albeit hidden behind a fn call. --- src/ser.rs | 88 ------------------------------------------------------ 1 file changed, 88 deletions(-) diff --git a/src/ser.rs b/src/ser.rs index 325b8bfb7..ffc112c79 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -1634,17 +1634,6 @@ pub trait Formatter { /// Writes an integer value like `-123` to the specified writer. #[inline] - #[cfg(feature = "std")] - fn write_i8(&mut self, writer: &mut W, value: i8) -> io::Result<()> - where - W: io::Write, - { - itoa::write(writer, value).map(drop) - } - - /// Writes an integer value like `-123` to the specified writer. - #[inline] - #[cfg(not(feature = "std"))] fn write_i8(&mut self, writer: &mut W, value: i8) -> io::Result<()> where W: io::Write, @@ -1656,17 +1645,6 @@ pub trait Formatter { /// Writes an integer value like `-123` to the specified writer. #[inline] - #[cfg(feature = "std")] - fn write_i16(&mut self, writer: &mut W, value: i16) -> io::Result<()> - where - W: io::Write, - { - itoa::write(writer, value).map(drop) - } - - /// Writes an integer value like `-123` to the specified writer. - #[inline] - #[cfg(not(feature = "std"))] fn write_i16(&mut self, writer: &mut W, value: i16) -> io::Result<()> where W: io::Write, @@ -1678,17 +1656,6 @@ pub trait Formatter { /// Writes an integer value like `-123` to the specified writer. #[inline] - #[cfg(feature = "std")] - fn write_i32(&mut self, writer: &mut W, value: i32) -> io::Result<()> - where - W: io::Write, - { - itoa::write(writer, value).map(drop) - } - - /// Writes an integer value like `-123` to the specified writer. - #[inline] - #[cfg(not(feature = "std"))] fn write_i32(&mut self, writer: &mut W, value: i32) -> io::Result<()> where W: io::Write, @@ -1700,17 +1667,6 @@ pub trait Formatter { /// Writes an integer value like `-123` to the specified writer. #[inline] - #[cfg(feature = "std")] - fn write_i64(&mut self, writer: &mut W, value: i64) -> io::Result<()> - where - W: io::Write, - { - itoa::write(writer, value).map(drop) - } - - /// Writes an integer value like `-123` to the specified writer. - #[inline] - #[cfg(not(feature = "std"))] fn write_i64(&mut self, writer: &mut W, value: i64) -> io::Result<()> where W: io::Write, @@ -1722,17 +1678,6 @@ pub trait Formatter { /// Writes an integer value like `123` to the specified writer. #[inline] - #[cfg(feature = "std")] - fn write_u8(&mut self, writer: &mut W, value: u8) -> io::Result<()> - where - W: io::Write, - { - itoa::write(writer, value).map(drop) - } - - /// Writes an integer value like `123` to the specified writer. - #[inline] - #[cfg(not(feature = "std"))] fn write_u8(&mut self, writer: &mut W, value: u8) -> io::Result<()> where W: io::Write, @@ -1744,17 +1689,6 @@ pub trait Formatter { /// Writes an integer value like `123` to the specified writer. #[inline] - #[cfg(feature = "std")] - fn write_u16(&mut self, writer: &mut W, value: u16) -> io::Result<()> - where - W: io::Write, - { - itoa::write(writer, value).map(drop) - } - - /// Writes an integer value like `123` to the specified writer. - #[inline] - #[cfg(not(feature = "std"))] fn write_u16(&mut self, writer: &mut W, value: u16) -> io::Result<()> where W: io::Write, @@ -1766,17 +1700,6 @@ pub trait Formatter { /// Writes an integer value like `123` to the specified writer. #[inline] - #[cfg(feature = "std")] - fn write_u32(&mut self, writer: &mut W, value: u32) -> io::Result<()> - where - W: io::Write, - { - itoa::write(writer, value).map(drop) - } - - /// Writes an integer value like `123` to the specified writer. - #[inline] - #[cfg(not(feature = "std"))] fn write_u32(&mut self, writer: &mut W, value: u32) -> io::Result<()> where W: io::Write, @@ -1788,17 +1711,6 @@ pub trait Formatter { /// Writes an integer value like `123` to the specified writer. #[inline] - #[cfg(feature = "std")] - fn write_u64(&mut self, writer: &mut W, value: u64) -> io::Result<()> - where - W: io::Write, - { - itoa::write(writer, value).map(drop) - } - - /// Writes an integer value like `123` to the specified writer. - #[inline] - #[cfg(not(feature = "std"))] fn write_u64(&mut self, writer: &mut W, value: u64) -> io::Result<()> where W: io::Write, From 7852d2f1e6e5a0f9849f96b4fe403788862a2746 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Tue, 21 Jan 2020 15:51:53 +0100 Subject: [PATCH 08/11] Add user-friendly error when no alloc or std feature are enabled --- src/features_check/error.rs | 1 + src/features_check/mod.rs | 13 +++++++++++++ src/lib.rs | 2 ++ 3 files changed, 16 insertions(+) create mode 100644 src/features_check/error.rs create mode 100644 src/features_check/mod.rs 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/lib.rs b/src/lib.rs index 1ed269e1e..30001405a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -432,6 +432,8 @@ pub mod map; pub mod ser; pub mod value; +mod features_check; + mod io; mod iter; mod number; From bac77ed0ff8b776f4bb50755e2c37c130e88dce2 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Tue, 21 Jan 2020 16:53:34 +0100 Subject: [PATCH 09/11] Imply using alloc crate when `std` is not enabled --- src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 30001405a..4d6512d42 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -342,7 +342,7 @@ extern crate ryu; //////////////////////////////////////////////////////////////////////////////// -#[cfg(feature = "alloc")] +#[cfg(not(feature = "std"))] extern crate alloc; /// A facade around all the types we need from the `std`, `core`, and `alloc` @@ -370,27 +370,27 @@ mod lib { pub use self::core::marker::{self, PhantomData}; pub use self::core::result::{self, Result}; - #[cfg(all(feature = "alloc", not(feature = "std")))] + #[cfg(not(feature = "std"))] pub use alloc::borrow::{Cow, ToOwned}; #[cfg(feature = "std")] pub use std::borrow::{Cow, ToOwned}; - #[cfg(all(feature = "alloc", not(feature = "std")))] + #[cfg(not(feature = "std"))] pub use alloc::string::{String, ToString}; #[cfg(feature = "std")] pub use std::string::{String, ToString}; - #[cfg(all(feature = "alloc", not(feature = "std")))] + #[cfg(not(feature = "std"))] pub use alloc::vec::{self, Vec}; #[cfg(feature = "std")] pub use std::vec::{self, Vec}; - #[cfg(all(feature = "alloc", not(feature = "std")))] + #[cfg(not(feature = "std"))] pub use alloc::boxed::Box; #[cfg(feature = "std")] pub use std::boxed::Box; - #[cfg(all(feature = "alloc", not(feature = "std")))] + #[cfg(not(feature = "std"))] pub use alloc::collections::{btree_map, BTreeMap}; #[cfg(feature = "std")] pub use std::collections::{btree_map, BTreeMap}; From 3b03040cf04eac8b28a2f5b65f84b018edddeea0 Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Tue, 21 Jan 2020 18:42:37 +0100 Subject: [PATCH 10/11] Make io a subset of `std::io` So that when implementing a no-`std` logic we don't break the build when some other dependency opts into `std` (causing API mismatch). --- src/error.rs | 9 ++---- src/io.rs | 82 ++++++++++++++++++++++++++++++++++++++++++++++-- src/value/mod.rs | 7 ----- 3 files changed, 81 insertions(+), 17 deletions(-) diff --git a/src/error.rs b/src/error.rs index 50c39df36..b0876f71a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -130,7 +130,6 @@ pub enum Category { } #[cfg_attr(feature = "cargo-clippy", allow(fallible_impl_from))] -#[cfg(feature = "std")] impl From for io::Error { /// Convert a `serde_json::Error` into an `io::Error`. /// @@ -333,9 +332,8 @@ impl Display for ErrorCode { } } -#[cfg(feature = "std")] -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, @@ -343,9 +341,6 @@ impl error::Error for Error { } } -#[cfg(not(feature = "std"))] -impl serde::de::StdError for Error {} - impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { Display::fmt(&*self.err, f) diff --git a/src/io.rs b/src/io.rs index 0ef20297a..0f8ca3a56 100644 --- a/src/io.rs +++ b/src/io.rs @@ -15,16 +15,87 @@ use lib::*; #[cfg(feature = "std")] pub use std::io::ErrorKind; +#[cfg(not(feature = "std"))] +pub enum ErrorKind { + InvalidData, + WriteZero, + Other, + UnexpectedEof, +} + +#[cfg(not(feature = "std"))] +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", + } + } +} #[cfg(feature = "std")] pub use std::io::Error; #[cfg(not(feature = "std"))] -pub type Error = &'static str; +pub struct Error { + repr: Repr, +} + +#[cfg(not(feature = "std"))] +enum Repr { + Simple(ErrorKind), + Custom(ErrorKind, Box), +} + +#[cfg(not(feature = "std"))] +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()), + } + } +} + +#[cfg(not(feature = "std"))] +impl Debug for Error { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(self, fmt) + } +} + +#[cfg(not(feature = "std"))] +impl serde::de::StdError for Error {} + +#[cfg(not(feature = "std"))] +impl From for Error { + #[inline] + fn from(kind: ErrorKind) -> Error { + Error { + repr: Repr::Simple(kind), + } + } +} + +#[cfg(not(feature = "std"))] +impl Error { + #[inline] + pub fn new(kind: ErrorKind, error: E) -> Error + where + E: Into>, + { + Error { + repr: Repr::Custom(kind, error.into()), + } + } +} #[cfg(feature = "std")] pub use std::io::Result; #[cfg(not(feature = "std"))] -pub type Result = core::result::Result; +pub type Result = result::Result; #[cfg(feature = "std")] pub use std::io::Write; @@ -35,7 +106,12 @@ pub trait Write { fn write_all(&mut self, mut buf: &[u8]) -> Result<()> { while !buf.is_empty() { match self.write(buf) { - Ok(0) => return Err("failed to write whole buffer"), + Ok(0) => { + return Err(Error::new( + ErrorKind::WriteZero, + "failed to write whole buffer", + )) + } Ok(n) => buf = &buf[n..], Err(e) => return Err(e), } diff --git a/src/value/mod.rs b/src/value/mod.rs index fdda55f7e..f0db11f42 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -193,18 +193,11 @@ struct WriterFormatter<'a, 'b: 'a> { impl<'a, 'b> io::Write for WriterFormatter<'a, 'b> { fn write(&mut self, buf: &[u8]) -> io::Result { - #[cfg(feature = "std")] fn io_error(_: E) -> io::Error { // Error value does not matter because fmt::Display impl below just // maps it to fmt::Error io::Error::new(io::ErrorKind::Other, "fmt error") } - - #[cfg(not(feature = "std"))] - fn io_error(_: E) -> io::Error { - "fmt error" - } - let s = try!(str::from_utf8(buf).map_err(io_error)); try!(self.inner.write_str(s).map_err(io_error)); Ok(buf.len()) From 8e8058a491b746909824b8775d74e2aa4ff0609c Mon Sep 17 00:00:00 2001 From: Igor Matuszewski Date: Tue, 21 Jan 2020 19:25:22 +0100 Subject: [PATCH 11/11] Move the core::io implementation to separate module --- src/{io.rs => io/core.rs} | 45 ++++----------------------------------- src/io/mod.rs | 19 +++++++++++++++++ 2 files changed, 23 insertions(+), 41 deletions(-) rename src/{io.rs => io/core.rs} (69%) create mode 100644 src/io/mod.rs diff --git a/src/io.rs b/src/io/core.rs similarity index 69% rename from src/io.rs rename to src/io/core.rs index 0f8ca3a56..d899eba1c 100644 --- a/src/io.rs +++ b/src/io/core.rs @@ -1,21 +1,9 @@ -//! 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`. -//! -//! Because of this, we simply redefine those traits as if the error type is -//! simply a `&'static str` and reimplement those traits for `core` primitives -//! or `alloc` types, e.g. `Vec`. -#[cfg(not(feature = "std"))] +//! Reimplements core logic and types from `std::io` in an `alloc`-friendly +//! fashion. +#![cfg(not(feature = "std"))] + use lib::*; -#[cfg(feature = "std")] -pub use std::io::ErrorKind; -#[cfg(not(feature = "std"))] pub enum ErrorKind { InvalidData, WriteZero, @@ -23,7 +11,6 @@ pub enum ErrorKind { UnexpectedEof, } -#[cfg(not(feature = "std"))] impl ErrorKind { #[inline] fn as_str(&self) -> &'static str { @@ -36,20 +23,15 @@ impl ErrorKind { } } -#[cfg(feature = "std")] -pub use std::io::Error; -#[cfg(not(feature = "std"))] pub struct Error { repr: Repr, } -#[cfg(not(feature = "std"))] enum Repr { Simple(ErrorKind), Custom(ErrorKind, Box), } -#[cfg(not(feature = "std"))] impl Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.repr { @@ -59,17 +41,14 @@ impl Display for Error { } } -#[cfg(not(feature = "std"))] impl Debug for Error { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(self, fmt) } } -#[cfg(not(feature = "std"))] impl serde::de::StdError for Error {} -#[cfg(not(feature = "std"))] impl From for Error { #[inline] fn from(kind: ErrorKind) -> Error { @@ -79,7 +58,6 @@ impl From for Error { } } -#[cfg(not(feature = "std"))] impl Error { #[inline] pub fn new(kind: ErrorKind, error: E) -> Error @@ -92,14 +70,8 @@ impl Error { } } -#[cfg(feature = "std")] -pub use std::io::Result; -#[cfg(not(feature = "std"))] pub type Result = result::Result; -#[cfg(feature = "std")] -pub use std::io::Write; -#[cfg(not(feature = "std"))] pub trait Write { fn write(&mut self, buf: &[u8]) -> Result; @@ -122,7 +94,6 @@ pub trait Write { fn flush(&mut self) -> Result<()>; } -#[cfg(not(feature = "std"))] impl Write for &mut W { #[inline] fn write(&mut self, buf: &[u8]) -> Result { @@ -140,7 +111,6 @@ impl Write for &mut W { } } -#[cfg(not(feature = "std"))] impl Write for Vec { #[inline] fn write(&mut self, buf: &[u8]) -> Result { @@ -160,9 +130,6 @@ impl Write for Vec { } } -#[cfg(feature = "std")] -pub use std::io::Read; -#[cfg(not(feature = "std"))] pub trait Read { fn read(&mut self, buf: &mut [u8]) -> Result; fn bytes(self) -> Bytes @@ -173,14 +140,10 @@ pub trait Read { } } -#[cfg(feature = "std")] -pub use std::io::Bytes; -#[cfg(not(feature = "std"))] pub struct Bytes { inner: R, } -#[cfg(not(feature = "std"))] impl Iterator for Bytes { type Item = Result; 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::*; +}