zerovec/map/
serde.rs

1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5use super::{MutableZeroVecLike, ZeroMap, ZeroMapBorrowed, ZeroMapKV, ZeroVecLike};
6use core::fmt;
7use core::marker::PhantomData;
8use serde::de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor};
9#[cfg(feature = "serde")]
10use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer};
11
12/// This impl requires enabling the optional `serde` Cargo feature of the `zerovec` crate
13#[cfg(feature = "serde")]
14impl<'a, K, V> Serialize for ZeroMap<'a, K, V>
15where
16    K: ZeroMapKV<'a> + Serialize + ?Sized + Ord,
17    V: ZeroMapKV<'a> + Serialize + ?Sized,
18    K::Container: Serialize,
19    V::Container: Serialize,
20{
21    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
22    where
23        S: Serializer,
24    {
25        if serializer.is_human_readable() {
26            // Many human-readable formats don't support values other
27            // than numbers and strings as map keys. For them, we can serialize
28            // as a vec of tuples instead
29            if let Some(k) = self.iter_keys().next() {
30                if !K::Container::zvl_get_as_t(k, super::serde_helpers::is_num_or_string) {
31                    let mut seq = serializer.serialize_seq(Some(self.len()))?;
32                    for (k, v) in self.iter() {
33                        K::Container::zvl_get_as_t(k, |k| {
34                            V::Container::zvl_get_as_t(v, |v| seq.serialize_element(&(k, v)))
35                        })?;
36                    }
37                    return seq.end();
38                }
39            }
40            let mut map = serializer.serialize_map(Some(self.len()))?;
41            for (k, v) in self.iter() {
42                K::Container::zvl_get_as_t(k, |k| map.serialize_key(k))?;
43                V::Container::zvl_get_as_t(v, |v| map.serialize_value(v))?;
44            }
45            map.end()
46        } else {
47            (&self.keys, &self.values).serialize(serializer)
48        }
49    }
50}
51
52/// This impl requires enabling the optional `serde` Cargo feature of the `zerovec` crate
53#[cfg(feature = "serde")]
54impl<'a, K, V> Serialize for ZeroMapBorrowed<'a, K, V>
55where
56    K: ZeroMapKV<'a> + Serialize + ?Sized + Ord,
57    V: ZeroMapKV<'a> + Serialize + ?Sized,
58    K::Container: Serialize,
59    V::Container: Serialize,
60{
61    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
62    where
63        S: Serializer,
64    {
65        ZeroMap::<K, V>::from(*self).serialize(serializer)
66    }
67}
68
69/// Modified example from https://serde.rs/deserialize-map.html
70struct ZeroMapMapVisitor<'a, K, V>
71where
72    K: ZeroMapKV<'a> + ?Sized + Ord,
73    V: ZeroMapKV<'a> + ?Sized,
74{
75    #[allow(clippy::type_complexity)] // it's a marker type, complexity doesn't matter
76    marker: PhantomData<fn() -> (&'a K::OwnedType, &'a V::OwnedType)>,
77}
78
79impl<'a, K, V> ZeroMapMapVisitor<'a, K, V>
80where
81    K: ZeroMapKV<'a> + ?Sized + Ord,
82    V: ZeroMapKV<'a> + ?Sized,
83{
84    fn new() -> Self {
85        ZeroMapMapVisitor {
86            marker: PhantomData,
87        }
88    }
89}
90
91impl<'a, 'de, K, V> Visitor<'de> for ZeroMapMapVisitor<'a, K, V>
92where
93    K: ZeroMapKV<'a> + Ord + ?Sized,
94    V: ZeroMapKV<'a> + ?Sized,
95    K::OwnedType: Deserialize<'de>,
96    V::OwnedType: Deserialize<'de>,
97{
98    type Value = ZeroMap<'a, K, V>;
99
100    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
101        formatter.write_str("a map produced by ZeroMap")
102    }
103
104    fn visit_seq<S>(self, mut access: S) -> Result<Self::Value, S::Error>
105    where
106        S: SeqAccess<'de>,
107    {
108        let mut map = ZeroMap::with_capacity(access.size_hint().unwrap_or(0));
109
110        // While there are entries remaining in the input, add them
111        // into our map.
112        while let Some((key, value)) = access.next_element::<(K::OwnedType, V::OwnedType)>()? {
113            // Try to append it at the end, hoping for a sorted map.
114            // If not sorted, return an error
115            // a serialized map that came from another ZeroMap
116            if map
117                .try_append(
118                    K::Container::owned_as_t(&key),
119                    V::Container::owned_as_t(&value),
120                )
121                .is_some()
122            {
123                return Err(de::Error::custom(
124                    "ZeroMap's keys must be sorted while deserializing",
125                ));
126            }
127        }
128
129        Ok(map)
130    }
131
132    fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
133    where
134        M: MapAccess<'de>,
135    {
136        let mut map = ZeroMap::with_capacity(access.size_hint().unwrap_or(0));
137
138        // While there are entries remaining in the input, add them
139        // into our map.
140        while let Some((key, value)) = access.next_entry::<K::OwnedType, V::OwnedType>()? {
141            // Try to append it at the end, hoping for a sorted map.
142            // If not sorted, return an error
143            // a serialized map that came from another ZeroMap
144            if map
145                .try_append(
146                    K::Container::owned_as_t(&key),
147                    V::Container::owned_as_t(&value),
148                )
149                .is_some()
150            {
151                return Err(de::Error::custom(
152                    "ZeroMap's keys must be sorted while deserializing",
153                ));
154            }
155        }
156
157        Ok(map)
158    }
159}
160
161/// This impl requires enabling the optional `serde` Cargo feature of the `zerovec` crate
162impl<'de, 'a, K, V> Deserialize<'de> for ZeroMap<'a, K, V>
163where
164    K: ZeroMapKV<'a> + Ord + ?Sized,
165    V: ZeroMapKV<'a> + ?Sized,
166    K::Container: Deserialize<'de>,
167    V::Container: Deserialize<'de>,
168    K::OwnedType: Deserialize<'de>,
169    V::OwnedType: Deserialize<'de>,
170    'de: 'a,
171{
172    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
173    where
174        D: Deserializer<'de>,
175    {
176        if deserializer.is_human_readable() {
177            deserializer.deserialize_any(ZeroMapMapVisitor::<'a, K, V>::new())
178        } else {
179            let (keys, values): (K::Container, V::Container) =
180                Deserialize::deserialize(deserializer)?;
181            if keys.zvl_len() != values.zvl_len() {
182                return Err(de::Error::custom(
183                    "Mismatched key and value sizes in ZeroMap",
184                ));
185            }
186            // #1433: If keys are out of order, treat it as GIGO.
187            debug_assert!(keys.zvl_is_ascending());
188            Ok(Self { keys, values })
189        }
190    }
191}
192
193// /// This impl requires enabling the optional `serde` Cargo feature of the `zerovec` crate
194impl<'de, 'a, K, V> Deserialize<'de> for ZeroMapBorrowed<'a, K, V>
195where
196    K: ZeroMapKV<'a> + Ord + ?Sized,
197    V: ZeroMapKV<'a> + ?Sized,
198    K::Container: Deserialize<'de>,
199    V::Container: Deserialize<'de>,
200    K::OwnedType: Deserialize<'de>,
201    V::OwnedType: Deserialize<'de>,
202    'de: 'a,
203{
204    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
205    where
206        D: Deserializer<'de>,
207    {
208        if deserializer.is_human_readable() {
209            Err(de::Error::custom(
210                "ZeroMapBorrowed cannot be deserialized from human-readable formats",
211            ))
212        } else {
213            let deserialized: ZeroMap<'a, K, V> = ZeroMap::deserialize(deserializer)?;
214            let keys = if let Some(keys) = deserialized.keys.zvl_as_borrowed_inner() {
215                keys
216            } else {
217                return Err(de::Error::custom(
218                    "ZeroMapBorrowed can only deserialize in zero-copy ways",
219                ));
220            };
221            let values = if let Some(values) = deserialized.values.zvl_as_borrowed_inner() {
222                values
223            } else {
224                return Err(de::Error::custom(
225                    "ZeroMapBorrowed can only deserialize in zero-copy ways",
226                ));
227            };
228            Ok(Self { keys, values })
229        }
230    }
231}
232
233#[cfg(test)]
234#[allow(non_camel_case_types)]
235mod test {
236    use crate::{map::ZeroMapBorrowed, ZeroMap};
237
238    #[derive(serde::Serialize, serde::Deserialize)]
239    struct DeriveTest_ZeroMap<'data> {
240        #[serde(borrow)]
241        _data: ZeroMap<'data, str, [u8]>,
242    }
243
244    #[derive(serde::Serialize, serde::Deserialize)]
245    struct DeriveTest_ZeroMapBorrowed<'data> {
246        #[serde(borrow)]
247        _data: ZeroMapBorrowed<'data, str, [u8]>,
248    }
249
250    const JSON_STR: &str = "{\"1\":\"uno\",\"2\":\"dos\",\"3\":\"tres\"}";
251    const BINCODE_BYTES: &[u8] = &[
252        12, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 3, 0,
253        3, 0, 6, 0, 117, 110, 111, 100, 111, 115, 116, 114, 101, 115,
254    ];
255
256    fn make_map() -> ZeroMap<'static, u32, str> {
257        let mut map = ZeroMap::new();
258        map.insert(&1, "uno");
259        map.insert(&2, "dos");
260        map.insert(&3, "tres");
261        map
262    }
263
264    #[test]
265    fn test_serde_json() {
266        let map = make_map();
267        let json_str = serde_json::to_string(&map).expect("serialize");
268        assert_eq!(JSON_STR, json_str);
269        let new_map: ZeroMap<u32, str> = serde_json::from_str(&json_str).expect("deserialize");
270        assert_eq!(
271            new_map.iter().collect::<Vec<_>>(),
272            map.iter().collect::<Vec<_>>()
273        );
274    }
275
276    #[test]
277    fn test_serde_json_complex_key() {
278        let mut map = ZeroMap::new();
279        map.insert(&(1, 1), "uno");
280        map.insert(&(2, 2), "dos");
281        map.insert(&(3, 3), "tres");
282        let json_str = serde_json::to_string(&map).expect("serialize");
283        assert_eq!(
284            json_str,
285            "[[[1,1],\"uno\"],[[2,2],\"dos\"],[[3,3],\"tres\"]]"
286        );
287        let new_map: ZeroMap<(u32, u32), str> =
288            serde_json::from_str(&json_str).expect("deserialize");
289        assert_eq!(
290            new_map.iter().collect::<Vec<_>>(),
291            map.iter().collect::<Vec<_>>()
292        );
293    }
294
295    #[test]
296    fn test_bincode() {
297        let map = make_map();
298        let bincode_bytes = bincode::serialize(&map).expect("serialize");
299        assert_eq!(BINCODE_BYTES, bincode_bytes);
300        let new_map: ZeroMap<u32, str> = bincode::deserialize(&bincode_bytes).expect("deserialize");
301        assert_eq!(
302            new_map.iter().collect::<Vec<_>>(),
303            map.iter().collect::<Vec<_>>()
304        );
305
306        let new_map: ZeroMapBorrowed<u32, str> =
307            bincode::deserialize(&bincode_bytes).expect("deserialize");
308        assert_eq!(
309            new_map.iter().collect::<Vec<_>>(),
310            map.iter().collect::<Vec<_>>()
311        );
312    }
313}