1 | use std::any::{Any, TypeId}; |
2 | |
3 | use rustc_hash::FxHashMap; |
4 | |
5 | use std::collections::hash_map; |
6 | use std::marker::PhantomData; |
7 | |
8 | /// A view into an occupied entry in a `TypeMap`. |
9 | #[derive (Debug)] |
10 | pub struct OccupiedEntry<'a, T> { |
11 | data: hash_map::OccupiedEntry<'a, TypeId, Box<dyn Any>>, |
12 | marker: PhantomData<fn(T)>, |
13 | } |
14 | |
15 | impl<'a, T: 'static> OccupiedEntry<'a, T> { |
16 | /// Gets a reference to the value in the entry. |
17 | pub fn get(&self) -> &T { |
18 | self.data.get().downcast_ref().unwrap() |
19 | } |
20 | |
21 | ///Gets a mutable reference to the value in the entry. |
22 | pub fn get_mut(&mut self) -> &mut T { |
23 | self.data.get_mut().downcast_mut().unwrap() |
24 | } |
25 | |
26 | /// Converts the `OccupiedEntry` into a mutable reference to the value in the entry |
27 | /// with a lifetime bound to the map itself. |
28 | pub fn into_mut(self) -> &'a mut T { |
29 | self.data.into_mut().downcast_mut().unwrap() |
30 | } |
31 | |
32 | /// Sets the value of the entry, and returns the entry's old value. |
33 | pub fn insert(&mut self, value: T) -> T { |
34 | self.data.insert(Box::new(value)).downcast().map(|boxed| *boxed).unwrap() |
35 | } |
36 | |
37 | /// Takes the value out of the entry, and returns it. |
38 | pub fn remove(self) -> T { |
39 | self.data.remove().downcast().map(|boxed| *boxed).unwrap() |
40 | } |
41 | } |
42 | |
43 | /// A view into a vacant entry in a `TypeMap`. |
44 | #[derive (Debug)] |
45 | pub struct VacantEntry<'a, T> { |
46 | data: hash_map::VacantEntry<'a, TypeId, Box<dyn Any>>, |
47 | marker: PhantomData<fn(T)>, |
48 | } |
49 | |
50 | impl<'a, T: 'static> VacantEntry<'a, T> { |
51 | /// Sets the value of the entry with the key of the `VacantEntry`, and returns a mutable reference to it. |
52 | pub fn insert(self, value: T) -> &'a mut T { |
53 | self.data.insert(Box::new(value)).downcast_mut().unwrap() |
54 | } |
55 | } |
56 | |
57 | /// A view into a single entry in a map, which may either be vacant or occupied. |
58 | #[derive (Debug)] |
59 | pub enum Entry<'a, T> { |
60 | Occupied(OccupiedEntry<'a, T>), |
61 | Vacant(VacantEntry<'a, T>), |
62 | } |
63 | |
64 | impl<'a, T: 'static> Entry<'a, T> { |
65 | /// Ensures a value is in the entry by inserting the default if empty, and returns |
66 | /// a mutable reference to the value in the entry. |
67 | pub fn or_insert(self, default: T) -> &'a mut T { |
68 | match self { |
69 | Entry::Occupied(inner: OccupiedEntry<'_, T>) => inner.into_mut(), |
70 | Entry::Vacant(inner: VacantEntry<'_, T>) => inner.insert(default), |
71 | } |
72 | } |
73 | |
74 | /// Ensures a value is in the entry by inserting the result of the default function if empty, and returns |
75 | /// a mutable reference to the value in the entry. |
76 | pub fn or_insert_with<F: FnOnce() -> T>(self, default: F) -> &'a mut T { |
77 | match self { |
78 | Entry::Occupied(inner: OccupiedEntry<'_, T>) => inner.into_mut(), |
79 | Entry::Vacant(inner: VacantEntry<'_, T>) => inner.insert(default()), |
80 | } |
81 | } |
82 | } |
83 | |
84 | #[derive (Debug, Default)] |
85 | /// The typemap container |
86 | pub struct TypeMap { |
87 | map: Option<FxHashMap<TypeId, Box<dyn Any>>>, |
88 | } |
89 | |
90 | impl TypeMap { |
91 | /// Create an empty `TypeMap`. |
92 | #[inline ] |
93 | pub fn new() -> Self { |
94 | Self { map: None } |
95 | } |
96 | |
97 | /// Insert a value into this `TypeMap`. |
98 | /// |
99 | /// If a value of this type already exists, it will be returned. |
100 | pub fn insert<T: 'static>(&mut self, val: T) -> Option<T> { |
101 | self.map |
102 | .get_or_insert_with(|| FxHashMap::default()) |
103 | .insert(TypeId::of::<T>(), Box::new(val)) |
104 | .and_then(|boxed| boxed.downcast().ok().map(|boxed| *boxed)) |
105 | } |
106 | |
107 | /// Check if container contains value for type |
108 | pub fn contains<T: 'static>(&self) -> bool { |
109 | self.map.as_ref().and_then(|m| m.get(&TypeId::of::<T>())).is_some() |
110 | } |
111 | |
112 | /// Get a reference to a value previously inserted on this `TypeMap`. |
113 | pub fn get<T: 'static>(&self) -> Option<&T> { |
114 | self.map |
115 | .as_ref() |
116 | .and_then(|m| m.get(&TypeId::of::<T>())) |
117 | .and_then(|boxed| boxed.downcast_ref()) |
118 | } |
119 | |
120 | /// Get a mutable reference to a value previously inserted on this `TypeMap`. |
121 | pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> { |
122 | self.map |
123 | .as_mut() |
124 | .and_then(|m| m.get_mut(&TypeId::of::<T>())) |
125 | .and_then(|boxed| boxed.downcast_mut()) |
126 | } |
127 | |
128 | /// Remove a value from this `TypeMap`. |
129 | /// |
130 | /// If a value of this type exists, it will be returned. |
131 | pub fn remove<T: 'static>(&mut self) -> Option<T> { |
132 | self.map |
133 | .as_mut() |
134 | .and_then(|m| m.remove(&TypeId::of::<T>())) |
135 | .and_then(|boxed| boxed.downcast().ok().map(|boxed| *boxed)) |
136 | } |
137 | |
138 | /// Clear the `TypeMap` of all inserted values. |
139 | #[inline ] |
140 | pub fn clear(&mut self) { |
141 | self.map = None; |
142 | } |
143 | |
144 | /// Get an entry in the `TypeMap` for in-place manipulation. |
145 | pub fn entry<T: 'static>(&mut self) -> Entry<T> { |
146 | match self.map.get_or_insert_with(|| FxHashMap::default()).entry(TypeId::of::<T>()) { |
147 | hash_map::Entry::Occupied(e) => { |
148 | Entry::Occupied(OccupiedEntry { data: e, marker: PhantomData }) |
149 | } |
150 | hash_map::Entry::Vacant(e) => { |
151 | Entry::Vacant(VacantEntry { data: e, marker: PhantomData }) |
152 | } |
153 | } |
154 | } |
155 | } |
156 | |
157 | /// Provides the same `TypeMap` container, but with `Send` + `Sync` bounds on values |
158 | pub mod concurrent { |
159 | |
160 | use std::any::{Any, TypeId}; |
161 | |
162 | use rustc_hash::FxHashMap; |
163 | |
164 | use std::collections::hash_map; |
165 | use std::marker::PhantomData; |
166 | |
167 | /// A view into an occupied entry in a `TypeMap`. |
168 | #[derive (Debug)] |
169 | pub struct OccupiedEntry<'a, T> { |
170 | data: hash_map::OccupiedEntry<'a, TypeId, Box<dyn Any + Send + Sync>>, |
171 | marker: PhantomData<fn(T)>, |
172 | } |
173 | |
174 | impl<'a, T: 'static + Send + Sync> OccupiedEntry<'a, T> { |
175 | /// Gets a reference to the value in the entry. |
176 | pub fn get(&self) -> &T { |
177 | self.data.get().downcast_ref().unwrap() |
178 | } |
179 | |
180 | ///Gets a mutable reference to the value in the entry. |
181 | pub fn get_mut(&mut self) -> &mut T { |
182 | self.data.get_mut().downcast_mut().unwrap() |
183 | } |
184 | |
185 | /// Converts the `OccupiedEntry` into a mutable reference to the value in the entry |
186 | /// with a lifetime bound to the map itself. |
187 | pub fn into_mut(self) -> &'a mut T { |
188 | self.data.into_mut().downcast_mut().unwrap() |
189 | } |
190 | |
191 | /// Sets the value of the entry, and returns the entry's old value. |
192 | pub fn insert(&mut self, value: T) -> T { |
193 | (self.data.insert(Box::new(value)) as Box<dyn Any>) |
194 | .downcast() |
195 | .map(|boxed| *boxed) |
196 | .unwrap() |
197 | } |
198 | |
199 | /// Takes the value out of the entry, and returns it. |
200 | pub fn remove(self) -> T { |
201 | (self.data.remove() as Box<dyn Any>).downcast().map(|boxed| *boxed).unwrap() |
202 | } |
203 | } |
204 | |
205 | /// A view into a vacant entry in a `TypeMap`. |
206 | #[derive (Debug)] |
207 | pub struct VacantEntry<'a, T> { |
208 | data: hash_map::VacantEntry<'a, TypeId, Box<dyn Any + Send + Sync>>, |
209 | marker: PhantomData<fn(T)>, |
210 | } |
211 | |
212 | impl<'a, T: 'static + Send + Sync> VacantEntry<'a, T> { |
213 | /// Sets the value of the entry with the key of the `VacantEntry`, and returns a mutable reference to it. |
214 | pub fn insert(self, value: T) -> &'a mut T { |
215 | self.data.insert(Box::new(value)).downcast_mut().unwrap() |
216 | } |
217 | } |
218 | |
219 | /// A view into a single entry in a map, which may either be vacant or occupied. |
220 | #[derive (Debug)] |
221 | pub enum Entry<'a, T> { |
222 | Occupied(OccupiedEntry<'a, T>), |
223 | Vacant(VacantEntry<'a, T>), |
224 | } |
225 | |
226 | impl<'a, T: 'static + Send + Sync> Entry<'a, T> { |
227 | /// Ensures a value is in the entry by inserting the default if empty, and returns |
228 | /// a mutable reference to the value in the entry. |
229 | pub fn or_insert(self, default: T) -> &'a mut T { |
230 | match self { |
231 | Entry::Occupied(inner) => inner.into_mut(), |
232 | Entry::Vacant(inner) => inner.insert(default), |
233 | } |
234 | } |
235 | |
236 | /// Ensures a value is in the entry by inserting the result of the default function if empty, and returns |
237 | /// a mutable reference to the value in the entry. |
238 | pub fn or_insert_with<F: FnOnce() -> T>(self, default: F) -> &'a mut T { |
239 | match self { |
240 | Entry::Occupied(inner) => inner.into_mut(), |
241 | Entry::Vacant(inner) => inner.insert(default()), |
242 | } |
243 | } |
244 | } |
245 | |
246 | #[derive (Debug, Default)] |
247 | /// The typemap container |
248 | pub struct TypeMap { |
249 | map: Option<FxHashMap<TypeId, Box<dyn Any + Send + Sync>>>, |
250 | } |
251 | |
252 | impl TypeMap { |
253 | /// Create an empty `TypeMap`. |
254 | #[inline ] |
255 | pub fn new() -> Self { |
256 | Self { map: None } |
257 | } |
258 | |
259 | /// Insert a value into this `TypeMap`. |
260 | /// |
261 | /// If a value of this type already exists, it will be returned. |
262 | pub fn insert<T: Send + Sync + 'static>(&mut self, val: T) -> Option<T> { |
263 | self.map |
264 | .get_or_insert_with(|| FxHashMap::default()) |
265 | .insert(TypeId::of::<T>(), Box::new(val)) |
266 | .and_then(|boxed| (boxed as Box<dyn Any>).downcast().ok().map(|boxed| *boxed)) |
267 | } |
268 | |
269 | /// Check if container contains value for type |
270 | pub fn contains<T: 'static>(&self) -> bool { |
271 | self.map.as_ref().and_then(|m| m.get(&TypeId::of::<T>())).is_some() |
272 | } |
273 | |
274 | /// Get a reference to a value previously inserted on this `TypeMap`. |
275 | pub fn get<T: 'static>(&self) -> Option<&T> { |
276 | self.map |
277 | .as_ref() |
278 | .and_then(|m| m.get(&TypeId::of::<T>())) |
279 | .and_then(|boxed| boxed.downcast_ref()) |
280 | } |
281 | |
282 | /// Get a mutable reference to a value previously inserted on this `TypeMap`. |
283 | pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> { |
284 | self.map |
285 | .as_mut() |
286 | .and_then(|m| m.get_mut(&TypeId::of::<T>())) |
287 | .and_then(|boxed| boxed.downcast_mut()) |
288 | } |
289 | |
290 | /// Remove a value from this `TypeMap`. |
291 | /// |
292 | /// If a value of this type exists, it will be returned. |
293 | pub fn remove<T: 'static>(&mut self) -> Option<T> { |
294 | self.map |
295 | .as_mut() |
296 | .and_then(|m| m.remove(&TypeId::of::<T>())) |
297 | .and_then(|boxed| (boxed as Box<dyn Any>).downcast().ok().map(|boxed| *boxed)) |
298 | } |
299 | |
300 | /// Clear the `TypeMap` of all inserted values. |
301 | #[inline ] |
302 | pub fn clear(&mut self) { |
303 | self.map = None; |
304 | } |
305 | |
306 | /// Get an entry in the `TypeMap` for in-place manipulation. |
307 | pub fn entry<T: 'static + Send + Sync>(&mut self) -> Entry<T> { |
308 | match self.map.get_or_insert_with(|| FxHashMap::default()).entry(TypeId::of::<T>()) { |
309 | hash_map::Entry::Occupied(e) => { |
310 | Entry::Occupied(OccupiedEntry { data: e, marker: PhantomData }) |
311 | } |
312 | hash_map::Entry::Vacant(e) => { |
313 | Entry::Vacant(VacantEntry { data: e, marker: PhantomData }) |
314 | } |
315 | } |
316 | } |
317 | } |
318 | } |
319 | |
320 | #[test ] |
321 | fn test_type_map() { |
322 | #[derive (Debug, PartialEq)] |
323 | struct MyType(i32); |
324 | |
325 | #[derive (Debug, PartialEq, Default)] |
326 | struct MyType2(String); |
327 | |
328 | let mut map = TypeMap::new(); |
329 | |
330 | map.insert(5i32); |
331 | map.insert(MyType(10)); |
332 | |
333 | assert_eq!(map.get(), Some(&5i32)); |
334 | assert_eq!(map.get_mut(), Some(&mut 5i32)); |
335 | |
336 | assert_eq!(map.remove::<i32>(), Some(5i32)); |
337 | assert!(map.get::<i32>().is_none()); |
338 | |
339 | assert_eq!(map.get::<bool>(), None); |
340 | assert_eq!(map.get(), Some(&MyType(10))); |
341 | |
342 | let entry = map.entry::<MyType2>(); |
343 | |
344 | let mut v = entry.or_insert_with(MyType2::default); |
345 | |
346 | v.0 = "Hello" .into(); |
347 | |
348 | assert_eq!(map.get(), Some(&MyType2("Hello" .into()))); |
349 | } |
350 | |