Skip to content

Commit

Permalink
add bit order option
Browse files Browse the repository at this point in the history
  • Loading branch information
pwfff authored and wrenger committed Jul 30, 2023
1 parent 3be527e commit 3857a23
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 10 deletions.
121 changes: 111 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,66 @@
//!
//! > Hint: You can use the rust-analyzer "Expand macro recursively" action to view the generated code.
//!
//! ### Bit Order
//!
//! The optional `order` macro argument determines the layout of the bits, with the default being
//! Lsb first:
//!
//! ```
//! # use bitfield_struct::bitfield;
//! #[bitfield(u8, order = Lsb)]
//! struct MyLsbByte {
//! /// The first field occupies the least significant bits
//! #[bits(4)]
//! kind: usize,
//! /// Booleans are 1 bit large
//! system: bool,
//! /// The bits attribute specifies the bit size of this field
//! #[bits(2)]
//! level: usize,
//! /// The last field spans over the most significant bits
//! present: bool
//! }
//!
//! let my_byte_lsb = MyLsbByte::new()
//! .with_kind(10)
//! .with_system(false)
//! .with_level(2)
//! .with_present(true);
//!
//! // .- present
//! // | .- level
//! // | | .- system
//! // | | | .- kind
//! assert!(my_byte_lsb.0 == 0b1_10_0_1010);
//!
//! #[bitfield(u8, order = Msb)]
//! struct MyMsbByte {
//! /// The first field occupies the least significant bits
//! #[bits(4)]
//! kind: usize,
//! /// Booleans are 1 bit large
//! system: bool,
//! /// The bits attribute specifies the bit size of this field
//! #[bits(2)]
//! level: usize,
//! /// The last field spans over the most significant bits
//! present: bool
//! }
//!
//! let my_byte_msb = MyMsbByte::new()
//! .with_kind(10)
//! .with_system(false)
//! .with_level(2)
//! .with_present(true);
//!
//! // .- kind
//! // | .- system
//! // | | .- level
//! // | | | .- present
//! assert!(my_byte_msb.0 == 0b1010_0_10_1);
//! ```
//!
//! ## `fmt::Debug` and `Default`
//!
//! This macro automatically creates a suitable `fmt::Debug` and `Default` implementations
Expand Down Expand Up @@ -239,6 +299,7 @@ fn bitfield_inner(args: TokenStream, input: TokenStream) -> syn::Result<TokenStr
bits,
debug,
default,
order,
} = syn::parse2::<Params>(args)?;

let span = input.fields.span();
Expand All @@ -254,7 +315,7 @@ fn bitfield_inner(args: TokenStream, input: TokenStream) -> syn::Result<TokenStr
let mut offset = 0;
let mut members = Vec::with_capacity(fields.named.len());
for field in fields.named {
let f = Member::new(ty.clone(), field, offset)?;
let f = Member::new(ty.clone(), bits, field, offset, order)?;
offset += f.bits;
members.push(f);
}
Expand Down Expand Up @@ -360,7 +421,13 @@ struct MemberInner {
}

impl Member {
fn new(base_ty: syn::Type, f: syn::Field, offset: usize) -> syn::Result<Self> {
fn new(
base_ty: syn::Type,
base_bits: usize,
f: syn::Field,
offset: usize,
order: Order,
) -> syn::Result<Self> {
let span = f.span();

let syn::Field {
Expand All @@ -383,6 +450,20 @@ impl Member {
} = parse_field(&attrs, &ty, ignore)?;

if bits > 0 && !ignore {
if offset + bits > base_bits {
return Err(syn::Error::new(
ty.span(),
"The total size of the members is too large!",
));
};

// compute the offset
let offset = if order == Order::Lsb {
offset
} else {
base_bits - offset - bits
};

if into.is_empty() || from.is_empty() {
return Err(syn::Error::new(
ty.span(),
Expand Down Expand Up @@ -706,12 +787,19 @@ impl Parse for BitsAttr {
}
}

#[derive(Clone, Copy, PartialEq)]
enum Order {
Lsb,
Msb,
}

/// The bitfield macro parameters
struct Params {
ty: syn::Type,
bits: usize,
debug: bool,
default: bool,
order: Order,
}

impl Parse for Params {
Expand All @@ -726,26 +814,39 @@ impl Parse for Params {

let mut debug = true;
let mut default = true;
let mut order = Order::Lsb;

// try parse additional args
while <Token![,]>::parse(input).is_ok() {
let ident = Ident::parse(input)?;
<Token![=]>::parse(input)?;
let value = syn::LitBool::parse(input)?.value;
if ident == "debug" {
debug = value;
} else if ident == "default" {
default = value;
} else {
return Err(syn::Error::new(ident.span(), "unknown argument"));
}
match ident.to_string().as_str() {
"debug" => {
let value = syn::LitBool::parse(input)?.value;
debug = value;
}
"default" => {
let value = syn::LitBool::parse(input)?.value;
default = value;
}
"order" => {
let value = match syn::Ident::parse(input)?.to_string().as_str() {
"Msb" | "msb" => Order::Msb,
"Lsb" | "lsb" => Order::Lsb,
_ => return Err(syn::Error::new(ident.span(), "unknown value for order")),
};
order = value
}
_ => return Err(syn::Error::new(ident.span(), "unknown argument")),
};
}

Ok(Params {
bits,
ty,
debug,
default,
order,
})
}
}
Expand Down
30 changes: 30 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,33 @@ fn default_padding() {

assert_eq!(v.0, 0x8fff_00ff);
}

#[test]
fn lsb_order() {
#[bitfield(u32, order=lsb)]
struct MyBitfield {
short: u16,
#[bits(8)]
__: (),
byte: u8,
}

let v = MyBitfield::new().with_short(0xe11e).with_byte(0xf0);

assert_eq!(v.0, 0xf0_00_e11e);
}

#[test]
fn msb_order() {
#[bitfield(u32, order=msb)]
struct MyBitfield {
short: u16,
#[bits(8)]
__: (),
byte: u8,
}

let v = MyBitfield::new().with_short(0xe11e).with_byte(0xf0);

assert_eq!(v.0, 0xe11e_00_f0);
}

0 comments on commit 3857a23

Please sign in to comment.