litemap/
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::LiteMap;
6use crate::store::*;
7use alloc::vec::Vec;
8use core::fmt;
9use core::marker::PhantomData;
10use serde::{
11    de::{MapAccess, SeqAccess, Visitor},
12    ser::{SerializeMap, SerializeSeq},
13    Deserialize, Deserializer, Serialize, Serializer,
14};
15
16impl<K, V, R> Serialize for LiteMap<K, V, R>
17where
18    K: Serialize,
19    V: Serialize,
20    R: Store<K, V>,
21{
22    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
23    where
24        S: Serializer,
25    {
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 sequence of tuples instead
29        if serializer.is_human_readable() {
30            let k_is_num_or_string = self
31                .values
32                .lm_get(0)
33                .is_some_and(|(k, _)| super::serde_helpers::is_num_or_string(k));
34            if !k_is_num_or_string {
35                let mut seq = serializer.serialize_seq(Some(self.len()))?;
36                // Note that we can't require StoreIterable for R, see below.
37                for index in 0..self.len() {
38                    #[allow(clippy::unwrap_used)] // looping over 0..len
39                    seq.serialize_element(&self.get_indexed(index).unwrap())?;
40                }
41                return seq.end();
42            }
43            // continue to regular serialization
44        }
45
46        // Note that we can't require StoreIterable for R, because the Higher-Rank Trait Bounds (HRTBs)
47        // `R: for<'a> StoreIterable<'a, K, V>` would end up being too strict for some use cases
48        // as it would require Self, K and V to be 'static. See https://github.com/rust-lang/types-team/blob/master/minutes/2022-07-08-implied-bounds-and-wf-checking.md#problem-fora-shouldnt-really-mean-for-all-a
49        // Instead, we require only R: Store and manually iterate over the items.
50        // For some R types this is equivalent to StoreIterable after compiler optimizations but retains
51        // flexibility for other types.
52        let mut map = serializer.serialize_map(Some(self.len()))?;
53        for index in 0..self.len() {
54            #[allow(clippy::unwrap_used)] // looping over 0..len
55            let (k, v) = self.get_indexed(index).unwrap();
56            map.serialize_entry(k, v)?;
57        }
58        map.end()
59    }
60}
61
62/// Modified example from https://serde.rs/deserialize-map.html
63#[allow(clippy::type_complexity)]
64struct LiteMapVisitor<K, V, R> {
65    marker: PhantomData<fn() -> LiteMap<K, V, R>>,
66}
67
68impl<K, V, R> LiteMapVisitor<K, V, R> {
69    fn new() -> Self {
70        Self {
71            marker: PhantomData,
72        }
73    }
74}
75
76impl<'de, K, V, R> Visitor<'de> for LiteMapVisitor<K, V, R>
77where
78    K: Deserialize<'de> + Ord,
79    V: Deserialize<'de>,
80    R: StoreBulkMut<K, V>,
81{
82    type Value = LiteMap<K, V, R>;
83
84    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
85        formatter.write_str("a map produced by LiteMap")
86    }
87
88    fn visit_seq<S>(self, mut access: S) -> Result<Self::Value, S::Error>
89    where
90        S: SeqAccess<'de>,
91    {
92        // See visit_map for an explanation of the fast-path and out-of-order handling
93        let mut map = LiteMap::with_capacity(access.size_hint().unwrap_or(0));
94        let mut out_of_order = Vec::new();
95
96        while let Some((key, value)) = access.next_element()? {
97            if let Some((key, value)) = map.try_append(key, value) {
98                out_of_order.push((key, value));
99            }
100        }
101
102        if !out_of_order.is_empty() {
103            map.extend(out_of_order);
104        }
105
106        Ok(map)
107    }
108
109    fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
110    where
111        M: MapAccess<'de>,
112    {
113        let mut map = LiteMap::with_capacity(access.size_hint().unwrap_or(0));
114        let mut out_of_order = Vec::new();
115
116        // While there are entries remaining in the input, add them
117        // into our map.
118        while let Some((key, value)) = access.next_entry()? {
119            // Try to append it at the end, hoping for a sorted map. Otherwise, collect
120            // out of order items and extend the map with them later. This way, we give
121            // the implementation an opportunity to avoid quadratic costs from calling
122            // insert() with out of order items.
123            // Handling ordered inputs first allows for arbitrary maps (e.g. from user JSON)
124            // to be deserialized into LiteMap without impacting performance in the case of
125            // deserializing a serialized map that came from another LiteMap.
126            if let Some((key, value)) = map.try_append(key, value) {
127                out_of_order.push((key, value));
128            }
129        }
130
131        if !out_of_order.is_empty() {
132            map.extend(out_of_order);
133        }
134
135        Ok(map)
136    }
137}
138
139impl<'de, K, V, R> Deserialize<'de> for LiteMap<K, V, R>
140where
141    K: Ord + Deserialize<'de>,
142    V: Deserialize<'de>,
143    R: StoreBulkMut<K, V>,
144{
145    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
146    where
147        D: Deserializer<'de>,
148    {
149        if deserializer.is_human_readable() {
150            // deserialize_any only works on self-describing (human-readable)
151            // formats
152            deserializer.deserialize_any(LiteMapVisitor::new())
153        } else {
154            deserializer.deserialize_map(LiteMapVisitor::new())
155        }
156    }
157}
158
159#[cfg(test)]
160mod test {
161    use crate::LiteMap;
162    use alloc::borrow::ToOwned;
163    use alloc::string::String;
164
165    fn get_simple_map() -> LiteMap<u32, String> {
166        [
167            (1, "one".to_owned()),
168            (2, "two".to_owned()),
169            (4, "four".to_owned()),
170            (5, "five".to_owned()),
171        ]
172        .into_iter()
173        .collect()
174    }
175
176    fn get_tuple_map() -> LiteMap<(u32, String), String> {
177        [
178            ((1, "en".to_owned()), "one".to_owned()),
179            ((1, "zh".to_owned()), "一".to_owned()),
180            ((2, "en".to_owned()), "two".to_owned()),
181            ((2, "zh".to_owned()), "二".to_owned()),
182            ((4, "en".to_owned()), "four".to_owned()),
183            ((5, "en".to_owned()), "five".to_owned()),
184            ((5, "zh".to_owned()), "五".to_owned()),
185            ((7, "zh".to_owned()), "七".to_owned()),
186        ]
187        .into_iter()
188        .collect()
189    }
190
191    #[test]
192    fn test_roundtrip_json() {
193        let map = get_simple_map();
194        let json = serde_json::to_string(&map).unwrap();
195        assert_eq!(
196            json,
197            "{\"1\":\"one\",\"2\":\"two\",\"4\":\"four\",\"5\":\"five\"}"
198        );
199        let deserialized: LiteMap<u32, String> = serde_json::from_str(&json).unwrap();
200        assert_eq!(map, deserialized);
201
202        let map = get_tuple_map();
203        let json = serde_json::to_string(&map).unwrap();
204        assert_eq!(
205            json,
206            "[[[1,\"en\"],\"one\"],[[1,\"zh\"],\"一\"],[[2,\"en\"],\"two\"],\
207                          [[2,\"zh\"],\"二\"],[[4,\"en\"],\"four\"],[[5,\"en\"],\"five\"],\
208                          [[5,\"zh\"],\"五\"],[[7,\"zh\"],\"七\"]]"
209        );
210        let deserialized: LiteMap<(u32, String), String> = serde_json::from_str(&json).unwrap();
211        assert_eq!(map, deserialized);
212    }
213
214    #[test]
215    fn test_roundtrip_postcard() {
216        let map = get_simple_map();
217        let postcard = postcard::to_stdvec(&map).unwrap();
218        let deserialized: LiteMap<u32, String> = postcard::from_bytes(&postcard).unwrap();
219        assert_eq!(map, deserialized);
220
221        let map = get_tuple_map();
222        let postcard = postcard::to_stdvec(&map).unwrap();
223        let deserialized: LiteMap<(u32, String), String> = postcard::from_bytes(&postcard).unwrap();
224        assert_eq!(map, deserialized);
225    }
226
227    /// Test that a LiteMap<_, _, Vec> is deserialized with an exact capacity
228    /// if the deserializer provides a size hint information, like postcard here.
229    #[test]
230    fn test_deserialize_capacity() {
231        for len in 0..50 {
232            let mut map = (0..len).map(|i| (i, i.to_string())).collect::<Vec<_>>();
233            let postcard = postcard::to_stdvec(&map).unwrap();
234            let deserialized: LiteMap<u32, String> = postcard::from_bytes(&postcard).unwrap();
235            assert_eq!(deserialized.values.capacity(), len);
236            assert_eq!(deserialized.values.len(), len);
237            // again, but with a shuffled map
238            rand::seq::SliceRandom::shuffle(&mut map[..], &mut rand::rng());
239            let postcard = postcard::to_stdvec(&map).unwrap();
240            let deserialized: LiteMap<u32, String> = postcard::from_bytes(&postcard).unwrap();
241            assert_eq!(deserialized.values.capacity(), len);
242            assert_eq!(deserialized.values.len(), len);
243        }
244    }
245}