use std::num::NonZeroU32;
#[inline(never)]
#[cold]
#[cfg_attr(debug_assertions, track_caller)]
fn panic_invalid_index(id: usize) -> ! {
panic!("invalid InternerSymbol index: {id}");
}
pub trait InternerSymbol: Sized + Copy + std::hash::Hash + Eq {
fn try_from_usize(id: usize) -> Option<Self>;
#[inline]
#[cfg_attr(debug_assertions, track_caller)]
fn from_usize(id: usize) -> Self {
match Self::try_from_usize(id) {
Some(symbol) => symbol,
None => panic_invalid_index(id),
}
}
fn to_usize(self) -> usize;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Symbol(NonZeroU32);
impl Symbol {
#[inline]
pub fn new(id: u32) -> Self {
Self::try_new(id).unwrap()
}
#[inline]
pub fn try_new(id: u32) -> Option<Self> {
if id < u32::MAX {
Some(unsafe { Self::new_unchecked(id) })
} else {
None
}
}
#[inline]
#[track_caller]
pub fn from_usize(id: usize) -> Self {
<Self as InternerSymbol>::from_usize(id)
}
#[inline]
pub fn try_from_usize(id: usize) -> Option<Self> {
if id < u32::MAX as usize {
Some(unsafe { Self::new_unchecked(id as u32) })
} else {
None
}
}
#[inline]
pub unsafe fn new_unchecked(id: u32) -> Self {
Self(unsafe { NonZeroU32::new_unchecked(id.unchecked_add(1)) })
}
#[inline]
pub fn get(self) -> u32 {
unsafe { self.0.get().unchecked_sub(1) }
}
#[inline]
pub fn to_usize(self) -> usize {
self.get() as usize
}
}
impl InternerSymbol for Symbol {
#[inline]
fn try_from_usize(id: usize) -> Option<Self> {
Self::try_from_usize(id)
}
#[inline]
fn to_usize(self) -> usize {
self.to_usize()
}
}