use core::ffi::{c_char, c_int, c_void};
use super::{CacheTopology, Level};
unsafe extern "C" {
fn sysctlbyname(
name: *const c_char,
oldp: *mut c_void,
oldlenp: *mut usize,
newp: *mut c_void,
newlen: usize,
) -> c_int;
}
fn sysctl_u64(name: &[u8]) -> Option<u64> {
debug_assert_eq!(name.last().copied(), Some(0), "name must be NUL-terminated");
let mut val: u64 = 0;
let mut len = core::mem::size_of::<u64>();
let rc = unsafe {
sysctlbyname(
name.as_ptr() as *const c_char,
(&mut val as *mut u64).cast::<c_void>(),
&mut len,
core::ptr::null_mut(),
0,
)
};
(rc == 0 && len > 0).then_some(val)
}
pub fn detect() -> Option<CacheTopology> {
let line = sysctl_u64(b"hw.cachelinesize\0").unwrap_or(64) as usize;
let l1 =
sysctl_u64(b"hw.perflevel0.l1dcachesize\0").or_else(|| sysctl_u64(b"hw.l1dcachesize\0"))?;
let l2 =
sysctl_u64(b"hw.perflevel0.l2cachesize\0").or_else(|| sysctl_u64(b"hw.l2cachesize\0"))?;
let l3 = sysctl_u64(b"hw.perflevel0.l3cachesize\0")
.or_else(|| sysctl_u64(b"hw.l3cachesize\0"))
.filter(|&b| b > 0);
let l2_shared = sysctl_u64(b"hw.perflevel0.cpusperl2\0")
.filter(|&c| c > 0)
.unwrap_or(1) as usize;
let lvl = |bytes: u64, assoc: usize, shared_by: usize| Level {
bytes: bytes as usize,
assoc,
line,
shared_by,
};
Some(CacheTopology {
l1d: lvl(l1, 8, 1),
l2: lvl(l2, 8, l2_shared),
l3: l3.map(|b| lvl(b, 16, 1)),
})
}