Skip to content

Commit b6922a8

Browse files
committed
dd: splice fast-path for skip= with nun-seekable input
1 parent cd50566 commit b6922a8

File tree

3 files changed

+32
-4
lines changed

3 files changed

+32
-4
lines changed

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: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ use std::ffi::OsString;
3535
use std::fs::Metadata;
3636
use std::fs::{File, OpenOptions};
3737
use std::io::{self, Read, Seek, SeekFrom, Write};
38-
#[cfg(any(target_os = "linux", target_os = "android"))]
3938
use std::os::fd::AsFd;
4039
#[cfg(any(target_os = "linux", target_os = "android"))]
4140
use std::os::unix::fs::OpenOptionsExt;
@@ -183,11 +182,37 @@ impl Num {
183182
/// This is more efficient than `io::copy` with `BufReader` because it reads
184183
/// directly in `buf_size`-sized chunks, matching GNU dd's behavior.
185184
/// 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);
185+
fn read_and_discard<R: Read + AsFd>(reader: &mut R, n: u64, buf_size: usize) -> io::Result<u64> {
189186
let mut total = 0u64;
190187
let mut remaining = n;
188+
#[cfg(any(target_os = "linux", target_os = "android"))]
189+
{
190+
//fast-path
191+
use std::sync::OnceLock;
192+
use uucore::pipes::{MAX_ROOTLESS_PIPE_SIZE, dev_null, splice};
193+
static INIT: OnceLock<Option<File>> = OnceLock::new();
194+
let null = INIT.get_or_init(|| {
195+
if let Some(f) = dev_null() {
196+
let _ = rustix::pipe::fcntl_setpipe_size(&reader, MAX_ROOTLESS_PIPE_SIZE);
197+
Some(f)
198+
} else {
199+
None
200+
}
201+
});
202+
if let Some(null) = null {
203+
while remaining > 0 {
204+
match splice(&reader, &null, remaining as usize) {
205+
Ok(0) => return Ok(total), // no need to allocate buf
206+
Ok(bytes_read) => {
207+
total += bytes_read as u64;
208+
remaining -= bytes_read as u64;
209+
}
210+
Err(_) => break,
211+
}
212+
}
213+
}
214+
}
215+
let mut buf = Vec::with_capacity(buf_size);
191216
while remaining > 0 {
192217
let to_read = cmp::min(remaining, buf_size as u64);
193218
buf.clear();

0 commit comments

Comments
 (0)