superbitty_macros/bitfields/
mod.rs1mod derives;
2mod parse;
3
4use proc_macro2::{Ident, Span, TokenStream};
5use quote::{format_ident, quote, quote_spanned};
6use syn::spanned::Spanned;
7
8use self::derives::derives;
9use crate::utils::{is_unsigned_int_primitive, SynErrors};
10
11pub(crate) fn bitfields_impl(item: TokenStream) -> syn::Result<TokenStream> {
12 let mut item = syn::parse2::<parse::BitfieldsStruct>(item)?;
13
14 disallow_invalid_attributes(&item.attrs)?;
15
16 let (struct_attrs, derives) = derives(item.attrs.drain(..))?;
17
18 verify_base_ty(&item.base_ty)?;
19
20 let assert_bitfields_compatible = assert_bitfields_compatible(&item.fields);
21
22 let derives = TokenStream::from_iter(derives.into_iter().map(|derive| derive(&item)));
23
24 let base_ty = &item.base_ty;
25
26 let (bitfields, after_last_bitfield_offset) = bitfields(item.fields);
27 let assert_bitfields_size = quote! {
28 const _: () = assert!(
29 #after_last_bitfield_offset <= #base_ty::BITS,
30 "bitfield size is too big - choose another base type",
31 );
32 };
33
34 let new_method = new_method(
35 &bitfields,
36 constructor_name(&item.constructor),
37 constructor_vis(&item.constructor, &item.vis),
38 &item.base_ty,
39 );
40 let bitfields_accessors =
41 bitfields.iter().map(|bitfield| bitfield_accessors(bitfield, &item.base_ty));
42
43 let struct_vis = &item.vis;
44 let struct_kw = &item.struct_token;
45 let struct_name = &item.ident;
46 let generics = &item.generics;
47 let (impl_generics, type_generics, where_clause) = item.generics.split_for_impl();
48 let result = quote! {
49 #(#struct_attrs)*
50 #struct_vis #struct_kw #struct_name #generics(
51 ::superbitty::Raw<#base_ty>,
53 )
54 #where_clause;
55
56 #assert_bitfields_compatible
57 #assert_bitfields_size
58
59 #derives
60
61 impl #impl_generics #struct_name #type_generics
62 #where_clause
63 {
64 #new_method
65
66 #(#bitfields_accessors)*
67 }
68 };
69 Ok(result)
70}
71
72fn disallow_invalid_attributes(attrs: &[syn::Attribute]) -> syn::Result<()> {
76 let mut errors = SynErrors::default();
77 for attr in attrs {
78 if !attr.path.is_ident("doc") && !attr.path.is_ident("derive") {
79 errors.push(syn::Error::new_spanned(
80 attr,
81 "only doc comments and `#[derive(…)]` are allowed with `bitfields!`",
82 ));
83 }
84 }
85 errors.into_result()
86}
87
88struct Bitfield {
89 parse: parse::BitfieldsStructField,
90 bit_offset: TokenStream,
91 type_shift: TokenStream,
92 bits_mask: TokenStream,
93 is_last: bool,
94}
95
96fn bitfields(fields: parse::BitfieldsStructFields) -> (Vec<Bitfield>, TokenStream) {
97 let mut prev_offset = quote!(0);
98 let mut result = Vec::with_capacity(fields.fields.len());
99 let fields_count = fields.fields.len();
100 for (index, field) in fields.fields.into_iter().enumerate() {
101 let bit_offset = quote! { ( #prev_offset ) };
102 let field_ty = &field.ty;
103 let bits_mask = quote! {
104 <#field_ty as ::superbitty::BitFieldCompatible>::BITS_MASK
105 };
106 prev_offset.extend(std::iter::once(quote! {
107 + <#field_ty as ::superbitty::BitFieldCompatible>::BITS_LEN
108 }));
109 let type_shift = quote! { <#field_ty as ::superbitty::BitFieldCompatible>::SHIFT };
110 let is_last = index == fields_count - 1;
111 result.push(Bitfield { parse: field, bit_offset, type_shift, bits_mask, is_last })
112 }
113 (result, quote! { ( #prev_offset) })
114}
115
116fn constructor_name(constructor: &parse::Constructor) -> Ident {
117 match constructor {
118 Some((_, constructor_name)) => constructor_name.clone(),
119 None => syn::Ident::new("new", Span::call_site()),
120 }
121}
122fn constructor_vis<'a>(
123 constructor: &'a parse::Constructor,
124 item_vis: &'a syn::Visibility,
125) -> &'a syn::Visibility {
126 match constructor {
127 Some((constructor_vis, _)) => constructor_vis,
128 None => item_vis,
129 }
130}
131
132fn new_method(
133 bitfields: &[Bitfield],
134 method_name: syn::Ident,
135 method_vis: &syn::Visibility,
136 base_ty: &syn::Type,
137) -> TokenStream {
138 let args = bitfields.iter().map(
139 |Bitfield { parse: parse::BitfieldsStructField { ident: name, ty, .. }, .. }| quote!(#name : #ty),
140 );
141 let fields_calculation = bitfields.iter().map(
142 |Bitfield {
143 parse: parse::BitfieldsStructField { ident: name, ty, .. },
144 bit_offset,
145 type_shift,
146 ..
147 }| {
148 quote! {
149 ((<#ty as ::superbitty::BitFieldCompatible>::into_raw(#name) >> #type_shift)
150 << #bit_offset)
151 }
152 },
153 );
154 quote! {
155 #method_vis fn #method_name( #( #args, )* ) -> Self {
156 Self(
157 unsafe { ::superbitty::Raw::new(( 0 #( | #fields_calculation )* ) as #base_ty) },
161 )
162 }
163 }
164}
165
166fn bitfield_accessors(
167 Bitfield {
168 parse: parse::BitfieldsStructField { attrs, vis, ident: field_name, ty },
169 bit_offset,
170 type_shift,
171 bits_mask,
172 is_last,
173 }: &Bitfield,
174 base_ty: &syn::Type,
175) -> TokenStream {
176 let setter_name = format_ident!("set_{field_name}");
177 let mut getter_stripped_field =
178 quote! { ((::superbitty::Raw::raw(self.0) as u128) >> #bit_offset) };
179 if !is_last {
180 getter_stripped_field = quote! { (#getter_stripped_field & #bits_mask) }
181 }
182 quote_spanned! {field_name.span()=>
183 #(#attrs)* #[inline]
185 #vis fn #field_name(&self) -> #ty {
186 unsafe {
189 <#ty as ::superbitty::BitFieldCompatible>::from_raw(
190 #getter_stripped_field << #type_shift,
191 )
192 }
193 }
194
195 #[inline]
196 #[allow(dead_code)] #vis fn #setter_name(&mut self, value: #ty) {
198 let raw_without_field = (::superbitty::Raw::raw(self.0) as u128) & !(#bits_mask << #bit_offset);
199 let field_in_place =
200 (<#ty as ::superbitty::BitFieldCompatible>::into_raw(value) >> #type_shift)
201 << #bit_offset;
202 *unsafe { ::superbitty::Raw::get_mut(&mut self.0) } =
205 (raw_without_field | field_in_place) as #base_ty;
206 }
207 }
208}
209
210fn verify_base_ty(base_ty: &syn::Type) -> syn::Result<()> {
211 if is_unsigned_int_primitive(base_ty) {
213 return Ok(());
214 }
215
216 return Err(syn::Error::new_spanned(
217 base_ty,
218 "unsupported base type for `bitfields!`: only primitive unsigned types are supported",
219 ));
220}
221
222fn assert_bitfields_compatible(fields: &parse::BitfieldsStructFields) -> TokenStream {
223 let field_asserts = fields.fields.iter().map(|parse::BitfieldsStructField { ty, .. }| {
224 quote_spanned! {ty.span()=>
225 ::superbitty::__helpers::assert_bitfield_compatible::<#ty>();
226 }
227 });
228 quote! {
229 const _: () = {
230 #(#field_asserts)*
231 };
232 }
233}