Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BREAKING CHANGE: DecodeLimit and DecodeAll extensions now advance input #314

Merged
merged 5 commits into from
Feb 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,21 @@ All notable changes to this crate are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this crate adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased
## [3.0.0] - 2022-02-02

### Fix

- Optimised the Decode::decode for [T; N] [#299](https://github.com/paritytech/parity-scale-codec/pull/299)

### Changed

- Migrated to 2021 edition, enforcing MSRV of `1.56.1`. [#298](https://github.com/paritytech/parity-scale-codec/pull/298)
- Upgrade to BitVec 1.0 [#311](https://github.com/paritytech/parity-scale-codec/pull/311)
- DecodeLimit and DecodeAll extensions now advance input [#314](https://github.com/paritytech/parity-scale-codec/pull/314)

### Added

- Add bytes::Bytes implementation [#309](https://github.com/paritytech/parity-scale-codec/pull/309)

## [2.3.1] - 2021-09-28

Expand All @@ -17,6 +30,7 @@ and this crate adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.h
## [2.3.0] - 2021-09-11

### Added

- `decode_and_advance_with_depth_limit` to the `DecodeLimit` trait. This allows advancing the cursor while decoding the input. PR #286

## [2.2.0] - 2021-07-02
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 4 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "parity-scale-codec"
description = "SCALE - Simple Concatenating Aggregated Little Endians"
version = "2.3.1"
version = "3.0.0"
wigy-opensource-developer marked this conversation as resolved.
Show resolved Hide resolved
authors = ["Parity Technologies <admin@parity.io>"]
license = "Apache-2.0"
repository = "https://github.com/paritytech/parity-scale-codec"
Expand All @@ -12,8 +12,8 @@ rust-version = "1.56.1"
[dependencies]
arrayvec = { version = "0.7", default-features = false }
serde = { version = "1.0.102", optional = true }
parity-scale-codec-derive = { path = "derive", version = "2.3.1", default-features = false, optional = true }
bitvec = { version = "1", default-features = false, features = ["alloc"], optional = true }
parity-scale-codec-derive = { path = "derive", version = "3.0.0", default-features = false, optional = true }
bitvec = { version = "1", default-features = false, features = [ "alloc" ], optional = true }
bytes = { version = "1", default-features = false, optional = true }
byte-slice-cast = { version = "1.0.0", default-features = false }
generic-array = { version = "0.14.4", optional = true }
Expand Down Expand Up @@ -59,7 +59,4 @@ chain-error = []
full = []

[workspace]
members = [
"derive",
"fuzzer",
]
members = ["derive", "fuzzer"]
2 changes: 1 addition & 1 deletion derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "parity-scale-codec-derive"
description = "Serialization and deserialization derive macro for Parity SCALE Codec"
version = "2.3.1"
version = "3.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
license = "Apache-2.0"
edition = "2021"
Expand Down
23 changes: 23 additions & 0 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Basic
hard_tabs = true
max_width = 100
use_small_heuristics = "Max"
# Imports
imports_granularity = "Crate"
reorder_imports = true
# Consistency
newline_style = "Unix"
# Format comments
comment_width = 100
wrap_comments = true
# Misc
chain_width = 80
spaces_around_ranges = false
binop_separator = "Back"
reorder_impl_items = false
match_arm_leading_pipes = "Preserve"
match_arm_blocks = false
match_block_trailing_comma = true
trailing_comma = "Vertical"
trailing_semicolon = false
use_field_init_shorthand = true
25 changes: 11 additions & 14 deletions src/decode_all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::{Error, Decode};
use crate::{Decode, Error};

/// The error message returned when `decode_all` fails.
pub(crate) const DECODE_ALL_ERR_MSG: &str = "Input buffer has still data left after decoding!";
Expand All @@ -23,12 +23,11 @@ pub trait DecodeAll: Sized {
/// Decode `Self` and consume all of the given input data.
///
/// If not all data is consumed, an error is returned.
fn decode_all(input: &[u8]) -> Result<Self, Error>;
fn decode_all(input: &mut &[u8]) -> Result<Self, Error>;
}

impl<T: Decode> DecodeAll for T {
fn decode_all(input: &[u8]) -> Result<Self, Error> {
let input = &mut &input[..];
fn decode_all(input: &mut &[u8]) -> Result<Self, Error> {
let res = T::decode(input)?;

if input.is_empty() {
Expand All @@ -42,7 +41,7 @@ impl<T: Decode> DecodeAll for T {
#[cfg(test)]
mod tests {
use super::*;
use crate::{Encode, Input, Compact, EncodeLike};
use crate::{Compact, Encode, EncodeLike, Input};

macro_rules! test_decode_all {
(
Expand All @@ -51,13 +50,13 @@ mod tests {
$(
{
let mut encoded = <$type as Encode>::encode(&$value);
<$type>::decode_all(&encoded).expect(
<$type>::decode_all(&mut encoded.as_slice()).expect(
&format!("`{} => {}` decodes all!", stringify!($type), stringify!($value)),
);

encoded.extend(&[1, 2, 3, 4, 5, 6]);
assert_eq!(
<$type>::decode_all(&encoded).unwrap_err().to_string(),
<$type>::decode_all(&mut encoded.as_slice()).unwrap_err().to_string(),
"Input buffer has still data left after decoding!",
);
}
Expand Down Expand Up @@ -86,13 +85,11 @@ mod tests {

impl Decode for TestStruct {
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
Ok(
Self {
data: Vec::<u32>::decode(input)?,
other: u8::decode(input)?,
compact: Compact::<u128>::decode(input)?,
}
)
Ok(Self {
data: Vec::<u32>::decode(input)?,
other: u8::decode(input)?,
compact: Compact::<u128>::decode(input)?,
})
}
}

Expand Down
80 changes: 38 additions & 42 deletions src/depth_limit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,32 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::{Error, Decode, Input};
use crate::{Decode, Error, Input};

/// The error message returned when depth limit is reached.
const DECODE_MAX_DEPTH_MSG: &str = "Maximum recursion depth reached when decoding";

/// Extension trait to [`Decode`] for decoding with a maximum recursion depth.
pub trait DecodeLimit: Sized {
/// Decode `Self` with the given maximum recursion depth.
/// Decode `Self` with the given maximum recursion depth and advance `input` by the number of
/// bytes consumed.
///
/// If `limit` is hit, an error is returned.
fn decode_with_depth_limit(limit: u32, input: &[u8]) -> Result<Self, Error>;

/// Decode `Self` and advance `input` by the number of bytes consumed.
///
/// If `limit` is hit, an error is returned.
fn decode_and_advance_with_depth_limit<I: Input>(limit: u32, input: &mut I) -> Result<Self, Error>;
fn decode_with_depth_limit<I: Input>(limit: u32, input: &mut I) -> Result<Self, Error>;

/// Decode `Self` and consume all of the given input data.
///
/// If not all data is consumed or `limit` is hit, an error is returned.
fn decode_all_with_depth_limit(limit: u32, input: &[u8]) -> Result<Self, Error>;
fn decode_all_with_depth_limit(limit: u32, input: &mut &[u8]) -> Result<Self, Error>;
}


struct DepthTrackingInput<'a, I> {
input: &'a mut I,
depth: u32,
max_depth: u32,
}

impl<'a, I:Input> Input for DepthTrackingInput<'a, I> {
impl<'a, I: Input> Input for DepthTrackingInput<'a, I> {
fn remaining_len(&mut self) -> Result<Option<usize>, Error> {
self.input.remaining_len()
}
Expand Down Expand Up @@ -72,36 +67,18 @@ impl<'a, I:Input> Input for DepthTrackingInput<'a, I> {
}

impl<T: Decode> DecodeLimit for T {
fn decode_all_with_depth_limit(limit: u32, input: &[u8]) -> Result<Self, Error> {
let mut input = DepthTrackingInput {
input: &mut &input[..],
depth: 0,
max_depth: limit,
};
let res = T::decode(&mut input)?;

if input.input.is_empty() {
Ok(res)
fn decode_all_with_depth_limit(limit: u32, input: &mut &[u8]) -> Result<Self, Error> {
let t = <Self as DecodeLimit>::decode_with_depth_limit(limit, input)?;

if input.is_empty() {
Ok(t)
} else {
Err(crate::decode_all::DECODE_ALL_ERR_MSG.into())
}
}

fn decode_and_advance_with_depth_limit<I: Input>(limit: u32, input: &mut I) -> Result<Self, Error> {
let mut input = DepthTrackingInput {
input,
depth: 0,
max_depth: limit,
};
T::decode(&mut input)
}

fn decode_with_depth_limit(limit: u32, input: &[u8]) -> Result<Self, Error> {
let mut input = DepthTrackingInput {
input: &mut &input[..],
depth: 0,
max_depth: limit,
};
fn decode_with_depth_limit<I: Input>(limit: u32, input: &mut I) -> Result<Self, Error> {
let mut input = DepthTrackingInput { input, depth: 0, max_depth: limit };
T::decode(&mut input)
}
}
Expand All @@ -117,19 +94,38 @@ mod tests {
let nested: NestedVec = vec![vec![vec![vec![1]]]];
let encoded = nested.encode();

let decoded = NestedVec::decode_with_depth_limit(3, &encoded).unwrap();
let decoded = NestedVec::decode_with_depth_limit(3, &mut encoded.as_slice()).unwrap();
assert_eq!(decoded, nested);
assert!(NestedVec::decode_with_depth_limit(2, &encoded).is_err());
assert!(NestedVec::decode_with_depth_limit(2, &mut encoded.as_slice()).is_err());
}

#[test]
fn decode_and_advance_works() {
fn decode_limit_advances_input() {
type NestedVec = Vec<Vec<Vec<Vec<u8>>>>;
let nested: NestedVec = vec![vec![vec![vec![1]]]];
let encoded = &mut &nested.encode()[..];
let encoded = nested.encode();
let encoded_slice = &mut encoded.as_slice();

let decoded = Vec::<u8>::decode_and_advance_with_depth_limit(1, encoded).unwrap();
let decoded = Vec::<u8>::decode_with_depth_limit(1, encoded_slice).unwrap();
assert_eq!(decoded, vec![4]);
assert!(NestedVec::decode_with_depth_limit(3, encoded).is_err());
assert!(NestedVec::decode_with_depth_limit(3, encoded_slice).is_err());
}

#[test]
fn decode_all_with_limit_advances_input() {
type NestedVec = Vec<Vec<Vec<Vec<u8>>>>;
let nested: NestedVec = vec![vec![vec![vec![1]]]];
let mut encoded = NestedVec::encode(&nested);

let decoded = NestedVec::decode_all_with_depth_limit(3, &mut encoded.as_slice()).unwrap();
assert_eq!(decoded, nested);

encoded.extend(&[1, 2, 3, 4, 5, 6]);
assert_eq!(
NestedVec::decode_all_with_depth_limit(3, &mut encoded.as_slice())
.unwrap_err()
.to_string(),
"Input buffer has still data left after decoding!",
);
}
}