1use std::collections::HashMap;
25
26pub struct InternPool {
28 map: HashMap<String, u32>,
30 strings: Vec<String>,
32}
33
34impl InternPool {
35 #[inline]
37 pub fn new() -> Self {
38 Self {
39 map: HashMap::new(),
40 strings: Vec::new(),
41 }
42 }
43
44 #[inline]
46 pub fn with_capacity(capacity: usize) -> Self {
47 Self {
48 map: HashMap::with_capacity(capacity),
49 strings: Vec::with_capacity(capacity),
50 }
51 }
52
53 pub fn intern(&mut self, s: &str) -> u32 {
58 if let Some(&idx) = self.map.get(s) {
59 return idx;
60 }
61
62 let idx = self.strings.len() as u32;
63 self.strings.push(s.to_string());
64 self.map.insert(s.to_string(), idx);
65 idx
66 }
67
68 #[inline]
70 pub fn get(&self, idx: u32) -> Option<&str> {
71 self.strings.get(idx as usize).map(|s| s.as_str())
72 }
73
74 #[inline]
76 pub fn len(&self) -> usize {
77 self.strings.len()
78 }
79
80 #[inline]
82 pub fn is_empty(&self) -> bool {
83 self.strings.is_empty()
84 }
85
86 pub fn serialize(&self) -> Vec<u8> {
94 if self.strings.is_empty() {
95 return vec![0, 0, 0, 0, 0, 0, 0, 0];
97 }
98
99 let count = self.strings.len() as u32;
100 let mut offsets = Vec::with_capacity(self.strings.len());
101 let mut data = Vec::new();
102
103 for s in &self.strings {
105 offsets.push(data.len() as u32);
106 data.extend_from_slice(s.as_bytes());
107 }
108
109 let pool_size = 8 + (offsets.len() * 4) + data.len();
111
112 let mut bytes = Vec::with_capacity(pool_size);
113
114 bytes.extend_from_slice(&(pool_size as u32).to_le_bytes());
116
117 bytes.extend_from_slice(&count.to_le_bytes());
119
120 for offset in offsets {
122 bytes.extend_from_slice(&offset.to_le_bytes());
123 }
124
125 bytes.extend_from_slice(&data);
127
128 bytes
129 }
130
131 pub fn deserialize(bytes: &[u8]) -> Result<(Self, usize), InternError> {
135 if bytes.len() < 8 {
136 return Err(InternError::BufferTooSmall);
137 }
138
139 let pool_size = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as usize;
141
142 if pool_size == 0 {
143 return Ok((Self::new(), 8));
145 }
146
147 if bytes.len() < pool_size {
148 return Err(InternError::BufferTooSmall);
149 }
150
151 let count = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]) as usize;
153
154 if count == 0 {
155 return Ok((Self::new(), pool_size));
156 }
157
158 let offsets_start = 8;
160 let offsets_end = offsets_start + (count * 4);
161
162 if bytes.len() < offsets_end {
163 return Err(InternError::BufferTooSmall);
164 }
165
166 let mut offsets = Vec::with_capacity(count);
167 for i in 0..count {
168 let offset_pos = offsets_start + (i * 4);
169 let offset = u32::from_le_bytes([
170 bytes[offset_pos],
171 bytes[offset_pos + 1],
172 bytes[offset_pos + 2],
173 bytes[offset_pos + 3],
174 ]) as usize;
175 offsets.push(offset);
176 }
177
178 let data_start = offsets_end;
180 let data = &bytes[data_start..pool_size];
181
182 let mut strings = Vec::with_capacity(count);
184 let mut map = HashMap::with_capacity(count);
185
186 for (idx, &offset) in offsets.iter().enumerate() {
187 let end = if idx + 1 < offsets.len() {
188 offsets[idx + 1]
189 } else {
190 data.len()
191 };
192
193 if offset > data.len() || end > data.len() || offset > end {
194 return Err(InternError::InvalidOffset);
195 }
196
197 let s = std::str::from_utf8(&data[offset..end])
198 .map_err(|_| InternError::InvalidUtf8)?
199 .to_string();
200
201 map.insert(s.clone(), idx as u32);
202 strings.push(s);
203 }
204
205 Ok((Self { map, strings }, pool_size))
206 }
207}
208
209impl Default for InternPool {
210 fn default() -> Self {
211 Self::new()
212 }
213}
214
215#[derive(Debug, Clone, PartialEq, Eq)]
217pub enum InternError {
218 BufferTooSmall,
220 InvalidOffset,
222 InvalidUtf8,
224}
225
226impl std::fmt::Display for InternError {
227 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
228 match self {
229 Self::BufferTooSmall => write!(f, "Buffer too small to contain intern pool"),
230 Self::InvalidOffset => write!(f, "Invalid string offset in intern pool"),
231 Self::InvalidUtf8 => write!(f, "Invalid UTF-8 in intern pool string data"),
232 }
233 }
234}
235
236impl std::error::Error for InternError {}
237
238pub struct InterningSerializer {
240 pool: InternPool,
241}
242
243impl InterningSerializer {
244 #[inline]
246 pub fn new() -> Self {
247 Self {
248 pool: InternPool::new(),
249 }
250 }
251
252 #[inline]
254 pub fn with_capacity(capacity: usize) -> Self {
255 Self {
256 pool: InternPool::with_capacity(capacity),
257 }
258 }
259
260 #[inline]
262 pub fn pool(&self) -> &InternPool {
263 &self.pool
264 }
265
266 #[inline]
268 pub fn pool_mut(&mut self) -> &mut InternPool {
269 &mut self.pool
270 }
271
272 #[inline]
274 pub fn intern(&mut self, s: &str) -> u32 {
275 self.pool.intern(s)
276 }
277
278 #[inline]
280 pub fn serialize_pool(&self) -> Vec<u8> {
281 self.pool.serialize()
282 }
283}
284
285impl Default for InterningSerializer {
286 fn default() -> Self {
287 Self::new()
288 }
289}
290
291pub struct InterningDeserializer {
293 pool: InternPool,
294}
295
296impl InterningDeserializer {
297 pub fn new(bytes: &[u8]) -> Result<(Self, usize), InternError> {
299 let (pool, consumed) = InternPool::deserialize(bytes)?;
300 Ok((Self { pool }, consumed))
301 }
302
303 #[inline]
305 pub fn get(&self, idx: u32) -> Option<&str> {
306 self.pool.get(idx)
307 }
308
309 #[inline]
311 pub fn pool(&self) -> &InternPool {
312 &self.pool
313 }
314}
315
316#[cfg(test)]
317mod tests {
318 use super::*;
319
320 #[test]
321 fn test_intern_pool_basic() {
322 let mut pool = InternPool::new();
323
324 let idx1 = pool.intern("hello");
325 let idx2 = pool.intern("world");
326 let idx3 = pool.intern("hello"); assert_eq!(idx1, 0);
329 assert_eq!(idx2, 1);
330 assert_eq!(idx3, 0); assert_eq!(pool.get(0), Some("hello"));
333 assert_eq!(pool.get(1), Some("world"));
334 assert_eq!(pool.get(2), None);
335 }
336
337 #[test]
338 fn test_intern_pool_roundtrip() {
339 let mut pool = InternPool::new();
340 pool.intern("foo");
341 pool.intern("bar");
342 pool.intern("baz");
343 pool.intern("foo"); let bytes = pool.serialize();
346 let (deserialized, consumed) = InternPool::deserialize(&bytes).unwrap();
347
348 assert_eq!(consumed, bytes.len());
349 assert_eq!(deserialized.len(), 3); assert_eq!(deserialized.get(0), Some("foo"));
351 assert_eq!(deserialized.get(1), Some("bar"));
352 assert_eq!(deserialized.get(2), Some("baz"));
353 }
354
355 #[test]
356 fn test_empty_pool() {
357 let pool = InternPool::new();
358 let bytes = pool.serialize();
359
360 let (deserialized, consumed) = InternPool::deserialize(&bytes).unwrap();
361 assert_eq!(consumed, 8);
362 assert!(deserialized.is_empty());
363 }
364
365 #[test]
366 fn test_interning_serializer() {
367 let mut serializer = InterningSerializer::new();
368
369 let idx1 = serializer.intern("test");
370 let idx2 = serializer.intern("data");
371 let idx3 = serializer.intern("test");
372
373 assert_eq!(idx1, 0);
374 assert_eq!(idx2, 1);
375 assert_eq!(idx3, 0);
376
377 let bytes = serializer.serialize_pool();
378 let (deserializer, _) = InterningDeserializer::new(&bytes).unwrap();
379
380 assert_eq!(deserializer.get(0), Some("test"));
381 assert_eq!(deserializer.get(1), Some("data"));
382 }
383
384 #[test]
385 fn test_unicode_strings() {
386 let mut pool = InternPool::new();
387 pool.intern("Hello 世界");
388 pool.intern("🦀 Rust");
389 pool.intern("Привет");
390
391 let bytes = pool.serialize();
392 let (deserialized, _) = InternPool::deserialize(&bytes).unwrap();
393
394 assert_eq!(deserialized.get(0), Some("Hello 世界"));
395 assert_eq!(deserialized.get(1), Some("🦀 Rust"));
396 assert_eq!(deserialized.get(2), Some("Привет"));
397 }
398
399 #[test]
400 fn test_large_pool() {
401 let mut pool = InternPool::new();
402
403 for i in 0..1000 {
405 pool.intern(&format!("string_{}", i));
406 }
407
408 for i in 0..500 {
410 pool.intern(&format!("string_{}", i));
411 }
412
413 assert_eq!(pool.len(), 1000); let bytes = pool.serialize();
416 let (deserialized, _) = InternPool::deserialize(&bytes).unwrap();
417
418 assert_eq!(deserialized.len(), 1000);
419 for i in 0..1000 {
420 assert_eq!(deserialized.get(i), Some(format!("string_{}", i).as_str()));
421 }
422 }
423}