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

Add and implement `DecodeAll` (#126)

parent f248552d
Pipeline #43534 passed with stages
in 13 minutes and 36 seconds
// Copyright 2017, 2018 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{Error, Decode};
/// The error message returned when `decode_all` fails.
const DECODE_ALL_ERR_MSG: &str = "Input buffer has still data left after decoding!";
/// Extension trait to [`Decode`] that ensures that the given input data is consumed completly while
/// decoding.
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>;
}
impl<T: Decode> DecodeAll for T {
fn decode_all(input: &[u8]) -> Result<Self, Error> {
let input = &mut &input[..];
let res = T::decode(input)?;
if input.is_empty() {
Ok(res)
} else {
Err(DECODE_ALL_ERR_MSG.into())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Encode, Input, Compact};
macro_rules! test_decode_all {
(
$( $type:ty => $value:expr; )*
) => {
$(
{
let mut encoded = <$type as Encode>::encode(&$value);
<$type>::decode_all(&encoded).expect(
&format!("`{} => {}` decodes all!", stringify!($type), stringify!($value)),
);
encoded.extend(&[1, 2, 3, 4, 5, 6]);
assert_eq!(<$type>::decode_all(&encoded).unwrap_err().what(), DECODE_ALL_ERR_MSG);
}
)*
};
}
#[derive(Debug)]
struct TestStruct {
data: Vec<u32>,
other: u8,
compact: Compact<u128>,
}
impl Encode for TestStruct {
fn encode(&self) -> Vec<u8> {
let mut res = Vec::new();
self.data.encode_to(&mut res);
self.other.encode_to(&mut res);
self.compact.encode_to(&mut res);
res
}
}
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)?,
}
)
}
}
#[test]
fn decode_all_works() {
test_decode_all! {
u8 => 120;
u16 => 30;
u32 => 1;
u64 => 2343545;
u128 => 34358394245459854;
Vec<u8> => vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
Vec<u32> => vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
Compact<u32> => Compact(32445);
Compact<u128> => Compact(34353454453545);
TestStruct => TestStruct { data: vec![1, 2, 4, 5, 6], other: 45, compact: Compact(123234545) };
}
}
}
\ No newline at end of file
......@@ -13,73 +13,73 @@
// limitations under the License.
//! # Parity SCALE Codec
//!
//! Rust implementation of the SCALE (Simple Concatenated Aggregate Little-Endian) data format
//!
//! Rust implementation of the SCALE (Simple Concatenated Aggregate Little-Endian) data format
//! for types used in the Parity Substrate framework.
//!
//! SCALE is a light-weight format which allows encoding (and decoding) which makes it highly
//! suitable for resource-constrained execution environments like blockchain runtimes and low-power,
//!
//! SCALE is a light-weight format which allows encoding (and decoding) which makes it highly
//! suitable for resource-constrained execution environments like blockchain runtimes and low-power,
//! low-memory devices.
//!
//! It is important to note that the encoding context (knowledge of how the types and data structures look)
//! needs to be known separately at both encoding and decoding ends.
//!
//! It is important to note that the encoding context (knowledge of how the types and data structures look)
//! needs to be known separately at both encoding and decoding ends.
//! The encoded data does not include this contextual information.
//!
//! To get a better understanding of how the encoding is done for different types,
//! take a look at the
//!
//! To get a better understanding of how the encoding is done for different types,
//! take a look at the
//! [low-level data formats overview page at the Substrate docs site](https://substrate.dev/docs/en/overview/low-level-data-format).
//!
//!
//! ## Implementation
//!
//!
//! The codec is implemented using the following traits:
//!
//!
//! ### Encode
//!
//!
//! The `Encode` trait is used for encoding of data into the SCALE format. The `Encode` trait contains the following functions:
//! * `size_hint(&self) -> usize`: Gets the capacity (in bytes) required for the encoded data.
//! This is to avoid double-allocation of memory needed for the encoding.
//! It can be an estimate and does not need to be an exact number.
//! * `size_hint(&self) -> usize`: Gets the capacity (in bytes) required for the encoded data.
//! This is to avoid double-allocation of memory needed for the encoding.
//! It can be an estimate and does not need to be an exact number.
//! If the size is not known, even no good maximum, then we can skip this function from the trait implementation.
//! This is required to be a cheap operation, so should not involve iterations etc.
//! * `encode_to<T: Output>(&self, dest: &mut T)`: Encodes the value and appends it to a destination buffer.
//! * `encode(&self) -> Vec<u8>`: Encodes the type data and returns a slice.
//! * `using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R`: Encodes the type data and executes a closure on the encoded value.
//! * `using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R`: Encodes the type data and executes a closure on the encoded value.
//! Returns the result from the executed closure.
//!
//! **Note:** Implementations should override `using_encoded` for value types and `encode_to` for allocating types.
//!
//! **Note:** Implementations should override `using_encoded` for value types and `encode_to` for allocating types.
//! `size_hint` should be implemented for all types, wherever possible. Wrapper types should override all methods.
//!
//!
//! ### Decode
//!
//!
//! The `Decode` trait is used for deserialization/decoding of encoded data into the respective types.
//!
//! * `fn decode<I: Input>(value: &mut I) -> Result<Self, Error>`: Tries to decode the value from SCALE format to the type it is called on.
//!
//! * `fn decode<I: Input>(value: &mut I) -> Result<Self, Error>`: Tries to decode the value from SCALE format to the type it is called on.
//! Returns an `Err` if the decoding fails.
//!
//!
//! ### CompactAs
//!
//! The `CompactAs` trait is used for wrapping custom types/structs as compact types, which makes them even more space/memory efficient.
//!
//! The `CompactAs` trait is used for wrapping custom types/structs as compact types, which makes them even more space/memory efficient.
//! The compact encoding is described [here](https://substrate.dev/docs/en/overview/low-level-data-format#compact-general-integers).
//!
//! * `encode_as(&self) -> &Self::As`: Encodes the type (self) as a compact type.
//!
//! * `encode_as(&self) -> &Self::As`: Encodes the type (self) as a compact type.
//! The type `As` is defined in the same trait and its implementation should be compact encode-able.
//! * `decode_from(_: Self::As) -> Self`: Decodes the type (self) from a compact encode-able type.
//!
//!
//! ### HasCompact
//!
//!
//! The `HasCompact` trait, if implemented, tells that the corresponding type is a compact encode-able type.
//!
//!
//! ## Usage Examples
//!
//!
//! Following are some examples to demonstrate usage of the codec.
//!
//!
//! ### Simple types
//!
//!
//! ```
//! use parity_scale_codec_derive::{Encode, Decode};
//! use parity_scale_codec::{Encode, Decode};
//!
//!
//! #[derive(Debug, PartialEq, Encode, Decode)]
//! enum EnumType {
//! #[codec(index = "15")]
......@@ -90,108 +90,108 @@
//! b: u64,
//! },
//! }
//!
//!
//! let a = EnumType::A;
//! let b = EnumType::B(1, 2);
//! let c = EnumType::C { a: 1, b: 2 };
//!
//!
//! a.using_encoded(|ref slice| {
//! assert_eq!(slice, &b"\x0f");
//! });
//!
//!
//! b.using_encoded(|ref slice| {
//! assert_eq!(slice, &b"\x01\x01\0\0\0\x02\0\0\0\0\0\0\0");
//! });
//!
//!
//! c.using_encoded(|ref slice| {
//! assert_eq!(slice, &b"\x02\x01\0\0\0\x02\0\0\0\0\0\0\0");
//! });
//!
//!
//! let mut da: &[u8] = b"\x0f";
//! assert_eq!(EnumType::decode(&mut da).ok(), Some(a));
//!
//!
//! let mut db: &[u8] = b"\x01\x01\0\0\0\x02\0\0\0\0\0\0\0";
//! assert_eq!(EnumType::decode(&mut db).ok(), Some(b));
//!
//!
//! let mut dc: &[u8] = b"\x02\x01\0\0\0\x02\0\0\0\0\0\0\0";
//! assert_eq!(EnumType::decode(&mut dc).ok(), Some(c));
//!
//!
//! let mut dz: &[u8] = &[0];
//! assert_eq!(EnumType::decode(&mut dz).ok(), None);
//!
//!
//! # fn main() { }
//! ```
//!
//!
//! ### Compact type with HasCompact
//!
//!
//! ```
//! use parity_scale_codec_derive::{Encode, Decode};;
//! use parity_scale_codec::{Encode, Decode, Compact, HasCompact};
//!
//!
//! #[derive(Debug, PartialEq, Encode, Decode)]
//! struct Test1CompactHasCompact<T: HasCompact> {
//! #[codec(compact)]
//! bar: T,
//! }
//!
//!
//! #[derive(Debug, PartialEq, Encode, Decode)]
//! struct Test1HasCompact<T: HasCompact> {
//! #[codec(encoded_as = "<T as HasCompact>::Type")]
//! bar: T,
//! }
//!
//!
//! let test_val: (u64, usize) = (0u64, 1usize);
//!
//!
//! let encoded = Test1HasCompact { bar: test_val.0 }.encode();
//! assert_eq!(encoded.len(), test_val.1);
//! assert_eq!(<Test1CompactHasCompact<u64>>::decode(&mut &encoded[..]).unwrap().bar, test_val.0);
//!
//!
//! # fn main() { }
//! ```
//! ### Type with CompactAs
//!
//!
//! ```rust
//!
//!
//! use serde_derive::{Serialize, Deserialize};
//! use parity_scale_codec_derive::{Encode, Decode};;
//! use parity_scale_codec::{Encode, Decode, Compact, HasCompact, CompactAs};
//!
//!
//! #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
//! #[derive(PartialEq, Eq, Clone)]
//! struct StructHasCompact(u32);
//!
//!
//! impl CompactAs for StructHasCompact {
//! type As = u32;
//!
//!
//! fn encode_as(&self) -> &Self::As {
//! &12
//! }
//!
//!
//! fn decode_from(_: Self::As) -> Self {
//! StructHasCompact(12)
//! }
//! }
//!
//!
//! impl From<Compact<StructHasCompact>> for StructHasCompact {
//! fn from(_: Compact<StructHasCompact>) -> Self {
//! StructHasCompact(12)
//! }
//! }
//!
//!
//! #[derive(Debug, PartialEq, Encode, Decode)]
//! enum TestGenericHasCompact<T> {
//! A {
//! #[codec(compact)] a: T
//! },
//! }
//!
//!
//! let a = TestGenericHasCompact::A::<StructHasCompact> {
//! a: StructHasCompact(12325678),
//! };
//!
//!
//! let encoded = a.encode();
//! assert_eq!(encoded.len(), 2);
//!
//!
//! # fn main() { }
//! ```
......@@ -240,6 +240,7 @@ mod joiner;
mod keyedvec;
#[cfg(feature = "bit-vec")]
mod bit_vec;
mod decode_all;
pub use self::codec::{
Input, Output, Error, Encode, Decode, Codec, EncodeAsRef, EncodeAppend, WrapperTypeEncode,
......@@ -248,3 +249,4 @@ pub use self::codec::{
pub use self::compact::{Compact, HasCompact, CompactAs};
pub use self::joiner::Joiner;
pub use self::keyedvec::KeyedVec;
pub use self::decode_all::DecodeAll;
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