use quote::ToTokens;
use super::*;
pub fn gen_field_accessors(
cache: &mut Cache,
bitstruct: &Bitstruct,
options: &BistructGenOptions,
) -> Result<TokenStream2> {
let bitstruct_name = &bitstruct.ident;
let mut offset = 0;
let mut field_accessors = Vec::new();
for field in bitstruct.fields.iter() {
let field_bit_width = field.bit_width()?;
field_accessors.push(gen_bitstruct_field_accessor(
cache, options, bitstruct, field, offset,
)?);
offset += field_bit_width;
}
if options.with_cow() {
Ok(quote! {
impl<'a> #bitstruct_name<'a> {
#(#field_accessors)*
}
})
} else {
Ok(quote! {
impl #bitstruct_name {
#(#field_accessors)*
}
})
}
}
fn gen_bitstruct_field_accessor(
cache: &mut Cache,
options: &BistructGenOptions,
bitstruct: &Bitstruct,
field: &BitstructField,
offset: u32,
) -> Result<TokenStream2> {
let bitstruct_name = &bitstruct.ident;
let field_ident = &field.ident;
let field_ident_with_set_prefix =
syn::Ident::new(&format!("set_{}", field_ident), field_ident.span());
let priv_getter_ident = syn::Ident::new(&format!("{}_inner", field_ident), field_ident.span());
let priv_setter_ident = syn::Ident::new(
&format!("{}_inner", field_ident_with_set_prefix),
field_ident.span(),
);
let field_store_type = field.store_type_token_stream()?;
let (priv_getter, priv_setter) = if options.for_small() {
let bitstruct_store_type = match &cache.bitstruct_store_type {
Some(bitstruct_store_type) => bitstruct_store_type,
None => &bitstruct.store_type()?.into_token_stream(),
};
let priv_getter =
gen_priv_getter_for_small(bitstruct_name, &priv_getter_ident, field, offset)?;
let priv_setter = gen_priv_setter_for_small(
bitstruct_name,
bitstruct_store_type,
&priv_setter_ident,
field,
offset,
)?;
(priv_getter, priv_setter)
} else {
let priv_getter = if options.bit_numbering == BitNumbering::LSB0 {
gen_priv_getter_lsb0(bitstruct_name, &priv_getter_ident, field, offset)?
} else {
gen_priv_getter_msb0(bitstruct_name, &priv_getter_ident, field, offset)?
};
let priv_setter = if options.bit_numbering == BitNumbering::LSB0 {
gen_priv_setter_lsb0(
bitstruct_name,
&priv_setter_ident,
field,
offset,
options.with_cow(),
)?
} else {
gen_priv_setter_msb0(
bitstruct_name,
&priv_setter_ident,
field,
offset,
options.with_cow(),
)?
};
(priv_getter, priv_setter)
};
let pub_getter = gen_pub_getter(
cache,
field,
&priv_getter_ident,
&priv_getter,
&field_store_type,
)?;
let pub_setter = gen_pub_setter(
cache,
field,
&priv_setter_ident,
&priv_setter,
&field_store_type,
)?;
Ok(quote! {
#pub_getter
#pub_setter
})
}
fn gen_priv_getter_lsb0(
bitstruct_name: &syn::Ident,
priv_getter_ident: &syn::Ident,
field: &BitstructField,
bit_offset: u32,
) -> Result<TokenStream2> {
let field_store_type = field.store_type_token_stream()?;
let first_byte_index = field.first_byte_index(bit_offset);
let first_byte_index = syn::Index::from(first_byte_index as usize);
let first_byte_bit_offset = field.first_byte_bit_offset(bit_offset);
let last_byte_index = field.last_byte_index(bit_offset)?;
let last_byte_index = syn::Index::from(last_byte_index as usize);
let mask_litint = field.bit_mask_litint()?;
let remain_bytes_getter = if last_byte_index.index > first_byte_index.index {
quote! {
for i in (#first_byte_index+1)..#last_byte_index {
value |= (bits.inner[i] as #field_store_type) >> ((i as u32 - #first_byte_index) * 8 - #first_byte_bit_offset);
}
value |= (bits.inner[#last_byte_index] as #field_store_type) << ((#last_byte_index - #first_byte_index) * 8 - #first_byte_bit_offset);
}
} else {
quote! {}
};
let priv_getter = quote! {
#[inline]
fn #priv_getter_ident(bits: &#bitstruct_name) -> #field_store_type {
let mut value = 0;
value |= (bits.inner[#first_byte_index] >> #first_byte_bit_offset) as #field_store_type;
#remain_bytes_getter
value & #mask_litint
}
};
Ok(priv_getter)
}
fn gen_priv_setter_lsb0(
bitstruct_name: &syn::Ident,
priv_setter_ident: &syn::Ident,
field: &BitstructField,
bit_offset: u32,
with_cow: bool,
) -> Result<TokenStream2> {
let field_store_type = field.store_type_token_stream()?;
let first_byte_index = field.first_byte_index(bit_offset);
let first_byte_index = syn::Index::from(first_byte_index as usize);
let first_byte_bit_offset = field.first_byte_bit_offset(bit_offset);
let last_byte_index = field.last_byte_index(bit_offset)?;
let last_byte_index = syn::Index::from(last_byte_index as usize);
let mask_litint = field.bit_mask_litint()?;
let mut_ref_ident = quote! {bytes};
let mut_ref_state = if with_cow {
quote! {let #mut_ref_ident = bits.inner.to_mut();}
} else {
quote! {let #mut_ref_ident = &mut bits.inner;}
};
let remain_bytes_setter = if last_byte_index.index > first_byte_index.index {
quote! {
for i in (#first_byte_index+1)..#last_byte_index {
#mut_ref_ident[i] = (value >> ((i as u32 - #first_byte_index) * 8 - #first_byte_bit_offset)) as u8;
}
const SHIFT: u32 = (#last_byte_index - #first_byte_index) * 8 - #first_byte_bit_offset;
#mut_ref_ident[#last_byte_index] &= !(#mask_litint >> SHIFT) as u8;
#mut_ref_ident[#last_byte_index] |= (value >> SHIFT) as u8;
}
} else {
quote! {}
};
let priv_setter = quote! {
#[inline]
fn #priv_setter_ident(bits: &mut #bitstruct_name, value: #field_store_type) {
let value = value & #mask_litint;
#mut_ref_state
#mut_ref_ident[#first_byte_index] &= !((#mask_litint) << #first_byte_bit_offset) as u8;
#mut_ref_ident[#first_byte_index] |= (value << #first_byte_bit_offset) as u8;
#remain_bytes_setter
}
};
Ok(priv_setter)
}
fn gen_priv_getter_msb0(
bitstruct_name: &syn::Ident,
priv_getter_ident: &syn::Ident,
field: &BitstructField,
bit_offset: u32,
) -> Result<TokenStream2> {
let field_store_type = field.store_type_token_stream()?;
let field_bit_width = field.bit_width()?;
let first_byte_index = field.first_byte_index(bit_offset);
let first_byte_index = syn::Index::from(first_byte_index as usize);
let first_byte_bit_offset = field.first_byte_bit_offset(bit_offset);
let last_byte_index = field.last_byte_index(bit_offset)?;
let last_byte_index = syn::Index::from(last_byte_index as usize);
let mask_litint = field.bit_mask_litint()?;
let first_byte_getter = if field_bit_width > 8 {
quote! {
value |= ((bits.inner[#first_byte_index] << #first_byte_bit_offset) as #field_store_type) << (#field_bit_width - 8);
}
} else {
quote! {
value |= ((bits.inner[#first_byte_index] << #first_byte_bit_offset) as #field_store_type) >> (8 - #field_bit_width);
}
};
let remain_bytes_getter = if last_byte_index.index > first_byte_index.index {
quote! {
for i in (#first_byte_index+1)..#last_byte_index {
value |= (bits.inner[i] as #field_store_type) << (#field_bit_width - (i as u32 - #first_byte_index + 1) * 8 + #first_byte_bit_offset);
}
value |= (bits.inner[#last_byte_index] as #field_store_type) >> ((#last_byte_index - #first_byte_index + 1) * 8 - #first_byte_bit_offset - #field_bit_width);
}
} else {
quote! {}
};
let priv_getter = quote! {
#[inline]
fn #priv_getter_ident(bits: &#bitstruct_name) -> #field_store_type {
let mut value = 0;
#first_byte_getter
#remain_bytes_getter
value & #mask_litint
}
};
Ok(priv_getter)
}
fn gen_priv_setter_msb0(
bitstruct_name: &syn::Ident,
priv_setter_ident: &syn::Ident,
field: &BitstructField,
bit_offset: u32,
with_cow: bool,
) -> Result<TokenStream2> {
let field_store_type = field.store_type_token_stream()?;
let field_bit_width = field.bit_width()?;
let first_byte_index = field.first_byte_index(bit_offset);
let first_byte_index = syn::Index::from(first_byte_index as usize);
let first_byte_bit_offset = field.first_byte_bit_offset(bit_offset);
let last_byte_index = field.last_byte_index(bit_offset)?;
let last_byte_index = syn::Index::from(last_byte_index as usize);
let mask_litint = field.bit_mask_litint()?;
let mut_ref_ident = quote! {bytes};
let mut_ref_state = if with_cow {
quote! {let #mut_ref_ident = bits.inner.to_mut();}
} else {
quote! {let #mut_ref_ident = &mut bits.inner;}
};
let first_byte_setter = if field_bit_width + first_byte_bit_offset > 8 {
quote! {
#mut_ref_ident[#first_byte_index] &= !((#mask_litint >> (#field_bit_width + #first_byte_bit_offset - 8)) as u8);
#mut_ref_ident[#first_byte_index] |= (value >> (#field_bit_width + #first_byte_bit_offset - 8)) as u8;
}
} else {
quote! {
#mut_ref_ident[#first_byte_index] &= !((#mask_litint << (8 - #field_bit_width - #first_byte_bit_offset)) as u8);
#mut_ref_ident[#first_byte_index] |= ((value << (8 - #field_bit_width - #first_byte_bit_offset)) as u8);
}
};
let remain_bytes_setter = if last_byte_index.index > first_byte_index.index {
quote! {
for i in (#first_byte_index+1)..#last_byte_index {
#mut_ref_ident[i] = (value >> (#field_bit_width - (i as u32 - #first_byte_index + 1) * 8 + #first_byte_bit_offset)) as u8;
}
const SHIFT: u32 = (#last_byte_index - #first_byte_index + 1) * 8 - #first_byte_bit_offset - #field_bit_width;
#mut_ref_ident[#last_byte_index] &= !((#mask_litint << SHIFT) as u8);
#mut_ref_ident[#last_byte_index] |= (value << SHIFT) as u8;
}
} else {
quote! {}
};
let priv_setter = quote! {
#[inline]
fn #priv_setter_ident(bits: &mut #bitstruct_name, value: #field_store_type) {
let value = value & #mask_litint;
#mut_ref_state
#first_byte_setter
#remain_bytes_setter
}
};
Ok(priv_setter)
}
fn gen_priv_getter_for_small(
bitstruct_name: &syn::Ident,
priv_getter_ident: &syn::Ident,
field: &BitstructField,
bit_offset: u32,
) -> Result<TokenStream2> {
let field_store_type = field.store_type_token_stream()?;
let mask_litint = field.bit_mask_litint()?;
let priv_getter = quote! {
#[inline]
const fn #priv_getter_ident(bits: &#bitstruct_name) -> #field_store_type {
((bits.inner >> #bit_offset) as #field_store_type) & #mask_litint
}
};
Ok(priv_getter)
}
fn gen_priv_setter_for_small(
bitstruct_name: &syn::Ident,
bitstruct_store_type: &TokenStream2,
priv_setter_ident: &syn::Ident,
field: &BitstructField,
bit_offset: u32,
) -> Result<TokenStream2> {
let field_store_type = field.store_type_token_stream()?;
let mask_litint = field.bit_mask_litint()?;
let priv_setter = quote! {
#[inline]
const fn #priv_setter_ident(bits: &mut #bitstruct_name, value: #field_store_type) {
bits.inner &= !((#mask_litint as #bitstruct_store_type) << #bit_offset);
bits.inner |= ((value & #mask_litint) as #bitstruct_store_type) << #bit_offset;
}
};
Ok(priv_setter)
}
fn gen_const_check_for_enum_traits(
enum_ident: &syn::Ident,
field_bit_width: u32,
) -> Result<TokenStream2> {
Ok(quote! {
const _:() = {
if <#enum_ident as bitstructs::BitstructToValue>::BIT_WIDTH != #field_bit_width
|| <#enum_ident as bitstructs::BitstructFromValue>::BIT_WIDTH != #field_bit_width {
let _:u32 = 0 - u32::MAX;
}
};
})
}
fn gen_pub_getter(
cache: &mut Cache,
field: &BitstructField,
priv_getter_ident: &syn::Ident,
priv_getter: &TokenStream2,
field_store_type: &TokenStream2,
) -> Result<TokenStream2> {
let doc_attrs = &field.doc_attrs();
let getter_name = &field.ident;
let field_bit_width = field.bit_width()?;
match &field.enum_type {
None => {
if field_bit_width == 1 {
Ok(quote! {
#(#doc_attrs)*
pub fn #getter_name(&self) -> bool {
#priv_getter
((#priv_getter_ident(self) as #field_store_type) & 1) != 0
}
})
} else {
Ok(quote! {
#(#doc_attrs)*
pub fn #getter_name(&self) -> #field_store_type {
#priv_getter
#priv_getter_ident(self)
}
})
}
}
Some(enum_type) => {
let enum_ident = &enum_type.ident;
let const_check_traits = gen_const_check_for_enum_traits(enum_ident, field_bit_width)?;
match &enum_type.variants {
None => Ok(quote! {
#(#doc_attrs)*
pub fn #getter_name(&self) -> #enum_ident {
#const_check_traits
#priv_getter
let value = #priv_getter_ident(self);
unsafe {<#enum_ident as bitstructs::BitstructFromValue>::from_value(value)}
}
}),
Some(_) => {
let variant_match_arms: &Vec<TokenStream2> = cache
.field_enum_type_match_arms
.get(enum_ident)
.ok_or(syn::Error::new(
enum_ident.span(),
format!("match arms of enum type {} should be in cache", enum_ident),
))?
.0
.as_ref();
Ok(quote! {
#(#doc_attrs)*
pub fn #getter_name(&self) -> #enum_ident {
#priv_getter
match #priv_getter_ident(self) {
#(#variant_match_arms)*
_ => unreachable!(),
}
}
})
}
}
}
}
}
fn gen_pub_setter(
cache: &mut Cache,
field: &BitstructField,
priv_setter_ident: &syn::Ident,
priv_setter: &TokenStream2,
field_store_type: &TokenStream2,
) -> Result<TokenStream2> {
let setter_name = format_ident!("set_{}", field.ident);
let field_bit_width = field.bit_width()?;
let unsafe_setter_doc = format!(
"Set the value of the field\n# Safety\nThe value must be less than 2^{}",
field_bit_width
);
let doc_attrs = field.doc_attrs();
match &field.enum_type {
None => {
if field_bit_width == 1 {
Ok(quote! {
#(#doc_attrs)*
pub fn #setter_name(&mut self, value: bool) {
let value = if value { 1 } else { 0 };
#priv_setter
#priv_setter_ident(self, value)
}
})
} else {
Ok(quote! {
#(#doc_attrs)*
#[doc = #unsafe_setter_doc]
pub unsafe fn #setter_name(&mut self, value: #field_store_type) {
#priv_setter
#priv_setter_ident(self, value)
}
})
}
}
Some(enum_type) => {
let enum_ident = &enum_type.ident;
let const_check_traits = gen_const_check_for_enum_traits(enum_ident, field_bit_width)?;
match &enum_type.variants {
None => Ok(quote! {
#(#doc_attrs)*
pub fn #setter_name(&mut self, value: #enum_ident) {
#const_check_traits
let value = unsafe {<#enum_ident as bitstructs::BitstructToValue>::to_value(value) };
#priv_setter
#priv_setter_ident(self, value)
}
}),
Some(_) => {
let variant_match_arms: &Vec<TokenStream2> = cache
.field_enum_type_match_arms
.get(enum_ident)
.ok_or(syn::Error::new(
enum_ident.span(),
format!("match arms of enum type {} should be in cache", enum_ident),
))?
.1
.as_ref();
Ok(quote! {
#(#doc_attrs)*
pub fn #setter_name(&mut self, value: #enum_ident) {
let value = match value {
#(#variant_match_arms)*
};
#priv_setter
#priv_setter_ident(self, value)
}
})
}
}
}
}
}