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 [RangeCfg] 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, EncodeSize, Error, FixedSize, RangeCfg, Read, ReadExt, Write,
23};
24use bytes::{Buf, BufMut};
25
26// Numeric types implementation
27macro_rules! impl_numeric {
28    ($type:ty, $read_method:ident, $write_method:ident) => {
29        impl Write for $type {
30            #[inline]
31            fn write(&self, buf: &mut impl BufMut) {
32                buf.$write_method(*self);
33            }
34        }
35
36        impl Read for $type {
37            type Cfg = ();
38            #[inline]
39            fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
40                at_least(buf, core::mem::size_of::<$type>())?;
41                Ok(buf.$read_method())
42            }
43        }
44
45        impl FixedSize for $type {
46            const SIZE: usize = core::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 Read for usize {
74    type Cfg = RangeCfg;
75
76    #[inline]
77    fn read_cfg(buf: &mut impl Buf, range: &Self::Cfg) -> Result<Self, Error> {
78        let self_as_u32: u32 = UInt::read(buf)?.into();
79        let result = usize::try_from(self_as_u32).map_err(|_| Error::InvalidUsize)?;
80        if !range.contains(&result) {
81            return Err(Error::InvalidLength(result));
82        }
83        Ok(result)
84    }
85}
86
87impl EncodeSize for usize {
88    #[inline]
89    fn encode_size(&self) -> usize {
90        let self_as_u32 =
91            u32::try_from(*self).expect("encode_size: usize value is larger than u32");
92        UInt(self_as_u32).encode_size()
93    }
94}
95
96// Bool implementation
97impl Write for bool {
98    #[inline]
99    fn write(&self, buf: &mut impl BufMut) {
100        buf.put_u8(if *self { 1 } else { 0 });
101    }
102}
103
104impl Read for bool {
105    type Cfg = ();
106    #[inline]
107    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
108        match u8::read(buf)? {
109            0 => Ok(false),
110            1 => Ok(true),
111            _ => Err(Error::InvalidBool),
112        }
113    }
114}
115
116impl FixedSize for bool {
117    const SIZE: usize = 1;
118}
119
120// Constant-size array implementation
121impl<const N: usize> Write for [u8; N] {
122    #[inline]
123    fn write(&self, buf: &mut impl BufMut) {
124        buf.put(&self[..]);
125    }
126}
127
128impl<const N: usize> Read for [u8; N] {
129    type Cfg = ();
130    #[inline]
131    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
132        at_least(buf, N)?;
133        let mut dst = [0; N];
134        buf.copy_to_slice(&mut dst);
135        Ok(dst)
136    }
137}
138
139impl<const N: usize> FixedSize for [u8; N] {
140    const SIZE: usize = N;
141}
142
143// Option implementation
144impl<T: Write> Write for Option<T> {
145    #[inline]
146    fn write(&self, buf: &mut impl BufMut) {
147        self.is_some().write(buf);
148        if let Some(inner) = self {
149            inner.write(buf);
150        }
151    }
152}
153
154impl<T: EncodeSize> EncodeSize for Option<T> {
155    #[inline]
156    fn encode_size(&self) -> usize {
157        match self {
158            Some(inner) => 1 + inner.encode_size(),
159            None => 1,
160        }
161    }
162}
163
164impl<T: Read> Read for Option<T> {
165    type Cfg = T::Cfg;
166
167    #[inline]
168    fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
169        if bool::read(buf)? {
170            Ok(Some(T::read_cfg(buf, cfg)?))
171        } else {
172            Ok(None)
173        }
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180    use crate::{Decode, DecodeExt, Encode, EncodeFixed};
181    use bytes::{Bytes, BytesMut};
182    use paste::paste;
183
184    // Float tests
185    macro_rules! impl_num_test {
186        ($type:ty, $size:expr) => {
187            paste! {
188                #[test]
189                fn [<test_ $type>]() {
190                    let expected_len = core::mem::size_of::<$type>();
191                    let values: [$type; 5] =
192                        [0 as $type, 1 as $type, 42 as $type, <$type>::MAX, <$type>::MIN];
193                    for value in values.iter() {
194                        let encoded = value.encode();
195                        assert_eq!(encoded.len(), expected_len);
196                        let decoded = <$type>::decode(encoded).unwrap();
197                        assert_eq!(*value, decoded);
198                        assert_eq!(value.encode_size(), expected_len);
199
200                        let fixed: [u8; $size] = value.encode_fixed();
201                        assert_eq!(fixed.len(), expected_len);
202                        let decoded = <$type>::decode(Bytes::copy_from_slice(&fixed)).unwrap();
203                        assert_eq!(*value, decoded);
204                    }
205                }
206            }
207        };
208    }
209    impl_num_test!(u8, 1);
210    impl_num_test!(u16, 2);
211    impl_num_test!(u32, 4);
212    impl_num_test!(u64, 8);
213    impl_num_test!(u128, 16);
214    impl_num_test!(i8, 1);
215    impl_num_test!(i16, 2);
216    impl_num_test!(i32, 4);
217    impl_num_test!(i64, 8);
218    impl_num_test!(i128, 16);
219    impl_num_test!(f32, 4);
220    impl_num_test!(f64, 8);
221
222    #[test]
223    fn test_endianness() {
224        // u16
225        let encoded = 0x0102u16.encode();
226        assert_eq!(encoded, Bytes::from_static(&[0x01, 0x02]));
227
228        // u32
229        let encoded = 0x01020304u32.encode();
230        assert_eq!(encoded, Bytes::from_static(&[0x01, 0x02, 0x03, 0x04]));
231
232        // f32
233        let encoded = 1.0f32.encode();
234        assert_eq!(encoded, Bytes::from_static(&[0x3F, 0x80, 0x00, 0x00])); // Big-endian IEEE 754
235    }
236
237    #[test]
238    fn test_bool() {
239        let values = [true, false];
240        for value in values.iter() {
241            let encoded = value.encode();
242            assert_eq!(encoded.len(), 1);
243            let decoded = bool::decode(encoded).unwrap();
244            assert_eq!(*value, decoded);
245            assert_eq!(value.encode_size(), 1);
246        }
247    }
248
249    #[test]
250    fn test_usize() {
251        let values = [0usize, 1, 42, u32::MAX as usize];
252        for value in values.iter() {
253            let encoded = value.encode();
254            assert_eq!(value.encode_size(), UInt(*value as u32).encode_size());
255            let decoded = usize::decode_cfg(encoded, &(..).into()).unwrap();
256            assert_eq!(*value, decoded);
257        }
258    }
259
260    #[cfg(target_pointer_width = "64")]
261    #[test]
262    #[should_panic(expected = "encode_size: usize value is larger than u32")]
263    fn test_usize_encode_panic() {
264        let value: usize = usize::MAX;
265        let _ = value.encode();
266    }
267
268    #[test]
269    #[should_panic(expected = "write: usize value is larger than u32")]
270    fn test_usize_write_panic() {
271        let mut buf = &mut BytesMut::new();
272        let value: usize = usize::MAX;
273        value.write(&mut buf);
274    }
275
276    #[test]
277    fn test_array() {
278        let values = [1u8, 2, 3];
279        let encoded = values.encode();
280        let decoded = <[u8; 3]>::decode(encoded).unwrap();
281        assert_eq!(values, decoded);
282    }
283
284    #[test]
285    fn test_option() {
286        let option_values = [Some(42u32), None];
287        for value in option_values {
288            let encoded = value.encode();
289            let decoded = Option::<u32>::decode(encoded).unwrap();
290            assert_eq!(value, decoded);
291        }
292    }
293
294    #[test]
295    fn test_option_length() {
296        let some = Some(42u32);
297        assert_eq!(some.encode_size(), 1 + 4);
298        assert_eq!(some.encode().len(), 1 + 4);
299        let none: Option<u32> = None;
300        assert_eq!(none.encode_size(), 1);
301        assert_eq!(none.encode().len(), 1);
302    }
303
304    #[test]
305    fn test_conformity() {
306        // Bool
307        assert_eq!(true.encode(), &[0x01][..]);
308        assert_eq!(false.encode(), &[0x00][..]);
309
310        // 8-bit integers
311        assert_eq!(0u8.encode(), &[0x00][..]);
312        assert_eq!(255u8.encode(), &[0xFF][..]);
313        assert_eq!(0i8.encode(), &[0x00][..]);
314        assert_eq!((-1i8).encode(), &[0xFF][..]);
315        assert_eq!(127i8.encode(), &[0x7F][..]);
316        assert_eq!((-128i8).encode(), &[0x80][..]);
317
318        // 16-bit integers
319        assert_eq!(0u16.encode(), &[0x00, 0x00][..]);
320        assert_eq!(0xABCDu16.encode(), &[0xAB, 0xCD][..]);
321        assert_eq!(u16::MAX.encode(), &[0xFF, 0xFF][..]);
322        assert_eq!(0i16.encode(), &[0x00, 0x00][..]);
323        assert_eq!((-1i16).encode(), &[0xFF, 0xFF][..]);
324        assert_eq!(0x1234i16.encode(), &[0x12, 0x34][..]);
325
326        // 32-bit integers
327        assert_eq!(0u32.encode(), &[0x00, 0x00, 0x00, 0x00][..]);
328        assert_eq!(0xABCDEF01u32.encode(), &[0xAB, 0xCD, 0xEF, 0x01][..]);
329        assert_eq!(u32::MAX.encode(), &[0xFF, 0xFF, 0xFF, 0xFF][..]);
330        assert_eq!(0i32.encode(), &[0x00, 0x00, 0x00, 0x00][..]);
331        assert_eq!((-1i32).encode(), &[0xFF, 0xFF, 0xFF, 0xFF][..]);
332        assert_eq!(0x12345678i32.encode(), &[0x12, 0x34, 0x56, 0x78][..]);
333
334        // 64-bit integers
335        assert_eq!(
336            0u64.encode(),
337            &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
338        );
339        assert_eq!(
340            0x0123456789ABCDEFu64.encode(),
341            &[0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF][..]
342        );
343        assert_eq!(
344            u64::MAX.encode(),
345            &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF][..]
346        );
347        assert_eq!(
348            0i64.encode(),
349            &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
350        );
351        assert_eq!(
352            (-1i64).encode(),
353            &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF][..]
354        );
355
356        // 128-bit integers
357        let u128_val = 0x0123456789ABCDEF0123456789ABCDEFu128;
358        let u128_bytes = [
359            0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB,
360            0xCD, 0xEF,
361        ];
362        assert_eq!(u128_val.encode(), &u128_bytes[..]);
363        assert_eq!(u128::MAX.encode(), &[0xFF; 16][..]);
364        assert_eq!((-1i128).encode(), &[0xFF; 16][..]);
365
366        assert_eq!(0.0f32.encode(), 0.0f32.to_be_bytes()[..]);
367        assert_eq!(1.0f32.encode(), 1.0f32.to_be_bytes()[..]);
368        assert_eq!((-1.0f32).encode(), (-1.0f32).to_be_bytes()[..]);
369        assert_eq!(f32::MAX.encode(), f32::MAX.to_be_bytes()[..]);
370        assert_eq!(f32::MIN.encode(), f32::MIN.to_be_bytes()[..]);
371        assert_eq!(f32::NAN.encode(), f32::NAN.to_be_bytes()[..]);
372        assert_eq!(f32::INFINITY.encode(), f32::INFINITY.to_be_bytes()[..]);
373        assert_eq!(
374            f32::NEG_INFINITY.encode(),
375            f32::NEG_INFINITY.to_be_bytes()[..]
376        );
377
378        // 32-bit floats
379        assert_eq!(1.0f32.encode(), &[0x3F, 0x80, 0x00, 0x00][..]);
380        assert_eq!((-1.0f32).encode(), &[0xBF, 0x80, 0x00, 0x00][..]);
381
382        // 64-bit floats
383        assert_eq!(0.0f64.encode(), 0.0f64.to_be_bytes()[..]);
384        assert_eq!(1.0f64.encode(), 1.0f64.to_be_bytes()[..]);
385        assert_eq!((-1.0f64).encode(), (-1.0f64).to_be_bytes()[..]);
386        assert_eq!(f64::MAX.encode(), f64::MAX.to_be_bytes()[..]);
387        assert_eq!(f64::MIN.encode(), f64::MIN.to_be_bytes()[..]);
388        assert_eq!(f64::NAN.encode(), f64::NAN.to_be_bytes()[..]);
389        assert_eq!(f64::INFINITY.encode(), f64::INFINITY.to_be_bytes()[..]);
390        assert_eq!(
391            f64::NEG_INFINITY.encode(),
392            f64::NEG_INFINITY.to_be_bytes()[..]
393        );
394        assert_eq!(
395            1.0f64.encode(),
396            &[0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
397        );
398        assert_eq!(
399            (-1.0f64).encode(),
400            &[0xBF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
401        );
402
403        // Fixed-size array
404        assert_eq!([1, 2, 3].encode(), &[0x01, 0x02, 0x03][..]);
405        assert_eq!([].encode(), &[][..]);
406
407        // Option
408        assert_eq!(Some(42u32).encode(), &[0x01, 0x00, 0x00, 0x00, 0x2A][..]);
409        assert_eq!(None::<u32>.encode(), &[0][..]);
410
411        // Usize
412        assert_eq!(0usize.encode(), &[0x00][..]);
413        assert_eq!(1usize.encode(), &[0x01][..]);
414        assert_eq!(127usize.encode(), &[0x7F][..]);
415        assert_eq!(128usize.encode(), &[0x80, 0x01][..]);
416        assert_eq!(
417            (u32::MAX as usize).encode(),
418            &[0xFF, 0xFF, 0xFF, 0xFF, 0x0F][..]
419        );
420    }
421}