From c311e208ae2c8c8238e97f171f3ca18b26c64eff Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Thu, 3 Apr 2025 18:47:45 +0200 Subject: [PATCH] seq: Add constant width support in fast path It is actually quite easy to implement, we just start with a padded number and increment as usual. --- src/uu/seq/BENCHMARKING.md | 5 +++-- src/uu/seq/src/seq.rs | 37 +++++++++++++++++++++++++------------ 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/uu/seq/BENCHMARKING.md b/src/uu/seq/BENCHMARKING.md index e9c92d1bc..5758d2a77 100644 --- a/src/uu/seq/BENCHMARKING.md +++ b/src/uu/seq/BENCHMARKING.md @@ -88,7 +88,8 @@ with the default implementation, at the expense of some added code complexity. Just from performance numbers, it is clear that GNU `seq` uses similar tricks, but we are more liberal on when we use our fast path (e.g. large -increments are supported). Our fast path implementation gets within ~10% -of `seq` performance. +increments are supported, equal width is supported). Our fast path +implementation gets within ~10% of `seq` performance when its fast +path is activated. [0]: https://github.com/sharkdp/hyperfine diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index fcdf8ad3b..01cb8980d 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -151,13 +151,17 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } }; - let precision = select_precision(&first, &increment, &last); - // If a format was passed on the command line, use that. // If not, use some default format based on parameters precision. - let format = match options.format { - Some(str) => Format::::parse(str)?, + let (format, padding, fast_allowed) = match options.format { + Some(str) => ( + Format::::parse(str)?, + 0, + false, + ), None => { + let precision = select_precision(&first, &increment, &last); + let padding = if options.equal_width { let precision_value = precision.unwrap_or(0); first @@ -188,19 +192,22 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { ..Default::default() }, }; - Format::from_formatter(formatter) + // Allow fast printing if precision is 0 (integer inputs), `print_seq` will do further checks. + ( + Format::from_formatter(formatter), + padding, + precision == Some(0), + ) } }; - // Allow fast printing, `print_seq` will do further checks. - let fast_allowed = options.format.is_none() && !options.equal_width && precision == Some(0); - let result = print_seq( (first.number, increment.number, last.number), &options.separator, &options.terminator, &format, fast_allowed, + padding, ); match result { @@ -304,8 +311,6 @@ fn fast_inc(val: &mut [u8], start: usize, end: usize, inc: &[u8]) -> usize { /// Integer print, default format, positive increment: fast code path /// that avoids reformating digit at all iterations. -/// TODO: We could easily support equal_width (we do quite a bit of work -/// _not_ supporting that and aligning the number to the left). fn fast_print_seq( mut stdout: impl Write, first: &BigUint, @@ -313,6 +318,7 @@ fn fast_print_seq( last: &BigUint, separator: &str, terminator: &str, + padding: usize, ) -> std::io::Result<()> { // Nothing to do, just return. if last < first { @@ -338,8 +344,9 @@ fn fast_print_seq( // // We keep track of start in this buffer, as the number grows. // When printing, we take a slice between start and end. - let size = separator.len() + last_length; - let mut buf = vec![0u8; size]; + let size = last_length.max(padding) + separator.len(); + // Fill with '0', this is needed for equal_width, and harmless otherwise. + let mut buf = vec![b'0'; size]; let buf = buf.as_mut_slice(); let num_end = buf.len() - separator.len(); @@ -349,6 +356,10 @@ fn fast_print_seq( buf[start..num_end].copy_from_slice(first_str.as_bytes()); buf[num_end..].copy_from_slice(separator.as_bytes()); + // Normally, if padding is > 0, it should be equal to last_length, + // so start would be == 0, but there are corner cases. + start = start.min(num_end - padding); + // Prepare the number to increment with as a string let inc_str = increment.to_string(); let inc_str = inc_str.as_bytes(); @@ -379,6 +390,7 @@ fn print_seq( terminator: &str, format: &Format, fast_allowed: bool, + padding: usize, // Used by fast path only ) -> std::io::Result<()> { let stdout = stdout().lock(); let mut stdout = BufWriter::new(stdout); @@ -402,6 +414,7 @@ fn print_seq( &last_bui, separator, terminator, + padding, ); } }