use crate::{Discriminant, IsoPoolable, Opaque, Poolable, RawPoolable};
use ahash::AHashMap;
use crossbeam_queue::ArrayQueue;
use nohash::IntMap;
#[cfg(feature = "serde")]
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::{
any::{Any, TypeId},
borrow::Borrow,
cell::RefCell,
cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd},
default::Default,
fmt::{self, Debug, Display},
hash::{Hash, Hasher},
mem::{self, ManuallyDrop},
ops::{Deref, DerefMut},
ptr,
sync::{Arc, LazyLock, Mutex, Weak},
};
pub mod arc;
thread_local! {
static POOLS: RefCell<IntMap<Discriminant, Opaque>> =
RefCell::new(IntMap::default());
}
const DEFAULT_SIZES: (usize, usize) = (1024, 1024);
static SIZES: LazyLock<Mutex<IntMap<Discriminant, (usize, usize)>>> =
LazyLock::new(|| Mutex::new(IntMap::default()));
fn with_pool<T, R, F>(sizes: Option<(usize, usize)>, f: F) -> R
where
T: IsoPoolable,
F: FnOnce(Option<&Pool<T>>) -> R,
{
let mut f = Some(f);
let res = POOLS.try_with(|pools| match pools.try_borrow_mut() {
Err(_) => (f.take().unwrap())(None),
Ok(mut pools) => match T::DISCRIMINANT {
Some(d) => {
let pool = pools.entry(d).or_insert_with(|| {
let (size, cap) = sizes.unwrap_or_else(|| {
SIZES
.lock()
.unwrap()
.get(&d)
.map(|(s, c)| (*s, *c))
.unwrap_or(DEFAULT_SIZES)
});
let b = Box::new(Pool::<T>::new(size, cap));
let t = Box::into_raw(b) as *mut ();
let drop = Some(Box::new(|t: *mut ()| unsafe {
drop(Box::from_raw(t as *mut Pool<T>))
}) as Box<dyn FnOnce(*mut ())>);
Opaque { t, drop }
});
(f.take().unwrap())(unsafe { Some(&*(pool.t as *mut Pool<T>)) })
}
None => (f.take().unwrap())(None),
},
});
match res {
Err(_) => (f.take().unwrap())(None),
Ok(r) => r,
}
}
pub fn clear() {
POOLS.with_borrow_mut(|pools| pools.clear())
}
pub fn clear_type<T: IsoPoolable>() {
POOLS.with_borrow_mut(|pools| {
if let Some(d) = T::DISCRIMINANT {
pools.remove(&d);
}
})
}
pub fn set_size<T: IsoPoolable>(max_pool_size: usize, max_element_capacity: usize) {
if let Some(d) = T::DISCRIMINANT {
SIZES.lock().unwrap().insert(d, (max_pool_size, max_element_capacity));
}
}
pub fn get_size<T: IsoPoolable>() -> Option<(usize, usize)> {
T::DISCRIMINANT.map(|d| {
SIZES.lock().unwrap().get(&d).map(|(s, c)| (*s, *c)).unwrap_or(DEFAULT_SIZES)
})
}
fn take_inner<T: IsoPoolable>(sizes: Option<(usize, usize)>) -> GPooled<T> {
with_pool(sizes, |pool| {
pool.map(|p| p.take()).unwrap_or_else(|| GPooled::orphan(T::empty()))
})
}
pub fn take<T: IsoPoolable>() -> GPooled<T> {
take_inner(None)
}
pub fn take_sz<T: IsoPoolable>(max: usize, max_elements: usize) -> GPooled<T> {
take_inner(Some((max, max_elements)))
}
pub fn pool<T: IsoPoolable>() -> Option<Pool<T>> {
with_pool(None, |pool| pool.cloned())
}
pub fn pool_sz<T: IsoPoolable>(max: usize, max_elements: usize) -> Option<Pool<T>> {
with_pool(Some((max, max_elements)), |pool| pool.cloned())
}
thread_local! {
static ANY_POOLS: RefCell<AHashMap<TypeId, Box<dyn Any>>> =
RefCell::new(AHashMap::default());
}
pub fn pool_any<T: Any + Poolable>(size: usize, max: usize) -> Pool<T> {
ANY_POOLS.with_borrow_mut(|pools| {
pools
.entry(TypeId::of::<T>())
.or_insert_with(|| Box::new(Pool::<T>::new(size, max)))
.downcast_ref::<Pool<T>>()
.unwrap()
.clone()
})
}
pub fn take_any<T: Any + Poolable>(size: usize, max: usize) -> GPooled<T> {
ANY_POOLS.with_borrow_mut(|pools| {
pools
.entry(TypeId::of::<T>())
.or_insert_with(|| Box::new(Pool::<T>::new(size, max)))
.downcast_ref::<Pool<T>>()
.unwrap()
.take()
})
}
#[derive(Clone)]
pub struct GPooled<T: Poolable> {
pool: ManuallyDrop<WeakPool<Self>>,
object: ManuallyDrop<T>,
}
impl<T: Poolable + Debug> fmt::Debug for GPooled<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", &self.object)
}
}
impl<T: Poolable + Display> fmt::Display for GPooled<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", &*self.object)
}
}
impl<T: IsoPoolable> Default for GPooled<T> {
fn default() -> Self {
take()
}
}
impl<T: IsoPoolable> GPooled<T> {
pub fn take() -> Self {
take()
}
pub fn take_sz(max: usize, max_elements: usize) -> Self {
take_sz(max, max_elements)
}
}
impl<T: IsoPoolable + Extend<E>, E> Extend<E> for GPooled<T> {
fn extend<I: IntoIterator<Item = E>>(&mut self, iter: I) {
self.object.extend(iter)
}
}
unsafe impl<T: Poolable> RawPoolable for GPooled<T> {
fn empty(pool: WeakPool<Self>) -> Self {
Self {
pool: ManuallyDrop::new(pool),
object: ManuallyDrop::new(Poolable::empty()),
}
}
fn reset(&mut self) {
Poolable::reset(&mut *self.object)
}
fn capacity(&self) -> usize {
Poolable::capacity(&*self.object)
}
fn really_drop(self) {
drop(self.detach())
}
}
impl<T: Poolable> Borrow<T> for GPooled<T> {
fn borrow(&self) -> &T {
&self.object
}
}
impl Borrow<str> for GPooled<String> {
fn borrow(&self) -> &str {
&self.object
}
}
impl<T: Poolable + PartialEq> PartialEq for GPooled<T> {
fn eq(&self, other: &GPooled<T>) -> bool {
self.object.eq(&other.object)
}
}
impl<T: Poolable + Eq> Eq for GPooled<T> {}
impl<T: Poolable + PartialOrd> PartialOrd for GPooled<T> {
fn partial_cmp(&self, other: &GPooled<T>) -> Option<Ordering> {
self.object.partial_cmp(&other.object)
}
}
impl<T: Poolable + Ord> Ord for GPooled<T> {
fn cmp(&self, other: &GPooled<T>) -> Ordering {
self.object.cmp(&other.object)
}
}
impl<T: Poolable + Hash> Hash for GPooled<T> {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
Hash::hash(&self.object, state)
}
}
impl<T: Poolable> GPooled<T> {
pub fn orphan(t: T) -> Self {
Self { pool: ManuallyDrop::new(WeakPool::new()), object: ManuallyDrop::new(t) }
}
pub fn assign(&mut self, pool: &Pool<T>) {
let old = mem::replace(&mut self.pool, ManuallyDrop::new(pool.downgrade()));
drop(ManuallyDrop::into_inner(old))
}
pub fn detach(self) -> T {
let mut t = ManuallyDrop::new(self);
unsafe {
ManuallyDrop::drop(&mut t.pool);
ManuallyDrop::take(&mut t.object)
}
}
}
impl<T: Poolable> AsRef<T> for GPooled<T> {
fn as_ref(&self) -> &T {
&self.object
}
}
impl<T: Poolable> Deref for GPooled<T> {
type Target = T;
fn deref(&self) -> &T {
&self.object
}
}
impl<T: Poolable> DerefMut for GPooled<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.object
}
}
impl<T: Poolable> Drop for GPooled<T> {
fn drop(&mut self) {
if self.really_dropped() {
match self.pool.upgrade() {
Some(pool) => pool.insert(unsafe { ptr::read(self) }),
None => unsafe {
ManuallyDrop::drop(&mut self.pool);
ManuallyDrop::drop(&mut self.object);
},
}
}
}
}
#[cfg(feature = "serde")]
impl<T: Poolable + Serialize> Serialize for GPooled<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.object.serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de, T: Poolable + DeserializeOwned + 'static> Deserialize<'de> for GPooled<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let mut t = take_any::<T>(1024, 1024);
Self::deserialize_in_place(deserializer, &mut t)?;
Ok(t)
}
fn deserialize_in_place<D>(deserializer: D, place: &mut Self) -> Result<(), D::Error>
where
D: serde::Deserializer<'de>,
{
<T as Deserialize>::deserialize_in_place(deserializer, &mut place.object)
}
}
#[derive(Debug)]
struct PoolInner<T: RawPoolable> {
max_elt_capacity: usize,
pool: ArrayQueue<T>,
}
impl<T: RawPoolable> Drop for PoolInner<T> {
fn drop(&mut self) {
while let Some(t) = self.pool.pop() {
RawPoolable::really_drop(t)
}
}
}
pub struct WeakPool<T: RawPoolable>(Weak<PoolInner<T>>);
impl<T: RawPoolable> Debug for WeakPool<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<weak pool>")
}
}
impl<T: RawPoolable> Clone for WeakPool<T> {
fn clone(&self) -> Self {
Self(Weak::clone(&self.0))
}
}
impl<T: RawPoolable> WeakPool<T> {
pub fn new() -> Self {
WeakPool(Weak::new())
}
pub fn upgrade(&self) -> Option<RawPool<T>> {
self.0.upgrade().map(RawPool)
}
}
pub type Pool<T> = RawPool<GPooled<T>>;
#[derive(Debug)]
pub struct RawPool<T: RawPoolable>(Arc<PoolInner<T>>);
impl<T: RawPoolable> Clone for RawPool<T> {
fn clone(&self) -> Self {
Self(Arc::clone(&self.0))
}
}
impl<T: RawPoolable> RawPool<T> {
pub fn downgrade(&self) -> WeakPool<T> {
WeakPool(Arc::downgrade(&self.0))
}
pub fn new(max_capacity: usize, max_elt_capacity: usize) -> RawPool<T> {
RawPool(Arc::new(PoolInner {
pool: ArrayQueue::new(max_capacity),
max_elt_capacity,
}))
}
pub fn try_take(&self) -> Option<T> {
self.0.pool.pop()
}
pub fn take(&self) -> T {
self.0.pool.pop().unwrap_or_else(|| RawPoolable::empty(self.downgrade()))
}
pub fn insert(&self, mut t: T) {
let cap = t.capacity();
if cap > 0 && cap <= self.0.max_elt_capacity {
t.reset();
if let Err(t) = self.0.pool.push(t) {
RawPoolable::really_drop(t)
}
} else {
RawPoolable::really_drop(t)
}
}
pub fn prune(&self) {
let len = self.0.pool.len();
let ten_percent = std::cmp::max(1, self.0.pool.capacity() / 10);
let one_percent = std::cmp::max(1, ten_percent / 10);
if len > ten_percent {
for _ in 0..ten_percent {
if let Some(v) = self.0.pool.pop() {
RawPoolable::really_drop(v)
}
}
} else if len > one_percent {
for _ in 0..one_percent {
if let Some(v) = self.0.pool.pop() {
RawPoolable::really_drop(v)
}
}
} else if len > 0 {
if let Some(v) = self.0.pool.pop() {
RawPoolable::really_drop(v)
}
}
}
}