use std::mem::ManuallyDrop;
#[cfg(any(target_os = "android", target_os = "linux"))]
use std::os::fd::{AsRawFd, BorrowedFd};
use std::pin::Pin;
use std::task::{self, Poll};
use std::{io, ptr};
use crate::extract::{Extract, Extractor};
#[cfg(any(target_os = "android", target_os = "linux"))]
use crate::new_flag;
use crate::op::{OpState, fd_iter_operation, fd_operation, operation};
use crate::{AsyncFd, man_link, sys};
mod read_buf;
mod traits;
#[cfg(any(target_os = "android", target_os = "linux"))]
pub(crate) use traits::BufId;
pub(crate) use traits::BufMutParts;
pub(crate) use std::io::*;
pub use read_buf::{ReadBuf, ReadBufPool};
#[doc(inline)]
pub use traits::{Buf, BufMut, BufMutSlice, BufSlice, IoMutSlice, IoSlice, StaticBuf};
macro_rules! stdio {
(
$fn: ident () -> $name: ident, $fd: expr
) => {
#[doc = concat!("Create a new [`", stringify!($name), "`].")]
pub fn $fn(sq: $crate::SubmissionQueue) -> $name {
unsafe { $name(::std::mem::ManuallyDrop::new($crate::fd::AsyncFd::from_raw($fd, $crate::fd::Kind::File, sq))) }
}
#[doc = concat!(
"An [`AsyncFd`] for ", stringify!($fn), ".\n\n",
"Created by calling [`", stringify!($fn), "`].\n\n",
"# Notes\n\n",
"This directly writes to the raw file descriptor, which means it's not buffered and will not flush anything buffered by the standard library.\n\n",
"When this type is dropped it will not close ", stringify!($fn), ".",
)]
pub struct $name(::std::mem::ManuallyDrop<$crate::fd::AsyncFd>);
impl ::std::ops::Deref for $name {
type Target = $crate::fd::AsyncFd;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ::std::fmt::Debug for $name {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(::std::stringify!($name))
.field("fd", &*self.0)
.finish()
}
}
impl ::std::ops::Drop for $name {
fn drop(&mut self) {
unsafe { ::std::ptr::drop_in_place(&mut self.0.sq) };
}
}
};
}
stdio!(stdin() -> Stdin, libc::STDIN_FILENO);
stdio!(stdout() -> Stdout, libc::STDOUT_FILENO);
stdio!(stderr() -> Stderr, libc::STDERR_FILENO);
pub(crate) const NO_OFFSET: u64 = u64::MAX;
impl AsyncFd {
#[doc = man_link!(read(2))]
pub fn read<'fd, B: BufMut>(&'fd self, buf: B) -> Read<'fd, B> {
Read::new(self, buf, NO_OFFSET)
}
pub fn multishot_read<'fd>(&'fd self, pool: ReadBufPool) -> MultishotRead<'fd> {
MultishotRead::new(self, pool, ())
}
pub fn read_n<'fd, B: BufMut>(&'fd self, buf: B, n: usize) -> ReadN<'fd, B> {
let buf = ReadNBuf { buf, last_read: 0 };
ReadN {
read: self.read(buf),
offset: NO_OFFSET,
left: n,
}
}
#[doc = man_link!(readv(2))]
pub fn read_vectored<'fd, B: BufMutSlice<N>, const N: usize>(
&'fd self,
mut bufs: B,
) -> ReadVectored<'fd, B, N> {
let iovecs = unsafe { bufs.as_iovecs_mut() };
ReadVectored::new(self, (bufs, iovecs), NO_OFFSET)
}
pub fn read_n_vectored<'fd, B: BufMutSlice<N>, const N: usize>(
&'fd self,
bufs: B,
n: usize,
) -> ReadNVectored<'fd, B, N> {
let bufs = ReadNBuf {
buf: bufs,
last_read: 0,
};
ReadNVectored {
read: self.read_vectored(bufs),
offset: NO_OFFSET,
left: n,
}
}
#[doc = man_link!(write(2))]
pub fn write<'fd, B: Buf>(&'fd self, buf: B) -> Write<'fd, B> {
Write::new(self, buf, NO_OFFSET)
}
pub fn write_all<'fd, B: Buf>(&'fd self, buf: B) -> WriteAll<'fd, B> {
let buf = SkipBuf { buf, skip: 0 };
WriteAll {
write: Extractor {
fut: self.write(buf),
},
offset: NO_OFFSET,
}
}
#[doc = man_link!(writev(2))]
pub fn write_vectored<'fd, B: BufSlice<N>, const N: usize>(
&'fd self,
bufs: B,
) -> WriteVectored<'fd, B, N> {
let iovecs = unsafe { bufs.as_iovecs() };
WriteVectored::new(self, (bufs, iovecs), NO_OFFSET)
}
pub fn write_all_vectored<'fd, B: BufSlice<N>, const N: usize>(
&'fd self,
bufs: B,
) -> WriteAllVectored<'fd, B, N> {
WriteAllVectored {
write: self.write_vectored(bufs).extract(),
offset: NO_OFFSET,
skip: 0,
}
}
#[doc = man_link!(splice(2))]
#[doc(alias = "splice")]
#[cfg(any(target_os = "android", target_os = "linux"))]
pub fn splice_to<'fd>(&'fd self, target: BorrowedFd<'fd>, length: u32) -> Splice<'fd> {
self.splice(target, SpliceDirection::To, length)
}
#[doc = man_link!(splice(2))]
#[doc(alias = "splice")]
#[cfg(any(target_os = "android", target_os = "linux"))]
pub fn splice_from<'fd>(&'fd self, target: BorrowedFd<'fd>, length: u32) -> Splice<'fd> {
self.splice(target, SpliceDirection::From, length)
}
#[cfg(any(target_os = "android", target_os = "linux"))]
fn splice<'fd>(
&'fd self,
target: BorrowedFd<'fd>,
direction: SpliceDirection,
length: u32,
) -> Splice<'fd> {
let target_fd = target.as_raw_fd();
let flags = SpliceFlag(0);
let args = (target_fd, direction, NO_OFFSET, NO_OFFSET, length, flags);
Splice::new(self, (), args)
}
#[doc = man_link!(close(2))]
pub fn close(self) -> Close {
let this = ManuallyDrop::new(self);
let fd = this.fd();
let kind = this.kind();
let sq = unsafe { ptr::read(&raw const this.sq) };
Close::new(sq, (), (fd, kind))
}
}
#[derive(Copy, Clone, Debug)]
#[cfg(any(target_os = "android", target_os = "linux"))]
pub(crate) enum SpliceDirection {
To,
From,
}
#[cfg(any(target_os = "android", target_os = "linux"))]
new_flag!(
pub struct SpliceFlag(u32) impl BitOr {
MOVE = libc::SPLICE_F_MOVE,
MORE = libc::SPLICE_F_MORE,
}
);
fd_operation!(
pub struct Read<B: BufMut>(sys::io::ReadOp<B>) -> io::Result<B>;
pub struct ReadVectored<B: BufMutSlice<N>; const N: usize>(sys::io::ReadVectoredOp<B, N>) -> io::Result<B>;
pub struct Write<B: Buf>(sys::io::WriteOp<B>) -> io::Result<usize>,
impl Extract -> io::Result<(B, usize)>;
pub struct WriteVectored<B: BufSlice<N>; const N: usize>(sys::io::WriteVectoredOp<B, N>) -> io::Result<usize>,
impl Extract -> io::Result<(B, usize)>;
);
impl<'fd, B: BufMut> Read<'fd, B> {
#[doc = man_link!(pread(2))]
pub fn from(mut self, offset: u64) -> Self {
if let Some(off) = self.state.args_mut() {
*off = offset;
}
self
}
}
impl<'fd, B: BufMutSlice<N>, const N: usize> ReadVectored<'fd, B, N> {
#[doc = man_link!(preadv(2))]
pub fn from(mut self, offset: u64) -> Self {
if let Some(off) = self.state.args_mut() {
*off = offset;
}
self
}
}
impl<'fd, B: Buf> Write<'fd, B> {
#[doc = man_link!(pwrite(2))]
pub fn at(mut self, offset: u64) -> Self {
if let Some(off) = self.state.args_mut() {
*off = offset;
}
self
}
}
impl<'fd, B: BufSlice<N>, const N: usize> WriteVectored<'fd, B, N> {
#[doc = man_link!(pwritev(2))]
pub fn at(mut self, offset: u64) -> Self {
if let Some(off) = self.state.args_mut() {
*off = offset;
}
self
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
fd_operation!(
pub struct Splice(sys::io::SpliceOp) -> io::Result<usize>;
);
impl<'fd, B: BufMut> Read<'fd, B> {
#[cfg(any(target_os = "android", target_os = "linux"))]
pub(crate) fn fd(&self) -> &'fd AsyncFd {
self.fd
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
impl<'fd> Splice<'fd> {
pub fn from(mut self, offset: u64) -> Self {
if let Some((_, _, off_in, _, _, _)) = self.state.args_mut() {
*off_in = offset;
}
self
}
pub fn at(mut self, offset: u64) -> Self {
if let Some((_, _, _, off_out, _, _)) = self.state.args_mut() {
*off_out = offset;
}
self
}
pub fn flags(mut self, flags: SpliceFlag) -> Self {
if let Some((_, _, _, _, _, f)) = self.state.args_mut() {
*f = flags;
}
self
}
}
fd_iter_operation! {
pub struct MultishotRead(sys::io::MultishotReadOp) -> io::Result<ReadBuf>;
}
#[derive(Debug)]
#[must_use = "`Future`s do nothing unless polled"]
pub struct ReadN<'fd, B: BufMut> {
read: Read<'fd, ReadNBuf<B>>,
offset: u64,
left: usize,
}
impl<'fd, B: BufMut> ReadN<'fd, B> {
pub fn from(mut self, offset: u64) -> Self {
if let Some(off) = self.read.state.args_mut() {
*off = offset;
self.offset = offset;
}
self
}
}
impl<'fd, B: BufMut> Future for ReadN<'fd, B> {
type Output = io::Result<B>;
fn poll(self: Pin<&mut Self>, ctx: &mut task::Context<'_>) -> Poll<Self::Output> {
let this = unsafe { Pin::into_inner_unchecked(self) };
let mut read = unsafe { Pin::new_unchecked(&mut this.read) };
match read.as_mut().poll(ctx) {
Poll::Ready(Ok(buf)) => {
if buf.last_read == 0 {
return Poll::Ready(Err(io::ErrorKind::UnexpectedEof.into()));
}
if buf.last_read >= this.left {
return Poll::Ready(Ok(buf.buf));
}
this.left -= buf.last_read;
if this.offset != NO_OFFSET {
this.offset += buf.last_read as u64;
}
this.read.state.reset(buf, this.offset);
unsafe { Pin::new_unchecked(this) }.poll(ctx)
}
Poll::Ready(Err(err)) => Poll::Ready(Err(err)),
Poll::Pending => Poll::Pending,
}
}
}
#[derive(Debug)]
#[must_use = "`Future`s do nothing unless polled"]
pub struct ReadNVectored<'fd, B: BufMutSlice<N>, const N: usize> {
read: ReadVectored<'fd, ReadNBuf<B>, N>,
offset: u64,
left: usize,
}
impl<'fd, B: BufMutSlice<N>, const N: usize> ReadNVectored<'fd, B, N> {
pub fn from(mut self, offset: u64) -> Self {
if let Some(off) = self.read.state.args_mut() {
*off = offset;
self.offset = offset;
}
self
}
}
impl<'fd, B: BufMutSlice<N>, const N: usize> Future for ReadNVectored<'fd, B, N> {
type Output = io::Result<B>;
fn poll(self: Pin<&mut Self>, ctx: &mut task::Context<'_>) -> Poll<Self::Output> {
let this = unsafe { Pin::into_inner_unchecked(self) };
let mut read = unsafe { Pin::new_unchecked(&mut this.read) };
match read.as_mut().poll(ctx) {
Poll::Ready(Ok(mut bufs)) => {
if bufs.last_read == 0 {
return Poll::Ready(Err(io::ErrorKind::UnexpectedEof.into()));
}
if bufs.last_read >= this.left {
return Poll::Ready(Ok(bufs.buf));
}
this.left -= bufs.last_read;
if this.offset != NO_OFFSET {
this.offset += bufs.last_read as u64;
}
let iovecs = unsafe { bufs.as_iovecs_mut() };
let resources = (bufs, iovecs);
this.read.state.reset(resources, this.offset);
unsafe { Pin::new_unchecked(this) }.poll(ctx)
}
Poll::Ready(Err(err)) => Poll::Ready(Err(err)),
Poll::Pending => Poll::Pending,
}
}
}
#[derive(Debug)]
#[must_use = "`Future`s do nothing unless polled"]
pub struct WriteAll<'fd, B: Buf> {
write: Extractor<Write<'fd, SkipBuf<B>>>,
offset: u64,
}
impl<'fd, B: Buf> WriteAll<'fd, B> {
pub fn at(mut self, offset: u64) -> Self {
if let Some(off) = self.write.fut.state.args_mut() {
*off = offset;
self.offset = offset;
}
self
}
fn poll_inner(self: Pin<&mut Self>, ctx: &mut task::Context<'_>) -> Poll<io::Result<B>> {
let this = unsafe { Pin::into_inner_unchecked(self) };
let mut write = unsafe { Pin::new_unchecked(&mut this.write) };
match write.as_mut().poll(ctx) {
Poll::Ready(Ok((_, 0))) => Poll::Ready(Err(io::ErrorKind::WriteZero.into())),
Poll::Ready(Ok((mut buf, n))) => {
buf.skip += n as u32;
if this.offset != NO_OFFSET {
this.offset += n as u64;
}
if let (_, 0) = unsafe { buf.parts() } {
return Poll::Ready(Ok(buf.buf));
}
this.write.fut.state.reset(buf, this.offset);
unsafe { Pin::new_unchecked(this) }.poll_inner(ctx)
}
Poll::Ready(Err(err)) => Poll::Ready(Err(err)),
Poll::Pending => Poll::Pending,
}
}
}
impl<'fd, B: Buf> Future for WriteAll<'fd, B> {
type Output = io::Result<()>;
fn poll(self: Pin<&mut Self>, ctx: &mut task::Context<'_>) -> Poll<Self::Output> {
self.poll_inner(ctx).map_ok(|_| ())
}
}
impl<'fd, B: Buf> Extract for WriteAll<'fd, B> {}
impl<'fd, B: Buf> Future for Extractor<WriteAll<'fd, B>> {
type Output = io::Result<B>;
fn poll(self: Pin<&mut Self>, ctx: &mut task::Context<'_>) -> Poll<Self::Output> {
unsafe { Pin::map_unchecked_mut(self, |s| &mut s.fut) }.poll_inner(ctx)
}
}
#[derive(Debug)]
#[must_use = "`Future`s do nothing unless polled"]
pub struct WriteAllVectored<'fd, B: BufSlice<N>, const N: usize> {
write: Extractor<WriteVectored<'fd, B, N>>,
offset: u64,
skip: u64,
}
impl<'fd, B: BufSlice<N>, const N: usize> WriteAllVectored<'fd, B, N> {
pub fn at(mut self, offset: u64) -> Self {
if let Some(off) = self.write.fut.state.args_mut() {
*off = offset;
self.offset = offset;
}
self
}
fn poll_inner(self: Pin<&mut Self>, ctx: &mut task::Context<'_>) -> Poll<io::Result<B>> {
let this = unsafe { Pin::into_inner_unchecked(self) };
let mut write = unsafe { Pin::new_unchecked(&mut this.write) };
match write.as_mut().poll(ctx) {
Poll::Ready(Ok((_, 0))) => Poll::Ready(Err(io::ErrorKind::WriteZero.into())),
Poll::Ready(Ok((bufs, n))) => {
this.skip += n as u64;
if this.offset != NO_OFFSET {
this.offset += n as u64;
}
let mut iovecs = unsafe { bufs.as_iovecs() };
let mut skip = this.skip;
for iovec in &mut iovecs {
if iovec.len() as u64 <= skip {
skip -= iovec.len() as u64;
unsafe { iovec.set_len(0) };
} else {
unsafe { iovec.skip(skip as usize) };
break;
}
}
if iovecs[N - 1].len() == 0 {
return Poll::Ready(Ok(bufs));
}
this.write.fut.state.reset((bufs, iovecs), this.offset);
unsafe { Pin::new_unchecked(this) }.poll_inner(ctx)
}
Poll::Ready(Err(err)) => Poll::Ready(Err(err)),
Poll::Pending => Poll::Pending,
}
}
}
impl<'fd, B: BufSlice<N>, const N: usize> Future for WriteAllVectored<'fd, B, N> {
type Output = io::Result<()>;
fn poll(self: Pin<&mut Self>, ctx: &mut task::Context<'_>) -> Poll<Self::Output> {
self.poll_inner(ctx).map_ok(|_| ())
}
}
impl<'fd, B: BufSlice<N>, const N: usize> Extract for WriteAllVectored<'fd, B, N> {}
impl<'fd, B: BufSlice<N>, const N: usize> Future for Extractor<WriteAllVectored<'fd, B, N>> {
type Output = io::Result<B>;
fn poll(self: Pin<&mut Self>, ctx: &mut task::Context<'_>) -> Poll<Self::Output> {
unsafe { Pin::map_unchecked_mut(self, |s| &mut s.fut) }.poll_inner(ctx)
}
}
operation!(
pub struct Close(sys::io::CloseOp) -> io::Result<()>;
);
#[derive(Debug)]
pub(crate) struct ReadNBuf<B> {
pub(crate) buf: B,
pub(crate) last_read: usize,
}
unsafe impl<B: BufMut> BufMut for ReadNBuf<B> {
unsafe fn parts_mut(&mut self) -> (*mut u8, u32) {
unsafe { self.buf.parts_mut() }
}
unsafe fn set_init(&mut self, n: usize) {
self.last_read = n;
unsafe { self.buf.set_init(n) };
}
fn spare_capacity(&self) -> u32 {
self.buf.spare_capacity()
}
fn has_spare_capacity(&self) -> bool {
self.buf.has_spare_capacity()
}
#[cfg(any(target_os = "android", target_os = "linux"))]
unsafe fn buffer_init(&mut self, id: BufId, n: u32) {
self.last_read = n as usize;
unsafe { self.buf.buffer_init(id, n) };
}
}
unsafe impl<B: BufMutSlice<N>, const N: usize> BufMutSlice<N> for ReadNBuf<B> {
unsafe fn as_iovecs_mut(&mut self) -> [IoMutSlice; N] {
unsafe { self.buf.as_iovecs_mut() }
}
unsafe fn set_init(&mut self, n: usize) {
self.last_read = n;
unsafe { self.buf.set_init(n) };
}
}
#[derive(Debug)]
pub(crate) struct SkipBuf<B> {
pub(crate) buf: B,
pub(crate) skip: u32,
}
unsafe impl<B: Buf> Buf for SkipBuf<B> {
unsafe fn parts(&self) -> (*const u8, u32) {
let (ptr, size) = unsafe { self.buf.parts() };
if self.skip >= size {
(ptr, 0)
} else {
(unsafe { ptr.add(self.skip as usize) }, size - self.skip)
}
}
}