Skip to content

Commit b4b217f

Browse files
authored
perf: use BufWriter<StdoutLock> for all verbose/listing output
Replace println! in list, create, and extract operations with writeln! on a BufWriter<StdoutLock>. This acquires stdout's mutex once per operation instead of once per write call, and batches writes to reduce write(2) syscalls. Also propagates write errors (e.g. broken pipe) gracefully instead of panicking.
1 parent 7c00028 commit b4b217f

3 files changed

Lines changed: 19 additions & 7 deletions

File tree

src/uu/tar/src/operations/create.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use crate::errors::TarError;
77
use std::collections::VecDeque;
88
use std::fs::{self, File};
9+
use std::io::{self, BufWriter, Write};
910
use std::path::Component::{self, ParentDir, Prefix, RootDir};
1011
use std::path::{self, Path, PathBuf};
1112
use tar::Builder;
@@ -34,6 +35,7 @@ pub fn create_archive(archive_path: &Path, files: &[&Path], verbose: bool) -> UR
3435

3536
// Create Builder instance
3637
let mut builder = Builder::new(file);
38+
let mut out = BufWriter::new(io::stdout().lock());
3739

3840
// Add each file or directory to the archive
3941
for &path in files {
@@ -58,7 +60,7 @@ pub fn create_archive(archive_path: &Path, files: &[&Path], verbose: bool) -> UR
5860
})
5961
.collect::<Vec<_>>()
6062
.join("\n");
61-
println!("{to_print}");
63+
writeln!(out, "{to_print}").map_err(TarError::Io)?;
6264
}
6365

6466
// Normalize path if needed (so far, handles only absolute paths)
@@ -70,7 +72,8 @@ pub fn create_archive(archive_path: &Path, files: &[&Path], verbose: bool) -> UR
7072
[..original_components.len() - normalized_components.len()]
7173
.iter()
7274
.collect();
73-
println!("Removing leading `{}' from member names", removed.display());
75+
writeln!(out, "Removing leading `{}' from member names", removed.display())
76+
.map_err(TarError::Io)?;
7477
}
7578

7679
normalized
@@ -98,6 +101,7 @@ pub fn create_archive(archive_path: &Path, files: &[&Path], verbose: bool) -> UR
98101
}
99102

100103
// Finish writing the archive
104+
out.flush().map_err(TarError::Io)?;
101105
builder.finish().map_err(TarError::CannotFinalizeArchive)?;
102106

103107
Ok(())

src/uu/tar/src/operations/extract.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
use crate::errors::TarError;
77
use std::fs::File;
8+
use std::io::{self, BufWriter, Write};
89
use std::path::Path;
910
use tar::Archive;
1011
use uucore::error::UResult;
@@ -28,10 +29,11 @@ pub fn extract_archive(archive_path: &Path, verbose: bool) -> UResult<()> {
2829

2930
// Create Archive instance
3031
let mut archive = Archive::new(file);
32+
let mut out = BufWriter::new(io::stdout().lock());
3133

3234
// Extract to current directory
3335
if verbose {
34-
println!("Extracting archive: {}", archive_path.display());
36+
writeln!(out, "Extracting archive: {}", archive_path.display()).map_err(TarError::Io)?;
3537
}
3638

3739
// Iterate through entries for verbose output and error handling
@@ -45,7 +47,7 @@ pub fn extract_archive(archive_path: &Path, verbose: bool) -> UResult<()> {
4547
.to_path_buf();
4648

4749
if verbose {
48-
println!("{}", path.display());
50+
writeln!(out, "{}", path.display()).map_err(TarError::Io)?;
4951
}
5052

5153
// Unpack the entry
@@ -55,5 +57,6 @@ pub fn extract_archive(archive_path: &Path, verbose: bool) -> UResult<()> {
5557
})?;
5658
}
5759

60+
out.flush().map_err(TarError::Io)?;
5861
Ok(())
5962
}

src/uu/tar/src/operations/list.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use crate::errors::TarError;
77
use chrono::{TimeZone, Utc};
88
use std::fs::File;
9+
use std::io::{self, BufWriter, Write};
910
use std::path::Path;
1011
use tar::Archive;
1112
use uucore::error::UResult;
@@ -16,6 +17,7 @@ pub fn list_archive(archive_path: &Path, verbose: bool) -> UResult<()> {
1617
let file: File =
1718
File::open(archive_path).map_err(|e| TarError::from_io_error(e, archive_path))?;
1819
let mut archive = Archive::new(file);
20+
let mut out = BufWriter::new(io::stdout().lock());
1921

2022
for entry_result in archive.entries().map_err(TarError::CannotReadEntries)? {
2123
let entry = entry_result.map_err(TarError::CannotReadEntry)?;
@@ -68,16 +70,19 @@ pub fn list_archive(archive_path: &Path, verbose: bool) -> UResult<()> {
6870
.unwrap_or_else(Utc::now);
6971
let date_str = dt.format("%Y-%m-%d %H:%M");
7072

71-
println!(
73+
writeln!(
74+
out,
7275
"{permissions} {owner}/{group} {size:>8} {date_str} {}",
7376
path.display()
74-
);
77+
)
78+
.map_err(TarError::Io)?;
7579
} else {
7680
let path = entry.path().map_err(TarError::CannotReadEntryPath)?;
7781

78-
println!("{}", path.display());
82+
writeln!(out, "{}", path.display()).map_err(TarError::Io)?;
7983
}
8084
}
8185

86+
out.flush().map_err(TarError::Io)?;
8287
Ok(())
8388
}

0 commit comments

Comments
 (0)