Config rewrite (#412)

Rewrite the config system to be slightly more friendly to adding new options
This commit is contained in:
Lena Hellström 2021-10-17 21:07:31 +02:00 committed by GitHub
parent 2fd2a8d8e2
commit 684f2562b1
10 changed files with 116 additions and 111 deletions

View File

@ -2,11 +2,11 @@
//! //!
//! *Important* make sure you use the same config for encoding and decoding, or else bincode will not work properly. //! *Important* make sure you use the same config for encoding and decoding, or else bincode will not work properly.
//! //!
//! To use a config, first create a type of [struct@Default]. This type will implement trait [Config] for further configuration. //! To use a config, first create a type of [Configuration]. This type will implement trait [Config] for use with bincode.
//! //!
//! ``` //! ```
//! use bincode::config::{Config, Default}; //! use bincode::config::{Config, Configuration};
//! let config = Default //! let config = Configuration::standard()
//! // pick one of: //! // pick one of:
//! .with_big_endian() //! .with_big_endian()
//! .with_little_endian() //! .with_little_endian()
@ -23,7 +23,8 @@
pub(crate) use self::internal::*; pub(crate) use self::internal::*;
use core::marker::PhantomData; use core::marker::PhantomData;
/// The config trait that is implemented by all types returned by this function, as well as [struct@Default]. /// The Configuration struct is used to build bincode configurations. The [Config] trait is implemented
/// by this struct when a valid configuration has been constructed.
/// ///
/// The following methods are mutually exclusive and will overwrite each other. The last call to one of these methods determines the behavior of the configuration: /// The following methods are mutually exclusive and will overwrite each other. The last call to one of these methods determines the behavior of the configuration:
/// ///
@ -38,15 +39,48 @@ use core::marker::PhantomData;
/// [with_variable_int_encoding]: #method.with_variable_int_encoding /// [with_variable_int_encoding]: #method.with_variable_int_encoding
/// [skip_fixed_array_length]: #method.skip_fixed_array_length /// [skip_fixed_array_length]: #method.skip_fixed_array_length
/// [write_fixed_array_length]: #method.write_fixed_array_length /// [write_fixed_array_length]: #method.write_fixed_array_length
pub trait Config: InternalConfig { #[derive(Copy, Clone)]
pub struct Configuration<E = LittleEndian, I = Varint, A = SkipFixedArrayLength> {
_e: PhantomData<E>,
_i: PhantomData<I>,
_a: PhantomData<A>,
}
impl Configuration {
/// The default config. By default this will be:
/// - Little endian
/// - Variable int encoding
/// - Skip fixed array length
pub fn standard() -> Self {
Self::generate()
}
/// Creates the "legacy" default config. This is the default config that was present in bincode 1.0
/// - Little endian
/// - Fixed int length encoding
/// - Write array lengths
pub fn legacy() -> Configuration<LittleEndian, Fixint, WriteFixedArrayLength> {
Self::generate()
}
}
impl<E, I, A> Configuration<E, I, A> {
fn generate<_E, _I, _A>() -> Configuration<_E, _I, _A> {
Configuration {
_e: PhantomData,
_i: PhantomData,
_a: PhantomData,
}
}
/// Makes bincode encode all integer types in big endian. /// Makes bincode encode all integer types in big endian.
fn with_big_endian(self) -> BigEndian<Self> { pub fn with_big_endian(self) -> Configuration<BigEndian, I, A> {
BigEndian { _pd: PhantomData } Self::generate()
} }
/// Makes bincode encode all integer types in little endian. /// Makes bincode encode all integer types in little endian.
fn with_little_endian(self) -> LittleEndian<Self> { pub fn with_little_endian(self) -> Configuration<LittleEndian, I, A> {
LittleEndian { _pd: PhantomData } Self::generate()
} }
/// Makes bincode encode all integer types with a variable integer encoding. /// Makes bincode encode all integer types with a variable integer encoding.
@ -91,8 +125,8 @@ pub trait Config: InternalConfig {
/// ///
/// Note that u256 and the like are unsupported by this format; if and when they are added to the /// Note that u256 and the like are unsupported by this format; if and when they are added to the
/// language, they may be supported via the extension point given by the 255 byte. /// language, they may be supported via the extension point given by the 255 byte.
fn with_variable_int_encoding(self) -> Varint<Self> { pub fn with_variable_int_encoding(self) -> Configuration<E, Varint, A> {
Varint { _pd: PhantomData } Self::generate()
} }
/// Fixed-size integer encoding. /// Fixed-size integer encoding.
@ -100,129 +134,89 @@ pub trait Config: InternalConfig {
/// * Fixed size integers are encoded directly /// * Fixed size integers are encoded directly
/// * Enum discriminants are encoded as u32 /// * Enum discriminants are encoded as u32
/// * Lengths and usize are encoded as u64 /// * Lengths and usize are encoded as u64
fn with_fixed_int_encoding(self) -> Fixint<Self> { pub fn with_fixed_int_encoding(self) -> Configuration<E, Fixint, A> {
Fixint { _pd: PhantomData } Self::generate()
} }
/// Skip writing the length of fixed size arrays (`[u8; N]`) before writing the array /// Skip writing the length of fixed size arrays (`[u8; N]`) before writing the array
fn skip_fixed_array_length(self) -> SkipFixedArrayLength<Self> { pub fn skip_fixed_array_length(self) -> Configuration<E, I, SkipFixedArrayLength> {
SkipFixedArrayLength { _pd: PhantomData } Self::generate()
} }
/// Write the length of fixed size arrays (`[u8; N]`) before writing the array /// Write the length of fixed size arrays (`[u8; N]`) before writing the array
fn write_fixed_array_length(self) -> WriteFixedArrayLength<Self> { pub fn write_fixed_array_length(self) -> Configuration<E, I, WriteFixedArrayLength> {
WriteFixedArrayLength { _pd: PhantomData } Self::generate()
} }
} }
impl<T: InternalConfig> Config for T {} /// Indicates a type is valid for controlling the bincode configuration
pub trait Config:
InternalEndianConfig + InternalArrayLengthConfig + InternalIntEncodingConfig + Copy + Clone
{
}
/// The default config. By default this will be: impl<T> Config for T where
/// - Little endian T: InternalEndianConfig + InternalArrayLengthConfig + InternalIntEncodingConfig + Copy + Clone
/// - Variable int encoding {
/// - Skip fixed array length
#[derive(Copy, Clone)]
pub struct Default;
impl InternalConfig for Default {
const ENDIAN: Endian = Endian::Little;
const INT_ENCODING: IntEncoding = IntEncoding::Variable;
const LIMIT: Option<u64> = None;
const ALLOW_TRAILING: bool = true;
const SKIP_FIXED_ARRAY_LENGTH: bool = true;
} }
#[doc(hidden)] #[doc(hidden)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct BigEndian<C: Config> { pub struct BigEndian {}
_pd: PhantomData<C>,
}
impl<C: InternalConfig> InternalConfig for BigEndian<C> { impl InternalEndianConfig for BigEndian {
const ENDIAN: Endian = Endian::Big; const ENDIAN: Endian = Endian::Big;
const INT_ENCODING: IntEncoding = C::INT_ENCODING;
const LIMIT: Option<u64> = C::LIMIT;
const ALLOW_TRAILING: bool = C::ALLOW_TRAILING;
const SKIP_FIXED_ARRAY_LENGTH: bool = C::SKIP_FIXED_ARRAY_LENGTH;
} }
#[doc(hidden)] #[doc(hidden)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct LittleEndian<C: Config> { pub struct LittleEndian {}
_pd: PhantomData<C>,
}
impl<C: InternalConfig> InternalConfig for LittleEndian<C> { impl InternalEndianConfig for LittleEndian {
const ENDIAN: Endian = Endian::Little; const ENDIAN: Endian = Endian::Little;
const INT_ENCODING: IntEncoding = C::INT_ENCODING;
const LIMIT: Option<u64> = C::LIMIT;
const ALLOW_TRAILING: bool = C::ALLOW_TRAILING;
const SKIP_FIXED_ARRAY_LENGTH: bool = C::SKIP_FIXED_ARRAY_LENGTH;
} }
#[doc(hidden)] #[doc(hidden)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct Fixint<C: Config> { pub struct Fixint {}
_pd: PhantomData<C>,
}
impl<C: InternalConfig> InternalConfig for Fixint<C> { impl InternalIntEncodingConfig for Fixint {
const ENDIAN: Endian = C::ENDIAN;
const INT_ENCODING: IntEncoding = IntEncoding::Fixed; const INT_ENCODING: IntEncoding = IntEncoding::Fixed;
const LIMIT: Option<u64> = C::LIMIT;
const ALLOW_TRAILING: bool = C::ALLOW_TRAILING;
const SKIP_FIXED_ARRAY_LENGTH: bool = C::SKIP_FIXED_ARRAY_LENGTH;
} }
#[doc(hidden)] #[doc(hidden)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct Varint<C: Config> { pub struct Varint {}
_pd: PhantomData<C>,
}
impl<C: InternalConfig> InternalConfig for Varint<C> { impl InternalIntEncodingConfig for Varint {
const ENDIAN: Endian = C::ENDIAN;
const INT_ENCODING: IntEncoding = IntEncoding::Variable; const INT_ENCODING: IntEncoding = IntEncoding::Variable;
const LIMIT: Option<u64> = C::LIMIT;
const ALLOW_TRAILING: bool = C::ALLOW_TRAILING;
const SKIP_FIXED_ARRAY_LENGTH: bool = C::SKIP_FIXED_ARRAY_LENGTH;
} }
#[doc(hidden)] #[doc(hidden)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct SkipFixedArrayLength<C: Config> { pub struct SkipFixedArrayLength {}
_pd: PhantomData<C>,
}
impl<C: InternalConfig> InternalConfig for SkipFixedArrayLength<C> { impl InternalArrayLengthConfig for SkipFixedArrayLength {
const ENDIAN: Endian = C::ENDIAN;
const INT_ENCODING: IntEncoding = C::INT_ENCODING;
const LIMIT: Option<u64> = C::LIMIT;
const ALLOW_TRAILING: bool = C::ALLOW_TRAILING;
const SKIP_FIXED_ARRAY_LENGTH: bool = true; const SKIP_FIXED_ARRAY_LENGTH: bool = true;
} }
#[doc(hidden)] #[doc(hidden)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct WriteFixedArrayLength<C: Config> { pub struct WriteFixedArrayLength {}
_pd: PhantomData<C>,
}
impl<C: InternalConfig> InternalConfig for WriteFixedArrayLength<C> { impl InternalArrayLengthConfig for WriteFixedArrayLength {
const ENDIAN: Endian = C::ENDIAN;
const INT_ENCODING: IntEncoding = C::INT_ENCODING;
const LIMIT: Option<u64> = C::LIMIT;
const ALLOW_TRAILING: bool = C::ALLOW_TRAILING;
const SKIP_FIXED_ARRAY_LENGTH: bool = false; const SKIP_FIXED_ARRAY_LENGTH: bool = false;
} }
mod internal { mod internal {
pub trait InternalConfig: Copy + Clone { use super::Configuration;
pub trait InternalEndianConfig {
const ENDIAN: Endian; const ENDIAN: Endian;
const INT_ENCODING: IntEncoding; }
const LIMIT: Option<u64>;
const ALLOW_TRAILING: bool; impl<E: InternalEndianConfig, I, A> InternalEndianConfig for Configuration<E, I, A> {
const SKIP_FIXED_ARRAY_LENGTH: bool; const ENDIAN: Endian = E::ENDIAN;
} }
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
@ -231,20 +225,25 @@ mod internal {
Big, Big,
} }
pub trait InternalIntEncodingConfig {
const INT_ENCODING: IntEncoding;
}
impl<E, I: InternalIntEncodingConfig, A> InternalIntEncodingConfig for Configuration<E, I, A> {
const INT_ENCODING: IntEncoding = I::INT_ENCODING;
}
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
pub enum IntEncoding { pub enum IntEncoding {
Fixed, Fixed,
Variable, Variable,
} }
impl<'a, C: InternalConfig> InternalConfig for &'a mut C pub trait InternalArrayLengthConfig {
where const SKIP_FIXED_ARRAY_LENGTH: bool;
&'a mut C: Copy + Clone, }
{
const ENDIAN: Endian = C::ENDIAN; impl<E, I, A: InternalArrayLengthConfig> InternalArrayLengthConfig for Configuration<E, I, A> {
const INT_ENCODING: IntEncoding = C::INT_ENCODING; const SKIP_FIXED_ARRAY_LENGTH: bool = A::SKIP_FIXED_ARRAY_LENGTH;
const LIMIT: Option<u64> = C::LIMIT;
const ALLOW_TRAILING: bool = C::ALLOW_TRAILING;
const SKIP_FIXED_ARRAY_LENGTH: bool = C::SKIP_FIXED_ARRAY_LENGTH;
} }
} }

