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<Self>;
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 = Self::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
143impl Write for () {
144    #[inline]
145    fn write(&self, _buf: &mut impl BufMut) {}
146}
147
148impl Read for () {
149    type Cfg = ();
150
151    #[inline]
152    fn read_cfg(_buf: &mut impl Buf, _cfg: &Self::Cfg) -> Result<Self, Error> {
153        Ok(())
154    }
155}
156
157impl FixedSize for () {
158    const SIZE: usize = 0;
159}
160
161// Option implementation
162impl<T: Write> Write for Option<T> {
163    #[inline]
164    fn write(&self, buf: &mut impl BufMut) {
165        self.is_some().write(buf);
166        if let Some(inner) = self {
167            inner.write(buf);
168        }
169    }
170}
171
172impl<T: EncodeSize> EncodeSize for Option<T> {
173    #[inline]
174    fn encode_size(&self) -> usize {
175        self.as_ref().map_or(1, |inner| 1 + inner.encode_size())
176    }
177}
178
179impl<T: Read> Read for Option<T> {
180    type Cfg = T::Cfg;
181
182    #[inline]
183    fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
184        if bool::read(buf)? {
185            Ok(Some(T::read_cfg(buf, cfg)?))
186        } else {
187            Ok(None)
188        }
189    }
190}
191
192#[cfg(test)]
193mod tests {
194    use super::*;
195    use crate::{Decode, DecodeExt, Encode, EncodeFixed};
196    use bytes::{Bytes, BytesMut};
197    use paste::paste;
198
199    // Float tests
200    macro_rules! impl_num_test {
201        ($type:ty, $size:expr) => {
202            paste! {
203                #[test]
204                fn [<test_ $type>]() {
205                    let expected_len = core::mem::size_of::<$type>();
206                    let values: [$type; 5] =
207                        [0 as $type, 1 as $type, 42 as $type, <$type>::MAX, <$type>::MIN];
208                    for value in values.iter() {
209                        let encoded = value.encode();
210                        assert_eq!(encoded.len(), expected_len);
211                        let decoded = <$type>::decode(encoded).unwrap();
212                        assert_eq!(*value, decoded);
213                        assert_eq!(value.encode_size(), expected_len);
214
215                        let fixed: [u8; $size] = value.encode_fixed();
216                        assert_eq!(fixed.len(), expected_len);
217                        let decoded = <$type>::decode(Bytes::copy_from_slice(&fixed)).unwrap();
218                        assert_eq!(*value, decoded);
219                    }
220                }
221            }
222        };
223    }
224    impl_num_test!(u8, 1);
225    impl_num_test!(u16, 2);
226    impl_num_test!(u32, 4);
227    impl_num_test!(u64, 8);
228    impl_num_test!(u128, 16);
229    impl_num_test!(i8, 1);
230    impl_num_test!(i16, 2);
231    impl_num_test!(i32, 4);
232    impl_num_test!(i64, 8);
233    impl_num_test!(i128, 16);
234    impl_num_test!(f32, 4);
235    impl_num_test!(f64, 8);
236
237    #[test]
238    fn test_endianness() {
239        // u16
240        let encoded = 0x0102u16.encode();
241        assert_eq!(encoded, Bytes::from_static(&[0x01, 0x02]));
242
243        // u32
244        let encoded = 0x01020304u32.encode();
245        assert_eq!(encoded, Bytes::from_static(&[0x01, 0x02, 0x03, 0x04]));
246
247        // f32
248        let encoded = 1.0f32.encode();
249        assert_eq!(encoded, Bytes::from_static(&[0x3F, 0x80, 0x00, 0x00])); // Big-endian IEEE 754
250    }
251
252    #[test]
253    fn test_bool() {
254        let values = [true, false];
255        for value in values.iter() {
256            let encoded = value.encode();
257            assert_eq!(encoded.len(), 1);
258            let decoded = bool::decode(encoded).unwrap();
259            assert_eq!(*value, decoded);
260            assert_eq!(value.encode_size(), 1);
261        }
262    }
263
264    #[test]
265    fn test_usize() {
266        let values = [0usize, 1, 42, u32::MAX as usize];
267        for value in values.iter() {
268            let encoded = value.encode();
269            assert_eq!(value.encode_size(), UInt(*value as u32).encode_size());
270            let decoded = usize::decode_cfg(encoded, &(..).into()).unwrap();
271            assert_eq!(*value, decoded);
272        }
273    }
274
275    #[cfg(target_pointer_width = "64")]
276    #[test]
277    #[should_panic(expected = "encode_size: usize value is larger than u32")]
278    fn test_usize_encode_panic() {
279        let value: usize = usize::MAX;
280        let _ = value.encode();
281    }
282
283    #[test]
284    #[should_panic(expected = "write: usize value is larger than u32")]
285    fn test_usize_write_panic() {
286        let mut buf = &mut BytesMut::new();
287        let value: usize = usize::MAX;
288        value.write(&mut buf);
289    }
290
291    #[test]
292    fn test_array() {
293        let values = [1u8, 2, 3];
294        let encoded = values.encode();
295        let decoded = <[u8; 3]>::decode(encoded).unwrap();
296        assert_eq!(values, decoded);
297    }
298
299    #[test]
300    fn test_option() {
301        let option_values = [Some(42u32), None];
302        for value in option_values {
303            let encoded = value.encode();
304            let decoded = Option::<u32>::decode(encoded).unwrap();
305            assert_eq!(value, decoded);
306        }
307    }
308
309    #[test]
310    fn test_option_length() {
311        let some = Some(42u32);
312        assert_eq!(some.encode_size(), 1 + 4);
313        assert_eq!(some.encode().len(), 1 + 4);
314        let none: Option<u32> = None;
315        assert_eq!(none.encode_size(), 1);
316        assert_eq!(none.encode().len(), 1);
317    }
318
319    #[test]
320    fn test_unit() {
321        let x = ();
322        // Not using an equality check, since that will always pass.
323        assert!(<()>::decode(x.encode()).is_ok());
324    }
325
326    #[test]
327    fn test_conformity() {
328        // Bool
329        assert_eq!(true.encode(), &[0x01][..]);
330        assert_eq!(false.encode(), &[0x00][..]);
331
332        // 8-bit integers
333        assert_eq!(0u8.encode(), &[0x00][..]);
334        assert_eq!(255u8.encode(), &[0xFF][..]);
335        assert_eq!(0i8.encode(), &[0x00][..]);
336        assert_eq!((-1i8).encode(), &[0xFF][..]);
337        assert_eq!(127i8.encode(), &[0x7F][..]);
338        assert_eq!((-128i8).encode(), &[0x80][..]);
339
340        // 16-bit integers
341        assert_eq!(0u16.encode(), &[0x00, 0x00][..]);
342        assert_eq!(0xABCDu16.encode(), &[0xAB, 0xCD][..]);
343        assert_eq!(u16::MAX.encode(), &[0xFF, 0xFF][..]);
344        assert_eq!(0i16.encode(), &[0x00, 0x00][..]);
345        assert_eq!((-1i16).encode(), &[0xFF, 0xFF][..]);
346        assert_eq!(0x1234i16.encode(), &[0x12, 0x34][..]);
347
348        // 32-bit integers
349        assert_eq!(0u32.encode(), &[0x00, 0x00, 0x00, 0x00][..]);
350        assert_eq!(0xABCDEF01u32.encode(), &[0xAB, 0xCD, 0xEF, 0x01][..]);
351        assert_eq!(u32::MAX.encode(), &[0xFF, 0xFF, 0xFF, 0xFF][..]);
352        assert_eq!(0i32.encode(), &[0x00, 0x00, 0x00, 0x00][..]);
353        assert_eq!((-1i32).encode(), &[0xFF, 0xFF, 0xFF, 0xFF][..]);
354        assert_eq!(0x12345678i32.encode(), &[0x12, 0x34, 0x56, 0x78][..]);
355
356        // 64-bit integers
357        assert_eq!(
358            0u64.encode(),
359            &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
360        );
361        assert_eq!(
362            0x0123456789ABCDEFu64.encode(),
363            &[0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF][..]
364        );
365        assert_eq!(
366            u64::MAX.encode(),
367            &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF][..]
368        );
369        assert_eq!(
370            0i64.encode(),
371            &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
372        );
373        assert_eq!(
374            (-1i64).encode(),
375            &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF][..]
376        );
377
378        // 128-bit integers
379        let u128_val = 0x0123456789ABCDEF0123456789ABCDEFu128;
380        let u128_bytes = [
381            0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB,
382            0xCD, 0xEF,
383        ];
384        assert_eq!(u128_val.encode(), &u128_bytes[..]);
385        assert_eq!(u128::MAX.encode(), &[0xFF; 16][..]);
386        assert_eq!((-1i128).encode(), &[0xFF; 16][..]);
387
388        assert_eq!(0.0f32.encode(), 0.0f32.to_be_bytes()[..]);
389        assert_eq!(1.0f32.encode(), 1.0f32.to_be_bytes()[..]);
390        assert_eq!((-1.0f32).encode(), (-1.0f32).to_be_bytes()[..]);
391        assert_eq!(f32::MAX.encode(), f32::MAX.to_be_bytes()[..]);
392        assert_eq!(f32::MIN.encode(), f32::MIN.to_be_bytes()[..]);
393        assert_eq!(f32::NAN.encode(), f32::NAN.to_be_bytes()[..]);
394        assert_eq!(f32::INFINITY.encode(), f32::INFINITY.to_be_bytes()[..]);
395        assert_eq!(
396            f32::NEG_INFINITY.encode(),
397            f32::NEG_INFINITY.to_be_bytes()[..]
398        );
399
400        // 32-bit floats
401        assert_eq!(1.0f32.encode(), &[0x3F, 0x80, 0x00, 0x00][..]);
402        assert_eq!((-1.0f32).encode(), &[0xBF, 0x80, 0x00, 0x00][..]);
403
404        // 64-bit floats
405        assert_eq!(0.0f64.encode(), 0.0f64.to_be_bytes()[..]);
406        assert_eq!(1.0f64.encode(), 1.0f64.to_be_bytes()[..]);
407        assert_eq!((-1.0f64).encode(), (-1.0f64).to_be_bytes()[..]);
408        assert_eq!(f64::MAX.encode(), f64::MAX.to_be_bytes()[..]);
409        assert_eq!(f64::MIN.encode(), f64::MIN.to_be_bytes()[..]);
410        assert_eq!(f64::NAN.encode(), f64::NAN.to_be_bytes()[..]);
411        assert_eq!(f64::INFINITY.encode(), f64::INFINITY.to_be_bytes()[..]);
412        assert_eq!(
413            f64::NEG_INFINITY.encode(),
414            f64::NEG_INFINITY.to_be_bytes()[..]
415        );
416        assert_eq!(
417            1.0f64.encode(),
418            &[0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
419        );
420        assert_eq!(
421            (-1.0f64).encode(),
422            &[0xBF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
423        );
424
425        // Fixed-size array
426        assert_eq!([1, 2, 3].encode(), &[0x01, 0x02, 0x03][..]);
427        assert_eq!([].encode(), &[][..]);
428
429        // Option
430        assert_eq!(Some(42u32).encode(), &[0x01, 0x00, 0x00, 0x00, 0x2A][..]);
431        assert_eq!(None::<u32>.encode(), &[0][..]);
432
433        // Usize
434        assert_eq!(0usize.encode(), &[0x00][..]);
435        assert_eq!(1usize.encode(), &[0x01][..]);
436        assert_eq!(127usize.encode(), &[0x7F][..]);
437        assert_eq!(128usize.encode(), &[0x80, 0x01][..]);
438        assert_eq!(
439            (u32::MAX as usize).encode(),
440            &[0xFF, 0xFF, 0xFF, 0xFF, 0x0F][..]
441        );
442    }
443
444    #[cfg(feature = "arbitrary")]
445    mod conformance {
446        use crate::conformance::CodecConformance;
447
448        commonware_conformance::conformance_tests! {
449            CodecConformance<u8>,
450            CodecConformance<u16>,
451            CodecConformance<u32>,
452            CodecConformance<u64>,
453            CodecConformance<u128>,
454            CodecConformance<i8>,
455            CodecConformance<i16>,
456            CodecConformance<i32>,
457            CodecConformance<i64>,
458            CodecConformance<i128>,
459            CodecConformance<f32>,
460            CodecConformance<f64>,
461            CodecConformance<bool>,
462            CodecConformance<[u8; 32]>,
463            CodecConformance<Option<u64>>,
464            CodecConformance<()>,
465        }
466    }
467}