use std::{io, marker::PhantomData, mem::MaybeUninit, pin::Pin, task::Waker};
use compio_buf::BufResult;
use crate::{OpCode, Overlapped, PushEntry, RawFd};
#[repr(C)]
pub(crate) struct RawOp<T: ?Sized> {
header: Overlapped,
cancelled: bool,
metadata: usize,
result: PushEntry<Option<Waker>, io::Result<usize>>,
flags: u32,
op: T,
}
#[repr(C)]
union OpCodePtrRepr {
ptr: *mut RawOp<dyn OpCode>,
components: OpCodePtrComponents,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct OpCodePtrComponents {
data_pointer: *mut (),
metadata: usize,
}
fn opcode_metadata<T: OpCode + 'static>() -> usize {
let mut op = MaybeUninit::<RawOp<T>>::uninit();
unsafe {
OpCodePtrRepr {
ptr: op.as_mut_ptr(),
}
.components
.metadata
}
}
const unsafe fn opcode_dyn_mut(ptr: *mut (), metadata: usize) -> *mut RawOp<dyn OpCode> {
OpCodePtrRepr {
components: OpCodePtrComponents {
data_pointer: ptr,
metadata,
},
}
.ptr
}
#[derive(PartialEq, Eq, Hash)]
pub struct Key<T: ?Sized> {
user_data: *mut (),
_p: PhantomData<Box<RawOp<T>>>,
}
impl<T: ?Sized> Unpin for Key<T> {}
impl<T: OpCode + 'static> Key<T> {
pub(crate) fn new(driver: RawFd, op: T) -> Self {
let header = Overlapped::new(driver);
let raw_op = Box::new(RawOp {
header,
cancelled: false,
metadata: opcode_metadata::<T>(),
result: PushEntry::Pending(None),
flags: 0,
op,
});
unsafe { Self::new_unchecked(Box::into_raw(raw_op) as _) }
}
}
impl<T: ?Sized> Key<T> {
pub unsafe fn new_unchecked(user_data: usize) -> Self {
Self {
user_data: user_data as _,
_p: PhantomData,
}
}
pub fn user_data(&self) -> usize {
self.user_data as _
}
fn as_opaque(&self) -> &RawOp<()> {
unsafe { &*(self.user_data as *const RawOp<()>) }
}
fn as_opaque_mut(&mut self) -> &mut RawOp<()> {
unsafe { &mut *(self.user_data as *mut RawOp<()>) }
}
fn as_dyn_mut_ptr(&mut self) -> *mut RawOp<dyn OpCode> {
let user_data = self.user_data;
let this = self.as_opaque_mut();
unsafe { opcode_dyn_mut(user_data, this.metadata) }
}
#[cfg(windows)]
pub(crate) fn as_mut_ptr(&mut self) -> *mut Overlapped {
&mut self.as_opaque_mut().header
}
pub(crate) fn set_cancelled(&mut self) -> bool {
self.as_opaque_mut().cancelled = true;
self.has_result()
}
pub(crate) fn set_result(&mut self, res: io::Result<usize>) -> bool {
let this = unsafe { &mut *self.as_dyn_mut_ptr() };
#[cfg(all(target_os = "linux", feature = "io-uring"))]
if let Ok(res) = res {
unsafe {
Pin::new_unchecked(&mut this.op).set_result(res);
}
}
if let PushEntry::Pending(Some(w)) =
std::mem::replace(&mut this.result, PushEntry::Ready(res))
{
w.wake();
}
this.cancelled
}
pub(crate) fn set_flags(&mut self, flags: u32) {
self.as_opaque_mut().flags = flags;
}
pub(crate) fn flags(&self) -> u32 {
self.as_opaque().flags
}
pub(crate) fn has_result(&self) -> bool {
self.as_opaque().result.is_ready()
}
pub(crate) fn set_waker(&mut self, waker: Waker) {
if let PushEntry::Pending(w) = &mut self.as_opaque_mut().result {
*w = Some(waker)
}
}
pub(crate) unsafe fn into_box(mut self) -> Box<RawOp<dyn OpCode>> {
Box::from_raw(self.as_dyn_mut_ptr())
}
}
impl<T> Key<T> {
pub(crate) unsafe fn into_inner(self) -> BufResult<usize, T> {
let op = unsafe { Box::from_raw(self.user_data as *mut RawOp<T>) };
BufResult(op.result.take_ready().unwrap_unchecked(), op.op)
}
}
impl<T: OpCode + ?Sized> Key<T> {
pub(crate) fn as_op_pin(&mut self) -> Pin<&mut dyn OpCode> {
unsafe {
let this = &mut *self.as_dyn_mut_ptr();
Pin::new_unchecked(&mut this.op)
}
}
#[cfg(windows)]
pub(crate) fn operate_blocking(&mut self) -> io::Result<usize> {
use std::task::Poll;
let optr = self.as_mut_ptr();
let op = self.as_op_pin();
let res = unsafe { op.operate(optr.cast()) };
match res {
Poll::Pending => unreachable!("this operation is not overlapped"),
Poll::Ready(res) => res,
}
}
}
impl<T: ?Sized> std::fmt::Debug for Key<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Key({})", self.user_data())
}
}