libcpuname 0.1.3

Identify CPU vendors, chips, and cores across multiple architectures
Documentation
use super::err::CoreNameError;
use super::Vendor;

/// # Sources
///
/// ## First-Party
///
/// - Intel maintains a list of their family/model pairings [in the Linux kernel](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/intel-family.h)
///     - Last checked 2025-08-01
/// - AMD maintains a map of family/model ranges to Zen versions [in the Linux kernel](https://github.com/torvalds/linux/blob/155a3c003e555a7300d156a5252c004c392ec6b0/arch/x86/kernel/cpu/amd.c#L477)
///     - Last checked 2025-07-15
///
/// ## Third-Party
///
/// - [Wikichip](https://wikichip.org) has pages with CPUID tables for the following vendors
///   (note that these tables have some inaccuracies and contain speculation):
///     - [Intel](https://en.wikichip.org/wiki/intel/cpuid) (last checked 2025-07-15)
///     - [AMD](https://en.wikichip.org/wiki/amd/cpuid) (last checked 2025-07-15)
///
/// ## Obsolete
///
/// - [CPU-World](https://www.cpu-world.com/) provides detailed information for chips ranging back to AMD K6, though the last update to the site was in 2022
///
pub const fn core_name(
    vendor: Vendor,
    family: u8,
    model: u8,
) -> Result<&'static str, CoreNameError> {
    debug_assert!(family <= 0x1E);

    match vendor {
        Vendor::Intel => intel_core_name(family, model),
        Vendor::Amd => amd_core_name(family, model),
        _ => Err(CoreNameError::NoKnownNamesForVendor(vendor)),
    }
}

const fn amd_core_name(family: u8, model: u8) -> Result<&'static str, CoreNameError> {
    match family {
        0x05 => match model {
            0x00..=0x05 => Ok("K5"),
            _ => Ok("K6"),
        },
        0x06 => Ok("K7"),
        0x0F => match model {
            0x00..=0x1F => Ok("Hammer rev. B-D"),
            _ => Ok("Hammer rev. E-G"),
        },
        0x10 => Ok("Greyhound"),
        0x11 => Ok("Griffin"),
        0x12 => Ok("Husky"),
        0x14 => Ok("Bobcat"),
        0x15 => match model {
            0x00..=0x01 => Ok("Bulldozer"),
            0x02..=0x1F => Ok("Piledriver"),
            0x30..=0x3F => Ok("Steamroller"),
            0x60..=0x7F => Ok("Excavator"),
            _ => Err(CoreNameError::NoKnownNamesForModel(model)),
        },
        0x16 => Ok("Jaguar"),
        0x17 => match model {
            0x00..=0x2F | 0x50..=0x5F => Ok("Zen 1"),
            0x30..=0x4F | 0x60..=0xAF => Ok("Zen 2"),
            _ => Err(CoreNameError::NoKnownNamesForModel(model)),
        },
        0x18 => Ok("Zen 1"),
        0x19 => match model {
            0x00..=0x0F | 0x20..=0x5F => Ok("Zen 3"),
            0x10..=0x1F | 0x60..=0xAF => Ok("Zen 4"),
            _ => Err(CoreNameError::NoKnownNamesForModel(model)),
        },
        0x1A => match model {
            0x00..=0x2F | 0x40..=0x4F | 0x60..=0x7F => Ok("Zen 5"),
            0x50..=0x5F | 0x90..=0xAF | 0xC0..=0xCF => Ok("Zen 6"),
            _ => Err(CoreNameError::NoKnownNamesForModel(model)),
        },
        _ => Err(CoreNameError::NoKnownNamesForFamily(family)),
    }
}

