Skip to content

Commit 35e7ffe

Browse files
markbtmbrubeck
authored andcommitted
Add support for bincode encode and decode
Add implementations for `bincode`'s `Encode`, `Decode` and `BorrowDecode`. Like `bincode` itself, we implement optimizations when the vector item is `u8` so that `SmallVec<[u8; N]>` can be encoded and decoded with just a memory copy.
1 parent 0d019cd commit 35e7ffe

File tree

3 files changed

+168
-0
lines changed

3 files changed

+168
-0
lines changed

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ specialization = []
2020
may_dangle = []
2121
drain_filter = []
2222
drain_keep_rest = ["drain_filter"]
23+
impl_bincode = ["bincode", "unty"]
2324

2425
# UNSTABLE FEATURES (requires Rust nightly)
2526
# Enable to use the #[debugger_visualizer] attribute.
@@ -29,6 +30,8 @@ debugger_visualizer = []
2930
serde = { version = "1", optional = true, default-features = false }
3031
malloc_size_of = { version = "0.1", optional = true, default-features = false }
3132
arbitrary = { version = "1", optional = true }
33+
bincode = { version = "2", optional = true, default-features = false }
34+
unty = { version = "0.0.4", optional = true, default-features = false }
3235

3336
[dev-dependencies]
3437
bincode1 = { package = "bincode", version = "1.0.1" }

src/lib.rs

+103
Original file line numberDiff line numberDiff line change
@@ -2498,3 +2498,106 @@ impl<T> Clone for ConstNonNull<T> {
24982498
}
24992499

25002500
impl<T> Copy for ConstNonNull<T> {}
2501+
2502+
#[cfg(feature = "impl_bincode")]
2503+
use bincode::{
2504+
de::{BorrowDecoder, Decode, Decoder, read::Reader},
2505+
enc::{Encode, Encoder, write::Writer},
2506+
error::{DecodeError, EncodeError},
2507+
BorrowDecode,
2508+
};
2509+
2510+
#[cfg(feature = "impl_bincode")]
2511+
impl<A, Context> Decode<Context> for SmallVec<A>
2512+
where
2513+
A: Array,
2514+
A::Item: Decode<Context>,
2515+
{
2516+
fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
2517+
use core::convert::TryInto;
2518+
let len = u64::decode(decoder)?;
2519+
let len = len.try_into().map_err(|_| DecodeError::OutsideUsizeRange(len))?;
2520+
decoder.claim_container_read::<A::Item>(len)?;
2521+
2522+
let mut vec = SmallVec::with_capacity(len);
2523+
if unty::type_equal::<A::Item, u8>() {
2524+
// Initialize the smallvec's buffer. Note that we need to do this through
2525+
// the raw pointer as we cannot name the type [u8; N] even though A::Item is u8.
2526+
let ptr = vec.as_mut_ptr();
2527+
// SAFETY: A::Item is u8 and the smallvec has been allocated with enough capacity
2528+
unsafe {
2529+
core::ptr::write_bytes(ptr, 0, len);
2530+
vec.set_len(len);
2531+
}
2532+
// Read the data into the smallvec's buffer.
2533+
let slice = vec.as_mut_slice();
2534+
// SAFETY: A::Item is u8
2535+
let slice = unsafe { core::mem::transmute::<&mut [A::Item], &mut [u8]>(slice) };
2536+
decoder.reader().read(slice)?;
2537+
} else {
2538+
for _ in 0..len {
2539+
decoder.unclaim_bytes_read(core::mem::size_of::<A::Item>());
2540+
vec.push(A::Item::decode(decoder)?);
2541+
}
2542+
}
2543+
Ok(vec)
2544+
}
2545+
}
2546+
2547+
#[cfg(feature = "impl_bincode")]
2548+
impl<'de, A, Context> BorrowDecode<'de, Context> for SmallVec<A>
2549+
where
2550+
A: Array,
2551+
A::Item: BorrowDecode<'de, Context>,
2552+
{
2553+
fn borrow_decode<D: BorrowDecoder<'de, Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
2554+
use core::convert::TryInto;
2555+
let len = u64::decode(decoder)?;
2556+
let len = len.try_into().map_err(|_| DecodeError::OutsideUsizeRange(len))?;
2557+
decoder.claim_container_read::<A::Item>(len)?;
2558+
2559+
let mut vec = SmallVec::with_capacity(len);
2560+
if unty::type_equal::<A::Item, u8>() {
2561+
// Initialize the smallvec's buffer. Note that we need to do this through
2562+
// the raw pointer as we cannot name the type [u8; N] even though A::Item is u8.
2563+
let ptr = vec.as_mut_ptr();
2564+
// SAFETY: A::Item is u8 and the smallvec has been allocated with enough capacity
2565+
unsafe {
2566+
core::ptr::write_bytes(ptr, 0, len);
2567+
vec.set_len(len);
2568+
}
2569+
// Read the data into the smallvec's buffer.
2570+
let slice = vec.as_mut_slice();
2571+
// SAFETY: A::Item is u8
2572+
let slice = unsafe { core::mem::transmute::<&mut [A::Item], &mut [u8]>(slice) };
2573+
decoder.reader().read(slice)?;
2574+
} else {
2575+
for _ in 0..len {
2576+
decoder.unclaim_bytes_read(core::mem::size_of::<A::Item>());
2577+
vec.push(A::Item::borrow_decode(decoder)?);
2578+
}
2579+
}
2580+
Ok(vec)
2581+
}
2582+
}
2583+
2584+
#[cfg(feature = "impl_bincode")]
2585+
impl<A> Encode for SmallVec<A>
2586+
where
2587+
A: Array,
2588+
A::Item: Encode,
2589+
{
2590+
fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
2591+
(self.len() as u64).encode(encoder)?;
2592+
if unty::type_equal::<A::Item, u8>() {
2593+
// Safety: A::Item is u8
2594+
let slice: &[u8] = unsafe { core::mem::transmute(self.as_slice()) };
2595+
encoder.writer().write(slice)?;
2596+
} else {
2597+
for item in self.iter() {
2598+
item.encode(encoder)?;
2599+
}
2600+
}
2601+
Ok(())
2602+
}
2603+
}

