diff --git a/.gitignore b/.gitignore index cd23ebe..0fde5bb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target/ Cargo.lock **/*.rs.bk +.claude diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bef046..d067d9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +* Implement `CompactEncoding>` for `&[u8]`, allowing byte slices to be encoded directly +* Implement `CompactEncoding>` for `&[T]` where `T: VecEncodable`, enabling encoding of slices like `&[&[u8]]` +* Implement `VecEncodable` for `Vec` to support `Vec>` +* Implement `VecEncodable>` for `&[u8]` to support `&[&[u8]]` + ### Changed +* `VecEncodable` trait now has an optional `Decode` type parameter (defaults to `Self`) to support types where the encoded type differs from the decoded type + ### Removed diff --git a/src/lib.rs b/src/lib.rs index 1030791..62e0264 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -180,9 +180,9 @@ const U32_SIZE: usize = 4; const U64_SIZE: usize = 8; /// Encoded size of a network port -pub const PORT_ENCODED_SIZE: usize = 2; +pub const PORT_ENCODED_SIZE: usize = 2; // ports are unsigned 16-bit integers (u16) /// Encoded size of an ipv4 address -pub const IPV4_ADDR_ENCODED_SIZE: usize = U32_SIZE; +pub const IPV4_ADDR_ENCODED_SIZE: usize = 4; /// Encoded size of an ipv6 address pub const IPV6_ADDR_ENCODED_SIZE: usize = 16; /// Encoded size for a [`SocketAddrV4`], an ipv4 address plus port. @@ -254,7 +254,7 @@ pub trait CompactEncoding { } /// Implement this for type `T` to have `CompactEncoding` implemented for `Vec` -pub trait VecEncodable: CompactEncoding { +pub trait VecEncodable: CompactEncoding { /// Calculate the resulting size in bytes of `vec` fn vec_encoded_size(vec: &[Self]) -> Result where @@ -263,15 +263,15 @@ pub trait VecEncodable: CompactEncoding { /// Encode `vec` to `buffer` fn vec_encode<'a>(vec: &[Self], buffer: &'a mut [u8]) -> Result<&'a mut [u8], EncodingError> where - Self: Sized, + Self: Sized + CompactEncoding, { encode_vec(vec, buffer) } - /// Decode [`Vec`] from buffer - fn vec_decode(buffer: &[u8]) -> Result<(Vec, &[u8]), EncodingError> + /// Decode [`Vec`] from buffer + fn vec_decode(buffer: &[u8]) -> Result<(Vec, &[u8]), EncodingError> where - Self: Sized, + Decode: Sized + CompactEncoding, { decode_vec(buffer) } @@ -955,6 +955,22 @@ impl CompactEncoding for Vec { decode_buffer_vec(buffer) } } +impl CompactEncoding> for &[u8] { + fn encoded_size(&self) -> Result { + Ok(encoded_size_usize(self.len()) + self.len()) + } + + fn encode<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], EncodingError> { + encode_buffer(self, buffer) + } + + fn decode(buffer: &[u8]) -> Result<(Vec, &[u8]), EncodingError> + where + Self: Sized, + { + decode_buffer_vec(buffer) + } +} impl CompactEncoding for Ipv4Addr { fn encoded_size(&self) -> std::result::Result { @@ -1063,6 +1079,31 @@ impl CompactEncoding for Vec { } } +impl CompactEncoding> for &[T] +where + T: VecEncodable + CompactEncoding, + D: CompactEncoding, +{ + fn encoded_size(&self) -> Result { + T::vec_encoded_size(self) + } + + fn encode<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], EncodingError> { + let mut rest = encode_usize_var(&self.len(), buffer)?; + for x in *self { + rest = x.encode(rest)?; + } + Ok(rest) + } + + fn decode(buffer: &[u8]) -> Result<(Vec, &[u8]), EncodingError> + where + D: Sized, + { + decode_vec(buffer) + } +} + /// Get the encoded size for a Vec with elements which have a fixed size encoding. pub fn vec_encoded_size_for_fixed_sized_elements( vec: &[T], @@ -1242,6 +1283,32 @@ impl VecEncodable for SocketAddrV6 { } } +impl VecEncodable for Vec { + fn vec_encoded_size(vec: &[Self]) -> Result + where + Self: Sized, + { + let mut out = encoded_size_usize(vec.len()); + for v in vec { + out += v.encoded_size()?; + } + Ok(out) + } +} + +impl VecEncodable> for &[u8] { + fn vec_encoded_size(vec: &[Self]) -> Result + where + Self: Sized, + { + let mut out = encoded_size_usize(vec.len()); + for v in vec { + out += v.encoded_size()?; + } + Ok(out) + } +} + #[cfg(test)] mod test { use super::*; @@ -1275,4 +1342,62 @@ mod test { Ok(()) } + #[test] + fn enc_dec_vec_vec_u8() -> Result<(), EncodingError> { + let input = vec![b"hello".to_vec(), b"goodbye".to_vec()]; + let buf = input.as_slice().to_encoded_bytes()?; + let (result, rest): (Vec>, &[u8]) = Vec::>::decode(&buf)?; + assert_eq!(result.len(), input.len()); + for (i, v) in result.iter().enumerate() { + assert_eq!(v, &input[i]); + } + assert!(rest.is_empty()); + Ok(()) + } + + #[test] + fn enc_dec_slice_slice_u8() -> Result<(), EncodingError> { + let input: &[&[u8]] = &[b"hello".as_slice(), b"goodbye".as_slice()]; + let buf = input.to_encoded_bytes()?; + let (result, rest): (Vec>, &[u8]) = Vec::>::decode(&buf)?; + assert_eq!(result.len(), input.len()); + for (i, v) in result.iter().enumerate() { + assert_eq!(v, &input[i]); + } + assert!(rest.is_empty()); + Ok(()) + } + + #[test] + fn enc_dec_byte_slice() -> Result<(), EncodingError> { + let input: &[u8] = b"hello world"; + let buf = input.to_encoded_bytes()?; + let (result, rest): (Vec, &[u8]) = Vec::::decode(&buf)?; + assert_eq!(result, input); + assert!(rest.is_empty()); + Ok(()) + } + + #[test] + fn enc_dec_empty_slice_slice_u8() -> Result<(), EncodingError> { + let input: &[&[u8]] = &[]; + let buf = input.to_encoded_bytes()?; + assert_eq!(buf.len(), 1); // just the length prefix + let (result, rest): (Vec>, &[u8]) = Vec::>::decode(&buf)?; + assert!(result.is_empty()); + assert!(rest.is_empty()); + Ok(()) + } + + #[test] + fn slice_and_vec_encode_identically() -> Result<(), EncodingError> { + let vec_input = vec![b"hello".to_vec(), b"goodbye".to_vec()]; + let slice_input: &[&[u8]] = &[b"hello".as_slice(), b"goodbye".as_slice()]; + + let vec_buf = vec_input.to_encoded_bytes()?; + let slice_buf = slice_input.to_encoded_bytes()?; + + assert_eq!(&*vec_buf, &*slice_buf); + Ok(()) + } }