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.
This commit is contained in:
Markus Westerlind 2017-09-11 15:54:49 +02:00
parent 40c670e625
commit ad3335e5d6
5 changed files with 207 additions and 65 deletions

View File

@ -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<D>(deserializer: D) -> Result<Self, D::Error>
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<E>(self, value: &str) -> Result<$ty, E>
where
E: Error,
{
value.parse().map_err(Error::custom)
}
fn visit_bytes<E>(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<D>(deserializer: D) -> Result<Self, D::Error>
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<A>(self, data: A) -> Result<Self::Value, A::Error>
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);

View File

@ -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)
}
}
}

View File

@ -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::<Vec<_>>()
};
}

View File

@ -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")]

View File

@ -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! {