mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
cat: avoid pipe() if stdout is pipe
This commit is contained in:
+1
-1
@@ -367,6 +367,7 @@ uutils
|
||||
|
||||
# * function names
|
||||
getcwd
|
||||
setpipe
|
||||
|
||||
# * other
|
||||
getlimits
|
||||
@@ -374,7 +375,6 @@ weblate
|
||||
algs
|
||||
wasm
|
||||
wasip
|
||||
SETPIPE
|
||||
|
||||
# * stty terminal flags
|
||||
brkint
|
||||
|
||||
+30
-23
@@ -23,33 +23,40 @@ pub(super) fn write_fast_using_splice<R: FdReadable, S: AsRawFd + AsFd>(
|
||||
handle: &InputHandle<R>,
|
||||
write_fd: &S,
|
||||
) -> CatResult<bool> {
|
||||
use nix::fcntl::{FcntlArg, fcntl};
|
||||
let (pipe_rd, pipe_wr) = pipe()?;
|
||||
// improve performance
|
||||
let _ = fcntl(
|
||||
write_fd,
|
||||
FcntlArg::F_SETPIPE_SZ(MAX_ROOTLESS_PIPE_SIZE as i32),
|
||||
);
|
||||
|
||||
loop {
|
||||
match splice(&handle.reader, &pipe_wr, MAX_ROOTLESS_PIPE_SIZE) {
|
||||
Ok(n) => {
|
||||
if n == 0 {
|
||||
return Ok(false);
|
||||
const FIRST_PIPE_SIZE: usize = 64 * 1024;
|
||||
if splice(&handle.reader, &write_fd, FIRST_PIPE_SIZE).is_ok() {
|
||||
// fcntl improves performance for large file which is large overhead for small files
|
||||
let _ = rustix::pipe::fcntl_setpipe_size(write_fd, MAX_ROOTLESS_PIPE_SIZE);
|
||||
loop {
|
||||
match splice(&handle.reader, &write_fd, MAX_ROOTLESS_PIPE_SIZE) {
|
||||
Ok(1..) => {}
|
||||
Ok(0) => return Ok(false),
|
||||
Err(_) => return Ok(true),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// output is not pipe. Needs broker to use splice() which is high cost for small files
|
||||
let (pipe_rd, pipe_wr) = pipe()?;
|
||||
loop {
|
||||
match splice(&handle.reader, &pipe_wr, MAX_ROOTLESS_PIPE_SIZE) {
|
||||
Ok(n) => {
|
||||
if n == 0 {
|
||||
return Ok(false);
|
||||
}
|
||||
if splice_exact(&pipe_rd, write_fd, n).is_err() {
|
||||
// If the first splice manages to copy to the intermediate
|
||||
// pipe, but the second splice to stdout fails for some reason
|
||||
// we can recover by copying the data that we have from the
|
||||
// intermediate pipe to stdout using normal read/write. Then
|
||||
// we tell the caller to fall back.
|
||||
copy_exact(&pipe_rd, write_fd, n)?;
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
if splice_exact(&pipe_rd, write_fd, n).is_err() {
|
||||
// If the first splice manages to copy to the intermediate
|
||||
// pipe, but the second splice to stdout fails for some reason
|
||||
// we can recover by copying the data that we have from the
|
||||
// intermediate pipe to stdout using normal read/write. Then
|
||||
// we tell the caller to fall back.
|
||||
copy_exact(&pipe_rd, write_fd, n)?;
|
||||
Err(_) => {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
|
||||
//! Thin zero-copy-related wrappers around functions from the `rustix` crate.
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use rustix::pipe::{SpliceFlags, fcntl_setpipe_size};
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use std::fs::File;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use std::os::fd::AsFd;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use rustix::pipe::SpliceFlags;
|
||||
pub const MAX_ROOTLESS_PIPE_SIZE: usize = 1024 * 1024;
|
||||
|
||||
/// A wrapper around [`rustix::pipe::pipe`] that ensures the pipe is cleaned up.
|
||||
@@ -22,7 +22,7 @@ pub const MAX_ROOTLESS_PIPE_SIZE: usize = 1024 * 1024;
|
||||
pub fn pipe() -> std::io::Result<(File, File)> {
|
||||
let (read, write) = rustix::pipe::pipe()?;
|
||||
// improve performance for splice
|
||||
let _ = fcntl(&read, FcntlArg::F_SETPIPE_SZ(MAX_ROOTLESS_PIPE_SIZE as i32));
|
||||
let _ = fcntl_setpipe_size(&read, MAX_ROOTLESS_PIPE_SIZE);
|
||||
|
||||
Ok((File::from(read), File::from(write)))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user