compio_fs/pipe/
splice.rs

1//! Linux-specific splice operations.
2
3use std::{
4    future::{Future, IntoFuture},
5    io::{self},
6    os::fd::AsFd,
7    pin::Pin,
8    task::{Context, Poll, ready},
9};
10
11use compio_driver::{SharedFd, ToSharedFd, op::Splice as SpliceOp};
12use compio_runtime::Submit;
13
14/// Splice data between two file descriptors without copying through userspace.
15///
16/// At least one of `fd_in` or `fd_out` must be a pipe. Returns a builder that
17/// can be configured with optional offsets and flags before awaiting.
18///
19/// # Example
20///
21/// ```ignore
22/// const HELLO: &[u8] = b"hello world";
23///
24/// let mut tempfile = tempfile();
25/// tempfile.write_all(HELLO).unwrap();
26///
27/// let file = File::open(tempfile.path()).await.unwrap();
28/// let (mut rx, tx) = anonymous().unwrap();
29///
30/// let n = splice(&file, &tx, HELLO.len()).offset_in(0).await.unwrap();
31/// assert_eq!(n, HELLO.len());
32///
33/// drop(tx);
34/// let (_, buf) = rx
35///    .read_exact(Vec::with_capacity(HELLO.len()))
36///    .await
37///    .unwrap();
38/// assert_eq!(&buf, HELLO);
39/// ```
40pub fn splice<I: AsFd + 'static, O: AsFd + 'static>(
41    fd_in: &impl ToSharedFd<I>,
42    fd_out: &impl ToSharedFd<O>,
43    len: usize,
44) -> Splice<I, O> {
45    Splice {
46        fd_in: fd_in.to_shared_fd(),
47        fd_out: fd_out.to_shared_fd(),
48        len,
49        offset_in: -1,
50        offset_out: -1,
51        flags: 0,
52    }
53}
54
55/// Builder for splice operations.
56pub struct Splice<I, O> {
57    fd_in: SharedFd<I>,
58    fd_out: SharedFd<O>,
59    len: usize,
60    offset_in: i64,
61    offset_out: i64,
62    flags: u32,
63}
64
65impl<I, O> Splice<I, O> {
66    /// Set the offset to read from.
67    pub fn offset_in(mut self, offset: i64) -> Self {
68        self.offset_in = offset;
69        self
70    }
71
72    /// Set the offset to write to.
73    pub fn offset_out(mut self, offset: i64) -> Self {
74        self.offset_out = offset;
75        self
76    }
77
78    /// Set splice flags.
79    pub fn flags(mut self, flags: u32) -> Self {
80        self.flags = flags;
81        self
82    }
83}
84
85impl<I: AsFd + 'static, O: AsFd + 'static> IntoFuture for Splice<I, O> {
86    type IntoFuture = SpliceFuture<I, O>;
87    type Output = io::Result<usize>;
88
89    fn into_future(self) -> Self::IntoFuture {
90        let inner = compio_runtime::submit(SpliceOp::new(
91            self.fd_in,
92            self.offset_in,
93            self.fd_out,
94            self.offset_out,
95            self.len,
96            self.flags,
97        ));
98        SpliceFuture { inner }
99    }
100}
101
102pin_project_lite::pin_project! {
103    /// Future for splice operations.
104    pub struct SpliceFuture<I, O> where I : AsFd, O: AsFd {
105        #[pin]
106        inner: Submit<SpliceOp<SharedFd<I>, SharedFd<O>>>,
107    }
108}
109
110impl<I: AsFd + 'static, O: AsFd + 'static> Future for SpliceFuture<I, O> {
111    type Output = io::Result<usize>;
112
113    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
114        let res = ready!(self.project().inner.poll(cx));
115        Poll::Ready(res.0)
116    }
117}