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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::Deref;

use crate::base::fnv::FnvMap;

use crate::{
    forget_lifetime,
    gc::{CloneUnrooted, CopyUnrooted, Gc, Trace},
    value::GcStr,
    Result,
};

/// Interned strings which allow for fast equality checks and hashing
#[derive(Eq)]
pub struct InternedStr(GcStr);

unsafe impl Send for InternedStr {}
unsafe impl Sync for InternedStr {}

unsafe impl CopyUnrooted for InternedStr {}
impl CloneUnrooted for InternedStr {
    type Value = Self;
    unsafe fn clone_unrooted(&self) -> Self::Value {
        self.copy_unrooted()
    }
}

// InternedStr are explicitly scanned in the intern table so we can skip them when they are
// encountered elsewhere
unsafe impl Trace for InternedStr {
    impl_trace! { self, _gc, {} }
}

impl PartialEq<InternedStr> for InternedStr {
    fn eq(&self, other: &InternedStr) -> bool {
        self.as_ptr() == other.as_ptr()
    }
}

impl<'a> PartialEq<&'a str> for InternedStr {
    fn eq(&self, other: &&'a str) -> bool {
        **self == **other
    }
}

impl PartialOrd for InternedStr {
    fn partial_cmp(&self, other: &InternedStr) -> Option<Ordering> {
        self.as_ptr().partial_cmp(&other.as_ptr())
    }
}

impl Ord for InternedStr {
    fn cmp(&self, other: &InternedStr) -> Ordering {
        self.as_ptr().cmp(&other.as_ptr())
    }
}

impl Hash for InternedStr {
    fn hash<H>(&self, hasher: &mut H)
    where
        H: Hasher,
    {
        self.as_ptr().hash(hasher)
    }
}

impl Deref for InternedStr {
    type Target = str;
    fn deref(&self) -> &str {
        &self.0
    }
}

impl AsRef<str> for InternedStr {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl InternedStr {
    pub fn inner(&self) -> &GcStr {
        &self.0
    }
}

#[cfg_attr(feature = "serde_derive", derive(DeserializeState, SerializeState))]
#[cfg_attr(
    feature = "serde_derive",
    serde(
        deserialize_state = "crate::serialization::DeSeed<'gc>",
        de_parameters = "'gc"
    )
)]
#[cfg_attr(
    feature = "serde_derive",
    serde(serialize_state = "crate::serialization::SeSeed")
)]
pub struct Interner {
    // Interned strings that are referenced will be inserted anyway so we can skip serializing this
    #[cfg_attr(feature = "serde_derive", serde(skip))]
    // For this map and this map only we can't use InternedStr as keys since the hash should
    // not be expected to be the same as ordinary strings, we use a transmute to &'static str to
    // have the keys as strings without any unsafety as the keys do not escape the interner and they
    // live as long as their values
    indexes: FnvMap<&'static str, InternedStr>,
}

unsafe impl Trace for Interner {
    impl_trace! { self, gc,
        match self {
            Self { indexes, .. } =>
                for (_, v) in indexes {
                    match v {
                        InternedStr(x) => mark::<GcStr>(x, gc),
                    }
                },
        }
    }
}

impl Interner {
    pub fn new() -> Interner {
        Interner {
            indexes: FnvMap::default(),
        }
    }

    pub fn intern(&mut self, gc: &mut Gc, s: &str) -> Result<InternedStr> {
        // SAFETY The interner is scanned
        unsafe {
            match self.indexes.get(s) {
                Some(interned_str) => return Ok(interned_str.clone_unrooted()),
                None => (),
            }

            let gc_str = InternedStr(gc.alloc(s)?.unrooted());

            // The key will live as long as the value it refers to and the static str never escapes
            // outside interner so this is safe
            let key: &'static str = forget_lifetime(&gc_str);
            self.indexes.insert(key, gc_str.clone_unrooted());
            Ok(gc_str)
        }
    }
}

impl fmt::Debug for InternedStr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "InternedStr({:p}, {:?})", &*self.0, self.0)
    }
}
impl fmt::Display for InternedStr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", &self[..])
    }
}