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