use crate::event::VehicleId;
pub const PARTITIONS: u64 = 1024;
pub fn fnv1a(bytes: &[u8]) -> u64 {
let mut hash = 0xcbf2_9ce4_8422_2325_u64;
for byte in bytes {
hash ^= u64::from(*byte);
hash = hash.wrapping_mul(0x0000_0100_0000_01b3);
}
hash
}
pub fn mix(mut x: u64) -> u64 {
x ^= x >> 30;
x = x.wrapping_mul(0xbf58_476d_1ce4_e5b9);
x ^= x >> 27;
x = x.wrapping_mul(0x94d0_49bb_1331_11eb);
x ^ (x >> 31)
}
pub fn partition_of(vehicle: VehicleId) -> u64 {
mix(vehicle.0) % PARTITIONS
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn matches_reference_vectors() {
assert_eq!(fnv1a(b""), 0xcbf2_9ce4_8422_2325);
assert_eq!(fnv1a(b"a"), 0xaf63_dc4c_8601_ec8c);
assert_eq!(fnv1a(b"vehicle-42"), 0xf4dc_ea25_6ede_2c6c);
assert_eq!(mix(0), 0);
assert_eq!(mix(1), 0x5692_161d_100b_05e5);
assert_eq!(mix(0xdead_beef), 0x4e06_2702_ec92_9eea);
assert_eq!(mix(u64::MAX), 0xb4d0_55fc_f2cb_bd7b);
assert_eq!(partition_of(VehicleId(1)), 485);
assert_eq!(partition_of(VehicleId(0xdead_beef)), 746);
assert_eq!(partition_of(VehicleId(u64::MAX)), 379);
}
#[test]
fn sequential_ids_spread_across_partitions() {
let per_partition = 100;
let mut counts = vec![0usize; PARTITIONS as usize];
for id in 0..(PARTITIONS * per_partition) {
counts[partition_of(VehicleId(id)) as usize] += 1;
}
for (partition, count) in counts.iter().enumerate() {
assert!(
(25..400).contains(count),
"partition {partition} took {count} of an expected ~{per_partition}"
);
}
}
}