#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct SetDigest {
acc: u64,
count: u32,
}
fn mix64(mut x: u64) -> u64 {
x = x.wrapping_add(0x9E37_79B9_7F4A_7C15);
x = (x ^ (x >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
x = (x ^ (x >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
x ^ (x >> 31)
}
impl SetDigest {
pub fn new() -> Self {
Self::default()
}
pub fn insert(&mut self, key: u64) {
self.acc = self.acc.wrapping_add(mix64(key));
self.count = self.count.wrapping_add(1);
}
pub fn remove(&mut self, key: u64) {
self.acc = self.acc.wrapping_sub(mix64(key));
self.count = self.count.wrapping_sub(1);
}
pub fn from_keys<I: IntoIterator<Item = u64>>(keys: I) -> Self {
let mut d = Self::new();
for k in keys {
d.insert(k);
}
d
}
pub fn digest(&self) -> u64 {
self.acc ^ mix64(self.count as u64)
}
pub fn len(&self) -> u32 {
self.count
}
pub fn is_empty(&self) -> bool {
self.count == 0
}
pub fn clear(&mut self) {
self.acc = 0;
self.count = 0;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StateDigest(u64);
impl Default for StateDigest {
fn default() -> Self {
Self::new()
}
}
impl StateDigest {
pub fn new() -> Self {
Self(0xcbf2_9ce4_8422_2325)
}
pub fn write(&mut self, bytes: &[u8]) {
for &byte in bytes {
self.0 ^= byte as u64;
self.0 = self.0.wrapping_mul(0x100_0000_01b3);
}
}
pub fn write_u32(&mut self, value: u32) {
self.write(&value.to_le_bytes());
}
pub fn write_i32(&mut self, value: i32) {
self.write(&value.to_le_bytes());
}
pub fn write_u64(&mut self, value: u64) {
self.write(&value.to_le_bytes());
}
pub fn write_f32(&mut self, value: f32) {
self.write_u32(value.to_bits());
}
pub fn finish(&self) -> u64 {
self.0
}
}
#[cfg(test)]
mod state_tests {
use super::*;
#[test]
fn the_fold_is_fnv1a_and_pinned() {
assert_eq!(StateDigest::new().finish(), 0xcbf2_9ce4_8422_2325, "the offset basis");
let mut digest = StateDigest::new();
digest.write(b"a");
assert_eq!(digest.finish(), 0xaf63_dc4c_8601_ec8c);
}
#[test]
fn the_same_values_in_a_different_order_are_a_different_world() {
let mut ab = StateDigest::new();
ab.write_i32(1);
ab.write_i32(2);
let mut ba = StateDigest::new();
ba.write_i32(2);
ba.write_i32(1);
assert_ne!(ab.finish(), ba.finish());
}
#[test]
fn a_low_bit_of_a_float_moves_the_digest() {
let mut before = StateDigest::new();
before.write_f32(1.0);
let mut after = StateDigest::new();
after.write_f32(f32::from_bits(1.0f32.to_bits() + 1));
assert_ne!(before.finish(), after.finish());
}
}