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