use std::{
fmt::{self, Debug, Formatter},
sync::atomic::{AtomicI64, Ordering},
};
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use crate::align::CachePadded;
pub struct StripedCounter<const N: usize = 64> {
slots: [CachePadded<AtomicI64>; N],
}
impl<const N: usize> Default for StripedCounter<N> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<const N: usize> StripedCounter<N> {
const _ASSERT_POWER_OF_TWO: () = assert!(
N > 0 && (N & (N - 1)) == 0,
"StripedCounter 条带数必须为 2 的正整数次幂"
);
pub const STRIPE_MASK: usize = N - 1;
pub const fn new() -> Self {
#[expect(clippy::let_unit_value, reason = "编译期触发静态断言求值")]
let _ = Self::_ASSERT_POWER_OF_TWO;
Self {
slots: [const { CachePadded::new(AtomicI64::new(0)) }; N],
}
}
#[must_use]
#[inline(always)]
pub const fn stripe_index(index: usize) -> usize {
index & Self::STRIPE_MASK
}
#[inline]
pub fn add(&self, stripe_index: usize, delta: i64) {
let idx = Self::stripe_index(stripe_index);
unsafe { self.slots.get_unchecked(idx) }.fetch_add(delta, Ordering::Relaxed);
}
#[inline]
pub fn sub(&self, stripe_index: usize, delta: i64) {
let idx = Self::stripe_index(stripe_index);
unsafe { self.slots.get_unchecked(idx) }.fetch_sub(delta, Ordering::Relaxed);
}
#[must_use]
#[inline]
pub fn get(&self) -> i64 {
self.slots.iter().map(|s| s.load(Ordering::Relaxed)).sum()
}
#[must_use]
#[inline]
pub fn get_positive(&self) -> usize {
self.get().max(0) as usize
}
#[inline]
pub fn reset(&self) {
for slot in &self.slots {
slot.store(0, Ordering::Relaxed);
}
}
#[must_use]
#[inline(always)]
pub const fn len(&self) -> usize {
N
}
#[must_use]
#[inline(always)]
pub const fn is_empty(&self) -> bool {
false
}
#[must_use]
#[inline(always)]
pub fn slots(&self) -> &[CachePadded<AtomicI64>] {
&self.slots
}
}
impl<const N: usize> Debug for StripedCounter<N> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("StripedCounter")
.field("len", &N)
.field("total", &self.get())
.finish()
}
}
pub type CacheAlignedLock<T> = CachePadded<RwLock<T>>;
pub struct StripedRwLock<T = (), const N: usize = 128> {
stripes: Box<[CacheAlignedLock<T>]>,
}
impl<T: Default, const N: usize> Default for StripedRwLock<T, N> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T, const N: usize> StripedRwLock<T, N> {
const _ASSERT_POWER_OF_TWO: () = assert!(
N > 0 && (N & (N - 1)) == 0,
"StripedRwLock 条带数必须为 2 的正整数次幂"
);
pub const STRIPE_MASK: usize = N - 1;
pub fn with_initializer<F>(mut init: F) -> Self
where
F: FnMut(usize) -> T,
{
#[expect(clippy::let_unit_value, reason = "编译期触发静态断言求值")]
let _ = Self::_ASSERT_POWER_OF_TWO;
let mut stripes = Vec::with_capacity(N);
stripes.extend((0..N).map(|i| CachePadded::new(RwLock::new(init(i)))));
Self {
stripes: stripes.into_boxed_slice(),
}
}
#[must_use]
#[inline(always)]
pub const fn stripe_index(hash: u64) -> usize {
(hash as usize) & Self::STRIPE_MASK
}
#[inline]
pub fn read(&self, hash: u64) -> RwLockReadGuard<'_, T> {
let idx = Self::stripe_index(hash);
unsafe { self.stripes.get_unchecked(idx) }.0.read()
}
#[inline]
pub fn write(&self, hash: u64) -> RwLockWriteGuard<'_, T> {
let idx = Self::stripe_index(hash);
unsafe { self.stripes.get_unchecked(idx) }.0.write()
}
#[inline]
pub fn read_at(&self, index: usize) -> RwLockReadGuard<'_, T> {
let idx = index & Self::STRIPE_MASK;
unsafe { self.stripes.get_unchecked(idx) }.0.read()
}
#[inline]
pub fn write_at(&self, index: usize) -> RwLockWriteGuard<'_, T> {
let idx = index & Self::STRIPE_MASK;
unsafe { self.stripes.get_unchecked(idx) }.0.write()
}
#[must_use]
#[inline(always)]
pub const fn len(&self) -> usize {
N
}
#[must_use]
#[inline(always)]
pub const fn is_empty(&self) -> bool {
false
}
#[must_use]
#[inline(always)]
pub fn stripes(&self) -> &[CacheAlignedLock<T>] {
&self.stripes
}
}
impl<T, const N: usize> Debug for StripedRwLock<T, N> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("StripedRwLock").field("len", &N).finish()
}
}
impl<T: Default, const N: usize> StripedRwLock<T, N> {
pub fn new() -> Self {
Self::with_initializer(|_| T::default())
}
}