From ad3335e5d6aac8db48773946def0720a0860aaad Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Mon, 11 Sep 2017 15:54:49 +0200 Subject: [PATCH] Serialize non-human-readble ip addresses as tuples Since we know exactly how many bytes we should serialize as we can hint to the serializer that it is not required which further reduces the serialized size when compared to just serializing as bytes. --- serde/src/de/impls.rs | 91 ++++++++++++++-------------------- serde/src/ser/impls.rs | 4 +- test_suite/tests/macros/mod.rs | 26 ++++++++++ test_suite/tests/test_de.rs | 89 ++++++++++++++++++++++++++++++--- test_suite/tests/test_ser.rs | 62 +++++++++++++++++++++++ 5 files changed, 207 insertions(+), 65 deletions(-) diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index be4a6301..b20d9ac7 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -868,71 +868,19 @@ map_impl!( //////////////////////////////////////////////////////////////////////////////// -macro_rules! count { - () => { - 0 - }; - ($first: expr $(,$rest: expr)*) => { - 1 + count!($($rest),*) - } -} - #[cfg(feature = "std")] macro_rules! parse_ip_impl { - ($ty:ty; $($size: expr),*) => { + ($ty:ty; $size: expr) => { impl<'de> Deserialize<'de> for $ty { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { - struct ParseVisitor; - impl<'de> Visitor<'de> for ParseVisitor { - type Value = $ty; - - #[allow(unused_assignments)] - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "Expected bytes of length ")?; - let mut i = 0; - $( - let sep = match i { - 0 => "", - _ if i + 1 == count!($size) => " or ", - _ => ", ", - }; - write!(formatter, "{}{}", sep, $size)?; - i += 1; - )* - Ok(()) - } - - fn visit_str(self, value: &str) -> Result<$ty, E> - where - E: Error, - { - value.parse().map_err(Error::custom) - } - - fn visit_bytes(self, value: &[u8]) -> Result<$ty, E> - where - E: Error, - { - match value.len() { - $( - $size => { - let mut buffer = [0; $size]; - buffer.copy_from_slice(value); - Ok(<$ty>::from(buffer)) - } - )* - _ => Err(Error::invalid_length(value.len(), &self)), - } - } - } if deserializer.is_human_readable() { let s = try!(String::deserialize(deserializer)); s.parse().map_err(Error::custom) } else { - deserializer.deserialize_bytes(ParseVisitor) + <[u8; $size]>::deserialize(deserializer).map(<$ty>::from) } } } @@ -940,7 +888,40 @@ macro_rules! parse_ip_impl { } #[cfg(feature = "std")] -parse_ip_impl!(net::IpAddr; 16, 4); +impl<'de> Deserialize<'de> for net::IpAddr { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + if deserializer.is_human_readable() { + let s = try!(String::deserialize(deserializer)); + s.parse().map_err(Error::custom) + } else { + struct EnumVisitor; + impl<'de> Visitor<'de> for EnumVisitor { + type Value = net::IpAddr; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a IpAddr") + } + + + fn visit_enum(self, data: A) -> Result + where + A: EnumAccess<'de>, + { + match try!(data.variant()) { + (0u32, v) => v.newtype_variant().map(net::IpAddr::V4), + (1u32, v) => v.newtype_variant().map(net::IpAddr::V6), + (_, _) => Err(Error::custom("Invalid IpAddr variant")), + } + } + } + const VARIANTS: &[&str] = &["V4", "V6"]; + deserializer.deserialize_enum("IpAddr", VARIANTS, EnumVisitor) + } + } +} #[cfg(feature = "std")] parse_ip_impl!(net::Ipv4Addr; 4); diff --git a/serde/src/ser/impls.rs b/serde/src/ser/impls.rs index 7fecd69f..014a6c52 100644 --- a/serde/src/ser/impls.rs +++ b/serde/src/ser/impls.rs @@ -569,7 +569,7 @@ impl Serialize for net::SocketAddrV4 { const MAX_LEN: usize = 21; serialize_display_bounded_length!(self, MAX_LEN, serializer) } else { - (self.ip().octets(), self.port()).serialize(serializer) + (self.ip(), self.port()).serialize(serializer) } } } @@ -585,7 +585,7 @@ impl Serialize for net::SocketAddrV6 { const MAX_LEN: usize = 47; serialize_display_bounded_length!(self, MAX_LEN, serializer) } else { - (self.ip().octets(), self.port()).serialize(serializer) + (self.ip(), self.port()).serialize(serializer) } } } diff --git a/test_suite/tests/macros/mod.rs b/test_suite/tests/macros/mod.rs index 4f6ea66e..9a917bcf 100644 --- a/test_suite/tests/macros/mod.rs +++ b/test_suite/tests/macros/mod.rs @@ -73,3 +73,29 @@ macro_rules! hashmap { } } } + +macro_rules! seq_impl { + (seq $first:expr,) => { + seq_impl!(seq $first) + }; + ($first:expr,) => { + seq_impl!($first) + }; + (seq $first:expr) => { + $first.into_iter() + }; + ($first:expr) => { + Some($first).into_iter() + }; + (seq $first:expr , $( $elem: tt)*) => { + $first.into_iter().chain(seq!( $($elem)* )) + }; + ($first:expr , $($elem: tt)*) => { + Some($first).into_iter().chain(seq!( $($elem)* )) + } +} +macro_rules! seq { + ($($tt: tt)*) => { + seq_impl!($($tt)*).collect::>() + }; +} diff --git a/test_suite/tests/test_de.rs b/test_suite/tests/test_de.rs index e1a015e2..ec0100bd 100644 --- a/test_suite/tests/test_de.rs +++ b/test_suite/tests/test_de.rs @@ -110,15 +110,15 @@ enum EnumSkipAll { ////////////////////////////////////////////////////////////////////////// macro_rules! declare_test { - ($name:ident { $($value:expr => $tokens:expr,)+ }) => { + ($name:ident $readable: ident { $($value:expr => $tokens:expr,)+ }) => { #[test] fn $name() { $( // Test ser/de roundtripping - assert_de_tokens(&$value, $tokens); + assert_de_tokens_readable(&$value, $tokens, $readable); // Test that the tokens are ignorable - assert_de_tokens_ignore($tokens); + assert_de_tokens_ignore($tokens, true); )+ } } @@ -127,7 +127,7 @@ macro_rules! declare_test { macro_rules! declare_tests { ($($name:ident { $($value:expr => $tokens:expr,)+ })+) => { $( - declare_test!($name { $($value => $tokens,)+ }); + declare_test!($name true { $($value => $tokens,)+ }); )+ } } @@ -143,7 +143,16 @@ macro_rules! declare_error_tests { } } -fn assert_de_tokens_ignore(ignorable_tokens: &[Token]) { +macro_rules! declare_non_human_readable_tests { + ($($name:ident { $($value:expr => $tokens:expr,)+ })+) => { + $( + declare_test!($name false { $($value => $tokens,)+ }); + )+ + } +} + + +fn assert_de_tokens_ignore(ignorable_tokens: &[Token], readable: bool) { #[derive(PartialEq, Debug, Deserialize)] struct IgnoreBase { a: i32, @@ -163,7 +172,7 @@ fn assert_de_tokens_ignore(ignorable_tokens: &[Token]) { .chain(vec![Token::MapEnd].into_iter()) .collect(); - let mut de = serde_test::Deserializer::new(&concated_tokens); + let mut de = serde_test::Deserializer::readable(&concated_tokens, readable); let base = IgnoreBase::deserialize(&mut de).unwrap(); assert_eq!(base, IgnoreBase { a: 1 }); } @@ -754,6 +763,70 @@ declare_tests! { } } +declare_non_human_readable_tests!{ + test_non_human_readable_net_ipv4addr { + net::Ipv4Addr::from(*b"1234") => &seq![ + Token::Tuple { len: 4 }, + seq b"1234".iter().map(|&b| Token::U8(b)), + Token::TupleEnd + ], + } + test_non_human_readable_net_ipv6addr { + net::Ipv6Addr::from(*b"1234567890123456") => &seq![ + Token::Tuple { len: 4 }, + seq b"1234567890123456".iter().map(|&b| Token::U8(b)), + Token::TupleEnd + ], + + } + test_non_human_readable_net_socketaddr { + net::SocketAddr::from((*b"1234567890123456", 1234)) => &seq![ + Token::Tuple { len: 2 }, + Token::Enum { name: "IpAddr" }, + Token::U32(1), + + Token::Tuple { len: 16 }, + seq b"1234567890123456".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + + Token::U16(1234), + Token::TupleEnd + ], + net::SocketAddr::from((*b"1234", 1234)) => &seq![ + Token::Tuple { len: 2 }, + Token::Enum { name: "IpAddr" }, + Token::U32(0), + + Token::Tuple { len: 4 }, + seq b"1234".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + + Token::U16(1234), + Token::TupleEnd + ], + net::SocketAddrV4::new(net::Ipv4Addr::from(*b"1234"), 1234) => &seq![ + Token::Tuple { len: 2 }, + + Token::Tuple { len: 4 }, + seq b"1234".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + + Token::U16(1234), + Token::TupleEnd + ], + net::SocketAddrV6::new(net::Ipv6Addr::from(*b"1234567890123456"), 1234, 0, 0) => &seq![ + Token::Tuple { len: 2 }, + + Token::Tuple { len: 16 }, + seq b"1234567890123456".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + + Token::U16(1234), + Token::TupleEnd + ], + } +} + #[cfg(feature = "unstable")] declare_tests! { test_rc_dst { @@ -795,7 +868,7 @@ fn test_osstring() { ]; assert_de_tokens(&value, &tokens); - assert_de_tokens_ignore(&tokens); + assert_de_tokens_ignore(&tokens, true); } #[cfg(windows)] @@ -815,7 +888,7 @@ fn test_osstring() { ]; assert_de_tokens(&value, &tokens); - assert_de_tokens_ignore(&tokens); + assert_de_tokens_ignore(&tokens, true); } #[cfg(feature = "unstable")] diff --git a/test_suite/tests/test_ser.rs b/test_suite/tests/test_ser.rs index a623e5d6..ce1ae654 100644 --- a/test_suite/tests/test_ser.rs +++ b/test_suite/tests/test_ser.rs @@ -78,6 +78,19 @@ macro_rules! declare_tests { } } +macro_rules! declare_non_human_readable_tests { + ($($name:ident { $($value:expr => $tokens:expr,)+ })+) => { + $( + #[test] + fn $name() { + $( + assert_ser_tokens_readable(&$value, $tokens, false); + )+ + } + )+ + } +} + declare_tests! { test_unit { () => &[Token::Unit], @@ -398,6 +411,55 @@ declare_tests! { } } +declare_non_human_readable_tests!{ + test_non_human_readable_net_ipv4addr { + net::Ipv4Addr::from(*b"1234") => &seq![ + Token::Tuple { len: 4 }, + seq b"1234".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + ], + } + test_non_human_readable_net_ipv6addr { + net::Ipv6Addr::from(*b"1234567890123456") => &seq![ + Token::Tuple { len: 16 }, + seq b"1234567890123456".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + ], + } + test_non_human_readable_net_socketaddr { + net::SocketAddr::from((*b"1234567890123456", 1234)) => &seq![ + Token::Tuple { len: 2 }, + + Token::Tuple { len: 16 }, + seq b"1234567890123456".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + + Token::U16(1234), + Token::TupleEnd, + ], + net::SocketAddrV4::new(net::Ipv4Addr::from(*b"1234"), 1234) => &seq![ + Token::Tuple { len: 2 }, + + Token::Tuple { len: 4 }, + seq b"1234".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + + Token::U16(1234), + Token::TupleEnd, + ], + net::SocketAddrV6::new(net::Ipv6Addr::from(*b"1234567890123456"), 1234, 0, 0) => &seq![ + Token::Tuple { len: 2 }, + + Token::Tuple { len: 16 }, + seq b"1234567890123456".iter().map(|&b| Token::U8(b)), + Token::TupleEnd, + + Token::U16(1234), + Token::TupleEnd, + ], + } +} + // Serde's implementation is not unstable, but the constructors are. #[cfg(feature = "unstable")] declare_tests! {