libcpuname 0.1.3

Identify CPU vendors, chips, and cores across multiple architectures
Documentation
use err::{ChipNameError, CoreNameError, ParseVendorError};

mod chips;
mod cores;
/// Errors that can be produced by functions in this module.
pub mod err;

#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Debug)]
#[cfg_attr(test, derive(strum::EnumIter))]
#[non_exhaustive]
/// Known vendors of physical x86 CPUs.
///
/// Each value in this enum corresponds to a known x86 CPU vendor with one or more associated CPUID vendor strings.
/// All vendor strings are composed of 12 ASCII characters and may include leading and trailing whitespace.
/// The [`std::str::FromStr`] trait can be used to convert a CPUID vendor string into one of these enum values.
///
/// # Example
///
/// ```rust
/// # fn main() -> Result<(), libcpuname::x86::err::ParseVendorError> {
/// let vendor = "GenuineIntel".parse::<libcpuname::x86::Vendor>()?;
/// println!("vendor='{vendor}'");
/// # Ok(())
/// # }
pub enum Vendor {
    /// Corresponds to the American company [Intel Corporation](https://intel.com/).
    ///
    /// The primary CPUID vendor string for this company is `GenuineIntel`.
    /// Some genuine CPUs with the vendor string `GenuineIotel` have also been found.
    Intel,
    /// Corresponds to the American company [Advanced Micro Devices](https://amd.com/).
    ///
    /// The primary CPUID vendor string for this company is `AuthenticAMD`.
    /// Some very early engineering samples of the company's K5 CPUs are known to have used the string `AMD ISBETTER`.
    Amd,
    /// Corresponds to the now-defunct American companies Integrated Device Technology and Centaur Technology.
    /// Some CPUs from VIA and Zhaoxin also report this vendor.
    ///
    /// The only known CPUID vendor string for these two companies is `CentaurHauls`.
    Centaur,
    /// Corresponds to the now-defunct American company Cyrix Corporation.
    /// Some CPUs from IBM and STMicroelectronics also report this vendor.
    ///
    /// The only known CPUID vendor string for this company is `CyrixInstead`.
    Cyrix,
    /// Corresponds to the Taiwanese company [DMP Electronics](https://www.compactpc.com.tw/).
    ///
    /// The only known CPUID vendor string for this company is `Vortex86 SoC`.
    Dmp,
    /// Corresponds to the Chinese company [Hygon Information Technology](http://www.hygon.cn/).
    ///
    /// The only known CPUID vendor string for this company is `HygonGenuine`.
    Hygon,
    /// Corresponds to the Russian company [Moscow Center of SPARC Technologies](http://www.mcst.ru/).
    ///
    /// The only known CPUID vendor string for this company is `E2K MACHINE `.
    Mcst,
    /// Corresponds to the now-defunct American company National Semiconductor Corporation.
    ///
    /// The only known CPUID vendor string for this company is `Geode by NSC`.
    Nsc,
    /// Corresponds to the now-defunct American company NexGen.
    ///
    /// The only known CPUID vendor string for this company is `NexGenDriven`.
    Nexgen,
    /// Corresponds to the Taiwanese company [RDC Technology Co](https://www.rdc.com.tw/).
    ///
    /// The only known CPUID vendor string for this company is `Genuine  RDC`.
    Rdc,
    /// Corresponds to the now-defunct American company Rise Technology.
    ///
    /// The only known CPUID vendor string for this company is `RiseRiseRise`.
    Rise,
    /// Corresponds to the Taiwanese company [Silicon Integrated Systems](https://www.sis.com/).
    ///
    /// The only known CPUID vendor string for this company is `SiS SiS SiS `.
    Sis,
    /// Corresponds to the now-defunct American company Transmeta Corporation.
    ///
    /// The two known CPUID vendor strings for this company are `TransmetaCPU` and `GenuineTMx86`.
    Transmeta,
    /// Corresponds to the Taiwanese company [United Microelectronics Corporation](https://umc.com/).
    ///
    /// The only known CPUID vendor string for this company is `UMC UMC UMC `.
    Umc,
    /// Corresponds to the Taiwanese company [VIA Technologies](https://viatech.com/).
    ///
    /// The only known CPUID vendor string for this company is `VIA VIA VIA `.
    Via,
    /// Corresponds to the Chinese joint venture company [Shanghai Zhaoxin Semiconductor Co](https://www.zhaoxin.com/).
    ///
    /// The only known CPUID vendor string for this company is `  Shanghai  `.
    Zhaoxin,
}

