mirror of
https://codeberg.org/icewind/bitbuffer.git
synced 2026-06-03 16:44:06 +02:00
Implement #[align] in BinWrite
This commit is contained in:
parent
b74c46afca
commit
f6e31f99d9
2 changed files with 110 additions and 6 deletions
|
|
@ -7,7 +7,7 @@ use syn::{
|
||||||
parse_macro_input, parse_quote, parse_str, Attribute, Data, DataStruct, DeriveInput, Expr,
|
parse_macro_input, parse_quote, parse_str, Attribute, Data, DataStruct, DeriveInput, Expr,
|
||||||
Fields, GenericParam, Ident, Index, Lit, LitInt, Member, Path, Type,
|
Fields, GenericParam, Ident, Index, Lit, LitInt, Member, Path, Type,
|
||||||
};
|
};
|
||||||
use syn_util::get_attribute_value;
|
use syn_util::{contains_attribute, get_attribute_value};
|
||||||
|
|
||||||
pub fn derive_bitwrite_trait(
|
pub fn derive_bitwrite_trait(
|
||||||
input: proc_macro::TokenStream,
|
input: proc_macro::TokenStream,
|
||||||
|
|
@ -87,6 +87,8 @@ pub fn derive_bitwrite_trait(
|
||||||
fn write(data: Data, struct_name: &Ident, attrs: &[Attribute]) -> TokenStream {
|
fn write(data: Data, struct_name: &Ident, attrs: &[Attribute]) -> TokenStream {
|
||||||
let span = struct_name.span();
|
let span = struct_name.span();
|
||||||
|
|
||||||
|
let align = get_align(attrs);
|
||||||
|
|
||||||
match data {
|
match data {
|
||||||
Data::Struct(DataStruct { fields, .. }) => {
|
Data::Struct(DataStruct { fields, .. }) => {
|
||||||
let expand = fields.iter().enumerate().map(|(i, field)| {
|
let expand = fields.iter().enumerate().map(|(i, field)| {
|
||||||
|
|
@ -114,6 +116,7 @@ fn write(data: Data, struct_name: &Ident, attrs: &[Attribute]) -> TokenStream {
|
||||||
let writes = fields.iter().enumerate().map(|(i, f)| {
|
let writes = fields.iter().enumerate().map(|(i, f)| {
|
||||||
// Get attributes `#[..]` on each field
|
// Get attributes `#[..]` on each field
|
||||||
let size = get_field_size(&f.attrs, f.span());
|
let size = get_field_size(&f.attrs, f.span());
|
||||||
|
let align = get_align(&f.attrs);
|
||||||
let span = f.span();
|
let span = f.span();
|
||||||
let member = f.ident.clone().map(Member::Named).unwrap_or_else(|| {
|
let member = f.ident.clone().map(Member::Named).unwrap_or_else(|| {
|
||||||
Member::Unnamed(Index {
|
Member::Unnamed(Index {
|
||||||
|
|
@ -125,6 +128,7 @@ fn write(data: Data, struct_name: &Ident, attrs: &[Attribute]) -> TokenStream {
|
||||||
Some(size) => {
|
Some(size) => {
|
||||||
quote_spanned! { span =>
|
quote_spanned! { span =>
|
||||||
{
|
{
|
||||||
|
#align;
|
||||||
let _size: usize = #size;
|
let _size: usize = #size;
|
||||||
__target__stream.write_sized(&self.#member, _size)?;
|
__target__stream.write_sized(&self.#member, _size)?;
|
||||||
}
|
}
|
||||||
|
|
@ -132,13 +136,17 @@ fn write(data: Data, struct_name: &Ident, attrs: &[Attribute]) -> TokenStream {
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
quote_spanned! { span => {
|
quote_spanned! { span => {
|
||||||
__target__stream.write(&self.#member)?;
|
{
|
||||||
|
#align;
|
||||||
|
__target__stream.write(&self.#member)?;
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
quote_spanned! {span=>
|
quote_spanned! {span=>
|
||||||
|
#align;
|
||||||
#(#expand)*
|
#(#expand)*
|
||||||
#(#writes)*
|
#(#writes)*
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -217,15 +225,24 @@ fn write(data: Data, struct_name: &Ident, attrs: &[Attribute]) -> TokenStream {
|
||||||
let variant_name = &variant.ident;
|
let variant_name = &variant.ident;
|
||||||
|
|
||||||
match &variant.fields {
|
match &variant.fields {
|
||||||
Fields::Unit => quote_spanned! {span =>
|
Fields::Unit => {
|
||||||
#struct_name::#variant_name => {},
|
if contains_attribute(&variant.attrs, &["align"]) {
|
||||||
},
|
return quote_spanned! { span =>
|
||||||
|
compile_error!("'align' attribute is not allowed on unit variants");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
quote_spanned! {span =>
|
||||||
|
#struct_name::#variant_name => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
Fields::Unnamed(f) => {
|
Fields::Unnamed(f) => {
|
||||||
let size = get_field_size(&variant.attrs, f.span());
|
let size = get_field_size(&variant.attrs, f.span());
|
||||||
|
let align = get_align(&variant.attrs);
|
||||||
match size {
|
match size {
|
||||||
Some(size) => {
|
Some(size) => {
|
||||||
quote_spanned! { span =>
|
quote_spanned! { span =>
|
||||||
#struct_name::#variant_name(inner) => {
|
#struct_name::#variant_name(inner) => {
|
||||||
|
#align;
|
||||||
let size:usize = #size;
|
let size:usize = #size;
|
||||||
__target__stream.write_sized(inner, size)?;
|
__target__stream.write_sized(inner, size)?;
|
||||||
}
|
}
|
||||||
|
|
@ -233,7 +250,10 @@ fn write(data: Data, struct_name: &Ident, attrs: &[Attribute]) -> TokenStream {
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
quote_spanned! { span =>
|
quote_spanned! { span =>
|
||||||
#struct_name::#variant_name(inner) => { __target__stream.write(inner)?; }
|
#struct_name::#variant_name(inner) => {
|
||||||
|
#align;
|
||||||
|
__target__stream.write(inner)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -246,6 +266,7 @@ fn write(data: Data, struct_name: &Ident, attrs: &[Attribute]) -> TokenStream {
|
||||||
let repr = repr_for_bits(discriminant_bits);
|
let repr = repr_for_bits(discriminant_bits);
|
||||||
|
|
||||||
quote_spanned! {span=>
|
quote_spanned! {span=>
|
||||||
|
#align;
|
||||||
let discriminant:#repr = match &self {
|
let discriminant:#repr = match &self {
|
||||||
#(#discriminant_value),*
|
#(#discriminant_value),*
|
||||||
};
|
};
|
||||||
|
|
@ -300,3 +321,13 @@ fn type_is_int(ty: &Type) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_align(attrs: &[Attribute]) -> TokenStream {
|
||||||
|
if contains_attribute(attrs, &["align"]) {
|
||||||
|
quote! {
|
||||||
|
__target__stream.align()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! { () }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -289,3 +289,76 @@ fn test_read_size_expression() {
|
||||||
stream.write(&val).unwrap();
|
stream.write(&val).unwrap();
|
||||||
assert_eq!(bytes, data);
|
assert_eq!(bytes, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(BitWrite, PartialEq, Debug)]
|
||||||
|
#[align]
|
||||||
|
struct AlignStruct(u8);
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_align() {
|
||||||
|
let bytes = vec![0, 0x80];
|
||||||
|
let mut data = Vec::new();
|
||||||
|
let mut stream = BitWriteStream::new(&mut data, BigEndian);
|
||||||
|
stream.write_bool(false).unwrap();
|
||||||
|
let val = AlignStruct(0x80);
|
||||||
|
stream.write(&val).unwrap();
|
||||||
|
assert_eq!(bytes, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(BitWrite, PartialEq, Debug)]
|
||||||
|
#[align]
|
||||||
|
struct AlignFieldStruct {
|
||||||
|
#[size = 1]
|
||||||
|
foo: u8,
|
||||||
|
#[align]
|
||||||
|
bar: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_align_field() {
|
||||||
|
let bytes = vec![0, 0x80];
|
||||||
|
let mut data = Vec::new();
|
||||||
|
let mut stream = BitWriteStream::new(&mut data, BigEndian);
|
||||||
|
let val = AlignFieldStruct { foo: 0, bar: 0x80 };
|
||||||
|
stream.write(&val).unwrap();
|
||||||
|
assert_eq!(bytes, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(BitWrite, PartialEq, Debug)]
|
||||||
|
#[discriminant_bits = 4]
|
||||||
|
#[align]
|
||||||
|
enum AlignEnum {
|
||||||
|
Foo,
|
||||||
|
Bar(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_align_enum() {
|
||||||
|
let bytes = vec![0x00, 0x18, 0];
|
||||||
|
let mut data = Vec::new();
|
||||||
|
let mut stream = BitWriteStream::new(&mut data, BigEndian);
|
||||||
|
stream.write_bool(false).unwrap();
|
||||||
|
let val = AlignEnum::Bar(0x80);
|
||||||
|
stream.write(&val).unwrap();
|
||||||
|
assert_eq!(bytes, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(BitWrite, PartialEq, Debug)]
|
||||||
|
#[discriminant_bits = 4]
|
||||||
|
#[align]
|
||||||
|
enum AlignEnumField {
|
||||||
|
Foo,
|
||||||
|
#[align]
|
||||||
|
Bar(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_align_enum_field() {
|
||||||
|
let bytes = vec![0x00, 0x10, 0x80];
|
||||||
|
let mut data = Vec::new();
|
||||||
|
let mut stream = BitWriteStream::new(&mut data, BigEndian);
|
||||||
|
stream.write_bool(false).unwrap();
|
||||||
|
let val = AlignEnumField::Bar(0x80);
|
||||||
|
stream.write(&val).unwrap();
|
||||||
|
assert_eq!(bytes, data);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue