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
extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;

#[proc_macro_derive(Cluster)]
pub fn derive_cluster(item: TokenStream) -> TokenStream {
    let input = syn::parse_macro_input!(item as syn::ItemStruct);

    let field_names: Vec<_> = input.fields.iter().map(|field| &field.ident).collect();
    let field_names_for_pack = &field_names;
    let field_names_for_unpack = &field_names;
    let field_types: Vec<_> = input.fields.iter().map(|field| &field.ty).collect();
    let field_types_for_pack = &field_types;
    let field_types_for_unpack = &field_types;
    let struct_name = &input.ident;

    let output = quote! {
        impl ni_fpga::Datatype for #struct_name {
            const SIZE_IN_BITS: usize = 0 #( + <#field_types as ni_fpga::Datatype>::SIZE_IN_BITS )*;

            fn pack(fpga_bits: &mut ni_fpga::FpgaBits, data: &Self) -> Result<(), ni_fpga::Error> {
                let mut remaining_bits = fpga_bits;
                #(
                {
                    let (field_bits, other_remaining_bits) = remaining_bits.split_at_mut(
                        <#field_types_for_pack as ni_fpga::Datatype>::SIZE_IN_BITS
                    );
                    remaining_bits = other_remaining_bits;
                    ni_fpga::Datatype::pack(field_bits, &data.#field_names_for_pack)?;
                }
                )*
                Ok(())
            }

            #[allow(clippy::eval_order_dependence)]
            fn unpack(fpga_bits: &ni_fpga::FpgaBits) -> Result<Self, ni_fpga::Error> {
                let mut remaining_bits = fpga_bits;
                Ok(
                    Self{
                        #(
                        #field_names_for_unpack: {
                            let (field_bits, other_remaining_bits) = remaining_bits.split_at(
                                <#field_types_for_unpack as ni_fpga::Datatype>::SIZE_IN_BITS
                            );
                            remaining_bits = other_remaining_bits;
                            ni_fpga::Datatype::unpack(field_bits)?
                        }
                        ),*
                    },
                )
            }
        }
    };

    output.into()
}

#[proc_macro_derive(Enum)]
pub fn derive_enum(item: TokenStream) -> TokenStream {
    let input = syn::parse_macro_input!(item as syn::ItemEnum);

    let backing_size = match (input.variants.len() as f64).log2().ceil().exp2() as usize {
        x if x < 8 => 8,
        x if x > 64 => return quote!{compile_error!("Enum can only be derived for enums with at most 2^64 variants!")}.into(),
        x => x,
    };
    let backing_type = syn::Type::Verbatim(match backing_size {
        8 => quote! {u8},
        16 => quote! {u16},
        32 => quote! {u32},
        64 => quote! {u64},
        _ => unreachable!(),
    });
    let discriminants: Vec<_> = (0..input.variants.len())
        .map(|discriminant| {
            syn::LitInt::new(
                &discriminant.to_string(),
                proc_macro::Span::call_site().into(),
            )
        })
        .collect();
    let discriminants_for_pack = &discriminants;
    let discriminants_for_unpack = &discriminants;
    let variants: Vec<_> = input.variants.iter().map(|v| &v.ident).collect();
    let variants_for_pack = &variants;
    let variants_for_unpack = &variants;
    let enum_name = &input.ident;

    let output = quote! {
        impl ni_fpga::Datatype for #enum_name {
            const SIZE_IN_BITS: usize = #backing_size;

            fn pack(fpga_bits: &mut ni_fpga::FpgaBits, data: &Self) -> Result<(), ni_fpga::Error> {
                match data {
                    #(
                    Self::#variants_for_pack => <#backing_type as ni_fpga::Datatype>::pack(fpga_bits, &#discriminants_for_pack)?
                    ),*
                };
                Ok(())
            }

            fn unpack(fpga_bits: &ni_fpga::FpgaBits) -> Result<Self, ni_fpga::Error> {
                match <#backing_type as ni_fpga::Datatype>::unpack(fpga_bits)? {
                    #(
                    #discriminants_for_unpack => Ok(Self::#variants_for_unpack)
                    ),*,
                    unknown => Err(ni_fpga::Error::InvalidEnumDiscriminant(unknown as u64)),
                }
            }
        }
    };

    output.into()
}