From 492dfc2dcb6d64af576446fd9e5e4314fa6fd97b Mon Sep 17 00:00:00 2001 From: Blake Griffith Date: Mon, 5 May 2025 13:52:59 -0400 Subject: [PATCH 1/9] chore: Release compact-encoding version 2.0.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8bf9b41..1f8cbc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "compact-encoding" -version = "1.1.0" +version = "2.0.0" license = "MIT OR Apache-2.0" description = "A series of compact encoding schemes for building small and fast parsers and serializers" documentation = "https://docs.rs/compact-encoding" From bca1d81a71c877aa93981d16a7a1a0eb9c5d3222 Mon Sep 17 00:00:00 2001 From: Blake Griffith Date: Wed, 4 Jun 2025 13:19:17 -0400 Subject: [PATCH 2/9] Add docs fix err msgs --- src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e57eda6..fdaef77 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -514,7 +514,7 @@ pub fn take_array( ) -> std::result::Result<([u8; N], &[u8]), EncodingError> { let Some((out, rest)) = buffer.split_first_chunk::() else { return Err(EncodingError::out_of_bounds(&format!( - "Could not write [{}] bytes to buffer of length [{}]", + "Could not take [{}] bytes from buffer of length [{}]", N, buffer.len() ))); @@ -717,7 +717,8 @@ fn encode_u64(val: u64, buffer: &mut [u8]) -> Result<&mut [u8], EncodingError> { write_array(&val.to_le_bytes(), buffer) } -fn encode_usize_var<'a>( +/// Encode a `usize` in a variable width way +pub fn encode_usize_var<'a>( value: &usize, buffer: &'a mut [u8], ) -> Result<&'a mut [u8], EncodingError> { From ca9304d68bd2e7260983208409a1116e5d5510ab Mon Sep 17 00:00:00 2001 From: Blake Griffith Date: Wed, 4 Jun 2025 13:19:32 -0400 Subject: [PATCH 3/9] impl CompactEncoding for usize --- src/lib.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index fdaef77..b774060 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -839,6 +839,23 @@ impl CompactEncoding for u64 { } } +impl CompactEncoding for usize { + fn encoded_size(&self) -> Result { + Ok(encoded_size_usize(*self)) + } + + fn encode<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], EncodingError> { + encode_usize_var(self, buffer) + } + + fn decode(buffer: &[u8]) -> Result<(Self, &[u8]), EncodingError> + where + Self: Sized, + { + decode_usize(buffer) + } +} + impl CompactEncoding for String { fn encoded_size(&self) -> Result { encoded_size_str(self) From c307245718c297734eb0a2d9cadea7f2419f7a88 Mon Sep 17 00:00:00 2001 From: Blake Griffith Date: Wed, 4 Jun 2025 13:19:52 -0400 Subject: [PATCH 4/9] Add vec_encoded_size_for_fixed_size_elements --- src/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index b774060..b6a7712 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1034,6 +1034,14 @@ impl CompactEncoding for Vec { } } +/// 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], + element_encoded_size: usize, +) -> usize { + encoded_size_usize(vec.len()) + (vec.len() * element_encoded_size) +} + impl VecEncodable for u32 { fn vec_encoded_size(vec: &[Self]) -> Result where From 0b7bfdc7c56bcefbdca2a2c80812f919bb410c9c Mon Sep 17 00:00:00 2001 From: Blake Griffith Date: Wed, 4 Jun 2025 13:20:06 -0400 Subject: [PATCH 5/9] impl CompactEncoding for SocketAddrV4/V6 --- src/lib.rs | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index b6a7712..4068a15 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -164,7 +164,7 @@ mod fixedwidth; pub use fixedwidth::{FixedWidthEncoding, FixedWidthU32, FixedWidthU64, FixedWidthUint}; use std::{ any::type_name, - net::{Ipv4Addr, Ipv6Addr}, + net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6}, }; pub use crate::error::{EncodingError, EncodingErrorKind}; @@ -1133,6 +1133,75 @@ impl CompactEncoding for Box<[T]> { } } +/// Encoded size for a [`SocketAddrV4`]. +pub const SOCKET_ADDR_V4_ENCODED_SIZE: usize = 4 + 2; +/// Encoded size for a [`SocketAddrV6`]. +pub const SOCKET_ADDR_V6_ENCODED_SIZE: usize = 16 + 2; + +impl CompactEncoding for SocketAddrV4 { + fn encoded_size(&self) -> Result { + Ok(SOCKET_ADDR_V4_ENCODED_SIZE) + } + + fn encode<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], EncodingError> { + let rest = self.ip().encode(buffer)?; + encode_u16(self.port(), rest) + } + + fn decode(buffer: &[u8]) -> Result<(Self, &[u8]), EncodingError> + where + Self: Sized, + { + let (ip, rest) = Ipv4Addr::decode(buffer)?; + let (port, rest) = decode_u16(rest)?; + Ok((SocketAddrV4::new(ip, port), rest)) + } +} +impl CompactEncoding for SocketAddrV6 { + fn encoded_size(&self) -> Result { + Ok(SOCKET_ADDR_V6_ENCODED_SIZE) + } + + fn encode<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], EncodingError> { + let rest = self.ip().encode(buffer)?; + encode_u16(self.port(), rest) + } + + fn decode(buffer: &[u8]) -> Result<(Self, &[u8]), EncodingError> + where + Self: Sized, + { + let (ip, rest) = Ipv6Addr::decode(buffer)?; + let (port, rest) = decode_u16(rest)?; + // TODO is this correct for flowinfo and scope_id? + Ok((SocketAddrV6::new(ip, port, 0, 0), rest)) + } +} + +impl VecEncodable for SocketAddrV4 { + fn vec_encoded_size(vec: &[Self]) -> Result + where + Self: Sized, + { + Ok(vec_encoded_size_for_fixed_sized_elements( + vec, + SOCKET_ADDR_V4_ENCODED_SIZE, + )) + } +} + +impl VecEncodable for SocketAddrV6 { + fn vec_encoded_size(vec: &[Self]) -> Result + where + Self: Sized, + { + Ok(vec_encoded_size_for_fixed_sized_elements( + vec, + SOCKET_ADDR_V6_ENCODED_SIZE, + )) + } +} + #[cfg(test)] mod test { use super::*; From 3595fb88a71bf813b70a902caea573f40843cdb5 Mon Sep 17 00:00:00 2001 From: Blake Griffith Date: Wed, 4 Jun 2025 13:28:58 -0400 Subject: [PATCH 6/9] Add more const encoded sizes. Also fix ipv6 encoded size --- src/lib.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4068a15..79c9a95 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -180,6 +180,17 @@ const U16_SIZE: usize = 2; const U32_SIZE: usize = 4; const U64_SIZE: usize = 8; +/// Encoded size of a network port +pub const PORT_ENCODED_SIZE: usize = 2; +/// Encoded size of an ipv4 address +pub const IPV4_ADDR_ENCODED_SIZE: usize = U32_SIZE; +/// Encoded size of an ipv6 address +pub const IPV6_ADDR_ENCODED_SIZE: usize = 16; +/// Encoded size for a [`SocketAddrV4`], an ipv4 address plus port. +pub const SOCKET_ADDR_V4_ENCODED_SIZE: usize = IPV4_ADDR_ENCODED_SIZE + PORT_ENCODED_SIZE; +/// Encoded size for a [`SocketAddrV6`], an ipv6 address plus port. +pub const SOCKET_ADDR_V6_ENCODED_SIZE: usize = IPV6_ADDR_ENCODED_SIZE + PORT_ENCODED_SIZE; + /// A trait for building small and fast parsers and serializers. pub trait CompactEncoding { /// The size in bytes required to encode `self`. @@ -965,9 +976,10 @@ impl CompactEncoding for Ipv4Addr { Ok((Ipv4Addr::from(*dest), rest)) } } + impl CompactEncoding for Ipv6Addr { fn encoded_size(&self) -> std::result::Result { - Ok(4) + Ok(IPV6_ADDR_ENCODED_SIZE) } fn encode<'a>(&self, buffer: &'a mut [u8]) -> std::result::Result<&'a mut [u8], EncodingError> { @@ -1133,11 +1145,6 @@ impl CompactEncoding for Box<[T]> { } } -/// Encoded size for a [`SocketAddrV4`]. -pub const SOCKET_ADDR_V4_ENCODED_SIZE: usize = 4 + 2; -/// Encoded size for a [`SocketAddrV6`]. -pub const SOCKET_ADDR_V6_ENCODED_SIZE: usize = 16 + 2; - impl CompactEncoding for SocketAddrV4 { fn encoded_size(&self) -> Result { Ok(SOCKET_ADDR_V4_ENCODED_SIZE) From 8ad0fc15ae6cb9770397732264012652ff94c19f Mon Sep 17 00:00:00 2001 From: Blake Griffith Date: Sun, 22 Jun 2025 03:16:24 -0400 Subject: [PATCH 7/9] wip fixupme --- src/lib.rs | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 79c9a95..478ab41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,9 @@ #![forbid(unsafe_code, missing_docs)] #![cfg_attr(test, deny(warnings))] - +// TODO compact encoding currently has no way to encode/decode`std::net::SocketAddr` because it +// could contain a ipv4 OR an ipv6 address and the current ipv4 encoding has no way for it to +// distinguish itself. +// So should this be a panic? //! # Series of compact encoding schemes for building small and fast parsers and serializers //! //! Binary compatible with the @@ -444,8 +447,20 @@ macro_rules! map_decode { }}; } -/// helper for mapping the first element of a two eleent tuple +#[macro_export] +/// Helper for mapping the first element of a two eleent tuple. +/// This is useful for cleanly handling the result of CompactEncoding::decode. macro_rules! map_first { + ($res:expr, $f:expr) => {{ + let (one, two) = $res; + let mapped = $f(one); + (mapped, two) + }}; +} + +#[macro_export] +/// like [`map_first`] but the mapping should return a result. +macro_rules! map_first_result { ($res:expr, $f:expr) => {{ let (one, two) = $res; let mapped = $f(one)?; @@ -618,16 +633,14 @@ pub fn decode_usize(buffer: &[u8]) -> Result<(usize, &[u8]), EncodingError> { let ([first], rest) = take_array::<1>(buffer)?; Ok(match first { x if x < U16_SIGNIFIER => (x.into(), rest), - U16_SIGNIFIER => map_first!(decode_u16(rest)?, |x: u16| Ok(x.into())), + U16_SIGNIFIER => map_first!(decode_u16(rest)?, |x: u16| x.into()), U32_SIGNIFIER => { - map_first!(decode_u32(rest)?, |val| usize::try_from(val).map_err( - |_| EncodingError::overflow("Could not convert u32 to usize") - )) + map_first_result!(decode_u32(rest)?, |val| usize::try_from(val) + .map_err(|_| EncodingError::overflow("Could not convert u32 to usize"))) } _ => { - map_first!(decode_u64(rest)?, |val| usize::try_from(val).map_err( - |_| EncodingError::overflow("Could not convert u64 to usize") - )) + map_first_result!(decode_u64(rest)?, |val| usize::try_from(val) + .map_err(|_| EncodingError::overflow("Could not convert u64 to usize"))) } }) } @@ -695,8 +708,8 @@ fn decode_u64_var(buffer: &[u8]) -> Result<(u64, &[u8]), EncodingError> { let ([first], rest) = take_array::<1>(buffer)?; Ok(match first { x if x < U16_SIGNIFIER => (x.into(), rest), - U16_SIGNIFIER => map_first!(decode_u16(rest)?, |x: u16| Ok(x.into())), - U32_SIGNIFIER => map_first!(decode_u32(rest)?, |x: u32| Ok(x.into())), + U16_SIGNIFIER => map_first!(decode_u16(rest)?, |x: u16| x.into()), + U32_SIGNIFIER => map_first!(decode_u32(rest)?, |x: u32| x.into()), _ => decode_u64(rest)?, }) } @@ -1242,4 +1255,12 @@ mod test { Ok(()) } + + #[test] + fn addr_6() -> Result<(), Box> { + let addr: Ipv6Addr = "1:2:3::1".parse()?; + let buff = addr.to_encoded_bytes()?.to_vec(); + assert_eq!(buff, vec![0, 1, 0, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); + Ok(()) + } } From 43b318a7a0294b015d094bb5d80cf0c11fb257a0 Mon Sep 17 00:00:00 2001 From: Blake Griffith Date: Mon, 1 Sep 2025 15:38:52 -0400 Subject: [PATCH 8/9] fix new lifetime elision warning --- src/fixedwidth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fixedwidth.rs b/src/fixedwidth.rs index 4ed4819..6b70e54 100644 --- a/src/fixedwidth.rs +++ b/src/fixedwidth.rs @@ -57,7 +57,7 @@ pub trait FixedWidthEncoding { Self: Sized; /// Get a uint in a form that encodes to a fixed width - fn as_fixed_width(&self) -> FixedWidthUint { + fn as_fixed_width(&self) -> FixedWidthUint<'_, Self> { FixedWidthUint(self) } } From 7d4af073c89f5d004eab3b03a30ab3f026865a71 Mon Sep 17 00:00:00 2001 From: Blake Griffith Date: Thu, 30 Oct 2025 01:06:51 -0400 Subject: [PATCH 9/9] lint --- src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/error.rs b/src/error.rs index 04b8f4a..07414f1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -87,7 +87,7 @@ impl From for std::io::Error { EncodingErrorKind::InvalidData => { std::io::Error::new(std::io::ErrorKind::InvalidData, format!("{e}")) } - _ => std::io::Error::new(std::io::ErrorKind::Other, format!("{e}")), + _ => std::io::Error::other(format!("{e}")), } } }