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