Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
2 changes: 1 addition & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl From<EncodingError> 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}")),
}
}
}
2 changes: 1 addition & 1 deletion src/fixedwidth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self> {
fn as_fixed_width(&self) -> FixedWidthUint<'_, Self> {
FixedWidthUint(self)
}
}
Expand Down
153 changes: 138 additions & 15 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -164,7 +167,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};
Expand All @@ -180,6 +183,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<Decode: ?Sized = Self> {
/// The size in bytes required to encode `self`.
Expand Down Expand Up @@ -433,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)?;
Expand Down Expand Up @@ -514,7 +540,7 @@ pub fn take_array<const N: usize>(
) -> std::result::Result<([u8; N], &[u8]), EncodingError> {
let Some((out, rest)) = buffer.split_first_chunk::<N>() 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()
)));
Expand Down Expand Up @@ -607,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")))
}
})
}
Expand Down Expand Up @@ -684,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)?,
})
}
Expand Down Expand Up @@ -717,7 +741,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> {
Expand Down Expand Up @@ -838,6 +863,23 @@ impl CompactEncoding for u64 {
}
}

impl CompactEncoding for usize {
fn encoded_size(&self) -> Result<usize, EncodingError> {
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<usize, EncodingError> {
encoded_size_str(self)
Expand Down Expand Up @@ -947,9 +989,10 @@ impl CompactEncoding for Ipv4Addr {
Ok((Ipv4Addr::from(*dest), rest))
}
}

impl CompactEncoding for Ipv6Addr {
fn encoded_size(&self) -> std::result::Result<usize, EncodingError> {
Ok(4)
Ok(IPV6_ADDR_ENCODED_SIZE)
}

fn encode<'a>(&self, buffer: &'a mut [u8]) -> std::result::Result<&'a mut [u8], EncodingError> {
Expand Down Expand Up @@ -1016,6 +1059,14 @@ impl<T: VecEncodable> CompactEncoding for Vec<T> {
}
}

/// Get the encoded size for a Vec with elements which have a fixed size encoding.
pub fn vec_encoded_size_for_fixed_sized_elements<T: CompactEncoding>(
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<usize, EncodingError>
where
Expand Down Expand Up @@ -1107,6 +1158,70 @@ impl<T: BoxedSliceEncodable> CompactEncoding for Box<[T]> {
}
}

impl CompactEncoding for SocketAddrV4 {
fn encoded_size(&self) -> Result<usize, EncodingError> {
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<usize, EncodingError> {
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<usize, EncodingError>
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<usize, EncodingError>
where
Self: Sized,
{
Ok(vec_encoded_size_for_fixed_sized_elements(
vec,
SOCKET_ADDR_V6_ENCODED_SIZE,
))
}
}

#[cfg(test)]
mod test {
use super::*;
Expand Down Expand Up @@ -1140,4 +1255,12 @@ mod test {

Ok(())
}

#[test]
fn addr_6() -> Result<(), Box<dyn std::error::Error>> {
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(())
}
}
Loading