1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-29 12:07:46 +00:00

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.
This commit is contained in:
Nicolas Boichat 2025-04-03 18:47:45 +02:00
parent 560d1eb1b7
commit c311e208ae
2 changed files with 28 additions and 14 deletions

View file

@ -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

View file

@ -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::<num_format::Float, &ExtendedBigDecimal>::parse(str)?,
let (format, padding, fast_allowed) = match options.format {
Some(str) => (
Format::<num_format::Float, &ExtendedBigDecimal>::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<num_format::Float, &ExtendedBigDecimal>,
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,
);
}
}