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:
parent
0701318120
commit
3852f09dd5
21 changed files with 1548 additions and 840 deletions
70
bitbuffer_derive/src/read/enum.rs
Normal file
70
bitbuffer_derive/src/read/enum.rs
Normal 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()})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
82
bitbuffer_derive/src/read/field.rs
Normal file
82
bitbuffer_derive/src/read/field.rs
Normal 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 ,)*
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
118
bitbuffer_derive/src/read/mod.rs
Normal file
118
bitbuffer_derive/src/read/mod.rs
Normal 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 ¶ms.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(¶ms, false)?;
|
||||
let parse_unchecked = parse_impl(¶ms, 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(¶ms, false)?;
|
||||
let parse_unchecked = parse_impl(¶ms, 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
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
14
bitbuffer_derive/src/read/struct.rs
Normal file
14
bitbuffer_derive/src/read/struct.rs
Normal 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, ¶ms.fields, params.span(), unchecked)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue