use std::{
sync::{
Arc,
atomic::{AtomicBool, AtomicUsize, Ordering},
},
};
use ahash::AHashMap;
use arc_swap::ArcSwap;
use parking_lot::Mutex;
use super::{MemoryPoolConfig, MemoryStats, calc_ratio};
#[derive(Debug)]
pub struct MemoryPool {
config: MemoryPoolConfig,
allocated_blocks: Mutex<AHashMap<usize, Vec<Box<[u8]>>>>,
stats: ArcSwap<MemoryStats>,
active: AtomicBool,
}
impl MemoryPool {
#[must_use]
pub fn new(config: MemoryPoolConfig) -> Self {
Self {
config,
allocated_blocks: Mutex::new(AHashMap::new()),
stats: ArcSwap::new(Arc::new(MemoryStats::default())),
active: AtomicBool::new(true),
}
}
pub fn allocate(&self, size: usize) -> Result<*mut u8, std::io::Error> {
if !self.active.load(Ordering::Relaxed) {
return Err(std::io::Error::other("Pool is inactive"));
}
if let Some(max_size) = self.config.max_size {
let current_stats = self.stats.load();
if current_stats.allocated_bytes + size > max_size {
return Err(std::io::Error::other("Pool size limit exceeded"));
}
}
let mut boxed = vec![0u8; size].into_boxed_slice();
let ptr = boxed.as_mut_ptr();
let mut blocks = self.allocated_blocks.lock();
blocks.entry(size).or_default().push(boxed);
let mut new_stats = (**self.stats.load()).clone();
new_stats.allocated_bytes += size;
new_stats.allocation_count += 1;
new_stats.total_allocated_bytes += size;
new_stats.peak_allocated_bytes = new_stats
.peak_allocated_bytes
.max(new_stats.allocated_bytes);
self.stats.store(Arc::new(new_stats));
Ok(ptr)
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn deallocate(&self, ptr: *mut u8, size: usize) -> Result<(), std::io::Error> {
if ptr.is_null() {
return Ok(());
}
let mut blocks = self.allocated_blocks.lock();
if let Some(block_list) = blocks.get_mut(&size) {
if let Some(pos) = block_list.iter().position(|b| b.as_ptr().cast_mut() == ptr) {
block_list.remove(pos);
} else {
return Err(std::io::Error::other("Allocation not found in pool"));
}
} else {
return Err(std::io::Error::other("Allocation size not found in pool"));
}
let mut new_stats = (**self.stats.load()).clone();
new_stats.allocated_bytes = new_stats.allocated_bytes.saturating_sub(size);
new_stats.deallocation_count += 1;
self.stats.store(Arc::new(new_stats));
Ok(())
}
#[must_use]
pub fn stats(&self) -> Arc<MemoryStats> {
self.stats.load_full()
}
#[must_use]
pub fn config(&self) -> &MemoryPoolConfig {
&self.config
}
#[must_use]
pub fn is_active(&self) -> bool {
self.active.load(Ordering::Relaxed)
}
pub fn deactivate(&self) {
self.active.store(false, Ordering::Relaxed);
}
#[allow(clippy::cast_precision_loss)]
#[must_use]
pub fn fragmentation_ratio(&self) -> f64 {
let stats = self.stats.load();
calc_ratio(stats.allocated_bytes, stats.heap_size)
}
}
impl Default for MemoryPool {
fn default() -> Self {
Self::new(MemoryPoolConfig {
initial_size: 1024 * 1024,
max_size: Some(100 * 1024 * 1024),
alignment: 8,
name: "default".to_string(),
})
}
}
#[derive(Debug)]
pub struct MemoryArena {
buffer: Mutex<Vec<u8>>,
offset: AtomicUsize,
capacity: usize,
name: String,
}
impl MemoryArena {
#[must_use]
pub fn new(capacity: usize, name: String) -> Self {
Self {
buffer: Mutex::new(vec![0u8; capacity]),
offset: AtomicUsize::new(0),
capacity,
name,
}
}
pub fn allocate(&self, size: usize) -> Result<Vec<u8>, std::io::Error> {
let current_offset = self.offset.fetch_add(size, Ordering::Relaxed);
if current_offset + size > self.capacity {
return Err(std::io::Error::new(
std::io::ErrorKind::OutOfMemory,
"Arena full",
));
}
let buffer = self.buffer.lock();
let slice = &buffer[current_offset..current_offset + size];
Ok(slice.to_vec())
}
pub fn reset(&self) {
self.offset.store(0, Ordering::Relaxed);
}
#[must_use]
pub fn usage(&self) -> usize {
self.offset.load(Ordering::Relaxed)
}
#[must_use]
pub fn capacity(&self) -> usize {
self.capacity
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
}
pub struct ThreadLocalCache<T> {
cache: Mutex<Vec<T>>,
max_size: usize,
factory: Box<dyn Fn() -> T + Send + Sync>,
}
impl<T> ThreadLocalCache<T> {
#[must_use]
pub fn new(max_size: usize, factory: Box<dyn Fn() -> T + Send + Sync>) -> Self {
Self {
cache: Mutex::new(Vec::new()),
max_size,
factory,
}
}
pub fn get_or_create(&self) -> T {
let mut cache = self.cache.lock();
if let Some(item) = cache.pop() {
item
} else {
(self.factory)()
}
}
pub fn return_item(&self, item: T) {
let mut cache = self.cache.lock();
if cache.len() < self.max_size {
cache.push(item);
}
}
#[must_use]
pub fn size(&self) -> usize {
self.cache.lock().len()
}
pub fn clear(&self) {
self.cache.lock().clear();
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.cache.lock().is_empty()
}
}