use std::future::Future;
use std::io::{ErrorKind, Result};
use std::net::ToSocketAddrs;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::{Duration, Instant};
use std::{io, ptr};
use orengine_macros::{poll_for_io_request, poll_for_time_bounded_io_request};
use socket2::SockAddr;
use crate as orengine;
use crate::io::io_request_data::IoRequestData;
use crate::io::sys::{AsRawFd, MessageSendHeader, RawFd};
use crate::io::worker::{local_worker, IoWorker};
pub struct SendTo<'fut> {
fd: RawFd,
message_header: MessageSendHeader<'fut>,
buf: &'fut [u8],
addr: &'fut SockAddr,
io_request_data: Option<IoRequestData>,
}
impl<'fut> SendTo<'fut> {
pub fn new(fd: RawFd, buf: &'fut [u8], addr: &'fut SockAddr) -> Self {
Self {
fd,
message_header: MessageSendHeader::new(),
buf,
addr,
io_request_data: None,
}
}
}
impl Future for SendTo<'_> {
type Output = Result<usize>;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let this = unsafe { self.get_unchecked_mut() };
#[allow(unused, reason = "Cannot write proc_macro else to make it readable.")]
let ret;
poll_for_io_request!((
local_worker().send_to(
this.fd,
this.message_header
.get_os_message_header_ptr(this.addr, &mut ptr::from_ref::<[u8]>(this.buf)),
unsafe { this.io_request_data.as_mut().unwrap_unchecked() }
),
ret
));
}
}
#[allow(
clippy::non_send_fields_in_send_ty,
reason = "We guarantee that `SendTo` is `Send`."
)]
unsafe impl Send for SendTo<'_> {}
pub struct SendToWithDeadline<'fut> {
fd: RawFd,
message_header: MessageSendHeader<'fut>,
buf: &'fut [u8],
addr: &'fut SockAddr,
io_request_data: Option<IoRequestData>,
deadline: Instant,
}
impl<'fut> SendToWithDeadline<'fut> {
pub fn new(fd: RawFd, buf: &'fut [u8], addr: &'fut SockAddr, deadline: Instant) -> Self {
Self {
fd,
message_header: MessageSendHeader::new(),
buf,
addr,
io_request_data: None,
deadline,
}
}
}
impl Future for SendToWithDeadline<'_> {
type Output = Result<usize>;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let this = unsafe { self.get_unchecked_mut() };
let worker = local_worker();
#[allow(unused, reason = "Cannot write proc_macro else to make it readable.")]
let ret;
poll_for_time_bounded_io_request!((
worker.send_to_with_deadline(
this.fd,
this.message_header
.get_os_message_header_ptr(this.addr, &mut ptr::from_ref::<[u8]>(this.buf)),
unsafe { this.io_request_data.as_mut().unwrap_unchecked() },
&mut this.deadline
),
ret
));
}
}
#[allow(
clippy::non_send_fields_in_send_ty,
reason = "We guarantee that `SendToWithDeadline` is `Send`."
)]
unsafe impl Send for SendToWithDeadline<'_> {}
#[inline(always)]
fn sock_addr_from_to_socket_addr<A: ToSocketAddrs>(to_addr: A) -> Result<SockAddr> {
let mut addrs = to_addr.to_socket_addrs()?;
if let Some(addr) = addrs.next() {
return Ok(SockAddr::from(addr));
}
Err(io::Error::new(
ErrorKind::InvalidInput,
"no addresses to send data to",
))
}
pub trait AsyncSendTo: AsRawFd {
#[inline(always)]
async fn send_to<A: ToSocketAddrs>(&mut self, buf: &[u8], addr: A) -> Result<usize> {
SendTo::new(self.as_raw_fd(), buf, &sock_addr_from_to_socket_addr(addr)?).await
}
#[inline(always)]
async fn send_to_with_deadline<A: ToSocketAddrs>(
&mut self,
buf: &[u8],
addr: A,
deadline: Instant,
) -> Result<usize> {
SendToWithDeadline::new(
self.as_raw_fd(),
buf,
&sock_addr_from_to_socket_addr(addr)?,
deadline,
)
.await
}
#[inline(always)]
async fn send_to_with_timeout<A: ToSocketAddrs>(
&mut self,
buf: &[u8],
addr: A,
timeout: Duration,
) -> Result<usize> {
SendToWithDeadline::new(
self.as_raw_fd(),
buf,
&sock_addr_from_to_socket_addr(addr)?,
Instant::now() + timeout,
)
.await
}
#[inline(always)]
async fn send_all_to<A: ToSocketAddrs>(&mut self, buf: &[u8], addr: A) -> Result<usize> {
let mut sent = 0;
let addr = sock_addr_from_to_socket_addr(addr)?;
while sent < buf.len() {
sent += SendTo::new(self.as_raw_fd(), buf, &addr).await?;
}
Ok(sent)
}
#[inline(always)]
async fn send_all_to_with_deadline<A: ToSocketAddrs>(
&mut self,
buf: &[u8],
addr: A,
deadline: Instant,
) -> Result<usize> {
let mut sent = 0;
let addr = sock_addr_from_to_socket_addr(addr)?;
while sent < buf.len() {
sent += SendToWithDeadline::new(self.as_raw_fd(), buf, &addr, deadline).await?;
}
Ok(sent)
}
#[inline(always)]
async fn send_all_to_with_timeout<A: ToSocketAddrs>(
&mut self,
buf: &[u8],
addr: A,
timeout: Duration,
) -> Result<usize> {
self.send_all_to_with_deadline(buf, addr, Instant::now() + timeout)
.await
}
}