use std::io;
use windows_sys::Win32::Foundation::{ERROR_BROKEN_PIPE, ERROR_HANDLE_EOF, HANDLE};
use windows_sys::Win32::Storage::FileSystem::{ReadFile, WriteFile};
use windows_sys::Win32::System::IO::{CancelIoEx, OVERLAPPED};
use crate::op::completion::{CompletionHandle, completion_for_current_thread};
use crate::platform::current::runtime::with_current_driver;
use crate::platform::windows::driver::{OverlappedHeader, OverlappedResult};
use crate::sys::handle::RawFile;
#[repr(C)]
struct OverlappedOp<D, M, T> {
header: OverlappedHeader,
data: D,
map: Option<M>,
handle: CompletionHandle<io::Result<T>>,
}
unsafe fn complete_thunk<D, M, T>(ptr: *mut OverlappedHeader, result: OverlappedResult)
where
M: FnOnce(D, OverlappedResult) -> io::Result<T>,
T: Send + 'static,
{
let mut op = unsafe { Box::from_raw(ptr.cast::<OverlappedOp<D, M, T>>()) };
let map = op
.map
.take()
.expect("overlapped completion must run exactly once");
let value = map(op.data, result);
op.handle.complete(value);
}
pub(crate) async fn submit<D, S, M, T>(
cancel_handle: RawFile,
data: D,
start: S,
map: M,
) -> io::Result<T>
where
D: 'static,
S: FnOnce(&mut D, *mut OVERLAPPED) -> io::Result<()>,
M: FnOnce(D, OverlappedResult) -> io::Result<T> + 'static,
T: Send + 'static,
{
let (future, handle) = completion_for_current_thread::<io::Result<T>>();
let op = Box::new(OverlappedOp {
header: OverlappedHeader::new(complete_thunk::<D, M, T>),
data,
map: Some(map),
handle: handle.clone(),
});
let ptr = Box::into_raw(op);
let overlapped = ptr.cast::<OVERLAPPED>();
let started = start(unsafe { &mut (*ptr).data }, overlapped);
match started {
Ok(()) => {
let cancel_target = cancel_handle;
let cancel_overlapped = overlapped as usize;
handle.set_cancel(move || {
unsafe {
CancelIoEx(
cancel_target.as_handle() as HANDLE,
cancel_overlapped as *const OVERLAPPED,
);
}
});
future.await
}
Err(error) => {
drop(unsafe { Box::from_raw(ptr) });
handle.complete(Err(error));
future.await
}
}
}
pub(crate) fn associate_file(fd: RawFile) -> io::Result<()> {
with_current_driver(|driver| driver.associate_handle(fd.as_handle()))
}
pub(crate) fn associate_file_reused(fd: RawFile) -> io::Result<()> {
match associate_file(fd) {
Err(error) if error.raw_os_error() == Some(ERROR_INVALID_PARAMETER_CODE) => Ok(()),
result => result,
}
}
const ERROR_INVALID_PARAMETER_CODE: i32 =
windows_sys::Win32::Foundation::ERROR_INVALID_PARAMETER as i32;
pub(crate) async fn read_at(fd: RawFile, len: usize, offset: u64) -> io::Result<Vec<u8>> {
let buffer = vec![0u8; len.max(1)];
let result = submit(
fd,
buffer,
|buffer, overlapped| {
unsafe {
(*overlapped).Anonymous.Anonymous.Offset = offset as u32;
(*overlapped).Anonymous.Anonymous.OffsetHigh = (offset >> 32) as u32;
}
let ok = unsafe {
ReadFile(
fd.as_handle(),
buffer.as_mut_ptr(),
u32::try_from(len).unwrap_or(u32::MAX),
std::ptr::null_mut(),
overlapped,
)
};
check_overlapped_submission(ok)
},
|mut buffer, result| match result.into_result() {
Ok(read) => {
buffer.truncate(read);
Ok(buffer)
}
Err(error) => Err(error),
},
)
.await;
match result {
Err(error) if is_end_of_stream(&error) => Ok(Vec::new()),
other => other,
}
}
pub(crate) async fn write_at(fd: RawFile, data: Vec<u8>, offset: u64) -> io::Result<usize> {
submit(
fd,
data,
|data, overlapped| {
unsafe {
(*overlapped).Anonymous.Anonymous.Offset = offset as u32;
(*overlapped).Anonymous.Anonymous.OffsetHigh = (offset >> 32) as u32;
}
let ok = unsafe {
WriteFile(
fd.as_handle(),
data.as_ptr(),
u32::try_from(data.len()).unwrap_or(u32::MAX),
std::ptr::null_mut(),
overlapped,
)
};
check_overlapped_submission(ok)
},
|_data, result| result.into_result(),
)
.await
}
pub(crate) fn check_overlapped_submission(ok: i32) -> io::Result<()> {
if ok != 0 {
return Ok(());
}
let error = io::Error::last_os_error();
if error.raw_os_error() == Some(windows_sys::Win32::Foundation::ERROR_IO_PENDING as i32) {
Ok(())
} else {
Err(error)
}
}
pub(crate) fn is_end_of_stream(error: &io::Error) -> bool {
matches!(
error.raw_os_error(),
Some(code) if code == ERROR_HANDLE_EOF as i32 || code == ERROR_BROKEN_PIPE as i32
)
}