Skip to content

Commit ffe2b0b

Browse files
authored
perf(nl): optimize line numbering by using itoa and direct writing (#10757)
1 parent 25a43c6 commit ffe2b0b

3 files changed

Lines changed: 66 additions & 29 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/uu/nl/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ path = "src/nl.rs"
1919

2020
[dependencies]
2121
clap = { workspace = true }
22+
itoa = { workspace = true }
2223
regex = { workspace = true }
2324
uucore = { workspace = true }
2425
fluent = { workspace = true }

src/uu/nl/src/nl.rs

Lines changed: 64 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use clap::{Arg, ArgAction, Command};
77
use std::ffi::{OsStr, OsString};
88
use std::fs::File;
9-
use std::io::{BufRead, BufReader, BufWriter, Read, Write, stdin, stdout};
9+
use std::io::{self, BufRead, BufReader, BufWriter, Read, Write, stdin, stdout};
1010
use std::path::Path;
1111
use uucore::display::Quotable;
1212
use uucore::error::{FromIo, UResult, USimpleError, set_exit_code};
@@ -122,13 +122,41 @@ impl<T: AsRef<str>> From<T> for NumberFormat {
122122
impl NumberFormat {
123123
/// Turns a line number into a `String` with at least `min_width` chars,
124124
/// formatted according to the `NumberFormat`s variant.
125-
fn format(&self, number: i64, min_width: usize) -> String {
125+
fn format_to<W: Write>(&self, writer: &mut W, number: i64, min_width: usize) -> io::Result<()> {
126+
let mut buffer = itoa::Buffer::new();
127+
126128
match self {
127-
Self::Left => format!("{number:<min_width$}"),
128-
Self::Right => format!("{number:>min_width$}"),
129-
Self::RightZero if number < 0 => format!("-{0:0>1$}", number.abs(), min_width - 1),
130-
Self::RightZero => format!("{number:0>min_width$}"),
129+
Self::Left => {
130+
let num = buffer.format(number);
131+
writer.write_all(num.as_bytes())?;
132+
for _ in num.len()..min_width {
133+
writer.write_all(b" ")?;
134+
}
135+
}
136+
Self::Right => {
137+
let num = buffer.format(number);
138+
for _ in num.len()..min_width {
139+
writer.write_all(b" ")?;
140+
}
141+
writer.write_all(num.as_bytes())?;
142+
}
143+
Self::RightZero if number < 0 => {
144+
writer.write_all(b"-")?;
145+
let num = buffer.format(number.abs());
146+
for _ in num.len()..min_width.saturating_sub(1) {
147+
writer.write_all(b"0")?;
148+
}
149+
writer.write_all(num.as_bytes())?;
150+
}
151+
Self::RightZero => {
152+
let num = buffer.format(number);
153+
for _ in num.len()..min_width {
154+
writer.write_all(b"0")?;
155+
}
156+
writer.write_all(num.as_bytes())?;
157+
}
131158
}
159+
Ok(())
132160
}
133161
}
134162

@@ -346,8 +374,7 @@ pub fn uu_app() -> Command {
346374
}
347375

348376
/// Helper to write: prefix bytes + line bytes + newline
349-
fn write_line(writer: &mut impl Write, prefix: &[u8], line: &[u8]) -> std::io::Result<()> {
350-
writer.write_all(prefix)?;
377+
fn write_line(writer: &mut impl Write, line: &[u8]) -> io::Result<()> {
351378
writer.write_all(line)?;
352379
writeln!(writer)
353380
}
@@ -416,19 +443,22 @@ fn nl<T: Read>(reader: &mut BufReader<T>, stats: &mut Stats, settings: &Settings
416443
translate!("nl-error-line-number-overflow"),
417444
));
418445
};
419-
let mut prefix = settings
446+
settings
420447
.number_format
421-
.format(line_number, settings.number_width)
422-
.into_bytes();
423-
prefix.extend_from_slice(settings.number_separator.as_encoded_bytes());
424-
write_line(&mut writer, &prefix, &line)
448+
.format_to(&mut writer, line_number, settings.number_width)
449+
.map_err_context(|| translate!("nl-error-could-not-write"))?;
450+
writer
451+
.write_all(settings.number_separator.as_encoded_bytes())
425452
.map_err_context(|| translate!("nl-error-could-not-write"))?;
426453
stats.line_number = line_number.checked_add(settings.line_increment);
427454
} else {
428455
let prefix = " ".repeat(settings.number_width + 1);
429-
write_line(&mut writer, prefix.as_bytes(), &line)
456+
writer
457+
.write_all(prefix.as_bytes())
430458
.map_err_context(|| translate!("nl-error-could-not-write"))?;
431459
}
460+
write_line(&mut writer, &line)
461+
.map_err_context(|| translate!("nl-error-could-not-write"))?;
432462
}
433463
}
434464
writer
@@ -442,21 +472,26 @@ mod test {
442472
use super::*;
443473

444474
#[test]
445-
#[allow(clippy::cognitive_complexity)]
446475
fn test_format() {
447-
assert_eq!(NumberFormat::Left.format(12, 1), "12");
448-
assert_eq!(NumberFormat::Left.format(-12, 1), "-12");
449-
assert_eq!(NumberFormat::Left.format(12, 4), "12 ");
450-
assert_eq!(NumberFormat::Left.format(-12, 4), "-12 ");
451-
452-
assert_eq!(NumberFormat::Right.format(12, 1), "12");
453-
assert_eq!(NumberFormat::Right.format(-12, 1), "-12");
454-
assert_eq!(NumberFormat::Right.format(12, 4), " 12");
455-
assert_eq!(NumberFormat::Right.format(-12, 4), " -12");
456-
457-
assert_eq!(NumberFormat::RightZero.format(12, 1), "12");
458-
assert_eq!(NumberFormat::RightZero.format(-12, 1), "-12");
459-
assert_eq!(NumberFormat::RightZero.format(12, 4), "0012");
460-
assert_eq!(NumberFormat::RightZero.format(-12, 4), "-012");
476+
let helper = |fmt: NumberFormat, num: i64, width: usize| -> String {
477+
let mut buf = Vec::new();
478+
fmt.format_to(&mut buf, num, width).unwrap();
479+
String::from_utf8(buf).unwrap()
480+
};
481+
482+
assert_eq!(helper(NumberFormat::Left, 12, 1), "12");
483+
assert_eq!(helper(NumberFormat::Left, -12, 1), "-12");
484+
assert_eq!(helper(NumberFormat::Left, 12, 4), "12 ");
485+
assert_eq!(helper(NumberFormat::Left, -12, 4), "-12 ");
486+
487+
assert_eq!(helper(NumberFormat::Right, 12, 1), "12");
488+
assert_eq!(helper(NumberFormat::Right, -12, 1), "-12");
489+
assert_eq!(helper(NumberFormat::Right, 12, 4), " 12");
490+
assert_eq!(helper(NumberFormat::Right, -12, 4), " -12");
491+
492+
assert_eq!(helper(NumberFormat::RightZero, 12, 1), "12");
493+
assert_eq!(helper(NumberFormat::RightZero, -12, 1), "-12");
494+
assert_eq!(helper(NumberFormat::RightZero, 12, 4), "0012");
495+
assert_eq!(helper(NumberFormat::RightZero, -12, 4), "-012");
461496
}
462497
}

0 commit comments

Comments
 (0)