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.
This commit is contained in:
Jeff Bailey
2026-04-08 20:03:07 +00:00
committed by GitHub
parent 30537acf7d
commit b469a73940
4 changed files with 24 additions and 11 deletions
+10 -2
View File
@@ -6,6 +6,7 @@
use crate::errors::TarError;
use std::collections::VecDeque;
use std::fs::{self, File};
use std::io::{self, BufWriter, Write};
use std::path::Component::{self, ParentDir, Prefix, RootDir};
use std::path::{self, Path, PathBuf};
use tar::Builder;
@@ -34,6 +35,7 @@ pub fn create_archive(archive_path: &Path, files: &[&Path], verbose: bool) -> UR
// Create Builder instance
let mut builder = Builder::new(file);
let mut out = BufWriter::new(io::stdout().lock());
// Add each file or directory to the archive
for &path in files {
@@ -58,7 +60,7 @@ pub fn create_archive(archive_path: &Path, files: &[&Path], verbose: bool) -> UR
})
.collect::<Vec<_>>()
.join("\n");
println!("{to_print}");
writeln!(out, "{to_print}").map_err(TarError::Io)?;
}
// Normalize path if needed (so far, handles only absolute paths)
@@ -70,7 +72,12 @@ pub fn create_archive(archive_path: &Path, files: &[&Path], verbose: bool) -> UR
[..original_components.len() - normalized_components.len()]
.iter()
.collect();
println!("Removing leading `{}' from member names", removed.display());
writeln!(
out,
"Removing leading `{}' from member names",
removed.display()
)
.map_err(TarError::Io)?;
}
normalized
@@ -98,6 +105,7 @@ pub fn create_archive(archive_path: &Path, files: &[&Path], verbose: bool) -> UR
}
// Finish writing the archive
out.flush().map_err(TarError::Io)?;
builder.finish().map_err(TarError::CannotFinalizeArchive)?;
Ok(())
+5 -2
View File
@@ -5,6 +5,7 @@
use crate::errors::TarError;
use std::fs::File;
use std::io::{self, BufWriter, Write};
use std::path::Path;
use tar::Archive;
use uucore::error::UResult;
@@ -28,10 +29,11 @@ pub fn extract_archive(archive_path: &Path, verbose: bool) -> UResult<()> {
// Create Archive instance
let mut archive = Archive::new(file);
let mut out = BufWriter::new(io::stdout().lock());
// Extract to current directory
if verbose {
println!("Extracting archive: {}", archive_path.display());
writeln!(out, "Extracting archive: {}", archive_path.display()).map_err(TarError::Io)?;
}
// Iterate through entries for verbose output and error handling
@@ -45,7 +47,7 @@ pub fn extract_archive(archive_path: &Path, verbose: bool) -> UResult<()> {
.to_path_buf();
if verbose {
println!("{}", path.display());
writeln!(out, "{}", path.display()).map_err(TarError::Io)?;
}
// Unpack the entry
@@ -55,5 +57,6 @@ pub fn extract_archive(archive_path: &Path, verbose: bool) -> UResult<()> {
})?;
}
out.flush().map_err(TarError::Io)?;
Ok(())
}
+8 -3
View File
@@ -6,6 +6,7 @@
use crate::errors::TarError;
use chrono::{TimeZone, Utc};
use std::fs::File;
use std::io::{self, BufWriter, Write};
use std::path::Path;
use tar::Archive;
use uucore::error::UResult;
@@ -16,6 +17,7 @@ pub fn list_archive(archive_path: &Path, verbose: bool) -> UResult<()> {
let file: File =
File::open(archive_path).map_err(|e| TarError::from_io_error(e, archive_path))?;
let mut archive = Archive::new(file);
let mut out = BufWriter::new(io::stdout().lock());
for entry_result in archive.entries().map_err(TarError::CannotReadEntries)? {
let entry = entry_result.map_err(TarError::CannotReadEntry)?;
@@ -68,16 +70,19 @@ pub fn list_archive(archive_path: &Path, verbose: bool) -> UResult<()> {
.unwrap_or_else(Utc::now);
let date_str = dt.format("%Y-%m-%d %H:%M");
println!(
writeln!(
out,
"{permissions} {owner}/{group} {size:>8} {date_str} {}",
path.display()
);
)
.map_err(TarError::Io)?;
} else {
let path = entry.path().map_err(TarError::CannotReadEntryPath)?;
println!("{}", path.display());
writeln!(out, "{}", path.display()).map_err(TarError::Io)?;
}
}
out.flush().map_err(TarError::Io)?;
Ok(())
}
+1 -4
View File
@@ -44,10 +44,7 @@ fn test_tar_error_code() {
.code(),
2
);
assert_eq!(
TarError::Io(io::Error::new(io::ErrorKind::Other, "test")).code(),
2
);
assert_eq!(TarError::Io(io::Error::other("test")).code(), 2);
}
#[test]