View File

@ -18,7 +18,7 @@ use crate::config::Config;
/// # let some_reader = bincode::de::read::SliceReader::new(slice); /// # let some_reader = bincode::de::read::SliceReader::new(slice);
/// use bincode::de::{DecoderImpl, Decode}; /// use bincode::de::{DecoderImpl, Decode};
/// use bincode::config; /// use bincode::config;
/// let mut decoder = DecoderImpl::new(some_reader, config::Default); /// let mut decoder = DecoderImpl::new(some_reader, config::Configuration::standard());
/// // this u32 can be any Decode /// // this u32 can be any Decode
/// let value = u32::decode(&mut decoder).unwrap(); /// let value = u32::decode(&mut decoder).unwrap();
/// ``` /// ```

View File

@ -3,7 +3,10 @@ use super::{
BorrowDecode, BorrowDecoder, Decode, Decoder, BorrowDecode, BorrowDecoder, Decode, Decoder,
}; };
use crate::{ use crate::{
config::{Endian, IntEncoding, InternalConfig}, config::{
Endian, IntEncoding, InternalArrayLengthConfig, InternalEndianConfig,
InternalIntEncodingConfig,
},
error::{DecodeError, IntegerType}, error::{DecodeError, IntegerType},
}; };
use core::{ use core::{

View File

@ -14,7 +14,7 @@ use crate::config::Config;
/// ``` /// ```
/// # use bincode::enc::{write::SliceWriter, EncoderImpl, Encode}; /// # use bincode::enc::{write::SliceWriter, EncoderImpl, Encode};
/// # use bincode::config::{self, Config}; /// # use bincode::config::{self, Config};
/// # let config = config::Default.with_fixed_int_encoding().with_big_endian(); /// # let config = config::Configuration::standard().with_fixed_int_encoding().with_big_endian();
/// let slice: &mut [u8] = &mut [0, 0, 0, 0]; /// let slice: &mut [u8] = &mut [0, 0, 0, 0];
/// let mut encoder = EncoderImpl::new(SliceWriter::new(slice), config); /// let mut encoder = EncoderImpl::new(SliceWriter::new(slice), config);
/// // this u32 can be any Encodable /// // this u32 can be any Encodable

View File

@ -1,6 +1,9 @@
use super::{write::Writer, Encode, Encoder}; use super::{write::Writer, Encode, Encoder};
use crate::{ use crate::{
config::{Endian, IntEncoding, InternalConfig}, config::{
Endian, IntEncoding, InternalArrayLengthConfig, InternalEndianConfig,
InternalIntEncodingConfig,
},
error::EncodeError, error::EncodeError,
}; };
use core::{ use core::{

View File

@ -24,7 +24,7 @@ impl enc::write::Writer for VecWriter {
/// Encode the given value into a `Vec<u8>`. /// Encode the given value into a `Vec<u8>`.
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub fn encode_to_vec<E: enc::Encode>(val: E) -> Result<Vec<u8>, EncodeError> { pub fn encode_to_vec<E: enc::Encode>(val: E) -> Result<Vec<u8>, EncodeError> {
encode_to_vec_with_config(val, config::Default) encode_to_vec_with_config(val, config::Configuration::standard())
} }
/// Encode the given value into a `Vec<u8>` with the given `Config`. See the [config] module for more information. /// Encode the given value into a `Vec<u8>` with the given `Config`. See the [config] module for more information.

View File

@ -16,7 +16,7 @@ use std::{
/// Decode type `D` from the given reader. The reader can be any type that implements `std::io::Read`, e.g. `std::fs::File`. /// Decode type `D` from the given reader. The reader can be any type that implements `std::io::Read`, e.g. `std::fs::File`.
#[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn decode_from<D: Decode, R: std::io::Read>(src: &mut R) -> Result<D, DecodeError> { pub fn decode_from<D: Decode, R: std::io::Read>(src: &mut R) -> Result<D, DecodeError> {
decode_from_with_config(src, config::Default) decode_from_with_config(src, config::Configuration::standard())
} }
/// Decode type `D` from the given reader with the given `Config`. The reader can be any type that implements `std::io::Read`, e.g. `std::fs::File`. /// Decode type `D` from the given reader with the given `Config`. The reader can be any type that implements `std::io::Read`, e.g. `std::fs::File`.
@ -47,7 +47,7 @@ pub fn encode_into_write<E: Encode, W: std::io::Write>(
val: E, val: E,
dst: &mut W, dst: &mut W,
) -> Result<usize, EncodeError> { ) -> Result<usize, EncodeError> {
encode_into_write_with_config(val, dst, config::Default) encode_into_write_with_config(val, dst, config::Configuration::standard())
} }
/// Encode the given value into any type that implements `std::io::Write`, e.g. `std::fs::File`, with the given `Config`. See the [config] module for more information. /// Encode the given value into any type that implements `std::io::Write`, e.g. `std::fs::File`, with the given `Config`. See the [config] module for more information.

View File

@ -52,7 +52,7 @@ pub fn encode_into_slice<E: enc::Encode>(
val: E, val: E,
dst: &mut [u8], dst: &mut [u8],
) -> Result<usize, error::EncodeError> { ) -> Result<usize, error::EncodeError> {
encode_into_slice_with_config(val, dst, config::Default) encode_into_slice_with_config(val, dst, config::Configuration::standard())
} }
/// Encode the given value into the given slice. Returns the amount of bytes that have been written. /// Encode the given value into the given slice. Returns the amount of bytes that have been written.
@ -77,7 +77,7 @@ pub fn encode_into_slice_with_config<E: enc::Encode, C: Config>(
pub fn decode<'__de, D: de::BorrowDecode<'__de>>( pub fn decode<'__de, D: de::BorrowDecode<'__de>>(
src: &'__de [u8], src: &'__de [u8],
) -> Result<D, error::DecodeError> { ) -> Result<D, error::DecodeError> {
decode_with_config(src, config::Default) decode_with_config(src, config::Configuration::standard())
} }
/// Attempt to decode a given type `D` from the given slice. /// Attempt to decode a given type `D` from the given slice.

View File

@ -91,7 +91,7 @@ fn test_std_commons() {
}); });
// Borrowed values // Borrowed values
let config = bincode::config::Default; let config = bincode::config::Configuration::standard();
let mut buffer = [0u8; 1024]; let mut buffer = [0u8; 1024];
// &CStr // &CStr

