Skip to main content

prune_lang/utils/
intern.rs

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