const FNV_OFFSET_BASIS: u64 = 0xcbf29ce484222325;
const FNV_PRIME: u64 = 0x100000001b3;
pub const fn fnv1a(bytes: &[u8]) -> u64 {
let mut hash = FNV_OFFSET_BASIS;
let mut i = 0;
while i < bytes.len() {
hash ^= bytes[i] as u64;
hash = hash.wrapping_mul(FNV_PRIME);
i += 1;
}
hash
}
pub fn interface_hash(signatures: &[&str]) -> u64 {
let mut sorted: Vec<&str> = signatures.to_vec();
sorted.sort();
let combined = sorted.join("\n");
fnv1a(combined.as_bytes())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_input() {
assert_eq!(fnv1a(b""), FNV_OFFSET_BASIS);
}
#[test]
fn known_vector() {
let hash = fnv1a(b"fidius");
assert_ne!(hash, 0);
assert_eq!(hash, fnv1a(b"fidius"));
}
#[test]
fn order_independence() {
let a = interface_hash(&[
"process:&[u8],Value->Result<Vec<u8>,PluginError>",
"name:->String",
]);
let b = interface_hash(&[
"name:->String",
"process:&[u8],Value->Result<Vec<u8>,PluginError>",
]);
assert_eq!(a, b);
}
#[test]
fn sensitivity() {
let a = interface_hash(&["name:->String"]);
let b = interface_hash(&["name:->string"]); assert_ne!(a, b);
}
#[test]
fn different_signatures_differ() {
let a = interface_hash(&["foo:->i32"]);
let b = interface_hash(&["bar:->i32"]);
let c = interface_hash(&["foo:->u32"]);
assert_ne!(a, b);
assert_ne!(a, c);
assert_ne!(b, c);
}
}