From 12c43fd4b50c32f57836e9936a8d69b3b44bb1e4 Mon Sep 17 00:00:00 2001 From: mattsu Date: Sat, 18 Oct 2025 21:00:43 +0900 Subject: [PATCH] refactor(uucore): integrate procfs for Linux memory parsing --- Cargo.lock | 25 ++++++++ src/uucore/Cargo.toml | 3 + .../src/lib/features/parser/parse_size.rs | 60 +++++++++++++------ 3 files changed, 69 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7592b2cb3..694da9dcc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2331,6 +2331,30 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "procfs" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25485360a54d6861439d60facef26de713b1e126bf015ec8f98239467a2b82f7" +dependencies = [ + "bitflags 2.9.1", + "chrono", + "flate2", + "procfs-core", + "rustix", +] + +[[package]] +name = "procfs-core" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6401bf7b6af22f78b563665d15a22e9aef27775b79b149a66ca022468a4e405" +dependencies = [ + "bitflags 2.9.1", + "chrono", + "hex", +] + [[package]] name = "quote" version = "1.0.41" @@ -4329,6 +4353,7 @@ dependencies = [ "num-traits", "number_prefix", "os_display", + "procfs", "selinux", "sha1", "sha2", diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index c18e6a1d4..43ed7fa68 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -101,6 +101,9 @@ xattr = { workspace = true, optional = true } [dev-dependencies] tempfile = { workspace = true } +[target.'cfg(target_os = "linux")'.dependencies] +procfs = "0.18" + [target.'cfg(target_os = "windows")'.dependencies] winapi-util = { workspace = true, optional = true } windows-sys = { workspace = true, optional = true, default-features = false, features = [ diff --git a/src/uucore/src/lib/features/parser/parse_size.rs b/src/uucore/src/lib/features/parser/parse_size.rs index da67a7602..60626b7d2 100644 --- a/src/uucore/src/lib/features/parser/parse_size.rs +++ b/src/uucore/src/lib/features/parser/parse_size.rs @@ -8,16 +8,17 @@ use std::error::Error; use std::fmt; -#[cfg(target_os = "linux")] -use std::io::BufRead; use std::num::{IntErrorKind, ParseIntError}; use crate::display::Quotable; +#[cfg(target_os = "linux")] +use procfs::{Current, Meminfo}; /// Error arising from trying to compute system memory. enum SystemError { IOError, ParseError, + #[cfg(not(target_os = "linux"))] NotFound, } @@ -43,25 +44,37 @@ impl From for SystemError { /// entry in the file. #[cfg(target_os = "linux")] fn total_physical_memory() -> Result { - // On Linux, the `/proc/meminfo` file has a table with information - // about memory usage. For example, - // - // MemTotal: 7811500 kB - // MemFree: 1487876 kB - // MemAvailable: 3857232 kB - // ... - // - // We just need to extract the number of `MemTotal` - let table = std::fs::read("/proc/meminfo")?; - for line in table.lines() { - let line = line?; - if line.starts_with("MemTotal:") && line.ends_with("kB") { - let num_kilobytes: u128 = line[9..line.len() - 2].trim().parse()?; - let num_bytes = 1024 * num_kilobytes; - return Ok(num_bytes); + let info = Meminfo::current().map_err(|_| SystemError::IOError)?; + Ok((info.mem_total as u128).saturating_mul(1024)) +} + +/// Return the number of bytes of memory that appear to be currently available. +#[cfg(target_os = "linux")] +pub fn available_memory_bytes() -> Option { + let info = Meminfo::current().ok()?; + + if let Some(available_kib) = info.mem_available { + let available_bytes = (available_kib as u128).saturating_mul(1024); + if available_bytes > 0 { + return Some(available_bytes); } } - Err(SystemError::NotFound) + + let fallback_kib = (info.mem_free as u128) + .saturating_add(info.buffers as u128) + .saturating_add(info.cached as u128); + + if fallback_kib > 0 { + Some(fallback_kib.saturating_mul(1024)) + } else { + total_physical_memory().ok() + } +} + +/// Return `None` when the platform does not expose Linux-like `/proc/meminfo`. +#[cfg(not(target_os = "linux"))] +pub fn available_memory_bytes() -> Option { + None } /// Get the total number of bytes of physical memory. @@ -361,6 +374,15 @@ pub fn parse_size_u64(size: &str) -> Result { Parser::default().parse_u64(size) } +/// Same as `parse_size_u64()`, except 0 fails to parse +pub fn parse_size_non_zero_u64(size: &str) -> Result { + let v = Parser::default().parse_u64(size)?; + if v == 0 { + return Err(ParseSizeError::ParseFailure("0".to_string())); + } + Ok(v) +} + /// Same as `parse_size_u64()` - deprecated #[deprecated = "Please use parse_size_u64(size: &str) -> Result OR parse_size_u128(size: &str) -> Result instead."] pub fn parse_size(size: &str) -> Result {