#![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::{CpuFeatures, 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;
}
}
let _: PlpStatus = PlpStatus::Unknown;
info
}
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: detect_compile_time_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))
}
fn detect_compile_time_features() -> CpuFeatures {
let mut f = CpuFeatures::empty();
if cfg!(target_feature = "sse") {
f |= CpuFeatures::SSE;
}
if cfg!(target_feature = "sse2") {
f |= CpuFeatures::SSE2;
}
if cfg!(target_feature = "sse3") {
f |= CpuFeatures::SSE3;
}
if cfg!(target_feature = "ssse3") {
f |= CpuFeatures::SSSE3;
}
if cfg!(target_feature = "sse4.1") {
f |= CpuFeatures::SSE4_1;
}
if cfg!(target_feature = "sse4.2") {
f |= CpuFeatures::SSE4_2;
}
if cfg!(target_feature = "avx") {
f |= CpuFeatures::AVX;
}
if cfg!(target_feature = "avx2") {
f |= CpuFeatures::AVX2;
}
if cfg!(target_feature = "avx512f") {
f |= CpuFeatures::AVX512F;
}
if cfg!(target_feature = "aes") {
f |= CpuFeatures::AES;
}
if cfg!(target_feature = "pclmulqdq") {
f |= CpuFeatures::PCLMULQDQ;
}
if cfg!(target_feature = "neon") {
f |= CpuFeatures::NEON;
}
f
}
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);
}
}