Skip to content

Commit a44128f

Browse files
committed
feat: refactor stdio handling
1 parent 223fcae commit a44128f

7 files changed

Lines changed: 73 additions & 12 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/cat/src/cat.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -476,31 +476,25 @@ fn get_input_type(path: &OsString) -> CatResult<InputType> {
476476
/// Writes handle to stdout with no configuration. This allows a
477477
/// simple memory copy.
478478
fn print_fast<R: FdReadable>(handle: &mut InputHandle<R>) -> CatResult<()> {
479-
let stdout = io::stdout();
480-
#[cfg(any(target_os = "linux", target_os = "android"))]
481-
let mut stdout = stdout;
482479
// Try to use the splice() system call for faster writing. If it works, we're done.
483480
#[cfg(any(target_os = "linux", target_os = "android"))]
484-
if !uucore::pipes::splice_unbounded_auto(&handle.reader, &mut stdout)?
481+
if !uucore::pipes::splice_unbounded_auto(&handle.reader, &mut *uucore::stdio::stdout_raw())?
485482
&& !uucore::pipes::might_fuse(&handle.reader)
486483
{
487484
return Ok(());
488485
}
489486

490487
// If we're not on Linux or Android, or the splice() call failed,
491488
// fall back on slower writing.
492-
print_unbuffered(handle, stdout)
489+
print_unbuffered(handle)
493490
}
494491

495492
#[cfg_attr(any(target_os = "linux", target_os = "android"), inline(never))] // splice fast-path does not require this allocation
496-
fn print_unbuffered<R: FdReadable>(
497-
handle: &mut InputHandle<R>,
498-
stdout: io::Stdout,
499-
) -> CatResult<()> {
493+
fn print_unbuffered<R: FdReadable>(handle: &mut InputHandle<R>) -> CatResult<()> {
500494
#[cfg(any(unix, target_os = "wasi"))]
501-
let mut stdout = uucore::io::RawWriter(stdout); // use raw syscall to remove buffering
495+
let mut stdout = uucore::stdio::stdout_raw();
502496
#[cfg(not(any(unix, target_os = "wasi")))]
503-
let mut stdout = stdout.lock();
497+
let mut stdout = io::stdout().lock();
504498
let mut buf = [0; 1024 * 64];
505499
loop {
506500
match handle.reader.read(&mut buf) {

src/uu/dd/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ doctest = false
2222
clap = { workspace = true }
2323
gcd = { workspace = true }
2424
libc = { workspace = true }
25+
rustix = { workspace = true }
2526
uucore = { workspace = true, features = [
2627
"format",
2728
"parser-size",

src/uucore/Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,13 @@ jiff = { workspace = true, optional = true, features = [
3636
"tzdb-concatenated",
3737
] }
3838
rustc-hash = { workspace = true }
39-
rustix = { workspace = true, features = ["fs", "net", "pipe", "process"] }
39+
rustix = { workspace = true, features = [
40+
"fs",
41+
"net",
42+
"pipe",
43+
"process",
44+
"stdio",
45+
] }
4046
time = { workspace = true, optional = true, features = [
4147
"formatting",
4248
"local-offset",

src/uucore/src/lib/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ pub use crate::mods::locale;
3232
pub use crate::mods::os;
3333
pub use crate::mods::panic;
3434
pub use crate::mods::posix;
35+
#[cfg(any(unix, target_os = "wasi"))]
36+
pub use crate::mods::stdio;
3537

3638
// * feature-gated modules
3739
#[cfg(feature = "backup-control")]

src/uucore/src/lib/mods.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@ pub mod locale;
1414
pub mod os;
1515
pub mod panic;
1616
pub mod posix;
17+
#[cfg(any(unix, target_os = "wasi"))]
18+
pub mod stdio;

src/uucore/src/lib/mods/stdio.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// This file is part of the uutils coreutils package.
2+
//
3+
// For the full copyright and license information, please view the LICENSE
4+
// file that was distributed with this source code.
5+
6+
//! Abstractions for raw access to stdin and stdout, without buffering.
7+
8+
use core::ops::{Deref, DerefMut};
9+
use std::{fs::File, mem::ManuallyDrop};
10+
11+
pub struct StdinRaw(ManuallyDrop<File>);
12+
13+
pub struct StdoutRaw(ManuallyDrop<File>);
14+
15+
pub fn stdin_raw() -> StdinRaw {
16+
// SAFETY: We ensure that the file descriptor is never closed by
17+
// wrapping the `File` in `ManuallyDrop`.
18+
let fd = unsafe { rustix::stdio::take_stdin() };
19+
StdinRaw(ManuallyDrop::new(File::from(fd)))
20+
}
21+
22+
pub fn stdout_raw() -> StdoutRaw {
23+
// SAFETY: We ensure that the file descriptor is never closed by
24+
// wrapping the `File` in `ManuallyDrop`.
25+
let fd = unsafe { rustix::stdio::take_stdout() };
26+
StdoutRaw(ManuallyDrop::new(File::from(fd)))
27+
}
28+
29+
impl Deref for StdinRaw {
30+
type Target = File;
31+
32+
fn deref(&self) -> &Self::Target {
33+
&self.0
34+
}
35+
}
36+
37+
impl DerefMut for StdinRaw {
38+
fn deref_mut(&mut self) -> &mut Self::Target {
39+
&mut self.0
40+
}
41+
}
42+
43+
impl Deref for StdoutRaw {
44+
type Target = File;
45+
46+
fn deref(&self) -> &Self::Target {
47+
&self.0
48+
}
49+
}
50+
51+
impl DerefMut for StdoutRaw {
52+
fn deref_mut(&mut self) -> &mut Self::Target {
53+
&mut self.0
54+
}
55+
}

0 commit comments

Comments
 (0)