1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

uucore/buf-copy: delete redundant functions

This commit is contained in:
Daringcuteseal 2025-01-03 17:42:28 +07:00
parent 2cdf30938d
commit 35a044b532
No known key found for this signature in database
GPG key ID: 173F4A541C0E39D7
3 changed files with 8 additions and 213 deletions

View file

@ -38,7 +38,7 @@ mod tests {
}; };
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
use {nix::unistd, std::os::fd::AsRawFd}; use std::os::fd::AsRawFd;
use std::io::{Read, Write}; use std::io::{Read, Write};
@ -53,67 +53,6 @@ mod tests {
.unwrap() .unwrap()
} }
#[cfg(any(target_os = "linux", target_os = "android"))]
#[test]
fn test_file_is_pipe() {
let temp_file = new_temp_file();
let (pipe_read, pipe_write) = pipes::pipe().unwrap();
assert!(is_pipe(&pipe_read).unwrap());
assert!(is_pipe(&pipe_write).unwrap());
assert!(!is_pipe(&temp_file).unwrap());
}
#[cfg(any(target_os = "linux", target_os = "android"))]
#[test]
fn test_valid_splice_errs() {
use nix::errno::Errno;
use nix::Error;
let err = Error::from(Errno::EINVAL);
assert_eq!(maybe_unsupported(err).unwrap(), (0, true));
let err = Error::from(Errno::ENOSYS);
assert_eq!(maybe_unsupported(err).unwrap(), (0, true));
let err = Error::from(Errno::EBADF);
assert_eq!(maybe_unsupported(err).unwrap(), (0, true));
let err = Error::from(Errno::EPERM);
assert!(maybe_unsupported(err).is_err());
}
#[cfg(any(target_os = "linux", target_os = "android"))]
#[test]
fn test_splice_data_to_pipe() {
let (pipe_read, pipe_write) = pipes::pipe().unwrap();
let data = b"Hello, world!";
let (bytes, _) = splice_data_to_pipe(data, &pipe_write).unwrap();
let mut buf = [0; 1024];
let n = unistd::read(pipe_read.as_raw_fd(), &mut buf).unwrap();
assert_eq!(&buf[..n], data);
assert_eq!(bytes as usize, data.len());
}
#[cfg(any(target_os = "linux", target_os = "android"))]
#[test]
fn test_splice_data_to_file() {
use std::io::{Read, Seek, SeekFrom};
let mut temp_file = new_temp_file();
let (pipe_read, pipe_write) = pipes::pipe().unwrap();
let data = b"Hello, world!";
let (bytes, _) = splice_data_to_fd(data, &pipe_read, &pipe_write, &temp_file).unwrap();
assert_eq!(bytes as usize, data.len());
// We would have been at the end already, so seek again to the start.
temp_file.seek(SeekFrom::Start(0)).unwrap();
let mut buf = Vec::new();
temp_file.read_to_end(&mut buf).unwrap();
assert_eq!(buf, data);
}
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
#[test] #[test]
fn test_copy_exact() { fn test_copy_exact() {
@ -176,32 +115,4 @@ mod tests {
assert_eq!(bytes_copied as usize, data.len()); assert_eq!(bytes_copied as usize, data.len());
assert_eq!(buf, data); assert_eq!(buf, data);
} }
#[cfg(any(target_os = "linux", target_os = "android"))]
#[test]
fn test_splice_write() {
use std::{
io::{Read, Seek, SeekFrom, Write},
thread,
};
let (pipe_read, mut pipe_write) = pipes::pipe().unwrap();
let mut dest_file = new_temp_file();
let data = b"Hello, world!";
let thread = thread::spawn(move || {
pipe_write.write_all(data).unwrap();
});
let (bytes, _) = splice_write(&pipe_read, &dest_file).unwrap();
thread.join().unwrap();
assert!(bytes == data.len() as u64);
// We would have been at the end already, so seek again to the start.
dest_file.seek(SeekFrom::Start(0)).unwrap();
let mut buf = Vec::new();
dest_file.read_to_end(&mut buf).unwrap();
assert_eq!(buf, data);
}
} }

View file

