Skip to content

Commit ec52290

Browse files
committed
tee: improve stdout performance
1 parent f65350a commit ec52290

2 files changed

Lines changed: 31 additions & 18 deletions

File tree

src/uu/tee/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ doctest = false
2121

2222
[dependencies]
2323
clap = { workspace = true }
24-
uucore = { workspace = true, features = ["libc", "parser", "signals"] }
2524
fluent = { workspace = true }
25+
rustix = { workspace = true, features = ["stdio"] }
26+
uucore = { workspace = true, features = ["libc", "parser", "signals"] }
2627

2728
[dev-dependencies]
2829
divan = { workspace = true }

src/uu/tee/src/tee.rs

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55

66
// spell-checker:ignore espidf nopipe
77

8+
use core::mem::ManuallyDrop;
89
use std::ffi::OsString;
10+
use std::fs::File;
911
use std::fs::OpenOptions;
10-
use std::io::{Error, ErrorKind, Read, Result, Write, stderr, stdin, stdout};
12+
use std::io::{Error, ErrorKind, Read, Result, Write, stderr, stdin};
1113
use std::path::PathBuf;
1214
use uucore::display::Quotable;
1315
use uucore::error::{UResult, strip_errno};
@@ -80,7 +82,7 @@ fn tee(options: &Options) -> Result<()> {
8082
0,
8183
NamedWriter {
8284
name: translate!("tee-standard-output").into(),
83-
inner: Writer::Stdout(stdout()),
85+
inner: Writer::stdout_raw(),
8486
},
8587
);
8688

@@ -197,21 +199,16 @@ impl MultiWriter {
197199

198200
fn write_flush(&mut self, buf: &[u8]) -> Result<()> {
199201
let mode = self.output_error_mode;
200-
self.writers.retain_mut(|writer| {
201-
let res = (|| {
202-
writer.inner.write_all(buf)?;
203-
writer.inner.flush()
204-
})();
205-
match res {
202+
self.writers
203+
.retain_mut(|writer| match writer.inner.write_all(buf) {
206204
Ok(()) => true,
207205
Err(e) => {
208206
if let Err(e) = process_error(mode, e, writer, &mut self.ignored_errors) {
209207
self.aborted.get_or_insert(e);
210208
}
211209
false
212210
}
213-
}
214-
});
211+
});
215212
self.aborted.take().map_or(
216213
if self.writers.is_empty() {
217214
// This error kind will never be raised by the standard
@@ -250,23 +247,38 @@ fn process_error(
250247
}
251248

252249
enum Writer {
253-
File(std::fs::File),
254-
Stdout(std::io::Stdout),
250+
File(File),
251+
StdoutRaw(ManuallyDrop<File>),
252+
}
253+
254+
impl Writer {
255+
#[cfg(not(windows))]
256+
fn stdout_raw() -> Self {
257+
// SAFETY: We ensure that the file descriptor is never closed by
258+
// wrapping the `File` in `ManuallyDrop`.
259+
let fd = unsafe { rustix::stdio::take_stdout() };
260+
let f = File::from(fd);
261+
Self::StdoutRaw(ManuallyDrop::new(f))
262+
}
263+
264+
#[cfg(windows)]
265+
fn stdout_raw() -> Self {
266+
let handle = stdout().as_raw_handle();
267+
let f = unsafe { File::from_raw_handle(handle) };
268+
Self::StdoutRaw(ManuallyDrop::new(f))
269+
}
255270
}
256271

257272
impl Write for Writer {
258273
fn write(&mut self, buf: &[u8]) -> Result<usize> {
259274
match self {
260275
Self::File(f) => f.write(buf),
261-
Self::Stdout(s) => s.write(buf),
276+
Self::StdoutRaw(s) => s.write(buf),
262277
}
263278
}
264279

265280
fn flush(&mut self) -> Result<()> {
266-
match self {
267-
Self::File(f) => f.flush(),
268-
Self::Stdout(s) => s.flush(),
269-
}
281+
Ok(())
270282
}
271283
}
272284

0 commit comments

Comments
 (0)