Skip to main content

wincode/schema/external/
smallvec.rs

1use {
2    crate::{
3        ReadResult, SchemaRead, SchemaWrite, WriteResult,
4        config::Config,
5        containers::decode_into_container_t,
6        io::{Reader, Writer},
7        len::SeqLen,
8        schema::{size_of_elem_slice, write_elem_slice_prealloc_check},
9    },
10    core::{mem::MaybeUninit, slice::from_raw_parts_mut},
11    smallvec::SmallVec,
12};
13
14unsafe impl<T, const N: usize, C: Config> SchemaWrite<C> for SmallVec<[T; N]>
15where
16    T: SchemaWrite<C>,
17    T::Src: Sized,
18{
19    type Src = SmallVec<[T::Src; N]>;
20
21    #[inline]
22    fn size_of(src: &Self::Src) -> WriteResult<usize> {
23        size_of_elem_slice::<T, C::LengthEncoding, C>(src)
24    }
25
26    #[inline]
27    fn write(writer: impl Writer, src: &Self::Src) -> WriteResult<()> {
28        write_elem_slice_prealloc_check::<T, C::LengthEncoding, C>(writer, src)
29    }
30}
31
32unsafe impl<'de, T, const N: usize, C: Config> SchemaRead<'de, C> for SmallVec<[T; N]>
33where
34    T: SchemaRead<'de, C>,
35{
36    type Dst = SmallVec<[T::Dst; N]>;
37
38    #[inline]
39    fn read(mut reader: impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()> {
40        let len = C::LengthEncoding::read_prealloc_check::<T::Dst>(reader.by_ref())?;
41
42        // `dst` owns the reserved (possibly heap) allocation from `make` on, but the caller
43        // still treats it as uninitialized and will not drop it. `Guard` frees it on any
44        // early exit -- an `Err` return *or* a panic while decoding.
45        struct Guard<'a, U>(&'a mut MaybeUninit<U>);
46        impl<U> Drop for Guard<'_, U> {
47            fn drop(&mut self) {
48                // SAFETY: the guard is only live while `dst` holds an empty
49                // `SmallVec` (length 0 until the guard is disarmed), so this drops
50                // just the reserved allocation, never any element.
51                unsafe { self.0.assume_init_drop() };
52            }
53        }
54
55        let guard = decode_into_container_t::<T, C, _>(
56            reader,
57            len,
58            // Build in place, since moving a `SmallVec::with_capacity(len)` local into `dst`
59            // would memcpy its whole `[T::Dst; N]` inline region.
60            move |len| {
61                dst.write(SmallVec::new()).reserve_exact(len);
62                Guard(dst)
63            },
64            // SAFETY: `dst` holds an empty `SmallVec` with capacity for `len` elements.
65            |guard| unsafe {
66                let ptr = guard.0.assume_init_mut().as_mut_ptr();
67                from_raw_parts_mut(ptr.cast::<MaybeUninit<T::Dst>>(), len)
68            },
69        )?;
70
71        // SAFETY: `decode_into_container_t` initialized all `len` elements on success.
72        unsafe { guard.0.assume_init_mut().set_len(len) };
73        // `dst` now fully owns the value; keep it rather than dropping via `guard`.
74        core::mem::forget(guard);
75        Ok(())
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use {
82        crate::{deserialize, proptest_config::proptest_cfg, serialize},
83        proptest::prelude::*,
84        smallvec::{SmallVec, smallvec},
85    };
86
87    type SmallVec4<T> = SmallVec<[T; 4]>;
88
89    #[test]
90    fn test_smallvec_inline_roundtrip() {
91        let value: SmallVec4<u8> = smallvec![1u8, 2, 3];
92        let serialized = serialize(&value).unwrap();
93        let deserialized: SmallVec4<u8> = deserialize(&serialized).unwrap();
94        assert_eq!(deserialized, value);
95    }
96
97    #[test]
98    fn test_smallvec_spilled_roundtrip() {
99        let value: SmallVec4<u8> = smallvec![1u8, 2, 3, 4, 5];
100        let serialized = serialize(&value).unwrap();
101        let deserialized: SmallVec4<u8> = deserialize(&serialized).unwrap();
102        assert_eq!(deserialized, value);
103    }
104
105    proptest! {
106        #![proptest_config(proptest_cfg())]
107
108        #[test]
109        fn test_smallvec_static(value in proptest::collection::vec(any::<u64>(), 0..=100).prop_map(SmallVec4::from_vec)) {
110            let bincode_serialized = bincode::serialize(&value).unwrap();
111            let schema_serialized = serialize(&value).unwrap();
112            prop_assert_eq!(&bincode_serialized, &schema_serialized);
113
114            let bincode_deserialized: SmallVec4<u64> = bincode::deserialize(&bincode_serialized).unwrap();
115            let schema_deserialized: SmallVec4<u64> = deserialize(&schema_serialized).unwrap();
116            prop_assert_eq!(&value, &bincode_deserialized);
117            prop_assert_eq!(value, schema_deserialized);
118        }
119
120        #[test]
121        fn test_smallvec_non_static(value in proptest::collection::vec(any::<String>(), 0..=16).prop_map(SmallVec4::from_vec)) {
122            let bincode_serialized = bincode::serialize(&value).unwrap();
123            let schema_serialized = serialize(&value).unwrap();
124            prop_assert_eq!(&bincode_serialized, &schema_serialized);
125
126            let bincode_deserialized: SmallVec4<String> = bincode::deserialize(&bincode_serialized).unwrap();
127            let schema_deserialized: SmallVec4<String> = deserialize(&schema_serialized).unwrap();
128            prop_assert_eq!(&value, &bincode_deserialized);
129            prop_assert_eq!(value, schema_deserialized);
130        }
131    }
132}