Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

include apache licenced fastrlp as reth-rlp #63

Merged
merged 3 commits into from
Oct 14, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
324 changes: 312 additions & 12 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
members = [
"bin/reth",
"crate-template",
"crates/common/rlp",
"crates/common/rlp-derive",
"crates/db",
"crates/executor",
"crates/interfaces",
Expand Down
16 changes: 16 additions & 0 deletions crates/common/rlp-derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "reth-rlp-derive"
version = "0.1.1"
license = "Apache-2.0"
edition = "2021"
description = "Procedural macros for fastrlp"
repository = "https://github.com/foundry-rs/reth"

[lib]
proc-macro = true

[dependencies]
bytes = { version = "1", default-features = false }
syn = "1"
quote = "1"
proc-macro2 = "1"
95 changes: 95 additions & 0 deletions crates/common/rlp-derive/src/de.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use proc_macro2::TokenStream;
use quote::quote;

pub fn impl_decodable(ast: &syn::DeriveInput) -> TokenStream {
let body = if let syn::Data::Struct(s) = &ast.data {
s
} else {
panic!("#[derive(RlpDecodable)] is only defined for structs.");
};

let stmts: Vec<_> =
body.fields.iter().enumerate().map(|(i, field)| decodable_field(i, field)).collect();
let name = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

let impl_block = quote! {
impl #impl_generics reth_rlp::Decodable for #name #ty_generics #where_clause {
fn decode(mut buf: &mut &[u8]) -> Result<Self, reth_rlp::DecodeError> {
let b = &mut &**buf;
let rlp_head = reth_rlp::Header::decode(b)?;

if !rlp_head.list {
return Err(reth_rlp::DecodeError::UnexpectedString);
}

let started_len = b.len();
let this = Self {
#(#stmts)*
};

let consumed = started_len - b.len();
if consumed != rlp_head.payload_length {
return Err(reth_rlp::DecodeError::ListLengthMismatch {
expected: rlp_head.payload_length,
got: consumed,
});
}

*buf = *b;

Ok(this)
}
}
};

quote! {
const _: () = {
extern crate reth_rlp;
#impl_block
};
}
}

pub fn impl_decodable_wrapper(ast: &syn::DeriveInput) -> TokenStream {
let body = if let syn::Data::Struct(s) = &ast.data {
s
} else {
panic!("#[derive(RlpEncodableWrapper)] is only defined for structs.");
};

assert_eq!(
body.fields.iter().count(),
1,
"#[derive(RlpEncodableWrapper)] is only defined for structs with one field."
);

let name = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

let impl_block = quote! {
impl #impl_generics reth_rlp::Decodable for #name #ty_generics #where_clause {
fn decode(buf: &mut &[u8]) -> Result<Self, reth_rlp::DecodeError> {
Ok(Self(reth_rlp::Decodable::decode(buf)?))
}
}
};

quote! {
const _: () = {
extern crate reth_rlp;
#impl_block
};
}
}

fn decodable_field(index: usize, field: &syn::Field) -> TokenStream {
let id = if let Some(ident) = &field.ident {
quote! { #ident }
} else {
let index = syn::Index::from(index);
quote! { #index }
};

quote! { #id: reth_rlp::Decodable::decode(b)?, }
}
152 changes: 152 additions & 0 deletions crates/common/rlp-derive/src/en.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
use proc_macro2::TokenStream;
use quote::quote;

pub fn impl_encodable(ast: &syn::DeriveInput) -> TokenStream {
let body = if let syn::Data::Struct(s) = &ast.data {
s
} else {
panic!("#[derive(RlpEncodable)] is only defined for structs.");
};

let length_stmts: Vec<_> =
body.fields.iter().enumerate().map(|(i, field)| encodable_length(i, field)).collect();

let stmts: Vec<_> =
body.fields.iter().enumerate().map(|(i, field)| encodable_field(i, field)).collect();
let name = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

let impl_block = quote! {
trait E {
fn rlp_header(&self) -> reth_rlp::Header;
}

impl #impl_generics E for #name #ty_generics #where_clause {
fn rlp_header(&self) -> reth_rlp::Header {
let mut rlp_head = reth_rlp::Header { list: true, payload_length: 0 };
#(#length_stmts)*
rlp_head
}
}

impl #impl_generics reth_rlp::Encodable for #name #ty_generics #where_clause {
fn length(&self) -> usize {
let rlp_head = E::rlp_header(self);
return reth_rlp::length_of_length(rlp_head.payload_length) + rlp_head.payload_length;
}
fn encode(&self, out: &mut dyn reth_rlp::BufMut) {
E::rlp_header(self).encode(out);
#(#stmts)*
}
}
};

quote! {
const _: () = {
extern crate reth_rlp;
#impl_block
};
}
}

pub fn impl_encodable_wrapper(ast: &syn::DeriveInput) -> TokenStream {
let body = if let syn::Data::Struct(s) = &ast.data {
s
} else {
panic!("#[derive(RlpEncodableWrapper)] is only defined for structs.");
};

let ident = {
let fields: Vec<_> = body.fields.iter().collect();
if fields.len() == 1 {
let field = fields.first().expect("fields.len() == 1; qed");
field_ident(0, field)
} else {
panic!("#[derive(RlpEncodableWrapper)] is only defined for structs with one field.")
}
};

let name = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

let impl_block = quote! {
impl #impl_generics reth_rlp::Encodable for #name #ty_generics #where_clause {
fn length(&self) -> usize {
self.#ident.length()
}
fn encode(&self, out: &mut dyn reth_rlp::BufMut) {
self.#ident.encode(out)
}
}
};

quote! {
const _: () = {
extern crate reth_rlp;
#impl_block
};
}
}