src/tests.rs

+62
Original file line numberDiff line numberDiff line change
@@ -1072,3 +1072,65 @@ fn test_insert_out_of_bounds() {
10721072
let mut v: SmallVec<[i32; 4]> = SmallVec::new();
10731073
v.insert(10, 6);
10741074
}
1075+
1076+
#[cfg(feature = "impl_bincode")]
1077+
#[test]
1078+
fn test_bincode() {
1079+
let config = bincode::config::standard();
1080+
let mut small_vec: SmallVec<[i32; 2]> = SmallVec::new();
1081+
let mut buffer = [0u8; 128];
1082+
small_vec.push(1);
1083+
let bytes_written = bincode::encode_into_slice(&small_vec, &mut buffer, config).unwrap();
1084+
let (decoded, bytes_read) =
1085+
bincode::decode_from_slice::<SmallVec<[i32; 2]>, _>(&buffer, config).unwrap();
1086+
assert_eq!(bytes_written, bytes_read);
1087+
assert_eq!(small_vec, decoded);
1088+
let (decoded, bytes_read) =
1089+
bincode::borrow_decode_from_slice::<SmallVec<[i32; 2]>, _>(&buffer, config).unwrap();
1090+
assert_eq!(bytes_written, bytes_read);
1091+
assert_eq!(small_vec, decoded);
1092+
// Spill the vec
1093+
small_vec.push(2);
1094+
small_vec.push(3);
1095+
small_vec.push(4);
1096+
// Check again after spilling.
1097+
let bytes_written = bincode::encode_into_slice(&small_vec, &mut buffer, config).unwrap();
1098+
let (decoded, bytes_read) =
1099+
bincode::decode_from_slice::<SmallVec<[i32; 2]>, _>(&buffer, config).unwrap();
1100+
assert_eq!(bytes_written, bytes_read);
1101+
assert_eq!(small_vec, decoded);
1102+
let (decoded, bytes_read) =
1103+
bincode::borrow_decode_from_slice::<SmallVec<[i32; 2]>, _>(&buffer, config).unwrap();
1104+
assert_eq!(bytes_written, bytes_read);
1105+
assert_eq!(small_vec, decoded);
1106+
}
1107+
1108+
#[cfg(feature = "impl_bincode")]
1109+
#[test]
1110+
fn test_bincode_u8() {
1111+
let config = bincode::config::standard();
1112+
let mut small_vec: SmallVec<[u8; 16]> = SmallVec::new();
1113+
let mut buffer = [0u8; 128];
1114+
small_vec.extend_from_slice(b"testing test");
1115+
let bytes_written = bincode::encode_into_slice(&small_vec, &mut buffer, config).unwrap();
1116+
let (decoded, bytes_read) =
1117+
bincode::decode_from_slice::<SmallVec<[u8; 16]>, _>(&buffer, config).unwrap();
1118+
assert_eq!(bytes_written, bytes_read);
1119+
assert_eq!(small_vec, decoded);
1120+
let (decoded, bytes_read) =
1121+
bincode::borrow_decode_from_slice::<SmallVec<[u8; 16]>, _>(&buffer, config).unwrap();
1122+
assert_eq!(bytes_written, bytes_read);
1123+
assert_eq!(small_vec, decoded);
1124+
// Spill the vec
1125+
small_vec.extend_from_slice(b"some more testing");
1126+
// Check again after spilling.
1127+
let bytes_written = bincode::encode_into_slice(&small_vec, &mut buffer, config).unwrap();
1128+
let (decoded, bytes_read) =
1129+
bincode::decode_from_slice::<SmallVec<[u8; 16]>, _>(&buffer, config).unwrap();
1130+
assert_eq!(bytes_written, bytes_read);
1131+
assert_eq!(small_vec, decoded);
1132+
let (decoded, bytes_read) =
1133+
bincode::borrow_decode_from_slice::<SmallVec<[u8; 16]>, _>(&buffer, config).unwrap();
1134+
assert_eq!(bytes_written, bytes_read);
1135+
assert_eq!(small_vec, decoded);
1136+
}

0 commit comments

Comments
 (0)