1use std::collections::HashMap;
2use std::cell::RefCell;
3use std::fmt::{Debug, Display, Error, Formatter};
4use std::cmp::{Ord, Ordering, PartialOrd};
5
6#[cfg(test)]
7mod test;
8
9thread_local! {
10 static INTERNER_TLS: RefCell<Interner> =
11 RefCell::new(Interner::new())
12}
13
14pub struct Interner {
15 map: HashMap<String, InternedString>,
16 strings: Vec<String>,
17}
18
19#[derive(Copy, Clone, Hash, Eq, PartialEq)]
20pub struct InternedString {
21 index: u32,
22}
23
24pub fn intern(s: &str) -> InternedString {
25 write(|interner| {
26 match interner.map.get(s) {
27 Some(&v) => {
28 return v;
29 }
30 None => {}
31 }
32
33 let index = interner.strings.len() as u32;
34 let result = InternedString { index: index };
35 interner.map.insert(s.to_string(), result);
36 interner.strings.push(s.to_string());
37 return result;
38 })
39}
40
41pub fn read<F, R>(f: F) -> R
42where
43 F: FnOnce(&Interner) -> R,
44{
45 INTERNER_TLS.with(|interner| f(&*interner.borrow()))
46}
47
48fn write<F, R>(f: F) -> R
49where
50 F: FnOnce(&mut Interner) -> R,
51{
52 INTERNER_TLS.with(|interner| f(&mut *interner.borrow_mut()))
53}
54
55impl Interner {
56 fn new() -> Interner {
57 Interner {
58 map: HashMap::new(),
59 strings: vec![],
60 }
61 }
62
63 pub fn data(&self, i: InternedString) -> &str {
64 &self.strings[i.index()]
65 }
66}
67
68impl InternedString {
69 fn index(&self) -> usize {
70 self.index as usize
71 }
72
73 pub fn len(&self) -> usize {
74 read(|interner| interner.data(*self).len())
75 }
76}
77
78impl Debug for InternedString {
79 fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
80 read(|interner| Debug::fmt(&interner.data(*self), fmt))
81 }
82}
83
84impl Display for InternedString {
85 fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
86 read(|interner| Display::fmt(&interner.data(*self), fmt))
87 }
88}
89
90impl PartialOrd<InternedString> for InternedString {
91 fn partial_cmp(&self, other: &InternedString) -> Option<Ordering> {
92 read(|interner| PartialOrd::partial_cmp(interner.data(*self), interner.data(*other)))
93 }
94}
95
96impl Ord for InternedString {
97 fn cmp(&self, other: &InternedString) -> Ordering {
98 read(|interner| Ord::cmp(interner.data(*self), interner.data(*other)))
99 }
100}