use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::Arc;
pub struct ObjectPool<T> {
pool: RefCell<Vec<T>>,
factory: Box<dyn Fn() -> T>,
max_size: usize,
}
impl<T> ObjectPool<T> {
pub fn new<F>(factory: F) -> Self
where
F: Fn() -> T + 'static,
{
Self {
pool: RefCell::new(Vec::new()),
factory: Box::new(factory),
max_size: 64,
}
}
pub fn with_max_size<F>(factory: F, max_size: usize) -> Self
where
F: Fn() -> T + 'static,
{
Self {
pool: RefCell::new(Vec::new()),
factory: Box::new(factory),
max_size,
}
}
pub fn get(&self) -> T {
self.pool
.borrow_mut()
.pop()
.unwrap_or_else(|| (self.factory)())
}
pub fn put(&self, item: T) {
let mut pool = self.pool.borrow_mut();
if pool.len() < self.max_size {
pool.push(item);
}
}
pub fn len(&self) -> usize {
self.pool.borrow().len()
}
pub fn is_empty(&self) -> bool {
self.pool.borrow().is_empty()
}
pub fn clear(&self) {
self.pool.borrow_mut().clear();
}
}
pub trait Clearable {
fn clear_for_reuse(&mut self);
}
impl<T> Clearable for Vec<T> {
fn clear_for_reuse(&mut self) {
self.clear();
}
}
impl<K, V> Clearable for HashMap<K, V> {
fn clear_for_reuse(&mut self) {
self.clear();
}
}
pub struct ClearingPool<T: Clearable> {
inner: ObjectPool<T>,
}
impl<T: Clearable> ClearingPool<T> {
pub fn new<F>(factory: F) -> Self
where
F: Fn() -> T + 'static,
{
Self {
inner: ObjectPool::new(factory),
}
}
pub fn get(&self) -> T {
self.inner.get()
}
pub fn put(&self, mut item: T) {
item.clear_for_reuse();
self.inner.put(item);
}
}
#[derive(Debug, Clone)]
pub struct SharedGeometry<V> {
pub id: String,
vertices: Arc<Vec<V>>,
ref_count: usize,
}
impl<V: Clone> SharedGeometry<V> {
pub fn new(id: impl Into<String>, vertices: Vec<V>) -> Self {
Self {
id: id.into(),
vertices: Arc::new(vertices),
ref_count: 1,
}
}
pub fn vertices(&self) -> &[V] {
&self.vertices
}
pub fn share(&self) -> Self {
Self {
id: self.id.clone(),
vertices: Arc::clone(&self.vertices),
ref_count: self.ref_count + 1,
}
}
pub fn strong_count(&self) -> usize {
Arc::strong_count(&self.vertices)
}
}
#[derive(Default)]
pub struct GeometryCache<V> {
cache: HashMap<String, SharedGeometry<V>>,
}
impl<V: Clone> GeometryCache<V> {
pub fn new() -> Self {
Self {
cache: HashMap::new(),
}
}
pub fn get_or_insert(&mut self, id: &str, vertices: Vec<V>) -> SharedGeometry<V> {
if let Some(existing) = self.cache.get(id) {
existing.share()
} else {
let shared = SharedGeometry::new(id, vertices);
self.cache.insert(id.to_string(), shared.share());
shared
}
}
pub fn contains(&self, id: &str) -> bool {
self.cache.contains_key(id)
}
pub fn len(&self) -> usize {
self.cache.len()
}
pub fn is_empty(&self) -> bool {
self.cache.is_empty()
}
pub fn clear(&mut self) {
self.cache.clear();
}
pub fn memory_usage(&self) -> usize {
self.cache
.values()
.map(|g| g.vertices.len() * std::mem::size_of::<V>())
.sum()
}
}
pub struct ScratchBuffer<T> {
buffer: RefCell<Vec<T>>,
initial_capacity: usize,
}
impl<T> ScratchBuffer<T> {
pub fn new(capacity: usize) -> Self {
Self {
buffer: RefCell::new(Vec::with_capacity(capacity)),
initial_capacity: capacity,
}
}
pub fn with_buffer<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut Vec<T>) -> R,
{
let mut buf = self.buffer.borrow_mut();
let result = f(&mut buf);
buf.clear();
result
}
pub fn capacity(&self) -> usize {
self.buffer.borrow().capacity()
}
pub fn shrink_if_needed(&self, max_capacity: usize) {
let mut buf = self.buffer.borrow_mut();
if buf.capacity() > max_capacity {
buf.shrink_to(self.initial_capacity);
}
}
}
#[derive(Debug, Clone, Default)]
pub struct MemoryStats {
pub pool_hits: usize,
pub pool_misses: usize,
pub cached_geometries: usize,
pub memory_saved_bytes: usize,
}
impl MemoryStats {
pub fn new() -> Self {
Self::default()
}
pub fn record_pool_hit(&mut self) {
self.pool_hits += 1;
}
pub fn record_pool_miss(&mut self) {
self.pool_misses += 1;
}
pub fn hit_rate(&self) -> f64 {
let total = self.pool_hits + self.pool_misses;
if total == 0 {
0.0
} else {
self.pool_hits as f64 / total as f64
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_object_pool() {
let pool: ObjectPool<Vec<i32>> = ObjectPool::new(|| Vec::with_capacity(10));
assert!(pool.is_empty());
let mut v1 = pool.get();
v1.push(1);
v1.push(2);
pool.put(v1);
assert_eq!(pool.len(), 1);
let v2 = pool.get();
assert!(pool.is_empty());
assert!(v2.capacity() >= 10);
}
#[test]
fn test_clearing_pool() {
let pool: ClearingPool<Vec<i32>> = ClearingPool::new(|| Vec::with_capacity(10));
let mut v1 = pool.get();
v1.push(1);
v1.push(2);
v1.push(3);
pool.put(v1);
let v2 = pool.get();
assert!(v2.is_empty()); assert!(v2.capacity() >= 10); }
#[test]
fn test_shared_geometry() {
let g1 = SharedGeometry::new("test", vec![(0.0, 0.0), (1.0, 0.0), (1.0, 1.0)]);
assert_eq!(g1.strong_count(), 1);
let g2 = g1.share();
assert_eq!(g1.strong_count(), 2);
assert_eq!(g2.strong_count(), 2);
assert_eq!(g1.vertices().len(), 3);
assert_eq!(g2.vertices().len(), 3);
}
#[test]
fn test_geometry_cache() {
let mut cache: GeometryCache<(f64, f64)> = GeometryCache::new();
let g1 = cache.get_or_insert(
"rect1",
vec![(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)],
);
let g2 = cache.get_or_insert("rect1", vec![]);
assert_eq!(g1.strong_count(), 3); assert_eq!(g2.strong_count(), 3);
assert_eq!(cache.len(), 1);
}
#[test]
fn test_scratch_buffer() {
let scratch = ScratchBuffer::<i32>::new(100);
let sum = scratch.with_buffer(|buf| {
buf.push(1);
buf.push(2);
buf.push(3);
buf.iter().sum::<i32>()
});
assert_eq!(sum, 6);
scratch.with_buffer(|buf| {
assert!(buf.is_empty());
});
}
#[test]
fn test_memory_stats() {
let mut stats = MemoryStats::new();
stats.record_pool_hit();
stats.record_pool_hit();
stats.record_pool_miss();
assert_eq!(stats.pool_hits, 2);
assert_eq!(stats.pool_misses, 1);
assert!((stats.hit_rate() - 0.666).abs() < 0.01);
}
}