Skip to main content

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};
25use core::num::{NonZeroU16, NonZeroU32, NonZeroU64};
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            type Cfg = ();
39            #[inline]
40            fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
41                at_least(buf, core::mem::size_of::<$type>())?;
42                Ok(buf.$read_method())
43            }
44        }
45
46        impl FixedSize for $type {
47            const SIZE: usize = core::mem::size_of::<$type>();
48        }
49    };
50}
51
52impl_numeric!(u8, get_u8, put_u8);
53impl_numeric!(u16, get_u16, put_u16);
54impl_numeric!(u32, get_u32, put_u32);
55impl_numeric!(u64, get_u64, put_u64);
56impl_numeric!(u128, get_u128, put_u128);
57impl_numeric!(i8, get_i8, put_i8);
58impl_numeric!(i16, get_i16, put_i16);
59impl_numeric!(i32, get_i32, put_i32);
60impl_numeric!(i64, get_i64, put_i64);
61impl_numeric!(i128, get_i128, put_i128);
62impl_numeric!(f32, get_f32, put_f32);
63impl_numeric!(f64, get_f64, put_f64);
64
65macro_rules! impl_nonzero {
66    ($nz:ty, $inner:ty, $name:expr) => {
67        impl Write for $nz {
68            #[inline]
69            fn write(&self, buf: &mut impl BufMut) {
70                self.get().write(buf);
71            }
72        }
73
74        impl Read for $nz {
75            type Cfg = ();
76            #[inline]
77            fn read_cfg(buf: &mut impl Buf, cfg: &()) -> Result<Self, Error> {
78                let v = <$inner>::read_cfg(buf, cfg)?;
79                <$nz>::new(v).ok_or(Error::Invalid($name, "value must not be zero"))
80            }
81        }
82
83        impl FixedSize for $nz {
84            const SIZE: usize = <$inner as FixedSize>::SIZE;
85        }
86    };
87}
88
89impl_nonzero!(NonZeroU16, u16, "NonZeroU16");
90impl_nonzero!(NonZeroU32, u32, "NonZeroU32");
91impl_nonzero!(NonZeroU64, u64, "NonZeroU64");
92
93// Usize implementation
94impl Write for usize {
95    #[inline]
96    fn write(&self, buf: &mut impl BufMut) {
97        let self_as_u32 = u32::try_from(*self).expect("write: usize value is larger than u32");
98        UInt(self_as_u32).write(buf);
99    }
100}
101
102impl Read for usize {
103    type Cfg = RangeCfg<Self>;
104
105    #[inline]
106    fn read_cfg(buf: &mut impl Buf, range: &Self::Cfg) -> Result<Self, Error> {
107        let self_as_u32: u32 = UInt::read(buf)?.into();
108        let result = Self::try_from(self_as_u32).map_err(|_| Error::InvalidUsize)?;
109        if !range.contains(&result) {
110            return Err(Error::InvalidLength(result));
111        }
112        Ok(result)
113    }
114}
115
116impl EncodeSize for usize {
117    #[inline]
118    fn encode_size(&self) -> usize {
119        let self_as_u32 =
120            u32::try_from(*self).expect("encode_size: usize value is larger than u32");
121        UInt(self_as_u32).encode_size()
122    }
123}
124
125// Bool implementation
126impl Write for bool {
127    #[inline]
128    fn write(&self, buf: &mut impl BufMut) {
129        buf.put_u8(if *self { 1 } else { 0 });
130    }
131}
132
133impl Read for bool {
134    type Cfg = ();
135    #[inline]
136    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
137        match u8::read(buf)? {
138            0 => Ok(false),
139            1 => Ok(true),
140            _ => Err(Error::InvalidBool),
141        }
142    }
143}
144
145impl FixedSize for bool {
146    const SIZE: usize = 1;
147}
148
149// Constant-size array implementation
150impl<const N: usize> Write for [u8; N] {
151    #[inline]
152    fn write(&self, buf: &mut impl BufMut) {
153        buf.put(&self[..]);
154    }
155}
156
157impl<const N: usize> Read for [u8; N] {
158    type Cfg = ();
159    #[inline]
160    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
161        at_least(buf, N)?;
162        let mut dst = [0; N];
163        buf.copy_to_slice(&mut dst);
164        Ok(dst)
165    }
166}
167
168impl<const N: usize> FixedSize for [u8; N] {
169    const SIZE: usize = N;
170}
171
172impl Write for () {
173    #[inline]
174    fn write(&self, _buf: &mut impl BufMut) {}
175}
176
177impl Read for () {
178    type Cfg = ();
179
180    #[inline]
181    fn read_cfg(_buf: &mut impl Buf, _cfg: &Self::Cfg) -> Result<Self, Error> {
182        Ok(())
183    }
184}
185
186impl FixedSize for () {
187    const SIZE: usize = 0;
188}
189
190// Option implementation
191impl<T: Write> Write for Option<T> {
192    #[inline]
193    fn write(&self, buf: &mut impl BufMut) {
194        self.is_some().write(buf);
195        if let Some(inner) = self {
196            inner.write(buf);
197        }
198    }
199}
200
201impl<T: EncodeSize> EncodeSize for Option<T> {
202    #[inline]
203    fn encode_size(&self) -> usize {
204        self.as_ref().map_or(1, |inner| 1 + inner.encode_size())
205    }
206}
207
208impl<T: Read> Read for Option<T> {
209    type Cfg = T::Cfg;
210
211    #[inline]
212    fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, Error> {
213        if bool::read(buf)? {
214            Ok(Some(T::read_cfg(buf, cfg)?))
215        } else {
216            Ok(None)
217        }
218    }
219}
220
221#[cfg(test)]
222mod tests {
223    use super::*;
224    use crate::{Decode, DecodeExt, Encode, EncodeFixed};
225    use bytes::{Bytes, BytesMut};
226    use paste::paste;
227
228    // Float tests
229    macro_rules! impl_num_test {
230        ($type:ty, $size:expr) => {
231            paste! {
232                #[test]
233                fn [<test_ $type>]() {
234                    let expected_len = core::mem::size_of::<$type>();
235                    let values: [$type; 5] =
236                        [0 as $type, 1 as $type, 42 as $type, <$type>::MAX, <$type>::MIN];
237                    for value in values.iter() {
238                        let encoded = value.encode();
239                        assert_eq!(encoded.len(), expected_len);
240                        let decoded = <$type>::decode(encoded).unwrap();
241                        assert_eq!(*value, decoded);
242                        assert_eq!(value.encode_size(), expected_len);
243
244                        let fixed: [u8; $size] = value.encode_fixed();
245                        assert_eq!(fixed.len(), expected_len);
246                        let decoded = <$type>::decode(Bytes::copy_from_slice(&fixed)).unwrap();
247                        assert_eq!(*value, decoded);
248                    }
249                }
250            }
251        };
252    }
253    impl_num_test!(u8, 1);
254    impl_num_test!(u16, 2);
255    impl_num_test!(u32, 4);
256    impl_num_test!(u64, 8);
257    impl_num_test!(u128, 16);
258    impl_num_test!(i8, 1);
259    impl_num_test!(i16, 2);
260    impl_num_test!(i32, 4);
261    impl_num_test!(i64, 8);
262    impl_num_test!(i128, 16);
263    impl_num_test!(f32, 4);
264    impl_num_test!(f64, 8);
265
266    #[test]
267    fn test_endianness() {
268        // u16
269        let encoded = 0x0102u16.encode();
270        assert_eq!(encoded, Bytes::from_static(&[0x01, 0x02]));
271
272        // u32
273        let encoded = 0x01020304u32.encode();
274        assert_eq!(encoded, Bytes::from_static(&[0x01, 0x02, 0x03, 0x04]));
275
276        // f32
277        let encoded = 1.0f32.encode();
278        assert_eq!(encoded, Bytes::from_static(&[0x3F, 0x80, 0x00, 0x00])); // Big-endian IEEE 754
279    }
280
281    #[test]
282    fn test_bool() {
283        let values = [true, false];
284        for value in values.iter() {
285            let encoded = value.encode();
286            assert_eq!(encoded.len(), 1);
287            let decoded = bool::decode(encoded).unwrap();
288            assert_eq!(*value, decoded);
289            assert_eq!(value.encode_size(), 1);
290        }
291    }
292
293    #[test]
294    fn test_usize() {
295        let values = [0usize, 1, 42, u32::MAX as usize];
296        for value in values.iter() {
297            let encoded = value.encode();
298            assert_eq!(value.encode_size(), UInt(*value as u32).encode_size());
299            let decoded = usize::decode_cfg(encoded, &(..).into()).unwrap();
300            assert_eq!(*value, decoded);
301        }
302    }
303
304    #[cfg(target_pointer_width = "64")]
305    #[test]
306    #[should_panic(expected = "encode_size: usize value is larger than u32")]
307    fn test_usize_encode_panic() {
308        let value: usize = usize::MAX;
309        let _ = value.encode();
310    }
311
312    #[test]
313    #[should_panic(expected = "write: usize value is larger than u32")]
314    fn test_usize_write_panic() {
315        let mut buf = &mut BytesMut::new();
316        let value: usize = usize::MAX;
317        value.write(&mut buf);
318    }
319
320    #[test]
321    fn test_array() {
322        let values = [1u8, 2, 3];
323        let encoded = values.encode();
324        let decoded = <[u8; 3]>::decode(encoded).unwrap();
325        assert_eq!(values, decoded);
326    }
327
328    #[test]
329    fn test_option() {
330        let option_values = [Some(42u32), None];
331        for value in option_values {
332            let encoded = value.encode();
333            let decoded = Option::<u32>::decode(encoded).unwrap();
334            assert_eq!(value, decoded);
335        }
336    }
337
338    #[test]
339    fn test_option_length() {
340        let some = Some(42u32);
341        assert_eq!(some.encode_size(), 1 + 4);
342        assert_eq!(some.encode().len(), 1 + 4);
343        let none: Option<u32> = None;
344        assert_eq!(none.encode_size(), 1);
345        assert_eq!(none.encode().len(), 1);
346    }
347
348    #[test]
349    fn test_unit() {
350        let x = ();
351        // Not using an equality check, since that will always pass.
352        assert!(<()>::decode(x.encode()).is_ok());
353    }
354
355    #[test]
356    fn test_nonzero_u16() {
357        let values = [
358            NonZeroU16::new(1).unwrap(),
359            NonZeroU16::new(42).unwrap(),
360            NonZeroU16::new(u16::MAX).unwrap(),
361        ];
362        for value in values {
363            let encoded = value.encode();
364            assert_eq!(encoded.len(), 2);
365            let decoded = NonZeroU16::decode(encoded).unwrap();
366            assert_eq!(value, decoded);
367        }
368        assert!(NonZeroU16::decode(0u16.encode()).is_err());
369    }
370
371    #[test]
372    fn test_nonzero_u32() {
373        let values = [
374            NonZeroU32::new(1).unwrap(),
375            NonZeroU32::new(u32::MAX).unwrap(),
376        ];
377        for value in values {
378            let encoded = value.encode();
379            assert_eq!(encoded.len(), 4);
380            let decoded = NonZeroU32::decode(encoded).unwrap();
381            assert_eq!(value, decoded);
382        }
383        assert!(NonZeroU32::decode(0u32.encode()).is_err());
384    }
385
386    #[test]
387    fn test_nonzero_u64() {
388        let values = [
389            NonZeroU64::new(1).unwrap(),
390            NonZeroU64::new(u64::MAX).unwrap(),
391        ];
392        for value in values {
393            let encoded = value.encode();
394            assert_eq!(encoded.len(), 8);
395            let decoded = NonZeroU64::decode(encoded).unwrap();
396            assert_eq!(value, decoded);
397        }
398        assert!(NonZeroU64::decode(0u64.encode()).is_err());
399    }
400
401    #[test]
402    fn test_conformity() {
403        // Bool
404        assert_eq!(true.encode(), &[0x01][..]);
405        assert_eq!(false.encode(), &[0x00][..]);
406
407        // 8-bit integers
408        assert_eq!(0u8.encode(), &[0x00][..]);
409        assert_eq!(255u8.encode(), &[0xFF][..]);
410        assert_eq!(0i8.encode(), &[0x00][..]);
411        assert_eq!((-1i8).encode(), &[0xFF][..]);
412        assert_eq!(127i8.encode(), &[0x7F][..]);
413        assert_eq!((-128i8).encode(), &[0x80][..]);
414
415        // 16-bit integers
416        assert_eq!(0u16.encode(), &[0x00, 0x00][..]);
417        assert_eq!(0xABCDu16.encode(), &[0xAB, 0xCD][..]);
418        assert_eq!(u16::MAX.encode(), &[0xFF, 0xFF][..]);
419        assert_eq!(0i16.encode(), &[0x00, 0x00][..]);
420        assert_eq!((-1i16).encode(), &[0xFF, 0xFF][..]);
421        assert_eq!(0x1234i16.encode(), &[0x12, 0x34][..]);
422
423        // 32-bit integers
424        assert_eq!(0u32.encode(), &[0x00, 0x00, 0x00, 0x00][..]);
425        assert_eq!(0xABCDEF01u32.encode(), &[0xAB, 0xCD, 0xEF, 0x01][..]);
426        assert_eq!(u32::MAX.encode(), &[0xFF, 0xFF, 0xFF, 0xFF][..]);
427        assert_eq!(0i32.encode(), &[0x00, 0x00, 0x00, 0x00][..]);
428        assert_eq!((-1i32).encode(), &[0xFF, 0xFF, 0xFF, 0xFF][..]);
429        assert_eq!(0x12345678i32.encode(), &[0x12, 0x34, 0x56, 0x78][..]);
430
431        // 64-bit integers
432        assert_eq!(
433            0u64.encode(),
434            &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
435        );
436        assert_eq!(
437            0x0123456789ABCDEFu64.encode(),
438            &[0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF][..]
439        );
440        assert_eq!(
441            u64::MAX.encode(),
442            &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF][..]
443        );
444        assert_eq!(
445            0i64.encode(),
446            &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
447        );
448        assert_eq!(
449            (-1i64).encode(),
450            &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF][..]
451        );
452
453        // 128-bit integers
454        let u128_val = 0x0123456789ABCDEF0123456789ABCDEFu128;
455        let u128_bytes = [
456            0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB,
457            0xCD, 0xEF,
458        ];
459        assert_eq!(u128_val.encode(), &u128_bytes[..]);
460        assert_eq!(u128::MAX.encode(), &[0xFF; 16][..]);
461        assert_eq!((-1i128).encode(), &[0xFF; 16][..]);
462
463        assert_eq!(0.0f32.encode(), 0.0f32.to_be_bytes()[..]);
464        assert_eq!(1.0f32.encode(), 1.0f32.to_be_bytes()[..]);
465        assert_eq!((-1.0f32).encode(), (-1.0f32).to_be_bytes()[..]);
466        assert_eq!(f32::MAX.encode(), f32::MAX.to_be_bytes()[..]);
467        assert_eq!(f32::MIN.encode(), f32::MIN.to_be_bytes()[..]);
468        assert_eq!(f32::NAN.encode(), f32::NAN.to_be_bytes()[..]);
469        assert_eq!(f32::INFINITY.encode(), f32::INFINITY.to_be_bytes()[..]);
470        assert_eq!(
471            f32::NEG_INFINITY.encode(),
472            f32::NEG_INFINITY.to_be_bytes()[..]
473        );
474
475        // 32-bit floats
476        assert_eq!(1.0f32.encode(), &[0x3F, 0x80, 0x00, 0x00][..]);
477        assert_eq!((-1.0f32).encode(), &[0xBF, 0x80, 0x00, 0x00][..]);
478
479        // 64-bit floats
480        assert_eq!(0.0f64.encode(), 0.0f64.to_be_bytes()[..]);
481        assert_eq!(1.0f64.encode(), 1.0f64.to_be_bytes()[..]);
482        assert_eq!((-1.0f64).encode(), (-1.0f64).to_be_bytes()[..]);
483        assert_eq!(f64::MAX.encode(), f64::MAX.to_be_bytes()[..]);
484        assert_eq!(f64::MIN.encode(), f64::MIN.to_be_bytes()[..]);
485        assert_eq!(f64::NAN.encode(), f64::NAN.to_be_bytes()[..]);
486        assert_eq!(f64::INFINITY.encode(), f64::INFINITY.to_be_bytes()[..]);
487        assert_eq!(
488            f64::NEG_INFINITY.encode(),
489            f64::NEG_INFINITY.to_be_bytes()[..]
490        );
491        assert_eq!(
492            1.0f64.encode(),
493            &[0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
494        );
495        assert_eq!(
496            (-1.0f64).encode(),
497            &[0xBF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]
498        );
499
500        // Fixed-size array
501        assert_eq!([1, 2, 3].encode(), &[0x01, 0x02, 0x03][..]);
502        assert_eq!([].encode(), &[][..]);
503
504        // Option
505        assert_eq!(Some(42u32).encode(), &[0x01, 0x00, 0x00, 0x00, 0x2A][..]);
506        assert_eq!(None::<u32>.encode(), &[0][..]);
507
508        // Usize
509        assert_eq!(0usize.encode(), &[0x00][..]);
510        assert_eq!(1usize.encode(), &[0x01][..]);
511        assert_eq!(127usize.encode(), &[0x7F][..]);
512        assert_eq!(128usize.encode(), &[0x80, 0x01][..]);
513        assert_eq!(
514            (u32::MAX as usize).encode(),
515            &[0xFF, 0xFF, 0xFF, 0xFF, 0x0F][..]
516        );
517    }
518
519    #[cfg(feature = "arbitrary")]
520    mod conformance {
521        use crate::conformance::CodecConformance;
522        use core::num::{NonZeroU16, NonZeroU32, NonZeroU64};
523
524        commonware_conformance::conformance_tests! {
525            CodecConformance<u8>,
526            CodecConformance<u16>,
527            CodecConformance<u32>,
528            CodecConformance<u64>,
529            CodecConformance<u128>,
530            CodecConformance<i8>,
531            CodecConformance<i16>,
532            CodecConformance<i32>,
533            CodecConformance<i64>,
534            CodecConformance<i128>,
535            CodecConformance<f32>,
536            CodecConformance<f64>,
537            CodecConformance<bool>,
538            CodecConformance<[u8; 32]>,
539            CodecConformance<Option<u64>>,
540            CodecConformance<()>,
541            CodecConformance<NonZeroU16>,
542            CodecConformance<NonZeroU32>,
543            CodecConformance<NonZeroU64>,
544        }
545    }
546}