Avoid creating owned strings in deserialization where not necessary

This commit is contained in:
Jonas Platte 2020-10-26 01:52:33 +01:00
parent 62d5108633
commit f2a78babbd
No known key found for this signature in database
GPG Key ID: 7D261D771D915378
8 changed files with 83 additions and 10 deletions

View File

@ -249,7 +249,7 @@ impl<'de> Deserialize<'de> for ErrCode {
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let s = Cow::<'de, str>::deserialize(deserializer)?; let s = ruma_serde::deserialize_cow_str(deserializer)?;
Ok(s.into()) Ok(s.into())
} }
} }

View File

@ -62,10 +62,9 @@ impl<'de> Deserialize<'de> for DeviceIdOrAllDevices {
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let value = &String::deserialize(deserializer)?; let s = ruma_serde::deserialize_cow_str(deserializer)?;
DeviceIdOrAllDevices::try_from(s.as_ref()).map_err(|_| {
DeviceIdOrAllDevices::try_from(&value[..]).map_err(|_| { de::Error::invalid_value(Unexpected::Str(&s), &"a valid device identifier or '*'")
de::Error::invalid_value(Unexpected::Str(&value), &"a valid device identifier or '*'")
}) })
} }
} }

View File

@ -151,7 +151,7 @@ impl<'de> Deserialize<'de> for RoomMemberCountIs {
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let s = String::deserialize(deserializer)?; let s = ruma_serde::deserialize_cow_str(deserializer)?;
FromStr::from_str(&s).map_err(serde::de::Error::custom) FromStr::from_str(&s).map_err(serde::de::Error::custom)
} }
} }

View File

@ -28,6 +28,7 @@ either = { version = "1.5.3", optional = true }
rand = { version = "0.7.3", optional = true } rand = { version = "0.7.3", optional = true }
ruma-identifiers-macros = { version = "=0.17.4", path = "../ruma-identifiers-macros" } ruma-identifiers-macros = { version = "=0.17.4", path = "../ruma-identifiers-macros" }
ruma-identifiers-validation = { version = "0.1.1", path = "../ruma-identifiers-validation", default-features = false } ruma-identifiers-validation = { version = "0.1.1", path = "../ruma-identifiers-validation", default-features = false }
ruma-serde = { version = "0.2.3", path = "../ruma-serde" }
# Renamed so we can have a serde feature. # Renamed so we can have a serde feature.
serde1 = { package = "serde", version = "1.0.114", optional = true, features = ["derive"] } serde1 = { package = "serde", version = "1.0.114", optional = true, features = ["derive"] }
strum = { version = "0.19.2", features = ["derive"] } strum = { version = "0.19.2", features = ["derive"] }

View File

@ -17,7 +17,7 @@ extern crate serde1 as serde;
use std::convert::TryFrom; use std::convert::TryFrom;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::de::{self, Deserialize as _, Deserializer, Unexpected}; use serde::de::{self, Deserializer, Unexpected};
#[doc(inline)] #[doc(inline)]
pub use crate::{ pub use crate::{
@ -81,7 +81,7 @@ where
D: Deserializer<'de>, D: Deserializer<'de>,
T: for<'a> std::convert::TryFrom<&'a str>, T: for<'a> std::convert::TryFrom<&'a str>,
{ {
std::borrow::Cow::<'_, str>::deserialize(deserializer).and_then(|v| { ruma_serde::deserialize_cow_str(deserializer).and_then(|v| {
T::try_from(&v).map_err(|_| de::Error::invalid_value(Unexpected::Str(&v), &expected_str)) T::try_from(&v).map_err(|_| de::Error::invalid_value(Unexpected::Str(&v), &expected_str))
}) })
} }

71
ruma-serde/src/cow.rs Normal file
View File

@ -0,0 +1,71 @@
use std::{borrow::Cow, str};
use serde::de::{self, Deserializer, Unexpected, Visitor};
pub fn deserialize_cow_str<'de, D>(deserializer: D) -> Result<Cow<'de, str>, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_string(CowStrVisitor)
}
struct CowStrVisitor;
impl<'de> Visitor<'de> for CowStrVisitor {
type Value = Cow<'de, str>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a string")
}
fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Cow::Borrowed(v))
}
fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
where
E: de::Error,
{
match str::from_utf8(v) {
Ok(s) => Ok(Cow::Borrowed(s)),
Err(_) => Err(de::Error::invalid_value(Unexpected::Bytes(v), &self)),
}
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Cow::Owned(v.to_owned()))
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Cow::Owned(v))
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: de::Error,
{
match str::from_utf8(v) {
Ok(s) => Ok(Cow::Owned(s.to_owned())),
Err(_) => Err(de::Error::invalid_value(Unexpected::Bytes(v), &self)),
}
}
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
where
E: de::Error,
{
match String::from_utf8(v) {
Ok(s) => Ok(Cow::Owned(s)),
Err(e) => Err(de::Error::invalid_value(Unexpected::Bytes(&e.into_bytes()), &self)),
}
}
}

View File

@ -2,7 +2,7 @@
//! string. //! string.
use serde::{ use serde::{
de::{Deserialize, DeserializeOwned, Deserializer, Error as _}, de::{DeserializeOwned, Deserializer, Error as _},
ser::{Error as _, Serialize, Serializer}, ser::{Error as _, Serialize, Serializer},
}; };
@ -20,6 +20,6 @@ where
T: DeserializeOwned, T: DeserializeOwned,
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let s = String::deserialize(deserializer)?; let s = crate::deserialize_cow_str(deserializer)?;
serde_json::from_str(&s).map_err(D::Error::custom) serde_json::from_str(&s).map_err(D::Error::custom)
} }

View File

@ -4,6 +4,7 @@ use serde::de::{Deserialize, IntoDeserializer};
pub mod can_be_empty; pub mod can_be_empty;
mod canonical_json; mod canonical_json;
mod cow;
pub mod duration; pub mod duration;
pub mod empty; pub mod empty;
pub mod json_string; pub mod json_string;
@ -16,6 +17,7 @@ pub use can_be_empty::{is_empty, CanBeEmpty};
pub use canonical_json::{ pub use canonical_json::{
to_string as to_canonical_json_string, value::CanonicalJsonValue, Error as CanonicalJsonError, to_string as to_canonical_json_string, value::CanonicalJsonValue, Error as CanonicalJsonError,
}; };
pub use cow::deserialize_cow_str;
pub use empty::vec_as_map_of_empty; pub use empty::vec_as_map_of_empty;
/// Check whether a value is equal to its default value. /// Check whether a value is equal to its default value.