pub const NO_PRESERVE: u64 = 0;
const FINGERPRINT_SEED: u64 = 0xcbf2_9ce4_8422_2325;
const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
#[must_use]
pub fn layout_fingerprint<S>() -> u64 {
let size = std::mem::size_of::<S>();
if size == 0 {
return NO_PRESERVE;
}
let mut hash = FINGERPRINT_SEED;
for b in std::any::type_name::<S>().bytes() {
hash ^= u64::from(b);
hash = hash.wrapping_mul(FNV_PRIME);
}
hash ^= (size as u64).wrapping_mul(0x9e37_79b9_7f4a_7c15);
hash = hash.rotate_left(17)
^ (std::mem::align_of::<S>() as u64).wrapping_mul(0xff51_afd7_ed55_8ccd);
if hash == NO_PRESERVE { 1 } else { hash }
}
#[must_use]
pub fn may_preserve(reloaded: u64, held: u64) -> bool {
reloaded != NO_PRESERVE && reloaded == held
}
#[cfg(test)]
mod tests {
use super::{NO_PRESERVE, layout_fingerprint, may_preserve};
#[test]
fn no_preserve_never_matches_itself() {
assert!(!may_preserve(NO_PRESERVE, NO_PRESERVE));
}
#[test]
fn no_preserve_never_matches_a_real_fingerprint() {
assert!(!may_preserve(NO_PRESERVE, 0x1234));
assert!(!may_preserve(0x1234, NO_PRESERVE));
}
#[test]
fn equal_real_fingerprints_preserve() {
assert!(may_preserve(0xdead_beef, 0xdead_beef));
}
#[test]
fn zero_sized_state_never_preserves() {
assert_eq!(layout_fingerprint::<()>(), NO_PRESERVE);
}
#[test]
fn distinct_types_get_distinct_fingerprints() {
assert_ne!(layout_fingerprint::<u32>(), layout_fingerprint::<u64>());
assert_ne!(
layout_fingerprint::<[u8; 4]>(),
layout_fingerprint::<[u8; 8]>()
);
assert_ne!(layout_fingerprint::<f64>(), layout_fingerprint::<i64>());
assert_ne!(layout_fingerprint::<u32>(), NO_PRESERVE);
}
#[test]
fn same_type_is_stable() {
assert_eq!(layout_fingerprint::<u64>(), layout_fingerprint::<u64>());
}
}