impl core::fmt::Display for Vendor {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        let s = match self {
            Self::Intel => "Intel",
            Self::Amd => "AMD",
            Self::Centaur => "Centaur",
            Self::Cyrix => "Cyrix",
            Self::Dmp => "DMP",
            Self::Hygon => "Hygon",
            Self::Mcst => "MCST",
            Self::Nsc => "National Semiconductor",
            Self::Nexgen => "NexGen",
            Self::Rdc => "RDC",
            Self::Rise => "Rise",
            Self::Sis => "SiS",
            Self::Transmeta => "Transmeta",
            Self::Umc => "UMC",
            Self::Via => "VIA",
            Self::Zhaoxin => "Zhaoxin",
        };

        write!(f, "{s}")
    }
}

impl core::str::FromStr for Vendor {
    type Err = ParseVendorError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.len() != 12 {
            return Err(ParseVendorError::InvalidLength(s.len()));
        }

        if !s.is_ascii() {
            return Err(ParseVendorError::InvalidCharacters);
        }

        match s {
            "GenuineIntel" | "GenuineIotel" => Ok(Self::Intel),
            "AuthenticAMD" | "AMD ISBETTER" => Ok(Self::Amd),
            "CentaurHauls" => Ok(Self::Centaur),
            "CyrixInstead" => Ok(Self::Cyrix),
            "Vortex86 SoC" => Ok(Self::Dmp),
            "HygonGenuine" => Ok(Self::Hygon),
            "E2K MACHINE " => Ok(Self::Mcst),
            "NexGenDriven" => Ok(Self::Nexgen),
            "Geode by NSC" => Ok(Self::Nsc),
            "Genuine  RDC" => Ok(Self::Rdc),
            "RiseRiseRise" => Ok(Self::Rise),
            "SiS SiS SiS " => Ok(Self::Sis),
            "TransmetaCPU" | "GenuineTMx86" => Ok(Self::Transmeta),
            "UMC UMC UMC " => Ok(Self::Umc),
            "VIA VIA VIA " => Ok(Self::Via),
            "  Shanghai  " => Ok(Self::Zhaoxin),
            _ => s
                .as_bytes()
                .try_into()
                // InvalidLength should never be returned - we already checked the length
                .map_or(Err(ParseVendorError::InvalidLength(s.len())), |bytes| {
                    Err(ParseVendorError::NoMatch(bytes))
                }),
        }
    }
}

/// Find the codename of a CPU's chip based on its CPUID values.
///
/// This function will return a string with a static lifetime that represents the chip(s) that correspond to the input CPUID values.
/// Each parameter is dependent on the one before it - for example, a `stepping` value of `0x5` will return a different result if the `family` or `vendor` values change.
/// If the input values correspond to multiple chip names, the returned string will contain all matching chip names, each separated by the delimiter ` / ` (e.g. `Interlagos / Zambezi`).
/// Additionally, if the input values correspond to multiple variants of one chip (such as Broadwell-U and Broadwell-Y), the returned string will separate the variant markers with the delimiter `/` (e.g. `Broadwell-U/Y`).
///
/// This function is marked as `const` and may be evaluated at compile-time if the arguments are static.
///
/// # Errors
///
/// An error will be returned if the input could not be resolved to a known chip name.
///
/// # Example
///
/// ```rust
/// # use std::str::FromStr;
/// # fn main() -> Result<(), libcpuname::x86::err::Error> {
/// let vendor = libcpuname::x86::Vendor::from_str("GenuineIntel")?;
/// let chip = libcpuname::x86::chip_name(vendor, 0x06, 0x2A, 0x0)?;
/// println!("chip='{chip}'");
/// # Ok(())
/// # }
pub const fn chip_name(
    vendor: Vendor,
    family: u8,
    model: u8,
    stepping: u8,
) -> Result<&'static str, ChipNameError> {
    if family > 0x1E {
        return Err(ChipNameError::InvalidFamily(family));
    }

    if stepping > 0xF {
        return Err(ChipNameError::InvalidStepping(stepping));
    }

    chips::chip_name(vendor, family, model, stepping)
}

