commonware_codec/types/
primitives.rs

1//! Codec implementations for Rust primitive types.
2//!
3//! # Fixed-size vs Variable-size
4//!
5//! Most primitives therefore have a compile-time constant `SIZE` and can be
6//! encoded/decoded without any configuration.
7//!
8//! `usize` is the lone exception: since most values refer to a length or size
9//! of an object in memory, values are biased towards smaller values. Therefore,
10//! it uses variable-length (varint) encoding to save space.  This means that
11//! it **does not implement [`FixedSize`]**.  When decoding a `usize`, callers
12//! must supply a [`RangeConfig`] to bound the allowable value — this protects
13//! against denial-of-service attacks that would allocate oversized buffers.
14//!
15//! ## Safety & portability
16//! * `usize` is restricted to values that fit in a `u32` to keep the on-wire
17//!   format identical across 32-bit and 64-bit architectures.
18//! * All fixed-size integers and floats are written big-endian to avoid host-
19//!   endian ambiguity.
20
21use crate::{
22    util::at_least, varint::UInt, Config, EncodeSize, Error, FixedSize, RangeConfig, Read, ReadExt,
23    Write,
24};
25use bytes::{Buf, BufMut};
26
27// Numeric types implementation
28macro_rules! impl_numeric {
29    ($type:ty, $read_method:ident, $write_method:ident) => {
30        impl Write for $type {
31            #[inline]
32            fn write(&self, buf: &mut impl BufMut) {
33                buf.$write_method(*self);
34            }
35        }
36
37        impl Read for $type {
38            #[inline]
39            fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
40                at_least(buf, std::mem::size_of::<$type>())?;
41                Ok(buf.$read_method())
42            }
43        }
44
45        impl FixedSize for $type {
46            const SIZE: usize = std::mem::size_of::<$type>();
47        }
48    };
49}
50
51impl_numeric!(u8, get_u8, put_u8);
52impl_numeric!(u16, get_u16, put_u16);
53impl_numeric!(u32, get_u32, put_u32);
54impl_numeric!(u64, get_u64, put_u64);
55impl_numeric!(u128, get_u128, put_u128);
56impl_numeric!(i8, get_i8, put_i8);
57impl_numeric!(i16, get_i16, put_i16);
58impl_numeric!(i32, get_i32, put_i32);
59impl_numeric!(i64, get_i64, put_i64);
60impl_numeric!(i128, get_i128, put_i128);
61impl_numeric!(f32, get_f32, put_f32);
62impl_numeric!(f64, get_f64, put_f64);
63
64// Usize implementation
65impl Write for usize {
66    #[inline]
67    fn write(&self, buf: &mut impl BufMut) {
68        let self_as_u32 = u32::try_from(*self).expect("write: usize value is larger than u32");
69        UInt(self_as_u32).write(buf);
70    }
71}
72
73impl<R: RangeConfig> Read<R> for usize {
74    #[inline]
75    fn read_cfg(buf: &mut impl Buf, range: &R) -> Result<Self, Error> {
76        let self_as_u32: u32 = UInt::read(buf)?.into();
77        let result = usize::try_from(self_as_u32).map_err(|_| Error::InvalidUsize)?;
78        if !range.contains(&result) {
79            return Err(Error::InvalidLength(result));
80        }
81        Ok(result)
82    }
83}
84
85impl EncodeSize for usize {
86    #[inline]
87    fn encode_size(&self) -> usize {
88        let self_as_u32 =
89            u32::try_from(*self).expect("encode_size: usize value is larger than u32");
90        UInt(self_as_u32).encode_size()
91    }
92}
93
94// Bool implementation
95impl Write for bool {
96    #[inline]
97    fn write(&self, buf: &mut impl BufMut) {
98        buf.put_u8(if *self { 1 } else { 0 });
99    }
100}
101
102impl Read for bool {
103    #[inline]
104    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
105        match u8::read(buf)? {
106            0 => Ok(false),
107            1 => Ok(true),
108            _ => Err(Error::InvalidBool),
109        }
110    }
111}
112
113impl FixedSize for bool {
114    const SIZE: usize = 1;
115}
116
117// Constant-size array implementation
118impl<const N: usize> Write for [u8; N] {
119    #[inline]
120    fn write(&self, buf: &mut impl BufMut) {
121        buf.put(&self[..]);
122    }
123}
124
125impl<const N: usize> Read for [u8; N] {
126    #[inline]
127    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
128        at_least(buf, N)?;
129        let mut dst = [0; N];
130        buf.copy_to_slice(&mut dst);
131        Ok(dst)
132    }
133}
134
135impl<const N: usize> FixedSize for [u8; N] {
136    const SIZE: usize = N;
137}
138
139// Option implementation
140impl<T: Write> Write for Option<T> {
141    #[inline]
142    fn write(&self, buf: &mut impl BufMut) {
143        self.is_some().write(buf);
144        if let Some(inner) = self {
145            inner.write(buf);
146        }
147    }
148}
149
150impl<T: EncodeSize> EncodeSize for Option<T> {
151    #[inline]
152    fn encode_size(&self) -> usize {
153        match self {
154            Some(inner) => 1 + inner.encode_size(),
155            None => 1,
156        }
157    }
158}
159
160impl<Cfg: Config, T: Read<Cfg>> Read<Cfg> for Option<T> {
161    #[inline]
162    fn read_cfg(buf: &mut impl Buf, cfg: &Cfg) -> Result<Self, Error> {
163        if bool::read(buf)? {
164            Ok(Some(T::read_cfg(buf, cfg)?))
165        } else {
166            Ok(None)
167        }
168    }
169}
170
171#[cfg(test)]
172mod tests {
173    use super::*;
174    use crate::{Decode, DecodeExt, Encode, EncodeFixed};
175    use bytes::{Bytes, BytesMut};
176    use paste::paste;
177
178    // Float tests
179    macro_rules! impl_num_test {
180        ($type:ty, $size:expr) => {
181            paste! {
182                #[test]
183                fn [<test_ $type>]() {
184                    let expected_len = std::mem::size_of::<$type>();
185                    let values: [$type; 5] =
186                        [0 as $type, 1 as $type, 42 as $type, <$type>::MAX, <$type>::MIN];
187                    for value in values.iter() {
188                        let encoded = value.encode();
189                        assert_eq!(encoded.len(), expected_len);
190                        let decoded = <$type>::decode(encoded).unwrap();
191                        assert_eq!(*value, decoded);
192                        assert_eq!(value.encode_size(), expected_len);
193
194                        let fixed: [u8; $size] = value.encode_fixed();
195                        assert_eq!(fixed.len(), expected_len);
196                        let decoded = <$type>::decode(Bytes::copy_from_slice(&fixed)).unwrap();
197                        assert_eq!(*value, decoded);
198                    }
199                }
200            }
201        };
202    }
203    impl_num_test!(u8, 1);
204    impl_num_test!(u16, 2);
205    impl_num_test!(u32, 4);
206    impl_num_test!(u64, 8);
207    impl_num_test!(u128, 16);
208    impl_num_test!(i8, 1);
209    impl_num_test!(i16, 2);
210    impl_num_test!(i32, 4);
211    impl_num_test!(i64, 8);
212    impl_num_test!(i128, 16);
213    impl_num_test!(f32, 4);
214    impl_num_test!(f64, 8);
215
216    #[test]
217    fn test_endianness() {
218        // u16
219        let encoded = 0x0102u16.encode();
220        assert_eq!(encoded, Bytes::from_static(&[0x01, 0x02]));
221
222        // u32
223        let encoded = 0x01020304u32.encode();
224        assert_eq!(encoded, Bytes::from_static(&[0x01, 0x02, 0x03, 0x04]));
225
226        // f32
227        let encoded = 1.0f32.encode();
228        assert_eq!(encoded, Bytes::from_static(&[0x3F, 0x80, 0x00, 0x00])); // Big-endian IEEE 754
229    }
230
231    #[test]
232    fn test_bool() {
233        let values = [true, false];
234        for value in values.iter() {
235            let encoded = value.encode();
236            assert_eq!(encoded.len(), 1);
237            let decoded = bool::decode(encoded).unwrap();
238            assert_eq!(*value, decoded);
239            assert_eq!(value.encode_size(), 1);
240        }
241    }
242
243    #[test]
244    fn test_usize() {
245        let values = [0usize, 1, 42, u32::MAX as usize];
246        for value in values.iter() {
247            let encoded = value.encode();
248            assert_eq!(value.encode_size(), UInt(*value as u32).encode_size());
249            let decoded = usize::decode_cfg(encoded, &..).unwrap();
250            assert_eq!(*value, decoded);
251        }
252    }
253
254    #[cfg(target_pointer_width = "64")]
255    #[test]
256    #[should_panic(expected = "encode_size: usize value is larger than u32")]
257    fn test_usize_encode_panic() {
258        let value: usize = usize::MAX;
259        let _ = value.encode();
260    }
261
262    #[test]
263    #[should_panic(expected = "write: usize value is larger than u32")]
264    fn test_usize_write_panic() {
265        let mut buf = &mut BytesMut::new();
266        let value: usize = usize::MAX;
267        value.write(&mut buf);
268    }
269
270    #[test]
271    fn test_array() {
272        let values = [1u8, 2, 3];
273        let encoded = values.encode();
274        let decoded = <[u8; 3]>::decode(encoded).unwrap();
275        assert_eq!(values, decoded);
276    }
277
278    #[test]
279    fn test_option() {
280        let option_values = [Some(42u32), None];
281        for value in option_values {
282            let encoded = value.encode();
283            let decoded = Option::<u32>::decode(encoded).unwrap();
284            assert_eq!(value, decoded);
285        }
286    }
287
288    #[test]
289    fn test_option_length() {
290        let some = Some(42u32);
291        assert_eq!(some.encode_size(), 1 + 4);
292        assert_eq!(some.encode().len(), 1 + 4);
293        let none: Option<u32> = None;
294        assert_eq!(none.encode_size(), 1);
295        assert_eq!(none.encode().len(), 1);
296    }
297}