// 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(&self, serializer: S) -> Result 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(&self, serializer: S) -> Result where S: Serializer, { ZeroMap::::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 (&'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(self, mut access: S) -> Result 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(self, mut access: M) -> Result 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::()? { // 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(deserializer: D) -> Result 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(deserializer: D) -> Result 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 = serde_json::from_str(&json_str).expect("deserialize"); assert_eq!( new_map.iter().collect::>(), map.iter().collect::>() ); } #[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::>(), map.iter().collect::>() ); } #[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 = bincode::deserialize(&bincode_bytes).expect("deserialize"); assert_eq!( new_map.iter().collect::>(), map.iter().collect::>() ); let new_map: ZeroMapBorrowed = bincode::deserialize(&bincode_bytes).expect("deserialize"); assert_eq!( new_map.iter().collect::>(), map.iter().collect::>() ); } }