Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c4b514b

Browse files
committedOct 4, 2023
Move RandomState and DefaultHasher into std::hash, but in a sneaky way
1 parent 2bbb619 commit c4b514b

File tree

8 files changed

+262
-155
lines changed

8 files changed

+262
-155
lines changed
 

‎library/std/src/collections/hash/map.rs

Lines changed: 1 addition & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,14 @@ use self::Entry::*;
66
use hashbrown::hash_map as base;
77

88
use crate::borrow::Borrow;
9-
use crate::cell::Cell;
109
use crate::collections::TryReserveError;
1110
use crate::collections::TryReserveErrorKind;
1211
use crate::error::Error;
1312
use crate::fmt::{self, Debug};
1413
#[allow(deprecated)]
15-
use crate::hash::{BuildHasher, Hash, Hasher, SipHasher13};
14+
use crate::hash::{private::RandomState, BuildHasher, Hash};
1615
use crate::iter::FusedIterator;
1716
use crate::ops::Index;
18-
use crate::sys;
1917

2018
/// A [hash map] implemented with quadratic probing and SIMD lookup.
2119
///
@@ -3072,152 +3070,6 @@ where
30723070
}
30733071
}
30743072

3075-
/// `RandomState` is the default state for [`HashMap`] types.
3076-
///
3077-
/// A particular instance `RandomState` will create the same instances of
3078-
/// [`Hasher`], but the hashers created by two different `RandomState`
3079-
/// instances are unlikely to produce the same result for the same values.
3080-
///
3081-
/// # Examples
3082-
///
3083-
/// ```
3084-
/// use std::collections::HashMap;
3085-
/// use std::collections::hash_map::RandomState;
3086-
///
3087-
/// let s = RandomState::new();
3088-
/// let mut map = HashMap::with_hasher(s);
3089-
/// map.insert(1, 2);
3090-
/// ```
3091-
#[derive(Clone)]
3092-
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
3093-
pub struct RandomState {
3094-
k0: u64,
3095-
k1: u64,
3096-
}
3097-
3098-
impl RandomState {
3099-
/// Constructs a new `RandomState` that is initialized with random keys.
3100-
///
3101-
/// # Examples
3102-
///
3103-
/// ```
3104-
/// use std::collections::hash_map::RandomState;
3105-
///
3106-
/// let s = RandomState::new();
3107-
/// ```
3108-
#[inline]
3109-
#[allow(deprecated)]
3110-
// rand
3111-
#[must_use]
3112-
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
3113-
pub fn new() -> RandomState {
3114-
// Historically this function did not cache keys from the OS and instead
3115-
// simply always called `rand::thread_rng().gen()` twice. In #31356 it
3116-
// was discovered, however, that because we re-seed the thread-local RNG
3117-
// from the OS periodically that this can cause excessive slowdown when
3118-
// many hash maps are created on a thread. To solve this performance
3119-
// trap we cache the first set of randomly generated keys per-thread.
3120-
//
3121-
// Later in #36481 it was discovered that exposing a deterministic
3122-
// iteration order allows a form of DOS attack. To counter that we
3123-
// increment one of the seeds on every RandomState creation, giving
3124-
// every corresponding HashMap a different iteration order.
3125-
thread_local!(static KEYS: Cell<(u64, u64)> = {
3126-
Cell::new(sys::hashmap_random_keys())
3127-
});
3128-
3129-
KEYS.with(|keys| {
3130-
let (k0, k1) = keys.get();
3131-
keys.set((k0.wrapping_add(1), k1));
3132-
RandomState { k0, k1 }
3133-
})
3134-
}
3135-
}
3136-
3137-
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
3138-
impl BuildHasher for RandomState {
3139-
type Hasher = DefaultHasher;
3140-
#[inline]
3141-
#[allow(deprecated)]
3142-
fn build_hasher(&self) -> DefaultHasher {
3143-
DefaultHasher(SipHasher13::new_with_keys(self.k0, self.k1))
3144-
}
3145-
}
3146-
3147-
/// The default [`Hasher`] used by [`RandomState`].
3148-
///
3149-
/// The internal algorithm is not specified, and so it and its hashes should
3150-
/// not be relied upon over releases.
3151-
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
3152-
#[allow(deprecated)]
3153-
#[derive(Clone, Debug)]
3154-
pub struct DefaultHasher(SipHasher13);
3155-
3156-
impl DefaultHasher {
3157-
/// Creates a new `DefaultHasher`.
3158-
///
3159-
/// This hasher is not guaranteed to be the same as all other
3160-
/// `DefaultHasher` instances, but is the same as all other `DefaultHasher`
3161-
/// instances created through `new` or `default`.
3162-
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
3163-
#[inline]
3164-
#[allow(deprecated)]
3165-
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
3166-
#[must_use]
3167-
pub const fn new() -> DefaultHasher {
3168-
DefaultHasher(SipHasher13::new_with_keys(0, 0))
3169-
}
3170-
}
3171-
3172-
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
3173-
impl Default for DefaultHasher {
3174-
/// Creates a new `DefaultHasher` using [`new`].
3175-
/// See its documentation for more.
3176-
///
3177-
/// [`new`]: DefaultHasher::new
3178-
#[inline]
3179-
fn default() -> DefaultHasher {
3180-
DefaultHasher::new()
3181-
}
3182-
}
3183-
3184-
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
3185-
impl Hasher for DefaultHasher {
3186-
// The underlying `SipHasher13` doesn't override the other
3187-
// `write_*` methods, so it's ok not to forward them here.
3188-
3189-
#[inline]
3190-
fn write(&mut self, msg: &[u8]) {
3191-
self.0.write(msg)
3192-
}
3193-
3194-
#[inline]
3195-
fn write_str(&mut self, s: &str) {
3196-
self.0.write_str(s);
3197-
}
3198-
3199-
#[inline]
3200-
fn finish(&self) -> u64 {
3201-
self.0.finish()
3202-
}
3203-
}
3204-
3205-
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
3206-
impl Default for RandomState {
3207-
/// Constructs a new `RandomState`.
3208-
#[inline]
3209-
fn default() -> RandomState {
3210-
RandomState::new()
3211-
}
3212-
}
3213-
3214-
#[stable(feature = "std_debug", since = "1.16.0")]
3215-
impl fmt::Debug for RandomState {
3216-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3217-
f.debug_struct("RandomState").finish_non_exhaustive()
3218-
}
3219-
}
3220-
32213073
#[inline]
32223074
fn map_entry<'a, K: 'a, V: 'a>(raw: base::RustcEntry<'a, K, V>) -> Entry<'a, K, V> {
32233075
match raw {

‎library/std/src/collections/hash/map/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use super::Entry::{Occupied, Vacant};
22
use super::HashMap;
3-
use super::RandomState;
43
use crate::assert_matches::assert_matches;
54
use crate::cell::RefCell;
5+
use crate::hash::private::RandomState;
66
use crate::test_helpers::test_rng;
77
use rand::Rng;
88
use realstd::collections::TryReserveErrorKind::*;

‎library/std/src/collections/hash/set.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ use hashbrown::hash_set as base;
66
use crate::borrow::Borrow;
77
use crate::collections::TryReserveError;
88
use crate::fmt;
9-
use crate::hash::{BuildHasher, Hash};
9+
use crate::hash::{private::RandomState, BuildHasher, Hash};
1010
use crate::iter::{Chain, FusedIterator};
1111
use crate::ops::{BitAnd, BitOr, BitXor, Sub};
1212

13-
use super::map::{map_try_reserve_error, RandomState};
13+
use super::map::map_try_reserve_error;
1414

1515
/// A [hash set] implemented as a `HashMap` where the value is `()`.
1616
///

‎library/std/src/collections/hash/set/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use super::super::map::RandomState;
21
use super::HashSet;
32

3+
use crate::hash::private::RandomState;
44
use crate::panic::{catch_unwind, AssertUnwindSafe};
55
use crate::sync::atomic::{AtomicU32, Ordering};
66
use crate::sync::Arc;

‎library/std/src/collections/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,11 @@ pub mod hash_map {
439439
//! A hash map implemented with quadratic probing and SIMD lookup.
440440
#[stable(feature = "rust1", since = "1.0.0")]
441441
pub use super::hash::map::*;
442+
443+
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
444+
pub use crate::hash::private::DefaultHasher;
445+
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
446+
pub use crate::hash::private::RandomState;
442447
}
443448

444449
#[stable(feature = "rust1", since = "1.0.0")]

‎library/std/src/hash/mod.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//! Generic hashing support.
2+
//!
3+
//! This module provides a generic way to compute the [hash] of a value.
4+
//! Hashes are most commonly used with [`HashMap`] and [`HashSet`].
5+
//!
6+
//! [hash]: https://en.wikipedia.org/wiki/Hash_function
7+
//! [`HashMap`]: ../../std/collections/struct.HashMap.html
8+
//! [`HashSet`]: ../../std/collections/struct.HashSet.html
9+
//!
10+
//! The simplest way to make a type hashable is to use `#[derive(Hash)]`:
11+
//!
12+
//! # Examples
13+
//!
14+
//! ```rust
15+
//! use std::collections::hash_map::DefaultHasher;
16+
//! use std::hash::{Hash, Hasher};
17+
//!
18+
//! #[derive(Hash)]
19+
//! struct Person {
20+
//! id: u32,
21+
//! name: String,
22+
//! phone: u64,
23+
//! }
24+
//!
25+
//! let person1 = Person {
26+
//! id: 5,
27+
//! name: "Janet".to_string(),
28+
//! phone: 555_666_7777,
29+
//! };
30+
//! let person2 = Person {
31+
//! id: 5,
32+
//! name: "Bob".to_string(),
33+
//! phone: 555_666_7777,
34+
//! };
35+
//!
36+
//! assert!(calculate_hash(&person1) != calculate_hash(&person2));
37+
//!
38+
//! fn calculate_hash<T: Hash>(t: &T) -> u64 {
39+
//! let mut s = DefaultHasher::new();
40+
//! t.hash(&mut s);
41+
//! s.finish()
42+
//! }
43+
//! ```
44+
//!
45+
//! If you need more control over how a value is hashed, you need to implement
46+
//! the [`Hash`] trait:
47+
//!
48+
//! ```rust
49+
//! use std::collections::hash_map::DefaultHasher;
50+
//! use std::hash::{Hash, Hasher};
51+
//!
52+
//! struct Person {
53+
//! id: u32,
54+
//! # #[allow(dead_code)]
55+
//! name: String,
56+
//! phone: u64,
57+
//! }
58+
//!
59+
//! impl Hash for Person {
60+
//! fn hash<H: Hasher>(&self, state: &mut H) {
61+
//! self.id.hash(state);
62+
//! self.phone.hash(state);
63+
//! }
64+
//! }
65+
//!
66+
//! let person1 = Person {
67+
//! id: 5,
68+
//! name: "Janet".to_string(),
69+
//! phone: 555_666_7777,
70+
//! };
71+
//! let person2 = Person {
72+
//! id: 5,
73+
//! name: "Bob".to_string(),
74+
//! phone: 555_666_7777,
75+
//! };
76+
//!
77+
//! assert_eq!(calculate_hash(&person1), calculate_hash(&person2));
78+
//!
79+
//! fn calculate_hash<T: Hash>(t: &T) -> u64 {
80+
//! let mut s = DefaultHasher::new();
81+
//! t.hash(&mut s);
82+
//! s.finish()
83+
//! }
84+
//! ```
85+
#![stable(feature = "rust1", since = "1.0.0")]
86+
87+
pub(crate) mod private;
88+
89+
#[stable(feature = "rust1", since = "1.0.0")]
90+
pub use core::hash::*;

‎library/std/src/hash/private.rs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
//! This module exists to isolate [`RandomState`] and [`DefaultHasher`] outside of the
2+
//! [`collections`] module without actually publicly exporting them, so that parts of that
3+
//! implementation can more easily be moved to the [`alloc`] crate.
4+
//!
5+
//! Although its items are public and contain stability attributes, they can't actually be accessed
6+
//! outside this crate.
7+
//!
8+
//! [`collections`]: crate::collections
9+
#[allow(deprecated)]
10+
use super::{BuildHasher, Hasher, SipHasher13};
11+
use crate::cell::Cell;
12+
use crate::fmt;
13+
use crate::sys;
14+
15+
/// `RandomState` is the default state for [`HashMap`] types.
16+
///
17+
/// A particular instance `RandomState` will create the same instances of
18+
/// [`Hasher`], but the hashers created by two different `RandomState`
19+
/// instances are unlikely to produce the same result for the same values.
20+
///
21+
/// [`HashMap`]: crate::collections::HashMap
22+
///
23+
/// # Examples
24+
///
25+
/// ```
26+
/// use std::collections::HashMap;
27+
/// use std::collections::hash_map::RandomState;
28+
///
29+
/// let s = RandomState::new();
30+
/// let mut map = HashMap::with_hasher(s);
31+
/// map.insert(1, 2);
32+
/// ```
33+
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
34+
#[derive(Clone)]
35+
pub struct RandomState {
36+
k0: u64,
37+
k1: u64,
38+
}
39+
40+
impl RandomState {
41+
/// Constructs a new `RandomState` that is initialized with random keys.
42+
///
43+
/// # Examples
44+
///
45+
/// ```
46+
/// use std::collections::hash_map::RandomState;
47+
///
48+
/// let s = RandomState::new();
49+
/// ```
50+
#[inline]
51+
#[allow(deprecated)]
52+
// rand
53+
#[must_use]
54+
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
55+
pub fn new() -> RandomState {
56+
// Historically this function did not cache keys from the OS and instead
57+
// simply always called `rand::thread_rng().gen()` twice. In #31356 it
58+
// was discovered, however, that because we re-seed the thread-local RNG
59+
// from the OS periodically that this can cause excessive slowdown when
60+
// many hash maps are created on a thread. To solve this performance
61+
// trap we cache the first set of randomly generated keys per-thread.
62+
//
63+
// Later in #36481 it was discovered that exposing a deterministic
64+
// iteration order allows a form of DOS attack. To counter that we
65+
// increment one of the seeds on every RandomState creation, giving
66+
// every corresponding HashMap a different iteration order.
67+
thread_local!(static KEYS: Cell<(u64, u64)> = {
68+
Cell::new(sys::hashmap_random_keys())
69+
});
70+
71+
KEYS.with(|keys| {
72+
let (k0, k1) = keys.get();
73+
keys.set((k0.wrapping_add(1), k1));
74+
RandomState { k0, k1 }
75+
})
76+
}
77+
}
78+
79+
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
80+
impl BuildHasher for RandomState {
81+
type Hasher = DefaultHasher;
82+
#[inline]
83+
#[allow(deprecated)]
84+
fn build_hasher(&self) -> DefaultHasher {
85+
DefaultHasher(SipHasher13::new_with_keys(self.k0, self.k1))
86+
}
87+
}
88+
89+
/// The default [`Hasher`] used by [`RandomState`].
90+
///
91+
/// The internal algorithm is not specified, and so it and its hashes should
92+
/// not be relied upon over releases.
93+
#[allow(deprecated)]
94+
#[derive(Clone, Debug)]
95+
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
96+
pub struct DefaultHasher(SipHasher13);
97+
98+
impl DefaultHasher {
99+
/// Creates a new `DefaultHasher`.
100+
///
101+
/// This hasher is not guaranteed to be the same as all other
102+
/// `DefaultHasher` instances, but is the same as all other `DefaultHasher`
103+
/// instances created through `new` or `default`.
104+
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
105+
#[inline]
106+
#[allow(deprecated)]
107+
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
108+
#[must_use]
109+
pub const fn new() -> DefaultHasher {
110+
DefaultHasher(SipHasher13::new_with_keys(0, 0))
111+
}
112+
}
113+
114+
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
115+
impl Default for DefaultHasher {
116+
/// Creates a new `DefaultHasher` using [`new`].
117+
/// See its documentation for more.
118+
///
119+
/// [`new`]: DefaultHasher::new
120+
#[inline]
121+
fn default() -> DefaultHasher {
122+
DefaultHasher::new()
123+
}
124+
}
125+
126+
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
127+
impl Hasher for DefaultHasher {
128+
// The underlying `SipHasher13` doesn't override the other
129+
// `write_*` methods, so it's ok not to forward them here.
130+
131+
#[inline]
132+
fn write(&mut self, msg: &[u8]) {
133+
self.0.write(msg)
134+
}
135+
136+
#[inline]
137+
fn write_str(&mut self, s: &str) {
138+
self.0.write_str(s);
139+
}
140+
141+
#[inline]
142+
fn finish(&self) -> u64 {
143+
self.0.finish()
144+
}
145+
}
146+
147+
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
148+
impl Default for RandomState {
149+
/// Constructs a new `RandomState`.
150+
#[inline]
151+
fn default() -> RandomState {
152+
RandomState::new()
153+
}
154+
}
155+
156+
#[stable(feature = "std_debug", since = "1.16.0")]
157+
impl fmt::Debug for RandomState {
158+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159+
f.debug_struct("RandomState").finish_non_exhaustive()
160+
}
161+
}

‎library/std/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -493,8 +493,6 @@ pub use core::convert;
493493
pub use core::default;
494494
#[stable(feature = "futures_api", since = "1.36.0")]
495495
pub use core::future;
496-
#[stable(feature = "rust1", since = "1.0.0")]
497-
pub use core::hash;
498496
#[stable(feature = "core_hint", since = "1.27.0")]
499497
pub use core::hint;
500498
#[stable(feature = "i128", since = "1.26.0")]
@@ -564,6 +562,7 @@ pub mod env;
564562
pub mod error;
565563
pub mod ffi;
566564
pub mod fs;
565+
pub mod hash;
567566
pub mod io;
568567
pub mod net;
569568
pub mod num;

0 commit comments

Comments
 (0)
Please sign in to comment.