rkyv/
hash.rs

1//! Hashing support for archived hash maps and sets.
2
3use core::{
4    hash::{Hash, Hasher},
5    ops::BitXor as _,
6};
7
8use crate::primitive::{FixedIsize, FixedUsize};
9
10/// A cross-platform 64-bit implementation of fxhash.
11#[derive(Default)]
12pub struct FxHasher64 {
13    hash: u64,
14}
15
16#[inline]
17fn hash_word(hash: u64, word: u64) -> u64 {
18    const ROTATE: u32 = 5;
19    const SEED: u64 = 0x51_7c_c1_b7_27_22_0a_95;
20
21    hash.rotate_left(ROTATE).bitxor(word).wrapping_mul(SEED)
22}
23
24#[inline]
25fn hash_bytes(mut hash: u64, bytes: &[u8]) -> u64 {
26    let ptr = bytes.as_ptr();
27    let len = bytes.len();
28
29    for i in 0..len / 8 {
30        let bytes = unsafe { ptr.cast::<[u8; 8]>().add(i).read_unaligned() };
31        hash = hash_word(hash, u64::from_le_bytes(bytes));
32    }
33
34    if bytes.len() & 4 != 0 {
35        let bytes = unsafe {
36            ptr.add(bytes.len() & !7).cast::<[u8; 4]>().read_unaligned()
37        };
38        hash = hash_word(hash, u32::from_le_bytes(bytes).into());
39    }
40
41    if bytes.len() & 2 != 0 {
42        let bytes = unsafe {
43            ptr.add(bytes.len() & !3).cast::<[u8; 2]>().read_unaligned()
44        };
45        hash = hash_word(hash, u16::from_le_bytes(bytes).into());
46    }
47
48    if bytes.len() & 1 != 0 {
49        let byte = unsafe { ptr.add(len - 1).read() };
50        hash = hash_word(hash, byte.into());
51    }
52
53    hash
54}
55
56impl Hasher for FxHasher64 {
57    #[inline]
58    fn write(&mut self, bytes: &[u8]) {
59        self.hash = hash_bytes(self.hash, bytes);
60    }
61
62    #[inline]
63    fn finish(&self) -> u64 {
64        self.hash
65    }
66
67    #[inline]
68    fn write_u8(&mut self, i: u8) {
69        self.hash = hash_word(self.hash, i as u64);
70    }
71
72    #[inline]
73    fn write_u16(&mut self, i: u16) {
74        self.hash = hash_word(self.hash, i as u64);
75    }
76
77    #[inline]
78    fn write_u32(&mut self, i: u32) {
79        self.hash = hash_word(self.hash, i as u64);
80    }
81
82    #[inline]
83    fn write_u64(&mut self, i: u64) {
84        self.hash = hash_word(self.hash, i);
85    }
86
87    #[inline]
88    fn write_u128(&mut self, i: u128) {
89        let bytes = i.to_ne_bytes();
90        let ptr = bytes.as_ptr().cast::<[u8; 8]>();
91        #[cfg(target_endian = "little")]
92        let (first, second) = (unsafe { ptr.read_unaligned() }, unsafe {
93            ptr.add(1).read_unaligned()
94        });
95        #[cfg(target_endian = "big")]
96        let (first, second) =
97            (unsafe { ptr.add(1).read_unaligned() }, unsafe {
98                ptr.read_unaligned()
99            });
100        self.hash = hash_word(
101            hash_word(self.hash, u64::from_ne_bytes(first)),
102            u64::from_ne_bytes(second),
103        );
104    }
105
106    #[inline]
107    fn write_usize(&mut self, i: usize) {
108        self.hash = hash_word(self.hash, i as FixedUsize as u64);
109    }
110
111    #[inline]
112    fn write_isize(&mut self, i: isize) {
113        self.write_i64(i as FixedIsize as i64)
114    }
115}
116
117/// Hashes the given value with the default value of the specified `Hasher`.
118pub fn hash_value<Q, H: Hasher + Default>(value: &Q) -> u64
119where
120    Q: Hash + ?Sized,
121{
122    let mut state = H::default();
123    value.hash(&mut state);
124    state.finish()
125}