Optimize Address implementation

This reduces the mem::size_of::<Address>() from 72 to 32 and removes
two heap allocations of String when constructing a new instance of Address.
This commit is contained in:
Paolo Barbolini
2020-08-22 11:56:14 +02:00
committed by Alexis Mousset
parent f865fc1bce
commit 694a6d2852

View File

@@ -19,32 +19,30 @@ use std::{
/// **NOTE**: Enable feature "serde" to be able serialize/deserialize it using [serde](https://serde.rs/). /// **NOTE**: Enable feature "serde" to be able serialize/deserialize it using [serde](https://serde.rs/).
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct Address { pub struct Address {
/// User part
pub user: String,
/// Domain part
pub domain: String,
/// Complete address /// Complete address
complete: String, serialized: String,
/// Index into `serialized` before the '@'
at_start: usize,
} }
impl<U, D> TryFrom<(U, D)> for Address impl<U, D> TryFrom<(U, D)> for Address
where where
U: Into<String>, U: AsRef<str>,
D: Into<String>, D: AsRef<str>,
{ {
type Error = AddressError; type Error = AddressError;
fn try_from(from: (U, D)) -> Result<Self, Self::Error> { fn try_from((user, domain): (U, D)) -> Result<Self, Self::Error> {
let (user, domain) = from; let user = user.as_ref();
let user = user.into(); Address::check_user(user)?;
Address::check_user(&user)?;
let domain = domain.into(); let domain = domain.as_ref();
Address::check_domain(&domain)?; Address::check_domain(domain)?;
let complete = format!("{}@{}", &user, &domain);
let serialized = format!("{}@{}", user, domain);
Ok(Address { Ok(Address {
user, serialized,
domain, at_start: user.len(),
complete,
}) })
} }
} }
@@ -65,10 +63,20 @@ static LITERAL_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?i)\[([A-f0-9:\.]+)\
impl Address { impl Address {
/// Create email address from parts /// Create email address from parts
pub fn new<U: Into<String>, D: Into<String>>(user: U, domain: D) -> Result<Self, AddressError> { pub fn new<U: AsRef<str>, D: AsRef<str>>(user: U, domain: D) -> Result<Self, AddressError> {
(user, domain).try_into() (user, domain).try_into()
} }
/// Get the user part of this `Address`
pub fn user(&self) -> &str {
&self.serialized[..self.at_start]
}
/// Get the domain part of this `Address`
pub fn domain(&self) -> &str {
&self.serialized[self.at_start + 1..]
}
fn check_user(user: &str) -> Result<(), AddressError> { fn check_user(user: &str) -> Result<(), AddressError> {
if USER_RE.is_match(user) { if USER_RE.is_match(user) {
Ok(()) Ok(())
@@ -104,7 +112,7 @@ impl Address {
impl Display for Address { impl Display for Address {
fn fmt(&self, f: &mut Formatter) -> FmtResult { fn fmt(&self, f: &mut Formatter) -> FmtResult {
f.write_str(&self.complete) f.write_str(&self.serialized)
} }
} }
@@ -119,22 +127,21 @@ impl FromStr for Address {
Address::check_user(user)?; Address::check_user(user)?;
Address::check_domain(domain)?; Address::check_domain(domain)?;
Ok(Address { Ok(Address {
user: user.into(), serialized: val.into(),
domain: domain.into(), at_start: user.len(),
complete: val.to_string(),
}) })
} }
} }
impl AsRef<str> for Address { impl AsRef<str> for Address {
fn as_ref(&self) -> &str { fn as_ref(&self) -> &str {
&self.complete.as_ref() &self.serialized
} }
} }
impl AsRef<OsStr> for Address { impl AsRef<OsStr> for Address {
fn as_ref(&self) -> &OsStr { fn as_ref(&self) -> &OsStr {
self.complete.as_ref() self.serialized.as_ref()
} }
} }
@@ -176,7 +183,7 @@ pub mod serde {
where where
S: Serializer, S: Serializer,
{ {
serializer.serialize_str(&self.to_string()) serializer.serialize_str(self.as_ref())
} }
} }
@@ -274,3 +281,20 @@ pub mod serde {
} }
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_address() {
let addr_str = "something@example.com";
let addr = Address::from_str(addr_str).unwrap();
let addr2 = Address::new("something", "example.com").unwrap();
assert_eq!(addr, addr2);
assert_eq!(addr.user(), "something");
assert_eq!(addr.domain(), "example.com");
assert_eq!(addr2.user(), "something");
assert_eq!(addr2.domain(), "example.com");
}
}