mirror of https://git.sr.ht/~stygianentity/bincode
406 lines
13 KiB
Rust
406 lines
13 KiB
Rust
//! `bincode` uses a Builder-pattern to configure the Serializers and Deserializers in this
|
|
//! crate. This means that if you need to customize the behavior of `bincode`, you should create an
|
|
//! instance of the `DefaultOptions` struct:
|
|
//!
|
|
//! ```rust
|
|
//! use bincode::Options;
|
|
//! let my_options = bincode::DefaultOptions::new();
|
|
//! ```
|
|
//!
|
|
//! # Options Struct vs bincode functions
|
|
//!
|
|
//! Due to historical reasons, the default options used by the `serialize()` and `deserialize()`
|
|
//! family of functions are different than the default options created by the `DefaultOptions` struct:
|
|
//!
|
|
//! | | Byte limit | Endianness | Int Encoding | Trailing Behavior |
|
|
//! |----------|------------|------------|--------------|-------------------|
|
|
//! | struct | Unlimited | Little | Varint | Reject |
|
|
//! | function | Unlimited | Little | Fixint | Allow |
|
|
//!
|
|
//! This means that if you want to use the `Serialize` / `Deserialize` structs with the same
|
|
//! settings as the functions, you should adjust the `DefaultOptions` struct like so:
|
|
//!
|
|
//! ```rust
|
|
//! use bincode::Options;
|
|
//! let my_options = bincode::DefaultOptions::new()
|
|
//! .with_fixint_encoding()
|
|
//! .allow_trailing_bytes();
|
|
//! ```
|
|
|
|
use crate::de::read::BincodeRead;
|
|
use crate::error::Result;
|
|
use std::io::{Read, Write};
|
|
use std::marker::PhantomData;
|
|
|
|
pub(crate) use self::endian::BincodeByteOrder;
|
|
pub(crate) use self::int::IntEncoding;
|
|
pub(crate) use self::internal::*;
|
|
pub(crate) use self::limit::SizeLimit;
|
|
pub(crate) use self::trailing::TrailingBytes;
|
|
|
|
pub use self::endian::{BigEndian, LittleEndian, NativeEndian};
|
|
pub use self::int::{FixintEncoding, VarintEncoding};
|
|
pub use self::limit::{Bounded, Infinite};
|
|
pub use self::trailing::{AllowTrailing, RejectTrailing};
|
|
|
|
mod endian;
|
|
mod int;
|
|
mod limit;
|
|
mod trailing;
|
|
|
|
/// The default options for bincode serialization/deserialization.
|
|
///
|
|
/// ### Defaults
|
|
/// By default bincode will use little-endian encoding for multi-byte integers, and will not
|
|
/// limit the number of serialized/deserialized bytes.
|
|
///
|
|
/// ### Configuring `DefaultOptions`
|
|
///
|
|
/// `DefaultOptions` implements the [Options] trait, which means it exposes functions to change the behavior of bincode.
|
|
///
|
|
/// For example, if you wanted to limit the bincode deserializer to 1 kilobyte of user input:
|
|
///
|
|
/// ```rust
|
|
/// use bincode::Options;
|
|
/// let my_options = bincode::DefaultOptions::new().with_limit(1024);
|
|
/// ```
|
|
///
|
|
/// ### DefaultOptions struct vs. functions
|
|
///
|
|
/// The default configuration used by this struct is not the same as that used by the bincode
|
|
/// helper functions in the root of this crate. See the
|
|
/// [config](index.html#options-struct-vs-bincode-functions) module for more details
|
|
#[derive(Copy, Clone)]
|
|
pub struct DefaultOptions(Infinite);
|
|
|
|
impl DefaultOptions {
|
|
/// Get a default configuration object.
|
|
///
|
|
/// ### Default Configuration:
|
|
///
|
|
/// | Byte limit | Endianness | Int Encoding | Trailing Behavior |
|
|
/// |------------|------------|--------------|-------------------|
|
|
/// | Unlimited | Little | Varint | Reject |
|
|
pub fn new() -> DefaultOptions {
|
|
DefaultOptions(Infinite)
|
|
}
|
|
}
|
|
|
|
impl Default for DefaultOptions {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl InternalOptions for DefaultOptions {
|
|
type Limit = Infinite;
|
|
type Endian = LittleEndian;
|
|
type IntEncoding = VarintEncoding;
|
|
type Trailing = RejectTrailing;
|
|
|
|
#[inline(always)]
|
|
fn limit(&mut self) -> &mut Infinite {
|
|
&mut self.0
|
|
}
|
|
}
|
|
|
|
/// A configuration builder trait whose options Bincode will use
|
|
/// while serializing and deserializing.
|
|
///
|
|
/// ### Options
|
|
/// Endianness: The endianness with which multi-byte integers will be read/written. *default: little endian*
|
|
///
|
|
/// Limit: The maximum number of bytes that will be read/written in a bincode serialize/deserialize. *default: unlimited*
|
|
///
|
|
/// Int Encoding: The encoding used for numbers, enum discriminants, and lengths. *default: varint*
|
|
///
|
|
/// Trailing Behavior: The behavior when there are trailing bytes left over in a slice after deserialization. *default: reject*
|
|
///
|
|
/// ### Byte Limit Details
|
|
/// The purpose of byte-limiting is to prevent Denial-Of-Service attacks whereby malicious attackers get bincode
|
|
/// deserialization to crash your process by allocating too much memory or keeping a connection open for too long.
|
|
///
|
|
/// When a byte limit is set, bincode will return `Err` on any deserialization that goes over the limit, or any
|
|
/// serialization that goes over the limit.
|
|
pub trait Options: InternalOptions + Sized {
|
|
/// Sets the byte limit to be unlimited.
|
|
/// This is the default.
|
|
fn with_no_limit(self) -> WithOtherLimit<Self, Infinite> {
|
|
WithOtherLimit::new(self, Infinite)
|
|
}
|
|
|
|
/// Sets the byte limit to `limit`.
|
|
fn with_limit(self, limit: u64) -> WithOtherLimit<Self, Bounded> {
|
|
WithOtherLimit::new(self, Bounded(limit))
|
|
}
|
|
|
|
/// Sets the endianness to little-endian
|
|
/// This is the default.
|
|
fn with_little_endian(self) -> WithOtherEndian<Self, LittleEndian> {
|
|
WithOtherEndian::new(self)
|
|
}
|
|
|
|
/// Sets the endianness to big-endian
|
|
fn with_big_endian(self) -> WithOtherEndian<Self, BigEndian> {
|
|
WithOtherEndian::new(self)
|
|
}
|
|
|
|
/// Sets the endianness to the the machine-native endianness
|
|
fn with_native_endian(self) -> WithOtherEndian<Self, NativeEndian> {
|
|
WithOtherEndian::new(self)
|
|
}
|
|
|
|
/// Sets the length encoding to varint
|
|
fn with_varint_encoding(self) -> WithOtherIntEncoding<Self, VarintEncoding> {
|
|
WithOtherIntEncoding::new(self)
|
|
}
|
|
|
|
/// Sets the length encoding to be fixed
|
|
fn with_fixint_encoding(self) -> WithOtherIntEncoding<Self, FixintEncoding> {
|
|
WithOtherIntEncoding::new(self)
|
|
}
|
|
|
|
/// Sets the deserializer to reject trailing bytes
|
|
fn reject_trailing_bytes(self) -> WithOtherTrailing<Self, RejectTrailing> {
|
|
WithOtherTrailing::new(self)
|
|
}
|
|
|
|
/// Sets the deserializer to allow trailing bytes
|
|
fn allow_trailing_bytes(self) -> WithOtherTrailing<Self, AllowTrailing> {
|
|
WithOtherTrailing::new(self)
|
|
}
|
|
|
|
/// Serializes a serializable object into a `Vec` of bytes using this configuration
|
|
#[inline(always)]
|
|
fn serialize<S: ?Sized + serde::Serialize>(self, t: &S) -> Result<Vec<u8>> {
|
|
crate::internal::serialize(t, self)
|
|
}
|
|
|
|
/// Returns the size that an object would be if serialized using Bincode with this configuration
|
|
#[inline(always)]
|
|
fn serialized_size<T: ?Sized + serde::Serialize>(self, t: &T) -> Result<u64> {
|
|
crate::internal::serialized_size(t, self)
|
|
}
|
|
|
|
/// Serializes an object directly into a `Writer` using this configuration
|
|
///
|
|
/// If the serialization would take more bytes than allowed by the size limit, an error
|
|
/// is returned and *no bytes* will be written into the `Writer`
|
|
#[inline(always)]
|
|
fn serialize_into<W: Write, T: ?Sized + serde::Serialize>(self, w: W, t: &T) -> Result<()> {
|
|
crate::internal::serialize_into(w, t, self)
|
|
}
|
|
|
|
/// Deserializes a slice of bytes into an instance of `T` using this configuration
|
|
#[inline(always)]
|
|
fn deserialize<'a, T: serde::Deserialize<'a>>(self, bytes: &'a [u8]) -> Result<T> {
|
|
crate::internal::deserialize(bytes, self)
|
|
}
|
|
|
|
/// TODO: document
|
|
#[doc(hidden)]
|
|
#[inline(always)]
|
|
fn deserialize_in_place<'a, R, T>(self, reader: R, place: &mut T) -> Result<()>
|
|
where
|
|
R: BincodeRead<'a>,
|
|
T: serde::de::Deserialize<'a>,
|
|
{
|
|
crate::internal::deserialize_in_place(reader, self, place)
|
|
}
|
|
|
|
/// Deserializes a slice of bytes with state `seed` using this configuration.
|
|
#[inline(always)]
|
|
fn deserialize_seed<'a, T: serde::de::DeserializeSeed<'a>>(
|
|
self,
|
|
seed: T,
|
|
bytes: &'a [u8],
|
|
) -> Result<T::Value> {
|
|
crate::internal::deserialize_seed(seed, bytes, self)
|
|
}
|
|
|
|
/// Deserializes an object directly from a `Read`er using this configuration
|
|
///
|
|
/// If this returns an `Error`, `reader` may be in an invalid state.
|
|
#[inline(always)]
|
|
fn deserialize_from<R: Read, T: serde::de::DeserializeOwned>(self, reader: R) -> Result<T> {
|
|
crate::internal::deserialize_from(reader, self)
|
|
}
|
|
|
|
/// Deserializes an object directly from a `Read`er with state `seed` using this configuration
|
|
///
|
|
/// If this returns an `Error`, `reader` may be in an invalid state.
|
|
#[inline(always)]
|
|
fn deserialize_from_seed<'a, R: Read, T: serde::de::DeserializeSeed<'a>>(
|
|
self,
|
|
seed: T,
|
|
reader: R,
|
|
) -> Result<T::Value> {
|
|
crate::internal::deserialize_from_seed(seed, reader, self)
|
|
}
|
|
|
|
/// Deserializes an object from a custom `BincodeRead`er using the default configuration.
|
|
/// It is highly recommended to use `deserialize_from` unless you need to implement
|
|
/// `BincodeRead` for performance reasons.
|
|
///
|
|
/// If this returns an `Error`, `reader` may be in an invalid state.
|
|
#[inline(always)]
|
|
fn deserialize_from_custom<'a, R: BincodeRead<'a>, T: serde::de::DeserializeOwned>(
|
|
self,
|
|
reader: R,
|
|
) -> Result<T> {
|
|
crate::internal::deserialize_from_custom(reader, self)
|
|
}
|
|
|
|
/// Deserializes an object from a custom `BincodeRead`er with state `seed` using the default
|
|
/// configuration. It is highly recommended to use `deserialize_from` unless you need to
|
|
/// implement `BincodeRead` for performance reasons.
|
|
///
|
|
/// If this returns an `Error`, `reader` may be in an invalid state.
|
|
#[inline(always)]
|
|
fn deserialize_from_custom_seed<'a, R: BincodeRead<'a>, T: serde::de::DeserializeSeed<'a>>(
|
|
self,
|
|
seed: T,
|
|
reader: R,
|
|
) -> Result<T::Value> {
|
|
crate::internal::deserialize_from_custom_seed(seed, reader, self)
|
|
}
|
|
}
|
|
|
|
impl<T: InternalOptions> Options for T {}
|
|
|
|
/// A configuration struct with a user-specified byte limit
|
|
#[derive(Clone, Copy)]
|
|
pub struct WithOtherLimit<O: Options, L: SizeLimit> {
|
|
_options: O,
|
|
pub(crate) new_limit: L,
|
|
}
|
|
|
|
/// A configuration struct with a user-specified endian order
|
|
#[derive(Clone, Copy)]
|
|
pub struct WithOtherEndian<O: Options, E: BincodeByteOrder> {
|
|
options: O,
|
|
_endian: PhantomData<E>,
|
|
}
|
|
|
|
/// A configuration struct with a user-specified length encoding
|
|
#[derive(Clone, Copy)]
|
|
pub struct WithOtherIntEncoding<O: Options, I: IntEncoding> {
|
|
options: O,
|
|
_length: PhantomData<I>,
|
|
}
|
|
|
|
/// A configuration struct with a user-specified trailing bytes behavior.
|
|
#[derive(Clone, Copy)]
|
|
pub struct WithOtherTrailing<O: Options, T: TrailingBytes> {
|
|
options: O,
|
|
_trailing: PhantomData<T>,
|
|
}
|
|
|
|
impl<O: Options, L: SizeLimit> WithOtherLimit<O, L> {
|
|
#[inline(always)]
|
|
pub(crate) fn new(options: O, limit: L) -> WithOtherLimit<O, L> {
|
|
WithOtherLimit {
|
|
_options: options,
|
|
new_limit: limit,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<O: Options, E: BincodeByteOrder> WithOtherEndian<O, E> {
|
|
#[inline(always)]
|
|
pub(crate) fn new(options: O) -> WithOtherEndian<O, E> {
|
|
WithOtherEndian {
|
|
options,
|
|
_endian: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<O: Options, I: IntEncoding> WithOtherIntEncoding<O, I> {
|
|
#[inline(always)]
|
|
pub(crate) fn new(options: O) -> WithOtherIntEncoding<O, I> {
|
|
WithOtherIntEncoding {
|
|
options,
|
|
_length: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<O: Options, T: TrailingBytes> WithOtherTrailing<O, T> {
|
|
#[inline(always)]
|
|
pub(crate) fn new(options: O) -> WithOtherTrailing<O, T> {
|
|
WithOtherTrailing {
|
|
options,
|
|
_trailing: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<O: Options, E: BincodeByteOrder + 'static> InternalOptions for WithOtherEndian<O, E> {
|
|
type Limit = O::Limit;
|
|
type Endian = E;
|
|
type IntEncoding = O::IntEncoding;
|
|
type Trailing = O::Trailing;
|
|
#[inline(always)]
|
|
fn limit(&mut self) -> &mut O::Limit {
|
|
self.options.limit()
|
|
}
|
|
}
|
|
|
|
impl<O: Options, L: SizeLimit + 'static> InternalOptions for WithOtherLimit<O, L> {
|
|
type Limit = L;
|
|
type Endian = O::Endian;
|
|
type IntEncoding = O::IntEncoding;
|
|
type Trailing = O::Trailing;
|
|
fn limit(&mut self) -> &mut L {
|
|
&mut self.new_limit
|
|
}
|
|
}
|
|
|
|
impl<O: Options, I: IntEncoding + 'static> InternalOptions for WithOtherIntEncoding<O, I> {
|
|
type Limit = O::Limit;
|
|
type Endian = O::Endian;
|
|
type IntEncoding = I;
|
|
type Trailing = O::Trailing;
|
|
|
|
fn limit(&mut self) -> &mut O::Limit {
|
|
self.options.limit()
|
|
}
|
|
}
|
|
|
|
impl<O: Options, T: TrailingBytes + 'static> InternalOptions for WithOtherTrailing<O, T> {
|
|
type Limit = O::Limit;
|
|
type Endian = O::Endian;
|
|
type IntEncoding = O::IntEncoding;
|
|
type Trailing = T;
|
|
|
|
fn limit(&mut self) -> &mut O::Limit {
|
|
self.options.limit()
|
|
}
|
|
}
|
|
|
|
mod internal {
|
|
use super::*;
|
|
|
|
pub trait InternalOptions {
|
|
type Limit: SizeLimit + 'static;
|
|
type Endian: BincodeByteOrder + 'static;
|
|
type IntEncoding: IntEncoding + 'static;
|
|
type Trailing: TrailingBytes + 'static;
|
|
|
|
fn limit(&mut self) -> &mut Self::Limit;
|
|
}
|
|
|
|
impl<'a, O: InternalOptions> InternalOptions for &'a mut O {
|
|
type Limit = O::Limit;
|
|
type Endian = O::Endian;
|
|
type IntEncoding = O::IntEncoding;
|
|
type Trailing = O::Trailing;
|
|
|
|
#[inline(always)]
|
|
fn limit(&mut self) -> &mut Self::Limit {
|
|
(*self).limit()
|
|
}
|
|
}
|
|
}
|