forked from paradigmxyz/reth
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlib.rs
165 lines (145 loc) · 6.05 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//! Derive macros for the Compact codec traits.
#![doc(
html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
)]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![allow(unreachable_pub, missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{
bracketed,
parse::{Parse, ParseStream},
parse_macro_input, DeriveInput, Result, Token,
};
mod arbitrary;
mod compact;
#[derive(Clone)]
pub(crate) struct ZstdConfig {
compressor: syn::Path,
decompressor: syn::Path,
}
/// Derives the `Compact` trait for custom structs, optimizing serialization with a possible
/// bitflag struct.
///
/// ## Implementation:
/// The derived `Compact` implementation leverages a bitflag struct when needed to manage the
/// presence of certain field types, primarily for compacting fields efficiently. This bitflag
/// struct records information about fields that require a small, fixed number of bits for their
/// encoding, such as `bool`, `Option<T>`, or other small types.
///
/// ### Bit Sizes for Fields:
/// The amount of bits used to store a field size is determined by the field's type. For specific
/// types, a fixed number of bits is allocated (from `fn get_bit_size`):
/// - `bool`, `Option<T>`, `TransactionKind`, `Signature`: **1 bit**
/// - `TxType`: **2 bits**
/// - `u64`, `BlockNumber`, `TxNumber`, `ChainId`, `NumTransactions`: **4 bits**
/// - `u128`: **5 bits**
/// - `U256`: **6 bits**
///
/// ### Warning: Extending structs, unused bits and backwards compatibility:
/// When the bitflag only has one bit left (for example, when adding many `Option<T>` fields),
/// you should introduce a new struct (e.g., `TExtension`) with additional fields, and use
/// `Option<TExtension>` in the original struct. This approach allows further field extensions while
/// maintaining backward compatibility.
///
/// ### Limitations:
/// - Fields not listed above, or types such `Vec`, or large composite types, should manage their
/// own encoding and do not rely on the bitflag struct.
/// - `Bytes` fields and any types containing a `Bytes` field should be placed last to ensure
/// efficient decoding.
#[proc_macro_derive(Compact, attributes(maybe_zero, reth_codecs))]
pub fn derive(input: TokenStream) -> TokenStream {
compact::derive(parse_macro_input!(input as DeriveInput), None)
}
/// Adds `zstd` compression to derived [`Compact`].
#[proc_macro_derive(CompactZstd, attributes(maybe_zero, reth_codecs, reth_zstd))]
pub fn derive_zstd(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let mut compressor = None;
let mut decompressor = None;
for attr in &input.attrs {
if attr.path().is_ident("reth_zstd") {
if let Err(err) = attr.parse_nested_meta(|meta| {
if meta.path.is_ident("compressor") {
let value = meta.value()?;
let path: syn::Path = value.parse()?;
compressor = Some(path);
} else if meta.path.is_ident("decompressor") {
let value = meta.value()?;
let path: syn::Path = value.parse()?;
decompressor = Some(path);
} else {
return Err(meta.error("unsupported attribute"))
}
Ok(())
}) {
return err.to_compile_error().into()
}
}
}
let (Some(compressor), Some(decompressor)) = (compressor, decompressor) else {
return quote! {
compile_error!("missing compressor or decompressor attribute");
}
.into()
};
compact::derive(input, Some(ZstdConfig { compressor, decompressor }))
}
/// Generates tests for given type.
///
/// If `compact` or `rlp` is passed to `add_arbitrary_tests`, there will be proptest roundtrip tests
/// generated. An integer value passed will limit the number of proptest cases generated (default:
/// 256).
///
/// Examples:
/// * `#[add_arbitrary_tests]`: will derive arbitrary with no tests.
/// * `#[add_arbitrary_tests(rlp)]`: will derive arbitrary and generate rlp roundtrip proptests.
/// * `#[add_arbitrary_tests(rlp, 10)]`: will derive arbitrary and generate rlp roundtrip proptests.
/// Limited to 10 cases.
/// * `#[add_arbitrary_tests(compact, rlp)]`. will derive arbitrary and generate rlp and compact
/// roundtrip proptests.
#[proc_macro_attribute]
pub fn add_arbitrary_tests(args: TokenStream, input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let tests =
arbitrary::maybe_generate_tests(args, &ast.ident, &format_ident!("{}Tests", ast.ident));
quote! {
#ast
#tests
}
.into()
}
struct GenerateTestsInput {
args: TokenStream,
ty: syn::Type,
mod_name: syn::Ident,
}
impl Parse for GenerateTestsInput {
fn parse(input: ParseStream<'_>) -> Result<Self> {
input.parse::<Token![#]>()?;
let args;
bracketed!(args in input);
let args = args.parse::<proc_macro2::TokenStream>()?;
let ty = input.parse()?;
input.parse::<Token![,]>()?;
let mod_name = input.parse()?;
Ok(Self { args: args.into(), ty, mod_name })
}
}
/// Generates tests for given type based on passed parameters.
///
/// See `arbitrary::maybe_generate_tests` for more information.
///
/// Examples:
/// * `generate_tests!(#[rlp] MyType, MyTypeTests)`: will generate rlp roundtrip tests for `MyType`
/// in a module named `MyTypeTests`.
/// * `generate_tests!(#[compact, 10] MyType, MyTypeTests)`: will generate compact roundtrip tests
/// for `MyType` limited to 10 cases.
#[proc_macro]
pub fn generate_tests(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as GenerateTestsInput);
arbitrary::maybe_generate_tests(input.args, &input.ty, &input.mod_name).into()
}