rustc_hashes/
lib.rs

1//! rustc encodes a lot of hashes. If hashes are stored as `u64` or `u128`, a `derive(Encodable)`
2//! will apply varint encoding to the hashes, which is less efficient than directly encoding the 8
3//! or 16 bytes of the hash. And if that hash depends on the `StableCrateHash` (which most in rustc
4//! do), the varint encoding will make the number of bytes encoded fluctuate between compiler
5//! versions.
6//!
7//! The types in this module represent 64-bit or 128-bit hashes produced by a `StableHasher`.
8//! `Hash64` and `Hash128` expose some utility functions to encourage users to not extract the inner
9//! hash value as an integer type and accidentally apply varint encoding to it.
10//!
11//! In contrast with `Fingerprint`, users of these types cannot and should not attempt to construct
12//! and decompose these types into constituent pieces. The point of these types is only to
13//! connect the fact that they can only be produced by a `StableHasher` to their
14//! `Encode`/`Decode` impls.
15
16use std::fmt;
17use std::ops::BitXorAssign;
18
19use rustc_stable_hash::{FromStableHash, SipHasher128Hash as StableHasherHash};
20
21/// A `u64` but encoded with a fixed size; for hashes this encoding is more compact than `u64`.
22#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
23pub struct Hash64 {
24    inner: u64,
25}
26
27impl Hash64 {
28    pub const ZERO: Hash64 = Hash64 { inner: 0 };
29
30    #[inline]
31    pub fn new(n: u64) -> Self {
32        Self { inner: n }
33    }
34
35    #[inline]
36    pub fn as_u64(self) -> u64 {
37        self.inner
38    }
39
40    #[inline]
41    pub fn wrapping_add(self, other: Self) -> Self {
42        Self { inner: self.inner.wrapping_add(other.inner) }
43    }
44}
45
46impl BitXorAssign<u64> for Hash64 {
47    #[inline]
48    fn bitxor_assign(&mut self, rhs: u64) {
49        self.inner ^= rhs;
50    }
51}
52
53impl FromStableHash for Hash64 {
54    type Hash = StableHasherHash;
55
56    #[inline]
57    fn from(StableHasherHash([_0, __1]): Self::Hash) -> Self {
58        Self { inner: _0 }
59    }
60}
61
62impl fmt::Debug for Hash64 {
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        self.inner.fmt(f)
65    }
66}
67
68impl fmt::LowerHex for Hash64 {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        fmt::LowerHex::fmt(&self.inner, f)
71    }
72}
73
74/// A `u128` but encoded with a fixed size; for hashes this encoding is more compact than `u128`.
75#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
76pub struct Hash128 {
77    inner: u128,
78}
79
80// We expect Hash128 to be well mixed. So there's no point in hashing both parts.
81//
82// This also allows using Hash128-containing types in UnHash-based hashmaps, which would otherwise
83// debug_assert! that we're hashing more than a single u64.
84impl std::hash::Hash for Hash128 {
85    fn hash<H: std::hash::Hasher>(&self, h: &mut H) {
86        h.write_u64(self.truncate().as_u64());
87    }
88}
89
90impl Hash128 {
91    #[inline]
92    pub fn new(n: u128) -> Self {
93        Self { inner: n }
94    }
95
96    #[inline]
97    pub fn truncate(self) -> Hash64 {
98        Hash64 { inner: self.inner as u64 }
99    }
100
101    #[inline]
102    pub fn wrapping_add(self, other: Self) -> Self {
103        Self { inner: self.inner.wrapping_add(other.inner) }
104    }
105
106    #[inline]
107    pub fn as_u128(self) -> u128 {
108        self.inner
109    }
110}
111
112impl FromStableHash for Hash128 {
113    type Hash = StableHasherHash;
114
115    #[inline]
116    fn from(StableHasherHash([_0, _1]): Self::Hash) -> Self {
117        Self { inner: u128::from(_0) | (u128::from(_1) << 64) }
118    }
119}
120
121impl fmt::Debug for Hash128 {
122    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123        self.inner.fmt(f)
124    }
125}
126
127impl fmt::LowerHex for Hash128 {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        fmt::LowerHex::fmt(&self.inner, f)
130    }
131}