Skip to content

Commit df3d13f

Browse files
authored
Merge pull request #2 from cowlicks/socketaddr
Add encodings for socket types and usize
2 parents a7cab88 + 6a4ed0c commit df3d13f

File tree

4 files changed

+187
-16
lines changed

4 files changed

+187
-16
lines changed

CHANGELOG.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
<!-- next-header -->
9+
10+
## [Unreleased] - ReleaseDate
11+
12+
### Added
13+
14+
* Implement `CompactEncoding` for:
15+
- `SocketAddrV4`
16+
- `SocketAddrV6`
17+
- `Ipv4Addr`
18+
- `Ipv6Addr`
19+
* We also implement `CompactEncoding` for `usize` as a variable width integer.
20+
The same as the JavaScript "uint" encoding
21+
22+
### Changed
23+
24+
### Removed
25+
26+
27+
28+
29+
<!-- next-url -->

release.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
pre-release-replacements = [
2+
{file="CHANGELOG.md", search="Unreleased", replace="{{version}}"},
3+
{file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1},
4+
{file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}"},
5+
{file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n\n## [Unreleased] - ReleaseDate\n\n### Added\n\n### Changed\n\n### Removed\n\n", exactly=1},
6+
{file="CHANGELOG.md", search="<!-- next-url -->", replace="<!-- next-url -->\n[Unreleased]: https://github.com/datrs/compact-encoding/compare/{{tag_name}}...HEAD", exactly=1},
7+
]

src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ impl From<EncodingError> for std::io::Error {
8787
EncodingErrorKind::InvalidData => {
8888
std::io::Error::new(std::io::ErrorKind::InvalidData, format!("{e}"))
8989
}
90-
_ => std::io::Error::new(std::io::ErrorKind::Other, format!("{e}")),
90+
_ => std::io::Error::other(format!("{e}")),
9191
}
9292
}
9393
}

src/lib.rs

Lines changed: 150 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#![forbid(unsafe_code, missing_docs)]
22
#![cfg_attr(test, deny(warnings))]
3-
43
//! # Series of compact encoding schemes for building small and fast parsers and serializers
54
//!
65
//! Binary compatible with the
@@ -164,7 +163,7 @@ mod fixedwidth;
164163
pub use fixedwidth::{FixedWidthEncoding, FixedWidthU32, FixedWidthU64, FixedWidthUint};
165164
use std::{
166165
any::type_name,
167-
net::{Ipv4Addr, Ipv6Addr},
166+
net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6},
168167
};
169168

170169
pub use crate::error::{EncodingError, EncodingErrorKind};
@@ -180,6 +179,17 @@ const U16_SIZE: usize = 2;
180179
const U32_SIZE: usize = 4;
181180
const U64_SIZE: usize = 8;
182181

182+
/// Encoded size of a network port
183+
pub const PORT_ENCODED_SIZE: usize = 2;
184+
/// Encoded size of an ipv4 address
185+
pub const IPV4_ADDR_ENCODED_SIZE: usize = U32_SIZE;
186+
/// Encoded size of an ipv6 address
187+
pub const IPV6_ADDR_ENCODED_SIZE: usize = 16;
188+
/// Encoded size for a [`SocketAddrV4`], an ipv4 address plus port.
189+
pub const SOCKET_ADDR_V4_ENCODED_SIZE: usize = IPV4_ADDR_ENCODED_SIZE + PORT_ENCODED_SIZE;
190+
/// Encoded size for a [`SocketAddrV6`], an ipv6 address plus port.
191+
pub const SOCKET_ADDR_V6_ENCODED_SIZE: usize = IPV6_ADDR_ENCODED_SIZE + PORT_ENCODED_SIZE;
192+
183193
/// A trait for building small and fast parsers and serializers.
184194
pub trait CompactEncoding<Decode: ?Sized = Self> {
185195
/// The size in bytes required to encode `self`.
@@ -433,8 +443,20 @@ macro_rules! map_decode {
433443
}};
434444
}
435445