const fn intel_core_name(family: u8, model: u8) -> Result<&'static str, CoreNameError> {
    match family {
        0x05 => Ok("P5"),
        0x06 => match model {
            // Big cores
            0x00..=0x08 | 0x0A | 0x0B => Ok("P6"),
            0x09 | 0x0D | 0x0E => Ok("Mobile"),
            0x0F..=0x16 => Ok("Merom"),
            0x17 | 0x1D => Ok("Penryn"),
            0x1A | 0x1E | 0x1F | 0x2E => Ok("Nehalem"),
            0x25 | 0x2C | 0x2F => Ok("Westmere"),
            0x2A | 0x2D => Ok("Sandy Bridge"),
            0x3A | 0x3E => Ok("Ivy Bridge"),
            0x3C | 0x3F | 0x45 | 0x46 => Ok("Haswell"),
            0x3D | 0x47 | 0x4F | 0x56 => Ok("Broadwell"),
            0x4E | 0x55 | 0x5E | 0x8E | 0x9E | 0xA5 | 0xA6 => Ok("Skylake"),
            0x66 => Ok("Palm Cove"),
            0x6A | 0x6C | 0x7D | 0x7E | 0x9D => Ok("Sunny Cove"),
            0x8C | 0x8D => Ok("Willow Cove"),
            0xA7 => Ok("Cypress Cove"),
            0x8F => Ok("Golden Cove"),
            0xCF | 0xD7 => Ok("Raptor Cove"),
            0xAD | 0xAE => Ok("Redwood Cove"),

            // Hybrids
            0x8A => Ok("Sunny Cove + Tremont"),
            0x97 | 0x9A => Ok("Golden Cove + Gracemont"),
            0xB7 | 0xBA | 0xBF => Ok("Raptor Cove + Gracemont"),
            0xAA | 0xAC => Ok("Redwood Cove + Crestmont"),
            0xB5 | 0xBD | 0xC5 | 0xC6 => Ok("Lion Cove + Skymont"),
            0xCC => Ok("Cougar Cove + Darkmont"),

            // Little cores
            0x1C | 0x26 => Ok("Bonnell"),
            0x27 | 0x35 | 0x36 => Ok("Saltwell"),
            0x37 | 0x4A | 0x4D | 0x5A | 0x5D => Ok("Silvermont"),
            0x4C | 0x57 | 0x75 | 0x85 => Ok("Airmont"),
            0x5C | 0x5F => Ok("Goldmont"),
            0x7A => Ok("Goldmont Plus"),
            0x86 | 0x96 | 0x9C => Ok("Tremont"),
            0xBE => Ok("Gracemont"),
            0xAF | 0xB6 => Ok("Crestmont"),
            0xDD => Ok("Darkmont"),

            _ => Err(CoreNameError::NoKnownNamesForModel(model)),
        },
        0x0F => match model {
            0x00 | 0x01 => Ok("Willamette"),
            0x02 => Ok("Northwood"),
            0x03 | 0x04 => Ok("Prescott"),
            0x06 => Ok("Cedar Mill"),
            _ => Err(CoreNameError::NoKnownNamesForModel(model)),
        },
        0x12 => match model {
            0x01 | 0x03 => Ok("Coyote Cove + Arctic Wolf"),
            _ => Err(CoreNameError::NoKnownNamesForModel(model)),
        },
        0x13 => match model {
            0x01 => Ok("Panther Cove"),
            _ => Err(CoreNameError::NoKnownNamesForModel(model)),
        },
        _ => Err(CoreNameError::NoKnownNamesForFamily(family)),
    }
}

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

    #[test]
    #[should_panic]
    fn invalid_family() {
        let _ = core_name(Vendor::Intel, 0xFF, 0x4E);
    }

    #[test]
    fn no_matches_for_vendor() {
        assert!(core_name(Vendor::Sis, 0x00, 0x0F).is_err())
    }

    #[test]
    fn valid_inputs_never_panic() {
        Vendor::iter().for_each(|vendor| {
            for family in 0..=0x1E {
                for model in 0..=u8::MAX {
                    let _ = core_name(vendor, family, model);
                }
            }
        });
    }
}