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

fix size expressions

This commit is contained in:
Robin Appelman 2021-07-13 19:10:09 +02:00
commit b7fa549e79
3 changed files with 58 additions and 22 deletions

View file

@ -5,7 +5,7 @@ use quote::{quote, quote_spanned};
use syn::spanned::Spanned; use syn::spanned::Spanned;
use syn::{ 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, Member, Path, Fields, GenericParam, Ident, Index, Lit, Member, Path, Type,
}; };
use syn_util::get_attribute_value; use syn_util::get_attribute_value;
@ -100,8 +100,14 @@ fn write(data: Data, struct_name: &Ident, attrs: &[Attribute]) -> TokenStream {
span: field.span(), span: field.span(),
}) })
}); });
quote_spanned! { field.span() => // extract int fields to be used in size expressions
let #name = &self.#member; if type_is_int(&field.ty) {
quote_spanned! { field.span() =>
#[allow(unused_variables)]
let #name = self.#member;
}
} else {
quote! {}
} }
}); });
@ -109,22 +115,24 @@ fn write(data: Data, struct_name: &Ident, attrs: &[Attribute]) -> TokenStream {
// 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 span = f.span(); let span = f.span();
let name = f let member = f.ident.clone().map(Member::Named).unwrap_or_else(|| {
.ident Member::Unnamed(Index {
.clone() index: i as u32,
.unwrap_or_else(|| Ident::new(&format!("__{}", i), span)); span,
})
});
match size { match size {
Some(size) => { Some(size) => {
quote_spanned! { span => quote_spanned! { span =>
{ {
let _size: usize = #size; let _size: usize = #size;
__target__stream.write_sized(#name, _size)?; __target__stream.write_sized(&self.#member, _size)?;
} }
} }
} }
None => { None => {
quote_spanned! { span => { quote_spanned! { span => {
__target__stream.write(#name)?; __target__stream.write(&self.#member)?;
}} }}
} }
} }
@ -258,14 +266,8 @@ fn get_field_size(attrs: &[Attribute], span: Span) -> Option<TokenStream> {
} }
Lit::Str(size_field) => { Lit::Str(size_field) => {
let size = parse_str::<Expr>(&size_field.value()).expect("size"); let size = parse_str::<Expr>(&size_field.value()).expect("size");
if size_field.value() == "input_size" { quote_spanned! {span =>
quote_spanned! {span => (#size) as usize
(#size) as usize
}
} else {
quote_spanned! {span =>
(*#size) as usize
}
} }
} }
_ => panic!("Unsupported value for size attribute"), _ => panic!("Unsupported value for size attribute"),
@ -278,3 +280,19 @@ fn get_field_size(attrs: &[Attribute], span: Span) -> Option<TokenStream> {
}) })
}) })
} }
fn type_is_int(ty: &Type) -> bool {
if let Type::Path(path) = ty {
if let Some(ident) = path.path.get_ident() {
let name = ident.to_string();
matches!(
name.as_str(),
"u8" | "u16" | "u32" | "u64" | "usize" | "i8" | "i16" | "i32" | "i64" | "isize"
)
} else {
false
}
} else {
false
}
}

View file

@ -6,10 +6,8 @@ use bitbuffer::{BitReadStream, Endianness};
use bitbuffer_derive::{BitWrite, BitWriteSized}; use bitbuffer_derive::{BitWrite, BitWriteSized};
#[derive(BitWrite)] #[derive(BitWrite)]
struct TestStruct { struct TestSizeExpression {
foo: u8, size: u8,
#[size = "size + 2"]
str: String, str: String,
} }
#[derive(BitWrite)]
struct UnnamedSize(u8, #[size = 5] String, bool);

View file

@ -260,3 +260,23 @@ fn test_empty_struct() {
stream.write(&EmptyStruct).unwrap(); stream.write(&EmptyStruct).unwrap();
assert_eq!(Vec::<u8>::new(), stream.finish()); assert_eq!(Vec::<u8>::new(), stream.finish());
} }
#[derive(BitWrite)]
struct TestSizeExpression {
size: u8,
#[size = "size + 2"]
str: String,
}
#[test]
fn test_read_size_expression() {
let bytes = vec![0b0000_0011, b'a', b'b', b'c', b'd', b'e'];
let mut stream = BitWriteStream::new(BigEndian);
let val = TestSizeExpression {
size: 3,
str: String::from("abcde"),
};
stream.write(&val).unwrap();
assert_eq!(bytes, stream.finish());
}