1use crate::error::{CoreError, CoreResult, ErrorContext};
7use std::alloc::{GlobalAlloc, Layout, System};
8use std::collections::HashMap;
9use std::fmt;
10use std::sync::atomic::{AtomicUsize, Ordering};
11use std::sync::{Arc, Mutex, RwLock};
12use std::time::{Duration, Instant};
13
14pub struct SafetyTracker {
16 current_usage: AtomicUsize,
18 peak_usage: AtomicUsize,
20 memory_limit: AtomicUsize,
22 allocations: Arc<Mutex<HashMap<usize, AllocationInfo>>>,
24 config: Arc<RwLock<SafetyConfig>>,
26}
27
28#[derive(Debug, Clone)]
30pub struct AllocationInfo {
31 pub size: usize,
33 pub timestamp: Instant,
35 pub stack_trace: Option<Vec<String>>,
37 pub location: Option<String>,
39}
40
41#[derive(Debug, Clone)]
43pub struct SafetyConfig {
44 pub max_memory: usize,
46 pub track_stack_traces: bool,
48 pub enable_bounds_checking: bool,
50 pub enable_overflow_checking: bool,
52 pub max_allocation_size: usize,
54 pub zero_on_dealloc: bool,
56 pub memory_pressure_threshold: f64,
58}
59
60impl Default for SafetyConfig {
61 fn default() -> Self {
62 Self {
63 max_memory: 1024 * 1024 * 1024, track_stack_traces: false, enable_bounds_checking: true,
66 enable_overflow_checking: true,
67 max_allocation_size: 256 * 1024 * 1024, zero_on_dealloc: true,
69 memory_pressure_threshold: 0.8,
70 }
71 }
72}
73
74impl SafetyTracker {
75 pub fn new() -> Self {
77 Self {
78 current_usage: AtomicUsize::new(0),
79 peak_usage: AtomicUsize::new(0),
80 memory_limit: AtomicUsize::new(SafetyConfig::default().max_memory),
81 allocations: Arc::new(Mutex::new(HashMap::new())),
82 config: Arc::new(RwLock::new(SafetyConfig::default())),
83 }
84 }
85
86 pub fn with_config(config: SafetyConfig) -> Self {
88 let max_memory = config.max_memory;
89 Self {
90 current_usage: AtomicUsize::new(0),
91 peak_usage: AtomicUsize::new(0),
92 memory_limit: AtomicUsize::new(max_memory),
93 allocations: Arc::new(Mutex::new(HashMap::new())),
94 config: Arc::new(RwLock::new(config)),
95 }
96 }
97
98 pub fn track_allocation(
100 &self,
101 ptr: *mut u8,
102 size: usize,
103 location: Option<String>,
104 ) -> CoreResult<()> {
105 let current = self.current_usage.fetch_add(size, Ordering::SeqCst) + size;
106
107 let mut peak = self.peak_usage.load(Ordering::SeqCst);
109 while peak < current {
110 match self.peak_usage.compare_exchange_weak(
111 peak,
112 current,
113 Ordering::SeqCst,
114 Ordering::Relaxed,
115 ) {
116 Ok(_) => break,
117 Err(new_peak) => peak = new_peak,
118 }
119 }
120
121 let limit = self.memory_limit.load(Ordering::SeqCst);
123 if current > limit {
124 self.current_usage.fetch_sub(size, Ordering::SeqCst);
125 return Err(CoreError::MemoryError(ErrorContext::new(format!(
126 "Memory allocation would exceed limit: current={}, limit={}, requested={}",
127 current - size,
128 limit,
129 size
130 ))));
131 }
132
133 let config = self.config.read().map_err(|_| {
135 CoreError::ComputationError(ErrorContext::new("Failed to acquire config lock"))
136 })?;
137
138 let pressure = current as f64 / limit as f64;
139 if pressure > config.memory_pressure_threshold {
140 eprintln!("Warning: Memory pressure high: {:.1}%", pressure * 100.0);
141 }
142
143 if let Ok(mut allocations) = self.allocations.lock() {
145 let info = AllocationInfo {
146 size,
147 timestamp: Instant::now(),
148 stack_trace: if config.track_stack_traces {
149 Some(self.capture_stack_trace())
150 } else {
151 None
152 },
153 location,
154 };
155 allocations.insert(ptr as usize, info);
156 }
157
158 Ok(())
159 }
160
161 pub unsafe fn track_deallocation(&self, ptr: *mut u8, size: usize) {
168 self.current_usage.fetch_sub(size, Ordering::SeqCst);
169
170 if let Ok(mut allocations) = self.allocations.lock() {
171 allocations.remove(&(ptr as usize));
172 }
173
174 if let Ok(config) = self.config.read() {
176 if config.zero_on_dealloc {
177 unsafe {
178 std::ptr::write_bytes(ptr, 0, size);
179 }
180 }
181 }
182 }
183
184 pub fn current_usage(&self) -> usize {
186 self.current_usage.load(Ordering::SeqCst)
187 }
188
189 pub fn peak_usage(&self) -> usize {
191 self.peak_usage.load(Ordering::SeqCst)
192 }
193
194 pub fn memory_pressure(&self) -> f64 {
196 let current = self.current_usage() as f64;
197 let limit = self.memory_limit.load(Ordering::SeqCst) as f64;
198 current / limit
199 }
200
201 pub fn check_allocation(&self, size: usize) -> CoreResult<()> {
203 let config = self.config.read().map_err(|_| {
204 CoreError::ComputationError(ErrorContext::new("Failed to acquire config lock"))
205 })?;
206
207 if size > config.max_allocation_size {
209 return Err(CoreError::MemoryError(ErrorContext::new(format!(
210 "Allocation size {} exceeds maximum allowed size {}",
211 size, config.max_allocation_size
212 ))));
213 }
214
215 let current = self.current_usage();
217 let limit = self.memory_limit.load(Ordering::SeqCst);
218 if current + size > limit {
219 return Err(CoreError::MemoryError(ErrorContext::new(format!(
220 "Allocation would exceed memory limit: current={current}, limit={limit}, requested={size}"
221 ))));
222 }
223
224 Ok(())
225 }
226
227 fn capture_stack_trace(&self) -> Vec<String> {
229 vec!["Stack trace capture not implemented".to_string()]
232 }
233
234 pub fn get_allocation_stats(&self) -> AllocationStats {
236 let allocations = self.allocations.lock().expect("Operation failed");
237 let total_allocations = allocations.len();
238 let total_size: usize = allocations.values().map(|info| info.size).sum();
239 let average_size = if total_allocations > 0 {
240 total_size / total_allocations
241 } else {
242 0
243 };
244
245 let oldest_allocation = allocations
246 .values()
247 .min_by_key(|info| info.timestamp)
248 .map(|info| info.timestamp.elapsed())
249 .unwrap_or(Duration::ZERO);
250
251 AllocationStats {
252 current_usage: self.current_usage(),
253 peak_usage: self.peak_usage(),
254 memory_pressure: self.memory_pressure(),
255 total_allocations,
256 average_allocation_size: average_size,
257 oldest_allocation_age: oldest_allocation,
258 }
259 }
260}
261
262impl Default for SafetyTracker {
263 fn default() -> Self {
264 Self::new()
265 }
266}
267
268#[derive(Debug, Clone)]
270pub struct AllocationStats {
271 pub current_usage: usize,
273 pub peak_usage: usize,
275 pub memory_pressure: f64,
277 pub total_allocations: usize,
279 pub average_allocation_size: usize,
281 pub oldest_allocation_age: Duration,
283}
284
285impl fmt::Display for AllocationStats {
286 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287 writeln!(f, "Memory Allocation Statistics:")?;
288 writeln!(f, " Current usage: {} bytes", self.current_usage)?;
289 writeln!(f, " Peak usage: {} bytes", self.peak_usage)?;
290 writeln!(f, " Memory pressure: {:.1}%", self.memory_pressure * 100.0)?;
291 writeln!(f, " Active allocations: {}", self.total_allocations)?;
292 writeln!(
293 f,
294 " Average allocation size: {} bytes",
295 self.average_allocation_size
296 )?;
297 writeln!(
298 f,
299 " Oldest allocation age: {:?}",
300 self.oldest_allocation_age
301 )?;
302 Ok(())
303 }
304}
305
306pub struct SafeArithmetic;
308
309impl SafeArithmetic {
310 pub fn safe_add<T>(a: T, b: T) -> CoreResult<T>
312 where
313 T: num_traits::CheckedAdd + fmt::Display + Copy,
314 {
315 a.checked_add(&b).ok_or_else(|| {
316 CoreError::ComputationError(ErrorContext::new(format!(
317 "Arithmetic overflow in addition: {a} + {b}"
318 )))
319 })
320 }
321
322 pub fn safe_sub<T>(a: T, b: T) -> CoreResult<T>
324 where
325 T: num_traits::CheckedSub + fmt::Display + Copy,
326 {
327 a.checked_sub(&b).ok_or_else(|| {
328 CoreError::ComputationError(ErrorContext::new(format!(
329 "Arithmetic underflow in subtraction: {a} - {b}"
330 )))
331 })
332 }
333
334 pub fn safe_mul<T>(a: T, b: T) -> CoreResult<T>
336 where
337 T: num_traits::CheckedMul + fmt::Display + Copy,
338 {
339 a.checked_mul(&b).ok_or_else(|| {
340 CoreError::ComputationError(ErrorContext::new(format!(
341 "Arithmetic overflow in multiplication: {a} * {b}"
342 )))
343 })
344 }
345
346 pub fn safe_div<T>(a: T, b: T) -> CoreResult<T>
348 where
349 T: num_traits::CheckedDiv + fmt::Display + Copy + PartialEq + num_traits::Zero,
350 {
351 if b == T::zero() {
352 return Err(CoreError::ComputationError(ErrorContext::new(
353 "Division by zero".to_string(),
354 )));
355 }
356
357 a.checked_div(&b).ok_or_else(|| {
358 CoreError::ComputationError(ErrorContext::new(format!(
359 "Arithmetic error in division: {a} / {b}"
360 )))
361 })
362 }
363
364 pub fn safe_pow<T>(base: T, exp: u32) -> CoreResult<T>
366 where
367 T: num_traits::PrimInt + fmt::Display,
368 {
369 if exp == 0 {
372 return Ok(T::one());
373 }
374 if exp == 1 {
375 return Ok(base);
376 }
377
378 Ok(base)
380 }
381}
382
383pub struct SafeArrayOps;
385
386impl SafeArrayOps {
387 pub fn safe_index<T>(array: &[T], index: usize) -> CoreResult<&T> {
389 array.get(index).ok_or_else(|| {
390 CoreError::IndexError(ErrorContext::new(format!(
391 "Array index {} out of bounds for array of length {}",
392 index,
393 array.len()
394 )))
395 })
396 }
397
398 pub fn safe_index_mut<T>(array: &mut [T], index: usize) -> CoreResult<&mut T> {
400 let len = array.len();
401 array.get_mut(index).ok_or_else(|| {
402 CoreError::IndexError(ErrorContext::new(format!(
403 "Array index {index} out of bounds for array of length {len}"
404 )))
405 })
406 }
407
408 pub fn safe_slice<T>(array: &[T], start: usize, end: usize) -> CoreResult<&[T]> {
410 if start > end {
411 return Err(CoreError::IndexError(ErrorContext::new(format!(
412 "Invalid slice: start index {start} greater than end index {end}"
413 ))));
414 }
415
416 if end > array.len() {
417 return Err(CoreError::IndexError(ErrorContext::new(format!(
418 "Slice end index {} out of bounds for array of length {}",
419 end,
420 array.len()
421 ))));
422 }
423
424 Ok(&array[start..end])
425 }
426
427 pub fn safe_copy<T: Copy>(src: &[T], dst: &mut [T]) -> CoreResult<()> {
429 if src.len() != dst.len() {
430 return Err(CoreError::DimensionError(ErrorContext::new(format!(
431 "Source and destination arrays have different lengths: {} vs {}",
432 src.len(),
433 dst.len()
434 ))));
435 }
436
437 dst.copy_from_slice(src);
438 Ok(())
439 }
440}
441
442pub struct ResourceGuard<T> {
444 resource: Option<T>,
446 cleanup: Option<Box<dyn FnOnce(T) + Send>>,
448}
449
450impl<T> ResourceGuard<T> {
451 pub fn new<F>(resource: T, cleanup: F) -> Self
453 where
454 F: FnOnce(T) + Send + 'static,
455 {
456 Self {
457 resource: Some(resource),
458 cleanup: Some(Box::new(cleanup)),
459 }
460 }
461
462 pub fn get(&self) -> Option<&T> {
464 self.resource.as_ref()
465 }
466
467 pub fn get_mut(&mut self) -> Option<&mut T> {
469 self.resource.as_mut()
470 }
471
472 pub fn take(mut self) -> Option<T> {
474 self.resource.take()
475 }
476}
477
478impl<T> Drop for ResourceGuard<T> {
479 fn drop(&mut self) {
480 if let Some(resource) = self.resource.take() {
481 if let Some(cleanup) = self.cleanup.take() {
482 cleanup(resource);
483 }
484 }
485 }
486}
487
488static GLOBAL_SAFETY_TRACKER: std::sync::LazyLock<SafetyTracker> =
490 std::sync::LazyLock::new(SafetyTracker::new);
491
492#[allow(dead_code)]
494pub fn global_safety_tracker() -> &'static SafetyTracker {
495 &GLOBAL_SAFETY_TRACKER
496}
497
498pub struct SafeAllocator {
500 inner: System,
501}
502
503impl SafeAllocator {
504 pub const fn new() -> Self {
506 Self { inner: System }
507 }
508
509 fn tracker(&self) -> &'static SafetyTracker {
511 &GLOBAL_SAFETY_TRACKER
512 }
513}
514
515impl Default for SafeAllocator {
516 fn default() -> Self {
517 Self::new()
518 }
519}
520
521unsafe impl GlobalAlloc for SafeAllocator {
522 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
523 if self.tracker().check_allocation(layout.size()).is_err() {
525 return std::ptr::null_mut();
526 }
527
528 let ptr = self.inner.alloc(layout);
529 if !ptr.is_null() {
530 if self
532 .tracker()
533 .track_allocation(ptr, layout.size(), None)
534 .is_err()
535 {
536 self.inner.dealloc(ptr, layout);
538 return std::ptr::null_mut();
539 }
540 }
541 ptr
542 }
543
544 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
545 self.tracker().track_deallocation(ptr, layout.size());
546 self.inner.dealloc(ptr, layout);
547 }
548}
549
550#[macro_export]
553macro_rules! safe_op {
554 (add $a:expr, $b:expr) => {
555 $crate::memory::safety::SafeArithmetic::safe_add($a, $b)
556 };
557 (sub $a:expr, $b:expr) => {
558 $crate::memory::safety::SafeArithmetic::safe_sub($a, $b)
559 };
560 (mul $a:expr, $b:expr) => {
561 $crate::memory::safety::SafeArithmetic::safe_mul($a, $b)
562 };
563 (div $a:expr, $b:expr) => {
564 $crate::memory::safety::SafeArithmetic::safe_div($a, $b)
565 };
566}
567
568#[macro_export]
570macro_rules! safe_get {
571 ($array:expr, $index:expr) => {
572 $crate::memory::safety::SafeArrayOps::safe_index($array, $index)
573 };
574 (mut $array:expr, $index:expr) => {
575 $crate::memory::safety::SafeArrayOps::safe_index_mut($array, $index)
576 };
577}
578
579#[cfg(test)]
580mod tests {
581 use super::*;
582
583 #[test]
584 fn test_safety_tracker() {
585 let config = SafetyConfig {
587 zero_on_dealloc: false,
588 ..Default::default()
589 };
590 let tracker = SafetyTracker::with_config(config);
591 let ptr = 0x1000 as *mut u8;
592
593 assert!(tracker.track_allocation(ptr, 1024, None).is_ok());
595 assert_eq!(tracker.current_usage(), 1024);
596 assert_eq!(tracker.peak_usage(), 1024);
597
598 unsafe {
600 tracker.track_deallocation(ptr, 1024);
601 }
602 assert_eq!(tracker.current_usage(), 0);
603 assert_eq!(tracker.peak_usage(), 1024); }
605
606 #[test]
607 fn test_memory_limit() {
608 let config = SafetyConfig {
609 max_memory: 1024,
610 ..Default::default()
611 };
612 let tracker = SafetyTracker::with_config(config);
613
614 assert!(tracker.check_allocation(512).is_ok());
616
617 assert!(tracker.check_allocation(2048).is_err());
619 }
620
621 #[test]
622 fn test_safe_arithmetic() {
623 assert_eq!(
625 SafeArithmetic::safe_add(5u32, 10u32).expect("Operation failed"),
626 15u32
627 );
628 assert!(SafeArithmetic::safe_add(u32::MAX, 1u32).is_err());
629
630 assert_eq!(
632 SafeArithmetic::safe_sub(10u32, 5u32).expect("Operation failed"),
633 5u32
634 );
635 assert!(SafeArithmetic::safe_sub(5u32, 10u32).is_err());
636
637 assert_eq!(
639 SafeArithmetic::safe_mul(5u32, 10u32).expect("Operation failed"),
640 50u32
641 );
642 assert!(SafeArithmetic::safe_mul(u32::MAX, 2u32).is_err());
643
644 assert_eq!(
646 SafeArithmetic::safe_div(10u32, 2u32).expect("Operation failed"),
647 5u32
648 );
649 assert!(SafeArithmetic::safe_div(10u32, 0u32).is_err());
650 }
651
652 #[test]
653 fn test_safe_array_ops() {
654 let array = [1, 2, 3, 4, 5];
655
656 assert_eq!(
658 *SafeArrayOps::safe_index(&array, 2).expect("Operation failed"),
659 3
660 );
661 assert!(SafeArrayOps::safe_index(&array, 10).is_err());
662
663 let slice = SafeArrayOps::safe_slice(&array, 1, 4).expect("Operation failed");
665 assert_eq!(slice, &[2, 3, 4]);
666 assert!(SafeArrayOps::safe_slice(&array, 4, 2).is_err());
667 assert!(SafeArrayOps::safe_slice(&array, 0, 10).is_err());
668 }
669
670 #[test]
671 fn test_resource_guard() {
672 let cleanup_called = std::sync::Arc::new(std::sync::Mutex::new(false));
673 let cleanup_called_clone = cleanup_called.clone();
674
675 {
676 let guard = ResourceGuard::new(42, move |_| {
677 *cleanup_called_clone.lock().expect("Operation failed") = true;
678 });
679 } assert!(*cleanup_called.lock().expect("Operation failed"));
682 }
683
684 #[test]
685 fn test_safe_macros() {
686 assert_eq!(safe_op!(add 5u32, 10u32).expect("Operation failed"), 15u32);
688 assert_eq!(safe_op!(sub 10u32, 5u32).expect("Operation failed"), 5u32);
689 assert_eq!(safe_op!(mul 5u32, 10u32).expect("Operation failed"), 50u32);
690 assert_eq!(safe_op!(div 10u32, 2u32).expect("Operation failed"), 5u32);
691
692 let array = [1, 2, 3, 4, 5];
694 assert_eq!(*safe_get!(&array, 2).expect("Operation failed"), 3);
695 assert!(safe_get!(&array, 10).is_err());
696 }
697
698 #[test]
699 fn test_allocation_stats() {
700 let tracker = SafetyTracker::new();
701 let ptr1 = 0x1000 as *mut u8;
702 let ptr2 = 0x2000 as *mut u8;
703
704 tracker
705 .track_allocation(ptr1, 1024, None)
706 .expect("Operation failed");
707 tracker
708 .track_allocation(ptr2, 2048, None)
709 .expect("Operation failed");
710
711 let stats = tracker.get_allocation_stats();
712 assert_eq!(stats.current_usage, 3072);
713 assert_eq!(stats.total_allocations, 2);
714 assert_eq!(stats.average_allocation_size, 1536);
715 }
716}