From 4d8ff5e99a5c2bcdb1b515985d7dd21a45381386 Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Sat, 21 Feb 2026 23:00:58 +0000 Subject: [PATCH 1/2] numfmt: optimize output handling by using stdout directly --- src/uu/numfmt/src/format.rs | 48 +++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/uu/numfmt/src/format.rs b/src/uu/numfmt/src/format.rs index 684611b3cee..88a11c9d9a8 100644 --- a/src/uu/numfmt/src/format.rs +++ b/src/uu/numfmt/src/format.rs @@ -2,7 +2,10 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. + // spell-checker:ignore powf + +use std::io::Write as _; use uucore::display::Quotable; use uucore::translate; @@ -468,20 +471,15 @@ fn split_bytes<'a>(input: &'a [u8], delim: &'a [u8]) -> impl Iterator Result<()> { - let delimiter = options.delimiter.as_ref().unwrap(); - let mut output: Vec = Vec::new(); - let eol = if options.zero_terminated { - b'\0' - } else { - b'\n' - }; + let delimiter = options.delimiter.as_deref().unwrap(); + let mut stdout = std::io::stdout().lock(); for (n, field) in (1..).zip(split_bytes(input, delimiter)) { let field_selected = uucore::ranges::contain(&options.fields, n); // add delimiter before second and subsequent fields if n > 1 { - output.extend_from_slice(delimiter); + stdout.write_all(delimiter).unwrap(); } if field_selected { @@ -490,20 +488,24 @@ pub fn format_and_print_delimited(input: &[u8], options: &NumfmtOptions) -> Resu .map_err(|_| translate!("numfmt-error-invalid-number", "input" => String::from_utf8_lossy(field).into_owned().quote()))? .trim_start(); let formatted = format_string(field_str, options, None)?; - output.extend_from_slice(formatted.as_bytes()); + stdout.write_all(formatted.as_bytes()).unwrap(); } else { // add unselected field without conversion - output.extend_from_slice(field); + stdout.write_all(field).unwrap(); } } - output.push(eol); - std::io::Write::write_all(&mut std::io::stdout(), &output).map_err(|e| e.to_string())?; + let eol = if options.zero_terminated { + b"\0" + } else { + b"\n" + }; + stdout.write_all(eol).unwrap(); Ok(()) } pub fn format_and_print_whitespace(s: &str, options: &NumfmtOptions) -> Result<()> { - let mut output = String::new(); + let mut stdout = std::io::stdout().lock(); for (n, (prefix, field)) in (1..).zip(WhitespaceSplitter { s: Some(s) }) { let field_selected = uucore::ranges::contain(&options.fields, n); @@ -513,7 +515,7 @@ pub fn format_and_print_whitespace(s: &str, options: &NumfmtOptions) -> Result<( // add delimiter before second and subsequent fields let prefix = if n > 1 { - output.push(' '); + stdout.write_all(b" ").unwrap(); &prefix[1..] } else { prefix @@ -525,24 +527,28 @@ pub fn format_and_print_whitespace(s: &str, options: &NumfmtOptions) -> Result<( None }; - output.push_str(&format_string(field, options, implicit_padding)?); + let formatted = format_string(field, options, implicit_padding)?; + stdout.write_all(formatted.as_bytes()).unwrap(); } else { // the -z option converts an initial \n into a space let prefix = if options.zero_terminated && prefix.starts_with('\n') { - output.push(' '); + stdout.write_all(b" ").unwrap(); &prefix[1..] } else { prefix }; // add unselected field without conversion - output.push_str(prefix); - output.push_str(field); + stdout.write_all(prefix.as_bytes()).unwrap(); + stdout.write_all(field.as_bytes()).unwrap(); } } - let eol = if options.zero_terminated { '\0' } else { '\n' }; - output.push(eol); - print!("{output}"); + let eol = if options.zero_terminated { + b"\0" + } else { + b"\n" + }; + stdout.write_all(eol).unwrap(); Ok(()) } From 9a82992806c4ff2131847f2c43bbeb0a69f0f8f7 Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Sun, 22 Feb 2026 00:55:27 +0000 Subject: [PATCH 2/2] refactor(numfmt): replace stdout usage with writer parameter --- src/uu/numfmt/src/format.rs | 35 ++++++++++++++++++-------------- src/uu/numfmt/src/numfmt.rs | 40 +++++++++++++++++++++---------------- 2 files changed, 43 insertions(+), 32 deletions(-) diff --git a/src/uu/numfmt/src/format.rs b/src/uu/numfmt/src/format.rs index 88a11c9d9a8..3a5db2ba060 100644 --- a/src/uu/numfmt/src/format.rs +++ b/src/uu/numfmt/src/format.rs @@ -5,7 +5,6 @@ // spell-checker:ignore powf -use std::io::Write as _; use uucore::display::Quotable; use uucore::translate; @@ -470,16 +469,19 @@ fn split_bytes<'a>(input: &'a [u8], delim: &'a [u8]) -> impl Iterator Result<()> { +pub fn write_formatted_with_delimiter( + writer: &mut W, + input: &[u8], + options: &NumfmtOptions, +) -> Result<()> { let delimiter = options.delimiter.as_deref().unwrap(); - let mut stdout = std::io::stdout().lock(); for (n, field) in (1..).zip(split_bytes(input, delimiter)) { let field_selected = uucore::ranges::contain(&options.fields, n); // add delimiter before second and subsequent fields if n > 1 { - stdout.write_all(delimiter).unwrap(); + writer.write_all(delimiter).unwrap(); } if field_selected { @@ -488,10 +490,10 @@ pub fn format_and_print_delimited(input: &[u8], options: &NumfmtOptions) -> Resu .map_err(|_| translate!("numfmt-error-invalid-number", "input" => String::from_utf8_lossy(field).into_owned().quote()))? .trim_start(); let formatted = format_string(field_str, options, None)?; - stdout.write_all(formatted.as_bytes()).unwrap(); + writer.write_all(formatted.as_bytes()).unwrap(); } else { // add unselected field without conversion - stdout.write_all(field).unwrap(); + writer.write_all(field).unwrap(); } } @@ -500,13 +502,16 @@ pub fn format_and_print_delimited(input: &[u8], options: &NumfmtOptions) -> Resu } else { b"\n" }; - stdout.write_all(eol).unwrap(); + writer.write_all(eol).unwrap(); Ok(()) } -pub fn format_and_print_whitespace(s: &str, options: &NumfmtOptions) -> Result<()> { - let mut stdout = std::io::stdout().lock(); +pub fn write_formatted_with_whitespace( + writer: &mut W, + s: &str, + options: &NumfmtOptions, +) -> Result<()> { for (n, (prefix, field)) in (1..).zip(WhitespaceSplitter { s: Some(s) }) { let field_selected = uucore::ranges::contain(&options.fields, n); @@ -515,7 +520,7 @@ pub fn format_and_print_whitespace(s: &str, options: &NumfmtOptions) -> Result<( // add delimiter before second and subsequent fields let prefix = if n > 1 { - stdout.write_all(b" ").unwrap(); + writer.write_all(b" ").unwrap(); &prefix[1..] } else { prefix @@ -528,18 +533,18 @@ pub fn format_and_print_whitespace(s: &str, options: &NumfmtOptions) -> Result<( }; let formatted = format_string(field, options, implicit_padding)?; - stdout.write_all(formatted.as_bytes()).unwrap(); + writer.write_all(formatted.as_bytes()).unwrap(); } else { // the -z option converts an initial \n into a space let prefix = if options.zero_terminated && prefix.starts_with('\n') { - stdout.write_all(b" ").unwrap(); + writer.write_all(b" ").unwrap(); &prefix[1..] } else { prefix }; // add unselected field without conversion - stdout.write_all(prefix.as_bytes()).unwrap(); - stdout.write_all(field.as_bytes()).unwrap(); + writer.write_all(prefix.as_bytes()).unwrap(); + writer.write_all(field.as_bytes()).unwrap(); } } @@ -548,7 +553,7 @@ pub fn format_and_print_whitespace(s: &str, options: &NumfmtOptions) -> Result<( } else { b"\n" }; - stdout.write_all(eol).unwrap(); + writer.write_all(eol).unwrap(); Ok(()) } diff --git a/src/uu/numfmt/src/numfmt.rs b/src/uu/numfmt/src/numfmt.rs index 2d53d41ce4c..d0bedfa49fb 100644 --- a/src/uu/numfmt/src/numfmt.rs +++ b/src/uu/numfmt/src/numfmt.rs @@ -4,7 +4,7 @@ // file that was distributed with this source code. use crate::errors::NumfmtError; -use crate::format::{format_and_print_delimited, format_and_print_whitespace}; +use crate::format::{write_formatted_with_delimiter, write_formatted_with_whitespace}; use crate::options::{ DEBUG, DELIMITER, FIELD, FIELD_DEFAULT, FORMAT, FROM, FROM_DEFAULT, FROM_UNIT, FROM_UNIT_DEFAULT, FormatOptions, HEADER, HEADER_DEFAULT, INVALID, InvalidModes, NUMBER, @@ -14,7 +14,7 @@ use crate::options::{ use crate::units::{Result, Unit}; use clap::{Arg, ArgAction, ArgMatches, Command, builder::ValueParser, parser::ValueSource}; use std::ffi::OsString; -use std::io::{BufRead, Error, Write, stderr}; +use std::io::{BufRead, Error, Write as _, stderr}; use std::result::Result as StdResult; use std::str::FromStr; @@ -32,8 +32,9 @@ pub mod options; mod units; fn handle_args<'a>(args: impl Iterator, options: &NumfmtOptions) -> UResult<()> { + let mut stdout = std::io::stdout().lock(); for l in args { - format_and_handle_validation(l, options)?; + write_line(&mut stdout, l, options)?; } Ok(()) } @@ -51,33 +52,32 @@ fn handle_buffer_iterator( options: &NumfmtOptions, terminator: u8, ) -> UResult<()> { + let mut stdout = std::io::stdout().lock(); for (idx, line_result) in iter.enumerate() { match line_result { Ok(line) if idx < options.header => { - std::io::stdout().write_all(&line)?; - std::io::stdout().write_all(&[terminator])?; + stdout.write_all(&line)?; + stdout.write_all(&[terminator])?; Ok(()) } - Ok(line) => format_and_handle_validation(&line, options), + Ok(line) => write_line(&mut stdout, &line, options), Err(err) => return Err(Box::new(NumfmtError::IoError(err.to_string()))), }?; } Ok(()) } -fn format_and_handle_validation(input_line: &[u8], options: &NumfmtOptions) -> UResult<()> { - let eol = if options.zero_terminated { - b'\0' - } else { - b'\n' - }; - +fn write_line( + writer: &mut W, + input_line: &[u8], + options: &NumfmtOptions, +) -> UResult<()> { let handled_line = if options.delimiter.is_some() { - format_and_print_delimited(input_line, options) + write_formatted_with_delimiter(writer, input_line, options) } else { // Whitespace mode requires valid UTF-8 match std::str::from_utf8(input_line) { - Ok(s) => format_and_print_whitespace(s, options), + Ok(s) => write_formatted_with_whitespace(writer, s, options), Err(_) => Err(translate!("numfmt-error-invalid-input")), } }; @@ -95,8 +95,14 @@ fn format_and_handle_validation(input_line: &[u8], options: &NumfmtOptions) -> U } InvalidModes::Ignore => {} } - std::io::stdout().write_all(input_line)?; - std::io::stdout().write_all(&[eol])?; + writer.write_all(input_line)?; + + let eol = if options.zero_terminated { + b"\0" + } else { + b"\n" + }; + writer.write_all(eol)?; } Ok(())