use std::io;
use std::io::{IoSlice, IoSliceMut};
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
use libc::{self, fcntl, F_GETFL, O_NONBLOCK};
pub(crate) struct Fd {
pub(crate) inner: RawFd,
borrow: bool,
}
impl Fd {
#[cfg(any(
target_os = "windows",
target_os = "macos",
all(target_os = "linux", not(target_env = "ohos")),
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
))]
pub(crate) fn new(value: RawFd) -> io::Result<Self> {
if value < 0 {
return Err(io::Error::last_os_error());
}
Ok(unsafe { Self::new_unchecked(value) })
}
pub(crate) unsafe fn new_unchecked(value: RawFd) -> Self {
Fd::new_unchecked_with_borrow(value, false)
}
pub(crate) unsafe fn new_unchecked_with_borrow(value: RawFd, borrow: bool) -> Self {
Fd {
inner: value,
borrow,
}
}
pub(crate) fn is_nonblocking(&self) -> io::Result<bool> {
unsafe {
let flags = fcntl(self.inner, F_GETFL);
if flags == -1 {
return Err(io::Error::last_os_error());
}
Ok((flags & O_NONBLOCK) != 0)
}
}
#[cfg(target_os = "macos")]
pub(crate) fn set_cloexec(&self) -> io::Result<()> {
unsafe {
let flags = fcntl(self.inner, libc::F_GETFD);
if flags < 0 {
return Err(io::Error::last_os_error());
}
if fcntl(self.inner, libc::F_SETFD, flags | libc::FD_CLOEXEC) < 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
}
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
let mut nonblocking = nonblocking as libc::c_int;
match unsafe { libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &mut nonblocking) } {
0 => Ok(()),
_ => Err(io::Error::last_os_error()),
}
}
#[inline]
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
let fd = self.as_raw_fd();
let amount = unsafe { libc::read(fd, buf.as_mut_ptr() as *mut _, buf.len()) };
if amount < 0 {
return Err(io::Error::last_os_error());
}
Ok(amount as usize)
}
#[inline]
pub fn readv(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
if bufs.len() > max_iov() {
return Err(io::Error::from(io::ErrorKind::InvalidInput));
}
let amount = unsafe {
libc::readv(
self.as_raw_fd(),
bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
bufs.len() as libc::c_int,
)
};
if amount < 0 {
return Err(io::Error::last_os_error());
}
Ok(amount as usize)
}
#[inline]
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
let fd = self.as_raw_fd();
let amount = unsafe { libc::write(fd, buf.as_ptr() as *const _, buf.len()) };
if amount < 0 {
return Err(io::Error::last_os_error());
}
Ok(amount as usize)
}
#[inline]
pub fn writev(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
if bufs.len() > max_iov() {
return Err(io::Error::from(io::ErrorKind::InvalidInput));
}
let amount = unsafe {
libc::writev(
self.as_raw_fd(),
bufs.as_ptr() as *const libc::iovec,
bufs.len() as libc::c_int,
)
};
if amount < 0 {
return Err(io::Error::last_os_error());
}
Ok(amount as usize)
}
}
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
target_os = "openbsd",
target_vendor = "apple",
))]
pub(crate) const fn max_iov() -> usize {
libc::IOV_MAX as usize
}
#[cfg(any(
target_os = "android",
target_os = "emscripten",
target_os = "linux",
target_os = "nto",
))]
pub(crate) const fn max_iov() -> usize {
libc::UIO_MAXIOV as usize
}
impl AsRawFd for Fd {
fn as_raw_fd(&self) -> RawFd {
self.inner
}
}
impl IntoRawFd for Fd {
fn into_raw_fd(mut self) -> RawFd {
let fd = self.inner;
self.inner = -1;
fd
}
}
impl Drop for Fd {
fn drop(&mut self) {
if !self.borrow && self.inner >= 0 {
unsafe { libc::close(self.inner) };
}
}
}