diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 317da29..e2e83f1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -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 diff --git a/Cargo.toml b/Cargo.toml index 829b0c9..be92f71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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. @@ -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" diff --git a/fuzz/travis-fuzz.sh b/fuzz/travis-fuzz.sh index ff44d25..5782ea7 100755 --- a/fuzz/travis-fuzz.sh +++ b/fuzz/travis-fuzz.sh @@ -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%.*}" diff --git a/src/lib.rs b/src/lib.rs index eca88d8..8689b75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2498,3 +2498,106 @@ impl Clone for ConstNonNull { } impl Copy for ConstNonNull {} + +#[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 Decode for SmallVec +where + A: Array, + A::Item: Decode, +{ + fn decode>(decoder: &mut D) -> Result { + use core::convert::TryInto; + let len = u64::decode(decoder)?; + let len = len.try_into().map_err(|_| DecodeError::OutsideUsizeRange(len))?; + decoder.claim_container_read::(len)?; + + let mut vec = SmallVec::with_capacity(len); + if unty::type_equal::() { + // 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::()); + vec.push(A::Item::decode(decoder)?); + } + } + Ok(vec) + } +} + +#[cfg(feature = "impl_bincode")] +impl<'de, A, Context> BorrowDecode<'de, Context> for SmallVec +where + A: Array, + A::Item: BorrowDecode<'de, Context>, +{ + fn borrow_decode>(decoder: &mut D) -> Result { + use core::convert::TryInto; + let len = u64::decode(decoder)?; + let len = len.try_into().map_err(|_| DecodeError::OutsideUsizeRange(len))?; + decoder.claim_container_read::(len)?; + + let mut vec = SmallVec::with_capacity(len); + if unty::type_equal::() { + // 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::()); + vec.push(A::Item::borrow_decode(decoder)?); + } + } + Ok(vec) + } +} + +#[cfg(feature = "impl_bincode")] +impl Encode for SmallVec +where + A: Array, + A::Item: Encode, +{ + fn encode(&self, encoder: &mut E) -> Result<(), EncodeError> { + (self.len() as u64).encode(encoder)?; + if unty::type_equal::() { + // 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(()) + } +} diff --git a/src/tests.rs b/src/tests.rs index 9ccaf32..88c5f32 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -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(); @@ -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::, _>(&buffer, config).unwrap(); + assert_eq!(bytes_written, bytes_read); + assert_eq!(small_vec, decoded); + let (decoded, bytes_read) = + bincode::borrow_decode_from_slice::, _>(&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::, _>(&buffer, config).unwrap(); + assert_eq!(bytes_written, bytes_read); + assert_eq!(small_vec, decoded); + let (decoded, bytes_read) = + bincode::borrow_decode_from_slice::, _>(&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::, _>(&buffer, config).unwrap(); + assert_eq!(bytes_written, bytes_read); + assert_eq!(small_vec, decoded); + let (decoded, bytes_read) = + bincode::borrow_decode_from_slice::, _>(&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::, _>(&buffer, config).unwrap(); + assert_eq!(bytes_written, bytes_read); + assert_eq!(small_vec, decoded); + let (decoded, bytes_read) = + bincode::borrow_decode_from_slice::, _>(&buffer, config).unwrap(); + assert_eq!(bytes_written, bytes_read); + assert_eq!(small_vec, decoded); +}