Update SSZ derive macro, create failing test

This commit is contained in:
Paul Hauner 2019-05-05 12:11:25 +10:00
parent b5d9157722
commit c5f00d1666
No known key found for this signature in database
GPG Key ID: 303E4494BB28068C
2 changed files with 94 additions and 52 deletions

View File

@ -1,5 +1,5 @@
use ssz::{Decodable, Encodable}; use ssz::{Decodable, Encodable};
use ssz_derive::Encode; use ssz_derive::{Decode, Encode};
fn round_trip<T: Encodable + Decodable + std::fmt::Debug + PartialEq>(items: Vec<T>) { fn round_trip<T: Encodable + Decodable + std::fmt::Debug + PartialEq>(items: Vec<T>) {
for item in items { for item in items {
@ -40,7 +40,7 @@ fn vec_of_vec_u16_round_trip() {
round_trip(items); round_trip(items);
} }
#[derive(Debug, PartialEq, Encode)] #[derive(Debug, PartialEq, Encode, Decode)]
struct FixedLen { struct FixedLen {
a: u16, a: u16,
b: u64, b: u64,
@ -72,7 +72,18 @@ fn fixed_len_struct_encoding() {
} }
} }
#[derive(Debug, PartialEq, Encode)] #[test]
fn vec_of_fixed_len_struct() {
let items: Vec<FixedLen> = vec![
FixedLen { a: 0, b: 0, c: 0 },
FixedLen { a: 1, b: 1, c: 1 },
FixedLen { a: 1, b: 0, c: 1 },
];
round_trip(items);
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct VariableLen { struct VariableLen {
a: u16, a: u16,
b: Vec<u16>, b: Vec<u16>,
@ -119,15 +130,30 @@ fn variable_len_struct_encoding() {
} }
} }
/*
#[test] #[test]
fn vec_of_fixed_len_struct() { fn vec_of_variable_len_struct() {
let items: Vec<FixedLen> = vec![ let items: Vec<VariableLen> = vec![
FixedLen { a: 0, b: 0, c: 0 }, VariableLen {
FixedLen { a: 1, b: 1, c: 1 }, a: 0,
FixedLen { a: 1, b: 0, c: 1 }, b: vec![],
c: 0,
},
VariableLen {
a: 255,
b: vec![0, 1, 2, 3],
c: 99,
},
VariableLen {
a: 255,
b: vec![0],
c: 99,
},
VariableLen {
a: 50,
b: vec![0],
c: 0,
},
]; ];
round_trip(items); round_trip(items);
} }
*/

View File

@ -1,26 +1,11 @@
#![recursion_limit="128"]
extern crate proc_macro; extern crate proc_macro;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use quote::quote; use quote::quote;
use syn::{parse_macro_input, DeriveInput}; use syn::{parse_macro_input, DeriveInput};
/*
/// Returns a Vec of `syn::Ident` for each named field in the struct.
///
/// # Panics
/// Any unnamed struct field (like in a tuple struct) will raise a panic at compile time.
fn get_named_field_idents<'a>(struct_data: &'a syn::DataStruct) -> Vec<&'a syn::Ident> {
struct_data
.fields
.iter()
.map(|f| match &f.ident {
Some(ref ident) => ident,
_ => panic!("ssz_derive only supports named struct fields."),
})
.collect()
}
*/
/// Returns a Vec of `syn::Ident` for each named field in the struct, whilst filtering out fields /// Returns a Vec of `syn::Ident` for each named field in the struct, whilst filtering out fields
/// that should not be serialized. /// that should not be serialized.
/// ///
@ -45,12 +30,9 @@ fn get_serializable_named_field_idents<'a>(
.collect() .collect()
} }
/// Returns a Vec of `syn::Ident` for each named field in the struct, whilst filtering out fields /// Returns a Vec of `syn::Type` for each named field in the struct, whilst filtering out fields
/// that should not be serialized. /// that should not be serialized.
/// fn get_serializable_field_types<'a>(struct_data: &'a syn::DataStruct) -> Vec<&'a syn::Type> {
/// # Panics
/// Any unnamed struct field (like in a tuple struct) will raise a panic at compile time.
fn get_serializable_named_field_types<'a>(struct_data: &'a syn::DataStruct) -> Vec<&'a syn::Type> {
struct_data struct_data
.fields .fields
.iter() .iter()
@ -97,7 +79,7 @@ pub fn ssz_encode_derive(input: TokenStream) -> TokenStream {
}; };
let field_idents = get_serializable_named_field_idents(&struct_data); let field_idents = get_serializable_named_field_idents(&struct_data);
let field_types_a = get_serializable_named_field_types(&struct_data); let field_types_a = get_serializable_field_types(&struct_data);
let field_types_b = field_types_a.clone(); let field_types_b = field_types_a.clone();
let output = quote! { let output = quote! {
@ -110,7 +92,7 @@ pub fn ssz_encode_derive(input: TokenStream) -> TokenStream {
} }
fn ssz_fixed_len() -> usize { fn ssz_fixed_len() -> usize {
if Self::is_ssz_fixed_len() { if <Self as Encodable>::is_ssz_fixed_len() {
#( #(
<#field_types_b as ssz::Encodable>::ssz_fixed_len() + <#field_types_b as ssz::Encodable>::ssz_fixed_len() +
)* )*
@ -134,7 +116,6 @@ pub fn ssz_encode_derive(input: TokenStream) -> TokenStream {
output.into() output.into()
} }
/*
/// Returns true if some field has an attribute declaring it should not be deserialized. /// Returns true if some field has an attribute declaring it should not be deserialized.
/// ///
/// The field attribute is: `#[ssz(skip_deserializing)]` /// The field attribute is: `#[ssz(skip_deserializing)]`
@ -161,21 +142,38 @@ pub fn ssz_decode_derive(input: TokenStream) -> TokenStream {
_ => panic!("ssz_derive only supports structs."), _ => panic!("ssz_derive only supports structs."),
}; };
let all_idents = get_named_field_idents(&struct_data); let mut register_types = vec![];
let mut decodes = vec![];
let mut is_fixed_lens = vec![];
let mut fixed_lens = vec![];
// Build quotes for fields that should be deserialized and those that should be built from // Build quotes for fields that should be deserialized and those that should be built from
// `Default`. // `Default`.
let mut quotes = vec![];
for field in &struct_data.fields { for field in &struct_data.fields {
match &field.ident { match &field.ident {
Some(ref ident) => { Some(ref ident) => {
if should_skip_deserializing(field) { if should_skip_deserializing(field) {
quotes.push(quote! { // Field should not be deserialized; use a `Default` impl to instantiate.
let #ident = <_>::default(); decodes.push(quote! {
#ident: <_>::default(),
}); });
} else { } else {
quotes.push(quote! { let ty = &field.ty;
let (#ident, i) = <_>::ssz_decode(bytes, i)?;
register_types.push(quote! {
builder.register_type::<#ty>()?;
});
decodes.push(quote! {
#ident: decoder.decode_next()?
});
is_fixed_lens.push(quote! {
<#ty as ssz::Decodable>::is_ssz_fixed_len()
});
fixed_lens.push(quote! {
<#ty as ssz::Decodable>::ssz_fixed_len()
}); });
} }
} }
@ -185,22 +183,40 @@ pub fn ssz_decode_derive(input: TokenStream) -> TokenStream {
let output = quote! { let output = quote! {
impl ssz::Decodable for #name { impl ssz::Decodable for #name {
fn ssz_decode(bytes: &[u8], i: usize) -> Result<(Self, usize), ssz::DecodeError> { fn is_ssz_fixed_len() -> bool {
#( #(
#quotes #is_fixed_lens &&
)*
true
}
fn ssz_fixed_len() -> usize {
if <Self as ssz::Decodable>::is_ssz_fixed_len() {
#(
#fixed_lens +
)*
0
} else {
ssz::BYTES_PER_LENGTH_OFFSET
}
}
fn from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
let mut builder = ssz::SszDecoderBuilder::new(bytes);
#(
#register_types
)* )*
Ok(( let mut decoder = builder.build()?;
Self {
#( Ok(Self {
#all_idents, #(
)* #decodes,
}, )*
i })
))
} }
} }
}; };
output.into() output.into()
} }
*/