Skip to main content

polydat_core/kernel/
intern.rs

1// Copyright 2024-2026 Jonathan Shook
2// SPDX-License-Identifier: Apache-2.0
3
4//! The static string interner: workload-compile-time string constants
5//! with process lifetime. Every string literal, `Const<&str>` argument,
6//! and tile static run is interned at kernel build; a compiled step
7//! that produces such a constant publishes a `(ptr, len)` pair to the
8//! interned bytes, which never move and are never freed, so the pair
9//! has a proven owner for the life of the process (jit_boundary.md,
10//! axiom S7). Interning the same text twice yields the same bytes.
11
12use std::sync::RwLock;
13
14/// Global static string interner for compile-time constants.
15pub struct StaticInterner;
16
17struct StaticTable {
18    entries: Vec<&'static str>,
19    index: std::collections::HashMap<&'static str, u32>,
20}
21
22static STATIC_STRINGS: RwLock<Option<StaticTable>> = RwLock::new(None);
23
24impl StaticInterner {
25    /// Intern a string and return its static slice.
26    pub fn intern(s: &str) -> &'static str {
27        // One read lock, released before the write lock is taken: a
28        // second read taken while a writer waits would deadlock.
29        let found: Option<&'static str> = STATIC_STRINGS
30            .read()
31            .unwrap()
32            .as_ref()
33            .and_then(|t| t.index.get(s).map(|&id| t.entries[id as usize]));
34        if let Some(text) = found {
35            return text;
36        }
37        let mut guard = STATIC_STRINGS.write().unwrap();
38        let table = guard.get_or_insert_with(|| StaticTable {
39            entries: Vec::new(),
40            index: std::collections::HashMap::new(),
41        });
42        if let Some(&id) = table.index.get(s) {
43            return table.entries[id as usize];
44        }
45        let leaked: &'static str = Box::leak(s.to_string().into_boxed_str());
46        let id = table.entries.len() as u32;
47        table.entries.push(leaked);
48        table.index.insert(leaked, id);
49        leaked
50    }
51
52    /// The interned string with id `id`; empty when there is none.
53    pub fn resolve(id: u32) -> &'static str {
54        STATIC_STRINGS
55            .read()
56            .unwrap()
57            .as_ref()
58            .and_then(|t| t.entries.get(id as usize).copied())
59            .unwrap_or("")
60    }
61
62    /// Number of distinct strings interned so far.
63    pub fn len() -> usize {
64        STATIC_STRINGS
65            .read()
66            .unwrap()
67            .as_ref()
68            .map_or(0, |t| t.entries.len())
69    }
70}
71
72/// The `(ptr, len)` pair of a static string, as a `Ref2` output slot
73/// pair publishes it.
74#[inline]
75pub fn static_pair(s: &'static str) -> (u64, u64) {
76    (s.as_ptr() as usize as u64, s.len() as u64)
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn interning_dedups_and_keeps_bytes_in_place() {
85        let a = StaticInterner::intern("intern-test-constant");
86        let b = StaticInterner::intern("intern-test-constant");
87        assert_eq!(
88            a.as_ptr(),
89            b.as_ptr(),
90            "the same text interns to the same bytes"
91        );
92        assert_eq!(a, "intern-test-constant");
93        let (p, l) = static_pair(a);
94        assert_eq!(p, a.as_ptr() as usize as u64);
95        assert_eq!(l, a.len() as u64);
96    }
97}