Skip to main content

midnight_serialize/
util.rs

1// This file is part of midnight-ledger.
2// Copyright (C) 2025 Midnight Foundation
3// SPDX-License-Identifier: Apache-2.0
4// Licensed under the Apache License, Version 2.0 (the "License");
5// You may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7// http://www.apache.org/licenses/LICENSE-2.0
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14use crate::{Deserializable, Serializable, Tagged};
15#[cfg(feature = "proptest")]
16use proptest::strategy::ValueTree;
17#[cfg(feature = "proptest")]
18use proptest::{
19    strategy::{NewTree, Strategy},
20    test_runner::TestRunner,
21};
22#[cfg(feature = "proptest")]
23use rand::Rng;
24#[cfg(feature = "proptest")]
25use rand::distributions::{Distribution, Standard};
26#[cfg(feature = "proptest")]
27use std::fmt::Debug;
28use std::io::{BufWriter, Read};
29#[cfg(feature = "proptest")]
30use std::marker::PhantomData;
31
32pub trait VecExt {
33    fn with_bounded_capacity(n: usize) -> Self;
34}
35
36impl<T> VecExt for Vec<T> {
37    fn with_bounded_capacity(n: usize) -> Self {
38        const MEMORY_LIMIT: usize = 1 << 25; // 32 MiB
39        let alloc_limit = MEMORY_LIMIT / std::mem::size_of::<T>();
40        Self::with_capacity(usize::min(alloc_limit, n))
41    }
42}
43
44pub trait ReadExt: Read {
45    fn read_exact_to_vec(&mut self, n: usize) -> std::io::Result<Vec<u8>> {
46        const CHUNK_SIZE: usize = 4096;
47        let mut res = Vec::with_capacity(CHUNK_SIZE);
48        let mut len = 0;
49        while n > len {
50            let new_len = usize::min(n, len + CHUNK_SIZE);
51            res.resize(new_len, 0);
52            self.read_exact(&mut res[len..])?;
53            len = new_len;
54        }
55        Ok(res)
56    }
57}
58
59impl<R: Read> ReadExt for R {}
60
61impl Serializable for () {
62    fn serialize(&self, _writer: &mut impl std::io::Write) -> std::io::Result<()> {
63        Ok(())
64    }
65    fn serialized_size(&self) -> usize {
66        0
67    }
68}
69
70impl Deserializable for () {
71    fn deserialize(_reader: &mut impl Read, _recursion_depth: u32) -> std::io::Result<Self> {
72        Ok(())
73    }
74}
75
76impl Tagged for () {
77    fn tag() -> std::borrow::Cow<'static, str> {
78        std::borrow::Cow::Borrowed("()")
79    }
80    fn tag_unique_factor() -> String {
81        "()".into()
82    }
83}
84
85impl Serializable for bool {
86    fn serialize(&self, writer: &mut impl std::io::Write) -> std::io::Result<()> {
87        writer.write_all(&[*self as u8])
88    }
89    fn serialized_size(&self) -> usize {
90        1
91    }
92}
93
94impl Deserializable for bool {
95    fn deserialize(reader: &mut impl Read, _recursion_depth: u32) -> std::io::Result<Self> {
96        let mut buf = [0u8];
97        reader.read_exact(&mut buf[..])?;
98        match buf[0] {
99            0 => Ok(false),
100            1 => Ok(true),
101            v => Err(std::io::Error::new(
102                std::io::ErrorKind::InvalidData,
103                format!("cannot deserialize {v} as bool"),
104            )),
105        }
106    }
107}
108
109impl Tagged for bool {
110    fn tag() -> std::borrow::Cow<'static, str> {
111        std::borrow::Cow::Borrowed("bool")
112    }
113    fn tag_unique_factor() -> String {
114        "bool".into()
115    }
116}
117
118macro_rules! via_le_bytes {
119    ($ty:ty, $len:expr_2021) => {
120        impl Serializable for $ty {
121            fn serialize(&self, writer: &mut impl std::io::Write) -> std::io::Result<()> {
122                writer.write_all(&self.to_le_bytes()[..])
123            }
124            fn serialized_size(&self) -> usize {
125                $len
126            }
127        }
128
129        impl Deserializable for $ty {
130            fn deserialize(reader: &mut impl Read, _recursion_depth: u32) -> std::io::Result<Self> {
131                let mut buf = [0u8; $len];
132                reader.read_exact(&mut buf[..])?;
133                Ok(<$ty>::from_le_bytes(buf))
134            }
135        }
136
137        impl Tagged for $ty {
138            fn tag() -> std::borrow::Cow<'static, str> {
139                std::borrow::Cow::Borrowed(stringify!($ty))
140            }
141            fn tag_unique_factor() -> String {
142                stringify!($ty).into()
143            }
144        }
145    };
146}
147
148macro_rules! via_scale {
149    ($ty:ty, $n:expr_2021) => {
150        impl Serializable for $ty {
151            fn serialize(&self, writer: &mut impl std::io::Write) -> std::io::Result<()> {
152                ScaleBigInt::from(*self).serialize(writer)
153            }
154            fn serialized_size(&self) -> usize {
155                ScaleBigInt::from(*self).serialized_size()
156            }
157        }
158
159        impl Deserializable for $ty {
160            fn deserialize(reader: &mut impl Read, recursion_depth: u32) -> std::io::Result<Self> {
161                <$ty>::try_from(ScaleBigInt::deserialize(reader, recursion_depth)?)
162            }
163        }
164
165        impl From<$ty> for ScaleBigInt {
166            fn from(val: $ty) -> ScaleBigInt {
167                let mut res = ScaleBigInt([0u8; SCALE_MAX_BYTES]);
168                let le_bytes = val.to_le_bytes();
169                res.0[..$n].copy_from_slice(&le_bytes[..]);
170                res
171            }
172        }
173
174        impl TryFrom<ScaleBigInt> for $ty {
175            type Error = std::io::Error;
176            fn try_from(val: ScaleBigInt) -> std::io::Result<$ty> {
177                if val.0[$n..].iter().any(|b| *b != 0) {
178                    return Err(std::io::Error::new(
179                        std::io::ErrorKind::InvalidData,
180                        concat!("out of range for ", stringify!($ty)),
181                    ));
182                }
183                Ok(<$ty>::from_le_bytes(
184                    val.0[..$n]
185                        .try_into()
186                        .expect("slice of known size must coerce to array"),
187                ))
188            }
189        }
190
191        impl Tagged for $ty {
192            fn tag() -> std::borrow::Cow<'static, str> {
193                std::borrow::Cow::Borrowed(stringify!($ty))
194            }
195            fn tag_unique_factor() -> String {
196                stringify!($ty).into()
197            }
198        }
199    };
200}
201
202via_le_bytes!(u8, 1);
203via_le_bytes!(u16, 2);
204via_le_bytes!(i8, 1);
205via_le_bytes!(i16, 2);
206via_le_bytes!(i32, 4);
207via_le_bytes!(i64, 8);
208via_le_bytes!(i128, 16);
209via_scale!(u32, 4);
210via_scale!(u64, 8);
211via_scale!(u128, 16);
212
213const SCALE_MAX_BYTES: usize = 67;
214pub struct ScaleBigInt(pub [u8; SCALE_MAX_BYTES]);
215
216impl Default for ScaleBigInt {
217    fn default() -> Self {
218        ScaleBigInt([0u8; SCALE_MAX_BYTES])
219    }
220}
221
222const SCALE_ONE_BYTE_MARKER: u8 = 0b00;
223const SCALE_TWO_BYTE_MARKER: u8 = 0b01;
224const SCALE_FOUR_BYTE_MARKER: u8 = 0b10;
225const SCALE_N_BYTE_MARKER: u8 = 0b11;
226
227impl Serializable for ScaleBigInt {
228    fn serialize(&self, writer: &mut impl std::io::Write) -> std::io::Result<()> {
229        let top2bits = |b| (b & 0b1100_0000) >> 6;
230        let bot6bits = |b| (b & 0b0011_1111) << 2;
231        match self.serialized_size() {
232            1 => writer.write_all(&[bot6bits(self.0[0]) | SCALE_ONE_BYTE_MARKER]),
233            2 => {
234                let b0 = bot6bits(self.0[0]) | SCALE_TWO_BYTE_MARKER;
235                let b1 = top2bits(self.0[0]) | bot6bits(self.0[1]);
236                writer.write_all(&[b0, b1])
237            }
238            4 => {
239                let b0 = bot6bits(self.0[0]) | SCALE_FOUR_BYTE_MARKER;
240                let b1 = top2bits(self.0[0]) | bot6bits(self.0[1]);
241                let b2 = top2bits(self.0[1]) | bot6bits(self.0[2]);
242                let b3 = top2bits(self.0[2]) | bot6bits(self.0[3]);
243                writer.write_all(&[b0, b1, b2, b3])
244            }
245            n => {
246                writer.write_all(&[(n as u8 - 5) << 2 | SCALE_N_BYTE_MARKER])?;
247                writer.write_all(&self.0[..n - 1])
248            }
249        }
250    }
251    fn serialized_size(&self) -> usize {
252        let trailing_zeros = self.0.iter().rev().take_while(|x| **x == 0).count();
253        let occupied = SCALE_MAX_BYTES - trailing_zeros;
254        let can_squeeze = self.0[occupied.saturating_sub(1)] < 64;
255        match (occupied, can_squeeze) {
256            (0, _) | (1, true) => 1,
257            (1, false) | (2, true) => 2,
258            (2, false) | (3, _) | (4, true) => 4,
259            (n, _) => n + 1,
260        }
261    }
262}
263
264impl Deserializable for ScaleBigInt {
265    fn deserialize(reader: &mut impl Read, recursion_depth: u32) -> std::io::Result<Self> {
266        let first = u8::deserialize(reader, recursion_depth)?;
267        let mut res = ScaleBigInt([0u8; SCALE_MAX_BYTES]);
268        let top6bits = |b| (b & 0b1111_1100) >> 2;
269        let bot2bits = |b| (b & 0b0000_0011) << 6;
270        match first & 0b11 {
271            SCALE_ONE_BYTE_MARKER => res.0[0] = top6bits(first),
272            SCALE_TWO_BYTE_MARKER => {
273                let second = u8::deserialize(reader, recursion_depth)?;
274                if second == 0 {
275                    return Err(std::io::Error::new(
276                        std::io::ErrorKind::InvalidData,
277                        "non-canonical scale encoding",
278                    ));
279                }
280                res.0[0] = top6bits(first) | bot2bits(second);
281                res.0[1] = top6bits(second);
282            }
283            SCALE_FOUR_BYTE_MARKER => {
284                let second = u8::deserialize(reader, recursion_depth)?;
285                let third = u8::deserialize(reader, recursion_depth)?;
286                let fourth = u8::deserialize(reader, recursion_depth)?;
287                if third == 0 && fourth == 0 {
288                    return Err(std::io::Error::new(
289                        std::io::ErrorKind::InvalidData,
290                        "non-canonical scale encoding",
291                    ));
292                }
293                res.0[0] = top6bits(first) | bot2bits(second);
294                res.0[1] = top6bits(second) | bot2bits(third);
295                res.0[2] = top6bits(third) | bot2bits(fourth);
296                res.0[3] = top6bits(fourth);
297            }
298            SCALE_N_BYTE_MARKER => {
299                let n = top6bits(first) as usize + 4;
300                reader.read_exact(&mut res.0[..n])?;
301                if res.0[n - 1] == 0 {
302                    return Err(std::io::Error::new(
303                        std::io::ErrorKind::InvalidData,
304                        "non-canonical scale encoding",
305                    ));
306                }
307            }
308            _ => unreachable!(),
309        }
310        Ok(res)
311    }
312}
313
314macro_rules! tuple_serializable {
315    (($a:tt, $aidx: tt)$(, ($as:tt, $asidx: tt))*) => {
316        impl<$a: Serializable,$($as: Serializable,)*> Serializable for ($a,$($as,)*) {
317            fn serialize(
318                &self,
319                writer: &mut impl std::io::Write,
320            ) -> std::io::Result<()> {
321                <$a as Serializable>::serialize(&(self.$aidx), writer)?;
322                $(<$as as Serializable>::serialize(&(self.$asidx), writer)?;)*
323                Ok(())
324            }
325
326            fn serialized_size(&self) -> usize {
327                <$a as Serializable>::serialized_size(&(self.$aidx)) $(+ <$as as Serializable>::serialized_size(&(self.$asidx)))*
328            }
329        }
330
331        impl<$a: Deserializable,$($as: Deserializable,)*> Deserializable for ($a,$($as,)*) {
332            fn deserialize(reader: &mut impl std::io::Read, mut recursion_depth: u32) -> std::io::Result<Self> {
333                <Self as Deserializable>::check_rec(&mut recursion_depth)?;
334                Ok((
335                <$a as Deserializable>::deserialize(reader, recursion_depth)?,
336                $(<$as as Deserializable>::deserialize(reader, recursion_depth)?,)*
337                ))
338            }
339        }
340
341        impl<$a: Tagged,$($as: Tagged,)*> Tagged for ($a, $($as,)*) {
342            fn tag() -> std::borrow::Cow<'static, str> {
343                let mut res = String::new();
344                res.push_str("(");
345                res.push_str(&$a::tag());
346                $(
347                    res.push_str(",");
348                    res.push_str(&$as::tag());
349                )*
350                res.push_str(")");
351                std::borrow::Cow::Owned(res)
352            }
353            fn tag_unique_factor() -> String {
354                let mut res = String::new();
355                res.push_str("(");
356                res.push_str(&$a::tag_unique_factor());
357                $(
358                    res.push_str(",");
359                    res.push_str(&$as::tag_unique_factor());
360                )*
361                res.push_str(")");
362                res
363            }
364        }
365    }
366}
367
368tuple_serializable!((A, 0));
369tuple_serializable!((A, 0), (B, 1));
370tuple_serializable!((A, 0), (B, 1), (C, 2));
371tuple_serializable!((A, 0), (B, 1), (C, 2), (D, 3));
372tuple_serializable!((A, 0), (B, 1), (C, 2), (D, 3), (E, 4));
373tuple_serializable!((A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5));
374tuple_serializable!((A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6));
375tuple_serializable!(
376    (A, 0),
377    (B, 1),
378    (C, 2),
379    (D, 3),
380    (E, 4),
381    (F, 5),
382    (G, 6),
383    (H, 7)
384);
385tuple_serializable!(
386    (A, 0),
387    (B, 1),
388    (C, 2),
389    (D, 3),
390    (E, 4),
391    (F, 5),
392    (G, 6),
393    (H, 7),
394    (I, 8)
395);
396tuple_serializable!(
397    (A, 0),
398    (B, 1),
399    (C, 2),
400    (D, 3),
401    (E, 4),
402    (F, 5),
403    (G, 6),
404    (H, 7),
405    (I, 8),
406    (J, 9)
407);
408tuple_serializable!(
409    (A, 0),
410    (B, 1),
411    (C, 2),
412    (D, 3),
413    (E, 4),
414    (F, 5),
415    (G, 6),
416    (H, 7),
417    (I, 8),
418    (J, 9),
419    (K, 10)
420);
421tuple_serializable!(
422    (A, 0),
423    (B, 1),
424    (C, 2),
425    (D, 3),
426    (E, 4),
427    (F, 5),
428    (G, 6),
429    (H, 7),
430    (I, 8),
431    (J, 9),
432    (K, 10),
433    (L, 11)
434);
435tuple_serializable!(
436    (A, 0),
437    (B, 1),
438    (C, 2),
439    (D, 3),
440    (E, 4),
441    (F, 5),
442    (G, 6),
443    (H, 7),
444    (I, 8),
445    (J, 9),
446    (K, 10),
447    (L, 11),
448    (M, 12)
449);
450tuple_serializable!(
451    (A, 0),
452    (B, 1),
453    (C, 2),
454    (D, 3),
455    (E, 4),
456    (F, 5),
457    (G, 6),
458    (H, 7),
459    (I, 8),
460    (J, 9),
461    (K, 10),
462    (L, 11),
463    (M, 12),
464    (N, 13)
465);
466tuple_serializable!(
467    (A, 0),
468    (B, 1),
469    (C, 2),
470    (D, 3),
471    (E, 4),
472    (F, 5),
473    (G, 6),
474    (H, 7),
475    (I, 8),
476    (J, 9),
477    (K, 10),
478    (L, 11),
479    (M, 12),
480    (N, 13),
481    (O, 14)
482);
483tuple_serializable!(
484    (A, 0),
485    (B, 1),
486    (C, 2),
487    (D, 3),
488    (E, 4),
489    (F, 5),
490    (G, 6),
491    (H, 7),
492    (I, 8),
493    (J, 9),
494    (K, 10),
495    (L, 11),
496    (M, 12),
497    (N, 13),
498    (O, 14),
499    (P, 15)
500);
501
502impl Deserializable for String {
503    fn deserialize(reader: &mut impl Read, recursion_depth: u32) -> std::io::Result<Self> {
504        let vec = <Vec<u8>>::deserialize(reader, recursion_depth)?;
505        String::from_utf8(vec).map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
506    }
507}
508
509impl Tagged for String {
510    fn tag() -> std::borrow::Cow<'static, str> {
511        std::borrow::Cow::Borrowed("string")
512    }
513    fn tag_unique_factor() -> String {
514        "string".into()
515    }
516}
517
518impl Tagged for str {
519    fn tag() -> std::borrow::Cow<'static, str> {
520        std::borrow::Cow::Borrowed("string")
521    }
522    fn tag_unique_factor() -> String {
523        "string".into()
524    }
525}
526
527pub fn gen_static_serialize_file<T: Serializable + Tagged>(
528    value: &T,
529) -> Result<(), std::io::Error> {
530    let mut file = BufWriter::new(std::fs::File::create(format!("{}.bin", T::tag(),))?);
531    crate::tagged_serialize(&value, &mut file)
532}
533
534pub fn test_file_deserialize<T: Deserializable + Tagged>(
535    path: std::path::PathBuf,
536) -> Result<T, std::io::Error> {
537    let bytes = std::fs::read(path)?;
538    crate::tagged_deserialize(&mut bytes.as_slice())
539}
540
541#[cfg(feature = "proptest")]
542pub struct NoSearch<T>(T);
543
544#[cfg(feature = "proptest")]
545impl<T: Debug + Clone> ValueTree for NoSearch<T> {
546    type Value = T;
547
548    fn current(&self) -> T {
549        self.0.clone()
550    }
551
552    fn simplify(&mut self) -> bool {
553        false
554    }
555
556    fn complicate(&mut self) -> bool {
557        false
558    }
559}
560
561#[derive(Debug)]
562#[cfg(feature = "proptest")]
563pub struct NoStrategy<T>(pub PhantomData<T>);
564
565#[cfg(feature = "proptest")]
566impl<T: Debug + Clone> Strategy for NoStrategy<T>
567where
568    Standard: Distribution<T>,
569{
570    type Tree = NoSearch<T>;
571    type Value = T;
572
573    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
574        Ok(NoSearch(runner.rng().r#gen()))
575    }
576}
577
578#[macro_export]
579macro_rules! tag_enforcement_test {
580    ($type:ident) => {
581        serialize::tag_enforcement_test!($type < >);
582    };
583    ($type:ident < $($targ:ty),* >) => {
584        #[cfg(test)]
585        ::pastey::paste! {
586            #[allow(non_snake_case)]
587            #[test]
588            fn [<tag_enforcement_test_ $type>]() {
589                let tag = <$type<$($targ),*> as serialize::Tagged>::tag();
590                println!("{tag}");
591                let unique_factor = <$type<$($targ),*> as serialize::Tagged>::tag_unique_factor();
592                let mut dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
593                dir.pop();
594                dir.push(".tag-decompositions");
595                ::std::fs::create_dir_all(&dir).unwrap();
596                let fpath = dir.join(tag.as_ref());
597                if ::std::fs::exists(&fpath).unwrap() {
598                    let read_factor = ::std::fs::read_to_string(&fpath).unwrap();
599                    assert_eq!(read_factor, unique_factor);
600                } else {
601                    ::std::fs::write(&fpath, unique_factor).unwrap();
602                }
603            }
604        }
605    };
606}
607
608#[macro_export]
609#[cfg(feature = "proptest")]
610macro_rules! randomised_tagged_serialization_test {
611    ($type:ident) => {
612        serialize::randomised_tagged_serialization_test!($type < >);
613    };
614    ($type:ident < $($targ:ty),* >) => {
615        #[cfg(test)]
616        ::pastey::paste! {
617            #[allow(non_snake_case)]
618            #[test]
619            fn [<proptest_deserialize_tagged_ $type>]() where $type<$($targ),*>: proptest::prelude::Arbitrary {
620                let mut runner = proptest::test_runner::TestRunner::default();
621
622                runner.run(&<$type<$($targ),*> as proptest::prelude::Arbitrary>::arbitrary(), |v| {
623                    let mut bytes: Vec<u8> = Vec::new();
624                    serialize::tagged_serialize(&v, &mut bytes).unwrap();
625                    let des_result: $type<$($targ),*> = serialize::tagged_deserialize(&mut bytes.as_slice()).unwrap();
626                    assert_eq!(des_result, v);
627
628                    Ok(())
629                }).unwrap();
630            }
631            serialize::randomised_serialization_test!($type<$($targ),*>);
632        }
633    }
634}
635
636#[macro_export]
637#[cfg(feature = "proptest")]
638macro_rules! randomised_serialization_test {
639    ($type:ident) => {
640        serialize::randomised_serialization_test!($type < >);
641    };
642    ($type:ident < $($targ:ty),* >) => {
643        #[cfg(test)]
644        ::pastey::paste! {
645            #[allow(non_snake_case)]
646            #[test]
647            fn [<proptest_deserialize_ $type>]() where $type<$($targ),*>: proptest::prelude::Arbitrary {
648                let mut runner = proptest::test_runner::TestRunner::default();
649
650                runner.run(&<$type<$($targ),*> as proptest::prelude::Arbitrary>::arbitrary(), |v| {
651                    let mut bytes: Vec<u8> = Vec::new();
652                    <$type<$($targ),*> as serialize::Serializable>::serialize(&v, &mut bytes).unwrap();
653                    let des_result: $type<$($targ),*> = <$type<$($targ),*> as serialize::Deserializable>::deserialize(&mut bytes.as_slice(), 0).unwrap();
654                    assert_eq!(des_result, v);
655
656                    Ok(())
657                }).unwrap();
658            }
659
660            #[allow(non_snake_case)]
661            #[test]
662            fn [<proptest_serialized_size_ $type>]() where $type<$($targ),*>: proptest::prelude::Arbitrary {
663                let mut runner = proptest::test_runner::TestRunner::default();
664
665                runner.run(&<$type<$($targ),*> as proptest::prelude::Arbitrary>::arbitrary(), |v| {
666                    let mut bytes: Vec<u8> = Vec::new();
667                    <$type<$($targ),*> as serialize::Serializable>::serialize(&v, &mut bytes).unwrap();
668                    assert_eq!(bytes.len(), <$type<$($targ),*> as serialize::Serializable>::serialized_size(&v));
669
670                    Ok(())
671                }).unwrap();
672            }
673
674            #[allow(non_snake_case)]
675            #[test]
676            fn [<proptest_random_data_deserialize_ $type>]() {
677                use rand::Rng;
678                let mut rng = rand::thread_rng();
679
680                for _ in 0..100 {
681                    let size: u8 = rng.r#gen();
682                    let mut bytes: Vec<u8> = Vec::new();
683                    for _i in 0..size {
684                        bytes.push(rng.r#gen())
685                    }
686                    let _ = <$type<$($targ),*> as serialize::Deserializable>::deserialize(&mut bytes.as_slice(), 0);
687                }
688            }
689        }
690    };
691}
692
693/// Produce a single arbitrary value without the ability to simplify or complicate
694#[macro_export]
695#[cfg(feature = "proptest")]
696macro_rules! simple_arbitrary {
697    ($type:ty) => {
698        impl Arbitrary for $type {
699            type Parameters = ();
700            type Strategy = NoStrategy<$type>;
701
702            fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
703                NoStrategy(PhantomData)
704            }
705        }
706    };
707}
708
709#[cfg(feature = "proptest")]
710#[allow(unused)]
711use crate as serialize;
712
713#[cfg(feature = "proptest")]
714randomised_serialization_test!(String);