inturn/
symbol.rs

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
10/// Trait for types that can be used as symbols in an `Interner`.
11pub trait InternerSymbol: Sized + Copy + std::hash::Hash + Eq {
12    /// Tries to create a new `Symbol` from a `usize`.
13    fn try_from_usize(id: usize) -> Option<Self>;
14
15    /// Creates a new `Symbol` from a `usize`.
16    ///
17    /// # Panics
18    ///
19    /// Panics if `id` is an invalid index for `Self`.
20    #[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    /// Converts the `Symbol` to a `usize`.
30    fn to_usize(self) -> usize;
31}
32
33/// Default unique identifier for a string in an `Interner`.
34#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
35pub struct Symbol(NonZeroU32);
36
37impl Symbol {
38    /// Creates a new `Symbol` from a `u32`.
39    ///
40    /// # Panics
41    ///
42    /// Panics if `id` is `u32::MAX`.
43    #[inline]
44    pub fn new(id: u32) -> Self {
45        Self::try_new(id).unwrap()
46    }
47
48    /// Tries to create a new `Symbol` from a `u32`.
49    #[inline]
50    pub fn try_new(id: u32) -> Option<Self> {
51        if id < u32::MAX {
52            // SAFETY: `id` is less than `u32::MAX`.
53            Some(unsafe { Self::new_unchecked(id) })
54        } else {
55            None
56        }
57    }
58
59    /// Creates a new `Symbol` from a `usize`.
60    ///
61    /// # Panics
62    ///
63    /// Panics if `id` is greater than or equal to `u32::MAX`.
64    #[inline]
65    #[track_caller]
66    pub fn from_usize(id: usize) -> Self {
67        <Self as InternerSymbol>::from_usize(id)
68    }
69
70    /// Tries to create a new `Symbol` from a `usize`.
71    #[inline]
72    pub fn try_from_usize(id: usize) -> Option<Self> {
73        if id < u32::MAX as usize {
74            // SAFETY: `id` is less than `u32::MAX`.
75            Some(unsafe { Self::new_unchecked(id as u32) })
76        } else {
77            None
78        }
79    }
80
81    /// Creates a new `Symbol` from a `u32` without checking if it is valid.
82    ///
83    /// # Safety
84    ///
85    /// The caller must ensure that `id` is less than `u32::MAX`.
86    #[inline]
87    pub unsafe fn new_unchecked(id: u32) -> Self {
88        Self(unsafe { NonZeroU32::new_unchecked(id.unchecked_add(1)) })
89    }
90
91    /// Returns the `u32` value of the `Symbol`.
92    #[inline]
93    pub fn get(self) -> u32 {
94        // SAFETY: `NonZeroU32` is guaranteed to be non-zero, so we can safely
95        // subtract 1 to get the original id.
96        unsafe { self.0.get().unchecked_sub(1) }
97    }
98
99    /// Returns the `usize` value of the `Symbol`.
100    #[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}