Skip to content

Commit

Permalink
Merge pull request #113 from knurling-rs/bitfield_autotruncate
Browse files Browse the repository at this point in the history
automatically truncate larger integer types passed to bitfields
  • Loading branch information
japaric authored Aug 14, 2020
2 parents d01c1d6 + 263e442 commit 124e3dc
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 20 deletions.
16 changes: 2 additions & 14 deletions book/src/bitfields.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,18 @@ binfmt::trace!(
);
```

The bitfield argument is expected to be a *unsigned* integer that's just large enough to contain the bitfields.
The bitfield argument is expected to be a *fully typed, unsigned* integer that's large enough to contain the bitfields.
For example, if bitfield ranges only cover up to bit `11` (e.g. `:8..12`) then the argument must be `u16`.

Bit indices are little-endian: the 0th bit is the rightmost bit.

Bitfields are not range inclusive, e.g.
``` rust
# extern crate binfmt;
binfmt::trace!("first two bits: {0:0..3}", 254);
binfmt::trace!("first two bits: {0:0..3}", 254u32);
```
will evaluate to `0b10`.


⚠️ Currently, the bitfielded argument must be of the smallest type that can contain the largest end index.

``` rust,compile_fail
# extern crate binfmt;
// this will not compile
binfmt::trace!("{0:0..3}", 555u16);
// this will compile
binfmt::trace!("{0:0..3}", 555u16 as u8);
```

⚠️ You can not reuse the same argument in a bitfield- and a non bitfield parameter. This will not compile:
``` rust,compile_fail
# extern crate binfmt;
Expand Down
2 changes: 1 addition & 1 deletion firmware/qemu/src/bin/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ fn main() -> ! {
isize::min_value()
);
binfmt::info!("usize: 0 = {:usize}, MAX = {:usize}", 0, usize::max_value());
binfmt::info!("bitfields {0:0..7} {0:9..14}", 0b0110_0011_1101_0010u16);
binfmt::info!("bitfields {0:0..3} {0:5..7}", 0b0110_0011_1101_0110u16);

binfmt::trace!("log trace");
binfmt::debug!("log debug");
Expand Down
10 changes: 5 additions & 5 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -730,16 +730,16 @@ impl Codegen {

match largest_bit_index {
0..=8 => {
exprs.push(quote!(_fmt_.u8(#arg)));
exprs.push(quote!(_fmt_.u8(&binfmt::export::truncate(*#arg))));
}
9..=16 => {
exprs.push(quote!(_fmt_.u16(#arg)));
exprs.push(quote!(_fmt_.u16(&binfmt::export::truncate(*#arg))));
}
17..=24 => {
exprs.push(quote!(_fmt_.u24(#arg)));
exprs.push(quote!(_fmt_.u24(&binfmt::export::truncate(*#arg))));
}
25..=32 => {
exprs.push(quote!(_fmt_.u32(#arg)));
exprs.push(quote!(_fmt_.u32(&binfmt::export::truncate(*#arg))));
}
_ => unreachable!(),
}
Expand Down Expand Up @@ -797,4 +797,4 @@ impl Codegen {

Ok(Codegen { pats, exprs })
}
}
}
74 changes: 74 additions & 0 deletions src/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,77 @@ pub fn istr(address: usize) -> Str {
address: address as *const u8 as u16,
}
}

mod sealed {
pub trait Truncate<U> {
fn truncate(self) -> U;
}

impl Truncate<u8> for u8 {
fn truncate(self) -> u8 {
self
}
}

impl Truncate<u8> for u16 {
fn truncate(self) -> u8 {
self as u8
}
}

impl Truncate<u8> for u32 {
fn truncate(self) -> u8 {
self as u8
}
}

impl Truncate<u8> for u64 {
fn truncate(self) -> u8 {
self as u8
}
}

// needed so we can call truncate() without having to check whether truncation is necessary first
impl Truncate<u16> for u16 {
fn truncate(self) -> u16 {
self
}
}

impl Truncate<u16> for u32 {
fn truncate(self) -> u16 {
self as u16
}
}


impl Truncate<u16> for u64 {
fn truncate(self) -> u16 {
self as u16
}
}

// needed so we can call truncate() without having to check whether truncation is necessary first
impl Truncate<u32> for u32 {
fn truncate(self) -> u32 {
self
}
}

impl Truncate<u32> for u64 {
fn truncate(self) -> u32 {
self as u32
}
}

// needed so we can call truncate() without having to check whether truncation is necessary first
impl Truncate<u64> for u64 {
fn truncate(self) -> u64 {
self
}
}
}

pub fn truncate<T>(x: impl sealed::Truncate<T>) -> T {
x.truncate()
}

0 comments on commit 124e3dc

Please sign in to comment.