436-
/// helper for mapping the first element of a two eleent tuple
446+
#[macro_export]
447+
/// Helper for mapping the first element of a two eleent tuple.
448+
/// This is useful for cleanly handling the result of CompactEncoding::decode.
437449
macro_rules! map_first {
450+
($res:expr, $f:expr) => {{
451+
let (one, two) = $res;
452+
let mapped = $f(one);
453+
(mapped, two)
454+
}};
455+
}
456+
457+
#[macro_export]
458+
/// like [`map_first`] but the mapping should return a result.
459+
macro_rules! map_first_result {
438460
($res:expr, $f:expr) => {{
439461
let (one, two) = $res;
440462
let mapped = $f(one)?;
@@ -514,7 +536,7 @@ pub fn take_array<const N: usize>(
514536
) -> std::result::Result<([u8; N], &[u8]), EncodingError> {
515537
let Some((out, rest)) = buffer.split_first_chunk::<N>() else {
516538
return Err(EncodingError::out_of_bounds(&format!(
517-
"Could not write [{}] bytes to buffer of length [{}]",
539+
"Could not take [{}] bytes from buffer of length [{}]",
518540
N,
519541
buffer.len()
520542
)));
@@ -607,16 +629,14 @@ pub fn decode_usize(buffer: &[u8]) -> Result<(usize, &[u8]), EncodingError> {
607629
let ([first], rest) = take_array::<1>(buffer)?;
608630
Ok(match first {
609631
x if x < U16_SIGNIFIER => (x.into(), rest),
610-
U16_SIGNIFIER => map_first!(decode_u16(rest)?, |x: u16| Ok(x.into())),
632+
U16_SIGNIFIER => map_first!(decode_u16(rest)?, |x: u16| x.into()),
611633
U32_SIGNIFIER => {
612-
map_first!(decode_u32(rest)?, |val| usize::try_from(val).map_err(
613-
|_| EncodingError::overflow("Could not convert u32 to usize")
614-
))
634+
map_first_result!(decode_u32(rest)?, |val| usize::try_from(val)
635+
.map_err(|_| EncodingError::overflow("Could not convert u32 to usize")))
615636
}
616637
_ => {
617-
map_first!(decode_u64(rest)?, |val| usize::try_from(val).map_err(
618-
|_| EncodingError::overflow("Could not convert u64 to usize")
619-
))
638+
map_first_result!(decode_u64(rest)?, |val| usize::try_from(val)
639+
.map_err(|_| EncodingError::overflow("Could not convert u64 to usize")))
620640
}
621641
})
622642
}
@@ -684,8 +704,8 @@ fn decode_u64_var(buffer: &[u8]) -> Result<(u64, &[u8]), EncodingError> {
684704
let ([first], rest) = take_array::<1>(buffer)?;
685705
Ok(match first {
686706
x if x < U16_SIGNIFIER => (x.into(), rest),
687-
U16_SIGNIFIER => map_first!(decode_u16(rest)?, |x: u16| Ok(x.into())),
688-
U32_SIGNIFIER => map_first!(decode_u32(rest)?, |x: u32| Ok(x.into())),
707+
U16_SIGNIFIER => map_first!(decode_u16(rest)?, |x: u16| x.into()),
708+
U32_SIGNIFIER => map_first!(decode_u32(rest)?, |x: u32| x.into()),
689709
_ => decode_u64(rest)?,
690710
})
691711
}
@@ -717,7 +737,8 @@ fn encode_u64(val: u64, buffer: &mut [u8]) -> Result<&mut [u8], EncodingError> {
717737
write_array(&val.to_le_bytes(), buffer)
718738
}
719739

720-
fn encode_usize_var<'a>(
740+
/// Encode a `usize` in a variable width way
741+
pub fn encode_usize_var<'a>(
721742
value: &usize,
722743
buffer: &'a mut [u8],
723744
) -> Result<&'a mut [u8], EncodingError> {
@@ -838,6 +859,23 @@ impl CompactEncoding for u64 {
838859
}
839860
}
840861

862+
impl CompactEncoding for usize {
863+
fn encoded_size(&self) -> Result<usize, EncodingError> {
864+
Ok(encoded_size_usize(*self))
865+
}
866+
867+
fn encode<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], EncodingError> {
868+
encode_usize_var(self, buffer)
869+
}
870+
871+
fn decode(buffer: &[u8]) -> Result<(Self, &[u8]), EncodingError>
872+
where
873+
Self: Sized,
874+
{
875+
decode_usize(buffer)
876+
}
877+
}
878+
841879
impl CompactEncoding for String {
842880
fn encoded_size(&self) -> Result<usize, EncodingError> {
843881
encoded_size_str(self)
@@ -947,11 +985,20 @@ impl CompactEncoding for Ipv4Addr {
947985
Ok((Ipv4Addr::from(*dest), rest))
948986
}
949987
}
988+
950989
impl CompactEncoding for Ipv6Addr {
951990
fn encoded_size(&self) -> std::result::Result<usize, EncodingError> {
952-
Ok(4)
991+
Ok(IPV6_ADDR_ENCODED_SIZE)
953992
}
954993

994+
/// ```
995+
/// # use std::net::Ipv6Addr;
996+
/// # use compact_encoding::CompactEncoding;
997+
/// let addr: Ipv6Addr = "1:2:3::1".parse()?;
998+
/// let buff = addr.to_encoded_bytes()?.to_vec();
999+
/// assert_eq!(buff, vec![0, 1, 0, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
1000+
/// # Ok::<(), Box<dyn std::error::Error>>(())
1001+
/// ```
9551002
fn encode<'a>(&self, buffer: &'a mut [u8]) -> std::result::Result<&'a mut [u8], EncodingError> {
9561003
let Some((dest, rest)) = buffer.split_first_chunk_mut::<16>() else {
9571004
return Err(EncodingError::out_of_bounds(&format!(
@@ -1016,6 +1063,14 @@ impl<T: VecEncodable> CompactEncoding for Vec<T> {
10161063
}
10171064
}
10181065

1066+
/// Get the encoded size for a Vec with elements which have a fixed size encoding.
1067+
pub fn vec_encoded_size_for_fixed_sized_elements<T: CompactEncoding>(
1068+
vec: &[T],
1069+
element_encoded_size: usize,
1070+
) -> usize {
1071+
encoded_size_usize(vec.len()) + (vec.len() * element_encoded_size)
1072+
}
1073+
10191074
impl VecEncodable for u32 {
10201075
fn vec_encoded_size(vec: &[Self]) -> Result<usize, EncodingError>
10211076
where
@@ -1107,6 +1162,86 @@ impl<T: BoxedSliceEncodable> CompactEncoding for Box<[T]> {
11071162
}
11081163
}
11091164

1165+
impl CompactEncoding for SocketAddrV4 {
1166+
fn encoded_size(&self) -> Result<usize, EncodingError> {
1167+
Ok(SOCKET_ADDR_V4_ENCODED_SIZE)
1168+
}
1169+
1170+
/// ```
1171+
/// # use std::net::SocketAddrV4;
1172+
/// # use compact_encoding::CompactEncoding;
1173+
/// let addr: SocketAddrV4 = "127.0.0.1:42".parse()?;
1174+
/// let buff = addr.to_encoded_bytes()?.to_vec();
1175+
/// assert_eq!(buff, vec![127, 0, 0, 1, 42, 0]);
1176+
/// # Ok::<(), Box<dyn std::error::Error>>(())
1177+
/// ```
1178+
fn encode<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], EncodingError> {
1179+
let rest = self.ip().encode(buffer)?;
1180+
encode_u16(self.port(), rest)
1181+
}
1182+
1183+
fn decode(buffer: &[u8]) -> Result<(Self, &[u8]), EncodingError>
1184+
where
1185+
Self: Sized,
1186+
{
1187+
let (ip, rest) = Ipv4Addr::decode(buffer)?;
1188+
let (port, rest) = decode_u16(rest)?;
1189+
Ok((SocketAddrV4::new(ip, port), rest))
1190+
}
1191+
}
1192+
impl CompactEncoding for SocketAddrV6 {
1193+
fn encoded_size(&self) -> Result<usize, EncodingError> {
1194+
Ok(SOCKET_ADDR_V6_ENCODED_SIZE)
1195+
}
1196+
1197+
/// ```
1198+
/// # use std::net::SocketAddrV6;
1199+
/// # use compact_encoding::CompactEncoding;
1200+
/// let addr: SocketAddrV6 = "[1:2:3::1]:80".parse()?;
1201+
/// let buff = addr.to_encoded_bytes()?.to_vec();
1202+
/// assert_eq!(buff, vec![0, 1, 0, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 80, 0]);
1203+
/// # Ok::<(), Box<dyn std::error::Error>>(())
1204+
/// ```
1205+
fn encode<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], EncodingError> {
1206+
let rest = self.ip().encode(buffer)?;
1207+
encode_u16(self.port(), rest)
1208+
}
1209+
1210+
fn decode(buffer: &[u8]) -> Result<(Self, &[u8]), EncodingError>
1211+
where
1212+
Self: Sized,
1213+
{
1214+
let (ip, rest) = Ipv6Addr::decode(buffer)?;
1215+
let (port, rest) = decode_u16(rest)?;
1216+
// TODO is this correct for flowinfo and scope_id?
1217+
Ok((SocketAddrV6::new(ip, port, 0, 0), rest))
1218+
}
1219+
}
1220+
1221+
impl VecEncodable for SocketAddrV4 {
1222+
fn vec_encoded_size(vec: &[Self]) -> Result<usize, EncodingError>
1223+
where
1224+
Self: Sized,
1225+
{
1226+
Ok(vec_encoded_size_for_fixed_sized_elements(
1227+
vec,
1228+
SOCKET_ADDR_V4_ENCODED_SIZE,
1229+
))
1230+
}
1231+
}
1232+
1233+
impl VecEncodable for SocketAddrV6 {
1234+
fn vec_encoded_size(vec: &[Self]) -> Result<usize, EncodingError>
1235+
where
1236+
Self: Sized,
1237+
{
1238+
Ok(vec_encoded_size_for_fixed_sized_elements(
1239+
vec,
1240+
SOCKET_ADDR_V6_ENCODED_SIZE,
1241+
))
1242+
}
1243+
}
1244+
11101245
#[cfg(test)]
11111246
mod test {
11121247
use super::*;

0 commit comments

Comments
 (0)