Skip to main content

jkl/
encode.rs

1//! Traits for fixed-size and variable-length binary serialization.
2//!
3//! [`FixedCode`] covers types with a constant byte-width representation,
4//! while [`VarCode`] extends to bit-level variable-length encoding via
5//! [`WriteBits`] / [`ReadBits`].
6
7use std::{error::Error, io};
8
9use crate::bits::{ReadBits, WriteBits};
10
11/// A fixed-size byte buffer that can be used as the serialized representation of a [`FixedCode`] value.
12///
13/// Implementors provide a `zeroed` constructor for use as a read buffer during decoding.
14pub trait ByteArray: AsRef<[u8]> + AsMut<[u8]> + Send + Sync + 'static {
15    /// Returns a zeroed byte array.
16    fn zeroed() -> Self;
17}
18
19impl<const N: usize> ByteArray for [u8; N] {
20    #[inline]
21    fn zeroed() -> Self {
22        [0u8; N]
23    }
24}
25
26/// A trait for generic encoding of values that can be represented as a fixed-size array of bytes.
27pub trait FixedCode: Sized {
28    const SIZE: usize;
29    type Array: ByteArray;
30    type Error: Error + Send + Sync + 'static;
31    fn fix_encode(&self) -> Self::Array;
32    fn fix_decode(input: &Self::Array) -> Result<Self, Self::Error>;
33
34    /// Encodes `self` and writes the resulting bytes to `write`.
35    #[inline]
36    fn fix_write(&self, write: &mut impl io::Write) -> io::Result<()> {
37        io::Write::write_all(write, self.fix_encode().as_ref())
38    }
39
40    /// Reads exactly [`SIZE`](Self::SIZE) bytes from `read` and decodes them.
41    #[inline]
42    fn fix_read(read: &mut impl io::Read) -> io::Result<Self> {
43        let mut buffer = Self::Array::zeroed();
44        io::Read::read_exact(read, buffer.as_mut())?;
45        Self::fix_decode(&buffer).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
46    }
47}
48
49impl<const N: usize> FixedCode for [u8; N]
50where
51    [u8; N]: ByteArray,
52{
53    const SIZE: usize = N;
54    type Array = [u8; N];
55    type Error = std::convert::Infallible;
56
57    #[inline]
58    fn fix_encode(&self) -> Self {
59        *self
60    }
61
62    #[inline]
63    fn fix_decode(input: &Self) -> Result<Self, Self::Error> {
64        Ok(*input)
65    }
66}
67
68macro_rules! impl_fixedcode_le_bytes {
69    ($($t:ty),* $(,)?) => {
70        $(
71            impl $crate::encode::FixedCode for $t {
72                const SIZE: usize = std::mem::size_of::<Self>();
73                type Array = [u8; Self::SIZE];
74                type Error = std::convert::Infallible;
75
76                fn fix_encode(&self) -> Self::Array {
77                    self.to_le_bytes()
78                }
79
80                fn fix_decode(input: &Self::Array) -> Result<Self, Self::Error> {
81                    Ok(Self::from_le_bytes(*input))
82                }
83            }
84        )*
85    };
86}
87
88impl_fixedcode_le_bytes!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, f32, f64);
89
90macro_rules! impl_fixedcode_tuple {
91    () => {
92        impl VarCode for () {
93            fn var_bit_len(&self) -> usize {
94                0
95            }
96
97            fn var_write(&self, _write: &mut WriteBits<impl io::Write>) -> io::Result<()> {
98                Ok(())
99            }
100
101            fn var_read(_read: &mut ReadBits<impl io::Read>) -> io::Result<Self> {
102                Ok(())
103            }
104        }
105    };
106    ($($a:ident)+) => {
107        #[allow(non_snake_case)]
108        impl<$($a),*> VarCode for ($($a,)*)
109        where
110            $($a: VarCode),*
111        {
112            fn var_bit_len(&self) -> usize {
113
114                let ($($a,)*) = self;
115                0 $(+ $a.var_bit_len())*
116            }
117
118            fn var_write(&self, write: &mut WriteBits<impl io::Write>) -> io::Result<()> {
119                let ($($a,)*) = self;
120                $(
121                    $a.var_write(write)?;
122                )*
123                Ok(())
124            }
125
126            fn var_read(read: &mut ReadBits<impl io::Read>) -> io::Result<Self> {
127                Ok((
128                    $(
129                        $a::var_read(read)?,
130                    )*
131                ))
132            }
133        }
134    };
135}
136
137for_tuple!(impl_fixedcode_tuple);
138
139/// A trait for variable-length, bit-level encoding and decoding.
140///
141/// Types implementing `VarCode` can serialize themselves into a variable number of bits
142/// via [`WriteBits`] and deserialize from [`ReadBits`]. A blanket implementation is provided
143/// for all [`FixedCode`] types, writing their fixed-size representation as whole bytes.
144pub trait VarCode {
145    fn var_bit_len(&self) -> usize;
146    fn var_write(&self, write: &mut WriteBits<impl io::Write>) -> io::Result<()>;
147    fn var_read(read: &mut ReadBits<impl io::Read>) -> io::Result<Self>
148    where
149        Self: Sized;
150}
151
152impl<T> VarCode for T
153where
154    T: FixedCode,
155{
156    fn var_bit_len(&self) -> usize {
157        Self::SIZE * 8
158    }
159
160    #[inline]
161    fn var_write(&self, write: &mut WriteBits<impl io::Write>) -> io::Result<()> {
162        FixedCode::fix_write(self, write)
163    }
164
165    #[inline]
166    fn var_read(read: &mut ReadBits<impl io::Read>) -> io::Result<T>
167    where
168        Self: Sized,
169    {
170        FixedCode::fix_read(read)
171    }
172}