use std::{
ops::{Deref, DerefMut},
sync::{Arc, Weak},
};
use dashmap::DashMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
#[serde(transparent)]
pub struct PoolItem<T: AllocItem> {
#[serde(skip)]
allocator: Option<Weak<PoolAllocator<T>>>,
inner: Option<T>,
}
impl serde_bytes::Serialize for PoolItem<Vec<u8>> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serde_bytes::Serialize::serialize(&self.inner, serializer)
}
}
impl<'de> serde_bytes::Deserialize<'de> for PoolItem<Vec<u8>> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(Self {
allocator: None,
inner: serde_bytes::Deserialize::deserialize(deserializer)?,
})
}
}
impl<T: AllocItem> From<T> for PoolItem<T> {
fn from(value: T) -> Self {
Self {
allocator: None,
inner: Some(value),
}
}
}
impl<T: AllocItem> Drop for PoolItem<T> {
fn drop(&mut self) {
self.allocator.as_ref().map(|weak| {
weak.upgrade()
.map(|alloc| alloc.return_for_reuse(self.inner.take().unwrap()))
});
}
}
impl<T: AllocItem> Deref for PoolItem<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.inner.as_ref().unwrap()
}
}
impl<T: AllocItem> DerefMut for PoolItem<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner.as_mut().unwrap()
}
}
pub trait AllocItem {
fn new(numel: usize) -> Self;
fn len(&self) -> usize;
}
impl<T: Default + Clone> AllocItem for Vec<T> {
fn new(numel: usize) -> Self {
vec![T::default(); numel]
}
fn len(&self) -> usize {
self.len()
}
}
#[derive(Debug)]
pub(crate) struct PoolAllocator<T> {
reusable: DashMap<usize, Vec<T>>,
}
impl<T: AllocItem> PoolAllocator<T> {
pub(crate) fn new() -> Self {
Self {
reusable: Default::default(),
}
}
pub(crate) fn alloc(self: &Arc<Self>, numel: usize) -> PoolItem<T> {
if let Some(mut reusable) = self.reusable.get_mut(&numel) {
if let Some(item) = reusable.pop() {
return PoolItem {
allocator: Some(Arc::downgrade(self)),
inner: Some(item),
};
}
}
let item = T::new(numel);
return PoolItem {
allocator: Some(Arc::downgrade(self)),
inner: Some(item),
};
}
fn return_for_reuse(&self, item: T) {
self.reusable.entry(item.len()).or_default().push(item)
}
}