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
use std::any::{Any, TypeId};
use std::borrow::Borrow;
use std::hash::Hash;
use std::ops::DerefMut;
use std::sync::{Arc, Mutex};

use hashbrown::HashMap;

use super::token::Tok;
use super::typed_interner::TypedInterner;

/// Operations that can be executed on [TypedInterner] without knowing its
/// concrete type
pub trait AnyInterner: Send + Sync {
  fn as_any_arc(self: Arc<Self>) -> Arc<dyn Any + Send + Sync>;
  fn sweep(&self) -> usize;
}

impl<T: Eq + Hash + Clone + Send + Sync + 'static> AnyInterner
  for TypedInterner<T>
{
  fn as_any_arc(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> { self }
  fn sweep(&self) -> usize { TypedInterner::sweep(self) }
}

/// A collection of interners based on their type. Can be used to intern any
/// object that implements [ToOwned]. Objects of the same type are stored
/// together in a [TypedInterner]
pub struct Interner {
  interners: Mutex<HashMap<TypeId, Arc<dyn AnyInterner>>>,
}
impl Interner {
  /// Create a new interner
  #[must_use]
  pub fn new() -> Self { Self { interners: Mutex::new(HashMap::new()) } }

  /// Intern something
  #[must_use]
  pub fn i<Q: ?Sized + Eq + Hash + ToOwned>(&self, q: &Q) -> Tok<Q::Owned>
  where
    Q::Owned: 'static + Eq + Hash + Clone + Borrow<Q> + Send + Sync,
  {
    let mut interners = self.interners.lock().unwrap();
    let interner = get_interner(&mut interners);
    interner.i(q)
  }

  /// Fully resolve a list of interned things. If the list is interned, use
  /// [Tok::ev]
  #[must_use]
  pub fn ev<'a, T: 'static + Eq + Hash + Clone>(
    s: impl IntoIterator<Item = &'a Tok<T>>,
  ) -> Vec<T> {
    s.into_iter().map(|t| (**t).clone()).collect()
  }

  /// Sweep values of a specific type. Useful if you just
  /// constructed a large number of values of a specific type, otherwise use
  /// [Interner::sweep]
  pub fn sweep_type<T: Eq + Hash + Clone + Send + Sync + 'static>(
    &self,
  ) -> usize {
    match self.interners.lock().unwrap().get(&TypeId::of::<T>()) {
      None => 0,
      Some(interner) => interner.sweep(),
    }
  }

  /// Sweep all interners
  pub fn sweep(&self) -> usize {
    self.interners.lock().unwrap().values().map(|v| v.sweep()).sum()
  }

  /// Intern a list and its elements. See also [Interner::ibv]
  pub fn iv<T: 'static + Eq + Hash + Clone + Sync + Send>(
    &self,
    s: impl IntoIterator<Item = T>,
  ) -> Tok<Vec<Tok<T>>> {
    self.i(&s.into_iter().map(|t| self.i(&t)).collect::<Vec<_>>())
  }

  /// Intern a list of borrowed items. See also [Interner::iv]
  pub fn ibv<'a, Q: ?Sized + Eq + Hash + ToOwned + 'a>(
    &self,
    s: impl IntoIterator<Item = &'a Q>,
  ) -> Tok<Vec<Tok<Q::Owned>>>
  where
    Q::Owned: Eq + Hash + Clone + Send + Sync,
  {
    self.i(&s.into_iter().map(|t| self.i(t)).collect::<Vec<_>>())
  }
}

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

/// Get or create an interner for a given type
#[must_use]
fn get_interner<T: 'static + Eq + Hash + Clone + Send + Sync>(
  interners: &mut impl DerefMut<Target = HashMap<TypeId, Arc<dyn AnyInterner>>>,
) -> Arc<TypedInterner<T>> {
  let boxed = interners
    .raw_entry_mut()
    .from_key(&TypeId::of::<T>())
    .or_insert_with(|| (TypeId::of::<T>(), TypedInterner::<T>::new()))
    .1
    .clone();
  (Arc::downcast(boxed.as_any_arc()))
    .expect("the typeid is supposed to protect from this")
}

#[cfg(test)]
mod test {
  use super::*;

  #[test]
  pub fn test_string() {
    let interner = Interner::new();
    let key1 = interner.i("foo");
    let key2 = interner.i(&"foo".to_string());
    assert_eq!(key1, key2)
  }

  #[test]
  pub fn test_slice() {
    let interner = Interner::new();
    let key1 = interner.i(&vec![1, 2, 3]);
    let key2 = interner.i(&[1, 2, 3][..]);
    assert_eq!(key1, key2);
  }

  #[test]
  pub fn test_str_slice() {
    let interner = Interner::new();
    let key1 = interner.iv(["a".to_string(), "b".to_string(), "c".to_string()]);
    let key2 = interner.ibv(vec!["a", "b", "c"]);
    assert_eq!(key1, key2);
  }
}