commonware_utils/sequence/
fixed_bytes.rs1use 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#[derive(Error, Debug, PartialEq)]
16pub enum Error {
17 #[error("invalid length")]
18 InvalidLength,
19}
20
21#[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 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 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}