Skip to main content

commonware_utils/sequence/
fixed_bytes.rs

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