Skip to content

Implement bincode2 encode/decode support for smallvec v1 #375

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 5, 2025
Merged
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
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ jobs:
os: [ubuntu-latest]
include:
- toolchain: stable
fuzz: 1
fuzz: 0
- toolchain: beta
fuzz: 1
fuzz: 0
- os: windows-latest
toolchain: nightly

Expand Down
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ specialization = []
may_dangle = []
drain_filter = []
drain_keep_rest = ["drain_filter"]
impl_bincode = ["bincode", "unty"]

# UNSTABLE FEATURES (requires Rust nightly)
# Enable to use the #[debugger_visualizer] attribute.
Expand All @@ -29,9 +30,11 @@ debugger_visualizer = []
serde = { version = "1", optional = true, default-features = false }
malloc_size_of = { version = "0.1", optional = true, default-features = false }
arbitrary = { version = "1", optional = true }
bincode = { version = "2", optional = true, default-features = false }
unty = { version = "0.0.4", optional = true, default-features = false }

[dev-dependencies]
bincode = "1.0.1"
bincode1 = { package = "bincode", version = "1.0.1" }
debugger_test = "0.1.0"
debugger_test_parser = "0.1.0"

Expand Down
2 changes: 1 addition & 1 deletion fuzz/travis-fuzz.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash
set -e
cargo install --force honggfuzz --version "^0.5.47"
cargo install --force honggfuzz --version 0.5.47
for TARGET in fuzz_targets/*; do
FILENAME=$(basename $TARGET)
FILE="${FILENAME%.*}"
Expand Down
103 changes: 103 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2498,3 +2498,106 @@ impl<T> Clone for ConstNonNull<T> {
}

impl<T> Copy for ConstNonNull<T> {}

#[cfg(feature = "impl_bincode")]
use bincode::{
de::{BorrowDecoder, Decode, Decoder, read::Reader},
enc::{Encode, Encoder, write::Writer},
error::{DecodeError, EncodeError},
BorrowDecode,
};

#[cfg(feature = "impl_bincode")]
impl<A, Context> Decode<Context> for SmallVec<A>
where
A: Array,
A::Item: Decode<Context>,
{
fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
use core::convert::TryInto;
let len = u64::decode(decoder)?;
let len = len.try_into().map_err(|_| DecodeError::OutsideUsizeRange(len))?;
decoder.claim_container_read::<A::Item>(len)?;

let mut vec = SmallVec::with_capacity(len);
if unty::type_equal::<A::Item, u8>() {
// Initialize the smallvec's buffer. Note that we need to do this through
// the raw pointer as we cannot name the type [u8; N] even though A::Item is u8.
let ptr = vec.as_mut_ptr();
// SAFETY: A::Item is u8 and the smallvec has been allocated with enough capacity
unsafe {
core::ptr::write_bytes(ptr, 0, len);
vec.set_len(len);
}
// Read the data into the smallvec's buffer.
let slice = vec.as_mut_slice();
// SAFETY: A::Item is u8
let slice = unsafe { core::mem::transmute::<&mut [A::Item], &mut [u8]>(slice) };
decoder.reader().read(slice)?;
} else {
for _ in 0..len {
decoder.unclaim_bytes_read(core::mem::size_of::<A::Item>());
vec.push(A::Item::decode(decoder)?);
}
}
Ok(vec)
}
}

#[cfg(feature = "impl_bincode")]
impl<'de, A, Context> BorrowDecode<'de, Context> for SmallVec<A>
where
A: Array,
A::Item: BorrowDecode<'de, Context>,
{
fn borrow_decode<D: BorrowDecoder<'de, Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
use core::convert::TryInto;
let len = u64::decode(decoder)?;
let len = len.try_into().map_err(|_| DecodeError::OutsideUsizeRange(len))?;
decoder.claim_container_read::<A::Item>(len)?;

let mut vec = SmallVec::with_capacity(len);
if unty::type_equal::<A::Item, u8>() {
// Initialize the smallvec's buffer. Note that we need to do this through
// the raw pointer as we cannot name the type [u8; N] even though A::Item is u8.
let ptr = vec.as_mut_ptr();
// SAFETY: A::Item is u8 and the smallvec has been allocated with enough capacity
unsafe {
core::ptr::write_bytes(ptr, 0, len);
vec.set_len(len);
}
// Read the data into the smallvec's buffer.
let slice = vec.as_mut_slice();
// SAFETY: A::Item is u8
let slice = unsafe { core::mem::transmute::<&mut [A::Item], &mut [u8]>(slice) };
decoder.reader().read(slice)?;
} else {
for _ in 0..len {
decoder.unclaim_bytes_read(core::mem::size_of::<A::Item>());
vec.push(A::Item::borrow_decode(decoder)?);
}
}
Ok(vec)
}
}

#[cfg(feature = "impl_bincode")]
impl<A> Encode for SmallVec<A>
where
A: Array,
A::Item: Encode,
{
fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
(self.len() as u64).encode(encoder)?;
if unty::type_equal::<A::Item, u8>() {
// Safety: A::Item is u8
let slice: &[u8] = unsafe { core::mem::transmute(self.as_slice()) };
encoder.writer().write(slice)?;
} else {
for item in self.iter() {
item.encode(encoder)?;
}
}
Ok(())
}
}
64 changes: 63 additions & 1 deletion src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -835,7 +835,7 @@ fn test_write() {
#[cfg(feature = "serde")]
#[test]
fn test_serde() {
use bincode::{config, deserialize};
use bincode1::{config, deserialize};
let mut small_vec: SmallVec<[i32; 2]> = SmallVec::new();
small_vec.push(1);
let encoded = config().limit(100).serialize(&small_vec).unwrap();
Expand Down Expand Up @@ -1072,3 +1072,65 @@ fn test_insert_out_of_bounds() {
let mut v: SmallVec<[i32; 4]> = SmallVec::new();
v.insert(10, 6);
}

#[cfg(feature = "impl_bincode")]
#[test]
fn test_bincode() {
let config = bincode::config::standard();
let mut small_vec: SmallVec<[i32; 2]> = SmallVec::new();
let mut buffer = [0u8; 128];
small_vec.push(1);
let bytes_written = bincode::encode_into_slice(&small_vec, &mut buffer, config).unwrap();
let (decoded, bytes_read) =
bincode::decode_from_slice::<SmallVec<[i32; 2]>, _>(&buffer, config).unwrap();
assert_eq!(bytes_written, bytes_read);
assert_eq!(small_vec, decoded);
let (decoded, bytes_read) =
bincode::borrow_decode_from_slice::<SmallVec<[i32; 2]>, _>(&buffer, config).unwrap();
assert_eq!(bytes_written, bytes_read);
assert_eq!(small_vec, decoded);
// Spill the vec
small_vec.push(2);
small_vec.push(3);
small_vec.push(4);
// Check again after spilling.
let bytes_written = bincode::encode_into_slice(&small_vec, &mut buffer, config).unwrap();
let (decoded, bytes_read) =
bincode::decode_from_slice::<SmallVec<[i32; 2]>, _>(&buffer, config).unwrap();
assert_eq!(bytes_written, bytes_read);
assert_eq!(small_vec, decoded);
let (decoded, bytes_read) =
bincode::borrow_decode_from_slice::<SmallVec<[i32; 2]>, _>(&buffer, config).unwrap();
assert_eq!(bytes_written, bytes_read);
assert_eq!(small_vec, decoded);
}

#[cfg(feature = "impl_bincode")]
#[test]
fn test_bincode_u8() {
let config = bincode::config::standard();
let mut small_vec: SmallVec<[u8; 16]> = SmallVec::new();
let mut buffer = [0u8; 128];
small_vec.extend_from_slice(b"testing test");
let bytes_written = bincode::encode_into_slice(&small_vec, &mut buffer, config).unwrap();
let (decoded, bytes_read) =
bincode::decode_from_slice::<SmallVec<[u8; 16]>, _>(&buffer, config).unwrap();
assert_eq!(bytes_written, bytes_read);
assert_eq!(small_vec, decoded);
let (decoded, bytes_read) =
bincode::borrow_decode_from_slice::<SmallVec<[u8; 16]>, _>(&buffer, config).unwrap();
assert_eq!(bytes_written, bytes_read);
assert_eq!(small_vec, decoded);
// Spill the vec
small_vec.extend_from_slice(b"some more testing");
// Check again after spilling.
let bytes_written = bincode::encode_into_slice(&small_vec, &mut buffer, config).unwrap();
let (decoded, bytes_read) =
bincode::decode_from_slice::<SmallVec<[u8; 16]>, _>(&buffer, config).unwrap();
assert_eq!(bytes_written, bytes_read);
assert_eq!(small_vec, decoded);
let (decoded, bytes_read) =
bincode::borrow_decode_from_slice::<SmallVec<[u8; 16]>, _>(&buffer, config).unwrap();
assert_eq!(bytes_written, bytes_read);
assert_eq!(small_vec, decoded);
}