use core::mem::MaybeUninit;
use super::Op;
use crate::{
buf::{
IoBufMut,
PtrBufMut,
},
future::Future,
};
pub struct Read<B: IoBufMut> {
fd: lx::RawFd,
buf: B,
off: u64,
}
unsafe impl<B: IoBufMut> Op for Read<B> {
type Output = (lx::Result<()>, B);
fn fill_sqe(&mut self, sqe: &mut lx::io_uring_sqe) {
sqe.opcode = lx::IORING_OP_READ;
sqe.fd = self.fd;
sqe.off = self.off;
sqe.addr = self.buf.uninit_bytes().as_ptr() as u64;
sqe.len = u32::try_from(self.buf.uninit_bytes().len()).unwrap();
}
fn complete(mut self, ret: i32) -> Self::Output {
let ret = ret as u32;
if let Ok(err) = lx::Error::try_from(ret) {
return (Err(err), self.buf);
}
unsafe { self.buf.set_len(usize::try_from(ret).unwrap()) };
(Ok(()), self.buf)
}
}
pub fn read<B: IoBufMut>(fd: &impl lx::AsRawFd, buf: B, off: u64) -> Future<Read<B>> {
Future::new(Read {
fd: fd.as_raw_fd(),
buf,
off,
})
}
pub async fn read_exact<B: IoBufMut>(
fd: &impl lx::AsRawFd,
mut buf: B,
mut off: u64,
) -> (lx::Result<()>, B) {
let max_len = usize::try_from(u32::MAX - u32::from(lx::MAX_ERRNO)).unwrap_or(usize::MAX);
let mut done = 0;
let total = buf.uninit_bytes().len();
while done != total {
let end = (done + max_len).min(total);
let slice_buf =
unsafe { PtrBufMut::new(&mut buf.uninit_bytes()[done..end] as *mut [MaybeUninit<u8>]) };
let (ret, slice_buf) = read(fd, slice_buf, off).await;
if let Err(err) = ret {
return (Err(err), buf);
}
let n = slice_buf.as_ref().len();
done += n;
if off != u64::MAX {
off += u64::try_from(n).unwrap();
}
}
(Ok(()), buf)
}