use super::{const_buf, mut_buf, Fd, IoVec, IoVecMut};
use crate::runtime::{FdDel, FdWait};
use crate::{
event::{POLLET, POLLIN, POLLOUT},
Error, Result,
};
use core::ops::Deref;
use core::ptr;
#[repr(C)]
pub struct AioFd<'a> {
fd: &'a Fd,
index: usize,
}
unsafe impl Send for AioFd<'_> {}
impl Deref for AioFd<'_> {
type Target = Fd;
fn deref(&self) -> &Self::Target {
self.fd
}
}
impl<'a> AioFd<'a> {
pub fn new(fd: &'a Fd) -> Self {
Self { fd, index: 0 }
}
pub fn new_with(fd: &'a Fd, cookie: usize) -> Self {
Self { fd, index: cookie }
}
pub fn wait_cookie(&self) -> usize {
self.index
}
}
impl AioFd<'_> {
#[deprecated(note = "should instead use wait_readable or wait_writable")]
pub async fn wait(&mut self, events: u32) -> Result<()> {
FdWait::new(self.fd(), &mut self.index, events | POLLET).await
}
pub async fn wait_readable(&mut self) -> Result<()> {
FdWait::new(self.fd(), &mut self.index, POLLIN | POLLET).await
}
pub async fn wait_writable(&mut self) -> Result<()> {
FdWait::new(self.fd(), &mut self.index, POLLOUT | POLLET).await
}
pub async fn wait_none(&mut self) {
FdDel::new(self.fd(), self.index).await
}
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
self.do_read(buf, false, |fd, buf| unsafe {
libc::read(fd, mut_buf(buf), buf.len())
})
.await
}
pub async fn read_all(&mut self, buf: &mut [u8]) -> Result<usize> {
self.do_read(buf, true, |fd, buf| unsafe {
libc::read(fd, mut_buf(buf), buf.len())
})
.await
}
pub(crate) async fn do_read<F>(
&mut self,
mut buf: &mut [u8],
all: bool,
mut f: F,
) -> Result<usize>
where
F: FnMut(i32, &mut [u8]) -> isize,
{
let mut bytes = 0;
loop {
let ret = f(self.fd.fd, buf);
#[allow(clippy::comparison_chain)]
if ret > 0 {
let n = ret as usize;
bytes += n;
if n == buf.len() {
return Ok(bytes);
}
buf = &mut buf[n..];
} else if ret == 0 {
return Ok(bytes);
} else {
let e = Error::last();
match e.errno {
libc::EAGAIN if all || bytes == 0 => self.wait_readable().await?,
libc::EAGAIN => return Ok(bytes),
libc::EINTR => continue,
_ if bytes > 0 => return Ok(bytes),
_ => return Err(e),
}
}
}
}
pub async fn write(&mut self, buf: &[u8]) -> Result<usize> {
self.do_write(buf, false, |fd, buf| unsafe {
libc::write(fd, const_buf(buf), buf.len())
})
.await
}
pub async fn write_all(&mut self, buf: &[u8]) -> Result<usize> {
self.do_write(buf, true, |fd, buf| unsafe {
libc::write(fd, const_buf(buf), buf.len())
})
.await
}
pub(crate) async fn do_write<F>(&mut self, mut buf: &[u8], all: bool, mut f: F) -> Result<usize>
where
F: FnMut(i32, &[u8]) -> isize,
{
let mut bytes = 0;
loop {
let ret = f(self.fd.fd, buf);
if ret >= 0 {
let n = ret as usize;
bytes += n;
if n == buf.len() {
return Ok(bytes);
}
buf = &buf[n..];
} else {
let e = Error::last();
match e.errno {
libc::EAGAIN if all || bytes == 0 => self.wait_writable().await?,
libc::EAGAIN => return Ok(bytes),
libc::EINTR => continue,
_ if bytes > 0 => return Ok(bytes),
_ => return Err(e),
}
}
}
}
pub async fn readv(&mut self, buf: &[&mut [u8]], off: usize) -> Result<usize> {
self.do_readv(buf, off, false, |fd, piov, iovcnt| unsafe {
libc::readv(fd, piov, iovcnt)
})
.await
}
pub async fn readv_all(&mut self, buf: &[&mut [u8]], off: usize) -> Result<usize> {
self.do_readv(buf, off, true, |fd, piov, iovcnt| unsafe {
libc::readv(fd, piov, iovcnt)
})
.await
}
pub(crate) async fn do_readv<F>(
&mut self,
buf: &[&mut [u8]],
off: usize,
all: bool,
mut f: F,
) -> Result<usize>
where
F: FnMut(i32, *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(self.fd.fd(), 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 {
return Ok(bytes);
} else {
let errno = Error::last().errno;
match errno {
hierr::EAGAIN if all || bytes == 0 => self.wait_readable().await?,
hierr::EAGAIN => return Ok(bytes),
hierr::EINTR => continue,
_ if bytes > 0 => return Ok(bytes),
_ => return Err(Error::new(errno)),
}
}
}
}
Ok(bytes)
}
pub async fn writev(&mut self, buf: &[&[u8]], off: usize) -> Result<usize> {
self.do_writev(buf, off, false, |fd, piov, iovcnt| unsafe {
libc::writev(fd, piov, iovcnt)
})
.await
}
pub async fn writev_all(&mut self, buf: &[&[u8]], off: usize) -> Result<usize> {
self.do_writev(buf, off, true, |fd, piov, iovcnt| unsafe {
libc::writev(fd, piov, iovcnt)
})
.await
}
pub(crate) async fn do_writev<F>(
&mut self,
buf: &[&[u8]],
off: usize,
all: bool,
mut f: F,
) -> Result<usize>
where
F: FnMut(i32, *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(self.fd.fd(), 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 if all || bytes == 0 => self.wait_writable().await?,
hierr::EAGAIN => return Ok(bytes),
hierr::EINTR => continue,
_ if bytes > 0 => return Ok(bytes),
_ => return Err(Error::new(errno)),
}
}
}
}
Ok(bytes)
}
pub async fn sendfile(&mut self, in_fd: i32, off: usize, count: usize) -> Result<usize> {
self.do_sendfile(in_fd, off, count, false).await
}
pub async fn sendfile_all(&mut self, in_fd: i32, off: usize, count: usize) -> Result<usize> {
self.do_sendfile(in_fd, off, count, true).await
}
async fn do_sendfile(
&mut self,
in_fd: i32,
off: usize,
count: usize,
all: bool,
) -> Result<usize> {
let mut off = off as i64;
let mut len = count;
let mut bytes = 0_usize;
loop {
let ret = unsafe { libc::sendfile(self.fd.fd, in_fd, &mut off, len) };
if ret > 0 {
bytes += ret as usize;
if len == ret as usize {
return Ok(bytes);
} else {
len -= ret as usize;
}
} else {
let e = Error::last();
match e.errno {
hierr::EAGAIN if all || bytes == 0 => self.wait_writable().await?,
hierr::EAGAIN => return Ok(bytes),
hierr::EINTR => continue,
_ if bytes > 0 => return Ok(bytes),
_ => return Err(e),
}
}
}
}
}