1use scirs2_core::ndarray::{Array1, Array2};
77use std::collections::HashMap;
78use std::marker::PhantomData;
79use std::ptr::NonNull;
80use std::sync::{Arc, Mutex, RwLock};
81
82pub struct MemorySafety;
84
85impl MemorySafety {
86 pub fn document_safety(operation: &str) -> MemorySafetyGuarantee {
88 match operation {
89 "array_indexing" => MemorySafetyGuarantee {
90 operation: operation.to_string(),
91 guarantees: vec![
92 "Bounds checking prevents buffer overflows".to_string(),
93 "Panic on out-of-bounds access in debug mode".to_string(),
94 "Optional bounds checking in release mode for performance".to_string(),
95 ],
96 unsafe_blocks: vec![],
97 mitigation_strategies: vec![
98 "Use checked indexing methods when bounds are uncertain".to_string(),
99 "Validate input dimensions before processing".to_string(),
100 ],
101 },
102 "parallel_processing" => MemorySafetyGuarantee {
103 operation: operation.to_string(),
104 guarantees: vec![
105 "Send and Sync traits prevent data races".to_string(),
106 "Rayon provides work-stealing without data races".to_string(),
107 "Immutable borrows allow safe parallel reading".to_string(),
108 ],
109 unsafe_blocks: vec![],
110 mitigation_strategies: vec![
111 "Use Arc<T> for shared ownership across threads".to_string(),
112 "Use Mutex<T> or RwLock<T> for shared mutable access".to_string(),
113 ],
114 },
115 "gpu_operations" => MemorySafetyGuarantee {
116 operation: operation.to_string(),
117 guarantees: vec![
118 "CUDA memory is managed through RAII wrappers".to_string(),
119 "GPU pointers are opaque and cannot be dereferenced on CPU".to_string(),
120 "Automatic cleanup of GPU resources on drop".to_string(),
121 ],
122 unsafe_blocks: vec![
123 "CUDA FFI calls require unsafe blocks".to_string(),
124 "Memory transfers between CPU and GPU use unsafe operations".to_string(),
125 ],
126 mitigation_strategies: vec![
127 "Wrap all CUDA operations in safe abstractions".to_string(),
128 "Validate GPU memory allocation success".to_string(),
129 "Use typed GPU pointers to prevent type confusion".to_string(),
130 ],
131 },
132 _ => MemorySafetyGuarantee {
133 operation: operation.to_string(),
134 guarantees: vec!["General Rust memory safety guarantees apply".to_string()],
135 unsafe_blocks: vec![],
136 mitigation_strategies: vec![],
137 },
138 }
139 }
140
141 pub fn validate_unsafe_usage(code_block: &str) -> UnsafeValidationResult {
143 let mut issues = Vec::new();
144 let mut recommendations = Vec::new();
145
146 if code_block.contains("transmute") {
148 issues.push("transmute operations can break type safety".to_string());
149 recommendations.push("Consider using safe casting alternatives".to_string());
150 }
151
152 if code_block.contains("from_raw_parts") {
153 issues.push("Raw pointer operations require careful validation".to_string());
154 recommendations.push("Ensure pointer validity and proper alignment".to_string());
155 }
156
157 if code_block.contains("assume_init") {
158 issues.push("Uninitialized memory access detected".to_string());
159 recommendations
160 .push("Use MaybeUninit for safer uninitialized memory handling".to_string());
161 }
162
163 let safety_score = if issues.is_empty() {
164 100
165 } else {
166 std::cmp::max(0, 100 - (issues.len() * 20)) as u8
167 };
168
169 UnsafeValidationResult {
170 safety_score,
171 issues,
172 recommendations,
173 requires_review: safety_score < 80,
174 }
175 }
176}
177
178#[derive(Debug, Clone)]
180pub struct MemorySafetyGuarantee {
181 pub operation: String,
182 pub guarantees: Vec<String>,
183 pub unsafe_blocks: Vec<String>,
184 pub mitigation_strategies: Vec<String>,
185}
186
187#[derive(Debug, Clone)]
189pub struct UnsafeValidationResult {
190 pub safety_score: u8, pub issues: Vec<String>,
192 pub recommendations: Vec<String>,
193 pub requires_review: bool,
194}
195
196pub trait SafeArrayOps<T> {
198 fn safe_get(&self, index: &[usize]) -> Option<&T>;
200
201 fn safe_get_mut(&mut self, index: &[usize]) -> Option<&mut T>;
203
204 fn validate_dimensions(&self) -> Result<(), String>;
206
207 fn is_valid_index(&self, index: &[usize]) -> bool;
209}
210
211impl<T> SafeArrayOps<T> for Array2<T> {
212 fn safe_get(&self, index: &[usize]) -> Option<&T> {
213 if index.len() != 2 {
214 return None;
215 }
216 self.get((index[0], index[1]))
217 }
218
219 fn safe_get_mut(&mut self, index: &[usize]) -> Option<&mut T> {
220 if index.len() != 2 {
221 return None;
222 }
223 self.get_mut((index[0], index[1]))
224 }
225
226 fn validate_dimensions(&self) -> Result<(), String> {
227 if self.nrows() == 0 || self.ncols() == 0 {
228 Err("Array has zero-sized dimension".to_string())
229 } else if self.nrows() > isize::MAX as usize || self.ncols() > isize::MAX as usize {
230 Err("Array dimension exceeds maximum safe size".to_string())
231 } else {
232 Ok(())
233 }
234 }
235
236 fn is_valid_index(&self, index: &[usize]) -> bool {
237 index.len() == 2 && index[0] < self.nrows() && index[1] < self.ncols()
238 }
239}
240
241impl<T> SafeArrayOps<T> for Array1<T> {
242 fn safe_get(&self, index: &[usize]) -> Option<&T> {
243 if index.len() != 1 {
244 return None;
245 }
246 self.get(index[0])
247 }
248
249 fn safe_get_mut(&mut self, index: &[usize]) -> Option<&mut T> {
250 if index.len() != 1 {
251 return None;
252 }
253 self.get_mut(index[0])
254 }
255
256 fn validate_dimensions(&self) -> Result<(), String> {
257 if self.is_empty() {
258 Err("Array is empty".to_string())
259 } else if self.len() > isize::MAX as usize {
260 Err("Array length exceeds maximum safe size".to_string())
261 } else {
262 Ok(())
263 }
264 }
265
266 fn is_valid_index(&self, index: &[usize]) -> bool {
267 index.len() == 1 && index[0] < self.len()
268 }
269}
270
271pub struct SafeMemoryPool<T> {
273 pools: Arc<Mutex<HashMap<usize, Vec<Vec<T>>>>>,
274 allocated_count: Arc<Mutex<usize>>,
275 max_pool_size: usize,
276}
277
278impl<T> SafeMemoryPool<T> {
279 pub fn new() -> Self {
281 Self {
282 pools: Arc::new(Mutex::new(HashMap::new())),
283 allocated_count: Arc::new(Mutex::new(0)),
284 max_pool_size: 1000, }
286 }
287
288 pub fn with_limits(max_pool_size: usize) -> Self {
290 Self {
291 pools: Arc::new(Mutex::new(HashMap::new())),
292 allocated_count: Arc::new(Mutex::new(0)),
293 max_pool_size,
294 }
295 }
296
297 pub fn allocate(&self, capacity: usize) -> SafePooledBuffer<T> {
299 let buffer = {
300 let mut pools = self.pools.lock().unwrap_or_else(|e| e.into_inner());
301 if let Some(pool) = pools.get_mut(&capacity) {
302 if let Some(mut buffer) = pool.pop() {
303 buffer.clear();
304 buffer
305 } else {
306 Vec::with_capacity(capacity)
307 }
308 } else {
309 Vec::with_capacity(capacity)
310 }
311 };
312
313 {
314 let mut count = self
315 .allocated_count
316 .lock()
317 .unwrap_or_else(|e| e.into_inner());
318 *count += 1;
319 }
320
321 SafePooledBuffer {
322 buffer: Some(buffer),
323 capacity,
324 pool: self.pools.clone(),
325 allocated_count: self.allocated_count.clone(),
326 max_pool_size: self.max_pool_size,
327 }
328 }
329
330 pub fn stats(&self) -> MemoryPoolStats {
332 let allocated_count = *self
333 .allocated_count
334 .lock()
335 .unwrap_or_else(|e| e.into_inner());
336 let pools = self.pools.lock().unwrap_or_else(|e| e.into_inner());
337 let pooled_count: usize = pools.values().map(|v| v.len()).sum();
338
339 MemoryPoolStats {
340 allocated_count,
341 pooled_count,
342 pool_sizes: pools.iter().map(|(&k, v)| (k, v.len())).collect(),
343 }
344 }
345}
346
347impl<T> Default for SafeMemoryPool<T> {
348 fn default() -> Self {
349 Self::new()
350 }
351}
352
353#[derive(Debug, Clone)]
355pub struct MemoryPoolStats {
356 pub allocated_count: usize,
357 pub pooled_count: usize,
358 pub pool_sizes: Vec<(usize, usize)>, }
360
361pub struct SafePooledBuffer<T> {
363 buffer: Option<Vec<T>>,
364 capacity: usize,
365 pool: Arc<Mutex<HashMap<usize, Vec<Vec<T>>>>>,
366 allocated_count: Arc<Mutex<usize>>,
367 max_pool_size: usize,
368}
369
370impl<T> SafePooledBuffer<T> {
371 pub fn as_mut_vec(&mut self) -> &mut Vec<T> {
373 self.buffer.as_mut().expect("Buffer has been consumed")
374 }
375
376 pub fn as_ref_vec(&self) -> &Vec<T> {
378 self.buffer.as_ref().expect("Buffer has been consumed")
379 }
380
381 pub fn into_inner(mut self) -> Vec<T> {
383 self.buffer.take().expect("Buffer has been consumed")
384 }
385}
386
387impl<T> Drop for SafePooledBuffer<T> {
388 fn drop(&mut self) {
389 if let Some(buffer) = self.buffer.take() {
390 let mut pools = self.pool.lock().unwrap_or_else(|e| e.into_inner());
392 let pool = pools.entry(self.capacity).or_default();
393
394 if pool.len() < self.max_pool_size {
395 pool.push(buffer);
396 }
397 let mut count = self
401 .allocated_count
402 .lock()
403 .unwrap_or_else(|e| e.into_inner());
404 *count = count.saturating_sub(1);
405 }
406 }
407}
408
409impl<T> std::ops::Deref for SafePooledBuffer<T> {
410 type Target = Vec<T>;
411
412 fn deref(&self) -> &Self::Target {
413 self.as_ref_vec()
414 }
415}
416
417impl<T> std::ops::DerefMut for SafePooledBuffer<T> {
418 fn deref_mut(&mut self) -> &mut Self::Target {
419 self.as_mut_vec()
420 }
421}
422
423#[derive(Debug)]
425pub struct SafePtr<T> {
426 ptr: NonNull<T>,
427 _marker: PhantomData<T>,
428}
429
430impl<T> SafePtr<T> {
431 pub unsafe fn new(ptr: NonNull<T>) -> Self {
440 Self {
441 ptr,
442 _marker: PhantomData,
443 }
444 }
445
446 pub unsafe fn as_ptr(&self) -> *const T {
452 self.ptr.as_ptr()
453 }
454
455 pub unsafe fn as_mut_ptr(&self) -> *mut T {
461 self.ptr.as_ptr()
462 }
463}
464
465unsafe impl<T: Send> Send for SafePtr<T> {}
467unsafe impl<T: Sync> Sync for SafePtr<T> {}
468
469pub struct SafeSharedModel<T> {
471 inner: Arc<RwLock<T>>,
472 id: String,
473}
474
475impl<T> SafeSharedModel<T> {
476 pub fn new(model: T, id: String) -> Self {
478 Self {
479 inner: Arc::new(RwLock::new(model)),
480 id,
481 }
482 }
483
484 pub fn read(&self) -> std::sync::RwLockReadGuard<'_, T> {
486 self.inner
487 .read()
488 .unwrap_or_else(|e| panic!("RwLock poisoned for model {}: {}", self.id, e))
489 }
490
491 pub fn write(&self) -> std::sync::RwLockWriteGuard<'_, T> {
493 self.inner
494 .write()
495 .unwrap_or_else(|e| panic!("RwLock poisoned for model {}: {}", self.id, e))
496 }
497
498 pub fn try_read(&self) -> Option<std::sync::RwLockReadGuard<'_, T>> {
500 self.inner.try_read().ok()
501 }
502
503 pub fn try_write(&self) -> Option<std::sync::RwLockWriteGuard<'_, T>> {
505 self.inner.try_write().ok()
506 }
507
508 pub fn clone_ref(&self) -> Self {
510 Self {
511 inner: Arc::clone(&self.inner),
512 id: self.id.clone(),
513 }
514 }
515}
516
517impl<T: Clone> SafeSharedModel<T> {
518 pub fn clone_model(&self) -> T {
520 self.read().clone()
521 }
522}
523
524#[allow(non_snake_case)]
525#[cfg(test)]
526mod tests {
527 use super::*;
528 use scirs2_core::ndarray::Array2;
529
530 #[test]
531 fn test_memory_safety_documentation() {
532 let guarantee = MemorySafety::document_safety("array_indexing");
533 assert_eq!(guarantee.operation, "array_indexing");
534 assert!(!guarantee.guarantees.is_empty());
535 }
536
537 #[test]
538 fn test_unsafe_validation() {
539 let safe_code = "let x = vec![1, 2, 3]; let y = &x[0];";
540 let result = MemorySafety::validate_unsafe_usage(safe_code);
541 assert_eq!(result.safety_score, 100);
542 assert!(result.issues.is_empty());
543
544 let unsafe_code = "let x = transmute::<i32, f32>(42);";
545 let result = MemorySafety::validate_unsafe_usage(unsafe_code);
546 assert!(result.safety_score < 100);
547 assert!(!result.issues.is_empty());
548 }
549
550 #[test]
551 fn test_safe_array_operations() {
552 let array = Array2::<f64>::zeros((10, 10));
553
554 assert!(array.safe_get(&[0, 0]).is_some());
556 assert!(array.safe_get(&[10, 10]).is_none());
557 assert!(array.safe_get(&[5]).is_none()); assert!(array.validate_dimensions().is_ok());
561
562 assert!(array.is_valid_index(&[5, 5]));
564 assert!(!array.is_valid_index(&[10, 5]));
565 }
566
567 #[test]
568 fn test_memory_pool() {
569 let pool = SafeMemoryPool::<i32>::new();
570
571 let buffer = pool.allocate(100);
573 assert_eq!(buffer.capacity(), 100);
574
575 let stats = pool.stats();
576 assert_eq!(stats.allocated_count, 1);
577
578 drop(buffer);
580
581 let stats = pool.stats();
582 assert_eq!(stats.allocated_count, 0);
583 assert_eq!(stats.pooled_count, 1);
584 }
585
586 #[test]
587 fn test_shared_model() {
588 let model = vec![1, 2, 3, 4, 5];
589 let shared = SafeSharedModel::new(model, "test_model".to_string());
590
591 {
593 let reader = shared.read();
594 assert_eq!(reader.len(), 5);
595 }
596
597 {
599 let mut writer = shared.write();
600 writer.push(6);
601 assert_eq!(writer.len(), 6);
602 }
603
604 let shared2 = shared.clone_ref();
606 let reader = shared2.read();
607 assert_eq!(reader.len(), 6);
608 }
609
610 #[test]
611 fn test_pooled_buffer_deref() {
612 let pool = SafeMemoryPool::<i32>::new();
613 let mut buffer = pool.allocate(10);
614
615 buffer.push(42);
617 assert_eq!(buffer.len(), 1);
618 assert_eq!(buffer[0], 42);
619
620 let inner = buffer.into_inner();
622 assert_eq!(inner, vec![42]);
623 }
624}