Update rustc-hash to 2.1.1
Test: m rust
Change-Id: Idcd7d374d159f41b891d428242974b72bfee7eb5
diff --git a/crates/rustc-hash/.android-checksum.json b/crates/rustc-hash/.android-checksum.json
index 15e5adb..6c5f2ab 100644
--- a/crates/rustc-hash/.android-checksum.json
+++ b/crates/rustc-hash/.android-checksum.json
@@ -1 +1 @@
-{"package":null,"files":{".cargo-checksum.json":"863157ed20c8babab30213f10c7e2da45edc7f67a1d19a50f483c1cc96546c7e","Android.bp":"0166b7f855b9216ba363f50cc0901df82242daf90fc9940361da77749b7482ed","CODE_OF_CONDUCT.md":"4b4a224205993952ead9065f08ba29827e3bf34f06730104085abc5221162303","Cargo.toml":"6561379ceb5564dc019039658d6e7667b17959a820686e64138c312cf461226f","LICENSE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-APACHE":"3c7cd2396b5b772507febd2615d3d5a55b80103845037df77c87ba6e64872f2c","LICENSE-MIT":"38620a3cfaeec97a9197e8c39e436ea7f0bc86699b1f1c35f1aa41785b6d4eac","METADATA":"69c51921bcdf20a3597d4183c3fc9ababe252f012f6239e553d4a4b676051d1e","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"ec6e2887fbb37bca13dcfa24fcce6af5811be324458f09417941e7264873b1bd","TEST_MAPPING":"c8e6af96c7e7d36ed743565ba1bc3dc62da4327124c995aeb7052f580ba00ae2","cargo_embargo.json":"aa45a963da01d3f018be316cd5b7646a5b413ce2611c5218f2914d2e8a9efd0e","src/lib.rs":"816d7101c898d699812d72e029f46f3bf8c0a09cc997afe2da2c5f8e0b07c0ef"}}
\ No newline at end of file
+{"package":null,"files":{".cargo-checksum.json":"3606361efc3fa51e1fac1e2eddaa93ff6c22c404759e863c628ea4379c47ed64","Android.bp":"66110e7c2b52939760daf5c31fe072085cc14e825a96bb076317355f8fafb71f","CHANGELOG.md":"bcdb80531bdc164feb011b8134f328b9720d36ad75a5bd6ad55a8f4ea782e01d","CODE_OF_CONDUCT.md":"96d570db292913a1a207285a5c381989e9422657cd6a129298e3bb8e4530d17c","Cargo.toml":"ba5aece204389a07158ceb3dd3bb1d5db1daf3893871ae0a42486d80791fc31e","LICENSE":"7cf57fcef46b57c6743c9a325624d9dfd451733414e287e3fb2e523dafd1270a","LICENSE-APACHE":"7cf57fcef46b57c6743c9a325624d9dfd451733414e287e3fb2e523dafd1270a","LICENSE-MIT":"541fad77c3da981323a20886680d345b71eaed9f4b52ba5aee16374b6eb18ea8","METADATA":"2c06464ed7913343b1a510b20c1f282c2ee16556a9b8f8049416ef760d33419c","MODULE_LICENSE_APACHE2":"0d6f8afa3940b7f06bebee651376d43bc8b0d5b437337be2696d30377451e93a","README.md":"791315d61e78ffdceef7ef433c52e9de90fb827ad53beba618b29f80895aff46","TEST_MAPPING":"c8e6af96c7e7d36ed743565ba1bc3dc62da4327124c995aeb7052f580ba00ae2","cargo_embargo.json":"aa45a963da01d3f018be316cd5b7646a5b413ce2611c5218f2914d2e8a9efd0e","src/lib.rs":"0303ff93a8b5126ce154f37c1778b74e5f3cc2bb4dc2f0e8848911d506251f87","src/random_state.rs":"55d634828094a6ee94c434bd752ed728fa8ea3893faacc69dfc492b8fe0747a1","src/seeded_state.rs":"153abf487b064a1e407218f16649449c209c9f133627e0194aa53fe699095df1"}}
\ No newline at end of file
diff --git a/crates/rustc-hash/.cargo-checksum.json b/crates/rustc-hash/.cargo-checksum.json
index 544af9f..1ca7109 100644
--- a/crates/rustc-hash/.cargo-checksum.json
+++ b/crates/rustc-hash/.cargo-checksum.json
@@ -1 +1 @@
-{"files":{"CODE_OF_CONDUCT.md":"edca092fde496419a9f1ba640048aa0270b62dfea576cd3175f0b53e3c230470","Cargo.toml":"647814b27b6fc4fbef1df70d796b53b723e776b68467372044e4182763007379","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"cac8197ac869d64a6efc26cab883a269392ae6db51f7453bca722f8f31d67c7c","src/lib.rs":"ddecafb5db609d0d8eebd19e4d98dc865e7e9282a4183421f9bd765c01a231c0"},"package":"08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"}
\ No newline at end of file
+{"files":{"CHANGELOG.md":"1c9951d52d63dfbff8d32ad7909761517db5dc8f9084dba7892da8d5028c9692","CODE_OF_CONDUCT.md":"3e77f5476805b69467641b2c682aa2355344395056939089182cd901c56dce63","Cargo.toml":"110004d0c56ebe79a83b2cbe44b1574a69010a9e4a8581e5215ac14251ef30cc","LICENSE-APACHE":"95bd3988beee069fa2848f648dab43cc6e0b2add2ad6bcb17360caf749802bcc","LICENSE-MIT":"30fefc3a7d6a0041541858293bcbea2dde4caa4c0a5802f996a7f7e8c0085652","README.md":"ccd7a15a2e2021dbbfd5b7f99a10666a64ac50f8d5d6926a858efdde724fb424","src/lib.rs":"6928d71e403482e0e6f3324fbcef23a731c9236a5315db829f4020991064c5fa","src/random_state.rs":"39063b702c38dc93b7a9039f19f4acfdc539acf1604584a87eeb43cca149ca7e","src/seeded_state.rs":"530ba6e25d766231cc7540f968d3e41c5af5a38d936542b407010b9d35746fd8"},"package":"357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"}
\ No newline at end of file
diff --git a/crates/rustc-hash/Android.bp b/crates/rustc-hash/Android.bp
index 3ccf3bf..6921938 100644
--- a/crates/rustc-hash/Android.bp
+++ b/crates/rustc-hash/Android.bp
@@ -18,9 +18,9 @@
host_supported: true,
crate_name: "rustc_hash",
cargo_env_compat: true,
- cargo_pkg_version: "1.1.0",
+ cargo_pkg_version: "2.1.1",
crate_root: "src/lib.rs",
- edition: "2015",
+ edition: "2021",
features: [
"default",
"std",
diff --git a/crates/rustc-hash/CHANGELOG.md b/crates/rustc-hash/CHANGELOG.md
new file mode 100644
index 0000000..d52aba0
--- /dev/null
+++ b/crates/rustc-hash/CHANGELOG.md
@@ -0,0 +1,32 @@
+# 2.1.1
+
+- Change the internal algorithm to better accomodate large hashmaps.
+ This mitigates a [regression with 2.0 in rustc](https://github.com/rust-lang/rust/issues/135477).
+ See [PR#55](https://github.com/rust-lang/rustc-hash/pull/55) for more details on the change (this PR was not merged).
+ This problem might be improved with changes to hashbrown in the future.
+
+## 2.1.0
+
+- Implement `Clone` for `FxRandomState`
+- Implement `Clone` for `FxSeededState`
+- Use SPDX license expression in license field
+
+## 2.0.0
+
+- Replace hash with faster and better finalized hash.
+ This replaces the previous "fxhash" algorithm originating in Firefox
+ with a custom hasher designed and implemented by Orson Peters ([`@orlp`](https://github.com/orlp)).
+ It was measured to have slightly better performance for rustc, has better theoretical properties
+ and also includes a significantly better string hasher.
+- Fix `no_std` builds
+
+## 1.2.0 (**YANKED**)
+
+**Note: This version has been yanked due to issues with the `no_std` feature!**
+
+- Add a `FxBuildHasher` unit struct
+- Improve documentation
+- Add seed API for supplying custom seeds other than 0
+- Add `FxRandomState` based on `rand` (behind the `rand` feature) for random seeds
+- Make many functions `const fn`
+- Implement `Clone` for `FxHasher` struct
diff --git a/crates/rustc-hash/CODE_OF_CONDUCT.md b/crates/rustc-hash/CODE_OF_CONDUCT.md
index d70b2b5..d6d7742 100644
--- a/crates/rustc-hash/CODE_OF_CONDUCT.md
+++ b/crates/rustc-hash/CODE_OF_CONDUCT.md
@@ -1,40 +1,3 @@
# The Rust Code of Conduct
-A version of this document [can be found online](https://www.rust-lang.org/conduct.html).
-
-## Conduct
-
-**Contact**: [rust-mods@rust-lang.org](mailto:rust-mods@rust-lang.org)
-
-* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic.
-* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all.
-* Please be kind and courteous. There's no need to be mean or rude.
-* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer.
-* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works.
-* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the <a href="http://citizencodeofconduct.org/">Citizen Code of Conduct</a>; if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups.
-* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [Rust moderation team][mod_team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back.
-* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.
-
-## Moderation
-
-
-These are the policies for upholding our community's standards of conduct. If you feel that a thread needs moderation, please contact the [Rust moderation team][mod_team].
-
-1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.)
-2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed.
-3. Moderators will first respond to such remarks with a warning.
-4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off.
-5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.
-6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology.
-7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, **in private**. Complaints about bans in-channel are not allowed.
-8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others.
-
-In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely.
-
-And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could've communicated better — remember that it's your responsibility to make your fellow Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust.
-
-The enforcement policies listed above apply to all official Rust venues; including official IRC channels (#rust, #rust-internals, #rust-tools, #rust-libs, #rustc, #rust-beginners, #rust-docs, #rust-community, #rust-lang, and #cargo); GitHub repositories under rust-lang, rust-lang-nursery, and rust-lang-deprecated; and all forums under rust-lang.org (users.rust-lang.org, internals.rust-lang.org). For other projects adopting the Rust Code of Conduct, please contact the maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion.
-
-*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).*
-
-[mod_team]: https://www.rust-lang.org/team.html#Moderation-team
+The Code of Conduct for this repository [can be found online](https://www.rust-lang.org/conduct.html).
\ No newline at end of file
diff --git a/crates/rustc-hash/Cargo.toml b/crates/rustc-hash/Cargo.toml
index 47330b7..a95ba3a 100644
--- a/crates/rustc-hash/Cargo.toml
+++ b/crates/rustc-hash/Cargo.toml
@@ -3,23 +3,47 @@
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
-# to registry (e.g., crates.io) dependencies
+# to registry (e.g., crates.io) dependencies.
#
-# If you believe there's an error in this file please file an
-# issue against the rust-lang/cargo repository. If you're
-# editing this file be aware that the upstream Cargo.toml
-# will likely look very different (and much more reasonable)
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
[package]
+edition = "2021"
name = "rustc-hash"
-version = "1.1.0"
+version = "2.1.1"
authors = ["The Rust Project Developers"]
-description = "speed, non-cryptographic hash used in rustc"
+build = false
+autolib = false
+autobins = false
+autoexamples = false
+autotests = false
+autobenches = false
+description = "A speedy, non-cryptographic hashing algorithm used by rustc"
readme = "README.md"
-keywords = ["hash", "fxhash", "rustc"]
-license = "Apache-2.0/MIT"
-repository = "https://github.com/rust-lang-nursery/rustc-hash"
+keywords = [
+ "hash",
+ "hasher",
+ "fxhash",
+ "rustc",
+]
+license = "Apache-2.0 OR MIT"
+repository = "https://github.com/rust-lang/rustc-hash"
[features]
default = ["std"]
+nightly = []
+rand = [
+ "dep:rand",
+ "std",
+]
std = []
+
+[lib]
+name = "rustc_hash"
+path = "src/lib.rs"
+
+[dependencies.rand]
+version = "0.8"
+optional = true
diff --git a/crates/rustc-hash/LICENSE-APACHE b/crates/rustc-hash/LICENSE-APACHE
index 16fe87b..a7e77cb 100644
--- a/crates/rustc-hash/LICENSE-APACHE
+++ b/crates/rustc-hash/LICENSE-APACHE
@@ -173,29 +173,4 @@
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
-END OF TERMS AND CONDITIONS
-
-APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
-Copyright [yyyy] [name of copyright owner]
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
+END OF TERMS AND CONDITIONS
\ No newline at end of file
diff --git a/crates/rustc-hash/LICENSE-MIT b/crates/rustc-hash/LICENSE-MIT
index 31aa793..468cd79 100644
--- a/crates/rustc-hash/LICENSE-MIT
+++ b/crates/rustc-hash/LICENSE-MIT
@@ -20,4 +20,4 @@
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
+DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/crates/rustc-hash/METADATA b/crates/rustc-hash/METADATA
index 857c91a..87ed0a3 100644
--- a/crates/rustc-hash/METADATA
+++ b/crates/rustc-hash/METADATA
@@ -1,17 +1,17 @@
name: "rustc-hash"
description: "speed, non-cryptographic hash used in rustc"
third_party {
- version: "1.1.0"
+ version: "2.1.1"
license_type: NOTICE
last_upgrade_date {
- year: 2020
- month: 3
- day: 31
+ year: 2025
+ month: 2
+ day: 6
}
homepage: "https://crates.io/crates/rustc-hash"
identifier {
type: "Archive"
- value: "https://static.crates.io/crates/rustc-hash/rustc-hash-1.1.0.crate"
- version: "1.1.0"
+ value: "https://static.crates.io/crates/rustc-hash/rustc-hash-2.1.1.crate"
+ version: "2.1.1"
}
}
diff --git a/crates/rustc-hash/README.md b/crates/rustc-hash/README.md
index e33057a..bcac345 100644
--- a/crates/rustc-hash/README.md
+++ b/crates/rustc-hash/README.md
@@ -3,21 +3,28 @@
[](https://crates.io/crates/rustc-hash)
[](https://docs.rs/rustc-hash)
-A speedy hash algorithm used within rustc. The hashmap in liballoc by
-default uses SipHash which isn't quite as speedy as we want. In the
-compiler we're not really worried about DOS attempts, so we use a fast
-non-cryptographic hash.
+A speedy, non-cryptographic hashing algorithm used by `rustc`.
+The [hash map in `std`](https://doc.rust-lang.org/std/collections/struct.HashMap.html) uses SipHash by default, which provides resistance against DOS attacks.
+These attacks aren't a concern in the compiler so we prefer to use a quicker,
+non-cryptographic hash algorithm.
-This is the same as the algorithm used by Firefox -- which is a
-homespun one not based on any widely-known algorithm -- though
-modified to produce 64-bit hash values instead of 32-bit hash
-values. It consistently out-performs an FNV-based hash within rustc
-itself -- the collision rate is similar or slightly worse than FNV,
-but the speed of the hash function itself is much higher because it
-works on up to 8 bytes at a time.
+The original hash algorithm provided by this crate was one taken from Firefox,
+hence the hasher it provides is called FxHasher. This name is kept for backwards
+compatibility, but the underlying hash has since been replaced. The current
+design for the hasher is a polynomial hash finished with a single bit rotation,
+together with a wyhash-inspired compression function for strings/slices, both
+designed by Orson Peters.
+
+For `rustc` we have tried many different hashing algorithms. Hashing speed is
+critical, especially for single integers. Spending more CPU cycles on a higher
+quality hash does not reduce hash collisions enough to make the compiler faster
+on real-world benchmarks.
## Usage
+This crate provides `FxHashMap` and `FxHashSet` as collections.
+They are simply type aliases for their `std::collection` counterparts using the Fx hasher.
+
```rust
use rustc_hash::FxHashMap;
@@ -27,12 +34,9 @@
### `no_std`
-This crate can be used as a `no_std` crate by disabling the `std`
-feature, which is on by default, as follows:
+The `std` feature is on by default to enable collections.
+It can be turned off in `Cargo.toml` like so:
```toml
-rustc-hash = { version = "1.0", default-features = false }
+rustc-hash = { version = "2.1", default-features = false }
```
-
-In this configuration, `FxHasher` is the only export, and the
-`FxHashMap`/`FxHashSet` type aliases are omitted.
diff --git a/crates/rustc-hash/src/lib.rs b/crates/rustc-hash/src/lib.rs
index ee9ad31..03117c9 100644
--- a/crates/rustc-hash/src/lib.rs
+++ b/crates/rustc-hash/src/lib.rs
@@ -1,14 +1,4 @@
-// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Fast, non-cryptographic hash used by rustc and Firefox.
+//! A speedy, non-cryptographic hashing algorithm used by `rustc`.
//!
//! # Example
//!
@@ -16,6 +6,7 @@
//! # #[cfg(feature = "std")]
//! # fn main() {
//! use rustc_hash::FxHashMap;
+//!
//! let mut map: FxHashMap<u32, u32> = FxHashMap::default();
//! map.insert(22, 44);
//! # }
@@ -24,88 +15,96 @@
//! ```
#![no_std]
+#![cfg_attr(feature = "nightly", feature(hasher_prefixfree_extras))]
#[cfg(feature = "std")]
extern crate std;
-use core::convert::TryInto;
+#[cfg(feature = "rand")]
+extern crate rand;
+
+#[cfg(feature = "rand")]
+mod random_state;
+
+mod seeded_state;
+
use core::default::Default;
-#[cfg(feature = "std")]
-use core::hash::BuildHasherDefault;
-use core::hash::Hasher;
-use core::mem::size_of;
-use core::ops::BitXor;
+use core::hash::{BuildHasher, Hasher};
#[cfg(feature = "std")]
use std::collections::{HashMap, HashSet};
-/// Type alias for a hashmap using the `fx` hash algorithm.
+/// Type alias for a hash map that uses the Fx hashing algorithm.
#[cfg(feature = "std")]
-pub type FxHashMap<K, V> = HashMap<K, V, BuildHasherDefault<FxHasher>>;
+pub type FxHashMap<K, V> = HashMap<K, V, FxBuildHasher>;
-/// Type alias for a hashmap using the `fx` hash algorithm.
+/// Type alias for a hash set that uses the Fx hashing algorithm.
#[cfg(feature = "std")]
-pub type FxHashSet<V> = HashSet<V, BuildHasherDefault<FxHasher>>;
+pub type FxHashSet<V> = HashSet<V, FxBuildHasher>;
+
+#[cfg(feature = "rand")]
+pub use random_state::{FxHashMapRand, FxHashSetRand, FxRandomState};
+
+pub use seeded_state::FxSeededState;
+#[cfg(feature = "std")]
+pub use seeded_state::{FxHashMapSeed, FxHashSetSeed};
/// A speedy hash algorithm for use within rustc. The hashmap in liballoc
/// by default uses SipHash which isn't quite as speedy as we want. In the
/// compiler we're not really worried about DOS attempts, so we use a fast
/// non-cryptographic hash.
///
-/// This is the same as the algorithm used by Firefox -- which is a homespun
-/// one not based on any widely-known algorithm -- though modified to produce
-/// 64-bit hash values instead of 32-bit hash values. It consistently
-/// out-performs an FNV-based hash within rustc itself -- the collision rate is
-/// similar or slightly worse than FNV, but the speed of the hash function
-/// itself is much higher because it works on up to 8 bytes at a time.
+/// The current implementation is a fast polynomial hash with a single
+/// bit rotation as a finishing step designed by Orson Peters.
+#[derive(Clone)]
pub struct FxHasher {
hash: usize,
}
-#[cfg(target_pointer_width = "32")]
-const K: usize = 0x9e3779b9;
+// One might view a polynomial hash
+// m[0] * k + m[1] * k^2 + m[2] * k^3 + ...
+// as a multilinear hash with keystream k[..]
+// m[0] * k[0] + m[1] * k[1] + m[2] * k[2] + ...
+// where keystream k just happens to be generated using a multiplicative
+// congruential pseudorandom number generator (MCG). For that reason we chose a
+// constant that was found to be good for a MCG in:
+// "Computationally Easy, Spectrally Good Multipliers for Congruential
+// Pseudorandom Number Generators" by Guy Steele and Sebastiano Vigna.
#[cfg(target_pointer_width = "64")]
-const K: usize = 0x517cc1b727220a95;
+const K: usize = 0xf1357aea2e62a9c5;
+#[cfg(target_pointer_width = "32")]
+const K: usize = 0x93d765dd;
+
+impl FxHasher {
+ /// Creates a `fx` hasher with a given seed.
+ pub const fn with_seed(seed: usize) -> FxHasher {
+ FxHasher { hash: seed }
+ }
+
+ /// Creates a default `fx` hasher.
+ pub const fn default() -> FxHasher {
+ FxHasher { hash: 0 }
+ }
+}
impl Default for FxHasher {
#[inline]
fn default() -> FxHasher {
- FxHasher { hash: 0 }
+ Self::default()
}
}
impl FxHasher {
#[inline]
fn add_to_hash(&mut self, i: usize) {
- self.hash = self.hash.rotate_left(5).bitxor(i).wrapping_mul(K);
+ self.hash = self.hash.wrapping_add(i).wrapping_mul(K);
}
}
impl Hasher for FxHasher {
#[inline]
- fn write(&mut self, mut bytes: &[u8]) {
- #[cfg(target_pointer_width = "32")]
- let read_usize = |bytes: &[u8]| u32::from_ne_bytes(bytes[..4].try_into().unwrap());
- #[cfg(target_pointer_width = "64")]
- let read_usize = |bytes: &[u8]| u64::from_ne_bytes(bytes[..8].try_into().unwrap());
-
- let mut hash = FxHasher { hash: self.hash };
- assert!(size_of::<usize>() <= 8);
- while bytes.len() >= size_of::<usize>() {
- hash.add_to_hash(read_usize(bytes) as usize);
- bytes = &bytes[size_of::<usize>()..];
- }
- if (size_of::<usize>() > 4) && (bytes.len() >= 4) {
- hash.add_to_hash(u32::from_ne_bytes(bytes[..4].try_into().unwrap()) as usize);
- bytes = &bytes[4..];
- }
- if (size_of::<usize>() > 2) && bytes.len() >= 2 {
- hash.add_to_hash(u16::from_ne_bytes(bytes[..2].try_into().unwrap()) as usize);
- bytes = &bytes[2..];
- }
- if (size_of::<usize>() > 1) && bytes.len() >= 1 {
- hash.add_to_hash(bytes[0] as usize);
- }
- self.hash = hash.hash;
+ fn write(&mut self, bytes: &[u8]) {
+ // Compress the byte string to a single u64 and add to our hash.
+ self.write_u64(hash_bytes(bytes));
}
#[inline]
@@ -123,17 +122,21 @@
self.add_to_hash(i as usize);
}
- #[cfg(target_pointer_width = "32")]
#[inline]
fn write_u64(&mut self, i: u64) {
self.add_to_hash(i as usize);
+ #[cfg(target_pointer_width = "32")]
self.add_to_hash((i >> 32) as usize);
}
- #[cfg(target_pointer_width = "64")]
#[inline]
- fn write_u64(&mut self, i: u64) {
+ fn write_u128(&mut self, i: u128) {
self.add_to_hash(i as usize);
+ #[cfg(target_pointer_width = "32")]
+ self.add_to_hash((i >> 32) as usize);
+ self.add_to_hash((i >> 64) as usize);
+ #[cfg(target_pointer_width = "32")]
+ self.add_to_hash((i >> 96) as usize);
}
#[inline]
@@ -141,8 +144,316 @@
self.add_to_hash(i);
}
+ #[cfg(feature = "nightly")]
+ #[inline]
+ fn write_length_prefix(&mut self, _len: usize) {
+ // Most cases will specialize hash_slice to call write(), which encodes
+ // the length already in a more efficient manner than we could here. For
+ // HashDoS-resistance you would still need to include this for the
+ // non-slice collection hashes, but for the purposes of rustc we do not
+ // care and do not wish to pay the performance penalty of mixing in len
+ // for those collections.
+ }
+
+ #[cfg(feature = "nightly")]
+ #[inline]
+ fn write_str(&mut self, s: &str) {
+ // Similarly here, write already encodes the length, so nothing special
+ // is needed.
+ self.write(s.as_bytes())
+ }
+
#[inline]
fn finish(&self) -> u64 {
- self.hash as u64
+ // Since we used a multiplicative hash our top bits have the most
+ // entropy (with the top bit having the most, decreasing as you go).
+ // As most hash table implementations (including hashbrown) compute
+ // the bucket index from the bottom bits we want to move bits from the
+ // top to the bottom. Ideally we'd rotate left by exactly the hash table
+ // size, but as we don't know this we'll choose 26 bits, giving decent
+ // entropy up until 2^26 table sizes. On 32-bit hosts we'll dial it
+ // back down a bit to 15 bits.
+
+ #[cfg(target_pointer_width = "64")]
+ const ROTATE: u32 = 26;
+ #[cfg(target_pointer_width = "32")]
+ const ROTATE: u32 = 15;
+
+ self.hash.rotate_left(ROTATE) as u64
+
+ // A bit reversal would be even better, except hashbrown also expects
+ // good entropy in the top 7 bits and a bit reverse would fill those
+ // bits with low entropy. More importantly, bit reversals are very slow
+ // on x86-64. A byte reversal is relatively fast, but still has a 2
+ // cycle latency on x86-64 compared to the 1 cycle latency of a rotate.
+ // It also suffers from the hashbrown-top-7-bit-issue.
+ }
+}
+
+// Nothing special, digits of pi.
+const SEED1: u64 = 0x243f6a8885a308d3;
+const SEED2: u64 = 0x13198a2e03707344;
+const PREVENT_TRIVIAL_ZERO_COLLAPSE: u64 = 0xa4093822299f31d0;
+
+#[inline]
+fn multiply_mix(x: u64, y: u64) -> u64 {
+ #[cfg(target_pointer_width = "64")]
+ {
+ // We compute the full u64 x u64 -> u128 product, this is a single mul
+ // instruction on x86-64, one mul plus one mulhi on ARM64.
+ let full = (x as u128) * (y as u128);
+ let lo = full as u64;
+ let hi = (full >> 64) as u64;
+
+ // The middle bits of the full product fluctuate the most with small
+ // changes in the input. This is the top bits of lo and the bottom bits
+ // of hi. We can thus make the entire output fluctuate with small
+ // changes to the input by XOR'ing these two halves.
+ lo ^ hi
+
+ // Unfortunately both 2^64 + 1 and 2^64 - 1 have small prime factors,
+ // otherwise combining with + or - could result in a really strong hash, as:
+ // x * y = 2^64 * hi + lo = (-1) * hi + lo = lo - hi, (mod 2^64 + 1)
+ // x * y = 2^64 * hi + lo = 1 * hi + lo = lo + hi, (mod 2^64 - 1)
+ // Multiplicative hashing is universal in a field (like mod p).
+ }
+
+ #[cfg(target_pointer_width = "32")]
+ {
+ // u64 x u64 -> u128 product is prohibitively expensive on 32-bit.
+ // Decompose into 32-bit parts.
+ let lx = x as u32;
+ let ly = y as u32;
+ let hx = (x >> 32) as u32;
+ let hy = (y >> 32) as u32;
+
+ // u32 x u32 -> u64 the low bits of one with the high bits of the other.
+ let afull = (lx as u64) * (hy as u64);
+ let bfull = (hx as u64) * (ly as u64);
+
+ // Combine, swapping low/high of one of them so the upper bits of the
+ // product of one combine with the lower bits of the other.
+ afull ^ bfull.rotate_right(32)
+ }
+}
+
+/// A wyhash-inspired non-collision-resistant hash for strings/slices designed
+/// by Orson Peters, with a focus on small strings and small codesize.
+///
+/// The 64-bit version of this hash passes the SMHasher3 test suite on the full
+/// 64-bit output, that is, f(hash_bytes(b) ^ f(seed)) for some good avalanching
+/// permutation f() passed all tests with zero failures. When using the 32-bit
+/// version of multiply_mix this hash has a few non-catastrophic failures where
+/// there are a handful more collisions than an optimal hash would give.
+///
+/// We don't bother avalanching here as we'll feed this hash into a
+/// multiplication after which we take the high bits, which avalanches for us.
+#[inline]
+fn hash_bytes(bytes: &[u8]) -> u64 {
+ let len = bytes.len();
+ let mut s0 = SEED1;
+ let mut s1 = SEED2;
+
+ if len <= 16 {
+ // XOR the input into s0, s1.
+ if len >= 8 {
+ s0 ^= u64::from_le_bytes(bytes[0..8].try_into().unwrap());
+ s1 ^= u64::from_le_bytes(bytes[len - 8..].try_into().unwrap());
+ } else if len >= 4 {
+ s0 ^= u32::from_le_bytes(bytes[0..4].try_into().unwrap()) as u64;
+ s1 ^= u32::from_le_bytes(bytes[len - 4..].try_into().unwrap()) as u64;
+ } else if len > 0 {
+ let lo = bytes[0];
+ let mid = bytes[len / 2];
+ let hi = bytes[len - 1];
+ s0 ^= lo as u64;
+ s1 ^= ((hi as u64) << 8) | mid as u64;
+ }
+ } else {
+ // Handle bulk (can partially overlap with suffix).
+ let mut off = 0;
+ while off < len - 16 {
+ let x = u64::from_le_bytes(bytes[off..off + 8].try_into().unwrap());
+ let y = u64::from_le_bytes(bytes[off + 8..off + 16].try_into().unwrap());
+
+ // Replace s1 with a mix of s0, x, and y, and s0 with s1.
+ // This ensures the compiler can unroll this loop into two
+ // independent streams, one operating on s0, the other on s1.
+ //
+ // Since zeroes are a common input we prevent an immediate trivial
+ // collapse of the hash function by XOR'ing a constant with y.
+ let t = multiply_mix(s0 ^ x, PREVENT_TRIVIAL_ZERO_COLLAPSE ^ y);
+ s0 = s1;
+ s1 = t;
+ off += 16;
+ }
+
+ let suffix = &bytes[len - 16..];
+ s0 ^= u64::from_le_bytes(suffix[0..8].try_into().unwrap());
+ s1 ^= u64::from_le_bytes(suffix[8..16].try_into().unwrap());
+ }
+
+ multiply_mix(s0, s1) ^ (len as u64)
+}
+
+/// An implementation of [`BuildHasher`] that produces [`FxHasher`]s.
+///
+/// ```
+/// use std::hash::BuildHasher;
+/// use rustc_hash::FxBuildHasher;
+/// assert_ne!(FxBuildHasher.hash_one(1), FxBuildHasher.hash_one(2));
+/// ```
+#[derive(Copy, Clone, Default)]
+pub struct FxBuildHasher;
+
+impl BuildHasher for FxBuildHasher {
+ type Hasher = FxHasher;
+ fn build_hasher(&self) -> FxHasher {
+ FxHasher::default()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ #[cfg(not(any(target_pointer_width = "64", target_pointer_width = "32")))]
+ compile_error!("The test suite only supports 64 bit and 32 bit usize");
+
+ use crate::{FxBuildHasher, FxHasher};
+ use core::hash::{BuildHasher, Hash, Hasher};
+
+ macro_rules! test_hash {
+ (
+ $(
+ hash($value:expr) == $result:expr,
+ )*
+ ) => {
+ $(
+ assert_eq!(FxBuildHasher.hash_one($value), $result);
+ )*
+ };
+ }
+
+ const B32: bool = cfg!(target_pointer_width = "32");
+
+ #[test]
+ fn unsigned() {
+ test_hash! {
+ hash(0_u8) == 0,
+ hash(1_u8) == if B32 { 3001993707 } else { 12157901119326311915 },
+ hash(100_u8) == if B32 { 3844759569 } else { 16751747135202103309 },
+ hash(u8::MAX) == if B32 { 999399879 } else { 1211781028898739645 },
+
+ hash(0_u16) == 0,
+ hash(1_u16) == if B32 { 3001993707 } else { 12157901119326311915 },
+ hash(100_u16) == if B32 { 3844759569 } else { 16751747135202103309 },
+ hash(u16::MAX) == if B32 { 3440503042 } else { 16279819243059860173 },
+
+ hash(0_u32) == 0,
+ hash(1_u32) == if B32 { 3001993707 } else { 12157901119326311915 },
+ hash(100_u32) == if B32 { 3844759569 } else { 16751747135202103309 },
+ hash(u32::MAX) == if B32 { 1293006356 } else { 7729994835221066939 },
+
+ hash(0_u64) == 0,
+ hash(1_u64) == if B32 { 275023839 } else { 12157901119326311915 },
+ hash(100_u64) == if B32 { 1732383522 } else { 16751747135202103309 },
+ hash(u64::MAX) == if B32 { 1017982517 } else { 6288842954450348564 },
+
+ hash(0_u128) == 0,
+ hash(1_u128) == if B32 { 1860738631 } else { 13032756267696824044 },
+ hash(100_u128) == if B32 { 1389515751 } else { 12003541609544029302 },
+ hash(u128::MAX) == if B32 { 2156022013 } else { 11702830760530184999 },
+
+ hash(0_usize) == 0,
+ hash(1_usize) == if B32 { 3001993707 } else { 12157901119326311915 },
+ hash(100_usize) == if B32 { 3844759569 } else { 16751747135202103309 },
+ hash(usize::MAX) == if B32 { 1293006356 } else { 6288842954450348564 },
+ }
+ }
+
+ #[test]
+ fn signed() {
+ test_hash! {
+ hash(i8::MIN) == if B32 { 2000713177 } else { 6684841074112525780 },
+ hash(0_i8) == 0,
+ hash(1_i8) == if B32 { 3001993707 } else { 12157901119326311915 },
+ hash(100_i8) == if B32 { 3844759569 } else { 16751747135202103309 },
+ hash(i8::MAX) == if B32 { 3293686765 } else { 12973684028562874344 },
+
+ hash(i16::MIN) == if B32 { 1073764727 } else { 14218860181193086044 },
+ hash(0_i16) == 0,
+ hash(1_i16) == if B32 { 3001993707 } else { 12157901119326311915 },
+ hash(100_i16) == if B32 { 3844759569 } else { 16751747135202103309 },
+ hash(i16::MAX) == if B32 { 2366738315 } else { 2060959061933882993 },
+
+ hash(i32::MIN) == if B32 { 16384 } else { 9943947977240134995 },
+ hash(0_i32) == 0,
+ hash(1_i32) == if B32 { 3001993707 } else { 12157901119326311915 },
+ hash(100_i32) == if B32 { 3844759569 } else { 16751747135202103309 },
+ hash(i32::MAX) == if B32 { 1293022740 } else { 16232790931690483559 },
+
+ hash(i64::MIN) == if B32 { 16384 } else { 33554432 },
+ hash(0_i64) == 0,
+ hash(1_i64) == if B32 { 275023839 } else { 12157901119326311915 },
+ hash(100_i64) == if B32 { 1732383522 } else { 16751747135202103309 },
+ hash(i64::MAX) == if B32 { 1017998901 } else { 6288842954483902996 },
+
+ hash(i128::MIN) == if B32 { 16384 } else { 33554432 },
+ hash(0_i128) == 0,
+ hash(1_i128) == if B32 { 1860738631 } else { 13032756267696824044 },
+ hash(100_i128) == if B32 { 1389515751 } else { 12003541609544029302 },
+ hash(i128::MAX) == if B32 { 2156005629 } else { 11702830760496630567 },
+
+ hash(isize::MIN) == if B32 { 16384 } else { 33554432 },
+ hash(0_isize) == 0,
+ hash(1_isize) == if B32 { 3001993707 } else { 12157901119326311915 },
+ hash(100_isize) == if B32 { 3844759569 } else { 16751747135202103309 },
+ hash(isize::MAX) == if B32 { 1293022740 } else { 6288842954483902996 },
+ }
+ }
+
+ // Avoid relying on any `Hash` implementations in the standard library.
+ struct HashBytes(&'static [u8]);
+ impl Hash for HashBytes {
+ fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
+ state.write(self.0);
+ }
+ }
+
+ #[test]
+ fn bytes() {
+ test_hash! {
+ hash(HashBytes(&[])) == if B32 { 2673204745 } else { 17606491139363777937 },
+ hash(HashBytes(&[0])) == if B32 { 2948228584 } else { 5448590020104574886 },
+ hash(HashBytes(&[0, 0, 0, 0, 0, 0])) == if B32 { 3223252423 } else { 16766921560080789783 },
+ hash(HashBytes(&[1])) == if B32 { 2943445104 } else { 5922447956811044110 },
+ hash(HashBytes(&[2])) == if B32 { 1055423297 } else { 5229781508510959783 },
+ hash(HashBytes(b"uwu")) == if B32 { 2699662140 } else { 7168164714682931527 },
+ hash(HashBytes(b"These are some bytes for testing rustc_hash.")) == if B32 { 2303640537 } else { 2349210501944688211 },
+ }
+ }
+
+ #[test]
+ fn with_seed_actually_different() {
+ let seeds = [
+ [1, 2],
+ [42, 17],
+ [124436707, 99237],
+ [usize::MIN, usize::MAX],
+ ];
+
+ for [a_seed, b_seed] in seeds {
+ let a = || FxHasher::with_seed(a_seed);
+ let b = || FxHasher::with_seed(b_seed);
+
+ for x in u8::MIN..=u8::MAX {
+ let mut a = a();
+ let mut b = b();
+
+ x.hash(&mut a);
+ x.hash(&mut b);
+
+ assert_ne!(a.finish(), b.finish())
+ }
+ }
}
}
diff --git a/crates/rustc-hash/src/random_state.rs b/crates/rustc-hash/src/random_state.rs
new file mode 100644
index 0000000..c8c35a0
--- /dev/null
+++ b/crates/rustc-hash/src/random_state.rs
@@ -0,0 +1,101 @@
+use std::collections::{HashMap, HashSet};
+
+use crate::FxHasher;
+
+/// Type alias for a hashmap using the `fx` hash algorithm with [`FxRandomState`].
+pub type FxHashMapRand<K, V> = HashMap<K, V, FxRandomState>;
+
+/// Type alias for a hashmap using the `fx` hash algorithm with [`FxRandomState`].
+pub type FxHashSetRand<V> = HashSet<V, FxRandomState>;
+
+/// `FxRandomState` is an alternative state for `HashMap` types.
+///
+/// A particular instance `FxRandomState` will create the same instances of
+/// [`Hasher`], but the hashers created by two different `FxRandomState`
+/// instances are unlikely to produce the same result for the same values.
+#[derive(Clone)]
+pub struct FxRandomState {
+ seed: usize,
+}
+
+impl FxRandomState {
+ /// Constructs a new `FxRandomState` that is initialized with random seed.
+ pub fn new() -> FxRandomState {
+ use rand::Rng;
+ use std::{cell::Cell, thread_local};
+
+ // This mirrors what `std::collections::hash_map::RandomState` does, as of 2024-01-14.
+ //
+ // Basically
+ // 1. Cache result of the rng in a thread local, so repeatedly
+ // creating maps is cheaper
+ // 2. Change the cached result on every creation, so maps created
+ // on the same thread don't have the same iteration order
+ thread_local!(static SEED: Cell<usize> = {
+ Cell::new(rand::thread_rng().gen())
+ });
+
+ SEED.with(|seed| {
+ let s = seed.get();
+ seed.set(s.wrapping_add(1));
+ FxRandomState { seed: s }
+ })
+ }
+}
+
+impl core::hash::BuildHasher for FxRandomState {
+ type Hasher = FxHasher;
+
+ fn build_hasher(&self) -> Self::Hasher {
+ FxHasher::with_seed(self.seed)
+ }
+}
+
+impl Default for FxRandomState {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::thread;
+
+ use crate::FxHashMapRand;
+
+ #[test]
+ fn cloned_random_states_are_equal() {
+ let a = FxHashMapRand::<&str, u32>::default();
+ let b = a.clone();
+
+ assert_eq!(a.hasher().seed, b.hasher().seed);
+ }
+
+ #[test]
+ fn random_states_are_different() {
+ let a = FxHashMapRand::<&str, u32>::default();
+ let b = FxHashMapRand::<&str, u32>::default();
+
+ // That's the whole point of them being random!
+ //
+ // N.B.: `FxRandomState` uses a thread-local set to a random value and then incremented,
+ // which means that this is *guaranteed* to pass :>
+ assert_ne!(a.hasher().seed, b.hasher().seed);
+ }
+
+ #[test]
+ fn random_states_are_different_cross_thread() {
+ // This is similar to the test above, but uses two different threads, so they both get
+ // completely random, unrelated values.
+ //
+ // This means that this test is technically flaky, but the probability of it failing is
+ // `1 / 2.pow(bit_size_of::<usize>())`. Or 1/1.7e19 for 64 bit platforms or 1/4294967295
+ // for 32 bit platforms. I suppose this is acceptable.
+ let a = FxHashMapRand::<&str, u32>::default();
+ let b = thread::spawn(|| FxHashMapRand::<&str, u32>::default())
+ .join()
+ .unwrap();
+
+ assert_ne!(a.hasher().seed, b.hasher().seed);
+ }
+}
diff --git a/crates/rustc-hash/src/seeded_state.rs b/crates/rustc-hash/src/seeded_state.rs
new file mode 100644
index 0000000..e841906
--- /dev/null
+++ b/crates/rustc-hash/src/seeded_state.rs
@@ -0,0 +1,76 @@
+use crate::FxHasher;
+
+/// Type alias for a hashmap using the `fx` hash algorithm with [`FxSeededState`].
+#[cfg(feature = "std")]
+pub type FxHashMapSeed<K, V> = std::collections::HashMap<K, V, FxSeededState>;
+
+/// Type alias for a hashmap using the `fx` hash algorithm with [`FxSeededState`].
+#[cfg(feature = "std")]
+pub type FxHashSetSeed<V> = std::collections::HashSet<V, FxSeededState>;
+
+/// [`FxSeededState`] is an alternative state for `HashMap` types, allowing to use [`FxHasher`] with a set seed.
+///
+/// ```
+/// # use std::collections::HashMap;
+/// use rustc_hash::FxSeededState;
+///
+/// let mut map = HashMap::with_hasher(FxSeededState::with_seed(12));
+/// map.insert(15, 610);
+/// assert_eq!(map[&15], 610);
+/// ```
+#[derive(Clone)]
+pub struct FxSeededState {
+ seed: usize,
+}
+
+impl FxSeededState {
+ /// Constructs a new `FxSeededState` that is initialized with a `seed`.
+ pub const fn with_seed(seed: usize) -> FxSeededState {
+ Self { seed }
+ }
+}
+
+impl core::hash::BuildHasher for FxSeededState {
+ type Hasher = FxHasher;
+
+ fn build_hasher(&self) -> Self::Hasher {
+ FxHasher::with_seed(self.seed)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use core::hash::BuildHasher;
+
+ use crate::FxSeededState;
+
+ #[test]
+ fn cloned_seeded_states_are_equal() {
+ let seed = 2;
+ let a = FxSeededState::with_seed(seed);
+ let b = a.clone();
+
+ assert_eq!(a.seed, b.seed);
+ assert_eq!(a.seed, seed);
+
+ assert_eq!(a.build_hasher().hash, b.build_hasher().hash);
+ }
+
+ #[test]
+ fn same_seed_produces_same_hasher() {
+ let seed = 1;
+ let a = FxSeededState::with_seed(seed);
+ let b = FxSeededState::with_seed(seed);
+
+ // The hashers should be the same, as they have the same seed.
+ assert_eq!(a.build_hasher().hash, b.build_hasher().hash);
+ }
+
+ #[test]
+ fn different_states_are_different() {
+ let a = FxSeededState::with_seed(1);
+ let b = FxSeededState::with_seed(2);
+
+ assert_ne!(a.build_hasher().hash, b.build_hasher().hash);
+ }
+}
diff --git a/pseudo_crate/Cargo.lock b/pseudo_crate/Cargo.lock
index 6864911..5b16360 100644
--- a/pseudo_crate/Cargo.lock
+++ b/pseudo_crate/Cargo.lock
@@ -394,7 +394,7 @@
"rusqlite",
"rustc-demangle",
"rustc-demangle-capi",
- "rustc-hash",
+ "rustc-hash 2.1.1",
"rustix",
"rustversion",
"ryu",
@@ -899,7 +899,7 @@
"proc-macro2 1.0.93",
"quote 1.0.38",
"regex",
- "rustc-hash",
+ "rustc-hash 1.1.0",
"shlex",
"which",
]
@@ -919,7 +919,7 @@
"proc-macro2 1.0.93",
"quote 1.0.38",
"regex",
- "rustc-hash",
+ "rustc-hash 1.1.0",
"shlex",
"syn 2.0.96",
]
@@ -942,7 +942,7 @@
"proc-macro2 1.0.93",
"quote 1.0.38",
"regex",
- "rustc-hash",
+ "rustc-hash 1.1.0",
"shlex",
"syn 2.0.96",
"which",
@@ -5267,6 +5267,12 @@
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
+name = "rustc-hash"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
+
+[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/pseudo_crate/Cargo.toml b/pseudo_crate/Cargo.toml
index 0faa577..179bbb0 100644
--- a/pseudo_crate/Cargo.toml
+++ b/pseudo_crate/Cargo.toml
@@ -303,7 +303,7 @@
rusqlite = "=0.32.1"
rustc-demangle = "=0.1.24"
rustc-demangle-capi = "=0.1.0"
-rustc-hash = "=1.1.0"
+rustc-hash = "=2.1.1"
rustix = "=0.38.31"
rustversion = "=1.0.19"
ryu = "=1.0.19"