pub const fn msb0_mask(width: u32, ranges: &[(u8, u8)]) -> u128 {
let mut mask = 0u128;
let mut i = 0;
while i < ranges.len() {
let (start, end) = ranges[i];
let mut bit = start;
while bit <= end {
mask |= 1u128 << (width - 1 - bit as u32);
bit += 1;
}
i += 1;
}
mask
}
pub const fn lsb0_mask(ranges: &[(u8, u8)]) -> u128 {
let mut mask = 0u128;
let mut i = 0;
while i < ranges.len() {
let (start, end) = ranges[i];
let mut bit = start;
while bit <= end {
mask |= 1u128 << bit;
bit += 1;
}
i += 1;
}
mask
}
#[doc(hidden)]
#[inline]
pub const fn __half_open_pair(start: u8, end: u8) -> (u8, u8) {
if start < end {
(start, end - 1)
} else {
(1, 0)
}
}
#[doc(hidden)]
pub trait __BitSpec {
fn __inclusive_pair(self) -> (u8, u8);
}
macro_rules! impl_runtime_bit {
($($ty:ty),* $(,)?) => {
$(
impl __BitSpec for $ty {
#[inline]
fn __inclusive_pair(self) -> (u8, u8) {
let bit = match u8::try_from(self) {
Ok(bit) => bit,
Err(_) => panic!("bit index must fit in u8"),
};
(bit, bit)
}
}
)*
};
}
impl_runtime_bit!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
impl<T> __BitSpec for core::ops::Range<T>
where
T: TryInto<u8>,
{
#[inline]
fn __inclusive_pair(self) -> (u8, u8) {
let start = match self.start.try_into() {
Ok(start) => start,
Err(_) => panic!("range start must fit in u8"),
};
let end = match self.end.try_into() {
Ok(end) => end,
Err(_) => panic!("range end must fit in u8"),
};
__half_open_pair(start, end)
}
}
impl<T> __BitSpec for core::ops::RangeInclusive<T>
where
T: TryInto<u8>,
{
#[inline]
fn __inclusive_pair(self) -> (u8, u8) {
let (start, end) = self.into_inner();
let start = match start.try_into() {
Ok(start) => start,
Err(_) => panic!("range start must fit in u8"),
};
let end = match end.try_into() {
Ok(end) => end,
Err(_) => panic!("range end must fit in u8"),
};
if start <= end {
(start, end)
} else {
(1, 0)
}
}
}
#[doc(hidden)]
#[inline]
pub fn __bit_spec_pair(spec: impl __BitSpec) -> (u8, u8) {
spec.__inclusive_pair()
}
#[doc(hidden)]
#[macro_export]
macro_rules! __bits_pairs {
(@acc [$($pairs:tt)*]) => {
[$($pairs)*]
};
(@acc [$($pairs:tt)*] $s:literal ..= $e:literal, $($rest:tt)*) => {
$crate::__bits_pairs!(@acc [$($pairs)* ($s as u8, $e as u8),] $($rest)*)
};
(@acc [$($pairs:tt)*] $s:literal ..= $e:literal) => {
$crate::__bits_pairs!(@acc [$($pairs)* ($s as u8, $e as u8),])
};
(@acc [$($pairs:tt)*] $s:literal .. $e:literal, $($rest:tt)*) => {
$crate::__bits_pairs!(
@acc [$($pairs)* $crate::mask::__half_open_pair($s as u8, $e as u8),]
$($rest)*
)
};
(@acc [$($pairs:tt)*] $s:literal .. $e:literal) => {
$crate::__bits_pairs!(
@acc [$($pairs)* $crate::mask::__half_open_pair($s as u8, $e as u8),]
)
};
(@acc [$($pairs:tt)*] $bit:literal, $($rest:tt)*) => {
$crate::__bits_pairs!(@acc [$($pairs)* ($bit as u8, $bit as u8),] $($rest)*)
};
(@acc [$($pairs:tt)*] $bit:literal) => {
$crate::__bits_pairs!(@acc [$($pairs)* ($bit as u8, $bit as u8),])
};
(@acc [$($pairs:tt)*] $spec:expr, $($rest:tt)*) => {
$crate::__bits_pairs!(
@acc [$($pairs)* $crate::mask::__bit_spec_pair($spec),]
$($rest)*
)
};
(@acc [$($pairs:tt)*] $spec:expr) => {
$crate::__bits_pairs!(
@acc [$($pairs)* $crate::mask::__bit_spec_pair($spec),]
)
};
(@acc [$($pairs:tt)*] $($rest:tt)+) => {
::core::compile_error!(
"unsupported bit spec: expected integer or range expressions as \
`N`, `N..=M`, or `N..M`, separated by commas"
)
};
($($tokens:tt)*) => {
$crate::__bits_pairs!(@acc [] $($tokens)*)
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __const_mask {
(msb0 $ty:ty; $($specs:tt)*) => {
$crate::mask::msb0_mask(<$ty>::BITS, &$crate::__bits_pairs!($($specs)*)) as $ty
};
(lsb0 $ty:ty; $($specs:tt)*) => {
$crate::mask::lsb0_mask(&$crate::__bits_pairs!($($specs)*)) as $ty
};
}
#[inline]
pub fn extract_mask<T: crate::BitField>(ranges: &[(u8, u8)]) -> T::Storage {
let raw = if T::IS_MSB0 {
msb0_mask(<T::Storage as crate::BitStorage>::BITS, ranges)
} else {
lsb0_mask(ranges)
};
<T::Storage as crate::BitStorage>::from_u128(raw)
}
#[inline]
pub fn extract_bits_auto<T>(val: T, ranges: &[(u8, u8)]) -> T
where
T: crate::BitField,
T: core::ops::BitAnd<T::Storage, Output = T>,
{
val & extract_mask::<T>(ranges)
}
#[inline]
pub fn insert_bits_auto<T>(dst: T, src: T::Storage, ranges: &[(u8, u8)]) -> T
where
T: crate::BitField
+ core::ops::BitAnd<T::Storage, Output = T>
+ core::ops::BitOr<T::Storage, Output = T>,
T::Storage: core::ops::BitAnd<Output = T::Storage> + core::ops::Not<Output = T::Storage>,
{
let mask = extract_mask::<T>(ranges);
(dst & !mask) | (src & mask)
}
#[inline]
pub fn place_bits_auto<T>(dst: T, lo: u8, hi: u8, val: T::Storage) -> T
where
T: crate::BitField
+ core::ops::BitAnd<T::Storage, Output = T>
+ core::ops::BitOr<T::Storage, Output = T>,
T::Storage: core::ops::BitAnd<Output = T::Storage>
+ core::ops::Not<Output = T::Storage>
+ core::ops::Shl<u32, Output = T::Storage>,
{
let shift = if T::IS_MSB0 {
<T::Storage as crate::BitStorage>::BITS - 1 - hi as u32
} else {
lo as u32
};
insert_bits_auto(dst, val << shift, &[(lo, hi)])
}
#[macro_export]
macro_rules! extract_bits {
(msb0 $ty:ty; $val:expr; $($specs:tt)*) => {{
let mask: $ty = $crate::__const_mask!(msb0 $ty; $($specs)*);
($val) & mask
}};
(lsb0 $ty:ty; $val:expr; $($specs:tt)*) => {{
let mask: $ty = $crate::__const_mask!(lsb0 $ty; $($specs)*);
($val) & mask
}};
($val:expr; $($specs:tt)*) => {
$crate::mask::extract_bits_auto($val, &$crate::__bits_pairs!($($specs)*))
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __insert_bits_range_first {
(@msb [$ty:ty] [$dst:expr] [$($specs:tt)*] ; $src:expr) => {{
let mask: $ty = $crate::__const_mask!(msb0 $ty; $($specs)*);
(($dst) & !mask) | (($src) & mask)
}};
(@lsb [$ty:ty] [$dst:expr] [$($specs:tt)*] ; $src:expr) => {{
let mask: $ty = $crate::__const_mask!(lsb0 $ty; $($specs)*);
(($dst) & !mask) | (($src) & mask)
}};
(@auto [$dst:expr] [$($specs:tt)*] ; $src:expr) => {
$crate::mask::insert_bits_auto($dst, ($src as _), &$crate::__bits_pairs!($($specs)*))
};
(@msb [$ty:ty] [$dst:expr] [$($specs:tt)*] $next:tt $($rest:tt)*) => {
$crate::__insert_bits_range_first!(
@msb [$ty] [$dst] [$($specs)* $next] $($rest)*
)
};
(@lsb [$ty:ty] [$dst:expr] [$($specs:tt)*] $next:tt $($rest:tt)*) => {
$crate::__insert_bits_range_first!(
@lsb [$ty] [$dst] [$($specs)* $next] $($rest)*
)
};
(@auto [$dst:expr] [$($specs:tt)*] $next:tt $($rest:tt)*) => {
$crate::__insert_bits_range_first!(
@auto [$dst] [$($specs)* $next] $($rest)*
)
};
}
#[macro_export]
macro_rules! insert_bits {
(msb0 $ty:ty; $dst:expr; $($tail:tt)*) => {
$crate::__insert_bits_range_first!(@msb [$ty] [$dst] [] $($tail)*)
};
(lsb0 $ty:ty; $dst:expr; $($tail:tt)*) => {
$crate::__insert_bits_range_first!(@lsb [$ty] [$dst] [] $($tail)*)
};
($dst:expr; $($tail:tt)*) => {
$crate::__insert_bits_range_first!(@auto [$dst] [] $($tail)*)
};
}
#[macro_export]
macro_rules! place_bits {
(msb0 $ty:ty; $dst:expr; $lo:literal ..= $hi:literal; $val:expr) => {{
const MASK: $ty = $crate::__const_mask!(msb0 $ty; $lo ..= $hi);
const SHIFT: u32 = <$ty>::BITS - 1 - ($hi as u32);
(($dst) & !MASK) | ((($val as $ty) << SHIFT) & MASK)
}};
(lsb0 $ty:ty; $dst:expr; $lo:literal ..= $hi:literal; $val:expr) => {{
const MASK: $ty = $crate::__const_mask!(lsb0 $ty; $lo ..= $hi);
const SHIFT: u32 = $lo as u32;
(($dst) & !MASK) | ((($val as $ty) << SHIFT) & MASK)
}};
($dst:expr; $lo:literal ..= $hi:literal; $val:expr) => {
$crate::mask::place_bits_auto($dst, $lo, $hi, ($val as _))
};
(msb0 $ty:ty; $dst:expr; $lo:literal .. $hi:literal; $val:expr) => {{
let (lo, hi) = $crate::mask::__half_open_pair($lo as u8, $hi as u8);
if lo > hi {
$dst
} else {
let mask: $ty = $crate::mask::msb0_mask(<$ty>::BITS, &[(lo, hi)]) as $ty;
let shift = <$ty>::BITS - 1 - hi as u32;
(($dst) & !mask) | ((($val as $ty) << shift) & mask)
}
}};
(lsb0 $ty:ty; $dst:expr; $lo:literal .. $hi:literal; $val:expr) => {{
let (lo, hi) = $crate::mask::__half_open_pair($lo as u8, $hi as u8);
if lo > hi {
$dst
} else {
let mask: $ty = $crate::mask::lsb0_mask(&[(lo, hi)]) as $ty;
(($dst) & !mask) | ((($val as $ty) << lo as u32) & mask)
}
}};
($dst:expr; $lo:literal .. $hi:literal; $val:expr) => {{
let (lo, hi) = $crate::mask::__half_open_pair($lo as u8, $hi as u8);
if lo > hi {
$dst
} else {
$crate::mask::place_bits_auto($dst, lo, hi, ($val as _))
}
}};
(msb0 $ty:ty; $dst:expr; $bit:literal; $val:expr) => {
$crate::place_bits!(msb0 $ty; $dst; $bit ..= $bit; $val)
};
(lsb0 $ty:ty; $dst:expr; $bit:literal; $val:expr) => {
$crate::place_bits!(lsb0 $ty; $dst; $bit ..= $bit; $val)
};
($dst:expr; $bit:literal; $val:expr) => {
$crate::place_bits!($dst; $bit ..= $bit; $val)
};
(msb0 $ty:ty; $dst:expr; $spec:expr; $val:expr) => {{
let (lo, hi) = $crate::mask::__bit_spec_pair($spec);
if lo > hi {
$dst
} else {
let mask: $ty = $crate::mask::msb0_mask(<$ty>::BITS, &[(lo, hi)]) as $ty;
let shift = <$ty>::BITS - 1 - hi as u32;
(($dst) & !mask) | ((($val as $ty) << shift) & mask)
}
}};
(lsb0 $ty:ty; $dst:expr; $spec:expr; $val:expr) => {{
let (lo, hi) = $crate::mask::__bit_spec_pair($spec);
if lo > hi {
$dst
} else {
let mask: $ty = $crate::mask::lsb0_mask(&[(lo, hi)]) as $ty;
(($dst) & !mask) | ((($val as $ty) << lo as u32) & mask)
}
}};
($dst:expr; $spec:expr; $val:expr) => {{
let (lo, hi) = $crate::mask::__bit_spec_pair($spec);
if lo > hi {
$dst
} else {
$crate::mask::place_bits_auto($dst, lo, hi, ($val as _))
}
}};
}