use std::sync::atomic;
use std::{fmt, ops};
use crate::util::UnsafeEqOrd;
pub trait Raw: Sized + Send + Sync + Copy + fmt::Debug + UnsafeEqOrd + 'static {
type Atomic: Atomic<Self>;
fn new() -> Self::Atomic;
fn add(self, count: Primitive) -> Self;
fn sub(self, other: Self) -> Primitive;
fn approx_midpoint(self, other: Self) -> Self;
fn from_primitive(i: Primitive) -> Self;
fn to_primitive(self) -> Primitive;
type Range: Iterator<Item = Self>;
fn range(range: ops::Range<Self>) -> Self::Range;
}
pub trait Atomic<E: Raw>: Send + Sync + 'static {
fn fetch_add(&self, count: usize) -> E;
fn load(&self) -> E;
fn load_mut(&mut self) -> E;
}
macro_rules! impl_raw {
($base:ty, $atomic:ty, $primitive:ty) => {
impl Raw for $base {
type Atomic = $atomic;
fn new() -> Self::Atomic { <$atomic>::new(1) }
fn add(self, count: usize) -> Self {
let count: $primitive = count.try_into().expect("count is too large");
<$base>::new(self.get() + count).expect("integer overflow")
}
fn sub(self, other: Self) -> usize {
(self.get() - other.get()).try_into().expect("usize should be sufficiently large")
}
fn approx_midpoint(self, other: Self) -> Self {
<$base>::new((self.get() + other.get()) / 2)
.expect("get() >= 1, get() + get() >= 2, half >= 1")
}
fn from_primitive(i: Primitive) -> Self {
i.try_into().ok().and_then(Self::new).expect("Invalid usize")
}
fn to_primitive(self) -> Primitive { self.get().try_into().expect("Too many entities") }
type Range = impl Iterator<Item = Self>;
fn range(range: ops::Range<Self>) -> Self::Range {
(range.start.get()..range.end.get()).map(|v| {
<$base>::new(v)
.expect("zero does not appear between two non-zero unsigned integers")
})
}
}
impl Atomic<$base> for $atomic {
fn fetch_add(&self, count: usize) -> $base {
let original = <$atomic>::fetch_add(
self,
count.try_into().expect("count is too large"),
atomic::Ordering::SeqCst,
);
<$base>::new(original).expect("integer overflow")
}
fn load(&self) -> $base {
let original = <$atomic>::load(self, atomic::Ordering::SeqCst);
<$base>::new(original).expect("invalid state")
}
fn load_mut(&mut self) -> $base {
let original = *<$atomic>::get_mut(self);
<$base>::new(original).expect("invalid state")
}
}
};
}
impl_raw!(std::num::NonZeroU16, std::sync::atomic::AtomicU16, u16);
impl_raw!(std::num::NonZeroU32, std::sync::atomic::AtomicU32, u32);
impl_raw!(std::num::NonZeroU64, std::sync::atomic::AtomicU64, u64);
pub type Primitive = usize;