plotnik_lib/ir/
strings.rs1use std::collections::HashMap;
7
8use super::ids::StringId;
9
10#[derive(Debug, Default)]
15pub struct StringInterner<'src> {
16 map: HashMap<&'src str, StringId>,
18 strings: Vec<&'src str>,
20}
21
22impl<'src> StringInterner<'src> {
23 pub fn new() -> Self {
25 Self::default()
26 }
27
28 pub fn intern(&mut self, s: &'src str) -> StringId {
32 if let Some(&id) = self.map.get(s) {
33 return id;
34 }
35
36 let id = self.strings.len() as StringId;
37 assert!(id < 0xFFFF, "string pool overflow (>65534 strings)");
38
39 self.map.insert(s, id);
40 self.strings.push(s);
41 id
42 }
43
44 pub fn get(&self, s: &str) -> Option<StringId> {
46 self.map.get(s).copied()
47 }
48
49 pub fn resolve(&self, id: StringId) -> &'src str {
54 self.strings[id as usize]
55 }
56
57 pub fn len(&self) -> usize {
59 self.strings.len()
60 }
61
62 pub fn is_empty(&self) -> bool {
64 self.strings.is_empty()
65 }
66
67 pub fn iter(&self) -> impl Iterator<Item = (StringId, &'src str)> + '_ {
69 self.strings
70 .iter()
71 .enumerate()
72 .map(|(i, s)| (i as StringId, *s))
73 }
74
75 pub fn total_bytes(&self) -> usize {
77 self.strings.iter().map(|s| s.len()).sum()
78 }
79
80 pub fn into_strings(self) -> Vec<&'src str> {
82 self.strings
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn intern_deduplicates() {
92 let mut interner = StringInterner::new();
93
94 let id1 = interner.intern("foo");
95 let id2 = interner.intern("bar");
96 let id3 = interner.intern("foo");
97
98 assert_eq!(id1, 0);
99 assert_eq!(id2, 1);
100 assert_eq!(id3, 0); assert_eq!(interner.len(), 2);
102 }
103
104 #[test]
105 fn resolve_works() {
106 let mut interner = StringInterner::new();
107 interner.intern("hello");
108 interner.intern("world");
109
110 assert_eq!(interner.resolve(0), "hello");
111 assert_eq!(interner.resolve(1), "world");
112 }
113
114 #[test]
115 fn get_returns_none_for_unknown() {
116 let interner = StringInterner::new();
117 assert_eq!(interner.get("unknown"), None);
118 }
119
120 #[test]
121 fn total_bytes() {
122 let mut interner = StringInterner::new();
123 interner.intern("foo"); interner.intern("hello"); interner.intern("foo"); assert_eq!(interner.total_bytes(), 8);
128 }
129
130 #[test]
131 fn iter_order() {
132 let mut interner = StringInterner::new();
133 interner.intern("a");
134 interner.intern("b");
135 interner.intern("c");
136
137 let pairs: Vec<_> = interner.iter().collect();
138 assert_eq!(pairs, vec![(0, "a"), (1, "b"), (2, "c")]);
139 }
140}