use std::marker::PhantomData;
use std::sync::atomic::{AtomicU8, Ordering};
pub struct AtomicEnum<E> {
atomic: AtomicU8,
_phantom: PhantomData<E>,
}
impl<E: AtomicIntLike> AtomicEnum<E> {
pub fn new(value: E) -> Self {
Self {
atomic: AtomicU8::new(value.to_u8()),
_phantom: PhantomData,
}
}
pub const fn default() -> Self {
Self {
atomic: AtomicU8::new(E::DEFAULT_ORD),
_phantom: PhantomData,
}
}
pub fn load(&self) -> E {
let raw = self.atomic.load(Ordering::Relaxed);
E::from_u8(raw)
}
pub fn store(&self, value: E) {
self.atomic.store(value.to_u8(), Ordering::Relaxed);
}
pub fn replace(&self, value: E) -> E {
let old = self.atomic.swap(value.to_u8(), Ordering::Relaxed);
E::from_u8(old)
}
}
pub trait AtomicIntLike: Copy {
const DEFAULT_ORD: u8;
fn to_u8(self) -> u8;
fn from_u8(value: u8) -> Self;
}
impl<T: AtomicIntLike> AtomicIntLike for Option<T> {
const DEFAULT_ORD: u8 = u8::MAX;
fn to_u8(self) -> u8 {
match self {
None => u8::MAX,
Some(inner) => T::to_u8(inner),
}
}
fn from_u8(value: u8) -> Self {
match value {
u8::MAX => None,
inner => Some(T::from_u8(inner)),
}
}
}
#[macro_export]
macro_rules! atomic_enum {
(
$(#[$attr:meta])*
$vis:vis enum $name:ident {
$(
$(#[$vattr:meta])*
$variant:ident = $ord:literal
),+ $(,)?
}
) => {
$(#[$attr])*
#[repr(u8)]
$vis enum $name {
$($(#[$vattr])* $variant = $ord,)+
}
impl $crate::AtomicIntLike for $name {
const DEFAULT_ORD: u8 = [$($ord),+][0];
fn to_u8(self) -> u8 { self as u8 }
fn from_u8(value: u8) -> Self {
match value {
$($ord => Self::$variant,)+
other => panic!(
concat!("invalid ", stringify!($name), " discriminant: {}"),
other
),
}
}
}
};
}