Skip to content

Commit 27d5cf0

Browse files
committed
dd: splice fast-path for skip= with nun-seekable input
1 parent e406cef commit 27d5cf0

3 files changed

Lines changed: 38 additions & 3 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/uu/dd/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ fluent = { workspace = true }
3434

3535
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
3636
nix = { workspace = true, features = ["fs", "signal"] }
37+
rustix = { workspace = true, features = ["pipe"] }
38+
uucore = { workspace = true, features = ["pipes"] }
3739

3840
[[bin]]
3941
name = "dd"

src/uu/dd/src/dd.rs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,16 +178,48 @@ impl Num {
178178
}
179179
}
180180

181+
// cannot put cfg in where... (https://github.com/rust-lang/rust/issues/115590)
182+
#[cfg(any(target_os = "linux", target_os = "android"))]
183+
trait DdReader: Read + AsFd {}
184+
#[cfg(not(any(target_os = "linux", target_os = "android")))]
185+
trait DdReader: Read {}
186+
#[cfg(any(target_os = "linux", target_os = "android"))]
187+
impl<T: Read + AsFd> DdReader for T {}
188+
#[cfg(not(any(target_os = "linux", target_os = "android")))]
189+
impl<T: Read> DdReader for T {}
181190
/// Read and discard `n` bytes from `reader` using a buffer of size `buf_size`.
182191
///
183192
/// This is more efficient than `io::copy` with `BufReader` because it reads
184193
/// directly in `buf_size`-sized chunks, matching GNU dd's behavior.
185194
/// Returns the total number of bytes actually read.
186-
fn read_and_discard<R: Read>(reader: &mut R, n: u64, buf_size: usize) -> io::Result<u64> {
187-
// todo: consider splice()ing to /dev/null on Linux
188-
let mut buf = Vec::with_capacity(buf_size);
195+
fn read_and_discard<R: DdReader>(reader: &mut R, n: u64, buf_size: usize) -> io::Result<u64> {
189196
let mut total = 0u64;
190197
let mut remaining = n;
198+
#[cfg(any(target_os = "linux", target_os = "android"))]
199+
{
200+
//fast-path
201+
use std::sync::OnceLock;
202+
use uucore::pipes::{MAX_ROOTLESS_PIPE_SIZE, dev_null, splice};
203+
static INIT: OnceLock<Option<File>> = OnceLock::new();
204+
let null = INIT.get_or_init(|| {
205+
dev_null().inspect(|_| {
206+
let _ = rustix::pipe::fcntl_setpipe_size(&reader, MAX_ROOTLESS_PIPE_SIZE);
207+
})
208+
});
209+
if let Some(null) = null {
210+
while remaining > 0 {
211+
match splice(&reader, &null, remaining as usize) {
212+
Ok(0) => return Ok(total), // no need to allocate buf
213+
Ok(bytes_read) => {
214+
total += bytes_read as u64;
215+
remaining -= bytes_read as u64;
216+
}
217+
Err(_) => break,
218+
}
219+
}
220+
}
221+
}
222+
let mut buf = Vec::with_capacity(buf_size);
191223
while remaining > 0 {
192224
let to_read = cmp::min(remaining, buf_size as u64);
193225
buf.clear();

0 commit comments

Comments
 (0)