stdx/
anymap.rs

1//! This file is a port of only the necessary features from <https://github.com/chris-morgan/anymap> version 1.0.0-beta.2 for use within rust-analyzer.
2//!
3//! Copyright © 2014–2022 Chris Morgan.
4//! COPYING: <https://github.com/chris-morgan/anymap/blob/master/COPYING>
5//! Note that the license is changed from Blue Oak Model 1.0.0 or MIT or Apache-2.0 to MIT OR Apache-2.0
6//!
7//! This implementation provides a safe and convenient store for one value of each type.
8//!
9//! Your starting point is [`Map`]. It has an example.
10//!
11//! # Cargo features
12//!
13//! This implementation has two independent features, each of which provides an implementation providing
14//! types `Map`, `AnyMap`, `OccupiedEntry`, `VacantEntry`, `Entry` and `RawMap`:
15//!
16//! - **std** (default, *enabled* in this build):
17//!   an implementation using `std::collections::hash_map`, placed in the crate root
18//!   (e.g. `anymap::AnyMap`).
19
20#![warn(missing_docs, unused_results)]
21
22use core::hash::Hasher;
23
24/// A hasher designed to eke a little more speed out, given `TypeId`'s known characteristics.
25///
26/// Specifically, this is a no-op hasher that expects to be fed a u64's worth of
27/// randomly-distributed bits. It works well for `TypeId` (eliminating start-up time, so that my
28/// `get_missing` benchmark is ~30ns rather than ~900ns, and being a good deal faster after that, so
29/// that my `insert_and_get_on_260_types` benchmark is ~12μs instead of ~21.5μs), but will
30/// panic in debug mode and always emit zeros in release mode for any other sorts of inputs, so
31/// yeah, don't use it! 😀
32#[derive(Default)]
33pub struct TypeIdHasher {
34    value: u64,
35}
36
37impl Hasher for TypeIdHasher {
38    #[inline]
39    fn write(&mut self, bytes: &[u8]) {
40        // This expects to receive exactly one 64-bit value, and there's no realistic chance of
41        // that changing, but I don't want to depend on something that isn't expressly part of the
42        // contract for safety. But I'm OK with release builds putting everything in one bucket
43        // if it *did* change (and debug builds panicking).
44        debug_assert_eq!(bytes.len(), 8);
45        let _ = bytes.try_into().map(|array| self.value = u64::from_ne_bytes(array));
46    }
47
48    #[inline]
49    fn finish(&self) -> u64 {
50        self.value
51    }
52}
53
54use core::any::{Any, TypeId};
55use core::hash::BuildHasherDefault;
56use core::marker::PhantomData;
57
58use ::std::collections::hash_map;
59
60/// Raw access to the underlying `HashMap`.
61///
62/// This alias is provided for convenience because of the ugly third generic parameter.
63#[expect(clippy::disallowed_types, reason = "Uses a custom hasher")]
64pub type RawMap<A> = hash_map::HashMap<TypeId, Box<A>, BuildHasherDefault<TypeIdHasher>>;
65
66/// A collection containing zero or one values for any given type and allowing convenient,
67/// type-safe access to those values.
68///
69/// The type parameter `A` allows you to use a different value type; normally you will want
70/// it to be `core::any::Any` (also known as `std::any::Any`), but there are other choices:
71///
72/// - You can add on `+ Send` or `+ Send + Sync` (e.g. `Map<dyn Any + Send>`) to add those
73///   auto traits.
74///
75/// Cumulatively, there are thus six forms of map:
76///
77/// - `[Map]<dyn [core::any::Any]>`,
78///   also spelled [`AnyMap`] for convenience.
79/// - `[Map]<dyn [core::any::Any] + Send>`
80/// - `[Map]<dyn [core::any::Any] + Send + Sync>`
81///
82/// ## Example
83///
84/// (Here, the [`AnyMap`] convenience alias is used;
85/// the first line could use `[anymap::Map][Map]::<[core::any::Any]>::default()`
86/// instead if desired.)
87///
88/// ```
89/// # use stdx::anymap;
90/// let mut data = anymap::AnyMap::default();
91/// assert_eq!(data.get(), None::<&i32>);
92/// ```
93///
94/// Values containing non-static references are not permitted.
95#[derive(Debug)]
96pub struct Map<A: ?Sized + Downcast = dyn Any> {
97    raw: RawMap<A>,
98}
99
100/// The most common type of `Map`: just using `Any`; `[Map]<dyn [Any]>`.
101///
102/// Why is this a separate type alias rather than a default value for `Map<A>`?
103/// `Map::default()` doesn't seem to be happy to infer that it should go with the default
104/// value. It's a bit sad, really. Ah well, I guess this approach will do.
105pub type AnyMap = Map<dyn Any>;
106
107impl<A: ?Sized + Downcast> Default for Map<A> {
108    #[inline]
109    fn default() -> Map<A> {
110        Map { raw: RawMap::with_hasher(Default::default()) }
111    }
112}
113
114impl<A: ?Sized + Downcast> Map<A> {
115    /// Returns a reference to the value stored in the collection for the type `T`,
116    /// if it exists.
117    #[inline]
118    #[must_use]
119    pub fn get<T: IntoBox<A>>(&self) -> Option<&T> {
120        self.raw.get(&TypeId::of::<T>()).map(|any| unsafe { any.downcast_ref_unchecked::<T>() })
121    }
122
123    /// Gets the entry for the given type in the collection for in-place manipulation
124    #[inline]
125    pub fn entry<T: IntoBox<A>>(&mut self) -> Entry<'_, A, T> {
126        match self.raw.entry(TypeId::of::<T>()) {
127            hash_map::Entry::Occupied(e) => {
128                Entry::Occupied(OccupiedEntry { inner: e, type_: PhantomData })
129            }
130            hash_map::Entry::Vacant(e) => {
131                Entry::Vacant(VacantEntry { inner: e, type_: PhantomData })
132            }
133        }
134    }
135}
136
137/// A view into a single occupied location in an `Map`.
138pub struct OccupiedEntry<'map, A: ?Sized + Downcast, V: 'map> {
139    inner: hash_map::OccupiedEntry<'map, TypeId, Box<A>>,
140    type_: PhantomData<V>,
141}
142
143/// A view into a single empty location in an `Map`.
144pub struct VacantEntry<'map, A: ?Sized + Downcast, V: 'map> {
145    inner: hash_map::VacantEntry<'map, TypeId, Box<A>>,
146    type_: PhantomData<V>,
147}
148
149/// A view into a single location in an `Map`, which may be vacant or occupied.
150pub enum Entry<'map, A: ?Sized + Downcast, V> {
151    /// An occupied Entry
152    Occupied(OccupiedEntry<'map, A, V>),
153    /// A vacant Entry
154    Vacant(VacantEntry<'map, A, V>),
155}
156
157impl<'map, A: ?Sized + Downcast, V: IntoBox<A>> Entry<'map, A, V> {
158    /// Ensures a value is in the entry by inserting the result of the default function if
159    /// empty, and returns a mutable reference to the value in the entry.
160    #[inline]
161    pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'map mut V {
162        match self {
163            Entry::Occupied(inner) => inner.into_mut(),
164            Entry::Vacant(inner) => inner.insert(default()),
165        }
166    }
167}
168
169impl<'map, A: ?Sized + Downcast, V: IntoBox<A>> OccupiedEntry<'map, A, V> {
170    /// Converts the `OccupiedEntry` into a mutable reference to the value in the entry
171    /// with a lifetime bound to the collection itself
172    #[inline]
173    #[must_use]
174    pub fn into_mut(self) -> &'map mut V {
175        unsafe { self.inner.into_mut().downcast_mut_unchecked() }
176    }
177}
178
179impl<'map, A: ?Sized + Downcast, V: IntoBox<A>> VacantEntry<'map, A, V> {
180    /// Sets the value of the entry with the `VacantEntry`'s key,
181    /// and returns a mutable reference to it
182    #[inline]
183    pub fn insert(self, value: V) -> &'map mut V {
184        unsafe { self.inner.insert(value.into_box()).downcast_mut_unchecked() }
185    }
186}
187
188#[cfg(test)]
189mod tests {
190    use super::*;
191
192    #[test]
193    fn test_varieties() {
194        fn assert_send<T: Send>() {}
195        fn assert_sync<T: Sync>() {}
196        fn assert_debug<T: ::core::fmt::Debug>() {}
197        assert_send::<Map<dyn Any + Send>>();
198        assert_send::<Map<dyn Any + Send + Sync>>();
199        assert_sync::<Map<dyn Any + Send + Sync>>();
200        assert_debug::<Map<dyn Any>>();
201        assert_debug::<Map<dyn Any + Send>>();
202        assert_debug::<Map<dyn Any + Send + Sync>>();
203    }
204
205    #[test]
206    fn type_id_hasher() {
207        use core::any::TypeId;
208        use core::hash::Hash as _;
209        fn verify_hashing_with(type_id: TypeId) {
210            let mut hasher = TypeIdHasher::default();
211            type_id.hash(&mut hasher);
212            _ = hasher.finish();
213        }
214        // Pick a variety of types, just to demonstrate it's all sane. Normal, zero-sized, unsized, &c.
215        verify_hashing_with(TypeId::of::<usize>());
216        verify_hashing_with(TypeId::of::<()>());
217        verify_hashing_with(TypeId::of::<str>());
218        verify_hashing_with(TypeId::of::<&str>());
219        verify_hashing_with(TypeId::of::<Vec<u8>>());
220    }
221}
222
223/// Methods for downcasting from an `Any`-like trait object.
224///
225/// This should only be implemented on trait objects for subtraits of `Any`, though you can
226/// implement it for other types and it'll work fine, so long as your implementation is correct.
227pub trait Downcast {
228    /// Gets the `TypeId` of `self`.
229    fn type_id(&self) -> TypeId;
230
231    // Note the bound through these downcast methods is 'static, rather than the inexpressible
232    // concept of Self-but-as-a-trait (where Self is `dyn Trait`). This is sufficient, exceeding
233    // TypeId's requirements. Sure, you *can* do CloneAny.downcast_unchecked::<NotClone>() and the
234    // type system won't protect you, but that doesn't introduce any unsafety: the method is
235    // already unsafe because you can specify the wrong type, and if this were exposing safe
236    // downcasting, CloneAny.downcast::<NotClone>() would just return an error, which is just as
237    // correct.
238    //
239    // Now in theory we could also add T: ?Sized, but that doesn't play nicely with the common
240    // implementation, so I'm doing without it.
241
242    /// Downcast from `&Any` to `&T`, without checking the type matches.
243    ///
244    /// # Safety
245    ///
246    /// The caller must ensure that `T` matches the trait object, on pain of *undefined behavior*.
247    unsafe fn downcast_ref_unchecked<T: 'static>(&self) -> &T;
248
249    /// Downcast from `&mut Any` to `&mut T`, without checking the type matches.
250    ///
251    /// # Safety
252    ///
253    /// The caller must ensure that `T` matches the trait object, on pain of *undefined behavior*.
254    unsafe fn downcast_mut_unchecked<T: 'static>(&mut self) -> &mut T;
255}
256
257/// A trait for the conversion of an object into a boxed trait object.
258pub trait IntoBox<A: ?Sized + Downcast>: Any {
259    /// Convert self into the appropriate boxed form.
260    fn into_box(self) -> Box<A>;
261}
262
263macro_rules! implement {
264    ($any_trait:ident $(+ $auto_traits:ident)*) => {
265        impl Downcast for dyn $any_trait $(+ $auto_traits)* {
266            #[inline]
267            fn type_id(&self) -> TypeId {
268                self.type_id()
269            }
270
271            #[inline]
272            unsafe fn downcast_ref_unchecked<T: 'static>(&self) -> &T {
273                unsafe { &*std::ptr::from_ref::<Self>(self).cast::<T>() }
274            }
275
276            #[inline]
277            unsafe fn downcast_mut_unchecked<T: 'static>(&mut self) -> &mut T {
278                unsafe { &mut *std::ptr::from_mut::<Self>(self).cast::<T>() }
279            }
280        }
281
282        impl<T: $any_trait $(+ $auto_traits)*> IntoBox<dyn $any_trait $(+ $auto_traits)*> for T {
283            #[inline]
284            fn into_box(self) -> Box<dyn $any_trait $(+ $auto_traits)*> {
285                Box::new(self)
286            }
287        }
288    }
289}
290
291implement!(Any);
292implement!(Any + Send);
293implement!(Any + Send + Sync);