reth_codecs_derive/compact/
flags.rs
1use super::*;
2use syn::Attribute;
3
4pub(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 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 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 pub fn from(mut buf: &[u8]) -> (Self, &[u8]) {
110 (#flags_ident::from_bytes([
111 #(#readable_bytes)*
112 ]), buf)
113 }
114 }
115 }
116 }
117}
118
119fn 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 for field in fields {
131 let StructFieldDescriptor { name, ftype, is_compact, use_alt_impl: _, is_reference: _ } =
132 field;
133 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
169fn 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
187fn 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 #[derive(Debug, Default)]
206 pub struct #flags {
207 }
208
209 impl #flags {
210 pub fn from(mut buf: &[u8]) -> (Self, &[u8]) {
212 (#flags::default(), buf)
213 }
214 pub fn into_bytes(self) -> [u8; 0] {
216 []
217 }
218 }
219 }
220}