turbo_bincode/serde_self_describing/
mod.rs

1//! Helpers for serializing serde-compatible types inside of a bincode [`Encode`] or [`Decode`]
2//! implementation using a self-describing format. This works with [types that
3//! `#[bincode(serde)]` does not support][bincode::serde#known-issues].
4//!
5//! These helper functions can be used in the [`Encode`] and [`Decode`] derive macros with the
6//! `#[bincode(with = "turbo_bincode]` attribute.
7//!
8//! [`Encode`]: bincode::Encode
9//! [`Decode`]: bincode::Decode
10
11use bincode::{
12    de::{BorrowDecoder, Decoder},
13    enc::Encoder,
14    error::{DecodeError, EncodeError},
15};
16use serde::{Serialize, de::DeserializeOwned};
17
18mod de;
19mod ser;
20
21/// Uses a u8 representation, which is slightly more efficient than bincode's default u32 varint
22/// approach for enum discriminants:
23/// https://docs.rs/bincode/latest/bincode/spec/index.html#discriminant-representation
24#[derive(Copy, Clone, PartialEq, Eq)]
25#[repr(u8)]
26enum TypeTag {
27    BoolTrue = 1,
28    BoolFalse,
29    U8,
30    U16,
31    U32,
32    U64,
33    I8,
34    I16,
35    I32,
36    I64,
37    F32,
38    F64,
39    Char,
40    String,
41    Bytes,
42    OptionNone,
43    OptionSome,
44    Unit,
45    UnitStruct,
46    UnitVariant,
47    NewtypeStruct,
48    NewtypeVariant,
49    SeqSized,
50    SeqUnsizedStart,
51    Tuple,
52    TupleStruct,
53    TupleVariant,
54    MapSized,
55    MapUnsizedStart,
56    Struct,
57    StructVariant,
58    CollectionEnd,
59}
60
61impl TryFrom<u8> for TypeTag {
62    type Error = DecodeError;
63
64    fn try_from(num: u8) -> Result<Self, DecodeError> {
65        let tag = match num {
66            1 => TypeTag::BoolTrue,
67            2 => TypeTag::BoolFalse,
68            3 => TypeTag::U8,
69            4 => TypeTag::U16,
70            5 => TypeTag::U32,
71            6 => TypeTag::U64,
72            7 => TypeTag::I8,
73            8 => TypeTag::I16,
74            9 => TypeTag::I32,
75            10 => TypeTag::I64,
76            11 => TypeTag::F32,
77            12 => TypeTag::F64,
78            13 => TypeTag::Char,
79            14 => TypeTag::String,
80            15 => TypeTag::Bytes,
81            16 => TypeTag::OptionNone,
82            17 => TypeTag::OptionSome,
83            18 => TypeTag::Unit,
84            19 => TypeTag::UnitStruct,
85            20 => TypeTag::UnitVariant,
86            21 => TypeTag::NewtypeStruct,
87            22 => TypeTag::NewtypeVariant,
88            23 => TypeTag::SeqSized,
89            24 => TypeTag::SeqUnsizedStart,
90            25 => TypeTag::Tuple,
91            26 => TypeTag::TupleStruct,
92            27 => TypeTag::TupleVariant,
93            28 => TypeTag::MapSized,
94            29 => TypeTag::MapUnsizedStart,
95            30 => TypeTag::Struct,
96            31 => TypeTag::StructVariant,
97            32 => TypeTag::CollectionEnd,
98            _ => {
99                return Err(DecodeError::OtherString(format!("invalid type tag: {num}")));
100            }
101        };
102        debug_assert_eq!(tag as u8, num);
103        Ok(tag)
104    }
105}
106
107pub fn encode<E: Encoder, T: Serialize>(value: &T, encoder: &mut E) -> Result<(), EncodeError> {
108    value
109        .serialize(&mut ser::BincodeSerializer::new(encoder))
110        .map_err(|e| e.0)
111}
112
113pub fn decode<Context, D: Decoder<Context = Context>, T: DeserializeOwned>(
114    decoder: &mut D,
115) -> Result<T, DecodeError> {
116    T::deserialize(de::BincodeDeserializer::new(decoder)).map_err(|e| e.0)
117}
118
119pub fn borrow_decode<
120    'de,
121    Context,
122    D: BorrowDecoder<'de, Context = Context>,
123    T: serde::de::Deserialize<'de>,
124>(
125    decoder: &mut D,
126) -> Result<T, DecodeError> {
127    T::deserialize(de::BincodeDeserializer::new(decoder)).map_err(|e| e.0)
128}
129
130#[cfg(test)]
131mod tests {
132    use std::{collections::HashMap, fmt::Debug};
133
134    use bincode::{Decode, Encode, decode_from_slice, encode_to_vec};
135    use serde::{Deserialize, Serialize, de::DeserializeOwned};
136
137    fn round_trip<T: Serialize + DeserializeOwned + PartialEq + Debug>(value: T) -> T {
138        #[derive(Encode)]
139        #[bincode(encode_bounds = "T: Serialize")]
140        struct EncodeWrapper<'a, T>(#[bincode(with = "crate::serde_self_describing")] &'a T);
141
142        #[derive(Decode)]
143        #[bincode(
144            decode_bounds = "T: DeserializeOwned",
145            borrow_decode_bounds = "T: Deserialize<'__de>"
146        )]
147        struct DecodeWrapper<T>(#[bincode(with = "crate::serde_self_describing")] T);
148
149        let config = bincode::config::standard();
150
151        let encoded = encode_to_vec(EncodeWrapper(&value), config).unwrap();
152        let (DecodeWrapper(decoded), len) = decode_from_slice(&encoded, config).unwrap();
153        assert_eq!(value, decoded);
154        assert_eq!(len, encoded.len(), "the entire buffer must be decoded");
155        decoded
156    }
157
158    #[test]
159    fn test_primitives() {
160        round_trip(true);
161        round_trip(false);
162        round_trip(42u8);
163        round_trip(42u16);
164        round_trip(42u32);
165        round_trip(42u64);
166        round_trip(-42i8);
167        round_trip(-42i16);
168        round_trip(-42i32);
169        round_trip(-42i64);
170        round_trip(1.23f32);
171        round_trip(1.23f64);
172        round_trip('a');
173    }
174
175    #[test]
176    fn test_string() {
177        round_trip(String::new());
178        round_trip(String::from("hello world"));
179    }
180
181    #[test]
182    fn test_option() {
183        round_trip(Option::<i32>::None);
184        round_trip(Some(42));
185        round_trip(Some(String::from("hello")));
186        round_trip(Some(Some(42)));
187    }
188
189    #[test]
190    fn test_vec() {
191        round_trip(Vec::<i32>::new());
192        round_trip(vec![String::from("a"), String::from("b")]);
193        round_trip(vec![vec![1, 2], vec![3, 4]]);
194        round_trip(b"abc\0def".to_vec());
195    }
196
197    #[test]
198    fn test_tuple() {
199        round_trip(());
200        round_trip((vec![1, 2], "hello".to_string(), Some(42), false, true));
201    }
202
203    #[test]
204    fn test_hashmap() {
205        let mut map = HashMap::new();
206        map.insert("key1".to_string(), 1);
207        map.insert("key2".to_string(), 2);
208        round_trip(map);
209
210        let empty: HashMap<String, i32> = HashMap::new();
211        round_trip(empty);
212    }
213
214    #[test]
215    fn test_struct() {
216        #[derive(Debug, PartialEq, Serialize, Deserialize)]
217        struct Struct {
218            a: String,
219            b: Option<i32>,
220            #[serde(flatten)]
221            flattened: Flattened,
222        }
223
224        #[derive(Debug, PartialEq, Serialize, Deserialize)]
225        struct Flattened {
226            d: i32,
227        }
228
229        round_trip(Struct {
230            a: "hello".to_string(),
231            b: None,
232            flattened: Flattened { d: 42 },
233        });
234        round_trip(Struct {
235            a: "hello".to_string(),
236            b: Some(42),
237            flattened: Flattened { d: 43 },
238        });
239    }
240
241    #[test]
242    fn test_unit_struct() {
243        #[derive(Debug, PartialEq, Serialize, Deserialize)]
244        struct UnitStruct;
245
246        round_trip(UnitStruct);
247    }
248
249    #[test]
250    fn test_newtype_struct() {
251        #[derive(Debug, PartialEq, Serialize, Deserialize)]
252        struct NewtypeStruct(i32);
253
254        round_trip(NewtypeStruct(42));
255    }
256
257    #[test]
258    fn test_tuple_struct() {
259        #[derive(Debug, PartialEq, Serialize, Deserialize)]
260        struct TupleStruct(i32, String, bool);
261
262        round_trip(TupleStruct(42, "hello".to_string(), true));
263    }
264
265    #[test]
266    fn test_enum_unit_variants() {
267        #[derive(Debug, PartialEq, Serialize, Deserialize)]
268        enum Color {
269            Red,
270            Green,
271            Blue,
272        }
273
274        round_trip(Color::Red);
275        round_trip(Color::Green);
276        round_trip(Color::Blue);
277    }
278
279    #[test]
280    fn test_enum_newtype_variants() {
281        #[derive(Debug, PartialEq, Serialize, Deserialize)]
282        enum Value {
283            #[allow(unused)]
284            #[serde(skip)]
285            Empty(()),
286            Int(i32),
287            Text(String),
288        }
289
290        round_trip(Value::Int(42));
291        round_trip(Value::Text("hello".to_string()));
292    }
293
294    #[test]
295    fn test_enum_tuple_variants() {
296        #[derive(Debug, PartialEq, Serialize, Deserialize)]
297        enum Point {
298            TwoD(i32, i32),
299            ThreeD(i32, i32, i32),
300        }
301
302        round_trip(Point::TwoD(1, 2));
303        round_trip(Point::ThreeD(1, 2, 3));
304    }
305
306    #[test]
307    fn test_enum_struct_variants() {
308        #[derive(Debug, PartialEq, Serialize, Deserialize)]
309        enum Message {
310            Request { id: u32, method: String },
311            Response { id: u32, result: Option<String> },
312        }
313
314        round_trip(Message::Request {
315            id: 1,
316            method: "get".to_string(),
317        });
318        round_trip(Message::Response {
319            id: 1,
320            result: Some("ok".to_string()),
321        });
322        round_trip(Message::Response {
323            id: 2,
324            result: None,
325        });
326    }
327}