use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct MemoryStats {
pub peak_usage: usize,
pub current_usage: usize,
pub timestamp: Instant,
}
pub struct MemoryMonitor {
initial_usage: usize,
peak_usage: usize,
start_time: Instant,
}
impl MemoryMonitor {
pub fn new() -> Self {
Self {
initial_usage: get_current_memory_usage(),
peak_usage: get_current_memory_usage(),
start_time: Instant::now(),
}
}
pub fn current_stats(&self) -> MemoryStats {
let current = get_current_memory_usage();
MemoryStats {
peak_usage: self.peak_usage.max(current),
current_usage: current,
timestamp: Instant::now(),
}
}
pub fn record_reading(&mut self) {
let current = get_current_memory_usage();
self.peak_usage = self.peak_usage.max(current);
}
pub fn initial_usage(&self) -> usize {
self.initial_usage
}
pub fn peak_usage(&self) -> usize {
self.peak_usage
}
pub fn elapsed(&self) -> Duration {
self.start_time.elapsed()
}
pub fn increase(&self) -> isize {
let current = get_current_memory_usage();
(current as isize) - (self.initial_usage as isize)
}
}
pub fn get_current_memory_usage() -> usize {
#[cfg(unix)]
{
use std::fs;
if let Ok(status) = fs::read_to_string("/proc/self/status") {
for line in status.lines() {
if line.starts_with("VmRSS:") {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 2 {
if let Ok(kb) = parts[1].parse::<usize>() {
return kb * 1024; }
}
}
}
}
estimate_heap_size()
}
#[cfg(not(unix))]
{
estimate_heap_size()
}
}
fn estimate_heap_size() -> usize {
10 * 1024 * 1024 }
#[derive(Debug)]
pub struct MemoryConstraint {
pub max_usage: usize,
pub relative_to_input: f64,
pub warning_threshold: f64,
}
impl MemoryConstraint {
pub fn new(max_usage: usize, relative_to_input: f64, warning_threshold: f64) -> Self {
Self {
max_usage,
relative_to_input,
warning_threshold,
}
}
pub fn is_exceeded(&self, current_usage: usize, input_size: usize) -> bool {
let allowed = self
.max_usage
.max((input_size as f64 * self.relative_to_input) as usize);
current_usage > allowed
}
pub fn is_near_warning(&self, current_usage: usize, input_size: usize) -> bool {
let allowed = self
.max_usage
.max((input_size as f64 * self.relative_to_input) as usize);
current_usage > (allowed as f64 * self.warning_threshold) as usize
}
pub fn max_allowed(&self, input_size: usize) -> usize {
self.max_usage
.max((input_size as f64 * self.relative_to_input) as usize)
}
}
pub mod constraints {
use super::*;
pub const SMALL: MemoryConstraint = MemoryConstraint {
max_usage: 100 * 1024 * 1024, relative_to_input: 10.0, warning_threshold: 0.8, };
pub const MEDIUM: MemoryConstraint = MemoryConstraint {
max_usage: 500 * 1024 * 1024, relative_to_input: 5.0, warning_threshold: 0.8, };
pub const LARGE: MemoryConstraint = MemoryConstraint {
max_usage: 2 * 1024 * 1024 * 1024, relative_to_input: 3.0, warning_threshold: 0.9, };
}
pub fn format_bytes(bytes: usize) -> String {
const UNITS: &[(&str, u64)] = &[
("B", 1),
("KB", 1024),
("MB", 1_048_576),
("GB", 1_073_741_824),
];
for (i, &(unit, size)) in UNITS.iter().enumerate() {
if bytes < (size * 1024) as usize || i == UNITS.len() - 1 {
if i == 0 {
return format!("{} {}", bytes, unit);
} else {
return format!("{:.2} {}", bytes as f64 / size as f64, unit);
}
} else {
continue;
}
}
unreachable!()
}