use cbh_model::sanitize_segment;
use sha2::{Digest, Sha256};
const FINGERPRINT_VERSION: &str = "mk1";
const FINGERPRINT_HEX_LEN: usize = 16;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct HardwareProfile {
pub processors: usize,
pub memory_regions: usize,
pub cpu_brand: Option<String>,
}
fn canonical(profile: &HardwareProfile) -> String {
let brand = profile
.cpu_brand
.as_deref()
.map(normalize_brand)
.unwrap_or_default();
format!(
"{FINGERPRINT_VERSION}\nprocessors={}\nmemory_regions={}\ncpu_brand={brand}",
profile.processors, profile.memory_regions,
)
}
fn normalize_brand(brand: &str) -> String {
brand.split_whitespace().collect::<Vec<_>>().join(" ")
}
pub(crate) fn fingerprint(profile: &HardwareProfile) -> String {
use std::fmt::Write as _;
let mut hasher = Sha256::new();
hasher.update(canonical(profile).as_bytes());
let digest = hasher.finalize();
let bytes_needed = FINGERPRINT_HEX_LEN.div_ceil(2);
let mut hex = String::with_capacity(FINGERPRINT_HEX_LEN);
for byte in digest.iter().take(bytes_needed) {
write!(hex, "{byte:02x}").expect("writing to a String never fails");
}
hex.truncate(FINGERPRINT_HEX_LEN);
hex
}
#[must_use]
pub fn resolve_machine_key(override_key: Option<&str>, profile: &HardwareProfile) -> String {
match override_key {
Some(key) => sanitize_segment(key),
None => fingerprint(profile),
}
}
#[cfg_attr(test, mutants::skip)] pub(crate) async fn system_profile() -> HardwareProfile {
let hardware = many_cpus::SystemHardware::current();
HardwareProfile {
processors: hardware.max_processor_count(),
memory_regions: hardware.max_memory_region_count(),
cpu_brand: detect_cpu_brand().await,
}
}
#[cfg(target_os = "linux")]
#[cfg_attr(test, mutants::skip)] async fn detect_cpu_brand() -> Option<String> {
let text = tokio::fs::read_to_string("/proc/cpuinfo").await.ok()?;
parse_cpuinfo_brand(&text)
}
#[cfg(target_os = "windows")]
#[cfg_attr(test, mutants::skip)] #[expect(
clippy::unused_async,
reason = "uniform async signature across the per-platform detect_cpu_brand variants"
)]
async fn detect_cpu_brand() -> Option<String> {
std::env::var("PROCESSOR_IDENTIFIER")
.ok()
.map(|value| normalize_brand(&value))
.filter(|value| !value.is_empty())
}
#[cfg(target_os = "macos")]
#[cfg_attr(test, mutants::skip)] async fn detect_cpu_brand() -> Option<String> {
let output = tokio::process::Command::new("sysctl")
.args(["-n", "machdep.cpu.brand_string"])
.output()
.await
.ok()?;
if !output.status.success() {
return None;
}
parse_sysctl_brand(&String::from_utf8_lossy(&output.stdout))
}
#[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
#[cfg_attr(test, mutants::skip)] #[expect(
clippy::unused_async,
reason = "uniform async signature across the per-platform detect_cpu_brand variants"
)]
async fn detect_cpu_brand() -> Option<String> {
None
}
#[cfg(any(test, target_os = "linux"))]
fn parse_cpuinfo_brand(text: &str) -> Option<String> {
for line in text.lines() {
let Some((key, value)) = line.split_once(':') else {
continue;
};
if key.trim() == "model name" {
let brand = normalize_brand(value);
if !brand.is_empty() {
return Some(brand);
}
}
}
None
}
#[cfg(any(test, target_os = "macos"))]
fn parse_sysctl_brand(text: &str) -> Option<String> {
let brand = normalize_brand(text);
if brand.is_empty() { None } else { Some(brand) }
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
fn profile(processors: usize, memory_regions: usize, brand: Option<&str>) -> HardwareProfile {
HardwareProfile {
processors,
memory_regions,
cpu_brand: brand.map(ToOwned::to_owned),
}
}
#[test]
fn fingerprint_is_stable_for_a_fixed_profile() {
let key = fingerprint(&profile(8, 1, Some("Test CPU 3000")));
assert_eq!(key, "d3ddd69dcf3b84ea");
}
#[test]
fn fingerprint_is_sixteen_lowercase_hex_chars() {
let key = fingerprint(&profile(4, 2, None));
assert_eq!(key.len(), FINGERPRINT_HEX_LEN);
assert!(
key.chars()
.all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase()),
"{key}"
);
}
#[test]
fn distinct_factors_produce_distinct_fingerprints() {
let base = fingerprint(&profile(8, 1, Some("Brand A")));
assert_ne!(base, fingerprint(&profile(16, 1, Some("Brand A"))));
assert_ne!(base, fingerprint(&profile(8, 2, Some("Brand A"))));
assert_ne!(base, fingerprint(&profile(8, 1, Some("Brand B"))));
assert_ne!(base, fingerprint(&profile(8, 1, None)));
}
#[test]
fn brand_whitespace_differences_do_not_change_the_fingerprint() {
assert_eq!(
fingerprint(&profile(8, 1, Some("Intel Xeon E5"))),
fingerprint(&profile(8, 1, Some(" Intel Xeon E5 "))),
);
}
#[test]
fn canonical_is_versioned_and_ordered() {
let rendered = canonical(&profile(8, 1, Some("CPU X")));
assert_eq!(
rendered,
"mk1\nprocessors=8\nmemory_regions=1\ncpu_brand=CPU X"
);
}
#[test]
fn canonical_renders_absent_brand_as_empty() {
let rendered = canonical(&profile(2, 1, None));
assert!(rendered.ends_with("cpu_brand="), "{rendered}");
}
#[test]
fn normalize_brand_collapses_and_trims_whitespace() {
assert_eq!(normalize_brand(" a b\tc \n"), "a b c");
assert_eq!(normalize_brand(" "), "");
}
#[test]
fn resolve_machine_key_uses_fingerprint_without_override() {
let hardware = profile(8, 1, Some("CPU X"));
assert_eq!(resolve_machine_key(None, &hardware), fingerprint(&hardware));
}
#[test]
fn resolve_machine_key_prefers_sanitized_override() {
let hardware = profile(8, 1, Some("CPU X"));
assert_eq!(
resolve_machine_key(Some("ci/pool one"), &hardware),
"ci_pool_one"
);
}
#[test]
fn parse_cpuinfo_brand_reads_the_first_model_name() {
let text = "\
processor\t: 0
vendor_id\t: GenuineIntel
model name\t: Intel(R) Xeon(R) CPU E5-2673 v4 @ 2.30GHz
processor\t: 1
model name\t: Some Other Core
";
assert_eq!(
parse_cpuinfo_brand(text),
Some("Intel(R) Xeon(R) CPU E5-2673 v4 @ 2.30GHz".to_owned())
);
}
#[test]
fn parse_cpuinfo_brand_absent_yields_none() {
assert_eq!(parse_cpuinfo_brand("processor\t: 0\nflags\t: fpu\n"), None);
assert_eq!(parse_cpuinfo_brand(""), None);
}
#[test]
fn parse_cpuinfo_brand_blank_value_yields_none() {
assert_eq!(parse_cpuinfo_brand("model name\t: \n"), None);
}
#[test]
fn parse_sysctl_brand_normalizes_and_handles_empty() {
assert_eq!(
parse_sysctl_brand("Apple M2 Pro\n"),
Some("Apple M2 Pro".to_owned())
);
assert_eq!(parse_sysctl_brand(" \n"), None);
}
#[tokio::test]
#[cfg_attr(miri, ignore)] async fn system_profile_reports_at_least_one_processor() {
let hardware = system_profile().await;
assert!(hardware.processors >= 1, "{hardware:?}");
assert!(hardware.memory_regions >= 1, "{hardware:?}");
assert_eq!(fingerprint(&hardware).len(), FINGERPRINT_HEX_LEN);
}
}