#![no_std]
use core::num::NonZeroU64;
use core::sync::atomic::{AtomicU64, Ordering::Relaxed};
#[repr(transparent)]
pub struct Id(AtomicU64);
impl Id {
#[inline]
pub const fn lazy() -> Self {
Self::LAZY_INITIALIZER
}
#[inline]
pub fn new() -> Self {
Self(AtomicU64::new(Self::next_id().get()))
}
pub const LAZY_INITIALIZER: Self = Self(AtomicU64::new(0));
#[inline]
pub fn get(&self) -> u64 {
self.get_nonzero().get()
}
#[inline]
pub fn get_nonzero(&self) -> NonZeroU64 {
if let Some(id) = NonZeroU64::new(self.0.load(Relaxed)) {
id
} else {
let my_id = self.lazy_init();
debug_assert_eq!(self.0.load(Relaxed), my_id.get());
my_id
}
}
#[inline]
fn get_ref(&self) -> &u64 {
let _ = self.get();
unsafe { &*(self as *const _ as *const u64) }
}
#[inline]
fn ensure_init(&mut self) -> NonZeroU64 {
let ptr: &mut u64 = self.0.get_mut();
if let Some(nz) = NonZeroU64::new(*ptr) {
return nz;
}
let id = Self::next_id();
*ptr = id.get();
id
}
const ID2SEQ: u64 = 0x1337_fe4415;
const SEQ2ID: u64 = 6848199123282258749;
#[inline]
fn next_id() -> NonZeroU64 {
const _ASSERT_ODD: [(); 1] = [(); (Id::SEQ2ID & 1) as usize];
let seq = next_seq();
let id = seq.get().wrapping_mul(Id::SEQ2ID);
unsafe {
debug_assert!(id != 0);
NonZeroU64::new_unchecked(id)
}
}
#[cold]
fn lazy_init(&self) -> NonZeroU64 {
let id = Self::next_id();
match self.0.compare_exchange(0, id.get(), Relaxed, Relaxed) {
Ok(_) => id,
Err(e) => {
debug_assert!(e != 0);
unsafe { core::num::NonZeroU64::new_unchecked(e) }
}
}
}
#[inline]
pub const fn from_raw_integer(id: NonZeroU64) -> Self {
Self(AtomicU64::new(id.get()))
}
}
impl PartialEq for Id {
#[inline]
fn eq(&self, o: &Self) -> bool {
self.get() == o.get()
}
}
impl PartialOrd for Id {
#[inline]
fn partial_cmp(&self, o: &Self) -> Option<core::cmp::Ordering> {
self.get().partial_cmp(&o.get())
}
}
impl Eq for Id {}
impl core::cmp::Ord for Id {
#[inline]
fn cmp(&self, o: &Self) -> core::cmp::Ordering {
self.get().cmp(o)
}
}
impl core::hash::Hash for Id {
#[inline]
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.get().hash(state)
}
}
impl PartialEq<u64> for Id {
#[inline]
fn eq(&self, o: &u64) -> bool {
self.get() == *o
}
}
impl PartialEq<Id> for u64 {
#[inline]
fn eq(&self, o: &Id) -> bool {
*self == o.get()
}
}
impl Clone for Id {
#[inline]
fn clone(&self) -> Self {
Self(AtomicU64::new(self.get()))
}
}
impl core::fmt::Debug for Id {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let v = self.get();
write!(f, "Id({:#x}; seq={})", v, v.wrapping_mul(Self::ID2SEQ))
}
}
impl core::fmt::Display for Id {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.get().fmt(f)
}
}
impl core::ops::Deref for Id {
type Target = u64;
#[inline]
fn deref(&self) -> &Self::Target {
self.get_ref()
}
}
impl core::borrow::Borrow<u64> for Id {
#[inline]
fn borrow(&self) -> &u64 {
self
}
}
impl AsRef<u64> for Id {
#[inline]
fn as_ref(&self) -> &u64 {
self
}
}
impl Default for Id {
#[inline]
fn default() -> Self {
Id::new()
}
}
impl From<Id> for u64 {
#[inline]
fn from(mut id: Id) -> Self {
id.ensure_init().get()
}
}
impl From<&Id> for u64 {
#[inline]
fn from(id: &Id) -> Self {
id.get()
}
}
impl From<Id> for NonZeroU64 {
#[inline]
fn from(mut id: Id) -> Self {
id.ensure_init()
}
}
static ID_ALLOC: AtomicU64 = AtomicU64::new(1);
#[inline]
fn next_seq() -> NonZeroU64 {
let seq = ID_ALLOC.fetch_add(1, Relaxed);
if seq > (i64::max_value() as u64) {
nostd_abort();
}
debug_assert!(seq != 0);
unsafe { NonZeroU64::new_unchecked(seq) }
}
#[cfg(test)]
mod test {
#[test]
fn mixing() {
fn syncmix(u: u64) -> u64 {
u.wrapping_mul(super::Id::SEQ2ID)
}
fn syncunmix(u: u64) -> u64 {
u.wrapping_mul(super::Id::ID2SEQ)
}
let count = if cfg!(miri) { 100 } else { 10000 };
for i in 0..count {
let v = [i, !i, syncmix(i), syncunmix(i)];
for (j, v) in v.iter().cloned().enumerate() {
assert_eq!(syncunmix(syncmix(v)), v, "i: {} step {}", i, j);
assert_eq!(syncmix(syncunmix(v)), v, "i: {} step {}", i, j);
}
}
}
}
#[cold]
#[inline(never)]
fn nostd_abort() -> ! {
struct PanicOnDrop();
impl Drop for PanicOnDrop {
#[inline]
fn drop(&mut self) {
panic!("Id counter overflow. Aborting by double panic (2/2)");
}
}
let _p = PanicOnDrop();
panic!("Id counter overflow. Aborting by double panic (1/2)");
}