#![cfg_attr(feature = "const_impl", feature(const_trait_impl))]
#![no_std]
#[cfg_attr(feature = "const_impl", const_trait)]
pub trait BitUtils: Sized + Copy {
fn bit(self, index: u8) -> bool;
fn try_bit(self, index: u8) -> Option<bool>;
#[must_use = "with_bit returns a new value instead of modifying the original"]
fn with_bit(self, index: u8, value: bool) -> Self;
#[must_use = "with_bit returns a new value instead of modifying the original"]
fn try_with_bit(self, index: u8, value: bool) -> Option<Self>;
fn bits(self, start: u8, end: u8) -> Self;
fn try_bits(self, start: u8, end: u8) -> Option<Self>;
#[must_use = "with_bits returns a new value instead of modifying the original"]
fn with_bits(self, start: u8, end: u8, value: Self) -> Self;
#[must_use = "with_bits returns a new value instead of modifying the original"]
fn try_with_bits(self, start: u8, end: u8, value: Self) -> Option<Self>;
}
#[cold]
#[inline(always)]
const fn cold() {}
macro_rules! impl_bit_utils {
(inner; $($constness:ident)? => $t:ty) => {
impl $($constness)? BitUtils for $t {
#[inline(always)]
fn bit(self, index: u8) -> bool {
let shifted = self.unbounded_shr(index as u32);
(shifted & 1) > 0
}
#[inline(always)]
fn try_bit(self, index: u8) -> Option<bool> {
let shifted = self.checked_shr(index as u32);
match shifted {
Some(x) => Some((x & 1) > 0),
None => {
cold();
None
}
}
}
#[inline(always)]
fn with_bit(self, index: u8, value: bool) -> Self {
const ONE: $t = 1;
if index >= Self::BITS as u8 {
cold();
return self;
}
let shifted_one = ONE << (index as usize);
let shifted_value = (value as Self) << (index as usize);
(self & !shifted_one) | shifted_value
}
#[inline(always)]
fn try_with_bit(self, index: u8, value: bool) -> Option<Self> {
const ONE: $t = 1;
if index >= Self::BITS as u8 {
cold();
return None;
}
let shifted_one = ONE << (index as usize);
let shifted_value = (value as Self) << (index as usize);
Some((self & !shifted_one) | shifted_value)
}
#[inline(always)]
fn bits(self, start: u8, end: u8) -> Self {
const ONE: $t = 1;
if start >= Self::BITS as u8 || end < start {
cold();
return 0;
}
let len = (end - start) as u32;
let mask = ONE.unbounded_shl(len).wrapping_sub(ONE);
let shifted_value = self >> (start as usize);
let result = shifted_value & mask;
unsafe { core::hint::assert_unchecked(result <= mask) };
result
}
#[inline(always)]
fn try_bits(self, start: u8, end: u8) -> Option<Self> {
const ONE: $t = 1;
if start >= Self::BITS as u8 || end > Self::BITS as u8 || end <= start {
cold();
return None;
}
let len = (end - start) as u32;
let mask = ONE.unbounded_shl(len).wrapping_sub(ONE);
let shifted_value = self >> (start as usize);
let result = shifted_value & mask;
unsafe { core::hint::assert_unchecked(result <= mask) };
Some(result)
}
#[inline(always)]
fn with_bits(self, start: u8, end: u8, value: Self) -> Self {
const ONE: $t = 1;
if start >= Self::BITS as u8 || end <= start {
cold();
return self;
}
let len = (end - start) as u32;
let mask = ONE.unbounded_shl(len).wrapping_sub(ONE);
let value = value & mask;
let shifted_mask = mask << (start as usize);
let shifted_value = value << (start as usize);
(self & !shifted_mask) | shifted_value
}
#[inline(always)]
fn try_with_bits(self, start: u8, end: u8, value: Self) -> Option<Self> {
const ONE: $t = 1;
if start >= Self::BITS as u8 || end > Self::BITS as u8 || end <= start {
cold();
return None;
}
let len = (end - start) as u32;
let mask = ONE.unbounded_shl(len).wrapping_sub(ONE);
let value = value & mask;
let shifted_mask = mask << (start as usize);
let shifted_value = value << (start as usize);
Some((self & !shifted_mask) | shifted_value)
}
}
};
($($t:ty),*) => {
$(
impl_bit_utils!(inner; => $t);
)*
};
(const => $($t:ty),*) => {
$(
impl_bit_utils!(inner; const => $t);
)*
};
}
#[cfg(not(feature = "const_impl"))]
impl_bit_utils!(u8, u16, u32, u64, i8, i16, i32, i64, i128);
#[cfg(feature = "const_impl")]
impl_bit_utils!(const => u8, u16, u32, u64, i8, i16, i32, i64, u128);
#[cfg(test)]
mod test {
use super::BitUtils;
const A: u8 = 0b0110_1010;
const B: u16 = 0b1000_1100_1111_0001;
#[test]
fn test_bit() {
assert!(!A.bit(0));
assert!(A.bit(1));
assert!(!A.bit(2));
assert!(A.bit(3));
assert!(!A.bit(4));
assert!(A.bit(5));
assert!(A.bit(6));
assert!(!A.bit(7));
for i in 8..255u8 {
assert!(!A.bit(i));
}
}
#[test]
fn test_try_bit() {
assert!(!A.try_bit(0).unwrap());
assert!(A.try_bit(1).unwrap());
assert!(!A.try_bit(2).unwrap());
assert!(A.try_bit(3).unwrap());
assert!(!A.try_bit(4).unwrap());
assert!(A.try_bit(5).unwrap());
assert!(A.try_bit(6).unwrap());
assert!(!A.try_bit(7).unwrap());
for i in 8..255u8 {
assert!(A.try_bit(i).is_none());
}
}
#[test]
fn test_with_bit() {
assert_eq!(B.with_bit(0, false), 0b1000_1100_1111_0000);
assert_eq!(B.with_bit(3, true), 0b1000_1100_1111_1001);
assert_eq!(B.with_bit(15, false), 0b0000_1100_1111_0001);
for i in 16..255u8 {
assert_eq!(B.with_bit(i, true), B);
}
}
#[test]
fn test_try_with_bit() {
assert_eq!(B.try_with_bit(0, false).unwrap(), 0b1000_1100_1111_0000);
assert_eq!(B.try_with_bit(3, true).unwrap(), 0b1000_1100_1111_1001);
assert_eq!(B.try_with_bit(15, false).unwrap(), 0b0000_1100_1111_0001);
for i in 16..255u8 {
assert!(B.try_with_bit(i, true).is_none());
}
}
#[test]
fn test_bits() {
assert_eq!(A.bits(0, 4), 0b1010);
assert_eq!(A.bits(0, 8), 0b0110_1010);
assert_eq!(A.bits(4, 8), 0b0110);
assert_eq!(A.bits(3, 7), 0b1101);
assert_eq!(A.bits(5, 38), 0b11);
for start in 8..255u8 {
for end in start..255u8 {
assert_eq!(A.bits(start, end), 0);
}
}
}
#[test]
fn test_try_bits() {
assert_eq!(A.try_bits(0, 4).unwrap(), 0b1010);
assert_eq!(A.try_bits(0, 8).unwrap(), 0b0110_1010);
assert_eq!(A.try_bits(4, 8).unwrap(), 0b0110);
assert_eq!(A.try_bits(3, 7).unwrap(), 0b1101);
assert!(A.try_bits(8, 9).is_none());
assert!(A.try_bits(5, 9).is_none());
assert!(A.try_bits(5, 38).is_none());
for start in 8..255u8 {
for end in start..255u8 {
assert!(A.try_bits(start, end).is_none());
}
}
}
#[test]
fn test_with_bits() {
assert_eq!(A.with_bits(0, 3, 0b111), 0b0110_1111);
assert_eq!(A.with_bits(2, 6, 0b0011), 0b0100_1110);
assert_eq!(A.with_bits(2, 6, 0b1111_0011), 0b0100_1110);
assert_eq!(A.with_bits(8, 10, 0b10101010), A);
assert_eq!(A.with_bits(3, 1, 0b10101010), A);
}
#[test]
fn test_try_with_bits() {
assert_eq!(A.try_with_bits(0, 3, 0b111).unwrap(), 0b0110_1111);
assert_eq!(A.try_with_bits(2, 6, 0b0011).unwrap(), 0b0100_1110);
assert_eq!(A.try_with_bits(2, 6, 0b1111_0011).unwrap(), 0b0100_1110);
assert!(A.try_with_bits(2, 8, 0b1111_0011).is_some());
assert!(A.try_with_bits(2, 9, 0b1111_0011).is_none());
assert!(A.try_with_bits(8, 10, 0b10101010).is_none());
assert!(A.try_with_bits(3, 1, 0b10101010).is_none());
}
}