use std::fmt;
pub fn artifact_fingerprint(bytes: &[u8]) -> u64 {
const OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
const PRIME: u64 = 0x0000_0100_0000_01b3;
let mut hash = OFFSET;
for byte in bytes {
hash = (hash ^ u64::from(*byte)).wrapping_mul(PRIME);
}
if hash == 0 {
1
} else {
hash
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FingerprintMismatch {
pub expected: u64,
pub actual: u64,
}
impl fmt::Display for FingerprintMismatch {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"artifact fingerprint mismatch: expected {:#018x}, found {:#018x} \
(stale or wrong artifact for this engine version?)",
self.expected, self.actual
)
}
}
impl std::error::Error for FingerprintMismatch {}
pub fn verify_artifact_fingerprint(bytes: &[u8], expected: u64) -> Result<(), FingerprintMismatch> {
let actual = artifact_fingerprint(bytes);
if actual == expected {
Ok(())
} else {
Err(FingerprintMismatch { expected, actual })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fingerprint_is_stable_and_nonzero() {
assert_eq!(
artifact_fingerprint(b"obadh"),
artifact_fingerprint(b"obadh")
);
assert_ne!(artifact_fingerprint(b""), 0);
assert_eq!(artifact_fingerprint(b""), 0xcbf2_9ce4_8422_2325);
}
#[test]
fn fingerprint_changes_on_any_byte_difference() {
assert_ne!(
artifact_fingerprint(b"bn.fst.v1"),
artifact_fingerprint(b"bn.fst.v2")
);
assert_ne!(
artifact_fingerprint(&[0, 1, 2]),
artifact_fingerprint(&[0, 2, 1])
);
}
#[test]
fn verify_passes_on_match_and_reports_both_sides_on_mismatch() {
let bytes = b"artifact bytes";
let good = artifact_fingerprint(bytes);
assert!(verify_artifact_fingerprint(bytes, good).is_ok());
let error = verify_artifact_fingerprint(bytes, good ^ 1).unwrap_err();
assert_eq!(error.expected, good ^ 1);
assert_eq!(error.actual, good);
}
}