pub fn available_memory_bytes() -> Option<u64> {
#[cfg(any(target_os = "linux", target_os = "android"))]
{
let meminfo = std::fs::read_to_string("/proc/meminfo").ok()?;
parse_mem_available_kib(&meminfo).map(|kib| kib.saturating_mul(1024))
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
{
None
}
}
#[cfg(any(target_os = "linux", target_os = "android", test))]
fn parse_mem_available_kib(meminfo: &str) -> Option<u64> {
meminfo
.lines()
.find_map(|line| line.strip_prefix("MemAvailable:"))
.and_then(|rest| rest.split_whitespace().next())
.and_then(|kib| kib.parse::<u64>().ok())
}
pub fn fits_in_available_memory(required_bytes: u64, headroom_bytes: u64) -> Option<bool> {
let available = available_memory_bytes()?;
Some(required_bytes.saturating_add(headroom_bytes) <= available)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_mem_available_line() {
let sample = "MemTotal: 16000000 kB\n\
MemFree: 2000000 kB\n\
MemAvailable: 8388608 kB\n\
Buffers: 100000 kB\n";
assert_eq!(parse_mem_available_kib(sample), Some(8_388_608));
}
#[test]
fn missing_mem_available_is_none() {
assert_eq!(parse_mem_available_kib("MemTotal: 16000000 kB\n"), None);
assert_eq!(parse_mem_available_kib(""), None);
}
#[test]
fn fits_contract_holds_when_available_known() {
match available_memory_bytes() {
Some(avail) => {
assert!(avail > 0);
assert_eq!(fits_in_available_memory(0, 0), Some(true));
assert_eq!(
fits_in_available_memory(avail.saturating_add(1), 0),
Some(false)
);
}
None => {
assert_eq!(fits_in_available_memory(0, 0), None);
}
}
}
}