use super::*;
fn ok_bits() -> TokenStream {
quote!(::core::result::Result::<(), ::bilge::BitsError>::Ok(()))
}
pub(crate) fn generate_getter_value(ty: &Type, offset: &TokenStream, is_array_elem_getter: bool) -> TokenStream {
let elem_offset = if is_array_elem_getter {
let size = shared::generate_type_bitsize(ty);
quote! {
let size = #size;
cursor >>= size * index;
}
} else {
quote!()
};
let inner = generate_getter_inner(ty, true);
let import = shared::import_traits();
quote! {
#import
type ArbIntOf<T> = <T as ::bilge::Bitsized>::ArbitraryInt;
type BaseIntOf<T> = <ArbIntOf<T> as ::bilge::arbitrary_int::traits::Integer>::UnderlyingType;
let mut cursor = self.value.value();
let field_offset = #offset;
cursor >>= field_offset;
#elem_offset
#inner
}
}
pub(crate) fn generate_getter_inner(ty: &Type, is_getter: bool) -> TokenStream {
use Type::*;
match ty {
Tuple(tuple) => {
if is_getter {
let unbraced = tuple
.elems
.iter()
.map(|elem| {
let getter = generate_getter_inner(elem, true);
quote! { {#getter} }
})
.reduce(|acc, next| quote!(#acc, #next))
.unwrap_or_else(|| quote!());
quote! { (#unbraced) }
} else {
generate_tuple_try_from(&tuple.elems)
}
}
Array(array) => {
let (len_expr, elem_ty) = length_and_type_of_nested_array(array);
if is_getter {
let array_elem = generate_getter_inner(&elem_ty, true);
quote! {
let array = {
let mut array: [::core::mem::MaybeUninit<#elem_ty>; #len_expr] = unsafe {
::core::mem::MaybeUninit::uninit().assume_init()
};
let mut i = 0;
while i < #len_expr {
let elem_value = {
#array_elem
};
array[i].write(elem_value);
i += 1;
}
unsafe { ::core::mem::transmute(array) }
};
array
}
} else if shared::is_always_filled(&elem_ty) {
ok_bits()
} else {
let array_elem = generate_getter_inner(&elem_ty, false);
let size = shared::generate_type_bitsize(&elem_ty);
let ok = ok_bits();
quote! { {
let mut i = 0usize;
loop {
if i >= #len_expr {
break #ok;
}
match { #array_elem } {
Ok(()) => {}
Err(e) => {
let size = #size;
break Err(e.at_offset(i.saturating_mul(size)));
}
}
i += 1;
}
} }
}
}
Path(_) => {
let size = shared::generate_type_bitsize(ty);
let elem_value = quote! {
let raw_value = cursor;
let size = #size;
cursor = cursor.wrapping_shr(size as u32);
let raw_value: BaseIntOf<#ty> = raw_value as BaseIntOf<#ty>;
let elem_value = <#ty as ::bilge::Bitsized>::ArbitraryInt::masked_new(raw_value);
};
if is_getter {
quote! {
#elem_value
match <#ty>::try_from(elem_value) {
Ok(v) => v,
Err(_) => ::core::panic!("unreachable"),
}
}
} else {
if shared::is_always_filled(ty) {
let ok = ok_bits();
quote! {
let size = #size;
cursor = cursor.wrapping_shr(size as u32);
#ok
}
} else {
let ok = ok_bits();
quote! { {
#elem_value
match <#ty>::try_from(elem_value) {
Ok(_) => #ok,
Err(e) => Err(::bilge::IntoBitsError::into_bits_error(
e,
stringify!(#ty),
elem_value.value() as u128,
<#ty as ::bilge::Bitsized>::BITS as u8,
)),
}
} }
}
}
}
_ => unreachable(()),
}
}
fn generate_tuple_try_from(elems: &syn::punctuated::Punctuated<Type, syn::Token![,]>) -> TokenStream {
let ok = ok_bits();
if elems.is_empty() || elems.iter().all(shared::is_always_filled) {
return ok;
}
let checks = elems.iter().enumerate().map(|(i, elem)| {
let check = generate_getter_inner(elem, false);
let size = shared::generate_type_bitsize(elem);
let index = proc_macro2::Literal::usize_unsuffixed(i);
quote! {
match { #check } {
Ok(()) => {
__bilge_rel += #size;
}
Err(e) => break Err(e.in_field(stringify!(#index), __bilge_rel)),
}
}
});
quote! { {
let mut __bilge_rel = 0usize;
loop {
#(#checks)*
break #ok;
}
} }
}
pub(crate) fn generate_setter_value(ty: &Type, offset: &TokenStream, is_array_elem_setter: bool) -> TokenStream {
let elem_offset = if is_array_elem_setter {
let size = shared::generate_type_bitsize(ty);
quote! {
let size = #size;
offset += size * index;
}
} else {
quote!()
};
let value_shifted = generate_setter_inner(ty);
let mask = generate_ty_mask(ty);
let import = shared::import_traits();
quote! {
#import
type ArbIntOf<T> = <T as ::bilge::Bitsized>::ArbitraryInt;
type BaseIntOf<T> = <ArbIntOf<T> as ::bilge::arbitrary_int::traits::Integer>::UnderlyingType;
let mut offset = #offset;
#elem_offset
let field_mask = #mask;
let field_mask: BaseIntOf<Self> = field_mask << offset;
let others_mask: BaseIntOf<Self> = !field_mask;
let struct_value: BaseIntOf<Self> = self.value.value();
let others_values: BaseIntOf<Self> = struct_value & others_mask;
#value_shifted
let new_struct_value = others_values | value_shifted;
self.value = <ArbIntOf<Self>>::new(new_struct_value);
}
}
fn generate_setter_inner(ty: &Type) -> TokenStream {
use Type::*;
match ty {
Tuple(tuple) => {
let mut tuple_index = syn::Index::from(0);
let value_shifted = tuple
.elems
.iter()
.map(|elem| {
let elem_name = quote!(value.#tuple_index);
tuple_index.index += 1;
let value_shifted = generate_setter_inner(elem);
quote! { {
let value = #elem_name;
#value_shifted
value_shifted
} }
})
.reduce(|acc, next| quote!(#acc | #next))
.unwrap_or_else(|| quote!(0));
quote! {
let value_shifted = #value_shifted;
}
}
Array(array) => {
let (len_expr, elem_ty) = length_and_type_of_nested_array(array);
let value_shifted = generate_setter_inner(&elem_ty);
quote! {
#[allow(clippy::useless_transmute)]
let value: [#elem_ty; #len_expr] = unsafe { ::core::mem::transmute(value) };
let mut acc = 0;
let mut i = 0;
while i < #len_expr {
let value = value[i];
#value_shifted
acc |= value_shifted;
i += 1;
}
let value_shifted = acc;
}
}
Path(_) => {
let size = shared::generate_type_bitsize(ty);
quote! {
let value = (<ArbIntOf<#ty>>::from(value).value() & (<<ArbIntOf<#ty> as ::bilge::arbitrary_int::traits::Integer>::UnsignedInteger as ::bilge::arbitrary_int::traits::Integer>::MAX.value() as <ArbIntOf<#ty> as ::bilge::arbitrary_int::traits::Integer>::UnderlyingType)) as <<ArbIntOf<#ty> as ::bilge::arbitrary_int::traits::Integer>::UnsignedInteger as ::bilge::arbitrary_int::traits::Integer>::UnderlyingType; let value: BaseIntOf<Self> = value as BaseIntOf<Self>;
let value_shifted = value << offset;
offset += #size;
}
}
_ => unreachable(()),
}
}
pub(crate) fn generate_constructor_part(ty: &Type, name: &Ident, shifted_name: &Ident, offset: &TokenStream) -> TokenStream {
let value_shifted = generate_setter_inner(ty);
quote! {
let #shifted_name = {
let mut offset = #offset;
let value = #name;
#value_shifted
value_shifted
};
}
}
fn generate_ty_mask(ty: &Type) -> TokenStream {
use Type::*;
match ty {
Tuple(tuple) => {
let mut previous_elem_sizes = vec![];
tuple
.elems
.iter()
.map(|elem| {
let mask = generate_ty_mask(elem);
let elem_size = shared::generate_type_bitsize(elem);
let elem_offset = previous_elem_sizes.iter().cloned().reduce(|acc, next| quote!((#acc + #next)));
previous_elem_sizes.push(elem_size);
if let Some(elem_offset) = elem_offset {
quote!(#mask << #elem_offset)
} else {
quote!(#mask)
}
})
.reduce(|acc, next| quote!(#acc | #next))
.unwrap_or_else(|| quote!(0))
}
Array(array) => {
let elem_ty = &array.elem;
let len_expr = &array.len;
let mask = generate_ty_mask(elem_ty);
let ty_size = shared::generate_type_bitsize(elem_ty);
quote! { {
let mask = #mask;
let mut field_mask = 0;
let mut i = 0;
while i < #len_expr {
field_mask |= mask << (i * #ty_size);
i += 1;
}
field_mask
} }
}
Path(_) => quote! {
(<<ArbIntOf<#ty> as ::bilge::arbitrary_int::traits::Integer>::UnsignedInteger as ::bilge::arbitrary_int::traits::Integer>::MAX.value() as BaseIntOf<Self>) },
_ => unreachable(()),
}
}
fn length_and_type_of_nested_array(array: &syn::TypeArray) -> (TokenStream, Type) {
let elem_ty = &array.elem;
let len_expr = &array.len;
if let Type::Array(array) = &**elem_ty {
let (child_len, child_ty) = length_and_type_of_nested_array(array);
(quote!((#len_expr) * (#child_len)), child_ty)
} else {
(quote!(#len_expr), *elem_ty.clone())
}
}