Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .vscode/cspell.dictionaries/workspace.wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ uutils

# * function names
getcwd
setpipe

# * other
getlimits
Expand Down
50 changes: 31 additions & 19 deletions src/uu/cat/src/splice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ use super::{CatResult, FdReadable, InputHandle};
use rustix::io::{read, write};
use std::os::{fd::AsFd, unix::io::AsRawFd};

use uucore::pipes::{pipe, splice, splice_exact};
use uucore::pipes::{MAX_ROOTLESS_PIPE_SIZE, pipe, splice, splice_exact};

const SPLICE_SIZE: usize = 1024 * 128;
const BUF_SIZE: usize = 1024 * 16;

/// This function is called from `write_fast()` on Linux and Android. The
Expand All @@ -24,27 +23,40 @@ pub(super) fn write_fast_using_splice<R: FdReadable, S: AsRawFd + AsFd>(
handle: &InputHandle<R>,
write_fd: &S,
) -> CatResult<bool> {
let (pipe_rd, pipe_wr) = pipe()?;

loop {
match splice(&handle.reader, &pipe_wr, SPLICE_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);
}
}
}
}
Expand Down
11 changes: 8 additions & 3 deletions src/uucore/src/lib/features/pipes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,26 @@
// file that was distributed with this source code.

//! 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.
///
/// Returns two `File` objects: everything written to the second can be read
/// from the first.
/// This is used only for resolving the limitation for splice: one of a input or output should be pipe
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn pipe() -> std::io::Result<(File, File)> {
let (read, write) = rustix::pipe::pipe()?;
// improve performance for splice
let _ = fcntl_setpipe_size(&read, MAX_ROOTLESS_PIPE_SIZE);

Ok((File::from(read), File::from(write)))
}

Expand Down
Loading