use std::sync::Mutex;
use std::time::Instant;
#[derive(Debug, Clone, Copy)]
struct Snapshot {
available_bytes: Option<u64>,
total_bytes: Option<u64>,
captured_at: Instant,
}
const CACHE_TTL_MILLIS: u128 = 100;
static SNAPSHOT: Mutex<Option<Snapshot>> = Mutex::new(None);
pub fn available_bytes() -> Option<u64> {
snapshot().available_bytes
}
pub fn total_bytes() -> Option<u64> {
snapshot().total_bytes
}
fn snapshot() -> Snapshot {
let mut guard = SNAPSHOT.lock().unwrap_or_else(|e| e.into_inner());
if let Some(s) = guard.as_ref()
&& s.captured_at.elapsed().as_millis() < CACHE_TTL_MILLIS
{
return *s;
}
let fresh = capture();
*guard = Some(fresh);
fresh
}
fn capture() -> Snapshot {
let (available, total) = capture_platform();
Snapshot {
available_bytes: available,
total_bytes: total,
captured_at: Instant::now(),
}
}
#[cfg(target_os = "linux")]
fn capture_platform() -> (Option<u64>, Option<u64>) {
let contents = match std::fs::read_to_string("/proc/meminfo") {
Ok(s) => s,
Err(_) => return (None, None),
};
let mut available = None;
let mut total = None;
let mut mem_free = None;
let mut buffers = None;
let mut cached = None;
for line in contents.lines() {
if let Some((field, rest)) = line.split_once(':') {
let kb = rest.trim().strip_suffix("kB").unwrap_or(rest.trim());
let kb: u64 = match kb.trim().parse() {
Ok(v) => v,
Err(_) => continue,
};
let bytes = kb.saturating_mul(1024);
match field {
"MemAvailable" => available = Some(bytes),
"MemTotal" => total = Some(bytes),
"MemFree" => mem_free = Some(bytes),
"Buffers" => buffers = Some(bytes),
"Cached" => cached = Some(bytes),
_ => {}
}
}
}
let available = available.or_else(|| match (mem_free, buffers, cached) {
(Some(f), Some(b), Some(c)) => Some(f + b + c),
(Some(f), _, _) => Some(f),
_ => None,
});
(available, total)
}
#[cfg(target_os = "windows")]
fn capture_platform() -> (Option<u64>, Option<u64>) {
use windows_sys::Win32::System::SystemInformation::{GlobalMemoryStatusEx, MEMORYSTATUSEX};
let mut status: MEMORYSTATUSEX = unsafe { std::mem::zeroed() };
status.dwLength = std::mem::size_of::<MEMORYSTATUSEX>() as u32;
let ok = unsafe { GlobalMemoryStatusEx(&mut status) };
if ok == 0 {
return (None, None);
}
(Some(status.ullAvailPhys), Some(status.ullTotalPhys))
}
#[cfg(not(any(target_os = "linux", target_os = "windows")))]
fn capture_platform() -> (Option<u64>, Option<u64>) {
(None, None)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn snapshot_is_internally_consistent() {
let s = snapshot();
match (s.available_bytes, s.total_bytes) {
(Some(a), Some(t)) => {
assert!(
a <= t,
"available ({a}) must not exceed total ({t}) — \
OS query inconsistency",
);
assert!(t > 0, "total must be positive on a real system");
}
(None, None) => {
}
(Some(_), None) | (None, Some(_)) => {
panic!("inconsistent snapshot: one field measured, the other not");
}
}
}
#[test]
fn back_to_back_calls_hit_cache() {
let a = snapshot();
let b = snapshot();
assert_eq!(a.captured_at, b.captured_at);
}
}