From 684f2562b1fd2fd8ca76f0d26fcfdcbb7413b474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lena=20Hellstr=C3=B6m?= Date: Sun, 17 Oct 2021 21:07:31 +0200 Subject: [PATCH] Config rewrite (#412) Rewrite the config system to be slightly more friendly to adding new options --- src/config.rs | 185 ++++++++++++++++++------------------- src/de/decoder.rs | 2 +- src/de/impls.rs | 5 +- src/enc/encoder.rs | 2 +- src/enc/impls.rs | 5 +- src/features/impl_alloc.rs | 2 +- src/features/impl_std.rs | 4 +- src/lib.rs | 4 +- tests/std.rs | 2 +- tests/utils.rs | 16 ++-- 10 files changed, 116 insertions(+), 111 deletions(-) diff --git a/src/config.rs b/src/config.rs index f575ee7..4cd83c6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,11 +2,11 @@ //! //! *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}; -//! let config = Default +//! use bincode::config::{Config, Configuration}; +//! let config = Configuration::standard() //! // pick one of: //! .with_big_endian() //! .with_little_endian() @@ -23,7 +23,8 @@ pub(crate) use self::internal::*; 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: /// @@ -38,15 +39,48 @@ use core::marker::PhantomData; /// [with_variable_int_encoding]: #method.with_variable_int_encoding /// [skip_fixed_array_length]: #method.skip_fixed_array_length /// [write_fixed_array_length]: #method.write_fixed_array_length -pub trait Config: InternalConfig { +#[derive(Copy, Clone)] +pub struct Configuration { + _e: PhantomData, + _i: PhantomData, + _a: PhantomData, +} + +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 { + Self::generate() + } +} + +impl Configuration { + 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. - fn with_big_endian(self) -> BigEndian { - BigEndian { _pd: PhantomData } + pub fn with_big_endian(self) -> Configuration { + Self::generate() } /// Makes bincode encode all integer types in little endian. - fn with_little_endian(self) -> LittleEndian { - LittleEndian { _pd: PhantomData } + pub fn with_little_endian(self) -> Configuration { + Self::generate() } /// 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 /// language, they may be supported via the extension point given by the 255 byte. - fn with_variable_int_encoding(self) -> Varint { - Varint { _pd: PhantomData } + pub fn with_variable_int_encoding(self) -> Configuration { + Self::generate() } /// Fixed-size integer encoding. @@ -100,129 +134,89 @@ pub trait Config: InternalConfig { /// * Fixed size integers are encoded directly /// * Enum discriminants are encoded as u32 /// * Lengths and usize are encoded as u64 - fn with_fixed_int_encoding(self) -> Fixint { - Fixint { _pd: PhantomData } + pub fn with_fixed_int_encoding(self) -> Configuration { + Self::generate() } /// Skip writing the length of fixed size arrays (`[u8; N]`) before writing the array - fn skip_fixed_array_length(self) -> SkipFixedArrayLength { - SkipFixedArrayLength { _pd: PhantomData } + pub fn skip_fixed_array_length(self) -> Configuration { + Self::generate() } /// Write the length of fixed size arrays (`[u8; N]`) before writing the array - fn write_fixed_array_length(self) -> WriteFixedArrayLength { - WriteFixedArrayLength { _pd: PhantomData } + pub fn write_fixed_array_length(self) -> Configuration { + Self::generate() } } -impl 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: -/// - Little endian -/// - 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 = None; - const ALLOW_TRAILING: bool = true; - const SKIP_FIXED_ARRAY_LENGTH: bool = true; +impl Config for T where + T: InternalEndianConfig + InternalArrayLengthConfig + InternalIntEncodingConfig + Copy + Clone +{ } #[doc(hidden)] #[derive(Copy, Clone)] -pub struct BigEndian { - _pd: PhantomData, -} +pub struct BigEndian {} -impl InternalConfig for BigEndian { +impl InternalEndianConfig for BigEndian { const ENDIAN: Endian = Endian::Big; - const INT_ENCODING: IntEncoding = C::INT_ENCODING; - const LIMIT: Option = C::LIMIT; - const ALLOW_TRAILING: bool = C::ALLOW_TRAILING; - const SKIP_FIXED_ARRAY_LENGTH: bool = C::SKIP_FIXED_ARRAY_LENGTH; } #[doc(hidden)] #[derive(Copy, Clone)] -pub struct LittleEndian { - _pd: PhantomData, -} +pub struct LittleEndian {} -impl InternalConfig for LittleEndian { +impl InternalEndianConfig for LittleEndian { const ENDIAN: Endian = Endian::Little; - const INT_ENCODING: IntEncoding = C::INT_ENCODING; - const LIMIT: Option = C::LIMIT; - const ALLOW_TRAILING: bool = C::ALLOW_TRAILING; - const SKIP_FIXED_ARRAY_LENGTH: bool = C::SKIP_FIXED_ARRAY_LENGTH; } #[doc(hidden)] #[derive(Copy, Clone)] -pub struct Fixint { - _pd: PhantomData, -} +pub struct Fixint {} -impl InternalConfig for Fixint { - const ENDIAN: Endian = C::ENDIAN; +impl InternalIntEncodingConfig for Fixint { const INT_ENCODING: IntEncoding = IntEncoding::Fixed; - const LIMIT: Option = C::LIMIT; - const ALLOW_TRAILING: bool = C::ALLOW_TRAILING; - const SKIP_FIXED_ARRAY_LENGTH: bool = C::SKIP_FIXED_ARRAY_LENGTH; } #[doc(hidden)] #[derive(Copy, Clone)] -pub struct Varint { - _pd: PhantomData, -} +pub struct Varint {} -impl InternalConfig for Varint { - const ENDIAN: Endian = C::ENDIAN; +impl InternalIntEncodingConfig for Varint { const INT_ENCODING: IntEncoding = IntEncoding::Variable; - const LIMIT: Option = C::LIMIT; - const ALLOW_TRAILING: bool = C::ALLOW_TRAILING; - const SKIP_FIXED_ARRAY_LENGTH: bool = C::SKIP_FIXED_ARRAY_LENGTH; } #[doc(hidden)] #[derive(Copy, Clone)] -pub struct SkipFixedArrayLength { - _pd: PhantomData, -} +pub struct SkipFixedArrayLength {} -impl InternalConfig for SkipFixedArrayLength { - const ENDIAN: Endian = C::ENDIAN; - const INT_ENCODING: IntEncoding = C::INT_ENCODING; - const LIMIT: Option = C::LIMIT; - const ALLOW_TRAILING: bool = C::ALLOW_TRAILING; +impl InternalArrayLengthConfig for SkipFixedArrayLength { const SKIP_FIXED_ARRAY_LENGTH: bool = true; } #[doc(hidden)] #[derive(Copy, Clone)] -pub struct WriteFixedArrayLength { - _pd: PhantomData, -} +pub struct WriteFixedArrayLength {} -impl InternalConfig for WriteFixedArrayLength { - const ENDIAN: Endian = C::ENDIAN; - const INT_ENCODING: IntEncoding = C::INT_ENCODING; - const LIMIT: Option = C::LIMIT; - const ALLOW_TRAILING: bool = C::ALLOW_TRAILING; +impl InternalArrayLengthConfig for WriteFixedArrayLength { const SKIP_FIXED_ARRAY_LENGTH: bool = false; } mod internal { - pub trait InternalConfig: Copy + Clone { + use super::Configuration; + + pub trait InternalEndianConfig { const ENDIAN: Endian; - const INT_ENCODING: IntEncoding; - const LIMIT: Option; - const ALLOW_TRAILING: bool; - const SKIP_FIXED_ARRAY_LENGTH: bool; + } + + impl InternalEndianConfig for Configuration { + const ENDIAN: Endian = E::ENDIAN; } #[derive(PartialEq, Eq)] @@ -231,20 +225,25 @@ mod internal { Big, } + pub trait InternalIntEncodingConfig { + const INT_ENCODING: IntEncoding; + } + + impl InternalIntEncodingConfig for Configuration { + const INT_ENCODING: IntEncoding = I::INT_ENCODING; + } + #[derive(PartialEq, Eq)] pub enum IntEncoding { Fixed, Variable, } - impl<'a, C: InternalConfig> InternalConfig for &'a mut C - where - &'a mut C: Copy + Clone, - { - const ENDIAN: Endian = C::ENDIAN; - const INT_ENCODING: IntEncoding = C::INT_ENCODING; - const LIMIT: Option = C::LIMIT; - const ALLOW_TRAILING: bool = C::ALLOW_TRAILING; - const SKIP_FIXED_ARRAY_LENGTH: bool = C::SKIP_FIXED_ARRAY_LENGTH; + pub trait InternalArrayLengthConfig { + const SKIP_FIXED_ARRAY_LENGTH: bool; + } + + impl InternalArrayLengthConfig for Configuration { + const SKIP_FIXED_ARRAY_LENGTH: bool = A::SKIP_FIXED_ARRAY_LENGTH; } } diff --git a/src/de/decoder.rs b/src/de/decoder.rs index 0955e19..fddc399 100644 --- a/src/de/decoder.rs +++ b/src/de/decoder.rs @@ -18,7 +18,7 @@ use crate::config::Config; /// # let some_reader = bincode::de::read::SliceReader::new(slice); /// use bincode::de::{DecoderImpl, Decode}; /// 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 /// let value = u32::decode(&mut decoder).unwrap(); /// ``` diff --git a/src/de/impls.rs b/src/de/impls.rs index 39fbaa3..3dccb96 100644 --- a/src/de/impls.rs +++ b/src/de/impls.rs @@ -3,7 +3,10 @@ use super::{ BorrowDecode, BorrowDecoder, Decode, Decoder, }; use crate::{ - config::{Endian, IntEncoding, InternalConfig}, + config::{ + Endian, IntEncoding, InternalArrayLengthConfig, InternalEndianConfig, + InternalIntEncodingConfig, + }, error::{DecodeError, IntegerType}, }; use core::{ diff --git a/src/enc/encoder.rs b/src/enc/encoder.rs index 135347f..8525b83 100644 --- a/src/enc/encoder.rs +++ b/src/enc/encoder.rs @@ -14,7 +14,7 @@ use crate::config::Config; /// ``` /// # use bincode::enc::{write::SliceWriter, EncoderImpl, Encode}; /// # 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 mut encoder = EncoderImpl::new(SliceWriter::new(slice), config); /// // this u32 can be any Encodable diff --git a/src/enc/impls.rs b/src/enc/impls.rs index 4a567a7..82645df 100644 --- a/src/enc/impls.rs +++ b/src/enc/impls.rs @@ -1,6 +1,9 @@ use super::{write::Writer, Encode, Encoder}; use crate::{ - config::{Endian, IntEncoding, InternalConfig}, + config::{ + Endian, IntEncoding, InternalArrayLengthConfig, InternalEndianConfig, + InternalIntEncodingConfig, + }, error::EncodeError, }; use core::{ diff --git a/src/features/impl_alloc.rs b/src/features/impl_alloc.rs index d146b4f..fd701c1 100644 --- a/src/features/impl_alloc.rs +++ b/src/features/impl_alloc.rs @@ -24,7 +24,7 @@ impl enc::write::Writer for VecWriter { /// Encode the given value into a `Vec`. #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] pub fn encode_to_vec(val: E) -> Result, 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` with the given `Config`. See the [config] module for more information. diff --git a/src/features/impl_std.rs b/src/features/impl_std.rs index ad58835..2b6c59b 100644 --- a/src/features/impl_std.rs +++ b/src/features/impl_std.rs @@ -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`. #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn decode_from(src: &mut R) -> Result { - 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`. @@ -47,7 +47,7 @@ pub fn encode_into_write( val: E, dst: &mut W, ) -> Result { - 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. diff --git a/src/lib.rs b/src/lib.rs index 20ccf30..4c6f837 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,7 +52,7 @@ pub fn encode_into_slice( val: E, dst: &mut [u8], ) -> Result { - 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. @@ -77,7 +77,7 @@ pub fn encode_into_slice_with_config( pub fn decode<'__de, D: de::BorrowDecode<'__de>>( src: &'__de [u8], ) -> Result { - decode_with_config(src, config::Default) + decode_with_config(src, config::Configuration::standard()) } /// Attempt to decode a given type `D` from the given slice. diff --git a/tests/std.rs b/tests/std.rs index 0781bec..09eff68 100644 --- a/tests/std.rs +++ b/tests/std.rs @@ -91,7 +91,7 @@ fn test_std_commons() { }); // Borrowed values - let config = bincode::config::Default; + let config = bincode::config::Configuration::standard(); let mut buffer = [0u8; 1024]; // &CStr diff --git a/tests/utils.rs b/tests/utils.rs index 383377b..5f4f280 100644 --- a/tests/utils.rs +++ b/tests/utils.rs @@ -34,7 +34,7 @@ where // A matrix of each different config option possible the_same_with_config( &element, - config::Default + config::Configuration::standard() .with_little_endian() .with_fixed_int_encoding() .skip_fixed_array_length(), @@ -42,7 +42,7 @@ where ); the_same_with_config( &element, - config::Default + config::Configuration::standard() .with_big_endian() .with_fixed_int_encoding() .skip_fixed_array_length(), @@ -50,7 +50,7 @@ where ); the_same_with_config( &element, - config::Default + config::Configuration::standard() .with_little_endian() .with_variable_int_encoding() .skip_fixed_array_length(), @@ -58,7 +58,7 @@ where ); the_same_with_config( &element, - config::Default + config::Configuration::standard() .with_big_endian() .with_variable_int_encoding() .skip_fixed_array_length(), @@ -66,7 +66,7 @@ where ); the_same_with_config( &element, - config::Default + config::Configuration::standard() .with_little_endian() .with_fixed_int_encoding() .write_fixed_array_length(), @@ -74,7 +74,7 @@ where ); the_same_with_config( &element, - config::Default + config::Configuration::standard() .with_big_endian() .with_fixed_int_encoding() .write_fixed_array_length(), @@ -82,7 +82,7 @@ where ); the_same_with_config( &element, - config::Default + config::Configuration::standard() .with_little_endian() .with_variable_int_encoding() .write_fixed_array_length(), @@ -90,7 +90,7 @@ where ); the_same_with_config( &element, - config::Default + config::Configuration::standard() .with_big_endian() .with_variable_int_encoding() .write_fixed_array_length(),