From 29524591a049241926eef543c071922854ca5dae Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Sun, 21 Feb 2021 13:49:24 +0100 Subject: [PATCH] Implement basic strict codec trait and derive --- derive/src/lib.rs | 33 +++++++++++++++++++++++++ src/codec.rs | 35 +++++++++++++++++++++++++-- src/lib.rs | 2 +- tests/single_field_struct_encoding.rs | 4 +-- 4 files changed, 69 insertions(+), 5 deletions(-) diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 0b2a0746..c44d1cd1 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -321,3 +321,36 @@ pub fn compact_as_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStr wrap_with_dummy_const(impl_block) } + +/// Derive marker trait `parity_scale_codec::StrictCodec` for struct and enum. +#[proc_macro_derive(Strict, attributes(codec))] +pub fn strict_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let mut input: DeriveInput = match syn::parse(input) { + Ok(input) => input, + Err(e) => return e.to_compile_error().into(), + }; + + if let Err(e) = utils::check_attributes(&input) { + return e.to_compile_error().into(); + } + + if let Err(e) = trait_bounds::add( + &input.ident, + &mut input.generics, + &input.data, + parse_quote!(_parity_scale_codec::StrictCodec), + None, + utils::has_dumb_trait_bound(&input.attrs), + ) { + return e.to_compile_error().into(); + } + + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let impl_block = quote! { + impl #impl_generics _parity_scale_codec::StrictCodec for #name #ty_generics #where_clause {} + }; + + wrap_with_dummy_const(impl_block) +} diff --git a/src/codec.rs b/src/codec.rs index 1db43ee4..358de05f 100644 --- a/src/codec.rs +++ b/src/codec.rs @@ -235,8 +235,8 @@ pub trait Encode { /// /// # Note /// - /// This works by using a special [`Output`] that only tracks the size. So, there are no allocations inside the - /// output. However, this can not prevent allocations that some types are doing inside their own encoding. + /// This works by using a special [`Output`] that only tracks the size. So, there are no allocations inside the + /// output. However, this can not prevent allocations that some types are doing inside their own encoding. fn encoded_size(&self) -> usize { let mut size_tracker = SizeTracker { written: 0 }; self.encode_to(&mut size_tracker); @@ -309,6 +309,9 @@ impl FullEncode for S {} pub trait FullCodec: Decode + FullEncode {} impl FullCodec for S {} +/// Marker trait indicating that SCALE on the struct has strict one-to-one correspondance. +pub trait StrictCodec: Codec {} + /// A marker trait for types that wrap other encodable type. /// /// Such types should not carry any additional information @@ -489,6 +492,8 @@ impl Decode for Result { } } +impl StrictCodec for Result {} + /// Shim type because we can't do a specialised implementation for `Option` directly. #[derive(Eq, PartialEq, Clone, Copy)] pub struct OptionBool(pub Option); @@ -526,6 +531,8 @@ impl Decode for OptionBool { } } +impl StrictCodec for OptionBool {} + impl, U: Encode> EncodeLike> for Option {} impl Encode for Option { @@ -561,6 +568,8 @@ impl Decode for Option { } } +impl StrictCodec for Option {} + macro_rules! impl_for_non_zero { ( $( $name:ty ),* $(,)? ) => { $( @@ -588,6 +597,8 @@ macro_rules! impl_for_non_zero { .ok_or_else(|| Error::from("cannot create non-zero number from 0")) } } + + impl StrictCodec for $name {} )* } } @@ -717,6 +728,8 @@ macro_rules! impl_array { } impl, U: Encode> EncodeLike<[U; $n]> for [T; $n] {} + + impl StrictCodec for [T; $n] {} )* } } @@ -778,6 +791,8 @@ impl Decode for PhantomData { } } +impl StrictCodec for PhantomData {} + #[cfg(any(feature = "std", feature = "full"))] impl Decode for String { fn decode(input: &mut I) -> Result { @@ -886,6 +901,8 @@ impl Decode for Vec { } } +impl StrictCodec for Vec {} + macro_rules! impl_codec_through_iterator { ($( $type:ident @@ -990,6 +1007,8 @@ impl Decode for VecDeque { } } +impl StrictCodec for VecDeque {} + impl EncodeLike for () {} impl Encode for () { @@ -1011,6 +1030,8 @@ impl Decode for () { } } +impl StrictCodec for () {} + macro_rules! impl_len { ( $( $type:ident< $($g:ident),* > ),* ) => { $( impl<$($g),*> DecodeLength for $type<$($g),*> { @@ -1063,6 +1084,7 @@ macro_rules! tuple_impl { } impl<$one: EncodeLike<$extra>, $extra: Encode> crate::EncodeLike<($extra,)> for ($one,) {} + impl<$one: StrictCodec> StrictCodec for ($one,) {} }; (($first:ident, $fextra:ident), $( ( $rest:ident, $rextra:ident ), )+) => { impl<$first: Encode, $($rest: Encode),+> Encode for ($first, $($rest),+) { @@ -1111,6 +1133,8 @@ macro_rules! tuple_impl { } } + impl<$first: StrictCodec, $($rest: StrictCodec),+> StrictCodec for ($first, $($rest),+) {} + tuple_impl!( $( ($rest, $rextra), )+ ); } } @@ -1151,6 +1175,8 @@ macro_rules! impl_endians { Ok(<$t>::from_le_bytes(buf)) } } + + impl StrictCodec for $t {} )* } } macro_rules! impl_one_byte { @@ -1176,6 +1202,8 @@ macro_rules! impl_one_byte { Ok(input.read_byte()? as $t) } } + + impl StrictCodec for $t {} )* } } @@ -1205,6 +1233,8 @@ impl Decode for bool { } } +impl StrictCodec for bool {} + impl Encode for Duration { fn size_hint(&self) -> usize { mem::size_of::() + mem::size_of::() @@ -1230,6 +1260,7 @@ impl Decode for Duration { } impl EncodeLike for Duration {} +impl StrictCodec for Duration {} #[cfg(test)] mod tests { diff --git a/src/lib.rs b/src/lib.rs index ca0d8c7a..54f5d5f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -278,7 +278,7 @@ mod error; pub use self::error::Error; pub use self::codec::{ Input, Output, Decode, Encode, Codec, EncodeAsRef, WrapperTypeEncode, WrapperTypeDecode, - OptionBool, DecodeLength, FullCodec, FullEncode, + OptionBool, DecodeLength, FullCodec, FullEncode, StrictCodec, }; #[cfg(feature = "std")] pub use self::codec::IoReader; diff --git a/tests/single_field_struct_encoding.rs b/tests/single_field_struct_encoding.rs index 5f51892c..2bdaf152 100644 --- a/tests/single_field_struct_encoding.rs +++ b/tests/single_field_struct_encoding.rs @@ -2,10 +2,10 @@ use parity_scale_codec_derive::{Encode, Decode, CompactAs}; #[cfg(feature="derive")] use parity_scale_codec::CompactAs; -use parity_scale_codec::{Compact, Decode, Encode, HasCompact}; +use parity_scale_codec::{Compact, Decode, Encode, HasCompact, Strict}; use serde_derive::{Serialize, Deserialize}; -#[derive(Debug, PartialEq, Encode, Decode)] +#[derive(Debug, PartialEq, Encode, Decode, Strict)] struct S { x: u32, }