Skip to main content

libperl_macrogen/
intern.rs

1use std::collections::HashMap;
2
3/// インターン済み文字列の識別子
4#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug, Default)]
5pub struct InternedStr(u32);
6
7impl InternedStr {
8    /// 内部IDを取得(デバッグ用)
9    pub fn as_u32(self) -> u32 {
10        self.0
11    }
12}
13
14/// 文字列インターナー
15#[derive(Clone, Debug, Default)]
16pub struct StringInterner {
17    strings: Vec<String>,
18    map: HashMap<String, InternedStr>,
19}
20
21impl StringInterner {
22    /// 新しいインターナーを作成
23    pub fn new() -> Self {
24        Self {
25            strings: Vec::new(),
26            map: HashMap::new(),
27        }
28    }
29
30    /// 文字列をインターンし、IDを返す
31    pub fn intern(&mut self, s: &str) -> InternedStr {
32        if let Some(&id) = self.map.get(s) {
33            return id;
34        }
35        let id = InternedStr(self.strings.len() as u32);
36        self.strings.push(s.to_owned());
37        self.map.insert(s.to_owned(), id);
38        id
39    }
40
41    /// IDから文字列を取得
42    pub fn get(&self, id: InternedStr) -> &str {
43        &self.strings[id.0 as usize]
44    }
45
46    /// 文字列がインターン済みか検索(新規登録しない)
47    pub fn lookup(&self, s: &str) -> Option<InternedStr> {
48        self.map.get(s).copied()
49    }
50
51    /// インターン済み文字列の数を返す
52    pub fn len(&self) -> usize {
53        self.strings.len()
54    }
55
56    /// インターナーが空かどうか
57    pub fn is_empty(&self) -> bool {
58        self.strings.is_empty()
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    #[test]
67    fn test_intern_new_string() {
68        let mut interner = StringInterner::new();
69        let id1 = interner.intern("hello");
70        let id2 = interner.intern("world");
71
72        assert_ne!(id1, id2);
73        assert_eq!(interner.get(id1), "hello");
74        assert_eq!(interner.get(id2), "world");
75    }
76
77    #[test]
78    fn test_intern_same_string() {
79        let mut interner = StringInterner::new();
80        let id1 = interner.intern("hello");
81        let id2 = interner.intern("hello");
82
83        assert_eq!(id1, id2);
84        assert_eq!(interner.len(), 1);
85    }
86
87    #[test]
88    fn test_intern_empty_string() {
89        let mut interner = StringInterner::new();
90        let id = interner.intern("");
91        assert_eq!(interner.get(id), "");
92    }
93}