compact_encoding/
fixedwidth.rs

1//! Allow encoding of unsigned ints in a fixed width way, instead of the default variable width.
2//!
3//! Why? Because the default [`CompactEncoding`] implementation for unsigned integers uses a
4//! variable width encoding. However sometimes want them encoded with a fixed width,
5//! [`FixedWidthEncoding`] lets us do this. To fixed width encode an unsigned integrer simply call
6//! [`FixedWidthEncoding::as_fixed_width`] on it. Like this:
7//! ```
8//! # use compact_encoding::EncodingError;
9//! use compact_encoding::{to_encoded_bytes, FixedWidthEncoding};
10//! let buff = to_encoded_bytes!(42u32.as_fixed_width());
11//! assert_eq!(buff, [42, 0, 0, 0].into());
12//! // vs variable width
13//! let buff = to_encoded_bytes!(42u32);
14//! assert_eq!(buff, [42].into());
15//! # Ok::<(), Box<dyn std::error::Error>>(())
16//! ```
17//!
18//! Likewise when decoding decoding from a fixed width encoded buffer you use
19//! [`FixedWidthUint::decode`] which will decode to the underlying unsigned integer type.
20//! So: `FixedWidthUint<u32>::decode(buffer) -> u32`.
21//! Note that we also provide type aliases to make this more ergonomic:
22//! `FixedWidthU64 = FixedWidthUint<u64`.
23//!
24//! ```
25//! # use compact_encoding::EncodingError;
26//! use compact_encoding::{map_decode, FixedWidthU32, FixedWidthUint};
27//! let buff = vec![42, 0, 0, 0]; // 42_u32 fixed width encoded
28//!
29//! let ((decoded,), _) = map_decode!(&buff, [FixedWidthUint<u32>]);
30//! assert_eq!(decoded, 42); // NOT! FixedWidthUint(42_u32)
31//!
32//! assert_eq!(map_decode!(&buff, [FixedWidthU32]).0 .0, 42); // or using the alias
33//! # Ok::<(), Box<dyn std::error::Error>>(())
34//! ```
35
36use crate::{
37    decode_u32, decode_u64, encode_u32, encode_u64, CompactEncoding, EncodingError, U32_SIZE,
38    U64_SIZE,
39};
40
41/// Implents functionality needed to encode unisegned integrer in a fixed width way with
42/// [`CompactEncoding`]
43pub trait FixedWidthEncoding {
44    /// The type we decode to
45    // TODO could we just use T?
46    type Decode;
47    /// The size in bytes required to encode `self`.
48    fn fw_encoded_size(&self) -> Result<usize, EncodingError>;
49
50    /// Encode `self` into `buffer` returning the remainder of `buffer`.
51    fn fw_encode<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], EncodingError>;
52
53    /// Decode a value from the given `buffer` of the type specified by the `Decode` type parameter
54    /// (which defaults to `Self`). Returns the decoded value and remaining undecoded bytes.
55    fn fw_decode(buffer: &[u8]) -> Result<(Self::Decode, &[u8]), EncodingError>
56    where
57        Self: Sized;
58
59    /// Get a uint in a form that encodes to a fixed width
60    fn as_fixed_width(&self) -> FixedWidthUint<Self> {
61        FixedWidthUint(self)
62    }
63}
64
65/// A fixed width encodable unsigned integer
66#[derive(Debug)]
67pub struct FixedWidthUint<'a, T: FixedWidthEncoding + ?Sized>(&'a T);
68
69impl<T: FixedWidthEncoding> CompactEncoding<T::Decode> for FixedWidthUint<'_, T> {
70    fn encoded_size(&self) -> Result<usize, EncodingError> {
71        self.0.fw_encoded_size()
72    }
73
74    fn encode<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], EncodingError> {
75        self.0.fw_encode(buffer)
76    }
77
78    fn decode(buffer: &[u8]) -> Result<(T::Decode, &[u8]), EncodingError>
79    where
80        Self: Sized,
81    {
82        <T as FixedWidthEncoding>::fw_decode(buffer)
83    }
84}
85
86impl FixedWidthEncoding for u32 {
87    type Decode = u32;
88
89    fn fw_encoded_size(&self) -> Result<usize, EncodingError> {
90        Ok(U32_SIZE)
91    }
92
93    fn fw_encode<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], EncodingError> {
94        encode_u32(*self, buffer)
95    }
96
97    fn fw_decode(buffer: &[u8]) -> Result<(Self::Decode, &[u8]), EncodingError>
98    where
99        Self: Sized,
100    {
101        decode_u32(buffer)
102    }
103}
104impl FixedWidthEncoding for u64 {
105    type Decode = u64;
106
107    fn fw_encoded_size(&self) -> Result<usize, EncodingError> {
108        Ok(U64_SIZE)
109    }
110
111    fn fw_encode<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], EncodingError> {
112        encode_u64(*self, buffer)
113    }
114
115    fn fw_decode(buffer: &[u8]) -> Result<(Self::Decode, &[u8]), EncodingError>
116    where
117        Self: Sized,
118    {
119        decode_u64(buffer)
120    }
121}
122
123/// A wrapper around [`u32`] to let us encoded/decode to/from a fixed width
124pub type FixedWidthU32<'a> = FixedWidthUint<'a, u32>;
125/// A wrapper around [`u64`] to let us encoded/decode to/from a fixed width
126pub type FixedWidthU64<'a> = FixedWidthUint<'a, u64>;
127#[cfg(test)]
128mod test {
129    use crate::{map_decode, to_encoded_bytes};
130
131    use super::*;
132
133    #[test]
134    fn fixed_width_u32() -> Result<(), EncodingError> {
135        let x = 42u32;
136        let fixed_buff = to_encoded_bytes!(x.as_fixed_width());
137        let var_buff = to_encoded_bytes!(x);
138        assert_eq!(fixed_buff, [42_u8, 0, 0, 0].into());
139        assert_eq!(var_buff, [42_u8].into());
140
141        let ((fixed_dec,), rest) = map_decode!(&fixed_buff, [FixedWidthU32]);
142        assert!(rest.is_empty());
143        assert_eq!(fixed_dec, x);
144
145        let ((var_dec,), rest) = map_decode!(&var_buff, [u32]);
146        assert!(rest.is_empty());
147        assert_eq!(var_dec, x);
148        Ok(())
149    }
150
151    #[test]
152    fn fixed_width_u64() -> Result<(), EncodingError> {
153        let x = 42u64;
154        let fixed_buff = to_encoded_bytes!(x.as_fixed_width());
155        let var_buff = to_encoded_bytes!(x);
156        assert_eq!(fixed_buff, [42, 0, 0, 0, 0, 0, 0, 0].into());
157        assert_eq!(var_buff, [42].into());
158
159        let ((fixed_dec,), rest) = map_decode!(&fixed_buff, [FixedWidthU64]);
160        assert!(rest.is_empty());
161        assert_eq!(fixed_dec, x);
162        Ok(())
163    }
164}