use std::{
any::{Any, TypeId},
collections::{hash_map::RandomState, HashMap},
};
pub struct TypePools<H = RandomState> {
pools: HashMap<TypeId, Box<dyn TypePoolTrait>, H>,
}
impl TypePools {
pub fn new() -> Self {
TypePools { pools: HashMap::new() }
}
pub fn type_pool<T: 'static>(&self) -> Option<&TypePool<T>> {
self.pools.get(&TypeId::of::<T>())
.map(|pool| {
unsafe { TypePool::<T>::cast_unchecked(pool.as_ref()) } })
}
pub fn type_pool_mut<T: 'static>(&mut self) -> Option<&mut TypePool<T>> {
self.pools.get_mut(&TypeId::of::<T>())
.map(|pool| {
unsafe { TypePool::<T>::cast_mut_unchecked(pool.as_mut()) } })
}
pub fn push<T: 'static>(&mut self, value: T) {
let pools = self.pools.get_mut(&TypeId::of::<T>());
if let Some(pools) = pools {
unsafe { TypePool::<T>::cast_mut_unchecked(pools.as_mut()) }
.values.push(value);
} else {
self.pools.insert(TypeId::of::<T>(), Box::new(TypePool::<T>::new()));
unsafe { TypePool::<T>::cast_mut_unchecked(self.pools.get_mut(&TypeId::of::<T>()).unwrap_unchecked().as_mut()) } .values.push(value)
}
}
pub fn pop<T: 'static>(&mut self) -> Option<T> {
self.type_pool_mut()
.and_then(|p| p.values.pop())
}
pub fn remove<T: 'static>(&mut self, idx: usize) -> Option<T> {
self.type_pool_mut()
.and_then(|p| p.values.remove(idx))
}
pub fn get<T: 'static>(&self, idx: usize) -> Option<&T> {
self.type_pool()
.and_then(|p| p.values.get(idx))
}
pub fn get_mut<T: 'static>(&mut self, idx: usize) -> Option<&mut T> {
self.type_pool_mut()
.and_then(|p| p.values.get_mut(idx))
}
pub fn len<T: 'static>(&self) -> Option<usize> {
self.type_pool()
.map(|f: &TypePool<T>| f.values.len())
}
pub fn types_count(&self) -> usize {
self.pools.keys().len()
}
pub fn types(&self) -> Vec<&TypeId> {
self.pools.keys().collect()
}
pub fn remove_type<T: 'static>(&mut self) {
self.pools.remove(&TypeId::of::<T>());
}
pub fn remove_empty(&mut self) {
let to_remove = self.pools.keys()
.filter(|key| unsafe{ self.pools.get(key).unwrap_unchecked() }.is_empty())
.map(|key| *key)
.collect::<Vec<TypeId>>();
for id in to_remove {
self.pools.remove(&id);
}
}
pub fn shrink_to_fit(&mut self) {
self.pools.shrink_to_fit()
}
}
trait TypePoolTrait {
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn is_empty(&self) -> bool;
}
pub struct TypePool<T> {
pub values: Vec<T>,
}
impl<T: 'static> TypePool<T> {
fn new() -> Self {
Self { values: Vec::new() }
}
unsafe fn cast_unchecked(pool: &dyn TypePoolTrait) -> &Self {
pool.as_any()
.downcast_ref::<TypePool<T>>()
.unwrap_unchecked()
}
unsafe fn cast_mut_unchecked(pool: &mut dyn TypePoolTrait) -> &mut Self {
pool.as_any_mut()
.downcast_mut::<TypePool<T>>()
.unwrap_unchecked()
}
}
impl<T: 'static> TypePoolTrait for TypePool<T> {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn is_empty(&self) -> bool {
self.values.is_empty()
}
}
#[cfg(test)]
mod tests {
use crate::TypePools;
#[test]
fn test_add() {
let mut pools = TypePools::new();
pools.push(1 as u32);
pools.push(2 as u32);
pools.push("Hello");
pools.push("World");
assert_eq!(*pools.get::<u32>(0).unwrap(), 1);
assert_eq!(*pools.get::<u32>(1).unwrap(), 2);
assert_eq!(*pools.get::<&str>(0).unwrap(), "Hello");
assert_eq!(*pools.get::<&str>(1).unwrap(), "World");
}
}