From 8aafb99910db17aae414f2a9e68f133bfe3f6778 Mon Sep 17 00:00:00 2001 From: Dennis Zhang <51769674+Dennis-Zhang-SH@users.noreply.github.com> Date: Thu, 1 Dec 2022 11:17:23 +0000 Subject: [PATCH 1/2] Minor improvements on macros --- Cargo.toml | 1 + src/de/map.rs | 22 +++++++++------------- src/de/mod.rs | 24 ++++++++++-------------- 3 files changed, 20 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1a4a29d..2bb24f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ log = "0.4" serde = "1.0" xml-rs = "0.8" thiserror = "1.0" +paste = "1.0.9" [dev-dependencies] serde = { version = "1.0", features = ["derive"] } diff --git a/src/de/map.rs b/src/de/map.rs index 0557d5e..c23a44e 100644 --- a/src/de/map.rs +++ b/src/de/map.rs @@ -1,5 +1,6 @@ use std::io::Read; +use paste::paste; use serde::de::{self, IntoDeserializer, Unexpected}; use serde::forward_to_deserialize_any; use xml::attribute::OwnedAttribute; @@ -89,9 +90,13 @@ impl<'de, 'a, R: 'a + Read, B: BufferedXmlReader> de::MapAccess<'de> for MapA struct AttrValueDeserializer(String); macro_rules! deserialize_type_attr { - ($deserialize:ident => $visit:ident) => { - fn $deserialize>(self, visitor: V) -> Result { - visitor.$visit(self.0.parse()?) + ($($type:ty), *) => { + paste! { + $( + fn []>(self, visitor: V) -> Result { + visitor.[](self.0.parse()?) + } + )* } }; } @@ -103,16 +108,7 @@ impl<'de> de::Deserializer<'de> for AttrValueDeserializer { visitor.visit_string(self.0) } - deserialize_type_attr!(deserialize_i8 => visit_i8); - deserialize_type_attr!(deserialize_i16 => visit_i16); - deserialize_type_attr!(deserialize_i32 => visit_i32); - deserialize_type_attr!(deserialize_i64 => visit_i64); - deserialize_type_attr!(deserialize_u8 => visit_u8); - deserialize_type_attr!(deserialize_u16 => visit_u16); - deserialize_type_attr!(deserialize_u32 => visit_u32); - deserialize_type_attr!(deserialize_u64 => visit_u64); - deserialize_type_attr!(deserialize_f32 => visit_f32); - deserialize_type_attr!(deserialize_f64 => visit_f64); + deserialize_type_attr! { i8, i16, i32, i64, u8, u16, u32, u64, f32, f64 } fn deserialize_enum>( self, diff --git a/src/de/mod.rs b/src/de/mod.rs index 486b38c..47d3a86 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -1,6 +1,7 @@ use std::{io::Read, marker::PhantomData}; use log::trace; +use paste::paste; use serde::de::{self, Unexpected}; use serde::forward_to_deserialize_any; use xml::name::OwnedName; @@ -236,10 +237,14 @@ impl<'de, R: Read, B: BufferedXmlReader> Deserializer { } macro_rules! deserialize_type { - ($deserialize:ident => $visit:ident) => { - fn $deserialize>(self, visitor: V) -> Result { - let value = self.prepare_parse_type::()?.parse()?; - visitor.$visit(value) + ($($type:ty), *) => { + paste! { + $( + fn []>(self, visitor: V) -> Result { + let value = self.prepare_parse_type::()?.parse()?; + visitor.[](value) + } + )* } }; } @@ -271,16 +276,7 @@ impl<'de, 'a, R: Read, B: BufferedXmlReader> de::Deserializer<'de> }) } - deserialize_type!(deserialize_i8 => visit_i8); - deserialize_type!(deserialize_i16 => visit_i16); - deserialize_type!(deserialize_i32 => visit_i32); - deserialize_type!(deserialize_i64 => visit_i64); - deserialize_type!(deserialize_u8 => visit_u8); - deserialize_type!(deserialize_u16 => visit_u16); - deserialize_type!(deserialize_u32 => visit_u32); - deserialize_type!(deserialize_u64 => visit_u64); - deserialize_type!(deserialize_f32 => visit_f32); - deserialize_type!(deserialize_f64 => visit_f64); + deserialize_type! {i8, i16, i32, i64, u8, u16, u32, u64, f32, f64} fn deserialize_bool>(self, visitor: V) -> Result { if let XmlEvent::StartElement { .. } = *self.peek()? { From 9638ec804d5e8789f329934d822d4f4102a23d3c Mon Sep 17 00:00:00 2001 From: Dennis Zhang <51769674+Dennis-Zhang-SH@users.noreply.github.com> Date: Thu, 1 Dec 2022 13:54:04 +0000 Subject: [PATCH 2/2] Some performance improve --- Cargo.toml | 2 ++ src/de/map.rs | 12 --------- src/de/mod.rs | 13 --------- src/lib.rs | 2 ++ src/macros.rs | 56 +++++++++++++++++++++++++++++++++++++++ src/ser/mod.rs | 72 ++++++++++++++++++++------------------------------ 6 files changed, 89 insertions(+), 68 deletions(-) create mode 100644 src/macros.rs diff --git a/Cargo.toml b/Cargo.toml index 2bb24f8..4e3f96d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,8 @@ serde = "1.0" xml-rs = "0.8" thiserror = "1.0" paste = "1.0.9" +itoa = "1.0.4" +ryu = "1.0.11" [dev-dependencies] serde = { version = "1.0", features = ["derive"] } diff --git a/src/de/map.rs b/src/de/map.rs index c23a44e..31da1ef 100644 --- a/src/de/map.rs +++ b/src/de/map.rs @@ -89,18 +89,6 @@ impl<'de, 'a, R: 'a + Read, B: BufferedXmlReader> de::MapAccess<'de> for MapA struct AttrValueDeserializer(String); -macro_rules! deserialize_type_attr { - ($($type:ty), *) => { - paste! { - $( - fn []>(self, visitor: V) -> Result { - visitor.[](self.0.parse()?) - } - )* - } - }; -} - impl<'de> de::Deserializer<'de> for AttrValueDeserializer { type Error = Error; diff --git a/src/de/mod.rs b/src/de/mod.rs index 47d3a86..f220462 100644 --- a/src/de/mod.rs +++ b/src/de/mod.rs @@ -236,19 +236,6 @@ impl<'de, R: Read, B: BufferedXmlReader> Deserializer { } } -macro_rules! deserialize_type { - ($($type:ty), *) => { - paste! { - $( - fn []>(self, visitor: V) -> Result { - let value = self.prepare_parse_type::()?.parse()?; - visitor.[](value) - } - )* - } - }; -} - impl<'de, 'a, R: Read, B: BufferedXmlReader> de::Deserializer<'de> for &'a mut Deserializer { diff --git a/src/lib.rs b/src/lib.rs index 41d9c82..8176348 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -184,6 +184,8 @@ //! ``` //! +#[macro_use] +mod macros; pub mod de; mod error; pub mod ser; diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..0d58e58 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,56 @@ +#[macro_export] +macro_rules! serialize_integers { + ($($type_suffix:expr), *) => { + paste! { + $( + #[inline] + fn [](self, value: []) -> Result + { + let mut buffer = itoa::Buffer::new(); + let s = buffer.format(value); + self.serialize_str(s) + } + + #[inline] + fn [](self, value: []) -> Result + { + let must_close_tag = self.build_start_tag()?; + let mut buffer = itoa::Buffer::new(); + let s = buffer.format(value); + self.characters(s)?; + if must_close_tag { + self.end_tag()?; + } + Ok(()) + } + )* + } + }; +} + +#[macro_export] +macro_rules! deserialize_type_attr { + ($($type:ty), *) => { + paste! { + $( + fn []>(self, visitor: V) -> Result { + visitor.[](self.0.parse()?) + } + )* + } + }; +} + +#[macro_export] +macro_rules! deserialize_type { + ($($type:ty), *) => { + paste! { + $( + fn []>(self, visitor: V) -> Result { + let value = self.prepare_parse_type::()?.parse()?; + visitor.[](value) + } + )* + } + }; +} \ No newline at end of file diff --git a/src/ser/mod.rs b/src/ser/mod.rs index 8130a8d..91111b7 100644 --- a/src/ser/mod.rs +++ b/src/ser/mod.rs @@ -10,8 +10,9 @@ use self::{ }; use crate::error::{Error, Result}; use log::debug; +use paste::paste; use serde::ser::Serialize; -use std::{collections::HashMap, io::Write}; +use std::{collections::HashMap, io::Write, str::from_utf8_unchecked}; use xml::writer::{EmitterConfig, EventWriter, XmlEvent}; /// A convenience method for serializing some object to a buffer. @@ -194,59 +195,41 @@ impl<'ser, W: Write> serde::ser::Serializer for &'ser mut Serializer { type SerializeStruct = StructSerializer<'ser, W>; type SerializeStructVariant = StructSerializer<'ser, W>; + #[inline] fn serialize_bool(self, v: bool) -> Result { - self.serialize_str(&v.to_string()) - } - - fn serialize_i8(self, v: i8) -> Result { - self.serialize_i64(i64::from(v)) - } - - fn serialize_i16(self, v: i16) -> Result { - self.serialize_i64(i64::from(v)) - } - - fn serialize_i32(self, v: i32) -> Result { - self.serialize_i64(i64::from(v)) - } - - fn serialize_i64(self, v: i64) -> Result { - self.serialize_str(&v.to_string()) - } - - fn serialize_u8(self, v: u8) -> Result { - self.serialize_u64(u64::from(v)) - } - - fn serialize_u16(self, v: u16) -> Result { - self.serialize_u64(u64::from(v)) - } - - fn serialize_u32(self, v: u32) -> Result { - self.serialize_u64(u64::from(v)) + let s = if v { + b"true" as &[u8] + } else { + b"false" as &[u8] + }; + self.serialize_bytes(s) } - fn serialize_u64(self, v: u64) -> Result { - let must_close_tag = self.build_start_tag()?; - self.characters(&v.to_string())?; - if must_close_tag { - self.end_tag()?; - } - Ok(()) - } + // some magic macros to short the code, could improve when concat_idents stable + serialize_integers! {8, 16, 32, 64} + #[inline] fn serialize_f32(self, v: f32) -> Result { - self.serialize_f64(f64::from(v)) + let mut buffer = ryu::Buffer::new(); + let s = buffer.format_finite(v); + self.serialize_str(s) } + #[inline] fn serialize_f64(self, v: f64) -> Result { - self.serialize_str(&v.to_string()) + let mut buffer = ryu::Buffer::new(); + let s = buffer.format_finite(v); + self.serialize_str(s) } + #[inline] fn serialize_char(self, v: char) -> Result { - self.serialize_str(&v.to_string()) + // A char encoded as UTF-8 takes 4 bytes at most. + let mut buf = [0; 4]; + self.serialize_str(v.encode_utf8(&mut buf)) } + #[inline] fn serialize_str(self, v: &str) -> Result { let must_close_tag = self.build_start_tag()?; self.characters(v)?; @@ -256,8 +239,11 @@ impl<'ser, W: Write> serde::ser::Serializer for &'ser mut Serializer { Ok(()) } - fn serialize_bytes(self, _v: &[u8]) -> Result { - unimplemented!() + #[inline] + fn serialize_bytes(self, v: &[u8]) -> Result { + // UTF-8 parsing error should be handled by crate users + let s = unsafe { from_utf8_unchecked(v) }; + self.serialize_str(s) } fn serialize_none(self) -> Result {