mirror of
https://codeberg.org/icewind/bitbuffer.git
synced 2026-06-03 16:44:06 +02:00
Merge pull request #8 from DCNick3/align
Re-introduce the align function & add #[align] attribute
This commit is contained in:
commit
80a1c7cc22
6 changed files with 355 additions and 19 deletions
|
|
@ -103,6 +103,38 @@
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
//! # Alignment
|
||||||
|
//!
|
||||||
|
//! You can request alignment for a struct, enum or a field using #[align] attribute.
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use bitbuffer::BitRead;
|
||||||
|
//! #
|
||||||
|
//! #[derive(BitRead)]
|
||||||
|
//! #[align] // align the reader before starting to read the struct
|
||||||
|
//! struct TestAlignStruct {
|
||||||
|
//! #[size = 1]
|
||||||
|
//! foo: u8,
|
||||||
|
//! #[align] // align the reader before reading the field
|
||||||
|
//! bar: u8,
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! It can also be applied to non-unit enum variants:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use bitbuffer::BitRead;
|
||||||
|
//! #
|
||||||
|
//! #[derive(BitRead)]
|
||||||
|
//! #[align] // align the reader before starting to read the enum
|
||||||
|
//! #[discriminant_bits = 2]
|
||||||
|
//! enum TestAlignEnum {
|
||||||
|
//! Foo(u8),
|
||||||
|
//! #[align] // align the reader before reading the variant (but after reading the discriminant)
|
||||||
|
//! Bar(u8),
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
//! # Endianness
|
//! # Endianness
|
||||||
//!
|
//!
|
||||||
//! If the struct that `BitRead` or `BitReadSized` is derived for requires a Endianness type parameter, you need to tell the derive macro the name of the type parameter used
|
//! If the struct that `BitRead` or `BitReadSized` is derived for requires a Endianness type parameter, you need to tell the derive macro the name of the type parameter used
|
||||||
|
|
@ -146,12 +178,12 @@ 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, Lit, LitInt, LitStr, Path,
|
Fields, GenericParam, Ident, Lit, LitInt, LitStr, Path,
|
||||||
};
|
};
|
||||||
use syn_util::get_attribute_value;
|
use syn_util::{contains_attribute, get_attribute_value};
|
||||||
|
|
||||||
/// See the [crate documentation](index.html) for details
|
/// See the [crate documentation](index.html) for details
|
||||||
#[proc_macro_derive(
|
#[proc_macro_derive(
|
||||||
BitRead,
|
BitRead,
|
||||||
attributes(size, size_bits, discriminant_bits, discriminant, endianness)
|
attributes(size, size_bits, discriminant_bits, discriminant, endianness, align)
|
||||||
)]
|
)]
|
||||||
pub fn derive_bitread(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn derive_bitread(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
derive_bitread_trait(input, "BitRead".to_owned(), None)
|
derive_bitread_trait(input, "BitRead".to_owned(), None)
|
||||||
|
|
@ -161,7 +193,7 @@ pub fn derive_bitread(input: proc_macro::TokenStream) -> proc_macro::TokenStream
|
||||||
/// See the [crate documentation](index.html) for details
|
/// See the [crate documentation](index.html) for details
|
||||||
#[proc_macro_derive(
|
#[proc_macro_derive(
|
||||||
BitReadSized,
|
BitReadSized,
|
||||||
attributes(size, size_bits, discriminant_bits, discriminant, endianness)
|
attributes(size, size_bits, discriminant_bits, discriminant, endianness, align)
|
||||||
)]
|
)]
|
||||||
pub fn derive_bitread_sized(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn derive_bitread_sized(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let extra_param = parse_str::<TokenStream>(", input_size: usize").unwrap();
|
let extra_param = parse_str::<TokenStream>(", input_size: usize").unwrap();
|
||||||
|
|
@ -171,7 +203,7 @@ pub fn derive_bitread_sized(input: proc_macro::TokenStream) -> proc_macro::Token
|
||||||
/// See the [crate documentation](index.html) for details
|
/// See the [crate documentation](index.html) for details
|
||||||
#[proc_macro_derive(
|
#[proc_macro_derive(
|
||||||
BitWrite,
|
BitWrite,
|
||||||
attributes(size, size_bits, discriminant_bits, discriminant, endianness)
|
attributes(size, size_bits, discriminant_bits, discriminant, endianness, align)
|
||||||
)]
|
)]
|
||||||
pub fn derive_bitwrite(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn derive_bitwrite(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
derive_bitwrite_trait(input, "BitWrite".into(), "write".into(), None)
|
derive_bitwrite_trait(input, "BitWrite".into(), "write".into(), None)
|
||||||
|
|
@ -181,7 +213,7 @@ pub fn derive_bitwrite(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
|
||||||
/// See the [crate documentation](index.html) for details
|
/// See the [crate documentation](index.html) for details
|
||||||
#[proc_macro_derive(
|
#[proc_macro_derive(
|
||||||
BitWriteSized,
|
BitWriteSized,
|
||||||
attributes(size, size_bits, discriminant_bits, discriminant, endianness)
|
attributes(size, size_bits, discriminant_bits, discriminant, endianness, align)
|
||||||
)]
|
)]
|
||||||
pub fn derive_bitwrite_sized(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
pub fn derive_bitwrite_sized(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
let extra_param = parse_str::<TokenStream>(", input_size: usize").unwrap();
|
let extra_param = parse_str::<TokenStream>(", input_size: usize").unwrap();
|
||||||
|
|
@ -266,6 +298,7 @@ fn derive_bitread_trait(
|
||||||
|
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
impl #impl_generics #trait_def for #name #ty_generics #where_clause {
|
impl #impl_generics #trait_def for #name #ty_generics #where_clause {
|
||||||
|
#[allow(unused_braces)]
|
||||||
fn read(stream: &mut ::bitbuffer::BitReadStream<#lifetime, #endianness_ident>#extra_param) -> ::bitbuffer::Result<Self> {
|
fn read(stream: &mut ::bitbuffer::BitReadStream<#lifetime, #endianness_ident>#extra_param) -> ::bitbuffer::Result<Self> {
|
||||||
// if the read has a predicable size, we can do the bounds check in one go
|
// if the read has a predicable size, we can do the bounds check in one go
|
||||||
match <Self as #trait_def>::#size_method_name(#extra_param_call) {
|
match <Self as #trait_def>::#size_method_name(#extra_param_call) {
|
||||||
|
|
@ -281,6 +314,7 @@ fn derive_bitread_trait(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused_braces)]
|
||||||
unsafe fn read_unchecked(stream: &mut ::bitbuffer::BitReadStream<#lifetime, #endianness_ident>#extra_param, end: bool) -> ::bitbuffer::Result<Self> {
|
unsafe fn read_unchecked(stream: &mut ::bitbuffer::BitReadStream<#lifetime, #endianness_ident>#extra_param, end: bool) -> ::bitbuffer::Result<Self> {
|
||||||
#parsed_unchecked
|
#parsed_unchecked
|
||||||
}
|
}
|
||||||
|
|
@ -299,11 +333,14 @@ fn derive_bitread_trait(
|
||||||
fn parse(data: Data, struct_name: &Ident, attrs: &[Attribute], unchecked: bool) -> TokenStream {
|
fn parse(data: Data, struct_name: &Ident, attrs: &[Attribute], unchecked: bool) -> 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 values = fields.iter().map(|f| {
|
let values = fields.iter().map(|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(attrs);
|
||||||
let field_type = &f.ty;
|
let field_type = &f.ty;
|
||||||
let span = f.span();
|
let span = f.span();
|
||||||
if unchecked {
|
if unchecked {
|
||||||
|
|
@ -311,6 +348,7 @@ fn parse(data: Data, struct_name: &Ident, attrs: &[Attribute], unchecked: bool)
|
||||||
Some(size) => {
|
Some(size) => {
|
||||||
quote_spanned! { span =>
|
quote_spanned! { span =>
|
||||||
{
|
{
|
||||||
|
#align;
|
||||||
let _size: usize = #size;
|
let _size: usize = #size;
|
||||||
stream.read_sized_unchecked::<#field_type>(_size, end)?
|
stream.read_sized_unchecked::<#field_type>(_size, end)?
|
||||||
}
|
}
|
||||||
|
|
@ -318,7 +356,10 @@ fn parse(data: Data, struct_name: &Ident, attrs: &[Attribute], unchecked: bool)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
quote_spanned! { span =>
|
quote_spanned! { span =>
|
||||||
stream.read_unchecked::<#field_type>(end)?
|
{
|
||||||
|
#align;
|
||||||
|
stream.read_unchecked::<#field_type>(end)?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -327,6 +368,7 @@ fn parse(data: Data, struct_name: &Ident, attrs: &[Attribute], unchecked: bool)
|
||||||
Some(size) => {
|
Some(size) => {
|
||||||
quote_spanned! { span =>
|
quote_spanned! { span =>
|
||||||
{
|
{
|
||||||
|
#align;
|
||||||
let _size: usize = #size;
|
let _size: usize = #size;
|
||||||
stream.read_sized::<#field_type>(_size)?
|
stream.read_sized::<#field_type>(_size)?
|
||||||
}
|
}
|
||||||
|
|
@ -334,7 +376,10 @@ fn parse(data: Data, struct_name: &Ident, attrs: &[Attribute], unchecked: bool)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
quote_spanned! { span =>
|
quote_spanned! { span =>
|
||||||
stream.read::<#field_type>()?
|
{
|
||||||
|
#align;
|
||||||
|
stream.read::<#field_type>()?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -356,6 +401,8 @@ fn parse(data: Data, struct_name: &Ident, attrs: &[Attribute], unchecked: bool)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
quote_spanned! { span =>
|
quote_spanned! { span =>
|
||||||
|
#align;
|
||||||
|
|
||||||
#(#definitions)*
|
#(#definitions)*
|
||||||
|
|
||||||
Ok(#struct_name {
|
Ok(#struct_name {
|
||||||
|
|
@ -364,11 +411,15 @@ fn parse(data: Data, struct_name: &Ident, attrs: &[Attribute], unchecked: bool)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Fields::Unnamed(_) => quote_spanned! { span =>
|
Fields::Unnamed(_) => quote_spanned! { span =>
|
||||||
|
#align;
|
||||||
|
|
||||||
Ok(#struct_name(
|
Ok(#struct_name(
|
||||||
#(#values ,)*
|
#(#values ,)*
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
Fields::Unit => quote_spanned! {span=>
|
Fields::Unit => quote_spanned! { span=>
|
||||||
|
#align;
|
||||||
|
|
||||||
Ok(#struct_name)
|
Ok(#struct_name)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -377,7 +428,7 @@ fn parse(data: Data, struct_name: &Ident, attrs: &[Attribute], unchecked: bool)
|
||||||
let discriminant_bits: u64 = match get_attribute_value(attrs, &["discriminant_bits"]) {
|
let discriminant_bits: u64 = match get_attribute_value(attrs, &["discriminant_bits"]) {
|
||||||
Some(attr) => attr,
|
Some(attr) => attr,
|
||||||
None => {
|
None => {
|
||||||
return quote! {span=>
|
return quote_spanned! { span=>
|
||||||
compile_error!("'discriminant_bits' attribute is required when deriving `BinRead` for enums");
|
compile_error!("'discriminant_bits' attribute is required when deriving `BinRead` for enums");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -388,15 +439,24 @@ fn parse(data: Data, struct_name: &Ident, attrs: &[Attribute], unchecked: bool)
|
||||||
let span = variant.span();
|
let span = variant.span();
|
||||||
let variant_name = &variant.ident;
|
let variant_name = &variant.ident;
|
||||||
let read_fields = match &variant.fields {
|
let read_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({
|
#struct_name::#variant_name({
|
||||||
|
#align;
|
||||||
let _size:usize = #size;
|
let _size:usize = #size;
|
||||||
stream.read_sized(_size)?
|
stream.read_sized(_size)?
|
||||||
})
|
})
|
||||||
|
|
@ -404,7 +464,10 @@ fn parse(data: Data, struct_name: &Ident, attrs: &[Attribute], unchecked: bool)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
quote_spanned! { span =>
|
quote_spanned! { span =>
|
||||||
#struct_name::#variant_name(stream.read()?)
|
{
|
||||||
|
#align;
|
||||||
|
#struct_name::#variant_name(stream.read()?)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -437,6 +500,7 @@ fn parse(data: Data, struct_name: &Ident, attrs: &[Attribute], unchecked: bool)
|
||||||
|
|
||||||
let enum_name = Lit::Str(LitStr::new(&struct_name.to_string(), struct_name.span()));
|
let enum_name = Lit::Str(LitStr::new(&struct_name.to_string(), struct_name.span()));
|
||||||
quote_spanned! {span=>
|
quote_spanned! {span=>
|
||||||
|
#align;
|
||||||
let discriminant:#repr = stream.read_int(#discriminant_bits as usize)?;
|
let discriminant:#repr = stream.read_int(#discriminant_bits as usize)?;
|
||||||
Ok(match discriminant {
|
Ok(match discriminant {
|
||||||
#(#match_arms)*
|
#(#match_arms)*
|
||||||
|
|
@ -453,6 +517,12 @@ fn parse(data: Data, struct_name: &Ident, attrs: &[Attribute], unchecked: bool)
|
||||||
fn size(data: Data, struct_name: &Ident, attrs: &[Attribute], has_input_size: bool) -> TokenStream {
|
fn size(data: Data, struct_name: &Ident, attrs: &[Attribute], has_input_size: bool) -> TokenStream {
|
||||||
let span = struct_name.span();
|
let span = struct_name.span();
|
||||||
|
|
||||||
|
if contains_attribute(attrs, &["align"]) {
|
||||||
|
return quote_spanned! { span =>
|
||||||
|
None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
match data {
|
match data {
|
||||||
Data::Struct(DataStruct { fields, .. }) => {
|
Data::Struct(DataStruct { fields, .. }) => {
|
||||||
let sizes = fields.iter().map(|f| {
|
let sizes = fields.iter().map(|f| {
|
||||||
|
|
@ -503,6 +573,7 @@ fn size(data: Data, struct_name: &Ident, attrs: &[Attribute], has_input_size: bo
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Unit variants having "align" attributes are not allowed, so we can just check if all variants are unit
|
||||||
let is_unit = data
|
let is_unit = data
|
||||||
.variants
|
.variants
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -526,6 +597,9 @@ fn is_const_size(attrs: &[Attribute], has_input_size: bool) -> bool {
|
||||||
if get_attribute_value::<Lit>(attrs, &["size_bits"]).is_some() {
|
if get_attribute_value::<Lit>(attrs, &["size_bits"]).is_some() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if contains_attribute(attrs, &["align"]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
get_attribute_value(attrs, &["size"])
|
get_attribute_value(attrs, &["size"])
|
||||||
.map(|size_lit| match size_lit {
|
.map(|size_lit| match size_lit {
|
||||||
Lit::Int(_) => true,
|
Lit::Int(_) => true,
|
||||||
|
|
@ -571,3 +645,13 @@ fn repr_for_bits(discriminant_bits: u64) -> TokenStream {
|
||||||
quote!(usize)
|
quote!(usize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_align(attrs: &[Attribute]) -> TokenStream {
|
||||||
|
if contains_attribute(attrs, &["align"]) {
|
||||||
|
quote! {
|
||||||
|
stream.align()?
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! { () }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
@ -72,6 +72,7 @@ pub fn derive_bitwrite_trait(
|
||||||
let write_method = Ident::new(&write_method_name, span);
|
let write_method = Ident::new(&write_method_name, span);
|
||||||
|
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
|
#[allow(unused_braces)]
|
||||||
impl #impl_generics #trait_def for #name #ty_generics #where_clause {
|
impl #impl_generics #trait_def for #name #ty_generics #where_clause {
|
||||||
fn #write_method(&self, __target__stream: &mut ::bitbuffer::BitWriteStream<#endianness_ident>#extra_param) -> ::bitbuffer::Result<()> {
|
fn #write_method(&self, __target__stream: &mut ::bitbuffer::BitWriteStream<#endianness_ident>#extra_param) -> ::bitbuffer::Result<()> {
|
||||||
#parsed
|
#parsed
|
||||||
|
|
@ -87,6 +88,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 +117,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 +129,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 +137,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 +226,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 +251,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 +267,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 +322,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! { () }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -335,3 +335,79 @@ fn test_bit_size_sized() {
|
||||||
Some(8 + 8 * 16 + 1)
|
Some(8 + 8 * 16 + 1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(BitRead, PartialEq, Debug)]
|
||||||
|
#[align]
|
||||||
|
struct AlignStruct(u8);
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_align() {
|
||||||
|
let bytes = vec![0, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||||
|
let buffer = BitReadBuffer::new(&bytes, BigEndian);
|
||||||
|
let mut stream = BitReadStream::from(buffer);
|
||||||
|
stream.read_bool().unwrap();
|
||||||
|
assert_eq!(AlignStruct(0x80), stream.read().unwrap());
|
||||||
|
assert_eq!(16, stream.pos());
|
||||||
|
assert_eq!(None, bit_size_of::<AlignStruct>());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(BitRead, PartialEq, Debug)]
|
||||||
|
#[align]
|
||||||
|
struct AlignFieldStruct {
|
||||||
|
#[size = 1]
|
||||||
|
foo: u8,
|
||||||
|
#[align]
|
||||||
|
bar: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_align_field() {
|
||||||
|
let bytes = vec![0, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||||
|
let buffer = BitReadBuffer::new(&bytes, BigEndian);
|
||||||
|
let mut stream = BitReadStream::from(buffer);
|
||||||
|
assert_eq!(
|
||||||
|
AlignFieldStruct { foo: 0, bar: 0x80 },
|
||||||
|
stream.read().unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(16, stream.pos());
|
||||||
|
assert_eq!(None, bit_size_of::<AlignStruct>());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(BitRead, PartialEq, Debug)]
|
||||||
|
#[discriminant_bits = 4]
|
||||||
|
#[align]
|
||||||
|
enum AlignEnum {
|
||||||
|
Foo,
|
||||||
|
Bar(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_align_enum() {
|
||||||
|
let bytes = vec![0x00, 0x18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||||
|
let buffer = BitReadBuffer::new(&bytes, BigEndian);
|
||||||
|
let mut stream = BitReadStream::from(buffer);
|
||||||
|
stream.read_bool().unwrap();
|
||||||
|
assert_eq!(AlignEnum::Bar(0x80), stream.read().unwrap());
|
||||||
|
assert_eq!(20, stream.pos());
|
||||||
|
assert_eq!(None, bit_size_of::<AlignEnum>());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(BitRead, PartialEq, Debug)]
|
||||||
|
#[discriminant_bits = 4]
|
||||||
|
#[align]
|
||||||
|
enum AlignEnumField {
|
||||||
|
Foo,
|
||||||
|
#[align]
|
||||||
|
Bar(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_align_enum_field() {
|
||||||
|
let bytes = vec![0x00, 0x10, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||||
|
let buffer = BitReadBuffer::new(&bytes, BigEndian);
|
||||||
|
let mut stream = BitReadStream::from(buffer);
|
||||||
|
stream.read_bool().unwrap();
|
||||||
|
assert_eq!(AlignEnumField::Bar(0x80), stream.read().unwrap());
|
||||||
|
assert_eq!(24, stream.pos());
|
||||||
|
assert_eq!(None, bit_size_of::<AlignEnum>());
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -423,6 +423,45 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Align the stream on the next byte and returns the amount of bits read
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// - [`ReadError::NotEnoughData`]: not enough bits available in the stream to skip
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use bitbuffer::{BitReadBuffer, BitReadStream, LittleEndian, Result};
|
||||||
|
/// #
|
||||||
|
/// # fn main() -> Result<()> {
|
||||||
|
/// # let bytes = vec![
|
||||||
|
/// # 0b1011_0101, 0b0110_1010, 0b1010_1100, 0b1001_1001,
|
||||||
|
/// # 0b1001_1001, 0b1001_1001, 0b1001_1001, 0b1110_0111
|
||||||
|
/// # ];
|
||||||
|
/// # let buffer = BitReadBuffer::new(&bytes, LittleEndian);
|
||||||
|
/// # let mut stream = BitReadStream::new(buffer);
|
||||||
|
/// stream.align()?;
|
||||||
|
/// assert_eq!(stream.pos(), 0);
|
||||||
|
///
|
||||||
|
/// stream.skip_bits(3)?;
|
||||||
|
/// assert_eq!(stream.pos(), 3);
|
||||||
|
/// stream.align();
|
||||||
|
/// assert_eq!(stream.pos(), 8);
|
||||||
|
/// assert_eq!(stream.read_int::<u8>(4)?, 0b1010);
|
||||||
|
/// #
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`ReadError::NotEnoughData`]: enum.ReadError.html#variant.NotEnoughData
|
||||||
|
pub fn align(&mut self) -> Result<usize> {
|
||||||
|
match self.pos % 8 {
|
||||||
|
0 => Ok(0),
|
||||||
|
n => self.skip_bits(8 - n).map(|_| 8 - n),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the position of the stream
|
/// Set the position of the stream
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,38 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Align the stream on the next byte by writing zero bits and returns the amount of bits written
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use bitbuffer::{BitReadBuffer, LittleEndian, Result};
|
||||||
|
/// #
|
||||||
|
/// # fn main() -> Result<()> {
|
||||||
|
/// # use bitbuffer::{BitWriteStream, LittleEndian};
|
||||||
|
///
|
||||||
|
/// let mut data = Vec::new();
|
||||||
|
/// let mut stream = BitWriteStream::new(&mut data, LittleEndian);
|
||||||
|
/// stream.write_bool(true)?;
|
||||||
|
/// stream.align();
|
||||||
|
/// assert_eq!(stream.bit_len(), 8);
|
||||||
|
/// assert_eq!(data, [0b0000_0001]);
|
||||||
|
/// #
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`ReadError::NotEnoughData`]: enum.ReadError.html#variant.NotEnoughData
|
||||||
|
pub fn align(&mut self) -> usize {
|
||||||
|
match self.bit_len() % 8 {
|
||||||
|
0 => 0,
|
||||||
|
n => {
|
||||||
|
self.push_bits(0, 8 - n);
|
||||||
|
8 - n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Write a boolean into the buffer
|
/// Write a boolean into the buffer
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue