numbat_codec/
top_de.rs

1use alloc::boxed::Box;
2use alloc::string::String;
3use alloc::vec::Vec;
4use core::num::NonZeroUsize;
5
6use crate::codec_err::DecodeError;
7use crate::nested_de::*;
8use crate::top_de_input::TopDecodeInput;
9use crate::transmute::*;
10use crate::TypeInfo;
11
12/// Trait that allows zero-copy read of values from an underlying API in big endian format.
13///
14/// 'Top' stands for the fact that values are deserialized on their own,
15/// so we have the benefit of knowing their length.
16/// This is useful in many scnearios, such as not having to encode Vec length and others.
17///
18/// The opther optimization that can be done when deserializing top-level objects
19/// is using special functions from the underlying API that do some of the work for the deserializer.
20/// These include getting values directly as i64/u64 or wrapping them directly into an owned Box<[u8]>.
21///
22/// BigInt/BigUint handling is not included here, because these are API-dependent
23/// and would overly complicate the trait.
24///
25pub trait TopDecode: Sized {
26    #[doc(hidden)]
27    const TYPE_INFO: TypeInfo = TypeInfo::Unknown;
28
29    /// Attempt to deserialize the value from input.
30    fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError>;
31
32    /// Version of `top_decode` that exits quickly in case of error.
33    /// Its purpose is to create smaller implementations
34    /// in cases where the application is supposed to exit directly on decode error.
35    fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
36        input: I,
37        c: ExitCtx,
38        exit: fn(ExitCtx, DecodeError) -> !,
39    ) -> Self {
40        match Self::top_decode(input) {
41            Ok(v) => v,
42            Err(e) => exit(c, e),
43        }
44    }
45
46    /// Allows types to provide optimized implementations for their boxed version.
47    /// Especially designed for byte arrays that can be transmuted directly from the input sometimes.
48    #[doc(hidden)]
49    #[inline]
50    fn top_decode_boxed<I: TopDecodeInput>(input: I) -> Result<Box<Self>, DecodeError> {
51        Ok(Box::new(Self::top_decode(input)?))
52    }
53
54    #[doc(hidden)]
55    #[inline]
56    fn top_decode_boxed_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
57        input: I,
58        c: ExitCtx,
59        exit: fn(ExitCtx, DecodeError) -> !,
60    ) -> Box<Self> {
61        Box::new(Self::top_decode_or_exit(input, c, exit))
62    }
63}
64
65/// Top-decodes the result using the NestedDecode implementation.
66pub fn top_decode_from_nested<T, I>(input: I) -> Result<T, DecodeError>
67where
68    I: TopDecodeInput,
69    T: NestedDecode,
70{
71    let bytes = input.into_boxed_slice_u8();
72    let mut_slice = &mut &*bytes;
73    let result = T::dep_decode(mut_slice)?;
74    if !mut_slice.is_empty() {
75        return Err(DecodeError::INPUT_TOO_LONG);
76    }
77    Ok(result)
78}
79
80/// Top-decodes the result using the NestedDecode implementation.
81/// Uses the fast-exit mechanism in case of error.
82pub fn top_decode_from_nested_or_exit<T, I, ExitCtx: Clone>(
83    input: I,
84    c: ExitCtx,
85    exit: fn(ExitCtx, DecodeError) -> !,
86) -> T
87where
88    I: TopDecodeInput,
89    T: NestedDecode,
90{
91    let bytes = input.into_boxed_slice_u8();
92    let mut_slice = &mut &*bytes;
93    let result = T::dep_decode_or_exit(mut_slice, c.clone(), exit);
94    if !mut_slice.is_empty() {
95        exit(c, DecodeError::INPUT_TOO_LONG);
96    }
97    result
98}
99
100impl TopDecode for () {
101    const TYPE_INFO: TypeInfo = TypeInfo::Unit;
102
103    fn top_decode<I: TopDecodeInput>(_: I) -> Result<Self, DecodeError> {
104        Ok(())
105    }
106
107    fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
108        _: I,
109        _: ExitCtx,
110        _: fn(ExitCtx, DecodeError) -> !,
111    ) -> Self {
112    }
113}
114
115impl<T: TopDecode> TopDecode for Box<T> {
116    fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
117        T::top_decode_boxed(input)
118    }
119
120    fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
121        input: I,
122        c: ExitCtx,
123        exit: fn(ExitCtx, DecodeError) -> !,
124    ) -> Self {
125        T::top_decode_boxed_or_exit(input, c, exit)
126    }
127}
128
129// Allowed to implement this because [T] cannot implement NestedDecode, being ?Sized.
130impl<T: NestedDecode> TopDecode for Box<[T]> {
131    fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
132        if let TypeInfo::U8 = T::TYPE_INFO {
133            let bytes = input.into_boxed_slice_u8();
134            let cast_bytes: Box<[T]> = unsafe { core::mem::transmute(bytes) };
135            Ok(cast_bytes)
136        } else {
137            let vec = Vec::<T>::top_decode(input)?;
138            Ok(vec_into_boxed_slice(vec))
139        }
140    }
141
142    /// Quick exit for any of the contained types
143    fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
144        input: I,
145        c: ExitCtx,
146        exit: fn(ExitCtx, DecodeError) -> !,
147    ) -> Self {
148        if let TypeInfo::U8 = T::TYPE_INFO {
149            let bytes = input.into_boxed_slice_u8();
150            let cast_bytes: Box<[T]> = unsafe { core::mem::transmute(bytes) };
151            cast_bytes
152        } else {
153            let vec = Vec::<T>::top_decode_or_exit(input, c, exit);
154            vec_into_boxed_slice(vec)
155        }
156    }
157}
158
159impl<T: NestedDecode> TopDecode for Vec<T> {
160    fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
161        if let TypeInfo::U8 = T::TYPE_INFO {
162            let bytes = input.into_boxed_slice_u8();
163            let bytes_vec = boxed_slice_into_vec(bytes);
164            let cast_vec: Vec<T> = unsafe { core::mem::transmute(bytes_vec) };
165            Ok(cast_vec)
166        } else {
167            let bytes = input.into_boxed_slice_u8();
168            let mut_slice = &mut &*bytes;
169            let mut result: Vec<T> = Vec::new();
170            while !mut_slice.is_empty() {
171                result.push(T::dep_decode(mut_slice)?);
172            }
173            Ok(result)
174        }
175    }
176
177    fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
178        input: I,
179        c: ExitCtx,
180        exit: fn(ExitCtx, DecodeError) -> !,
181    ) -> Self {
182        if let TypeInfo::U8 = T::TYPE_INFO {
183            let bytes = input.into_boxed_slice_u8();
184            let bytes_vec = boxed_slice_into_vec(bytes);
185            let cast_vec: Vec<T> = unsafe { core::mem::transmute(bytes_vec) };
186            cast_vec
187        } else {
188            let bytes = input.into_boxed_slice_u8();
189            let mut_slice = &mut &*bytes;
190            let mut result: Vec<T> = Vec::new();
191            while !mut_slice.is_empty() {
192                result.push(T::dep_decode_or_exit(mut_slice, c.clone(), exit));
193            }
194            result
195        }
196    }
197}
198
199impl TopDecode for String {
200    fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
201        let raw = Vec::<u8>::top_decode(input)?;
202        match String::from_utf8(raw) {
203            Ok(s) => Ok(s),
204            Err(_) => Err(DecodeError::UTF8_DECODE_ERROR),
205        }
206    }
207
208    fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
209        input: I,
210        c: ExitCtx,
211        exit: fn(ExitCtx, DecodeError) -> !,
212    ) -> Self {
213        let raw = Vec::<u8>::top_decode_or_exit(input, c.clone(), exit);
214        match String::from_utf8(raw) {
215            Ok(s) => s,
216            Err(_) => exit(c, DecodeError::UTF8_DECODE_ERROR),
217        }
218    }
219}
220
221impl TopDecode for Box<str> {
222    fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
223        Ok(String::top_decode(input)?.into_boxed_str())
224    }
225
226    fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
227        input: I,
228        c: ExitCtx,
229        exit: fn(ExitCtx, DecodeError) -> !,
230    ) -> Self {
231        String::top_decode_or_exit(input, c, exit).into_boxed_str()
232    }
233}
234
235macro_rules! decode_num_unsigned {
236    ($ty:ty, $bounds_ty:ty, $type_info:expr) => {
237        impl TopDecode for $ty {
238            const TYPE_INFO: TypeInfo = $type_info;
239
240            fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
241                let arg_u64 = input.into_u64();
242                let max = <$bounds_ty>::MAX as u64;
243                if arg_u64 > max {
244                    Err(DecodeError::INPUT_TOO_LONG)
245                } else {
246                    Ok(arg_u64 as $ty)
247                }
248            }
249
250            fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
251                input: I,
252                c: ExitCtx,
253                exit: fn(ExitCtx, DecodeError) -> !,
254            ) -> Self {
255                let arg_u64 = input.into_u64();
256                let max = <$bounds_ty>::MAX as u64;
257                if arg_u64 > max {
258                    exit(c, DecodeError::INPUT_TOO_LONG)
259                } else {
260                    arg_u64 as $ty
261                }
262            }
263        }
264    };
265}
266
267decode_num_unsigned!(u8, u8, TypeInfo::U8);
268decode_num_unsigned!(u16, u16, TypeInfo::U16);
269decode_num_unsigned!(u32, u32, TypeInfo::U32);
270decode_num_unsigned!(usize, u32, TypeInfo::USIZE); // even if usize can be 64 bits on some platforms, we always deserialize as max 32 bits
271decode_num_unsigned!(u64, u64, TypeInfo::U64);
272
273macro_rules! decode_num_signed {
274    ($ty:ty, $bounds_ty:ty, $type_info:expr) => {
275        impl TopDecode for $ty {
276            const TYPE_INFO: TypeInfo = $type_info;
277
278            fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
279                let arg_i64 = input.into_i64();
280                let min = <$bounds_ty>::MIN as i64;
281                let max = <$bounds_ty>::MAX as i64;
282                if arg_i64 < min || arg_i64 > max {
283                    Err(DecodeError::INPUT_OUT_OF_RANGE)
284                } else {
285                    Ok(arg_i64 as $ty)
286                }
287            }
288
289            fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
290                input: I,
291                c: ExitCtx,
292                exit: fn(ExitCtx, DecodeError) -> !,
293            ) -> Self {
294                let arg_i64 = input.into_i64();
295                let min = <$bounds_ty>::MIN as i64;
296                let max = <$bounds_ty>::MAX as i64;
297                if arg_i64 < min || arg_i64 > max {
298                    exit(c, DecodeError::INPUT_OUT_OF_RANGE)
299                } else {
300                    arg_i64 as $ty
301                }
302            }
303        }
304    };
305}
306
307decode_num_signed!(i8, i8, TypeInfo::I8);
308decode_num_signed!(i16, i16, TypeInfo::I16);
309decode_num_signed!(i32, i32, TypeInfo::I32);
310decode_num_signed!(isize, i32, TypeInfo::ISIZE); // even if isize can be 64 bits on some platforms, we always deserialize as max 32 bits
311decode_num_signed!(i64, i64, TypeInfo::I64);
312
313impl TopDecode for bool {
314    const TYPE_INFO: TypeInfo = TypeInfo::Bool;
315
316    fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
317        match input.into_u64() {
318            0 => Ok(false),
319            1 => Ok(true),
320            _ => Err(DecodeError::INPUT_OUT_OF_RANGE),
321        }
322    }
323
324    fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
325        input: I,
326        c: ExitCtx,
327        exit: fn(ExitCtx, DecodeError) -> !,
328    ) -> Self {
329        match input.into_u64() {
330            0 => false,
331            1 => true,
332            _ => exit(c, DecodeError::INPUT_OUT_OF_RANGE),
333        }
334    }
335}
336
337impl<T: NestedDecode> TopDecode for Option<T> {
338    fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
339        let bytes = input.into_boxed_slice_u8();
340        if bytes.is_empty() {
341            Ok(None)
342        } else {
343            let item = dep_decode_from_byte_slice::<T>(&bytes[1..])?;
344            Ok(Some(item))
345        }
346    }
347
348    fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
349        input: I,
350        c: ExitCtx,
351        exit: fn(ExitCtx, DecodeError) -> !,
352    ) -> Self {
353        let bytes = input.into_boxed_slice_u8();
354        if bytes.is_empty() {
355            None
356        } else {
357            let item = dep_decode_from_byte_slice_or_exit(&bytes[1..], c, exit);
358            Some(item)
359        }
360    }
361}
362
363macro_rules! tuple_impls {
364    ($($len:expr => ($($n:tt $name:ident)+))+) => {
365        $(
366            impl<$($name),+> TopDecode for ($($name,)+)
367            where
368                $($name: NestedDecode,)+
369            {
370                fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
371                    top_decode_from_nested(input)
372                }
373
374                fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(input: I, c: ExitCtx, exit: fn(ExitCtx, DecodeError) -> !) -> Self {
375                    top_decode_from_nested_or_exit(input, c, exit)
376                }
377            }
378        )+
379    }
380}
381
382tuple_impls! {
383    1 => (0 T0)
384    2 => (0 T0 1 T1)
385    3 => (0 T0 1 T1 2 T2)
386    4 => (0 T0 1 T1 2 T2 3 T3)
387    5 => (0 T0 1 T1 2 T2 3 T3 4 T4)
388    6 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5)
389    7 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6)
390    8 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7)
391    9 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8)
392    10 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9)
393    11 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10)
394    12 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11)
395    13 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12)
396    14 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13)
397    15 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14)
398    16 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15)
399}
400
401impl TopDecode for NonZeroUsize {
402    fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
403        if let Some(nz) = NonZeroUsize::new(usize::top_decode(input)?) {
404            Ok(nz)
405        } else {
406            Err(DecodeError::INVALID_VALUE)
407        }
408    }
409
410    fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
411        input: I,
412        c: ExitCtx,
413        exit: fn(ExitCtx, DecodeError) -> !,
414    ) -> Self {
415        if let Some(nz) = NonZeroUsize::new(usize::top_decode_or_exit(input, c.clone(), exit)) {
416            nz
417        } else {
418            exit(c, DecodeError::INVALID_VALUE)
419        }
420    }
421}
422
423////////////////////////////////////////////////////////////////////////////////
424
425#[cfg(test)]
426mod tests {
427    use super::super::test_struct::*;
428    use super::*;
429    use crate::test_util::check_top_decode;
430    use core::fmt::Debug;
431
432    fn deser_ok<V>(element: V, bytes: &[u8])
433    where
434        V: TopDecode + PartialEq + Debug + 'static,
435    {
436        let deserialized: V = check_top_decode::<V>(&bytes[..]);
437        assert_eq!(deserialized, element);
438    }
439
440    #[test]
441    fn test_top_numbers_decompacted() {
442        // unsigned positive
443        deser_ok(5u8, &[5]);
444        deser_ok(5u16, &[5]);
445        deser_ok(5u32, &[5]);
446        deser_ok(5u64, &[5]);
447        deser_ok(5usize, &[5]);
448        // signed positive
449        deser_ok(5i8, &[5]);
450        deser_ok(5i16, &[5]);
451        deser_ok(5i32, &[5]);
452        deser_ok(5i64, &[5]);
453        deser_ok(5isize, &[5]);
454        // signed negative
455        deser_ok(-5i8, &[251]);
456        deser_ok(-5i16, &[251]);
457        deser_ok(-5i32, &[251]);
458        deser_ok(-5i64, &[251]);
459        deser_ok(-5isize, &[251]);
460        // non zero usize
461        deser_ok(NonZeroUsize::new(5).unwrap(), &[5]);
462    }
463
464    #[test]
465    fn test_top_numbers_decompacted_2() {
466        deser_ok(-1i32, &[255]);
467        deser_ok(-1i32, &[255, 255]);
468        deser_ok(-1i32, &[255, 255, 255, 255]);
469        deser_ok(-1i64, &[255, 255, 255, 255, 255, 255, 255, 255]);
470    }
471
472    #[test]
473    fn test_top_decode_str() {
474        deser_ok(String::from("abc"), &[b'a', b'b', b'c']);
475        deser_ok(String::from("abc").into_boxed_str(), &[b'a', b'b', b'c']);
476    }
477
478    #[test]
479    fn test_struct() {
480        let test = Test {
481            int: 1,
482            seq: [5, 6].to_vec(),
483            another_byte: 7,
484        };
485        deser_ok(test, &[0, 1, 0, 0, 0, 2, 5, 6, 7]);
486    }
487
488    #[test]
489    fn test_enum() {
490        let u = E::Unit;
491        let expected: &[u8] = &[/*variant index*/ 0, 0, 0, 0];
492        deser_ok(u, expected);
493
494        let n = E::Newtype(1);
495        let expected: &[u8] = &[/*variant index*/ 0, 0, 0, 1, /*data*/ 0, 0, 0, 1];
496        deser_ok(n, expected);
497
498        let t = E::Tuple(1, 2);
499        let expected: &[u8] = &[
500            /*variant index*/ 0, 0, 0, 2, /*(*/ 0, 0, 0, 1, /*,*/ 0, 0, 0,
501            2, /*)*/
502        ];
503        deser_ok(t, expected);
504
505        let s = E::Struct { a: 1 };
506        let expected: &[u8] = &[/*variant index*/ 0, 0, 0, 3, /*data*/ 0, 0, 0, 1];
507        deser_ok(s, expected);
508    }
509}