Skip to main content

prune_lang/utils/
intern.rs

1use once_cell::sync::Lazy;
2use std::collections::HashMap;
3use std::fmt;
4use std::sync;
5
6static INDEXD_BUF_SIZE: usize = 256;
7
8static INTERNER: Lazy<sync::Mutex<Interner>> = Lazy::new(|| {
9    let mut interner = Interner {
10        str_to_idx: HashMap::new(),
11        idx_to_str: Vec::new(),
12    };
13    for i in 0..INDEXD_BUF_SIZE {
14        let s = format!("x_{}", i);
15        let s = s.leak();
16        interner.str_to_idx.insert(s.to_string(), i);
17        interner.idx_to_str.push(s);
18    }
19    sync::Mutex::new(interner)
20});
21
22struct Interner {
23    str_to_idx: HashMap<String, usize>,
24    idx_to_str: Vec<&'static str>,
25}
26
27#[derive(Clone, Copy, Hash, Eq, PartialEq, PartialOrd, Ord)]
28pub struct InternStr(usize);
29impl InternStr {
30    pub fn new<S: AsRef<str>>(s: S) -> InternStr {
31        let mut interner = INTERNER.lock().unwrap();
32        if let Some(idx) = interner.str_to_idx.get(s.as_ref()) {
33            InternStr(*idx)
34        } else {
35            let s = s.as_ref().to_string();
36            let idx = interner.idx_to_str.len();
37            interner.str_to_idx.insert(s.clone(), idx);
38            interner.idx_to_str.push(Box::leak(Box::new(s)));
39            InternStr(idx)
40        }
41    }
42
43    pub fn indexd(idx: usize) -> InternStr {
44        if idx < INDEXD_BUF_SIZE {
45            InternStr(idx)
46        } else {
47            InternStr::new(format!("x_{}", idx).as_str())
48        }
49    }
50
51    pub fn as_str(&self) -> &'static str {
52        let interner = INTERNER.lock().unwrap();
53        interner.idx_to_str[self.0]
54    }
55}
56
57impl fmt::Debug for InternStr {
58    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59        write!(f, "{}", self.as_str())
60    }
61}
62
63impl fmt::Display for InternStr {
64    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65        write!(f, "{}", self.as_str())
66    }
67}
68
69impl AsRef<str> for InternStr {
70    fn as_ref(&self) -> &str {
71        self.as_str()
72    }
73}
74
75#[test]
76fn intern_new_test() {
77    // test function InternStr::new()
78    let foo1: &str = "foo";
79    let foo2: String = "foo".to_string();
80    let bar1: &str = "bar";
81    let bar2: String = "bar".to_string();
82    let s1 = InternStr::new(&foo1);
83    let s2 = InternStr::new(&foo2);
84    let s3 = InternStr::new(&bar1);
85    let s4 = InternStr::new(&bar2);
86    assert_eq!(s1, s2);
87    assert_eq!(s3, s4);
88    assert_ne!(s1, s3);
89    assert_ne!(s2, s4);
90    assert_eq!(format!("{}", s1), "foo");
91    assert_eq!(format!("{}", s2), "foo");
92    assert_eq!(format!("{}", s3), "bar");
93    assert_eq!(format!("{}", s4), "bar");
94}
95
96#[test]
97fn intern_indexd_test() {
98    let s1 = InternStr::indexd(42);
99    let s2 = InternStr::indexd(42);
100    let s3 = InternStr::indexd(500);
101    let s4 = InternStr::indexd(500);
102    assert_eq!(s1, s2);
103    assert_eq!(s3, s4);
104    assert_ne!(s1, s3);
105    assert_ne!(s2, s4);
106    assert_eq!(format!("{}", s1), "x_42");
107    assert_eq!(format!("{}", s2), "x_42");
108    assert_eq!(format!("{}", s3), "x_500");
109    assert_eq!(format!("{}", s4), "x_500");
110}