Skip to main content

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