Unverified Commit 07850f2e authored by Bastian Köcher's avatar Bastian Köcher Committed by GitHub
Browse files

Add `EncodeLike` (#145)

* Add `EncodeLike`

* Some stuff

* Make it work

* Review feedback

* Add example

* Update README

* Add bound
parent a6d9c0aa
Pipeline #44961 passed with stages
in 13 minutes and 31 seconds
......@@ -55,6 +55,12 @@ The type `As` is defined in the same trait and its implementation should be comp
The `HasCompact` trait, if implemented, tells that the corresponding type is a compact encode-able type.
### EncodeLike
The `EncodeLike` trait needs to be implemented for each type manually. When using derive, it is
done automatically for you. Basically the trait gives you the opportunity to accept multiple types
to a function that all encode to the same representation.
## Usage Examples
Following are some examples to demonstrate usage of the codec.
......@@ -62,6 +68,7 @@ Following are some examples to demonstrate usage of the codec.
### Simple types
```rust
use parity_scale_codec::{Encode, Decode};
#[derive(Debug, PartialEq, Encode, Decode)]
......@@ -108,6 +115,7 @@ assert_eq!(EnumType::decode(&mut dz).ok(), None);
### Compact type with HasCompact
```rust
use parity_scale_codec::{Encode, Decode, Compact, HasCompact};
#[derive(Debug, PartialEq, Encode, Decode)]
......
......@@ -102,6 +102,8 @@ pub fn encode_derive(input: TokenStream) -> TokenStream {
impl #impl_generics _parity_scale_codec::Encode for #name #ty_generics #where_clause {
#encode_impl
}
impl #impl_generics _parity_scale_codec::EncodeLike for #name #ty_generics #where_clause {}
};
wrap_with_dummy_const(&input, "_IMPL_ENCODE_FOR_", impl_block)
......
......@@ -14,10 +14,12 @@
//! Serialisation.
use crate::alloc::vec::Vec;
use crate::alloc::boxed::Box;
use crate::alloc::collections::{BTreeMap, BTreeSet, VecDeque, LinkedList, BinaryHeap};
use crate::compact::Compact;
use crate::{
Compact, EncodeLike,
alloc::{
vec::Vec, boxed::Box, collections::{BTreeMap, BTreeSet, VecDeque, LinkedList, BinaryHeap},
}
};
#[cfg(any(feature = "std", feature = "full"))]
use crate::alloc::{
......@@ -27,9 +29,8 @@ use crate::alloc::{
rc::Rc,
};
use core::{mem, slice, ops::Deref};
use core::marker::PhantomData;
use core::iter::FromIterator;
use core::{mem, slice, ops::Deref, marker::PhantomData, iter::FromIterator};
use arrayvec::ArrayVec;
#[cfg(feature = "std")]
......@@ -285,23 +286,41 @@ impl<S: Decode + Encode> Codec for S {}
/// is assumed to be the same as the wrapped type.
pub trait WrapperTypeEncode: Deref {}
impl<T> WrapperTypeEncode for Vec<T> {}
impl<T: ?Sized> WrapperTypeEncode for Box<T> {}
impl<T: ?Sized + Encode> EncodeLike for Box<T> {}
impl<T: Encode> EncodeLike<T> for Box<T> {}
impl<'a, T: ?Sized> WrapperTypeEncode for &'a T {}
impl<'a, T: Encode> EncodeLike for &'a T {}
impl<'a, T: ?Sized> WrapperTypeEncode for &'a mut T {}
impl<'a, T: Encode> EncodeLike for &'a mut T {}
#[cfg(any(feature = "std", feature = "full"))]
impl<'a, T: ToOwned + ?Sized> WrapperTypeEncode for Cow<'a, T> {}
#[cfg(any(feature = "std", feature = "full"))]
impl<T: ?Sized> WrapperTypeEncode for Arc<T> {}
#[cfg(any(feature = "std", feature = "full"))]
impl<T: ?Sized> WrapperTypeEncode for Rc<T> {}
#[cfg(any(feature = "std", feature = "full"))]
impl WrapperTypeEncode for String {}
mod feature_full_wrapper_type_encode {
use super::*;
impl<'a, T: ToOwned + ?Sized> WrapperTypeEncode for Cow<'a, T> {}
impl<'a, T: ToOwned + Encode + ?Sized> EncodeLike for Cow<'a, T> {}
impl<'a, T: ToOwned + Encode> EncodeLike<T> for Cow<'a, T> {}
impl<T: ?Sized> WrapperTypeEncode for Arc<T> {}
impl<T: Encode> EncodeLike for Arc<T> {}
impl<T: Encode> EncodeLike<T> for Arc<T> {}
impl<T: ?Sized> WrapperTypeEncode for Rc<T> {}
impl<T: Encode> EncodeLike for Rc<T> {}
impl<T: Encode> EncodeLike<T> for Rc<T> {}
impl WrapperTypeEncode for String {}
impl EncodeLike for String {}
impl EncodeLike<&str> for String {}
impl EncodeLike<String> for &str {}
}
impl<T, X> Encode for X where
T: Encode + ?Sized,
X: WrapperTypeEncode<Target=T>,
X: WrapperTypeEncode<Target = T>,
{
fn size_hint(&self) -> usize {
(&**self).size_hint()
......@@ -376,6 +395,8 @@ impl<T: Encode, E: Encode> Encode for Result<T, E> {
}
}
impl<T: Encode, E: Encode> EncodeLike for Result<T, E> {}
impl<T: Decode, E: Decode> Decode for Result<T, E> {
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
match input.read_byte()? {
......@@ -410,6 +431,8 @@ impl Encode for OptionBool {
}
}
impl EncodeLike for OptionBool {}
impl Decode for OptionBool {
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
match input.read_byte()? {
......@@ -421,6 +444,8 @@ impl Decode for OptionBool {
}
}
impl<T: Encode> EncodeLike for Option<T> {}
impl<T: Encode> Encode for Option<T> {
fn size_hint(&self) -> usize {
1 + match *self {
......@@ -451,30 +476,34 @@ impl<T: Decode> Decode for Option<T> {
}
macro_rules! impl_array {
( $( $n:expr, )* ) => { $(
impl<T: Encode> Encode for [T; $n] {
fn encode_to<W: Output>(&self, dest: &mut W) {
for item in self.iter() {
item.encode_to(dest);
( $( $n:expr, )* ) => {
$(
impl<T: Encode> Encode for [T; $n] {
fn encode_to<W: Output>(&self, dest: &mut W) {
for item in self.iter() {
item.encode_to(dest);
}
}
}
}
impl<T: Decode> Decode for [T; $n] {
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
let mut r = ArrayVec::new();
for _ in 0..$n {
r.push(T::decode(input)?);
}
let i = r.into_inner();
match i {
Ok(a) => Ok(a),
Err(_) => Err("failed to get inner array from ArrayVec".into()),
impl<T: Decode> Decode for [T; $n] {
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
let mut r = ArrayVec::new();
for _ in 0..$n {
r.push(T::decode(input)?);
}
let i = r.into_inner();
match i {
Ok(a) => Ok(a),
Err(_) => Err("failed to get inner array from ArrayVec".into()),
}
}
}
}
)* }
impl<T: Encode> EncodeLike for [T; $n] {}
)*
}
}
impl_array!(
......@@ -496,6 +525,8 @@ impl_array!(
253, 254, 255, 256, 384, 512, 768, 1024, 2048, 4096, 8192, 16384, 32768,
);
impl EncodeLike for &str {}
impl Encode for str {
fn size_hint(&self) -> usize {
self.as_bytes().size_hint()
......@@ -523,6 +554,8 @@ impl<'a, T: ToOwned + ?Sized> Decode for Cow<'a, T>
}
}
impl<T> EncodeLike for PhantomData<T> {}
impl<T> Encode for PhantomData<T> {
fn encode_to<W: Output>(&self, _dest: &mut W) {}
}
......@@ -549,6 +582,9 @@ pub(crate) fn compact_encode_len_to<W: Output>(dest: &mut W, len: usize) -> Resu
Ok(())
}
impl<T: Encode> EncodeLike for &[T] {}
impl<T: Encode> EncodeLike<Vec<T>> for &[T] {}
impl<T: Encode> Encode for [T] {
fn size_hint(&self) -> usize {
if let IsU8::Yes = <T as Encode>::IS_U8 {
......@@ -574,6 +610,10 @@ impl<T: Encode> Encode for [T] {
}
}
impl<T> WrapperTypeEncode for Vec<T> {}
impl<T: Encode> EncodeLike for Vec<T> {}
impl<T: Encode> EncodeLike<&[T]> for Vec<T> {}
impl<T: Decode> Decode for Vec<T> {
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
<Compact<u32>>::decode(input).and_then(move |Compact(len)| {
......@@ -603,8 +643,9 @@ impl<T: Decode> Decode for Vec<T> {
macro_rules! impl_codec_through_iterator {
($(
$type:ty
{$( $encode_generics:tt )*}
{$( $decode_generics:tt )*}
{ $( $generics:ident ),* }
{ $( $encode_generics:tt )* }
{ $( $decode_generics:tt )* }
)*) => {$(
impl<$($encode_generics)*> Encode for $type {
fn encode_to<W: Output>(&self, dest: &mut W) {
......@@ -623,16 +664,22 @@ macro_rules! impl_codec_through_iterator {
})
}
}
impl<$($encode_generics)*> EncodeLike for $type {}
impl<$($encode_generics)*> EncodeLike<&[( $( $generics ),* )]> for $type {}
)*}
}
impl_codec_through_iterator! {
BTreeMap<K, V> { K: Encode , V: Encode } { K: Decode + Ord, V: Decode }
BTreeSet<T> { T: Encode } { T: Decode + Ord }
LinkedList<T> { T: Encode } { T: Decode }
BinaryHeap<T> { T: Encode } { T: Decode + Ord }
BTreeMap<K, V> { K, V } { K: Encode , V: Encode } { K: Decode + Ord, V: Decode }
BTreeSet<T> { T } { T: Encode } { T: Decode + Ord }
LinkedList<T> { T } { T: Encode } { T: Decode }
BinaryHeap<T> { T } { T: Encode } { T: Decode + Ord }
}
impl<T: Encode + Ord> EncodeLike for VecDeque<T> {}
impl<T: Encode + Ord> EncodeLike<&[T]> for VecDeque<T> {}
impl<T: Encode + Ord> Encode for VecDeque<T> {
fn encode_to<W: Output>(&self, dest: &mut W) {
compact_encode_len_to(dest, self.len()).expect("Compact encodes length");
......@@ -658,6 +705,8 @@ impl<T: Decode> Decode for VecDeque<T> {
}
}
impl EncodeLike for () {}
impl Encode for () {
fn encode_to<W: Output>(&self, _dest: &mut W) {
}
......@@ -692,7 +741,9 @@ macro_rules! impl_len {
impl_len!(Vec<T>, BTreeSet<T>, BTreeMap<K, V>, VecDeque<T>, BinaryHeap<T>, LinkedList<T>);
macro_rules! tuple_impl {
($one:ident,) => {
(
($one:ident, $extra:ident),
) => {
impl<$one: Encode> Encode for ($one,) {
fn size_hint(&self) -> usize {
self.0.size_hint()
......@@ -719,11 +770,11 @@ macro_rules! tuple_impl {
}
}
}
impl<$one: EncodeLike<$extra>, $extra: Encode> crate::EncodeLike<($extra,)> for ($one,) {}
};
($first:ident, $($rest:ident,)+) => {
impl<$first: Encode, $($rest: Encode),+>
Encode for
($first, $($rest),+) {
(($first:ident, $fextra:ident), $( ( $rest:ident, $rextra:ident ), )+) => {
impl<$first: Encode, $($rest: Encode),+> Encode for ($first, $($rest),+) {
fn size_hint(&self) -> usize {
let (
ref $first,
......@@ -744,9 +795,7 @@ macro_rules! tuple_impl {
}
}
impl<$first: Decode, $($rest: Decode),+>
Decode for
($first, $($rest),+) {
impl<$first: Decode, $($rest: Decode),+> Decode for ($first, $($rest),+) {
fn decode<INPUT: Input>(input: &mut INPUT) -> Result<Self, super::Error> {
Ok((
match $first::decode(input) {
......@@ -761,14 +810,22 @@ macro_rules! tuple_impl {
}
}
tuple_impl!($($rest,)+);
impl<$first: EncodeLike<$fextra>, $fextra: Encode,
$($rest: EncodeLike<$rextra>, $rextra: Encode),+> crate::EncodeLike<($fextra, $( $rextra ),+)>
for ($first, $($rest),+) {}
tuple_impl!( $( ($rest, $rextra), )+ );
}
}
#[allow(non_snake_case)]
mod inner_tuple_impl {
use super::{Error, Input, Output, Decode, Encode, Vec};
tuple_impl!(A, B, C, D, E, F, G, H, I, J, K,);
use super::*;
tuple_impl!(
(A0, A1), (B0, B1), (C0, C1), (D0, D1), (E0, E1), (F0, F1), (G0, G1), (H0, H1), (I0, I1),
(J0, J1), (K0, K1), (L0, L1), (M0, M1), (N0, N1), (O0, O1), (P0, P1), (Q0, Q1), (R0, R1),
);
}
/// Trait to allow conversion to a know endian representation when sensitive.
......@@ -798,6 +855,8 @@ macro_rules! impl_endians {
fn as_le_then<T, F: FnOnce(&Self) -> T>(&self, f: F) -> T { let d = self.to_le(); f(&d) }
}
impl EncodeLike for $t {}
impl Encode for $t {
fn size_hint(&self) -> usize {
mem::size_of::<$t>()
......@@ -842,6 +901,8 @@ macro_rules! impl_non_endians {
( $( $t:ty $( { $is_u8:ident } )? ),* ) => { $(
impl EndianSensitive for $t {}
impl EncodeLike for $t {}
impl Encode for $t {
$( const $is_u8: IsU8 = IsU8::Yes; )?
......@@ -1118,4 +1179,10 @@ mod tests {
assert_eq!(io_reader.0.seek(SeekFrom::Current(0)).unwrap(), 3);
assert_eq!(io_reader.remaining_len().unwrap(), 0);
}
#[test]
fn shared_references_implement_encode() {
std::sync::Arc::new(10u32).encode();
std::rc::Rc::new(10u32).encode();
}
}
......@@ -15,7 +15,7 @@
//! [Compact encoding](https://substrate.dev/docs/en/overview/low-level-data-format#compact-general-integers)
use crate::alloc::vec::Vec;
use crate::codec::{Input, Output, Error, Encode, Decode, EncodeAsRef};
use crate::{Input, Output, Error, Encode, Decode, EncodeAsRef, EncodeLike};
use arrayvec::ArrayVec;
......@@ -91,6 +91,11 @@ pub trait CompactAs: From<Compact<Self>> {
fn decode_from(_: Self::As) -> Self;
}
impl<T> EncodeLike for Compact<T>
where
for<'a> CompactRef<'a, T>: Encode,
{}
impl<T> Encode for Compact<T>
where
for<'a> CompactRef<'a, T>: Encode,
......@@ -112,6 +117,12 @@ where
}
}
impl<'a, T> EncodeLike for CompactRef<'a, T>
where
T: CompactAs,
for<'b> CompactRef<'b, T::As>: Encode,
{}
impl<'a, T> Encode for CompactRef<'a, T>
where
T: CompactAs,
......
......@@ -41,7 +41,7 @@ impl<T: Decode> DecodeAll for T {
#[cfg(test)]
mod tests {
use super::*;
use crate::{Encode, Input, Compact};
use crate::{Encode, Input, Compact, EncodeLike};
macro_rules! test_decode_all {
(
......@@ -68,6 +68,8 @@ mod tests {
compact: Compact<u128>,
}
impl EncodeLike for TestStruct {}
impl Encode for TestStruct {
fn encode(&self) -> Vec<u8> {
let mut res = Vec::new();
......
......@@ -111,7 +111,7 @@ impl<T: Encode + 'static> EncodeAppend for Vec<T> {
#[cfg(test)]
mod tests {
use super::*;
use crate::Input;
use crate::{Input, EncodeLike};
#[test]
fn vec_encode_append_works() {
......@@ -146,6 +146,8 @@ mod tests {
#[derive(Eq, PartialEq, Debug)]
struct NoCopy { data: u32 };
impl EncodeLike for NoCopy {}
impl Encode for NoCopy {
fn encode(&self) -> Vec<u8> {
self.data.encode()
......
// Copyright 2019 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::Encode;
/// A marker trait that tells the compiler that two types encode to the same representation.
///
/// E.g. `Vec<u8>` has the same encoded representation as `&[u8]`.
///
/// # Example
///
/// ```
///# use parity_scale_codec::{EncodeLike, Encode};
/// fn encode_like<T: EncodeLike<R>, R: Encode>(data: &R) {
/// data.encode();
/// }
///
/// fn main() {
/// // Just pass the a reference to the normal tuple.
/// encode_like::<(u32, u32), _>(&(1u32, 2u32));
/// // Pass a tuple of references
/// encode_like::<(u32, u32), _>(&(&1u32, &2u32));
/// // Pass a tuple of a reference and a value.
/// encode_like::<(u32, u32), _>(&(&1u32, 2u32));
/// }
/// ```
pub trait EncodeLike<T: Encode = Self>: Sized + Encode {}
impl<T: Encode> EncodeLike<&T> for T {}
impl<T: Encode> EncodeLike<T> for &T {}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::BTreeMap;
struct ComplexStuff<T>(T);
impl<T: Encode> ComplexStuff<T> {
fn complex_method<R: Encode>(value: &R) -> Vec<u8> where T: EncodeLike<R> {
value.encode()
}
}
#[test]
fn vec_and_slice_are_working() {
let slice: &[u8] = &[1, 2, 3, 4];
let data: Vec<u8> = slice.iter().copied().collect();
let data_encoded = data.encode();
let slice_encoded = ComplexStuff::<Vec<u8>>::complex_method(&slice);
assert_eq!(slice_encoded, data_encoded);
}
#[test]
fn btreemap_and_slice_are_working() {
let slice: &[(u32, u32)] = &[(1, 2), (23, 24), (28, 30), (45, 80)];
let data: BTreeMap<u32, u32> = slice.iter().copied().collect();
let data_encoded = data.encode();
let slice_encoded = ComplexStuff::<BTreeMap<u32, u32>>::complex_method(&slice);
assert_eq!(slice_encoded, data_encoded);
}
#[test]
fn interface_testing() {
let value = 10u32;
let data = (value, value, value);
let encoded = ComplexStuff::<(u32, u32, u32)>::complex_method(&data);
assert_eq!(data.encode(), encoded);
let data = (&value, &value, &value);
let encoded = ComplexStuff::<(u32, u32, u32)>::complex_method(&data);
assert_eq!(data.encode(), encoded);
let data = (&value, value, &value);
let encoded = ComplexStuff::<(u32, u32, u32)>::complex_method(&data);
assert_eq!(data.encode(), encoded);
let vec_data: Vec<u8> = vec![1, 2, 3];
ComplexStuff::<Vec<u8>>::complex_method(&vec_data);
ComplexStuff::<&'static str>::complex_method(&String::from("test"));
ComplexStuff::<&'static str>::complex_method(&"test");
let slice: &[u8] = &vec_data;
assert_eq!(
ComplexStuff::<(u32, Vec<u8>)>::complex_method(&(1u32, slice.to_vec())),
ComplexStuff::<(u32, Vec<u8>)>::complex_method(&(1u32, slice))
);
}
}
\ No newline at end of file
......@@ -70,6 +70,12 @@
//!
//! The `HasCompact` trait, if implemented, tells that the corresponding type is a compact encode-able type.
//!
//! ### EncodeLike
//!
//! The `EncodeLike` trait needs to be implemented for each type manually. When using derive, it is
//! done automatically for you. Basically the trait gives you the opportunity to accept multiple types
//! to a function that all encode to the same representation.
//!
//! ## Usage Examples
//!
//! Following are some examples to demonstrate usage of the codec.
......@@ -249,6 +255,7 @@ mod generic_array;
mod vec_array;
mod decode_all;
mod encode_append;
mod encode_like;
pub use self::codec::{
Input, Output, Error, Encode, Decode, Codec, EncodeAsRef, WrapperTypeEncode,
......@@ -261,3 +268,4 @@ pub use self::joiner::Joiner;
pub use self::keyedvec::KeyedVec;
pub use self::decode_all::DecodeAll;
pub use self::encode_append::EncodeAppend;
pub use self::encode_like::EncodeLike;
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