use std::io;
use std::os::fd::AsFd;
use std::os::fd::AsRawFd;
use std::os::fd::BorrowedFd;
use std::os::unix::net::UnixStream;
use tokio::io::Interest;
use tokio::net::tcp::OwnedWriteHalf;
use crate::file_region::FileRegion;
const MAX_SENDFILE_COUNT: usize = 0x7fff_f000;
pub(crate) fn is_eligible(region: &FileRegion) -> bool {
region.is_regular_file()
&& libc::off_t::try_from(region.offset()).is_ok()
&& region
.offset()
.checked_add(region.len())
.is_some_and(|end| libc::off_t::try_from(end).is_ok())
}
pub(crate) fn probe_file_region(region: &FileRegion) -> io::Result<bool> {
if let Some(supported) = region.cached_sendfile_support() {
return Ok(supported);
}
let (writer, _reader) = UnixStream::pair()?;
let mut offset = libc::off_t::try_from(region.offset())
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "file-region offset exceeds off_t"))?;
match try_sendfile(writer.as_fd(), region.lease().file().as_fd(), &mut offset, 1) {
Ok(1) => {
region.cache_sendfile_support(true);
Ok(true)
}
Ok(_) => Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"sendfile preflight could not read the first leased byte",
)),
Err(error) if is_unsupported(&error) => {
region.cache_sendfile_support(false);
Ok(false)
}
Err(error) => Err(error),
}
}
pub(crate) async fn send_file_region(writer: &mut OwnedWriteHalf, region: &FileRegion) -> io::Result<u64> {
let mut offset = libc::off_t::try_from(region.offset())
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "file-region offset exceeds off_t"))?;
let mut remaining = region.len();
let mut sent = 0_u64;
while remaining != 0 {
let count = usize::try_from(remaining.min(MAX_SENDFILE_COUNT as u64))
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "sendfile count exceeds usize"))?;
let tcp_stream = writer.as_ref();
let result = tcp_stream
.async_io(Interest::WRITABLE, || {
try_sendfile(tcp_stream.as_fd(), region.lease().file().as_fd(), &mut offset, count)
})
.await;
match result {
Ok(0) => {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"sendfile reached EOF before the leased file region ended",
));
}
Ok(written) => {
remaining -= written as u64;
sent += written as u64;
}
Err(error) if error.kind() == io::ErrorKind::Interrupted => {}
Err(error) => return Err(error),
}
}
Ok(sent)
}
fn is_unsupported(error: &io::Error) -> bool {
error.raw_os_error().is_some_and(|code| {
code == libc::EINVAL || code == libc::ENOSYS || code == libc::EOPNOTSUPP || code == libc::EXDEV
})
}
fn try_sendfile(
out_fd: BorrowedFd<'_>,
in_fd: BorrowedFd<'_>,
offset: &mut libc::off_t,
count: usize,
) -> io::Result<usize> {
let written = unsafe { libc::sendfile(out_fd.as_raw_fd(), in_fd.as_raw_fd(), offset, count) };
if written < 0 {
Err(io::Error::last_os_error())
} else {
Ok(written as usize)
}
}