use crate::{Discriminant, IsoPoolable, Opaque};
use nohash::IntMap;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::{
borrow::Borrow,
cell::RefCell,
collections::HashMap,
fmt::Display,
hash::Hash,
mem::ManuallyDrop,
ops::{Deref, DerefMut},
ptr,
sync::{LazyLock, Mutex},
};
struct Pool<T: IsoPoolable> {
max: usize,
max_capacity: usize,
data: Vec<T>,
}
impl<T: IsoPoolable> Pool<T> {
fn new(max: usize, max_capacity: usize) -> Self {
Self { max, max_capacity, data: Vec::with_capacity(max) }
}
}
thread_local! {
static POOLS: RefCell<IntMap<Discriminant, Opaque>> =
RefCell::new(HashMap::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<&mut 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(&mut *(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)>) -> T {
with_pool(sizes, |pool| pool.and_then(|p| p.data.pop())).unwrap_or_else(|| T::empty())
}
pub fn take<T: IsoPoolable>() -> T {
take_inner(None)
}
pub fn take_sz<T: IsoPoolable>(max: usize, max_elt: usize) -> T {
take_inner(Some((max, max_elt)))
}
unsafe fn insert_raw_inner<T: IsoPoolable>(
sizes: Option<(usize, usize)>,
t: T,
) -> Option<T> {
with_pool(sizes, |pool| match pool {
Some(pool) if pool.data.len() < pool.max && t.capacity() <= pool.max_capacity => {
pool.data.push(t);
None
}
None | Some(_) => Some(t),
})
}
pub unsafe fn insert_raw<T: IsoPoolable>(t: T) -> Option<T> {
unsafe { insert_raw_inner(None, t) }
}
pub unsafe fn insert_raw_sz<T: IsoPoolable>(
max: usize,
max_elt: usize,
t: T,
) -> Option<T> {
unsafe { insert_raw_inner(Some((max, max_elt)), t) }
}
pub fn insert<T: IsoPoolable>(mut t: T) -> Option<T> {
t.reset();
unsafe { insert_raw(t) }
}
pub fn insert_sz<T: IsoPoolable>(max: usize, max_elt: usize, mut t: T) -> Option<T> {
t.reset();
unsafe { insert_raw_inner(Some((max, max_elt)), t) }
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LPooled<T: IsoPoolable>(ManuallyDrop<T>);
impl<T: IsoPoolable + Display> Display for LPooled<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &*self.0)
}
}
impl<T: IsoPoolable> Borrow<T> for LPooled<T> {
fn borrow(&self) -> &T {
&self.0
}
}
impl Borrow<str> for LPooled<String> {
fn borrow(&self) -> &str {
&self.0
}
}
impl<T: IsoPoolable> Default for LPooled<T> {
fn default() -> Self {
Self::take()
}
}
impl<T: IsoPoolable> LPooled<T> {
pub fn take() -> Self {
Self(ManuallyDrop::new(take()))
}
pub fn take_sz(max: usize, max_elements: usize) -> Self {
Self(ManuallyDrop::new(take_sz(max, max_elements)))
}
pub fn detach(self) -> T {
let t = ManuallyDrop::new(self);
ManuallyDrop::into_inner(unsafe { ptr::read(&t.0) })
}
}
impl<T: IsoPoolable> From<T> for LPooled<T> {
fn from(t: T) -> Self {
Self(ManuallyDrop::new(t))
}
}
impl<T: IsoPoolable> AsRef<T> for LPooled<T> {
fn as_ref(&self) -> &T {
&self.0
}
}
impl<T: IsoPoolable> Deref for LPooled<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T: IsoPoolable> DerefMut for LPooled<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.0
}
}
impl<T: IsoPoolable> Drop for LPooled<T> {
fn drop(&mut self) {
if self.really_dropped() {
if let Some(t) = insert(unsafe { ptr::read(&*self.0) }) {
drop(t)
}
} else {
unsafe {
ManuallyDrop::drop(&mut self.0);
}
}
}
}
#[cfg(feature = "serde")]
impl<T: IsoPoolable + Serialize> Serialize for LPooled<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.0.serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de, T: IsoPoolable + DeserializeOwned + 'static> Deserialize<'de> for LPooled<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let mut t = LPooled::take();
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.0)
}
}
impl<T: IsoPoolable + Extend<E>, E> Extend<E> for LPooled<T> {
fn extend<I: IntoIterator<Item = E>>(&mut self, iter: I) {
self.0.extend(iter)
}
}
impl<T: IsoPoolable + Extend<E>, E> FromIterator<E> for LPooled<T> {
fn from_iter<I: IntoIterator<Item = E>>(iter: I) -> Self {
let mut t = Self::take();
t.extend(iter);
t
}
}