bin_proto/
codec.rs

1#[cfg(feature = "alloc")]
2use alloc::vec::Vec;
3#[cfg(feature = "std")]
4use std::io::{self, Cursor};
5
6use bitstream_io::{BitRead, BitReader, BitWrite, BitWriter, Endianness};
7#[cfg(not(feature = "std"))]
8use core2::io::{self, Cursor};
9
10use crate::{Error, Result};
11
12/// A trait for bit-level decoding.
13pub trait BitDecode<Ctx = (), Tag = ()>: Sized {
14    /// Reads self from a stream.
15    fn decode<R, E>(read: &mut R, ctx: &mut Ctx, tag: Tag) -> Result<Self>
16    where
17        R: BitRead,
18        E: Endianness;
19}
20
21/// Utility functionality for bit-level decoding.
22pub trait BitDecodeExt<Ctx = (), Tag = ()>:
23    BitDecode<Ctx, Tag> + bit_decode::Sealed<Ctx, Tag>
24{
25    /// Parses a new value from its raw byte representation with provided context and tag.
26    ///
27    /// Returns a tuple of the parsed value and the number of bits read.
28    fn decode_bytes_ctx<E>(
29        bytes: &[u8],
30        byte_order: E,
31        ctx: &mut Ctx,
32        tag: Tag,
33    ) -> Result<(Self, u64)>
34    where
35        E: Endianness,
36    {
37        let mut buffer = BitReader::endian(io::Cursor::new(bytes), byte_order);
38        let this = Self::decode::<_, E>(&mut buffer, ctx, tag)?;
39        Ok((this, buffer.position_in_bits()?))
40    }
41
42    /// Parses a new value from its raw byte representation with provided context and tag, consuming
43    /// entire buffer.
44    fn decode_all_bytes_ctx<E>(bytes: &[u8], byte_order: E, ctx: &mut Ctx, tag: Tag) -> Result<Self>
45    where
46        E: Endianness,
47    {
48        let (decoded, read_bits) = Self::decode_bytes_ctx(bytes, byte_order, ctx, tag)?;
49        let available_bits = u64::try_from(bytes.len())? * 8;
50        if read_bits == available_bits {
51            Ok(decoded)
52        } else {
53            Err(Error::Underrun {
54                read_bits,
55                available_bits,
56            })
57        }
58    }
59}
60
61impl<T, Ctx, Tag> BitDecodeExt<Ctx, Tag> for T where
62    T: BitDecode<Ctx, Tag> + bit_decode::Sealed<Ctx, Tag>
63{
64}
65
66/// A trait for bit-level encoding.
67pub trait BitEncode<Ctx = (), Tag = ()> {
68    /// Writes a value to a stream.
69    fn encode<W, E>(&self, write: &mut W, ctx: &mut Ctx, tag: Tag) -> Result<()>
70    where
71        W: BitWrite,
72        E: Endianness;
73}
74
75/// Utility functionality for bit-level encoding.
76pub trait BitEncodeExt<Ctx = (), Tag = ()>:
77    BitEncode<Ctx, Tag> + bit_encode::Sealed<Ctx, Tag>
78{
79    /// Gets the raw bytes of this type with provided context and tag.
80    #[cfg(feature = "alloc")]
81    fn encode_bytes_ctx<E>(&self, byte_order: E, ctx: &mut Ctx, tag: Tag) -> Result<Vec<u8>>
82    where
83        E: Endianness,
84    {
85        let mut data = Vec::new();
86        let mut writer = BitWriter::endian(&mut data, byte_order);
87        self.encode::<_, E>(&mut writer, ctx, tag)?;
88        writer.byte_align()?;
89
90        Ok(data)
91    }
92
93    /// Fills the buffer with the raw bytes of this type with provided context and tag.
94    ///
95    /// Returns the number of bytes written.
96    fn encode_bytes_ctx_buf<E>(
97        &self,
98        byte_order: E,
99        ctx: &mut Ctx,
100        tag: Tag,
101        buf: &mut [u8],
102    ) -> Result<u64>
103    where
104        E: Endianness,
105    {
106        let mut cursor = Cursor::new(buf);
107        let mut writer = BitWriter::endian(&mut cursor, byte_order);
108        self.encode::<_, E>(&mut writer, ctx, tag)?;
109        writer.byte_align()?;
110
111        Ok(cursor.position())
112    }
113}
114
115impl<T, Ctx, Tag> BitEncodeExt<Ctx, Tag> for T where
116    T: BitEncode<Ctx, Tag> + bit_encode::Sealed<Ctx, Tag>
117{
118}
119
120/// A trait with helper functions for simple codecs.
121pub trait BitCodec: BitDecode + BitEncode + bit_codec::Sealed {
122    /// Parses a new value from its raw byte representation.
123    ///
124    /// Returns a tuple of the parsed value and the number of bits read.
125    fn decode_bytes<E>(bytes: &[u8], byte_order: E) -> Result<(Self, u64)>
126    where
127        E: Endianness,
128    {
129        Self::decode_bytes_ctx(bytes, byte_order, &mut (), ())
130    }
131
132    /// Parses a new value from its raw byte representation, consuming entire buffer.
133    fn decode_all_bytes<E>(bytes: &[u8], byte_order: E) -> Result<Self>
134    where
135        E: Endianness,
136    {
137        Self::decode_all_bytes_ctx(bytes, byte_order, &mut (), ())
138    }
139
140    /// Gets the raw bytes of this type.
141    #[cfg(feature = "alloc")]
142    fn encode_bytes<E>(&self, byte_order: E) -> Result<Vec<u8>>
143    where
144        E: Endianness,
145    {
146        self.encode_bytes_ctx(byte_order, &mut (), ())
147    }
148
149    /// Fills the buffer with the raw bytes of this type.
150    ///
151    /// Returns the number of bytes written.
152    fn encode_bytes_buf<E>(&self, byte_order: E, buf: &mut [u8]) -> Result<u64>
153    where
154        E: Endianness,
155    {
156        self.encode_bytes_ctx_buf(byte_order, &mut (), (), buf)
157    }
158}
159
160impl<T> BitCodec for T where T: BitDecode + BitEncode + bit_codec::Sealed {}
161
162mod bit_encode {
163    use super::BitEncode;
164
165    pub trait Sealed<Ctx, Tag> {}
166
167    impl<Ctx, Tag, T> Sealed<Ctx, Tag> for T where T: BitEncode<Ctx, Tag> {}
168}
169
170mod bit_decode {
171    use super::BitDecode;
172
173    pub trait Sealed<Ctx, Tag> {}
174
175    impl<Ctx, Tag, T> Sealed<Ctx, Tag> for T where T: BitDecode<Ctx, Tag> {}
176}
177
178mod bit_codec {
179    use super::{BitDecode, BitEncode};
180
181    pub trait Sealed {}
182
183    impl<T> Sealed for T where T: BitDecode + BitEncode {}
184}
185
186macro_rules! test_decode {
187    ($ty:ty | $tag:expr; $bytes:expr => $exp:expr) => {
188        #[cfg(test)]
189        #[test]
190        fn decode() {
191            let bytes: &[u8] = &$bytes;
192            let exp: $ty = $exp;
193            let read: $ty = $crate::BitDecode::<(), _>::decode::<_, ::bitstream_io::BigEndian>(
194                &mut ::bitstream_io::BitReader::endian(bytes, ::bitstream_io::BigEndian),
195                &mut (),
196                $tag,
197            )
198            .unwrap();
199            assert_eq!(exp, read);
200        }
201    };
202    ($ty:ty; $bytes:expr => $exp:expr) => {
203        #[cfg(test)]
204        #[test]
205        fn decode() {
206            let bytes: &[u8] = &$bytes;
207            let exp: $ty = $exp;
208            let decoded: $ty = $crate::BitDecode::decode::<_, ::bitstream_io::BigEndian>(
209                &mut ::bitstream_io::BitReader::endian(bytes, ::bitstream_io::BigEndian),
210                &mut (),
211                (),
212            )
213            .unwrap();
214            assert_eq!(exp, decoded);
215        }
216    };
217}
218
219macro_rules! test_encode {
220    ($ty:ty $(| $tag:expr)?; $value:expr => $exp:expr) => {
221        #[cfg(test)]
222        #[test]
223        fn encode() {
224            use $crate::BitEncode;
225
226            let exp: &[u8] = &$exp;
227            let value: $ty = $value;
228
229            #[cfg(feature = "alloc")]
230            {
231                let mut buffer: ::alloc::vec::Vec<u8> = ::alloc::vec::Vec::new();
232                value
233                    .encode::<_, ::bitstream_io::BigEndian>(
234                        &mut ::bitstream_io::BitWriter::endian(&mut buffer, ::bitstream_io::BigEndian),
235                        &mut (),
236                        ($($tag)?),
237                    )
238                    .unwrap();
239                assert_eq!(exp, &buffer);
240            }
241
242            #[cfg(not(feature = "alloc"))]
243            {
244                let mut buffer = [0u8; 16];
245                value
246                    .encode::<_, ::bitstream_io::BigEndian>(
247                        &mut ::bitstream_io::BitWriter::endian(&mut ::core2::io::Cursor::new(buffer.as_mut_slice()), ::bitstream_io::BigEndian),
248                        &mut (),
249                        ($($tag)?),
250                    )
251                    .unwrap();
252                assert_eq!(exp, &buffer[..exp.len()]);
253                assert!(::core::iter::Iterator::all(
254                    &mut ::core::iter::IntoIterator::into_iter(&buffer[exp.len()..]),
255                    |x| *x == 0)
256                );
257            }
258        }
259    };
260}
261
262macro_rules! test_codec {
263    ($ty:ty$(| $tag_write:expr, $tag_read:expr)?; $value:expr => $bytes:expr) => {
264        test_decode!($ty$(| $tag_read)?; $bytes => $value);
265        test_encode!($ty$(| $tag_write)?; $value => $bytes);
266    }
267}
268
269macro_rules! test_roundtrip {
270    ($ty:ty) => {
271        #[cfg(all(test, feature = "alloc"))]
272        ::proptest::proptest!(
273            #[test]
274            fn roundtrip(x in ::proptest::arbitrary::any::<$ty>()) {
275                let encoded = $crate::BitEncodeExt::encode_bytes_ctx(&x, ::bitstream_io::BigEndian, &mut (), ()).unwrap();
276                let decoded = <$ty as $crate::BitDecodeExt>::decode_bytes_ctx(&encoded, ::bitstream_io::BigEndian, &mut (), ()).unwrap().0;
277                ::proptest::prop_assert_eq!(x, decoded);
278            }
279        );
280    }
281}
282
283#[allow(unused)]
284macro_rules! test_untagged_and_codec {
285    ($ty:ty | $tag_write:expr, $tag_read:expr; $value:expr => $bytes:expr) => {
286        test_codec!($ty | $tag_write, $tag_read; $value => $bytes);
287        #[cfg(test)]
288        mod untagged {
289            use super::*;
290
291            test_decode!($ty| $crate::Untagged; $bytes => $value);
292        }
293    }
294}