use alloc::boxed::Box;
use alloc::vec::Vec;
use core::hash::{Hash, Hasher};
use core::marker::PhantomData;
use core::ops::Deref;
use core::sync::atomic::Ordering;
use num_traits::{PrimInt, Zero};
use radium::Radium;
use super::atomic_slice::AtomicBitSlice;
use super::boxed::BoxedBitSet;
pub struct AtomicBoxedBitSet<A, V>(Box<[A]>, PhantomData<V>);
impl<A: Radium, V> core::fmt::Debug for AtomicBoxedBitSet<A, V>
where
A::Item: PrimInt + core::ops::BitAndAssign,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("AtomicBoxedBitSet")?;
core::fmt::Debug::fmt(&**self, f)
}
}
impl<A: Radium, V> core::fmt::Display for AtomicBoxedBitSet<A, V>
where
A::Item: PrimInt + core::ops::BitAndAssign,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Display::fmt(&**self, f)
}
}
impl<A: Radium, V> Deref for AtomicBoxedBitSet<A, V>
where
A::Item: PrimInt,
{
type Target = AtomicBitSlice<A, V>;
#[inline]
fn deref(&self) -> &Self::Target {
AtomicBitSlice::from_slice_ref(&self.0)
}
}
impl<A: Radium, V> Hash for AtomicBoxedBitSet<A, V>
where
A::Item: Hash + PrimInt,
{
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl<A: Radium, V> AtomicBoxedBitSet<A, V>
where
A::Item: PrimInt,
{
pub fn from_boxed_slice(store: Box<[A]>) -> Self {
Self(store, PhantomData)
}
}
impl<A: Radium + Default, V> AtomicBoxedBitSet<A, V>
where
A::Item: PrimInt,
{
pub fn with_capacity(bits: usize) -> Self {
let bits_per = core::mem::size_of::<A>() * 8;
let store_size = bits.div_ceil(bits_per);
let mut store = Vec::new();
store.resize_with(store_size, A::default);
Self(store.into_boxed_slice(), PhantomData)
}
}
impl<A: Radium, V> AtomicBoxedBitSet<A, V>
where
A::Item: PrimInt,
{
pub fn drain(&self) -> BoxedBitSet<A::Item, V>
where
A::Item: Default,
{
let mut drained = BoxedBitSet::<A::Item, V>::with_capacity(self.capacity());
for (source, target) in self.0.iter().zip(drained.as_raw_mut_slice().iter_mut()) {
*target = source.swap(A::Item::zero(), Ordering::AcqRel);
}
drained
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
use alloc::vec::Vec;
use core::sync::atomic::AtomicU64;
use proptest::prelude::*;
#[test]
fn test_boxed_basic() {
let bs = AtomicBoxedBitSet::<AtomicU64, usize>::with_capacity(256);
assert!(bs.is_empty());
assert_eq!(bs.len(), 0);
assert_eq!(bs.capacity(), 256);
assert!(bs.insert(0));
assert!(!bs.insert(0));
assert!(bs.insert(63));
assert!(bs.insert(64));
assert!(bs.insert(200));
assert!(!bs.is_empty());
assert_eq!(bs.len(), 4);
assert!(bs.contains(&0));
assert!(bs.contains(&63));
assert!(bs.contains(&64));
assert!(bs.contains(&200));
assert!(!bs.contains(&1));
assert!(bs.remove(63));
assert!(!bs.remove(63));
assert_eq!(bs.len(), 3);
bs.clear();
assert!(bs.is_empty());
assert_eq!(bs.len(), 0);
}
#[test]
fn test_boxed_boundary() {
let bs = AtomicBoxedBitSet::<AtomicU64, usize>::with_capacity(256);
bs.insert(0);
bs.insert(255);
assert_eq!(bs.len(), 2);
assert!(bs.contains(&0));
assert!(bs.contains(&255));
let items: Vec<usize> = bs.iter().collect();
assert_eq!(items, vec![0, 255]);
bs.remove(255);
assert!(!bs.contains(&255));
let bs = AtomicBoxedBitSet::<AtomicU64, usize>::with_capacity(65536);
bs.insert(0);
bs.insert(65535);
bs.insert(32768);
assert_eq!(bs.len(), 3);
let items: Vec<usize> = bs.iter().collect();
assert_eq!(items, vec![0, 32768, 65535]);
bs.remove(65535);
assert!(!bs.contains(&65535));
assert_eq!(bs.len(), 2);
}
#[test]
fn test_boxed_drain() {
let bs = AtomicBoxedBitSet::<AtomicU64, usize>::with_capacity(256);
bs.insert(3);
bs.insert(100);
bs.insert(200);
let drained = bs.drain();
assert!(bs.is_empty());
let items: Vec<usize> = drained.iter().collect();
assert_eq!(items, vec![3, 100, 200]);
}
#[test]
fn test_boxed_set_relations() {
let a = AtomicBoxedBitSet::<AtomicU64, usize>::with_capacity(256);
a.insert(1);
a.insert(65);
let b = AtomicBoxedBitSet::<AtomicU64, usize>::with_capacity(256);
b.insert(1);
b.insert(65);
b.insert(200);
let c = AtomicBoxedBitSet::<AtomicU64, usize>::with_capacity(256);
c.insert(2);
c.insert(66);
assert!(a.is_subset(&b));
assert!(!b.is_subset(&a));
assert!(b.is_superset(&a));
assert!(!a.is_superset(&b));
assert!(a.is_disjoint(&c));
assert!(!a.is_disjoint(&b));
}
#[test]
fn test_mismatched_capacity_subset() {
let small = AtomicBoxedBitSet::<AtomicU64, usize>::with_capacity(64);
small.insert(1);
let large = AtomicBoxedBitSet::<AtomicU64, usize>::with_capacity(256);
large.insert(1);
large.insert(200);
assert!(small.is_subset(&large));
assert!(!large.is_subset(&small));
let s2 = AtomicBoxedBitSet::<AtomicU64, usize>::with_capacity(128);
s2.insert(100);
let l2 = AtomicBoxedBitSet::<AtomicU64, usize>::with_capacity(64);
assert!(!s2.is_subset(&l2));
let empty = AtomicBoxedBitSet::<AtomicU64, usize>::with_capacity(64);
assert!(empty.is_subset(&large));
assert!(empty.is_subset(&small));
}
#[test]
fn test_mismatched_capacity_disjoint() {
let a = AtomicBoxedBitSet::<AtomicU64, usize>::with_capacity(64);
a.insert(1);
let b = AtomicBoxedBitSet::<AtomicU64, usize>::with_capacity(256);
b.insert(200); assert!(a.is_disjoint(&b));
b.insert(1); assert!(!a.is_disjoint(&b));
}
#[test]
fn test_boxed_toggle() {
let bs = AtomicBoxedBitSet::<AtomicU64, usize>::with_capacity(256);
bs.insert(10);
bs.toggle(10);
assert!(!bs.contains(&10));
bs.toggle(10);
assert!(bs.contains(&10));
bs.toggle(200);
assert!(bs.contains(&200));
}
proptest! {
#[test]
fn parity_atomic_boxed_65536(ops in prop::collection::vec((any::<bool>(), 0..65536usize), 0..200)) {
let atomic = AtomicBoxedBitSet::<AtomicU64, usize>::with_capacity(65536);
let mut plain = BoxedBitSet::<u64, usize>::with_capacity(65536);
for &(insert, idx) in &ops {
if insert {
atomic.insert(idx);
plain.insert(idx);
} else {
atomic.remove(idx);
plain.remove(idx);
}
}
prop_assert_eq!(atomic.len(), plain.len(), "len mismatch");
prop_assert_eq!(atomic.is_empty(), plain.is_empty(), "is_empty mismatch");
let atomic_bits: Vec<usize> = atomic.iter().collect();
let plain_bits: Vec<usize> = plain.iter().collect();
prop_assert_eq!(atomic_bits, plain_bits, "iter mismatch");
}
}
}