#![doc = include_str!("../doc/order.md")]
use crate::{
index::{
BitEnd,
BitIdx,
BitMask,
BitPos,
BitSel,
},
mem::{
bits_of,
BitRegister,
},
};
#[doc = include_str!("../doc/order/BitOrder.md")]
pub unsafe trait BitOrder: 'static {
fn at<R>(index: BitIdx<R>) -> BitPos<R>
where R: BitRegister;
#[inline]
fn select<R>(index: BitIdx<R>) -> BitSel<R>
where R: BitRegister {
Self::at::<R>(index).select()
}
#[inline]
fn mask<R>(
from: impl Into<Option<BitIdx<R>>>,
upto: impl Into<Option<BitEnd<R>>>,
) -> BitMask<R>
where
R: BitRegister,
{
let (from, upto) = match (from.into(), upto.into()) {
(None, None) => return BitMask::ALL,
(Some(from), None) => (from, BitEnd::MAX),
(None, Some(upto)) => (BitIdx::MIN, upto),
(Some(from), Some(upto)) => (from, upto),
};
from.range(upto).map(Self::select::<R>).sum()
}
}
#[doc = include_str!("../doc/order/Lsb0.md")]
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Lsb0;
#[doc = include_str!("../doc/order/Msb0.md")]
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Msb0;
unsafe impl BitOrder for Lsb0 {
#[inline]
fn at<R>(index: BitIdx<R>) -> BitPos<R>
where R: BitRegister {
unsafe { BitPos::new_unchecked(index.into_inner()) }
}
#[inline]
fn select<R>(index: BitIdx<R>) -> BitSel<R>
where R: BitRegister {
unsafe { BitSel::new_unchecked(R::ONE << index.into_inner()) }
}
#[inline]
fn mask<R>(
from: impl Into<Option<BitIdx<R>>>,
upto: impl Into<Option<BitEnd<R>>>,
) -> BitMask<R>
where
R: BitRegister,
{
let from = from.into().unwrap_or(BitIdx::MIN).into_inner();
let upto = upto.into().unwrap_or(BitEnd::MAX).into_inner();
debug_assert!(
from <= upto,
"Ranges must run from low index ({}) to high ({})",
from,
upto,
);
let ct = upto - from;
if ct == bits_of::<R>() as u8 {
return BitMask::ALL;
}
BitMask::new(!(R::ALL << ct) << from)
}
}
unsafe impl BitOrder for Msb0 {
#[inline]
fn at<R>(index: BitIdx<R>) -> BitPos<R>
where R: BitRegister {
unsafe { BitPos::new_unchecked(R::MASK - index.into_inner()) }
}
#[inline]
fn select<R>(index: BitIdx<R>) -> BitSel<R>
where R: BitRegister {
let msbit: R = R::ONE << R::MASK;
unsafe { BitSel::new_unchecked(msbit >> index.into_inner()) }
}
#[inline]
fn mask<R>(
from: impl Into<Option<BitIdx<R>>>,
upto: impl Into<Option<BitEnd<R>>>,
) -> BitMask<R>
where
R: BitRegister,
{
let from = from.into().unwrap_or(BitIdx::MIN).into_inner();
let upto = upto.into().unwrap_or(BitEnd::MAX).into_inner();
debug_assert!(
from <= upto,
"ranges must run from low index ({}) to high ({})",
from,
upto,
);
let ct = upto - from;
if ct == bits_of::<R>() as u8 {
return BitMask::ALL;
}
BitMask::new(!(R::ALL >> ct) >> from)
}
}
#[cfg(target_endian = "little")]
#[doc = include_str!("../doc/order/LocalBits.md")]
pub use self::Lsb0 as LocalBits;
#[cfg(target_endian = "big")]
#[doc = include_str!("../doc/order/LocalBits.md")]
pub use self::Msb0 as LocalBits;
#[cfg(not(any(target_endian = "big", target_endian = "little")))]
compile_fail!(
"This architecture is not supported! Please consider filing an issue"
);
#[inline]
#[cfg(not(tarpaulin_include))]
#[doc = include_str!("../doc/order/verify.md")]
pub fn verify<O>(verbose: bool)
where O: BitOrder {
verify_for_type::<u8, O>(verbose);
verify_for_type::<u16, O>(verbose);
verify_for_type::<u32, O>(verbose);
verify_for_type::<usize, O>(verbose);
#[cfg(target_pointer_width = "64")]
verify_for_type::<u64, O>(verbose);
}
#[cfg(miri)]
pub fn verify_for_type<R, O>(_: bool)
where
R: BitRegister,
O: BitOrder,
{
}
#[cfg(not(miri))]
#[doc = include_str!("../doc/order/verify_for_type.md")]
pub fn verify_for_type<R, O>(verbose: bool)
where
R: BitRegister,
O: BitOrder,
{
use core::any::type_name;
let mut accum = BitMask::<R>::ZERO;
let ord_name = type_name::<O>();
let reg_name = type_name::<R>();
for n in 0 .. bits_of::<R>() 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 {}",
ord_name,
reg_name,
n,
pos.into_inner(),
);
}
assert!(
pos.into_inner() < bits_of::<R>() as u8,
"Error when verifying the implementation of `BitOrder` for `{}`: \
Index {} produces a bit position ({}) that exceeds the type width \
{}",
ord_name,
n,
pos.into_inner(),
bits_of::<R>(),
);
let sel = O::select::<R>(idx);
if verbose {
#[cfg(feature = "std")]
println!(
"`<{} as BitOrder>::select::<{}>({})` produces {:b}",
ord_name, reg_name, n, sel,
);
}
assert_eq!(
sel.into_inner().count_ones(),
1,
"Error when verifying the implementation of `BitOrder` for `{}`: \
Index {} produces a bit selector ({:b}) that is not a one-hot mask",
ord_name,
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})",
ord_name,
n,
sel,
pos.into_inner(),
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",
ord_name,
n,
pos.into_inner(),
);
accum.insert(sel);
if verbose {
#[cfg(feature = "std")]
println!(
"`<{} as BitOrder>::at::<{}>({})` accumulates {:b}",
ord_name, reg_name, 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}",
ord_name,
accum,
);
for from in BitIdx::<R>::range_all() {
for upto in BitEnd::<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 = ord_name,
m = reg_name,
f = from,
u = upto,
bad = mask,
good = check,
);
}
}
}
#[cfg(test)]
pub struct HiLo;
#[cfg(test)]
unsafe impl BitOrder for HiLo {
fn at<R>(index: BitIdx<R>) -> BitPos<R>
where R: BitRegister {
BitPos::new(index.into_inner() ^ 4).unwrap()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_impl() {
assert_eq!(Lsb0::mask(None, None), BitMask::<u8>::ALL);
assert_eq!(Msb0::mask(None, None), BitMask::<u8>::ALL);
assert_eq!(HiLo::mask(None, None), BitMask::<u8>::ALL);
assert_eq!(
HiLo::mask(None, BitEnd::<u8>::new(3).unwrap()),
BitMask::new(0b0111_0000),
);
assert_eq!(
HiLo::mask(BitIdx::<u8>::new(3).unwrap(), None),
BitMask::new(0b1000_1111),
);
}
mod lsb0 {
use super::*;
#[test]
fn verify_u8() {
verify_for_type::<u8, Lsb0>(cfg!(feature = "verbose"));
}
#[test]
#[cfg(not(tarpaulin))]
fn verify_u16() {
verify_for_type::<u16, Lsb0>(cfg!(feature = "verbose"));
}
#[test]
#[cfg(not(tarpaulin))]
fn verify_u32() {
verify_for_type::<u32, Lsb0>(cfg!(feature = "verbose"));
}
#[test]
#[cfg(all(target_pointer_width = "64", not(tarpaulin)))]
fn verify_u64() {
verify_for_type::<u64, Lsb0>(cfg!(feature = "verbose"));
}
#[test]
#[cfg(not(tarpaulin))]
fn verify_usize() {
verify_for_type::<usize, Lsb0>(cfg!(feature = "verbose"));
}
}
mod msb0 {
use super::*;
#[test]
fn verify_u8() {
verify_for_type::<u8, Msb0>(cfg!(feature = "verbose"));
}
#[test]
#[cfg(not(tarpaulin))]
fn verify_u16() {
verify_for_type::<u16, Msb0>(cfg!(feature = "verbose"));
}
#[test]
#[cfg(not(tarpaulin))]
fn verify_u32() {
verify_for_type::<u32, Msb0>(cfg!(feature = "verbose"));
}
#[test]
#[cfg(all(target_pointer_width = "64", not(tarpaulin)))]
fn verify_u64() {
verify_for_type::<u64, Msb0>(cfg!(feature = "verbose"));
}
#[test]
#[cfg(not(tarpaulin))]
fn verify_usize() {
verify_for_type::<usize, Msb0>(cfg!(feature = "verbose"));
}
}
mod hilo {
use super::*;
#[test]
fn verify_u8() {
verify_for_type::<u8, HiLo>(cfg!(feature = "verbose"));
}
#[test]
#[cfg(not(tarpaulin))]
fn verify_u16() {
verify_for_type::<u16, HiLo>(cfg!(feature = "verbose"));
}
#[test]
#[cfg(not(tarpaulin))]
fn verify_u32() {
verify_for_type::<u32, HiLo>(cfg!(feature = "verbose"));
}
#[test]
#[cfg(all(target_pointer_width = "64", not(tarpaulin)))]
fn verify_u64() {
verify_for_type::<u64, HiLo>(cfg!(feature = "verbose"));
}
#[test]
#[cfg(not(tarpaulin))]
fn verify_usize() {
verify_for_type::<usize, HiLo>(cfg!(feature = "verbose"));
}
}
}