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