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
// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! A global, uniqued cache of strings that is never purged. Inspired by
//! [servo/string-cache].
//!
//! This module provides storage for strings that are meant to remain in use for
//! the entire running duration of a program. Strings that are stored in this
//! global, static cache are never evicted, and so the memory consumed by them
//! can only ever grow.
//!
//! The strings can be accessed via the [`Symbol`] type, which acts as a pointer
//! to the underlying string data.
//!
//! NOTE: If you're looking for a `#[forbid(unsafe_code)]` attribute here, you
//! won't find one: symbol-pool (and its inspiration, servo/string-cache) uses
//! `unsafe` Rust in order to store and dereference `Symbol` pointers to
//! strings.
//!
//! [servo/string-cache]: https://github.com/servo/string-cache
//! [`Symbol`]: crate::Symbol

mod pool;
pub mod symbol;

use once_cell::sync::Lazy;
use pool::Pool;
use std::sync::Mutex;

pub use symbol::Symbol;

/// The global, unique cache of strings.
pub(crate) static SYMBOL_POOL: Lazy<Mutex<Pool>> = Lazy::new(|| Mutex::new(Pool::new()));

#[cfg(test)]
mod tests {
    use crate::{Pool, Symbol, SYMBOL_POOL};
    use std::mem::replace;

    #[test]
    fn test_serialization() {
        // Internally, a Symbol behaves like a pointer. Naively serializing it
        // as an address in the pool is incorrect, as it may be serialized by
        // one process with its own pool, and deserialized by another process
        // with a different pool.
        let s = Symbol::from("serialize me!");
        let serialized = serde_json::to_string(&s).unwrap();

        // Artificially reset the pool for testing purposes. The address pointed
        // to by the Symbol is now no longer valid.
        let _ = replace(&mut SYMBOL_POOL.lock().unwrap().0, Pool::new().0);

        // Below, test that deserialization still succeeds.
        let deserialized: Symbol = serde_json::from_str(&serialized).unwrap();
        assert_eq!(deserialized.as_str(), "serialize me!");
    }
}