diff --git a/Cargo.toml b/Cargo.toml index 72e0221..9dcab8b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,8 +10,10 @@ keywords = ["json5", "parse", "parser", "serde", "json"] edition = "2018" [dependencies] +itoa = "1.0.1" pest = "2.0" pest_derive = "2.0" +ryu = "1.0.9" serde = "1.0" [dev-dependencies] diff --git a/src/lib.rs b/src/lib.rs index 1694979..a069dcb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,7 +119,7 @@ //! ); //! assert_eq!( //! json5::to_string(&Val::Object(map)), -//! Ok("{\"a\":[null,true,42,42.42,NaN,\"hello\"]}".to_owned()), +//! Ok("{\"a\":[null,true,42.0,42.42,NaN,\"hello\"]}".to_owned()), //! ) //! ``` //! @@ -146,7 +146,7 @@ //! ); //! assert_eq!( //! json5::to_string(&Value::Object(map)), -//! Ok("{\"a\":[null,true,42,42.42,\"hello\"]}".to_owned()), +//! Ok("{\"a\":[null,true,42.0,42.42,\"hello\"]}".to_owned()), //! ) //! ``` //! diff --git a/src/ser.rs b/src/ser.rs index 6e271fc..515041c 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -1,31 +1,40 @@ use serde::ser::{self, Serialize}; -use std::{f32, f64}; +use std::{ + f32, f64, + fmt::{self, Write}, +}; use crate::error::{Error, Result}; /// Attempts to serialize the input as a JSON5 string (actually a JSON string). pub fn to_string(value: &T) -> Result +where + T: Serialize, +{ + Ok(String::from_utf8(to_bytes(value)?).expect("serialization emitted invalid UTF-8")) +} + +/// Attempts to serialize the input as a JSON5 byte sequence. +pub fn to_bytes(value: &T) -> Result> where T: Serialize, { let mut serializer = Serializer { - output: String::new(), + output: Vec::with_capacity(128), }; value.serialize(&mut serializer)?; Ok(serializer.output) } struct Serializer { - output: String, + output: Vec, // TODO settings for formatting (single vs double quotes, whitespace etc) } impl Serializer { - fn call_to_string(&mut self, v: &T) -> Result<()> - where - T: ToString, - { - self.output += &v.to_string(); + fn serialize_integer(&mut self, v: I) -> Result<()> { + self.output + .extend_from_slice(itoa::Buffer::new().format(v).as_bytes()); Ok(()) } } @@ -43,75 +52,79 @@ impl<'a> ser::Serializer for &'a mut Serializer { type SerializeStructVariant = Self; fn serialize_bool(self, v: bool) -> Result<()> { - self.call_to_string(&v) + self.output + .extend_from_slice(if v { b"true" } else { b"false" }); + Ok(()) } fn serialize_i8(self, v: i8) -> Result<()> { - self.call_to_string(&v) + self.serialize_integer(v) } fn serialize_i16(self, v: i16) -> Result<()> { - self.call_to_string(&v) + self.serialize_integer(v) } fn serialize_i32(self, v: i32) -> Result<()> { - self.call_to_string(&v) + self.serialize_integer(v) } fn serialize_i64(self, v: i64) -> Result<()> { - self.call_to_string(&v) + self.serialize_integer(v) } fn serialize_u8(self, v: u8) -> Result<()> { - self.call_to_string(&v) + self.serialize_integer(v) } fn serialize_u16(self, v: u16) -> Result<()> { - self.call_to_string(&v) + self.serialize_integer(v) } fn serialize_u32(self, v: u32) -> Result<()> { - self.call_to_string(&v) + self.serialize_integer(v) } fn serialize_u64(self, v: u64) -> Result<()> { - self.call_to_string(&v) + self.serialize_integer(v) } fn serialize_f32(self, v: f32) -> Result<()> { if v == f32::INFINITY { - self.output += "Infinity"; + self.output.extend_from_slice(b"Infinity"); } else if v == f32::NEG_INFINITY { - self.output += "-Infinity"; + self.output.extend_from_slice(b"-Infinity"); } else if v.is_nan() { - self.output += "NaN"; + self.output.extend_from_slice(b"NaN"); } else { - self.call_to_string(&v)?; + self.output + .extend_from_slice(ryu::Buffer::new().format_finite(v).as_bytes()); } Ok(()) } fn serialize_f64(self, v: f64) -> Result<()> { if v == f64::INFINITY { - self.output += "Infinity"; + self.output.extend_from_slice(b"Infinity"); } else if v == f64::NEG_INFINITY { - self.output += "-Infinity"; + self.output.extend_from_slice(b"-Infinity"); } else if v.is_nan() { - self.output += "NaN"; + self.output.extend_from_slice(b"NaN"); } else { - self.call_to_string(&v)?; + self.output + .extend_from_slice(ryu::Buffer::new().format_finite(v).as_bytes()); } Ok(()) } fn serialize_char(self, v: char) -> Result<()> { - self.serialize_str(&v.to_string()) + self.serialize_str(v.encode_utf8(&mut [0; 4])) } fn serialize_str(self, v: &str) -> Result<()> { - self.output += "\""; - self.output += &escape(v); - self.output += "\""; + self.output.push(b'"'); + let _ = Escaper(&mut self.output).write_str(v); + self.output.push(b'"'); Ok(()) } @@ -131,7 +144,7 @@ impl<'a> ser::Serializer for &'a mut Serializer { } fn serialize_unit(self) -> Result<()> { - self.output += "null"; + self.output.extend_from_slice(b"null"); Ok(()) } @@ -165,16 +178,16 @@ impl<'a> ser::Serializer for &'a mut Serializer { where T: ?Sized + Serialize, { - self.output += "{"; + self.output.push(b'{'); variant.serialize(&mut *self)?; // TODO drop the quotes where possible - self.output += ":"; + self.output.push(b':'); value.serialize(&mut *self)?; - self.output += "}"; + self.output.push(b'}'); Ok(()) } fn serialize_seq(self, _len: Option) -> Result { - self.output += "["; + self.output.push(b'['); Ok(self) } @@ -197,14 +210,14 @@ impl<'a> ser::Serializer for &'a mut Serializer { variant: &'static str, _len: usize, ) -> Result { - self.output += "{"; + self.output.push(b'{'); variant.serialize(&mut *self)?; - self.output += ":["; + self.output.extend_from_slice(b":["); Ok(self) } fn serialize_map(self, _len: Option) -> Result { - self.output += "{"; + self.output.push(b'{'); Ok(self) } @@ -219,11 +232,17 @@ impl<'a> ser::Serializer for &'a mut Serializer { variant: &'static str, _len: usize, ) -> Result { - self.output += "{"; + self.output.push(b'{'); variant.serialize(&mut *self)?; - self.output += ":{"; + self.output.extend_from_slice(b":{"); Ok(self) } + + fn collect_str(self, value: &T) -> Result<()> { + // Panic here becuase that's what std's `.to_string()` does + write!(Escaper(&mut self.output), "{}", value).expect("Display implementation failed"); + Ok(()) + } } impl<'a> ser::SerializeSeq for &'a mut Serializer { @@ -234,14 +253,14 @@ impl<'a> ser::SerializeSeq for &'a mut Serializer { where T: ?Sized + Serialize, { - if !self.output.ends_with('[') { - self.output += ","; + if self.output.last() != Some(&b'[') { + self.output.push(b','); } value.serialize(&mut **self) } fn end(self) -> Result<()> { - self.output += "]"; + self.output.push(b']'); Ok(()) } } @@ -290,7 +309,7 @@ impl<'a> ser::SerializeTupleVariant for &'a mut Serializer { } fn end(self) -> Result<()> { - self.output += "]}"; + self.output.extend_from_slice(b"]}"); Ok(()) } } @@ -303,8 +322,8 @@ impl<'a> ser::SerializeMap for &'a mut Serializer { where T: ?Sized + Serialize, { - if !self.output.ends_with('{') { - self.output += ","; + if self.output.last() != Some(&b'{') { + self.output.push(b','); } key.serialize(&mut **self) } @@ -313,12 +332,12 @@ impl<'a> ser::SerializeMap for &'a mut Serializer { where T: ?Sized + Serialize, { - self.output += ":"; + self.output.push(b':'); value.serialize(&mut **self) } fn end(self) -> Result<()> { - self.output += "}"; + self.output.push(b'}'); Ok(()) } } @@ -352,23 +371,27 @@ impl<'a> ser::SerializeStructVariant for &'a mut Serializer { } fn end(self) -> Result<()> { - self.output += "}}"; + self.output.extend_from_slice(b"}}"); Ok(()) } } -fn escape(v: &str) -> String { - v.chars() - .flat_map(|c| match c { - '"' => vec!['\\', c], - '\n' => vec!['\\', 'n'], - '\r' => vec!['\\', 'r'], - '\t' => vec!['\\', 't'], - '/' => vec!['\\', '/'], - '\\' => vec!['\\', '\\'], - '\u{0008}' => vec!['\\', 'b'], - '\u{000c}' => vec!['\\', 'f'], - c => vec![c], - }) - .collect() +struct Escaper<'a>(&'a mut Vec); +impl Write for Escaper<'_> { + fn write_str(&mut self, s: &str) -> fmt::Result { + for byte in s.bytes() { + match byte { + b'"' => self.0.extend_from_slice(b"\\\""), + b'\n' => self.0.extend_from_slice(b"\\n"), + b'\r' => self.0.extend_from_slice(b"\\r"), + b'\t' => self.0.extend_from_slice(b"\\t"), + b'/' => self.0.extend_from_slice(b"\\/"), + b'\\' => self.0.extend_from_slice(b"\\\\"), + 0x08 => self.0.extend_from_slice(b"\\b"), + 0x0C => self.0.extend_from_slice(b"\\f"), + byte => self.0.push(byte), + } + } + Ok(()) + } } diff --git a/tests/ser.rs b/tests/ser.rs index 31c5e03..e3d24fc 100644 --- a/tests/ser.rs +++ b/tests/ser.rs @@ -148,7 +148,7 @@ fn serializes_newtype_struct() { struct B(f64); serializes_to(A(42), "42"); - serializes_to(B(42.), "42"); + serializes_to(B(42.), "42.0"); } #[test] @@ -168,7 +168,7 @@ fn serializes_seq() { Val::Bool(true), Val::String("hello".to_owned()), ], - "[42,true,\"hello\"]", + "[42.0,true,\"hello\"]", ) } @@ -185,8 +185,8 @@ fn serializes_tuple_struct() { #[derive(Serialize, PartialEq, Debug)] struct B(f64, i32); - serializes_to(A(1, 2.), "[1,2]"); - serializes_to(B(1., 2), "[1,2]"); + serializes_to(A(1, 2.), "[1,2.0]"); + serializes_to(B(1., 2), "[1.0,2]"); } #[test]