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