behavior_tree_lite/
symbol.rs

1//! Our custom implementation of symbol.
2//!
3//! Copied and modified from <https://github.com/remexre/symbol-rs>
4//! to conform to modern Rust and drop support for no-std
5
6use ::once_cell::sync::{Lazy, OnceCell};
7use std::cmp::Ordering;
8use std::collections::HashSet;
9use std::fmt::{Debug, Display, Formatter, Result as FmtResult};
10use std::ops::Deref;
11use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
12use std::sync::Mutex;
13
14static SYMBOL_HEAP: Lazy<Mutex<HashSet<&'static str>>> = Lazy::new(|| Mutex::new(HashSet::new()));
15
16/// An interned string with O(1) equality.
17#[allow(clippy::derived_hash_with_manual_eq)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize))]
19#[cfg_attr(feature = "serde", serde(transparent))]
20#[derive(Clone, Copy, Eq, Hash)]
21pub struct Symbol {
22    s: &'static str,
23}
24
25impl Symbol {
26    /// Retrieves the address of the backing string.
27    pub fn addr(self) -> usize {
28        self.s.as_ptr() as usize
29    }
30
31    /// Retrieves the string from the Symbol.
32    pub fn as_str(self) -> &'static str {
33        self.s
34    }
35
36    /// Generates a new symbol with a name of the form `G#n`, where `n` is some positive integer.
37    pub fn gensym() -> Symbol {
38        static COUNTER: OnceCell<AtomicUsize> = OnceCell::with_value(AtomicUsize::new(0));
39        let counter = COUNTER.get().unwrap();
40
41        let n = if let Ok(mut heap) = SYMBOL_HEAP.lock() {
42            loop {
43                let n = leak_string(format!(
44                    "G#{}",
45                    counter.fetch_add(1, AtomicOrdering::SeqCst)
46                ));
47                if heap.insert(n) {
48                    break n;
49                }
50            }
51        } else {
52            panic!("SYMBOL_HEAP is not initialized");
53        };
54
55        Symbol::from(n)
56    }
57
58    pub fn count() -> usize {
59        SYMBOL_HEAP.lock().unwrap().len()
60    }
61}
62
63impl Debug for Symbol {
64    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
65        Debug::fmt(self.s, fmt)
66    }
67}
68
69impl Deref for Symbol {
70    type Target = str;
71    fn deref(&self) -> &str {
72        self.s
73    }
74}
75
76impl Display for Symbol {
77    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
78        fmt.write_str(self.s)
79    }
80}
81
82impl<S: AsRef<str>> From<S> for Symbol {
83    fn from(s: S) -> Symbol {
84        let s = s.as_ref();
85        let s = {
86            let mut heap = SYMBOL_HEAP.lock().unwrap();
87            if let Some(s) = heap.get(s) {
88                s
89            } else {
90                let s = leak_string(s.to_owned());
91                heap.insert(s);
92                s
93            }
94        };
95        Symbol { s }
96    }
97}
98
99impl Ord for Symbol {
100    fn cmp(&self, other: &Self) -> Ordering {
101        let l = self.addr();
102        let r = other.addr();
103        l.cmp(&r)
104    }
105}
106
107impl PartialEq for Symbol {
108    fn eq(&self, other: &Self) -> bool {
109        self.cmp(other) == Ordering::Equal
110    }
111}
112
113impl PartialOrd for Symbol {
114    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
115        Some(self.cmp(other))
116    }
117}
118
119impl<S: AsRef<str>> PartialEq<S> for Symbol {
120    fn eq(&self, other: &S) -> bool {
121        self.partial_cmp(&other.as_ref()) == Some(Ordering::Equal)
122    }
123}
124
125impl<S: AsRef<str>> PartialOrd<S> for Symbol {
126    fn partial_cmp(&self, other: &S) -> Option<Ordering> {
127        self.s.partial_cmp(other.as_ref())
128    }
129}
130
131fn leak_string(s: String) -> &'static str {
132    Box::leak(s.into_boxed_str())
133}