use parking_lot::Mutex;
use std::collections::VecDeque;
use std::fmt::{self, Debug};
use std::sync::Arc;
pub struct ObjectPool<T> {
available: Mutex<VecDeque<T>>,
create_fn: Arc<dyn Fn() -> T + Send + Sync>,
max_size: usize,
}
impl<T: std::fmt::Debug> std::fmt::Debug for ObjectPool<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ObjectPool")
.field("available", &self.available)
.field("max_size", &self.max_size)
.field("create_fn", &"<function>")
.finish()
}
}
impl<T> ObjectPool<T> {
fn return_object(&self, object: T) {
let mut available = self.available.lock();
if available.len() < self.max_size {
available.push_back(object);
}
}
}
impl<T: Send + 'static> ObjectPool<T> {
#[must_use]
pub fn new<F>(initial_size: usize, max_size: usize, create_fn: F) -> Self
where
F: Fn() -> T + Send + Sync + 'static,
{
let create_fn = Arc::new(create_fn);
let factory = Arc::clone(&create_fn);
let mut available = VecDeque::with_capacity(max_size);
for _ in 0..initial_size {
available.push_back((factory)());
}
Self {
available: Mutex::new(available),
create_fn,
max_size,
}
}
pub fn get(&self) -> PooledObject<'_, T> {
let object = {
let mut available = self.available.lock();
available.pop_front().unwrap_or_else(|| (self.create_fn)())
};
PooledObject {
object: Some(object),
pool: self,
}
}
}
pub struct PooledObject<'a, T> {
object: Option<T>,
pool: &'a ObjectPool<T>,
}
impl<T> PooledObject<'_, T> {
pub fn return_to_pool(mut self) {
if let Some(object) = self.object.take() {
self.pool.return_object(object);
}
}
}
impl<T> Drop for PooledObject<'_, T> {
fn drop(&mut self) {
if let Some(object) = self.object.take() {
self.pool.return_object(object);
}
}
}
impl<T> std::ops::Deref for PooledObject<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.object
.as_ref()
.expect("Object already returned to pool")
}
}
impl<T> std::ops::DerefMut for PooledObject<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.object
.as_mut()
.expect("Object already returned to pool")
}
}
impl<T: Debug> Debug for PooledObject<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PooledObject")
.field("object", &self.object)
.finish()
}
}
#[derive(Debug)]
pub struct StringPool {
inner: ObjectPool<String>,
}
impl StringPool {
#[must_use]
pub fn new(initial_size: usize, max_size: usize, initial_capacity: usize) -> Self {
Self {
inner: ObjectPool::new(initial_size, max_size, move || {
String::with_capacity(initial_capacity)
}),
}
}
pub fn get(&self) -> PooledString<'_> {
let mut string = self.inner.get();
string.clear(); PooledString(string)
}
pub fn get_with_value<S: AsRef<str>>(&self, value: S) -> PooledString<'_> {
let mut string = self.inner.get();
string.clear(); string.push_str(value.as_ref());
PooledString(string)
}
}
pub struct PooledString<'a>(PooledObject<'a, String>);
impl PooledString<'_> {
pub fn return_to_pool(self) {
self.0.return_to_pool();
}
}
impl std::ops::Deref for PooledString<'_> {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for PooledString<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl Debug for PooledString<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&**self, f)
}
}
impl fmt::Display for PooledString<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
pub struct VecPool<T> {
inner: ObjectPool<Vec<T>>,
}
impl<T: Send + 'static> VecPool<T> {
#[must_use]
pub fn new(initial_size: usize, max_size: usize, initial_capacity: usize) -> Self {
Self {
inner: ObjectPool::new(initial_size, max_size, move || {
Vec::with_capacity(initial_capacity)
}),
}
}
pub fn get(&self) -> PooledVec<'_, T> {
let mut vec = self.inner.get();
vec.clear(); PooledVec(vec)
}
}
pub struct PooledVec<'a, T>(PooledObject<'a, Vec<T>>);
impl<T> PooledVec<'_, T> {
pub fn return_to_pool(self) {
self.0.return_to_pool();
}
}
impl<T> std::ops::Deref for PooledVec<'_, T> {
type Target = Vec<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> std::ops::DerefMut for PooledVec<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T: Debug> Debug for PooledVec<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&**self, f)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_object_pool() {
let pool = ObjectPool::new(5, 10, || String::from("test"));
let mut obj1 = pool.get();
assert_eq!(*obj1, "test");
obj1.push_str("-modified");
assert_eq!(*obj1, "test-modified");
drop(obj1);
let mut obj2 = pool.get();
assert_eq!(*obj2, "test");
obj2.push_str("-new");
assert_eq!(*obj2, "test-new");
}
#[test]
fn test_string_pool() {
let pool = StringPool::new(5, 10, 32);
let mut str1 = pool.get();
str1.push_str("hello");
assert_eq!(*str1, "hello");
drop(str1);
let str2 = pool.get();
assert_eq!(*str2, "");
let str3 = pool.get_with_value("world");
assert_eq!(*str3, "world");
}
#[test]
fn test_vec_pool() {
let pool = VecPool::new(5, 10, 32);
let mut vec1 = pool.get();
vec1.push(1);
vec1.push(2);
assert_eq!(*vec1, vec![1, 2]);
drop(vec1);
let vec2 = pool.get();
assert_eq!(*vec2, Vec::<i32>::new());
}
}