use crate::{UtilsError, UtilsResult};
use std::alloc::{GlobalAlloc, Layout};
use std::collections::HashMap;
use std::fs::File;
use std::sync::{Arc, Mutex, RwLock};
use std::time::{Duration, Instant};
pub struct TrackingAllocator<A: GlobalAlloc> {
inner: A,
stats: Arc<RwLock<AllocationStats>>,
}
#[derive(Debug, Clone, Default)]
pub struct AllocationStats {
pub total_allocated: u64,
pub total_deallocated: u64,
pub current_allocated: u64,
pub peak_allocated: u64,
pub allocation_count: u64,
pub deallocation_count: u64,
pub leak_count: u64,
}
impl<A: GlobalAlloc> TrackingAllocator<A> {
pub fn new(inner: A) -> Self {
Self {
inner,
stats: Arc::new(RwLock::new(AllocationStats::default())),
}
}
pub fn stats(&self) -> AllocationStats {
self.stats.read().expect("operation should succeed").clone()
}
pub fn reset_stats(&self) {
let mut stats = self.stats.write().expect("operation should succeed");
*stats = AllocationStats::default();
}
}
unsafe impl<A: GlobalAlloc> GlobalAlloc for TrackingAllocator<A> {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let ptr = self.inner.alloc(layout);
if !ptr.is_null() {
let mut stats = self.stats.write().expect("operation should succeed");
stats.total_allocated += layout.size() as u64;
stats.current_allocated += layout.size() as u64;
stats.allocation_count += 1;
if stats.current_allocated > stats.peak_allocated {
stats.peak_allocated = stats.current_allocated;
}
}
ptr
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
self.inner.dealloc(ptr, layout);
let mut stats = self.stats.write().expect("operation should succeed");
stats.total_deallocated += layout.size() as u64;
stats.current_allocated = stats.current_allocated.saturating_sub(layout.size() as u64);
stats.deallocation_count += 1;
}
}
pub struct MemoryPool<T> {
blocks: Vec<Box<[T]>>,
free_list: Vec<*mut T>,
block_size: usize,
stats: AllocationStats,
}
impl<T: Default + Clone> MemoryPool<T> {
pub fn new(block_size: usize) -> Self {
Self {
blocks: Vec::new(),
free_list: Vec::new(),
block_size,
stats: AllocationStats::default(),
}
}
pub fn allocate(&mut self) -> Option<&mut T> {
if self.free_list.is_empty() {
self.add_block();
}
if let Some(ptr) = self.free_list.pop() {
self.stats.allocation_count += 1;
self.stats.current_allocated += std::mem::size_of::<T>() as u64;
unsafe { Some(&mut *ptr) }
} else {
None
}
}
pub fn deallocate(&mut self, item: &mut T) {
let ptr = item as *mut T;
self.free_list.push(ptr);
self.stats.deallocation_count += 1;
self.stats.current_allocated = self
.stats
.current_allocated
.saturating_sub(std::mem::size_of::<T>() as u64);
}
fn add_block(&mut self) {
let mut block = vec![T::default(); self.block_size].into_boxed_slice();
for item in block.iter_mut() {
self.free_list.push(item as *mut T);
}
self.blocks.push(block);
self.stats.total_allocated += (self.block_size * std::mem::size_of::<T>()) as u64;
}
pub fn stats(&self) -> &AllocationStats {
&self.stats
}
pub fn capacity(&self) -> usize {
self.blocks.len() * self.block_size
}
pub fn used(&self) -> usize {
self.capacity() - self.free_list.len()
}
}
pub struct LeakDetector {
allocations: Arc<Mutex<HashMap<usize, AllocationInfo>>>,
enabled: bool,
}
#[derive(Debug, Clone)]
pub struct AllocationInfo {
pub size: usize,
pub timestamp: Instant,
pub backtrace: String,
}
impl LeakDetector {
pub fn new() -> Self {
Self {
allocations: Arc::new(Mutex::new(HashMap::new())),
enabled: true,
}
}
pub fn enable(&mut self) {
self.enabled = true;
}
pub fn disable(&mut self) {
self.enabled = false;
}
pub fn track_allocation(&self, ptr: *mut u8, size: usize) {
if !self.enabled {
return;
}
let mut allocations = self.allocations.lock().expect("operation should succeed");
allocations.insert(
ptr as usize,
AllocationInfo {
size,
timestamp: Instant::now(),
backtrace: format!("Allocation at {ptr:p}"), },
);
}
pub fn track_deallocation(&self, ptr: *mut u8) {
if !self.enabled {
return;
}
let mut allocations = self.allocations.lock().expect("operation should succeed");
allocations.remove(&(ptr as usize));
}
pub fn check_leaks(&self) -> Vec<AllocationInfo> {
let allocations = self.allocations.lock().expect("operation should succeed");
allocations.values().cloned().collect()
}
pub fn check_leaks_older_than(&self, duration: Duration) -> Vec<AllocationInfo> {
let allocations = self.allocations.lock().expect("operation should succeed");
let now = Instant::now();
allocations
.values()
.filter(|info| now.duration_since(info.timestamp) > duration)
.cloned()
.collect()
}
pub fn total_leaked_bytes(&self) -> usize {
let allocations = self.allocations.lock().expect("operation should succeed");
allocations.values().map(|info| info.size).sum()
}
pub fn clear(&self) {
let mut allocations = self.allocations.lock().expect("operation should succeed");
allocations.clear();
}
}
impl Default for LeakDetector {
fn default() -> Self {
Self::new()
}
}
pub struct MemoryMappedFile {
#[allow(dead_code)]
file: File,
ptr: *mut u8,
size: usize,
}
impl MemoryMappedFile {
#[cfg(unix)]
pub fn new(file: File, writable: bool) -> Result<Self, std::io::Error> {
use std::os::unix::io::AsRawFd;
let size = file.metadata()?.len() as usize;
let prot = if writable {
libc::PROT_READ | libc::PROT_WRITE
} else {
libc::PROT_READ
};
let ptr = unsafe {
libc::mmap(
std::ptr::null_mut(),
size,
prot,
libc::MAP_SHARED,
file.as_raw_fd(),
0,
)
};
if ptr == libc::MAP_FAILED {
return Err(std::io::Error::last_os_error());
}
Ok(Self {
file,
ptr: ptr as *mut u8,
size,
})
}
#[cfg(windows)]
pub fn new(file: File, writable: bool) -> Result<Self, std::io::Error> {
use std::os::windows::io::AsRawHandle;
use winapi::um::handleapi::CloseHandle;
use winapi::um::memoryapi::{
CreateFileMappingW, MapViewOfFile, FILE_MAP_READ, FILE_MAP_WRITE,
};
use winapi::um::winnt::{PAGE_READONLY, PAGE_READWRITE};
let size = file.metadata()?.len() as usize;
let protect = if writable {
PAGE_READWRITE
} else {
PAGE_READONLY
};
let access = if writable {
FILE_MAP_WRITE
} else {
FILE_MAP_READ
};
let mapping = unsafe {
CreateFileMappingW(
file.as_raw_handle() as _,
std::ptr::null_mut(),
protect,
0,
0,
std::ptr::null(),
)
};
if mapping.is_null() {
return Err(std::io::Error::last_os_error());
}
let ptr = unsafe { MapViewOfFile(mapping, access, 0, 0, 0) };
unsafe { CloseHandle(mapping) };
if ptr.is_null() {
return Err(std::io::Error::last_os_error());
}
Ok(Self {
file,
ptr: ptr as *mut u8,
size,
})
}
#[cfg(not(any(unix, windows)))]
pub fn new(_file: File, _writable: bool) -> Result<Self, std::io::Error> {
Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"Memory mapping not supported on this platform",
))
}
pub fn as_slice(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.ptr, self.size) }
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { std::slice::from_raw_parts_mut(self.ptr, self.size) }
}
pub fn size(&self) -> usize {
self.size
}
}
impl Drop for MemoryMappedFile {
fn drop(&mut self) {
if !self.ptr.is_null() {
#[cfg(unix)]
unsafe {
libc::munmap(self.ptr as *mut libc::c_void, self.size);
}
#[cfg(windows)]
unsafe {
winapi::um::memoryapi::UnmapViewOfFile(self.ptr as *mut winapi::ctypes::c_void);
}
}
}
}
unsafe impl Send for MemoryMappedFile {}
unsafe impl Sync for MemoryMappedFile {}
pub struct GcHelper<T> {
data: Arc<T>,
weak_refs: Arc<Mutex<Vec<std::sync::Weak<T>>>>,
}
impl<T> GcHelper<T> {
pub fn new(data: T) -> Self {
Self {
data: Arc::new(data),
weak_refs: Arc::new(Mutex::new(Vec::new())),
}
}
pub fn get_ref(&self) -> Arc<T> {
self.data.clone()
}
pub fn get_weak_ref(&self) -> std::sync::Weak<T> {
let weak = Arc::downgrade(&self.data);
let mut refs = self.weak_refs.lock().expect("operation should succeed");
refs.push(weak.clone());
weak
}
pub fn collect_garbage(&self) {
let mut refs = self.weak_refs.lock().expect("operation should succeed");
refs.retain(|weak_ref| weak_ref.upgrade().is_some());
}
pub fn ref_count(&self) -> usize {
Arc::strong_count(&self.data)
}
pub fn weak_ref_count(&self) -> usize {
let refs = self.weak_refs.lock().expect("operation should succeed");
refs.len()
}
}
impl<T> Clone for GcHelper<T> {
fn clone(&self) -> Self {
Self {
data: self.data.clone(),
weak_refs: self.weak_refs.clone(),
}
}
}
pub struct MemoryMonitor {
start_time: Instant,
peak_memory: u64,
current_memory: u64,
samples: Vec<(Instant, u64)>,
}
impl MemoryMonitor {
pub fn new() -> Self {
Self {
start_time: Instant::now(),
peak_memory: 0,
current_memory: 0,
samples: Vec::new(),
}
}
pub fn update(&mut self, memory_usage: u64) {
self.current_memory = memory_usage;
if memory_usage > self.peak_memory {
self.peak_memory = memory_usage;
}
self.samples.push((Instant::now(), memory_usage));
}
pub fn peak_memory(&self) -> u64 {
self.peak_memory
}
pub fn current_memory(&self) -> u64 {
self.current_memory
}
pub fn average_memory(&self) -> f64 {
if self.samples.is_empty() {
return 0.0;
}
let sum: u64 = self.samples.iter().map(|(_, mem)| *mem).sum();
sum as f64 / self.samples.len() as f64
}
pub fn memory_over_time(&self) -> &[(Instant, u64)] {
&self.samples
}
pub fn duration(&self) -> Duration {
Instant::now().duration_since(self.start_time)
}
}
impl Default for MemoryMonitor {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct SafeVec<T> {
data: Vec<T>,
bounds_check: bool,
}
impl<T> SafeVec<T> {
pub fn new() -> Self {
Self {
data: Vec::new(),
bounds_check: true,
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
data: Vec::with_capacity(capacity),
bounds_check: true,
}
}
pub fn from_vec(vec: Vec<T>) -> Self {
Self {
data: vec,
bounds_check: true,
}
}
pub fn disable_bounds_check(mut self) -> Self {
self.bounds_check = false;
self
}
pub fn get(&self, index: usize) -> UtilsResult<&T> {
if self.bounds_check && index >= self.data.len() {
return Err(UtilsError::InvalidParameter(format!(
"Index {} out of bounds for vector of length {}",
index,
self.data.len()
)));
}
self.data
.get(index)
.ok_or_else(|| UtilsError::InvalidParameter(format!("Index {index} out of bounds")))
}
pub fn get_mut(&mut self, index: usize) -> UtilsResult<&mut T> {
if self.bounds_check && index >= self.data.len() {
return Err(UtilsError::InvalidParameter(format!(
"Index {} out of bounds for vector of length {}",
index,
self.data.len()
)));
}
let len = self.data.len();
self.data.get_mut(index).ok_or_else(|| {
UtilsError::InvalidParameter(format!(
"Index {index} out of bounds for vector of length {len}"
))
})
}
pub fn safe_slice(&self, start: usize, end: usize) -> UtilsResult<&[T]> {
if self.bounds_check {
if start > end {
return Err(UtilsError::InvalidParameter(
"Start index cannot be greater than end index".to_string(),
));
}
if end > self.data.len() {
return Err(UtilsError::InvalidParameter(format!(
"End index {end} out of bounds for vector of length {}",
self.data.len()
)));
}
}
Ok(&self.data[start..end])
}
pub fn push(&mut self, item: T) {
self.data.push(item);
}
pub fn pop(&mut self) -> Option<T> {
self.data.pop()
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn capacity(&self) -> usize {
self.data.capacity()
}
pub fn reserve(&mut self, additional: usize) {
self.data.reserve(additional);
}
pub unsafe fn as_vec(&self) -> &Vec<T> {
&self.data
}
pub fn into_vec(self) -> Vec<T> {
self.data
}
}
impl<T> Default for SafeVec<T> {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct SafeBuffer<T> {
data: Vec<T>,
capacity: usize,
size: usize,
overflow_protection: bool,
}
impl<T: Clone> SafeBuffer<T> {
pub fn new(capacity: usize, default_value: T) -> Self {
Self {
data: vec![default_value; capacity],
capacity,
size: 0,
overflow_protection: true,
}
}
pub fn write(&mut self, index: usize, value: T) -> UtilsResult<()> {
if self.overflow_protection && index >= self.capacity {
return Err(UtilsError::InvalidParameter(format!(
"Buffer overflow: index {} exceeds capacity {}",
index, self.capacity
)));
}
if index < self.data.len() {
self.data[index] = value;
self.size = self.size.max(index + 1);
Ok(())
} else {
Err(UtilsError::InvalidParameter(format!(
"Index {} out of bounds for buffer of capacity {}",
index, self.capacity
)))
}
}
pub fn read(&self, index: usize) -> UtilsResult<&T> {
if index >= self.size {
return Err(UtilsError::InvalidParameter(format!(
"Index {} out of bounds for buffer of size {}",
index, self.size
)));
}
self.data
.get(index)
.ok_or_else(|| UtilsError::InvalidParameter(format!("Index {index} out of bounds")))
}
pub fn append(&mut self, value: T) -> UtilsResult<()> {
if self.size >= self.capacity {
return Err(UtilsError::InvalidParameter(
"Buffer overflow: cannot append to full buffer".to_string(),
));
}
self.data[self.size] = value;
self.size += 1;
Ok(())
}
pub fn size(&self) -> usize {
self.size
}
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn is_full(&self) -> bool {
self.size >= self.capacity
}
pub fn clear(&mut self) {
self.size = 0;
}
pub unsafe fn disable_overflow_protection(&mut self) {
self.overflow_protection = false;
}
}
pub struct SafePtr<T> {
data: Arc<RwLock<Option<T>>>,
cleanup_fn: Option<Box<dyn Fn() + Send + Sync>>,
}
impl<T> std::fmt::Debug for SafePtr<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SafePtr")
.field("data", &"Arc<RwLock<Option<T>>>")
.field(
"cleanup_fn",
&self.cleanup_fn.as_ref().map(|_| "Some(cleanup_fn)"),
)
.finish()
}
}
impl<T> SafePtr<T> {
pub fn new(value: T) -> Self {
Self {
data: Arc::new(RwLock::new(Some(value))),
cleanup_fn: None,
}
}
pub fn with_cleanup<F>(value: T, cleanup: F) -> Self
where
F: Fn() + Send + Sync + 'static,
{
Self {
data: Arc::new(RwLock::new(Some(value))),
cleanup_fn: Some(Box::new(cleanup)),
}
}
pub fn try_read(&self) -> UtilsResult<std::sync::RwLockReadGuard<'_, Option<T>>> {
self.data
.read()
.map_err(|e| UtilsError::InvalidParameter(format!("Failed to acquire read lock: {e}")))
}
pub fn try_write(&self) -> UtilsResult<std::sync::RwLockWriteGuard<'_, Option<T>>> {
self.data
.write()
.map_err(|e| UtilsError::InvalidParameter(format!("Failed to acquire write lock: {e}")))
}
pub fn is_valid(&self) -> bool {
if let Ok(guard) = self.data.read() {
guard.is_some()
} else {
false
}
}
pub fn take(&self) -> UtilsResult<Option<T>> {
let mut guard = self.try_write()?;
Ok(guard.take())
}
pub fn ref_count(&self) -> usize {
Arc::strong_count(&self.data)
}
}
impl<T> Clone for SafePtr<T> {
fn clone(&self) -> Self {
Self {
data: self.data.clone(),
cleanup_fn: None, }
}
}
impl<T> Drop for SafePtr<T> {
fn drop(&mut self) {
if Arc::strong_count(&self.data) == 1 {
if let Some(cleanup) = &self.cleanup_fn {
cleanup();
}
}
}
}
pub struct MemoryAlignment;
impl MemoryAlignment {
pub fn is_aligned<T>(ptr: *const T, alignment: usize) -> bool {
(ptr as usize) % alignment == 0
}
pub fn alignment_of<T>() -> usize {
std::mem::align_of::<T>()
}
pub fn aligned_size(size: usize, alignment: usize) -> usize {
(size + alignment - 1) & !(alignment - 1)
}
pub fn aligned_layout(
size: usize,
alignment: usize,
) -> Result<Layout, std::alloc::LayoutError> {
Layout::from_size_align(size, alignment)
}
}
pub struct StackGuard<F: FnOnce()> {
cleanup: Option<F>,
}
impl<F: FnOnce()> StackGuard<F> {
pub fn new(cleanup: F) -> Self {
Self {
cleanup: Some(cleanup),
}
}
pub fn cleanup(mut self) {
if let Some(cleanup) = self.cleanup.take() {
cleanup();
}
}
}
impl<F: FnOnce()> Drop for StackGuard<F> {
fn drop(&mut self) {
if let Some(cleanup) = self.cleanup.take() {
cleanup();
}
}
}
#[macro_export]
macro_rules! defer {
($cleanup:expr) => {
let _guard = $crate::memory::StackGuard::new(|| $cleanup);
};
}
pub struct MemoryValidator;
impl MemoryValidator {
pub unsafe fn validate_range<T>(ptr: *const T, count: usize) -> UtilsResult<()> {
if ptr.is_null() {
return Err(UtilsError::InvalidParameter("Null pointer".to_string()));
}
let end_ptr = unsafe { ptr.add(count) };
if end_ptr < ptr {
return Err(UtilsError::InvalidParameter("Pointer overflow".to_string()));
}
Ok(())
}
pub fn validate_alignment<T>(ptr: *const T, required_alignment: usize) -> UtilsResult<()> {
if !MemoryAlignment::is_aligned(ptr, required_alignment) {
return Err(UtilsError::InvalidParameter(format!(
"Pointer not aligned to {required_alignment} byte boundary"
)));
}
Ok(())
}
pub fn validate_buffer_access(
buffer_size: usize,
offset: usize,
access_size: usize,
) -> UtilsResult<()> {
if offset >= buffer_size {
return Err(UtilsError::InvalidParameter(format!(
"Offset {offset} exceeds buffer size {buffer_size}"
)));
}
if offset + access_size > buffer_size {
return Err(UtilsError::InvalidParameter(format!(
"Access range {}..{} exceeds buffer size {}",
offset,
offset + access_size,
buffer_size
)));
}
Ok(())
}
}
#[allow(non_snake_case)]
#[cfg(test)]
mod tests {
use super::*;
use std::alloc::System;
#[test]
fn test_tracking_allocator_stats() {
let allocator = TrackingAllocator::new(System);
let initial_stats = allocator.stats();
assert_eq!(initial_stats.allocation_count, 0);
assert_eq!(initial_stats.current_allocated, 0);
}
#[test]
fn test_memory_pool() {
let mut pool: MemoryPool<u64> = MemoryPool::new(10);
{
let item1 = pool.allocate().expect("operation should succeed");
*item1 = 42;
assert_eq!(*item1, 42);
}
assert_eq!(pool.used(), 1);
{
let item2 = pool.allocate().expect("operation should succeed");
*item2 = 84;
assert_eq!(*item2, 84);
}
assert_eq!(pool.used(), 2);
assert_eq!(pool.capacity(), 10);
}
#[test]
fn test_leak_detector() {
let detector = LeakDetector::new();
let ptr = Box::into_raw(Box::new(42u64));
detector.track_allocation(ptr as *mut u8, 8);
assert_eq!(detector.total_leaked_bytes(), 8);
detector.track_deallocation(ptr as *mut u8);
assert_eq!(detector.total_leaked_bytes(), 0);
unsafe { drop(Box::from_raw(ptr)) };
}
#[test]
fn test_gc_helper() {
let gc = GcHelper::new(42u64);
assert_eq!(gc.ref_count(), 1);
let strong_ref = gc.get_ref();
assert_eq!(gc.ref_count(), 2);
assert_eq!(*strong_ref, 42);
let weak_ref = gc.get_weak_ref();
assert!(weak_ref.upgrade().is_some());
drop(strong_ref);
assert_eq!(gc.ref_count(), 1);
}
#[test]
fn test_memory_monitor() {
let mut monitor = MemoryMonitor::new();
assert_eq!(monitor.peak_memory(), 0);
assert_eq!(monitor.current_memory(), 0);
monitor.update(1024);
assert_eq!(monitor.peak_memory(), 1024);
assert_eq!(monitor.current_memory(), 1024);
monitor.update(512);
assert_eq!(monitor.peak_memory(), 1024);
assert_eq!(monitor.current_memory(), 512);
monitor.update(2048);
assert_eq!(monitor.peak_memory(), 2048);
assert_eq!(monitor.current_memory(), 2048);
assert_eq!(monitor.average_memory(), (1024.0 + 512.0 + 2048.0) / 3.0);
}
#[test]
fn test_safe_vec() {
let mut safe_vec = SafeVec::new();
safe_vec.push(1);
safe_vec.push(2);
safe_vec.push(3);
assert_eq!(*safe_vec.get(0).expect("operation should succeed"), 1);
assert_eq!(*safe_vec.get(2).expect("operation should succeed"), 3);
assert!(safe_vec.get(5).is_err());
let slice = safe_vec.safe_slice(1, 3).expect("operation should succeed");
assert_eq!(slice, &[2, 3]);
assert!(safe_vec.safe_slice(2, 5).is_err());
assert!(safe_vec.safe_slice(3, 2).is_err());
}
#[test]
fn test_safe_buffer() {
let mut buffer = SafeBuffer::new(5, 0);
buffer.write(0, 42).expect("operation should succeed");
buffer.write(1, 84).expect("operation should succeed");
assert_eq!(*buffer.read(0).expect("operation should succeed"), 42);
assert_eq!(*buffer.read(1).expect("operation should succeed"), 84);
assert_eq!(buffer.size(), 2);
buffer.append(100).expect("operation should succeed");
buffer.append(200).expect("operation should succeed");
buffer.append(300).expect("operation should succeed");
assert!(buffer.is_full());
assert!(buffer.append(400).is_err());
assert!(buffer.write(10, 500).is_err()); }
#[test]
fn test_safe_ptr() {
let ptr = SafePtr::new(42);
assert!(ptr.is_valid());
assert_eq!(ptr.ref_count(), 1);
let _ptr2 = ptr.clone();
assert_eq!(ptr.ref_count(), 2);
{
let guard = ptr.try_read().expect("operation should succeed");
assert_eq!(*guard, Some(42));
}
let value = ptr.take().expect("operation should succeed");
assert_eq!(value, Some(42));
assert!(!ptr.is_valid());
}
#[test]
fn test_memory_alignment() {
let data = 42u64;
let ptr = &data as *const u64;
assert!(MemoryAlignment::is_aligned(ptr, 8)); assert_eq!(MemoryAlignment::alignment_of::<u64>(), 8);
assert_eq!(MemoryAlignment::aligned_size(10, 8), 16);
assert_eq!(MemoryAlignment::aligned_size(16, 8), 16);
assert_eq!(MemoryAlignment::aligned_size(17, 8), 24);
}
#[test]
fn test_stack_guard() {
use std::sync::Arc;
let cleanup_called = Arc::new(Mutex::new(false));
let cleanup_called_clone = cleanup_called.clone();
{
let _guard = StackGuard::new(|| {
*cleanup_called_clone
.lock()
.expect("operation should succeed") = true;
});
assert!(!*cleanup_called.lock().expect("operation should succeed"));
}
assert!(*cleanup_called.lock().expect("operation should succeed"));
}
#[test]
fn test_memory_validator() {
let null_ptr: *const u8 = std::ptr::null();
assert!(unsafe { MemoryValidator::validate_range(null_ptr, 10) }.is_err());
let data = [1u8, 2, 3, 4, 5];
let ptr = data.as_ptr();
assert!(unsafe { MemoryValidator::validate_range(ptr, 5) }.is_ok());
let aligned_ptr = &42u64 as *const u64;
assert!(MemoryValidator::validate_alignment(aligned_ptr, 8).is_ok());
assert!(MemoryValidator::validate_buffer_access(10, 0, 5).is_ok());
assert!(MemoryValidator::validate_buffer_access(10, 5, 5).is_ok());
assert!(MemoryValidator::validate_buffer_access(10, 10, 1).is_err()); assert!(MemoryValidator::validate_buffer_access(10, 8, 5).is_err()); }
#[test]
fn test_defer_macro() {
use std::sync::Arc;
let cleanup_called = Arc::new(Mutex::new(false));
let cleanup_called_clone = cleanup_called.clone();
{
defer!({
*cleanup_called_clone
.lock()
.expect("operation should succeed") = true;
});
assert!(!*cleanup_called.lock().expect("operation should succeed"));
}
assert!(*cleanup_called.lock().expect("operation should succeed"));
}
}