use crate::{
index::{
BitIdx,
BitMask,
BitPos,
BitSel,
BitTail,
},
mem::BitRegister,
};
pub unsafe trait BitOrder: 'static {
fn at<R>(index: BitIdx<R>) -> BitPos<R>
where R: BitRegister;
#[cfg(not(tarpaulin_include))]
fn select<R>(index: BitIdx<R>) -> BitSel<R>
where R: BitRegister {
Self::at::<R>(index).select()
}
fn mask<R>(
from: impl Into<Option<BitIdx<R>>>,
upto: impl Into<Option<BitTail<R>>>,
) -> BitMask<R>
where
R: BitRegister,
{
let (from, upto) = match (from.into(), upto.into()) {
(None, None) => return BitMask::ALL,
(Some(from), None) => (from, BitTail::LAST),
(None, Some(upto)) => (BitIdx::ZERO, upto),
(Some(from), Some(upto)) => (from, upto),
};
from.range(upto).map(Self::select::<R>).sum()
}
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Lsb0;
unsafe impl BitOrder for Lsb0 {
fn at<R>(index: BitIdx<R>) -> BitPos<R>
where R: BitRegister {
unsafe { BitPos::new_unchecked(index.value()) }
}
fn select<R>(index: BitIdx<R>) -> BitSel<R>
where R: BitRegister {
unsafe { BitSel::new_unchecked(R::ONE << index.value()) }
}
fn mask<R>(
from: impl Into<Option<BitIdx<R>>>,
upto: impl Into<Option<BitTail<R>>>,
) -> BitMask<R>
where
R: BitRegister,
{
let from = from.into().unwrap_or(BitIdx::ZERO).value();
let upto = upto.into().unwrap_or(BitTail::LAST).value();
debug_assert!(
from <= upto,
"Ranges must run from low index ({}) to high ({})",
from,
upto
);
let ct = upto - from;
if ct == R::BITS as u8 {
return BitMask::ALL;
}
BitMask::new(!(R::ALL << ct) << from)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Msb0;
unsafe impl BitOrder for Msb0 {
fn at<R>(index: BitIdx<R>) -> BitPos<R>
where R: BitRegister {
unsafe { BitPos::new_unchecked(R::MASK - index.value()) }
}
fn select<R>(index: BitIdx<R>) -> BitSel<R>
where R: BitRegister {
let msbit: R = R::ONE << R::MASK;
unsafe { BitSel::new_unchecked(msbit >> index.value()) }
}
fn mask<R>(
from: impl Into<Option<BitIdx<R>>>,
upto: impl Into<Option<BitTail<R>>>,
) -> BitMask<R>
where
R: BitRegister,
{
let from = from.into().unwrap_or(BitIdx::ZERO).value();
let upto = upto.into().unwrap_or(BitTail::LAST).value();
debug_assert!(
from <= upto,
"Ranges must run from low index ({}) to high ({})",
from,
upto
);
let ct = upto - from;
if ct == R::BITS as u8 {
return BitMask::ALL;
}
BitMask::new(!(R::ALL >> ct) >> from)
}
}
#[cfg(target_endian = "little")]
pub use self::Lsb0 as LocalBits;
#[cfg(target_endian = "big")]
pub use self::Msb0 as LocalBits;
#[cfg(not(any(target_endian = "big", target_endian = "little")))]
compile_fail!(concat!(
"This architecture is currently not supported. File an issue at ",
env!(CARGO_PKG_REPOSITORY)
));
pub fn verify<O>(verbose: bool)
where O: BitOrder {
verify_for_type::<O, u8>(verbose);
verify_for_type::<O, u16>(verbose);
verify_for_type::<O, u32>(verbose);
verify_for_type::<O, usize>(verbose);
#[cfg(target_pointer_width = "64")]
verify_for_type::<O, u64>(verbose);
}
pub fn verify_for_type<O, R>(verbose: bool)
where
O: BitOrder,
R: BitRegister,
{
use core::any::type_name;
let mut accum = BitMask::<R>::ZERO;
let oname = type_name::<O>();
let mname = type_name::<R>();
for n in 0 .. R::BITS as u8 {
let idx = unsafe { BitIdx::<R>::new_unchecked(n) };
let pos = O::at::<R>(idx);
if verbose {
#[cfg(feature = "std")]
println!(
"`<{} as BitOrder>::at::<{}>({})` produces {}",
oname,
mname,
n,
pos.value(),
);
}
assert!(
pos.value() < R::BITS as u8,
"Error when verifying the implementation of `BitOrder` for `{}`: \
Index {} produces a bit position ({}) that exceeds the type width \
{}",
oname,
n,
pos.value(),
R::BITS,
);
let sel = O::select::<R>(idx);
if verbose {
#[cfg(feature = "std")]
println!(
"`<{} as BitOrder>::select::<{}>({})` produces {:b}",
oname, mname, n, sel,
);
}
assert_eq!(
sel.value().count_ones(),
1,
"Error when verifying the implementation of `BitOrder` for `{}`: \
Index {} produces a bit selector ({:b}) that is not a one-hot mask",
oname,
n,
sel,
);
let shl = pos.select();
assert_eq!(
sel,
shl,
"Error when verifying the implementation of `BitOrder` for `{}`: \
Index {} produces a bit selector ({:b}) that is not equal to `1 \
<< {}` ({:b})",
oname,
n,
sel,
pos.value(),
shl,
);
assert!(
!accum.test(sel),
"Error when verifying the implementation of `BitOrder` for `{}`: \
Index {} produces a bit position ({}) that has already been \
produced by a prior index",
oname,
n,
pos.value(),
);
accum.insert(sel);
if verbose {
#[cfg(feature = "std")]
println!(
"`<{} as BitOrder>::at::<{}>({})` accumulates {:b}",
oname, mname, n, accum,
);
}
}
assert_eq!(
accum,
BitMask::ALL,
"Error when verifying the implementation of `BitOrder` for `{}`: The \
bit positions marked with a `0` here were never produced from an \
index, despite all possible indices being passed in for translation: \
{:b}",
oname,
accum,
);
for from in BitIdx::<R>::range_all() {
for upto in BitTail::<R>::range_from(from) {
let mask = O::mask(from, upto);
let check = from
.range(upto)
.map(O::at)
.map(BitPos::select)
.sum::<BitMask<R>>();
assert_eq!(
mask,
check,
"Error when verifying the implementation of `BitOrder` for \
`{o}`: `{o}::mask::<{m}>({f}, {u})` produced {bad:b}, but \
expected {good:b}",
o = oname,
m = mname,
f = from,
u = upto,
bad = mask,
good = check,
);
}
}
}
#[cfg(all(test, not(miri)))]
mod tests {
use super::*;
#[test]
fn verify_impls() {
verify::<Lsb0>(cfg!(feature = "testing"));
verify::<Msb0>(cfg!(feature = "testing"));
}
}