Commit c4f354d5 authored by thiolliere's avatar thiolliere Committed by Bastian Köcher
Browse files

impl optional len (#149)



* impl optional len

* typo

* typo

* bump version

* Apply suggestions from code review
Co-Authored-By: default avatarBastian Köcher <bkchr@users.noreply.github.com>

* fix

* fix

* space
parent 389be60e
Pipeline #48167 passed with stages
in 14 minutes and 10 seconds
......@@ -82,7 +82,7 @@ version = "0.1.0"
dependencies = [
"bitvec 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"honggfuzz 0.5.45 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-scale-codec 1.0.3",
"parity-scale-codec 1.0.4",
]
[[package]]
......@@ -272,7 +272,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "parity-scale-codec"
version = "1.0.3"
version = "1.0.4"
dependencies = [
"arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
"bitvec 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
......
[package]
name = "parity-scale-codec"
description = "SCALE - Simple Concatenating Aggregated Little Endians"
version = "1.0.3"
version = "1.0.4"
authors = ["Parity Technologies <admin@parity.io>"]
license = "Apache-2.0"
repository = "https://github.com/paritytech/parity-scale-codec"
......
......@@ -14,6 +14,8 @@
//! Serialisation.
const MAX_PREALLOCATION: usize = 4 * 1024;
use crate::{
Compact, EncodeLike,
alloc::{
......@@ -96,8 +98,13 @@ impl From<&'static str> for Error {
/// Trait that allows reading of data into a slice.
pub trait Input {
/// Return remaining length of input.
fn remaining_len(&mut self) -> Result<usize, Error>;
/// Should return the remaining length of the input data. If no information about the input
/// length is available, `None` should be returned.
///
/// The length is used to constrain the preallocation while decoding. Returning a garbage
/// length can open the doors for a denial of service attack to your application.
/// Otherwise, returning `None` can decrease the performance of your application.
fn remaining_len(&mut self) -> Result<Option<usize>, Error>;
/// Read the exact number of bytes required to fill the given buffer.
///
......@@ -114,8 +121,8 @@ pub trait Input {
}
impl<'a> Input for &'a [u8] {
fn remaining_len(&mut self) -> Result<usize, Error> {
Ok(self.len())
fn remaining_len(&mut self) -> Result<Option<usize>, Error> {
Ok(Some(self.len()))
}
fn read(&mut self, into: &mut [u8]) -> Result<(), Error> {
......@@ -163,7 +170,7 @@ pub struct IoReader<R: std::io::Read + std::io::Seek>(pub R);
#[cfg(feature = "std")]
impl<R: std::io::Read + std::io::Seek> Input for IoReader<R> {
fn remaining_len(&mut self) -> Result<usize, Error> {
fn remaining_len(&mut self) -> Result<Option<usize>, Error> {
use std::convert::TryInto;
use std::io::SeekFrom;
......@@ -179,6 +186,7 @@ impl<R: std::io::Read + std::io::Seek> Input for IoReader<R> {
len.saturating_sub(old_pos)
.try_into()
.map_err(|_| "Input cannot fit into usize length".into())
.map(Some)
}
fn read(&mut self, into: &mut [u8]) -> Result<(), Error> {
......@@ -619,16 +627,42 @@ impl<T: Decode> Decode for Vec<T> {
<Compact<u32>>::decode(input).and_then(move |Compact(len)| {
let len = len as usize;
if let IsU8::Yes = <T as Decode>::IS_U8 {
if len > input.remaining_len()? {
return Err("Not enough data to decode vector".into());
let input_len = input.remaining_len()?;
// If there is input len and it cannot be pre-allocated then return directly.
if input_len.map(|l| l < len).unwrap_or(false) {
return Err("Not enough data to decode vector".into())
}
let mut r = vec![0; len];
input.read(&mut r)?;
// Note: we checked that if input_len is some then it can preallocated.
let r = if input_len.is_some() || len < MAX_PREALLOCATION {
// Here we pre-allocate the whole buffer.
let mut r = vec![0; len];
input.read(&mut r)?;
r
} else {
// Here we pre-allocate only the maximum pre-allocation
let mut r = vec![];
let mut remains = len;
while remains != 0 {
let len_read = MAX_PREALLOCATION.min(remains);
let len_filled = r.len();
r.resize(len_filled + len_read, 0);
input.read(&mut r[len_filled..])?;
remains -= len_read;
}
r
};
let r = unsafe { mem::transmute::<Vec<u8>, Vec<T>>(r) };
Ok(r)
} else {
let capacity = input.remaining_len()?.checked_div(mem::size_of::<T>())
let capacity = input.remaining_len()?
.unwrap_or(MAX_PREALLOCATION)
.checked_div(mem::size_of::<T>())
.unwrap_or(0);
let mut r = Vec::with_capacity(capacity);
for _ in 0..len {
......@@ -1170,16 +1204,16 @@ mod tests {
let mut io_reader = IoReader(std::io::Cursor::new(&[1u8, 2, 3][..]));
assert_eq!(io_reader.0.seek(SeekFrom::Current(0)).unwrap(), 0);
assert_eq!(io_reader.remaining_len().unwrap(), 3);
assert_eq!(io_reader.remaining_len().unwrap().unwrap(), 3);
assert_eq!(io_reader.read_byte().unwrap(), 1);
assert_eq!(io_reader.0.seek(SeekFrom::Current(0)).unwrap(), 1);
assert_eq!(io_reader.remaining_len().unwrap(), 2);
assert_eq!(io_reader.remaining_len().unwrap().unwrap(), 2);
assert_eq!(io_reader.read_byte().unwrap(), 2);
assert_eq!(io_reader.read_byte().unwrap(), 3);
assert_eq!(io_reader.0.seek(SeekFrom::Current(0)).unwrap(), 3);
assert_eq!(io_reader.remaining_len().unwrap(), 0);
assert_eq!(io_reader.remaining_len().unwrap().unwrap(), 0);
}
#[test]
......@@ -1187,4 +1221,32 @@ mod tests {
std::sync::Arc::new(10u32).encode();
std::rc::Rc::new(10u32).encode();
}
#[test]
fn not_limit_input_test() {
use crate::Input;
struct NoLimit<'a>(&'a [u8]);
impl<'a> Input for NoLimit<'a> {
fn remaining_len(&mut self) -> Result<Option<usize>, Error> {
Ok(None)
}
fn read(&mut self, into: &mut [u8]) -> Result<(), Error> {
self.0.read(into)
}
}
let len = MAX_PREALLOCATION * 2 + 1;
let mut i = Compact(len as u32).encode();
i.resize(i.len() + len, 0);
assert_eq!(<Vec<u8>>::decode(&mut NoLimit(&i[..])).unwrap(), vec![0u8; len]);
let i = Compact(len as u32).encode();
assert_eq!(<Vec<u8>>::decode(&mut NoLimit(&i[..])).err().unwrap().what(), "Not enough data to fill buffer");
let i = Compact(1000u32).encode();
assert_eq!(<Vec<u8>>::decode(&mut NoLimit(&i[..])).err().unwrap().what(), "Not enough data to fill buffer");
}
}
......@@ -46,8 +46,13 @@ struct PrefixInput<'a, T> {
}
impl<'a, T: 'a + Input> Input for PrefixInput<'a, T> {
fn remaining_len(&mut self) -> Result<usize, Error> {
Ok(self.input.remaining_len()?.saturating_add(self.prefix.iter().count()))
fn remaining_len(&mut self) -> Result<Option<usize>, Error> {
let len = if let Some(len) = self.input.remaining_len()? {
Some(len.saturating_add(self.prefix.iter().count()))
} else {
None
};
Ok(len)
}
fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment