cat: replace copy_exact() with write_all

This commit is contained in:
oech3
2026-04-14 22:57:02 +02:00
committed by Sylvestre Ledru
parent 6f939edb72
commit 970688834a
2 changed files with 10 additions and 29 deletions
+3 -1
View File
@@ -479,10 +479,12 @@ fn get_input_type(path: &OsString) -> CatResult<InputType> {
fn write_fast<R: FdReadable>(handle: &mut InputHandle<R>) -> CatResult<()> {
let stdout = io::stdout();
#[cfg(any(target_os = "linux", target_os = "android"))]
let mut stdout = stdout;
#[cfg(any(target_os = "linux", target_os = "android"))]
{
// 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, &stdout)? {
if !splice::write_fast_using_splice(handle, &mut stdout)? {
return Ok(());
}
}
+7 -28
View File
@@ -4,13 +4,11 @@
// file that was distributed with this source code.
use super::{CatResult, FdReadable, InputHandle};
use rustix::io::{read, write};
use std::io::{Read, Write};
use std::os::{fd::AsFd, unix::io::AsRawFd};
use uucore::pipes::{MAX_ROOTLESS_PIPE_SIZE, might_fuse, pipe, splice, splice_exact};
const BUF_SIZE: usize = 1024 * 16;
/// 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
@@ -19,14 +17,14 @@ const BUF_SIZE: usize = 1024 * 16;
/// 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<R: FdReadable, S: AsRawFd + AsFd>(
pub(super) fn write_fast_using_splice<R: FdReadable, S: AsRawFd + AsFd + Write>(
handle: &InputHandle<R>,
write_fd: &S,
write_fd: &mut S,
) -> CatResult<bool> {
if splice(&handle.reader, &write_fd, MAX_ROOTLESS_PIPE_SIZE).is_ok() {
// fcntl improves throughput
// todo: avoid fcntl overhead for small input, but don't fcntl inside of the loop
let _ = rustix::pipe::fcntl_setpipe_size(write_fd, MAX_ROOTLESS_PIPE_SIZE);
let _ = rustix::pipe::fcntl_setpipe_size(&mut *write_fd, MAX_ROOTLESS_PIPE_SIZE);
loop {
match splice(&handle.reader, &write_fd, MAX_ROOTLESS_PIPE_SIZE) {
Ok(1..) => {}
@@ -46,7 +44,9 @@ pub(super) fn write_fast_using_splice<R: FdReadable, S: AsRawFd + AsFd>(
// 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)?;
let mut drain = Vec::with_capacity(n); // bounded by pipe size
pipe_rd.take(n as u64).read_to_end(&mut drain)?;
write_fd.write_all(&drain)?;
return Ok(true);
}
}
@@ -57,24 +57,3 @@ pub(super) fn write_fast_using_splice<R: FdReadable, S: AsRawFd + AsFd>(
Ok(true)
}
}
/// Move exactly `num_bytes` bytes from `read_fd` to `write_fd`.
///
/// Panics if not enough bytes can be read.
fn copy_exact(read_fd: &impl AsFd, write_fd: &impl AsFd, num_bytes: usize) -> std::io::Result<()> {
let mut left = num_bytes;
let mut buf = [0; BUF_SIZE];
while left > 0 {
let n = read(read_fd, &mut buf)?;
assert_ne!(n, 0, "unexpected end of pipe");
let mut written = 0;
while written < n {
match write(write_fd, &buf[written..n])? {
0 => unreachable!("fd should be writable"),
w => written += w,
}
}
left -= n;
}
Ok(())
}