Skip to content

Commit

Permalink
BREAKING CHANGE: DecodeLimit and DecodeAll extensions now advance inp…
Browse files Browse the repository at this point in the history
…ut (#314)

* BREAKING CHANGE: DecodeLimit and DecodeAll extensions now properly advance input

* Prepare for release 3.0.0

* This repo uses hard tabs
  • Loading branch information
wigy-opensource-developer authored Feb 3, 2022
1 parent 2bffcf9 commit 0841cc4
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 67 deletions.
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"
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!",
);
}
}

0 comments on commit 0841cc4

Please sign in to comment.