diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 037a7f6c4..b5831f855 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -5,6 +5,7 @@ // spell-checker:ignore (ToDO) somegroup nlink tabsize dired subdired dtype colorterm stringly +use std::iter; #[cfg(windows)] use std::os::windows::fs::MetadataExt; use std::{cell::OnceCell, num::IntErrorKind}; @@ -2420,12 +2421,33 @@ fn display_dir_entry_size( } } -fn pad_left(string: &str, count: usize) -> String { - format!("{string:>count$}") +// A simple, performant, ExtendPad trait to add a string to a Vec, padding with spaces +// on the left or right, without making additional copies, or using formatting functions. +trait ExtendPad { + fn extend_pad_left(&mut self, string: &str, count: usize); + fn extend_pad_right(&mut self, string: &str, count: usize); } -fn pad_right(string: &str, count: usize) -> String { - format!("{string: { + fn extend_pad_left(&mut self, string: &str, count: usize) { + if string.len() < count { + self.extend(iter::repeat_n(b' ', count - string.len())); + } + self.extend(string.as_bytes()); + } + + fn extend_pad_right(&mut self, string: &str, count: usize) { + self.extend(string.as_bytes()); + if string.len() < count { + self.extend(iter::repeat_n(b' ', count - string.len())); + } + } +} + +// TODO: Consider converting callers to use ExtendPad instead, as it avoids +// additional copies. +fn pad_left(string: &str, count: usize) -> String { + format!("{string:>count$}") } fn return_total( @@ -2784,61 +2806,55 @@ fn display_item_long( output_display.extend(b"+"); } output_display.extend(b" "); - output_display.extend(pad_left(&display_symlink_count(md), padding.link_count).as_bytes()); + output_display.extend_pad_left(&display_symlink_count(md), padding.link_count); if config.long.owner { output_display.extend(b" "); - output_display.extend(pad_right(&display_uname(md, config), padding.uname).as_bytes()); + output_display.extend_pad_right(&display_uname(md, config), padding.uname); } if config.long.group { output_display.extend(b" "); - output_display.extend(pad_right(&display_group(md, config), padding.group).as_bytes()); + output_display.extend_pad_right(&display_group(md, config), padding.group); } if config.context { output_display.extend(b" "); - output_display.extend(pad_right(&item.security_context, padding.context).as_bytes()); + output_display.extend_pad_right(&item.security_context, padding.context); } // Author is only different from owner on GNU/Hurd, so we reuse // the owner, since GNU/Hurd is not currently supported by Rust. if config.long.author { output_display.extend(b" "); - output_display.extend(pad_right(&display_uname(md, config), padding.uname).as_bytes()); + output_display.extend_pad_right(&display_uname(md, config), padding.uname); } match display_len_or_rdev(md, config) { SizeOrDeviceId::Size(size) => { output_display.extend(b" "); - output_display.extend(pad_left(&size, padding.size).as_bytes()); + output_display.extend_pad_left(&size, padding.size); } SizeOrDeviceId::Device(major, minor) => { output_display.extend(b" "); - output_display.extend( - pad_left( - &major, - #[cfg(not(unix))] - 0usize, - #[cfg(unix)] - padding.major.max( - padding - .size - .saturating_sub(padding.minor.saturating_add(2usize)), - ), - ) - .as_bytes(), + output_display.extend_pad_left( + &major, + #[cfg(not(unix))] + 0usize, + #[cfg(unix)] + padding.major.max( + padding + .size + .saturating_sub(padding.minor.saturating_add(2usize)), + ), ); output_display.extend(b", "); - output_display.extend( - pad_left( - &minor, - #[cfg(not(unix))] - 0usize, - #[cfg(unix)] - padding.minor, - ) - .as_bytes(), + output_display.extend_pad_left( + &minor, + #[cfg(not(unix))] + 0usize, + #[cfg(unix)] + padding.minor, ); } }; @@ -2917,28 +2933,28 @@ fn display_item_long( output_display.extend(b"."); } output_display.extend(b" "); - output_display.extend(pad_left("?", padding.link_count).as_bytes()); + output_display.extend_pad_left("?", padding.link_count); if config.long.owner { output_display.extend(b" "); - output_display.extend(pad_right("?", padding.uname).as_bytes()); + output_display.extend_pad_right("?", padding.uname); } if config.long.group { output_display.extend(b" "); - output_display.extend(pad_right("?", padding.group).as_bytes()); + output_display.extend_pad_right("?", padding.group); } if config.context { output_display.extend(b" "); - output_display.extend(pad_right(&item.security_context, padding.context).as_bytes()); + output_display.extend_pad_right(&item.security_context, padding.context); } // Author is only different from owner on GNU/Hurd, so we reuse // the owner, since GNU/Hurd is not currently supported by Rust. if config.long.author { output_display.extend(b" "); - output_display.extend(pad_right("?", padding.uname).as_bytes()); + output_display.extend_pad_right("?", padding.uname); } let displayed_item = display_item_name( @@ -2953,9 +2969,9 @@ fn display_item_long( let date_len = 12; output_display.extend(b" "); - output_display.extend(pad_left("?", padding.size).as_bytes()); + output_display.extend_pad_left("?", padding.size); output_display.extend(b" "); - output_display.extend(pad_left("?", date_len).as_bytes()); + output_display.extend_pad_left("?", date_len); output_display.extend(b" "); if config.dired {