commonware_utils/sequence/
fixed_bytes.rs

1use crate::{hex, Array, Span};
2use bytes::{Buf, BufMut};
3use commonware_codec::{Error as CodecError, FixedSize, Read, ReadExt, Write};
4use core::{
5    cmp::{Ord, PartialOrd},
6    fmt::{Debug, Display},
7    hash::Hash,
8    ops::Deref,
9};
10use thiserror::Error;
11
12/// Errors returned by `Bytes` functions.
13#[derive(Error, Debug, PartialEq)]
14pub enum Error {
15    #[error("invalid length")]
16    InvalidLength,
17}
18
19/// An `Array` implementation for fixed-length byte arrays.
20#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
21#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
22#[repr(transparent)]
23pub struct FixedBytes<const N: usize>([u8; N]);
24
25impl<const N: usize> FixedBytes<N> {
26    /// Creates a new `FixedBytes` instance from an array of length `N`.
27    pub const fn new(value: [u8; N]) -> Self {
28        Self(value)
29    }
30}
31
32impl<const N: usize> Write for FixedBytes<N> {
33    fn write(&self, buf: &mut impl BufMut) {
34        self.0.write(buf);
35    }
36}
37
38impl<const N: usize> Read for FixedBytes<N> {
39    type Cfg = ();
40
41    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
42        Ok(Self(<[u8; N]>::read(buf)?))
43    }
44}
45
46impl<const N: usize> FixedSize for FixedBytes<N> {
47    const SIZE: usize = N;
48}
49
50impl<const N: usize> Span for FixedBytes<N> {}
51
52impl<const N: usize> Array for FixedBytes<N> {}
53
54impl<const N: usize> AsRef<[u8]> for FixedBytes<N> {
55    fn as_ref(&self) -> &[u8] {
56        &self.0
57    }
58}
59
60impl<const N: usize> Deref for FixedBytes<N> {
61    type Target = [u8];
62    fn deref(&self) -> &[u8] {
63        &self.0
64    }
65}
66
67impl<const N: usize> Display for FixedBytes<N> {
68    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
69        write!(f, "{}", hex(&self.0))
70    }
71}
72
73impl<const N: usize> From<[u8; N]> for FixedBytes<N> {
74    fn from(value: [u8; N]) -> Self {
75        Self::new(value)
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82    use crate::fixed_bytes;
83    use bytes::{Buf, BytesMut};
84    use commonware_codec::{DecodeExt, Encode};
85
86    #[test]
87    fn test_codec() {
88        let original = FixedBytes::new([1, 2, 3, 4]);
89        let encoded = original.encode();
90        assert_eq!(encoded.len(), original.len());
91        let decoded = FixedBytes::decode(encoded).unwrap();
92        assert_eq!(original, decoded);
93    }
94
95    #[test]
96    fn test_bytes_creation_and_conversion() {
97        let value = [1, 2, 3, 4];
98        let bytes = FixedBytes::new(value);
99        assert_eq!(bytes.as_ref(), &value);
100
101        let bytes_into = value.into();
102        assert_eq!(bytes, bytes_into);
103
104        let slice = [1, 2, 3, 4];
105        let bytes_from_slice = FixedBytes::decode(slice.as_ref()).unwrap();
106        assert_eq!(bytes_from_slice, bytes);
107
108        let vec = vec![1, 2, 3, 4];
109        let bytes_from_vec = FixedBytes::decode(vec.as_ref()).unwrap();
110        assert_eq!(bytes_from_vec, bytes);
111
112        // Test with incorrect length
113        let slice_too_short = [1, 2, 3];
114        assert!(matches!(
115            FixedBytes::<4>::decode(slice_too_short.as_ref()),
116            Err(CodecError::EndOfBuffer)
117        ));
118
119        let vec_too_long = vec![1, 2, 3, 4, 5];
120        assert!(matches!(
121            FixedBytes::<4>::decode(vec_too_long.as_ref()),
122            Err(CodecError::ExtraData(1))
123        ));
124    }
125
126    #[test]
127    fn test_read() {
128        let mut buf = BytesMut::from(&[1, 2, 3, 4][..]);
129        let bytes = FixedBytes::<4>::read(&mut buf).unwrap();
130        assert_eq!(bytes.as_ref(), &[1, 2, 3, 4]);
131        assert_eq!(buf.remaining(), 0);
132
133        let mut buf = BytesMut::from(&[1, 2, 3][..]);
134        let result = FixedBytes::<4>::read(&mut buf);
135        assert!(matches!(result, Err(CodecError::EndOfBuffer)));
136
137        let mut buf = BytesMut::from(&[1, 2, 3, 4, 5][..]);
138        let bytes = FixedBytes::<4>::read(&mut buf).unwrap();
139        assert_eq!(bytes.as_ref(), &[1, 2, 3, 4]);
140        assert_eq!(buf.remaining(), 1);
141        assert_eq!(buf[0], 5);
142    }
143
144    #[test]
145    fn test_display() {
146        let bytes = fixed_bytes!("0x01020304");
147        assert_eq!(format!("{bytes}"), "01020304");
148    }
149
150    #[test]
151    fn test_ord_and_eq() {
152        let a = FixedBytes::new([1, 2, 3, 4]);
153        let b = FixedBytes::new([1, 2, 3, 5]);
154        assert!(a < b);
155        assert_ne!(a, b);
156
157        let c = FixedBytes::new([1, 2, 3, 4]);
158        assert_eq!(a, c);
159    }
160
161    #[cfg(feature = "arbitrary")]
162    mod conformance {
163        use super::*;
164        use commonware_codec::conformance::CodecConformance;
165
166        commonware_conformance::conformance_tests! {
167            CodecConformance<FixedBytes<16>>,
168        }
169    }
170}