prune_lang/utils/
intern.rs1use 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 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}