use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use crate::render::Buffer;
use crate::utils::lock::lock_or_recover;
pub struct ObjectPool<T> {
factory: Box<dyn Fn() -> T>,
pool: RefCell<Vec<T>>,
max_size: usize,
stats: RefCell<PoolStats>,
}
#[derive(Debug, Clone, Default)]
pub struct PoolStats {
pub acquires: usize,
pub hits: usize,
pub misses: usize,
pub releases: usize,
pub discards: usize,
}
impl PoolStats {
pub fn hit_rate(&self) -> f32 {
if self.acquires == 0 {
0.0
} else {
self.hits as f32 / self.acquires as f32
}
}
}
impl<T> ObjectPool<T> {
pub fn new<F>(factory: F) -> Self
where
F: Fn() -> T + 'static,
{
Self::with_capacity(factory, 16)
}
pub fn with_capacity<F>(factory: F, max_size: usize) -> Self
where
F: Fn() -> T + 'static,
{
Self {
factory: Box::new(factory),
pool: RefCell::new(Vec::with_capacity(max_size)),
max_size,
stats: RefCell::new(PoolStats::default()),
}
}
pub fn acquire(&self) -> T {
let mut stats = self.stats.borrow_mut();
stats.acquires += 1;
if let Some(obj) = self.pool.borrow_mut().pop() {
stats.hits += 1;
obj
} else {
stats.misses += 1;
(self.factory)()
}
}
pub fn release(&self, obj: T) {
let mut stats = self.stats.borrow_mut();
stats.releases += 1;
let mut pool = self.pool.borrow_mut();
if pool.len() < self.max_size {
pool.push(obj);
} else {
stats.discards += 1;
}
}
pub fn size(&self) -> usize {
self.pool.borrow().len()
}
pub fn stats(&self) -> PoolStats {
self.stats.borrow().clone()
}
pub fn clear(&self) {
self.pool.borrow_mut().clear();
}
pub fn prewarm(&self, count: usize) {
let target = count.min(self.max_size);
let mut pool = self.pool.borrow_mut();
while pool.len() < target {
pool.push((self.factory)());
}
}
}
pub struct SyncObjectPool<T> {
factory: Box<dyn Fn() -> T + Send + Sync>,
pool: Mutex<Vec<T>>,
max_size: usize,
stats: Mutex<PoolStats>,
}
impl<T: Send> SyncObjectPool<T> {
pub fn new<F>(factory: F) -> Self
where
F: Fn() -> T + Send + Sync + 'static,
{
Self::with_capacity(factory, 16)
}
pub fn with_capacity<F>(factory: F, max_size: usize) -> Self
where
F: Fn() -> T + Send + Sync + 'static,
{
Self {
factory: Box::new(factory),
pool: Mutex::new(Vec::with_capacity(max_size)),
max_size,
stats: Mutex::new(PoolStats::default()),
}
}
pub fn acquire(&self) -> T {
let mut stats = lock_or_recover(&self.stats);
stats.acquires += 1;
if let Some(obj) = lock_or_recover(&self.pool).pop() {
stats.hits += 1;
obj
} else {
stats.misses += 1;
(self.factory)()
}
}
pub fn release(&self, obj: T) {
let mut stats = lock_or_recover(&self.stats);
stats.releases += 1;
let mut pool = lock_or_recover(&self.pool);
if pool.len() < self.max_size {
pool.push(obj);
} else {
stats.discards += 1;
}
}
pub fn stats(&self) -> PoolStats {
lock_or_recover(&self.stats).clone()
}
}
pub struct BufferPool {
pools: RefCell<HashMap<(u16, u16), Vec<Buffer>>>,
max_per_size: usize,
stats: RefCell<PoolStats>,
}
impl Default for BufferPool {
fn default() -> Self {
Self::new()
}
}
impl BufferPool {
pub fn new() -> Self {
Self::with_capacity(4)
}
pub fn with_capacity(max_per_size: usize) -> Self {
Self {
pools: RefCell::new(HashMap::new()),
max_per_size,
stats: RefCell::new(PoolStats::default()),
}
}
pub fn acquire(&self, width: u16, height: u16) -> Buffer {
let mut stats = self.stats.borrow_mut();
stats.acquires += 1;
let key = (width, height);
let mut pools = self.pools.borrow_mut();
if let Some(pool) = pools.get_mut(&key) {
if let Some(mut buf) = pool.pop() {
stats.hits += 1;
buf.clear();
return buf;
}
}
for ((w, h), pool) in pools.iter_mut() {
if *w >= width && *h >= height {
if let Some(mut buf) = pool.pop() {
stats.hits += 1;
buf.resize(width, height);
return buf;
}
}
}
stats.misses += 1;
Buffer::new(width, height)
}
pub fn release(&self, buf: Buffer) {
let mut stats = self.stats.borrow_mut();
stats.releases += 1;
let key = (buf.width(), buf.height());
let mut pools = self.pools.borrow_mut();
let pool = pools.entry(key).or_default();
if pool.len() < self.max_per_size {
pool.push(buf);
} else {
stats.discards += 1;
}
}
pub fn stats(&self) -> PoolStats {
self.stats.borrow().clone()
}
pub fn clear(&self) {
self.pools.borrow_mut().clear();
}
pub fn total_buffered(&self) -> usize {
self.pools.borrow().values().map(|v| v.len()).sum()
}
}
pub struct StringPool {
strings: RefCell<HashMap<String, Arc<str>>>,
stats: RefCell<PoolStats>,
}
impl Default for StringPool {
fn default() -> Self {
Self::new()
}
}
impl StringPool {
pub fn new() -> Self {
Self {
strings: RefCell::new(HashMap::new()),
stats: RefCell::new(PoolStats::default()),
}
}
pub fn intern(&self, s: impl AsRef<str>) -> Arc<str> {
let s = s.as_ref();
let mut stats = self.stats.borrow_mut();
stats.acquires += 1;
let mut strings = self.strings.borrow_mut();
if let Some(interned) = strings.get(s) {
stats.hits += 1;
interned.clone()
} else {
stats.misses += 1;
let interned: Arc<str> = s.into();
strings.insert(s.to_owned(), interned.clone());
interned
}
}
pub fn contains(&self, s: &str) -> bool {
self.strings.borrow().contains_key(s)
}
pub fn len(&self) -> usize {
self.strings.borrow().len()
}
pub fn is_empty(&self) -> bool {
self.strings.borrow().is_empty()
}
pub fn stats(&self) -> PoolStats {
self.stats.borrow().clone()
}
pub fn clear(&self) {
self.strings.borrow_mut().clear();
}
}
pub struct SyncStringPool {
strings: Mutex<HashMap<String, Arc<str>>>,
stats: Mutex<PoolStats>,
}
impl Default for SyncStringPool {
fn default() -> Self {
Self::new()
}
}
impl SyncStringPool {
pub fn new() -> Self {
Self {
strings: Mutex::new(HashMap::new()),
stats: Mutex::new(PoolStats::default()),
}
}
pub fn intern(&self, s: impl AsRef<str>) -> Arc<str> {
let s = s.as_ref();
let mut stats = lock_or_recover(&self.stats);
stats.acquires += 1;
let mut strings = lock_or_recover(&self.strings);
if let Some(interned) = strings.get(s) {
stats.hits += 1;
interned.clone()
} else {
stats.misses += 1;
let interned: Arc<str> = s.into();
strings.insert(s.to_owned(), interned.clone());
interned
}
}
pub fn stats(&self) -> PoolStats {
lock_or_recover(&self.stats).clone()
}
}
pub struct VecPool<T> {
pool: RefCell<Vec<Vec<T>>>,
default_capacity: usize,
max_size: usize,
stats: RefCell<PoolStats>,
}
impl<T> VecPool<T> {
pub fn new(default_capacity: usize) -> Self {
Self::with_max_size(default_capacity, 16)
}
pub fn with_max_size(default_capacity: usize, max_size: usize) -> Self {
Self {
pool: RefCell::new(Vec::with_capacity(max_size)),
default_capacity,
max_size,
stats: RefCell::new(PoolStats::default()),
}
}
pub fn acquire(&self) -> Vec<T> {
let mut stats = self.stats.borrow_mut();
stats.acquires += 1;
if let Some(vec) = self.pool.borrow_mut().pop() {
stats.hits += 1;
vec
} else {
stats.misses += 1;
Vec::with_capacity(self.default_capacity)
}
}
pub fn release(&self, mut vec: Vec<T>) {
let mut stats = self.stats.borrow_mut();
stats.releases += 1;
vec.clear();
let mut pool = self.pool.borrow_mut();
if pool.len() < self.max_size {
pool.push(vec);
} else {
stats.discards += 1;
}
}
pub fn stats(&self) -> PoolStats {
self.stats.borrow().clone()
}
pub fn clear(&self) {
self.pool.borrow_mut().clear();
}
pub fn prewarm(&self, count: usize) {
let target = count.min(self.max_size);
let mut pool = self.pool.borrow_mut();
while pool.len() < target {
pool.push(Vec::with_capacity(self.default_capacity));
}
}
}
pub struct Pooled<'a, T> {
value: Option<T>,
pool: &'a ObjectPool<T>,
}
impl<'a, T> Pooled<'a, T> {
pub fn new(pool: &'a ObjectPool<T>) -> Self {
Self {
value: Some(pool.acquire()),
pool,
}
}
pub fn take(mut self) -> T {
self.value.take().unwrap_or_else(|| {
panic!("Pooled value already taken - this is a bug in Pooled implementation")
})
}
}
impl<T> std::ops::Deref for Pooled<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.value.as_ref().unwrap_or_else(|| {
panic!("Pooled value is None - deref called after take(), this is a bug")
})
}
}
impl<T> std::ops::DerefMut for Pooled<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.value.as_mut().unwrap_or_else(|| {
panic!("Pooled value is None - deref_mut called after take(), this is a bug")
})
}
}
impl<T> Drop for Pooled<'_, T> {
fn drop(&mut self) {
if let Some(value) = self.value.take() {
self.pool.release(value);
}
}
}
pub fn object_pool<T, F>(factory: F) -> ObjectPool<T>
where
F: Fn() -> T + 'static,
{
ObjectPool::new(factory)
}
pub fn buffer_pool() -> BufferPool {
BufferPool::new()
}
pub fn string_pool() -> StringPool {
StringPool::new()
}
pub fn vec_pool<T>(capacity: usize) -> VecPool<T> {
VecPool::new(capacity)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_object_pool_basic() {
let pool: ObjectPool<Vec<u8>> = ObjectPool::new(|| Vec::with_capacity(64));
let mut v1 = pool.acquire();
assert!(v1.capacity() >= 64);
v1.push(1);
v1.push(2);
pool.release(v1);
assert_eq!(pool.size(), 1);
let v2 = pool.acquire();
assert!(v2.capacity() >= 64);
assert_eq!(pool.size(), 0);
let stats = pool.stats();
assert_eq!(stats.acquires, 2);
assert_eq!(stats.misses, 1);
assert_eq!(stats.hits, 1);
}
#[test]
fn test_object_pool_max_size() {
let pool: ObjectPool<u32> = ObjectPool::with_capacity(|| 0, 2);
pool.release(1);
pool.release(2);
pool.release(3);
assert_eq!(pool.size(), 2);
assert_eq!(pool.stats().discards, 1);
}
#[test]
fn test_object_pool_prewarm() {
let pool: ObjectPool<String> = ObjectPool::with_capacity(|| String::with_capacity(32), 10);
pool.prewarm(5);
assert_eq!(pool.size(), 5);
let _ = pool.acquire();
assert_eq!(pool.size(), 4);
}
#[test]
fn test_buffer_pool_basic() {
let pool = BufferPool::new();
let buf = pool.acquire(80, 24);
assert_eq!(buf.width(), 80);
assert_eq!(buf.height(), 24);
pool.release(buf);
assert_eq!(pool.total_buffered(), 1);
let buf2 = pool.acquire(80, 24);
assert_eq!(buf2.width(), 80);
assert_eq!(pool.stats().hits, 1);
}
#[test]
fn test_buffer_pool_resize() {
let pool = BufferPool::new();
let buf = Buffer::new(160, 48);
pool.release(buf);
let buf2 = pool.acquire(80, 24);
assert_eq!(buf2.width(), 80);
assert_eq!(buf2.height(), 24);
assert_eq!(pool.stats().hits, 1);
}
#[test]
fn test_string_pool_basic() {
let pool = StringPool::new();
let s1 = pool.intern("hello");
let s2 = pool.intern("hello");
let s3 = pool.intern("world");
assert!(Arc::ptr_eq(&s1, &s2));
assert!(!Arc::ptr_eq(&s1, &s3));
assert_eq!(pool.len(), 2);
assert_eq!(pool.stats().hits, 1);
assert_eq!(pool.stats().misses, 2);
}
#[test]
fn test_vec_pool_basic() {
let pool: VecPool<i32> = VecPool::new(16);
let mut v = pool.acquire();
assert!(v.capacity() >= 16);
v.push(1);
v.push(2);
pool.release(v);
let v2 = pool.acquire();
assert!(v2.is_empty());
assert!(v2.capacity() >= 16);
}
#[test]
fn test_pooled_guard() {
let pool: ObjectPool<String> = ObjectPool::new(|| String::with_capacity(32));
{
let mut s = Pooled::new(&pool);
s.push_str("hello");
assert_eq!(&*s, "hello");
}
assert_eq!(pool.size(), 1);
}
#[test]
fn test_pooled_take() {
let pool: ObjectPool<String> = ObjectPool::new(|| String::with_capacity(32));
let s = {
let mut pooled = Pooled::new(&pool);
pooled.push_str("hello");
pooled.take() };
assert_eq!(s, "hello");
assert_eq!(pool.size(), 0); }
#[test]
fn test_sync_object_pool() {
use std::thread;
let pool: Arc<SyncObjectPool<Vec<u8>>> =
Arc::new(SyncObjectPool::new(|| Vec::with_capacity(64)));
let handles: Vec<_> = (0..4)
.map(|_| {
let pool = pool.clone();
thread::spawn(move || {
for _ in 0..10 {
let v = pool.acquire();
pool.release(v);
}
})
})
.collect();
for h in handles {
h.join().unwrap();
}
let stats = pool.stats();
assert_eq!(stats.acquires, 40);
assert_eq!(stats.releases, 40);
}
#[test]
fn test_sync_string_pool() {
use std::thread;
let pool: Arc<SyncStringPool> = Arc::new(SyncStringPool::new());
let handles: Vec<_> = (0..4)
.map(|i| {
let pool = pool.clone();
thread::spawn(move || {
for j in 0..10 {
let _ = pool.intern(format!("string-{}-{}", i, j));
}
})
})
.collect();
for h in handles {
h.join().unwrap();
}
let stats = pool.stats();
assert_eq!(stats.acquires, 40);
}
#[test]
fn test_pool_stats_hit_rate() {
let pool: ObjectPool<u32> = ObjectPool::new(|| 0);
pool.release(1);
let _ = pool.acquire();
let _ = pool.acquire();
let stats = pool.stats();
assert_eq!(stats.hits, 1);
assert_eq!(stats.misses, 1);
assert!((stats.hit_rate() - 0.5).abs() < 0.01);
}
}