#![warn(missing_docs, unused_results)]
use core::hash::Hasher;
#[derive(Default)]
pub struct TypeIdHasher {
value: u64,
}
impl Hasher for TypeIdHasher {
#[inline]
fn write(&mut self, bytes: &[u8]) {
debug_assert_eq!(bytes.len(), 8);
let _ = bytes.try_into().map(|array| self.value = u64::from_ne_bytes(array));
}
#[inline]
fn finish(&self) -> u64 {
self.value
}
}
use core::any::{Any, TypeId};
use core::hash::BuildHasherDefault;
use core::marker::PhantomData;
use ::std::collections::hash_map;
#[expect(clippy::disallowed_types, reason = "Uses a custom hasher")]
pub type RawMap<A> = hash_map::HashMap<TypeId, Box<A>, BuildHasherDefault<TypeIdHasher>>;
#[derive(Debug)]
pub struct Map<A: ?Sized + Downcast = dyn Any> {
raw: RawMap<A>,
}
pub type AnyMap = Map<dyn Any>;
impl<A: ?Sized + Downcast> Default for Map<A> {
#[inline]
fn default() -> Map<A> {
Map { raw: RawMap::with_hasher(Default::default()) }
}
}
impl<A: ?Sized + Downcast> Map<A> {
#[inline]
#[must_use]
pub fn get<T: IntoBox<A>>(&self) -> Option<&T> {
self.raw.get(&TypeId::of::<T>()).map(|any| unsafe { any.downcast_unchecked_ref::<T>() })
}
#[inline]
pub fn entry<T: IntoBox<A>>(&mut self) -> Entry<'_, A, T> {
match self.raw.entry(TypeId::of::<T>()) {
hash_map::Entry::Occupied(e) => {
Entry::Occupied(OccupiedEntry { inner: e, type_: PhantomData })
}
hash_map::Entry::Vacant(e) => {
Entry::Vacant(VacantEntry { inner: e, type_: PhantomData })
}
}
}
}
pub struct OccupiedEntry<'map, A: ?Sized + Downcast, V: 'map> {
inner: hash_map::OccupiedEntry<'map, TypeId, Box<A>>,
type_: PhantomData<V>,
}
pub struct VacantEntry<'map, A: ?Sized + Downcast, V: 'map> {
inner: hash_map::VacantEntry<'map, TypeId, Box<A>>,
type_: PhantomData<V>,
}
pub enum Entry<'map, A: ?Sized + Downcast, V> {
Occupied(OccupiedEntry<'map, A, V>),
Vacant(VacantEntry<'map, A, V>),
}
impl<'map, A: ?Sized + Downcast, V: IntoBox<A>> Entry<'map, A, V> {
#[inline]
pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'map mut V {
match self {
Entry::Occupied(inner) => inner.into_mut(),
Entry::Vacant(inner) => inner.insert(default()),
}
}
}
impl<'map, A: ?Sized + Downcast, V: IntoBox<A>> OccupiedEntry<'map, A, V> {
#[inline]
#[must_use]
pub fn into_mut(self) -> &'map mut V {
unsafe { self.inner.into_mut().downcast_unchecked_mut() }
}
}
impl<'map, A: ?Sized + Downcast, V: IntoBox<A>> VacantEntry<'map, A, V> {
#[inline]
pub fn insert(self, value: V) -> &'map mut V {
unsafe { self.inner.insert(value.into_box()).downcast_unchecked_mut() }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_varieties() {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
fn assert_debug<T: ::core::fmt::Debug>() {}
assert_send::<Map<dyn Any + Send>>();
assert_send::<Map<dyn Any + Send + Sync>>();
assert_sync::<Map<dyn Any + Send + Sync>>();
assert_debug::<Map<dyn Any>>();
assert_debug::<Map<dyn Any + Send>>();
assert_debug::<Map<dyn Any + Send + Sync>>();
}
#[test]
fn type_id_hasher() {
use core::any::TypeId;
use core::hash::Hash as _;
fn verify_hashing_with(type_id: TypeId) {
let mut hasher = TypeIdHasher::default();
type_id.hash(&mut hasher);
_ = hasher.finish();
}
verify_hashing_with(TypeId::of::<usize>());
verify_hashing_with(TypeId::of::<()>());
verify_hashing_with(TypeId::of::<str>());
verify_hashing_with(TypeId::of::<&str>());
verify_hashing_with(TypeId::of::<Vec<u8>>());
}
}
pub trait Downcast {
fn type_id(&self) -> TypeId;
unsafe fn downcast_unchecked_ref<T: 'static>(&self) -> &T;
unsafe fn downcast_unchecked_mut<T: 'static>(&mut self) -> &mut T;
}
pub trait IntoBox<A: ?Sized + Downcast>: Any {
fn into_box(self) -> Box<A>;
}
macro_rules! implement {
($any_trait:ident $(+ $auto_traits:ident)*) => {
impl Downcast for dyn $any_trait $(+ $auto_traits)* {
#[inline]
fn type_id(&self) -> TypeId {
self.type_id()
}
#[inline]
unsafe fn downcast_unchecked_ref<T: 'static>(&self) -> &T {
unsafe { &*std::ptr::from_ref::<Self>(self).cast::<T>() }
}
#[inline]
unsafe fn downcast_unchecked_mut<T: 'static>(&mut self) -> &mut T {
unsafe { &mut *std::ptr::from_mut::<Self>(self).cast::<T>() }
}
}
impl<T: $any_trait $(+ $auto_traits)*> IntoBox<dyn $any_trait $(+ $auto_traits)*> for T {
#[inline]
fn into_box(self) -> Box<dyn $any_trait $(+ $auto_traits)*> {
Box::new(self)
}
}
}
}
implement!(Any);
implement!(Any + Send);
implement!(Any + Send + Sync);