1
use std::borrow::Cow;
2
use std::collections::hash_map::RandomState;
3
use std::fmt::Debug;
4
use std::hash::{BuildHasher, Hash};
5
use std::path::{Path, PathBuf};
6
use std::sync::{Arc, Mutex};
7

            
8
use crate::global::GlobalPool;
9
use crate::pool::{Pool, PoolKindSealed, Poolable};
10
use crate::{PoolKind, Pooled};
11

            
12
/// A pooled string that belongs to a [`StringPool`].
13
pub type SharedString<S = RandomState> = Pooled<SharedPool<String, S>, S>;
14
/// A pooled path that belongs to a [`PathPool`].
15
pub type SharedPath<S = RandomState> = Pooled<SharedPool<PathBuf, S>, S>;
16
/// A pooled buffer that belongs to a [`BufferPool`].
17
pub type SharedBuffer<S = RandomState> = Pooled<SharedPool<Vec<u8>, S>, S>;
18

            
19
/// A string interning pool that manages [`SharedString`]s.
20
///
21
/// Each [`StringPool`] has its own storage. When comparing [`SharedString`]s
22
/// from separate pools, the full string comparison function must be used.
23
pub type StringPool<S = RandomState> = SharedPool<String, S>;
24
/// A path interning pool that manages [`SharedPath`]s.
25
///
26
/// Each [`PathPool`] has its own storage. When comparing [`SharedPath`]s
27
/// from separate pools, the full string comparison function must be used.
28
pub type PathPool<S = RandomState> = SharedPool<PathBuf, S>;
29
/// A path interning pool that manages [`SharedBuffer`]s.
30
///
31
/// Each [`BufferPool`] has its own storage. When comparing [`SharedBuffer`]s
32
/// from separate pools, the full string comparison function must be used.
33
pub type BufferPool<S = RandomState> = SharedPool<Vec<u8>, S>;
34

            
35
/// A shared pool of values that ensures only one copy of any given value exists
36
/// at any time.
37
///
38
/// To retrieve a [`Pooled`] value, use [`SharedPool::get()`], which is
39
/// implemented for these types:
40
///
41
/// - [`String`]/[`&str`](str)
42
/// - [`PathBuf`]/[`&Path`](Path)
43
/// - [`Vec<u8>`]/`&[u8]`
44
#[derive(Debug)]
45
pub struct SharedPool<T, S = RandomState>(Arc<Mutex<Pool<Self, S>>>)
46
where
47
    T: Poolable + Debug + Clone + Eq + PartialEq + Hash + Ord + PartialOrd,
48
    S: BuildHasher;
49

            
50
impl<T, S> SharedPool<T, S>
51
where
52
    T: Poolable + Debug + Clone + Eq + PartialEq + Hash + Ord + PartialOrd,
53
    S: BuildHasher,
54
{
55
    /// Returns a collection of the currently pooled items.
56
    #[must_use]
57
3
    pub fn pooled<C>(&self) -> C
58
3
    where
59
3
        C: FromIterator<Pooled<Self, S>>,
60
3
    {
61
3
        self.with_active_symbols(|pool| {
62
3
            pool.active
63
3
                .iter()
64
3
                .map(|data| Pooled(data.clone()))
65
3
                .collect()
66
3
        })
67
3
    }
68
}
69

            
70
impl<S> SharedPool<String, S>
71
where
72
    S: BuildHasher,
73
{
74
    /// Creates a new pool using the provided [`BuildHasher`] for hashing
75
    /// values.
76
    #[must_use]
77
1
    pub fn with_hasher(hasher: S) -> Self {
78
1
        Self::with_capacity_and_hasher(0, hasher)
79
1
    }
80

            
81
    /// Creates a new pool using the provided [`BuildHasher`] for hashing
82
    /// values. The pool will have enough capacity to allow inserting
83
    /// `initial_capacity` pooled entries without reallocation.
84
    #[must_use]
85
1
    pub fn with_capacity_and_hasher(initial_capacity: usize, hasher: S) -> Self {
86
1
        Self(Arc::new(Mutex::new(Pool::with_capacity_and_hasher(
87
1
            initial_capacity,
88
1
            hasher,
89
1
        ))))
90
1
    }
91

            
92
    /// Returns a copy of an existing [`SharedString`] if one is found.
93
    /// Otherwise, a new [`SharedString`] is created and returned.
94
    ///
95
    /// While any copies of the returned [`SharedString`] are still allocated,
96
    /// calling this function is guaranteed to return a copy of the same string.
97
    #[must_use]
98
15
    pub fn get<'a, V>(&self, value: V) -> SharedString<S>
99
15
    where
100
15
        V: Into<Cow<'a, str>>,
101
15
    {
102
15
        let value = value.into();
103
15
        self.with_active_symbols(|symbols| symbols.get(value, self))
104
15
    }
105
}
106

            
107
impl<S> SharedPool<PathBuf, S>
108
where
109
    S: BuildHasher,
