reth_codecs_derive/compact/
flags.rs

1use super::*;
2use syn::Attribute;
3
4/// Generates the flag fieldset struct that is going to be used to store the length of fields and
5/// their potential presence.
6pub(crate) fn generate_flag_struct(
7    ident: &Ident,
8    attrs: &[Attribute],
9    has_lifetime: bool,
10    fields: &FieldList,
11    is_zstd: bool,
12) -> TokenStream2 {
13    let is_enum = fields.iter().any(|field| matches!(field, FieldTypes::EnumVariant(_)));
14
15    let flags_ident = format_ident!("{ident}Flags");
16    let mod_flags_ident = format_ident!("{ident}_flags");
17
18    let reth_codecs = parse_reth_codecs_path(attrs).unwrap();
19
20    let mut field_flags = vec![];
21
22    let total_bits = if is_enum {
23        field_flags.push(quote! {
24            pub variant: B8,
25        });
26        8
27    } else {
28        build_struct_field_flags(
29            fields
30                .iter()
31                .filter_map(|f| {
32                    if let FieldTypes::StructField(f) = f {
33                        return Some(f)
34                    }
35                    None
36                })
37                .collect::<Vec<_>>(),
38            &mut field_flags,
39            is_zstd,
40        )
41    };
42
43    if total_bits == 0 {
44        return placeholder_flag_struct(ident, &flags_ident)
45    }
46
47    let (total_bytes, unused_bits) = pad_flag_struct(total_bits, &mut field_flags);
48
49    // Provides the number of bytes used to represent the flag struct.
50    let readable_bytes = vec![
51        quote! {
52            buf.get_u8(),
53        };
54        total_bytes.into()
55    ];
56
57    let docs = format!(
58        "Fieldset that facilitates compacting the parent type. Used bytes: {total_bytes} | Unused bits: {unused_bits}"
59    );
60    let bitflag_encoded_bytes = format!("Used bytes by [`{flags_ident}`]");
61    let bitflag_unused_bits = format!("Unused bits for new fields by [`{flags_ident}`]");
62    let impl_bitflag_encoded_bytes = if has_lifetime {
63        quote! {
64            impl<'a> #ident<'a> {
65                #[doc = #bitflag_encoded_bytes]
66                pub const fn bitflag_encoded_bytes() -> usize {
67                    #total_bytes as usize
68                }
69                #[doc = #bitflag_unused_bits]
70                pub const fn bitflag_unused_bits() -> usize {
71                    #unused_bits as usize
72                }
73           }
74        }
75    } else {
76        quote! {
77            impl #ident {
78                #[doc = #bitflag_encoded_bytes]
79                pub const fn bitflag_encoded_bytes() -> usize {
80                    #total_bytes as usize
81                }
82                #[doc = #bitflag_unused_bits]
83                pub const fn bitflag_unused_bits() -> usize {
84                    #unused_bits as usize
85                }
86           }
87        }
88    };
89
90    // Generate the flag struct.
91    quote! {
92        #impl_bitflag_encoded_bytes
93        pub use #mod_flags_ident::#flags_ident;
94        #[expect(non_snake_case)]
95        mod #mod_flags_ident {
96            use #reth_codecs::__private::Buf;
97            use #reth_codecs::__private::modular_bitfield;
98            use #reth_codecs::__private::modular_bitfield::prelude::*;
99
100            #[doc = #docs]
101            #[bitfield]
102            #[derive(Clone, Copy, Debug, Default)]
103            pub struct #flags_ident {
104                #(#field_flags)*
105            }
106
107            impl #flags_ident {
108                /// Deserializes this fieldset and returns it, alongside the original slice in an advanced position.
109                pub fn from(mut buf: &[u8]) -> (Self, &[u8]) {
110                    (#flags_ident::from_bytes([
111                        #(#readable_bytes)*
112                    ]), buf)
113                }
114            }
115        }
116    }
117}
118
119/// Builds the flag struct for the user struct fields.
120///
121/// Returns the total number of bits necessary.
122fn build_struct_field_flags(
123    fields: Vec<&StructFieldDescriptor>,
124    field_flags: &mut Vec<TokenStream2>,
125    is_zstd: bool,
126) -> u8 {
127    let mut total_bits = 0;
128
129    // Find out the adequate bit size for the length of each field, if applicable.
130    for field in fields {
131        let StructFieldDescriptor { name, ftype, is_compact, use_alt_impl: _, is_reference: _ } =
132            field;
133        // This happens when dealing with a wrapper struct eg. Struct(pub U256).
134        let name = if name.is_empty() { "placeholder" } else { name };
135
136        if *is_compact {
137            if is_flag_type(ftype) {
138                let name = format_ident!("{name}_len");
139                let bitsize = get_bit_size(ftype);
140                let bsize = format_ident!("B{bitsize}");
141                total_bits += bitsize;
142
143                field_flags.push(quote! {
144                    pub #name: #bsize ,
145                });
146            } else {
147                let name = format_ident!("{name}");
148
149                field_flags.push(quote! {
150                    pub #name: bool ,
151                });
152
153                total_bits += 1;
154            }
155        }
156    }
157
158    if is_zstd {
159        field_flags.push(quote! {
160            pub __zstd: B1,
161        });
162
163        total_bits += 1;
164    }
165
166    total_bits
167}
168
169/// Total number of bits should be divisible by 8, so we might need to pad the struct with an unused
170/// skipped field.
171///
172/// Returns the total number of bytes used by the flags struct and how many unused bits.
173fn pad_flag_struct(total_bits: u8, field_flags: &mut Vec<TokenStream2>) -> (u8, u8) {
174    let remaining = 8 - total_bits % 8;
175    if remaining == 8 {
176        (total_bits / 8, 0)
177    } else {
178        let bsize = format_ident!("B{remaining}");
179        field_flags.push(quote! {
180            #[skip]
181            unused: #bsize ,
182        });
183        ((total_bits + remaining) / 8, remaining)
184    }
185}
186
187/// Placeholder struct for when there are no bitfields to be added.
188fn placeholder_flag_struct(ident: &Ident, flags: &Ident) -> TokenStream2 {
189    let bitflag_encoded_bytes = format!("Used bytes by [`{flags}`]");
190    let bitflag_unused_bits = format!("Unused bits for new fields by [`{flags}`]");
191    quote! {
192        impl #ident {
193            #[doc = #bitflag_encoded_bytes]
194            pub const fn bitflag_encoded_bytes() -> usize {
195                0
196            }
197
198            #[doc = #bitflag_unused_bits]
199            pub const fn bitflag_unused_bits() -> usize {
200                0
201            }
202        }
203
204        /// Placeholder struct for when there is no need for a fieldset. Doesn't actually write or read any data.
205        #[derive(Debug, Default)]
206        pub struct #flags {
207        }
208
209        impl #flags {
210            /// Placeholder: does not read any value.
211            pub fn from(mut buf: &[u8]) -> (Self, &[u8]) {
212                (#flags::default(), buf)
213            }
214            /// Placeholder: returns an empty array.
215            pub fn into_bytes(self) -> [u8; 0] {
216                []
217            }
218        }
219    }
220}