1use dashmap::DashMap;
4use once_cell::sync::Lazy;
5use std::sync::atomic::{AtomicU64, Ordering};
6
7#[repr(transparent)]
10#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
11pub struct LabelId(u64);
12
13static INTERNER: Lazy<LabelInterner> = Lazy::new(LabelInterner::default);
14
15struct LabelInterner {
16 name_to_id: DashMap<String, u64>,
17 id_to_name: DashMap<u64, String>,
18 next_id: AtomicU64,
19}
20
21impl Default for LabelInterner {
22 fn default() -> Self {
23 Self {
24 name_to_id: DashMap::new(),
25 id_to_name: DashMap::new(),
26 next_id: AtomicU64::new(1),
27 }
28 }
29}
30
31impl LabelId {
32 pub fn intern(name: &str) -> Self {
35 if let Some(id) = INTERNER.name_to_id.get(name) {
36 return LabelId(*id);
37 }
38 let id = INTERNER.next_id.fetch_add(1, Ordering::SeqCst);
39 INTERNER.name_to_id.insert(name.to_string(), id);
40 INTERNER.id_to_name.insert(id, name.to_string());
41 LabelId(id)
42 }
43
44 pub fn name(&self) -> String {
46 INTERNER
47 .id_to_name
48 .get(&self.0)
49 .map(|e| e.value().clone())
50 .unwrap_or_else(|| format!("<unknown:{}>", self.0))
51 }
52
53 pub fn fresh() -> Self {
55 let id = INTERNER.next_id.fetch_add(1, Ordering::SeqCst);
56 let name = format!("_tmp_{}", id);
57 INTERNER.name_to_id.insert(name.clone(), id);
58 INTERNER.id_to_name.insert(id, name);
59 LabelId(id)
60 }
61
62 pub fn prime(&self) -> Self {
64 Self::intern(&format!("{}'", self.name()))
65 }
66
67 pub fn primes(&self, n: usize) -> Self {
69 Self::intern(&format!("{}{}", self.name(), "'".repeat(n)))
70 }
71
72 pub fn unprime(&self) -> Self {
74 let name = self.name();
75 if let Some(stripped) = name.strip_suffix('\'') {
76 Self::intern(stripped)
77 } else {
78 *self
79 }
80 }
81
82 pub fn base(&self) -> Self {
84 Self::intern(self.name().trim_end_matches('\''))
85 }
86}
87
88#[macro_export]
90macro_rules! label {
91 ($name:expr) => {
92 $crate::LabelId::intern($name)
93 };
94}
95
96#[macro_export]
98macro_rules! fresh {
99 () => {
100 $crate::LabelId::fresh()
101 };
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_intern_idempotent() {
110 let id1 = LabelId::intern("i");
111 let id2 = LabelId::intern("i");
112 assert_eq!(id1, id2);
113 }
114
115 #[test]
116 fn test_prime_operations() {
117 let i = LabelId::intern("test_i");
118 assert_eq!(i.prime().name(), "test_i'");
119 assert_eq!(i.primes(2).name(), "test_i''");
120 }
121
122 #[test]
123 fn test_fresh_unique() {
124 let f1 = LabelId::fresh();
125 let f2 = LabelId::fresh();
126 assert_ne!(f1, f2);
127 }
128}