From 0a70ae7452dfd0630b1dd9b6b155ba8bbcc6647b Mon Sep 17 00:00:00 2001 From: FidelSch Date: Mon, 9 Feb 2026 12:34:55 -0300 Subject: [PATCH 1/4] cat: optimize splice usage based on file size --- src/uu/cat/src/cat.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/uu/cat/src/cat.rs b/src/uu/cat/src/cat.rs index babb0ce925e..8ace65dc850 100644 --- a/src/uu/cat/src/cat.rs +++ b/src/uu/cat/src/cat.rs @@ -26,6 +26,7 @@ use uucore::{fast_inc::fast_inc_one, format_usage}; /// Linux splice support #[cfg(any(target_os = "linux", target_os = "android"))] mod splice; +const FILE_SPLICE_SIZE_THRESHOLD: u64 = 1024 * 10; // 10KB // Allocate 32 digits for the line number. // An estimate is that we can print about 1e8 lines/seconds, so 32 digits @@ -359,9 +360,10 @@ fn cat_handle( handle: &mut InputHandle, options: &OutputOptions, state: &mut OutputState, + skip_splice: bool, ) -> CatResult<()> { if options.can_write_fast() { - write_fast(handle) + write_fast(handle, skip_splice) } else { write_lines(handle, options, state) } @@ -378,7 +380,7 @@ fn cat_path(path: &OsString, options: &OutputOptions, state: &mut OutputState) - reader: stdin, is_interactive: io::stdin().is_terminal(), }; - cat_handle(&mut handle, options, state) + cat_handle(&mut handle, options, state, false) } InputType::Directory => Err(CatError::IsDirectory), #[cfg(unix)] @@ -388,11 +390,20 @@ fn cat_path(path: &OsString, options: &OutputOptions, state: &mut OutputState) - if is_unsafe_overwrite(&file, &io::stdout()) { return Err(CatError::OutputIsInput); } + + // Skip splice if file is small enough + let metadata = file.metadata(); + let skip_splice = if let Ok(metadata) = metadata { + metadata.len() <= FILE_SPLICE_SIZE_THRESHOLD + } else { + false + }; + let mut handle = InputHandle { reader: file, is_interactive: false, }; - cat_handle(&mut handle, options, state) + cat_handle(&mut handle, options, state, skip_splice) } } } @@ -474,14 +485,14 @@ fn get_input_type(path: &OsString) -> CatResult { /// Writes handle to stdout with no configuration. This allows a /// simple memory copy. -fn write_fast(handle: &mut InputHandle) -> CatResult<()> { +fn write_fast(handle: &mut InputHandle, skip_splice: bool) -> CatResult<()> { let stdout = io::stdout(); let mut stdout_lock = stdout.lock(); #[cfg(any(target_os = "linux", target_os = "android"))] { // If we're on Linux or Android, try to use the splice() system call // for faster writing. If it works, we're done. - if !splice::write_fast_using_splice(handle, &stdout_lock)? { + if !skip_splice && !splice::write_fast_using_splice(handle, &stdout_lock)? { return Ok(()); } } From d02752946aaadd0abb4bc047c08d9c16cf4136e5 Mon Sep 17 00:00:00 2001 From: FidelSch Date: Mon, 9 Feb 2026 14:10:30 -0300 Subject: [PATCH 2/4] cat: increase file splice threshold to 16KB --- src/uu/cat/src/cat.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/cat/src/cat.rs b/src/uu/cat/src/cat.rs index 8ace65dc850..ab92ea9cd3c 100644 --- a/src/uu/cat/src/cat.rs +++ b/src/uu/cat/src/cat.rs @@ -26,7 +26,7 @@ use uucore::{fast_inc::fast_inc_one, format_usage}; /// Linux splice support #[cfg(any(target_os = "linux", target_os = "android"))] mod splice; -const FILE_SPLICE_SIZE_THRESHOLD: u64 = 1024 * 10; // 10KB +const FILE_SPLICE_SIZE_THRESHOLD: u64 = 1024 * 16; // 16KB // Allocate 32 digits for the line number. // An estimate is that we can print about 1e8 lines/seconds, so 32 digits From 70afe0334115b2d400c44cf6b915976ed10cbc60 Mon Sep 17 00:00:00 2001 From: FidelSch Date: Mon, 9 Feb 2026 14:20:49 -0300 Subject: [PATCH 3/4] lint(cat): unused variables --- src/uu/cat/src/cat.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/uu/cat/src/cat.rs b/src/uu/cat/src/cat.rs index ab92ea9cd3c..6ad6dd8bc3c 100644 --- a/src/uu/cat/src/cat.rs +++ b/src/uu/cat/src/cat.rs @@ -485,6 +485,7 @@ fn get_input_type(path: &OsString) -> CatResult { /// Writes handle to stdout with no configuration. This allows a /// simple memory copy. +#[allow(unused_variables)] fn write_fast(handle: &mut InputHandle, skip_splice: bool) -> CatResult<()> { let stdout = io::stdout(); let mut stdout_lock = stdout.lock(); From 04ad03d4eeda6b1846f8928b79065cc88900bda9 Mon Sep 17 00:00:00 2001 From: FidelSch Date: Mon, 9 Feb 2026 16:03:30 -0300 Subject: [PATCH 4/4] cat: bypass clap when possible --- src/uu/cat/src/cat.rs | 113 ++++++++++++++++++++++++++---------------- 1 file changed, 69 insertions(+), 44 deletions(-) diff --git a/src/uu/cat/src/cat.rs b/src/uu/cat/src/cat.rs index 6ad6dd8bc3c..d5e10f101d3 100644 --- a/src/uu/cat/src/cat.rs +++ b/src/uu/cat/src/cat.rs @@ -219,54 +219,79 @@ mod options { #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { - let matches = uucore::clap_localization::handle_clap_result(uu_app(), args)?; + let args: Vec = args.collect(); + let mut files: Vec; + let options; - let number_mode = if matches.get_flag(options::NUMBER_NONBLANK) { - NumberingMode::NonEmpty - } else if matches.get_flag(options::NUMBER) { - NumberingMode::All + if args.len() > 1 + && args + .iter() + .skip(1) + .all(|a| !a.to_string_lossy().starts_with('-') || a == "-") + { + // No options, just files. + // Skip clap parsing + files = args.into_iter().skip(1).collect(); + if files.is_empty() { + files.push(OsString::from("-")); + } + options = OutputOptions { + show_ends: false, + number: NumberingMode::None, + show_nonprint: false, + show_tabs: false, + squeeze_blank: false, + }; } else { - NumberingMode::None - }; + let matches = uucore::clap_localization::handle_clap_result(uu_app(), args)?; - let show_nonprint = [ - options::SHOW_ALL.to_owned(), - options::SHOW_NONPRINTING_ENDS.to_owned(), - options::SHOW_NONPRINTING_TABS.to_owned(), - options::SHOW_NONPRINTING.to_owned(), - ] - .iter() - .any(|v| matches.get_flag(v)); - - let show_ends = [ - options::SHOW_ENDS.to_owned(), - options::SHOW_ALL.to_owned(), - options::SHOW_NONPRINTING_ENDS.to_owned(), - ] - .iter() - .any(|v| matches.get_flag(v)); - - let show_tabs = [ - options::SHOW_ALL.to_owned(), - options::SHOW_TABS.to_owned(), - options::SHOW_NONPRINTING_TABS.to_owned(), - ] - .iter() - .any(|v| matches.get_flag(v)); - - let squeeze_blank = matches.get_flag(options::SQUEEZE_BLANK); - let files: Vec = match matches.get_many::(options::FILE) { - Some(v) => v.cloned().collect(), - None => vec![OsString::from("-")], - }; + let number_mode = if matches.get_flag(options::NUMBER_NONBLANK) { + NumberingMode::NonEmpty + } else if matches.get_flag(options::NUMBER) { + NumberingMode::All + } else { + NumberingMode::None + }; - let options = OutputOptions { - show_ends, - number: number_mode, - show_nonprint, - show_tabs, - squeeze_blank, - }; + let show_nonprint = [ + options::SHOW_ALL.to_owned(), + options::SHOW_NONPRINTING_ENDS.to_owned(), + options::SHOW_NONPRINTING_TABS.to_owned(), + options::SHOW_NONPRINTING.to_owned(), + ] + .iter() + .any(|v| matches.get_flag(v)); + + let show_ends = [ + options::SHOW_ENDS.to_owned(), + options::SHOW_ALL.to_owned(), + options::SHOW_NONPRINTING_ENDS.to_owned(), + ] + .iter() + .any(|v| matches.get_flag(v)); + + let show_tabs = [ + options::SHOW_ALL.to_owned(), + options::SHOW_TABS.to_owned(), + options::SHOW_NONPRINTING_TABS.to_owned(), + ] + .iter() + .any(|v| matches.get_flag(v)); + + let squeeze_blank = matches.get_flag(options::SQUEEZE_BLANK); + files = match matches.get_many::(options::FILE) { + Some(v) => v.cloned().collect(), + None => vec![OsString::from("-")], + }; + + options = OutputOptions { + show_ends, + number: number_mode, + show_nonprint, + show_tabs, + squeeze_blank, + }; + } cat_files(&files, &options) }