Skip to content

Commit 7c4d8e9

Browse files
committed
auto merge of #15257 : erickt/rust/hashmap, r=alexcrichton
While `HashMap::new` and `HashMap::with_capacity` were being initialized with a random `SipHasher`, it turns out that `HashMap::from_iter` was just using the default instance of `SipHasher`, which wasn't randomized. This closes that bug, and also inlines some important methods.
2 parents 89259b3 + d90b71c commit 7c4d8e9

File tree

3 files changed

+121
-16
lines changed

3 files changed

+121
-16
lines changed

src/libstd/collections/hashmap.rs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,13 @@ use collections::{Collection, Mutable, Set, MutableSet, Map, MutableMap};
1616
use default::Default;
1717
use fmt::Show;
1818
use fmt;
19-
use hash::{Hash, Hasher, sip};
19+
use hash::{Hash, Hasher, RandomSipHasher};
2020
use iter::{Iterator, FilterMap, Chain, Repeat, Zip, Extendable};
2121
use iter::{range, range_inclusive, FromIterator};
2222
use iter;
2323
use mem::replace;
2424
use num;
2525
use option::{Some, None, Option};
26-
use rand::Rng;
27-
use rand;
2826
use result::{Ok, Err};
2927

3028
mod table {
@@ -733,7 +731,7 @@ impl DefaultResizePolicy {
733731
/// }
734732
/// ```
735733
#[deriving(Clone)]
736-
pub struct HashMap<K, V, H = sip::SipHasher> {
734+
pub struct HashMap<K, V, H = RandomSipHasher> {
737735
// All hashes are keyed on these values, to prevent hash collision attacks.
738736
hasher: H,
739737

@@ -1033,18 +1031,17 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> MutableMap<K, V> for HashMap<K, V, H>
10331031

10341032
}
10351033

1036-
impl<K: Hash + Eq, V> HashMap<K, V, sip::SipHasher> {
1034+
impl<K: Hash + Eq, V> HashMap<K, V, RandomSipHasher> {
10371035
/// Create an empty HashMap.
1038-
pub fn new() -> HashMap<K, V, sip::SipHasher> {
1036+
#[inline]
1037+
pub fn new() -> HashMap<K, V, RandomSipHasher> {
10391038
HashMap::with_capacity(INITIAL_CAPACITY)
10401039
}
10411040

10421041
/// Creates an empty hash map with the given initial capacity.
1043-
pub fn with_capacity(capacity: uint) -> HashMap<K, V, sip::SipHasher> {
1044-
let mut r = rand::task_rng();
1045-
let r0 = r.gen();
1046-
let r1 = r.gen();
1047-
let hasher = sip::SipHasher::new_with_keys(r0, r1);
1042+
#[inline]
1043+
pub fn with_capacity(capacity: uint) -> HashMap<K, V, RandomSipHasher> {
1044+
let hasher = RandomSipHasher::new();
10481045
HashMap::with_capacity_and_hasher(capacity, hasher)
10491046
}
10501047
}
@@ -1053,6 +1050,7 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> {
10531050
/// Creates an empty hashmap which will use the given hasher to hash keys.
10541051
///
10551052
/// The creates map has the default initial capacity.
1053+
#[inline]
10561054
pub fn with_hasher(hasher: H) -> HashMap<K, V, H> {
10571055
HashMap::with_capacity_and_hasher(INITIAL_CAPACITY, hasher)
10581056
}
@@ -1064,6 +1062,7 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> {
10641062
/// is designed to allow HashMaps to be resistant to attacks that
10651063
/// cause many collisions and very poor performance. Setting it
10661064
/// manually using this function can expose a DoS attack vector.
1065+
#[inline]
10671066
pub fn with_capacity_and_hasher(capacity: uint, hasher: H) -> HashMap<K, V, H> {
10681067
let cap = num::next_power_of_two(max(INITIAL_CAPACITY, capacity));
10691068
HashMap {
@@ -1489,7 +1488,7 @@ pub type SetMoveItems<K> =
14891488
/// HashMap where the value is (). As with the `HashMap` type, a `HashSet`
14901489
/// requires that the elements implement the `Eq` and `Hash` traits.
14911490
#[deriving(Clone)]
1492-
pub struct HashSet<T, H = sip::SipHasher> {
1491+
pub struct HashSet<T, H = RandomSipHasher> {
14931492
map: HashMap<T, (), H>
14941493
}
14951494

@@ -1529,15 +1528,17 @@ impl<T: Eq + Hash<S>, S, H: Hasher<S>> MutableSet<T> for HashSet<T, H> {
15291528
fn remove(&mut self, value: &T) -> bool { self.map.remove(value) }
15301529
}
15311530

1532-
impl<T: Hash + Eq> HashSet<T, sip::SipHasher> {
1531+
impl<T: Hash + Eq> HashSet<T, RandomSipHasher> {
15331532
/// Create an empty HashSet
1534-
pub fn new() -> HashSet<T, sip::SipHasher> {
1533+
#[inline]
1534+
pub fn new() -> HashSet<T, RandomSipHasher> {
15351535
HashSet::with_capacity(INITIAL_CAPACITY)
15361536
}
15371537

15381538
/// Create an empty HashSet with space for at least `n` elements in
15391539
/// the hash table.
1540-
pub fn with_capacity(capacity: uint) -> HashSet<T, sip::SipHasher> {
1540+
#[inline]
1541+
pub fn with_capacity(capacity: uint) -> HashSet<T, RandomSipHasher> {
15411542
HashSet { map: HashMap::with_capacity(capacity) }
15421543
}
15431544
}
@@ -1547,6 +1548,7 @@ impl<T: Eq + Hash<S>, S, H: Hasher<S>> HashSet<T, H> {
15471548
/// keys.
15481549
///
15491550
/// The hash set is also created with the default initial capacity.
1551+
#[inline]
15501552
pub fn with_hasher(hasher: H) -> HashSet<T, H> {
15511553
HashSet::with_capacity_and_hasher(INITIAL_CAPACITY, hasher)
15521554
}
@@ -1558,6 +1560,7 @@ impl<T: Eq + Hash<S>, S, H: Hasher<S>> HashSet<T, H> {
15581560
/// is designed to allow `HashSet`s to be resistant to attacks that
15591561
/// cause many collisions and very poor performance. Setting it
15601562
/// manually using this function can expose a DoS attack vector.
1563+
#[inline]
15611564
pub fn with_capacity_and_hasher(capacity: uint, hasher: H) -> HashSet<T, H> {
15621565
HashSet { map: HashMap::with_capacity_and_hasher(capacity, hasher) }
15631566
}

src/libstd/hash.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
/*!
12+
* Generic hashing support.
13+
*
14+
* This module provides a generic way to compute the hash of a value. The
15+
* simplest way to make a type hashable is to use `#[deriving(Hash)]`:
16+
*
17+
* # Example
18+
*
19+
* ```rust
20+
* use std::hash;
21+
* use std::hash::Hash;
22+
*
23+
* #[deriving(Hash)]
24+
* struct Person {
25+
* id: uint,
26+
* name: String,
27+
* phone: u64,
28+
* }
29+
*
30+
* let person1 = Person { id: 5, name: "Janet".to_string(), phone: 555_666_7777 };
31+
* let person2 = Person { id: 5, name: "Bob".to_string(), phone: 555_666_7777 };
32+
*
33+
* assert!(hash::hash(&person1) != hash::hash(&person2));
34+
* ```
35+
*
36+
* If you need more control over how a value is hashed, you need to implement
37+
* the trait `Hash`:
38+
*
39+
* ```rust
40+
* use std::hash;
41+
* use std::hash::Hash;
42+
* use std::hash::sip::SipState;
43+
*
44+
* struct Person {
45+
* id: uint,
46+
* name: String,
47+
* phone: u64,
48+
* }
49+
*
50+
* impl Hash for Person {
51+
* fn hash(&self, state: &mut SipState) {
52+
* self.id.hash(state);
53+
* self.phone.hash(state);
54+
* }
55+
* }
56+
*
57+
* let person1 = Person { id: 5, name: "Janet".to_string(), phone: 555_666_7777 };
58+
* let person2 = Person { id: 5, name: "Bob".to_string(), phone: 555_666_7777 };
59+
*
60+
* assert!(hash::hash(&person1) == hash::hash(&person2));
61+
* ```
62+
*/
63+
64+
pub use core_collections::hash::{Hash, Hasher, Writer, hash, sip};
65+
66+
use default::Default;
67+
use rand::Rng;
68+
use rand;
69+
70+
/// `RandomSipHasher` computes the SipHash algorithm from a stream of bytes
71+
/// initialized with random keys.
72+
#[deriving(Clone)]
73+
pub struct RandomSipHasher {
74+
hasher: sip::SipHasher,
75+
}
76+
77+
impl RandomSipHasher {
78+
/// Construct a new `RandomSipHasher` that is initialized with random keys.
79+
#[inline]
80+
pub fn new() -> RandomSipHasher {
81+
let mut r = rand::task_rng();
82+
let r0 = r.gen();
83+
let r1 = r.gen();
84+
RandomSipHasher {
85+
hasher: sip::SipHasher::new_with_keys(r0, r1),
86+
}
87+
}
88+
}
89+
90+
impl Hasher<sip::SipState> for RandomSipHasher {
91+
#[inline]
92+
fn hash<T: Hash<sip::SipState>>(&self, value: &T) -> u64 {
93+
self.hasher.hash(value)
94+
}
95+
}
96+
97+
impl Default for RandomSipHasher {
98+
#[inline]
99+
fn default() -> RandomSipHasher {
100+
RandomSipHasher::new()
101+
}
102+
}

src/libstd/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,6 @@ pub use core::option;
167167
pub use alloc::owned;
168168
pub use alloc::rc;
169169

170-
pub use core_collections::hash;
171170
pub use core_collections::slice;
172171
pub use core_collections::str;
173172
pub use core_collections::string;
@@ -237,6 +236,7 @@ pub mod to_str;
237236
/* Common data structures */
238237

239238
pub mod collections;
239+
pub mod hash;
240240

241241
/* Tasks and communication */
242242

0 commit comments

Comments
 (0)