1use std::collections::HashMap;
2
3#[derive(Debug)]
19pub struct Interner<I = u32> {
20 map: HashMap<&'static str, I>,
21 vec: Vec<&'static str>,
22 buf: String,
23 full: Vec<String>,
24}
25
26impl<I> Interner<I>
27where
28 I: From<u32> + Copy,
29 u32: From<I>,
30{
31 pub fn with_capacity(cap: usize) -> Self {
40 Interner {
41 map: Default::default(),
42 vec: Vec::new(),
43 buf: String::with_capacity(cap.next_power_of_two()),
44 full: Vec::new(),
45 }
46 }
47
48 pub fn intern(&mut self, name: &str) -> I {
65 match self.map.get(name) {
66 Some(id) => (*id).into(),
67 None => {
68 let name = self.alloc(name);
69 let id = self.map.len() as u32;
70 self.map.insert(name, id.into());
71 self.vec.push(name);
72
73 id.into()
74 }
75 }
76 }
77
78 fn alloc(&mut self, name: &str) -> &'static str {
79 let cap = self.buf.capacity();
80 if cap < self.buf.len() + name.len() {
81 let new_cap = (cap.max(name.len()) + 1).next_power_of_two();
82 let new_buf = String::with_capacity(new_cap);
83 let old_buf = std::mem::replace(&mut self.buf, new_buf);
84 self.full.push(old_buf);
85 }
86 let interned = {
87 let start = self.buf.len();
88 self.buf.push_str(name);
89 &self.buf[start..]
90 };
91
92 unsafe { &*(interned as *const str) }
93 }
94
95 pub fn lookup(&self, id: I) -> &'static str {
96 let index: u32 = u32::from(id);
97 self.vec[index as usize]
98 }
99}
100#[cfg(test)]
101mod tests {
102 #[test]
103 fn intern() {
104 use super::Interner;
105 const TEST_CASE: &str = "test_case";
106 const DUMMY_ONE: &str = "dummy_one";
107 const DUMMY_TWO: &str = "dummy_two";
108 const DUMMY_THREE: &str = "dummy_three";
109
110 let mut interner = <Interner<u32>>::with_capacity(4);
111 _ = interner.intern(DUMMY_ONE);
112 _ = interner.intern(DUMMY_TWO);
113 _ = interner.intern(DUMMY_THREE);
114 _ = interner.intern(TEST_CASE);
115 assert_eq!(interner.intern(TEST_CASE), 3)
116 }
117
118 #[test]
119 fn lookup() {
120 use super::Interner;
121 let mut interner: Interner<u32> = Interner::with_capacity(32);
122 interner.intern("hello");
123 interner.intern("and");
124 interner.intern("good");
125 interner.intern("morning");
126 assert_eq!(interner.lookup(2), "good");
127 }
128}