codeq/
with_checksum.rs

1use std::io::Error;
2use std::io::Read;
3use std::io::Write;
4use std::marker::PhantomData;
5
6use crate::codec::Decode;
7use crate::codec::Encode;
8use crate::config::CodeqConfig;
9use crate::fixed_size::FixedSize;
10
11/// A wrapper that appends a checksum to the encoded data.
12///
13/// During encoding, the inner data is first encoded, then a checksum of the encoded data is
14/// calculated and appended. During decoding, the inner data is first decoded, then the checksum
15/// is verified against the decoded data. If the checksums do not match, an error is returned.
16///
17/// The generic parameter `C` specifies the checksum configuration to use for protecting the data.
18///
19/// Example:
20#[cfg_attr(not(feature = "crc32fast"), doc = "```ignore")]
21#[cfg_attr(feature = "crc32fast", doc = "```rust")]
22/// # use codeq::{Encode, WithChecksum};
23/// use codeq::config::Crc32fast;
24///
25/// let wc = WithChecksum::<Crc32fast, u64>::new(5);
26/// let mut b = Vec::new();
27/// let n = wc.encode(&mut b).unwrap();
28/// assert_eq!(n, 16);
29/// assert_eq!(
30///     vec![
31///         0, 0, 0, 0, 0, 0, 0, 5, // data
32///         0, 0, 0, 0, 21, 72, 43, 230, // checksum
33///     ],
34///     b
35/// );
36/// ```
37/// 
38/// Create a new wrapper with either [`WithChecksum::new`] or [`CodeqConfig::wrap`], for example:
39/// ```ignore
40/// let wc = Crc32fast::wrap(5);
41/// ```
42#[derive(Debug)]
43#[derive(Clone)]
44#[derive(PartialEq, Eq)]
45pub struct WithChecksum<C, T>
46where C: CodeqConfig
47{
48    pub(crate) data: T,
49    _p: PhantomData<C>,
50}
51
52impl<C, T> WithChecksum<C, T>
53where C: CodeqConfig
54{
55    /// Creates a new wrapper around the given data.
56    pub fn new(data: T) -> Self {
57        Self {
58            data,
59            _p: Default::default(),
60        }
61    }
62
63    /// Unwraps and returns the inner data
64    pub fn into_inner(self) -> T {
65        self.data
66    }
67}
68
69impl<C, T> FixedSize for WithChecksum<C, T>
70where
71    C: CodeqConfig,
72    T: FixedSize,
73{
74    fn encoded_size() -> usize {
75        T::encoded_size() + 8
76    }
77}
78
79impl<C, T> Encode for WithChecksum<C, T>
80where
81    C: CodeqConfig,
82    T: Encode,
83{
84    fn encode<W: Write>(&self, mut w: W) -> Result<usize, Error> {
85        let mut n = 0;
86        let mut cw = C::new_writer(&mut w);
87
88        n += self.data.encode(&mut cw)?;
89        n += cw.write_checksum()?;
90
91        Ok(n)
92    }
93}
94
95impl<C, T> Decode for WithChecksum<C, T>
96where
97    C: CodeqConfig,
98    T: Decode,
99{
100    fn decode<R: Read>(r: R) -> Result<Self, Error> {
101        let mut cr = C::new_reader(r);
102
103        let data = T::decode(&mut cr)?;
104        cr.verify_checksum(|| "WithChecksum::decode()")?;
105
106        let meta = Self {
107            data,
108            _p: Default::default(),
109        };
110
111        Ok(meta)
112    }
113}
114
115#[cfg(feature = "crc32fast")]
116#[cfg(test)]
117mod tests_crc32fast {
118    use crate::codec::Encode;
119    use crate::config::CodeqConfig;
120    use crate::config::Crc32fast;
121    use crate::testing::test_codec;
122
123    #[test]
124    fn test_with_checksum_codec() -> anyhow::Result<()> {
125        let wc = Crc32fast::wrap(5u64);
126        let mut b = Vec::new();
127        let n = wc.encode(&mut b)?;
128        assert_eq!(n, b.len());
129
130        assert_eq!(
131            vec![
132                0, 0, 0, 0, 0, 0, 0, 5, // data
133                0, 0, 0, 0, 21, 72, 43, 230, // checksum
134            ],
135            b
136        );
137
138        test_codec(b.as_slice(), &wc)?;
139
140        Ok(())
141    }
142}