reth_codecs_derive/compact/structs.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
use super::*;
#[derive(Debug)]
pub struct StructHandler<'a> {
fields_iterator: std::iter::Peekable<std::slice::Iter<'a, FieldTypes>>,
lines: Vec<TokenStream2>,
pub is_wrapper: bool,
}
impl<'a> StructHandler<'a> {
pub fn new(fields: &'a FieldList) -> Self {
StructHandler {
lines: vec![],
fields_iterator: fields.iter().peekable(),
is_wrapper: false,
}
}
pub fn next_field(&mut self) -> Option<&'a FieldTypes> {
self.fields_iterator.next()
}
pub fn generate_to(mut self) -> Vec<TokenStream2> {
while let Some(field) = self.next_field() {
match field {
FieldTypes::EnumVariant(_) | FieldTypes::EnumUnnamedField(_) => unreachable!(),
FieldTypes::StructField(field_descriptor) => self.to(field_descriptor),
}
}
self.lines
}
pub fn generate_from(&mut self, known_types: &[&str]) -> Vec<TokenStream2> {
while let Some(field) = self.next_field() {
match field {
FieldTypes::EnumVariant(_) | FieldTypes::EnumUnnamedField(_) => unreachable!(),
FieldTypes::StructField(field_descriptor) => {
self.from(field_descriptor, known_types)
}
}
}
self.lines.clone()
}
/// Generates `to_compact` code for a struct field.
fn to(&mut self, field_descriptor: &StructFieldDescriptor) {
let (name, ftype, is_compact, use_alt_impl) = field_descriptor;
let to_compact_ident = if *use_alt_impl {
format_ident!("specialized_to_compact")
} else {
format_ident!("to_compact")
};
// Should only happen on wrapper structs like `Struct(pub Field)`
if name.is_empty() {
self.is_wrapper = true;
self.lines.push(quote! {
let _len = self.0.#to_compact_ident(&mut buffer);
});
if is_flag_type(ftype) {
self.lines.push(quote! {
flags.set_placeholder_len(_len as u8);
})
}
return
}
let name = format_ident!("{name}");
let set_len_method = format_ident!("set_{name}_len");
let len = format_ident!("{name}_len");
// B256 with #[maybe_zero] attribute for example
if *is_compact && !is_flag_type(ftype) {
let itype = format_ident!("{ftype}");
let set_bool_method = format_ident!("set_{name}");
self.lines.push(quote! {
if self.#name != #itype::zero() {
flags.#set_bool_method(true);
self.#name.#to_compact_ident(&mut buffer);
};
});
} else {
self.lines.push(quote! {
let #len = self.#name.#to_compact_ident(&mut buffer);
});
}
if is_flag_type(ftype) {
self.lines.push(quote! {
flags.#set_len_method(#len as u8);
})
}
}
/// Generates `from_compact` code for a struct field.
fn from(&mut self, field_descriptor: &StructFieldDescriptor, known_types: &[&str]) {
let (name, ftype, is_compact, use_alt_impl) = field_descriptor;
let (name, len) = if name.is_empty() {
self.is_wrapper = true;
// Should only happen on wrapper structs like `Struct(pub Field)`
(format_ident!("placeholder"), format_ident!("placeholder_len"))
} else {
(format_ident!("{name}"), format_ident!("{name}_len"))
};
let from_compact_ident = if *use_alt_impl {
format_ident!("specialized_from_compact")
} else {
format_ident!("from_compact")
};
// ! Be careful before changing the following assert ! Especially if the type does not
// implement proptest tests.
//
// The limitation of the last placed field applies to fields with potentially large sizes,
// like the `Transaction` field. These fields may have inner "Bytes" fields, sometimes even
// nested further, making it impossible to check with `proc_macro`. The goal is to place
// such fields as the last ones, so we don't need to store their length separately. Instead,
// we can simply read them until the end of the buffer.
//
// However, certain types don't require this approach because they don't contain inner
// "Bytes" fields. For these types, we can add them to a "known_types" list so it doesn't
// trigger this error. These types can handle their own deserialization without
// relying on the length provided by the higher-level deserializer. For example, a
// type "T" with two "u64" fields doesn't need the length parameter from
// "T::from_compact(buf, len)" since the length of "u64" is known internally (bitpacked).
assert!(
known_types.contains(&ftype.as_str()) ||
is_flag_type(ftype) ||
self.fields_iterator.peek().is_none(),
"`{ftype}` field should be placed as the last one since it's not known.
If it's an alias type (which are not supported by proc_macro), be sure to add it to either `known_types` or `get_bit_size` lists in the derive crate."
);
if ftype == "Bytes" {
self.lines.push(quote! {
let mut #name = Bytes::new();
(#name, buf) = Bytes::from_compact(buf, buf.len() as usize);
})
} else {
let ident_type = format_ident!("{ftype}");
if !is_flag_type(ftype) {
// It's a type that handles its own length requirements. (B256, Custom, ...)
self.lines.push(quote! {
let (#name, new_buf) = #ident_type::#from_compact_ident(buf, buf.len());
})
} else if *is_compact {
self.lines.push(quote! {
let (#name, new_buf) = #ident_type::#from_compact_ident(buf, flags.#len() as usize);
});
} else {
todo!()
}
self.lines.push(quote! {
buf = new_buf;
});
}
}
}