blasoxide-cpuinfo 0.2.0

cpu info provider for blasoxide
Documentation
#![deny(warnings)]

#[derive(Debug, Clone, Copy)]
pub struct CpuInfo {
    pub l1_cache_size: usize,
    pub l2_cache_size: usize,
    pub l3_cache_size: usize,
    pub num_cpus: usize,
}

impl CpuInfo {
    #[cfg(target_os = "linux")]
    pub fn try_new() -> Result<CpuInfo, ()> {
        use libc::sysconf;

        unsafe {
            let l1_cache_size = sysconf(libc::_SC_LEVEL1_DCACHE_SIZE);
            if l1_cache_size <= 0 {
                return Err(());
            }

            let l2_cache_size = sysconf(libc::_SC_LEVEL2_CACHE_SIZE);
            if l2_cache_size <= 0 {
                return Err(());
            }

            let l3_cache_size = sysconf(libc::_SC_LEVEL3_CACHE_SIZE);
            if l3_cache_size <= 0 {
                return Err(());
            }

            let num_cpus = sysconf(libc::_SC_NPROCESSORS_CONF);
            if num_cpus <= 0 {
                return Err(());
            }

            Ok(CpuInfo {
                l1_cache_size: l1_cache_size as usize,
                l2_cache_size: l2_cache_size as usize,
                l3_cache_size: l3_cache_size as usize,
                num_cpus: num_cpus as usize,
            })
        }
    }

    #[cfg(target_os = "macos")]
    pub fn try_new() -> Result<CpuInfo, ()> {
        use std::{mem, ptr};
        use libc::{c_int, c_void, sysconf, sysctl};

        unsafe {
            let mut l1_cache_size: c_int = 0;
            let mut l2_cache_size: c_int = 0;
            let mut l3_cache_size: c_int = 0;
            let mut size = mem::size_of::<c_int>();

            const NAMELEN: libc::c_uint = 2;

            let mut name = [libc::CTL_HW, libc::HW_L1DCACHESIZE];
            if sysctl(
                name.as_mut_ptr(),
                NAMELEN,
                &mut l1_cache_size as *mut c_int as *mut c_void,
                &mut size,
                ptr::null_mut(),
                0,
            ) != 0
            {
                return Err(());
            }

            name[1] = libc::HW_L2CACHESIZE;
            if sysctl(
                name.as_mut_ptr(),
                NAMELEN,
                &mut l2_cache_size as *mut c_int as *mut c_void,
                &mut size,
                ptr::null_mut(),
                0,
            ) != 0
            {
                return Err(());
            }

            name[1] = libc::HW_L3CACHESIZE;
            if sysctl(
                name.as_mut_ptr(),
                NAMELEN,
                &mut l3_cache_size as *mut c_int as *mut c_void,
                &mut size,
                ptr::null_mut(),
                0,
            ) != 0
            {
                return Err(());
            }

            if l1_cache_size <= 0 || l2_cache_size <= 0 || l3_cache_size <= 0 {
                return Err(());
            }

            let num_cpus = sysconf(libc::_SC_NPROCESSORS_CONF);
            if num_cpus <= 0 {
                return Err(());
            }

            Ok(CpuInfo {
                l1_cache_size: l1_cache_size as usize,
                l2_cache_size: l2_cache_size as usize,
                l3_cache_size: l3_cache_size as usize,
                num_cpus: num_cpus as usize,
            })
        }
    }

    #[cfg(target_os = "windows")]
    pub fn try_new() -> Result<CpuInfo, ()> {
        use std::{mem, ptr};
        use winapi::shared::minwindef::{FALSE, TRUE};
        use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER;
        use winapi::um::errhandlingapi::GetLastError;
        use winapi::um::sysinfoapi::GetLogicalProcessorInformation;
        use winapi::um::winnt::{
            CacheData, RelationCache, RelationProcessorCore, SYSTEM_LOGICAL_PROCESSOR_INFORMATION,
        };

        unsafe {
            let mut buffer = Vec::new();
            let mut req_bytes = 0;
            let struct_size = mem::size_of::<SYSTEM_LOGICAL_PROCESSOR_INFORMATION>();

            if TRUE == GetLogicalProcessorInformation(ptr::null_mut(), &mut req_bytes) {
                return Err(());
            }

            if GetLastError() == ERROR_INSUFFICIENT_BUFFER {
                buffer.reserve(req_bytes as usize / struct_size);
                buffer.set_len(req_bytes as usize / struct_size);
            } else {
                return Err(());
            }

            if FALSE == GetLogicalProcessorInformation(buffer.as_mut_ptr(), &mut req_bytes) {
                return Err(());
            }

            let mut l1_cache_size = 0;
            let mut l2_cache_size = 0;
            let mut l3_cache_size = 0;
            let mut num_cpus = 0;

            for info in buffer {
                if info.Relationship == RelationCache {
                    let cache = info.u.Cache();
                    match cache.Level {
                        1 => {
                            if cache.Type == CacheData {
                                l1_cache_size = cache.Size
                            }
                        }
                        2 => l2_cache_size = cache.Size,
                        3 => l3_cache_size = cache.Size,
                        _ => {
                            return Err(());
                        }
                    }
                } else if info.Relationship == RelationProcessorCore {
                    num_cpus += 1;
                }
            }

            if l1_cache_size == 0
                || l2_cache_size == 0
                || l3_cache_size == 0
                || num_cpus == 0 {
                    return Err(());
            }

            Ok(CpuInfo {
                l1_cache_size: l1_cache_size as usize,
                l2_cache_size: l2_cache_size as usize,
                l3_cache_size: l3_cache_size as usize,
                num_cpus,
            })
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_create_cpu_info() {
        let _info = CpuInfo::try_new().unwrap();
    }
}