@ -3,20 +3,15 @@
// For the full copyright and license information, please view the LICENSE // For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code. // file that was distributed with this source code.
use nix::sys::stat::fstat; //! Buffer-based copying implementation for Linux and Android.
use nix::{errno::Errno, libc::S_IFIFO};
type Result<T> = std::result::Result<T, Error>;
/// Buffer-based copying utilities for Linux and Android.
use crate::{ use crate::{
error::UResult, error::UResult,
pipes::{pipe, splice, splice_exact, vmsplice}, pipes::{pipe, splice, splice_exact},
}; };
/// Buffer-based copying utilities for unix (excluding Linux). /// Buffer-based copying utilities for unix (excluding Linux).
use std::{ use std::{
fs::File,
io::{Read, Write}, io::{Read, Write},
os::fd::{AsFd, AsRawFd, RawFd}, os::fd::{AsFd, AsRawFd, RawFd},
}; };
@ -51,8 +46,9 @@ impl From<nix::Error> for Error {
/// not use any intermediate user-space buffer. It falls backs to /// not use any intermediate user-space buffer. It falls backs to
/// `std::io::copy` when the call fails and is still recoverable. /// `std::io::copy` when the call fails and is still recoverable.
/// ///
/// # Arguments * `source` - `Read` implementor to copy data from. * `dest` - /// # Arguments
/// `Write` implementor to copy data to. /// * `source` - `Read` implementor to copy data from.
/// * `dest` - `Write` implementor to copy data to.
/// ///
/// # Returns /// # Returns
/// ///
@ -146,117 +142,3 @@ pub(crate) fn copy_exact(
} }
Ok(written) Ok(written)
} }
// The generalization of this function (and other splice_data functions) is not trivial as most
// utilities will just write data finitely. However, `yes`, which is the sole crate using these
// functions as of now, continuously loops the data write. Coupling the `is_pipe` check together
// with the data write logic means that the check has to be done for every single write, which adds
// unnecessary overhead.
//
/// Helper function to determine whether a given handle (such as a file) is a pipe or not. Can be
/// used to determine whether to use the `splice_data_to_pipe` or the `splice_data_to_fd` function.
/// This function is available exclusively to Linux and Android as it is meant to be used at the
/// scope of splice operations.
///
///
/// # Arguments
/// * `out` - path of handle
///
/// # Returns
/// A `bool` indicating whether the given handle is a pipe or not.
#[inline]
pub fn is_pipe<P>(path: &P) -> Result<bool>
where
P: AsRawFd,
{
Ok(fstat(path.as_raw_fd())?.st_mode as nix::libc::mode_t & S_IFIFO != 0)
}
/// Write input `bytes` to a handle using a temporary pipe. A `vmsplice()` call
/// is issued to write to the temporary pipe, which then gets written to the
/// final destination using `splice()`.
///
/// # Arguments * `bytes` - data to be written * `dest` - destination handle
///
/// # Returns When write succeeds, the amount of bytes written is returned as a
/// `u64`. The `bool` indicates if we need to fall back to normal copying or
/// not. `true` means we need to fall back, `false` means we don't have to.
///
/// A `UError` error is returned when the operation is not supported or when an
/// I/O error occurs.
pub fn splice_data_to_fd<T: AsFd>(
bytes: &[u8],
read_pipe: &File,
write_pipe: &File,
dest: &T,
) -> UResult<(u64, bool)> {
let mut n_bytes: u64 = 0;
let mut bytes = bytes;
while !bytes.is_empty() {
let len = match vmsplice(&write_pipe, bytes) {
Ok(n) => n,
Err(e) => return Ok(maybe_unsupported(e)?),
};
if let Err(e) = splice_exact(&read_pipe, dest, len) {
return Ok(maybe_unsupported(e)?);
}
bytes = &bytes[len..];
n_bytes += len as u64;
}
Ok((n_bytes, false))
}
/// Write input `bytes` to a file descriptor. This uses the Linux-specific
/// `vmsplice()` call to write into a file descriptor directly, which only works
/// if the destination is a pipe.
///
/// # Arguments
/// * `bytes` - data to be written
/// * `dest` - destination handle
///
/// # Returns
/// When write succeeds, the amount of bytes written is returned as a
/// `u64`. The `bool` indicates if we need to fall back to normal copying or
/// not. `true` means we need to fall back, `false` means we don't have to.
///
/// A `UError` error is returned when the operation is not supported or when an
/// I/O error occurs.
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn splice_data_to_pipe<T>(bytes: &[u8], dest: &T) -> UResult<(u64, bool)>
where
T: AsRawFd + AsFd,
{
let mut n_bytes: u64 = 0;
let mut bytes = bytes;
while !bytes.is_empty() {
let len = match vmsplice(dest, bytes) {
Ok(n) => n,
// The maybe_unsupported call below may emit an error, when the
// error is considered as unrecoverable error (ones that won't make
// us fall back to other method)
Err(e) => return Ok(maybe_unsupported(e)?),
};
bytes = &bytes[len..];
n_bytes += len as u64;
}
Ok((n_bytes, false))
}
/// Several error values from `nix::Error` (`EINVAL`, `ENOSYS`, and `EBADF`) get
/// treated as errors indicating that the `splice` call is not available, i.e we
/// can still recover from the error. Thus, return the final result of the call
/// as `Result` and indicate that we have to fall back using other write method.
///
/// # Arguments
/// * `error` - the `nix::Error` received
///
/// # Returns
/// Result with tuple containing a `u64` `0` indicating that no data had been
/// written and a `true` indicating we have to fall back, if error is still
/// recoverable. Returns an `Error` implementing `UError` otherwise.
pub(crate) fn maybe_unsupported(error: nix::Error) -> Result<(u64, bool)> {
match error {
Errno::EINVAL | Errno::ENOSYS | Errno::EBADF => Ok((0, true)),
_ => Err(error.into()),
}
}

View file

@ -2,6 +2,8 @@
// //
// For the full copyright and license information, please view the LICENSE // For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code. // file that was distributed with this source code.
//!
//! Buffer-based copying implementation for other platforms.
use std::io::{Read, Write}; use std::io::{Read, Write};