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 = total_size.checked_div(total_allocations).unwrap_or(0);
240
241 let oldest_allocation = allocations
242 .values()
243 .min_by_key(|info| info.timestamp)
244 .map(|info| info.timestamp.elapsed())
245 .unwrap_or(Duration::ZERO);
246
247 AllocationStats {
248 current_usage: self.current_usage(),
249 peak_usage: self.peak_usage(),
250 memory_pressure: self.memory_pressure(),
251 total_allocations,
252 average_allocation_size: average_size,
253 oldest_allocation_age: oldest_allocation,
254 }
255 }
256}
257
258impl Default for SafetyTracker {
259 fn default() -> Self {
260 Self::new()
261 }
262}
263
264#[derive(Debug, Clone)]
266pub struct AllocationStats {
267 pub current_usage: usize,
269 pub peak_usage: usize,
271 pub memory_pressure: f64,
273 pub total_allocations: usize,
275 pub average_allocation_size: usize,
277 pub oldest_allocation_age: Duration,
279}
280
281impl fmt::Display for AllocationStats {
282 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283 writeln!(f, "Memory Allocation Statistics:")?;
284 writeln!(f, " Current usage: {} bytes", self.current_usage)?;
285 writeln!(f, " Peak usage: {} bytes", self.peak_usage)?;
286 writeln!(f, " Memory pressure: {:.1}%", self.memory_pressure * 100.0)?;
287 writeln!(f, " Active allocations: {}", self.total_allocations)?;
288 writeln!(
289 f,
290 " Average allocation size: {} bytes",
291 self.average_allocation_size
292 )?;
293 writeln!(
294 f,
295 " Oldest allocation age: {:?}",
296 self.oldest_allocation_age
297 )?;
298 Ok(())
299 }
300}
301
302pub struct SafeArithmetic;
304
305impl SafeArithmetic {
306 pub fn safe_add<T>(a: T, b: T) -> CoreResult<T>
308 where
309 T: num_traits::CheckedAdd + fmt::Display + Copy,
310 {
311 a.checked_add(&b).ok_or_else(|| {
312 CoreError::ComputationError(ErrorContext::new(format!(
313 "Arithmetic overflow in addition: {a} + {b}"
314 )))
315 })
316 }
317
318 pub fn safe_sub<T>(a: T, b: T) -> CoreResult<T>
320 where
321 T: num_traits::CheckedSub + fmt::Display + Copy,
322 {
323 a.checked_sub(&b).ok_or_else(|| {
324 CoreError::ComputationError(ErrorContext::new(format!(
325 "Arithmetic underflow in subtraction: {a} - {b}"
326 )))
327 })
328 }
329
330 pub fn safe_mul<T>(a: T, b: T) -> CoreResult<T>
332 where
333 T: num_traits::CheckedMul + fmt::Display + Copy,
334 {
335 a.checked_mul(&b).ok_or_else(|| {
336 CoreError::ComputationError(ErrorContext::new(format!(
337 "Arithmetic overflow in multiplication: {a} * {b}"
338 )))
339 })
340 }
341
342 pub fn safe_div<T>(a: T, b: T) -> CoreResult<T>
344 where
345 T: num_traits::CheckedDiv + fmt::Display + Copy + PartialEq + num_traits::Zero,
346 {
347 if b == T::zero() {
348 return Err(CoreError::ComputationError(ErrorContext::new(
349 "Division by zero".to_string(),
350 )));
351 }
352
353 a.checked_div(&b).ok_or_else(|| {
354 CoreError::ComputationError(ErrorContext::new(format!(
355 "Arithmetic error in division: {a} / {b}"
356 )))
357 })
358 }
359
360 pub fn safe_pow<T>(base: T, exp: u32) -> CoreResult<T>
362 where
363 T: num_traits::PrimInt + fmt::Display,
364 {
365 if exp == 0 {
368 return Ok(T::one());
369 }
370 if exp == 1 {
371 return Ok(base);
372 }
373
374 Ok(base)
376 }
377}
378
379pub struct SafeArrayOps;
381
382impl SafeArrayOps {
383 pub fn safe_index<T>(array: &[T], index: usize) -> CoreResult<&T> {
385 array.get(index).ok_or_else(|| {
386 CoreError::IndexError(ErrorContext::new(format!(
387 "Array index {} out of bounds for array of length {}",
388 index,
389 array.len()
390 )))
391 })
392 }
393
394 pub fn safe_index_mut<T>(array: &mut [T], index: usize) -> CoreResult<&mut T> {
396 let len = array.len();
397 array.get_mut(index).ok_or_else(|| {
398 CoreError::IndexError(ErrorContext::new(format!(
399 "Array index {index} out of bounds for array of length {len}"
400 )))
401 })
402 }
403
404 pub fn safe_slice<T>(array: &[T], start: usize, end: usize) -> CoreResult<&[T]> {
406 if start > end {
407 return Err(CoreError::IndexError(ErrorContext::new(format!(
408 "Invalid slice: start index {start} greater than end index {end}"
409 ))));
410 }
411
412 if end > array.len() {
413 return Err(CoreError::IndexError(ErrorContext::new(format!(
414 "Slice end index {} out of bounds for array of length {}",
415 end,
416 array.len()
417 ))));
418 }
419
420 Ok(&array[start..end])
421 }
422
423 pub fn safe_copy<T: Copy>(src: &[T], dst: &mut [T]) -> CoreResult<()> {
425 if src.len() != dst.len() {
426 return Err(CoreError::DimensionError(ErrorContext::new(format!(
427 "Source and destination arrays have different lengths: {} vs {}",
428 src.len(),
429 dst.len()
430 ))));
431 }
432
433 dst.copy_from_slice(src);
434 Ok(())
435 }
436}
437
438pub struct ResourceGuard<T> {
440 resource: Option<T>,
442 cleanup: Option<Box<dyn FnOnce(T) + Send>>,
444}
445
446impl<T> ResourceGuard<T> {
447 pub fn new<F>(resource: T, cleanup: F) -> Self
449 where
450 F: FnOnce(T) + Send + 'static,
451 {
452 Self {
453 resource: Some(resource),
454 cleanup: Some(Box::new(cleanup)),
455 }
456 }
457
458 pub fn get(&self) -> Option<&T> {
460 self.resource.as_ref()
461 }
462
463 pub fn get_mut(&mut self) -> Option<&mut T> {
465 self.resource.as_mut()
466 }
467
468 pub fn take(mut self) -> Option<T> {
470 self.resource.take()
471 }
472}
473
474impl<T> Drop for ResourceGuard<T> {
475 fn drop(&mut self) {
476 if let Some(resource) = self.resource.take() {
477 if let Some(cleanup) = self.cleanup.take() {
478 cleanup(resource);
479 }
480 }
481 }
482}
483
484static GLOBAL_SAFETY_TRACKER: std::sync::LazyLock<SafetyTracker> =
486 std::sync::LazyLock::new(SafetyTracker::new);
487
488#[allow(dead_code)]
490pub fn global_safety_tracker() -> &'static SafetyTracker {
491 &GLOBAL_SAFETY_TRACKER
492}
493
494pub struct SafeAllocator {
496 inner: System,
497}
498
499impl SafeAllocator {
500 pub const fn new() -> Self {
502 Self { inner: System }
503 }
504
505 fn tracker(&self) -> &'static SafetyTracker {
507 &GLOBAL_SAFETY_TRACKER
508 }
509}
510
511impl Default for SafeAllocator {
512 fn default() -> Self {
513 Self::new()
514 }
515}
516
517unsafe impl GlobalAlloc for SafeAllocator {
518 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
519 if self.tracker().check_allocation(layout.size()).is_err() {
521 return std::ptr::null_mut();
522 }
523
524 let ptr = self.inner.alloc(layout);
525 if !ptr.is_null() {
526 if self
528 .tracker()
529 .track_allocation(ptr, layout.size(), None)
530 .is_err()
531 {
532 self.inner.dealloc(ptr, layout);
534 return std::ptr::null_mut();
535 }
536 }
537 ptr
538 }
539
540 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
541 self.tracker().track_deallocation(ptr, layout.size());
542 self.inner.dealloc(ptr, layout);
543 }
544}
545
546#[macro_export]
549macro_rules! safe_op {
550 (add $a:expr, $b:expr) => {
551 $crate::memory::safety::SafeArithmetic::safe_add($a, $b)
552 };
553 (sub $a:expr, $b:expr) => {
554 $crate::memory::safety::SafeArithmetic::safe_sub($a, $b)
555 };
556 (mul $a:expr, $b:expr) => {
557 $crate::memory::safety::SafeArithmetic::safe_mul($a, $b)
558 };
559 (div $a:expr, $b:expr) => {
560 $crate::memory::safety::SafeArithmetic::safe_div($a, $b)
561 };
562}
563
564#[macro_export]
566macro_rules! safe_get {
567 ($array:expr, $index:expr) => {
568 $crate::memory::safety::SafeArrayOps::safe_index($array, $index)
569 };
570 (mut $array:expr, $index:expr) => {
571 $crate::memory::safety::SafeArrayOps::safe_index_mut($array, $index)
572 };
573}
574
575#[cfg(test)]
576mod tests {
577 use super::*;
578
579 #[test]
580 fn test_safety_tracker() {
581 let config = SafetyConfig {
583 zero_on_dealloc: false,
584 ..Default::default()
585 };
586 let tracker = SafetyTracker::with_config(config);
587 let ptr = 0x1000 as *mut u8;
588
589 assert!(tracker.track_allocation(ptr, 1024, None).is_ok());
591 assert_eq!(tracker.current_usage(), 1024);
592 assert_eq!(tracker.peak_usage(), 1024);
593
594 unsafe {
596 tracker.track_deallocation(ptr, 1024);
597 }
598 assert_eq!(tracker.current_usage(), 0);
599 assert_eq!(tracker.peak_usage(), 1024); }
601
602 #[test]
603 fn test_memory_limit() {
604 let config = SafetyConfig {
605 max_memory: 1024,
606 ..Default::default()
607 };
608 let tracker = SafetyTracker::with_config(config);
609
610 assert!(tracker.check_allocation(512).is_ok());
612
613 assert!(tracker.check_allocation(2048).is_err());
615 }
616
617 #[test]
618 fn test_safe_arithmetic() {
619 assert_eq!(
621 SafeArithmetic::safe_add(5u32, 10u32).expect("Operation failed"),
622 15u32
623 );
624 assert!(SafeArithmetic::safe_add(u32::MAX, 1u32).is_err());
625
626 assert_eq!(
628 SafeArithmetic::safe_sub(10u32, 5u32).expect("Operation failed"),
629 5u32
630 );
631 assert!(SafeArithmetic::safe_sub(5u32, 10u32).is_err());
632
633 assert_eq!(
635 SafeArithmetic::safe_mul(5u32, 10u32).expect("Operation failed"),
636 50u32
637 );
638 assert!(SafeArithmetic::safe_mul(u32::MAX, 2u32).is_err());
639
640 assert_eq!(
642 SafeArithmetic::safe_div(10u32, 2u32).expect("Operation failed"),
643 5u32
644 );
645 assert!(SafeArithmetic::safe_div(10u32, 0u32).is_err());
646 }
647
648 #[test]
649 fn test_safe_array_ops() {
650 let array = [1, 2, 3, 4, 5];
651
652 assert_eq!(
654 *SafeArrayOps::safe_index(&array, 2).expect("Operation failed"),
655 3
656 );
657 assert!(SafeArrayOps::safe_index(&array, 10).is_err());
658
659 let slice = SafeArrayOps::safe_slice(&array, 1, 4).expect("Operation failed");
661 assert_eq!(slice, &[2, 3, 4]);
662 assert!(SafeArrayOps::safe_slice(&array, 4, 2).is_err());
663 assert!(SafeArrayOps::safe_slice(&array, 0, 10).is_err());
664 }
665
666 #[test]
667 fn test_resource_guard() {
668 let cleanup_called = std::sync::Arc::new(std::sync::Mutex::new(false));
669 let cleanup_called_clone = cleanup_called.clone();
670
671 {
672 let guard = ResourceGuard::new(42, move |_| {
673 *cleanup_called_clone.lock().expect("Operation failed") = true;
674 });
675 } assert!(*cleanup_called.lock().expect("Operation failed"));
678 }
679
680 #[test]
681 fn test_safe_macros() {
682 assert_eq!(safe_op!(add 5u32, 10u32).expect("Operation failed"), 15u32);
684 assert_eq!(safe_op!(sub 10u32, 5u32).expect("Operation failed"), 5u32);
685 assert_eq!(safe_op!(mul 5u32, 10u32).expect("Operation failed"), 50u32);
686 assert_eq!(safe_op!(div 10u32, 2u32).expect("Operation failed"), 5u32);
687
688 let array = [1, 2, 3, 4, 5];
690 assert_eq!(*safe_get!(&array, 2).expect("Operation failed"), 3);
691 assert!(safe_get!(&array, 10).is_err());
692 }
693
694 #[test]
695 fn test_allocation_stats() {
696 let tracker = SafetyTracker::new();
697 let ptr1 = 0x1000 as *mut u8;
698 let ptr2 = 0x2000 as *mut u8;
699
700 tracker
701 .track_allocation(ptr1, 1024, None)
702 .expect("Operation failed");
703 tracker
704 .track_allocation(ptr2, 2048, None)
705 .expect("Operation failed");
706
707 let stats = tracker.get_allocation_stats();
708 assert_eq!(stats.current_usage, 3072);
709 assert_eq!(stats.total_allocations, 2);
710 assert_eq!(stats.average_allocation_size, 1536);
711 }
712}