1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! Contains definitions of symbols -- internalized strings which support fast hashing and
//! comparison.

use std::{
    cell::RefCell,
    collections::HashMap,
    fmt,
    fmt::{Error, Formatter},
    rc::Rc,
};

/// Representation of a symbol.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub struct Symbol(usize);

impl Symbol {
    pub fn display<'a>(&'a self, pool: &'a SymbolPool) -> SymbolDisplay<'a> {
        SymbolDisplay { sym: self, pool }
    }
}

/// A helper to support symbols in formatting.
pub struct SymbolDisplay<'a> {
    sym: &'a Symbol,
    pool: &'a SymbolPool,
}

impl<'a> fmt::Display for SymbolDisplay<'a> {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
        f.write_str(&self.pool.string(*self.sym))
    }
}

/// A pool of symbols. Allows to lookup a symbol by a string representation, and discover
/// the string representation of an existing symbol. This struct does not need be mutable
/// for operations on it, which is important so references to it can be freely passed around.
#[derive(Debug)]
pub struct SymbolPool {
    inner: RefCell<InnerPool>,
}

#[derive(Debug)]
struct InnerPool {
    strings: Vec<Rc<String>>,
    lookup: HashMap<Rc<String>, usize>,
}

impl SymbolPool {
    /// Creates a new SymbolPool.
    pub fn new() -> SymbolPool {
        SymbolPool {
            inner: RefCell::new(InnerPool {
                strings: vec![],
                lookup: HashMap::new(),
            }),
        }
    }

    /// Looks up a symbol by its string representation. If a symbol with this representation
    /// already exists, it will be returned, otherwise a new one will be created in the
    /// pool. The implementation uses internally a RefCell for storing symbols, so the pool
    /// does not need to be mutable.
    pub fn make(&self, s: &str) -> Symbol {
        let mut pool = self.inner.borrow_mut();
        let key = Rc::new(s.to_string());
        if let Some(n) = pool.lookup.get(&key) {
            return Symbol(*n);
        }
        let new_sym = pool.strings.len();
        pool.strings.push(key.clone());
        pool.lookup.insert(key, new_sym);
        Symbol(new_sym)
    }

    /// Returns the string representation of this symbol, as an rc'ed string to avoid copies.
    /// If the past symbol was not created from this pool, a runtime error may happen (or a wrong
    /// string will be returned).
    pub fn string(&self, sym: Symbol) -> Rc<String> {
        self.inner.borrow().strings[sym.0].clone()
    }
}

impl Default for SymbolPool {
    fn default() -> Self {
        Self::new()
    }
}