hopr_transport_protocol/msg/
codec.rs

1use tokio_util::codec::{Decoder, Encoder};
2
3pub mod v1 {
4    use super::*;
5    use hopr_crypto_packet::chain::ChainPacketComponents;
6
7    #[derive(Clone)]
8    pub struct MsgCodec;
9
10    impl Encoder<Box<[u8]>> for MsgCodec {
11        type Error = std::io::Error;
12
13        fn encode(&mut self, item: Box<[u8]>, dst: &mut tokio_util::bytes::BytesMut) -> Result<(), Self::Error> {
14            tracing::trace!(size = item.len(), protocol = "msg", "Encoding data");
15
16            dst.extend_from_slice(&item);
17            Ok(())
18        }
19    }
20
21    impl Decoder for MsgCodec {
22        type Item = Box<[u8]>;
23
24        type Error = std::io::Error;
25
26        fn decode(&mut self, src: &mut tokio_util::bytes::BytesMut) -> Result<Option<Self::Item>, Self::Error> {
27            let len = src.len();
28            if len >= ChainPacketComponents::SIZE {
29                let packet = src.split_to(ChainPacketComponents::SIZE).freeze();
30
31                tracing::trace!(size = packet.len(), protocol = "msg", "Decoding data");
32                Ok(Some(Box::from_iter(packet)))
33            } else {
34                tracing::trace!(
35                    available_bytes = len,
36                    protocol = "msg",
37                    "Skipping decoding operation, insufficient bytes available"
38                );
39                Ok(None)
40            }
41        }
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48    use anyhow::Context;
49    use hopr_crypto_packet::chain::ChainPacketComponents;
50
51    #[test]
52    fn codec_serialization_and_deserialization_are_reverse_operations() -> anyhow::Result<()> {
53        let mut codec = v1::MsgCodec;
54        let mut buf = tokio_util::bytes::BytesMut::new();
55
56        const PAYLOAD_SIZE: usize = ChainPacketComponents::SIZE;
57        let random_data_of_expected_packet_size: Box<[u8]> =
58            Box::from(hopr_crypto_random::random_bytes::<PAYLOAD_SIZE>());
59
60        codec.encode(random_data_of_expected_packet_size.clone(), &mut buf)?;
61
62        let actual = codec.decode(&mut buf)?;
63
64        assert_eq!(actual, Some(random_data_of_expected_packet_size));
65
66        assert_eq!(buf.len(), 0);
67
68        Ok(())
69    }
70
71    #[test]
72    fn codec_deserialization_of_an_incomplete_byte_sequence_should_not_produce_an_item() -> anyhow::Result<()> {
73        let mut codec = v1::MsgCodec;
74        let mut buf = tokio_util::bytes::BytesMut::new();
75
76        const LESS_THAN_PAYLOAD_SIZE: usize = ChainPacketComponents::SIZE - 1;
77        let random_data_too_few_bytes: Box<[u8]> =
78            Box::from(hopr_crypto_random::random_bytes::<LESS_THAN_PAYLOAD_SIZE>());
79
80        codec.encode(random_data_too_few_bytes, &mut buf)?;
81
82        let actual = codec.decode(&mut buf)?;
83
84        assert_eq!(actual, None);
85
86        assert_eq!(buf.len(), LESS_THAN_PAYLOAD_SIZE);
87
88        Ok(())
89    }
90
91    #[test]
92    fn codec_deserialization_of_too_many_bytes_should_produce_the_value_from_only_the_bytes_needed_for_an_item(
93    ) -> anyhow::Result<()> {
94        let mut codec = v1::MsgCodec;
95        let mut buf = tokio_util::bytes::BytesMut::new();
96
97        const MORE_THAN_PAYLOAD_SIZE: usize = ChainPacketComponents::SIZE + 1;
98        let random_data_more_bytes_than_needed: Box<[u8]> =
99            Box::from(hopr_crypto_random::random_bytes::<MORE_THAN_PAYLOAD_SIZE>());
100
101        codec.encode(random_data_more_bytes_than_needed.clone(), &mut buf)?;
102
103        let actual = codec.decode(&mut buf)?.context("The value should be available")?;
104
105        assert_eq!(
106            actual[..],
107            random_data_more_bytes_than_needed[..ChainPacketComponents::SIZE]
108        );
109
110        assert_eq!(buf.len(), MORE_THAN_PAYLOAD_SIZE - ChainPacketComponents::SIZE);
111
112        Ok(())
113    }
114}