Skip to main content

commonware_utils/sequence/
fixed_bytes.rs

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