View File

@ -34,7 +34,7 @@ where
// A matrix of each different config option possible // A matrix of each different config option possible
the_same_with_config( the_same_with_config(
&element, &element,
config::Default config::Configuration::standard()
.with_little_endian() .with_little_endian()
.with_fixed_int_encoding() .with_fixed_int_encoding()
.skip_fixed_array_length(), .skip_fixed_array_length(),
@ -42,7 +42,7 @@ where
); );
the_same_with_config( the_same_with_config(
&element, &element,
config::Default config::Configuration::standard()
.with_big_endian() .with_big_endian()
.with_fixed_int_encoding() .with_fixed_int_encoding()
.skip_fixed_array_length(), .skip_fixed_array_length(),
@ -50,7 +50,7 @@ where
); );
the_same_with_config( the_same_with_config(
&element, &element,
config::Default config::Configuration::standard()
.with_little_endian() .with_little_endian()
.with_variable_int_encoding() .with_variable_int_encoding()
.skip_fixed_array_length(), .skip_fixed_array_length(),
@ -58,7 +58,7 @@ where
); );
the_same_with_config( the_same_with_config(
&element, &element,
config::Default config::Configuration::standard()
.with_big_endian() .with_big_endian()
.with_variable_int_encoding() .with_variable_int_encoding()
.skip_fixed_array_length(), .skip_fixed_array_length(),
@ -66,7 +66,7 @@ where
); );
the_same_with_config( the_same_with_config(
&element, &element,
config::Default config::Configuration::standard()
.with_little_endian() .with_little_endian()
.with_fixed_int_encoding() .with_fixed_int_encoding()
.write_fixed_array_length(), .write_fixed_array_length(),
@ -74,7 +74,7 @@ where
); );
the_same_with_config( the_same_with_config(
&element, &element,
config::Default config::Configuration::standard()
.with_big_endian() .with_big_endian()
.with_fixed_int_encoding() .with_fixed_int_encoding()
.write_fixed_array_length(), .write_fixed_array_length(),
@ -82,7 +82,7 @@ where
); );
the_same_with_config( the_same_with_config(
&element, &element,
config::Default config::Configuration::standard()
.with_little_endian() .with_little_endian()
.with_variable_int_encoding() .with_variable_int_encoding()
.write_fixed_array_length(), .write_fixed_array_length(),
@ -90,7 +90,7 @@ where
); );
the_same_with_config( the_same_with_config(
&element, &element,
config::Default config::Configuration::standard()
.with_big_endian() .with_big_endian()
.with_variable_int_encoding() .with_variable_int_encoding()
.write_fixed_array_length(), .write_fixed_array_length(),