#[cfg(target_os="windows")]
mod windows {
use crate::pal::windows::*;
use crate::arena::Arena;
use crate::error::{Error, ErrorKind, SourceSpan};
use std::fs::File;
use std::ffi::{OsStr, c_void};
use std::os::windows::io::{FromRawHandle, AsRawHandle};
use allocator_api2::vec::Vec;
pub fn open_file_async_for_read(path: &impl AsRef<OsStr>) -> Result<File, Error> {
let path = path.as_ref();
let mut path_nt = Vec::from(path.as_encoded_bytes());
path_nt.push(b'\0');
let file = unsafe{CreateFileA(
path_nt.as_ptr(),
AccessMask::GENERIC_READ | AccessMask::SYNCHRONIZE,
ShareMode::FILE_SHARE_READ,
None,
CreateDisposition::OPEN_ALWAYS,
FlagsAndAttributes::FILE_FLAG_OVERLAPPED,
std::ptr::null_mut(),
)};
if file == INVALID_HANDLE_VALUE {
return Err(Error::from_io_error(std::io::Error::last_os_error(), path));
}
Ok(unsafe{File::from_raw_handle(file)})
}
pub fn read_files_async<'a, 'b>(files: impl IntoIterator<Item=&'b File>, arena: &'a Arena) -> Vec<Result<&'a [u8], Error>, &'a Arena> {
let files = {
let mut temp = Vec::new_in(arena);
temp.extend(files);
temp
};
let mut iosbs = Vec::with_capacity_in(files.len(), arena);
iosbs.extend(files.iter().map(|_| IoStatusBlock::empty()));
let iosbs = iosbs.leak();
let mut files_data = Vec::with_capacity_in(files.len(), arena);
let mut file_handles = Vec::with_capacity_in(files.len(), arena);
file_handles.extend(files.iter().copied().map(File::as_raw_handle));
let mut num_apcs_queued: usize = 0;
let mut num_apcs_finished: usize = 0;
for (i, file) in files.iter().enumerate() {
let size = file.metadata().expect("metadata always succeeds on windows").len();
let cap = if size > 0 {((((size - 1) / 4096) + 1) * 4096) as usize} else {0};
let buf = arena.alloc(std::alloc::Layout::from_size_align(cap, 4096).unwrap()).unwrap();
extern "C" fn apc(data: *mut c_void, _: *mut IoStatusBlock, _: ULONG) {
unsafe{*data.cast::<usize>() += 1};
}
let nt_status = unsafe{NtReadFile(
file.as_raw_handle(),
std::ptr::null_mut(),
Some(apc),
(&raw mut num_apcs_finished).cast(),
iosbs.as_mut_ptr().add(i),
buf.cast::<u8>().as_ptr(),
buf.len() as DWORD,
Some(&0_i64),
None
)};
if nt_status != NTSTATUS::PENDING {
files_data.push(Err(Error::new(
ErrorKind::IO_ERROR,
"",
"".as_ref(),
"",
SourceSpan{offset: 0, line: 0},
format_args!("IO Error (status is not STATUS_PENDING): {:?}", nt_status),
)));
} else {
num_apcs_queued += 1;
files_data.push(Ok(std::ptr::slice_from_raw_parts_mut(buf.as_ptr().cast::<u8>(), cap)));
}
}
while num_apcs_queued != num_apcs_finished {
let result = unsafe{SleepEx(500, TRUE)};
const WAIT_IO_COMPLETION: DWORD = 0xC0;
if result == 0 {
panic!("File read took more than 500ms");
} else if result != WAIT_IO_COMPLETION {
panic!("Woken for invalid reason: {:x?}", result);
}
}
for (iosb, file_data) in iosbs.iter_mut().zip(&mut files_data) {
if unsafe{!iosb.status.status.is_success()} {
*file_data = Err(Error::new(
ErrorKind::IO_ERROR,
"",
"".as_ref(),
"",
SourceSpan{offset: 0, line: 0},
format_args!("IO Error (status is not STATUS_PENDING): {:?}", unsafe{iosb.status.status}),
));
}
let Ok(file_data) = file_data else {continue};
let new_ptr = std::ptr::slice_from_raw_parts_mut(file_data.cast::<u8>(), iosb.information as usize);
*file_data = new_ptr;
}
let mut ret_val = Vec::with_capacity_in(files.len(), arena);
ret_val.extend(files_data.into_iter().map(|data: Result<*mut [u8], Error>| {
data.map(|ptr| unsafe{&*ptr})
}));
ret_val
}
}
#[cfg(target_os="windows")]
pub use windows::*;
#[cfg(not(target_os = "windows"))]
mod fallback {
use crate::arena::Arena;
use crate::error::Error;
use std::fs::File;
use std::ffi::OsStr;
use std::io::Read;
use allocator_api2::vec::Vec;
pub fn open_file_async_for_read(path: &impl AsRef<OsStr>) -> Result<File, Error> {
let path = path.as_ref();
File::open(path).map_err(|err| Error::from_io_error(err, path))
}
pub fn read_files_async<'a, 'b>(files: impl IntoIterator<Item=&'b File>, arena: &'a Arena) -> Vec<Result<&'a [u8], Error>, &'a Arena> {
let mut files = {
let mut temp = Vec::new_in(arena);
temp.extend(files);
temp
};
let mut files_data = Vec::with_capacity_in(files.len(), arena);
for file in files.iter_mut() {
let size = file.metadata().expect("metadata always succeeds on windows").len();
let cap = if size > 0 {((((size - 1) / 4096) + 1) * 4096) as usize} else {0};
let buf = arena.alloc_zeroed(std::alloc::Layout::from_size_align(cap, 4096).unwrap()).unwrap();
let buf = unsafe{std::slice::from_raw_parts_mut(buf.cast::<u8>().as_ptr(), cap)};
match file.read(buf) {
Ok(size) => files_data.push(Ok(&buf[..size])),
Err(err) => files_data.push(Err(Error::from_io_error(err, "file_unknown (ig)".as_ref())))
}
}
files_data
}
}
#[cfg(not(target_os = "windows"))]
pub use fallback::*;