From 4600a9af7ff58248271667d860879a6a18b6191b Mon Sep 17 00:00:00 2001 From: freax13 Date: Tue, 26 Nov 2019 20:11:07 +0100 Subject: [PATCH] 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 a987aa0..35ca7b8 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 5c9c20b..f8f0228 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 5e3e8f0..7f4967a 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 0000000..d95d137 --- /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 1a9a954..53cbcbc 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 17e3689..ebfad4f 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 66fc991..1d82c4a 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 84672e9..63916e3 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 71b0504..a5d68b9 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 cfae381..170663c 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 c1f0810..d537359 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 2b743d2..cf00046 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 47990a5..95a1d89 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 fee9939..6c9e25b 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 cfcf957..af9e016 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 b0e09be..61c78dc 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