diff --git a/CHANGELOG.md b/CHANGELOG.md index a6895f72..b4b8f819 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Added + +* The `Bytes` type now supports borrowed and Cow arrays of fixed size (requires Rust 1.51+) + + ```rust + #[serde_as(as = "Bytes")] + #[serde(borrow)] + borrowed_array: &'a [u8; 15], + #[serde_as(as = "Bytes")] + #[serde(borrow)] + cow_array: Cow<'a, [u8; 15]>, + ``` + + Note: For borrowed arrays the used Deserializer needs to support Serde's 0-copy deserialization. + ## [1.9.2] - 2021-06-07 ### Fixed diff --git a/src/de/const_arrays.rs b/src/de/const_arrays.rs index c535849d..81850227 100644 --- a/src/de/const_arrays.rs +++ b/src/de/const_arrays.rs @@ -1,6 +1,7 @@ use super::*; use crate::utils::{MapIter, SeqIter}; use serde::de::*; +use std::borrow::Cow; use std::collections::{BTreeMap, HashMap}; use std::convert::TryInto; use std::fmt; @@ -191,6 +192,135 @@ impl<'de, const N: usize> DeserializeAs<'de, [u8; N]> for Bytes { } } +impl<'de, const N: usize> DeserializeAs<'de, &'de [u8; N]> for Bytes { + fn deserialize_as(deserializer: D) -> Result<&'de [u8; N], D::Error> + where + D: Deserializer<'de>, + { + struct ArrayVisitor; + + impl<'de, const M: usize> Visitor<'de> for ArrayVisitor { + type Value = &'de [u8; M]; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_fmt(format_args!("a borrowed byte array of size {}", M)) + } + + fn visit_borrowed_bytes(self, v: &'de [u8]) -> Result + where + E: Error, + { + v.try_into() + .map_err(|_| Error::invalid_length(v.len(), &self)) + } + + fn visit_borrowed_str(self, v: &'de str) -> Result + where + E: Error, + { + v.as_bytes() + .try_into() + .map_err(|_| Error::invalid_length(v.len(), &self)) + } + } + + deserializer.deserialize_bytes(ArrayVisitor::) + } +} + +impl<'de, const N: usize> DeserializeAs<'de, Cow<'de, [u8; N]>> for Bytes { + fn deserialize_as(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + struct CowVisitor; + + impl<'de, const M: usize> Visitor<'de> for CowVisitor { + type Value = Cow<'de, [u8; M]>; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a byte array") + } + + fn visit_borrowed_bytes(self, v: &'de [u8]) -> Result + where + E: Error, + { + Ok(Cow::Borrowed( + v.try_into() + .map_err(|_| Error::invalid_length(v.len(), &self))?, + )) + } + + fn visit_borrowed_str(self, v: &'de str) -> Result + where + E: Error, + { + Ok(Cow::Borrowed( + v.as_bytes() + .try_into() + .map_err(|_| Error::invalid_length(v.len(), &self))?, + )) + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: Error, + { + Ok(Cow::Owned( + v.to_vec() + .try_into() + .map_err(|_| Error::invalid_length(v.len(), &self))?, + )) + } + + fn visit_str(self, v: &str) -> Result + where + E: Error, + { + Ok(Cow::Owned( + v.as_bytes() + .to_vec() + .try_into() + .map_err(|_| Error::invalid_length(v.len(), &self))?, + )) + } + + fn visit_byte_buf(self, v: Vec) -> Result + where + E: Error, + { + let len = v.len(); + Ok(Cow::Owned( + v.try_into() + .map_err(|_| Error::invalid_length(len, &self))?, + )) + } + + fn visit_string(self, v: String) -> Result + where + E: Error, + { + let len = v.len(); + Ok(Cow::Owned( + v.into_bytes() + .try_into() + .map_err(|_| Error::invalid_length(len, &self))?, + )) + } + + fn visit_seq(self, seq: V) -> Result + where + V: SeqAccess<'de>, + { + Ok(Cow::Owned(array_from_iterator(SeqIter::new(seq), &self)?)) + } + } + + deserializer.deserialize_bytes(CowVisitor) + } +} + impl<'de, const N: usize> DeserializeAs<'de, Box<[u8; N]>> for Bytes { fn deserialize_as(deserializer: D) -> Result, D::Error> where diff --git a/src/lib.rs b/src/lib.rs index 1b65832d..c6fd5f3d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1333,11 +1333,13 @@ pub struct TimestampNanoSecondsWithFrac< /// The type provides de-/serialization for these types: /// /// * `[u8; N]`, Rust 1.51+, not possible using `serde_bytes` +/// * `&[u8; N]`, Rust 1.51+, not possible using `serde_bytes` /// * `&[u8]` /// * `Box<[u8; N]>`, Rust 1.51+, not possible using `serde_bytes` /// * `Box<[u8]>` /// * `Vec` /// * `Cow<'_, [u8]>` +/// * `Cow<'_, [u8; N]>`, Rust 1.51+, not possible using `serde_bytes` /// /// # Examples /// @@ -1359,6 +1361,10 @@ pub struct TimestampNanoSecondsWithFrac< /// #[serde_as(as = "Bytes")] /// #[serde(borrow)] /// cow: Cow<'a, [u8]>, +/// # #[cfg(FALSE)] +/// #[serde_as(as = "Bytes")] +/// #[serde(borrow)] +/// cow_array: Cow<'a, [u8; 15]>, /// #[serde_as(as = "Bytes")] /// vec: Vec, /// } @@ -1368,16 +1374,19 @@ pub struct TimestampNanoSecondsWithFrac< /// array: b"0123456789ABCDE".clone(), /// boxed: b"...".to_vec().into_boxed_slice(), /// cow: Cow::Borrowed(b"FooBar"), +/// # #[cfg(FALSE)] +/// cow_array: Cow::Borrowed(&[42u8; 15]), /// vec: vec![0x41, 0x61, 0x21], /// }; /// let expected = r#"( /// array: "MDEyMzQ1Njc4OUFCQ0RF", /// boxed: "Li4u", /// cow: "Rm9vQmFy", +/// cow_array: "KioqKioqKioqKioqKioq", /// vec: "QWEh", /// )"#; /// # drop(expected); -/// # // Create a fake expected value without the array to make the test compile without const generics +/// # // Create a fake expected value that doesn't use const generics /// # let expected = r#"( /// # boxed: "Li4u", /// # cow: "Rm9vQmFy", @@ -1391,6 +1400,50 @@ pub struct TimestampNanoSecondsWithFrac< /// # } /// ``` /// +/// Fully borrowed types can also be used but you'll need a Deserializer that +/// supports Serde's 0-copy deserialization: +/// +/// ``` +/// # #[cfg(feature = "macros")] { +/// # use serde::{Deserialize, Serialize}; +/// # use serde_with::{serde_as, Bytes}; +/// # use std::borrow::Cow; +/// # +/// #[serde_as] +/// # #[derive(Debug, PartialEq)] +/// #[derive(Deserialize, Serialize)] +/// struct TestBorrows<'a> { +/// # #[cfg(FALSE)] +/// #[serde_as(as = "Bytes")] +/// #[serde(borrow)] +/// array_buf: &'a [u8; 15], +/// #[serde_as(as = "Bytes")] +/// #[serde(borrow)] +/// buf: &'a [u8], +/// } +/// +/// let value = TestBorrows { +/// # #[cfg(FALSE)] +/// array_buf: &[10u8; 15], +/// buf: &[20u8, 21u8, 22u8], +/// }; +/// let expected = r#"( +/// array_buf: "CgoKCgoKCgoKCgoKCgoK", +/// buf: "FBUW", +/// )"#; +/// # drop(expected); +/// # // Create a fake expected value that doesn't use const generics +/// # let expected = r#"( +/// # buf: "FBUW", +/// # )"#; +/// +/// # let pretty_config = ron::ser::PrettyConfig::new() +/// # .with_new_line("\n".into()); +/// assert_eq!(expected, ron::ser::to_string_pretty(&value, pretty_config).unwrap()); +/// // RON doesn't support borrowed deserialization of byte arrays +/// # } +/// ``` +/// /// ## Alternative to [`BytesOrString`] /// /// The [`Bytes`] can replace [`BytesOrString`]. diff --git a/src/ser/const_arrays.rs b/src/ser/const_arrays.rs index f7d4daed..efac473c 100644 --- a/src/ser/const_arrays.rs +++ b/src/ser/const_arrays.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use super::*; use std::collections::{BTreeMap, HashMap}; @@ -52,6 +53,15 @@ impl<'a, const N: usize> SerializeAs<[u8; N]> for Bytes { } } +impl<'a, const N: usize> SerializeAs<&[u8; N]> for Bytes { + fn serialize_as(bytes: &&[u8; N], serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_bytes(*bytes) + } +} + impl<'a, const N: usize> SerializeAs> for Bytes { fn serialize_as(bytes: &Box<[u8; N]>, serializer: S) -> Result where @@ -60,3 +70,12 @@ impl<'a, const N: usize> SerializeAs> for Bytes { serializer.serialize_bytes(&**bytes) } } + +impl<'a, const N: usize> SerializeAs> for Bytes { + fn serialize_as(bytes: &Cow<'a, [u8; N]>, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_bytes(bytes.as_ref()) + } +}