Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make RandomState a lazy singleton. Fixes #27243 #28916

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 40 additions & 7 deletions src/libstd/collections/hash/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use self::SearchResult::*;
use self::VacantEntryState::*;

use borrow::Borrow;
use cell::UnsafeCell;
use clone::Clone;
use cmp::{max, Eq, PartialEq};
use default::Default;
Expand All @@ -25,6 +26,7 @@ use ops::{Deref, FnMut, FnOnce, Index};
use option::Option::{self, Some, None};
use rand::{self, Rng};
use result::Result;
use sync::Once;

use super::table::{
self,
Expand Down Expand Up @@ -1599,28 +1601,48 @@ impl<'a, K, V, S> Extend<(&'a K, &'a V)> for HashMap<K, V, S>
/// instances are unlikely to produce the same result for the same values.
#[derive(Clone)]
#[unstable(feature = "hashmap_hasher",
reason = "hashing an hash maps may be altered",
reason = "hashing and hash maps may be altered",
issue = "27713")]
pub struct RandomState {
k0: u64,
k1: u64,
}

#[unstable(feature = "hashmap_hasher",
reason = "hashing an hash maps may be altered",
reason = "hashing and hash maps may be altered",
issue = "27713")]
impl RandomState {
/// Constructs a new `RandomState` that is initialized with random keys.
/// Constructs a new `RandomState`.
///
/// This is initialized with a random seed lazily, only once during the program's lifetime.
/// Subsequent calls to `RandomState::new` will return the same value.
#[inline]
#[allow(deprecated)] // rand
pub fn new() -> RandomState {
let mut r = rand::thread_rng();
RandomState { k0: r.gen(), k1: r.gen() }
static ONCE: Once = Once::new();

// This `SyncCell` is not actually `Sync` outside of this very specific scenario where
// access is synchronized by `ONCE`.
struct SyncCell(UnsafeCell<u64>);
unsafe impl Sync for SyncCell {}

static K0: SyncCell = SyncCell(UnsafeCell::new(0));
static K1: SyncCell = SyncCell(UnsafeCell::new(0));

unsafe {
ONCE.call_once(|| {
let mut r = rand::thread_rng();
*K0.0.get() = r.gen();
*K1.0.get() = r.gen();
});

RandomState { k0: *K0.0.get(), k1: *K1.0.get() }
}
}
}

#[unstable(feature = "hashmap_hasher",
reason = "hashing an hash maps may be altered",
reason = "hashing and hash maps may be altered",
issue = "27713")]
impl HashState for RandomState {
type Hasher = SipHasher;
Expand Down Expand Up @@ -1671,7 +1693,7 @@ impl<K, S, Q: ?Sized> super::Recover<Q> for HashMap<K, (), S>
mod test_map {
use prelude::v1::*;

use super::HashMap;
use super::{HashMap, RandomState};
use super::Entry::{Occupied, Vacant};
use iter::range_inclusive;
use cell::RefCell;
Expand Down Expand Up @@ -2371,4 +2393,15 @@ mod test_map {
assert_eq!(a[&2], "two");
assert_eq!(a[&3], "three");
}

#[test]
fn test_random_state() {
let state = RandomState::new();
assert!(state.k0 != 0);
assert!(state.k1 != 0);

let state2 = RandomState::new();
assert_eq!(state2.k0, state.k0);
assert_eq!(state2.k1, state.k1);
}
}