use core::fmt;
use core::sync::atomic::{AtomicU64, Ordering};
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct HandlerId(u64);
impl HandlerId {
#[inline]
pub(crate) const fn from_raw(raw: u64) -> Self {
Self(raw)
}
#[inline]
#[must_use]
pub const fn as_u64(self) -> u64 {
self.0
}
}
impl fmt::Debug for HandlerId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "HandlerId({})", self.0)
}
}
impl fmt::Display for HandlerId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
#[derive(Debug)]
pub(crate) struct HandlerIdGenerator {
next: AtomicU64,
}
impl HandlerIdGenerator {
#[inline]
pub(crate) const fn new() -> Self {
Self {
next: AtomicU64::new(1),
}
}
#[inline]
pub(crate) fn next(&self) -> HandlerId {
let raw = self.next.fetch_add(1, Ordering::Relaxed);
HandlerId::from_raw(raw)
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::{HandlerId, HandlerIdGenerator};
#[test]
fn debug_format_includes_value() {
let id = HandlerId::from_raw(42);
assert_eq!(format!("{id:?}"), "HandlerId(42)");
}
#[test]
fn display_format_is_bare_number() {
let id = HandlerId::from_raw(42);
assert_eq!(format!("{id}"), "42");
}
#[test]
fn ids_are_copy_eq_hash() {
let a = HandlerId::from_raw(7);
let b = a;
assert_eq!(a, b);
let mut set = std::collections::HashSet::new();
let _ = set.insert(a);
assert!(set.contains(&b));
}
#[test]
fn generator_produces_unique_ascending_ids() {
let generator = HandlerIdGenerator::new();
let a = generator.next();
let b = generator.next();
let c = generator.next();
assert_eq!(a.as_u64(), 1);
assert_eq!(b.as_u64(), 2);
assert_eq!(c.as_u64(), 3);
assert_ne!(a, b);
assert_ne!(b, c);
}
#[test]
fn generator_is_thread_safe_and_unique() {
use std::sync::Arc;
use std::thread;
let generator = Arc::new(HandlerIdGenerator::new());
let mut handles = Vec::new();
for _ in 0..8 {
let g = Arc::clone(&generator);
handles.push(thread::spawn(move || {
let mut ids = Vec::with_capacity(1000);
for _ in 0..1000 {
ids.push(g.next());
}
ids
}));
}
let mut all = Vec::with_capacity(8 * 1000);
for h in handles {
let mut ids = h.join().expect("worker thread did not panic");
all.append(&mut ids);
}
let unique: std::collections::HashSet<_> = all.iter().copied().collect();
assert_eq!(unique.len(), all.len());
}
}