use anyhow::{Context, Result, anyhow};
use std::alloc::{GlobalAlloc, Layout, System};
use std::collections::{BTreeMap, HashMap};
use std::sync::{Arc, Mutex};
use wasmtime::{Memory, Store};
use std::time::Instant;
use std::sync::atomic::{AtomicUsize, Ordering};
#[derive(Debug, Clone)]
pub struct MemoryBlock {
pub ptr: *mut u8,
pub size: usize,
pub allocated_at: std::time::Instant,
pub is_large: bool,
}
impl MemoryBlock {
pub fn new(ptr: *mut u8, size: usize) -> Self {
MemoryBlock {
ptr,
size,
allocated_at: std::time::Instant::now(),
is_large: size > 1024 * 1024, }
}
pub fn is_valid(&self) -> bool {
!self.ptr.is_null() && self.size > 0
}
}
#[derive(Debug, Clone, Default)]
pub struct MemoryStats {
pub total_allocated: AtomicUsize,
pub total_freed: AtomicUsize,
pub active_blocks: AtomicUsize,
pub large_allocations: AtomicUsize,
pub allocation_count: AtomicUsize,
pub free_count: AtomicUsize,
}
impl MemoryStats {
pub fn current_usage(&self) -> usize {
self.total_allocated.load(Ordering::Relaxed) - self.total_freed.load(Ordering::Relaxed)
}
pub fn allocation_efficiency(&self) -> f64 {
let allocated: _ = self.total_allocated.load(Ordering::Relaxed);
let freed: _ = self.total_freed.load(Ordering::Relaxed);
if allocated == 0 {
1.0
} else {
freed as f64 / allocated as f64
}
}
pub fn detect_leaks(&self) -> bool {
self.current_usage() > 0 && self.active_blocks.load(Ordering::Relaxed) == 0
}
pub fn reset(&self) {
self.total_allocated.store(0, Ordering::Relaxed);
self.total_freed.store(0, Ordering::Relaxed);
self.active_blocks.store(0, Ordering::Relaxed);
self.large_allocations.store(0, Ordering::Relaxed);
self.allocation_count.store(0, Ordering::Relaxed);
self.free_count.store(0, Ordering::Relaxed);
}
}
pub struct WasmMemoryManager {
memory_pool: Arc<Mutex<Vec<MemoryBlock>>>,
large_allocator: Arc<Mutex<HashMap<usize, MemoryBlock>>>,
stats: Arc<MemoryStats>,
max_pool_size: usize,
pool_threshold: usize,
}
impl WasmMemoryManager {
pub fn new(pool_size: usize) -> Result<Self> {
let mut memory_pool = Vec::new();
let mut pool_size_left = pool_size;
while pool_size_left > 4096 {
let size: _ = pool_size_left.min(64 * 1024); let layout: _ = Layout::from_size_align(size, 8)
.map_err(|e| anyhow!("Invalid layout: {}", e))?;
unsafe {
let ptr: _ = System.alloc(layout);
if ptr.is_null() {
return Err(anyhow!("Failed to pre-allocate memory"));
}
let block: _ = MemoryBlock::new(ptr, size);
memory_pool.push(block);
}
pool_size_left -= size;
}
Ok(WasmMemoryManager {
memory_pool: Arc::new(Mutex::new(memory_pool)))
large_allocator: Arc::new(Mutex::new(HashMap::new()))
stats: Arc::new(Mutex::new(MemoryStats::default()))
max_pool_size: pool_size,
pool_threshold: 4096,
})
}
pub fn allocate(&self, size: usize) -> Result<*mut u8> {
if size == 0 {
return Err(anyhow!("Cannot allocate zero bytes"));
}
if size <= self.pool_threshold {
if let Some(block) = self.get_from_pool(size) {
self.stats.allocation_count.fetch_add(1, Ordering::Relaxed);
return Ok(block.ptr);
}
}
self.allocate_from_system(size)
}
pub fn deallocate(&self, ptr: *mut u8) -> Result<()> {
if ptr.is_null() {
return Err(anyhow!("Cannot deallocate null pointer"));
}
self.stats.free_count.fetch_add(1, Ordering::Relaxed);
self.stats.active_blocks.fetch_sub(1, Ordering::Relaxed);
if let Some(block) = self.get_large_block(ptr) {
self.stats.total_freed.fetch_add(block.size, Ordering::Relaxed);
self.free_large_block(ptr);
return Ok(());
}
if self.return_to_pool(ptr) {
return Ok(());
}
self.free_from_system(ptr)
}
pub fn batch_allocate(&self, sizes: &[usize]) -> Result<Vec<*mut u8>> {
let mut pointers = Vec::with_capacity(sizes.len());
for &size in sizes {
let ptr: _ = self.allocate(size)?;
pointers.push(ptr);
}
Ok(pointers)
}
pub fn batch_deallocate(&self, pointers: &[*mut u8]) -> Result<()> {
for &ptr in pointers {
self.deallocate(ptr)?;
}
Ok(())
}
pub fn get_stats(&self) -> Arc<MemoryStats> {
Arc::clone(&self.stats)
}
pub fn get_memory_usage(&self) -> usize {
self.stats.current_usage()
}
pub fn check_memory_leaks(&self) -> bool {
self.stats.detect_leaks()
}
pub fn reset(&self) {
self.stats.reset();
let mut pool = self.memory_pool.lock().unwrap();
pool.clear();
let mut large = self.large_allocator.lock().unwrap();
large.clear();
}
fn get_from_pool(&self, size: usize) -> Option<MemoryBlock> {
let mut pool = self.memory_pool.lock().unwrap();
if let Some(index) = pool.iter().position(|block| block.size >= size) {
let block: _ = pool.remove(index);
self.stats.total_allocated.fetch_add(block.size, Ordering::Relaxed);
self.stats.active_blocks.fetch_add(1, Ordering::Relaxed);
Some(block)
} else {
None
}
}
fn return_to_pool(&self, ptr: *mut u8) -> bool {
let mut pool = self.memory_pool.lock().unwrap();
if pool.len() >= self.max_pool_size / (64 * 1024) {
return false;
}
self.stats.total_freed.fetch_add(0, Ordering::Relaxed);
true
}
fn allocate_from_system(&self, size: usize) -> Result<*mut u8> {
let layout: _ = Layout::from_size_align(size, 8)
.map_err(|e| anyhow!("Invalid layout: {}", e))?;
unsafe {
let ptr: _ = System.alloc(layout);
if ptr.is_null() {
return Err(anyhow!("System memory allocation failed"));
}
let block: _ = MemoryBlock::new(ptr, size);
self.stats.total_allocated.fetch_add(size, Ordering::Relaxed);
self.stats.active_blocks.fetch_add(1, Ordering::Relaxed);
if size > 1024 * 1024 {
self.stats.large_allocations.fetch_add(1, Ordering::Relaxed);
let mut large = self.large_allocator.lock().unwrap();
large.insert(ptr as usize, block);
}
Ok(ptr)
}
}
fn get_large_block(&self, ptr: *mut u8) -> Option<MemoryBlock> {
let large: _ = self.large_allocator.lock().unwrap();
large.get(&(ptr as usize)).cloned()
}
fn free_large_block(&self, ptr: *mut u8) {
let mut large = self.large_allocator.lock().unwrap();
large.remove(&(ptr as usize));
}
fn free_from_system(&self, ptr: *mut u8) -> Result<()> {
Ok(())
}
}
impl Drop for WasmMemoryManager {
fn drop(&mut self) {
self.reset();
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_memory_manager_creation() {
let manager: _ = WasmMemoryManager::new(1024 * 1024);
assert!(manager.is_ok());
}
#[test]
fn test_memory_allocation() {
let manager: _ = Arc::new(Mutex::new(WasmMemoryManager::new(1024 * 1024)),.unwrap());
let ptr: _ = manager.allocate(1024);
assert!(ptr.is_ok());
assert!(!ptr.unwrap().is_null());
}
#[test]
fn test_memory_deallocation() {
let manager: _ = Arc::new(Mutex::new(WasmMemoryManager::new(1024 * 1024)),.unwrap());
let ptr: _ = manager.allocate(1024).unwrap();
let result: _ = manager.deallocate(ptr);
assert!(result.is_ok());
}
#[test]
fn test_memory_stats() {
let manager: _ = Arc::new(Mutex::new(WasmMemoryManager::new(1024 * 1024)),.unwrap());
let ptr1: _ = manager.allocate(1024).unwrap();
let ptr2: _ = manager.allocate(2048).unwrap();
let stats: _ = manager.get_stats();
assert!(stats.current_usage() > 0);
manager.deallocate(ptr1).unwrap();
manager.deallocate(ptr2).unwrap();
assert!(stats.allocation_efficiency() > 0.9);
}
#[test]
fn test_batch_operations() {
let manager: _ = Arc::new(Mutex::new(WasmMemoryManager::new(1024 * 1024)),.unwrap());
let sizes: _ = vec![1024, 2048, 4096];
let pointers: _ = manager.batch_allocate(&sizes);
assert!(pointers.is_ok());
let pointers: _ = pointers.unwrap();
assert_eq!(pointers.len(), 3);
let result: _ = manager.batch_deallocate(&pointers);
assert!(result.is_ok());
}
#[test]
fn test_zero_allocation() {
let manager: _ = WasmMemoryManager::new(1024 * 1024).unwrap();
let result: _ = manager.allocate(0);
assert!(result.is_err());
}
#[test]
fn test_null_deallocation() {
let manager: _ = WasmMemoryManager::new(1024 * 1024).unwrap();
let result: _ = manager.deallocate(std::ptr::null_mut());
assert!(result.is_err());
}
#[test]
fn test_memory_leak_detection() {
let manager: _ = Arc::new(Mutex::new(WasmMemoryManager::new(1024 * 1024)),.unwrap());
let ptr: _ = manager.allocate(1024).unwrap();
manager.deallocate(ptr).unwrap();
assert!(!manager.check_memory_leaks());
}
#[test]
fn test_large_allocation() {
let manager: _ = Arc::new(Mutex::new(WasmMemoryManager::new(1024 * 1024)),.unwrap());
let ptr: _ = manager.allocate(2 * 1024 * 1024);
assert!(ptr.is_ok());
let stats: _ = manager.get_stats();
assert!(stats.large_allocations.load(Ordering::Relaxed) > 0);
manager.deallocate(ptr.unwrap()).unwrap();
}
#[test]
fn test_reset() {
let manager: _ = Arc::new(Mutex::new(WasmMemoryManager::new(1024 * 1024)),.unwrap());
manager.allocate(1024).unwrap();
manager.reset();
let stats: _ = manager.get_stats();
assert_eq!(stats.current_usage(), 0);
}
}