platform_serialization/de/
mod.rs

1//! Decoder-based structs and traits.
2
3mod impl_core;
4mod impl_tuples;
5mod impls;
6
7pub use bincode::de::{BorrowDecoder, Decoder};
8pub use bincode::error::DecodeError;
9pub use bincode::{BorrowDecode, Decode};
10use platform_version::version::PlatformVersion;
11
12/// Decode with the default `()` context to avoid repeated generic arguments.
13pub trait DefaultDecode: bincode::Decode<crate::BincodeContext> {
14    fn decode<D: Decoder<Context = crate::BincodeContext>>(
15        decoder: &mut D,
16    ) -> Result<Self, DecodeError>
17    where
18        Self: Sized,
19    {
20        <Self as bincode::Decode<crate::BincodeContext>>::decode(decoder)
21    }
22}
23impl<T> DefaultDecode for T where T: bincode::Decode<crate::BincodeContext> {}
24
25/// BorrowDecode with the default `()` context.
26pub trait DefaultBorrowDecode<'de>: bincode::BorrowDecode<'de, crate::BincodeContext> {
27    fn borrow_decode<D: BorrowDecoder<'de, Context = crate::BincodeContext>>(
28        decoder: &mut D,
29    ) -> Result<Self, DecodeError>
30    where
31        Self: Sized,
32    {
33        <Self as bincode::BorrowDecode<'de, crate::BincodeContext>>::borrow_decode(decoder)
34    }
35}
36impl<'de, T> DefaultBorrowDecode<'de> for T where
37    T: bincode::BorrowDecode<'de, crate::BincodeContext>
38{
39}
40
41/// Trait that makes a type able to be decoded, akin to serde's `DeserializeOwned` trait.
42///
43/// This trait should be implemented for types which do not have references to data in the reader. For types that contain e.g. `&str` and `&[u8]`, implement [BorrowDecode] instead.
44///
45/// Whenever you implement `Decode` for your type, the base trait `BorrowDecode` is automatically implemented.
46///
47/// This trait will be automatically implemented if you enable the `derive` feature and add `#[derive(bincode::Decode)]` to your type. Note that if the type contains any lifetimes, `BorrowDecode` will be implemented instead.
48///
49/// # Implementing this trait manually
50///
51/// If you want to implement this trait for your type, the easiest way is to add a `#[derive(bincode::Decode)]`, build and check your `target/generated/bincode/` folder. This should generate a `<Struct name>_Decode.rs` file.
52///
53/// For this struct:
54///
55/// ```
56/// struct Entity {
57///     pub x: f32,
58///     pub y: f32,
59/// }
60/// ```
61///
62/// It will look something like:
63///
64/// ```
65/// # struct Entity {
66/// #     pub x: f32,
67/// #     pub y: f32,
68/// # }
69/// impl<Context> bincode::Decode<Context> for Entity {
70///     fn decode<D: bincode::de::Decoder<Context = Context>>(
71///         decoder: &mut D,
72///     ) -> core::result::Result<Self, bincode::error::DecodeError> {
73///         Ok(Self {
74///             x: bincode::Decode::decode(decoder)?,
75///             y: bincode::Decode::decode(decoder)?,
76///         })
77///     }
78/// }
79/// impl<'de, Context> bincode::BorrowDecode<'de, Context> for Entity {
80///     fn borrow_decode<D: bincode::de::BorrowDecoder<'de, Context = Context>>(
81///         decoder: &mut D,
82///     ) -> core::result::Result<Self, bincode::error::DecodeError> {
83///         Ok(Self {
84///             x: bincode::BorrowDecode::borrow_decode(decoder)?,
85///             y: bincode::BorrowDecode::borrow_decode(decoder)?,
86///         })
87///     }
88/// }
89/// ```
90///
91/// From here you can add/remove fields, or add custom logic.
92///
93/// To get specific integer types, you can use:
94/// ```
95/// # struct Foo;
96/// # impl<Context> bincode::Decode<Context> for Foo {
97/// #     fn decode<D: bincode::de::Decoder<Context = Context>>(
98/// #         decoder: &mut D,
99/// #     ) -> core::result::Result<Self, bincode::error::DecodeError> {
100/// let x: u8 = bincode::Decode::decode(decoder)?;
101/// let x = <u8 as bincode::Decode<Context>>::decode(decoder)?;
102/// #         Ok(Foo)
103/// #     }
104/// # }
105/// # bincode::impl_borrow_decode!(Foo);
106/// ```
107pub trait PlatformVersionedDecode: Sized {
108    /// Attempt to decode this type with the given [Decode].
109    fn platform_versioned_decode<D: Decoder<Context = crate::BincodeContext>>(
110        decoder: &mut D,
111        platform_version: &PlatformVersion,
112    ) -> Result<Self, DecodeError>;
113}
114
115/// Trait that makes a type able to be decoded, akin to serde's `Deserialize` trait.
116///
117/// This trait should be implemented for types that contain borrowed data, like `&str` and `&[u8]`. If your type does not have borrowed data, consider implementing [Decode] instead.
118///
119/// This trait will be automatically implemented if you enable the `derive` feature and add `#[derive(bincode::Decode)]` to a type with a lifetime.
120pub trait PlatformVersionedBorrowDecode<'de>: Sized {
121    /// Attempt to decode this type with the given [BorrowDecode].
122    fn platform_versioned_borrow_decode<D: BorrowDecoder<'de, Context = crate::BincodeContext>>(
123        decoder: &mut D,
124        platform_version: &PlatformVersion,
125    ) -> Result<Self, DecodeError>;
126}
127
128/// Helper macro to implement `PlatformVersionedBorrowDecode` for any type that implements `PlatformVersionedDecode`.
129#[macro_export]
130macro_rules! impl_platform_versioned_borrow_decode {
131    ($ty:ty) => {
132        impl<'de> $crate::PlatformVersionedBorrowDecode<'de> for $ty {
133            fn platform_versioned_borrow_decode<
134                D: bincode::de::BorrowDecoder<'de, Context = $crate::BincodeContext>,
135            >(
136                decoder: &mut D,
137                platform_version: &PlatformVersion,
138            ) -> core::result::Result<Self, bincode::error::DecodeError> {
139                // Here we directly call the platform_versioned_decode method from
140                // PlatformVersionedDecode, assuming it correctly handles decoding based
141                // on the platform version.
142                <$ty as $crate::PlatformVersionedDecode>::platform_versioned_decode(
143                    decoder,
144                    platform_version,
145                )
146            }
147        }
148    };
149}
150
151/// Decodes only the option variant from the decoder. Will not read any more data than that.
152#[inline]
153pub(crate) fn decode_option_variant<D: Decoder<Context = crate::BincodeContext>>(
154    decoder: &mut D,
155    type_name: &'static str,
156) -> Result<Option<()>, DecodeError> {
157    let is_some = <u8 as DefaultDecode>::decode(decoder)?;
158    match is_some {
159        0 => Ok(None),
160        1 => Ok(Some(())),
161        x => Err(DecodeError::UnexpectedVariant {
162            found: x as u32,
163            allowed: &bincode::error::AllowedEnumVariants::Range { max: 1, min: 0 },
164            type_name,
165        }),
166    }
167}
168
169/// Maximum allowed collection length during deserialization (1 Mi elements).
170///
171/// This acts as a defense-in-depth guard against crafted payloads that encode
172/// enormous collection lengths in a varint prefix. Even when the bincode config
173/// has no byte-budget limit, this cap prevents a single decoded length field
174/// from triggering an unbounded allocation that could OOM-abort the process.
175///
176/// Set to 1 MiB (1,048,576) elements because:
177/// - The largest legitimate structure (StateTransition) is capped at 100 KB,
178///   meaning even a `Vec<u8>` can hold at most ~100 K elements in practice.
179/// - For wider element types (`[u8; 32]`, etc.) the memory impact is
180///   `element_count × element_size`, so a smaller cap keeps the worst case
181///   well within OS limits (1 Mi × 64 bytes = 64 MiB).
182/// - This is a last-resort guard; the primary protection is the per-type
183///   byte-budget configured via `#[platform_serialize(limit = N)]`.
184const MAX_COLLECTION_LEN: u64 = 1024 * 1024;
185
186/// Decodes the length of any slice, container, etc from the decoder
187#[inline]
188pub(crate) fn decode_slice_len<D: Decoder<Context = crate::BincodeContext>>(
189    decoder: &mut D,
190) -> Result<usize, DecodeError> {
191    let v = <u64 as DefaultDecode>::decode(decoder)?;
192
193    if v > MAX_COLLECTION_LEN {
194        return Err(DecodeError::LimitExceeded);
195    }
196
197    v.try_into().map_err(|_| DecodeError::OutsideUsizeRange(v))
198}
199
200#[cfg(test)]
201mod tests {
202    use super::*;
203    use bincode::config;
204    use platform_version::version::PlatformVersion;
205
206    fn cfg() -> impl bincode::config::Config {
207        config::standard().with_big_endian().with_no_limit()
208    }
209
210    fn pv() -> &'static PlatformVersion {
211        PlatformVersion::first()
212    }
213
214    /// Helper: encode a u64 varint using bincode big-endian standard config,
215    /// then attempt to decode it as a collection length via `decode_slice_len`.
216    fn try_decode_len(value: u64) -> Result<usize, DecodeError> {
217        let config = config::standard().with_big_endian().with_no_limit();
218        let encoded = bincode::encode_to_vec(value, config).unwrap();
219        let reader = bincode::de::read::SliceReader::new(&encoded);
220        let mut decoder = bincode::de::DecoderImpl::new(reader, config, ());
221        decode_slice_len(&mut decoder)
222    }
223
224    #[test]
225    fn decode_slice_len_accepts_small_values() {
226        assert_eq!(try_decode_len(0).unwrap(), 0);
227        assert_eq!(try_decode_len(1).unwrap(), 1);
228        assert_eq!(try_decode_len(1024).unwrap(), 1024);
229    }
230
231    #[test]
232    fn decode_slice_len_accepts_at_limit() {
233        let limit = MAX_COLLECTION_LEN;
234        assert_eq!(try_decode_len(limit).unwrap(), limit as usize);
235    }
236
237    #[test]
238    fn decode_slice_len_rejects_above_limit() {
239        let above = MAX_COLLECTION_LEN + 1;
240        match try_decode_len(above) {
241            Err(DecodeError::LimitExceeded) => {} // expected
242            other => panic!("expected LimitExceeded, got {:?}", other),
243        }
244    }
245
246    #[test]
247    fn decode_slice_len_rejects_huge_value() {
248        // Simulates the attack vector: a varint-encoded u64::MAX
249        match try_decode_len(u64::MAX) {
250            Err(DecodeError::LimitExceeded) => {} // expected
251            other => panic!("expected LimitExceeded, got {:?}", other),
252        }
253    }
254
255    // -----------------------------------------------------------------------
256    // decode_option_variant
257    // -----------------------------------------------------------------------
258
259    fn try_decode_option_variant(byte: u8) -> Result<Option<()>, DecodeError> {
260        let data = bincode::encode_to_vec(byte, cfg()).unwrap();
261        let reader = bincode::de::read::SliceReader::new(&data);
262        let mut decoder = bincode::de::DecoderImpl::new(reader, cfg(), ());
263        decode_option_variant(&mut decoder, "test::Option")
264    }
265
266    #[test]
267    fn decode_option_variant_none() {
268        assert_eq!(try_decode_option_variant(0).unwrap(), None);
269    }
270
271    #[test]
272    fn decode_option_variant_some() {
273        assert_eq!(try_decode_option_variant(1).unwrap(), Some(()));
274    }
275
276    #[test]
277    fn decode_option_variant_invalid() {
278        match try_decode_option_variant(2) {
279            Err(DecodeError::UnexpectedVariant { found: 2, .. }) => {}
280            other => panic!("expected UnexpectedVariant, got {:?}", other),
281        }
282        match try_decode_option_variant(255) {
283            Err(DecodeError::UnexpectedVariant { found: 255, .. }) => {}
284            other => panic!("expected UnexpectedVariant, got {:?}", other),
285        }
286    }
287
288    // -----------------------------------------------------------------------
289    // PlatformVersionedDecode for Option<T>
290    // -----------------------------------------------------------------------
291
292    #[test]
293    fn option_some_round_trip() {
294        let value: Option<u32> = Some(42);
295        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
296        let decoded: Option<u32> =
297            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
298        assert_eq!(decoded, Some(42));
299    }
300
301    #[test]
302    fn option_none_round_trip() {
303        let value: Option<u32> = None;
304        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
305        let decoded: Option<u32> =
306            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
307        assert_eq!(decoded, None);
308    }
309
310    // -----------------------------------------------------------------------
311    // PlatformVersionedDecode for Result<T, U>
312    // -----------------------------------------------------------------------
313
314    #[test]
315    fn result_ok_round_trip() {
316        let value: Result<u32, u8> = Ok(123);
317        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
318        let decoded: Result<u32, u8> =
319            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
320        assert_eq!(decoded, Ok(123));
321    }
322
323    #[test]
324    fn result_err_round_trip() {
325        let value: Result<u32, u8> = Err(99);
326        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
327        let decoded: Result<u32, u8> =
328            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
329        assert_eq!(decoded, Err(99));
330    }
331
332    #[test]
333    fn result_invalid_variant() {
334        // Manually encode variant tag 2 (invalid for Result which expects 0 or 1)
335        let mut data = bincode::encode_to_vec(2u32, cfg()).unwrap();
336        // Append some dummy payload bytes
337        data.extend_from_slice(&[0u8; 8]);
338        let result =
339            crate::platform_versioned_decode_from_slice::<Result<u32, u8>, _>(&data, cfg(), pv());
340        match result {
341            Err(DecodeError::UnexpectedVariant { found: 2, .. }) => {}
342            other => panic!("expected UnexpectedVariant, got {:?}", other),
343        }
344    }
345
346    // -----------------------------------------------------------------------
347    // PlatformVersionedDecode for Bound<T>
348    // -----------------------------------------------------------------------
349
350    #[test]
351    fn bound_unbounded_round_trip() {
352        use core::ops::Bound;
353        let value: Bound<u32> = Bound::Unbounded;
354        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
355        let decoded: Bound<u32> =
356            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
357        assert_eq!(decoded, Bound::Unbounded);
358    }
359
360    #[test]
361    fn bound_included_round_trip() {
362        use core::ops::Bound;
363        let value: Bound<u32> = Bound::Included(10);
364        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
365        let decoded: Bound<u32> =
366            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
367        assert_eq!(decoded, Bound::Included(10));
368    }
369
370    #[test]
371    fn bound_excluded_round_trip() {
372        use core::ops::Bound;
373        let value: Bound<u32> = Bound::Excluded(20);
374        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
375        let decoded: Bound<u32> =
376            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
377        assert_eq!(decoded, Bound::Excluded(20));
378    }
379
380    #[test]
381    fn bound_invalid_variant() {
382        use core::ops::Bound;
383        // variant 3 is invalid for Bound (valid: 0, 1, 2)
384        let mut data = bincode::encode_to_vec(3u32, cfg()).unwrap();
385        data.extend_from_slice(&[0u8; 8]);
386        let result =
387            crate::platform_versioned_decode_from_slice::<Bound<u32>, _>(&data, cfg(), pv());
388        match result {
389            Err(DecodeError::UnexpectedVariant { found: 3, .. }) => {}
390            other => panic!("expected UnexpectedVariant, got {:?}", other),
391        }
392    }
393
394    // -----------------------------------------------------------------------
395    // Array decode: u8 optimization vs non-u8 path
396    // -----------------------------------------------------------------------
397
398    #[test]
399    fn array_u8_decode_round_trip() {
400        // This triggers the u8-optimized path in [T; N] decode
401        let value: [u8; 4] = [0xDE, 0xAD, 0xBE, 0xEF];
402        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
403        let decoded: [u8; 4] =
404            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
405        assert_eq!(decoded, value);
406    }
407
408    #[test]
409    fn array_non_u8_decode_round_trip() {
410        // This triggers the non-u8 path through collect_into_array
411        let value: [u32; 3] = [100, 200, 300];
412        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
413        let decoded: [u32; 3] =
414            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
415        assert_eq!(decoded, value);
416    }
417
418    #[test]
419    fn array_borrow_decode_u8_path() {
420        let value: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
421        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
422        let decoded: [u8; 8] =
423            crate::platform_versioned_borrow_decode_from_slice(&encoded, cfg(), pv()).unwrap();
424        assert_eq!(decoded, value);
425    }
426
427    #[test]
428    fn array_borrow_decode_non_u8_path() {
429        let value: [i16; 4] = [-1, 0, 1, 32767];
430        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
431        let decoded: [i16; 4] =
432            crate::platform_versioned_borrow_decode_from_slice(&encoded, cfg(), pv()).unwrap();
433        assert_eq!(decoded, value);
434    }
435
436    // -----------------------------------------------------------------------
437    // Cell / RefCell
438    // -----------------------------------------------------------------------
439
440    #[test]
441    fn cell_round_trip() {
442        use core::cell::Cell;
443        let value = Cell::new(42u32);
444        let encoded = crate::platform_encode_to_vec(&value, cfg(), pv()).unwrap();
445        let decoded: Cell<u32> =
446            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
447        assert_eq!(decoded.get(), 42);
448    }
449
450    #[test]
451    fn refcell_round_trip() {
452        use core::cell::RefCell;
453        let value = RefCell::new(99u16);
454        let encoded = crate::platform_encode_to_vec(&value, cfg(), pv()).unwrap();
455        let decoded: RefCell<u16> =
456            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
457        assert_eq!(*decoded.borrow(), 99);
458    }
459
460    // -----------------------------------------------------------------------
461    // Range / RangeInclusive
462    // -----------------------------------------------------------------------
463
464    #[test]
465    fn range_round_trip() {
466        let value: core::ops::Range<u32> = 10..20;
467        let encoded = crate::platform_encode_to_vec(value.clone(), cfg(), pv()).unwrap();
468        let decoded: core::ops::Range<u32> =
469            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
470        assert_eq!(decoded, value);
471    }
472
473    #[test]
474    fn range_inclusive_round_trip() {
475        let value: core::ops::RangeInclusive<i32> = -5..=5;
476        let encoded = crate::platform_encode_to_vec(value.clone(), cfg(), pv()).unwrap();
477        let decoded: core::ops::RangeInclusive<i32> =
478            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
479        assert_eq!(decoded, value);
480    }
481
482    // -----------------------------------------------------------------------
483    // PhantomData / unit
484    // -----------------------------------------------------------------------
485
486    #[test]
487    fn unit_round_trip() {
488        let value = ();
489        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
490        let decoded: () =
491            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
492        assert_eq!(decoded, ());
493    }
494
495    #[test]
496    fn phantom_data_round_trip() {
497        use core::marker::PhantomData;
498        let value: PhantomData<u32> = PhantomData;
499        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
500        let decoded: PhantomData<u32> =
501            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
502        assert_eq!(decoded, PhantomData);
503    }
504
505    // -----------------------------------------------------------------------
506    // Primitive type round-trips (representative subset)
507    // -----------------------------------------------------------------------
508
509    #[test]
510    fn bool_round_trip() {
511        for value in [true, false] {
512            let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
513            let decoded: bool =
514                crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
515            assert_eq!(decoded, value);
516        }
517    }
518
519    #[test]
520    fn char_round_trip() {
521        for value in ['a', '\u{1F600}', '\0'] {
522            let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
523            let decoded: char =
524                crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
525            assert_eq!(decoded, value);
526        }
527    }
528
529    #[test]
530    fn f64_round_trip() {
531        let value: f64 = core::f64::consts::PI;
532        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
533        let decoded: f64 =
534            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
535        assert_eq!(decoded, value);
536    }
537
538    #[test]
539    fn duration_round_trip() {
540        let value = core::time::Duration::new(123, 456_789);
541        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
542        let decoded: core::time::Duration =
543            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
544        assert_eq!(decoded, value);
545    }
546
547    #[test]
548    fn borrow_decode_byte_slice() {
549        // Use bincode's own encode for &[u8] to match the borrow decode path
550        let data: &[u8] = b"hello bytes";
551        let encoded = bincode::encode_to_vec(data, cfg()).unwrap();
552        let decoded: &[u8] =
553            crate::platform_versioned_borrow_decode_from_slice(&encoded, cfg(), pv()).unwrap();
554        assert_eq!(decoded, data);
555    }
556
557    #[test]
558    fn borrow_decode_str() {
559        let data = "borrow me";
560        let encoded = crate::platform_encode_to_vec(data, cfg(), pv()).unwrap();
561        let decoded: &str =
562            crate::platform_versioned_borrow_decode_from_slice(&encoded, cfg(), pv()).unwrap();
563        assert_eq!(decoded, data);
564    }
565
566    // -----------------------------------------------------------------------
567    // NonZero type round-trips (cover the delegation impls in de/impls.rs)
568    // -----------------------------------------------------------------------
569
570    macro_rules! nonzero_round_trip_test {
571        ($name:ident, $ty:ty, $val:expr) => {
572            #[test]
573            fn $name() {
574                let value: $ty = <$ty>::new($val).unwrap();
575                let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
576                let decoded: $ty =
577                    crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
578                assert_eq!(decoded, value);
579            }
580        };
581    }
582
583    nonzero_round_trip_test!(nonzero_u8_round_trip, core::num::NonZeroU8, 1);
584    nonzero_round_trip_test!(nonzero_u16_round_trip, core::num::NonZeroU16, 100);
585    nonzero_round_trip_test!(nonzero_u32_round_trip, core::num::NonZeroU32, 1000);
586    nonzero_round_trip_test!(nonzero_u64_round_trip, core::num::NonZeroU64, 10000);
587    nonzero_round_trip_test!(nonzero_u128_round_trip, core::num::NonZeroU128, 100000);
588    nonzero_round_trip_test!(nonzero_usize_round_trip, core::num::NonZeroUsize, 42);
589    nonzero_round_trip_test!(nonzero_i8_round_trip, core::num::NonZeroI8, -1);
590    nonzero_round_trip_test!(nonzero_i16_round_trip, core::num::NonZeroI16, -100);
591    nonzero_round_trip_test!(nonzero_i32_round_trip, core::num::NonZeroI32, -1000);
592    nonzero_round_trip_test!(nonzero_i64_round_trip, core::num::NonZeroI64, -10000);
593    nonzero_round_trip_test!(nonzero_i128_round_trip, core::num::NonZeroI128, -100000);
594    nonzero_round_trip_test!(nonzero_isize_round_trip, core::num::NonZeroIsize, -42);
595
596    // -----------------------------------------------------------------------
597    // Remaining primitive types (i8, i16, i32, i64, i128, isize, u128, f32)
598    // -----------------------------------------------------------------------
599
600    #[test]
601    fn i8_round_trip() {
602        let value: i8 = -128;
603        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
604        let decoded: i8 =
605            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
606        assert_eq!(decoded, value);
607    }
608
609    #[test]
610    fn i16_round_trip() {
611        let value: i16 = -32768;
612        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
613        let decoded: i16 =
614            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
615        assert_eq!(decoded, value);
616    }
617
618    #[test]
619    fn i32_round_trip() {
620        let value: i32 = -2_000_000;
621        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
622        let decoded: i32 =
623            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
624        assert_eq!(decoded, value);
625    }
626
627    #[test]
628    fn i128_round_trip() {
629        let value: i128 = i128::MIN;
630        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
631        let decoded: i128 =
632            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
633        assert_eq!(decoded, value);
634    }
635
636    #[test]
637    fn isize_round_trip() {
638        let value: isize = -999;
639        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
640        let decoded: isize =
641            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
642        assert_eq!(decoded, value);
643    }
644
645    #[test]
646    fn u128_round_trip() {
647        let value: u128 = u128::MAX;
648        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
649        let decoded: u128 =
650            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
651        assert_eq!(decoded, value);
652    }
653
654    #[test]
655    fn usize_round_trip() {
656        let value: usize = 12345;
657        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
658        let decoded: usize =
659            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
660        assert_eq!(decoded, value);
661    }
662
663    #[test]
664    fn f32_round_trip() {
665        let value: f32 = core::f32::consts::E;
666        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
667        let decoded: f32 =
668            crate::platform_versioned_decode_from_slice(&encoded, cfg(), pv()).unwrap();
669        assert_eq!(decoded, value);
670    }
671
672    // -----------------------------------------------------------------------
673    // Borrow-decode round-trips for Cell, RefCell, Option, Range, etc.
674    // -----------------------------------------------------------------------
675
676    #[test]
677    fn cell_borrow_decode_round_trip() {
678        use core::cell::Cell;
679        let value = Cell::new(77u32);
680        let encoded = crate::platform_encode_to_vec(&value, cfg(), pv()).unwrap();
681        let decoded: Cell<u32> =
682            crate::platform_versioned_borrow_decode_from_slice(&encoded, cfg(), pv()).unwrap();
683        assert_eq!(decoded.get(), 77);
684    }
685
686    #[test]
687    fn refcell_borrow_decode_round_trip() {
688        use core::cell::RefCell;
689        let value = RefCell::new(88u16);
690        let encoded = crate::platform_encode_to_vec(&value, cfg(), pv()).unwrap();
691        let decoded: RefCell<u16> =
692            crate::platform_versioned_borrow_decode_from_slice(&encoded, cfg(), pv()).unwrap();
693        assert_eq!(*decoded.borrow(), 88);
694    }
695
696    #[test]
697    fn option_borrow_decode_some() {
698        let value: Option<u32> = Some(42);
699        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
700        let decoded: Option<u32> =
701            crate::platform_versioned_borrow_decode_from_slice(&encoded, cfg(), pv()).unwrap();
702        assert_eq!(decoded, Some(42));
703    }
704
705    #[test]
706    fn option_borrow_decode_none() {
707        let value: Option<u32> = None;
708        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
709        let decoded: Option<u32> =
710            crate::platform_versioned_borrow_decode_from_slice(&encoded, cfg(), pv()).unwrap();
711        assert_eq!(decoded, None);
712    }
713
714    #[test]
715    fn result_borrow_decode_ok() {
716        let value: Result<u32, u8> = Ok(123);
717        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
718        let decoded: Result<u32, u8> =
719            crate::platform_versioned_borrow_decode_from_slice(&encoded, cfg(), pv()).unwrap();
720        assert_eq!(decoded, Ok(123));
721    }
722
723    #[test]
724    fn result_borrow_decode_err() {
725        let value: Result<u32, u8> = Err(99);
726        let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
727        let decoded: Result<u32, u8> =
728            crate::platform_versioned_borrow_decode_from_slice(&encoded, cfg(), pv()).unwrap();
729        assert_eq!(decoded, Err(99));
730    }
731
732    #[test]
733    fn result_borrow_decode_invalid_variant() {
734        let mut data = bincode::encode_to_vec(2u32, cfg()).unwrap();
735        data.extend_from_slice(&[0u8; 8]);
736        let result = crate::platform_versioned_borrow_decode_from_slice::<Result<u32, u8>, _>(
737            &data,
738            cfg(),
739            pv(),
740        );
741        match result {
742            Err(DecodeError::UnexpectedVariant { found: 2, .. }) => {}
743            other => panic!("expected UnexpectedVariant, got {:?}", other),
744        }
745    }
746
747    #[test]
748    fn range_borrow_decode_round_trip() {
749        let value: core::ops::Range<u32> = 5..15;
750        let encoded = crate::platform_encode_to_vec(value.clone(), cfg(), pv()).unwrap();
751        let decoded: core::ops::Range<u32> =
752            crate::platform_versioned_borrow_decode_from_slice(&encoded, cfg(), pv()).unwrap();
753        assert_eq!(decoded, value);
754    }
755
756    #[test]
757    fn range_inclusive_borrow_decode_round_trip() {
758        let value: core::ops::RangeInclusive<i32> = -10..=10;
759        let encoded = crate::platform_encode_to_vec(value.clone(), cfg(), pv()).unwrap();
760        let decoded: core::ops::RangeInclusive<i32> =
761            crate::platform_versioned_borrow_decode_from_slice(&encoded, cfg(), pv()).unwrap();
762        assert_eq!(decoded, value);
763    }
764
765    #[test]
766    fn bound_borrow_decode_all_variants() {
767        use core::ops::Bound;
768        for value in [
769            Bound::Unbounded,
770            Bound::Included(10u32),
771            Bound::Excluded(20u32),
772        ] {
773            let encoded = crate::platform_encode_to_vec(value, cfg(), pv()).unwrap();
774            let decoded: Bound<u32> =
775                crate::platform_versioned_borrow_decode_from_slice(&encoded, cfg(), pv()).unwrap();
776            assert_eq!(decoded, value);
777        }
778    }
779
780    #[test]
781    fn bound_borrow_decode_invalid_variant() {
782        use core::ops::Bound;
783        let mut data = bincode::encode_to_vec(3u32, cfg()).unwrap();
784        data.extend_from_slice(&[0u8; 8]);
785        let result =
786            crate::platform_versioned_borrow_decode_from_slice::<Bound<u32>, _>(&data, cfg(), pv());
787        match result {
788            Err(DecodeError::UnexpectedVariant { found: 3, .. }) => {}
789            other => panic!("expected UnexpectedVariant, got {:?}", other),
790        }
791    }
792}