bee_common/
packable.rs

1// Copyright 2020-2021 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4//! A module that provides a `Packable` trait to serialize and deserialize types.
5
6pub use std::io::{Read, Write};
7
8/// A trait to pack and unpack types to and from bytes.
9pub trait Packable {
10    /// Associated error type.
11    type Error: std::fmt::Debug;
12
13    /// Returns the length of the packed bytes.
14    fn packed_len(&self) -> usize;
15
16    /// Packs the instance to bytes and writes them to the passed writer.
17    fn pack<W: Write>(&self, writer: &mut W) -> Result<(), Self::Error>;
18
19    /// Packs the instance to bytes and writes them to a newly allocated vector.
20    fn pack_new(&self) -> Vec<u8> {
21        let mut bytes = Vec::with_capacity(self.packed_len());
22        // Packing to bytes can't fail.
23        self.pack(&mut bytes).unwrap();
24
25        bytes
26    }
27
28    /// Reads bytes from the passed reader and unpacks them into an instance.
29    fn unpack_inner<R: Read + ?Sized, const CHECK: bool>(reader: &mut R) -> Result<Self, Self::Error>
30    where
31        Self: Sized;
32
33    /// Reads bytes from the passed reader and unpacks them into an instance.
34    /// Applies syntactic checks.
35    fn unpack<R: Read + ?Sized>(reader: &mut R) -> Result<Self, Self::Error>
36    where
37        Self: Sized,
38    {
39        Self::unpack_inner::<R, true>(reader)
40    }
41
42    /// Reads bytes from the passed reader and unpacks them into an instance.
43    /// Doesn't apply syntactic checks.
44    fn unpack_unchecked<R: Read + ?Sized>(reader: &mut R) -> Result<Self, Self::Error>
45    where
46        Self: Sized,
47    {
48        Self::unpack_inner::<R, false>(reader)
49    }
50}
51
52impl<const N: usize> Packable for [u8; N] {
53    type Error = std::io::Error;
54
55    fn packed_len(&self) -> usize {
56        N
57    }
58
59    fn pack<W: Write>(&self, writer: &mut W) -> Result<(), Self::Error> {
60        writer.write_all(self)?;
61
62        Ok(())
63    }
64
65    fn unpack_inner<R: Read + ?Sized, const CHECK: bool>(reader: &mut R) -> Result<Self, Self::Error>
66    where
67        Self: Sized,
68    {
69        let mut bytes = [0u8; N];
70        reader.read_exact(&mut bytes)?;
71
72        Ok(bytes)
73    }
74}
75
76impl Packable for bool {
77    type Error = std::io::Error;
78
79    fn packed_len(&self) -> usize {
80        (*self as u8).packed_len()
81    }
82
83    fn pack<W: Write>(&self, writer: &mut W) -> Result<(), Self::Error> {
84        (*self as u8).pack(writer)
85    }
86
87    fn unpack_inner<R: Read + ?Sized, const CHECK: bool>(reader: &mut R) -> Result<Self, Self::Error>
88    where
89        Self: Sized,
90    {
91        Ok(u8::unpack_inner::<R, CHECK>(reader)? != 0)
92    }
93}
94
95impl<P> Packable for Vec<P>
96where
97    P: Packable,
98    P::Error: From<std::io::Error>,
99{
100    type Error = P::Error;
101
102    fn packed_len(&self) -> usize {
103        0u64.packed_len() + self.iter().map(Packable::packed_len).sum::<usize>()
104    }
105
106    fn pack<W: Write>(&self, writer: &mut W) -> Result<(), Self::Error> {
107        (self.len() as u64).pack(writer)?;
108        self.iter().try_for_each(|x| x.pack(writer))
109    }
110
111    fn unpack_inner<R: Read + ?Sized, const CHECK: bool>(reader: &mut R) -> Result<Self, Self::Error>
112    where
113        Self: Sized,
114    {
115        (0..u64::unpack_inner::<R, CHECK>(reader)?)
116            .map(|_| P::unpack_inner::<R, CHECK>(reader))
117            .collect()
118    }
119}
120
121/// Error that occurs on `Option<P: Packable>` operations.
122#[derive(Debug)]
123pub enum OptionError<E> {
124    /// Error that occurs on boolean `Packable` operations.
125    Bool(<bool as Packable>::Error),
126    /// Error that occurs on inner `Packable` operations.
127    Inner(E),
128}
129
130impl<E> From<E> for OptionError<E> {
131    fn from(inner: E) -> Self {
132        OptionError::Inner(inner)
133    }
134}
135
136impl<P: Packable> Packable for Option<P> {
137    type Error = OptionError<P::Error>;
138
139    fn packed_len(&self) -> usize {
140        true.packed_len() + self.as_ref().map_or(0, Packable::packed_len)
141    }
142
143    fn pack<W: Write>(&self, writer: &mut W) -> Result<(), Self::Error> {
144        if let Some(p) = self {
145            true.pack(writer).map_err(OptionError::Bool)?;
146            p.pack(writer).map_err(OptionError::Inner)?;
147        } else {
148            false.pack(writer).map_err(OptionError::Bool)?;
149        }
150
151        Ok(())
152    }
153
154    fn unpack_inner<R: Read + ?Sized, const CHECK: bool>(reader: &mut R) -> Result<Self, Self::Error>
155    where
156        Self: Sized,
157    {
158        Ok(if bool::unpack_inner::<R, CHECK>(reader).map_err(OptionError::Bool)? {
159            Some(P::unpack_inner::<R, CHECK>(reader).map_err(OptionError::Inner)?)
160        } else {
161            None
162        })
163    }
164}
165
166macro_rules! impl_packable_for_num {
167    ($ty:ident) => {
168        impl Packable for $ty {
169            type Error = std::io::Error;
170
171            fn packed_len(&self) -> usize {
172                std::mem::size_of_val(&self.to_le_bytes())
173            }
174
175            fn pack<W: Write>(&self, writer: &mut W) -> Result<(), Self::Error> {
176                writer.write_all(&self.to_le_bytes())?;
177
178                Ok(())
179            }
180
181            fn unpack_inner<R: Read + ?Sized, const CHECK: bool>(reader: &mut R) -> Result<Self, Self::Error>
182            where
183                Self: Sized,
184            {
185                let mut bytes = [0; $ty::MIN.to_le_bytes().len()];
186                reader.read_exact(&mut bytes)?;
187
188                Ok($ty::from_le_bytes(bytes))
189            }
190        }
191    };
192}
193
194impl_packable_for_num!(i8);
195impl_packable_for_num!(u8);
196impl_packable_for_num!(i16);
197impl_packable_for_num!(u16);
198impl_packable_for_num!(i32);
199impl_packable_for_num!(u32);
200impl_packable_for_num!(i64);
201impl_packable_for_num!(u64);
202#[cfg(has_i128)]
203impl_packable_for_num!(i128);
204#[cfg(has_u128)]
205impl_packable_for_num!(u128);