mirror of
https://codeberg.org/icewind/bitbuffer.git
synced 2026-06-03 08:34:07 +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
|
||||
//!
|
||||
//! 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,
|
||||
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
|
||||
#[proc_macro_derive(
|
||||
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 {
|
||||
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
|
||||
#[proc_macro_derive(
|
||||
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 {
|
||||
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
|
||||
#[proc_macro_derive(
|
||||
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 {
|
||||
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
|
||||
#[proc_macro_derive(
|
||||
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 {
|
||||
let extra_param = parse_str::<TokenStream>(", input_size: usize").unwrap();
|
||||
|
|
@ -266,6 +298,7 @@ fn derive_bitread_trait(
|
|||
|
||||
let expanded = quote! {
|
||||
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> {
|
||||
// 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) {
|
||||
|
|
@ -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> {
|
||||
#parsed_unchecked
|
||||
}
|
||||
|
|
@ -299,11 +333,14 @@ fn derive_bitread_trait(
|
|||
fn parse(data: Data, struct_name: &Ident, attrs: &[Attribute], unchecked: bool) -> TokenStream {
|
||||
let span = struct_name.span();
|
||||
|
||||
let align = get_align(attrs);
|
||||
|
||||
match data {
|
||||
Data::Struct(DataStruct { fields, .. }) => {
|
||||
let values = fields.iter().map(|f| {
|
||||
// Get attributes `#[..]` on each field
|
||||
let size = get_field_size(&f.attrs, f.span());
|
||||
let align = get_align(attrs);
|
||||
let field_type = &f.ty;
|
||||
let span = f.span();
|
||||
if unchecked {
|
||||
|
|
@ -311,6 +348,7 @@ fn parse(data: Data, struct_name: &Ident, attrs: &[Attribute], unchecked: bool)
|
|||
Some(size) => {
|
||||
quote_spanned! { span =>
|
||||
{
|
||||
#align;
|
||||
let _size: usize = #size;
|
||||
stream.read_sized_unchecked::<#field_type>(_size, end)?
|
||||
}
|
||||
|
|
@ -318,7 +356,10 @@ fn parse(data: Data, struct_name: &Ident, attrs: &[Attribute], unchecked: bool)
|
|||
}
|
||||
None => {
|
||||
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) => {
|
||||
quote_spanned! { span =>
|
||||
{
|
||||
#align;
|
||||
let _size: usize = #size;
|
||||
stream.read_sized::<#field_type>(_size)?
|
||||
}
|
||||
|
|
@ -334,7 +376,10 @@ fn parse(data: Data, struct_name: &Ident, attrs: &[Attribute], unchecked: bool)
|
|||
}
|
||||
None => {
|
||||
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 =>
|
||||
#align;
|
||||
|
||||
#(#definitions)*
|
||||
|
||||
Ok(#struct_name {
|
||||
|
|
@ -364,11 +411,15 @@ fn parse(data: Data, struct_name: &Ident, attrs: &[Attribute], unchecked: bool)
|
|||
}
|
||||
}
|
||||
Fields::Unnamed(_) => quote_spanned! { span =>
|
||||
#align;
|
||||
|
||||
Ok(#struct_name(
|
||||
#(#values ,)*
|
||||
))
|
||||
},
|
||||
Fields::Unit => quote_spanned! {span=>
|
||||
Fields::Unit => quote_spanned! { span=>
|
||||
#align;
|
||||
|
||||
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"]) {
|
||||
Some(attr) => attr,
|
||||
None => {
|
||||
return quote! {span=>
|
||||
return quote_spanned! { span=>
|
||||
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 variant_name = &variant.ident;
|
||||
let read_fields = match &variant.fields {
|
||||
Fields::Unit => quote_spanned! {span=>
|
||||
#struct_name::#variant_name
|
||||
},
|
||||
Fields::Unit => {
|
||||
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) => {
|
||||
let size = get_field_size(&variant.attrs, f.span());
|
||||
let align = get_align(&variant.attrs);
|
||||
match size {
|
||||
Some(size) => {
|
||||
quote_spanned! { span =>
|
||||
#struct_name::#variant_name({
|
||||
#align;
|
||||
let _size:usize = #size;
|
||||
stream.read_sized(_size)?
|
||||
})
|
||||
|
|
@ -404,7 +464,10 @@ fn parse(data: Data, struct_name: &Ident, attrs: &[Attribute], unchecked: bool)
|
|||
}
|
||||
None => {
|
||||
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()));
|
||||
quote_spanned! {span=>
|
||||
#align;
|
||||
let discriminant:#repr = stream.read_int(#discriminant_bits as usize)?;
|
||||
Ok(match discriminant {
|
||||
#(#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 {
|
||||
let span = struct_name.span();
|
||||
|
||||
if contains_attribute(attrs, &["align"]) {
|
||||
return quote_spanned! { span =>
|
||||
None
|
||||
};
|
||||
}
|
||||
|
||||
match data {
|
||||
Data::Struct(DataStruct { fields, .. }) => {
|
||||
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
|
||||
.variants
|
||||
.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() {
|
||||
return false;
|
||||
}
|
||||
if contains_attribute(attrs, &["align"]) {
|
||||
return false;
|
||||
}
|
||||
get_attribute_value(attrs, &["size"])
|
||||
.map(|size_lit| match size_lit {
|
||||
Lit::Int(_) => true,
|
||||
|
|
@ -571,3 +645,13 @@ fn repr_for_bits(discriminant_bits: u64) -> TokenStream {
|
|||
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,
|
||||
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(
|
||||
input: proc_macro::TokenStream,
|
||||
|
|
@ -72,6 +72,7 @@ pub fn derive_bitwrite_trait(
|
|||
let write_method = Ident::new(&write_method_name, span);
|
||||
|
||||
let expanded = quote! {
|
||||
#[allow(unused_braces)]
|
||||
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<()> {
|
||||
#parsed
|
||||
|
|
@ -87,6 +88,8 @@ pub fn derive_bitwrite_trait(
|
|||
fn write(data: Data, struct_name: &Ident, attrs: &[Attribute]) -> TokenStream {
|
||||
let span = struct_name.span();
|
||||
|
||||
let align = get_align(attrs);
|
||||
|
||||
match data {
|
||||
Data::Struct(DataStruct { fields, .. }) => {
|
||||
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)| {
|
||||
// Get attributes `#[..]` on each field
|
||||
let size = get_field_size(&f.attrs, f.span());
|
||||
let align = get_align(&f.attrs);
|
||||
let span = f.span();
|
||||
let member = f.ident.clone().map(Member::Named).unwrap_or_else(|| {
|
||||
Member::Unnamed(Index {
|
||||
|
|
@ -125,6 +129,7 @@ fn write(data: Data, struct_name: &Ident, attrs: &[Attribute]) -> TokenStream {
|
|||
Some(size) => {
|
||||
quote_spanned! { span =>
|
||||
{
|
||||
#align;
|
||||
let _size: usize = #size;
|
||||
__target__stream.write_sized(&self.#member, _size)?;
|
||||
}
|
||||
|
|
@ -132,13 +137,17 @@ fn write(data: Data, struct_name: &Ident, attrs: &[Attribute]) -> TokenStream {
|
|||
}
|
||||
None => {
|
||||
quote_spanned! { span => {
|
||||
__target__stream.write(&self.#member)?;
|
||||
{
|
||||
#align;
|
||||
__target__stream.write(&self.#member)?;
|
||||
}
|
||||
}}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
quote_spanned! {span=>
|
||||
#align;
|
||||
#(#expand)*
|
||||
#(#writes)*
|
||||
Ok(())
|
||||
|
|
@ -217,15 +226,24 @@ fn write(data: Data, struct_name: &Ident, attrs: &[Attribute]) -> TokenStream {
|
|||
let variant_name = &variant.ident;
|
||||
|
||||
match &variant.fields {
|
||||
Fields::Unit => quote_spanned! {span =>
|
||||
#struct_name::#variant_name => {},
|
||||
},
|
||||
Fields::Unit => {
|
||||
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) => {
|
||||
let size = get_field_size(&variant.attrs, f.span());
|
||||
let align = get_align(&variant.attrs);
|
||||
match size {
|
||||
Some(size) => {
|
||||
quote_spanned! { span =>
|
||||
#struct_name::#variant_name(inner) => {
|
||||
#align;
|
||||
let size:usize = #size;
|
||||
__target__stream.write_sized(inner, size)?;
|
||||
}
|
||||
|
|
@ -233,7 +251,10 @@ fn write(data: Data, struct_name: &Ident, attrs: &[Attribute]) -> TokenStream {
|
|||
}
|
||||
None => {
|
||||
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);
|
||||
|
||||
quote_spanned! {span=>
|
||||
#align;
|
||||
let discriminant:#repr = match &self {
|
||||
#(#discriminant_value),*
|
||||
};
|
||||
|
|
@ -300,3 +322,13 @@ fn type_is_int(ty: &Type) -> bool {
|
|||
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)
|
||||
);
|
||||
}
|
||||
|
||||
#[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();
|
||||
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
|
||||
///
|
||||
/// # 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
|
||||
///
|
||||
/// # Examples
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue