Skip to main content

telltale_vm/
intern.rs

1//! String interning for hot runtime paths.
2
3use serde::{Deserialize, Serialize};
4use std::collections::BTreeMap;
5
6/// Stable identifier for interned runtime strings.
7pub type StringId = u32;
8
9/// Runtime symbol table used to intern repeated role/label strings.
10#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11pub struct SymbolTable {
12    symbols: Vec<String>,
13    index: BTreeMap<String, StringId>,
14}
15
16impl SymbolTable {
17    /// Create an empty symbol table.
18    #[must_use]
19    pub fn new() -> Self {
20        Self::default()
21    }
22
23    /// Intern a string and return its stable id.
24    pub fn intern(&mut self, value: &str) -> StringId {
25        if let Some(id) = self.index.get(value) {
26            return *id;
27        }
28        let id = match u32::try_from(self.symbols.len()) {
29            Ok(id) => id,
30            Err(_) => return u32::MAX,
31        };
32        let owned = value.to_string();
33        self.symbols.push(owned.clone());
34        self.index.insert(owned, id);
35        id
36    }
37
38    /// Resolve an id to a string, if present.
39    #[must_use]
40    #[allow(clippy::as_conversions)]
41    pub fn resolve(&self, id: StringId) -> Option<&str> {
42        // u32 -> usize is always safe on 32-bit or larger platforms
43        self.symbols.get(id as usize).map(String::as_str)
44    }
45
46    /// Number of interned symbols.
47    #[must_use]
48    pub fn len(&self) -> usize {
49        self.symbols.len()
50    }
51
52    /// Whether the table is empty.
53    #[must_use]
54    pub fn is_empty(&self) -> bool {
55        self.symbols.is_empty()
56    }
57}