Unverified Commit 72bb694b authored by thiolliere's avatar thiolliere Committed by GitHub
Browse files

forward encoding for single field struct (#54)

* forward encoding for single field struct
* code refactor
parent efcdf2c3
......@@ -26,6 +26,46 @@ use crate::utils;
type FieldsList = Punctuated<Field, Comma>;
fn encode_single_field(
closure: &TokenStream,
field: &Field,
field_name: TokenStream,
) -> TokenStream {
let encoded_as = utils::get_encoded_as_type(field);
let compact = utils::get_enable_compact(field);
if encoded_as.is_some() && compact {
return Error::new(
Span::call_site(),
"`encoded_as` and `compact` can not be used at the same time!"
).to_compile_error();
}
if compact {
let field_type = &field.ty;
quote_spanned! {
field.span() => {
<<#field_type as _parity_codec::HasCompact>::Type as
_parity_codec::EncodeAsRef<'_, #field_type>>::RefType::from(#field_name)
.using_encoded(#closure)
}
}
} else if let Some(encoded_as) = encoded_as {
let field_type = &field.ty;
quote_spanned! {
field.span() => {
<#encoded_as as
_parity_codec::EncodeAsRef<'_, #field_type>>::RefType::from(#field_name)
.using_encoded(#closure)
}
}
} else {
quote_spanned! { field.span() =>
_parity_codec::Encode::using_encoded(&#field_name, #closure)
}
}
}
fn encode_fields<F>(
dest: &TokenStream,
fields: &FieldsList,
......@@ -79,27 +119,59 @@ fn encode_fields<F>(
}
}
pub fn quote(data: &Data, type_name: &Ident, self_: &TokenStream, dest: &TokenStream) -> TokenStream {
let call_site = Span::call_site();
match *data {
fn try_impl_encode_single_field_optimisation(data: &Data) -> Option<TokenStream> {
let ref closure = quote!(f);
let optimisation = match *data {
Data::Struct(ref data) => {
match data.fields {
Fields::Named(ref fields) if fields.named.len() == 1 => {
let field = fields.named.first().unwrap();
let ref name = field.value().ident;
Some(encode_single_field(
closure,
field.value(),
quote!(&self.#name)
))
},
Fields::Unnamed(ref fields) if fields.unnamed.len() == 1 => {
Some(encode_single_field(
closure,
fields.unnamed.first().unwrap().value(),
quote!(&self.0)
))
},
_ => None,
}
},
_ => None,
};
optimisation.map(|optimisation| {
quote! {
fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, #closure: F) -> R {
#optimisation
}
}
})
}
fn impl_encode(data: &Data, type_name: &Ident) -> TokenStream {
let self_ = quote!(self);
let ref dest = quote!(dest);
let encoding = match *data {
Data::Struct(ref data) => {
match data.fields {
Fields::Named(ref fields) => encode_fields(
dest,
&fields.named,
|_, name| quote_spanned!(call_site => &#self_.#name),
|_, name| quote!(&#self_.#name),
),
Fields::Unnamed(ref fields) => encode_fields(
dest,
&fields.unnamed,
|i, _| {
let index = Index { index: i as u32, span: call_site };
quote_spanned!(call_site => &#self_.#index)
},
|i, _| quote!(&#self_.#i),
),
Fields::Unit => quote_spanned! { call_site =>
drop(#dest);
},
Fields::Unit => quote!(drop(#dest);),
}
},
Data::Enum(ref data) => {
......@@ -116,7 +188,7 @@ pub fn quote(data: &Data, type_name: &Ident, self_: &TokenStream, dest: &TokenSt
match f.fields {
Fields::Named(ref fields) => {
let field_name = |_, ident: &Option<Ident>| quote_spanned!(call_site => #ident);
let field_name = |_, ident: &Option<Ident>| quote!(#ident);
let names = fields.named
.iter()
.enumerate()
......@@ -139,8 +211,8 @@ pub fn quote(data: &Data, type_name: &Ident, self_: &TokenStream, dest: &TokenSt
let field_name = |i, _: &Option<Ident>| {
let data = stringify(i as u8);
let ident = from_utf8(&data).expect("We never go beyond ASCII");
let ident = Ident::new(ident, call_site);
quote_spanned!(call_site => #ident)
let ident = Ident::new(ident, Span::call_site());
quote!(#ident)
};
let names = fields.unnamed
.iter()
......@@ -177,8 +249,23 @@ pub fn quote(data: &Data, type_name: &Ident, self_: &TokenStream, dest: &TokenSt
}
},
Data::Union(_) => Error::new(Span::call_site(), "Union types are not supported.").to_compile_error(),
};
quote! {
fn encode_to<EncOut: _parity_codec::Output>(&#self_, #dest: &mut EncOut) {
#encoding
}
}
}
pub fn quote(data: &Data, type_name: &Ident) -> TokenStream {
if let Some(implementation) = try_impl_encode_single_field_optimisation(data) {
implementation
} else {
impl_encode(data, type_name)
}
}
pub fn stringify(id: u8) -> [u8; 2] {
const CHARS: &[u8] = b"abcdefghijklmnopqrstuvwxyz";
let len = CHARS.len() as u8;
......
......@@ -71,15 +71,11 @@ pub fn encode_derive(input: TokenStream) -> TokenStream {
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let self_ = quote!(self);
let dest_ = quote!(dest);
let encoding = encode::quote(&input.data, name, &self_, &dest_);
let encode_impl = encode::quote(&input.data, name);
let impl_block = quote! {
impl #impl_generics _parity_codec::Encode for #name #ty_generics #where_clause {
fn encode_to<EncOut: _parity_codec::Output>(&#self_, #dest_: &mut EncOut) {
#encoding
}
#encode_impl
}
};
......
#[macro_use]
extern crate parity_codec_derive;
use parity_codec::{Encode, Decode, HasCompact, Compact, EncodeAsRef, CompactAs};
#[derive(Debug, PartialEq, Encode, Decode)]
struct S {
x: u32,
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct Sc {
#[codec(compact)]
x: u32,
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct Sh<T: HasCompact> {
#[codec(encoded_as = "<T as HasCompact>::Type")]
x: T,
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct U(u32);
#[derive(Debug, PartialEq, Encode, Decode)]
struct Uc(#[codec(compact)] u32);
#[derive(Debug, PartialEq, Encode, Decode)]
struct Uh<T: HasCompact>(#[codec(encoded_as = "<T as HasCompact>::Type")] T);
#[test]
fn test_encoding() {
let x = 3u32;
let s = S { x }.encode();
let sc = Sc { x }.encode();
let sh = Sh { x }.encode();
let u = U(x).encode();
let uc = Uc(x).encode();
let uh = Uh(x).encode();
assert_eq!(&s, &[3, 0, 0, 0]);
assert_eq!(&sc, &[12]);
assert_eq!(&sh, &[12]);
assert_eq!(&u, &[3, 0, 0, 0]);
assert_eq!(&uc, &[12]);
assert_eq!(&uh, &[12]);
}
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