use super::{const_buf, mut_buf, IoVec, IoVecMut};
use crate::{Error, Result};
use core::ptr;
pub struct Fd {
pub(crate) fd: i32,
}
impl Fd {
pub fn new(fd: i32) -> Self {
Self { fd }
}
pub fn fd(&self) -> i32 {
self.fd
}
pub fn close(&mut self) {
unsafe { libc::close(self.fd) };
self.fd = -1;
}
pub fn try_read(&self, buf: &mut [u8]) -> Result<usize> {
self.try_read_do(|| unsafe { libc::read(self.fd, mut_buf(buf), buf.len()) })
}
pub(crate) fn try_read_do<F: FnMut() -> isize>(&self, mut f: F) -> Result<usize> {
loop {
let ret = f();
if ret >= 0 {
return Ok(ret as usize);
} else {
let err = Error::last();
match err.errno {
hierr::EINTR => continue,
_ => return Err(err),
}
}
}
}
pub fn try_write(&self, buf: &[u8]) -> Result<usize> {
self.try_write_do(|| unsafe { libc::write(self.fd, const_buf(buf), buf.len()) })
}
pub(crate) fn try_write_do<F: FnMut() -> isize>(&self, mut f: F) -> Result<usize> {
loop {
let ret = f();
if ret >= 0 {
return Ok(ret as usize);
} else {
let err = Error::last();
match err.errno {
hierr::EINTR => continue,
_ => return Err(err),
}
}
}
}
pub fn try_sendfile(&self, in_fd: i32, off: usize, count: usize) -> Result<usize> {
let mut off = off as i64;
self.try_write_do(|| unsafe { libc::sendfile(self.fd, in_fd, &mut off, count) })
}
pub fn try_readv(&self, buf: &[&mut [u8]], off: usize) -> Result<usize> {
self.try_readv_do(buf, off, |piovec, iovcnt| unsafe {
libc::readv(self.fd, piovec, iovcnt)
})
}
pub(crate) fn try_readv_do<F>(&self, buf: &[&mut [u8]], off: usize, mut f: F) -> Result<usize>
where
F: FnMut(*const libc::iovec, i32) -> isize,
{
let iovec = &mut [libc::iovec {
iov_base: ptr::null_mut(),
iov_len: 0,
}; 64];
let mut buf = IoVecMut::new(buf, off);
let mut bytes = 0_usize;
'top: while let Some((piovec, iovcnt, size)) = buf.to_iovec(iovec) {
loop {
let ret = f(piovec, iovcnt);
if ret > 0 {
bytes += ret as usize;
if ret as usize == size {
buf = buf.next_iovec(iovcnt as usize);
} else {
buf = buf.next_bytes(ret as usize);
}
continue 'top;
} else if ret == 0 {
break 'top;
} else {
let errno = Error::last().errno;
match errno {
hierr::EAGAIN => break 'top,
hierr::EINTR => continue,
_ if bytes > 0 => break 'top,
_ => return Err(Error::new(errno)),
}
}
}
}
Ok(bytes)
}
pub fn try_writev(&self, buf: &[&[u8]], off: usize) -> Result<usize> {
self.try_writev_do(buf, off, |piovec, iovcnt| unsafe {
libc::writev(self.fd, piovec, iovcnt)
})
}
pub(crate) fn try_writev_do<F>(&self, buf: &[&[u8]], off: usize, mut f: F) -> Result<usize>
where
F: FnMut(*const libc::iovec, i32) -> isize,
{
let iovec = &mut [libc::iovec {
iov_base: ptr::null_mut(),
iov_len: 0,
}; 64];
let mut buf = IoVec::new(buf, off);
let mut bytes = 0_usize;
'top: while let Some((piovec, iovcnt, size)) = buf.to_iovec(iovec) {
loop {
let ret = f(piovec, iovcnt);
if ret > 0 {
bytes += ret as usize;
if ret as usize == size {
buf = buf.next_iovec(iovcnt as usize);
} else {
buf = buf.next_bytes(ret as usize);
}
continue 'top;
} else {
let errno = Error::last().errno;
match errno {
hierr::EAGAIN => break 'top,
hierr::EINTR => continue,
_ if bytes > 0 => break 'top,
_ => return Err(Error::new(errno)),
}
}
}
}
Ok(bytes)
}
}
impl Clone for Fd {
fn clone(&self) -> Self {
Self {
fd: unsafe { libc::dup(self.fd) },
}
}
}
impl Drop for Fd {
fn drop(&mut self) {
unsafe { libc::close(self.fd) };
}
}