#![warn(missing_docs)]
#![allow(private_interfaces)]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::style))]
#[cfg(not(debug_assertions))]
macro_rules! unreach {
() => ({
unsafe {
core::hint::unreachable_unchecked();
}
})
}
#[cfg(debug_assertions)]
macro_rules! unreach {
() => ({
unreachable!()
})
}
mod typ;
pub use typ::{Type, RawType};
mod value;
pub use value::Value;
mod hash;
type Key = core::any::TypeId;
pub type ValueBox = Box<dyn core::any::Any + Send + Sync>;
#[cold]
#[inline(never)]
fn unlikely_vacant_insert(this: std::collections::hash_map::VacantEntry<'_, Key, ValueBox>, val: ValueBox) -> &'_ mut ValueBox {
this.insert(val)
}
type HashMap = std::collections::HashMap<Key, ValueBox, hash::UniqueHasherBuilder>;
pub struct TypeMap {
inner: HashMap,
}
impl TypeMap {
#[inline]
pub fn new() -> Self {
Self {
inner: HashMap::with_capacity_and_hasher(0, hash::UniqueHasherBuilder),
}
}
#[inline]
pub fn len(&self) -> usize {
self.inner.len()
}
#[inline]
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
#[inline]
pub fn clear(&mut self) {
self.inner.clear()
}
#[inline]
pub fn has<T: Type>(&self) -> bool {
self.inner.contains_key(&T::id())
}
#[inline]
pub fn contains_key<T: Type>(&self) -> bool {
self.inner.contains_key(&T::id())
}
#[inline]
pub fn get<T: Type>(&self) -> Option<&T> {
self.inner.get(&T::id()).map(|raw| Value::<T>::new_inner_ref(raw).downcast_ref())
}
#[inline]
pub fn get_raw(&self, id: &Key) -> Option<&Value<RawType>> {
self.inner.get(id).map(Value::new_inner_ref)
}
#[inline]
pub fn get_mut<T: Type>(&mut self) -> Option<&mut T> {
self.inner.get_mut(&T::id()).map(|raw| Value::<T>::new_inner_mut(raw).downcast_mut())
}
#[inline]
pub fn get_mut_raw(&mut self, id: &Key) -> Option<&mut Value<RawType>> {
self.inner.get_mut(id).map(Value::new_inner_mut)
}
#[inline]
pub fn get_or_default<T: Type + Default>(&mut self) -> &mut T {
use std::collections::hash_map::Entry;
match self.inner.entry(T::id()) {
Entry::Occupied(occupied) => {
match occupied.into_mut().downcast_mut() {
Some(res) => res,
None => unreach!(),
}
},
Entry::Vacant(vacant) => {
let ptr = unlikely_vacant_insert(vacant, Box::<T>::default());
match ptr.downcast_mut() {
Some(res) => res,
None => unreach!(),
}
}
}
}
#[inline]
pub fn insert<T: Type>(&mut self, value: T) -> Option<Box<T>> {
self.insert_raw(Value::new_inner(Box::new(value))).map(Value::downcast)
}
pub fn insert_raw<T: Type>(&mut self, value: Value<T>) -> Option<Value<T>> {
use std::collections::hash_map::Entry;
match self.inner.entry(T::id()) {
Entry::Occupied(mut occupied) => Some(
Value::<T>::new_inner(
occupied.insert(value.into_raw())
)
),
Entry::Vacant(vacant) => {
vacant.insert(value.into_raw());
None
}
}
}
#[inline]
pub fn remove_raw(&mut self, id: &Key) -> Option<Value<RawType>> {
self.inner.remove(id).map(Value::new_inner)
}
#[inline]
pub fn remove<T: Type>(&mut self) -> Option<Box<T>> {
self.inner.remove(&T::id()).map(|val| Value::<T>::new_inner(val).downcast())
}
}
impl core::default::Default for TypeMap {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl core::fmt::Debug for TypeMap {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
writeln!(f, "TypeMap {{ size={}, capacity={} }}", self.len(), self.capacity())
}
}