use super::{
config::{Config, ReprKind},
field_info::FieldInfo,
BitfieldStruct,
};
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote_spanned, ToTokens};
use syn::{self, punctuated::Punctuated, spanned::Spanned as _, Token};
impl BitfieldStruct {
pub fn expand(&self, config: &Config) -> TokenStream2 {
let span = self.item_struct.span();
let check_filled = self.generate_check_for_filled(config);
let struct_definition = self.generate_struct(config);
let constructor_definition = self.generate_constructor(config);
let specifier_impl = self.generate_specifier_impl(config);
let byte_conversion_impls = self.expand_byte_conversion_impls(config);
let getters_and_setters = self.expand_getters_and_setters(config);
let bytes_check = self.expand_optional_bytes_check(config);
let repr_impls_and_checks = self.expand_repr_from_impls_and_checks(config);
let debug_impl = self.generate_debug_impl(config);
quote_spanned!(span=>
#struct_definition
#check_filled
#constructor_definition
#byte_conversion_impls
#getters_and_setters
#specifier_impl
#bytes_check
#repr_impls_and_checks
#debug_impl
)
}
pub fn generate_specifier_impl(&self, config: &Config) -> Option<TokenStream2> {
config.derive_specifier.as_ref()?;
let span = self.item_struct.span();
let ident = &self.item_struct.ident;
let (impl_generics, ty_generics, where_clause) = self.item_struct.generics.split_for_impl();
let bits = self.generate_target_or_actual_bitfield_size(config);
let next_divisible_by_8 = Self::next_divisible_by_8(&bits);
Some(quote_spanned!(span=>
const _: () = {
impl #impl_generics ::modular_bitfield::private::checks::CheckSpecifierHasAtMost128Bits for #ident #ty_generics #where_clause {
type CheckType = ::modular_bitfield::private::checks::BitCount<{(#bits <= 128) as ::core::primitive::usize}>;
}
};
impl #impl_generics ::modular_bitfield::Specifier for #ident #ty_generics #where_clause {
const BITS: ::core::primitive::usize = #bits;
type Bytes = <::modular_bitfield::private::checks::BitCount<{if #bits > 128 { 128 } else { #bits }}> as ::modular_bitfield::private::SpecifierBytes>::Bytes;
type InOut = Self;
#[inline]
fn into_bytes(
value: Self::InOut,
) -> ::core::result::Result<Self::Bytes, ::modular_bitfield::error::OutOfBounds> {
::core::result::Result::Ok(
<::modular_bitfield::private::checks::BitCount<{#next_divisible_by_8}> as ::modular_bitfield::private::ArrayBytesConversion>::array_into_bytes(
value.bytes
)
)
}
#[inline]
fn from_bytes(
bytes: Self::Bytes,
) -> ::core::result::Result<Self::InOut, ::modular_bitfield::error::InvalidBitPattern<Self::Bytes>>
{
#[allow(clippy::cast_possible_truncation)]
let __bf_max_value: Self::Bytes = (1 as Self::Bytes)
.checked_shl(Self::BITS as ::core::primitive::u32)
.unwrap_or(<Self::Bytes>::MAX);
if bytes <= __bf_max_value {
let __bf_bytes = bytes.to_le_bytes();
::core::result::Result::Ok(Self {
bytes: <::modular_bitfield::private::checks::BitCount<{#next_divisible_by_8}> as ::modular_bitfield::private::ArrayBytesConversion>::bytes_into_array(bytes)
})
} else {
::core::result::Result::Err(::modular_bitfield::error::InvalidBitPattern::new(bytes))
}
}
}
))
}
pub fn generate_debug_impl(&self, config: &Config) -> Option<TokenStream2> {
config.derive_debug.as_ref()?;
let span = self.item_struct.span();
let ident = &self.item_struct.ident;
let (impl_generics, ty_generics, where_clause) = self.item_struct.generics.split_for_impl();
let is_tuple = matches!(self.item_struct.fields, syn::Fields::Unnamed(_));
let builder_name = if is_tuple {
quote_spanned!(span=> debug_tuple)
} else {
quote_spanned!(span=> debug_struct)
};
let fields = self.field_infos(config).map(|info| {
let FieldInfo {
index: _,
field,
config,
} = &info;
if config.skip_getters() {
return None;
}
let field_span = field.span();
let field_name = if field.ident.is_some() {
let field_name = info.name();
quote_spanned!(field_span=> #field_name,)
} else {
<_>::default()
};
let field_ident = info.ident_frag();
let field_getter = field.ident.as_ref().map_or_else(
|| format_ident!("get_{}_or_err", field_ident),
|_| format_ident!("{}_or_err", field_ident),
);
Some(quote_spanned!(field_span=>
.field(
#field_name
self.#field_getter()
.as_ref()
.map_or_else(
|__bf_err| __bf_err as &dyn ::core::fmt::Debug,
|__bf_field| __bf_field as &dyn ::core::fmt::Debug
)
)
))
});
Some(quote_spanned!(span=>
impl #impl_generics ::core::fmt::Debug for #ident #ty_generics #where_clause {
fn fmt(&self, __bf_f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
__bf_f.#builder_name(::core::stringify!(#ident))
#( #fields )*
.finish()
}
}
))
}
fn generate_bitfield_size(&self) -> TokenStream2 {
self.item_struct
.fields
.iter()
.map(|field| -> syn::Expr {
let span = field.span();
let ty = &field.ty;
syn::parse_quote_spanned!(span=>
<#ty as ::modular_bitfield::Specifier>::BITS
)
})
.collect::<Punctuated<syn::Expr, Token![+]>>()
.into_token_stream()
}
fn generate_target_or_actual_bitfield_size(&self, config: &Config) -> TokenStream2 {
config.bits.as_ref().map_or_else(
|| self.generate_bitfield_size(),
|bits_config| {
let span = bits_config.span;
let value = bits_config.value;
quote_spanned!(span=>
#value
)
},
)
}
fn generate_filled_check_for_unaligned_bits(
&self,
config: &Config,
required_bits: usize,
) -> TokenStream2 {
let span = self.item_struct.span();
let ident = &self.item_struct.ident;
let (impl_generics, ty_generics, where_clause) = self.item_struct.generics.split_for_impl();
let actual_bits = self.generate_bitfield_size();
let check_ident = if config.filled_enabled() {
quote_spanned!(span=> CheckFillsUnalignedBits)
} else {
quote_spanned!(span=> CheckDoesNotFillUnalignedBits)
};
let comparator = if config.filled_enabled() {
quote_spanned!(span=> ==)
} else {
quote_spanned!(span=> >)
};
quote_spanned!(span=>
const _: () = {
impl #impl_generics ::modular_bitfield::private::checks::#check_ident for #ident #ty_generics #where_clause {
type CheckType = ::modular_bitfield::private::checks::BitCount<{(#required_bits #comparator #actual_bits) as ::core::primitive::usize}>;
}
};
)
}
fn generate_filled_check_for_aligned_bits(&self, config: &Config) -> TokenStream2 {
let span = self.item_struct.span();
let ident = &self.item_struct.ident;
let (impl_generics, ty_generics, where_clause) = self.item_struct.generics.split_for_impl();
let actual_bits = self.generate_bitfield_size();
let check_ident = if config.filled_enabled() {
quote_spanned!(span=> CheckTotalSizeMultipleOf8)
} else {
quote_spanned!(span=> CheckTotalSizeIsNotMultipleOf8)
};
quote_spanned!(span=>
const _: () = {
impl #impl_generics ::modular_bitfield::private::checks::#check_ident for #ident #ty_generics #where_clause {
type Size = ::modular_bitfield::private::checks::TotalSize<::modular_bitfield::private::checks::BitCount<{(#actual_bits) % 8}>>;
}
};
)
}
fn generate_check_for_filled(&self, config: &Config) -> TokenStream2 {
match config.bits.as_ref() {
Some(bits_config) => {
self.generate_filled_check_for_unaligned_bits(config, bits_config.value)
}
None => self.generate_filled_check_for_aligned_bits(config),
}
}
fn next_divisible_by_8(value: &TokenStream2) -> TokenStream2 {
let span = value.span();
quote_spanned!(span=>
(((#value - 1) / 8) + 1) * 8
)
}
fn generate_struct(&self, config: &Config) -> TokenStream2 {
let span = self.item_struct.span();
let attrs = &config.retained_attributes;
let vis = &self.item_struct.vis;
let ident = &self.item_struct.ident;
let generics = &self.item_struct.generics;
let size = self.generate_target_or_actual_bitfield_size(config);
let next_divisible_by_8 = Self::next_divisible_by_8(&size);
quote_spanned!(span=>
#( #attrs )*
#vis struct #ident #generics
{
bytes: [::core::primitive::u8; #next_divisible_by_8 / 8],
}
)
}
fn generate_constructor(&self, config: &Config) -> TokenStream2 {
let span = self.item_struct.span();
let ident = &self.item_struct.ident;
let (impl_generics, ty_generics, where_clause) = self.item_struct.generics.split_for_impl();
let size = self.generate_target_or_actual_bitfield_size(config);
let next_divisible_by_8 = Self::next_divisible_by_8(&size);
quote_spanned!(span=>
impl #impl_generics #ident #ty_generics #where_clause
{
#[allow(clippy::new_without_default)]
#[must_use]
pub const fn new() -> Self {
Self {
bytes: [0_u8; #next_divisible_by_8 / 8],
}
}
}
)
}
fn expand_optional_bytes_check(&self, config: &Config) -> Option<TokenStream2> {
let ident = &self.item_struct.ident;
config.bytes.as_ref().map(|config| {
let bytes = config.value;
quote_spanned!(config.span=>
const _: () = {
struct ExpectedBytes { __bf_unused: [::core::primitive::u8; #bytes] }
::modular_bitfield::private::static_assertions::assert_eq_size!(
ExpectedBytes,
#ident
);
};
)
})
}
fn expand_repr_from_impls_and_checks(&self, config: &Config) -> Option<TokenStream2> {
let ident = &self.item_struct.ident;
let (impl_generics, ty_generics, where_clause) = self.item_struct.generics.split_for_impl();
let where_predicates = where_clause.map(|w| &w.predicates);
config.repr.as_ref().map(|repr| {
let kind = &repr.value;
let span = repr.span;
let prim = match kind {
ReprKind::U8 => quote_spanned!(span=> ::core::primitive::u8),
ReprKind::U16 => quote_spanned!(span=> ::core::primitive::u16),
ReprKind::U32 => quote_spanned!(span=> ::core::primitive::u32),
ReprKind::U64 => quote_spanned!(span=> ::core::primitive::u64),
ReprKind::U128 => quote_spanned!(span=> ::core::primitive::u128),
};
let actual_bits = self.generate_target_or_actual_bitfield_size(config);
let trait_check_ident = match kind {
ReprKind::U8 => quote_spanned!(span=> IsU8Compatible),
ReprKind::U16 => quote_spanned!(span=> IsU16Compatible),
ReprKind::U32 => quote_spanned!(span=> IsU32Compatible),
ReprKind::U64 => quote_spanned!(span=> IsU64Compatible),
ReprKind::U128 => quote_spanned!(span=> IsU128Compatible),
};
quote_spanned!(span=>
impl #impl_generics ::core::convert::From<#prim> for #ident #ty_generics
where
::modular_bitfield::private::checks::BitCount<{#actual_bits}>: ::modular_bitfield::private::#trait_check_ident,
#where_predicates
{
#[inline]
fn from(__bf_prim: #prim) -> Self {
Self { bytes: <#prim>::to_le_bytes(__bf_prim) }
}
}
impl #impl_generics ::core::convert::From<#ident #ty_generics> for #prim
where
::modular_bitfield::private::checks::BitCount<{#actual_bits}>: ::modular_bitfield::private::#trait_check_ident,
#where_predicates
{
#[inline]
fn from(__bf_bitfield: #ident #ty_generics) -> Self {
<Self>::from_le_bytes(__bf_bitfield.bytes)
}
}
)
})
}
fn expand_byte_conversion_impls(&self, config: &Config) -> TokenStream2 {
let span = self.item_struct.span();
let ident = &self.item_struct.ident;
let (impl_generics, ty_generics, where_clause) = self.item_struct.generics.split_for_impl();
let size = self.generate_target_or_actual_bitfield_size(config);
let next_divisible_by_8 = Self::next_divisible_by_8(&size);
let bytes_ty = quote_spanned!(span=> [::core::primitive::u8; #next_divisible_by_8 / 8]);
let (from_bytes, from_impl) = if config.filled_enabled() {
(
quote_spanned!(span=>
#[inline]
#[must_use]
pub const fn from_bytes(bytes: #bytes_ty) -> Self {
Self { bytes }
}
),
quote_spanned!(span=>
impl #impl_generics ::core::convert::From<#bytes_ty> for #ident #ty_generics #where_clause {
fn from(bytes: #bytes_ty) -> Self {
Self::from_bytes(bytes)
}
}
),
)
} else {
(
quote_spanned!(span=>
#[inline]
pub fn from_bytes(
bytes: #bytes_ty
) -> ::core::result::Result<Self, ::modular_bitfield::error::OutOfBounds> {
#[allow(clippy::identity_op)]
if ::core::primitive::u16::from(bytes[(#next_divisible_by_8 / 8) - 1]) < (1 << (8 - (#next_divisible_by_8 - (#size)))) {
::core::result::Result::Ok(Self { bytes })
} else {
::core::result::Result::Err(::modular_bitfield::error::OutOfBounds)
}
}
),
quote_spanned!(span=>
impl #impl_generics ::core::convert::TryFrom<#bytes_ty> for #ident #ty_generics #where_clause {
type Error = ::modular_bitfield::error::OutOfBounds;
#[inline]
fn try_from(bytes: #bytes_ty) -> ::core::result::Result<Self, Self::Error> {
Self::from_bytes(bytes)
}
}
),
)
};
quote_spanned!(span=>
#from_impl
impl #impl_generics ::core::convert::From<#ident #ty_generics> for #bytes_ty #where_clause {
#[inline]
fn from(bytes: #ident #ty_generics) -> Self {
bytes.into_bytes()
}
}
impl #impl_generics #ident #ty_generics #where_clause {
#[inline]
pub const fn into_bytes(self) -> #bytes_ty {
self.bytes
}
#from_bytes
}
)
}
fn expand_bits_checks_for_field(field_info: FieldInfo<'_>) -> TokenStream2 {
let FieldInfo {
index: _,
field,
config,
} = field_info;
config.bits.as_ref().map(|bits| {
let ty = &field.ty;
let expected_bits = bits.value;
let expected_span = bits.span;
let actual_span = field.ty.span();
let actual_ty = quote_spanned!(actual_span=>
::modular_bitfield::private::checks::BitCount<{<#ty as ::modular_bitfield::Specifier>::BITS}>
);
quote_spanned!(expected_span=>
let _: #actual_ty = ::modular_bitfield::private::checks::BitCount::<{#expected_bits}>;
)
}).unwrap_or_default()
}
fn expand_getters_for_field(
&self,
offset: &TokenStream2,
info: &FieldInfo<'_>,
) -> Option<TokenStream2> {
let FieldInfo {
index: _,
field,
config,
} = info;
if config.skip_getters() {
return None;
}
let struct_ident = &self.item_struct.ident;
let span = field.span();
let ident = info.ident_frag();
let name = info.name();
let retained_attrs = &config.retained_attrs;
let get_ident = field
.ident
.clone()
.unwrap_or_else(|| format_ident!("get_{}", ident));
let get_checked_ident = field.ident.as_ref().map_or_else(
|| format_ident!("get_{}_or_err", ident),
|_| format_ident!("{}_or_err", ident),
);
let ty = &field.ty;
let vis = &field.vis;
let get_assert_msg =
format!("value contains invalid bit pattern for field {struct_ident}.{name}");
let getter_docs = format!("Returns the value of `{name}`.");
let checked_getter_docs = format!(
"Returns the value of `{name}`.\n\n\
# Errors\n\n\
If the returned value contains an invalid bit pattern for `{name}`.",
);
let getters = quote_spanned!(span=>
#[doc = #getter_docs]
#[inline]
#[must_use]
#( #retained_attrs )*
#vis fn #get_ident(&self) -> <#ty as ::modular_bitfield::Specifier>::InOut {
self.#get_checked_ident().expect(#get_assert_msg)
}
#[doc = #checked_getter_docs]
#[inline]
#[allow(dead_code)]
#( #retained_attrs )*
#vis fn #get_checked_ident(
&self,
) -> ::core::result::Result<
<#ty as ::modular_bitfield::Specifier>::InOut,
::modular_bitfield::error::InvalidBitPattern<<#ty as ::modular_bitfield::Specifier>::Bytes>
> {
let __bf_read: <#ty as ::modular_bitfield::Specifier>::Bytes = {
::modular_bitfield::private::read_specifier::<#ty>(&self.bytes[..], #offset)
};
<#ty as ::modular_bitfield::Specifier>::from_bytes(__bf_read)
}
);
Some(getters)
}
fn expand_setters_for_field(
&self,
offset: &TokenStream2,
info: &FieldInfo<'_>,
) -> Option<TokenStream2> {
let FieldInfo {
index: _,
field,
config,
} = info;
if config.skip_setters() {
return None;
}
let struct_ident = &self.item_struct.ident;
let span = field.span();
let retained_attrs = &config.retained_attrs;
let ident = info.ident_frag();
let name = info.name();
let ty = &field.ty;
let vis = &field.vis;
let set_ident = format_ident!("set_{}", ident);
let set_checked_ident = format_ident!("set_{}_checked", ident);
let with_ident = format_ident!("with_{}", ident);
let with_checked_ident = format_ident!("with_{}_checked", ident);
let set_assert_msg = format!("value out of bounds for field {struct_ident}.{name}");
let setter_docs = format!(
"Sets the value of `{name}` to the given value.\n\n\
# Panics\n\n\
If the given value is out of bounds for `{name}`.",
);
let checked_setter_docs = format!(
"Sets the value of `{name}` to the given value.\n\n\
# Errors\n\n\
If the given value is out of bounds for `{name}`.",
);
let with_docs = format!(
"Returns a copy of the bitfield with the value of `{name}` \
set to the given value.\n\n\
# Panics\n\n\
If the given value is out of bounds for `{name}`.",
);
let checked_with_docs = format!(
"Returns a copy of the bitfield with the value of `{name}` \
set to the given value.\n\n\
# Errors\n\n\
If the given value is out of bounds for `{name}`.",
);
let setters = quote_spanned!(span=>
#[doc = #with_docs]
#[inline]
#[allow(dead_code)]
#[must_use]
#( #retained_attrs )*
#vis fn #with_ident(
mut self,
new_val: <#ty as ::modular_bitfield::Specifier>::InOut
) -> Self {
self.#set_ident(new_val);
self
}
#[doc = #checked_with_docs]
#[inline]
#[allow(dead_code)]
#( #retained_attrs )*
#vis fn #with_checked_ident(
mut self,
new_val: <#ty as ::modular_bitfield::Specifier>::InOut,
) -> ::core::result::Result<Self, ::modular_bitfield::error::OutOfBounds> {
self.#set_checked_ident(new_val)?;
::core::result::Result::Ok(self)
}
#[doc = #setter_docs]
#[inline]
#[allow(dead_code)]
#( #retained_attrs )*
#vis fn #set_ident(&mut self, new_val: <#ty as ::modular_bitfield::Specifier>::InOut) {
self.#set_checked_ident(new_val).expect(#set_assert_msg);
}
#[doc = #checked_setter_docs]
#[inline]
#( #retained_attrs )*
#vis fn #set_checked_ident(
&mut self,
new_val: <#ty as ::modular_bitfield::Specifier>::InOut
) -> ::core::result::Result<(), ::modular_bitfield::error::OutOfBounds> {
const __BF_BASE_BITS: ::core::primitive::usize =
::core::mem::size_of::<<#ty as ::modular_bitfield::Specifier>::Bytes>() * 8;
const __BF_MAX_VALUE: <#ty as ::modular_bitfield::Specifier>::Bytes =
!0 >> (__BF_BASE_BITS - <#ty as ::modular_bitfield::Specifier>::BITS);
let __bf_raw_val =
<#ty as ::modular_bitfield::Specifier>::into_bytes(new_val)?;
#[allow(clippy::absurd_extreme_comparisons)]
if __bf_raw_val <= __BF_MAX_VALUE {
::modular_bitfield::private::write_specifier::<#ty>(&mut self.bytes[..], #offset, __bf_raw_val);
::core::result::Result::Ok(())
} else {
::core::result::Result::Err(::modular_bitfield::error::OutOfBounds)
}
}
);
Some(setters)
}
fn expand_getters_and_setters_for_field(
&self,
offset: &mut Punctuated<syn::Expr, syn::Token![+]>,
info: &FieldInfo<'_>,
) -> TokenStream2 {
let field = info.field;
let span = field.span();
let ty = &field.ty;
let offset_ts = if offset.is_empty() {
quote_spanned!(span=> 0)
} else {
offset.to_token_stream()
};
let getters = self.expand_getters_for_field(&offset_ts, info);
let setters = self.expand_setters_for_field(&offset_ts, info);
let getters_and_setters = quote_spanned!(span=>
#getters
#setters
);
offset.push(syn::parse_quote_spanned!(span=> <#ty as ::modular_bitfield::Specifier>::BITS));
getters_and_setters
}
fn expand_getters_and_setters(&self, config: &Config) -> TokenStream2 {
let span = self.item_struct.span();
let ident = &self.item_struct.ident;
let (impl_generics, ty_generics, where_clause) = self.item_struct.generics.split_for_impl();
let mut offset = Punctuated::<syn::Expr, Token![+]>::new();
let bits_checks = self
.field_infos(config)
.map(|field_info| Self::expand_bits_checks_for_field(field_info));
let setters_and_getters = self
.field_infos(config)
.map(|field_info| self.expand_getters_and_setters_for_field(&mut offset, &field_info));
quote_spanned!(span=>
const _: () = {
#( #bits_checks )*
};
impl #impl_generics #ident #ty_generics #where_clause {
#( #setters_and_getters )*
}
)
}
}