alignment_exporter_derive/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, Data, DeriveInput};
4
5/// Any struct that uses this procedural macro is automatically annotated with `#[repr(C)]`. Learn more about the significance of this annotation and why it is required [here](https://doc.rust-lang.org/nomicon/other-reprs.html#reprc).
6#[proc_macro_attribute]
7pub fn export_alignment(_attr: TokenStream, item: TokenStream) -> TokenStream {
8    let input = parse_macro_input!(item as DeriveInput);
9    let struct_name = &input.ident;
10    let data = match &input.data {
11        Data::Struct(data) => data,
12        _ => {
13            return TokenStream::from(quote! {
14                compile_error!("export_alignment can only be used on structs");
15            });
16        }
17    };
18    let mut code = Vec::new();
19    if let syn::Fields::Named(ref fields) = data.fields {
20        for field in &fields.named {
21            let ty = &field.ty;
22            code.push(quote! {{
23                let size = std::mem::size_of::<#ty>();
24                let alignment = std::mem::align_of::<#ty>();
25                result.push(::alignment_exporter::Alignment {
26                    size,
27                    offset,
28                    ty_name: stringify!(#ty)
29                });
30                if alignment > max_alignment {
31                    max_alignment = alignment;
32                }
33                if offset % alignment != 0 {
34                    let padding = alignment - (offset % alignment);
35                    offset += padding;
36                }
37                let field_offset = offset;
38                offset += size;
39            }});
40        }
41    }
42    let output = quote! {
43        #[repr(C)]
44        #input
45
46        impl ::alignment_exporter::AlignmentExporter for #struct_name {
47            fn get_alignment() -> &'static [::alignment_exporter::Alignment] {
48                static RESULT: ::std::sync::LazyLock<::std::vec::Vec<::alignment_exporter::Alignment>> = ::std::sync::LazyLock::new(|| {
49                    let mut result = ::std::vec::Vec::new();
50                    let mut offset = 0;
51                    let mut max_alignment = 0;
52                    #(#code)*
53                    result
54                });
55                RESULT.as_slice()
56            }
57        }
58    };
59    output.into()
60}