From b38eef8d507769e50700af9cb90b4cd3fd435fa7 Mon Sep 17 00:00:00 2001 From: oech3 <79379754+oech3@users.noreply.github.com> Date: Mon, 18 May 2026 15:35:31 +0900 Subject: [PATCH] cp: omit pipe() if input is pipe --- src/uu/cat/src/cat.rs | 14 ++++----- src/uu/cat/src/splice.rs | 29 ------------------- src/uu/tail/src/tail.rs | 2 +- src/uucore/src/lib/features/buf_copy/linux.rs | 23 +++++---------- src/uucore/src/lib/features/pipes.rs | 16 ++++++++++ 5 files changed, 30 insertions(+), 54 deletions(-) delete mode 100644 src/uu/cat/src/splice.rs diff --git a/src/uu/cat/src/cat.rs b/src/uu/cat/src/cat.rs index 66bd69dfc85..a2ddcc22f03 100644 --- a/src/uu/cat/src/cat.rs +++ b/src/uu/cat/src/cat.rs @@ -23,10 +23,6 @@ use uucore::error::{UResult, strip_errno}; use uucore::translate; use uucore::{fast_inc::fast_inc_one, format_usage}; -/// Linux splice support -#[cfg(any(target_os = "linux", target_os = "android"))] -mod splice; - // Allocate 32 digits for the line number. // An estimate is that we can print about 1e8 lines/seconds, so 32 digits // would be enough for billions of universe lifetimes. @@ -483,14 +479,14 @@ fn print_fast(handle: &mut InputHandle) -> CatResult<()> { let stdout = io::stdout(); #[cfg(any(target_os = "linux", target_os = "android"))] let mut stdout = stdout; + // Try to use the splice() system call for faster writing. If it works, we're done. #[cfg(any(target_os = "linux", target_os = "android"))] + if !uucore::pipes::splice_unbounded_auto(&handle.reader, &mut stdout)? + && !uucore::pipes::might_fuse(&handle.reader) { - // If we're on Linux or Android, try to use the splice() system call - // for faster writing. If it works, we're done. - if !splice::write_fast_using_splice(handle, &mut stdout)? { - return Ok(()); - } + return Ok(()); } + // If we're not on Linux or Android, or the splice() call failed, // fall back on slower writing. print_unbuffered(handle, stdout) diff --git a/src/uu/cat/src/splice.rs b/src/uu/cat/src/splice.rs deleted file mode 100644 index 5b4f25dbf67..00000000000 --- a/src/uu/cat/src/splice.rs +++ /dev/null @@ -1,29 +0,0 @@ -// This file is part of the uutils coreutils package. -// -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. -use super::{CatResult, FdReadable, InputHandle}; - -use std::os::fd::AsFd; - -use uucore::pipes::{MAX_ROOTLESS_PIPE_SIZE, might_fuse, splice}; - -/// This function is called from `write_fast()` on Linux and Android. The -/// function `splice()` is used to move data between two file descriptors -/// without copying between kernel and user spaces. This results in a large -/// speedup. -/// -/// The `bool` in the result value indicates if we need to fall back to normal -/// copying or not. False means we don't have to. -#[inline] -pub(super) fn write_fast_using_splice( - handle: &InputHandle, - write_fd: &mut S, -) -> CatResult { - let res = match splice(&handle.reader, &write_fd, MAX_ROOTLESS_PIPE_SIZE) { - Ok(_) => uucore::pipes::splice_unbounded(&handle.reader, write_fd)?, - // both of in/output are not pipe - _ => uucore::pipes::splice_unbounded_broker(&handle.reader, write_fd)?, - }; - Ok(res || might_fuse(&handle.reader)) -} diff --git a/src/uu/tail/src/tail.rs b/src/uu/tail/src/tail.rs index 72f6e57c0e4..334559f3a6c 100644 --- a/src/uu/tail/src/tail.rs +++ b/src/uu/tail/src/tail.rs @@ -591,7 +591,7 @@ fn print_target_section< } else { // zero-copy fast-path #[cfg(any(target_os = "linux", target_os = "android"))] - if uucore::pipes::splice_unbounded_broker(file, &mut stdout)? { + if uucore::pipes::splice_unbounded_auto(file, &mut stdout)? { io::copy(file, &mut stdout)?; } #[cfg(not(any(target_os = "linux", target_os = "android")))] diff --git a/src/uucore/src/lib/features/buf_copy/linux.rs b/src/uucore/src/lib/features/buf_copy/linux.rs index 0af494754b5..6adb47ad4f9 100644 --- a/src/uucore/src/lib/features/buf_copy/linux.rs +++ b/src/uucore/src/lib/features/buf_copy/linux.rs @@ -39,21 +39,14 @@ where R: Read + AsFd + AsRawFd, S: Write + AsFd + AsRawFd, { - // If we're on Linux or Android, try to use the splice() system call - // for faster writing. If it works, we're done. - // todo: bypass broker pipe this if input or output is pipe. We use this mostly for stream. - if !crate::pipes::splice_unbounded_broker(src, dest)? { - return Ok(()); + // try to use the splice() system call + // for faster writing. If it works, we're done + if crate::pipes::splice_unbounded_auto(&src, dest)? { + std::io::copy(src, dest)?; + // todo: Do not mix writing by raw syscall and std's buffered write, + // or order of output would be wrong when this was called multiple times + // and splice_unbounded_auto sent content partially. flush works as an workaround. + dest.flush()?; } - - // If the splice() call failed, fall back on slower writing. - std::io::copy(src, dest)?; - - // If the splice() call failed and there has been some data written to - // stdout via while loop above AND there will be second splice() call - // that will succeed, data pushed through splice will be output before - // the data buffered in stdout.lock. Therefore additional explicit flush - // is required here. - dest.flush()?; Ok(()) } diff --git a/src/uucore/src/lib/features/pipes.rs b/src/uucore/src/lib/features/pipes.rs index f4411edbcae..652e57b7a0b 100644 --- a/src/uucore/src/lib/features/pipes.rs +++ b/src/uucore/src/lib/features/pipes.rs @@ -154,6 +154,22 @@ where } } +/// try splice_unbounded 1st and splice_unbounded_broker if both of in/output are not pipe +#[inline] +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn splice_unbounded_auto(source: &R, dest: &mut S) -> std::io::Result +where + R: Read + AsFd, + S: AsFd, +{ + // use splice to check that input or output is pipe which is efficient + let fallback = match splice(&source, dest, MAX_ROOTLESS_PIPE_SIZE) { + Ok(_) => splice_unbounded(source, dest)?, + _ => splice_unbounded_broker(source, dest)?, + }; + Ok(fallback) +} + /// splice `n` bytes with safe read/write fallback /// return actually sent bytes #[inline]