Upstream commit: 474dd0229cb20cf513036619781ed97bd8073c3f Enterprise-only files removed or emptied: 63 Enterprise-only snippets removed: 117 in 50 files Dangling module declarations removed: 5 Cargo edits turning enterprise off: 14 Verification: clean Enterprise feature gates left for rebuilt features: 19 in 18 files Produced by tools/fork/strip.py. The full report is in docs/fork/strip-reports/ on main.
141 lines
3.8 KiB
Rust
141 lines
3.8 KiB
Rust
/*
|
|
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
|
*/
|
|
|
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
|
|
use crate::ipc::{USIZE_BITS, USIZE_BITS_MASK, bitset::Bitset};
|
|
|
|
pub struct AtomicBitset<const N: usize>([AtomicUsize; N]);
|
|
|
|
impl<const N: usize> AtomicBitset<N> {
|
|
#[allow(clippy::new_without_default)]
|
|
#[allow(clippy::declare_interior_mutable_const)]
|
|
pub const fn new() -> Self {
|
|
Self({
|
|
const INIT: AtomicUsize = AtomicUsize::new(0);
|
|
let mut array = [INIT; N];
|
|
let mut i = 0;
|
|
while i < N {
|
|
array[i] = AtomicUsize::new(0);
|
|
i += 1;
|
|
}
|
|
array
|
|
})
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn set(&self, index: impl Into<usize>) {
|
|
let index = index.into();
|
|
self.0[index / USIZE_BITS].fetch_or(1 << (index & USIZE_BITS_MASK), Ordering::Relaxed);
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn clear(&self, index: impl Into<usize>) {
|
|
let index = index.into();
|
|
self.0[index / USIZE_BITS].fetch_and(!(1 << (index & USIZE_BITS_MASK)), Ordering::Relaxed);
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn get(&self, index: impl Into<usize>) -> bool {
|
|
let index = index.into();
|
|
self.0[index / USIZE_BITS].load(Ordering::Relaxed) & (1 << (index & USIZE_BITS_MASK)) != 0
|
|
}
|
|
|
|
pub fn update(&self, bitset: impl AsRef<Bitset<N>>) {
|
|
let bitset = bitset.as_ref();
|
|
for i in 0..N {
|
|
self.0[i].store(bitset.0[i], Ordering::Relaxed);
|
|
}
|
|
}
|
|
|
|
pub fn union(&self, bitset: impl AsRef<Bitset<N>>) {
|
|
let bitset = bitset.as_ref();
|
|
for i in 0..N {
|
|
self.0[i].fetch_or(bitset.0[i], Ordering::Relaxed);
|
|
}
|
|
}
|
|
|
|
pub fn clear_all(&self) {
|
|
for i in 0..N {
|
|
self.0[i].store(0, Ordering::Relaxed);
|
|
}
|
|
}
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
for i in 0..N {
|
|
if self.0[i].load(Ordering::Relaxed) != 0 {
|
|
return false;
|
|
}
|
|
}
|
|
true
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
const TEST_SIZE: usize = 1000;
|
|
type TestBitset = AtomicBitset<{ TEST_SIZE.div_ceil(USIZE_BITS) }>;
|
|
static BITSET: TestBitset = TestBitset::new();
|
|
|
|
#[test]
|
|
fn test_atomic_bitset() {
|
|
for i in 0..TEST_SIZE {
|
|
assert!(!BITSET.get(i), "Bit {} should be unset in new BITSET", i);
|
|
}
|
|
|
|
for i in 0..TEST_SIZE {
|
|
assert!(!BITSET.get(i), "Bit {} should be initially unset", i);
|
|
BITSET.set(i);
|
|
assert!(BITSET.get(i), "Bit {} should be set after setting", i);
|
|
}
|
|
|
|
BITSET.clear_all();
|
|
|
|
for i in 0..TEST_SIZE {
|
|
BITSET.set(i);
|
|
assert!(BITSET.get(i), "Bit {} should be set before clearing", i);
|
|
BITSET.clear(i);
|
|
assert!(!BITSET.get(i), "Bit {} should be unset after clearing", i);
|
|
}
|
|
|
|
BITSET.clear_all();
|
|
|
|
// Set even bits
|
|
for i in (0..TEST_SIZE).step_by(2) {
|
|
BITSET.set(i);
|
|
}
|
|
|
|
// Check all bits
|
|
for i in 0..TEST_SIZE {
|
|
if i % 2 == 0 {
|
|
assert!(BITSET.get(i), "Even bit {} should be set", i);
|
|
} else {
|
|
assert!(!BITSET.get(i), "Odd bit {} should be unset", i);
|
|
}
|
|
}
|
|
|
|
// Clear even bits and set odd bits
|
|
for i in 0..TEST_SIZE {
|
|
if i % 2 == 0 {
|
|
BITSET.clear(i);
|
|
} else {
|
|
BITSET.set(i);
|
|
}
|
|
}
|
|
|
|
// Check all bits again
|
|
for i in 0..TEST_SIZE {
|
|
if i % 2 == 0 {
|
|
assert!(!BITSET.get(i), "Even bit {} should now be unset", i);
|
|
} else {
|
|
assert!(BITSET.get(i), "Odd bit {} should now be set", i);
|
|
}
|
|
}
|
|
}
|
|
}
|