mirror of https://git.sr.ht/~stygianentity/bincode
Feature/config limit (#439)
* Added Limit<N> and NoLimit to the configuration * Added a limit check to Decoder and DecoderImpl * Added test cases, added a helper function specialized for containers * Added a test to see if inlining makes the limit config faster, added inlining to the decoder
This commit is contained in:
parent
ed57fd0810
commit
63f4607992
|
|
@ -50,6 +50,10 @@ rand = "0.8"
|
||||||
name = "varint"
|
name = "varint"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "inline"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[profile.bench]
|
[profile.bench]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
debug = 1
|
debug = 1
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||||
|
|
||||||
|
use bincode::config::Configuration;
|
||||||
|
|
||||||
|
fn inline_decoder_claim_bytes_read(c: &mut Criterion) {
|
||||||
|
let config = Configuration::standard().with_limit::<100000>();
|
||||||
|
let slice = bincode::encode_to_vec(vec![String::from("Hello world"); 1000], config).unwrap();
|
||||||
|
|
||||||
|
c.bench_function("inline_decoder_claim_bytes_read", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let _: Vec<String> =
|
||||||
|
black_box(bincode::decode_from_slice(black_box(&slice), config).unwrap());
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, inline_decoder_claim_bytes_read);
|
||||||
|
criterion_main!(benches);
|
||||||
|
|
@ -40,10 +40,11 @@ use core::marker::PhantomData;
|
||||||
/// [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
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct Configuration<E = LittleEndian, I = Varint, A = SkipFixedArrayLength> {
|
pub struct Configuration<E = LittleEndian, I = Varint, A = SkipFixedArrayLength, L = NoLimit> {
|
||||||
_e: PhantomData<E>,
|
_e: PhantomData<E>,
|
||||||
_i: PhantomData<I>,
|
_i: PhantomData<I>,
|
||||||
_a: PhantomData<A>,
|
_a: PhantomData<A>,
|
||||||
|
_l: PhantomData<L>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Configuration {
|
impl Configuration {
|
||||||
|
|
@ -59,17 +60,18 @@ impl Configuration {
|
||||||
/// - Little endian
|
/// - Little endian
|
||||||
/// - Fixed int length encoding
|
/// - Fixed int length encoding
|
||||||
/// - Write array lengths
|
/// - Write array lengths
|
||||||
pub fn legacy() -> Configuration<LittleEndian, Fixint, WriteFixedArrayLength> {
|
pub fn legacy() -> Configuration<LittleEndian, Fixint, WriteFixedArrayLength, NoLimit> {
|
||||||
Self::generate()
|
Self::generate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, I, A> Configuration<E, I, A> {
|
impl<E, I, A, L> Configuration<E, I, A, L> {
|
||||||
fn generate<_E, _I, _A>() -> Configuration<_E, _I, _A> {
|
fn generate<_E, _I, _A, _L>() -> Configuration<_E, _I, _A, _L> {
|
||||||
Configuration {
|
Configuration {
|
||||||
_e: PhantomData,
|
_e: PhantomData,
|
||||||
_i: PhantomData,
|
_i: PhantomData,
|
||||||
_a: PhantomData,
|
_a: PhantomData,
|
||||||
|
_l: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -162,16 +164,36 @@ impl<E, I, A> Configuration<E, I, A> {
|
||||||
pub fn write_fixed_array_length(self) -> Configuration<E, I, WriteFixedArrayLength> {
|
pub fn write_fixed_array_length(self) -> Configuration<E, I, WriteFixedArrayLength> {
|
||||||
Self::generate()
|
Self::generate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the byte limit to `limit`.
|
||||||
|
pub fn with_limit<const N: usize>(self) -> Configuration<E, I, A, Limit<N>> {
|
||||||
|
Self::generate()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear the byte limit.
|
||||||
|
pub fn with_no_limit(self) -> Configuration<E, I, A, NoLimit> {
|
||||||
|
Self::generate()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indicates a type is valid for controlling the bincode configuration
|
/// Indicates a type is valid for controlling the bincode configuration
|
||||||
pub trait Config:
|
pub trait Config:
|
||||||
InternalEndianConfig + InternalArrayLengthConfig + InternalIntEncodingConfig + Copy + Clone
|
InternalEndianConfig
|
||||||
|
+ InternalArrayLengthConfig
|
||||||
|
+ InternalIntEncodingConfig
|
||||||
|
+ InternalLimitConfig
|
||||||
|
+ Copy
|
||||||
|
+ Clone
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Config for T where
|
impl<T> Config for T where
|
||||||
T: InternalEndianConfig + InternalArrayLengthConfig + InternalIntEncodingConfig + Copy + Clone
|
T: InternalEndianConfig
|
||||||
|
+ InternalArrayLengthConfig
|
||||||
|
+ InternalIntEncodingConfig
|
||||||
|
+ InternalLimitConfig
|
||||||
|
+ Copy
|
||||||
|
+ Clone
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -223,6 +245,20 @@ impl InternalArrayLengthConfig for WriteFixedArrayLength {
|
||||||
const SKIP_FIXED_ARRAY_LENGTH: bool = false;
|
const SKIP_FIXED_ARRAY_LENGTH: bool = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct NoLimit {}
|
||||||
|
impl InternalLimitConfig for NoLimit {
|
||||||
|
const LIMIT: Option<usize> = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Limit<const N: usize> {}
|
||||||
|
impl<const N: usize> InternalLimitConfig for Limit<N> {
|
||||||
|
const LIMIT: Option<usize> = Some(N);
|
||||||
|
}
|
||||||
|
|
||||||
mod internal {
|
mod internal {
|
||||||
use super::Configuration;
|
use super::Configuration;
|
||||||
|
|
||||||
|
|
@ -230,7 +266,7 @@ mod internal {
|
||||||
const ENDIAN: Endian;
|
const ENDIAN: Endian;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: InternalEndianConfig, I, A> InternalEndianConfig for Configuration<E, I, A> {
|
impl<E: InternalEndianConfig, I, A, L> InternalEndianConfig for Configuration<E, I, A, L> {
|
||||||
const ENDIAN: Endian = E::ENDIAN;
|
const ENDIAN: Endian = E::ENDIAN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -244,7 +280,9 @@ mod internal {
|
||||||
const INT_ENCODING: IntEncoding;
|
const INT_ENCODING: IntEncoding;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, I: InternalIntEncodingConfig, A> InternalIntEncodingConfig for Configuration<E, I, A> {
|
impl<E, I: InternalIntEncodingConfig, A, L> InternalIntEncodingConfig
|
||||||
|
for Configuration<E, I, A, L>
|
||||||
|
{
|
||||||
const INT_ENCODING: IntEncoding = I::INT_ENCODING;
|
const INT_ENCODING: IntEncoding = I::INT_ENCODING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -258,7 +296,17 @@ mod internal {
|
||||||
const SKIP_FIXED_ARRAY_LENGTH: bool;
|
const SKIP_FIXED_ARRAY_LENGTH: bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, I, A: InternalArrayLengthConfig> InternalArrayLengthConfig for Configuration<E, I, A> {
|
impl<E, I, A: InternalArrayLengthConfig, L> InternalArrayLengthConfig
|
||||||
|
for Configuration<E, I, A, L>
|
||||||
|
{
|
||||||
const SKIP_FIXED_ARRAY_LENGTH: bool = A::SKIP_FIXED_ARRAY_LENGTH;
|
const SKIP_FIXED_ARRAY_LENGTH: bool = A::SKIP_FIXED_ARRAY_LENGTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait InternalLimitConfig {
|
||||||
|
const LIMIT: Option<usize>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E, I, A, L: InternalLimitConfig> InternalLimitConfig for Configuration<E, I, A, L> {
|
||||||
|
const LIMIT: Option<usize> = L::LIMIT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use super::{
|
||||||
read::{BorrowReader, Reader},
|
read::{BorrowReader, Reader},
|
||||||
BorrowDecoder, Decoder,
|
BorrowDecoder, Decoder,
|
||||||
};
|
};
|
||||||
use crate::{config::Config, utils::Sealed};
|
use crate::{config::Config, error::DecodeError, utils::Sealed};
|
||||||
|
|
||||||
/// A Decoder that reads bytes from a given reader `R`.
|
/// A Decoder that reads bytes from a given reader `R`.
|
||||||
///
|
///
|
||||||
|
|
@ -24,12 +24,17 @@ use crate::{config::Config, utils::Sealed};
|
||||||
pub struct DecoderImpl<R, C: Config> {
|
pub struct DecoderImpl<R, C: Config> {
|
||||||
reader: R,
|
reader: R,
|
||||||
config: C,
|
config: C,
|
||||||
|
bytes_read: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Reader, C: Config> DecoderImpl<R, C> {
|
impl<R: Reader, C: Config> DecoderImpl<R, C> {
|
||||||
/// Construct a new Decoder
|
/// Construct a new Decoder
|
||||||
pub fn new(reader: R, config: C) -> DecoderImpl<R, C> {
|
pub fn new(reader: R, config: C) -> DecoderImpl<R, C> {
|
||||||
DecoderImpl { reader, config }
|
DecoderImpl {
|
||||||
|
reader,
|
||||||
|
config,
|
||||||
|
bytes_read: 0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,4 +60,32 @@ impl<R: Reader, C: Config> Decoder for DecoderImpl<R, C> {
|
||||||
fn config(&self) -> &Self::C {
|
fn config(&self) -> &Self::C {
|
||||||
&self.config
|
&self.config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn claim_bytes_read(&mut self, n: usize) -> Result<(), DecodeError> {
|
||||||
|
// C::LIMIT is a const so this check should get compiled away
|
||||||
|
if let Some(limit) = C::LIMIT {
|
||||||
|
// Make sure we don't accidentally overflow `bytes_read`
|
||||||
|
self.bytes_read = self
|
||||||
|
.bytes_read
|
||||||
|
.checked_add(n)
|
||||||
|
.ok_or(DecodeError::LimitExceeded)?;
|
||||||
|
if self.bytes_read > limit {
|
||||||
|
Err(DecodeError::LimitExceeded)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn unclaim_bytes_read(&mut self, n: usize) {
|
||||||
|
// C::LIMIT is a const so this check should get compiled away
|
||||||
|
if C::LIMIT.is_some() {
|
||||||
|
// We should always be claiming more than we unclaim, so this should never underflow
|
||||||
|
self.bytes_read -= n;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ impl Decode for bool {
|
||||||
impl Decode for u8 {
|
impl Decode for u8 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
|
decoder.claim_bytes_read(1)?;
|
||||||
if let Some(buf) = decoder.reader().peek_read(1) {
|
if let Some(buf) = decoder.reader().peek_read(1) {
|
||||||
let byte = buf[0];
|
let byte = buf[0];
|
||||||
decoder.reader().consume(1);
|
decoder.reader().consume(1);
|
||||||
|
|
@ -55,6 +56,7 @@ impl Decode for NonZeroU8 {
|
||||||
|
|
||||||
impl Decode for u16 {
|
impl Decode for u16 {
|
||||||
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
|
decoder.claim_bytes_read(2)?;
|
||||||
match D::C::INT_ENCODING {
|
match D::C::INT_ENCODING {
|
||||||
IntEncoding::Variable => {
|
IntEncoding::Variable => {
|
||||||
crate::varint::varint_decode_u16(decoder.reader(), D::C::ENDIAN)
|
crate::varint::varint_decode_u16(decoder.reader(), D::C::ENDIAN)
|
||||||
|
|
@ -81,6 +83,7 @@ impl Decode for NonZeroU16 {
|
||||||
|
|
||||||
impl Decode for u32 {
|
impl Decode for u32 {
|
||||||
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
|
decoder.claim_bytes_read(4)?;
|
||||||
match D::C::INT_ENCODING {
|
match D::C::INT_ENCODING {
|
||||||
IntEncoding::Variable => {
|
IntEncoding::Variable => {
|
||||||
crate::varint::varint_decode_u32(decoder.reader(), D::C::ENDIAN)
|
crate::varint::varint_decode_u32(decoder.reader(), D::C::ENDIAN)
|
||||||
|
|
@ -107,6 +110,7 @@ impl Decode for NonZeroU32 {
|
||||||
|
|
||||||
impl Decode for u64 {
|
impl Decode for u64 {
|
||||||
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
|
decoder.claim_bytes_read(8)?;
|
||||||
match D::C::INT_ENCODING {
|
match D::C::INT_ENCODING {
|
||||||
IntEncoding::Variable => {
|
IntEncoding::Variable => {
|
||||||
crate::varint::varint_decode_u64(decoder.reader(), D::C::ENDIAN)
|
crate::varint::varint_decode_u64(decoder.reader(), D::C::ENDIAN)
|
||||||
|
|
@ -133,6 +137,7 @@ impl Decode for NonZeroU64 {
|
||||||
|
|
||||||
impl Decode for u128 {
|
impl Decode for u128 {
|
||||||
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
|
decoder.claim_bytes_read(16)?;
|
||||||
match D::C::INT_ENCODING {
|
match D::C::INT_ENCODING {
|
||||||
IntEncoding::Variable => {
|
IntEncoding::Variable => {
|
||||||
crate::varint::varint_decode_u128(decoder.reader(), D::C::ENDIAN)
|
crate::varint::varint_decode_u128(decoder.reader(), D::C::ENDIAN)
|
||||||
|
|
@ -159,6 +164,7 @@ impl Decode for NonZeroU128 {
|
||||||
|
|
||||||
impl Decode for usize {
|
impl Decode for usize {
|
||||||
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
|
decoder.claim_bytes_read(8)?;
|
||||||
match D::C::INT_ENCODING {
|
match D::C::INT_ENCODING {
|
||||||
IntEncoding::Variable => {
|
IntEncoding::Variable => {
|
||||||
crate::varint::varint_decode_usize(decoder.reader(), D::C::ENDIAN)
|
crate::varint::varint_decode_usize(decoder.reader(), D::C::ENDIAN)
|
||||||
|
|
@ -185,6 +191,7 @@ impl Decode for NonZeroUsize {
|
||||||
|
|
||||||
impl Decode for i8 {
|
impl Decode for i8 {
|
||||||
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
|
decoder.claim_bytes_read(1)?;
|
||||||
let mut bytes = [0u8; 1];
|
let mut bytes = [0u8; 1];
|
||||||
decoder.reader().read(&mut bytes)?;
|
decoder.reader().read(&mut bytes)?;
|
||||||
Ok(bytes[0] as i8)
|
Ok(bytes[0] as i8)
|
||||||
|
|
@ -201,6 +208,7 @@ impl Decode for NonZeroI8 {
|
||||||
|
|
||||||
impl Decode for i16 {
|
impl Decode for i16 {
|
||||||
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
|
decoder.claim_bytes_read(2)?;
|
||||||
match D::C::INT_ENCODING {
|
match D::C::INT_ENCODING {
|
||||||
IntEncoding::Variable => {
|
IntEncoding::Variable => {
|
||||||
crate::varint::varint_decode_i16(decoder.reader(), D::C::ENDIAN)
|
crate::varint::varint_decode_i16(decoder.reader(), D::C::ENDIAN)
|
||||||
|
|
@ -227,6 +235,7 @@ impl Decode for NonZeroI16 {
|
||||||
|
|
||||||
impl Decode for i32 {
|
impl Decode for i32 {
|
||||||
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
|
decoder.claim_bytes_read(4)?;
|
||||||
match D::C::INT_ENCODING {
|
match D::C::INT_ENCODING {
|
||||||
IntEncoding::Variable => {
|
IntEncoding::Variable => {
|
||||||
crate::varint::varint_decode_i32(decoder.reader(), D::C::ENDIAN)
|
crate::varint::varint_decode_i32(decoder.reader(), D::C::ENDIAN)
|
||||||
|
|
@ -253,6 +262,7 @@ impl Decode for NonZeroI32 {
|
||||||
|
|
||||||
impl Decode for i64 {
|
impl Decode for i64 {
|
||||||
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
|
decoder.claim_bytes_read(8)?;
|
||||||
match D::C::INT_ENCODING {
|
match D::C::INT_ENCODING {
|
||||||
IntEncoding::Variable => {
|
IntEncoding::Variable => {
|
||||||
crate::varint::varint_decode_i64(decoder.reader(), D::C::ENDIAN)
|
crate::varint::varint_decode_i64(decoder.reader(), D::C::ENDIAN)
|
||||||
|
|
@ -279,6 +289,7 @@ impl Decode for NonZeroI64 {
|
||||||
|
|
||||||
impl Decode for i128 {
|
impl Decode for i128 {
|
||||||
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
|
decoder.claim_bytes_read(16)?;
|
||||||
match D::C::INT_ENCODING {
|
match D::C::INT_ENCODING {
|
||||||
IntEncoding::Variable => {
|
IntEncoding::Variable => {
|
||||||
crate::varint::varint_decode_i128(decoder.reader(), D::C::ENDIAN)
|
crate::varint::varint_decode_i128(decoder.reader(), D::C::ENDIAN)
|
||||||
|
|
@ -305,6 +316,7 @@ impl Decode for NonZeroI128 {
|
||||||
|
|
||||||
impl Decode for isize {
|
impl Decode for isize {
|
||||||
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
|
decoder.claim_bytes_read(8)?;
|
||||||
match D::C::INT_ENCODING {
|
match D::C::INT_ENCODING {
|
||||||
IntEncoding::Variable => {
|
IntEncoding::Variable => {
|
||||||
crate::varint::varint_decode_isize(decoder.reader(), D::C::ENDIAN)
|
crate::varint::varint_decode_isize(decoder.reader(), D::C::ENDIAN)
|
||||||
|
|
@ -331,6 +343,7 @@ impl Decode for NonZeroIsize {
|
||||||
|
|
||||||
impl Decode for f32 {
|
impl Decode for f32 {
|
||||||
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
|
decoder.claim_bytes_read(4)?;
|
||||||
let mut bytes = [0u8; 4];
|
let mut bytes = [0u8; 4];
|
||||||
decoder.reader().read(&mut bytes)?;
|
decoder.reader().read(&mut bytes)?;
|
||||||
Ok(match D::C::ENDIAN {
|
Ok(match D::C::ENDIAN {
|
||||||
|
|
@ -342,6 +355,7 @@ impl Decode for f32 {
|
||||||
|
|
||||||
impl Decode for f64 {
|
impl Decode for f64 {
|
||||||
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
|
decoder.claim_bytes_read(8)?;
|
||||||
let mut bytes = [0u8; 8];
|
let mut bytes = [0u8; 8];
|
||||||
decoder.reader().read(&mut bytes)?;
|
decoder.reader().read(&mut bytes)?;
|
||||||
Ok(match D::C::ENDIAN {
|
Ok(match D::C::ENDIAN {
|
||||||
|
|
@ -362,6 +376,10 @@ impl Decode for char {
|
||||||
if width == 0 {
|
if width == 0 {
|
||||||
return Err(DecodeError::InvalidCharEncoding(array));
|
return Err(DecodeError::InvalidCharEncoding(array));
|
||||||
}
|
}
|
||||||
|
// Normally we have to `.claim_bytes_read` before reading, however in this
|
||||||
|
// case the amount of bytes read from `char` can vary wildly, and it should
|
||||||
|
// only read up to 4 bytes too much.
|
||||||
|
decoder.claim_bytes_read(width)?;
|
||||||
if width == 1 {
|
if width == 1 {
|
||||||
return Ok(array[0] as char);
|
return Ok(array[0] as char);
|
||||||
}
|
}
|
||||||
|
|
@ -379,6 +397,7 @@ impl Decode for char {
|
||||||
impl<'a, 'de: 'a> BorrowDecode<'de> for &'a [u8] {
|
impl<'a, 'de: 'a> BorrowDecode<'de> for &'a [u8] {
|
||||||
fn borrow_decode<D: BorrowDecoder<'de>>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn borrow_decode<D: BorrowDecoder<'de>>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
let len = super::decode_slice_len(&mut decoder)?;
|
let len = super::decode_slice_len(&mut decoder)?;
|
||||||
|
decoder.claim_bytes_read(len)?;
|
||||||
decoder.borrow_reader().take_bytes(len)
|
decoder.borrow_reader().take_bytes(len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -397,7 +416,7 @@ impl<'a, 'de: 'a> BorrowDecode<'de> for Option<&'a [u8]> {
|
||||||
|
|
||||||
impl<'a, 'de: 'a> BorrowDecode<'de> for &'a str {
|
impl<'a, 'de: 'a> BorrowDecode<'de> for &'a str {
|
||||||
fn borrow_decode<D: BorrowDecoder<'de>>(decoder: D) -> Result<Self, DecodeError> {
|
fn borrow_decode<D: BorrowDecoder<'de>>(decoder: D) -> Result<Self, DecodeError> {
|
||||||
let slice: &[u8] = BorrowDecode::borrow_decode(decoder)?;
|
let slice = <&[u8]>::borrow_decode(decoder)?;
|
||||||
core::str::from_utf8(slice).map_err(DecodeError::Utf8)
|
core::str::from_utf8(slice).map_err(DecodeError::Utf8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -429,6 +448,9 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
decoder.claim_bytes_read(core::mem::size_of::<[T; N]>())?;
|
||||||
|
|
||||||
|
// Optimize for `[u8; N]`
|
||||||
if TypeId::of::<u8>() == TypeId::of::<T>() {
|
if TypeId::of::<u8>() == TypeId::of::<T>() {
|
||||||
let mut buf = [0u8; N];
|
let mut buf = [0u8; N];
|
||||||
decoder.reader().read(&mut buf)?;
|
decoder.reader().read(&mut buf)?;
|
||||||
|
|
@ -439,8 +461,11 @@ where
|
||||||
let res = unsafe { ptr.read() };
|
let res = unsafe { ptr.read() };
|
||||||
Ok(res)
|
Ok(res)
|
||||||
} else {
|
} else {
|
||||||
let result =
|
let result = super::impl_core::collect_into_array(&mut (0..N).map(|_| {
|
||||||
super::impl_core::collect_into_array(&mut (0..N).map(|_| T::decode(&mut decoder)));
|
// See the documentation on `unclaim_bytes_read` as to why we're doing this here
|
||||||
|
decoder.unclaim_bytes_read(core::mem::size_of::<T>());
|
||||||
|
T::decode(&mut decoder)
|
||||||
|
}));
|
||||||
|
|
||||||
// result is only None if N does not match the values of `(0..N)`, which it always should
|
// result is only None if N does not match the values of `(0..N)`, which it always should
|
||||||
// So this unwrap should never occur
|
// So this unwrap should never occur
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,11 @@ mod impl_tuples;
|
||||||
mod impls;
|
mod impls;
|
||||||
|
|
||||||
use self::read::{BorrowReader, Reader};
|
use self::read::{BorrowReader, Reader};
|
||||||
use crate::{config::Config, error::DecodeError, utils::Sealed};
|
use crate::{
|
||||||
|
config::{Config, InternalLimitConfig},
|
||||||
|
error::DecodeError,
|
||||||
|
utils::Sealed,
|
||||||
|
};
|
||||||
|
|
||||||
pub mod read;
|
pub mod read;
|
||||||
|
|
||||||
|
|
@ -53,6 +57,65 @@ pub trait Decoder: Sealed {
|
||||||
|
|
||||||
/// Returns a mutable reference to the config
|
/// Returns a mutable reference to the config
|
||||||
fn config(&self) -> &Self::C;
|
fn config(&self) -> &Self::C;
|
||||||
|
|
||||||
|
/// Claim that `n` bytes are going to be read from the decoder.
|
||||||
|
/// This can be used to validate `Configuration::Limit<N>()`.
|
||||||
|
fn claim_bytes_read(&mut self, n: usize) -> Result<(), DecodeError>;
|
||||||
|
|
||||||
|
/// Claim that we're going to read a container which contains `len` entries of `T`.
|
||||||
|
/// This will correctly handle overflowing if `len * size_of::<T>() > usize::max_value`
|
||||||
|
fn claim_container_read<T>(&mut self, len: usize) -> Result<(), DecodeError> {
|
||||||
|
if <Self::C as InternalLimitConfig>::LIMIT.is_some() {
|
||||||
|
match len.checked_mul(core::mem::size_of::<T>()) {
|
||||||
|
Some(val) => self.claim_bytes_read(val),
|
||||||
|
None => Err(DecodeError::LimitExceeded),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Notify the decoder that `n` bytes are being reclaimed.
|
||||||
|
///
|
||||||
|
/// When decoding container types, a typical implementation would claim to read `len * size_of::<T>()` bytes.
|
||||||
|
/// This is to ensure that bincode won't allocate several GB of memory while constructing the container.
|
||||||
|
///
|
||||||
|
/// Because the implementation claims `len * size_of::<T>()`, but then has to decode each `T`, this would be marked
|
||||||
|
/// as double. This function allows us to un-claim each `T` that gets decoded.
|
||||||
|
///
|
||||||
|
/// We cannot check if `len * size_of::<T>()` is valid without claiming it, because this would mean that if you have
|
||||||
|
/// a nested container (e.g. `Vec<Vec<T>>`), it does not know how much memory is already claimed, and could easily
|
||||||
|
/// allocate much more than the user intends.
|
||||||
|
/// ```
|
||||||
|
/// # use bincode::de::{Decode, Decoder};
|
||||||
|
/// # use bincode::error::DecodeError;
|
||||||
|
/// # struct Container<T>(Vec<T>);
|
||||||
|
/// # impl<T> Container<T> {
|
||||||
|
/// # fn with_capacity(cap: usize) -> Self {
|
||||||
|
/// # Self(Vec::with_capacity(cap))
|
||||||
|
/// # }
|
||||||
|
/// #
|
||||||
|
/// # fn push(&mut self, t: T) {
|
||||||
|
/// # self.0.push(t);
|
||||||
|
/// # }
|
||||||
|
/// # }
|
||||||
|
/// impl<T: Decode> Decode for Container<T> {
|
||||||
|
/// fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
|
/// let len = u64::decode(&mut decoder)? as usize;
|
||||||
|
/// // Make sure we don't allocate too much memory
|
||||||
|
/// decoder.claim_bytes_read(len * core::mem::size_of::<T>());
|
||||||
|
///
|
||||||
|
/// let mut result = Container::with_capacity(len);
|
||||||
|
/// for _ in 0..len {
|
||||||
|
/// // un-claim the memory
|
||||||
|
/// decoder.unclaim_bytes_read(core::mem::size_of::<T>());
|
||||||
|
/// result.push(T::decode(&mut decoder)?)
|
||||||
|
/// }
|
||||||
|
/// Ok(result)
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
fn unclaim_bytes_read(&mut self, n: usize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Any source that can decode basic types. This type is most notably implemented for [Decoder].
|
/// Any source that can decode basic types. This type is most notably implemented for [Decoder].
|
||||||
|
|
@ -81,6 +144,16 @@ where
|
||||||
fn config(&self) -> &Self::C {
|
fn config(&self) -> &Self::C {
|
||||||
T::config(self)
|
T::config(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn claim_bytes_read(&mut self, n: usize) -> Result<(), DecodeError> {
|
||||||
|
T::claim_bytes_read(self, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn unclaim_bytes_read(&mut self, n: usize) {
|
||||||
|
T::unclaim_bytes_read(self, n)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'de, T> BorrowDecoder<'de> for &'a mut T
|
impl<'a, 'de, T> BorrowDecoder<'de> for &'a mut T
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,9 @@ pub enum DecodeError {
|
||||||
/// The reader reached its end but more bytes were expected.
|
/// The reader reached its end but more bytes were expected.
|
||||||
UnexpectedEnd,
|
UnexpectedEnd,
|
||||||
|
|
||||||
|
/// The given configuration limit was exceeded
|
||||||
|
LimitExceeded,
|
||||||
|
|
||||||
/// Invalid type was found. The decoder tried to read type `expected`, but found type `found` instead.
|
/// Invalid type was found. The decoder tried to read type `expected`, but found type `found` instead.
|
||||||
InvalidIntegerType {
|
InvalidIntegerType {
|
||||||
/// The type that was being read from the reader
|
/// The type that was being read from the reader
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,13 @@ where
|
||||||
{
|
{
|
||||||
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
let len = crate::de::decode_slice_len(&mut decoder)?;
|
let len = crate::de::decode_slice_len(&mut decoder)?;
|
||||||
|
decoder.claim_container_read::<T>(len)?;
|
||||||
|
|
||||||
let mut map = BinaryHeap::with_capacity(len);
|
let mut map = BinaryHeap::with_capacity(len);
|
||||||
for _ in 0..len {
|
for _ in 0..len {
|
||||||
|
// See the documentation on `unclaim_bytes_read` as to why we're doing this here
|
||||||
|
decoder.unclaim_bytes_read(core::mem::size_of::<T>());
|
||||||
|
|
||||||
let key = T::decode(&mut decoder)?;
|
let key = T::decode(&mut decoder)?;
|
||||||
map.push(key);
|
map.push(key);
|
||||||
}
|
}
|
||||||
|
|
@ -81,8 +86,13 @@ where
|
||||||
{
|
{
|
||||||
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
let len = crate::de::decode_slice_len(&mut decoder)?;
|
let len = crate::de::decode_slice_len(&mut decoder)?;
|
||||||
|
decoder.claim_container_read::<(K, V)>(len)?;
|
||||||
|
|
||||||
let mut map = BTreeMap::new();
|
let mut map = BTreeMap::new();
|
||||||
for _ in 0..len {
|
for _ in 0..len {
|
||||||
|
// See the documentation on `unclaim_bytes_read` as to why we're doing this here
|
||||||
|
decoder.unclaim_bytes_read(core::mem::size_of::<(K, V)>());
|
||||||
|
|
||||||
let key = K::decode(&mut decoder)?;
|
let key = K::decode(&mut decoder)?;
|
||||||
let value = V::decode(&mut decoder)?;
|
let value = V::decode(&mut decoder)?;
|
||||||
map.insert(key, value);
|
map.insert(key, value);
|
||||||
|
|
@ -112,8 +122,13 @@ where
|
||||||
{
|
{
|
||||||
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
let len = crate::de::decode_slice_len(&mut decoder)?;
|
let len = crate::de::decode_slice_len(&mut decoder)?;
|
||||||
|
decoder.claim_container_read::<T>(len)?;
|
||||||
|
|
||||||
let mut map = BTreeSet::new();
|
let mut map = BTreeSet::new();
|
||||||
for _ in 0..len {
|
for _ in 0..len {
|
||||||
|
// See the documentation on `unclaim_bytes_read` as to why we're doing this here
|
||||||
|
decoder.unclaim_bytes_read(core::mem::size_of::<T>());
|
||||||
|
|
||||||
let key = T::decode(&mut decoder)?;
|
let key = T::decode(&mut decoder)?;
|
||||||
map.insert(key);
|
map.insert(key);
|
||||||
}
|
}
|
||||||
|
|
@ -140,8 +155,13 @@ where
|
||||||
{
|
{
|
||||||
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
let len = crate::de::decode_slice_len(&mut decoder)?;
|
let len = crate::de::decode_slice_len(&mut decoder)?;
|
||||||
|
decoder.claim_container_read::<T>(len)?;
|
||||||
|
|
||||||
let mut map = VecDeque::with_capacity(len);
|
let mut map = VecDeque::with_capacity(len);
|
||||||
for _ in 0..len {
|
for _ in 0..len {
|
||||||
|
// See the documentation on `unclaim_bytes_read` as to why we're doing this here
|
||||||
|
decoder.unclaim_bytes_read(core::mem::size_of::<T>());
|
||||||
|
|
||||||
let key = T::decode(&mut decoder)?;
|
let key = T::decode(&mut decoder)?;
|
||||||
map.push_back(key);
|
map.push_back(key);
|
||||||
}
|
}
|
||||||
|
|
@ -168,8 +188,13 @@ where
|
||||||
{
|
{
|
||||||
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
let len = crate::de::decode_slice_len(&mut decoder)?;
|
let len = crate::de::decode_slice_len(&mut decoder)?;
|
||||||
|
decoder.claim_container_read::<T>(len)?;
|
||||||
|
|
||||||
let mut vec = Vec::with_capacity(len);
|
let mut vec = Vec::with_capacity(len);
|
||||||
for _ in 0..len {
|
for _ in 0..len {
|
||||||
|
// See the documentation on `unclaim_bytes_read` as to why we're doing this here
|
||||||
|
decoder.unclaim_bytes_read(core::mem::size_of::<T>());
|
||||||
|
|
||||||
vec.push(T::decode(&mut decoder)?);
|
vec.push(T::decode(&mut decoder)?);
|
||||||
}
|
}
|
||||||
Ok(vec)
|
Ok(vec)
|
||||||
|
|
|
||||||
|
|
@ -369,8 +369,13 @@ where
|
||||||
{
|
{
|
||||||
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
fn decode<D: Decoder>(mut decoder: D) -> Result<Self, DecodeError> {
|
||||||
let len = crate::de::decode_slice_len(&mut decoder)?;
|
let len = crate::de::decode_slice_len(&mut decoder)?;
|
||||||
|
decoder.claim_container_read::<(K, V)>(len)?;
|
||||||
|
|
||||||
let mut map = HashMap::with_capacity(len);
|
let mut map = HashMap::with_capacity(len);
|
||||||
for _ in 0..len {
|
for _ in 0..len {
|
||||||
|
// See the documentation on `unclaim_bytes_read` as to why we're doing this here
|
||||||
|
decoder.unclaim_bytes_read(core::mem::size_of::<(K, V)>());
|
||||||
|
|
||||||
let k = K::decode(&mut decoder)?;
|
let k = K::decode(&mut decoder)?;
|
||||||
let v = V::decode(&mut decoder)?;
|
let v = V::decode(&mut decoder)?;
|
||||||
map.insert(k, v);
|
map.insert(k, v);
|
||||||
|
|
|
||||||
|
|
@ -89,3 +89,41 @@ fn test_alloc_commons() {
|
||||||
set
|
set
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_container_limits() {
|
||||||
|
use bincode::{error::DecodeError, Decode};
|
||||||
|
|
||||||
|
const DECODE_LIMIT: usize = 100_000;
|
||||||
|
|
||||||
|
// for this test we'll create a malformed package of a lot of bytes
|
||||||
|
let test_cases = &[
|
||||||
|
// u64::max_value(), should overflow
|
||||||
|
bincode::encode_to_vec(u64::max_value(), Configuration::standard()).unwrap(),
|
||||||
|
// A high value which doesn't overflow, but exceeds the decode limit
|
||||||
|
bincode::encode_to_vec(DECODE_LIMIT as u64, Configuration::standard()).unwrap(),
|
||||||
|
];
|
||||||
|
|
||||||
|
fn validate_fail<T: Decode + core::fmt::Debug>(slice: &[u8]) {
|
||||||
|
let result = bincode::decode_from_slice::<T, _>(
|
||||||
|
slice,
|
||||||
|
Configuration::standard().with_limit::<DECODE_LIMIT>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(result.unwrap_err(), DecodeError::LimitExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
for slice in test_cases {
|
||||||
|
validate_fail::<BinaryHeap<i32>>(slice);
|
||||||
|
validate_fail::<BTreeMap<i32, i32>>(slice);
|
||||||
|
validate_fail::<BTreeSet<i32>>(slice);
|
||||||
|
validate_fail::<VecDeque<i32>>(slice);
|
||||||
|
validate_fail::<Vec<i32>>(slice);
|
||||||
|
validate_fail::<String>(slice);
|
||||||
|
validate_fail::<Box<[u8]>>(slice);
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
{
|
||||||
|
validate_fail::<std::collections::HashMap<i32, i32>>(slice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue