From 263e442447fb18e96f26e8648c05db5f75914d56 Mon Sep 17 00:00:00 2001 From: Lotte Steenbrink Date: Thu, 13 Aug 2020 14:59:35 +0200 Subject: [PATCH] automatically truncate larger integer types passed to bitfields --- book/src/bitfields.md | 16 +------- firmware/qemu/src/bin/log.rs | 2 +- macros/src/lib.rs | 10 ++--- src/export.rs | 74 ++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 20 deletions(-) diff --git a/book/src/bitfields.md b/book/src/bitfields.md index 6fa23049..ef4fe394 100644 --- a/book/src/bitfields.md +++ b/book/src/bitfields.md @@ -14,7 +14,7 @@ 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. @@ -22,22 +22,10 @@ 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; diff --git a/firmware/qemu/src/bin/log.rs b/firmware/qemu/src/bin/log.rs index aa7b9bc4..a58bfcb7 100644 --- a/firmware/qemu/src/bin/log.rs +++ b/firmware/qemu/src/bin/log.rs @@ -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"); diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 3bdcc154..767ae7c3 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -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!(), } @@ -797,4 +797,4 @@ impl Codegen { Ok(Codegen { pats, exprs }) } -} +} \ No newline at end of file diff --git a/src/export.rs b/src/export.rs index 12341a7d..014a5cac 100644 --- a/src/export.rs +++ b/src/export.rs @@ -81,3 +81,77 @@ pub fn istr(address: usize) -> Str { address: address as *const u8 as u16, } } + +mod sealed { + pub trait Truncate { + fn truncate(self) -> U; + } + + impl Truncate for u8 { + fn truncate(self) -> u8 { + self + } + } + + impl Truncate for u16 { + fn truncate(self) -> u8 { + self as u8 + } + } + + impl Truncate for u32 { + fn truncate(self) -> u8 { + self as u8 + } + } + + impl Truncate 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 for u16 { + fn truncate(self) -> u16 { + self + } + } + + impl Truncate for u32 { + fn truncate(self) -> u16 { + self as u16 + } + } + + + impl Truncate 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 for u32 { + fn truncate(self) -> u32 { + self + } + } + + impl Truncate 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 for u64 { + fn truncate(self) -> u64 { + self + } + } +} + +pub fn truncate(x: impl sealed::Truncate) -> T { + x.truncate() +} \ No newline at end of file