use core::ops::{Shl, Shr};
pub trait Address:
Copy + Shl<u8, Output = Self> + Shr<u8, Output = Self> + Sized
{
const BITS: u8;
fn to_u8(self) -> u8;
fn rotate_right(self, n: u32) -> Self;
}
macro_rules! impl_address {
($($t:ty),+) => {
$(
impl Address for $t {
const BITS: u8 = <$t>::BITS as u8;
#[inline(always)]
fn to_u8(self) -> u8 {
self as u8
}
#[inline(always)]
fn rotate_right(self, n: u32) -> Self {
<$t>::rotate_right(self, n)
}
}
)+
};
}
impl_address!(u8, u16, u32, u64, u128);
#[inline(always)]
pub(crate) fn extract_bits<A>(address: A, offset: u8, len: u8) -> u8
where
A: Address,
{
(address.rotate_right((A::BITS - offset).wrapping_sub(len) as u32)).to_u8()
& ((1u16 << len) - 1) as u8
}
#[inline(always)]
pub(crate) fn extract_bits_saturated<A>(address: A, offset: u8, len: u8) -> u8
where
A: Address,
{
let remaining = u8::saturating_sub(A::BITS - offset, len);
if remaining + offset == A::BITS {
return 0;
}
(address << offset >> (remaining + offset)).to_u8()
}
#[allow(clippy::unusual_byte_groupings)] #[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_extraction() {
let key: u32 = 0b11111111_01010101_11110000_11001100;
assert_eq!(extract_bits(key, 0, 8), 0b11111111);
assert_eq!(extract_bits(key, 8, 8), 0b01010101);
assert_eq!(extract_bits(key, 16, 8), 0b11110000);
assert_eq!(extract_bits(key, 24, 8), 0b11001100);
}
#[test]
fn test_saturated_extraction() {
let key: u32 = 0b000000_000000_000000_000000_000000_01;
assert_eq!(extract_bits_saturated(key, 30, 6), 1);
let key: u32 = 0b000001_000001_000001_000001_000001_01;
assert_eq!(extract_bits_saturated(key, 30, 6), 1);
}
#[test]
fn test_msb_alignement() {
let key: u32 = 0b000000_000000_000000_000000_000000_01;
assert_eq!(extract_bits(key, 30, 6), 0b0001_0000);
let key: u32 = 0b000001_000001_000001_000001_000001_01;
assert_eq!(extract_bits(key, 30, 6), 0b0001_0000);
assert_eq!(extract_bits(key, 0, 6), 0b0000_0001);
}
}