diff options
Diffstat (limited to 'vendor/zerovec/src/map/serde.rs')
-rw-r--r-- | vendor/zerovec/src/map/serde.rs | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/vendor/zerovec/src/map/serde.rs b/vendor/zerovec/src/map/serde.rs new file mode 100644 index 000000000..dbe4b433d --- /dev/null +++ b/vendor/zerovec/src/map/serde.rs @@ -0,0 +1,313 @@ +// This file is part of ICU4X. For terms of use, please see the file +// called LICENSE at the top level of the ICU4X source tree +// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). + +use super::{MutableZeroVecLike, ZeroMap, ZeroMapBorrowed, ZeroMapKV, ZeroVecLike}; +use core::fmt; +use core::marker::PhantomData; +use serde::de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor}; +#[cfg(feature = "serde")] +use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer}; + +/// This impl can be made available by enabling the optional `serde` feature of the `zerovec` crate +#[cfg(feature = "serde")] +impl<'a, K, V> Serialize for ZeroMap<'a, K, V> +where + K: ZeroMapKV<'a> + Serialize + ?Sized + Ord, + V: ZeroMapKV<'a> + Serialize + ?Sized, + K::Container: Serialize, + V::Container: Serialize, +{ + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + if serializer.is_human_readable() { + // Many human-readable formats don't support values other + // than numbers and strings as map keys. For them, we can serialize + // as a vec of tuples instead + if let Some(k) = self.iter_keys().next() { + if !K::Container::zvl_get_as_t(k, super::serde_helpers::is_num_or_string) { + let mut seq = serializer.serialize_seq(Some(self.len()))?; + for (k, v) in self.iter() { + K::Container::zvl_get_as_t(k, |k| { + V::Container::zvl_get_as_t(v, |v| seq.serialize_element(&(k, v))) + })?; + } + return seq.end(); + } + } + let mut map = serializer.serialize_map(Some(self.len()))?; + for (k, v) in self.iter() { + K::Container::zvl_get_as_t(k, |k| map.serialize_key(k))?; + V::Container::zvl_get_as_t(v, |v| map.serialize_value(v))?; + } + map.end() + } else { + (&self.keys, &self.values).serialize(serializer) + } + } +} + +/// This impl can be made available by enabling the optional `serde` feature of the `zerovec` crate +#[cfg(feature = "serde")] +impl<'a, K, V> Serialize for ZeroMapBorrowed<'a, K, V> +where + K: ZeroMapKV<'a> + Serialize + ?Sized + Ord, + V: ZeroMapKV<'a> + Serialize + ?Sized, + K::Container: Serialize, + V::Container: Serialize, +{ + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + ZeroMap::<K, V>::from(*self).serialize(serializer) + } +} + +/// Modified example from https://serde.rs/deserialize-map.html +struct ZeroMapMapVisitor<'a, K, V> +where + K: ZeroMapKV<'a> + ?Sized + Ord, + V: ZeroMapKV<'a> + ?Sized, +{ + #[allow(clippy::type_complexity)] // it's a marker type, complexity doesn't matter + marker: PhantomData<fn() -> (&'a K::OwnedType, &'a V::OwnedType)>, +} + +impl<'a, K, V> ZeroMapMapVisitor<'a, K, V> +where + K: ZeroMapKV<'a> + ?Sized + Ord, + V: ZeroMapKV<'a> + ?Sized, +{ + fn new() -> Self { + ZeroMapMapVisitor { + marker: PhantomData, + } + } +} + +impl<'a, 'de, K, V> Visitor<'de> for ZeroMapMapVisitor<'a, K, V> +where + K: ZeroMapKV<'a> + Ord + ?Sized, + V: ZeroMapKV<'a> + ?Sized, + K::OwnedType: Deserialize<'de>, + V::OwnedType: Deserialize<'de>, +{ + type Value = ZeroMap<'a, K, V>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a map produced by ZeroMap") + } + + fn visit_seq<S>(self, mut access: S) -> Result<Self::Value, S::Error> + where + S: SeqAccess<'de>, + { + let mut map = ZeroMap::with_capacity(access.size_hint().unwrap_or(0)); + + // While there are entries remaining in the input, add them + // into our map. + while let Some((key, value)) = access.next_element::<(K::OwnedType, V::OwnedType)>()? { + // Try to append it at the end, hoping for a sorted map. + // If not sorted, return an error + // a serialized map that came from another ZeroMap + if map + .try_append( + K::Container::owned_as_t(&key), + V::Container::owned_as_t(&value), + ) + .is_some() + { + return Err(de::Error::custom( + "ZeroMap's keys must be sorted while deserializing", + )); + } + } + + Ok(map) + } + + fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error> + where + M: MapAccess<'de>, + { + let mut map = ZeroMap::with_capacity(access.size_hint().unwrap_or(0)); + + // While there are entries remaining in the input, add them + // into our map. + while let Some((key, value)) = access.next_entry::<K::OwnedType, V::OwnedType>()? { + // Try to append it at the end, hoping for a sorted map. + // If not sorted, return an error + // a serialized map that came from another ZeroMap + if map + .try_append( + K::Container::owned_as_t(&key), + V::Container::owned_as_t(&value), + ) + .is_some() + { + return Err(de::Error::custom( + "ZeroMap's keys must be sorted while deserializing", + )); + } + } + + Ok(map) + } +} + +/// This impl can be made available by enabling the optional `serde` feature of the `zerovec` crate +impl<'de, 'a, K, V> Deserialize<'de> for ZeroMap<'a, K, V> +where + K: ZeroMapKV<'a> + Ord + ?Sized, + V: ZeroMapKV<'a> + ?Sized, + K::Container: Deserialize<'de>, + V::Container: Deserialize<'de>, + K::OwnedType: Deserialize<'de>, + V::OwnedType: Deserialize<'de>, + 'de: 'a, +{ + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + if deserializer.is_human_readable() { + deserializer.deserialize_any(ZeroMapMapVisitor::<'a, K, V>::new()) + } else { + let (keys, values): (K::Container, V::Container) = + Deserialize::deserialize(deserializer)?; + if keys.zvl_len() != values.zvl_len() { + return Err(de::Error::custom( + "Mismatched key and value sizes in ZeroMap", + )); + } + // #1433: If keys are out of order, treat it as GIGO. + debug_assert!(keys.zvl_is_ascending()); + Ok(Self { keys, values }) + } + } +} + +// /// This impl can be made available by enabling the optional `serde` feature of the `zerovec` crate +impl<'de, 'a, K, V> Deserialize<'de> for ZeroMapBorrowed<'a, K, V> +where + K: ZeroMapKV<'a> + Ord + ?Sized, + V: ZeroMapKV<'a> + ?Sized, + K::Container: Deserialize<'de>, + V::Container: Deserialize<'de>, + K::OwnedType: Deserialize<'de>, + V::OwnedType: Deserialize<'de>, + 'de: 'a, +{ + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + if deserializer.is_human_readable() { + Err(de::Error::custom( + "ZeroMapBorrowed cannot be deserialized from human-readable formats", + )) + } else { + let deserialized: ZeroMap<'a, K, V> = ZeroMap::deserialize(deserializer)?; + let keys = if let Some(keys) = deserialized.keys.zvl_as_borrowed_inner() { + keys + } else { + return Err(de::Error::custom( + "ZeroMapBorrowed can only deserialize in zero-copy ways", + )); + }; + let values = if let Some(values) = deserialized.values.zvl_as_borrowed_inner() { + values + } else { + return Err(de::Error::custom( + "ZeroMapBorrowed can only deserialize in zero-copy ways", + )); + }; + Ok(Self { keys, values }) + } + } +} + +#[cfg(test)] +#[allow(non_camel_case_types)] +mod test { + use crate::{map::ZeroMapBorrowed, ZeroMap}; + + #[derive(serde::Serialize, serde::Deserialize)] + struct DeriveTest_ZeroMap<'data> { + #[serde(borrow)] + _data: ZeroMap<'data, str, [u8]>, + } + + #[derive(serde::Serialize, serde::Deserialize)] + struct DeriveTest_ZeroMapBorrowed<'data> { + #[serde(borrow)] + _data: ZeroMapBorrowed<'data, str, [u8]>, + } + + const JSON_STR: &str = "{\"1\":\"uno\",\"2\":\"dos\",\"3\":\"tres\"}"; + const BINCODE_BYTES: &[u8] = &[ + 12, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 3, 0, 6, 0, 117, 110, 111, 100, 111, 115, 116, 114, 101, 115, + ]; + + fn make_map() -> ZeroMap<'static, u32, str> { + let mut map = ZeroMap::new(); + map.insert(&1, "uno"); + map.insert(&2, "dos"); + map.insert(&3, "tres"); + map + } + + #[test] + fn test_serde_json() { + let map = make_map(); + let json_str = serde_json::to_string(&map).expect("serialize"); + assert_eq!(JSON_STR, json_str); + let new_map: ZeroMap<u32, str> = serde_json::from_str(&json_str).expect("deserialize"); + assert_eq!( + new_map.iter().collect::<Vec<_>>(), + map.iter().collect::<Vec<_>>() + ); + } + + #[test] + fn test_serde_json_complex_key() { + let mut map = ZeroMap::new(); + map.insert(&(1, 1), "uno"); + map.insert(&(2, 2), "dos"); + map.insert(&(3, 3), "tres"); + let json_str = serde_json::to_string(&map).expect("serialize"); + assert_eq!( + json_str, + "[[[1,1],\"uno\"],[[2,2],\"dos\"],[[3,3],\"tres\"]]" + ); + let new_map: ZeroMap<(u32, u32), str> = + serde_json::from_str(&json_str).expect("deserialize"); + assert_eq!( + new_map.iter().collect::<Vec<_>>(), + map.iter().collect::<Vec<_>>() + ); + } + + #[test] + fn test_bincode() { + let map = make_map(); + let bincode_bytes = bincode::serialize(&map).expect("serialize"); + assert_eq!(BINCODE_BYTES, bincode_bytes); + let new_map: ZeroMap<u32, str> = bincode::deserialize(&bincode_bytes).expect("deserialize"); + assert_eq!( + new_map.iter().collect::<Vec<_>>(), + map.iter().collect::<Vec<_>>() + ); + + let new_map: ZeroMapBorrowed<u32, str> = + bincode::deserialize(&bincode_bytes).expect("deserialize"); + assert_eq!( + new_map.iter().collect::<Vec<_>>(), + map.iter().collect::<Vec<_>>() + ); + } +} |