use crate::{hex, Array, Span}; use bytes::{Buf, BufMut}; use commonware_codec::{Error as CodecError, FixedSize, Read, ReadExt, Write}; use core::{ cmp::{Ord, PartialOrd}, fmt::{Debug, Display}, hash::Hash, ops::Deref, }; use thiserror::Error; /// Errors returned by `Bytes` functions. #[derive(Error, Debug, PartialEq)] pub enum Error { #[error("invalid length")] InvalidLength, } /// An `Array` implementation for fixed-length byte arrays. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[repr(transparent)] pub struct FixedBytes([u8; N]); impl FixedBytes { /// Creates a new `FixedBytes` instance from an array of length `N`. pub const fn new(value: [u8; N]) -> Self { Self(value) } } impl Write for FixedBytes { fn write(&self, buf: &mut impl BufMut) { self.0.write(buf); } } impl Read for FixedBytes { type Cfg = (); fn read_cfg(buf: &mut impl Buf, _: &()) -> Result { Ok(Self(<[u8; N]>::read(buf)?)) } } impl FixedSize for FixedBytes { const SIZE: usize = N; } impl Span for FixedBytes {} impl Array for FixedBytes {} impl AsRef<[u8]> for FixedBytes { fn as_ref(&self) -> &[u8] { &self.0 } } impl Deref for FixedBytes { type Target = [u8]; fn deref(&self) -> &[u8] { &self.0 } } impl Display for FixedBytes { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{}", hex(&self.0)) } } impl From<[u8; N]> for FixedBytes { fn from(value: [u8; N]) -> Self { Self::new(value) } } #[cfg(test)] mod tests { use super::*; use crate::fixed_bytes; use bytes::{Buf, BytesMut}; use commonware_codec::{DecodeExt, Encode}; #[test] fn test_codec() { let original = FixedBytes::new([1, 2, 3, 4]); let encoded = original.encode(); assert_eq!(encoded.len(), original.len()); let decoded = FixedBytes::decode(encoded).unwrap(); assert_eq!(original, decoded); } #[test] fn test_bytes_creation_and_conversion() { let value = [1, 2, 3, 4]; let bytes = FixedBytes::new(value); assert_eq!(bytes.as_ref(), &value); let bytes_into = value.into(); assert_eq!(bytes, bytes_into); let slice = [1, 2, 3, 4]; let bytes_from_slice = FixedBytes::decode(slice.as_ref()).unwrap(); assert_eq!(bytes_from_slice, bytes); let vec = vec![1, 2, 3, 4]; let bytes_from_vec = FixedBytes::decode(vec.as_ref()).unwrap(); assert_eq!(bytes_from_vec, bytes); // Test with incorrect length let slice_too_short = [1, 2, 3]; assert!(matches!( FixedBytes::<4>::decode(slice_too_short.as_ref()), Err(CodecError::EndOfBuffer) )); let vec_too_long = vec![1, 2, 3, 4, 5]; assert!(matches!( FixedBytes::<4>::decode(vec_too_long.as_ref()), Err(CodecError::ExtraData(1)) )); } #[test] fn test_read() { let mut buf = BytesMut::from(&[1, 2, 3, 4][..]); let bytes = FixedBytes::<4>::read(&mut buf).unwrap(); assert_eq!(bytes.as_ref(), &[1, 2, 3, 4]); assert_eq!(buf.remaining(), 0); let mut buf = BytesMut::from(&[1, 2, 3][..]); let result = FixedBytes::<4>::read(&mut buf); assert!(matches!(result, Err(CodecError::EndOfBuffer))); let mut buf = BytesMut::from(&[1, 2, 3, 4, 5][..]); let bytes = FixedBytes::<4>::read(&mut buf).unwrap(); assert_eq!(bytes.as_ref(), &[1, 2, 3, 4]); assert_eq!(buf.remaining(), 1); assert_eq!(buf[0], 5); } #[test] fn test_display() { let bytes = fixed_bytes!("0x01020304"); assert_eq!(format!("{bytes}"), "01020304"); } #[test] fn test_ord_and_eq() { let a = FixedBytes::new([1, 2, 3, 4]); let b = FixedBytes::new([1, 2, 3, 5]); assert!(a < b); assert_ne!(a, b); let c = FixedBytes::new([1, 2, 3, 4]); assert_eq!(a, c); } #[cfg(feature = "arbitrary")] mod conformance { use super::*; use commonware_codec::conformance::CodecConformance; commonware_conformance::conformance_tests! { CodecConformance>, } } }