diff --git a/Cargo.lock b/Cargo.lock index 07d2f4e..623f14a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,6 +71,12 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +[[package]] +name = "bytecount" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" + [[package]] name = "cfg-if" version = "1.0.0" @@ -195,6 +201,12 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "getrandom" version = "0.2.12" @@ -212,6 +224,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.3.4" @@ -235,6 +253,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "lazy_static" version = "1.4.0" @@ -307,6 +331,17 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "papergrid" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7419ad52a7de9b60d33e11085a0fe3df1fbd5926aa3f93d3dd53afbc9e86725" +dependencies = [ + "bytecount", + "fnv", + "unicode-width", +] + [[package]] name = "phf" version = "0.11.2" @@ -361,6 +396,30 @@ dependencies = [ "yansi", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.78" @@ -523,6 +582,44 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.205" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.205" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "serde_json" +version = "1.0.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -541,6 +638,17 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.58" @@ -566,6 +674,29 @@ dependencies = [ "windows", ] +[[package]] +name = "tabled" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c9303ee60b9bedf722012ea29ae3711ba13a67c9b9ae28993838b63057cb1b" +dependencies = [ + "papergrid", + "tabled_derive", +] + +[[package]] +name = "tabled_derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf0fb8bfdc709786c154e24a66777493fb63ae97e3036d914c8666774c477069" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "tempfile" version = "3.10.1" @@ -649,6 +780,7 @@ dependencies = [ "rand", "regex", "rlimit", + "tabled", "tempfile", "textwrap", "uu_ctrlaltdel", @@ -683,6 +815,8 @@ name = "uu_lsmem" version = "0.0.1" dependencies = [ "clap", + "serde", + "serde_json", "uucore", ] @@ -736,6 +870,12 @@ version = "0.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d841f8408028085ca65896cdd60b9925d4e407cb69989a64889f2bebbb51147b" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -803,7 +943,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.58", ] [[package]] @@ -814,7 +954,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.58", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 0e685f5..09301ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,8 @@ textwrap = { version = "0.16.0", features = ["terminal_size"] } xattr = "1.3.1" tempfile = "3.9.0" rand = { version = "0.8", features = ["small_rng"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.122" [dependencies] clap = { workspace = true } @@ -56,13 +58,13 @@ uucore = { workspace = true } phf = { workspace = true } textwrap = { workspace = true } - # lscpu = { optional = true, version = "0.0.1", package = "uu_lscpu", path = "src/uu/lscpu" } lsmem = { optional = true, version = "0.0.1", package = "uu_lsmem", path = "src/uu/lsmem" } mountpoint = { optional = true, version = "0.0.1", package = "uu_mountpoint", path = "src/uu/mountpoint" } ctrlaltdel = { optional = true, version = "0.0.1", package = "uu_ctrlaltdel", path = "src/uu/ctrlaltdel" } rev = { optional = true, version = "0.0.1", package = "uu_rev", path = "src/uu/rev" } +tabled = "0.16.0" [dev-dependencies] pretty_assertions = "1" diff --git a/src/uu/lsmem/Cargo.toml b/src/uu/lsmem/Cargo.toml index a76660a..2beae33 100644 --- a/src/uu/lsmem/Cargo.toml +++ b/src/uu/lsmem/Cargo.toml @@ -13,3 +13,5 @@ path = "src/main.rs" [dependencies] uucore = { workspace = true } clap = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } \ No newline at end of file diff --git a/src/uu/lsmem/src/lsmem.rs b/src/uu/lsmem/src/lsmem.rs index 1249159..877d662 100644 --- a/src/uu/lsmem/src/lsmem.rs +++ b/src/uu/lsmem/src/lsmem.rs @@ -3,47 +3,157 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use clap::{crate_version, Command}; -use uucore::{error::UResult, format_usage, help_about, help_usage}; +mod utils; -use std::borrow::BorrowMut; -use std::io::{self, BufRead, BufReader, BufWriter, Read, Write}; +use clap::{crate_version, Command}; +use clap::{Arg, ArgAction}; +use serde::Deserialize; +use std::fs; +use std::io::{self, BufRead, BufReader}; use std::path::{Path, PathBuf}; -use std::{default, fs}; +use std::str::FromStr; +use uucore::{error::UResult, format_usage, help_about, help_usage}; const ABOUT: &str = help_about!("lsmem.md"); const USAGE: &str = help_usage!("lsmem.md"); +mod options { + pub const BYTES: &str = "bytes"; +} + +const BUFSIZ: usize = 1024; + const PATH_SYS_MEMORY: &str = "/sys/devices/system/memory"; const PATH_BLOCK_SIZE_BYTES: &str = "/sys/devices/system/memory/block_size_bytes"; +const PATH_VALID_ZONES: &str = "/sys/devices/system/memory/valid_zones"; +const PATH_SUB_REMOVABLE: &str = "removable"; +const PATH_SUB_STATE: &str = "state"; + +struct ColDesc { + name: &'static str, // Rust's equivalent to `const char *` + whint: f64, // Rust uses `f64` for double precision floating-point numbers + flags: i32, // Using `i32` for integers + help: &'static str, // Rust's equivalent to `const char *` +} -#[derive(PartialEq, Clone)] +#[derive(Debug, Deserialize)] +enum Columns { + #[serde(rename = "RANGE")] + Range, + #[serde(rename = "SIZE")] + Size, + #[serde(rename = "STATE")] + State, + #[serde(rename = "REMOVABLE")] + Removable, + #[serde(rename = "BLOCK")] + Block, + #[serde(rename = "NODE")] + Node, + #[serde(rename = "ZONES")] + Zones, +} +const SCOLS_FL_RIGHT: i32 = 1; // Placeholder value, replace with the actual flag value if needed. + +static COLDESCS: [ColDesc; 7] = [ + ColDesc { + name: "RANGE", + whint: 0.0, + flags: 0, + help: "start and end address of the memory range", + }, + ColDesc { + name: "SIZE", + whint: 5.0, + flags: SCOLS_FL_RIGHT, + help: "size of the memory range", + }, + ColDesc { + name: "STATE", + whint: 0.0, + flags: SCOLS_FL_RIGHT, + help: "online status of the memory range", + }, + ColDesc { + name: "REMOVABLE", + whint: 0.0, + flags: SCOLS_FL_RIGHT, + help: "memory is removable", + }, + ColDesc { + name: "BLOCK", + whint: 0.0, + flags: SCOLS_FL_RIGHT, + help: "memory block number or blocks range", + }, + ColDesc { + name: "NODE", + whint: 0.0, + flags: SCOLS_FL_RIGHT, + help: "numa node of memory", + }, + ColDesc { + name: "ZONES", + whint: 0.0, + flags: SCOLS_FL_RIGHT, + help: "valid zones for the memory range", + }, +]; + +#[derive(Debug, Deserialize, PartialEq, Clone, Copy)] enum ZoneId { - ZoneDma = 0, + #[serde(rename = "ZONE_DMA")] + ZoneDma, + #[serde(rename = "ZONE_DMA32")] ZoneDma32, + #[serde(rename = "ZONE_NORMAL")] ZoneNormal, + #[serde(rename = "ZONE_HIGHMEM")] ZoneHighmem, + #[serde(rename = "ZONE_MOVABLE")] ZoneMovable, + #[serde(rename = "ZONE_DEVICE")] ZoneDevice, + #[serde(rename = "ZONE_NONE")] ZoneNone, + #[serde(rename = "ZONE_UNKNOWN")] ZoneUnknown, + #[serde(rename = "MAX_NR_ZONES")] MaxNrZones, } -static ZONE_NAMES: [&str; ZoneId::MaxNrZones as usize] = [ - "DMA", "DMA32", "Normal", "Highmem", "Movable", "Device", - "None", // Block contains more than one zone, can't be offlined - "Unknown", -]; - #[derive(PartialEq, Clone)] enum MemoryState { - Online = 0, + Online, Offline, GoingOffline, Unknown, } +impl core::fmt::Display for MemoryState { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + MemoryState::Online => write!(f, "online"), + MemoryState::Offline => write!(f, "offline"), + MemoryState::GoingOffline => write!(f, "going-offline"), + MemoryState::Unknown => write!(f, "unknown"), + } + } +} + +impl FromStr for MemoryState { + type Err = (); + fn from_str(input: &str) -> Result { + match input { + "online" => Ok(MemoryState::Online), + "offline" => Ok(MemoryState::Offline), + "going-offline" => Ok(MemoryState::GoingOffline), + "unknown" => Ok(MemoryState::Unknown), + _ => Err(()), + } + } +} + #[derive(Clone)] struct MemoryBlock { index: u64, @@ -52,12 +162,26 @@ struct MemoryBlock { node: i32, nr_zones: usize, zones: [ZoneId; ZoneId::MaxNrZones as usize], - removable: u8, + removable: bool, +} + +impl MemoryBlock { + fn new() -> Self { + MemoryBlock { + index: 0, + count: 0, + state: MemoryState::Unknown, + node: 0, + nr_zones: 0, + zones: [ZoneId::ZoneUnknown; ZoneId::MaxNrZones as usize], + removable: true, + } + } } struct Lsmem { ndirs: usize, - dirs: Vec, + dirs: Vec, blocks: Vec, nblocks: usize, block_size: u64, @@ -79,6 +203,8 @@ struct Lsmem { split_by_removable: bool, split_by_zones: bool, have_zones: bool, + + table: Vec>, } impl Lsmem { @@ -107,46 +233,61 @@ impl Lsmem { split_by_removable: false, split_by_zones: false, have_zones: false, + + table: Vec::default(), } } } -fn read_info() -> Lsmem { - let mut lsmem = Lsmem::new(); - lsmem.block_size = read_block_size_bytes().unwrap(); - let mut blocks = get_blocks(); - lsmem.ndirs = blocks.len(); +fn read_info(lsmem: &mut Lsmem) { + lsmem.block_size = u64::from_str_radix( + &read_file_content::(Path::new(PATH_BLOCK_SIZE_BYTES)).unwrap(), + 16, + ) + .unwrap(); + lsmem.dirs = get_block_paths(); + lsmem.ndirs = lsmem.dirs.len(); + for path in lsmem.dirs.iter() { + match memory_block_get_node(&path) { + Ok(_) => lsmem.have_nodes = true, + Err(_) => continue, + } + } for i in 0..lsmem.ndirs { - let blk = blocks[i].borrow_mut(); + let blk = memory_block_read_attrs(&lsmem, &lsmem.dirs[i]); if blk.state == MemoryState::Online { lsmem.mem_online += lsmem.block_size; } else { lsmem.mem_offline += lsmem.block_size; } if is_mergeable(&lsmem, &blk) { - blocks[lsmem.nblocks - 1].count += 1; + lsmem.blocks[lsmem.nblocks - 1].count += 1; + continue; } lsmem.nblocks += 1; lsmem.blocks.push(blk.clone()); } - return lsmem; } -fn get_blocks() -> Vec { - let mut blocks = Vec::::new(); - - list_files_and_folders(PATH_SYS_MEMORY).unwrap(); - - return blocks; +fn get_block_paths() -> Vec { + let mut paths = Vec::::new(); + for entry in fs::read_dir(PATH_SYS_MEMORY).unwrap() { + let entry = entry.unwrap(); + let path = entry.path(); + let filename = path.to_str().unwrap().split("/").last().unwrap(); + if path.is_dir() && filename.starts_with("memory") { + paths.push(path); + } + } + return paths; } fn is_mergeable(lsmem: &Lsmem, blk: &MemoryBlock) -> bool { if lsmem.nblocks == 0 { return false; } - if lsmem.list_all { return false; } @@ -181,6 +322,131 @@ fn is_mergeable(lsmem: &Lsmem, blk: &MemoryBlock) -> bool { return true; } +fn memory_block_get_node(path: &PathBuf) -> Result::Err> { + for entry in fs::read_dir(path).unwrap() { + let entry = entry.unwrap(); + let path = entry.path(); + let filename_str = path.to_string_lossy(); + if path.is_dir() && filename_str.starts_with("node") { + return filename_str["node".len()..].parse(); + } + } + return Ok(-1); +} + +fn memory_block_read_attrs(lsmem: &Lsmem, path: &PathBuf) -> MemoryBlock { + let mut blk = MemoryBlock::new(); + blk.count = 1; + blk.state = MemoryState::Unknown; + // blk.index = strtoumax(name + 6, NULL, 10); /* get of "memory" */ + let mut removable_path = path.clone(); + removable_path.push(PATH_SUB_REMOVABLE); + blk.removable = read_file_content::(&removable_path).is_ok(); + + let mut state_path = path.clone(); + state_path.push(PATH_SUB_STATE); + if let Ok(state_raw) = read_file_content::(&state_path) { + blk.state = MemoryState::from_str(&state_raw).unwrap(); + } + + if lsmem.have_nodes { + blk.node = memory_block_get_node(&path).unwrap(); + } + + blk.nr_zones = 0; + if lsmem.have_zones { + if let Ok(raw_content) = read_file_content::(Path::new(PATH_VALID_ZONES)) { + let zone_toks = raw_content.split(" ").collect::>(); + for i in 0..std::cmp::min(zone_toks.len(), ZoneId::MaxNrZones as usize) { + blk.zones[i] = serde_json::from_str(zone_toks[i]).unwrap(); + blk.nr_zones += 1; + } + } + } + return blk; +} + +fn get_column_id(idx: usize) -> Columns { + return Columns::Range; +} + +fn add_scols_line(lsmem: &mut Lsmem, blk: &MemoryBlock, ncolumns: usize, zone_names: &[&str]) { + // let mut new_line = Vec::new(); + + // let mut line = lsmem.table.new_line().unwrap_or_else(|| err_oom()); + + // for i in 0..ncolumns { + // let mut str_val: Option = None; + + // match get_column_id(i) { + // Columns::Range => { + // let start = blk.index * lsmem.block_size; + // let size = blk.count * lsmem.block_size; + // str_val = Some(format!("0x{:016x}-0x{:016x}", start, start + size - 1)); + // } + // Columns::Size => { + // if lsmem.bytes { + // str_val = Some(format!("{}", blk.count * lsmem.block_size)); + // } else { + // str_val = Some(utils::size_to_human_string(blk.count * lsmem.block_size)); + // } + // } + // Columns::State => { + // str_val = Some( + // match blk.state { + // MemoryState::Online => "online", + // MemoryState::Offline => "offline", + // MemoryState::GoingOffline => "on->off", + // _ => "?", + // } + // .to_string(), + // ); + // } + // Columns::Removable => { + // if blk.state == MemoryState::Online { + // str_val = Some(if blk.removable { "yes" } else { "no" }.to_string()); + // } + // } + // Columns::Block => { + // if blk.count == 1 { + // str_val = Some(format!("{}", blk.index)); + // } else { + // str_val = Some(format!("{}-{}", blk.index, blk.index + blk.count - 1)); + // } + // } + // Columns::Node => { + // if lsmem.have_nodes { + // str_val = Some(format!("{}", blk.node)); + // } + // } + // Columns::Zones => { + // if lsmem.have_zones { + // let mut valid_zones = String::new(); + // for j in 0..blk.nr_zones { + // let zone_id = blk.zones[j]; + // let zone_name = zone_names[zone_id as usize]; + // if valid_zones.len() + zone_name.len() > BUFSIZ - 2 { + // break; + // } + // if !valid_zones.is_empty() { + // valid_zones.push('/'); + // } + // valid_zones.push_str(zone_name); + // } + // str_val = Some(valid_zones); + // } + // } + // _ => {} + // } + + // if let Some(ref str_val) = str_val { + // if line.refer_data(i, str_val).is_err() { + // panic!() + // } + // } + // } +} + fn print_summary(lsmem: &Lsmem) { if lsmem.bytes { println!("{:<23} {:>15}", "Memory block size:", lsmem.block_size); @@ -190,130 +456,47 @@ fn print_summary(lsmem: &Lsmem) { println!( "{:<23} {:>15}", "Memory block size:", - size_to_human_string(lsmem.block_size) + utils::size_to_human_string(lsmem.block_size) ); println!( "{:<23} {:>15}", "Total online memory:", - size_to_human_string(lsmem.mem_online) + utils::size_to_human_string(lsmem.mem_online) ); println!( "{:<23} {:>15}", "Total offline memory:", - size_to_human_string(lsmem.mem_offline) + utils::size_to_human_string(lsmem.mem_offline) ); } } -fn size_to_human_string(bytes: u64) -> String { - // char buf[32]; - // int dec, exp; - // uint64_t frac; - // const char *letters = "BKMGTPE"; - // char suffix[sizeof(" KiB")], *psuf = suffix; - // char c; - - // if (options & SIZE_SUFFIX_SPACE) - // *psuf++ = ' '; - - // exp = get_exp(bytes); - // c = *(letters + (exp ? exp / 10 : 0)); - // dec = exp ? bytes / (1ULL << exp) : bytes; - // frac = exp ? bytes % (1ULL << exp) : 0; - - // *psuf++ = c; - - // if ((options & SIZE_SUFFIX_3LETTER) && (c != 'B')) { - // *psuf++ = 'i'; - // *psuf++ = 'B'; - // } - - // *psuf = '\0'; - - // /* fprintf(stderr, "exp: %d, unit: %c, dec: %d, frac: %jd\n", - // * exp, suffix[0], dec, frac); - // */ - // /* round */ - // if (frac) { - // /* get 3 digits after decimal point */ - // if (frac >= UINT64_MAX / 1000) - // frac = ((frac / 1024) * 1000) / (1ULL << (exp - 10)) ; - // else - // frac = (frac * 1000) / (1ULL << (exp)) ; - - // if (options & SIZE_DECIMAL_2DIGITS) { - // /* round 4/5 and keep 2 digits after decimal point */ - // frac = (frac + 5) / 10 ; - // } else { - // /* round 4/5 and keep 1 digit after decimal point */ - // frac = ((frac + 50) / 100) * 10 ; - // } - - // /* rounding could have overflowed */ - // if (frac == 100) { - // dec++; - // frac = 0; - // } - // } - - // if (frac) { - // struct lconv const *l = localeconv(); - // char *dp = l ? l->decimal_point : NULL; - // int len; - - // if (!dp || !*dp) - // dp = "."; - - // len = snprintf(buf, sizeof(buf), "%d%s%02" PRIu64, dec, dp, frac); - // if (len > 0 && (size_t) len < sizeof(buf)) { - // /* remove potential extraneous zero */ - // if (buf[len - 1] == '0') - // buf[len--] = '\0'; - // /* append suffix */ - // xstrncpy(buf+len, suffix, sizeof(buf) - len); - // } else - // *buf = '\0'; /* snprintf error */ - // } else - // snprintf(buf, sizeof(buf), "%d%s", dec, suffix); - - // return strdup(buf); - todo!("todo") -} - -fn list_files_and_folders>(path: P) -> io::Result> { - let mut paths = Vec::::new(); - for entry in fs::read_dir(path)? { - let entry = entry?; - let path = entry.path(); - let filename_str = path.to_string_lossy(); - if path.is_dir() && filename_str.starts_with("memory") { - paths.push(path); - } - } - - Ok(paths) -} - -fn read_block_size_bytes() -> io::Result { - // Open the file - let file = fs::File::open("/sys/devices/system/memory/block_size_bytes")?; - - // Create a buffered reader - let mut reader = io::BufReader::new(file); +// fn size_to_human_string(bytes: u64) -> String { +// todo!("todo") +// } - // Read the contents into a String +fn read_file_content(path: &Path) -> io::Result +where + T::Err: std::fmt::Debug, // Required to unwrap the result of T::from_str +{ + let file = fs::File::open(path)?; + let mut reader = BufReader::new(file); let mut content = String::new(); reader.read_line(&mut content)?; - - // Return the trimmed content to remove any trailing newline Ok(content.trim().to_string().parse().unwrap()) } #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { - let lsmem = read_info(); + let matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?; + let opt_bytes = matches.get_flag(options::BYTES); + + let mut lsmem = Lsmem::new(); + lsmem.bytes = opt_bytes; + + read_info(&mut lsmem); + print_summary(&lsmem); - let _matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?; Ok(()) } @@ -323,4 +506,11 @@ pub fn uu_app() -> Command { .about(ABOUT) .override_usage(format_usage(USAGE)) .infer_long_args(true) + .arg( + Arg::new(options::BYTES) + .short('b') + .long("bytes") + .help("print SIZE in bytes rather than in human readable format") + .action(ArgAction::SetTrue), + ) } diff --git a/src/uu/lsmem/src/utils.rs b/src/uu/lsmem/src/utils.rs new file mode 100644 index 0000000..8300374 --- /dev/null +++ b/src/uu/lsmem/src/utils.rs @@ -0,0 +1,76 @@ +enum HumanStringSize { + Suffix1Letter = 0, + Suffix3Letter = (1 << 0), + SuffixSpace = (1 << 1), + Decimal2Digits = (1 << 2), +} + +pub fn size_to_human_string(bytes: u64) -> String { + let mut buf = String::with_capacity(32); + let mut dec; + let mut frac; + let letters = "BKMGTPE"; + let mut suffix = String::with_capacity(4); + + let exp = get_exp(bytes); + let c = letters + .chars() + .nth(if exp != 0 { exp / 10 } else { 0 }) + .unwrap_or('B'); + dec = if exp != 0 { + bytes / ((1 as u64) << exp) + } else { + bytes + }; + frac = if exp != 0 { + bytes % ((1 as u64) << exp) + } else { + 0 + }; + + suffix.push(c); + + // Rounding logic + if frac != 0 { + if frac >= u64::MAX / 1000 { + frac = ((frac / 1024) * 1000) / (1 << (exp - 10)); + } else { + frac = (frac * 1000) / (1 << exp); + } + + // Round to 1 decimal place + frac = ((frac + 50) / 100) * 10; + + // Check for overflow due to rounding + if frac == 100 { + dec += 1; + frac = 0; + } + } + + // Format the result + if frac != 0 { + let decimal_point = "."; + buf = format!("{}{}{:02}", dec, decimal_point, frac); + if buf.ends_with('0') { + buf.pop(); // Remove extraneous zero + } + buf += &suffix; + } else { + buf += &format!("{dec}"); + buf += &suffix; + } + + buf +} + +fn get_exp(n: u64) -> usize { + let mut shft = 10; + while shft <= 60 { + if n < (1 << shft) { + break; + } + shft += 10; + } + shft - 10 +}