/// Find the codenames of a CPU's cores based on its CPUID values.
///
/// This function will return a string with a static lifetime that represents the core microarchitecture(s) that correspond to the input CPUID values.
/// Each parameter is dependent on the one before it - for example, a `model` value of `0x4F` will return a different result if the `family` or `vendor` values change.
/// If the input values refer to a hybrid CPU, the returned string will contain all core microarchitectures in the CPU, separated by the delimiter ` + ` (e.g. `Sunny Cove + Tremont`).
///
/// This function is marked as `const` and may be evaluated at compile-time if the arguments are static.
///
/// # Errors
///
/// An error will be returned if the input could not be resolved to a known core name.
///
/// # Example
///
/// ```rust
/// # use std::str::FromStr;
/// # fn main() -> Result<(), libcpuname::x86::err::Error> {
/// let vendor = libcpuname::x86::Vendor::from_str("GenuineIntel")?;
/// let core = libcpuname::x86::core_name(vendor, 0x06, 0x2A)?;
/// println!("uarch='{core}'");
/// # Ok(())
/// # }
pub const fn core_name(
    vendor: Vendor,
    family: u8,
    model: u8,
) -> Result<&'static str, CoreNameError> {
    if family > 0x1E {
        return Err(CoreNameError::InvalidFamily(family));
    }

    cores::core_name(vendor, family, model)
}

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

    #[test]
    fn parse_vendor() {
        assert!(matches!(
            "InvalidIntel".parse::<Vendor>(),
            Err(ParseVendorError::NoMatch(_))
        ));
        assert_eq!(
            "Invalid".parse::<Vendor>(),
            Err(ParseVendorError::InvalidLength(7))
        );
        assert_eq!(
            "InvalidI😳".parse::<Vendor>(),
            Err(ParseVendorError::InvalidCharacters)
        );
        assert_eq!("GenuineIntel".parse::<Vendor>(), Ok(Vendor::Intel));
        assert_eq!("AuthenticAMD".parse::<Vendor>(), Ok(Vendor::Amd));
    }

    #[test]
    fn vendor_display() {
        assert!(Vendor::iter().all(|it| !it.to_string().is_empty()));

        assert_eq!(Vendor::Intel.to_string(), "Intel");
        assert_eq!(Vendor::Amd.to_string(), "AMD");
    }

    #[test]
    fn chip_names() {
        assert_eq!(
            chip_name(Vendor::Intel, 0xFF, 0x0, 0x0),
            Err(ChipNameError::InvalidFamily(0xFF))
        );
        assert_eq!(
            chip_name(Vendor::Intel, 0x06, 0x4E, 0xFF),
            Err(ChipNameError::InvalidStepping(0xFF))
        );
        assert_eq!(chip_name(Vendor::Intel, 0x06, 0x4E, 0x0), Ok("Skylake-U/Y"));
        assert_eq!(chip_name(Vendor::Amd, 0x15, 0x70, 0x0), Ok("Stoney Ridge"));
    }

    #[test]
    fn core_names() {
        assert_eq!(
            core_name(Vendor::Intel, 0xFF, 0x0),
            Err(CoreNameError::InvalidFamily(0xFF))
        );
        assert_eq!(core_name(Vendor::Intel, 0x06, 0x4E), Ok("Skylake"));
        assert_eq!(core_name(Vendor::Amd, 0x15, 0x7F), Ok("Excavator"));
    }
}