use crate::const_utils::low_bit_mask_u64;
use std::{
fmt::{self, Debug},
iter::ExactSizeIterator,
marker::PhantomData,
};
#[cfg(all(test, not(feature = "only_new_tests")))]
mod tests;
#[must_use = "BitArray64 is returned by value by every mutating method."]
#[derive(StableAbi, PartialEq, Eq)]
#[repr(transparent)]
pub struct BitArray64<E> {
bits: u64,
_marker: PhantomData<E>,
}
impl<E> Copy for BitArray64<E> {}
impl<E> Clone for BitArray64<E> {
fn clone(&self) -> Self {
Self {
bits: self.bits,
_marker: PhantomData,
}
}
}
impl<E> BitArray64<E> {
#[inline]
pub const fn with_count(count: usize) -> Self
where
E: BooleanEnum,
{
Self {
bits: low_bit_mask_u64(count as u32),
_marker: PhantomData,
}
}
#[inline]
pub const fn from_u64(bits: u64) -> Self {
Self {
bits,
_marker: PhantomData,
}
}
#[inline]
pub const fn empty() -> Self {
Self {
bits: 0,
_marker: PhantomData,
}
}
}
impl<E> BitArray64<E> {
pub const fn at(self, index: usize) -> E
where
E: BooleanEnum,
{
Self::assert_index(index);
bool_to_enum((self.bits & (1u64 << index)) != 0)
}
pub const fn set(mut self, index: usize, value: E) -> Self
where
E: BooleanEnum,
{
if enum_to_bool(value) {
self.bits |= 1u64 << index;
} else {
self.bits &= !(1u64 << index);
}
self
}
#[track_caller]
const fn assert_index(index: usize) {
use const_panic::{concat_panic, FmtArg, PanicVal};
if index >= 64 {
concat_panic(&[&[
PanicVal::write_str("index out of bounds: the length is "),
PanicVal::from_usize(64, FmtArg::DEBUG),
PanicVal::write_str(" but the index is "),
PanicVal::from_usize(index, FmtArg::DEBUG),
]])
}
}
}
impl<E> BitArray64<E> {
pub const fn truncated(mut self, length: usize) -> Self {
self.bits &= low_bit_mask_u64(length as u32);
self
}
#[inline]
pub const fn bits(self) -> u64 {
self.bits
}
#[allow(clippy::missing_const_for_fn)]
pub fn iter(self) -> BitArray64Iter<E> {
BitArray64Iter {
count: 64,
bits: self.bits(),
_marker: PhantomData,
}
}
pub fn eq(self, other: Self, count: usize) -> bool {
let all_accessible = low_bit_mask_u64(count as u32);
let implication = (!self.bits | other.bits) & all_accessible;
println!(
"self:{:b}\nother:{:b}\nall_accessible:{:b}\nimplication:{:b}",
self.bits, other.bits, all_accessible, implication,
);
implication == all_accessible
}
}
impl<E> Debug for BitArray64<E>
where
E: BooleanEnum,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
pub unsafe trait BooleanEnum: Debug + Copy + 'static {
const FALSE: Self;
const TRUE: Self;
}
const _: () = assert!(std::mem::size_of::<bool>() == 1);
unsafe impl BooleanEnum for bool {
const FALSE: Self = false;
const TRUE: Self = true;
}
pub const fn bool_to_enum<E>(b: bool) -> E
where
E: BooleanEnum,
{
if b {
E::TRUE
} else {
E::FALSE
}
}
pub const fn enum_to_bool<E>(b: E) -> bool
where
E: BooleanEnum,
{
enum_as_u8(b) == EnumConsts::<E>::TRUE_U8
}
const fn enum_as_u8<E: BooleanEnum>(x: E) -> u8 {
unsafe { const_transmute!(E, u8, x) }
}
struct EnumConsts<E: BooleanEnum>(E);
impl<E: BooleanEnum> EnumConsts<E> {
const TRUE_U8: u8 = enum_as_u8(E::TRUE);
}
#[derive(Debug, Clone)]
pub struct BitArray64Iter<E> {
count: usize,
bits: u64,
_marker: PhantomData<E>,
}
impl<E> BitArray64Iter<E>
where
E: BooleanEnum,
{
#[inline]
fn next_inner<F>(&mut self, f: F) -> Option<E>
where
F: FnOnce(&mut Self) -> E,
{
if self.count == 0 {
None
} else {
Some(f(self))
}
}
}
impl<E> Iterator for BitArray64Iter<E>
where
E: BooleanEnum,
{
type Item = E;
fn next(&mut self) -> Option<E> {
self.next_inner(|this| {
this.count -= 1;
let cond = (this.bits & 1) != 0;
this.bits >>= 1;
bool_to_enum(cond)
})
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len(), Some(self.len()))
}
}
impl<E> DoubleEndedIterator for BitArray64Iter<E>
where
E: BooleanEnum,
{
fn next_back(&mut self) -> Option<E> {
self.next_inner(|this| {
this.count -= 1;
bool_to_enum((this.bits & (1 << this.count)) != 0)
})
}
}
impl<E> ExactSizeIterator for BitArray64Iter<E>
where
E: BooleanEnum,
{
#[inline]
fn len(&self) -> usize {
self.count
}
}