use std::cell::UnsafeCell;
use windows_sys::Win32::System::IO::{OVERLAPPED, OVERLAPPED_0, OVERLAPPED_0_0};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OperationState {
Idle,
Submitted,
Pending,
Completed,
Cancelled,
}
#[derive(Debug)]
#[repr(C)]
pub struct Operation<P> {
overlapped: UnsafeCell<OVERLAPPED>,
#[allow(dead_code)]
reclaim: Option<unsafe fn(*mut OVERLAPPED)>,
state: OperationState,
payload: P,
}
const RECLAIM_OFFSET: usize = core::mem::offset_of!(Operation<()>, reclaim);
pub(crate) unsafe fn reclaim_operation<P>(overlapped: *mut OVERLAPPED) {
drop(unsafe { Box::from_raw(overlapped.cast::<Operation<P>>()) });
}
pub(crate) unsafe fn reclaim_from_overlapped(overlapped: *mut OVERLAPPED) {
let slot = unsafe {
overlapped
.cast::<u8>()
.add(RECLAIM_OFFSET)
.cast::<Option<unsafe fn(*mut OVERLAPPED)>>()
};
if let Some(reclaim) = unsafe { *slot } {
unsafe { reclaim(overlapped) };
}
}
#[cfg(any(feature = "fs", feature = "socket", feature = "device"))]
pub(crate) unsafe fn payload_ptr_from_overlapped<P>(overlapped: *mut OVERLAPPED) -> *mut P {
let offset = core::mem::offset_of!(Operation<P>, payload);
unsafe { overlapped.cast::<u8>().add(offset).cast::<P>() }
}
pub unsafe fn reclaim_overlapped(overlapped: *mut OVERLAPPED) {
unsafe { reclaim_from_overlapped(overlapped) };
}
impl<P> Operation<P> {
#[must_use]
pub fn new(payload: P) -> Self {
let overlapped = OVERLAPPED {
Internal: 0,
InternalHigh: 0,
Anonymous: OVERLAPPED_0 {
Anonymous: OVERLAPPED_0_0 {
Offset: 0,
OffsetHigh: 0,
},
},
hEvent: std::ptr::null_mut(),
};
Self {
overlapped: UnsafeCell::new(overlapped),
reclaim: None,
state: OperationState::Idle,
payload,
}
}
#[must_use]
pub fn state(&self) -> OperationState {
self.state
}
pub fn set_state(&mut self, state: OperationState) {
self.state = state;
}
pub(crate) fn arm(&mut self) {
self.reclaim = Some(reclaim_operation::<P>);
}
#[must_use]
pub fn into_overlapped(mut self) -> *mut OVERLAPPED {
self.arm();
self.state = OperationState::Pending;
let boxed = Box::new(self);
let overlapped = boxed.overlapped_ptr();
let _ = Box::into_raw(boxed);
overlapped
}
#[must_use]
pub unsafe fn from_overlapped(overlapped: *mut OVERLAPPED) -> Self {
unsafe { *Box::from_raw(overlapped.cast::<Operation<P>>()) }
}
#[must_use]
pub fn payload(&self) -> &P {
&self.payload
}
#[must_use]
pub fn payload_mut(&mut self) -> &mut P {
&mut self.payload
}
#[must_use]
pub fn into_payload(self) -> P {
self.payload
}
pub fn set_offset(&mut self, offset: u64) {
let overlapped = self.overlapped.get_mut();
overlapped.Anonymous.Anonymous.Offset = offset as u32;
overlapped.Anonymous.Anonymous.OffsetHigh = (offset >> 32) as u32;
}
#[must_use]
pub fn overlapped_ptr(&self) -> *mut OVERLAPPED {
self.overlapped.get()
}
}
#[cfg(test)]
mod tests;