Skip to content

Commit dc99341

Browse files
authored
Merge pull request #1 from cowlicks/hyperswarm-changes
Refactor to remove `State` and make easier to use downstream
2 parents ec005c4 + d1d597f commit dc99341

File tree

8 files changed

+1575
-1288
lines changed

8 files changed

+1575
-1288
lines changed

benches/simple.rs

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,28 @@
11
use std::time::Instant;
22

3-
use compact_encoding::{CompactEncoding, State};
3+
use compact_encoding::{map_decode, map_encode, sum_encoded_size, CompactEncoding, EncodingError};
44
use criterion::{black_box, criterion_group, criterion_main, Criterion};
55

66
const U32_VALUE: u32 = 0xF0E1D2C3;
77
const STR_VALUE: &str = "foo";
88
const U64_VALUE: u64 = u64::MAX;
99

10-
fn preencode() -> State {
11-
let mut enc_state = State::new();
12-
enc_state.preencode(&U32_VALUE).unwrap();
13-
enc_state.preencode_str(STR_VALUE).unwrap();
14-
enc_state.preencode(&U64_VALUE).unwrap();
15-
enc_state
10+
fn preencode() -> Result<usize, EncodingError> {
11+
Ok(sum_encoded_size!(U32_VALUE, STR_VALUE, U64_VALUE))
1612
}
1713

18-
fn encode(enc_state: &mut State, buffer: &mut [u8]) {
19-
enc_state.encode(&U32_VALUE, buffer).unwrap();
20-
enc_state.encode_str(STR_VALUE, buffer).unwrap();
21-
enc_state.encode(&U64_VALUE, buffer).unwrap();
14+
fn encode(buffer: &mut [u8]) -> Result<(), EncodingError> {
15+
let _ = map_encode!(buffer, U32_VALUE, STR_VALUE, U64_VALUE);
16+
Ok(())
2217
}
2318

24-
fn decode(dec_state: &mut State, buffer: &[u8]) {
25-
let _: u32 = dec_state.decode(buffer).unwrap();
26-
let _: String = dec_state.decode(buffer).unwrap();
27-
let _: u64 = dec_state.decode(buffer).unwrap();
19+
fn decode(buffer: &[u8]) -> Result<(), EncodingError> {
20+
map_decode!(buffer, [u32, String, u64]);
21+
Ok(())
22+
}
23+
24+
fn create_buffer(encoded_size: usize) -> Box<[u8]> {
25+
vec![0; encoded_size].into_boxed_slice()
2826
}
2927

3028
fn preencode_generic_simple(c: &mut Criterion) {
@@ -36,10 +34,10 @@ fn preencode_generic_simple(c: &mut Criterion) {
3634
fn create_buffer_generic_simple(c: &mut Criterion) {
3735
c.bench_function("create buffer generic simple", |b| {
3836
b.iter_custom(|iters| {
39-
let enc_state = preencode();
37+
let encoded_size = preencode().unwrap();
4038
let start = Instant::now();
4139
for _ in 0..iters {
42-
black_box(enc_state.create_buffer());
40+
black_box(create_buffer(encoded_size));
4341
}
4442
start.elapsed()
4543
});
@@ -50,13 +48,12 @@ fn create_buffer_generic_simple(c: &mut Criterion) {
5048
fn encode_generic_simple(c: &mut Criterion) {
5149
c.bench_function("encode generic simple", |b| {
5250
b.iter_custom(|iters| {
53-
let enc_state = preencode();
54-
let buffer = enc_state.create_buffer();
51+
let encoded_size = preencode().unwrap();
52+
let buffer = create_buffer(encoded_size);
5553
let start = Instant::now();
5654
for _ in 0..iters {
57-
let mut enc_state = enc_state.clone();
5855
let mut buffer = buffer.clone();
59-
black_box(encode(&mut enc_state, &mut buffer));
56+
black_box(encode(&mut buffer).unwrap());
6057
}
6158
start.elapsed()
6259
});
@@ -67,15 +64,13 @@ fn encode_generic_simple(c: &mut Criterion) {
6764
fn decode_generic_simple(c: &mut Criterion) {
6865
c.bench_function("decode generic simple", |b| {
6966
b.iter_custom(|iters| {
70-
let mut enc_state = preencode();
71-
let mut buffer = enc_state.create_buffer();
72-
encode(&mut enc_state, &mut buffer);
73-
let dec_state = State::from_buffer(&buffer);
67+
let encoded_size = preencode().unwrap();
68+
let mut buffer = vec![0_u8; encoded_size];
69+
encode(&mut buffer).unwrap();
7470
let start = Instant::now();
7571
for _ in 0..iters {
76-
let mut dec_state = dec_state.clone();
7772
let buffer = buffer.clone();
78-
black_box(decode(&mut dec_state, &buffer));
73+
black_box(decode(&buffer).unwrap());
7974
}
8075
start.elapsed()
8176
});

src/error.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//! Basic types of compact_encoding.
2+
use std::fmt;
3+
4+
/// Specific type [EncodingError]
5+
#[derive(fmt::Debug, Clone, PartialEq)]
6+
pub enum EncodingErrorKind {
7+
/// Encoding or decoding did not stay within the bounds of the buffer
8+
OutOfBounds,
9+
/// Buffer data overflowed type during encoding or decoding.
10+
Overflow,
11+
/// Buffer contained invalid data during decoding.
12+
InvalidData,
13+
/// Some external error occurred causing a [`crate::CompactEncoding`] method to fail.
14+
External,
15+
}
16+
17+
/// Encoding/decoding error.
18+
#[derive(fmt::Debug, Clone, PartialEq)]
19+
pub struct EncodingError {
20+
/// Specific type of error
21+
pub kind: EncodingErrorKind,
22+
/// Message for the error
23+
pub message: String,
24+
}
25+
26+
impl std::error::Error for EncodingError {
27+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
28+
None
29+
}
30+
}
31+
32+
impl EncodingError {
33+
/// Create EncodingError
34+
pub fn new(kind: EncodingErrorKind, message: &str) -> Self {
35+
Self {
36+
kind,
37+
message: message.to_string(),
38+
}
39+
}
40+
/// Helper function for making an overflow error
41+
pub fn overflow(message: &str) -> Self {
42+
Self {
43+
kind: EncodingErrorKind::Overflow,
44+
message: message.to_string(),
45+
}
46+
}
47+
/// Helper function for making an out of bounds error
48+
pub fn out_of_bounds(message: &str) -> Self {
49+
Self {
50+
kind: EncodingErrorKind::OutOfBounds,
51+
message: message.to_string(),
52+
}
53+
}
54+
/// Helper function for making an invalid data error
55+
pub fn invalid_data(message: &str) -> Self {
56+
Self {
57+
kind: EncodingErrorKind::InvalidData,
58+
message: message.to_string(),
59+
}
60+
}
61+
/// Helper function for making an invalid data error
62+
pub fn external(message: &str) -> Self {
63+
Self {
64+
kind: EncodingErrorKind::External,
65+
message: message.to_string(),
66+
}
67+
}
68+
}
69+
70+
impl fmt::Display for EncodingError {
71+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72+
let prefix = match self.kind {
73+
EncodingErrorKind::OutOfBounds => "Compact encoding failed, out of bounds",
74+
EncodingErrorKind::Overflow => "Compact encoding failed, overflow",
75+
EncodingErrorKind::InvalidData => "Compact encoding failed, invalid data",
76+
EncodingErrorKind::External => {
77+
"An external error caused a compact encoding operation to fail"
78+
}
79+
};
80+
write!(f, "{}: {}", prefix, self.message,)
81+
}
82+
}
83+
84+
impl From<EncodingError> for std::io::Error {
85+
fn from(e: EncodingError) -> Self {
86+
match e.kind {
87+
EncodingErrorKind::InvalidData => {
88+
std::io::Error::new(std::io::ErrorKind::InvalidData, format!("{e}"))
89+
}
90+
_ => std::io::Error::new(std::io::ErrorKind::Other, format!("{e}")),
91+
}
92+
}
93+
}

src/fixedwidth.rs

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
//! Allow encoding of unsigned ints in a fixed width way, instead of the default variable width.
2+
//!
3+
//! Why? Because the default [`CompactEncoding`] implementation for unsigned integers uses a
4+
//! variable width encoding. However sometimes want them encoded with a fixed width,
5+
//! [`FixedWidthEncoding`] lets us do this. To fixed width encode an unsigned integrer simply call
6+
//! [`FixedWidthEncoding::as_fixed_width`] on it. Like this:
7+
//! ```
8+
//! # use compact_encoding::EncodingError;
9+
//! use compact_encoding::{to_encoded_bytes, FixedWidthEncoding};
10+
//! let buff = to_encoded_bytes!(42u32.as_fixed_width());
11+
//! assert_eq!(buff, [42, 0, 0, 0].into());
12+
//! // vs variable width
13+
//! let buff = to_encoded_bytes!(42u32);
14+
//! assert_eq!(buff, [42].into());
15+
//! # Ok::<(), Box<dyn std::error::Error>>(())
16+
//! ```
17+
//!
18+
//! Likewise when decoding decoding from a fixed width encoded buffer you use
19+
//! [`FixedWidthUint::decode`] which will decode to the underlying unsigned integer type.
20+
//! So: `FixedWidthUint<u32>::decode(buffer) -> u32`.
21+
//! Note that we also provide type aliases to make this more ergonomic:
22+
//! `FixedWidthU64 = FixedWidthUint<u64`.
23+
//!
24+
//! ```
25+
//! # use compact_encoding::EncodingError;
26+
//! use compact_encoding::{map_decode, FixedWidthU32, FixedWidthUint};
27+
//! let buff = vec![42, 0, 0, 0]; // 42_u32 fixed width encoded
28+
//!
29+
//! let ((decoded,), _) = map_decode!(&buff, [FixedWidthUint<u32>]);
30+
//! assert_eq!(decoded, 42); // NOT! FixedWidthUint(42_u32)
31+
//!
32+
//! assert_eq!(map_decode!(&buff, [FixedWidthU32]).0 .0, 42); // or using the alias
33+
//! # Ok::<(), Box<dyn std::error::Error>>(())
34+
//! ```
35+
36+
use crate::{
37+
decode_u32, decode_u64, encode_u32, encode_u64, CompactEncoding, EncodingError, U32_SIZE,
38+
U64_SIZE,
39+
};
40+
41+
/// Implents functionality needed to encode unisegned integrer in a fixed width way with
42+
/// [`CompactEncoding`]
43+
pub trait FixedWidthEncoding {
44+
/// The type we decode to
45+
// TODO could we just use T?
46+
type Decode;
47+
/// The size in bytes required to encode `self`.
48+
fn fw_encoded_size(&self) -> Result<usize, EncodingError>;
49+
50+
/// Encode `self` into `buffer` returning the remainder of `buffer`.
51+
fn fw_encode<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], EncodingError>;
52+
53+
/// Decode a value from the given `buffer` of the type specified by the `Decode` type parameter
54+
/// (which defaults to `Self`). Returns the decoded value and remaining undecoded bytes.
55+
fn fw_decode(buffer: &[u8]) -> Result<(Self::Decode, &[u8]), EncodingError>
56+
where
57+
Self: Sized;
58+
59+
/// Get a uint in a form that encodes to a fixed width
60+
fn as_fixed_width(&self) -> FixedWidthUint<Self> {
61+
FixedWidthUint(self)
62+
}
63+
}
64+
65+
/// A fixed width encodable unsigned integer
66+
#[derive(Debug)]
67+
pub struct FixedWidthUint<'a, T: FixedWidthEncoding + ?Sized>(&'a T);
68+
69+
impl<T: FixedWidthEncoding> CompactEncoding<T::Decode> for FixedWidthUint<'_, T> {
70+
fn encoded_size(&self) -> Result<usize, EncodingError> {
71+
self.0.fw_encoded_size()
72+
}
73+
74+
fn encode<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], EncodingError> {
75+
self.0.fw_encode(buffer)
76+
}
77+
78+
fn decode(buffer: &[u8]) -> Result<(T::Decode, &[u8]), EncodingError>
79+
where
80+
Self: Sized,
81+
{
82+
<T as FixedWidthEncoding>::fw_decode(buffer)
83+
}
84+
}
85+
86+
impl FixedWidthEncoding for u32 {
87+
type Decode = u32;
88+
89+
fn fw_encoded_size(&self) -> Result<usize, EncodingError> {
90+
Ok(U32_SIZE)
91+
}
92+
93+
fn fw_encode<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], EncodingError> {
94+
encode_u32(*self, buffer)
95+
}
96+
97+
fn fw_decode(buffer: &[u8]) -> Result<(Self::Decode, &[u8]), EncodingError>
98+
where
99+
Self: Sized,
100+
{
101+
decode_u32(buffer)
102+
}
103+
}
104+
impl FixedWidthEncoding for u64 {
105+
type Decode = u64;
106+
107+
fn fw_encoded_size(&self) -> Result<usize, EncodingError> {
108+
Ok(U64_SIZE)
109+
}
110+
111+
fn fw_encode<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], EncodingError> {
112+
encode_u64(*self, buffer)
113+
}
114+
115+
fn fw_decode(buffer: &[u8]) -> Result<(Self::Decode, &[u8]), EncodingError>
116+
where
117+
Self: Sized,
118+
{
119+
decode_u64(buffer)
120+
}
121+
}
122+
123+
/// A wrapper around [`u32`] to let us encoded/decode to/from a fixed width
124+
pub type FixedWidthU32<'a> = FixedWidthUint<'a, u32>;
125+
/// A wrapper around [`u64`] to let us encoded/decode to/from a fixed width
126+
pub type FixedWidthU64<'a> = FixedWidthUint<'a, u64>;
127+
#[cfg(test)]
128+
mod test {
129+
use crate::{map_decode, to_encoded_bytes};
130+
131+
use super::*;
132+
133+
#[test]
134+
fn fixed_width_u32() -> Result<(), EncodingError> {
135+
let x = 42u32;
136+
let fixed_buff = to_encoded_bytes!(x.as_fixed_width());
137+
let var_buff = to_encoded_bytes!(x);
138+
assert_eq!(fixed_buff, [42_u8, 0, 0, 0].into());
139+
assert_eq!(var_buff, [42_u8].into());
140+
141+
let ((fixed_dec,), rest) = map_decode!(&fixed_buff, [FixedWidthU32]);
142+
assert!(rest.is_empty());
143+
assert_eq!(fixed_dec, x);
144+
145+
let ((var_dec,), rest) = map_decode!(&var_buff, [u32]);
146+
assert!(rest.is_empty());
147+
assert_eq!(var_dec, x);
148+
Ok(())
149+
}
150+
151+
#[test]
152+
fn fixed_width_u64() -> Result<(), EncodingError> {
153+
let x = 42u64;
154+
let fixed_buff = to_encoded_bytes!(x.as_fixed_width());
155+
let var_buff = to_encoded_bytes!(x);
156+
assert_eq!(fixed_buff, [42, 0, 0, 0, 0, 0, 0, 0].into());
157+
assert_eq!(var_buff, [42].into());
158+
159+
let ((fixed_dec,), rest) = map_decode!(&fixed_buff, [FixedWidthU64]);
160+
assert!(rest.is_empty());
161+
assert_eq!(fixed_dec, x);
162+
Ok(())
163+
}
164+
}

0 commit comments

Comments
 (0)