diff --git a/src/error.rs b/src/error.rs index e3e7969..5884c1b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -71,6 +71,13 @@ pub enum DecodeError { /// The length of the array found in the binary format. found: usize, }, + + /// The decoder tried to decode a `CStr` or `CString`, but the incoming data contained a 0 byte + #[cfg(feature = "std")] + CStrNulError { + /// The inner exception + inner: std::ffi::FromBytesWithNulError, + }, } /// Integer types. Used by [DecodeError]. These types have no purpose other than being shown in errors. diff --git a/src/features/impl_std.rs b/src/features/impl_std.rs index 9bfde7c..4099208 100644 --- a/src/features/impl_std.rs +++ b/src/features/impl_std.rs @@ -1,9 +1,10 @@ use crate::{ config::{self, Config}, - de::{read::Reader, Decodable, Decoder}, - enc::{write::Writer, Encodeable, Encoder}, + de::{read::Reader, BorrowDecodable, BorrowDecode, Decodable, Decode, Decoder}, + enc::{write::Writer, Encode, Encodeable, Encoder}, error::{DecodeError, EncodeError}, }; +use std::ffi::{CStr, CString}; /// Decode type `D` from the given reader. The reader can be any type that implements `std::io::Read`, e.g. `std::fs::File`. pub fn decode_from(src: &mut R) -> Result { @@ -71,3 +72,36 @@ impl<'storage, W: std::io::Write> Writer for IoWriter<'storage, W> { Ok(()) } } + +impl<'a> Encodeable for &'a CStr { + fn encode(&self, encoder: E) -> Result<(), EncodeError> { + self.to_bytes_with_nul().encode(encoder) + } +} + +impl<'de> BorrowDecodable<'de> for &'de CStr { + fn borrow_decode>(decoder: D) -> Result { + let bytes = <&[u8]>::borrow_decode(decoder)?; + CStr::from_bytes_with_nul(bytes).map_err(|e| DecodeError::CStrNulError { inner: e }) + } +} + +impl Encodeable for CString { + fn encode(&self, encoder: E) -> Result<(), EncodeError> { + self.as_bytes_with_nul().encode(encoder) + } +} + +impl Decodable for CString { + fn decode(decoder: D) -> Result { + // BlockedTODO: https://github.com/rust-lang/rust/issues/73179 + // use `from_vec_with_nul` instead, combined with: + // let bytes = std::vec::Vec::::decode(decoder)?; + + // now we have to allocate twice unfortunately + let vec: std::vec::Vec = std::vec::Vec::decode(decoder)?; + let cstr = + CStr::from_bytes_with_nul(&vec).map_err(|e| DecodeError::CStrNulError { inner: e })?; + Ok(cstr.into()) + } +} diff --git a/tests/std.rs b/tests/std.rs index da8b984..d9365a2 100644 --- a/tests/std.rs +++ b/tests/std.rs @@ -1,5 +1,9 @@ #![cfg(feature = "std")] +mod utils; + +use utils::the_same; + struct Foo { pub a: u32, pub b: u32, @@ -49,3 +53,17 @@ fn test_std_file() { assert_eq!(foo.a, 30); assert_eq!(foo.b, 50); } + +#[test] +fn test_std_commons() { + use std::ffi::{CStr, CString}; + the_same(CString::new("Hello world").unwrap()); + + let config = bincode::config::Default; + let cstr = CStr::from_bytes_with_nul(b"Hello world\0").unwrap(); + let mut buffer = [0u8; 1024]; + let len = bincode::encode_into_slice_with_config(cstr, &mut buffer, config).unwrap(); + let decoded: &CStr = bincode::decode_with_config(&mut buffer[..len], config).unwrap(); + + assert_eq!(cstr, decoded); +} diff --git a/tests/utils.rs b/tests/utils.rs index 69955b6..2823a6a 100644 --- a/tests/utils.rs +++ b/tests/utils.rs @@ -3,12 +3,7 @@ use core::fmt::Debug; fn the_same_with_config(element: V, config: C) where - V: bincode::enc::Encodeable - + for<'de> bincode::de::Decodable - + PartialEq - + Debug - + Clone - + 'static, + V: bincode::enc::Encodeable + bincode::de::Decodable + PartialEq + Debug + Clone + 'static, C: Config, { let mut buffer = [0u8; 1024]; @@ -26,12 +21,7 @@ where pub fn the_same(element: V) where - V: bincode::enc::Encodeable - + for<'de> bincode::de::Decodable - + PartialEq - + Debug - + Clone - + 'static, + V: bincode::enc::Encodeable + bincode::de::Decodable + PartialEq + Debug + Clone + 'static, { // A matrix of each different config option possible the_same_with_config(