use core::ops::{Shl, Shr};
c0nst::c0nst! {
pub c0nst trait UnboundedShl: Sized + [c0nst] Shl<u32> {
fn unbounded_shl(self, rhs: u32) -> <Self as Shl<u32>>::Output;
}
}
c0nst::c0nst! {
pub c0nst trait UnboundedShr: Sized + [c0nst] Shr<u32> {
fn unbounded_shr(self, rhs: u32) -> <Self as Shr<u32>>::Output;
}
}
macro_rules! unbounded_shift_impl {
(unsigned $($t:ty)*) => {$(
c0nst::c0nst! {
c0nst impl UnboundedShl for $t {
#[inline]
fn unbounded_shl(self, rhs: u32) -> Self {
if rhs < <$t>::BITS { self << rhs } else { 0 }
}
}
}
c0nst::c0nst! {
c0nst impl UnboundedShr for $t {
#[inline]
fn unbounded_shr(self, rhs: u32) -> Self {
if rhs < <$t>::BITS { self >> rhs } else { 0 }
}
}
}
)*};
(signed $($t:ty)*) => {$(
c0nst::c0nst! {
c0nst impl UnboundedShl for $t {
#[inline]
fn unbounded_shl(self, rhs: u32) -> Self {
if rhs < <$t>::BITS { self << rhs } else { 0 }
}
}
}
c0nst::c0nst! {
c0nst impl UnboundedShr for $t {
#[inline]
fn unbounded_shr(self, rhs: u32) -> Self {
if rhs < <$t>::BITS {
self >> rhs
} else {
self >> (<$t>::BITS - 1)
}
}
}
}
)*};
}
unbounded_shift_impl!(unsigned usize u8 u16 u32 u64 u128);
unbounded_shift_impl!(signed isize i8 i16 i32 i64 i128);
c0nst::c0nst! {
pub c0nst trait FunnelShl: Sized {
type Output;
fn funnel_shl(self, rhs: Self, n: u32) -> Self::Output;
}
}
c0nst::c0nst! {
pub c0nst trait FunnelShr: Sized {
type Output;
fn funnel_shr(self, rhs: Self, n: u32) -> Self::Output;
}
}
macro_rules! funnel_shift_impl {
($($t:ty)*) => {$(
c0nst::c0nst! {
c0nst impl FunnelShl for $t {
type Output = $t;
#[inline]
#[track_caller]
fn funnel_shl(self, rhs: Self, n: u32) -> Self {
assert!(n < <$t>::BITS, "attempt to funnel shift left with overflow");
if n == 0 {
self
} else {
(self << n) | (rhs >> (<$t>::BITS - n))
}
}
}
}
c0nst::c0nst! {
c0nst impl FunnelShr for $t {
type Output = $t;
#[inline]
#[track_caller]
fn funnel_shr(self, rhs: Self, n: u32) -> Self {
assert!(n < <$t>::BITS, "attempt to funnel shift right with overflow");
if n == 0 {
rhs
} else {
(rhs >> n) | (self << (<$t>::BITS - n))
}
}
}
}
)*};
}
funnel_shift_impl!(usize u8 u16 u32 u64 u128);
c0nst::c0nst! {
pub c0nst trait ShlExact: Sized + [c0nst] Shl<u32> {
fn shl_exact(self, rhs: u32) -> Option<<Self as Shl<u32>>::Output>;
}
}
c0nst::c0nst! {
pub c0nst trait ShrExact: Sized + [c0nst] Shr<u32> {
fn shr_exact(self, rhs: u32) -> Option<<Self as Shr<u32>>::Output>;
}
}
macro_rules! exact_shift_impl {
(unsigned $($t:ty)*) => {$(
c0nst::c0nst! {
c0nst impl ShlExact for $t {
#[inline]
fn shl_exact(self, rhs: u32) -> Option<Self> {
if rhs <= <$t>::leading_zeros(self) && rhs < <$t>::BITS {
Some(self << rhs)
} else {
None
}
}
}
}
exact_shift_impl!(@shr $t);
)*};
(signed $($t:ty)*) => {$(
c0nst::c0nst! {
c0nst impl ShlExact for $t {
#[inline]
fn shl_exact(self, rhs: u32) -> Option<Self> {
if rhs < <$t>::leading_zeros(self) || rhs < <$t>::leading_ones(self) {
Some(self << rhs)
} else {
None
}
}
}
}
exact_shift_impl!(@shr $t);
)*};
(@shr $t:ty) => {
c0nst::c0nst! {
c0nst impl ShrExact for $t {
#[inline]
fn shr_exact(self, rhs: u32) -> Option<Self> {
if rhs <= <$t>::trailing_zeros(self) && rhs < <$t>::BITS {
Some(self >> rhs)
} else {
None
}
}
}
}
};
}
exact_shift_impl!(unsigned usize u8 u16 u32 u64 u128);
exact_shift_impl!(signed isize i8 i16 i32 i64 i128);
c0nst::c0nst! {
pub c0nst trait HighestOne: Sized {
fn highest_one(self) -> Option<u32>;
}
}
c0nst::c0nst! {
pub c0nst trait LowestOne: Sized {
fn lowest_one(self) -> Option<u32>;
}
}
c0nst::c0nst! {
pub c0nst trait IsolateHighestOne: Sized {
type Output;
fn isolate_highest_one(self) -> Self::Output;
}
}
c0nst::c0nst! {
pub c0nst trait IsolateLowestOne: Sized {
type Output;
fn isolate_lowest_one(self) -> Self::Output;
}
}
macro_rules! isolate_one_impl {
($($t:ty => $u:ty;)*) => {$(
c0nst::c0nst! {
c0nst impl HighestOne for $t {
#[inline]
fn highest_one(self) -> Option<u32> {
if self == 0 {
None
} else {
Some(<$t>::BITS - 1 - <$t>::leading_zeros(self))
}
}
}
}
c0nst::c0nst! {
c0nst impl LowestOne for $t {
#[inline]
fn lowest_one(self) -> Option<u32> {
if self == 0 {
None
} else {
Some(<$t>::trailing_zeros(self))
}
}
}
}
c0nst::c0nst! {
c0nst impl IsolateHighestOne for $t {
type Output = $t;
#[inline]
fn isolate_highest_one(self) -> Self {
let bits = self as $u;
(bits & ((1 as $u) << (<$u>::BITS - 1)).wrapping_shr(<$u>::leading_zeros(bits))) as $t
}
}
}
c0nst::c0nst! {
c0nst impl IsolateLowestOne for $t {
type Output = $t;
#[inline]
fn isolate_lowest_one(self) -> Self {
self & <$t>::wrapping_neg(self)
}
}
}
)*};
}
isolate_one_impl! {
u8 => u8; u16 => u16; u32 => u32; u64 => u64; usize => usize; u128 => u128;
i8 => u8; i16 => u16; i32 => u32; i64 => u64; isize => usize; i128 => u128;
}
c0nst::c0nst! {
pub c0nst trait BitWidth: Sized {
fn bit_width(self) -> u32;
}
}
macro_rules! bit_width_impl {
($($t:ty)*) => {$(
c0nst::c0nst! {
c0nst impl BitWidth for $t {
#[inline]
fn bit_width(self) -> u32 {
<$t>::BITS - <$t>::leading_zeros(self)
}
}
}
)*};
}
bit_width_impl!(usize u8 u16 u32 u64 u128);
c0nst::c0nst! {
pub c0nst trait DepositBits: Sized {
type Output;
fn deposit_bits(self, mask: Self) -> Self::Output;
}
}
c0nst::c0nst! {
pub c0nst trait ExtractBits: Sized {
type Output;
fn extract_bits(self, mask: Self) -> Self::Output;
}
}
macro_rules! deposit_extract_impl {
($($t:ty)*) => {$(
c0nst::c0nst! {
c0nst impl DepositBits for $t {
type Output = $t;
#[inline]
fn deposit_bits(self, mask: Self) -> Self {
let mut result: $t = 0;
let mut remaining = mask;
let mut bb: $t = 1;
while remaining != 0 {
let lowest = remaining & <$t>::wrapping_neg(remaining);
let bit_mask = (((self & bb) != 0) as $t).wrapping_neg();
result |= lowest & bit_mask;
remaining &= remaining - 1;
bb = <$t>::wrapping_shl(bb, 1);
}
result
}
}
}
c0nst::c0nst! {
c0nst impl ExtractBits for $t {
type Output = $t;
#[inline]
fn extract_bits(self, mask: Self) -> Self {
let mut result: $t = 0;
let mut remaining = mask;
let mut bb: $t = 1;
while remaining != 0 {
let lowest = remaining & <$t>::wrapping_neg(remaining);
let bit_mask = (((self & lowest) != 0) as $t).wrapping_neg();
result |= bb & bit_mask;
remaining &= remaining - 1;
bb = <$t>::wrapping_shl(bb, 1);
}
result
}
}
}
)*};
}
deposit_extract_impl!(usize u8 u16 u32 u64 u128);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unbounded_shifts() {
assert_eq!(UnboundedShl::unbounded_shl(1u8, 7), 0x80);
assert_eq!(UnboundedShl::unbounded_shl(1u8, 8), 0);
assert_eq!(UnboundedShr::unbounded_shr(0x80u8, 8), 0);
assert_eq!(UnboundedShr::unbounded_shr(-1i8, 100), -1);
assert_eq!(UnboundedShr::unbounded_shr(i8::MAX, 100), 0);
}
#[test]
fn funnel_shifts() {
assert_eq!(FunnelShl::funnel_shl(0x01u8, 0x80, 1), 0x03);
assert_eq!(FunnelShl::funnel_shl(0xABu8, 0xCD, 0), 0xAB);
assert_eq!(FunnelShr::funnel_shr(0x01u8, 0x80, 1), 0xC0);
assert_eq!(FunnelShr::funnel_shr(0xABu8, 0xCD, 0), 0xCD);
assert_eq!(
FunnelShl::funnel_shl(0x81u8, 0x81, 1),
0x81u8.rotate_left(1)
);
}
#[test]
#[should_panic(expected = "attempt to funnel shift left with overflow")]
fn funnel_shl_panics() {
let _ = FunnelShl::funnel_shl(1u8, 1, 8);
}
#[test]
fn exact_shifts() {
assert_eq!(ShlExact::shl_exact(0x11u8, 3), Some(0x88));
assert_eq!(ShlExact::shl_exact(0x11u8, 4), None);
assert_eq!(ShrExact::shr_exact(0x88u8, 3), Some(0x11));
assert_eq!(ShrExact::shr_exact(0x88u8, 4), None);
assert_eq!(ShrExact::shr_exact(0u8, 7), Some(0));
assert_eq!(ShrExact::shr_exact(0u8, 8), None);
assert_eq!(ShlExact::shl_exact(-1i8, 7), Some(i8::MIN));
assert_eq!(ShlExact::shl_exact(-2i8, 6), Some(i8::MIN));
assert_eq!(ShlExact::shl_exact(-2i8, 7), None);
assert_eq!(ShlExact::shl_exact(1i8, 6), Some(64));
assert_eq!(ShlExact::shl_exact(1i8, 7), None);
}
#[test]
fn isolate() {
assert_eq!(HighestOne::highest_one(0b0101_0000u8), Some(6));
assert_eq!(HighestOne::highest_one(0u8), None);
assert_eq!(LowestOne::lowest_one(0b0101_0000u8), Some(4));
assert_eq!(LowestOne::lowest_one(0i64), None);
assert_eq!(
IsolateHighestOne::isolate_highest_one(0b0101_0000u8),
0b0100_0000
);
assert_eq!(
IsolateLowestOne::isolate_lowest_one(0b0101_0000u8),
0b0001_0000
);
assert_eq!(IsolateHighestOne::isolate_highest_one(0u8), 0);
assert_eq!(IsolateLowestOne::isolate_lowest_one(0u8), 0);
assert_eq!(HighestOne::highest_one(-1i8), Some(7));
assert_eq!(IsolateHighestOne::isolate_highest_one(-1i8), i8::MIN);
assert_eq!(IsolateLowestOne::isolate_lowest_one(-2i8), 2);
}
#[test]
fn bit_width() {
assert_eq!(BitWidth::bit_width(0u8), 0);
assert_eq!(BitWidth::bit_width(1u8), 1);
assert_eq!(BitWidth::bit_width(255u8), 8);
assert_eq!(BitWidth::bit_width(0x0101u16), 9);
}
#[test]
fn deposit_extract() {
assert_eq!(DepositBits::deposit_bits(0b101u8, 0b1111_0000), 0b0101_0000);
assert_eq!(DepositBits::deposit_bits(0xFFu8, 0b1010_1010), 0b1010_1010);
assert_eq!(ExtractBits::extract_bits(0b0101_0011u8, 0b1111_0000), 0b101);
assert_eq!(ExtractBits::extract_bits(0xFFu8, 0b1010_1010), 0b1111);
let mask = 0b0110_1001u8;
for x in 0u8..16 {
let deposited = DepositBits::deposit_bits(x, mask);
assert_eq!(deposited & !mask, 0);
assert_eq!(ExtractBits::extract_bits(deposited, mask), x);
}
}
}