110
{
111
    /// Returns a copy of an existing [`SharedPath`] if one is found. Otherwise,
112
    /// a new [`SharedPath`] is created and returned.
113
    ///
114
    /// While any copies of the returned [`SharedPath`] are still allocated,
115
    /// calling this function is guaranteed to return a copy of the same path.
116
    #[must_use]
117
4
    pub fn get<'a, V>(&self, value: V) -> SharedPath<S>
118
4
    where
119
4
        V: Into<Cow<'a, Path>>,
120
4
    {
121
4
        let value = value.into();
122
4
        self.with_active_symbols(|symbols| symbols.get(value, self))
123
4
    }
124
}
125

            
126
impl<S> SharedPool<Vec<u8>, S>
127
where
128
    S: BuildHasher,
129
{
130
    /// Returns a copy of an existing [`SharedBuffer`] if one is found. Otherwise,
131
    /// a new [`SharedBuffer`] is created and returned.
132
    ///
133
    /// While any copies of the returned [`SharedBuffer`] are still allocated,
134
    /// calling this function is guaranteed to return a copy of the same buffer.
135
    #[must_use]
136
4
    pub fn get<'a, V>(&self, value: V) -> SharedBuffer<S>
137
4
    where
138
4
        V: Into<Cow<'a, [u8]>>,
139
4
    {
140
4
        let value = value.into();
141
4
        self.with_active_symbols(|symbols| symbols.get(value, self))
142
4
    }
143
}
144

            
145
impl<T, S> Clone for SharedPool<T, S>
146
where
147
    T: Poolable + Debug + Clone + Eq + PartialEq + Hash + Ord + PartialOrd,
148
    S: BuildHasher,
149
{
150
13
    fn clone(&self) -> Self {
151
13
        Self(self.0.clone())
152
13
    }
153
}
154

            
155
impl<T, S> PoolKind<S> for SharedPool<T, S>
156
where
157
    T: Poolable + Debug + Clone + Eq + PartialEq + Hash + Ord + PartialOrd,
158
    S: BuildHasher,
159
{
160
}
161

            
162
impl<T, S> PoolKindSealed<S> for SharedPool<T, S>
163
where
164
    T: Poolable + Debug + Clone + Eq + PartialEq + Hash + Ord + PartialOrd,
165
    S: BuildHasher,
166
{
167
    type Owned = T;
168
    type Pooled = T::Boxed;
169

            
170
39
    fn with_active_symbols<R>(&self, logic: impl FnOnce(&mut Pool<Self, S>) -> R) -> R {
171
39
        let mut symbols = self.0.lock().expect("poisoned");
172
39

            
173
39
        logic(&mut symbols)
174
39
    }
175

            
176
    fn address_of(&self) -> *const () {
177
        Arc::as_ptr(&self.0).cast()
178
    }
179
}
180

            
181
impl<T, S> PartialEq for SharedPool<T, S>
182
where
183
    T: Poolable + Debug + Clone + Eq + PartialEq + Hash + Ord + PartialOrd,
184
    S: BuildHasher,
185
{
186
9
    fn eq(&self, other: &SharedPool<T, S>) -> bool {
187
9
        Arc::ptr_eq(&self.0, &other.0)
188
9
    }
189
}
190

            
191
impl<T, S, S2> PartialEq<&'static GlobalPool<T, S2>> for SharedPool<T, S>
192
where
193
    T: Poolable + Debug + Clone + Eq + PartialEq + Hash + Ord + PartialOrd,
194
    S: BuildHasher,
195
    S2: BuildHasher,
196
{
197
7
    fn eq(&self, _other: &&'static GlobalPool<T, S2>) -> bool {
198
7
        false
199
7
    }
200
}
201

            
202
impl<T, S, S2> PartialEq<SharedPool<T, S>> for &'static GlobalPool<T, S2>
203
where
204
    T: Poolable + Debug + Clone + Eq + PartialEq + Hash + Ord + PartialOrd,
205
    S: BuildHasher,
206
    S2: BuildHasher,
207
{
208
6
    fn eq(&self, _other: &SharedPool<T, S>) -> bool {
209
6
        false
210
6
    }
211
}
212

            
213
impl<T> Default for SharedPool<T, RandomState>
214
where
215
    T: Poolable + Debug + Clone + Eq + PartialEq + Hash + Ord + PartialOrd,
216
{
217
10
    fn default() -> Self {
218
10
        Self(Arc::default())
219
10
    }
220
}