#![cfg(target_os = "windows")]
use std::ffi::{OsStr, OsString};
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use std::path::Path;
use windows_sys::Win32::Storage::FileSystem::{
GetDiskFreeSpaceExW, GetDiskFreeSpaceW, GetVolumePathNameW,
};
use windows_sys::Win32::System::SystemInformation::{GlobalMemoryStatusEx, MEMORYSTATUSEX};
use super::PlpStatus;
use crate::hardware::cpu::CpuInfo;
use crate::hardware::drive::DriveInfo;
use crate::hardware::io_primitives::IoPrimitives;
use crate::hardware::memory::MemoryInfo;
pub(crate) fn probe_drive() -> DriveInfo {
let cwd = match std::env::current_dir() {
Ok(p) => p,
Err(_) => return DriveInfo::default(),
};
let mut info = DriveInfo::default();
let volume = match volume_path(&cwd) {
Some(v) => v,
None => return info,
};
let mut free_to_caller: u64 = 0;
let mut total: u64 = 0;
let mut free_total: u64 = 0;
let volume_w = wide(volume.as_os_str());
let ok = unsafe {
GetDiskFreeSpaceExW(
volume_w.as_ptr(),
&mut free_to_caller,
&mut total,
&mut free_total,
)
};
if ok != 0 {
info.total_bytes = total;
info.available_bytes = free_to_caller;
}
let mut sectors_per_cluster: u32 = 0;
let mut bytes_per_sector: u32 = 0;
let mut free_clusters: u32 = 0;
let mut total_clusters: u32 = 0;
let ok = unsafe {
GetDiskFreeSpaceW(
volume_w.as_ptr(),
&mut sectors_per_cluster,
&mut bytes_per_sector,
&mut free_clusters,
&mut total_clusters,
)
};
if ok != 0 && bytes_per_sector > 0 {
info.logical_sector = bytes_per_sector;
info.physical_sector = bytes_per_sector;
let cluster = bytes_per_sector.saturating_mul(sectors_per_cluster);
if cluster > 0 {
info.optimal_block = cluster;
}
}
info.plp = probe_plp_windows(&volume);
info
}
fn probe_plp_windows(volume: &OsString) -> PlpStatus {
use std::os::windows::ffi::OsStrExt;
use windows_sys::Win32::Foundation::{CloseHandle, GENERIC_READ, INVALID_HANDLE_VALUE};
use windows_sys::Win32::Storage::FileSystem::{
CreateFileW, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING,
};
use windows_sys::Win32::System::Ioctl::{
PropertyStandardQuery, StorageDeviceProperty, IOCTL_STORAGE_QUERY_PROPERTY,
STORAGE_PROPERTY_QUERY,
};
use windows_sys::Win32::System::IO::DeviceIoControl;
let s = volume.to_string_lossy();
let trimmed = s.trim_end_matches('\\');
let drive = trimmed.split('\\').next().unwrap_or("");
if drive.len() != 2 || !drive.ends_with(':') {
return PlpStatus::Unknown;
}
let device_path = format!(r"\\.\{drive}");
let wide_path: Vec<u16> = std::ffi::OsStr::new(&device_path)
.encode_wide()
.chain(std::iter::once(0))
.collect();
let handle = unsafe {
CreateFileW(
wide_path.as_ptr(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
std::ptr::null(),
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
std::ptr::null_mut(),
)
};
if handle == INVALID_HANDLE_VALUE {
return PlpStatus::Unknown;
}
let query = STORAGE_PROPERTY_QUERY {
PropertyId: StorageDeviceProperty,
QueryType: PropertyStandardQuery,
AdditionalParameters: [0],
};
let mut buf: Vec<u8> = vec![0u8; 1024];
let mut bytes_returned: u32 = 0;
let ok = unsafe {
DeviceIoControl(
handle,
IOCTL_STORAGE_QUERY_PROPERTY,
&query as *const _ as *const _,
std::mem::size_of::<STORAGE_PROPERTY_QUERY>() as u32,
buf.as_mut_ptr().cast(),
buf.len() as u32,
&mut bytes_returned,
std::ptr::null_mut(),
)
};
let result = if ok == 0 {
PlpStatus::Unknown
} else {
parse_device_descriptor(&buf)
};
let _ = unsafe { CloseHandle(handle) };
result
}
fn parse_device_descriptor(buf: &[u8]) -> PlpStatus {
use windows_sys::Win32::System::Ioctl::STORAGE_DEVICE_DESCRIPTOR;
if buf.len() < std::mem::size_of::<STORAGE_DEVICE_DESCRIPTOR>() {
return PlpStatus::Unknown;
}
let descriptor: &STORAGE_DEVICE_DESCRIPTOR =
unsafe { &*(buf.as_ptr() as *const STORAGE_DEVICE_DESCRIPTOR) };
let vendor = c_string_at_offset(buf, descriptor.VendorIdOffset as usize);
let model = c_string_at_offset(buf, descriptor.ProductIdOffset as usize);
crate::hardware::plp::lookup_table(&vendor, &model)
}
fn c_string_at_offset(buf: &[u8], offset: usize) -> String {
if offset == 0 || offset >= buf.len() {
return String::new();
}
let tail = &buf[offset..];
let nul = tail.iter().position(|&b| b == 0).unwrap_or(tail.len());
String::from_utf8_lossy(&tail[..nul]).trim().to_string()
}
fn volume_path(path: &Path) -> Option<OsString> {
let path_w = wide(path.as_os_str());
let mut buf: Vec<u16> = vec![0u16; 261];
let ok = unsafe { GetVolumePathNameW(path_w.as_ptr(), buf.as_mut_ptr(), buf.len() as u32) };
if ok == 0 {
return None;
}
let len = buf.iter().position(|&c| c == 0).unwrap_or(buf.len());
Some(OsString::from_wide(&buf[..len]))
}
fn wide(s: &OsStr) -> Vec<u16> {
let mut v: Vec<u16> = s.encode_wide().collect();
v.push(0);
v
}
pub(crate) fn probe_memory() -> MemoryInfo {
let mut status: MEMORYSTATUSEX = unsafe { std::mem::zeroed() };
status.dwLength = std::mem::size_of::<MEMORYSTATUSEX>() as u32;
let ok = unsafe { GlobalMemoryStatusEx(&mut status) };
if ok == 0 {
return MemoryInfo::default();
}
MemoryInfo {
total_bytes: status.ullTotalPhys,
available_bytes: status.ullAvailPhys,
}
}
pub(crate) fn probe_cpu() -> CpuInfo {
let cores_logical = std::thread::available_parallelism()
.map(|n| u32::try_from(n.get()).unwrap_or(u32::MAX))
.unwrap_or(1);
let cores_physical = probe_physical_cores().unwrap_or(cores_logical);
let (l1, l2, l3) = probe_cache_sizes().unwrap_or((0, 0, 0));
CpuInfo {
cores_logical,
cores_physical,
features: super::super::cpu::runtime_features(),
cache_l1: l1,
cache_l2: l2,
cache_l3: l3,
}
}
fn probe_physical_cores() -> Option<u32> {
use windows_sys::Win32::System::SystemInformation::{
GetLogicalProcessorInformationEx, RelationProcessorCore,
SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX,
};
let mut needed: u32 = 0;
unsafe {
let _ = GetLogicalProcessorInformationEx(
RelationProcessorCore,
std::ptr::null_mut(),
&mut needed,
);
}
if needed == 0 {
return None;
}
let mut buf: Vec<u8> = vec![0u8; needed as usize];
let ok = unsafe {
GetLogicalProcessorInformationEx(
RelationProcessorCore,
buf.as_mut_ptr() as *mut SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX,
&mut needed,
)
};
if ok == 0 {
return None;
}
let mut count = 0u32;
let mut offset = 0usize;
while offset < needed as usize {
let header = unsafe {
&*(buf.as_ptr().add(offset) as *const SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
};
if header.Relationship == RelationProcessorCore {
count += 1;
}
if header.Size == 0 {
break; }
offset += header.Size as usize;
}
if count == 0 {
None
} else {
Some(count)
}
}
fn probe_cache_sizes() -> Option<(usize, usize, usize)> {
use windows_sys::Win32::System::SystemInformation::{
GetLogicalProcessorInformationEx, RelationCache, SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX,
};
let mut needed: u32 = 0;
unsafe {
let _ = GetLogicalProcessorInformationEx(RelationCache, std::ptr::null_mut(), &mut needed);
}
if needed == 0 {
return None;
}
let mut buf: Vec<u8> = vec![0u8; needed as usize];
let ok = unsafe {
GetLogicalProcessorInformationEx(
RelationCache,
buf.as_mut_ptr() as *mut SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX,
&mut needed,
)
};
if ok == 0 {
return None;
}
let mut l1 = 0usize;
let mut l2 = 0usize;
let mut l3 = 0usize;
let mut offset = 0usize;
while offset < needed as usize {
let header = unsafe {
&*(buf.as_ptr().add(offset) as *const SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
};
if header.Relationship == RelationCache {
let cache = unsafe { &header.Anonymous.Cache };
let size = cache.CacheSize as usize;
match cache.Level {
1 => l1 = l1.max(size),
2 => l2 = l2.max(size),
3 => l3 = l3.max(size),
_ => {}
}
}
if header.Size == 0 {
break;
}
offset += header.Size as usize;
}
Some((l1, l2, l3))
}
pub(crate) fn probe_io_primitives() -> IoPrimitives {
IoPrimitives {
io_uring: false,
iocp: true,
kqueue: false,
nvme_passthrough: false, direct_io: true,
mmap: true,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_probe_memory_reports_non_zero_total() {
let m = probe_memory();
assert!(
m.total_bytes > 0,
"Windows GlobalMemoryStatusEx must report >0"
);
}
#[test]
fn test_probe_drive_reports_logical_sector() {
let info = probe_drive();
assert!(info.logical_sector >= 512);
}
#[test]
fn test_probe_cpu_reports_at_least_one_logical_core() {
let c = probe_cpu();
assert!(c.cores_logical >= 1);
assert!(c.cores_physical >= 1);
}
#[test]
fn test_probe_io_primitives_iocp_and_mmap_true() {
let p = probe_io_primitives();
assert!(p.iocp);
assert!(p.mmap);
assert!(!p.io_uring);
}
}