pub(super) fn stable_content_hash(bytes: &[u8]) -> String {
format!("{:016x}", stable_hash64(bytes))
}
pub(super) fn stable_id<'a>(prefix: &str, parts: impl IntoIterator<Item = &'a str>) -> String {
let mut bytes = Vec::new();
for part in parts {
bytes.extend_from_slice(&(part.len() as u64).to_le_bytes());
bytes.extend_from_slice(part.as_bytes());
}
format!("{prefix}:{:016x}", stable_hash64(&bytes))
}
pub(super) fn stable_hash64(bytes: &[u8]) -> u64 {
const FNV_OFFSET_BASIS: u64 = 0xcbf29ce484222325;
const FNV_PRIME: u64 = 0x100000001b3;
let mut hash = FNV_OFFSET_BASIS;
for byte in bytes {
hash ^= u64::from(*byte);
hash = hash.wrapping_mul(FNV_PRIME);
}
hash
}