diff options
Diffstat (limited to 'ext/flash/sendfile.rs')
-rw-r--r-- | ext/flash/sendfile.rs | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/ext/flash/sendfile.rs b/ext/flash/sendfile.rs new file mode 100644 index 000000000..4efa7bc35 --- /dev/null +++ b/ext/flash/sendfile.rs @@ -0,0 +1,81 @@ +// Forked from https://github.com/Thomasdezeeuw/sendfile/blob/024f82cd4dede9048392a5bd6d8afcd4d5aa83d5/src/lib.rs +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +use std::future::Future; +use std::io; +use std::os::unix::io::RawFd; +use std::pin::Pin; +use std::task::{self, Poll}; + +pub struct SendFile { + pub io: (RawFd, RawFd), + pub written: usize, +} + +impl SendFile { + #[inline] + pub fn try_send(&mut self) -> Result<usize, std::io::Error> { + #[cfg(target_os = "linux")] + { + // This is the maximum the Linux kernel will write in a single call. + let count = 0x7ffff000; + let mut offset = self.written as libc::off_t; + + // SAFETY: call to libc::sendfile() + let res = + unsafe { libc::sendfile(self.io.1, self.io.0, &mut offset, count) }; + if res == -1 { + Err(io::Error::last_os_error()) + } else { + self.written = offset as usize; + Ok(res as usize) + } + } + #[cfg(target_os = "macos")] + { + // Send all bytes. + let mut length = 0; + // On macOS `length` is value-result parameter. It determines the number + // of bytes to write and returns the number of bytes written also in + // case of `EAGAIN` errors. + // SAFETY: call to libc::sendfile() + let res = unsafe { + libc::sendfile( + self.io.0, + self.io.1, + self.written as libc::off_t, + &mut length, + std::ptr::null_mut(), + 0, + ) + }; + self.written += length as usize; + if res == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(length as usize) + } + } + } +} + +impl Future for SendFile { + type Output = Result<(), std::io::Error>; + + fn poll( + mut self: Pin<&mut Self>, + _: &mut task::Context<'_>, + ) -> Poll<Self::Output> { + loop { + match self.try_send() { + Ok(0) => break Poll::Ready(Ok(())), + Ok(_) => continue, + Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { + break Poll::Pending + } + Err(ref err) if err.kind() == io::ErrorKind::Interrupted => continue, // Try again. + Err(err) => break Poll::Ready(Err(err)), + } + } + } +} |