pub fn impl_max_encoded_len(ast: &syn::DeriveInput) -> TokenStream {
let body = if let syn::Data::Struct(s) = &ast.data {
s
} else {
panic!("#[derive(RlpEncodable)] is only defined for structs.");
};

let stmts: Vec<_> = body
.fields
.iter()
.enumerate()
.map(|(index, field)| encodable_max_length(index, field))
.collect();
let name = &ast.ident;

let impl_block = quote! {
unsafe impl reth_rlp::MaxEncodedLen<{ reth_rlp::const_add(reth_rlp::length_of_length(#(#stmts)*), #(#stmts)*) }> for #name {}
unsafe impl reth_rlp::MaxEncodedLenAssoc for #name {
const LEN: usize = { reth_rlp::const_add(reth_rlp::length_of_length(#(#stmts)*), { #(#stmts)* }) };
}
};

quote! {
const _: () = {
extern crate reth_rlp;
#impl_block
};
}
}

fn field_ident(index: usize, field: &syn::Field) -> TokenStream {
if let Some(ident) = &field.ident {
quote! { #ident }
} else {
let index = syn::Index::from(index);
quote! { #index }
}
}

fn encodable_length(index: usize, field: &syn::Field) -> TokenStream {
let ident = field_ident(index, field);

quote! { rlp_head.payload_length += reth_rlp::Encodable::length(&self.#ident); }
}

fn encodable_max_length(index: usize, field: &syn::Field) -> TokenStream {
let fieldtype = &field.ty;

if index == 0 {
quote! { <#fieldtype as reth_rlp::MaxEncodedLenAssoc>::LEN }
} else {
quote! { + <#fieldtype as reth_rlp::MaxEncodedLenAssoc>::LEN }
}
}

fn encodable_field(index: usize, field: &syn::Field) -> TokenStream {
let ident = field_ident(index, field);

let id = quote! { self.#ident };

quote! { reth_rlp::Encodable::encode(&#id, out); }
}
53 changes: 53 additions & 0 deletions crates/common/rlp-derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//! Derive macro for `#[derive(RlpEncodable, RlpDecodable)]`.
//!
//! For example of usage see `./tests/rlp.rs`.
//!
//! This library also supports up to 1 `#[rlp(default)]` in a struct,
//! which is similar to [`#[serde(default)]`](https://serde.rs/field-attrs.html#default)
//! with the caveat that we use the `Default` value if
//! the field deserialization fails, as we don't serialize field
//! names and there is no way to tell if it is present or not.

extern crate proc_macro;

mod de;
mod en;

use de::*;
use en::*;
use proc_macro::TokenStream;

#[proc_macro_derive(RlpEncodable, attributes(rlp))]
pub fn encodable(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
let gen = impl_encodable(&ast);
gen.into()
}

#[proc_macro_derive(RlpEncodableWrapper, attributes(rlp))]
pub fn encodable_wrapper(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
let gen = impl_encodable_wrapper(&ast);
gen.into()
}

#[proc_macro_derive(RlpMaxEncodedLen, attributes(rlp))]
pub fn max_encoded_len(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
let gen = impl_max_encoded_len(&ast);
gen.into()
}

#[proc_macro_derive(RlpDecodable, attributes(rlp))]
pub fn decodable(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
let gen = impl_decodable(&ast);
gen.into()
}

#[proc_macro_derive(RlpDecodableWrapper, attributes(rlp))]
pub fn decodable_wrapper(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
let gen = impl_decodable_wrapper(&ast);
gen.into()
}
34 changes: 34 additions & 0 deletions crates/common/rlp/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[package]
name = "reth-rlp"
version = "0.1.2"
edition = "2021"
license = "Apache-2.0"
description = "Fast RLP serialization library"
repository = "https://github.com/foundry-rs/reth"

[dependencies]
arrayvec = { version = "0.7", default-features = false }
auto_impl = "1"
bytes = { version = "1", default-features = false }
ethnum = { version = "1", default-features = false, optional = true }
ethereum-types = { version = "0.14", default-features = false, optional = true }
reth-rlp-derive = { version = "0.1", path = "../rlp-derive", optional = true }

[dev-dependencies]
reth-rlp-test = { path = ".", package = "reth-rlp", features = [
"derive",
"std",
"ethnum",
"ethereum-types",
] }
criterion = "0.4.0"
hex-literal = "0.3"

[features]
alloc = []
derive = ["reth-rlp-derive"]
std = ["alloc"]

[[bench]]
name = "bench"
harness = false
Loading