1use std::num::NonZeroU32;
2
3#[inline(never)]
4#[cold]
5#[cfg_attr(debug_assertions, track_caller)]
6fn panic_invalid_index(id: usize) -> ! {
7 panic!("invalid InternerSymbol index: {id}");
8}
9
10pub trait InternerSymbol: Sized + Copy + std::hash::Hash + Eq {
12 fn try_from_usize(id: usize) -> Option<Self>;
14
15 #[inline]
21 #[cfg_attr(debug_assertions, track_caller)]
22 fn from_usize(id: usize) -> Self {
23 match Self::try_from_usize(id) {
24 Some(symbol) => symbol,
25 None => panic_invalid_index(id),
26 }
27 }
28
29 fn to_usize(self) -> usize;
31}
32
33#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
35pub struct Symbol(NonZeroU32);
36
37impl Symbol {
38 #[inline]
44 pub fn new(id: u32) -> Self {
45 Self::try_new(id).unwrap()
46 }
47
48 #[inline]
50 pub fn try_new(id: u32) -> Option<Self> {
51 if id < u32::MAX {
52 Some(unsafe { Self::new_unchecked(id) })
54 } else {
55 None
56 }
57 }
58
59 #[inline]
65 #[track_caller]
66 pub fn from_usize(id: usize) -> Self {
67 <Self as InternerSymbol>::from_usize(id)
68 }
69
70 #[inline]
72 pub fn try_from_usize(id: usize) -> Option<Self> {
73 if id < u32::MAX as usize {
74 Some(unsafe { Self::new_unchecked(id as u32) })
76 } else {
77 None
78 }
79 }
80
81 #[inline]
87 pub unsafe fn new_unchecked(id: u32) -> Self {
88 Self(unsafe { NonZeroU32::new_unchecked(id.unchecked_add(1)) })
89 }
90
91 #[inline]
93 pub fn get(self) -> u32 {
94 unsafe { self.0.get().unchecked_sub(1) }
97 }
98
99 #[inline]
101 pub fn to_usize(self) -> usize {
102 self.get() as usize
103 }
104}
105
106impl InternerSymbol for Symbol {
107 #[inline]
108 fn try_from_usize(id: usize) -> Option<Self> {
109 Self::try_from_usize(id)
110 }
111
112 #[inline]
113 fn to_usize(self) -> usize {
114 self.to_usize()
115 }
116}