Skip to content

Commit 29a7598

Browse files
committed
pipes.rs: dedup code by including fallback to splice_exact
1 parent 29fdaa6 commit 29a7598

1 file changed

Lines changed: 32 additions & 34 deletions

File tree

src/uucore/src/lib/features/pipes.rs

Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -43,27 +43,41 @@ pub fn pipe<const SIZE_REQUIRED: bool>(s: usize) -> std::io::Result<(PipeReader,
4343
///
4444
/// At least one of `source` and `target` must be some sort of pipe.
4545
/// To get around this requirement, consider splicing from your source into
46-
/// a [`pipe`] and then from the pipe into your target (with `splice_exact`):
46+
/// a [`pipe`] and then from the pipe into your target (with `drain_pipe`):
4747
/// this is still very efficient.
4848
#[inline]
4949
pub fn splice(source: &impl AsFd, target: &impl AsFd, len: usize) -> rustix::io::Result<usize> {
5050
rustix::pipe::splice(source, None, target, None, len, SpliceFlags::empty())
5151
}
5252

53-
/// Try to splice `len` bytes from `source` into `target`.
53+
/// splice `len` bytes from `pipe` into `dest`.
5454
///
55-
/// Note that this splice_exact does not provide bytes sent when it failed.
56-
/// In the case failed relaying splice via pipe, all content of the pipe
57-
/// should be drained by `read` to keep bytes sent accurate.
55+
/// returns Ok(Ok(())) if splice succeed
56+
/// returns Ok(Err(())) if splice failed, but read/write fallback succeed
57+
/// returns Err(e) if read/write fallback failed too
58+
///
59+
/// Thus, ?.is_err() returns serious error at early stage and checks that splice failed
5860
#[inline]
59-
pub fn splice_exact(source: &impl AsFd, target: &impl AsFd, len: usize) -> rustix::io::Result<()> {
60-
let mut left = len;
61-
while left > 0 {
62-
let written = splice(source, target, left)?;
63-
debug_assert_ne!(written, 0, "unexpected end of data");
64-
left -= written;
61+
pub fn drain_pipe(
62+
pipe: &PipeReader,
63+
dest: &impl AsFd,
64+
len: usize,
65+
) -> std::io::Result<Result<(), ()>> {
66+
debug_assert!(len <= MAX_ROOTLESS_PIPE_SIZE, "unexpected RAM usage");
67+
let mut remaining = len;
68+
while remaining > 0 {
69+
if let Ok(s) = splice(pipe, dest, remaining) {
70+
remaining -= s;
71+
} else {
72+
// read/write fallback
73+
// use read_to_end to make pipe empty for the case write failed
74+
let mut drain = Vec::with_capacity(remaining);
75+
pipe.take(remaining as u64).read_to_end(&mut drain)?;
76+
RawWriter(&dest).write_all(&drain)?;
77+
return Ok(Err(()));
78+
}
6579
}
66-
Ok(())
80+
Ok(Ok(()))
6781
}
6882

6983
/// check that source is FUSE
@@ -119,17 +133,7 @@ pub fn splice_unbounded_broker(
119133
match splice(&source, &pipe_wr, MAX_ROOTLESS_PIPE_SIZE) {
120134
Ok(0) => return Ok(Ok(())),
121135
Ok(n) => {
122-
if splice_exact(&pipe_rd, dest, n).is_err() {
123-
// If the first splice manages to copy to the intermediate
124-
// pipe, but the second splice to stdout fails for some reason
125-
// we can recover by copying the data that we have from the
126-
// intermediate pipe to stdout using unbuffered read/write. Then
127-
// we tell the caller to fall back.
128-
// use read_to_end to drain pipe for the case write failed
129-
debug_assert!(n <= MAX_ROOTLESS_PIPE_SIZE, "unexpected RAM usage");
130-
let mut drain = Vec::with_capacity(n);
131-
pipe_rd.take(n as u64).read_to_end(&mut drain)?;
132-
RawWriter(&dest).write_all(&drain)?;
136+
if drain_pipe(pipe_rd, dest, n)?.is_err() {
133137
return Ok(Err(()));
134138
}
135139
}
@@ -197,19 +201,13 @@ pub fn send_n_bytes(input: impl AsFd, target: impl AsFd, n: u64) -> std::io::Res
197201
Ok(s) => {
198202
n -= s as u64;
199203
bytes_written += s as u64;
200-
if splice_exact(&broker_r, &target, s).is_ok() {
201-
if n == 0 {
202-
// avoid unnecessary splice for small input
203-
break false;
204-
}
205-
} else {
206-
debug_assert!(s <= MAX_ROOTLESS_PIPE_SIZE, "unexpected RAM usage");
207-
// use read_to_end to drain pipe at this fallback for the case write failed
208-
let mut drain = Vec::with_capacity(s);
209-
broker_r.take(s as u64).read_to_end(&mut drain)?;
210-
RawWriter(&target).write_all(&drain)?;
204+
if drain_pipe(broker_r, &target, s)?.is_err() {
211205
break true;
212206
}
207+
if n == 0 {
208+
// avoid unnecessary splice for small input
209+
break false;
210+
}
213211
}
214212
_ => break true,
215213
}

0 commit comments

Comments
 (0)