use std::alloc::{GlobalAlloc, Layout};
use std::collections::BTreeMap;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::atomic::AtomicU64;
use std::collections::HashMap;
use std::time::SystemTime;
pub struct ZeroCopyAllocator {
pools: Arc<Mutex<HashMap<usize, MemoryPool>>>,
large_allocations: Arc<Mutex<HashMap<usize, LargeAllocation>>>,
stats: Arc<AllocatorStats>,
config: AllocatorConfig,
}
#[derive(Debug)]
struct MemoryPool {
free_blocks: Vec<*mut u8>,
allocated_blocks: Vec<*mut u8>,
block_size: usize,
max_blocks: usize,
created_at: Instant,
last_used: Instant,
}
#[derive(Debug)]
struct LargeAllocation {
ptr: *mut u8,
size: usize,
created_at: Instant,
usage_count: AtomicUsize,
}
pub struct AllocatorStats {
pub total_allocations: AtomicU64,
pub total_deallocations: AtomicU64,
pub zero_copy_allocations: AtomicU64,
pub pool_hits: AtomicU64,
pub pool_misses: AtomicU64,
pub active_allocations: AtomicUsize,
pub total_allocated_bytes: AtomicU64,
pub total_freed_bytes: AtomicU64,
}
#[derive(Debug, Clone)]
pub struct AllocatorConfig {
pub pool_sizes: Vec<usize>,
pub initial_pool_blocks: usize,
pub max_pool_blocks: usize,
pub large_allocation_threshold: usize,
pub pool_cleanup_interval: Duration,
pub compression_threshold: usize,
}
impl Default for AllocatorConfig {
fn default() -> Self {
Self {
pool_sizes: vec![16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384],
initial_pool_blocks: 100,
max_pool_blocks: 10000,
large_allocation_threshold: 64 * 1024, pool_cleanup_interval: Duration::from_secs(300), compression_threshold: 1024, }
}
}
impl ZeroCopyAllocator {
pub fn new(config: AllocatorConfig) -> Self {
let mut pools = HashMap::new();
for &size in &config.pool_sizes {
pools.insert(size, MemoryPool::new(size, config.initial_pool_blocks));
}
Self {
pools: Arc::new(Mutex::new(pools)))
large_allocations: Arc::new(Mutex::new(HashMap::new()))
stats: Arc::new(Mutex::new(AllocatorStats {)),
total_allocations: AtomicU64::new(0))
total_deallocations: AtomicU64::new(0),
zero_copy_allocations: AtomicU64::new(0),
pool_hits: AtomicU64::new(0),
pool_misses: AtomicU64::new(0),
active_allocations: AtomicUsize::new(0),
total_allocated_bytes: AtomicU64::new(0),
total_freed_bytes: AtomicU64::new(0),
}),
config,
}
}
pub fn allocate(&self, size: usize) -> *mut u8 {
self.stats.total_allocations.fetch_add(1, Ordering::Relaxed);
self.stats.active_allocations.fetch_add(1, Ordering::Relaxed);
self.stats.total_allocated_bytes.fetch_add(size as u64, Ordering::Relaxed);
if let Some(pool_size) = self.find_closest_pool_size(size) {
if let Some(ptr) = self.allocate_from_pool(pool_size, size) {
self.stats.zero_copy_allocations.fetch_add(1, Ordering::Relaxed);
self.stats.pool_hits.fetch_add(1, Ordering::Relaxed);
return ptr;
} else {
self.stats.pool_misses.fetch_add(1, Ordering::Relaxed);
}
}
if size >= self.config.large_allocation_threshold {
self.allocate_large(size)
} else {
unsafe {
let layout: _ = Layout::from_size_align_unchecked(size, 1);
std::alloc::alloc(layout)
}
}
}
pub fn deallocate(&self, ptr: *mut u8, size: usize) {
self.stats.total_deallocations.fetch_add(1, Ordering::Relaxed);
self.stats.active_allocations.fetch_sub(1, Ordering::Relaxed);
self.stats.total_freed_bytes.fetch_add(size as u64, Ordering::Relaxed);
{
let large_allocs: _ = self.large_allocations.lock().unwrap();
for (_, alloc) in large_allocs.iter() {
if alloc.ptr == ptr {
drop(large_allocs);
self.deallocate_large(ptr, size);
return;
}
}
}
if let Some(pool_size) = self.find_closest_pool_size(size) {
if self.deallocate_to_pool(pool_size, ptr) {
return;
}
}
unsafe {
let layout: _ = Layout::from_size_align_unchecked(size, 1);
std::alloc::dealloc(ptr, layout);
}
}
fn find_closest_pool_size(&self, size: usize) -> Option<usize> {
self.config.pool_sizes.iter()
.cloned()
.filter(|pool_size| *pool_size >= size)
.min()
}
fn allocate_from_pool(&self, pool_size: usize, requested_size: usize) -> Option<*mut u8> {
let mut pools = self.pools.lock().unwrap();
if let Some(pool) = pools.get_mut(&pool_size) {
if let Some(ptr) = pool.allocate(requested_size) {
Some(ptr)
} else {
None
}
} else {
None
}
}
fn deallocate_to_pool(&self, pool_size: usize, ptr: *mut u8) -> bool {
let mut pools = self.pools.lock().unwrap();
if let Some(pool) = pools.get_mut(&pool_size) {
pool.deallocate(ptr);
true
} else {
false
}
}
fn allocate_large(&self, size: usize) -> *mut u8 {
let layout: _ = unsafe { Layout::from_size_align_unchecked(size, 1) };
let ptr: _ = unsafe { std::alloc::alloc(layout) };
if !ptr.is_null() {
let mut large_allocs = self.large_allocations.lock().unwrap();
large_allocs.insert(
ptr as usize,
LargeAllocation {
ptr,
size,
created_at: Instant::now(),
usage_count: AtomicUsize::new(1),
}
);
}
ptr
}
fn deallocate_large(&self, ptr: *mut u8, size: usize) {
let mut large_allocs = self.large_allocations.lock().unwrap();
large_allocs.remove(&(ptr as usize));
unsafe {
let layout: _ = Layout::from_size_align_unchecked(size, 1);
std::alloc::dealloc(ptr, layout);
}
}
pub fn generate_address_for_tracking(&self) -> usize {
self.generate_address()
}
fn generate_address(&self) -> usize {
let timestamp: _ = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos() as usize;
let random: _ = fastrand::usize(..);
timestamp ^ random
}
pub fn get_stats(&self) -> AllocatorStatsSnapshot {
let pools: _ = self.pools.lock().unwrap();
let total_pool_blocks: usize = pools.values()
.map(|pool| pool.free_blocks.len() + pool.allocated_blocks.len())
.sum();
AllocatorStatsSnapshot {
total_allocations: self.stats.total_allocations.load(Ordering::Relaxed),
total_deallocations: self.stats.total_deallocations.load(Ordering::Relaxed),
zero_copy_allocations: self.stats.zero_copy_allocations.load(Ordering::Relaxed),
pool_hits: self.stats.pool_hits.load(Ordering::Relaxed),
pool_misses: self.stats.pool_misses.load(Ordering::Relaxed),
active_allocations: self.stats.active_allocations.load(Ordering::Relaxed),
total_allocated_bytes: self.stats.total_allocated_bytes.load(Ordering::Relaxed),
total_freed_bytes: self.stats.total_freed_bytes.load(Ordering::Relaxed),
total_pool_blocks,
large_allocations_count: self.large_allocations.lock().unwrap().len(),
}
}
pub fn cleanup_idle_pools(&self) {
let mut pools = self.pools.lock().unwrap();
let now: _ = Instant::now();
for pool in pools.values_mut() {
if now.duration_since(pool.last_used) > Duration::from_secs(300) {
pool.cleanup();
}
}
}
}
#[derive(Debug, Clone)]
pub struct AllocatorStatsSnapshot {
pub total_allocations: u64,
pub total_deallocations: u64,
pub zero_copy_allocations: u64,
pub pool_hits: u64,
pub pool_misses: u64,
pub active_allocations: usize,
pub total_allocated_bytes: u64,
pub total_freed_bytes: u64,
pub total_pool_blocks: usize,
pub large_allocations_count: usize,
}
impl MemoryPool {
fn new(block_size: usize, initial_blocks: usize) -> Self {
let mut free_blocks = Vec::with_capacity(initial_blocks);
for _ in 0..initial_blocks {
let layout: _ = unsafe { Layout::from_size_align_unchecked(block_size, 1) };
let ptr: _ = unsafe { std::alloc::alloc(layout) };
if !ptr.is_null() {
free_blocks.push(ptr);
}
}
Self {
free_blocks,
allocated_blocks: Vec::new(),
block_size,
max_blocks: 10000,
created_at: Instant::now(),
last_used: Instant::now(),
}
}
fn allocate(&mut self, _requested_size: usize) -> Option<*mut u8> {
self.last_used = Instant::now();
if let Some(ptr) = self.free_blocks.pop() {
self.allocated_blocks.push(ptr);
Some(ptr)
} else if self.allocated_blocks.len() < self.max_blocks {
let layout: _ = unsafe { Layout::from_size_align_unchecked(self.block_size, 1) };
let ptr: _ = unsafe { std::alloc::alloc(layout) };
if !ptr.is_null() {
self.allocated_blocks.push(ptr);
Some(ptr)
} else {
None
}
} else {
None
}
}
fn deallocate(&mut self, ptr: *mut u8) {
self.last_used = Instant::now();
if let Some(pos) = self.allocated_blocks.iter().position(|&p| p == ptr) {
self.allocated_blocks.swap_remove(pos);
}
self.free_blocks.push(ptr);
}
fn cleanup(&mut self) {
let retain_count: _ = std::cmp::min(self.free_blocks.len(), 10);
let mut blocks_to_free = Vec::new();
if self.free_blocks.len() > retain_count {
blocks_to_free = self.free_blocks.split_off(retain_count);
}
for ptr in blocks_to_free {
unsafe {
let layout: _ = Layout::from_size_align_unchecked(self.block_size, 1);
std::alloc::dealloc(ptr, layout);
}
}
}
}
impl Drop for MemoryPool {
fn drop(&mut self) {
let all_blocks: _ = std::mem::take(&mut self.free_blocks);
for ptr in all_blocks {
unsafe {
let layout: _ = Layout::from_size_align_unchecked(self.block_size, 1);
std::alloc::dealloc(ptr, layout);
}
}
let allocated_blocks: _ = std::mem::take(&mut self.allocated_blocks);
for ptr in allocated_blocks {
unsafe {
let layout: _ = Layout::from_size_align_unchecked(self.block_size, 1);
std::alloc::dealloc(ptr, layout);
}
}
}
}
unsafe impl GlobalAlloc for ZeroCopyAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
self.allocate(layout.size())
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
self.deallocate(ptr, layout.size());
}
}