pub const BITS_IN_BYTE: usize = 8;
pub unsafe trait BitStorage: Sized {
const BITS_IN_STORAGE: usize = std::mem::size_of::<Self>() * BITS_IN_BYTE;
fn empty() -> Self;
fn is_empty(&self) -> bool;
unsafe fn get_bit(&self, index: usize) -> bool;
unsafe fn set_bit(&mut self, index: usize) -> bool;
unsafe fn clear_bit(&mut self, index: usize) -> bool;
fn clear_all(&mut self) {
*self = Self::empty();
}
fn count_ones(&self) -> usize;
fn first_bit_set(&self) -> Option<usize>;
fn last_bit_set(&self) -> Option<usize>;
}
macro_rules! bit_storage_integer_impl {
($int: ty, $doc: expr) => {
#[doc = $doc]
unsafe impl BitStorage for $int {
#[inline(always)]
fn empty() -> Self { 0 }
#[inline(always)]
fn is_empty(&self) -> bool {
*self == Self::empty()
}
unsafe fn get_bit(&self, index: usize) -> bool {
let mask = 1 << index;
(*self & mask) != 0
}
unsafe fn set_bit(&mut self, index: usize) -> bool {
let mask = 1 << index;
let prev = *self & mask;
*self |= mask;
prev == 0
}
unsafe fn clear_bit(&mut self, index: usize) -> bool {
let mask = 1 << index;
let prev = *self & mask;
*self &= !mask;
prev != 0
}
#[inline(always)]
fn count_ones(&self) -> usize {
<$int>::count_ones(*self) as usize
}
fn first_bit_set(&self) -> Option<usize> {
if *self == Self::empty() {
None
} else {
let mut mask = 1;
for idx in 0..Self::BITS_IN_STORAGE {
if *self & mask != 0 { return Some(idx); }
mask <<= 1;
}
unreachable!()
}
}
fn last_bit_set(&self) -> Option<usize> {
if *self == Self::empty() {
None
} else {
let mut mask = 1 << (Self::BITS_IN_STORAGE - 1);
for idx in (0..=(Self::BITS_IN_STORAGE - 1)).rev() {
if *self & mask != 0 { return Some(idx); }
mask >>= 1;
}
unreachable!()
}
}
}
};
($int: ty) => {
bit_storage_integer_impl! {
$int,
concat!(
"SAFETY: this implementation is safe because the width in ",
"bits of an `",
stringify!($int),
"` is fixed and equal to `std::mem::size_of::<",
stringify!($int),
">() * 8`.",
)
}
};
}
bit_storage_integer_impl! { u8 }
bit_storage_integer_impl! { u16 }
bit_storage_integer_impl! { u32 }
bit_storage_integer_impl! { u64 }
bit_storage_integer_impl! { u128 }
macro_rules! bit_storage_integer_tests {
($(($int: ty, $mod_name: ident))*) => {
$(
#[cfg(test)]
mod $mod_name {
use super::*;
#[test]
fn empty() {
assert_eq!(<$int>::empty(), 0);
}
#[test]
fn is_empty() {
assert!(<$int>::empty().is_empty());
let v: $int = 3;
assert!(!v.is_empty());
}
#[test]
fn get_bit() {
let v = <$int>::empty();
assert!(unsafe { !v.get_bit(0) });
assert!(unsafe { !v.get_bit(7) });
let v: $int = 1 << 3;
assert!(unsafe { !v.get_bit(0) });
assert!(unsafe { v.get_bit(3) });
}
#[test]
fn set_bit() {
let mut v = <$int>::empty();
assert!(unsafe { v.set_bit(0) });
assert!(unsafe { v.get_bit(0) });
assert!(unsafe { v.set_bit(7) });
assert!(unsafe { v.get_bit(7) });
assert!(unsafe { !v.set_bit(0) });
}
#[test]
fn clear_bit() {
let mut v = <$int>::empty();
assert!(unsafe { !v.clear_bit(0) });
assert!(unsafe { v.set_bit(0) });
assert!(unsafe { v.clear_bit(0) });
assert!(unsafe { !v.get_bit(0) });
}
#[test]
fn clear_all() {
let mut v: $int = 42;
v.clear_all();
assert!(v.is_empty());
let mut v: $int = 1 << 3;
v.clear_all();
assert!(v.is_empty());
}
#[test]
fn count_ones() {
let v = <$int>::empty();
assert_eq!(v.count_ones(), 0);
let mut v = v;
unsafe { v.set_bit(0); }
assert_eq!(v.count_ones(), 1);
unsafe { v.set_bit(3); }
assert_eq!(v.count_ones(), 2);
unsafe { v.set_bit(7); }
assert_eq!(v.count_ones(), 3);
unsafe { v.clear_bit(4); }
assert_eq!(v.count_ones(), 3);
unsafe { v.clear_bit(7); }
assert_eq!(v.count_ones(), 2);
v.clear_all();
assert_eq!(v.count_ones(), 0);
}
#[test]
fn first_bit_set() {
let v = <$int>::empty();
assert_eq!(v.first_bit_set(), None);
let mut v = v;
unsafe { v.set_bit(3); }
assert_eq!(v.first_bit_set(), Some(3));
unsafe { v.set_bit(7); }
assert_eq!(v.first_bit_set(), Some(3));
unsafe { v.set_bit(1); }
assert_eq!(v.first_bit_set(), Some(1));
}
#[test]
fn last_bit_set() {
let v = <$int>::empty();
assert_eq!(v.last_bit_set(), None);
let mut v = v;
unsafe { v.set_bit(3); }
assert_eq!(v.last_bit_set(), Some(3));
unsafe { v.set_bit(1); }
assert_eq!(v.last_bit_set(), Some(3));
unsafe { v.set_bit(7); }
assert_eq!(v.last_bit_set(), Some(7));
}
}
)*
}
}
bit_storage_integer_tests! {
(u8, test_u8)
(u16, test_u16)
(u32, test_u32)
(u64, test_u64)
(u128, test_u128)
}