zerovec/map2d/
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::{ZeroMap2d, ZeroMap2dBorrowed, ZeroMap2dCursor};
6use crate::map::{MutableZeroVecLike, ZeroMapKV, ZeroVecLike};
7use crate::ZeroVec;
8use alloc::vec::Vec;
9use core::fmt;
10use core::marker::PhantomData;
11use serde::de::{self, Deserialize, Deserializer, MapAccess, Visitor};
12#[cfg(feature = "serde")]
13use serde::ser::{Serialize, SerializeMap, Serializer};
14
15/// This impl requires enabling the optional `serde` Cargo feature of the `zerovec` crate
16#[cfg(feature = "serde")]
17impl<'a, K0, K1, V> Serialize for ZeroMap2d<'a, K0, K1, V>
18where
19    K0: ZeroMapKV<'a> + Serialize + ?Sized + Ord,
20    K1: ZeroMapKV<'a> + Serialize + ?Sized + Ord,
21    V: ZeroMapKV<'a> + Serialize + ?Sized,
22    K0::Container: Serialize,
23    K1::Container: Serialize,
24    V::Container: Serialize,
25{
26    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
27    where
28        S: Serializer,
29    {
30        if serializer.is_human_readable() {
31            let mut serde_map = serializer.serialize_map(None)?;
32            for cursor in self.iter0() {
33                K0::Container::zvl_get_as_t(cursor.key0(), |k| serde_map.serialize_key(k))?;
34                let inner_map = ZeroMap2dInnerMapSerialize { cursor };
35                serde_map.serialize_value(&inner_map)?;
36            }
37            serde_map.end()
38        } else {
39            (&self.keys0, &self.joiner, &self.keys1, &self.values).serialize(serializer)
40        }
41    }
42}
43
44/// Helper struct for human-serializing the inner map of a ZeroMap2d
45#[cfg(feature = "serde")]
46struct ZeroMap2dInnerMapSerialize<'a, 'l, K0, K1, V>
47where
48    K0: ZeroMapKV<'a> + ?Sized + Ord,
49    K1: ZeroMapKV<'a> + ?Sized + Ord,
50    V: ZeroMapKV<'a> + ?Sized,
51{
52    pub cursor: ZeroMap2dCursor<'l, 'a, K0, K1, V>,
53}
54
55#[cfg(feature = "serde")]
56impl<'a, 'l, K0, K1, V> Serialize for ZeroMap2dInnerMapSerialize<'a, 'l, K0, K1, V>
57where
58    K0: ZeroMapKV<'a> + Serialize + ?Sized + Ord,
59    K1: ZeroMapKV<'a> + Serialize + ?Sized + Ord,
60    V: ZeroMapKV<'a> + Serialize + ?Sized,
61    K0::Container: Serialize,
62    K1::Container: Serialize,
63    V::Container: Serialize,
64{
65    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
66    where
67        S: Serializer,
68    {
69        let mut serde_map = serializer.serialize_map(None)?;
70        for (key1, v) in self.cursor.iter1() {
71            K1::Container::zvl_get_as_t(key1, |k| serde_map.serialize_key(k))?;
72            V::Container::zvl_get_as_t(v, |v| serde_map.serialize_value(v))?;
73        }
74        serde_map.end()
75    }
76}
77
78/// This impl requires enabling the optional `serde` Cargo feature of the `zerovec` crate
79#[cfg(feature = "serde")]
80impl<'a, K0, K1, V> Serialize for ZeroMap2dBorrowed<'a, K0, K1, V>
81where
82    K0: ZeroMapKV<'a> + Serialize + ?Sized + Ord,
83    K1: ZeroMapKV<'a> + Serialize + ?Sized + Ord,
84    V: ZeroMapKV<'a> + Serialize + ?Sized,
85    K0::Container: Serialize,
86    K1::Container: Serialize,
87    V::Container: Serialize,
88{
89    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
90    where
91        S: Serializer,
92    {
93        ZeroMap2d::<K0, K1, V>::from(*self).serialize(serializer)
94    }
95}
96
97/// Modified example from https://serde.rs/deserialize-map.html
98struct ZeroMap2dMapVisitor<'a, K0, K1, V>
99where
100    K0: ZeroMapKV<'a> + ?Sized + Ord,
101    K1: ZeroMapKV<'a> + ?Sized + Ord,
102    V: ZeroMapKV<'a> + ?Sized,
103{
104    #[allow(clippy::type_complexity)] // it's a marker type, complexity doesn't matter
105    marker: PhantomData<fn() -> (&'a K0::OwnedType, &'a K1::OwnedType, &'a V::OwnedType)>,
106}
107
108impl<'a, K0, K1, V> ZeroMap2dMapVisitor<'a, K0, K1, V>
109where
110    K0: ZeroMapKV<'a> + ?Sized + Ord,
111    K1: ZeroMapKV<'a> + ?Sized + Ord,
112    V: ZeroMapKV<'a> + ?Sized,
113{
114    fn new() -> Self {
115        ZeroMap2dMapVisitor {
116            marker: PhantomData,
117        }
118    }
119}
120
121impl<'a, 'de, K0, K1, V> Visitor<'de> for ZeroMap2dMapVisitor<'a, K0, K1, V>
122where
123    K0: ZeroMapKV<'a> + Ord + ?Sized + Ord,
124    K1: ZeroMapKV<'a> + Ord + ?Sized + Ord,
125    V: ZeroMapKV<'a> + ?Sized,
126    K1::Container: Deserialize<'de>,
127    V::Container: Deserialize<'de>,
128    K0::OwnedType: Deserialize<'de>,
129    K1::OwnedType: Deserialize<'de>,
130    V::OwnedType: Deserialize<'de>,
131{
132    type Value = ZeroMap2d<'a, K0, K1, V>;
133
134    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
135        formatter.write_str("a map produced by ZeroMap2d")
136    }
137
138    fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
139    where
140        M: MapAccess<'de>,
141    {
142        let mut map = ZeroMap2d::with_capacity(access.size_hint().unwrap_or(0));
143
144        // On the first level, pull out the K0s and a TupleVecMap of the
145        // K1s and Vs, and then collect them into a ZeroMap2d
146        while let Some((key0, inner_map)) =
147            access.next_entry::<K0::OwnedType, TupleVecMap<K1::OwnedType, V::OwnedType>>()?
148        {
149            for (key1, value) in inner_map.entries.iter() {
150                if map
151                    .try_append(
152                        K0::Container::owned_as_t(&key0),
153                        K1::Container::owned_as_t(key1),
154                        V::Container::owned_as_t(value),
155                    )
156                    .is_some()
157                {
158                    return Err(de::Error::custom(
159                        "ZeroMap2d's keys must be sorted while deserializing",
160                    ));
161                }
162            }
163        }
164
165        Ok(map)
166    }
167}
168
169/// Helper struct for human-deserializing the inner map of a ZeroMap2d
170struct TupleVecMap<K1, V> {
171    pub entries: Vec<(K1, V)>,
172}
173
174struct TupleVecMapVisitor<K1, V> {
175    #[allow(clippy::type_complexity)] // it's a marker type, complexity doesn't matter
176    marker: PhantomData<fn() -> (K1, V)>,
177}
178
179impl<K1, V> TupleVecMapVisitor<K1, V> {
180    fn new() -> Self {
181        TupleVecMapVisitor {
182            marker: PhantomData,
183        }
184    }
185}
186
187impl<'de, K1, V> Visitor<'de> for TupleVecMapVisitor<K1, V>
188where
189    K1: Deserialize<'de>,
190    V: Deserialize<'de>,
191{
192    type Value = TupleVecMap<K1, V>;
193
194    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
195        formatter.write_str("an inner map produced by ZeroMap2d")
196    }
197
198    fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
199    where
200        M: MapAccess<'de>,
201    {
202        let mut result = Vec::with_capacity(access.size_hint().unwrap_or(0));
203        while let Some((key1, value)) = access.next_entry::<K1, V>()? {
204            result.push((key1, value));
205        }
206        Ok(TupleVecMap { entries: result })
207    }
208}
209
210impl<'de, K1, V> Deserialize<'de> for TupleVecMap<K1, V>
211where
212    K1: Deserialize<'de>,
213    V: Deserialize<'de>,
214{
215    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
216    where
217        D: Deserializer<'de>,
218    {
219        deserializer.deserialize_map(TupleVecMapVisitor::<K1, V>::new())
220    }
221}
222
223/// This impl requires enabling the optional `serde` Cargo feature of the `zerovec` crate
224impl<'de, 'a, K0, K1, V> Deserialize<'de> for ZeroMap2d<'a, K0, K1, V>
225where
226    K0: ZeroMapKV<'a> + Ord + ?Sized,
227    K1: ZeroMapKV<'a> + Ord + ?Sized,
228    V: ZeroMapKV<'a> + ?Sized,
229    K0::Container: Deserialize<'de>,
230    K1::Container: Deserialize<'de>,
231    V::Container: Deserialize<'de>,
232    K0::OwnedType: Deserialize<'de>,
233    K1::OwnedType: Deserialize<'de>,
234    V::OwnedType: Deserialize<'de>,
235    'de: 'a,
236{
237    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
238    where
239        D: Deserializer<'de>,
240    {
241        if deserializer.is_human_readable() {
242            deserializer.deserialize_map(ZeroMap2dMapVisitor::<'a, K0, K1, V>::new())
243        } else {
244            let (keys0, joiner, keys1, values): (
245                K0::Container,
246                ZeroVec<u32>,
247                K1::Container,
248                V::Container,
249            ) = Deserialize::deserialize(deserializer)?;
250            // Invariant 1: len(keys0) == len(joiner)
251            if keys0.zvl_len() != joiner.len() {
252                return Err(de::Error::custom(
253                    "Mismatched keys0 and joiner sizes in ZeroMap2d",
254                ));
255            }
256            // Invariant 2: len(keys1) == len(values)
257            if keys1.zvl_len() != values.zvl_len() {
258                return Err(de::Error::custom(
259                    "Mismatched keys1 and value sizes in ZeroMap2d",
260                ));
261            }
262            // Invariant 3: joiner is sorted
263            if !joiner.zvl_is_ascending() {
264                return Err(de::Error::custom(
265                    "ZeroMap2d deserializing joiner array out of order",
266                ));
267            }
268            // Invariant 4: the last element of joiner is the length of keys1
269            if let Some(last_joiner0) = joiner.last() {
270                if keys1.zvl_len() != last_joiner0 as usize {
271                    return Err(de::Error::custom(
272                        "ZeroMap2d deserializing joiner array malformed",
273                    ));
274                }
275            }
276            let result = Self {
277                keys0,
278                joiner,
279                keys1,
280                values,
281            };
282            // In debug mode, check the optional invariants, too
283            #[cfg(debug_assertions)]
284            result.check_invariants();
285            Ok(result)
286        }
287    }
288}
289
290/// This impl requires enabling the optional `serde` Cargo feature of the `zerovec` crate
291impl<'de, 'a, K0, K1, V> Deserialize<'de> for ZeroMap2dBorrowed<'a, K0, K1, V>
292where
293    K0: ZeroMapKV<'a> + Ord + ?Sized,
294    K1: ZeroMapKV<'a> + Ord + ?Sized,
295    V: ZeroMapKV<'a> + ?Sized,
296    K0::Container: Deserialize<'de>,
297    K1::Container: Deserialize<'de>,
298    V::Container: Deserialize<'de>,
299    K0::OwnedType: Deserialize<'de>,
300    K1::OwnedType: Deserialize<'de>,
301    V::OwnedType: Deserialize<'de>,
302    'de: 'a,
303{
304    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
305    where
306        D: Deserializer<'de>,
307    {
308        if deserializer.is_human_readable() {
309            Err(de::Error::custom(
310                "ZeroMap2dBorrowed cannot be deserialized from human-readable formats",
311            ))
312        } else {
313            let deserialized: ZeroMap2d<'a, K0, K1, V> = ZeroMap2d::deserialize(deserializer)?;
314            let keys0 = if let Some(keys0) = deserialized.keys0.zvl_as_borrowed_inner() {
315                keys0
316            } else {
317                return Err(de::Error::custom(
318                    "ZeroMap2dBorrowed can only deserialize in zero-copy ways",
319                ));
320            };
321            let joiner = if let Some(joiner) = deserialized.joiner.zvl_as_borrowed_inner() {
322                joiner
323            } else {
324                return Err(de::Error::custom(
325                    "ZeroMap2dBorrowed can only deserialize in zero-copy ways",
326                ));
327            };
328            let keys1 = if let Some(keys1) = deserialized.keys1.zvl_as_borrowed_inner() {
329                keys1
330            } else {
331                return Err(de::Error::custom(
332                    "ZeroMap2dBorrowed can only deserialize in zero-copy ways",
333                ));
334            };
335            let values = if let Some(values) = deserialized.values.zvl_as_borrowed_inner() {
336                values
337            } else {
338                return Err(de::Error::custom(
339                    "ZeroMap2dBorrowed can only deserialize in zero-copy ways",
340                ));
341            };
342            Ok(Self {
343                keys0,
344                joiner,
345                keys1,
346                values,
347            })
348        }
349    }
350}
351
352#[cfg(test)]
353#[allow(non_camel_case_types)]
354mod test {
355    use crate::map2d::{ZeroMap2d, ZeroMap2dBorrowed};
356
357    #[derive(serde::Serialize, serde::Deserialize)]
358    struct DeriveTest_ZeroMap2d<'data> {
359        #[serde(borrow)]
360        _data: ZeroMap2d<'data, u16, str, [u8]>,
361    }
362
363    #[derive(serde::Serialize, serde::Deserialize)]
364    struct DeriveTest_ZeroMap2dBorrowed<'data> {
365        #[serde(borrow)]
366        _data: ZeroMap2dBorrowed<'data, u16, str, [u8]>,
367    }
368
369    const JSON_STR: &str = "{\"1\":{\"1\":\"uno\"},\"2\":{\"2\":\"dos\",\"3\":\"tres\"}}";
370    const BINCODE_BYTES: &[u8] = &[
371        8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0,
372        0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 3, 0, 16, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 6, 0,
373        117, 110, 111, 100, 111, 115, 116, 114, 101, 115,
374    ];
375
376    fn make_map() -> ZeroMap2d<'static, u32, u16, str> {
377        let mut map = ZeroMap2d::new();
378        map.insert(&1, &1, "uno");
379        map.insert(&2, &2, "dos");
380        map.insert(&2, &3, "tres");
381        map
382    }
383
384    #[test]
385    fn test_serde_json() {
386        let map = make_map();
387        let json_str = serde_json::to_string(&map).expect("serialize");
388        assert_eq!(JSON_STR, json_str);
389        let new_map: ZeroMap2d<u32, u16, str> =
390            serde_json::from_str(&json_str).expect("deserialize");
391        assert_eq!(format!("{new_map:?}"), format!("{map:?}"));
392    }
393
394    #[test]
395    fn test_bincode() {
396        let map = make_map();
397        let bincode_bytes = bincode::serialize(&map).expect("serialize");
398        assert_eq!(BINCODE_BYTES, bincode_bytes);
399        let new_map: ZeroMap2d<u32, u16, str> =
400            bincode::deserialize(&bincode_bytes).expect("deserialize");
401        assert_eq!(
402            format!("{new_map:?}"),
403            format!("{map:?}").replace("Owned", "Borrowed"),
404        );
405
406        let new_map: ZeroMap2dBorrowed<u32, u16, str> =
407            bincode::deserialize(&bincode_bytes).expect("deserialize");
408        assert_eq!(
409            format!("{new_map:?}"),
410            format!("{map:?}")
411                .replace("Owned", "Borrowed")
412                .replace("ZeroMap2d", "ZeroMap2dBorrowed")
413        );
414    }
415
416    #[test]
417    fn test_serde_rmp() {
418        let map = make_map();
419        let rmp_buf = rmp_serde::to_vec(&map).expect("serialize");
420        let new_map: ZeroMap2d<u32, u16, str> = rmp_serde::from_slice(&rmp_buf).unwrap();
421        assert_eq!(map, new_map);
422    }
423
424    #[test]
425    fn test_sample_bincode() {
426        // This is the map from the main docs page for ZeroMap2d
427        let mut map: ZeroMap2d<u16, u16, str> = ZeroMap2d::new();
428        map.insert(&1, &2, "three");
429        let bincode_bytes: Vec<u8> = bincode::serialize(&map).expect("serialize");
430        assert_eq!(
431            bincode_bytes.as_slice(),
432            &[
433                2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0,
434                0, 0, 2, 0, 7, 0, 0, 0, 0, 0, 0, 0, 1, 0, 116, 104, 114, 101, 101
435            ]
436        );
437    }
438}