1
0
Fork 0
mirror of https://codeberg.org/icewind/bitbuffer.git synced 2026-06-04 00:54:07 +02:00

rewrite derive macro

This commit is contained in:
Robin Appelman 2023-09-12 22:30:45 +02:00
commit 3852f09dd5
21 changed files with 1548 additions and 840 deletions

View file

@ -0,0 +1,70 @@
use crate::params::{EnumParam, VariantBody};
use crate::read::field::read_struct_or_enum;
use proc_macro2::{Ident, TokenStream};
use quote::quote_spanned;
use syn::Path;
pub fn derive_encode_enum(params: &EnumParam, unchecked: bool) -> TokenStream {
let discriminant_bits = params.discriminant_bits;
let repr = params.discriminant_repr();
let ident = params.ident.clone();
let span = params.span;
let match_arms = params
.variants
.iter()
.zip(params.read_discriminant_tokens())
.map(|(variant, discriminant_token)| {
let span = variant.span();
let variant_name = &variant.variant_name;
let mut variant_path = Path::from(params.ident.clone());
variant_path
.segments
.push(variant.variant_name.clone().into());
let read_variant = match &variant.body {
VariantBody::Unit => quote_spanned! { span =>
Ok(#ident::#variant_name)
},
VariantBody::Fields(fields) => {
read_struct_or_enum(&variant_path, fields, span.clone(), unchecked)
}
};
quote_spanned! {span=>
#discriminant_token => #read_variant,
}
});
let read_fn = Ident::new(
if unchecked {
"read_int_unchecked"
} else {
"read_int"
},
span,
);
let end_param = if unchecked {
Some(quote_spanned!(span => end))
} else {
None
};
let error_handle = if unchecked {
None
} else {
Some(quote_spanned!(span => ?))
};
let name = ident.to_string();
quote_spanned! {span =>
#[allow(clippy::unnecessary_cast)]
let discriminant:#repr = __stream.#read_fn(#discriminant_bits as usize, #end_param)#error_handle;
match discriminant {
#(#match_arms)*
_ => {
#[allow(clippy::unnecessary_cast)]
return Err(::bitbuffer::BitError::UnmatchedDiscriminant{discriminant: discriminant as usize, enum_name: #name.to_string()})
}
}
}
}

View file

@ -0,0 +1,82 @@
use crate::params::FieldParam;
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote_spanned;
use syn::Path;
pub fn read_struct_or_enum(
struct_name: &Path,
fields: &[FieldParam],
span: Span,
unchecked: bool,
) -> TokenStream {
let named = fields.iter().any(|f| f.field_name.is_some());
let values = fields.iter().map(|f| {
let align = &f.align;
let field_type = &f.ty;
let span = f.span();
let read_fn = Ident::new(if unchecked { "read_unchecked" } else { "read" }, span);
let read_sized_fn = Ident::new(
if unchecked {
"read_sized_unchecked"
} else {
"read_sized"
},
span,
);
let end_param = if unchecked {
Some(quote_spanned!(span => end))
} else {
None
};
match &f.size {
Some(size) => {
quote_spanned! { span =>
{
#align
let _size: usize = #size;
__stream.#read_sized_fn::<#field_type>(_size, #end_param)?
}
}
}
None => {
quote_spanned! { span =>
{
#align
__stream.#read_fn::<#field_type>(#end_param)?
}
}
}
}
});
if named {
let definitions = fields.iter().zip(values).map(|(f, value)| {
let name = &f.field_name;
quote_spanned! { span =>
let #name = #value;
}
});
let struct_definition = fields.iter().map(|f| {
let name = f
.field_name
.as_ref()
.expect("unnamed field in named struct?");
quote_spanned! { span =>
#name,
}
});
quote_spanned! { span =>
#(#definitions)*
Ok(#struct_name {
#(#struct_definition)*
})
}
} else {
quote_spanned! { span =>
Ok(#struct_name(
#(#values ,)*
))
}
}
}

View file

@ -0,0 +1,118 @@
mod r#enum;
mod field;
mod r#struct;
use self::r#enum::derive_encode_enum;
use self::r#struct::derive_encode_struct;
use crate::params::{InputInnerParams, InputParams};
use crate::size_hint::SizeHint;
use crate::Derivable;
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned};
use syn::Result;
fn parse_impl(params: &InputParams, unchecked: bool) -> Result<TokenStream> {
Ok(match &params.inner {
InputInnerParams::Struct(inner) => derive_encode_struct(inner, unchecked),
InputInnerParams::Enum(inner) => derive_encode_enum(inner, unchecked),
})
}
pub struct Read;
impl Derivable for Read {
type Params = InputParams;
fn derive(params: Self::Params) -> Result<TokenStream> {
let (impl_generics, ty_generics, where_clause) = params.generics_for_impl();
let parse = parse_impl(&params, false)?;
let parse_unchecked = parse_impl(&params, true)?;
let size = params.size_hint();
let lifetime = params.lifetime.clone();
let endianness = params.endianness();
let name = params.ident.clone();
let align = params.align;
let span = params.span;
Ok(quote_spanned! {span =>
impl #impl_generics ::bitbuffer::BitRead<#lifetime, #endianness> for #name #ty_generics #where_clause {
#[allow(unused_braces, unused_variables)]
fn read(__stream: &mut ::bitbuffer::BitReadStream<#lifetime, #endianness>) -> ::bitbuffer::Result<Self> {
// if the read has a predicable size, we can do the bounds check in one go
match <Self as ::bitbuffer::BitRead<#endianness>>::bit_size() {
Some(size) => {
let end = __stream.check_read(size)?;
unsafe {
<Self as ::bitbuffer::BitRead<#endianness>>::read_unchecked(__stream, end)
}
},
None => {
#align
#parse
}
}
}
#[allow(unused_braces, unused_variables)]
unsafe fn read_unchecked(__stream: &mut ::bitbuffer::BitReadStream<#lifetime, #endianness>, end: bool) -> ::bitbuffer::Result<Self> {
#align
#parse_unchecked
}
fn bit_size() -> Option<usize> {
#size
}
}
})
}
}
pub struct ReadSized;
impl Derivable for ReadSized {
type Params = InputParams;
fn derive(params: Self::Params) -> Result<TokenStream> {
let (impl_generics, ty_generics, where_clause) = params.generics_for_impl();
let parse = parse_impl(&params, false)?;
let parse_unchecked = parse_impl(&params, true)?;
let size = params.size_hint();
let lifetime = params.lifetime.clone();
let endianness = params.endianness();
let name = params.ident.clone();
let align = params.align;
Ok(quote! {
impl #impl_generics ::bitbuffer::BitReadSized<#lifetime, #endianness> for #name #ty_generics #where_clause {
#[allow(unused_braces)]
fn read(__stream: &mut ::bitbuffer::BitReadStream<#lifetime, #endianness>, input_size: usize) -> ::bitbuffer::Result<Self> {
// if the read has a predicable size, we can do the bounds check in one go
match <Self as ::bitbuffer::BitReadSized<#endianness>>::bit_size_sized(input_size) {
Some(size) => {
let end = __stream.check_read(size)?;
unsafe {
<Self as ::bitbuffer::BitReadSized<#endianness>>::read_unchecked(__stream, input_size, end)
}
},
None => {
#align
#parse
}
}
}
#[allow(unused_braces)]
unsafe fn read_unchecked(__stream: &mut ::bitbuffer::BitReadStream<#lifetime, #endianness>, input_size: usize, end: bool) -> ::bitbuffer::Result<Self> {
#align
#parse_unchecked
}
fn bit_size_sized(input_size: usize) -> Option<usize> {
#size
}
}
})
}
}

View file

@ -0,0 +1,14 @@
use crate::params::StructParam;
use crate::read::field::read_struct_or_enum;
use proc_macro2::TokenStream;
use quote::quote;
use syn::Path;
pub fn derive_encode_struct(params: &StructParam, unchecked: bool) -> TokenStream {
let path = Path::from(params.ident.clone());
if params.is_unit {
quote!(Ok(#path))
} else {
read_struct_or_enum(&path, &params.fields, params.span(), unchecked)
}
}